Skip to content

Commit f1ee4d5

Browse files
authored
Merge branch 'main' into fix-claude-cli-session-invalidation
2 parents d46c1b2 + a16150c commit f1ee4d5

139 files changed

Lines changed: 6112 additions & 1851 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ Docs: https://docs.openclaw.ai
66

77
### Fixes
88

9+
- Agents/skills: apply the full effective tool policy pipeline to inline `command-dispatch: tool` skill dispatch before owner-only filtering, preserving configured allow, deny, sandbox, sender, group, and subagent restrictions. (#78525)
10+
- Providers/GitHub Copilot: request identity-encoded Copilot API responses across token exchange, catalog, model calls, usage, and embeddings so compressed Business-account error payloads no longer reach JSON parsers as gzip bytes. Fixes #82871. Thanks @tonyfe01.
11+
- Telegram: preserve replied-to bot messages, captions, and media metadata in group reply chains so follow-up replies understand what the user is reacting to. (#82863)
912
- Agents/diagnostics: split slow embedded-run `attempt-dispatch` startup summaries into workspace, prompt, runtime-plan, and final dispatch subspans so traces identify the delayed setup phase. Fixes #82782. (#82783) Thanks @galiniliev.
1013
- CLI/media: accept HTTP(S) URLs in `openclaw infer image describe --file`, fetching remote images through the guarded media path instead of treating URLs as local files. Fixes #82837. (#82854) Thanks @neeravmakwana.
14+
- Agents/subagents: keep session-backed parent runs active when the child wait call times out before the child session has actually settled, so late subagent completions are reconciled instead of being lost. Fixes #82787. Thanks @ramitrkar-hash.
15+
- Control UI: advertise shared Gateway protocol constants in browser connect frames, fixing protocol mismatch handshakes after the protocol 5 bump. Fixes #82882. Thanks @galiniliev.
1116
- Agents/subagents: route group/channel subagent completions through message-tool-only handoffs when required and keep active-requester wake failures from dropping completion delivery. Fixes #82803. Thanks @galiniliev, @yozakura-ava, and @moeedahmed.
1217
- Memory-core: scan persisted memory source sessions on startup, comparing on-disk transcripts against the index and marking only missing/newer/resized files dirty for incremental sync. Fixes #82341. (#82341) Thanks @giodl73-repo.
1318
- Telegram: keep the top-level default account in the account list when named accounts or bindings are added alongside top-level credentials, preserving default polling while still letting named-only configs resolve to a single account. Fixes #82794. (#82794) Thanks @giodl73-repo.
@@ -16,11 +21,15 @@ Docs: https://docs.openclaw.ai
1621
- Agents/tools: keep the `message` tool available in embedded runs when it is explicitly allowed through `tools.alsoAllow` or runtime tool allowlists, so channel plugins with custom reply delivery can still use configured message sends. Fixes #82833. Thanks @cn1313113.
1722
- WhatsApp: honor forced document delivery for outbound image, GIF, and video media so `forceDocument`/`asDocument` sends preserve original media bytes instead of using compressed media payloads. (#79272) Thanks @itsuzef.
1823
- WhatsApp: name outbound document attachments from their MIME type when no filename is provided, so PDF and CSV sends arrive as `file.pdf` and `file.csv` instead of an extensionless `file`. Thanks @mcaxtr.
24+
- Process/diagnostics: report active lane blockers in lane wait warnings so `queueAhead=0` no longer hides commands waiting behind active work. Fixes #82791. (#82792) Thanks @galiniliev.
1925

2026
## 2026.5.17
2127

2228
### Changes
2329

30+
- Control UI: move settings-only destinations into the Settings workspace and add sidebar recent-session shortcuts plus a one-click new-session action.
31+
- Control UI: speed up scoped settings pages by loading required config before schema refreshes, caching burst schema responses, and opening Communications on lighter message settings first.
32+
- Control UI: simplify the Cron Jobs workspace with modal job creation, collapsed filters, and an empty state aimed at first-time setup.
2433
- Security/audit: add `security.audit.suppressions` for intentionally accepted audit findings, keeping suppressed matches out of the active summary while preserving them in JSON output with an active suppression notice. (#76949) Thanks @100menotu001.
2534
- Agents/subagents: label delegated task and subagent completion handoffs as ready for parent review, and tell requester agents to review/verify results before calling them done. (#78985) Thanks @100menotu001.
2635
- Providers/media: add fal and OpenRouter music-generation providers for the shared `music_generate` tool, including fal MiniMax/ACE/Stable Audio endpoints and OpenRouter Lyria audio output.
@@ -44,6 +53,8 @@ Docs: https://docs.openclaw.ai
4453

4554
### Fixes
4655

56+
- Gateway/Docker: fail closed for non-loopback gateway starts without explicit shared-secret or trusted-proxy auth, and stop the image default command from bypassing config validation. Fixes #82865. (#82866) Thanks @coygeek.
57+
- Agents/followups: route queued followup turns through CLI runtime backends instead of embedded harness lookup, preventing `claude-cli`/`google-gemini-cli` followups from failing before delivery. Fixes #82847. (#82857) Thanks @hclsys.
4758
- CLI/sessions: let `openclaw sessions cleanup --fix-missing` prune malformed rows with unresolvable transcript metadata instead of throwing. Fixes #80970. (#82745) Thanks @IWhatsskill.
4859
- Gateway/usage: refresh large session usage summaries in the background and reuse durable transcript metadata so `sessions.usage` no longer blocks Gateway requests on full transcript rescans. Fixes #82773. (#82778) Thanks @hclsys.
4960
- TUI: restore the submitted draft when chat is busy instead of clearing it or queueing another run. Fixes #45326. (#82774) Thanks @hyspacex.
@@ -61,6 +72,10 @@ Docs: https://docs.openclaw.ai
6172
- CLI/web: resolve provider-scoped web search/fetch SecretRefs for `infer web ... --provider ...` while leaving unrelated plugin secrets untouched. Fixes #82621. Thanks @leno23.
6273
- Providers/Anthropic Vertex: resolve installed provider public surfaces from package-local `dist/`, restoring `anthropic-vertex/*` model calls after plugin externalization. Fixes #82781. Thanks @0L1v3DaD.
6374
- Gateway/exec approvals: bind path-shaped allowlists, safe-bin trust, skill auto-allow, Allow Always persistence, and approval audit metadata to the executable realpath so symlinked binaries cannot keep approvals after retargeting. Fixes #45595. Thanks @jasonftl.
75+
- Mac app: reorganize Settings around a grouped sidebar, with separate Connection and Exec Approvals pages so everyday permissions and app toggles are easier to scan.
76+
- Mac app: redraw the animated menu bar critter to match the rounded app mascot with antennae, side arms, two feet, and smoother template rendering.
77+
- Mac app: cache settings config schema/drafts and load channel config in parallel with channel probes, making repeated Channels and Config tab switches responsive over remote tunnels.
78+
- Control UI: negotiate the Gateway protocol from shared constants so rebuilt dashboards connect to current gateways instead of reporting a protocol mismatch.
6479
- Mac app: let menu gateway/session error text wrap across a few lines and stop rebuilding dynamic Context/Gateway menu rows while the menu is open, reducing flicker.
6580
- Mac app: make device pairing approval sheets friendlier, with concise Mac/device copy, shortened identifiers, friendly scope labels, and Approve as the primary action.
6681
- Providers/Qwen: honor session thinking level for `qwen-chat-template` payloads so `/think off` disables nested llama.cpp chat-template thinking controls. Fixes #82768. Thanks @bfox55.
@@ -85,6 +100,7 @@ Docs: https://docs.openclaw.ai
85100
- QQBot: treat only explicit truthy `QQBOT_DEBUG` values as enabling debug logs, so false-like values such as `0` no longer expose debug output. Fixes #82644. (#82697) Thanks @leno23.
86101
- Agents/session_status: resolve implicit no-arg status lookups against the live run session, so `/think` changes report the current thinking level instead of stale sandbox state. Fixes #82669. (#82696) Thanks @leno23.
87102
- Discord: keep progress drafts visible for message-tool-only guild replies under the default coding tool profile. Fixes #82747. Thanks @eliranwong.
103+
- Agents: prefer current structured assistant final answers when assembling final reply payloads, reducing reliance on streamed preview fragments after channel transcript recovery. (#82850) Thanks @joshavant.
88104
- Discord: keep unmentioned room-event history until a visible Discord send succeeds, so quiet ambient context does not disappear before message-tool delivery. (#82573) Thanks @obviyus.
89105
- CLI/setup: order the model/auth provider picker as OpenAI, Anthropic, xAI, Google, then the remaining providers alphabetically.
90106
- Diagnostics/usage/voice-call: treat explicit zero and non-finite limits as empty results and reject invalid voice-call numeric CLI flags. Fixes #82646, #82650, #82651, and #82653. (#82679) Thanks @leno23.
@@ -118,6 +134,7 @@ Docs: https://docs.openclaw.ai
118134
- Providers/xAI: continue polling video generations while xAI reports in-flight jobs as `pending`, so Grok video requests no longer fail before the final `done` response. (#82610) Thanks @Manzojunior.
119135
- Logs: redact raw Basic auth and named security headers from `logs.tail` output before returning lines to read-scoped clients. Fixes #66832. Thanks @Magicray1217.
120136
- CLI/gateway: emit structured JSON for gateway transport close/timeout failures when `--json` is requested by health, gateway health, and devices list commands. Fixes #79108. Thanks @TurboTheTurtle.
137+
- Agents/Telegram: retry Bedrock non-visible terminal turns and mark non-deliverable attempts as trajectory errors instead of silent success. Fixes #82394. (#82905) Thanks @joshavant.
121138
- Telegram: normalize announce group targets via a new `resolveSessionTarget` channel hook so scheduled announcements resolve consistently against the same Telegram session conversation registry as inbound turns. Fixes #81229. Thanks @giodl73-repo.
122139
- QA/RTT: let `pnpm rtt` lease Convex-backed Telegram credentials while preserving RTT sample counts, sample timeouts, and result stats on the RTT harness path.
123140
- Discord: bind delayed gateway `identify` retries to the originating socket generation so retries triggered after a reconnect do not identify against a fresh socket. Fixes #82225. Thanks @giodl73-repo.

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,4 +293,4 @@ USER node
293293
HEALTHCHECK --interval=3m --timeout=10s --start-period=15s --retries=3 \
294294
CMD node -e "fetch('http://127.0.0.1:18789/healthz').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
295295
ENTRYPOINT ["tini", "-s", "--"]
296-
CMD ["node", "openclaw.mjs", "gateway", "--allow-unconfigured"]
296+
CMD ["node", "openclaw.mjs", "gateway"]

apps/macos/Sources/OpenClaw/CLIInstallPrompter.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ final class CLIInstallPrompter {
2626
case .alertFirstButtonReturn:
2727
Task { await self.installCLI() }
2828
case .alertThirdButtonReturn:
29-
self.openSettings(tab: .general)
29+
self.openSettings(tab: .connection)
3030
default:
3131
break
3232
}

apps/macos/Sources/OpenClaw/ChannelsStore+Config.swift

Lines changed: 159 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,43 +2,102 @@ import Foundation
22
import OpenClawProtocol
33

44
extension ChannelsStore {
5-
func loadConfigSchema() async {
6-
guard !self.configSchemaLoading else { return }
5+
func loadConfigSchema(force: Bool = false) async {
6+
let sourceKey = self.currentConfigCacheSourceKey()
7+
self.resetConfigSchemaCacheIfSourceChanged(sourceKey)
8+
if !force, self.configSchema != nil {
9+
return
10+
}
11+
guard !self.queueConfigSchemaReloadIfLoading(sourceKey: sourceKey, force: force) else { return }
712
self.configSchemaLoading = true
8-
defer { self.configSchemaLoading = false }
13+
self.configSchemaLoadingSourceKey = sourceKey
14+
defer {
15+
self.configSchemaLoading = false
16+
self.configSchemaLoadingSourceKey = nil
17+
}
918

10-
do {
11-
let res: ConfigSchemaResponse = try await GatewayConnection.shared.requestDecoded(
12-
method: .configSchema,
13-
params: nil,
14-
timeoutMs: 8000)
15-
let schemaValue = res.schema.foundationValue
16-
self.configSchema = ConfigSchemaNode(raw: schemaValue)
17-
let hintValues = res.uihints.mapValues { $0.foundationValue }
18-
self.configUiHints = decodeUiHints(hintValues)
19-
} catch {
20-
self.configStatus = error.localizedDescription
19+
var requestSourceKey = sourceKey
20+
21+
while true {
22+
self.configSchemaLoadingSourceKey = requestSourceKey
23+
do {
24+
let res: ConfigSchemaResponse = try await GatewayConnection.shared.requestDecoded(
25+
method: .configSchema,
26+
params: nil,
27+
timeoutMs: 8000)
28+
self.applyConfigSchemaResponse(res, sourceKey: requestSourceKey)
29+
} catch {
30+
self.configStatus = error.localizedDescription
31+
}
32+
33+
guard self.configSchemaReloadPending else { break }
34+
self.configSchemaReloadPending = false
35+
requestSourceKey = self.currentConfigCacheSourceKey()
36+
self.resetConfigSchemaCacheIfSourceChanged(requestSourceKey)
2137
}
2238
}
2339

24-
func loadConfig() async {
25-
do {
26-
let snap: ConfigSnapshot = try await GatewayConnection.shared.requestDecoded(
27-
method: .configGet,
28-
params: nil,
29-
timeoutMs: 10000)
30-
self.configStatus = snap.valid == false
31-
? "Config invalid; fix it in ~/.openclaw/openclaw.json."
32-
: nil
33-
self.configRoot = snap.config?.mapValues { $0.foundationValue } ?? [:]
34-
self.configDraft = cloneConfigValue(self.configRoot) as? [String: Any] ?? self.configRoot
35-
self.configDirty = false
36-
self.configLoaded = true
37-
38-
self.applyUIConfig(snap)
39-
} catch {
40-
self.configStatus = error.localizedDescription
40+
func loadConfig(force: Bool = true) async {
41+
let sourceKey = self.currentConfigCacheSourceKey()
42+
self.resetConfigCacheIfSourceChanged(sourceKey)
43+
if !force, self.configLoaded {
44+
return
45+
}
46+
guard !self.queueConfigReloadIfLoading(sourceKey: sourceKey, force: force) else { return }
47+
self.configLoading = true
48+
self.configLoadingSourceKey = sourceKey
49+
defer {
50+
self.configLoading = false
51+
self.configLoadingSourceKey = nil
4152
}
53+
54+
var requestForce = force
55+
var requestSourceKey = sourceKey
56+
57+
while true {
58+
self.configLoadingSourceKey = requestSourceKey
59+
do {
60+
let snap: ConfigSnapshot = try await GatewayConnection.shared.requestDecoded(
61+
method: .configGet,
62+
params: nil,
63+
timeoutMs: 10000)
64+
self.applyConfigSnapshot(snap, sourceKey: requestSourceKey, force: requestForce)
65+
} catch {
66+
self.configStatus = error.localizedDescription
67+
}
68+
69+
guard self.configForceReloadPending else { break }
70+
self.configForceReloadPending = false
71+
requestForce = true
72+
requestSourceKey = self.currentConfigCacheSourceKey()
73+
self.resetConfigCacheIfSourceChanged(requestSourceKey)
74+
}
75+
}
76+
77+
func applyConfigSnapshot(_ snap: ConfigSnapshot, sourceKey: String, force: Bool) {
78+
guard self.configSourceKey == sourceKey else { return }
79+
guard force || !self.configDirty else { return }
80+
81+
self.configStatus = snap.valid == false
82+
? "Config invalid; fix it in ~/.openclaw/openclaw.json."
83+
: nil
84+
self.configRoot = snap.config?.mapValues { $0.foundationValue } ?? [:]
85+
self.configDraft = cloneConfigValue(self.configRoot) as? [String: Any] ?? self.configRoot
86+
self.configDirty = false
87+
self.configLoaded = true
88+
self.configSourceKey = sourceKey
89+
90+
self.applyUIConfig(snap)
91+
}
92+
93+
func applyConfigSchemaResponse(_ res: ConfigSchemaResponse, sourceKey: String) {
94+
guard self.configSchemaSourceKey == sourceKey else { return }
95+
96+
let schemaValue = res.schema.foundationValue
97+
self.configSchema = ConfigSchemaNode(raw: schemaValue)
98+
let hintValues = res.uihints.mapValues { $0.foundationValue }
99+
self.configUiHints = decodeUiHints(hintValues)
100+
self.configSchemaSourceKey = sourceKey
42101
}
43102

44103
private func applyUIConfig(_ snap: ConfigSnapshot) {
@@ -85,7 +144,75 @@ extension ChannelsStore {
85144
}
86145

87146
func reloadConfigDraft() async {
88-
await self.loadConfig()
147+
await self.loadConfig(force: true)
148+
}
149+
150+
func resetConfigSchemaCacheIfSourceChanged(_ sourceKey: String) {
151+
guard let cachedSourceKey = self.configSchemaSourceKey else {
152+
self.configSchemaSourceKey = sourceKey
153+
return
154+
}
155+
guard cachedSourceKey != sourceKey else { return }
156+
self.configSchema = nil
157+
self.configUiHints = [:]
158+
self.configSchemaSourceKey = sourceKey
159+
}
160+
161+
func resetConfigCacheIfSourceChanged(_ sourceKey: String) {
162+
guard let cachedSourceKey = self.configSourceKey else {
163+
self.configSourceKey = sourceKey
164+
return
165+
}
166+
guard cachedSourceKey != sourceKey else { return }
167+
self.configRoot = [:]
168+
self.configDraft = [:]
169+
self.configDirty = false
170+
self.configLoaded = false
171+
self.configSourceKey = sourceKey
172+
}
173+
174+
func queueConfigReloadIfLoading(sourceKey: String, force: Bool) -> Bool {
175+
guard self.configLoading else { return false }
176+
if force || self.configLoadingSourceKey != sourceKey {
177+
self.configForceReloadPending = true
178+
}
179+
return true
180+
}
181+
182+
func queueConfigSchemaReloadIfLoading(sourceKey: String, force: Bool) -> Bool {
183+
guard self.configSchemaLoading else { return false }
184+
if force || self.configSchemaLoadingSourceKey != sourceKey {
185+
self.configSchemaReloadPending = true
186+
}
187+
return true
188+
}
189+
190+
private func currentConfigCacheSourceKey() -> String {
191+
let root = OpenClawConfigFile.loadDict()
192+
let settings = CommandResolver.connectionSettings(configRoot: root)
193+
let env = ProcessInfo.processInfo.environment
194+
return [
195+
"mode:\(settings.mode.rawValue)",
196+
"target:\(settings.target)",
197+
"identity:\(settings.identity)",
198+
"project:\(settings.projectRoot)",
199+
"cli:\(settings.cliPath)",
200+
"port:\(GatewayEnvironment.gatewayPort())",
201+
"gateway:\(Self.configFingerprint(root["gateway"]))",
202+
"token:\(Self.configFingerprint(env["OPENCLAW_GATEWAY_TOKEN"]))",
203+
"password:\(Self.configFingerprint(env["OPENCLAW_GATEWAY_PASSWORD"]))",
204+
].joined(separator: "|")
205+
}
206+
207+
private static func configFingerprint(_ value: Any?) -> String {
208+
guard let value else { return "nil" }
209+
if JSONSerialization.isValidJSONObject(value),
210+
let data = try? JSONSerialization.data(withJSONObject: value, options: [.sortedKeys])
211+
{
212+
return "\(data.count):\(data.hashValue)"
213+
}
214+
let text = String(describing: value)
215+
return "\(text.count):\(text.hashValue)"
89216
}
90217
}
91218

apps/macos/Sources/OpenClaw/ChannelsStore+Lifecycle.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ extension ChannelsStore {
2525
guard self.pollTask == nil else { return }
2626
self.pollTask = Task.detached { [weak self] in
2727
guard let self else { return }
28-
await self.refresh(probe: true)
29-
await self.loadConfigSchema()
30-
await self.loadConfig()
28+
async let schemaLoad: Void = self.loadConfigSchema()
29+
async let configLoad: Void = self.loadConfig(force: false)
30+
async let statusRefresh: Void = self.refresh(probe: true)
31+
_ = await (schemaLoad, configLoad, statusRefresh)
3132
while !Task.isCancelled {
3233
try? await Task.sleep(nanoseconds: UInt64(self.interval * 1_000_000_000))
3334
await self.refresh(probe: false)

0 commit comments

Comments
 (0)