fix(runtime-prompt): use macOS product version on Darwin (#95145)#95189
fix(runtime-prompt): use macOS product version on Darwin (#95145)#95189Sanjays2402 wants to merge 8 commits into
Conversation
…OS line
Adds a shared helper that returns the OS display string used by the agent
runtime prompt line (`buildRuntimeLine`). On Darwin it prefers the macOS
marketing product version reported by `sw_vers -productVersion` (e.g.
`macOS 26.5.1`) instead of the kernel release reported by `os.release()`
(e.g. `Darwin 25.5.0`). On other platforms it preserves the historical
`${os.type()} ${os.release()}` shape that prompts already render.
Architecture is intentionally omitted from the return value; the renderer
appends `arch` separately, so returning `macOS 26.5.1` keeps the rendered
line as `os=macOS 26.5.1 (arm64)` rather than `os=macOS 26.5.1 (arm64) (arm64)`.
Refs openclaw#95145
Adds 5 cases for the new prompt helper:
- darwin with Darwin 25.5.0 + sw_vers 26.5.1 → "macOS 26.5.1" (Tahoe)
- darwin with Darwin 24.5.0 + sw_vers 15.6 → "macOS 15.6" (Sequoia)
- darwin with blank sw_vers → falls back to `macOS ${os.release()}`
- linux → "Linux 6.10.0-amd64" (preserves os.type/os.release shape)
- win32 → "Windows_NT 10.0.26100" (preserves os.type/os.release shape)
The first case is the regression for openclaw#95145 — before this change the runtime
prompt rendered `os=Darwin 25.5.0` on macOS 26 hosts, which agents would
read as macOS 15.5 (Sequoia).
…enclaw#95145) Replace the kernel-only `${os.type()} ${os.release()}` with the shared helper that returns the macOS marketing product version on Darwin. On a macOS 26 (Tahoe / Darwin 25.x) host the runtime prompt line now reads `os=macOS 26.5.1 (arm64)` instead of `os=Darwin 25.5.0 (arm64)`, which agents were mapping to macOS 15 (Sequoia). attempt.ts is the embedded agent runner's main prompt path, so this is the highest-traffic call site for the broken mapping.
…enclaw#95145) Compaction reuses the runtime prompt and was carrying the same broken `${os.type()} ${os.release()}` value for the OS line. Switching to the shared helper means compacted summaries also see the correct macOS marketing version on Tahoe hosts.
…penclaw#95145) Third of the three runtime-prompt call sites that built the OS line from `os.type()` + `os.release()`. The CLI runner is used by external agents (e.g. `openclaw run` and provider CLI backends), so without this fix those flows would still see `Darwin 25.5.0` on a macOS 26 host. After this commit `grep -nR 'os.type() } ${os.release()}' src/agents` returns no hits in runtime-prompt code paths.
…e-line shape Asserts that when callers pass `os: 'macOS 26.5.1'` (the value produced by `resolveRuntimePromptOs()` on a Tahoe host), `buildRuntimeLine` renders `os=macOS 26.5.1 (arm64)` exactly once, without the legacy `Darwin 25.x` kernel release leaking through and without duplicating the architecture suffix. This is the end-to-end guard for the runtime-prompt regression in openclaw#95145.
|
Thanks for the context here. I swept through the related work, and this is now duplicate or superseded. Close: the same macOS Tahoe runtime prompt fix has already landed on current main via the merged canonical PR, and this branch is now a conflicting duplicate with no meaningful unique remainder. Canonical path: Keep the merged current-main implementation from #95225 and close duplicate candidate branches for the same Tahoe runtime prompt bug. So I’m closing this here and keeping the remaining discussion on #95225. Review detailsBest possible solution: Keep the merged current-main implementation from #95225 and close duplicate candidate branches for the same Tahoe runtime prompt bug. Do we have a high-confidence way to reproduce the issue? No current-main failure remains: current main routes the runtime prompt OS field through the merged sw_vers-backed helper. The original failure remains source-proven in v2026.6.11, where all three prompt producers still pass raw Is this the best way to solve the issue? Yes, the merged current-main path is the best current solution: it keeps the Darwin product-version fix in a shared helper, preserves non-Darwin prompt strings, and avoids changing the raw status release field. This branch solved the same problem but is now redundant and conflicting. Security review: Security review cleared: No security or supply-chain concern was found; the PR only changes local OS metadata formatting and focused tests. AGENTS.md: found and applied where relevant. What I checked:
Likely related people:
Codex review notes: model internal, reasoning high; reviewed against 267898cddb98; fix evidence: commit 825d9a66623a, main fix timestamp 2026-06-29T08:03:56-07:00. |
…allers (openclaw#95145) Addresses the reviewer P1 from clawsweeper on PR openclaw#95189: resolveRuntimePromptOs() is invoked from embedded attempt, compaction, and CLI runtime-prompt callers on every prompt build, so the Darwin branch was synchronously spawning sw_vers once per prompt without reuse. resolveOsSummary() already caches its full summary by platform/release/arch tuple, but the underlying macosProductVersion() helper spawned sw_vers unconditionally on each call — so resolveRuntimePromptOs's direct call to macosProductVersion() bypassed the existing cache entirely. Add a module-level cachedMacosProductVersionByRelease map keyed by os.release(). The macOS marketing product version is stable for the life of a process for a given kernel release; once resolved, both resolveOsSummary() and resolveRuntimePromptOs() reuse the cached string and no longer re-spawn sw_vers. Non-Darwin output is unchanged. Also export a test-only __resetOsSummaryCachesForTests() helper that clears both module-level caches, and wire beforeEach() hooks into the existing test file. Without the reset, the new cache exposes a pre-existing test-isolation gap — the 'falls back to os.release on darwin when sw_vers returns blank' case reuses kernel release 25.5.0 from an earlier case that already cached '26.5.1', so the cache would return the earlier mock and the fallback case would never exercise its fallback. The reset hook makes each test case authoritative for its own mock. Two new cache regression tests: - reuses cached macOS product version across repeated calls (asserts spawnSync is called exactly once across 3 invocations) - runtime-prompt and os-summary share the cached Darwin product version (asserts resolveRuntimePromptOs called after resolveOsSummary for the same kernel release does not re-spawn sw_vers — the two helpers share the module-level cache) Verification: - node scripts/run-vitest.mjs src/infra/os-summary.test.ts src/agents/system-prompt.test.ts src/agents/embedded-agent-runner/run/attempt-system-prompt.test.ts → all 3 shards pass (11 + 1 + 4 tests) - pnpm tsgo:core → clean - pnpm tsgo:core:test → clean
|
Thanks @clawsweeper — addressed the cache P1 in 64677b03. Cache before merge — doneAdded a module-level
Test isolation gap surfaced + fixedAdding the cache exposed a pre-existing test-isolation hole: the existing "falls back to os.release on darwin when sw_vers returns blank" case reuses kernel release New cache regression testsTwo new cases under
VerificationRe: canonical-fix overlap (#95158, #95194, #95195)I see your note that several open PRs target the same linked bug. Happy to defer to whichever lane maintainers prefer — if any of #95158/#95194/#95195 lands first with the cached shape, close this one as superseded. The patch here is intentionally narrow (Darwin-only cached lookup + 2 cache regression tests + 1 test-isolation export) so it should also be straightforward to cherry-pick into another lane if useful. |
|
Nice implementation! I appreciate the clear commit messages and focused changes. |
…e-dangle The __resetOsSummaryCachesForTests export tripped oxlint's eslint/no-underscore-dangle rule (error at src/infra/os-summary.ts:45), the sole check-lint failure on this branch. Rename it to resetOsSummaryCachesForTests across the module and its test rather than adding a lint suppression: the only existing no-underscore-dangle suppression in production code is for an unrenameable compile-time define (__OPENCLAW_VERSION__), and every production suppression is tracked by an explicit allowlist guard test, so a rename is the least-invasive, convention-matching fix for a test-only internal helper.
|
ClawSweeper applied the proposed close for this PR.
|
|
🦞👀 Reason: structured ClawSweeper close marker: close-required (sha=64af1a46dd21a1bdf99fedd8b8fd8e4cf5ae660c) Usage: |
Closes #95145
What Problem This Solves
Fixes an issue where agents running on macOS 26 (Tahoe) saw the wrong OS in their runtime prompt —
os=Darwin 25.5.0 (arm64)instead ofos=macOS 26.5.1 (arm64). Agents reading "Darwin 25" mapped it to macOS 15 (Sequoia), because the Darwin kernel major version stopped tracking the macOS marketing major version starting with macOS 26.The OS line is rendered by
buildRuntimeLine()from theruntimeInfo.osfield that three callers populate: the embedded agent runner attempt path, embedded-agent compaction, and the CLI runner. All three were building that field with`${os.type()} ${os.release()}`instead of reusing the existingsw_vers-backed helper.src/infra/os-summary.tsandsrc/infra/system-presence.tsalready had the right pattern for status/presence surfaces; this PR brings the runtime prompt onto the same path.Why This Change Was Made
Adds
resolveRuntimePromptOs()next to the existingresolveOsSummary()and routes the three runtime callers through it. The new helper returnsmacOS <productVersion>on Darwin and the legacy${os.type()} ${os.release()}on every other platform, so non-Darwin prompt output is byte-identical. Architecture is deliberately not appended — the renderer already addsarchseparately, and folding it intooswould have producedos=macOS 26.5.1 (arm64) (arm64).Non-goals (left untouched):
os-summary.label(macos 15.4 (arm64)style) — still used by status/diagnostics, not the prompt line.openai-chatgpt-responses.tsUser-Agent string — that surface is sent to OpenAI, not into agent context.sw_versfor the prompt helper — runtime prompt callers fire once per attempt, andresolveOsSummary()already caches when called from status surfaces.User Impact
Agent runtime prompts on macOS 26 (Tahoe) hosts now correctly report the macOS marketing version, fixing the
os=token that downstream prompts and behavior keyed off of. Any agent logic that branched on macOS version (skill availability, command syntax hints, OS-specific guidance) now sees the correct major. No behavior change on Linux, Windows, or pre-Tahoe macOS — the rendered prompt line is byte-identical there.Evidence
Live host probe (this PR was developed on a macOS 26.5 / Darwin 25.5.0 host):
Vitest — the three suites called out in the issue triage all pass:
Typecheck:
New tests added:
resolveRuntimePromptOscases for Tahoe (Darwin 25.x), Sequoia (Darwin 24.x), blanksw_versfallback, Linux, and Windows.buildRuntimeLineregression that assertsos=macOS 26.5.1 (arm64)is rendered exactly once and that the legacyDarwin 25substring does not leak through.