Documents the agent writes open in the workspace panel instead of another
application, and local images the agent mentions show up in the conversation.
Workspace preview
- PDF (pdf.js with its own layout and text layer), Word (docx-preview inside a
scripts-disabled sandboxed iframe) and Excel (SheetJS; .xlsx, .xlsm, .xls) open
in the side panel with zoom and fit, per-file scroll/zoom/sheet memory, and a
refresh when the agent rewrites the file. The engines load lazily.
- Bytes come from a new GET /api/sessions/:id/workspace/raw route, with an
extension allowlist, size caps, the workspace boundary and canonical-path
checks. The file endpoint returns metadata and a version for documents. The
client fetches with the bearer credential, so it works in Electron, LAN H5 and
remote access alike.
- Chat links, output cards and the change card open pdf/docx/xlsx in the
workspace; documents outside the workdir still go to the system application.
- Image viewer with fit, zoom and pan, and "open in system app".
Chat images
- Markdown images outside the workdir, at ~/, C:\ and file:// paths render, open
in the viewer, and offer "open original" (pictures only).
- Images returned by tools such as Read appear as thumbnails under the call.
Hardening found in review
- previewFsUrl escapes each path segment; a double-escaped %2e%2e used to leave
/preview-fs/<session>/.
- The CORS, API timing and remote-access header decorators set headers in place.
Rebuilding the response buffered whole files in memory and dropped
Content-Length.
- The engine owns the pdf.js worker, so closing one document no longer fails the
next open.
- Office archives are inflated in steps to check their real sizes, not the sizes
they declare.
- A viewer that fails to load stays in its panel instead of taking the window down.
Adds pdfjs-dist, docx-preview, xlsx (SheetJS 0.20.3 tarball) and fflate as
renderer dev dependencies; Vite bundles them.
Refs #1397
Keep explicit file identities across output cards and prose links, preserve
shell outputs without checkpoint evidence, and retain a file card when
video preview fails. Add cross-project path and opening regressions.
* fix(provider): honor configured output budget for direct Anthropic providers
Anthropic-format providers connecting directly to an upstream could not set a
reply output budget: the field was hidden in the UI, stripped before
persistence, and dropped by a local-proxy-only gate at request build time, so
low-cap relay upstreams returned 400/truncation with no user recourse.
Surface the budget for Anthropic, persist it as a budget-only object so stale
OpenAI-compatibility options cannot leak, and remove only the numeric gate in
getConfiguredProviderOutputBudget. getOutputBudgetHeaders keeps its local-proxy
guard so the internal provenance header never reaches an external provider.
Adds kernel/desktop unit tests for the direct-budget path and the provenance
non-leak.
* fix(provider): disable optional manual thinking below its token minimum
---------
Co-authored-by: gugugaga <267102352+omazili-guga@users.noreply.github.com>
Co-authored-by: 程序员阿江(Relakkes) <relakkes@gmail.com>
Add independent chat font, text size and reading width settings with preview, migration and cross-window sync. Preserve virtualized reading positions when appearance changes.
Validation: 499 focused tests, lint/typecheck, build, persistence upgrades, chat contracts and 8 offline agent scenarios passed. Full desktop/server/coverage gates remain affected by documented Windows baseline failures; macOS native rendering was not tested.
A referenced ReadSession walked backward through the whole transcript and scanned it from the start, which starved the sidebar poll into a flashing load-failure banner. Bound that read and keep a failed list refresh off the sidebar.
The provider editor dropped the session model from a value whose inferred type had no index signature, and the trace detail stopped reading the session revision it still stored.
The sidebar's "using standard history" notice flickered because a busy
passive checkpoint marked the index degraded, and the next reconciliation
cleared it again, bouncing the session list between the index and a full
JSONL scan. Incremental reconciliation now keeps serving a snapshot that
already has committed rows, and a transient SQLITE_BUSY no longer changes
the public state.