#What this config do ?

1 messages · Page 1 of 1 (latest)

viscid brook
#

"tools": {
"elevated": {
"allowFrom": {
"discord": [
"something"
]
},
"enabled": true
},
"exec": {
"ask": "off",
"host": "gateway",
"security": "full"
},
"loopDetection": {
"enabled": true
}
}

hollow mulchBOT
frosty patrolBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> âś… Mark Solution

crude shoreBOT
#

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.
    something needs to be a real matcher, usually a user id or a prefixed matcher like id:..., username:..., name:..., etc.

  • tools.exec.host: "gateway"
    exec runs 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:

viscid brook
#

"acp": {
"stream": {
"deliveryMode": "live",
"coalesceIdleMs": 300,
"maxChunkChars": 1200,
"tagVisibility": {
"tool_call": true,
"tool_call_update": true
}
},
"runtime": {
"ttlMinutes": 240
}
},

crude shoreBOT
#

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.

viscid brook
#

"plugins": {
"entries": {
"acpx": {
"enabled": true,
"config": {
"permissionMode": "approve-all",
"timeoutSeconds": 3600
}
}
}
},

crude shoreBOT
#

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 exec approvals.
  • It only matters for ACP sessions, not regular agent replies.
  • approve-all is 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.

viscid brook
#

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 ?

crude shoreBOT
#

Mostly yes, with 3 notes:

  1. As pasted, it’s not complete JSON/JSON5
    You’re missing the final closing braces for the outer object.

  2. 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" }
    
  3. streaming: "block" and draftChunk are 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: false is valid, and applies to the whole guild.
  • If you only want specific channels in that guild, use:
    guilds: {
    
viscid brook
#

how do i assign a default CWD based on discord channel name ?

crude shoreBOT
#

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",
      },
    },
  ],
viscid brook
#

if my primary use case is to activate acp in a thread, does the default CWD default to the parent channel ?

crude shoreBOT
#

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 / cwd is separate

If you don’t pass --cwd, OpenClaw uses:

  1. bindings[].acp.cwd if set
  2. otherwise agents.list[].runtime.acp.cwd if set
  3. otherwise it inherits the target agent’s workspace
  4. 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”.

viscid brook
#

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

crude shoreBOT
#

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.
  • dangerouslyAllowNameMatching is not this. That’s about mutable user/name matching, not binding ACP cwd by channel name.

If your goal is “keep the same cwd when I reset workspaces”, the best pattern is:

  1. Keep a stable parent Discord channel
  2. Bind that channel to ACP with acp.cwd
  3. 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.

viscid brook
#

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'

crude shoreBOT
#

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:

  1. Custom plugin slash command
    Best if you want several shortcuts like /abc, /xyz, /docs.

  2. 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 /abc are not a built-in config feature.

Docs:

If you want, I can sketch the smallest possible plugin approach for /abc.

viscid brook
#

yes show me what that means, the smallest possible plugin approach

crude shoreBOT
#

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 or id:... / username:... / name:... style entry.)

  • tools.exec.host: "gateway"
    exec runs 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:

viscid brook
#

can i store this plugin in the workspace to i can easily commit it ?

crude shoreBOT
#

Yes, absolutely.

The clean way is:

  1. put the plugin in your normal git repo/workspace
  2. install it into OpenClaw from that local path
  3. 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 it
  • openclaw plugins install -l ./path = links it for dev
viscid brook
#

what should the plugin code be to set the current (non acp CWD)

crude shoreBOT
#

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:

If you want, I can write the subagent version next, which is probably the closest supported equivalent to “set cwd for this conversation”.

viscid brook
#

I really hate having to keep typing the cwd for all new acp spawn, whats your rec

crude shoreBOT
#

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 here once, then /acp cwd once

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"
      }
    }
  ]
viscid brook
#

What if i use a new thread id

#

or i create new threads all the time per task

crude shoreBOT
#

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

  1. Use agents.list[].runtime.acp.cwd
    Best if most new ACP sessions should start in the same repo no matter which thread you use.

  2. Reuse one thread per project
    Best if each project has its own long-lived thread.

#
  1. 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.cwd for 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.