#What this config do ?
1 messages · Page 1 of 1 (latest)
In plain English, this says:
-
tools.elevated.enabled: true
Elevated exec is allowed, meaning a permitted user can tell a sandboxed agent to run commands outside the sandbox. -
tools.elevated.allowFrom.discord: ["something"]
Only matching Discord senders can use elevated mode.
somethingneeds to be a real matcher, usually a user id or a prefixed matcher likeid:...,username:...,name:..., etc. -
tools.exec.host: "gateway"
execruns on the gateway machine. -
tools.exec.security: "full"
No allowlist restriction, exec is fully open. -
tools.exec.ask: "off"
No approval prompt before running commands.
tools.loopDetection.enabled: true
Turns on protection against agents getting stuck in repetitive tool-call loops.
So the overall effect is basically:
“Allow a specific Discord user to use elevated exec, and let exec run directly on the gateway with full access and no approval prompts, while loop detection is on.”
The important security bit is this combo:
host: "gateway"security: "full"ask: "off"
That is effectively YOLO shell access for the agent on the gateway. Fine for a trusted private setup, risky for anything broader.
Docs:
- https://docs.openclaw.ai/tools/exec
- https://docs.openclaw.ai/tools/loop-detection
- https://docs.openclaw.ai/gateway/sandbox-vs-tool-policy-vs-elevated
If you want, I can also rewrite this into a “safe version” and an “unsafe but convenient version”.
"acp": {
"stream": {
"deliveryMode": "live",
"coalesceIdleMs": 300,
"maxChunkChars": 1200,
"tagVisibility": {
"tool_call": true,
"tool_call_update": true
}
},
"runtime": {
"ttlMinutes": 240
}
},
This block is for ACP sessions only, like /acp spawn codex or sessions_spawn({ runtime: "acp" }). It does not change normal non-ACP chat behavior.
What each part does:
-
acp.stream.deliveryMode: "live"
Stream ACP output as it happens instead of waiting until the run is finished. -
acp.stream.coalesceIdleMs: 300
Buffer tiny streaming updates and flush after about 300ms of idle time, so it feels live without spamming every token. -
acp.stream.maxChunkChars: 1200
Split long streamed output into chunks of about 1200 characters max. -
acp.stream.tagVisibility.tool_call: true
Show visible updates when the ACP harness starts a tool call. -
acp.stream.tagVisibility.tool_call_update: true
Show visible tool progress/update events too.
acp.runtime.ttlMinutes: 240
Keep idle ACP runtime workers/sessions around for up to 240 minutes (4 hours) before they become eligible for cleanup.
So the plain-English summary is:
“For ACP harnesses, stream updates live, batch them a little, show tool activity in chat, and keep idle ACP sessions warm for 4 hours.”
Docs: https://docs.openclaw.ai/tools/acp-agents
If you want, I can also explain the difference between ACP, subagents, and the normal main agent, because that part trips a lot of people up.
"plugins": {
"entries": {
"acpx": {
"enabled": true,
"config": {
"permissionMode": "approve-all",
"timeoutSeconds": 3600
}
}
}
},
This is the ACPX plugin config, which affects ACP harness sessions like Codex / Claude Code / Gemini spawned through ACP.
What it does:
-
plugins.entries.acpx.enabled: true
Enables the bundled ACP runtime backend plugin. -
plugins.entries.acpx.config.permissionMode: "approve-all"
This is the big one. It auto-approves all file writes and shell commands for ACP sessions.
Basically, ACP harnesses won’t stop to ask for permission. -
plugins.entries.acpx.config.timeoutSeconds: 3600
Lets an ACP runtime turn run for up to 3600 seconds (1 hour) before timing out.
Default is much lower, around 120 seconds.
Important nuance:
- This is separate from normal OpenClaw
execapprovals. - It only matters for ACP sessions, not regular agent replies.
approve-allis the ACP-side break-glass / YOLO setting, so only use it if you trust that harness.
So plain English:
“Enable the ACP backend, let ACP agents write files and run commands without prompting, and give them up to 1 hour per run.”
Docs: https://docs.openclaw.ai/tools/acp-agents
If you want, I can combine all 3 snippets you posted into one “overall security meaning” summary.
is this a valid config ?
"channels": {
"discord": {
"enabled": true,
"token": {
"source": "env",
"provider": "default",
"id": "DISCORD_BOT_TOKEN"
},
"groupPolicy": "allowlist",
"guilds": {
"1492415216963817495": {
"requireMention": false
}
},
"markdown": {
"tables": "block"
},
"status": "online",
"ackReactionScope": "all",
"threadBindings": {
"spawnAcpSessions": true,
"enabled": true
},
"streaming": "block",
"draftChunk": {
"minChars": 200,
"maxChars": 800,
"breakPreference": "paragraph"
}
}
markdown tables block ?
Mostly yes, with 3 notes:
-
As pasted, it’s not complete JSON/JSON5
You’re missing the final closing braces for the outer object. -
markdown.tables: "block"is schema-valid, but right now it effectively resolves to"code"at runtime.
So for Discord,"block"won’t give you some special extra table mode today. If you want predictable behavior, use:markdown: { tables: "code" } -
streaming: "block"anddraftChunkare legacy flat forms
They’re still recognized/migrated on current builds, but the preferred modern shape is nested.
So I’d write it like this instead:
{
channels: {
discord: {
enabled: true,
token: {
source: "env",
provider: "default",
id: "DISCORD_BOT_TOKEN",
},
groupPolicy: "allowlist",
guilds: {
"1492415216963817495": {
requireMention: false,
},
},
markdown: {
tables: "code", // "block" is accepted but currently behaves like "code"
},
status: "online",
ackReactionScope: "all",
threadBindings: {
enabled: true,
spawnAcpSessions: true,
},
streaming: {
mode: "block",
preview: {
chunk: {
minChars: 200,
maxChars: 800,
breakPreference: "paragraph",
},
},
},
},
},
}
A couple quick clarifiers:
guilds.<id>.requireMention: falseis valid, and applies to the whole guild.- If you only want specific channels in that guild, use:
guilds: {
"guild-id": {
channels: {
"channel-id": { requireMention: false },
},
},
}
Docs:
- https://docs.openclaw.ai/concepts/markdown-formatting
- https://docs.openclaw.ai/concepts/streaming
- https://docs.openclaw.ai/tools/acp-agents
If you want, I can turn your full config into a cleaned-up known-good JSON5 block.
how do i assign a default CWD based on discord channel name ?
Short version: don’t do it by channel name, do it by channel ID.
There are 2 clean ways:
1) Normal OpenClaw agent
For normal agent sessions, the default CWD = the agent workspace.
So if you want different Discord channels to use different default CWDs, make one agent per repo/workspace and bind each channel to that agent.
{
agents: {
list: [
{
id: "repo-a",
workspace: "/workspace/repo-a",
},
{
id: "repo-b",
workspace: "/workspace/repo-b",
},
],
},
bindings: [
{
agentId: "repo-a",
match: {
channel: "discord",
accountId: "default",
peer: { kind: "channel", id: "123456789012345678" }, // Discord channel ID
},
},
{
agentId: "repo-b",
match: {
channel: "discord",
accountId: "default",
peer: { kind: "channel", id: "234567890123456789" },
},
},
],
}
2) ACP sessions (Codex / Claude Code / Gemini)
If you’re using ACP, bind the channel and set acp.cwd:
{
bindings: [
{
type: "acp",
agentId: "codex",
match: {
channel: "discord",
accountId: "default",
peer: { kind: "channel", id: "123456789012345678" },
},
acp: {
cwd: "/workspace/repo-a",
},
},
],
}
Important note
- Channel name is a bad key because it can change.
- Use the Discord channel ID instead.
So the real answer is:
You don’t assign per-channel default CWD directly by channel name.
You route that channel to an agent with the right workspace, or for ACP usebindings[].acp.cwd.
Docs:
If you want, I can write the exact config for your Discord channel IDs.
if my primary use case is to activate acp in a thread, does the default CWD default to the parent channel ?
No, the default ACP cwd does not come from the parent Discord channel or thread.
For ACP:
- the chat surface (channel/thread) is just the routing/binding target
- the runtime workspace /
cwdis separate
If you don’t pass --cwd, OpenClaw uses:
bindings[].acp.cwdif set- otherwise
agents.list[].runtime.acp.cwdif set - otherwise it inherits the target agent’s workspace
- if that inherited workspace is missing, it falls back to the backend default cwd
So thread activation does not automatically map to parent channel name/id → cwd.
Plain English:
ACP in a Discord thread stays bound to that thread for chat routing, but its filesystem cwd comes from ACP config / agent workspace, not from the thread’s parent channel.
Docs: https://docs.openclaw.ai/tools/acp-agents
If you want, I can show the exact config for “each Discord thread in this channel should start Codex in /workspace/my-repo”.
can the config match on a regex match channel name instead of channel id ? I often reset the channel and want the cwd to stick
No, not for ACP/channel bindings.
For this use case, bindings are by exact conversation identity, so for Discord that means channel/thread ID, not regex on channel name.
A few practical notes:
- Rename existing channel → fine, the ID stays the same, so the binding still works.
- Delete + recreate channel → new channel = new ID, so the old binding will not carry over.
dangerouslyAllowNameMatchingis not this. That’s about mutable user/name matching, not binding ACPcwdby channel name.
If your goal is “keep the same cwd when I reset workspaces”, the best pattern is:
- Keep a stable parent Discord channel
- Bind that channel to ACP with
acp.cwd - Work inside threads under that parent channel
That works well because thread messages can inherit the parent channel ACP binding, so the cwd can stick from the parent channel setup.
So the recommendation is:
Use a stable parent channel ID, not regex on channel name.
Then let threads inherit that ACP binding.
Docs:
- https://docs.openclaw.ai/tools/acp-agents
- https://docs.openclaw.ai/channels/discord
- https://docs.openclaw.ai/gateway/configuration-reference
If you want, I can sketch the exact “parent channel bound to /workspace/repo, threads inherit it” config.
no, thats not what i want. how about we find another way to do this. how do execute the same, update the current directory using a discord slash / command. for example, i want to make /abc go to 'my-project/abc'
Got it, that makes sense.
The short answer is: not with built-in config alone.
There is a built-in slash command for this:
/acp cwd /absolute/path/to/my-project/abc
But there is not a native config feature that says:
/abc => /acp cwd /.../my-project/abc
What is supported today
Built-in
Use the ACP command directly:
/acp spawn codex --bind here
/acp cwd /workspace/my-project/abc
(cwd should be an absolute path.)
If you want /abc specifically
You’d need a custom extension, not just config. Realistically that means one of these:
-
Custom plugin slash command
Best if you want several shortcuts like/abc,/xyz,/docs. -
User-invocable skill slash command
Possible as a custom command surface, but for actually setting ACP cwd cleanly you’d still usually want a custom backend/plugin path.
Recommendation
If your goal is just fast switching, I’d do one of these:
- Simple / supported: use
/acp cwd /workspace/my-project/abc - Ergonomic / custom: add a tiny plugin command like
/abc
So, plain English:
OpenClaw already supports changing ACP cwd from Discord with
/acp cwd ..., but custom aliases like/abcare not a built-in config feature.
Docs:
If you want, I can sketch the smallest possible plugin approach for /abc.
yes show me what that means, the smallest possible plugin approach
In plain English, that config means:
-
tools.elevated.enabled: true
Elevated mode is available. -
tools.elevated.allowFrom.discord: ["something"]
Only matching Discord senders can use elevated mode.
("something"needs to be a real matcher, usually a user id orid:.../username:.../name:...style entry.) -
tools.exec.host: "gateway"
execruns on the gateway machine by default. -
tools.exec.security: "full"
Host exec is not restricted to an allowlist. -
tools.exec.ask: "off"
No approval prompts.
tools.loopDetection.enabled: true
Turns on anti-stuck repeated-tool-call detection.
Net effect
This is basically:
“Let allowed Discord users run commands on the gateway, with full access, and no approval prompt.”
That is pretty high-trust / YOLO config. I would only do that on a private setup I fully trust.
Small nuance: elevated mainly matters when the agent is sandboxed. If the agent is already unsandboxed, exec is already on the host.
Smallest possible plugin approach
If your goal is a custom command like /abc, this cannot be done by config alone.
The smallest real approach is a tiny plugin with one registerCommand().
index.ts
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
export default definePluginEntry({
id: "cwd-shortcuts",
name: "CWD Shortcuts",
description: "Tiny custom commands",
register(api) {
api.registerCommand({
name: "abc",
description: "Custom shortcut",
acceptsArgs: false,
handler: async (_ctx) => {
// put your real logic here
return { text: "hello from /abc" };
},
});
},
});
openclaw.plugin.json
{
"id": "cwd-shortcuts",
"name": "CWD Shortcuts",
"description": "Tiny custom commands"
}
package.json
{
"name": "@you/openclaw-cwd-shortcuts",
"version": "1.0.0",
"type": "module",
"openclaw": {
"extensions": ["./index.ts"]
}
}
Important part
If you specifically want:
/abc -> /acp cwd /some/path
there is no built-in config alias for that.
So the plugin handler is where you’d implement that behavior.
Also, for it to show as a native Discord slash command, native commands need to be on:
commands.native: "auto"orchannels.discord.commands.native: true
Docs:
If you want, I can show the next-smallest actual /abc -> fixed ACP cwd plugin skeleton too.
can i store this plugin in the workspace to i can easily commit it ?
Yes, absolutely.
The clean way is:
- put the plugin in your normal git repo/workspace
- install it into OpenClaw from that local path
- commit it like any other project code
Best dev workflow
Keep it somewhere like:
your-repo/
plugins/
cwd-shortcuts/
package.json
openclaw.plugin.json
index.ts
Then install it as a linked local plugin:
openclaw plugins install -l ./plugins/cwd-shortcuts
openclaw gateway restart
Why -l is nice
openclaw plugins install ./path= copies itopenclaw plugins install -l ./path= links it for dev
So if you want “edit in workspace, commit in git, easy iteration”, use -l.
My recommendation
- Yes, store it in your workspace/repo
- Use
openclaw plugins install -l <path> - restart the gateway after changes so the new plugin code gets picked up
Docs:
If you want, I can sketch the exact folder layout for a tiny /abc plugin next.
what should the plugin code be to set the current (non acp CWD)
Short answer: you can’t supportedly set a persistent “current cwd” for a normal non-ACP chat session.
For normal chat, OpenClaw uses the agent workspace as the agent’s only working directory for tools/context. ACP is the one that has a mutable session cwd.
So:
- non-ACP main session → fixed to
agents.defaults.workspace(or that agent’s workspace) - ACP session → supports
/acp cwd ... - subagent/acp child sessions → can carry a spawned workspace override
- main normal session → no per-session cwd setter
There’s even a source guard for this: spawnedWorkspaceDir is only supported for subagent:* or acp:* sessions, not the normal main session.
So what can a plugin do?
Option 1, change the default normal workspace (global/per-agent, not current-session-only)
This is the closest thing:
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
export default definePluginEntry({
id: "workspace-setter",
name: "Workspace Setter",
description: "Change the normal agent workspace",
register(api) {
api.registerCommand({
name: "workspace",
description: "Set agents.defaults.workspace",
acceptsArgs: true,
handler: async (ctx) => {
const next = ctx.args?.trim() ?? "";
const cfg = api.runtime.config.loadConfig();
if (!next) {
const current = api.runtime.agent.resolveAgentWorkspaceDir(cfg) ?? "(unset)";
return {
text:
`Current normal-session workspace:\n${current}\n\n` +
`Usage: /workspace /absolute/path\n\n` +
`This changes the default workspace, not just the current session.`,
};
}
cfg.agents ??= {};
cfg.agents.defaults ??= {};
cfg.agents.defaults.workspace = next;
await api.runtime.config.writeConfigFile(cfg);
return {
text:
`Updated agents.defaults.workspace to:\n${next}\n\n` +
`Restart the gateway and start a new session for it to take effect.`,
};
},
});
},
});
Option 2, if you only care about shell commands
Don’t change session cwd at all. Just pass workdir on each exec call.
That changes where the shell command runs, but does not change the session’s actual workspace/context.
Option 3, if you want session-local cwd
Use ACP or spawn a subagent with a workspace override.
That’s the supported path for “this conversation/thread should work in a different folder”.
Docs:
- https://docs.openclaw.ai/concepts/agent
- https://docs.openclaw.ai/concepts/agent-workspace
- https://docs.openclaw.ai/tools/slash-commands
- https://docs.openclaw.ai/plugins/sdk-runtime
If you want, I can write the subagent version next, which is probably the closest supported equivalent to “set cwd for this conversation”.
I really hate having to keep typing the cwd for all new acp spawn, whats your rec
Yeah, I wouldn’t want to keep typing that either.
My recommendation
Use built-in config, not a plugin.
Pick based on how stable your repo mapping is:
1. Same repo for that ACP agent almost all the time
Set:
agents.list[].runtime.acp.cwd
That gives that ACP agent a default cwd for new ACP sessions.
2. Same Discord channel/thread should always mean the same repo
Set:
bindings[].acp.cwd
That’s the nicest setup if you want “this thread = this project”.
bindings[].acp.cwd overrides agents.list[].runtime.acp.cwd, so you can have a general default plus per-thread/per-channel exceptions.
3. You do not want config changes, but you reuse the same conversation
Use a persistent bound ACP session:
/acp spawn codex --bind here
/acp cwd /absolute/path/to/repo
Then keep working in that same channel/thread instead of spawning fresh every time.
What I’d personally do
- one project per Discord thread/channel → use
bindings[].acp.cwd - one project per ACP agent → use
agents.list[].runtime.acp.cwd - ad hoc / temporary → use
/acp spawn ... --bind hereonce, then/acp cwdonce
Tiny example
Agent default
{
agents: {
list: [
{
id: "codex",
runtime: {
acp: {
cwd: "/workspace/my-project"
}
}
}
]
}
}
Per-channel/thread override
{
bindings: [
{
type: "acp",
agentId: "codex",
match: {
channel: "discord",
peer: { kind: "channel", id: "1494988260995498094" }
},
acp: {
cwd: "/workspace/my-project"
}
}
]
}
So my actual recommendation is:
If this thread is always the same repo, use
bindings[].acp.cwd.
That’s the cleanest “stop making me type cwd” answer.
Docs:
If you want, I can turn your exact Discord thread ID + repo path into the exact config block.
Then that specific ACP binding won’t match.
For bindings[].type: "acp", OpenClaw wants a concrete match.peer.id, so:
- same thread, renamed → still works
- new thread with a new id → does not reuse the old ACP binding
- delete/recreate thread → same problem, new id
So if you make fresh threads a lot, my rec changes a bit:
Best options
-
Use
agents.list[].runtime.acp.cwd
Best if most new ACP sessions should start in the same repo no matter which thread you use. -
Reuse one thread per project
Best if each project has its own long-lived thread.
- If you really want lots of new threads, use:
- agent-level default cwd, or
- a tiny helper/plugin/command to spawn with your preferred cwd
So basically:
thread-specific ACP binding = exact thread id only
agent-level ACP cwd default = works across new thread ids
If you want, I can show the exact config for the agent-level default version, which is probably the right fit if you keep making new threads.
Yep, then don’t use thread-specific ACP bindings.
That’s the wrong tool for your workflow.
If you create a new thread per task, my recommendation is:
Best fit
1. Put the default here
agents.list[].runtime.acp.cwd
So every new ACP spawn for that agent starts in the same repo by default, no matter which thread you made.
If you work in multiple repos a lot
2. Make one ACP agent per repo
Example idea:
codex-api→runtime.acp.cwd = "/workspace/api"codex-web→runtime.acp.cwd = "/workspace/web"
Then in each fresh thread you just do:
/acp spawn codex-api --bind here
No repeated --cwd.
What I would avoid
bindings[].acp.cwdfor your case
Good for one stable thread/channel = one project
Bad for new thread every task
So the short version is:
new thread every task → use agent-level ACP cwd defaults, not per-thread bindings.
If you want, I can sketch a nice multi-agent config for “one agent per repo” next.