@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,623 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-attempt provenance files: `<analysis>/.loom/provenance/<attempt_id>.json`.
|
|
3
|
+
*
|
|
4
|
+
* The notebook block carries a compact summary of each recorded run; this is
|
|
5
|
+
* the full record behind it -- every job's tool, version, effective params,
|
|
6
|
+
* inputs and outputs, as Galaxy reported them. Written only by the harness,
|
|
7
|
+
* into the `.loom/` tree the write jail already protects from the agent's file
|
|
8
|
+
* tools and bash redirects, so it is the half of the record the model cannot
|
|
9
|
+
* edit after the fact.
|
|
10
|
+
*
|
|
11
|
+
* The file is created when the attempt is first recorded (by the submission
|
|
12
|
+
* hook, or by reconcile for a run nobody recorded), and that first write names
|
|
13
|
+
* the Galaxy ids the attempt owns. Enrichment only ever adds job records to a
|
|
14
|
+
* file whose owned ids include the block asking. That matters because the
|
|
15
|
+
* notebook is not protected: `attempt_id` sits in a block the agent can edit,
|
|
16
|
+
* and without the ownership check a block re-pointed at another attempt's id
|
|
17
|
+
* would merge its own jobs into that attempt's record -- a forged attribution
|
|
18
|
+
* written by the harness itself.
|
|
19
|
+
*
|
|
20
|
+
* Writes are atomic (a sibling temp file renamed over the target), refuse to
|
|
21
|
+
* go through a symlink at any level below the analysis directory, and are
|
|
22
|
+
* serialised per file inside this process.
|
|
23
|
+
*
|
|
24
|
+
* The bytes go through a ProvenanceFs: the real file system under a file
|
|
25
|
+
* notebook, a Map under a page notebook (which has no directory beside it),
|
|
26
|
+
* or whatever a shell installs with setProvenanceFs.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import * as path from "path";
|
|
30
|
+
import { capabilityProfile } from "../../shared/capability-profile.js";
|
|
31
|
+
import { isGalaxyFetchOverridden, sameGalaxyServer } from "./galaxy-api";
|
|
32
|
+
import type { ProvenanceFs, ProvenanceStat } from "./provenance-fs";
|
|
33
|
+
import { fileProvenanceFs } from "./provenance-fs-file";
|
|
34
|
+
import { memoryProvenanceFs } from "./provenance-memory-fs";
|
|
35
|
+
import { isUlid } from "./ulid";
|
|
36
|
+
|
|
37
|
+
let installed: ProvenanceFs | null = null;
|
|
38
|
+
// One Map per brain process, like the registry's: a page notebook has no
|
|
39
|
+
// directory beside it, and a second session_start in the same worker reopens
|
|
40
|
+
// what the first wrote.
|
|
41
|
+
let pageFs: ProvenanceFs | null = null;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A shell's own byte store for the records (it must keep the sync read); null
|
|
45
|
+
* restores the default and starts the page Map afresh, so one test's cursor
|
|
46
|
+
* never leaks into the next.
|
|
47
|
+
*/
|
|
48
|
+
export function setProvenanceFs(next: ProvenanceFs | null): void {
|
|
49
|
+
installed = next;
|
|
50
|
+
if (!next) pageFs = null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Resolved on every call: the profile is installed after import.
|
|
54
|
+
function provenanceFs(): ProvenanceFs {
|
|
55
|
+
if (installed) return installed;
|
|
56
|
+
if (capabilityProfile().notebook !== "page") return fileProvenanceFs;
|
|
57
|
+
return (pageFs ??= memoryProvenanceFs());
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export const PROVENANCE_SCHEMA_VERSION = 1;
|
|
61
|
+
|
|
62
|
+
/** Relative to the analysis directory. Same tree the UDT definitions use. */
|
|
63
|
+
export const PROVENANCE_DIR_PARTS = [".loom", "provenance"] as const;
|
|
64
|
+
|
|
65
|
+
/** What a field is when Galaxy did not say. Never inferred from anything else. */
|
|
66
|
+
export const UNKNOWN = "unknown";
|
|
67
|
+
export type Maybe<T> = T | typeof UNKNOWN;
|
|
68
|
+
|
|
69
|
+
export type AttemptKind = "invocation" | "jobs";
|
|
70
|
+
|
|
71
|
+
/** One input or output dataset of a job. */
|
|
72
|
+
export interface ProvenanceDataset {
|
|
73
|
+
/** The tool's own name for the input/output slot. */
|
|
74
|
+
name: string;
|
|
75
|
+
id: string;
|
|
76
|
+
src: Maybe<string>;
|
|
77
|
+
dataset_name: Maybe<string>;
|
|
78
|
+
ext: Maybe<string>;
|
|
79
|
+
dbkey: Maybe<string>;
|
|
80
|
+
create_time: Maybe<string>;
|
|
81
|
+
state: Maybe<string>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface ProvenanceJob {
|
|
85
|
+
job_id: string;
|
|
86
|
+
tool_id: Maybe<string>;
|
|
87
|
+
tool_version: Maybe<string>;
|
|
88
|
+
/**
|
|
89
|
+
* Where `tool_version` came from, since job details never carry it.
|
|
90
|
+
* `notebook` is the block's own summary, for an attempt recorded before
|
|
91
|
+
* these files existed -- editable text, and labelled as such.
|
|
92
|
+
*/
|
|
93
|
+
tool_version_source:
|
|
94
|
+
"submission" | "invocation_step" | "job_listing" | "tool_id" | "notebook" | typeof UNKNOWN;
|
|
95
|
+
state: Maybe<string>;
|
|
96
|
+
exit_code: number | null | typeof UNKNOWN;
|
|
97
|
+
create_time: Maybe<string>;
|
|
98
|
+
update_time: Maybe<string>;
|
|
99
|
+
command_version: Maybe<string>;
|
|
100
|
+
params: Record<string, unknown> | typeof UNKNOWN;
|
|
101
|
+
inputs: ProvenanceDataset[] | typeof UNKNOWN;
|
|
102
|
+
outputs: ProvenanceDataset[] | typeof UNKNOWN;
|
|
103
|
+
output_collections: { name: string; id: string; src: Maybe<string> }[] | typeof UNKNOWN;
|
|
104
|
+
/** For a workflow step job: which step produced it. */
|
|
105
|
+
step?: {
|
|
106
|
+
order_index: number | typeof UNKNOWN;
|
|
107
|
+
label: Maybe<string>;
|
|
108
|
+
subworkflow_invocation_id?: string;
|
|
109
|
+
};
|
|
110
|
+
/** Set when this job's details could not be fetched; the fields above are then `unknown`. */
|
|
111
|
+
unavailable?: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export interface AttemptRecord {
|
|
115
|
+
schema: number;
|
|
116
|
+
attempt_id: string;
|
|
117
|
+
kind: AttemptKind;
|
|
118
|
+
galaxy_server_url: string;
|
|
119
|
+
history_id: Maybe<string>;
|
|
120
|
+
/** Who made the submission, as far as the harness knows. Mirrors the block. */
|
|
121
|
+
submitted_by: "harness" | "agent" | "unknown" | "replay";
|
|
122
|
+
created_at: string;
|
|
123
|
+
/** The Galaxy ids this attempt owns. Set at creation and never widened. */
|
|
124
|
+
ids: { invocation_id?: string; job_ids?: string[] };
|
|
125
|
+
/**
|
|
126
|
+
* Who decided `ids` and `submitted_by`. `submission` and `reconcile` read
|
|
127
|
+
* them out of Galaxy's own answers; `notebook` means the record was created
|
|
128
|
+
* late, from a block that predates these files, so both came from notebook
|
|
129
|
+
* text the agent can edit and are only as good as that text.
|
|
130
|
+
*/
|
|
131
|
+
origin: "submission" | "reconcile" | "notebook";
|
|
132
|
+
/**
|
|
133
|
+
* What Galaxy said about each job when the attempt was recorded -- the
|
|
134
|
+
* submission response is the only answer that carries `tool_version`, and
|
|
135
|
+
* keeping it here rather than reading it back off the block means a hand
|
|
136
|
+
* edit to the block's summary cannot become the recorded version.
|
|
137
|
+
*/
|
|
138
|
+
seeds?: Record<string, { tool_id?: string; tool_version?: string }>;
|
|
139
|
+
/** Set when any of this record came from the Tier-1 fixture seam, not a server. */
|
|
140
|
+
fixture?: true;
|
|
141
|
+
enrichment: {
|
|
142
|
+
state: "pending" | "complete" | "unavailable";
|
|
143
|
+
attempts: number;
|
|
144
|
+
/**
|
|
145
|
+
* Attempts per block (`job:<id>` / `invocation:<id>`). The count that
|
|
146
|
+
* decides when a block goes unavailable lives here, not in the block's
|
|
147
|
+
* `enrichment_attempts`, which is notebook text: editing it would let the
|
|
148
|
+
* agent retry forever or exhaust a record on purpose.
|
|
149
|
+
*/
|
|
150
|
+
attempts_by_block?: Record<string, number>;
|
|
151
|
+
error?: string;
|
|
152
|
+
updated_at: string;
|
|
153
|
+
};
|
|
154
|
+
jobs: Record<string, ProvenanceJob>;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export class ProvenanceRefusal extends Error {
|
|
158
|
+
constructor(message: string) {
|
|
159
|
+
super(message);
|
|
160
|
+
this.name = "ProvenanceRefusal";
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Analysis-relative POSIX path of an attempt's file, for activity rows. */
|
|
165
|
+
export function provenanceRelativePath(attemptId: string): string {
|
|
166
|
+
return [...PROVENANCE_DIR_PARTS, `${attemptId}.json`].join("/");
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* The absolute path for an attempt, or a refusal. The id is a ULID or nothing:
|
|
171
|
+
* it reaches here from a notebook block, so it is agent-editable text, and a
|
|
172
|
+
* filename built from it must not be able to name anything but one file in one
|
|
173
|
+
* directory.
|
|
174
|
+
*/
|
|
175
|
+
export function provenancePath(analysisDir: string, attemptId: string): string {
|
|
176
|
+
if (!isUlid(attemptId)) {
|
|
177
|
+
throw new ProvenanceRefusal(`"${attemptId}" is not an attempt id`);
|
|
178
|
+
}
|
|
179
|
+
return path.join(analysisDir, ...PROVENANCE_DIR_PARTS, `${attemptId}.json`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Make sure every directory from the analysis dir down to the provenance dir
|
|
184
|
+
* is a real directory. A symlinked `.loom` or `provenance` would carry the
|
|
185
|
+
* write somewhere the jail never looked at.
|
|
186
|
+
*/
|
|
187
|
+
async function ensureProvenanceDir(analysisDir: string, create = true): Promise<string | null> {
|
|
188
|
+
let current = analysisDir;
|
|
189
|
+
for (const part of PROVENANCE_DIR_PARTS) {
|
|
190
|
+
current = path.join(current, part);
|
|
191
|
+
let stat: ProvenanceStat | null = await provenanceFs().lstat(current);
|
|
192
|
+
if (!stat && !create) return null;
|
|
193
|
+
if (!stat) {
|
|
194
|
+
await provenanceFs().mkdir(current);
|
|
195
|
+
stat = await provenanceFs().lstat(current);
|
|
196
|
+
// Removed between mkdir and lstat: surface it as the ENOENT fsp.lstat
|
|
197
|
+
// threw here before the seam, so enrichment retries instead of giving up.
|
|
198
|
+
if (!stat) {
|
|
199
|
+
throw Object.assign(new Error(`ENOENT: ${current} vanished after mkdir`), {
|
|
200
|
+
code: "ENOENT",
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
205
|
+
throw new ProvenanceRefusal(`${current} is not a plain directory`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return current;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Per-file promise chain, so two enrichment passes never interleave a read-modify-write. */
|
|
212
|
+
const fileLocks = new Map<string, Promise<unknown>>();
|
|
213
|
+
|
|
214
|
+
function withFileLock<T>(file: string, work: () => Promise<T>): Promise<T> {
|
|
215
|
+
const prior = fileLocks.get(file) ?? Promise.resolve();
|
|
216
|
+
const next = prior.catch(() => undefined).then(work);
|
|
217
|
+
const settled = next.catch(() => undefined);
|
|
218
|
+
fileLocks.set(file, settled);
|
|
219
|
+
void settled.then(() => {
|
|
220
|
+
if (fileLocks.get(file) === settled) fileLocks.delete(file);
|
|
221
|
+
});
|
|
222
|
+
return next;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Read an attempt's record. Null when there is none. A file that is there but
|
|
227
|
+
* is not a record -- a symlink, unparseable, or naming a different attempt --
|
|
228
|
+
* is a refusal rather than a null, so a caller never mistakes a planted file
|
|
229
|
+
* for permission to start a fresh one over it.
|
|
230
|
+
*/
|
|
231
|
+
export async function readAttemptRecord(
|
|
232
|
+
analysisDir: string,
|
|
233
|
+
attemptId: string,
|
|
234
|
+
): Promise<AttemptRecord | null> {
|
|
235
|
+
const file = provenancePath(analysisDir, attemptId);
|
|
236
|
+
// The directories are checked on the way in too: reading through a
|
|
237
|
+
// symlinked `.loom` would hand back a record planted somewhere else.
|
|
238
|
+
if (!(await ensureProvenanceDir(analysisDir, false))) return null;
|
|
239
|
+
const stat = await provenanceFs().lstat(file);
|
|
240
|
+
if (!stat) return null;
|
|
241
|
+
if (!stat.isFile()) throw new ProvenanceRefusal(`${file} is not a plain file`);
|
|
242
|
+
let parsed: unknown;
|
|
243
|
+
try {
|
|
244
|
+
parsed = JSON.parse(await provenanceFs().readFile(file));
|
|
245
|
+
} catch {
|
|
246
|
+
throw new ProvenanceRefusal(`${file} is not a readable provenance record`);
|
|
247
|
+
}
|
|
248
|
+
if (!isAttemptRecord(parsed) || parsed.attempt_id !== attemptId) {
|
|
249
|
+
throw new ProvenanceRefusal(`${file} does not hold the record for ${attemptId}`);
|
|
250
|
+
}
|
|
251
|
+
return parsed;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function isAttemptRecord(value: unknown): value is AttemptRecord {
|
|
255
|
+
if (!value || typeof value !== "object") return false;
|
|
256
|
+
const v = value as Partial<AttemptRecord>;
|
|
257
|
+
return (
|
|
258
|
+
typeof v.attempt_id === "string" &&
|
|
259
|
+
(v.kind === "invocation" || v.kind === "jobs") &&
|
|
260
|
+
!!v.ids &&
|
|
261
|
+
typeof v.ids === "object" &&
|
|
262
|
+
!!v.jobs &&
|
|
263
|
+
typeof v.jobs === "object" &&
|
|
264
|
+
!!v.enrichment &&
|
|
265
|
+
typeof v.enrichment === "object"
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function writeAtomically(file: string, record: AttemptRecord): Promise<void> {
|
|
270
|
+
return provenanceFs().replace(file, JSON.stringify(record, null, 2) + "\n");
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export interface AttemptSeed {
|
|
274
|
+
attemptId: string;
|
|
275
|
+
kind: AttemptKind;
|
|
276
|
+
galaxyServerUrl: string;
|
|
277
|
+
historyId?: string;
|
|
278
|
+
submittedBy: AttemptRecord["submitted_by"];
|
|
279
|
+
ids: AttemptRecord["ids"];
|
|
280
|
+
origin: AttemptRecord["origin"];
|
|
281
|
+
seeds?: AttemptRecord["seeds"];
|
|
282
|
+
createdAt?: string;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Create the attempt's record if there is none, and return whatever is there.
|
|
287
|
+
*
|
|
288
|
+
* Creation is exclusive and first-writer-wins: the ids an attempt owns are
|
|
289
|
+
* decided once, by whoever recorded it first, and later callers get the
|
|
290
|
+
* existing record back to check themselves against rather than a chance to
|
|
291
|
+
* restate it.
|
|
292
|
+
*/
|
|
293
|
+
export async function ensureAttemptRecord(
|
|
294
|
+
analysisDir: string,
|
|
295
|
+
seed: AttemptSeed,
|
|
296
|
+
): Promise<AttemptRecord> {
|
|
297
|
+
const file = provenancePath(analysisDir, seed.attemptId);
|
|
298
|
+
return withFileLock(file, async () => {
|
|
299
|
+
const existing = await readAttemptRecord(analysisDir, seed.attemptId);
|
|
300
|
+
if (existing) return existing;
|
|
301
|
+
await ensureProvenanceDir(analysisDir);
|
|
302
|
+
const now = new Date().toISOString();
|
|
303
|
+
const record: AttemptRecord = {
|
|
304
|
+
schema: PROVENANCE_SCHEMA_VERSION,
|
|
305
|
+
attempt_id: seed.attemptId,
|
|
306
|
+
kind: seed.kind,
|
|
307
|
+
galaxy_server_url: seed.galaxyServerUrl,
|
|
308
|
+
// A record made from notebook text takes nothing from that text it would
|
|
309
|
+
// then vouch for: who submitted it and which history it ran in are left
|
|
310
|
+
// unknown (enrichment fills the history from Galaxy's own answer).
|
|
311
|
+
history_id: seed.origin === "notebook" ? UNKNOWN : (seed.historyId ?? UNKNOWN),
|
|
312
|
+
submitted_by: seed.origin === "notebook" ? "unknown" : seed.submittedBy,
|
|
313
|
+
created_at: seed.createdAt ?? now,
|
|
314
|
+
ids: normalizeIds(seed.ids),
|
|
315
|
+
origin: seed.origin,
|
|
316
|
+
...(seed.origin !== "notebook" && seed.seeds && Object.keys(seed.seeds).length > 0
|
|
317
|
+
? { seeds: seed.seeds }
|
|
318
|
+
: {}),
|
|
319
|
+
...(isGalaxyFetchOverridden() ? { fixture: true as const } : {}),
|
|
320
|
+
enrichment: { state: "pending", attempts: 0, updated_at: now },
|
|
321
|
+
jobs: {},
|
|
322
|
+
};
|
|
323
|
+
try {
|
|
324
|
+
await provenanceFs().writeExclusive(file, JSON.stringify(record, null, 2) + "\n");
|
|
325
|
+
} catch (err) {
|
|
326
|
+
if ((err as NodeJS.ErrnoException)?.code !== "EEXIST") throw err;
|
|
327
|
+
const raced = await readAttemptRecord(analysisDir, seed.attemptId);
|
|
328
|
+
if (raced) return raced;
|
|
329
|
+
throw err;
|
|
330
|
+
}
|
|
331
|
+
return record;
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function normalizeIds(ids: AttemptRecord["ids"]): AttemptRecord["ids"] {
|
|
336
|
+
return {
|
|
337
|
+
...(ids.invocation_id ? { invocation_id: ids.invocation_id } : {}),
|
|
338
|
+
...(ids.job_ids && ids.job_ids.length > 0 ? { job_ids: [...new Set(ids.job_ids)] } : {}),
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** Whether this record was created for the block asking about it. */
|
|
343
|
+
export function attemptOwns(
|
|
344
|
+
record: AttemptRecord,
|
|
345
|
+
blockKind: "invocation" | "job",
|
|
346
|
+
id: string,
|
|
347
|
+
): boolean {
|
|
348
|
+
if (blockKind === "invocation") {
|
|
349
|
+
return record.kind === "invocation" && record.ids.invocation_id === id;
|
|
350
|
+
}
|
|
351
|
+
return record.kind === "jobs" && (record.ids.job_ids ?? []).includes(id);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export interface EnrichmentWrite {
|
|
355
|
+
blockKind: "invocation" | "job";
|
|
356
|
+
blockId: string;
|
|
357
|
+
state: AttemptRecord["enrichment"]["state"];
|
|
358
|
+
attempts: number;
|
|
359
|
+
error?: string;
|
|
360
|
+
/** Job records to add or refresh. Keys outside what the attempt owns are refused. */
|
|
361
|
+
jobs: ProvenanceJob[];
|
|
362
|
+
/** The server these answers came from; must be the one the attempt was recorded on. */
|
|
363
|
+
serverUrl: string;
|
|
364
|
+
/** The history Galaxy says these jobs ran in, when it said. Must match the attempt's. */
|
|
365
|
+
historyId?: string;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Fold one enrichment result into an existing attempt record.
|
|
370
|
+
*
|
|
371
|
+
* Refuses unless the record was created for this block -- see the module
|
|
372
|
+
* note. For a tool-run attempt each job record must be one of the job ids the
|
|
373
|
+
* attempt owns; for an invocation, the step jobs are whatever Galaxy says the
|
|
374
|
+
* invocation ran, which the ownership of the invocation id already vouches for.
|
|
375
|
+
*/
|
|
376
|
+
export async function writeEnrichment(
|
|
377
|
+
analysisDir: string,
|
|
378
|
+
attemptId: string,
|
|
379
|
+
update: EnrichmentWrite,
|
|
380
|
+
): Promise<AttemptRecord> {
|
|
381
|
+
const file = provenancePath(analysisDir, attemptId);
|
|
382
|
+
return withFileLock(file, async () => {
|
|
383
|
+
const record = await ownedRecord(analysisDir, attemptId, update);
|
|
384
|
+
if (
|
|
385
|
+
update.historyId &&
|
|
386
|
+
record.history_id !== UNKNOWN &&
|
|
387
|
+
record.history_id !== update.historyId
|
|
388
|
+
) {
|
|
389
|
+
throw new ProvenanceRefusal(
|
|
390
|
+
`Galaxy says these jobs ran in history ${update.historyId}, not the attempt's ${record.history_id}`,
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
if (update.blockKind === "job") {
|
|
394
|
+
const owned = new Set(record.ids.job_ids ?? []);
|
|
395
|
+
for (const job of update.jobs) {
|
|
396
|
+
if (!owned.has(job.job_id)) {
|
|
397
|
+
throw new ProvenanceRefusal(`job ${job.job_id} is not part of attempt ${attemptId}`);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
const jobs = { ...record.jobs };
|
|
402
|
+
for (const job of update.jobs) jobs[job.job_id] = job;
|
|
403
|
+
const next: AttemptRecord = {
|
|
404
|
+
...record,
|
|
405
|
+
...(record.history_id === UNKNOWN && update.historyId
|
|
406
|
+
? { history_id: update.historyId }
|
|
407
|
+
: {}),
|
|
408
|
+
...(isGalaxyFetchOverridden() ? { fixture: true as const } : {}),
|
|
409
|
+
jobs,
|
|
410
|
+
enrichment: {
|
|
411
|
+
state: attemptState(record, jobs, update.state),
|
|
412
|
+
attempts: update.attempts,
|
|
413
|
+
attempts_by_block: {
|
|
414
|
+
...record.enrichment.attempts_by_block,
|
|
415
|
+
[`${update.blockKind}:${update.blockId}`]: update.attempts,
|
|
416
|
+
},
|
|
417
|
+
...(update.error ? { error: update.error } : {}),
|
|
418
|
+
updated_at: new Date().toISOString(),
|
|
419
|
+
},
|
|
420
|
+
};
|
|
421
|
+
await ensureProvenanceDir(analysisDir);
|
|
422
|
+
await writeAtomically(file, next);
|
|
423
|
+
return next;
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/** The record, when it exists, belongs to this block, and was recorded on this server. */
|
|
428
|
+
async function ownedRecord(
|
|
429
|
+
analysisDir: string,
|
|
430
|
+
attemptId: string,
|
|
431
|
+
who: { blockKind: "invocation" | "job"; blockId: string; serverUrl: string },
|
|
432
|
+
): Promise<AttemptRecord> {
|
|
433
|
+
const record = await readAttemptRecord(analysisDir, attemptId);
|
|
434
|
+
if (!record) throw new ProvenanceRefusal(`no provenance record for ${attemptId}`);
|
|
435
|
+
if (!attemptOwns(record, who.blockKind, who.blockId)) {
|
|
436
|
+
throw new ProvenanceRefusal(
|
|
437
|
+
`${who.blockKind} ${who.blockId} is not part of attempt ${attemptId}`,
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
// Ids are only unique per server, so a record is bound to the server it was
|
|
441
|
+
// recorded on: another server's job with a colliding id is another job.
|
|
442
|
+
if (record.galaxy_server_url && !sameGalaxyServer(record.galaxy_server_url, who.serverUrl)) {
|
|
443
|
+
throw new ProvenanceRefusal(
|
|
444
|
+
`attempt ${attemptId} was recorded on ${record.galaxy_server_url}, not ${who.serverUrl}`,
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
return record;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/** Count one failed enrichment attempt for a block, in the record rather than the notebook. */
|
|
451
|
+
export async function noteEnrichmentRetry(
|
|
452
|
+
analysisDir: string,
|
|
453
|
+
attemptId: string,
|
|
454
|
+
who: { blockKind: "invocation" | "job"; blockId: string; serverUrl: string },
|
|
455
|
+
attempts: number,
|
|
456
|
+
error: string,
|
|
457
|
+
): Promise<void> {
|
|
458
|
+
const file = provenancePath(analysisDir, attemptId);
|
|
459
|
+
await withFileLock(file, async () => {
|
|
460
|
+
const record = await ownedRecord(analysisDir, attemptId, who);
|
|
461
|
+
await ensureProvenanceDir(analysisDir);
|
|
462
|
+
await writeAtomically(file, {
|
|
463
|
+
...record,
|
|
464
|
+
enrichment: {
|
|
465
|
+
...record.enrichment,
|
|
466
|
+
attempts: Math.max(record.enrichment.attempts, attempts),
|
|
467
|
+
attempts_by_block: {
|
|
468
|
+
...record.enrichment.attempts_by_block,
|
|
469
|
+
[`${who.blockKind}:${who.blockId}`]: attempts,
|
|
470
|
+
},
|
|
471
|
+
error,
|
|
472
|
+
updated_at: new Date().toISOString(),
|
|
473
|
+
},
|
|
474
|
+
});
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* A mapped tool run is one attempt but several blocks, each enriched on its
|
|
480
|
+
* own, so the attempt's state is read off its jobs rather than taken from
|
|
481
|
+
* whichever block wrote last: complete only when every owned job has a full
|
|
482
|
+
* record, unavailable as soon as one cannot have one.
|
|
483
|
+
*/
|
|
484
|
+
function attemptState(
|
|
485
|
+
record: AttemptRecord,
|
|
486
|
+
jobs: Record<string, ProvenanceJob>,
|
|
487
|
+
reported: AttemptRecord["enrichment"]["state"],
|
|
488
|
+
): AttemptRecord["enrichment"]["state"] {
|
|
489
|
+
if (record.kind === "invocation") return reported;
|
|
490
|
+
const owned = record.ids.job_ids ?? [];
|
|
491
|
+
if (owned.some((id) => jobs[id]?.unavailable)) return "unavailable";
|
|
492
|
+
if (reported === "unavailable") return "unavailable";
|
|
493
|
+
return owned.every((id) => jobs[id]) ? "complete" : "pending";
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/** Synchronous existence check, for the evidence gate's tool_call hook. */
|
|
497
|
+
export function readAttemptRecordSync(
|
|
498
|
+
analysisDir: string,
|
|
499
|
+
attemptId: string,
|
|
500
|
+
): AttemptRecord | null {
|
|
501
|
+
try {
|
|
502
|
+
const file = provenancePath(analysisDir, attemptId);
|
|
503
|
+
let dir = analysisDir;
|
|
504
|
+
for (const part of PROVENANCE_DIR_PARTS) {
|
|
505
|
+
dir = path.join(dir, part);
|
|
506
|
+
if (!provenanceFs().lstatSync(dir)?.isDirectory()) return null;
|
|
507
|
+
}
|
|
508
|
+
if (!provenanceFs().lstatSync(file)?.isFile()) return null;
|
|
509
|
+
const parsed: unknown = JSON.parse(provenanceFs().readFileSync(file));
|
|
510
|
+
return isAttemptRecord(parsed) && parsed.attempt_id === attemptId ? parsed : null;
|
|
511
|
+
} catch {
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
517
|
+
// Reconcile cursor
|
|
518
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Where reconcile keeps what it has already looked at: when the analysis
|
|
522
|
+
* started (the earliest work it will claim for this analysis) and, per
|
|
523
|
+
* server and history, the newest `create_time` it has seen. Beside the
|
|
524
|
+
* attempt records for the same reason they are here -- the window decides
|
|
525
|
+
* what gets written as unattributed work, and the agent must not be able to
|
|
526
|
+
* move it. Not a ULID name, so it can never collide with an attempt.
|
|
527
|
+
*/
|
|
528
|
+
const RECONCILE_STATE_FILE = "reconcile-state.json";
|
|
529
|
+
|
|
530
|
+
export interface ReconcileState {
|
|
531
|
+
schema: number;
|
|
532
|
+
analysis_started_at: string;
|
|
533
|
+
histories: Record<string, { stamp: string }>;
|
|
534
|
+
/**
|
|
535
|
+
* Per server, the history a page binding bound this analysis to, sealed the
|
|
536
|
+
* first time reconcile used it. Only consulted before the harness has
|
|
537
|
+
* submitted anything; a later edit to the notebook's binding cannot move it.
|
|
538
|
+
*/
|
|
539
|
+
bound_histories?: Record<string, string>;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
export async function readReconcileState(analysisDir: string): Promise<ReconcileState | null> {
|
|
543
|
+
if (!(await ensureProvenanceDir(analysisDir, false))) return null;
|
|
544
|
+
const file = path.join(analysisDir, ...PROVENANCE_DIR_PARTS, RECONCILE_STATE_FILE);
|
|
545
|
+
try {
|
|
546
|
+
if (!(await provenanceFs().lstat(file))?.isFile()) return null;
|
|
547
|
+
const parsed = JSON.parse(await provenanceFs().readFile(file)) as Partial<ReconcileState>;
|
|
548
|
+
if (
|
|
549
|
+
typeof parsed.analysis_started_at !== "string" ||
|
|
550
|
+
Number.isNaN(Date.parse(parsed.analysis_started_at))
|
|
551
|
+
) {
|
|
552
|
+
return null;
|
|
553
|
+
}
|
|
554
|
+
return {
|
|
555
|
+
schema: PROVENANCE_SCHEMA_VERSION,
|
|
556
|
+
analysis_started_at: parsed.analysis_started_at,
|
|
557
|
+
histories: parsed.histories && typeof parsed.histories === "object" ? parsed.histories : {},
|
|
558
|
+
...(parsed.bound_histories && typeof parsed.bound_histories === "object"
|
|
559
|
+
? { bound_histories: parsed.bound_histories }
|
|
560
|
+
: {}),
|
|
561
|
+
};
|
|
562
|
+
} catch {
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
export async function writeReconcileState(
|
|
568
|
+
analysisDir: string,
|
|
569
|
+
state: ReconcileState,
|
|
570
|
+
): Promise<void> {
|
|
571
|
+
const dir = await ensureProvenanceDir(analysisDir);
|
|
572
|
+
if (!dir) return;
|
|
573
|
+
const file = path.join(dir, RECONCILE_STATE_FILE);
|
|
574
|
+
await withFileLock(file, () =>
|
|
575
|
+
provenanceFs().replace(file, JSON.stringify(state, null, 2) + "\n"),
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Write a sidecar under the provenance tree (`parts` relative to it, e.g.
|
|
581
|
+
* `["udt", "<stem>.yaml"]`), creating the directories on the way down and
|
|
582
|
+
* never overwriting: an existing file at the name is the record, since the
|
|
583
|
+
* callers put a uuid in the name. Exclusive create refuses a planted symlink
|
|
584
|
+
* too, the way the attempt records do.
|
|
585
|
+
*/
|
|
586
|
+
export async function writeProvenanceSidecar(
|
|
587
|
+
analysisDir: string,
|
|
588
|
+
parts: readonly string[],
|
|
589
|
+
text: string,
|
|
590
|
+
): Promise<void> {
|
|
591
|
+
let dir = analysisDir;
|
|
592
|
+
for (const part of [...PROVENANCE_DIR_PARTS, ...parts.slice(0, -1)]) {
|
|
593
|
+
dir = path.join(dir, part);
|
|
594
|
+
await provenanceFs().mkdir(dir);
|
|
595
|
+
}
|
|
596
|
+
try {
|
|
597
|
+
await provenanceFs().writeExclusive(path.join(dir, parts[parts.length - 1]), text);
|
|
598
|
+
} catch (err) {
|
|
599
|
+
if ((err as NodeJS.ErrnoException)?.code !== "EEXIST") throw err;
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Every attempt record in the analysis. Unreadable or planted files are
|
|
605
|
+
* skipped: this feeds decisions (which history is bound, which ids are
|
|
606
|
+
* claimed), and only a well-formed record named for its own attempt counts.
|
|
607
|
+
*/
|
|
608
|
+
export async function listAttemptRecords(analysisDir: string): Promise<AttemptRecord[]> {
|
|
609
|
+
const dir = await ensureProvenanceDir(analysisDir, false);
|
|
610
|
+
if (!dir) return [];
|
|
611
|
+
const out: AttemptRecord[] = [];
|
|
612
|
+
for (const name of await provenanceFs().readdir(dir)) {
|
|
613
|
+
const id = name.endsWith(".json") ? name.slice(0, -5) : "";
|
|
614
|
+
if (!isUlid(id)) continue;
|
|
615
|
+
try {
|
|
616
|
+
const record = await readAttemptRecord(analysisDir, id);
|
|
617
|
+
if (record) out.push(record);
|
|
618
|
+
} catch {
|
|
619
|
+
// Not a record this module wrote.
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
return out;
|
|
623
|
+
}
|