{"record":{"id":"501c0aeb57a9fd01","repo":"JuliusBrussee/caveman","slug":"cave-host-sandbox-nested-under-required","errorCode":"cave_host_sandbox_nested_under_required","errorMessage":"cave_host_sandbox_nested_under_required","messagePattern":"cave_host_sandbox_nested_under_required","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/agent/src/definition-graph.ts","lineNumber":27,"sourceCode":"  // reached under a sandbox-required ancestor must be re-checked, not skipped.\n  const visited = [new Set<AgentDefinition>(), new Set<AgentDefinition>()];\n  const active = new Set<AgentDefinition>();\n\n  const visit = (\n    definition: AgentDefinition,\n    depth: number,\n    sandboxRequired: boolean,\n  ): void => {\n    if (!definition || definition.kind !== \"agent\" ||\n        !Array.isArray(definition.tools)) {\n      throw new Error(\"cave_agent_definition_invalid\");\n    }\n    if (depth > 8) throw new Error(\"cave_subagent_depth_limit\");\n    if (active.has(definition)) throw new Error(\"cave_subagent_definition_cycle\");\n    // Host mode is an opt-in the root makes for itself. A descendant cannot use\n    // it to run closures outside an ancestor's required containment.\n    if (sandboxRequired && definition.sandbox === \"host\") {\n      throw new Error(\"cave_host_sandbox_nested_under_required\");\n    }\n    const memo = visited[sandboxRequired ? 1 : 0]!;\n    if (memo.has(definition)) return;\n    active.add(definition);\n    const childSandboxRequired = sandboxRequired ||\n      definition.sandbox === \"required\";\n    const names = new Set<string>();\n    for (const declared of definition.tools) {\n      if (!declared || declared.kind !== \"tool\" ||\n          typeof declared.name !== \"string\") {\n        throw new Error(\"cave_tool_definition_invalid\");\n      }\n      if (names.has(declared.name)) throw new Error(\"cave_duplicate_tool_name\");\n      names.add(declared.name);\n      if (declared.name.startsWith(\"cave_\")) {\n        throw new Error(`cave_reserved_tool_name:${declared.name}`);\n      }\n      if (typeof Reflect.get(declared, TOOL_IMPLEMENTATION_SOURCE) !== \"string\") {","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/definition-graph.ts#L9-L45","documentation":"Sandbox policy is monotonic: once any ancestor declares sandbox: 'required', no descendant may declare sandbox: 'host'. Host mode is an opt-in the ROOT makes for itself; a child attempting to escape an ancestor's containment by declaring host mode is a policy violation and is rejected during graph validation, before anything runs.","triggerScenarios":"Agent A with sandbox: 'required' has a tool whose runtime.definition (subagent) B declares sandbox: 'host'; or a deeper descendant of a required ancestor does so.","commonSituations":"Composing agents from libraries where one module assumed host privileges while the composition root mandated containment; copy-pasting a host-mode agent under a hardened orchestrator; attempts to get shell access beneath a sandboxed runner.","solutions":["Change the descendant's sandbox from 'host' to 'required' (inherit containment) or remove the explicit override","If host execution is genuinely needed, the ROOT agent must be the one declaring host mode, with no required ancestor above it","Audit third-party agent definitions for their sandbox field before nesting them under a required root"],"exampleFix":"// before\nconst root = agent({ kind: \"agent\", sandbox: \"required\", tools: [\n  tool({ name: \"delegate\", runtime: { kind: \"subagent\", definition: hostChild } }) // hostChild has sandbox: \"host\"\n]});\n\n// after\nconst containedChild = agent({ kind: \"agent\", sandbox: \"required\", tools: [...] });\nconst root = agent({ kind: \"agent\", sandbox: \"required\", tools: [\n  tool({ name: \"delegate\", runtime: { kind: \"subagent\", definition: containedChild } })\n]});","handlingStrategy":"validation","validationCode":"function assertSandboxMonotonic(def: any, required = false): void {\n  const nowRequired = required || def.sandbox === \"required\";\n  if (required && def.sandbox === \"host\") {\n    throw new Error(`agent ${def.name} declares host mode under a required-sandbox ancestor`);\n  }\n  for (const t of def.tools ?? []) {\n    if (t.runtime?.kind === \"subagent\") assertSandboxMonotonic(t.runtime.definition, nowRequired);\n  }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Apply a single sandbox policy at composition time; do not mix host-mode children under required-mode roots","Audit third-party agent definitions' sandbox field before nesting them","Only the root definition should ever declare sandbox: 'host'"],"tags":["security","sandbox","policy","subagent","containment"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}