@galaxyproject/orbit 0.0.0 → 0.9.0
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 +123 -0
- package/LICENSE +21 -0
- package/README.md +760 -5
- package/bin/legacy-mcp-config.js +363 -0
- package/bin/orbit.js +810 -0
- package/bin/pi-extension-path.d.ts +4 -0
- package/bin/pi-extension-path.js +44 -0
- package/bin/provider-auth.d.ts +22 -0
- package/bin/provider-auth.js +77 -0
- package/bin/thinking-pref.js +38 -0
- package/bin/update-check.d.ts +15 -0
- package/bin/update-check.js +137 -0
- package/extensions/cli-update/index.ts +32 -0
- package/extensions/orbit/activity-hooks.ts +141 -0
- package/extensions/orbit/activity.ts +116 -0
- package/extensions/orbit/agent-dir.ts +11 -0
- package/extensions/orbit/auto-resume.ts +352 -0
- package/extensions/orbit/config.ts +3 -0
- package/extensions/orbit/confusables.ts +1 -0
- package/extensions/orbit/context.ts +957 -0
- package/extensions/orbit/dashboard-commands.ts +191 -0
- package/extensions/orbit/dashboard-store.ts +473 -0
- package/extensions/orbit/dashboard-tools.ts +1185 -0
- package/extensions/orbit/empty-completion.ts +99 -0
- package/extensions/orbit/evidence-gate.ts +1276 -0
- package/extensions/orbit/evidence-override-command.ts +294 -0
- package/extensions/orbit/exec-guard/bash-risk.ts +1408 -0
- package/extensions/orbit/exec-guard/gate.ts +181 -0
- package/extensions/orbit/exec-guard/guardian-config.ts +54 -0
- package/extensions/orbit/exec-guard/index.ts +1 -0
- package/extensions/orbit/exec-guard/model-tier.ts +36 -0
- package/extensions/orbit/exec-guard/path-jail.ts +54 -0
- package/extensions/orbit/exec-guard/policy.ts +342 -0
- package/extensions/orbit/exec-guard/runtime-state.ts +15 -0
- package/extensions/orbit/exec-guard/sensitive-read.ts +525 -0
- package/extensions/orbit/exec-guard/types.ts +42 -0
- package/extensions/orbit/execution-commands.ts +134 -0
- package/extensions/orbit/feedback-command.ts +81 -0
- package/extensions/orbit/feedback.ts +96 -0
- package/extensions/orbit/galaxy-api.test.ts +122 -0
- package/extensions/orbit/galaxy-api.ts +606 -0
- package/extensions/orbit/galaxy-artifact-link-guidance.ts +5 -0
- package/extensions/orbit/galaxy-destructive-gate.ts +113 -0
- package/extensions/orbit/galaxy-enrich.ts +1108 -0
- package/extensions/orbit/galaxy-failure.ts +101 -0
- package/extensions/orbit/galaxy-fixture.ts +126 -0
- package/extensions/orbit/galaxy-history-guard.ts +233 -0
- package/extensions/orbit/galaxy-job-block.ts +347 -0
- package/extensions/orbit/galaxy-job-ops.ts +288 -0
- package/extensions/orbit/galaxy-live-source.ts +929 -0
- package/extensions/orbit/galaxy-markdown-adapter.ts +248 -0
- package/extensions/orbit/galaxy-op-policy.ts +332 -0
- package/extensions/orbit/galaxy-ops-tools.ts +326 -0
- package/extensions/orbit/galaxy-output-store.ts +80 -0
- package/extensions/orbit/galaxy-output.ts +346 -0
- package/extensions/orbit/galaxy-page-binding.ts +222 -0
- package/extensions/orbit/galaxy-page-markdown-guidance.ts +15 -0
- package/extensions/orbit/galaxy-page-ops.ts +159 -0
- package/extensions/orbit/galaxy-page-sync.test.ts +417 -0
- package/extensions/orbit/galaxy-page-sync.ts +318 -0
- package/extensions/orbit/galaxy-pages-api.ts +203 -0
- package/extensions/orbit/galaxy-pages-sync.ts +409 -0
- package/extensions/orbit/galaxy-poll-guard.ts +189 -0
- package/extensions/orbit/galaxy-poller.ts +706 -0
- package/extensions/orbit/galaxy-provenance.ts +623 -0
- package/extensions/orbit/galaxy-reconcile.ts +956 -0
- package/extensions/orbit/galaxy-recovery.ts +176 -0
- package/extensions/orbit/galaxy-repeat-guard.ts +55 -0
- package/extensions/orbit/galaxy-submission-capture.ts +707 -0
- package/extensions/orbit/galaxy-submission.ts +502 -0
- package/extensions/orbit/galaxy-tool-source.ts +78 -0
- package/extensions/orbit/galaxy-tool-wrap.ts +234 -0
- package/extensions/orbit/galaxy-udt-block.ts +127 -0
- package/extensions/orbit/galaxy-upload-tus.ts +334 -0
- package/extensions/orbit/galaxy-upload.ts +300 -0
- package/extensions/orbit/galaxy-version-capture.ts +65 -0
- package/extensions/orbit/git.ts +173 -0
- package/extensions/orbit/harness-block-fields.ts +597 -0
- package/extensions/orbit/index.ts +593 -0
- package/extensions/orbit/init-gate.ts +284 -0
- package/extensions/orbit/instructions-command.ts +136 -0
- package/extensions/orbit/invocation-failure-hint.ts +57 -0
- package/extensions/orbit/iwc-plan-check.ts +94 -0
- package/extensions/orbit/lesson-command.ts +303 -0
- package/extensions/orbit/lesson-hint.ts +245 -0
- package/extensions/orbit/lesson-nudge.ts +106 -0
- package/extensions/orbit/lessons/compose.ts +177 -0
- package/extensions/orbit/lessons/counters.ts +120 -0
- package/extensions/orbit/lessons/enabled.ts +116 -0
- package/extensions/orbit/lessons/matcher.ts +346 -0
- package/extensions/orbit/lessons/paths.ts +286 -0
- package/extensions/orbit/lessons/pi-event-contract.ts +105 -0
- package/extensions/orbit/lessons/propose-replay.ts +103 -0
- package/extensions/orbit/lessons/propose-tool.ts +222 -0
- package/extensions/orbit/lessons/propose.ts +502 -0
- package/extensions/orbit/lessons/replay.ts +89 -0
- package/extensions/orbit/lessons/reproduction-index.ts +83 -0
- package/extensions/orbit/lessons/search-tool.ts +99 -0
- package/extensions/orbit/lessons/search.ts +136 -0
- package/extensions/orbit/lessons/store.ts +415 -0
- package/extensions/orbit/lessons/text.ts +36 -0
- package/extensions/orbit/lessons/types.ts +69 -0
- package/extensions/orbit/lessons/user-dir.ts +24 -0
- package/extensions/orbit/lessons/user-lesson.ts +88 -0
- package/extensions/orbit/lessons/wrapper.ts +61 -0
- package/extensions/orbit/lessons-command.ts +147 -0
- package/extensions/orbit/local-exec.ts +39 -0
- package/extensions/orbit/mcp-output.ts +179 -0
- package/extensions/orbit/mcp-servers.test.ts +24 -0
- package/extensions/orbit/mcp-servers.ts +25 -0
- package/extensions/orbit/model-script.ts +351 -0
- package/extensions/orbit/notebook-anchors.ts +342 -0
- package/extensions/orbit/notebook-writer.ts +863 -0
- package/extensions/orbit/observation-allowlists.ts +1109 -0
- package/extensions/orbit/observation-replay.ts +148 -0
- package/extensions/orbit/observation-triggers.ts +563 -0
- package/extensions/orbit/observation-ui.ts +175 -0
- package/extensions/orbit/observations-command.ts +421 -0
- package/extensions/orbit/observations-config.ts +200 -0
- package/extensions/orbit/observations.ts +1381 -0
- package/extensions/orbit/profiles.ts +270 -0
- package/extensions/orbit/progress-updates.ts +95 -0
- package/extensions/orbit/proposal-commands.ts +882 -0
- package/extensions/orbit/provenance-fs-file.ts +59 -0
- package/extensions/orbit/provenance-fs.ts +32 -0
- package/extensions/orbit/provenance-memory-fs.ts +59 -0
- package/extensions/orbit/record-store-file.ts +177 -0
- package/extensions/orbit/record-store-page.ts +77 -0
- package/extensions/orbit/record-store-seam.ts +47 -0
- package/extensions/orbit/record-store.ts +149 -0
- package/extensions/orbit/registry-carrier.ts +107 -0
- package/extensions/orbit/registry-eligibility.ts +95 -0
- package/extensions/orbit/registry-evaluation.ts +403 -0
- package/extensions/orbit/registry-evaluator.ts +519 -0
- package/extensions/orbit/registry-import.ts +91 -0
- package/extensions/orbit/registry-lock.ts +221 -0
- package/extensions/orbit/registry-memory-fs.ts +42 -0
- package/extensions/orbit/registry-page-carrier.ts +103 -0
- package/extensions/orbit/registry-proposal.ts +1348 -0
- package/extensions/orbit/registry-runtime.ts +234 -0
- package/extensions/orbit/registry-schema.ts +735 -0
- package/extensions/orbit/registry-submitter.ts +59 -0
- package/extensions/orbit/registry-templates.ts +346 -0
- package/extensions/orbit/registry.ts +476 -0
- package/extensions/orbit/sandbox/index.ts +120 -0
- package/extensions/orbit/sandbox/sandbox-bash.ts +93 -0
- package/extensions/orbit/sandbox/sandbox-config.ts +87 -0
- package/extensions/orbit/sandbox/sandbox-init-message.ts +28 -0
- package/extensions/orbit/secret-redaction.ts +247 -0
- package/extensions/orbit/session-index/cwd.ts +18 -0
- package/extensions/orbit/session-index/db.ts +56 -0
- package/extensions/orbit/session-index/indexer.ts +154 -0
- package/extensions/orbit/session-index/is-enabled.ts +17 -0
- package/extensions/orbit/session-index/parse.ts +254 -0
- package/extensions/orbit/session-index/query.ts +209 -0
- package/extensions/orbit/session-index/schema.ts +72 -0
- package/extensions/orbit/session-index/tools.ts +152 -0
- package/extensions/orbit/session-lifecycle.ts +321 -0
- package/extensions/orbit/skill-triggers.ts +159 -0
- package/extensions/orbit/skills-command.ts +48 -0
- package/extensions/orbit/skills-discovery.ts +435 -0
- package/extensions/orbit/skills.ts +84 -0
- package/extensions/orbit/sra-import-gate.ts +77 -0
- package/extensions/orbit/state.ts +265 -0
- package/extensions/orbit/submission-replay.ts +147 -0
- package/extensions/orbit/sync-command.ts +157 -0
- package/extensions/orbit/teams/critic-parser.ts +93 -0
- package/extensions/orbit/teams/dispatcher.ts +210 -0
- package/extensions/orbit/teams/is-enabled.ts +22 -0
- package/extensions/orbit/teams/tool.ts +187 -0
- package/extensions/orbit/teams/types.ts +57 -0
- package/extensions/orbit/teams/validate.ts +42 -0
- package/extensions/orbit/tester-id-command.ts +133 -0
- package/extensions/orbit/tools-sync.ts +279 -0
- package/extensions/orbit/tools.ts +1300 -0
- package/extensions/orbit/types.ts +41 -0
- package/extensions/orbit/ui-bridge.ts +55 -0
- package/extensions/orbit/ulid.ts +60 -0
- package/extensions/orbit/user-instructions.ts +388 -0
- package/extensions/orbit/vendor/skills/_catalog.json +68 -0
- package/extensions/orbit/vendor/skills/_manifest.json +1106 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/SKILL.md +112 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/_provenance.json +218 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/_required_tools.json +33 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/_verify.json +4 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/add.json +50 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/draft-extract.json +51 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/draft-next-step.json +42 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/draft-validate.json +140 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/list.json +32 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/notes/galaxy-tool-job-failure-reference.md +137 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/notes/open-requirements-ledger.md +173 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/schemas/galaxy-tool-summary.schema.json +175 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/schemas/galaxy-workflow-draft.schema.json +2727 -0
- package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/SKILL.md +82 -0
- package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/_provenance.json +133 -0
- package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/_required_tools.json +16 -0
- package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/_verify.json +4 -0
- package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/references/cli/draft-validate.json +140 -0
- package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/references/notes/galaxy-workflow-draft-format.md +135 -0
- package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/references/notes/open-requirements-ledger.md +173 -0
- package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/references/schemas/galaxy-workflow-draft.schema.json +2727 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/SKILL.md +84 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/_provenance.json +166 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/_verify.json +29 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/galaxy-workflow-testability-design.md +136 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/iwc-shortcuts-anti-patterns.md +357 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/iwc-test-data-conventions.md +308 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/planemo-asserts-idioms.md +246 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/planemo-workflow-test-architecture.md +151 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/schemas/galaxy-workflow-test-plan.schema.json +552 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/schemas/summary-galaxy-workflow.schema.json +687 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/schemas/tests-format.schema.json +7328 -0
- package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/SKILL.md +127 -0
- package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/_provenance.json +198 -0
- package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/_required_tools.json +16 -0
- package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/_verify.json +4 -0
- package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/cli/convert.json +123 -0
- package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/notes/galaxy-data-flow-draft-contract.md +129 -0
- package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/notes/iwc-shortcuts-anti-patterns.md +357 -0
- package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/notes/iwc-test-data-conventions.md +308 -0
- package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/notes/open-requirements-ledger.md +173 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/SKILL.md +73 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/_provenance.json +160 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/_verify.json +4 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/galaxy-collection-semantics.md +43 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/galaxy-collection-semantics.yml +1250 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/galaxy-tool-job-failure-reference.md +137 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/galaxy-workflow-invocation-failure-reference.md +138 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/iwc-shortcuts-anti-patterns.md +357 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/nextflow-operators-to-galaxy-collection-recipes.md +98 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/planemo-asserts-idioms.md +246 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/planemo-workflow-test-architecture.md +151 -0
- package/extensions/orbit/vendor/skills/discover-shed-tool/SKILL.md +174 -0
- package/extensions/orbit/vendor/skills/discover-shed-tool/_provenance.json +130 -0
- package/extensions/orbit/vendor/skills/discover-shed-tool/_required_tools.json +18 -0
- package/extensions/orbit/vendor/skills/discover-shed-tool/_verify.json +17 -0
- package/extensions/orbit/vendor/skills/discover-shed-tool/references/cli/tool-revisions.json +48 -0
- package/extensions/orbit/vendor/skills/discover-shed-tool/references/cli/tool-search.json +95 -0
- package/extensions/orbit/vendor/skills/discover-shed-tool/references/cli/tool-versions.json +39 -0
- package/extensions/orbit/vendor/skills/discover-shed-tool/references/notes/component-tool-shed-search.md +230 -0
- package/extensions/orbit/vendor/skills/discover-shed-tool/references/schemas/galaxy-tool-discovery.schema.json +231 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/SKILL.md +93 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/_provenance.json +201 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/_required_tools.json +28 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/_verify.json +17 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/cli/planemo.md +44 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/cli/validate-tests.json +40 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/galaxy-workflow-testability-design.md +136 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/iwc-shortcuts-anti-patterns.md +357 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/iwc-test-data-conventions.md +308 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/planemo-asserts-idioms.md +246 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/planemo-workflow-test-architecture.md +151 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/schemas/galaxy-workflow-test-plan.schema.json +552 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/schemas/tests-format.schema.json +7328 -0
- package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/SKILL.md +81 -0
- package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/_provenance.json +93 -0
- package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/_verify.json +17 -0
- package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/references/notes/open-requirements-ledger.md +173 -0
- package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/references/schemas/summary-galaxy-workflow.schema.json +687 -0
- package/extensions/orbit/vendor/skills/pipeline-update-interview-to-galaxy/SKILL.md +81 -0
- package/extensions/orbit/vendor/skills/pipeline-update-interview-to-galaxy/_assembly.json +23 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/SKILL.md +79 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/_provenance.json +125 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/_required_tools.json +28 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/_verify.json +4 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/references/cli/planemo.md +44 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/references/cli/validate-tests.json +40 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/references/notes/galaxy-workflow-invocation-failure-reference.md +138 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/references/notes/planemo-asserts-idioms.md +246 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/references/notes/planemo-workflow-test-architecture.md +151 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/references/schemas/tests-format.schema.json +7328 -0
- package/extensions/orbit/vendor/skills/skills/LICENSE +21 -0
- package/extensions/orbit/vendor/skills/skills/collection-manipulation/README.md +39 -0
- package/extensions/orbit/vendor/skills/skills/collection-manipulation/SKILL.md +892 -0
- package/extensions/orbit/vendor/skills/skills/collection-manipulation/references/api-patterns.md +927 -0
- package/extensions/orbit/vendor/skills/skills/collection-manipulation/references/apply-rules.md +1482 -0
- package/extensions/orbit/vendor/skills/skills/collection-manipulation/references/test-patterns.md +996 -0
- package/extensions/orbit/vendor/skills/skills/collection-manipulation/references/tools.md +609 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-data-libraries/SKILL.md +170 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/README.md +162 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/SKILL.md +99 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/examples/README.md +119 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/examples/tool-checking.md +419 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/examples/workflow-testing.md +617 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/galaxy-integration.md +614 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/SKILL.md +144 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/examples/average_col3.ipynb +68 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/examples/extract_sample_metadata.ipynb +190 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/examples/variant_annotation.ipynb +816 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/examples/vcp_variant_map.ipynb +443 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/scripts/README.md +165 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/scripts/galaxy_tool_checker.py +521 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-mcp-reference/SKILL.md +128 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-mcp-reference/gotchas.md +135 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-mcp-reference/history-access.md +132 -0
- package/extensions/orbit/vendor/skills/skills/reproduciblify/SKILL.md +146 -0
- package/extensions/orbit/vendor/skills/skills/reproduciblify/references/directives.yml +496 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/SKILL.md +245 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/01-minimal-echo.yml +27 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/02-single-data-in-out.yml +27 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/03-multiple-data-js.yml +28 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/04-select-integer-boolean.yml +41 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/05-resource-requirements.yml +25 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/06-biocontainer-tool.yml +38 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/07-inline-python-script.yml +56 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/08-configfile-script.yml +74 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/references/common-mistakes.md +76 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/references/schema-reference.md +155 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/references/templating.md +196 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/scripts/validate.py +109 -0
- package/extensions/orbit/vendor/skills/skills/workflow-reports/README.md +71 -0
- package/extensions/orbit/vendor/skills/skills/workflow-reports/SKILL.md +190 -0
- package/extensions/orbit/vendor/skills/skills/workflow-reports/examples/histology-staining.md +145 -0
- package/extensions/orbit/vendor/skills/skills/workflow-reports/examples/tissue-microarray-analysis.md +176 -0
- package/extensions/orbit/vendor/skills/skills/workflow-reports/references/directives.md +415 -0
- package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/SKILL.md +98 -0
- package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/_provenance.json +93 -0
- package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/_required_tools.json +28 -0
- package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/_verify.json +17 -0
- package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/references/cli/convert.json +123 -0
- package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/references/cli/foundry.md +25 -0
- package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/references/cli/validate.json +142 -0
- package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/references/schemas/summary-galaxy-workflow.schema.json +687 -0
- package/extensions/orbit/vendor/skills/validate-galaxy-workflow/SKILL.md +74 -0
- package/extensions/orbit/vendor/skills/validate-galaxy-workflow/_provenance.json +71 -0
- package/extensions/orbit/vendor/skills/validate-galaxy-workflow/_required_tools.json +16 -0
- package/extensions/orbit/vendor/skills/validate-galaxy-workflow/_verify.json +4 -0
- package/extensions/orbit/vendor/skills/validate-galaxy-workflow/references/cli/validate.json +142 -0
- package/extensions/orbit/vendor/skills/validate-galaxy-workflow/references/notes/galaxy-workflow-invocation-failure-reference.md +138 -0
- package/extensions/orbit/vendor/skills/validate-galaxy-workflow/references/notes/galaxy-workflow-testability-design.md +136 -0
- package/extensions/orbit/vendor-skills.ts +283 -0
- package/extensions/orbit/workspace-state-dir.ts +46 -0
- package/extensions/orbit-handoff/index.ts +73 -0
- package/extensions/orbit-handoff/orbit-launcher.ts +142 -0
- package/extensions/whats-new/index.ts +98 -0
- package/lessons/LICENSE +17 -0
- package/lessons/README.md +117 -0
- package/lessons/SCHEMA.md +187 -0
- package/lessons/build-snapshot.mjs +311 -0
- package/lessons/data/downloaded-file-is-not-what-its-extension-says.md +61 -0
- package/lessons/galaxy-api/403-history-is-a-hard-stop.md +57 -0
- package/lessons/galaxy-api/collection-into-single-dataset-input.md +61 -0
- package/lessons/galaxy-api/connectedvalue-in-command-line.md +62 -0
- package/lessons/galaxy-api/hid-is-not-an-id.md +59 -0
- package/lessons/galaxy-api/invoke-workflow-inputs-not-params.md +60 -0
- package/lessons/galaxy-api/repeat-param-pipe-keys.md +63 -0
- package/lessons/galaxy-api/run-tool-returns-on-submit.md +61 -0
- package/lessons/galaxy-tools/reference-index-not-on-server.md +62 -0
- package/lessons/log.md +42 -0
- package/lessons/reproduction/input-population-mismatch.md +59 -0
- package/lessons/reproduction/methods-text-vs-executed-parameters.md +62 -0
- package/lessons/snapshot.json +1004 -0
- package/lessons/stats/de-contrast-direction-and-sample-labels.md +63 -0
- package/lessons/stats/na-coerced-to-zero-in-filters.md +59 -0
- package/lessons/validate.mjs +1219 -0
- package/package.json +103 -5
- package/shared/agent-prompt.d.ts +8 -0
- package/shared/agent-prompt.js +15 -0
- package/shared/approval-prompt.d.ts +4 -0
- package/shared/approval-prompt.js +61 -0
- package/shared/brain-env.d.ts +14 -0
- package/shared/brain-env.js +108 -0
- package/shared/brain-exit.d.ts +20 -0
- package/shared/brain-exit.js +149 -0
- package/shared/capability-profile.d.ts +16 -0
- package/shared/capability-profile.js +75 -0
- package/shared/config-source.d.ts +13 -0
- package/shared/config-source.js +17 -0
- package/shared/confusables.d.ts +3 -0
- package/shared/confusables.js +93 -0
- package/shared/custom-provider.d.ts +45 -0
- package/shared/custom-provider.js +169 -0
- package/shared/dashboard-contract.d.ts +123 -0
- package/shared/dashboard-contract.js +560 -0
- package/shared/dashboard-layout-store.d.ts +44 -0
- package/shared/dashboard-layout-store.js +281 -0
- package/shared/feedback-contract.d.ts +61 -0
- package/shared/feedback-contract.js +146 -0
- package/shared/galaxy-artifact-links.d.ts +30 -0
- package/shared/galaxy-artifact-links.js +166 -0
- package/shared/galaxy-credentials.d.ts +26 -0
- package/shared/galaxy-credentials.js +71 -0
- package/shared/galaxy-destructive.d.ts +21 -0
- package/shared/galaxy-destructive.js +269 -0
- package/shared/galaxy-guidance.d.ts +2 -0
- package/shared/galaxy-guidance.js +53 -0
- package/shared/galaxy-live-contract.d.ts +92 -0
- package/shared/galaxy-live-contract.js +180 -0
- package/shared/galaxy-page-cells.d.ts +2 -0
- package/shared/galaxy-page-cells.js +22 -0
- package/shared/galaxy-tool-family.d.ts +5 -0
- package/shared/galaxy-tool-family.js +27 -0
- package/shared/galaxy-url.d.ts +1 -0
- package/shared/galaxy-url.js +48 -0
- package/shared/gtn.d.ts +31 -0
- package/shared/gtn.js +253 -0
- package/shared/handled-prompt.d.ts +3 -0
- package/shared/handled-prompt.js +26 -0
- package/shared/known-provider.d.ts +11 -0
- package/shared/known-provider.js +89 -0
- package/shared/lesson-hint-marker.d.ts +7 -0
- package/shared/lesson-hint-marker.js +32 -0
- package/shared/lesson-rules.d.ts +150 -0
- package/shared/lesson-rules.js +203 -0
- package/shared/malformed-object-ids.d.ts +1 -0
- package/shared/malformed-object-ids.js +37 -0
- package/shared/markdown-sections.d.ts +10 -0
- package/shared/markdown-sections.js +118 -0
- package/shared/notebook-fences.d.ts +12 -0
- package/shared/notebook-fences.js +53 -0
- package/shared/observation-contract.d.ts +91 -0
- package/shared/observation-contract.js +544 -0
- package/shared/orbit-config.d.ts +226 -0
- package/shared/orbit-config.js +189 -0
- package/shared/orbit-env.d.ts +13 -0
- package/shared/orbit-env.js +124 -0
- package/shared/orbit-shell-contract.d.ts +74 -0
- package/shared/orbit-shell-contract.js +30 -0
- package/shared/page-content-hash.d.ts +1 -0
- package/shared/page-content-hash.js +13 -0
- package/shared/pi-default-model.d.ts +7 -0
- package/shared/pi-default-model.js +41 -0
- package/shared/prompt-blocks.d.ts +87 -0
- package/shared/prompt-blocks.js +898 -0
- package/shared/provider-auth-caps.d.ts +22 -0
- package/shared/provider-auth-caps.js +57 -0
- package/shared/random.d.ts +2 -0
- package/shared/random.js +15 -0
- package/shared/record-excerpt.d.ts +4 -0
- package/shared/record-excerpt.js +16 -0
- package/shared/redirect-guard.d.ts +36 -0
- package/shared/redirect-guard.js +235 -0
- package/shared/skills-pin.d.ts +8 -0
- package/shared/skills-pin.js +9 -0
- package/shared/skills-router.d.ts +17 -0
- package/shared/skills-router.js +63 -0
- package/shared/sra-import.d.ts +34 -0
- package/shared/sra-import.js +206 -0
- package/shared/state-dir.d.ts +21 -0
- package/shared/state-dir.js +240 -0
- package/shared/team-dispatch-contract.d.ts +28 -0
- package/shared/team-dispatch-contract.js +1 -0
- package/shared/truncate-middle.d.ts +11 -0
- package/shared/truncate-middle.js +68 -0
- package/shared/version-compare.d.ts +10 -0
- package/shared/version-compare.js +74 -0
- package/shared/whats-new.d.ts +31 -0
- package/shared/whats-new.js +83 -0
- package/shared/wsl.d.ts +10 -0
- package/shared/wsl.js +24 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import * as os from "os";
|
|
3
|
+
import * as path from "path";
|
|
4
|
+
import { getNotebookPath } from "../state";
|
|
5
|
+
import { appendActivityEvent } from "../activity";
|
|
6
|
+
import { redactArgs } from "../activity-hooks";
|
|
7
|
+
import {
|
|
8
|
+
loadGuardianConfig,
|
|
9
|
+
resolveBypass,
|
|
10
|
+
trustWorkspace,
|
|
11
|
+
recordConsent,
|
|
12
|
+
} from "./guardian-config";
|
|
13
|
+
import { createPathResolver } from "./path-jail";
|
|
14
|
+
import { classifyModelTier } from "./model-tier";
|
|
15
|
+
import { decide } from "./policy";
|
|
16
|
+
import { CONSENT_VERSION, type PolicyResult } from "./types";
|
|
17
|
+
import { buildApprovalPrompt } from "../../../shared/approval-prompt.js";
|
|
18
|
+
|
|
19
|
+
// In-memory "allow for this session" set, keyed by tool + raw input signature.
|
|
20
|
+
const sessionAllow = new Set<string>();
|
|
21
|
+
function sig(toolName: string, input: Record<string, unknown>): string {
|
|
22
|
+
return toolName + ":" + JSON.stringify(input);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function audit(
|
|
26
|
+
toolName: string,
|
|
27
|
+
input: Record<string, unknown>,
|
|
28
|
+
result: PolicyResult,
|
|
29
|
+
outcome: string,
|
|
30
|
+
): void {
|
|
31
|
+
const nb = getNotebookPath();
|
|
32
|
+
if (!nb) return;
|
|
33
|
+
appendActivityEvent(path.dirname(nb), {
|
|
34
|
+
timestamp: new Date().toISOString(),
|
|
35
|
+
kind: "guard.decision",
|
|
36
|
+
source: "exec-guard",
|
|
37
|
+
payload: {
|
|
38
|
+
toolName,
|
|
39
|
+
decision: result.decision,
|
|
40
|
+
category: result.category,
|
|
41
|
+
reason: result.reason,
|
|
42
|
+
outcome,
|
|
43
|
+
args: redactArgs(toolName, input),
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// The state dir (.loom/.orbit) is deliberately not a root of its own. Under
|
|
49
|
+
// cwd it is already inside the jail; the only thing a separate root ever added
|
|
50
|
+
// was trust in wherever a symlinked state dir pointed, which the user never
|
|
51
|
+
// granted.
|
|
52
|
+
export function workspaceRoots(cwd: string, extra: string[]): string[] {
|
|
53
|
+
return [cwd, os.tmpdir(), ...extra];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function registerExecGuard(pi: ExtensionAPI): void {
|
|
57
|
+
pi.on("tool_call", async (event, ctx) => {
|
|
58
|
+
const config = loadGuardianConfig();
|
|
59
|
+
if (!config.enabled) return; // gate fully disabled (advanced escape hatch)
|
|
60
|
+
// Fold the effective bypass back into the config the pure engine sees.
|
|
61
|
+
config.dangerouslyBypassPermissions = resolveBypass(config);
|
|
62
|
+
|
|
63
|
+
const input = event.input as Record<string, unknown>;
|
|
64
|
+
const cwd = ctx.cwd;
|
|
65
|
+
const resolver = createPathResolver(
|
|
66
|
+
workspaceRoots(cwd, config.extraWorkspaceRoots),
|
|
67
|
+
os.homedir(),
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
let result: PolicyResult;
|
|
71
|
+
try {
|
|
72
|
+
result = decide(
|
|
73
|
+
{
|
|
74
|
+
toolName: event.toolName,
|
|
75
|
+
toolInput: input,
|
|
76
|
+
modelTier: classifyModelTier(ctx.model),
|
|
77
|
+
config,
|
|
78
|
+
interactive: ctx.hasUI,
|
|
79
|
+
cwd,
|
|
80
|
+
},
|
|
81
|
+
{ resolver, home: os.homedir() },
|
|
82
|
+
);
|
|
83
|
+
} catch {
|
|
84
|
+
const reason = "exec-guard internal error; failing closed";
|
|
85
|
+
audit(event.toolName, input, { decision: "deny", category: "error", reason }, "blocked");
|
|
86
|
+
return { block: true, reason };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (result.decision === "allow") {
|
|
90
|
+
audit(event.toolName, input, result, "allowed");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (result.decision === "deny") {
|
|
94
|
+
audit(event.toolName, input, result, "blocked");
|
|
95
|
+
return { block: true, reason: result.reason };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Destructive Galaxy op (#338): its own yes/no confirm, never cached and never
|
|
99
|
+
// offered "allow for session"/"trust workspace" -- every irreversible action
|
|
100
|
+
// re-prompts. Independent of the one-time local-exec consent disclosure (that's
|
|
101
|
+
// about shell execution; this is its own, more pointed prompt). The decision here
|
|
102
|
+
// is always "ask" -- the non-interactive case already denied above.
|
|
103
|
+
if (result.category === "galaxy:destructive") {
|
|
104
|
+
const proceed = await ctx.ui.confirm(
|
|
105
|
+
"Confirm destructive Galaxy operation",
|
|
106
|
+
`${ctx.model?.id ?? "The model"} wants to: ${result.reason}\n\nContinue?`,
|
|
107
|
+
{},
|
|
108
|
+
);
|
|
109
|
+
if (proceed) {
|
|
110
|
+
audit(event.toolName, input, result, "allowed:destructive-confirm");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
audit(event.toolName, input, result, "blocked:destructive-user");
|
|
114
|
+
return { block: true, reason: "Destructive Galaxy operation cancelled by user." };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ask: session memory first.
|
|
118
|
+
if (sessionAllow.has(sig(event.toolName, input))) {
|
|
119
|
+
audit(event.toolName, input, result, "allowed:session");
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// One-time local-execution disclosure on the first gated action. Relies on
|
|
124
|
+
// the persisted flag (saveConfig is synchronous), so a failed save simply
|
|
125
|
+
// re-discloses rather than silently proceeding.
|
|
126
|
+
if (!config.consentAcknowledged) {
|
|
127
|
+
const ok = await ctx.ui.confirm(
|
|
128
|
+
"Orbit runs actions on your computer",
|
|
129
|
+
"Orbit can run shell commands and read/write files as you. Commands from the AI are gated, but no gate is perfect -- only use it in workspaces you trust. Continue?",
|
|
130
|
+
{},
|
|
131
|
+
);
|
|
132
|
+
if (!ok) {
|
|
133
|
+
audit(event.toolName, input, result, "blocked:consent-declined");
|
|
134
|
+
return { block: true, reason: "Local execution not consented." };
|
|
135
|
+
}
|
|
136
|
+
recordConsent(CONSENT_VERSION);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const modelName = ctx.model?.id ?? "the model";
|
|
140
|
+
// Heading and the thing being approved are separated by a blank line so a
|
|
141
|
+
// shell can render the command as a readable body instead of a headline.
|
|
142
|
+
// Lowercased to match the policy engine, which gates on the normalized
|
|
143
|
+
// name -- otherwise a "Bash" call would be gated as a command but prompted
|
|
144
|
+
// as a path, showing the user nothing of what they're approving.
|
|
145
|
+
const isBash = event.toolName.toLowerCase() === "bash";
|
|
146
|
+
const heading = isBash
|
|
147
|
+
? `Allow ${modelName} to run this command?`
|
|
148
|
+
: `Allow ${modelName} to ${event.toolName} this path?`;
|
|
149
|
+
// File tools also arrive with the Anthropic `file_path` spelling and the policy
|
|
150
|
+
// layer gates both, so the prompt names every path the call carries: answering
|
|
151
|
+
// it while looking at one of two different targets is not consent.
|
|
152
|
+
const paths = [...new Set([input.path, input.file_path].filter((v) => typeof v === "string"))];
|
|
153
|
+
const detail = String(isBash ? (input.command ?? "") : paths.join(" + "));
|
|
154
|
+
const choice = await ctx.ui.select(
|
|
155
|
+
buildApprovalPrompt(heading, detail),
|
|
156
|
+
[
|
|
157
|
+
"Allow once",
|
|
158
|
+
"Allow for this session",
|
|
159
|
+
"Trust this workspace (stop asking for routine commands)",
|
|
160
|
+
"Deny",
|
|
161
|
+
],
|
|
162
|
+
{},
|
|
163
|
+
);
|
|
164
|
+
if (choice === "Allow once") {
|
|
165
|
+
audit(event.toolName, input, result, "allowed:once");
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (choice === "Allow for this session") {
|
|
169
|
+
sessionAllow.add(sig(event.toolName, input));
|
|
170
|
+
audit(event.toolName, input, result, "allowed:session");
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (choice && choice.startsWith("Trust this workspace")) {
|
|
174
|
+
trustWorkspace(cwd);
|
|
175
|
+
audit(event.toolName, input, result, "allowed:trust-workspace");
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
audit(event.toolName, input, result, "blocked:user");
|
|
179
|
+
return { block: true, reason: "Denied by user." };
|
|
180
|
+
});
|
|
181
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { loadConfig, saveConfig } from "../../../shared/orbit-config.js";
|
|
2
|
+
import type { GuardianConfig } from "./types";
|
|
3
|
+
import { readEnv } from "../../../shared/orbit-env.js";
|
|
4
|
+
|
|
5
|
+
export function loadGuardianConfig(): GuardianConfig {
|
|
6
|
+
const g = loadConfig().guardian ?? {};
|
|
7
|
+
return {
|
|
8
|
+
enabled: g.enabled !== false,
|
|
9
|
+
dangerouslyBypassPermissions: g.dangerouslyBypassPermissions === true,
|
|
10
|
+
trustedWorkspaces: g.trustedWorkspaces ?? [],
|
|
11
|
+
extraWorkspaceRoots: g.extraWorkspaceRoots ?? [],
|
|
12
|
+
consentAcknowledged: g.consentAcknowledged ?? null,
|
|
13
|
+
sandbox: g.sandbox === true,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Bypass is ON if (env flag OR config) AND NOT force-off. The agent can never
|
|
19
|
+
* flip the env or config: those are human-only channels (the gate makes
|
|
20
|
+
* writing ~/.orbit/config.json gated and editing guardian.* catastrophic).
|
|
21
|
+
*/
|
|
22
|
+
export function resolveBypass(cfg: GuardianConfig): boolean {
|
|
23
|
+
if (readEnv("SAFE") === "1") return false;
|
|
24
|
+
if (readEnv("DANGEROUSLY_BYPASS_PERMISSIONS") === "1") return true;
|
|
25
|
+
return cfg.dangerouslyBypassPermissions === true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The OS bash sandbox is opt-in: enabling it necessarily restricts bash network
|
|
30
|
+
* (ASRT cannot confine writes alone), so it's a deliberate user choice, not a
|
|
31
|
+
* default. On via LOOM_SANDBOX=1 or guardian.sandbox. Default off.
|
|
32
|
+
*/
|
|
33
|
+
export function resolveSandbox(cfg: GuardianConfig): boolean {
|
|
34
|
+
if (readEnv("SANDBOX") === "1") return true;
|
|
35
|
+
return cfg.sandbox === true;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function trustWorkspace(dir: string): void {
|
|
39
|
+
const cfg = loadConfig();
|
|
40
|
+
const g = cfg.guardian ?? {};
|
|
41
|
+
const set = new Set(g.trustedWorkspaces ?? []);
|
|
42
|
+
set.add(dir);
|
|
43
|
+
cfg.guardian = { ...g, trustedWorkspaces: [...set] };
|
|
44
|
+
saveConfig(cfg);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function recordConsent(version: string): void {
|
|
48
|
+
const cfg = loadConfig();
|
|
49
|
+
cfg.guardian = {
|
|
50
|
+
...(cfg.guardian ?? {}),
|
|
51
|
+
consentAcknowledged: { version, at: new Date().toISOString() },
|
|
52
|
+
};
|
|
53
|
+
saveConfig(cfg);
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { registerExecGuard } from "./gate";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ModelTier } from "./types";
|
|
2
|
+
|
|
3
|
+
export interface TierInput {
|
|
4
|
+
id?: string;
|
|
5
|
+
provider?: string;
|
|
6
|
+
cost?: { input: number; output: number };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Tokens that mark a model as weak/cheap regardless of cost. Matched as whole
|
|
10
|
+
// dash/dot-delimited tokens of the lowercased id (NOT substrings) so "mini"
|
|
11
|
+
// flags gpt-4o-mini but not gemini. Conservative: when unsure -> weak.
|
|
12
|
+
const WEAK_ID_MARKERS = ["haiku", "mini", "flash", "small", "lite", "8b", "7b", "1b", "3b", "nano"];
|
|
13
|
+
|
|
14
|
+
// Frontier id markers that are trusted even if a custom price table is wrong.
|
|
15
|
+
const TRUSTED_ID_MARKERS = [
|
|
16
|
+
"opus",
|
|
17
|
+
"sonnet",
|
|
18
|
+
"gpt-5",
|
|
19
|
+
"gpt-4.1",
|
|
20
|
+
"o1",
|
|
21
|
+
"o3",
|
|
22
|
+
"gemini-2.5-pro",
|
|
23
|
+
"gemini-1.5-pro",
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
export function classifyModelTier(model: TierInput | undefined): ModelTier {
|
|
27
|
+
if (!model || !model.id) return "weak";
|
|
28
|
+
const id = model.id.toLowerCase();
|
|
29
|
+
const idTokens = id.split(/[^a-z0-9]+/);
|
|
30
|
+
if (idTokens.some((tok) => WEAK_ID_MARKERS.includes(tok))) return "weak";
|
|
31
|
+
if (TRUSTED_ID_MARKERS.some((m) => id.includes(m))) return "trusted";
|
|
32
|
+
// Fall back to price: frontier output pricing is well above cheap tiers.
|
|
33
|
+
// Threshold chosen so Haiku ($5 out) is weak and Sonnet ($15 out) is trusted.
|
|
34
|
+
const out = model.cost?.output ?? 0;
|
|
35
|
+
return out >= 10 ? "trusted" : "weak";
|
|
36
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
import type { PathResolver } from "./types";
|
|
4
|
+
|
|
5
|
+
// Expand a leading `~` or `$HOME`/`${HOME}` to the home dir. The shell does this
|
|
6
|
+
// in bash, and pi resolves `~` in its file tools, but `path.resolve` does not --
|
|
7
|
+
// so without this a `~/.aws/config` read resolves to `<cwd>/~/.aws/config` and
|
|
8
|
+
// dodges both the sensitive-read floor and the workspace jail.
|
|
9
|
+
export function expandHome(p: string, home: string): string {
|
|
10
|
+
if (!home) return p;
|
|
11
|
+
if (p === "~" || p === "~/") return home;
|
|
12
|
+
if (p.startsWith("~/")) return path.join(home, p.slice(2));
|
|
13
|
+
const m = p.match(/^\$\{?HOME\}?(?=\/|$)/);
|
|
14
|
+
if (m) return path.join(home, p.slice(m[0].length));
|
|
15
|
+
return p;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function realpathDeepest(target: string): string {
|
|
19
|
+
// realpath the longest existing prefix, then re-append the missing tail.
|
|
20
|
+
let cur = path.resolve(target);
|
|
21
|
+
const tail: string[] = [];
|
|
22
|
+
for (;;) {
|
|
23
|
+
try {
|
|
24
|
+
return path.join(fs.realpathSync(cur), ...tail.reverse());
|
|
25
|
+
} catch {
|
|
26
|
+
const parent = path.dirname(cur);
|
|
27
|
+
if (parent === cur) return path.resolve(target); // hit the root, nothing exists
|
|
28
|
+
tail.push(path.basename(cur));
|
|
29
|
+
cur = parent;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function createPathResolver(roots: string[], home = ""): PathResolver {
|
|
35
|
+
const realRoots = roots
|
|
36
|
+
.map((r) => {
|
|
37
|
+
const expanded = expandHome(r, home);
|
|
38
|
+
try {
|
|
39
|
+
return fs.realpathSync(expanded);
|
|
40
|
+
} catch {
|
|
41
|
+
return path.resolve(expanded);
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
.filter(Boolean);
|
|
45
|
+
return {
|
|
46
|
+
contains(targetPath: string) {
|
|
47
|
+
const resolved = realpathDeepest(expandHome(targetPath, home));
|
|
48
|
+
const inside = realRoots.some(
|
|
49
|
+
(root) => resolved === root || resolved.startsWith(root + path.sep),
|
|
50
|
+
);
|
|
51
|
+
return { resolved, inside };
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import { classifyBash } from "./bash-risk";
|
|
3
|
+
import { expandHome } from "./path-jail";
|
|
4
|
+
import {
|
|
5
|
+
isSensitivePath,
|
|
6
|
+
isCredentialStore,
|
|
7
|
+
isProtectedWritePath,
|
|
8
|
+
isOrbitStatePath,
|
|
9
|
+
isRecordStorePath,
|
|
10
|
+
} from "./sensitive-read";
|
|
11
|
+
import type { PolicyDeps, PolicyRequest, PolicyResult } from "./types";
|
|
12
|
+
import {
|
|
13
|
+
classifyGalaxyDestructive,
|
|
14
|
+
describeGalaxyDestructive,
|
|
15
|
+
isGalaxyDestructiveCurl,
|
|
16
|
+
} from "../../../shared/galaxy-destructive.js";
|
|
17
|
+
import type { GalaxyDestructiveOp } from "../../../shared/galaxy-destructive.js";
|
|
18
|
+
|
|
19
|
+
// A dedicated credential store's CONTENTS are never readable by the agent --
|
|
20
|
+
// denied for every tier, not downgraded to an ask. This is the floor that closes
|
|
21
|
+
// #183: a capable model that gets to read ~/.orbit/config.json echoes the keys
|
|
22
|
+
// straight into the provider's request logs. Approval can't override it.
|
|
23
|
+
function denyCredentialStore(p: string, home: string): PolicyResult {
|
|
24
|
+
return {
|
|
25
|
+
decision: "deny",
|
|
26
|
+
category: "read:credential-store",
|
|
27
|
+
reason: `access to credential store ${shown(p, home)} blocked for all models`,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Reasons land in the activity log, and a path the classifier resolved is
|
|
32
|
+
// absolute; spelling the home dir as `~` keeps the username out of it.
|
|
33
|
+
// Compared as forward-slashed strings so the spelling is the same on every host.
|
|
34
|
+
function shown(p: string, home: string): string {
|
|
35
|
+
const fp = p.replace(/\\/g, "/");
|
|
36
|
+
const fh = home.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
37
|
+
if (!fh) return p;
|
|
38
|
+
if (fp.toLowerCase() === fh.toLowerCase()) return "~";
|
|
39
|
+
return fp.toLowerCase().startsWith(fh.toLowerCase() + "/") ? `~/${fp.slice(fh.length + 1)}` : p;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const FILE_WRITE_TOOLS = new Set(["write", "edit"]);
|
|
43
|
+
// Tools that write a local file they're handed a path for. Readers that take a
|
|
44
|
+
// path (the local uploader) are left alone: reading the notebook is fine.
|
|
45
|
+
const LOCAL_WRITER_TOOL = /download/i;
|
|
46
|
+
// Parameter names those tools use for the destination.
|
|
47
|
+
const LOCAL_PATH_KEY = /^(?:file_?path|path|output_?path|dest(?:ination)?(?:_path)?|out_?file)$/i;
|
|
48
|
+
// The record files only the hooked file tools may write.
|
|
49
|
+
const RECORD_BASENAMES = new Set(["notebook.md", "activity.jsonl"]);
|
|
50
|
+
// Read-like pi tools that take an optional `path`. grep reads file CONTENTS, so
|
|
51
|
+
// `grep <pat> ~/.ssh/id_rsa` is a credential-leak vector; ls/find/glob enumerate
|
|
52
|
+
// names under a path. All face the sensitive-path floor AND the workspace jail --
|
|
53
|
+
// a read pointed outside the work dir prompts, same as a write. A path-less call
|
|
54
|
+
// (e.g. grep with just a pattern) searches the cwd and is allowed.
|
|
55
|
+
const FILE_READ_TOOLS = new Set(["read", "grep", "ls", "find", "glob"]);
|
|
56
|
+
|
|
57
|
+
function pick(toolInput: Record<string, unknown>, key: string): string | undefined {
|
|
58
|
+
const v = toolInput[key];
|
|
59
|
+
return typeof v === "string" ? v : undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// pi declares its file tools with a `path` parameter, but every one of their
|
|
63
|
+
// renderers resolves `file_path ?? path` (dist/core/tools/{edit,write,read}.js):
|
|
64
|
+
// the Anthropic spelling arrives often enough that pi displays it. A floor must
|
|
65
|
+
// not assume one spelling, so both keys are collected and each check below runs
|
|
66
|
+
// across all of them -- a benign `path` cannot launder a sensitive `file_path`,
|
|
67
|
+
// and the path the human is asked about is one the tool could actually touch.
|
|
68
|
+
const PATH_KEYS = ["path", "file_path"];
|
|
69
|
+
|
|
70
|
+
interface PathTarget {
|
|
71
|
+
raw: string;
|
|
72
|
+
resolved: string;
|
|
73
|
+
inside: boolean;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function pathTargets(req: PolicyRequest, deps: PolicyDeps): PathTarget[] {
|
|
77
|
+
const out: PathTarget[] = [];
|
|
78
|
+
for (const key of PATH_KEYS) {
|
|
79
|
+
const raw = pick(req.toolInput, key);
|
|
80
|
+
if (!raw || out.some((t) => t.raw === raw)) continue;
|
|
81
|
+
out.push({ raw, ...deps.resolver.contains(raw) });
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Apply the weak-model + non-interactive modifiers to an `ask`.
|
|
87
|
+
function finalizeAsk(req: PolicyRequest, category: string, reason: string): PolicyResult {
|
|
88
|
+
if (req.modelTier === "weak") {
|
|
89
|
+
return { decision: "deny", category, reason: `${reason} (denied: low-capability model)` };
|
|
90
|
+
}
|
|
91
|
+
if (!req.interactive) {
|
|
92
|
+
return {
|
|
93
|
+
decision: "deny",
|
|
94
|
+
category,
|
|
95
|
+
reason: `${reason} (denied: no interactive session to approve)`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return { decision: "ask", category, reason };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// A destructive, irreversible Galaxy operation (#338). Always confirm -- regardless of
|
|
102
|
+
// model tier, so it deliberately does NOT go through finalizeAsk (no weak->deny downgrade).
|
|
103
|
+
// A non-interactive session has no one to approve, so it denies rather than running silently.
|
|
104
|
+
function destructiveGalaxy(req: PolicyRequest, op: GalaxyDestructiveOp): PolicyResult {
|
|
105
|
+
const headline = describeGalaxyDestructive(op).headline;
|
|
106
|
+
if (!req.interactive) {
|
|
107
|
+
return {
|
|
108
|
+
decision: "deny",
|
|
109
|
+
category: "galaxy:destructive",
|
|
110
|
+
reason: `${headline} (denied: no interactive session to approve)`,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
return { decision: "ask", category: "galaxy:destructive", reason: headline };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function decide(req: PolicyRequest, deps: PolicyDeps): PolicyResult {
|
|
117
|
+
// 1. Bypass short-circuit (human-only; see guardian-config.resolveBypass).
|
|
118
|
+
if (req.config.dangerouslyBypassPermissions) {
|
|
119
|
+
return { decision: "allow", category: "bypass", reason: "permissions bypassed" };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// pi emits its built-in file tools lowercase ("bash"/"read"/"write"/"edit"); we
|
|
123
|
+
// normalize so a future capitalized or renamed emission can't slip past the jail
|
|
124
|
+
// into the "other -> allow" bucket. (Verified: pi has exactly write+edit, lowercase.)
|
|
125
|
+
const toolName = req.toolName.toLowerCase();
|
|
126
|
+
|
|
127
|
+
if (toolName === "bash") {
|
|
128
|
+
const command = pick(req.toolInput, "command") ?? "";
|
|
129
|
+
const c = classifyBash(command, deps.home, req.cwd);
|
|
130
|
+
if (c.kind === "catastrophic") {
|
|
131
|
+
return { decision: "deny", category: "bash:catastrophic", reason: c.reason };
|
|
132
|
+
}
|
|
133
|
+
// The classifier judges the command string; only this layer has a resolver.
|
|
134
|
+
// A write target that looks like ordinary work product in the analysis
|
|
135
|
+
// workspace but realpaths into Orbit's own state is still Orbit's own state.
|
|
136
|
+
for (const p of c.stateDirWriteTargets) {
|
|
137
|
+
if (isOrbitStatePath(deps.resolver.contains(p).resolved, deps.home)) {
|
|
138
|
+
return {
|
|
139
|
+
decision: "deny",
|
|
140
|
+
category: "bash:catastrophic",
|
|
141
|
+
reason: "write to the Orbit config directory",
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// Sensitive-read floor: every content-read target, including inside a pipe or
|
|
146
|
+
// compound command (closes the `cat secret | tool` evasion). A dedicated
|
|
147
|
+
// credential store is denied for ALL tiers; any other sensitive path
|
|
148
|
+
// downgrades to an ask (deny for weak / non-interactive).
|
|
149
|
+
// Targets resolved after a cd go first, so the credential store a cd reached
|
|
150
|
+
// is denied even when the operand as typed is only basename-sensitive
|
|
151
|
+
// (`cd ~/.ssh && cat id_rsa`). A command with no cd has none, and keeps the
|
|
152
|
+
// single first-match pass it always had.
|
|
153
|
+
const cdReads = c.cdReadPaths.map((p) => ({ p, ...deps.resolver.contains(p) }));
|
|
154
|
+
for (const { p, resolved } of cdReads) {
|
|
155
|
+
if (isCredentialStore(resolved, deps.home)) return denyCredentialStore(p, deps.home);
|
|
156
|
+
}
|
|
157
|
+
for (const p of c.sensitiveReadPaths) {
|
|
158
|
+
const { resolved } = deps.resolver.contains(p);
|
|
159
|
+
if (isCredentialStore(resolved, deps.home)) return denyCredentialStore(p, deps.home);
|
|
160
|
+
if (isSensitivePath(resolved, deps.home)) {
|
|
161
|
+
return finalizeAsk(req, "read:sensitive", `read of sensitive path ${shown(p, deps.home)}`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
for (const { p, resolved } of cdReads) {
|
|
165
|
+
if (isSensitivePath(resolved, deps.home)) {
|
|
166
|
+
return finalizeAsk(req, "read:sensitive", `read of sensitive path ${shown(p, deps.home)}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Workspace-jail floor: only confidently-parsed simple read commands, so a
|
|
170
|
+
// compound command's jail semantics stay unchanged (it falls to "unknown").
|
|
171
|
+
for (const p of c.readPaths) {
|
|
172
|
+
const { inside } = deps.resolver.contains(p);
|
|
173
|
+
if (!inside) {
|
|
174
|
+
return finalizeAsk(req, "read:escape", `read outside workspace: ${p}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (c.kind === "safe") {
|
|
178
|
+
return { decision: "allow", category: "bash:safe", reason: c.reason };
|
|
179
|
+
}
|
|
180
|
+
// Destructive Galaxy API call via raw curl/wget (DELETE /api/histories/{id}). A
|
|
181
|
+
// guardrail, not a boundary -- routes the unknown command to the same destructive
|
|
182
|
+
// confirm as the tool path (asks even a weak model) instead of a generic "unknown" ask.
|
|
183
|
+
// Catastrophic pipes were already denied above, so they never reach here.
|
|
184
|
+
{
|
|
185
|
+
const op = isGalaxyDestructiveCurl(command);
|
|
186
|
+
if (op) return destructiveGalaxy(req, op);
|
|
187
|
+
}
|
|
188
|
+
// Unknown command. A trusted workspace relaxes by one notch only, and only
|
|
189
|
+
// for this category: trusted model ask->allow, weak model deny->ask (the
|
|
190
|
+
// human stays in the loop). It never lifts the catastrophic/jail/sensitive
|
|
191
|
+
// floor above, and it never covers a command run after a cd into Orbit's own
|
|
192
|
+
// state or a credential store -- trusting the workspace says nothing about
|
|
193
|
+
// those directories.
|
|
194
|
+
// Nor a line that names the analysis record or the registry: what an
|
|
195
|
+
// interpreter or script does with them is beyond this classifier, and the
|
|
196
|
+
// shell write forms it does model are denied above.
|
|
197
|
+
if (req.config.trustedWorkspaces.includes(req.cwd) && !c.guardedCwd && !c.namesRecord) {
|
|
198
|
+
if (req.modelTier === "trusted") {
|
|
199
|
+
return {
|
|
200
|
+
decision: "allow",
|
|
201
|
+
category: "bash:trusted-workspace",
|
|
202
|
+
reason: "unknown command in trusted workspace",
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
if (!req.interactive) {
|
|
206
|
+
return {
|
|
207
|
+
decision: "deny",
|
|
208
|
+
category: "bash:unknown",
|
|
209
|
+
reason: `${c.reason} (denied: no interactive session to approve)`,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
return { decision: "ask", category: "bash:trusted-workspace", reason: c.reason };
|
|
213
|
+
}
|
|
214
|
+
// Unrecognized command in an untrusted workspace. Both tiers get a human
|
|
215
|
+
// approval prompt when interactive -- a weak model is no longer hard-denied
|
|
216
|
+
// here, so a user who explicitly chose local execution can approve routine
|
|
217
|
+
// local work (run a script it just wrote, a compound/redirected command)
|
|
218
|
+
// instead of being stuck (#232). Nothing is auto-allowed: the silent pass
|
|
219
|
+
// stays reserved for a trusted model in a trusted workspace (above), and the
|
|
220
|
+
// catastrophic / credential-store / sensitive-read / jail floors are all
|
|
221
|
+
// enforced before this point, so this branch only governs the residual
|
|
222
|
+
// "command we can't reason about" case. A headless session still denies --
|
|
223
|
+
// there is no one to approve.
|
|
224
|
+
if (!req.interactive) {
|
|
225
|
+
return {
|
|
226
|
+
decision: "deny",
|
|
227
|
+
category: "bash:unknown",
|
|
228
|
+
reason: `${c.reason} (denied: no interactive session to approve)`,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
return { decision: "ask", category: "bash:unknown", reason: c.reason };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (FILE_READ_TOOLS.has(toolName)) {
|
|
235
|
+
// Each floor sweeps every target before the next one runs, so the strictest
|
|
236
|
+
// verdict wins no matter which key carries the offending path.
|
|
237
|
+
const targets = pathTargets(req, deps);
|
|
238
|
+
for (const t of targets) {
|
|
239
|
+
if (isCredentialStore(t.resolved, deps.home)) return denyCredentialStore(t.raw, deps.home);
|
|
240
|
+
}
|
|
241
|
+
for (const t of targets) {
|
|
242
|
+
if (isSensitivePath(t.resolved, deps.home)) {
|
|
243
|
+
return finalizeAsk(req, "read:sensitive", `${req.toolName} of sensitive path ${t.raw}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
// Out-of-workspace reads prompt. Sensitive paths are floored above.
|
|
247
|
+
for (const t of targets) {
|
|
248
|
+
if (!t.inside) {
|
|
249
|
+
return finalizeAsk(req, "read:escape", `${req.toolName} outside workspace: ${t.raw}`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return { decision: "allow", category: "read:ok", reason: "non-sensitive read" };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (FILE_WRITE_TOOLS.has(toolName)) {
|
|
256
|
+
const targets = pathTargets(req, deps);
|
|
257
|
+
if (targets.length === 0) {
|
|
258
|
+
return finalizeAsk(req, "write:no-path", "write with no resolvable path");
|
|
259
|
+
}
|
|
260
|
+
// Credential-shaped writes are floored regardless of jail membership, mirroring
|
|
261
|
+
// the read branch -- a secret dropped inside the workspace is still a secret.
|
|
262
|
+
for (const t of targets) {
|
|
263
|
+
if (isSensitivePath(t.resolved, deps.home)) {
|
|
264
|
+
return finalizeAsk(req, "write:sensitive", `write to sensitive path ${t.raw}`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// Gated even inside the jail: a script under .git/hooks runs on the next git
|
|
268
|
+
// operation, and .loom/ is Orbit's own state -- these always prompt, regardless
|
|
269
|
+
// of being in the workspace. The lone carve-out is the $HOME/.loom/analyses
|
|
270
|
+
// tree (Orbit's default cwd), where the analysis's own files are work product;
|
|
271
|
+
// a .git/.loom/.orbit nested inside an analysis still gates. See
|
|
272
|
+
// isProtectedWritePath.
|
|
273
|
+
//
|
|
274
|
+
// The path as written is checked as well as the realpath: a state dir that
|
|
275
|
+
// is itself a symlink (onto a bigger disk, say) realpaths to somewhere with
|
|
276
|
+
// no state segment, and since it is also a jail root the write would
|
|
277
|
+
// otherwise pass silently.
|
|
278
|
+
// The registry and provenance are a floor, not an ask: approving the
|
|
279
|
+
// model's write to its own approval record would make the approval mean
|
|
280
|
+
// nothing.
|
|
281
|
+
for (const t of targets) {
|
|
282
|
+
const lexical = path.resolve(req.cwd, expandHome(t.raw, deps.home));
|
|
283
|
+
if (isRecordStorePath(t.resolved) || isRecordStorePath(lexical)) {
|
|
284
|
+
return {
|
|
285
|
+
decision: "deny",
|
|
286
|
+
category: "write:record-store",
|
|
287
|
+
reason: `write to Orbit's harness-owned record ${t.raw} (approvals, run evaluations, provenance) blocked for all models`,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
for (const t of targets) {
|
|
292
|
+
const lexical = path.resolve(req.cwd, expandHome(t.raw, deps.home));
|
|
293
|
+
if (isProtectedWritePath(t.resolved, deps.home) || isProtectedWritePath(lexical, deps.home)) {
|
|
294
|
+
return finalizeAsk(req, "write:protected", `write to protected path ${t.raw}`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
for (const t of targets) {
|
|
298
|
+
if (!t.inside) {
|
|
299
|
+
return finalizeAsk(req, "write:escape", `write outside workspace: ${t.raw}`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return { decision: "allow", category: "write:in-jail", reason: "write inside workspace" };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// A tool that writes a local file it is handed a path for -- download_dataset
|
|
306
|
+
// with `file_path` is the one that exists -- must not land
|
|
307
|
+
// on the analysis record or the harness-owned registry, any more than the
|
|
308
|
+
// shell may. Lexical and resolved both, as for the write tools.
|
|
309
|
+
for (const [key, value] of LOCAL_WRITER_TOOL.test(toolName)
|
|
310
|
+
? Object.entries(req.toolInput)
|
|
311
|
+
: []) {
|
|
312
|
+
if (typeof value !== "string" || !LOCAL_PATH_KEY.test(key) || value.length === 0) continue;
|
|
313
|
+
const lexical = path.resolve(req.cwd, expandHome(value, deps.home));
|
|
314
|
+
const { resolved } = deps.resolver.contains(value);
|
|
315
|
+
const recordFile = [lexical, resolved].some((p) =>
|
|
316
|
+
RECORD_BASENAMES.has(path.basename(p).toLowerCase()),
|
|
317
|
+
);
|
|
318
|
+
if (recordFile || isRecordStorePath(lexical) || isRecordStorePath(resolved)) {
|
|
319
|
+
return {
|
|
320
|
+
decision: "deny",
|
|
321
|
+
category: "write:record-store",
|
|
322
|
+
reason: `${req.toolName} would write ${value}, part of the analysis record`,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Destructive Galaxy operations (whole-history delete/purge) -- called directly or
|
|
328
|
+
// dispatched through the code-mode run_galaxy_tool envelope -- must be confirmed
|
|
329
|
+
// regardless of tier (#338). Non-destructive Galaxy/MCP tools fall through to the allow.
|
|
330
|
+
{
|
|
331
|
+
const op = classifyGalaxyDestructive(toolName, req.toolInput);
|
|
332
|
+
if (op) return destructiveGalaxy(req, op);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Everything else is allowed: Galaxy/notebook tools, web fetchers, MCP. These are
|
|
336
|
+
// the remote/egress surface (notebook_push_to_galaxy, galaxy_upload_*/run_user_tool,
|
|
337
|
+
// skills_fetch's ~/.loom cache write) and are deliberately OUT of scope
|
|
338
|
+
// for the working-dir write-jail -- a local write boundary cannot and does not
|
|
339
|
+
// promise anything about data leaving the machine. Gating egress is separate future
|
|
340
|
+
// work; do not let this fallthrough be read as "confined".
|
|
341
|
+
return { decision: "allow", category: "other", reason: "non-local-execution tool" };
|
|
342
|
+
}
|