mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
dba64f2e455bbd8fa819932182cc07581d8d3646
450
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
698a17aac2 |
feat(web): refresh home and preview workspace controls (#6692)
* feat(web): home type-fan picker, floating account cluster, docked tab dropdown Home hero: the 12 create-scenario types render as a fanned card carousel above the composer (flat elliptical arc, apex card = selected, arrows / card click switch along the arc; selected card scales about its own center on an inner body so the outer transform stays a pure arc move). Entry chrome: the account module leaves the nav-rail bottom and rides a fixed top-right cluster next to the DeepSeek campaign badge — credits as its own pill, avatar-only circular trigger, hover menu opens downward (right-aligned, width-capped). The cluster is app-region no-drag so the menu stays reachable in the desktop shell. Project route: the docked workspace tab strip becomes one white dropdown spanning the chat column (MRU order, most recent first), and the chat collapse control lifts out of the chat card header to sit left of it. Design files: the category tab bar drops its near-opaque sticky backdrop strip and scrolls with content. * feat(web): polish top-right capsule and dock controls, seed default deck chip - Home composer seeds 幻灯片 (deck) as the default creation type on a fresh mount — same silent deferred-apply path as a user pick, skipped whenever a persisted chip draft owns the composer. - Type fan: tighter card spacing (arc pivot 3700px → 3000px, ~128px between neighbours) so the hand-of-cards overlap reads again. - Top-right chrome: credits + avatar merge into ONE capsule (pill material on the shared wrapper, chrome-free 24px segments inside, no hover fill on the credits segment). - Project dock: the chat-collapse control gains a white seat matching the tab dropdown, and the dropdown drops its border (shadow only). * test(web): align refreshed home chrome acceptance coverage - Read account badges and member labels from the floating account menu. - Match the default deck route, non-sticky Design Files tabs, and lifted chat control. - Navigate projects through the visible docked Home control instead of hidden state nodes. - Cover the affected Web suite and focused UI P0 restoration flows. * fix(web): lock submit until default routing is ready - Keep fresh-home sends disabled while the plugin catalog and default deck binding settle. - Let explicit plugin handoffs supersede the default without leaving Send locked. - Add a red-spec that holds the catalog response and verifies the routing barrier. * test(e2e): await seeded type before picker actions - Wait for the fresh-home deck binding before opening the creation-type menu. - Switch to Prototype instead of clicking the already-active deck row. - Preserve clear-selection coverage without racing menu reconciliation. * fix(web): keep type menu open during internal scroll - Listen for scroll only on the trigger ancestor chain and the viewport. - Let the portaled creation-type list scroll without dismissing itself. - Add coverage that distinguishes internal menu scroll from anchor-moving scroll. * test(web): await routed submit readiness in legacy flows - Wait for the Home submit control to become enabled before exercising the AMR workspace race path. - Wait for refreshed workspace metadata and default routing to settle before submitting a community template. - Keep the assertions aligned with the production guard that blocks submits until a deterministic default route exists. * feat(web): replace the home type fan with a capsule pill row The 12 create-scenario types render as ONE line of capsule pills exactly as wide as the composer below: pills that fit render inline (8px gaps throughout), 视频 stays pinned before the 全部 trigger, and the rest fold into the 全部 popover. Fit is measured against an invisible probe row so visibility never feeds back into its own measurement. 幻灯片 leads the order (default-selected type); 网站复刻 trails so it folds first. The fanned card carousel this replaces is fully removed (component + styles) — selection still flows through the same onPick scenario path. * feat(web): drop the clear-type control, cap the type menu at 6 rows Clearing the creation type is gone (per product): the composer pill loses its inline x (the chevron now stays in every state) and the menu loses its leading Clear row, so a type is only ever swapped for another. TemplatePicker no longer takes onClear. The type menu shows exactly 6 rows and scrolls for the rest (6 x 44px row + 5 x 2px gap + 12px padding). Specs that drove the removed control are rewritten around swapping instead: the e2e clearHomeTemplate helper is deleted, pickHomeTemplate now observes the pill's has-selection state, and the three clear-specific specs assert the affordance is absent. * feat(web): lift the GitHub star badge into the top-right cluster The star count leaves the account menu's social row and becomes its own chip between the campaign badge and the account capsule, sharing their pill material. The menu's social row keeps Discord / X / mail. * chore(web): clean up stacked EOF whitespace - Remove the trailing blank line introduced in the home hero stylesheet. - Keep the shared Playwright home helper free of the same EOF artifact. * fix(web): polish preview workspace controls * fix(web): stabilize floating comment interactions - Treat a configured comment portal as an overlay from the first render and pre-mount its empty host, preventing the preview from briefly entering the 320px local side-dock layout before the floating card appears. - Route the floating card's close button and Escape key through one cleanup path, restore focus to the originating toolbar trigger, and clear board, create, and composer state so the card reopens with one click. - Add red-spec coverage for the transient side-dock class, single-action close/reopen behavior, and focus restoration; the tests fail on the PR base and pass with this fix. - Align the signed-out balance action with pill-radius controls, remove the redundant portal label, delete obsolete radial/reset/search/card picker CSS, and clean the two stacked EOF whitespace issues. - Preserve the intended steady-state overlay behavior and validate the change with 389 focused tests, workspace typecheck, guard, diff checks, and Electron frame/focus sampling. * test(e2e): align shared project title assertion with focus mode - Wait for the file workspace to become visible after first-open materialization. - Assert the canonical project-title state without requiring the intentionally hidden focus-mode dock to be visible. - Preserve stale-title rejection and the visible catalog-title check after returning to All Projects. - Validate with three focused repetitions, guard, workspace typecheck, and the entry-settings CI lane. * fix(web): stabilize responsive home controls - Recompute the Home type-pill split when localized labels or intrinsic pill widths change without a container resize. - Observe the measured pill children and tail so late font layout cannot leave the overflow decision stale. - Restore composer errors to the reviewed in-flow presentation instead of carrying the unrelated fixed top-chrome toast. - Add focused coverage for locale-style rerenders and post-render width settling. * test(e2e): await default home routing in attachment flow - Wait for the visible deck binding before checking the Home attachment submit lifecycle. - Use the shared long timeout so grouped CI does not race catalogue readiness. - Preserve the production submit gate and the attachment add/remove assertions. * feat(web): add a view-all-comments entry to the comment popover * feat(web): cap the docked tab dropdown at six scrollable rows * fix(web): preserve comment and tab menu interaction semantics - Return focus to the still-open View all comments control when the floating comment panel closes. - Include border-box borders in the six-row tab menu height so the last row is not clipped. - Add focused regression coverage for comment focus restoration and menu sizing. * test(web): await default routing in AMR home flows - Wait for the cold Home composer to finish binding its default deck route before clicking Send. - Preserve the existing AMR balance retry and auth-expiry assertions after the new readiness gate. * fix(web): preserve explicit Home submit routes * fix(web): defer restored Home plugin apply * fix(web): close workspace chrome review gaps - Clamp the Home template menu to six rows and the visible viewport, flipping it above the trigger when needed. - Reset docked tab-menu state when its route-owned portal disappears and preserve editor-owned keyboard shortcuts. - Scope the project header account identity, balance, and billing destination to the project workspace while retaining the ambient fallback for unbound local projects. - Cover tall and short viewports, dock remounts, composer focus, and ambient/project workspace divergence. --------- Co-authored-by: Cheems <94773058+itscheems@users.noreply.github.com> Co-authored-by: Amy <1184569493@qq.com> |
||
|
|
4908baa4f4 |
feat(design-systems): ship structured runtime workflow (#6805)
* feat(design-systems): add structured runtime contract (#6456) * feat(design-systems): add structured runtime contract * chore: retrigger CI for PR 6456 --------- Co-authored-by: xiaoche-hub <298951296+xiaoche-hub@users.noreply.github.com> Co-authored-by: mrcfps <mrc@powerformer.com> * feat(design-systems): resolve structured runtime intents * fix(design-systems): preserve workspace runtime scope * test(design-systems): cover scoped prompt runtime * fix(design-systems): use one component authority * fix(design-systems): preserve run workspace authority * fix(design-systems): honor runtime rollback gate * fix(design-systems): pin run binding authority * fix(runs): reject client-supplied scopes * fix(design-systems): keep user package assets scoped * feat(design-systems): validate generated UI adherence * test(e2e): assert durable reload authority state * fix(design-systems): reject non-executable adherence evidence * fix: harden design system adherence validation * fix(design-systems): validate JSX selectors structurally * fix(design-systems): close adherence input gaps * chore(nix): refresh pnpm dependency hashes * chore(ci): retrigger design system validation * fix(daemon): bound design system artifact reads * fix(design-systems): bridge Codex runtime tools * docs(codex): clarify shell environment policy --------- Co-authored-by: open-design-crew[bot] <299007234+open-design-crew[bot]@users.noreply.github.com> Co-authored-by: xiaoche-hub <298951296+xiaoche-hub@users.noreply.github.com> Co-authored-by: mrcfps <mrc@powerformer.com> |
||
|
|
e654592ba6 |
fix(daemon): point users at the Settings section that actually exists (#6831)
* fix(daemon): point users at the Settings section that actually exists
When image generation failed for a missing credential, the reply told
the user to configure it in "Settings → Media". No such nav item exists —
the section is "Media providers" ("媒体生成提供商" in zh-CN) — so users
following the instruction found nothing.
The cause is that Settings destinations were spelled independently
wherever a producer happened to need one: 29 provider errors in
apps/daemon/src/media/index.ts said a bare unnamed "Settings" (one said
"Settings -> Media Providers"), and the od-media-generation skill said
"Settings → Media". None of them were tied to the Settings dialog, so a
rename could not reach them.
packages/contracts/src/settings-nav.ts is now the single place a Settings
destination is spelled, and every producer reads it: the media provider
errors, the ElevenLabs voice-options prompt (daemon + contracts), and the
external-MCP auth guidance. e2e/tests/settings-nav-copy.test.ts holds the
constants against the labels apps/web actually renders and fails the
build if a producer re-inlines its own guess.
Swept the same drift class while here: both hatch-pet SKILL.md copies
pointed at "Settings → Pets", but #5517 folded the pet picker into
General, so the real path is "Settings → General → Pets".
Verified the gate red on the pre-fix tree (3 failing assertions) and
green after. Adjacent, not fixed: these strings are English and the
agent translates them ad hoc, so a localized client can still render a
section name that does not match its own UI — see the report.
Record: recvre8FrTE2Oa (V0.19.1 acceptance)
* fix(contracts): gate destinations on rendered nav, not the i18n dictionary
Review on #6831 caught that SETTINGS_EXTERNAL_MCP named a place users
cannot navigate to. `mcpClient` kept its Settings render block but lost
its sidebar nav item (SettingsDialog.tsx says so in its own comment), and
reconnecting an external MCP server now lives in the top-level
Integrations view. So the destination is not a Settings section at all.
The stale string predates this branch — it was hardcoded in
prompts/system.ts on main. But promoting it into a named constant made it
worse twice over: the docblock asserted "The Settings nav item where
external MCP servers are added/reconnected" in a canonical location, and
the new gate whitelisted it by construction, so the check built to catch
dead paths became the thing defending one.
The deeper defect was the gate's invariant. Comparing a constant to its
i18n key's value proves only that some dictionary entry matches — not
that anything renders it. `settings.externalMcpTitle` still resolves to
"External MCP" with no nav item behind it, which is exactly how the dead
path passed. The same hole covered SETTINGS_MEDIA_PROVIDERS.
Now the gate parses what the UI actually renders — `settings-nav-item`
buttons and INTEGRATION_TABS — and resolves those keys through the
dictionary, using it as a lookup rather than as the source of truth.
Verified by reintroducing "Settings → External MCP": the old gate passed
it, the new one fails with the rendered destinations listed.
Replaces SETTINGS_EXTERNAL_MCP with INTEGRATIONS_MCP_PATH
(`Integrations → MCP`) and adds a regression test pinning that a
dictionary-only label is rejected.
|
||
|
|
9120f3dab1 |
fix(web): name the reason when an invite target is already a member (#6830)
Inviting an address that is already an ACTIVE member of the workspace
showed the generic "invitation failed, try again later" copy, so the user
had no way to learn that retrying could never succeed.
The dialog already branches on B's allowlisted invite-create codes; the
gap was in the allowlist itself. B rejects that case with HTTP 409
`{"error":"invite_existing_member"}`, which was absent from
WORKSPACE_INVITE_CREATE_ERROR_ALIASES, so it degraded to the
transport-shaped `create_409` and fell through to the retry-later
fallback. Add the alias, and split the two 409 duplicate variants apart:
"already a team member" and "already has a pending invitation" call for
different next steps and no longer share one merged sentence.
The code -> copy mapping moves out of the component into
`collab/invite-error-copy.ts` as a pure code -> i18n-key function, so a
new B code has exactly one place to be taught and the mapping is
testable on its own.
Verified red on the alias-less baseline (4 failures across both specs)
and green after.
Record: recvrovm9Bcyy0 (V0.19.1 acceptance)
|
||
|
|
b55d443b44 |
fix(web): streamline Home submit preflight (#6756)
* fix(web): retry transient home balance preflight * refactor(web): streamline home submit preflight * fix(web): keep local submit independent from cloud identity * fix(web): preserve local and legacy submit lanes * fix(web): keep in-memory workspace witness valid * fix(web): retain workspace preflight compatibility * fix(web): require workspace witness for team plugin apply * fix(plugins): apply exact local plugin source * fix(web): invalidate legacy witness on identity change * refactor: keep Home submit local-first * test: align local-first creation coverage * fix: preserve exact local plugin context * fix: fence exact local plugin use * fix: disable inexact legacy plugin fallback * test: mock exact plugin apply in visuals * fix: reject unsupported exact project source * Revert "fix: reject unsupported exact project source" This reverts commit |
||
|
|
85d2e4893c |
fix(amr): recover expired cloud sessions (#6786)
* fix(amr): recover expired cloud sessions * fix(diagnostics): redact dynamic API routes * fix(daemon): honor settings in AMR diagnostics * fix(daemon): align workspace auth revisions * fix(web): gate expired cloud sessions on sign-in * fix(web): scope cloud sign-in gate to cloud execution * test(web): align cloud auth fixture * fix(web): recover transient run authority failures * test(daemon): expect retryable authority detail * test(e2e): align BYOK auth boundary * test(e2e): align expired cloud recovery flows |
||
|
|
e78bb22613 |
fix(daemon): inline shared pipeline atom bodies once across active-stage blocks (#6245)
The system-prompt composer rendered each pipeline stage's atoms independently, so an atom declared by multiple stages (od-default declares discovery-question-form in both task-type and discovery) had its full SKILL.md body inlined once per stage — duplicating ~70 lines in every default free-form Home run's system prompt. Add renderActiveStageBlocks to @open-design/contracts: it renders all stage blocks while inlining each atom body exactly once; a later stage that re-declares an atom keeps its subsection header but carries a one-line back-reference to the stage that inlined the body. Stage headers stay per spec §23.4. The server's activeStageBlocks build now goes through this builder, so any plugin sharing atoms across stages is handled at composition time. Fixes #6238 Co-authored-by: John Doe <johndoe@example.com> |
||
|
|
abbc7cbe3b |
feat(analytics): report the Share tab publish flow (publish_file / artifact_publish_result / copy_publish_link) (#6651)
* feat(analytics): report the Share tab publish flow
The 0.18.0 unified Share panel made 'Publish this file for everyone' the
primary sharing path, but the whole chain was untracked: no click event,
no outcome event, no copy-link event. This wires the three points:
- ui_click element 'publish_file' when the publish button is clicked
- artifact_publish_result (new event, mirroring the artifact_deploy_result
split) when a publish/unpublish attempt settles, with action/result/
error_code/publish_duration_ms
- ui_click element 'copy_publish_link' on the post-publish copy button
Both viewer copies (ReactComponentViewer, HtmlViewer) report the same
shape; ReactComponentViewer has no projectKind prop so it reports
project_kind null. Failure codes are stable analytics values
(workspace_identity_required | publish_failed), not i18n keys.
* fix(analytics): gate HtmlViewer publish tracking on workspaceActive and close the error_code type
- Wrap the new publish/copy-link/result emissions in HtmlViewer behind
fire*-style helpers that early-return on !workspaceActive, matching every
neighboring emission site; the publish/unpublish network calls stay
unconditional. ReactComponentViewer has no workspaceActive prop and is
unchanged.
- Declare TrackingPublishErrorCode as a closed two-value union in
contracts shared-enums, use it for ArtifactPublishResultProps.error_code,
and import it from publish-error-code.ts so the helper and contract
cannot drift.
- Extend the publish-tracking suite with unpublish success/failed cases and
a ReactComponentViewer case asserting project_kind: null.
Generated-By: looper 0.11.8 (runner=fixer, agent=claude-code)
* fix(analytics): report the real project kind from ReactComponentViewer publish events
ReactComponentViewer's publishTrackingIdentity() hard-coded
project_kind: null, so every publish_file, copy_publish_link, and
artifact_publish_result emitted for a React artifact lost project-kind
attribution — even though FileViewer requires Props.projectKind and had
the value in scope at the render site.
Thread projectKind into ReactComponentViewer and return it from
publishTrackingIdentity(), matching the HtmlViewer copy so both viewers
really do report the same shape.
The React-path test now asserts project_kind 'prototype' on both the
publish click and the publish result, and additionally covers the
copy_publish_link click that previously had no React-path assertion.
Generated-By: looper 0.11.8 (runner=fixer, agent=claude-code)
* fix(analytics): read the live workspaceActive ref when a publish settles
firePublishResult closed over the workspaceActive prop captured by the
render that created the click handler. A publish/unpublish request can
start while the HtmlViewer is active and settle after the user switches
tabs; the in-flight continuation still saw the render-time `true`, so a
retained (inert) viewer emitted artifact_publish_result anyway — exactly
what the gate added in
|
||
|
|
15c48e7001 |
fix(daemon): make run telemetry attempt-aware (#6598)
* fix(daemon): make run telemetry attempt-aware * fix(daemon): preserve terminal causes across restart Generated-By: looper 0.11.8 (runner=fixer, agent=codex) |
||
|
|
0460157e1e |
feat(analytics): observe conversation fork outcomes (#6707)
* feat(analytics): observe conversation fork outcomes * docs(daemon): note native session fork follow-up |
||
|
|
cf2cc74818 |
feat(media): route Open Design Cloud generation through Vela (#6500)
* feat(media): route Open Design Cloud generation through Vela Add Vela image and video adapters behind od media generate with trusted workspace attribution, multi-image support, video polling, and fail-closed behavior. Expose AMR-specific media defaults and pin the packaged Vela CLI used by the integration. * fix(nix): refresh pnpm dependency hashes Refresh the daemon and web fixed-output hashes after the bundled Vela CLI lockfile update. The values come from the failed Nix CI derivations. * chore(pack): bundle Vela CLI 0.0.31-test.0 0.0.29-test.0 is the build QA hit the 413 with: an agent that reads a generated image turns it into base64 for the next model request, and a 1.7 MB PNG became 2.13 MiB of attachment that the ingress in front of Link rejected. 0.0.31-test.0 configures OpenCode to downscale read images to 512px and cap them at 1 MiB of base64. Verified against this build: the same 1,678,735-byte PNG now reaches the model as a 512x288 attachment, 326,762 bytes of base64 -- 6.85x smaller -- and the model still describes the image correctly from it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VDg4zffeaNboRdhAh17evB * chore(pack): bundle Vela CLI 0.0.31-test.1 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VDg4zffeaNboRdhAh17evB * feat(media): request published aspect ratios from Vela The Vela image adapter refused every aspect but 1:1 and then sent no size at all, so the one shape it did accept was still whatever the provider defaulted to -- the guard proved nothing about the output. Vela now publishes (aspect_ratio, resolution) profiles per model and per request kind, and all five image models carry 1:1, 16:9, 9:16, 4:3, and 3:4. Resolve the pair from `vela media models --json` and pass --aspect-ratio and --resolution, which the CLI requires together. When one aspect ratio publishes several resolutions, prefer the model's default tier: asking for a shape should not quietly change quality or price. When the requested ratio is not published, fail with the ratios that are, before spawning a render. The lookup runs per request rather than from a cache. Vela owns which shapes a model can deliver, so a catalogue that gains or loses one has to take effect immediately, and one CLI call is nothing beside the generation behind it. The prompt contract already advertised these five ratios, so this closes the gap where the prompt promised what the adapter rejected. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VDg4zffeaNboRdhAh17evB * fix(media): harden multimodal generation and delivery (#6607) * fix(media): harden multimodal generation and delivery Use daemon-authored artifact paths for created and modified outputs. Preserve media references through CLI staging and make Cloud model routing deterministic. Recover one transient post-tool continuation, surface useful generation progress, and pin the verified Vela CLI package. * fix(web): make produced rows keyboard accessible Expose the row-level open action to keyboard and assistive technology users while preserving the nested Open and Download controls. Cover Enter and Space activation in the component regression test. * build(nix): refresh pnpm dependency hashes Update the daemon and web fixed-output hashes after the bundled Vela CLI lockfile change. --------- Co-authored-by: Cheems <94773058+itscheems@users.noreply.github.com> * fix(media): keep image errors user-friendly and diagnosable * feat(media): forward quality and resolution to Vela images `od media generate` had no way to name an image quality tier or output resolution, so neither reached the Vela CLI. A user asking for "high quality, 1K" got whatever the model's default profile produced, with the request surviving only as prose inside the prompt. Thread both through the existing layers -- CLI flag whitelist, the generate route, generateMedia, MediaContext -- and append them to the `vela image gen|edit` argv. Unspecified stays unspecified. Tiers are priced more than an order of magnitude apart, so substituting a tier for a caller who named none would silently re-price every unqualified request; omitting `--quality` is what leaves the model's own default in charge. Resolution keeps its existing default-profile selection, because Vela's CLI refuses a resolution without an aspect ratio and an aspect ratio is always sent. Both values are validated against `vela media models` before spawning: quality is published per model (only gpt-image-2 is tiered today) and resolution must be published for the requested aspect. Failing here costs nothing, versus a round trip that ends in a provider rejection. Bump the bundled Vela CLI to 0.0.32, the first release whose `image gen`/`image edit` publish `--quality`. * feat(media): teach the agent to map a named size or tier onto the flags The forwarding change added `--quality` / `--resolution` and told the agent when NOT to send them, but never said what counts as the user asking. A request for "2k" came back as `16:9 2K · model default quality`: the size survived only as prose inside the prompt, where the provider never sees it. Name the mapping in both prompt copies, in one sentence each. The golden system-prompt matrix tracks total prompt length, so its two character counts move with the added text. * fix(media): match published Vela resolutions and tiers case-insensitively A real run asked for `--resolution 2k`. Vela publishes `2K`, the lookup compared the two exactly, and the request failed before reaching the provider; the agent read the supported value out of the error and retried ten seconds later with the shifted letter. Fold case on both sides when matching, and put the catalogue's own spelling on the wire rather than the caller's. No published value differs from another by case alone, so nothing becomes ambiguous, and a request that names a shape or tier the model genuinely lacks still fails the same way. --------- Co-authored-by: Cheems <94773058+itscheems@users.noreply.github.com> Co-authored-by: a1chzt <chizblank@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Co-authored-by: open-design-crew[bot] <299007234+open-design-crew[bot]@users.noreply.github.com> |
||
|
|
1ca8620e9f |
feat(web): report preview iframe failures to PostHog (#6671)
* feat(web): report preview iframe failures * fix(web): preserve preview error stacks * fix(preview): scope and normalize observability bridge * fix(preview): ignore retained iframe diagnostics * fix(preview): consume early diagnostics once |
||
|
|
16a83e19d3 |
fix(web): improve Workspace failure diagnostics (#6666)
* fix(web): improve workspace failure diagnostics * fix(web): address workspace diagnostics review * fix(web): bound workspace analytics error codes |
||
|
|
2fb33fb282 |
fix(web): make project conversation forks reliable (#6673)
* fix(web): make conversation forks reliable * fix: preserve fallback fork boundary |
||
|
|
d1f5e1f2fb |
chore(release): bump main to 0.18.2 ahead of stable 0.18.1 (#6588)
Co-authored-by: open-design-release-bot[bot] <open-design-release-bot[bot]@users.noreply.github.com> |
||
|
|
34192c6719 |
fix(diagnostics): keep the previous session's daemon log and cut event tails on line boundaries (#6531)
* fix(diagnostics): keep pre-restart daemon log and line-aligned event tails Two gaps made incident support bundles useless after a relaunch: 1. Every packaged launch opened each sidecar's latest.log with mode "w", destroying the previous session's log — the bundle held only the ~70 lines written since the restart while the incident-time daemon log was gone. openLog now rotates the prior file aside as previous.log (exactly one prior session, rename overwrites), and the diagnostics export bundles logs/<app>/previous.log whenever it exists, under the same per-file tail cap. The source entry is existence-conditional so non-rotating launchers (tools-dev appends) do not gain missing-file placeholder noise. 2. The 2MB tail cap on runs/*/events.jsonl cut bytes from mid-file, so the first kept line was half a JSON object and jsonl consumers fell over on line 1. readMaybeTail now advances the cut to the next line boundary (with a guard byte so an already-aligned cut loses nothing); a window with no newline keeps the raw cut. * fix(packaged): keep the prior log when rotation fails instead of truncating it A non-ENOENT rename failure (Windows share-lock on previous.log, read-only or exotic filesystem) still fell through to open(path, "w"), destroying the only copy of the pre-restart log while previous.log stayed unavailable — the exact data loss this rotation exists to prevent. Truncate only when the prior bytes are provably elsewhere: moved aside, or never written (first-launch ENOENT). Otherwise open in append mode, since a merged two-session log is recoverable and an erased one is not. * fix(diagnostics): never export a partial record, and never hide an unreadable log Two holes in the truncation/collection contract: - A single event larger than the whole tail budget left no complete line to align to, and the fallback kept the raw cut — so the export shipped the interior of one JSON record and any consumer streaming the bundle through a jsonl parser died on line 1, exactly what alignment exists to prevent. Signal "no complete line" instead and let the collector record it, so the bundle carries a self-describing placeholder rather than half an object. - The optional-source gate treated EVERY access failure as absence, so a previous.log that exists but is unreachable (EACCES on the log directory, EIO) vanished with no manifest entry and no warning. Only ENOENT may drop the entry now; anything else stays listed and reports the real error. |
||
|
|
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 |
||
|
|
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> |
||
|
|
99fc8a45bc |
Revert "feat(design-systems): add structured runtime contract (#6456)" (#6520)
This reverts commit
|
||
|
|
2b8aa8c457 |
feat(design-systems): add structured runtime contract (#6456)
* feat(design-systems): add structured runtime contract * chore: retrigger CI for PR 6456 --------- Co-authored-by: xiaoche-hub <298951296+xiaoche-hub@users.noreply.github.com> Co-authored-by: mrcfps <mrc@powerformer.com> |
||
|
|
d771c1ca5f |
feat(daemon): export execution diagnostics (#6433)
* feat(daemon): export execution diagnostics * fix(daemon): preserve observed execution diagnostics --------- Co-authored-by: bone3deep1962-collab <bone3deep1962@gmail.com> |
||
|
|
e6aeb38da6 |
feat(analytics): track recoverable task outcomes (#6411)
Co-authored-by: bone3deep1962-collab <bone3deep1962@gmail.com> |
||
|
+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 @ |
||
|
|
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> |
||
|
|
ebcba704ef |
feat(prompts): introduce on-demand discovery in SP v2.0 (#6223)
* feat(prompts): introduce on-demand discovery in SP v2.0 * fix(prompts): prevent nested deck label overlap * fix(plugins): stabilize od-default task routing * fix: address prompt and deck audit regressions * fix(prompts): restore plan mode precedence * fix(daemon): accept all form answer headers --------- Co-authored-by: bone3deep1962-collab <bone3deep1962@gmail.com> |
||
|
|
9757b25279 |
fix(daemon): auto-mint assistantMessageId when clients omit pin (#6107)
* fix(daemon): auto-mint assistantMessageId when API clients omit it Web always supplies assistantMessageId. Headless/MCP/eval clients that already send conversationId but omit the pin left lastMessageId null and skipped multi-turn native session resume (missing_cursor). Mint the id server-side whenever a conversation is bound and the client did not, and seed the user message the same way the old MCP-only path did. * fix(daemon): validate conversation ownership before omit-pin seed Reorder project-ownership checks before minting assistantMessageId and seeding the user prompt so cross-project omit-pin requests cannot corrupt foreign chats. Make ChatRunCreateRequest.assistantMessageId optional and expose conversationId/assistantMessageId on McpRunCreateRequest with contract tests for the omit-pin shape. Generated-By: looper 0.11.1 (runner=fixer, agent=grok-build) * fix(daemon): reject missing conversation before omit-pin mint Return CONVERSATION_NOT_FOUND when a client supplies a stale or mistyped conversationId so omit-pin does not mint an unpersisted assistantMessageId and plugin snapshot resolve does not FK-crash with a 500. Generated-By: looper 0.11.1 (runner=fixer, agent=grok-build) * fix(daemon): seed latest turn and fallback-bound user prompts Prefer currentPrompt over full message transcripts when seeding omit-pin user turns, and still seed after conversationId fallback when the client already supplied assistantMessageId. Generated-By: looper 0.11.1 (runner=fixer, agent=grok-build) * fix(daemon): preserve attachments on omit-pin seeded user turns Copy request attachments and commentAttachments into the server-seeded user message so omit-pin/headless clients keep chips and annotation context after conversation reload. Generated-By: looper 0.11.1 (runner=fixer, agent=grok-build) * fix(daemon): treat empty currentPrompt as authoritative omit-pin seed Attachments-only ChatRequest turns send currentPrompt as '' while message is the flattened transcript. Prefer any string currentPrompt, including empty, so seeded user rows keep empty content and do not duplicate history. Generated-By: looper 0.11.1 (runner=fixer, agent=grok-build) * fix(daemon): seed turn metadata and bump project activity on omit-pin Persist sessionMode, runContext, and appliedPluginSnapshot on omit-pin seeded user turns so reload/retry keep applied context. Call updateProject after seed so listProjects reorders headless/API activity like PUT /messages. Generated-By: looper 0.11.1 (runner=fixer, agent=grok-build) * fix(daemon): require projectId for bound conversation ownership Reject POST /api/runs when conversationId is present but projectId is missing or non-string, so omit-pin cannot seed messages without owning project context. Cover both early and post-fallback ownership checks. Generated-By: looper 0.11.1 (runner=fixer, agent=grok-build) * fix(daemon): preserve slideIndex and BMP kind on omit-pin seeds Keep deck annotation slideIndex and classify .bmp as image when seeding user messages for clients that omit assistantMessageId. Generated-By: looper 0.11.1 (runner=fixer, agent=grok-build) * fix(daemon): seed empty message when omit-pin has attachments When currentPrompt is unset and message is empty, still seed the user turn if attachment metadata is present so chips/annotations survive reload. Generated-By: looper 0.11.1 (runner=fixer, agent=grok-build) * fix(daemon): seed omit-pin chat content from original request Plugin resolution may rewrite meta.message with a rendered scenario brief for the run; persist requestBody currentPrompt/message for the user turn so the internal brief is not shown as chat content. Generated-By: looper 0.11.1 (runner=fixer, agent=grok-build) |
||
|
|
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) |
||
|
|
4f3a0b3c06 |
fix(daemon): continue stalled post-tool sessions (#6237)
* fix(daemon): continue stalled post-tool sessions * fix(daemon): separate post-tool continuation budget Generated-By: looper 0.11.1 (runner=fixer, agent=codex) * fix(daemon): preserve retry telemetry budget Generated-By: looper 0.11.1 (runner=fixer, agent=codex) * fix(daemon): align retry failure telemetry Generated-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> |
||
|
|
af2a833506 |
fix(daemon): surface AMR OpenCode stall context (#6040)
* fix(daemon): surface AMR OpenCode stall context * fix(daemon): accept runtime-omitted OpenCode diagnostics Generated-By: looper 0.11.0 (runner=fixer, agent=codex) * fix(ci): make static gate runner-independent (#6039) --------- Co-authored-by: PerishFire <39043006+PerishCode@users.noreply.github.com> |
||
|
|
ad5a7d6ba1 |
Revert "feat(web): add guided inspiration and deliverable workflow (#5899)" (#6120)
This reverts commit
|
||
|
|
c3de4d5b95 | fix(daemon): classify model and timeout failures (#6103) | ||
|
|
a63eca6a4a |
fix(desktop): cover clear-cache in updater IPC teardown and host bridge guard (#6101)
* fix(desktop): include clear-cache in updater IPC teardown and host bridge guard The od:update:clear-cache handler was registered but missing from UPDATER_IPC_CHANNELS, so runtime teardown never removed it and a second createDesktopRuntime() in the same process would throw on re-registration. The host bridge structural guard likewise accepted an updater bridge that lacks the clear-cache method, deferring the failure to call time. Red specs: the boundary test now derives the teardown table from the registered od:update:* handlers, and the host contract test rejects a bridge missing clear-cache. * refactor(desktop): deduplicate deferred-installer timeout and update action literals The mac and Windows deferred-installer timeouts were two constants with one value branched on platform; fold them into one. Route handle()'s action dispatch through DESKTOP_UPDATE_ACTIONS instead of repeating the string literals. * docs(pack): correct the beta-s acceptance feed path to the betas channel release-beta-s publishes RELEASE_CHANNEL=betas, so the internal feed lives under betas/latest, not beta/latest. |
||
|
|
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> |
||
|
|
7787556cf1 |
fix(daemon): full ACP tool transcript and richer usage telemetry (#6057)
* fix(daemon): full ACP tool transcript and richer usage telemetry Map all ACP tool_call frames to canonical tool_use/tool_result for run.events, PostHog run_finished, and Langfuse spans. Preserve artifact_count via Write/Edit normalization, tighten path extraction, forward truncated results, and emit real tool startedAt. Enrich usage scanning (thought_tokens, anthropic vs openai cache semantics), canonical tool-family PostHog props, analytics recovery finish snapshot, and shared content-tool redaction. * fix(daemon): prefer ACP kind over noncanonical tool names Normalize recognized ACP kinds (e.g. kind:read + name:read_file → Read) before emitting tool transcripts so Langfuse content-tool redaction matches canonical families and does not leak file contents. Generated-By: looper 0.11.0 (runner=fixer, agent=grok-build) * fix(daemon): fail-closed redaction for unknown ACP tool payloads Generated-By: looper 0.11.0 (runner=fixer, agent=grok-build) * fix(daemon): flush open ACP tools as errors on session fail When a session dies mid-tool (timeout, exit, RPC error), emit pending tool_use/tool_result pairs with isError so transcripts stay complete. Generated-By: looper 0.11.0 (runner=fixer, agent=grok-build) * fix(daemon): redact ACP Bash stdout in tool transcripts Execute tools can dump private files (e.g. cat .env); replace Bash tool_result content with a length summary before the canonical transcript ships so Langfuse cannot receive raw execute output. Generated-By: looper 0.11.0 (runner=fixer, agent=grok-build) * fix(daemon): redact Linux home paths in Langfuse Bash tool spans Extend local-path masking for ACP Bash partial redaction to cover /home and /root (not only /Users and Windows Users), so Linux home paths do not leak into Langfuse tool inputs when content telemetry is on. Generated-By: looper 0.11.0 (runner=fixer, agent=grok-build) * fix(daemon): redact custom ACP tool names before Langfuse telemetry Sanitize path/token-like kind:other tool names at transcript emit and canonicalize non-allowlisted names for Langfuse span labels, toolName metadata, and redaction placeholders so untrusted custom names cannot leak even when content telemetry is off. Generated-By: looper 0.11.0 (runner=fixer, agent=grok-build) * test(e2e): force-click unstable comment targets in preview iframe UI P0 project-workspace timed out on comment attachment when Playwright kept waiting for hero-title stability under auto-fit zoom / bridge inject. Click once visible with force so the critical flow stays reliable. Generated-By: looper 0.11.0 (runner=fixer, agent=grok-build) * fix(daemon): merge partial usage frames in reverse analytics scan Keep provider_usage for thought/cache-only trailing usage events while continuing the reverse scan to the latest primary input/output/total record so run_finished and Langfuse do not drop complete token counts. Generated-By: looper 0.11.0 (runner=fixer, agent=grok-build) * fix(daemon): flush open ACP tools before AMR no-output fail Clean-flush pending non-think tools on successful session/prompt before evaluating emittedConcreteToolEvent so missing terminal tool_call_update does not mark a productive run as acp_no_visible_output. Generated-By: looper 0.11.0 (runner=fixer, agent=grok-build) * fix(daemon): merge partial primary usage and flush tools on ACP abort Require both input and output before ending the reverse usage scan so output-only or input-only trailing frames still merge earlier counters. Flush deferred open ACP tools as errored on abort so user-canceled runs keep complete tool transcripts for Langfuse/PostHog. Generated-By: looper 0.11.0 (runner=fixer, agent=grok-build) * fix(daemon): hash path-like ACP toolCallIds before telemetry Adapters can embed user paths in toolCallId (e.g. read:/home/alice/.env). Hash those values at tool_use/tool_result emit so Langfuse span ids and metadata.toolCallId cannot leak secrets, while keeping the raw id as the local frame-correlation map key. Generated-By: looper 0.11.0 (runner=fixer, agent=grok-build) * fix(daemon): always hash ACP adapter toolCallIds for telemetry Identifier-shaped secrets (sk-proj-*, ghp_*, JWTs) passed the prior allowlist because they contain no path characters. Hash every non-empty adapter toolCallId before tool_use/tool_result emission so Langfuse span ids and metadata.toolCallId cannot leak tokens. Generated-By: looper 0.11.0 (runner=fixer, agent=grok-build) * fix(daemon): keep reverse usage scan open for missing cache fields When a newer usage frame already has input+output but omits cache counters, continue scanning older frames so cache_read/cache_creation still merge into run_finished and Langfuse. Generated-By: looper 0.11.0 (runner=fixer, agent=grok-build) * fix(daemon): retain flushed ACP tool IDs against late terminals Keep acpToolRunEventState entries after flushOpenAcpTools so a racy tool_call_update cannot recreate the same id and emit a second tool_use/tool_result pair after timeout, abort, or prompt flush. Generated-By: looper 0.11.0 (runner=fixer, agent=grok-build) * fix(daemon): block kind:other Bash-like tool names Custom ACP tools with kind:other must not emit Bash/shell/execute/terminal names, which would unlock Langfuse partial redaction and leak rawInput. Generated-By: looper 0.11.0 (runner=fixer, agent=grok-build) * fix(daemon): fail closed on Bash names without execute kind ACP tools missing kind must not preserve name/title Bash labels that unlock Langfuse partial redaction; only recognized execute-family kinds may emit Bash. Generated-By: looper 0.11.0 (runner=fixer, agent=grok-build) |
||
|
|
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>
|
||
|
|
ca4ecf0bf2 |
chore(release): bump main to 0.16.1 ahead of stable 0.16.0 (#6000)
Co-authored-by: open-design-release-bot[bot] <open-design-release-bot[bot]@users.noreply.github.com> |
||
|
|
dcdb0c4204 |
fix packaged updates after payload cwd cleanup (#5967)
* fix packaged payload startup after cwd cleanup * fix deferred installer helper cwd Generated-By: looper 0.11.0 (runner=fixer, agent=codex) --------- Co-authored-by: Looper <looper@noreply.github.com> |
||
|
|
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 |
||
|
|
2e377a5152 |
name every packaged startup failure instead of bucketing it as unknown (#5956)
* name every packaged startup failure instead of bucketing it as unknown
The weekly `packaged_runtime_failed` alert is dominated by failures the event
had enough evidence to name and reported as opaque anyway. Triaging a 7d window
turned up three places where the emitter threw away a cause it was already
holding:
- 62 devices died with the daemon log tail read successfully, but
`parseDaemonLogTail` only ever matched `ERR_*`. A daemon that threw anything
else (SQLite corruption, EPERM, a plain throw) reported error_code=null AND
missing_module=null with its reason printed in the tail we had just read.
- 21 devices hit win32 CreateProcess failures. Node puts the real cause on the
error's `code`/`errno`/`syscall`; the event sent `.message` only, so
`spawn UNKNOWN` was indistinguishable from any other opaque failure.
- 149 devices produced an Error with an empty `.message` and no `.stack`. Every
free-form field went null, leaving residue that could not even be counted
apart from "no error at all".
So: parse the sidecar's own fatal line as a backstop when `ERR_*` misses, carry
the errno triplet off the thrown object, classify a rejected spawn as its own
`spawn-failed` kind rather than `unknown`, and guarantee a thrown error never
serializes to error_message=null — an unnamed leftover is now a named, countable
bucket.
Classification-only; the failures themselves are unchanged. Every test input is
a verbatim field payload from the alert window.
* reduce syscall to its operation token before sending it
Review catch, and a real leak. Node does not guarantee `syscall` holds only the
operation name: spawning a missing binary sets it to the whole invocation.
Verified against real Node —
spawn('/Users/elian/nonexistent/bin/thing')
→ syscall === 'spawn /Users/elian/nonexistent/bin/thing'
Forwarding that verbatim would have put the local username and install path
into an event that is retained even for opted-out users, while every other path
this module sends goes through scrubUserPaths.
Keep the first token — the operation is the whole analytic value, and the path
is already available (scrubbed) via error_message — then scrub what survives
and cap it, as a second line of defence. `code` is capped too.
This also fixes a classification miss the same review implies: the spawn-failed
branch compared the RAW syscall to 'spawn', so a spawn error carrying a path
never matched and fell through to `unknown` — the exact bucket this PR exists
to drain.
|
||
|
|
bb6c79c86d | fix packaged Windows obsolete outer cleanup (#5955) | ||
|
|
447b18b98e | fix packaged mac relaunch blocked by obsolete outer (#5940) | ||
|
|
1226773663 |
feat(daemon): route completed-run telemetry through Vela (#5666)
* feat(daemon): route completed-run telemetry through Vela * fix(daemon): migrate test telemetry relay domain * feat(daemon): send Vela feedback scores with completed-run telemetry Map score-create into the Vela envelope and use the same Vela sink for thumbs feedback, binding scores via the client run id instead of skipping them when account-aware telemetry is selected. * fix(daemon): skip anonymous fallback for Vela feedback auth errors Score-only feedback batches must stay on the account-scoped Vela path. When Vela returns 401/403, fail the delivery instead of posting to the anonymous relay, which has no matching trace for those scores. Generated-By: looper 0.11.0-beta.1 (runner=fixer, agent=grok-build) |
||
|
|
d1372da029 |
fix(daemon): reconcile run terminals after restart (#5817)
* reconcile run terminals after restart * fix(daemon): preserve replayed failure taxonomy Reclassify analytics-incomplete terminal failures from their persisted state and events while keeping the interrupted template exclusive to daemon-restart recovery. Generated-By: looper 0.11.0 (runner=fixer, agent=codex) * fix(daemon): preserve repaired errors and skip completed logs The repair path previously collapsed every failed message to restart copy and hydrated event logs before checking completed telemetry checkpoints. Use durable recovery metadata for status detail and gate log reads on outstanding PostHog or Langfuse work. Generated-By: looper 0.11.0 (runner=fixer, agent=codex) |
||
|
|
6fb644a57d |
feat(analytics): report conversation turn index (#5917)
Co-authored-by: bone3deep1962-collab <bone3deep1962@gmail.com> |
||
|
|
4e8db9a9b7 | add runtime version and retry provenance (#5814) | ||
|
|
ef35a08e24 |
feat(daemon): E-lite root-cause telemetry for inactivity_timeout attribution (#5775)
* feat(daemon): E-lite root-cause telemetry for inactivity_timeout attribution Add four run_finished fields that separate WHY a stalled run died, which the coarse `last_observed_phase` (WHICH phase) cannot: - approval_requested: an ACP approval/permission gate fired. Only the ACP path is daemon-observable (stream/CLI runtimes pass a skip-permissions flag so no gate fires); surfaced via an acp_approval_request diagnostic and folded in by summarizeRunDiagnosticsForAnalytics. - stdin_backpressure: the prompt write to the child's stdin was queued because the OS pipe buffer was full (the child was not draining stdin) — the corroborating signal for a stdin_write-phase stall. - tool_result_sent: a tool_result came back for the run's LAST tool_use. A stall with tool_call_seen && !tool_result_sent is the tool-result-not-delivered root cause. - last_progress_age_ms: age of the last agent activity at finish (the inactivity-watchdog clock). Near the ceiling on a stall, ~0 on a clean finish. Contract fields on RunFinishedProps; daemon populates two off the run object (stdin_backpressure, last_progress_age_ms) and derives two from run.events in the diagnostics summarizer (tool_result_sent, approval_requested). * fix(daemon): pair tool_result_sent by id so parallel tools attribute correctly Review catch (nettee/Looper): the first cut flipped `tool_result_sent` to true on any tool_result after a tool_use, so a multi-tool turn like tool_use(A), tool_use(B), tool_result(A) reported delivered even though B was still outstanding — bucketing multi-tool stalls as "provider stalled after delivery" instead of "tool result not delivered". Now paired by id (`tool_use.id` <-> `tool_result.toolUseId`, the same pairing summarizeRunTimingAnalytics uses): `tool_result_sent` is true only when EVERY committed tool_use received a matching tool_result (outstanding set empty). Tests cover the reviewer's regression case plus parallel all-resolved and sequential-last-outstanding. * fix(daemon): count-pair id-less tool events so degraded stalls aren't "delivered" Second review catch (nettee/Looper): id-based pairing alone still fell through to `tool_result_sent: true` when a runtime emits a tool_use with a null id, because those were counted in `sawAnyToolUse` but never tracked as outstanding. That is not hypothetical — `agent-protocol/pi-rpc/events.ts` and `copilot-stream.ts` both emit `toolCallId ?? null` for tool_use.id AND tool_result.toolUseId on degraded provider events, so an unpaired id-less tool call would mask exactly the stall this metric attributes. Id-less tool events are now paired by count alongside the id-keyed set: `tool_result_sent` requires both an empty outstanding-id set and at least as many id-less results as id-less uses. Tests cover an unpaired id-less stall and an id-less pair that did resolve. * fix(daemon): capture stdin backpressure on the default text-input path Third review catch (nettee/Looper): `stdin_backpressure` never recorded anything for non-`stream-json` runtimes. `child.stdin.end(composed, ...)` returns the stream rather than a boolean, and `writableNeedDrain` is already back to false by the time it returns — even for a chunk that `write(chunk)` would have rejected. Claude is the only runtime on the stream-json path, so the field was permanently false on exactly the runs whose `stdin_write` stalls it exists to attribute. The write and the close are now issued separately through `writePromptAndEndStdin`, which returns the boolean the OS pipe actually gave us. Extracted into chat-run-lifecycle.ts so it is unit-testable: tests cover the backpressured write, the accepted write, and the flush callback still firing (the `stdin_write_end` lifecycle mark). Verified red against the previous behavior. |
||
|
|
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 |