Skip to content

Commit 4bf94aa

Browse files
authored
feat: add local exec-policy CLI (#64050)
* feat: add local exec-policy CLI * fix: harden exec-policy CLI output * fix: harden exec approvals writes * fix: tighten local exec-policy sync * docs: document exec-policy CLI * fix: harden exec-policy rollback and approvals path checks * fix: reject exec-policy sync when host remains node * fix: validate approvals path before mkdir * fix: guard exec-policy rollback against newer approvals writes * fix: restore exec approvals via hardened rollback path * fix: guard exec-policy config writes with base hash * docs: add exec-policy changelog entry * fix: clarify exec-policy show for node host * fix: strip stale exec-policy decisions
1 parent 2d126fc commit 4bf94aa

14 files changed

Lines changed: 1256 additions & 18 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ Docs: https://docs.openclaw.ai
1111
- Docs i18n: chunk raw doc translation, reject truncated tagged outputs, avoid ambiguous body-only wrapper unwrapping, and recover from terminated Pi translation sessions without changing the default `openai/gpt-5.4` path. (#62969, #63808) Thanks @hxy91819.
1212
- QA/testing: add a `--runner multipass` lane for `openclaw qa suite` so repo-backed QA scenarios can run inside a disposable Linux VM and write back the usual report, summary, and VM logs. (#63426) Thanks @shakkernerd.
1313
- Gateway: split startup and runtime seams so gateway lifecycle sequencing, reload state, and shutdown behavior stay easier to maintain without changing observed behavior. (#63975) Thanks @gumadeiras.
14+
- CLI/exec policy: add a local `openclaw exec-policy` command with `show`, `preset`, and `set` subcommands for synchronizing requested `tools.exec.*` config with the local exec approvals file, plus follow-up hardening for node-host rejection, rollback safety, and sync conflict detection.
1415
- Models/providers: add per-provider `models.providers.*.request.allowPrivateNetwork` for trusted self-hosted OpenAI-compatible endpoints, keep the opt-in scoped to model request surfaces, and refresh cached WebSocket managers when request transport overrides change. (#63671) Thanks @qas.
1516

1617
### Fixes

docs/cli/approvals.md

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
summary: "CLI reference for `openclaw approvals` (exec approvals for gateway or node hosts)"
2+
summary: "CLI reference for `openclaw approvals` and `openclaw exec-policy`"
33
read_when:
44
- You want to edit exec approvals from the CLI
55
- You need to manage allowlists on gateway or node hosts
@@ -18,6 +18,45 @@ Related:
1818
- Exec approvals: [Exec approvals](/tools/exec-approvals)
1919
- Nodes: [Nodes](/nodes)
2020

21+
## `openclaw exec-policy`
22+
23+
`openclaw exec-policy` is the local convenience command for keeping the requested
24+
`tools.exec.*` config and the local host approvals file aligned in one step.
25+
26+
Use it when you want to:
27+
28+
- inspect the local requested policy, host approvals file, and effective merge
29+
- apply a local preset such as YOLO or deny-all
30+
- synchronize local `tools.exec.*` and local `~/.openclaw/exec-approvals.json`
31+
32+
Examples:
33+
34+
```bash
35+
openclaw exec-policy show
36+
openclaw exec-policy show --json
37+
38+
openclaw exec-policy preset yolo
39+
openclaw exec-policy preset cautious --json
40+
41+
openclaw exec-policy set --host gateway --security full --ask off --ask-fallback full
42+
```
43+
44+
Output modes:
45+
46+
- no `--json`: prints the human-readable table view
47+
- `--json`: prints machine-readable structured output
48+
49+
Current scope:
50+
51+
- `exec-policy` is **local-only**
52+
- it updates the local config file and the local approvals file together
53+
- it does **not** push policy to the gateway host or a node host
54+
- `--host node` is rejected in this command because node exec approvals are fetched from the node at runtime and must be managed through node-targeted approvals commands instead
55+
- `openclaw exec-policy show` marks `host=node` scopes as node-managed at runtime instead of deriving an effective policy from the local approvals file
56+
57+
If you need to edit remote host approvals directly, keep using `openclaw approvals set --gateway`
58+
or `openclaw approvals set --node <id|name|ip>`.
59+
2160
## Common commands
2261

2362
```bash
@@ -100,6 +139,16 @@ Why `tools.exec.host=gateway` in this example:
100139

101140
This matches the current host-default YOLO behavior. Tighten it if you want approvals.
102141

142+
Local shortcut:
143+
144+
```bash
145+
openclaw exec-policy preset yolo
146+
```
147+
148+
That local shortcut updates both the requested local `tools.exec.*` config and the
149+
local approvals defaults together. It is equivalent in intent to the manual two-step
150+
setup above, but only for the local machine.
151+
103152
## Allowlist helpers
104153

105154
```bash

docs/tools/exec-approvals.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ session or config defaults request `ask: "on-miss"`.
2020
Use `openclaw approvals get`, `openclaw approvals get --gateway`, or
2121
`openclaw approvals get --node <id|name|ip>` to inspect the requested policy,
2222
host policy sources, and the effective result.
23+
For the local machine, `openclaw exec-policy show` exposes the same merged view and
24+
`openclaw exec-policy set|preset` can synchronize the local requested policy with the
25+
local host approvals file in one step. When a local scope requests `host=node`,
26+
`openclaw exec-policy show` reports that scope as node-managed at runtime instead of
27+
pretending the local approvals file is the effective source of truth.
2328

2429
If the companion app UI is **not available**, any request that requires a prompt is
2530
resolved by the **ask fallback** (default: deny).
@@ -143,6 +148,21 @@ openclaw approvals set --stdin <<'EOF'
143148
EOF
144149
```
145150

151+
Local shortcut for the same gateway-host policy on the current machine:
152+
153+
```bash
154+
openclaw exec-policy preset yolo
155+
```
156+
157+
That local shortcut updates both:
158+
159+
- local `tools.exec.host/security/ask`
160+
- local `~/.openclaw/exec-approvals.json` defaults
161+
162+
It is intentionally local-only. If you need to change gateway-host or node-host approvals
163+
remotely, continue using `openclaw approvals set --gateway` or
164+
`openclaw approvals set --node <id|name|ip>`.
165+
146166
For a node host, apply the same approvals file on that node instead:
147167

148168
```bash
@@ -158,6 +178,12 @@ openclaw approvals set --node <id|name|ip> --stdin <<'EOF'
158178
EOF
159179
```
160180

181+
Important local-only limitation:
182+
183+
- `openclaw exec-policy` does not synchronize node approvals
184+
- `openclaw exec-policy set --host node` is rejected
185+
- node exec approvals are fetched from the node at runtime, so node-targeted updates must use `openclaw approvals --node ...`
186+
161187
Session-only shortcut:
162188

163189
- `/exec security=full ask=off` changes only the current session.

extensions/msteams/src/attachments.helpers.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -214,9 +214,7 @@ describe("msteams attachment helpers", () => {
214214
messageId: "msg-1",
215215
});
216216
expect(urls).toHaveLength(1);
217-
expect(urls[0]).toContain(
218-
"/chats/19%3Areal-graph-chat-id%40unq.gbl.spaces/messages/msg-1",
219-
);
217+
expect(urls[0]).toContain("/chats/19%3Areal-graph-chat-id%40unq.gbl.spaces/messages/msg-1");
220218
});
221219

222220
it("still builds URLs when a: conversation ID is passed (caller did not resolve)", () => {

extensions/msteams/src/monitor-handler/message-handler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,13 @@ import {
2525
import { isRecord } from "../attachments/shared.js";
2626
import type { StoredConversationReference } from "../conversation-store.js";
2727
import { formatUnknownError } from "../errors.js";
28-
import { resolveGraphChatId } from "../graph-upload.js";
2928
import {
3029
fetchChannelMessage,
3130
fetchThreadReplies,
3231
formatThreadContext,
3332
resolveTeamGroupId,
3433
} from "../graph-thread.js";
34+
import { resolveGraphChatId } from "../graph-upload.js";
3535
import {
3636
extractMSTeamsConversationMessageId,
3737
extractMSTeamsQuoteInfo,

src/agents/subagent-registry.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,10 @@ async function sweepSubagentRuns() {
485485
// Session-mode runs have no archiveAtMs — apply absolute TTL after cleanup completes.
486486
// Use cleanupCompletedAt (not endedAt) to avoid interrupting deferred cleanup flows.
487487
if (!entry.archiveAtMs) {
488-
if (typeof entry.cleanupCompletedAt === "number" && now - entry.cleanupCompletedAt > SESSION_RUN_TTL_MS) {
488+
if (
489+
typeof entry.cleanupCompletedAt === "number" &&
490+
now - entry.cleanupCompletedAt > SESSION_RUN_TTL_MS
491+
) {
489492
clearPendingLifecycleError(runId);
490493
void notifyContextEngineSubagentEnded({
491494
childSessionKey: entry.childSessionKey,

0 commit comments

Comments
 (0)