@nhtio/adk 0.1.0-master-2a4e1bd2 → 0.1.0-master-cd595e6f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +40 -0
- package/artifact_methods-CJDQ5Lv-.mjs +1 -0
- package/artifact_methods-D6k_82r3.js +1 -0
- package/batteries/artifacts/ecmascript.cjs +1 -1
- package/batteries/artifacts/ecmascript.mjs +1 -1
- package/batteries/artifacts/toon.cjs +1 -1
- package/batteries/artifacts/toon.mjs +1 -1
- package/batteries/artifacts/xml.cjs +1 -1
- package/batteries/artifacts/xml.mjs +1 -1
- package/batteries/artifacts/yaml.cjs +1 -1
- package/batteries/artifacts/yaml.mjs +1 -1
- package/batteries/artifacts.cjs +4 -4
- package/batteries/artifacts.mjs +4 -4
- package/batteries/dev-tools/forge.cjs +3 -3
- package/batteries/dev-tools/forge.mjs +3 -3
- package/batteries/dev-tools.cjs +2 -2
- package/batteries/dev-tools.mjs +2 -2
- package/batteries/encoding.cjs +7 -7
- package/batteries/encoding.mjs +7 -7
- package/batteries/generation/gemini/adapter.cjs +1 -1
- package/batteries/generation/gemini/adapter.mjs +1 -1
- package/batteries/generation/local_diffusion/adapter.cjs +1 -1
- package/batteries/generation/local_diffusion/adapter.mjs +1 -1
- package/batteries/generation/openai/adapter.cjs +1 -1
- package/batteries/generation/openai/adapter.mjs +1 -1
- package/batteries/isolation/browser.d.ts +1 -1
- package/batteries/isolation/child_process/transport.d.ts +2 -2
- package/batteries/isolation/child_process.cjs +4 -4
- package/batteries/isolation/child_process.cjs.map +1 -1
- package/batteries/isolation/child_process.mjs +4 -4
- package/batteries/isolation/child_process.mjs.map +1 -1
- package/batteries/isolation/codec.d.ts +2 -2
- package/batteries/isolation/crash_policy.d.ts +1 -1
- package/batteries/isolation/isolate_function.d.ts +2 -2
- package/batteries/isolation/protocol.d.ts +4 -4
- package/batteries/isolation/serve.d.ts +2 -2
- package/batteries/isolation/types.d.ts +4 -4
- package/batteries/isolation.cjs +1 -1
- package/batteries/isolation.mjs +1 -1
- package/batteries/llm/anthropic_messages/adapter.cjs +6 -6
- package/batteries/llm/anthropic_messages/adapter.cjs.map +1 -1
- package/batteries/llm/anthropic_messages/adapter.mjs +6 -6
- package/batteries/llm/anthropic_messages/adapter.mjs.map +1 -1
- package/batteries/llm/anthropic_messages/count_tokens.cjs +1 -1
- package/batteries/llm/anthropic_messages/count_tokens.mjs +1 -1
- package/batteries/llm/anthropic_messages/helpers.cjs +3 -3
- package/batteries/llm/anthropic_messages/helpers.mjs +3 -3
- package/batteries/llm/anthropic_messages/types.d.ts +2 -2
- package/batteries/llm/anthropic_messages/validation.cjs +3 -3
- package/batteries/llm/anthropic_messages/validation.cjs.map +1 -1
- package/batteries/llm/anthropic_messages/validation.mjs +3 -3
- package/batteries/llm/anthropic_messages/validation.mjs.map +1 -1
- package/batteries/llm/anthropic_messages.cjs +2 -2
- package/batteries/llm/anthropic_messages.mjs +2 -2
- package/batteries/llm/bedrock_converse/adapter.cjs +29 -7
- package/batteries/llm/bedrock_converse/adapter.cjs.map +1 -1
- package/batteries/llm/bedrock_converse/adapter.mjs +30 -8
- package/batteries/llm/bedrock_converse/adapter.mjs.map +1 -1
- package/batteries/llm/bedrock_converse/exceptions.cjs +3 -0
- package/batteries/llm/bedrock_converse/exceptions.cjs.map +1 -1
- package/batteries/llm/bedrock_converse/exceptions.d.ts +6 -0
- package/batteries/llm/bedrock_converse/exceptions.mjs +3 -1
- package/batteries/llm/bedrock_converse/exceptions.mjs.map +1 -1
- package/batteries/llm/bedrock_converse/helpers.cjs +1 -1
- package/batteries/llm/bedrock_converse/helpers.mjs +1 -1
- package/batteries/llm/bedrock_converse/types.d.ts +6 -2
- package/batteries/llm/bedrock_converse/validation.cjs +7 -1
- package/batteries/llm/bedrock_converse/validation.cjs.map +1 -1
- package/batteries/llm/bedrock_converse/validation.mjs +7 -1
- package/batteries/llm/bedrock_converse/validation.mjs.map +1 -1
- package/batteries/llm/bedrock_converse.cjs +2 -2
- package/batteries/llm/bedrock_converse.mjs +2 -2
- package/batteries/llm/claude_code_cli/adapter.cjs +6 -6
- package/batteries/llm/claude_code_cli/adapter.cjs.map +1 -1
- package/batteries/llm/claude_code_cli/adapter.mjs +6 -6
- package/batteries/llm/claude_code_cli/adapter.mjs.map +1 -1
- package/batteries/llm/claude_code_cli/helpers.cjs +3 -3
- package/batteries/llm/claude_code_cli/helpers.mjs +3 -3
- package/batteries/llm/claude_code_cli/types.d.ts +3 -2
- package/batteries/llm/claude_code_cli/validation.cjs +3 -3
- package/batteries/llm/claude_code_cli/validation.cjs.map +1 -1
- package/batteries/llm/claude_code_cli/validation.mjs +3 -3
- package/batteries/llm/claude_code_cli/validation.mjs.map +1 -1
- package/batteries/llm/claude_code_cli.cjs +1 -1
- package/batteries/llm/claude_code_cli.mjs +1 -1
- package/batteries/llm/gemini_generate_content/adapter.cjs +29 -7
- package/batteries/llm/gemini_generate_content/adapter.cjs.map +1 -1
- package/batteries/llm/gemini_generate_content/adapter.mjs +30 -8
- package/batteries/llm/gemini_generate_content/adapter.mjs.map +1 -1
- package/batteries/llm/gemini_generate_content/exceptions.cjs +3 -0
- package/batteries/llm/gemini_generate_content/exceptions.cjs.map +1 -1
- package/batteries/llm/gemini_generate_content/exceptions.d.ts +6 -0
- package/batteries/llm/gemini_generate_content/exceptions.mjs +3 -1
- package/batteries/llm/gemini_generate_content/exceptions.mjs.map +1 -1
- package/batteries/llm/gemini_generate_content/helpers.cjs +1 -1
- package/batteries/llm/gemini_generate_content/helpers.mjs +1 -1
- package/batteries/llm/gemini_generate_content/types.d.ts +7 -3
- package/batteries/llm/gemini_generate_content/validation.cjs +11 -2
- package/batteries/llm/gemini_generate_content/validation.cjs.map +1 -1
- package/batteries/llm/gemini_generate_content/validation.mjs +11 -2
- package/batteries/llm/gemini_generate_content/validation.mjs.map +1 -1
- package/batteries/llm/gemini_generate_content.cjs +1 -1
- package/batteries/llm/gemini_generate_content.mjs +1 -1
- package/batteries/llm/litert_lm/adapter.cjs +6 -6
- package/batteries/llm/litert_lm/adapter.cjs.map +1 -1
- package/batteries/llm/litert_lm/adapter.mjs +6 -6
- package/batteries/llm/litert_lm/adapter.mjs.map +1 -1
- package/batteries/llm/litert_lm/helpers.cjs +3 -3
- package/batteries/llm/litert_lm/helpers.mjs +3 -3
- package/batteries/llm/litert_lm/types.d.ts +2 -2
- package/batteries/llm/litert_lm/validation.cjs +3 -3
- package/batteries/llm/litert_lm/validation.cjs.map +1 -1
- package/batteries/llm/litert_lm/validation.mjs +3 -3
- package/batteries/llm/litert_lm/validation.mjs.map +1 -1
- package/batteries/llm/litert_lm.cjs +1 -1
- package/batteries/llm/litert_lm.mjs +1 -1
- package/batteries/llm/ollama/adapter.cjs +6 -6
- package/batteries/llm/ollama/adapter.cjs.map +1 -1
- package/batteries/llm/ollama/adapter.mjs +6 -6
- package/batteries/llm/ollama/adapter.mjs.map +1 -1
- package/batteries/llm/ollama/helpers.cjs +4 -4
- package/batteries/llm/ollama/helpers.mjs +4 -4
- package/batteries/llm/ollama/types.d.ts +2 -2
- package/batteries/llm/ollama/validation.cjs +3 -3
- package/batteries/llm/ollama/validation.cjs.map +1 -1
- package/batteries/llm/ollama/validation.mjs +3 -3
- package/batteries/llm/ollama/validation.mjs.map +1 -1
- package/batteries/llm/ollama.cjs +2 -2
- package/batteries/llm/ollama.mjs +2 -2
- package/batteries/llm/openai_chat_completions/adapter.cjs +6 -6
- package/batteries/llm/openai_chat_completions/adapter.cjs.map +1 -1
- package/batteries/llm/openai_chat_completions/adapter.mjs +6 -6
- package/batteries/llm/openai_chat_completions/adapter.mjs.map +1 -1
- package/batteries/llm/openai_chat_completions/helpers.cjs +4 -4
- package/batteries/llm/openai_chat_completions/helpers.mjs +4 -4
- package/batteries/llm/openai_chat_completions/types.d.ts +2 -2
- package/batteries/llm/openai_chat_completions/validation.cjs +3 -3
- package/batteries/llm/openai_chat_completions/validation.cjs.map +1 -1
- package/batteries/llm/openai_chat_completions/validation.mjs +3 -3
- package/batteries/llm/openai_chat_completions/validation.mjs.map +1 -1
- package/batteries/llm/openai_chat_completions.cjs +2 -2
- package/batteries/llm/openai_chat_completions.mjs +2 -2
- package/batteries/llm/openai_responses/adapter.cjs +6 -6
- package/batteries/llm/openai_responses/adapter.cjs.map +1 -1
- package/batteries/llm/openai_responses/adapter.mjs +6 -6
- package/batteries/llm/openai_responses/adapter.mjs.map +1 -1
- package/batteries/llm/openai_responses/helpers.cjs +3 -3
- package/batteries/llm/openai_responses/helpers.mjs +3 -3
- package/batteries/llm/openai_responses/types.d.ts +2 -2
- package/batteries/llm/openai_responses/validation.cjs +3 -3
- package/batteries/llm/openai_responses/validation.cjs.map +1 -1
- package/batteries/llm/openai_responses/validation.mjs +3 -3
- package/batteries/llm/openai_responses/validation.mjs.map +1 -1
- package/batteries/llm/openai_responses.cjs +2 -2
- package/batteries/llm/openai_responses.mjs +2 -2
- package/batteries/llm/transformers_js/adapter.cjs +6 -6
- package/batteries/llm/transformers_js/adapter.cjs.map +1 -1
- package/batteries/llm/transformers_js/adapter.mjs +6 -6
- package/batteries/llm/transformers_js/adapter.mjs.map +1 -1
- package/batteries/llm/transformers_js/helpers.cjs +3 -3
- package/batteries/llm/transformers_js/helpers.mjs +3 -3
- package/batteries/llm/transformers_js/types.d.ts +2 -2
- package/batteries/llm/transformers_js/validation.cjs +3 -3
- package/batteries/llm/transformers_js/validation.cjs.map +1 -1
- package/batteries/llm/transformers_js/validation.mjs +3 -3
- package/batteries/llm/transformers_js/validation.mjs.map +1 -1
- package/batteries/llm/transformers_js.cjs +1 -1
- package/batteries/llm/transformers_js.mjs +1 -1
- package/batteries/llm/webllm_chat_completions/adapter.cjs +6 -6
- package/batteries/llm/webllm_chat_completions/adapter.cjs.map +1 -1
- package/batteries/llm/webllm_chat_completions/adapter.mjs +6 -6
- package/batteries/llm/webllm_chat_completions/adapter.mjs.map +1 -1
- package/batteries/llm/webllm_chat_completions/helpers.cjs +1 -1
- package/batteries/llm/webllm_chat_completions/helpers.mjs +1 -1
- package/batteries/llm/webllm_chat_completions/validation.cjs +3 -3
- package/batteries/llm/webllm_chat_completions/validation.cjs.map +1 -1
- package/batteries/llm/webllm_chat_completions/validation.mjs +3 -3
- package/batteries/llm/webllm_chat_completions/validation.mjs.map +1 -1
- package/batteries/llm/webllm_chat_completions.cjs +2 -2
- package/batteries/llm/webllm_chat_completions.mjs +2 -2
- package/batteries/llm.cjs +2 -2
- package/batteries/llm.mjs +2 -2
- package/batteries/media/builder.d.ts +3 -0
- package/batteries/media/contracts.cjs.map +1 -1
- package/batteries/media/contracts.d.ts +40 -1
- package/batteries/media/contracts.mjs.map +1 -1
- package/batteries/media/engines/sharp.cjs +36 -2
- package/batteries/media/engines/sharp.cjs.map +1 -1
- package/batteries/media/engines/sharp.mjs +36 -2
- package/batteries/media/engines/sharp.mjs.map +1 -1
- package/batteries/media/forge.cjs +5 -5
- package/batteries/media/forge.mjs +5 -5
- package/batteries/media/index.d.ts +1 -1
- package/batteries/media/lint.cjs +3 -2
- package/batteries/media/lint.cjs.map +1 -1
- package/batteries/media/lint.mjs +3 -2
- package/batteries/media/lint.mjs.map +1 -1
- package/batteries/media.cjs +66 -2
- package/batteries/media.cjs.map +1 -1
- package/batteries/media.mjs +66 -2
- package/batteries/media.mjs.map +1 -1
- package/batteries/orchestration/conformance.cjs +123 -74
- package/batteries/orchestration/conformance.cjs.map +1 -1
- package/batteries/orchestration/conformance.d.ts +10 -8
- package/batteries/orchestration/conformance.mjs +54 -5
- package/batteries/orchestration/conformance.mjs.map +1 -1
- package/batteries/orchestration/dispatch_reasoner.cjs +4 -4
- package/batteries/orchestration/dispatch_reasoner.mjs +4 -4
- package/batteries/orchestration/executor.cjs +2 -2
- package/batteries/orchestration/executor.mjs +2 -2
- package/batteries/orchestration/forge.cjs +4 -4
- package/batteries/orchestration/forge.mjs +4 -4
- package/batteries/orchestration/validation.cjs +2 -2
- package/batteries/orchestration/validation.mjs +2 -2
- package/batteries/orchestration.cjs +4 -4
- package/batteries/orchestration.mjs +4 -4
- package/batteries/sandbox/contracts/policy_enforcer.d.ts +13 -1
- package/batteries/sandbox/js/compartment.d.ts +7 -2
- package/batteries/sandbox/js/exceptions.d.ts +4 -0
- package/batteries/sandbox/js/runner.d.ts +1 -1
- package/batteries/sandbox/js/service.d.ts +1 -1
- package/batteries/sandbox/js/ses_contracts.d.ts +4 -3
- package/batteries/sandbox/js.cjs +17 -8
- package/batteries/sandbox/js.cjs.map +1 -1
- package/batteries/sandbox/js.mjs +17 -9
- package/batteries/sandbox/js.mjs.map +1 -1
- package/batteries/sandbox/manager.d.ts +4 -3
- package/batteries/sandbox/node/srt_enforcer.d.ts +1 -1
- package/batteries/sandbox/node.cjs +36 -12
- package/batteries/sandbox/node.cjs.map +1 -1
- package/batteries/sandbox/node.mjs +36 -12
- package/batteries/sandbox/node.mjs.map +1 -1
- package/batteries/sandbox/tools.cjs +1 -1
- package/batteries/sandbox/tools.mjs +1 -1
- package/batteries/sandbox/types.d.ts +10 -1
- package/batteries/sandbox.cjs +27 -9
- package/batteries/sandbox.cjs.map +1 -1
- package/batteries/sandbox.mjs +28 -11
- package/batteries/sandbox.mjs.map +1 -1
- package/batteries/skills.cjs +9 -9
- package/batteries/skills.mjs +9 -9
- package/batteries/tools/_shared.cjs +1 -1
- package/batteries/tools/_shared.mjs +1 -1
- package/batteries/tools/color.cjs +2 -2
- package/batteries/tools/color.mjs +2 -2
- package/batteries/tools/comparison.cjs +2 -2
- package/batteries/tools/comparison.mjs +2 -2
- package/batteries/tools/data_structure.cjs +2 -2
- package/batteries/tools/data_structure.mjs +2 -2
- package/batteries/tools/datetime_extended.cjs +3 -3
- package/batteries/tools/datetime_extended.mjs +3 -3
- package/batteries/tools/datetime_math.cjs +3 -3
- package/batteries/tools/datetime_math.mjs +3 -3
- package/batteries/tools/encoding.cjs +2 -2
- package/batteries/tools/encoding.mjs +2 -2
- package/batteries/tools/formatting.cjs +2 -2
- package/batteries/tools/formatting.mjs +2 -2
- package/batteries/tools/geo_basics.cjs +2 -2
- package/batteries/tools/geo_basics.mjs +2 -2
- package/batteries/tools/math.cjs +2 -2
- package/batteries/tools/math.mjs +2 -2
- package/batteries/tools/memory.cjs +5 -5
- package/batteries/tools/memory.mjs +5 -5
- package/batteries/tools/parsing.cjs +3 -3
- package/batteries/tools/parsing.mjs +3 -3
- package/batteries/tools/retrievables.cjs +5 -5
- package/batteries/tools/retrievables.mjs +5 -5
- package/batteries/tools/scrapper.cjs +1 -1
- package/batteries/tools/scrapper.mjs +1 -1
- package/batteries/tools/searxng.cjs +1 -1
- package/batteries/tools/searxng.mjs +1 -1
- package/batteries/tools/standing_instructions.cjs +3 -3
- package/batteries/tools/standing_instructions.mjs +3 -3
- package/batteries/tools/statistics.cjs +3 -3
- package/batteries/tools/statistics.mjs +3 -3
- package/batteries/tools/string_processing.cjs +2 -2
- package/batteries/tools/string_processing.mjs +2 -2
- package/batteries/tools/structured_data.cjs +2 -2
- package/batteries/tools/structured_data.mjs +2 -2
- package/batteries/tools/text_analysis.cjs +3 -3
- package/batteries/tools/text_analysis.mjs +3 -3
- package/batteries/tools/text_comparison.cjs +2 -2
- package/batteries/tools/text_comparison.mjs +2 -2
- package/batteries/tools/time.cjs +3 -3
- package/batteries/tools/time.mjs +3 -3
- package/batteries/tools/unit_conversion.cjs +2 -2
- package/batteries/tools/unit_conversion.mjs +2 -2
- package/batteries/tools.cjs +2 -2
- package/batteries/tools.mjs +2 -2
- package/batteries/validation.cjs +5 -5
- package/batteries/validation.mjs +5 -5
- package/batteries/vector/conformance/index.d.ts +11 -16
- package/batteries/vector/conformance.cjs +107 -36
- package/batteries/vector/conformance.cjs.map +1 -1
- package/batteries/vector/conformance.mjs +84 -13
- package/batteries/vector/conformance.mjs.map +1 -1
- package/batteries/vector/retrievable.cjs +1 -1
- package/batteries/vector/retrievable.mjs +1 -1
- package/batteries.cjs +5 -5
- package/batteries.mjs +5 -5
- package/{chat_common-DbbIESdo.mjs → chat_common-CmKV5Dz4.mjs} +2 -2
- package/{chat_common-DbbIESdo.mjs.map → chat_common-CmKV5Dz4.mjs.map} +1 -1
- package/{chat_common-B3O3zz-i.js → chat_common-Dpan9moo.js} +2 -2
- package/{chat_common-B3O3zz-i.js.map → chat_common-Dpan9moo.js.map} +1 -1
- package/{common-v9Jo9M5k.mjs → common-BpdHIpQE.mjs} +7 -7
- package/{common-v9Jo9M5k.mjs.map → common-BpdHIpQE.mjs.map} +1 -1
- package/{common-BV3HGeLj.js → common-H44Ik9ou.js} +7 -7
- package/{common-BV3HGeLj.js.map → common-H44Ik9ou.js.map} +1 -1
- package/common.cjs +7 -7
- package/common.mjs +7 -7
- package/{dispatch_runner-CbNyqSMy.mjs → dispatch_runner-BZDXUe0G.mjs} +5 -5
- package/{dispatch_runner-CbNyqSMy.mjs.map → dispatch_runner-BZDXUe0G.mjs.map} +1 -1
- package/{dispatch_runner-Bl3Xwebu.js → dispatch_runner-CKhm-scw.js} +5 -5
- package/{dispatch_runner-Bl3Xwebu.js.map → dispatch_runner-CKhm-scw.js.map} +1 -1
- package/dispatch_runner.cjs +1 -1
- package/dispatch_runner.mjs +1 -1
- package/{ecmascript-BSnvyY8T.js → ecmascript-CCtgamDh.js} +3 -3
- package/{ecmascript-BSnvyY8T.js.map → ecmascript-CCtgamDh.js.map} +1 -1
- package/{ecmascript-CZ7_YKfZ.mjs → ecmascript-DcmUQ9Jx.mjs} +3 -3
- package/{ecmascript-CZ7_YKfZ.mjs.map → ecmascript-DcmUQ9Jx.mjs.map} +1 -1
- package/eslint.cjs +1 -1
- package/eslint.mjs +1 -1
- package/exceptions.cjs +1 -1
- package/exceptions.mjs +1 -1
- package/forge.cjs +2 -2
- package/forge.mjs +2 -2
- package/guards.cjs +7 -7
- package/guards.mjs +7 -7
- package/{helpers-CDmLDBq5.mjs → helpers-BH7HTtkD.mjs} +3 -3
- package/{helpers-CDmLDBq5.mjs.map → helpers-BH7HTtkD.mjs.map} +1 -1
- package/{helpers-Tvvwpx1M.js → helpers-CEjjkXso.js} +3 -3
- package/{helpers-Tvvwpx1M.js.map → helpers-CEjjkXso.js.map} +1 -1
- package/index.cjs +10 -10
- package/index.mjs +10 -10
- package/{isolation-BQCTQYZ_.js → isolation-7Jrcucgc.js} +11 -11
- package/isolation-7Jrcucgc.js.map +1 -0
- package/{isolation-7xbkc12r.mjs → isolation-CO0NiK8R.mjs} +11 -11
- package/isolation-CO0NiK8R.mjs.map +1 -0
- package/lib/classes/tokenizable.d.ts +2 -3
- package/mcp/adk-docs-corpus.json +2 -2
- package/package.json +469 -469
- package/{retrievable-C5dXuDhD.js → retrievable-BTwjmPnl.js} +3 -3
- package/{retrievable-C5dXuDhD.js.map → retrievable-BTwjmPnl.js.map} +1 -1
- package/{retrievable-gl_TIHnX.mjs → retrievable-CXTAUuuv.mjs} +3 -3
- package/{retrievable-gl_TIHnX.mjs.map → retrievable-CXTAUuuv.mjs.map} +1 -1
- package/{scrapper-DD6-_2u-.js → scrapper-CJexXXK-.js} +4 -4
- package/{scrapper-DD6-_2u-.js.map → scrapper-CJexXXK-.js.map} +1 -1
- package/{scrapper-u_orpxIB.mjs → scrapper-DiQ5FfDL.mjs} +4 -4
- package/{scrapper-u_orpxIB.mjs.map → scrapper-DiQ5FfDL.mjs.map} +1 -1
- package/{searxng-fVdJUCK2.mjs → searxng-BFTJ6rTE.mjs} +4 -4
- package/{searxng-fVdJUCK2.mjs.map → searxng-BFTJ6rTE.mjs.map} +1 -1
- package/{searxng-C7GK0Nrz.js → searxng-DzlGZG4V.js} +4 -4
- package/{searxng-C7GK0Nrz.js.map → searxng-DzlGZG4V.js.map} +1 -1
- package/server.json +2 -2
- package/skills/adk-assembly/SKILL.md +2 -2
- package/{spooled_artifact-DriNQRj7.js → spooled_artifact-BpcesRBv.js} +384 -384
- package/spooled_artifact-BpcesRBv.js.map +1 -0
- package/{spooled_artifact-BkueYJhR.mjs → spooled_artifact-Bw_7swIp.mjs} +385 -385
- package/spooled_artifact-Bw_7swIp.mjs.map +1 -0
- package/spooled_artifact.cjs +2 -2
- package/spooled_artifact.mjs +2 -2
- package/{spooled_markdown_artifact-CfHRZUkl.mjs → spooled_markdown_artifact-vd6eNtIP.mjs} +3 -3
- package/{spooled_markdown_artifact-CfHRZUkl.mjs.map → spooled_markdown_artifact-vd6eNtIP.mjs.map} +1 -1
- package/{spooled_markdown_artifact-C_VkCDhF.js → spooled_markdown_artifact-wJPnE8Ij.js} +3 -3
- package/{spooled_markdown_artifact-C_VkCDhF.js.map → spooled_markdown_artifact-wJPnE8Ij.js.map} +1 -1
- package/{thought-B8JAi3-W.mjs → thought-BE1KGEhV.mjs} +3 -3
- package/{thought-B8JAi3-W.mjs.map → thought-BE1KGEhV.mjs.map} +1 -1
- package/{thought--fRgOwTP.js → thought-bS7O9O5H.js} +3 -3
- package/{thought--fRgOwTP.js.map → thought-bS7O9O5H.js.map} +1 -1
- package/{tokenizable-CWW7iSrD.js → tokenizable-BMZuGWRd.js} +35 -29
- package/tokenizable-BMZuGWRd.js.map +1 -0
- package/{tokenizable-DLMP68ZV.mjs → tokenizable-VB4Av8GF.mjs} +36 -30
- package/tokenizable-VB4Av8GF.mjs.map +1 -0
- package/{tool_call-CPjyvkm4.mjs → tool_call-Cfaj-6zK.mjs} +4 -4
- package/{tool_call-CPjyvkm4.mjs.map → tool_call-Cfaj-6zK.mjs.map} +1 -1
- package/{tool_call-DgqVwL5n.js → tool_call-Fs_Uh5wQ.js} +4 -4
- package/{tool_call-DgqVwL5n.js.map → tool_call-Fs_Uh5wQ.js.map} +1 -1
- package/{tools-BmLzkvNr.mjs → tools-B3_8C78Q.mjs} +6 -6
- package/{tools-BmLzkvNr.mjs.map → tools-B3_8C78Q.mjs.map} +1 -1
- package/{tools-DmcIe2Cb.js → tools-CqOcmHR8.js} +6 -6
- package/{tools-DmcIe2Cb.js.map → tools-CqOcmHR8.js.map} +1 -1
- package/{toon-BvTQsKdN.mjs → toon-BSlr88px.mjs} +5 -5
- package/{toon-BvTQsKdN.mjs.map → toon-BSlr88px.mjs.map} +1 -1
- package/{toon-B05eKx-q.js → toon-D71tkyI3.js} +5 -5
- package/{toon-B05eKx-q.js.map → toon-D71tkyI3.js.map} +1 -1
- package/{turn_runner-DEGCvfVZ.mjs → turn_runner-BHHD6h77.mjs} +5 -5
- package/{turn_runner-DEGCvfVZ.mjs.map → turn_runner-BHHD6h77.mjs.map} +1 -1
- package/{turn_runner-DT8TseR4.js → turn_runner-DhLI7OTH.js} +5 -5
- package/{turn_runner-DT8TseR4.js.map → turn_runner-DhLI7OTH.js.map} +1 -1
- package/turn_runner.cjs +1 -1
- package/turn_runner.mjs +1 -1
- package/types-D6X_aUIb.mjs.map +1 -1
- package/types-DrSosVaM.js.map +1 -1
- package/{validate-lzy9nG_E.mjs → validate-Bi7FGjoq.mjs} +13 -1
- package/{validate-lzy9nG_E.mjs.map → validate-Bi7FGjoq.mjs.map} +1 -1
- package/{validate-CdOS7S2u.js → validate-CkQI-48O.js} +13 -1
- package/{validate-CdOS7S2u.js.map → validate-CkQI-48O.js.map} +1 -1
- package/{xml-DDTIMpY3.js → xml-BeNvlifb.js} +5 -5
- package/{xml-DDTIMpY3.js.map → xml-BeNvlifb.js.map} +1 -1
- package/{xml-VhJ4NrMB.mjs → xml-vyCZZiF_.mjs} +5 -5
- package/{xml-VhJ4NrMB.mjs.map → xml-vyCZZiF_.mjs.map} +1 -1
- package/{yaml-Cx16IYJq.js → yaml-DhLLa2cj.js} +5 -5
- package/{yaml-Cx16IYJq.js.map → yaml-DhLLa2cj.js.map} +1 -1
- package/{yaml-Du4wFGzL.mjs → yaml-Dtz0Bd7J.mjs} +5 -5
- package/{yaml-Du4wFGzL.mjs.map → yaml-Dtz0Bd7J.mjs.map} +1 -1
- package/artifact_methods-BMCahBLn.mjs +0 -1
- package/artifact_methods-f_bSy4kX.js +0 -1
- package/isolation-7xbkc12r.mjs.map +0 -1
- package/isolation-BQCTQYZ_.js.map +0 -1
- package/spooled_artifact-BkueYJhR.mjs.map +0 -1
- package/spooled_artifact-DriNQRj7.js.map +0 -1
- package/tokenizable-CWW7iSrD.js.map +0 -1
- package/tokenizable-DLMP68ZV.mjs.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.cjs","names":[],"sources":["../../../src/batteries/sandbox/node/srt_enforcer.ts","../../../src/batteries/sandbox/node/search_ripgrep.ts"],"sourcesContent":["import { statSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { spawn } from 'node:child_process'\nimport { quoteShellArgs, validateAbsoluteBinaryPath, validateBinShell } from '../escape'\nimport { derivedRulesFromSrt, primeGlobMatcher, reproduceMandatoryDeny } from './fs_node'\nimport {\n E_INVALID_SANDBOX_CONFIG,\n E_SANDBOX_UNSUPPORTED_ENV,\n E_SANDBOX_POLICY_CONFLICT,\n} from '../exceptions'\nimport type { SandboxPolicy, DerivedRules } from '../types'\nimport type { SandboxPolicyEnforcer } from '../contracts/policy_enforcer'\n\ntype SrtManager = {\n initialize(config: SrtConfig): Promise<void>\n isSupportedPlatform(): boolean\n isSandboxingEnabled(): boolean\n checkDependenciesAsync(): Promise<{ errors: string[]; warnings: string[] }>\n wrapWithSandboxArgv(\n command: string,\n shell?: string,\n custom?: unknown,\n signal?: AbortSignal,\n cwd?: string,\n options?: { commandId?: string }\n ): Promise<{ argv: string[]; env: Record<string, string> }>\n getFsReadConfig(): { denyOnly: string[]; allowWithinDeny?: string[] }\n getFsWriteConfig(): { allowOnly: string[]; denyWithinAllow: string[] }\n getNetworkRestrictionConfig(): {\n allowedHosts?: string[]\n deniedHosts?: string[]\n }\n getConfig(): SrtConfig | undefined\n getSandboxViolationStore(): {\n getViolationsForCommand(command: string): unknown[]\n }\n reset(): Promise<void>\n}\ntype SrtConfig = {\n filesystem: Record<string, unknown>\n network: Record<string, unknown>\n /** SRT's Linux mandatory-deny scan depth is session-level. */\n mandatoryDenySearchDepth?: number\n git?: { safeDirectories: string[] }\n /** Top-level and session-level: SRT reads this from the config `initialize()` stored, not per call. */\n enableWeakerNestedSandbox?: boolean\n bwrapPath?: string\n socatPath?: string\n}\n\nconst unsupported = (): never => {\n throw new E_SANDBOX_UNSUPPORTED_ENV([\n `SRT sandbox is unavailable on ${process.platform}; use WSL2 or the SES browser path`,\n ])\n}\nconst nonEmpty = (v: readonly string[] | undefined): string[] => [...(v ?? [])]\n\n/**\n * Host variables a sandboxed child inherits when the caller names no allow-list.\n *\n * @remarks\n * `PATH` ONLY, and it is the minimum rather than a convenience. The ripgrep searcher spawns `rg` by\n * BARE NAME, so with no `PATH` the child sees only the one a shell synthesises\n * (`/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:.`) — which does not contain `/opt/homebrew/bin` or a Nix\n * profile, so `search_files` would fail as `io-failure` on any such host and present as *\"the search\n * tool is broken\"* rather than as a configuration error.\n *\n * `PATH` is not a credential: a model that runs `env` learns where binaries live, not a secret. Every\n * genuinely sensitive variable — and `HOME`, `USER`, `TMPDIR`, which leak host layout — stays out.\n */\nconst DEFAULT_ENV_ALLOW_LIST: readonly string[] = ['PATH']\n\n/** POSIX environment-variable names. Anything else could not be exported by a shell anyway. */\nconst ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/\n\n/**\n * Resolve the host half of a sandboxed child's environment ONCE, at construction.\n *\n * @remarks\n * DENY BY DEFAULT. Before this existed the enforcer spread the entire `process.env` into every child,\n * so a model directing the shell's argv could run `env` and read the host's credentials straight back\n * into its own context — which no filesystem or network policy prevents, because the secret arrives in\n * the tool result rather than over the wire.\n *\n * The allow-list REPLACES this default rather than extending it; that is stated on the option, because\n * the opposite assumption is the natural one and would silently drop `PATH`.\n *\n * @param options - The enforcer's construction options.\n * @returns The host variables to seed the child's environment with.\n */\nconst resolveHostEnv = (options: SrtEnforcerOptions): Record<string, string> => {\n if (options.inheritHostEnv === true) {\n const all: Record<string, string> = {}\n for (const [name, value] of Object.entries(process.env))\n if (value !== undefined) all[name] = value\n return all\n }\n const names = options.envAllowList ?? DEFAULT_ENV_ALLOW_LIST\n const picked: Record<string, string> = {}\n for (const name of names) {\n if (!ENV_NAME.test(name))\n throw new E_INVALID_SANDBOX_CONFIG([\n `envAllowList entry ${JSON.stringify(name)} is not a valid environment variable name`,\n ])\n const value = process.env[name]\n if (value !== undefined) picked[name] = value\n }\n return picked\n}\n\n/**\n * Whether `<cwd>/.git` is a real DIRECTORY.\n *\n * @remarks\n * Load-bearing on Linux only: upstream pushes the workspace-root `.git/hooks` and `.git/config` deny\n * entries just when `.git` is a directory, because in a worktree it is a FILE and denying it would\n * make bubblewrap fail. Treating a worktree as a directory would deny what the profile permits.\n */\nconst dotGitIsDirectory = (cwd: string): boolean => {\n try {\n return statSync(resolve(cwd, '.git')).isDirectory()\n } catch {\n return false\n }\n}\n\n/**\n * The sole ADK-to-SRT translation point. Keep the upstream type behind this local firewall.\n *\n * @remarks\n * `runtime` carries ADAPTER-level settings that are deliberately absent from `SandboxPolicy`. That\n * type is ADK-owned, SRT-neutral vocabulary — naming an SRT feature in it would make the firewall\n * nominal and leave a non-SRT enforcer unable to implement the contract — so an SRT-specific switch\n * belongs on the adapter's own options, exactly as `binShell` already does.\n */\nconst mapPolicy = (\n policy: SandboxPolicy,\n runtime: {\n enableWeakerNestedSandbox?: boolean\n bwrapPath?: string\n socatPath?: string\n } = {}\n): SrtConfig => {\n const fs = policy.filesystem\n const net = policy.network\n if (\n net.disabled &&\n (net.deniedDomains?.length ||\n Object.keys(net.deniedDomainReasons ?? {}).length ||\n (net.allowedDomains && !(net.allowedDomains.length === 1 && net.allowedDomains[0] === '*')))\n )\n throw new E_INVALID_SANDBOX_CONFIG(['network.disabled contradicts network domain restrictions'])\n const allowedDomains = net.disabled ? ['*'] : nonEmpty(net.allowedDomains)\n const deniedDomains = net.disabled ? [] : nonEmpty(net.deniedDomains)\n const deniedDomainReasons = net.disabled ? {} : { ...(net.deniedDomainReasons ?? {}) }\n return {\n ...(fs.mandatoryDenySearchDepth !== undefined\n ? { mandatoryDenySearchDepth: fs.mandatoryDenySearchDepth }\n : {}),\n // Emitted only when enabled, so an untouched configuration is byte-identical to before this\n // option existed — a drift snapshot taken either side of the upgrade compares equal.\n ...(runtime.enableWeakerNestedSandbox === true ? { enableWeakerNestedSandbox: true } : {}),\n ...(runtime.bwrapPath !== undefined ? { bwrapPath: runtime.bwrapPath } : {}),\n ...(runtime.socatPath !== undefined ? { socatPath: runtime.socatPath } : {}),\n filesystem: {\n ...(fs.disabled ? { disabled: true } : {}),\n denyRead: nonEmpty(fs.denyRead),\n allowRead: nonEmpty(fs.allowRead),\n allowWrite: nonEmpty(fs.allowWrite),\n denyWrite: nonEmpty(fs.denyWrite),\n allowGitConfig: fs.allowGitConfig ?? false,\n },\n network: {\n allowedDomains,\n deniedDomains,\n deniedDomainReasons,\n strictAllowlist: true,\n allowLocalBinding: false,\n allowUnixSockets: [],\n allowMachLookup: [],\n },\n git: {\n safeDirectories: nonEmpty(fs.gitSafeDirectories ?? [process.cwd()]),\n },\n }\n}\n\nconst toWeb = (stream: NodeJS.ReadableStream): ReadableStream<Uint8Array> =>\n (\n ReadableStream as unknown as {\n from?: (x: unknown) => ReadableStream<Uint8Array>\n }\n ).from\n ? (\n ReadableStream as unknown as {\n from: (x: unknown) => ReadableStream<Uint8Array>\n }\n ).from(stream)\n : new ReadableStream({\n start(controller) {\n stream.on('data', (x: Buffer) => controller.enqueue(new Uint8Array(x)))\n stream.on('end', () => controller.close())\n stream.on('error', (e) => controller.error(e))\n },\n })\n\n/** Options for {@link srtEnforcer}. */\n/**\n * Whether an enforcer in THIS process performed the `initialize()` that is currently in force.\n *\n * @remarks\n * UPSTREAM'S OWN FLAG CANNOT ANSWER THIS. `isSandboxingEnabled()` is literally `config !== undefined`\n * (`sandbox-manager.js:672-675`) and `reset()` clears `managerContext`, the proxies, the registries —\n * **but never `config`**. So after any reset the flag stays `true` forever, and an enforcer that\n * trusted it would \"adopt\" a sandbox that is no longer in force, silently reporting a stale policy\n * and refusing to re-initialise. Verified: initialize, reset, and `isSandboxingEnabled()` is still\n * `true`.\n *\n * We therefore track what WE established. `true` here means this module owns the live session, so a\n * subsequent construction must re-initialise rather than adopt. `false` with upstream reporting\n * enabled means somebody else got there first — the genuine adoption case.\n */\nlet selfInitialized = false\n\n/**\n * Whether the session THIS module established is still claimed by a live enforcer.\n *\n * @remarks\n * FIRST-WRITER-WINS IS ENFORCED HERE, not only in the manager. `manager.ts` admits or refuses a\n * second handle by comparing policies — but that check runs AFTER `srtEnforcer()` has already\n * constructed, and construction is what touches the process-global. So a reset-and-reinitialise in the\n * constructor would tear down a live policy before anything could refuse it: caller 2 widens the real\n * sandbox, the manager then throws, and caller 1 carries on against a baseline that is no longer in\n * force. The refusal would arrive after the damage.\n *\n * `claimed` is therefore set when we initialise and cleared only by `dispose()`. While it is `true`,\n * re-initialising is refused outright; once cleared, the next construction may re-establish a session,\n * which is what makes sequential owned handles work.\n */\nlet selfSessionClaimed = false\n\n/**\n * Relinquish this module's ownership marker.\n *\n * @remarks\n * TEST SEAM, and a narrow one. The marker exists because upstream's `isSandboxingEnabled()` is\n * `config !== undefined` and `reset()` never clears `config` — so after the first `initialize()` the\n * flag is permanently `true` and cannot by itself distinguish *\"a session we established\"* from\n * *\"somebody else's\"*. Ownership therefore persists for the life of the module, which is correct in\n * production (a process that initialised once keeps re-initialising its own session) but makes the\n * ADOPTION branch unreachable in a test file that has already constructed an owned enforcer.\n *\n * Production code has no reason to call this: relinquishing ownership while a session we established\n * is still live would make the next construction adopt it and refuse to re-initialise.\n */\nexport const releaseSrtOwnershipForTests = (): void => {\n selfInitialized = false\n selfSessionClaimed = false\n}\n\n/** Construction options for {@link srtEnforcer}. */\nexport type SrtEnforcerOptions = {\n /**\n * Absolute path to the POSIX shell used to invoke wrapped commands. Defaults to `/bin/bash`.\n *\n * @remarks\n * Validated at construction in TWO checks, both required: it must be ABSOLUTE (a bare `bash` passes\n * any basename test while remaining `PATH`-dependent — the exact hazard the absolute default\n * avoids), and its basename must be on the allow-list (`sh`, `bash`, `dash`, `zsh`, `ksh`) — the\n * shells whose quoting the single escaper is correct for. An allow-list rather than a deny-list is\n * deliberate: `fish` and `nu` are POSIX-ish enough to look safe and different enough to break\n * single-quote escaping, so an unverified shell must fail closed.\n */\n binShell?: string\n /**\n * The ADK-owned policy to enforce.\n *\n * @remarks\n * Mapped to SRT's config inside this module and nowhere else. The derived baseline is captured\n * immediately after `initialize()`, because `SandboxManager` is a process-global singleton whose\n * SECOND `initialize()` is a no-op — a later call with a different policy silently keeps the first.\n */\n policy: SandboxPolicy\n /**\n * Host environment variable NAMES a sandboxed child may inherit. Defaults to `['PATH']`.\n *\n * @remarks\n * The child inherits NOTHING from the host beyond these names. That default is deliberate: a model\n * that can direct the shell's argv can run `env`, so anything inherited is readable back into its\n * context — and no filesystem or network policy stops it, because the value arrives in the tool\n * result rather than over the wire.\n *\n * **This REPLACES the default, it does not extend it.** A caller who needs `CARGO_HOME` and still\n * wants binaries to resolve must pass BOTH: `['PATH', 'CARGO_HOME']`. Passing `['CARGO_HOME']` alone\n * drops `PATH`, which breaks `search_files` on any host where `rg` lives outside `/usr/bin`.\n *\n * An entry that is not a valid POSIX environment-variable name throws `E_INVALID_SANDBOX_CONFIG` at\n * construction rather than being skipped, so a typo surfaces as a startup error instead of a\n * variable that silently never arrives.\n */\n envAllowList?: readonly string[]\n /**\n * Pass the ENTIRE host environment to sandboxed children. Defaults to `false`.\n *\n * @remarks\n * The escape hatch for a deployment that genuinely needs ambient configuration, and it is worth\n * being blunt about what it re-opens: **every secret in the host process becomes readable by the\n * model**, because `run_shell_command` exists precisely to run commands the model chose and `env` is\n * one of them. Prefer naming what you need in `envAllowList`.\n */\n inheritHostEnv?: boolean\n /**\n * Absolute path to a Linux bubblewrap wrapper or binary. SRT reads it from the config passed to\n * `initialize()`; on the Linux launch path it becomes argv[0] of the bwrap command\n * (`linux-sandbox-utils.js:1539`). A wrapper script placed at this path therefore receives every\n * bwrap argument and can strip or rewrite options such as `--unshare-net`. On Linux a nonexistent\n * path fails in SRT's `initialize()`; on macOS it is neither validated nor used.\n */\n bwrapPath?: string\n /**\n * Absolute path to a Linux socat binary or wrapper. SRT passes it to `initializeLinuxNetworkBridge`\n * on Linux. A nonexistent path fails in SRT's `initialize()`; on macOS it is neither validated nor\n * used.\n */\n socatPath?: string\n /**\n * Enable SRT's weaker nested-sandbox mode, for running inside an unprivileged container.\n *\n * @remarks\n * Bubblewrap cannot mount a fresh `/proc` inside an unprivileged container, so the sandbox fails to\n * start at all — the symptom is `apply-seccomp: write /proc/self/uid_map: Operation not permitted`\n * with exit 1, an empty stdout and NO diagnostics, which is indistinguishable from a policy denial.\n * This flag makes the inner sandbox bind-mount the container's EXISTING `/proc` instead.\n *\n * **It considerably weakens the boundary**, in upstream's own words: the bind-mounted `/proc`\n * exposes process information a fresh mount would hide. Only enable it when the OUTER container\n * already provides the isolation you need — it trades inner isolation for the sandbox running at all.\n *\n * Session-level: SRT reads it from the config given to `initialize()`, never per call.\n */\n enableWeakerNestedSandbox?: boolean\n}\n\n/**\n * Construct the SRT-backed policy enforcer — the OS boundary.\n *\n * @remarks\n * REFUSES rather than degrades on an unsupported platform: native `win32` throws\n * `E_SANDBOX_UNSUPPORTED_ENV` naming WSL2, because Windows folds all four filesystem-policy lists\n * case-insensitively and throws on per-exec allows. A half-built branch there would be a DIVERGENT\n * boundary rather than a limited one, so there is deliberately no native-Windows evaluator.\n *\n * `run()` resolves on SPAWN with live streams plus a separate `completed` promise, never a settled\n * exit code. That split is not stylistic: one promise cannot both hand over unread child streams AND\n * carry an exit code, because settling requires the streams to have ended and they cannot end before\n * someone drains them. A caller MUST drain both concurrently — pipe buffers are per-fd, so draining\n * one to completion first can block the other and hang the child.\n *\n * @param options - Shell and policy configuration.\n * @returns An enforcer whose derived baseline is already captured.\n */\nexport const srtEnforcer = async (options: SrtEnforcerOptions): Promise<SandboxPolicyEnforcer> => {\n if (process.platform !== 'darwin' && process.platform !== 'linux') return unsupported()\n const binShell = validateBinShell(options.binShell)\n const bwrapPath = validateAbsoluteBinaryPath(options.bwrapPath, 'bwrapPath')\n const socatPath = validateAbsoluteBinaryPath(options.socatPath, 'socatPath')\n // Resolved ONCE: the allow-list is fixed for the enforcer's life, so re-picking per spawn would only\n // add a chance for the two to disagree. Validation throws here, at construction, so a typo'd variable\n // name is a startup error rather than a variable that silently never arrives.\n const hostEnv = resolveHostEnv(options)\n const srt = (await import('@anthropic-ai/sandbox-runtime')) as unknown as {\n SandboxManager: SrtManager\n }\n const manager = srt.SandboxManager\n // SandboxManager is process-global and first-writer-wins. Detect before initialize: an already\n // enabled manager belongs to somebody else, and initializing it (even with a different policy)\n // would leave this handle describing a sandbox that is not in force.\n // ADOPT only when the live session is somebody ELSE'S. See `selfInitialized`: upstream's flag is\n // sticky across `reset()`, so it alone would make every enforcer after the first one adopt.\n // CLAIM FIRST, CLASSIFY SECOND — and both synchronously, because the boundary is a process-global\n // with no lock to take. An earlier revision computed `adopted` from `isSandboxingEnabled()` and only\n // claimed after `initialize()` resolved, which left TWO windows for concurrent constructions:\n // · both read `claimed === false`, both proceeded, and the second re-initialised over the first;\n // · worse, the second saw `enabled === true` (the first had just initialised) with our marker not\n // yet set, so it classified a session WE established as FOREIGN and adopted it — reporting the\n // other caller's policy as though it were an adopted third-party sandbox.\n // Verified both: `Promise.all` of two enforcers resolved BOTH, the second with `adopted: true` and\n // the first's derived lists. Claiming before any await closes them together.\n const claimedByUs = selfSessionClaimed\n const alreadyLive = manager.isSandboxingEnabled()\n const adopted = alreadyLive && !selfInitialized && !claimedByUs\n if (!adopted) {\n // RESET BEFORE RE-INITIALISING OUR OWN SESSION. Upstream's `initialize()` assigns `config`\n // unconditionally, but the DERIVED getters are computed from the managerContext built during\n // initialisation — so calling it twice without a reset leaves the FIRST policy's derived lists in\n // force. Verified: init A, then init B, and `getFsReadConfig()` still reports A; reset then init C\n // reports C. Without this, a second `srtEnforcer()` in one process silently enforces the previous\n // policy while reporting the new one.\n //\n // Only ever our OWN session: `adopted` is false here, and `selfInitialized` proves we established\n // what is currently live rather than inheriting somebody else's.\n //\n // AND ONLY A RELEASED ONE. Resetting a session a live handle still holds would silently replace\n // that handle's policy with this one — first-writer-wins broken in the constructor, before the\n // manager's admission check can refuse anything. Refuse here instead, where nothing has changed\n // yet: the caller must dispose the existing handle before establishing a different policy.\n if (selfSessionClaimed) {\n throw new E_SANDBOX_POLICY_CONFLICT([\n 'a sandbox session established by this process is still live; dispose that handle before ' +\n 'constructing another enforcer, or reuse it — replacing it here would widen the live ' +\n 'policy before admission could refuse it',\n ])\n }\n // CLAIM SYNCHRONOUSLY, BEFORE THE FIRST AWAIT. Setting this after `initialize()` leaves a window\n // in which two concurrent constructions both read `false`, both proceed, and the second silently\n // re-initialises over the first — verified: `Promise.all` of two enforcers with different policies\n // resolved BOTH, and the second reported the first's derived lists. The check and the claim must be\n // one synchronous step, because there is no lock to take: the boundary is a process-global.\n selfSessionClaimed = true\n try {\n if (selfInitialized && manager.isSandboxingEnabled()) await manager.reset()\n // THE nested-sandbox flag MUST ride this call and only this one. SRT resolves it from the\n // module-level config `initialize()` stored (`getEnableWeakerNestedSandbox()` reads\n // `config?.enableWeakerNestedSandbox`), NOT from the per-call config handed to\n // `wrapWithSandboxArgv` — so passing it only at the `run()` site below would compile, ship, and\n // silently do nothing.\n await manager.initialize(\n mapPolicy(options.policy, {\n enableWeakerNestedSandbox: options.enableWeakerNestedSandbox,\n bwrapPath,\n socatPath,\n })\n )\n selfInitialized = true\n } catch (error) {\n // A failed establishment must not leave the claim held, or the process is permanently wedged\n // with no live session to dispose.\n selfSessionClaimed = false\n throw error\n }\n }\n\n const platform = process.platform === 'linux' ? 'linux' : 'darwin'\n const cwd = process.cwd()\n const derive = (): DerivedRules => {\n const config = manager.getConfig()\n const filesystem = config?.filesystem ?? {}\n const network = config?.network ?? {}\n const allowGitConfig = adopted\n ? Boolean(filesystem.allowGitConfig)\n : (options.policy.filesystem.allowGitConfig ?? false)\n const searchDepth = adopted\n ? (config?.mandatoryDenySearchDepth ?? 3)\n : (options.policy.filesystem.mandatoryDenySearchDepth ?? 3)\n const dotGit = dotGitIsDirectory(cwd)\n return derivedRulesFromSrt({\n platform,\n read: manager.getFsReadConfig(),\n write: manager.getFsWriteConfig(),\n filesystemDisabled: adopted\n ? Boolean(filesystem.disabled)\n : options.policy.filesystem.disabled,\n // In adoption mode these are literal foreign lists. In particular, ['*'] is not evidence\n // that the foreign consumer used ADK's disabled mode.\n network: {\n // ADOPTION ⇒ always `false`: we did not construct this session, so \"we were constructed in\n // disabled mode\" cannot be true of it. OWNED ⇒ the ADK policy's own value, which is what makes\n // the drift SKIP branch and the `false → true` widening check implementable.\n disabled: adopted ? false : (options.policy.network.disabled ?? false),\n allowedDomains: adopted\n ? Array.isArray(network.allowedDomains)\n ? network.allowedDomains\n : []\n : options.policy.network.disabled\n ? ['*']\n : nonEmpty(options.policy.network.allowedDomains),\n deniedDomains: adopted\n ? Array.isArray(network.deniedDomains)\n ? network.deniedDomains\n : []\n : options.policy.network.disabled\n ? []\n : nonEmpty(options.policy.network.deniedDomains),\n },\n mandatoryDeny: {\n form: platform === 'linux' ? 'expanded-paths' : 'glob',\n entries: reproduceMandatoryDeny({\n cwd,\n allowGitConfig,\n platform,\n dotGitIsDirectory: dotGit,\n }),\n allowGitConfig,\n searchDepth,\n ...(platform === 'linux' ? { dotGitWasDirectory: dotGit } : {}),\n },\n })\n }\n // CAPTURE the derived baseline immediately after our initialize, or the live foreign baseline\n // when adopting. It is deliberately derived from SRT's getters, not from the requested policy.\n const derived: DerivedRules = derive()\n // PRIME THE GLOB MATCHER with upstream's own `globToRegex` for every rule in the derived lists.\n // This is the whole reason `srtEnforcer` is async at the right moment: the evaluator is synchronous\n // (it is consulted per path) while the upstream import is not, so unless priming happens HERE the\n // synchronous fallback becomes the only path that ever runs in production — making the plan's\n // \"deep-import the matcher, never port it\" rule true on paper and false in fact.\n await primeGlobMatcher([\n ...derived.read.denyOnly,\n ...derived.read.allowWithinDeny,\n ...derived.write.allowOnly,\n ...derived.write.denyWithinAllow,\n ...derived.mandatoryDeny.entries,\n ])\n const enforcer: SandboxPolicyEnforcer = {\n isSupported: () => manager.isSupportedPlatform(),\n adopted,\n checkDependencies: async () => manager.checkDependenciesAsync(),\n run: async (op) => {\n const mapped = mapPolicy(op.policy)\n const command = await quoteShellArgs(op.argv)\n const wrapped = await manager.wrapWithSandboxArgv(\n command,\n binShell,\n mapped,\n op.signal,\n op.cwd,\n { commandId: op.correlationId }\n )\n const child = spawn(wrapped.argv[0], wrapped.argv.slice(1), {\n cwd: op.cwd,\n // SRT's `wrapped.env` is the unchanged process environment, not sandbox plumbing. Merging it\n // here would defeat the allow-list by reintroducing every host variable. SRT's proxy, CA\n // bundle, and git safe-directory plumbing are encoded in `wrapped.argv` instead. The caller's\n // per-call environment remains the final overlay, by contract.\n env: { ...hostEnv, ...(op.env ?? {}) },\n stdio: ['ignore', 'pipe', 'pipe'],\n shell: false,\n })\n const completed = new Promise<{ exitCode: number; failed: boolean }>((settle) => {\n // 'error' MUST be handled, and not only so `completed` settles: an unhandled 'error' on a\n // ChildProcess is an uncaught exception that terminates the HOST process. A missing wrapper\n // binary or an unusable `cwd` would therefore kill the agent instead of failing one command.\n // Both listeners are `once` and the promise settles first-write-wins, so the pair is safe:\n // a spawn failure emits 'error' then 'close', and the later 'close' is discarded.\n child.once('error', () => settle({ exitCode: 1, failed: true }))\n child.once('close', (code) => settle({ exitCode: code ?? 1, failed: (code ?? 1) !== 0 }))\n })\n return {\n stdout: toWeb(child.stdout!),\n stderr: toWeb(child.stderr!),\n completed,\n }\n },\n // Owned sessions are immutable from this adapter's perspective and retain the cheap cached\n // snapshot. An adopted session must re-read SRT on every call: a foreign updateConfig() can widen\n // the live sandbox between invocations, and drift() must compare against that live state.\n effectivePolicy: () => (adopted ? derive() : derived),\n diagnosticsFor: (id) =>\n manager.getSandboxViolationStore().getViolationsForCommand(id).map(String),\n dispose: async () => {\n // Adoption is reported as a no-op. Resetting here would tear down ACEs owned by the host\n // application; only the manager we initialized may be reset.\n // ADOPTION IS A REPORTED NO-OP: resetting here would tear down ACEs owned by the host\n // application. Only a session we established may be reset.\n //\n // `selfInitialized` is deliberately NOT cleared. `reset()` leaves upstream's `config` set, so\n // `isSandboxingEnabled()` stays `true` forever after the first initialise — and clearing our\n // marker would make the NEXT construction see \"enabled, not ours\" and adopt a dead session,\n // silently enforcing the disposed policy. Ownership is a property of this process having\n // initialised at all, not of a session still being live.\n if (!adopted && manager.isSandboxingEnabled()) {\n await manager.reset()\n // Release the claim so a later construction may establish a new session. `selfInitialized`\n // deliberately stays true: upstream's `config` survives `reset()`, so clearing it would make\n // the next construction see \"enabled, not ours\" and adopt a dead session.\n selfSessionClaimed = false\n }\n },\n }\n return enforcer\n}\n\nexport { mapPolicy }\n","import { assertArgvValue, assertAllowedRipgrepFlag } from '../escape'\nimport type { HitFrame, PathFrame, SandboxPolicy } from '../types'\nimport type { SandboxPolicyEnforcer } from '../contracts/policy_enforcer'\n\n/**\n * A classified ripgrep outcome.\n *\n * @remarks\n * CLASSIFICATION ORDER IS LOAD-BEARING: diagnostics are checked BEFORE the exit status, because `rg`\n * runs under the sandbox and a denied path surfaces as a non-zero exit *plus* a violation record.\n * Triage generically first and a policy refusal is permanently mislabelled `io-failure` — the model\n * is told \"search broke\" when the truth is \"you may not read there\", and `denied-by-policy` becomes\n * unreachable despite being in the tool's outcome list.\n *\n * `no-matches` is a RESULT, not an error: `rg` exits 1 when it ran correctly and matched nothing.\n */\nexport type RipgrepFailure =\n | { kind: 'denied-by-policy'; diagnostics: readonly string[] }\n | { kind: 'invalid-pattern'; message: string }\n | { kind: 'no-matches' }\n | { kind: 'io-failure'; message: string; exitCode: number }\n\n/**\n * Convert a classified ripgrep failure into the error the searcher throws.\n *\n * @remarks\n * Exhaustive by construction: a new {@link RipgrepFailure} arm is a compile error here rather than\n * a silently generic message. `denied-by-policy` carries its diagnostics into the message — the\n * classification is checked BEFORE the generic exit-code triage precisely so a policy refusal is\n * not mislabelled `io-failure`, and flattening it back to a bare exit code here would discard the\n * only thing that makes that ordering worth having.\n *\n * @param failure - A classified failure other than `no-matches`, which is a result, not an error.\n * @returns The error to throw.\n */\nconst toRipgrepError = (failure: Exclude<RipgrepFailure, { kind: 'no-matches' }>): Error => {\n switch (failure.kind) {\n case 'denied-by-policy':\n return new Error(`denied-by-policy: ${failure.diagnostics.join('; ')}`)\n case 'invalid-pattern':\n return new Error(failure.message)\n case 'io-failure':\n return new Error(failure.message || `ripgrep exited ${failure.exitCode}`)\n }\n}\n\nconst bytes = new TextDecoder()\nconst collect = async (\n stream: ReadableStream<Uint8Array>,\n onText: (text: string) => void\n): Promise<void> => {\n const reader = stream.getReader()\n try {\n for (;;) {\n const next = await reader.read()\n if (next.done) return\n onText(bytes.decode(next.value, { stream: true }))\n }\n } finally {\n reader.releaseLock()\n }\n}\n\n/** Ripgrep backend. Both child pipes are drained immediately and concurrently. */\nexport const createRipgrepSearch = (\n enforcer: SandboxPolicyEnforcer,\n policy: SandboxPolicy\n): {\n searchContent(o: {\n root: string\n pattern: string\n maxDepth: number\n limit: number\n ignoreCase?: boolean\n literal?: boolean\n glob?: string\n iglob?: string\n follow?: boolean\n hidden?: boolean\n noIgnore?: boolean\n signal?: AbortSignal\n }): AsyncIterable<HitFrame>\n findPaths(o: {\n root: string\n glob: string\n maxDepth: number\n limit: number\n iglob?: string\n follow?: boolean\n hidden?: boolean\n noIgnore?: boolean\n signal?: AbortSignal\n }): AsyncIterable<PathFrame>\n} => {\n const run = async (argv: string[], cwd: string, signal?: AbortSignal) => {\n const correlationId = `${crypto.randomUUID()}-${Date.now().toString(36)}`\n const spawned = await enforcer.run({ argv, cwd, policy, correlationId, signal })\n let out = ''\n let err = ''\n const stdout = collect(spawned.stdout, (chunk) => {\n out += chunk\n })\n const stderr = collect(spawned.stderr, (chunk) => {\n err += chunk\n })\n const [, , completed] = await Promise.all([stdout, stderr, spawned.completed])\n const diagnostics = enforcer.diagnosticsFor(correlationId)\n const exitCode = completed.exitCode\n if (diagnostics.length > 0)\n return { failure: { kind: 'denied-by-policy', diagnostics } as RipgrepFailure, out, err }\n if (exitCode === 2 && err.startsWith('regex parse error'))\n return { failure: { kind: 'invalid-pattern', message: err } as RipgrepFailure, out, err }\n if (exitCode === 1) return { failure: { kind: 'no-matches' } as RipgrepFailure, out, err }\n if (exitCode !== 0)\n return { failure: { kind: 'io-failure', message: err, exitCode } as RipgrepFailure, out, err }\n return { failure: undefined, out, err }\n }\n /**\n * Defence-in-depth check that this adapter only ever emits flags it declared.\n *\n * @remarks\n * Scans the WHOLE argv rather than a fixed index window: the previous `slice(1, 5)` silently depended\n * on argv layout, so adding or reordering an argument moved the window off the flags. It also stops at\n * `--`, because everything after the terminator is a model-supplied VALUE — a pattern like `--foo` is\n * a legitimate search string there, not a flag, and `assertArgvValue` already owns that half.\n */\n const assertAdapterFlags = (argv: readonly string[]): void => {\n for (const arg of argv.slice(1)) {\n if (arg === '--') break\n if (arg.startsWith('--')) assertAllowedRipgrepFlag(arg)\n }\n }\n return {\n async *searchContent(o) {\n if (!Number.isInteger(o.limit) || o.limit < 1)\n throw new Error('limit must be a positive integer')\n if (o.follow)\n throw new Error('follow is refused: descendant symlink containment audit pending')\n const argv = [\n 'rg',\n '--json',\n ...(o.ignoreCase ? ['--ignore-case'] : []),\n ...(o.literal ? ['--fixed-strings'] : []),\n ...(o.glob ? ['--glob', assertArgvValue(o.glob)] : []),\n ...(o.iglob ? ['--iglob', assertArgvValue(o.iglob)] : []),\n ...(o.hidden ? ['--hidden'] : []),\n ...(o.noIgnore ? ['--no-ignore'] : []),\n '--max-depth',\n String(o.maxDepth),\n '--',\n assertArgvValue(o.pattern),\n assertArgvValue(o.root),\n ]\n assertAdapterFlags(argv)\n const result = await run(argv, o.root, o.signal)\n if (result.failure) {\n if (result.failure.kind === 'no-matches') {\n yield { kind: 'done', complete: true }\n return\n }\n throw toRipgrepError(result.failure)\n }\n let shown = 0\n let overLimit = false\n for (const line of result.out.split('\\n')) {\n if (!line) continue\n try {\n const item = JSON.parse(line) as {\n type: string\n data?: { path?: { text?: string }; line_number?: number; lines?: { text?: string } }\n }\n if (\n item.type === 'match' &&\n item.data?.path?.text &&\n item.data.line_number &&\n item.data.lines\n ) {\n if (shown >= o.limit) {\n overLimit = true\n break\n }\n yield {\n kind: 'item',\n path: item.data.path.text,\n line: item.data.line_number,\n text: item.data.lines.text ?? '',\n }\n shown++\n }\n } catch {\n /* malformed rg diagnostics are an I/O failure in the real adapter */\n }\n }\n yield overLimit\n ? { kind: 'done', complete: false, omitted: 'over-limit', bound: 'limit', shown }\n : { kind: 'done', complete: true }\n },\n async *findPaths(o) {\n if (!Number.isInteger(o.limit) || o.limit < 1)\n throw new Error('limit must be a positive integer')\n if (o.follow)\n throw new Error('follow is refused: descendant symlink containment audit pending')\n const argv = [\n 'rg',\n '--files',\n '--glob',\n assertArgvValue(o.glob),\n ...(o.iglob ? ['--iglob', assertArgvValue(o.iglob)] : []),\n ...(o.hidden ? ['--hidden'] : []),\n ...(o.noIgnore ? ['--no-ignore'] : []),\n '--max-depth',\n String(o.maxDepth),\n '--',\n assertArgvValue(o.root),\n ]\n assertAdapterFlags(argv)\n const result = await run(argv, o.root, o.signal)\n if (result.failure) {\n if (result.failure.kind === 'no-matches') {\n yield { kind: 'done', complete: true }\n return\n }\n throw toRipgrepError(result.failure)\n }\n let shown = 0\n let overLimit = false\n for (const path of result.out.split('\\n')) {\n if (!path) continue\n if (shown >= o.limit) {\n overLimit = true\n break\n }\n yield { kind: 'item', path }\n shown++\n }\n yield overLimit\n ? { kind: 'done', complete: false, omitted: 'over-limit', bound: 'limit', shown }\n : { kind: 'done', complete: true }\n },\n }\n}\n"],"mappings":";;;;;;;;;AAkDA,IAAM,oBAA2B;CAC/B,MAAM,IAAI,mBAAA,0BAA0B,CAClC,iCAAiC,QAAQ,SAAS,mCACpD,CAAC;AACH;AACA,IAAM,YAAY,MAA+C,CAAC,GAAI,KAAK,CAAC,CAAE;;;;;;;;;;;;;;AAe9E,IAAM,yBAA4C,CAAC,MAAM;;AAGzD,IAAM,WAAW;;;;;;;;;;;;;;;;AAiBjB,IAAM,kBAAkB,YAAwD;CAC9E,IAAI,QAAQ,mBAAmB,MAAM;EACnC,MAAM,MAA8B,CAAC;EACrC,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,QAAQ,GAAG,GACpD,IAAI,UAAU,KAAA,GAAW,IAAI,QAAQ;EACvC,OAAO;CACT;CACA,MAAM,QAAQ,QAAQ,gBAAgB;CACtC,MAAM,SAAiC,CAAC;CACxC,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,CAAC,SAAS,KAAK,IAAI,GACrB,MAAM,IAAI,mBAAA,yBAAyB,CACjC,sBAAsB,KAAK,UAAU,IAAI,EAAE,0CAC7C,CAAC;EACH,MAAM,QAAQ,QAAQ,IAAI;EAC1B,IAAI,UAAU,KAAA,GAAW,OAAO,QAAQ;CAC1C;CACA,OAAO;AACT;;;;;;;;;AAUA,IAAM,qBAAqB,QAAyB;CAClD,IAAI;EACF,QAAA,GAAA,QAAA,WAAA,GAAA,UAAA,SAAwB,KAAK,MAAM,CAAC,EAAE,YAAY;CACpD,QAAQ;EACN,OAAO;CACT;AACF;;;;;;;;;;AAWA,IAAM,aACJ,QACA,UAII,CAAC,MACS;CACd,MAAM,KAAK,OAAO;CAClB,MAAM,MAAM,OAAO;CACnB,IACE,IAAI,aACH,IAAI,eAAe,UAClB,OAAO,KAAK,IAAI,uBAAuB,CAAC,CAAC,EAAE,UAC1C,IAAI,kBAAkB,EAAE,IAAI,eAAe,WAAW,KAAK,IAAI,eAAe,OAAO,OAExF,MAAM,IAAI,mBAAA,yBAAyB,CAAC,0DAA0D,CAAC;CACjG,MAAM,iBAAiB,IAAI,WAAW,CAAC,GAAG,IAAI,SAAS,IAAI,cAAc;CACzE,MAAM,gBAAgB,IAAI,WAAW,CAAC,IAAI,SAAS,IAAI,aAAa;CACpE,MAAM,sBAAsB,IAAI,WAAW,CAAC,IAAI,EAAE,GAAI,IAAI,uBAAuB,CAAC,EAAG;CACrF,OAAO;EACL,GAAI,GAAG,6BAA6B,KAAA,IAChC,EAAE,0BAA0B,GAAG,yBAAyB,IACxD,CAAC;EAGL,GAAI,QAAQ,8BAA8B,OAAO,EAAE,2BAA2B,KAAK,IAAI,CAAC;EACxF,GAAI,QAAQ,cAAc,KAAA,IAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;EAC1E,GAAI,QAAQ,cAAc,KAAA,IAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;EAC1E,YAAY;GACV,GAAI,GAAG,WAAW,EAAE,UAAU,KAAK,IAAI,CAAC;GACxC,UAAU,SAAS,GAAG,QAAQ;GAC9B,WAAW,SAAS,GAAG,SAAS;GAChC,YAAY,SAAS,GAAG,UAAU;GAClC,WAAW,SAAS,GAAG,SAAS;GAChC,gBAAgB,GAAG,kBAAkB;EACvC;EACA,SAAS;GACP;GACA;GACA;GACA,iBAAiB;GACjB,mBAAmB;GACnB,kBAAkB,CAAC;GACnB,iBAAiB,CAAC;EACpB;EACA,KAAK,EACH,iBAAiB,SAAS,GAAG,sBAAsB,CAAC,QAAQ,IAAI,CAAC,CAAC,EACpE;CACF;AACF;AAEA,IAAM,SAAS,WAEX,eAGA,OAEI,eAGA,KAAK,MAAM,IACb,IAAI,eAAe,EACjB,MAAM,YAAY;CAChB,OAAO,GAAG,SAAS,MAAc,WAAW,QAAQ,IAAI,WAAW,CAAC,CAAC,CAAC;CACtE,OAAO,GAAG,aAAa,WAAW,MAAM,CAAC;CACzC,OAAO,GAAG,UAAU,MAAM,WAAW,MAAM,CAAC,CAAC;AAC/C,EACF,CAAC;;;;;;;;;;;;;;;;;AAkBP,IAAI,kBAAkB;;;;;;;;;;;;;;;;AAiBtB,IAAI,qBAAqB;;;;;;;;;;;;;;;;;;;AA0HzB,IAAa,cAAc,OAAO,YAAgE;CAChG,IAAI,QAAQ,aAAa,YAAY,QAAQ,aAAa,SAAS,OAAO,YAAY;CACtF,MAAM,WAAW,eAAA,iBAAiB,QAAQ,QAAQ;CAClD,MAAM,YAAY,eAAA,2BAA2B,QAAQ,WAAW,WAAW;CAC3E,MAAM,YAAY,eAAA,2BAA2B,QAAQ,WAAW,WAAW;CAI3E,MAAM,UAAU,eAAe,OAAO;CAItC,MAAM,WAAU,MAHG,OAAO,kCAGN;CAepB,MAAM,cAAc;CAEpB,MAAM,UADc,QAAQ,oBACZ,KAAe,CAAC,mBAAmB,CAAC;CACpD,IAAI,CAAC,SAAS;EAeZ,IAAI,oBACF,MAAM,IAAI,mBAAA,0BAA0B,CAClC,qNAGF,CAAC;EAOH,qBAAqB;EACrB,IAAI;GACF,IAAI,mBAAmB,QAAQ,oBAAoB,GAAG,MAAM,QAAQ,MAAM;GAM1E,MAAM,QAAQ,WACZ,UAAU,QAAQ,QAAQ;IACxB,2BAA2B,QAAQ;IACnC;IACA;GACF,CAAC,CACH;GACA,kBAAkB;EACpB,SAAS,OAAO;GAGd,qBAAqB;GACrB,MAAM;EACR;CACF;CAEA,MAAM,WAAW,QAAQ,aAAa,UAAU,UAAU;CAC1D,MAAM,MAAM,QAAQ,IAAI;CACxB,MAAM,eAA6B;EACjC,MAAM,SAAS,QAAQ,UAAU;EACjC,MAAM,aAAa,QAAQ,cAAc,CAAC;EAC1C,MAAM,UAAU,QAAQ,WAAW,CAAC;EACpC,MAAM,iBAAiB,UACnB,QAAQ,WAAW,cAAc,IAChC,QAAQ,OAAO,WAAW,kBAAkB;EACjD,MAAM,cAAc,UACf,QAAQ,4BAA4B,IACpC,QAAQ,OAAO,WAAW,4BAA4B;EAC3D,MAAM,SAAS,kBAAkB,GAAG;EACpC,OAAO,gBAAA,oBAAoB;GACzB;GACA,MAAM,QAAQ,gBAAgB;GAC9B,OAAO,QAAQ,iBAAiB;GAChC,oBAAoB,UAChB,QAAQ,WAAW,QAAQ,IAC3B,QAAQ,OAAO,WAAW;GAG9B,SAAS;IAIP,UAAU,UAAU,QAAS,QAAQ,OAAO,QAAQ,YAAY;IAChE,gBAAgB,UACZ,MAAM,QAAQ,QAAQ,cAAc,IAClC,QAAQ,iBACR,CAAC,IACH,QAAQ,OAAO,QAAQ,WACrB,CAAC,GAAG,IACJ,SAAS,QAAQ,OAAO,QAAQ,cAAc;IACpD,eAAe,UACX,MAAM,QAAQ,QAAQ,aAAa,IACjC,QAAQ,gBACR,CAAC,IACH,QAAQ,OAAO,QAAQ,WACrB,CAAC,IACD,SAAS,QAAQ,OAAO,QAAQ,aAAa;GACrD;GACA,eAAe;IACb,MAAM,aAAa,UAAU,mBAAmB;IAChD,SAAS,gBAAA,uBAAuB;KAC9B;KACA;KACA;KACA,mBAAmB;IACrB,CAAC;IACD;IACA;IACA,GAAI,aAAa,UAAU,EAAE,oBAAoB,OAAO,IAAI,CAAC;GAC/D;EACF,CAAC;CACH;CAGA,MAAM,UAAwB,OAAO;CAMrC,MAAM,gBAAA,iBAAiB;EACrB,GAAG,QAAQ,KAAK;EAChB,GAAG,QAAQ,KAAK;EAChB,GAAG,QAAQ,MAAM;EACjB,GAAG,QAAQ,MAAM;EACjB,GAAG,QAAQ,cAAc;CAC3B,CAAC;CAmED,OAAO;EAjEL,mBAAmB,QAAQ,oBAAoB;EAC/C;EACA,mBAAmB,YAAY,QAAQ,uBAAuB;EAC9D,KAAK,OAAO,OAAO;GACjB,MAAM,SAAS,UAAU,GAAG,MAAM;GAClC,MAAM,UAAU,MAAM,eAAA,eAAe,GAAG,IAAI;GAC5C,MAAM,UAAU,MAAM,QAAQ,oBAC5B,SACA,UACA,QACA,GAAG,QACH,GAAG,KACH,EAAE,WAAW,GAAG,cAAc,CAChC;GACA,MAAM,SAAA,GAAA,mBAAA,OAAc,QAAQ,KAAK,IAAI,QAAQ,KAAK,MAAM,CAAC,GAAG;IAC1D,KAAK,GAAG;IAKR,KAAK;KAAE,GAAG;KAAS,GAAI,GAAG,OAAO,CAAC;IAAG;IACrC,OAAO;KAAC;KAAU;KAAQ;IAAM;IAChC,OAAO;GACT,CAAC;GACD,MAAM,YAAY,IAAI,SAAgD,WAAW;IAM/E,MAAM,KAAK,eAAe,OAAO;KAAE,UAAU;KAAG,QAAQ;IAAK,CAAC,CAAC;IAC/D,MAAM,KAAK,UAAU,SAAS,OAAO;KAAE,UAAU,QAAQ;KAAG,SAAS,QAAQ,OAAO;IAAE,CAAC,CAAC;GAC1F,CAAC;GACD,OAAO;IACL,QAAQ,MAAM,MAAM,MAAO;IAC3B,QAAQ,MAAM,MAAM,MAAO;IAC3B;GACF;EACF;EAIA,uBAAwB,UAAU,OAAO,IAAI;EAC7C,iBAAiB,OACf,QAAQ,yBAAyB,EAAE,wBAAwB,EAAE,EAAE,IAAI,MAAM;EAC3E,SAAS,YAAY;GAWnB,IAAI,CAAC,WAAW,QAAQ,oBAAoB,GAAG;IAC7C,MAAM,QAAQ,MAAM;IAIpB,qBAAqB;GACvB;EACF;CAEK;AACT;;;;;;;;;;;;;;;;ACjiBA,IAAM,kBAAkB,YAAoE;CAC1F,QAAQ,QAAQ,MAAhB;EACE,KAAK,oBACH,uBAAO,IAAI,MAAM,qBAAqB,QAAQ,YAAY,KAAK,IAAI,GAAG;EACxE,KAAK,mBACH,OAAO,IAAI,MAAM,QAAQ,OAAO;EAClC,KAAK,cACH,OAAO,IAAI,MAAM,QAAQ,WAAW,kBAAkB,QAAQ,UAAU;CAC5E;AACF;AAEA,IAAM,QAAQ,IAAI,YAAY;AAC9B,IAAM,UAAU,OACd,QACA,WACkB;CAClB,MAAM,SAAS,OAAO,UAAU;CAChC,IAAI;EACF,SAAS;GACP,MAAM,OAAO,MAAM,OAAO,KAAK;GAC/B,IAAI,KAAK,MAAM;GACf,OAAO,MAAM,OAAO,KAAK,OAAO,EAAE,QAAQ,KAAK,CAAC,CAAC;EACnD;CACF,UAAU;EACR,OAAO,YAAY;CACrB;AACF;;AAGA,IAAa,uBACX,UACA,WA2BG;CACH,MAAM,MAAM,OAAO,MAAgB,KAAa,WAAyB;EACvE,MAAM,gBAAgB,GAAG,OAAO,WAAW,EAAE,GAAG,KAAK,IAAI,EAAE,SAAS,EAAE;EACtE,MAAM,UAAU,MAAM,SAAS,IAAI;GAAE;GAAM;GAAK;GAAQ;GAAe;EAAO,CAAC;EAC/E,IAAI,MAAM;EACV,IAAI,MAAM;EACV,MAAM,SAAS,QAAQ,QAAQ,SAAS,UAAU;GAChD,OAAO;EACT,CAAC;EACD,MAAM,SAAS,QAAQ,QAAQ,SAAS,UAAU;GAChD,OAAO;EACT,CAAC;EACD,MAAM,KAAK,aAAa,MAAM,QAAQ,IAAI;GAAC;GAAQ;GAAQ,QAAQ;EAAS,CAAC;EAC7E,MAAM,cAAc,SAAS,eAAe,aAAa;EACzD,MAAM,WAAW,UAAU;EAC3B,IAAI,YAAY,SAAS,GACvB,OAAO;GAAE,SAAS;IAAE,MAAM;IAAoB;GAAY;GAAqB;GAAK;EAAI;EAC1F,IAAI,aAAa,KAAK,IAAI,WAAW,mBAAmB,GACtD,OAAO;GAAE,SAAS;IAAE,MAAM;IAAmB,SAAS;GAAI;GAAqB;GAAK;EAAI;EAC1F,IAAI,aAAa,GAAG,OAAO;GAAE,SAAS,EAAE,MAAM,aAAa;GAAqB;GAAK;EAAI;EACzF,IAAI,aAAa,GACf,OAAO;GAAE,SAAS;IAAE,MAAM;IAAc,SAAS;IAAK;GAAS;GAAqB;GAAK;EAAI;EAC/F,OAAO;GAAE,SAAS,KAAA;GAAW;GAAK;EAAI;CACxC;;;;;;;;;;CAUA,MAAM,sBAAsB,SAAkC;EAC5D,KAAK,MAAM,OAAO,KAAK,MAAM,CAAC,GAAG;GAC/B,IAAI,QAAQ,MAAM;GAClB,IAAI,IAAI,WAAW,IAAI,GAAG,eAAA,yBAAyB,GAAG;EACxD;CACF;CACA,OAAO;EACL,OAAO,cAAc,GAAG;GACtB,IAAI,CAAC,OAAO,UAAU,EAAE,KAAK,KAAK,EAAE,QAAQ,GAC1C,MAAM,IAAI,MAAM,kCAAkC;GACpD,IAAI,EAAE,QACJ,MAAM,IAAI,MAAM,iEAAiE;GACnF,MAAM,OAAO;IACX;IACA;IACA,GAAI,EAAE,aAAa,CAAC,eAAe,IAAI,CAAC;IACxC,GAAI,EAAE,UAAU,CAAC,iBAAiB,IAAI,CAAC;IACvC,GAAI,EAAE,OAAO,CAAC,UAAU,eAAA,gBAAgB,EAAE,IAAI,CAAC,IAAI,CAAC;IACpD,GAAI,EAAE,QAAQ,CAAC,WAAW,eAAA,gBAAgB,EAAE,KAAK,CAAC,IAAI,CAAC;IACvD,GAAI,EAAE,SAAS,CAAC,UAAU,IAAI,CAAC;IAC/B,GAAI,EAAE,WAAW,CAAC,aAAa,IAAI,CAAC;IACpC;IACA,OAAO,EAAE,QAAQ;IACjB;IACA,eAAA,gBAAgB,EAAE,OAAO;IACzB,eAAA,gBAAgB,EAAE,IAAI;GACxB;GACA,mBAAmB,IAAI;GACvB,MAAM,SAAS,MAAM,IAAI,MAAM,EAAE,MAAM,EAAE,MAAM;GAC/C,IAAI,OAAO,SAAS;IAClB,IAAI,OAAO,QAAQ,SAAS,cAAc;KACxC,MAAM;MAAE,MAAM;MAAQ,UAAU;KAAK;KACrC;IACF;IACA,MAAM,eAAe,OAAO,OAAO;GACrC;GACA,IAAI,QAAQ;GACZ,IAAI,YAAY;GAChB,KAAK,MAAM,QAAQ,OAAO,IAAI,MAAM,IAAI,GAAG;IACzC,IAAI,CAAC,MAAM;IACX,IAAI;KACF,MAAM,OAAO,KAAK,MAAM,IAAI;KAI5B,IACE,KAAK,SAAS,WACd,KAAK,MAAM,MAAM,QACjB,KAAK,KAAK,eACV,KAAK,KAAK,OACV;MACA,IAAI,SAAS,EAAE,OAAO;OACpB,YAAY;OACZ;MACF;MACA,MAAM;OACJ,MAAM;OACN,MAAM,KAAK,KAAK,KAAK;OACrB,MAAM,KAAK,KAAK;OAChB,MAAM,KAAK,KAAK,MAAM,QAAQ;MAChC;MACA;KACF;IACF,QAAQ,CAER;GACF;GACA,MAAM,YACF;IAAE,MAAM;IAAQ,UAAU;IAAO,SAAS;IAAc,OAAO;IAAS;GAAM,IAC9E;IAAE,MAAM;IAAQ,UAAU;GAAK;EACrC;EACA,OAAO,UAAU,GAAG;GAClB,IAAI,CAAC,OAAO,UAAU,EAAE,KAAK,KAAK,EAAE,QAAQ,GAC1C,MAAM,IAAI,MAAM,kCAAkC;GACpD,IAAI,EAAE,QACJ,MAAM,IAAI,MAAM,iEAAiE;GACnF,MAAM,OAAO;IACX;IACA;IACA;IACA,eAAA,gBAAgB,EAAE,IAAI;IACtB,GAAI,EAAE,QAAQ,CAAC,WAAW,eAAA,gBAAgB,EAAE,KAAK,CAAC,IAAI,CAAC;IACvD,GAAI,EAAE,SAAS,CAAC,UAAU,IAAI,CAAC;IAC/B,GAAI,EAAE,WAAW,CAAC,aAAa,IAAI,CAAC;IACpC;IACA,OAAO,EAAE,QAAQ;IACjB;IACA,eAAA,gBAAgB,EAAE,IAAI;GACxB;GACA,mBAAmB,IAAI;GACvB,MAAM,SAAS,MAAM,IAAI,MAAM,EAAE,MAAM,EAAE,MAAM;GAC/C,IAAI,OAAO,SAAS;IAClB,IAAI,OAAO,QAAQ,SAAS,cAAc;KACxC,MAAM;MAAE,MAAM;MAAQ,UAAU;KAAK;KACrC;IACF;IACA,MAAM,eAAe,OAAO,OAAO;GACrC;GACA,IAAI,QAAQ;GACZ,IAAI,YAAY;GAChB,KAAK,MAAM,QAAQ,OAAO,IAAI,MAAM,IAAI,GAAG;IACzC,IAAI,CAAC,MAAM;IACX,IAAI,SAAS,EAAE,OAAO;KACpB,YAAY;KACZ;IACF;IACA,MAAM;KAAE,MAAM;KAAQ;IAAK;IAC3B;GACF;GACA,MAAM,YACF;IAAE,MAAM;IAAQ,UAAU;IAAO,SAAS;IAAc,OAAO;IAAS;GAAM,IAC9E;IAAE,MAAM;IAAQ,UAAU;GAAK;EACrC;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"node.cjs","names":[],"sources":["../../../src/batteries/sandbox/node/srt_enforcer.ts","../../../src/batteries/sandbox/node/search_ripgrep.ts"],"sourcesContent":["import { statSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { spawn } from 'node:child_process'\nimport { quoteShellArgs, validateAbsoluteBinaryPath, validateBinShell } from '../escape'\nimport { derivedRulesFromSrt, primeGlobMatcher, reproduceMandatoryDeny } from './fs_node'\nimport {\n E_INVALID_SANDBOX_CONFIG,\n E_SANDBOX_UNSUPPORTED_ENV,\n E_SANDBOX_POLICY_CONFLICT,\n} from '../exceptions'\nimport type { SandboxPolicy, DerivedRules } from '../types'\nimport type { SandboxPolicyEnforcer } from '../contracts/policy_enforcer'\n\ntype SrtManager = {\n initialize(config: SrtConfig): Promise<void>\n isSupportedPlatform(): boolean\n isSandboxingEnabled(): boolean\n checkDependenciesAsync(): Promise<{ errors: string[]; warnings: string[] }>\n wrapWithSandboxArgv(\n command: string,\n shell?: string,\n custom?: unknown,\n signal?: AbortSignal,\n cwd?: string,\n options?: { commandId?: string }\n ): Promise<{ argv: string[]; env: Record<string, string> }>\n getFsReadConfig(): { denyOnly: string[]; allowWithinDeny?: string[] }\n getFsWriteConfig(): { allowOnly: string[]; denyWithinAllow: string[] }\n getNetworkRestrictionConfig(): {\n allowedHosts?: string[]\n deniedHosts?: string[]\n }\n getConfig(): SrtConfig | undefined\n getSandboxViolationStore(): {\n getViolationsForCommand(command: string): unknown[]\n }\n reset(): Promise<void>\n}\ntype SrtConfig = {\n filesystem: Record<string, unknown>\n network?: Record<string, unknown>\n /** SRT's Linux mandatory-deny scan depth is session-level. */\n mandatoryDenySearchDepth?: number\n git?: { safeDirectories: string[] }\n /** Top-level and session-level: SRT reads this from the config `initialize()` stored, not per call. */\n enableWeakerNestedSandbox?: boolean\n bwrapPath?: string\n socatPath?: string\n}\n\nconst unsupported = (): never => {\n throw new E_SANDBOX_UNSUPPORTED_ENV([\n `SRT sandbox is unavailable on ${process.platform}; use WSL2 or the SES browser path`,\n ])\n}\nconst nonEmpty = (v: readonly string[] | undefined): string[] => [...(v ?? [])]\n\n/**\n * Host variables a sandboxed child inherits when the caller names no allow-list.\n *\n * @remarks\n * `PATH` ONLY, and it is the minimum rather than a convenience. The ripgrep searcher spawns `rg` by\n * BARE NAME, so with no `PATH` the child sees only the one a shell synthesises\n * (`/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:.`) — which does not contain `/opt/homebrew/bin` or a Nix\n * profile, so `search_files` would fail as `io-failure` on any such host and present as *\"the search\n * tool is broken\"* rather than as a configuration error.\n *\n * `PATH` is not a credential: a model that runs `env` learns where binaries live, not a secret. Every\n * genuinely sensitive variable — and `HOME`, `USER`, `TMPDIR`, which leak host layout — stays out.\n */\nconst DEFAULT_ENV_ALLOW_LIST: readonly string[] = ['PATH']\n\n/** POSIX environment-variable names. Anything else could not be exported by a shell anyway. */\nconst ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/\n\n/**\n * Resolve the host half of a sandboxed child's environment ONCE, at construction.\n *\n * @remarks\n * DENY BY DEFAULT. Before this existed the enforcer spread the entire `process.env` into every child,\n * so a model directing the shell's argv could run `env` and read the host's credentials straight back\n * into its own context — which no filesystem or network policy prevents, because the secret arrives in\n * the tool result rather than over the wire.\n *\n * The allow-list REPLACES this default rather than extending it; that is stated on the option, because\n * the opposite assumption is the natural one and would silently drop `PATH`.\n *\n * @param options - The enforcer's construction options.\n * @returns The host variables to seed the child's environment with.\n */\nconst resolveHostEnv = (options: SrtEnforcerOptions): Record<string, string> => {\n if (options.inheritHostEnv === true) {\n const all: Record<string, string> = {}\n for (const [name, value] of Object.entries(process.env))\n if (value !== undefined) all[name] = value\n return all\n }\n const names = options.envAllowList ?? DEFAULT_ENV_ALLOW_LIST\n const picked: Record<string, string> = {}\n for (const name of names) {\n if (!ENV_NAME.test(name))\n throw new E_INVALID_SANDBOX_CONFIG([\n `envAllowList entry ${JSON.stringify(name)} is not a valid environment variable name`,\n ])\n const value = process.env[name]\n if (value !== undefined) picked[name] = value\n }\n return picked\n}\n\n/**\n * Whether `<cwd>/.git` is a real DIRECTORY.\n *\n * @remarks\n * Load-bearing on Linux only: upstream pushes the workspace-root `.git/hooks` and `.git/config` deny\n * entries just when `.git` is a directory, because in a worktree it is a FILE and denying it would\n * make bubblewrap fail. Treating a worktree as a directory would deny what the profile permits.\n */\nconst dotGitIsDirectory = (cwd: string): boolean => {\n try {\n return statSync(resolve(cwd, '.git')).isDirectory()\n } catch {\n return false\n }\n}\n\n/**\n * The sole ADK-to-SRT translation point. Keep the upstream type behind this local firewall.\n *\n * @remarks\n * `runtime` carries ADAPTER-level settings that are deliberately absent from `SandboxPolicy`. That\n * type is ADK-owned, SRT-neutral vocabulary — naming an SRT feature in it would make the firewall\n * nominal and leave a non-SRT enforcer unable to implement the contract — so an SRT-specific switch\n * belongs on the adapter's own options, exactly as `binShell` already does.\n */\nconst mapPolicy = (\n policy: SandboxPolicy,\n runtime: {\n enableWeakerNestedSandbox?: boolean\n bwrapPath?: string\n socatPath?: string\n } = {}\n): SrtConfig => {\n const fs = policy.filesystem\n const net = policy.network\n if (\n net.disabled &&\n (net.deniedDomains?.length ||\n Object.keys(net.deniedDomainReasons ?? {}).length ||\n (net.allowedDomains && !(net.allowedDomains.length === 1 && net.allowedDomains[0] === '*')))\n )\n throw new E_INVALID_SANDBOX_CONFIG(['network.disabled contradicts network domain restrictions'])\n const allowedDomains = net.disabled ? ['*'] : nonEmpty(net.allowedDomains)\n const deniedDomains = net.disabled ? [] : nonEmpty(net.deniedDomains)\n const deniedDomainReasons = net.disabled ? {} : { ...(net.deniedDomainReasons ?? {}) }\n return {\n ...(fs.mandatoryDenySearchDepth !== undefined\n ? { mandatoryDenySearchDepth: fs.mandatoryDenySearchDepth }\n : {}),\n // Emitted only when enabled, so an untouched configuration is byte-identical to before this\n // option existed — a drift snapshot taken either side of the upgrade compares equal.\n ...(runtime.enableWeakerNestedSandbox === true ? { enableWeakerNestedSandbox: true } : {}),\n ...(runtime.bwrapPath !== undefined ? { bwrapPath: runtime.bwrapPath } : {}),\n ...(runtime.socatPath !== undefined ? { socatPath: runtime.socatPath } : {}),\n filesystem: {\n ...(fs.disabled ? { disabled: true } : {}),\n denyRead: nonEmpty(fs.denyRead),\n allowRead: nonEmpty(fs.allowRead),\n allowWrite: nonEmpty(fs.allowWrite),\n denyWrite: nonEmpty(fs.denyWrite),\n allowGitConfig: fs.allowGitConfig ?? false,\n },\n // SRT keys network restriction by presence of this object. There is no schema-valid allow-all\n // domain pattern, so disabled deliberately omits the key instead of emitting the rejected '*'.\n ...(!net.disabled\n ? {\n network: {\n allowedDomains,\n deniedDomains,\n deniedDomainReasons,\n strictAllowlist: true,\n allowLocalBinding: false,\n allowUnixSockets: [],\n allowMachLookup: [],\n },\n }\n : {}),\n git: {\n safeDirectories: nonEmpty(fs.gitSafeDirectories ?? [process.cwd()]),\n },\n }\n}\n\nconst toWeb = (stream: NodeJS.ReadableStream): ReadableStream<Uint8Array> =>\n (\n ReadableStream as unknown as {\n from?: (x: unknown) => ReadableStream<Uint8Array>\n }\n ).from\n ? (\n ReadableStream as unknown as {\n from: (x: unknown) => ReadableStream<Uint8Array>\n }\n ).from(stream)\n : new ReadableStream({\n start(controller) {\n stream.on('data', (x: Buffer) => controller.enqueue(new Uint8Array(x)))\n stream.on('end', () => controller.close())\n stream.on('error', (e) => controller.error(e))\n },\n })\n\n/** Options for {@link srtEnforcer}. */\n/**\n * Whether an enforcer in THIS process performed the `initialize()` that is currently in force.\n *\n * @remarks\n * UPSTREAM'S OWN FLAG CANNOT ANSWER THIS. `isSandboxingEnabled()` is literally `config !== undefined`\n * (`sandbox-manager.js:672-675`) and `reset()` clears `managerContext`, the proxies, the registries —\n * **but never `config`**. So after any reset the flag stays `true` forever, and an enforcer that\n * trusted it would \"adopt\" a sandbox that is no longer in force, silently reporting a stale policy\n * and refusing to re-initialise. Verified: initialize, reset, and `isSandboxingEnabled()` is still\n * `true`.\n *\n * We therefore track what WE established. `true` here means this module owns the live session, so a\n * subsequent construction must re-initialise rather than adopt. `false` with upstream reporting\n * enabled means somebody else got there first — the genuine adoption case.\n */\nlet selfInitialized = false\n\n/**\n * Whether the session THIS module established is still claimed by a live enforcer.\n *\n * @remarks\n * FIRST-WRITER-WINS IS ENFORCED HERE, not only in the manager. `manager.ts` admits or refuses a\n * second handle by comparing policies — but that check runs AFTER `srtEnforcer()` has already\n * constructed, and construction is what touches the process-global. So a reset-and-reinitialise in the\n * constructor would tear down a live policy before anything could refuse it: caller 2 widens the real\n * sandbox, the manager then throws, and caller 1 carries on against a baseline that is no longer in\n * force. The refusal would arrive after the damage.\n *\n * `claimed` is therefore set when we initialise and cleared only by `dispose()`. While it is `true`,\n * re-initialising is refused outright; once cleared, the next construction may re-establish a session,\n * which is what makes sequential owned handles work.\n */\nlet selfSessionClaimed = false\n\n/**\n * Relinquish this module's ownership marker.\n *\n * @remarks\n * TEST SEAM, and a narrow one. The marker exists because upstream's `isSandboxingEnabled()` is\n * `config !== undefined` and `reset()` never clears `config` — so after the first `initialize()` the\n * flag is permanently `true` and cannot by itself distinguish *\"a session we established\"* from\n * *\"somebody else's\"*. Ownership therefore persists for the life of the module, which is correct in\n * production (a process that initialised once keeps re-initialising its own session) but makes the\n * ADOPTION branch unreachable in a test file that has already constructed an owned enforcer.\n *\n * Production code has no reason to call this: relinquishing ownership while a session we established\n * is still live would make the next construction adopt it and refuse to re-initialise.\n */\nexport const releaseSrtOwnershipForTests = (): void => {\n selfInitialized = false\n selfSessionClaimed = false\n}\n\n/** Construction options for {@link srtEnforcer}. */\nexport type SrtEnforcerOptions = {\n /**\n * Absolute path to the POSIX shell used to invoke wrapped commands. Defaults to `/bin/bash`.\n *\n * @remarks\n * Validated at construction in TWO checks, both required: it must be ABSOLUTE (a bare `bash` passes\n * any basename test while remaining `PATH`-dependent — the exact hazard the absolute default\n * avoids), and its basename must be on the allow-list (`sh`, `bash`, `dash`, `zsh`, `ksh`) — the\n * shells whose quoting the single escaper is correct for. An allow-list rather than a deny-list is\n * deliberate: `fish` and `nu` are POSIX-ish enough to look safe and different enough to break\n * single-quote escaping, so an unverified shell must fail closed.\n */\n binShell?: string\n /**\n * The ADK-owned policy to enforce.\n *\n * @remarks\n * Mapped to SRT's config inside this module and nowhere else. The derived baseline is captured\n * immediately after `initialize()`, because `SandboxManager` is a process-global singleton whose\n * SECOND `initialize()` is a no-op — a later call with a different policy silently keeps the first.\n */\n policy: SandboxPolicy\n /**\n * Host environment variable NAMES a sandboxed child may inherit. Defaults to `['PATH']`.\n *\n * @remarks\n * The child inherits NOTHING from the host beyond these names. That default is deliberate: a model\n * that can direct the shell's argv can run `env`, so anything inherited is readable back into its\n * context — and no filesystem or network policy stops it, because the value arrives in the tool\n * result rather than over the wire.\n *\n * **This REPLACES the default, it does not extend it.** A caller who needs `CARGO_HOME` and still\n * wants binaries to resolve must pass BOTH: `['PATH', 'CARGO_HOME']`. Passing `['CARGO_HOME']` alone\n * drops `PATH`, which breaks `search_files` on any host where `rg` lives outside `/usr/bin`.\n *\n * An entry that is not a valid POSIX environment-variable name throws `E_INVALID_SANDBOX_CONFIG` at\n * construction rather than being skipped, so a typo surfaces as a startup error instead of a\n * variable that silently never arrives.\n */\n envAllowList?: readonly string[]\n /**\n * Pass the ENTIRE host environment to sandboxed children. Defaults to `false`.\n *\n * @remarks\n * The escape hatch for a deployment that genuinely needs ambient configuration, and it is worth\n * being blunt about what it re-opens: **every secret in the host process becomes readable by the\n * model**, because `run_shell_command` exists precisely to run commands the model chose and `env` is\n * one of them. Prefer naming what you need in `envAllowList`.\n */\n inheritHostEnv?: boolean\n /**\n * Absolute path to a Linux bubblewrap wrapper or binary. SRT reads it from the config passed to\n * `initialize()`; on the Linux launch path it becomes argv[0] of the bwrap command\n * (`linux-sandbox-utils.js:1539`). A wrapper script placed at this path therefore receives every\n * bwrap argument and can strip or rewrite options such as `--unshare-net`. On Linux a nonexistent\n * path fails in SRT's `initialize()`; on macOS it is neither validated nor used.\n */\n bwrapPath?: string\n /**\n * Absolute path to a Linux socat binary or wrapper. SRT passes it to `initializeLinuxNetworkBridge`\n * on Linux. A nonexistent path fails in SRT's `initialize()`; on macOS it is neither validated nor\n * used.\n */\n socatPath?: string\n /**\n * Enable SRT's weaker nested-sandbox mode, for running inside an unprivileged container.\n *\n * @remarks\n * Bubblewrap cannot mount a fresh `/proc` inside an unprivileged container, so the sandbox fails to\n * start at all — the symptom is `apply-seccomp: write /proc/self/uid_map: Operation not permitted`\n * with exit 1, an empty stdout and NO diagnostics, which is indistinguishable from a policy denial.\n * This flag makes the inner sandbox bind-mount the container's EXISTING `/proc` instead.\n *\n * **It considerably weakens the boundary**, in upstream's own words: the bind-mounted `/proc`\n * exposes process information a fresh mount would hide. Only enable it when the OUTER container\n * already provides the isolation you need — it trades inner isolation for the sandbox running at all.\n *\n * Session-level: SRT reads it from the config given to `initialize()`, never per call.\n */\n enableWeakerNestedSandbox?: boolean\n}\n\n/**\n * Construct the SRT-backed policy enforcer — the OS boundary.\n *\n * @remarks\n * REFUSES rather than degrades on an unsupported platform: native `win32` throws\n * `E_SANDBOX_UNSUPPORTED_ENV` naming WSL2, because Windows folds all four filesystem-policy lists\n * case-insensitively and throws on per-exec allows. A half-built branch there would be a DIVERGENT\n * boundary rather than a limited one, so there is deliberately no native-Windows evaluator.\n *\n * `run()` resolves on SPAWN with live streams plus a separate `completed` promise, never a settled\n * exit code. That split is not stylistic: one promise cannot both hand over unread child streams AND\n * carry an exit code, because settling requires the streams to have ended and they cannot end before\n * someone drains them. A caller MUST drain both concurrently — pipe buffers are per-fd, so draining\n * one to completion first can block the other and hang the child.\n *\n * @param options - Shell and policy configuration.\n * @returns An enforcer whose derived baseline is already captured.\n */\nexport const srtEnforcer = async (options: SrtEnforcerOptions): Promise<SandboxPolicyEnforcer> => {\n if (process.platform !== 'darwin' && process.platform !== 'linux') return unsupported()\n const binShell = validateBinShell(options.binShell)\n const bwrapPath = validateAbsoluteBinaryPath(options.bwrapPath, 'bwrapPath')\n const socatPath = validateAbsoluteBinaryPath(options.socatPath, 'socatPath')\n // Resolved ONCE: the allow-list is fixed for the enforcer's life, so re-picking per spawn would only\n // add a chance for the two to disagree. Validation throws here, at construction, so a typo'd variable\n // name is a startup error rather than a variable that silently never arrives.\n const hostEnv = resolveHostEnv(options)\n const srt = (await import('@anthropic-ai/sandbox-runtime')) as unknown as {\n SandboxManager: SrtManager\n }\n const manager = srt.SandboxManager\n // SandboxManager is process-global and first-writer-wins. Detect before initialize: an already\n // enabled manager belongs to somebody else, and initializing it (even with a different policy)\n // would leave this handle describing a sandbox that is not in force.\n // ADOPT only when the live session is somebody ELSE'S. See `selfInitialized`: upstream's flag is\n // sticky across `reset()`, so it alone would make every enforcer after the first one adopt.\n // CLAIM FIRST, CLASSIFY SECOND — and both synchronously, because the boundary is a process-global\n // with no lock to take. An earlier revision computed `adopted` from `isSandboxingEnabled()` and only\n // claimed after `initialize()` resolved, which left TWO windows for concurrent constructions:\n // · both read `claimed === false`, both proceeded, and the second re-initialised over the first;\n // · worse, the second saw `enabled === true` (the first had just initialised) with our marker not\n // yet set, so it classified a session WE established as FOREIGN and adopted it — reporting the\n // other caller's policy as though it were an adopted third-party sandbox.\n // Verified both: `Promise.all` of two enforcers resolved BOTH, the second with `adopted: true` and\n // the first's derived lists. Claiming before any await closes them together.\n const claimedByUs = selfSessionClaimed\n const alreadyLive = manager.isSandboxingEnabled()\n const adopted = alreadyLive && !selfInitialized && !claimedByUs\n if (!adopted) {\n // RESET BEFORE RE-INITIALISING OUR OWN SESSION. Upstream's `initialize()` assigns `config`\n // unconditionally, but the DERIVED getters are computed from the managerContext built during\n // initialisation — so calling it twice without a reset leaves the FIRST policy's derived lists in\n // force. Verified: init A, then init B, and `getFsReadConfig()` still reports A; reset then init C\n // reports C. Without this, a second `srtEnforcer()` in one process silently enforces the previous\n // policy while reporting the new one.\n //\n // Only ever our OWN session: `adopted` is false here, and `selfInitialized` proves we established\n // what is currently live rather than inheriting somebody else's.\n //\n // AND ONLY A RELEASED ONE. Resetting a session a live handle still holds would silently replace\n // that handle's policy with this one — first-writer-wins broken in the constructor, before the\n // manager's admission check can refuse anything. Refuse here instead, where nothing has changed\n // yet: the caller must dispose the existing handle before establishing a different policy.\n if (selfSessionClaimed) {\n throw new E_SANDBOX_POLICY_CONFLICT([\n 'a sandbox session established by this process is still live; dispose that handle before ' +\n 'constructing another enforcer, or reuse it — replacing it here would widen the live ' +\n 'policy before admission could refuse it',\n ])\n }\n // CLAIM SYNCHRONOUSLY, BEFORE THE FIRST AWAIT. Setting this after `initialize()` leaves a window\n // in which two concurrent constructions both read `false`, both proceed, and the second silently\n // re-initialises over the first — verified: `Promise.all` of two enforcers with different policies\n // resolved BOTH, and the second reported the first's derived lists. The check and the claim must be\n // one synchronous step, because there is no lock to take: the boundary is a process-global.\n selfSessionClaimed = true\n try {\n if (selfInitialized && manager.isSandboxingEnabled()) await manager.reset()\n // THE nested-sandbox flag MUST ride this call and only this one. SRT resolves it from the\n // module-level config `initialize()` stored (`getEnableWeakerNestedSandbox()` reads\n // `config?.enableWeakerNestedSandbox`), NOT from the per-call config handed to\n // `wrapWithSandboxArgv` — so passing it only at the `run()` site below would compile, ship, and\n // silently do nothing.\n await manager.initialize(\n mapPolicy(options.policy, {\n enableWeakerNestedSandbox: options.enableWeakerNestedSandbox,\n bwrapPath,\n socatPath,\n })\n )\n selfInitialized = true\n } catch (error) {\n // A failed establishment must not leave the claim held, or the process is permanently wedged\n // with no live session to dispose.\n selfSessionClaimed = false\n throw error\n }\n }\n\n const platform = process.platform === 'linux' ? 'linux' : 'darwin'\n const cwd = process.cwd()\n const derive = (): DerivedRules => {\n const config = manager.getConfig()\n const filesystem = config?.filesystem ?? {}\n const network = config?.network ?? {}\n const allowGitConfig = adopted\n ? Boolean(filesystem.allowGitConfig)\n : (options.policy.filesystem.allowGitConfig ?? false)\n const searchDepth = adopted\n ? (config?.mandatoryDenySearchDepth ?? 3)\n : (options.policy.filesystem.mandatoryDenySearchDepth ?? 3)\n const dotGit = dotGitIsDirectory(cwd)\n return derivedRulesFromSrt({\n platform,\n read: manager.getFsReadConfig(),\n write: manager.getFsWriteConfig(),\n filesystemDisabled: adopted\n ? Boolean(filesystem.disabled)\n : options.policy.filesystem.disabled,\n // In adoption mode these are literal foreign lists. In particular, ['*'] is not evidence\n // that the foreign consumer used ADK's disabled mode.\n network: {\n // ADOPTION ⇒ always `false`: we did not construct this session, so \"we were constructed in\n // disabled mode\" cannot be true of it. OWNED ⇒ the ADK policy's own value, which is what makes\n // the drift SKIP branch and the `false → true` widening check implementable.\n disabled: adopted ? false : (options.policy.network.disabled ?? false),\n allowedDomains: adopted\n ? Array.isArray(network.allowedDomains)\n ? network.allowedDomains\n : []\n : options.policy.network.disabled\n ? undefined\n : nonEmpty(options.policy.network.allowedDomains),\n deniedDomains: adopted\n ? Array.isArray(network.deniedDomains)\n ? network.deniedDomains\n : []\n : options.policy.network.disabled\n ? []\n : nonEmpty(options.policy.network.deniedDomains),\n },\n mandatoryDeny: {\n form: platform === 'linux' ? 'expanded-paths' : 'glob',\n entries: reproduceMandatoryDeny({\n cwd,\n allowGitConfig,\n platform,\n dotGitIsDirectory: dotGit,\n }),\n allowGitConfig,\n searchDepth,\n ...(platform === 'linux' ? { dotGitWasDirectory: dotGit } : {}),\n },\n })\n }\n // CAPTURE the derived baseline immediately after our initialize, or the live foreign baseline\n // when adopting. It is deliberately derived from SRT's getters, not from the requested policy.\n const derived: DerivedRules = derive()\n // PRIME THE GLOB MATCHER with upstream's own `globToRegex` for every rule in the derived lists.\n // This is the whole reason `srtEnforcer` is async at the right moment: the evaluator is synchronous\n // (it is consulted per path) while the upstream import is not, so unless priming happens HERE the\n // synchronous fallback becomes the only path that ever runs in production — making the plan's\n // \"deep-import the matcher, never port it\" rule true on paper and false in fact.\n await primeGlobMatcher([\n ...derived.read.denyOnly,\n ...derived.read.allowWithinDeny,\n ...derived.write.allowOnly,\n ...derived.write.denyWithinAllow,\n ...derived.mandatoryDeny.entries,\n ])\n const enforcer: SandboxPolicyEnforcer = {\n isSupported: () => manager.isSupportedPlatform(),\n adopted,\n checkDependencies: async () => manager.checkDependenciesAsync(),\n run: async (op) => {\n const mapped = mapPolicy(op.policy)\n const command = await quoteShellArgs(op.argv)\n const wrapped = await manager.wrapWithSandboxArgv(\n command,\n binShell,\n mapped,\n op.signal,\n op.cwd,\n { commandId: op.correlationId }\n )\n const child = spawn(wrapped.argv[0], wrapped.argv.slice(1), {\n cwd: op.cwd,\n // SRT's `wrapped.env` is the unchanged process environment, not sandbox plumbing. Merging it\n // here would defeat the allow-list by reintroducing every host variable. SRT's proxy, CA\n // bundle, and git safe-directory plumbing are encoded in `wrapped.argv` instead. The caller's\n // per-call environment remains the final overlay, by contract.\n env: { ...hostEnv, ...(op.env ?? {}) },\n stdio: ['ignore', 'pipe', 'pipe'],\n shell: false,\n detached: true,\n })\n let terminate: (() => void) | undefined\n if (op.signal) {\n terminate = () => {\n try {\n if (child.pid) process.kill(-child.pid, 'SIGKILL')\n else child.kill('SIGKILL')\n } catch {\n child.kill('SIGKILL')\n }\n }\n if (op.signal.aborted) terminate()\n else op.signal.addEventListener('abort', terminate, { once: true })\n }\n const cleanup = () => {\n if (terminate && op.signal) op.signal.removeEventListener('abort', terminate)\n terminate = undefined\n }\n const completed = new Promise<{ exitCode: number; failed: boolean }>((settle) => {\n // 'error' MUST be handled, and not only so `completed` settles: an unhandled 'error' on a\n // ChildProcess is an uncaught exception that terminates the HOST process. A missing wrapper\n // binary or an unusable `cwd` would therefore kill the agent instead of failing one command.\n // Both listeners are `once` and the promise settles first-write-wins, so the pair is safe:\n // a spawn failure emits 'error' then 'close', and the later 'close' is discarded.\n child.once('error', () => {\n cleanup()\n settle({ exitCode: 1, failed: true })\n })\n child.once('close', (code) => {\n cleanup()\n settle({ exitCode: code ?? 1, failed: (code ?? 1) !== 0 })\n })\n })\n return {\n stdout: toWeb(child.stdout!),\n stderr: toWeb(child.stderr!),\n completed,\n }\n },\n // Owned sessions are immutable from this adapter's perspective and retain the cheap cached\n // snapshot. An adopted session must re-read SRT on every call: a foreign updateConfig() can widen\n // the live sandbox between invocations, and drift() must compare against that live state.\n effectivePolicy: () => (adopted ? derive() : derived),\n diagnosticsFor: (id) =>\n manager.getSandboxViolationStore().getViolationsForCommand(id).map(String),\n dispose: async () => {\n // Adoption is reported as a no-op. Resetting here would tear down ACEs owned by the host\n // application; only the manager we initialized may be reset.\n // ADOPTION IS A REPORTED NO-OP: resetting here would tear down ACEs owned by the host\n // application. Only a session we established may be reset.\n //\n // `selfInitialized` is deliberately NOT cleared. `reset()` leaves upstream's `config` set, so\n // `isSandboxingEnabled()` stays `true` forever after the first initialise — and clearing our\n // marker would make the NEXT construction see \"enabled, not ours\" and adopt a dead session,\n // silently enforcing the disposed policy. Ownership is a property of this process having\n // initialised at all, not of a session still being live.\n if (!adopted && manager.isSandboxingEnabled()) {\n await manager.reset()\n // Release the claim so a later construction may establish a new session. `selfInitialized`\n // deliberately stays true: upstream's `config` survives `reset()`, so clearing it would make\n // the next construction see \"enabled, not ours\" and adopt a dead session.\n selfSessionClaimed = false\n }\n },\n }\n return enforcer\n}\n\nexport { mapPolicy }\n","import { assertArgvValue, assertAllowedRipgrepFlag } from '../escape'\nimport type { HitFrame, PathFrame, SandboxPolicy } from '../types'\nimport type { SandboxPolicyEnforcer } from '../contracts/policy_enforcer'\n\n/**\n * A classified ripgrep outcome.\n *\n * @remarks\n * CLASSIFICATION ORDER IS LOAD-BEARING: diagnostics are checked BEFORE the exit status, because `rg`\n * runs under the sandbox and a denied path surfaces as a non-zero exit *plus* a violation record.\n * Triage generically first and a policy refusal is permanently mislabelled `io-failure` — the model\n * is told \"search broke\" when the truth is \"you may not read there\", and `denied-by-policy` becomes\n * unreachable despite being in the tool's outcome list.\n *\n * `no-matches` is a RESULT, not an error: `rg` exits 1 when it ran correctly and matched nothing.\n */\nexport type RipgrepFailure =\n | { kind: 'denied-by-policy'; diagnostics: readonly string[] }\n | { kind: 'invalid-pattern'; message: string }\n | { kind: 'no-matches' }\n | { kind: 'io-failure'; message: string; exitCode: number }\n\n/**\n * Convert a classified ripgrep failure into the error the searcher throws.\n *\n * @remarks\n * Exhaustive by construction: a new {@link RipgrepFailure} arm is a compile error here rather than\n * a silently generic message. `denied-by-policy` carries its diagnostics into the message — the\n * classification is checked BEFORE the generic exit-code triage precisely so a policy refusal is\n * not mislabelled `io-failure`, and flattening it back to a bare exit code here would discard the\n * only thing that makes that ordering worth having.\n *\n * @param failure - A classified failure other than `no-matches`, which is a result, not an error.\n * @returns The error to throw.\n */\nconst toRipgrepError = (failure: Exclude<RipgrepFailure, { kind: 'no-matches' }>): Error => {\n switch (failure.kind) {\n case 'denied-by-policy':\n return new Error(`denied-by-policy: ${failure.diagnostics.join('; ')}`)\n case 'invalid-pattern':\n return new Error(failure.message)\n case 'io-failure':\n return new Error(failure.message || `ripgrep exited ${failure.exitCode}`)\n }\n}\n\nconst bytes = new TextDecoder()\nconst collect = async (\n stream: ReadableStream<Uint8Array>,\n onText: (text: string) => void\n): Promise<void> => {\n const reader = stream.getReader()\n try {\n for (;;) {\n const next = await reader.read()\n if (next.done) return\n onText(bytes.decode(next.value, { stream: true }))\n }\n } finally {\n reader.releaseLock()\n }\n}\n\n/** Ripgrep backend. Both child pipes are drained immediately and concurrently. */\nexport const createRipgrepSearch = (\n enforcer: SandboxPolicyEnforcer,\n policy: SandboxPolicy\n): {\n searchContent(o: {\n root: string\n pattern: string\n maxDepth: number\n limit: number\n ignoreCase?: boolean\n literal?: boolean\n glob?: string\n iglob?: string\n follow?: boolean\n hidden?: boolean\n noIgnore?: boolean\n signal?: AbortSignal\n }): AsyncIterable<HitFrame>\n findPaths(o: {\n root: string\n glob: string\n maxDepth: number\n limit: number\n iglob?: string\n follow?: boolean\n hidden?: boolean\n noIgnore?: boolean\n signal?: AbortSignal\n }): AsyncIterable<PathFrame>\n} => {\n const run = async (argv: string[], cwd: string, signal?: AbortSignal) => {\n const correlationId = `${crypto.randomUUID()}-${Date.now().toString(36)}`\n const spawned = await enforcer.run({ argv, cwd, policy, correlationId, signal })\n let out = ''\n let err = ''\n const stdout = collect(spawned.stdout, (chunk) => {\n out += chunk\n })\n const stderr = collect(spawned.stderr, (chunk) => {\n err += chunk\n })\n const [, , completed] = await Promise.all([stdout, stderr, spawned.completed])\n const diagnostics = enforcer.diagnosticsFor(correlationId)\n const exitCode = completed.exitCode\n if (diagnostics.length > 0)\n return { failure: { kind: 'denied-by-policy', diagnostics } as RipgrepFailure, out, err }\n if (exitCode === 2 && err.startsWith('regex parse error'))\n return { failure: { kind: 'invalid-pattern', message: err } as RipgrepFailure, out, err }\n if (exitCode === 1) return { failure: { kind: 'no-matches' } as RipgrepFailure, out, err }\n if (exitCode !== 0)\n return { failure: { kind: 'io-failure', message: err, exitCode } as RipgrepFailure, out, err }\n return { failure: undefined, out, err }\n }\n /**\n * Defence-in-depth check that this adapter only ever emits flags it declared.\n *\n * @remarks\n * Scans the WHOLE argv rather than a fixed index window: the previous `slice(1, 5)` silently depended\n * on argv layout, so adding or reordering an argument moved the window off the flags. It also stops at\n * `--`, because everything after the terminator is a model-supplied VALUE — a pattern like `--foo` is\n * a legitimate search string there, not a flag, and `assertArgvValue` already owns that half.\n */\n const assertAdapterFlags = (argv: readonly string[]): void => {\n for (const arg of argv.slice(1)) {\n if (arg === '--') break\n if (arg.startsWith('--')) assertAllowedRipgrepFlag(arg)\n }\n }\n return {\n async *searchContent(o) {\n if (!Number.isInteger(o.limit) || o.limit < 1)\n throw new Error('limit must be a positive integer')\n if (o.follow)\n throw new Error('follow is refused: descendant symlink containment audit pending')\n const argv = [\n 'rg',\n '--json',\n ...(o.ignoreCase ? ['--ignore-case'] : []),\n ...(o.literal ? ['--fixed-strings'] : []),\n ...(o.glob ? ['--glob', assertArgvValue(o.glob)] : []),\n ...(o.iglob ? ['--iglob', assertArgvValue(o.iglob)] : []),\n ...(o.hidden ? ['--hidden'] : []),\n ...(o.noIgnore ? ['--no-ignore'] : []),\n '--max-depth',\n String(o.maxDepth),\n '--',\n assertArgvValue(o.pattern),\n assertArgvValue(o.root),\n ]\n assertAdapterFlags(argv)\n const result = await run(argv, o.root, o.signal)\n if (result.failure) {\n if (result.failure.kind === 'no-matches') {\n yield { kind: 'done', complete: true }\n return\n }\n throw toRipgrepError(result.failure)\n }\n let shown = 0\n let overLimit = false\n for (const line of result.out.split('\\n')) {\n if (!line) continue\n try {\n const item = JSON.parse(line) as {\n type: string\n data?: { path?: { text?: string }; line_number?: number; lines?: { text?: string } }\n }\n if (\n item.type === 'match' &&\n item.data?.path?.text &&\n item.data.line_number &&\n item.data.lines\n ) {\n if (shown >= o.limit) {\n overLimit = true\n break\n }\n yield {\n kind: 'item',\n path: item.data.path.text,\n line: item.data.line_number,\n text: item.data.lines.text ?? '',\n }\n shown++\n }\n } catch {\n /* malformed rg diagnostics are an I/O failure in the real adapter */\n }\n }\n yield overLimit\n ? { kind: 'done', complete: false, omitted: 'over-limit', bound: 'limit', shown }\n : { kind: 'done', complete: true }\n },\n async *findPaths(o) {\n if (!Number.isInteger(o.limit) || o.limit < 1)\n throw new Error('limit must be a positive integer')\n if (o.follow)\n throw new Error('follow is refused: descendant symlink containment audit pending')\n const argv = [\n 'rg',\n '--files',\n '--glob',\n assertArgvValue(o.glob),\n ...(o.iglob ? ['--iglob', assertArgvValue(o.iglob)] : []),\n ...(o.hidden ? ['--hidden'] : []),\n ...(o.noIgnore ? ['--no-ignore'] : []),\n '--max-depth',\n String(o.maxDepth),\n '--',\n assertArgvValue(o.root),\n ]\n assertAdapterFlags(argv)\n const result = await run(argv, o.root, o.signal)\n if (result.failure) {\n if (result.failure.kind === 'no-matches') {\n yield { kind: 'done', complete: true }\n return\n }\n throw toRipgrepError(result.failure)\n }\n let shown = 0\n let overLimit = false\n for (const path of result.out.split('\\n')) {\n if (!path) continue\n if (shown >= o.limit) {\n overLimit = true\n break\n }\n yield { kind: 'item', path }\n shown++\n }\n yield overLimit\n ? { kind: 'done', complete: false, omitted: 'over-limit', bound: 'limit', shown }\n : { kind: 'done', complete: true }\n },\n }\n}\n"],"mappings":"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"}
|
|
@@ -99,7 +99,7 @@ var mapPolicy = (policy, runtime = {}) => {
|
|
|
99
99
|
denyWrite: nonEmpty(fs.denyWrite),
|
|
100
100
|
allowGitConfig: fs.allowGitConfig ?? false
|
|
101
101
|
},
|
|
102
|
-
network: {
|
|
102
|
+
...!net.disabled ? { network: {
|
|
103
103
|
allowedDomains,
|
|
104
104
|
deniedDomains,
|
|
105
105
|
deniedDomainReasons,
|
|
@@ -107,7 +107,7 @@ var mapPolicy = (policy, runtime = {}) => {
|
|
|
107
107
|
allowLocalBinding: false,
|
|
108
108
|
allowUnixSockets: [],
|
|
109
109
|
allowMachLookup: []
|
|
110
|
-
},
|
|
110
|
+
} } : {},
|
|
111
111
|
git: { safeDirectories: nonEmpty(fs.gitSafeDirectories ?? [process.cwd()]) }
|
|
112
112
|
};
|
|
113
113
|
};
|
|
@@ -208,7 +208,7 @@ var srtEnforcer = async (options) => {
|
|
|
208
208
|
filesystemDisabled: adopted ? Boolean(filesystem.disabled) : options.policy.filesystem.disabled,
|
|
209
209
|
network: {
|
|
210
210
|
disabled: adopted ? false : options.policy.network.disabled ?? false,
|
|
211
|
-
allowedDomains: adopted ? Array.isArray(network.allowedDomains) ? network.allowedDomains : [] : options.policy.network.disabled ?
|
|
211
|
+
allowedDomains: adopted ? Array.isArray(network.allowedDomains) ? network.allowedDomains : [] : options.policy.network.disabled ? void 0 : nonEmpty(options.policy.network.allowedDomains),
|
|
212
212
|
deniedDomains: adopted ? Array.isArray(network.deniedDomains) ? network.deniedDomains : [] : options.policy.network.disabled ? [] : nonEmpty(options.policy.network.deniedDomains)
|
|
213
213
|
},
|
|
214
214
|
mandatoryDeny: {
|
|
@@ -252,17 +252,41 @@ var srtEnforcer = async (options) => {
|
|
|
252
252
|
"pipe",
|
|
253
253
|
"pipe"
|
|
254
254
|
],
|
|
255
|
-
shell: false
|
|
255
|
+
shell: false,
|
|
256
|
+
detached: true
|
|
256
257
|
});
|
|
258
|
+
let terminate;
|
|
259
|
+
if (op.signal) {
|
|
260
|
+
terminate = () => {
|
|
261
|
+
try {
|
|
262
|
+
if (child.pid) process.kill(-child.pid, "SIGKILL");
|
|
263
|
+
else child.kill("SIGKILL");
|
|
264
|
+
} catch {
|
|
265
|
+
child.kill("SIGKILL");
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
if (op.signal.aborted) terminate();
|
|
269
|
+
else op.signal.addEventListener("abort", terminate, { once: true });
|
|
270
|
+
}
|
|
271
|
+
const cleanup = () => {
|
|
272
|
+
if (terminate && op.signal) op.signal.removeEventListener("abort", terminate);
|
|
273
|
+
terminate = void 0;
|
|
274
|
+
};
|
|
257
275
|
const completed = new Promise((settle) => {
|
|
258
|
-
child.once("error", () =>
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
276
|
+
child.once("error", () => {
|
|
277
|
+
cleanup();
|
|
278
|
+
settle({
|
|
279
|
+
exitCode: 1,
|
|
280
|
+
failed: true
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
child.once("close", (code) => {
|
|
284
|
+
cleanup();
|
|
285
|
+
settle({
|
|
286
|
+
exitCode: code ?? 1,
|
|
287
|
+
failed: (code ?? 1) !== 0
|
|
288
|
+
});
|
|
289
|
+
});
|
|
266
290
|
});
|
|
267
291
|
return {
|
|
268
292
|
stdout: toWeb(child.stdout),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.mjs","names":[],"sources":["../../../src/batteries/sandbox/node/srt_enforcer.ts","../../../src/batteries/sandbox/node/search_ripgrep.ts"],"sourcesContent":["import { statSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { spawn } from 'node:child_process'\nimport { quoteShellArgs, validateAbsoluteBinaryPath, validateBinShell } from '../escape'\nimport { derivedRulesFromSrt, primeGlobMatcher, reproduceMandatoryDeny } from './fs_node'\nimport {\n E_INVALID_SANDBOX_CONFIG,\n E_SANDBOX_UNSUPPORTED_ENV,\n E_SANDBOX_POLICY_CONFLICT,\n} from '../exceptions'\nimport type { SandboxPolicy, DerivedRules } from '../types'\nimport type { SandboxPolicyEnforcer } from '../contracts/policy_enforcer'\n\ntype SrtManager = {\n initialize(config: SrtConfig): Promise<void>\n isSupportedPlatform(): boolean\n isSandboxingEnabled(): boolean\n checkDependenciesAsync(): Promise<{ errors: string[]; warnings: string[] }>\n wrapWithSandboxArgv(\n command: string,\n shell?: string,\n custom?: unknown,\n signal?: AbortSignal,\n cwd?: string,\n options?: { commandId?: string }\n ): Promise<{ argv: string[]; env: Record<string, string> }>\n getFsReadConfig(): { denyOnly: string[]; allowWithinDeny?: string[] }\n getFsWriteConfig(): { allowOnly: string[]; denyWithinAllow: string[] }\n getNetworkRestrictionConfig(): {\n allowedHosts?: string[]\n deniedHosts?: string[]\n }\n getConfig(): SrtConfig | undefined\n getSandboxViolationStore(): {\n getViolationsForCommand(command: string): unknown[]\n }\n reset(): Promise<void>\n}\ntype SrtConfig = {\n filesystem: Record<string, unknown>\n network: Record<string, unknown>\n /** SRT's Linux mandatory-deny scan depth is session-level. */\n mandatoryDenySearchDepth?: number\n git?: { safeDirectories: string[] }\n /** Top-level and session-level: SRT reads this from the config `initialize()` stored, not per call. */\n enableWeakerNestedSandbox?: boolean\n bwrapPath?: string\n socatPath?: string\n}\n\nconst unsupported = (): never => {\n throw new E_SANDBOX_UNSUPPORTED_ENV([\n `SRT sandbox is unavailable on ${process.platform}; use WSL2 or the SES browser path`,\n ])\n}\nconst nonEmpty = (v: readonly string[] | undefined): string[] => [...(v ?? [])]\n\n/**\n * Host variables a sandboxed child inherits when the caller names no allow-list.\n *\n * @remarks\n * `PATH` ONLY, and it is the minimum rather than a convenience. The ripgrep searcher spawns `rg` by\n * BARE NAME, so with no `PATH` the child sees only the one a shell synthesises\n * (`/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:.`) — which does not contain `/opt/homebrew/bin` or a Nix\n * profile, so `search_files` would fail as `io-failure` on any such host and present as *\"the search\n * tool is broken\"* rather than as a configuration error.\n *\n * `PATH` is not a credential: a model that runs `env` learns where binaries live, not a secret. Every\n * genuinely sensitive variable — and `HOME`, `USER`, `TMPDIR`, which leak host layout — stays out.\n */\nconst DEFAULT_ENV_ALLOW_LIST: readonly string[] = ['PATH']\n\n/** POSIX environment-variable names. Anything else could not be exported by a shell anyway. */\nconst ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/\n\n/**\n * Resolve the host half of a sandboxed child's environment ONCE, at construction.\n *\n * @remarks\n * DENY BY DEFAULT. Before this existed the enforcer spread the entire `process.env` into every child,\n * so a model directing the shell's argv could run `env` and read the host's credentials straight back\n * into its own context — which no filesystem or network policy prevents, because the secret arrives in\n * the tool result rather than over the wire.\n *\n * The allow-list REPLACES this default rather than extending it; that is stated on the option, because\n * the opposite assumption is the natural one and would silently drop `PATH`.\n *\n * @param options - The enforcer's construction options.\n * @returns The host variables to seed the child's environment with.\n */\nconst resolveHostEnv = (options: SrtEnforcerOptions): Record<string, string> => {\n if (options.inheritHostEnv === true) {\n const all: Record<string, string> = {}\n for (const [name, value] of Object.entries(process.env))\n if (value !== undefined) all[name] = value\n return all\n }\n const names = options.envAllowList ?? DEFAULT_ENV_ALLOW_LIST\n const picked: Record<string, string> = {}\n for (const name of names) {\n if (!ENV_NAME.test(name))\n throw new E_INVALID_SANDBOX_CONFIG([\n `envAllowList entry ${JSON.stringify(name)} is not a valid environment variable name`,\n ])\n const value = process.env[name]\n if (value !== undefined) picked[name] = value\n }\n return picked\n}\n\n/**\n * Whether `<cwd>/.git` is a real DIRECTORY.\n *\n * @remarks\n * Load-bearing on Linux only: upstream pushes the workspace-root `.git/hooks` and `.git/config` deny\n * entries just when `.git` is a directory, because in a worktree it is a FILE and denying it would\n * make bubblewrap fail. Treating a worktree as a directory would deny what the profile permits.\n */\nconst dotGitIsDirectory = (cwd: string): boolean => {\n try {\n return statSync(resolve(cwd, '.git')).isDirectory()\n } catch {\n return false\n }\n}\n\n/**\n * The sole ADK-to-SRT translation point. Keep the upstream type behind this local firewall.\n *\n * @remarks\n * `runtime` carries ADAPTER-level settings that are deliberately absent from `SandboxPolicy`. That\n * type is ADK-owned, SRT-neutral vocabulary — naming an SRT feature in it would make the firewall\n * nominal and leave a non-SRT enforcer unable to implement the contract — so an SRT-specific switch\n * belongs on the adapter's own options, exactly as `binShell` already does.\n */\nconst mapPolicy = (\n policy: SandboxPolicy,\n runtime: {\n enableWeakerNestedSandbox?: boolean\n bwrapPath?: string\n socatPath?: string\n } = {}\n): SrtConfig => {\n const fs = policy.filesystem\n const net = policy.network\n if (\n net.disabled &&\n (net.deniedDomains?.length ||\n Object.keys(net.deniedDomainReasons ?? {}).length ||\n (net.allowedDomains && !(net.allowedDomains.length === 1 && net.allowedDomains[0] === '*')))\n )\n throw new E_INVALID_SANDBOX_CONFIG(['network.disabled contradicts network domain restrictions'])\n const allowedDomains = net.disabled ? ['*'] : nonEmpty(net.allowedDomains)\n const deniedDomains = net.disabled ? [] : nonEmpty(net.deniedDomains)\n const deniedDomainReasons = net.disabled ? {} : { ...(net.deniedDomainReasons ?? {}) }\n return {\n ...(fs.mandatoryDenySearchDepth !== undefined\n ? { mandatoryDenySearchDepth: fs.mandatoryDenySearchDepth }\n : {}),\n // Emitted only when enabled, so an untouched configuration is byte-identical to before this\n // option existed — a drift snapshot taken either side of the upgrade compares equal.\n ...(runtime.enableWeakerNestedSandbox === true ? { enableWeakerNestedSandbox: true } : {}),\n ...(runtime.bwrapPath !== undefined ? { bwrapPath: runtime.bwrapPath } : {}),\n ...(runtime.socatPath !== undefined ? { socatPath: runtime.socatPath } : {}),\n filesystem: {\n ...(fs.disabled ? { disabled: true } : {}),\n denyRead: nonEmpty(fs.denyRead),\n allowRead: nonEmpty(fs.allowRead),\n allowWrite: nonEmpty(fs.allowWrite),\n denyWrite: nonEmpty(fs.denyWrite),\n allowGitConfig: fs.allowGitConfig ?? false,\n },\n network: {\n allowedDomains,\n deniedDomains,\n deniedDomainReasons,\n strictAllowlist: true,\n allowLocalBinding: false,\n allowUnixSockets: [],\n allowMachLookup: [],\n },\n git: {\n safeDirectories: nonEmpty(fs.gitSafeDirectories ?? [process.cwd()]),\n },\n }\n}\n\nconst toWeb = (stream: NodeJS.ReadableStream): ReadableStream<Uint8Array> =>\n (\n ReadableStream as unknown as {\n from?: (x: unknown) => ReadableStream<Uint8Array>\n }\n ).from\n ? (\n ReadableStream as unknown as {\n from: (x: unknown) => ReadableStream<Uint8Array>\n }\n ).from(stream)\n : new ReadableStream({\n start(controller) {\n stream.on('data', (x: Buffer) => controller.enqueue(new Uint8Array(x)))\n stream.on('end', () => controller.close())\n stream.on('error', (e) => controller.error(e))\n },\n })\n\n/** Options for {@link srtEnforcer}. */\n/**\n * Whether an enforcer in THIS process performed the `initialize()` that is currently in force.\n *\n * @remarks\n * UPSTREAM'S OWN FLAG CANNOT ANSWER THIS. `isSandboxingEnabled()` is literally `config !== undefined`\n * (`sandbox-manager.js:672-675`) and `reset()` clears `managerContext`, the proxies, the registries —\n * **but never `config`**. So after any reset the flag stays `true` forever, and an enforcer that\n * trusted it would \"adopt\" a sandbox that is no longer in force, silently reporting a stale policy\n * and refusing to re-initialise. Verified: initialize, reset, and `isSandboxingEnabled()` is still\n * `true`.\n *\n * We therefore track what WE established. `true` here means this module owns the live session, so a\n * subsequent construction must re-initialise rather than adopt. `false` with upstream reporting\n * enabled means somebody else got there first — the genuine adoption case.\n */\nlet selfInitialized = false\n\n/**\n * Whether the session THIS module established is still claimed by a live enforcer.\n *\n * @remarks\n * FIRST-WRITER-WINS IS ENFORCED HERE, not only in the manager. `manager.ts` admits or refuses a\n * second handle by comparing policies — but that check runs AFTER `srtEnforcer()` has already\n * constructed, and construction is what touches the process-global. So a reset-and-reinitialise in the\n * constructor would tear down a live policy before anything could refuse it: caller 2 widens the real\n * sandbox, the manager then throws, and caller 1 carries on against a baseline that is no longer in\n * force. The refusal would arrive after the damage.\n *\n * `claimed` is therefore set when we initialise and cleared only by `dispose()`. While it is `true`,\n * re-initialising is refused outright; once cleared, the next construction may re-establish a session,\n * which is what makes sequential owned handles work.\n */\nlet selfSessionClaimed = false\n\n/**\n * Relinquish this module's ownership marker.\n *\n * @remarks\n * TEST SEAM, and a narrow one. The marker exists because upstream's `isSandboxingEnabled()` is\n * `config !== undefined` and `reset()` never clears `config` — so after the first `initialize()` the\n * flag is permanently `true` and cannot by itself distinguish *\"a session we established\"* from\n * *\"somebody else's\"*. Ownership therefore persists for the life of the module, which is correct in\n * production (a process that initialised once keeps re-initialising its own session) but makes the\n * ADOPTION branch unreachable in a test file that has already constructed an owned enforcer.\n *\n * Production code has no reason to call this: relinquishing ownership while a session we established\n * is still live would make the next construction adopt it and refuse to re-initialise.\n */\nexport const releaseSrtOwnershipForTests = (): void => {\n selfInitialized = false\n selfSessionClaimed = false\n}\n\n/** Construction options for {@link srtEnforcer}. */\nexport type SrtEnforcerOptions = {\n /**\n * Absolute path to the POSIX shell used to invoke wrapped commands. Defaults to `/bin/bash`.\n *\n * @remarks\n * Validated at construction in TWO checks, both required: it must be ABSOLUTE (a bare `bash` passes\n * any basename test while remaining `PATH`-dependent — the exact hazard the absolute default\n * avoids), and its basename must be on the allow-list (`sh`, `bash`, `dash`, `zsh`, `ksh`) — the\n * shells whose quoting the single escaper is correct for. An allow-list rather than a deny-list is\n * deliberate: `fish` and `nu` are POSIX-ish enough to look safe and different enough to break\n * single-quote escaping, so an unverified shell must fail closed.\n */\n binShell?: string\n /**\n * The ADK-owned policy to enforce.\n *\n * @remarks\n * Mapped to SRT's config inside this module and nowhere else. The derived baseline is captured\n * immediately after `initialize()`, because `SandboxManager` is a process-global singleton whose\n * SECOND `initialize()` is a no-op — a later call with a different policy silently keeps the first.\n */\n policy: SandboxPolicy\n /**\n * Host environment variable NAMES a sandboxed child may inherit. Defaults to `['PATH']`.\n *\n * @remarks\n * The child inherits NOTHING from the host beyond these names. That default is deliberate: a model\n * that can direct the shell's argv can run `env`, so anything inherited is readable back into its\n * context — and no filesystem or network policy stops it, because the value arrives in the tool\n * result rather than over the wire.\n *\n * **This REPLACES the default, it does not extend it.** A caller who needs `CARGO_HOME` and still\n * wants binaries to resolve must pass BOTH: `['PATH', 'CARGO_HOME']`. Passing `['CARGO_HOME']` alone\n * drops `PATH`, which breaks `search_files` on any host where `rg` lives outside `/usr/bin`.\n *\n * An entry that is not a valid POSIX environment-variable name throws `E_INVALID_SANDBOX_CONFIG` at\n * construction rather than being skipped, so a typo surfaces as a startup error instead of a\n * variable that silently never arrives.\n */\n envAllowList?: readonly string[]\n /**\n * Pass the ENTIRE host environment to sandboxed children. Defaults to `false`.\n *\n * @remarks\n * The escape hatch for a deployment that genuinely needs ambient configuration, and it is worth\n * being blunt about what it re-opens: **every secret in the host process becomes readable by the\n * model**, because `run_shell_command` exists precisely to run commands the model chose and `env` is\n * one of them. Prefer naming what you need in `envAllowList`.\n */\n inheritHostEnv?: boolean\n /**\n * Absolute path to a Linux bubblewrap wrapper or binary. SRT reads it from the config passed to\n * `initialize()`; on the Linux launch path it becomes argv[0] of the bwrap command\n * (`linux-sandbox-utils.js:1539`). A wrapper script placed at this path therefore receives every\n * bwrap argument and can strip or rewrite options such as `--unshare-net`. On Linux a nonexistent\n * path fails in SRT's `initialize()`; on macOS it is neither validated nor used.\n */\n bwrapPath?: string\n /**\n * Absolute path to a Linux socat binary or wrapper. SRT passes it to `initializeLinuxNetworkBridge`\n * on Linux. A nonexistent path fails in SRT's `initialize()`; on macOS it is neither validated nor\n * used.\n */\n socatPath?: string\n /**\n * Enable SRT's weaker nested-sandbox mode, for running inside an unprivileged container.\n *\n * @remarks\n * Bubblewrap cannot mount a fresh `/proc` inside an unprivileged container, so the sandbox fails to\n * start at all — the symptom is `apply-seccomp: write /proc/self/uid_map: Operation not permitted`\n * with exit 1, an empty stdout and NO diagnostics, which is indistinguishable from a policy denial.\n * This flag makes the inner sandbox bind-mount the container's EXISTING `/proc` instead.\n *\n * **It considerably weakens the boundary**, in upstream's own words: the bind-mounted `/proc`\n * exposes process information a fresh mount would hide. Only enable it when the OUTER container\n * already provides the isolation you need — it trades inner isolation for the sandbox running at all.\n *\n * Session-level: SRT reads it from the config given to `initialize()`, never per call.\n */\n enableWeakerNestedSandbox?: boolean\n}\n\n/**\n * Construct the SRT-backed policy enforcer — the OS boundary.\n *\n * @remarks\n * REFUSES rather than degrades on an unsupported platform: native `win32` throws\n * `E_SANDBOX_UNSUPPORTED_ENV` naming WSL2, because Windows folds all four filesystem-policy lists\n * case-insensitively and throws on per-exec allows. A half-built branch there would be a DIVERGENT\n * boundary rather than a limited one, so there is deliberately no native-Windows evaluator.\n *\n * `run()` resolves on SPAWN with live streams plus a separate `completed` promise, never a settled\n * exit code. That split is not stylistic: one promise cannot both hand over unread child streams AND\n * carry an exit code, because settling requires the streams to have ended and they cannot end before\n * someone drains them. A caller MUST drain both concurrently — pipe buffers are per-fd, so draining\n * one to completion first can block the other and hang the child.\n *\n * @param options - Shell and policy configuration.\n * @returns An enforcer whose derived baseline is already captured.\n */\nexport const srtEnforcer = async (options: SrtEnforcerOptions): Promise<SandboxPolicyEnforcer> => {\n if (process.platform !== 'darwin' && process.platform !== 'linux') return unsupported()\n const binShell = validateBinShell(options.binShell)\n const bwrapPath = validateAbsoluteBinaryPath(options.bwrapPath, 'bwrapPath')\n const socatPath = validateAbsoluteBinaryPath(options.socatPath, 'socatPath')\n // Resolved ONCE: the allow-list is fixed for the enforcer's life, so re-picking per spawn would only\n // add a chance for the two to disagree. Validation throws here, at construction, so a typo'd variable\n // name is a startup error rather than a variable that silently never arrives.\n const hostEnv = resolveHostEnv(options)\n const srt = (await import('@anthropic-ai/sandbox-runtime')) as unknown as {\n SandboxManager: SrtManager\n }\n const manager = srt.SandboxManager\n // SandboxManager is process-global and first-writer-wins. Detect before initialize: an already\n // enabled manager belongs to somebody else, and initializing it (even with a different policy)\n // would leave this handle describing a sandbox that is not in force.\n // ADOPT only when the live session is somebody ELSE'S. See `selfInitialized`: upstream's flag is\n // sticky across `reset()`, so it alone would make every enforcer after the first one adopt.\n // CLAIM FIRST, CLASSIFY SECOND — and both synchronously, because the boundary is a process-global\n // with no lock to take. An earlier revision computed `adopted` from `isSandboxingEnabled()` and only\n // claimed after `initialize()` resolved, which left TWO windows for concurrent constructions:\n // · both read `claimed === false`, both proceeded, and the second re-initialised over the first;\n // · worse, the second saw `enabled === true` (the first had just initialised) with our marker not\n // yet set, so it classified a session WE established as FOREIGN and adopted it — reporting the\n // other caller's policy as though it were an adopted third-party sandbox.\n // Verified both: `Promise.all` of two enforcers resolved BOTH, the second with `adopted: true` and\n // the first's derived lists. Claiming before any await closes them together.\n const claimedByUs = selfSessionClaimed\n const alreadyLive = manager.isSandboxingEnabled()\n const adopted = alreadyLive && !selfInitialized && !claimedByUs\n if (!adopted) {\n // RESET BEFORE RE-INITIALISING OUR OWN SESSION. Upstream's `initialize()` assigns `config`\n // unconditionally, but the DERIVED getters are computed from the managerContext built during\n // initialisation — so calling it twice without a reset leaves the FIRST policy's derived lists in\n // force. Verified: init A, then init B, and `getFsReadConfig()` still reports A; reset then init C\n // reports C. Without this, a second `srtEnforcer()` in one process silently enforces the previous\n // policy while reporting the new one.\n //\n // Only ever our OWN session: `adopted` is false here, and `selfInitialized` proves we established\n // what is currently live rather than inheriting somebody else's.\n //\n // AND ONLY A RELEASED ONE. Resetting a session a live handle still holds would silently replace\n // that handle's policy with this one — first-writer-wins broken in the constructor, before the\n // manager's admission check can refuse anything. Refuse here instead, where nothing has changed\n // yet: the caller must dispose the existing handle before establishing a different policy.\n if (selfSessionClaimed) {\n throw new E_SANDBOX_POLICY_CONFLICT([\n 'a sandbox session established by this process is still live; dispose that handle before ' +\n 'constructing another enforcer, or reuse it — replacing it here would widen the live ' +\n 'policy before admission could refuse it',\n ])\n }\n // CLAIM SYNCHRONOUSLY, BEFORE THE FIRST AWAIT. Setting this after `initialize()` leaves a window\n // in which two concurrent constructions both read `false`, both proceed, and the second silently\n // re-initialises over the first — verified: `Promise.all` of two enforcers with different policies\n // resolved BOTH, and the second reported the first's derived lists. The check and the claim must be\n // one synchronous step, because there is no lock to take: the boundary is a process-global.\n selfSessionClaimed = true\n try {\n if (selfInitialized && manager.isSandboxingEnabled()) await manager.reset()\n // THE nested-sandbox flag MUST ride this call and only this one. SRT resolves it from the\n // module-level config `initialize()` stored (`getEnableWeakerNestedSandbox()` reads\n // `config?.enableWeakerNestedSandbox`), NOT from the per-call config handed to\n // `wrapWithSandboxArgv` — so passing it only at the `run()` site below would compile, ship, and\n // silently do nothing.\n await manager.initialize(\n mapPolicy(options.policy, {\n enableWeakerNestedSandbox: options.enableWeakerNestedSandbox,\n bwrapPath,\n socatPath,\n })\n )\n selfInitialized = true\n } catch (error) {\n // A failed establishment must not leave the claim held, or the process is permanently wedged\n // with no live session to dispose.\n selfSessionClaimed = false\n throw error\n }\n }\n\n const platform = process.platform === 'linux' ? 'linux' : 'darwin'\n const cwd = process.cwd()\n const derive = (): DerivedRules => {\n const config = manager.getConfig()\n const filesystem = config?.filesystem ?? {}\n const network = config?.network ?? {}\n const allowGitConfig = adopted\n ? Boolean(filesystem.allowGitConfig)\n : (options.policy.filesystem.allowGitConfig ?? false)\n const searchDepth = adopted\n ? (config?.mandatoryDenySearchDepth ?? 3)\n : (options.policy.filesystem.mandatoryDenySearchDepth ?? 3)\n const dotGit = dotGitIsDirectory(cwd)\n return derivedRulesFromSrt({\n platform,\n read: manager.getFsReadConfig(),\n write: manager.getFsWriteConfig(),\n filesystemDisabled: adopted\n ? Boolean(filesystem.disabled)\n : options.policy.filesystem.disabled,\n // In adoption mode these are literal foreign lists. In particular, ['*'] is not evidence\n // that the foreign consumer used ADK's disabled mode.\n network: {\n // ADOPTION ⇒ always `false`: we did not construct this session, so \"we were constructed in\n // disabled mode\" cannot be true of it. OWNED ⇒ the ADK policy's own value, which is what makes\n // the drift SKIP branch and the `false → true` widening check implementable.\n disabled: adopted ? false : (options.policy.network.disabled ?? false),\n allowedDomains: adopted\n ? Array.isArray(network.allowedDomains)\n ? network.allowedDomains\n : []\n : options.policy.network.disabled\n ? ['*']\n : nonEmpty(options.policy.network.allowedDomains),\n deniedDomains: adopted\n ? Array.isArray(network.deniedDomains)\n ? network.deniedDomains\n : []\n : options.policy.network.disabled\n ? []\n : nonEmpty(options.policy.network.deniedDomains),\n },\n mandatoryDeny: {\n form: platform === 'linux' ? 'expanded-paths' : 'glob',\n entries: reproduceMandatoryDeny({\n cwd,\n allowGitConfig,\n platform,\n dotGitIsDirectory: dotGit,\n }),\n allowGitConfig,\n searchDepth,\n ...(platform === 'linux' ? { dotGitWasDirectory: dotGit } : {}),\n },\n })\n }\n // CAPTURE the derived baseline immediately after our initialize, or the live foreign baseline\n // when adopting. It is deliberately derived from SRT's getters, not from the requested policy.\n const derived: DerivedRules = derive()\n // PRIME THE GLOB MATCHER with upstream's own `globToRegex` for every rule in the derived lists.\n // This is the whole reason `srtEnforcer` is async at the right moment: the evaluator is synchronous\n // (it is consulted per path) while the upstream import is not, so unless priming happens HERE the\n // synchronous fallback becomes the only path that ever runs in production — making the plan's\n // \"deep-import the matcher, never port it\" rule true on paper and false in fact.\n await primeGlobMatcher([\n ...derived.read.denyOnly,\n ...derived.read.allowWithinDeny,\n ...derived.write.allowOnly,\n ...derived.write.denyWithinAllow,\n ...derived.mandatoryDeny.entries,\n ])\n const enforcer: SandboxPolicyEnforcer = {\n isSupported: () => manager.isSupportedPlatform(),\n adopted,\n checkDependencies: async () => manager.checkDependenciesAsync(),\n run: async (op) => {\n const mapped = mapPolicy(op.policy)\n const command = await quoteShellArgs(op.argv)\n const wrapped = await manager.wrapWithSandboxArgv(\n command,\n binShell,\n mapped,\n op.signal,\n op.cwd,\n { commandId: op.correlationId }\n )\n const child = spawn(wrapped.argv[0], wrapped.argv.slice(1), {\n cwd: op.cwd,\n // SRT's `wrapped.env` is the unchanged process environment, not sandbox plumbing. Merging it\n // here would defeat the allow-list by reintroducing every host variable. SRT's proxy, CA\n // bundle, and git safe-directory plumbing are encoded in `wrapped.argv` instead. The caller's\n // per-call environment remains the final overlay, by contract.\n env: { ...hostEnv, ...(op.env ?? {}) },\n stdio: ['ignore', 'pipe', 'pipe'],\n shell: false,\n })\n const completed = new Promise<{ exitCode: number; failed: boolean }>((settle) => {\n // 'error' MUST be handled, and not only so `completed` settles: an unhandled 'error' on a\n // ChildProcess is an uncaught exception that terminates the HOST process. A missing wrapper\n // binary or an unusable `cwd` would therefore kill the agent instead of failing one command.\n // Both listeners are `once` and the promise settles first-write-wins, so the pair is safe:\n // a spawn failure emits 'error' then 'close', and the later 'close' is discarded.\n child.once('error', () => settle({ exitCode: 1, failed: true }))\n child.once('close', (code) => settle({ exitCode: code ?? 1, failed: (code ?? 1) !== 0 }))\n })\n return {\n stdout: toWeb(child.stdout!),\n stderr: toWeb(child.stderr!),\n completed,\n }\n },\n // Owned sessions are immutable from this adapter's perspective and retain the cheap cached\n // snapshot. An adopted session must re-read SRT on every call: a foreign updateConfig() can widen\n // the live sandbox between invocations, and drift() must compare against that live state.\n effectivePolicy: () => (adopted ? derive() : derived),\n diagnosticsFor: (id) =>\n manager.getSandboxViolationStore().getViolationsForCommand(id).map(String),\n dispose: async () => {\n // Adoption is reported as a no-op. Resetting here would tear down ACEs owned by the host\n // application; only the manager we initialized may be reset.\n // ADOPTION IS A REPORTED NO-OP: resetting here would tear down ACEs owned by the host\n // application. Only a session we established may be reset.\n //\n // `selfInitialized` is deliberately NOT cleared. `reset()` leaves upstream's `config` set, so\n // `isSandboxingEnabled()` stays `true` forever after the first initialise — and clearing our\n // marker would make the NEXT construction see \"enabled, not ours\" and adopt a dead session,\n // silently enforcing the disposed policy. Ownership is a property of this process having\n // initialised at all, not of a session still being live.\n if (!adopted && manager.isSandboxingEnabled()) {\n await manager.reset()\n // Release the claim so a later construction may establish a new session. `selfInitialized`\n // deliberately stays true: upstream's `config` survives `reset()`, so clearing it would make\n // the next construction see \"enabled, not ours\" and adopt a dead session.\n selfSessionClaimed = false\n }\n },\n }\n return enforcer\n}\n\nexport { mapPolicy }\n","import { assertArgvValue, assertAllowedRipgrepFlag } from '../escape'\nimport type { HitFrame, PathFrame, SandboxPolicy } from '../types'\nimport type { SandboxPolicyEnforcer } from '../contracts/policy_enforcer'\n\n/**\n * A classified ripgrep outcome.\n *\n * @remarks\n * CLASSIFICATION ORDER IS LOAD-BEARING: diagnostics are checked BEFORE the exit status, because `rg`\n * runs under the sandbox and a denied path surfaces as a non-zero exit *plus* a violation record.\n * Triage generically first and a policy refusal is permanently mislabelled `io-failure` — the model\n * is told \"search broke\" when the truth is \"you may not read there\", and `denied-by-policy` becomes\n * unreachable despite being in the tool's outcome list.\n *\n * `no-matches` is a RESULT, not an error: `rg` exits 1 when it ran correctly and matched nothing.\n */\nexport type RipgrepFailure =\n | { kind: 'denied-by-policy'; diagnostics: readonly string[] }\n | { kind: 'invalid-pattern'; message: string }\n | { kind: 'no-matches' }\n | { kind: 'io-failure'; message: string; exitCode: number }\n\n/**\n * Convert a classified ripgrep failure into the error the searcher throws.\n *\n * @remarks\n * Exhaustive by construction: a new {@link RipgrepFailure} arm is a compile error here rather than\n * a silently generic message. `denied-by-policy` carries its diagnostics into the message — the\n * classification is checked BEFORE the generic exit-code triage precisely so a policy refusal is\n * not mislabelled `io-failure`, and flattening it back to a bare exit code here would discard the\n * only thing that makes that ordering worth having.\n *\n * @param failure - A classified failure other than `no-matches`, which is a result, not an error.\n * @returns The error to throw.\n */\nconst toRipgrepError = (failure: Exclude<RipgrepFailure, { kind: 'no-matches' }>): Error => {\n switch (failure.kind) {\n case 'denied-by-policy':\n return new Error(`denied-by-policy: ${failure.diagnostics.join('; ')}`)\n case 'invalid-pattern':\n return new Error(failure.message)\n case 'io-failure':\n return new Error(failure.message || `ripgrep exited ${failure.exitCode}`)\n }\n}\n\nconst bytes = new TextDecoder()\nconst collect = async (\n stream: ReadableStream<Uint8Array>,\n onText: (text: string) => void\n): Promise<void> => {\n const reader = stream.getReader()\n try {\n for (;;) {\n const next = await reader.read()\n if (next.done) return\n onText(bytes.decode(next.value, { stream: true }))\n }\n } finally {\n reader.releaseLock()\n }\n}\n\n/** Ripgrep backend. Both child pipes are drained immediately and concurrently. */\nexport const createRipgrepSearch = (\n enforcer: SandboxPolicyEnforcer,\n policy: SandboxPolicy\n): {\n searchContent(o: {\n root: string\n pattern: string\n maxDepth: number\n limit: number\n ignoreCase?: boolean\n literal?: boolean\n glob?: string\n iglob?: string\n follow?: boolean\n hidden?: boolean\n noIgnore?: boolean\n signal?: AbortSignal\n }): AsyncIterable<HitFrame>\n findPaths(o: {\n root: string\n glob: string\n maxDepth: number\n limit: number\n iglob?: string\n follow?: boolean\n hidden?: boolean\n noIgnore?: boolean\n signal?: AbortSignal\n }): AsyncIterable<PathFrame>\n} => {\n const run = async (argv: string[], cwd: string, signal?: AbortSignal) => {\n const correlationId = `${crypto.randomUUID()}-${Date.now().toString(36)}`\n const spawned = await enforcer.run({ argv, cwd, policy, correlationId, signal })\n let out = ''\n let err = ''\n const stdout = collect(spawned.stdout, (chunk) => {\n out += chunk\n })\n const stderr = collect(spawned.stderr, (chunk) => {\n err += chunk\n })\n const [, , completed] = await Promise.all([stdout, stderr, spawned.completed])\n const diagnostics = enforcer.diagnosticsFor(correlationId)\n const exitCode = completed.exitCode\n if (diagnostics.length > 0)\n return { failure: { kind: 'denied-by-policy', diagnostics } as RipgrepFailure, out, err }\n if (exitCode === 2 && err.startsWith('regex parse error'))\n return { failure: { kind: 'invalid-pattern', message: err } as RipgrepFailure, out, err }\n if (exitCode === 1) return { failure: { kind: 'no-matches' } as RipgrepFailure, out, err }\n if (exitCode !== 0)\n return { failure: { kind: 'io-failure', message: err, exitCode } as RipgrepFailure, out, err }\n return { failure: undefined, out, err }\n }\n /**\n * Defence-in-depth check that this adapter only ever emits flags it declared.\n *\n * @remarks\n * Scans the WHOLE argv rather than a fixed index window: the previous `slice(1, 5)` silently depended\n * on argv layout, so adding or reordering an argument moved the window off the flags. It also stops at\n * `--`, because everything after the terminator is a model-supplied VALUE — a pattern like `--foo` is\n * a legitimate search string there, not a flag, and `assertArgvValue` already owns that half.\n */\n const assertAdapterFlags = (argv: readonly string[]): void => {\n for (const arg of argv.slice(1)) {\n if (arg === '--') break\n if (arg.startsWith('--')) assertAllowedRipgrepFlag(arg)\n }\n }\n return {\n async *searchContent(o) {\n if (!Number.isInteger(o.limit) || o.limit < 1)\n throw new Error('limit must be a positive integer')\n if (o.follow)\n throw new Error('follow is refused: descendant symlink containment audit pending')\n const argv = [\n 'rg',\n '--json',\n ...(o.ignoreCase ? ['--ignore-case'] : []),\n ...(o.literal ? ['--fixed-strings'] : []),\n ...(o.glob ? ['--glob', assertArgvValue(o.glob)] : []),\n ...(o.iglob ? ['--iglob', assertArgvValue(o.iglob)] : []),\n ...(o.hidden ? ['--hidden'] : []),\n ...(o.noIgnore ? ['--no-ignore'] : []),\n '--max-depth',\n String(o.maxDepth),\n '--',\n assertArgvValue(o.pattern),\n assertArgvValue(o.root),\n ]\n assertAdapterFlags(argv)\n const result = await run(argv, o.root, o.signal)\n if (result.failure) {\n if (result.failure.kind === 'no-matches') {\n yield { kind: 'done', complete: true }\n return\n }\n throw toRipgrepError(result.failure)\n }\n let shown = 0\n let overLimit = false\n for (const line of result.out.split('\\n')) {\n if (!line) continue\n try {\n const item = JSON.parse(line) as {\n type: string\n data?: { path?: { text?: string }; line_number?: number; lines?: { text?: string } }\n }\n if (\n item.type === 'match' &&\n item.data?.path?.text &&\n item.data.line_number &&\n item.data.lines\n ) {\n if (shown >= o.limit) {\n overLimit = true\n break\n }\n yield {\n kind: 'item',\n path: item.data.path.text,\n line: item.data.line_number,\n text: item.data.lines.text ?? '',\n }\n shown++\n }\n } catch {\n /* malformed rg diagnostics are an I/O failure in the real adapter */\n }\n }\n yield overLimit\n ? { kind: 'done', complete: false, omitted: 'over-limit', bound: 'limit', shown }\n : { kind: 'done', complete: true }\n },\n async *findPaths(o) {\n if (!Number.isInteger(o.limit) || o.limit < 1)\n throw new Error('limit must be a positive integer')\n if (o.follow)\n throw new Error('follow is refused: descendant symlink containment audit pending')\n const argv = [\n 'rg',\n '--files',\n '--glob',\n assertArgvValue(o.glob),\n ...(o.iglob ? ['--iglob', assertArgvValue(o.iglob)] : []),\n ...(o.hidden ? ['--hidden'] : []),\n ...(o.noIgnore ? ['--no-ignore'] : []),\n '--max-depth',\n String(o.maxDepth),\n '--',\n assertArgvValue(o.root),\n ]\n assertAdapterFlags(argv)\n const result = await run(argv, o.root, o.signal)\n if (result.failure) {\n if (result.failure.kind === 'no-matches') {\n yield { kind: 'done', complete: true }\n return\n }\n throw toRipgrepError(result.failure)\n }\n let shown = 0\n let overLimit = false\n for (const path of result.out.split('\\n')) {\n if (!path) continue\n if (shown >= o.limit) {\n overLimit = true\n break\n }\n yield { kind: 'item', path }\n shown++\n }\n yield overLimit\n ? { kind: 'done', complete: false, omitted: 'over-limit', bound: 'limit', shown }\n : { kind: 'done', complete: true }\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"node.mjs","names":[],"sources":["../../../src/batteries/sandbox/node/srt_enforcer.ts","../../../src/batteries/sandbox/node/search_ripgrep.ts"],"sourcesContent":["import { statSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport { spawn } from 'node:child_process'\nimport { quoteShellArgs, validateAbsoluteBinaryPath, validateBinShell } from '../escape'\nimport { derivedRulesFromSrt, primeGlobMatcher, reproduceMandatoryDeny } from './fs_node'\nimport {\n E_INVALID_SANDBOX_CONFIG,\n E_SANDBOX_UNSUPPORTED_ENV,\n E_SANDBOX_POLICY_CONFLICT,\n} from '../exceptions'\nimport type { SandboxPolicy, DerivedRules } from '../types'\nimport type { SandboxPolicyEnforcer } from '../contracts/policy_enforcer'\n\ntype SrtManager = {\n initialize(config: SrtConfig): Promise<void>\n isSupportedPlatform(): boolean\n isSandboxingEnabled(): boolean\n checkDependenciesAsync(): Promise<{ errors: string[]; warnings: string[] }>\n wrapWithSandboxArgv(\n command: string,\n shell?: string,\n custom?: unknown,\n signal?: AbortSignal,\n cwd?: string,\n options?: { commandId?: string }\n ): Promise<{ argv: string[]; env: Record<string, string> }>\n getFsReadConfig(): { denyOnly: string[]; allowWithinDeny?: string[] }\n getFsWriteConfig(): { allowOnly: string[]; denyWithinAllow: string[] }\n getNetworkRestrictionConfig(): {\n allowedHosts?: string[]\n deniedHosts?: string[]\n }\n getConfig(): SrtConfig | undefined\n getSandboxViolationStore(): {\n getViolationsForCommand(command: string): unknown[]\n }\n reset(): Promise<void>\n}\ntype SrtConfig = {\n filesystem: Record<string, unknown>\n network?: Record<string, unknown>\n /** SRT's Linux mandatory-deny scan depth is session-level. */\n mandatoryDenySearchDepth?: number\n git?: { safeDirectories: string[] }\n /** Top-level and session-level: SRT reads this from the config `initialize()` stored, not per call. */\n enableWeakerNestedSandbox?: boolean\n bwrapPath?: string\n socatPath?: string\n}\n\nconst unsupported = (): never => {\n throw new E_SANDBOX_UNSUPPORTED_ENV([\n `SRT sandbox is unavailable on ${process.platform}; use WSL2 or the SES browser path`,\n ])\n}\nconst nonEmpty = (v: readonly string[] | undefined): string[] => [...(v ?? [])]\n\n/**\n * Host variables a sandboxed child inherits when the caller names no allow-list.\n *\n * @remarks\n * `PATH` ONLY, and it is the minimum rather than a convenience. The ripgrep searcher spawns `rg` by\n * BARE NAME, so with no `PATH` the child sees only the one a shell synthesises\n * (`/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:.`) — which does not contain `/opt/homebrew/bin` or a Nix\n * profile, so `search_files` would fail as `io-failure` on any such host and present as *\"the search\n * tool is broken\"* rather than as a configuration error.\n *\n * `PATH` is not a credential: a model that runs `env` learns where binaries live, not a secret. Every\n * genuinely sensitive variable — and `HOME`, `USER`, `TMPDIR`, which leak host layout — stays out.\n */\nconst DEFAULT_ENV_ALLOW_LIST: readonly string[] = ['PATH']\n\n/** POSIX environment-variable names. Anything else could not be exported by a shell anyway. */\nconst ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/\n\n/**\n * Resolve the host half of a sandboxed child's environment ONCE, at construction.\n *\n * @remarks\n * DENY BY DEFAULT. Before this existed the enforcer spread the entire `process.env` into every child,\n * so a model directing the shell's argv could run `env` and read the host's credentials straight back\n * into its own context — which no filesystem or network policy prevents, because the secret arrives in\n * the tool result rather than over the wire.\n *\n * The allow-list REPLACES this default rather than extending it; that is stated on the option, because\n * the opposite assumption is the natural one and would silently drop `PATH`.\n *\n * @param options - The enforcer's construction options.\n * @returns The host variables to seed the child's environment with.\n */\nconst resolveHostEnv = (options: SrtEnforcerOptions): Record<string, string> => {\n if (options.inheritHostEnv === true) {\n const all: Record<string, string> = {}\n for (const [name, value] of Object.entries(process.env))\n if (value !== undefined) all[name] = value\n return all\n }\n const names = options.envAllowList ?? DEFAULT_ENV_ALLOW_LIST\n const picked: Record<string, string> = {}\n for (const name of names) {\n if (!ENV_NAME.test(name))\n throw new E_INVALID_SANDBOX_CONFIG([\n `envAllowList entry ${JSON.stringify(name)} is not a valid environment variable name`,\n ])\n const value = process.env[name]\n if (value !== undefined) picked[name] = value\n }\n return picked\n}\n\n/**\n * Whether `<cwd>/.git` is a real DIRECTORY.\n *\n * @remarks\n * Load-bearing on Linux only: upstream pushes the workspace-root `.git/hooks` and `.git/config` deny\n * entries just when `.git` is a directory, because in a worktree it is a FILE and denying it would\n * make bubblewrap fail. Treating a worktree as a directory would deny what the profile permits.\n */\nconst dotGitIsDirectory = (cwd: string): boolean => {\n try {\n return statSync(resolve(cwd, '.git')).isDirectory()\n } catch {\n return false\n }\n}\n\n/**\n * The sole ADK-to-SRT translation point. Keep the upstream type behind this local firewall.\n *\n * @remarks\n * `runtime` carries ADAPTER-level settings that are deliberately absent from `SandboxPolicy`. That\n * type is ADK-owned, SRT-neutral vocabulary — naming an SRT feature in it would make the firewall\n * nominal and leave a non-SRT enforcer unable to implement the contract — so an SRT-specific switch\n * belongs on the adapter's own options, exactly as `binShell` already does.\n */\nconst mapPolicy = (\n policy: SandboxPolicy,\n runtime: {\n enableWeakerNestedSandbox?: boolean\n bwrapPath?: string\n socatPath?: string\n } = {}\n): SrtConfig => {\n const fs = policy.filesystem\n const net = policy.network\n if (\n net.disabled &&\n (net.deniedDomains?.length ||\n Object.keys(net.deniedDomainReasons ?? {}).length ||\n (net.allowedDomains && !(net.allowedDomains.length === 1 && net.allowedDomains[0] === '*')))\n )\n throw new E_INVALID_SANDBOX_CONFIG(['network.disabled contradicts network domain restrictions'])\n const allowedDomains = net.disabled ? ['*'] : nonEmpty(net.allowedDomains)\n const deniedDomains = net.disabled ? [] : nonEmpty(net.deniedDomains)\n const deniedDomainReasons = net.disabled ? {} : { ...(net.deniedDomainReasons ?? {}) }\n return {\n ...(fs.mandatoryDenySearchDepth !== undefined\n ? { mandatoryDenySearchDepth: fs.mandatoryDenySearchDepth }\n : {}),\n // Emitted only when enabled, so an untouched configuration is byte-identical to before this\n // option existed — a drift snapshot taken either side of the upgrade compares equal.\n ...(runtime.enableWeakerNestedSandbox === true ? { enableWeakerNestedSandbox: true } : {}),\n ...(runtime.bwrapPath !== undefined ? { bwrapPath: runtime.bwrapPath } : {}),\n ...(runtime.socatPath !== undefined ? { socatPath: runtime.socatPath } : {}),\n filesystem: {\n ...(fs.disabled ? { disabled: true } : {}),\n denyRead: nonEmpty(fs.denyRead),\n allowRead: nonEmpty(fs.allowRead),\n allowWrite: nonEmpty(fs.allowWrite),\n denyWrite: nonEmpty(fs.denyWrite),\n allowGitConfig: fs.allowGitConfig ?? false,\n },\n // SRT keys network restriction by presence of this object. There is no schema-valid allow-all\n // domain pattern, so disabled deliberately omits the key instead of emitting the rejected '*'.\n ...(!net.disabled\n ? {\n network: {\n allowedDomains,\n deniedDomains,\n deniedDomainReasons,\n strictAllowlist: true,\n allowLocalBinding: false,\n allowUnixSockets: [],\n allowMachLookup: [],\n },\n }\n : {}),\n git: {\n safeDirectories: nonEmpty(fs.gitSafeDirectories ?? [process.cwd()]),\n },\n }\n}\n\nconst toWeb = (stream: NodeJS.ReadableStream): ReadableStream<Uint8Array> =>\n (\n ReadableStream as unknown as {\n from?: (x: unknown) => ReadableStream<Uint8Array>\n }\n ).from\n ? (\n ReadableStream as unknown as {\n from: (x: unknown) => ReadableStream<Uint8Array>\n }\n ).from(stream)\n : new ReadableStream({\n start(controller) {\n stream.on('data', (x: Buffer) => controller.enqueue(new Uint8Array(x)))\n stream.on('end', () => controller.close())\n stream.on('error', (e) => controller.error(e))\n },\n })\n\n/** Options for {@link srtEnforcer}. */\n/**\n * Whether an enforcer in THIS process performed the `initialize()` that is currently in force.\n *\n * @remarks\n * UPSTREAM'S OWN FLAG CANNOT ANSWER THIS. `isSandboxingEnabled()` is literally `config !== undefined`\n * (`sandbox-manager.js:672-675`) and `reset()` clears `managerContext`, the proxies, the registries —\n * **but never `config`**. So after any reset the flag stays `true` forever, and an enforcer that\n * trusted it would \"adopt\" a sandbox that is no longer in force, silently reporting a stale policy\n * and refusing to re-initialise. Verified: initialize, reset, and `isSandboxingEnabled()` is still\n * `true`.\n *\n * We therefore track what WE established. `true` here means this module owns the live session, so a\n * subsequent construction must re-initialise rather than adopt. `false` with upstream reporting\n * enabled means somebody else got there first — the genuine adoption case.\n */\nlet selfInitialized = false\n\n/**\n * Whether the session THIS module established is still claimed by a live enforcer.\n *\n * @remarks\n * FIRST-WRITER-WINS IS ENFORCED HERE, not only in the manager. `manager.ts` admits or refuses a\n * second handle by comparing policies — but that check runs AFTER `srtEnforcer()` has already\n * constructed, and construction is what touches the process-global. So a reset-and-reinitialise in the\n * constructor would tear down a live policy before anything could refuse it: caller 2 widens the real\n * sandbox, the manager then throws, and caller 1 carries on against a baseline that is no longer in\n * force. The refusal would arrive after the damage.\n *\n * `claimed` is therefore set when we initialise and cleared only by `dispose()`. While it is `true`,\n * re-initialising is refused outright; once cleared, the next construction may re-establish a session,\n * which is what makes sequential owned handles work.\n */\nlet selfSessionClaimed = false\n\n/**\n * Relinquish this module's ownership marker.\n *\n * @remarks\n * TEST SEAM, and a narrow one. The marker exists because upstream's `isSandboxingEnabled()` is\n * `config !== undefined` and `reset()` never clears `config` — so after the first `initialize()` the\n * flag is permanently `true` and cannot by itself distinguish *\"a session we established\"* from\n * *\"somebody else's\"*. Ownership therefore persists for the life of the module, which is correct in\n * production (a process that initialised once keeps re-initialising its own session) but makes the\n * ADOPTION branch unreachable in a test file that has already constructed an owned enforcer.\n *\n * Production code has no reason to call this: relinquishing ownership while a session we established\n * is still live would make the next construction adopt it and refuse to re-initialise.\n */\nexport const releaseSrtOwnershipForTests = (): void => {\n selfInitialized = false\n selfSessionClaimed = false\n}\n\n/** Construction options for {@link srtEnforcer}. */\nexport type SrtEnforcerOptions = {\n /**\n * Absolute path to the POSIX shell used to invoke wrapped commands. Defaults to `/bin/bash`.\n *\n * @remarks\n * Validated at construction in TWO checks, both required: it must be ABSOLUTE (a bare `bash` passes\n * any basename test while remaining `PATH`-dependent — the exact hazard the absolute default\n * avoids), and its basename must be on the allow-list (`sh`, `bash`, `dash`, `zsh`, `ksh`) — the\n * shells whose quoting the single escaper is correct for. An allow-list rather than a deny-list is\n * deliberate: `fish` and `nu` are POSIX-ish enough to look safe and different enough to break\n * single-quote escaping, so an unverified shell must fail closed.\n */\n binShell?: string\n /**\n * The ADK-owned policy to enforce.\n *\n * @remarks\n * Mapped to SRT's config inside this module and nowhere else. The derived baseline is captured\n * immediately after `initialize()`, because `SandboxManager` is a process-global singleton whose\n * SECOND `initialize()` is a no-op — a later call with a different policy silently keeps the first.\n */\n policy: SandboxPolicy\n /**\n * Host environment variable NAMES a sandboxed child may inherit. Defaults to `['PATH']`.\n *\n * @remarks\n * The child inherits NOTHING from the host beyond these names. That default is deliberate: a model\n * that can direct the shell's argv can run `env`, so anything inherited is readable back into its\n * context — and no filesystem or network policy stops it, because the value arrives in the tool\n * result rather than over the wire.\n *\n * **This REPLACES the default, it does not extend it.** A caller who needs `CARGO_HOME` and still\n * wants binaries to resolve must pass BOTH: `['PATH', 'CARGO_HOME']`. Passing `['CARGO_HOME']` alone\n * drops `PATH`, which breaks `search_files` on any host where `rg` lives outside `/usr/bin`.\n *\n * An entry that is not a valid POSIX environment-variable name throws `E_INVALID_SANDBOX_CONFIG` at\n * construction rather than being skipped, so a typo surfaces as a startup error instead of a\n * variable that silently never arrives.\n */\n envAllowList?: readonly string[]\n /**\n * Pass the ENTIRE host environment to sandboxed children. Defaults to `false`.\n *\n * @remarks\n * The escape hatch for a deployment that genuinely needs ambient configuration, and it is worth\n * being blunt about what it re-opens: **every secret in the host process becomes readable by the\n * model**, because `run_shell_command` exists precisely to run commands the model chose and `env` is\n * one of them. Prefer naming what you need in `envAllowList`.\n */\n inheritHostEnv?: boolean\n /**\n * Absolute path to a Linux bubblewrap wrapper or binary. SRT reads it from the config passed to\n * `initialize()`; on the Linux launch path it becomes argv[0] of the bwrap command\n * (`linux-sandbox-utils.js:1539`). A wrapper script placed at this path therefore receives every\n * bwrap argument and can strip or rewrite options such as `--unshare-net`. On Linux a nonexistent\n * path fails in SRT's `initialize()`; on macOS it is neither validated nor used.\n */\n bwrapPath?: string\n /**\n * Absolute path to a Linux socat binary or wrapper. SRT passes it to `initializeLinuxNetworkBridge`\n * on Linux. A nonexistent path fails in SRT's `initialize()`; on macOS it is neither validated nor\n * used.\n */\n socatPath?: string\n /**\n * Enable SRT's weaker nested-sandbox mode, for running inside an unprivileged container.\n *\n * @remarks\n * Bubblewrap cannot mount a fresh `/proc` inside an unprivileged container, so the sandbox fails to\n * start at all — the symptom is `apply-seccomp: write /proc/self/uid_map: Operation not permitted`\n * with exit 1, an empty stdout and NO diagnostics, which is indistinguishable from a policy denial.\n * This flag makes the inner sandbox bind-mount the container's EXISTING `/proc` instead.\n *\n * **It considerably weakens the boundary**, in upstream's own words: the bind-mounted `/proc`\n * exposes process information a fresh mount would hide. Only enable it when the OUTER container\n * already provides the isolation you need — it trades inner isolation for the sandbox running at all.\n *\n * Session-level: SRT reads it from the config given to `initialize()`, never per call.\n */\n enableWeakerNestedSandbox?: boolean\n}\n\n/**\n * Construct the SRT-backed policy enforcer — the OS boundary.\n *\n * @remarks\n * REFUSES rather than degrades on an unsupported platform: native `win32` throws\n * `E_SANDBOX_UNSUPPORTED_ENV` naming WSL2, because Windows folds all four filesystem-policy lists\n * case-insensitively and throws on per-exec allows. A half-built branch there would be a DIVERGENT\n * boundary rather than a limited one, so there is deliberately no native-Windows evaluator.\n *\n * `run()` resolves on SPAWN with live streams plus a separate `completed` promise, never a settled\n * exit code. That split is not stylistic: one promise cannot both hand over unread child streams AND\n * carry an exit code, because settling requires the streams to have ended and they cannot end before\n * someone drains them. A caller MUST drain both concurrently — pipe buffers are per-fd, so draining\n * one to completion first can block the other and hang the child.\n *\n * @param options - Shell and policy configuration.\n * @returns An enforcer whose derived baseline is already captured.\n */\nexport const srtEnforcer = async (options: SrtEnforcerOptions): Promise<SandboxPolicyEnforcer> => {\n if (process.platform !== 'darwin' && process.platform !== 'linux') return unsupported()\n const binShell = validateBinShell(options.binShell)\n const bwrapPath = validateAbsoluteBinaryPath(options.bwrapPath, 'bwrapPath')\n const socatPath = validateAbsoluteBinaryPath(options.socatPath, 'socatPath')\n // Resolved ONCE: the allow-list is fixed for the enforcer's life, so re-picking per spawn would only\n // add a chance for the two to disagree. Validation throws here, at construction, so a typo'd variable\n // name is a startup error rather than a variable that silently never arrives.\n const hostEnv = resolveHostEnv(options)\n const srt = (await import('@anthropic-ai/sandbox-runtime')) as unknown as {\n SandboxManager: SrtManager\n }\n const manager = srt.SandboxManager\n // SandboxManager is process-global and first-writer-wins. Detect before initialize: an already\n // enabled manager belongs to somebody else, and initializing it (even with a different policy)\n // would leave this handle describing a sandbox that is not in force.\n // ADOPT only when the live session is somebody ELSE'S. See `selfInitialized`: upstream's flag is\n // sticky across `reset()`, so it alone would make every enforcer after the first one adopt.\n // CLAIM FIRST, CLASSIFY SECOND — and both synchronously, because the boundary is a process-global\n // with no lock to take. An earlier revision computed `adopted` from `isSandboxingEnabled()` and only\n // claimed after `initialize()` resolved, which left TWO windows for concurrent constructions:\n // · both read `claimed === false`, both proceeded, and the second re-initialised over the first;\n // · worse, the second saw `enabled === true` (the first had just initialised) with our marker not\n // yet set, so it classified a session WE established as FOREIGN and adopted it — reporting the\n // other caller's policy as though it were an adopted third-party sandbox.\n // Verified both: `Promise.all` of two enforcers resolved BOTH, the second with `adopted: true` and\n // the first's derived lists. Claiming before any await closes them together.\n const claimedByUs = selfSessionClaimed\n const alreadyLive = manager.isSandboxingEnabled()\n const adopted = alreadyLive && !selfInitialized && !claimedByUs\n if (!adopted) {\n // RESET BEFORE RE-INITIALISING OUR OWN SESSION. Upstream's `initialize()` assigns `config`\n // unconditionally, but the DERIVED getters are computed from the managerContext built during\n // initialisation — so calling it twice without a reset leaves the FIRST policy's derived lists in\n // force. Verified: init A, then init B, and `getFsReadConfig()` still reports A; reset then init C\n // reports C. Without this, a second `srtEnforcer()` in one process silently enforces the previous\n // policy while reporting the new one.\n //\n // Only ever our OWN session: `adopted` is false here, and `selfInitialized` proves we established\n // what is currently live rather than inheriting somebody else's.\n //\n // AND ONLY A RELEASED ONE. Resetting a session a live handle still holds would silently replace\n // that handle's policy with this one — first-writer-wins broken in the constructor, before the\n // manager's admission check can refuse anything. Refuse here instead, where nothing has changed\n // yet: the caller must dispose the existing handle before establishing a different policy.\n if (selfSessionClaimed) {\n throw new E_SANDBOX_POLICY_CONFLICT([\n 'a sandbox session established by this process is still live; dispose that handle before ' +\n 'constructing another enforcer, or reuse it — replacing it here would widen the live ' +\n 'policy before admission could refuse it',\n ])\n }\n // CLAIM SYNCHRONOUSLY, BEFORE THE FIRST AWAIT. Setting this after `initialize()` leaves a window\n // in which two concurrent constructions both read `false`, both proceed, and the second silently\n // re-initialises over the first — verified: `Promise.all` of two enforcers with different policies\n // resolved BOTH, and the second reported the first's derived lists. The check and the claim must be\n // one synchronous step, because there is no lock to take: the boundary is a process-global.\n selfSessionClaimed = true\n try {\n if (selfInitialized && manager.isSandboxingEnabled()) await manager.reset()\n // THE nested-sandbox flag MUST ride this call and only this one. SRT resolves it from the\n // module-level config `initialize()` stored (`getEnableWeakerNestedSandbox()` reads\n // `config?.enableWeakerNestedSandbox`), NOT from the per-call config handed to\n // `wrapWithSandboxArgv` — so passing it only at the `run()` site below would compile, ship, and\n // silently do nothing.\n await manager.initialize(\n mapPolicy(options.policy, {\n enableWeakerNestedSandbox: options.enableWeakerNestedSandbox,\n bwrapPath,\n socatPath,\n })\n )\n selfInitialized = true\n } catch (error) {\n // A failed establishment must not leave the claim held, or the process is permanently wedged\n // with no live session to dispose.\n selfSessionClaimed = false\n throw error\n }\n }\n\n const platform = process.platform === 'linux' ? 'linux' : 'darwin'\n const cwd = process.cwd()\n const derive = (): DerivedRules => {\n const config = manager.getConfig()\n const filesystem = config?.filesystem ?? {}\n const network = config?.network ?? {}\n const allowGitConfig = adopted\n ? Boolean(filesystem.allowGitConfig)\n : (options.policy.filesystem.allowGitConfig ?? false)\n const searchDepth = adopted\n ? (config?.mandatoryDenySearchDepth ?? 3)\n : (options.policy.filesystem.mandatoryDenySearchDepth ?? 3)\n const dotGit = dotGitIsDirectory(cwd)\n return derivedRulesFromSrt({\n platform,\n read: manager.getFsReadConfig(),\n write: manager.getFsWriteConfig(),\n filesystemDisabled: adopted\n ? Boolean(filesystem.disabled)\n : options.policy.filesystem.disabled,\n // In adoption mode these are literal foreign lists. In particular, ['*'] is not evidence\n // that the foreign consumer used ADK's disabled mode.\n network: {\n // ADOPTION ⇒ always `false`: we did not construct this session, so \"we were constructed in\n // disabled mode\" cannot be true of it. OWNED ⇒ the ADK policy's own value, which is what makes\n // the drift SKIP branch and the `false → true` widening check implementable.\n disabled: adopted ? false : (options.policy.network.disabled ?? false),\n allowedDomains: adopted\n ? Array.isArray(network.allowedDomains)\n ? network.allowedDomains\n : []\n : options.policy.network.disabled\n ? undefined\n : nonEmpty(options.policy.network.allowedDomains),\n deniedDomains: adopted\n ? Array.isArray(network.deniedDomains)\n ? network.deniedDomains\n : []\n : options.policy.network.disabled\n ? []\n : nonEmpty(options.policy.network.deniedDomains),\n },\n mandatoryDeny: {\n form: platform === 'linux' ? 'expanded-paths' : 'glob',\n entries: reproduceMandatoryDeny({\n cwd,\n allowGitConfig,\n platform,\n dotGitIsDirectory: dotGit,\n }),\n allowGitConfig,\n searchDepth,\n ...(platform === 'linux' ? { dotGitWasDirectory: dotGit } : {}),\n },\n })\n }\n // CAPTURE the derived baseline immediately after our initialize, or the live foreign baseline\n // when adopting. It is deliberately derived from SRT's getters, not from the requested policy.\n const derived: DerivedRules = derive()\n // PRIME THE GLOB MATCHER with upstream's own `globToRegex` for every rule in the derived lists.\n // This is the whole reason `srtEnforcer` is async at the right moment: the evaluator is synchronous\n // (it is consulted per path) while the upstream import is not, so unless priming happens HERE the\n // synchronous fallback becomes the only path that ever runs in production — making the plan's\n // \"deep-import the matcher, never port it\" rule true on paper and false in fact.\n await primeGlobMatcher([\n ...derived.read.denyOnly,\n ...derived.read.allowWithinDeny,\n ...derived.write.allowOnly,\n ...derived.write.denyWithinAllow,\n ...derived.mandatoryDeny.entries,\n ])\n const enforcer: SandboxPolicyEnforcer = {\n isSupported: () => manager.isSupportedPlatform(),\n adopted,\n checkDependencies: async () => manager.checkDependenciesAsync(),\n run: async (op) => {\n const mapped = mapPolicy(op.policy)\n const command = await quoteShellArgs(op.argv)\n const wrapped = await manager.wrapWithSandboxArgv(\n command,\n binShell,\n mapped,\n op.signal,\n op.cwd,\n { commandId: op.correlationId }\n )\n const child = spawn(wrapped.argv[0], wrapped.argv.slice(1), {\n cwd: op.cwd,\n // SRT's `wrapped.env` is the unchanged process environment, not sandbox plumbing. Merging it\n // here would defeat the allow-list by reintroducing every host variable. SRT's proxy, CA\n // bundle, and git safe-directory plumbing are encoded in `wrapped.argv` instead. The caller's\n // per-call environment remains the final overlay, by contract.\n env: { ...hostEnv, ...(op.env ?? {}) },\n stdio: ['ignore', 'pipe', 'pipe'],\n shell: false,\n detached: true,\n })\n let terminate: (() => void) | undefined\n if (op.signal) {\n terminate = () => {\n try {\n if (child.pid) process.kill(-child.pid, 'SIGKILL')\n else child.kill('SIGKILL')\n } catch {\n child.kill('SIGKILL')\n }\n }\n if (op.signal.aborted) terminate()\n else op.signal.addEventListener('abort', terminate, { once: true })\n }\n const cleanup = () => {\n if (terminate && op.signal) op.signal.removeEventListener('abort', terminate)\n terminate = undefined\n }\n const completed = new Promise<{ exitCode: number; failed: boolean }>((settle) => {\n // 'error' MUST be handled, and not only so `completed` settles: an unhandled 'error' on a\n // ChildProcess is an uncaught exception that terminates the HOST process. A missing wrapper\n // binary or an unusable `cwd` would therefore kill the agent instead of failing one command.\n // Both listeners are `once` and the promise settles first-write-wins, so the pair is safe:\n // a spawn failure emits 'error' then 'close', and the later 'close' is discarded.\n child.once('error', () => {\n cleanup()\n settle({ exitCode: 1, failed: true })\n })\n child.once('close', (code) => {\n cleanup()\n settle({ exitCode: code ?? 1, failed: (code ?? 1) !== 0 })\n })\n })\n return {\n stdout: toWeb(child.stdout!),\n stderr: toWeb(child.stderr!),\n completed,\n }\n },\n // Owned sessions are immutable from this adapter's perspective and retain the cheap cached\n // snapshot. An adopted session must re-read SRT on every call: a foreign updateConfig() can widen\n // the live sandbox between invocations, and drift() must compare against that live state.\n effectivePolicy: () => (adopted ? derive() : derived),\n diagnosticsFor: (id) =>\n manager.getSandboxViolationStore().getViolationsForCommand(id).map(String),\n dispose: async () => {\n // Adoption is reported as a no-op. Resetting here would tear down ACEs owned by the host\n // application; only the manager we initialized may be reset.\n // ADOPTION IS A REPORTED NO-OP: resetting here would tear down ACEs owned by the host\n // application. Only a session we established may be reset.\n //\n // `selfInitialized` is deliberately NOT cleared. `reset()` leaves upstream's `config` set, so\n // `isSandboxingEnabled()` stays `true` forever after the first initialise — and clearing our\n // marker would make the NEXT construction see \"enabled, not ours\" and adopt a dead session,\n // silently enforcing the disposed policy. Ownership is a property of this process having\n // initialised at all, not of a session still being live.\n if (!adopted && manager.isSandboxingEnabled()) {\n await manager.reset()\n // Release the claim so a later construction may establish a new session. `selfInitialized`\n // deliberately stays true: upstream's `config` survives `reset()`, so clearing it would make\n // the next construction see \"enabled, not ours\" and adopt a dead session.\n selfSessionClaimed = false\n }\n },\n }\n return enforcer\n}\n\nexport { mapPolicy }\n","import { assertArgvValue, assertAllowedRipgrepFlag } from '../escape'\nimport type { HitFrame, PathFrame, SandboxPolicy } from '../types'\nimport type { SandboxPolicyEnforcer } from '../contracts/policy_enforcer'\n\n/**\n * A classified ripgrep outcome.\n *\n * @remarks\n * CLASSIFICATION ORDER IS LOAD-BEARING: diagnostics are checked BEFORE the exit status, because `rg`\n * runs under the sandbox and a denied path surfaces as a non-zero exit *plus* a violation record.\n * Triage generically first and a policy refusal is permanently mislabelled `io-failure` — the model\n * is told \"search broke\" when the truth is \"you may not read there\", and `denied-by-policy` becomes\n * unreachable despite being in the tool's outcome list.\n *\n * `no-matches` is a RESULT, not an error: `rg` exits 1 when it ran correctly and matched nothing.\n */\nexport type RipgrepFailure =\n | { kind: 'denied-by-policy'; diagnostics: readonly string[] }\n | { kind: 'invalid-pattern'; message: string }\n | { kind: 'no-matches' }\n | { kind: 'io-failure'; message: string; exitCode: number }\n\n/**\n * Convert a classified ripgrep failure into the error the searcher throws.\n *\n * @remarks\n * Exhaustive by construction: a new {@link RipgrepFailure} arm is a compile error here rather than\n * a silently generic message. `denied-by-policy` carries its diagnostics into the message — the\n * classification is checked BEFORE the generic exit-code triage precisely so a policy refusal is\n * not mislabelled `io-failure`, and flattening it back to a bare exit code here would discard the\n * only thing that makes that ordering worth having.\n *\n * @param failure - A classified failure other than `no-matches`, which is a result, not an error.\n * @returns The error to throw.\n */\nconst toRipgrepError = (failure: Exclude<RipgrepFailure, { kind: 'no-matches' }>): Error => {\n switch (failure.kind) {\n case 'denied-by-policy':\n return new Error(`denied-by-policy: ${failure.diagnostics.join('; ')}`)\n case 'invalid-pattern':\n return new Error(failure.message)\n case 'io-failure':\n return new Error(failure.message || `ripgrep exited ${failure.exitCode}`)\n }\n}\n\nconst bytes = new TextDecoder()\nconst collect = async (\n stream: ReadableStream<Uint8Array>,\n onText: (text: string) => void\n): Promise<void> => {\n const reader = stream.getReader()\n try {\n for (;;) {\n const next = await reader.read()\n if (next.done) return\n onText(bytes.decode(next.value, { stream: true }))\n }\n } finally {\n reader.releaseLock()\n }\n}\n\n/** Ripgrep backend. Both child pipes are drained immediately and concurrently. */\nexport const createRipgrepSearch = (\n enforcer: SandboxPolicyEnforcer,\n policy: SandboxPolicy\n): {\n searchContent(o: {\n root: string\n pattern: string\n maxDepth: number\n limit: number\n ignoreCase?: boolean\n literal?: boolean\n glob?: string\n iglob?: string\n follow?: boolean\n hidden?: boolean\n noIgnore?: boolean\n signal?: AbortSignal\n }): AsyncIterable<HitFrame>\n findPaths(o: {\n root: string\n glob: string\n maxDepth: number\n limit: number\n iglob?: string\n follow?: boolean\n hidden?: boolean\n noIgnore?: boolean\n signal?: AbortSignal\n }): AsyncIterable<PathFrame>\n} => {\n const run = async (argv: string[], cwd: string, signal?: AbortSignal) => {\n const correlationId = `${crypto.randomUUID()}-${Date.now().toString(36)}`\n const spawned = await enforcer.run({ argv, cwd, policy, correlationId, signal })\n let out = ''\n let err = ''\n const stdout = collect(spawned.stdout, (chunk) => {\n out += chunk\n })\n const stderr = collect(spawned.stderr, (chunk) => {\n err += chunk\n })\n const [, , completed] = await Promise.all([stdout, stderr, spawned.completed])\n const diagnostics = enforcer.diagnosticsFor(correlationId)\n const exitCode = completed.exitCode\n if (diagnostics.length > 0)\n return { failure: { kind: 'denied-by-policy', diagnostics } as RipgrepFailure, out, err }\n if (exitCode === 2 && err.startsWith('regex parse error'))\n return { failure: { kind: 'invalid-pattern', message: err } as RipgrepFailure, out, err }\n if (exitCode === 1) return { failure: { kind: 'no-matches' } as RipgrepFailure, out, err }\n if (exitCode !== 0)\n return { failure: { kind: 'io-failure', message: err, exitCode } as RipgrepFailure, out, err }\n return { failure: undefined, out, err }\n }\n /**\n * Defence-in-depth check that this adapter only ever emits flags it declared.\n *\n * @remarks\n * Scans the WHOLE argv rather than a fixed index window: the previous `slice(1, 5)` silently depended\n * on argv layout, so adding or reordering an argument moved the window off the flags. It also stops at\n * `--`, because everything after the terminator is a model-supplied VALUE — a pattern like `--foo` is\n * a legitimate search string there, not a flag, and `assertArgvValue` already owns that half.\n */\n const assertAdapterFlags = (argv: readonly string[]): void => {\n for (const arg of argv.slice(1)) {\n if (arg === '--') break\n if (arg.startsWith('--')) assertAllowedRipgrepFlag(arg)\n }\n }\n return {\n async *searchContent(o) {\n if (!Number.isInteger(o.limit) || o.limit < 1)\n throw new Error('limit must be a positive integer')\n if (o.follow)\n throw new Error('follow is refused: descendant symlink containment audit pending')\n const argv = [\n 'rg',\n '--json',\n ...(o.ignoreCase ? ['--ignore-case'] : []),\n ...(o.literal ? ['--fixed-strings'] : []),\n ...(o.glob ? ['--glob', assertArgvValue(o.glob)] : []),\n ...(o.iglob ? ['--iglob', assertArgvValue(o.iglob)] : []),\n ...(o.hidden ? ['--hidden'] : []),\n ...(o.noIgnore ? ['--no-ignore'] : []),\n '--max-depth',\n String(o.maxDepth),\n '--',\n assertArgvValue(o.pattern),\n assertArgvValue(o.root),\n ]\n assertAdapterFlags(argv)\n const result = await run(argv, o.root, o.signal)\n if (result.failure) {\n if (result.failure.kind === 'no-matches') {\n yield { kind: 'done', complete: true }\n return\n }\n throw toRipgrepError(result.failure)\n }\n let shown = 0\n let overLimit = false\n for (const line of result.out.split('\\n')) {\n if (!line) continue\n try {\n const item = JSON.parse(line) as {\n type: string\n data?: { path?: { text?: string }; line_number?: number; lines?: { text?: string } }\n }\n if (\n item.type === 'match' &&\n item.data?.path?.text &&\n item.data.line_number &&\n item.data.lines\n ) {\n if (shown >= o.limit) {\n overLimit = true\n break\n }\n yield {\n kind: 'item',\n path: item.data.path.text,\n line: item.data.line_number,\n text: item.data.lines.text ?? '',\n }\n shown++\n }\n } catch {\n /* malformed rg diagnostics are an I/O failure in the real adapter */\n }\n }\n yield overLimit\n ? { kind: 'done', complete: false, omitted: 'over-limit', bound: 'limit', shown }\n : { kind: 'done', complete: true }\n },\n async *findPaths(o) {\n if (!Number.isInteger(o.limit) || o.limit < 1)\n throw new Error('limit must be a positive integer')\n if (o.follow)\n throw new Error('follow is refused: descendant symlink containment audit pending')\n const argv = [\n 'rg',\n '--files',\n '--glob',\n assertArgvValue(o.glob),\n ...(o.iglob ? ['--iglob', assertArgvValue(o.iglob)] : []),\n ...(o.hidden ? ['--hidden'] : []),\n ...(o.noIgnore ? ['--no-ignore'] : []),\n '--max-depth',\n String(o.maxDepth),\n '--',\n assertArgvValue(o.root),\n ]\n assertAdapterFlags(argv)\n const result = await run(argv, o.root, o.signal)\n if (result.failure) {\n if (result.failure.kind === 'no-matches') {\n yield { kind: 'done', complete: true }\n return\n }\n throw toRipgrepError(result.failure)\n }\n let shown = 0\n let overLimit = false\n for (const path of result.out.split('\\n')) {\n if (!path) continue\n if (shown >= o.limit) {\n overLimit = true\n break\n }\n yield { kind: 'item', path }\n shown++\n }\n yield overLimit\n ? { kind: 'done', complete: false, omitted: 'over-limit', bound: 'limit', shown }\n : { kind: 'done', complete: true }\n },\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("../../chunk-Cek0wNdY.js");
|
|
3
|
-
const require_tools = require("../../tools-
|
|
3
|
+
const require_tools = require("../../tools-CqOcmHR8.js");
|
|
4
4
|
exports.createSandboxTools = require_tools.createSandboxTools;
|
|
5
5
|
exports.forgeSandboxTools = require_tools.forgeSandboxTools;
|
|
6
6
|
exports.narratingPath = require_tools.narratingPath;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as narratingPath, n as descriptions, r as forgeSandboxTools, t as createSandboxTools } from "../../tools-
|
|
1
|
+
import { i as narratingPath, n as descriptions, r as forgeSandboxTools, t as createSandboxTools } from "../../tools-B3_8C78Q.mjs";
|
|
2
2
|
export { createSandboxTools, forgeSandboxTools, narratingPath, descriptions as sandboxToolDescriptions };
|
|
@@ -46,8 +46,17 @@ export interface SandboxPolicy {
|
|
|
46
46
|
/** Git safe directories passed to spawned children. */ readonly gitSafeDirectories?: readonly string[];
|
|
47
47
|
/** Linux mandatory-deny scan depth. */ readonly mandatoryDenySearchDepth?: number;
|
|
48
48
|
};
|
|
49
|
-
/**
|
|
49
|
+
/**
|
|
50
|
+
* Network rules. An absent allow list means deny all unless disabled.
|
|
51
|
+
*
|
|
52
|
+
* @remarks
|
|
53
|
+
* `disabled` means that SRT's network configuration key is omitted entirely. SRT decides whether
|
|
54
|
+
* to create a network namespace from the presence of that key; its `NetworkConfigSchema` has no
|
|
55
|
+
* schema-valid allow-all domain pattern (`"*"` is explicitly rejected). Consequently disabled
|
|
56
|
+
* means no network namespace restriction, rather than an allow-all domain entry.
|
|
57
|
+
*/
|
|
50
58
|
readonly network: {
|
|
59
|
+
/** Omit SRT's network key, avoiding `--unshare-net`, when true. */
|
|
51
60
|
readonly disabled?: boolean;
|
|
52
61
|
readonly allowedDomains?: readonly string[];
|
|
53
62
|
readonly deniedDomains?: readonly string[];
|