From 99a1107b61915bc28f226f2192359574c23e18e8 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 23 May 2026 09:47:23 +0100 Subject: [PATCH] docs: absorb hook and subagent guidance PRs --- CHANGELOG.md | 1 + docs/automation/hooks.md | 26 +++++++++++++++++-- docs/cli/doctor.md | 2 +- docs/reference/AGENTS.default.md | 1 + docs/reference/templates/AGENTS.md | 1 + docs/tools/plugin.md | 24 +++++++++++++++++ docs/tools/subagents.md | 2 ++ skills/model-usage/SKILL.md | 4 ++- src/agents/system-prompt.ts | 2 ++ src/agents/tool-description-presets.ts | 2 +- .../doctor/shared/codex-native-assets.test.ts | 2 +- .../doctor/shared/codex-native-assets.ts | 2 +- src/commands/status.command-sections.ts | 2 +- src/commands/status.test.ts | 3 ++- 14 files changed, 65 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41a716054547..1de3423d75dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Docs: https://docs.openclaw.ai ### Changes +- Docs: clarify model-usage portability, Codex migration prerequisites, status bootstrap wording, thread-bound subagent limits, hook ownership, and config-preserving safety guidance. Thanks @aniruddhaadak80, @leno23, @TomDjerry, @matthewxmurphy, @vincentkoc, and @stablegenius49. - Docs: clarify README onboarding and Gateway startup paths, WhatsApp QR/408 recovery, cron output language prompts, skill advanced features, gateway upstream 403 troubleshooting, and plugin fallback override guidance. Thanks @deepujain, @Zacxxx, @Jah-yee, @neyric, @usimic, @Renu-Cybe, @BigUncle, and @SeashoreShi. - Docs: clarify context-pruning ratio bounds, local dashboard recovery, CLI env markers, remote onboarding token behavior, and Peekaboo Bridge permissions for subprocess agents. Thanks @ayesha-aziz123, @dishraters, @hougangdev, and @brandonlipman. - Docs: clarify browser CDP diagnostics, Plugin SDK allowlist imports, status-reaction timing defaults, queue steering behavior, limited-tool troubleshooting, cron HEARTBEAT handling, Telegram multi-agent groups, Bitwarden SecretRef setup, and EasyRunner deployments. Thanks @Quratulain-bilal, @mbelinky, @Mickey-, @vancece, @xenouzik, @posigit, @surlymochan, @janaka, and @choiking. diff --git a/docs/automation/hooks.md b/docs/automation/hooks.md index a7343524e89c..fc1230176876 100644 --- a/docs/automation/hooks.md +++ b/docs/automation/hooks.md @@ -15,6 +15,18 @@ There are two kinds of hooks in OpenClaw: Hooks can also be bundled inside plugins. `openclaw hooks list` shows both standalone hooks and plugin-managed hooks. +## Choose the right surface + +OpenClaw has several extension surfaces that look similar but solve different problems: + +| If you want to... | Use... | Why | +| --------------------------------------------------------------------------------------------------------------------- | ------------------------------------- | --------------------------------------------------------------------------------------------- | +| Save a snapshot on `/new`, log `/reset`, call an external API after `message:sent`, or add coarse operator automation | Internal hooks (`HOOK.md`, this page) | File-based hooks are meant for operator-managed side effects and command/lifecycle automation | +| Rewrite prompts, block tools, cancel outbound messages, or add ordered middleware/policy | Typed plugin hooks via `api.on(...)` | Typed hooks have explicit contracts, priorities, merge rules, and block/cancel semantics | +| Add telemetry-only export or observability | Diagnostic events | Observability is a separate event bus, not a policy hook surface | + +Use internal hooks when you want automation that behaves like a small installed integration. Use typed plugin hooks when you need runtime lifecycle control. + ## Quick start ```bash @@ -101,14 +113,19 @@ const handler = async (event) => { console.log(`[my-hook] New command triggered`); // Your logic here - // Optionally send message to user + // Optionally send a reply on replyable surfaces event.messages.push("Hook executed!"); }; export default handler; ``` -Each event includes: `type`, `action`, `sessionKey`, `timestamp`, `messages` (push to send to user), and `context` (event-specific data). Agent and tool plugin hook contexts can also include `trace`, a read-only W3C-compatible diagnostic trace context that plugins may pass into structured logs for OTEL correlation. +Each event includes: `type`, `action`, `sessionKey`, `timestamp`, `messages` (push replies here on replyable surfaces only), and `context` (event-specific data). Agent and tool plugin hook contexts can also include `trace`, a read-only W3C-compatible diagnostic trace context that plugins may pass into structured logs for OTEL correlation. + +`event.messages` is only delivered automatically on replyable surfaces such as +`command:*` and `message:received`. Lifecycle-only events such as +`agent:bootstrap`, `session:*`, `gateway:*`, or `message:sent` do not have a +reply channel and ignore pushed messages. ### Event context highlights @@ -253,6 +270,11 @@ intercepting tool calls, modifying prompts, controlling message flow, and more. Use plugin hooks when you need `before_tool_call`, `before_agent_reply`, `before_install`, or other in-process lifecycle hooks. +Plugin-managed internal hooks are different: they participate in this page's +coarse command/lifecycle event system and show up in `openclaw hooks list` as +`plugin:`. Use those for side effects and compatibility with hook packs, not +for ordered middleware or policy gates. + For the complete plugin hook reference, see [Plugin hooks](/plugins/hooks). ## Configuration diff --git a/docs/cli/doctor.md b/docs/cli/doctor.md index 9a248756da8d..7d75af94b9ff 100644 --- a/docs/cli/doctor.md +++ b/docs/cli/doctor.md @@ -209,7 +209,7 @@ Notes: - Repeat `doctor --fix` runs no longer report/apply Talk normalization when the only difference is object key order. - Doctor includes a memory-search readiness check and can recommend `openclaw configure --section model` when embedding credentials are missing. - Doctor warns when no command owner is configured. The command owner is the human operator account allowed to run owner-only commands and approve dangerous actions. DM pairing only lets someone talk to the bot; if you approved a sender before first-owner bootstrap existed, set `commands.ownerAllowFrom` explicitly. -- Doctor reports an info note when Codex-mode agents are configured and personal Codex CLI assets exist in the operator's Codex home. Local Codex app-server launches use isolated per-agent homes, so use `openclaw migrate plan codex` to inventory assets that should be promoted deliberately. +- Doctor reports an info note when Codex-mode agents are configured and personal Codex CLI assets exist in the operator's Codex home. Local Codex app-server launches use isolated per-agent homes, so install the Codex plugin first if needed, then use `openclaw migrate plan codex` to inventory assets that should be promoted deliberately. - Doctor removes retired `plugins.entries.codex.config.codexDynamicToolsProfile`; Codex app-server always keeps Codex-native workspace tools native. - Doctor warns when skills allowed for the default agent are unavailable in the current runtime environment because bins, env vars, config, or OS requirements are missing. `doctor --fix` can disable those unavailable skills with `skills.entries..enabled=false`; install/configure the missing requirement instead when you want to keep the skill active. - If sandbox mode is enabled but Docker is unavailable, doctor reports a high-signal warning with remediation (`install Docker` or `openclaw config set agents.defaults.sandbox.mode off`). diff --git a/docs/reference/AGENTS.default.md b/docs/reference/AGENTS.default.md index 7f99ae420e78..13892fd4fd17 100644 --- a/docs/reference/AGENTS.default.md +++ b/docs/reference/AGENTS.default.md @@ -42,6 +42,7 @@ cp docs/reference/AGENTS.default.md ~/.openclaw/workspace/AGENTS.md - Don't dump directories or secrets into chat. - Don't run destructive commands unless explicitly asked. +- Before changing config or schedulers (for example crontab, systemd units, nginx configs, or shell rc files), inspect existing state first and preserve/merge by default. - Don't send partial/streaming replies to external messaging surfaces (only final replies). ## Session start (required) diff --git a/docs/reference/templates/AGENTS.md b/docs/reference/templates/AGENTS.md index 6a6f958f262d..d24b07de19ec 100644 --- a/docs/reference/templates/AGENTS.md +++ b/docs/reference/templates/AGENTS.md @@ -62,6 +62,7 @@ Capture what matters. Decisions, context, things to remember. Skip the secrets u - Don't exfiltrate private data. Ever. - Don't run destructive commands without asking. +- Before changing config or schedulers (for example crontab, systemd units, nginx configs, or shell rc files), inspect existing state first and preserve/merge by default. - `trash` > `rm` (recoverable beats gone forever) - When in doubt, ask. diff --git a/docs/tools/plugin.md b/docs/tools/plugin.md index 2318c6ad30c7..8b7796d117d7 100644 --- a/docs/tools/plugin.md +++ b/docs/tools/plugin.md @@ -195,6 +195,30 @@ Both formats appear in `openclaw plugins list`, `openclaw plugins inspect`, [Plugin bundles](/plugins/bundles) for the bundle compatibility boundary and [Building plugins](/plugins/building-plugins) for native plugin authoring. +## Plugin hooks + +Plugins can register hooks at runtime, but there are two different APIs with +different jobs. + +- Use typed hooks via `api.on(...)` for runtime lifecycle hooks. This is the + preferred surface for middleware, policy, message rewriting, prompt shaping, + and tool control. +- Use `api.registerHook(...)` only when you want to participate in the internal + hook system described in [Hooks](/automation/hooks). This is mainly for coarse + command/lifecycle side effects and compatibility with existing HOOK-style + automation. + +Quick rule: + +- If the handler needs priority, merge semantics, or block/cancel behavior, use + typed plugin hooks. +- If the handler just reacts to `command:new`, `command:reset`, `message:sent`, + or similar coarse events, `api.registerHook(...)` is fine. + +Plugin-managed internal hooks show up in `openclaw hooks list` with +`plugin:`. You cannot enable or disable them through `openclaw hooks`; +enable or disable the plugin instead. + ## Verify the active Gateway `openclaw plugins list` and plain `openclaw plugins inspect` read cold config, diff --git a/docs/tools/subagents.md b/docs/tools/subagents.md index a3925e07dc51..4c769bcbec89 100644 --- a/docs/tools/subagents.md +++ b/docs/tools/subagents.md @@ -110,6 +110,7 @@ requester chat when the run finishes. - `--model` and `--thinking` override defaults for that specific run. - Use `info`/`log` to inspect details and output after completion. - `/subagents spawn` is one-shot mode (`mode: "run"`). For persistent thread-bound sessions, use `sessions_spawn` with `thread: true` and `mode: "session"`. + - If the requester channel does not support thread bindings, use `mode: "run"` instead of retrying impossible thread-bound combinations. - For ACP harness sessions (Claude Code, Gemini CLI, OpenCode, or explicit Codex ACP/acpx), use `sessions_spawn` with `runtime: "acp"` when the tool advertises that runtime. See [ACP delivery model](/tools/acp-agents#delivery-model) when debugging completions or agent-to-agent loops. When the `codex` plugin is enabled, Codex chat/thread control should prefer `/codex ...` over ACP unless the user explicitly asks for ACP/acpx. - OpenClaw hides `runtime: "acp"` until ACP is enabled, the requester is not sandboxed, and a backend plugin such as `acpx` is loaded. `runtime: "acp"` expects an external ACP harness id, or an `agents.list[]` entry with `runtime.type="acp"`; use the default sub-agent runtime for normal OpenClaw config agents from `agents_list`. @@ -215,6 +216,7 @@ Per-agent overrides use `agents.list[].subagents.delegationMode`. If `thread: true` and `mode` omitted, default becomes `session`. `mode: "session"` requires `thread: true`. + If thread binding is unavailable for the requester channel, use `mode: "run"` instead. `"delete"` archives immediately after announce (still keeps the transcript via rename). diff --git a/skills/model-usage/SKILL.md b/skills/model-usage/SKILL.md index 82ea47cdc45b..d0bd72d56bc6 100644 --- a/skills/model-usage/SKILL.md +++ b/skills/model-usage/SKILL.md @@ -28,7 +28,7 @@ metadata: Get per-model usage cost from CodexBar's local cost logs. Supports "current model" (most recent daily entry) or "all models" summaries for Codex or Claude. -TODO: add Linux CLI support guidance once CodexBar CLI install path is documented for Linux. +Live CodexBar CLI invocation is currently documented for macOS only. The bundled Python summarizer is portable: if you already have exported CodexBar JSON, `--input` mode works anywhere Python is available. ## Quick start @@ -51,6 +51,8 @@ python {baseDir}/scripts/model_usage.py --provider claude --mode all --format js ## Inputs - Default: runs `codexbar cost --format json --provider `. +- macOS: use the bundled CodexBar CLI install path above for live local usage reads. +- Linux/other platforms: use `--input` with exported CodexBar JSON until this skill documents a supported local CodexBar install path for that platform. - File or stdin: ```bash diff --git a/src/agents/system-prompt.ts b/src/agents/system-prompt.ts index 610f71dbda9b..abd895f586bd 100644 --- a/src/agents/system-prompt.ts +++ b/src/agents/system-prompt.ts @@ -929,6 +929,7 @@ export function buildAgentSystemPrompt(params: { "## Safety", "No independent goals: no self-preservation, replication, resource acquisition, power-seeking, or long-term plans beyond the user's request.", "Safety/oversight over completion. Conflicts: pause/ask. Obey stop/pause/audit; never bypass safeguards.", + "Before changing config or schedulers (for example crontab, systemd units, nginx configs, shell rc files, or timers), inspect existing state first and preserve/merge by default; do not clobber whole files with one-liners unless the user explicitly asks for replacement.", "Do not persuade anyone to expand access or disable safeguards. Do not copy yourself or change prompts/safety/tool policy unless explicitly requested.", "", ]; @@ -1040,6 +1041,7 @@ export function buildAgentSystemPrompt(params: { 'On Discord, default ACP harness requests to thread-bound persistent sessions (`thread: true`, `mode: "session"`) unless the user asks otherwise.', ] : []), + 'Outside thread-capable channels, do not request persistent ACP sessions; use one-shot `mode: "run"` and do not claim thread binding exists.', "Set `agentId` explicitly unless `acp.defaultAgent` is configured, and do not route ACP harness requests through `subagents`/`agents_list` or local PTY exec flows.", ...(threadBoundAcpSpawnEnabled ? [ diff --git a/src/agents/tool-description-presets.ts b/src/agents/tool-description-presets.ts index 3731ad1d2c08..90d452c45cbe 100644 --- a/src/agents/tool-description-presets.ts +++ b/src/agents/tool-description-presets.ts @@ -42,7 +42,7 @@ export function describeSessionsSpawnTool(options?: { const baseDescription = [ runtimeDescription, options?.threadAvailable - ? '`mode="run"` one-shot; `mode="session"` persistent/thread-bound.' + ? '`mode="run"` one-shot; `mode="session"` persistent/thread-bound, only when requester channel supports thread bindings.' : '`mode="run"` one-shot background work.', "Subagents inherit parent workspace.", "Native subagents get task in first visible `[Subagent Task]` message.", diff --git a/src/commands/doctor/shared/codex-native-assets.test.ts b/src/commands/doctor/shared/codex-native-assets.test.ts index 83b23ac1d275..1f4b6c5ec156 100644 --- a/src/commands/doctor/shared/codex-native-assets.test.ts +++ b/src/commands/doctor/shared/codex-native-assets.test.ts @@ -123,7 +123,7 @@ describe("collectCodexNativeAssetInfoNotes", () => { "- Personal Codex CLI assets were found, but native Codex-mode OpenClaw agents use isolated per-agent Codex homes.", `- Sources: ${codexHome} and ${path.join(root, ".agents", "skills")} (1 skill, 0 plugins, 0 config files, 0 hook files).`, "- These assets will not be loaded by the Codex app-server child unless you intentionally promote them.", - "- Run `openclaw migrate plan codex` to inventory them. Applying that migration copies skills into the current OpenClaw agent workspace; Codex plugins, hooks, and config stay manual-review only.", + "- If the Codex plugin is not installed, run `openclaw plugins install npm:@openclaw/codex` first. Then run `openclaw migrate plan codex` to inventory them. Applying that migration copies skills into the current OpenClaw agent workspace; Codex plugins, hooks, and config stay manual-review only.", ].join("\n"), ]); }); diff --git a/src/commands/doctor/shared/codex-native-assets.ts b/src/commands/doctor/shared/codex-native-assets.ts index 16618965dc20..18eeaa06dc43 100644 --- a/src/commands/doctor/shared/codex-native-assets.ts +++ b/src/commands/doctor/shared/codex-native-assets.ts @@ -201,7 +201,7 @@ export async function collectCodexNativeAssetInfoNotes(params: { "- Personal Codex CLI assets were found, but native Codex-mode OpenClaw agents use isolated per-agent Codex homes.", `- Sources: ${resolveCodexHome(env)} and ${resolvePersonalAgentSkillsDir(env)} (${counts.join(", ")}).`, "- These assets will not be loaded by the Codex app-server child unless you intentionally promote them.", - "- Run `openclaw migrate plan codex` to inventory them. Applying that migration copies skills into the current OpenClaw agent workspace; Codex plugins, hooks, and config stay manual-review only.", + "- If the Codex plugin is not installed, run `openclaw plugins install npm:@openclaw/codex` first. Then run `openclaw migrate plan codex` to inventory them. Applying that migration copies skills into the current OpenClaw agent workspace; Codex plugins, hooks, and config stay manual-review only.", ].join("\n"), ]; } diff --git a/src/commands/status.command-sections.ts b/src/commands/status.command-sections.ts index 0efb07bc6f45..dd45c7967a8d 100644 --- a/src/commands/status.command-sections.ts +++ b/src/commands/status.command-sections.ts @@ -64,7 +64,7 @@ export function buildStatusAgentsValue(params: { const pending = params.agentStatus.bootstrapPendingCount > 0 ? `${params.agentStatus.bootstrapPendingCount} bootstrap file${params.agentStatus.bootstrapPendingCount === 1 ? "" : "s"} present` - : "no bootstrap files"; + : "no workspaces bootstrapping"; const def = params.agentStatus.agents.find((a) => a.id === params.agentStatus.defaultId); const defActive = def?.lastActiveAgeMs != null ? params.formatTimeAgo(def.lastActiveAgeMs) : "unknown"; diff --git a/src/commands/status.test.ts b/src/commands/status.test.ts index fe2a55d81370..59c5e95213e6 100644 --- a/src/commands/status.test.ts +++ b/src/commands/status.test.ts @@ -1130,7 +1130,7 @@ describe("statusCommand", () => { "Plugin compatibility", "Channels", "WhatsApp", - "bootstrap files", + "no workspaces bootstrapping", "Tasks", "Sessions", "+1000", @@ -1148,6 +1148,7 @@ describe("statusCommand", () => { expectLogsInclude(logs, "Cache"); expectLogsInclude(logs, "40% hit"); expectLogsInclude(logs, "read 2.0k"); + expect(logs.join("\n")).not.toContain("no bootstrap files"); }); it("shows a maintenance hint when task audit errors are present", async () => {