{
  "_readme": [
    "VISITOR THREAD CONFIG  ->  ~/.cumulus/threads/demoapp-v.config.json",
    "",
    "WHY THIS FILE EXISTS, AND WHY THE '-v' MATTERS",
    "The server hands the browser THREAD_ID = 'demoapp-v' and device-thread.js",
    "appends 16 hex characters, so every visitor gets 'demoapp-v-<deviceId>'.",
    "Config is resolved by prefix-fallback: a turn on 'demoapp-v-a3f8c2d1' looks for",
    "its own exact file, then strips one trailing '-segment' at a time and takes the",
    "longest match:",
    "",
    "    demoapp-v-a3f8c2d1.config.json   (none — visitors never get their own)",
    "    demoapp-v.config.json            <- THIS FILE. Every visitor turn.",
    "    demoapp.config.json              (only if this file is absent)",
    "",
    "That middle layer is the whole point. Without it, visitor turns inherit your",
    "management thread's config and run your expensive model for every anonymous",
    "visitor. With it, the two are set independently and neither can affect the",
    "other — writes are always exact, so a visitor session can never mutate this",
    "file or the base.",
    "",
    "COST IS THE MAIN DIAL. Visitor traffic is unbounded and mostly shallow, so a",
    "small fast model is usually right here even when the base thread runs a large",
    "one. Both live gateway apps on this box do exactly that.",
    "",
    "Apply with:  node agent/apply-thread-configs.mjs",
    "Keys starting with '_' are annotations and are stripped before sending."
  ],

  "projectDir": "/absolute/path/to/your/app",
  "model": "claude",
  "claudeModel": "claude-haiku-4-5",
  "effort": "medium",
  "alwaysInclude": ["docs/demoapp-system-prompt.md"],

  "_allowedTools": [
    "THE ONLY TOOLS A VISITOR TURN MAY USE. This is an allowlist, so it is",
    "deny-by-default: anything not named here is refused, including tools that a",
    "future cumulus or Claude CLI release adds. That is the point — the alternative",
    "is a denylist, which silently grants every newly-added tool to every visitor of",
    "every deployed app until you notice and edit it. One real app needed 47 deny",
    "entries to get a safe surface; this list is five.",
    "",
    "Your app's own MCP shim tools are NOT affected: cumulus does not know their",
    "names, so it cannot deny them. They stay available, which is what you want —",
    "they are the tools you deliberately gave this namespace.",
    "",
    "Names may be bare ('read_file') or fully qualified",
    "('mcp__cumulus-history__read_file'). An entry that matches nothing is inert,",
    "and inertness DENIES — a typo fails closed, so check the journal after a deploy.",
    "",
    "Start from this list and add only what your app actually needs. Note what is",
    "absent and why: Bash/Write/Edit (a visitor must not run shell or write files),",
    "Task/Workflow (spawning sub-agents on anonymous traffic), send_to_agent (a",
    "visitor could message your maintainer threads), schedule_trigger (a visitor",
    "could arm turns that fire long after they leave), forget_content (destructive).",
    "",
    "READING FILES — WHY 'read_file' IS HERE AND 'Read' IS NOT.",
    "They are not interchangeable. 'Read' is the Claude CLI's own built-in tool and",
    "cumulus has no root hook into it: allow it and a visitor can ask for any file",
    "the gateway user can read, including ~/.cumulus/gateway.config.json, which",
    "holds your API keys and every provider credential. 'read_file' is cumulus's",
    "own tool, and while Read is denied it is CONFINED to this thread's projectDir",
    "— so a visitor can reach your app's files and nothing above them. Denying Read",
    "is what switches that confinement on, so the two lines work as a pair. If you",
    "add 'Read' back you give up the confinement entirely; prefer widening",
    "projectDir instead.",
    "",
    "Omit this key entirely to leave a thread unrestricted."
  ],
  "allowedTools": ["read_file", "search_content", "retrieve_content", "search_history"],

  "_disallowedTools": [
    "Applied ON TOP of the allowlist — it can subtract, never add. Redundant while",
    "the allowlist above omits AskUserQuestion, and kept as the explicit statement",
    "of why: there is nobody on the other end of a visitor turn, so a question hangs.",
    "If you widen allowedTools, this line keeps holding."
  ],
  "disallowedTools": ["AskUserQuestion"]
}
