Commit Graph

16325 Commits

Author SHA1 Message Date
Peter Steinberger 14bb0b4e52 refactor(discord): deprecate abort signal helper (#109166) 2026-07-16 09:08:00 -07:00
wangmiao0668000666 813021fa79 fix(feishu): honor abortSignal in drive comment reply retry loop (#108408)
* fix(feishu): honor abortSignal in drive comment reply retry loop

The inter-attempt delay in monitor.comment.ts used a bare setTimeout and
never received the owning monitor account's AbortSignal, so an account
teardown (ws disconnect, webhook stop, config reload) left the in-flight
comment reply retry running for up to ~6s after the rest of the lifecycle
settled. delayMs now wraps waitForAbortableDelay from async.ts and the
abortSignal is threaded through fetchDriveCommentContext →
resolveDriveCommentEventTurn → handleFeishuCommentEvent →
createFeishuDriveCommentNoticeHandler, sourced from
monitorSingleAccount.

The retry loop checks the waitMs return value and the abortSignal flag,
breaking out of the next 1s tick instead of running the full 6×1s poll
budget. Sibling surface monitor.bot-identity.ts already uses the same
pattern; #104431 explicitly skipped the comment-monitor retry because
it is a result-predicate poll, not an error retry, and this fix is
orthogonal to that decision.

Not tested: live Feishu tenant, real bot identity, real drive comment,
and account restart. requestFeishuOpenApi itself does not accept
AbortSignal — that is a separate, larger refactor and is out of scope.

* test(feishu): prove comment retry abort

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 09:05:23 -07:00
Peter Steinberger cd96603f9a fix(codex): show Atlas and ChatGPT sessions in catalog (#109142)
* fix(codex): include all interactive session sources

* chore: remove release-owned changelog entry
2026-07-16 08:55:40 -07:00
Peter Steinberger 0fa77124e9 fix(acpx): migrate codex adapter to maintained @agentclientprotocol/codex-acp (#109075)
* fix(acpx): migrate codex adapter to maintained @agentclientprotocol/codex-acp

The bundled codex ACP adapter pinned the npm-deprecated
@zed-industries/codex-acp@0.16.0, which embeds codex crates from the
rust-v0.137.0 era: GPT-5.6-era models are unknown to it and sessions fail
with 'requires a newer version of Codex'. Migrate to the maintained
@agentclientprotocol/codex-acp@1.1.4 (Node CLI, depends on @openai/codex
^0.144.4 matching the codex plugin pin):

- Wrapper generation resolves the adapter bin from its package manifest and
  delivers startup config via CODEX_CONFIG; OpenClaw-owned wrapper args are
  stripped before launch. Explicit new-adapter commands keep their own
  -c/--config args untouched; only retired Zed commands migrate, with their
  -c TOML overrides (dotted/quoted keys, attached forms, MCP config)
  faithfully converted.
- Model overrides emit the bare model id plus a separate
  model_reasoning_effort; composite model/effort ids are accepted as parse
  input but never emitted.
- Process reaper recognizes the maintained adapter tree and packaged codex
  app-server children without claiming native codex plugin processes.

Live proof: direct adapter smoke with local subscription auth initialized
1.1.4, advertised model gpt-5.6-sol with separate medium effort, and
completed a turn. Fixes #108664

* fix(acpx): pin codex-acp 1.1.2 to satisfy the lockfile release-age gate

1.1.4 is under the 48-hour minimumReleaseAge and fails lockfile
verification repo-wide. 1.1.2 (2026-07-09) passes the gate, carries the
same CODEX_CONFIG startup contract, and its ^0.144.0 codex dependency
resolves to the same 0.144.4 the codex plugin pins. Lockfile re-resolved
from main without any release-age bypass; shrinkwrap regenerated.

* chore(acpx): split oversized auth-bridge test file, register adapter as CLI-resolved dep

codex-auth-bridge.test.ts crossed the max-lines gate after the migration
regressions; the command-migration cluster moves to
codex-auth-bridge.migration.test.ts. knip's CLI-resolved dependency list
swaps the retired zed adapter for @agentclientprotocol/codex-acp (invoked
via generated wrapper/npx, never imported).
2026-07-16 08:51:30 -07:00
Peter Steinberger c46cc1e2d4 refactor(channels): unify inbound replay protection (#109117)
* refactor(channels): unify inbound replay-guard orchestration on shared factory

* fix(plugin-sdk): bind replay-guard settlement to claim handles

* fix(plugin-sdk): claim-handle settlement (rest)

* fix(discord): align skipped queue cleanup

* style(feishu): simplify bot menu claim settlement

* refactor(plugin-sdk): extract replay dedupe contracts
2026-07-16 08:46:34 -07:00
xingzhou d15e84610a fix(discord): keep voice allowlists from granting owner authority (#108813)
* fix(discord): keep voice allowlists from granting owner authority

* fix(discord): normalize wildcard voice admission

* refactor(discord): separate voice admission from ownership

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 08:44:42 -07:00
sunlit-deng 9c08675852 fix(voice-call): ignore blank provider environment values (#108816)
* fix(voice-call): ignore blank provider environment values

* refactor(voice-call): reuse shared credential resolver

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 08:39:39 -07:00
qingminlong ac3635b50c fix(qa-lab): keep child output UTF-8 safe when capped (#108156)
* fix(qa-lab): keep child output UTF-8 safe when capped

* fix(qa-lab): preserve uncapped malformed output

Co-authored-by: qingminlong <34085845+qingminglong@users.noreply.github.com>

* refactor(qa-lab): keep utf8 repair plugin-local

Co-authored-by: qingminlong <34085845+qingminglong@users.noreply.github.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 08:35:42 -07:00
Wynne668 38448750eb fix(openai): stop realtime voice reconnects promptly on close (#108209)
* fix(openai): cancel realtime reconnect backoff on close

* test(openai): cover reconnect after bridge close

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 08:27:36 -07:00
wahaha1223 aaa37a0c26 fix(googlechat): ignore blank service account env during setup (#108948) 2026-07-16 08:11:11 -07:00
wahaha1223 77f9da5461 fix(synology-chat): ignore blank token accounts (#108922) 2026-07-16 07:51:16 -07:00
Peter Steinberger 12786fe07d test: speed up timeout and pairing suites (#108863) 2026-07-16 07:47:32 -07:00
Peter Steinberger 2bd1a63075 feat: add Baseten Model API provider (#108708)
* feat: add Baseten model provider

* test: satisfy Baseten provider lint

* fix: align Baseten thinking controls

* docs: refresh Baseten documentation metadata

* fix: preserve Baseten DeepSeek replay metadata

* docs: remove release-owned Baseten changelog entry
2026-07-16 07:41:33 -07:00
Peter Steinberger 44314c9451 refactor(channels): table-drive setup and control-plane helpers (#109028)
* refactor(channels): table-drive setup credentials, config schemas, doctor detectors, status issues

* refactor(channels): control-plane tables (rest)

* fix(plugin-sdk): refresh surface budgets after rebase

* style(plugin-sdk): format credential helpers

* fix(channels): preserve generic schema refinement types

* fix(channels): preserve schema input and output inference

* fix(clickclack): forward credential patch fields

* test(plugin-sdk): use redacted credential fixtures

* fix(plugin-sdk): refresh API baseline after inference fix

* fix(channels): keep multi-account envelope optional

* fix(channels): preserve policy validation invariants

* fix(extensions): remove stale awaits from file checks

* style(qqbot): format synchronous file check

* fix(config): refresh bundled channel metadata

* fix(qqbot): reject unsupported pairing policy
2026-07-16 07:17:01 -07:00
Peter Steinberger 0340d7c0c1 fix(tts): normalize sibling provider credentials (#109051) 2026-07-16 07:08:42 -07:00
Erick Kinnee a1ed5e7b3c fix(acp): skip unsupported automatic thinking config in manager gate (#108392)
* fix(acp): skip unsupported automatic thinking config in manager gate

When an ACP backend does not advertise thinking-related config options
(e.g. OpenCode advertising only mode/model), the manager's automatic
startup-control path would reject the session with
ACP_BACKEND_UNSUPPORTED_CONTROL.

Fix:
- Add THINKING_CONFIG_KEYS set and isThinkingConfigKey helper to
  manager.runtime-controls.ts, following the existing OPTIONAL_TIMEOUT_CONFIG_KEYS pattern
- Skip thinking keys in the advertised-keys validation gate (auto-derived
  options are best-effort)
- Skip unsupported-control rejection for thinking keys at the
  setConfigOption catch level
- Extract isUnsupportedControlRejection helper to avoid duplicating the
  error code check
- Forward getCapabilities input handle in ACPX runtime so session-scoped
  capability queries work
- Inject thinking/thought_level aliases for Codex ACP sessions when
  reasoning_effort is advertised

Closes #103802

* fix(acp): honor thinking control capabilities

---------

Co-authored-by: Erick Kinnee <erick@ekinnee.dev>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 07:03:15 -07:00
LavyaT a24cf632c7 fix(anthropic): align exact Claude 1M model limits (#108751)
* fix(anthropic): declare GA 1M context window for opus-4-7/sonnet-4-6/opus-4-6 (issue #108152)

* test(anthropic): cover corrected context resolution

* fix(anthropic): align exact Claude model limits

* fix(anthropic): scope Claude CLI output limits

* fix(anthropic): preserve plan-safe Claude CLI context

* test(anthropic): align status context expectations

* test(anthropic): align gateway context expectations

* test(anthropic): align lookup context expectations

* test(anthropic): align CLI status context

* test(anthropic): align CLI reseed context cap

---------

Co-authored-by: Lavya Tandel <lavya@loom.local>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 06:48:56 -07:00
Peter Steinberger a50dd6276b refactor(test): complete channel activity runtime mock (#109022) 2026-07-16 06:45:28 -07:00
lsr911 d2e9fa22bc fix(gradium): treat blank env API key as unconfigured in synthesis paths (#108800)
* fix(gradium): treat blank env API key as unconfigured in synthesis paths

The isGradiumProviderConfigured helper already uses trimToUndefined on
GRADIUM_API_KEY, but synthesize and synthesizeTelephony still used the
bare env var. This made whitespace-only credentials pass the
missing-key guard and send unusable bearer tokens.

- Wrap process.env.GRADIUM_API_KEY with trimToUndefined in synthesize
- Wrap process.env.GRADIUM_API_KEY with trimToUndefined in
  synthesizeTelephony
- Add test for whitespace-only env key rejection in synthesize

This completes the blank-credential guard for the Gradium provider,
matching the pattern from OpenAI (#108212), Xiaomi (#108558),
Deepgram (#108565), and Inworld (#108783).

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

* fix(gradium): centralize speech API key resolution

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 06:29:47 -07:00
Peter Steinberger e3f343bd53 fix(codex): preserve Unicode in stderr tails (#109013) 2026-07-16 06:04:03 -07:00
lsr911 7a81f29980 fix(inworld): treat blank env API key as unconfigured in speech provider (#108783)
* fix(inworld): treat blank env API key as unconfigured in speech provider

A whitespace-only INWORLD_API_KEY made the Inworld speech provider
report configured, then sent an unusable bearer credential from
synthesis, telephony synthesis, and voice listing.

- Wrap process.env.INWORLD_API_KEY with trimToUndefined in isConfigured
  so blank values are treated as not configured
- Wrap process.env.INWORLD_API_KEY with trimToUndefined in synthesize,
  synthesizeTelephony, and listVoices so blank values are rejected
  locally before any network request
- Add test coverage for whitespace-only env key in isConfigured and
  synthesize paths

This matches the blank-credential guard pattern applied in the OpenAI
(#108212), Xiaomi (#108558), and Deepgram (#108565) providers.

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

* fix(inworld): centralize speech API key resolution

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 05:55:55 -07:00
Alix-007 ee70f20fed fix(qa): stop Docker startup hangs on stalled health checks (#108992)
* fix(qa): bound Docker health probe deadlines

* fix(qa): race Matrix health candidates

Co-authored-by: Alix-007 <li.long15@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 05:51:38 -07:00
Andrey Gruzdev 878f55c4c2 fix: text-only Mattermost replies fail with blank attachment fields (#108281)
* fix(mattermost): ignore blank attachment payload fields

* test(mattermost): prove blank placeholder send path

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 05:46:48 -07:00
mushuiyu886 576a222da2 fix(codex): preserve split UTF-8 in app-server stderr (#108335) 2026-07-16 05:41:56 -07:00
Yehonal 175181f42f fix(discord): suppress typing when hooks silently handle a turn (#108449)
* fix(discord): defer typing until reply dispatch is accepted

* fix(discord): preserve typing for accepted fast replies

* refactor(discord): create typing feedback after admission

* refactor(discord): keep queue key resolver internal

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 05:30:42 -07:00
sunlit-deng f643fc3c97 fix(minimax): ignore blank environment API key (#108777)
* fix(minimax): ignore blank environment API key

* fix(minimax): centralize TTS API key resolution

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 05:26:33 -07:00
Peter Steinberger 16d1f487fe feat(widgets): unify show_widget across surfaces (#108983)
* feat(widgets): unify show_widget across surfaces

* docs(canvas): keep lazy show_widget descriptor in sync with the loaded tool
2026-07-16 05:24:11 -07:00
lsr911 df7fcef4b5 fix(xiaomi): treat blank env API key as unconfigured in speech provider (#108558)
* fix(xiaomi): treat blank env API key as unconfigured in speech provider

A whitespace-only XIAOMI_API_KEY made the Xiaomi MiMo speech provider
report configured, then sent an unusable bearer credential from synthesis.

- Wrap process.env.XIAOMI_API_KEY with trimToUndefined in isConfigured
  so blank values are treated as not configured
- Wrap process.env.XIAOMI_API_KEY with trimToUndefined in synthesize
  so blank values are rejected locally before any network request
- Add test coverage for whitespace-only and blank env key in both paths

This matches the blank-credential guard applied in the OpenAI speech
provider (PR #108212).

* refactor(xiaomi): centralize speech key resolution

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 05:05:03 -07:00
qingminlong 552fd224cf fix(opencode): reject output stream failures in session catalog (#108218)
* fix(opencode): reject output stream failures in session catalog

* test(opencode): format stream failure regression

* test(opencode): prove output pipe failure cleanup

Co-authored-by: qingminlong <qing.minlong@xydigit.com>

* test(opencode): satisfy pipe proof lint

Co-authored-by: qingminlong <qing.minlong@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 04:57:28 -07:00
Peter Steinberger e1fda44ecc fix(onepassword): preserve oversized token diagnostics (#108964) 2026-07-16 04:52:36 -07:00
Peter Steinberger 2d9584ccda feat(canvas): interactive widgets can send follow-up prompts in web chat (#108889)
* feat(canvas): let widget buttons send prompts into web chat

Inline show_widget documents get a sendPrompt(text) bridge: a private
MessageChannel offered to the Control UI before widget code runs. The chat
adopts only the first offer per frame, requires transient user activation,
a visible focused frame, plain text (no slash commands), and rate limits
10 prompts/minute per widget. Accepted prompts run the normal user send
path of the owning chat pane.

* fix(canvas): appease lint/knip/docs gates for widget prompts

Split widget prompt tests into their own file, drive validation and rate
limits through the real port path instead of exported internals, use
addEventListener on the adopted port, and regenerate docs_map.

* fix(canvas): install widget listeners per window for non-isolated tests

Module-boolean listener flags broke in shared vitest workers where each test
file gets a fresh jsdom window; key installation by window instead and give
port flushing more headroom.
2026-07-16 04:52:26 -07:00
Peter Steinberger 48866bbc40 fix(memory): preserve linked MEMORY.md during promotion (#108921)
* fix(memory): preserve promotion symlink targets

Follow-up to #108397.

* fix(memory): retain writable-target compatibility

* fix(memory): prove atomic rename stage
2026-07-16 04:46:29 -07:00
Peter Steinberger 3f25f4b5fc fix(doctor): stop duplicating state schema migration labels (#108947)
* fix(doctor): distinguish state schema migrations

* fix(doctor): enforce migration formatter exhaustiveness

* docs(changelog): note state schema reporting fix

* docs(changelog): keep release note in PR body
2026-07-16 04:37:36 -07:00
Peter Steinberger 990e8d24b0 feat(presentation): hosted-widget launch via typed web-app actions (#108927)
* feat(presentation): support hosted widget actions

* refactor(discord): route widget launches through presentation

* chore: keep changelog release-owned

* test(interactive): type the invalid web-app boundary case explicitly

* test: type web-app action fixtures against the split union

The two-member web-app union no longer accepts a distributed
type: "url" | "web-app" literal or an empty receipt in typed
callbacks; use explicit action casts and the Discord receipt factory.
2026-07-16 04:35:31 -07:00
sunlit-deng c4e696d1ba fix(vydra): ignore blank environment API key (#108757)
* fix(vydra): ignore blank environment API key

* refactor(tts): centralize speech API key resolution

* chore(plugin-sdk): refresh speech API baseline

* chore(plugin-sdk): pin speech resolver surface

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 04:33:25 -07:00
wahaha1223 549322a5f2 fix(nextcloud-talk): bound API password file reads (#108321) 2026-07-16 04:33:05 -07:00
Peter Steinberger f7a665aaa1 refactor(clickclack): deduplicate gateway event fixtures (#108942) 2026-07-16 04:28:10 -07:00
Wynne668 f1f4b8ae8d fix: keep image attachments when steering active runs (#107657)
* fix: preserve images during active-run steering

* fix(codex): preserve steering fallback order

Co-authored-by: ZengWen-DT <ceng.wen@xydigit.com>

* test(codex): satisfy strict steering assertions

Co-authored-by: ZengWen-DT <ceng.wen@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 04:23:03 -07:00
zhanxingxin1998 5810de21b9 fix(clickclack): prevent reconnect from skipping in-flight events (#108577)
* fix(clickclack): commit websocket cursors after processing

Serialize realtime events and wait for queued handlers before reconnecting so only successfully processed cursors advance.

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

* fix(clickclack): replay failed websocket events

Keep the last successful cursor when realtime handling fails, then reconnect through durable backlog recovery before reopening the stream.

Co-authored-by: 詹幸心0668001037 <zhan.xingxin@xydigit.com>

---------

Co-authored-by: OpenAI Codex <noreply@openai.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 04:12:54 -07:00
Peter Steinberger d5eebf02e2 fix(workboard): record resolved runtime metadata instead of hardcoded codex engine (#108887)
* fix(workboard): record resolved runtime metadata instead of hardcoded codex engine

Workboard executions labeled every dispatched run engine=codex, model=default,
and id suffix :codex even for Claude/other harness agents. The gateway agent
admission phase now returns the resolved {harness, provider, model} for plugin
subagent runs; the dispatcher records it verbatim and omits engine/model when
unresolved. Engine becomes an open runtime identifier in the workboard
contract (built-in launch choices stay a closed list), store/UI normalizers
preserve historical labels as written instead of inventing codex, and new
execution ids use an :agent-session suffix. Fixes #108362

* fix(workboard): accept undefined engine in ui engineModel helper
2026-07-16 04:10:27 -07:00
Alix-007 93c1543efc fix(qqbot): bound channel API response reads (#108895)
* fix(qqbot): bound channel API response reads

* refactor(qqbot): centralize channel API cleanup

Co-authored-by: Alix-007 <li.long15@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 04:04:24 -07:00
Peter Steinberger 66604df9b6 refactor(channels): share retry helpers and remove dead state probes (#108903)
* refactor(channels): generalize channel retry runner and retry-after parsing; drop dead configured-state probes

* fix(channels): align retry and metadata contracts
2026-07-16 04:03:15 -07:00
Wynne668 7241b2bc80 fix(vault): reject oversized credential files (#108761)
* fix(vault): bound token and JWT file reads

* fix(vault): remove awaits from synchronous credential reads
2026-07-16 04:02:44 -07:00
Peter Steinberger 3550d3140e fix(line): validate generic batch media URLs (#108917) 2026-07-16 03:59:26 -07:00
xingzhou 6e594ca7ca fix(qa-lab): preserve Unicode in capped evidence previews (#108545)
* fix(qa-lab): preserve UTF-8 evidence previews

* fix(qa-lab): complete short evidence preview reads

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 03:50:30 -07:00
Ian Alloway dbcf7abda1 fix(anthropic-vertex): keep ADC fetch provider-local (#108350)
* fix(anthropic-vertex): shim native fetch so Google auth avoids gaxios's node-fetch import

@anthropic-ai/vertex-sdk's bundled gaxios only uses native fetch when a
global `window.fetch` exists; otherwise it dynamically imports
`node-fetch`, which can fail to resolve depending on how the plugin's
dependencies are installed. That failure surfaces deep inside gaxios's
token-exchange path as "Cannot convert undefined or null to object",
breaking every Vertex auth request (#107341). The same gaxios root
cause hit a different provider in #41380, where a global window.fetch
shim was the confirmed workaround.

Fixes #107341

* fix(anthropic-vertex): compare window directly against undefined

oxlint's unicorn/no-typeof-undefined flagged the typeof check; a
direct comparison is safe here since target.window is a property
access, not a possibly-undeclared identifier.

* fix(anthropic-vertex): keep ADC fetch provider-local

Co-authored-by: Ian Alloway <ian@allowayllc.com>

* test(anthropic-vertex): inject auth in API fixtures

Co-authored-by: Ian Alloway <ian@allowayllc.com>

* refactor(anthropic-vertex): clarify auth transport contract

Co-authored-by: Ian Alloway <ian@allowayllc.com>

* fix(anthropic-vertex): preserve ADC proxy routing

* chore: leave changelog to release workflow

---------

Co-authored-by: Ian Alloway <ian@allowayllc.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 03:47:40 -07:00
Yuval Dinodia 0317d7e628 fix(memory-core): write MEMORY.md atomically during short-term promotion (#108397)
* fix(memory-core): write MEMORY.md atomically during short-term promotion

applyShortTermPromotions rewrote MEMORY.md with a single non-atomic
fs.writeFile, which truncates the file before streaming the new content.
An OS write failure part way through (for example EFBIG on a size-limited
or full volume) left MEMORY.md truncated to the bytes written before the
failure, permanently dropping user long-term memory. The dreaming cron
path invokes this writer automatically, and the recall store is only
updated after the write, so the promotion stays eligible and the next
run reads the already-truncated file.

Route the write through replaceFileAtomic (temp file, fsync, atomic
rename), the same durable-write helper the sibling DREAMS.md writer in
this extension already uses. On failure the temp file is discarded and
the existing MEMORY.md is left untouched; on success the content and the
existing file mode are preserved.

* fix(memory-core): harden atomic promotion durability

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 03:43:21 -07:00
Alix-007 60987aa96d fix(qa-channel): bound stalled bus requests (#108684)
* fix(qa-channel): bound stalled bus requests

* test(qa-channel): cover stalled bus response bodies

* test(qa-channel): make stalled-body timeout deterministic

* style(qa-channel): format timeout regression

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 03:41:51 -07:00
Eden d572c5cb2d fix(line): run post-ack webhook processing on its own admitted work root (#107734)
* fix(line): run post-ack webhook processing on its own admitted work root

LINE acks the webhook and dispatches event processing fire-and-forget on
the same async chain. The HTTP request admission that chain inherited is
released as soon as the route handler returns, and a released admission
refuses subordinate queue work - so every LINE inbound agent turn fails
with "GatewayDrainingError: Gateway is draining; new tasks are not
accepted" even though the gateway is healthy. DMs, group mentions, and
postbacks are all affected; the user-visible symptom is the bot replying
"Sorry, I encountered an error processing your message." to everything.

Add runDetachedWebhookWork to the plugin-sdk webhook-request-guards
surface (a thin wrapper over the gateway independent-root continuation,
the same shape core uses in gateway/server/hooks.ts) and route all three
LINE ack-first dispatch sites through it: the gateway monitor handler
(the live path), and the createLineNodeWebhookHandler / Express
middleware handlers (public webhook building blocks an embedder can
register under the gateway). #65375 unified these three into one ack-first
pattern; keeping the detach consistent avoids re-introducing the same
latent defect in the two that are not on the live gateway path today.

The continuation is reserved synchronously while the request is still
admitted, so the detached processing stays accepted and a real restart
drain can wait for it instead of stranding it mid-turn.

Tests pin every layer: the guards suite proves detached post-ack work is
admitted after the request admission is released (and that the inherited
chain without the helper is refused); the monitor lifecycle suite and the
webhook-node suite assert each dispatch site goes through the detached
root. Red/green verified: reverting any dispatch fails its test.

* fix(plugin-sdk): account for runDetachedWebhookWork in public surface budget

* fix(channels): track detached webhook processing

Co-authored-by: 許元豪 <146086744+edenfunf@users.noreply.github.com>

* docs: refresh generated docs map

* chore(plugin-sdk): refresh API baseline

* fix(webhooks): preserve post-ack ordering

* test(plugin-sdk): satisfy detached work lint

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 03:39:02 -07:00
Eden 80de877ed3 fix(line): support /allowlist config edits (#106638)
* feat(line): wire base.allowlist config-edit adapter

LINE was the only DM/group channel without a base.allowlist adapter, so
`openclaw allow line` / the /allowlist command replied "does not support"
instead of editing the LINE allowlist. Reuse buildDmGroupAccountAllowlistAdapter
(DM + group + per-group-override scopes), matching Telegram/Signal; entry
normalization flows through the existing lineConfigAdapter.formatAllowFrom.

* fix(allowlist): preserve inherited account entries

* fix(allowlist): preserve all-scope store edits

Co-authored-by: Eden <146086744+edenfunf@users.noreply.github.com>

* test(allowlist): import config type

Co-authored-by: Eden <146086744+edenfunf@users.noreply.github.com>

* fix(allowlist): preserve empty effective overrides

Co-authored-by: Eden <146086744+edenfunf@users.noreply.github.com>

* fix(allowlist): reject group-only store edits

Co-authored-by: Eden <146086744+edenfunf@users.noreply.github.com>

* fix(allowlist): preserve cleared channel overrides

Co-authored-by: Eden <146086744+edenfunf@users.noreply.github.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 03:38:13 -07:00