Commit Graph

331 Commits

Author SHA1 Message Date
Peter Steinberger 76b6496f34 perf(gateway): defer non-startup runtime imports (#121780)
Amp-Thread-ID: https://ampcode.com/threads/T-019fec00-3abd-710b-b9de-8925c23ad779

Co-authored-by: Amp <amp@ampcode.com>
2026-08-10 19:17:40 -07:00
Peter Steinberger 1ced7441eb refactor(agents): move announce, completion, recovery, and registry families into concept directories (#121553)
* refactor(agents): move announce family into subagents/announce/

* refactor(agents): move completion and main-session recovery families

* refactor(agents): move registry family into subagents/registry

* refactor(agents): update registry state type imports

* style(agents): format moved subagent imports

* test(agents): preserve isolated registry test routing

* fix(scripts): recognize relocated subagent announce seams
2026-08-10 16:06:39 -07:00
Peter Steinberger 08507909ed fix(cli): guard remaining embedded state writers (#121282) 2026-08-09 17:09:35 -07:00
Peter Steinberger ce53f7e82e refactor(agents)!: remove the session write lease (#121113)
* refactor(agents): remove session write lease

* refactor(plugin-sdk): deprecate session write lease

* refactor(doctor): remove session lock checks

* test(agents): remove session lease fixtures

* test(agents): align writer rebound assertion

* refactor(infra): remove retired session lock exports

* test(tooling): preserve embedded abort race shard
2026-08-09 15:30:48 -07:00
Peter Steinberger 8ede4046e2 fix(cli): guard embedded agent state ownership (#120896) 2026-08-08 23:59:50 -07:00
Peter Steinberger da4a656cdb improve: doctor migration checks no longer load every bundled plugin runtime (#120678)
* perf(plugins): declare doctor contract surfaces

* perf(doctor): slim migration import closures

* perf(plugins): narrow doctor declaration record surface and wire owner-test lane

Registry records carry only the doctorContract declaration instead of the whole
parsed manifest, and check:changed now selects the src/plugins-owned declaration
honesty and closure-guard tests for extension module/manifest changes so
cross-lane drift cannot pass PR classification.

* fix(doctor): keep control-plane dist imports require-safe

Keep doctor and channel control-plane chunks off exec-class dependencies, and enforce native require(esm) loading during postbuild.

* chore(plugin-sdk): regenerate API baseline

* chore(plugin-sdk): sync export ordering

* fix(plugins): satisfy doctor contract CI boundaries

* perf(doctor): make qqbot doctor closure dependency-light

qqbot was the last plugin above 5s in doctor state-migration enumeration
(~8s under tsx/jiti). The cost was not the state-key builder (already a
leaf): its doctor closure value-imported the runtime-doctor SDK barrel,
whose plugin-state-store/state-db re-exports pull kysely (~330 modules),
plus security-runtime for one fileExists (~200 modules), all resolved
per-module by jiti during enumeration.

Split the migration-define helpers and light re-exports into a new
private-local plugin-sdk/runtime-doctor-migrations subpath; runtime-doctor
re-exports it so its public surface is byte-identical (API baseline hash
unchanged). qqbot's doctor-contract and state-migrations now import only
the light subpath, swapping fileExists for the equivalent async
legacyStateFileExists already in the closure.

qqbot enumeration: ~8.0s/531 modules -> ~0.25s/18 modules.

* chore(plugin-sdk): drop private-local subpath from API baseline

runtime-doctor-migrations is private-local-only; the baseline tracks public
modules, and the earlier line was generated before the classification.

* fix(plugins): register runtime-doctor-migrations boundary paths

The private-local subpath list feeds the extension package boundary map;
the shared paths config and xai's derived overrides must carry the same
entry or the boundary contract test fails.
2026-08-08 13:29:18 -07:00
Sally O'Malley 4cbbfc2159 fix(systemd): apply dotenv changes on gateway restart (#119441)
* fix(systemd): load state dotenv at gateway startup

Signed-off-by: sallyom <somalley@redhat.com>

* fix(systemd): refresh managed dotenv values on restart

Signed-off-by: sallyom <somalley@redhat.com>

* fix(gateway): clear removed managed dotenv values

Signed-off-by: sallyom <somalley@redhat.com>

* fix(dotenv): canonicalize managed override keys

Signed-off-by: sallyom <somalley@redhat.com>

* fix(secrets): preserve providerless env refs

Signed-off-by: sallyom <somalley@redhat.com>

---------

Signed-off-by: sallyom <somalley@redhat.com>
2026-08-07 10:04:04 -04:00
Peter Steinberger 9aa24e8f2e refactor(test): replace logic-bearing vi.mock factories with boundary fakes, batch 2 (#120117)
* refactor(test): exercise real Teams monitor lifecycle

* refactor(test): use real gateway lifecycle state

* refactor(test): use real restart delivery queue

* test: type Teams SDK boundary fixture
2026-08-06 22:02:50 -07:00
Serghei 9c61076f66 perf(gateway): keep reload and recovery machinery off gateway cold start (#119094)
The run loop primes cli/gateway-cli/lifecycle.runtime.ts before installing
signal handlers, so every symbol that hub re-exports loads before the HTTP
listener binds. Two of those re-exports resolved through forwarding modules:
gateway/server-reload-handlers.ts also re-exports the hot-reload and managed
reloader modules, and agents/main-session-restart-recovery.ts also re-exports
its -runtime sibling. ESM evaluates the whole forwarding module, so priming the
hub pulled both graphs onto the cold-start path.

Re-export each symbol from the module that defines it instead.
abortPendingChannelReloads lives in gateway/server-reload-contracts.ts, whose
own imports are all type-only, and markRestartAbortedMainSessions lives in
agents/main-session-restart-recovery-marking.ts. Both retargets resolve to the
same module instances, so shared reload-generation state and behavior are
unchanged, and the eager priming that protects signal handlers from dist chunk
rotation stays in place.

Add a CLI-lane import-boundary test so these re-exports cannot be routed back
through a forwarding module, and point the two run-loop test mocks at the
defining modules.
2026-08-04 19:37:05 -07:00
Peter Steinberger 25e3b6c9b7 fix(cli): honor local port in gateway call (#119046) 2026-08-03 18:50:21 -07:00
Peter Steinberger 9c22a4349d chore: speed up gateway CLI test startup (#118620)
* test(cli): avoid unrelated startup graphs

* test(cli): preserve startup coverage

---------

Co-authored-by: Peter Steinberger <steipete@mac-studio-sf2.local>
2026-08-03 03:17:25 -07:00
Peter Steinberger 357087b9c7 refactor(gateway): lifecycle-owned ambient state via factory-registered drains (#118271)
* refactor(runtime): own ambient state lifecycle cleanup

* chore(plugin-sdk): refresh additive API baseline

* fix(runtime): restore agent wait listener lifecycle

Keep agent-job data process-shared while preserving module-local event subscription ownership. Ratchet the public wildcard budget down after replacing text-runtime's wildcard singleton export with named exports.
2026-08-02 18:06:46 -07:00
Peter Steinberger ec7e5dd769 fix(gateway): recover channel autostart after crash loops (#118311)
* fix(gateway): recover channel autostart after crash loops

* docs(gateway): clarify crash-loop recovery steps

* test(gateway): type crash-loop recovery context

* test(gateway): correct recovery mock context

* test(gateway): keep recovery monitor coverage compact

* chore: leave crash-loop note to release flow

---------

Co-authored-by: Peter Steinberger <steipete@mac-studio-sf2.local>
2026-08-02 18:01:39 -07:00
Peter Steinberger 184c7a00fa fix(gateway): restore fresh dev bootstrap (#118003) 2026-08-02 11:16:33 -07:00
Peter Steinberger ad53d4419e fix: make gateway token recovery usable from macOS onboarding (#118051)
* fix(gateway): add safe token recovery

Co-authored-by: 宇宙熊Yzx <53250620+849261680@users.noreply.github.com>

* chore: move gateway release note to PR

* test(cli): classify gateway token output

---------

Co-authored-by: 宇宙熊Yzx <53250620+849261680@users.noreply.github.com>
2026-08-02 10:06:50 -07:00
Peter Steinberger 4d14f298b0 perf(cli): keep read-only commands on cold paths (#117932)
* perf(cli): keep read-only commands on cold paths

* perf(channels): reuse list metadata snapshot
2026-08-02 02:54:40 -07:00
Peter Steinberger 3856b4fa1b refactor(cli): unify gateway RPC transport (#117601) 2026-08-01 14:06:30 -07:00
Peter Steinberger 433bb3f954 fix(cli): make commands, completion, and JSON output reliable (#116033)
* fix(cli): make commands, completion, and JSON output reliable

* fix(cli): reconcile completion coverage with current main

* test(cli): keep test routing stable across isolation lanes
2026-07-31 16:20:34 -07:00
Peter Lee daff73b7ad fix(gateway): prevent crash loops from state DB schema migration errors (#116733) 2026-08-01 03:34:49 +08:00
Peter Steinberger d81465f779 fix(cli): keep Gateway auth failures parseable in JSON (#116597)
* fix(cli): preserve gateway auth JSON errors

* docs(changelog): note Gateway JSON auth errors

* docs(changelog): defer Gateway JSON note to release
2026-07-30 17:48:36 -07:00
北京刻熵科技有限责任公司 9308557f72 fix(daemon): detect and resolve dueling user+system systemd gateway units
After upgrading on Linux, a user-scope unit
(~/.config/systemd/user/openclaw-gateway.service) and a system-scope unit
(/etc/systemd/system/openclaw-gateway.service) can both exist and both try to
manage the gateway. They bind the same port and each instance's stale-process
detection SIGTERMs the other, producing an endless restart cascade (#79375).

The detector `findInstalledSystemdGatewayScope` checked the user path first and
returned early, so it could never surface that both scopes coexist.

This adds, in three layers:

1. Detection: `findSystemdGatewayInstallation` — a discriminated union
   (none/user/system/dueling) that reports every installed scope without
   early-returning. `findInstalledSystemdGatewayScope` is refactored to delegate
   to it while preserving the exact user-first preference its four lifecycle
   callers rely on (no behavior change for stop/restart/is-enabled/runtime).
   Adds `uninstallUserSystemdGatewayUnit` (removes only the $HOME user unit, no
   root needed) and the pure `formatDuelingScopesWarning` helper.

2. Doctor: `maybeResolveDuelingSystemdGatewayScopes` detects the dueling state
   and, after the existing confirm/policy gate, removes the redundant user-scope
   unit while keeping the root-installed system unit authoritative. Declining or
   an externally-managed policy falls back to the existing cleanup hints.

3. Startup guard: in service mode, when a stale-kill actually happened, log a
   targeted remediation pointing at `openclaw doctor --fix` instead of letting
   the loop look like routine stale cleanup. Diagnostic only — the kill decision
   is unchanged.
2026-07-30 08:52:05 +09:00
Peter Steinberger d16e33e08e fix: restore trustworthy end-to-end QA and cross-channel delivery (#115404)
* fix(qa): repair verified end-to-end and channel regressions

* fix(gateway): make interrupted restart recovery lifecycle-safe

* test(heartbeat): target the canonical recovery session store

* fix(gateway): prioritize durable restart recovery before heartbeat

* fix(qa): preserve safe restart recovery and channel expiry

* fix(qa): fail closed and fence restart recovery

* test(agents): isolate restart recovery timing

* test(agents): prove actual restart retry timing

* fix(qa): report incompatible profile scenarios

* fix(scripts): resolve symlinked docker scheduler entrypoints

* fix(qa): require fresh native test evidence

* fix(heartbeat): fence active restart recovery delivery

* fix(gateway): consume untargeted restart acknowledgements

* fix(qa): satisfy exhaustive hosted validation gates

* fix(agents): fence stopped restart recovery dispatch

* style(agents): format restart recovery lifecycle regression

* test(gateway): isolate context prewarm sidecar lifecycle

* test(qa): make scenario process timeout cleanup deterministic

* fix(qa): stamp synthetic gateway configs with current version

* fix(openai): preserve vision capabilities in stale model catalogs

* test(qa): align profile channel rejection with current main

* fix(openai): forward supported moderation for image edits

* fix: restore latest-main CI and image edit documentation

* fix(qa): retain relocated code-mode evidence validation

* fix(openai): expose GPT-5.4 vision in static catalog

* fix(pricing): honor explicit model cost overrides

* test(pricing): keep isolated provider regressions deterministic

* fix(openai): inherit transport for discovered static models

* fix(gateway): honor agent-owned static image capabilities

* test(gateway): preserve prepared-snapshot attachment races

* test(gateway): isolate subagent persistence failure injection

* test(gateway): exercise concurrent voice replay admission

* fix(gateway): restore stale model image capabilities

* fix(agents): publish configured model vision capabilities

* fix(agents): isolate detached media transcript ownership

* test(agents): preserve generic transcript lock regression

* fix(gateway): require proven static model route identity

* fix(qa): accept bounded full-size generated image attachments

* fix(qa): require fresh script producer evidence

* test(qa): prove native E2E scenario execution
2026-07-29 12:45:27 -04:00
Vincent Koc f6e874835c fix(gateway): stop managed crash loops for pending agent DB migrations (#115631)
* fix(gateway): recover managed services after agent DB migration

* test(infra): use managed temp directory cleanup
2026-07-29 19:21:46 +08:00
wm0018 a1d31204f1 fix(gateway): drain root work continuations before direct-stop process exit (#105848)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-29 14:28:14 +08:00
Peter Steinberger ba8bccb750 fix(cli): gateway health JSON connects before loading presentation code (#115244)
* fix(gateway): start one-shot CLI clients immediately

Closes #115243

* fix(cli): defer gateway health presentation imports

* style(cli): format gateway health loader

* test(cli): match gateway health call shape

* perf(cli): route gateway health JSON before Commander

* fix(cli): preserve routed health output contract

* test(cli): match routed JSON indentation

* fix(cli): guard routed health config resolution

* perf(cli): avoid duplicate health config bootstrap
2026-07-28 14:19:18 -04:00
Peter Steinberger 1cbbda18c3 perf(logging): flush file transport asynchronously off the request path (#114769)
* perf(logging): flush file transport asynchronously off the request path

* fix(ci): record flushLogger surface growth and drain before log reads

- +1 public export/callable budget with annotation: logger file-transport
  flush for graceful shutdown drains; regenerate SDK API baseline
- three tests that read the log file right after logging now drain the
  async file transport first (flushLogger) instead of racing the flusher

* test: drain async log transport before reading talk and request-trace logs

* test: drain async log transport in CLI logger file tests

* test: drop unused withTempDirSync import
2026-07-27 19:40:43 -04:00
Peter Steinberger a1a507ddaf fix: gateway boot recovers from a dead-owner migration lease and names its breaker escape hatch (#114718)
* fix(gateway): reclaim startup migration leases whose owner process is gone

* fix(gateway): name a runnable command in crash-loop breaker suppression messages

* fix(gateway): scope the breaker recovery hint to the suppressed account
2026-07-27 16:11:00 -04:00
Peter Steinberger c1dab6a5e0 test: speed up measured CI hotspots (#114367)
* test: remove redundant followup goal integration

* test: cover CLI help table in process

* test: inject usage cost settle timing

* test: reuse rendered plugin SDK baseline

* test: reuse bundled provider registrations

* test: isolate startup quarantine mapping

* test: inject session patch auth metadata

* test: avoid CLI help catch shadowing

* test(cli): keep gateway dependencies internal

* test(auto-reply): stabilize cold followup setup
2026-07-27 06:25:56 -04:00
Peter Steinberger 269bc5c89e fix(cli): preserve machine-readable stdout (#113654)
Co-authored-by: 1052326311 <65798732+1052326311@users.noreply.github.com>
2026-07-27 05:44:16 -04:00
Peter Steinberger 669db2968f refactor(agents): retire TOOLS.md into an AGENTS.md section with a doctor migration (#113966)
* fix(agents): stop retired attestation hashes from faking a vanished workspace

* feat(agents): migrate TOOLS.md content into the AGENTS.md tools section

* refactor(agents): drop TOOLS.md from the workspace bootstrap set

* refactor(policy): read tool policy entries from AGENTS.md

* docs: describe local tool notes as an AGENTS.md section

* test(codex): drop TOOLS.md developer-instruction coverage with the removed path

* test(agents): cover the TOOLS.md doctor migration behaviors

* refactor(doctor): satisfy TOOLS.md migration lint

* test(agents): split workspace attestation survival coverage

* refactor(codex): simplify workspace context basenames

* refactor(doctor): keep TOOLS.md migration helpers module-local

* docs: regenerate docs map

* fix(doctor): keep migration claims fresh

* fix(doctor): preserve nested tool notes

* refactor(doctor): split tools migration helpers

* refactor(doctor): limit TOOLS.md migration to workspace root

* style(doctor): keep migration under line limit

* fix(doctor): recover interrupted AGENTS publish

* docs(hooks): describe root-only TOOLS.md migration accurately

* fix(doctor): preserve migrated tool guidance visibility

* test(agents): regenerate prompt snapshots after rebase

* fix(policy): block evaluation while TOOLS.md is unmigrated

* fix(doctor): check merged bootstrap budget per agent

* fix(doctor): keep budget helper internal

* refactor(doctor): keep migration budget helpers to their consumers
2026-07-27 03:56:30 -04:00
Peter Steinberger e069b9dbdd fix(ui): repair settings titles, scope form-unsafe banner, consolidate advanced settings (#113160)
* feat(ui): repair settings titles, scope form-unsafe banner, consolidate advanced tier

- Advanced/Notifications settings headers rendered raw i18n keys
  (tabs.advanced/tabs.notifications); configPageTitle now resolves through
  the nav registry titleForRoute, deleting the drifting key map.
- Form-unsafe banner is value-aware (only when the user's config has a value
  at a form-unsupported path in the active scope), names the paths, offers an
  Open Raw editor action, and sits in the content column instead of full-bleed.
- Advanced tier collapses to one synced toggle: hidden advanced fields render
  a ghost row that enables the toggle; the Advanced page always reveals and
  hides the toggle; per-section details state and the controlled-open dance
  are deleted; toggle visibility mirrors the renderer's tier split.
- schema.tags no longer tags facet-less paths as "advanced" (common fields
  like update.channel wore a lying chip); the chip is no longer rendered in
  form rows while tag:advanced search keeps working.

* fix(config): dev bootstrap writes canonical agents.entries; finish stale agents.list sweep

- openclaw gateway run --dev failed on a fresh state dir: the bootstrap wrote
  the retired agents.list array and validation rejected its own config
  (Unrecognized key: list). It now writes the keyed agents.entries record;
  new dev.test.ts validates the written shape against the zod schema.
- Revive the dead gateway hot-reload rule: prefix agents.list never matched
  canonical config diffs, so per-agent heartbeat edits fell through to the
  agents:none tail rule; the rule now matches agents.entries.
- config set replacement protection moves from the dead agents.list array
  to the agents.entries map (joins plugins.entries/auth.profiles family).
- Sweep remaining stale agents.list config-path strings in hints, fix-it
  messages, elevated gates, doctor hints, audit text, and type docs to
  agents.entries.*; RPC ids, the internal list projection, and doctor
  legacy-migration references intentionally keep the old name.

* test(ui): derive nav i18n audit from route registry; fix stale bootstrap hint assertion

- Knip flagged navigationCopyEntries (test-only export); the audit now walks
  ALL_ROUTES through prod titleForRoute/subtitleForRoute and rejects raw
  dotted-key output, so no export exists solely for the test.
- commands-context-report expected the retired agents.list[] hint wording.

* fix(config): align rebase resolution with main's roster-aware diagnostic

- tool-policy-diagnostic: keep main's test expectations (dotted
  agents.entries.<id> paths from the roster-aware implementation); our
  pre-rebase bracket-style assertions no longer match any code.
- Reapply the placeholder sweep the conflict resolution dropped:
  generic agents.list[] fallbacks in tool-policy-diagnostic and the audit
  sandbox-mode hint now say agents.entries.*.
2026-07-25 00:51:57 -07:00
RickLin 328fa5b964 fix(gateway): actively relaunch launchd restarts (#104637)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-21 08:58:18 -07:00
Peter Steinberger 6f43c50f37 fix(cli): preserve failure exit semantics (#112210) 2026-07-21 01:48:25 -07:00
Peter Steinberger 4ff2cdd215 fix(sessions): reconcile client-abandoned running sessions on restart (#111699) 2026-07-20 19:39:41 -07:00
Peter Steinberger b61619e03c fix(gateway): dev mode suppresses ambient channel env auto-configuration (#112011)
Channel plugins declare package env triggers in manifests, and ambient
environment variables count as "channel configured". A dev gateway
(gateway run --dev) inherits the operator's shell, so real channel
credentials silently configured channels and could connect development
instances to live services (observed with reef and a Telegram-range
connection during isolated stress testing).

Dev mode now drops presence signals whose only sources are env or
manifest-env across activation planning, auto-enable, autostart, health
recovery, readiness, reloads, and startup warnings. Explicit
channels.<id> config still works, --dev-ambient-channels restores the
old behavior, and startup logs the suppressed channel ids once. Non-dev
gateways are unchanged.
2026-07-20 19:28:07 -07:00
Peter Steinberger 783a5d21cf refactor(config): purge numeric tuning knobs behind built-in defaults (#111382) 2026-07-19 07:35:45 -07:00
Peter Steinberger 9d97e10efe refactor: move non-session runtime journals to SQLite (#109427)
* refactor: migrate runtime JSONL state to SQLite

* chore: refresh SQLite migration validation

* fix: preserve safe audit migration order

* fix: sanitize retired audit archives safely

* fix: satisfy SQLite storage architecture gates

* fix: serialize plugin doctor state migrations

* fix: bound memory event SQLite projections

* fix: rotate memory event projections safely

* fix: preserve imported event retention age

* fix: harden memory event export projection

* fix: canonicalize memory event migrations

* fix: harden legacy journal migrations

* fix: report unsafe legacy journal paths

* fix: preserve journal ownership during migration

* fix: keep legacy file imports doctor-only

* fix: align SQLite audit CI coverage

* refactor: keep state helpers module-private

* fix: harden legacy state migration recovery

* fix: reconcile SQLite state audit migration

* test: use neutral audit redaction fixtures

* fix: close SQLite migration recovery gaps

* refactor: split audit migration recovery helpers

* test: prove completed audit pads stay out of backups

* fix: preserve audit record ordinals across blank lines

* fix: align SQLite audit CI contracts

* build: package SQLite audit E2E entries

* fix: preserve audit ordinals in backup snapshots
2026-07-18 11:42:14 +01:00
Peter Steinberger fab3cdf1d8 refactor(gateway): simplify lifecycle mutation contracts (#110583)
* refactor(gateway): simplify lifecycle mutation contracts

* refactor(cli): unexport internal emitDaemonActionMessage
2026-07-18 11:09:21 +01:00
Peter Steinberger 1710313596 fix(cli): offer config repair after invalid startup (#110533)
* fix(cli): offer doctor recovery for invalid config

* fix(cli): preserve invalid config overrides

* fix(cli): keep invalid config recovery controlled
2026-07-18 09:30:27 +01:00
Peter Steinberger 8992f92dab feat(gateway): idempotent start, non-interactive stop guard, unified lifecycle audit (#110323)
* feat: guard gateway lifecycle mutations

* fix: harden gateway lifecycle mutation guards

* fix: harden gateway lifecycle audit safety

* fix(cli): repair gateway lifecycle checks

* fix(cli): satisfy lifecycle CI gates
2026-07-18 07:01:38 +01:00
Peter Steinberger b440cfb15e fix(gateway): occupied non-gateway ports exit nonzero (#110310)
* fix(gateway): reject unrelated occupied ports

* docs(changelog): defer gateway collision note

* test(gateway): isolate unconfirmed lock collision
2026-07-18 03:23:52 +01:00
qingminlong d6a561e92b fix(gateway): clarify malformed call params errors (#105217)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-16 18:24:08 -07:00
Shakker 333c4f9a61 feat: add restart handoff consume protocol 2026-07-16 17:01:39 +01:00
Shakker e123606b46 feat: support externally supervised gateway restarts 2026-07-16 17:01:39 +01:00
Peter Steinberger 9497450511 refactor: eliminate dead-export baseline (#108376)
Burn the grandfathered unused-export baseline to zero and enforce a hard-zero Knip gate.
2026-07-15 17:05:07 +01:00
Peter Steinberger 65188a7430 fix(agents): reject synchronous sessions_send self-targets, break down missing-cost entries, dedup usage merges (#108354)
* fix(agents): reject synchronous sessions_send self-targets, break down missing-cost entries, dedup usage merges

- sessions_send fails fast with a clear error when a synchronous send resolves
  to the calling session's own key, instead of enqueueing behind the sender's
  own lane until timeout and falling back to an empty reply; fire-and-forget
  (timeoutSeconds: 0) self-delivery keeps working (#107172)
- missingCostEntries now carries a provider/model breakdown surfaced on the
  status runtime line and gateway CLI cost line, so zero-priced usage (e.g.
  openai-codex/*) is attributable instead of an opaque counter (#98348)
- codex-synthetic-usage merge helpers share one precedence helper; behavior
  unchanged, duplicate ranking logic deleted (#107497)

* test: use a valid billing type in synthetic-usage precedence case

* test: fix billing expectation to match valid type
2026-07-15 08:23:49 -07:00
Peter Steinberger a97bdfb687 feat(agents): narrow gateway tool to read-only config
Phase 3 of #107237. Removes config.apply/config.patch/restart from the
regular-agent gateway tool (mirroring Phase 2's update.run removal); only
config.get and config.schema.lookup reads remain. Persistent config changes
and restarts now go exclusively through the human-approved openclaw
delegation path — closing the last unmediated agent config-write surface.
gateway stays owner-only/control-plane gated (config reads expose secrets and
host topology). Legacy setups can re-enable writes via the existing per-agent
tool allowlist; no new config key. Net -2271 LOC.

Refs #107237
2026-07-15 01:56:31 -07:00
Peter Steinberger f81f9d8570 chore: enforce max-lines suppression ratchet (#107315)
* ci: enforce max-lines suppression ratchet

* chore: prune stale max-lines suppression

* fix: close max-lines ratchet enforcement gaps

* fix: harden max-lines ratchet checks

* fix(ci): satisfy max-lines ratchet checks

* style: format max-lines declarations

* fix(ci): match oxlint suppression grammar

* test: isolate max-lines git fixtures

* chore: prune resolved max-lines debt

* test: skip newline path fixture on Windows

* fix: harden max-lines suppression ratchet

* chore: refresh max-lines baseline

* fix: close max-lines ratchet bypasses

* fix: derive ratchet base from PR merge tree

* fix: support older Git in staged ratchet

* fix: align max-lines declarations and baseline

* chore: refresh max-lines baseline for current main

* fix: exclude generated wizard locales from max-lines

* chore: prune resolved max-lines debt
2026-07-14 09:27:02 -07:00
Ayaan Zaidi 319a796079 fix(gateway): never leave the restart admission fence closed without a restart
A failed, refused, superseded, or thrown restart emission could leave the
reversible restart-signal admission fence closed forever: concurrent emitters
could overwrite the live rollback lease with a dead stand-in, the fenced body
had no try/finally, and the outer catch swallowed errors precisely because the
stuck fence made isGatewayRestartDraining() true. The gateway then rejected
every new task with GatewayDrainingError - silently - until an operator
restarted the process.

beginGatewayRestartSignalAdmission now returns null instead of stand-in
leases (single fence owner), emitPreparedGatewayRestart reopens the fence on
every non-delivery path via try/finally while preserving it whenever a queued
SIGUSR1 is unconsumed, refused-signal cleanup force-clears orphaned fences,
and admission close/reopen transitions are logged with their reason. The
self-contained SQLite restart-intent persistence moves to restart-intent.ts
to keep restart.ts within the LOC ratchet.

Fixes #107322
2026-07-14 16:14:48 +05:30
Peter Steinberger e69df7ef22 refactor(cli): privatize internal exports (#106848) 2026-07-13 15:09:26 -07:00