852 Commits

Author SHA1 Message Date
Guo-Yixin
2970aa4323
fix(scripts): reject invalid Claude Keychain credential containers (#6281)
* fix(scripts): validate Claude Keychain export credentials

* fix(scripts): reuse validated Keychain access token
2026-10-05 15:39:32 +08:00
jay
fff9d185c5
fix(harness): normalize local HTTP host case in skillscan (#6280)
* fix(harness): normalize local HTTP host case in skillscan

_LOCAL_HTTP_HOSTS holds lowercase names and every membership test
compared the extracted host verbatim, so an endpoint written with any
uppercase letter (e.g. http://LOCALHOST:8080) was classified as an
external endpoint: non-root deployments failed with EACCES while root
deployments silently wrote plans outside the workspace.

Normalize the host to lowercase in _http_host and in the
network-cleartext/local scan branch so all three call sites agree.

Fixes #6278

* test(skillscan): cover local host case and external endpoint controls

* fix(skillscan): parse IPv6 hosts and detect metadata case variants

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-05 15:17:03 +08:00
Guo-Yixin
57a2d7fa70
fix(models): decode CLI credential files as UTF-8 (#6277)
* fix(models): decode CLI credential files as UTF-8

* fix(doctor): accept BOM CLI credentials consistently
2026-10-05 14:47:14 +08:00
quifox
420b39ed90
fix(acp): apply invocation timeout to initialization and session creation (#6292)
* fix(acp): apply invocation timeout to initialization and session creation

* fix(acp): preserve SDK timeout errors before the invocation deadline
2026-10-05 14:39:59 +08:00
Magno Gouveia
c0895d295b
docs(i18n): add Brazilian Portuguese README translation (#6286)
* docs(i18n): add Brazilian Portuguese README translation

* docs(i18n): disambiguate one-line agent setup heading in pt readme

* docs: keep inherited agent guidance within budget

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-05 13:38:46 +08:00
NanPan
8237dd75b3
fix(boxlite): keep private-loop shutdown retryable (#6283)
* fix(boxlite): keep private-loop shutdown retryable

* test(boxlite): cover private-loop shutdown retry

* docs(sandbox): pin BoxLite loop shutdown ownership

* fix(ci): format BoxLite shutdown changes and trim guidance

* fix(boxlite): drain acquisitions before shutdown teardown

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-05 13:38:17 +08:00
siye566
e8f1e269e5
fix(uploads): exclude punctuated numeric columns from document outlines (#6266)
* fix(uploads): exclude punctuated numeric columns from outlines

* fix(uploads): filter currency and later numeric outline columns

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-05 13:20:00 +08:00
Daoyuan Li
49dcd49f91
fix(agents): keep malformed todo statuses recoverable (#6299)
* fix(agents): keep malformed todo statuses recoverable

* docs(agents): fit todo guidance within inherited size limit

* docs(agents): clarify todo attribution guidance

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-05 12:21:39 +08:00
siye566
335c383147
fix(paths): reject Windows console device aliases (#6279) 2026-10-05 10:25:51 +08:00
quifox
cc2ccaa523
fix(channels): handle streamed run errors before dedupe cleanup (#6272)
* fix(channels): handle streamed run error frames

* fix(channels): log streamed run error details
2026-10-05 06:54:32 +08:00
quifox
e8de7584b7
fix: include completed child usage in lead budget checks (#6218)
* fix: include completed child usage in lead budget checks

* test: require completed usage middleware in lead budget regressions

* docs: fit middleware guidance within the size budget

Condense the subagent, loop, and token budget guard descriptions while retaining their limits, lifecycle rules, and completed child usage ordering.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-04 21:14:35 +08:00
Daoyuan Li
a9046508cf
fix(artifacts): bound reads before saving edited outputs (#6250) 2026-10-04 19:39:50 +08:00
Daoyuan Li
4dad94dcc0
fix(tui): guard conversation switching and invalid resume references (#6249)
* fix(tui): guard conversation switching and invalid resume references

* fix(tui): discard late stream actions after conversation switches

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-04 19:28:32 +08:00
Daoyuan Li
5c3329afad
fix(tui): preserve cursor and undo on unchanged history recalls (#6248)
* fix(tui): preserve composer on Up with empty history

* fix(tui): preserve cursor and undo on identical history recalls
2026-10-04 17:04:58 +08:00
Wenchao An
648870b590
feat(extensions): add full Custom Agent team collaboration plugin (#6243)
* feat(extensions): add full Custom Agent team collaboration plugin

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

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

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

* fix(agent-teams): align mention and input text limits
2026-10-04 16:36:48 +08:00
Grapette.L
e7e5a5e9a3
fix(gateway): read uvicorn's WEB_CONCURRENCY in the remaining worker-count readers (#6253)
* fix(gateway): read uvicorn's WEB_CONCURRENCY in the remaining worker-count readers

* fix(gateway): name the worker spelling an operator can act on

The browser refusal and the dedupe warning both took the count but printed
GATEWAY_WORKERS, so on the launches that pass uvicorn no --workers the advice
silences the gate while the extra processes keep starting. Resolve which
variable set the count and print that, and let an unparsable GATEWAY_WORKERS
fall through to WEB_CONCURRENCY instead of reporting a single worker.

Also stop the comments from claiming the readers are equivalent: they share
the two spellings and the blank-means-unset rule, but channel_connections.py
turns an unparsable value into 0 and refuses while these stay inert.

* docs: state the single-worker requirement under both spellings

The browser tool group and the inbound dedupe warning now refuse or warn on
WEB_CONCURRENCY as well, so guidance that names only GATEWAY_WORKERS=1 is
incomplete for the shipped Dockerfile and serve.sh launches, where uvicorn
takes the process count from WEB_CONCURRENCY.

* test(features): isolate the browser capability surface from the shell's worker count

browser_capability() asks for the Gateway worker count, which this change makes
readable from WEB_CONCURRENCY, so an exported count in the invoking shell turned
test_features_reports_browser_control_enabled_when_configured_and_runtime_available
into a failure that has nothing to do with the router.
2026-10-04 16:36:01 +08:00
KennySimpson
830de11c66
fix(uploads): exclude indented bold lines from outlines (#6194)
* fix(uploads): exclude indented bold lines from outlines

* refactor(uploads): name the indented-code pattern
2026-10-04 14:54:08 +08:00
subhash7476
20bd989fc1
feat(runtime): add per-LLM-call telemetry and summarization no-op event (#6236)
* feat(runtime): add P0 per-LLM-call telemetry and summarization no-op counter

Measurement only; no runtime behavior changes.

- RunJournal enriches llm.ai.response / llm.error metadata with
  langchain_run_id, caller_category, provider, model, stop_reason, status,
  token counts and rendered request size (chars). Unavailable values are None.
- Summarization middleware emits a middleware:summarize event per summarizer
  call and counts outputs identical to the prior summary_text. The call is
  never skipped.
- All telemetry fails soft.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(runtime): register summarize middleware tag, document telemetry contract

- Add MIDDLEWARE_SUMMARIZE_TAG to the middleware event catalog and the
  contract's known_tags; emit the middleware:summarize event with it.
- Document the optional per-call telemetry metadata on llm.ai.response and
  llm.error in contracts/run_event_stream_contract.json.
- Measure request size by walking string leaves instead of json.dumps on the
  event-loop thread.
- Initialise summarization call/no-op counters in __init__.
- Move RunJournal telemetry docs to the runtime guide.
- Add tests: llm.error without a start, replayed on_llm_end, and a DB-store
  metadata round trip.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

* fix(runtime): refresh token counters on usage replay

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-04 14:52:04 +08:00
Xuehao Xu
2e4191e56f
fix(frontend): preserve complete artifact editing baselines (#6036)
* fix(frontend): preserve complete artifact editing baselines

* fix(frontend): keep artifact edit exit available during reloads

* refactor(frontend): remove redundant artifact action fragments
2026-10-04 14:40:42 +08:00
sxh
130a9ab9f0
fix(skillscan): report secrets bound by unpacking and container or conditional literals (#6247)
* fix(skillscan): report secrets bound by unpacking and container literals

The AST walk behind secret-env-assignment claims "precision, never less
coverage", but it only read a single assignment target and a bare string
value, so a credential moved into `host, api_key = endpoint, "..."` or
into `api_key = ["..."]` stopped being reported even though the pre-#5648
line sweep still reported the same bytes.

Pair unpacking targets with values positionally (fixed head and tail only,
so a runtime-sized slice names nothing) and assert on each literal a
container or conditional holds, in source order.

* fix(skillscan): distinguish mapping labels from credentials

---------

Co-authored-by: sxh313 <sxh313@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-04 11:00:05 +08:00
quifox
06e9bfd269
fix: propagate current run failures from wait endpoints (#6217)
* fix: propagate current run failures from wait endpoints

* docs: clarify checkpoint wait errors and SDK behavior
2026-10-04 07:46:59 +08:00
Totoro
35023bb908
feat(scheduler): create conversation schedules and support agent self-stop (#6229)
* feat(scheduler): create conversation schedules and support agent self-stop

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

---------

Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-04 06:46:13 +08:00
creed
9eb01b371a
feat(config): select an explicit backend dotenv file (#6227)
Signed-off-by: 97three <2212371308@qq.com>
2026-10-03 23:11:48 +08:00
creed
7bb3624c06
feat(serper): add configured source-domain filters (#6228)
* feat(serper): add configured source-domain filters

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

* fix(helm): align embedded config version with example

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

---------

Signed-off-by: 97three <2212371308@qq.com>
2026-10-03 21:58:50 +08:00
Hyeonsang Cho
9bcf93ec40
fix(telegram): deny all users when allowed_users has no valid ID (#6230)
* fix(telegram): deny all users when allowed_users has no valid ID

TelegramChannel parsed channels.telegram.allowed_users with int() and
dropped every entry that failed without logging, and _check_user treated
an empty result as "no allowlist". A list of @usernames therefore opened
the bot to everyone. A single string ID was iterated character by
character ("123456" allowed users 1-6 and blocked 123456), which is what
an allowed_users: $ENV reference resolves to, and null or a bare integer
crashed the channel at startup.

The allowlist is now parsed into frozenset[int] | None: None (unset,
null, empty list, blank string) still allows everyone, a single ID is a
one-entry list, entries that are not positive numeric IDs (@usernames,
floats, booleans, comma-joined strings) are dropped with a warning, and
a configured list with no valid ID denies every user and logs an error.

* docs(changelog): reference #6230 for the telegram allowed_users fix

* fix(telegram): say "positive" in the allowed_users drop warning

0 and negative integers are numeric but still dropped, so "expected one
numeric Telegram user ID" misdescribed them. The warning now asks for a
positive numeric ID, matching _parse_telegram_user_id, and the drop test
covers 0 and -5 alongside @alice.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-03 21:37:39 +08:00
Daoyuan Li
9b16792168
fix(tui): preserve drafts when Down is not navigating history (#6219) 2026-10-03 16:43:17 +08:00
Guo-Yixin
7b8a7925c2
fix(config): reject invalid tool output budgets (#6172)
* fix(config): validate tool output budget limits

* fix(config): align Helm config schema version
2026-10-03 16:38:00 +08:00
Daoyuan Li
c84b32205d
fix(frontend): ignore literal base tags in HTML artifact previews (#6176)
* fix(frontend): ignore literal base tags in HTML artifact previews

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

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-03 16:35:55 +08:00
Canyon
3095027d79
docs(readme): sync TOC with headings and fix deploy.sh paths (#6216)
* docs(readme): sync TOC with headings and fix deploy.sh paths

* docs(readme): move skills content stranded in the RAGFlow chapter back into Skills & Tools
2026-10-03 16:29:49 +08:00
Guo-Yixin
68014de157
fix(artifacts): invalidate digests after external file replacement (#6195)
* fix(artifacts): invalidate digests after external file replacement

* docs(gateway): keep artifact guidance within the module budget

* fix(artifacts): validate oversized ranges by file identity

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-03 16:23:07 +08:00
lilei
741948ba9b
fix: invalidate converted uploads after same-size source edits (#6165)
* fix: invalidate converted uploads after same-size source edits

* fix: log converted upload ownership rejection reasons

* docs: clarify source timestamp limits and thread ownership
2026-10-03 14:51:05 +08:00
Daoyuan Li
b0c1b2e7ce
fix(artifacts): handle CRLF splits in CSV/TSV previews (#6180)
* fix(artifacts): handle truncated CRLF after quoted fields

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

* fix: address QQ channel review feedback

* docs: trim inherited agent guidance below CI limit

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

* docs: keep merged channel guidance within CI budget
2026-10-03 14:32:44 +08:00
Hyeonsang Cho
bb4f7ac5e2
fix(browser): pin browser connections to the SSRF-vetted addresses (#6201)
* fix(browser): pin browser connections to the SSRF-vetted addresses

The navigate screen and the per-request guard resolve a hostname to vet
it, but Chromium resolved the name again to open the connection. A
rebinding DNS server could answer both checks with a public address and
the connection with a private or cloud-metadata one, and the browser
followed it.

Launch each private browser through a per-session loopback SOCKS5 proxy.
Over SOCKS5 Chromium sends the hostname itself, so the proxy resolves it
exactly once through the session's egress resolver and connects only to
the addresses that resolver vetted. resolve_browser_egress applies the
same allow_private_addresses policy as validate_browser_url, on top of a
new url_safety.resolve_public_addresses helper that
validate_public_http_url now shares. Loopback is proxied explicitly with
bypass "<-loopback>". The tools, navigate_and_capture and the Live
WebSocket all pass the resolver, because a thread's first caller fixes
its launch options.

CDP-attached Chrome is unchanged. Delegated fetch services (crawl4ai,
Browserless, fastcrw) still resolve on their own side, which the Gateway
cannot pin.

* docs(changelog): reference #6201 in the browser egress pinning entry

* fix(browser): bound egress proxy faults and close, and record the WebRTC gap

Review follow-ups for the pinned browser egress proxy:

- A resolver exception other than ValueError escaped the connection
  handler as an unretrieved task error. Log it with its traceback and
  reply SOCKS5 general failure (0x01), so a resolver fault stays
  distinguishable from a policy refusal (0x02).
- Bound the resolve with its own timeout. The handshake timeout did not
  cover it, so a wedged getaddrinfo held the half-open tunnel until
  Chromium gave up. A timeout replies host unreachable.
- Bound close(): Server.wait_closed() also waits for a connection whose
  handler registered after the writer sweep, which could stall session
  close and eviction for the 15 s handshake. A handler that starts after
  close now drops its connection without opening a tunnel.
- Docs said a launched browser connects "solely" through the proxy.
  WebRTC UDP does not traverse a SOCKS proxy, so AGENTS.md, README (en/zh)
  and both changelogs now scope the guarantee to TCP and name the gap.
2026-10-03 10:05:47 +08:00
siye566
3a7e160a55
fix(paths): reject superscript Windows device names (#6148) 2026-10-03 09:04:06 +08:00
TAI-YUAN GUO
a873b78950
fix(tui): preserve multiline pasted input (carries #4630 forward) (#6168)
* fix(tui): preserve multiline pasted input

The TUI composer used a single-line `Input`, so a bracketed paste
containing code, a stack trace, or multiple paragraphs kept only the
first line when submitted. The composer is now a multiline `TextArea`:
Enter still submits, Up/Down move inside the document before falling
back to input history at the first/last line, and the slash palette is
unchanged.

Originally #4630 by @felix-windsor, cherry-picked onto current main;
conflicts in app.py key gating (transcript paging keys), test imports,
and the relocated TUI docs were resolved by hand.

Fixes #4613

* fix(tui): submit multiline input unchanged and document the widgets layer

Address the review on #4630:

- Send non-command input to the agent and history exactly as typed or
  pasted. Only the empty-input check uses strip(), so leading
  indentation and trailing newlines in pasted code and stack traces
  survive. Slash commands are still trimmed so they resolve as before.
- widgets/composer.py imports Textual, so the module-layout notes in
  tui/AGENTS.md and docs/TUI.md now exempt widgets/ alongside app.py.
- Cover pressing Down on the last document line (history fallback),
  whitespace-only input, and slash commands with surrounding whitespace.

Co-authored-by: Felix <felixwindsor3344@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(tui): treat soft-wrapped rows as composer navigation boundaries

TextArea soft-wraps long lines, and its Up/Down move between the wrapped
rows of one logical line. The history fallback only looked at logical
rows, so Up from the second visual row of a long prompt recalled history
instead of moving the cursor, and Down from its first visual row could
replace the draft with history's empty value.

Use the TextArea navigator's first/last wrapped-row checks for the
boundaries, and cover wrapped prompts in both directions. Also restore a
tui/AGENTS.md sentence about PageUp/PageDown to the app.py entry it
belongs to.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Felix <felixwindsor3344@gmail.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-03 08:29:29 +08:00
Carl Calaquian
9e3589bb96
fix(tenki): upgrade sdk and preserve timeout failures (#6162)
* fix(tenki): upgrade sdk and preserve timeout failures

* fix(tenki): tolerate noisy warm-pool health probes

* docs(tenki): compact sandbox guidance within chain budget

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-03 08:19:14 +08:00
lakers-abing
fafe904106
fix(scheduled-tasks): honor explicitly null assistant_id (#6185)
Co-authored-by: lakers-abing <236384465+lakers-abing@users.noreply.github.com>
2026-10-02 23:02:18 +08:00
Linyao Du
616c6e2e36
fix(uploads): reject Windows staging aliases before batch ingestion (#6149)
* fix(uploads): reject Windows staging aliases before batch ingestion

* fix(uploads): cover shelf names and case-insensitive staging aliases

* test(uploads): isolate legacy alias filesystem cases
2026-10-02 22:50:30 +08:00
Insecshin
759136e074
fix(config): correct Gemini official endpoint reasoning example (#6161)
* fix(config): correct Gemini official endpoint reasoning example

* docs: clarify official Gemini endpoint heading
2026-10-02 22:32:21 +08:00
creed
f4af75a7fd
feat(community): add bounded retries for Jina web fetch (#6143)
* feat(community): add bounded retries for Jina web fetch

Closes #6116

Keep retries opt-in and share a total deadline across requests and backoff. Preserve cancellation and the existing Error contract. Include offline DNS and local-wheel fixture corrections needed to run the complete backend suite in the isolated verifier.

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

* docs(community): scope Jina retry guidance to its provider

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

* fix(community): jitter bounded Jina retry backoff

* docs(community): apply provider-scoped Jina guidance

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

---------

Signed-off-by: 97three <2212371308@qq.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-02 21:35:56 +08:00
Daoyuan Li
7c52d5a187
fix(middleware): isolate temporary files for concurrent tool outputs (#6150)
* fix(middleware): isolate temporary files for concurrent tool outputs

* docs: trim tool output middleware guidance

* fix(middleware): preserve output permissions and clarify crash cleanup

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-01 20:17:13 +08:00
creed
71f109275b
feat(serper): support relative search time ranges (#6113)
* feat(serper): support relative search time ranges

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

* docs(serper): address review and trim inherited guidance

---------

Signed-off-by: 97three <2212371308@qq.com>
2026-10-01 20:12:43 +08:00
Daoyuan Li
59743568ba
fix(frontend): ignore inherited artifact language keys (#6152) 2026-10-01 19:42:06 +08:00
Guo-Yixin
d2cfe4a986
fix(models): honor Codex SSE terminal events (#6155)
* fix(models): honor Codex SSE terminal events

* fix(models): preserve malformed Codex error diagnostics

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-01 19:32:49 +08:00
Totoro
b57fa6fb9e
feat(scheduler): push scheduled task results to IM channels via a notification outbox (#6135)
* feat(persistence): add notification delivery outbox for scheduled task IM push

New notification_deliveries table and repository (migration 0012) backing
the outbox pattern for issue #4254: idempotent enqueue keyed on task_run_id
plus event plus provider plus target, atomic claim of due rows, and
exponential-backoff retry state moving pending to sending to sent or failed.
Execution status stays in scheduled_task_runs; delivery status lives here.

* feat(scheduler): enqueue and deliver scheduled-task notifications over IM channels

Closes the loop for issue #4254. On run completion the scheduler writes
outbox rows for the task owner's connected channel identities (success and
failure events; interrupts stay silent), and a new NotificationDeliveryWorker
polls due rows and pushes them through the channel's proactive send path.
Channel gains send_notification; WeCom implements it via the WS client's
send_message with errcode checking, the base class raises for providers
without proactive push.

Delivery is best-effort and never shadows execution status: enqueue and send
failures are logged and recorded in the outbox, which retries with backoff.
Channel liveness is re-checked at delivery time, and the worker stops before
the channel service on shutdown. Feature activates only when channel
connections are enabled; no new dependencies.

Verified end to end against a live WeCom bot: scheduled run completion
produced an outbox row that was delivered in under one second.

* feat(scheduler): include scheduled run result summary in IM notifications

The v1 notification only carried the run outcome skeleton (status, task id,
run id). Completed runs now include the agent's final answer so the IM push
is actually useful headlessly.

- The delivery worker accepts an optional resolve_run_summary(run_id,
  user_id) resolver; for run_completed events it enriches the rendered text
  with a "Result:" line (bounded to 1000 chars). The lookup is best-effort:
  resolver failures or missing summaries degrade to the skeleton text and
  never block or fail the delivery.
- The summary is read at delivery time from runs.last_ai_message (the
  convenience column written on run completion), not snapshotted into the
  outbox payload; retried deliveries re-read the freshest value and the
  outbox row payload stays skeleton-only. The claimed outbox row is never
  mutated in place.
- Gateway wiring scopes the read to the outbox row's owner_user_id so a
  stale row cannot pull another user's run content.
- Failed runs keep the error-only skeleton: a partial answer on a failed
  run would be misleading.

Tests: 9 new worker cases (rendering incl. truncation, sync/async
resolvers, failure/missing fallbacks, no resolver call for failed runs).
Full scheduled + outbox + channels regression green (441 passed), ruff
check + format clean.

* fix(scheduler): address review feedback on notification delivery reliability

Review follow-ups for the scheduled-task IM notification outbox:

1. Crash recovery for "sending" rows: claim now stamps updated_at and the
   worker reconciles rows stuck in "sending" past a 10-minute lease back to
   "pending" before every claim. Per-row try/except isolates a crashing
   delivery from the rest of the claimed batch.
2. Double-send race under multiple workers: claim returns exactly the rows
   its own UPDATE...RETURNING flipped (guarded by status='pending'), so a
   rival worker's concurrent claim can no longer be handed out twice.
3. Restore non-ASCII comments and drop the BOM accidentally introduced in
   test_scheduled_task_service.py.
4. Manual "run now" completions no longer enqueue IM notifications; the
   user is already watching the UI. Missing trigger metadata fails safe
   towards notifying.
5. Channel-outage failures no longer consume the retry budget: they are
   parked with count_attempt=False on a flat 15-minute backoff so an
   hours-long outage cannot exhaust retries and drop the notification.

Tests: repository 16, worker 21, scheduled-task service 31; targeted
regression across notification/scheduler/channel suites: 511 passed.

* fix(scheduler): address round-2 review on IM notification delivery

Park WeCom transport/outage failures without burning retries, bound worker stop, include task titles, align enqueue with delivery, and document the IM push behavior.

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

* docs(scheduler): complete IM notification docs and drop unused list API

Add backend/channels/migrations AGENTS notes, CHANGELOG and config.example hints for the outbox push path, and remove the unused list_by_task_run repository method flagged in review.

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

* fix(scheduler): address round-3 review on IM notification delivery

Cap channel-down parking by row age and parked attempts, redact IM egress
content, narrow WeCom transport exceptions, add lifespan wiring tests, and
run ruff format so lint-backend passes.

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

* fix(scheduler): redact entire PEM blocks in IM egress text

The previous BEGIN-only pattern left key material and the END marker in notifications.

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

* fix(persistence): align 0014 parked_attempts with bootstrap schema tests

Backfill existing rows with a temporary server default, then drop it so create_all matches Alembic, and move HEAD anchors to 0014.

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

* fix(persistence): format notification tests and shorten alembic revision id

ruff format --check failed on test_notification_delivery_worker.py.
0015_notification_delivery_parked_attempts exceeds alembic_version.version_num
(VARCHAR 32) and truncates on Postgres CI; use 0015_parked_attempts instead.

* test: fix post-merge CI failures after migration renumber

Update migration 0015 test HEAD expectation to 0017_parked_attempts and add queue_timeout_seconds to the lifespan notification worker mock scheduler config.

* fix(persistence): chain the notification outbox migrations after 0026

Main's head moved from 0016 to 0026_mcp_task_lease_tokens, so the two
outbox revisions are renumbered and re-parented:

- 0017_notification_deliveries -> 0027_notification_deliveries
- 0018_parked_attempts -> 0028_parked_attempts

Only the revision ids, parents and the migration guide change.

* fix(scheduler): adapt the notification hook and its docs to main

- tests: the completion tests assert on the atomic complete_run record;
  new cases cover a completion that was not recorded, a task deleted
  meanwhile, a failed task read, and the outbox being off.
- AGENTS.md: drop the root note and shorten the backend note to a pointer;
  the two notes pushed two effective guidance chains past the 96 KiB limit.
- CHANGELOG: move the entry from the released 2.1.0 section to Unreleased
  and add its link references.

* test(persistence): move the chain-head pin to the outbox migrations

test_migration_0026 pinned 0026 as the head, so it failed once the outbox
revisions chained after it. It now checks chain membership like the older
migration tests, and a new test file owns the head pin, checks the parents
and the 32-character revision id limit, and runs the upgrade and downgrade.

* fix(gateway): wire scheduled-task notifications after the channel service starts

The outbox wiring sat in the scheduler block and asked for the running
channel service there. #5035 moved the channel service start to after the
scheduler, and the two changes merged without a conflict, so on every
startup the lookup returned None and the feature disabled itself with the
"write-only outbox" warning. The lifespan tests patched the lookup to a
constant and kept passing.

The wiring now runs in its own step after the channel service start. The
scheduler is built as on main, and ScheduledTaskService gains
attach_notification_outbox() so the enqueue side is enabled once the
delivery worker is running. The guard, the worker and the shutdown order
are unchanged.

Tests: the lifespan helper can model the real accessor (None until the
channel service has started); a new case fails without this fix.

* fix(scheduler): enqueue from scheduler start and keep the push when the title read fails

Follow-up to the startup-order fix, from review.

- The enqueue side is wired with the scheduler again (it only needs the
  durable table), and only the delivery worker waits for the channel
  service. Attaching it later left a window after the scheduler started in
  which a finished run wrote no outbox row. If the channel service is
  missing or the worker cannot start, the enqueue side is detached, so the
  outbox still never becomes write-only.
- A failed task read after complete_run no longer drops the notification:
  the title is optional and the message falls back to the task id. A task
  deleted meanwhile still stays silent.

* fix(channels): park WeCom notifications while the SDK socket is closed

The WeCom SDK raises a plain RuntimeError when its socket is not open, and
send_notification only mapped ConnectionError, TimeoutError and OSError to
ChannelUnavailable. A disconnect therefore spent the counted retry budget
and settled the row as failed after about 15 minutes, although the docs
say transport outages park without consuming retries.

send_notification now asks the client's is_connected first. The new test
uses a real, never-connected SDK client: it raised RuntimeError after three
retries before, and parks now.

* docs(scheduler): describe which outcomes notify; mirror the changelog entry

- README (en/zh) and CONFIGURATION.md: only runs that finish as success or
  failed notify; occurrences that end without a finished run (launch error,
  queue timeout, restart recovery) do not. Parking lasts up to about a day.
- CHANGELOG: cite the issue as text like the rest of the file, and add the
  entry and link reference to CHANGELOG_zh.md.
- AGENTS.md: drop the backend pointer so this branch adds nothing to the
  guidance chains closest to their size limit; the scheduler-side rules
  and the lifespan order now live in app/channels/AGENTS.md.

* docs(changelog): cite #6135 next to #4843 for the notification outbox entry

* fix(scheduler,channels): re-check the IM binding at delivery time; park WeCom transport errors, count its rejections

Review round 1 on #6135.

The delivery worker now takes resolve_connections (the Gateway wires
ChannelConnectionRepository.list_connections) and, before the channel
liveness check and the summary read, requires the row's owner to still
hold a connected (provider, external_account_id). A disconnected target
is finalized with mark_failed(terminal=True); a lookup failure or an
unexpected result shape parks the row. Terminal outbox rows ignore
later status writes.

WeCom: the SDK raises RuntimeError from send_message for a socket that
is not open and for a dropped connection, but also for a platform
rejection ("Reply ack error: errcode=..."). Only that prefix keeps
counting; other RuntimeErrors, ConnectionError/TimeoutError/OSError and
websockets' ConnectionClosed park the row as ChannelUnavailable.

* fix(channels): treat a WeCom ACK rejection as proof the transport is up; pin the rejection prefix through the retry branch and the real connection-row shape

---------

Co-authored-by: GaoTianyuanhr <75788039+GaoTianyuanhr@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: GaoTianyuanhr <GaoTianyuanhr@users.noreply.github.com>
Co-authored-by: Totoro-qaq <279883115+Totoro-qaq@users.noreply.github.com>
2026-10-01 17:59:30 +08:00
SuperSgdk
f74a290c42
fix(uploads): reject reserved staging filenames before batch ingestion (#6122)
* fix(uploads): reject filenames reserved for temporary staging

Guard thread and shelf upload names, validate SDK batches before copying, and preserve legacy shelf downloads. Add regression coverage and document the reserved staging namespace.

* fix(uploads): report reserved names before HTTP batch ingestion

Preflight reserved staging basenames before thread storage or sandbox setup. Return HTTP 400 with a rename hint; add HTTP and strict blocking-I/O regressions and update upload documentation.

* fix(uploads): reject Windows-style staging basenames on POSIX

Normalize backslashes before the HTTP reserved-name preflight. Add POSIX basename regressions for single and mixed batches and strict blocking-I/O coverage. Clarify the preflight scope and upload documentation.

Follow-up to the review of PR #6122; AI-assisted with OpenAI Codex.

---------

Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-01 10:22:38 +08:00
PeaceMaker-best
90d466c917
feat(mcp): propagate cache resets across workers (#6126)
* feat(mcp): propagate cache resets across workers

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

* docs: keep MCP reset guidance within instruction budgets

---------

Signed-off-by: PeaceMaker-best <221849497+PeaceMaker-best@users.noreply.github.com>
Co-authored-by: PeaceMaker-best <221849497+PeaceMaker-best@users.noreply.github.com>
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-01 10:21:24 +08:00
Guo-Yixin
4d5607229e
fix(subagents): preserve direct-return tool results (#6083)
* fix(subagents): preserve direct-return tool results

* fix(subagents): include middleware tools and surface direct failures
2026-10-01 10:05:39 +08:00
Storm
69004bd52c
fix(sandbox): give fresh bash.exec runs immediate stdin EOF (#6117)
Why this change:
In Lark broker mode the shim forwards piped stdin to the broker only
after EOF. AIO's /v1/bash transport keeps a subprocess stdin pipe open
for writes, so a bare `lark-cli ...` never saw EOF and the shim blocked
forever on input that would never arrive. An earlier brace-group/eval
approach fixed the hang but changed top-level parsing (aliases/extglob)
or let a trailing backslash consume the appended delimiter.

What changed:
- _run_bash_exec prefixes the original command with `exec < /dev/null`
  on its own line, giving the fresh, per-invocation session immediate
  EOF on default stdin. Explicit pipes/heredocs/files still override
  fd0. The plain prefix keeps top-level parsing and appends no
  delimiter a trailing backslash can consume.
- The persistent /v1/shell PTY transport keeps commands unchanged; the
  shim already treats TTY stdin as no input.
- The shim drains non-TTY stdin under grace/tail idle budgets
  (DEERFLOW_LARK_BROKER_STDIN_{GRACE,TAIL}_SECONDS, default 2s, valid
  (0, 600]); an idle pipe exits 124 without contacting the broker, and
  read errors fail loudly instead of becoming empty input.
- Broker logs each exec as argc/exit/elapsed only (never argument
  values) and warns on concurrency-cap rejections.

Problem solved:
Lark commands without explicit stdin hung indefinitely in broker mode,
stalled producers could execute partial writes, and broker logs risked
capturing argument values. Docs updated: README, harness AGENTS.md,
docker/lark-cli-broker README.
2026-10-01 07:34:32 +08:00