Community translations by veiseule.ai โ€” Help improve them on Crowdin
Skip to main content

๊ตฌ์„ฑ ๐Ÿ”งยถ

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=1
  • OPENCLAW_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) URL
  • data: 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 whatsapp
  • openclaw 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)
      },
    },
  },
}

ํ•ด๊ฒฐ ์ˆœ์„œ:

  1. DM ๋ณ„ ์žฌ์ •์˜: channels.<provider>.dms[userId].historyLimit
  2. ํ”„๋กœ๋ฐ”์ด๋” ๊ธฐ๋ณธ๊ฐ’: channels.<provider>.dmHistoryLimit
  3. ์ œํ•œ ์—†์Œ (๋ชจ๋“  ํžˆ์Šคํ† ๋ฆฌ ์œ ์ง€)

์ง€์› ํ”„๋กœ๋ฐ”์ด๋”: 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 (์„ ํƒ ์‚ฌํ•ญ; ์ฑ„๋„๋ณ„)

๊ฒฐ์ •์  ๋งค์นญ ์ˆœ์„œ:

  1. match.peer
  2. match.guildId
  3. match.teamId
  4. match.accountId (์ •ํ™• ์ผ์น˜, peer/guild/team ์—†์Œ)
  5. match.accountId: "*" (์ฑ„๋„ ์ „์ฒด, peer/guild/team ์—†์Œ)
  6. ๊ธฐ๋ณธ ์—์ด์ „ํŠธ (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: true enables /config (reads/writes openclaw.json).
  • channels.<provider>.configWrites gates config mutations initiated by that channel (default: true). This applies to /config set|unset plus provider-specific auto-migrations (Telegram supergroup ID changes, Slack channel ID changes).
  • commands.debug: true enables /debug (runtime-only overrides).
  • commands.restart: true enables /restart and the gateway tool restart action.
  • commands.useAccessGroups: false allows 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 stream streams 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 by channels.discord.textChunkLimit (default 2000). Set channels.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>.responsePrefix
  • channels.<channel>.accounts.<id>.responsePrefix

ํ•ด๊ฒฐ ์ˆœ์„œ (๊ฐ€์žฅ ๊ตฌ์ฒด์ ์ธ ํ•ญ๋ชฉ์ด ์šฐ์„ ):

  1. channels.<channel>.accounts.<id>.responsePrefix
  2. channels.<channel>.responsePrefix
  3. messages.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-6
  • sonnet -> anthropic/claude-sonnet-4-5
  • gpt -> openai/gpt-5.2
  • gpt-mini -> openai/gpt-5-mini
  • gemini -> google/gemini-3-pro-preview
  • gemini-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: 3
  • softTrimRatio: 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: true
  • memoryFlush.softThresholdTokens: 4000
  • memoryFlush.prompt / memoryFlush.systemPrompt: built-in defaults with NO_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: true to 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 inherits minChars from blockStreamingChunk with maxChars capped to the channel text limit. Signal/Slack/Discord/Google Chat default to minChars: 1500 unless 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 (use minMs/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 to instant for direct chats / mentions and message for 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. Set 0m to disable.
  • model: optional override model for heartbeat runs (provider/model).
  • includeReasoning: when true, heartbeats will also deliver the separate Reasoning: 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[] ํ•ญ๋ชฉ:
    1. ์ œ๊ณต์ž ํ•ญ๋ชฉ (type: "provider" ๋˜๋Š” ์ƒ๋žต):
      1. provider: API ์ œ๊ณต์ž ID (openai, anthropic, google/gemini, groq ๋“ฑ).
      1. model: ๋ชจ๋ธ ID ์˜ค๋ฒ„๋ผ์ด๋“œ (์ด๋ฏธ์ง€์—๋Š” ํ•„์ˆ˜; ์˜ค๋””์˜ค ์ œ๊ณต์ž์˜ ๊ธฐ๋ณธ๊ฐ’์€ gpt-4o-mini-transcribe/whisper-large-v3-turbo, ๋น„๋””์˜ค์˜ ๊ธฐ๋ณธ๊ฐ’์€ gemini-3-flash-preview).
      1. profile / preferredProfile: ์ธ์ฆ ํ”„๋กœํ•„ ์„ ํƒ.
    1. CLI ํ•ญ๋ชฉ (type: "cli"):
      1. command: ์‹คํ–‰ํ•  ์‹คํ–‰ ํŒŒ์ผ.
      1. args: ํ…œํ”Œ๋ฆฟ ์ธ์ž ({{MediaPath}}, {{Prompt}}, {{MaxChars}} ๋“ฑ ์ง€์›).
    1. capabilities: ๊ณต์œ  ํ•ญ๋ชฉ์„ ์ œํ•œํ•˜๊ธฐ ์œ„ํ•œ ์„ ํƒ์  ๋ชฉ๋ก (image, audio, video). 9. ์ƒ๋žต ์‹œ ๊ธฐ๋ณธ๊ฐ’: openai/anthropic/minimax โ†’ ์ด๋ฏธ์ง€, google โ†’ ์ด๋ฏธ์ง€+์˜ค๋””์˜ค+๋น„๋””์˜ค, groq โ†’ ์˜ค๋””์˜ค.
    1. prompt, maxChars, maxBytes, timeoutSeconds, language๋Š” ํ•ญ๋ชฉ๋ณ„๋กœ ์˜ค๋ฒ„๋ผ์ด๋“œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  1. ๊ตฌ์„ฑ๋œ ๋ชจ๋ธ์ด ์—†์œผ๋ฉด (๋˜๋Š” enabled: false์ธ ๊ฒฝ์šฐ) ์ดํ•ด(understanding)๋Š” ๊ฑด๋„ˆ๋›ฐ๋ฉฐ, ๋ชจ๋ธ์€ ์—ฌ์ „ํžˆ ์›๋ณธ ์ฒจ๋ถ€ ํŒŒ์ผ์„ ๋ฐ›์Šต๋‹ˆ๋‹ค.

  2. ์ œ๊ณต์ž ์ธ์ฆ์€ ํ‘œ์ค€ ๋ชจ๋ธ ์ธ์ฆ ์ˆœ์„œ๋ฅผ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค(์ธ์ฆ ํ”„๋กœํ•„, 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" }],
      },
    },
  },
}
  1. agents.defaults.subagents๋Š” ํ•˜์œ„ ์—์ด์ „ํŠธ ๊ธฐ๋ณธ๊ฐ’์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค:
    1. model: ์ƒ์„ฑ๋œ ํ•˜์œ„ ์—์ด์ „ํŠธ์˜ ๊ธฐ๋ณธ ๋ชจ๋ธ(๋ฌธ์ž์—ด ๋˜๋Š” { primary, fallbacks }). 16. ์ƒ๋žต ์‹œ, ํ•˜์œ„ ์—์ด์ „ํŠธ๋Š” ์—์ด์ „ํŠธ๋ณ„ ๋˜๋Š” ํ˜ธ์ถœ๋ณ„๋กœ ์˜ค๋ฒ„๋ผ์ด๋“œ๋˜์ง€ ์•Š๋Š” ํ•œ ํ˜ธ์ถœ์ž์˜ ๋ชจ๋ธ์„ ์ƒ์†ํ•ฉ๋‹ˆ๋‹ค.
    1. maxConcurrent: ๋™์‹œ ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ํ•˜์œ„ ์—์ด์ „ํŠธ ์ตœ๋Œ€ ์ˆ˜(๊ธฐ๋ณธ๊ฐ’ 1).
    1. archiveAfterMinutes: N๋ถ„ ํ›„ ํ•˜์œ„ ์—์ด์ „ํŠธ ์„ธ์…˜์„ ์ž๋™ ์•„์นด์ด๋ธŒ(๊ธฐ๋ณธ๊ฐ’ 60; ๋น„ํ™œ์„ฑํ™”ํ•˜๋ ค๋ฉด 0 ์„ค์ •).
    1. ํ•˜์œ„ ์—์ด์ „ํŠธ๋ณ„ ๋„๊ตฌ ์ •์ฑ…: tools.subagents.tools.allow / tools.subagents.tools.deny (deny๊ฐ€ ์šฐ์„ ).
  1. tools.profile์€ tools.allow/tools.deny ์ด์ „์— ์ ์šฉ๋˜๋Š” ๊ธฐ๋ณธ ๋„๊ตฌ ํ—ˆ์šฉ ๋ชฉ๋ก์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค:
  • minimal: session_status ๋งŒ
  • coding: group:fs, group:runtime, group:sessions, group:memory, image
  • messaging: group:messaging, sessions_list, sessions_history, sessions_send, session_status
  • full: ์ œํ•œ ์—†์Œ (๋ฏธ์„ค์ •๊ณผ ๋™์ผ)

์—์ด์ „ํŠธ๋ณ„ ์žฌ์ •์˜: agents.list[].tools.profile.

์˜ˆ์‹œ (๊ธฐ๋ณธ์€ ๋ฉ”์‹œ์ง• ์ „์šฉ, Slack + Discord ๋„๊ตฌ๋„ ํ—ˆ์šฉ):

{
  tools: {
    profile: "messaging",
    allow: ["slack", "discord"],
  },
}

์˜ˆ์‹œ (์ฝ”๋”ฉ ํ”„๋กœํ•„์ด์ง€๋งŒ exec/process ๋Š” ์ „์—ญ ์ฐจ๋‹จ):

{
  tools: {
    profile: "coding",
    deny: ["group:runtime"],
  },
}
  1. tools.byProvider๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํŠน์ • ์ œ๊ณต์ž(๋˜๋Š” ๋‹จ์ผ provider/model)์— ๋Œ€ํ•ด ๋„๊ตฌ๋ฅผ ์ถ”๊ฐ€๋กœ ์ œํ•œํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์—์ด์ „ํŠธ๋ณ„ ์žฌ์ •์˜: agents.list[].tools.byProvider.

  2. ์ ์šฉ ์ˆœ์„œ: ๊ธฐ๋ณธ ํ”„๋กœํ•„ โ†’ ์ œ๊ณต์ž ํ”„๋กœํ•„ โ†’ ํ—ˆ์šฉ/๊ฑฐ๋ถ€ ์ •์ฑ….

  3. ์ œ๊ณต์ž ํ‚ค๋Š” provider(์˜ˆ: google-antigravity) ๋˜๋Š” provider/model (์˜ˆ: openai/gpt-5.2)์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์‹œ (์ „์—ญ ์ฝ”๋”ฉ ํ”„๋กœํ•„์€ ์œ ์ง€ํ•˜๋˜, Google Antigravity ์—๋Š” ์ตœ์†Œ ๋„๊ตฌ๋งŒ):

{
  tools: {
    profile: "coding",
    byProvider: {
      "google-antigravity": { profile: "minimal" },
    },
  },
}
  1. ์˜ˆ์‹œ(์ œ๊ณต์ž/๋ชจ๋ธ๋ณ„ ํ—ˆ์šฉ ๋ชฉ๋ก):
{
  tools: {
    allow: ["group:fs", "group:runtime", "sessions_list"],
    byProvider: {
      "openai/gpt-5.2": { allow: ["group:fs", "sessions_list"] },
    },
  },
}
  1. tools.allow / tools.deny๋Š” ์ „์—ญ ๋„๊ตฌ ํ—ˆ์šฉ/๊ฑฐ๋ถ€ ์ •์ฑ…์„ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค(deny๊ฐ€ ์šฐ์„ ).
  2. ๋งค์นญ์€ ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•˜์ง€ ์•Š์œผ๋ฉฐ * ์™€์ผ๋“œ์นด๋“œ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค("*"๋Š” ๋ชจ๋“  ๋„๊ตฌ๋ฅผ ์˜๋ฏธ).
  3. ์ด๋Š” Docker ์ƒŒ๋“œ๋ฐ•์Šค๊ฐ€ ๊บผ์ ธ ์žˆ์–ด๋„ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

  4. ์˜ˆ์‹œ(๋ธŒ๋ผ์šฐ์ €/์บ”๋ฒ„์Šค ์ „์ฒด ๋น„ํ™œ์„ฑํ™”):

29. {
  tools: { deny: ["browser", "canvas"] },
}
  1. ๋„๊ตฌ ๊ทธ๋ฃน(๋‹จ์ถ•ํ‚ค)์€ ์ „์—ญ ๋ฐ ์—์ด์ „ํŠธ๋ณ„ ๋„๊ตฌ ์ •์ฑ…์—์„œ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค:
  • group:runtime: exec, bash, process
  • group:fs: read, write, edit, apply_patch
  • group:sessions: sessions_list, sessions_history, sessions_send, sessions_spawn, session_status
  • group:memory: memory_search, memory_get
  • group:web: web_search, web_fetch
  • group:ui: browser, canvas
  • group:automation: cron, gateway
  • group:messaging: message
  • group:nodes: nodes
  • group:openclaw: ๋ชจ๋“  ๊ธฐ๋ณธ ์ œ๊ณต OpenClaw ๋„๊ตฌ(ํ”„๋กœ๋ฐ”์ด๋” ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์ œ์™ธ)
  1. tools.elevated๋Š” ์ƒ์Šน๋œ(ํ˜ธ์ŠคํŠธ) ์‹คํ–‰ ์ ‘๊ทผ์„ ์ œ์–ดํ•ฉ๋‹ˆ๋‹ค:
    1. enabled: ์ƒ์Šน ๋ชจ๋“œ ํ—ˆ์šฉ(๊ธฐ๋ณธ๊ฐ’ true).
    1. allowFrom: ์ฑ„๋„๋ณ„ ํ—ˆ์šฉ ๋ชฉ๋ก(๋น„์–ด ์žˆ์œผ๋ฉด ๋น„ํ™œ์„ฑํ™”).
    1. whatsapp: E.164 ๋ฒˆํ˜ธ.
    1. telegram: ์ฑ„ํŒ… ID ๋˜๋Š” ์‚ฌ์šฉ์ž ์ด๋ฆ„.
    1. discord: ์‚ฌ์šฉ์ž ID ๋˜๋Š” ์‚ฌ์šฉ์ž ์ด๋ฆ„(์ƒ๋žต ์‹œ channels.discord.dm.allowFrom๋กœ ๋Œ€์ฒด).
    1. signal: E.164 ๋ฒˆํ˜ธ.
    1. imessage: ํ•ธ๋“ค์ด๋‚˜ ์ฑ„ํŒ… ID.
    1. 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, or dmPolicy: "open").
  • identityLinks: map canonical ids to provider-prefixed peers so the same person shares a DM session across channels when using per-peer, per-channel-peer, or per-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: daily or idle (default: daily when reset is present).
  • atHour: local hour (0-23) for the daily reset boundary.
  • idleMinutes: sliding idle window in minutes. ์ผ์ผ + ์œ ํœด๊ฐ€ ๋ชจ๋‘ ๊ตฌ์„ฑ๋œ ๊ฒฝ์šฐ ๋จผ์ € ๋งŒ๋ฃŒ๋˜๋Š” ์ชฝ์ด ์šฐ์„ ํ•ฉ๋‹ˆ๋‹ค.
  • resetByType: per-session overrides for direct, group, and thread. Legacy dm key is accepted as an alias for direct.
  • If you only set legacy session.idleMinutes without any reset/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: allow or deny fallback when no rule matches.
  • sendPolicy.rules[]: match by channel, chatType (direct|group|room), or keyPrefix (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: set false to 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 (set false to disable act:evaluate and wait --fn)
  • control service: loopback only (port derived from gateway.port, default 18791)
  • 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.basePath sets the URL prefix where the Control UI is served.
  • Examples: "/ui", "/openclaw", "/apps/openclaw".
  • Default: root (/) (unchanged).
  • gateway.controlUi.root sets the filesystem root for Control UI assets (default: dist/control-ui).
  • gateway.controlUi.allowInsecureAuth allows token-only auth for the Control UI when device identity is omitted (typically over HTTP). Default: false. Prefer HTTPS (Tailscale Serve) or 127.0.0.1.
  • gateway.controlUi.dangerouslyDisableDeviceAuth disables 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 (or x-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 gateway refuses to start unless gateway.mode is set to local (or you pass the override flag).
  • gateway.port controls 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 > default 18789.
  • 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.token is only for remote CLI calls; it does not enable local gateway auth. gateway.token is ignored.

Auth and Tailscale:

  • gateway.auth.mode sets the handshake requirements (token or password). When unset, token auth is assumed.
  • gateway.auth.token stores the shared token for token auth (used by the CLI on the same machine).
  • When gateway.auth.mode is set, only that method is accepted (plus optional Tailscale headers).
  • gateway.auth.password can be set here, or via OPENCLAW_GATEWAY_PASSWORD (recommended).
  • gateway.auth.allowTailscale allows Tailscale Serve identity headers (tailscale-user-login) to satisfy auth when the request arrives on loopback with x-forwarded-for, x-forwarded-proto, and x-forwarded-host. OpenClaw verifies the identity by resolving the x-forwarded-for address via tailscale whois before accepting it. When true, Serve requests do not need a token/password; set false to require explicit credentials. Defaults to true when tailscale.mode = "serve" and auth mode is not password.
  • gateway.tailscale.mode: "serve" uses Tailscale Serve (tailnet only, loopback bind).
  • gateway.tailscale.mode: "funnel" exposes the dashboard publicly; requires auth.
  • gateway.tailscale.resetOnExit resets Serve/Funnel config on shutdown.

Remote client defaults (CLI):

  • gateway.remote.url sets the default Gateway WebSocket URL for CLI calls when gateway.mode = "remote".
  • gateway.remote.transport selects the macOS remote transport (ssh default, direct for ws/wss). When direct, gateway.remote.url must be ws:// or wss://. ws://host defaults to port 18789.
  • gateway.remote.token supplies the token for remote calls (leave unset for no auth).
  • gateway.remote.password supplies the password for remote calls (leave unset for no auth).

macOS app behavior:

  • OpenClaw.app watches ~/.openclaw/openclaw.json and switches modes live when gateway.mode or gateway.remote.url changes.
  • If gateway.mode is unset but gateway.remote.url is set, the macOS app treats it as remote mode.
  • When you change connection mode in the macOS app, it writes gateway.mode (and gateway.remote.url + gateway.remote.transport in 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 (์ธ์Šคํ„ด์Šค๋ณ„ ๊ณ ์œ )
  1. ํŽธ์˜ ํ”Œ๋ž˜๊ทธ (CLI):
    1. openclaw --dev โ€ฆ โ†’ ~/.openclaw-dev๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ๊ธฐ๋ณธ๊ฐ’ 19001์—์„œ ํฌํŠธ๋ฅผ ์ด๋™
    1. openclaw --profile <name> โ€ฆ โ†’ ~/.openclaw-<name>๋ฅผ ์‚ฌ์šฉ (ํฌํŠธ๋Š” ์„ค์ •/ํ™˜๊ฒฝ ๋ณ€์ˆ˜/ํ”Œ๋ž˜๊ทธ๋กœ ์ง€์ •)
  1. ํŒŒ์ƒ๋œ ํฌํŠธ ๋งคํ•‘(gateway/browser/canvas)์€ Gateway runbook์„ ์ฐธ์กฐํ•˜์„ธ์š”.
  2. ๋ธŒ๋ผ์šฐ์ €/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๋ฅผ ํ†ตํ•ด ํ•ด์„๋จ
  1. /hooks/agent๋Š” ํ•ญ์ƒ ๋ฉ”์ธ ์„ธ์…˜์— ์š”์•ฝ์„ ๊ฒŒ์‹œํ•˜๋ฉฐ(์„ ํƒ์ ์œผ๋กœ wakeMode: "now"๋ฅผ ํ†ตํ•ด ์ฆ‰์‹œ ํ•˜ํŠธ๋น„ํŠธ๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•  ์ˆ˜ ์žˆ์Œ).

  2. ๋งคํ•‘ ์ฐธ๊ณ  ์‚ฌํ•ญ:

    1. match.path๋Š” /hooks ๋’ค์˜ ํ•˜์œ„ ๊ฒฝ๋กœ์™€ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค (์˜ˆ: /hooks/gmail โ†’ gmail).
    1. match.source๋Š” ํŽ˜์ด๋กœ๋“œ ํ•„๋“œ์™€ ์ผ์น˜ํ•ฉ๋‹ˆ๋‹ค (์˜ˆ: { source: "gmail" }) ๋”ฐ๋ผ์„œ ๋ฒ”์šฉ /hooks/ingest ๊ฒฝ๋กœ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    1. {{messages[0].subject}}์™€ ๊ฐ™์€ ํ…œํ”Œ๋ฆฟ์€ ํŽ˜์ด๋กœ๋“œ์—์„œ ๊ฐ’์„ ์ฝ์Šต๋‹ˆ๋‹ค.
    1. transform์€ ํ›… ์•ก์…˜์„ ๋ฐ˜ํ™˜ํ•˜๋Š” JS/TS ๋ชจ๋“ˆ์„ ๊ฐ€๋ฆฌํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
    1. deliver: true๋Š” ์ตœ์ข… ์‘๋‹ต์„ ์ฑ„๋„๋กœ ์ „์†กํ•ฉ๋‹ˆ๋‹ค; channel์˜ ๊ธฐ๋ณธ๊ฐ’์€ last์ž…๋‹ˆ๋‹ค (WhatsApp์œผ๋กœ ํด๋ฐฑ).
    1. ์ด์ „ ์ „๋‹ฌ ๊ฒฝ๋กœ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ channel + to๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์„ค์ •ํ•˜์„ธ์š” (Telegram/Discord/Google Chat/Slack/Signal/iMessage/MS Teams์—๋Š” ํ•„์ˆ˜).
    1. model์€ ์ด ํ›… ์‹คํ–‰์— ์‚ฌ์šฉํ•  LLM์„ ์žฌ์ •์˜ํ•ฉ๋‹ˆ๋‹ค (provider/model ๋˜๋Š” ๋ณ„์นญ; agents.defaults.models๊ฐ€ ์„ค์ •๋œ ๊ฒฝ์šฐ ํ—ˆ์šฉ ๋ชฉ๋ก์— ์žˆ์–ด์•ผ ํ•จ).
  1. 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",
    },
  },
}
  1. Gmail ํ›…์— ๋Œ€ํ•œ ๋ชจ๋ธ ์žฌ์ •์˜:
    1. hooks.gmail.model์€ Gmail ํ›… ์ฒ˜๋ฆฌ์— ์‚ฌ์šฉํ•  ๋ชจ๋ธ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค (๊ธฐ๋ณธ๊ฐ’: ์„ธ์…˜ primary).
    1. agents.defaults.models์˜ provider/model ์ฐธ์กฐ ๋˜๋Š” ๋ณ„์นญ์„ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.
    1. ์ธ์ฆ/๋ ˆ์ดํŠธ๋ฆฌ๋ฐ‹/ํƒ€์ž„์•„์›ƒ ์‹œ agents.defaults.model.fallbacks, ๊ทธ ๋‹ค์Œ agents.defaults.model.primary๋กœ ํด๋ฐฑํ•ฉ๋‹ˆ๋‹ค.
    1. agents.defaults.models๊ฐ€ ์„ค์ •๋œ ๊ฒฝ์šฐ, ํ›… ๋ชจ๋ธ์„ ํ—ˆ์šฉ ๋ชฉ๋ก์— ํฌํ•จํ•˜์„ธ์š”.
    1. ์‹œ์ž‘ ์‹œ ๊ตฌ์„ฑ๋œ ๋ชจ๋ธ์ด ๋ชจ๋ธ ์นดํƒˆ๋กœ๊ทธ ๋˜๋Š” ํ—ˆ์šฉ ๋ชฉ๋ก์— ์—†์œผ๋ฉด ๊ฒฝ๊ณ ํ•ฉ๋‹ˆ๋‹ค.
    1. hooks.gmail.thinking์€ Gmail ํ›…์˜ ๊ธฐ๋ณธ thinking ๋ ˆ๋ฒจ์„ ์„ค์ •ํ•˜๋ฉฐ, ํ›…๋ณ„ thinking์— ์˜ํ•ด ์žฌ์ •์˜๋ฉ๋‹ˆ๋‹ค.
  1. ๊ฒŒ์ดํŠธ์›จ์ด ์ž๋™ ์‹œ์ž‘:
    1. hooks.enabled=true์ด๊ณ  hooks.gmail.account๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฉด, ๊ฒŒ์ดํŠธ์›จ์ด๋Š” ๋ถ€ํŒ… ์‹œ gog gmail watch serve๋ฅผ ์‹œ์ž‘ํ•˜๊ณ  ์›Œ์น˜๋ฅผ ์ž๋™ ๊ฐฑ์‹ ํ•ฉ๋‹ˆ๋‹ค.
    1. ์ž๋™ ์‹œ์ž‘์„ ๋น„ํ™œ์„ฑํ™”ํ•˜๋ ค๋ฉด OPENCLAW_SKIP_GMAIL_WATCHER=1์„ ์„ค์ •ํ•˜์„ธ์š” (์ˆ˜๋™ ์‹คํ–‰์šฉ).
    1. ๊ฒŒ์ดํŠธ์›จ์ด์™€ ํ•จ๊ป˜ ๋ณ„๋„์˜ gog gmail watch serve๋ฅผ ์‹คํ–‰ํ•˜์ง€ ๋งˆ์„ธ์š”; listen tcp 127.0.0.1:8788: bind: address already in use ์˜ค๋ฅ˜๋กœ ์‹คํŒจํ•ฉ๋‹ˆ๋‹ค.
  1. ์ฐธ๊ณ : tailscale.mode๊ฐ€ ์ผœ์ ธ ์žˆ์œผ๋ฉด, Tailscale์ด /gmail-pubsub์„ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ํ”„๋ก์‹œํ•  ์ˆ˜ ์žˆ๋„๋ก OpenClaw๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ serve.path๋ฅผ /๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค (์„ค์ •๋œ ๊ฒฝ๋กœ ์ ‘๋‘์‚ฌ๋ฅผ ์ œ๊ฑฐํ•จ).
  2. ๋ฐฑ์—”๋“œ๊ฐ€ ์ ‘๋‘์‚ฌ๊ฐ€ ๋ถ™์€ ๊ฒฝ๋กœ๋ฅผ ๋ฐ›์•„์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ, hooks.gmail.tailscale.target์„ ์ „์ฒด URL๋กœ ์„ค์ •ํ•˜๊ณ  serve.path๋ฅผ ๋งž์ถ”์„ธ์š”.

37. canvasHost (LAN/ํ…Œ์ผ๋„ท Canvas ํŒŒ์ผ ์„œ๋ฒ„ + ๋ผ์ด๋ธŒ ๋ฆฌ๋กœ๋“œ)ยถ

  1. ๊ฒŒ์ดํŠธ์›จ์ด๋Š” HTML/CSS/JS ๋””๋ ‰ํ„ฐ๋ฆฌ๋ฅผ HTTP๋กœ ์ œ๊ณตํ•˜์—ฌ iOS/Android ๋…ธ๋“œ๊ฐ€ ๊ฐ„๋‹จํžˆ canvas.navigate๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  2. ๊ธฐ๋ณธ ๋ฃจํŠธ: ~/.openclaw/workspace/canvas
    ๊ธฐ๋ณธ ํฌํŠธ: 18793 (openclaw ๋ธŒ๋ผ์šฐ์ € CDP ํฌํŠธ 18792์™€์˜ ์ถฉ๋Œ์„ ํ”ผํ•˜๊ธฐ ์œ„ํ•ด ์„ ํƒ๋จ)
    ์„œ๋ฒ„๋Š” ๋…ธ๋“œ๊ฐ€ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ฒŒ์ดํŠธ์›จ์ด ๋ฐ”์ธ๋“œ ํ˜ธ์ŠคํŠธ(LAN ๋˜๋Š” Tailnet)์—์„œ ์ˆ˜์‹ ํ•ฉ๋‹ˆ๋‹ค.

  3. ์„œ๋ฒ„:

  • 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 ๐Ÿฆž