Providers stream commentary as cumulative snapshots, and the draft line id was derived from the commentary text, so each snapshot minted a new id and stacked another line instead of replacing the open one. Id-less commentary now remembers its open line and reuses that id when the next snapshot continues it; a snapshot sanitized to nothing no longer clears the line, since only an explicit item id retracts.
Also fixes activation: the commentary gate guessed "partial" when streaming.mode was unset, which made progress.commentary a silent no-op on channels whose own default is progress (Telegram, Discord). It now takes the caller's resolved mode, matching resolveChannelStreamingPreviewToolProgress. Proven live on Telegram: three overlapping commentary lines collapse to one.
Three gates my own changes broke:
- docs/channels/telegram.md needed a blank line before </Note>; I formatted the
TypeScript but never ran the docs formatter over the pages I edited.
- The mock harness matched a literal with a regex, which oxlint's prefer-includes
rejects.
- Widening resolveChannelPreviewStreamMode's defaultMode to StreamingMode and
adding rendersRollingLinesNatively changes the exported plugin-SDK surface, so
the API baseline hash had to be regenerated.
ClawSweeper's accepted-migration path requires operator-facing docs for the
default change: state what an existing install sees after upgrading and the
one-line setting that restores streamed answer text.
A status headline replaced the rolling tool lines instead of sitting above
them, so a default Discord draft showed one preamble sentence for an entire
tool-heavy turn. Operators reached for `/verbose` to see any activity, which
delivers durable per-tool-call messages and floods the channel.
- Render the headline above the lines; both stay visible in one message.
- Shorten the start gate from 5s to 1.5s. The gate only creates the draft
when the timer fires and finalize cancels it, so quick answers still post
no draft while a 3s tool turn stops being silent.
- Drop Discord's label-gated tool-progress default so
resolveChannelStreamingPreviewToolProgress is the single owner. An explicit
`toolProgress: false` still silences the lines.
- Resolve that toggle against a "progress" mode guess when `streaming.mode`
is unset, so the progress-draft channels stop ignoring an explicit
`progress.toolProgress` opt-out.
Telegram now defaults to `streaming.mode: "progress"` like Discord; set
`"partial"` to keep streamed answer text. Its renderer draws work lines from
the compositor's structured lines, so `rendersRollingLinesNatively` keeps
them out of the composed text rather than printing every line twice.
* feat(cli): run agent exec against the ambient config, composed in memory
Exec previously ignored the operator's config entirely, so a one-shot turn
could not reach configured providers, credentials, or agentRuntime harness
selection. It now layers config the way other folder-scoped coding CLIs do.
The composed config is published as this process's runtime snapshot rather
than serialized to a temp file and re-read through OPENCLAW_CONFIG_PATH. The
snapshot is the only in-process config cache, so the file only ever fed it --
while writing env-substituted provider keys to disk where the run's own exec
tool could read them.
* fix(cli): resolve exec stored credentials from the configured agent dir
* chore(scripts): allow agent exec the file-scoped config loader at its process boundary
* test(cli): cover the exec credential default and pinned-config flags
* feat(harness): report copilot code-mode engagement on the attempt result
* test(copilot): prove code-mode engagement through the production tool bridge
* docs: describe the normalized codeModeEngaged value for native harnesses
* fix(voice-call): pin Twilio webhook verification to the configured public path
buildTwilioVerificationUrl overwrote the configured publicUrl path with the
incoming request path, so behind a path-rewriting reverse proxy the
reconstructed verification URL no longer matched the URL Twilio signed and
valid webhooks were rejected. Use the configured public path (keep the request
query), mirroring the Plivo sibling fixed in #112559. Restores callback-path
binding and adds proxy-prefix accept + local-path reject regression tests.
* fix(voice-call): pin Twilio verification to public URL path
---------
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(ui): rename the Memory Palace browser to Memory Wiki and document it
* refactor(memory-wiki): drop the old memory-palace module files
* docs: refresh generated docs map
* test(ui): align dream-diary hub-tab assertion with the wiki sub-tab id
* docs: describe memory wiki clustering behavior accurately
* docs(memory-wiki): record the wiki.overview rename decision at the registration site
* test(ui): query the Agents channels hub tab by tab role after the hub-tabs refactor
* feat(gateway): add loopback locality controls
* fix(gateway): keep loopback auth delays enforced under concurrency
The pending-timer cap let an attacker park cheap failures in every slot and then guess without penalty. Delays now key off a per-key deadline, so parallel guesses wait out the same escalating penalty and are still bounded by the max delay.
* fix(gateway): share one loopback penalty timer per key
Concurrent failures on a key now share a single timer and deadline instead of allocating one per in-flight request. Also corrects the security doc: the delay raises the cost of repeated guessing from one source, but credentials are compared before the failure response is delayed, so it is not a defense against parallel fan-out.
* docs(gateway): record why loopback delay stays post-verification
* docs: refresh generated docs map
* docs: refresh plugin SDK API baseline
* test: update loopback locality CI expectations