* fix(sessions): persist sender metadata in user turn transcript JSONL
Thread senderId/senderName/senderUsername/senderE164 from the channel
inbound context into the persisted user-turn transcript message so that
group chat session JSONL records include __openclaw sender identity.
Ref #90531
* fix(sessions): exclude senderE164 from persisted transcript for privacy
Remove phone-number field from the persisted __openclaw sender envelope,
keeping only senderId, senderName, and senderUsername. Privacy-sensitive
E.164 metadata can be added back by maintainers if needed.
Ref #90531
* fix(infra): spread base fields in applyExecPolicyLayer return values
The `as TBase & ExecPolicyLayer` casts failed because the returned
objects did not spread `...base`, losing generic TBase fields.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(infra): use ...base spread instead of ...baseWithoutMode
The baseWithoutMode destructuring produces Omit<TBase, "mode"> which is
not assignable to TBase & ExecPolicyLayer when the as-cast is removed.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(infra): preserve baseWithoutMode in second applyExecPolicyLayer branch
The second branch (security/ask override without a mode change) must
exclude base.mode from the spread so that mode is not leaked into results
when only security or ask fields are being overridden.
Without this fix, the spread ...base carries mode through to the returned
object, breaking callers that expect applyExecPolicyLayer to clear stale
mode when applying explicit security/ask policy fields.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(sessions): scope persisted sender attribution
* fix(sessions): preserve sender metadata through hooks
* refactor(sessions): keep sender metadata path lean
* fix(sessions): preserve sender metadata in runtime writes
* fix(sessions): preserve queued sender attribution
* test(sessions): use complete message fixtures
* refactor(sessions): rely on narrowed user message type
* test(sessions): use shared temp directory helper
* test(sessions): align sender metadata assertions
* fix(sessions): honor sender metadata redaction hooks
* test(agents): use automatic temp cleanup
* test(sessions): cover queued turn provenance
* test(auto-reply): expect room sender metadata
* refactor(sessions): isolate queued transcript context
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Summary:
- Merged feat(tencent): add Tencent Hy3 provider (TokenHub and TokenPlan) after ClawSweeper review.
Automerge notes:
- Ran the ClawSweeper repair loop before final review.
- Included post-review commit in the final squash: fix(tencent): preserve TokenHub auth compatibility
- Included post-review commit in the final squash: refactor(tencent): unify TokenPlan env/flag naming with TokenHub
- Included post-review commit in the final squash: docs: refresh Tencent provider docs metadata
- Included post-review commit in the final squash: fix: allow TokenPlan provider config overlays
- Included post-review commit in the final squash: docs: dedupe Tencent provider glossary labels
- Included post-review commit in the final squash: fix(tencent): repair TokenHub model defaults
Validation:
- ClawSweeper review passed for head 30c9fc130f.
- Required merge gates passed before the squash merge.
Prepared head SHA: 30c9fc130f
Review: https://github.com/openclaw/openclaw/pull/99076#issuecomment-4888527271
Co-authored-by: leisang <leisang@tencent.com>
Co-authored-by: Mason Huang <masonxhuang@tencent.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: hxy91819
Centralized managed worktrees under <state-dir>/worktrees/<repo-fingerprint>/<name>
with branch-per-task (openclaw/<name>), .worktreeinclude provisioning, an optional
.openclaw/worktree-setup.sh repo hook, and a SQLite registry in the shared state DB.
Removal always snapshots the tree (untracked included, gitignored excluded) to
refs/openclaw/snapshots/<id>; restore rebuilds the branch at the original commit with
the snapshot content as uncommitted state. Lossless run-end cleanup, 7-day idle GC for
run-owned worktrees (manual exempt), orphan reconciliation, 30-day snapshot retention.
Surfaces: worktrees.* gateway RPC (operator.admin mutations), openclaw worktrees CLI,
Control UI page, plugin-SDK facade + Workboard kind:"worktree" materialization.
E2E-verified on Testbox: full create->work->remove->restore->gc lifecycle.
* feat(ios): durable offline command outbox for chat sends
Text messages sent while the gateway is unreachable queue in a durable
per-gateway outbox (new outbox_commands table in the per-gateway chat
cache SQLite store, schema v2) instead of failing. Queued bubbles render
with visible Queued/Sending/Not sent states and flush strictly in
createdAt order once transport health recovers; each command's client
UUID rides as the send idempotency key, so at-least-once delivery plus
gateway dedupe keeps the transcript exact.
Contract summary:
- Bounds: 50 queued commands per gateway; refused enqueues keep the
draft. Queued rows older than 48h expire to failed("expired") rather
than silently sending stale commands; tap-to-retry refreshes
createdAt so an expired row can resend as new intent.
- Failure taxonomy: transport-level failures keep rows queued without
burning retry attempts (backoff ladder, then health drops so the
reconnect machinery owns pacing); gateway rejections burn attempts
and fail terminally after 3, with context-menu retry/delete.
- Deletes are tombstoned synchronously and rechecked after the claim
await so an active flush can never send a removed command; Delete is
hidden while a bubble is already in flight; offline enqueue is
guarded against double submit during the health probe.
- Post-reconnect live sends route behind draining outbox rows (FIFO),
and a cold open assumes a backlog until restore adopts durable rows.
- Crash safety: 'sending' rows revert to 'queued' at startup; flushed
turns are spliced into the session's cached transcript before their
outbox row is deleted, and stale history snapshots cannot evict a
just-flushed turn until a snapshot confirms it.
- Per-gateway scoping and purge ride the transcript cache: one SQLite
file per gateway; reset/forget drops the queue with the cache.
Fixture/unpaired transports get no outbox.
Part of #46664
* chore(ios): sync native i18n inventory
* style(chat-ui): satisfy strict SwiftFormat lint (doc comment, scope blank line)
The relevant-mainline-drift check in scripts/pr merge-verify hard-failed
any landing whose prepared head predated a sibling merge, serializing all
agent landings behind a fresh CI cycle per merged PR. Required checks
still gate at the prepared head and GitHub mergeable state still blocks
real conflicts. OPENCLAW_PR_STRICT_DRIFT=1 restores the previous hard
gate for release-critical flows.
* feat(goals): keep active session goals in per-turn context (#100409)
Active goals now inject one bounded user-role context line on every
non-heartbeat turn, refreshed at queued/interrupt admission via
provenance-tracked generated lines so operator stops take effect
immediately. Adds qa/scenarios/goals continuance scenarios and
goals.* coverage IDs.
* docs: regenerate docs map for goal context section
* fix(qa): use requiredChannelDriver instead of execution.channel pin in goal scenarios
Commit noop-only reload plans through the runtime secrets activator so getRuntimeConfig readers observe edited config without a Gateway restart, and make heartbeat wakes read the current runtime config while schedule recalculation stays owned by updateConfig/restart-heartbeat. Fixes stale messages.visibleReplies / messages.groupChat.visibleReplies and all sibling none-classified keys. Supersedes #100321; thanks @Sedrak-Hovhannisyan for the report.
Co-Authored-By: Codex <noreply@openai.com>
Approval wait now fits inside the Codex dynamic-tool watchdog (70s +10s
gateway grace under the 90s kill), approval cards carry proposal id,
skill name, description, file count, and body size (spoof-safe
rendering), and timeouts return a structured pending-not-failed outcome
instead of a bare error. Expired requests cannot execute late; no
auto-apply; generic plugin approvals unchanged.
Prompt section drops from 13 lines to 2 (2128 -> 319 bytes); action
mechanics move into the skill_workshop tool schema (+25 bytes), for a
net -1784 bytes per request on affected sessions. Routing contract and
approval flow unchanged.
* feat(commands): add /learn to draft skills from recent work (#100408)
/learn rewrites the turn into a standards-guided Skill Workshop authoring
instruction: the agent gathers named sources (or distills the current
conversation) and files ONE pending skill proposal via skill_workshop.
Approval flow unchanged; sandboxed/tool-restricted agents get a clear
unavailable reply. Extracts the harness OpenClaw-tools predicate into
shared helpers and reserves the command name against plugin shadowing.
* docs: regenerate docs map for /learn section
* feat(gateway): add system.info RPC and Gateway Host card in Settings
Settings quick page now shows where the Gateway runs and how the host
is doing: machine name, LAN address and runtime port, OS, Node/PID,
uptime, CPU count and load, RAM, and free disk on the state-dir volume.
Backed by a new additive operator.read RPC (system.info); the card
polls every 10s while visible and hides for clients without the read
scope or against older gateways.
Refs #100465
* docs: regenerate docs map for Gateway host status section
* fix(gateway): harden system info compatibility
* fix(ui): clear stale gateway host info
* docs(changelog): note gateway host status
* fix: CLI-backend agent runtimes skip the BOOTSTRAP.md first-run ritual
CLI-backend runtimes (claude-cli, gemini-cli) never rendered the
"Bootstrap Pending" system-prompt gate the embedded runner produces, so
on a fresh workspace with a pending BOOTSTRAP.md the agent greeted the
user instead of running the first-run bootstrap ritual. BOOTSTRAP.md
reached CLI backends only as passive Project Context, never as a blocking
directive, because bootstrapMode was computed only on the embedded path.
Promote the embedded runner's bootstrap routing to a shared module
(src/agents/bootstrap-routing.ts) so both runtimes share one routing path,
then compute bootstrapMode in the CLI prepare step and thread it through
buildCliAgentSystemPrompt. The embedded-runner change is import-only.
Codex is unaffected: it runs on the embedded runtime and already renders
the gate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(agents): gate CLI bootstrap on native file access
---------
Co-authored-by: kruegerb <william.krueger@gmail.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
* fix(browser): drain download saves and use monotonic cursor for act response
- Add drainPendingDownloadSaves() to wait for in-flight saveAs before sampling
- Use monotonic downloadSeq cursor instead of bounded-list length
- Propagate downloads info in POST /act response for click/batch/evaluate
Fixes#93250
* fix(lint): add braces around single-line if returns
Fixes eslint(curly) failures in drainPendingDownloadSaves and
pickNewDownloads. PR #93307 required CI gate.
Ref: ClawSweeper P1 review finding
* fix(browser): scope act download metadata to action
* fix(browser): broadcast downloads to all active captures to prevent misattribution
When concurrent /act calls overlap on the same page, using
state.actionDownloadCaptures.at(-1) assigned downloads to the wrong
action's capture. Push managed save promises to all active captures
so the triggering action always receives its download metadata.
Also adds regression tests: broadcast-to-all-captures, sequential
capture isolation, and strengthen the dispose test to assert no
re-capture after disposal.
* fix(browser): prevent unhandled rejection when download capture action throws before drain
Move managedSave.catch() before the captures-branch check so the
rejection handler always runs, preventing an unhandled promise
rejection when the action throws before drain() is called. Simplify
the handler by removing the now-dead return + catch at the bottom.
* fix(browser): type downloads in BrowserActResponse, fix lint unused var
- Add optional downloads field to BrowserActResponse type contract so
typed callers of browserAct() can consume the new payload without casts.
- Remove unused afterDispose variable in pw-session tests (lint fix).
* fix(test): correct post-dispose download assertion
capture.promises is not cleared by dispose(), so re-draining a disposed
capture still returns pre-dispose results. This is benign — callers
should drain before disposing. Update the test to assert the actual
behavior (still shows old results, new download not captured).
* test(browser): cover /act download metadata response
* refactor(browser): report action-owned downloads safely
* fix(browser): close action download ownership races
* test(browser): type action download capture mock
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(auto-reply): render chat history since last reply as per-message prose
The inbound chat-history block dumped batched history as a raw JSON array, which models read poorly compared to the chat-window block's per-message prose. Reuse the existing formatChatWindowMessage renderer for history entries so both blocks share one shape, keep the untrusted framing label, and keep media rendered as a bare content-type tag so local paths and URLs stay redacted. Teach the metadata stripper to consume the new prose block form.
* fix(auto-reply): preserve every media content type in chat-history prose
The prose chat-history renderer only forwarded the first attachment's
content type per history message, dropping the rest for entries with
multiple media items. Join all bounded content types instead, and
regenerate the prompt snapshot fixture this changes.
* test(qa-lab): accept prose pending history
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* fix(ios): restore in-flight chat runs from gateway history
Restore active Apple chat ownership across reconnect, foreground, and sequence-gap recovery using the existing chat.history snapshot. Preserve agent/session scoping and Gateway user-turn identity across Codex and Copilot mirrors, including current offline-cache integration.
* fix(ios): restore in-flight chat runs from gateway history