Commit Graph

1506 Commits

Author SHA1 Message Date
Peter Steinberger 19be0f6dc0 fix: honor scenario drivers in generated QA commands (#131101)
* fix: honor scenario drivers in generated QA commands

Carry declared driver requirements through coverage search, grouping and quoted command output. Preserve unconstrained defaults and validate generated arguments through real CLI parsing and canonical lane selection. Closes #131088.

* test: capture generated QA commands through the shell
2026-08-27 11:58:30 -07:00
Peter Steinberger ff3cf3de3b docs(start): correct headless credential handoff to the SQLite auth store (#131024) 2026-08-27 09:40:27 -07:00
Peter Steinberger 3aaf13ca84 feat(ui): show activity cards for online people (#130664)
* feat(ui): show activity cards for online people

Record live person timing independently of heartbeat freshness and expose keyboard- and touch-accessible cards with visible session links. Preserve continuous online intervals across overlapping tabs without adding persistent activity history.

Closes #130649

* build(protocol): refresh generated presence timing fields

* fix(gateway): scope presence snapshots to read-access operators

Apply one recipient projection to hello, system-presence, and events. Keep person timing and time-zone details behind read access, and filter watched references with the canonical session-list policy without leaking hidden counts. Preserve idle-person metadata for readers and prevent non-reader activity-driven frames.

* fix(plugins): preserve Gateway capability load context

Carry the owning registry's metadata generation, workspace, install records, and built-artifact preference through capability discovery. Preserve standalone source loading and existing speech eligibility while avoiding synchronous source transforms on cold Talk catalog requests.

Fixes #130777

* test: align integrated presence and capability coverage

Keep the recipient matrix in its timing-aware owner, close the suite-owned Gateway for shutdown proof, and spy on the canonical install-record reader. Remove the retired private carrier's assertion allowance.

* fix(gateway): broadcast presence when clients connect

Publish completed connection rows through the canonical scoped broadcaster so established readers see first connections and reconnects without waiting for profile edits or activity. Preserve shared online intervals and reset them only after the final socket closes.

* fix(ci): align presence fixtures and sidebar type ownership

Reuse the canonical sidebar host type to remove the type-only lazy-runtime cycle. Assert omitted scopes through hello auth and denied presence reads, and supply presence version callbacks in the shared WebSocket context fixture. Retain startup admission and drain assertions without changing deadlines.

* fix(ui): retain latest scroll commands until their destination

Do not treat the virtualizer's idle debounce or a decreasing offset as reader takeover. Settle at the actual DOM end within one pixel, retaining the separate eight-pixel UI-follow policy and explicit input cancellation. Cover retargeted idle delivery and near-end precision with the real dependency, synchronize pointer baseline capture, and type the existing composer snapshot as its textarea contract.

Fixes #130892
2026-08-27 08:07:22 -07:00
Peter Steinberger 52750a2a65 fix(agents): restore direct-command proactive compaction (#130863)
Share usage-based maintenance with successful built-in command turns while
preserving diagnostic-only pressure estimates and native compaction ownership.
Carry the winning auth profile privately, fence session accounting, and retain
completed local replies when ordinary post-turn summarization fails.

Cover threshold, delivery, cancellation, successor, and routing boundaries;
clarify the documented scheduling behavior without adding configuration.

Closes #130716
2026-08-27 03:27:56 -07:00
Peter Steinberger 45de8e7a9e fix(matrix): automatic replies lose native table formatting (#130820) 2026-08-27 01:37:01 -07:00
Peter Steinberger 625f3cf7ea fix(agents): preserve Codex for reasoning capability metadata (#130752)
* fix(agents): preserve Codex for reasoning capability metadata

Keep native-compatible reasoning metadata out of authored transport overrides.
Share effective harness availability across execution, session metadata, and
thinking controls while preserving exact-request fallback and locked ownership.

Fixes #130665.

* fix(agents): keep cold harness registry reads side-effect free
2026-08-27 00:41:18 -07:00
Peter Steinberger 1d872fe87e fix: filter watched-session presence for each recipient (#130764) 2026-08-27 00:07:48 -07:00
Peter Steinberger 7eebd6d4a6 docs(hooks): clarify setup and execution contracts (#130734) 2026-08-26 23:12:06 -07:00
JC 8a0cf73053 fix(memory): isolate sqlite-vec KNN from the event loop (#128078)
* fix(memory): isolate sqlite-vec KNN in subprocess

* fix(memory): satisfy KNN subprocess CI guards

* test(release): include memory KNN child artifact

* test(memory): update KNN subprocess fixtures

* fix(memory): bound KNN child row payloads

* fix(memory): preserve published readers during cancellable KNN

Keep shadow reindex database, vector readiness, FTS and metadata state in
an owned async context. Public operations retain the published connection,
and escaped shadow continuations cannot write to it after rebuild closes.

Terminate the one-shot query child through its owned handle and retain
admission until close. Remove process-tree/PID probing and test-only hooks.
Cover publication overlap, callback reads, cancellation and recovery.

Co-authored-by: OpenClaw Assistant <assistant@openclaw.local>

* refactor(memory): separate database context ownership

Keep the published reader and shadow writer context in its own owner module. Repair plugin test imports and typed lint findings without changing the verified KNN behavior.

---------

Co-authored-by: OpenClaw Assistant <assistant@openclaw.local>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-26 20:29:02 -07:00
Markus Hartung 18c801daa1 fix(ai): stabilize runtime context across Responses tool rounds (#130166)
* fix(agents): stabilize runtime context across tool rounds

Keep current-turn metadata after the active user and before tool calls so Responses continuation retains the complete prior request prefix. Preserve active hook context and intentionally resend when transient context expires on a later turn.

Reworks #130166. Thanks to Markus Hartung for investigating continuation failures.

Co-authored-by: Markus Hartung <mail@hartmark.se>

* fix(ai): localize continuation ordering to Responses

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-26 19:01:32 -07:00
Ayaan Gazali 1f2e99832b fix(channels): bundled channels reject the documented responsePrefix override (#118148)
* fix(channels): bundled channels reject the documented responsePrefix override

* fix(feishu): pass channel and account to the reply prefix resolver

The ordinary reply dispatcher built the prefix context without channel or
accountId, so channels.feishu.responsePrefix and its account override fell
through to the global value. The comment dispatcher already passed them.

* fix(channels): accept twitch's root responsePrefix and type ClickClack's

Twitch validates as a two-branch union with both branches closed, so
responsePrefix declared only on the account shape left
channels.twitch.responsePrefix rejected whenever accounts is present. Also
declares responsePrefix on ClickClackAccountConfig so typed callers can express
what its schema already accepts.

* fix(clickclack): apply the configured responsePrefix in model mode

Model mode sends the completion straight to ClickClack instead of going through
the agent reply pipeline, so the documented responsePrefix parsed and then did
nothing. Resolve it through the public reply-pipeline seam so template
variables like {model} render from the completion's own provider and model.

* fix(clickclack): do not double the response prefix in model mode

systemPrompt is operator-owned, so a model can be instructed to emit the
configured prefix itself and the unconditional concatenation then sent it
twice. Match the shared reply normalizer's startsWith guard.

* test(clickclack): resolve inbound access through the real resolver

Main now requires access.channelIngress, which the hand-built fixture did not
provide, so the model-mode messages never dispatched. Drop the fixture and let
handleClickClackInbound resolve access from a DM-shaped message, matching the
sibling bot-loop tests.

* test(channels): reject a key missing from any composed branch

A union alternative is a configuration mode an operator can pick, so a key
present in only one mode is unusable in the other even though the union still
validates. Treat any rejecting alternative as a rejection, and walk allOf when
collecting account schemas.

* fix(channels): offer responsePrefix only where the reply path applies it

Applying the prefix to an outbound reply is per-channel wiring, not a shared
step. Buzz, Nostr, QA Channel, Raft, Reef and SMS never read it, so accepting
the key there validated a setting their delivery path ignores. Anchor the
contract to the channels that consume the prefix.

* fix(channels): complete shared response prefix delivery

Co-authored-by: ayaangazali <ayaangazali.work@gmail.com>

* docs(config): refresh response prefix baseline

* test(tlon): use canonical prefix fixture helpers

* test(channels): prove Twitch reply prefix delivery

Exercise schema admission and the real shared reply dispatcher at the Twitch native send boundary, replacing redundant schema-only coverage. Keep Tlon fixtures on canonical imports and recording options.

* docs(channels): clarify explicit message prefix behavior

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-26 17:08:28 -07:00
Peter Steinberger a4c01c1a77 feat(ui): save appearance preferences per user profile (#130340)
* feat(ui): save appearance preferences per user profile

When a Control UI connection is bound to an authenticated user profile
(trusted-proxy, Tailscale, GitHub identity), theme, theme mode, and accent
become per-profile: stored in the existing user_preferences KV store via
users.prefs.set, overriding gateway-wide ui.prefs, with live cross-device
updates over a new additive users.prefs.changed event scoped to the same
merged profile. Restore default deletes only the profile key and falls back
to the gateway-wide value. talk.config projects the caller's profile accent
for native clients. Profile-less connections (token/password/none auth)
keep the existing gateway-config behavior byte-identically.

Release-note context: on multi-person gateways appearance choices are now
personal and follow you across devices; an admin restyling their own UI no
longer reskins the whole team.

* chore(protocol): allowlist users.prefs.changed for mobile clients

iOS and Android resolve the profile accent through talk.config on connect
and config refresh; live per-profile appearance push on natives is a named
follow-up.

* chore(protocol): regenerate Swift/Kotlin models for users.prefs.changed

* fix(ui): honor explicit defaults and identity switches for profile appearance

Addresses the three ClawSweeper review findings:
- resetValue for profile-bound appearance keys is the deletion fallback
  (gateway value), so an explicit product-default selection persists as a
  profile write instead of being misclassified as a reset.
- An identity switch between two known scopes forces a full appearance
  reconcile and clears appearance keys the returning identity never set,
  so a shared browser cannot keep the previous profile's look (boot keeps
  the last-seen shortcut).
- talk.config joins the profile-dependent dispatch gate so a pending
  GitHub identity sync completes before the profile accent is projected;
  token clients pass through untouched.

* chore(ui): satisfy assertion-safety ratchet and docs formatting

* refactor(ui): extract server-prefs storage primitives

server-prefs.ts crossed the 700-line cap after the review fixes; the
stateless localStorage persistence primitives move to
server-prefs-storage.ts (no behavior change). Shrink-prunes the
assertion-safety baseline for the moved casts.

* fix(ui): keep imported custom themes out of profile storage

Custom palettes are browser-local by contract, so a profile must never
carry theme=custom to a browser that cannot render it. The wire contract
drops custom from the storable theme values (readers self-heal any stored
value), a profile-bound custom selection stays retained browser-local
(including the offline-queued path), and the exhaustive theme test now
encodes the exception.
2026-08-26 16:48:26 -07:00
Peter Steinberger 5153f25495 fix(memory): prevent forgotten session content from returning (#130451) 2026-08-26 16:26:00 -07:00
Dallin Romney cf7b3a24ec fix(qa): run Buzz profile scenarios with pooled credentials (#130316)
* fix(qa): honor pooled Buzz credentials in profiles

* test(qa): guard optional Buzz cleanup

* fix(qa): delegate Buzz profile credential selection

* refactor(qa): simplify Buzz source handoff

* docs(qa): clarify Buzz credential delegation
2026-08-26 15:07:13 -07:00
Marvinthebored ce4a680544 feat(models): add configurable model selection scopes (#127813)
* feat: make model persistence scope explicit

* fix: keep unauthorized model directives as plain text

An unauthorized sender's inline directives are cleared to plain text before
they are acted on, but the persistent write target was derived from the
directives as they arrived. An unauthorized `/model <provider/model> -a` or
`-g` therefore reached the owner-authority error return instead of the
plain-text path every other directive takes.

Derive the target, the authority flag, and the persist flag after the
clearing. Authorized senders skip the clearing, so their behavior is
unchanged; the only closure that captures the target is invoked well after
the new declaration site.

Covers both persistent flags with a regression test that fails on the prior
head with "Agent and global model defaults require owner authority or
operator.admin scope." and passes here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat(models): make model selection scope configurable

Preserve current model-selection defaults when the optional scope preference is unset. Add explicit session, agent, and global overrides without broadening config-write authority.

Co-authored-by: Marvinthebored <peter@lindsey.jp>

* test(models): simplify scope expectation fixtures

---------

Co-authored-by: Marvinthebored <peter@lindsey.jp>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-26 14:31:56 -07:00
Masato Hoshino df559c3284 fix(agents): stored auth profile order is ignored when resolving CLI runtime aliases (#129165)
* fix(agents): honor the stored auth profile order when resolving CLI runtime aliases

`resolveCliRuntimeFromAuthProfile` built its ordered profile list from
`cfg.auth.order` alone, and `RuntimeAuthAliasParams` carried no auth store at
all, so the order an operator sets through `models auth order set` never
reached CLI runtime alias resolution. The canonical sibling resolver,
`resolveAuthProfileOrderWithMetadata`, already reads the stored order first and
falls back to config, so the two disagreed about the same question.

With no config `auth.order` and two profiles sharing a provider auth key, the
ordered list came out empty and the resolver fell through to its "exactly one
compatible profile" branch, returning undefined. The gateway then reported
model availability and picked the turn's execution backend as if the reorder
had not happened, and no restart cleared it because the store was never read on
this path.

Rather than re-deriving the precedence at the call site, extract it: the
stored-first explicit-order resolution moves out of
`resolveAuthProfileOrderWithMetadata` into `resolveExplicitAuthOrderSelection`,
and alias routing becomes its second consumer. Both now resolve the order
through one implementation, across normalized provider keys, so they cannot
drift apart again. The store is read from the lifecycle-published runtime
snapshot for the requested agent and its declared inherited owner, matching how
prepared model catalog and registry resolve the same pair; no new snapshot API
and no SQLite reopen on request paths.

An authored stored order also owns selection, so it suppresses the
unique-compatible-profile fallback the way the canonical resolver does.

Tests cover stored-order-only selection, stored-over-config precedence,
non-normalized stored keys, inheritance from the main agent, authoritative
empty orders with a control for the fallback that must survive, and the
unchanged config-only path.

* fix(agents): align runtime routing with stored auth selection

Share stored-over-config precedence, preserve stale-profile repair, and route explicitly selected stored credentials without config metadata. Remove unreachable OpenAI order-alias merging.

Co-authored-by: masatohoshino <g515hoshino@gmail.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-26 13:50:02 -07:00
Peter Steinberger d9b41332e4 fix(ui): restore person grouping for multiple session owners (#130326)
* fix(ui): restore person grouping for multiple session owners

Use the live session-owner roster for Person grouping and People sorting instead of the connection-time identity policy. Preserve preferences while the roster is unknown and retain single-owner dormancy across sidebar and Sessions page.

* docs(ui): match the owner sorting label

* refactor(ui): reuse session owner and menu selection paths
2026-08-26 13:28:36 -07:00
Peter Steinberger 8e39f58365 docs(memory): add memory provenance and deletion concepts page (#130278)
* docs(memory): add memory provenance and deletion concepts page

New concepts/memory-provenance page tells the operator policy story end to end: recorded session lineage through consolidation, the admission policy and its pipeline-only boundary, memory forget guarantees (whole-entry purge, derived-artifact sweep, forgotten tombstones) and reported boundaries (transcripts, curated writes, paraphrased prose), plus the person/source purge workflow. Cross-linked from memory architecture, dreaming, builtin, overview, CLI, and config reference; registered in docs navigation.

* docs(i18n): add glossary terms for the memory provenance page

* docs(memory): distinguish curated entries from missing lineage in the provenance page
2026-08-26 11:36:39 -07:00
Josh Avant e6ed7e30cc feat(audit): record session action decisions (#129093)
* feat(audit): record session action decisions

* fix(protocol): preserve session sharing client compatibility
2026-08-26 08:21:37 -07:00
Peter Steinberger 49d916e8e9 fix(memory): preserve session recall when upgrading from QMD (#130016)
* fix(memory): preserve QMD session search across builtin migration

* fix(memory): migrate agent defaults to canonical memory owner
2026-08-26 03:18:42 -07:00
Ho Lim 6956a2ed5e fix(agents): retry live model switches outside fallback chain (#114214)
* fix(agents): retry unconfigured live model switches

Signed-off-by: Ho Lim <subhoya@gmail.com>

* refactor(agents): centralize live switch target resolution

Signed-off-by: Ho Lim <subhoya@gmail.com>

* test(agents): clarify stale switch fallback model

Signed-off-by: Ho Lim <subhoya@gmail.com>

* docs(agents): clarify live model switch retry behavior

Co-authored-by: Ho Lim <subhoya@gmail.com>

---------

Signed-off-by: Ho Lim <subhoya@gmail.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-25 22:28:57 -07:00
Peter Steinberger 4a2439f01d docs(memory): document shipped turn-taint propagation for network tool output (#129837)
* docs(memory): document shipped turn-taint propagation for network tool output

docs/concepts/memory-architecture.md still claimed content origin is not
propagated within an owner turn. The runtime has since shipped exactly that:
tool results declaring resultContentSource "network" taint the rest of the
turn (packages/agent-core/src/agent-loop.ts), tainted assistant messages
classify untrusted for memory (packages/memory-host-sdk session-provenance),
and taint clears on the next user message. The remaining gap - tools that do
not declare a network content source do not taint - is now stated honestly.

* docs(memory): state untainted assistant provenance as agent, not sender class
2026-08-25 21:15:07 -07:00
Peter Steinberger 2f17d11e90 fix(backup): preserve configured agent state roots (#129773)
* fix(backup): honor configured agent state roots

Inventory configured agent directories as durable backup owners regardless of layout. Reuse the owner inventory for archive planning, SQLite snapshotting, verification, restore safety, and Git/SQLite backup selection.

Add manifest-first plugin backup resource declarations so regenerable runtime roots stay plugin-owned without provider IDs in core.

Closes #129730

* fix(backup): preserve canonical ownership checks
2026-08-25 21:13:37 -07:00
Peter Steinberger 6a1dedb00c refactor(agents): remove redundant heartbeat system prompts (#129642)
* refactor(agents): remove redundant heartbeat system prompts

* test(agents): align heartbeat cleanup checks and prompt snapshots
2026-08-25 16:10:22 -07:00
Peter Steinberger 34067fcc2e fix(worktrees): preserve nested repositories during cleanup (#129454)
* fix(worktrees): preserve nested repositories during cleanup

Automatic idle, limit, and run-end cleanup now treats nested repositories and linked worktrees as separate lifecycle boundaries. This prevents repeated cleanup warnings and avoids deleting unregistered nested user state.

Closes #129414

* test: isolate media auth fixture from plugin loading

Keep the OAuth fixture outside the refresh window and stub only the unrelated retired-profile plugin lookup so the media auth contract completes deterministically in CI.
2026-08-25 14:16:44 -07:00
Dallin Romney 1d526c5c0e refactor(qa): adopt Crabline server artifacts (#118008)
* refactor(qa): adopt Crabline server artifacts

* fix(deps): restore Crabline release-age guard
2026-08-25 10:38:28 -07:00
wangmiao0668000666 ebf07e56e8 fix(memory): apply temporal decay to dated files in memory subdirectories (#121103)
* fix(memory): apply temporal decay to dated files in memory subdirectories

DATED_MEMORY_PATH_RE only matched memory/YYYY-MM-DD.md at the root, so
dated files in subdirectories (e.g. dreaming phase reports at
memory/dreaming/light/2026-05-14.md) were classified as evergreen and
kept full ranking weight forever even with temporalDecay enabled.

Accept a date-only Markdown basename at any depth below memory/, matching
the nested-path shape short-term promotion already recognizes. MEMORY.md,
USER.md, and undated files remain evergreen; non-memory sources keep the
mtime fallback.

Fixes #121046

* refactor(memory): trim issue-specific commentary from dated-path matcher

* fix(memory): decay nested and slugged dated memory files

Co-authored-by: wangmiao0668000666 <wang.miao86@xydigit.com>

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-08-25 00:36:10 -07:00
Peter Steinberger 8f0503657c feat(github): surface team-session attribution (#129012)
* feat(github): surface team-session attribution

Worked on by:
- @steipete

* fix(github): preserve PR body after attribution

Worked on by:
- @steipete
2026-08-24 22:57:03 -07:00
Ayaan Zaidi 614e084cf8 improve: serialize Telegram Mantis proofs (#128811)
Queue Telegram-visible Mantis proofs at workflow level so only one shared-user run allocates a runner at a time.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-24 23:10:45 +05:30
Peter Steinberger 053b89d80f improve(ui): open short session links without extra lookup (#128778)
* perf(ui): remove short session route waterfall

* docs: clarify short link gateway requirement
2026-08-24 09:33:20 -07:00
Peter Steinberger e30279f89f fix(google): support stable Gemini Embedding 2 request contracts (#128716)
* fix(memory): support stable Gemini Embedding 2

* fix(memory): validate async Gemini batch dimensions

* docs(memory): explain stable Gemini index rebuild

* docs(memory): cover explicit-dimension rebuilds

* fix(google): honor stable Gemini embedding task contracts

Co-authored-by: Franck MEYER <meyerfranckpro@gmail.com>

---------

Co-authored-by: Codex OpenClaw Migration <noreply@local>
Co-authored-by: Franck MEYER <meyerfranckpro@gmail.com>
2026-08-24 05:38:54 -07:00
Peter Steinberger 295809df98 feat(geolocation): resolve client addresses to a coarse city via a bundled plugin (#128546)
* feat(geolocation): resolve client addresses to a coarse city via a bundled plugin

The Activity identity card could show a client's IP address but not where it
was, so an operator still had to look the address up by hand.

Add a bundled `geolocation` plugin that owns address-to-place resolution behind
one authenticated route, `GET /plugins/geolocation/lookup?ip=`. It downloads a
MaxMind-format database on first lookup into the state directory, answers from
that local copy, and refreshes it monthly, so a lookup never sends an address
to a third party. The Control UI renders the resolved city on the device row
next to the address and the client-reported time zone.

The default source is DB-IP City Lite under CC BY 4.0. That license requires
attribution, so every response carries the credit and the UI renders it next to
the value; the database is downloaded at runtime and never redistributed.
Plugin code and the `maxmind` reader are MIT. No free city-level IP database is
MIT-licensed, so the obligation lives with the data rather than the code, and
`databaseUrl` plus the attribution fields make the source swappable.

No new core provider kind: with one implementation the plugin owns everything
through the existing HTTP-route seam, keeping core plugin-agnostic. A second
provider is what would justify promoting this to a registry contract.

Availability and lookup failure stay distinguishable: a missing or still
downloading database answers 503, never `found: false`. A failed refresh serves
the cached copy, and a body that does not parse as an MMDB is discarded without
replacing a working database.

* fix(docs): correct geolocation config examples and add zh-CN glossary entries

The config examples used `plugins.<id>` instead of the real
`plugins.entries.<id>.config` shape, which the docs config-example
validator and src/config/docs-config-examples.test.ts both reject.
New doc labels also need zh-CN glossary entries.

* chore(labeler): cover the geolocation extension directory

AGENTS.md requires a labeler entry plus a GitHub label for every new
plugin surface; test/scripts/labeler-extension-coverage.test.ts enforces
the labeler half.

* fix(geolocation): address review findings on caching, download bounds, and scope

Cold-start lookups were permanently suppressed. The loader cached one promise
per address including failures, so the 15s browser deadline expiring against a
first download that takes ~46s cached a blank forever, and a mounted row only
looks up again when its IP changes. Lookups now return a discriminated
located/absent/unavailable result: only definitive answers are cached, and the
element retries an unavailable one on a widening 5s/15s/45s schedule.

Download limits ran after allocation. The size check happened only after
`response.arrayBuffer()` had buffered the whole body, and gunzip had no output
ceiling, so a replaced source or a compression bomb could exhaust Gateway memory
before rejection. The body now streams against a compressed ceiling enforced
per chunk, and inflation uses zlib's maxOutputLength.

Cached placements were not scoped to the Gateway. The cache keyed only by
address while endpoint and credentials come from the shared Gateway context, so
a switch could render the previous Gateway's answer. The shared reset hook now
supports multiple subscribers - a single slot silently dropped whichever
registered first - and the geolocation cache subscribes.

Unresolvable ranges no longer trigger a download. Only loopback suppresses `ip`
at connect, so Tailscale carrier-grade-NAT and LAN addresses are recorded and
displayed. No geolocation database contains them, so a tailnet-only or LAN-only
Gateway was downloading 125 MB to answer nothing. The route now answers those
ranges without loading the database, using the already-public
`isPrivateOrLoopbackHost` seam so the SDK surface budget is unchanged.

The quickstart queried a reserved documentation range while showing a located
response, which cannot happen; it now uses a routable address and documents the
not-found case.

* fix(deps): resync the lockfile after dropping the net-policy dependency

The geolocation plugin briefly depended on @openclaw/net-policy before
switching to the already-public isPrivateOrLoopbackHost SDK seam. The
package.json entry was removed without regenerating the lockfile, so the
frozen-lockfile install failed and every downstream CI job failed with it.
2026-08-24 04:56:03 -07:00
Peter Steinberger 234df15a6d chore: refresh dependencies after seven-day cooldown (#128414)
* build(deps): refresh dependencies after cooldown

Apply dependency, toolchain, action, image, and exact tool updates released by the inclusive 2026-08-16 seven-day cutoff. Adapt owner boundaries for the resulting CUA, logging, Teams, Markdown, native, and test-harness contract changes while retaining versions blocked by upstream compatibility constraints.

* fix(ui): align markdown renderer env typing

* fix(deps): align postcss and mistral peer contracts

* fix(deps): repair refreshed dependency contracts

* fix(deps): retain tslog startup budget

* fix(ci): verify Android tools with SHA-256

* fix(ci): fence Android SDK cache version
2026-08-24 03:01:54 -07:00
Peter Steinberger 9284e23cdd feat(control-ui): show client IP and time zone on the activity identity card (#128438)
* feat(control-ui): show client IP and time zone on the activity identity card

The Activity identity card showed only host and platform, so an operator
looking at a teammate could not tell where that person was connecting from.
Presence already carried a best-effort `ip`, and it was simply not rendered.

Add the client's self-reported IANA time zone to the connect handshake and
presence entry, and render both `ip` and `timeZone` on the device row. The
time zone matters because the connecting address is frequently unusable for
location: connect handling omits `ip` for loopback clients, and tunneled or
Tailscale clients land in private/CGNAT ranges. A browser knows its own zone
regardless of how it reached the gateway.

Both protocol additions are optional fields, so no version bump is needed.

* build(protocol): regenerate Swift models for presence timeZone
2026-08-23 20:07:06 -07:00
Mitra Kermanian d710cbd8be docs: fix four broken cross-page anchor links (#128278)
* docs: fix five broken cross-page anchor links

Five internal doc links pointed at anchors that do not exist on the target
page, so readers following them landed at the top of the page instead of the
referenced section.

- /gateway/cloud-workers#desktop -> #desktop-interactive
  (heading is "## Desktop (interactive)"), from experimental-features and
  the gateway protocol reference.
- /web/urls#special-documents-and-startup-modes ->
  #other-special-documents-and-startup-modes
  (heading is "## Other special documents and startup modes"), twice from
  the Control UI page.
- /plugins/sdk-runtime#api-runtime-agent -> #runtime-namespaces
  "api.runtime.agent" is an <Accordion> title, not a heading, so it emits no
  anchor; the enclosing "## Runtime namespaces" heading is the real target.

Sibling links on all three target pages already use heading-derived anchors
(#coordinator-backed-crabbox, #route-table, #reusable-runtime-utilities),
and this was the only link in docs/** aiming at an Accordion title out of
1342 such titles.

Docs-only change; no runtime or config surface is touched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs: restore the sdk-entrypoints deep link

Running the real Mintlify anchor audit (pnpm docs:check-links:anchors) showed
that /plugins/sdk-runtime#api-runtime-agent already resolves: Mintlify does
generate anchors from <Accordion title="..."> elements, so the original link
was correct.

The audit flags fragments it cannot resolve -- it reported both
cloud-workers#desktop and urls#special-documents-and-startup-modes on main --
and it never reported this one. Retargeting it to #runtime-namespaces would
have replaced a precise deep link with a coarser section link, so this restores
the original.

The remaining four link instances in this PR are confirmed broken by the same
audit: main reports 313 broken links across 45 files, this branch reports 309
across 42, with no newly broken links.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 16:31:01 -07:00
Jason O'Neal c96e813750 fix(doctor): migrate legacy Claude CLI allowlists to canonical refs (#126866)
`agents.defaults.modelPolicy.allow` seeded from a legacy model map kept
`claude-cli/*` refs that doctor never rewrote, while the model map migrated
to canonical `anthropic/*` keys — leaving no selectable Claude model after
upgrade. `openclaw doctor --fix` now canonicalizes explicit allowlists in
the same pass: legacy refs migrate to provider refs with their claude-cli
runtime policy preserved, and superseded legacy model-map keys are dropped.

Closes #124952

Co-authored-by: Jason O'Neal <jason.allen.oneal@gmail.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-23 10:29:18 +05:30
Jason O'Neal d0700b3bc2 fix(agents): heartbeat runs no longer block visible turns (#126853)
* fix(heartbeat): keep background runs off the main lane

* docs(queue): describe heartbeat background lane

* fix(agents): forward heartbeat lane to embedded fallback

* test(agents): prove heartbeat and visible lane overlap

* test(agents): expose heartbeat lane in embedded harnesses

* refactor(agents): keep heartbeat lane choice at fallback boundary

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

---------

Signed-off-by: sallyom <somalley@redhat.com>
Co-authored-by: sallyom <somalley@redhat.com>
2026-08-22 15:15:18 -04:00
Ayaan Zaidi 6917a15bce fix(mantis): prove the PR merged onto current main instead of the raw head (#127961)
The Telegram Desktop proof previously built the candidate from the raw PR
head, so a head behind main failed for reasons main already fixed
(observed on #127770: 59 commits behind, hitting the Unknown-model defect
fixed by #127952). GitHub's cached PR test merge cannot be the candidate
either: it was observed 50 commits / 12 hours stale and never refreshed.

The workflow now resolves the live refs/heads/main tip and merge base at
dispatch, requires a main-targeting PR, and builds a deterministic local
merge (merge-tree --merge-base + commit-tree with pinned identity/date) as
the candidate; conflicts fail with a direct rebase message and there is no
fallback to the raw head. Lane labels and docs now say the candidate is
the PR merged onto main.
2026-08-22 21:28:26 +05:30
Ayaan Zaidi da0cb592dd perf(e2e): reuse one authorized Telegram Desktop session per Mantis run (#127835)
* refactor(mantis): reuse authorized desktop captures

* fix(mantis): budget desktop authorization failures

* chore(mantis): bound desktop proof retries

* fix(e2e): drop unused recorder failure fact type export

* fix(ci): route Mantis desktop teardown through the recorder wrapper

Cleanup invoked the internal recorder executable as mantis-sut, which is
deliberately kept out of the docker group and cannot read the
recorder-owned session file; teardown therefore failed and blocked
safe_to_release. The cleanup step already runs as the recorder user, so
call the public wrapper whose exec shim cds into the session root.

* fix(e2e): make recorder failure fact lane-readable; document v2 lifecycle

The Mantis workflow runs the recorder as the desktop user while the lane
reads the authorization-failure fact as mantis-sut; 0600 made that read
fail EACCES and silently disabled the two-attempt retry budget. Write the
fact 0644 — the 0770 attempt directory bounds visibility.

Update the mantis doc's recorder section for the v2 session lifecycle:
required --session handle with healthy-session reuse, capture-only stop,
and teardown owning authorization termination and lease release.
2026-08-22 15:58:12 +05:30
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
Ayaan Zaidi 10a3df4fb6 feat(mantis): let proof agents extend desktop control (#127271)
* feat(mantis): let proof agents extend desktop control

* fix(mantis): constrain desktop extensions

* docs(mantis): document desktop action authority

* fix(mantis): bind actions to recorded window
2026-08-21 23:09:17 +05:30
Peter Steinberger e7cfff2167 feat(control-ui): stream live draft previews in the typing indicator (#126994)
* feat(control-ui): stream live draft previews in the typing indicator

Multi-identity sessions now show what a teammate is typing, not just that
they are typing: the composer's per-keystroke session.typing sends carry a
bounded tail of the draft (optional preview field, 400 code points max),
the gateway throttle re-emits on changed payloads at 250ms (boolean-only
stays at 1s, trailing edge keeps the latest draft), and the transcript
renders a per-actor bubble with the live text plus a blinking caret.
Actors without preview data keep the three-dot bubble.

Previews are ephemeral presence: never persisted, never part of the
session transcript or model context, excluded from aria-live regions, and
gated by the existing >=2-live-viewers, sharing-role, and incognito
checks. No new config surface.

* chore(protocol): regenerate Swift gateway models for typing preview

* fix(gateway): aggregate typing previews across same-actor connections

A boolean-only session.typing update from a second connection of the same
actor (another tab or device) erased their live draft preview, because
typing liveness aggregated per actor while the broadcast preview came only
from the latest request. Preview aggregation now lives with the connection
aggregation owner: updateTypingConnections tracks per-connection previews
and returns the newest non-empty preview among live connections, so the
broadcast keeps the active draft until its connection stops or expires.

Regression fails pre-fix (event lost its preview field).
2026-08-20 22:30:41 -07:00
Bek 76bb7ff2b6 fix(skills): omit content hashes from prompts (#126951) 2026-08-21 01:13:42 -04:00
Sarah Fortune 67bcea131e feat(slack): add compact commentary progress (#126480)
* feat(slack): add compact progress style

* chore(slack): protect compact progress contract

* chore(slack): clarify compact progress comment

* chore(slack): document compact task card behavior

* chore(slack): document compact draft card behavior

* chore(slack): document compact progress config contract

* fix(slack): enforce compact commentary-only progress

* chore(slack): strengthen compact style guard

* fix(slack): type compact progress config

* fix(slack): scope compact plan suppression

---------

Co-authored-by: Sarah Fortune <sarah.fortune@gmail.com>
2026-08-20 15:02:33 -07:00
Ayaan Zaidi 4768ac53c5 feat(qa): acknowledge Mantis PR requests (#126702)
Give maintainers immediate visibility when Mantis is requested. Bare mentions now react, link the active run, and keep one run-owned status comment through proof, short-circuit, or failure.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-20 21:19:40 +05:30
Ayaan Zaidi 8458777e6a feat(qa): trigger Mantis proof from pull requests (#126658)
Add trusted ClawSweeper-label and maintainer-comment dispatch for Mantis Telegram proof. Short-circuit non-visible PRs before desktop setup while preserving exact-head, fork, credential, and comment-ownership boundaries.
2026-08-20 20:05:51 +05:30
Ayaan Zaidi b7b7eec5ee feat(qa): run Mantis Telegram proof on local desktop (#126220)
Move Mantis Telegram Desktop proof from the remote AWS/Crabbox lane to a recorder-driven local Docker desktop. Keep proof scenarios agent-authored, cache trusted build outputs, and publish exact visible Telegram evidence without writing the QA bot token to artifacts.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
2026-08-20 11:12:51 +00:00
Peter Steinberger 147edf47f3 fix(auth): keep a retired auth JSON from stranding a migrated store (#126562)
* fix(auth): keep a retired auth JSON from stranding a migrated store

Runtime failed closed with AUTH_PROFILE_MIGRATION_REQUIRED whenever a retired
credential file was present, even when the canonical SQLite store already held
the agent's profiles. One leftover auth.json therefore made a fully migrated
install unusable, and the gateway lifecycle preflight refused start/restart on
top of it, so every channel and provider stayed offline until Doctor ran.

A legacy file is now only fatal when the canonical store cannot serve
credentials. Doctor's importer never overwrites a usable stored credential, so
a file sitting beside a populated store is unarchived bytes, not pending
migration: runtime logs a one-time warning and keeps serving. An empty store
with a credential file still fails closed and never falls through to
environment auth. Startup degrades that owner to configured-unavailable
instead of refusing to boot, which lets the lifecycle preflight go away.

* refactor(secrets): retire the auth-profiles.json vocabulary

Auth profiles moved to SQLite, but operator-facing surfaces still named the
retired JSON file. The duplicate-agentDir error told operators to copy
auth-profiles.json to share credentials, which does nothing and lands the
second agent in a migration-required state; `openclaw migrate plan codex`
reported a target file that is never created; and the secrets picker labelled
candidates with a filename that no longer exists.

Renames the SecretTargetConfigFile discriminator to "auth-profile-store" and
corrects the operator-facing text, the migrate plan target, and the docs that
described the file as a live target. Genuine legacy-filename uses in doctor,
the security fixer, and migration fixtures are unchanged.

Also deletes resolveSecretPlanTargetByPath and ResolvedSecretPlanTarget from
the plugin SDK. They have no callers in core, plugins, or tests, and the
symbols are absent from the latest stable tag, so they carry no compatibility
obligation and are removed rather than deprecated. Their inline parameter type
was the only thing putting the retired filename on the public SDK surface.

* improve(wizard): warn about device-code phishing

The device-code prompt only warned against sharing the code, and only when an
expiry was known. Device-code phishing works the other way around: the attacker
starts the login and gets the victim to enter the attacker's code. Codes
delivered over a chat channel are the risky case and carry no expiry hint, so
the warning is now unconditional and covers received codes, matching the Codex
CLI prompt.

Also documents the Codex auth handoff: a subscription profile is installed as
in-memory external auth rather than persisted, and token refresh is inverted
so the refresh token stays in OpenClaw's store.

* fix(test): make transcript read-failure injection order-independent

server.sessions.compaction-read-errors.test.ts injected its failures with
mockRejectedValueOnce, which fails the NEXT call to loadTranscriptEvents
globally. Under --isolate=false a shard shares one worker, so any sibling
transcript read could consume the one-shot rejection before the compaction RPC
issued its own; compaction then ran against the real reader and returned ok,
failing three assertions. This shard was already red on main; a prior repair
fixed the mock's initialization order but left the call-order dependency.

Key the injection on the seeded sessionId instead, so unrelated readers cannot
consume it and the re-read case counts only its own session's reads.

Also updates two expectations invalidated by this branch: the duplicate-agentDir
remediation text, and the plugin SDK export ratchet, shrunk by the two retired
secret-plan exports.
2026-08-20 00:42:41 -07:00
Peter Steinberger 0135046830 refactor(llama-cpp): use one provider for managed and existing servers (#126434)
* refactor(llama-cpp): unify server ownership modes

* test(llama-cpp): preserve shared discovery limits

* fix(plugin-sdk): retain provider auth removal export
2026-08-19 13:57:33 -07:00
Peter Steinberger 51599041bc fix: explain preserved session worktrees accurately (#126347)
* fix(sessions): report worktree preservation reasons

* fix(sessions): align preservation checks with current main

* perf(ui): keep preservation copy within startup budget

* perf(ui): reuse localized preservation copy

* test(ui): match preserved worktree confirmation copy

* test(ui): await committed raw config state
2026-08-19 10:59:42 -07:00