{"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":null,"httpStatus":null,"severity":"error","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/3ee70a102609e550bd2e68004bf5990a9341c851/packages/agent/src/definition-graph.ts#L9-L45","documentation":"Thrown by visit in validateAgentGraph when a nested agent definition declares sandbox: \"host\" while an ancestor in the chain has already required sandboxing (sandbox: \"required\" propagates to children via childSandboxRequired). Host mode would let the nested closure escape containment that an ancestor mandated, so it is rejected.","triggerScenarios":"A root or ancestor agent sets sandbox: \"required\", and any descendant definition (reached through a subagent tool's runtime.definition) sets sandbox: \"host\".","commonSituations":"Composing a sandboxed root agent with third-party subagent definitions that default to host mode, or upgrading a root to sandbox: \"required\" without auditing existing nested agents.","solutions":["Change the nested agent's sandbox from \"host\" to \"required\" (or another contained mode).","Remove sandbox: \"required\" from the ancestor if host execution is genuinely intended for the whole tree.","Replace the host-sandboxed subagent with a tool that performs the needed work inside the sandbox."],"exampleFix":"// before\nconst nested = { kind: \"agent\", sandbox: \"host\", tools: [] };\nconst root = { kind: \"agent\", sandbox: \"required\", tools: [subagentTool(nested)] };\n// after\nconst nested = { kind: \"agent\", sandbox: \"required\", tools: [] };\nconst root = { kind: \"agent\", sandbox: \"required\", tools: [subagentTool(nested)] };","handlingStrategy":"validation","validationCode":"function auditsHostSandbox(def: AgentDefinition, required = false): boolean {\n  if (required && def.sandbox === \"host\") return false;\n  const childRequired = required || def.sandbox === \"required\";\n  return (def.tools ?? []).every(t => t.runtime?.kind !== \"subagent\" || auditsHostSandbox(t.runtime.definition, childRequired));\n}\nif (!auditsHostSandbox(root)) throw new Error(\"host-sandboxed agent nested under required sandbox\");","typeGuard":null,"tryCatchPattern":"try {\n  validateAgentGraph(root);\n} catch (e) {\n  if (e instanceof Error && e.message === \"cave_host_sandbox_nested_under_required\") {\n    console.error(\"A nested agent uses sandbox: 'host' under a required-sandbox ancestor; set the child to 'required'\");\n  } else throw e;\n}","preventionTips":["When the root requires sandboxing, audit every nested third-party definition for sandbox: \"host\".","Default nested agents to sandbox: \"required\" to inherit safely.","Re-validate the whole tree after changing any ancestor's sandbox setting."],"tags":["sandbox","agent-definition","security"],"backgroundTag":"conflicting-config-options","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}