diff --git a/CHANGELOG.md b/CHANGELOG.md index 99ae2bc526b..7201b58a72e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,7 @@ Docs: https://docs.openclaw.ai plain prompts instead of OpenClaw internal runtime-context envelopes, while keeping those envelopes out of ACP transcripts. - TTS/status: show configured TTS model, voice, and sanitized custom endpoint in `/status`, preserve OpenAI-compatible TTS instructions on custom endpoints, and retry empty Microsoft/Edge TTS output once. Addresses #46602, #47232, and #43936. Thanks @leekuangtao, @Huntterxx, and @rex993. +- Agents/Gateway: steer agent-driven config edits and restarts through the owner-only `gateway` tool, document `config.schema.lookup` as the field-doc source, and warn against using `gateway stop && gateway start` as a restart substitute on macOS. Fixes #71929. Thanks @ygc3817922006-sketch. - Providers/vLLM: send Nemotron 3 chat-template kwargs when thinking is off and honor configured `params.chat_template_kwargs` for OpenAI-compatible completions, so vLLM/Nemotron replies stay visible instead of becoming diff --git a/docs/cli/gateway.md b/docs/cli/gateway.md index 3237ac2aab4..4c2cf07954a 100644 --- a/docs/cli/gateway.md +++ b/docs/cli/gateway.md @@ -324,6 +324,7 @@ Command options: Notes: - `gateway install` supports `--port`, `--runtime`, `--token`, `--force`, `--json`. +- Use `gateway restart` to restart a managed service. Do not chain `gateway stop` and `gateway start` as a restart substitute; on macOS, `gateway stop` intentionally disables the LaunchAgent before stopping it. - When token auth requires a token and `gateway.auth.token` is SecretRef-managed, `gateway install` validates that the SecretRef is resolvable but does not persist the resolved token into service environment metadata. - If token auth requires a token and the configured token SecretRef is unresolved, install fails closed instead of persisting fallback plaintext. - For password auth on `gateway run`, prefer `OPENCLAW_GATEWAY_PASSWORD`, `--password-file`, or a SecretRef-backed `gateway.auth.password` over inline `--password`. diff --git a/docs/concepts/system-prompt.md b/docs/concepts/system-prompt.md index 40521da24d7..660632f82b9 100644 --- a/docs/concepts/system-prompt.md +++ b/docs/concepts/system-prompt.md @@ -214,6 +214,10 @@ stale. The prompt also notes the public docs mirror, community Discord, and Claw ([https://clawhub.ai](https://clawhub.ai)) for skills discovery. It tells the model to consult docs first for OpenClaw behavior, commands, configuration, or architecture, and to run `openclaw status` itself when possible (asking the user only when it lacks access). +For configuration specifically, it points agents to the `gateway` tool action +`config.schema.lookup` for exact field-level docs and constraints, then to +`docs/gateway/configuration.md` and `docs/gateway/configuration-reference.md` +for broader guidance. ## Related diff --git a/docs/gateway/configuration-reference.md b/docs/gateway/configuration-reference.md index 26a930ec22f..8c8402affee 100644 --- a/docs/gateway/configuration-reference.md +++ b/docs/gateway/configuration-reference.md @@ -16,6 +16,11 @@ Code truth: - `config.schema.lookup` returns one path-scoped schema node for drill-down tooling - `pnpm config:docs:check` / `pnpm config:docs:gen` validate the config-doc baseline hash against the current schema surface +Agent lookup path: use the `gateway` tool action `config.schema.lookup` for +exact field-level docs and constraints before edits. Use +[Configuration](/gateway/configuration) for task-oriented guidance and this page +for the broader field map, defaults, and links to subsystem references. + Dedicated deep references: - [Memory configuration reference](/reference/memory-config) for `agents.defaults.memorySearch.*`, `memory.qmd.*`, `memory.citations`, and dreaming config under `plugins.entries.memory-core.config.dreaming` diff --git a/docs/gateway/configuration.md b/docs/gateway/configuration.md index 14cd62e331c..2d944edb481 100644 --- a/docs/gateway/configuration.md +++ b/docs/gateway/configuration.md @@ -21,6 +21,11 @@ If the file is missing, OpenClaw uses safe defaults. Common reasons to add a con See the [full reference](/gateway/configuration-reference) for every available field. +Agents and automation should use `config.schema.lookup` for exact field-level +docs before editing config. Use this page for task-oriented guidance and +[Configuration reference](/gateway/configuration-reference) for the broader +field map and defaults. + **New to configuration?** Start with `openclaw onboard` for interactive setup, or check out the [Configuration Examples](/gateway/configuration-examples) guide for complete copy-paste configs. @@ -575,6 +580,11 @@ For tooling that writes config over the gateway API, prefer this flow: - `config.apply` only when you intend to replace the entire config - `update.run` for explicit self-update plus restart +Agents should treat `config.schema.lookup` as the first stop for exact +field-level docs and constraints. Use [Configuration reference](/gateway/configuration-reference) +when they need the broader config map, defaults, or links to dedicated +subsystem references. + Control-plane writes (`config.apply`, `config.patch`, `update.run`) are rate-limited to 3 requests per 60 seconds per `deviceId+clientIp`. Restart diff --git a/docs/gateway/index.md b/docs/gateway/index.md index 8af2ee77c6e..d527be86dd8 100644 --- a/docs/gateway/index.md +++ b/docs/gateway/index.md @@ -251,6 +251,8 @@ openclaw gateway restart openclaw gateway stop ``` +Use `openclaw gateway restart` for restarts. Do not chain `openclaw gateway stop` and `openclaw gateway start`; on macOS, `gateway stop` intentionally disables the LaunchAgent before stopping it. + LaunchAgent labels are `ai.openclaw.gateway` (default) or `ai.openclaw.` (named profile). `openclaw doctor` audits and repairs service config drift. diff --git a/docs/tools/index.md b/docs/tools/index.md index 53842f0c7f0..7e2d1293f01 100644 --- a/docs/tools/index.md +++ b/docs/tools/index.md @@ -95,6 +95,8 @@ active runtime model label from the latest transcript usage entry. For partial changes, prefer `config.schema.lookup` then `config.patch`. Use `config.apply` only when you intentionally replace the entire config. +For broader config docs, read [Configuration](/gateway/configuration) and +[Configuration reference](/gateway/configuration-reference). The tool also refuses to change `tools.exec.ask` or `tools.exec.security`; legacy `tools.bash.*` aliases normalize to the same protected exec paths. diff --git a/src/agents/openclaw-gateway-tool.test.ts b/src/agents/openclaw-gateway-tool.test.ts index 3a14533d021..9daffdeb3cc 100644 --- a/src/agents/openclaw-gateway-tool.test.ts +++ b/src/agents/openclaw-gateway-tool.test.ts @@ -25,6 +25,29 @@ function requireGatewayTool(agentSessionKey?: string) { }); } +function collectActionValues(schema: unknown, values: Set): void { + if (!schema || typeof schema !== "object") { + return; + } + + const record = schema as Record; + if (typeof record.const === "string") { + values.add(record.const); + } + if (Array.isArray(record.enum)) { + for (const value of record.enum) { + if (typeof value === "string") { + values.add(value); + } + } + } + if (Array.isArray(record.anyOf)) { + for (const variant of record.anyOf) { + collectActionValues(variant, values); + } + } +} + function expectConfigMutationCall(params: { callGatewayTool: { mock: { @@ -96,6 +119,19 @@ describe("gateway tool", () => { expect(tool.ownerOnly).toBe(true); }); + it("exposes restart and config actions in the gateway tool schema", async () => { + const tool = requireGatewayTool(); + const parameters = tool.parameters as { + properties?: Record; + }; + const values = new Set(); + collectActionValues(parameters.properties?.action, values); + + expect([...values]).toEqual( + expect.arrayContaining(["restart", "config.get", "config.patch", "config.apply"]), + ); + }); + it("schedules SIGUSR1 restart", async () => { const kill = vi.spyOn(process, "kill").mockImplementation(() => true); const restartSignalKillCalls = () => diff --git a/src/agents/pi-tools.create-openclaw-coding-tools.test.ts b/src/agents/pi-tools.create-openclaw-coding-tools.test.ts index f7d90fac9ab..1b4193ce1c3 100644 --- a/src/agents/pi-tools.create-openclaw-coding-tools.test.ts +++ b/src/agents/pi-tools.create-openclaw-coding-tools.test.ts @@ -86,6 +86,25 @@ function expectNoSubagentControlTools(tools: ReturnType { const testConfig: OpenClawConfig = {}; + it("exposes gateway config and restart actions to owner sessions", () => { + const tools = createOpenClawCodingTools({ config: testConfig, senderIsOwner: true }); + const gateway = tools.find((tool) => tool.name === "gateway"); + expect(gateway).toBeDefined(); + + const parameters = gateway?.parameters as { + properties?: Record; + }; + const action = parameters.properties?.action as + | { const?: unknown; enum?: unknown[] } + | undefined; + const values = new Set(); + collectActionValues(action, values); + + expect([...values]).toEqual( + expect.arrayContaining(["restart", "config.get", "config.patch", "config.apply"]), + ); + }); + it("preserves action enums in normalized schemas", () => { const defaultTools = createOpenClawCodingTools({ config: testConfig, senderIsOwner: true }); const toolNames = ["canvas", "nodes", "cron", "gateway", "message"]; diff --git a/src/agents/system-prompt.test.ts b/src/agents/system-prompt.test.ts index 35c56877dc1..f2ea67fd90e 100644 --- a/src/agents/system-prompt.test.ts +++ b/src/agents/system-prompt.test.ts @@ -226,10 +226,27 @@ describe("buildAgentSystemPrompt", () => { }); expect(prompt).toContain("## OpenClaw CLI Quick Reference"); + expect(prompt).toContain("use the first-class `gateway` tool"); + expect(prompt).toContain( + "Only use CLI service lifecycle commands when the user explicitly asks", + ); expect(prompt).toContain("openclaw gateway restart"); + expect(prompt).toContain("Do not chain `openclaw gateway stop`"); expect(prompt).toContain("Do not invent commands"); }); + it("points agents to config field docs and broader configuration docs", () => { + const prompt = buildAgentSystemPrompt({ + workspaceDir: "/tmp/openclaw", + docsPath: "/tmp/openclaw/docs", + }); + + expect(prompt).toContain("For config field docs"); + expect(prompt).toContain("`gateway` tool action `config.schema.lookup`"); + expect(prompt).toContain("docs/gateway/configuration.md"); + expect(prompt).toContain("docs/gateway/configuration-reference.md"); + }); + it("guides runtime completion events without exposing internal metadata", () => { const prompt = buildAgentSystemPrompt({ workspaceDir: "/tmp/openclaw", @@ -559,6 +576,7 @@ describe("buildAgentSystemPrompt", () => { expect(prompt).toContain("config.schema.lookup"); expect(prompt).toContain("config.apply"); expect(prompt).toContain("config.patch"); + expect(prompt).toContain("Config writes hot-reload when possible"); expect(prompt).toContain("update.run"); expect(prompt).not.toContain("Use config.schema to"); expect(prompt).not.toContain("config.schema, config.apply"); diff --git a/src/agents/system-prompt.ts b/src/agents/system-prompt.ts index 315152ab832..2f91cc155fd 100644 --- a/src/agents/system-prompt.ts +++ b/src/agents/system-prompt.ts @@ -422,6 +422,7 @@ function buildDocsSection(params: { docsPath ? "For OpenClaw behavior, commands, config, or architecture: consult local docs first." : "For OpenClaw behavior, commands, config, or architecture: consult the docs mirror first.", + "For config field docs, prefer the `gateway` tool action `config.schema.lookup`; for broader config guidance, read `docs/gateway/configuration.md` and `docs/gateway/configuration-reference.md`.", sourcePath ? "If docs are incomplete or stale, inspect the local OpenClaw source code before answering." : "If docs are incomplete or stale, review the OpenClaw source on GitHub before answering.", @@ -784,11 +785,15 @@ export function buildAgentSystemPrompt(params: { ...safetySection, "## OpenClaw CLI Quick Reference", "OpenClaw is controlled via subcommands. Do not invent commands.", - "To manage the Gateway daemon service (start/stop/restart):", + "For config changes, use the first-class `gateway` tool (`config.schema.lookup`, `config.get`, `config.patch`, `config.apply`) instead of editing config through exec; the gateway tool hot-reloads config when possible and uses a safe restart only when required.", + "Use the `gateway` tool action `restart` for Gateway restarts. Only use CLI service lifecycle commands when the user explicitly asks for them.", + "Gateway service lifecycle quick reference:", "- openclaw gateway status", + "- openclaw gateway restart", + "Operator-only, explicit user request:", "- openclaw gateway start", "- openclaw gateway stop", - "- openclaw gateway restart", + "Do not chain `openclaw gateway stop` and `openclaw gateway start` as a restart substitute.", "If unsure, ask the user to run `openclaw help` (or `openclaw gateway --help`) and paste the output.", "", ...skillsSection, @@ -800,7 +805,7 @@ export function buildAgentSystemPrompt(params: { "Get Updates (self-update) is ONLY allowed when the user explicitly asks for it.", "Do not run config.apply or update.run unless the user explicitly requests an update or config change; if it's not explicit, ask first.", "Use config.schema.lookup with a specific dot path to inspect only the relevant config subtree before making config changes or answering config-field questions; avoid guessing field names/types.", - "Actions: config.schema.lookup, config.get, config.apply (validate + write full config, then restart), config.patch (partial update, merges with existing), update.run (update deps or git, then restart).", + "Actions: config.schema.lookup, config.get, config.patch (partial update, merges with existing), config.apply (validate + write full config), update.run (update deps or git, then restart). Config writes hot-reload when possible and use a safe restart only when required.", "After restart, OpenClaw pings the last active session automatically.", ].join("\n") : "", diff --git a/src/agents/test-helpers/fast-openclaw-tools.ts b/src/agents/test-helpers/fast-openclaw-tools.ts index bfaff6c89f7..aa71bacc98c 100644 --- a/src/agents/test-helpers/fast-openclaw-tools.ts +++ b/src/agents/test-helpers/fast-openclaw-tools.ts @@ -22,7 +22,14 @@ const coreTools = [ stubActionTool("nodes", ["list", "invoke"]), stubActionTool("cron", ["schedule", "cancel"]), stubActionTool("message", ["send", "reply"]), - stubActionTool("gateway", ["status"]), + stubActionTool("gateway", [ + "restart", + "config.get", + "config.schema.lookup", + "config.apply", + "config.patch", + "update.run", + ]), stubActionTool("agents_list", ["list", "show"]), stubActionTool("sessions_list", ["list", "show"]), stubActionTool("sessions_history", ["read", "tail"]),