Commit Graph
2791 Commits
Author SHA1 Message Date
3e09ebf282 feat(release): publish localized changelog metadata (#5520)
* feat(release): publish changelog metadata

* fix(release): verify release notes from stored bytes

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

---------

Co-authored-by: Looper <looper@noreply.github.com>
2026-07-13 08:21:31 +00:00
Chanhyo JungandGitHub 7e302031d3 fix(web): use current logo on home surfaces (#5396)
Fixes #5365

Verified with focused RED/GREEN regression, web typecheck, pnpm guard, root typecheck, visual DOM readback, and independent Codex diff review.
2026-07-13 07:59:27 +00:00
Dion KobiandGitHub 4b4c7f4028 Improve empty agent output guidance (#4241)
* Improve empty agent output guidance

* Preserve updated empty-output guidance
2026-07-13 07:59:24 +00:00
Nicholas-XiongandGitHub 5127ad188b fix: add Kiro to agentLabels.ts (AGENT_LABELS + AGENT_ALIASES) (#5085)
* fix: add Kiro to agentLabels.ts (AGENT_LABELS + AGENT_ALIASES)

- Add kiro to AGENT_LABELS so exactAgentDisplayName('kiro') returns 'Kiro'
- Add 'kiro cli' and 'kiro-cli' aliases to AGENT_ALIASES
- Kiro icon (kiro.svg) already exists, this was purely a missing label mapping

Closes #5081

* test: pin Kiro label and alias resolution in agentLabels utility

PerishCode's review on PR #5085: mapping change is merge-safe but the
new behavior should be pinned in the existing utility test matrix.

Added 4 tests covering:
- agentDisplayName resolves 'kiro cli' → 'Kiro' (alias)
- agentDisplayName resolves 'kiro-cli' → 'Kiro' (alias)
- exactAgentDisplayName returns 'Kiro' for exact id 'kiro' (canonical label)
- exactAgentDisplayName returns null for 'kiro cli'/'kiro-cli' (aliases are
  not themselves agents — substring aliases stay routed via agentDisplayName
  and do not pollute the exact-id table)

Locks the fix from db025410 and prevents regression.

* test: update kiro alias assertions to match exactAgentDisplayName behavior
2026-07-13 07:59:21 +00:00
2a6aaeb3d2 refactor(daemon): group browser bridges into browser/ (#5192)
Co-authored-by: LA <la@LAs-MacBook-Pro.lan>
2026-07-13 07:59:19 +00:00
80f94faa04 fix(daemon): classify structured Vela ACP auth-required details as AMR_AUTH_REQUIRED (#5158)
classifyAmrAccountFailureDetails only mapped the insufficient_balance /
recharge half of the accountAction union. When vela reports an auth
failure through ACP structured details while the protocol message is
generic, the run fell into the opaque AGENT_EXECUTION_FAILED bucket
instead of the AMR_AUTH_REQUIRED card with its relogin affordance.

Add the symmetric auth_required / relogin branch, mirroring the
structured balance branch that #5053 introduced.

Co-authored-by: tomsen-ai <230283659+tomsen-ai@users.noreply.github.com>
2026-07-13 07:59:16 +00:00
lefarcenandGitHub aa2d631473 fix(daemon): classify antigravity silent-exit connection tests instead of "exit 0" (#5431)
The Antigravity CLI (`agy`) is silent on stdout/stderr in print mode for
both missing-auth and quota-exhausted failures: it exits 0 with no
assistant text and only records the real upstream error in its
`--log-file`. The connection-test path never handed `agy` a `--log-file`
and never inspected one, so every such failure collapsed into
`kind: 'unknown'` with `detail: 'exit 0'`, which Settings renders as the
opaque "Test failed: exit 0" the user reported — with no hint that they
simply need to sign in.

Mirror the chat-run empty-output guard already in server.ts: give the
smoke test a temp `--log-file` under its throwaway cwd, and when `agy`
produces no visible text, fold the log tail into the classifier input and
route to the specific result — `agent_auth_required` (with the actionable
"open a terminal and run `agy` to sign in" guidance) or `rate_limited`
(quota) — falling back to `agent_auth_required` on a clean silent exit,
since that empirically means missing OAuth. A non-clean crash still
reports as `agent_spawn_failed` with its exit code.

Both the onboarding and Settings test surfaces already render these kinds,
so the fix reaches the UI (and the `od` CLI, which shares the endpoint)
with no contract or i18n change.

Generated-By: looper 0.0.0-dev (runner=worker, agent=claude-code)
2026-07-13 07:59:13 +00:00
SivanColaandGitHub 91d737d10e docs: add Reasonix to localized platform tables (#5477) 2026-07-13 07:59:11 +00:00
YOMXXXandGitHub e522062121 fix(web): isolate BYOK messages endpoint models (#5494) 2026-07-13 07:43:06 +00:00
f841cdf3fe Add Osaka meetup recap and AI PPT tutorial (#5439)
Co-authored-by: koki yanlai xu <koki@kokideMacBook-Air.local>
2026-07-13 06:37:41 +00:00
Marc ChanandGitHub 6c457a6436 fix(daemon): preserve data dir for media wrapper env (#5459)
* fix(daemon): preserve data dir for media wrapper env

Generated-By: looper 0.10.2 (runner=worker, agent=codex)

* fix(daemon): derive AMR home from daemon data dir

Generated-By: looper 0.10.2 (runner=fixer, agent=codex)
2026-07-13 06:25:25 +00:00
AmyandGitHub 8325aa3d21 test: expand project context daemon coverage (#5516)
* test: cover project context daemon flows

* test: remove private daemon import from e2e

Generated-By: looper 0.10.1 (runner=fixer, agent=codex)
2026-07-13 06:23:49 +00:00
YOMXXXandGitHub c6241ecad2 fix(daemon): honor BYOK media defaults in dispatch hint (#5440) 2026-07-13 06:23:18 +00:00
lefarcenandGitHub d4762a9522 fix(desktop): break renderer crash-reload loops with a circuit breaker (#5322)
* fix(desktop): break renderer crash-reload loops with a circuit breaker

A renderer that crashes deterministically — a GPU/V8 CHECK failure, a
corrupt profile that aborts on every load — is reloaded forever by the
desktop poll loop: load → crash → reload → crash. One 0.14.0 device did
exactly this, emitting 26,011 desktop_renderer_crash events in a single
day (96% of all renderer crashes on the release) while its window stayed
blank the whole time.

Add RendererCrashLoopBreaker (pure, unit-tested): after 5 crashes inside
a 60s rolling window it opens. The poll loop then parks on a static,
dependency-free error screen that explains recovery is automatic, rather
than cycling the crash; markRendererFailed no-ops while open. Per-crash
telemetry is suppressed once open — the tripping crash is still reported,
flagged loop_tripped — so one wedged device can't emit tens of thousands
of identical events. The breaker re-arms after a 5-minute quiet cooldown
so a transient fault still self-heals without a reinstall.

* feat(desktop): make crash-loop recovery active + observable

Review feedback: the breaker only "waited" — its cooldown re-arm reloaded
the same URL from the same cache, and the whole flow was near-invisible.

On re-arm it now actively recovers: clears the HTTP + V8 code caches
(non-destructive — no cookies or storage) before the retry reload,
targeting the most common recoverable cause (a corrupt cached bundle or
shader) instead of hoping conditions changed on their own.

And it's now observable: a console.warn when the breaker opens (parking),
a console.info per recovery attempt, and a bounded recovery-attempt
analytics signal (reason:"recovery-attempt", recovery_attempt: N, at most
one per cooldown). Triage can tell a chronic looper (index keeps
climbing) from a device that recovered (events simply stop) without
re-introducing the per-crash spam the breaker was built to kill.

* revert(desktop): drop active cache-clear recovery, keep it passive + observable

Reviewer worry: clearing caches on every cooldown could amplify churn on a
wedged device without helping the dominant crash cause (a GPU/V8 CHECK,
exit 0x80000003), i.e. pour oil on the fire. Back the breaker off to
"stop the loop + passive retry": on re-arm it reloads without mutating any
device state and lets a transient fault clear on its own.

The observability added alongside stays: a warn on breaker-open, an info
per recovery attempt, and the bounded recovery-attempt analytics signal
(recovery_attempt: N) so chronic loopers are still visible.

* feat(desktop): add report + save-logs actions to the renderer crash screen

The recovery screen was informational only. On a renderer crash the daemon
is still alive, so give the wedged user an actionable exit — reusing IPC the
preload already exposes, no new surface:

- "Report a problem" → window.__od__.openExternal() opens a prefilled GitHub
  new-issue whose title + body carry the app version, OS, and the renderer
  exit code (e.g. 0x80000003) a triager needs, and ask the user to attach
  the logs.
- "Save logs…" → window.openDesignDesktop.exportDiagnostics() runs the
  existing diagnostics-bundle export (save dialog → zip).

Neither an issue URL nor mailto can carry a file attachment, so the flow is
save-then-attach. Each button hides itself if its bridge is missing (preload
failed to load) to avoid a dead control.

* feat(desktop): add "Email us" (support@open-design.ai) to the crash screen

Alongside "Report a problem" / "Save logs…", the recovery screen now offers a
prefilled mailto to our support address for users who'd rather email than open
a GitHub account. It reuses the existing openExternal bridge; the
shell:open-external handler now also allows a mailto addressed strictly to the
support address (isSupportMailtoUrl, unit-tested) — no other scheme or address
opens, so widening past http can't be abused into arbitrary-scheme launches.
The link hides itself if the bridge is absent, like the other actions.

* style(desktop): match crash screen to the app's monochrome brand palette

The crash screen used a stray blue accent that didn't match the app. Repaint
it with the neutral design tokens from apps/web (tokens.css): warm off-white
#faf9f7 + near-black #1a1916, a monochrome near-black primary button, and a
plain underlined (not blue) email link. Dark mode inverts the pair to a light
button on the dark bg (a near-black button would vanish), mirroring the
black/white onboarding look.

* fix(desktop): address review — tighten support mailto + reveal crash screen

Two Looper findings on the crash-screen actions:

- Security: isSupportMailtoUrl validated only protocol + pathname, so
  `mailto:support@open-design.ai?bcc=attacker@example.com` (or ?to=/?cc=)
  still passed and shell.openExternal would launch the mail client with
  extra recipients/headers through the renderer-exposed bridge. Now reject
  any query key other than the crash screen's own subject/body.

- Startup lifecycle: parking on the crash screen left `revealed` false and
  the splash open when the loop tripped before revealWhenReady(), so the
  runtime's show() kept focusing the splash — a user re-focusing during a
  startup crash loop was sent back to the boot splash. Route the park
  through revealMainWindow() so the recovery screen becomes the revealed,
  focused window and the splash is torn down (no-op after normal reveal).

* feat(diagnostics): make the support bundle root-cause native renderer crashes

Audit of "Save logs…" for the crash-recovery screen: on a renderer crash the
bundle collected text logs but NOT the one artifact that can explain an opaque
native abort like 0x80000003 (a V8/Chromium CHECK) — a minidump. crashReporter
was never started (no dumps existed anywhere), the crash-report collector was
macOS-only, and there was no GPU info (the usual suspect for such crashes).

Close the gap end to end:
- apps/desktop: start crashReporter (uploadToServer:false — on-device only) and
  relocate Electron's crashDumps into <logs/desktop>/crashes, inside the log
  tree the export already bundles; snapshot GPU + feature-status +
  runtime info to <logs/desktop>/gpu-info.json at startup.
- packages/diagnostics: add a `binary` LogSource kind (copied verbatim, no text
  decode/redaction so a minidump isn't corrupted) and findCrashDumps() to
  collect .dmp files; buildDiagnosticsZip gains a `crashDumps` input.
- apps/daemon: bundle logs/desktop/gpu-info.json and the relocated crash
  minidumps (cross-platform, capped, within 14 days).

The renderer crash context (reason/exit code/url) is already captured — the
main-process console.error lands in logs/desktop/latest.log — so a bundle now
carries stack (minidump) + GPU state + context: enough to root-cause.

* fix(desktop): mkdir crashDumps dir before setPath so first-crash dumps land in the log tree

Review (PerishCode): app.setPath('crashDumps', dir) throws when the dir does
not exist; on a fresh runtime logs/desktop/crashes won't exist yet, so setPath
threw, we fell back to Electron's default crashDumps location, and the daemon
export (which only scans <logs/desktop>/crashes) missed the minidump for
exactly the first crash loop this is meant to root-cause. mkdirSync the dir
(recursive) before setPath.

* fix(desktop): reject CR/LF in the support mailto subject/body

Review (PerishCode): the key allowlist rejected to/cc/bcc keys, but a decoded
CRLF in an allowed value (subject=ok%0D%0ABcc:attacker@…) could still inject a
mail header once the mailto reaches the mail client. Reject any subject/body
value containing CR or LF.
2026-07-13 04:58:16 +00:00
lefarcenandGitHub fc5b276f7d feat(ci): surface merge-queue needs-validation ejection as a PR comment (#5519)
A PR ejected from the merge queue by the needs-validation gate leaves no
trace on the PR itself: the gate fails on the queue's transient ref, the
PR's own checks stay green, and mergeQueueEntry silently goes null. The
only way to learn why the PR fell out of the queue is to dig through
merge_group workflow runs, which nobody watches (observed on #5329).

Producer: the merge_group gate in ci.yml now emits a handoff/comment
artifact targeting the labeled PR right before it blocks, and uploads it
on the failure path. Consumer: comment.atom.yml accepts merge_group
workflow_run events; since a merge_group run's head_sha is the queue's
synthetic merge commit, artifacts from those runs are bound to their
producing run by run_id, and the base-freshness skip is waived (PRs
ahead in the queue move the base while the run completes). The live-PR
head check still guards staleness.

Topology coverage pins the new boundary: comment.atom consumes
pull_request + merge_group; autofix/report stay pull_request-only.
2026-07-13 04:56:12 +00:00
88b411efd7 fix(daemon): reap the failed attempt's process group on same-run retry (#5463)
* fix(daemon): reap the failed attempt's process group on same-run retry

A same-run retry tore down the failing attempt with run.child.kill('SIGTERM') —
a signal to the direct child PID only. Killing a parent does not kill its
children on POSIX, so any descendants the CLI spawned (MCP servers, tool
subprocesses, internal runners) were orphaned (re-parented to PID 1) and kept
running. One process group leaked per retry, accumulating over the daemon's
lifetime. The cancel path already disbands the whole group via
process.kill(-pgid); the daemon even spawns detached and captures
run.processGroupId, but the retry teardown never used it.

Add signalProcessGroup(pgid, signal) — a group signal that works even after the
direct child object has exited (grandchildren outlive it), kept SEPARATE from
signalChildProcess so the shared cancel/escalation path keeps its childHasExited
guard against the cross-generation kill fixed in #5202. reapProcessGroup(pgid)
does SIGTERM then a SIGKILL escalation, both bound to the CAPTURED pgid.

tearDownAttemptForRetry now snapshots the attempt's child + pgid, reaps its
group by the captured pgid (falling back to a direct-child signal on win32 / no
pgid), and clears run.processGroupId so the next attempt captures cleanly. The
escalation targets the captured pgid, never the shared field the next attempt
overwrites — so it cannot hit the healthy new attempt's group (#5202).

Fixes #5462.

Regression: tests/retry-orphan-process-group.test.ts drives a full run over the
HTTP API with a fake CLI that spawns a long-lived grandchild then stalls into a
retryable timeout; it asserts the grandchild is dead after the run finishes. Red
on origin/main (grandchild survives, PPID=1), green with this fix. Verified live
on a real daemon: a 20x retry stress leaked 20/20 grandchildren before the fix
and 0/20 after, with every run still completing 'succeeded'.

* fix(daemon): reap the process group on cancel/shutdown when the child already exited

Secondary case from #5462: when the parent CLI has already exited but a
grandchild still holds the inherited stdio pipe (run still running), cancel
returned 200 and finished the run canceled, but the grandchild survived — the
same childHasExited short-circuit in signalChildProcess that the retry teardown
hit. killChild (used only by the terminating cancel / shutdownActive paths, never
by a re-spawning retry) now falls back to signalProcessGroup by pgid when the
direct child has already exited, so cancel/shutdown reap surviving descendants
too. No cross-generation risk: both callers terminate and never re-spawn into
the pgid (cf. #5202).

Covered by the existing cancel/shutdown/lifecycle suites staying green (no
regression); the retry-orphan regression exercises the shared signalProcessGroup
helper end-to-end. A dedicated cancel-after-parent-exit HTTP regression can be
added if preferred — the forensics harness confirms this case manually.

---------

Co-authored-by: tomsen-ai <230283659+tomsen-ai@users.noreply.github.com>
2026-07-13 04:25:49 +00:00
lefarcenandGitHub bc5b6f0588 fix(daemon): flag runs that end with unfinished work so status never reads "Completed" early (#5470)
A design-generation run that ended its turn with TodoWrite tasks still
pending / in_progress / stopped — or was truncated by max_tokens — was
stamped `succeeded` and read "Completed" on the Pet task center and the
project pill, while the chat footer simultaneously read "Stopped with
unfinished work". Users trusted "Completed", assumed the work was done, and
re-requested, creating duplicate generations and eroding trust in the
run-state UI (#1247 / #1060).

Root cause: the daemon equates "the turn/process ended" with "succeeded" and
never consults the agent's own declared work; multiple web surfaces then map
`succeeded` -> "Completed" without any completeness signal, even though the
chat footer already distinguishes unfinished work.

Fix — one canonical signal, consumed everywhere so the surfaces cannot drift:

- packages/contracts/api/run-completeness.ts defines the canonical
  "unfinished declared work" predicate (status !== 'completed', so pending /
  in_progress / stopped all count), matching the web footer's
  unfinishedTodosFromEvents exactly.
- Daemon folds the last TodoWrite snapshot + a max_tokens truncation off the
  agent event stream (every runtime, via the single emitAgentEvent choke
  point) and derives run.endedWithUnfinishedWork once at finish().
- The project-status projection derives `incomplete` from the already
  persisted events_json — no schema migration — so it survives reload/reattach
  instead of flashing "Completed" once the in-memory run ages out.
- Contracts carry endedWithUnfinishedWork on ChatRunStatusResponse and the SSE
  end payload (backward compatible: absent = finished), plus a new
  `incomplete` ProjectDisplayStatus.
- Web surfaces (Pet task center, project pill, DesignsTab) render `incomplete`
  as amber needs-attention, never the success color; the footer now sources
  the same shared predicate.

Truncation uses the display-only option (spec Option A): the run stays
`succeeded` but is flagged incomplete, avoiding the code===0 / stdin-close
hazards of reclassifying a truncated turn to failed.

Regression coverage, red on main / green here: an HTTP-boundary e2e
(e2e/tests/dialog/task-completeness.test.ts) drives the interactive chat path
for unfinished / stopped / truncated / finished runs; daemon + contracts +
web unit tests pin the projection and the canonical predicate.

Ref: https://plane.powerformer.net/open-design/projects/49832a02-3158-4faf-bf2f-d0e39c40c7e6/issues/011f6f51-7a25-4043-ae94-182e0b8b0931

Generated-By: looper 0.0.0-dev (runner=worker, agent=claude-code)
2026-07-13 04:17:24 +00:00
lefarcenandGitHub 4bbb9825f2 fix(daemon): preserve user files when regenerating design systems (#5472)
Generated-mode design systems used to unconditionally rewrite every
derived file on each update path (metadata edit, revision accept,
brand re-extract, and the README-missing read-path regeneration),
silently clobbering user-created components/directories and edited
docs back to scaffold values. Users then had to redo their work.

Introduce a hidden `.od-generated.json` fingerprint manifest recording
what the generator last wrote per derived file, and a shared
`filterGeneratedWritesPreservingUserEdits` helper that only overwrites
a file when it is still byte-identical to that fingerprint. Anything
the user edited — or any pre-existing file with no fingerprint (legacy
systems) — is preserved; absent files are written and fingerprinted.

The filter lives inside `writeGeneratedDesignSystemFiles`, so all three
callers (create/update/ensure) inherit non-destructive semantics; the
revision-accept atomic-write path is filtered separately and drops
fileChange-owned paths from the manifest so hand-authored revision
content survives later regenerations.

Refs: https://plane.powerformer.net/open-design/projects/49832a02-3158-4faf-bf2f-d0e39c40c7e6/issues/50579eb2-c64b-4a2d-8fda-d10f4d4af626

Generated-By: looper 0.0.0-dev (runner=worker, agent=claude-code)
2026-07-13 04:10:01 +00:00
489fda8996 fix(daemon): do not treat a Task sub-agent turn_end as the main turn's completion (#5488)
The claude stream-json parser read `message.stop_reason` off any
`type:'assistant'` wrapper and, when it was not `tool_use`, emitted
`turn_end` without inspecting the top-level `parent_tool_use_id`. Under
`--verbose`, a Task sub-agent's messages stream inline with a non-null
`parent_tool_use_id`, and its internal turn ends with its own
`stop_reason: 'end_turn'`. That sub-turn boundary was therefore treated
as the run's turn completion: applyClaudeStreamJsonRunBookkeeping set
`run.turnCompletedCleanly = true` and closed stdin while the main turn
was still running. If the main agent then exited non-zero without a
result frame (crash / OOM / connection-drop abort), the close handler
found `turnCompletedCleanly === true` and classifyChatRunCloseStatus
translated the non-zero exit into `succeeded` — a silent false success
that hid the failure, skipped the same-run retry, and surfaced the
sub-agent's internal scratch as the answer.

Guard the emit on `parent_tool_use_id == null` so only a main-turn frame
fires `turn_end`. Suppressing at the parser keeps the invariant in one
place: the same branch also resets the per-turn artifact-echo dedup state
(`recentWriteContents` / `wroteHtmlFileThisTurn`), which likewise must
not fire on a sub-agent boundary. The loose `== null` preserves existing
behavior for any main-turn frame that omits the field.

Fixes #5487

Co-authored-by: tomsen-ai <230283659+tomsen-ai@users.noreply.github.com>
2026-07-13 04:09:48 +00:00
Aria ZhaoandGitHub ba871e77a7 fix(daemon): cancel active runs when deleting a conversation or project (#5468) (#5498)
Deleting a conversation or project left its active agent run's CLI
subprocess alive — it kept consuming provider quota, stayed non-terminal
in GET /api/runs with no UI trace, and for a project delete kept writing
into a directory that had been removed out from under it.

Both DELETE handlers now cancel the non-terminal runs they own (reusing
the existing design.runs.cancel) before the row is removed, through a
shared cancelRunsOwnedBy helper.
2026-07-13 04:02:32 +00:00
9d030e6fdf feat(chat): map long-tail failure_detail values to specific run-error cards (#5329)
* 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.

* feat(chat): map long-tail failure_detail values to specific run-error cards

#5321 pipes the daemon's failure_detail to the chat but only mapped the
high/medium-frequency causes (hard_quota, workspace_credits, cli_not_installed).
This wires the recoverable long tail the daemon already classifies — timeout,
inactivity_timeout, empty_output, session_resume_expired, git_bash_missing —
each to a named failure type + one-line fix with a plain Retry.

These are engine-neutral outcomes, so they resolve in a new
AGENT_AGNOSTIC_DETAIL_FAILURE_UI layer BEFORE the AMR/Antigravity agent
branches (which would otherwise return a generic retry for AMR). Adds the
title/message i18n keys across all 19 locales and unit coverage for the
new mappings.

---------

Co-authored-by: xiaoche-hub <298951296+xiaoche-hub@users.noreply.github.com>
2026-07-13 03:49:33 +00:00
4963e3964f fix(daemon): allow Grok Build headless edits (#5508)
Disable Grok plan mode and auto-approve tool calls for non-interactive runs so permission requests cannot become successful no-ops. Add regression coverage for the required argv flags.\n\nFixes #5507

Co-authored-by: Kirill Turanskiy <thebtf@users.noreply.github.com>
2026-07-13 03:43:58 +00:00
cfc6ae0890 fix(daemon): harden the AMR proxy against unhandled pipe errors (#5512)
* fix(daemon): guard AMR proxy stream bodies against error crashes

The AMR/vela API proxy pipes the upstream response body to the client
(`upstreamRes.pipe(res)`) and the client upload body to the upstream
(`req.pipe(upstream)`). Only the outbound ClientRequest had an `'error'`
handler; neither piped stream body did. `.pipe()` does not forward a
source `'error'`, and a stream that emits `'error'` with no listener
throws — an unhandled exception that crashes the privileged daemon.

Both legs have real error paths: the upstream response body can
ECONNRESET mid-stream (a routine network drop), and the inbound request
body errors when a client aborts an upload. Either one takes the whole
daemon down today.

Route both pipes through `pipeProxyStreamWithGuard`, which attaches the
missing source-error handler: an upstream response error 502s (or
destroys the response once headers are sent), and a client-upload error
destroys the upstream request. Same class as #5126 (unhandled read-stream
error in the library asset route), different site.

Regression test pins the crash vector — a bare pipe throws on a source
error — against the guard routing it to the handler instead.

* chore: re-trigger fork-pr approval workflow (transient blip)

---------

Co-authored-by: tomsen-ai <230283659+tomsen-ai@users.noreply.github.com>
2026-07-13 03:43:09 +00:00
ab0d390bbf fix(web): remove Kimi clone prompt example (#5492)
* fix(web): remove Kimi clone prompt example

* test(web): update clone prompt contract

* ci: retrigger PR checks

---------

Co-authored-by: elifive555555 <296440099+elifive555555@users.noreply.github.com>
2026-07-13 03:06:21 +00:00
4567a0d575 fix(daemon): import scanRunEventsForUsageAnalytics for devloop token reconciliation (#5434)
The plugin devloop tokens_used reconciliation in firePipelineForRun (server.ts,
reached from routes/runs.ts when a run resolves a plugin snapshot with a
pipeline) calls scanRunEventsForUsageAnalytics(run.events, ...), but server.ts
never imported it. Every reconciliation pass therefore threw
`ReferenceError: scanRunEventsForUsageAnalytics is not defined`, which the
surrounding .catch swallows into a `[plugins] devloop tokens_used reconciliation
failed` warning — so run_devloop_iterations.tokens_used has silently never been
backfilled since the call was added.

Regression source: PR #4644 (24298737c) added the call and the UPDATE but not
the import. Fix adds the missing name to the existing run-analytics-observability
import.

The call site is a private closure inside startServer reached only by a completed
plugin-devloop pipeline run, so a faithful end-to-end red spec would need a
bespoke pipeline-plugin fixture + fake agent + full HTTP devloop run — dispro-
portionate for a missing-import ReferenceError that is certain by static analysis
and git-blame. Verified instead by: the absent import (grep), the guaranteed
throw, git blame to #4644, daemon typecheck (both tsconfigs), pnpm guard, and the
existing plugins-pipeline-runner / plugins-headless-run suites staying green.

Co-authored-by: tomsen-ai <230283659+tomsen-ai@users.noreply.github.com>
2026-07-11 08:39:54 +00:00
a8779679bd chore(plugin-previews): refresh baked preview manifest (#5388)
Co-authored-by: open-design-bot <bot@open-design.ai>
2026-07-11 08:39:09 +00:00
Alex LuceroandGitHub 9075d3d059 Add CLI native session continue action (#5271) 2026-07-11 08:39:02 +00:00
9166c3bf09 feat(contracts): agent-ready capability contracts + design RFCs (#5398) (#5400)
* feat(contracts): agent-ready capability contracts + design RFCs (#5398)

Slice 1 of the "agent-ready OD" proposal (#5398): pure-TypeScript contract
shapes for the assistant's capability layer, plus the design RFCs. No behavior
code.

- packages/contracts/src/agent-tools/: descriptor (declare), actions
  (call/result), manifest (list + paged search), registry (port), task
  (cross-project AgentTask). Discriminated on `surface`; dual-track law is
  structural; action identity is a daemon-minted `invocationId`.
- docs/rfc-drafts/agent-ready.md: umbrella design + slice roadmap, the
  MCP/WebMCP-congruent capability model, tool-search options, and
  "options considered -> recommendation" for the load-bearing decisions.
- docs/rfc-drafts/agent-ready-cross-project.md: the maintainer's separate
  reviewable permissions writeup (AgentTask + permission options).

Registry runtime, endpoints, and behavior are later slices per the roadmap.

* fix(contracts): narrow AgentToolSearchQuery.surface to 'api'

Search is the PULL path over the persistent registry, which per the RFC
holds only api-surface tools — browser availability is PUSH-advertised
per-session and cannot be answered by a session-less search query. Narrow
the searchable surface to 'api' so the contract does not encode a
browser-tool discovery path the runtime would have to special-case or
break. Addresses the blocking review on #5400.

* fix(contracts): address review — narrow search result to api-only; distinct browser vs api call paths

Resolves the two remaining blocking findings on PR #5400:

- manifest.ts: narrow `AgentToolSearchResult.tools` from the full
  `AgentToolDescriptor` union to `ApiToolDescriptor[]`, enforcing the
  PULL/api-only search invariant on the response side too (the query
  `surface` was already narrowed to `'api'`). A registry impl can no
  longer type-check while leaking `browser` descriptors into a
  session-less search result.

- agent-ready.md: stop presenting `BrowserActionRequest` as the generic
  `tools/call` for every capability. The two surfaces now have distinct
  request paths: `api` tools are invoked through their declared `/api/*`
  route + `od` subcommand (no round-trip envelope), while
  `BrowserActionRequest`/`Result` is the browser-surface path only — the
  genuinely new inbound seam.

- agent-ready-cross-project.md: reconcile the slice-5 cross-project
  routing fields (`taskId?`/`targetProjectId?`) with the distinct-paths
  model — added on both the browser envelope and the api-call request,
  not bolted onto `BrowserActionRequest` for api tools.

Validation: contracts typecheck pass; pnpm guard pass.

---------

Co-authored-by: LA <la@LAs-MacBook-Pro.lan>
2026-07-11 08:38:54 +00:00
SivanColaandGitHub 7d207ce95d Add Reasonix MCP installer support (#5397) 2026-07-11 08:38:26 +00:00
lefarcenandGitHub 1ab1f6cd00 fix(web): label the library filter selects for screen readers (#5389)
Generated-By: looper 0.0.0-dev (runner=worker, agent=codex)
2026-07-11 08:38:17 +00:00
071b9bf972 refactor(daemon): group data-migration modules into migration/ (#5191)
Co-authored-by: LA <la@LAs-MacBook-Pro.lan>
2026-07-11 08:37:40 +00:00
Ethan GuoandGitHub 443e31319c fix(daemon): drop shell:true from host-tool launch (Windows command injection) (#5146) 2026-07-11 08:37:15 +00:00
Ethan GuoandGitHub 20854e7f9e fix(daemon): block SSRF in brand-extraction fetches (#5121)
* fix(daemon): block SSRF in brand-extraction fetches

* fix(daemon): follow public redirects in brand SSRF guard, block only private hops

* fix(daemon): drop system-Chrome brand fallback (unguardable SSRF sink)
2026-07-11 08:37:05 +00:00
PhilippandGitHub 1637f3374a fix(media): add missing withMediaRequestInit for OpenRouter fetches (#4990)
* fix(media): add missing withMediaRequestInit for OpenRouter fetches

* test(media): add regression tests for requestInit.dispatcher in OpenRouter adapter
2026-07-11 08:36:55 +00:00
open-design-release-bot[bot]GitHubopen-design-release-bot[bot] <open-design-release-bot[bot]@users.noreply.github.com>
fb9ae974ab chore(release): bump main to 0.14.2 ahead of stable 0.14.1 (#5441)
Co-authored-by: open-design-release-bot[bot] <open-design-release-bot[bot]@users.noreply.github.com>
2026-07-10 14:50:47 +00:00
38248c8397 refactor(daemon): group CLI help-text modules into cli-help/ (#5193)
Co-authored-by: LA <la@LAs-MacBook-Pro.lan>
2026-07-10 13:38:06 +00:00
da1199aaa7 refactor(daemon): group design lifecycle modules into design/ (#5194)
Co-authored-by: LA <la@LAs-MacBook-Pro.lan>
2026-07-10 13:37:53 +00:00
af72a6cbb5 refactor(host): split index.ts into cohesive sibling modules (part of #5165) (#5170)
Co-authored-by: LA <la@LAs-MacBook-Pro.lan>
2026-07-10 13:37:45 +00:00
5c8642f125 refactor(platform): split index.ts god-file into cohesive sibling modules (#5166)
Export-surface-stable split of packages/platform/src/index.ts (~1111 lines,
one file, ~5 unrelated concerns) into cohesive sibling modules, with index.ts
kept as a pure barrel of explicit named re-exports.

- command.ts    cross-platform command-invocation construction
- process.ts    process lifecycle, stamps, snapshots, stop escalation
- proxy-env.ts  system proxy discovery + proxy-aware env merging
- fs.ts         path containment, atomic copy, removal, log tails
- http.ts       HTTP readiness polling
- toolchain.ts  user-level toolchain bin discovery

Function bodies moved byte-identically; only import paths adjusted. Added a
@module docblock per file and JSDoc to every exported function. Tiny private
error-extraction helpers (errorCode/errorMessage) are duplicated where used to
keep the public surface identical (no new exported names). Public export set
unchanged (42 names); typecheck + 76 tests + build all green.

Tracked by #5165.

Co-authored-by: LA <la@LAs-MacBook-Pro.lan>
2026-07-10 13:37:37 +00:00
c23e158d88 fix(daemon): surface Claude is_error result terminations instead of classifying the run as succeeded (#5159)
A Claude CLI error termination (error_during_execution, error_max_turns,
resume failures) emits a result frame with is_error true, stop_reason
null, and the human-readable cause in errors[] — then exits 1. Three
layers each washed that signal away:

- claude-stream.ts forwarded the frame as a plain usage event, dropping
  is_error and errors[] (qoder-stream already honors this contract).
- applyClaudeStreamJsonRunBookkeeping treated the null stop_reason as a
  clean terminal turn and set turnCompletedCleanly.
- classifyChatRunCloseStatus translated the non-zero exit into
  'succeeded' via turnCompletedCleanly, skipping the CLI failure
  diagnostics entirely.

Net effect: the run reads as succeeded with nothing surfaced — a silent
failure.

The fix keeps one authority per decision:

- The parser emits the error (message from errors[]) and flags the
  usage event isError; success frames keep their exact shape.
- Bookkeeping still closes stdin (the turn IS over) but no longer marks
  an error termination clean. A SessionEnd-hook non-zero exit after a
  normal result (#3373) carries no is_error and keeps the clean path.
- The chat path suppresses the error event during a resume attempt so
  the close handler's transparent reseed (#4438) stays invisible,
  mirroring the existing ACP resume_failed suppression.
- The connection test skips terminal result errors — its own result
  frame parse (#4501) stays the sole authority for the output_parse
  phase classification.

Co-authored-by: tomsen-ai <230283659+tomsen-ai@users.noreply.github.com>
2026-07-10 13:37:28 +00:00
Ethan GuoandGitHub 4c2a9daa16 fix(daemon): handle read-stream errors in library asset routes (#5126)
* fix(daemon): handle read-stream errors in library asset routes

* fix(daemon): strip success headers from the library asset stream-error 404
2026-07-10 13:36:44 +00:00
Ethan GuoandGitHub ad5b83f32d fix(daemon): fix quadratic ReDoS in design-system swatch parsers (#5113) 2026-07-10 13:36:22 +00:00
laihenyiandGitHub 72c8b1a240 fix(desktop): guard render-process-gone against teardown crash (#5098)
The main window's render-process-gone handler read
window.webContents.getURL() unconditionally. During app quit / teardown
the window and its webContents can already be destroyed when the event
fires, so getURL() throws "Object has been destroyed" as a fatal
uncaught exception. It is most visible right after launch when the
auto-updater triggers a quit-to-apply, making the app look like it
crashes seconds after startup.

Guard the same way sendUpdaterStatus already does a few lines below:
short-circuit once the window or its webContents is destroyed, and skip
crash-report / recovery work when the renderer is exiting as part of
shutdown.

Add a source-assertion red spec (mirroring renderer-recovery-poll-loop
and window-chrome, since createDesktopRuntime can't be instantiated
under vitest) that pins the teardown-guard invariant.

Fixes #5095
2026-07-10 13:36:07 +00:00
Nicholas-XiongandGitHub 65396fe05d fix(web): move conversation count next to conversations header (#3848) (#5156)
Move the conversation count from centered position between the
header title and '+ New' button to immediately after the title.
The '+ New' button is pushed to the right via margin-left: auto.
2026-07-10 13:33:29 +00:00
Aria ZhaoandGitHub 96bb1cc9b7 fix(web): localize built-in skill edit as a user-override flow (#4793)
* i18n(web): add built-in skill user-override strings

Add skillsOverrideCreate / skillsOverrideSave / skillsBuiltInOverrideWarning
to the typed dictionary and every locale, ahead of wiring the built-in
skill edit flow through them.

* fix(web): localize the built-in skill override confirmation

The built-in-skill edit warning was hardcoded English, so it stayed
English under non-English locales even though the rest of the dialog
followed the active language. Read it from the i18n dictionary instead.

* fix(web): label built-in skill editing as create/save user override

Editing a built-in skill silently produced a user override, but the
affordances read "Edit" / "Save" — so the built-in → user type change
was a surprise. The edit action, confirmation, and form heading now read
"Create user override" and the submit reads "Save as user override".

* test(web): lock built-in skill override wording across locales

Cover the regression gap raised on #4793: assert the built-in skill edit
flow renders the user-override i18n keys end to end — the edit tooltip, the
confirmation warning, the confirm button, the form heading, and the submit
button — in the default en locale and a translated zh-CN path, and that a
user skill keeps the plain Edit/Save affordance with no override warning.

Assertions compare against the locale dictionaries rather than hardcoded
literals, so a copy tweak in a locale file stays green while reverting to
the old Edit/Save keys (or a hardcoded-English warning) goes red.
2026-07-10 13:33:06 +00:00
Marc ChanandGitHub 10adca2cbf fix(web): stop AMR request loop in settings (#5426) 2026-07-10 10:53:31 +00:00
lefarcenandGitHub a36d6fb6c1 perf(bake): render plugin previews concurrently (bounded worker pool) (#5429)
The plugin-preview bake renders one preview at a time — a headless-Chrome
screencast + ffmpeg encode per plugin, ~16s each, fully serial. When a PR
touches the deck renderer (as #5083 did), every deck example's served preview
HTML changes, so the content-hash skip re-bakes them all: 80 clips × ~16s ≈
19 min of pure serial render, dwarfing the ~3 min of setup. The bake is the
single long pole on those PRs (24m vs ~5m for everything else).

Parallelize the render with a bounded worker pool (PREVIEW_CONCURRENCY, default
4 = the standard GitHub runner's vCPUs). Local A/B on 6 forced re-bakes: 83s
serial -> 32s at concurrency 3 (~2.6x); the 80-item CI sweep amortizes launch
cost across ~20 clips/worker, so it should land closer to ~4x — dropping the
render from ~19 min to ~5 min. Shared by the pre-merge, post-merge, and
release-cut bake workflows, so all three get faster from this one change.

Three things had to hold for concurrency to be correct, each verified locally:

- One browser PER worker, not a shared browser with N pages. CDP screencast
  only follows the ACTIVE tab, so a page captured while another is foreground
  is starved of frames — I reproduced both failure modes (a frames-0 skip and
  a 5KB near-empty clip). Browser-per-worker keeps every captured page the sole
  foreground tab; clip sizes then match the serial baseline exactly. Launch is
  lazy so an all-cached sweep (the common post-merge case) pays no browser.
- ffmpeg/ffprobe moved off execFileSync onto promisified execFile: a
  synchronous encode blocks the event loop and stalls every other page's
  screencastFrameAck (Chrome only sends the next frame after the ack).
- Clamp inter-frame durations to >=0. CDP frame timestamps are occasionally
  non-monotonic, and the resulting negative concat `duration` makes ffmpeg
  reject the whole clip ("Invalid data found when processing input") — which
  fails the strict pre-merge job. Pre-existing, but CPU contention from
  concurrent renders makes the blips more frequent. Reproduced serially
  (example-deck-swiss-international) and confirmed the clamp turns it green.

Also exit explicitly at the end: puppeteer leaves a lingering handle after
browser.close(), and with a browser per worker that keeps the CLI alive after
all work is done — on CI the step would hang to the 90-min job timeout. Drain
stdout/stderr first so the piped CI summary is never truncated.

Verified against a real daemon (127.0.0.1 /api/plugins): 6/6 baked, 0 skipped,
clean exit (0 lingering processes), clips byte-comparable to serial output.
2026-07-10 10:40:53 +00:00
lefarcenandGitHub 5f411466b8 fix(web): lock the discovery Continue button after submit (#5428)
Clicking Continue on the discovery question form only queues the answer
send; the parent's busy/answered signals arrive a beat later. Until then
the CTA stayed enabled with no feedback, so an impatient user (thinking it
was stuck) — or the auto-continue countdown racing the click — could fire
the same submit again and again, piling dozens of identical
"[form answers - discovery]" sends into the queue.

Give the panel a single-submit invariant: on the first commit it
optimistically locks (a synchronous ref guards the chokepoint), disables
Continue/Skip, and shows a spinner + "Submitting…" so it never looks
stuck. The lock spans the async file upload and is released if that upload
fails so the user can retry; otherwise it hands off to submitDisabled once
the turn is busy and releases on answered, so an idle form stays
actionable.
2026-07-10 10:22:35 +00:00
592f7c2c0b fix(acp): replay absorbed complete frames when a line aggregate fails (#5179)
Co-authored-by: tomsen-ai <230283659+tomsen-ai@users.noreply.github.com>
Co-authored-by: Amy <1184569493@qq.com>
2026-07-10 10:15:07 +00:00
bc6a246c65 feat(slides): enhance deck project experience (#5083)
* feat(deck): enhance slide deck functionality with speaker notes and navigation improvements

- Added a switch for including speaker notes in slide decks, defaulting to true.
- Updated keyboard navigation to include 'R' for resetting to the first slide.
- Implemented click navigation for half-slide transitions.
- Enhanced the deck framework to support hidden programmatic prev/next controls.
- Improved handling of file inputs in question forms to support file submissions.

This update aims to improve user experience in creating and presenting slide decks, ensuring better accessibility and functionality.

* feat(artifacts): enforce semantic naming for user-facing deliverables

- Updated guidelines to require semantic filenames derived from user briefs for new artifacts, moving away from defaulting to `index.html`.
- Enhanced documentation to clarify naming conventions for HTML files, emphasizing the use of descriptive names like `investor-pitch-deck.html`.
- Adjusted prompts and hints across the system to reflect these changes, ensuring consistency in user-facing outputs.
- Improved test coverage to validate the new naming conventions and ensure proper implementation in various contexts.

This update aims to enhance clarity and usability in project file management, promoting better organization and user experience.

* feat(file-viewer): enhance speaker notes display and improve file workspace functionality

- Updated the speaker notes section to include a styled placeholder for empty notes, improving user experience.
- Changed the icon in the file workspace from "slides" to "panel-left" for better clarity.
- Introduced new sub-category filtering options in the file workspace, enhancing organization and navigation.
- Improved styling for the page creator search input and added visual feedback for loading states.

These changes aim to enhance usability and visual consistency across the file viewer and workspace components.

* test(web): align workspace tests with pages switcher

* test(e2e): open design files through pages menu

* fix(deck): keep source mode reachable

* fix(workspace): restore uploaded file tabs

* feat(deck-thumbnail): implement virtualized thumbnail rail for improved performance

- Introduced a new `DeckThumbnailRail` component to optimize the rendering of deck thumbnails, reducing memory usage and improving load times.
- Enhanced the `FileViewer` to integrate the new thumbnail rail, allowing for lazy loading of thumbnails based on viewport visibility.
- Added tests for the `DeckThumbnailRail` to ensure correct functionality and caching behavior.
- Updated styles for better visual consistency and performance during thumbnail rendering.

These changes aim to enhance user experience by providing a smoother and more efficient way to navigate through deck slides.

* feat(deck): enhance deck rendering with thumbnail support and keyboard shortcuts

- Integrated `DeckSlideThumbnail` for rendering individual slides in a shadow root, improving performance and reducing main-thread load.
- Updated `DeckThumbnailRail` to utilize the new thumbnail component, allowing for efficient slide navigation.
- Implemented keyboard shortcuts for deck navigation, enhancing user experience during presentations.
- Added tests for `buildDeckRenderInput` to ensure proper functionality of the deck stage fallback.
- Improved styling for loading states in the viewer, providing a smoother visual experience.

These changes aim to optimize the deck viewing experience by reducing resource consumption and enhancing interactivity.

* feat(deck): improve thumbnail loading and speaker notes editing experience

- Refactored `DeckSlideThumbnail` to enhance loading states with a new shimmer effect, improving visual feedback during thumbnail rendering.
- Updated `DeckThumbnailRail` to manage loading states for thumbnails, ensuring a smoother user experience.
- Simplified the speaker notes editing interface by removing the toggle switch, allowing direct editing from the notes body.
- Enhanced accessibility by adding roles and focus states to speaker notes elements.
- Added tests to verify the new loading states and speaker notes functionality.

These changes aim to optimize the user experience in deck navigation and speaker notes management, providing a more intuitive and responsive interface.

* feat(file-viewer): enhance version export functionality and speaker notes interaction

- Introduced new export options for file versions, including PDF and image formats, improving the ability to download and share content.
- Implemented a streamlined process for exporting version content, with progress indicators and error handling for a better user experience.
- Enhanced speaker notes editing by refining event handling to prevent unintended focus shifts, ensuring a smoother editing experience.
- Updated styles for improved visual consistency in the file version download interface.
- Added tests to verify the functionality of version exports and speaker notes interactions.

These changes aim to optimize the file viewer's usability and enhance the overall experience when managing and exporting project files.

* fix(web): polish export menus and creator filters

* fix(file-viewer): fall back to preview exports

* test(file-viewer): cover version export fallbacks

* test(file-viewer): assert loading skeleton by role

* fix(file-viewer): restore html source mode affordance

* fix(file-viewer): capture full version fallback snapshots

* docs: trim slides handoff eof

* test(exports): cover full preview snapshot requests

* test(file-viewer): account for snapshot options arg

* refactor(file-viewer, file-workspace): streamline export functions and enhance page creator dialog visibility

* test(file-workspace): enhance slide template creation and mock plugin responses

* feat(export): add support for versioned exports and enhance HTML export functionality

* feat(export): implement versioned export support across multiple formats and enhance related tests

* feat(file-viewer): enhance manual edit functionality with commit sequence tracking and add test for inline text session saving

* style(speaker-notes): update filmstrip and thumb-frame styles for improved responsiveness and add corresponding tests

* feat(file-viewer): enhance comment composer functionality with portal metrics and improve draw toolbar layout for mobile views

* feat(analytics): track slide deck viewing experience

Add analytics for the new deck/slides experience introduced in this PR:
the deck viewer surface, in-deck slide navigation, thumbnail rail, and
speaker-notes authoring. Presentation entry stays on the existing
present_popover click to avoid double-counting.

- contracts: DeckViewerSurfaceViewProps (surface_view),
  DeckViewerClickProps (ui_click), SpeakerNotesSaveResultProps
  (new speaker_notes_save_result event), wired into the event unions.
- web: track* wrappers + fire helpers in FileViewer — deck_viewer
  surface_view on deck mount, slide_prev/next/reset, thumbnail_select,
  thumbnail_rail_toggle (expand/collapse), speaker_notes_edit, and
  speaker_notes_save_result (preview + presenter surfaces).
- tests: deck analytics suite asserting the emitted (event, props) pairs.

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

* feat(file-viewer, file-workspace): enhance export toast functionality and improve localization for page creation

* feat(home-view, plugins-home-section, agent-skill): update default facet preference, enhance category selection handling, and improve localization support

- Set `preferDefaultFacet` to true in HomeView component for better user experience.
- Introduced `handlePickCategory` function in PluginsHomeSection to manage category selection based on card layout visibility.
- Updated PRIMARY_CATEGORIES to reflect the new order and added localization for titles and descriptions in adaptAgentSkill.
- Enhanced tests to verify the correct handling of display copy and tags from SKILL frontmatter.

* feat(plugins-home, file-workspace): introduce commercial category labels and enhance deck handling

- Added a new module for resolving and localizing commercial category labels for plugins.
- Updated the FileWorkspace and PluginCard components to display the commercial category label on deck cards.
- Enhanced the PageCreatorPresetFrame to adjust frame classes based on deck presets.
- Refactored subcategory handling in facets to align with the new commercial category taxonomy.
- Improved localization support for new category labels across multiple languages.

* feat(deck-preview): implement deck preview scaling and enhance home hero layout

- Introduced `useDeckPreviewScale` hook to manage scaling of deck previews within a fixed 1280×720 logical viewport, ensuring proportional rendering across various templates.
- Updated `FileWorkspace`, `HomeHero`, and `PluginCard` components to utilize the new scaling functionality for improved visual consistency.
- Enhanced the layout of the home hero section to accommodate new subtype chips and descriptions, including a "More" dropdown for overflow handling.
- Improved localization support for new UI elements across multiple languages.

* fix(PreviewModal): clarify sidebar prop documentation and update test description

- Updated the documentation for the `PreviewSidebar` interface to specify that the sidebar's stage-edge handles are not shown when the sidebar prop is absent.
- Modified the test suite description for `PreviewModal` to be more concise and relevant.

* test(e2e): select prototype visual card

---------

Co-authored-by: free666799 <293857035+free666799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 09:41:47 +00:00