mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
e01efc78f60c85d4f3efd15b65f752d8ff74d837
908
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4de25d3f1b |
fix(web): 让长演讲者备注在 presenter 视图里滚动而非裁切 (#6271) (#6314)
问题:.presenter 视图里 .notes 这个 aside 作为 body 的 grid item 默认
min-height: auto,不收缩;body 的隐式 grid row 也 auto,跟着内容撑。
当备注文字到几百词时,.notes 把 body row 撑过 viewport,而 body
{ overflow: hidden } 直接切掉底部,.notes-body { overflow: auto }
永远不触发 —— 用户看不到下面的备注,也没有滚动条 (#6271)。
修复:
1. body 加 grid-template-rows: minmax(0, 1fr),把 implicit row 锁到
viewport 高度,row 不再随内容增长。
2. .notes 加 min-height: 0,让 grid item 在 bounded row 里可以收缩,
.notes-body { overflow: auto } 才能真正生效。这跟 .stage 已有的
min-height: 0 模式对称。
回归测试:在 speaker-notes.test.ts 加一个 describe,80 行 ~800 词的
备注塞进 buildSpeakerNotesPresenterHtml,断言:
- body 的 CSS 里包含 grid-template-rows: minmax(0, 1fr)
- .notes 的 CSS 里包含 min-height: 0
- .notes-body 仍然带 overflow: auto
- 完整长备注文本被原样写入 od-presenter-data
锁住这几个不变量,后续 refactor 不会再让长备注被静默裁切。
Co-authored-by: Ray Xi <2667192167@qq.com>
|
||
|
|
cc7d8a5cd5 |
fix(web): recover deck preview after returning to a project tab (#6519)
* fix(web): recover deck preview after returning to a project tab A workspace-scoped deck whose HTML references a relative project asset holds its srcDoc preview empty until GET /api/projects/:id/files answers (the #6142 scoped-asset rewrite hold). The viewer's file-list effect called fetchProjectFiles without an AbortSignal, which pins the shared single-flight entry: a stalled read (queued behind saturated connections; every failure path resolves []) then survives unmount forever and every later mount of the same project + workspace identity rejoins the same dead promise — so leaving and returning to the project showed a permanently white main stage while the thumbnail rail and speaker notes (rendered from source directly) stayed healthy. Two changes: - Abort the file-list read on effect cleanup so a viewer's pending read dies with the viewer and a fresh mount always issues its own read. - Cover the held (empty-srcdoc) preview with the standard artifact-preview-first-load loading indicator instead of showing a bare white iframe, mirroring the URL-load path's cover. * fix(web): remount deck srcdoc preview after a hidden-tab load (#6587) A deck that finishes parsing into its srcdoc iframe while the browser tab is hidden never experiences a real layout pass: Chrome keeps the hidden iframe's child viewport at 0x0, so the deck's one-shot fit resolves to transform: scale(0), and the in-frame recovery loop (chaseFirstLayout, 30 x 50ms resize nudges) is exhausted by background-timer throttling long before the user returns. Neither the host nor the srcdoc bridge listened for visibilitychange, so nothing ever re-ran the fit — the main stage stayed permanently white while the host-rendered thumbnail rail and speaker notes looked healthy. The srcdoc onLoad handler now latches whether the load completed with document.visibilityState === 'hidden'. On the first return to a visible document, the active viewer remounts the srcdoc iframe for a fresh parse (the same mechanism Comment re-activation already relies on) and clears the latch, so visibility round-trips after a healthy visible load never remount (no flicker) and retained viewers (workspaceActive === false) are never touched. Slide position is restored by the existing syncCachedSlideStateToIframe onLoad path. * fix(web): stop pinning the srcDoc loading cover over a loaded empty file The srcDoc first-load cover keyed on srcDoc emptiness alone (!srcDocTransportContent), which cannot distinguish "content still pending" (previewSource === null: the scoped-asset hold or a Reload's synchronous clear) from "loaded, zero bytes" (previewSource === ''), so a legitimate empty HTML file kept a permanent role=status/aria-busy loader with no request in flight. Narrow the condition with an explicit previewSource === null pending check; the redirect-loop-blocked and lazy-transport branches are untouched. * fix(web): run the hidden-load recovery check on viewer activation The visibilitychange recovery listener only exists while the viewer is active. When a retained deck's load completes in a hidden browser tab (latch armed, correctly no action) and the browser returns to visible BEFORE the user clicks back into the project, the event fires with no listener armed and never replays — activation then re-arms the listener but the latch dangles and the 0x0-parsed deck stays white on exactly the switch-back-to-the-project-tab flow. Run one immediate check after addEventListener: the handler's own guards (visible + latch + srcdoc active + deck) make it a one-shot no-op everywhere else, and the latch keeps it idempotent across effect re-runs. |
||
|
|
9065a67a5b |
fix(web): stop provisional comment pin numbers colliding after deletion (#6517)
* fix(web): stop provisional comment pin numbers colliding after deletion The provisional pin bubble for a brand-new, not-yet-saved comment was numbered comments.length + 1, but the daemon assigns MAX(pin_seq) + 1 on create. Pin numbers are permanent (a deletion retires its number), so after deleting an earlier comment the count-based guess collided with a pin still on the canvas (e.g. one surviving comment numbered 2 -> new pin also showed 2). Introduce provisionalNextPinNumber() in apps/web/src/comments.ts, which mirrors the daemon's MAX(pin_seq)+1 invariant over the rendered marker numbers, and use it for the active-pin preview in FileViewer. * fix(web): retire non-open comments' pin numbers in the provisional pin Review follow-up (PR #6517): the provisional pin number was computed over creationSortedSideComments, which filters to the current file AND status === 'open' — narrower than the daemon's MAX(pin_seq) scope, which scans every row for the project/file. A resolved/attached/failed comment keeps its pin_seq row, so with a hidden pinSeq of 2 and no open pins the bubble showed 1 while the daemon would assign 3. Compute provisionalNextPinNumber over the file's comments across ALL statuses in FileViewer and pass the result into CommentPreviewOverlays, so the overlay's open-only marker list stays untouched while the provisional number mirrors the daemon's full scope. |
||
|
|
caae6a373e |
feat(web): localize DeepSeek V4 Flash campaign (19 locales) (#6561)
* feat(web): localize DeepSeek V4 Flash campaign across 19 locales Move all campaign user-facing copy into Dict keys so modal, model badges, and the workbench pricing badge follow the active UI language. Also route personal free accounts through amrAccountPlan and portal the campaign badge above workspace-tabs chrome so it stays clickable. * fix(web): fully translate DeepSeek campaign copy for all 19 locales Replace English placeholders in incomplete locale packs and add a matrix test so non-English campaign strings cannot silently equal the EN fallback (with an explicit allowlist for intentional day-unit reuse). * ci: re-trigger validation after runner queue stall * ci: bootstrap Resolve runner profiles on Blacksmith when mode is blacksmith OD_CI_RUNNER_MODE=blacksmith still scheduled the profile-resolution job on nexu-runners-small, so a private ARC stall blocked every trusted PR before workloads could reach Blacksmith. Align bootstrap with the mode contract. * ci: ensure pull_request workflow fires for blacksmith bootstrap * revert: drop Blacksmith CI bootstrap change from campaign PR Keep this PR scoped to DeepSeek campaign i18n + badge/audience fixes. The runner-bootstrap alignment belongs in a separate CI PR with topology coverage, not a backport-labeled product fix. * ci: re-run validation on GitHub-hosted runners (economic mode) * ci: re-fire pull_request checks for Validate workspace |
||
|
|
8b3ec59cfe |
feat(campaign): DeepSeek V4 Flash free-week surfaces (landing + web + attribution) (#6507)
* feat(campaign): port DeepSeek V4 Flash free-week surfaces from #6450 1:1 port of the demo net diff (base feat/workspace-team) onto main. Landing home banner, landing pricing benefits, cloud-team-demo review page, in-app home modal, workbench badge, model-switcher benefit badge, and the campaign attribution/analytics pipeline. No behavior changes in this commit; agreed behavior fixes land separately. * fix(campaign): show the DeepSeek V4 Flash modal only on the active home view The dialog portals to document.body while EntryShell hides inactive entry views with display:none, so the campaign escaped the home view and interrupted projects/tasks/plugins routes. Thread EntryShell's existing isActive signal through HomeView into a new `active` prop: when inactive the modal neither opens nor renders (and closes without being marked seen, so returning to home while still unseen re-opens it). Requirement: 弹窗只在 #/home 展示,不在编辑器或其他工作流中打断用户。 * fix(campaign): fail closed when frequency-control storage is unreadable hasSeenCampaign() answered "unseen" when localStorage threw (private mode / storage disabled), so the modal re-opened on every Home mount even though markCampaignSeen() could never persist the dismissal. The campaign promise is one appearance per activity window — an unreadable store now counts as seen. * feat(campaign): make the paid 立即使用 CTA switch to DeepSeek V4 Flash for real 产品拍板 D5 + Demo 表格「点击立即使用模型切换为DSV4」: the paid modal CTA previously only clicked the composer chip open and highlighted it. Now it performs the actual switch — agent to Cloud (amr), model to deepseek-v4-flash — through EntryShell's onAgentChange/onAgentModelChange persistence pair (the same sink InlineModelSwitcher writes through), threaded HomeView → DeepSeekV4FlashCampaign as onUseCampaignModel. The chip keeps the data-campaign-highlight pulse as feedback but no longer opens the picker; the use_now analytics element is unchanged. * fix(campaign): carry metricsConsent + device id on the unpaid modal upgrade The unpaid modal's recordAmrEntry was the only campaign touchpoint that dropped metricsConsent — the workbench badge and the model-switcher upgrade already forward it — so the consent-gated AMR analytics mirror never fired and the plans URL missed od_device_id. Thread config.telemetry.metrics and config.installationId from EntryShell through HomeView into the modal, and stamp the URL via amrHandoffDeviceId + attributedAmrUrl(plansUrl, attribution, deviceId). * fix(campaign): make the campaign/campaignAudience/campaignUsage review params production-inert 产品拍板 D7 + 需求文档「该入口仅用于本地 Demo,不作为线上产品规则」: the review URL fixtures forced campaign surfaces for real users on released builds. Web: new isCampaignReviewAllowed() gate (NODE_ENV !== 'production', inlined at build time) now fronts isDeepSeekV4FlashCampaignReview, the campaignAudience override, the modal's shouldForceCampaignReview, and the switcher's campaignUsage read. Landing (Astro): inline scripts cannot see build-time constants, so the home banner, pricing page, and cloud-team-demo inject the verdict as data-campaign-review-allowed (__OD_LANDING_NOINDEX__: staging/PR previews true, production false) and preview activation now requires it. The [locale] wrappers re-render the same components, so they inherit the gate. * fix(landing): stamp campaign attribution only inside the activity window Pricing subscribe CTAs and the cloud-team-demo upgrade/pricing clicks minted __odRecordCampaignEntry with campaignId and stamped campaign_id on analytics props unconditionally, so clicks outside the fixed Aug 6-13 window were counted toward the campaign. Both surfaces now pass the campaign id and the campaign_id prop only while campaignActive is true; od_entry_* attribution itself is still recorded outside the window, just without the campaign stamp. Inbound first-touch od_campaign_id preservation in posthog-analytics.ts is untouched. * test(campaign): use undefined-safe access for window.open mock calls Strict TS (tsc -b) flags open.mock.calls[0][0] as possibly undefined in the modal spec added for the unpaid-upgrade consent behavior; go through String(...[0]?.[0]) instead. No behavioral change. * chore(campaign): drop demo-only artifacts before shipping Remove the cloud-team-demo static review page (fake team cards and prices, noindex but publicly reachable — review-only material that must not ship), its locale wrapper and test, and five unreferenced campaign poster PNGs left over from demo iterations. The team-modal campaign surfaces the page previewed live in the real Cloud plan-selection component (powerformer/vela#1379). * fix(campaign): make review URLs a pure UI projection with zero config writes Product boundary ruling: the client-side campaign change is a badge next to DeepSeek V4 Flash (auto-hides after the window) plus the modal CTA reusing the existing model-selection callbacks — selection behavior must not change. The demo's review fixtures violated that: an effect force- switched the persisted agent to AMR under ?campaignAudience=, and the model write-back effect persisted the review projection into the user's config. Both writes are removed; review URLs now project AMR + DeepSeek purely in the UI and leave persisted state untouched. This also roots out the CI heap OOM in InlineModelSwitcher.compact-model-click.test.tsx (run 31076791463): the stateful test wrapper re-rendered on every persisted write, looping the unconditional write-back forever. With review write-back gone the file completes in ~10s. The review-URL test now asserts the projection and that onAgentChange/onAgentModelChange are never called. * refactor(campaign): remove DeepSeek V4 Flash URL review backdoors Product decision: the ?campaign= / ?campaignAudience= / ?campaignUsage= review parameter mechanisms are deleted entirely. Campaign visibility is now decided only by the real activity window plus the real subscription audience, and pre-launch acceptance switches to a temporary startAt constant override instead of URL fixtures. web: - campaigns/deepseek-v4-flash.ts: drop review param constants, review gate, review detection, and the audience URL override; visibility and audience resolution take only `now` (plus plan/loggedIn); restricted presentation kept as a reserved branch for the backend usage-limit signal (no trigger wired yet) - DeepSeekV4FlashCampaign.tsx: drop the forced review open and the synthetic openedAt+7d countdown; the countdown always runs against the real endAtExclusive boundary - InlineModelSwitcher.tsx: drop campaignUsage URL read, audience override, review agent/model fixtures and the currentAgent/model review projection (incl. data-campaign-review); 已暂停 and 升级可用 badge branches stay as reserved presentation flags with no trigger - EntryShell.tsx / use-deepseek-v4-flash-campaign.ts: no more location.search plumbing; home-hero.css review-width rule removed landing-page: - index.astro / pricing/index.astro: preview branch removed (review params, review-allowed attribute, synthetic preview countdown); campaignActive follows the fixed window only - _lib/deepseek-v4-flash-campaign.ts: reviewParam field removed tests: review-only specs deleted; convertible specs rewritten as pure time-window assertions (clock pinned inside/outside the real window); new negative specs pin that no campaign surface reads URL parameters. * fix(campaign): device id on badge handoff + impressions on window-open transition Two review findings from PerishCode, both verified real: - EntryShell workbench-badge handoff called attributedAmrUrl without the device id, so opted-in clicks omitted od_device_id (confirmed in live browser acceptance: the modal URL carried it, the badge URL did not). Now derives it via amrHandoffDeviceId like the other two touchpoints; the source-contract assertion pins the three-arg call. - The pricing page emitted campaign impressions only during initial script execution, so a tab opened before startAt showed the surfaces at launch without any surface_view. Impressions now share the reveal loop's cadence with a one-time flag, covering both load-time and mid-session window-open transitions. * temp!: open the campaign window early for pre-launch acceptance — REVERT BEFORE MERGE startAt 2026-08-06T20:00 → 00:00 (+08:00) in both front-end constants so reviewers can see the real campaign state (true countdown, no review backdoors) on local runs and the PR preview today. endAtExclusive is untouched. This commit MUST be reverted before this PR enters the merge queue — merging it would launch the landing surfaces ahead of 20:00. * test(campaign): derive the start-instant countdown from the window constants Pins the assertion to endAtExclusive - startAt instead of a literal '7天 00:00:00', so the temporary pre-launch start-time override (and any future window change) cannot silently break the suite. * Revert "temp!: open the campaign window early for pre-launch acceptance — REVERT BEFORE MERGE" This reverts commit |
||
|
|
b014b969c6 |
fix(web): lazy-render design file grids and gate thumbnail fetches (#6513)
* fix(web): lazy-render design file grids and gate thumbnail fetches The design files panel root view intentionally lists every nested file, so a web-clone project can put 4000+ HTML files in one section. Every page card rendered at once, and every HtmlCardThumbnail fetched its content on mount, exhausting local sockets (net::ERR_INSUFFICIENT_RESOURCES) and starving the web<->daemon proxy. - Thumbnail content fetches now start only once a card nears the viewport (IntersectionObserver, 800px bottom rootMargin) and pass through a FIFO pool capped at 6 concurrent fetches; slots return on settle and on unmount, including tasks still queued. - The page-card grid and image masonry render incrementally (48 per batch) behind an invisible end-of-grid sentinel keyed per batch, so the grid reads exactly like a fully rendered one; directory and tab changes reset the revealed count. - Environments without IntersectionObserver keep the previous eager behavior. * fix(web): hold thumbnail fetch slots until the request settles Review follow-up: releasing a slot during effect cleanup freed pool capacity while the underlying fetch was still on the network, so a directory/project switch with six thumbnails pending could start six replacement fetches and push real connection concurrency to twelve. Cleanup now only abandons the reservation: queued tasks are dropped before they start, but a started request keeps its slot until it settles, so the cap bounds actual network concurrency across unmount/remount churn. The regression test encodes the exact switch-while-pending scenario. |
||
|
|
886bcc4db0 |
feat(web): streamline onboarding model source flow (#6475)
* feat(web): streamline onboarding model source flow * test(e2e): align onboarding coverage with cloud-first entry * test(e2e): isolate cloud status in non-auth specs * test(e2e): keep restoration focused on tab state * test(e2e): close cloud-first onboarding gaps * test(e2e): align runtime checks with cloud-first gate * ci(e2e): retire automations from P0 gate --------- Co-authored-by: Amy <1184569493@qq.com> |
||
|
|
ad4f3609ca |
fix(collab): quell the packaged-client cloud 502 storm (heartbeat backoff, upstream error relay, proxy replay) (#6521)
* test(collab): red specs for the packaged-client presence/proxy 502 storm Encode the three client-side halves of the 502 storm as failing specs on main before any fix: - web: a persistently failing presence upstream is heartbeated every 10s forever (62 POSTs in 10 minutes) with no backoff. - daemon: every vela CLI presence failure is flattened into a 502 whose message embeds the spawned command line; upstream 403/404 rejections are indistinguishable from infra outages, every beat re-spawns the CLI, and presence spawns carry no timeout. - web sidecar: an idempotent /api request that dies with a connection reset on a reused pooled daemon socket surfaces as a synthesized 502 instead of being replayed once on a fresh connection. createDaemonProxyHandler is exported as the proxy spec's seam. * fix(collab): stop the packaged-client presence/proxy 502 storm Three client-side halves of one incident (hours of ~2050 DevTools errors: POST /presence/heartbeat and PUT /workspace/billing/interests 502ing every few seconds while the collab upstream rejected one project): - web CollabClient: presence heartbeats move from a fixed 10s interval to a self-scheduling chain with exponential backoff on consecutive failures (10s -> 20s -> 40s -> ... capped at 5min, +/-10% jitter). One success or a new presence identity restores the base cadence, so recovery is untouched. - daemon presence routes: vela CLI failures are classified via the CLI's 'API request failed with status NNN' output. Upstream 401/403 relay as 403, 404 as 404, timeouts as 503; only genuine infrastructure failures keep 502. Response bodies no longer embed the spawned command line or stderr (daemon log keeps the full detail). Repeated consecutive failures for one project open a 20s negative cache that answers without spawning another CLI process, and presence spawns now carry a 10s timeoutMs so a wedged CLI cannot pile up children. - web sidecar proxy: proxied requests use an explicit keep-alive agent whose pooled idle sockets die well before the daemon's 120s keep-alive window, and an idempotent request with a small fully-buffered body that fails with ECONNRESET/EPIPE on a reused pooled socket before any response bytes is replayed once on a fresh connection instead of surfacing a synthesized 502 the daemon never sent. Non-idempotent methods and large bodies keep the streaming path and are never replayed. |
||
|
|
9ce7b3aeec |
fix(byok): restore BYOK model switching in the home and composer pickers (#6515)
* fix(byok): restore BYOK model switching in the home and composer pickers Two #6142 regressions with the same root shape: a model-picker surface stopped deriving its catalogue from the active execution mode. - InlineModelSwitcher: the compact home popover unconditionally rendered the local CLI agent's models, so a BYOK chip (e.g. gpt-4o) opened onto the Open Design cloud catalogue. The popover body now always reflects config.mode; compact only chooses layout density. - AvatarMenu: the project composer popover collapsed the BYOK model area into a read-only box, dropping the previously working model switcher. BYOK mode now offers a selectable provider catalogue (shared cache + local discovery fetch + curated seeds) writing through onApiModelChange, mirroring the daemon-mode list. * test(e2e): expect the BYOK catalogue in the topbar switcher visual case The settings-workspace visual case pinned the #6142 regression as expected behavior: it asserted the compact BYOK popover has no in-place model dropdown and only a Settings hint. With the BYOK catalogue restored, the case now asserts the provider model picker renders in place (current model shown, options selectable from a deterministic mocked catalogue) while the execution-configuration guards stay: no mode segmented control and no provider tabs in the top bar, and the Settings route entry remains. |
||
|
|
66004e0cd7 |
test: raise CI flake budgets for waitFor and wall clocks (#6415)
* test: raise CI flake budgets for waitFor and wall clocks
Web jsdom waitFor was dying on the 1s default under runner contention.
Daemon deploy wall-clock asserts, logo body-timeout margin, and vela
fixed sleeps had zero/thin slack on slow CI CPUs.
* test: scope flake budgets and restore ReDoS guard
Keep the doctype prolog linear-time assert at 500ms so a reintroduced
lazy-body regex (~5s) cannot pass. Drop the global waitFor 5s raise and
apply a 2s budget only to the App.connectors privacy-choice waits that
flaked at 1015–1093ms under CI contention.
Generated-By: looper 0.11.8 (runner=fixer, agent=grok-build)
* test: restore CSS budget and abort-aware logo body mock
Generated-By: looper 0.11.8 (runner=fixer, agent=grok-build)
* test: wait for Vela login close before no-fallback assert
loginInFlight can go idle between child exit and close after cancel,
so the canceled late-fallback test now waits on supervisor settlement
(close/error terminal + pending fallback) instead of status idle.
Generated-By: looper 0.11.8 (runner=fixer, agent=grok-build)
* test: narrow AbortSignal for logo body mock typecheck
Preflight failed because init.signal was still possibly null after the
optional guard. Capture a local signal so tsc can prove it is defined.
Generated-By: looper 0.11.8 (runner=fixer, agent=grok-build)
* test: use global Testing Library asyncUtilTimeout 5s
Raise waitFor/findBy failure budget in the shared jsdom setup instead of
scoping a 2s override to App.connectors privacy cases. Drop no-op deploy
comment churn that did not change the ReDoS wall-clock guards.
* test: scope waitFor budget to privacy waits only
Drop suite-wide Testing Library asyncUtilTimeout 5s so unrelated
waitFor/findBy failures still surface assertion errors quickly.
Restore a 2s timeout only on App.connectors first-run privacy waits
that flaked under CI contention.
Generated-By: looper 0.11.8 (runner=fixer, agent=grok-build)
* Revert "test: scope waitFor budget to privacy waits only"
This reverts commit
|
||
|
|
2671061c47 |
fix(web): settle project workspace scope when bootstrap arrives late (#6504)
useProjectWorkspaceScope skipped its mount-time scope read whenever the current render could seed from `initialScope`. That answer is recomputed every render, but state is seeded only once, in the useState initializer. App.tsx fills routeProjectSnapshotRef asynchronously, so a route bootstrap that resolves after ProjectView mounted flips `initialScopeCanSeed` on an already-running hook. Because it is an effect dependency, the effect re-ran, aborted the in-flight GET /api/projects/:id/workspace-scope, and returned without fetching or setting state — pinning `loading: true` forever. Nothing could recover it: with no resolved scope the workspace invalidation SSE never subscribes, so no event could bump the revision and retry. The stuck `loading` reaches ProjectView as `workspaceContextReadOnly`, which makes `viewerOnly` true and renders the whole workspace as a read-only shared project — dead composer, disabled create actions — on a project the user owns. Key the skip on whether state was actually seeded at mount, which is what the early return always assumed. A late bootstrap scope is now ignored and the mount-time read settles normally. |
||
|
|
e6aeb38da6 |
feat(analytics): track recoverable task outcomes (#6411)
Co-authored-by: bone3deep1962-collab <bone3deep1962@gmail.com> |
||
|
|
125f3739ea |
fix(web): back off billing retries instead of hammering a failing transport (#6446)
The packaged client's first open of an unmaterialized team project fires a burst of od:// requests; when that burst hits transient transport failures the proxy answers with synthetic 502s, and the billing read's retry loop made it worse: every mounted consumer armed its own fixed 5s timer, each timer's retry event fanned out to every consumer, and each listener force-refetched past the coalescing cache — one failure became a standing 5s-cadence request storm that fed the very burst it was waiting out (observed by QA as hundreds of repeated billing requests while the left panel span). - Replace the per-hook fixed 5s retry timer with one module-level schedule per requestKey whose delay grows 5s → 10s → 20s → 40s → 60s cap while failures are consecutive; a success resets the count but deliberately leaves a pending timer armed so stale consumers still re-sync. - Stop forcing the retry read. Failures are never cached, so the plain coalesced path is a genuine refetch, and a retry that lands just after a concurrent consumer's success now joins that fresh result instead of evicting it and issuing another request. - Hard-expiry revalidation keeps its forced read via an explicit force flag on the retry event — its cached answer is void by definition, which the plain path cannot know. Red tests first: both new specs fail on the previous implementation (fixed-cadence retry fires at t=10s; retry stampedes a third request past a 0.5s-old success) and pass with the fix. 403 stays fail-closed with no retry; the 83 web test files touching workspace context/billing all pass. |
||
|
+13 |
356c8c364f |
feat(workspace-team): team workspaces, shared resources, workspace-scoped billing, and the #5517 redesign (#6142)
* fix(web): scope plugin URL imports to workspace * test(web): cover billing interest remount lifecycle * feat(packaged): enable the workspace-team transport for test-profile builds The vela test backend now serves the workspace-team API, so a packaged build baked with the test AMR profile should drive team projects / collab / resource sharing there instead of leaving them dormant. Replace the single feature-test equality check with a profile -> vela web origin map so adding a backend is one entry, and keep every unlisted profile (prod above all) off. * test(daemon): cover billing catch-up after SSE reconnect * fix(collab): surface and start first materialization when opening a shared project QA P0: a brand-new member on a fresh install joins someone's workspace, opens a project from it, and sees no loading state and no files — content only appears on a SECOND open, much later. Two defects, both on the first `/collab/status` request that opening a shared project makes. 1. No pull is started. The daemon's self-materialization block was gated on `callerIsOwner`, so a non-owner member got the placeholder record registered and nothing else. The web's auto-pull is gated on `publishedVersion` advancing past its cursor, and a fresh daemon's first status response cannot carry a published head: `collab.publishedVersion()` reads an in-process map that has never been written, and the real hub head is fetched fire-and-forget into `headEnrichmentCache` for a LATER poll to consume. Materialization therefore arrived only whenever a proactive lane (hub push / reconnect catch-up / the 30s recovery floor) next fired — the "second open" the report describes. 2. No download state is shown. `useProjectCollab.downloadPending` — which picks DesignFilesPanel's "Syncing files from the team…" over its empty state and create-a-file CTAs — reads only `publishedVersion > cursor`, `contentTransferState`, and an in-flight pull. All three are blank on that first response, so the member was shown an empty project inviting them to start creating over content that was still on its way. The fix makes the placeholder stamp the load-bearing signal on both sides: - `materializePlaceholderOnOpen` (routes/collab-sync.ts) replaces the owner-only block: any viewer opening a shared project whose only local record is an unmaterialized placeholder starts its pull on that same request, through the same coalesced flow. The retracted-share heal in its catch stays owner-only — a member who hits the hub tombstone has merely lost access and must not unshare anyone's project. - `/collab/status` reports `awaitingFirstMaterialization`, a purely local, synchronous fact that needs no hub round-trip and is therefore available on the very first response. `od collab status` prints it too. - `localFilesAreNotTheContentYet` (useProjectCollab.ts) makes it always download-pending, bypassing the member-only `shouldAutoPull` gate: whether the local files are the content is a fact about the record, not about who may pull it, so the owner of an unmaterialized placeholder (the reinstall case, recvqzaDvUU6B3) stops seeing the same empty state too. Red evidence: all six new specs fail on origin/feat/workspace-team @ |
||
|
|
d87ba8e5b9 |
ci: route Linux validation to Nexu runners (#6407)
* ci: route UI P0 to Nexu runners * ci: route Linux validation to Nexu runners * ci: size Nexu runners by workload * ci: preserve economic runner bootstrap * test: budget scope process coverage * ci: run browser validation on large runners * ci: align runner mode bootstrap semantics * ci: cap Playwright workers on large runners * ci: match hosted Playwright concurrency * test(desktop): accept full retry after interrupted download * test(web): stabilize privacy choice click * ci: use preseeded Playwright on Nexu runners |
||
|
|
798a85e8da | fix(i18n): localize new project folder action (#5977) | ||
|
|
4d0376e437 |
fix(byok): restore local runtime configuration (#6356)
* fix(byok): restore local runtime configuration Return BYOK settings to the browser-local configuration path and pass credentials only to the active OpenCode child run. Remove the unpublished secure-profile migration, credential backends, API, CLI, contracts, and MCP exposure while retaining raw-secret rejection on external MCP calls. Validated with focused web and daemon suites, full web tests, workspace typecheck, guard, and diff checks. * test(e2e): align BYOK settings checks with local storage Update the settings UI contract after rolling BYOK credentials back to local app config storage. Assert the entered key is persisted, keep incomplete BYOK drafts from activating, and seed the Local CLI isolation case with a usable BYOK key. Validated with all seven P0 cases in settings-api-protocol.test.ts and the e2e TypeScript check. * fix(daemon): validate plugin request ids explicitly Use a field-specific canonical UUID/ULID validator for attributed request IDs and document that format in the MCP schema and server instructions. Invalid calls now identify requestId instead of incorrectly blaming pluginWorkflowId, while Codex can generate a valid ID on its first attempt. Validated with a red/green MCP observability regression test, daemon typecheck, repository guard, and a real Local Codex plugin smoke that completed in one start_run attempt. * test(pack): follow active PTY consumer in prebundle coverage Point the macOS prebundle boundary test at the Terminal runtime after the retired BYOK credential service was removed. This keeps native node-pty externalization covered without restoring legacy credential code. Validated with the focused mac-prebundle suite, the complete tools-pack suite, tools-pack typechecks, repository guard, and deleted-file reference scans. --------- Co-authored-by: Cheems <94773058+itscheems@users.noreply.github.com> |
||
|
|
517f39acde | fix(web): dismiss stale todo after continuation starts (#6307) | ||
|
|
dac2729115 | fix(web): keep Azure deployment names editable (#6034) | ||
|
|
4a6c2da730 |
fix(web): surface BYOK migration validation errors (#6280)
* fix(web): surface BYOK migration validation errors Preserve structured daemon errors during legacy credential migration and reserve offline guidance for fetch failures. Keep legacy browser credentials until migration succeeds, and cover HTTP validation, malformed responses, and network failures. Validated with focused web tests, web typecheck, workspace guard, and workspace typecheck. * fix(web): stabilize BYOK persistence error telemetry Map secure-profile HTTP and network failures to semantic error codes before emitting settings_byok_test_result, while preserving existing fallback behavior for unrelated connection-test errors. Validated with the complete web test suite, web typecheck, workspace typecheck, guard, and git diff --check. --------- Co-authored-by: Cheems <94773058+itscheems@users.noreply.github.com> |
||
|
|
1da4f9e0f8 | fix(web): persist browser preview viewport (#4899) | ||
|
|
8518ec847b |
fix(amr): recover late login failures and trace auth stages (#5986)
* fix(amr): recover late login failures and trace auth stages * fix(amr): preserve scoped cancel attempt IDs Generated-By: looper 0.11.1 (runner=fixer, agent=codex) * fix(amr): defer unsupported Vela auth stages Generated-By: looper 0.11.1 (runner=fixer, agent=codex) * fix(amr): narrow fallback telemetry to shipped Vela Generated-By: looper 0.11.1 (runner=fixer, agent=codex) * fix(amr): preserve live login after stale cancel Generated-By: looper 0.11.1 (runner=fixer, agent=codex) * fix(amr): preserve cancel during login startup Retain cancellation intent when a provisional cancel races the delayed canonical login response, then cancel the canonical attempt before polling can begin. Generated-By: looper 0.11.1 (runner=fixer, agent=codex) * fix(amr): rejoin newer login after startup cancel Generated-By: looper 0.11.1 (runner=fixer, agent=codex) * fix(amr): preserve cancel across web login starts Generated-By: looper 0.11.1 (runner=fixer, agent=codex) * fix(amr): retain cancel when status refresh fails Generated-By: looper 0.11.1 (runner=fixer, agent=codex) * fix(amr): cancel onboarding status preflight Treat cancellation before an auth attempt exists as a local preflight cancel so a delayed status response cannot start login.\n\nGenerated-By: looper 0.11.1 (runner=fixer, agent=codex) |
||
|
|
809d45f27c |
feat(integrations): add the local Open Design Cloud flow for Codex (#6055)
* feat(daemon): expose Vela login through CLI and MCP * feat(mcp): add interactive local brief card * feat(packaged): bootstrap local MCP headlessly * feat(daemon): secure local BYOK credential profiles * feat(cli): manage secure BYOK profiles * feat(web): store BYOK keys in secure profiles * fix(runs): deduplicate cloud generation requests * feat(daemon): add end-to-end plugin observability * fix(pack): include native PTY runtime in desktop packages Lazy-load node-pty in the daemon so missing native binaries only disable Terminal and interactive Keychain operations instead of crashing startup. Ship and validate target prebuilds in macOS and Windows packages. Repair the macOS spawn helper before signing, and invalidate stale Windows packaging caches. Cover the packaged PTY path in macOS and Windows smoke tests. Require Vela CLI for every beta desktop build target. * fix(mcp): localize and stabilize plugin briefs Keep collect and confirm on one MCP Apps card, report intrinsic size changes, and remove the fragile widget-state fallback. Localize brief forms, summaries, status, and public errors from the current request or Host locale while preserving stable protocol ids and selections. Keep internal runtime identifiers out of user-facing MCP guidance. * fix(packaged): register live Studio URLs with daemon Register the packaged web sidecar loopback origin over namespace-scoped daemon IPC after dynamic binding. Invalidate install-info by the live web port and remove the second MCP URL cache so run and project responses use the current Studio address. Validate loopback origins and cover registration, rebind, and cache refresh. * fix(mcp): stabilize brief confirmation lifecycle Keep confirmed briefs locked after Host publication failures and retry only the cached follow-up message instead of confirming twice. Replace blank initialization with a compact loading state, accept immediate and delayed tool results, and bump the MCP Apps resource to v3 so Codex does not reuse the stale card. Add an executable JSDOM Host bridge harness covering double-submit, publication retry, delayed results, and repeated draft notifications. Validated with the full daemon suite, workspace typecheck, and guard. * fix(mcp): prevent brief app resize feedback loops Replace the self-observed intrinsic-height loop with deduplicated body measurements so Codex MCP app iframes do not repeatedly resize themselves and fail sandbox startup. Bump the brief resource URI to v4 to avoid cached v3 cards and cover the regression with the focused brief-app spec. Validated with daemon tests/build, workspace guard/typecheck, a verified beta DMG, and a direct packaged MCP resource smoke. * fix(mcp): use the final Open Design plugin identity Make open-design the only accepted external plugin ID across MCP tools, run attribution, artifact version origins, and telemetry. Rename the Brief MCP App resource to v5 and reject the unpublished old ID instead of aliasing it. Validated with 171 daemon tests, 5 web tests, daemon/web typechecks, and guard. * chore(pack): bump Vela CLI to 0.0.27 * chore(nix): refresh pnpm dependency hashes * fix(mcp): honor host and plugin request contracts Respect independently advertised MCP Apps capabilities and prefer the Codex-native follow-up bridge so confirmed briefs render as readable user turns instead of context-only messages. Bump the brief resource to v6 to invalidate stale cards.\n\nMark Vela login and account probes as open-world interactions, and require stable request IDs for attributed generation so lost responses cannot create a second logical run. * fix(cli): support keyless BYOK profiles Add an explicit --no-api-key save mode for local and self-hosted providers while keeping credential-bearing profiles stdin-only. Reject missing or conflicting credential modes and cover both paths in the CLI tests. * fix(pack): replace headless owner when desktop opens Advertise headless packaged runtimes as having no visible window, then shut them down before a normal desktop launch continues instead of treating SHOW as a successful focus. Validated with the focused packaged headless and launcher lifecycle tests (20 passed). * fix(pack): clean up failed headless startup Acquire the headless identity, sidecars, and IPC server through one failure-atomic lifecycle so later bootstrap errors close every previously acquired owner in reverse order. Cover MCP-install and web-identity publication failures; validated with 22 focused tests and the packaged typecheck. * fix(byok): remove raw provider request contract Expose only daemon-owned BYOK profile references from shared run types and the web transport, while retaining the daemon's fail-closed rejection for legacy or untyped raw credential payloads. Validated with 76 web provider tests plus web and contracts typechecks. * fix(daemon): preserve interrupted run idempotency Hydrate queued or running durable requests as DAEMON_RESTARTED failures before idempotency reuse, preserving the original request and workflow mappings instead of creating a second cloud operation. Share the restart transition with boot reconciliation and expose synthetic terminal events to reattaching clients. Validated with 48 focused tests and the daemon typecheck. * fix(byok): roll back failed credential metadata writes Snapshot secure-store values before mutation and compensate create, update, and delete operations when profile metadata cannot be persisted, preventing orphaned or lost credentials. Validated with nine focused credential-service tests and the daemon typecheck. * fix(web): reconcile selected BYOK profile metadata Treat the daemon's selected secure profile as authoritative for protocol, endpoint, model, and API version so CLI profile edits cannot leave the browser showing stale execution settings. Validated with 66 config tests and the web typecheck. * fix(byok): migrate legacy credentials before cleanup Import each supported legacy browser credential into a stable daemon-owned secure profile before removing plaintext copies. Keep the original browser record on secure-store or unsupported-provider failures and surface a Settings recovery path. Cover successful migration, duplicate projections, and injected secure-backend failure while preserving existing Bedrock downgrade behavior in memory. * fix(byok): secure Windows profiles with DPAPI Dispatch native Windows BYOK profiles to a CurrentUser DPAPI backend rooted under the resolved daemon data directory. Secrets enter PowerShell only through stdin, encrypted blobs stay separate from non-secret profile metadata, and API responses remain secret-free. Add platform dispatch coverage, a Windows-only real DPAPI round-trip test, and the packaged Windows smoke path for create, resolve, response-redaction, and delete behavior. * fix(web): preserve config writes during hydration * fix(byok): persist secure credentials across entry flows * test(byok): cover secure onboarding persistence --------- Co-authored-by: Cheems <94773058+itscheems@users.noreply.github.com> Co-authored-by: lefarcen <935902669@qq.com> |
||
|
|
7c8c7b79f3 |
fix(web): allow skipping required chat questions (#6177)
* fix(web): allow skipping required chat questions * fix(web): auto-skip unanswered chat questions --------- Co-authored-by: xiaoche-hub <298951296+xiaoche-hub@users.noreply.github.com> |
||
|
|
f9ccdfc4e3 | fix(web): prune per-project tab-state cache on project deletion (#5761) | ||
|
|
bfa7060372 | test: remove redundant UI P0 variants (#6153) | ||
|
|
a7e205939d |
test: remove deterministic CI waits (#6144)
* test: remove deterministic CI waits * test: stabilize workspace restoration reload |
||
|
|
3f670090d7 |
ci: narrow daemon core merge validation (#6126)
* ci: narrow daemon core merge validation * ci: preserve runtime definition shadow |
||
|
|
ad5a7d6ba1 |
Revert "feat(web): add guided inspiration and deliverable workflow (#5899)" (#6120)
This reverts commit
|
||
|
|
70f3953079 |
fix(manual-edit): keep runtime targets selected when their last style declaration clears (#6098)
Clearing a runtime-only target's final declaration (e.g. toggling an active alignment back to default) legitimately deletes its override rule, but the style-save reconcile guard used rule ownership to recognize runtime-backed targets, so the still-rendered target was classified as vanished: the save dropped the selection and forced the full-reload canvas flash this path exists to avoid. Runtime-backed is now a property of the document — the new isManualEditRuntimeRenderedSource() consults the brand payload — independent of whether the target currently owns an override rule. |
||
|
|
340cbb9b94 |
feat(web): add guided inspiration and deliverable workflow (#5899)
* feat(cli): add project rename command to CLI Introduced a new command `od project rename <id> --name "<title>"` to allow users to rename existing projects. The command validates input and sends a PATCH request to update the project name in the backend. Updated CLI help documentation to include this new command. * feat(web): enhance InspirationPicker with reference-site shortcuts and selection chips Added functionality to the InspirationPicker component, allowing users to browse reference sites (e.g., Dribbble, Behance) directly from the picker. Implemented a selection chips feature that lists all current picks, enabling easy removal across different families. Updated styles for better visual representation of selected items. Enhanced internationalization support with new translations for the added features. * feat(web): enhance InspirationPicker with detailed previews and selection management Updated the InspirationPicker component to include a detailed preview feature for selected items, allowing users to view full documents directly from the picker. Implemented a "Selected" section that displays current picks with a remove option, improving user interaction. Adjusted styles for better visual consistency and added new translations for enhanced internationalization support. * fix(web): restore uploaded inspiration thumbs and drop preview elapsed Resolve locked inspiration-upload previews via the submitted path map so answered forms still show real images, and keep the floating preview status pill as a stage label only. * feat(web): implement TaskDeliverableCard for displaying task outputs Added a new TaskDeliverableCard component to showcase the primary deliverable and its associated outputs. This includes a preview feature for various file types, an actions menu for sharing and downloading, and improved styling for better user experience. Enhanced internationalization support with new translations for deliverable-related terms. Updated InspirationPicker to utilize the new deliverable ranking and searching functionalities. * fix(inspiration): unify design system previews and source priority * fix(web): align deliverable card regression coverage * test(web): scope question form assertions * fix(web): keep the deliverable card off failed and canceled runs The card's status label is an unconditional Done, so gating it on the shared terminal predicate let a failed or canceled run that wrote files present a partial artifact as delivered while the same message reported the error. Gate on the existing runSucceeded predicate instead; those turns fall back to the plain file ledger, so the files stay reachable without the false completion signal. * fix(daemon): serialize DESIGN.md font stacks through a strict font-family grammar extractFonts is deliberately permissive and its output was interpolated verbatim into the generated <style> raw text, so a Body font value like "Karla</style><img src=…>" could close the style element and inject active markup into every auto-loaded card iframe. sanitizeFontStack now rebuilds each stack from validated family names only (quoted names, unquoted generic/system keywords), dropping anything else and falling back to the trusted default stack; both the full preview and the compact card renderer route every font interpolation through it. Card and preview fixtures with a </style> payload pin the boundary. * fix(daemon): ground additional inspiration systems in the run prompt The picker's extra design-system picks reached the agent as ids on a metadata line only, so a user-authored system contributed none of its colors, fonts, or rules. The daemon now resolves each additional id through the same DESIGN.md reader chain as the primary (workspace file first, then registry), bounds the result (3 systems, 6000 chars each), and composes an 'Additional inspiration systems' section with explicit primary-wins precedence. Prompt tests pin a unique secondary token reaching the composed prompt, both at the composer seam and end-to-end through the chat route with bento's #FAD4C0. * test(e2e): stabilize preview comment click * test(web): update runtime override history expectation --------- Co-authored-by: Amy <1184569493@qq.com> |
||
|
|
b99a9fdc3d |
feat(manual-edit): add direct manipulation and reliable history (#5890)
* feat(web): manual edit v2 direct manipulation (v2.2–v2.4) Selection overlay with action bar and drag/resize handles, floating text toolbar with range-format highlight (queryCommandState round trip), Manus color picker, image replace/crop/paste/drop, element copy/duplicate/delete, and the systematized in-place content pipeline: every element-scoped patch now reconciles the live iframe DOM from the saved source over od-edit-apply-dom (replace/insert-after/append-child/prepend-child/remove) with positional identity restamping, so commits, undo and redo never reload the canvas, never flash, and never move the scroll position. Frozen-source reloads remain only as a scroll-capturing fallback. Includes the Chromium Home/End double-booking fix, the dblclick word-selection guard, and light targets-broadcast dedupe. * test(web): manual edit v2 unit and e2e coverage Bridge (in-place apply ops, positional restamp, selection format state, Home/End consumption, dblclick selection guard), source-patches (insert readback, restore descriptors, sanitization), gestures/shortcuts/color units, and host-level FileViewer suites proving content commits mutate the canvas without a srcDoc reload, hand selection to inserted elements, and record labeled manual versions for undo/redo. Also realigns the manual-edit-history and srcdoc-reload-races helpers with the v2.1 opt-in inspector two-step flow. * docs(specs): manual edit v2 architecture spec Six-layer map (identity/chrome/style/content/history/external-sync), core invariants (DOM equals saved source without reloads; positional identity restamps on structural change; every save is a labeled manual version), patch-kind application matrix, performance contract, and the v2.1–v2.4 revision log including the real-browser verification round. * fix(tools-dev): fail fast when a sidecar process dies during startup wait waitForDaemonRuntime/waitForWebRuntime now accept a liveness probe for the spawned pid, so a crashed daemon/web process surfaces immediately instead of burning the full startup timeout. * fix(web): edit brand-kit pages in place — no reload flash, no scroll jump Brand-kit targets carry runtime-annotated ids with no markup of their own in the saved source (edits persist into the payload / runtime overrides), so the in-place readback came up empty and every commit fell back to a srcDoc reload — the flash-and-jump-to-top the user hit when editing text after an image. New od-edit-apply-dom op 'apply-content' mirrors the patch fields (text/href/src/alt/html/attributes, on*/data-od-* filtered) onto the live element instead, and set-outer-html reuses replace with the patch html. The reload fallback's scroll restore also gains a clamped-only retry ladder (600–4000ms) so async-rendering pages (brand kit renders after load) no longer land at the top when a reload is genuinely needed. * refactor(FileViewer): unify manual edit history management and enhance undo/redo functionality This update introduces a consistent approach to managing manual edit history and undo/redo stacks by utilizing refs to mirror state. The new `updateManualEditHistoryStack` and `updateManualEditUndoneStack` functions ensure that the state remains in sync with the refs, preventing stale data issues during undo/redo operations. Additionally, the handling of in-session undo (Cmd+Z) has been improved to escalate to the host's global history when no local changes remain, ensuring a seamless user experience. Tests have been added to verify the correct behavior of these features. * feat(manual-edit): enhance manual edit functionality with space scaling and improved gesture handling This update introduces a new `ManualEditSpaceScale` interface to manage the scaling between viewport pixels and CSS pixels, ensuring accurate positioning during manual edits. The `manualEditMovePreviewTransform` and `manualEditResizeStyles` functions have been updated to utilize this scaling, improving the precision of drag and resize gestures. Additionally, the `ManualEditSelectionOverlay` component now maintains pointer capture during gestures to prevent interaction issues with nested iframes. Tests have been added to verify the correct behavior of these enhancements, ensuring a smoother user experience in manual editing scenarios. * feat(FileViewer): add localized toasts for image upload process in manual edit This update enhances the manual editing experience by introducing localized toast notifications for the image upload process. New messages for uploading, processing, and successful addition of images have been added to the i18n files. The `insertManualEditImage` function now manages these notifications, providing users with real-time feedback during image uploads. Additionally, tests have been implemented to ensure the correct display of these toasts across different locales. * fix(manual-edit): drag whole image to move + lock selection frame to element box (#5888) Two direct-manipulation fixes reported from real prototype/landing/deck edits: 1. Move by dragging the image body, not only the top pill. Adds a full-interior moveBody surface inside the selection frame that starts the same undoable move gesture; scoped to image targets so text/link bodies keep click-to-edit. Rendered under the resize handles so edges still resize. 2. Keep the selection frame locked to the element during resize. The frame previously drew the gesture-intent x/width and only adopted the iframe's measured y/height, so an element that re-centers, caps at max-width, or reflows on width change left the frame drifting horizontally off the picture. adoptMeasuredExtent now takes the full measured rect and displayRect follows it, so the chrome always wraps the element. Adds ManualEditSelectionOverlay tests for body-drag move and measured-box frame sync; records the change as spec v2.5. Co-authored-by: Claude <noreply@anthropic.com> * test(web): stabilize manual edit browser validation * fix(manual-edit): address structural edit review blockers * fix(manual-edit): preserve runtime formatting and restore order * fix(manual-edit): flush toolbar styles before navigation * fix(manual-edit): preserve runtime target identity * fix(manual-edit): serialize saves and image handoff * fix(manual-edit): clear stale runtime clipboard * fix(manual-edit): scrub rich text identities * fix(manual-edit): save inspector text after formatting * fix(manual-edit): action-bar boundary avoidance, select-without-reflow, image paste shortcut (#5892) Three edit-mode canvas fixes from user feedback: 1. Action bar no longer covers the element frame/handles. A small or edge-hugging element previously had its move pill and resize handles hidden under the floating action bar (clamped to the top), so it could not be moved or resized. New pure helper manualEditActionBarTop places the bar above the frame, flips it below when the element hugs the top edge, and keeps it inside the canvas; horizontal clamping already handles the side edges. Adds a data-placement attribute. 2. Selecting a text element no longer makes it jump/grow. The bridge fused 'select' and 'enter inline edit' into one click, and making a deck text box contenteditable releases its line-clamp/height cap so it visibly reflows larger the instant it is selected. The first click now only selects (no contenteditable, no reflow); editing begins on a second click of the already-selected element or a double-click — matching how images/containers already select. 3. A selected image/container can be pasted with Cmd/Ctrl+V. The native paste event only fires while an editable element holds the caret, which never happens for non-text selections, so the shortcut did nothing for images. The bridge keydown handler now forwards od-edit-paste-request for a selected non-editable element (paired with the existing Cmd/Ctrl+C copy). Updates the 14 inline-edit bridge tests to enter edit via double-click and adds coverage for all three fixes; recorded as spec v2.6. Co-authored-by: Claude <noreply@anthropic.com> * fix(manual-edit): image corner scaling, nowrap unlock on resize, restore image paste Three edit-mode canvas fixes from user feedback: 1. Images get four corner anchors that scale aspect-locked from any corner, anchored at the opposite corner (new resize-nw/ne/sw/se gesture kinds). The dominant pointer axis drives the scale, the floor binds the larger dimension, and corner gestures skip snapping so the ratio never breaks. Corner resizes persist width AND height (space-scale corrected) plus the offsets that keep the anchored corner fixed. 2. Resizing an element with white-space: nowrap (or pre) now unlocks wrapping (normal / pre-wrap) alongside the width — a narrowed heading previously stayed on one line and overflowed its own new box. ManualEditStyles and the bridge style props gain whiteSpace. 3. Image paste works again. The v2.6 Cmd/Ctrl+V keydown branch called preventDefault, which suppresses the native paste event — the only place clipboard image bytes are readable — so pasting an image did nothing. The shortcut now arms a 150ms one-shot element-paste fallback instead; any real paste event disarms it and routes image vs element itself, so the two channels never double-fire. Adds gesture/overlay/bridge coverage for all three; recorded as spec v2.7. * fix(manual-edit): replay runtime history in place * fix(brands): reject scraped CSS junk font families before token emission An extraction can capture CSS source text instead of a real font family (e.g. Tailwind v4's `--theme(--default-font-family` from aliyun.com). Interpolated raw into the kit's `--brand-font-family`, the unbalanced parenthesis swallows every later :root declaration, so the component kit renders unstyled (UA serif, collapsed buttons) while earlier color tokens still work. isRenderableFontFamily() now guards both emission seams: the seed's fontStack (kit/variables/artifacts token block) and brandFontAssets' display-family stack (h1-h3 face). Junk families drop out and the renderable fallbacks + system stack take over. * fix(manual-edit): replay runtime-only style history from the persisted override rule Runtime-only brand-kit targets persist set-style patches in the style[data-od-manual-edit-runtime-overrides] rule, not as element inline styles. The in-place history replay read inline styles only, so redo of a runtime style change replayed empty values while reporting success — the file held the redone style while the canvas stayed on the undone one. readManualEditSavedStyles() now reads a target's persisted styles wherever they live (inline attribute for source-backed nodes, the runtime override rule otherwise), and the set-style history branch replays through it. The same inline-only read also made reconcileManualEditStyleSave() treat every runtime style save as "target vanished" — erroring, dropping the selection, and reloading the canvas — so it now consults hasManualEditRuntimeStyleOverride() before taking that path. Regressions: source-patches unit coverage for the saved-styles read and the override predicate, plus a FileViewer spec that styles a runtime target, undoes and redoes it without any srcdoc change, and asserts the redo preview carries the persisted value (red on the previous head). * fix(manual-edit): merge partial runtime style patches into the persisted rule Successive debounced toolbar autosaves send only the touched property, but setRuntimeStyleOverride() rebuilt the target's override rule solely from the incoming patch, so a runtime-only brand-kit target lost its earlier saved declarations (fontSize save, then color save -> font-size silently dropped). Persistence now merges the incoming patch into the existing rule declarations, treating empty incoming values as deletions and removing the rule once its last declaration is deleted. --------- Co-authored-by: Claude <noreply@anthropic.com> |
||
|
|
de2020fb88 |
docs: reconcile code-evolution drift (#6083)
* docs: reconcile code-evolution drift * fix(web): preserve generic-disconnect recovery retries Generated-By: looper 0.11.0 (runner=fixer, agent=codex) * fix(web): retry capped reattach probe after cleanup Generated-By: looper 0.11.0 (runner=fixer, agent=codex) |
||
|
|
a2ba3ee2ad |
feat(updater): manual cache clear, installer-reinstall floor, and crash-recovery lifecycle (#6032)
* feat(updater): manual cache clear + installer-reinstall floor on the installed outer version
Two disaster-recovery capabilities for the packaged updater:
- Reuse control.launcher.version.min properly: the gate now compares the
physically installed outer package version (read at check time from the
outer bundle's open-design-config.json via the launcher launch path;
OD_UPDATE_INSTALLED_VERSION overrides for tests), not the running payload
version. When the floor trips - or min is set but the outer version is
unreadable (conservative) - the updater selects the installer artifact and
offers a same-version installer reinstall when no newer release exists
(clamped to min <= candidate so the offer always converges). The status
snapshot carries a reinstall block {reason, installedVersion, minVersion,
url}; UpdateDialog/UpdaterPopup render reinstall copy with an optional
operator jump link. tools-release publish-metadata publishes the block from
RELEASE_LAUNCHER_VERSION_MIN(_URL) (per-channel repo vars with a _STABLE
fallback wired in all release workflows) and hard-fails when min exceeds
the release version; verify-metadata re-checks the published block and
summary-metadata surfaces the floor.
- Manual clear-cache action (od:update:clear-cache IPC, sidecar clear-cache
action, Settings > About row with a two-stage inline confirm): resets
one-shot update state (downloaded release, install freeze) back to idle,
purges releases/staging/downloads/.back, removes a stale launcher
attempt.json plus any non-confirmed desktop-handoff journal, and deletes
non-retained payload versions. Runtime active/lastSuccessful versions,
retained cleanup entries, install.json, and a confirmed handoff journal
are never touched; locked files defer through the existing cleanup.json
retry machinery.
Also: tools-pack --update-action now validates through sidecar-proto's
exported isDesktopUpdateAction so clear-cache reaches the ops surface;
tools-serve updater fixture gains control min/url knobs and CLI flags;
9 new i18n keys across all 19 locales; tools/pack/AGENTS.md documents the
gate semantics and clear-cache boundaries.
Validated locally per the updater matrix (desktop/web/tools-serve/tools-pack
suites, guard, typechecks) plus a mac release-beta closed loop: platform full
spec, same-version reinstall against a real outer/payload version split,
clear-cache recovery, and a real-feed payload update to 0.15.1-beta.4.
* test(updater): close Windows recovery acceptance
* test(updater): mirror Windows same-version reinstall recovery on the mac lane
Port the Windows runSameVersionUpdaterRecoveryAcceptance shape into the mac
full profile: after the payload cold-start acceptance, restart the packaged
app against a fixture publishing an installed-outer floor at the running
payload version and assert the same-version installer reinstall snapshot
(reason/installedVersion/minVersion/url), the popup reinstall learn-more
link, clear-cache back to idle with launcher pointers untouched, explicit
re-check recovery, and the installer open in dry-run mode — macOS has no
silent DMG install to execute, unlike the Windows NSIS transaction lane.
Extends the popup eval expression/validator with reinstallLinkVisible and
records an updaterRecovery block in the smoke summary. Full-profile test
timeout raised 240s -> 360s for the extra app restart.
Validated locally: release-smoke full profile green in 80s with the recovery
summary populated (reinstall block exact, cleared idle, recovered dmg,
dryRun install).
* refactor(release): centralize launcher version floor channel policy in one resolver
Channel policy for control.launcher.version.{min,url} is now managed as one
repo-vars pair per channel (RELEASE_LAUNCHER_VERSION_MIN[_URL]_<CHANNEL>).
Workflows pass the channel pair plus the STABLE pair through verbatim — the
|| fallback expressions are gone from YAML — and the shared resolver in
tools/release/src/storage/launcher-version-floor.ts owns resolution and
validation: pair-level stable fallback for non-stable channels (never mixing
one channel's min with another's url), version format, http(s) url, orphan
url rejection, and the floor <= releaseVersion guard. publish-metadata and
verify-metadata consume the same resolver, so verify re-derives the expected
block from the same channel policy instead of trusting a pre-resolved env.
release-workflows.test.ts pins the verbatim passthrough keys in all five
release lanes and rejects any reintroduced YAML fallback.
* feat(updater): let clear-cache rebuild a provably-owned corrupt update store
A corrupt store is one of the blocking scenarios the manual clear exists to
recover from, yet the action previously bailed on the very store errors it
should heal. clear-cache now rebuilds the update root when — and only when —
ownership is provable: the current-generation sentinel is the proof, and
everything else inside an owned root is updater cache by definition. Roots
without a sentinel, with a foreign-generation marker, or failing IO are never
touched; the original store error surfaces unchanged. Four specs pin the
boundary (corrupt metadata rebuilt, stray entries rebuilt, unowned root
refused with files intact, foreign-generation marker refused).
* fix(launcher): pre-arm delegated payload launches so pre-bookkeeping crashes roll back
A payload that spawned successfully but died before reaching its own launcher
bookkeeping left no attempt.json behind, so every later cold start re-selected
the same broken active pointer and re-delegated to the dead payload — a
permanent dead loop the installer fallback could never surface (found while
designing the crash-rollback acceptance).
The delegating parent now arms attempt.json BEFORE spawning the payload and
passes the pointer as --od-launcher-delegated-* argv. Selection gains an
active-delegated reason: an attempt matching active AND the delegated pointer
is the launch in progress, not a previous failure; only an exact active match
qualifies, so a stale pointer cannot defeat the rollback. Rollback
(last-successful) delegations deliberately carry no marker and never re-arm —
the attempt on disk IS the rollback evidence the child must re-derive.
Covered end to end: packaged outer delegation pre-arms (and skips pre-arm on
rollback), the updater's payload activation pre-arms and threads the pointer
through the deferred relaunch, and confirm clears the attempt for delegated
launches. launcher-proto 18, packaged 237, desktop 264 all green.
* fix(updater): scrub a stale payload install freeze after a rollback
A payload install freezes the updater with installResult.activeVersion
awaiting the relaunch. When that payload crashed and the launcher rolled
back, the app restarts on the OLD version with the freeze still persisted —
every later check became a frozen no-op, so a rolled-back install never saw
another update offer (the crash-rollback acceptance exposed this in its
self-heal phase). Cold-start restore now detects the broken promise —
installResult.activeVersion newer than the running version on a live process
— and drops the freeze and stale install result while keeping the verified
downloaded release actionable. The installer-artifact freeze (no
activeVersion) keeps its existing restore semantics. Red spec:
'clears a stale payload install freeze when the promised relaunch never
became the running version'.
* test(updater): full-lifecycle acceptance — silent startup, crash rollback, coverage map
Two new packaged acceptance tests on both platform lanes (mac validated
locally; win symmetric for the Windows box):
- 'applies a downloaded payload silently on the next cold start': enables the
daemon-owned allowSilentUpdates through the production GET+PUT
/api/app-config path, restarts, and asserts the first scheduler tick
installs, quits, and relaunches with converged pointers — no updater action
issued.
- 'rolls back a crashing payload and self-heals on the next good update':
repacks the real payload archive with a crash-before-bookkeeping stub
(mac: shebang script via ditto; win: a real executable — System32 where.exe
— via the installed app's bundled 7z, since a script cannot stand in for an
exe), drives the update through the popup, asserts the pre-armed attempt
survives the crash as rollback evidence, cold-starts into the rolled-back
base version (degraded steady state pinned: broken pointer stays active
with its attempt), then self-heals via a version-bumped healthy payload —
real recovery releases ship as version+1.
Spec hygiene: resetPackagedRuntimeState wipes launcher/runtime/daemon state
before each lifecycle test (uninstall keeps runtime data by design, and
leftover pointers silently delegate fresh installs onto stale payloads);
mac waitForHealthyDesktopVersion gains win's requireSettledLauncher opt-out
for rollback waits.
docs/testing/updater-lifecycle.md is the new lifecycle-to-test coverage map
(incl. deliberate manual-only nodes), linked from tools/pack/AGENTS.md and
the e2e coverage status doc.
* fix(updater): harden Windows full-lifecycle acceptance
* test(release): make channel-prepare hermetic against local git tags
prepare-preview derives its latest-stable floor from the clone's real
open-design-v* tags, so the channel-prepare tests were green on tagless CI
checkouts but permanently red on any developer clone once shipped stable
tags advanced past main's package version (0.15.1 vs v0.15.1/v0.16.0 today).
The spawned scripts now run with GIT_DIR pointing at a throwaway repo
holding exactly the tags each test declares, and a new negative case pins
the floor rejection itself (packaged version below a v99.0.0 stable tag).
* test(e2e): model delegated payload launch
Pass the updater's pre-armed payload pointer into the cross-runtime launcher simulation so it represents the spawned payload process instead of a later crash-recovery launch.
Generated-By: looper 0.11.0 (runner=fixer, agent=codex)
* test(web): wait for question form readiness
Wait for the asynchronously derived submit-disabled state before asserting it, removing the suite-order race observed by the Web workspace tests check.
Generated-By: looper 0.11.0 (runner=fixer, agent=codex)
* fix(updater): preserve recovery state across locks and restarts
Generated-By: looper 0.11.0 (runner=fixer, agent=codex)
---------
Co-authored-by: Looper <looper@noreply.github.com>
|
||
|
|
83f3d57081 | fix(web): remove preview run status overlay (#6027) | ||
|
|
506c2900b9 | fix(message-center): guard invalid published dates (#6007) | ||
|
|
ff445a7a43 | fix(web): remove duplicate preview run elapsed (#6005) | ||
|
|
99bc3d60d9 |
fix(web): dedupe project workspace tabs on repeated open (#5993)
* fix(web): dedupe project workspace tabs on repeated open The openWorkspaceTab event handler unconditionally appended a new tab on every project-open event, so reopening the same project stacked duplicate tabs instead of focusing the existing one. Mirror the dedup logic already in syncStateToRoute: when a project tab with the same projectId exists, update its conversationId/fileName/lastActiveAt and activate it rather than appending. Fixes #2641 * fix(web): coalesce duplicate project tabs on workspace restore normalizeTabsState deduplicated entry tabs and tab ids but not project tabs sharing a projectId, so workspaces persisted before the dedupe fix restored with their pre-existing duplicate project tabs still visible. Coalesce project tabs by projectId in normalizeTabsState (keep the active match, else the newest), mirroring the entry-tab dedupe. This repairs affected persisted state on restore, not just new corruption. Adds a restore regression with two distinct tab ids for the same project. Follow-up to #2641 / review feedback on #5993 |
||
|
|
034c3895d1 |
fix(web): hide empty tool_call/tool_call_update status rows (#4621)
buildBlocks() in AssistantMessage filters a fixed list of internal status labels before they reach StatusPill, but tool_call / tool_call_update are not in it, so they fall through and render as raw, expandable status pills with no tool name, input, output, or detail — looking like "a tool ran but produced no output". These are transient ACP markers: on the live SSE path the daemon already normalizes them to `running` (TRANSIENT_ACP_STATUS_LABELS in providers/daemon.ts), which buildBlocks() skips. The persisted-events path does not normalize, so the bare labels survive into the UI. Add both labels to the existing skip list, with a comment pointing at the daemon source of truth. Add a red-spec test asserting the two empty status rows render no pill, plus a guard that status rows carrying a real detail still render. |
||
|
|
adbb0ff9a4 |
fix(message-center): honor active AMR profile for anonymous sync (#5968)
* fix(message-center): honor active AMR profile for anonymous sync * fix(vela): keep control context on one config snapshot Generated-By: looper 0.11.0 (runner=fixer, agent=codex) |
||
|
|
2337932714 |
feat: expose GPT-5.5 Fast service tier (#4287)
* feat: support Codex service tiers * fix: forward service tiers through MCP runs * fix: clear default service tier selections * fix: preserve service tier on model option updates * fix: preserve service tier without explicit model * fix: preserve service tier in connection tests * test: stabilize API empty stream UI check * fix: preserve codex service tier labels * fix: preserve service tier without saved model * fix: address service tier review follow-ups * fix: pin legacy chat assistant rows before start * fix: accept bare model catalog responses |
||
|
|
50843ff149 |
give the unattributable client failures a real error code (#5960)
* give the unattributable client failures a real error code
Three buckets in the daily stability report name nothing, and in every case the
evidence was already in scope at the emission site:
- BYOK connection test: 801 failures across 201 devices in 24h — 10% of ALL
connection tests — reported `result.kind` verbatim, so every response the
daemon's status→kind map had no case for collapsed into `unknown`. The user
is told "connection failed" and we cannot say whether it was an out-of-credits
402, a self-signed cert, or a base URL pointing at an HTML login portal.
`ConnectionTestResponse` already carried `status` and a redacted `detail`;
only the emission site threw them away.
- Frontend exceptions: the Top-5 table literally renders a row as
`Error — 235 次 / ~74 台`. Two independent sources — `buildExceptionList`
copied `error.name`/`error.message` straight through and both can be '', and
`parseStack` returned a zero-frame stacktrace for a stackless error, which
PostHog drops at ingestion so neither `$exception_type` nor
`$exception_value` survives.
- Export failures (8.1%, 210/24h): everything that was not a sidecar-skew
message fell through to `err.name`, i.e. the literal "Error".
The daemon side of the same path flattened every sidecar contract violation to
`UPSTREAM_UNAVAILABLE`, which forced the web layer to regex-match English prose
to tell a version-skewed mesh from a dead renderer; it now forwards the
sidecar's real code, and the web classifier treats the remaining generic
envelope codes as non-classifications so a specific message still wins.
Two existing export-code expectations changed on purpose and say why inline: a
generic envelope `.code` no longer outranks message classification, and a
message carrying an HTTP status now reports that status instead of "Error".
* keep the forwarded sidecar code inside ApiErrorCode
Follow-up on the same PR. Forwarding the sidecar contract violation's `code`
compiled, but it was leaving the declared union: `ctx.http.sendApiError` is
typed `(...args: any[]) => any` in server-context.ts, so nothing caught that
`SIDECAR_UNKNOWN_MESSAGE` is not an `ApiErrorCode`. Every web-side handler
typed against that union would have failed to match the code the daemon
actually put on the wire.
Declare the two SIDECAR_* codes in API_ERROR_CODES and narrow the helper's
return type to ApiErrorCode, allowlisting exactly those two so a future sidecar
code cannot silently leave the union again.
No consumer of the export 502 switches on the old code — the UPSTREAM_UNAVAILABLE
call sites are finalize (useFinalizeProject), AMR run guidance, and ChatPane run
errors, none of which are on this route — so this is additive, not a behavior
change for anything that exists today.
* make the attribution actually fire on the production paths
Three review findings, all correct, all verified against the real code paths
rather than the synthetic ones the first cut tested.
1. The sidecar code passthrough was dead. `requestJsonIpc` rejects with
`new Error(response.error?.message)` — its response type does not even
declare a `code` — so SIDECAR_UNKNOWN_MESSAGE never reaches the route, and
the helper always fell back to UPSTREAM_UNAVAILABLE. Removed the passthrough
(and the two API_ERROR_CODES entries added for it: nothing emits them). The
message-based classifier already achieves the attribution and is tested;
dead code that reads as working is worse than no code. Threading the code
through IPC → API → web export result is a three-layer change and belongs in
its own PR.
2. The zero-frame guard checked the input instead of the output. A header-only
stack ("Error: Script error.") is truthy, so it skipped the fallback, and the
parser then dropped that single line as a message — yielding `frames: []`,
the exact ingestion failure this change exists to prevent. The guard now runs
on the PARSED result, covering missing / empty / header-only / unparseable
stacks, with a fixture matrix over all four.
3. The certificate bucket could not fire in production. undici reports a TLS
failure as a TypeError whose message is the generic `fetch failed`, with the
reason only on `cause.code`, and `networkErrorToKind` maps just an allowlist
— so a self-signed cert became `kind: unknown` + `detail: "fetch failed"`.
The unit test passed only because it was fed a detail string production never
emits. The daemon now appends the cause code to `detail`, and the client
tests assert against that real shape.
* stop a 2xx status from claiming a failure it cannot explain
Review catch, and it made this PR's headline case unreachable. The status
fallback ran before every detail classifier, so the daemon's
`{ kind: 'unknown', status: 200, detail: 'Unexpected token < …' }` — which is
exactly the 'base URL points at an HTML login portal' shape, returned when a 2xx
body fails JSON.parse (connectionTest.ts) — was reported as HTTP_200, and
INVALID_JSON_RESPONSE could never fire.
Reproduced before fixing:
byokErrorCode({kind:'unknown',status:200,detail:'Unexpected token < …'})
→ 'HTTP_200'
The cut is now at 300 rather than 0. A 2xx that still failed did so for a reason
the status cannot express, so it falls through to the detail classifiers; 3xx
stays on the status side because the daemon fetches with `redirect: 'error'`,
making a redirect a real failure the status does name.
* read the daemon-appended cause code instead of enumerating codes
Review catch on a gap my own previous commit opened: forwarding cause.code from
the daemon put OpenSSL's TLS codes on the wire, and this file could not read
them. UNABLE_TO_GET_ISSUER_CERT_LOCALLY, CERT_NOT_YET_VALID and CERT_HAS_EXPIRED
have no Node-errno shape, and the `\bcertificate\b` fallback never matches a
name that spells it CERT — so the signal was preserved on the wire and dropped
here, landing back in UNKNOWN_NO_SIGNAL.
Read the daemon's known trailing `(CODE)` position directly rather than
extending an allowlist: an enumeration would silently drop every code added
upstream after this file was written, which is the same failure mode one layer
down. Inline codes keep a widened pattern, and the TLS fallback now also matches
CERT-shaped names.
Fixtures cover all seven codes the daemon can now forward.
|
||
|
|
6302432571 |
fix(i18n): localize BYOK provider titles to English (#5826)
* fix(i18n): localize BYOK provider titles to English
Five BYOK provider titles in apps/web/src/state/config.ts were hardcoded in
Chinese while the rest of the provider list used English-facing names:
qwen 千问 -> Qwen
volcengine 火山引擎 -> Volcengine Ark
qianfan 百度千帆 -> Baidu Qianfan
mimo 小米 MiMo -> Xiaomi MiMo
zhipu 智谱 -> Zhipu AI
These were rendered into the Settings > Execution mode > BYOK provider chip
list verbatim, producing a mixed-language list when the UI was set to
English (issue #5693). The providerLabel for each was already in English;
only the user-facing title was inconsistent.
The new titles mirror the providerLabel phrasing where the existing
providerLabel read naturally as a standalone name (Qwen, Volcengine Ark,
Baidu Qianfan, Zhipu AI) and use a friendly "Xiaomi MiMo" for mimo where
the providerLabel carried extra "OpenAI" suffix not appropriate for a chip
title.
No i18n catalog entries needed — these were never translated, the title
field is the display string.
Fixes #5693
* fix tests: update BYOK provider tab labels to English
Two test assertions in apps/web/tests/components/SettingsDialog.execution.test.tsx
hardcoded the old Chinese tab names that were just localized to English in
the BYOK_PROVIDER_PRESET_SPECS array (commit
|
||
|
|
3a5c52931c |
fix(web): lift home context picker secondary text to a legible token (#4468) (#4851)
* fix(web): lift home context picker secondary text to a legible token (#4468) The home context picker (the @-mention surface above the prompt) drew its secondary text — per-tab counts, section labels, option meta, and the hover-card kicker — in `--text-faint`. In dark mode that token (#4e4b46) sits at ~1.8:1 against the picker panel, far below WCAG AA, so the counts and category labels read as illegible bleed-through. Move those four selectors to `--text-muted`, matching the option description that already uses it. The picker's secondary tier now clears AA in dark mode (~5.3:1) and stays consistent across the tabs and list; light mode improves from ~2:1 toward AA on the same selectors. Adds a stylesheet-level contrast guard that resolves each selector's color token and asserts WCAG AA against the surface it renders on. * fix(web): extend picker contrast lift to the hover-card meta row (#4468) |
||
|
|
d5b6cc8e34 |
fix(web): localize message center dates (#5954)
* fix(web): localize message center dates * test(web): avoid Intl constructor spying in message center locale test Generated-By: looper 0.11.0 (runner=fixer, agent=codex) |
||
|
|
2d1f25ac60 |
feat(web): add message center (#5920)
* feat(web): add message center demo * fix(web): align message center actions * fix(web): remove message read toggles * fix(web): show unread count on message filter * feat(web): connect message center to vela (#5777) * feat(web): connect message center to vela * fix(web): harden message center read syncing Generated-By: looper 0.10.6 (runner=fixer, agent=codex) * fix(web): preserve cached message center reads Generated-By: looper 0.10.6 (runner=fixer, agent=codex) * fix(web): repair message center read sync and visual selector Generated-By: looper 0.10.6 (runner=fixer, agent=codex) * fix(web): refresh message center write auth and bound pagination Generated-By: looper 0.10.6 (runner=fixer, agent=codex) * fix(web): preserve message center state across locale and logout Generated-By: looper 0.10.6 (runner=fixer, agent=codex) * fix(message-center): stabilize login and anonymous delivery (#5910) * journal: stop repeated amr login polling * fix(web): drop anonymous message-center window gate Align with Vela audience+TTL visibility by stopping startedAt local windows so anonymous clients can pull all non-expired global messages. * refactor(web): promote message center from demo * fix(daemon): guard vela message center proxy stream Generated-By: looper 0.11.0 (runner=fixer, agent=codex) * fix(cli): add message center command Generated-By: looper 0.11.0 (runner=fixer, agent=codex) * test(e2e): harden visual workspace file readiness Generated-By: looper 0.11.0 (runner=fixer, agent=codex) * fix(web): separate message center sync empty states Generated-By: looper 0.11.0 (runner=fixer, agent=codex) --------- Co-authored-by: bone3deep1962-collab <bone3deep1962@gmail.com> |
||
|
|
421ac5ad56 |
fix(web): keep model picker anchored within visible bounds (#5907)
Co-authored-by: Cheems <94773058+itscheems@users.noreply.github.com> |
||
|
|
d8b6b797fe |
Simplify chat execution disclosures (#5667)
* Simplify chat execution disclosures * test: cover collapsed file disclosures * refactor(web): consolidate chat task activity * fix(web): show small file outputs inline * fix(web): separate file outputs from reads * fix(web): collapse completed todo cards * refactor(web): compact completed todo lists * fix(web): make completed todos expandable * fix(web): keep four output files visible * refactor(web): clarify chat activity timeline * fix(web): make file output disclosure interactive * feat(web): emphasize chat run states * style(web): simplify chat run status colors * style(web): remove chat run status icons * feat(web): stream one chat activity at a time * style(web): align chat completion row * fix(web): simplify priority chat states * feat(web): simplify secondary chat details * style(web): simplify secondary chat hierarchy * fix(web): keep execution state out of artifacts * fix(web): use icons for artifact operations * style(web): frame todo disclosure header * fix(web): clarify artifact and tool semantics * fix(web): clarify chat disclosure hierarchy * fix(web): keep execution history neutral * fix(web): simplify user action cards * fix(web): align failure card actions * fix(web): refine chat action hierarchy * fix(web): address chat disclosure review feedback * fix(web): restore chat recovery actions * fix(web): make collapsed action details inert --------- Co-authored-by: xiaoche-hub <298951296+xiaoche-hub@users.noreply.github.com> |
||
|
|
c9e622d0a9 |
fix(daemon): ship baseline OpenCode for Windows and stop retrying AVX2 illegal-instruction crashes (#5733)
* Classify AVX2 illegal-instruction agent crashes as cpu_unsupported and stop retrying them The Windows opencode.exe bundled with the AMR runtime is a Bun build that requires AVX2. On CPUs without it (Intel Atom/Celeron/Pentium N-series through 2021) the binary dies with STATUS_ILLEGAL_INSTRUCTION before readiness; vela surfaces an ACP fatal, the daemon classified it as a retryable fatal_rpc_error, and the automatic retry deterministically failed again. 88 of 105 pre-readiness Windows crash traces in Langfuse are this single failure. The crash text (Bun 'Illegal instruction' banner with a no_avx2 CPU feature line, or the raw 0xC000001D / decimal 3221225501 exit status) now classifies as process_exit/cpu_unsupported ahead of the fatal_rpc_error close-reason promotion, is never auto-retried, and renders a dedicated error card telling the user to update Open Design instead of a dead Retry button. * Bump @powerformer/vela-cli to 0.0.23 so packaged Windows builds bundle the baseline (non-AVX2) OpenCode The 0.0.23 win32-x64 platform package compiles the OpenCode fork with --target=windows-x64-baseline, so the bundled opencode.exe no longer requires AVX2 and starts on Intel Atom/Celeron/Pentium N-series CPUs. Verified in vela CI: the build log shows the baseline target and the new post-publish smoke job boots the published bundle's opencode.exe on a real Windows runner (powerformer/vela#838). * Refresh Nix pnpm deps hashes for the vela-cli 0.0.23 lockfile change * Add the abort-after-panic (exit status 3) production shape to cpu_unsupported specs A user on an AVX-but-no-AVX2 CPU (Sandy/Ivy Bridge era) hit the same illegal-instruction crash, but Bun panicked during its own panic handler and abort()ed, so vela reported exit status 3 instead of STATUS_ILLEGAL_INSTRUCTION. The stderr banner (no_avx2 / Illegal instruction) is the only signal; encode the real Langfuse trace shape so the classifier keeps catching it. * Bump @powerformer/vela-cli to 0.0.25 so packaged Windows builds bundle the baseline (non-AVX2) OpenCode 0.0.25 is the first stable cut after powerformer/vela#838 merged: its win32-x64 package compiles the OpenCode fork with --target=windows-x64-baseline (verified in the release CI log), the post-publish smoke boots the published bundle on a real Windows runner, and the lockfile regenerator now pins the baseline asset so a routine version bump cannot silently flip back. 0.0.24 predated that merge and still shipped the AVX2 build. * Tighten cpu_unsupported to precise signals only (no bare 'Illegal instruction') Review is right that a bare illegal-instruction line is too broad: an unrelated SIGILL on an AVX2-capable machine (runtime bug, corrupted jump) would be mislabeled 'Processor not supported' and lose its retry. Bun's crash banner always prints the CPU-feature line, so machines actually missing AVX2 are identified by no_avx2; the bare Windows STATUS_ILLEGAL_INSTRUCTION renderings (0xC000001D / 3221225501) stay because those runs carry no banner at all. Generic illegal-instruction banners without no_avx2 return to process_crashed / fatal_rpc_error, locked by a new negative spec. * Refresh Nix pnpm deps hashes for the vela-cli 0.0.25 lockfile change * Gate raw STATUS_ILLEGAL_INSTRUCTION on the bundled-opencode startup context |
||
|
|
4fb217c957 | fix(web): keep loaded config when re-persisting a downgraded protocol fails (#5506) |