Commit Graph

44950 Commits

Author SHA1 Message Date
Peter Steinberger 54257e0200 fix(logging): preserve incomplete tail records (#127810)
* fix(logging): preserve incomplete tail records

* test(logging): terminate channel log fixtures

---------

Co-authored-by: Amp <amp@ampcode.com>
2026-08-22 02:35:30 -07:00
Peter Steinberger 3ae680f714 perf(gateway): reuse session subscription lookups (#127811)
Co-authored-by: Amp <amp@ampcode.com>
2026-08-22 02:04:22 -07:00
Vincent Koc 40ede061ab fix(plugins): normalize correction versions in drift repair commands (#127800)
Punchcard-Session: brisk-timber-meadow-w6
2026-08-22 02:00:31 -07:00
Peter Lee 97fe326660 fix(discord): treat audio transcripts as untrusted input (#123098)
Centralize untrusted audio transcript framing across channel plugins and keep Discord voice transcripts out of typed command provenance.

Co-authored-by: xialonglee <li.xialong@xydigit.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-22 14:28:18 +05:30
Josh Lehman 0ab6578478 fix: preserve source code in tool results (#127697)
Preserve opaque source strings across tool-result replay while retaining masking for direct .env reads, registered or recognizable secrets, structured sensitive fields, and payment data.
2026-08-22 13:30:45 +05:30
sloptop 4c866a9bee fix(models): scope status alias resolution to the selected agent (#127631)
`models status --agent` selected the agent's primary and fallbacks but
resolved aliases from global defaults only, so a bare per-agent alias was
displayed and probed as a different provider than the runtime selects.
Status, --check, auth diagnostics, provider-use reporting and --probe could
all inspect the wrong route and report false missing or auth state.

The canonical resolvers already merge per-agent model rows when given an
agentId, so this passes the agentId that was already resolved locally and
deletes the synthetic global-default config that carried the agent primary.
Displayed aliases now come from the effective agent-scoped index, which
applies per-agent replacement instead of reading agents.defaults.models.
Unscoped status keeps reporting global defaults.

Refs #127585
2026-08-22 00:29:20 -07:00
Yuval Dinodia a0f3457f38 fix(agents): preserve existing files after failed host writes (#118470)
Preserve existing unrestricted host files when a write or edit fails partway through by updating regular files in place and restoring the original prefix and length.

Writable-but-unreadable regular files now fail before mutation.

Co-authored-by: yetval <yetvald@gmail.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-22 12:56:45 +05:30
Yuval Dinodia ff6db34233 fix(skills): preserve local changes during updates (#118190)
Prevent ClawHub skill updates from silently replacing locally modified files. Verify recorded install digests at the rollback boundary, restore on mismatch, and require explicit --force for unverifiable legacy installs.

Co-authored-by: yetval <yetvald@gmail.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-22 12:32:04 +05:30
Marvinthebored 2fdfd64a1a fix(channels): honor queued replies without receipts (#127667)
Restore compatibility-aware visible dispatch detection when legacy prepared runners return queued counters without a settled receipt. Preserve settled-receipt authority.

Co-authored-by: Marvinthebored <peter@lindsey.jp>
2026-08-22 12:08:52 +05:30
Peter Steinberger 32cf13dc90 fix(onboard): emit JSON on option-validation failures (#127794)
`openclaw onboard --json` wrote a JSON object to stdout on success, and the
top-level CLI handler emitted {"ok":false,"error":...} for thrown errors, but
every option-validation rejection printed to stderr and exited 1 with an empty
stdout. Automation parsing stdout got a valid object for some bad flags and a
parse error for others, with no way to tell a rejected flag from a crash.

Measured before this change, all with --json and all with empty stdout:
--mode, --flow, --daemon-runtime, --gateway-bind, --gateway-auth,
--gateway-token-ref-env, a missing --gateway-password, and --auth-choice.

Routes every onboarding option rejection through one shared primitive that
emits { ok: false, phase: "options", message } when --json is set, then reports
and exits exactly as before. 'options' joins the existing phase vocabulary
('daemon-install', 'gateway-health'). Every human message, stderr stream, and
exit code is unchanged; this only adds the stdout object that was missing.
2026-08-21 23:23:15 -07:00
Peter Steinberger 9d33b0b727 perf(gateway): defer session lookup caches (#127790)
Amp-Thread-ID: https://ampcode.com/threads/T-01a027b2-a0b6-73f9-a463-3c5f52891db9

Co-authored-by: Amp <amp@ampcode.com>
2026-08-21 23:02:52 -07:00
Peter Steinberger 500bb6031d fix(onboard): name the available providers when --import-from is unknown (#127780)
Onboarding answered an unknown --import-from with "Migration provider \"x\" is
not installed or bundled. Install it before starting the transactional import."
— no list of valid ids, no next command, and advice to go install something
that is usually just a typo.

Its siblings already do this correctly: resolveMigrationProvider appends
"Available providers: ..." and the migrate selection helper appends
"Available skills: ...". The missing --import-from case in onboarding itself
already points at `openclaw migrate list`. Only the unknown-id case dead-ended.

Collapses the two duplicated throws into one guard used by every path, which
also closes a gap: the no-back-navigation branch returned the picker result
without validating it at all.
2026-08-21 22:12:32 -07:00
Peter Steinberger 15569b210e perf(gateway): avoid single-chunk request body copy (#127783)
Amp-Thread-ID: https://ampcode.com/threads/T-01a021f5-984a-7628-a30c-491c166ff247

Co-authored-by: Amp <amp@ampcode.com>
2026-08-21 22:12:09 -07:00
Peter Steinberger ec39376967 fix(onboard): reject --gateway-bind custom without a valid customBindHost (#127779)
Non-interactive onboarding accepted `--gateway-bind custom` with no
`gateway.customBindHost`, wrote `bind: "custom"` to the config, and reported
success. The Gateway refuses that config outright:

  Gateway failed to start: gateway.bind=custom requires gateway.customBindHost.

`doctor` and `config validate` both call the written config healthy, so the
first sign of trouble is a Gateway that will not start.

Every other onboarding flag already rejects a mode whose companion value is
missing (--gateway-auth password, --auth-choice openai-api-key, --auth-choice
custom-api-key). This restores the same contract for bind, reusing the same
validateDottedDecimalIPv4Input the interactive wizard and `openclaw configure`
apply to this field. Pre-seeding gateway.customBindHost keeps working.
2026-08-21 21:59:01 -07:00
Jason O'Neal 89e2d43095 perf(sqlite): skip schema write lock on current state (#126825)
Allow healthy current-version state databases to open without taking SQLite writer locks while preserving schema repair, legacy cron migration, and external ownership fencing.

Co-authored-by: Jason O'Neal <jason.allen.oneal@gmail.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-22 10:19:20 +05:30
Peter Steinberger 6cb08cb8ee perf(media): skip unused local MIME sniff (#127772)
Amp-Thread-ID: https://ampcode.com/threads/T-01a021f5-984a-7628-a30c-491c166ff247

Co-authored-by: Amp <amp@ampcode.com>
2026-08-21 21:15:00 -07:00
Jason O'Neal ef18fbe566 fix(doctor): repair plugin host links before startup migration (#126830)
Repair managed OpenClaw host links under the startup migration lease before plugin state migrations, preventing retained Codex upgrades from repeating the same readiness failure.

Co-authored-by: Jason O'Neal <jason.allen.oneal@gmail.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-22 09:37:24 +05:30
ClawSweeper 8c1ea23826 fix: preserve internal reply images across gateway restart (#127729)
Co-authored-by: RoboClaw <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-21 21:03:58 -07:00
ClawSweeper 7329a8d3d7 fix: Code Mode shell calls stall near yield deadline (#127759)
* fix: prevent Code Mode shell waits from stalling

Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>

* fix: honor remaining Code Mode deadline

---------

Co-authored-by: RoboClaw <309084314+roboclaw-bot@users.noreply.github.com>
Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
2026-08-21 21:02:03 -07:00
Peter Steinberger cf5f4871e8 perf(tui): reuse prepared transcript render lines (#127767)
Amp-Thread-ID: https://ampcode.com/threads/T-01a02570-1023-77c8-9513-e43f179ee673

Co-authored-by: Amp <amp@ampcode.com>
2026-08-21 20:37:50 -07:00
Marvinthebored 575146a0a9 fix(cli): report exact Claude stream failures (#127169)
Preserve parser-owned bounded diagnostics in managed Claude live turns and share the canonical output-limit formatter.

Co-authored-by: Marvinthebored <peter@lindsey.jp>
2026-08-22 08:58:10 +05:30
Dallin Romney 0ac2606bc6 test(plugins): preserve configless manifest expectation (#127765) 2026-08-21 20:27:59 -07:00
Ayaan Zaidi fcc0bb7490 fix(agents): preserve opaque session key casing (#127279)
Use canonical session-key normalization during subagent reconciliation so case-distinct Matrix and Signal peers cannot be reported as completed. Preserve structural casing reconciliation and remove the O(N) fallback scan.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Co-authored-by: Alix-007 <li.long15@xydigit.com>
2026-08-22 08:11:41 +05:30
Peter Steinberger 877ab6b0b2 perf(gateway): bound health recent-session projection (#127744)
Amp-Thread-ID: https://ampcode.com/threads/T-01a02525-2a28-740a-b408-ee5382998a8c

Co-authored-by: Amp <amp@ampcode.com>
2026-08-21 19:04:24 -07:00
Peter Steinberger 62ccf026ca fix(config): match the config directory through symlinks when diagnosing permissions (#127734)
The permission diagnosis added in #127703 compared Node's reported errno
path against the configured directory as raw strings. Node reports the
canonical path, so a config directory reached through a symlink never
matched and the operator fell back to the raw EACCES the change existed to
replace. macOS /var -> /private/var makes this ordinary, not exotic.

Resolve the directory only when the raw comparison fails, so successful
config writes gain no syscall. The narrow path check stays: an unrelated
permission error from the caller's own mutation must keep propagating.
2026-08-21 18:57:18 -07:00
Peter Steinberger 611b3f1753 fix(agents): preserve authoritative child completion results (#127739)
* fix(agents): preserve authoritative child completion results

* fix(agents): decouple child completion result types
2026-08-21 18:47:47 -07:00
Peter Steinberger f048a4370d fix: deliver current-source media before acknowledging (#127737) 2026-08-21 18:47:26 -07:00
Josh Avant 67c06ed54f fix(memory): respect provenance in automatic context (#127469)
* fix: enforce memory provenance for automatic context

* fix: preserve memory provenance compatibility

* fix: repair legacy memory provenance before recall

* fix: fail closed for unsupported memory runtimes

* fix: record command memory exclusions

* docs: clarify memory provenance injection

* docs(memory): clarify bootstrap without a runtime
2026-08-21 18:44:40 -07:00
Peter Steinberger 6ced650a50 fix(cli): render models list JSON failures (#127726) 2026-08-21 18:25:51 -07:00
Peter Steinberger a90800474d fix(cron): restore failure alerts after clock rollback (#127731) 2026-08-21 18:18:23 -07:00
Dallin Romney fc425351d6 fix(plugins): restore shipped channel compatibility (#126003)
* fix(plugins): restore shipped channel compatibility

* fix(plugins): align shipped compatibility guardrails

* style(plugins): align legacy setup formatting

* fix(plugins): gate channel compat removal on readers

* fix(channels): scope legacy allowlist updates

* chore(plugins): align rebased SDK budgets
2026-08-21 18:17:04 -07:00
Peter Steinberger 0d29baae01 fix(cli): render status timeout JSON failures (#127721) 2026-08-21 18:04:25 -07:00
Peter Steinberger 992948356f fix(secrets): fail closed for configured references (#127669)
* fix(secrets): fail closed for configured references

Keep explicit SecretRefs authoritative across Gateway auth, onboarding, probes, channel credentials, outbound sends, and GitHub Copilot discovery/embeddings. Ambient credentials remain available only when no reference owns the surface.

* test(secrets): align gateway fallback expectations

* test(providers): remove load-sensitive stream timing

* test(xai): normalize stream capture failures

* fix(copilot): preserve direct auth precedence

* test(ollama): keep progressing streams alive

* test(models): mark resolved config fixtures

* fix(models): drop stale probe import
2026-08-21 18:00:28 -07:00
Peter Steinberger f6b42ea223 test: trim duplicate core assertions (#127708) 2026-08-21 17:47:23 -07:00
Peter Steinberger 9c85319792 fix(agents): stop recommending unavailable session tools (#127648)
* fix(agents): stop recommending unavailable session tools

* fix(agents): preserve authorized tool guidance placement

* fix(agents): keep spawn recovery guidance executable
2026-08-21 17:47:17 -07:00
Vito Cappello eee905934a fix(state): keep divergent retired device identity from blocking gateway readiness (#126748)
* fix(state): keep divergent retired device identity from blocking gateway readiness

Classify a divergent retired identity file as a startup notice when the canonical SQLite identity remains valid and authoritative under #120610. Preserve the fatal warning for missing or invalid canonical state, so the readiness gate itself remains unchanged.

Fixes #117270.

Release note: prevents gateway crash loops caused by a recreated retired device identity JSON after a verified SQLite migration.

* fix(state): preserve device identity migration receipt integrity

Reuse canonical SQLite identity validation and retain incomplete receipts for preserved divergent claims.

Co-authored-by: Vito Cappello <3279061+VACInc@users.noreply.github.com>

---------

Co-authored-by: VACInc <3279061+VACInc@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-21 17:47:11 -07:00
Peter Steinberger 89bb601b91 fix(qa): replace Linux-only /proc probes in the Telegram proof harness (#127718)
The Mantis lane and the Telegram user driver reached for /proc to enforce
media containment and to prove a pid was still alive. Off Linux those paths
do not exist, so the containment check compared a literal "/proc/self/fd/N"
string against the output root and refused every file, while both liveness
probes silently reported "gone": the lane stole a held harness lock and the
driver deleted the observer pid file without ever signalling the process.

Containment now uses each runtime's strongest portable construct. Python
descends the media path one component at a time from an open directory
descriptor (O_DIRECTORY|O_NOFOLLOW, dir_fd), which is race-free and also
covers the intermediate directories the old leaf-only O_NOFOLLOW did not.
Node has no openat(2), so publicRelativePath re-walks the resolved
components after the descriptor is open and refuses any that became a
symlink. Liveness moves to process.kill(pid, 0) and to ps state/args, which
keeps the pid-reuse and zombie handling the /proc reads provided.

The compound-command approval binding test asserted an operand count that
only held where the host interpreters live in a root-owned prefix; a
Homebrew python3 is writable, so it correctly binds as a third executable
operand. Assert the script operands by path instead.
2026-08-21 17:45:12 -07:00
Peter Steinberger 0a704810e4 fix(onboard): preserve named-agent profiles during config resets (#127716) 2026-08-21 17:42:40 -07:00
Peter Steinberger 99e337190e fix(daemon): preserve the Gateway when stopping or restarting a Mac node (#127707)
* fix(daemon): skip gateway port-release assertion when stopping a node-host LaunchAgent

The generic LaunchAgent stop path (assertGatewayPortReleasedAfterStop)
resolves a gateway-relative port from the stopped service's own
program arguments/env and then asserts that port is no longer bound
after stop. `resolveNodeService()` reuses this exact same stop path
for the node-host service, but a node-host's "--port" argument is the
Gateway port it connects to *outward as a client* -- it never binds
that port itself.

On a host where the node-host and Gateway are co-located (a common,
documented topology), this made `openclaw node stop`/`restart` fail
with a false-positive "gateway port NNNN is still busy after
LaunchAgent stop" error, because the assertion was inadvertently
checking whether the co-located Gateway's own (legitimately still
open) port had been released -- something the node-host stop has no
business asserting.

Skip the port-release assertion entirely when OPENCLAW_SERVICE_KIND
indicates the node-host service, using the existing service-kind
env marker already used elsewhere in the daemon lifecycle code for
this exact gateway-vs-node distinction.

Fixes openclaw/openclaw#124296 (secondary bug: node stop/restart
false-positive port-collision guard when co-located with Gateway).

* fix(daemon): apply node-vs-gateway port guard exemption to LaunchAgent restart

The stop path (assertGatewayPortReleasedAfterStop in launchd-stop.ts) was
previously patched to skip the gateway-port-released check when
OPENCLAW_SERVICE_KIND is "node", but restartLaunchAgent in
launchd-lifecycle.ts had its own independent busy-port ownership guard
that was not touched, so `openclaw node restart` still failed for
co-located node-host + Gateway topologies.

Extract the OPENCLAW_SERVICE_KIND-based decision into a single shared
helper, shouldSkipGatewayPortOwnershipCheck (new module
launchd-node-gateway-guard.ts), and use it from both the stop and
restart guards so the two lifecycle paths can no longer diverge.

Add a regression test for co-located restart succeeding, mirroring the
existing stop regression test.

Refs openclaw/openclaw#124296

* test(daemon): add real-launchctl proof for co-located node/gateway port guard (#124296)

Adds an e2e integration test that drives real launchctl (no mocked
launchctl or port-inspection calls): a real "gateway" LaunchAgent that
genuinely binds a scratch TCP port, and a real "node-host" LaunchAgent
(OPENCLAW_SERVICE_KIND=node) targeting that same port. Verifies both
stopLaunchAgent and restartLaunchAgent succeed against the real busy
co-located port, and that the co-located Gateway LaunchAgent stays
running throughout.

Run against the pre-fix commit (2a7d735), this reproduces the exact
reported false-positive:

  Error: gateway port <port> is busy but is not verifiably owned by
  LaunchAgent ai.openclaw.launchd-int-node-<id>

Run against this branch, all 8 launchd integration tests pass.

* test(daemon): probe real port busy-ness before exercising node lifecycle

Addresses ClawSweeper P2 ask: the real-launchctl e2e proof only relied on
the LaunchAgent runtime status reporting 'running' for the gateway, which
doesn't guarantee the listener has actually bound the scratch TCP port yet
(status vs. socket bind can race). Add explicit probePortUsage() calls
(the same real, unmocked TCP probe used in production code) immediately
before both the stop and restart assertions, so the test proves the port
is genuinely busy at the moment the guard is exercised, not just inferred
from launchd state.

* refactor(daemon): resolve gateway ownership at launchd context boundary

Co-authored-by: Anis Khan <2815766+aniskhan001@users.noreply.github.com>

---------

Co-authored-by: Anis Khan <2815766+aniskhan001@users.noreply.github.com>
2026-08-21 17:38:44 -07:00
Peter Steinberger 980e67e2ae fix(sessions): retain assigned owners during doctor and startup repairs (#127706) 2026-08-21 17:33:49 -07:00
Peter Steinberger 7e84b2c722 fix(config): diagnose an unwritable config directory instead of leaking EACCES (#127703)
Every config write failed with a bare Node errno naming openclaw.json.lock
when the config directory was not writable. The lock file is an internal
artifact; the directory permissions are the actual problem, so operators
were sent to investigate the wrong thing.

withConfigMutationLock is the choke point for all config writes, so the
diagnosis lands there once and every writer benefits. Only EACCES, EPERM,
and EROFS whose reported path sits directly in the config directory are
relabelled; the caller's mutation runs inside the same scope, so an
unrelated permission error from its own work must keep propagating.
2026-08-21 17:32:51 -07:00
Peter Steinberger 12f72dac81 fix(gateway): fail streaming responses when agent runs fail (#127662)
* fix(gateway): fail streaming responses when agent runs fail

* test(gateway): preserve typed streaming failure fixtures
2026-08-21 17:28:01 -07:00
Peter Steinberger 5f6de37e20 fix(secrets): preserve authored env shorthand provenance (#127685)
* fix(secrets): preserve authored env shorthand provenance

Fixes #127684

* fix(gateway): keep provenance scope narrow

* fix(gateway): carry provenance through auth reads
2026-08-21 17:25:46 -07:00
Peter Steinberger 66c0a23a06 fix(onboard): stop printing two contradictory Fix lines for one failure (#127698)
Non-interactive onboarding's gateway-health failure emitted the generic
classification hint and the caller's phase-specific hint together, so the
first and most prominent Fix told operators to restart a managed gateway
while the next line said no daemon had been installed.

logNonInteractiveOnboardingFailure now suppresses the classification hint
when the caller already supplied one; the caller knows whether
--install-daemon was requested and the classifier does not. Callers that
pass no hints are unchanged.
2026-08-21 17:13:02 -07:00
Peter Steinberger 9acf1180a6 fix(talk): keep unused speech secrets from blocking startup (#127687)
* fix(talk): isolate selected speech secrets

* test(talk): use provider-scoped secret fixtures
2026-08-21 17:03:23 -07:00
Peter Steinberger 89e38cfef9 fix(tui): stop local shell descendants on exit (#127652)
* fix(tui): own local shell process scopes

Keep local shell root results independent from authoritative process-scope
extinction, and make TUI shutdown cancel and join its ProcessSupervisor scope.
On Windows, create commands atomically inside a retained non-breakaway Job
Object and prove extinction from exact Job accounting.

Fixes #127488

* fix(process): preserve optional extinction waits
2026-08-21 17:01:09 -07:00
Peter Steinberger 7469b54272 fix(cli): render plugin search JSON failures (#127679) 2026-08-21 16:35:17 -07:00
Peter Steinberger 79c8251245 perf(onboarding): avoid broad provider-auth runtime imports (#127680) 2026-08-21 16:26:26 -07:00
Peter Steinberger be0bcb395d fix(gateway): clear stale credential warnings after channel plugin removal (#127503)
* fix(gateway): retire removed channel credential owners

Retire stale account diagnostics only after successful channel plugin removal. Preserve independently owned credential diagnostics across snapshot replacement and rollback.

* test(gateway): cover plugin-disable owner pruning
2026-08-21 15:58:48 -07:00
Peter Steinberger 11081a95b8 test: remove final moving-tip duplicates (#126990) 2026-08-21 15:54:24 -07:00