* fix(web): remove Ingest source panel from Automations tab (#2711)
* fix(web): remove Ingest source panel from Automations tab
The Automations tab carried a free-form "Ingest source" composer that
let users paste arbitrary content (URL, repo path, connector event,
chat snippet) and turn it into a source packet plus evolution proposals.
The form was confusing next to the routine/template flow on the same
page, exposed an internal canonicalization concept users don't need to
think about, and shipped before the surrounding evolution-proposal flow
was wired into a coherent end-to-end story.
Drop the UI surface only:
- Remove the <section className="automations-ingest"> block, the
Template / Source / Compression / Connector selects, the title/source
ref/content fields, the recent-packets list, and the Ingest button.
- Drop the now-dead local state (sourcePackets / sourceForm /
ingestingSource), the patchSourceForm and submitSourceIngestion
helpers, the SOURCE_KIND_OPTIONS / COMPRESSION_OPTIONS constants, the
SourceIngestionForm type and DEFAULT_SOURCE_FORM, the
/api/automation-source-packets refresh leg, and the sourcePackets
side-write inside crystallizeRun.
- Remove the matching .automations-ingest / .automation-ingest-* CSS
block (plus the two responsive overrides) from tasks.css.
- Delete the test case that drove the form in TasksView.templates.test.
Backend stays intact: apps/daemon/src/automation-ingestions.ts, the
POST /api/automation-ingestions route, `od automation ingest` CLI, the
routine-evolution call site, and the AutomationContentPacket /
AutomationSourceKind / AutomationTokenCompressionMode contracts all
remain, since routine scheduling still depends on them.
* fix(web): drop crystallize test assertion on removed packet list
The crystallize test was asserting that the new content packet's title
shows up on the page. That assertion only passed because the daemon
response was being side-written into the deleted sourcePackets state
and rendered in the Ingest source recent-packets strip. With that UI
removed, the packet title has no surface to land on; the proposal title
(`Skill: Artifact polish loop run`) is still asserted and remains the
real signal that crystallize succeeded.
* test(e2e): restore #2305 / #2578 e2e regressions lost in PR #2461 merge
Sync merge c14baf07d (Merge origin/main into release/v0.8.0 inside PR
#2461) took the release-side blob of these three files, silently
reverting #2305 (chore(e2e): improve test framework quality) and #2578
([codex] test(e2e): harden settings and entry regressions):
- e2e/ui/settings-memory-routines.test.ts: 363 -> 2120 lines
- e2e/ui/project-management-flows.test.ts: 758 -> 1080 lines
- e2e/ui/settings-api-protocol.test.ts: 205 -> 390 lines
Restore each file to the version at the main parent of the merge
(866661ac6). No new edits — pure restoration of merged-out content.
* chore(assets): restore #2561 / #2401 brand mark refreshes lost in PR #2461 merge
Sync merge c14baf07d also reverted these three asset blobs to the
release-side (pre-refresh) versions:
- apps/landing-page/public/apple-touch-icon.png: 6122 -> 7983 bytes (#2561)
- apps/landing-page/public/favicon.png: 916 -> 1504 bytes (#2561)
- apps/web/public/app-icon.svg: 672 -> 4964 bytes (#2401/#2439 — optically
centered title-bar inner mark)
The companion landing changes from #2561 (sub-page-layout.astro,
index.astro, favicon.ico, logo.webp) survived the merge; only the
PNG/SVG blobs landed back at the release-side. Restore each to the
version at the main parent of the merge (866661ac6).
* test(web): drop dead automation-ingest-select.test.ts (follow-up to #2711)
#2733 (preserve ingest select chevron) and its #2609 follow-up shipped
on top of the broken main from PR #2461, which kept the Ingest source
panel that #2711 had already deleted on release. Now that the cherry-
pick of #2711 in this PR removes that panel and its .automation-ingest*
CSS, this test loses its subject (".automation-ingest-field select"
class no longer exists) and goes red.
Remove the test instead of keeping a broken assertion against deleted
markup. The shared readExpandedIndexCss helper is still used by other
style tests.
* feat(plugins): site-wide plugin detail pages, share-to-site links, landing deploy trigger
Why: a merged plugin PR didn't redeploy the landing site (plugins/** was missing from the deploy paths), and the desktop Share menu copied a local/404 link instead of the public marketplace URL. The landing plugin routing left by the detail-page rework also 404'd: the locale listing's cards used a multi-segment href while detail pages were single-segment, and only 388 bundled _official plugins had pages.
What changed:
- Deploy: landing-page deploy/ci trigger on plugins/**, and skip the slow previews step on an exact cache hit (cache key aligned across both workflows so a PR-built cache is reused by main).
- Share URL: packages/contracts/plugin-url.ts owns the single-segment plugin URL scheme; the web Share menu and the landing site both derive links from it. Web links now point at https://open-design.ai/plugins/<slug>/.
- Full detail coverage: detail pages now cover all 403 local plugins (_official incl. atoms + community), each rendered from its local manifest. Fixes the locale-listing 404s and the community manifest-name/catalog-id (- vs /) mismatch.
- Self-host: daemon exposes OD_SITE_ORIGIN via /api/app-config; web falls back to the canonical origin until the daemon answers.
Validation: pnpm guard, pnpm typecheck (all packages), contracts + web tests green, and a full build E2E confirming all 403 catalog ids and locale-listing cards resolve to built detail pages (0 missing).
* chore: retrigger CI
* ci(landing): carry plugins/** trigger + previews cache-hit into #2994 split workflows
Merged origin/main, which split landing deploy into staging + manual production (#2994). git auto-migrated my landing-page-deploy.yml changes into landing-page-staging.yml via rename detection (plugins/** path, fallback-preview-card.ts cache key, cache-hit skip all carried). The new manual landing-page-production.yml didn't have them, so add the previews cache-key alignment + cache-hit skip there too (plugins/** path is N/A — production is workflow_dispatch only).
* fix(ci): wrangler-action uses pnpm so it tolerates landing's workspace dep
This PR added @open-design/contracts (workspace:*) to apps/landing-page/package.json so the landing site can share the plugin-url slug rules. But the landing deploy/preview steps run cloudflare/wrangler-action with packageManager: npm in workingDirectory apps/landing-page, and 'npm i wrangler' chokes on the workspace: protocol (EUNSUPPORTEDPROTOCOL), failing 'Validate landing page'. Switch all three landing wrangler-action steps (staging / ci preview / production) to packageManager: pnpm, which is workspace-aware.
* test(e2e): bundled plugins now offer the README badge
After this branch, buildPluginShareUrl returns a public open-design.ai link for bundled plugins (not just official-marketplace ones), so the home-starter share menu now shows 'Copy README badge'. Update the assertion from toHaveCount(0) to toBeVisible().
* fix(landing): drop @open-design/contracts dep, use a landing-local slug helper
Per review on #2999: the marketing site must not import @open-design/contracts (AGENTS.md boundary — it's the web/daemon product-runtime contract layer). Move the slug/path helpers into landing-local app/_lib/plugin-slug.ts; the web client keeps contracts' plugin-url. The two derive the same scheme and are verified in lockstep by the e2e route check (403 share URLs -> 403 detail pages, 0 missing). landing no longer has a workspace dep, so revert the wrangler-action packageManager back to npm.
* fix(landing): include plugins/_official in previews cache key
Per review on #2999: generate-previews.ts builds bundled-plugin preview jobs from plugins/_official/**/open-design.json and renders fallback cards from manifest fields (title/description/mode/scenario/tags). With plugins/** now triggering the workflow but the cache key not hashing plugin inputs, a plugin-only PR/merge could exact-hit an old cache and skip the preview regen, shipping with a stale or missing /previews/plugins/<manifest-id>.png. Add plugins/_official/** to the cache key in all three landing workflows (ci, staging, production). community is not currently covered by generate-previews so its glob is omitted.
* fix(plugins): include community marketplace installs in share gate
hasPublicPage now covers sourceMarketplaceId === 'community' so the
README badge and public detail link surface for community installs.
Community manifest names carry a community- prefix that diverges from
the landing-page route slug, so URL derivation uses sourceMarketplaceEntryName
(community/<folder>) instead — pluginDetailSlug takes the last segment,
matching the /plugins/<folder>/ route the landing page emits.
Adds component tests for buildPluginShareUrl, badge copy, and the
Open-in-marketplace link for a community/registry-starter record.
Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code)
---------
Co-authored-by: mrcfps <mrc@powerformer.com>
* feat(runtimes): register AMR (vela) as an ACP stdio agent
AMR is the vela CLI's ACP runtime mode. `vela agent run --runtime opencode`
speaks ACP JSON-RPC over stdio (see vela's
`specs/current/runtime/manual-agent-run-openrouter.md`); per
`docs/new-agent-runtime-acp.md` we expose it through the same `streamFormat:
'acp-json-rpc'` transport that already powers Hermes, Devin, Kimi, etc.
The new `defs/amr.ts` is the entire wiring — `buildArgs` returns
`['agent', 'run', '--runtime', 'opencode']`, `fetchModels` reuses
`detectAcpModels`, and the fallback list seeds the OpenRouter ids vela's
e2e baseline uses. `executables.ts`/`app-config.ts`/`metadata.ts` get the
matching `VELA_BIN`/`VELA_LINK_URL`/`VELA_RUNTIME_KEY`/`VELA_OPENCODE_BIN`
allowlist + install/docs URLs, so users can configure the per-agent env in
Settings without leaking into other adapters.
Coverage: `tests/fixtures/fake-vela.mjs` is a minimal ACP stub that returns
the documented `initialize` / `session/new` / `session/set_model` /
`session/prompt` shapes; `tests/amr-acp-integration.test.ts` spawns it via
`child_process.spawn` and drives a full turn through `attachAcpSession` and
`detectAcpModels`, so the ACP transport contract for AMR is end-to-end
verified locally even before a real `vela` binary is installed.
Validated:
- pnpm guard
- pnpm typecheck (all workspace projects)
- pnpm --filter @open-design/daemon test (2881/2881)
Deferred: real OpenRouter-backed turn through a built `vela` binary —
the runtime def needs no changes for that path, only `VELA_RUNTIME_KEY`
and `VELA_LINK_URL` in env (or Settings).
* fix(runtimes/amr): pin a concrete default model and bare openai ids
End-to-end validation against a freshly-built `vela` (nexu-io/vela@main)
+ OpenRouter surfaced two contract details the first AMR runtime def
got wrong:
1. vela rejects `session/prompt` with `session/set_model must be called
before session/prompt`. attachAcpSession in apps/daemon/src/acp.ts
skips set_model whenever the picked model is the synthetic 'default'
id, so AMR's fallback list must NOT include DEFAULT_MODEL_OPTION. The
def now ships a concrete `gpt-5.4-mini` as both `fetchModels`'
default option and `fallbackModels[0]`, which makes attachAcpSession
always send a real `session/set_model` for AMR turns.
2. `vela --runtime opencode` auto-prepends `openai/` to whatever modelId
it forwards to opencode's openai provider. With OpenRouter-style ids
like `openai/gpt-5.4-mini`, opencode receives the double-prefixed
`openai/openai/gpt-5.4-mini` and replies `ProviderModelNotFoundError`.
The new fallback list ships the bare ids opencode's openai registry
actually knows about (gpt-5.4, gpt-5.4-mini, gpt-5.4-fast, etc.).
Stub + tests:
- tests/fixtures/fake-vela.mjs now enforces the set_model gate the same
way real vela does, so a regression that silently goes back to
model: 'default' would surface as a fatal error in tests instead of a
hidden production failure.
- tests/amr-acp-integration.test.ts pins both contracts: no 'default' /
no 'openai/' prefix in fallbackModels, and a negative case that
asserts session/prompt fails when no model is set.
Adds `apps/daemon/scripts/verify-amr-real-vela.mjs` — a small dev-time
runner that drives `attachAcpSession` against a real `vela` binary and
prints the daemon's chat events, so future protocol drift can be checked
against an actual OpenRouter call.
Verified locally: `vela agent run --runtime opencode` + OpenRouter
returns the prompted string ("AMR-E2E-PASS") through the full daemon
pipeline; daemon test suite stays 2883/2883.
* fix(runtimes/amr): substitute concrete model when chat run sends 'default'
A plugin-driven AMR run from the UI surfaced a real-world hole in the
prior commit:
json-rpc id 3: session/set_model must be called before session/prompt
The Default-design-router plugin (and any caller that doesn't pin a
real model) sends `model: 'default'` straight through, which the AMR
runtime def cannot accept — vela rejects `session/prompt` without
`session/set_model` and attachAcpSession skips set_model whenever
model === 'default'. Just leaving DEFAULT_MODEL_OPTION out of the
adapter's `fallbackModels` is not enough: the chat-run handler in
server.ts still forwarded 'default' verbatim.
This adds `resolveModelForAgent(def, resolved, env?)` as the
single source of truth for the substitution:
1. If the caller picked a real id, pass it through.
2. Else, if `def.defaultModelEnvVar` is set and the daemon process
env has a non-empty value for it, return that (operator escape
hatch — see below).
3. Else, if the def's `fallbackModels` does NOT contain a 'default'
id, return `fallbackModels[0].id`.
4. Else, return the original value (the historic shape — defs that
list 'default' themselves are untouched).
AMR sets `defaultModelEnvVar: 'VELA_DEFAULT_MODEL'`, so when
opencode's openai-provider registry deprecates `gpt-5.4-mini`
upstream, an operator can swap the fallback id without a code change
by exporting `VELA_DEFAULT_MODEL=gpt-5.5` before launching tools-dev
/ od. Worth noting the env var must live in the daemon's `process.env`
(Settings-UI per-agent env values only reach the spawned child, not
the daemon's resolver) — the new field's docblock spells this out.
Coverage:
- `tests/runtimes/resolve-model.test.ts` — 8 unit tests covering all
four resolver branches plus the env-override happy path / fallback /
ignore-when-user-picked-a-real-id case.
- `pnpm --filter @open-design/daemon typecheck` clean.
* chore(runtimes/amr): move AMR to the top of the base agent list
So `AMR (vela)` shows up first in the agent picker / status views,
ahead of claude / codex. Pure ordering change; no behavior delta.
* feat(amr): Sign-in / Sign-out button on the AMR Settings card
The first half of the AMR work assumed the operator would set
VELA_RUNTIME_KEY / VELA_LINK_URL on the daemon process and never
surfaced login state to users. This adds the missing UX so a fresh
install can drive the full path from Settings:
- GET /api/integrations/vela/status reads ~/.vela/config.json
for the active profile and returns { loggedIn, profile, user }
(without leaking the runtime/control keys themselves).
- POST /api/integrations/vela/login spawns `vela login` once
(409 if one is already in flight). The vela CLI opens the user's
browser to the device-authorization page itself — Open Design
only needs to kick the subprocess off.
- POST /api/integrations/vela/logout removes ~/.vela/config.json
so the next status read returns logged-out.
`AmrAgentCard` is a dedicated agent-card component for AMR because
the existing `<button>` row can't host an interactive sub-control
(nested interactive elements). It polls /status after a login click
until the daemon reports loggedIn=true (or 5 minutes elapse), and
exposes a Sign-out action on hover. Other adapters (claude, codex,
hermes, …) keep their existing `<button>` card.
i18n: 8 new keys (settings.amrLogin / Logout / LoggingIn / etc.)
added to en + zh-CN. Other locales spread `en` and inherit the
English copy until translations land.
Coverage:
- `tests/integrations/vela.test.ts` pins the config.json reader
against a tmp HOME — including the negative case where a profile
has user info but no runtimeKey (still logged-out), and the
secret-leak guard ("rt-secret-*" must not appear in the projection
payload).
- `tests/components/AmrAgentCard.test.tsx` covers all four UI
states (logged-out, logging-in, logged-in, logging-out) plus the
click-propagation invariant the divergent card was built to keep.
`pnpm --filter @open-design/daemon test` 2901 / 2901 passing.
`pnpm --filter @open-design/web test` 1719 / 1719 passing.
`pnpm typecheck` + `pnpm guard` clean.
Dev script side-effects: `apps/daemon/scripts/verify-amr-real-vela.mjs`
no longer requires both VELA_RUNTIME_KEY and VELA_LINK_URL — if
VELA_PROFILE is set, the vela CLI is allowed to resolve credentials
from `~/.vela/config.json`. Added the two AMR `.mjs` fixtures to
`scripts/guard.ts` allowlist with the executable-fixture / dev-runner
rationale.
* fix(connection-test): substitute model for AMR before attachAcpSession
The chat-run path in server.ts already routes the requested model through
`resolveModelForAgent` so AMR / vela (whose CLI demands an explicit
`session/set_model` before `session/prompt`) gets the def's first
concrete fallback id when the chat run ships `model: 'default'`.
`connectionTest.ts` was wiring `attachAcpSession({ ..., model: model ?? null })`
directly, which made the Test Connection button on the AMR Settings
card deadlock with the same `session/set_model must be called before
session/prompt` error the chat-run path already handles — surfaced as a
permanent "Testing connection…" spinner in the UI.
Reuse the same helper here so Test Connection mirrors chat-run behavior.
* test(amr): three-layer end-to-end coverage for the AMR login + turn flow
The PR up to this point shipped runtime + UI code with unit-level Vitest
coverage. This commit adds the cross-layer regression net the live demo
relied on:
1. apps/daemon/tests/integrations/vela.routes.test.ts (HTTP, Vitest)
Spins up the real daemon Express app via `startServer({port:0,...})`,
persists `agentCliEnv.amr.VELA_BIN = <fake>` into app-config.json,
and exercises every /api/integrations/vela/* endpoint against the
extended fake-vela stub:
- status reads ~/.vela/config.json under various states
- login spawns the fake, waits for config.json to appear, returns
pid + startedAt + profile
- 409 already-running guard with the stub's delay knob
- logout removes the file (idempotent)
- secrets (runtimeKey / controlKey) never leak in the projection
- login → status round-trip flips loggedIn=false → true
2. e2e/tests/amr/turn.test.ts (tools-dev orchestrated, Vitest)
Boots a namespaced daemon + web pair through `createSmokeSuite`,
inlines a self-contained fake `vela` binary that handles BOTH
`vela login` (writes ~/.vela/config.json) and
`vela agent run --runtime opencode` (ACP stdio with the
`session/set_model must precede session/prompt` gate the real binary
enforces), then drives a complete /api/runs lifecycle for
`agentId: 'amr', model: 'default'` and asserts the assistant message
captures the fake's streamed text. This is the test that would have
surfaced today's plugin-default-model regression (the `set_model
before prompt` error) at PR time instead of demo time.
3. e2e/ui/amr-login-pill.test.ts (Playwright)
Mocks /api/agents + /api/integrations/vela/{status,login,logout}
to drive the Settings AMR card through the full Sign in → Signed in
→ Sign out cycle. Pins the AmrLoginPill polling contract and the
aria-label semantics (the pill's accessible name is "Sign out" once
logged in, regardless of which label the hover-state text shows).
fake-vela.mjs extensions:
- Handles `vela login` argv by writing
~/.vela/config.json for the active VELA_PROFILE and exiting 0 —
mirrors real vela's on-disk side-effect without the device-auth
loop.
- FAKE_VELA_LOGIN_DELAY_MS knob so route tests can observe the
in-flight state of the spawn lifecycle.
- FAKE_VELA_LOGIN_USER_EMAIL / _USER_PLAN to assert the surfaced
user fields end-to-end.
Validated:
- `pnpm guard` + `pnpm typecheck` (all workspace projects)
- `pnpm --filter @open-design/daemon test`: 2998 / 2998 passing,
including the new 8-test integration suite.
- `cd e2e && pnpm test tests/amr`: 1 / 1 passing.
- `cd e2e && pnpm exec playwright test ui/amr-login-pill.test.ts`:
1 / 1 passing (6.7s).
* feat(amr): package native cli and refine login ui
* feat(amr): wire vela cli beta packaging
* docs(amr): document vela ci packaging review
* docs(amr): refine vela ci integration review
* fix(ci): refresh nix pnpm dependency hashes
* fix(pack): clean up Vela CLI packaging
* fix(pack): bundle Vela CLI support files
* fix(amr): recover login attempts from stale auth state
* test: expand AMR and automations coverage
* fix(amr): address review follow-ups
* test(web): align tasks fixtures with contracts
* fix(daemon): type wildcard route params
* fix(ci): refresh PR merge validation
* fix(amr): clear env credentials on logout
* feat(settings): inline local CLI model configuration
* fix(amr): recognize daemon env credentials
* [codex] Fix Vela companion packaging (#2979)
* Fix Vela companion packaging
* Update Nix pnpm dependency hashes
* [codex] Surface AMR account failures (#2980)
* fix: surface AMR account failures
* fix: cover AMR recovery error guidance
* chore: bump beta base version to 0.8.1 (#2990)
* Fix AMR profile and packaged runtime review issues
* Detect packaged AMR OpenCode companion tree
* feat(web): polish AMR frontend flows
* Polish AMR onboarding card
* fix: read AMR login state from dot-amr config (#3048)
* test: tighten AMR credential and packaging coverage
* test: restore AMR executable test env helper
* [codex] Fix packaged mac Dock identity and AMR label (#3076)
* Fix packaged mac sidecar Dock identity
* Rename AMR assistant label
* Fix AMR live models and dot-amr login state (#3073)
* fix: read AMR login state from dot-amr config
* fix: load live AMR models before runs
* fix: point AMR onboarding link to production wallet
* fix: address AMR model review feedback
* fix: persist live AMR model fallback
* [codex] Fix AMR link catalog model ids (#3088)
* Fix packaged mac sidecar Dock identity
* Rename AMR assistant label
* Fix AMR link catalog model ids
* Fix AMR model normalization typecheck
* Use live AMR model for default runs
* fix: polish AMR runtime settings UI
* Accelerate AMR startup defaults (#3092)
* Surface AMR insufficient balance wallet URL (#3099)
* fix(web): polish onboarding controls (#3112)
* fix(web): show CLI scan loading state
* Avoid duplicate AMR wallet recharge links (#3117)
* Avoid duplicate AMR wallet recharge links
* Use Vela CLI 0.0.3 test package
* chore(nix): refresh pnpm deps hash
* Fix AMR wallet guidance display
---------
Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
* chore(pack): pin Vela CLI 0.0.3-test.1 (#3127)
* chore(nix): refresh pnpm deps hash
* chore(pack): pin Vela CLI 0.0.3
* chore(nix): refresh pnpm deps hash
* fix(web): suppress AMR exit 130 fallback (#3136)
* feat(web): nudge users to hosted AMR on model/auth/quota failures (#3083)
* feat(web): nudge users to hosted AMR on model/auth/quota failures
When a non-AMR agent run fails with an auth / quota / upstream model
error, surface an inline nudge under the error pill linking to Open
Design's hosted AMR gateway (https://open-design.ai/amr). The nudge
fires `surface_view` (element=run_failed_toast) on impression and
`ui_click` (element=go_amr) on the link.
Also teach the daemon to classify CLI-agent auth/quota/upstream failures
(Claude Code, codex, ...) into specific API error codes
(AGENT_AUTH_REQUIRED / RATE_LIMITED / UPSTREAM_UNAVAILABLE) instead of
the generic AGENT_EXECUTION_FAILED, so both the error message and the
nudge key off accurate codes. AMR's own runs are excluded from the
nudge — they keep the dedicated sign-in / recharge affordances.
* feat(web): rework failed-run AMR guidance into per-case error UI
Replace the single inline nudge with a per-case failed-run experience
driven by the run's error code + agent:
- The error card is now neutral gray (was red) and always carries a
retry button; it is driven by the persisted per-message error event so
it survives a reload.
- Non-AMR agent hitting a model/auth/quota wall: a theme-color promotion
card under the error card offers "switch to AMR & retry" — switches the
run to AMR, opens Settings on the AMR card, and auto-retries once the
account signs in (ProjectView polls vela login status, independent of
the Settings pill lifecycle, with success / 5-min-timeout / unmount
exits).
- AMR agent unauthorized: clearer copy + an "authorize & retry" button.
- AMR agent out of balance: clearer copy + a "top up" button to the AMR
wallet, with manual retry.
- Settings AMR card: when opened from the nudge, it scrolls into view and
pulses, and an authorize-button coachmark (a fake hand cursor that
rises in and dismisses on hover) points at the sign-in control when not
yet authorized.
analytics: surface_view (run_failed_toast) on the promotion card and
ui_click (go_amr) on its action are retained. i18n adds chat.amrCard.*
and chat.amrError.* (en / zh-CN / zh-TW translated; other locales fall
back to en) and drops the old chat.amrErrorGuidance keys.
* fix(daemon): require status context for numeric service-failure codes
Per review on #3083: the model-service classifier matched bare HTTP
status numbers (`500`, `502`, `429`, `401`), so ordinary CLI output like
`line 500`, `read 502 bytes`, or `exit code 401` could be misclassified
as a provider outage / auth wall and wrongly surface the AMR nudge. Now
a status number only counts when it carries explicit context (`HTTP 500`,
`status 503`, `code: 401`, `502 Bad Gateway`); textual provider phrases
(overloaded, bad gateway, service unavailable, rate limit, …) are
unchanged. Adds fixtures proving unrelated numeric output stays null.
* fix(web): keep error pill for failed runs ChatPane's card doesn't cover
Per review on #3083: the per-message gray error pill was suppressed for
every persisted error status event, but ChatPane only renders the
replacement top-level error card for `retryableAssistantMessage` (the
last failed assistant). So a failed turn that is no longer last (after a
follow-up) or an older failed run in history showed neither the pill nor
the card — its error detail vanished, undercutting reload/history
survival. ChatPane now passes `errorCardOwnerId` (the assistant id whose
error the card represents); AssistantMessage suppresses only that one
pill and keeps rendering StatusPill for all other error events.
* fix(daemon): don't treat a process exit code as an HTTP status
Follow-up to review on #3083: the status-context helper accepted a bare
`code` prefix, so `exit code 401` / `process exited with code 429` still
matched and got classified as AGENT_AUTH_REQUIRED / RATE_LIMITED (the
very `exit code 401` case the comment calls out as noise). `code` now
only counts when qualified (`status code` / `error code` / `response
code`) or punctuation-bound (`code: 401`); bare `exit code N` no longer
matches. Adds fixtures for exit-code lines returning null.
* chore(web): translate AMR card / error keys for 16 remaining locales
PR #3083 added 10 new `chat.amrCard.*` / `chat.amrError.*` keys but only
provided en/zh-CN/zh-TW translations; the other 16 locales fell back to
English. Translate the card title/body, three chips, primary CTA, and
the AMR self-error (auth / balance) messages and buttons for ar, de,
es-ES, fa, fr, hu, id, it, ja, ko, pl, pt-BR, ru, th, tr, uk.
* fix(amr): address review feedback on #2355
Targeted fixes for the unresolved review threads on #2355. Each fix
includes / updates a focused test.
- runtimes/executables.ts: `packagedVelaOpenCodeCompanionTree` now
verifies the inner `opencode` executable exists + is runnable, not
just the directory. This closes the false-positive availability path
that let `detectAgents()` surface AMR as available even when the
packaged companion was empty / partially copied (mrcfps, 4 threads).
- runtimes/executables.ts: `resolveAmrOpenCodeExecutable` now prefers
the bundled `<OD_RESOURCE_ROOT>/bin/libexec/opencode/opencode` over a
stale `opencode` on the user's PATH, so packaged AMR builds can't be
hijacked by a global installation.
- web/EntryShell.tsx: when the Local CLI scan returns an available
agent and the previously-selected agent is AMR, switch the selection
to the first available local agent so the runtime and persisted
agent agree before Continue.
- server.ts (model-probe branch): for AMR, check `readVelaLoginStatus`
BEFORE rejecting on an empty live-model catalog — a signed-out user
was getting `AMR_MODEL_UNAVAILABLE` ("choose a model") instead of
the correct `AMR_AUTH_REQUIRED` (sign-in affordance).
- server.ts (default model fallback): if the user asked for the AMR
agent default and the cached id is no longer in the FRESH catalog,
fall back to `liveModels[0]` from the probe instead of rejecting the
run as `AMR_MODEL_UNAVAILABLE`.
- integrations/vela.ts: route `vela login` through
`createCommandInvocation` so an npm/Node-style `vela.cmd` / `.bat`
shim on Windows gets the correct `cmd.exe /d /s /c …` wrapping with
verbatim args (matches `execAgentFile` / chat-run spawning).
- tools/pack/src/linux.ts: in containerized Linux builds, bind-mount
the host directory of `OPEN_DESIGN_VELA_CLI_BIN` and rewrite the env
to the container-side path. The host path was being passed in as-is
even though the default container only mounts /project, /tools-pack
and cache/home — `copyOptionalVelaCliBinary` saw a missing path.
Deferred (out of scope for this PR):
- `od amr status/login/logout/cancel` CLI subcommands (AGENTS.md
UI/CLI dual-track rule, server.ts:5763) — sizable surface; tracked
for a separate focused PR.
- Strict `--require-vela-cli` for Windows + mac-x64 beta builds:
prematurely blocked — `@powerformer/vela-cli` only publishes the
`darwin-arm64` platform binary today; adding the flag elsewhere
would fail the builds. Revisit once win/x64/linux binaries ship.
* fix(amr): hoist sendAmrAccountFailure above the AMR catalog preflight (TDZ)
The new signed-out AMR branch in the catalog preflight at server.ts:10875
calls `sendAmrAccountFailure(...)` to emit AMR_AUTH_REQUIRED, but the
const declaration sat ~100 lines below at the outer function scope. Because
`const` is TDZ-aware, that branch would have thrown `ReferenceError:
Cannot access 'sendAmrAccountFailure' before initialization` for the
exact users it tries to help — defeating the original intent.
Hoist the helper to just above the AMR preflight block so it's available
to every AMR code path in this function. Behavior elsewhere is unchanged.
Also rerun the daemon test suite: `launch.test.ts > resolveAgentLaunch
uses packaged built-in Vela for AMR` was creating the
`<resourceRoot>/bin/libexec/opencode/` companion *directory* only, but
this PR's earlier tightening of `packagedVelaOpenCodeCompanionTree`
also requires the inner `opencode` executable. Add it to that fixture
to match the new contract; the test was a sibling of the executables /
env-and-detection fixtures already updated in 13fc4f4.
Addresses #2355 review (mrcfps, 2026-05-28).
* feat(web): add hover cancel for AMR login (#3158)
* feat(web): add hover cancel for AMR login
* fix(web): don't bounce AmrLoginPill back to 'Signing in…' after local cancel
Both codex-connector (P2) and looper (CHANGES_REQUESTED) on this PR
flagged the same race in the new local-cancel path: `handleCancelLogin`
dispatches `notifyAmrLoginStatusChanged('login-canceled')` immediately
after `/login/cancel` returns, but the `AMR_LOGIN_STATUS_EVENT` listener
unconditionally re-enters `refresh()` and then restarts polling
whenever `/api/integrations/vela/status` still reports
`loginInFlight: true`.
That is a real race because the daemon's `cancelVelaLogin()` only sends
SIGTERM (escalating to SIGKILL after `LOGIN_CANCEL_KILL_GRACE_MS` =
2000 ms) and keeps the child in `activeLoginProcs` until it actually
exits — so the first `/status` read after a successful cancel can
legally still come back as in-flight. Under that window the pill flips
back to 'Signing in…' and can later surface the timeout/error path even
though the user already canceled, defeating the behavior promised in
the PR description.
Fix the listener instead of every dispatch site: in the
`login-canceled` branch, after the local reset (stopPolling +
setPending(null) + clear refs), optimistically mark every subscribed
pill instance as not-in-flight (`setStatus((c) => c ? { ...c,
loginInFlight: false } : c)`) and `return` — skip the
refresh-and-reconcile branch below entirely. The next explicit refresh
(component mount, user interaction, or a `status-changed` event) will
pick up the daemon's confirmed state once the child has actually
exited.
Add a focused regression test that holds `/api/integrations/vela/status`
at `loginInFlight: true` even after a successful `/login/cancel`,
asserting that the pill stays at the Canceled → Authorize sequence and
never bounces back to 'Signing in…'. This test fails on the pre-fix
listener and passes on the new behavior; existing
'cancels an in-flight AMR sign-in…' and 'reconciles late AMR browser
completion to Signed in after local cancel' tests continue to pass.
Addresses review feedback on #3158 (chatgpt-codex-connector, nettee).
---------
Co-authored-by: lefarcen <935902669@qq.com>
---------
Co-authored-by: a1chzt <chizblank@gmail.com>
Co-authored-by: Amy <1184569493@qq.com>
Co-authored-by: Mason <jinmeihong0201@gmail.com>
Co-authored-by: Caprika <56862773+alchemistklk@users.noreply.github.com>
Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
* Add template social sharing menu
* Update plugin share e2e expectations
* Add additional template social share targets
* Remove Bilibili template share target
* Open social share destinations in new tabs
* Address template share review feedback
* Use canonical public plugin share URLs
* Gate public plugin share links by marketplace provenance
* Update plugin share e2e for local-only badges
* Limit public share URLs to official marketplace
Two CI failures from PR #2461 root-caused to wrong picks in the
merge:
* apps/web/src/components/plugins-home/PluginCard.tsx — reverted to
release-side. The release-side version uses `localizePluginTitle
(locale, record)` / `localizePluginDescription(locale, record)`
to read each plugin's `titleI18n` / `descriptionI18n` fields,
driving the 'localizes plugin card titles' test in
plugins-home-section.test.tsx (which asserts '瑞士国际主义 Deck'
appears under zh-CN). The main-side version replaced that
record-level i18n with hardcoded English + `t()` aria-label
keys — a finer-grained i18n migration but a fundamental loss of
the record-level localization the test exercises. Taking
release-side keeps the test functionality; the aria-label i18n
keys are micro-optimisation we can re-port in a follow-up.
* e2e/ui/settings-local-cli-codex-fallback.test.ts — added the
`SETTINGS_MENU_LABEL` constant declaration that the menu-
dismissal helper (kept from main in c14baf07) references at
line 161. main's diff added the const at the top of the file but
it didn't carry through auto-merge alongside the helper block;
this restores it.
Both fixes verified locally:
- PluginCard now grep-finds locale + localizePluginTitle usage.
- fallback test grep-finds SETTINGS_MENU_LABEL declaration.
PR #2461 sync prep — resolves 14 conflicts merging 84 main-side commits
on top of 58 release-side commits accumulated during the 0.8.0 cycle.
Resolution summary:
Take main (theirs) where main carried deliberate forward progress:
- apps/web/src/components/PluginCard.tsx — 7 hunks, i18n migration:
hardcoded English aria-labels/titles replaced with t() calls keyed
on pluginCard.* (all 8 keys verified present in en.ts).
- apps/web/src/components/TasksView.tsx — 1 hunk, source-ingestion
feature: sortedRoutines (newest-first), sourceIngestionTemplates,
patchSourceForm, submitSourceIngestion. activeCount/pausedCount
semantics preserved (now keyed on sortedRoutines, count unchanged).
- e2e/ui/app.test.ts — new node:fs/promises + tmpdir + path + @/timeouts
imports needed by main-side test helpers.
- e2e/ui/settings-local-cli-codex-fallback.test.ts — menu-dismissal
helper block added by main.
Keep both sides where each added a different field to the same object
literal:
- apps/web/src/components/ProjectView.tsx (locale + analyticsHints
spread).
- apps/web/src/components/DesignSystemFlow.tsx (locale + analyticsHints).
Take release (ours) where release carried deliberate work that ships
0.8.0:
- CHANGELOG.md — release-side 0.8.0 entry + PR link refs; main's
Unreleased section was the same body of work, now finalized.
- apps/landing-page/public/{apple-touch-icon,favicon}.png +
apps/web/public/app-icon.svg — release-side visual refresh assets
consistent with 0.8.0 stable ship.
- tools/pack/src/linux.ts — packageVersion const required by line 466;
taking main's empty line would build-error.
- e2e/ui/project-management-flows.test.ts +
e2e/ui/settings-api-protocol.test.ts +
e2e/ui/settings-memory-routines.test.ts — release-side release-smoke
hardening (shangxinyu1 + PerishFire) takes precedence on overlap.
Closes-issue / unblocks: PR #2461 sync release/v0.8.0 → main.
* fix(tools-dev): preserve web origin trust on web start
Restart daemon/web when the trusted web port is missing, and reuse the active web port during repeated starts so run web and start web keep app-config origin checks aligned.
Generated-By: looper 0.0.0-dev (runner=worker, agent=opencode)
* fix(plugins): refresh official registry bundled count
Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
* fix(tools-dev): preserve daemon/web reserved ports
Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
* fix(tools-dev): preserve daemon reuse on web start
Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
* fix(tools-dev): preserve running daemon port on web reuse
Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
* fix(tools-dev): reserve explicit web port before daemon allocation
Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
* test(web): stabilize media provider reload flash timing
Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
* fix(web): restore merged reattach workspace coverage
Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
* fix(tools-dev): reserve allocated daemon port
Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
* test(e2e): wait for artifact manifest persistence
Generated-By: looper 0.0.0-dev (runner=fixer, agent=opencode)
* chore(e2e): improve test framework quality
- Add lib/timeouts.ts with CI-scaled short/medium/long/xlong constants
- Add lib/playwright/mock-factory.ts to centralise standard localStorage,
/api/agents, and /api/app-config mock setup; migrate critical-smoke and
workspace-keyboard-flows to use applyStandardMocks()
- Delete empty lib/shared.ts placeholder
- Replace waitFor({ state: 'detached' }).catch(() => {}) with
waitFor({ state: 'hidden' }) in all UI tests; 'hidden' resolves
immediately when the element was never in the DOM, eliminating the
silent error-swallowing catch
- Remove redundant .catch(() => false) from all isVisible() call sites
since isVisible() never throws in Playwright
- Convert .waitFor().then(() => true).catch(() => false) guards in
openDesignFile() to explicit try/catch blocks for clarity
- Simplify sendPrompt() in app.test.ts: replace the 3-attempt manual
retry loop with a single fill + pressSequentially fallback; the core
workaround for contenteditable unreliability is preserved but the
loop structure is gone
* fix(e2e): guard routeMockAgents to GET only
routeMockAgents was intercepting all HTTP methods and returning the mock
fixture, silently swallowing any agent mutation requests. Mirror the
GET-only guard from routeAppConfig so writes fall through to the daemon.
* fix(e2e): address code review findings
- sendPrompt() in app.test.ts, workspace-keyboard-flows.test.ts,
app-restoration.test.ts: drop fill() (unreliable on contenteditable,
inputValue() always returns '' for them) and go straight to
pressSequentially(), which types key-by-key and is authoritative
- Import T from timeouts.ts in app.test.ts and use T.short for the
input/button waits, making the timeouts module non-dead
* fix(e2e): resolve adversarial review findings
- Revert sendPrompt to fill(): chat-composer-input is a textarea, not
contenteditable; fill() is atomic and ~60x faster than pressSequentially
- Use T.medium in all waitForLoadingToClear calls: CI workers scale this
to 20s automatically via the CI env var, eliminating cold-runner flakes
- Add T import to 6 files that needed it for T.medium
- Fix openDesignFile try/catch scope in app-manual-edit: previously the
catch block only caught waitFor but click/expect errors were also swallowed;
now only waitFor is inside try, real interaction failures propagate
- Fix regex escaping: .replace('.', '\\.') -> .replace(/\./g, '\\.') in
app-manual-edit and app-design-files to handle multi-dot filenames
- Migrate entry-chrome-flows.test.ts to applyStandardMocks: it had the
identical 3-call setup pattern as the factory but was not migrated
- Add GET method guard to project-management-flows app-config route handler,
matching the pattern used by every other route handler in the suite
- Remove no-op 'as const' from timeouts.ts: Math.ceil returns number,
not a literal, so the assertion had no effect
- Update e2e/AGENTS.md: remove deleted lib/shared.ts entry, document
lib/timeouts.ts and lib/playwright/mock-factory.ts
* fix(e2e): scope openDesignFile try/catch to waitFor only
Move click and expect(preview).toBeVisible() outside the catch block so
that a regression in either open path (tab-click or file-list fallback)
fails loudly instead of being silently absorbed. The try now wraps only
the fileTabButton.waitFor existence probe; the subsequent click and final
assertion are unconditional.
---------
Co-authored-by: Patrick A <186436799+eefynet@users.noreply.github.com>
Co-authored-by: Patrick A <259201958+eefynet@users.noreply.github.com>
* fix(web): chat pane preserves scroll position when todo card grows
The PinnedTodoSlot renders outside the chat-log scroll container. When
the todo card grows (new tasks added via TodoWrite), the scroll container's
clientHeight shrinks in the flex layout, drifting the user away from the
bottom. The existing ResizeObserver only observed children of the chat-log
div, so pinned-todo growth was invisible to followLatestIfPinned.
Fix: pass a containerRef to PinnedTodoSlot and observe that element in the
same ResizeObserver. syncPinnedTodo() is called on effect setup and from
the MutationObserver callback so observation stays current as the slot
appears and disappears across TodoWrite snapshots.
Red spec: apps/web/tests/components/chat-todo-autoscroll.test.tsx
* fixup! fix(web): chat pane preserves scroll position when todo card grows
Clarify test comment: the second test confirms followLatestIfPinned
snaps scroll to bottom when fired. The structural guarantee (pinned-todo
element is observed) is separately asserted in test 1, which is the
check that goes red on main without the fix.
* fix(web): correctness extend MutationObserver to pane ancestor for PinnedTodoSlot mount detection
The MutationObserver was only watching the .chat-log element. PinnedTodoSlot
(.chat-pinned-todo) is a sibling of .chat-log-wrap inside .pane, outside the
observed subtree. syncPinnedTodo inside the MutationObserver callback was
therefore dead code for mount/unmount transitions of the slot.
Add a second observation on paneEl (el.parentElement?.parentElement) with
childList-only so the MutationObserver fires when PinnedTodoSlot mounts or
unmounts and syncPinnedTodo can register/deregister the element with the
ResizeObserver.
* test(e2e): chat pane auto-scroll on todo card growth
Add Playwright spec that goes red on origin/main and green on this fix
branch. Scenario A asserts that a chat-log pinned to the bottom snaps
back after the PinnedTodoCard grows (the ResizeObserver-on-pinned-todo
path). Scenario B asserts that a deliberate scroll-up is not overridden.
Also allow OD_WORKSPACE_ROOT env override in next.config.ts so Turbopack
resolves node_modules correctly when the web app is booted from a worktree
whose node_modules symlinks resolve outside the default workspace root.
* docs(agents): note pinned-todo observer coverage in chat UI conventions
PinnedTodoSlot sits outside the .chat-log scroll container, so the
ResizeObserver and MutationObserver coverage that keeps auto-scroll
working when the todo card grows is non-obvious to future implementers.
Document the invariant in the Chat UI conventions section.
* fix(web): validate OD_WORKSPACE_ROOT, harden autoscroll test precondition
* fix(web): validate OD_WORKSPACE_ROOT existence, make autoscroll precondition unconditional
* fix(web): throw on invalid OD_WORKSPACE_ROOT instead of warn-and-fallback
* fix(web): require pnpm-workspace.yaml at OD_WORKSPACE_ROOT, drop dead test branch
Three follow-ups to nettee's review feedback:
1. apps/web/next.config.ts gains a pnpm-workspace.yaml existence check
after the relative-path validation. Without it, an override like
'<repo>/apps' or '<repo>/apps/web' passes the relative(resolved, WEB_ROOT)
check but the resolved path is missing the sibling packages/* directory
that apps/web imports from (for example @open-design/contracts). Next
would later fail deep inside file tracing / Turbopack with a much
harder-to-diagnose error. Now we throw at config load with a clear message.
2. e2e/ui/chat-todo-autoscroll.test.ts drops the redundant
'if (scrollUpOccurred)' branch. The hard precondition above it already
guarantees distanceAfterScroll > 80, so the if was dead code that read
as a false-green path. The body now runs unconditionally.
3. Same test tightens the post-grow assertion. The previous
toBeGreaterThan(60) would pass even if a regression dragged the log
most of the way back to the bottom (e.g. before=150, after=61).
Replaced with Math.abs(distanceAfterGrow - distanceAfterScroll) less than
SCROLL_PRESERVATION_TOLERANCE_PX (20) — a delta check that actually
verifies the comment's claim of 'within ~20px of where the user left it'.
* fix(web): canonicalize workspace root with realpathSync and tighten scenario B assertion
- Use realpathSync on both resolved and WEB_ROOT before the ancestor check so
that symlinked paths (macOS /tmp vs /private/tmp, worktree checkouts) compare
correctly instead of false-throwing on a physically valid override.
- Add isAbsolute(rel) guard for the Windows cross-drive case where path.relative()
returns an absolute path instead of a ..-prefixed string.
- Scenario B: replace distance-to-bottom delta assertion with scrollTop preservation
check. Growing the pinned todo naturally increases distance-to-bottom by ~extraPx
(clientHeight shrinks while scrollTop is held fixed), so the old Math.abs(after -
before) < 20 check would fail on correct behavior. asserting scrollTop directly
catches the real regression: followLatestIfPinned incorrectly snapping a non-pinned
user back to the bottom.
- Add hard precondition that clientHeight actually changed so the test fails fast
if the layout stops exercising the non-pinned path.
* test(e2e,web): add clientHeight guard to scenario A and mount-wiring unit test
---------
Co-authored-by: Patrick A <eefynet@users.noreply.github.com>
Co-authored-by: Patrick A <259201958+eefynet@users.noreply.github.com>
* Update home starter template categories
* fix(web): address PR #2501 review follow-ups
- usePluginFacets.clearFacets() now also resets `mode` to 'all' so the
empty-state "Clear filters" CTA escapes Saved mode in one click. A
fresh browser clicking Saved (or Saved combined with a zero-match
search) was previously stranded on the empty view because the CTA
only cleared `selection`/`query`.
- Restore the "Link code folder" item in the composer Tools -> Import
panel. The earlier refactor dropped its `onLinkFolder` wiring and
left every remaining ImportItem disabled, so chats without linked
dirs lost the only enabled entry point to `openFolderDialog()` /
`patchProject({ metadata.linkedDirs })`.
Adds regressions:
- plugins-home-section: Clear filters from the Saved empty state
- ChatComposer.import-menu: folder-link click-through hits the folder
dialog and patches `linkedDirs`
---------
Co-authored-by: qiongyu1999 <2694684348@qq.com>
Co-authored-by: lefarcen <935902669@qq.com>
Conflict resolved by taking origin/main:
- apps/web/src/components/EntryNavRail.tsx design-systems rail
button icon name palette-filled (release-side) -> blocks (main);
main's icon swap is part of the more recent design-systems rail
pass.
* Polish design system home flows
* Polish home prompt presets
* Polish home working directory controls
* test: align home hero chrome smoke
* fix: stabilize home composer ci checks
---------
Co-authored-by: qiongyu1999 <2694684348@qq.com>
Conflicts resolved by taking origin/main on both files. Root cause:
main's PR #2460 (fix(landing): align logo.webp with brand icon) changed
HomeHero.tsx's .home-hero__brand-mark to render <img src=/app-icon.svg>
instead of an inlined <HeroBrandIcon /> SVG, and bundled the matching
CSS (26px round badge with bg-panel + border + padding 2px) plus a
gap/font-size tune. The release-side visual-refresh CSS still targeted
the SVG layout (38px square, transparent, inset SVG selector). Keeping
release's CSS would leave main's <img> unstyled.
- apps/web/src/styles/home/home-hero.css three blocks, all taken from
main: .home-hero__brand gap 8px, .home-hero__brand-mark redesigned for
<img> child, .home-hero__brand-name font-size 16px.
- apps/web/src/index.css two blocks, both taken from main: workspace
tab close column 22px and .workspace-tab__close 18x18 (paired
tune-down of tab UI spacing).
* feat(daemon): add project working directory management and editor hand-off functionality
- Introduced new flags for project commands to manage working directories, including `--working-dir` and `--dir`.
- Implemented API routes for listing available editors and opening projects in selected editors.
- Added a hand-off button in the ChatPane header to facilitate opening project folders in local applications.
- Enhanced the HomeHero component to include working directory and design system settings, improving user experience in project creation.
- Created HomeHeroSettingsChips component for inline management of working directory and design system selection.
* feat(chat): implement voice transcription proxy and enhance UI components
- Added a new API route for voice transcription using OpenAI's `/audio/transcriptions` endpoint, allowing users to send audio blobs directly for transcription.
- Integrated multer for handling audio file uploads in memory, ensuring efficient processing without disk storage.
- Updated the HomeHero component to include example prompt suggestions for plugins, enhancing user interaction.
- Introduced the EditorIcon component to visually represent different editors in the hand-off menu, improving the user experience.
- Refined the HandoffButton component to utilize the new EditorIcon, providing a more cohesive interface for selecting editors.
- Enhanced CSS styles for various components to improve layout and responsiveness, including adjustments to tab and button sizes for better usability.
* style(workspace-shell): enhance layout and overflow handling
- Updated CSS for .workspace-shell to ensure full viewport width and height, with proper overflow management.
- Adjusted grid layout to prevent content overflow and maintain responsiveness.
- Modified styles for .workspace-tabs-chrome to improve width handling and prevent overflow issues.
* refactor(chat): remove voice transcription proxy and related components
- Deleted the voice transcription proxy implementation, including the associated API route and multer configuration.
- Removed the MicButton component from the ChatComposer and HomeHero components to streamline the UI.
- Updated HomeHero to include example suggestions without the voice input functionality.
- Adjusted CSS styles for various components to maintain layout consistency after the removal of the MicButton.
* feat(daemon): implement minting of HMAC tokens for working directory management
- Added a new function `mintImportTokenFromCurrentSecret` to generate HMAC tokens bound to a specified base directory, enhancing security for working directory operations.
- Updated the `desktop-auth.ts` file to include the new token minting functionality, which returns structured errors when the desktop auth secret is cleared.
- Introduced new IPC message types for minting import tokens in the sidecar protocol, allowing seamless integration with the daemon's working directory management.
- Enhanced the `WorkingDirPill` component to utilize the new token minting flow for secure directory selection in desktop builds.
- Updated CSS styles for the HomeHero component to accommodate new example suggestion features and maintain layout consistency.
* fix(HomeView): import HOME_HERO_CHIPS constant for improved chip management
- Updated the HomeView component to import the HOME_HERO_CHIPS constant from the chips module, enhancing the management of hero chips within the component.
* feat(daemon): implement mintImportTokenViaSidecar for secure working directory management
- Introduced the `mintImportTokenViaSidecar` function to facilitate the minting of HMAC tokens for desktop-import operations via the daemon's sidecar IPC. This allows CLI commands to bypass authentication when the desktop-auth gate is active.
- Updated the CLI to utilize the new token minting function when setting the working directory, ensuring secure access to trust-gated API endpoints.
- Enhanced the sidecar server to handle minting requests and return structured error messages for improved user feedback.
- Added tests to validate the new token minting functionality and its integration with the working directory management process.
- Refactored related components to support the new token flow, improving overall security and user experience.
* feat(HomeHero): enhance UI components and styles for improved user experience
- Updated HomeHero component to replace active dot indicators with Plug icons for better visual representation of active plugins.
- Adjusted CSS styles for various elements, including padding and dimensions, to enhance layout consistency and responsiveness.
- Introduced new styles for active type icons and improved hover effects for buttons.
- Updated HomeHeroSettingsChips to change button titles and icons for clarity.
- Added tests to ensure proper rendering and functionality of updated components.
* feat(ProjectDesignSystemPicker): enhance design system selection with preview functionality
- Updated the ProjectDesignSystemPicker component to include a preview feature for design systems, allowing users to see a preview of the selected design system.
- Implemented hover functionality to update the preview based on the hovered design system.
- Added fullscreen preview capability for a more immersive experience.
- Enhanced CSS styles for the design system picker to improve layout and responsiveness.
- Introduced tests to validate the new preview functionality and ensure proper interaction within the component.
* feat: refactor project metadata handling and enhance design system picker
- Updated the default scenario plugin ID retrieval to use project metadata, improving the logic for determining the appropriate plugin based on project intent.
- Enhanced the ProjectDesignSystemPicker and related components to support localized design system summaries and categories, improving user experience.
- Introduced new translations for working directory and design system picker components, ensuring better accessibility and usability across different locales.
- Added a new 'live-artifact' project type to the HomeHero chips, expanding the functionality for users creating refreshable artifacts.
- Updated tests to validate the new project metadata handling and design system picker functionalities.
* feat: enhance localization and styling for design system components
- Added French translations for working directory and design system picker components, improving accessibility for French-speaking users.
- Updated CSS styles for the pet task item to ensure consistent padding and layout.
- Introduced a new test suite for HomeHeroSettingsChips to validate localization and design system selection functionality.
- Enhanced ProjectDesignSystemPicker tests to ensure proper localization and interaction with design system categories.
* fix: update .gitignore to include all claude-sessions directories and remove specific session files
- Modified .gitignore to ensure all claude-sessions directories are ignored by using a wildcard pattern.
- Deleted two specific claude-sessions markdown files to clean up unnecessary session data.
* fix: repair home automation ci regressions
* fix: stabilize artifact consistency e2e
* Remove folder picker changes from PR 2400
---------
Co-authored-by: pftom <1043269994@qq.com>
Co-authored-by: qiongyu1999 <2694684348@qq.com>
Conflicts resolved by taking origin/main on all six points:
- apps/web/src/components/HomeHero.tsx:479-487 brand div removed
(main dropped the .home-hero__brand wrapper; the release-side visual
refresh still had it).
- apps/web/src/components/HomeHero.tsx:894-898 attach Icon size
18 (main's update) replaces 20 from release.
- apps/web/src/components/HomeHero.tsx:913-927 submit button uses
<Icon name="arrow-up" size={22} /> (main's component refactor)
instead of the release-side inline SVG.
- apps/web/src/components/EntryShell.tsx:578-582 Discord Icon size
14 (main) instead of 16 (release).
- apps/web/src/styles/home/home-hero.css drop .home-hero__brand /
__brand-mark / __brand-name rules — main removed both the component
div and these CSS rules together; keeping the CSS would be dead code.
- apps/web/src/styles/home/entry-layout.css Discord badge icon color
#5865f2 (main, the brand color introduced by PR #2386) instead of
release's neutral var(--text-strong).
* Reapply "fix(web): demote Plugins and Integrations to nav rail footer (#1806)" (#2360)
This reverts commit 1ab8758045.
* fixup: align EntryHelpMenu Discord URL with #2386 update
The revert of #2360 brought back EntryHelpMenu.tsx as #1806 originally
added it, with DISCORD_URL = 'https://discord.gg/BYShPgWpq'. #2386 later
rotated the Discord invite to mHAjSMV6gz, but only in the places that
existed on main at the time (EntryShell avatar dropdown + the e2e test);
EntryHelpMenu didn't exist then, so it never got updated. The e2e test
the revert reintroduced asserts the new URL, so the component must
match.
* test(e2e): harden extended coverage contracts
* docs(testing): add e2e hardening status
* fix(web): persist artifact chips after daemon runs
* ci: install playwright browsers for e2e vitest
* Fix daemon run recovery across reloads
Pin daemon-created runs to assistant messages immediately so hard reloads before the create response can reattach.
Replay terminal and active run events from the beginning on reload so restored turns keep assistant text, thinking events, produced files, and artifacts.
Fixes#2366Fixes#2368Fixes#2371
* test(e2e): preserve fake runtime selection across reload
* fix(web): scope daemon run recovery to daemon mode
* fix(e2e): remove duplicate delayed smoke flag
* fix(web): scope replay artifact recovery to current run
* fix(daemon): remove duplicate run-create pin
* Fix daemon run recovery across reloads
Pin daemon-created runs to assistant messages immediately so hard reloads before the create response can reattach.
Replay terminal and active run events from the beginning on reload so restored turns keep assistant text, thinking events, produced files, and artifacts.
Fixes#2366Fixes#2368Fixes#2371
* Fix ProjectView daemon run recovery tests
Shared device frames serve at /frames/<name>.html and previously
assigned the raw ?screen= value to the inner iframe.src. A
project-relative value like screen=screens/foo.html resolved against
/frames/, producing /frames/screens/foo.html (404), instead of the
embedding project's /api/projects/:id/raw/screens/foo.html.
The five frame HTML files now resolve relative ?screen= values
against document.referrer when present (the embedding project
preview), falling back to location.href so standalone /frames/*
loads keep working. Absolute and root-relative paths are passed
through unchanged.
Adds an e2e Vitest spec that evaluates each frame's inline <script>
in a Node vm and asserts iframe.src under five scenarios per file
(25 cases total): project-relative against referrer, root-relative
pass-through, absolute pass-through, empty referrer fallback, and
missing ?screen= no-op.
Fixes#2234