Commit Graph
215 Commits
Author SHA1 Message Date
AmyandGitHub 73d07d8dfb test(e2e): stabilize AMR onboarding assertions (#4462) 2026-06-17 09:08:09 +00:00
b02d20e6b9 fix: unify Discord invite links (#4452)
Co-authored-by: koki yanlai xu <koki@kokideMacBook-Air.local>
2026-06-17 08:25:16 +00:00
PerishFireandGitHub 15d3759834 [codex] Optimize CI topology and Playwright suites (#4460)
* Consolidate PR validation into ci

* Tune absorbed CI runner tiers

* Fold visual validation into CI Playwright shards

* Run PR shards during manual CI dispatch

* Optimize CI topology and scopes

* Increase UI P0 Playwright workers

* Relax visual Playwright timeout
2026-06-17 08:24:52 +00:00
CaprikaandGitHub fdb1638183 [codex] Refresh local CLI auth state after API key switches (#4453)
* Refresh Codex auth status after CLI changes

* Probe Claude Code auth status

* Stabilize project route during agent refresh

* Respect API key auth during CLI probes
2026-06-17 16:05:58 +08:00
AmyandGitHub 8ff19c6b41 [codex] Add main-risk smoke and E2E coverage (#4322)
* test(e2e): cover daemon reload artifact recovery

* test(e2e): expand critical flow coverage

* test(daemon): add startup and recovery smoke coverage

* test(e2e): add visual and desktop chrome smoke coverage

* ci: gate packaged onboarding smoke to nightly

* fix(web): repair artifact replay type narrowing

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)

* fix(ci): repair preflight smoke coverage checks

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)

* fix(web): tighten artifact recovery polling

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)

* fix(web): keep error recovery retries alive

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)

* fix(e2e): relax brittle UI P0 assertions

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)

* fix(web): recover pointer artifact outputs

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)

* fix(e2e): stabilize manual edit reload smoke

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)

* fix(e2e): avoid noisy win onboarding cleanup

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)

* fix(web): stop failed artifact recovery polling

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)

* fix(daemon): isolate startup smoke data root

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)

* fix(web): recover standalone html artifacts

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)

* fix(web): guard pointer artifact recovery by run time

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)

* fix(ci): narrow strict visual gate to critical smoke

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)

* fix(ci): limit PR visual capture to critical cases

* fix(ci): keep PR visual capture complete

* fix(web): keep reload artifact recovery retryable

* fix(ci): clarify PR visual gate layering

* fix(e2e): stabilize visual home P2 capture

* fix(e2e): align visual gallery detail clicks

* fix(e2e): preserve packaged install during onboarding cleanup

* fix(ci): shard PR visual capture

* fix(ci): remove duplicate nix validation from ci

* fix(ci): keep visual capture screenshots flat

* fix(ci): point nix hash autofix at ci-nix

* fix(ci): stabilize PR P0 recovery gate

* fix(web): ignore stale artifact pointers during recovery

* fix(web): reset BYOK model after clearing key

* ci: run critical UI P0 subset on PRs

* ci: combine PR UI P0 critical shards

* ci: combine PR visual capture shards

* ci: preserve visual capture shard manifests

* fix(web): clear ProjectView delayed timers on unmount

* test(e2e): harden onboarding connect gate coverage

* test(e2e): align AMR auth recovery and visual catalog timeout
2026-06-17 16:01:53 +08:00
lefarcenandGitHub e1b8d9921e Merge pull request #4421 from nexu-io/release/v0.11.0
release: Open Design 0.11.0 — The Bazaar
2026-06-17 15:27:29 +08:00
d0d1df8e60 fix(web): portal New Project design-system dropdown so it isn't clipped (#4346)
* test(e2e): red-spec reproducing #4303 design-system dropdown clipping

The New Project design-system popover (local DesignSystemPicker in
NewProjectPanel.tsx) opens with plain position:absolute and is clipped
by its .newproj-body { overflow-y: auto } ancestor, so lower options are
truncated. This P1 UI spec opens the picker on a short window and asserts
the popover does not spill past the .newproj-body / viewport bottom.

https://claude.ai/code/session_01EqWRnbYoxmugj4R1yj2LJ6

* fix(web): portal New Project design-system dropdown so it isn't clipped (#4303)

The New Project modal's design-system picker opened its popover with plain
position:absolute, anchored inside .ds-picker. Its clipping ancestor is
.newproj-body { overflow-y: auto } (the modal scroll container), so when the
trigger sat low in the body or the window was short, the popover was truncated
and the sections below bled through — the reported misaligned/truncated UI.
This is platform-independent (shared web CSS rendered by the same Chromium in
macOS and Windows desktop), not a macOS-only regression.

Mirror the shared DesignSystemPicker: render the open popover through a portal
to document.body, positioned from the trigger's viewport rect with
viewport-aware up/down placement and a clamped max-height, so it escapes the
clip box. A scoped .ds-picker-popover-portal variant flips the popover to
position:fixed (z-index above the modal backdrop) and lets the list flex and
scroll inside the bounded height. The sibling PlatformPicker shares the same
absolute-popover pattern and is left for a follow-up.

https://claude.ai/code/session_01EqWRnbYoxmugj4R1yj2LJ6

* fix(web): clamp design-system popover height to available viewport space

Review follow-up (#4346, thanks @mrcfps): the anchor used
Math.max(200, Math.min(440, space)), so when both sides of the trigger were
under the 200px preferred minimum (a very short window) the fixed popover was
still sized to 200px and overflowed the viewport instead of staying scrollable.

Size the popover to the chosen side's actual available space, honoring the
200px preferred minimum only when the side can fit it. The list already flexes
and scrolls inside the bounded height, so a smaller box just scrolls rather than
spilling past the viewport edge.

Extend e2e/ui/new-project-ds-picker-clipping.test.ts with a tight-viewport case
(1280x360) that asserts both sides fall under 200px and the popover stays within
the viewport.

https://claude.ai/code/session_01EqWRnbYoxmugj4R1yj2LJ6

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-17 14:32:28 +08:00
lefarcenandGitHub 1e2f47fb8d fix(amr): surface vela login URL/code so sign-in survives a failed browser handoff (#4402)
* fix(amr): surface vela login URL/code so sign-in survives a failed browser handoff

AMR (vela) sign-in is a device-authorization flow: `vela login` prints the
activation URL + user code to stdout, then best-effort opens the browser and
keeps polling until the server-issued expiry (>5min). The daemon spawned it
headless, only read the first 250ms of output, and the UI showed nothing but a
"Signing in…" spinner — so when the browser failed to open (stripped PATH in a
packaged build, blocked network) the user had no URL to fall back to and stared
at a dead spinner for the full AMR_LOGIN_TIMEOUT_MS (5 minutes).

- daemon: parseVelaLoginActivation + lifetime stdout/stderr capture surface
  activationUrl / userCode / browserOpenFailed on VelaLoginStatus.
- web: the AMR account pill's signing-in state now shows an activation block
  (open sign-in page + copy code) as soon as the URL is known, instead of only
  a spinner; it adapts to the active theme.
- web: an AMR run that fails with AMR_AUTH_REQUIRED now offers inline sign-in in
  the chat error card (the login pill) and auto-retries once on success, rather
  than bouncing the user out to Settings.

Tests: red->green parser spec (validated against real vela login output),
AmrAccountControl activation-block specs, ChatPane inline-auth specs.

* test(amr): real-vela E2E for the login activation capture seam

Spawns an actual `vela login` child through the daemon's spawnVelaLogin and
asserts readVelaLoginStatus surfaces the real activation URL + code while the
login is in flight, then cancels (never approves, so no config is written and
any existing session is untouched). Covers the daemon capture wiring
(beginLoginActivationCapture -> readVelaLoginStatus) that the pure parser spec
can't reach. Auto-skips where the vela CLI isn't installed, so CI is unaffected.

* fix(amr): polish inline auth retry layout

* fix(daemon): try AMR login direct first, fall back to IPv4 proxy

loopback AMR API proxy (forced IPv4) to dodge broken `amr-api.open-design.ai`
edge paths for some hosts (#3726). But behind a corporate transparent proxy
that hijacks `amr-api.open-design.ai` onto an internal gateway (e.g.
飞连/CorpLink → 30.x), the extra proxy hop makes the upstream lose the client
IP and reject device authorization with
`502: Invalid IP address: undefined` — even though the direct path resolves
fine. Result: AMR "Authorize" fails in every packaged build that ships this
change (beta/main), while release builds without it still work.

Only `vela login` was rerouted; the model catalog and chat inference already
go direct (to amr-link / VELA_LINK_URL), so this restores login to the same
direct-first default. The IPv4 proxy is kept strictly as a fallback: the
route now starts login over a direct connection and only retries through the
proxy when the direct attempt fails to start — never when a login is already
in flight. Both cohorts win: transparent-proxy users succeed directly,
broken-edge users (#3726) still get the proxy.

Tests: fake-vela gains FAKE_VELA_LOGIN_FAIL_WITHOUT_API_URL to model a broken
direct path; the login suite now asserts direct-first (no VELA_API_URL on a
healthy path) and proxy-fallback (VELA_API_URL set only after a direct
failure, incl. OD_PUBLIC_BASE_URL derivation).

* fix(amr): clear stale login activation details

* fix(amr): refresh login status immediately after auth starts

* fix(amr): sync inline auth card with login status

* fix(amr): tighten inline auth retry state

* fix(amr): wait for direct login activation before fallback

* fix(amr): show activation details only in chat auth card

* fix(amr): guard clipboard copy explicitly in the activation block

navigator.clipboard?.writeText(...).then(...) already short-circuits the whole
chain when the clipboard API is absent, but make the guard explicit so the copy
handler reads as a clean no-op rather than relying on optional-chain reach.

* fix(amr): don't kill a slow-but-healthy direct login; only fall back on real failure

The login activation watchdog turned a merely slow direct CreateDeviceAuthorization
into a hard failure: after the grace it killed the direct child and let the route
retry through the IPv4 proxy — the exact proxy hop that 502s on the transparent-proxy
networks this fix targets. Now the grace only stops blocking the request; a still-
running direct attempt is left alive (the UI polls /status), and the proxy fallback
fires only on an explicit pre-activation exit/error. The grace is overridable via
OD_AMR_LOGIN_ACTIVATION_GRACE_MS so the new route test can exercise the slow-direct
path quickly (fake-vela gains FAKE_VELA_LOGIN_ACTIVATION_DELAY_MS).

Addresses review feedback on #4402.

* test(e2e): AMR auth failure now signs in inline, not via Settings

The chat AMR_AUTH_REQUIRED card replaced its 'open Settings to authorize' bounce
with an inline sign-in pill (POSTs vela login in place, auto-retries on success).
Update the [P0] runtime-recovery spec to assert the inline flow: clicking
Authorize & retry issues the login request and opens no Settings dialog. The
non-AMR 'Switch to AMR' recommendation flow (AmrGuidance) is unchanged.

* fix(amr): only auto-retry inline auth on a real sign-in transition

The inline AMR auth card auto-retried the failed run whenever /status reported
signed-in — via both the 500ms shared-status poll and the pill's onStatusChange.
Because the per-assistant-id guard resets on each retry (new run = new id), a run
that keeps failing AMR_AUTH_REQUIRED while /status reads signed-in retried
forever (caught by the runtime-recovery UI P0 spec). Now we retry only on an
explicit signed-out -> signed-in transition; a null/unknown status never
fabricates one, and once signed-in we don't retry again until an explicit
signed-out is seen.

- Unit specs updated to drive the transition (and a loop-guard spec for the
  already-signed-in case).
- e2e: the AMR auth spec asserts the inline sign-in flow (no Settings bounce);
  the switch-to-Codex spec mocks /status signed-out, the realistic state for an
  AMR_AUTH_REQUIRED failure, so the Authorize & retry action is offered.

* fix(amr): fall back to the proxy on a post-grace direct login failure

The direct-first login route only fell back to the IPv4 proxy when the direct
child crashed inside the 250ms startup grace. A direct vela login that survives
the grace and then errors out before printing its activation URL (the common
broken-edge shape, #3726) returned 202 and stranded the user on a dead direct
login. Re-introduce waitForActivation: the route now blocks until the direct
attempt reaches device-auth steady state (prints its activation URL) or
exits/errors before it; only a pre-activation exit/error triggers the proxy
retry, and a slow-but-healthy direct login is left running (never killed).

fake-vela now prints the activation block (so the daemon can detect steady
state) and gains FAKE_VELA_LOGIN_FAIL_WITHOUT_API_URL_DELAY_MS; the route suite
gains a regression where the direct attempt fails past LOGIN_STARTUP_GRACE_MS
and must still reach the proxy. Addresses review feedback on #4402.
2026-06-17 14:29:15 +08:00
PerishFireandGitHub 4b9a15734d [codex] Consolidate PR validation into CI (#4451)
* Consolidate PR validation into ci

* Tune absorbed CI runner tiers

* Fold visual validation into CI Playwright shards

* Run PR shards during manual CI dispatch
2026-06-17 14:21:04 +08:00
PerishFireandGitHub 891981d460 [codex] Optimize CI runtime topology (#4450)
* Optimize e2e tools-dev runtime parallelism

* Remove visual networkidle wait

* Optimize e2e vitest parallelism

* Optimize Nix flake caching

* Test CI on Blacksmith runners

* Allow parallel manual CI runs

* Tier CI runner sizes

* Temporarily narrow CI debug scope

* Instrument watcher CI timeouts

* Instrument watcher event diagnostics

* Avoid default polling in watcher tests

* Skip runtime trace during watcher debug

* Probe ARM runner tiers for CI

* Focus CI runner probe on x64 browser

* Probe browser workers by runner size

* Probe Playwright file parallelism

* Probe Playwright worker scaling on 8v

* Reshape CI topology

* Fix split E2E Vitest CI lane

* Simplify daemon CI topology

* Optimize Windows payload CI setup

* Revert "Optimize Windows payload CI setup"

This reverts commit 5cbc48c0af.

* Cache better-sqlite3 Nix binding separately

* Revert "Cache better-sqlite3 Nix binding separately"

This reverts commit 0384e3787e.

* Remove unused Nix cache setup from CI

* Use Blacksmith ARM for lightweight CI jobs
2026-06-17 12:53:06 +08:00
lefarcenandGitHub 67ade60bce fix(e2e): seed win smoke onboarding into the live daemon data dir (#4444)
The packaged Windows smoke's onboarding seed derived its target path from
the installed app's baked config, which falls back to AppData and never
matches the data dir the running daemon actually reads. `tools-pack win
start` rewrites `namespaceBaseRoot` to the tools-pack runtime root and hands
it to the runtime via OD_PACKAGED_CONFIG_PATH, so the live daemon's
RUNTIME_DATA_DIR is always under `runtimeNamespaceRoot`. The seed landed in
the AppData fallback instead, so the daemon never read `onboardingCompleted`.

This stayed invisible until #4389 removed the step-0 "Skip" affordance from
the now-required Connect onboarding step: `ensureMainAppShell` used to click
Skip to reach home, so an ineffective seed didn't matter. With Skip gone and
the seed missing its target, the app sat on onboarding and the smoke failed
with "packaged windows runtime did not reach main app shell".

Write the seed to `<runtimeNamespaceRoot>/data/app-config.json` — the exact
dir the running daemon reads (already asserted for logs at line ~400) and the
same approach the macOS smoke uses. Drop the now-dead manifest-derivation
helper chain.
2026-06-17 12:12:56 +08:00
lefarcen 39cad2b8a5 chore(release): bump version to 0.11.0 for release/v0.11.0 2026-06-17 11:05:56 +08:00
lefarcenandGitHub f6cceb303d fix(plugins): unwrap cosmetic hard wraps in bundled manifest descriptions (#4406)
Picking a Home example-prompt card (or "Replicate this content" in the
plugin detail modal) seeds the composer with the plugin manifest's
`description`. Many descriptions were authored with cosmetic hard wraps
(a single newline mid-paragraph, ~75 cols, as if formatted for a
terminal/markdown source). The composer renders the seed with
`white-space: pre-wrap`, so those newlines survived as short ragged
lines that never filled the editor width — leaving a large blank gutter
on the right of the input box (e.g. the Trading Analysis Dashboard
template: 4 hard breaks, ~200px / 29% of the editor blank).

Reflow the offending `description` / `description_i18n` strings in the
56 affected bundled manifests into flowing text (wrapped lines joined,
real blank-line paragraph breaks preserved; CJK joins carry no space).
Surgical string edits only — manifest formatting/key order untouched
(146 lines changed across 56 files). The full build spec still reaches
the agent as plugin context (SKILL.md + example.html); only the
human-facing seed copy changed.

Add an e2e guard (plugin-description-hardwrap.test.ts) that fails if any
bundled manifest description carries an intra-paragraph hard wrap, so
the cleanup can't silently regress when new examples are added.
2026-06-16 22:39:55 +08:00
lefarcenandGitHub a4abae3804 fix(release): build the daily beta from main, not the latest release branch (#4393)
* fix(release): build the daily beta from main, not the latest release branch

notify-daily-feishu picked the highest-semver release/vX.Y.Z branch and
built beta off it. Once that branch ships stable, its base version equals
the latest stable, so release-beta's strictly-greater-than-stable guard
rejects every scheduled run until someone hand-bumps the retired branch —
which is exactly how the daily beta + Feishu card silently stalled for
days after 0.10.1 shipped.

Beta is the daily R&D channel, so track the development tip (main), which
always leads stable. Keep the workflow_dispatch `ref` input for one-off
builds from a specific branch.

* test(release): scope daily-beta resolve check and assert ref override

Strengthen the regression test per review: scope the assertions to the
resolve job and prove both control-flow branches — the empty-input
default writes ref=main, and the workflow_dispatch override still
forwards ${{ inputs.ref }} verbatim — so a refactor can't keep the test
green while breaking the preserved override path.
2026-06-16 13:00:24 +00:00
f81439bdae feat(onboarding): gate Connect step, sequential stepper, restyle progress bar (#4389)
* feat(onboarding): gate Connect step, sequential stepper, restyle progress bar

- Remove "Skip for now" — Connect is now a required step, not skippable.
- Gate Continue until the selected runtime is usable: AMR signed in, an
  available local CLI selected, or a BYOK connection test that passed.
- Make the stepper a strictly-sequential wizard: only already-reached steps
  are clickable, no forward jumps. Keys off a maxStepReached counter instead
  of the async AMR login probe, so the indicator no longer flickers
  disabled->enabled while that status resolves.
- Disable the "+" new-tab affordance (button + Cmd/Ctrl+T) while on the
  onboarding route, so opening a tab can't bypass the Connect gate.
- Restyle the stepper as a thin segmented progress bar with centered labels;
  drop the oversized "Welcome" hero title.
- Deprecate the now-unreachable onboarding `skip` / `skipped` analytics enums
  (kept for historical data; no longer emitted).

* test(onboarding): update specs for the new Connect gate behavior

CI flagged onboarding tests still asserting the removed/changed UI.

Vitest (apps/web):
- e2e helper: wait for Continue to be enabled before clicking — AMR detection
  lags, so the Connect gate keeps it disabled until vela/status confirms
  signed-in; clicking a disabled button was a no-op and stalled at step 0.
- "switches runtime" test: assert Continue is now disabled after switching to
  a not-yet-ready Local runtime (gate behavior).
- about_you_submit-once test: advance via Continue instead of a stepper
  forward-jump, which is no longer allowed.
- replace the Skip-exits test with one asserting the Connect step has no Skip
  affordance and emits no skip/skipped telemetry.

Playwright (e2e/ui):
- gotoOnboarding: key off the "Choose a runtime" heading instead of the
  removed "Welcome" hero title.
- replace the skip-exits P0 with a no-Skip-affordance assertion.
- visual-home: same Welcome -> Choose a runtime marker update.

---------

Co-authored-by: free666799 <293857035+free666799@users.noreply.github.com>
2026-06-16 09:55:57 +00:00
lefarcenandGitHub 30b8eac48c chore(release): bump version to 0.10.2 on main (#4387)
main read 0.10.1 (== latest stable 0.10.1), so the daily beta build's
strictly-greater-than-stable version guard refused to produce a beta
from main. Advance the 0.10.x dev line to 0.10.2 across the same 16
lockstep workspace package.json that #4381 bumped, and align the
packaged-smoke stable dry-run fixture to 0.10.2.

This continues the existing beta line on R2 (0.10.2-beta.4) so a build
from main produces 0.10.2-beta.5.
2026-06-16 06:08:40 +00:00
lefarcenandGitHub 66d9cf9fb6 fix(release): accept releaseVersion/releaseNumber in beta metadata reader (#4383)
The daily beta build (notify-daily-feishu) reads beta/latest/metadata.json
via scripts/release-beta.ts, which only understood the legacy
betaVersion/betaNumber fields. The unified release publisher and the
in-flight tools-release rewrite stamp the feed with generic
releaseVersion/releaseNumber instead, so once a build from that tooling
wrote the feed the daily reader died with 'must include betaVersion or
baseVersion+betaNumber' and the scheduled beta + Feishu card stopped.

Accept either field spelling so the reader survives whichever publisher
last wrote the feed.
2026-06-16 05:29:52 +00:00
lefarcenandGitHub a3a5f02d87 chore(release): bump version to 0.10.1 on main (#4381)
Back-fill the 0.10.1 version bump that shipped via the release/v0.10.1
branch (tag open-design-v0.10.1, already published) but never merged
back to main. The feature/perf content of that release already landed
on main via #4198, #4203 and #4202; only the version-number bump and
the packaged-smoke stable fixture were left behind, so main still read
0.10.0.

This applies the release-specific deltas onto current main:
- bump version 0.10.0 -> 0.10.1 across the 16 workspace package.json
  in the 0.10.x release set (apps/daemon, apps/desktop, apps/landing-page,
  apps/packaged, apps/web, e2e, root, packages/contracts, packages/download,
  packages/host, packages/launcher-proto, packages/platform,
  packages/sidecar-proto, packages/sidecar, tools/dev, tools/pack)
- align e2e packaged-smoke stable fixture to 0.10.1

launcher-proto was added to main after the 0.10.1 release branch was cut,
so it was not in the original release commit; it is part of the lockstep
0.10.x release set (private workspace package, sibling of sidecar-proto),
so it is bumped here to keep main release metadata consistent.

Independently-versioned workspace packages (agui-adapter, components,
diagnostics, metatool, plugin-runtime, registry-protocol, tools/serve,
telemetry-worker) are intentionally left untouched.

Supersedes the stale release/v0.10.1 PR (#4205).
2026-06-16 03:59:48 +00:00
CaprikaandGitHub f2fef8d9a5 [codex] fix prompt title summaries (#4348)
* fix prompt title summaries

* fix ui p0 title expectations

* use selected agent for title suggestions

* strip generated titles across streams
2026-06-15 15:32:29 +00:00
Passive BookandGitHub 95900a615b Fix design system preview image asset URLs (#4177)
* fix: rewrite design system preview image assets

* fix: reopen design review feedback cards

* fix: preserve app urls in design previews

* fix: preserve design preview asset suffixes

* fix: rewrite inline design preview css assets

* fix: refresh design manifest labels

* fix: surface invalid design manifests

* test: stabilize workspace restoration p0

* fix: validate design manifest card entries
2026-06-15 08:54:42 +00:00
Marc ChanandGitHub 6c555f93d6 chore(ci): stabilize visual regression screenshots (#4290)
* Stabilize visual regression screenshots

* Stabilize BYOK visual settings states
2026-06-15 02:53:00 +00:00
a0afc584bb [codex] centralize daemon data directory docs (#4222)
* docs: centralize daemon data directory contract

* fix(e2e): allow slower artifact consistency navigation

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* docs: localize daemon data directory pointers

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-06-15 02:52:05 +00:00
AmyandGitHub f82a4e3828 fix(web): apply composer BYOK model selection (#4209) 2026-06-12 08:50:17 +00:00
AmyandGitHub 571b43dce0 [codex] Expand UI E2E coverage and P0 CI signal (#4196)
* add PR UI P0 gate

* fix(ci): keep critical UI check in required workflow

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)

* ci: split workspace restoration P0 shard

* ci: reduce visual and daemon test wall time

* ci: skip duplicate critical UI gate on PRs

* ci: increase daemon test sharding

* test(e2e): expand critical UI flow coverage

* test(e2e): promote critical onboarding and automation flows

* test: expand ui e2e coverage

* test: repair PR P0 onboarding and media sync checks

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)
2026-06-12 04:41:49 +00:00
21e01f6a25 Fix raw asset review cards in design systems (#4093)
* fix: avoid raw asset review cards

* fix: resolve web component styles in dev

* fix: keep design system component images reviewable

* fix: use webpack for local web dev sidecar

* fix: improve design system review cards

* fix: render design system review previews from srcdoc

* fix: keep image ui kit cards generic

* fix: rebase inlined preview css urls

* test: allow slow send-now navigation

---------

Co-authored-by: Siri-Ray <2667192167@qq.com>
2026-06-11 15:37:02 +00:00
SarinandGitHub 2f301902d7 fix(web): keep New Project design-system dropdown above form sections (#4026)
The 'Design system' dropdown in New Project rendered behind the form
sections below it (Target platforms, Companion surfaces, Fidelity,
Create), so the form bled through the open popover.

Every .newproj-section carries a residual identity transform from the
od-fade-slide-up entrance animation (animation-fill-mode: both), turning
each section into its own stacking context. .ds-picker is such a section,
so the popover's z-index was trapped inside the picker and could not rise
above later sibling sections. Lift the OPEN picker's stacking context
above its siblings so the dropdown paints on top.

Red->green Playwright spec samples points down the open popover and
asserts elementFromPoint resolves into the popover.
2026-06-11 14:06:46 +00:00
AmyandGitHub d055737de7 [codex] Add PR UI P0 gate (#4142)
* add PR UI P0 gate

* fix(ci): keep critical UI check in required workflow

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)
2026-06-11 09:09:00 +00:00
AmyandGitHub 8c3500c807 [codex] Cover preview toolbar and conversation history flows (#4134)
* test(e2e): expand home and project detail coverage

* test: cover preview toolbar and conversation history flows

* test: lower priority of supplemental e2e cases

* Revert "test: lower priority of supplemental e2e cases"

This reverts commit 86d9872500.
2026-06-11 07:17:40 +00:00
4c8914dc71 feat(web): split onboarding newsletter into its own third step (#4106)
* feat: move onboarding newsletter signup to its own third step

* test(e2e): walk amr-onboarding through the new newsletter step before finishing

* fix(web): keep about_you_submit exactly-once on the onboarding completion path

The newsletter step is now its own (final) step with a clickable stepper
and Back button. Emitting the about_you_submit survey snapshot when the
user leaves the About-you step double-fired on Back-then-Continue and
fired zero times on a forward stepper jump. Move it onto the completion
path, stamp it with fixed About-you coordinates (it describes the
About-you submission regardless of which step finishes), and guard it
with a per-session ref so it is exactly-once. Adds regression tests for
the stepper-jump and Back-then-Continue flows.

* test(web): walk amr onboarding e2e through the newsletter step before finishing

---------

Co-authored-by: qiongyu1999 <2694684348@qq.com>
Co-authored-by: lefarcen <935902669@qq.com>
2026-06-10 16:14:59 +00:00
70c70ff9a3 feat(web): replicate-first Use menu + send-button attention pulse (#4059)
* feat(web): lead the plugin detail Use menu with Replicate this content

The split-button menu only exists when the plugin ships an example
query, and reproducing the previewed result is what most users open it
for — so the replicate option now comes first and structure-only Use
drops to second.

* feat(web): pulse the send button after a plugin Use or example-prompt pick

Choosing Use / Use with query in the plugin detail modal, or clicking an
example-prompt card, seeds the composer and returns the user to it — but
nothing signals that sending is the next step. Flash the send button
twice (house ease-out, 0.25s delay so the scroll-back settles first,
none under prefers-reduced-motion) via a new HomeHeroHandle.pulseSend()
fired from routePluginUse and useExamplePlugin.

* feat(web): replicate-content as the primary detail CTA + sheen-style send pulse

- The detail modal's primary Use button now reads (and acts as)
  'Replicate this content' when the plugin ships an example query;
  structure-only Use moves behind the caret menu. Shared via
  pluginUsePrimaryAction so all four detail variants agree.
- The send-button attention effect drops the ring+scale flash for a
  soft light sweep crossing the button twice — same trigger points,
  same reduced-motion opt-out.
- Plain structure-only Use now pulses only when the composer is
  actually submittable (draft text or staged files); flashing a
  disabled send button pointed at a dead end.

* feat(web): first-run guidance trail — prototype chip and first preset card sheen

Brand-new users (no projects, fresh storage) get a sheen pulse on the
Prototype type chip ~1s after Home settles; picking any type chip
advances a persisted stage and the first example-prompt card pulses
once when the chip's presets render, completing the trail (the existing
send-button pulse covers the final beat after a card pick). Users with
projects have the stage completed silently so the trail never shows.

The send-button sheen generalizes into a shared
.home-hero__attention-sheen class (accent-tinted glint on light chip /
card surfaces), still skipped under prefers-reduced-motion.

* fix(web): gate the first-run guide on project loading completing

firstRunGuide was true whenever projects was the initial empty array,
so the trail could arm for an existing user whose projects were still
loading. Make the prop tri-state (undefined while loading): the guide
neither arms the chip pulse nor silently completes the stage until the
projects answer is known.

* fix(web): close any in-flight guide stage for existing users + gate the card pulse

A chip pick during the projects-loading window can advance the guide
stage to 'card' before firstRunGuide resolves; if the user then turns
out to have projects, the stage lingered and the beat-2 effect (not
gated on firstRunGuide) could sheen the first preset card for a
non-new user. The firstRunGuide === false branch now completes ANY
unfinished stage, and the card-pulse effect requires
firstRunGuide === true.

* fix(web): pulse Send from the static prompt-example path too

The fallback prompt-example cards (rendered when a chip has no example
plugins) seed and focus the composer entirely inside HomeHero, so they
never reached the HomeView-routed pulseSend(). Extract a local
triggerSendAttention() shared by the imperative handle and
usePromptExample, with a focused spec clicking a
home-hero-prompt-example card.

* fix(test): null-guard the first prompt-example card lookup

tsc (noUncheckedIndexedAccess) rejects passing exampleCards[0] straight
to fireEvent.click; guard explicitly.

* fix(web): gate the Send pulse on the composer actually being submittable

usePlugin() now resolves a boolean: whether the bound plugin left the
composer submittable (inputs valid, apply not failed/superseded). All
three pulse call sites (use-with-query, plain Use, example preset pick)
consult it, so a plugin with required inputs and no defaults routes the
user to the inputs form without a misleading flash on the disabled Send
button. Spec: required-input plugin via detail-modal replicate → Send
stays disabled and unsheened.

* fix(web): carry the guide's beat 2 through the static prompt-example fallback

A chip with no plugin presets renders static prompt-example cards, but
the beat-2 effect only watched filteredExamplePlugins — a fresh user on
that path never got the card pulse and the persisted stage stuck at
'card'. Either card surface now satisfies beat 2, the first static
prompt-example button carries the sheen, and a regression spec covers
the presetless-chip path.

* test(e2e): route freeform Use through the Use-plugin-only menu option

The replicate-first menu change makes the detail modal's primary Use button
seed the example query for query-bearing starters, so the structure-only
freeform path now lives in the caret menu's 'Use plugin only' option. Update
the P0 critical 'keeps the prompt freeform' spec to open the caret menu and
click that option, matching the shipped behavior.

* test(e2e): route the remaining freeform Use cases through the menu option

The replicate-first primary CTA also seeds the prompt for query-bearing
starters rendered via PluginExampleDetail/PreviewModal, so the neighboring
[P1] 'Use plugin from the details modal' case (an html-preview fixture with an
example query) that clicked the primary button and asserted an empty composer
would fail once it runs outside the @critical gate. Route it through the caret
menu's 'Use plugin only' option like the P0 case, and correct the now-stale
'no pre-fill' comments on the chip-only and use-with-query cases.

---------

Co-authored-by: qiongyu1999 <2694684348@qq.com>
Co-authored-by: lefarcen <ontf116@gmail.com>
2026-06-10 12:00:53 +00:00
AmyandGitHub a0221deaba [codex] Fix onboarding dropdown styling (#4066)
* Fix onboarding dropdown styling

* Fix onboarding dropdown placement test

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)

* Fix searchable onboarding dropdown regressions

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)
2026-06-10 09:28:21 +00:00
844a0c0af1 fix(web): scope Use actions to the plugin detail modal and decouple the Community filter from hero chips (#4037)
* fix(web): remove inline Use actions from Community gallery tiles

The Home Community gallery tile grew an inline Use / Use with query
split button in its title bar, repeating per card. The gallery tile is
a pure live preview: opening the detail modal is where Use and Use
with query live. Drop the gallery use menu (and its CSS) and route the
visual-home use-with-query e2e flow through the detail modal's split
Use button. The rich management layout (PluginsView installed tab)
keeps its hover-overlay Use menu.

* fix(web): decouple the Community category filter from hero type chips

Picking an example-prompt type chip (Prototype / Slide deck / ...) —
including the default chip on first paint — force-synced the Community
grid's category selection through presetSelection, silently rewriting
the user's browsing filter. The two surfaces share a taxonomy but are
independent controls: the chip drives what the composer generates, the
pills only filter the gallery. Remove the presetSelection plumbing
(HomeView memo -> PluginsHomeSection prop -> usePluginFacets effect);
the Community grid now boots on All and only moves when its own pills
are clicked.

* fix(e2e): match the Deck Writer detail dialog by plugin name

Deck Writer carries a previewEntry, so its detail surface is the
PreviewModal whose dialog is labelled 'Deck Writer preview' — the
'... details' label only exists on the scenario detail. Match the
dialog on the plugin name so the visual use-with-query flow finds it.

---------

Co-authored-by: qiongyu1999 <2694684348@qq.com>
2026-06-10 06:54:30 +00:00
AmyandGitHub 39cf931290 Remove project instructions UI and preserve stream errors (#4045)
* Remove project instructions UI and preserve stream errors

* Stop injecting hidden project instructions

Generated-By: looper 0.9.7 (runner=fixer, agent=codex)
2026-06-10 06:14:46 +00:00
3f5fe0f96c feat(amr): forward AMR_CLIENT_SOURCE to vela + source on wallet links (#4005)
* fix(landing): keep auth redirects on AMR wallet

* feat(amr): tag vela CLI launches with AMR_CLIENT_SOURCE + source on wallet links

So vela analytics can attribute Open Design's command funnel and model spend
back to this host (source=open_design), and the AMR wallet landing carries the
source for the web page_view.

- runtimes/env: set AMR_CLIENT_SOURCE=open_design for the amr agent spawn (not
  PII, so no telemetry-consent gate)
- integrations/vela: spawnVelaLogin forwards OD_INSTALLATION_ID (consent-gated)
- web/daemon: AMR wallet recharge URL gains ?source=open_design

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(web): expect ?source=open_design on AMR console/recharge URLs

The AMR console/recharge links the OD app opens into vela web now carry
?source=open_design so vela attributes the visit to Open Design. Update the
console/recharge URL assertions accordingly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(daemon): expect ?source=open_design on AMR insufficient-balance recharge URL

DEFAULT_AMR_RECHARGE_URL now carries the source param for attribution.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(amr): bump bundled vela CLI to 0.0.15 + fix e2e recharge URL assertion

- tools/pack: bundle @powerformer/vela-cli 0.0.15 (the release carrying the
  source-attribution logic that reads AMR_CLIENT_SOURCE and tags model_request)
- e2e: expect the insufficient-balance recharge URL to carry ?source=open_design
  (addresses review on DEFAULT_AMR_RECHARGE_URL)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* chore(nix): refresh pnpm-deps hashes after vela CLI bump

The tools/pack @powerformer/vela-cli bump changed pnpm-lock.yaml, invalidating
the daemon/web fetchPnpmDeps fixed-output hashes. Update to the values the Nix
flake check computed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: audit <a@b.c>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 02:02:48 +00:00
6313286232 feat(home): plugin detail "Use plugin" split menu + align use-with-query with example-prompt seed (#3997)
* feat(home): plugin detail "Use plugin" split menu + align use-with-query with example-prompt seed

- PreviewModal.primaryAction gains an optional split-button menu. The plugin
  detail modal's "Use plugin" now offers two variants — 仅使用插件 (use) and
  复刻此内容 (use-with-query) — wired through the existing routePluginUse action,
  i18n'd across all 19 locales (new preview.usePlugin / usePluginOnly* /
  replicateContent* keys).
- use-with-query now seeds the composer with the SAME human-friendly text the
  Home example-prompt cards use. Extracted examplePresetSeedPrompt (+ its
  dependencies) from HomeHero into a shared plugins-home/presetSeedPrompt module
  so both surfaces share one source of truth, instead of the detail modal
  dumping the raw od.useCase.query meta-instruction ("follow the en field
  verbatim; start from example.html") into the textarea.
- The example-prompt active plugin chip now shows the same × clear button as the
  Community path (activeBadge.isExplicitPlugin distinguishes a user-picked
  plugin from a task-chip default).
- Analytics: community_gallery/use_plugin (action=use|use_with_query) lands the
  documented row-118 event; chat_composer/plugin_chip_clear mirrors
  working_dir_clear for the chip ×.

* feat(prompt): generate artifact copy in the user's UI locale

Chinese (and other non-English) prompts were producing English pages: when a
template / plugin / design-system ships English reference content, the agent
copied that wording verbatim. The UI-locale override only covered chat prose
and generated form controls, not the artifact body.

Extend the locale override so all user-visible copy in the generated
HTML/React/page/deck (headings, body, nav, button/link labels, captions, alt
text, form fields) is written in the run's UI locale by default — and reference
content in another language must be translated/adapted, not shipped verbatim.
Brand names / code / identifiers stay as-is; an explicit user request for a
different output language still wins.

* fix(plugin-details): scenario fallback gets the use/use-with-query split menu

Review follow-up (#3997): PluginScenarioDetail was a single onUse(record,'use')
button, so any plugin routed to the text/scenario fallback lost the new split
action — users couldn't pick "Use plugin only" vs "Replicate this content" even
when od.useCase.query exists. Reuse the shared buildPluginUseMenu in the footer
as an upward-opening split button (caret menu), matching the html/design/media
variants, and keep a plain single button when there is no query.

Also update the home use-with-query visual e2e: use-with-query now seeds the
rendered preset text ("Draft a topic deck.") rather than the raw `{{topic}}`
query, matching the example-prompt card path.

Regression tests: scenario+query renders the split menu caret; scenario without
query keeps a single button.

* fix(home): preserve placeholder write-back when use-with-query seed is the query

Review follow-up (#3997, blocking): use-with-query unconditionally nulled
`queryTemplate`, dropping the only path that writes composer edits back into
`active.inputs`. For presets whose seed IS the rendered (human-friendly) query,
that meant editing a hydrated value before running submitted the plugin with
stale default inputs.

examplePresetSeedPrompt now returns `{ text, fromRenderedQuery }`. routePluginUse
keeps the raw `{{...}}` query template (and `queryTemplateAllowsPrefix`) only when
the seed came from the rendered query; it still nulls the template when we fell
back to a description / meta-instruction seed (no placeholders to extract). The
meta-instruction → description fix is unchanged.

Tests: friendly-query use-with-query now asserts the placeholder edit flows back
into pluginInputs (empty-draft and edited-draft-prefix cases); the
meta-instruction case still seeds the description with no write-back.

* fix(home): persist explicit-pick flag for active plugin chip visibility

Review follow-up (#3997): the active plugin chip's visibility + clear-button
gate inferred "explicit pick" from `record.id !== defaultPluginIdForChip(chipId)`.
An example-prompt preset whose plugin id equals the chip's default plugin — the
prototype rail binds `example-web-prototype`, which is also the prototype chip's
default — was therefore misclassified as a task-default binding, so its chip/×
never appeared even though the user explicitly picked the preset.

ActivePlugin now carries an `explicitPick` flag, set on the example-prompt
preset (useExamplePlugin) and Community / detail-modal (routePluginUse) paths.
Both `activeBadge` (chip label vs plugin title + ×) and
`shouldShowActivePluginChip` (the chip-visibility gate) short-circuit on it
before falling back to the id heuristic — so an explicit pick always surfaces
its own chip, while a type-chip default binding still stands in for the task
chip. Added a HomeView.activePluginChip regression covering the preset-equals-
default case.

* fix(home): keep prefix matching on for use-with-query placeholder extraction

Review follow-up (#3997): `queryTemplateAllowsPrefix` only enabled prefix
matching when the composer already had text BEFORE the user clicked Replicate.
The empty-draft → prepend-intro → edit-placeholder path then fell through:
extraction stayed anchored at `^`, so a placeholder edit after a freshly-added
intro stopped writing back into active.inputs and submit could re-apply stale
defaults.

Enable prefix matching whenever we track the query template (`hasTemplate`).
Suffix matching is equally correct with no prefix, and now tolerates an intro
the user prepends after the seed is inserted. Added the empty-draft → add
prefix → edit placeholder regression.

---------

Co-authored-by: qiongyu1999 <2694684348@qq.com>
2026-06-09 16:04:03 +00:00
PerishFireandGitHub f9433a7a07 release: harden packaged launcher validation (#3995)
* Unify release metadata publishing

* Allow empty release asset suffix

* Harden beta-s release publishing

* Clean beta Windows release namespace

* Clean Windows launcher namespace

* Retry transient mac notarization uploads

* Control mac notarization upload mode

* Notarize release mac DMGs after build

* Harden release mac DMG notarization retries

* Make beta-s mac updates payload-first

* Bake mac updater metadata URL

* Add unsafe DMG install helper

* Fix beta-s feed and About version

* Improve release validation observability

* Build launcher proto before packaging

* Notarize release-stable mac artifacts

* Enable mac notarization in tools-pack

* test: update packaged release workflow assertions
2026-06-09 11:50:01 +00:00
lefarcenandGitHub 6a10e8e178 fix(daemon): classify Claude mid-stream socket drops as retryable (#3937)
* fix(daemon): classify Claude mid-stream socket drops as retryable

When the Claude Code CLI's connection to the Anthropic API (or a custom
ANTHROPIC_BASE_URL relay) drops mid-response, the SDK surfaces 'The socket
connection was closed unexpectedly...'. The daemon relayed this raw SDK
string verbatim into the error bubble because diagnoseClaudeCliFailure had
no branch for an established-then-dropped connection (distinct from the
existing connection-refused case).

Add a connection-drop branch that classifies socket-close / ECONNRESET /
socket hang up / fetch failed / premature close into a human-readable,
retryable diagnostic, pointing at the proxy when a custom endpoint is set
and at the network otherwise. Most visible on long, large generations.

The fake-agent CLI gains a 'Return a daemon socket-drop failure' mode whose
claude shape was captured from the real Claude Code CLI 2.1.168 driven at a
socket-dropping endpoint (error on stdout as a synthetic assistant block +
result{is_error:true}, exit 1, empty stderr), so the e2e path exercises the
real failure shape rather than a guessed one.

* fix(daemon): use OpenClaude socket-drop diagnostics

Generated-By: looper 0.9.7 (runner=fixer, agent=opencode)

* fix(daemon): harden Claude connection-drop diagnostic from real repro

Reproduced end-to-end against the real Claude Code CLI (2.1.168, official
auth, no ANTHROPIC_BASE_URL) by tunneling through a local proxy that resets
the TLS stream mid-response. Two real error faces observed: 'The socket
connection was closed unexpectedly' (SSE cut) and 'Unable to connect to API
(ECONNRESET)' (tunnel reset); the CLI retries internally for minutes before
surfacing either, which is why long runs appear to abort after a while.

Broaden the connection-drop matcher with 'unable to connect to api' and
ETIMEDOUT, and lock the real ECONNRESET transcript into the test suite.

* fix(daemon): narrow Claude API drop matcher

Generated-By: looper 0.9.7 (runner=fixer, agent=opencode)

* feat(daemon,web): structured AGENT_CONNECTION_DROPPED code + localized message

Promote the Claude connection-drop diagnostic from a raw English string to a
stable, machine-readable failure class so the web can localize it and triage
can count it by code.

- contracts: add AGENT_CONNECTION_DROPPED to API_ERROR_CODES.
- daemon: ClaudeCliDiagnostic carries an optional code; the connection-drop
  branches stamp AGENT_CONNECTION_DROPPED; server.ts prefers diagnostic.code
  over the generic service-failure sniff so the run records the specific class
  (flows into run_finished error_code + routine_runs.error_code for triage).
- web: resolveRunFailureUi maps AGENT_CONNECTION_DROPPED (any agent) to the
  localized chat.connectionDropped message, plain retry, no AMR promotion.
- i18n: add chat.connectionDropped to types.ts and all 19 locale bundles.

* fix(daemon): classify Claude in-stream connection-drop error frame

End-to-end repro on a real daemon + fake Claude CLI revealed the diagnostic
never fired: Claude surfaces a connection drop as an in-stream `error` frame
(assistant error:"unknown" + the raw SDK string), which claude-stream emits
as a type:'error' event. The daemon's stream-error handler sent that raw
string verbatim as a non-retryable AGENT_EXECUTION_FAILED and set
agentStreamError, so the child-exit diagnoseClaudeCliFailure path was skipped
entirely.

Run the same per-agent diagnostic in the stream-error handler so a connection
drop emits AGENT_CONNECTION_DROPPED (retryable, with localizable copy +
triage-countable code) instead of the raw SDK string. Verified end-to-end via
the real-daemon-run Playwright test, which now asserts the localized card copy.
2026-06-09 11:12:54 +00:00
AmyandGitHub 90817ce72c Rebalance Playwright critical gate (#3904)
* Rebalance Playwright critical gate

* Fix staged attachment e2e selectors

* Run critical gate before visual PR checks

* Expand strict PR visual coverage

* Widen visual harness config fixtures

Generated-By: looper 0.9.2 (runner=fixer, agent=codex)

* Expand strict visual suite to 35 cases

* Stabilize critical app artifact selectors

* Fix onboarding visual model assertion

Generated-By: looper 0.9.2 (runner=fixer, agent=codex)

* Stabilize settings dialog e2e entry

* Reduce UI extended E2E failure fanout

* Handle nested settings menus in e2e helper

* Sync AMR logout status to settings card

* Centralize agents route mocks in E2E

* Fix critical smoke settings grep

Generated-By: looper 0.9.2 (runner=fixer, agent=codex)

* Consolidate E2E agents route fixtures

* Stabilize critical project instructions test

Generated-By: looper 0.9.2 (runner=fixer, agent=codex)

* Let nested agents mocks handle routed requests

Generated-By: looper 0.9.2 (runner=fixer, agent=codex)

* Stabilize project instructions critical gate

Generated-By: looper 0.9.2 (runner=fixer, agent=codex)

* Stabilize visual home plugin surfaces

* Upload critical gate artifacts in visual PR job

* Fix visual PR gate reports and fixtures

* Address PR 3904 review paths

* Localize project instructions control
2026-06-09 10:07:37 +00:00
5e5134ba86 fix(web): interrupt the active run when sending a queued chat now (#3959)
* fix(web): interrupt the active run when sending a queued chat now

Clicking the up-arrow ("send now") on a queued chat send while the
agent is still working only moved that item to the front of the queue.
For the common single-item case that is a no-op: the button looks like
a send action but nothing happens until the in-flight turn finishes on
its own, so it reads as broken.

Make "send now" do what it says: when the conversation is busy, stop the
active run and prioritize the chosen item. Stopping flips the conversation
out of its busy state and the existing auto-start effect then flushes the
now-first queued send through the same path as a natural completion, so
runs still never overlap.

handleStop moves ahead of the queued-send handlers so sendQueuedChatSendNow
can call it.

* fix(web): preserve replacement run streaming on stale cancel

Generated-By: looper 0.9.7 (runner=fixer, agent=opencode)

* fix(web): ignore stale run finalization after queued send

Generated-By: looper 0.9.7 (runner=fixer, agent=opencode)

* fix(web): ignore a superseded run's late stream error

Final stale-callback edge in the "send now" interrupt path. The stream
onError handler unconditionally painted a global failure banner, appended
an assistant error event, and marked the assistant message failed. A run
interrupted by send-now can still land in onError (e.g. a canceled stream
that lost its terminal SSE and whose fallback status fetch sees a
non-terminal run), so the superseded run could flash a stale failure
banner while the replacement run is already streaming.

Gate the banner / message finalization on the same controller-pair
ownership check used for the marker and latest-run teardown. Only the run
that still owns the active controllers surfaces the error.

* fix(web): ignore stale run completion side effects

Generated-By: looper 0.9.7 (runner=fixer, agent=opencode)

* fix(web): guard reattach terminal callbacks for superseded runs

The send-now interrupt's real target is often a reattached run (the agent
was already streaming when the user queued). Apply the same controller-pair
ownership guard the stream path uses to the reattach terminal callbacks: a
superseded reattached run's late onDone must not persist artifact /
produced-file output, and its late onError (or the consume catch) must not
paint a global failure banner, over the replacement run that now owns the
conversation. Normal completion is unchanged.

* test(web): cover the interrupted reattached run's late error

Red-green regression for the reattach guard: a reattached run interrupted
by send-now, whose late onError lands after the replacement queued send has
started, must not paint a global failure banner. Goes red without the
controller-pair ownership guard on the reattach error path.

* fix(web): don't drop a normal run's completion after its terminal status

The superseded-run guards checked ownership as
`abortRef === controller && cancelRef === cancelController`, but the daemon
emits the terminal onRunStatus *before* onDone, and that terminal status
clears the run's active refs. So for a normal successful run the refs were
already null by the time onDone ran, the guard read false, and onDone bailed
— skipping artifact persistence and produced-file attachment. (Caught by the
artifact-consistency e2e: the saved file metadata never aligned.)

Reframe the check as "a newer run owns the active slot": a run may finalize
when abortRef is null (its own terminal status cleared it — the normal path)
or still points at its own controller; only a live replacement (abortRef set
to a different controller) means it was superseded. Applied to every stream
and reattach terminal callback. Adds a unit regression that fires the terminal
status before onDone and asserts the completion flow still runs.

* fix(web): tag superseded runs at interrupt time, not via abortRef

The previous guard read ownership off abortRef in each terminal callback,
but that is unreliable: a normal run's terminal onRunStatus clears the refs
before onDone, and handleStop() clears them before a canceled run's late
callbacks arrive — so abortRef === null was ambiguous between "completed
normally" and "interrupted, replacement not yet attached". The latter let a
superseded run still run its completion flow and repaint the shared artifact.

Record the interrupted run's abort controller in a WeakSet synchronously in
sendQueuedChatSendNow — before handleStop() clears anything — and have every
stream/reattach terminal callback skip its current-run side effects when its
controller is tagged. This is independent of abortRef churn and holds even
before the replacement send attaches. The reattach onDone also bails ahead of
parser.flush()/setArtifact()/message finalize so a stale run can't repaint the
artifact preview; only run bookkeeping is released for a superseded run.

Adds a regression that interrupts a reattached run and delivers its late
onDone, asserting the completion flow (file refetch) does not run.

* fix(web): reset applying comment status when send-now interrupts

A turn moves its preview-comment attachments to 'applying' when it starts.
Now that send-now suppresses the interrupted run's terminal callbacks, those
callbacks no longer flip the comments to 'needs_review'/'failed', so they were
left stuck as 'applying' after the replacement send took over — the UI kept
showing an interrupted review comment as still being applied.

Reset the conversation's 'applying' preview comments back to 'open' in the
send-now interrupt path (local state + persisted), so a canceled turn's
comments don't strand mid-apply. Adds a red-green regression.

* fix(web): only reset the in-flight run's stuck applying comments

The previous reset flipped every 'applying' comment in the conversation to
'open', but queued sends (including the one being prioritized by send-now)
also hold their attachments in 'applying'. Resetting those raced the
replacement run's re-apply and reopened comments still reserved by other
queued work.

Scope the reset to the interrupted in-flight run's comments only — the
'applying' ones not owned by any queued send. Regression flipped to assert a
queued comment-bearing send's own comment is NOT reset to 'open' when
send-now flushes it.

* test(e2e): cover send-now interrupt end-to-end

Browser-level e2e against a real daemon + fake agent for the send-now
queued-send feature: start a held (never-finishing) run so the conversation
stays busy, queue a second turn, click the queued item's send-now arrow, and
assert the held run is canceled and the queued turn dispatches as a fresh run
while the queued strip drains.

Adds a 'Hold the daemon run open until canceled' fake-agent mode that stays
alive until the daemon cancels it, and a data-testid on the queued send-now
button for a stable selector.

---------

Co-authored-by: audit <a@b.c>
2026-06-09 08:50:04 +00:00
d859f6dec7 fix(web): flatten the composer footer into one tidy inset card (#3962)
* fix(web): flatten the composer footer into one tidy inset card

Follow-up polish to the alignment fix (#3949). The composer mounts under
`.chat-composer-fixed-layer`, whose shell had `padding: 0`, so the staged
chips, the input field and the toolbar hugged the card's rounded frame and
the footer stacked several competing layers: a tinted outer card around a
separate inner box, a toolbar divider, and the + / Send buttons flush
against the card edges with no side margin.

Collapse it into a single inset card:
- Keep the typing area a clean white field (`--bg-panel`) with a soft
  resting border so it reads as the one input surface inside the tinted card.
- Drop the toolbar's `border-top` divider (and its 2px spacing) — the white
  field is the only separator the footer needs now.
- Give the shell an even `6px 8px` inset so the chips, input and toolbar all
  sit off the frame, including a real side margin on + and Send.
- Pin the toolbar row to the 28px control height (was a 32px min-height that
  left 4px of dead space and made the footer band feel too tall).
- Lower the footer label-collapse `@container` breakpoint 430px -> 414px to
  account for the shell's new side padding shrinking the queried container
  width, so the "Design"/"Send" labels stay until the pane is genuinely tight.

Adds e2e/ui/composer-footer-card.test.ts as the regression boundary: the +
and Send keep a side inset, the input field is evenly inset, and the toolbar
has no top border. Red on main (insets ~1px, border 1px), green here.

* fix(web): scope composer footer inset to the fixed layer, keep @container generic

Addresses review feedback: the previous commit lowered the shared
`@container (max-width: 430px)` label-collapse breakpoint to 414px to
compensate for the fixed-layer shell's new side padding. But that @container
block governs every `.composer-shell` — including the comment composer
(`.comment-side-new-comment-shell` in FileViewer.tsx) — so the global shift
would have delayed the Send/label collapse on surfaces whose sizing never
changed, risking cramped/overflowing controls there.

Keep the breakpoint generic at 430px and instead apply the footer's side
inset as a margin on the fixed-layer shell's children rather than horizontal
padding on the shell. The shell's content-box width (what the @container
measures) stays full, so the chat footer keeps its + / Send side margins
while every other composer's responsive behavior is untouched.

---------

Co-authored-by: audit <a@b.c>
2026-06-09 06:10:39 +00:00
2ce9d09f41 Fix Windows launcher beta update lifecycle (#3960)
* fix windows beta updater acceptance harness

* fix: harden windows launcher beta updates

* fix: align launcher payload e2e relaunch stub

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* fix: gate windows uninstall version sync

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* fix: verify launcher payload before activation

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-06-09 06:00:59 +00:00
992824c594 fix(web): align composer footer controls to one height (#3949)
The composer's bottom toolbar mixes five controls authored in five
components — the + icon, the working-dir pill, the agent avatar, the
session-mode ("Design"/CLI) toggle and Send. The composer mounts under
`.chat-composer-fixed-layer` (a body-level portal), so chat.css's
`.app`-scoped "one control system" normalization never reached it. The
controls drifted to 28/30/32px: the + and avatar/Design at 32px (the
28px override floored back up by a leaking min-width/min-height:32 from
chat.css), Send at 30px, and only the working-dir pill at 28px. They
stay vertically centered, but the differing heights leave the pills and
Send visibly misaligned against the left buttons.

Normalize all five to one 28px control height in routines.css (the
last-loaded, winning stylesheet): clear the leaking 32px min-width/
min-height on the + icon, drop fixed-layer Send from 30px to 28px, and
pin the session-mode toggle and agent avatar — which had no fixed-layer
override — to 28px. Covers both the `.app` and `.chat-composer-fixed-layer`
hosts.

Adds e2e/ui/composer-toolbar-alignment.test.ts as the regression
boundary: every interactive control in the composer row must share one
height. Red on main (height spread 4px), green here (spread 0).

Co-authored-by: audit <a@b.c>
2026-06-09 03:00:12 +00:00
3aeb3a125e feat(web): live-preview Community gallery + redesigned plugin detail modal (#3903)
* feat(web): live-preview Community gallery + redesigned plugin detail modal

Community (Home) now renders each plugin as a minimal live-preview tile —
a macOS-window bar (status dot + name + open-in-new-tab) over an eagerly
rendered, scaled example.html iframe that auto-pans on hover; clicking the
tile opens the detail modal. Adds a `gallery` card layout so the installed-
plugins management view keeps its rich hover-overlay card.

Reworks the plugin detail modal for designers:
- Preview is the hero: the info sidebar starts collapsed and the scaled
  iframe fills the stage edge-to-edge (measure via clientWidth so the modal
  entrance transform no longer leaves a white gutter).
- Header: title + description on the left, Use plugin + Share on the right,
  one centered row. Drops the More and Plugin-info header buttons; the
  sidebar opens via the preview-edge handle.
- Fullscreen moves to a hover-only icon button in the preview's top-right.
- Sidebar (variant="minimal"): author + example query up top, developer
  manifest detail behind a "Developer details" disclosure; calmer labels,
  box-free author, softer query block in the regular UI font.
- Softer slide-in animation when the info panel opens.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(web): restore detail test hook + keyboard guard on gallery plugin cards

The gallery tile replaced the rich card's "Details" button with a whole-card
onClick, which broke e2e/visual flows (and the strict visual job) that open
the detail modal via `plugins-home-details-<id>`, and HomeView.plugin-i18n
which drove Use through the inline card buttons.

- Re-add `data-testid="plugins-home-details-<id>"` to the gallery article so
  click-to-open keeps a stable hook equivalent to the old Details button.
- Guard the card keydown handler with `event.currentTarget === event.target`
  so Enter/Space on the nested ↗ external-link anchor opens the real page
  instead of being swallowed into the detail modal.
- HomeView.plugin-i18n: drive plain Use through the detail modal (gallery has
  no inline Use button); drop the Home use-with-query case — that affordance
  now lives on the rich PluginsView cards (covered by PluginsView.test.tsx)
  with query localization covered by state/projects.test.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(web): scope the header sidebar-toggle removal to the plugin detail

Removing the PreviewModal header sidebar toggle globally also dropped the
design-system modal's "DESIGN.md" button, breaking the entry-chrome-flows
e2e. Add a `hideSidebarToggle` prop and set it only on the HTML plugin
detail (PluginExampleDetail), which opens its collapsed info panel via the
preview-edge handle. Design-system / media / scenario keep their toggle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(web): keep fullscreen for media views; adapt Home e2e to the gallery flow

- PreviewModal: render the stage fullscreen button for custom-stage views
  too (image/video/audio media players). The header no longer carries a
  fullscreen control, so this hover icon is their only fullscreen affordance
  (addresses review feedback).
- entry-chrome-flows e2e: the Home Community gallery has no inline Use /
  Use-with-query buttons, so drive Use through the plugin's detail modal
  (plain "Use" routes the plugin as the active driver). The starter-query
  pre-hydration is gone from Home — the live tile preview replaces it — so
  the project-creation P0 now types the brief before submitting and still
  asserts the picked plugin is carried as the run driver (pluginId).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(web): remove Saved from Community + make gallery tile a11y-accessible

Per product direction, drop the Saved collection from the Community gallery
so the minimal tiles aren't paired with an orphaned Saved filter:
- Remove the Saved filter chip, the per-card save button, the `saved` facet
  mode, and the savedPlugins store; delete savedPlugins.ts and the chip /
  card-save styles. Update unit + e2e specs that drove the save flow.

Accessibility (review feedback): the gallery tile was a focusable
`role="listitem"` with click/keydown, which announced as a bare list item.
Make the tile's title a real `<button>` (the announced "open details"
control, and the e2e hook); the article stays a non-interactive listitem
with a mouse-only click-through.

Also adapt the remaining Home e2e to the redesigned detail modal: the info
sidebar opens via the preview-edge handle (header "Plugin info" toggle and
the inline "More" share menu were removed), and keep fullscreen for media
(custom-stage) views.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(e2e): drive the direct-Use P0 through the gallery detail modal

The last Home e2e still clicked the inline `plugins-home-use-<id>` rich-card
button, which the gallery tile no longer renders. Open the starter's detail
modal and Use it from there; the rest of the flow (active-driver chip, empty
prompt, freeform fill, create-project pins the plugin) is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(web): keep the plugin info sidebar reachable on small screens

The plugin detail modal hides the header sidebar toggle and starts the info
panel collapsed, leaving the preview-edge handle as the only opener — but
that handle is display:none under 760px, so on phones the panel was
unreachable (review feedback). Always render the toggle and only hide it on
desktop (where the handle works); a max-width:760px rule shows it again so
mobile users can open the info panel.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(web): scope Saved removal to the Community gallery, keep it in PluginsView

Earlier this PR removed the Saved collection from the shared
PluginsHomeSection, which also stripped the save button + Saved chip from the
installed Plugins tab (PluginsView, rich layout) and dropped access to
existing saved-plugin data (review feedback).

Restore the Saved store, facet mode, rich-card save button, and chip, but
gate the chip on the rich layout via a new `showSaved` prop. The minimal
Community gallery (cardLayout="gallery") has no per-card save affordance, so
it hides the chip; PluginsView keeps the full Saved experience.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: qiongyu1999 <2694684348@qq.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-08 16:04:58 +00:00
CaprikaandGitHub 1828bb942d [codex] Fix onboarding AMR login cancel (#3912)
* Fix onboarding AMR login cancel

* Handle onboarding AMR cancel race
2026-06-08 11:42:56 +00:00
AmyandGitHub 532ced93f1 test(e2e): mock streamed agents in main P0 flows (#3877) 2026-06-08 07:10:56 +00:00
AmyandGitHub ae58382040 test(e2e): stabilize main P0 follow-up alerts (#3844)
* test(e2e): stabilize main P0 follow-up alerts

* ci: cover no-browser Playwright failure alerts

Generated-By: looper 0.9.2 (runner=fixer, agent=codex)

* ci: distinguish fallback CI alert fingerprints

Generated-By: looper 0.9.2 (runner=fixer, agent=codex)

* test(e2e): tolerate AMR onboarding model defaults
2026-06-08 06:10:04 +00:00
7231c891f8 Allow release-stable preflight dry runs (#3857)
* Allow release stable preflight dry runs

* Fix stable dry-run nightly branch validation

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-06-08 04:46:02 +00:00
7a1ad1a438 feat(packaged): add launcher payload updates (#3812)
* feat(packaged): add launcher payload runtime

* ci: focus launcher payload windows smoke

* ci: publish beta mac x64 launcher payload

* test(e2e): harden payload updater smoke

* fix(pack): inject launch config for installed windows smoke

* fix(daemon): allow packaged payload resource roots

* fix(updater): relaunch after payload update

* test(e2e): refresh windows updater UI status

* test(e2e): ensure windows smoke reaches main shell

* test(e2e): skip onboarding before windows updater smoke

* fix(updater): detach windows payload relaunch helper

* test(e2e): run payload smoke without updater dry run

* fix(desktop): relaunch windows payload updates on quit

* fix(updater): preserve installed payload relaunch target

* fix(updater): require launcher target for payload updates

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* test(e2e): align mac x64 artifact mode assertion

* test(e2e): canonicalize launcher relaunch root

* fix(updater): validate launcher payload config before activation

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* fix(updater): require live launcher target for payload updates

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-06-07 07:38:57 +00:00
8dcf4ff9f4 fix(plugins): route picked plugins through their own pipeline and trust local installs (#3743)
* fix(plugins): route picked plugins through their own pipeline and trust local installs

Two related defects prevented an installed plugin from actually driving a
generation when the user picked it:

- HomeView "Use" attached the plugin as background context only (via
  requestPluginContextUse), so submit fell back to the hidden od-default
  scenario and the plugin's pipeline + SKILL.md/asset context never
  applied. Route the picked plugin as the active driver instead
  (usePlugin), preserving the use-with-query textarea seed behavior.
- resolvePluginFolder hard-coded trust='restricted' for every install,
  ignoring sourceKind. A local scenario plugin could then never obtain the
  pipeline:* capability it needs to run its own pipeline. Honor the
  documented source-kind policy (local => trusted) via defaultTrustForRecord
  and resolveCapabilitiesGranted.

* fix(plugins): preserve draft on use-with-query and update trust/route test coverage

Addresses review feedback on the plugin-use routing + local-trust change:

- use-with-query no longer discards the user's existing draft. routePluginUse
  now APPENDS the rendered plugin query to the current prompt (matching the
  legacy context-use semantics) before routing the plugin as the active
  driver, instead of letting usePlugin replace the whole prompt.
- Update daemon trust coverage for the local-default flip: the installer
  test asserts a local install is trusted by default, and the headless
  pipeline fixture drops its ephemeral grantCaps to exercise the
  stored/default grant path (local trusted => pipeline:* already granted).
- Update web + e2e tests for the new active-driver behavior: picking a
  plugin now routes it as the run driver (its own pipeline) rather than
  attaching it as background context. Drops two context-only `Use` regression
  tests whose scenario no longer exists under active-driver routing.

* test(plugins): stabilize direct-Use e2e timing and cover plugin-use submit

The P0 "direct Use routes the plugin as the active driver" browser test could
race the in-flight apply: it submitted before `/api/plugins/:id/apply`
resolved, so the active snapshot was not yet bound and the create-project
request never fired (waitForRequest timed out). Wait for the apply response
and an enabled submit button before clicking.

Also extend the HomeView prefill unit test to drive the full plain-`use` →
type a draft → submit flow and assert the routed plugin (not od-default)
drives the run, so this path is covered at the unit layer too.

* test(e2e): assert plugin-use submit via the project request, not a run POST

The two P0 plugin-use flows ("direct Use" and "Use with query carries the
prompt") still waited on POST /api/runs after submit. Under active-driver
routing the picked plugin is a scenario pipeline whose run is fired from the
bound snapshot when the project page mounts — Home no longer posts a separate
/api/runs — so waitForRequest(isCreateRunRequest) timed out even though the
create-project request (which pins pluginId) succeeded. Assert on the project
request + navigation instead, and drop the now-unused message-listing helper.

* fix(plugins): preserve query template on use-with-query so edits flow into inputs

Routing use-with-query passed the rendered prompt as nextPrompt, which made
usePlugin null out active.queryTemplate (nextPrompt-set branch). handlePromptChange
then stopped extracting edited {{...}} values back into active.inputs, so a user
who edited the hydrated query before submit would send the stale default inputs
and the applied snapshot would disagree with the visible prompt.

routePluginUse now passes an explicit queryTemplate aligned with the combined
prompt (untouched draft prefix + raw placeholder-bearing plugin query), so
extraction keeps working. Adds a prefill test that edits a hydrated
use-with-query prompt and asserts the edited value reaches the submitted
pluginInputs.

Also stabilizes the two P0 plugin-use e2e flows: they asserted live navigation
after submit, but the real e2e daemon has no mocked plugin installed so the
live create-project call cannot complete. Assert on the create-project request
payload (which pins the routed pluginId) instead, and drop the now-unused
project-context/message helpers.

* fix(plugins): extract use-with-query inputs independent of the mutable draft prefix

The previous fix baked the user's draft prefix into the query template, so
extractPluginInputsFromPrompt anchored the whole prompt and editing that prefix
made extraction return null — a later placeholder edit then never reached
pluginInputs and the snapshot refreshed from stale defaults.

The template now carries the plugin query segment only. usePlugin/active gain a
queryTemplateAllowsPrefix flag (set by the use-with-query route when a draft
prefix exists) that tells extractPluginInputsFromPrompt to match the query as a
suffix after any leading text. Editing the draft prefix no longer breaks
placeholder extraction.

Adds a regression: existing draft -> use-with-query -> edit both the draft
prefix and a hydrated placeholder -> submit asserts pluginInputs reflects the
edited placeholder.

---------

Co-authored-by: qiongyu1999 <2694684348@qq.com>
2026-06-05 16:09:25 +00:00
0582749df5 Refactor beta release workflows (#3741)
* Refactor beta release workflows

* Fix beta workflow runner bootstrap

* Fix self-hosted beta release bootstrap

* Use pnpm cmd in self-hosted beta Windows bootstrap

* Pass mac notarization profile to beta builds

* Allow direct Apple notarization credentials

* Fix beta Windows asset handoff

* Skip Windows report zip in beta publish

* Fix beta metadata workflow cleanup

* Stabilize self-hosted beta metadata checkout

* Retry self-hosted beta manifest downloads

* Use release storage for self-hosted manifests

* Isolate self-hosted beta metadata scripts

* Add Windows pack phase diagnostics

* Disable beta self-hosted Windows security checks

* Remove beta Windows security preflight

* Add release-beta-s version reservation

* Fix beta daily workflow caller inputs

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* Guard beta latest publish and build-only runs

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* Tighten beta latest publish guards

* Fix beta publish artifact suffix handling

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* Fix beta metadata suffix and Windows publish target handling

* Fix self-hosted beta release ref and publish guards

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* Fix beta release commit identity wiring

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

* Fix hosted beta checkout pinning

Generated-By: looper 0.9.5 (runner=fixer, agent=codex)

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-06-05 13:01:54 +00:00