mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
2f5c9bb218e5f42a5a890e07ad00b3f096ccbd49
2734
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2f5c9bb218 |
feat: use model metadata in picker badges and bump Vela CLI (#5407)
* Use model metadata in picker badges * fix: preserve model picker metadata fallbacks Generated-By: looper 0.10.2 (runner=fixer, agent=codex) * fix: pass BYOK model metadata to pickers Generated-By: looper 0.10.2 (runner=fixer, agent=codex) * Bump bundled Vela CLI * Keep BYOK model permissions out of discovery * Preserve Vela model metadata over cache * fix: refresh nix pnpm dependency hashes |
||
|
|
d71bc60e2c |
feat(landing-page): align pricing cards with cloud subscription modal, add Free tier (#5404)
* feat(landing-page): align pricing cards with cloud subscription modal, add Free tier Mirror the vela subscription modal on the /pricing plan cards across all 10 locales: add the Free card (console-linked CTA, trial-credit lead row, per-plan model availability with Grok-4.5 in the trial pool), add per-tier concurrency rows, interval-suffixed CTAs, and the updated model rosters (Claude-Fable-5 / Grok-4.5 / Qwen-3.7-Max, vela naming). Drop rows the modal no longer shows (cost anchor line, deliverable estimate + tooltip). FAQ updates in the same locales: refund answer switches to the new no-refund-cancel-anytime policy, and a new entry explains the Free plan's limited-time trial credit. * fix(landing-page): align Free-plan FAQ answer with the trial-credit story The plans-difference FAQ answer still said Free is local CLI + BYOK only, contradicting the Free card's trial-pool model row and the new trial-credit FAQ entry. Rewrite the answer in all 10 locales to state the 7-day trial window (hosted trial pool, currently Grok-4.5) and the BYOK-only fallback after the credit expires. --------- Co-authored-by: NJUHua <113895241+NJUHua@users.noreply.github.com> |
||
|
|
c2e728a502 |
fix(daemon): filter BAAI BGE embedding models out of the BYOK chat-model picker (#5380)
BYOK model discovery (listProviderModels, providers/models route) filters an OpenAI-compatible gateway's /models catalogue down to chat-completion-capable models via isOpenAiChatModelId, matching against substrings like `embedding`, `rerank`, `tts`. BAAI's BGE embedding family (e.g. `BAAI/bge-large-en-v1.5`, served by gateways like SiliconFlow and Volcengine Ark) doesn't match any of them, so it was surfacing as a normal, selectable chat model — then 404ing with "model does not exist" the moment a user actually tested it, exactly as reported. `bge-reranker-*` variants were already excluded by the existing `rerank` check; this adds the plain embedding variants. Fixes #5367 |
||
|
|
afec21ac4d |
fix byok-opencode: route non-OpenAI openai-protocol hosts to @ai-sdk/openai-compatible (#5344)
The BYOK OpenCode path mapped protocol "openai" straight to @ai-sdk/openai (Responses API /responses) for every host, so providers that only serve /chat/completions (DeepSeek, vLLM, etc.) 404 on /responses. Route non-api.openai.com openai-protocol hosts to @ai-sdk/openai-compatible; real OpenAI stays on @ai-sdk/openai. Adds a DeepSeek regression test and corrects the vLLM test to openai-compatible. |
||
|
|
20a077f66c |
fix(desktop): avoid native file dialog AppHang on Windows Downloads (#5314)
Native Windows file dialogs enumerate the shown folder synchronously via the shell. On OneDrive-backed Downloads folders that stalls the dialog's UI thread long enough for Windows to flag the app 'not responding' (AppHangB1, reported against 0.14.0.0). The diagnostics Save dialog also forced its defaultPath into the Downloads folder, opening the dialog directly in that slow location. Seed the diagnostics Save dialog with a bare filename (matching the PDF/PPTX flows) and add dontAddToRecent to every native Save/Open dialog to trim shell recent-items work against the browsed folder. Adds a regression test locking the filename-only + dontAddToRecent behavior. |
||
|
|
91179e6ffa |
fix(daemon): reset turnCompletedCleanly on same-run retry teardown (#5341)
A same-run retry reuses one run object across attempts. tearDownAttemptForRetry cleared the per-attempt fields (child, exitCode, signal, error, stdinOpen) before re-spawning but left run.turnCompletedCleanly untouched. That flag is set by a clean `turn_end` (applyClaudeStreamJsonRunBookkeeping) and read by the terminal classifier: when attempt 1 finished a clean-but-empty turn (retryable no-output timeout) and attempt 2 then crashed (exit 1, no output), the crashed run was classified 'succeeded' off attempt 1's stale flag — and reported retry_result: success in telemetry. Clear the flag in teardown alongside the other per-attempt resets, with an invariant comment so future per-attempt state that feeds terminal classification is cleared in the same place. Regression: tests/retry-stale-turn-completed-flag.test.ts drives the full run lifecycle over the daemon HTTP API with a fake claude CLI (empty turn then hang, crash on retry). Red on main, green with this fix; deterministic 8/8 both ways. Co-authored-by: tomsen-ai <230283659+tomsen-ai@users.noreply.github.com> |
||
|
|
729525a884 |
fix(daemon): backfill Windows CLI cache env (#5306)
* fix(daemon): backfill Windows CLI cache env * fix(daemon): keep Windows cache env sandboxed |
||
|
|
cd07d359df |
feat(chat): surface daemon failure_detail to sharpen run-error guidance (#5321)
* feat(chat): surface daemon failure_detail to sharpen run-error guidance The daemon already classifies each run failure into failure_category / failure_detail (run-failure-classification.ts) for retry policy and telemetry, but kept it daemon-internal — so the chat could only key error guidance off the coarse errorCode. That collapses distinct causes that share one code: a hard quota and a transient 429 both arrive as RATE_LIMITED, and a text-detected missing CLI leaks in as the opaque AGENT_EXECUTION_FAILED. Propagate the classification to the client (contract DTO + SSE end frame, mirroring the proven `resumable` path) and let resolveRunFailureUi take `detail` as a first-class input that can override a too-coarse code: - hard_quota / workspace_credits_exhausted -> a distinct "Quota exhausted" card that drops the futile Retry (primaryAction 'none') and steers to the hosted-AMR switch card instead. - cli_not_installed (text-detected) -> the same install-the-CLI card the AGENT_UNAVAILABLE code path already renders. A transient 429 without a hard-quota detail still offers Retry, and the Antigravity terminal switch-model flow still wins over the generic detail override. Stacks on #5308 (the code->guidance mapping this detail layer refines). * chore(i18n): rebrand user-facing "AMR" to "Open Design Cloud" The hosted model service is now Open Design Cloud, but the run-error cards this PR sharpens (switch / auth / balance), plus settings account + wallet labels, avatar console, onboarding gate, and the handoff website link, still showed the old "AMR" (or bare "Open Design") name. Normalize every user-visible brand mention to "Open Design Cloud" across all 19 locales. Value-only: no keys, agent ids, URLs, or raw daemon error strings touched. * fix(chat): persist daemon failure classification onto assistant message The daemon emits the terminal `error` SSE frame from the child-close handler before a run is finalized, so `failureCategory` / `failureDetail` (computed at finalize) weren't known when that frame was first persisted to the assistant message — the stored `status:error` event kept only `{ detail, code }`. A failure persisted without the live web error handler saving the message, or a conversation reloaded before that save landed, therefore fell back to the coarse errorCode UI and lost the specific fix guidance this PR adds. Stamp the finalize-time classification onto the persisted assistant message: enrich the last `status:error` event in place (append one only if a failed run somehow never persisted an error frame). Covered by a daemon regression that reads the stored assistant message via the conversation-messages API after a failed hard-quota run. * fix(chat): keep daemon failure classification on the live web reload/recovery paths Two live-propagation gaps let the sharpened run-error card degrade back to the coarse/raw UI even though the daemon now sends the classification: - appendErrorStatusEvent() no-op'd whenever the message already had a trailing status:error with the same detail. In the reload/reattach race (reload reads the daemon-persisted error frame first, then the run finishes and onError arrives with the finalize-time classification) that dropped the new code / failureCategory / failureDetail. Merge those into the existing event in place instead; no-op only when nothing is added. - The error-frame-then-status recovery probe in providers/daemon.ts broke out on a terminal failed status without copying failureCategory / failureDetail (unlike the post-loop status fallback), so markErrorRunFailure stamped null and the failureDetail mapping fell back to the generic card on the reconnect path. Mirror the two assignments before the break. Covered by chat-events (in-place merge) and daemon-sse-reconnect (error frame + terminal status fetch, no end frame) regressions. * fix(web): update AMR tier-upgrade test for 3-arg resolveRunFailureUi The main merge kept a two-argument call site that passed agentId as detail after the signature gained an explicit detail parameter. Co-authored-by: Cursor <cursoragent@cursor.com> * ci: re-trigger validate workspace on approved head Co-authored-by: Cursor <cursoragent@cursor.com> * test: align AMR rebrand assertions with Open Design Cloud copy Update unit and e2e selectors that still expected pre-rebrand strings for account status, handoff website link, and switch-and-retry CTA. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: xiaoche-hub <298951296+xiaoche-hub@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
323246a64c |
Website Clone: first-party Home entry, fidelity, real-clone example cards + tracking (#5178)
* Add Website Clone home entry with bundled web-clone skill and root-relative asset preview fix
* Retrigger CI
* Open the site entry after website-clone turns
* Harden web-clone skill asset and color fidelity
Recon now scrolls the full page and captures @font-face rules, real font/image
resource URLs, computed section palette, and resolved root variables. Asset
harvest is rebuilt on the browser network stack so cross-origin CDN images,
fonts, and media (hotlink-protected included) all land locally, with a
self-hosted fonts.css and an originalUrl→localPath manifest. Clone audit gains
--recon --strict fidelity gates (fonts self-hosted, images downloaded, key
section colors exact) that exit 2 on violations, and SKILL.md makes all three
a mandatory delivery gate.
* Default web-clone mode to faithful reproduction when unconfirmed
A real nike.com run showed the agent silently downgrading to visual-homage
mode (placeholder brand, typography blocks instead of photography, approximated
palette) because the user skipped the turn-1 confirmations and the site was a
copyright-heavy commercial SPA. Fidelity is the point of this scenario: default
to faithful reproduction, land real assets first, record trademark/copyright
replacements as a pre-deploy checklist for the user to decide in Step 6, and
never skip script recon just because the target looks bot-sensitive.
* Streamline web-clone runs: skip discovery with URL, exempt brand constraints, actionable playwright setup
Three run-path optimizations for the Website Clone scenario:
1. POST /api/projects derives metadata.skipDiscoveryBrief for web-clone
projects whose pending prompt already carries the target URL — the URL is
the brief, so the turn-1 question form no longer stalls the run (an
explicit client flag still wins). Covered by
project-create-web-clone-discovery.test.ts.
2. Prompt composition skips the design-system authoritative block and craft
rules for intent=web-clone runs: the fidelity target is the original
site, not the project brand; brand application belongs to the skill's
Step 6 replacement phase.
3. The plugin's playwright-loader now also resolves from the process cwd
(so 'npm i -D playwright' in the project root works for scripts staged
under .od-skills/) and fails with exact fix commands; SKILL.md documents
the staged-path resolution and forbids downgrading to eyeballing because
of missing deps.
* Swap the copyright guardrail for faithful reproduction on web-clone runs
The official designer prompt's 'don't recreate copyrighted designs — help
the user build something original instead' bullet made web-clone agents
silently rebrand clones (Nike → placeholder brand, original SVGs instead of
real photography) even after the skill mandated faithful defaults: a system
prompt guardrail outranks skill blocks. For intent=web-clone runs the bullet
is swapped for a faithful-reproduction rule that routes trademark/copyright
caution through a pre-deploy replacement checklist (NOTES.md) the user owns.
Prompt tests guard both directions of the substitution.
Also: recon now captures scroll-feel signals (scroll-behavior, scroll-snap
rule count, sticky/fixed census) and SKILL.md makes matching the original's
scroll behavior (smooth-scroll libs, snap, sticky) a fidelity requirement.
* Extract web-clone into a shared global skill referenced by ref
The Website Clone workflow (SKILL.md + 14 recon/harvest/audit scripts +
references) moves from the example-web-clone plugin into the global skill
registry at skills/web-clone/. example-web-clone now references it via
od.context.skills:[{ref:'web-clone'}] instead of shipping its own copy, so the
upcoming per-site clone example plugins can all share one skill rather than
each duplicating a 16KB SKILL.md and its scripts.
Pinned-plugin skill resolution (server.ts) previously only loaded a plugin's
LOCAL ./SKILL.md; a {ref} skill loaded nothing, so the body never entered the
prompt and the scripts never staged. It now falls back to resolving the ref
against the global registry, loading the body and registering the skill dir for
cwd staging. Verified end-to-end in a live run: skills/web-clone stages to
.od-skills/web-clone-<hash>/ with SKILL.md + all scripts.
guard.ts residual-JS allowlist follows the scripts to their new home; a
structural test locks the manifest<->skill contract.
* Add 7 Website Clone example-prompt cards for the Home clone rail
nexu.io, open-design.ai, anthropic.com, vercel.com, resend.com, linear.app,
raycast.com — each a thin example plugin that references the shared web-clone
global skill (od.context.skills:[{ref:'web-clone'}]) so none duplicate the
workflow. Each seeds the composer with a faithful-clone prompt for its URL and
ships a self-contained example.html preview built from a real recon capture of
the target homepage, so the gallery card shows what the card clones. They
surface under the Website clone chip's example-prompt rail (verified in a live
desktop run) and inherit the chip's intent:'web-clone' metadata, so clicking one
runs through the full fidelity path (skip discovery, brand exemption, faithful
reproduction, script recon/harvest/audit).
* Polish clone example-card previews with a framed hero capture
The first pass inlined a downscaled full-page screenshot cropped mid-content —
it read as a raw screenshot dump. Each preview is now a crisp above-the-fold
hero capture (1440x900 DPR2, 16:10) presented inside a browser-window chrome
(traffic lights + host URL pill) on a brand-tinted gradient, so the gallery
card reads as an intentional site preview. Verified by rendering the previews
headless.
* Cache the plugin list so Home remounts don't grey the create rail
HomeView re-fetched + parsed the large /api/plugins payload on every remount,
leaving the create-rail chips disabled for 1-2s each time. A module-level cache
(listPluginsFresh, 10s TTL) serves warm mounts without the network round trip so
pluginsLoading clears within a frame; the first render stays cheap (empty list)
to keep the click off the critical path. Also hide the base example-web-clone
plugin from the example rail (the per-site clone-* cards are the real examples).
* Ship real self-contained clones as example-card previews + align seed prompt
Each clone-* example.html is now a real, self-contained reproduction of its
target site (inlined CSS/fonts/images via document.styleSheets serialization so
CSS-in-JS/Next.js SPAs like Vercel/Raycast capture correctly, oversized icon
sprites dropped, rasters downscaled to stay under the 5MB preview cap, plus a
scroll-reveal motion layer). Preview renders the real HTML; Remix copies it into
a project as index.html. Manifests reuse the base web-clone useCase.query with a
{{targetUrl}} default per site, so picking a card seeds the same preset prompt
with the URL filled in instead of the marketing description.
* Shrink clone example previews ~33% (dedup + drop EOT + WebP)
Three lossless-to-near-lossless passes on the self-contained clones:
- Dedup repeated data-URIs (same image used in <img> + css url() N times): store
each once as a :root CSS var, point url() at var(), hydrate img[src] from the
var via a tiny script. Resend alone shed 1.8MB.
- Drop leftover EOT/fontobject font data-URIs (IE-only, redundant with woff2).
- Re-encode inlined raster images to WebP (~50% smaller than JPEG at equivalent
quality) via cwebp q72.
Total across the 7 cards: ~23MB → ~15.5MB, every card now under 3MB (was up to
5MB). Image hydration + fidelity verified by headless render.
* Lead create rail with Website clone; seed example cards with just the URL
- Move 'web-clone' to the front of CREATE_RAIL_ORDER so Website clone is the
first create-rail card, ahead of Slides (deck).
- Website-clone example cards (Clone Nexu, Clone Vercel, …) now seed the
composer with the same short 'clone this site: <url>' scaffold the chip uses
(localized webClonePromptSeed prefix + the card's targetUrl), e.g.
'Website URL to clone: https://nexu.io', instead of the multi-paragraph
build spec. Picking the chip with an empty composer already seeds the bare
prefix for the user to paste a URL after.
- Fix a test-only regression from the Home plugin-list cache: the module-level
visible-plugins cache leaked a case's mocked /api/plugins payload into later
cases via listPluginsFresh, greying the rail / stranding scenarios on
'Template None'. Add resetPluginsCache() and clear it in a global afterEach.
* Track web-clone example-card picks (example_prompt / example_open_project)
The Website-clone example cards (Clone Nexu / Vercel / …) had no analytics on
pick or remix, so the site-clone funnel could not be broken down per example.
Wire the two clicks the contracts already type (埋点文档 row 116):
- useExamplePlugin → ui_click area=chat_composer element=example_prompt
- duplicateExamplePlugin (Remix) → element=example_open_project
Both carry chip_id (web-clone) + plugin_id (example-clone-<site>) + plugin_type
for per-site breakdown. The created project's own project_kind=web_clone (already
derived from metadata.intent via projectKindToTracking) still rides
project_create_result / run_created, so 'how many projects are website clones' is
answerable without extra work. No contracts change — the element enum and the
web_clone project_kind already existed. Adds a behavioral test asserting both
events fire with the right triplet + attribution.
* Mirror web-clone copyright-guardrail swap into contracts/BYOK prompt
PR #5178 blocking review (nettee/looper): the daemon prompt special-cases
intent='web-clone' (faithful-reproduction rule) but the contracts prompt stack —
which apps/web uses for API/BYOK runs — imported OFFICIAL_DESIGNER_PROMPT directly
and never routed web-clone, so BYOK website-clone runs kept the original 'build
something original' guardrail. Daemon and BYOK now behave identically:
- contracts/prompts/official-system.ts: add COPYRIGHT_GUARDRAIL_BULLET,
WEB_CLONE_COPYRIGHT_GUARDRAIL_BULLET, renderOfficialDesignerPrompt (mirrors the
daemon renderer; contracts base has no execution placeholders so it only swaps
the bullet).
- contracts/prompts/system.ts: composeSystemPrompt routes the charter through
renderOfficialDesignerPrompt({ webCloneFidelity: metadata.intent==='web-clone' }).
- Add system-prompt-web-clone.test.ts covering both modes + the byte-identical
bullet guard.
* Complete web-clone BYOK prompt parity + rebase relative JS asset refs
Two remaining blocking issues from PR #5178 looper re-review:
1. contracts composeSystemPrompt still appended the active-design-system
'authoritative' block + visual-direction override unconditionally, while the
daemon suppresses both for intent==='web-clone'. A BYOK web-clone run with an
active design system was still told the design system wins, fighting faithful
reproduction. Gate both sections behind !isWebCloneRun; add contracts tests
asserting web-clone BYOK prompts drop the design-system guidance.
2. rewriteInlinedScriptAssetRefs only handled root-relative literals. When
js/app.js is inlined into the HTML entry, a sibling lookup like
fetch('./data.json') / new Worker('./worker.js') / import('./chunk.js')
resolved against the HTML root instead of js/. Take the source script path and
rebase confirmed relative refs against it (mirrors the CSS pass), membership-
confirmed; add unit cases for ./sibling, ../parent, and escaping/missing.
* Website-clone examples: plain text prompt cards, drop embedded HTML + Remix
The 7 site example cards embedded a full self-contained clone of each target site
(anthropic.com, vercel.com, …) as example.html and offered a one-click Remix that
copied that markup into a project. Shipping verbatim copies of other companies'
sites is an IP risk, so replace them with plain text prompt cards:
- Delete plugins/_official/examples/clone-* (7 dirs, incl. the example.html
reproductions and the Remix-enabling preview.entry).
- Add a 'web-clone' entry to HOME_PROMPT_EXAMPLES (en + zh-CN, others fall back to
en): 7 text cards that fill the composer with 'Website URL to clone: <url>'.
With the site plugins gone, the web-clone chip's preset rail is empty so these
static text cards render instead. Clicking one fills the URL prompt and (via
the existing usePromptExample) still fires ui_click element=example_prompt
chip_id=web-clone — no plugin_id / no Remix / no example_open_project.
- Remove the now-dead webCloneCardTargetUrl seed override; refresh stale comments.
- Rewrite HomeView.web-clone-tracking test for the text-card path (asserts text
cards render, no plugin-preset / no remix affordance, and the pick still tracks).
The base example-web-clone skill + web-clone run behavior (intent, fidelity
prompt, discovery skip) are unchanged.
* Give web-clone example cards a site logo + domain, not raw prompt text
The 7 Website-clone examples rendered as near-identical white boxes each showing
'Website URL to clone: https://<site>' — visually flat and hard to scan. Render
them as a logo gallery instead: each card shows the site's own favicon (resolved
at render time via a public favicon service — no third-party brand assets are
bundled) in a rounded tile above the bare domain (nexu.io, vercel.com, …). A
blocked/failed icon fetch falls back to a lettered monogram tile. Click behavior
is unchanged: the full 'Website URL to clone: <url>' prompt is still what fills
the composer and fires ui_click element=example_prompt. Non-URL prompt examples
(other chips) keep the plain text card.
* Web-clone examples: keep only open-design, add kimi
Legal review flagged brand-copy risk in the example rail. Drop the
nexu / anthropic / vercel / resend / linear / raycast site prompts and
keep just open-design.ai plus kimi.com (official Kimi partnership).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: free666799 <293857035+free666799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
|
||
|
|
57386e4ef1 |
fix(analytics): attribute byok opencode providers (#5356)
Co-authored-by: Siri-Ray <2667192167@qq.com> |
||
|
|
3d696c81f9 |
feat(web): revise onboarding "hear about us" source options (#5311)
* feat(web): revise onboarding "hear about us" source options Replace the 11-item discovery-source list on the onboarding About-you step with a 14-item list (13 named channels + Other), reordered by expected volume and de-duplicated: - Merge Instagram + Threads into a single "Instagram / Threads / Facebook" chip (value meta_social). - Add TikTok, Discord / community, Email / Newsletter, and Other. - Normalize the AI-tool value to ai_tool and clarify labels with brand examples (Search (Google / Bing), AI tool (ChatGPT / Perplexity)). i18n: rebuild the onboardingSource block across all 19 locales. zh-CN / zh-TW / fr are localized; the remaining English-fallback locales now all share one identical up-to-date English string, removing prior stale wording drift. * feat(web): add free-text detail for the onboarding "Other" source When a visitor picks "Other" on the "Where did you hear about us?" step, reveal an optional text input so they can name the actual channel. The raw text is kept separate from the aggregatable `other` bucket: - source stays 'other' for funnel splits; the typed detail rides along as `sourceOther` (persisted, capped, only kept while source==='other'). - PostHog: person property `od_onboarding_source_other` and the about_you_submit event property `discovery_source_other`. - Memory profile body appends the detail as "Other (<text>)". i18n: new onboardingSourceOtherPlaceholder across all 19 locales. Tests cover person-property capture, stale-detail drop, and storage round-trip. * fix(web): render the onboarding "Other" input inline with its chip The free-text detail previously floated on its own line below the source group, reading as detached from the "Other" chip that reveals it. Give OnboardingChipField an optional `trailing` slot rendered at the end of the chip row and pass the input through it, so it sits directly after the Other chip (filling the rest of the row, or wrapping at a comfortable width) and matches the chip height. Autofocus on reveal. * fix(web): mirror discovery_source_other onto the completion-result carrier Review follow-up: the onboarding_complete_result event is the fallback mirror of about_you_submit, preserving the survey snapshot when the individual click is dropped on navigation. It carried discovery_source but not the new discovery_source_other, so the fallback path kept discovery_source: 'other' while losing the raw channel the user typed. Extend OnboardingCompleteResultProps and the completion payload to carry discovery_source_other when liveProfile.source === 'other', and add a regression asserting both snapshot carriers stay in parity. * i18n(web): localize the onboarding About-you survey across all locales The onboarding "About you" block (role / org size / use case / discovery source options + their labels, placeholders, and the profile heading) shipped English-only for every locale except ru / zh-CN / zh-TW when it was introduced (#4744) — the surrounding onboarding steps were already translated, so this block read as a conspicuous English island. Localize all 49 About-you strings into the 14 English-fallback locales (ar, de, es-ES, fa, hu, id, it, ja, ko, pl, pt-BR, th, tr, uk) and complete the partially-translated fr. Brand names (X, GitHub, YouTube, TikTok, Reddit, LinkedIn, Instagram/Threads/Facebook, Discord, Google, Bing, ChatGPT, Perplexity), leading emoji, and numeric size ranges are preserved verbatim; only the descriptive wording is translated. * i18n(web): fully localize residual jargon in the onboarding survey Follow-up to the About-you localization: some role/use-case values kept English or transliterated business jargon (Growth, Operations, Design system, deck) that not every reader is a tech insider for. Translate these into each language's natural native word — e.g. Growth → Wachstum (de) / Croissance (fr) / Зростання (uk) / 成長 (ja) / 성장 (ko), deck → Folien (de) / слайды (ru) / 슬라이드 (ko). Universal acronyms (AI) and true brand names stay as-is. * fix(web): keep the "Other" free-text out of analytics (privacy) Review follow-up: the raw text a user types in the onboarding "Other" source box was flowing into analytics as an event property (discovery_source_other) and a durable person property (od_onboarding_source_other). Analytics events/profiles must stay free-text/PII-free, and the scrubber does not sanitize arbitrary event or person-property payloads — so an email, real name, or private note could ship to PostHog unchanged and persist indefinitely. Carry only the enumerated `other` bucket into analytics and AMR. Drop discovery_source_other from the OnboardingClickProps / OnboardingComplete ResultProps contracts and both emit paths, drop od_onboarding_source_other from the person properties, and stop persisting sourceOther to the stored attribution profile. The typed detail is preserved solely in the app-owned Memory note ("Discovery source: Other (<text>)") and the live input UI — the free-text home the contract explicitly allows. Tests now assert the raw text never reaches either analytics carrier or storage. * fix(web): keep the "Other" free-text ref in sync to avoid note data-loss Review follow-up: the Other-source free-text input updated only React state, but its sole sink — the Memory note written from `profileRef.current` — relies on a later useEffect to sync the ref. A fast type-then-Continue could read the ref before that effect ran and drop the last keystrokes (or the whole custom channel). Mirror the value into `profileRef.current` synchronously in the input's onChange so the note always reflects the latest input, removing the timing dependency. Adds a behavioral test asserting the typed Other value reaches the `/api/memory/user_profile` payload. Note: jsdom/RTL cannot reproduce the underlying race (the async memory PUT always reads the ref after the sync effect flushes), so this is a behavioral guard rather than a strict red spec; the fix removes the dependency by construction. * refactor(web): route all About-you edits through a ref-syncing helper Review follow-up: the previous fix only covered the "Other" text input, but the source chip's onChange (and the role/org/use-case/email handlers) still updated React state alone. A user could pick Other, type a channel, switch to another chip, and continue before the state→ref useEffect ran — leaving the live ref (read by emitAboutYouSubmit / the Memory note / newsletter submit) with a stale source and abandoned sourceOther. Introduce a single `updateProfile(producer)` helper that mirrors the next value into `profileRef.current` synchronously and calls setProfile, and route every About-you field through it. This removes the state/ref timing dependency for the whole survey by construction, rather than patching one field at a time. Adds a behavioral guard that switching away from Other clears the abandoned free-text from the memory note. --------- Co-authored-by: Scarlett Huang <scarletthuang@Scarletts-MacBook-Air.local> |
||
|
|
9ef4f0daa6 |
[codex] Refine privacy consent choices (#5384)
* Refine privacy consent choices * Add privacy consent value copy * Polish privacy consent copy * Refine privacy banner wording * Fix privacy consent review issues * chore: retrigger privacy consent CI * Fix privacy banner dismissal in AMR onboarding tests * Fix privacy choice telemetry preservation * Preserve settings draft during privacy choice * Preserve pending settings privacy choices * Align privacy policy consent labels --------- Co-authored-by: elifive555555 <296440099+elifive555555@users.noreply.github.com> |
||
|
|
9b5cdd8436 |
fix(daemon): move connected-MCP directive out of the cached system prompt (#5336)
* fix(daemon): move connected-MCP directive out of the cached system prompt
The '## External MCP servers — already authenticated' directive was
composed into daemonSystemPrompt, part of the cacheable system prefix
sent upstream. Its membership is driven by live OAuth Bearer validity
(isTokenExpired), so a token expiring/refreshing/reconnecting mid-
conversation changes the directive -> changes the system prompt bytes ->
invalidates the whole downstream prompt-cache prefix, including the
conversation-history cache AMR/Claude-on-Bedrock relies on. Production
telemetry (stable_prompt_cache_miss_reason='stable-prompt-changed')
attributes the bulk of resume-turn prefix drift to this directive, rising
with conversation length as more tokens cross expiry.
Move it from composeSystemPrompt into the per-turn client instruction
slice (alongside runContext/cwd hints): re-sent uncached every turn,
landing after the cached system+history prefix. The model still sees the
current MCP auth state each turn (more accurately -- live state, not the
seed snapshot), while the cacheable prefix stays byte-stable across
resumes. renderConnectedExternalMcpDirective is exported and its leading
separator dropped so it composes cleanly in the joined slice;
composeSystemPrompt no longer takes connectedExternalMcp.
* test(daemon): e2e red-spec for the connected-MCP directive moving to the per-turn slice
Drives a real two-turn codex session (native resume) with a connected
external MCP server (enabled config + live OAuth Bearer) through the
daemon HTTP boundary, capturing the exact prompt each turn reaches the
agent's stdin. Asserts the '## External MCP servers — already
authenticated' directive is present on turn 1 AND re-sent on the turn-2
clean resume — which only holds once the directive rides in the per-turn
instruction slice instead of the cached stable block. Verified red on
origin/main (turn-2 stdin lacks the directive because the cached block is
not re-sent on a clean resume) and green with the fix.
* test(daemon): keep the MCP-directive red-spec hermetic
The new test writes an authenticated github MCP server into the
process-wide OD_DATA_DIR (tests/setup.ts shares one data root for the
whole daemon Vitest run) and the shared afterEach never reset it, so the
connected-MCP state could leak into later test files and make them
suite-order dependent. Reset the MCP config (empty servers) and clear the
github token in afterEach — both idempotent no-ops for the other tests in
this file.
* test(daemon): let MCP teardown failures fail fast
Drop the unconditional .catch(() => {}) around the afterEach MCP reset.
writeMcpConfig always overwrites to an empty server list and clearToken
is a documented no-op when the entry is absent, so neither needs the
catch — and swallowing a real teardown failure would silently let MCP
state leak into later tests, defeating the isolation guarantee.
|
||
|
|
81b20dc6a2 |
fix(web): avoid missing design-system preview assets (#5381)
Co-authored-by: lefarcen <935902669@qq.com> |
||
|
|
6469a3e488 |
feat(landing-page): add /solutions/html-to-ppt/ tool page + slides cross-links (#5387)
Co-authored-by: Joey <276262049+xne998808-ai@users.noreply.github.com> |
||
|
|
e02a36f51c |
feat(examples): add Open Design homepage — interactive 3D showcase (#5355)
* feat(examples): add Open Design homepage — interactive 3D showcase Adds the live open-design.ai homepage as a first-party, self-contained example: an interactive React Three Fiber / Next.js hero with a real-time 3D "Open Design" wordmark, sticker collage, variable fonts, cursor parallax, and scroll-driven motion. Renders in the sandboxed powered preview. Made repo- and preview-safe without touching the app's runtime: - 3D geometry Draco-compressed (KHR_draco_mesh_compression): hero wordmark 4.4MB -> 300KB, cnt 2.2MB -> 548KB, decoded by the bundled DRACOLoader. Visually identical (verified). - Work/sticker imagery converted to LOSSLESS WebP: pixel-identical to the source PNGs (alpha + visible-pixel diff = 0), 12.2MB -> 7.1MB. - Every file now < 1MB, so the template is fully self-contained in-repo with no external asset host. - Path-portable so it renders from the nested preview subpath, not just the site root: absolute asset paths made relative; font/bgm URLs derived from document.baseURI instead of window.location.origin; App-Router createHrefFromUrl pinned to "/" and history push/replaceState URL neutralized so hydration matches and baseURI stays put (fixes the React #418 + broken relative-asset resolution seen when serving a Next RSC bundle from a subpath). Verified end-to-end from a deep subpath: 3 Draco models + 41 WebP + 3 fonts load, zero 404s, zero console errors, no hydration error, interactive (scroll/cursor). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(examples): list the full asset set in open-design homepage manifest Addresses review: od.context.assets previously listed only the design assets (models, fonts, imagery, bgm) and omitted the Next runtime chunks under assets/_next/. Applying the plugin would stage an incomplete copy of the static export, missing the runtime files example.html needs to render. Now lists every file the entry requires (example.html + all 54 files under assets/, including the _next chunks + css). Also cleaned up the truncated od.useCase.query text into a complete sentence. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(examples): complete the open-design homepage asset closure Addresses the review's local-asset-closure blocker (three dangling references the mirror never captured): - Restore the Next polyfills chunk assets/_next/.../a6dad97d9634a72d.js (referenced by the noModule fallback script; modern browsers skip it, so it never 404'd at runtime but left the closure incomplete). - The work grid's passcode-protected /2026 tile pointed at work/tt01|tt02 images that were never in any mirror source (they 404 on the live site too). Add lockscreen placeholder images in the site palette so the reference resolves and — importantly — the client work data still matches the server-rendered HTML (removing the tile instead reintroduced a React #418 text hydration mismatch). - od.context.assets now lists the full closure (58 entries). Also corrected the manifest/SKILL 'zero external dependencies' wording: the Draco decoder is fetched from Google's gstatic CDN on demand (same pattern as the other WebGL examples); everything else is bundled locally. Re-verified from a preview subpath: 3 Draco models + fonts + imagery load, zero local 404s, zero console errors, no #418. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * fix(examples): remove third-party network calls + committed key from homepage bundle Addresses review: the bundled preview shipped two undisclosed external requests, contradicting the manifest/SKILL 'only Draco loads externally'. - useWeather() made a live QWeather request with a hardcoded API key (devapi.qweather.com/...&key=...) to append a live temperature to the clock HUD. Replaced the fetch with static data ({now:{temp:"19"}}) — no network call, key removed from the bundle. The HUD still shows a temperature (static, as befits a mirror showcase). - Removed the Vercel toolbar injection IIFE (vercel.live/_next-live/ feedback/feedback.js + a data-deployment-id). It was cookie-gated to Vercel team members so it never fired in preview, but it left a committed external endpoint in a first-party plugin. Re-verified from a preview subpath: the ONLY external request is now the gstatic Draco decoder (as the manifest states); 3 models + fonts + imagery load, zero local 404s, zero console errors, no #418. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Tuola Ge <gexingli@refly.ai> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
28d8158994 |
fix(web): lower AMR low-balance warning threshold (#5349)
* fix(web): lower AMR low-balance warning threshold * fix(web): tailor low-balance CTA for free AMR users * fix(web): tighten AMR low-balance dialog * fix(web): add close buttons to AMR balance dialogs * fix(web): simplify model quality tags * fix(web): adjust model cost labels * chore: trigger PR CI --------- Co-authored-by: bone3deep1962-collab <bone3deep1962@gmail.com> |
||
|
|
d5aeab77fc |
fix(ci): push the release-cut baked manifest as a ruleset bypass bot (#5358)
* fix(ci): push the release-cut baked manifest as a ruleset bypass bot bake-plugin-previews-release.yml renders every preview on a release/** cut and pushes the authoritative manifest straight onto the release branch. But release/** is guarded by the 'Protected branches (preview/*, release/v*)' ruleset whose pull_request rule rejects direct pushes — and the workflow pushed with PREVIEW_BAKE_TOKEN||github.token, i.e. github-actions[bot], which is NOT a bypass actor. So the writeback failed with 'GH013: Repository rule violations found for refs/heads/release/vX.Y.Z' (observed on release/v0.14.2), and the release branch's bundled data/plugin-previews/manifest.json was never refreshed — the packaged app then falls back to rendering each preview as a live iframe (pathological for the WebGL example templates). Mint an open-design-bot token (app id 3640364 — already a bypass actor on that ruleset, same App the metrics/blog/contributors workflows use) after the render and push with it via an explicit token URL. persist-credentials: false on checkout so the leftover github.token extraheader can't override the inline bot token on push (same failure mode fixed in #5357). No ruleset change required — the bypass actor already exists; the workflow just wasn't using it. * test(ci): lock the release-cut bake's ruleset-bypass push contract Per .github/AGENTS.md, cross-workflow auth changes must update topology coverage in e2e/tests/packaged-smoke-workflow.test.ts rather than rely on YAML review. Add a companion assertion (alongside the existing bake-plugin-previews.yml one) that locks bake-plugin-previews-release.yml's three co-dependent auth invariants: persist-credentials: false on checkout, the open-design-bot token mint (BOT_APP_CLIENT_ID/BOT_APP_PRIVATE_KEY — the App that is a ruleset bypass actor, not RELEASE_BOT_APP_ID), and the explicit x-access-token:${BOT_TOKEN} push. A refactor dropping any one silently reintroduces the GH013 rejection that stranded release/v0.14.2's manifest. |
||
|
|
b3356807b6 |
fix(amr): improve unavailable model gating (#5335)
* fix(amr): honor full model list metadata * fix(amr): classify tier entitlement failures * fix(amr): refine unavailable model upgrade affordance * fix(web): restore stale AMR model fallback Generated-By: looper 0.10.1 (runner=fixer, agent=codex) * fix(web): surface AMR tier upgrade failures Generated-By: looper 0.10.1 (runner=fixer, agent=codex) * fix(amr): mirror chat upgrade attribution Generated-By: looper 0.10.1 (runner=fixer, agent=codex) * fix(amr): skip disabled default models Generated-By: looper 0.10.1 (runner=fixer, agent=codex) * fix(amr): honor catalog default model * chore(pack): bump vela cli to 0.0.20 * fix(amr): align locked catalog fallback state * chore(nix): refresh pnpm deps hash * fix(amr): refresh entitlement-aware model state * fix(amr): refresh models after upgrade return * fix(amr): preserve default model analytics * fix(amr): tighten model gating follow-ups Generated-By: looper 0.10.1 (runner=fixer, agent=codex) * fix(amr): repair default and refresh follow-ups Generated-By: looper 0.10.1 (runner=fixer, agent=codex) * fix(amr): resolve explicit default before spawn Generated-By: looper 0.10.1 (runner=fixer, agent=codex) * fix(amr): concretize default model in smoke paths Generated-By: looper 0.10.1 (runner=fixer, agent=codex) * fix(amr): repair default model gating Generated-By: looper 0.10.1 (runner=fixer, agent=codex) * fix(amr): share connection-test model cache Generated-By: looper 0.10.1 (runner=fixer, agent=codex) --------- Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com> |
||
|
|
39c8eea0c5 |
test: cover recent daemon and e2e follow-ups (#5354)
* test(e2e): cover recent main follow-ups * test: cover recent main automation gaps |
||
|
|
6cf1221cde |
fix(ci): push the rolling plugin-preview manifest as the release-bot App (#5357)
The post-merge bake force-pushes the chore/plugin-previews branch with the release-bot App token so the rolling PR's sync is attributed to app/open-design-release-bot and its CI runs automatically — bake-plugin-previews-automerge.yml then approves + enqueues it. But the Checkout step persisted the default GITHUB_TOKEN as an http.https://github.com/.extraheader Authorization header, which overrides the inline App token on push, so the force-push authenticated as github-actions[bot]. A github-actions[bot]-attributed pull_request run is held as action_required (GitHub's bot-recursion gate, 0 jobs, no REST approve for non-fork runs), so the rolling PR's CI never ran without a manual 'Approve and run'. With no green CI the automerge reactor (gated on workflow_run.conclusion == 'success') stayed skipped and the manifest PR sat BLOCKED — main's baked-preview manifest went stale and freshly-baked clips (e.g. the WebGL example templates) never surfaced in the gallery. Set persist-credentials: false so the App token is the sole push credential and the rolling PR is attributed to the App again. Nothing between checkout and the push needs the persisted credential. |
||
|
|
35d2bb33b1 |
fix(daemon): stop design-system workspace project renames from reverting (#5134)
* fix(daemon): stop design-system workspace project renames from reverting For projects bound to a user design system (importedFrom: 'design-system'), ensureUserDesignSystemWorkspaceProject re-stamps the project name from the registry title every time the workspace is ensured. A rename applied through PATCH /api/projects/:id therefore appeared to succeed and then silently reverted on the next open of the design system. Keep the registry title as the master record, but write project renames through to it: the PATCH handler now propagates the new name to the design-system title (updateUserDesignSystem) before updating the project row, so the ensure sync agrees with the rename instead of undoing it. Non-workspace projects and non-user design systems are unaffected. Tested: new unit tests for the gating predicate and the write-through (metadata title + DESIGN.md heading updated, body preserved, blank and non-workspace inputs ignored); tsc --noEmit clean for src and tests. The pre-existing github-import.test.ts failure on native Windows (spawn EFTYPE) reproduces without this change. * fix(daemon): address review on design-system rename write-through Follow-up to the rename write-through, resolving two correctness issues raised in review on #5134. 1. Decide propagation from the post-patch project shape. The PATCH /api/projects/:id handler chose which design system to rename from the pre-patch row, so a request that renamed and rebound/detached in one call (e.g. { name, designSystemId: null }, or a switch to another design system) renamed the system the project was leaving and mutated a DESIGN.md/metadata the request was explicitly detaching from. The handler now derives the target from { ...existing, ...patch }, so only the design system the project remains bound to after the PATCH is renamed. 2. Stop swallowing a failed write-through. propagateWorkspaceProjectRename returned false both when propagation did not apply and when it failed, and the route ignored the result and still returned 200. A failed design-system write left the project row renamed while the next workspace ensure would revert it -- the original silent-revert bug as a partial update. The helper now returns a tri-state ('not-applicable' | 'propagated' | 'failed') and the route aborts with 409 CONFLICT on 'failed', leaving the project row unchanged. Tests: expands tests/design-systems/workspace-project-rename.test.ts to 12 cases covering the predicate, the tri-state helper, and the PATCH route end to end (rename survives the next ensure; detach and switch do not rename the old system; a failed propagation returns 409 with no partial update). |
||
|
|
fb4d48c609 |
chore(plugin-previews): refresh baked preview manifest (#5285)
Co-authored-by: open-design-bot <bot@open-design.ai> |
||
|
|
87a6a1a71a |
feat(desktop): observe abnormal exits (runtime 闪退) + child-process crashes (#5333)
* feat(desktop): observe abnormal exits (runtime 闪退) + child-process crashes The crash telemetry only covered renderer crashes (desktop_renderer_crash) and startup failures (packaged_runtime_failed). The most common "闪退" a user reports — the app was running fine, then the whole window vanished — is a MAIN-process crash / OS kill / force-quit after a hang, and none of it reaches analytics: a dead process can't report itself, and uncaughtException/unresponsive were log-only. Add abnormal-exit detection via a session marker: beginDesktopSession stamps a `clean: false` marker at launch; a graceful shutdown flips it clean. If the next launch finds the previous marker still dirty, the previous run died without a graceful quit — emit `desktop_unclean_exit` (previous version/session/started-at) once the daemon is up to relay it. This catches the whole class regardless of cause. Also emit `desktop_child_process_crash` on `child-process-gone` (GPU / utility crashes — a common cause of a window that then goes blank). Both events ride the existing free-form POST /api/observability/event (no contract change), same as desktop_renderer_crash. The lifecycle logic is a pure, unit-tested module; all fs/telemetry is best-effort and can never block startup or shutdown. * fix(desktop): only report unclean exit for runs that reached running Review (nettee): stamping the dirty marker at startup meant a bootstrap failure AFTER beginDesktopSession but before the app was up would leave the marker dirty and be reported as desktop_unclean_exit next launch — but that's a startup failure (already covered by packaged_runtime_failed), not a 'while running' crash. Add a reachedRunning flag: the marker is written not-yet-running at startup, markDesktopSessionRunning flips it true only once the runtime is up (after onDesktopReady), and a previous run is reported ONLY when it reached running and never went clean. This also makes each previous state reported at most once (begin overwrites the marker). * fix(desktop): mark session clean at end of shutdown, not start Review (nettee): endDesktopSessionCleanly ran at the very start of shutdown(), before the awaited teardown (beforeShutdown / IPC close / desktop.close / app.quit). A quit that then hangs in one of those steps and is force-quit or OS-killed would already have flipped the marker clean, hiding that abnormal exit. Move the clean-mark to just before app.quit(), after teardown completes. * fix(desktop): mark session running on actual reveal, not runtime creation Review (nettee): createDesktopRuntime() starts async bootstrap with void tick() and returns before the first window load/reveal, so calling markDesktopSessionRunning right after it fired too early — a crash during the first load would be mislabeled a runtime exit. Add an onRevealed callback fired from revealMainWindow (the real 'app is up' moment: web app mounted + window shown) and mark running from there instead. * fix(desktop): persist unclean-exit until its report is acked Review (nettee): beginDesktopSession overwrote the marker (dropping the crash from disk) while the crash was only held in memory and reported via a fire-and-forget POST that swallows failures — so a report that fails (daemon not reachable yet, transient network) lost the signal permanently. Carry the crash forward in the marker's unreportedCrash field; report returns whether it was acked; clearReportedCrash removes it only on success, so a failed report retries next launch. markRunning / clean preserve the carried-forward crash. * fix(desktop): queue unreported crashes so consecutive failures lose none Review (nettee): with a single carried-forward slot, a launch that is only retrying crash A and then itself crashes replaced A with B on the next launch, losing A. Carry a bounded queue (unreportedCrashes[], cap 20, oldest dropped) instead: begin appends the previous run's crash to the carried list, each is reported and cleared independently by sessionId, so a consecutive crash+failed- report sequence keeps every crash. markRunning/clean preserve the queue. |
||
|
|
0ba89a9672 |
docs(i18n): sync zh-TW README with English parity (#5099)
Bring docs/i18n/README.zh-TW.md back in line with README.md: - Add the hosted `curl ... install.sh | sh` install path plus the install.sh wrapper note (#4866). - Add the WSL2 note in the coding-agent install section and the WSL2 / wsl-setup.md pointer in the run-from-source line. - Add the macOS Docker Desktop bridge-networking note. - Add utm_source / utm_medium / utm_content attribution to the six open-design.ai links (#4792). Headings and structure unchanged; all referenced docs (wsl-setup.md, deploy/README.md#docker-desktop-on-macos, windows-troubleshooting.md) exist. |
||
|
|
3b681b4efc |
i18n: polish Russian localization (#5136)
* i18n: polish Russian localization * i18n: keep plugin integrity label verbatim * i18n: sync Russian docs with current README --------- Co-authored-by: DrMaks22 <191687254+DrMaks22@users.noreply.github.com> |
||
|
|
96ec1bedb3 |
feat(i18n): add Thai contributing translation (#4295)
* feat(i18n): add Thai README translation * feat(i18n): add Thai quickstart translation * feat(i18n): add Thai contributing translation * fix(i18n): update broken link in Thai contributing guide The parser reference pointed to apps/daemon/src/claude-stream.ts which no longer exists. Update to apps/daemon/src/runtimes/claude-stream.ts |
||
|
|
3649f81cb7 | feat(i18n): add Thai README translation (#4293) | ||
|
|
7193e48618 | docs: fix claude stream parser links (#5232) | ||
|
|
461e24d20c |
feat(web): surface actionable run-failure guidance for common errors (#5308)
The daemon already classifies run failures into failure_category / user_action (run-failure-classification.ts), but the chat error card only mapped ~7 codes to clear copy — everything else fell back to the generic "Task failed" title plus the raw upstream string, so users hitting common failures could not tell the cause or the fix (#895). Extend resolveRunFailureUi to name the failure type and give a one-line fix for eight more high-frequency, clear-root-cause codes, with the raw error preserved in the card's collapsible source area: - AGENT_UNAVAILABLE (CLI not installed) - AGENT_PROMPT_TOO_LARGE (shorten input / new conversation) - AMR_MODEL_UNAVAILABLE (switch model) - TOOL_LOOP_DETECTED (check target, then retry) - ROLE_MARKER_HALLUCINATION (retry recovers) - AGENT_RUNTIME_DEF_INVALID (update / contact support) - RATE_LIMITED (non-antigravity: wait/switch, keeps AMR promotion) - UPSTREAM_UNAVAILABLE (transient, keeps AMR promotion) The six agent-agnostic codes resolve before the amr/antigravity branches so a hosted run also gets the specific guidance. Adds 7 title keys + 8 message keys across all 18 locales and updates amr-guidance unit tests. Co-authored-by: xiaoche-hub <298951296+xiaoche-hub@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
49d64f0e8c |
fix(examples): resize WebGL canvases lazily in the render loop (#5327)
The powered-preview iframe can finish loading while its viewport is still 0x0 (hidden/animating dialog). Every WebGL example sized its canvas once at init plus on window resize, so when no resize event is delivered after the dialog settles, the drawing buffer stays 0x0 and the preview renders black until something (e.g. opening the plugin-info panel) changes the iframe width. Guard each resize handler to no-op when dimensions are unchanged and call it from the render loop, so the canvas self-heals within one frame of the iframe getting its real size — no reliance on resize events. Applies to all 15 webgl-* examples. Reported by QA: preview stays black until fullscreen/plugin-info is toggled. Co-authored-by: Tuola Ge <gexingli@refly.ai> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
59311ce3aa |
fix(daemon): classify byok opencode failures (#5309)
Co-authored-by: Siri-Ray <2667192167@qq.com> |
||
|
|
d41cdd9404 |
feat(release): post an immediate "branch cut" Feishu card on both release cuts (#5323)
Cutting a release branch (Tuesday minor via cut-release, Thursday patch via
cut-patch-release) pushes to release/**, which triggers notify-release-feishu to
build a full prerelease before it posts the download card — 20-40 minutes during
which the team has no signal the cut even happened.
Post an eager green "🌿 已切出 release 分支" card the moment the branch exists, from
the cut workflow itself, reusing feishu-notice.ts (node:crypto only, so the
existing setup-node suffices). The card names:
- the branch and whether it is a 大版本 (minor) or 小版本 (patch),
- the exact backport label (`backport release/vX.Y.Z`) so people know which
label to apply for backports,
- a note that the prerelease build is running and a download card will follow.
The download card from notify-release-feishu is unchanged; each cut now yields
an immediate ack card plus the eventual download card. The patch workflow's eager
card only fires on the happy (published) path, alongside the existing skip card.
Topology test asserts both workflows post the eager card after the branch push and
name the backport label.
|
||
|
|
dbc70663aa |
fix(release): let the Thursday patch cut survive an already-matching version (#5320)
The first scheduled cut-patch-release run (2026-07-09) failed at "Create branch + bump version + push" with "nothing to commit, working tree clean". The guard had passed (open-design-v0.14.0 published → happy path) and it computed patch v0.14.1, but main's apps/packaged/package.json was already 0.14.1: in steady state main leads stable by exactly one patch (finalize-release bumps main to X.Y.(Z+1) after X.Y.Z ships), and the Thursday patch equals that same X.Y.(Z+1). So `npm pkg set version=0.14.1` is an idempotent no-op, `git commit -am` finds an empty tree, and under `set -euo pipefail` the step dies before the push — no branch cut, no prerelease, no Feishu card. This collides every Thursday the version already matches, so it is structural, not a one-off. Commit the release marker with `--allow-empty` so the branch is always cut and pushed (which is what triggers notify-release-feishu); a real version bump still commits the change normally. Add a topology assertion that the release commit tolerates an empty tree. Verified locally: with a real `npm pkg set version=<same>` (idempotent, no diff) the old `git commit -am` fails exactly as CI did while `--allow-empty` succeeds with a marker commit; a differing version still commits the bump. |
||
|
|
08da51b3b4 |
test: expand daemon and UI e2e coverage (#5107)
* test: expand daemon and UI e2e coverage * fix: refresh retry assistant metadata Generated-By: looper 0.9.11 (runner=fixer, agent=codex) * fix: persist inherited run session mode Generated-By: looper 0.9.11 (runner=fixer, agent=codex) * fix: stabilize AMR inline auth retry Generated-By: looper 0.9.11 (runner=fixer, agent=codex) * fix: reconcile legacy chat assistant pins Generated-By: looper 0.9.11 (runner=fixer, agent=codex) * test: stabilize fake AMR auth recovery Generated-By: looper 0.9.11 (runner=fixer, agent=codex) * test: restore e2e interaction actionability Generated-By: looper 0.9.11 (runner=fixer, agent=codex) * test: address review feedback for PR 5107 * test: stabilize PR 5107 CI interactions * test: tighten PR 5107 review coverage * fix: inherit run session mode after conversation fallback * test: assert AMR promotion retry renders recovery Generated-By: looper 0.10.1 (runner=fixer, agent=codex) --------- Co-authored-by: Amy <1184569493@qq.com> |
||
|
|
7d005a17c3 |
feat(examples): add 10 WebGL example templates (#5312)
* feat(examples): add 10 WebGL example templates Adds 10 first-party powered-preview WebGL examples under plugins/_official/examples, each a self-contained dir (SKILL.md + example.html + open-design.json, self-contained i18n): C-tier — faithful Codrops ports (MIT + credit + LICENSE; original imagery): webgl-depth-gallery, webgl-pixel-reveal-gallery, webgl-horizontal-parallax, webgl-distortion-grain D-tier — original self-contained WebGL2 shader heroes (no third-party assets): webgl-caustic-pool, webgl-halftone-drift, webgl-holographic-foil, webgl-voronoi-cells, webgl-aurora-veil, webgl-liquid-iridescence All changed files are <1MB (pass the blob-size gate). The two large mirror showcases (oryzo, opendesign) are deferred: they exceed the 1MB blob guard and need their heavy assets moved to repository-assets R2 (per the gate's guidance). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(examples): stop shader setup after WebGL2 fallback message Wrap each pure-shader example script in an IIFE and return early when getContext('webgl2') is unavailable, so unsupported environments settle on the fallback message instead of throwing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Tuola Ge <gexingli@refly.ai> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
3b4cba7033 |
fix(web): keep plugin preview sidebar collapse handle visible (OPEND-418) (#5300)
Scrollable plugin-info content was painting over the sidebar seam handle, making the right panel impossible to collapse after interacting inside it. Co-authored-by: xiaoche-hub <298951296+xiaoche-hub@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
9abba14fc9 |
fix(chat): abort BYOK proxy upstreams when the client disconnects (#5182)
Co-authored-by: tomsen-ai <230283659+tomsen-ai@users.noreply.github.com> |
||
|
|
d513f9fd78 |
fix(web): float the draw composer near annotations (#5296)
Generated-By: looper 0.0.0-dev (runner=worker, agent=codex) |
||
|
|
e4e127d058 |
fix(desktop): recover from first-launch document-level 502 (#5186)
On initial packaged desktop launch the window could be covered by a raw 502 page near the end of the splash. Two root causes: 1. The od:// protocol handler proxied the top navigation to the web sidecar with a single fetch; a transient undici socket throw (setTypeOfService EINVAL, #895) on that one attempt became the 502 document the window rendered. 2. An HTTP 502 response with a body is a successful load to Electron (did-fail-load only fires for net::ERR_*), so the existing renderer- recovery loop never retried it and the splash revealed the raw 502 after its 15s ceiling. Fixes: - protocol.ts: retry idempotent (GET/HEAD) od:// fetches up to 3x with short backoff before surfacing the 502; resolved responses (incl. upstream 5xx) and non-idempotent methods keep single-attempt semantics. - runtime.ts: route 5xx main-frame documents from did-navigate into the markRendererFailed reload path, clear rendererFailed before awaiting loadURL (so a mid-load did-navigate flag survives), and re-poll a still- failed renderer at the prompt cadence. The window now self-heals without a manual reload. Red specs added first (protocol retry contract; 5xx-document recovery wiring), verified red on main and green on this branch. Co-authored-by: 박세원 <s-w.park@samsung.com> |
||
|
|
4b9072f01b |
feat(web): onboarding PR-C — first-loop completion funnel (+ stage dims) (#5160)
* feat(web): first-loop ledger + onboarding_completed funnel event Tracks the first-generation loop of a recommendation-started project (prompt_sent -> generated -> artifact_viewed -> delivered) in a session ledger keyed by the created project id, and fires onboarding_completed exactly once, when the loop closes with a delivery (share option picked / export succeeded) IN THAT SAME project, carrying every step reached in order (spec S8.3 loop, S11.1). Project-scoped so a delivery in an unrelated later project can never close another project's loop; the ledger is dropped on completion so post-completion activity can't re-open or grow it. Delivery taps live in FileViewer's share/export path (which knows the project id); the earlier steps ride the existing ProjectView funnel points. Also adds the optional studio_stage / feature_id dims to the studio hint events (spec S11.1) ahead of the stage-layer and S8.7 feature-hint work, which stay blocked on the in-flight NextStepActions branch. Rebased onto main after #5111 merged; adapted to main's project-keyed onboarding-entry guards and the split contracts analytics/events barrel. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(web): fire onboarding delivered only on export/share success Move the first-loop `delivered` tap from the share-option click to the export/share success callback in all three export flows (programmatic, template, image). Closing the onboarding loop now means the artifact was actually delivered, not merely that the user opened an export option. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: xiaoche-hub <298951296+xiaoche-hub@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
d4af1c5cca |
refactor(daemon): fold plugin-asset-cache/plugin-preview-bakes into plugins/ (#5195)
Move the two top-level plugin-* leaf modules into the plugins/ folder so plugin-owned modules live under one directory. Both files are pure leaf modules (node/undici imports only), so the moves are byte-identical renames; only the four import specifiers in server.ts and the two colocated tests are repointed. No public export surface change; no behavior change. Co-authored-by: LA <la@LAs-MacBook-Pro.lan> |
||
|
|
efe3228983 |
refactor(daemon): fold prompt-templates into media/ (#5196)
Move prompt-templates.ts under the media/ folder so the prompt-template reader lives with the other media modules it belongs with. It is a pure leaf module (no relative imports), so the move is a byte-identical rename; only the static-resource route importer and the colocated test are repointed. The server.ts references are content-directory string literals, not module imports, and are left untouched. No public export surface change; no behavior change. Co-authored-by: LA <la@LAs-MacBook-Pro.lan> |
||
|
|
c89e1cf344 |
fix(daemon): target the stalled child in watchdog forced-shutdown escalation (#5202)
* fix(daemon): target the stalled child in watchdog forced-shutdown escalation * test(daemon): restore OD_CHAT_RUN_INACTIVITY_KILL_GRACE_MS in env snapshot --------- Co-authored-by: tomsen-ai <230283659+tomsen-ai@users.noreply.github.com> |
||
|
|
b0e4f83e85 |
ci(release): Thursday patch-release cut, gated on the Tuesday minor shipping (#5294)
* ci(release): add Thursday patch-release cut gated on the Tuesday minor shipping cut-release cuts a minor every Tuesday (from main HEAD) and, via the release/** push, fires notify-release-feishu to build a prerelease and post a Feishu card. Add cut-patch-release: the same flow one weekday later (Thursday 09:00 Asia/Shanghai) with three deltas: - Bumps the PATCH of the highest release/vX.Y.Z branch (X.Y.(Z+1)), still cut from main HEAD like cut-release. - Gates on this week's minor having actually shipped: the line's minor base X.Y.0 must be a PUBLISHED stable GitHub Release (open-design-vX.Y.0, isDraft=false, isPrerelease=false — i.e. release-stable ran `gh release edit --draft=false --latest`, which rolls the tag back on failure). If it is not published, no branch is cut and nothing is built; instead a Feishu notice card announces the skip. - On the happy path it is otherwise identical to cut-release: create the branch, bump apps/packaged, App-token push (which triggers notify-release-feishu), and create the backport label. feishu-notice.ts is a lightweight sibling of feishu.ts for non-build events (here: the skip card). It reuses the same signed-webhook + retry transport and depends only on node:crypto, so it runs under the existing setup-node with no pnpm install. packaged-smoke-workflow.test.ts locks the guard: Thursday cron + patch bump, the published-stable predicate, and that every branch-cutting step is gated so the skip path posts the notice without cutting or building. * fix(release): gate the patch cut on the final version's minor, not the highest branch The guard target MINOR_BASE was derived from the highest release/v* branch before the manual workflow_dispatch `version=` override was applied. On the auto path V comes from that same branch, so it matched — but a manual `version=0.15.1` while the latest branch is release/v0.14.0 would check open-design-v0.14.0 (the wrong minor), letting the workflow cut release/v0.15.1 even when 0.15.0 has not shipped (and the inverse could wrongly block a valid manual patch on another line). Decide V first (validated override wins, else patch-bump the highest branch), then derive MINOR_BASE from V's own major/minor. Add topology assertions that MINOR_BASE comes from V and never from the highest branch, covering the manual dispatch path. |
||
|
|
5b39e86572 |
fix: preserve localized automation schedule summaries (#3591)
* fix: preserve localized automation schedule summaries * i18n schedule summary in automation modal * fix: pass nextRunAt to modal schedule summary * fix: prefer template title for automation modal display * fix: use localized template title in automation modal trigger * fix: make schedule timezone label formatting explicit * fix: restore template picker default name behavior * fix: stabilize automation modal template/title test paths * fix: align automation modal accessibility copy and i18n keys * fix(web): close remaining automation i18n thread gaps * fix: localize automation template kind labels * fix: align Chinese automation template filter label * test: cover localized automation schedule modal |
||
|
|
956b6607f5 |
feat(preview): add powered WebGL previews and examples (#5266)
* feat(powered-preview): implement cross-origin isolation for WebGL and Worker artifacts - Introduced a new powered preview feature that allows rendering HTML artifacts in a cross-origin-isolated iframe, enabling the use of Web Workers, WASM, and SharedArrayBuffer. - Added API endpoint `/api/preview/isolation` to report the daemon's directly-reachable HTTP origin for rendering. - Implemented logic in the file viewer to determine if an artifact requires powered preview based on its content. - Updated the file serving routes to support powered preview, ensuring proper headers are set for cross-origin isolation. - Enhanced the user interface to include new scenarios for WebGL and Worker visualizations in the home hero section, with corresponding translations and descriptions. This feature addresses the limitations of the opaque-origin preview sandbox, allowing for more complex GPU and compute applications to function correctly. * feat(preview): add WebGL templates and lazy HTML previews * test(web): stabilize project back routing assertion * fix(preview): address powered preview review blockers * fix(preview): isolate powered preview origin |
||
|
|
7b98646144 |
feat(media): wire MiniMax image-01 through the minimax provider slot (#4563)
* feat(media): register minimax-image-01 in catalog (web + daemon mirror) * test(media): add minimax-image T2I happy path test (red) * feat(media): implement renderMinimaxImage + dispatch branch (green) Wires up image generation for the existing 'minimax' provider slot via MiniMax's /v1/image_generation endpoint (image-01). T2I is supported by default; I2I fires when --image is supplied (subject_reference). The renderer follows the same wire-model precedence as nanobanana/openrouter: credentials.model -> ctx.wireModel -> MINIMAX_IMAGE_MODEL_MAP -> ctx.model. The renderer reports a separate base URL constant from the legacy TTS host because MiniMax's image endpoint lives on api.minimax.io while TTS still uses api.minimaxi.chat. Existing TTS users get image generation for free under the same provider card and stored media-config.json entry. * test(media): add minimax-image I2I test (subject_reference) Verifies --image is forwarded as subject_reference[0].image_file with the expected data URL prefix, that providerNote does NOT echo the data URL (no PII leak in metadata), and that the catalog id minimax-image-01 falls through to the wire model image-01 via MINIMAX_IMAGE_MODEL_MAP when no override is set. Also adjusts the wire-model precedence in renderMinimaxImage: now keys the catalog map off ctx.wireModel instead of ctx.model, so the vendor- prefixed catalog id resolves to MiniMax wire name while a user-set OD_MEDIA_MODEL_ALIASES alias passes through unchanged. * test(media): add minimax-image base_resp failure test Covers the MiniMax-specific envelope where HTTP 200 still carries a logical failure via base_resp.status_code. Renderer must throw with the status_msg before attempting to decode data.image_base64, and must NOT write any output file when the upstream rejected the request. * test(media): add minimax-image HTTP error surface test Covers the dispatcher contract that non-2xx responses are surfaced to the caller with status code and body excerpt. Mirrors the nanobanana upstream-errors test in shape (401 with text/plain body). * test(media): add minimax-image default base URL + wire model test Verifies the renderer hits api.minimax.io/v1/image_generation (NOT the legacy TTS host api.minimaxi.chat) and uses image-01 as the wire model when no provider config is supplied. Locks in the two image-specific defaults against accidental TTS-host leakage or future catalog drift. * feat(web): add minimax to image picker allowlist supportedModels() filters the catalog by supportedProviders[surface] — without adding minimax to the image set, the data-driven picker would filter out our new minimax-image-01 entry even though the catalog and renderer are wired. Audio already had minimax; this only affects the image surface. * docs(media): note minimax image in the integrated-providers head comment The head-of-file comment enumerates the real providers integrated today. Adding minimax (image-01 T2I+I2I on api.minimax.io, alongside the legacy TTS host api.minimaxi.chat) keeps the comment honest with the codebase. * fix(media): stop inheriting TTS baseUrl for MiniMax image requests The minimax provider slot is shared between TTS (api.minimaxi.chat/v1) and image (api.minimax.io). The earlier renderer naively appended /v1/image_generation to credentials.baseUrl, which produced https://api.minimaxi.chat/v1/v1/image_generation — a 404 against the TTS host — whenever a user had configured a baseUrl for their TTS setup. Fix: ignore credentials.baseUrl for image and pin the renderer to its own host via MINIMAX_IMAGE_DEFAULT_BASE_URL, with an optional OD_MINIMAX_IMAGE_BASE_URL env var for operator overrides (proxies, staging gateways). A per-surface baseUrl field on the provider schema would be a cleaner long-term fix; tracked as a follow-up. Adds a regression test that simulates the bug configuration (baseUrl=https://api.minimaxi.chat/v1) and asserts the renderer hits the image-specific host, plus a test for the env-var override path. * fix(media): address review feedback on renderMinimaxImage - harden ctx.imageRef access to .dataUrl-guard, matching the defensive pattern used by 6 other renderers (custom-image, volcengine-video, openrouter-image, leonardo, etc.). Without this, a non-null imageRef with no dataUrl would emit image_file: undefined to MiniMax and surface as a confusing 400 instead of being skipped cleanly. - extend minimaxImageAspectFor to cover MiniMax's full allowlist (1:1, 16:9, 4:3, 3:2, 2:3, 3:4, 9:16, 21:9). Previously 3:2, 2:3, and 21:9 were silently dropped, causing MiniMax to default to 1:1 for any agent or future UI that asked for a wider aspect. - rewrite the wire-model precedence comment to accurately describe the OR-chain semantics (credentials.model || map[ctx.wireModel] || ctx.wireModel) rather than the misleading 3-tier ladder. Also correct the MINIMAX_IMAGE_DEFAULT_BASE_URL header comment which described credentials.baseUrl as part of the resolution order even though the renderer deliberately ignores it. - add 3 new tests: missing-api-key error path (anchors the "configure it in Settings" message), OD_MEDIA_MODEL_ALIASES passthrough (anchors the alias-survives-map behavior so a future refactor that re-keys the map off ctx.model would fail here instead of silently breaking aliases), and 21:9 aspect forwarding (anchors the extended aspect ratio coverage). Validation: pnpm guard (60/60), daemon test media-minimax-image (10/10), daemon + web typecheck, daemon + web build, and verify-media-models all green. * fix(media): resolve minimax image test import to media/index.js The test imported ../src/media.js, but apps/daemon/src/media is a directory with index.ts and no media.ts, so the import failed to resolve under the daemon's NodeNext test typecheck before the MiniMax assertions could run. Point it at ../src/media/index.js to match the existing daemon media test. |
||
|
|
8cc4c5779f | fix(daemon): support kiro mcp install (#5118) | ||
|
|
91f22f301d |
refactor(daemon): split acp.ts + pi-rpc.ts into an agent-protocol/ capability-barrel (#5200)
* refactor(daemon): split acp.ts + pi-rpc.ts into agent-protocol/ capability-barrel Move acp.ts (1744) and pi-rpc.ts (684) into a single agent-protocol/ capability-barrel module: a core/ foundation kernel (shared JSON-line-stream transport) plus per-protocol concern subdirs (acp/, pi-rpc/), each with a barrel. The shared createJsonLineStream transport moves into core/, dissolving the former pi-rpc -> acp edge into a clean star topology (allowedEdges: []). Pure structural move: every function body byte-identical (verified via line-multiset, 1960==1960, 0 added/0 dropped); public surface reproduced exactly (10 names) through the root barrel. All importers repointed to ./agent-protocol/index.js (server.ts, connectionTest.ts, runtimes/defs/shared.ts + 5 test files). Guard registration deferred (check-barrel-imports infra not on main yet). * docs(daemon): @module docblocks, per-export JSDoc, and README for agent-protocol/ Documentation only — zero code lines changed (verified by line-multiset, 1960==1960). Every file gets a @module docblock, every exported symbol gets JSDoc, and README.md mirrors design-systems/README.md (What changed / Why this shape / Import conventions / Directory structure / Consumers). * fix(daemon): repoint verify-amr-real-vela script to the agent-protocol barrel The acp.ts -> agent-protocol/ split stops emitting dist/acp.js, so the real-vela smoke verifier's dynamic import of ../dist/acp.js would fail with ERR_MODULE_NOT_FOUND after a clean build. Repoint it to ../dist/agent-protocol/index.js, which re-exports attachAcpSession. Verified: daemon build emits dist/agent-protocol/index.js and the script's import resolves attachAcpSession as a function. --------- Co-authored-by: LA <la@LAs-MacBook-Pro.lan> |