176 Commits

Author SHA1 Message Date
Amorend
85c3909c2e
feat: show real-time context window usage (#3125) (#3183)
* feat: show real-time context window usage in chat UI (#3125)

Adds a `context_usage` block to `GET /api/threads/{id}/token-usage`
(token count from the live checkpoint, the thread model's
`context_window`, and a percentage), introduces a new
`ModelConfig.context_window` distinct from the per-call `max_tokens`
output cap, and surfaces the percentage in the chat header — inside
`TokenUsageIndicator` when token-usage tracking is on, or as a
standalone badge when it's off so context capacity stays visible
independent of cost tracking.

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

* feat: per-category breakdown for context window usage

Replace the single-number context_usage payload with a Claude-Code-style
breakdown — messages, system prompt, skills, system/MCP tools (active +
deferred), custom agents, memory injection, autocompact buffer, and free
space — and surface it in the chat UI with a segmented progress bar and
per-row table.

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

* docs(config): document context_window across model examples

Add `context_window` to every example model in config.example.yaml so the
new chat-UI "% context used" indicator works out of the box for whichever
example a user adopts. Each value is the published default at the time of
writing; users are pointed at the official model spec to verify. Bumps
config_version to 11 so `make config-upgrade` flags outdated user configs.

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

* style: ruff format (line-length 240)

No behavior change — collapses two multi-line expressions that fit on
one line under the project's 240-char limit. Picked up by `make format`.

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

* review: address Copilot bot comments on #3183

- token-usage-indicator: switch `{contextPercentage && (...)}` to an
  explicit `!= null` check. (The string `"0"` is actually truthy in JS so
  the original code wasn't buggy, but the explicit check is clearer.)
- context-usage-breakdown: drop the `useMemo` around segments/totals — the
  computation is O(n) over a handful of rows and the previous memo deps
  omitted `t.contextUsage.categories`, so the bar's tooltips/aria-labels
  could stay in the old language after a locale switch.
- context_usage._split_tools: snapshot MCP names from
  `get_cached_mcp_tools()` directly instead of re-reading
  `extensions_config.json` after `get_available_tools()` already loaded
  it. Removes redundant file I/O on every `/token-usage` poll.
  (`get_available_tools()` still emits its own INFO logs — silencing
  those is out of scope here.)

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

* style(frontend): prettier --write context-usage-breakdown

CI's `pnpm format` (prettier --check) caught two lines previously
formatted by hand. Collapses one comma to fit on one line; no behavior
change.

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

* fix(gateway): correct context-usage breakdown + add exact token counting

The context-usage indicator shipped two bugs that silently zeroed whole
breakdown rows (both caught by try/except, so the feature looked alive but
produced wrong numbers):

1. _count_system_prompt passed app_config= to get_deferred_tools_prompt_section,
   which only accepts deferred_names -> TypeError swallowed -> system_prompt
   row always 0, and used_tokens/percentage undercounted by the full prompt.
   Also subtracted the deferred section twice (the rendered prompt already
   excluded it). Fix: derive deferred names deterministically and pass them to
   apply_prompt_template; drop the redundant subtraction.

2. _split_tools imported a non-existent get_deferred_registry -> ImportError
   swallowed -> all four tool-category rows always 0. Fix: classify via the
   public is_mcp_tool predicate + tool_search.enabled (mirrors
   build_deferred_tool_setup); the MCP tag is set by get_available_tools.

Added token_usage.counting (approximate|exact). 'exact' routes text/schema/
message counting through the model tokenizer (tiktoken cl100k_base) via the
existing memory-module machinery (lazy load + cache + cooldown + CJK-aware
fallback), so CJK-heavy threads stop being undercounted by chars//4.

Regression + e2e tests added; 6621 backend tests pass.

* fix(gateway): harden context usage accounting

* fix(gateway): count promoted MCP tools as active in context usage

Promoted tools (deferred MCP tools the thread has fetched via tool_search)
have their full schema bound on every subsequent turn by
DeferredToolFilterMiddleware, so they consume context like any active tool.
The breakdown previously left them in the reserved *_deferred rows, under-
counting the thread's used_tokens.

Classification now treats a tool as deferred only when tool_search is enabled,
it is MCP-sourced, AND it has not been promoted. The promoted set is read from
the checkpoint's channel_values and scoped by catalog hash — matching the
runtime middleware, so a stale promotion from MCP-config drift cannot inflate
the active count.

The static system prompt still lists all deferred tool names (promotions only
affect schema binding, not the prompt), so _count_system_prompt's deferred
rendering is intentionally left unchanged.

8 new tests cover classification, catalog-hash scoping (match / drift /
compute-failure / malformed), and checkpoint extraction.

* fix(context): address review feedback

* fix(context): count structured message payloads

* fix(context): harden usage accounting

* fix(config): bump schema for context usage fields

* refactor: narrow context usage to core indicator

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-31 21:57:22 +08:00
Xinmin Zeng
f2e832330e
fix(sandbox): enforce disabled skills in filesystem views (#4178)
* fix(sandbox): project enabled skills into sandbox views

* fix(skills): keep projection mutations consistent

* fix(skills): fail closed on projection errors

* fix(skills): isolate per-scope failures during boot projection rebuild

rebuild_all_skill_projections() propagated any exception from the public
rebuild or from a single user's rebuild straight out of the gateway
lifespan startup, uncaught. A single broken user directory (bad
permissions, corrupted _skill_states.json, unreadable content) would
therefore abort gateway boot for every user, not just that one -
_rebuild_*_locked already fails closed internally (clears the view and
re-raises), so the boot loop only needed to stop treating that re-raise
as fatal.

Each scope's rebuild now fails closed independently and boot continues;
a scope left empty by a boot failure self-heals on the next sandbox
acquire via ensure_skill_projections().

Also patches deerflow.skills.projection.rebuild_all_skill_projections in
the memory-flush lifespan test fixture, matching the two sibling
fixtures in the same file — this call is now on the lifespan startup
path and the fixture's minimal SimpleNamespace config predates it.

* test(skills): update authz test for the projection-aware public toggle

_persist_shared_skill_state (introduced earlier in this branch) reads
the shared extensions_config.json fresh from disk under the projection
lock instead of through the cached get_extensions_config() singleton -
that's the whole point of the fix (stale worker caches must not clobber
another worker's concurrent update). The name no longer exists on the
skills router module, so the test's monkeypatch of it started raising
AttributeError instead of exercising the endpoint.

The mock storage in this test isn't a real LocalSkillStorage instance,
so _persist_shared_skill_state's projection-mutation branch is already
skipped (nullcontext) and it falls back to a fresh ExtensionsConfig()
for the nonexistent tmp config_path - no replacement monkeypatch needed.

* fix(sandbox): make skill projection ensure best-effort in acquire

acquire() called _ensure_skills_projection() directly, outside any
try/except, in both LocalSandboxProvider and AioSandboxProvider. Every
other skill-mount setup path in these providers has always caught
exceptions and logged a warning rather than failing sandbox acquire
outright (e.g. when config.yaml can't be resolved) - these two new call
sites broke that contract, so any projection failure (including simply
not having a config.yaml, as in CI's test environment) now failed
acquire() itself instead of just leaving skill mounts off.

_ensure_skills_projection now catches its own exceptions and returns
None; both providers' callers already tolerate that (a None projection
skips the skill-specific mounts, matching the existing degrade path)
after making _append_public_skill_mapping and the custom/legacy mount
block in LocalSandboxProvider explicitly None-safe.

Caught by running the full suite with config.yaml removed, matching
CI's environment - not caught locally because a real config.yaml was
present, masking the failure.

* fix(sandbox): make E2B skill projection mounts best-effort

_skill_projection_mounts called ensure_skill_projections with no guard,
unlike Local/AIO's _ensure_skills_projection. A raise propagated out of
_apply_mounts before the configured-mounts loop ran, so a skills
projection failure dropped the operator's own configured mounts too -
only caught by create()'s outer warning, with nothing applied at all.

Swallow here and return an empty mount list on failure, matching the
Local/AIO pattern: still fail-closed for skills, but no longer widens
the blast radius to unrelated configured mounts.

Review feedback from PR #4178.

* docs(skills): document projection trade-offs flagged in review

- _update_tree_digest: note the metadata-only (not content) hashing
  trade-off and why runtime writes through this codebase are still
  covered regardless (rebuild-under-lock + rename always changes inode).
- LocalSandboxProvider.acquire: note the acquire-time self-heal cost
  (cheap on a fresh manifest, ~400ms rebuild under lock on stale/drift).
- skill_projection_mutation: drop the no-op except-Exception-then-raise;
  a raise from the mutation already propagates past the yield with the
  view left cleared, no explicit re-raise needed.
- provisioner README: spell out that hostPath skills volumes require
  the gateway and K8s node to share DEER_FLOW_HOST_BASE_DIR (single-node
  or shared storage), and that the custom/legacy volumes' hostPath type
  Directory (not DirectoryOrCreate) makes a violation of that assumption
  a visible Pod-creation failure instead of a silent empty mount.

Review feedback from PR #4178.

* fix(skills): lazily repair user projections

* fix(skills): close projection review gaps

* fix(skills): refresh user projection enable state

* fix(skills): close projection review follow-ups

* fix(skills): preserve state across projection writes

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-31 17:55:24 +08:00
MiaoRuidx
0cc28d2c42
fix(sandbox): enforce deployment-wide E2B capacity (#4575)
* docs: design deployment-wide E2B capacity

* fix(sandbox): enforce deployment-wide E2B capacity

* fix(sandbox): address E2B capacity review findings

* fix(sandbox): grace stale E2B capacity inventory

---------

Co-authored-by: MiaoRuidx <12540796+MiaoRuidx@users.noreply.github.com>
2026-07-31 17:13:12 +08:00
Huixin615
133a82c6c2
fix: isolate MCP server toggles from invalid peer configs (#4577)
* fix: isolate MCP server toggle updates

* fix: write extensions config atomically

* fix: normalize MCP transport aliases
2026-07-31 08:32:39 +08:00
qin-chenghan
60fe0c4433
fix(frontend): refresh active artifact content (#4584)
* fix(frontend): refresh active artifact content

* fix(frontend): remove 1Hz polling, keep only final refetch after run

Address review feedback: the 1Hz refetchInterval could poll
indefinitely when a write tool call is left unresolved (abort,
error, or missing ToolMessage). Removing the polling entirely
eliminates this risk while still satisfying the core requirement:
artifact content is refreshed once when the run settles, so edits
are visible without a manual reload.

- Remove refetchInterval / hasActiveWrite logic from hooks.ts
- Delete refresh.ts (hasActiveWriteForArtifact helper)
- Delete refresh.test.ts

* docs: align README and AGENTS.md with settle-time refetch behavior
2026-07-30 23:47:16 +08:00
qin-chenghan
0d8e11ad49
fix(frontend): persist artifact panel state (#4580) 2026-07-30 15:54:15 +08:00
Eilen Shin
9d915ca8ca
fix(agent): route subagents by net benefit (#4384)
* fix(agent): route subagents by net benefit

* fix(agent): refine subagent routing boundaries

* fix(agent): clarify routing limits and batches

* fix(agent): handle single-subagent routing
2026-07-30 07:21:55 +08:00
Ryker_Feng
594dbe1205
fix(console): disable cost reporting when model pricing mixes currencies (#4564)
The console cost estimator summed per-run spend across every priced model
without checking that they shared a currency, so a deployment that priced
one model in CNY and another in USD produced a meaningless aggregate under
a single currency label. Track the first priced currency and, on a
mismatch, log a warning and drop all pricing so cost/currency fields report
null instead of an invalid sum. Currency codes are now trimmed and
case-normalized so equivalent spellings don't false-trip the guard.
2026-07-29 22:24:04 +08:00
ShitK
6b5f5e789a
fix(tests): require explicit opt-in for live client tests (#4482)
* fix(tests): require explicit opt-in for live client tests

* test: align live target with marker
2026-07-29 22:16:48 +08:00
ShitK
4e44938551
fix: align pnpm consumers with Corepack fallback (#4405)
* fix: align pnpm consumers with Corepack fallback

* fix: run pnpm helper from frontend workspace

* fix: preserve Corepack resolution hint
2026-07-29 08:08:33 +08:00
Daoyuan Li
9bb8225079
fix(memory): harden OpenViking retries and watermarks (#4552) 2026-07-29 07:24:55 +08:00
Vanzeren
352f247a81
feat(memory): add mem0 HTTP memory backend (#4528)
* feat(memory): add mem0 HTTP memory backend

* fix(memory): address mem0 review feedback

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-29 07:11:20 +08:00
RongfuShuiping
b3af8c9183
feat(memory): keep tool-mode fact recall explicit (#4521)
* feat(memory): keep tool-mode fact recall explicit

* fix(memory): clarify optional tool-mode context
2026-07-29 06:50:19 +08:00
阿泽
9c7cd4cad3
feat(sandbox): add thread data mount override for upload sync (#4536) 2026-07-28 23:41:14 +08:00
RongfuShuiping
2aaf74b0f8
feat(memory): add OpenViking HTTP backend (#4509)
* feat(memory): add OpenViking HTTP backend

* fix(memory): harden OpenViking lifecycle

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-28 23:36:25 +08:00
Aari
a5059b8284
fix(subagents): isolate callbacks and activate skills lazily (#4497) 2026-07-28 23:29:14 +08:00
Vanzeren
c48de5e70b
feat(checkpoint): make delta snapshot_frequency configurable (#4516)
* feat(checkpoint): make delta snapshot_frequency configurable

* fix(config): carry legacy checkpoint_delta_snapshot_frequency with warning

Addresses review on #4516: the rename from the flat
database.checkpoint_delta_snapshot_frequency key to nested
database.checkpoint_delta.snapshot_frequency silently dropped the old
value (pydantic extra="ignore"). Add a before-validator that maps the
legacy key onto the nested one with a deprecation warning (nested key
wins when both are set), plus a CHANGELOG breaking-change note covering
the rename and the 1000 -> 10 default change.

* fix(checkpoint): validate frozen snapshot frequency
2026-07-28 23:21:23 +08:00
阿泽
ea74367502
fix(runtime): honor LangGraph Server identity for user-scoped data (#4538)
* fix(runtime): honor LangGraph Server identity for user-scoped data

* fix(runtime): scope custom agent SOUL by resolved user
2026-07-28 22:59:14 +08:00
qin-chenghan
1bccc8e20e
feat(frontend): allow chat replies during clarification (#4530)
* feat(frontend): allow chat replies during clarification

* fix(frontend): unlock input polish during clarification

Remove hasOpenHumanInputCard from inputPolishDisabled so the polish
button stays available when a clarification card is open, matching
the composer unlock behavior. Clean up the now-unused useMemo and
import.
2026-07-28 22:19:50 +08:00
Aari
e47bf80122
fix(runtime): regenerate interrupted responses (#4524) 2026-07-28 22:15:09 +08:00
MiaoRuidx
8a78c264b7
fix(runtime): cancel runs across live gateway workers (#4500)
* docs(runtime): design cross-worker cancellation

* fix(runtime): cancel runs across gateway workers

* fix(runtime): harden cross-worker cancellation races

让取消请求与 owner 终态写入通过持久化 CAS 决定先后,保证首次取消 action 在不同 worker 路由下保持一致。\n\n将 heartbeat 收敛为续租后仅发送本地中止信号,并补齐完成竞态与路由重试的回归用例。

* docs(runtime): drop implementation plan from PR

移除仅用于实现过程的跨 worker 取消设计记录,保留 README 和 backend/AGENTS.md 中面向最终行为的文档。

* fix(runtime): preserve local cancel fallback

* test(runtime): adapt worker run manager fakes

* docs(runtime): fix run cancel migration registry

---------

Co-authored-by: MiaoRuidx <12540796+MiaoRuidx@users.noreply.github.com>
2026-07-28 21:45:20 +08:00
ShitK
b1984cf4ab
fix(security): reject legacy MCP credentials in run metadata (#4448)
* docs: design run metadata secret admission

* docs: refine run metadata secret boundaries

* docs: plan run metadata secret fix

* fix(security): centralize legacy run metadata policy

* fix(security): reject secrets at run admission

* fix(security): hide legacy secrets from history APIs

* docs(security): migrate MCP credentials to secret context

* fix(security): redact legacy runnable config metadata

* fix(security): reject legacy config metadata credentials

* fix(security): hide legacy secrets from run kwargs

* docs(security): clarify config redaction boundary

* docs: keep issue 4416 planning local
2026-07-28 21:31:23 +08:00
阿泽
94003c1f47
feat(models): support cumulative vLLM stream usage (#4537)
* feat(models): support cumulative vLLM stream usage

* fix(models): preserve active cumulative usage streams
2026-07-28 19:56:40 +08:00
Aari
d455a1815e
fix(sandbox): allow grep to search a single file (#4512) 2026-07-28 07:49:13 +08:00
qin-chenghan
795af20a6b
feat(memory): built-in FTS5/BM25 retrieval adapter (#4360)
* feat(memory): integrate FTS5 retrieval adapter

* deps: add jieba as default dependency for Chinese tokenization

Without jieba, FTS5 unicode61 tokenizer treats entire Chinese sentences
as single tokens, making single-character or sub-phrase searches
impossible (e.g. '吃' or '油泼面' returns 0 hits against
'用户喜欢吃油泼面'). jieba segments Chinese text into meaningful tokens
before indexing.

* fix(memory): avoid treating hyphens as FTS5 operators

* feat(memory): make Chinese tokenization optional

* fix(memory): warm every requested retrieval scope

* fix(memory): close retrieval resources on shutdown

* fix(memory): close backend when shutdown flush fails

* fix(memory): recreate corrupt retrieval index

* fix(memory): tolerate partial retrieval rebuilds

* fix(memory): warm retrieval index in background

* fix(memory): preserve shutdown flush budget

* fix(memory): stop retrying partial lazy rebuilds

* fix(memory): close retrieval through storage

* refactor(memory): simplify retrieval scope limit

* docs(memory): clarify retrieval shutdown lifecycle

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-27 23:17:18 +08:00
Zybnev Sergey
a9a5fc9ced
fix(telegram): render final replies as Rich Messages (#4387)
* fix: render Telegram replies as rich messages

* Исправление fallback Rich Messages в Telegram
2026-07-27 22:51:02 +08:00
Ryker_Feng
fcbf0609b0
feat(chat): edit and rerun latest user turn (#4377)
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-27 22:46:51 +08:00
Vanzeren
6f53fd5e99
feat(runtime): enforce artifact delivery from workspace snapshots (#4494) 2026-07-27 22:27:16 +08:00
hataa
6091ce7576
feat(authz): derive Gateway route permissions from AuthorizationProvider (#4439)
* feat(authz): derive Gateway route permissions from AuthorizationProvider (Phase 2A, #4063)

Phase 2A: replace legacy _ALL_PERMISSIONS with provider-derived route
permissions. When authorization.enabled, each threads:*/runs:* permission
is evaluated independently via provider.aauthorize(resource='route').
Disabled mode preserves legacy behavior. owner_check and require_admin_user
remain unchanged.

10 new tests: disabled/enabled/RBAC policy/fail-closed/fail-open/
authenticate integration/middleware integration.

* fix(authz): move AuthorizationConfig import to runtime for fallback in _get_route_authorization_config
2026-07-27 14:19:04 +08:00
March-77
b22f85c686
fix(sandbox): reconcile E2B sandboxes safely (#4443)
* fix(sandbox): reconcile E2B sandboxes safely

* fix(sandbox): clear failed E2B adoption intent
2026-07-27 14:10:24 +08:00
Vanzeren
e01173d8b2
bench(checkpoint): production-shaped full/delta benchmark with configurable snapshot frequency (#4467)
* feat(checkpoint): production-shaped full/delta benchmark with configurable snapshot frequency

- Group benchmark scripts into per-family folders (checkpoint/, sandbox/)
- Extract shared benchmark infrastructure into checkpoint_bench_common.py
- Add checkpoint_delta_snapshot_frequency config (default 1000, process-frozen);
  freeze it in make_lead_agent and DeerFlowClient; key the state-schema
  adaptation cache by resolved frequency
- New bench_production.py: per-case child processes run N ainvoke turns through
  the real lead-agent graph (scripted deterministic model, real AsyncSqliteSaver),
  then measure GET /state + POST /history through the real Gateway route stack
  in one event loop (httpx ASGITransport), cold/warm accessor-cache split,
  cross-mode digest gates
- New summarize_production.py: delta/full ratios plus decision metrics
  (snapshot_write_spike, cache_effect_ms, checkpoint_write_share,
  auto-discovered history per-limit ratios)

* fix(checkpoint): address production benchmark review
2026-07-27 11:47:49 +08:00
Vanzeren
625c07b993
fix(runtime): resume original title when regenerating (#4480)
* fix(runtime): rusume original title when regenerating

* test(runtime): cover regenerated title sync
2026-07-27 11:32:10 +08:00
March-77
2e5c8da257
fix(sandbox): bypass proxies for local AIO traffic (#4444)
* fix(sandbox): bypass proxies for local AIO traffic

* fix(sandbox): classify public IPv6 proxy targets
2026-07-27 07:47:39 +08:00
Huixin615
090e80c1dd
fix(runtime): fail-stop runs when lease ownership cannot be confirmed (#4431)
* fix(runtime): fail-stop runs after lease expiry

* test(runtime): cover late successful lease renewal

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-27 07:25:34 +08:00
Huixin615
1cd5dea336
fix(streaming): signal replay history gaps (#4426)
* fix(streaming): signal replay history gaps

* fix(streaming): guard initial Redis replay window

* fix(frontend): align inactive gap recovery

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-27 07:13:06 +08:00
Vanzeren
1c7531242c
feat(runtime): record terminal artifact delivery receipts (slice 1 of #4272) (#4365)
* feat(runtime): record terminal artifact delivery receipts (#4272)

* fix(runtime): persist delivery receipts across recovery

* test(runtime): cover delivery receipt invariants

* fix(runtime): preserve terminal status on receipt outages
2026-07-26 21:45:47 +08:00
Ryker_Feng
7aa314b4c1
feat: add Lark CLI integration (#3971)
* feat: add lark cli integration

* fix: polish lark integration actions

* feat: support lark incremental permissions

* fix: detect lark authorization completion

* fix: harden lark integration install

* feat: expand lark auth scopes and reuse host auth in sandbox

Default lark auth to least-privilege (recommend=false, base sign-in only)
and expose the full set of lark-cli --domain business domains as native
--domain grants instead of a 4-domain read-only mapping. Resolve the
skill pack from the latest larksuite/cli GitHub release at install time
with content-hash integrity, and surface version/runtime drift in status.

Share the per-user lark-cli config/data profile between the Gateway
Settings auth flow and agent conversations by mounting the integration
dirs into the AIO sandbox and injecting the matching env for lark-cli
commands, with an allowlisted extra_mounts path in the provisioner/K8s
backend and traversal guards on integration paths.

* style: fix lint issues from ruff and prettier

Sort imports in the provisioner PVC test and re-wrap two long i18n
description strings to satisfy backend ruff and frontend prettier CI.

* fix(lark): address managed integration review feedback

* fix(frontend): stabilize integrations settings e2e

* test(sandbox): isolate remote backend legacy visibility check

* test: fix backend unit failures after merge

* Harden Lark integration review fixes

* Format Lark integration E2E test

* fix(lark): harden sandbox credential exposure and status disclosure

Address willem_bd's security review on PR #3971:

- Mount the per-user lark-cli config dir (long-lived appSecret) read-only
  into the AIO sandbox; only the refreshable-token data dir stays writable.
- Redact host filesystem paths (install_path, cli.path) from
  GET /lark/status and the config/auth complete responses for non-admin
  callers, fail-closed on any auth error.
- Document the npm postinstall trade-off (--ignore-scripts is not viable
  because @larksuite/cli fetches its platform binary in postinstall).
- Document the sandbox credential trust boundary in AGENTS.md and README,
  pointing at the sidecar-broker follow-up (#4338).

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-26 08:09:17 +08:00
Huixin615
8af760fc30
fix(runtime): make orphan reconciliation lease-aware (#4427) 2026-07-25 23:26:17 +08:00
Vanzeren
3c8b82c594
fix(runtime): serialize checkpoint writes with active runs (#4437)
* fix(runtime): serialize checkpoint writes with active runs

* fix(runtime): address checkpoint reservation reviews

* fix(runtime): address reservation race reviews

* fix(runtime): refine reservation conflict semantics
2026-07-25 23:18:34 +08:00
March-77
a65eb531ae
fix(telegram): receive inbound attachments (#4392)
* fix(telegram): receive inbound attachments

* refactor(telegram): tighten inbound attachment handoff

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-25 21:55:31 +08:00
luo jiyin
3b77a7401b
fix(sandbox): enforce E2B replica capacity limits (#4391)
* fix(sandbox): enforce E2B replica capacity limits (in-process)

Add SandboxCapacityExceededError with diagnostic fields.  Add
overflow_policy (wait/reject/burst), acquire_timeout, and burst_limit
config options.

Implement atomic capacity reservation with a four-slot model:
reserved / active / warm / transitioning.  Transitioning slots close
the window where active-to-warm or warm-to-active transitions appear
to have zero occupied slots, which would let concurrent acquires
exceed the configured replica ceiling.

Re-route release, reclaim, and evict through transitioning counters.
Add shutdown guard: reject waiters, kill VMs created during shutdown.

Add 14 tests: policy enforcement, release+acquire race, warm-reclaim
race, shutdown-waiter interaction, shutdown-during-create, and
concurrent different-thread capacity assertion.

Related: #4339

* fix: harden e2b sandbox capacity lifecycle

* fix: retain e2b capacity during uncertain eviction

* fix: serialize e2b tombstone eviction

* fix: retain capacity after uncertain e2b cleanup

* fix: track e2b remote operations during shutdown

* fix(sandbox): validate E2B capacity config

* fix(sandbox): classify capacity errors

* fix(sandbox): harden E2B capacity lifecycle

* test(sandbox): cover E2B review findings

* docs(changelog): note E2B capacity behavior

* docs(readme): explain E2B overflow handling

* docs(backend): record E2B lifecycle rules

* docs(sandbox): clarify destructive E2B reset

* fix(sandbox): close E2B capacity race gaps

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-25 10:54:14 +08:00
ShitK
0f0955bf7b
fix(client): preserve ToolMessage artifacts (#4422)
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-25 09:47:58 +08:00
Nan Gao
58befaf248
fix(thread-history): keep completed subtask cards stable after reload (#4432)
* fix(thread-history): hide subagent AI responses

* refactor(thread-runs): remove unused _is_middleware_message_row helper

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-25 09:29:11 +08:00
H Haidong
c7538cfb35
fix(runs): terminate orphaned streams after lease recovery (#4420)
* fix(runs): terminate orphaned streams after lease recovery

* fix(runs): include recovered ids in callback warnings

* fix(runs): harden orphan recovery lifecycle
2026-07-24 19:34:20 +08:00
ShitK
a4ede80deb
fix(runtime): reject unsupported run options and stream modes (#4430)
* fix(runtime): reject unsupported run options

* fix(runtime): align SDK run compatibility

* fix(frontend): avoid unsupported events stream mode

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-24 19:24:24 +08:00
Huixin615
fbc1463809
fix(gateway): preserve regenerate state in branched threads (#4358)
* fix(gateway): preserve regenerate state in branched threads

* test(gateway): isolate branch regenerate regression config

* fix(gateway): preserve branching for legacy histories

* fix(gateway): harden branch regenerate lineage

* docs(gateway): clarify branch checkpoint behavior

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-24 08:57:48 +08:00
Huixin615
4a2ecd430e
fix(streaming): expose custom events to astream_events (#4403)
* fix(streaming): expose custom events to astream_events

* test(streaming): validate real custom event emitters

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-23 22:56:12 +08:00
hataa
7857fa0cce
feat(authz): enforce tool authorization at assembly and runtime (#4370)
* feat(authz): enforce tool authorization at assembly and runtime

* fix(middleware): guard deferred tool setup lookup (#4370)

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-07-23 22:51:35 +08:00
Huixin615
d4fdc2758e
fix(frontend): clarify run duration display (#4348)
* fix(frontend): clarify run duration display

* fix(frontend): preserve run metadata across history merge

* ci: retrigger workflows
2026-07-23 14:57:16 +08:00
DanielWalnut
cb698832de
feat(frontend): add localized AI disclaimer (#4374)
* feat(frontend): add localized AI disclaimer

* fix(frontend): preserve sidecar composer alignment
2026-07-22 21:31:27 +08:00