272 Commits

Author SHA1 Message Date
一片一片
f0ba280365
fix(artifacts): parse write-file URLs through the guarded helper (#7609)
`ArtifactFileDetail` and `loadArtifactContentFromToolCall` re-parsed
`write-file:` URLs with `new URL` + `decodeURIComponent` after
`parseWriteFileArtifact` had already rejected malformed values. The panel
restores its selection from session storage and only type-checks it as a
string, so a stale or hand-edited `write-file:%E0%A4%A` (or
`write-file://[`) threw `URIError`/`TypeError` during render.

Export the guarded parser and read `path` / `tool_call_id` from one parsed
result in both places; a rejected URL degrades to the raw string or
`undefined` instead of taking the panel down.
2026-10-28 07:35:01 +08:00
一片一片
52f6ff078c
fix(api): survive a malformed CSRF cookie (#7589)
* fix(api): survive a malformed CSRF cookie

`readCsrfCookie` percent-decodes a cookie the gateway sets verbatim.
Cookies are shared by every app on the same host (ports included), so a
stray or truncated `%` escape from another local app, a proxy, or a stale
value makes `decodeURIComponent` throw `URIError` inside `fetch` and
`getCsrfHeaders` — every state-changing call fails with an exception
before the gateway can answer 403.

Fall back to the raw cookie value when decoding fails, matching
`getLocaleFromCookie` (#7553).

* test(api): exercise sibling CSRF cookies in the DOM

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-27 09:47:24 +08:00
Totoro
ab8cd4e10f
fix(channels): keep IM binding codes on screen and report goal state in replies (#7591)
* fix(channels): keep IM binding codes on screen and report goal state in replies

Connecting Slack, Discord, Feishu, DingTalk, WeCom, Buzz or QQ now opens a
dialog that keeps the /connect command on screen with a copy button, its
expiry time and the binding status, instead of a toast that disappears.

The bots' /connect replies and the IM /goal replies now use the owner's
web UI language (else channel_connections.notification_locale), and /goal
reports the goal's state as the web goal bar does: in progress, why it
stopped, or that it was met.

* docs(changelog): note the IM binding dialog and localized IM replies (#7591)

* test(channels): check IM reply tables against every notification locale

The connect-reply check compared a fixed en-US/zh-CN pair, and the /goal
reply table had none. Both now match NOTIFICATION_LOCALES with the same
keys in each, like the existing _GOAL_REASON_TEXT check, so a new locale
fails here instead of raising KeyError at reply time.

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-27 09:46:58 +08:00
一片一片
a2fd094b40
fix(i18n): survive a malformed locale cookie (#7553)
* fix(i18n): survive a malformed locale cookie

getLocaleFromCookie decoded the raw cookie value without a guard, so a
malformed percent-encoding threw URIError during locale bootstrap and in
formatTimeAgo. It also split the cookie on every "=", truncating values
that contain one. Decode defensively and split on the first "=" only.

* fix(i18n): normalize the cookie locale in formatTimeAgo

The second caller of getLocaleFromCookie cast the raw value to Locale, so a
malformed-but-known-prefixed cookie skipped normalization. Normalize it like
useI18n does, and cover the malformed and valid cookie paths.

---------

Co-authored-by: 一片一片 <192845873+gdut4140@users.noreply.github.com>
2026-10-26 23:37:38 +08:00
JasonH
5dc508b407
fix(auth): recover session checks after network failures (#7529)
* fix(auth): recover session checks after network failures

* fix(auth): retain reconnect wakeups during pending refreshes

* docs(auth): scope recovery guidance within budget

* fix(auth): preserve reconnects until tabs become visible

* test: include auth guidance in approved inventory

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-26 19:29:33 +08:00
Hyeonsang Cho
9c00fd839a
fix(frontend): return SSO sign-ins to their next path (#7467)
* fix(frontend): return SSO sign-ins to their next path

The (auth) layout redirected every signed-in request to /workspace. The
Gateway sets the session cookie on the same redirect that lands on
/auth/callback?next=..., so the callback page never rendered and every SSO
sign-in landed on the workspace instead of the page that asked for it. A
signed-in visit to /login?next=... lost its target the same way.

Layouts cannot read searchParams, so the layout now renders the page for a
signed-in session and each page sends the user on: the callback and login to
the validated next path, setup to the workspace. The login page shows a
short wait message instead of the form while it redirects, and uses
router.replace so Back does not return to it.

* docs(changelog): reference #7467 for the SSO next-path fix

* fix(auth): reject control characters in the next redirect path

Browsers delete tab, LF and CR from a URL before parsing it, so a next of
"/\n//evil.example" passed both validators' "//" check and then navigated
to //evil.example. The login page's post-sign-in push already followed it,
and the signed-in login and SSO callback redirects reach it too.

validateAuthNextPath and validate_next_param now reject every C0 control
character and DEL. Percent-encoded forms are left alone: URL parsers do
not decode them, so they stay on the local origin.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-26 19:24:35 +08:00
jianyx1
4b1e06bbf9
feat(runtime): honor the web UI language in replies and DeerMem updates (#7294)
* feat(runtime): honor the web UI language in replies and DeerMem updates

Carry Settings > Appearance > Language as request-only run data: the
Gateway validates context.locale against a finite allowlist, the lead
agent prefers it for replies, and bundled DeerMem writes new facts and
summary prose in it. Explicit user language requests still take
precedence; checkpoint configurable, static prompts, and memory backend
APIs are unchanged.

Refs #7287

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

* fix(runtime): guard language context and pin locale maps

* test(runtime): pin UI locales across frontend and Gateway

Add the shared locale fixture and document the intentional language fallback for internal runs. Clarify live-locale precedence in the run context contract.

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-10-26 13:24:30 +08:00
YongxinFu
8337a80ae6
fix(frontend): count code points when previewing table cells and memory facts (#7503)
* fix(frontend): count code points when previewing table cells and memory facts

artifact-table-preview cut a cell at 120 UTF-16 code units and
memory-settings-page's truncateFactPreview measured and cut fact previews
at 140, so an emoji spanning two units could be split at the cut — the
lone half renders as U+FFFD — and a preview that already fit the
documented code-point budget was still shortened. Both previews now share
truncateToCodePoints in core/utils/text, which measures and cuts by code
points, the same rule briefReason in core/scheduled-tasks/format.ts
already follows.

Add unit tests for the helper and a DOM regression test covering both the
fits-exactly and the cut-inside-an-emoji cases.

* fix(frontend): count code points in the artifact-cell expand gate too

The expandable gate still measured value.length (UTF-16 units), so a
120-code-point cell ending in an astral character opened the expand
control even though the inline preview already showed the whole cell —
the same units-vs-code-points mismatch the preview cut fixed.

The gate now counts Array.from(value), and the boundary test asserts
the fits cell renders as a plain cell with no expand control while the
overflows cell keeps its button and whole-emoji cut.

Suggested-by: gdut4140 (port of the #7509 expandable gate)
2026-10-26 08:23:49 +08:00
Luck
ce0fa4c114
feat(batch): add browser review for persisted results (#7006)
* feat(batch): add browser review for persisted results

* fix(batch): address browser review feedback

* test(batch): cover persisted result review regressions

Exercise running-to-terminal detail refresh, scoped citation dialogs, omitted evidence, translated page labels, and legacy evidence bounds. Fix component lint and formatting and synchronize the review contract documentation.

* docs(frontend): keep batch review guidance within budget

* fix(batch): refresh retried reviews and account for omitted evidence

* fix(ci): keep batch review guidance within size budget

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-25 23:27:35 +08:00
ShaoLOWu
21fb080530
fix(frontend): allow non-ASCII local parts in auth password forms (#7488) (#7498)
* fix(frontend): allow non-ASCII local parts in auth password forms (#7488)

The sign-in, register, admin-setup and change-password forms relied on the
browser's type="email" constraint validation, which rejects any non-ASCII
local part before the submit handler runs. The backend identity schema
deliberately accepts such addresses (see emailSchema in @/core/auth/types),
so an account created via the API, an administrator or OIDC could never sign
in through the password form.

Forms are now noValidate, and the permissive isValidEmail helper (one @,
non-empty local part and domain, no whitespace) runs in the handlers instead,
staying strictly weaker than what the backend accepts.

* fix(auth): normalize emails and restore form password checks

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-25 22:09:58 +08:00
王一帆
bd62f96732
feat(knowledge): add optional platform connections and hierarchical spaces (phase 3) (#7417)
* feat(knowledge): add optional platform connections and hierarchical spaces

* fix(knowledge): return actionable errors for unavailable policy storage

* perf(knowledge): resolve space policy once per snapshot
2026-10-25 19:52:31 +08:00
一片一片
6b2ac58555
fix(scheduled-tasks): count code points when shortening a run summary (#7452)
briefReason() measured and sliced UTF-16 code units, so a cut inside an
emoji's surrogate pair left a lone high surrogate that the outcome notice
renders as U+FFFD. Measuring code points also stops shortening a summary
that fits the documented budget.

Add a regression test for both the fits-exactly and truncating cases.

Co-authored-by: 一片一片 <192845873+gdut4140@users.noreply.github.com>
2026-10-25 15:49:10 +08:00
一片一片
6213fee192
fix(frontend): bound notification bodies by code points (#7474)
Both chat pages capped the "conversation finished" notification body with
String#length / substring, which count UTF-16 code units: a cut inside an
astral character's surrogate pair rendered U+FFFD, and a body that fits 200
code points was shortened early.

Extract notificationBody() so both pages share the code-point bound and the
"Conversation finished" fallback, and add boundary regressions.

Co-authored-by: 一片一片 <192845873+gdut4140@users.noreply.github.com>
2026-10-25 15:13:16 +08:00
Hyeonsang Cho
1792e4e432
fix(frontend): stop re-validating account emails the backend accepted (#7449)
* fix(frontend): stop re-validating account emails the backend accepted

userSchema checked email with zod's email(), which rejects many addresses
pydantic EmailStr registers (a&b@example.com, a@b.c, Unicode). /auth/me for
such an account parsed as malformed: SSR answered gateway_unavailable, the
offline banner never recovered, and the admin user list failed for every
administrator. The backend owns address validation; keep a non-empty string.

* docs(changelog): reference #7449 for the account email schema fix
2026-10-25 10:09:49 +08:00
dependabot[bot]
04691473d4
chore(deps): bump zod from 3.25.76 to 4.6.6 in /frontend (#7392)
* chore(deps): bump zod from 3.25.76 to 4.6.6 in /frontend

Bumps [zod](https://github.com/colinhacks/zod) from 3.25.76 to 4.6.6.
- [Release notes](https://github.com/colinhacks/zod/releases)
- [Commits](https://github.com/colinhacks/zod/compare/v3.25.76...v4.6.6)

---
updated-dependencies:
- dependency-name: zod
  dependency-version: 4.6.6
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* fix(frontend): complete Zod 4 migration

Use explicit string keys for extension setting records so the discovery
schema remains assignable to the extension loader contract under Zod 4.
Cover accepted primitives, rejected non-primitive settings and inferred
public types through the discovery API.

Keep Nextra 4.6.1 and its docs theme on their previous Zod 4.3.6 dependency
until upstream fixes required children validation. The app remains on Zod
4.6.6. Document and lock the targeted compatibility overrides.

* docs: keep frontend guidance within hard budget

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 22:36:41 +08:00
Xuehao Xu
f6a5a64c9b
feat(knowledge): add optional ingestion and processing extension (#7293)
* feat(knowledge): add optional ingestion and processing extension

* fix(knowledge): address catalog and ingestion review regressions

* fix(knowledge): add sanitized provider diagnostics and Chinese docs

* fix(knowledge): align capabilities and repair catalog e2e assertions
2026-10-24 08:19:02 +08:00
Tsai Yuan
0530d0c7c6
fix(frontend): resolve relative message links to artifacts (#7356)
* fix(frontend): resolve relative links in messages to thread artifacts

An assistant message that links a file it wrote with a relative
destination, e.g. [Report](report.md), rendered a plain relative href.
The browser resolved it against the chat route to
/workspace/chats/report.md, so clicking it opened a "thread" whose id is
the file name and the Gateway rejected it (422). Only /mnt/ links were
rewritten, and the clarification and present_files message text did not
get the thread-aware link renderer at all.

- A relative link (no scheme, no leading /, no .. segment) that
  suffix-matches exactly one thread artifact now opens that artifact in
  a new tab, using the rule message images already use. When zero or
  several artifacts match it renders as text instead of a link to a
  broken chat URL. Links outside the message list are unchanged.
- The artifact list reaches the link renderer through a context:
  Streamdown memoizes its root without comparing `components`, so a
  renderer built before the list arrives would keep the stale list.
- /mnt/ artifact links get the normal link styling (they rendered as
  unstyled text before).
- A reopened thread rebuilds its values from the POST /history head,
  which carries messages but not `artifacts`. The message list and the
  artifact panel now add the /mnt/ paths from present_files calls in the
  messages, so links resolve and the panel lists the files after reload.

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

* fix(frontend): address artifact link review

---------

Co-authored-by: huangzz <110116@nd.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
2026-10-23 22:50:52 +08:00
Tsai Yuan
03c95f5278
fix(subagents): report model output length caps (#7355)
* fix(subagents): report model_length_capped when a subagent hits the output limit

ModelLengthFinishReasonMiddleware only ran on the lead chain. A subagent
whose response was cut at the provider output limit (finish_reason=length,
stop_reason=max_tokens, MAX_TOKENS, Responses max_output_tokens) reached
the lead as a clean `completed`, and a tool call truncated at that
boundary still executed with partial arguments.

Register the same middleware on the subagent chain, in the lead's
relative position (after configured extensions, before Safety). It now
also records each detected cap per context run_id and exposes
`consume_stop_reason`, so the executor's existing duck-typed guard
collection reports `stop_reason=model_length_capped`. Add the value to
the subagent status contract (Python enum, contract fixture, frontend
whitelist) with the label "model output length limit", and update the
subagent docs.

Tests cover the middleware channel, chain placement, the contract, and
an end-to-end executor run with a fake model.

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

* test(tasks): cover cap reasons on completed and failed runs

Extend the existing shared-contract round-trip matrix to preserve both terminal statuses alongside every supported stop reason, including model_length_capped and loop_capped.

---------

Co-authored-by: huangzz <110116@nd.com>
Co-authored-by: Claude Code <noreply@anthropic.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-23 21:42:11 +08:00
Huang-404-Q
4e20cbb4d6
fix(uploads): treat a missing message as an empty one (#7342)
The unsuccessful-upload branch called result.message.trim() directly. A
gateway or proxy that omits message turns a handled upload failure into a
TypeError while handling it, losing the clean "Upload failed" path.

Coerce a non-string message to an empty string before trimming.

The regression test sends success:false without a message and expects
"Upload failed"; it throws a TypeError on main.

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-23 20:03:17 +08:00
TYY-2005
cc013c9680
fix(frontend): classify thinking-only token steps correctly (#7175)
* fix(frontend): classify thinking-only token steps correctly

* docs(frontend): explain thinking-only token step classification

---------

Co-authored-by: TYY-2005 <334029581+TYY-2005@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-23 11:24:26 +08:00
Xuehao Xu
29ba806fc6
feat(knowledge): add independent Wiki and entity relation retrieval tools (#7171)
* feat(knowledge): add independent Wiki and entity relation retrieval tools

* fix(knowledge): address review diagnostics and CI catalog coverage

* fix(knowledge): deduplicate graph evidence and update catalog e2e

* fix(knowledge): clarify bounded evidence and pin browser CI runners

* Update e2e-tests to run on ubuntu-latest

* Update workflow to use ubuntu-latest

* test(ci): stop pinning maintainer-controlled browser runner images

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-22 16:54:58 +08:00
Cordis798
17f2bea275
fix(api): preserve Request inputs in authenticated fetch (#7254)
* fix(api): preserve Request inputs in authenticated fetch

* docs(frontend): keep fetch guidance within the size budget

* test(api): address fetcher review feedback

---------

Co-authored-by: Cordis798 <284918518+Cordis798@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-22 16:05:43 +08:00
lihongyuan99
2ad2132336
fix(citations): pair inline code within a paragraph, not across blank lines (#5876)
* fix(citations): pair inline code within a paragraph, not across blank lines

maskInlineCode paired backticks over the whole document, so a stray backtick in
one paragraph and another in a later one formed a span CommonMark never renders.
The citation between them was masked out of the Sources list, and the stolen
opener simultaneously un-masked a real code span further down.

Scope the pairing to blank-line-delimited paragraphs, keeping the captured
separator so string length and occurrence indices still align with the original
markdown.

* fix(citations): end the paragraph on a CRLF blank line too

core/messages/utils.ts recognises a blank line as \r?\n[ \t]*\r?\n; the citations
scanner now uses the same shape, so CRLF markdown gets the same pairing limit
instead of falling back to whole-document pairing.

* fix(citations): mask fences in containers and end inline spans at block bounds

Follow-up to the paragraph-scoped inline pairing review on #5876.

Indented fences inside a list item, and blockquote-prefixed fences, were only
blanked by accident before: the whole-document backtick pairing happened to
close them. Scoping the pairing to a paragraph removed that safety net and let
sample citations surface as sources, so the fence matcher now accepts container
prefixes and is marker-aware.

Inline spans also end at the other block boundaries CommonMark uses - ATX
headings, thematic breaks / setext underlines, interrupting list items and a
blockquote's empty continuation line - not only at blank lines.

* fix(citations): anchor fence detection and end fences with their container

An unanchored container-prefix match turns any line that merely contains a
three-backtick run into a fence opener, which masks everything after it and
drops live citations. Anchor the match to the line start.

A fence also has to end with the container it was opened in: a blockquote loses
its fence at the first line without a `>` marker, and an indented fence at the
first non-blank line that is less indented than its opener. Without that, the
citation following a closed blockquote is masked away, which is the same failure
direction as the original bug.

* fix(citations): track the container a fence was opened in

Separate a fence marker's own indentation from the indentation its container
gives it, require whitespace after a list marker before treating it as a
container, count blockquote depth instead of testing it as a boolean, and check
a closer's indentation against the container before ending the block.

Cover the four shapes from the review on #5876 with tests, and check the scanner
against remark-parse over twenty fence shapes.

* fix(citations): cap an opener's indentation at three columns past its container

A marker four columns past the container's content column is an indented code
block, so it must not open a fence; otherwise the citation below it is blanked
to end of input instead of surfacing as a source.

* fix(citations): match fence openers and closers to what their tails allow

A closing fence is bare, and a backtick fence's info string carries no
backtick. So ` ```text ` inside a ``` block is literal content rather
than a closer, and ` ```md `x` ` is a paragraph line rather than an
opener. Missing either check sends the scanner the wrong way: the first
lets a citation after the fake closer surface as a phantom source, the
second blanks a citation the reader can actually click.

* fix(citations): measure fence columns in columns, and only close on a closer

A tab after a list marker advances to the next four-column stop, so sizing a
list item by the character length of its marker put the content column two
columns too far left and kept a dedented citation masked inside the fence.

A closing fence now comes from its own pattern instead of the opener's: the
opener also accepts list markers, which are plain content once a block is open,
so `- ``` ` inside a fence closed it early and surfaced the citation under it.

The optional space after a block quote marker is padding, not indentation, so
measuring a quoted marker from the raw prefix cost the fence one column of its
three-column budget and rejected a valid closer.

* test(citations): pin the list-nested closer shape as its own case

* fix(citations): keep a blockquote fence escape inside an indented code run

Losing the `>` closes a quoted fence, but the escaping line is not free text:
four columns of indentation at the top level starts an indented code block, so
its citations are still code. Without that, the paragraph-scoped inline pairing
this scanner replaced leaves them visible as phantom sources.

* fix(citations): read blank lines and surviving containers around quote escapes

* fix(citations): count a quote that opens behind a list marker

BLOCKQUOTE_PREFIX_RE matches from the start of a line, so a fence opened as
"- > ```md" recorded quoteDepth 0 even though it sits inside a quote. The
escape test then never fired and the fence masked to end of input, blanking
citations that remark-parse renders as live links: 35 of the 924 swept
container combinations were worse than main, all in that family.

Read the opener's own container prefix instead, and park the list stack the
quote hides in so a dedented line is measured against the item that survives
it.

* fix(citations): keep literal quote lines and multi-marker fences contained

A fence that was not opened in a block quote has no quote container a later
line can leave, so a `>` inside it is literal code: stripping it made an
indented content line read as column zero and escape the fence. A line can also
open several list items at once, and the fence belongs to the deepest of them,
so recording only the first marker put `- - - ```md` four columns past its
container and rejected a valid opener.

* fix(citations): scan the container prefix in one pass

Reading the containers a fence marker may sit behind was a single regex whose
alternation could take a space either as a step of its own or as the padding of
the next marker, so it described the same prefix exponentially many ways. A line
carrying no fence therefore made the engine enumerate every decomposition: the
deep-quote shapes tests/e2e/user-message-plain-text.spec.ts renders cost a second
at 26 markers, roughly double for each marker added, and never return for the
three thousand markers those messages carry, which is why the e2e job timed out
waiting for a message that had already rendered on main.

The scan now walks each position once. Which shapes open a fence is unchanged:
the 32-shape and 35-shape differentials against remark-parse, the 924-combination
sweep and this file's 50 existing cases all give the same answers as before.

* fix(citations): keep the list a finished quote lived in, and let paragraph text block a non-1 ordered fence

maskFencedCodeBlocks cleared the item stack on every blockquote-depth change, so a
fence opened after a quote ended measured against the document root and hid a
citation the reader had already left the item for. It also pushed any ordered
marker as a container, so a `2. ~~~md` line that CommonMark keeps inside the
preceding paragraph opened a fence and masked a rendered citation.

* fix(citations): test the paragraph boundary on the CR-normalized line

A CRLF document keeps its "\r" on every line after the split on "\n", and
THEMATIC_BREAK_RE is anchored at the end of the line, so a setext underline
"---\r" was not recognised as a leaf block. The paragraph it had already ended
stayed live, the following "2. ```md" was read as paragraph text, its fence
never opened, and the real closer was then re-read as a top-level opener that
masked the rendered citation to EOF.

Use the same CR-normalized line the position classification already uses, and
pin the LF/CRLF pair plus the cross-depth quote shape against the parser tree.

* fix(citations): keep list items and quote markers the reader keeps

Three container rules in the fence scanner disagreed with remark-parse:

- A dedented line of paragraph text is a lazy continuation, so it does not
  leave the list item. Popping the item there recorded the next fence at
  column zero and hid a rendered citation to end of input.
- Padding after a list marker tops out at four columns: the fifth belongs to
  an indented code block inside an item whose content starts one column past
  the marker, not to a column-six container.
- A `>` deeper than the quote markers a quoted fence already consumed is
  literal code. Stripping it made the content look dedented past the fence's
  column and ended the fence one line early, surfacing a citation the reader
  shows as code.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-22 16:05:03 +08:00
Totoro
0baa99ca90
feat(models): add scoped model controls to Web chats (#7249)
* test: stabilize reporter evidence and guard missing chat nodes

* feat(models): scope Web model choices to tasks, turns and conversations

* test(e2e): mock model-control preview for ordinary chat submissions

* fix(models): harden scoped choices and stale Agent submissions

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-22 08:08:23 +08:00
Totoro
059758f321
fix(runtime): stop Goal continuations after loop hard stops (#7167)
* fix(runtime): stop Goal continuations after loop hard stops

* chore: remove redundant SVG sources from Goal fix

* test(goal): remove PR-only screenshot generation

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-20 16:55:13 +08:00
Lucien
1ae50826b1
fix(frontend): isolate workspace query caches by authenticated owner (#7146)
* fix(frontend): isolate query caches by authenticated owner

* fix(frontend): share auth owner across isolation boundaries

* docs(frontend): keep agent guidance within size budget

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 10:26:48 +08:00
TYY-2005
36ee986d55
fix(frontend): restore thinking-only steps after reconnect (#7144)
* fix(frontend): restore thinking-only steps after reconnect

* docs(frontend): explain reconnect answer boundary

---------

Co-authored-by: TYY-2005 <TYY-2005@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 09:57:28 +08:00
EastonJiang
b8bb728368
fix(frontend): stop chat sends after unsuccessful upload responses (#7122)
Co-authored-by: Jaxon1216 <224378083+Jaxon1216@users.noreply.github.com>
2026-10-19 23:10:33 +08:00
Qiu
a1f23b294a
fix(frontend): preserve memory mutations against stale reads (#6959)
* fix(frontend): preserve memory mutations against stale reads

* test: register memory guidance in scoped inventory

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-19 11:33:35 +08:00
PeaceMaker-best
dbebd18dd7
fix(deer-flow): retry failed agent setup saves (#6628)
* fix(deer-flow): retry failed agent setup saves

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

* fix(frontend): fence superseded agent save callbacks

---------

Signed-off-by: PeaceMaker-best <221849497+PeaceMaker-best@users.noreply.github.com>
Co-authored-by: PeaceMaker-best <221849497+PeaceMaker-best@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-19 10:55:50 +08:00
TYY-2005
5f9a521d60
fix(frontend): stop stalled subtask pagination without losing steps (#6729)
* fix(frontend): stop stalled subtask pagination without losing steps

* docs(frontend): note the non-advancing page stop in the subtask backfill

The AGENTS.md backfill sentence only said the loop pages until a short page. It also stops on a full page that fails to advance the after_seq cursor, so record that stop condition as well.

The file sits at 49,149 of its 49,152-byte hard limit, so this is the minimal wording change that fits.

---------

Co-authored-by: TYY-2005 <334029581+TYY-2005@users.noreply.github.com>
2026-10-19 07:32:43 +08:00
TYY-2005
ff020ff6ea
fix(frontend): preserve submitted input during initial stream gap recovery (#6800)
* fix(frontend): preserve submitted input during gap recovery

* docs(frontend): clarify replay recovery input precedence

* docs(frontend): keep replay gap guidance within budget

---------

Co-authored-by: TYY-2005 <TYY-2005@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
Co-authored-by: TYY-2005 <334029581+TYY-2005@users.noreply.github.com>
2026-10-18 10:29:17 +08:00
Xuehao Xu
c056ee556a
feat(knowledge): add WeKnora read-only extension (phase 3) (#6950)
* feat(knowledge): add WeKnora read-only extension (phase 3)

* fix(weknora): complete extension catalog and harden response validation

* test(extensions): cover WeKnora discovery in browser catalog

* docs(weknora): explain extension and MCP use cases

* docs(weknora): correct guide language and sync Chinese overview

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-18 10:28:30 +08:00
Xuehao Xu
169002a4c9
feat(workflows): add Phase 2 approvals, immutable artifacts and configuration evidence (#6920)
* feat(workflows): add versioned approvals and run configuration evidence

* docs: trim repeated setup guidance to respect inherited size limit

* docs: leave room in inherited agent guidance

* test(channels): align Buzz capability expectation with upstream registry

* fix(workflows): fence file consumers and preserve review access

* fix(runtime): reuse assembly evidence key at run admission
2026-10-17 09:41:15 +08:00
JY-M666
ec07de9618
fix(frontend): recognize tab-separated web fetch headings (#6923)
Co-authored-by: JY-M666 <316074816+JY-M666@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-16 11:01:02 +08:00
王一帆
6bc1020d34
feat(knowledge): decouple scope and read-only catalogs (phase 2) (#6904)
* feat(knowledge): decouple scope and read-only catalogs

* docs(knowledge): keep frontend guidance within repository budget

* fix(knowledge): align execution identity and harden provider boundaries

* docs: keep knowledge guidance within CI size budgets

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-15 15:10:39 +08:00
Totoro
c994826d06
feat(channels): generalize group notifications and add opt-in task/goal IM notices (#6903)
* feat(channels): generalize IM group targets and add task/goal run notifications

- Generalize proactive group notification capability (proactive_group_notifications), binding validation, and scheduled task group selection across Feishu, QQ, and WeCom while keeping WeChat personal-only.
- Add QQ group active notification delivery via POST /v2/groups/{group_openid}/messages (msg_type=2) and WeCom group active notification delivery via WS Client.sendMessage(chatid, body) with bot_id:group:chatid workspace isolation.
- Generalize notification outbox sources via migration 0043_runtime_im_notices (source_kind, source_key, run_id, nullable task_id/task_run_id) with backward-compatible SQLite/PostgreSQL upgrades.
- Add opt-in ordinary task & goal run IM notifications (run_completed, run_failed, human_input_requested) on the shared outbox, delivery worker, renderer, and settings UI.

* fix(notifications): address review feedback and group/runtime notification edge cases

* test(migrations): update CURRENT_HEAD to 0043_runtime_im_notices across migration chain tests

* fix(channels): preserve group binding names and legacy authorization

Carry owner-chosen group names through existing connect state, keep private bindings out of legacy group fallback, and restore completion metadata retries. Cover real binding and worker finalization paths and synchronize notification docs.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-15 10:30:55 +08:00
Lucien
8008a6195f
fix(artifacts): authenticate isolated HTML preview resources (#6701)
* fix(artifacts): authenticate isolated HTML preview resources

* docs(artifacts): retain precise path guidance within size budget

* fix: preserve HTML preview auth and rendering semantics

* fix(artifacts): quote rewritten CSS URLs

* docs: keep merged artifact guidance within size limit

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-15 10:15:44 +08:00
Xuehao Xu
76102fab0b
feat(extensions): add opt-in script workflow MVP (#6858)
* feat(extensions): add opt-in script workflow MVP

* fix(workflows): clarify timeout handling and stabilize request identity

* fix(ci): register workflow catalog and trim inherited guidance

* fix(workflows): retain launch ownership and resolve injected input anchors
2026-10-14 22:42:17 +08:00
LingYi
96f930f5be
fix(frontend): align one-time DST conversion with the scheduler (#6756)
Co-authored-by: LingYi-Liang <270316896+LingYi-Liang@users.noreply.github.com>
2026-10-14 22:17:51 +08:00
Xuehao Xu
201b9840cd
feat(knowledge): unify read-only retrieval and source contracts (phase 1) (#6716)
* feat(knowledge): unify read-only retrieval and source contracts

* fix(knowledge): address citation and contract review feedback

* fix(knowledge): resolve citations in delegated task reports

* docs(frontend): keep agent guidance within CI size budget

* docs(backend): preserve merged guidance chain headroom
2026-10-14 14:16:08 +08:00
Totoro
cb90f5dca4
feat(channels): control IM notifications and add WeChat/QQ delivery (#6850)
* feat(channels): control IM notifications and add WeChat/QQ delivery

* fix(scheduler): skip notification preference locks without recipients

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-14 11:14:48 +08:00
haotianqi666
4210854a9f
fix(frontend): resolve local images in Markdown artifact previews (#6827)
* fix(frontend): resolve local images in Markdown artifact previews

* fix(frontend): key artifact images by resolver context

---------

Co-authored-by: haotianqi666 <250384991+haotianqi666@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-14 08:59:53 +08:00
Lucien
c5fd453c37
fix(artifacts): preserve account-scoped drafts across navigation (#6700)
* fix(artifacts): preserve account-scoped drafts across navigation

* docs(artifacts): keep draft lifecycle guidance within its budget

* fix(artifacts): retain drafts through unknown auth and isolate typing updates

* fix(artifacts): preserve drafts when visiting public showcases

* fix(artifacts): preserve edits after pending save
2026-10-13 15:44:20 +08:00
Totoro
5ae95e6a34
feat: add Feishu group notifications and fix mention caret placement (#6762)
* feat(feishu): deliver scheduled-task notifications to bound users

* feat: add Feishu group notifications and fix mention caret placement

* fix(frontend): preserve group targets while capabilities load

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-13 07:17:56 +08:00
TYY-2005
e9133787ff
fix(frontend): reject prototype keys in subagent status parsing (#6669)
* fix(frontend): reject prototype keys in subagent status parsing

* docs: keep frontend task guidance within CI budget

---------

Co-authored-by: TYY-2005 <334029581+TYY-2005@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-12 20:26:35 +08:00
Hyeonsang Cho
eb7fc28010
fix(frontend): keep the thread list offset past hidden side chats (#6702)
* fix(frontend): keep the thread list offset past hidden side chats

Primary thread lists hide sidecar threads on the client, so a page of 50
visible chats can consume more server rows. fetchInfiniteThreadsPage kept
that server offset in a Symbol property on the page array, but TanStack
Query's structural sharing (replaceEqualDeep) rebuilds a changed array
without symbol keys, and the list cache writers (rename, unread, pin,
delete, upsert) rebuild pages with map/filter. After the first refetch or
cache write, getInfiniteThreadsNextPageParam fell back to the sum of the
visible page lengths, so the next page started at 50 instead of 53 and
re-fetched rows already loaded. The same fallback treated a full page
that lost a row to a local delete as the end of the list.

Each cached page is now { threads, hiddenCount, hasMore }. The next offset
is the sum of visible and hidden rows over the loaded pages, so local
deletes and inserts still move it with the server list, and the end of the
list comes from the server response rather than the page length.
useInfiniteThreads projects the pages back to thread arrays with select,
so list components are unchanged.

* test(frontend): pin the sidebar's next-page offset past side chats in E2E

Three side chats in the first 50 server rows push the sidebar's second
page to offset 53. After a rename through the sidebar menu, scrolling to
the sentinel must request offset 53, not 50. On main this test fails with
the request at offset 50.

* docs(changelog): reference #6702

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-12 17:35:27 +08:00
Hyeonsang Cho
91a88e08e6
fix(frontend): show a date-only run_at at midnight in the task's zone (#6692)
* fix(frontend): show a date-only run_at at midnight in the task's zone

A chat-created one-time task can store run_at as a bare date
("2026-12-07"). The server reads it with datetime.fromisoformat as
midnight in the task's timezone. onceRunAtInstant instead returned it
unchanged: its offset check matched the trailing "-07" as a UTC offset,
and its local-time pattern needed a time anyway. new Date() then read it
as UTC midnight, so the schedule line, chat card and edit form showed
08:00 in Asia/Shanghai and the previous evening west of UTC.

A bare date is now matched first and converted as midnight in the task's
zone, the same way a time-bearing local run_at is.

* docs(changelog): reference #6692

* test(frontend): pin the clock in the date-only run_at E2E

The detail page formats the schedule line against the real clock: it
drops the year inside the current year and switches to relative labels
in the final week. The exact-text assertion therefore failed throughout
2027 ("Once, Dec 7 00:00 ...") and again as "Once, Tue 00:00 ..." in the
week before the run. Fix "now" at 2026-10-01, the same instant the unit
test uses, so the assertion stays deterministic.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-11 20:34:07 +08:00
Hyeonsang Cho
3f71b2e13f
fix(frontend): return to the opened workspace page after signing in (#6681)
* fix(frontend): return to the opened workspace page after signing in

Opening a workspace page without a valid session sent the user to a bare
/login: the workspace layout called redirect("/login"), and a layout
cannot read the request URL, so the path and query were both lost. A
scheduled-task notification link (/workspace/scheduled-tasks?task_id=X)
opened after the session expired therefore landed on the default
workspace after sign-in.

For an unauthenticated verdict the layout now renders
WorkspaceLoginRedirect, which replaces the page with
/login?next=<path+query> from the browser. It renders nothing else, so
the workspace pages under it never mount.

AuthProvider.refreshUser and useRequireAuth kept the path but dropped
the query. They now share currentReturnPath() with the fetcher, which
already kept both.

* docs(changelog): reference #6681

* fix(frontend): keep the fragment in the sign-in return path

currentReturnPath() kept the path and query but not location.hash, so
an anchored deep link (/workspace/chats/x#section) lost its anchor across
the login round trip. It is now the single source for all four bounce
sites, so include the fragment. The server never receives the fragment,
so only the browser-side bounce can keep it. validateAuthNextPath, the
backend's validate_next_param and its quote() into /auth/callback all
carry a '#' through unchanged.

The models API test stubbed window.location without hash; a real
Location always has one, so the stub now carries it.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-11 19:05:03 +08:00
hataa
2f4f4d4b6c
feat(authz): admin user-management UI and disabled-account copy (#4063 gap 3) (#6641)
* feat(authz): admin user-management UI and disabled-account copy (#4063 gap 3)

Frontend slice of the account lifecycle backend (#6566):

- Settings gains an admin-only Users page (core/auth/admin-users.ts ->
  GET/PATCH /api/v1/admin/users): email, read-only role, disabled state,
  and a suspend/restore toggle per row; the Gateway's rejection detail
  (last-active-admin 409) renders verbatim. The nav entry is hidden for
  non-admins and the page guards independently; ?settings=users joins the
  deep-link whitelist (now an exhaustive Record over SettingsSection).
- A session that turns out disabled maps account_disabled to a login
  redirect that states the reason on all three surfaces: the SSR
  AuthResult tag (consumed exhaustively by the workspace/artifacts/auth
  layouts), the shared fetcher's 401 interception, and
  AuthProvider.refreshUser. No next target is preserved - a disabled
  account cannot sign back in. Login failures surface the localized
  account_disabled copy when the code is present.
- userSchema stays disabled-optional (absent = not disabled); zh/en
  locales and docs updated in the same change.

* fix(authz): address review on admin user UI and disabled copy (#4063 gap 3)

Review follow-ups on PR #6641:

- Direct /login visits with a suspended session now surface the SSR
  account_disabled verdict through AuthDisabledContext instead of
  discarding it (willem-bd): the auth layout wraps pages with the
  provider and the login form seeds its reason from it — no redirect,
  so no loop. Regression tests cover both verdict states.
- admin-users.ts parses both endpoints against userSchema /
  z.array(userSchema) (zhfeng): malformed payloads fail the load
  instead of rendering phantom rows; toggle error copy is scoped to
  GatewayApiError with a localized fallback for anything else.
- The localized account_disabled copy is login-only (zhfeng): sign-up
  keeps the backend message verbatim so a suspended account's email
  cannot be probed through the register surface.
- Settings page folds the load banner and toggle error into a single
  alert region (zhfeng); adds coded-envelope 409, malformed-row, and
  load-failure tests; fetcher.ts documents the 401 body-consumption
  contract.
- prettier across touched files (CI format gate).

The admin-users query key keeps user?.id, matching the
model-settings-page precedent (converge later per review).
2026-10-11 12:54:51 +08:00