* feat(daemon): expose Vela login through CLI and MCP
* feat(mcp): add interactive local brief card
* feat(packaged): bootstrap local MCP headlessly
* feat(daemon): secure local BYOK credential profiles
* feat(cli): manage secure BYOK profiles
* feat(web): store BYOK keys in secure profiles
* fix(runs): deduplicate cloud generation requests
* feat(daemon): add end-to-end plugin observability
* fix(pack): include native PTY runtime in desktop packages
Lazy-load node-pty in the daemon so missing native binaries only
disable Terminal and interactive Keychain operations instead of
crashing startup.
Ship and validate target prebuilds in macOS and Windows packages.
Repair the macOS spawn helper before signing, and invalidate stale
Windows packaging caches.
Cover the packaged PTY path in macOS and Windows smoke tests. Require
Vela CLI for every beta desktop build target.
* fix(mcp): localize and stabilize plugin briefs
Keep collect and confirm on one MCP Apps card, report intrinsic size changes,
and remove the fragile widget-state fallback.
Localize brief forms, summaries, status, and public errors from the current
request or Host locale while preserving stable protocol ids and selections.
Keep internal runtime identifiers out of user-facing MCP guidance.
* fix(packaged): register live Studio URLs with daemon
Register the packaged web sidecar loopback origin over namespace-scoped daemon
IPC after dynamic binding.
Invalidate install-info by the live web port and remove the second MCP URL cache
so run and project responses use the current Studio address.
Validate loopback origins and cover registration, rebind, and cache refresh.
* fix(mcp): stabilize brief confirmation lifecycle
Keep confirmed briefs locked after Host publication failures and retry only the cached follow-up message instead of confirming twice.
Replace blank initialization with a compact loading state, accept immediate and delayed tool results, and bump the MCP Apps resource to v3 so Codex does not reuse the stale card.
Add an executable JSDOM Host bridge harness covering double-submit, publication retry, delayed results, and repeated draft notifications. Validated with the full daemon suite, workspace typecheck, and guard.
* fix(mcp): prevent brief app resize feedback loops
Replace the self-observed intrinsic-height loop with deduplicated body measurements so Codex MCP app iframes do not repeatedly resize themselves and fail sandbox startup.
Bump the brief resource URI to v4 to avoid cached v3 cards and cover the regression with the focused brief-app spec. Validated with daemon tests/build, workspace guard/typecheck, a verified beta DMG, and a direct packaged MCP resource smoke.
* fix(mcp): use the final Open Design plugin identity
Make open-design the only accepted external plugin ID across MCP tools, run attribution, artifact version origins, and telemetry. Rename the Brief MCP App resource to v5 and reject the unpublished old ID instead of aliasing it.
Validated with 171 daemon tests, 5 web tests, daemon/web typechecks, and guard.
* chore(pack): bump Vela CLI to 0.0.27
* chore(nix): refresh pnpm dependency hashes
* fix(mcp): honor host and plugin request contracts
Respect independently advertised MCP Apps capabilities and prefer the Codex-native follow-up bridge so confirmed briefs render as readable user turns instead of context-only messages. Bump the brief resource to v6 to invalidate stale cards.\n\nMark Vela login and account probes as open-world interactions, and require stable request IDs for attributed generation so lost responses cannot create a second logical run.
* fix(cli): support keyless BYOK profiles
Add an explicit --no-api-key save mode for local and self-hosted providers while keeping credential-bearing profiles stdin-only. Reject missing or conflicting credential modes and cover both paths in the CLI tests.
* fix(pack): replace headless owner when desktop opens
Advertise headless packaged runtimes as having no visible window, then shut them down before a normal desktop launch continues instead of treating SHOW as a successful focus.
Validated with the focused packaged headless and launcher lifecycle tests (20 passed).
* fix(pack): clean up failed headless startup
Acquire the headless identity, sidecars, and IPC server through one failure-atomic lifecycle so later bootstrap errors close every previously acquired owner in reverse order.
Cover MCP-install and web-identity publication failures; validated with 22 focused tests and the packaged typecheck.
* fix(byok): remove raw provider request contract
Expose only daemon-owned BYOK profile references from shared run types and the web transport, while retaining the daemon's fail-closed rejection for legacy or untyped raw credential payloads.
Validated with 76 web provider tests plus web and contracts typechecks.
* fix(daemon): preserve interrupted run idempotency
Hydrate queued or running durable requests as DAEMON_RESTARTED failures before idempotency reuse, preserving the original request and workflow mappings instead of creating a second cloud operation.
Share the restart transition with boot reconciliation and expose synthetic terminal events to reattaching clients. Validated with 48 focused tests and the daemon typecheck.
* fix(byok): roll back failed credential metadata writes
Snapshot secure-store values before mutation and compensate create, update, and delete operations when profile metadata cannot be persisted, preventing orphaned or lost credentials.
Validated with nine focused credential-service tests and the daemon typecheck.
* fix(web): reconcile selected BYOK profile metadata
Treat the daemon's selected secure profile as authoritative for protocol, endpoint, model, and API version so CLI profile edits cannot leave the browser showing stale execution settings.
Validated with 66 config tests and the web typecheck.
* fix(byok): migrate legacy credentials before cleanup
Import each supported legacy browser credential into a stable daemon-owned secure profile before removing plaintext copies. Keep the original browser record on secure-store or unsupported-provider failures and surface a Settings recovery path.
Cover successful migration, duplicate projections, and injected secure-backend failure while preserving existing Bedrock downgrade behavior in memory.
* fix(byok): secure Windows profiles with DPAPI
Dispatch native Windows BYOK profiles to a CurrentUser DPAPI backend rooted under the resolved daemon data directory. Secrets enter PowerShell only through stdin, encrypted blobs stay separate from non-secret profile metadata, and API responses remain secret-free.
Add platform dispatch coverage, a Windows-only real DPAPI round-trip test, and the packaged Windows smoke path for create, resolve, response-redaction, and delete behavior.
* fix(web): preserve config writes during hydration
* fix(byok): persist secure credentials across entry flows
* test(byok): cover secure onboarding persistence
---------
Co-authored-by: Cheems <94773058+itscheems@users.noreply.github.com>
Co-authored-by: lefarcen <935902669@qq.com>
* feat: support Codex service tiers
* fix: forward service tiers through MCP runs
* fix: clear default service tier selections
* fix: preserve service tier on model option updates
* fix: preserve service tier without explicit model
* fix: preserve service tier in connection tests
* test: stabilize API empty stream UI check
* fix: preserve codex service tier labels
* fix: preserve service tier without saved model
* fix: address service tier review follow-ups
* fix: pin legacy chat assistant rows before start
* fix: accept bare model catalog responses
OpenCode Desktop ships two binaries on PATH after install:
- `opencode` — GUI launcher; double-clicking it opens the
desktop app, never reads stdin.
- `opencode-cli` — headless CLI; speaks the
`run --format json --dangerously-skip-permissions -`
protocol the daemon expects.
The OpenCode AGENT_DEF set `bin: 'opencode'`, so
`resolveAgentExecutable` resolved the GUI launcher for every user
with the desktop install. The daemon happily spawned that binary,
but the GUI launcher discards stdin and never emits the JSON event
stream — so the agent silently produced no output for the entire
conversation. Issue #814 tracks the user-visible symptom.
Fix: declare `bin: 'opencode-cli'` and `fallbackBins: ['opencode']`
on the OpenCode adapter. `resolveAgentExecutable` walks `bin`
first, then each fallback in order, so this gives us:
- Desktop install (both binaries on PATH) → resolves
`opencode-cli` (the CLI), GUI is ignored. The #814 fix path.
- Legacy CLI-only install (only `opencode` on PATH, no
`-cli` suffix) → falls through to `opencode`, which is the
actual CLI in that install shape, so the daemon keeps working
unchanged.
- Neither installed → falls through to null, same as before.
Same mechanism Claude Code uses to fall back to `openclaude`
(issue #235), so this is well-trodden ground in the codebase.
Tests:
- One declarative test asserts `opencode.bin === 'opencode-cli'`
and `opencode.fallbackBins.includes('opencode')` — runs on every
platform including Windows, catches anyone who accidentally
flips `bin` back to the GUI name.
- Two filesystem-backed tests pin the resolution priority: when
both binaries are on PATH (the desktop case) `opencode-cli`
wins; when only the legacy `opencode` is on PATH the fallback
takes over.
- Existing comment guarding "most agents have a single binary"
updated to remove `opencode` from the example list.
Verified locally:
- daemon vitest: 993/993
- daemon `tsc -p tsconfig.json` and `tsc -p tsconfig.tests.json`: both clean
- i18n-check passes
Reported by @giuliastro, diagnosed by @lefarcen who also asked for
the `opencode-cli --version` output in the issue thread — happy to
narrow this further if their reply turns up subcommand differences
between the two binaries.
Fixes#814
* fix(settings): add install onboarding links for unavailable local CLIs
* fix(settings): rename Claude config dir label to config directory
---------
Co-authored-by: mrcfps <mrc@powerformer.com>
* feat(daemon): expose gemini-3 preview models and 2.5-flash-lite in the Gemini fallback list
Issue #981: the Gemini agent picker only offered 2.5 Pro / 2.5 Flash
even though the Gemini CLI ships gemini-3-pro-preview, gemini-3-flash-preview,
and gemini-2.5-flash-lite. Users had to fall back to "Default" or
type the id by hand to reach the newer generation.
Add the three model ids to fallbackModels with the same shape as the
existing entries so the picker surfaces them inline.
* test(daemon): pin Gemini picker fallbackModels order
mrcfps flagged on PR #986 that the new fallback ids only had implicit
coverage via the static array, which means a future reorder/removal in
AGENT_DEFS could silently reshape the Settings UI without tripping CI.
Mirror the codex/qoder regression-test pattern so the Gemini picker
contents stay locked to the documented priority order: default,
gemini-3-pro-preview, gemini-3-flash-preview, gemini-2.5-pro,
gemini-2.5-flash, gemini-2.5-flash-lite.
This is especially worth covering for Gemini because the CLI accepts
arbitrary custom ids, so a regression in the picker would not surface
during manual QA.
---------
Co-authored-by: Nagendhra <nagendhra405@gmail.com>
* Optimize Windows packaged web output
* Fix packaged contracts runtime build
* Optimize Windows packaged size pruning
* Prune Windows root Next payload
* Remove Windows bundled Node runtime
* Prune Windows standalone duplicate Next
* Add tools-pack cache foundation
* Cache Windows packaged build layers
* Cache Windows workspace builds
* Cache Electron-ready Windows app
* Split Windows tools-pack module
* Cache Windows dir build outputs
* Split Windows pack build modules
* Document Windows NSIS smoke namespace limits
* Move Windows NSIS smoke note to agents guide
* Optimize Windows beta packaging
* Bump packaged beta base version
* Improve Windows installer namespace UX
* Improve Windows tools-pack cache keys
* Stabilize Windows beta cache version keys
* Cache Windows workspace build outputs
* Optimize windows release beta cache layers
* Cache windows release dependencies
* Trim windows release cache before save
* Refresh windows tools-pack cache key
* Improve windows installer preflight prompts
* Fallback NSIS installer strings to English
* Fix Windows installer cleanup and preflight
* Improve Windows NSIS state logging
* Fix system NSIS Persian language alias
* Use long-path removal for Windows uninstall
* Fix mac tools-pack tests on Windows
* Address Windows packaging review feedback
* Fix Windows installer cache namespace isolation
* Include web output mode in Windows tarball cache key
* Use unique Windows release cache save keys
* Support overriding the Codex executable path
* Replace save-as-template prompts with an in-app dialog
* Seed local packaged app config from workspace
* Fix packaged config and connection test overrides
* Keep tools-pack mac config seeding self-contained
* Require absolute CODEX_BIN overrides
* fix(daemon, packaged): unbreak GUI-launched agent detection on minimal PATHs (#442)
GUI-launched daemons (Finder/Dock on macOS, .desktop on Linux) inherit a
stripped PATH from launchd / the desktop session and don't read the
user's interactive shell rc files, so any CLI installed via `npm i -g`
under a sudo-free prefix like ~/.npm-global was silently undetected.
Two layers maintained their own copies of the user-toolchain bin list
(`apps/daemon/src/agents.ts:userToolchainDirs` for the resolver,
`apps/packaged/src/sidecars.ts:resolvePackagedPathEnv` for the packaged
sidecar PATH builder) and had already drifted on `~/.asdf/shims` and
`~/Library/pnpm`. Adding ~/.npm-global to one side would have
preserved the same anti-pattern.
Extracts `wellKnownUserToolchainBins` into @open-design/platform as the
single source of truth, has both layers consume it, and extends the
list to cover ~/.npm-global/bin, ~/.npm-packages/bin, plus
$NPM_CONFIG_PREFIX/bin / $npm_config_prefix/bin for users with a
non-standard prefix. New vitest coverage in the platform package and
a regression test in apps/daemon/tests/agents.test.ts modelled on the
existing mise case.
Verified end-to-end: under PATH=/usr/bin:/bin:/usr/sbin:/sbin (the
launchd default a `.app` actually inherits), `resolveAgentExecutable`
now returns ~/.npm-global/bin/gemini instead of null.
* fix(daemon): isolate OD_AGENT_HOME resolution from $NPM_CONFIG_PREFIX leakage
Address review feedback on PR #614:
- mrcfps spotted that the daemon wrapper called wellKnownUserToolchainBins
without passing `env`, so the helper read its default process.env. A
developer or CI runner with NPM_CONFIG_PREFIX / npm_config_prefix
exported would inject that real <prefix>/bin into resolveOnPath() even
while the OD_AGENT_HOME hook pointed home at a temp fixture, making
agent-detection tests environment-dependent. Reproduced locally: with
OD_AGENT_HOME=<tmp> + NPM_CONFIG_PREFIX=/Users/me/.npm-global,
resolveAgentExecutable({ bin: 'codex' }) returned the real machine's
binary instead of null. Wrapper now passes `env: {}` whenever
homeOverride is set, alongside the existing includeSystemBins gate.
- lefarcen suggested also handling whitespace-only NPM_CONFIG_PREFIX
values (e.g. NPM_CONFIG_PREFIX=" ") so the helper does not emit a
bogus "<whitespace>/bin" entry. Added a .trim() check before
appending.
- lefarcen also suggested a comment pointer from the daemon wrapper to
the platform helper so readers don't have to grep. Added the
reference inline.
Coverage:
- packages/platform/tests/index.test.ts: new whitespace-prefix case.
- apps/daemon/tests/agents.test.ts: new env-isolation regression
asserting that OD_AGENT_HOME + NPM_CONFIG_PREFIX cannot leak the
real prefix bin into the sandbox.
* test(daemon): preserve $NPM_CONFIG_PREFIX across the env-isolation case (#614)
Address mrcfps's second-round review on PR #614: the env-isolation
regression sets `process.env.NPM_CONFIG_PREFIX = realPrefix` in its
body and then unconditionally `delete`s it in `finally`. On a developer
machine or CI runner that already exported `NPM_CONFIG_PREFIX`, that
mutates the worker-wide env for every later test, making downstream
env-sensitive assertions order-dependent.
Move the save/restore into the file's existing afterEach hook (mirroring
the OD_AGENT_HOME / OD_DAEMON_URL / OD_TOOL_TOKEN pattern) and drop the
in-test `delete`. Same coverage, no worker-state mutation.
* fix(platform): prioritise $NPM_CONFIG_PREFIX over the conventional npm guesses (#614)
Address mrcfps's third-round review on PR #614: when the user has
explicitly configured a prefix via $NPM_CONFIG_PREFIX (or
$npm_config_prefix), that's where `npm i -g` puts the *current*
binaries. The conventional guesses ~/.npm-global / ~/.npm-packages
often hold *stale* installs from an older prefix the user has since
rewritten — searching the env-driven prefix first matches npm's own
resolution order (env > .npmrc > default) and gives "explicit beats
convention" semantics.
Move the env-driven push above the conventional `dirs.push(.npm-global,
.npm-packages)`. Add a vitest case in the platform package that asserts
$NPM_CONFIG_PREFIX/bin's index in the result is strictly less than
~/.npm-global/bin's and ~/.npm-packages/bin's.
`resolveOnPath()` and the packaged PATH builder both preserve insertion
order, so first hit wins and the new ordering propagates to both
layers.
* fix(platform): lift $NPM_CONFIG_PREFIX above every conventional bin (#614)
Address mrcfps's fourth-round review on PR #614: the previous fix only
moved $NPM_CONFIG_PREFIX/bin ahead of ~/.npm-global / ~/.npm-packages,
but ~/.local/bin still appeared earlier in the array. Under a minimal
GUI-launch PATH a stale agent in ~/.local/bin (also a shared dumping
ground for pip --user / cargo install / hand-built binaries) could
outrank the user's *current* explicit npm prefix.
Move the env-driven push to the head of `dirs` so the explicit prefix
wins over every conventional location below — ~/.local/bin included.
Matches npm's own resolution order (env > .npmrc > default) across the
whole list, not just the npm-prefix block.
Tightened the existing order test to assert `explicitIdx === 0` and
that ~/.local/bin's index is strictly greater than the explicit
prefix's index, so a future drift would fail loudly.
* feat(daemon): let Codex image projects avoid API-key setup
Codex has a built-in image generation path available inside the agent runtime, while the generic media dispatcher still routes gpt-image models through the daemon OpenAI provider. Pass the active agent id into prompt composition so Codex-only gpt-image projects can use built-in imagegen first without changing non-Codex media behavior.
Constraint: Existing media contract remains the default path for non-Codex agents and explicit provider fallback
Rejected: Add a nested daemon Codex media provider | heavier auth, streaming, timeout, cancellation, and output parsing surface for this parity fix
Confidence: high
Scope-risk: narrow
Directive: Keep this override after the media contract so it can intentionally supersede dispatcher-only wording for Codex gpt-image projects
Tested: pnpm --dir apps/daemon exec vitest run -c vitest.config.ts tests/system-prompt-template.test.ts
Tested: pnpm --filter @open-design/daemon typecheck
Tested: pnpm guard
Tested: pnpm typecheck
Not-tested: Live Codex image generation inside the Open Design UI
* fix(daemon): harden Codex imagegen prompt routing
PR review found the Codex override could be superseded by the web-supplied media contract, trusted unvalidated image model metadata, and assumed generated image paths outside the workspace were readable.
This keeps the override daemon-owned, appends it last in the live prompt, validates against registered gpt-image model IDs, allowlists only Codex's generated_images folder, and tightens copy-failure instructions.
Constraint: The web contracts composer still emits the generic media contract without agent identity.
Rejected: Mirror Codex-specific prompt logic into contracts/web | duplicates daemon model registry and still leaves final ordering fragile.
Confidence: high
Scope-risk: narrow
Directive: Keep Codex imagegen override appended after client systemPrompt so it remains the final media instruction for Codex gpt-image projects.
Tested: pnpm --dir apps/daemon exec vitest run -c vitest.config.ts tests/system-prompt-template.test.ts tests/agents.test.ts tests/chat-route.test.ts
Tested: pnpm --filter @open-design/daemon typecheck
Tested: pnpm guard
Tested: pnpm typecheck
Not-tested: Live Codex image generation inside the Open Design UI
* fix(daemon): keep Codex add-dir writable scope narrow
PR review found Codex --add-dir grants writable workspace access, so passing skill, design-system, and linked reference directories through the same chat allowlist broke their documented read-only boundary.
This routes chat extra directories by active agent: Codex receives only the validated generated_images output directory needed for built-in imagegen, while non-Codex adapters keep the existing resource and linked-directory read access behavior.
Constraint: Codex CLI treats --add-dir as writable sandbox expansion.
Constraint: The daemon still stages active skill files into the project cwd as Codex's read-safe path.
Rejected: Keep one shared extraAllowedDirs list for all agents | grants Codex write access to read-only resources.
Confidence: high
Scope-risk: narrow
Directive: Do not add read-only resource/reference directories to Codex --add-dir unless Codex gains a read-only allowlist flag.
Tested: git diff --check -- apps/daemon/src/server.ts apps/daemon/tests/chat-route.test.ts
Tested: pnpm --filter @open-design/daemon exec vitest run tests/chat-route.test.ts
Tested: pnpm --filter @open-design/daemon typecheck
Tested: pnpm guard
Tested: pnpm typecheck
Not-tested: Live Codex image generation inside the Open Design UI
* fix(daemon): validate Codex imagegen add-dir grants
PR review found the generated_images grant still trusted symlinked paths and rendered the Codex override before proving the sandbox grant would be present.
This validates the generated_images directory before prompt assembly, rejects final-component symlinks and protected-root canonical escapes, passes Codex the canonical grant path, and only appends the Codex imagegen override when that same path is in extraAllowedDirs.
Constraint: Codex --add-dir grants writable workspace access, so path aliases into read-only resource roots must be rejected.
Rejected: Keep returning the nominal CODEX_HOME path after validation | leaves Codex operating through a symlink alias instead of the audited grant target.
Confidence: high
Scope-risk: narrow
Directive: Keep Codex imagegen prompt rendering downstream of generated_images validation and grant resolution.
Tested: git diff --check -- apps/daemon/src/server.ts apps/daemon/tests/chat-route.test.ts
Tested: pnpm --filter @open-design/daemon exec vitest run -c vitest.config.ts tests/chat-route.test.ts
Tested: pnpm --filter @open-design/daemon exec vitest run -c vitest.config.ts tests/agents.test.ts tests/chat-route.test.ts
Tested: pnpm --filter @open-design/daemon typecheck
Tested: pnpm guard
Tested: pnpm typecheck
Not-tested: Live Codex image generation inside the Open Design UI
* fix(daemon): add required env field to McpServerStdio in live-artifacts MCP descriptor
The ACP schema's McpServerStdio marks env as a required field
(List[EnvVariable] with no default). Omitting it causes Pydantic V2
Union validation to fail across all three variants (HttpMcpServer,
SseMcpServer, McpServerStdio), returning -32602 Invalid params on
session/new for agents with mcpDiscovery: 'mature-acp' (Hermes,
Devin, Kimi).
This bug is invisible when mcpServers resolves to an empty array
(no live-artifacts token), so it only manifests when the MCP
live-artifacts integration is enabled.
* fix(daemon): recover from -32602 Invalid params on session/set_model
Extend the existing -32603 Internal error recovery logic to also
handle -32602 (Invalid params) when the set_model request fails.
This allows the prompt to proceed with the default model instead of
hanging or timing out.
Some ACP agents may not support session/set_model or may reject the
model ID — treating this as a non-fatal condition and falling back to
the default model is more resilient than failing the entire run.
* fix(daemon): narrow -32602 handling and update test fixtures for env field
Address PR #627 review feedback:
1. Narrow -32602 Invalid params suppression to setModelRequestId only.
Unexpected-id -32602 errors are now treated as real protocol failures
and propagated via fail(), matching the reviewer's suggestion. Only
-32603 Internal errors from unexpected IDs are still suppressed as
cleanup noise.
2. Update all buildLiveArtifactsMcpServersForAgent test fixtures to
include the new required env: [] field.
* fix(daemon): remove --no-session from pi adapter to persist session files
The pi agent was the only adapter explicitly passing `--no-session`
in its `buildArgs`, preventing pi from writing session files.
All other adapters either run in single-shot mode by design or use
the ACP JSON-RPC session lifecycle without suppressing persistence.
Removing `--no-session` lets `--mode rpc` retain its default behavior
of writing session state, which is needed for multi-prompt continuity
and matches the rest of the harness ecosystem.
* test(daemon): add pi buildArgs regression tests; fix docs for --no-session removal
- Adds test for pi.buildArgs base shape: returns ["--mode", "rpc"]
and does not include --no-session (prevents regression).
- Adds test for --model and --thinking option passthrough.
- Updates pi-rpc.ts lifecycle comment to remove [--no-session].
- Updates README.md and all localized READMEs to reflect the
corrected pi CLI invocation.
* fix(daemon): preserve ANTHROPIC_API_KEY when ANTHROPIC_BASE_URL is set
The claude adapter currently strips ANTHROPIC_API_KEY unconditionally
so that Claude Code's own auth resolution (claude login) wins instead
of silently falling back to API-key billing.
However, when ANTHROPIC_BASE_URL is set the user is intentionally
routing Claude Code to a custom endpoint (e.g. a Kimi/Moonshot proxy).
In that case claude login is meaningless, so preserve the API key so
the child can authenticate against the custom base URL.
* fix(daemon): guard against empty ANTHROPIC_BASE_URL values
Address review feedback: check that ANTHROPIC_BASE_URL contains a
non-empty, non-whitespace string before preserving ANTHROPIC_API_KEY.
This prevents the #398 billing guard from being bypassed when the
variable is set to '' or whitespace.
* docs: add live artifacts implementation spec
* docs: align live artifacts implementation plan
* Ralph iteration 1: work in progress
* Ralph iteration 2: work in progress
* Ralph iteration 3: work in progress
* Ralph iteration 4: work in progress
* Ralph iteration 5: work in progress
* Ralph iteration 6: work in progress
* Ralph iteration 7: work in progress
* Ralph iteration 8: work in progress
* Ralph iteration 9: work in progress
* Ralph iteration 10: work in progress
* Ralph iteration 11: work in progress
* Ralph iteration 12: work in progress
* Ralph iteration 13: work in progress
* Ralph iteration 14: work in progress
* Ralph iteration 15: work in progress
* Ralph iteration 16: work in progress
* Ralph iteration 17: work in progress
* Ralph iteration 18: work in progress
* Ralph iteration 19: work in progress
* Ralph iteration 20: work in progress
* Ralph iteration 21: work in progress
* Ralph iteration 22: work in progress
* Ralph iteration 23: work in progress
* Ralph iteration 24: work in progress
* Ralph iteration 25: work in progress
* Ralph iteration 26: work in progress
* Ralph iteration 27: work in progress
* Ralph iteration 28: work in progress
* Ralph iteration 29: work in progress
* Ralph iteration 30: work in progress
* Ralph iteration 31: work in progress
* Ralph iteration 32: work in progress
* Ralph iteration 33: work in progress
* Ralph iteration 34: work in progress
* Ralph iteration 35: work in progress
* Ralph iteration 36: work in progress
* Ralph iteration 37: work in progress
* Ralph iteration 38: work in progress
* Ralph iteration 39: work in progress
* Ralph iteration 40: work in progress
* Ralph iteration 41: work in progress
* Ralph iteration 42: work in progress
* Ralph iteration 43: work in progress
* Ralph iteration 44: work in progress
* Ralph iteration 45: work in progress
* Ralph iteration 46: work in progress
* Ralph iteration 47: work in progress
* Ralph iteration 48: work in progress
* Ralph iteration 49: work in progress
* Ralph iteration 50: work in progress
* Ralph iteration 51: work in progress
* Ralph iteration 52: work in progress
* Ralph iteration 53: work in progress
* Ralph iteration 54: work in progress
* Ralph iteration 55: work in progress
* Ralph iteration 56: work in progress
* Ralph iteration 57: work in progress
* Ralph iteration 58: work in progress
* Ralph iteration 59: work in progress
* Ralph iteration 60: work in progress
* Ralph iteration 61: work in progress
* Ralph iteration 62: work in progress
* Ralph iteration 63: work in progress
* Ralph iteration 64: work in progress
* Ralph iteration 65: work in progress
* Ralph iteration 1: work in progress
* Ralph iteration 2: work in progress
* Ralph iteration 3: work in progress
* Ralph iteration 4: work in progress
* Ralph iteration 5: work in progress
* Ralph iteration 6: work in progress
* Ralph iteration 8: work in progress
* Ralph iteration 9: work in progress
* Ralph iteration 17: work in progress
* Add Composio-backed connectors
* Add Composio-backed connector catalog
* Fix connector callback flow
* Update live artifact connector refresh
* Fix live artifact refresh updates
* Improve live artifact viewer toolbar
* Refine live artifact source tabs
* Expand Composio connector catalog
* Improve Composio connector browsing
* Fix artifact refresh source safety checks
Generated-By: looper 0.4.1 (runner=fixer, agent=opencode)
* Fix live artifacts PR feedback
Generated-By: looper 0.5.0 (runner=fixer, agent=opencode)
* Fix live artifact preview CORS validation
Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
* Fix connector OAuth IPv6 loopback hosts
Allow bracketed IPv6 loopback Host headers when deriving connector OAuth callback URLs so IPv6-bound daemons can complete connection flow.
Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
* Preserve live artifact refresh permissions
Respect explicit refresh permission choices during live artifact create and update flows so revoked connector sources remain gated.
Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
* Fix live artifact preview cache freshness
Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
* Fix live artifact refresh validation
Guard manual refreshes with local daemon checks and reject daemon_tool sources without a toolName before refresh execution.
Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
* Fix Composio credential invalidation
Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
* Fix live artifact CORS methods
Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
* Fix workspace validation
Restore media config test isolation under Vitest setup data-dir overrides and add the missing French live artifact display copy so the workspace test suite stays aligned.\n\nGenerated-By: looper 0.5.2 (runner=fixer, agent=opencode)
* Fix connector safety filtering
Keep agent-preview connector listings aligned with execution safety policy and prune stale Composio OAuth state records before they accumulate.
Generated-By: looper 0.5.2 (runner=fixer, agent=opencode)
* Fix agent runtime cleanup
Generated-By: looper 0.5.2 (runner=fixer, agent=opencode)
* Fix live artifact daemon access
Validate local-only live artifact routes against the peer socket address and pass daemon-resolved CLI paths to ACP MCP descriptors.\n\nGenerated-By: looper 0.5.2 (runner=fixer, agent=opencode)
* Fix connector run limit pruning
Evict stale connector rate-limit buckets so long-lived daemon processes do not retain per-run entries indefinitely.\n\nGenerated-By: looper 0.5.2 (runner=fixer, agent=opencode)
* Fix connector compact schemas
Generated-By: looper 0.5.2 (runner=fixer, agent=opencode)
* Improve connector connection feedback
* Adjust connector gate positioning
* Fix live artifact refresh commits
Avoid marking refresh candidates failed after snapshot or state persistence errors by deferring live artifact mutations until the durable refresh metadata is written. Also align connector OAuth callback host validation with daemon loopback handling.\n\nGenerated-By: looper 0.5.4 (runner=fixer, agent=opencode)
* Improve connector search relevance
* fix(daemon): harden connector connection state
Require loopback daemon validation before connector connect side effects and only clear provider-owned connector statuses during credential reset.
Generated-By: looper 0.5.4 (runner=fixer, agent=opencode)
* fix(daemon): guard connector disconnect route
Require local daemon request validation before connector disconnect side effects.
Generated-By: looper 0.5.4 (runner=fixer, agent=opencode)
* fix(daemon): guard composio config updates
Generated-By: looper 0.5.4 (runner=fixer, agent=opencode)
* fix(daemon): dispatch live artifacts mcp first
Route the live-artifacts MCP server before the generic MCP CLI so od mcp live-artifacts starts the dedicated server instead of failing generic argument parsing.\n\nGenerated-By: looper 0.5.4 (runner=fixer, agent=opencode)
* fix(daemon): handle integer connector schemas
Allow JSON Schema integer connector inputs while preserving fractional-value validation so generated connector tool schemas accept valid page sizes and limits.
Generated-By: looper 0.5.4 (runner=fixer, agent=opencode)
* fix: align live artifact refresh error codes
Generated-By: looper 0.5.4 (runner=fixer, agent=opencode)
* Fix live artifact connector refresh flow
* Update live artifact design cards
* Add beta badge to live artifact form
* Remove live artifact tile model
* Fix live artifact refresh sync
* Fix live artifact MCP refresh durability
Generated-By: looper 0.5.4 (runner=fixer, agent=opencode)
* Fix live artifact refresh safety
Enforce persisted refresh opt-out and connector auto-read gating before refresh sources execute.
Generated-By: looper 0.5.5 (runner=fixer, agent=opencode)
Probe 'claude -p --help' (subcommand help) instead of 'claude --help'
(global help), because --add-dir and --include-partial-messages are
subcommand-specific flags that do not appear in the global help output.
Previously, successful capability probing would set caps.addDir = false
because the flag was not found in global help, causing the daemon to
never pass --add-dir to spawned Claude Code processes. This broke
absolute-path reads for skill seeds and design-system specs, as
Claude Code's sandbox policy blocks access to paths outside the
project cwd unless explicitly allowed via --add-dir.
The probing-failure / timeout path already worked correctly (empty
caps object defaults to passing --add-dir). The failure manifested
only when help probing succeeded and explicitly failed to locate
the subcommand flags.
Co-authored-by: bojiehuang <bojiehuang@bojiehuangdeMacBook-Pro.local>
* docs(readme): refresh contributors wall
* docs(readme): refresh contributors wall
* feat: kilo cli
* fix: use default model option for kilo
* chore: add agent_diff id unique test
* chore: add deepseek to docs
* feat(daemon): add DeepSeek TUI as a code agent adapter
Register `deepseek` (with `deepseek-tui` cargo-only fallback) in
AGENT_DEFS via `deepseek exec --auto [--model X] <prompt>` and plain-text
streaming. Ships `deepseek-v4-pro` / `deepseek-v4-flash` as fallback
model hints; users can paste any other id (incl. NIM / Fireworks /
SGLang routes) via the custom-model input.
Web UI gets a DeepSeek-blue gradient icon, label/alias mapping, and
docs/agent-adapters.md §5.9 documents the auth state, prompt-as-argv
Windows size limit, and the upstream gap that prevents stdin delivery
today (clap declares `prompt: String` as a required positional).
Adds .deepseek/ to .gitignore alongside the other per-agent runtime
data dirs so first-launch trust files don't leak into git.
* fix(daemon): drop unsupported deepseek-tui fallback bin
The `deepseek` dispatcher owns `exec` / `--auto`; `deepseek-tui` is the
runtime companion it invokes. Listing `deepseek-tui` in fallbackBins
advertised availability for a host that only had the TUI binary, but
buildArgs still emitted `<resolved> exec --auto <prompt>` — which
deepseek-tui itself doesn't accept, so the first /api/chat run would
fail. Upstream documents both binaries as required (npm and cargo paths
install them together), so the fallback didn't correspond to a supported
install. Pin the absence in the agents test and update docs §5.9 + the
adapter table to match.
Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
* fix(daemon): pre-flight DeepSeek TUI prompts against argv byte budget
DeepSeek's exec mode requires the prompt as a positional argv arg (no
`-` stdin sentinel upstream), so a fully composed OD prompt — system
text + history + skills + design-system content + the user message —
can blow Windows' ~32 KB CreateProcess limit (or Linux MAX_ARG_STRLEN
on extreme edges) and surface as a generic spawn failure instead of
a DeepSeek-specific, user-actionable message. The adapter now declares
`maxPromptArgBytes = 30_000` (leaves ~2.7 KB argv headroom for `exec
--auto --model <id>` and Windows quoting), and the /api/chat spawn
path checks the composed prompt against that budget before calling
`spawn`. Oversized prompts fail fast with `AGENT_PROMPT_TOO_LARGE`
and guidance to reduce skills/design context or pick an adapter with
stdin support.
Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
* test(daemon): pin DeepSeek argv-budget guard with regression tests
The previous spawn-path guard inlined the byte-budget check in the
chat handler, so the only safety net for the DeepSeek argv-only
prompt-delivery shape was a static "the field exists" assertion —
nothing actually exercised the AGENT_PROMPT_TOO_LARGE path or the
short-prompt happy path. Extract the check into a pure
`checkPromptArgvBudget(def, composed)` helper in agents.ts, call it
from /api/chat before bin resolution (so the guard is order-
independent and fires regardless of whether the adapter binary is
on PATH in CI), and add a regression test that exercises both the
oversized-prompt branch (over the conservative under-Windows-
CreateProcess budget) and the short-prompt branch, plus a UTF-8
byte-vs-codepoint case and a stdin-adapter no-op case so the guard
can't silently regress or leak onto adapters that ship the prompt
over stdin.
Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
* fix(daemon): pre-flight DeepSeek prompts against Windows .cmd-shim quoting
The first-pass argv-byte guard only inspects the raw composed prompt, so
on Windows an npm-installed `deepseek` resolves to a `.cmd` shim and the
spawn path then wraps the call in `cmd.exe /d /s /c "<inner>"` with
every embedded `"` doubled by `quoteWindowsCommandArg`. A quote-heavy
prompt (code blocks, JSON-shaped skill seeds) under the 30,000-byte
budget can therefore still expand past CreateProcess's 32_767-char
`lpCommandLine` cap and surface as a generic spawn ENAMETOOLONG instead
of the DeepSeek-named, actionable `AGENT_PROMPT_TOO_LARGE` the budget
guard was meant to provide. Add a second pure helper
`checkWindowsCmdShimCommandLineBudget(def, resolvedBin, args)` that
mirrors the platform layer's per-arg quoting and recomputes the would-be
command line length whenever the resolved binary is a `.cmd` / `.bat`
shim, and call it from `/api/chat` after `buildArgs` / `resolveAgentBin`
so the same SSE error fires before `spawn`. Pin the new path with a
quote-heavy regression (prompt is under the byte budget but doubles
past the kernel cap) plus no-op tests for non-`.cmd` resolutions, null
bin, and stdin-only adapters so the guard can't drift back.
Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
* fix(daemon): extend DeepSeek argv guard to direct .exe Windows installs
The cmd-shim guard added in 9011361 early-returned for non-`.cmd` /
`.bat` resolutions, so a Windows host that resolved `deepseek` directly
to a `.exe` (cargo-installed CLI, hand-built release, anything outside
the npm shim path) bypassed the post-`buildArgs` budget check entirely.
Direct `.exe` spawns skip the `cmd.exe /d /s /c "<inner>"` wrap, but
Node/libuv still composes a CreateProcess `lpCommandLine` by walking
each argv element through `quote_cmd_arg` — every embedded `"` becomes
`\"`, backslashes adjacent to a quote get doubled. A quote-heavy
prompt (code blocks, JSON-shaped skill seeds) under the 30,000-byte
`maxPromptArgBytes` budget can therefore still expand past the kernel's
32_767-char cap on those installs and surface as a generic spawn
ENAMETOOLONG instead of the actionable `AGENT_PROMPT_TOO_LARGE` the
guard was meant to provide.
Add a sibling pure helper `checkWindowsDirectExeCommandLineBudget(def,
resolvedBin, args)` that mirrors libuv's quoting math (empty -> `""`;
no whitespace/quote -> pass-through; quote-only -> simple wrap;
otherwise per-char escape with backslash-doubling around quotes and
trailing backslashes) and recomputes the would-be command line length
whenever the resolved binary is a non-shim Windows install. The two
Windows guards are mutually exclusive: the cmd-shim guard owns
`.bat` / `.cmd`, the direct-exe guard owns everything else, so a
single oversized prompt never double-emits an SSE error. POSIX paths
on POSIX hosts still skip both guards (no CreateProcess in play), and
stdin-delivered adapters still skip the entire post-buildArgs path.
Wire the new helper into `/api/chat` after the cmd-shim check, pin
the path with a quote-heavy regression on a `C:\Program
Files\DeepSeek\deepseek.exe` resolved bin (prompt under the byte
budget but past the kernel cap once libuv-quoted), plus no-op tests
for ordinary prompts, `.cmd`/`.bat` resolutions, POSIX paths,
null/empty bin, and stdin-only adapters, plus a mutual-exclusivity
assertion so the two guards' division of labour can't drift.
Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
* fix(daemon): neutralize cmd.exe %var% expansion in DeepSeek argv
Wrap each `%` in `"^%"` inside `quoteWindowsCommandArg` so cmd.exe's
percent-expansion can't substitute env values into a `.cmd`-shim spawn
when the DeepSeek argv carries a prompt mentioning `%DEEPSEEK_API_KEY%`.
Mirror the change in the agents.ts helper used by the budget guard, add
`AGENT_PROMPT_TOO_LARGE` to the contracts so typed SSE consumers
recognize the existing daemon error code, and pin the literal-prompt
contract with regression tests.
Generated-By: looper 0.4.0 (runner=fixer, agent=claude-code)
* fix(daemon): strip ANTHROPIC_API_KEY when spawning Claude Code
Claude Code prefers ANTHROPIC_API_KEY over the user's logged-in
subscription auth when both are present, silently billing API usage
even though the daemon's "Local CLI" mode implies the user wants
their `claude login` credentials (Pro/Max plan) to be used. The
daemon inherited the launching shell's env, so anyone who ever
exported ANTHROPIC_API_KEY for SDK work or scripts hit this.
Strip ANTHROPIC_API_KEY from the env passed to spawn for the
claude adapter only, so Claude Code's own auth resolution applies.
Fixes#398
* fix(daemon): strip ANTHROPIC_API_KEY case-insensitively, extract helper, add tests
Address review feedback on #400:
- Extract spawnEnvForAgent helper in agents.ts so the strip is unit-
testable and lives next to the other agent-spawn primitives.
- Strip ANTHROPIC_API_KEY case-insensitively. Windows env-var names are
case-insensitive at the kernel level; spreading process.env into a
plain object loses Node's case-insensitive accessor, so a mixed-case
Anthropic_Api_Key would survive `delete env.ANTHROPIC_API_KEY` and
still reach Claude Code via the spawned env block on Windows.
- Add four tests covering: uppercase strip for claude, case-insensitive
strip for claude, preservation for non-claude adapters, and input
immutability.
---------
Co-authored-by: Sebastian Westberg <sebastianwestberg@Sebastians-MacBook-Pro.local>
Recent Codex CLI versions (≥ 0.122 per #237 reports) reject a bare `-`
argv element with `error: unexpected argument '-' found`, exiting code 2
before any prompt is read. Open Design currently appends `-` after the
positional flags in `apps/daemon/src/agents.ts`, while also setting
`promptViaStdin: true` to deliver the prompt via stdin pipe. The `-`
became redundant after PR #258 standardized stdin delivery — only its
removal was missed for codex, leaving the agent unusable on `main` /
0.2.0 across every platform that runs a current Codex CLI.
This matches the pattern that was already corrected for `cursor-agent`
upstream of this commit (apps/daemon/src/agents.ts line ~392 comment),
where the same `-` sentinel was found to surface as a literal user
prompt. The fix here is the codex equivalent: remove the trailing
`args.push('-')` and rely on `promptViaStdin: true` for delivery.
The existing `agents.test.ts` cases asserted `args.at(-1) === '-'`,
which encoded the broken contract; those assertions are dropped and a
new regression test pins `args.includes('-') === false` across the
plugin, model, reasoning, and disable-plugins permutations so the
sentinel can't sneak back in via a future buildArgs branch.
Out of scope for this PR (not yet verified, left for separate
investigation):
- `--full-auto` is also flagged as deprecated by current Codex versions
(`use \`--sandbox workspace-write\` instead`). It still parses and
runs — the warning is non-fatal — and the right replacement involves
pinning the auto-approval policy that `--full-auto` implies, not just
`--sandbox`. Cleaning this up safely deserves its own PR after
confirming the modern equivalent.
- OpenCode (`apps/daemon/src/agents.ts:320`) and Qwen Code
(`apps/daemon/src/agents.ts:433`) follow the same `args.push('-')` +
`promptViaStdin: true` shape. Whether their current CLIs also reject
a bare `-` is unverified; if they do, the same minimal fix applies
there. Calling out for maintainer assessment alongside #163 / #218.
Closes#237.
Today open-design detects code agents by exact PATH binary name (`claude`,
`codex`, `gemini`, …). OpenClaude (https://github.com/Gitlawb/openclaude)
is an argv-compatible Claude Code fork that ships under the binary name
`openclaude`, so users with only `openclaude` installed see "Claude Code:
not detected" and have to write a wrapper script (issue #235, "Path B"
workaround).
This change adds a generic fallback-bin mechanism and uses it to declare
`openclaude` as a Claude Code fallback:
- `AGENT_DEFS[claude]` gains `fallbackBins: ['openclaude']` — a new
optional declarative slot. Agents without a fallback are unchanged.
- `resolveAgentExecutable(def)` is a new exported helper that walks
`[def.bin, ...def.fallbackBins]` and returns the first PATH hit, or
null. The two existing `resolveOnPath(def.bin)` callsites in
`probe()` and `resolveAgentBin()` route through this helper instead
of reading `def.bin` directly.
- `stripFns()` is extended to drop `fallbackBins` from the API response
payload — same posture as `helpArgs` / `capabilityFlags` / probe-only
metadata that shouldn't bleed into the public agents list.
Behavior contract:
- If both `claude` and `openclaude` are on PATH, `claude` still wins
(the order in the candidate list matters).
- If only `openclaude` is on PATH, the agent shows up as "Claude Code"
and chat / model-listing / spawning all use the resolved openclaude
path. Users don't need wrappers and the agent picker stays a single
"Claude Code" entry instead of confusingly splitting into two.
- Agents with no `fallbackBins` field (codex / gemini / opencode / …)
resolve via `def.bin` exactly as before.
Why fallback (not a separate AGENT_DEFS entry):
The issue author explicitly preferred the fallback shape over a second
top-level "OpenClaude" entry, and OpenClaude is a verbatim Claude Code
fork — the buildArgs / fallbackModels / streamFormat / capabilityFlags
would all be duplicated. A new `fallbackBins` slot keeps the Claude
config single-sourced and lets future drop-in forks be added with one
extra string.
Test coverage (5 new tests in `tests/agents.test.ts`):
1. `claude.fallbackBins` includes `'openclaude'` — declarative regression
guard so the AGENT_DEFS entry can't silently drift back.
2. `resolveAgentExecutable` prefers `def.bin` when both candidates are
present (primary always wins).
3. `resolveAgentExecutable` falls back through `fallbackBins` when
`def.bin` is missing (the OpenClaude-only install case).
4. `resolveAgentExecutable` returns null when no candidate is on PATH.
5. `resolveAgentExecutable` handles defs with no `fallbackBins` field
(no-regression guard for codex / gemini / opencode / …).
The 4 filesystem-backed cases skip on Windows because resolveOnPath
walks PATHEXT extensions there and the fixture files don't carry them;
the declarative AGENT_DEFS assertion runs on every platform.
All 230 daemon tests pass locally (was 225 before this PR).
Closes#235
This change moves Claude Code and GitHub Copilot CLI to stdin-based prompt delivery, aligning them with other agents. By doing so, we can bypass OS command-line length limits on Windows and set shell: false universally, improving security and robustness.
Integration of createCommandInvocation ensures that Windows npm shims (.cmd/.bat) are correctly handled while keeping shell: false, addressing the P1 warning from the automated review and maintaining consistent execution across platforms.
* fix(daemon): clamp codex reasoning effort to model-supported values
Codex's CLI accepts `none | minimal | low | medium | high | xhigh`, but
real models support narrower subsets — picking Minimal in OD's settings
made every chat turn fail with `agent exited with code 1` because the
OpenAI Responses API rejects `reasoning.effort=minimal` on the gpt-5.2
through 5.5 family with `unsupported_value`.
Add a client-side clamp that maps the user's pick to one the chosen
model accepts (Minimal -> low for gpt-5.5 family, xhigh -> high for
gpt-5.1, gpt-5.1-codex-mini caps at medium). Default / unknown model
is treated as gpt-5.5 since that's codex's current default. The user's
UI pick stays valid; the wire request gets a level the model can use.
* docs(daemon): clarify clampCodexReasoning unknown-model behavior
Per PR review — make explicit that unknown / future model ids pass
through unclamped on purpose. The server error then surfaces as the
signal that a new model family rule should be added here, rather than
silently downgrading every unrecognized id.
* test(daemon): cover codex reasoning-effort clamp matrix
Adds a table-driven test in apps/daemon/tests/agents.test.ts that drives
clampCodexReasoning through the public buildArgs surface. The 18 cases
cover:
- minimal -> low for default / explicit gpt-5.2-5.5
- low / medium / high pass-through on gpt-5.5
- xhigh -> high for gpt-5.1
- gpt-5.1-codex-mini cap (low/minimal -> medium, high/xhigh -> high)
- unknown / future models pass through unchanged
- 'default' reasoning omits the -c flag entirely
- path-style model ids (vendor/gpt-5.5-foo) clamp correctly
Tests stay at the wire level (asserting on the args array codex.buildArgs
produces), keeping the helper non-exported and matching the rest of the
file's style.
* docs(daemon): clarify gpt-5.1-codex-mini accepts medium / high
Per PR review — the original "caps at `medium`" wording was misleading
because the clamp returns `high` for `high` / `xhigh` inputs, which
contradicts the comment. The model actually accepts `{medium, high}`,
so describing it as "accepts only `medium` / `high`" matches the
function's behavior.
Logic unchanged.
Claude Code is the last agent that still embeds the composed prompt in
argv (`-p <prompt>`). Composed prompts (system prompt + design system +
skill body + user message) routinely exceed:
- Linux : MAX_ARG_STRLEN ~128 KB per argv entry → `spawn E2BIG`
- Windows: CreateProcess command-line cap ~32 KB direct, ~8 KB via
`.cmd` shim → `spawn ENAMETOOLONG`
the prompt through `child.stdin` (`promptViaStdin: true`). Claude was
explicitly excluded at the time. #97 mitigated the Windows case for
Claude with a `.od-prompt.md` temp-file fallback, but that path still
leaves a stray file in the project, doesn't trigger on Linux, and is
slower than the stdin route the other agents already use.
This change brings Claude in line with the rest:
- Drop the prompt from argv; keep the bare `-p` print-mode flag
- Set `promptViaStdin: true` so server.ts pipes `composed` on stdin
- Add a regression test that asserts the prompt body never lands in
argv even at 200 KB
Verified locally that `claude -p --output-format stream-json --verbose`
(no positional prompt) reads the prompt from stdin under the default
`--input-format text`, which has no length cap.
Fixes#82
Refs #100, #52
* feat(daemon): add pi coding agent adapter
Add pi (https://pi.dev) as a supported coding agent, using its
--mode rpc JSON-RPC protocol over stdio for structured event streaming.
Changes:
- apps/daemon/pi-rpc.js: new RPC session handler that drives pi's
--mode rpc protocol, translating typed agent events (text_delta,
thinking_delta, tool_use, tool_result, usage, status) into the
daemon's UI event format. Auto-resolves extension UI requests
(fire-and-forget consumed, dialogs auto-approved) so pi stays
unblocked in the headless web UI. Kills the process after agent_end
since pi's RPC process is designed for multi-prompt sessions.
- apps/daemon/agents.js: add pi agent definition with custom
fetchModels (pi --list-models outputs to stderr, not stdout),
575+ models from 20+ providers, reasoning/thinking level support
via --thinking flag, and streamFormat 'pi-rpc'.
- apps/daemon/server.js: wire pi-rpc stream format to
attachPiRpcSession; skip stdin.end() for pi-rpc since the RPC
session manages stdin bidirectionally.
- apps/daemon/acp.js: export createJsonLineStream for reuse by
pi-rpc.js.
- apps/daemon/pi-rpc.test.mjs: 19 unit tests covering model list
parsing (TSV, dedup, edge cases), RPC event translation (text,
thinking, tools, usage, compaction, retry), sendCommand wire
format, extension UI auto-resolution.
- e2e/tests/structured-streams.test.ts: add pi RPC tool_use/tool_result
event mapping test alongside existing Claude/Copilot fixtures.
Verified end-to-end: daemon /api/chat → pi RPC → SSE stream with
status, text_delta, usage, and tool events. Live E2E test passes
(OD_E2E_RUNTIMES=pi). All 59 project tests green.
* refactor(daemon): migrate pi-rpc to TypeScript
Follow upstream #118 TypeScript migration convention: rename
pi-rpc.js → pi-rpc.ts and pi-rpc.test.mjs → pi-rpc.test.ts
with @ts-nocheck header (same as all other daemon modules).
Import paths remain ./pi-rpc.js per NodeNext module resolution.
* fix(daemon): avoid duplicate usage events in pi-rpc handler
Pi emits both message_end and turn_end per turn, both carrying
usage data. Emitting from both handlers caused double-counting
in the UI and any consumer that aggregates usage.
Remove usage emission from the message_end branch since turn_end
is the canonical per-turn usage source. Keep tool call extraction
in message_end (unique data not available in turn_end).
Add regression test confirming exactly one usage event is emitted
when both message_end and turn_end carry usage for the same turn.
Addresses Copilot P2 review on PR #117.
* fix(daemon): scope pi RPC id counter per session, bump graceful shutdown
Move nextRpcId and sendCommand inside attachPiRpcSession as local
state, matching the pattern in acp.ts where nextId is scoped per
session. Prevents RPC id collisions across concurrent /api/chat
requests.
Bump post-agent_end SIGTERM grace period from 2s to 5s and make it
configurable via PI_GRACEFUL_SHUTDOWN_MS env var for resource-
constrained machines.
Add test confirming concurrent sessions get independent id sequences.
* fix(daemon): wrap parser.feed in try-catch in pi-rpc
Catch errors from parser.feed and route them through the
existing fail() handler instead of letting them propagate
as unhandled exceptions.
* feat(dev): add desktop tools-dev control plane
* refactor(sidecar): split Open Design contracts
Move Open Design-specific sidecar protocol definitions into @open-design/contracts so sidecar and platform can remain descriptor-driven primitives.
* refactor(daemon): organize package sources
Keep daemon app code, tests, and sidecar entrypoints in separate package directories so each layer can be built and verified independently.
* chore(repo): streamline maintenance entrypoints
Centralize agent guidance by directory and reduce root command chains while preserving the existing build scope.
* docs: translate agent guidance to English
* fix(sidecar): tolerate stale IPC sockets
Remove stale Unix socket files only after confirming no listener is active, so tools-dev can restart after unclean shutdowns.