417 Commits

Author SHA1 Message Date
Totoro
2dd2e5415c
feat(scheduled-tasks): finish the user-facing side of conversation schedules (RFC #6340, 1/2) (#6378)
* feat(scheduler): lifecycle rules, goal/limit parity and coded errors for scheduled tasks

- Migration 0031: nullable scheduled_tasks.unmet_streak_after_seq (internal
  streak boundary) and stop_condition (the user's stop rule, own column).
- Stop rule is normalized (stop_rule.py) and appended only at launch; the
  stored prompt never contains it. Wording adapts when scheduler.tool_enabled
  is off.
- Willem 1: page-created tasks get goal evaluation, <standing_notes>, the
  previous-run reference and own-schedule stop; request_stop and the
  scheduled capability no longer require an origin conversation.
- Repository: single reactivation choke point raising
  ScheduledTaskLimitsExhausted; streak boundary moves on goal/prompt/stop
  condition/note changes (not on resume); goal-check failures neither count
  nor reset the streak; pause of a finished task returns "finished"; trials
  on a paused task keep the host pause marker.
- New repository reads: automatic_runs_used_for, active_run_status_for,
  list_manageable_from_thread, thread_relation/thread_run on thread tasks,
  task_ids_for_thread, run_number, and run_number/total_tokens/summary on
  run history rows.
- REST: goal/max_runs/end_at/stop_condition on create and PATCH (null
  clears, naive end_at = task timezone), resume recomputes from now with an
  optional renewal body, once_time_passed, task_finished, limits_exhausted,
  scheduler_not_running on create, trigger returns outcome/existing/thread_id,
  task responses add automatic_runs_used and active_run_status.
- Coded errors {code, message, params} for routes, shared validation and the
  chat capability (scheduled_task_errors.py), pinned by
  contracts/scheduled_task_errors_contract.json; goal notes contract v2 with
  check_failure_codes, scheduled_origin_key and host_run_errors (host_notes.py).
- /api/features adds scheduled_tasks {available, running, tool_enabled,
  min_interval_seconds}; ScheduledTaskService.is_running.
- Docs: six README scheduler sections, API.md scheduled tasks section
  (breaking error shape), CONFIGURATION.md, AGENTS notes, CHANGELOGs.
- Tests: migration 0031, stop rule, error contract, run listing, lifecycle
  over HTTP, repository, service and capability cases; updated tests for the
  new error shape, finished-task pause and head revision.

* feat(scheduler): conversation update/resume, run-conversation authority and readable launches

- schedule_task: update and resume actions, stop_condition and clear_fields
  parameters, plain parameter types (value rules come back as coded results),
  rewritten docstrings for schedule_task and stop_scheduled_task.
- Capability: update/resume share the REST validation and error codes;
  interactive turns in a task's run conversation may manage that task;
  scheduled runs keep only own-stop; grants require a running scheduler.
- Results: compact JSON-safe task views with local times, display card/text,
  last run in list, timezone_source/browser_timezone, now_local; no ISO, user,
  lease or origin fields; stop_instructions removed.
- Timezone: explicit > browser (context.client_timezone, validated in
  start_run and never forwarded to the run config) > not needed for intervals
  and offset run_at > timezone_required.
- Trial gate accepts natural direct requests after acknowledgements and
  particles; trial results report thread_id and existing.
- Scheduled launches: stable message id scheduled-<task_run_id>, server-owned
  deerflow_scheduled_origin metadata with the user-language parts, run thread
  title for fresh-thread tasks.
- Worker records goal continuations in the verdict; lead prompt language rule
  covers stored task text and scheduled runs.
- Docs: six READMEs, CONFIGURATION, API, runtime/tools AGENTS, CHANGELOGs.

* feat(frontend): scheduled-task core types, formatting, errors and glossary

- throwGatewayApiError now throws GatewayApiError (status, code, params,
  rawMessage); message stays the string detail or detail.message
- scheduled-task types gain stop_condition, automatic_runs_used,
  active_run_status, origin/notes, run_number/total_tokens/summary, thread
  relation, trigger result, renewal and scheduled origin; isTaskBusy and
  hasActiveRun helpers
- api: fetchScheduledTask, resume with optional renewal body, typed trigger
  result and update payload with null clears
- hooks: polling at 3 s while a run is active (list, thread list, run
  history), useScheduledTask with live gating and final 404, useInView,
  localized error toasts; run history refreshes tasks when a run finishes
- new pure modules: errors.ts (contract code to localized copy), format.ts
  (relative task times, viewer time, display zone), status.ts, actions.ts,
  run-error.ts; describeTaskSchedule in cron.ts; goal-outcome adds unchecked
  goal checks, describeTaskOutcome and parseStopRunId
- features: scheduled_tasks block with defaults when missing
- buildRunContext sends client_timezone
- titleOfThread takes a localized untitled label; breadcrumb names scheduled
  tasks and agents; reference chips use the localized untitled label
- schedule input: shared browserTimeZone, labelled selects, aria-pressed
  type toggles, interval minimum from the hint template
- i18n: all PR1 scheduled-task keys in en-US and zh-CN, glossary fixes,
  recipe instructions moved into i18n
- e2e mocks follow the new task, trigger, resume, thread-task and features
  shapes; expectNoRawIdentifiers helpers for e2e and DOM tests
- tests for errors, format, cron, status, actions, goal outcome, run errors,
  hooks, run context, thread titles and features; scheduled e2e copy updated

* feat(frontend): list-first scheduled tasks page with outcomes and editable goals

- page.tsx is now a thin composition of components/workspace/scheduled-tasks:
  status tabs (All/Active/Paused/Finished) and search over the task list,
  ?task_id= deep link with router.replace on selection, ?thread_id= chip
  with Show all, empty state, scheduler-off notice and unavailable state
- task list rows show the schedule in words and one status line (next run,
  running now, paused by agent, auto-paused, finished by its cap)
- task detail: status badge, actions from availableActions with reasons,
  More actions menu (Duplicate, Copy task ID, Delete), outcome notice,
  Runs / Stops when / Each run's goal / Does / Notes from chat / History,
  origin chat link; success toasts for every action
- run rows: run number or Trial run, summary, goal and assumption badges,
  continuations, tokens, Open chat link; raw errors and the evaluator
  reason only behind Details
- create/edit/duplicate dialog with goal, stop condition, safety caps and
  advanced agent/context options; PATCH sends only changed fields, null
  clears a cap
- renew dialog raises or removes caps and resumes in one request; a
  limits_exhausted Resume opens it
- removed i18n keys the old page used; added feedback.resumedNoTime
- recorded en-3 and zh-1 page fixtures; scheduled e2e specs rewritten for
  the new page; DOM tests for the detail and the form dialog
- CHANGELOG entries and the frontend AGENTS.md scheduled-task note

* feat(frontend): live schedule card, run conversation provenance and header entry

- schedule_task results (create, update, pause, resume, trial, delete) become
  an assistant:scheduled-task message group rendered as a live card: schedule,
  next run, stop condition with the safety cap, goal, where results go, Run
  once now, Pause/Resume and Open task; the card polls the task only while it
  is on screen and shows deleted, scheduler-off and limits-exhausted states
- one card per task per assistant turn; the card group is not a turn boundary
  for usage, branching, editing, outline or skill attribution
- a human message with deerflow_scheduled_origin stays a human group with
  scheduledOrigin and renders as a "Scheduled run / Trial run" block with the
  task instructions, stop condition and notes collapsed; the launched text is
  never shown and the message is not editable
- readable step labels for schedule_task actions and stop_scheduled_task
- ThreadScheduledTasksButton replaces the header link: hidden without tasks,
  shows the count, links to the task (or the chat filter), and a run's chat
  links to its task
- recorded en-3 / zh-1 chat and run-thread fixtures; chat-card and run-thread
  e2e specs; unit and DOM tests for parsing, grouping, turn boundaries, card,
  run block, header button, step labels and reconnect hydration
- i18n: header.countLabelOne added, unused card.next removed; CHANGELOG and
  frontend AGENTS.md notes

* fix(scheduler): address verification findings

Move the scheduled-task lifecycle guidance from backend/AGENTS.md into the persistence guide and leave a one-line pointer, so the effective AGENTS.md chains for agents/middlewares, sandbox and subagents stay under the 98304-byte hard limit enforced by scripts/check_agent_guidance.py in lint-check.

* fix(scheduler): address review findings

* fix(scheduled-tasks): polish from live acceptance

- zh schedule wording: an interval of one unit reads 每分钟 / 每小时 / 每秒,
  and a saved interval of whole days reads 每天 / 每 2 天 (Every day / Every 2
  days) instead of 每 1 分钟 or 每 24 小时; several units keep the number
- zh spacing: 已于{time} 满足 and 在{time} 的运行中 now leave a space between
  the interpolated time and the Chinese after it; a copy test guards every
  scheduled-task zh string against a placeholder glued to Chinese
- run summary: when the first line of the final reply only leads in to a list
  (ends with ":" or ":"), the list items follow it as plain text (bullets,
  numbering, checkboxes, bold, code and links stripped), joined with ";" for
  CJK text and "; " otherwise, within the 160-character cap
- a schedule_task result (create, update, pause, resume, trial, delete) that
  arrives in a chat invalidates that chat's task list, the tasks list and the
  named task, so the header button and cards update without waiting for the
  idle poll; results already in the loaded history do not refetch
- e2e and unit fixtures are now the live acceptance recordings (DeepSeek
  Flash, demo data, fictional names): live-weekday.json (create, trial, edit,
  pause, resume in one zh chat) and live-minute.json (per-minute chat, run 1
  and run 2, paused by the agent), fetched with GET from the demo Gateway and
  trimmed; the hand-shaped en-3 / zh-1 fixtures are removed and the chat-card,
  run-thread and tasks-page specs run in both locales over the recorded data

* docs(changelog): reference #6378 for the scheduled-tasks entries

* docs(agents): shorten the scheduled-task pointer to stay under the guidance limit

* test(scheduled-tasks): pin the live takes to their recorded time zone

The live fixtures were recorded in Asia/Shanghai, and an interval task
reads in the viewer's zone, so the 16:48 assertions failed on UTC CI
runners. Pin the browser zone in the two live-take e2e specs and the
zh live-take unit test.

* fix(scheduled-tasks): keep untouched end times and drop a chat filter the new task is not in

- The edit, duplicate and renew dialogs keep a stored end time's instant
  while its field still shows it. A minute-precision wall value cannot
  keep seconds or name the later 01:30 of a DST fall-back, so a
  title-only edit moved the cap an hour earlier, and raising the run
  limit re-sent the end time without its seconds.
- Creating or duplicating a task from a chat's task view drops the chat
  filter when the new task does not belong to that chat, instead of
  showing "This task isn't one of this chat's tasks".

* fix(scheduled-tasks): tie pause notices to the pause they describe

- Paused by agent links and times only the run named in last_error. When
  that run is not loaded, the notice shows no link and the task's own time
  instead of an earlier run that also asked to stop.
- The outcome notice reads the latest history page whichever page is
  shown, so browsing older runs no longer swaps the auto-pause reason and
  "Open latest run" to an earlier miss. It shares that page's request.

* fix(scheduled-tasks): three lifecycle gaps found while re-checking review classes

- The edit form compares a cron in the form its schedule input emits, so an
  untouched "0 09 * * 0,6" is not re-sent as "0 9 * * 6,0" and a title-only
  edit no longer re-arms or refuses a finished task.
- Chat resume asks for a timezone, as update does, when a naive end_at would
  land in the UTC placeholder of a zone-free task.
- Re-arming a task clears a host pause marker, as resume does, so a later
  manual pause no longer reads as "Paused by agent".

* fix(scheduled-tasks): keep the notice's latest-runs query from refreshing older pages

useLatestScheduledTaskRuns kept React Query's default refetch on mount,
focus and reconnect, so going offline and back while an older history page
was shown fetched the latest page again. Refreshing stays with the history,
which refreshes the latest page only while it is shown.

* test(scheduled-tasks): let the viewer's time carry its own day label

Between 16:00 and 24:00 UTC, UTC and Asia/Shanghai are on different days,
so the run prompt shows "Tomorrow 01:00 · Today 09:00 your time" and the
fixed "01:00 · 09:00 your time" assertion failed depending on the hour.

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-07 07:47:32 +08:00
PeaceMaker-best
77df35baa8
fix(frontend): keep recovered run cancellation target (#6075)
* fix(frontend): keep recovered run cancellation target

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

* docs(frontend): explain recovered stream error metadata contract

---------

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-06 11:39:58 +08:00
PeaceMaker-best
3606c4a098
feat(agents): add portable custom agent packages (#6127)
* feat(agents): add portable agent packages

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

* fix(agents): show readable package import validation errors

---------

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-06 10:20:38 +08:00
Totoro
cdbcc0d9e9
feat(scheduled-tasks): show goal outcomes and end conditions in run history (#6326)
* docs(scheduler): document tool_enabled, goal notices and goal-evaluation change

Follow-up to #6229:
- CONFIGURATION.md: add scheduler.tool_enabled to the example and notes.
- CONFIGURATION.md, README (en/zh/pt): unmet goal runs send a goal-unmet
  notice instead of a completion notice, and three unmet runs add an
  auto-pause notice; the docs still said only success/failed notify.
- CHANGELOG (en/zh): the #6229 entry now states the default-on goal
  evaluation change for scheduled/webhook/autonomous goal runs and that
  evaluator usage counts toward run usage.

* feat(scheduled-tasks): show goal outcomes and end conditions in run history

Follow-up to #6229. The API already returns these fields; the tasks page
did not show them, so a met goal read as plain "Success", an unmet
reason code rendered as a red execution error, and the goal-unmet IM
notice pointed to a workspace showing neither objective nor verdict.

- Task detail shows the per-run goal, max automatic runs and end time
  when a conversation-created task has them.
- Goal runs show "Goal met" (or met relying on stated assumptions);
  unmet runs show a localized reason plus the host code in neutral
  styling. The run whose agent asked to stop the schedule is marked.
- Runs without a goal, and failed runs, render as before.

Read-only and frontend-only: no API change.

* docs(changelog): add #6326 scheduled-task goal outcome entry

* fix(scheduled-tasks): show readable unmet reasons instead of raw codes

Unmet runs now show only the localized reason; the host code moves to the
tooltip so it can still be matched with the IM notice. Codes without a
label are still shown verbatim.

* fix(scheduler): show readable goal reasons instead of internal codes

Task detail now localizes the host-written last_error values from goal and
stop finalization (agent stop, automatic pause, unmet reason codes) under a
matching label, and Chinese copy is tightened. Goal-unmet and auto-pause IM
notices state the reason in words instead of printing the raw code; unknown
codes are still not forwarded.

* docs(changelog): describe readable goal reasons in #6326 entry

* test(scheduler): pin host goal strings with a cross-language contract

Address review on #6326:
- Add contracts/scheduled_goal_notes_contract.json listing the task
  last_error strings and unmet reason codes the tasks page and IM notices
  translate. A backend test checks finalization constants, blocker and
  stand-down codes against it; a frontend test checks every listed value
  has a label.
- Move the two last_error strings in finalization.py into constants.
- Frontend reason lookup strips the blocked: prefix like the IM map and
  now covers goal_not_met_yet, with en/zh labels.

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-05 21:56:04 +08:00
Wenchao An
648870b590
feat(extensions): add full Custom Agent team collaboration plugin (#6243)
* feat(extensions): add full Custom Agent team collaboration plugin

* fix(extensions): register agent teams in the capability catalog

* fix(agent-teams): recover incomplete teams and preserve request capacity

* fix(agent-teams): preserve clarification waits and active handoff lineage

* fix(agent-teams): align mention and input text limits
2026-10-04 16:36:48 +08:00
Totoro
35023bb908
feat(scheduler): create conversation schedules and support agent self-stop (#6229)
* feat(scheduler): create conversation schedules and support agent self-stop

* fix(scheduler): validate goals and trial requests before dispatch

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-04 06:46:13 +08:00
Wenchao An
59b051b0fd
feat(frontend): support extension providers in composer mentions (#6189)
* feat(frontend): support extension providers in composer mentions

* fix(mentions): retain settled plugin suggestions during search

* fix(extensions): validate identifiers and skip empty mention searches

* test: stabilize history pagination after latest page refresh

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-03 20:30:23 +08:00
Daoyuan Li
c84b32205d
fix(frontend): ignore literal base tags in HTML artifact previews (#6176)
* fix(frontend): ignore literal base tags in HTML artifact previews

* fix(frontend): address HTML preview base review feedback

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-03 16:35:55 +08:00
Daoyuan Li
b0c1b2e7ce
fix(artifacts): handle CRLF splits in CSV/TSV previews (#6180)
* fix(artifacts): handle truncated CRLF after quoted fields

* Handle LF-first truncated CRLF artifact samples
2026-10-03 14:48:32 +08:00
Xuehao Xu
9b17bef630
feat(channel): add QQ WebSocket channel (#6081)
* feat: add QQ WebSocket channel

* fix: address QQ channel review feedback

* docs: trim inherited agent guidance below CI limit

* fix: align QQ gateway and passive reply limits with protocol

* docs: keep merged channel guidance within CI budget
2026-10-03 14:32:44 +08:00
Wenchao An
9ff47cfae5
feat(extensions): show the bundled catalog by default (#6188)
* feat(extensions): show the bundled catalog by default

* test(extensions): pin bundled catalog namespaces
2026-10-02 23:01:34 +08:00
Daoyuan Li
59743568ba
fix(frontend): ignore inherited artifact language keys (#6152) 2026-10-01 19:42:06 +08:00
Wenchao An
9f5897cef2
feat(frontend): reserve slash menu for built-in commands (#6154)
* feat(frontend): reserve slash menu for built-in commands

* chore(frontend): remove unused skill removal translations
2026-10-01 19:31:31 +08:00
lihongyuan99
241a7b3b9b
fix(frontend): key the Julia entry of extensionMap by its extension (#6130)
extensionMap is keyed by extension and valued by language, but the Julia row
was written backwards as `julia: "jl"`. checkCodeFile tests membership with
`extension in extensionMap`, so ".jl" never matched and a Julia artifact was
not a code file at all, while ".julia" resolved to a language named "jl".

Add both `jl: "julia"` and `julia: "julia"`, the same pair form the map already
uses for haskell/hs and elixir/ex, so no extension that classified before stops
classifying.
2026-10-01 09:58:23 +08:00
creed
9c75236ea1
fix(frontend): strip closing hashes from web fetch titles (#6103)
* fix(frontend): strip closing hashes from web fetch titles

Signed-off-by: 97three <2212371308@qq.com>

* fix(frontend): scan web fetch heading suffixes in linear time

Signed-off-by: 97three <2212371308@qq.com>

---------

Signed-off-by: 97three <2212371308@qq.com>
2026-10-01 08:51:40 +08:00
kuse
ee3159a01d
fix(threads): re-anchor re-persisted history rows to their earliest seq (#6128)
* fix(threads): re-anchor re-persisted history rows to their earliest seq

reconcileThreadHistoryRows collapsed a repeated (run_id, identity) to
its latest-seq copy, deleting the earliest-seq row before
buildVisibleHistoryMessages could apply its earliest-seq-wins
anchoring. A message re-persisted under a newer seq therefore jumped
to the tail of the thread, contradicting the documented contract in
this function's docstring, in buildVisibleHistoryMessages, and in the
backend get_message_seqs earliest-seq-wins rule.

Re-anchor the surviving row to the earliest seq its identity held in
the merged window after deduping, and make the retained-history
stability check value-based so re-anchored rows do not force a state
update on every pass.

Fixes #6125

* fix(threads): address history reconciliation review

---------

Co-authored-by: liwenjie200543 <175601277+liwenjie200543@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-01 08:20:42 +08:00
Wenchao An
4c05e88c52
feat: add inline @ references with multiple skill activation (#6063)
* feat(frontend): unify composer references with an @ picker

* feat: edit references inline and activate multiple skills

* docs: describe inline references and multiple skill selection

* fix: prevent sent drafts from returning and repair CI checks

* fix: reconcile inline references and address review feedback

* fix: preserve references through discovery errors and polish
2026-09-30 23:09:05 +08:00
Daoyuan Li
5c67d61d65
fix(frontend): detect artifact types from file basenames (#6091) 2026-09-30 20:11:26 +08:00
Wenchao An
1e462ae4a2
fix(frontend): consolidate run duration into reasoning headers (#6057)
* fix(frontend): show run duration in reasoning headers

* fix(frontend): cover all reasoning duration disclosures

* fix(frontend): collapse restored reasoning and cache duration targets

* chore: remove PR-only reasoning screenshot
2026-09-30 17:22:41 +08:00
xihongshichaojidan8
d0e4d3525c
fix(frontend): show media icons for APNG, AVIF and WebM (#6089) 2026-09-30 15:58:45 +08:00
yang rui
fcff55a075
fix(frontend): preserve first-turn order during chat creation (#6074) 2026-09-30 10:45:19 +08:00
Wenchao An
9dc7bb30da
fix(capabilities): restore administrator management of shared MCP plugins (#6018)
* fix(capabilities): restore administrator management of shared MCP plugins

* fix(mcp): clarify control ownership and require mutation scope
2026-09-30 10:14:42 +08:00
Storm
1b691689da
fix(integrations): keep Lark runtime status consistent (#5994)
* fix(integrations): keep Lark runtime status consistent

Probe sandbox runtime readiness for every Lark mutation response outside the credential lock. Expose explicit sandbox_runtime_probed state so the frontend can distinguish conservative fallbacks without inferring semantics from runtime detail text, while cancelling stale reads before updating the cache.

* style(frontend): format Lark cache test

* fix(integrations): snapshot Lark credential status under the lock

Why this change is needed:
PR #5994 moved the post-mutation status read outside the per-user
credential lock so the new sandbox runtime probe would not extend the
critical section. As flagged in review, that lets a concurrent app
switch or auth flow land between the committed mutation and the status
read; in remote-provisioner mode the runtime probe can spend seconds
before probe_lark_auth runs, so the raced switch can clear the new
tokens and make a completed login report success=false, or mix an old
app_id with a newer auth state.

What changed:
- add _LarkCredentialSnapshot (app config + auth probe) and capture it
  in the same _lark_credential_lock section that commits the mutation
- thread the snapshot through _get_lark_mutation_status into
  get_lark_integration_status(credential_snapshot=...); only the
  credential-independent runtime probe still runs after the lock
- apply to complete_lark_auth, complete_lark_config, and
  set_lark_app_credentials; install keeps the one-shot status build

Problem solved:
Mutation responses stay tied to the just-committed credential
generation: a raced app switch can no longer flip a completed
device-code login to success=false or blend a stale app_id with newer
auth state, while the runtime probe stays off the lock.

* fix(integrations): address Lark status review follow-ups

- install_lark_integration: snapshot credential-derived status fields
  under _lark_credential_lock so the install response cannot mix newer
  credential state read across the unlocked runtime probe
- useInstallLarkIntegration: route the cache write through
  cacheLarkMutationStatus so install cancels in-flight reads and keeps
  probed runtime fields under frontend/backend version skew
- README: soften the UI claim - the status API exposes
  sandbox_runtime_probed and the mutation cache honors it

* test(integrations): tighten Lark status fixtures and guidance docs

- cache.test: use a distinct mutation runtime mode in the unprobed-merge
  case so the assertions prove the cached runtime fields win
- e2e mock: include broker in the sandbox_runtime_mode union cast so the
  fixture cannot drift from LarkSandboxRuntimeMode
- AGENTS.md (gateway + skills): mention sandbox_runtime_probed alongside
  the existing runtime readiness fields, keeping the gateway guidance
  file under its byte budget

* fix(integrations): harden Lark mutation cache against skew and races

- cacheLarkMutationStatus: await cancelQueries before writing so an
  in-flight GET whose fetch already resolved cannot clobber the fresher
  mutation status, matching the existing threads/archive pattern
- self-heal version skew: an unprobed (pre-field backend) mutation
  response now triggers a status refetch instead of risking the
  conservative runtime fallback lingering until the next focus/remount
- api: normalize a missing sandbox_runtime_probed to false at the
  boundary so the declared-non-optional type matches skewed runtimes
- tests: pin the skew contract (missing-field cases for cache and api)
  and add a useInstallLarkIntegration regression test proving the
  install cache write goes through cacheLarkMutationStatus
- README: describe the probed flag as cross-version semantics instead of
  claiming the same-version API surfaces both states
- lark_cli: note that install's credential snapshot runs the auth probe
  subprocess while holding the credential lock

* refactor(integrations): derive sandbox_runtime_probed inside the resolver

_resolve_sandbox_runtime_readiness now returns a fourth probed element
next to the branch that produces the unevaluated remote fallback, so the
'was readiness actually evaluated' invariant can no longer drift from the
fallback logic it mirrors. Callers and resolver-level tests updated to
pin probed per branch.
2026-09-29 17:23:36 +08:00
Wenchao An
52a3e2564f
fix(mcp): isolate personal connections from deployment configuration (#5966)
* fix(mcp): isolate personal connections from deployment configuration

* fix(mcp): preserve personal task ownership and repair plugin E2E fixtures

* fix(mcp): handle corrupt personal config and cache owned calls

* fix(mcp): pin personal endpoint addresses and handle invalid listings

* fix(mcp): recheck current authority for privileged personal connections
2026-09-28 16:02:34 +08:00
Hyeonsang Cho
dfad2dcc74
fix(frontend): keep additional_kwargs on the optimistic bubble through an upload (#5982)
* fix(frontend): keep additional_kwargs on the optimistic bubble through an upload

When a send carried files, the upload-complete update replaced the
optimistic human message's additional_kwargs with only the uploaded
files. Quote and conversation reference chips vanished until the server
echoed the message, and project attachments staged with the send were
missing from the bubble both while uploading and after. The submitted
message was always intact.

The optimistic copy (before and after the upload) and the submit now
build additional_kwargs through one helper,
buildHumanMessageAdditionalKwargs, and share a single uploaded-files
value, so the bubble always matches what was sent.

* docs(changelog): reference #5982

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-09-28 15:49:38 +08:00
RongJie G
81c5c83384
fix: make MCP notification recovery rollback-safe (#5908)
Signed-off-by: CorgiBoyG <111257566+CorgiBoyG@users.noreply.github.com>
2026-09-27 20:46:56 +08:00
李靖
10bd065615
feat(agents): persist tool artifacts as resolvable handles (#4676) (#4929)
* feat(agents): persist tool artifacts as resolvable handles (#4676)

MCP and sandbox tools return file paths, URLs, and task ids that are
lost when context compaction summarizes the conversation. This adds a
per-thread artifact handle registry so the model can reference
artifacts by short deterministic handles (art_xxxxxxxx) that survive
compaction and resolve to real references at the tool-call boundary.

- ThreadState.tool_artifacts: reducer channel capped at 100 entries,
  same-handle latest-wins, survives summarization compaction
- artifact_registry: deterministic handle derivation from
  (thread_id, tool_call_id, call_index); extraction precedence
  structured_content -> file/image blocks -> conservative text scan;
  HTML-escaped model-context rendering with char budget
- ArtifactCaptureMiddleware: before_model hook captures lightweight
  metadata (never content) and tracks handle consumption
- ArtifactResolutionMiddleware: rewrites handles in tool-call args to
  real references before execution; unknown handles untouched
- DurableContextMiddleware: projects the registry into the hidden
  durable-context data block ([available]/[consumed] status)
- config: new tool_artifacts section (all switches default on)
- frontend: artifact badges on tool-call steps keyed by tool_call_id

Fixes #4676

* fix(agents): harden artifact registry after review (#4676)

Review findings on #4929:

- capture/consumption updates emitted in the same before_model call are
  now concatenated instead of dict-merged; dict-merging clobbered the
  shared tool_artifacts key and permanently dropped fresh captures
- handles embed a per-result ordinal so multiple references inside one
  tool result never collide (two paths in one text block previously
  collapsed into one handle)
- structured_content extraction pulls concrete refs from known keys
  (file/path/url/task_id/..., string or list-of-string values) instead
  of storing a truncated JSON preview, falls back to an untruncated
  whole-object data entry, and no longer skips file/image content blocks
- plain-string tool results (e.g. bash/write_file output) are scanned
  for sandbox paths / remote URLs too, gated by detect_refs_in_text
- wire max_entries through configure_tool_artifact_max_entries at
  assembly (clamped 10..1000) and gate detect_refs_in_text scans
- resolution requires both enabled and resolve_handles_in_args,
  matching its docs
- add real ToolCallRequest.override coverage and the spec 14.4
  integration cycle (capture -> inject -> resolve -> consumption)

* fix(agents): address second-round artifact registry review (#4676)

Follow-up findings on #4929:

- drop generic `output`/`outputs` from structured ref keys: prose under
  those keys no longer becomes a bogus file entry (falls back to the
  whole-object data entry; path-shaped refs are still caught by the
  free-text scan)
- strip closing quotes, brackets, and backticks from detected refs so
  markdown- and JSON-wrapped paths resolve to clean real references
- remove the process-wide cap setter: the reducer enforces only an
  absolute ceiling (1000) and ArtifactCaptureMiddleware applies the
  configured max_entries per agent when emitting updates, so agents
  with different configs in one process never share or flap a cap
- skip extraction for tool messages whose tool_call_id already has
  registry entries and memoize known-empty results (bounded FIFO), so
  steady-state capture cost drops to the new message tail instead of a
  full-history rescan per model call
- correct the inverted ordering claim: capture is position-independent
  in the wrap chain because it is a before_model hook reading state
  messages; fixed the assembly comment, AGENTS.md item 15, and the
  ordering test docstring

CI parity verified locally: ruff check + format --check, full offline
suite, blocking-io suite, collect-only, uv lock --check,
check_agent_guidance, frontend format/lint/typecheck/build/tests.

* fix(agents): sliding-window cap and URI gates for artifact registry (#4676)

Third-round review findings on #4929:

- max_entries is now a sliding retention window: fresh captures are
  always registered, and when the projection exceeds the configured
  cap the middleware appends a trailing {"op": "trim_to", "keep": N}
  directive that the reducer applies by evicting the oldest entries.
  The previous remaining-budget emission froze the registry at the cap,
  permanently dropping post-compaction artifacts — the exact failure
  mode #4676 targets — and silently disabled the round-2 rescan
  optimizations at the point threads grew longest
- _track_consumption memoizes settled tool-call args in a bounded FIFO
  alongside the existing empty-result memo, so historical AIMessage
  args are not regex-rescanned every model call
- file/image content-block URLs must be http(s) or path-shaped;
  data:/blob: URIs (MCP embedded resources carrying base64 payloads)
  can no longer enter thread state, tool args, or crowd out the render
  budget; protocol-relative //host forms are rejected as well
- structured-content ref values go through the same URI-shape gate as
  content blocks, closing the embedded-payload hole via known keys such
  as url/urls
- tool_artifacts.enabled now gates the whole middleware including
  consumption tracking via an early return in before_model

Docs updated to describe the sliding window, URI gates, enabled scope,
and both bounded memos.

* fix(agents): final evictions and pending-aware consumption for artifact registry (#4676)

Fourth-round review findings on #4929:

- the sliding window interacted with the seen_call_ids skip to
  resurrect evicted entries every round: once an entry was trimmed, its
  tool_call_id left the seen set while its ToolMessage stayed in
  context, so the next model call re-extracted it, re-registered it as
  newest, and evicted something else — a never-converging rotation.
  The empty-result memo is now a unified _processed_results memo that
  records every extraction regardless of outcome, making evictions
  final; steady state over a saturated thread is quiescent (pinned by
  a multi-round test asserting no updates and zero extractions)
- the consumption scan built its handle map from pre-capture state, so
  a call referencing a handle captured in the same before_model call
  missed, got settled as quiet, and its consumption mark was lost
  forever. _track_consumption now receives the pending capture entries
  and resolves against state + pending; a call settles as quiet only
  after every referenced handle resolved, so transient misses retry
  next round instead of permanently suppressing the mark

AGENTS.md entry 15 updated to describe both memos and the
eviction-is-final semantics.

* fix(artifacts): address occurrence identity, PII and URL review findings

* fix(artifacts): enforce resolved policy checks and bounded registry handling

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-09-27 11:09:41 +08:00
lihongyuan99
19618fc30b
fix(frontend): stop treating a fence line with an info string as a closer (#5919)
CommonMark forbids an info string on a closing fence, so "```python" inside a
plain ``` fence is content, not the end of the block. The closer check that two
fence consumers share only compared character and run length, which ends the
block early and pushes whatever follows back into prose: on the render path a
system marker inside the sample loses its tags and its content becomes visible
text, and on the export path stripInternalMarkers deletes real user content from
the thread markdown.

mermaid.ts already had the correct predicate privately. Move it into the shared
leaf module and use it from both consumers, so the three fence scanners in
core/streamdown and core/messages agree on what closes a block.
2026-09-27 10:57:24 +08:00
Hyeonsang Cho
59aa91d622
fix(projects): label untitled threads in the conversation-files view (#5879)
* fix(projects): label untitled threads in the conversation-files view

GET /api/projects/{id}/threads/files returns each member thread's
display_name as `str | None`: the thread meta row is created with NULL and
only filled once title generation has run after the first reply, or never
if that step fails. The frontend's ProjectThreadFileGroup declared the field
as a required string and ConversationFileGroup rendered it verbatim, so a
thread whose files were uploaded before its first reply showed them under
an empty heading. The sibling ProjectThread type already models the field
as nullable and every consumer of it falls back to t.projects.untitled.

Make the group type nullable to match the wire, and render the heading
through a shared threadTitle() helper that both the thread-name map and the
file group now use, so the "Untitled" rule lives in one place.

The DOM test covers null, missing and whitespace-only titles.

* docs(changelog): reference #5879 in the conversation-files entries

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-09-26 12:06:12 +08:00
Jholly
8121dbcb57
fix(frontend): prevent run creation retries (#4725)
* fix(frontend): prevent run creation retries

* test(frontend): cover safe run stream reconnection

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-09-25 21:46:34 +08:00
Nan Gao
bcaf6db36a
feat(models): add declarative reasoning capability contract (#5780)
* feat(models): add declarative reasoning capability contract (#5073)

Introduce an optional per-model `reasoning:` block beside the legacy
`supports_thinking` / `supports_reasoning_effort` booleans. It declares
thinking availability (unsupported | optional | required), the accepted
effort values with aliases, default, and serialization path, the payload
dialect, and the reasoning-history requirement. When present the booleans
are derived from it and contradictory profiles fail at config load.

`deerflow.models.reasoning` normalizes legacy and declared profiles into
one immutable contract and resolves every caller's generic request against
it. `create_chat_model` is the single enforcement point: required-thinking
models never receive a synthesized disable payload, unsupported effort
values never reach the provider, and the dialect synthesizes the on/off
payload when no template exists. Legacy profiles keep the historical path
byte-for-byte. The lead agent and subagent descriptor resolve the same
policy first so run metadata reports effective values.

`/api/models` and `DeerFlowClient` project the contract as `reasoning`;
the frontend derives mode clamping and effort choices from it through
`core/models/reasoning.ts`, and user preferences accept provider-specific
effort tokens. The setup wizard's Z.AI GLM-5.3-Flash profile migrates to
the contract, restoring its low/high/max effort control.

* docs(models): move the reasoning contract guidance into models/AGENTS.md

backend/AGENTS.md sits within 46 bytes of the 96 KiB effective-chain hard
limit for the middlewares, runtime, sandbox, and subagents guides, so the
new section pushed scripts/check_agent_guidance.py over it. The models
guide is the nearest file for the factory and the contract module, and
its chain has ample room.

* fix(models): address review on the reasoning contract

- vLLM dialect: the enable path now mirrors whichever chat_template_kwargs
  switch the template declares, like the disable path already did, so a
  template using the older `thinking` key no longer also grows
  `enable_thinking`.
- Validate operator-supplied effort values at `effort.path` inside
  `when_thinking_enabled`, `when_thinking_disabled`, and the `thinking`
  shortcut against the contract, not only the profile-level value; they are
  forwarded when the caller chooses nothing, so the "unknown values never
  reach the provider" guarantee now holds for them too.
- Constrain `effort.path` to dotted identifiers and reject single-segment
  paths that would replace a whole mapping (`extra_body`, `thinking`,
  `model_kwargs`, `default_headers`, `default_query`).
- Z.AI GLM-5.3-Flash profile: `default: high` instead of `max`, because the
  default also governs summarization, title generation, and subagent calls
  that never choose an effort; `max` stays selectable. Documented in the
  config example, the configuration manual, and the design note.

* fix(models): close reasoning contract review gaps

* fix(models): preserve native Ollama reasoning config

* fix(models): keep native reasoning strings and project reasoning into the descriptor

- `ModelConfig.reasoning` also accepts the level string ChatOllama takes
  for gpt-oss style models (`low` / `medium` / `high`); the factory forwards
  both the boolean and the string on the legacy path, so profiles that
  passed `reasoning: high` through before the contract field existed keep
  loading and keep sending it.
- The assembly descriptor no longer excludes `reasoning` from
  `model_parameters`: the native ChatOllama value and a declared contract's
  dialect/history/effort change the request payload, so they now move the
  assembly fingerprint.
- e2e: the sidecar spec pinned the pre-contract body where a model without
  effort control still received `reasoning_effort: minimal`; the preset is
  now dropped for such models (the Gateway discarded it anyway), so the
  assertion expects the key to be absent.
2026-09-24 20:39:30 +08:00
Ryker_Feng
6d961e00e3
feat(skills): show loaded skill snapshots in answer details (#5758)
* feat(skills): show loaded skill snapshots in answer details

* docs: bring agent guidance under size budgets

* fix: align skill usage snapshots with visible tool output

* fix: validate skill usage producers and clarification run boundaries
2026-09-24 17:49:52 +08:00
AochenShen99
ad58e8aa7d
fix(artifacts): preserve literal percent sequences in file names (#5817) 2026-09-24 16:19:27 +08:00
Ryanba
7c948a38c4
fix(frontend): avoid subtask render state mutation (#3157)
* fix(frontend): avoid subtask render state mutation

* fix(frontend): avoid subtask render state mutation

* Fix the lint error

* 修复子任务终态回退渲染

* fix(frontend): preserve subtask loading semantics

* fix(frontend): harden subtask render merging

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-09-24 15:34:32 +08:00
tomyan007
45411cee69
feat(memory): add cognitiveStyle for collaboration preferences (#3182)
* feat(memory): add cognitiveStyle for collaboration preferences

Extend cross-session memory with user.cognitiveStyle and cognitive facts,
backward-compatible normalization, prompt injection as Thinking Style, and
settings UI. Add design notes and unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(memory): normalize legacy memory imports on frontend

Add normalizeMemoryPayload aligned with backend normalize_memory_data,
use it for Settings import and API reads, and add unit/regression tests.
Document schema-evolution steps and sync FACT_EXTRACTION_PROMPT cognitive category.

* fix(memory): harden legacy memory normalization

* fix(memory): keep import envelope validation strict

* fix(memory): keep unknown fields through frontend import normalization

normalizeMemoryPayload rebuilt the memory document from a section whitelist,
so every key it did not name was dropped. Because readMemoryResponse routes
all memory API responses through it, the loss showed up on read, export, and
import alike: revision vanished, and any future backend field would silently
break lossless export/import.

The Gateway models are extra="allow" and deermem normalizes additively, which
left the frontend as the only lossy hop in the chain. Spread before overriding
at each nesting level so normalization only validates and defaults
contract-owned fields, leaving the envelope check as strict as before.

Covered by a unit test on the strict import path, two backend tests for
extension preservation and caller immutability, and an E2E round-trip that
asserts on the captured import request body.

* docs(memory): state the normalization invariant that actually holds

The docstring cited `display` as a backend-driven field alongside
`revision`, but #4546 was closed unmerged and no such field exists in the
contract. It also promised lossless export/import, which the Gateway's
`extra="ignore"` models preclude for any undeclared field.

Replace both with the invariant the code is really defending: the frontend
must not be narrower than the Gateway contract, using the declared
top-level `revision` as the concrete example.

* fix(memory): preserve legacy migration and reject lossy imports

* fix(memory): align legacy fact normalization defaults

* test(memory): use authenticated app for import regression

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-09-23 18:29:12 +08:00
Wenchao An
2a9beb34b9
feat(plugins): support manifests and static asset directories (#5685)
* feat(plugins): support manifest-backed browser assets

* fix(plugins): validate asset roots and clarify module timeouts

* fix(plugins): sandbox asset documents and support Turbo imports

* docs(gateway): keep guidance within the merged size budget

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-09-23 15:40:22 +08:00
dong
b200e5b91b
fix(frontend): show empty skill export requirements (#5659) 2026-09-22 11:44:43 +08:00
Wenchao An
e2f19d8335
feat(plugins): full-stack plugin APIs and bookmarks (#5647)
* feat(plugins): add full-stack contributions and bookmarks example

* ci(plugins): provision bookmark gateway for browser tests

* fix(plugins): authenticate module downloads through configured backend

* fix(plugins): isolate contributions and localize extension UI

* fix(plugins): preserve bookmark agent routing and contain async callbacks

* fix(plugins): pin durable batch workers to app extension snapshots
2026-09-22 11:18:57 +08:00
lihongyuan99
1e3bfa09d4
fix(frontend): read web_fetch titles that start with blank lines or indented headings (#5560)
* fix(frontend): read web_fetch titles that start with blank lines or indented headings

* docs(frontend): describe the indented-code guard as it actually behaves

* fix(frontend): reject mixed code indentation in web-fetch titles

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-09-20 20:26:48 +08:00
hataa
71087f2f8e
feat(authz): gate composer send on runs:create (Phase 4 follow-up, #4063) (#5528)
* feat(authz): gate composer send on runs:create (Phase 4 follow-up, #4063)

Mirrors #5294's stop gating on the send side: both chat routes pass
canCreateRuns (from PERMISSIONS.RUNS_CREATE, lockstep with the backend
enum) into the shared composer. The gate sits at the top of
submitThreadMessage — the single choke point every composer entry
(submit button, Enter, goal-set-triggered run) funnels through — and
denies with a toast plus a rejected promise so PromptInput keeps the
text. The idle submit button is disabled and explains the boundary via
conditionally-spread aria-label/title (startTurnUnavailable, en/zh);
while streaming the button stays the runs:cancel stop affordance.

Also removes the unreachable kind === "stop" branch in handleSubmit
(flagged during #5294's review): the Enter path early-returns with the
streaming toast before the classifier runs.

* fix(frontend): reject denied goal starts before saving state

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-09-20 17:45:03 +08:00
Daoyuan Li
015ebcc88c
fix(frontend): preserve literal think tags in code (#5540)
* fix(frontend): preserve literal think tags in code

* Fix indented continuations of inline code spans

* Respect paragraph boundaries when extracting inline reasoning

* fix: respect block boundaries and escaped backtick runs

* fix(frontend): avoid quadratic reasoning delimiter backtracking

* fix(frontend): track reasoning fences inside list items

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-09-20 17:38:20 +08:00
YxinMiracle
0b7cef2e0b
feat(channels): support WeChat QR login from the web UI (#5582)
* feat(channels): add WeChat QR login and binding recovery

* fix(channels): enforce single-worker WeChat QR login and preserve bot ID

Reject QR login endpoints when multiple Gateway workers are configured, while keeping manual token setup available.

Preserve the configured bot ID when the provider omits it or returns an empty value. Add regression coverage for worker guards and credential persistence.

* fix(channels): sync WeChat completion state on provider updates

Show the connected step when refreshed provider data confirms the binding, so the dialog no longer waits indefinitely after polling is cancelled.

Add regression tests for provider updates during pending poll and binding requests, including late responses and expiry.

* fix(channels): preserve WeChat pairing codes across waits and redirects

---------

Co-authored-by: YxinMiracle <“939157765@qq.com”>
2026-09-20 17:21:49 +08:00
Wenchao An
1f437f86c6
feat(agents): persist default knowledge scopes for custom agents (#5579)
* feat(agents): persist default knowledge scopes for custom agents

* style: format agent knowledge guidance

* fix(i18n): clarify default knowledge reset hint

* fix(knowledge): preserve retries for initially unbound agents
2026-09-20 16:31:35 +08:00
Xuehao Xu
8ef58eaa90
feat(models): manage shared models from Settings (#5596)
* feat(models): add admin UI for shared model management

* docs(gateway): keep model guidance within size budget
2026-09-20 16:13:58 +08:00
Wenchao An
075f4a3607
refactor(capabilities): simplify labels and connection validation (#5580) 2026-09-20 15:02:02 +08:00
0xzkslr-ai
03505ac4e0
fix(frontend): rejoin active runs after reopening chats (#5536)
* fix(frontend): rejoin active runs after reopening chats

* test(frontend): mock thread runs query in stream options test

* fix(frontend): avoid rejoining completed runs from stale cache

* fix(frontend): tighten active run recovery cleanup

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-09-20 11:40:31 +08:00
Wenchao An
42334f26d7
feat(capabilities): unify catalog, plugin configuration and agent selection (#5497)
* feat(capabilities): unify catalog, plugin configuration and agent selection

* fix(capabilities): address review isolation, validation and demo issues

* fix(capabilities): preserve concurrent selections and guide launcher repair
2026-09-19 12:14:24 +08:00
Wenchao An
34bbeb1806
feat(knowledge): add verifiable RAGFlow source citations (#5551)
* feat(knowledge): add verifiable RAGFlow source citations

* docs(knowledge): scope RAGFlow guidance to its own directory

* fix(knowledge): preserve citations through rendering and budgets
2026-09-19 07:44:05 +08:00
zhangwei-way
b6503e9a35
feat(knowledge): add per-message RAGFlow retrieval scope (#5238)
* feat(knowledge): integrate RAGFlow retrieval and management

* test(knowledge): cover merged listing tool

* feat(knowledge): add per-message retrieval scope

* chore(docs): remove unrelated document

* docs(knowledge): add interaction screenshots

* feat(knowledge): simplify scope selector trigger

* docs(knowledge): refresh selector screenshot

* feat(knowledge): defer standalone management

* docs(knowledge): show chat-only scope UI

* fix(knowledge): honor scope on clarification replies

* fix(knowledge): harden scoped replay validation

* docs(knowledge): clarify replay scope precedence

* fix(knowledge): keep provider settings on tools

* fix(config): preserve tools-only knowledge settings

* fix(knowledge): submit custom assistant identity

* refactor(knowledge): trim PR scope changes

* fix(knowledge): sanitize document scope display

* feat(knowledge): enable scope selection in main chat

* fix(knowledge): emphasize active scope icon without button frame

* fix(knowledge): close context scrubbing and refresh e2e checks

* fix(knowledge): preserve idempotent canonical retries

* fix(knowledge): accept promptless conversation runs

* style(knowledge): format backend regression tests

* chore(knowledge): trim PR scope and fix frontend format

* fix(knowledge): remove shared-scope notice

* fix(knowledge): remove scope persistence notice

* docs(knowledge): include main chat in catalog scope

* fix(knowledge): preserve scope recovery and upgrades

* fix(config): preserve LightRAG knowledge upgrades

---------

Co-authored-by: foreleven <for-eleven@hotmail.com>
2026-09-18 16:59:31 +08:00
Tsai Yuan
972020cf85
fix: keep streamed answers out of thinking and improve local bash probes (#5001)
* fix: improve streaming reasoning and local bash guidance

* test: cover reasoning-only processing group

* fix(frontend): preserve streaming reasoning order

* test(frontend): cover reasoning tool-call regrouping

* fix(frontend): keep thinking-only blocks in processing

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-09-18 11:30:11 +08:00