* 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>
* fix(ollama): skip local checks for hosted web search
* fix(ollama): explain hosted search API key requirements
* docs(ollama): separate hosted and local search setup
With agents.defaults.heartbeat.every set to "0m", a background exec completion queued its system event and requested a targeted exec-event wake, but the shared unscheduled-wake policy only admitted immediate wakes, so the follow-up agent turn silently never ran. Admit the exact exec-event/event producer shape (session or agent target required) in one per-source predicate, dispatch wake timers outside the requesting attempt's transcript-writer context, and document that "0m" disables recurring cadence only.
Closes#62505
Co-authored-by: Jason O'Neal <jason.allen.oneal@gmail.com>
Co-authored-by: Ayaan Zaidi <hi@obviy.us>
The gateway.reload documentation advertised four settings the shipped
build rejects. gateway.reload is a strictObject accepting only `mode`,
and `mode` is a union of "off" | "hybrid", so a config written from the
docs fails validation and the gateway fails closed on the unknown keys.
Retired but still documented:
- `debounceMs` and `deferralTimeoutMs` (RETIRED_TUNING_PATHS; stripped by
`openclaw doctor --fix` via stripRetiredTuningKnobs)
- `mode: "restart"` and `mode: "hot"` (mapped to "hybrid" by
`openclaw doctor --fix`)
Correct all four docs pages that still describe them, and say what
replaced them: debounce and active-work deferral now run behind built-in
defaults rather than config keys.
Refs #116973
* refactor(sessions): migrate runtime storage to sqlite
* test(sessions): fix sqlite CI regressions
* test(sessions): align remaining sqlite fixtures
* fix(codex): require sqlite trajectory recorder
* test(sessions): align orphan recovery sqlite fixture
* test(sessions): align sqlite rebase fixtures
* fix(sessions): finish current-main integration of the sqlite flip
Resolve the whole-store SDK removal across its owner boundary: drop the
loadSessionStore re-export and the registry whole-store wrappers, wire
hasTrackedActiveSessionRun into gateway chat, complete the
preserveLockedHarnessIds cleanup contract, flip the codex thread-history
import to storePath targets, and port remaining main-side tests from
file-store helpers to session accessor reads.
* chore: drop committed pebbles log, revert plugin-inspector bump, refresh generated docs
Remove the 1.8k-line .pebbles/events.jsonl work log from the branch, restore
the plugin-inspector advisory lane to main's pinned 0.3.10 so the supply-chain
bump gets its own review, and regenerate docs_map, the plugin SDK API baseline,
and the export-surface ratchet for the merged tree.
* feat(sessions): keep archived transcripts by default with zstd cold storage
Codex-style retention: deleting or resetting a session archives its
transcript as a zstd-compressed JSONL artifact (plain when the runtime
lacks node:zlib zstd) and keeps it until the disk budget evicts oldest
first. resetArchiveRetention now governs both deleted and reset archives
and defaults to keep; maxDiskBytes defaults to 2gb so retention stays
bounded, with archives evicted before live sessions. The cron reaper
follows the same knob instead of deleting archives on its own timer.
* fix(state): converge agent DB migration lineages and bound database growth
Merge coherence: run both structure-gated legacy memory-schema repairs
(flip-lineage drop, main-lineage identity rebuild) before the flip
migration so pre-flip v1/v2 and pre-merge flip v1/v4 databases all
converge, and hoist foreign_keys=OFF outside the schema transaction
where the pragma was silently ignored and the v1 sessions rebuild
cascade-deleted session_entries.
Growth guards: fresh agent DBs enable auto_vacuum=INCREMENTAL, WAL
maintenance releases freed pages in bounded passes (never a blocking
full VACUUM), and doctor reports state/agent DB bloat from freelist
stats.
* fix(codex): resolve the store path for thread-history import via the SDK
The supervision catalog passed the legacy sessionFile locator to the
storePath-targeted transcript mirror; resolve the agent store path with
the session-store SDK helper instead of a runtime-object seam so test
fakes and headless callers need no extra surface. Drop the obsolete
missing-session-id preprocessing case: sessions rows are NOT NULL on
session_id and upsert repairs id-less patches at write time.
* fix(sessions): fail safe on malformed disk-budget config and doctor stat errors
A malformed explicit maxDiskBytes disables the budget instead of
falling back to the destructive 2gb default the user never chose, and
the doctor bloat check skips databases whose paths stat-fail instead of
aborting doctor.
* fix(sessions): complete sqlite conflict translations
* test(sqlite): align hardening checks with maintenance
* test(sessions): inspect compressed transcript archives
* fix(tests): await session seeds and drop unused helpers flagged by CI lint
The five unawaited writeSessionStoreSeed calls raced their SQLite seeds
against the assertions, failing compact shards; the bloat probe drops a
useless initializer and the merged tests drop now-unused helpers.
* test(sessions): type legacy proof events directly
* test(sessions): align hardening contracts
* perf(sessions): read usage transcript sizes from SQL aggregates
Usage/cost scans walked every session and materialized every transcript
event just to re-stringify it for a byte estimate — the #86718 stall
class reborn on the DB. readTranscriptStatsSync sums stored JSON bytes
in SQLite without loading a single row.
* fix(sessions): re-root foreign-root transcript paths onto the current sessions dir
Restored backups, moved OPENCLAW_STATE_DIR, and rehearsal copies carry
absolute sessionFile paths from the old root; the containment fallback
kept those foreign paths, so migration read (and would archive) files in
the original root and reported local copies missing. Re-root the
canonical agents/<id>/sessions suffix onto the current dir when the file
exists there; genuine cross-root layouts still fall through unchanged.
* test(agents): seed harness admission through sqlite
* fix(sqlite): close agent db on pragma setup failure
* fix(doctor): compact and retrofit incremental auto-vacuum after session import
The migration is the sanctioned offline window: post-import compact
reclaims import churn and applies auto_vacuum=INCREMENTAL to databases
created before the fresh-DB pragma existed, so runtime maintenance can
release pages in bounded passes on every install.
---------
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Update OpenClaw ClawHub docs and user-facing copy for canonical owner-qualified skill routes.\n\nEvidence:\n- pnpm docs:list\n- pnpm test src/plugins/clawhub.test.ts src/cli/plugins-cli.install.test.ts src/gateway/server-methods/skills.clawhub.test.ts ui/src/ui/views/skills.test.ts\n- pnpm exec oxfmt --check --threads=1 docs/clawhub/cli.md docs/clawhub/publishing.md docs/cli/skills.md docs/help/faq.md docs/start/showcase.md docs/tools/creating-skills.md docs/tools/skills.md src/gateway/server-methods/skills.clawhub.test.ts src/plugins/clawhub.test.ts src/plugins/clawhub.ts ui/src/ui/views/skills.test.ts\n- git diff --check\n- exact-head hosted CI passed for 8530374388d8a73235b2ac8444b95a4a4c7d0f1c\n\nNote: repo-native scripts/pr prepare-run was attempted; local broad pnpm test was stopped after unrelated existing failures in agent/media/provider shards, while hosted exact-head CI and targeted ClawHub route/copy validation were green.
* feat(browser): add optional vision understanding to screenshot tool
* fix(browser): wrap vision output as external content, enforce maxBytes, forward auth profiles
* fix(browser): remove no-op scope/attachments config, drop profile pass-through lacking runtime support
* feat(media-understanding): add profile/preferredProfile to DescribeImageFileWithModelParams and forward to describeImage
* style(browser): add curly braces to satisfy eslint curly rule
* fix(browser): correct tools.browser.enabled help text to match actual behavior
* fix(browser): thread agentDir/workspaceDir from plugin tool context into browser vision
* refactor(browser): move vision config from tools.browser to browser.models
The browser plugin's vision configuration now lives on the top-level
`browser` config namespace (browser.models, browser.visionEnabled,
browser.visionPrompt, etc.) instead of `tools.browser`. This aligns
with the plugin's existing config location and avoids confusion between
tool-level and plugin-level settings.
- Remove tools.browser from ToolsSchema and ToolsConfig
- Add models/vision* fields to BrowserConfig and its zod schema
- Update getBrowserVisionConfig to read from cfg.browser
- Update schema help, labels, and quality test
- Update vision.test.ts to use new config shape
* docs(browser): add screenshot vision configuration section
Document the new browser.models config for automatic screenshot
description via vision models, enabling text-only main models to
reason about web page content.
* fix(browser): remove deliverable media markers from vision result, drop unused import
P1: Vision-success path no longer exposes the raw screenshot as
deliverable media (removes MEDIA: line and details.media.mediaUrl).
This prevents channel delivery from auto-sending sensitive page content
when the intended output is a text description.
P2: Remove unused ToolsMediaUnderstandingSchema import that would fail
noUnusedLocals typecheck.
* fix(browser): add command/args fields to browser models schema
The browser vision model schema uses .strict(), so CLI-type entries
with command/args were rejected by TypeScript. Add these fields to
align with MediaUnderstandingModelSchema.
* chore(browser): remove debug console.log statements
* fix(browser): harden screenshot vision result against MEDIA: directive injection and restore image sanitization on failure fallback
ClawSweeper #84247 review round 2:
P1 (security, high): neutralize line-start MEDIA: directives in vision descriptions
before wrapping with wrapExternalContent. The agent media extractor scans every
browser tool-result text block via splitMediaFromOutput which treats line-start
MEDIA: as a trusted local-media delivery directive, and browser is on the
trusted-media allowlist. Without neutralization, page or vision-provider output
containing 'MEDIA:/tmp/secret.png' could synthesize a channel-deliverable media
artifact from untrusted content. wrapExternalContent itself does not strip
line-start directives. Introduce neutralizeMediaDirectives in vision.ts that
prepends '[neutralized] ' to any line whose trimStart() begins with MEDIA:
(case-insensitive), defanging the parser anchor while keeping the original
text human-readable.
P2 (compatibility): pass resolveRuntimeImageSanitization() to imageResultFromFile
in the vision-failure catch fallback. The non-vision screenshot path already
forwards this option (d5cc0d53b7) so configured agents.defaults.imageMaxDimensionPx
takes effect. Without this fix, any provider timeout/error silently bypasses the
sanitization guard and returns a raw full-resolution screenshot.
Regression coverage:
- vision.test.ts: 6 unit cases for neutralizeMediaDirectives (no-op fast path,
mid-line MEDIA: untouched, line-start defanged, leading-whitespace defanged,
case-insensitive, multiple directives per blob).
- browser-tool.test.ts: 2 integration cases that drive the full screenshot
tool execute path:
- 'neutralizes MEDIA: directives in vision text and does not attach media'
asserts no line matches /^\s*MEDIA:/i in returned text, secret path text
is preserved verbatim, details.media is absent, and imageResultFromFile
is not called on the success path.
- 'preserves screenshot image sanitization on vision failure fallback'
mocks describeImageFileWithModel to reject and asserts the fallback
imageResultFromFile call receives imageSanitization: {maxDimensionPx:1600}
plus the 'browser screenshot vision failed' extraText.
* fix(browser): apply clawsweeper fallback media fix from PR #84247
* refactor: reuse media image understanding for browser screenshots
* refactor: use structured media delivery
* test: update music completion media instruction expectation
* fix: trim buffered reply directive padding
* test: refresh codex prompt snapshots for message media aliases
---------
Co-authored-by: scotthuang <scotthuang@tencent.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
Summary:
- Adds `--global` to `openclaw skills install` and `openclaw skills update`, routing ClawHub installs and updates to the shared managed skills root with docs, changelog, and CLI command tests.
- Reproducibility: not applicable. as a bug reproduction; this is a new CLI feature request. Source inspection confirms current `main` lacks `--global`, and the source PR includes after-fix terminal proof for the new path.
Automerge notes:
- PR branch already contained follow-up commit before automerge: fix(cli): address skills global review
- PR branch already contained follow-up commit before automerge: feat(cli): support installing skills to shared global directory via -…
Validation:
- ClawSweeper review passed for head 6eb7187fc1.
- Required merge gates passed before the squash merge.
Prepared head SHA: 6eb7187fc1
Review: https://github.com/openclaw/openclaw/pull/83705#issuecomment-4480023577
Co-authored-by: Hongwei Ma <marvae24@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
feat(docker): add image apt package build arg
Add OPENCLAW_IMAGE_APT_PACKAGES as the preferred runtime-neutral image build arg for Docker and Podman apt package installs while keeping OPENCLAW_DOCKER_APT_PACKAGES as the legacy fallback.
Maintainer verification:
- pnpm docs:list
- node scripts/run-vitest.mjs run --config test/vitest/vitest.e2e.config.ts src/docker-setup.e2e.test.ts
- node scripts/run-vitest.mjs src/dockerfile.test.ts test/scripts/test-install-sh-docker.test.ts
- node scripts/run-vitest.mjs run --config test/vitest/vitest.cron.config.ts src/cron/isolated-agent.model-overrides.test.ts
- pnpm exec oxfmt --check --threads=1 docs/install/docker.md docs/install/podman.md scripts/clawdock/README.md docs/help/faq.md CHANGELOG.md
- git diff --check origin/main...HEAD
- .agents/skills/autoreview/scripts/autoreview --mode local
- .agents/skills/autoreview/scripts/autoreview --mode branch
- pnpm check:changed via Blacksmith Testbox tbx_01krwqmfhcdekaczvrkxnb7t59, Actions run 26014630478, exit 0
Known CI note: checks-node-core-runtime-shared timed out repeatedly in unrelated src/cron/isolated-agent.model-overrides.test.ts on GitHub Actions; the same test passes locally after this rebase.
Co-authored-by: Said Urtabajev <said@bumpclub.ee>