mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
codex/tools-dev-telemetry
10
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
64710082d0 |
fix(diagnostics): collect the AMR run's runtime log records (OPEND-3498) (#8488)
* fix(diagnostics): collect the AMR run's runtime log records (OPEND-3498) AMR incidents never carried AMR-side evidence. The collector looked for OpenCode logs under <dataDir>/amr/opencode-home, but Vela drops the OPENCODE_TEST_HOME it receives and runs OpenCode under per-conversation homes in $AMR_HOME/opencode-sessions, so nothing was listed and no note explained the gap. $AMR_HOME/logs/agent-runtime.jsonl was never listed either. - Take the incident run's records from agent-runtime.jsonl, plus those of the OpenCode sessions it owns until another run takes a session over, so other runs' records never enter the bundle. - Report a missing runtime log, a missing run id and the not-yet-located session logs as explicit collection notes, and keep those reasons instead of a consent verdict. - Give sources that appear after opting in a boundary at their current size on start, so a newly listed shared log is not omitted forever. Locating the per-conversation OpenCode session logs is left for a follow-up. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(diagnostics): keep a rotated log's baseline when extending the fence On Windows the packaged launcher rotates latest.log to previous.log at every start. extend() saw previous.log as an unbaselined path and recorded a boundary at its current size, so the rotated log's bytes written after the real boundary were dropped as consent_boundary. Found in a packaged Windows run: the daemon log written after opting in was missing from the bundle. Skip files that share a baselined creation time; the fence decides their boundary from that baseline. Skipping only defers to apply() and never admits bytes by itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
e5d75aa8a1 |
fix(diagnostics): stop repeating API failures from flooding automatic uploads (#8461)
* fix(diagnostics): stop repeating API failures from flooding automatic uploads Stable 0.24.1 devices uploaded thousands of diagnostic incidents a day from one deterministic failure: GET /api/runs answering 400 PROJECT_SCOPE_REQUIRED. - Key API-failure incidents by failure signature per hour instead of the per-request id, so a polling caller yields one incident per hour. - Drop earlier "[diagnostics] incident delivered" receipt lines from automatic bundles; on busy devices they filled the daemon log tail. - Desktop pet task center: stop interval polling once the daemon requires a project scope, and stop restarting the poller on every 1.5s config reload. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(diagnostics): filter receipts before allocating the log tail budget Scan automatic text logs backward with bounded buffers, preserving complete lines and consent offsets before applying the retained byte limit. Cover receipt suffixes larger than 4 MiB and filtered-tail boundary cases. Generated-By: looper 0.13.0 (runner=fixer, agent=codex) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
282416ccd8 |
feat(diagnostics): automatically collect consented failure evidence (OPEND-3397) (#8357)
* feat(diagnostics): persist and upload consented Agent fault evidence * feat(diagnostics): capture cancellation and delivery experience failures * feat(diagnostics): collect visible frontend failures through consented relay * fix(web): reset cached analytics before diagnostic test mocks Generated-By: looper 0.13.0 (runner=fixer, agent=codex) * fix(web): reset template picker before it becomes interactive * test(web): await campaign presentation before retry polling |
||
|
|
f1a0b60c6c |
chore: unify product name display to OpenDesign repo-wide (#6998)
* chore: unify product name display to OpenDesign repo-wide Rename the user-facing product name from "Open Design" to "OpenDesign" across documentation, web/desktop UI strings, i18n locales, prompts, plugin/skill/design-system content, changelogs, and specs, matching the landing-page rename already in flight. Deliberately excluded so installed-app identity stays untouched: - packages/release PRODUCT_NAME and everything derived from it (app bundle names, installer/registry identity, updater feeds) - tools/pack, tools/release, apps/packaged, landing page (PR #6983) - mocks/ recorded traces and LICENSE - test fixtures and comments that encode real on-disk install paths (Open Design.app, Program Files\Open Design, AppData paths) Validated with pnpm guard, pnpm typecheck, and targeted web/desktop/host test runs over the renamed strings. * fix(web): keep two-letter author monogram for CamelCase brand names authorInitials split words on whitespace only, so the renamed single-token author name "OpenDesign" produced a one-letter monogram. Treat lower-to-upper CamelCase boundaries as word breaks so the avatar fallback stays "OD". * fix(release): restore spaced Windows source artifact names in win.ps1 tools/pack still derives builder outputs from the unchanged PRODUCT_NAME ("Open Design"), so the asset script must look for the spaced filenames. Matches the mac/mac-intel/linux asset scripts, which were already kept on the original names. --------- Co-authored-by: Joey <236967869+joeylee12629-star@users.noreply.github.com> |
||
|
|
85d2e4893c |
fix(amr): recover expired cloud sessions (#6786)
* fix(amr): recover expired cloud sessions * fix(diagnostics): redact dynamic API routes * fix(daemon): honor settings in AMR diagnostics * fix(daemon): align workspace auth revisions * fix(web): gate expired cloud sessions on sign-in * fix(web): scope cloud sign-in gate to cloud execution * test(web): align cloud auth fixture * fix(web): recover transient run authority failures * test(daemon): expect retryable authority detail * test(e2e): align BYOK auth boundary * test(e2e): align expired cloud recovery flows |
||
|
|
34192c6719 |
fix(diagnostics): keep the previous session's daemon log and cut event tails on line boundaries (#6531)
* fix(diagnostics): keep pre-restart daemon log and line-aligned event tails Two gaps made incident support bundles useless after a relaunch: 1. Every packaged launch opened each sidecar's latest.log with mode "w", destroying the previous session's log — the bundle held only the ~70 lines written since the restart while the incident-time daemon log was gone. openLog now rotates the prior file aside as previous.log (exactly one prior session, rename overwrites), and the diagnostics export bundles logs/<app>/previous.log whenever it exists, under the same per-file tail cap. The source entry is existence-conditional so non-rotating launchers (tools-dev appends) do not gain missing-file placeholder noise. 2. The 2MB tail cap on runs/*/events.jsonl cut bytes from mid-file, so the first kept line was half a JSON object and jsonl consumers fell over on line 1. readMaybeTail now advances the cut to the next line boundary (with a guard byte so an already-aligned cut loses nothing); a window with no newline keeps the raw cut. * fix(packaged): keep the prior log when rotation fails instead of truncating it A non-ENOENT rename failure (Windows share-lock on previous.log, read-only or exotic filesystem) still fell through to open(path, "w"), destroying the only copy of the pre-restart log while previous.log stayed unavailable — the exact data loss this rotation exists to prevent. Truncate only when the prior bytes are provably elsewhere: moved aside, or never written (first-launch ENOENT). Otherwise open in append mode, since a merged two-session log is recoverable and an erased one is not. * fix(diagnostics): never export a partial record, and never hide an unreadable log Two holes in the truncation/collection contract: - A single event larger than the whole tail budget left no complete line to align to, and the fallback kept the raw cut — so the export shipped the interior of one JSON record and any consumer streaming the bundle through a jsonl parser died on line 1, exactly what alignment exists to prevent. Signal "no complete line" instead and let the collector record it, so the bundle carries a self-describing placeholder rather than half an object. - The optional-source gate treated EVERY access failure as absence, so a previous.log that exists but is unreachable (EACCES on the log directory, EIO) vanished with no manifest entry and no warning. Only ENOENT may drop the entry now; anything else stays listed and reports the real error. |
||
|
|
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. |
||
|
|
56c410e9ef |
[codex] strengthen daemon diagnostics coverage (#4948)
* test: expand automation coverage gaps * test: cover additional automation gaps * test: strengthen daemon diagnostics coverage * fix(e2e): align media provider project modal helper |
||
|
|
ae67ad41b1 |
fix(amr): resolve run model catalog from cache instead of a blocking per-run vela model list (#4075)
* fix(amr): resolve run model catalog from the shared cache instead of a blocking per-run `vela model list`
Packaged AMR runs failed with "AMR model … is not available from Vela.
Refresh the AMR model list, choose a supported model, and retry this run"
even for a logged-in user who had a real model selected.
Root cause: the AMR run preflight ran a fresh, blocking
`vela model list` (the authoritative remote catalog) on every turn and
fail-closed the run whenever that single call timed out or errored.
That call needs network reachability to the AMR gateway AND `$HOME`
(the offline `vela model preset` / `--version` calls need neither),
has a 10s ceiling, and only retries a narrow set of network errors.
Under CorpLink/飞连 the round-trip routinely approached the timeout, so
runs died at preflight — while the model picker still showed models
(seeded from the offline preset), making AMR look selectable but unusable.
Fix (daemon):
- Preflight now resolves the catalog through the same AmrModelLoadingCache
the UI's /api/amr/models endpoint uses: a cached authoritative
`vela model list` when hot, otherwise the offline `vela model preset`
seed while a remote refresh runs in the background. The blocking remote
probe is off the per-run hot path; a transient failure degrades to
preset instead of fail-closing. vela's own `session/set_model` stays
the final gate for a genuinely unsupported model.
- The previously-silent `catch {}` around the catalog probe now logs, so
the failure is no longer invisible in logs/diagnostics.
- Backfill `$HOME` in the AMR spawn env: `execAgentFile` replaces the
child environment wholesale, and `vela model list` exits non-zero with
"$HOME is not defined" when it is missing (preset/--version do not),
which silently decapitated the catalog probe.
- Raise the cache's remote-refresh interval from 60s to 10min: the
catalog changes on the order of days, cached entries are returned
instantly, and the per-run path now reads this cache.
Diagnostics: the export bundle now also carries the coding-agent CLIs'
own logs (claude / codex / opencode / amr) plus recent per-run
events.jsonl, via shared builders in @open-design/diagnostics used by
BOTH the daemon HTTP export and the desktop IPC export (the latter,
which users actually trigger, previously shipped neither run logs nor
agent logs). Secret-bearing files (~/.amr/config.json, ~/.codex/auth.json)
are never swept; only *.log files, tail-bounded and redacted.
* fix(diagnostics): resolve AMR OpenCode log dir from the effective OPENCODE_TEST_HOME
The AMR CLI log sweep hardcoded `<dataDir>/amr/opencode-home/...`, which
misses the provider logs as soon as a user sets
`agentCliEnv.amr.OPENCODE_TEST_HOME` (a supported app-config override that
spawnEnvForAgent preserves) — the run writes logs elsewhere while the
bundle keeps looking in the default path.
- buildAgentCliLogSources now takes an explicit `amrOpenCodeHome`; it
sweeps `<amrOpenCodeHome>/.local/share/opencode/log` and falls back to
the `<dataDir>/amr/opencode-home` default when not provided.
- The daemon export resolves the effective home through the live
`spawnEnvForAgent('amr', …)` (honoring the app-config override) so the
two cannot drift.
- The desktop export reads the override from app-config directly (it may
not import daemon internals) with leading-`~` expansion, falling back to
the default otherwise.
* fix(diagnostics): honor CLAUDE_CONFIG_DIR / CODEX_HOME overrides in the agent-log sweep
Same class as the AMR OpenCode-home fix: `agentCliEnv.claude.CLAUDE_CONFIG_DIR`
and `agentCliEnv.codex.CODEX_HOME` are supported app-config overrides
(`app-config.ts`), but the collector hardcoded `~/.claude` and `~/.codex/log`,
so a user who relocates either CLI home would have those provider logs dropped
from the bundle.
- buildAgentCliLogSources now takes `claudeConfigDir` / `codexHome`,
falling back to `~/.claude` and `~/.codex` when not provided.
- The daemon export resolves all three homes (amr/claude/codex) through the
live spawnEnvForAgent so they cannot drift from the spawn path.
- The desktop export reads the overrides from app-config directly with `~`
expansion.
* fix(diagnostics): resolve OpenCode XDG_DATA_HOME from the effective spawn env
Sandbox mode (sandbox-mode.ts) rewrites XDG_DATA_HOME to a sandbox root, and
OpenCode resolves its data/log dir from XDG_DATA_HOME. The export was passing
the host process's XDG_DATA_HOME, so a sandboxed runtime would miss the
OpenCode CLI logs. Resolve it from `spawnEnvForAgent('opencode', …)` (which
reapplies sandbox env) alongside the amr/claude/codex homes; falls back to the
host value when unavailable. Sandbox is opt-in (OD_SANDBOX_MODE) and off in
packaged/dev runtimes, but this keeps the resolution consistent and correct.
|
||
|
|
80d305858b |
feat(diagnostics): add one-click log export from Settings → About (#798)
* feat(diagnostics): add one-click log export from Settings → About
Adds a new "Export diagnostics" entry under the About section that bundles
daemon/web/desktop logs, machine info, and recent macOS crash reports into
a zip the user can share when reporting issues.
- Browser hits a new daemon HTTP endpoint and triggers a download.
- Electron uses an IPC bridge with the native save dialog and reveals the
saved file in Finder/Explorer; the Help menu also exposes it as a
fallback when the daemon is unresponsive.
Packaging + redaction lives in a new @open-design/diagnostics package so
both surfaces share it. Sensitive JSON keys, URL query secrets, and the
current user's home path are redacted before packaging.
* build(nix): include packages/diagnostics in daemon build targets
The Nix daemon derivation builds workspace siblings in dependency order
before compiling apps/daemon. Without @open-design/diagnostics in that
list, the daemon TypeScript build fails inside the Nix sandbox with
`Cannot find module '@open-design/diagnostics'` because pnpm install
only creates the symlink — the dist output that the package.json
exports point at isn't produced until each sibling's build script runs.
* build(tools-pack): include @open-design/diagnostics in packaged INTERNAL_PACKAGES
Without this, packaged win/mac/linux builds fail with `npm error 404` when
the post-build `npm install --omit=dev --no-package-lock` step in the
assembled app tries to resolve `@open-design/diagnostics@0.2.0` from the
public npm registry. The package is workspace-private, so it has to be
tarballed via `pnpm pack` and file:-referenced from the assembled
package.json like every other internal workspace dep that daemon/desktop
depend on.
Also wires the package's `pnpm --filter ... build` into the pre-pack
workspace build step so the dist/ exists before pnpm pack runs, and
updates the two test fixtures (`win-app.test.ts`, `workspace-build.test.ts`)
that mirror INTERNAL_PACKAGES.
The diagnostics package itself is repinned to exact dependency versions
already used elsewhere in the workspace (`jszip 3.10.1`, `@types/node
20.19.39`, `esbuild 0.28.0`, `typescript 5.9.3`, `vitest 4.1.6`) so it
passes the new `pnpm guard` exact-version rule and produces a minimal
lockfile diff vs main (additions only, no resolution-string churn).
* fix(diagnostics): include `~` in bearer-token redaction char class
RFC 6750 token68 syntax allows `~`, so tokens like `Authorization: Bearer
abcd~efgh` were only partially matched by `HTTP_AUTH_SCHEME_RE`. The
regex stopped at the first `~`, leaving the tail (`~efgh`) un-redacted in
the exported diagnostics zip — a clear leak since this feature explicitly
generates support bundles for external sharing.
Add `~` to the character class and a regression test.
* fix(diagnostics): only collect renderer.log from desktop
`buildSidecarLogSources` unconditionally added `logs/${app}/renderer.log`
for daemon/web/desktop, but only the desktop runtime writes a renderer
log (see apps/desktop/src/main/runtime.ts) — daemon and web are pure
Node services with no Electron renderer. Every export therefore produced
missing-file placeholders and manifest warnings for the two phantom
paths, polluting the bundle.
Gate the renderer.log source on APP_KEYS.DESKTOP so the daemon-side
collector matches the desktop-side collector in apps/desktop/src/main/
diagnostics.ts:63.
* fix(diagnostics): mirror desktop-side renderer.log gate
The previous fix only updated the daemon-side `buildSidecarLogSources`
in `apps/daemon/src/diagnostics-export.ts`. The desktop-side collector
at `apps/desktop/src/main/diagnostics.ts` had an identical copy of the
same bug that I overlooked: it also unconditionally added
`logs/${appKey}/renderer.log` for daemon/web/desktop, producing
missing-file placeholders + manifest warnings for the two phantom paths
on every desktop-initiated export.
Apply the same `appKey === APP_KEYS.DESKTOP` gate here so both export
entry points (browser via daemon HTTP, Electron via native save dialog)
emit the same clean manifest.
* feat(diagnostics): add `od diagnostics export` CLI subcommand
AGENTS.md's dual-track capability-exposure contract requires every
user-facing feature to ship on both the web UI and the `od` CLI. The
diagnostics export was only reachable through Settings → About and the
desktop Help menu; this commit closes the loop with an `od diagnostics
export [<path>] [--json]` subcommand registered in SUBCOMMAND_MAP.
The CLI is a thin shell over the existing GET /api/diagnostics/export
endpoint — same zip output, same redaction, same crash-report scope.
Defaults to writing `open-design-diagnostics-<timestamp>.zip` in the
current directory; `--output <path>` or a positional arg overrides.
`--json` prints `{path, sizeBytes}` for shell pipelines.
Use cases this unlocks:
- A CI script can `od diagnostics export ~/artifacts/bundle.zip` after
a failed run.
- Bug reporters on headless boxes can grab a bundle without booting
the web UI.
- `od doctor` follow-ups can collect a full snapshot when a probe fails.
* fix(diagnostics): surface non-sidecar launch in manifest warnings
`buildSidecarLogSources()` returns `[]` when the daemon has no sidecar
runtime context, which is the standard `od` (plain) launch path —
`runDaemonCliStartup()` -> `startDaemonRuntime()` does not pass a
runtime. Settings → About and the new `od diagnostics export` previously
reported success but produced a bundle with only the summary JSONs, so
operators could not tell "no logs because plain launch" from "no logs
because something genuinely broke."
- Extend `DiagnosticsContext` with an optional upstream `warnings:
string[]` that `buildManifest` merges into the manifest warnings.
- Emit STANDALONE_LAUNCH_WARNING from the daemon handler when
`options.runtime == null`. The warning names the limitation and
points the user at the sidecar entry points that DO capture logs.
- Add a regression spec at `apps/daemon/tests/diagnostics-export.test.ts`
that drives the handler with `runtime: null` and asserts the warning
surfaces in `summary/manifest.json` (and that `files` is empty so a
user reading the bundle does not confuse "no log sources" with
"missing files").
|