* [AI] fix(agents): detect legacy openai-codex provider in model-not-found hint
In buildMissingProviderModelRegistrationHint, add an early-return check
for the legacy openai-codex alias (via normalizeProviderId). Instead
of suggesting a models.providers[] config entry that the config
validator rejects without baseUrl, the hint now points operators to
run openclaw doctor --fix for migration or check provider auth.
Fixes#100066
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(agents): cover legacy Codex provider config hint
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Vincent Koc <25068+vincentkoc@users.noreply.github.com>
* feat(skills): diagnose skill_workshop hidden by tool policy (#87570)
Workshop can be enabled and auto-capturing while tools.profile hides the
skill_workshop tool; every inspection surface looked healthy. plugins
inspect and openclaw doctor now name the excluding policy layer (global/
agent/provider profile, allowlist, denylist) and the exact alsoAllow
grant to add, via a shared resolveSkillWorkshopToolPolicyAvailability
helper that /learn's guard now reuses instead of composing policy
itself. Diagnosis only; no policy behavior change.
* ci: retrigger
* style: restore exec approval e2e formatting
* fix(install): trap SIGINT so Ctrl+C exits cleanly during upgrade doctor
Three changes to fix the install script's Ctrl+C handling:
1. Add INT/TERM signal traps that clean up temp files and exit with
the correct signal exit codes (130 for SIGINT, 143 for SIGTERM).
2. Preserve signal exit codes (>128) through run_quiet_step so the
doctor path can distinguish user cancellation from normal errors.
Non-signal failures still return 1, preserving existing caller
semantics for all other installer steps.
3. Fix guardCancel in onboard-helpers.ts: exit(0) changed to exit(1)
so Clack prompt cancellation (Escape/Ctrl+C) is treated as failure,
not success. This prevents the installer from continuing with plugin
updates after the user explicitly cancelled.
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
* fix(install): abort dashboard launch on doctor cancellation
When a user cancels the interactive upgrade-doctor prompt (Clack
cancellation exits 1, SIGINT exits 130), clear should_open_dashboard
so the installer does not launch a dead dashboard after an incomplete
upgrade.
Also propagate non-zero exit from run_doctor() so the non-interactive
upgrade path correctly skips dashboard launch on failure.
* fix: guard every run_doctor caller and add focused tests
The existing-config path called run_doctor without checking its return
value, so a failed or cancelled doctor would still launch the dashboard.
Now both run_doctor call sites guard the return value with if-then.
Adds focused tests verifying: every run_doctor caller is guarded,
dashboard flag is cleared on doctor failure, signal exit codes
propagate through run_quiet_step, and SIGINT (exit 130) triggers
abort_install_int.
* retrigger proof check
* fix: exit 130 on Clack cancellation so installer treats it as SIGINT
guardCancel now exits with 130 (SIGINT convention) instead of 1. When
the user presses Ctrl+C at an interactive doctor prompt, the installer
sees doctor_exit=130 and calls abort_install_int, aborting cleanly
instead of continuing after exit 1.
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
* fix: narrow exit 130 to doctor-prompter path only
Revert guardCancel to exit 0 by default (matching main) and pass
exit code 130 only from doctor-prompter where the installer needs
to distinguish user cancellation from normal failures.
This preserves the existing cancellation behavior for configure,
wizard, gateway, and daemon prompts while keeping the SIGINT
convention for the installer's doctor subprocess.
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
---------
Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
new Date(parsed.timestamp).getTime() can return NaN for unparseable
strings. Without a guard, NaN silently propagates into downstream
usage/cost calculations and corrupts billing data.
Add Number.isNaN(timestamp) check, falling back to 0 (same default
as the path when no timestamp key is present).
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(infra): enforce maxBytes in body-less HTTP error snippet path
* chore: rebase to trigger CI after boundaries check fix
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
* fix(sessions): log warning when parseJsonlEntries skips malformed lines
* fix(sessions): also warn in buildSessionInfo for malformed JSONL lines
* fix(sessions): warn in parseSessionEntries for malformed JSONL lines
* test(sessions): add warning regression tests for parseSessionEntries
* test(sessions): add parseJsonlEntries warning regression test
Add test verifying parseJsonlEntries logs warning for malformed JSONL
lines via loadEntriesFromFile, covering the second of three instrumented
session JSONL readers.
Co-Authored-By: Claude <noreply@anthropic.com>
* test(sessions): update parseSessionEntries warning expectations after rebase
* style(sessions): remove unnecessary type assertion in test
* test(sessions): add buildSessionInfo warning regression tests via SessionManager.list
---------
Co-authored-by: Claude <noreply@anthropic.com>
* 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(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(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>