Add optional per-job pacing bounds across the cron API, CLI, tool schema, public output, and SQLite job envelope, requiring at least one bound. Allow only the currently running paced job to record a one-shot next_check proposal and carry it through isolated-run completion.
After successful runs, clamp the proposal to the job bounds and persist an exact one-shot slot marker so maintenance preserves only that timestamp. Clear the marker on runs, edits, and schedule normalization; preserve existing no-proposal, skip, timeout, and error scheduling behavior.
* docs: add The main session concept page and cross-links
* docs: precise memory-priming wording on the main-session page
* docs: update Control UI sidebar description to the Home and zones model
* improve(gateway): restart only the changed account on channel config reload
When a config change is scoped entirely to one channel account
(channels.{kind}.accounts.{accountId}[.*]), restart just that account
instead of the whole channel. Wholesale channel restarts disconnect every
account on the channel; on gateways running many accounts, each
account-scoped config write (adding an account, changing one account's
settings) briefly dropped every other account's connection.
The reload plan gains a restartChannelAccounts bucket populated only when
all of a channel's changed paths are account-scoped; any channel-global
path falls back to the existing wholesale restart, and a channel scheduled
for wholesale restart drops its per-account entries so each (channel,
account) pair restarts at most once. The executor runs per-account
restarts through the existing stopChannel/startChannel accountId parameter.
* fix(gateway): preserve account reload admission
* fix(gateway): scope surgical reloads to isolated plugins
* test(gateway): avoid preactivating reload snapshot
* docs(channels): define account reload isolation contract
* fix(gateway): preflight scoped account reloads
* fix(gateway): re-drain live scoped reload targets
* docs: refresh generated docs map
* fix(mattermost): align scoped reload with durable ingress
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
* perf(sqlite): gate schema version before integrity scans and quarantine terminal open failures
* feat(gateway): refuse incompatible database schemas at boot and verify integrity in the background
* feat(update): refuse installing builds that cannot open the current databases
* docs: add database schema reference with version history and downgrade guidance
* test(update): tolerate optional runner options in git-mutation mocks
* refactor(update): split npm package-target metadata out of update-check
* chore: model verifier internals for knip and regenerate docs map
* chore(i18n): resync stale iOS localization catalog
* chore: allowlist read-only preflight and verifier raw SQLite access
* feat: normalize web search output contract
* refactor(agents): promote web_search contract from a dedicated output module
* fix(agents): wrap unwrapped web_search text and pass unknown provider payloads through as raw
* fix(agents): gate web_search results branch on conforming rows and cover metadata fields
* fix(agents): gate every provider text path at the web_search boundary
* test(agents): align web_search fixtures with boundary-owned wrapping
* fix(agents): make the web_search boundary own the untrusted-content envelope
* fix(agents): report declared web_search errors first and align the documented contract
* fix(agents): bind envelope stripping to real markers and densify result rows
* fix(agents): emit canonical urls and a closed error code from web_search
* fix(agents): keep structured provider error diagnostics in the wrapped message
* fix(agents): satisfy production export and lint gates for the web_search contract
* feat(onboarding): persist app recommendations once
* feat(onboarding): add self-naming birth sequence
* chore(wizard): hoist bootstrap-defer imports to the top of the module
* docs(bootstrap): persist agreed identity into IDENTITY.md and SOUL.md as well
* docs: regenerate docs map after onboarding plan merge
* fix(wizard): reuse a pending stored offer instead of rescanning apps
* fix(onboard): deduplicate bootstrap recommendations
* feat(slack): add user-identity config surface (session token/cookie)
Introduce an account-level identity discriminator ("bot" default | "user") for
the Slack channel, plus sessionToken/sessionCookie secret fields for the
browser-session (user) path. Schema enforces structural coupling only
(identity="user" rejects appToken/signingSecret/relay/non-default mode);
credential presence is deferred to runtime like bot/app tokens. Account entries
inherit the top-level identity. Adds env fallback (SLACK_SESSION_TOKEN/COOKIE,
default account only), secret-contract registration, provenance *Source fields,
and UI hints. Config surface only; resolver/transport are later phases.
* refactor(slack): identity-aware operation-token resolver + dedup
Route Slack read/write token selection through a single identity-aware
resolveSlackOperationToken: identity="user" selects the browser-session token
for both operations (companion cookie attached later at client construction),
identity="bot" keeps the exact userToken/botToken + userTokenReadOnly behavior.
Delete the duplicated token-selection gate in action-runtime and route direct
sends/DM opens through the shared resolver with identity-neutral error wording.
Behavior-neutral for bot identity; net non-test LOC -2.
* refactor(slack): retarget user identity to xoxp + companion app
Replace the abandoned browser-session (xoxc/xoxd) approach for identity="user"
with the official path: authenticate as the human via the existing userToken
(xoxp) while a companion app (appToken/socket or signingSecret/http) carries
events. Remove sessionToken/sessionCookie config, env vars, secret-contract
entries, resolver fields, and their tests. Invert the schema coupling so
identity="user" permits the companion-app transport credentials, and drop the
now-vestigial user-identity structural guard entirely (identity is a permissive
discriminator; credential presence is a runtime concern). Resolver returns the
user token for user-identity reads and writes. Behavior-neutral for bot
identity.
* feat(slack): user-identity provider transport (xoxp + companion app)
Wire the identity="user" event transport. The companion Slack app runs Socket
Mode (appToken) or HTTP (signingSecret) while Bolt authenticates as the human
via the user token (no bot token). Startup auth.test runs on the user token and
its user_id becomes the self/mention target and self-send dedupe id (Bolt's
lazy per-event authorize populates context.botUserId=user_id, so the existing
self-event middleware drops the human's own messages). Identity-aware token
requirements and diagnostics; the bot-token identity warning is suppressed for
user identity. Behavior-neutral for identity="bot".
* feat(slack): user-identity setup, doctor, and docs
Add the setup wizard, doctor/status, and documentation for identity="user".
The wizard collects a user token plus the companion-app transport credential
(app token for socket, signing secret for http) and only persists identity when
"user" so bot setups keep byte-identical generated config; doctor validates the
user token via auth.test and warns on a missing transport credential; account
inspection reports user-token status. Message actions are gated on the active
identity's credential so user accounts expose actions. Docs add the exact
companion-app recipe (user scopes, on-behalf-of-user message events, socket/http
examples, the DM/group-DM-only-via-user-scope note, and self-send dedupe).
Behavior-neutral for identity="bot".
* test(slack): isolate user-identity provider tests to fix parallel flake
The new user-identity provider tests shared the globalThis-stored Slack test
client/handlers across files under parallel isolate:false, so their reaction
closures could reference a prior file's state and intermittently fail sibling
monitor tests (monitor.tool-result reactions). Track started monitors and
abort/await them all in afterEach (even on failed assertions), reset the shared
auth.test/mock state, and dispose the global Slack test runtime per file via a
new disposeSlackTestRuntime helper. Full Slack suite green across repeated
parallel runs.
* fix(slack): regenerate channel config metadata after rebase
* fix(slack): resolve setup consistent-return and doctor dead-export lint
* style: format install script test
* docs(slack): regenerate docs map for user-identity section
* test(slack): use renamed createSlackBoltApp token param in interop test
The rebase folded main's new wrapReceiver test into the renamed
createSlackBoltApp signature (botToken -> token); update the call site.
* feat(onboard): add browser-first guided handoff
* chore: drop release-only CHANGELOG edit from browser-handoff branch
* fix(onboard): make browser handoff reach the gateway on real installs
The presence probe connected as a raw shared-auth client with a possibly
SecretRef-managed token, which the gateway rejects as an unpaired Control UI
client ("device identity required"), so the handoff never fired and always
fell through to the terminal hatch. Read presence as a CLI-mode loopback client
(the trusted path every openclaw command uses) so operator.read is granted, and
share that one auth path between the reachability gate and the wait loop. Also
short-circuit when gateway.controlUi.enabled is false so onboarding does not open
a dead dashboard URL and block for the full timeout.
* fix(onboard): share resolved target between handoff gate and wait
The reachability preflight now resolves the same target (and shared secret) the
wait loop uses, and presents the configured secret to the CLI-mode presence
read. Known remaining limitation: on fresh quickstart gateways whose auth token
is written to config but resolved differently at runtime, the presence read is
rejected with a token mismatch and the handoff falls back to the terminal hatch.
Tracked for follow-up before enabling the browser handoff by default.
* docs(onboarding): mark phase 3 browser handoff as held with diagnosis
* docs(onboarding): phase 3 proven end-to-end; token-mismatch hold was a test artifact
* fix(onboard): satisfy docs-map and test-type gates for the handoff branch
* refactor(onboard): split guided manual stage
* feat(linux): add Quick Chat floating composer to the Tauri companion
- frameless transparent always-on-top quickchat window (on-demand, canvas
pattern), positioned top-third on the cursor monitor, CSS fade+zoom in/out
- global shortcut Ctrl+Shift+Space on X11 (GNOME reserves Alt+Space); tray
'Quick Chat' item is the Wayland entry point; blur/Esc/close-request hide
- sends via the CLI agent turn with --message-file staged 0600 (argv is
world-readable procfs) and cleaned up after the turn; failures surface as
desktop notifications; Ctrl+Enter also opens the dashboard
- default-agent identity chip (emoji/monogram) from agents list --json, 60s cache
- window-scoped capability + permission set; window-state denylist; docs
* chore(linux): register quickchat.js as a knip native-asset entry
* feat(onboarding): recommend plugins and skills from installed apps
Scan installed macOS apps during classic onboarding (TCC-free), gather
candidates from official catalogs + ClawHub search, let the configured
model pick genuine matches, and offer an opt-in multiselect install step.
Adds a device.apps node-host command (default-off sharing, Android-parity
envelope) so remote gateways can request a paired Mac's inventory, and a
wizard.appRecommendations kill switch. Custom setup-inference completions
no longer inherit the 32-token verification-probe output cap.
* feat(onboarding): recommend apps in guided flow
* fix(onboarding): harden app recommendations against ClawHub self-promotion
Third-party ClawHub skills are never pre-selected regardless of model tier
(publisher-controlled listing text reaches the matcher prompt and could
promote itself); their labels now say they install third-party code.
Installed-app scans follow symlinked .app bundles. Matcher output stays
bounded by the resolved model's own maxTokens budget (documented invariant).
* fix(onboarding): key official catalog candidates by resolved plugin id
Real catalog entries are package manifests without a top-level id; keying the
candidate map and channel/provider classification by entry.id collapsed the
whole official catalog into one undefined-keyed entry, so no official plugin
or channel was ever recommended. Regression test runs against the bundled
catalogs.
* fix(onboarding): satisfy lint, types, deadcode, and migration gates
Split the guided-onboarding test into a self-contained custodian suite to stay
under max-lines. Narrow app-recommendation exports (drop dead node-payload
normalizer, unexport internal types/helpers, route candidate tests through the
public API), replace map-spread with a helper, unexport device.apps result
types, add installedAppsSharing to node-host migration expectations, cast the
wizard multiselect mock, and regenerate the docs map.
* test(onboarding): register new live test in the shard classifier
* feat(onboard): guided onboarding becomes the custodian flow
Question zero asks for discovery consent up front (full access recommended,
guarded asks once or configures manually; persisted as wizard.accessMode and
honored on reruns). Discovery quietly collects auto-ladder failures into one
summary with details behind 'See other options', quips about detected coding
agents, and announces the working route as a default with an easy undo.
After inference and the memory offer (skipped when discovery was declined),
fresh installs auto-apply the standard setup plan and hatch the agent
directly in the terminal; configured installs (pre-existing model or gateway
config) stay verification-only and never reinstall or restart the Gateway
service. Setup-apply failures fall back to the conversational chat.
Remote-gateway onboarding keeps its legacy chat handoff explicitly.
listManualSetupInferenceOptions lists manual providers without scanning.
* docs(onboarding): add custodian redesign implementation plan (living doc)
* docs: regenerate docs map for onboarding-redesign page
* feat(mac): add Quick Chat floating composer with global shortcut
- Spotlight-style non-activating key panel on Option+Space (KeyboardShortcuts
3.0.1 exact-pinned, recorder row in Settings -> General) plus a menu bar item
- shows the main-session agent identity via agent.identity.get with a
'main session' chip; Return sends via chat.send reusing the idempotency key
on ambiguous retries; Cmd+Return also opens full chat; Shift+Return newline
- targeted permission strip (notifications/accessibility/screen recording)
with a scoped 1s status poll while visible; grant flow keeps the bar alive
and avoids PermissionMonitor's AppleScript probe
- extract shared ChatSendStatus acceptance mapping; TalkModeRuntime adopts it
- rename anchored-panel 'quick chat' wording to 'compact chat panel'; docs
* chore(mac): regenerate docs map and native i18n inventory for Quick Chat
* ci: retrigger checks after stalled push event
* chore(mac): refresh native locale artifacts for Quick Chat strings
* chore(android): regenerate locale strings for refreshed shared translations