11 Commits

Author SHA1 Message Date
Xuehao Xu
d1f77fc1f8
fix(frontend): restore user input after stream reconnect (#5428)
* fix(frontend): restore input on stream reconnect

* fix(frontend): close reconnect review feedback

* fix(frontend): address reconnect review feedback
2026-09-15 08:16:33 +08:00
JeffJiang
dfaeef3772
fix(frontend): support standalone demo APIs and runtime GitHub stars (#5302)
* fix(frontend): support standalone demo APIs and runtime GitHub stars

* Update API origin URL to use environment variables

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* test(frontend): align static demo tests with runtime origin

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-09-09 00:00:56 +08:00
PeaceMaker-best
6022bdf5ae
perf(frontend): avoid redundant chat state snapshots (#5159)
* perf(frontend): avoid redundant chat state snapshots

Signed-off-by: PeaceMaker-best <221849497+PeaceMaker-best@users.noreply.github.com>

* fix(streaming): preserve incremental chat semantics

Signed-off-by: PeaceMaker-best <221849497+PeaceMaker-best@users.noreply.github.com>

---------

Signed-off-by: PeaceMaker-best <221849497+PeaceMaker-best@users.noreply.github.com>
Co-authored-by: PeaceMaker-best <221849497+PeaceMaker-best@users.noreply.github.com>
2026-09-04 08:20:12 +08:00
Nefelibata
c6f6a01f56
fix(runtime): prevent IndexError in MemoryStreamBridge._make_gap on empty events buffer (#5047)
* fix(runtime): prevent IndexError in MemoryStreamBridge._make_gap on empty events buffer

* fix(runtime): handle empty stream replay gap bounds across backend and frontend

- Clamp MemoryStreamBridge queue_maxsize at 1 and validate StreamBridgeConfig.queue_maxsize >= 1
- Update StreamGap docstring to clarify None retained bounds
- Allow StreamReplayGapData and parseStreamReplayGap in frontend to accept string | null bounds, safely resuming when bounds are null
- Add backend and frontend regression unit tests for queue clamping and null bounds replay gap

* docs(stream-bridge): bump config_version and document empty buffer replay gap behavior

* docs: document nullable gap bounds and sync helm config_version to 37
2026-08-29 17:21:29 +08:00
Nan Gao
73e3699347
feat(frontend): render markdown artifacts in the "open in new window" view (#5056)
* feat(frontend): render markdown artifacts in the new window

The artifacts panel's "open in new window" action handed the browser the
raw Gateway response. For markdown that is a `text/markdown` body the
browser can only show as source, so the new window was a text dump rather
than a reader.

Route markdown artifacts to a new `/artifacts/view` page that renders them
with the same components the panel uses (SafeStreamdown + the artifact
rehype chain + citation links/panel), including the truncated-preview
banner and its "load full file" action. Everything else keeps the raw
Gateway URL — notably HTML/SVG, which the Gateway deliberately serves as a
download so active content never executes in the application origin.

- `core/artifacts/viewer.ts` centralizes which stored artifacts are
  markdown (`.skill` archives included, since they hold a SKILL.md), so
  the panel and the viewer route cannot drift.
- `ArtifactFilePreview` and its siblings move out of
  `artifact-file-detail.tsx` into `artifact-file-preview.tsx`; otherwise
  the standalone route would pull the CodeMirror editor into its bundle.
- The window title comes from the route's `generateMetadata`, not
  `document.title`, which the App Router overwrites after hydration.
- The viewer reads content through `useStandaloneArtifactContent`, which
  shares `useArtifactContent`'s query key but not its `useThread`
  dependency, since a detached window has no thread context.

Claude-Session: https://claude.ai/code/session_013AiCrC5SBc3HdFYNxsp1EC

* fix(frontend): keep the artifact target across re-authentication

Review found the standalone viewer unrecoverable from an expired session.
The window's target lives entirely in `?path=...&thread_id=...`, and both
auth paths dropped it:

- The layout guard redirected to `/login` with no `next` at all. A layout
  cannot read `searchParams`, so the guard moves into the page, which can
  — and rebuilds the full viewer address for `next`. The layout loses its
  AuthProvider along the way: nothing under this route reads `useAuth`,
  and the guard now makes a single `getServerSideUser` call per request.
- The shared fetch wrapper built `next` from `window.location.pathname`,
  which silently truncated the query string. It now carries `search` too,
  so any route holding state in the query survives a 401, not just this
  one. `validateAuthNextPath` already accepts a query string.

`buildArtifactViewerURL` is split out of `resolveArtifactOpenURL`: the
guard needs the route itself, never the Gateway fallback that the latter
takes for non-markdown targets.

Tests: the login round trip (unit — the rebuilt URL survives
`validateAuthNextPath` and parses back to the same target), the fetch
wrapper preserving the query on 401 (unit), and the expired-session
window reaching `/login` with the artifact intact (E2E). The E2E asserts
on the popup's navigation *requests*, since `(auth)/layout` answers
`/login` with a server redirect under DEER_FLOW_AUTH_DISABLED and no
navigation commits.

`tests/unit/core/models/api.test.ts` stubbed `window.location` without
`search`; a real Location always has it.

Claude-Session: https://claude.ai/code/session_013AiCrC5SBc3HdFYNxsp1EC

* fix(frontend): keep public showcase artifacts out of the auth gate

Review found that the viewer's access check regressed `/showcase`. Those
pages render with `isMock`, their artifacts are served by the
unauthenticated demo route, and the raw artifact URL this window replaced
stayed public — so gating the window unconditionally bounced every
logged-out showcase visitor to /login for a document that is already
public.

`requiresAuthenticatedViewer` exempts a mock target only when
`resolveStaticDemoArtifact` would actually serve it. The allowlist is the
authority rather than the flag: `mock=true` is caller-supplied, so a
target the demo route answers with 404 — a non-allowlisted path, or a
thread that is not a demo thread — still needs a session.

Covered in `tests/e2e-auth/`, since the default E2E config disables auth
and cannot see this: a public showcase artifact renders without a
session, while a non-allowlisted path and a missing mock flag both land
on /login. Verified the positive case goes red without the exemption.

Claude-Session: https://claude.ai/code/session_013AiCrC5SBc3HdFYNxsp1EC
2026-08-29 11:07:16 +08:00
Huixin615
1cd5dea336
fix(streaming): signal replay history gaps (#4426)
* fix(streaming): signal replay history gaps

* fix(streaming): guard initial Redis replay window

* fix(frontend): align inactive gap recovery

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-27 07:13:06 +08:00
ShitK
a4ede80deb
fix(runtime): reject unsupported run options and stream modes (#4430)
* fix(runtime): reject unsupported run options

* fix(runtime): align SDK run compatibility

* fix(frontend): avoid unsupported events stream mode

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-24 19:24:24 +08:00
lllyfff
a817a0ed87
Fix(frontend): stale run reconnect and cancel handling (#3908)
* Fix stale run reconnect and cancel handling

* fix the frontend

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* Fix prettier formatting and document error/timeout reconnect intent

Wrap the over-long assertion in api-client.test.ts so the CI prettier --check
job passes, and add a comment above TERMINAL_RUN_STATUSES noting that
error/timeout short-circuit intentionally drops the transient onError toast on
reload (the persisted error state still loads from the checkpoint via
useThreadHistory).

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

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
2026-07-04 09:15:36 +08:00
Jiahan Chen
6fb22bb311
test(frontend): migrate unit tests to rstest (#3703)
* test(frontend): migrate unit tests to rstest

* docs: updates AGENTS.md

* test(frontend): fix rstest lint formatting
2026-06-22 16:12:49 +08:00
zgenu
737abc0e45
fix: ignore stale run reconnect conflicts (#3284)
* fix: ignore stale run reconnect conflicts

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

* fix: ignore stale run reconnect conflicts

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-05-28 17:29:30 +08:00
yangzheli
4efc8d404f
feat(frontend): set up Vitest frontend testing infrastructure with CI workflow (#2147)
* feat: set up Vitest frontend testing infrastructure with CI workflow

Migrate existing 4 frontend test files from Node.js native test runner
(node:test + node:assert/strict) to Vitest, reorganize test directory
structure under tests/unit/ mirroring src/ layout, and add a dedicated
CI workflow for frontend unit tests.

- Add vitest as devDependency, remove tsx
- Create vitest.config.ts with @/ path alias
- Migrate tests to Vitest API (test/expect/vi)
- Rename .mjs test files to .ts
- Move tests from src/ to tests/unit/ (mirrors src/ layout)
- Add frontend/Makefile `test` target
- Add .github/workflows/frontend-unit-tests.yml (parallel to backend)
- Update CONTRIBUTING.md, README.md, AGENTS.md, CLAUDE.md

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

* style: fix the lint error

* style: fix the lint error

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-04-12 18:00:43 +08:00