4555 Commits

Author SHA1 Message Date
haochen
f7f9a7971b
fix(sandbox): normalize mount container_path trailing slashes at config load (#7393)
* fix(sandbox): normalize mount container_path trailing slashes at config load

A container_path declared with a trailing slash (e.g. /mnt/reports/) was
accepted by VolumeMountConfig and normalized by LocalSandboxProvider when
building path mappings, but the sandbox tool path gates compare the raw
configured value, so every ls/read/write/glob/grep/bash access to the
mount failed with a confusing PermissionError. Normalize trailing
slashes in the config model so both layers agree, and reject the
container root '/' which the gates cannot express.

* fix(sandbox): normalize skills.container_path and pin empty-value rejection

Review follow-up on #7393:
- Share the container-path normalizer with SkillsConfig, whose prefix
  feeds the same as-is comparison in _is_skills_path and had the same
  trailing-slash dead-path failure mode.
- Pin the empty-string rejection: on main an empty container_path made
  the gates match every absolute path, applying one mount's read_only
  flag to all paths.
- Cover both config fields in CONFIGURATION.md.

---------

Co-authored-by: Zhangchenhao-111 <285911435+Zhangchenhao-111@users.noreply.github.com>
2026-10-25 11:44:36 +08:00
chaodidiao
811b99c491
test(mcp): cover failed-owner retry across event loops (#7458) 2026-10-25 11:32:39 +08:00
Felix Shi
737d50f625
fix(channels): retry the runtime config file lock instead of failing after ~10s on Windows (#6888)
* fix(channels): retry the runtime config file lock instead of failing after ~10s on Windows

The runtime channel config sidecar lock (#6844) used Windows' blocking msvcrt LK_LOCK mode, which gives up after roughly ten seconds and surfaces cross-worker contention as Permission denied instead of serializing the read-modify-write cycles it exists to serialize, while fcntl.flock(LOCK_EX) waits on POSIX. Retry the non-blocking acquisition (LK_NBLCK + 0.2s) on the Windows fallback, mirroring the extensions manager's lock (#6020), and bind fcntl/msvcrt at module level so the fallback branch is testable.

Tests: new backend/tests/test_channel_runtime_config_store_lock.py (5 tests: retry cadence via fake msvcrt, sidecar byte creation, POSIX flock preservation, direct Windows regression through the sys.modules seam, real cross-thread contention). 3 of them fail on the unmodified base (one with OSError 36 raised by the blocking call itself) and all pass with the fix. Related suites: store peers + connections router 86 passed / 1 skipped; blocking_io + encoding + channels 498 passed / 2 skipped. Ruff format and check clean on both files. Windows 11, Python 3.12 (uv-managed venv).

* fix(channels): share contention retries and diagnose prolonged lock waits

* docs(channels): shorten guidance to satisfy lint budget

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-25 11:05:04 +08:00
Aling33
cb6ee72132
fix(config): delete read-only thread directories on Windows (#7446)
`Paths.delete_thread_dir()` called `shutil.rmtree()` directly. Windows
refuses to unlink a file that carries the read-only attribute
(`WinError 5`) where POSIX only needs a writable parent, so one read-only
file under `user-data/` made the whole thread undeletable: `DELETE
/api/threads/{id}` answered 500 `Failed to delete local thread data`,
`rmtree` had already removed everything it walked before that entry, and
every later retry failed on the same file.

Retry at the call site: clear the read-only attribute tree-wide and call
`rmtree()` once more. Only `PermissionError` is retried, the retry still
raises when it cannot help, so a file another process holds open stays a
real error instead of becoming a silent skip. A `onexc` handler cannot do
this portably: POSIX `_rmtree_safe_fd()` catches the handler's own
`PermissionError`, re-dispatches it as a `scandir` error and leaves the
deletion to fail later as `ENOTEMPTY`.

Co-authored-by: Aling33 <along308@users.noreply.github.com>
2026-10-25 11:02:49 +08:00
sxl-lucky7
afcb915de3
fix(skills): reject non-finite JSON export values before writing (#7456) 2026-10-25 11:02:02 +08:00
N
481f8168d4
fix(console): include legacy run input tokens in model usage (#7411) 2026-10-25 11:01:44 +08:00
lukyturnflow
0f8ba6f244
test(channels): assert the blocked-rename error portably on Windows (#7276)
* test(channels): assert the blocked-rename error portably on Windows

Rebased onto the channels-store refactor. The suite pins that a failed
atomic-rename save must surface the blocked-rename error, never the
cleanup error. Replacing a file over a non-empty directory is EISDIR
(errno 21) on POSIX but WinError 5 (errno 13, PermissionError) on
Windows, so the assertions now accept the platform's blocked-rename
error via a shared helper instead of hard-coding errno 21, which broke
on Windows dev hosts.

Verified by the maintainer on macOS (15/15 green on the POSIX arm) and
run here on Windows (15/15 green on the nt arm); POSIX regression
signal is unchanged.

* style: ruff format 0.15.12

---------

Co-authored-by: Jay0130-a <309071492+Jay0130-a@users.noreply.github.com>
2026-10-25 10:26:23 +08:00
Xuehao Xu
8237bc6c9e
fix(subagents): preserve test summaries before evidence truncation (#7407)
* fix(subagents): preserve test summaries before evidence truncation

* fix(subagents): honor Node counters and cache harvested summaries

* fix(subagents): retain warning counts and Go case evidence

* fix(subagents): retain long pytest sibling summaries

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-25 10:25:04 +08:00
Wang Xinming
3113c150cd
fix(provisioner): reuse userdata PVC across sandbox mounts (#7274)
* fix(provisioner): reuse userdata PVC across sandbox mounts

* fix(provisioner): keep shared volume names in sync and document skills PVC caveat

* test(provisioner): pin skills PVC caveat and hostPath broker manifest
2026-10-25 10:24:23 +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
wannT-1
ec87cb9a21
fix(chart-visualization): wire generate_spreadsheet and fail CLI nonzero (#7395)
* fix(chart-visualization): wire generate_spreadsheet and fail CLI nonzero

SKILL.md already selects generate_spreadsheet and the reference file
documents it, but CHART_TYPE_MAP had no entry, so the CLI reported
Unknown tool. Map it to the AntV spreadsheet type and exit non-zero
after any unknown tool, missing tool, or HTTP failure. Successful chart
URLs stay on stdout.

Co-authored-by: wannT-1 <200890097+wannT-1@users.noreply.github.com>

* docs(agents): fold chart CLI failures into the shared stderr/stdout note

The sandbox guidance chain was over the AG002 hard limit. Share one
nonzero-exit rule for video, podcast, and chart-visualization CLIs.

Co-authored-by: wannT-1 <wannT-1@users.noreply.github.com>

---------

Co-authored-by: wannT-1 <200890097+wannT-1@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
Co-authored-by: wannT-1 <wannT-1@users.noreply.github.com>
2026-10-25 09:57:13 +08:00
CodeAlex
de03ef97e8
fix(scripts): make doctor.py --help print usage without running diagnostics (#7433)
* fix(scripts): make doctor.py --help print usage without running diagnostics

scripts/doctor.py never parsed its arguments: `python scripts/doctor.py
--help` printed no usage and ran the whole diagnostic suite (subprocess
probes, config resolution, provider checks) and exited 0; any unrecognized
flag was silently ignored the same way. Closes #7386.

- Parse arguments with argparse.ArgumentParser in the __main__ entry point:
  --help prints usage and exits 0, unknown flags exit 2.
- The no-argument invocation used by `make doctor` is unchanged, and main()
  stays directly callable from the existing unit tests (they run in-process
  under pytest, whose argv must not reach argparse).
- Add backend/tests/test_doctor_script.py: offline subprocess cases for
  --help (usage, no diagnostics run), an unknown flag (exit 2), and the
  no-argument run (report footer present, exit 0 or 1).

* test(scripts): isolate doctor CLI subprocess checks

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-25 09:50:48 +08:00
sun04222
041a21b6a0
fix(skills): decode skill-review analyzer output as UTF-8 (#7432)
The analyzer prints ensure_ascii=False JSON, but the parent pipes used
the ambient locale codec and the child inherited the ambient stdio
codec: non-ASCII skill facts crashed the review pass on cp1252
Windows and mojibake'd on cp936. Pin both sides to UTF-8.

Fixes #7387
2026-10-25 09:06:19 +08:00
ShiYuan Xiong
b3b1424799
fix(dingtalk): validate access token expiry (#7450)
* fix(dingtalk): validate access token expiry

* fix(dingtalk): reject token lifetimes within refresh margin

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-25 08:57:28 +08:00
jay
63211d5da6
fix(readability): preserve <a> links when Readability.js silently degrades on Windows (#7412)
* fix(readability): preserve <a> links when Readability.js silently degrades on Windows

On Windows, simple_json_from_html_string(use_readability=True) invokes the
Readability.js subprocess, which succeeds without error but returns the
unextracted document instead of the extracted article. The pure-Python
fallback (use_readability=False) also drops every <a> destination. The net
effect is that web_fetch returns link-free Markdown on Windows.

Fix: after extraction, check whether the output retained the <a> elements
that the resolved HTML carried. When they are all gone, fall back to
converting the resolved HTML directly with markdownify — which preserves
<a> as [text](href) — so links survive regardless of the Readability.js
outcome.

Measured: 17 of 22 test_web_fetch_relative_links.py failures resolved;
43/48 pass on Windows (5 remaining are pre-existing malformed-markup
extraction tests, unrelated to this change).

Closes #6459

* test(readability): add degraded-extraction and scope-pinning tests

* test(readability): detect unwanted article fallback

* test(readability): classify HTML body in authority-tag census

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-25 08:56:21 +08:00
Tsai Yuan
2b6716c920
fix(knowledge): accept case-insensitive upload media types (#7397)
* fix(knowledge): accept case-insensitive upload media types

* test(knowledge): pin upload media type normalization
2026-10-25 08:47:15 +08:00
spud
e9074b7126
feat(runtime): publish owner-confirmed run.terminal evidence (#7134)
* feat(runtime): publish authoritative run terminal events

* fix(runtime): preserve terminal attribution diagnostics

* docs(runtime): consolidate terminal event docs and trim agent guidance

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-25 00:12:26 +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
MsNBBc3R
2055991eb2
fix(video): preserve reference image MIME types (#7340)
* fix(video): preserve reference image MIME types

* fix(video): reject unrecognized reference image extensions

* fix(tests): register video generation guidance

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 22:34:14 +08:00
DDT
afe244e7f9
fix(config): pin explicit app config path across get_app_config (#7092)
* fix(config): pin explicit app config path across get_app_config

Fixes #7086. reload_app_config(config_path) now records an
_app_config_source pin (mirroring _extensions_config_source) so
get_app_config and DeerFlowClient(config_path=...) keep following the
explicit file instead of ambient default resolution.

* fix(config): synchronize app config cache and absolute pins

Serialize singleton reads, reloads, resets and injections so a concurrent call cannot publish a config under another file's pin. Keep explicit paths absolute across working-directory changes while preserving ambient discovery and failed-reload behavior.

Add deterministic concurrency and relative-path regressions. Validation: 33,875 offline tests and 262 blocking-I/O tests passed; frontend check, backend formatting and agent guidance passed.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 22:04:48 +08:00
llb555
8984093552
fix(uploads): make companion record removal best-effort (#7409)
* fix(uploads): make companion record removal best-effort

* style(uploads): format the new test and log the markdown-reference removal

* fix(uploads): report the orphaned record through the module that owns it
2026-10-24 21:56:38 +08:00
Kiko0301
d142aee83f
fix(memory): keep sample import backups unique per run (#7418)
* fix(memory): keep sample import backups unique per run

* docs(memory): clarify backup retention and align script format

---------

Co-authored-by: Kiko0301 <191240073+Kiko0301@users.noreply.github.com>
2026-10-24 21:50:27 +08:00
dependabot[bot]
ac7b2c24ae
chore(deps): bump pillow from 12.3.0 to 13.0.0 in /tests/skills (#7443)
Bumps [pillow](https://github.com/python-pillow/Pillow) from 12.3.0 to 13.0.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/12.3.0...13.0.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-version: 13.0.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-24 21:21:45 +08:00
Hyeonsang Cho
6dc28f5868
fix(persistence): auto-create the Postgres database only when it is missing (#7410)
* fix(persistence): auto-create the Postgres database only when it is missing

init_engine retried bootstrap through CREATE DATABASE whenever the error
text contained "does not exist". Postgres words missing relations,
columns, functions and roles the same way, so a bootstrap that failed
inside an existing database (for example a migration reading a dropped
column) went on to CREATE DATABASE. Startup then failed with
DuplicateDatabaseError, or with "permission denied to create database"
for a role without CREATEDB, and the original error appeared nowhere in
the traceback.

Recognize a missing database by SQLSTATE 3D000 (invalid_catalog_name)
instead, following __cause__ for a wrapped copy, and re-raise every
other bootstrap error unchanged. A live PostgreSQL test covers both the
missing-database path and an existing database whose migration fails.

* docs(changelog): reference #7410 for the Postgres auto-create fix

* fix(persistence): walk orig, __cause__ and __context__ for the missing-database SQLSTATE

Match _is_unique_violation's chain walk, with its cycle guard, so a wrapper
that keeps the driver error only on `orig` or `__context__` still triggers
auto-create. Drop the live test's English message assertion; the SQLSTATE
check already proves the original error surfaced.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 21:18:18 +08:00
XuRuuuy
c9d3e48a4a
fix(uploads): never truncate an existing upload on same-name collision (#7388)
* fix(uploads): never truncate an existing upload on same-name collision

Same-name uploads silently destroyed the earlier file on disk: the shared
write path (open_upload_file_no_symlink) opened the destination with
O_WRONLY|O_CREAT (no O_EXCL) and ftruncate(0), so re-uploading report.pdf
opened and emptied the report.pdf already stored for the thread, and two
concurrent same-name uploads could race on one inode. The Gateway
ingestion pipeline already refuses overwrites (staged no-overwrite link
commit), but the embedded client upload path (copy_upload_file_no_symlink)
and the channel ingress paths (write_upload_file_no_symlink) still went
through the truncating open.

Open every upload destination with O_EXCL. On FileExistsError, claim the
next free _N suffix against the directory's current contents via the
existing claim_unique_filename helper and retry — the same deterministic
behavior the Gateway pipeline documents (name_1.ext). The existing file is
never opened, so its bytes survive intact under any future naming policy.

Embedded-client and channel inbound-file responses now report the name the
bytes actually landed under instead of the requested name.

* test(uploads): cover concurrent same-name writers

* fix(uploads): skip known copy collisions before staging

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 20:57:27 +08:00
ordinary being
d2e2d57f1b
fix(scheduler): reconcile running occurrences from terminal durable runs (#7269)
* fix(scheduler): reconcile running occurrences from terminal durable runs

A completion hook can fail after the durable run has already committed its terminal status (transient DB, observer, or notification error). Single-instance run_once only recovered expired launching claims, so the occurrence stayed running: a recurring schedule stopped firing and a once task never reached its terminal parent state until a restart.

Add a non-destructive ScheduledTaskRepository.reconcile_terminal_runs pass. It only selects running occurrences whose linked runs row is already terminal and whose scheduled metadata matches, then finalizes through the existing finalize_occurrence path with the run's status, error, and goal verdict. Live runs are never taken over or interrupted; multi-instance reconciliation already handles terminal runs through lease ownership. Single-instance run_once calls it after expired-launch recovery. No schema change.

Tests: a terminal success finalizes, a live run is left alone, error/timeout/interrupted map correctly, and a success with an unsatisfied goal verdict becomes unmet. Full scheduler suite: 1041 passed / 130 skipped; guidance 0 errors / 0 warnings; ruff clean.

* fix(scheduler): isolate terminal-run reconciliation failures per occurrence

A poisoned occurrence (the same observer or database failure that made the sweep necessary) ran in the same transaction as every other candidate, so one raised exception rolled back the whole pass and escaped run_once before admission and queue draining. Give each candidate its own session/transaction: a failure rolls back only that occurrence, logs its id, and the remaining candidates still commit. The service also catches a remaining pass-level failure so the rest of the poller runs.

Extend coverage: scheduled-trigger finalization (the motivating case), a second reconcile returning 0 with no state change, once-task completed projection, and a poisoned-observer candidate ordered before a healthy one whose success still commits. Full scheduler suite 1044 passed / 130 skipped.

---------

Co-authored-by: Tong-bit-art <Tong-bit-art@users.noreply.github.com>
2026-10-24 20:56:35 +08:00
Aling33
3fdd9bdfff
fix(artifacts): keep sandbox paths inside CJK corner brackets whole (#7377)
The quote-delimiter tables only knew ASCII quotes, backticks and curly
quotes, so `已生成「/mnt/user-data/outputs/季度 报告.pdf」。` captured the
handle `/mnt/user-data/outputs/季度` (the path pattern stops at the space)
and `已生成『…』。` kept the closer inside `real_ref`; either handle is dead
on a later read.

Register `「」` and `『』` as wrappers: a paired closer stays inside a
filename, an unpaired one is trimmed from a bare token, and corner-trailed
URLs and refs are no longer cut.

The persistence regression (capture -> registry round-trip -> resolution ->
real file read with the tool result gone) is ported from #7376 by
@yangjj-iso, per review.
2026-10-24 20:45:02 +08:00
Zeren Wang
74d2565f64
feat(projects): search shelf documents by keyword (#7286)
* feat(projects): search shelf documents by keyword

Add search_project_documents: two-tier name/summary then bounded content scan, case-insensitive substring matching with original-text offsets. Never converts on the fly; reports stop_reason, content_scanned, skipped reasons, and capped unscanned/truncated ids.

* fix(projects): derive unscanned count without paging after budget trip

Review feedback: once the content budget trips, stop paging list_active to enumerate unscanned ids — cap ids at the trip page and derive the honest total from one count_active query (tier 1 is exhaustive, so active - tier1 hits - processed = unscanned). Also add a tool-level isolation test pinning that search never crosses projects or users.

* fix(projects): scan the shelf over one stable snapshot, not offset pages

Review feedback: offset paging re-read the live shelf per page, so a concurrent upload/trash between pages could duplicate a match or silently skip one while still claiming complete coverage. Tier 1 now materializes the whole shelf in a single SELECT (one read snapshot) and tier 2 iterates those same rows; the budget-trip unscanned count is derived exactly from the snapshot with no extra query. Mid-scan upload/trash regression tests pin both failure modes.
2026-10-24 18:07:00 +08:00
sxl-lucky7
9b89c7569d
fix(skills): preserve transparency in composed PowerPoint slides (#7312)
* fix(skills): preserve transparency in composed PowerPoint slides

* ci(skills): run public skill regressions

* ci: remove public skill test workflow

* docs: move skill test commands out of inherited guidance

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 18:05:55 +08:00
Tsai Yuan
5dca9ae240
fix(unbrowse): bound target error final URLs (#7399)
* fix(unbrowse): bound target error final URLs

* fix(unbrowse): redact final URL userinfo

* fix(unbrowse): remove credentials from final URL diagnostics

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 16:41:35 +08:00
haochen
bde5f2d090
fix(sandbox): close pipe write fds once when Popen fails (#7394)
* fix(sandbox): close pipe write fds once when Popen fails

The command runners closed the pipe write descriptors twice on the
spawn-failure path: the except block released all four pipe fds, then
the finally block closed the two write fds again. In a busy gateway
another thread can claim a freed descriptor number between the two
closes, so the second os.close silently closed an unrelated descriptor
(a concurrent command's capture pipe or an HTTP socket). Guard the
parent's write-end close on a successful spawn.

* fix(sandbox): release pipe fds on BaseException and scope the Windows runner test

Review follow-up on #7394:
- Catch BaseException in both command runners' spawn-failure cleanup so a
  KeyboardInterrupt mid-spawn still releases each pipe fd exactly once;
  the guarded finally keeps the no-double-close guarantee.
- Skip the _run_windows_command failure-path param on non-Windows
  runners: it reads subprocess.CREATE_NEW_PROCESS_GROUP when building
  the call, which raises AttributeError on POSIX and would fail Linux CI.
- Add a KeyboardInterrupt regression test (fd-per-fd close accounting).

---------

Co-authored-by: Zhangchenhao-111 <285911435+Zhangchenhao-111@users.noreply.github.com>
2026-10-24 16:39:22 +08:00
Nan Gao
a9917e986b
docs(channels): align leader-lease failover and runtime-config notes with the merged code (#7420)
The two-Gateway harness checklist in CONFIGURATION.md said a crashed
leader hands its providers over within lease_seconds + grace_seconds; a
peer only tries to acquire on its fixed lease_seconds // 3 tick, so the
worst case is one renewal interval more (50 s with the defaults), as the
leader-lease section of the same file already says. The grace_seconds
field description had the same shorter bound.

The channels.leader_lease comment in config.example.yaml still said web
UI credential changes reach the holder only when it next starts the
channel; since #6988 the holder applies them within one renewal interval.

The harness `check` footer still listed the IM channel gates as waiting
for leader election, which landed in #6843; it now points at the manual
channel item in CONFIGURATION.md, which also gains the runtime-config step.
2026-10-24 16:35:26 +08:00
Hyeonsang Cho
ee8e6762ac
fix(mcp): let ordinary users save bundled personal connections (#7400)
* fix(mcp): let ordinary users save bundled personal connections

The personal MCP policy read `server.cwd`, which McpServerConfigResponse
does not declare. A bundled launcher definition (DingTalk, WeCom,
HubSpot) never carries `cwd`, so for a non-administrator the check
raised AttributeError and every create, edit or enable of one returned
500. The web UI installs catalog connections with scope "user", so an
ordinary user could not install any of the three.

Read a supplied `cwd` from the model's extras instead. A bundled
launcher with a working directory still requires an administrator.

* docs(changelog): reference #7400 for the bundled personal MCP fix

* docs(changelog): say #7400 makes bundled personal installs work, not work again

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 16:12:04 +08:00
NanPan
7cfd3c57d2
fix(gateway): bound run event type filters (#7370)
* fix(gateway): bound run event type filters

* test(gateway): cover run event filter cardinality

* style(gateway): group run event filter limit with module constants
2026-10-24 16:11:57 +08:00
sun04222
9a11f9aec7
fix(benchmark): guard the POSIX-only resource import in skill_export (#7381)
skill_export.py imported the POSIX-only stdlib module `resource`
unconditionally at module level, so the benchmark entry point died with
ModuleNotFoundError on Windows before argument parsing. Mirror the
try/except ImportError guard already established by
checkpoint/checkpoint_bench_common.py, report "peak_rss_bytes": null when
the module is unavailable, and leave POSIX scaling unchanged (macOS
bytes, Linux KiB scaled to bytes).

Fixes #7380
2026-10-24 13:31:12 +08:00
sun04222
d2f22e7ad8
fix(scripts): make configure.py --help print usage without generating files (#7404)
configure.py did not parse its arguments, so `--help` printed no usage and
ran the bootstrap as a side effect (creating config.yaml, .env and
frontend/.env in a fresh checkout), and any unrecognized flag was silently
ignored the same way. Parse arguments with argparse before touching the
filesystem: --help now prints usage and exits 0, unknown flags exit 2, and
the no-argument invocation used by `make configure` is unchanged.

Extend the regression tests from #7102 with two offline subprocess cases
covering both new behaviors.

Fixes #7382
2026-10-24 13:29:48 +08:00
wuyi-github
5a563b4e99
feat(channels): add thread-scoped discard and lifecycle helpers for Feishu pending card registry (#7357)
* feat(channels): add thread-level discard and pruning helpers for Feishu card registry

Enhance PendingCardRegistry with thread-scoped retirement (discard_thread), explicit stale card pruning (prune_expired), and read-only inspection (get_pending_entry). Add matching unit tests covering multi-card lifecycles.

Relates to #1199

* feat(channels): connect thread-scoped card retirement to /new lifecycle
2026-10-24 11:41:49 +08:00
CodeAlex
4f9a76d5ef
fix(mindie): decode escaped newlines correctly inside tilde-fenced code blocks (#7285)
* fix(stream): tolerate ~~~-fenced code and \-prefixed closer when decoding escaped newlines

_decode_escaped_newlines_outside_fences only recognised ``` fences, so replies
that used ~~~ fences (e.g. code blocks rendered by the model with
~~~python ...) kept literal backslash-n sequences escaped instead of decoding
the ones outside fences, and a fence whose closer was split as backslash+tilde
across chunks was misread mid-stream. Fixes #7262.

- detect the opening fence with (?:```|~~~) and remember which delimiter the
  fence was opened with, so closing requires the same delimiter
- while inside a fence, hold back a closer that begins with a backslash so a
  fence ending in backslash+tilde mid-stream is not misread; the held tail is
  revisited on the next push and normalised on flush
- keep backtick hold semantics unchanged

* fix(mindie): preserve unclosed fences during newline decoding

Match one-shot decoding to streaming by choosing the earliest backtick or tilde opener and preserving the remainder when its matching closer is absent. Correct the string-return docstring and cover mixed delimiters across every two-chunk split and character-by-character streaming.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 11:41:35 +08:00
vency-799
0d84203086
fix(skills): stop rebuilding the thread skill view every turn on network (#7333)
* fix(skills): stop rebuilding the thread skill view every turn on network filesystems

* fix(skills): repair thread views with stable content fingerprints

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 11:41:21 +08:00
Kiko0301
3138ac42cc
fix(gateway): preserve IDs for id-less run inputs (#7349)
* fix(gateway): preserve IDs for id-less run inputs

* test(runtime): avoid narrow lease deadline in rollback test

---------

Co-authored-by: Kiko0301 <191240073+Kiko0301@users.noreply.github.com>
2026-10-24 11:37:11 +08:00
NanPan
e1430c832c
fix(extensions): make notify dispatch admission atomic with shutdown (#7383)
* fix(extensions): fence notify admission against shutdown

* test(extensions): cover notify admission shutdown race

* docs(extensions): record atomic notification admission

* style(tests): format extension notify shutdown regression

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 11:36:06 +08:00
Hyeonsang Cho
3d4a9ca09f
fix(examples): keep the extension example's tests passing as the contract grows (#7364)
* fix(examples): keep the extension example's tests passing as the contract grows

The README's package-test commands failed one test on main:
isinstance(registry, ExtensionRegistry) is False because the test's
FakeRegistry does not implement knowledge_provider(), which
extension-api 0.2.7 added to the runtime-checkable protocol. The same
assertion broke earlier when plugin() was added. Nothing in CI runs
these tests, so each addition to the contract broke them silently.

FakeRegistry now records knowledge_provider() and plugin() calls, and
the test asserts the example registers neither, as its README says.
Extension Package Check, which already runs when the contract changes,
now installs each bundled example that has a tests/ directory next to
the checkout's extension-api and runs the README's pytest and ruff
commands. A backend test pins that step and checks that every tested
example declares those tools in its dev extra.

The example README and the extensions guide now count nine
contribution kinds, including knowledge providers.

* docs(changelog): reference #7364 for the extension example test fix

* test(extensions): name the missing package-test step in the wiring pin

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 11:07:26 +08:00
llb555
2c227dc193
fix(projects): keep shelf staging cleanup best-effort across the routes (#7229) 2026-10-24 11:07:09 +08:00
Aling33
15a31e30eb
fix(scripts): reject a non-file --out path in dev_multi_instance (#7289)
`render-config`, `secrets` and `nginx-conf` write `--out` through
`os.replace`, so an existing directory surfaced as a raw `PermissionError`
traceback, and a file ancestor as `OSError` from `mkdir`.

Validate the first existing ancestor once in `main()` before any writer runs,
and state the file contract in `--help` for all three commands.
2026-10-24 11:07:00 +08:00
kunkun403
991459829b
fix(unbrowse): surface target HTTP errors in fetched pages (#7257)
* fix(unbrowse): surface target HTTP errors in fetched pages

* fix(unbrowse): retain empty-page status and final URL
2026-10-24 10:49:45 +08:00
haotianqi666
46da5008cd
fix(skills): report podcast CLI failures with a nonzero status (#7236)
Co-authored-by: haotianqi666 <250384991+haotianqi666@users.noreply.github.com>
2026-10-24 10:49:05 +08:00
Sun Shihao
bba880b676
fix(projects): drain shelf staging on cancellation (#7360) 2026-10-24 10:47:40 +08:00
dependabot[bot]
916e647a75
chore(deps-dev): bump happy-dom from 20.11.1 to 21.0.4 in /frontend (#7391)
* chore(deps-dev): bump happy-dom from 20.11.1 to 21.0.4 in /frontend

Bumps [happy-dom](https://github.com/capricorn86/happy-dom) from 20.11.1 to 21.0.4.
- [Release notes](https://github.com/capricorn86/happy-dom/releases)
- [Commits](https://github.com/capricorn86/happy-dom/compare/v20.11.1...v21.0.4)

---
updated-dependencies:
- dependency-name: happy-dom
  dependency-version: 21.0.4
  dependency-type: direct:development
...

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

* test(frontend): isolate FlipDisplay animations in DOM suites

---------

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 10:45:01 +08:00
Smilewithoutfalling
cf15f05f4d
fix(ci): retarget label docs at triage.yml and guard that applied labels are declared (#7328)
* fix(ci): retarget label docs at triage.yml and guard label declarations

.github/labels.yml still described the auto-labeling setup as the three
workflows triage.yml replaced (pr-labeler.yml / pr-triage.yml /
issue-triage.yml), and its section headers pointed at the deleted
.github/labeler.yml and pr-triage.yml. A reader following those pointers
lands on 404s.

The same pair had an unguarded failure mode: triage.yml applies labels by
name, and GitHub rejects the whole addLabels batch with 422 when any name is
missing from the repository. Nothing checked that a label added to the
workflow also reached the declarative source of truth.

Point the comments at triage.yml and add scripts/check_label_declarations.py,
which fails when the workflow applies a label labels.yml does not declare.

* fix(ci): trace applied labels and cover the declaration guard

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 10:28:06 +08:00
Cordis798
4583c46311
fix(subagents): exclude ambiguous tool calls from parent context snapshots (#7372)
Co-authored-by: Cordis798 <284918518+Cordis798@users.noreply.github.com>
2026-10-24 10:26:21 +08:00