4571 Commits

Author SHA1 Message Date
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
wannT-1
f481c7e19f
docs(lark): pin atomic publish cleanup so it cannot mask the error (#7329)
Document that a failed write, fsync, or replace in
_publish_bytes_atomically leaves the previous file intact, and that
temporary removal must not replace that error, including interruptions.
Add site-specific regression tests for unlink and warning-log failures.

The cleanup_failed_atomic_write call itself already landed on main in #7200.

Co-authored-by: wannT-1 <200890097+wannT-1@users.noreply.github.com>
2026-10-24 10:23:31 +08:00
Hyeonsang Cho
5bda01f524
fix(gateway): answer malformed run bodies with 4xx instead of 500 (#7362)
* fix(gateway): answer malformed run bodies with 4xx instead of 500

RunCreateRequest types config, context and input as free-form dicts, so
some bodies it accepts still failed during run admission with an
exception nothing mapped, and the run routes answered 500:

- a non-object config.configurable (build_run_config's dict.update, and
  .get in the stateless thread selector)
- a non-object config.context (build_run_config's own ValueError)
- an assistant_id that does not normalize to letters, digits and hyphens
- a non-string agent_name in config.configurable, config.context or
  context (_load_scope_agent_config)
- an AI message whose tool_calls is a string, object or list of strings
  (AttributeError from LangChain's validator)

The request model now requires config.configurable and config.context to
be objects or null (422 invalid_run_config_section), and
build_run_config checks configurable as it already checked context.
start_run maps build_run_config's ValueError to 422, as it does for
validate_thread_id. Message conversion also maps AttributeError to its
existing 400, and a non-string agent_name gets the existing 422 for an
unresolvable agent. All of these fail before admission, so no run is
created.

* docs(changelog): reference #7362 for the malformed run body fix

* docs(changelog): note the configurable shapes #7362 now rejects
2026-10-24 10:12:05 +08:00
lukyturnflow
d0d66d62fa
fix(uploads): report rejected filenames and make success mean "a file was stored" (#7165)
Unsafe filenames and unnamed parts were dropped silently: the response kept
`success: true` with an empty `files` list, so the #7122 frontend gate never
blocked a chat send whose attachment had been rejected (#7160).

Both drop arms now record the rejected name in `skipped_files`, and `success`
follows `bool(uploaded_files)`: an all-rejected batch fails, a mixed batch
succeeds and lists what was dropped.
2026-10-24 10:07:10 +08:00
李刚 / Bryan Li
a041af542f
fix(skillscan): keep a live client handle through a branch that rebinds it later (#7128)
* fix(skillscan): keep a live client handle through a branch that rebinds it later

The instance-client signal dropped every name a compound statement could bind
before walking any of its parts, so an outbound call that really happened first
disappeared when a later statement in that same body rebound the name
(issue #4296, case 1).

Dropping up front is what keeps a benign skill from being hard-blocked for the
parts that can *follow* a sibling's rebinding -- a `finally` after a handler
replaced the handle, an `else` after a loop body -- so those keep it. Parts that
cannot follow a sibling (an `if`/`elif`/`else` branch, the one `match` case that
runs, a `try` body, a loop body) are now walked with the enclosing handle intact
and apply their own bindings in source order, the rule the top level already
uses. Names the statement binds as it dispatches -- a loop target, an
`except ... as` name, a `match` capture -- stay dropped before every part.

* fix(skillscan): walk a compound header before dropping its dispatch bindings

A loop target, an `except ... as` name and a `match` capture bind only after
that header has been evaluated, so dropping them first hid a real sink written
in the header itself: `for s in s.post(...)` with `s` a live handle reported
nothing, while the same call under a differently named target was found. Move
the drop to after the header loop and before the parts, which is all
`_branching_parts` needs; the body still starts with those names gone, so the
loop-value case stays unreported.

Raised in review on #7128. Pins the two header shapes, the inverse that keeps
the body conservative, and the three paths the review called out as unpinned:
the mutually-exclusive two-level `if`/`elif` case, a `while` body, and a `try`
`else` after a body rebind.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 08:53:50 +08:00
Totoro
4e4ccce06b
fix(gateway): close initialized sandbox provider on shutdown (#7347)
Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-24 08:52:56 +08:00
hataa
35d0dcd6f2
feat(plugins): add sectioned long-form report writing example (#7371)
* feat(plugins): add sectioned long-form report writing example

A standalone example extension whose save_section model tool stores a
万字-scale report section by section (SQLite, per-user and per-thread)
and assembles every section into one markdown document on a final
section_index=999 call. The assembled document is returned as a tool
artifact with only a short status line re-entering the model context, so
the full report never consumes the context budget.

Assembly drops a section's leading heading when it restates the section
title (models habitually open each section with '# <title>' while the
assembler already emits '## <title>'), which otherwise doubles every
heading in the document. The outline section passes through unwrapped
and keeps its own heading as the document title.

* fix(frontend): register report sections in extension catalog

* docs(report-sections): correct the test command path

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 08:50:56 +08:00
yinx65588-boop
4ec0fe1bb4
fix(tenki): retain cleanup ownership across provider retirement (#7311)
* fix(tenki): retain sandbox ownership through provider retirement

* fix(tenki): fence terminal callbacks by sandbox generation

* docs(sandbox): keep Tenki lifecycle guidance within the chain budget

* fix(tenki): report pending exit cleanup without a traceback

---------

Co-authored-by: yinx65588-boop <yinx65588-boop@users.noreply.github.com>
Co-authored-by: yinx65588-boop <272180046+yinx65588-boop@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 08:49:35 +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
dependabot[bot]
2efe854980
chore(deps): bump next from 16.3.8 to 16.4.1 in /frontend (#7384)
Bumps [next](https://github.com/vercel/next.js) from 16.3.8 to 16.4.1.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Commits](https://github.com/vercel/next.js/compare/v16.3.8...v16.4.1)

---
updated-dependencies:
- dependency-name: next
  dependency-version: 16.4.1
  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 08:15:40 +08:00
Kiko0301
eb7f41cf7e
fix(skills): reject lossy JSON exports with duplicate columns (#7305)
* fix(skills): reject lossy JSON exports with duplicate columns

* fix(skills): clarify JSON export errors and close action connections

---------

Co-authored-by: Kiko0301 <191240073+Kiko0301@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 08:14:58 +08:00
haotianqi666
868c1ff807
fix(agents): treat read_file JSON errors as document data (#7369)
Co-authored-by: haotianqi666 <250384991+haotianqi666@users.noreply.github.com>
2026-10-24 08:06:38 +08:00
lihongyuan99
8c2f066159
fix(skills): read one shell vocabulary in the curl-pipe-shell rule (#7378)
`shell-curl-pipe-shell` hard-codes `bash|zsh|dash|fish|sh` in the download-pipe
matcher while the structural `env` fallback reads `_SHELL_NAMES`. The two lists
drift: `curl ... | ksh` (and csh/tcsh/ash/mksh) pipes a fetched script in
silence, even though sandbox_audit_middleware already treats ksh as a shell.

Derive the alternation from `_SHELL_NAMES` and widen both to the same ten
names, so a shell added once is visible to both detectors.
2026-10-24 08:03:36 +08:00
wannT-1
e1a7cba2ef
fix(extensions): keep atomic-write cleanup from replacing the save error (#7164)
* fix(extensions): keep atomic-write cleanup from replacing the save error

Extension snapshot rollback, the managed plugins rewrite, and skill-archive
upload copies now use cleanup_failed_atomic_write so a failed unlink or
warning log cannot mask the original publication error.

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

* fix(skills): keep upload and storage cleanup from replacing the save error

Route the skill-archive upload discard, custom-skill file writes, and
per-user skill-state writes through cleanup_failed_atomic_write. Close the
extension rollback descriptor on fdopen failure, and record the plugins
temporary path before the write so a short write is still removed.

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

* fix(skills): keep form close and snapshot delete from masking the save error

Discard the uploaded skill archive before closing the multipart form, and
suppress close errors so a flush failure cannot replace the install outcome.
Snapshot rollback of a missing original now uses cleanup_failed_atomic_write,
and skill-state writes clean up on BaseException including KeyboardInterrupt.

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

* test(extensions): assert logging cleanup cannot replace the delete-branch error

The log-handler check only ran for the unlink parameter, where the handler
is never patched. Move it under the logging failure so a handler exception
must stay out of the surfaced install error.

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: wannT-1 <wannT-1@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 07:38:10 +08:00
BaiHeng Peng
cc8a947c7d
feat(extensions): add JSON output for configured extension inventory (#7351)
Co-authored-by: BaiHeng Peng <191328900+Aldenshile@users.noreply.github.com>
2026-10-24 07:19:45 +08:00
leom1919
afc7014b48
docs(readme): fix dead link to checkpoint operations docs (#7361) 2026-10-24 07:18:47 +08:00
Gusanity
d318a89b80
fix: honor checkpoint object in thread state updates (#7115)
* fix: honor checkpoint object in thread state updates

Prefer the checkpoint object over the deprecated checkpoint_id, validate
its thread_id, and preserve checkpoint namespace when updating state.

Add regression coverage for checkpoint-object forks and thread mismatch.

* fix: preserve client context handling in checkpoint PR

* fix: support state updates in checkpoint namespaces

* fix: reject unknown checkpoint namespaces

* fix(gateway): isolate subgraph titles from thread metadata

Only synchronize state titles into conversation metadata for the root checkpoint namespace. Cover persisted child updates with and without checkpoint IDs in full and delta modes, and retain root title synchronization and branch sequence cleanup.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-24 07:17:27 +08:00
ShiYuan Xiong
75df1be6b7
fix(benchmark): reject negative idle timeouts before provider creation (#7363)
* fix(benchmark): reject negative idle timeout before provisioning

* test(benchmark): cover registered providers for idle timeout validation
2026-10-23 23:56:44 +08:00
Xuehao Xu
cfe2bec371
feat(workflows): add multi-Gateway coordination and session-authorized failover (#7138)
* feat(workflows): add leased multi-Gateway coordination and fenced dispatch

* test(workflows): validate deployed failover and canonicalize config revisions

* fix(workflows): share namespace across registration and lease fencing

* fix(workflows): recover interrupted cancellation without admission caps

* fix(workflows): retain execution lease through child cleanup

* docs: trim guidance padding to fit merged instruction budget

* fix(workflows): release rejected admission slots and normalize gateway routing

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-23 23:31:43 +08:00
Akaliup
810a400d82
fix(skills): report video generation failures with a nonzero exit status (#7034) 2026-10-23 23:06:18 +08:00
lihongyuan99
0a42289233
fix(sandbox): keep quarantine teardown idempotent when a peer finishes first (#7313)
* fix(sandbox): return from quarantine teardown when a peer already did the work

retire() and retire_replaced_generations() guard their final rmdir() but list
the fence directory and unlink each record unguarded, while contains() and
_exists() in the same class already report a directory removed underneath them
as "no fence". The teardown exclusion is a TTL lease, not a lock, so a peer can
finish the same teardown in those two windows and the resulting FileNotFoundError
escapes the unwrapped _retire_absent_quarantine() call at the top of creation.

Both passes now tolerate a peer that removed the directory or the record first.

* test(sandbox): correct quarantine docstring reference

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-23 22:55:45 +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
NanPan
ef54c14b4d
fix(sandbox): use saved process group id on timeout (#7353)
* fix(sandbox): retain process group id for timeout cleanup

* test(sandbox): cover exited leader during group cleanup

* fix(sandbox): capture process group id before drain threads

* style(sandbox): separate timeout regression test

* test(sandbox): cover missing process-group fallback

* style(tests): format sandbox timeout regressions

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-23 22:35:10 +08:00
NanPan
1700ac2c82
fix(events): honor empty event type filters in DB store (#7352)
* fix(events): treat empty event type list as an empty filter

* test(events): cover empty event-type filters across stores
2026-10-23 22:34:23 +08:00
Evan
195bf358ca
fix(acp): fail fast when an ACP agent exits and drain its stderr (#7354)
* fix(acp): fail fast when an ACP agent exits and drain its stderr

An ACP agent that exited before answering (missing credentials, a wrong
argument) left invoke_acp_agent waiting for the whole timeout_seconds
budget (30 minutes by default) and then suggested raising the timeout.
The pinned SDK never fails pending requests when the agent's stdout
reaches EOF, so nothing noticed the process was gone.

The SDK also opens the agent's stderr as a pipe and never reads it. An
agent that logged more than asyncio's pipe buffering absorbs blocked on
its next stderr write and stalled the invocation the same way.

Race the ACP exchange against the agent process: when it exits (or a
newer SDK reports the connection closed), allow a short grace for
responses still buffered in the pipe, then report the exit code. Drain
stderr continuously into a bounded tail that is logged on failure and
kept out of model-visible text.

* fix(acp): log the full stderr tail on timeout and pin tail retention

Log the timeout tail after the SDK context has terminated and reaped the
agent, so lines still in flight when it stalled are included. Make the
exit test write more stderr than the retained tail, so keeping the start
of the stream instead of its end fails the assertion on the fatal line.
2026-10-23 22:30:26 +08:00
ShiYuan Xiong
3802db3392
feat(agent): add read-only run status introspection (phase 1) (#7297)
* feat(agent): expose read-only run status tool

* refactor(tools): name the subagent dispatch tool set

Hoist the hardcoded subagent dispatch tool-name literal into _SUBAGENT_DISPATCH_TOOL_NAMES so a future dispatch-tool rename has one place to update. No behavior change: the folded-subagent context_fill guard compares the same two names.

* test(tools): account for run status in collision assembly

* docs: keep script guidance within size budget
2026-10-23 22:06:00 +08:00
Cordis798
57ceff7d50
fix(eval): distinguish booleans from numbers in snapshot grading (#7358)
Co-authored-by: Cordis798 <284918518+Cordis798@users.noreply.github.com>
2026-10-23 22:02:50 +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
Daoyuan Li
6e240b4bc3
fix(sandbox): search the skills root across category mounts (#7338)
* fix(sandbox): search skills across category mounts

* fix(sandbox): address virtual search mount review

Search explicitly configured category mounts regardless of ignored names,
while retaining descendant filters and host-parent exclusion. Warn with the
virtual mount path before re-raising broken-mount errors, defer ordinary
path resolution, and cover lookahead across mount boundaries.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-23 21:41:24 +08:00
Aling33
48c884a533
fix(sandbox): treat a fetched process substitution as an interpreter program (#7290)
* fix(sandbox): treat a fetched process substitution as a program operand

`bash <(curl url)` hands the fetched file to bash as an *operand*, not as a
redirect: bash runs it. The audit only recognised the stdin spelling
(`bash < <(curl url)`), so the operand spelling executable-fetched content
through the sandbox audit unimpeded — the miss documented in the middlewares
AGENTS.md known-gaps list.

An interpreter in command position whose first operand is a risky process
substitution now blocks. The two exceptions mirror the stdin rule: a script
operand in front (`bash script.sh <(curl url)`) keeps the substitution an
ordinary argument, and a shell noexec flag (`bash -n <(curl url)`) only
parses it.

Verified in Git Bash:
  bash <(echo "echo FETCHED_AS_PROGRAM")            -> FETCHED_AS_PROGRAM
  bash <(echo "echo FETCHED_WITH_ARG") consumer.sh  -> FETCHED_WITH_ARG
  bash -n <(echo "echo NOEXEC")                     -> exit 0, no output

* docs(sandbox): clarify substitution audit limitations

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-23 21:08:17 +08:00
Tsai Yuan
9f1fd2fc05
fix(models): preserve escapes in MindIE tilde fences (#7264) 2026-10-23 20:59:38 +08:00
Hyeonsang Cho
3d2ed725f6
fix(tui): let quit skip an interrupted run's unfinished step (#7326)
* fix(tui): let quit skip an interrupted run's unfinished step

After Ctrl+C the synchronous agent stream keeps running until its next
event. The run was a Textual thread worker, i.e. a thread on asyncio's
default executor, which App.run() joins on shutdown (up to 300 s on
3.12) and interpreter exit joins again with no timeout. Quitting while
a long step was still running therefore hung silently after the
terminal had been restored, and further Ctrl+C presses only printed a
traceback.

Run workers on a daemon thread instead. Quit now waits visibly for
running workers (an interrupted step still checkpoints, a finished run
still saves its title) and refuses sends meanwhile; a second quit
request exits at once. Report unexpected worker exceptions as an error
row and end the run, since Textual no longer catches them.

* docs(changelog): reference #7326 for the TUI quit-after-interrupt fix

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-23 20:33:07 +08:00
Totoro
fc4f719664
fix(goal): retain supplied evidence and retry one malformed evaluator verdict (#7345)
* fix(goal): use spare evidence budget for long user input

* fix(goal): retry one malformed evaluator verdict

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-23 20:29:35 +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
Hyeonsang Cho
e7d96fe051
fix(tui): append streamed assistant deltas verbatim (#7325)
* fix(tui): append streamed assistant deltas verbatim

The reducer merged each delta by comparing it with the row text: an
equal delta was a no-op, a delta starting with the row replaced it, and
a delta the row started with was dropped. Those rules absorbed re-sends,
replayed history and cumulative snapshots, none of which
DeerFlowClient.stream emits any more, so they only deleted real tokens
("ha" + "ha" -> "ha", "1" + "10" -> "10") and every standalone Markdown
marker an answer opened with (closing **, list bullets, table pipes,
closing code fences).

Append every delta on both the id-keyed and anonymous paths, and drop
_merge_stream_text. Rewrite the tests that pinned the re-send premise
as append contracts, run the merge cases through reduce() on both
paths, and drive the real client stream into the reducer.

* docs(changelog): note TUI streamed-delta fix (#7325)

* docs(changelog): drop trailing spaces after the #7325 entries

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-23 18:29:08 +08:00