๊ตฌ์ฑ ๐งยถ
OpenClaw ๋ ~/.openclaw/openclaw.json ์์ ์ ํ์ JSON5 ๊ตฌ์ฑ์ ์ฝ์ต๋๋ค (์ฃผ์ + ํํ ์ฝค๋ง ํ์ฉ).
ํ์ผ์ด ์์ผ๋ฉด OpenClaw ๋ ๋น๊ต์ ์์ ํ ๊ธฐ๋ณธ๊ฐ (์๋ฒ ๋๋ Pi ์์ด์ ํธ + ๋ฐ์ ์๋ณ ์ธ์
+ ์ํฌ์คํ์ด์ค ~/.openclaw/workspace) ์ ์ฌ์ฉํฉ๋๋ค. ์ผ๋ฐ์ ์ผ๋ก ๋ค์๊ณผ ๊ฐ์ ๊ฒฝ์ฐ์๋ง ๊ตฌ์ฑ์ด ํ์ํฉ๋๋ค.
- ๋ด์ ํธ๋ฆฌ๊ฑฐํ ์ ์๋ ์ฌ์ฉ์๋ฅผ ์ ํ (
channels.whatsapp.allowFrom,channels.telegram.allowFrom๋ฑ) - ๊ทธ๋ฃน ํ์ฉ ๋ชฉ๋ก + ๋ฉ์
๋์ ์ ์ด (
channels.whatsapp.groups,channels.telegram.groups,channels.discord.guilds,agents.list[].groupChat) - ๋ฉ์์ง ์ ๋์ฌ ์ฌ์ฉ์ ์ง์ (
messages) - ์์ด์ ํธ์ ์ํฌ์คํ์ด์ค ์ค์ (
agents.defaults.workspace๋๋agents.list[].workspace) - ์๋ฒ ๋๋ ์์ด์ ํธ ๊ธฐ๋ณธ๊ฐ (
agents.defaults) ๋ฐ ์ธ์ ๋์ (session) ํ๋ - ์์ด์ ํธ๋ณ ์์ด๋ดํฐํฐ ์ค์ (
agents.list[].identity)
๊ตฌ์ฑ์ด ์ฒ์์ด์ ๊ฐ์? ์์ธํ ์ค๋ช ์ด ํฌํจ๋ ์ ์ฒด ์์ ๋ Configuration Examples ๊ฐ์ด๋๋ฅผ ์ฐธ๊ณ ํ์ญ์์ค!
์๊ฒฉํ ๊ตฌ์ฑ ๊ฒ์ฆยถ
OpenClaw ๋ ์คํค๋ง์ ์์ ํ ์ผ์นํ๋ ๊ตฌ์ฑ๋ง ํ์ฉํฉ๋๋ค. ์ ์ ์๋ ํค, ์๋ชป๋ ํ์ , ์ ํจํ์ง ์์ ๊ฐ์ด ์์ผ๋ฉด ์์ ์ ์ํด Gateway(๊ฒ์ดํธ์จ์ด)๊ฐ ์์์ ๊ฑฐ๋ถํฉ๋๋ค.
๊ฒ์ฆ์ ์คํจํ๋ฉด:
- Gateway ๊ฐ ๋ถํ ๋์ง ์์ต๋๋ค.
- ์ง๋จ ๋ช
๋ น๋ง ํ์ฉ๋ฉ๋๋ค (์:
openclaw doctor,openclaw logs,openclaw health,openclaw status,openclaw service,openclaw help). - ์ ํํ ๋ฌธ์ ๋ฅผ ํ์ธํ๋ ค๋ฉด
openclaw doctor๋ฅผ ์คํํ์ญ์์ค. - ๋ง์ด๊ทธ๋ ์ด์
/๋ณต๊ตฌ๋ฅผ ์ ์ฉํ๋ ค๋ฉด
openclaw doctor --fix(๋๋--yes) ๋ฅผ ์คํํ์ญ์์ค.
Doctor ๋ ๋ช
์์ ์ผ๋ก --fix/--yes ์ ๋์ํ์ง ์๋ ํ ๋ณ๊ฒฝ ์ฌํญ์ ์ ๋ ๊ธฐ๋กํ์ง ์์ต๋๋ค.
์คํค๋ง + UI ํํธยถ
Gateway ๋ UI ํธ์ง๊ธฐ๋ฅผ ์ํด config.schema ๋ฅผ ํตํด ๊ตฌ์ฑ์ JSON Schema ํํ์ ๋
ธ์ถํฉ๋๋ค.
Control UI ๋ ์ด ์คํค๋ง๋ก๋ถํฐ ํผ์ ๋ ๋๋งํ๋ฉฐ, ํ์ถ๊ตฌ๋ก Raw JSON ํธ์ง๊ธฐ๋ฅผ ์ ๊ณตํฉ๋๋ค.
์ฑ๋ ํ๋ฌ๊ทธ์ธ๊ณผ ํ์ฅ์ ์์ฒด ๊ตฌ์ฑ์ ๋ํ ์คํค๋ง + UI ํํธ๋ฅผ ๋ฑ๋กํ ์ ์์ผ๋ฏ๋ก,
ํ๋์ฝ๋ฉ๋ ํผ ์์ด๋ ์ฑ ์ ๋ฐ์์ ์คํค๋ง ๊ธฐ๋ฐ ์ค์ ์ ์ ์งํ ์ ์์ต๋๋ค.
ํํธ (๋ผ๋ฒจ, ๊ทธ๋ฃนํ, ๋ฏผ๊ฐ ํ๋) ๋ ์คํค๋ง์ ํจ๊ป ์ ๊ณต๋์ด,
ํด๋ผ์ด์ธํธ๊ฐ ๊ตฌ์ฑ ์ง์์ ํ๋์ฝ๋ฉํ์ง ์๊ณ ๋ ๋ ๋์ ํผ์ ๋ ๋๋งํ ์ ์์ต๋๋ค.
์ ์ฉ + ์ฌ์์ (RPC)ยถ
config.apply ์ ์ฌ์ฉํ์ฌ ์ ์ฒด ๊ตฌ์ฑ์ ๊ฒ์ฆ + ๊ธฐ๋กํ๊ณ Gateway ๋ฅผ ํ ๋จ๊ณ๋ก ์ฌ์์ํ์ญ์์ค.
์ฌ์์ ์ผํฐ๋์ ๊ธฐ๋กํ๊ณ , Gateway ๊ฐ ๋ค์ ์ฌ๋ผ์จ ํ ๋ง์ง๋ง ํ์ฑ ์ธ์
์ ํํฉ๋๋ค.
๊ฒฝ๊ณ : config.apply ๋ ์ ์ฒด ๊ตฌ์ฑ์ ๊ต์ฒดํฉ๋๋ค. ์ผ๋ถ ํค๋ง ๋ณ๊ฒฝํ๋ ค๋ฉด
config.patch ๋๋ openclaw config set ๋ฅผ ์ฌ์ฉํ์ญ์์ค. ~/.openclaw/openclaw.json ์ ๋ฐฑ์
์ ์ ์งํ์ญ์์ค.
๋งค๊ฐ๋ณ์:
raw(string) โ ์ ์ฒด ๊ตฌ์ฑ์ ์ํ JSON5 ํ์ด๋ก๋baseHash(์ ํ) โconfig.get์ ๊ตฌ์ฑ ํด์ (๊ตฌ์ฑ์ด ์ด๋ฏธ ์กด์ฌํ ๋ ํ์)sessionKey(์ ํ) โ ๊นจ์ฐ๊ธฐ ํ์ ์ํ ๋ง์ง๋ง ํ์ฑ ์ธ์ ํคnote(์ ํ) โ ์ฌ์์ ์ผํฐ๋์ ํฌํจํ ๋ฉ๋ชจrestartDelayMs(์ ํ) โ ์ฌ์์ ์ ์ง์ฐ (๊ธฐ๋ณธ๊ฐ 2000)
์์ (gateway call ์ฌ์ฉ):
openclaw gateway call config.get --params '{}' # capture payload.hash
openclaw gateway call config.apply --params '{
"raw": "{\\n agents: { defaults: { workspace: \\"~/.openclaw/workspace\\" } }\\n}\\n",
"baseHash": "<hash-from-config.get>",
"sessionKey": "agent:main:whatsapp:dm:+15555550123",
"restartDelayMs": 1000
}'
๋ถ๋ถ ์ ๋ฐ์ดํธ (RPC)ยถ
config.patch ์ ์ฌ์ฉํ๋ฉด ๊ด๋ จ ์๋ ํค๋ฅผ ๋ฎ์ด์ฐ์ง ์๊ณ ๊ธฐ์กด ๊ตฌ์ฑ์ ๋ถ๋ถ ์
๋ฐ์ดํธ๋ฅผ ๋ณํฉํ ์ ์์ต๋๋ค. JSON merge patch ์๋ฏธ๋ฅผ ์ ์ฉํฉ๋๋ค.
- ๊ฐ์ฒด๋ ์ฌ๊ท์ ์ผ๋ก ๋ณํฉ๋ฉ๋๋ค.
null๋ ํค๋ฅผ ์ญ์ ํฉ๋๋ค.- ๋ฐฐ์ด์ ๊ต์ฒด๋ฉ๋๋ค.
config.apply์ ๋ง์ฐฌ๊ฐ์ง๋ก, ๊ฒ์ฆ โ ๊ธฐ๋ก โ ์ฌ์์ ์ผํฐ๋ ์ ์ฅ โ Gateway ์ฌ์์ ์์ฝ์ ์ํํฉ๋๋ค
(sessionKey์ด ์ ๊ณต๋๋ฉด ์ ํ์ ์ผ๋ก ๊นจ์).
๋งค๊ฐ๋ณ์:
raw(string) โ ๋ณ๊ฒฝํ ํค๋ง ํฌํจํ JSON5 ํ์ด๋ก๋baseHash(ํ์) โconfig.get์ ๊ตฌ์ฑ ํด์sessionKey(์ ํ) โ ๊นจ์ฐ๊ธฐ ํ์ ์ํ ๋ง์ง๋ง ํ์ฑ ์ธ์ ํคnote(์ ํ) โ ์ฌ์์ ์ผํฐ๋์ ํฌํจํ ๋ฉ๋ชจrestartDelayMs(์ ํ) โ ์ฌ์์ ์ ์ง์ฐ (๊ธฐ๋ณธ๊ฐ 2000)
์์ :
openclaw gateway call config.get --params '{}' # capture payload.hash
openclaw gateway call config.patch --params '{
"raw": "{\\n channels: { telegram: { groups: { \\"*\\": { requireMention: false } } } }\\n}\\n",
"baseHash": "<hash-from-config.get>",
"sessionKey": "agent:main:whatsapp:dm:+15555550123",
"restartDelayMs": 1000
}'
์ต์ ๊ตฌ์ฑ (๊ถ์ฅ ์์์ )ยถ
{
agents: { defaults: { workspace: "~/.openclaw/workspace" } },
channels: { whatsapp: { allowFrom: ["+15555550123"] } },
}
๋ค์์ผ๋ก ๊ธฐ๋ณธ ์ด๋ฏธ์ง๋ฅผ ํ ๋ฒ ๋น๋ํ์ญ์์ค:
scripts/sandbox-setup.sh
์ ํ ์ฑํ ๋ชจ๋ (๊ทธ๋ฃน ์ ์ด์ ๊ถ์ฅ)ยถ
๊ทธ๋ฃน์์ WhatsApp @-๋ฉ์ ์ ๋ด์ด ์๋ตํ์ง ์๋๋ก ํ๋ ค๋ฉด (ํน์ ํ ์คํธ ํธ๋ฆฌ๊ฑฐ์๋ง ์๋ต):
{
agents: {
defaults: { workspace: "~/.openclaw/workspace" },
list: [
{
id: "main",
groupChat: { mentionPatterns: ["@openclaw", "reisponde"] },
},
],
},
channels: {
whatsapp: {
// Allowlist is DMs only; including your own number enables self-chat mode.
allowFrom: ["+15555550123"],
groups: { "*": { requireMention: true } },
},
},
}
๊ตฌ์ฑ ํฌํจ ($include)ยถ
$include ์ง์์ด๋ฅผ ์ฌ์ฉํ์ฌ ๊ตฌ์ฑ์ ์ฌ๋ฌ ํ์ผ๋ก ๋ถํ ํ ์ ์์ต๋๋ค. ์ด๋ ๋ค์์ ์ ์ฉํฉ๋๋ค.
- ๋๊ท๋ชจ ๊ตฌ์ฑ ์ ๋ฆฌ (์: ํด๋ผ์ด์ธํธ๋ณ ์์ด์ ํธ ์ ์)
- ํ๊ฒฝ ๊ฐ ๊ณตํต ์ค์ ๊ณต์
- ๋ฏผ๊ฐํ ๊ตฌ์ฑ ๋ถ๋ฆฌ ๋ณด๊ด
๊ธฐ๋ณธ ์ฌ์ฉ๋ฒยถ
// ~/.openclaw/openclaw.json
{
gateway: { port: 18789 },
// Include a single file (replaces the key's value)
agents: { $include: "./agents.json5" },
// Include multiple files (deep-merged in order)
broadcast: {
$include: ["./clients/mueller.json5", "./clients/schmidt.json5"],
},
}
// ~/.openclaw/agents.json5
{
defaults: { sandbox: { mode: "all", scope: "session" } },
list: [{ id: "main", workspace: "~/.openclaw/workspace" }],
}
๋ณํฉ ๋์ยถ
- ๋จ์ผ ํ์ผ:
$include๋ฅผ ํฌํจํ ๊ฐ์ฒด๋ฅผ ๊ต์ฒดํฉ๋๋ค. - ํ์ผ ๋ฐฐ์ด: ์์๋๋ก ๊น์ ๋ณํฉ์ ์ํํฉ๋๋ค (๋ค์ ํ์ผ์ด ์์ ํ์ผ์ ๋ฎ์ด์).
- ํ์ ํค ํฌํจ: ํฌํจ ์ดํ์ ํ์ ํค๊ฐ ๋ณํฉ๋ฉ๋๋ค (ํฌํจ๋ ๊ฐ ๋ฎ์ด์).
- ํ์ ํค + ๋ฐฐ์ด/ํ๋ฆฌ๋ฏธํฐ๋ธ: ์ง์๋์ง ์์ต๋๋ค (ํฌํจ๋ ์ฝํ ์ธ ๋ ๊ฐ์ฒด์ฌ์ผ ํจ).
// Sibling keys override included values
{
$include: "./base.json5", // { a: 1, b: 2 }
b: 99, // Result: { a: 1, b: 99 }
}
์ค์ฒฉ ํฌํจยถ
ํฌํจ๋ ํ์ผ์ ์์ฒด์ ์ผ๋ก $include ์ง์์ด๋ฅผ ํฌํจํ ์ ์์ต๋๋ค (์ต๋ 10 ๋จ๊ณ ๊น์ด).
// clients/mueller.json5
{
agents: { $include: "./mueller/agents.json5" },
broadcast: { $include: "./mueller/broadcast.json5" },
}
๊ฒฝ๋ก ํด์ยถ
- ์๋ ๊ฒฝ๋ก: ํฌํจํ๋ ํ์ผ์ ๊ธฐ์ค์ผ๋ก ํด์๋ฉ๋๋ค.
- ์ ๋ ๊ฒฝ๋ก: ๊ทธ๋๋ก ์ฌ์ฉ๋ฉ๋๋ค.
- ์์ ๋๋ ํ ๋ฆฌ:
../์ฐธ์กฐ๋ ์์๋๋ก ๋์ํฉ๋๋ค.
{ "$include": "./sub/config.json5" } // relative
{ "$include": "/etc/openclaw/base.json5" } // absolute
{ "$include": "../shared/common.json5" } // parent dir
์ค๋ฅ ์ฒ๋ฆฌยถ
- ํ์ผ ๋๋ฝ: ํด์๋ ๊ฒฝ๋ก์ ํจ๊ป ๋ช ํํ ์ค๋ฅ๋ฅผ ํ์ํฉ๋๋ค.
- ํ์ฑ ์ค๋ฅ: ์ด๋ค ํฌํจ ํ์ผ์์ ์คํจํ๋์ง ํ์ํฉ๋๋ค.
- ์ํ ํฌํจ: ํฌํจ ์ฒด์ธ๊ณผ ํจ๊ป ๊ฐ์ง ๋ฐ ๋ณด๊ณ ๋ฉ๋๋ค.
์์ : ๋ค์ค ํด๋ผ์ด์ธํธ ๋ฒ์ ์ค์ ยถ
// ~/.openclaw/openclaw.json
{
gateway: { port: 18789, auth: { token: "secret" } },
// Common agent defaults
agents: {
defaults: {
sandbox: { mode: "all", scope: "session" },
},
// Merge agent lists from all clients
list: { $include: ["./clients/mueller/agents.json5", "./clients/schmidt/agents.json5"] },
},
// Merge broadcast configs
broadcast: {
$include: ["./clients/mueller/broadcast.json5", "./clients/schmidt/broadcast.json5"],
},
channels: { whatsapp: { groupPolicy: "allowlist" } },
}
// ~/.openclaw/clients/mueller/agents.json5
[
{ id: "mueller-transcribe", workspace: "~/clients/mueller/transcribe" },
{ id: "mueller-docs", workspace: "~/clients/mueller/docs" },
]
// ~/.openclaw/clients/mueller/broadcast.json5
{
"120363403215116621@g.us": ["mueller-transcribe", "mueller-docs"],
}
๊ณตํต ์ต์ ยถ
ํ๊ฒฝ ๋ณ์ + .envยถ
OpenClaw ๋ ๋ถ๋ชจ ํ๋ก์ธ์ค (์ ธ, launchd/systemd, CI ๋ฑ) ๋ก๋ถํฐ ํ๊ฒฝ ๋ณ์๋ฅผ ์ฝ์ต๋๋ค.
์ถ๊ฐ๋ก ๋ค์์ ๋ก๋ํฉ๋๋ค.
- ํ์ฌ ์์
๋๋ ํ ๋ฆฌ์
.env(์กด์ฌ ์) ~/.openclaw/.env์ ์ ์ญ ๋์ฒด.env(์ผ๋ช$OPENCLAW_STATE_DIR/.env)
๋ .env ํ์ผ ๋ชจ๋ ๊ธฐ์กด ํ๊ฒฝ ๋ณ์๋ฅผ ๋ฎ์ด์ฐ์ง ์์ต๋๋ค.
๊ตฌ์ฑ ๋ด์ ์ธ๋ผ์ธ ํ๊ฒฝ ๋ณ์๋ฅผ ์ ๊ณตํ ์๋ ์์ต๋๋ค. ์ด ๊ฐ์
ํ๋ก์ธ์ค ํ๊ฒฝ์ ํค๊ฐ ์๋ ๊ฒฝ์ฐ์๋ง ์ ์ฉ๋ฉ๋๋ค (๋์ผํ ๋น๋ฎ์ด์ฐ๊ธฐ ๊ท์น).
{
env: {
OPENROUTER_API_KEY: "sk-or-...",
vars: {
GROQ_API_KEY: "gsk-...",
},
},
}
์ ์ฒด ์ฐ์ ์์์ ์์ค๋ /environment ๋ฅผ ์ฐธ๊ณ ํ์ญ์์ค.
env.shellEnv (์ ํ)ยถ
ํธ์ ๊ธฐ๋ฅ ์ตํธ์ธ: ํ์ฑํ๋์ด ์๊ณ ์์๋๋ ํค๊ฐ ์์ง ์ค์ ๋์ง ์์๋ค๋ฉด,
OpenClaw ๋ ๋ก๊ทธ์ธ ์
ธ์ ์คํํ์ฌ ๋๋ฝ๋ ์์ ํค๋ง ๊ฐ์ ธ์ต๋๋ค (์ ๋ ๋ฎ์ด์ฐ์ง ์์).
์ด๋ ์ฌ์ค์ ์
ธ ํ๋กํ์ ์์ฑํฉ๋๋ค.
{
env: {
shellEnv: {
enabled: true,
timeoutMs: 15000,
},
},
}
ํ๊ฒฝ ๋ณ์์ ํด๋นํ๋ ๊ฐ:
OPENCLAW_LOAD_SHELL_ENV=1OPENCLAW_SHELL_ENV_TIMEOUT_MS=15000
๊ตฌ์ฑ์์ ํ๊ฒฝ ๋ณ์ ์นํยถ
์ด๋ค ๊ตฌ์ฑ ๋ฌธ์์ด ๊ฐ์์๋ ${VAR_NAME} ๋ฌธ๋ฒ์ ์ฌ์ฉํ์ฌ
ํ๊ฒฝ ๋ณ์๋ฅผ ์ง์ ์ฐธ์กฐํ ์ ์์ต๋๋ค. ๋ณ์๋ ๊ฒ์ฆ ์ ์ ๊ตฌ์ฑ ๋ก๋ ์์ ์ ์นํ๋ฉ๋๋ค.
{
models: {
providers: {
"vercel-gateway": {
apiKey: "${VERCEL_GATEWAY_API_KEY}",
},
},
},
gateway: {
auth: {
token: "${OPENCLAW_GATEWAY_TOKEN}",
},
},
}
๊ท์น:
- ๋๋ฌธ์ ํ๊ฒฝ ๋ณ์ ์ด๋ฆ๋ง ๋งค์นญ๋ฉ๋๋ค:
[A-Z_][A-Z0-9_]* - ๋๋ฝ๋์๊ฑฐ๋ ๋น์ด ์๋ ํ๊ฒฝ ๋ณ์๋ ๊ตฌ์ฑ ๋ก๋ ์ ์ค๋ฅ๋ฅผ ๋ฐ์์ํต๋๋ค.
- ๋ฆฌํฐ๋ด
${VAR}์ ์ถ๋ ฅํ๋ ค๋ฉด$${VAR}๋ก ์ด์ค์ผ์ดํํ์ญ์์ค. $include์ ํจ๊ป ๋์ํฉ๋๋ค (ํฌํจ๋ ํ์ผ์๋ ์นํ ์ ์ฉ).
์ธ๋ผ์ธ ์นํ:
{
models: {
providers: {
custom: {
baseUrl: "${CUSTOM_API_BASE}/v1", // โ "https://api.example.com/v1"
},
},
},
}
์ธ์ฆ ์ ์ฅ์ (OAuth + API ํค)ยถ
OpenClaw ๋ ์์ด์ ํธ๋ณ ์ธ์ฆ ํ๋กํ (OAuth + API ํค) ์ ๋ค์ ์์น์ ์ ์ฅํฉ๋๋ค.
<agentDir>/auth-profiles.json(๊ธฐ๋ณธ๊ฐ:~/.openclaw/agents/<agentId>/agent/auth-profiles.json)
์ถ๊ฐ ์ฐธ๊ณ : /concepts/oauth
๋ ๊ฑฐ์ OAuth ๊ฐ์ ธ์ค๊ธฐ:
~/.openclaw/credentials/oauth.json(๋๋$OPENCLAW_STATE_DIR/credentials/oauth.json)
์๋ฒ ๋๋ Pi ์์ด์ ํธ๋ ๋ค์ ์์น์ ๋ฐํ์ ์บ์๋ฅผ ์ ์งํฉ๋๋ค.
<agentDir>/auth.json(์๋ ๊ด๋ฆฌ๋จ; ์๋ ํธ์ง ๊ธ์ง)
๋ ๊ฑฐ์ ์์ด์ ํธ ๋๋ ํ ๋ฆฌ (๋ค์ค ์์ด์ ํธ ์ด์ ):
~/.openclaw/agent/*(openclaw doctor๊ฐ~/.openclaw/agents/<defaultAgentId>/agent/*๋ก ๋ง์ด๊ทธ๋ ์ด์ )
์ค๋ฒ๋ผ์ด๋:
- OAuth ๋๋ ํ ๋ฆฌ (๋ ๊ฑฐ์ ๊ฐ์ ธ์ค๊ธฐ ์ ์ฉ):
OPENCLAW_OAUTH_DIR - ์์ด์ ํธ ๋๋ ํ ๋ฆฌ (๊ธฐ๋ณธ ์์ด์ ํธ ๋ฃจํธ ์ฌ์ ์):
OPENCLAW_AGENT_DIR(๊ถ์ฅ),PI_CODING_AGENT_DIR(๋ ๊ฑฐ์)
์ฒซ ์ฌ์ฉ ์ OpenClaw ๋ oauth.json ํญ๋ชฉ์ auth-profiles.json ๋ก ๊ฐ์ ธ์ต๋๋ค.
authยถ
์ธ์ฆ ํ๋กํ์ ์ํ ์ ํ์ ๋ฉํ๋ฐ์ดํฐ์
๋๋ค. ์ด๋ ๋น๋ฐ ์ ๋ณด๋ฅผ ์ ์ฅํ์ง ์์ผ๋ฉฐ,
ํ๋กํ ID ๋ฅผ ํ๋ก๋ฐ์ด๋ + ๋ชจ๋ (๋ฐ ์ ํ์ ์ด๋ฉ์ผ) ์ ๋งคํํ๊ณ
ํ์ผ์ค๋ฒ์ ์ฌ์ฉ๋๋ ํ๋ก๋ฐ์ด๋ ํ์ ์์๋ฅผ ์ ์ํฉ๋๋ค.
{
auth: {
profiles: {
"anthropic:me@example.com": { provider: "anthropic", mode: "oauth", email: "me@example.com" },
"anthropic:work": { provider: "anthropic", mode: "api_key" },
},
order: {
anthropic: ["anthropic:me@example.com", "anthropic:work"],
},
},
}
agents.list[].identityยถ
๊ธฐ๋ณธ๊ฐ๊ณผ UX ์ ์ฌ์ฉ๋๋ ์ ํ์ ์์ด์ ํธ๋ณ ์์ด๋ดํฐํฐ์ ๋๋ค. ์ด๋ macOS ์จ๋ณด๋ฉ ์ด์์คํดํธ๊ฐ ๊ธฐ๋กํฉ๋๋ค.
์ค์ ๋ ๊ฒฝ์ฐ, OpenClaw ๋ (๋ช ์์ ์ผ๋ก ์ค์ ํ์ง ์์์ ๋๋ง) ๊ธฐ๋ณธ๊ฐ์ ํ์ํฉ๋๋ค.
- ํ์ฑ ์์ด์ ํธ์
identity.emoji์์messages.ackReaction(๐ ๋ก ํด๋ฐฑ) - ์์ด์ ํธ์
identity.name/identity.emoji์์agents.list[].groupChat.mentionPatterns
(Telegram/Slack/Discord/Google Chat/iMessage/WhatsApp ์ ๋ฐ์์ โ@Samanthaโ ๊ฐ ๋์) identity.avatar๋ ์ํฌ์คํ์ด์ค ์๋ ์ด๋ฏธ์ง ๊ฒฝ๋ก ๋๋ ์๊ฒฉ URL/data URL ์ ํ์ฉํฉ๋๋ค. ๋ก์ปฌ ํ์ผ์ ์์ด์ ํธ ์ํฌ์คํ์ด์ค ๋ด๋ถ์ ์์ด์ผ ํฉ๋๋ค.
identity.avatar ๋ ๋ค์์ ํ์ฉํฉ๋๋ค.
- ์ํฌ์คํ์ด์ค ์๋ ๊ฒฝ๋ก (์์ด์ ํธ ์ํฌ์คํ์ด์ค ๋ด๋ถ์ ์์ด์ผ ํจ)
http(s)URLdata:URI
{
agents: {
list: [
{
id: "main",
identity: {
name: "Samantha",
theme: "helpful sloth",
emoji: "๐ฆฅ",
avatar: "avatars/samantha.png",
},
},
],
},
}
wizardยถ
CLI ๋ง๋ฒ์ฌ (onboard, configure, doctor) ๊ฐ ๊ธฐ๋กํ๋ ๋ฉํ๋ฐ์ดํฐ์
๋๋ค.
{
wizard: {
lastRunAt: "2026-01-01T00:00:00.000Z",
lastRunVersion: "2026.1.4",
lastRunCommit: "abc1234",
lastRunCommand: "configure",
lastRunMode: "local",
},
}
loggingยถ
- ๊ธฐ๋ณธ ๋ก๊ทธ ํ์ผ:
/tmp/openclaw/openclaw-YYYY-MM-DD.log - ์์ ์ ์ธ ๊ฒฝ๋ก๊ฐ ํ์ํ๋ฉด
logging.file๋ฅผ/tmp/openclaw/openclaw.log๋ก ์ค์ ํ์ญ์์ค. - ์ฝ์ ์ถ๋ ฅ์ ๋ค์์ผ๋ก ๋ณ๋ ์กฐ์ ํ ์ ์์ต๋๋ค.
logging.consoleLevel(๊ธฐ๋ณธ๊ฐinfo,--verbose์debug๋ก ์์น)logging.consoleStyle(pretty|compact|json)- ๋๊ตฌ ์์ฝ์ ๋น๋ฐ ์ ๋ณด ์ ์ถ์ ๋ฐฉ์งํ๊ธฐ ์ํด ๋ง์คํนํ ์ ์์ต๋๋ค.
logging.redactSensitive(off|tools, ๊ธฐ๋ณธ๊ฐ:tools)logging.redactPatterns(์ ๊ท์ ๋ฌธ์์ด ๋ฐฐ์ด; ๊ธฐ๋ณธ๊ฐ ์ฌ์ ์)
{
logging: {
level: "info",
file: "/tmp/openclaw/openclaw.log",
consoleLevel: "info",
consoleStyle: "pretty",
redactSensitive: "tools",
redactPatterns: [
// Example: override defaults with your own rules.
"\\bTOKEN\\b\\s*[=:]\\s*([\"']?)([^\\s\"']+)\\1",
"/\\bsk-[A-Za-z0-9_-]{8,}\\b/gi",
],
},
}
channels.whatsapp.dmPolicyยถ
WhatsApp ๋ค์ด๋ ํธ ๋ฉ์์ง (DM) ์ฒ๋ฆฌ ๋ฐฉ์์ ์ ์ดํฉ๋๋ค.
"pairing"(๊ธฐ๋ณธ๊ฐ): ์ ์ ์๋ ๋ฐ์ ์๋ ํ์ด๋ง ์ฝ๋๋ฅผ ๋ฐ์ผ๋ฉฐ, ์์ ์๊ฐ ์น์ธํด์ผ ํฉ๋๋ค."allowlist":channels.whatsapp.allowFrom(๋๋ ํ์ด๋ง ํ์ฉ ์ ์ฅ์) ์ ์๋ ๋ฐ์ ์๋ง ํ์ฉ"open": ๋ชจ๋ ์์ DM ํ์ฉ (channels.whatsapp.allowFrom์"*"ํฌํจ ํ์)"disabled": ๋ชจ๋ ์์ DM ๋ฌด์
ํ์ด๋ง ์ฝ๋๋ 1 ์๊ฐ ํ ๋ง๋ฃ๋ฉ๋๋ค. ๋ด์ ์ ์์ฒญ์ด ์์ฑ๋ ๋๋ง ํ์ด๋ง ์ฝ๋๋ฅผ ์ ์กํฉ๋๋ค. ๋๊ธฐ ์ค์ธ DM ํ์ด๋ง ์์ฒญ์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฑ๋๋น 3 ๊ฐ๋ก ์ ํ๋ฉ๋๋ค.
ํ์ด๋ง ์น์ธ:
openclaw pairing list whatsappopenclaw pairing approve whatsapp <code>
channels.whatsapp.allowFromยถ
WhatsApp ์๋ ์๋ต์ ํธ๋ฆฌ๊ฑฐํ ์ ์๋ E.164 ์ ํ๋ฒํธ ํ์ฉ ๋ชฉ๋ก (DM ์ ์ฉ).
๋น์ด ์๊ณ channels.whatsapp.dmPolicy="pairing" ์ธ ๊ฒฝ์ฐ, ์ ์ ์๋ ๋ฐ์ ์๋ ํ์ด๋ง ์ฝ๋๋ฅผ ๋ฐ์ต๋๋ค.
๊ทธ๋ฃน์ ๊ฒฝ์ฐ channels.whatsapp.groupPolicy + channels.whatsapp.groupAllowFrom ๋ฅผ ์ฌ์ฉํ์ญ์์ค.
{
channels: {
whatsapp: {
dmPolicy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["+15555550123", "+447700900123"],
textChunkLimit: 4000, // optional outbound chunk size (chars)
chunkMode: "length", // optional chunking mode (length | newline)
mediaMaxMb: 50, // optional inbound media cap (MB)
},
},
}
channels.whatsapp.sendReadReceiptsยถ
์์ WhatsApp ๋ฉ์์ง๋ฅผ ์ฝ์ ์ฒ๋ฆฌ (ํ๋ ์ฒดํฌ) ํ ์ง ์ฌ๋ถ๋ฅผ ์ ์ดํฉ๋๋ค. ๊ธฐ๋ณธ๊ฐ: true.
์ ํ ์ฑํ ๋ชจ๋์์๋ ํ์ฑํ๋์ด ์์ด๋ ํญ์ ์ฝ์ ํ์ธ์ ๊ฑด๋๋๋๋ค.
๊ณ์ ๋ณ ์ฌ์ ์: channels.whatsapp.accounts.<id>.sendReadReceipts.
{
channels: {
whatsapp: { sendReadReceipts: false },
},
}
channels.whatsapp.accounts (๋ค์ค ๊ณ์ )ยถ
ํ๋์ ๊ฒ์ดํธ์จ์ด์์ ์ฌ๋ฌ WhatsApp ๊ณ์ ์ ์คํํฉ๋๋ค.
{
channels: {
whatsapp: {
accounts: {
default: {}, // optional; keeps the default id stable
personal: {},
biz: {
// Optional override. Default: ~/.openclaw/credentials/whatsapp/biz
// authDir: "~/.openclaw/credentials/whatsapp/biz",
},
},
},
},
}
์ฐธ๊ณ :
- ๋ฐ์ ๋ช
๋ น์
default๊ณ์ ์ด ์์ผ๋ฉด ์ด๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ์ฌ์ฉํ๊ณ , ์์ผ๋ฉด ์ ๋ ฌ๋ ์ฒซ ๋ฒ์งธ ๊ณ์ id ๋ฅผ ์ฌ์ฉํฉ๋๋ค. - ๋ ๊ฑฐ์ ๋จ์ผ ๊ณ์ Baileys ์ธ์ฆ ๋๋ ํ ๋ฆฌ๋
openclaw doctor๊ฐwhatsapp/default๋ก ๋ง์ด๊ทธ๋ ์ด์ ํฉ๋๋ค.
channels.telegram.accounts / channels.discord.accounts / channels.googlechat.accounts / channels.slack.accounts / channels.mattermost.accounts / channels.signal.accounts / channels.imessage.accountsยถ
์ฑ๋๋น ์ฌ๋ฌ ๊ณ์ ์ ์คํํฉ๋๋ค (๊ฐ ๊ณ์ ์ ์์ฒด accountId ๋ฐ ์ ํ์ name ์ ๊ฐ์ง).
{
channels: {
telegram: {
accounts: {
default: {
name: "Primary bot",
botToken: "123456:ABC...",
},
alerts: {
name: "Alerts bot",
botToken: "987654:XYZ...",
},
},
},
},
}
์ฐธ๊ณ :
default๋accountId์ด ์๋ต๋์์ ๋ ์ฌ์ฉ๋ฉ๋๋ค (CLI + ๋ผ์ฐํ ).- ํ๊ฒฝ ๋ณ์ ํ ํฐ์ ๊ธฐ๋ณธ ๊ณ์ ์๋ง ์ ์ฉ๋ฉ๋๋ค.
- ๊ธฐ๋ณธ ์ฑ๋ ์ค์ (๊ทธ๋ฃน ์ ์ฑ , ๋ฉ์ ๊ฒ์ดํ ๋ฑ) ์ ๊ณ์ ๋ณ๋ก ์ฌ์ ์๋์ง ์๋ ํ ๋ชจ๋ ๊ณ์ ์ ์ ์ฉ๋ฉ๋๋ค. ๊ณ์ ๋ณ๋ก ์ค๋ฒ๋ผ์ด๋๋์ง ์๋ ํ ๋ชจ๋ ๊ณ์ ์ ์ ์ฉ๋ฉ๋๋ค.
- ๊ฐ ๊ณ์ ์ ์๋ก ๋ค๋ฅธ agents.defaults ๋ก ๋ผ์ฐํ
ํ๋ ค๋ฉด
bindings[].match.accountId๋ฅผ ์ฌ์ฉํ์ญ์์ค.
๊ทธ๋ฃน ์ฑํ
๋ฉ์
๊ฒ์ดํ
(agents.list[].groupChat + messages.groupChat)ยถ
๊ทธ๋ฃน ๋ฉ์์ง๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฉ์ ํ์ (๋ฉํ๋ฐ์ดํฐ ๋ฉ์ ๋๋ ์ ๊ท์ ํจํด) ์ ๋๋ค. WhatsApp, Telegram, Discord, Google Chat, iMessage ๊ทธ๋ฃน ์ฑํ ์ ์ ์ฉ๋ฉ๋๋ค.
๋ฉ์ ์ ํ:
- ๋ฉํ๋ฐ์ดํฐ ๋ฉ์
: ํ๋ซํผ ๋ค์ดํฐ๋ธ @-๋ฉ์
(์: WhatsApp ํญ ๋ฉ์
). WhatsApp ์
ํ ์ฑํ
๋ชจ๋์์๋ ๋ฌด์๋ฉ๋๋ค (
channels.whatsapp.allowFrom์ฐธ๊ณ ). - ํ
์คํธ ํจํด:
agents.list[].groupChat.mentionPatterns์ ์ ์๋ ์ ๊ท์ ํจํด. ์ ํ ์ฑํ ๋ชจ๋์ ๊ด๊ณ์์ด ํญ์ ๊ฒ์ฌ๋ฉ๋๋ค. - ๋ฉ์
๊ฒ์ดํ
์ ๋ฉ์
๊ฐ์ง๊ฐ ๊ฐ๋ฅํ ๊ฒฝ์ฐ์๋ง ์ ์ฉ๋ฉ๋๋ค (๋ค์ดํฐ๋ธ ๋ฉ์
๋๋ ์ต์ ํ๋์
mentionPattern).
{
messages: {
groupChat: { historyLimit: 50 },
},
agents: {
list: [{ id: "main", groupChat: { mentionPatterns: ["@openclaw", "openclaw"] } }],
},
}
messages.groupChat.historyLimit ๋ ๊ทธ๋ฃน ํ์คํ ๋ฆฌ ์ปจํ
์คํธ์ ์ ์ญ ๊ธฐ๋ณธ๊ฐ์ ์ค์ ํฉ๋๋ค. ์ฑ๋์ channels.<channel>.historyLimit (๋๋ ๋ค์ค ๊ณ์ ์ ๊ฒฝ์ฐ channels.<channel>.accounts.*.historyLimit) ์ผ๋ก ์ฌ์ ์ํ ์ ์์ต๋๋ค. ํ์คํ ๋ฆฌ ๋ํ์ ๋นํ์ฑํํ๋ ค๋ฉด 0 ์ ์ค์ ํ์ญ์์ค.
DM ํ์คํ ๋ฆฌ ์ ํยถ
DM ๋ํ๋ ์์ด์ ํธ๊ฐ ๊ด๋ฆฌํ๋ ์ธ์ ๊ธฐ๋ฐ ํ์คํ ๋ฆฌ๋ฅผ ์ฌ์ฉํฉ๋๋ค. DM ์ธ์ ๋น ์ ์ง๋๋ ์ฌ์ฉ์ ํด ์๋ฅผ ์ ํํ ์ ์์ต๋๋ค.
{
channels: {
telegram: {
dmHistoryLimit: 30, // limit DM sessions to 30 user turns
dms: {
"123456789": { historyLimit: 50 }, // per-user override (user ID)
},
},
},
}
ํด๊ฒฐ ์์:
- DM ๋ณ ์ฌ์ ์:
channels.<provider>.dms[userId].historyLimit - ํ๋ก๋ฐ์ด๋ ๊ธฐ๋ณธ๊ฐ:
channels.<provider>.dmHistoryLimit - ์ ํ ์์ (๋ชจ๋ ํ์คํ ๋ฆฌ ์ ์ง)
์ง์ ํ๋ก๋ฐ์ด๋: telegram, whatsapp, discord, slack, signal, imessage, msteams.
์์ด์ ํธ๋ณ ์ฌ์ ์ (์ค์ ์ ์ฐ์ ์ ์ฉ, [] ๋ณด๋ค ์ฐ์ ):
{
agents: {
list: [
{ id: "work", groupChat: { mentionPatterns: ["@workbot", "\\+15555550123"] } },
{ id: "personal", groupChat: { mentionPatterns: ["@homebot", "\\+15555550999"] } },
],
},
}
๋ฉ์
๊ฒ์ดํ
๊ธฐ๋ณธ๊ฐ์ ์ฑ๋๋ณ๋ก ์กด์ฌํฉ๋๋ค (channels.whatsapp.groups, channels.telegram.groups, channels.imessage.groups, channels.discord.guilds). *.groups ์ด ์ค์ ๋๋ฉด ๊ทธ๋ฃน ํ์ฉ ๋ชฉ๋ก ์ญํ ๋ ์ํํ๋ฉฐ, ๋ชจ๋ ๊ทธ๋ฃน์ ํ์ฉํ๋ ค๋ฉด "*" ๋ฅผ ํฌํจํ์ญ์์ค.
๋ค์ดํฐ๋ธ @-๋ฉ์ ์ ๋ฌด์ํ๊ณ ํน์ ํ ์คํธ ํธ๋ฆฌ๊ฑฐ์๋ง ์๋ตํ๋ ค๋ฉด:
{
channels: {
whatsapp: {
// Include your own number to enable self-chat mode (ignore native @-mentions).
allowFrom: ["+15555550123"],
groups: { "*": { requireMention: true } },
},
},
agents: {
list: [
{
id: "main",
groupChat: {
// Only these text patterns will trigger responses
mentionPatterns: ["reisponde", "@openclaw"],
},
},
],
},
}
๊ทธ๋ฃน ์ ์ฑ (์ฑ๋๋ณ)ยถ
channels.*.groupPolicy ๋ฅผ ์ฌ์ฉํ์ฌ ๊ทธ๋ฃน/๋ฃธ ๋ฉ์์ง๋ฅผ ์์ ์๋ฝํ ์ง ์ฌ๋ถ๋ฅผ ์ ์ดํฉ๋๋ค.
{
channels: {
whatsapp: {
groupPolicy: "allowlist",
groupAllowFrom: ["+15551234567"],
},
telegram: {
groupPolicy: "allowlist",
groupAllowFrom: ["tg:123456789", "@alice"],
},
signal: {
groupPolicy: "allowlist",
groupAllowFrom: ["+15551234567"],
},
imessage: {
groupPolicy: "allowlist",
groupAllowFrom: ["chat_id:123"],
},
msteams: {
groupPolicy: "allowlist",
groupAllowFrom: ["user@org.com"],
},
discord: {
groupPolicy: "allowlist",
guilds: {
GUILD_ID: {
channels: { help: { allow: true } },
},
},
},
slack: {
groupPolicy: "allowlist",
channels: { "#general": { allow: true } },
},
},
}
์ฐธ๊ณ :
"open": ๊ทธ๋ฃน์ด ํ์ฉ ๋ชฉ๋ก์ ์ฐํํฉ๋๋ค. ๋ฉ์ ๊ฒ์ดํ ์ ์ฌ์ ํ ์ ์ฉ๋ฉ๋๋ค."disabled": ๋ชจ๋ ๊ทธ๋ฃน/๋ฃธ ๋ฉ์์ง๋ฅผ ์ฐจ๋จํฉ๋๋ค."allowlist": ๊ตฌ์ฑ๋ ํ์ฉ ๋ชฉ๋ก๊ณผ ์ผ์นํ๋ ๊ทธ๋ฃน/๋ฃธ๋ง ํ์ฉํฉ๋๋ค.channels.defaults.groupPolicy๋ ํ๋ก๋ฐ์ด๋์groupPolicy์ด ์ค์ ๋์ง ์์์ ๋ ๊ธฐ๋ณธ๊ฐ์ ์ค์ ํฉ๋๋ค.- WhatsApp/Telegram/Signal/iMessage/Microsoft Teams ๋
groupAllowFrom์ ์ฌ์ฉํฉ๋๋ค (ํด๋ฐฑ: ๋ช ์์ allowFrom). - Discord/Slack ์ ์ฑ๋ ํ์ฉ ๋ชฉ๋ก (
channels.discord.guilds.*.channels,channels.slack.channels) ์ ์ฌ์ฉํฉ๋๋ค. - ๊ทธ๋ฃน DM (Discord/Slack) ์ ์ฌ์ ํ
dm.groupEnabled+dm.groupChannels์ ์ํด ์ ์ด๋ฉ๋๋ค. - ๊ธฐ๋ณธ๊ฐ์
groupPolicy: "allowlist"์ ๋๋ค(channels.defaults.groupPolicy๋ก ์ค๋ฒ๋ผ์ด๋๋์ง ์๋ ํ). ํ์ฉ ๋ชฉ๋ก์ด ๊ตฌ์ฑ๋์ง ์์ผ๋ฉด ๊ทธ๋ฃน ๋ฉ์์ง๋ ์ฐจ๋จ๋ฉ๋๋ค.
๋ฉํฐ ์์ด์ ํธ ๋ผ์ฐํ
(agents.list + bindings)ยถ
ํ๋์ Gateway ์์์ ์ฌ๋ฌ ๊ฐ์ ๊ฒฉ๋ฆฌ๋ ์์ด์ ํธ(๋ถ๋ฆฌ๋ ์ํฌ์คํ์ด์ค, agentDir, ์ธ์
)๋ฅผ ์คํํฉ๋๋ค.
์์ ๋ฉ์์ง๋ ๋ฐ์ธ๋ฉ์ ํตํด ์์ด์ ํธ๋ก ๋ผ์ฐํ
๋ฉ๋๋ค.
- gateway/configuration.md
id: ์์ ์ ์ธ ์์ด์ ํธ ID (ํ์).default: ์ ํ ์ฌํญ; ์ฌ๋ฌ ๊ฐ๊ฐ ์ค์ ๋ ๊ฒฝ์ฐ ์ฒซ ๋ฒ์งธ๊ฐ ์ ์ฉ๋๋ฉฐ ๊ฒฝ๊ณ ๊ฐ ๊ธฐ๋ก๋ฉ๋๋ค. ์๋ฌด๊ฒ๋ ์ค์ ๋์ง ์์ ๊ฒฝ์ฐ, ๋ชฉ๋ก์ ์ฒซ ๋ฒ์งธ ํญ๋ชฉ์ด ๊ธฐ๋ณธ ์์ด์ ํธ๊ฐ ๋ฉ๋๋ค.name: ์์ด์ ํธ์ ํ์ ์ด๋ฆ.workspace: ๊ธฐ๋ณธ๊ฐ~/.openclaw/workspace-<agentId>(main์ ๊ฒฝ์ฐagents.defaults.workspace๋ก ๋์ฒด).agentDir: ๊ธฐ๋ณธ๊ฐ~/.openclaw/agents/<agentId>/agent.model: ์์ด์ ํธ๋ณ ๊ธฐ๋ณธ ๋ชจ๋ธ๋ก, ํด๋น ์์ด์ ํธ์ ๋ํดagents.defaults.model์ ์ฌ์ ์ํฉ๋๋ค.- ๋ฌธ์์ด ํ์:
"provider/model",agents.defaults.model.primary๋ง ์ฌ์ ์ํฉ๋๋ค. - ๊ฐ์ฒด ํ์:
{ primary, fallbacks }(fallbacks๋agents.defaults.model.fallbacks๋ฅผ ์ฌ์ ์ํ๋ฉฐ,[]๋ ํด๋น ์์ด์ ํธ์ ๋ํด ์ ์ญ ํด๋ฐฑ์ ๋นํ์ฑํ).
- ๋ฌธ์์ด ํ์:
identity: ์์ด์ ํธ๋ณ ์ด๋ฆ/ํ ๋ง/์ด๋ชจ์ง (๋ฉ์ ํจํด + ํ์ธ ๋ฐ์์ ์ฌ์ฉ).groupChat: ์์ด์ ํธ๋ณ ๋ฉ์ ๊ฒ์ดํ (mentionPatterns).sandbox: ์์ด์ ํธ๋ณ ์๋๋ฐ์ค ์ค์ (agents.defaults.sandbox๋ฅผ ์ฌ์ ์).mode:"off"|"non-main"|"all"workspaceAccess:"none"|"ro"|"rw"scope:"session"|"agent"|"shared"workspaceRoot: ์ฌ์ฉ์ ์ง์ ์๋๋ฐ์ค ์ํฌ์คํ์ด์ค ๋ฃจํธdocker: ์์ด์ ํธ๋ณ Docker ์ฌ์ ์ (์:image,network,env,setupCommand, ์ ํ;scope: "shared"์ผ ๋๋ ๋ฌด์๋จ)browser: ์์ด์ ํธ๋ณ ์๋๋ฐ์ค ๋ธ๋ผ์ฐ์ ์ฌ์ ์ (scope: "shared"์ผ ๋๋ ๋ฌด์๋จ)prune: ์์ด์ ํธ๋ณ ์๋๋ฐ์ค ์ ๋ฆฌ(pruning) ์ฌ์ ์ (scope: "shared"์ผ ๋๋ ๋ฌด์๋จ)
subagents: ์์ด์ ํธ๋ณ ํ์ ์์ด์ ํธ ๊ธฐ๋ณธ๊ฐ.allowAgents: ์ด ์์ด์ ํธ์์sessions_spawn์ ํ์ฉํ ์์ด์ ํธ ID์ ํ์ฉ ๋ชฉ๋ก (["*"]= ๋ชจ๋ ํ์ฉ; ๊ธฐ๋ณธ๊ฐ: ๋์ผ ์์ด์ ํธ๋ง)
tools: ์์ด์ ํธ๋ณ ๋๊ตฌ ์ ํ (์๋๋ฐ์ค ๋๊ตฌ ์ ์ฑ ์ด์ ์ ์ ์ฉ).profile: ๊ธฐ๋ณธ ๋๊ตฌ ํ๋กํ (ํ์ฉ/์ฐจ๋จ ์ด์ ์ ์ ์ฉ)allow: ํ์ฉ๋ ๋๊ตฌ ์ด๋ฆ ๋ฐฐ์ดdeny: ๊ฑฐ๋ถ๋ ๋๊ตฌ ์ด๋ฆ ๋ฐฐ์ด (๊ฑฐ๋ถ๊ฐ ์ฐ์ )
agents.defaults: ๊ณต์ ์์ด์ ํธ ๊ธฐ๋ณธ๊ฐ (๋ชจ๋ธ, ์ํฌ์คํ์ด์ค, ์๋๋ฐ์ค ๋ฑ).bindings[]: ์์ ๋ฉ์์ง๋ฅผagentId๋ก ๋ผ์ฐํ ํฉ๋๋ค.match.channel(ํ์)match.accountId(์ ํ ์ฌํญ;*= ๋ชจ๋ ๊ณ์ ; ์๋ต = ๊ธฐ๋ณธ ๊ณ์ )match.peer(์ ํ ์ฌํญ;{ kind: direct|group|channel, id })match.guildId/match.teamId(์ ํ ์ฌํญ; ์ฑ๋๋ณ)
๊ฒฐ์ ์ ๋งค์นญ ์์:
match.peermatch.guildIdmatch.teamIdmatch.accountId(์ ํ ์ผ์น, peer/guild/team ์์)match.accountId: "*"(์ฑ๋ ์ ์ฒด, peer/guild/team ์์)- ๊ธฐ๋ณธ ์์ด์ ํธ (
agents.list[].default, ๊ทธ๋ ์ง ์์ผ๋ฉด ์ฒซ ๋ฒ์งธ ๋ชฉ๋ก ํญ๋ชฉ, ๊ทธ ์ธ์๋"main")
๊ฐ ๋งค์นญ ๋จ๊ณ ๋ด์์๋ bindings์์ ์ฒ์์ผ๋ก ์ผ์นํ๋ ํญ๋ชฉ์ด ์ ์ฉ๋ฉ๋๋ค.
์์ด์ ํธ๋ณ ์ก์ธ์ค ํ๋กํ(๋ค์ค ์์ด์ ํธ)ยถ
๊ฐ ์์ด์ ํธ๋ ์์ฒด ์๋๋ฐ์ค + ๋๊ตฌ ์ ์ฑ ์ ๊ฐ์ง ์ ์์ต๋๋ค. ์ด๋ฅผ ์ฌ์ฉํด ํ๋์ ๊ฒ์ดํธ์จ์ด์์ ์ ๊ทผ ์์ค์ ํผํฉํ ์ ์์ต๋๋ค:
- ์ ์ฒด ์ ๊ทผ (๊ฐ์ธ ์์ด์ ํธ)
- ์ฝ๊ธฐ ์ ์ฉ ๋๊ตฌ + ์ํฌ์คํ์ด์ค
- ํ์ผ์์คํ ์ ๊ทผ ์์ (๋ฉ์์ง/์ธ์ ๋๊ตฌ๋ง)
์ฐ์ ์์์ ์ถ๊ฐ ์์ ๋ Multi-Agent Sandbox & Tools๋ฅผ ์ฐธ์กฐํ์ธ์.
์ ์ฒด ์ ๊ทผ (์๋๋ฐ์ค ์์):
{
agents: {
list: [
{
id: "personal",
workspace: "~/.openclaw/workspace-personal",
sandbox: { mode: "off" },
},
],
},
}
์ฝ๊ธฐ ์ ์ฉ ๋๊ตฌ + ์ฝ๊ธฐ ์ ์ฉ ์ํฌ์คํ์ด์ค:
{
agents: {
list: [
{
id: "family",
workspace: "~/.openclaw/workspace-family",
sandbox: {
mode: "all",
scope: "agent",
workspaceAccess: "ro",
},
tools: {
allow: [
"read",
"sessions_list",
"sessions_history",
"sessions_send",
"sessions_spawn",
"session_status",
],
deny: ["write", "edit", "apply_patch", "exec", "process", "browser"],
},
},
],
},
}
ํ์ผ์์คํ ์ ๊ทผ ์์ (๋ฉ์์ง/์ธ์ ๋๊ตฌ ํ์ฑํ):
{
agents: {
list: [
{
id: "public",
workspace: "~/.openclaw/workspace-public",
sandbox: {
mode: "all",
scope: "agent",
workspaceAccess: "none",
},
tools: {
allow: [
"sessions_list",
"sessions_history",
"sessions_send",
"sessions_spawn",
"session_status",
"whatsapp",
"telegram",
"slack",
"discord",
"gateway",
],
deny: [
"read",
"write",
"edit",
"apply_patch",
"exec",
"process",
"browser",
"canvas",
"nodes",
"cron",
"gateway",
"image",
],
},
},
],
},
}
์์: WhatsApp ๊ณ์ ๋ ๊ฐ โ ์์ด์ ํธ ๋ ๊ฐ:
{
agents: {
list: [
{ id: "home", default: true, workspace: "~/.openclaw/workspace-home" },
{ id: "work", workspace: "~/.openclaw/workspace-work" },
],
},
bindings: [
{ agentId: "home", match: { channel: "whatsapp", accountId: "personal" } },
{ agentId: "work", match: { channel: "whatsapp", accountId: "biz" } },
],
channels: {
whatsapp: {
accounts: {
personal: {},
biz: {},
},
},
},
}
tools.agentToAgent (์ ํ ์ฌํญ)ยถ
์์ด์ ํธ ๊ฐ ๋ฉ์์ง์ ์ตํธ์ธ ๋ฐฉ์์ ๋๋ค:
{
tools: {
agentToAgent: {
enabled: false,
allow: ["home", "work"],
},
},
}
messages.queueยถ
์์ด์ ํธ ์คํ์ด ์ด๋ฏธ ํ์ฑํ๋ ์ํ์์ ์์ ๋ฉ์์ง๊ฐ ์ด๋ป๊ฒ ๋์ํ๋์ง ์ ์ดํฉ๋๋ค.
{
messages: {
queue: {
mode: "collect", // steer | followup | collect | steer-backlog (steer+backlog ok) | interrupt (queue=steer legacy)
debounceMs: 1000,
cap: 20,
drop: "summarize", // old | new | summarize
byChannel: {
whatsapp: "collect",
telegram: "collect",
discord: "collect",
imessage: "collect",
webchat: "collect",
},
},
},
}
messages.inboundยถ
๊ฐ์ ๋ฐ์ ์๋ก๋ถํฐ์ ๋น ๋ฅธ ์ฐ์ ์์ ๋ฉ์์ง๋ฅผ ๋๋ฐ์ด์คํ์ฌ, ์ฐ๋ฌ์ ์จ ์ฌ๋ฌ ๋ฉ์์ง๊ฐ ํ๋์ ์์ด์ ํธ ํด์ด ๋๋๋ก ํฉ๋๋ค. ๋๋ฐ์ด์ฑ์ ์ฑ๋ + ๋ํ ๋จ์๋ก ๋ฒ์๊ฐ ์ง์ ๋๋ฉฐ, ์๋ต ์ค๋ ๋ฉ/ID ๋ฅผ ์ํด ๊ฐ์ฅ ์ต๊ทผ ๋ฉ์์ง๋ฅผ ์ฌ์ฉํฉ๋๋ค.
{
messages: {
inbound: {
debounceMs: 2000, // 0 disables
byChannel: {
whatsapp: 5000,
slack: 1500,
discord: 1500,
},
},
},
}
๋ ธํธ:
- ํ ์คํธ ์ ์ฉ ๋ฉ์์ง ๋ฌถ์์ ๋๋ฐ์ด์คํ๋ฉฐ, ๋ฏธ๋์ด/์ฒจ๋ถ ํ์ผ์ ์ฆ์ ํ๋ฌ์๋ฉ๋๋ค.
- ์ ์ด ๋ช
๋ น(์:
/queue,/new)์ ๋๋ฐ์ด์ฑ์ ์ฐํํ์ฌ ๋จ๋ ์ผ๋ก ์ ์ง๋ฉ๋๋ค.
commands (์ฑํ
๋ช
๋ น ์ฒ๋ฆฌ)ยถ
์ปค๋ฅํฐ ์ ๋ฐ์์ ์ฑํ ๋ช ๋ น์ ์ด๋ป๊ฒ ํ์ฑํํ ์ง ์ ์ดํฉ๋๋ค.
{
commands: {
native: "auto", // register native commands when supported (auto)
text: true, // parse slash commands in chat messages
bash: false, // allow ! (alias: /bash) (host-only; requires tools.elevated allowlists)
bashForegroundMs: 2000, // bash foreground window (0 backgrounds immediately)
config: false, // allow /config (writes to disk)
debug: false, // allow /debug (runtime-only overrides)
restart: false, // allow /restart + gateway restart tool
useAccessGroups: true, // enforce access-group allowlists/policies for commands
},
}
๋ ธํธ:
- ํ
์คํธ ๋ช
๋ น์ ๋จ๋
๋ฉ์์ง๋ก ๋ณด๋ด์ผ ํ๋ฉฐ, ์ ํ
/๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค(์ผ๋ฐ ํ ์คํธ ๋ณ์นญ ๋ถ๊ฐ). commands.text: false๋ ์ฑํ ๋ฉ์์ง์์ ๋ช ๋ น ํ์ฑ์ ๋นํ์ฑํํฉ๋๋ค.commands.native: "auto"(๊ธฐ๋ณธ๊ฐ)๋ Discord/Telegram์ ๋ค์ดํฐ๋ธ ๋ช ๋ น์ ์ผ๊ณ Slack์ ๋ ์ฑ๋ก ๋ก๋๋ค. ์ง์๋์ง ์๋ ์ฑ๋์ ํ ์คํธ ์ ์ฉ์ผ๋ก ์ ์ง๋ฉ๋๋ค.commands.native: true|false๋ก ์ ์ฒด๋ฅผ ๊ฐ์ ์ค์ ํ๊ฑฐ๋,channels.discord.commands.native,channels.telegram.commands.native,channels.slack.commands.native(bool ๋๋"auto")๋ก ์ฑ๋๋ณ ์ฌ์ ์๊ฐ ๊ฐ๋ฅํฉ๋๋ค.false๋ ์์ ์ Discord/Telegram์ ์ด์ ์ ๋ฑ๋ก๋ ๋ช ๋ น์ ์ ๊ฑฐํฉ๋๋ค. Slack ๋ช ๋ น์ Slack ์ฑ์์ ๊ด๋ฆฌ๋ฉ๋๋ค.channels.telegram.customCommands๋ Telegram ๋ด ๋ฉ๋ด ํญ๋ชฉ์ ์ถ๊ฐํฉ๋๋ค. ์ด๋ฆ์ ์ ๊ทํ๋๋ฉฐ, ๋ค์ดํฐ๋ธ ๋ช ๋ น๊ณผ์ ์ถฉ๋์ ๋ฌด์๋ฉ๋๋ค.commands.bash: true๋! <cmd>๋ก ํธ์คํธ ์ ธ ๋ช ๋ น์ ์คํํ ์ ์๊ฒ ํฉ๋๋ค(/bash <cmd>๋ ๋ณ์นญ์ผ๋ก ๋์).tools.elevated.enabled๊ฐ ํ์ํ๋ฉฐ,tools.elevated.allowFrom.<channel>์์ ๋ฐ์ ์๋ฅผ ํ์ฉ ๋ชฉ๋ก์ ์ถ๊ฐํด์ผ ํฉ๋๋ค.commands.bashForegroundMs๋ ๋ฐฑ๊ทธ๋ผ์ด๋๋ก ์ ํ๋๊ธฐ ์ bash๊ฐ ๋๊ธฐํ๋ ์๊ฐ์ ์ ์ดํฉ๋๋ค.` ์๋์ ์์นํฉ๋๋ค.- bash ์์
์ด ์คํ ์ค์ธ ๋์์๋ ์๋ก์ด
! <cmd>์์ฒญ์ด ๊ฑฐ๋ถ๋ฉ๋๋ค(ํ ๋ฒ์ ํ๋๋ง). While a bash job is running, new! <cmd>requests are rejected (one at a time). commands.config: trueenables/config(reads/writesopenclaw.json).channels.<provider>.configWritesgates config mutations initiated by that channel (default: true). This applies to/config set|unsetplus provider-specific auto-migrations (Telegram supergroup ID changes, Slack channel ID changes).commands.debug: trueenables/debug(runtime-only overrides).commands.restart: trueenables/restartand the gateway tool restart action.commands.useAccessGroups: falseallows commands to bypass access-group allowlists/policies.- ์ฌ๋์ ๋ช
๋ น๊ณผ ์ง์์ด๋ ๊ถํ์ด ์๋ ๋ฐ์ ์์๊ฒ๋ง ์ ์ฉ๋ฉ๋๋ค. Authorization is derived from
channel allowlists/pairing plus
commands.useAccessGroups.
web (WhatsApp web channel runtime)ยถ
WhatsApp runs through the gatewayโs web channel (Baileys Web). It starts automatically when a linked session exists.
Set web.enabled: false to keep it off by default.
{
web: {
enabled: true,
heartbeatSeconds: 60,
reconnect: {
initialMs: 2000,
maxMs: 120000,
factor: 1.4,
jitter: 0.2,
maxAttempts: 0,
},
},
}
channels.telegram (bot transport)ยถ
OpenClaw starts Telegram only when a channels.telegram config section exists. The bot token is resolved from channels.telegram.botToken (or channels.telegram.tokenFile), with TELEGRAM_BOT_TOKEN as a fallback for the default account.
Set channels.telegram.enabled: false to disable automatic startup.
Multi-account support lives under channels.telegram.accounts (see the multi-account section above). Env tokens only apply to the default account.
Set channels.telegram.configWrites: false to block Telegram-initiated config writes (including supergroup ID migrations and /config set|unset).
{
channels: {
telegram: {
enabled: true,
botToken: "your-bot-token",
dmPolicy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["tg:123456789"], // optional; "open" requires ["*"]
groups: {
"*": { requireMention: true },
"-1001234567890": {
allowFrom: ["@admin"],
systemPrompt: "Keep answers brief.",
topics: {
"99": {
requireMention: false,
skills: ["search"],
systemPrompt: "Stay on topic.",
},
},
},
},
customCommands: [
{ command: "backup", description: "Git backup" },
{ command: "generate", description: "Create an image" },
],
historyLimit: 50, // include last N group messages as context (0 disables)
replyToMode: "first", // off | first | all
linkPreview: true, // toggle outbound link previews
streamMode: "partial", // off | partial | block (draft streaming; separate from block streaming)
draftChunk: {
// optional; only for streamMode=block
minChars: 200,
maxChars: 800,
breakPreference: "paragraph", // paragraph | newline | sentence
},
actions: { reactions: true, sendMessage: true }, // tool action gates (false disables)
reactionNotifications: "own", // off | own | all
mediaMaxMb: 5,
retry: {
// outbound retry policy
attempts: 3,
minDelayMs: 400,
maxDelayMs: 30000,
jitter: 0.1,
},
network: {
// transport overrides
autoSelectFamily: false,
},
proxy: "socks5://localhost:9050",
webhookUrl: "https://example.com/telegram-webhook", // requires webhookSecret
webhookSecret: "secret",
webhookPath: "/telegram-webhook",
},
},
}
Draft streaming notes:
- Uses Telegram
sendMessageDraft(draft bubble, not a real message). - Requires private chat topics (message_thread_id in DMs; bot has topics enabled).
/reasoning streamstreams reasoning into the draft, then sends the final answer. Retry policy defaults and behavior are documented in Retry policy.
channels.discord (bot transport)ยถ
Configure the Discord bot by setting the bot token and optional gating:
Multi-account support lives under channels.discord.accounts (see the multi-account section above). Env tokens only apply to the default account.
{
channels: {
discord: {
enabled: true,
token: "your-bot-token",
mediaMaxMb: 8, // clamp inbound media size
allowBots: false, // allow bot-authored messages
actions: {
// tool action gates (false disables)
reactions: true,
stickers: true,
polls: true,
permissions: true,
messages: true,
threads: true,
pins: true,
search: true,
memberInfo: true,
roleInfo: true,
roles: false,
channelInfo: true,
voiceStatus: true,
events: true,
moderation: false,
},
replyToMode: "off", // off | first | all
dm: {
enabled: true, // disable all DMs when false
policy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["1234567890", "steipete"], // optional DM allowlist ("open" requires ["*"])
groupEnabled: false, // enable group DMs
groupChannels: ["openclaw-dm"], // optional group DM allowlist
},
guilds: {
"123456789012345678": {
// guild id (preferred) or slug
slug: "friends-of-openclaw",
requireMention: false, // per-guild default
reactionNotifications: "own", // off | own | all | allowlist
users: ["987654321098765432"], // optional per-guild user allowlist
channels: {
general: { allow: true },
help: {
allow: true,
requireMention: true,
users: ["987654321098765432"],
skills: ["docs"],
systemPrompt: "Short answers only.",
},
},
},
},
historyLimit: 20, // include last N guild messages as context
textChunkLimit: 2000, // optional outbound text chunk size (chars)
chunkMode: "length", // optional chunking mode (length | newline)
maxLinesPerMessage: 17, // soft max lines per message (Discord UI clipping)
retry: {
// outbound retry policy
attempts: 3,
minDelayMs: 500,
maxDelayMs: 30000,
jitter: 0.1,
},
},
},
}
OpenClaw starts Discord only when a channels.discord config section exists. The token is resolved from channels.discord.token, with DISCORD_BOT_TOKEN as a fallback for the default account (unless channels.discord.enabled is false). Use user:<id> (DM) or channel:<id> (guild channel) when specifying delivery targets for cron/CLI commands; bare numeric IDs are ambiguous and rejected.
Guild slugs are lowercase with spaces replaced by -; channel keys use the slugged channel name (no leading #). Prefer guild ids as keys to avoid rename ambiguity.
Bot-authored messages are ignored by default. Enable with channels.discord.allowBots (own messages are still filtered to prevent self-reply loops).
Reaction notification modes:
off: ๋ฐ์ ์ด๋ฒคํธ ์์.own: ๋ด ์์ ์ ๋ฉ์์ง์ ๋ํ ๋ฐ์ (๊ธฐ๋ณธ๊ฐ).all: ๋ชจ๋ ๋ฉ์์ง์ ๋ชจ๋ ๋ฐ์.allowlist:guilds.<id>.users์ ๋ฐ์๋ง ๋ชจ๋ ๋ฉ์์ง์ ์ ์ฉ (๋น ๋ชฉ๋ก์ ๋นํ์ฑํ). Outbound text is chunked bychannels.discord.textChunkLimit(default 2000). Setchannels.discord.chunkMode="newline"to split on blank lines (paragraph boundaries) before length chunking. Discord ํด๋ผ์ด์ธํธ๋ ๋งค์ฐ ๊ธด ๋ฉ์์ง๋ฅผ ์๋ผ์ ํ์ํ ์ ์์ผ๋ฏ๋ก,channels.discord.maxLinesPerMessage(๊ธฐ๋ณธ๊ฐ 17)๋ 2000์ ๋ฏธ๋ง์ด๋๋ผ๋ ๊ธด ์ฌ๋ฌ ์ค ์๋ต์ ๋ถํ ํฉ๋๋ค. ์ฌ์๋ ์ ์ฑ ์ ๊ธฐ๋ณธ๊ฐ๊ณผ ๋์์ Retry policy์ ๋ฌธ์ํ๋์ด ์์ต๋๋ค.
channels.googlechat (Chat API ์นํ
)ยถ
Google Chat์ ์ฑ ์์ค ์ธ์ฆ(์๋น์ค ๊ณ์ )์ ์ฌ์ฉํ๋ HTTP ์นํ
์ผ๋ก ๋์ํฉ๋๋ค.
๋ค์ค ๊ณ์ ์ง์์ channels.googlechat.accounts ์๋์ ์์ต๋๋ค(์์ ๋ค์ค ๊ณ์ ์น์
์ฐธ์กฐ). ํ๊ฒฝ ๋ณ์๋ ๊ธฐ๋ณธ ๊ณ์ ์๋ง ์ ์ฉ๋ฉ๋๋ค.
{
channels: {
googlechat: {
enabled: true,
serviceAccountFile: "/path/to/service-account.json",
audienceType: "app-url", // app-url | project-number
audience: "https://gateway.example.com/googlechat",
webhookPath: "/googlechat",
botUser: "users/1234567890", // ์ ํ ์ฌํญ; ๋ฉ์
๊ฐ์ง ํฅ์
dm: {
enabled: true,
policy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["users/1234567890"], // ์ ํ ์ฌํญ; "open"์๋ ["*"] ํ์
},
groupPolicy: "allowlist",
groups: {
"spaces/AAAA": { allow: true, requireMention: true },
},
actions: { reactions: true },
typingIndicator: "message",
mediaMaxMb: 20,
},
},
}
๋ ธํธ:
- ์๋น์ค ๊ณ์ JSON์ ์ธ๋ผ์ธ(
serviceAccount) ๋๋ ํ์ผ ๊ธฐ๋ฐ(serviceAccountFile)์ผ ์ ์์ต๋๋ค. - ๊ธฐ๋ณธ ๊ณ์ ์ ๋ํ ํ๊ฒฝ ๋ณ์ ๋์ฒด๊ฐ:
GOOGLE_CHAT_SERVICE_ACCOUNT๋๋GOOGLE_CHAT_SERVICE_ACCOUNT_FILE. audienceType๊ณผaudience๋ Chat ์ฑ์ ์นํ ์ธ์ฆ ์ค์ ๊ณผ ์ผ์นํด์ผ ํฉ๋๋ค.- ์ ๋ฌ ๋์์ ์ค์ ํ ๋
spaces/<spaceId>๋๋users/<userId|email>์ ์ฌ์ฉํ์ธ์.
channels.slack (์์ผ ๋ชจ๋)ยถ
Slack์ ์์ผ ๋ชจ๋๋ก ์คํ๋๋ฉฐ ๋ด ํ ํฐ๊ณผ ์ฑ ํ ํฐ์ด ๋ชจ๋ ํ์ํฉ๋๋ค:
{
channels: {
slack: {
enabled: true,
botToken: "xoxb-...",
appToken: "xapp-...",
dm: {
enabled: true,
policy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["U123", "U456", "*"], // ์ ํ ์ฌํญ; "open"์๋ ["*"] ํ์
groupEnabled: false,
groupChannels: ["G123"],
},
channels: {
C123: { allow: true, requireMention: true, allowBots: false },
"#general": {
allow: true,
requireMention: true,
allowBots: false,
users: ["U123"],
skills: ["docs"],
systemPrompt: "Short answers only.",
},
},
historyLimit: 50, // ๋ง์ง๋ง N๊ฐ์ ์ฑ๋/๊ทธ๋ฃน ๋ฉ์์ง๋ฅผ ์ปจํ
์คํธ๋ก ํฌํจ (0์ ๋นํ์ฑํ)
allowBots: false,
reactionNotifications: "own", // off | own | all | allowlist
reactionAllowlist: ["U123"],
replyToMode: "off", // off | first | all
thread: {
historyScope: "thread", // thread | channel
inheritParent: false,
},
actions: {
reactions: true,
messages: true,
pins: true,
memberInfo: true,
emojiList: true,
},
slashCommand: {
enabled: true,
name: "openclaw",
sessionPrefix: "slack:slash",
ephemeral: true,
},
textChunkLimit: 4000,
chunkMode: "length",
mediaMaxMb: 20,
},
},
}
๋ค์ค ๊ณ์ ์ง์์ channels.slack.accounts ์๋์ ์์ต๋๋ค(์์ ๋ค์ค ๊ณ์ ์น์
์ฐธ์กฐ). ํ๊ฒฝ ๋ณ์ ํ ํฐ์ ๊ธฐ๋ณธ ๊ณ์ ์๋ง ์ ์ฉ๋ฉ๋๋ค.
OpenClaw๋ ๊ณต๊ธ์๊ฐ ํ์ฑํ๋์ด ์๊ณ ๋ ํ ํฐ์ด ๋ชจ๋ ์ค์ ๋๋ฉด(SLACK_BOT_TOKEN + SLACK_APP_TOKEN ๋๋ ์ค์ ์ ํตํด) Slack์ ์์ํฉ๋๋ค. ํฌ๋ก /CLI ๋ช
๋ น์ ์ ๋ฌ ๋์์ ์ง์ ํ ๋ DM์ user:<id>, ์ฑ๋์ channel:<id>๋ฅผ ์ฌ์ฉํ์ธ์.
Slack์์ ์์๋ ์ค์ ์ฐ๊ธฐ(์ฑ๋ ID ๋ง์ด๊ทธ๋ ์ด์
๋ฐ /config set|unset ํฌํจ)๋ฅผ ์ฐจ๋จํ๋ ค๋ฉด channels.slack.configWrites: false๋ก ์ค์ ํ์ธ์.
๋ด์ด ์์ฑํ ๋ฉ์์ง๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ฌด์๋ฉ๋๋ค. channels.slack.allowBots ๋๋ channels.slack.channels.<id>๋ก ํ์ฑํํ์ธ์..allowBots`.
๋ฐ์ ์๋ฆผ ๋ชจ๋:
off: ๋ฐ์ ์ด๋ฒคํธ ์์.own: ๋ด ์์ ์ ๋ฉ์์ง์ ๋ํ ๋ฐ์ (๊ธฐ๋ณธ๊ฐ).all: ๋ชจ๋ ๋ฉ์์ง์ ๋ชจ๋ ๋ฐ์.allowlist: ๋ชจ๋ ๋ฉ์์ง์์channels.slack.reactionAllowlist์ ์๋ ์ฌ์ฉ์๋ค์ ๋ฐ์๋ง ํ์ฉ(๋น ๋ชฉ๋ก์ ๋นํ์ฑํ).
์ค๋ ๋ ์ธ์ ๊ฒฉ๋ฆฌ:
channels.slack.thread.historyScope๋ ์ค๋ ๋ ๊ธฐ๋ก์ด ์ค๋ ๋๋ณ(thread, ๊ธฐ๋ณธ๊ฐ)์ธ์ง ์ฑ๋ ์ ์ฒด(channel)์์ ๊ณต์ ๋๋์ง๋ฅผ ์ ์ดํฉ๋๋ค.channels.slack.thread.inheritParent๋ ์ ์ค๋ ๋ ์ธ์ ์ด ์์ ์ฑ๋์ ๋ํ๋ฅผ ์์ํ ์ง ์ฌ๋ถ๋ฅผ ์ ์ดํฉ๋๋ค(๊ธฐ๋ณธ๊ฐ: false).
Slack ์ก์
๊ทธ๋ฃน(slack ๋๊ตฌ ์ก์
์ ์ ์ด):
| ์์ ๊ทธ๋ฃน | ๊ธฐ๋ณธ๊ฐ | ์ฐธ๊ณ ์๋ฃ |
|---|---|---|
| reactions | enabled | ๋ฐ์ ์ถ๊ฐ + ๋ชฉ๋ก |
| messages | enabled | ์ฝ๊ธฐ/์ ์ก/ํธ์ง/์ญ์ |
| pins | enabled | ๊ณ ์ /ํด์ /๋ชฉ๋ก |
| memberInfo | enabled | ๋ฉค๋ฒ ์ ๋ณด |
| emojiList | enabled | ์ฌ์ฉ์ ์ง์ ์ด๋ชจ์ง ๋ชฉ๋ก |
channels.mattermost (๋ด ํ ํฐ)ยถ
Mattermost ๋ ํ๋ฌ๊ทธ์ธ์ผ๋ก ์ ๊ณต๋๋ฉฐ ์ฝ์ด ์ค์น์ ๋ฒ๋ค๋ก ํฌํจ๋์ง ์์ต๋๋ค.
๋จผ์ ์ค์นํ์ธ์: openclaw plugins install @openclaw/mattermost(๋๋ git ์ฒดํฌ์์์์ ./extensions/mattermost).
Mattermost๋ ๋ด ํ ํฐ๊ณผ ์๋ฒ์ ๊ธฐ๋ณธ URL์ด ํ์ํฉ๋๋ค:
{
channels: {
mattermost: {
enabled: true,
botToken: "mm-token",
baseUrl: "https://chat.example.com",
dmPolicy: "pairing",
chatmode: "oncall", // oncall | onmessage | onchar
oncharPrefixes: [">", "!"],
textChunkLimit: 4000,
chunkMode: "length",
},
},
}
OpenClaw๋ ๊ณ์ ์ด ๊ตฌ์ฑ๋๊ณ (๋ด ํ ํฐ + ๊ธฐ๋ณธ URL) ํ์ฑํ๋๋ฉด Mattermost๋ฅผ ์์ํฉ๋๋ค. ํ ํฐ๊ณผ ๊ธฐ๋ณธ URL์ ๊ธฐ๋ณธ ๊ณ์ ์ ๊ฒฝ์ฐ channels.mattermost.botToken + channels.mattermost.baseUrl ๋๋ MATTERMOST_BOT_TOKEN + MATTERMOST_URL์์ ํ์ธ๋ฉ๋๋ค(channels.mattermost.enabled๊ฐ false๊ฐ ์๋ ๊ฒฝ์ฐ).
์ฑํ ๋ชจ๋:
oncall(๊ธฐ๋ณธ๊ฐ): @๋ฉ์ ๋ ๊ฒฝ์ฐ์๋ง ์ฑ๋ ๋ฉ์์ง์ ์๋ตํฉ๋๋ค.onmessage: ๋ชจ๋ ์ฑ๋ ๋ฉ์์ง์ ์๋ตํฉ๋๋ค.onchar: ๋ฉ์์ง๊ฐ ํธ๋ฆฌ๊ฑฐ ์ ๋์ฌ(channels.mattermost.oncharPrefixes, ๊ธฐ๋ณธ๊ฐ[">", "!"])๋ก ์์ํ ๋ ์๋ตํฉ๋๋ค.
์ ๊ทผ ์ ์ด:
- ๊ธฐ๋ณธ DM:
channels.mattermost.dmPolicy="pairing"(์ ์ ์๋ ๋ฐ์ ์๋ ํ์ด๋ง ์ฝ๋๋ฅผ ๋ฐ์). - ๊ณต๊ฐ ๋ค์ด๋ ํธ ๋ฉ์์ง:
channels.mattermost.dmPolicy="open"์channels.mattermost.allowFrom=["*"]๋ฅผ ํจ๊ป ์ฌ์ฉํฉ๋๋ค. - ๊ธฐ๋ณธ๊ฐ์
groupPolicy: "allowlist"์ ๋๋ค (channels.defaults.groupPolicy๋ก ์ฌ์ ์๋์ง ์๋ ํ). ํ์ฉ ๋ชฉ๋ก์ด ๊ตฌ์ฑ๋์ง ์์ผ๋ฉด ๊ทธ๋ฃน ๋ฉ์์ง๋ ์ฐจ๋จ๋ฉ๋๋ค.
๋ค์ค ๊ณ์ ์ง์์ channels.mattermost.accounts ์๋์ ์์ต๋๋ค(์์ ๋ค์ค ๊ณ์ ์น์
์ฐธ์กฐ). ํ๊ฒฝ ๋ณ์๋ ๊ธฐ๋ณธ ๊ณ์ ์๋ง ์ ์ฉ๋ฉ๋๋ค.
์ ์ก ๋์์ ์ง์ ํ ๋ channel:<id> ๋๋ user:<id>(๋๋ @username)๋ฅผ ์ฌ์ฉํ์ธ์. ์ ๋์ฌ ์๋ id๋ ์ฑ๋ id๋ก ์ฒ๋ฆฌ๋ฉ๋๋ค.
channels.signal (signal-cli)ยถ
Signal ๋ฐ์์ ์์คํ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ํฌ ์ ์์ต๋๋ค(๊ณต์ ๋ฐ์ ํด๋ง):
{
channels: {
signal: {
reactionNotifications: "own", // off | own | all | allowlist
reactionAllowlist: ["+15551234567", "uuid:123e4567-e89b-12d3-a456-426614174000"],
historyLimit: 50, // ์ต๊ทผ N๊ฐ์ ๊ทธ๋ฃน ๋ฉ์์ง๋ฅผ ์ปจํ
์คํธ๋ก ํฌํจ (0์ ๋นํ์ฑํ)
},
},
}
๋ฐ์ ์๋ฆผ ๋ชจ๋:
off: ๋ฐ์ ์ด๋ฒคํธ ์์.own: ๋ด ์์ ์ ๋ฉ์์ง์ ๋ํ ๋ฐ์ (๊ธฐ๋ณธ๊ฐ).all: ๋ชจ๋ ๋ฉ์์ง์ ๋ชจ๋ ๋ฐ์.allowlist: ๋ชจ๋ ๋ฉ์์ง์ ๋ํดchannels.signal.reactionAllowlist์ ํฌํจ๋ ๋ฐ์ ์์ ๋ฐ์๋ง ํ์ฉํฉ๋๋ค(๋น ๋ชฉ๋ก์ ๋นํ์ฑํ).
channels.imessage (imsg CLI)ยถ
OpenClaw๋ imsg rpc(stdio ์์ JSON-RPC)๋ฅผ ์คํํฉ๋๋ค. ๋ฐ๋ชฌ์ด๋ ํฌํธ๊ฐ ํ์ํ์ง ์์ต๋๋ค.
{
channels: {
imessage: {
enabled: true,
cliPath: "imsg",
dbPath: "~/Library/Messages/chat.db",
remoteHost: "user@gateway-host", // SSH ๋ํผ ์ฌ์ฉ ์ ์๊ฒฉ ์ฒจ๋ถํ์ผ์ ์ํ SCP
dmPolicy: "pairing", // pairing | allowlist | open | disabled
allowFrom: ["+15555550123", "user@example.com", "chat_id:123"],
historyLimit: 50, // ์ต๊ทผ N๊ฐ์ ๊ทธ๋ฃน ๋ฉ์์ง๋ฅผ ์ปจํ
์คํธ๋ก ํฌํจ (0์ ๋นํ์ฑํ)
includeAttachments: false,
mediaMaxMb: 16,
service: "auto",
region: "US",
},
},
}
๋ค์ค ๊ณ์ ์ง์์ channels.imessage.accounts ์๋์ ์์ต๋๋ค(์์ ๋ค์ค ๊ณ์ ์น์
์ฐธ์กฐ).
๋ ธํธ:
- Messages DB์ ๋ํ ์ ์ฒด ๋์คํฌ ์ ๊ทผ ๊ถํ์ด ํ์ํฉ๋๋ค.
- ์ฒซ ์ ์ก ์ Messages ์๋ํ ๊ถํ์ ์์ฒญํฉ๋๋ค.
chat_id:<id>๋์์ ์ฌ์ฉํ๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค. ์ฑํ ๋ชฉ๋ก์ ๋ณด๋ ค๋ฉดimsg chats --limit 20์ ์ฌ์ฉํ์ธ์.channels.imessage.cliPath๋ ๋ํผ ์คํฌ๋ฆฝํธ(์:imsg rpc๋ฅผ ์คํํ๋ ๋ค๋ฅธ Mac์ผ๋ก์ssh)๋ฅผ ๊ฐ๋ฆฌํฌ ์ ์์ต๋๋ค. ๋น๋ฐ๋ฒํธ ํ๋กฌํํธ๋ฅผ ํผํ๋ ค๋ฉด SSH ํค๋ฅผ ์ฌ์ฉํ์ธ์.- ์๊ฒฉ SSH ๋ํผ์ ๊ฒฝ์ฐ,
includeAttachments๊ฐ ํ์ฑํ๋๋ฉด SCP๋ก ์ฒจ๋ถํ์ผ์ ๊ฐ์ ธ์ค๊ธฐ ์ํดchannels.imessage.remoteHost๋ฅผ ์ค์ ํ์ธ์.
๋ํผ ์์ :
#!/usr/bin/env bash
exec ssh -T gateway-host imsg "$@"
agents.defaults.workspaceยถ
์์ด์ ํธ๊ฐ ํ์ผ ์์ ์ ์ฌ์ฉํ๋ ๋จ์ผ ์ ์ญ ์ํฌ์คํ์ด์ค ๋๋ ํฐ๋ฆฌ๋ฅผ ์ค์ ํฉ๋๋ค.
๊ธฐ๋ณธ๊ฐ: ~/.openclaw/workspace.
{
agents: { defaults: { workspace: "~/.openclaw/workspace" } },
}
agents.defaults.sandbox๊ฐ ํ์ฑํ๋ ๊ฒฝ์ฐ, ๋ฉ์ธ ์ธ์
์ด ์๋ ์ธ์
์ agents.defaults.sandbox.workspaceRoot ์๋์ ์ค์ฝํ๋ณ ์ํฌ์คํ์ด์ค๋ก ์ด๋ฅผ ์ฌ์ ์ํ ์ ์์ต๋๋ค.
agents.defaults.repoRootยถ
์์คํ
ํ๋กฌํํธ์ Runtime ์ค์ ํ์ํ ์ ํ์ ๋ฆฌํฌ์งํ ๋ฆฌ ๋ฃจํธ์
๋๋ค. ์ค์ ๋์ง ์์ ๊ฒฝ์ฐ, OpenClaw๋ ์ํฌ์คํ์ด์ค(๋ฐ ํ์ฌ ์์
๋๋ ํฐ๋ฆฌ)์์ ์๋ก ํ์ํ๋ฉฐ .git ๋๋ ํฐ๋ฆฌ๋ฅผ ๊ฐ์งํ๋ ค๊ณ ์๋ํฉ๋๋ค. ์ฌ์ฉํ๋ ค๋ฉด ๊ฒฝ๋ก๊ฐ ์กด์ฌํด์ผ ํฉ๋๋ค.
{
agents: { defaults: { repoRoot: "~/Projects/openclaw" } },
}
agents.defaults.skipBootstrapยถ
์ํฌ์คํ์ด์ค ๋ถํธ์คํธ๋ฉ ํ์ผ(AGENTS.md, SOUL.md, TOOLS.md, IDENTITY.md, USER.md, HEARTBEAT.md, BOOTSTRAP.md)์ ์๋ ์์ฑ์ ๋นํ์ฑํํฉ๋๋ค.
์ํฌ์คํ์ด์ค ํ์ผ์ด ๋ฆฌํฌ์งํ ๋ฆฌ์์ ์ ๊ณต๋๋ ์ฌ์ ์๋๋ ๋ฐฐํฌ์ ์ฌ์ฉํ์ธ์.
{
agents: { defaults: { skipBootstrap: true } },
}
agents.defaults.bootstrapMaxCharsยถ
์๋ฆฌ๊ธฐ ์ ์ ์์คํ
ํ๋กฌํํธ์ ์ฃผ์
๋๋ ๊ฐ ์ํฌ์คํ์ด์ค ๋ถํธ์คํธ๋ฉ ํ์ผ์ ์ต๋ ๋ฌธ์ ์์
๋๋ค. ๊ธฐ๋ณธ๊ฐ: 20000.
ํ์ผ์ด ์ด ์ ํ์ ์ด๊ณผํ๋ฉด, OpenClaw๋ ๊ฒฝ๊ณ ๋ฅผ ๊ธฐ๋กํ๊ณ ๋ง์ปค์ ํจ๊ป ์/๋ค๋ฅผ ์๋ผ ์ฃผ์ ํฉ๋๋ค.
{
agents: { defaults: { bootstrapMaxChars: 20000 } },
}
agents.defaults.userTimezoneยถ
์ฌ์ฉ์์ ์๊ฐ๋๋ฅผ ์์คํ ํ๋กฌํํธ ์ปจํ ์คํธ์ ์ค์ ํฉ๋๋ค(๋ฉ์์ง ๋ดํฌ์ ํ์์คํฌํ์๋ ์ ์ฉ๋์ง ์์). ์ค์ ๋์ง ์์ ๊ฒฝ์ฐ, OpenClaw๋ ์คํ ์ ํธ์คํธ์ ์๊ฐ๋๋ฅผ ์ฌ์ฉํฉ๋๋ค.
ํ๊ฒฝ ๋ณ์ + `.env`
agents.defaults.timeFormatยถ
์์คํ
ํ๋กฌํํธ์ ํ์ฌ ๋ ์ง ๋ฐ ์๊ฐ ์น์
์ ํ์๋๋ ์๊ฐ ํ์์ ์ ์ดํฉ๋๋ค.
๊ธฐ๋ณธ๊ฐ: auto(OS ์ค์ ).
2026-02-08T09:25:33Z
๋ฉ์์งยถ
์์ /๋ฐ์ ์ ๋์ฌ์ ์ ํ์ ํ์ธ(ack) ๋ฐ์์ ์ ์ดํฉ๋๋ค. ํ์, ์ธ์ , ์คํธ๋ฆฌ๋ฐ ์ปจํ ์คํธ์ ๋ํด์๋ Messages๋ฅผ ์ฐธ์กฐํ์ธ์.
{
messages: {
responsePrefix: "๐ฆ", // or "auto"
ackReaction: "๐",
ackReactionScope: "group-mentions",
removeAckAfterReply: false,
},
}
responsePrefix๋ ์ด๋ฏธ ์กด์ฌํ์ง ์๋ ํ ์ฑ๋ ์ ๋ฐ์ ๊ฑธ์ณ ๋ชจ๋ ๋ฐ์ ๋ต๋ณ(๋๊ตฌ ์์ฝ, ๋ธ๋ก ์คํธ๋ฆฌ๋ฐ, ์ต์ข
๋ต๋ณ)์ ์ ์ฉ๋ฉ๋๋ค.
์ฌ์ ์๋ ์ฑ๋๋ณ ๋ฐ ๊ณ์ ๋ณ๋ก ๊ตฌ์ฑํ ์ ์์ต๋๋ค:
channels.<channel>.responsePrefixchannels.<channel>.accounts.<id>.responsePrefix
ํด๊ฒฐ ์์ (๊ฐ์ฅ ๊ตฌ์ฒด์ ์ธ ํญ๋ชฉ์ด ์ฐ์ ):
channels.<channel>.accounts.<id>.responsePrefixchannels.<channel>.responsePrefixmessages.responsePrefix
์๋ฏธ:
undefined๋ ๋ค์ ๋จ๊ณ๋ก ๊ทธ๋๋ก ์ ๋ฌ๋ฉ๋๋ค.""๋ ์ ๋์ฌ๋ฅผ ๋ช ์์ ์ผ๋ก ๋นํ์ฑํํ๊ณ ์ฐ์ ์ ์ฉ์ ์ค๋จํฉ๋๋ค."auto"๋ ๋ผ์ฐํ ๋ ์์ด์ ํธ์ ๋ํด[{identity.name}]๋ฅผ ๋์ถํฉ๋๋ค.
์ฌ์ ์๋ ํ์ฅ์ ํฌํจํ ๋ชจ๋ ์ฑ๋๊ณผ ๋ชจ๋ ๋ฐ์ ๋ต๋ณ ์ ํ์ ์ ์ฉ๋ฉ๋๋ค.
messages.responsePrefix๊ฐ ์ค์ ๋์ง ์์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๋์ฌ๋ ์ ์ฉ๋์ง ์์ต๋๋ค. WhatsApp ์๊ธฐ ์์ ๊ณผ์ ์ฑํ
๋ต๋ณ์ ์์ธ์
๋๋ค: ์ค์ ๋ ๊ฒฝ์ฐ ๊ธฐ๋ณธ๊ฐ์ [{identity.name}]์ด๋ฉฐ, ๊ทธ๋ ์ง ์์ผ๋ฉด [openclaw]๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ์ ํด๋์ ํ ๋ด ๋ํ๊ฐ ์ฝ๊ธฐ ์ฝ๋๋ก ํฉ๋๋ค.
์ค์ ์ ๋ผ์ฐํ
๋ ์์ด์ ํธ์ ๋ํด [{identity.name}]๋ฅผ ๋์ถํ๋ ค๋ฉด "auto"๋ก ์ค์ ํ์ธ์.
ํ ํ๋ฆฟ ๋ณ์ยถ
responsePrefix ๋ฌธ์์ด์๋ ๋์ ์ผ๋ก ํด์๋๋ ํ
ํ๋ฆฟ ๋ณ์๋ฅผ ํฌํจํ ์ ์์ต๋๋ค:
| ๋ณ์ | ์ค๋ช | ์์ |
|---|---|---|
| {model} | ์งง์ ๋ชจ๋ธ ์ด๋ฆ | claude-opus-4-6, gpt-4o |
| {modelFull} | ์ ์ฒด ๋ชจ๋ธ ์๋ณ์ | anthropic/claude-opus-4-6 |
| {provider} | ์ ๊ณต์ ์ด๋ฆ | anthropic, openai |
| {thinkingLevel} | ํ์ฌ ์ฌ๊ณ ์์ค | high, low, off |
| {identity.name} | ์์ด์ ํธ ์๋ณ ์ด๋ฆ | ("auto" ๋ชจ๋์ ๋์ผ) |
๋ณ์๋ ๋์๋ฌธ์๋ฅผ ๊ตฌ๋ถํ์ง ์์ต๋๋ค ({MODEL} = {model}). {think}๋ {thinkingLevel}์ ๋ณ์นญ์
๋๋ค.
ํด๊ฒฐ๋์ง ์์ ๋ณ์๋ ๋ฆฌํฐ๋ด ํ
์คํธ๋ก ๊ทธ๋๋ก ์ ์ง๋ฉ๋๋ค.
{
messages: {
responsePrefix: "[{model} | think:{thinkingLevel}]",
},
}
์์ ์ถ๋ ฅ: [claude-opus-4-6 | think:high] Here's my response...
WhatsApp ์์ ์ ๋์ฌ๋ channels.whatsapp.messagePrefix๋ฅผ ํตํด ๊ตฌ์ฑ๋ฉ๋๋ค(์ฌ์ฉ ์ค๋จ๋จ:
messages.messagePrefix). ๊ธฐ๋ณธ๊ฐ์ ๋ณ๊ฒฝ๋์ง ์์ต๋๋ค: channels.whatsapp.allowFrom์ด ๋น์ด ์์ผ๋ฉด "[openclaw]", ๊ทธ๋ ์ง ์์ผ๋ฉด ""(์ ๋์ฌ ์์)์
๋๋ค. "[openclaw]"๋ฅผ ์ฌ์ฉํ ๋ ๋ผ์ฐํ
๋ ์์ด์ ํธ์ identity.name์ด ์ค์ ๋์ด ์์ผ๋ฉด OpenClaw๋ ๋์ [{identity.name}]๋ฅผ ์ฌ์ฉํฉ๋๋ค.
ackReaction์ ๋ฐ์์ ์ง์ํ๋ ์ฑ๋(Slack/Discord/Telegram/Google Chat)์์ ์์ ๋ฉ์์ง๋ฅผ ํ์ธํ๊ธฐ ์ํด ์ต์ ์ ๋
ธ๋ ฅ(best-effort)์ผ๋ก ์ด๋ชจ์ง ๋ฐ์์ ์ ์กํฉ๋๋ค. ๊ธฐ๋ณธ๊ฐ์ ์ค์ ๋์ด ์์ ๊ฒฝ์ฐ ํ์ฑ ์์ด์ ํธ์ identity.emoji์ด๋ฉฐ, ๊ทธ๋ ์ง ์์ผ๋ฉด "๐"์
๋๋ค. ""๋ก ์ค์ ํ๋ฉด ๋นํ์ฑํ๋ฉ๋๋ค.
ackReactionScope๋ ๋ฐ์์ด ์ธ์ ์คํ๋๋์ง๋ฅผ ์ ์ดํฉ๋๋ค:
group-mentions(๊ธฐ๋ณธ๊ฐ): ๊ทธ๋ฃน/๋ฃธ์์ ๋ฉ์ ์ด ํ์ํ๊ณ ๋ด์ด ๋ฉ์ ๋ ๊ฒฝ์ฐ์๋งgroup-all: ๋ชจ๋ ๊ทธ๋ฃน/๋ฃธ ๋ฉ์์งdirect: ๋ค์ด๋ ํธ ๋ฉ์์ง ์ ์ฉall: ๋ชจ๋ ๋ฉ์์ง
removeAckAfterReply๋ ์๋ต์ด ์ ์ก๋ ํ ๋ด์ ํ์ธ(ack) ๋ฐ์์ ์ ๊ฑฐํฉ๋๋ค
(Slack/Discord/Telegram/Google Chat ์ ์ฉ). ๊ธฐ๋ณธ๊ฐ: false.
messages.ttsยถ
๋ฐ์ ์๋ต์ ๋ํด ํ ์คํธ ์์ฑ ๋ณํ(text-to-speech)์ ํ์ฑํํฉ๋๋ค. ์ผ์ ธ ์์ผ๋ฉด OpenClaw๊ฐ ElevenLabs ๋๋ OpenAI๋ฅผ ์ฌ์ฉํด ์ค๋์ค๋ฅผ ์์ฑํ๊ณ ์๋ต์ ์ฒจ๋ถํฉ๋๋ค. Telegram์ Opus ์์ฑ ๋ ธํธ๋ฅผ ์ฌ์ฉํ๋ฉฐ, ๋ค๋ฅธ ์ฑ๋์ MP3 ์ค๋์ค๋ฅผ ์ ์กํฉ๋๋ค.
{
messages: {
tts: {
auto: "always", // off | always | inbound | tagged
mode: "final", // final | all (include tool/block replies)
provider: "elevenlabs",
summaryModel: "openai/gpt-4.1-mini",
modelOverrides: {
enabled: true,
},
maxTextLength: 4000,
timeoutMs: 30000,
prefsPath: "~/.openclaw/settings/tts.json",
elevenlabs: {
apiKey: "elevenlabs_api_key",
baseUrl: "https://api.elevenlabs.io",
voiceId: "voice_id",
modelId: "eleven_multilingual_v2",
seed: 42,
applyTextNormalization: "auto",
languageCode: "en",
voiceSettings: {
stability: 0.5,
similarityBoost: 0.75,
style: 0.0,
useSpeakerBoost: true,
speed: 1.0,
},
},
openai: {
apiKey: "openai_api_key",
model: "gpt-4o-mini-tts",
voice: "alloy",
},
},
},
}
๋ ธํธ:
messages.tts.auto๋ ์๋ TTS๋ฅผ ์ ์ดํฉ๋๋ค(off,always,inbound,tagged)./tts off|always|inbound|tagged๋ ์ธ์ ๋ณ ์๋ ๋ชจ๋๋ฅผ ์ค์ ํฉ๋๋ค(์ค์ ๋ณด๋ค ์ฐ์ ).messages.tts.enabled๋ ๋ ๊ฑฐ์์ด๋ฉฐ, doctor๊ฐ ์ด๋ฅผmessages.tts.auto๋ก ๋ง์ด๊ทธ๋ ์ด์ ํฉ๋๋ค.prefsPath๋ ๋ก์ปฌ ์ค๋ฒ๋ผ์ด๋(provider/limit/summarize)๋ฅผ ์ ์ฅํฉ๋๋ค.maxTextLength๋ TTS ์ ๋ ฅ์ ํ๋ ์ ํ์ด๋ฉฐ, ์์ฝ์ ์ด์ ๋ง๊ฒ ์๋ฆฝ๋๋ค.summaryModel์ ์๋ ์์ฝ์ ์ํดagents.defaults.model.primary๋ฅผ ์ฌ์ ์ํฉ๋๋ค.provider/model๋๋agents.defaults.models์ ๋ณ์นญ(alias)์ ํ์ฉํฉ๋๋ค.modelOverrides๋[[tts:...]]ํ๊ทธ์ ๊ฐ์ ๋ชจ๋ธ ๊ธฐ๋ฐ ์ค๋ฒ๋ผ์ด๋๋ฅผ ํ์ฑํํฉ๋๋ค(๊ธฐ๋ณธ๊ฐ: ์ผ์ง)./tts limit๋ฐ/tts summary๋ ์ฌ์ฉ์๋ณ ์์ฝ ์ค์ ์ ์ ์ดํฉ๋๋ค.apiKey๊ฐ์ELEVENLABS_API_KEY/XI_API_KEY๋ฐOPENAI_API_KEY๋ก ํด๋ฐฑ๋ฉ๋๋ค.elevenlabs.baseUrl์ ElevenLabs API ๊ธฐ๋ณธ URL์ ์ฌ์ ์ํฉ๋๋ค.elevenlabs.voiceSettings๋stability/similarityBoost/style(0..1),useSpeakerBoost, ๊ทธ๋ฆฌ๊ณspeed(0.5..2.0)๋ฅผ ์ง์ํฉ๋๋ค.
talkยถ
Talk ๋ชจ๋์ ๊ธฐ๋ณธ๊ฐ(macOS/iOS/Android). Voice ID๊ฐ ์ค์ ๋์ง ์์ ๊ฒฝ์ฐ ELEVENLABS_VOICE_ID ๋๋ SAG_VOICE_ID๋ก ํด๋ฐฑ๋ฉ๋๋ค.
apiKey๊ฐ ์ค์ ๋์ง ์์ ๊ฒฝ์ฐ ELEVENLABS_API_KEY(๋๋ ๊ฒ์ดํธ์จ์ด์ ์
ธ ํ๋กํ)๋ก ํด๋ฐฑ๋ฉ๋๋ค.
voiceAliases๋ฅผ ์ฌ์ฉํ๋ฉด Talk ์ง์๋ฌธ์์ ์น์ํ ์ด๋ฆ์ ์ฌ์ฉํ ์ ์์ต๋๋ค(์: "voice":"Clawd").
{
talk: {
voiceId: "elevenlabs_voice_id",
voiceAliases: {
Clawd: "EXAVITQu4vr4xnSDxMaL",
Roger: "CwhRBWXzGAHq8TQ4Fs17",
},
modelId: "eleven_v3",
outputFormat: "mp3_44100_128",
apiKey: "elevenlabs_api_key",
interruptOnSpeech: true,
},
}
agents.defaultsยถ
์๋ฒ ๋๋ ์์ด์ ํธ ๋ฐํ์(model/thinking/verbose/timeouts)์ ์ ์ดํฉ๋๋ค.
agents.defaults.models๋ ๊ตฌ์ฑ๋ ๋ชจ๋ธ ์นดํ๋ก๊ทธ๋ฅผ ์ ์ํ๋ฉฐ(/model์ ํ์ฉ ๋ชฉ๋ก ์ญํ ๋ ํจ).
agents.defaults.model.primary๋ ๊ธฐ๋ณธ ๋ชจ๋ธ์ ์ค์ ํ๊ณ , agents.defaults.model.fallbacks๋ ์ ์ญ ํ์ผ์ค๋ฒ์
๋๋ค.
agents.defaults.imageModel์ ์ ํ ์ฌํญ์ด๋ฉฐ ๊ธฐ๋ณธ ๋ชจ๋ธ์ ์ด๋ฏธ์ง ์
๋ ฅ์ด ์๋ ๊ฒฝ์ฐ์๋ง ์ฌ์ฉ๋ฉ๋๋ค.
๊ฐ agents.defaults.models ํญ๋ชฉ์๋ ๋ค์์ด ํฌํจ๋ ์ ์์ต๋๋ค:
alias(์ ํ ์ฌํญ, ์:/opus์ ๊ฐ์ ๋ชจ๋ธ ๋จ์ถํค).params(์ ํ ์ฌํญ: ๋ชจ๋ธ ์์ฒญ์ผ๋ก ๊ทธ๋๋ก ์ ๋ฌ๋๋ ๊ณต๊ธ์๋ณ API ํ๋ผ๋ฏธํฐ).
params๋ ์คํธ๋ฆฌ๋ฐ ์คํ(์๋ฒ ๋๋ ์์ด์ ํธ + ์ปดํฉ์
)์๋ ์ ์ฉ๋ฉ๋๋ค. ํ์ฌ ์ง์๋๋ ํค: temperature, maxTokens. ์ด๋ ํธ์ถ ์ ์ต์
๊ณผ ๋ณํฉ๋๋ฉฐ, ํธ์ถ์๊ฐ ์ ๊ณตํ ๊ฐ์ด ์ฐ์ ํฉ๋๋ค. temperature๋ ๊ณ ๊ธ ์กฐ์ ์ต์
์
๋๋คโ๋ชจ๋ธ์ ๊ธฐ๋ณธ๊ฐ์ ์๊ณ ์์ผ๋ฉฐ ๋ณ๊ฒฝ์ด ํ์ํ ๊ฒฝ์ฐ๊ฐ ์๋๋ผ๋ฉด ์ค์ ํ์ง ๋ง์ธ์.
Example:
{
agents: {
defaults: {
models: {
"anthropic/claude-sonnet-4-5-20250929": {
params: { temperature: 0.6 },
},
"openai/gpt-5.2": {
params: { maxTokens: 8192 },
},
},
},
},
}
Z.AI GLM-4.x ๋ชจ๋ธ์ ๋ค์ ์ค ํ๋๋ฅผ ํ์ง ์๋ ํ ์๋์ผ๋ก ์ฌ๊ณ (thinking) ๋ชจ๋๋ฅผ ํ์ฑํํฉ๋๋ค:
--thinking off๋ฅผ ์ค์ ํ๊ฑฐ๋,agents.defaults.models["zai/<model>"].params.thinking์ ์ง์ ์ ์ํฉ๋๋ค.
OpenClaw์๋ ๋ช ๊ฐ์ง ๋ด์ฅ ๋ณ์นญ(aliase) ๋จ์ถ ํ๊ธฐ๋ ํจ๊ป ์ ๊ณต๋ฉ๋๋ค. ๊ธฐ๋ณธ๊ฐ์ ํด๋น ๋ชจ๋ธ์ด ์ด๋ฏธ agents.defaults.models์ ์กด์ฌํ ๋๋ง ์ ์ฉ๋ฉ๋๋ค:
opus->anthropic/claude-opus-4-6sonnet->anthropic/claude-sonnet-4-5gpt->openai/gpt-5.2gpt-mini->openai/gpt-5-minigemini->google/gemini-3-pro-previewgemini-flash->google/gemini-3-flash-preview
๋์ผํ ๋ณ์นญ ์ด๋ฆ(๋์๋ฌธ์ ๋ฌด์)์ ์ง์ ๊ตฌ์ฑํ๋ฉด, ์ฌ์ฉ์ ๊ฐ์ด ์ฐ์ ํฉ๋๋ค(๊ธฐ๋ณธ๊ฐ์ ์ ๋ ๋ฎ์ด์ฐ์ง ์์).
์์: Opus 4.6์ ๊ธฐ๋ณธ์ผ๋ก ํ๊ณ MiniMax M2.1์ ํด๋ฐฑ์ผ๋ก ์ฌ์ฉ(ํธ์คํ ๋ MiniMax):
{
agents: {
defaults: {
models: {
"anthropic/claude-opus-4-6": { alias: "opus" },
"minimax/MiniMax-M2.1": { alias: "minimax" },
},
model: {
primary: "anthropic/claude-opus-4-6",
fallbacks: ["minimax/MiniMax-M2.1"],
},
},
},
}
MiniMax ์ธ์ฆ: MINIMAX_API_KEY(ํ๊ฒฝ ๋ณ์)๋ฅผ ์ค์ ํ๊ฑฐ๋ models.providers.minimax๋ฅผ ๊ตฌ์ฑํ์ธ์.
agents.defaults.cliBackends (CLI ํด๋ฐฑ)ยถ
ํ
์คํธ ์ ์ฉ ํด๋ฐฑ ์คํ(๋๊ตฌ ํธ์ถ ์์)์ ์ํ ์ ํ์ CLI ๋ฐฑ์๋์
๋๋ค. API ์ ๊ณต์๊ฐ ์คํจํ์ ๋์ ๋ฐฑ์
๊ฒฝ๋ก๋ก ์ ์ฉํฉ๋๋ค. ํ์ผ ๊ฒฝ๋ก๋ฅผ ๋ฐ๋ imageArg๋ฅผ ๊ตฌ์ฑํ๋ฉด ์ด๋ฏธ์ง ํจ์ค์ค๋ฃจ๊ฐ ์ง์๋ฉ๋๋ค.
๋ ธํธ:
- CLI ๋ฐฑ์๋๋ ํ ์คํธ ์ฐ์ ์ด๋ฉฐ, ๋๊ตฌ๋ ํญ์ ๋นํ์ฑํ๋ฉ๋๋ค.
sessionArg๊ฐ ์ค์ ๋๋ฉด ์ธ์ ์ด ์ง์๋๋ฉฐ, ์ธ์ ID๋ ๋ฐฑ์๋๋ณ๋ก ์ ์ง๋ฉ๋๋ค.claude-cli์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ๊ฐ์ด ๋ฏธ๋ฆฌ ์ฐ๊ฒฐ๋์ด ์์ต๋๋ค. PATH๊ฐ ์ต์์ธ ๊ฒฝ์ฐ(launchd/systemd) ๋ช ๋ น ๊ฒฝ๋ก๋ฅผ ์ฌ์ ์ํ์ธ์.
Example:
{
agents: {
defaults: {
cliBackends: {
"claude-cli": {
command: "/opt/homebrew/bin/claude",
},
"my-cli": {
command: "my-cli",
args: ["--json"],
output: "json",
modelArg: "--model",
sessionArg: "--session",
sessionMode: "existing",
systemPromptArg: "--system",
systemPromptWhen: "first",
imageArg: "--image",
imageMode: "repeat",
},
},
},
},
}
{
agents: {
defaults: {
models: {
"anthropic/claude-opus-4-6": { alias: "Opus" },
"anthropic/claude-sonnet-4-1": { alias: "Sonnet" },
"openrouter/deepseek/deepseek-r1:free": {},
"zai/glm-4.7": {
alias: "GLM",
params: {
thinking: {
type: "enabled",
clear_thinking: false,
},
},
},
},
model: {
primary: "anthropic/claude-opus-4-6",
fallbacks: [
"openrouter/deepseek/deepseek-r1:free",
"openrouter/meta-llama/llama-3.3-70b-instruct:free",
],
},
imageModel: {
primary: "openrouter/qwen/qwen-2.5-vl-72b-instruct:free",
fallbacks: ["openrouter/google/gemini-2.0-flash-vision:free"],
},
thinkingDefault: "low",
verboseDefault: "off",
elevatedDefault: "on",
timeoutSeconds: 600,
mediaMaxMb: 5,
heartbeat: {
every: "30m",
target: "last",
},
maxConcurrent: 3,
subagents: {
model: "minimax/MiniMax-M2.1",
maxConcurrent: 1,
archiveAfterMinutes: 60,
},
exec: {
backgroundMs: 10000,
timeoutSec: 1800,
cleanupMs: 1800000,
},
contextTokens: 200000,
},
},
}
agents.defaults.contextPruning (๋๊ตฌ ๊ฒฐ๊ณผ ๊ฐ์ง์น๊ธฐ)ยถ
agents.defaults.contextPruning์ LLM์ผ๋ก ์์ฒญ์ ๋ณด๋ด๊ธฐ ์ง์ ์ ์ธ๋ฉ๋ชจ๋ฆฌ ์ปจํ
์คํธ์์ ์ค๋๋ ๋๊ตฌ ๊ฒฐ๊ณผ๋ฅผ ์ ๊ฑฐํฉ๋๋ค.
์ด๋ ๋์คํฌ์ ์ ์ฅ๋ ์ธ์
๊ธฐ๋ก์ ์์ ํ์ง ์์ต๋๋ค(*.jsonl์ ์์ ํ๊ฒ ์ ์ง๋จ).
์ด๋ ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ ํฐ ๋๊ตฌ ์ถ๋ ฅ์ด ๋์ ๋๋ ์๋ค์ค๋ฌ์ด ์์ด์ ํธ์ ํ ํฐ ์ฌ์ฉ๋์ ์ค์ด๊ธฐ ์ํ ๊ฒ์ ๋๋ค.
์์ ์์ค ๊ฐ์:
- ์ฌ์ฉ์/์ด์์คํดํธ ๋ฉ์์ง๋ ์ ๋ ๊ฑด๋๋ฆฌ์ง ์์ต๋๋ค.
- ๋ง์ง๋ง
keepLastAssistants๊ฐ์ ์ด์์คํดํธ ๋ฉ์์ง๋ฅผ ๋ณดํธํฉ๋๋ค(๊ทธ ์ดํ์ ๋๊ตฌ ๊ฒฐ๊ณผ๋ ๊ฐ์ง์น๊ธฐ๋์ง ์์). - ๋ถํธ์คํธ๋ฉ ํ๋ฆฌํฝ์ค๋ฅผ ๋ณดํธํฉ๋๋ค(์ฒซ ๋ฒ์งธ ์ฌ์ฉ์ ๋ฉ์์ง ์ด์ ์ ๋ด์ฉ์ ๊ฐ์ง์น๊ธฐ๋์ง ์์).
- ๋ชจ๋:
adaptive: ์ถ์ ๋ ์ปจํ ์คํธ ๋น์จ์ดsoftTrimRatio๋ฅผ ์ด๊ณผํ๋ฉด ๊ณผ๋ํ๊ฒ ํฐ ๋๊ตฌ ๊ฒฐ๊ณผ๋ฅผ ์ํํธ ํธ๋ฆผํฉ๋๋ค(์/๋ค ์ ์ง). ๊ทธ๋ฐ ๋ค์ ์ถ์ ๋ ์ปจํ ์คํธ ๋น์จ์ดhardClearRatio๋ฅผ ์ด๊ณผํ๊ณ ๊ทธ๋ฆฌ๊ณ ์ ๋ฆฌ ๊ฐ๋ฅํ ๋๊ตฌ ๊ฒฐ๊ณผ์ ๋ถ๋์ด ์ถฉ๋ถํ ๋(minPrunableToolChars) ๊ฐ์ฅ ์ค๋๋ ์ ๊ฒฉ ๋๊ตฌ ๊ฒฐ๊ณผ๋ฅผ ํ๋ ํด๋ฆฌ์ดํฉ๋๋ค.aggressive: ๋น์จ ๊ฒ์ฌ ์์ด ์ปท์คํ ์ด์ ์ ์ ๊ฒฉ ๋๊ตฌ ๊ฒฐ๊ณผ๋ฅผ ํญ์hardClear.placeholder๋ก ๋์ฒดํฉ๋๋ค.
์ํํธ vs ํ๋ ํ๋ฃจ๋(LLM์ ์ ์ก๋๋ ์ปจํ ์คํธ์์ ๋ฌด์์ด ๋ฐ๋๋์ง):
- ์ํํธ ํธ๋ฆผ: ๊ณผ๋ํ๊ฒ ํฐ ๋๊ตฌ ๊ฒฐ๊ณผ์๋ง ์ ์ฉ๋ฉ๋๋ค. ์ฒ์ + ๋์ ์ ์งํ๊ณ ๊ฐ์ด๋ฐ์
...๋ฅผ ์ฝ์ ํฉ๋๋ค. - ์ด์ :
toolResult("โฆvery long outputโฆ") - ์ดํ:
toolResult("HEADโฆ\n...\nโฆTAIL\n\n[Tool result trimmed: โฆ]") - ํ๋ ํด๋ฆฌ์ด: ์ ์ฒด ๋๊ตฌ ๊ฒฐ๊ณผ๋ฅผ ํ๋ ์ด์คํ๋๋ก ๋์ฒดํฉ๋๋ค.
- ์ด์ :
toolResult("โฆvery long outputโฆ") - ์ดํ:
toolResult("[Old tool result content cleared]")
์ฐธ๊ณ / ํ์ฌ ์ ํ ์ฌํญ:
- ํ์ฌ ์ด๋ฏธ์ง ๋ธ๋ก์ ํฌํจํ ๋๊ตฌ ๊ฒฐ๊ณผ๋ ๊ฑด๋๋๋๋ค(ํธ๋ฆผ/ํด๋ฆฌ์ด๋์ง ์์).
- ์ถ์ ๋ โ์ปจํ ์คํธ ๋น์จโ์ ์ ํํ ํ ํฐ์ด ์๋ ๋ฌธ์ ์๋ฅผ ๊ธฐ์ค์ผ๋ก ํฉ๋๋ค(๊ทผ์ฌ์น).
- ์ธ์
์ ์์ง
keepLastAssistants์ด์์ ์ด์์คํดํธ ๋ฉ์์ง๊ฐ ์์ผ๋ฉด ํ๋ฃจ๋์ ๊ฑด๋๋๋๋ค. aggressive๋ชจ๋์์๋hardClear.enabled๊ฐ ๋ฌด์๋ฉ๋๋ค(์ ๊ฒฉ ๋๊ตฌ ๊ฒฐ๊ณผ๋ ํญ์hardClear.placeholder๋ก ๋์ฒด๋จ).
๊ธฐ๋ณธ๊ฐ(adaptive):
{
agents: { defaults: { contextPruning: { mode: "adaptive" } } },
}
๋นํ์ฑํํ๋ ค๋ฉด:
{
agents: { defaults: { contextPruning: { mode: "off" } } },
}
๊ธฐ๋ณธ๊ฐ(mode๊ฐ "adaptive" ๋๋ "aggressive"์ผ ๋):
keepLastAssistants:3softTrimRatio:0.3(adaptive ์ ์ฉ)hardClearRatio:0.5(adaptive ์ ์ฉ)minPrunableToolChars:50000(adaptive ์ ์ฉ)softTrim:{ maxChars: 4000, headChars: 1500, tailChars: 1500 }(adaptive ์ ์ฉ)hardClear:{ enabled: true, placeholder: "[Old tool result content cleared]" }
์์(aggressive, ์ต์):
{
agents: { defaults: { contextPruning: { mode: "aggressive" } } },
}
์์(adaptive ํ๋):
{
agents: {
defaults: {
contextPruning: {
mode: "adaptive",
keepLastAssistants: 3,
softTrimRatio: 0.3,
hardClearRatio: 0.5,
minPrunableToolChars: 50000,
softTrim: { maxChars: 4000, headChars: 1500, tailChars: 1500 },
hardClear: { enabled: true, placeholder: "[Old tool result content cleared]" },
// ์ ํ ์ฌํญ: ํน์ ๋๊ตฌ๋ก ํ๋ฃจ๋ ์ ํ(deny ์ฐ์ ; "*" ์์ผ๋์นด๋ ์ง์)
tools: { deny: ["browser", "canvas"] },
},
},
},
}
๋์ ์ธ๋ถ ์ฌํญ์ /concepts/session-pruning์ ์ฐธ์กฐํ์ธ์.
agents.defaults.compaction (ํค๋๋ฃธ ์์ฝ + ๋ฉ๋ชจ๋ฆฌ ํ๋ฌ์)ยถ
agents.defaults.compaction.mode๋ ์ปดํฉ์
์์ฝ ์ ๋ต์ ์ ํํฉ๋๋ค. ๊ธฐ๋ณธ๊ฐ์ default์ด๋ฉฐ, ๋งค์ฐ ๊ธด ํ์คํ ๋ฆฌ์ ๋ํด ์ฒญํฌ ์์ฝ์ ํ์ฑํํ๋ ค๋ฉด safeguard๋ก ์ค์ ํ์ธ์. /concepts/compaction์ ์ฐธ๊ณ ํ์ญ์์ค.
agents.defaults.compaction.reserveTokensFloor๋ Pi ์ปดํฉ์
์ ์ํ ์ต์ reserveTokens
๊ฐ์ ๊ฐ์ ํฉ๋๋ค(๊ธฐ๋ณธ๊ฐ: 20000). ๋นํ์ฑํํ๋ ค๋ฉด 0์ผ๋ก ์ค์ ํ์ธ์.
agents.defaults.compaction.memoryFlush๋ ์๋ ์ปดํฉ์
์ ์ ๋ฌด์ ์์ด์ ํธ ํด์ ์คํํ์ฌ
๋ชจ๋ธ์ด ๋ด๊ตฌ์ฑ ์๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋์คํฌ์ ์ ์ฅํ๋๋ก ์ง์ํฉ๋๋ค(์:
memory/YYYY-MM-DD.md). ์ธ์
ํ ํฐ ์ถ์ ์น๊ฐ ์ปดํฉ์
ํ๋ ์๋์
์ํํธ ์๊ณ๊ฐ์ ์ด๊ณผํ๋ฉด ํธ๋ฆฌ๊ฑฐ๋ฉ๋๋ค.
๋ ๊ฑฐ์ ๊ธฐ๋ณธ๊ฐ:
memoryFlush.enabled:truememoryFlush.softThresholdTokens:4000memoryFlush.prompt/memoryFlush.systemPrompt: built-in defaults withNO_REPLY- Note: memory flush is skipped when the session workspace is read-only
(
agents.defaults.sandbox.workspaceAccess: "ro"or"none").
Example (tuned):
{
agents: {
defaults: {
compaction: {
mode: "safeguard",
reserveTokensFloor: 24000,
memoryFlush: {
enabled: true,
softThresholdTokens: 6000,
systemPrompt: "Session nearing compaction. Store durable memories now.",
prompt: "Write any lasting notes to memory/YYYY-MM-DD.md; reply with NO_REPLY if nothing to store.",
},
},
},
},
}
Block streaming:
-
agents.defaults.blockStreamingDefault:"on"/"off"(๊ธฐ๋ณธ๊ฐ ๊บผ์ง). -
Channel overrides:
*.blockStreaming(and per-account variants) to force block streaming on/off. Non-Telegram channels require an explicit*.blockStreaming: trueto enable block replies. -
agents.defaults.blockStreamingBreak:"text_end"or"message_end"(default: text_end). -
agents.defaults.blockStreamingChunk: soft chunking for streamed blocks. Defaults to 800โ1200 chars, prefers paragraph breaks (\n\n), then newlines, then sentences. Example:
json5
{
agents: { defaults: { blockStreamingChunk: { minChars: 800, maxChars: 1200 } } },
}
-
agents.defaults.blockStreamingCoalesce: merge streamed blocks before sending. Defaults to{ idleMs: 1000 }and inheritsminCharsfromblockStreamingChunkwithmaxCharscapped to the channel text limit. Signal/Slack/Discord/Google Chat default tominChars: 1500unless overridden. Channel overrides:channels.whatsapp.blockStreamingCoalesce,channels.telegram.blockStreamingCoalesce,channels.discord.blockStreamingCoalesce,channels.slack.blockStreamingCoalesce,channels.mattermost.blockStreamingCoalesce,channels.signal.blockStreamingCoalesce,channels.imessage.blockStreamingCoalesce,channels.msteams.blockStreamingCoalesce,channels.googlechat.blockStreamingCoalesce(and per-account variants). -
agents.defaults.humanDelay: randomized pause between block replies after the first. Modes:off(default),natural(800โ2500ms),custom(useminMs/maxMs). Per-agent override:agents.list[].humanDelay. Example:
json5
{
agents: { defaults: { humanDelay: { mode: "natural" } } },
}
See /concepts/streaming for behavior + chunking details.
Typing indicators:
agents.defaults.typingMode:"never" | "instant" | "thinking" | "message". Defaults toinstantfor direct chats / mentions andmessagefor unmentioned group chats.session.typingMode: per-session override for the mode.agents.defaults.typingIntervalSeconds: how often the typing signal is refreshed (default: 6s).session.typingIntervalSeconds: per-session override for the refresh interval. See /concepts/typing-indicators for behavior details.
agents.defaults.model.primary should be set as provider/model (e.g. anthropic/claude-opus-4-6).
Aliases come from agents.defaults.models.*.alias (e.g. Opus).
If you omit the provider, OpenClaw currently assumes anthropic as a temporary
deprecation fallback.
Z.AI models are available as zai/<model> (e.g. zai/glm-4.7) and require
ZAI_API_KEY (or legacy Z_AI_API_KEY) in the environment.
agents.defaults.heartbeat configures periodic heartbeat runs:
every: duration string (ms,s,m,h); default unit minutes. Default:30m. Set0mto disable.model: optional override model for heartbeat runs (provider/model).includeReasoning: whentrue, heartbeats will also deliver the separateReasoning:message when available (same shape as/reasoning on). Default:false.session: optional session key to control which session the heartbeat runs in. Default:main.to: ์ ํ์ ์์ ์ ์ค๋ฒ๋ผ์ด๋(์ฑ๋๋ณ ID, ์: WhatsApp์ ๊ฒฝ์ฐ E.164, Telegram์ ๊ฒฝ์ฐ ์ฑํ ID).target: ์ ํ์ ์ ์ก ์ฑ๋(last,whatsapp,telegram,discord,slack,msteams,signal,imessage,none). ๊ธฐ๋ณธ๊ฐ:last.prompt: ํํธ๋นํธ ๋ณธ๋ฌธ์ ๋ํ ์ ํ์ ์ค๋ฒ๋ผ์ด๋(๊ธฐ๋ณธ๊ฐ: ์กด์ฌํ๋ค๋ฉดRead HEARTBEAT.md if it exists (workspace context).). Follow it strictly. Do not infer or repeat old tasks from prior chats. If nothing needs attention, reply HEARTBEAT_OK.). ์ค๋ฒ๋ผ์ด๋๋ ๊ทธ๋๋ก ์ ์ก๋ฉ๋๋ค. ํ์ผ์ ๊ณ์ ์ฝ๊ณ ์ถ๋ค๋ฉดRead HEARTBEAT.md` ์ค์ ํฌํจํ์ธ์.ackMaxChars: ์ ๋ฌ ์ ์HEARTBEAT_OK์ดํ ํ์ฉ๋๋ ์ต๋ ๋ฌธ์ ์(๊ธฐ๋ณธ๊ฐ: 300).
์์ด์ ํธ๋ณ Heartbeat:
- ํน์ ์์ด์ ํธ์ ๋ํด ํํธ๋นํธ ์ค์ ์ ํ์ฑํํ๊ฑฐ๋ ์ค๋ฒ๋ผ์ด๋ํ๋ ค๋ฉด
agents.list[].heartbeat๋ฅผ ์ค์ ํ์ธ์. - ์ด๋ค ์์ด์ ํธ ํญ๋ชฉ์ด๋
heartbeat๋ฅผ ์ ์ํ๋ฉด ๊ทธ ์์ด์ ํธ๋ค๋ง ํํธ๋นํธ๋ฅผ ์คํํฉ๋๋ค; ๊ธฐ๋ณธ๊ฐ์ ํด๋น ์์ด์ ํธ๋ค์ ์ํ ๊ณตํต ๊ธฐ์ค์ ์ด ๋ฉ๋๋ค.
Heartbeat ๋ ์ ์ฒด ์์ด์ ํธ ํด์ ์คํํฉ๋๋ค. ์งง์ ๊ฐ๊ฒฉ์ ๋ ๋ง์ ํ ํฐ์ ์๋ชจํฉ๋๋ค; every์ ์ ์ํ๊ณ , HEARTBEAT.md๋ฅผ ์์ฃผ ์๊ฒ ์ ์งํ๊ณ , ๊ทธ๋ฆฌ๊ณ /๋๋ ๋ ์ ๋ ดํ model์ ์ ํํ์ธ์.
tools.exec๋ ๋ฐฑ๊ทธ๋ผ์ด๋ exec ๊ธฐ๋ณธ๊ฐ์ ๊ตฌ์ฑํฉ๋๋ค:
backgroundMs: ์๋ ๋ฐฑ๊ทธ๋ผ์ด๋ ์ ํ ์ ๊น์ง์ ์๊ฐ(ms, ๊ธฐ๋ณธ๊ฐ 10000)timeoutSec: ์ด ์คํ ์๊ฐ(์ด) ์ดํ ์๋ ์ข ๋ฃ(๊ธฐ๋ณธ๊ฐ 1800)cleanupMs: ์๋ฃ๋ ์ธ์ ์ ๋ฉ๋ชจ๋ฆฌ์ ์ ์งํ๋ ์๊ฐ(ms, ๊ธฐ๋ณธ๊ฐ 1800000)notifyOnExit: ๋ฐฑ๊ทธ๋ผ์ด๋ exec ์ข ๋ฃ ์ ์์คํ ์ด๋ฒคํธ๋ฅผ ํ์ ๋ฃ๊ณ ํํธ๋นํธ๋ฅผ ์์ฒญํฉ๋๋ค(๊ธฐ๋ณธ๊ฐ true)applyPatch.enabled: ์คํ์ apply_patchํ์ฑํ(OpenAI/OpenAI Codex ์ ์ฉ; ๊ธฐ๋ณธ๊ฐ false)applyPatch.allowModels: ๋ชจ๋ธ ID์ ์ ํ์ ํ์ฉ ๋ชฉ๋ก(์:gpt-5.2๋๋openai/gpt-5.2) ์ฐธ๊ณ :applyPatch๋tools.execํ์์๋ง ์์ต๋๋ค.
tools.web๋ ์น ๊ฒ์ + ๊ฐ์ ธ์ค๊ธฐ ๋๊ตฌ๋ฅผ ๊ตฌ์ฑํฉ๋๋ค:
tools.web.search.enabled(๊ธฐ๋ณธ๊ฐ: ํค๊ฐ ์กด์ฌํ ๋ true)tools.web.search.apiKey(๊ถ์ฅ:openclaw configure --section web์ ํตํด ์ค์ ํ๊ฑฐ๋BRAVE_API_KEYํ๊ฒฝ ๋ณ์๋ฅผ ์ฌ์ฉ)tools.web.search.maxResults(1โ10, ๊ธฐ๋ณธ๊ฐ 5)tools.web.search.timeoutSeconds(๊ธฐ๋ณธ๊ฐ 30)tools.web.search.cacheTtlMinutes(๊ธฐ๋ณธ๊ฐ 15)tools.web.fetch.enabled(๊ธฐ๋ณธ๊ฐ true)tools.web.fetch.maxChars(๊ธฐ๋ณธ๊ฐ 50000)tools.web.fetch.maxCharsCap(๊ธฐ๋ณธ๊ฐ 50000; ๊ตฌ์ฑ/๋๊ตฌ ํธ์ถ์ maxChars๋ฅผ ์ํ ์ฒ๋ฆฌ)tools.web.fetch.timeoutSeconds(๊ธฐ๋ณธ๊ฐ 30)tools.web.fetch.cacheTtlMinutes(๊ธฐ๋ณธ๊ฐ 15)tools.web.fetch.userAgent(์ ํ์ ์ค๋ฒ๋ผ์ด๋)tools.web.fetch.readability(๊ธฐ๋ณธ๊ฐ true; ๋นํ์ฑํํ๋ฉด ๊ธฐ๋ณธ HTML ์ ๋ฆฌ๋ง ์ฌ์ฉ)tools.web.fetch.firecrawl.enabled(API ํค๊ฐ ์ค์ ๋์ด ์์ ๋ ๊ธฐ๋ณธ๊ฐ true)tools.web.fetch.firecrawl.apiKey(์ ํ ์ฌํญ; ๊ธฐ๋ณธ๊ฐFIRECRAWL_API_KEY)tools.web.fetch.firecrawl.baseUrl(๊ธฐ๋ณธ๊ฐ https://api.firecrawl.dev)tools.web.fetch.firecrawl.onlyMainContent(๊ธฐ๋ณธ๊ฐ true)tools.web.fetch.firecrawl.maxAgeMs(์ ํ)tools.web.fetch.firecrawl.timeoutSeconds(์ ํ)
tools.media๋ ์ธ๋ฐ์ด๋ ๋ฏธ๋์ด ์ดํด(์ด๋ฏธ์ง/์ค๋์ค/๋น๋์ค)๋ฅผ ๊ตฌ์ฑํฉ๋๋ค:
tools.media.models: ๊ณต์ ๋ชจ๋ธ ๋ชฉ๋ก(๊ธฐ๋ฅ ํ๊ทธ ์ง์ ; ๊ธฐ๋ฅ๋ณ ๋ชฉ๋ก ์ดํ์ ์ฌ์ฉ).tools.media.concurrency: ์ต๋ ๋์ ๊ธฐ๋ฅ ์คํ ์(๊ธฐ๋ณธ๊ฐ 2).tools.media.image/tools.media.audio/tools.media.video:enabled: ์ตํธ์์ ์ค์์น(๋ชจ๋ธ์ด ๊ตฌ์ฑ๋์ด ์์ ๋ ๊ธฐ๋ณธ๊ฐ true).prompt: ์ ํ์ ํ๋กฌํํธ ์ค๋ฒ๋ผ์ด๋(์ด๋ฏธ์ง/๋น๋์ค๋maxCharsํํธ๋ฅผ ์๋์ผ๋ก ์ถ๊ฐ).maxChars: ์ต๋ ์ถ๋ ฅ ๋ฌธ์ ์(์ด๋ฏธ์ง/๋น๋์ค ๊ธฐ๋ณธ๊ฐ 500; ์ค๋์ค๋ ๋ฏธ์ค์ ).maxBytes: ์ ์กํ ์ต๋ ๋ฏธ๋์ด ํฌ๊ธฐ(๊ธฐ๋ณธ๊ฐ: ์ด๋ฏธ์ง 10MB, ์ค๋์ค 20MB, ๋น๋์ค 50MB).timeoutSeconds: ์์ฒญ ํ์์์(๊ธฐ๋ณธ๊ฐ: ์ด๋ฏธ์ง 60์ด, ์ค๋์ค 60์ด, ๋น๋์ค 120์ด).language: ์ ํ์ ์ค๋์ค ํํธ.attachments: ์ฒจ๋ถ ์ ์ฑ (mode,maxAttachments,prefer).scope:match.channel,match.chatType, ๋๋match.keyPrefix๋ฅผ ์ฌ์ฉํ๋ ์ ํ์ ๊ฒ์ดํ (์ฒซ ๋ฒ์งธ ์ผ์น๊ฐ ์ฐ์ ).models: ์ ๋ ฌ๋ ๋ชจ๋ธ ํญ๋ชฉ ๋ชฉ๋ก; ์คํจํ๊ฑฐ๋ ๋ฏธ๋์ด๊ฐ ๋๋ฌด ํฌ๋ฉด ๋ค์ ํญ๋ชฉ์ผ๋ก ํด๋ฐฑ๋ฉ๋๋ค.- ๊ฐ
models[]ํญ๋ชฉ: -
- ์ ๊ณต์ ํญ๋ชฉ (
type: "provider"๋๋ ์๋ต): -
provider: API ์ ๊ณต์ ID (openai,anthropic,google/gemini,groq๋ฑ).
-
model: ๋ชจ๋ธ ID ์ค๋ฒ๋ผ์ด๋ (์ด๋ฏธ์ง์๋ ํ์; ์ค๋์ค ์ ๊ณต์์ ๊ธฐ๋ณธ๊ฐ์gpt-4o-mini-transcribe/whisper-large-v3-turbo, ๋น๋์ค์ ๊ธฐ๋ณธ๊ฐ์gemini-3-flash-preview).
-
profile/preferredProfile: ์ธ์ฆ ํ๋กํ ์ ํ.
- ์ ๊ณต์ ํญ๋ชฉ (
-
- CLI ํญ๋ชฉ (
type: "cli"): -
command: ์คํํ ์คํ ํ์ผ.
-
args: ํ ํ๋ฆฟ ์ธ์ ({{MediaPath}},{{Prompt}},{{MaxChars}}๋ฑ ์ง์).
- CLI ํญ๋ชฉ (
-
capabilities: ๊ณต์ ํญ๋ชฉ์ ์ ํํ๊ธฐ ์ํ ์ ํ์ ๋ชฉ๋ก (image,audio,video). 9. ์๋ต ์ ๊ธฐ๋ณธ๊ฐ:openai/anthropic/minimaxโ ์ด๋ฏธ์ง,googleโ ์ด๋ฏธ์ง+์ค๋์ค+๋น๋์ค,groqโ ์ค๋์ค.
-
prompt,maxChars,maxBytes,timeoutSeconds,language๋ ํญ๋ชฉ๋ณ๋ก ์ค๋ฒ๋ผ์ด๋ํ ์ ์์ต๋๋ค.
-
๊ตฌ์ฑ๋ ๋ชจ๋ธ์ด ์์ผ๋ฉด (๋๋
enabled: false์ธ ๊ฒฝ์ฐ) ์ดํด(understanding)๋ ๊ฑด๋๋ฐ๋ฉฐ, ๋ชจ๋ธ์ ์ฌ์ ํ ์๋ณธ ์ฒจ๋ถ ํ์ผ์ ๋ฐ์ต๋๋ค. -
์ ๊ณต์ ์ธ์ฆ์ ํ์ค ๋ชจ๋ธ ์ธ์ฆ ์์๋ฅผ ๋ฐ๋ฆ ๋๋ค(์ธ์ฆ ํ๋กํ,
OPENAI_API_KEY/GROQ_API_KEY/GEMINI_API_KEY๊ฐ์ ํ๊ฒฝ ๋ณ์, ๋๋models.providers.*.apiKey).
Example:
13. {
tools: {
media: {
audio: {
enabled: true,
maxBytes: 20971520,
scope: {
default: "deny",
rules: [{ action: "allow", match: { chatType: "direct" } }],
},
models: [
{ provider: "openai", model: "gpt-4o-mini-transcribe" },
{ type: "cli", command: "whisper", args: ["--model", "base", "{{MediaPath}}"] },
],
},
video: {
enabled: true,
maxBytes: 52428800,
models: [{ provider: "google", model: "gemini-3-flash-preview" }],
},
},
},
}
agents.defaults.subagents๋ ํ์ ์์ด์ ํธ ๊ธฐ๋ณธ๊ฐ์ ๊ตฌ์ฑํฉ๋๋ค:
-
model: ์์ฑ๋ ํ์ ์์ด์ ํธ์ ๊ธฐ๋ณธ ๋ชจ๋ธ(๋ฌธ์์ด ๋๋{ primary, fallbacks }). 16. ์๋ต ์, ํ์ ์์ด์ ํธ๋ ์์ด์ ํธ๋ณ ๋๋ ํธ์ถ๋ณ๋ก ์ค๋ฒ๋ผ์ด๋๋์ง ์๋ ํ ํธ์ถ์์ ๋ชจ๋ธ์ ์์ํฉ๋๋ค.
-
maxConcurrent: ๋์ ์คํ ๊ฐ๋ฅํ ํ์ ์์ด์ ํธ ์ต๋ ์(๊ธฐ๋ณธ๊ฐ 1).
-
archiveAfterMinutes: N๋ถ ํ ํ์ ์์ด์ ํธ ์ธ์ ์ ์๋ ์์นด์ด๋ธ(๊ธฐ๋ณธ๊ฐ 60; ๋นํ์ฑํํ๋ ค๋ฉด0์ค์ ).
-
- ํ์ ์์ด์ ํธ๋ณ ๋๊ตฌ ์ ์ฑ
:
tools.subagents.tools.allow/tools.subagents.tools.deny(deny๊ฐ ์ฐ์ ).
- ํ์ ์์ด์ ํธ๋ณ ๋๊ตฌ ์ ์ฑ
:
tools.profile์tools.allow/tools.deny์ด์ ์ ์ ์ฉ๋๋ ๊ธฐ๋ณธ ๋๊ตฌ ํ์ฉ ๋ชฉ๋ก์ ์ค์ ํฉ๋๋ค:
minimal:session_status๋งcoding:group:fs,group:runtime,group:sessions,group:memory,imagemessaging:group:messaging,sessions_list,sessions_history,sessions_send,session_statusfull: ์ ํ ์์ (๋ฏธ์ค์ ๊ณผ ๋์ผ)
์์ด์ ํธ๋ณ ์ฌ์ ์: agents.list[].tools.profile.
์์ (๊ธฐ๋ณธ์ ๋ฉ์์ง ์ ์ฉ, Slack + Discord ๋๊ตฌ๋ ํ์ฉ):
{
tools: {
profile: "messaging",
allow: ["slack", "discord"],
},
}
์์ (์ฝ๋ฉ ํ๋กํ์ด์ง๋ง exec/process ๋ ์ ์ญ ์ฐจ๋จ):
{
tools: {
profile: "coding",
deny: ["group:runtime"],
},
}
-
tools.byProvider๋ฅผ ์ฌ์ฉํ๋ฉด ํน์ ์ ๊ณต์(๋๋ ๋จ์ผprovider/model)์ ๋ํด ๋๊ตฌ๋ฅผ ์ถ๊ฐ๋ก ์ ํํ ์ ์์ต๋๋ค. ์์ด์ ํธ๋ณ ์ฌ์ ์:agents.list[].tools.byProvider. -
์ ์ฉ ์์: ๊ธฐ๋ณธ ํ๋กํ โ ์ ๊ณต์ ํ๋กํ โ ํ์ฉ/๊ฑฐ๋ถ ์ ์ฑ .
- ์ ๊ณต์ ํค๋
provider(์:google-antigravity) ๋๋provider/model(์:openai/gpt-5.2)์ ํ์ฉํฉ๋๋ค.
์์ (์ ์ญ ์ฝ๋ฉ ํ๋กํ์ ์ ์งํ๋, Google Antigravity ์๋ ์ต์ ๋๊ตฌ๋ง):
{
tools: {
profile: "coding",
byProvider: {
"google-antigravity": { profile: "minimal" },
},
},
}
- ์์(์ ๊ณต์/๋ชจ๋ธ๋ณ ํ์ฉ ๋ชฉ๋ก):
{
tools: {
allow: ["group:fs", "group:runtime", "sessions_list"],
byProvider: {
"openai/gpt-5.2": { allow: ["group:fs", "sessions_list"] },
},
},
}
tools.allow/tools.deny๋ ์ ์ญ ๋๊ตฌ ํ์ฉ/๊ฑฐ๋ถ ์ ์ฑ ์ ๊ตฌ์ฑํฉ๋๋ค(deny๊ฐ ์ฐ์ ).- ๋งค์นญ์ ๋์๋ฌธ์๋ฅผ ๊ตฌ๋ถํ์ง ์์ผ๋ฉฐ
*์์ผ๋์นด๋๋ฅผ ์ง์ํฉ๋๋ค("*"๋ ๋ชจ๋ ๋๊ตฌ๋ฅผ ์๋ฏธ). -
์ด๋ Docker ์๋๋ฐ์ค๊ฐ ๊บผ์ ธ ์์ด๋ ์ ์ฉ๋ฉ๋๋ค.
-
์์(๋ธ๋ผ์ฐ์ /์บ๋ฒ์ค ์ ์ฒด ๋นํ์ฑํ):
29. {
tools: { deny: ["browser", "canvas"] },
}
- ๋๊ตฌ ๊ทธ๋ฃน(๋จ์ถํค)์ ์ ์ญ ๋ฐ ์์ด์ ํธ๋ณ ๋๊ตฌ ์ ์ฑ ์์ ์๋ํฉ๋๋ค:
group:runtime:exec,bash,processgroup:fs:read,write,edit,apply_patchgroup:sessions:sessions_list,sessions_history,sessions_send,sessions_spawn,session_statusgroup:memory:memory_search,memory_getgroup:web:web_search,web_fetchgroup:ui:browser,canvasgroup:automation:cron,gatewaygroup:messaging:messagegroup:nodes:nodesgroup:openclaw: ๋ชจ๋ ๊ธฐ๋ณธ ์ ๊ณต OpenClaw ๋๊ตฌ(ํ๋ก๋ฐ์ด๋ ํ๋ฌ๊ทธ์ธ์ ์ ์ธ)
tools.elevated๋ ์์น๋(ํธ์คํธ) ์คํ ์ ๊ทผ์ ์ ์ดํฉ๋๋ค:
-
enabled: ์์น ๋ชจ๋ ํ์ฉ(๊ธฐ๋ณธ๊ฐ true).
-
allowFrom: ์ฑ๋๋ณ ํ์ฉ ๋ชฉ๋ก(๋น์ด ์์ผ๋ฉด ๋นํ์ฑํ).
-
whatsapp: E.164 ๋ฒํธ.
-
telegram: ์ฑํ ID ๋๋ ์ฌ์ฉ์ ์ด๋ฆ.
-
discord: ์ฌ์ฉ์ ID ๋๋ ์ฌ์ฉ์ ์ด๋ฆ(์๋ต ์channels.discord.dm.allowFrom๋ก ๋์ฒด).
-
signal: E.164 ๋ฒํธ.
-
imessage: ํธ๋ค์ด๋ ์ฑํ ID.
-
webchat: ์ธ์ ID ๋๋ ์ฌ์ฉ์ ์ด๋ฆ.
Example:
40. {
tools: {
elevated: {
enabled: true,
allowFrom: {
whatsapp: ["+15555550123"],
discord: ["steipete", "1234567890123"],
},
},
},
}
์์ด์ ํธ๋ณ ์ค๋ฒ๋ผ์ด๋(์ถ๊ฐ ์ ํ):
{
agents: {
list: [
{
id: "family",
tools: {
elevated: { enabled: false },
},
},
],
},
}
๋ ธํธ:
tools.elevated๋ ์ ์ญ ๊ธฐ์ค์ ์ ๋๋ค.agents.list[].tools.elevated๋ ์ถ๊ฐ๋ก ์ ํ๋ง ํ ์ ์์ต๋๋ค(๋ ๋ค ํ์ฉํด์ผ ํจ)./elevated on|off|ask|full์ ์ธ์ ํค๋ณ๋ก ์ํ๋ฅผ ์ ์ฅํ๋ฉฐ, ์ธ๋ผ์ธ ์ง์๋ฌธ์ ๋จ์ผ ๋ฉ์์ง์๋ง ์ ์ฉ๋ฉ๋๋ค.- Elevated
exec๋ ํธ์คํธ์์ ์คํ๋๋ฉฐ ์๋๋ฐ์ฑ์ ์ฐํํฉ๋๋ค. - ๋๊ตฌ ์ ์ฑ
์ ์ฌ์ ํ ์ ์ฉ๋๋ฉฐ,
exec๊ฐ ๊ฑฐ๋ถ๋๋ฉด elevated๋ ์ฌ์ฉํ ์ ์์ต๋๋ค.
agents.defaults.maxConcurrent๋ ์ธ์
์ ๋ฐ์ ๊ฑธ์ณ ๋ณ๋ ฌ๋ก ์คํ๋ ์ ์๋ ๋ด์ฅ ์์ด์ ํธ ์คํ์ ์ต๋ ๊ฐ์๋ฅผ ์ค์ ํฉ๋๋ค. ๊ฐ ์ธ์
์ ์ฌ์ ํ ์ง๋ ฌํ๋ฉ๋๋ค(์ธ์
ํค๋น ํ ๋ฒ์ ํ๋์ ์คํ). ๊ธฐ๋ณธ๊ฐ: 1.
agents.defaults.sandboxยถ
๋ด์ฅ ์์ด์ ํธ๋ฅผ ์ํ ์ ํ์ Docker ์๋๋ฐ์ฑ. ํธ์คํธ ์์คํ ์ ์ ๊ทผํ์ง ๋ชปํ๋๋ก ๋ฉ์ธ ์ธ์ ์ด ์๋ ์ธ์ ์ ๋์์ผ๋ก ํฉ๋๋ค.
์์ธํ ๋ด์ฉ: Sandboxing
๊ธฐ๋ณธ๊ฐ(ํ์ฑํ๋ ๊ฒฝ์ฐ):
- ๋ฒ์:
"agent"(์์ด์ ํธ๋น ํ๋์ ์ปจํ ์ด๋ + ์ํฌ์คํ์ด์ค) - Debian bookworm-slim ๊ธฐ๋ฐ ์ด๋ฏธ์ง
- ์์ด์ ํธ ์ํฌ์คํ์ด์ค ์ ๊ทผ:
workspaceAccess: "none"(๊ธฐ๋ณธ๊ฐ) "none":~/.openclaw/sandboxes์๋์ ๋ฒ์๋ณ ์๋๋ฐ์ค ์ํฌ์คํ์ด์ค๋ฅผ ์ฌ์ฉ"ro": ์๋๋ฐ์ค ์ํฌ์คํ์ด์ค๋ฅผ/workspace์ ์ ์งํ๊ณ , ์์ด์ ํธ ์ํฌ์คํ์ด์ค๋ฅผ/agent์ ์ฝ๊ธฐ ์ ์ฉ์ผ๋ก ๋ง์ดํธ(write/edit/apply_patch๋นํ์ฑํ)"rw": ์์ด์ ํธ ์ํฌ์คํ์ด์ค๋ฅผ/workspace์ ์ฝ๊ธฐ/์ฐ๊ธฐ ๋ง์ดํธ- ์๋ ์ ๋ฆฌ: ์ ํด > 24์๊ฐ ๋๋ ์๋ช > 7์ผ
- ๋๊ตฌ ์ ์ฑ
:
exec,process,read,write,edit,apply_patch,sessions_list,sessions_history,sessions_send,sessions_spawn,session_status๋ง ํ์ฉ(๊ฑฐ๋ถ ์ฐ์ ) tools.sandbox.tools๋ก ์ค์ ํ๊ณ , ์์ด์ ํธ๋ณ๋กagents.list[].tools.sandbox.tools์์ ์ค๋ฒ๋ผ์ด๋- ์๋๋ฐ์ค ์ ์ฑ
์์ ์ง์๋๋ ๋๊ตฌ ๊ทธ๋ฃน ์ฝ์ด:
group:runtime,group:fs,group:sessions,group:memory(Sandbox vs Tool Policy vs Elevated ์ฐธ์กฐ) - ์ ํ์ ์๋๋ฐ์ค ๋ธ๋ผ์ฐ์ (Chromium + CDP, noVNC ๊ด์ฐฐ์)
- ํ๋๋ ์ต์
:
network,user,pidsLimit,memory,cpus,ulimits,seccompProfile,apparmorProfile
๊ฒฝ๊ณ : scope: "shared"๋ ์ปจํ
์ด๋์ ์ํฌ์คํ์ด์ค๋ฅผ ๊ณต์ ํจ์ ์๋ฏธํฉ๋๋ค. ์ธ์
๊ฐ ๊ฒฉ๋ฆฌ๊ฐ ์์ต๋๋ค. ์ธ์
๋ณ ๊ฒฉ๋ฆฌ๋ฅผ ์ํด scope: "session"์ ์ฌ์ฉํ์ธ์.
๋ ๊ฑฐ์: perSession์ ์ฌ์ ํ ์ง์๋ฉ๋๋ค(true โ scope: "session", false โ scope: "shared").
setupCommand๋ ์ปจํ
์ด๋๊ฐ ์์ฑ๋ ํ ํ ๋ฒ๋ง ์คํ๋ฉ๋๋ค(์ปจํ
์ด๋ ๋ด๋ถ์์ sh -lc๋ฅผ ํตํด).
ํจํค์ง ์ค์น๋ฅผ ์ํด์๋ ๋คํธ์ํฌ ์ก์ , ์ฐ๊ธฐ ๊ฐ๋ฅํ ๋ฃจํธ ํ์ผ์์คํ
, ๊ทธ๋ฆฌ๊ณ root ์ฌ์ฉ์๋ฅผ ๋ณด์ฅํ์ธ์.
{
agents: {
defaults: {
sandbox: {
mode: "non-main", // off | non-main | all
scope: "agent", // session | agent | shared (agent is default)
workspaceAccess: "none", // none | ro | rw
workspaceRoot: "~/.openclaw/sandboxes",
docker: {
image: "openclaw-sandbox:bookworm-slim",
containerPrefix: "openclaw-sbx-",
workdir: "/workspace",
readOnlyRoot: true,
tmpfs: ["/tmp", "/var/tmp", "/run"],
network: "none",
user: "1000:1000",
capDrop: ["ALL"],
env: { LANG: "C.UTF-8" },
setupCommand: "apt-get update && apt-get install -y git curl jq",
// Per-agent override (multi-agent): agents.list[].sandbox.docker.*
pidsLimit: 256,
memory: "1g",
memorySwap: "2g",
cpus: 1,
ulimits: {
nofile: { soft: 1024, hard: 2048 },
nproc: 256,
},
seccompProfile: "/path/to/seccomp.json",
apparmorProfile: "openclaw-sandbox",
dns: ["1.1.1.1", "8.8.8.8"],
extraHosts: ["internal.service:10.0.0.5"],
binds: ["/var/run/docker.sock:/var/run/docker.sock", "/home/user/source:/source:rw"],
},
browser: {
enabled: false,
image: "openclaw-sandbox-browser:bookworm-slim",
containerPrefix: "openclaw-sbx-browser-",
cdpPort: 9222,
vncPort: 5900,
noVncPort: 6080,
headless: false,
enableNoVnc: true,
allowHostControl: false,
allowedControlUrls: ["http://10.0.0.42:18791"],
allowedControlHosts: ["browser.lab.local", "10.0.0.42"],
allowedControlPorts: [18791],
autoStart: true,
autoStartTimeoutMs: 12000,
},
prune: {
idleHours: 24, // 0 disables idle pruning
maxAgeDays: 7, // 0 disables max-age pruning
},
},
},
},
tools: {
sandbox: {
tools: {
allow: [
"exec",
"process",
"read",
"write",
"edit",
"apply_patch",
"sessions_list",
"sessions_history",
"sessions_send",
"sessions_spawn",
"session_status",
],
deny: ["browser", "canvas", "nodes", "cron", "discord", "gateway"],
},
},
},
}
๋ค์์ผ๋ก ๊ธฐ๋ณธ ์๋๋ฐ์ค ์ด๋ฏธ์ง๋ฅผ ํ ๋ฒ ๋น๋ํฉ๋๋ค:
scripts/sandbox-setup.sh
์ฐธ๊ณ : ์๋๋ฐ์ค ์ปจํ
์ด๋์ ๊ธฐ๋ณธ๊ฐ์ network: "none"์
๋๋ค. ์์ด์ ํธ์ ์์๋ฐ์ด๋ ์ ๊ทผ์ด ํ์ํ๋ฉด agents.defaults.sandbox.docker.network๋ฅผ "bridge"(๋๋ ์ฌ์ฉ์ ์ง์ ๋คํธ์ํฌ)๋ก ์ค์ ํ์ธ์.
์ฐธ๊ณ : ์ธ๋ฐ์ด๋ ์ฒจ๋ถ ํ์ผ์ ํ์ฑ ์ํฌ์คํ์ด์ค์ media/inbound/*์ ์คํ
์ด์ง๋ฉ๋๋ค. workspaceAccess: "rw"์ธ ๊ฒฝ์ฐ, ์ด๋ ํ์ผ์ด ์์ด์ ํธ ์ํฌ์คํ์ด์ค์ ๊ธฐ๋ก๋จ์ ์๋ฏธํฉ๋๋ค.
์ฐธ๊ณ : docker.binds๋ ์ถ๊ฐ ํธ์คํธ ๋๋ ํฐ๋ฆฌ๋ฅผ ๋ง์ดํธํ๋ฉฐ, ์ ์ญ ๋ฐ ์์ด์ ํธ๋ณ ๋ฐ์ธ๋๋ ๋ณํฉ๋ฉ๋๋ค.
์ ํ์ ๋ธ๋ผ์ฐ์ ์ด๋ฏธ์ง๋ฅผ ๋ค์์ผ๋ก ๋น๋ํฉ๋๋ค:
scripts/sandbox-browser-setup.sh
agents.defaults.sandbox.browser.enabled=true์ธ ๊ฒฝ์ฐ, ๋ธ๋ผ์ฐ์ ๋๊ตฌ๋ ์๋๋ฐ์ค๋ Chromium ์ธ์คํด์ค(CDP)๋ฅผ ์ฌ์ฉํฉ๋๋ค. noVNC๊ฐ ํ์ฑํ๋์ด ์์ผ๋ฉด(headless=false์ผ ๋ ๊ธฐ๋ณธ๊ฐ), noVNC URL์ด ์์คํ
ํ๋กฌํํธ์ ์ฃผ์
๋์ด ์์ด์ ํธ๊ฐ ์ด๋ฅผ ์ฐธ์กฐํ ์ ์์ต๋๋ค.
์ด๋ ๋ฉ์ธ ์ค์ ์์ browser.enabled๋ฅผ ํ์๋ก ํ์ง ์์ต๋๋ค. ์๋๋ฐ์ค ์ ์ด URL์ ์ธ์
๋ณ๋ก ์ฃผ์
๋ฉ๋๋ค.
agents.defaults.sandbox.browser.allowHostControl(๊ธฐ๋ณธ๊ฐ: false)์ ์๋๋ฐ์ค๋ ์ธ์
์ด ๋ธ๋ผ์ฐ์ ๋๊ตฌ๋ฅผ ํตํด ํธ์คํธ ๋ธ๋ผ์ฐ์ ์ ์ด ์๋ฒ๋ฅผ ๋ช
์์ ์ผ๋ก ๋์์ผ๋ก ์ง์ ํ ์ ์๊ฒ ํฉ๋๋ค (target: "host"). ์๊ฒฉํ ์๋๋ฐ์ค ๊ฒฉ๋ฆฌ๋ฅผ ์ํ๋ค๋ฉด ์ด ์ต์
์ ๋์ธ์.
์๊ฒฉ ์ ์ด๋ฅผ ์ํ ํ์ฉ ๋ชฉ๋ก:
allowedControlUrls:target: "custom"์ ๋ํด ํ์ฉ๋๋ ์ ํํ ์ ์ด URL.allowedControlHosts: ํ์ฉ๋๋ ํธ์คํธ๋ช (ํธ์คํธ๋ช ๋ง, ํฌํธ ์์).allowedControlPorts: ํ์ฉ๋๋ ํฌํธ(๊ธฐ๋ณธ๊ฐ: http=80, https=443). ๊ธฐ๋ณธ๊ฐ: ๋ชจ๋ ํ์ฉ ๋ชฉ๋ก์ด ์ค์ ๋์ง ์์(์ ํ ์์).allowHostControl์ ๊ธฐ๋ณธ๊ฐ์ false์ ๋๋ค.
models (์ปค์คํ
ํ๋ก๋ฐ์ด๋ + ๊ธฐ๋ณธ URL)ยถ
OpenClaw๋ pi-coding-agent ๋ชจ๋ธ ์นดํ๋ก๊ทธ๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ปค์คํ
ํ๋ก๋ฐ์ด๋๋ฅผ ์ถ๊ฐํ ์ ์์ต๋๋ค (LiteLLM, ๋ก์ปฌ OpenAI ํธํ ์๋ฒ, Anthropic ํ๋ก์ ๋ฑ)
~/.openclaw/agents/<agentId>/agent/models.json์ ์์ฑํ๊ฑฐ๋ OpenClaw ์ค์ ์ models.providers ์๋์ ๋์ผํ ์คํค๋ง๋ฅผ ์ ์ํ์ฌ ์ถ๊ฐํ ์ ์์ต๋๋ค.
ํ๋ก๋ฐ์ด๋๋ณ ๊ฐ์ + ์์ : /concepts/model-providers.
models.providers๊ฐ ์กด์ฌํ๋ฉด, OpenClaw๋ ์์ ์models.json์~/.openclaw/agents/<agentId>/agent/์ ์์ฑ/๋ณํฉํฉ๋๋ค:- ๊ธฐ๋ณธ ๋์: ๋ณํฉ (๊ธฐ์กด ํ๋ก๋ฐ์ด๋๋ฅผ ์ ์งํ๊ณ ์ด๋ฆ ๊ธฐ์ค์ผ๋ก ๋ฎ์ด์)
ํ์ผ ๋ด์ฉ์ ๋ฎ์ด์ฐ๋ ค๋ฉด models.mode: "replace"๋ก ์ค์ ํ์ธ์
`agents.defaults.model.primary`(ํ๋ก๋ฐ์ด๋/๋ชจ๋ธ)๋ฅผ ํตํด ๋ชจ๋ธ์ ์ ํํฉ๋๋ค.
{ agents: { defaults: { model: { primary: "custom-proxy/llama-3.1-8b" }, models: { "custom-proxy/llama-3.1-8b": {}, }, }, }, models: { mode: "merge", providers: { "custom-proxy": { baseUrl: "http://localhost:4000/v1", apiKey: "LITELLM_KEY", api: "openai-completions", models: [ { id: "llama-3.1-8b", name: "Llama 3.1 8B", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 128000, maxTokens: 32000, }, ], }, }, }, }ยถ
OpenCode Zen (๋ฉํฐ ๋ชจ๋ธ ํ๋ก์) OpenCode Zen์ ๋ชจ๋ธ๋ณ ์๋ํฌ์ธํธ๋ฅผ ์ ๊ณตํ๋ ๋ฉํฐ ๋ชจ๋ธ ๊ฒ์ดํธ์จ์ด์ ๋๋ค.
๋ ธํธ:
- OpenClaw๋ pi-ai์ ๋ด์ฅ
opencodeํ๋ก๋ฐ์ด๋๋ฅผ ์ฌ์ฉํฉ๋๋ค. https://opencode.ai/auth์์OPENCODE_API_KEY(๋๋OPENCODE_ZEN_API_KEY)๋ฅผ ์ค์ ํ์ธ์. - ๋ชจ๋ธ ์ฐธ์กฐ๋
opencode/<modelId>ํ์์ ์ฌ์ฉํฉ๋๋ค (์:opencode/claude-opus-4-6). agents.defaults.models๋ฅผ ํตํด ํ์ฉ ๋ชฉ๋ก์ ํ์ฑํํ ๊ฒฝ์ฐ, ์ฌ์ฉํ ๊ฐ ๋ชจ๋ธ์ ์ถ๊ฐํ์ธ์.
๋ฐ๋ก๊ฐ๊ธฐ: `openclaw onboard --auth-choice opencode-zen`.
{ agents: { defaults: { model: { primary: "opencode/claude-opus-4-6" }, models: { "opencode/claude-opus-4-6": { alias: "Opus" } }, }, }, }ยถ
Z.AI (GLM-4.7) โ ํ๋ก๋ฐ์ด๋ ๋ณ์นญ ์ง์ Z.AI ๋ชจ๋ธ์ ๋ด์ฅ zai ํ๋ก๋ฐ์ด๋๋ฅผ ํตํด ์ฌ์ฉํ ์ ์์ต๋๋ค.
ํ๊ฒฝ์ ZAI_API_KEY๋ฅผ ์ค์ ํ๊ณ ํ๋ก๋ฐ์ด๋/๋ชจ๋ธ ํ์์ผ๋ก ๋ชจ๋ธ์ ์ฐธ์กฐํ์ธ์.
๋ฐ๋ก๊ฐ๊ธฐ: `openclaw onboard --auth-choice zai-api-key`.
๋ ธํธ:
- { agents: { defaults: { model: { primary: "zai/glm-4.7" }, models: { "zai/glm-4.7": {} }, }, }, }
z.ai/*์z-ai/*๋ ํ์ฉ๋๋ ๋ณ์นญ์ด๋ฉฐzai/*๋ก ์ ๊ทํ๋ฉ๋๋ค.ZAI_API_KEY๊ฐ ์์ผ๋ฉดzai/*์ ๋ํ ์์ฒญ์ ๋ฐํ์์ ์ธ์ฆ ์ค๋ฅ๋ก ์คํจํฉ๋๋ค.- ์์ ์ค๋ฅ:
No API key found for provider "zai".Z.AI์ ์ผ๋ฐ API ์๋ํฌ์ธํธ๋https://api.z.ai/api/paas/v4์ ๋๋ค. GLM ์ฝ๋ฉ ์์ฒญ์ ์ ์ฉ ์ฝ๋ฉ ์๋ํฌ์ธํธhttps://api.z.ai/api/coding/paas/v4๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๋ด์ฅzaiํ๋ก๋ฐ์ด๋๋ ์ฝ๋ฉ ์๋ํฌ์ธํธ๋ฅผ ์ฌ์ฉํฉ๋๋ค. - ์ผ๋ฐ ์๋ํฌ์ธํธ๊ฐ ํ์ํ๋ค๋ฉด, ์์ ์ปค์คํ
ํ๋ก๋ฐ์ด๋ ์น์
์ ์ฐธ๊ณ ํ์ฌ
models.providers์์ ๊ธฐ๋ณธ URL์ ์ค๋ฒ๋ผ์ด๋ํ๋ ์ปค์คํ ํ๋ก๋ฐ์ด๋๋ฅผ ์ ์ํ์ธ์.
Moonshot AI (Kimi)ยถ
๋ฌธ์/์ค์ ์๋ ๊ฐ์ง ํ๋ ์ด์คํ๋๋ฅผ ์ฌ์ฉํ์ธ์. ์ค์ API ํค๋ฅผ ์ปค๋ฐํ์ง ๋ง์ธ์.
{
env: { MOONSHOT_API_KEY: "sk-..." },
agents: {
defaults: {
model: { primary: "moonshot/kimi-k2.5" },
models: { "moonshot/kimi-k2.5": { alias: "Kimi K2.5" } },
},
},
models: {
mode: "merge",
providers: {
moonshot: {
baseUrl: "https://api.moonshot.ai/v1",
apiKey: "${MOONSHOT_API_KEY}",
api: "openai-completions",
models: [
{
id: "kimi-k2.5",
name: "Kimi K2.5",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 256000,
maxTokens: 8192,
},
],
},
},
},
}
๋ ธํธ:
- ํ๊ฒฝ ๋ณ์์
MOONSHOT_API_KEY๋ฅผ ์ค์ ํ๊ฑฐ๋openclaw onboard --auth-choice moonshot-api-key๋ฅผ ์ฌ์ฉํ์ธ์. - ๋ชจ๋ธ ์ฐธ์กฐ:
moonshot/kimi-k2.5. - ์ค๊ตญ ์๋ํฌ์ธํธ์ ๊ฒฝ์ฐ ๋ค์ ์ค ํ๋๋ฅผ ์ ํํ์ธ์:
openclaw onboard --auth-choice moonshot-api-key-cn๋ฅผ ์คํํ์ธ์(๋ง๋ฒ์ฌ๊ฐhttps://api.moonshot.cn/v1๋ฅผ ์ค์ ํฉ๋๋ค), ๋๋models.providers.moonshot์baseUrl: "https://api.moonshot.cn/v1"๋ฅผ ์๋์ผ๋ก ์ค์ ํ์ธ์.
Kimi Codingยถ
Moonshot AI์ Kimi Coding ์๋ํฌ์ธํธ(Anthropic ํธํ, ๋ด์ฅ ํ๋ก๋ฐ์ด๋)๋ฅผ ์ฌ์ฉํ์ธ์:
{
env: { KIMI_API_KEY: "sk-..." },
agents: {
defaults: {
model: { primary: "kimi-coding/k2p5" },
models: { "kimi-coding/k2p5": { alias: "Kimi K2.5" } },
},
},
}
๋ ธํธ:
- ํ๊ฒฝ ๋ณ์์
KIMI_API_KEY๋ฅผ ์ค์ ํ๊ฑฐ๋openclaw onboard --auth-choice kimi-code-api-key๋ฅผ ์ฌ์ฉํ์ธ์. - ๋ชจ๋ธ ์ฐธ์กฐ:
kimi-coding/k2p5.
Synthetic (Anthropic ํธํ)ยถ
Synthetic์ Anthropic ํธํ ์๋ํฌ์ธํธ๋ฅผ ์ฌ์ฉํ์ธ์:
{
env: { SYNTHETIC_API_KEY: "sk-..." },
agents: {
defaults: {
model: { primary: "synthetic/hf:MiniMaxAI/MiniMax-M2.1" },
models: { "synthetic/hf:MiniMaxAI/MiniMax-M2.1": { alias: "MiniMax M2.1" } },
},
},
models: {
mode: "merge",
providers: {
synthetic: {
baseUrl: "https://api.synthetic.new/anthropic",
apiKey: "${SYNTHETIC_API_KEY}",
api: "anthropic-messages",
models: [
{
id: "hf:MiniMaxAI/MiniMax-M2.1",
name: "MiniMax M2.1",
reasoning: false,
input: ["text"],
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 192000,
maxTokens: 65536,
},
],
},
},
},
}
๋ ธํธ:
SYNTHETIC_API_KEY๋ฅผ ์ค์ ํ๊ฑฐ๋openclaw onboard --auth-choice synthetic-api-key๋ฅผ ์ฌ์ฉํ์ธ์.- ๋ชจ๋ธ ์ฐธ์กฐ:
synthetic/hf:MiniMaxAI/MiniMax-M2.1. - Anthropic ํด๋ผ์ด์ธํธ๊ฐ
/v1๋ฅผ ์๋์ผ๋ก ์ถ๊ฐํ๋ฏ๋ก Base URL์๋/v1๋ฅผ ํฌํจํ์ง ์์์ผ ํฉ๋๋ค.
๋ก์ปฌ ๋ชจ๋ธ(LM Studio) โ ๊ถ์ฅ ์ค์ ยถ
ํ์ฌ ๋ก์ปฌ ๊ฐ์ด๋๋ /gateway/local-models๋ฅผ ์ฐธ๊ณ ํ์ธ์. ์์ฝ(TL;DR): ์ถฉ๋ถํ ํ๋์จ์ด์์ LM Studio Responses API๋ก MiniMax M2.1์ ์คํํ๊ณ , ์ฅ์ ๋๋น์ฉ์ผ๋ก ํธ์คํฐ๋ ๋ชจ๋ธ์ ๋ณํฉ ์ํ๋ก ์ ์งํ์ธ์.
MiniMax M2.1ยถ
LM Studio ์์ด MiniMax M2.1์ ์ง์ ์ฌ์ฉ:
{
agent: {
model: { primary: "minimax/MiniMax-M2.1" },
models: {
"anthropic/claude-opus-4-6": { alias: "Opus" },
"minimax/MiniMax-M2.1": { alias: "Minimax" },
},
},
models: {
mode: "merge",
providers: {
minimax: {
baseUrl: "https://api.minimax.io/anthropic",
apiKey: "${MINIMAX_API_KEY}",
api: "anthropic-messages",
models: [
{
id: "MiniMax-M2.1",
name: "MiniMax M2.1",
reasoning: false,
input: ["text"],
// Pricing: update in models.json if you need exact cost tracking.
cost: { input: 15, output: 60, cacheRead: 2, cacheWrite: 10 },
contextWindow: 200000,
maxTokens: 8192,
},
],
},
},
},
}
๋ ธํธ:
- ํ๊ฒฝ ๋ณ์
MINIMAX_API_KEY๋ฅผ ์ค์ ํ๊ฑฐ๋openclaw onboard --auth-choice minimax-api๋ฅผ ์ฌ์ฉํ์ธ์. - ์ฌ์ฉ ๊ฐ๋ฅํ ๋ชจ๋ธ:
MiniMax-M2.1(๊ธฐ๋ณธ๊ฐ). - ์ ํํ ๋น์ฉ ์ถ์ ์ด ํ์ํ๋ฉด
models.json์์ ๊ฐ๊ฒฉ์ ์ ๋ฐ์ดํธํ์ธ์.
Cerebras (GLM 4.6 / 4.7)ยถ
Cerebras์ OpenAI ํธํ ์๋ํฌ์ธํธ๋ฅผ ์ฌ์ฉํ์ธ์:
{
env: { CEREBRAS_API_KEY: "sk-..." },
agents: {
defaults: {
model: {
primary: "cerebras/zai-glm-4.7",
fallbacks: ["cerebras/zai-glm-4.6"],
},
models: {
"cerebras/zai-glm-4.7": { alias: "GLM 4.7 (Cerebras)" },
"cerebras/zai-glm-4.6": { alias: "GLM 4.6 (Cerebras)" },
},
},
},
models: {
mode: "merge",
providers: {
cerebras: {
baseUrl: "https://api.cerebras.ai/v1",
apiKey: "${CEREBRAS_API_KEY}",
api: "openai-completions",
models: [
{ id: "zai-glm-4.7", name: "GLM 4.7 (Cerebras)" },
{ id: "zai-glm-4.6", name: "GLM 4.6 (Cerebras)" },
],
},
},
},
}
๋ ธํธ:
- Cerebras์๋
cerebras/zai-glm-4.7๋ฅผ ์ฌ์ฉํ๊ณ , Z.AI ์ง์ ์ฐ๊ฒฐ์๋zai/glm-4.7๋ฅผ ์ฌ์ฉํ์ธ์. - ํ๊ฒฝ ๋๋ ์ค์ ์์
CEREBRAS_API_KEY๋ฅผ ์ค์ ํ์ธ์.
๋ ธํธ:
- ์ง์๋๋ API:
openai-completions,openai-responses,anthropic-messages,google-generative-ai - ์ปค์คํ
์ธ์ฆ์ด ํ์ํ ๊ฒฝ์ฐ
authHeader: true+headers๋ฅผ ์ฌ์ฉํ์ธ์. models.json์ ๋ค๋ฅธ ์์น์ ์ ์ฅํ๋ ค๋ฉดOPENCLAW_AGENT_DIR(๋๋PI_CODING_AGENT_DIR)๋ก ์์ด์ ํธ ์ค์ ๋ฃจํธ๋ฅผ ์ค๋ฒ๋ผ์ด๋ํ์ธ์(๊ธฐ๋ณธ๊ฐ:~/.openclaw/agents/main/agent).
์ธ์
ยถ
์ธ์ ๋ฒ์, ๋ฆฌ์ ์ ์ฑ , ๋ฆฌ์ ํธ๋ฆฌ๊ฑฐ, ๊ทธ๋ฆฌ๊ณ ์ธ์ ์คํ ์ด๊ฐ ๊ธฐ๋ก๋๋ ์์น๋ฅผ ์ ์ดํฉ๋๋ค.
{
session: {
scope: "per-sender",
dmScope: "main",
identityLinks: {
alice: ["telegram:123456789", "discord:987654321012345678"],
},
reset: {
mode: "daily",
atHour: 4,
idleMinutes: 60,
},
resetByType: {
thread: { mode: "daily", atHour: 4 },
direct: { mode: "idle", idleMinutes: 240 },
group: { mode: "idle", idleMinutes: 120 },
},
resetTriggers: ["/new", "/reset"],
// Default is already per-agent under ~/.openclaw/agents/<agentId>/sessions/sessions.json
// You can override with {agentId} templating:
store: "~/.openclaw/agents/{agentId}/sessions/sessions.json",
// Direct chats collapse to agent:<agentId>:<mainKey> (default: "main").
mainKey: "main",
agentToAgent: {
// Max ping-pong reply turns between requester/target (0โ5).
maxPingPongTurns: 5,
},
sendPolicy: {
rules: [{ action: "deny", match: { channel: "discord", chatType: "group" } }],
default: "allow",
},
},
}
ํ๋:
mainKey: ๋ค์ด๋ ํธ ์ฑํ ๋ฒํท ํค(๊ธฐ๋ณธ๊ฐ:"main").agentId๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ ๊ธฐ๋ณธ DM ์ค๋ ๋๋ฅผ โ์ด๋ฆ ๋ณ๊ฒฝโํ๊ณ ์ถ์ ๋ ์ ์ฉํฉ๋๋ค.- ์๋๋ฐ์ค ์ฐธ๊ณ :
agents.defaults.sandbox.mode: "non-main"์ ๋ฉ์ธ ์ธ์ ์ ๊ฐ์งํ๊ธฐ ์ํด ์ด ํค๋ฅผ ์ฌ์ฉํฉ๋๋ค.mainKey์ ์ผ์นํ์ง ์๋ ๋ชจ๋ ์ธ์ ํค(๊ทธ๋ฃน/์ฑ๋)๋ ์๋๋ฐ์ค ์ฒ๋ฆฌ๋ฉ๋๋ค. dmScope: DM ์ธ์ ์ ๊ทธ๋ฃนํํ๋ ๋ฐฉ์(๊ธฐ๋ณธ๊ฐ:"main").main: ๋ชจ๋ DM์ด ์ฐ์์ฑ์ ์ํด ๋ฉ์ธ ์ธ์ ์ ๊ณต์ ํฉ๋๋ค.per-peer: ์ฑ๋ ์ ๋ฐ์์ ๋ฐ์ ์ ID๋ณ๋ก DM์ ๋ถ๋ฆฌํฉ๋๋ค.per-channel-peer: isolate DMs per channel + sender (recommended for multi-user inboxes).per-account-channel-peer: isolate DMs per account + channel + sender (recommended for multi-account inboxes).- Secure DM mode (recommended): set
session.dmScope: "per-channel-peer"when multiple people can DM the bot (shared inboxes, multi-person allowlists, ordmPolicy: "open"). identityLinks: map canonical ids to provider-prefixed peers so the same person shares a DM session across channels when usingper-peer,per-channel-peer, orper-account-channel-peer.- Example:
alice: ["telegram:123456789", "discord:987654321012345678"]. reset: primary reset policy. Defaults to daily resets at 4:00 AM local time on the gateway host.mode:dailyoridle(default:dailywhenresetis present).atHour: local hour (0-23) for the daily reset boundary.idleMinutes: sliding idle window in minutes. ์ผ์ผ + ์ ํด๊ฐ ๋ชจ๋ ๊ตฌ์ฑ๋ ๊ฒฝ์ฐ ๋จผ์ ๋ง๋ฃ๋๋ ์ชฝ์ด ์ฐ์ ํฉ๋๋ค.resetByType: per-session overrides fordirect,group, andthread. Legacydmkey is accepted as an alias fordirect.- If you only set legacy
session.idleMinuteswithout anyreset/resetByType, OpenClaw stays in idle-only mode for backward compatibility. heartbeatIdleMinutes: optional idle override for heartbeat checks (daily reset still applies when enabled).agentToAgent.maxPingPongTurns: max reply-back turns between requester/target (0โ5, default 5).sendPolicy.default:allowordenyfallback when no rule matches.sendPolicy.rules[]: match bychannel,chatType(direct|group|room), orkeyPrefix(e.g.cron:). First deny wins; otherwise allow.
skills (skills config)ยถ
Controls bundled allowlist, install preferences, extra skill folders, and per-skill
overrides. Applies to bundled skills and ~/.openclaw/skills (workspace skills
still win on name conflicts).
ํ๋:
allowBundled: ๋ฒ๋ค๋ skills ์ ์ฉ ์ ํ์ ํ์ฉ ๋ชฉ๋ก์ ๋๋ค. If set, only those bundled skills are eligible (managed/workspace skills unaffected).load.extraDirs: ์ค์บํ ์ถ๊ฐ skill ๋๋ ํ ๋ฆฌ(๊ฐ์ฅ ๋ฎ์ ์ฐ์ ์์).install.preferBrew: ๊ฐ๋ฅํ ๊ฒฝ์ฐ brew ์ค์น ๊ด๋ฆฌ์๋ฅผ ์ ํธํฉ๋๋ค(๊ธฐ๋ณธ๊ฐ: true).install.nodeManager: node installer preference (npm|pnpm|yarn, default: npm).entries.<skillKey>: per-skill config overrides.
Skill ๋ณ ํ๋:
enabled: ๋ฒ๋ค๋์๊ฑฐ๋ ์ค์น๋์ด ์๋๋ผ๋ skill ์ ๋นํ์ฑํํ๋ ค๋ฉดfalse๋ฅผ ์ค์ ํฉ๋๋ค.env: ์์ด์ ํธ ์คํ ์ ์ฃผ์ ๋๋ ํ๊ฒฝ ๋ณ์(์ด๋ฏธ ์ค์ ๋์ด ์์ง ์์ ๊ฒฝ์ฐ์๋ง).apiKey: optional convenience for skills that declare a primary env var (e.g.nano-banana-proโGEMINI_API_KEY).
Example:
{
skills: {
allowBundled: ["gemini", "peekaboo"],
load: {
extraDirs: ["~/Projects/agent-scripts/skills", "~/Projects/oss/some-skill-pack/skills"],
},
install: {
preferBrew: true,
nodeManager: "npm",
},
entries: {
"nano-banana-pro": {
apiKey: "GEMINI_KEY_HERE",
env: {
GEMINI_API_KEY: "GEMINI_KEY_HERE",
},
},
peekaboo: { enabled: true },
sag: { enabled: false },
},
},
}
plugins (extensions)ยถ
Controls plugin discovery, allow/deny, and per-plugin config. Plugins are loaded
from ~/.openclaw/extensions, <workspace>/.openclaw/extensions, plus any
plugins.load.paths entries. Config changes require a gateway restart.
See /plugin for full usage.
ํ๋:
enabled: master toggle for plugin loading (default: true).allow: optional allowlist of plugin ids; when set, only listed plugins load.deny: optional denylist of plugin ids (deny wins).load.paths: extra plugin files or directories to load (absolute or~).entries.<pluginId>: per-plugin overrides.enabled: setfalseto disable.config: plugin-specific config object (validated by the plugin if provided).
Example:
{
plugins: {
enabled: true,
allow: ["voice-call"],
load: {
paths: ["~/Projects/oss/voice-call-extension"],
},
entries: {
"voice-call": {
enabled: true,
config: {
provider: "twilio",
},
},
},
},
}
browser (openclaw-managed browser)ยถ
OpenClaw can start a dedicated, isolated Chrome/Brave/Edge/Chromium instance for openclaw and expose a small loopback control service.
Profiles can point at a remote Chromium-based browser via profiles.<name>.cdpUrl. Remote
profiles are attach-only (start/stop/reset are disabled).
browser.cdpUrl remains for legacy single-profile configs and as the base
scheme/host for profiles that only set cdpPort.
๊ธฐ๋ณธ๊ฐ:
- enabled:
true - evaluateEnabled:
true(setfalseto disableact:evaluateandwait --fn) - control service: loopback only (port derived from
gateway.port, default18791) - CDP URL:
http://127.0.0.1:18792(control service + 1, legacy single-profile) - profile color:
#FF4500(lobster-orange) - Note: the control server is started by the running gateway (OpenClaw.app menubar, or
openclaw gateway). - Auto-detect order: default browser if Chromium-based; otherwise Chrome โ Brave โ Edge โ Chromium โ Chrome Canary.
{
browser: {
enabled: true,
evaluateEnabled: true,
// cdpUrl: "http://127.0.0.1:18792", // legacy single-profile override
defaultProfile: "chrome",
profiles: {
openclaw: { cdpPort: 18800, color: "#FF4500" },
work: { cdpPort: 18801, color: "#0066CC" },
remote: { cdpUrl: "http://10.0.0.42:9222", color: "#00AA00" },
},
color: "#FF4500",
// Advanced:
// headless: false,
// noSandbox: false,
// executablePath: "/Applications/Brave Browser.app/Contents/MacOS/Brave Browser",
// attachOnly: false, // set true when tunneling a remote CDP to localhost
},
}
ui (Appearance)ยถ
Optional accent color used by the native apps for UI chrome (e.g. Talk Mode bubble tint).
If unset, clients fall back to a muted light-blue.
{
ui: {
seamColor: "#FF4500", // hex (RRGGBB or #RRGGBB)
// Optional: Control UI assistant identity override.
// If unset, the Control UI uses the active agent identity (config or IDENTITY.md).
assistant: {
name: "OpenClaw",
avatar: "CB", // emoji, short text, or image URL/data URI
},
},
}
gateway (Gateway server mode + bind)ยถ
Use gateway.mode to explicitly declare whether this machine should run the Gateway.
๊ธฐ๋ณธ๊ฐ:
- mode: unset (treated as โdo not auto-startโ)
- bind:
loopback - port:
18789(single port for WS + HTTP)
{
gateway: {
mode: "local", // or "remote"
port: 18789, // WS + HTTP multiplex
bind: "loopback",
// controlUi: { enabled: true, basePath: "/openclaw" }
// auth: { mode: "token", token: "your-token" } // token gates WS + Control UI access
// tailscale: { mode: "off" | "serve" | "funnel" }
},
}
Control UI base path:
gateway.controlUi.basePathsets the URL prefix where the Control UI is served.- Examples:
"/ui","/openclaw","/apps/openclaw". - Default: root (
/) (unchanged). gateway.controlUi.rootsets the filesystem root for Control UI assets (default:dist/control-ui).gateway.controlUi.allowInsecureAuthallows token-only auth for the Control UI when device identity is omitted (typically over HTTP). Default:false. Prefer HTTPS (Tailscale Serve) or127.0.0.1.gateway.controlUi.dangerouslyDisableDeviceAuthdisables device identity checks for the Control UI (token/password only). Default:false. Break-glass only.
๊ด๋ จ ๋ฌธ์:
์ ๋ขฐ๋ ํ๋ก์:
gateway.trustedProxies: list of reverse proxy IPs that terminate TLS in front of the Gateway.- When a connection comes from one of these IPs, OpenClaw uses
x-forwarded-for(orx-real-ip) to determine the client IP for local pairing checks and HTTP auth/local checks. - Only list proxies you fully control, and ensure they overwrite incoming
x-forwarded-for.
์ฐธ๊ณ :
openclaw gatewayrefuses to start unlessgateway.modeis set tolocal(or you pass the override flag).gateway.portcontrols the single multiplexed port used for WebSocket + HTTP (control UI, hooks, A2UI).- OpenAI Chat Completions endpoint: disabled by default; enable with
gateway.http.endpoints.chatCompletions.enabled: true. - Precedence:
--port>OPENCLAW_GATEWAY_PORT>gateway.port> default18789. - Gateway auth is required by default (token/password or Tailscale Serve identity). Non-loopback binds require a shared token/password.
- The onboarding wizard generates a gateway token by default (even on loopback).
gateway.remote.tokenis only for remote CLI calls; it does not enable local gateway auth.gateway.tokenis ignored.
Auth and Tailscale:
gateway.auth.modesets the handshake requirements (tokenorpassword). When unset, token auth is assumed.gateway.auth.tokenstores the shared token for token auth (used by the CLI on the same machine).- When
gateway.auth.modeis set, only that method is accepted (plus optional Tailscale headers). gateway.auth.passwordcan be set here, or viaOPENCLAW_GATEWAY_PASSWORD(recommended).gateway.auth.allowTailscaleallows Tailscale Serve identity headers (tailscale-user-login) to satisfy auth when the request arrives on loopback withx-forwarded-for,x-forwarded-proto, andx-forwarded-host. OpenClaw verifies the identity by resolving thex-forwarded-foraddress viatailscale whoisbefore accepting it. Whentrue, Serve requests do not need a token/password; setfalseto require explicit credentials. Defaults totruewhentailscale.mode = "serve"and auth mode is notpassword.gateway.tailscale.mode: "serve"uses Tailscale Serve (tailnet only, loopback bind).gateway.tailscale.mode: "funnel"exposes the dashboard publicly; requires auth.gateway.tailscale.resetOnExitresets Serve/Funnel config on shutdown.
Remote client defaults (CLI):
gateway.remote.urlsets the default Gateway WebSocket URL for CLI calls whengateway.mode = "remote".gateway.remote.transportselects the macOS remote transport (sshdefault,directfor ws/wss). Whendirect,gateway.remote.urlmust bews://orwss://.ws://hostdefaults to port18789.gateway.remote.tokensupplies the token for remote calls (leave unset for no auth).gateway.remote.passwordsupplies the password for remote calls (leave unset for no auth).
macOS app behavior:
- OpenClaw.app watches
~/.openclaw/openclaw.jsonand switches modes live whengateway.modeorgateway.remote.urlchanges. - If
gateway.modeis unset butgateway.remote.urlis set, the macOS app treats it as remote mode. - When you change connection mode in the macOS app, it writes
gateway.mode(andgateway.remote.url+gateway.remote.transportin remote mode) back to the config file.
{
gateway: {
mode: "remote",
remote: {
url: "ws://gateway.tailnet:18789",
token: "your-token",
password: "your-password",
},
},
}
Direct transport example (macOS app):
{
gateway: {
mode: "remote",
remote: {
transport: "direct",
url: "wss://gateway.example.ts.net",
token: "your-token",
},
},
}
gateway.reload (Config hot reload)ยถ
The Gateway watches ~/.openclaw/openclaw.json (or OPENCLAW_CONFIG_PATH) and applies changes automatically.
๋ชจ๋:
hybrid(default): hot-apply safe changes; restart the Gateway for critical changes.hot: only apply hot-safe changes; log when a restart is required.restart: restart the Gateway on any config change.off: disable hot reload.
{
gateway: {
reload: {
mode: "hybrid",
debounceMs: 300,
},
},
}
ํซ ๋ฆฌ๋ก๋ ๋งคํธ๋ฆญ์ค (ํ์ผ + ์ํฅ)ยถ
๊ฐ์๋๋ ํ์ผ:
~/.openclaw/openclaw.json(๋๋OPENCLAW_CONFIG_PATH)
ํซ ์ ์ฉ๋จ (์ ์ฒด ๊ฒ์ดํธ์จ์ด ์ฌ์์ ์์):
hooks(์นํ ์ธ์ฆ/๊ฒฝ๋ก/๋งคํ) +hooks.gmail(Gmail ์์ฒ ์ฌ์์)browser(๋ธ๋ผ์ฐ์ ์ ์ด ์๋ฒ ์ฌ์์)cron(ํฌ๋ก ์๋น์ค ์ฌ์์ + ๋์์ฑ ์ ๋ฐ์ดํธ)agents.defaults.heartbeat(ํํธ๋นํธ ๋ฌ๋ ์ฌ์์)web(WhatsApp ์น ์ฑ๋ ์ฌ์์)telegram,discord,signal,imessage(์ฑ๋ ์ฌ์์)agent,models,routing,messages,session,whatsapp,logging,skills,ui,talk,identity,wizard(๋์ ์ฝ๊ธฐ)
์ ์ฒด ๊ฒ์ดํธ์จ์ด ์ฌ์์ ํ์:
gateway(ํฌํธ/๋ฐ์ธ๋/์ธ์ฆ/์ ์ด UI/tailscale)bridge(๋ ๊ฑฐ์)๋์ค์ปค๋ฒ๋ฆฌcanvasHostํ๋ฌ๊ทธ์ธ- ์ ์ ์๊ฑฐ๋ ์ง์๋์ง ์๋ ์ค์ ๊ฒฝ๋ก (์์ ์ ์ํด ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ์์)
๋ฉํฐ ์ธ์คํด์ค ๊ฒฉ๋ฆฌยถ
ํ๋์ ํธ์คํธ์์ ์ฌ๋ฌ ๊ฒ์ดํธ์จ์ด๋ฅผ ์คํํ๋ ค๋ฉด(์ค๋ณต์ฑ ๋๋ ๊ตฌ์กฐ์ฉ ๋ด์ ์ํด), ์ธ์คํด์ค๋ณ ์ํ + ์ค์ ์ ๊ฒฉ๋ฆฌํ๊ณ ๊ณ ์ ํ ํฌํธ๋ฅผ ์ฌ์ฉํ์ธ์:
OPENCLAW_CONFIG_PATH(์ธ์คํด์ค๋ณ ์ค์ )OPENCLAW_STATE_DIR(์ธ์ /์๊ฒฉ ์ฆ๋ช )agents.defaults.workspace(๋ฉ๋ชจ๋ฆฌ)gateway.port(์ธ์คํด์ค๋ณ ๊ณ ์ )
- ํธ์ ํ๋๊ทธ (CLI):
-
openclaw --dev โฆโ~/.openclaw-dev๋ฅผ ์ฌ์ฉํ๊ณ ๊ธฐ๋ณธ๊ฐ19001์์ ํฌํธ๋ฅผ ์ด๋
-
openclaw --profile <name> โฆโ~/.openclaw-<name>๋ฅผ ์ฌ์ฉ (ํฌํธ๋ ์ค์ /ํ๊ฒฝ ๋ณ์/ํ๋๊ทธ๋ก ์ง์ )
- ํ์๋ ํฌํธ ๋งคํ(gateway/browser/canvas)์ Gateway runbook์ ์ฐธ์กฐํ์ธ์.
- ๋ธ๋ผ์ฐ์ /CDP ํฌํธ ๊ฒฉ๋ฆฌ ์์ธ ๋ด์ฉ์ Multiple gateways๋ฅผ ์ฐธ์กฐํ์ธ์.
Example:
40. OPENCLAW_CONFIG_PATH=~/.openclaw/a.json \
OPENCLAW_STATE_DIR=~/.openclaw-a \
openclaw gateway --port 19001
hooks (๊ฒ์ดํธ์จ์ด ์นํ
)ยถ
๊ฒ์ดํธ์จ์ด HTTP ์๋ฒ์ ๊ฐ๋จํ HTTP ์นํ ์๋ํฌ์ธํธ๋ฅผ ํ์ฑํํฉ๋๋ค.
๊ธฐ๋ณธ๊ฐ:
- enabled:
false - path:
/hooks - maxBodyBytes:
262144(256 KB)
{
hooks: {
enabled: true,
token: "shared-secret",
path: "/hooks",
presets: ["gmail"],
transformsDir: "~/.openclaw/hooks",
mappings: [
{
match: { path: "gmail" },
action: "agent",
wakeMode: "now",
name: "Gmail",
sessionKey: "hook:gmail:{{messages[0].id}}",
messageTemplate: "From: {{messages[0].from}}\nSubject: {{messages[0].subject}}\n{{messages[0].snippet}}",
deliver: true,
channel: "last",
model: "openai/gpt-5.2-mini",
},
],
},
}
์์ฒญ์๋ ํ ํ ํฐ์ด ํฌํจ๋์ด์ผ ํฉ๋๋ค:
Authorization: Bearer <token>๋๋x-openclaw-token: <token>
์๋ํฌ์ธํธ:
POST /hooks/wakeโ{ text, mode?: "now"|"next-heartbeat" }POST /hooks/agentโ{ message, name?, sessionKey?, wakeMode?, deliver?, channel?, to?, model?, thinking?, timeoutSeconds?}` ๋ฐํPOST /hooks/<name>โhooks.mappings๋ฅผ ํตํด ํด์๋จ
-
/hooks/agent๋ ํญ์ ๋ฉ์ธ ์ธ์ ์ ์์ฝ์ ๊ฒ์ํ๋ฉฐ(์ ํ์ ์ผ๋กwakeMode: "now"๋ฅผ ํตํด ์ฆ์ ํํธ๋นํธ๋ฅผ ํธ๋ฆฌ๊ฑฐํ ์ ์์). -
๋งคํ ์ฐธ๊ณ ์ฌํญ:
-
match.path๋/hooks๋ค์ ํ์ ๊ฒฝ๋ก์ ์ผ์นํฉ๋๋ค (์:/hooks/gmailโgmail).
-
match.source๋ ํ์ด๋ก๋ ํ๋์ ์ผ์นํฉ๋๋ค (์:{ source: "gmail" }) ๋ฐ๋ผ์ ๋ฒ์ฉ/hooks/ingest๊ฒฝ๋ก๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
-
{{messages[0].subject}}์ ๊ฐ์ ํ ํ๋ฆฟ์ ํ์ด๋ก๋์์ ๊ฐ์ ์ฝ์ต๋๋ค.
-
transform์ ํ ์ก์ ์ ๋ฐํํ๋ JS/TS ๋ชจ๋์ ๊ฐ๋ฆฌํฌ ์ ์์ต๋๋ค.
-
deliver: true๋ ์ต์ข ์๋ต์ ์ฑ๋๋ก ์ ์กํฉ๋๋ค;channel์ ๊ธฐ๋ณธ๊ฐ์last์ ๋๋ค (WhatsApp์ผ๋ก ํด๋ฐฑ).
-
- ์ด์ ์ ๋ฌ ๊ฒฝ๋ก๊ฐ ์๋ ๊ฒฝ์ฐ
channel+to๋ฅผ ๋ช ์์ ์ผ๋ก ์ค์ ํ์ธ์ (Telegram/Discord/Google Chat/Slack/Signal/iMessage/MS Teams์๋ ํ์).
- ์ด์ ์ ๋ฌ ๊ฒฝ๋ก๊ฐ ์๋ ๊ฒฝ์ฐ
-
model์ ์ด ํ ์คํ์ ์ฌ์ฉํ LLM์ ์ฌ์ ์ํฉ๋๋ค (provider/model๋๋ ๋ณ์นญ;agents.defaults.models๊ฐ ์ค์ ๋ ๊ฒฝ์ฐ ํ์ฉ ๋ชฉ๋ก์ ์์ด์ผ ํจ).
- Gmail ํฌํผ ์ค์ (
openclaw webhooks gmail setup/run์์ ์ฌ์ฉ๋จ):
23. {
hooks: {
gmail: {
account: "openclaw@gmail.com",
topic: "projects/<project-id>/topics/gog-gmail-watch",
subscription: "gog-gmail-watch-push",
pushToken: "shared-push-token",
hookUrl: "http://127.0.0.1:18789/hooks/gmail",
includeBody: true,
maxBytes: 20000,
renewEveryMinutes: 720,
serve: { bind: "127.0.0.1", port: 8788, path: "/" },
tailscale: { mode: "funnel", path: "/gmail-pubsub" },
// ์ ํ ์ฌํญ: Gmail ํ
์ฒ๋ฆฌ๋ฅผ ์ํด ๋ ์ ๋ ดํ ๋ชจ๋ธ ์ฌ์ฉ
// ์ธ์ฆ/๋ ์ดํธ๋ฆฌ๋ฐ/ํ์์์ ์ agents.defaults.model.fallbacks, ๊ทธ ๋ค์ primary๋ก ํด๋ฐฑ
model: "openrouter/meta-llama/llama-3.3-70b-instruct:free",
// ์ ํ ์ฌํญ: Gmail ํ
์ ๊ธฐ๋ณธ thinking ๋ ๋ฒจ
thinking: "off",
},
},
}
- Gmail ํ ์ ๋ํ ๋ชจ๋ธ ์ฌ์ ์:
-
hooks.gmail.model์ Gmail ํ ์ฒ๋ฆฌ์ ์ฌ์ฉํ ๋ชจ๋ธ์ ์ง์ ํฉ๋๋ค (๊ธฐ๋ณธ๊ฐ: ์ธ์ primary).
-
agents.defaults.models์provider/model์ฐธ์กฐ ๋๋ ๋ณ์นญ์ ํ์ฉํฉ๋๋ค.
-
- ์ธ์ฆ/๋ ์ดํธ๋ฆฌ๋ฐ/ํ์์์ ์
agents.defaults.model.fallbacks, ๊ทธ ๋ค์agents.defaults.model.primary๋ก ํด๋ฐฑํฉ๋๋ค.
- ์ธ์ฆ/๋ ์ดํธ๋ฆฌ๋ฐ/ํ์์์ ์
-
agents.defaults.models๊ฐ ์ค์ ๋ ๊ฒฝ์ฐ, ํ ๋ชจ๋ธ์ ํ์ฉ ๋ชฉ๋ก์ ํฌํจํ์ธ์.
-
- ์์ ์ ๊ตฌ์ฑ๋ ๋ชจ๋ธ์ด ๋ชจ๋ธ ์นดํ๋ก๊ทธ ๋๋ ํ์ฉ ๋ชฉ๋ก์ ์์ผ๋ฉด ๊ฒฝ๊ณ ํฉ๋๋ค.
-
hooks.gmail.thinking์ Gmail ํ ์ ๊ธฐ๋ณธ thinking ๋ ๋ฒจ์ ์ค์ ํ๋ฉฐ, ํ ๋ณthinking์ ์ํด ์ฌ์ ์๋ฉ๋๋ค.
- ๊ฒ์ดํธ์จ์ด ์๋ ์์:
-
hooks.enabled=true์ด๊ณhooks.gmail.account๊ฐ ์ค์ ๋์ด ์์ผ๋ฉด, ๊ฒ์ดํธ์จ์ด๋ ๋ถํ ์gog gmail watch serve๋ฅผ ์์ํ๊ณ ์์น๋ฅผ ์๋ ๊ฐฑ์ ํฉ๋๋ค.
-
- ์๋ ์์์ ๋นํ์ฑํํ๋ ค๋ฉด
OPENCLAW_SKIP_GMAIL_WATCHER=1์ ์ค์ ํ์ธ์ (์๋ ์คํ์ฉ).
- ์๋ ์์์ ๋นํ์ฑํํ๋ ค๋ฉด
-
- ๊ฒ์ดํธ์จ์ด์ ํจ๊ป ๋ณ๋์
gog gmail watch serve๋ฅผ ์คํํ์ง ๋ง์ธ์;listen tcp 127.0.0.1:8788: bind: address already in use์ค๋ฅ๋ก ์คํจํฉ๋๋ค.
- ๊ฒ์ดํธ์จ์ด์ ํจ๊ป ๋ณ๋์
- ์ฐธ๊ณ :
tailscale.mode๊ฐ ์ผ์ ธ ์์ผ๋ฉด, Tailscale์ด/gmail-pubsub์ ์ฌ๋ฐ๋ฅด๊ฒ ํ๋ก์ํ ์ ์๋๋ก OpenClaw๋ ๊ธฐ๋ณธ์ ์ผ๋กserve.path๋ฅผ/๋ก ์ค์ ํฉ๋๋ค (์ค์ ๋ ๊ฒฝ๋ก ์ ๋์ฌ๋ฅผ ์ ๊ฑฐํจ). - ๋ฐฑ์๋๊ฐ ์ ๋์ฌ๊ฐ ๋ถ์ ๊ฒฝ๋ก๋ฅผ ๋ฐ์์ผ ํ๋ ๊ฒฝ์ฐ,
hooks.gmail.tailscale.target์ ์ ์ฒด URL๋ก ์ค์ ํ๊ณserve.path๋ฅผ ๋ง์ถ์ธ์.
37. canvasHost (LAN/ํ
์ผ๋ท Canvas ํ์ผ ์๋ฒ + ๋ผ์ด๋ธ ๋ฆฌ๋ก๋)ยถ
-
๊ฒ์ดํธ์จ์ด๋ HTML/CSS/JS ๋๋ ํฐ๋ฆฌ๋ฅผ HTTP๋ก ์ ๊ณตํ์ฌ iOS/Android ๋ ธ๋๊ฐ ๊ฐ๋จํ
canvas.navigate๋ก ์ ๊ทผํ ์ ์์ต๋๋ค. -
๊ธฐ๋ณธ ๋ฃจํธ:
~/.openclaw/workspace/canvas
๊ธฐ๋ณธ ํฌํธ:18793(openclaw ๋ธ๋ผ์ฐ์ CDP ํฌํธ18792์์ ์ถฉ๋์ ํผํ๊ธฐ ์ํด ์ ํ๋จ)
์๋ฒ๋ ๋ ธ๋๊ฐ ์ ๊ทผํ ์ ์๋๋ก ๊ฒ์ดํธ์จ์ด ๋ฐ์ธ๋ ํธ์คํธ(LAN ๋๋ Tailnet)์์ ์์ ํฉ๋๋ค. -
์๋ฒ:
canvasHost.root์๋์ ํ์ผ์ ์ ๊ณตํฉ๋๋ค- ์ ๊ณต๋๋ HTML์ ์์ฃผ ์์ ๋ผ์ด๋ธ ๋ฆฌ๋ก๋ ํด๋ผ์ด์ธํธ๋ฅผ ์ฃผ์ ํฉ๋๋ค
- ๋๋ ํฐ๋ฆฌ๋ฅผ ๊ฐ์ํ๊ณ
/__openclaw__/ws์ WebSocket ์๋ํฌ์ธํธ๋ฅผ ํตํด ๋ฆฌ๋ก๋๋ฅผ ๋ธ๋ก๋์บ์คํธํฉ๋๋ค - ๋๋ ํฐ๋ฆฌ๊ฐ ๋น์ด ์์ ๋ ์์์ฉ
index.html์ ์๋ ์์ฑํฉ๋๋ค (์ฆ์ ๋ฌด์ธ๊ฐ๊ฐ ๋ณด์ด๋๋ก) /__openclaw__/a2ui/์์ A2UI๋ ์ ๊ณตํ๋ฉฐ ๋ ธ๋์canvasHostUrl๋ก ๊ด๊ณ ๋ฉ๋๋ค (Canvas/A2UI์ ๋ํด ๋ ธ๋๊ฐ ํญ์ ์ฌ์ฉ)
๋๋ ํฐ๋ฆฌ๊ฐ ํฌ๊ฑฐ๋ EMFILE์ ๋๋ฌํ๋ฉด ๋ผ์ด๋ธ ๋ฆฌ๋ก๋(๋ฐ ํ์ผ ๊ฐ์)๋ฅผ ๋นํ์ฑํํฉ๋๋ค:
- config:
canvasHost: { liveReload: false }
{
canvasHost: {
root: "~/.openclaw/workspace/canvas",
port: 18793,
liveReload: true,
},
}
canvasHost.* ๋ณ๊ฒฝ ์ฌํญ์ ๊ฒ์ดํธ์จ์ด ์ฌ์์์ด ํ์ํฉ๋๋ค (config reload ์ ์ฌ์์๋จ).
๋นํ์ฑํํ๋ ค๋ฉด:
- config:
canvasHost: { enabled: false } - env:
OPENCLAW_SKIP_CANVAS_HOST=1
bridge (๋ ๊ฑฐ์ TCP ๋ธ๋ฆฌ์ง, ์ ๊ฑฐ๋จ)ยถ
ํ์ฌ ๋น๋์๋ ๋ ์ด์ TCP ๋ธ๋ฆฌ์ง ๋ฆฌ์ค๋๊ฐ ํฌํจ๋์ง ์์ผ๋ฉฐ bridge.* ์ค์ ํค๋ ๋ฌด์๋ฉ๋๋ค.
๋
ธ๋๋ ๊ฒ์ดํธ์จ์ด WebSocket์ ํตํด ์ฐ๊ฒฐํฉ๋๋ค. ์ด ์น์
์ ์ญ์ฌ์ ์ฐธ๊ณ ์ฉ์ผ๋ก ์ ์ง๋ฉ๋๋ค.
๋ ๊ฑฐ์ ๋์:
- ๊ฒ์ดํธ์จ์ด๋ ๋
ธ๋(iOS/Android)๋ฅผ ์ํด ๊ฐ๋จํ TCP ๋ธ๋ฆฌ์ง๋ฅผ ๋
ธ์ถํ ์ ์์์ผ๋ฉฐ, ์ผ๋ฐ์ ์ผ๋ก ํฌํธ
18790์ ์ฌ์ฉํ์ต๋๋ค.
๊ธฐ๋ณธ๊ฐ:
- enabled:
true - port:
18790 - bind:
lan(0.0.0.0์ ๋ฐ์ธ๋)
๋ฐ์ธ๋ ๋ชจ๋:
lan:0.0.0.0(LAN/WiโFi ๋ฐ Tailscale์ ํฌํจํ ๋ชจ๋ ์ธํฐํ์ด์ค์์ ์ ๊ทผ ๊ฐ๋ฅ)tailnet: ๋จธ์ ์ Tailscale IP์๋ง ๋ฐ์ธ๋ (Vienna โ London์ ๊ถ์ฅ)loopback:127.0.0.1(๋ก์ปฌ ์ ์ฉ)auto: tailnet IP๊ฐ ์์ผ๋ฉด ์ฐ์ , ์์ผ๋ฉดlan
TLS:
bridge.tls.enabled: ๋ธ๋ฆฌ์ง ์ฐ๊ฒฐ์ TLS ํ์ฑํ (ํ์ฑํ ์ TLS ์ ์ฉ).bridge.tls.autoGenerate: ์ธ์ฆ์/ํค๊ฐ ์์ ๋ ์์ฒด ์๋ช ์ธ์ฆ์๋ฅผ ์์ฑ (๊ธฐ๋ณธ๊ฐ: true).bridge.tls.certPath/bridge.tls.keyPath: ๋ธ๋ฆฌ์ง ์ธ์ฆ์ + ๊ฐ์ธ ํค์ PEM ๊ฒฝ๋ก.bridge.tls.caPath: ์ ํ์ PEM CA ๋ฒ๋ค (์ปค์คํ ๋ฃจํธ ๋๋ ํฅํ mTLS).
TLS๊ฐ ํ์ฑํ๋๋ฉด ๊ฒ์ดํธ์จ์ด๋ ๋
ธ๋๊ฐ ์ธ์ฆ์๋ฅผ ๊ณ ์ (pin)ํ ์ ์๋๋ก discovery TXT ๋ ์ฝ๋์ bridgeTls=1๊ณผ bridgeTlsSha256์ ๊ด๊ณ ํฉ๋๋ค. ์๋ ์ฐ๊ฒฐ์ ์์ง ์ง๋ฌธ์ด ์ ์ฅ๋์ง ์์ ๊ฒฝ์ฐ ์ต์ด ์ ๋ขฐ(TOFU)๋ฅผ ์ฌ์ฉํฉ๋๋ค.
์๋ ์์ฑ ์ธ์ฆ์๋ PATH์ openssl์ด ํ์ํฉ๋๋ค; ์์ฑ์ ์คํจํ๋ฉด ๋ธ๋ฆฌ์ง๋ ์์๋์ง ์์ต๋๋ค.
{
bridge: {
enabled: true,
port: 18790,
bind: "tailnet",
tls: {
enabled: true,
// ์๋ต ์ ~/.openclaw/bridge/tls/bridge-{cert,key}.pem ์ฌ์ฉ
// certPath: "~/.openclaw/bridge/tls/bridge-cert.pem",
// keyPath: "~/.openclaw/bridge/tls/bridge-key.pem"
},
},
}
discovery.mdns (Bonjour / mDNS ๋ธ๋ก๋์บ์คํธ ๋ชจ๋)ยถ
LAN mDNS ๋์ค์ปค๋ฒ๋ฆฌ ๋ธ๋ก๋์บ์คํธ(_openclaw-gw._tcp)๋ฅผ ์ ์ดํฉ๋๋ค.
minimal(๊ธฐ๋ณธ๊ฐ): TXT ๋ ์ฝ๋์์cliPath+sshPort๋ฅผ ์๋ตfull: TXT ๋ ์ฝ๋์cliPath+sshPortํฌํจoff: mDNS ๋ธ๋ก๋์บ์คํธ๋ฅผ ์์ ํ ๋นํ์ฑํ- ํธ์คํธ๋ช
: ๊ธฐ๋ณธ๊ฐ์
openclaw(openclaw.local์ ๊ด๊ณ )OPENCLAW_MDNS_HOSTNAME์ผ๋ก ์ฌ์ ์ํฉ๋๋ค.
{
discovery: { mdns: { mode: "minimal" } },
}
discovery.wideArea (๊ด์ญ Bonjour / ์ ๋์บ์คํธ DNSโSD)ยถ
ํ์ฑํ๋๋ฉด Gateway๋ ๊ตฌ์ฑ๋ ๊ฒ์ ๋๋ฉ์ธ(์: openclaw.internal.)์ ์ฌ์ฉํ์ฌ ~/.openclaw/dns/ ์๋์ _openclaw-gw._tcp์ ๋ํ ์ ๋์บ์คํธ DNSโSD ์กด์ ์์ฑํฉ๋๋ค.
iOS/Android๊ฐ ๋คํธ์ํฌ๋ฅผ ๋์ด(Vienna โ London) ๊ฒ์ํ ์ ์๋๋ก ๋ค์๊ณผ ํจ๊ป ์ฌ์ฉํ์ธ์:
- ์ ํํ ๋๋ฉ์ธ์ ์ ๊ณตํ๋ ๊ฒ์ดํธ์จ์ด ํธ์คํธ์ DNS ์๋ฒ (CoreDNS ๊ถ์ฅ)
- ํด๋ผ์ด์ธํธ๊ฐ ํด๋น ๋๋ฉ์ธ์ ๊ฒ์ดํธ์จ์ด DNS ์๋ฒ๋ฅผ ํตํด ํด์ํ๋๋ก ํ๋ Tailscale ๋ถํ DNS
์ผํ์ฑ ์ค์ ๋์ฐ๋ฏธ (๊ฒ์ดํธ์จ์ด ํธ์คํธ):
openclaw dns setup --apply
{
discovery: { wideArea: { enabled: true } },
}
๋ฏธ๋์ด ๋ชจ๋ธ ํ ํ๋ฆฟ ๋ณ์ยถ
ํ
ํ๋ฆฟ ์๋ฆฌํ์๋ tools.media.*.models[].args ๋ฐ tools.media.models[].args(๊ทธ๋ฆฌ๊ณ ํฅํ ํ
ํ๋ฆฟ์ด ์ ์ฉ๋๋ ๋ชจ๋ ์ธ์ ํ๋)์์ ํ์ฅ๋ฉ๋๋ค.
| ๋ณ์ | ์ค๋ช | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|
{{Body}} |
์ ์ฒด ์ธ๋ฐ์ด๋ ๋ฉ์์ง ๋ณธ๋ฌธ | |||||||||
{{RawBody}} |
์๋ณธ ์ธ๋ฐ์ด๋ ๋ฉ์์ง ๋ณธ๋ฌธ(ํ์คํ ๋ฆฌ/๋ฐ์ ์ ๋ํผ ์์; ๋ช ๋ น ํ์ฑ์ ์ต์ ) | |||||||||
{{BodyStripped}} |
๊ทธ๋ฃน ๋ฉ์ ์ด ์ ๊ฑฐ๋ ๋ณธ๋ฌธ(์์ด์ ํธ์ ๊ฐ์ฅ ์ ํฉํ ๊ธฐ๋ณธ๊ฐ) | |||||||||
{{From}} |
๋ฐ์ ์ ์๋ณ์(WhatsApp์ ๊ฒฝ์ฐ E.164; ์ฑ๋๋ณ๋ก ๋ค๋ฅผ ์ ์์) | |||||||||
{{To}} |
์์ ์ ์๋ณ์ | |||||||||
{{MessageSid}} |
์ฑ๋ ๋ฉ์์ง ID(์ฌ์ฉ ๊ฐ๋ฅํ ๊ฒฝ์ฐ) | |||||||||
{{SessionId}} |
ํ์ฌ ์ธ์ UUID | |||||||||
{{IsNewSession}} |
์ ์ธ์
์ด ์์ฑ๋์์ ๋ "true" |
|||||||||
{{MediaUrl}} |
์ธ๋ฐ์ด๋ ๋ฏธ๋์ด ์์ฌ-URL(์๋ ๊ฒฝ์ฐ) | |||||||||
{{MediaPath}} |
๋ก์ปฌ ๋ฏธ๋์ด ๊ฒฝ๋ก(๋ค์ด๋ก๋๋ ๊ฒฝ์ฐ) | |||||||||
{{MediaType}} |
๋ฏธ๋์ด ์ ํ(image/audio/document/โฆ) | |||||||||
{{Transcript}} |
Audio transcript (when enabled) | |||||||||
{{Prompt}} |
Resolved media prompt for CLI entries | |||||||||
{{MaxChars}} |
Resolved max output chars for CLI entries | |||||||||
{{ChatType}} |
"direct" or "group" |
|||||||||
{{GroupSubject}} |
Group subject (best effort) | |||||||||
{{GroupMembers}} |
Group members preview (best effort) | |||||||||
{{SenderName}} |
Sender display name (best effort) | |||||||||
{{SenderE164}} |
Sender phone number (best effort) | |||||||||
{{Provider}} |
Provider hint (whatsapp | telegram | discord | googlechat | slack | signal | imessage | msteams | webchat | โฆ) |
Cron (Gateway scheduler)ยถ
Cron is a Gateway-owned scheduler for wakeups and scheduled jobs. See Cron jobs for the feature overview and CLI examples.
{
cron: {
enabled: true,
maxConcurrentRuns: 2,
},
}
๋ค์: Agent Runtime ๐ฆ