4560 Commits

Author SHA1 Message Date
yi-coding
c04946a73f
fix(mcp): retain durable control claims through slow calls and cancellation (#6780)
* fix(mcp): retain durable control claims through slow calls and cancellation

* docs(mcp): distinguish poll and cancel claim fencing

---------

Co-authored-by: xyi74976-del <290725798+xyi74976-del@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-21 22:03:21 +08:00
Qiu
a674132eed
fix(channels): recover from undecodable thread-binding state (#7233)
Fixes bytedance/deer-flow#7232.

Co-authored-by: Jun516 <174629376+Jun516@users.noreply.github.com>
2026-10-21 21:56:12 +08:00
dependabot[bot]
feb7f3ac65
chore(deps): bump soupsieve from 2.8.4 to 2.9 in /backend (#7250)
Bumps [soupsieve](https://github.com/facelessuser/soupsieve) from 2.8.4 to 2.9.
- [Release notes](https://github.com/facelessuser/soupsieve/releases)
- [Commits](https://github.com/facelessuser/soupsieve/compare/2.8.4...2.9)

---
updated-dependencies:
- dependency-name: soupsieve
  dependency-version: '2.9'
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-21 21:16:30 +08:00
Storm
c277c1572e
fix(harness): complete Lark device auth via a single-flight login run (#7182)
* fix(harness): complete Lark device auth via a single-flight login run

The web authorization polling loop reused the request wait budget as the
lark-cli auth login subprocess lifetime: each /lark/auth/complete poll ran
the CLI with subprocess timeout=wait_timeout_seconds (8s from the frontend)
and killed it on timeout. The login transaction is not atomic — poll, redeem
the single-use device code, fetch user info, then persist credentials — so a
kill after redemption but before persistence stranded the flow, and the next
poll with the same code failed with 'device_code is invalid'.

Own the login process by the flow generation instead: the first poll spawns
one background run per (user_id, generation) and later polls attach to it;
wait_timeout_seconds only bounds the caller's wait (unchanged 504 contract)
and never terminates the CLI. Advancing the generation or re-issuing a device
code abandons and kills the orphaned process, and a finished run's outcome
stays attachable for 120s so late polls return the same result instead of
re-consuming the redeemed code.

* fix(lark): tolerate vanished credential-tree entries and bound owner-alive grace

Publication could kill the login worker with an unhandled FileNotFoundError
when a concurrently-written temp file vanished between iterdir() and lstat(),
leaving the marker live but no result file ever published; every foreign poll
then blocked 504 until the 600 s attempt bound, and could respawn an
already-redeemed code.  Also, a bare-pid owner_alive check trusted for the
full 600 s let a recycled-pid or crashed-thread marker outlive any process
that could still publish.

- _credential_tree_path_kind returns "gone" on FileNotFoundError so the
  walk skips vanished entries instead of aborting; the same tolerance is
  applied to iterdir().
- _write_lark_login_result_once treats the re-harden as best-effort: a
  filesystem hiccup is logged and publication continues so the marker sweep
  still runs.
- The marker now records owner_pid_started_at and
  _read_live_lark_login_marker_locked only keeps an owner-alive marker
  within a 10 s publication grace, so a stale marker is swept and the flow
  respawns instead of wedging every foreign poll.

Regression: test_walk_and_harden_tolerates_a_vanished_entry,
test_write_result_once_continues_when_hardening_fails,
test_abandoned_marker_with_dead_owner_is_swept_within_grace.
Focused suites: 139 passed, 12 skipped.

* fix(lark): harden surviving credentials through filesystem churn

Atomic login publication can remove a descendant after lstat but before
chmod. Catch FileNotFoundError around the entire per-entry walk so that
lstat, chmod and directory enumeration tolerate those disappearances.
A vanished root, permission error or symlink remains a hard failure.

Exercise real removals at each boundary and concurrent result publication;
assert that injection ran and surviving directories/files reach 0700/0600.
The regressions detect a skipped walk, swallowed permission failures and
loss of directory-churn tolerance in isolated mutation runs.

* fix(lark): retain login ownership through durable completion

Browser approval can take longer than the publication grace. Start that
grace at the first observed CLI exit, then reject abandoned attempts
without retrying a device code that may already have been redeemed.

Serialize atomic result publication under the credential lock. Preserve
published results through cleanup failures and retain terminal failures
on the marker when result storage fails. Never bypass credential hardening.
Reserve storage before spawning and roll back failed marker, process or
thread startup by reaping the CLI, clearing the registry and waking waiters.

Add process/filesystem failure regressions, a long-login two-worker case,
and strict unhandled-thread gates. Fix invocation logging in both workers
and reap forked test CLIs after the parent observes their launch. Document
the authorization lifecycle and required regression assertions.

Validated the reproduced failures and ten isolated mutations; final
focused, blocking-I/O, full-suite and independent review details are in PR #7182.
2026-10-21 21:00:35 +08:00
Lucien
ff07f87cc3
fix(browser): preserve Live keyboard modifiers (#7213)
* fix(browser): preserve Live keyboard modifiers

* test(browser): cover Meta modifier on named keys

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-21 20:57:56 +08:00
Tsai Yuan
c70dd685a9
fix(scripts): select sandbox containers by anchored name (#7189) 2026-10-21 20:57:02 +08:00
dependabot[bot]
702528e5c3
chore(deps): bump aiohttp from 3.14.3 to 3.14.4 in /backend (#7226)
---
updated-dependencies:
- dependency-name: aiohttp
  dependency-version: 3.14.4
  dependency-type: indirect
...

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-21 20:21:06 +08:00
dependabot[bot]
68f73b9c61
chore(deps): bump oauthlib from 3.3.1 to 4.0.0 in /backend (#7240)
Bumps [oauthlib](https://github.com/oauthlib/oauthlib) from 3.3.1 to 4.0.0.
- [Release notes](https://github.com/oauthlib/oauthlib/releases)
- [Changelog](https://github.com/oauthlib/oauthlib/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/oauthlib/oauthlib/compare/v3.3.1...v4.0.0)

---
updated-dependencies:
- dependency-name: oauthlib
  dependency-version: 4.0.0
  dependency-type: indirect
...

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-21 19:55:33 +08:00
dependabot[bot]
813ae3f2f5
chore(deps): bump certifi from 2026.4.22 to 2026.10.19 in /backend (#7239)
Bumps [certifi](https://github.com/certifi/python-certifi) from 2026.4.22 to 2026.10.19.
- [Commits](https://github.com/certifi/python-certifi/compare/2026.04.22...2026.10.19)

---
updated-dependencies:
- dependency-name: certifi
  dependency-version: 2026.10.19
  dependency-type: indirect
...

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-21 19:54:58 +08:00
Totoro
f6fe1e9c9e
fix(workflows): align sequential Agent handoff size limits (#7231)
Allow ordinary Agent tasks the same 16,000 JSON bytes already accepted for
file-reference tasks, keeping the 12,000-byte output cap and bounded rejection.
Cover ASCII, escaped Unicode, maximum output and pre-admission rejection.

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-21 19:02:26 +08:00
Willem Jiang
4b7f2454fe
test(e2e): stabilize virtualized pagination and compaction checks (#7244)
* test(e2e): stabilize virtualized pagination and compaction checks

* test: register the scoped E2E guidance file
2026-10-21 18:58:53 +08:00
李刚 / Bryan Li
033fb1aa1f
fix(github-research): report a failed fetch instead of returning it as content (#7221)
`get_readme` and `get_file_content` answered every failure with a string --
`[README not found: HTTP 404]`, `[File not found: HTTP 404]` -- while `main()`
prints any string result on stdout and exits 0. A rate-limited run, a missing
repository and a real README therefore arrived on the same channel with the same
exit code, so a caller reading stdout could not tell a failure from a body.

Both readers now raise `GitHubAPIError` naming the operation, the target and the
underlying HTTP error. The CLI's existing handler already writes an exception to
stderr and exits 1, so a failure leaves the success channel empty and the status
stays visible; a successful fetch is unchanged.

Pins both transports (requests and the urllib fallback), 404 against 403 so a
rate limit cannot read as a missing repository, the file reader's path, and the
CLI contract: non-zero exit, empty stdout, status on stderr.
2026-10-21 18:18:51 +08:00
Nan Gao
f39cdd6077
fix(mcp): keep rotating OAuth refresh tokens across restarts and Gateway instances (#7207)
* fix(mcp): keep rotating OAuth refresh tokens across restarts and Gateway instances

MCP servers on the OAuth refresh_token grant lost their rotated refresh
token whenever a new process or manager started (restart, tool-cache
reset for non-task servers, every personal-connection run, a second
Gateway instance) and re-seeded with the consumed configured token,
getting invalid_grant for good.

- New table mcp_oauth_tokens (migration 0047_mcp_oauth_tokens), one row
  per refresh_token connection keyed (scope, owner_user_id, server_key):
  ('deployment', '', name) or ('personal', owner, personal runtime
  name). Access and refresh tokens are DEER_FLOW_CREDENTIALS_KEY
  ciphertext; a config fingerprint (token_url, client_id, grant, scope,
  audience, seed) makes a changed seed win.
- OAuthTokenManager re-reads the row under its process lock, adopts a
  peer's valid token, and otherwise claims the refresh with a lease plus
  a version compare-and-set (database clock), fetches outside any
  transaction, writes under the lease and releases it on failure.
- The Gateway installs the store (sync engine via to_thread) around the
  LangGraph runtime for sqlite/postgres; personal managers pass their
  owner. client_credentials stays in process; an unloadable key falls
  back to process memory; database errors fail the refresh.
- credentials_key_consumers lists refresh_token MCP OAuth servers, so a
  declared multi-instance deployment needs the shared key.

Docs: MCP_SERVER.md, CONFIGURATION.md, Helm README, README, mcp and
migrations AGENTS.md, CHANGELOG (en/zh).

* docs(changelog): link the MCP OAuth token store entry to #7207

* fix(mcp): prune removed connections' OAuth token rows and ask owners to re-authorize on invalid_grant

Review follow-up (willem-bd):
- Both MCP config writers drop mcp_oauth_tokens rows of connections a
  save removes (deployment: _commit_mcp_config_write; personal:
  _mutate, keeping each connection's enabled/disabled runtime-name
  variants so a toggle keeps its chain). Best effort; no startup sweep,
  since a missing config file would look like every server removed.
- invalid_grant on a refresh_token request raises
  McpOAuthReauthorizationRequired: the tool error names the server and
  asks for a new refresh token, plus the shared-key hint when this
  instance could not decrypt the stored chain.
- Annotate the gateway helpers' config as AppConfig.

* fix(mcp): renew the OAuth refresh lease, keep unstored rotations, and key access tokens by request parameters

Review follow-up (willem-bd, second round):
- HTTPX timeouts bound each I/O wait, not a whole response: the refresh
  holder now renews its lease every third of REFRESH_LEASE_SECONDS while
  the token request runs and abandons it after REFRESH_MAX_SECONDS (120 s);
  waiters outlast a live holder. Regression uses a real loopback endpoint
  that streams its body for three lease lengths.
- A refresh whose tokens could not be stored keeps them pending
  (unsaved_base_version): they are written back before an older stored
  token is adopted or refreshed with, retried every few seconds while the
  access token is still valid, and dropped once a peer stored a newer
  chain. A failed save releases the lease.
- New token_request_fingerprint (extra_token_params and response fields):
  a stored access token is reused only under the same request parameters;
  otherwise the stored refresh-token chain is refreshed.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-21 18:17:29 +08:00
huyengiang101086-spec
1ad8147947
fix(channels): pin JSON state encoding across platforms (#7228)
* fix(channels): pin JSON state encoding across platforms

* test(channels): check encoding pins at each state I/O call

---------

Co-authored-by: huyengiang101086-spec <270684046+huyengiang101086-spec@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-21 18:16:22 +08:00
Willem Jiang
d4012c4b24
ci(e2e): split browser tests into four parallel shards (#7241)
* ci(e2e): split browser tests into four parallel shards

* docs: keep E2E guidance within the module size budget
2026-10-21 17:03:50 +08:00
dependabot[bot]
d7a11c1581
chore(deps): bump pillow from 12.3.0 to 13.0.0 in /backend (#7238)
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: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-10-21 17:03:23 +08:00
luckiest-draw
3f3cd23965
fix(config): avoid Unicode failure after generating configuration (#7102)
* fix(config): avoid Unicode failure after generating configuration

* fix(setup): handle legacy stdout encodings in setup commands

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-21 16:03:39 +08:00
ningkexin96
4f948c7b8d
feat(deploy): select production Compose env files (#6522)
* feat(deploy): select production Compose env files

Use an operator-selected dotenv file consistently for interpolation, container environment, secret probes and build extras. Preserve default selection and shutdown after a profile is removed.

Co-Authored-By: Codex <noreply@openai.com>

* fix(deploy): address production env selection review

Clarify Docker launcher entry points and render real Compose through the launcher to verify both service environments.

Co-Authored-By: Codex <noreply@openai.com>

* test(deploy): cover unreadable env selectors

Reject unreadable explicit selectors before Docker is invoked, using POSIX permission checks and restoring the fixture mode in finally.

Co-Authored-By: Codex <noreply@openai.com>

* test(deploy): scope dotenv assertions to selected secrets

---------

Co-authored-by: Codex <noreply@openai.com>
2026-10-21 15:58:30 +08:00
Willem Jiang
140f0dc5f7
doc(changelog): update the PR refence order for merging (#7227) 2026-10-21 15:51:11 +08:00
Qiu
8b68832f32
fix(channels): release standalone DingTalk upload sandboxes (#7203)
Refs bytedance/deer-flow#3296.

Co-authored-by: Jun516 <174629376+Jun516@users.noreply.github.com>
2026-10-21 10:53:49 +08:00
Willem Jiang
de7307e6b3
doc(changelog): update the changelog with the latest PR merged (#7222)
* chore(doc):sync the chinese changelog info

* chore(doc):sync the changelog with 2.2.0 milestone

* chroe(doc):sync the Chinese changelog with the english one
2026-10-21 10:19:35 +08:00
Willem Jiang
e790e7e6ba
ci: upgrade Playwright to 1.62.1 for Ubuntu 26.04 (#7219) 2026-10-21 09:13:55 +08:00
Nan Gao
dc6f0d7421
fix(memory): persist the DeerMem extraction watermark across restarts and replicas (#7205)
* fix(memory): persist the DeerMem extraction watermark across restarts and replicas

The updater's per-thread extraction watermark lived only in an in-process
LRU, so a restart or a second Gateway replica on the same storage root fed
already-extracted turns to the memory LLM again.

FileMemoryStorage now keeps it in users/{user}/agents/{agent}/.metadata/
watermarks.json, keyed by thread id, written atomically under the per-user
lock without touching memory.json and capped by watermark_max_keys. Each
update feeds after the furthest of the LRU entry and the persisted one, and
writes through after an extraction or a pre-screen skip; bypass_watermark is
unchanged and sidecar IO failures fall back to the LRU. Memory clears remove
the file with the other .metadata sidecars. Content-based message identities
are stored as SHA-256 digests so the sidecar holds no conversation text.

* docs(changelog): link the memory watermark entry to #7204 and #7205

* fix(memory): never replace an unreadable watermark sidecar

set_extraction_watermark rewrites the whole watermarks.json, and the shared
sidecar reader turns an OSError into an empty mapping, so a failed read could
replace every other thread's watermark with the current one (or delete the
file). The watermark sidecar now has its own reader: a missing file reads as
empty, malformed content reads as empty (nothing to keep; the next write
repairs it), and any other OSError propagates before anything is written. The
updater already logs that and keeps the extraction successful.

* fix(memory): keep the persisted watermark when the last message has no identity

_message_identity returns None for a last message with neither an id nor
text. The updater wrote that None through, which deleted the thread's entry
from watermarks.json, losing a watermark a peer may have advanced and forcing
a full feed on every replica. The write-through now skips None: the persisted
entry still marks an extracted prefix and the furthest-of-both read keeps
using it.
2026-10-21 07:26:44 +08:00
jay
cb1d782fb3
fix(community): reject booleans in the knowledge_search provider settings (#7042) 2026-10-20 23:17:51 +08:00
SumEn
f4eaa51a73
fix(uploads): retain filesystem ownership across request cancellation (#7170)
* fix(uploads): retain filesystem ownership across cancellation

* fix(uploads): preserve cancellation across cleanup failures
2026-10-20 23:17:00 +08:00
Hyeonsang Cho
19e861d0c3
fix(skills): keep load error text out of user-facing skill read 500s (#7155)
* fix(skills): keep load error text out of user-facing skill read 500s

GET /api/skills, /api/skills/custom and /api/skills/{name} are open to
every signed-in user, but their 500 detail echoed str(e). A malformed or
unreadable extensions_config.json (re-read on every load by the
user-scoped storage) made all three return the file's absolute host path,
plus the [Errno 13] text for a permission error.

Return a fixed detail like the diagnostics and reload routes already do,
chain the cause, and keep the full error in the Gateway log.

* docs(changelog): reference #7155 for the skill read 500 detail fix

* test(skills): pin capability skill discovery 500 through the real Gateway app

GET /api/capabilities/installations/skills reads the same user-scoped
storage as the skill routes but has no handler of its own. Its 500 body
is TraceMiddleware's fixed fallback, so drive create_app() with a
malformed extensions_config.json and assert no host path reaches the
body on any of the four reads.
2026-10-20 22:51:02 +08:00
Stella-xixi
5cef1a11ef
fix(checkpoints): run conservative retention after completion (#6051)
* fix(checkpoints): wire conservative retention after runs

* fix(checkpoints): fence retention before checkpoint reads

* fix(checkpoints): address retention review feedback

* fix(checkpoints): retry delta reads after retention race

* fix(checkpoints): bound admission retention to the root tail

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 22:23:47 +08:00
FanouZeng-TT
eb7c342fb1
fix(sandbox): measure warm-pool idle age on a monotonic clock (#7035)
* fix(sandbox): measure warm-pool idle age on a monotonic clock

Warm-pool expiry measures a duration, but every provider stamped and reaped
entries using the wall clock. A forward correction could therefore destroy
fresh sandboxes, while a backward correction could retain expired ones until
the clock caught up.

Move every warm-pool writer and reader to time.monotonic(), including the
Tenki and OpenSandbox reset paths missed by the earlier closed attempt. Keep
absolute SandboxInfo.created_at and reconciliation grace timestamps on the
wall clock. Convert AIO's sibling active-idle timestamps as one clock domain,
and update finite clock mocks for the additional duration read.

Fixes #6970

* test(sandbox): keep active-idle race fixtures in the monotonic domain

The production AIO activity clock moved to monotonic time, but the shared
orphan-reconciliation helper still stamped active fixtures with wall time.
CI then computed a negative idle age, so two teardown race tests never reached
the ownership gate. Use the same monotonic domain as production.

* test(sandbox): read warm-pool idle fixtures on the monotonic clock

The idle-age conversion left several fixtures seeding the fields the reaper
now reads with values from the clock it no longer reads:

- `test_idle_reaping_forces_a_new_remote` froze `warm_pool_lifecycle.time.time`,
  so the benchmark's reaper compared against real elapsed monotonic seconds —
  zero over the test's lifetime. Nothing expired, the entry survived, and the
  second turn came back as a warm hit.
- Three orphan-reconciliation fixtures seeded `_last_activity` with `0.0` and
  two aio fixtures with `time.time()`, both of which are far below a running
  monotonic clock: the computed idle age went negative, so the reaper never
  reached the ownership claim these tests are built to exercise.

Seed them from `time.monotonic()` instead, and keep `created_at` (a wall-clock
field) on `time.time()`.
2026-10-20 19:36:04 +08:00
trent87
0d73798beb
feature(skills): keep allowed-tools as compatibility metadata (#6935)
* refactor(skills): keep allowed-tools as compatibility metadata

* docs(skills): align current guides with agent tool authority

* test(skills): simplify authority regressions and sync Korean docs
2026-10-20 19:34:28 +08:00
Willem Jiang
61e3513c66
docs: scope E2B upload guidance to fix CI budgets (#7181) 2026-10-20 19:25:54 +08:00
Totoro
059758f321
fix(runtime): stop Goal continuations after loop hard stops (#7167)
* fix(runtime): stop Goal continuations after loop hard stops

* chore: remove redundant SVG sources from Goal fix

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

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-20 16:55:13 +08:00
Dragon ze
d85e0e8608
fix(channels): keep staging cleanup from replacing the save error (#7106)
* fix(channels): keep staging cleanup from replacing the save error

The channel stores publish state through a staged temporary file and, if the
rename fails, close and unlink that file inside the handler before re-raising.
Neither step was guarded, so a failing close skipped the unlink and a failing
unlink replaced the error that actually caused the save to fail -- leaving the
staged copy, which for the runtime-config and WeChat stores holds an 0600 IM
token, behind with nothing reporting it.

Make both steps independently best-effort, name the leaked file in a WARNING,
and suppress the diagnostic itself so a broken log handler cannot become the
reported failure. This is the same contract PR #6871 establishes for browser
captures, applied to the three JSON publishers in app/channels.

* fix(channels): address staging cleanup review follow-ups

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 16:52:47 +08:00
Zicheng (Axelsen) Xie
1ce0b765de
fix(uploads): preserve separate bold spans in outline titles (#7150) 2026-10-20 16:39:33 +08:00
Fake1new
d288935865
fix(skills): preserve Markdown table cells in data-analysis exports (#7163)
* fix(skills): preserve Markdown table cells in data-analysis exports

* docs(skills): document Markdown cell escaping contract

Clarify handling of Markdown exports and escaping rules.

* docs: describe data-analysis Markdown export escaping

Clarify data-analysis skill features and behavior in README.

* test(skills): cover Markdown export headers and multiline cells

Add a test for markdown export functionality in data analysis.
2026-10-20 14:30:57 +08:00
Lucien
2c880a3133
fix(helm): forward Live Browser WebSocket upgrades (#7148)
* fix(helm): proxy Live Browser WebSocket upgrades

* fix(helm): use default browser connect and send timeouts
2026-10-20 13:17:54 +08:00
SumEn
53f09dc152
fix(projects): preserve document upload outcomes during cleanup (#7141)
* fix(projects): preserve upload outcomes during staging cleanup

* fix(projects): guard staging discard failures

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 13:15:40 +08:00
李刚 / Bryan Li
1fd266e0c0
feat(decisions): add an OpenAI Decisions backend for the decision slots (#7127)
* feat(decisions): add an OpenAI Decisions backend for the decision slots

Issue #7005 asked for a second decision provider behind the interfaces the
TypeSafe (Jev) integration already uses, not a new decision system.
`deerflow/openai_decisions` speaks `POST /v1/decisions`: a `noul` question
becomes a `predicate` whose true/false rubric folds into `instructions` (the
endpoint has no criteria field, and dropping it would move the decision
boundary while the adapter kept reporting the same criteria in its policy
identity), answers arrive as an array rather than a mapping, and a refusal is an
answer `type` rather than an HTTP failure -- so it lands in the existing
per-question error channel and each consumer keeps its own failure direction.

Transport is reused rather than copied. `provider_label` and the connection's
`endpoint_path` are the only two seams a dialect needs, so retry and backoff,
the deadline budget, the bounded never-decoded body read and the error taxonomy
keep one owner; `resolve_decisions_connection` pairs the two so a Decisions
connection cannot be built pointing at System One. The three adapters override
their connection resolution, their client, and the value the endpoint judges
(`input` text instead of a `state` object), and inherit thresholds, rubrics,
caches and audit policy unchanged. Wiring stays a `use:` class path, so no
config schema is added.

* fix(decisions): isolate endpoint identities and correct diagnostics

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 11:50:46 +08:00
Jason Draper
f2c2490d26
chore(frontend): drop deprecated hast and @types/gsap packages (#7108) 2026-10-20 11:30:44 +08:00
LuckVd
2174c1d81e
fix(memory): quarantine unreadable memory summaries (#7076)
* fix(memory): quarantine unreadable memory.json in the read-error branch too

MarkdownMemoryStorage._load_memory_file quarantined summaries that failed
to decode as JSON/Markdown, but the earlier except (OSError, UnicodeError)
branch (unreadable bytes, e.g. a UTF-16 BOM) still returned None without
quarantining. The leftover file was then silently overwritten by the next
save, destroying the user's memories with no backup.

Call the existing best-effort self._quarantine_unreadable(path) there as
well, matching the documented quarantine contract, and add a regression
test asserting the original bytes survive a subsequent load+save.

Fixes #7072

* fix(memory): preserve summaries on transient read failures

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 11:25:01 +08:00
杰尼龟
68d4e5527c
fix(tui): accept whitespace separators in slash commands (#7117)
* fix(tui): accept whitespace separators in slash commands

* fix(tui): align slash palette with Unicode whitespace

Close the slash palette on every whitespace separator accepted by the command resolver, including pasted tabs and Unicode spaces. Document the accepted delimiters and cover parser arguments, palette closure, Enter submission and Tab completion.

Validation: 352 focused tests, 34,089 offline backend tests and 262 blocking-I/O tests passed. Backend formatting, frontend check and agent guidance passed.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 11:19:45 +08:00
Starchaser1024
a83aa36309
fix(models): fall through an unusable credential source instead of ending the lookup (#7140)
* fix(models): fall through an unusable credential source instead of ending the lookup

load_claude_code_credential's env branch returned None even when
_credential_from_direct_token rejected the value, so a valid
~/.claude/.credentials.json was never consulted for a blank token.

load_codex_cli_credential's 'or' chain stops at the first truthy layout,
so a blank or non-string top-level access_token hid a usable 'token' or
'tokens.access_token' in the same file -- and made the loader disagree
with scripts/doctor.py, which validates all three layouts.

Fixes #7139

* fix(models): try both env token sources and log the rejected one

A whitespace-only CLAUDE_CODE_OAUTH_TOKEN is rejected by _credential_from_direct_token, but os.getenv(A) or os.getenv(B) never reads ANTHROPIC_AUTH_TOKEN while A is truthy, so the blank first variable hid a usable second one. Test each source with the same non-blank rule, and log the rejected variable at debug (no token material) so the resolution outcome stays observable.

Also refresh the doctor.py mirror comment: the loader rejects whitespace-only tokens in every layout now, so the documented strictness divergence is gone.

* style(tests): format credential fallback log assertion

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 10:30:52 +08:00
Lucien
1ae50826b1
fix(frontend): isolate workspace query caches by authenticated owner (#7146)
* fix(frontend): isolate query caches by authenticated owner

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

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

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 10:26:48 +08:00
LingYi
ee02e51df9
fix(memory): preserve consecutive pre-compaction snapshots (#7152)
* fix(memory): preserve consecutive pre-compaction snapshots

* fix(memory): warn on growing emergency snapshot backlogs

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 10:24:16 +08:00
Gusanity
a759a84201
test: skip POSIX-only skill mutation tests on Windows (#7154)
* test: skip POSIX-only skill mutation tests on Windows

* test: release SQLite fixture connection on Windows

---------

Co-authored-by: unknown <F028@sophon.gd>
2026-10-20 10:03:05 +08:00
OUAO-FRANK
5e5913f4cf
fix(codex): honor Retry-After in the provider retry loop (#7151)
* fix(codex): honor Retry-After in the provider retry loop

* style(codex): format retry-after regression helper

* fix(codex): honor HTTP-date Retry-After hints

---------

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

* docs(frontend): explain reconnect answer boundary

---------

Co-authored-by: TYY-2005 <TYY-2005@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 09:57:28 +08:00
lakers-abing
f23a411325
fix(storage): resolve single-module colon backend imports (#7017)
* fix(storage): resolve single-module colon backend imports

* docs(storage): describe both backend import separators

---------

Co-authored-by: lakers-abing <236384465+lakers-abing@users.noreply.github.com>
2026-10-20 09:35:56 +08:00
yu1128-L
badfd7d3f1
fix(docker): make dev provisioner opt-in via compose profile (#6948)
* fix(docker): make dev provisioner opt-in via compose profile

Gate the Kubernetes provisioner behind a Compose profile and have the
wrapper enable it only when config selects provisioner sandbox mode.
This lets the default Docker Compose stack start without kubeconfig.

Fixes #3232

* test(docker): render every compose profile in the env-file regression test

main added backend/tests/test_compose_default_bind_host.py, whose _render helper asserted that the dev provisioner still loads ../.env. `docker compose config` without a profile omits profile-gated services, so that assertion failed once the provisioner became opt-in.

Pass `--profile *` from the helper: the test is about an existing .env reaching every service, so it has to render the services a profile gates rather than only the ones `up` starts by default.

* docs(docker): restore script mode and localize direct Compose guidance

The dev provisioner is now opt-in, but the earlier edit also flipped scripts/docker.sh from 100755 to 100644 even though README.md still documents direct `./scripts/docker.sh` invocation. Restore the executable bit.

Also add the direct-Compose guidance to the translated READMEs so readers of README_zh.md, README_ja.md, and README_fr.md learn that `--profile provisioner` selects the Kubernetes mode and the default stack does not need Kubernetes.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 09:10:24 +08:00
Hyeonsang Cho
5a530b31ee
fix(skills): answer invalid custom-skill history names with 400 (#7123)
* fix(skills): answer invalid custom-skill history names with 400

GET /api/skills/custom/{skill_name}/history let the ValueError from
validate_skill_name fall through to its catch-all, so a malformed name
returned 500 and logged a traceback on every request, while delete and
rollback answer 400.

Validate the name up front instead of adding a handler-wide
except ValueError: a corrupt history line raises JSONDecodeError, also a
ValueError, and must stay a server error. Both outcomes are pinned.

* fix(skills): keep corrupt history a 500 on rollback, validate delete names early

Sibling sweep of the history fix. Rollback's handler-wide except ValueError
also caught json.JSONDecodeError, so a corrupt history record answered 400
with the parser message and no log; it is now a logged 500 like history.

Delete only failed an invalid name inside the drained mutation tail, after
taking the owner's mutation lock, and logged it as a tail error although
_drain_skill_mutation keeps caller 4xx unlogged. It now rejects the name
before any storage work. Each guard is pinned by a mutation-checked test.

* docs(changelog): reference #7123 for the skill history status fix

* fix(skills): treat non-UTF-8 skill history as a server fault on rollback

UnicodeDecodeError is a ValueError too, so a non-UTF-8 history file still
reached rollback's ValueError -> 400 mapping while GET .../history answered
the same file with 500. Catch it alongside JSONDecodeError and pin both
corruption shapes on both routes.

Also drop the whitespace-only separator lines the changelog merge added
between other entries.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 09:00:03 +08:00
MsNBBc3R
2259df1130
fix(skill-creator): tolerate malformed timing measurements (#7078)
* fix(skill-creator): tolerate non-object timing measurements

Treat optional timing JSON values that are not objects as missing measurements so they cannot abort benchmark aggregation. Preserve grading durations, character-count fallback and recorded zeros; add regression coverage for both layouts and subsequent runs.

* fix(skill-creator): warn and validate timing measurements

* fix(skill-creator): tolerate unreadable timing files

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-20 08:30:37 +08:00