mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-15 23:24:03 -06:00
11b3bf374c
* feat(slack): unify the native progress turn into one streamed message
Native progress mode now streams the whole turn into a single Slack message:
narration flows as markdown_text chunks interleaved with plan/task chunks,
task rows carry file-path details and +N/-N output, the terminal task links
the session via url_source, and the final answer lands through stopStream in
the same message. Media/oversized/error finals keep their normal-delivery
fallbacks.
Deletes the finished-card receipt collapse (the '\u{1F6E0} N tool calls · ⏱ Ns' edit)
outright: the card now stays as its finished self.
Live-verified on a real workspace: exactly one bot message per turn.
* fix(slack): serialize native stream updates and keep append-only rendered text monotonic
Overlapping progress updates (compositor render, narration payload, final)
computed their narration delta before awaiting the network and committed
state only afterwards, so concurrent updates re-appended identical
narration into the streamed message (each status line landed 3x live).
A single ordering chain now makes each update's compute -> append -> commit
atomic.
applyAppendOnlyStreamUpdate also replaced the accumulated rendered text
with the incoming cumulative partial once an appended chunk had diverged
rendered from source, dropping content the sink already displayed; rendered
now only ever extends.
* fix(slack): stop refreshing thread status once a turn has visible output
Slack clears the assistant thread status as soon as the app puts anything
in the thread, and renders its own rotating agent-working row ("Generating
response...", "Finding answers...") for every status write after that -- it
ignores the app-supplied string. The typing keepalive re-set the status
every 3s for up to 60s, so each turn painted a duplicate status row under
the streamed card or progress message.
The status write is now gated on the turn having visible output, which the
dispatcher already tracks (delivered reply, committed preview, or posted
draft message). The first status still fires before any output, so slow
turns keep their indicator, and the typing reaction is tracked separately
so a suppressed status write still cleans up its reaction.
* fix(slack): let the plan card own the status line instead of echoing it
The status headline and plan explanation fed both the streamed narration
markdown and the plan card title, so every headline rendered twice: once as
static text and once in the card that keeps updating it in place. Narration
now carries only authored commentary and reasoning, and a preamble payload
whose text the card title already shows is not streamed again.
* feat(slack): make the native agent card the default progress surface
Slack's native plan/task card was opt-in behind streaming.progress
.nativeTaskCards while the Block Kit session card shipped as the default.
The native surface is the better product on every axis we can measure --
one streamed message instead of three artifacts, live task rows with file
paths and diff counts, and Slack's own agent chrome -- so it becomes the
default and an explicit false selects the Block Kit card instead.
The session link is now emitted only when it can actually work: the
operator set gateway.publicOrigin and left the Control UI enabled.
Installations with no externally reachable Gateway get no link rather than
a dead one.
The progress card still only appears for turns that do real work; the
existing compositor start gate keeps plain question-and-answer turns
card-free.
* fix(slack): finish the final inside a buffered native stream
A short narration leaves the SDK session un-flushed, so `delivered` stays
false until `stop` makes its first network call. Requiring delivery before
finishing in-stream sent the final through normal delivery and then
finalized the stream anyway, producing exactly the second message this path
exists to prevent. Stop-time rejection already falls back via
SlackStreamNotDeliveredError, so a live session is enough.
Addresses the ClawSweeper P1/P2 finding on this PR.
* refactor(slack): collapse duplicate streaming surfaces and drop dead code
Cleanup pass over the progress/streaming neighborhood, all verified unused
by exhaustive reference search:
- Deleted buildSlackProgressStreamStartChunks/UpdateChunks: byte-identical
pass-throughs to the same builder, plus the render-module branch that
chose between them. One exported builder now.
- Collapsed slackStreaming.draftMode, a lossless restatement of the mode it
was derived from, and its outbound mapper; nine comparisons now read the
mode directly. Inbound legacy parsing stays for doctor migration.
- Dropped stopSlackStream's text parameter, the draft stream's stop() member
and onMessageSent hook, a redundant nativeStreaming argument, four dead
members on the progress runtime, and two single-expression wrappers.
- Deduped the native card title, which was computed twice per render.
Production LOC for the whole PR drops from +216 to +114.
* chore(config): regenerate bundled channel metadata for the Slack card default
The generated metadata still carried the old opt-in help text and
default-false description for streaming.progress.nativeTaskCards, so
config UI and diagnostics would publish stale guidance.
* fix(slack): un-export the now-internal legacy draft-mode type
Collapsing draftMode removed the type's only external consumer, so knip
flagged it as an unused export. Doctor migration still parses these legacy
values inbound, so the type stays module-local.