Commit Graph
2734 Commits
Author SHA1 Message Date
Marc ChanandGitHub 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
2026-07-10 06:39:02 +00:00
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>
2026-07-10 06:15:46 +00:00
Yash YadavandGitHub 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
2026-07-10 06:14:58 +00:00
Jack ChenandGitHub 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.
2026-07-10 04:21:41 +00:00
AYDIN ADNANandGitHub 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.
2026-07-10 04:13:24 +00:00
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>
2026-07-10 03:58:41 +00:00
YOMXXXandGitHub 729525a884 fix(daemon): backfill Windows CLI cache env (#5306)
* fix(daemon): backfill Windows CLI cache env

* fix(daemon): keep Windows cache env sandboxed
2026-07-10 03:46:48 +00:00
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>
2026-07-10 03:46:01 +00:00
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>
2026-07-10 03:28:06 +00:00
57386e4ef1 fix(analytics): attribute byok opencode providers (#5356)
Co-authored-by: Siri-Ray <2667192167@qq.com>
2026-07-10 02:56:02 +00:00
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>
2026-07-10 02:53:07 +00:00
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>
2026-07-10 02:39:28 +00:00
lefarcenandGitHub 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.
2026-07-10 02:30:31 +00:00
81b20dc6a2 fix(web): avoid missing design-system preview assets (#5381)
Co-authored-by: lefarcen <935902669@qq.com>
2026-07-09 14:34:45 +00:00
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>
2026-07-09 14:05:04 +00:00
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>
2026-07-09 12:42:32 +00:00
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>
2026-07-09 11:46:11 +00:00
lefarcenandGitHub 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.
2026-07-09 11:10:06 +00:00
Marc ChanGitHubopen-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
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>
2026-07-09 10:37:26 +00:00
AmyandGitHub 39c8eea0c5 test: cover recent daemon and e2e follow-ups (#5354)
* test(e2e): cover recent main follow-ups

* test: cover recent main automation gaps
2026-07-09 10:18:59 +00:00
lefarcenandGitHub 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.
2026-07-09 09:56:03 +00:00
EthanandGitHub 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).
2026-07-09 09:47:30 +00:00
fb4d48c609 chore(plugin-previews): refresh baked preview manifest (#5285)
Co-authored-by: open-design-bot <bot@open-design.ai>
2026-07-09 09:36:35 +00:00
lefarcenandGitHub 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.
2026-07-09 07:55:13 +00:00
laihenyiandGitHub 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.
2026-07-09 07:43:53 +00:00
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>
2026-07-09 07:43:37 +00:00
tanangularandGitHub 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
2026-07-09 07:42:49 +00:00
tanangularandGitHub 3649f81cb7 feat(i18n): add Thai README translation (#4293) 2026-07-09 07:39:36 +00:00
DongYoon KimandGitHub 7193e48618 docs: fix claude stream parser links (#5232) 2026-07-09 07:36:37 +00:00
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>
2026-07-09 07:10:11 +00:00
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>
2026-07-09 06:52:57 +00:00
59311ce3aa fix(daemon): classify byok opencode failures (#5309)
Co-authored-by: Siri-Ray <2667192167@qq.com>
2026-07-09 04:40:18 +00:00
lefarcenandGitHub 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.
2026-07-09 04:01:52 +00:00
lefarcenandGitHub 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.
2026-07-09 03:22:52 +00:00
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>
2026-07-09 03:05:42 +00:00
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>
2026-07-08 17:29:38 +00:00
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>
2026-07-08 11:46:06 +00:00
9abba14fc9 fix(chat): abort BYOK proxy upstreams when the client disconnects (#5182)
Co-authored-by: tomsen-ai <230283659+tomsen-ai@users.noreply.github.com>
2026-07-08 11:35:11 +00:00
lefarcenandGitHub d513f9fd78 fix(web): float the draw composer near annotations (#5296)
Generated-By: looper 0.0.0-dev (runner=worker, agent=codex)
2026-07-08 10:59:58 +00:00
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>
2026-07-08 10:56:05 +00:00
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>
2026-07-08 10:48:52 +00:00
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>
2026-07-08 09:56:44 +00:00
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>
2026-07-08 09:28:55 +00:00
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>
2026-07-08 09:23:49 +00:00
lefarcenandGitHub 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.
2026-07-08 09:22:47 +00:00
mehmet turacandGitHub 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
2026-07-08 09:01:37 +00:00
Tom HuangandGitHub 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
2026-07-08 08:58:51 +00:00
RyanandGitHub 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.
2026-07-08 08:16:23 +00:00
YOMXXXandGitHub 8cc4c5779f fix(daemon): support kiro mcp install (#5118) 2026-07-08 07:54:23 +00:00
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>
2026-07-08 07:51:12 +00:00