11554 Commits

Author SHA1 Message Date
Nora
a298fa22f6 fix(slack-stream): non-null assertion for lastStreamPayload spread
tsc does not narrow mutable let variables through spread type expressions
even when an explicit !== null guard precedes the spread. The assertion is
safe — lastStreamPayload !== null is checked in the same condition.
2026-03-12 03:56:28 -04:00
Nora
7b9fc01fba fix(slack-stream): use !== null narrowing for lastStreamPayload spread
tsgo (native TypeScript compiler) does not narrow a let variable through a
truthy check in a compound && condition when it appears in a spread
expression. Replace the intermediate const + truthy check with an explicit
!== null guard which tsgo reliably narrows.
2026-03-12 03:56:28 -04:00
Nora
d1653b7750 style(slack-stream): apply oxfmt formatting to dispatch.ts
One log line exceeded the print width and one deliverNormally call was
split across lines where oxfmt prefers a single line.
2026-03-12 03:56:28 -04:00
Nora
d8e1aff7ee fix(slack-stream): always deliver fallback on streaming failure regardless of orphan deletion
When a streaming append/start call fails and chat.delete also fails, the
stream message is left in 'streaming' state — never finalized via
chat.stopStream, which may render as invisible or broken on mobile Slack.
streamSession.stopped is already set to true so the end-of-dispatch
finalizer also skips the stream, leaving the payload with no recovery path.

Remove the orphanDeleted guard from the deliverWithStreaming catch block:
always call deliverNormally here even if deletion failed, to ensure the user
receives the complete answer. A cosmetic duplicate on desktop clients is
preferable to a silently truncated answer.

The guard is intentionally kept in the finalizer catch: there the stream
message has already been fully finalized (all content visible), so skipping
deliverNormally on deletion failure avoids a true content duplicate.
2026-03-12 03:56:28 -04:00
Nora
c9fbb445a7 fix(slack-stream): bind lastStreamPayload to local const before spread
TypeScript cannot narrow a mutable let variable through a null-check guard
when it is used in a spread expression (TS2698: Spread types may only be
created from object types). Binding to a local const lets the compiler
narrow the type correctly.
2026-03-12 03:56:28 -04:00
Nora
0eb89b16e1 fix(slack-stream): guard fallback delivery behind orphan-deletion success
If chat.delete throws after a stream failure, deliverNormally was called
unconditionally — leaving both the unfinalizable stream message and the
fallback reply visible, recreating the exact duplicate this PR prevents.

Fix: introduce orphanDeleted flag in both failure paths (deliverWithStreaming
catch and the finalizer catch). deliverNormally is now only called when:
  - there was no orphaned stream message to begin with (streamMessageTs
    undefined = stream never flushed to Slack), OR
  - chat.delete succeeded

If deletion fails, the stream message is still visible with its full content,
so skipping the fallback is the correct behaviour — the user sees the content
without a duplicate.
2026-03-12 03:56:28 -04:00
Nora
85018c4b56 fix(slack-stream): re-deliver full accumulated text on mid-stream failure
When appendSlackStream throws for a later payload, the fallback was calling
deliverNormally(payload, ...) with only the current chunk — dropping all text
from earlier payloads that was already live in the stream message.

dispatchReplyFromConfig can emit multiple final payloads per turn (it
iterates the replies array), so a mid-stream Slack API error could silently
truncate the visible answer.

Fix: accumulate all successfully-streamed text in streamedText (updated after
each successful startSlackStream / appendSlackStream). On failure, re-deliver
{ ...payload, text: streamedText + current chunk } so the user always gets
the complete content. The finalizer fallback (stopSlackStream failure) also
uses streamedText for the same reason.
2026-03-12 03:56:28 -04:00
Nora
8bd3281652 fix(slack-stream): clean up orphaned messages + track streamMessageTs on session
Prevents ghost/duplicate messages on mobile Slack when streaming fails.

## Problem

When a streaming API call fails mid-stream, the partially-created stream
message (sent to Slack via chat.startStream) would persist alongside the
fallback normal reply, causing a duplicate on mobile clients.

Two issues also existed in the original cleanup approach:
1. streamTs is declared private on ChatStreamer in @slack/web-api — accessing
   it directly fails TypeScript strict-mode / pnpm check at compile time.
2. When stopSlackStream fails in the finalizer, the orphaned message was
   deleted but no fallback reply was sent — user gets silence.

## Fix

### src/slack/streaming.ts
- Add streamMessageTs?: string to SlackStreamSession. Populated lazily from
  the first non-null response returned by streamer.append() — which is the
  ChatStartStreamResponse carrying the stream message ts. Never undefined if
  a message was actually sent to Slack; undefined means nothing to clean up.
- Capture ts in startSlackStream (from the initial append response).
- Also backfill in appendSlackStream in case the first append was buffered
  (text < SDK buffer_size of 256 chars → returns null).

### src/slack/monitor/message-handler/dispatch.ts
- On streaming failure: mark stream stopped, delete orphaned message via
  streamMessageTs (not private streamer.streamTs), then fall back to normal
  delivery.
- On finalizer stopSlackStream failure: delete orphaned message + call
  deliverNormally(lastStreamPayload) so the user gets a response.
- Track lastStreamPayload in outer scope across deliverWithStreaming calls.
2026-03-12 03:56:28 -04:00
Vincent Koc
9aeaa19e9e
Agents: clear invalidated Kimi tool arg repair (#43824) 2026-03-12 03:53:06 -04:00
Val Alexander
c5ea6134d0
feat(ui): add chat infrastructure modules (slice 1/3 of dashboard-v2) (#41497)
* feat(ui): add chat infrastructure modules (slice 1 of dashboard-v2)

New self-contained chat modules extracted from dashboard-v2-structure:

- chat/slash-commands.ts: slash command definitions and completions
- chat/slash-command-executor.ts: execute slash commands via gateway RPC
- chat/slash-command-executor.node.test.ts: test coverage
- chat/speech.ts: speech-to-text (STT) support
- chat/input-history.ts: per-session input history navigation
- chat/pinned-messages.ts: pinned message management
- chat/deleted-messages.ts: deleted message tracking
- chat/export.ts: shared exportChatMarkdown helper
- chat-export.ts: re-export shim for backwards compat

Gateway fix:
- Restore usage/cost stripping in chat.history sanitization
- Add test coverage for sanitization behavior

These modules are additive and tree-shaken — no existing code
imports them yet. They will be wired in subsequent slices.

* Update ui/src/ui/chat/export.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* fix(ui): address review feedback on chat infra slice

- export.ts: handle array content blocks (Claude API format) instead
  of silently exporting empty strings
- slash-command-executor.ts: restrict /kill all to current session's
  subagent subtree instead of all sessions globally
- slash-command-executor.ts: only count truly aborted runs (check
  aborted !== false) in /kill summary

* fix: scope /kill <id> to current session subtree and preserve usage.cost in chat.history

- Restrict /kill <id> matching to only subagents belonging to the current
  session's agent subtree (P1 review feedback)
- Preserve nested usage.cost in chat.history sanitization so cost badges
  remain available (P2 review feedback)

* fix(ui): tighten slash kill scoping

* fix(ui): support legacy slash kill scopes

* fix(ci): repair pr branch checks

* Gateway: harden chat abort and export

* UI: align slash commands with session tree scope

* UI: resolve session aliases for slash command lookups

* Update .gitignore

* Cron: use shared nested lane resolver

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-12 03:48:58 -04:00
Ayaan Zaidi
ed0ec57a7b
fix: scope telegram polling restart to telegram errors (#43799)
* fix: scope telegram polling restart to telegram errors

* fix: make telegram error tagging best-effort

* fix: scope telegram polling restart to telegram errors (#43799)
2026-03-12 13:14:17 +05:30
Vincent Koc
82e3ac21ee
Infra: tighten exec allowlist glob matching (#43798)
* Infra: tighten exec allowlist glob matching

* Changelog: note GHSA-f8r2 exec allowlist fix
2026-03-12 03:33:50 -04:00
Vincent Koc
d8ee97c466
Agents: recover malformed Anthropic-compatible tool call args (#42835)
* Agents: recover malformed anthropic tool call args

* Agents: add malformed tool call regression test

* Changelog: note Kimi tool call arg recovery

* Agents: repair toolcall end message snapshots

* Agents: narrow Kimi tool call arg repair
2026-03-12 03:28:22 -04:00
Josh Avant
0bcb95e8fa
Models: enforce source-managed SecretRef markers in models.json (#43759)
Merged via squash.

Prepared head SHA: 4a065ef5d849273756ceb0dd241ca24ca9e621ca
Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
Reviewed-by: @joshavant
2026-03-12 02:22:52 -05:00
Mathias Nagler
e8a162d3d8
fix(mattermost): prevent duplicate messages when block streaming + threading are active (#41362)
* fix(mattermost): prevent duplicate messages when block streaming + threading are active

Remove replyToId from createBlockReplyPayloadKey so identical content is
deduplicated regardless of threading target. Add explicit threading dock
to the Mattermost plugin with resolveReplyToMode reading from config
(default "all"), and add replyToMode to the Mattermost config schema.

Fixes #41219

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

* fix(mattermost): address PR review — per-account replyToMode and test clarity

Read replyToMode from the merged per-account config via
resolveMattermostAccount so account-level overrides are honored in
multi-account setups. Add replyToMode to MattermostAccountConfig type.
Rename misleading test to clarify it exercises shouldDropFinalPayloads
short-circuit, not payload key dedup.

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

* Replies: keep block-pipeline reply targets distinct

* Tests: cover block reply target-aware dedupe

* Update CHANGELOG.md

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-12 03:15:17 -04:00
wangchunyue(王春跃)
6c196c913f
fix(cron): prevent duplicate proactive delivery on transient retry (#40646)
* fix(cron): prevent duplicate proactive delivery on transient retry

* refactor: scope skipQueue to retryTransient path only

Non-retrying direct delivery (structured content / thread) keeps the
write-ahead queue so recoverPendingDeliveries can replay after a crash.

Addresses review feedback from codex-connector.

* fix: preserve write-ahead queue on initial delivery attempt

The first call through retryTransientDirectCronDelivery now keeps the
write-ahead queue entry so recoverPendingDeliveries can replay after a
crash.  Only subsequent retry attempts set skipQueue to prevent
duplicate sends.

Addresses second codex-connector review on ea5ae5c.

* ci: retrigger checks

* Cron: bypass write-ahead queue for direct isolated delivery

* Tests: assert isolated cron skipQueue invariants

* Changelog: add cron duplicate-delivery fix entry

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-12 03:01:19 -04:00
lisitan
f3c00fce15
fix: prevent duplicate assistant messages in TUI (fixes #35278) (#35364)
* fix: prevent duplicate assistant messages in TUI (fixes #35278)

When startAssistant() is called multiple times with the same runId,
it was creating duplicate AssistantMessageComponent instances instead
of reusing the existing one. This caused messages to appear twice in
the terminal UI.

The fix checks if a component already exists for the runId before
creating a new one. If it exists, we update its text instead of
appending a duplicate component.

Test coverage includes verification that:
- Only one component is created when startAssistant is called twice
- The second text replaces the first
- Component count remains 1 (prevents regression)

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>

* Changelog: add TUI duplicate-render fix entry

---------

Co-authored-by: 沐沐 <mumu@example.com>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Happy <yesreply@happy.engineering>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-03-12 02:59:42 -04:00
Vincent Koc
99ec687d7a
fix(agents): enforce sandboxed session_status visibility (#43754)
* agents: guard sandboxed session_status access

* test(agents): cover sandboxed session_status scope

* docs(changelog): credit session_status hardening

* agents: preflight sandboxed session_status checks

* test(agents): cover session_status existence oracle

* agents: preserve legacy session_status tree keys

* test(agents): cover legacy session_status tree keys

* Update CHANGELOG.md
2026-03-12 02:54:25 -04:00
Vincent Koc
12dc299cde
fix(imessage): dedupe reflected self-chat duplicates (#38440)
* iMessage: drop reflected self-chat duplicates

* Changelog: add iMessage self-chat echo dedupe entry

* iMessage: keep self-chat dedupe scoped to final group identity

* iMessage: harden self-chat cache

* iMessage: sanitize self-chat duplicate logs

* iMessage: scope group self-chat dedupe by sender

* iMessage: move self-chat cache identity into cache

* iMessage: hash full self-chat text

* Update CHANGELOG.md
2026-03-12 02:27:35 -04:00
Vincent Koc
d8d8dc7421 Infra: fail closed without device scope baseline 2026-03-12 01:42:12 -04:00
Vincent Koc
276ee259ca Tests: clean up temp git helper directory 2026-03-12 01:42:12 -04:00
Vincent Koc
4f462facda
Infra: cap device tokens to approved scopes (#43686)
* Infra: cap device tokens to approved scopes

* Changelog: note device token hardening
2026-03-12 01:25:52 -04:00
Vincent Koc
2504cb6a1e
Security: escape invisible exec approval format chars (#43687)
* Infra: escape invisible exec approval chars

* Gateway: sanitize exec approval display text

* Tests: cover sanitized exec approval payloads

* Tests: cover sanitized exec approval forwarding

* Changelog: note exec approval prompt hardening
2026-03-12 01:20:04 -04:00
Vincent Koc
1dcef7b644
Infra: block GIT_EXEC_PATH in host env sanitizer (#43685)
* Infra: block GIT_EXEC_PATH in host env sanitizer

* Changelog: note host env hardening
2026-03-12 01:16:03 -04:00
Vincent Koc
18f15850e6
fix(browser): restore proxy attachment media size cap (#43684)
* browser: honor shared proxy file size cap

* test(browser): cover proxy file size cap

* docs(changelog): note browser proxy size cap fix
2026-03-12 01:04:31 -04:00
Ayaan Zaidi
fbc1bd6f8e fix: clear telegram polling cleanup timers 2026-03-12 09:36:04 +05:30
Huang X
70abee69e9 fix(telegram): avoid polling restart hang after stall detection 2026-03-12 09:36:04 +05:30
Toven
ade748176f
OpenRouter: surface free Hunter and Healer stealth models for the next week (#43642)
* Models: add temporary Hunter and Healer alpha to OpenRouter catalog

* Add temporary OpenRouter stealth catalog entries

---------

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-03-11 22:58:48 -05:00
David Rudduck
f01c41b27a
fix(context-engine): guard compact() throw + fire hooks for ownsCompaction engines (#41361)
Merged via squash.

Prepared head SHA: 0957b32dc63b16d710403565953b77bfbd2bd987
Co-authored-by: davidrudduck <47308254+davidrudduck@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
2026-03-11 20:19:20 -07:00
Frank Yang
5231277163
fix(acp): rehydrate restarted main ACP sessions (#43285)
Merged via squash.

Prepared head SHA: f06318e58fe3e3fedd70426ca7eeecf6d71bb604
Co-authored-by: frankekn <4488090+frankekn@users.noreply.github.com>
Co-authored-by: frankekn <4488090+frankekn@users.noreply.github.com>
Reviewed-by: @frankekn
2026-03-12 11:05:09 +08:00
Peter Steinberger
5ca780fa78
feat: expose runtime version in gateway status 2026-03-12 02:55:31 +00:00
Robin Waslander
e95f2dcd6e
fix(sandbox): anchor fs-bridge writeFile commit to canonical parent path
Refs: GHSA-xvx8-77m6-gwg6
2026-03-12 03:52:24 +01:00
Peter Steinberger
43a10677ed
fix: isolate plugin discovery env from global state 2026-03-12 02:46:29 +00:00
Peter Steinberger
17fd46ab66
test: fix websocket tool shape coverage 2026-03-12 02:16:56 +00:00
Robin Waslander
487a3ba8ce
fix(discord): enforce users/roles allowlist in reaction ingress
References GHSA-9vvh-2768-c8vp.
2026-03-12 03:13:46 +01:00
Peter Steinberger
980619b9be
fix: harden openai websocket replay 2026-03-12 02:13:06 +00:00
Peter Steinberger
607c158a75
test(cli): update daemon coverage restart contract 2026-03-12 01:43:27 +00:00
Peter Steinberger
b31836317a
fix(cli): handle scheduled gateway restarts consistently 2026-03-12 01:38:39 +00:00
Robin Waslander
841ee24340
fix(daemon): address clanker review findings for kickstart restart
Bug 1 (high): replace fixed sleep 1 with caller-PID polling in both
kickstart and start-after-exit handoff modes. The helper now waits until
kill -0 $caller_pid fails before issuing launchctl kickstart -k.

Bug 2 (medium): gate enable+bootstrap fallback on isLaunchctlNotLoaded().
Only attempt re-registration when kickstart -k fails because the job is
absent; all other kickstart failures now re-throw the original error.

Follows up on 3c0fd3dffe.
Fixes #43311, #43406, #43035, #43049
2026-03-12 02:16:24 +01:00
Robin Waslander
b7a37c2023
fix(node-host): extend script-runner set and add fail-closed guard for mutable-file approval
tsx, jiti, ts-node, ts-node-esm, vite-node, and esno were not recognized
as interpreter-style script runners in invoke-system-run-plan.ts. These
runners produced mutableFileOperand: null, causing invoke-system-run.ts
to skip revalidation entirely. A mutated script payload would execute
without the approval binding check that node ./run.js already enforced.

Two-part fix:
- Add tsx, jiti, and related TypeScript/ESM loaders to the known script
  runner set so they produce a valid mutableFileOperand from the planner
- Add a fail-closed runtime guard in invoke-system-run.ts that denies
  execution when a script run should have a mutable-file binding but the
  approval plan is missing it, preventing unknown future runners from
  silently bypassing revalidation

Fixes GHSA-qc36-x95h-7j53
2026-03-12 01:34:35 +01:00
Luke
a5ceb62d44
fix(whatsapp): trim leading whitespace in direct outbound sends (#43539)
Trim leading whitespace from direct WhatsApp text and media caption sends.

Also guard empty text-only web sends after trimming.
2026-03-12 11:32:04 +11:00
Peter Steinberger
7e3787517f fix: harden state dir permissions during onboard 2026-03-12 00:26:36 +00:00
Robin Waslander
ebed3bbde1
fix(gateway): enforce browser origin check regardless of proxy headers
In trusted-proxy mode, enforceOriginCheckForAnyClient was set to false
whenever proxy headers were present. This allowed browser-originated
WebSocket connections from untrusted origins to bypass origin validation
entirely, as the check only ran for control-ui and webchat client types.

An attacker serving a page from an untrusted origin could connect through
a trusted reverse proxy, inherit proxy-injected identity, and obtain
operator.admin access via the sharedAuthOk / roleCanSkipDeviceIdentity
path without any origin restriction.

Remove the hasProxyHeaders exemption so origin validation runs for all
browser-originated connections regardless of how the request arrived.

Fixes GHSA-5wcw-8jjv-m286
2026-03-12 01:16:52 +01:00
Robin Waslander
3c0fd3dffe
fix(daemon): replace bootout with kickstart -k for launchd restarts on macOS
On macOS, launchctl bootout permanently unloads the LaunchAgent plist.
Even with KeepAlive: true, launchd cannot respawn a service whose plist
has been removed from its registry. This left users with a dead gateway
requiring manual 'openclaw gateway install' to recover.

Affected trigger paths:
- openclaw gateway restart from an agent session (#43311)
- SIGTERM on config reload (#43406)
- Gateway self-restart via SIGTERM (#43035)
- Hot reload on channel config change (#43049)

Switch restartLaunchAgent() to launchctl kickstart -k, which force-kills
and restarts the service without unloading the plist. When the restart
originates from inside the launchd-managed process tree, delegate to a
new detached handoff helper (launchd-restart-handoff.ts) to avoid the
caller being killed mid-command. Self-restart paths in process-respawn.ts
now schedule the detached start-after-exit handoff before exiting instead
of relying on exit/KeepAlive timing.

Fixes #43311, #43406, #43035, #43049
2026-03-12 01:16:49 +01:00
Peter Steinberger
e11be576fb fix: repair bundled plugin dirs after npm install 2026-03-11 23:53:50 +00:00
Vincent Koc
b6d83749c8
fix(terminal): sanitize skills JSON and fallback on legacy Windows (#43520)
* Terminal: use ASCII borders on legacy Windows consoles

* Skills: sanitize JSON output for control bytes

* Changelog: credit terminal follow-up fixes

* Update CHANGELOG.md

* Update CHANGELOG.md

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>

* Skills: strip remaining escape sequences from JSON output

---------

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-03-11 19:53:07 -04:00
Peter Steinberger
0e397e62b7 chore: bump version to 2026.3.10 2026-03-11 23:29:53 +00:00
Brian Yu
cced1e0f76 preserve openai phase param 2026-03-11 23:15:52 +00:00
Gustavo Madeira Santana
da6f97a3f6 Memory: revalidate multimodal files before indexing 2026-03-11 22:51:34 +00:00
zhoulf1006
453c8d7c1b
fix(hooks): add missing trigger and channelId to agent_end, llm_input, and llm_output hook contexts (#42362)
Merged via squash.

Prepared head SHA: e6d7b7e31aa6ae12813b4609adb6e569a4084d08
Co-authored-by: zhoulf1006 <35586967+zhoulf1006@users.noreply.github.com>
Co-authored-by: hydro13 <6640526+hydro13@users.noreply.github.com>
Reviewed-by: @hydro13
2026-03-11 23:40:13 +01:00