@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.
Files changed (451) hide show
  1. package/CHANGELOG.md +123 -0
  2. package/LICENSE +21 -0
  3. package/README.md +760 -5
  4. package/bin/legacy-mcp-config.js +363 -0
  5. package/bin/orbit.js +810 -0
  6. package/bin/pi-extension-path.d.ts +4 -0
  7. package/bin/pi-extension-path.js +44 -0
  8. package/bin/provider-auth.d.ts +22 -0
  9. package/bin/provider-auth.js +77 -0
  10. package/bin/thinking-pref.js +38 -0
  11. package/bin/update-check.d.ts +15 -0
  12. package/bin/update-check.js +137 -0
  13. package/extensions/cli-update/index.ts +32 -0
  14. package/extensions/orbit/activity-hooks.ts +141 -0
  15. package/extensions/orbit/activity.ts +116 -0
  16. package/extensions/orbit/agent-dir.ts +11 -0
  17. package/extensions/orbit/auto-resume.ts +352 -0
  18. package/extensions/orbit/config.ts +3 -0
  19. package/extensions/orbit/confusables.ts +1 -0
  20. package/extensions/orbit/context.ts +957 -0
  21. package/extensions/orbit/dashboard-commands.ts +191 -0
  22. package/extensions/orbit/dashboard-store.ts +473 -0
  23. package/extensions/orbit/dashboard-tools.ts +1185 -0
  24. package/extensions/orbit/empty-completion.ts +99 -0
  25. package/extensions/orbit/evidence-gate.ts +1276 -0
  26. package/extensions/orbit/evidence-override-command.ts +294 -0
  27. package/extensions/orbit/exec-guard/bash-risk.ts +1408 -0
  28. package/extensions/orbit/exec-guard/gate.ts +181 -0
  29. package/extensions/orbit/exec-guard/guardian-config.ts +54 -0
  30. package/extensions/orbit/exec-guard/index.ts +1 -0
  31. package/extensions/orbit/exec-guard/model-tier.ts +36 -0
  32. package/extensions/orbit/exec-guard/path-jail.ts +54 -0
  33. package/extensions/orbit/exec-guard/policy.ts +342 -0
  34. package/extensions/orbit/exec-guard/runtime-state.ts +15 -0
  35. package/extensions/orbit/exec-guard/sensitive-read.ts +525 -0
  36. package/extensions/orbit/exec-guard/types.ts +42 -0
  37. package/extensions/orbit/execution-commands.ts +134 -0
  38. package/extensions/orbit/feedback-command.ts +81 -0
  39. package/extensions/orbit/feedback.ts +96 -0
  40. package/extensions/orbit/galaxy-api.test.ts +122 -0
  41. package/extensions/orbit/galaxy-api.ts +606 -0
  42. package/extensions/orbit/galaxy-artifact-link-guidance.ts +5 -0
  43. package/extensions/orbit/galaxy-destructive-gate.ts +113 -0
  44. package/extensions/orbit/galaxy-enrich.ts +1108 -0
  45. package/extensions/orbit/galaxy-failure.ts +101 -0
  46. package/extensions/orbit/galaxy-fixture.ts +126 -0
  47. package/extensions/orbit/galaxy-history-guard.ts +233 -0
  48. package/extensions/orbit/galaxy-job-block.ts +347 -0
  49. package/extensions/orbit/galaxy-job-ops.ts +288 -0
  50. package/extensions/orbit/galaxy-live-source.ts +929 -0
  51. package/extensions/orbit/galaxy-markdown-adapter.ts +248 -0
  52. package/extensions/orbit/galaxy-op-policy.ts +332 -0
  53. package/extensions/orbit/galaxy-ops-tools.ts +326 -0
  54. package/extensions/orbit/galaxy-output-store.ts +80 -0
  55. package/extensions/orbit/galaxy-output.ts +346 -0
  56. package/extensions/orbit/galaxy-page-binding.ts +222 -0
  57. package/extensions/orbit/galaxy-page-markdown-guidance.ts +15 -0
  58. package/extensions/orbit/galaxy-page-ops.ts +159 -0
  59. package/extensions/orbit/galaxy-page-sync.test.ts +417 -0
  60. package/extensions/orbit/galaxy-page-sync.ts +318 -0
  61. package/extensions/orbit/galaxy-pages-api.ts +203 -0
  62. package/extensions/orbit/galaxy-pages-sync.ts +409 -0
  63. package/extensions/orbit/galaxy-poll-guard.ts +189 -0
  64. package/extensions/orbit/galaxy-poller.ts +706 -0
  65. package/extensions/orbit/galaxy-provenance.ts +623 -0
  66. package/extensions/orbit/galaxy-reconcile.ts +956 -0
  67. package/extensions/orbit/galaxy-recovery.ts +176 -0
  68. package/extensions/orbit/galaxy-repeat-guard.ts +55 -0
  69. package/extensions/orbit/galaxy-submission-capture.ts +707 -0
  70. package/extensions/orbit/galaxy-submission.ts +502 -0
  71. package/extensions/orbit/galaxy-tool-source.ts +78 -0
  72. package/extensions/orbit/galaxy-tool-wrap.ts +234 -0
  73. package/extensions/orbit/galaxy-udt-block.ts +127 -0
  74. package/extensions/orbit/galaxy-upload-tus.ts +334 -0
  75. package/extensions/orbit/galaxy-upload.ts +300 -0
  76. package/extensions/orbit/galaxy-version-capture.ts +65 -0
  77. package/extensions/orbit/git.ts +173 -0
  78. package/extensions/orbit/harness-block-fields.ts +597 -0
  79. package/extensions/orbit/index.ts +593 -0
  80. package/extensions/orbit/init-gate.ts +284 -0
  81. package/extensions/orbit/instructions-command.ts +136 -0
  82. package/extensions/orbit/invocation-failure-hint.ts +57 -0
  83. package/extensions/orbit/iwc-plan-check.ts +94 -0
  84. package/extensions/orbit/lesson-command.ts +303 -0
  85. package/extensions/orbit/lesson-hint.ts +245 -0
  86. package/extensions/orbit/lesson-nudge.ts +106 -0
  87. package/extensions/orbit/lessons/compose.ts +177 -0
  88. package/extensions/orbit/lessons/counters.ts +120 -0
  89. package/extensions/orbit/lessons/enabled.ts +116 -0
  90. package/extensions/orbit/lessons/matcher.ts +346 -0
  91. package/extensions/orbit/lessons/paths.ts +286 -0
  92. package/extensions/orbit/lessons/pi-event-contract.ts +105 -0
  93. package/extensions/orbit/lessons/propose-replay.ts +103 -0
  94. package/extensions/orbit/lessons/propose-tool.ts +222 -0
  95. package/extensions/orbit/lessons/propose.ts +502 -0
  96. package/extensions/orbit/lessons/replay.ts +89 -0
  97. package/extensions/orbit/lessons/reproduction-index.ts +83 -0
  98. package/extensions/orbit/lessons/search-tool.ts +99 -0
  99. package/extensions/orbit/lessons/search.ts +136 -0
  100. package/extensions/orbit/lessons/store.ts +415 -0
  101. package/extensions/orbit/lessons/text.ts +36 -0
  102. package/extensions/orbit/lessons/types.ts +69 -0
  103. package/extensions/orbit/lessons/user-dir.ts +24 -0
  104. package/extensions/orbit/lessons/user-lesson.ts +88 -0
  105. package/extensions/orbit/lessons/wrapper.ts +61 -0
  106. package/extensions/orbit/lessons-command.ts +147 -0
  107. package/extensions/orbit/local-exec.ts +39 -0
  108. package/extensions/orbit/mcp-output.ts +179 -0
  109. package/extensions/orbit/mcp-servers.test.ts +24 -0
  110. package/extensions/orbit/mcp-servers.ts +25 -0
  111. package/extensions/orbit/model-script.ts +351 -0
  112. package/extensions/orbit/notebook-anchors.ts +342 -0
  113. package/extensions/orbit/notebook-writer.ts +863 -0
  114. package/extensions/orbit/observation-allowlists.ts +1109 -0
  115. package/extensions/orbit/observation-replay.ts +148 -0
  116. package/extensions/orbit/observation-triggers.ts +563 -0
  117. package/extensions/orbit/observation-ui.ts +175 -0
  118. package/extensions/orbit/observations-command.ts +421 -0
  119. package/extensions/orbit/observations-config.ts +200 -0
  120. package/extensions/orbit/observations.ts +1381 -0
  121. package/extensions/orbit/profiles.ts +270 -0
  122. package/extensions/orbit/progress-updates.ts +95 -0
  123. package/extensions/orbit/proposal-commands.ts +882 -0
  124. package/extensions/orbit/provenance-fs-file.ts +59 -0
  125. package/extensions/orbit/provenance-fs.ts +32 -0
  126. package/extensions/orbit/provenance-memory-fs.ts +59 -0
  127. package/extensions/orbit/record-store-file.ts +177 -0
  128. package/extensions/orbit/record-store-page.ts +77 -0
  129. package/extensions/orbit/record-store-seam.ts +47 -0
  130. package/extensions/orbit/record-store.ts +149 -0
  131. package/extensions/orbit/registry-carrier.ts +107 -0
  132. package/extensions/orbit/registry-eligibility.ts +95 -0
  133. package/extensions/orbit/registry-evaluation.ts +403 -0
  134. package/extensions/orbit/registry-evaluator.ts +519 -0
  135. package/extensions/orbit/registry-import.ts +91 -0
  136. package/extensions/orbit/registry-lock.ts +221 -0
  137. package/extensions/orbit/registry-memory-fs.ts +42 -0
  138. package/extensions/orbit/registry-page-carrier.ts +103 -0
  139. package/extensions/orbit/registry-proposal.ts +1348 -0
  140. package/extensions/orbit/registry-runtime.ts +234 -0
  141. package/extensions/orbit/registry-schema.ts +735 -0
  142. package/extensions/orbit/registry-submitter.ts +59 -0
  143. package/extensions/orbit/registry-templates.ts +346 -0
  144. package/extensions/orbit/registry.ts +476 -0
  145. package/extensions/orbit/sandbox/index.ts +120 -0
  146. package/extensions/orbit/sandbox/sandbox-bash.ts +93 -0
  147. package/extensions/orbit/sandbox/sandbox-config.ts +87 -0
  148. package/extensions/orbit/sandbox/sandbox-init-message.ts +28 -0
  149. package/extensions/orbit/secret-redaction.ts +247 -0
  150. package/extensions/orbit/session-index/cwd.ts +18 -0
  151. package/extensions/orbit/session-index/db.ts +56 -0
  152. package/extensions/orbit/session-index/indexer.ts +154 -0
  153. package/extensions/orbit/session-index/is-enabled.ts +17 -0
  154. package/extensions/orbit/session-index/parse.ts +254 -0
  155. package/extensions/orbit/session-index/query.ts +209 -0
  156. package/extensions/orbit/session-index/schema.ts +72 -0
  157. package/extensions/orbit/session-index/tools.ts +152 -0
  158. package/extensions/orbit/session-lifecycle.ts +321 -0
  159. package/extensions/orbit/skill-triggers.ts +159 -0
  160. package/extensions/orbit/skills-command.ts +48 -0
  161. package/extensions/orbit/skills-discovery.ts +435 -0
  162. package/extensions/orbit/skills.ts +84 -0
  163. package/extensions/orbit/sra-import-gate.ts +77 -0
  164. package/extensions/orbit/state.ts +265 -0
  165. package/extensions/orbit/submission-replay.ts +147 -0
  166. package/extensions/orbit/sync-command.ts +157 -0
  167. package/extensions/orbit/teams/critic-parser.ts +93 -0
  168. package/extensions/orbit/teams/dispatcher.ts +210 -0
  169. package/extensions/orbit/teams/is-enabled.ts +22 -0
  170. package/extensions/orbit/teams/tool.ts +187 -0
  171. package/extensions/orbit/teams/types.ts +57 -0
  172. package/extensions/orbit/teams/validate.ts +42 -0
  173. package/extensions/orbit/tester-id-command.ts +133 -0
  174. package/extensions/orbit/tools-sync.ts +279 -0
  175. package/extensions/orbit/tools.ts +1300 -0
  176. package/extensions/orbit/types.ts +41 -0
  177. package/extensions/orbit/ui-bridge.ts +55 -0
  178. package/extensions/orbit/ulid.ts +60 -0
  179. package/extensions/orbit/user-instructions.ts +388 -0
  180. package/extensions/orbit/vendor/skills/_catalog.json +68 -0
  181. package/extensions/orbit/vendor/skills/_manifest.json +1106 -0
  182. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/SKILL.md +112 -0
  183. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/_provenance.json +218 -0
  184. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/_required_tools.json +33 -0
  185. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/_verify.json +4 -0
  186. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/add.json +50 -0
  187. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/draft-extract.json +51 -0
  188. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/draft-next-step.json +42 -0
  189. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/draft-validate.json +140 -0
  190. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/list.json +32 -0
  191. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/notes/galaxy-tool-job-failure-reference.md +137 -0
  192. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/notes/open-requirements-ledger.md +173 -0
  193. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/schemas/galaxy-tool-summary.schema.json +175 -0
  194. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/schemas/galaxy-workflow-draft.schema.json +2727 -0
  195. package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/SKILL.md +82 -0
  196. package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/_provenance.json +133 -0
  197. package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/_required_tools.json +16 -0
  198. package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/_verify.json +4 -0
  199. package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/references/cli/draft-validate.json +140 -0
  200. package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/references/notes/galaxy-workflow-draft-format.md +135 -0
  201. package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/references/notes/open-requirements-ledger.md +173 -0
  202. package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/references/schemas/galaxy-workflow-draft.schema.json +2727 -0
  203. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/SKILL.md +84 -0
  204. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/_provenance.json +166 -0
  205. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/_verify.json +29 -0
  206. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/galaxy-workflow-testability-design.md +136 -0
  207. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/iwc-shortcuts-anti-patterns.md +357 -0
  208. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/iwc-test-data-conventions.md +308 -0
  209. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/planemo-asserts-idioms.md +246 -0
  210. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/planemo-workflow-test-architecture.md +151 -0
  211. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/schemas/galaxy-workflow-test-plan.schema.json +552 -0
  212. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/schemas/summary-galaxy-workflow.schema.json +687 -0
  213. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/schemas/tests-format.schema.json +7328 -0
  214. package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/SKILL.md +127 -0
  215. package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/_provenance.json +198 -0
  216. package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/_required_tools.json +16 -0
  217. package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/_verify.json +4 -0
  218. package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/cli/convert.json +123 -0
  219. package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/notes/galaxy-data-flow-draft-contract.md +129 -0
  220. package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/notes/iwc-shortcuts-anti-patterns.md +357 -0
  221. package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/notes/iwc-test-data-conventions.md +308 -0
  222. package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/notes/open-requirements-ledger.md +173 -0
  223. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/SKILL.md +73 -0
  224. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/_provenance.json +160 -0
  225. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/_verify.json +4 -0
  226. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/galaxy-collection-semantics.md +43 -0
  227. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/galaxy-collection-semantics.yml +1250 -0
  228. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/galaxy-tool-job-failure-reference.md +137 -0
  229. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/galaxy-workflow-invocation-failure-reference.md +138 -0
  230. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/iwc-shortcuts-anti-patterns.md +357 -0
  231. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/nextflow-operators-to-galaxy-collection-recipes.md +98 -0
  232. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/planemo-asserts-idioms.md +246 -0
  233. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/planemo-workflow-test-architecture.md +151 -0
  234. package/extensions/orbit/vendor/skills/discover-shed-tool/SKILL.md +174 -0
  235. package/extensions/orbit/vendor/skills/discover-shed-tool/_provenance.json +130 -0
  236. package/extensions/orbit/vendor/skills/discover-shed-tool/_required_tools.json +18 -0
  237. package/extensions/orbit/vendor/skills/discover-shed-tool/_verify.json +17 -0
  238. package/extensions/orbit/vendor/skills/discover-shed-tool/references/cli/tool-revisions.json +48 -0
  239. package/extensions/orbit/vendor/skills/discover-shed-tool/references/cli/tool-search.json +95 -0
  240. package/extensions/orbit/vendor/skills/discover-shed-tool/references/cli/tool-versions.json +39 -0
  241. package/extensions/orbit/vendor/skills/discover-shed-tool/references/notes/component-tool-shed-search.md +230 -0
  242. package/extensions/orbit/vendor/skills/discover-shed-tool/references/schemas/galaxy-tool-discovery.schema.json +231 -0
  243. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/SKILL.md +93 -0
  244. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/_provenance.json +201 -0
  245. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/_required_tools.json +28 -0
  246. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/_verify.json +17 -0
  247. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/cli/planemo.md +44 -0
  248. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/cli/validate-tests.json +40 -0
  249. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/galaxy-workflow-testability-design.md +136 -0
  250. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/iwc-shortcuts-anti-patterns.md +357 -0
  251. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/iwc-test-data-conventions.md +308 -0
  252. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/planemo-asserts-idioms.md +246 -0
  253. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/planemo-workflow-test-architecture.md +151 -0
  254. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/schemas/galaxy-workflow-test-plan.schema.json +552 -0
  255. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/schemas/tests-format.schema.json +7328 -0
  256. package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/SKILL.md +81 -0
  257. package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/_provenance.json +93 -0
  258. package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/_verify.json +17 -0
  259. package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/references/notes/open-requirements-ledger.md +173 -0
  260. package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/references/schemas/summary-galaxy-workflow.schema.json +687 -0
  261. package/extensions/orbit/vendor/skills/pipeline-update-interview-to-galaxy/SKILL.md +81 -0
  262. package/extensions/orbit/vendor/skills/pipeline-update-interview-to-galaxy/_assembly.json +23 -0
  263. package/extensions/orbit/vendor/skills/run-workflow-test/SKILL.md +79 -0
  264. package/extensions/orbit/vendor/skills/run-workflow-test/_provenance.json +125 -0
  265. package/extensions/orbit/vendor/skills/run-workflow-test/_required_tools.json +28 -0
  266. package/extensions/orbit/vendor/skills/run-workflow-test/_verify.json +4 -0
  267. package/extensions/orbit/vendor/skills/run-workflow-test/references/cli/planemo.md +44 -0
  268. package/extensions/orbit/vendor/skills/run-workflow-test/references/cli/validate-tests.json +40 -0
  269. package/extensions/orbit/vendor/skills/run-workflow-test/references/notes/galaxy-workflow-invocation-failure-reference.md +138 -0
  270. package/extensions/orbit/vendor/skills/run-workflow-test/references/notes/planemo-asserts-idioms.md +246 -0
  271. package/extensions/orbit/vendor/skills/run-workflow-test/references/notes/planemo-workflow-test-architecture.md +151 -0
  272. package/extensions/orbit/vendor/skills/run-workflow-test/references/schemas/tests-format.schema.json +7328 -0
  273. package/extensions/orbit/vendor/skills/skills/LICENSE +21 -0
  274. package/extensions/orbit/vendor/skills/skills/collection-manipulation/README.md +39 -0
  275. package/extensions/orbit/vendor/skills/skills/collection-manipulation/SKILL.md +892 -0
  276. package/extensions/orbit/vendor/skills/skills/collection-manipulation/references/api-patterns.md +927 -0
  277. package/extensions/orbit/vendor/skills/skills/collection-manipulation/references/apply-rules.md +1482 -0
  278. package/extensions/orbit/vendor/skills/skills/collection-manipulation/references/test-patterns.md +996 -0
  279. package/extensions/orbit/vendor/skills/skills/collection-manipulation/references/tools.md +609 -0
  280. package/extensions/orbit/vendor/skills/skills/galaxy-data-libraries/SKILL.md +170 -0
  281. package/extensions/orbit/vendor/skills/skills/galaxy-integration/README.md +162 -0
  282. package/extensions/orbit/vendor/skills/skills/galaxy-integration/SKILL.md +99 -0
  283. package/extensions/orbit/vendor/skills/skills/galaxy-integration/examples/README.md +119 -0
  284. package/extensions/orbit/vendor/skills/skills/galaxy-integration/examples/tool-checking.md +419 -0
  285. package/extensions/orbit/vendor/skills/skills/galaxy-integration/examples/workflow-testing.md +617 -0
  286. package/extensions/orbit/vendor/skills/skills/galaxy-integration/galaxy-integration.md +614 -0
  287. package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/SKILL.md +144 -0
  288. package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/examples/average_col3.ipynb +68 -0
  289. package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/examples/extract_sample_metadata.ipynb +190 -0
  290. package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/examples/variant_annotation.ipynb +816 -0
  291. package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/examples/vcp_variant_map.ipynb +443 -0
  292. package/extensions/orbit/vendor/skills/skills/galaxy-integration/scripts/README.md +165 -0
  293. package/extensions/orbit/vendor/skills/skills/galaxy-integration/scripts/galaxy_tool_checker.py +521 -0
  294. package/extensions/orbit/vendor/skills/skills/galaxy-mcp-reference/SKILL.md +128 -0
  295. package/extensions/orbit/vendor/skills/skills/galaxy-mcp-reference/gotchas.md +135 -0
  296. package/extensions/orbit/vendor/skills/skills/galaxy-mcp-reference/history-access.md +132 -0
  297. package/extensions/orbit/vendor/skills/skills/reproduciblify/SKILL.md +146 -0
  298. package/extensions/orbit/vendor/skills/skills/reproduciblify/references/directives.yml +496 -0
  299. package/extensions/orbit/vendor/skills/skills/udt-authoring/SKILL.md +245 -0
  300. package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/01-minimal-echo.yml +27 -0
  301. package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/02-single-data-in-out.yml +27 -0
  302. package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/03-multiple-data-js.yml +28 -0
  303. package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/04-select-integer-boolean.yml +41 -0
  304. package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/05-resource-requirements.yml +25 -0
  305. package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/06-biocontainer-tool.yml +38 -0
  306. package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/07-inline-python-script.yml +56 -0
  307. package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/08-configfile-script.yml +74 -0
  308. package/extensions/orbit/vendor/skills/skills/udt-authoring/references/common-mistakes.md +76 -0
  309. package/extensions/orbit/vendor/skills/skills/udt-authoring/references/schema-reference.md +155 -0
  310. package/extensions/orbit/vendor/skills/skills/udt-authoring/references/templating.md +196 -0
  311. package/extensions/orbit/vendor/skills/skills/udt-authoring/scripts/validate.py +109 -0
  312. package/extensions/orbit/vendor/skills/skills/workflow-reports/README.md +71 -0
  313. package/extensions/orbit/vendor/skills/skills/workflow-reports/SKILL.md +190 -0
  314. package/extensions/orbit/vendor/skills/skills/workflow-reports/examples/histology-staining.md +145 -0
  315. package/extensions/orbit/vendor/skills/skills/workflow-reports/examples/tissue-microarray-analysis.md +176 -0
  316. package/extensions/orbit/vendor/skills/skills/workflow-reports/references/directives.md +415 -0
  317. package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/SKILL.md +98 -0
  318. package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/_provenance.json +93 -0
  319. package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/_required_tools.json +28 -0
  320. package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/_verify.json +17 -0
  321. package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/references/cli/convert.json +123 -0
  322. package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/references/cli/foundry.md +25 -0
  323. package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/references/cli/validate.json +142 -0
  324. package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/references/schemas/summary-galaxy-workflow.schema.json +687 -0
  325. package/extensions/orbit/vendor/skills/validate-galaxy-workflow/SKILL.md +74 -0
  326. package/extensions/orbit/vendor/skills/validate-galaxy-workflow/_provenance.json +71 -0
  327. package/extensions/orbit/vendor/skills/validate-galaxy-workflow/_required_tools.json +16 -0
  328. package/extensions/orbit/vendor/skills/validate-galaxy-workflow/_verify.json +4 -0
  329. package/extensions/orbit/vendor/skills/validate-galaxy-workflow/references/cli/validate.json +142 -0
  330. package/extensions/orbit/vendor/skills/validate-galaxy-workflow/references/notes/galaxy-workflow-invocation-failure-reference.md +138 -0
  331. package/extensions/orbit/vendor/skills/validate-galaxy-workflow/references/notes/galaxy-workflow-testability-design.md +136 -0
  332. package/extensions/orbit/vendor-skills.ts +283 -0
  333. package/extensions/orbit/workspace-state-dir.ts +46 -0
  334. package/extensions/orbit-handoff/index.ts +73 -0
  335. package/extensions/orbit-handoff/orbit-launcher.ts +142 -0
  336. package/extensions/whats-new/index.ts +98 -0
  337. package/lessons/LICENSE +17 -0
  338. package/lessons/README.md +117 -0
  339. package/lessons/SCHEMA.md +187 -0
  340. package/lessons/build-snapshot.mjs +311 -0
  341. package/lessons/data/downloaded-file-is-not-what-its-extension-says.md +61 -0
  342. package/lessons/galaxy-api/403-history-is-a-hard-stop.md +57 -0
  343. package/lessons/galaxy-api/collection-into-single-dataset-input.md +61 -0
  344. package/lessons/galaxy-api/connectedvalue-in-command-line.md +62 -0
  345. package/lessons/galaxy-api/hid-is-not-an-id.md +59 -0
  346. package/lessons/galaxy-api/invoke-workflow-inputs-not-params.md +60 -0
  347. package/lessons/galaxy-api/repeat-param-pipe-keys.md +63 -0
  348. package/lessons/galaxy-api/run-tool-returns-on-submit.md +61 -0
  349. package/lessons/galaxy-tools/reference-index-not-on-server.md +62 -0
  350. package/lessons/log.md +42 -0
  351. package/lessons/reproduction/input-population-mismatch.md +59 -0
  352. package/lessons/reproduction/methods-text-vs-executed-parameters.md +62 -0
  353. package/lessons/snapshot.json +1004 -0
  354. package/lessons/stats/de-contrast-direction-and-sample-labels.md +63 -0
  355. package/lessons/stats/na-coerced-to-zero-in-filters.md +59 -0
  356. package/lessons/validate.mjs +1219 -0
  357. package/package.json +103 -5
  358. package/shared/agent-prompt.d.ts +8 -0
  359. package/shared/agent-prompt.js +15 -0
  360. package/shared/approval-prompt.d.ts +4 -0
  361. package/shared/approval-prompt.js +61 -0
  362. package/shared/brain-env.d.ts +14 -0
  363. package/shared/brain-env.js +108 -0
  364. package/shared/brain-exit.d.ts +20 -0
  365. package/shared/brain-exit.js +149 -0
  366. package/shared/capability-profile.d.ts +16 -0
  367. package/shared/capability-profile.js +75 -0
  368. package/shared/config-source.d.ts +13 -0
  369. package/shared/config-source.js +17 -0
  370. package/shared/confusables.d.ts +3 -0
  371. package/shared/confusables.js +93 -0
  372. package/shared/custom-provider.d.ts +45 -0
  373. package/shared/custom-provider.js +169 -0
  374. package/shared/dashboard-contract.d.ts +123 -0
  375. package/shared/dashboard-contract.js +560 -0
  376. package/shared/dashboard-layout-store.d.ts +44 -0
  377. package/shared/dashboard-layout-store.js +281 -0
  378. package/shared/feedback-contract.d.ts +61 -0
  379. package/shared/feedback-contract.js +146 -0
  380. package/shared/galaxy-artifact-links.d.ts +30 -0
  381. package/shared/galaxy-artifact-links.js +166 -0
  382. package/shared/galaxy-credentials.d.ts +26 -0
  383. package/shared/galaxy-credentials.js +71 -0
  384. package/shared/galaxy-destructive.d.ts +21 -0
  385. package/shared/galaxy-destructive.js +269 -0
  386. package/shared/galaxy-guidance.d.ts +2 -0
  387. package/shared/galaxy-guidance.js +53 -0
  388. package/shared/galaxy-live-contract.d.ts +92 -0
  389. package/shared/galaxy-live-contract.js +180 -0
  390. package/shared/galaxy-page-cells.d.ts +2 -0
  391. package/shared/galaxy-page-cells.js +22 -0
  392. package/shared/galaxy-tool-family.d.ts +5 -0
  393. package/shared/galaxy-tool-family.js +27 -0
  394. package/shared/galaxy-url.d.ts +1 -0
  395. package/shared/galaxy-url.js +48 -0
  396. package/shared/gtn.d.ts +31 -0
  397. package/shared/gtn.js +253 -0
  398. package/shared/handled-prompt.d.ts +3 -0
  399. package/shared/handled-prompt.js +26 -0
  400. package/shared/known-provider.d.ts +11 -0
  401. package/shared/known-provider.js +89 -0
  402. package/shared/lesson-hint-marker.d.ts +7 -0
  403. package/shared/lesson-hint-marker.js +32 -0
  404. package/shared/lesson-rules.d.ts +150 -0
  405. package/shared/lesson-rules.js +203 -0
  406. package/shared/malformed-object-ids.d.ts +1 -0
  407. package/shared/malformed-object-ids.js +37 -0
  408. package/shared/markdown-sections.d.ts +10 -0
  409. package/shared/markdown-sections.js +118 -0
  410. package/shared/notebook-fences.d.ts +12 -0
  411. package/shared/notebook-fences.js +53 -0
  412. package/shared/observation-contract.d.ts +91 -0
  413. package/shared/observation-contract.js +544 -0
  414. package/shared/orbit-config.d.ts +226 -0
  415. package/shared/orbit-config.js +189 -0
  416. package/shared/orbit-env.d.ts +13 -0
  417. package/shared/orbit-env.js +124 -0
  418. package/shared/orbit-shell-contract.d.ts +74 -0
  419. package/shared/orbit-shell-contract.js +30 -0
  420. package/shared/page-content-hash.d.ts +1 -0
  421. package/shared/page-content-hash.js +13 -0
  422. package/shared/pi-default-model.d.ts +7 -0
  423. package/shared/pi-default-model.js +41 -0
  424. package/shared/prompt-blocks.d.ts +87 -0
  425. package/shared/prompt-blocks.js +898 -0
  426. package/shared/provider-auth-caps.d.ts +22 -0
  427. package/shared/provider-auth-caps.js +57 -0
  428. package/shared/random.d.ts +2 -0
  429. package/shared/random.js +15 -0
  430. package/shared/record-excerpt.d.ts +4 -0
  431. package/shared/record-excerpt.js +16 -0
  432. package/shared/redirect-guard.d.ts +36 -0
  433. package/shared/redirect-guard.js +235 -0
  434. package/shared/skills-pin.d.ts +8 -0
  435. package/shared/skills-pin.js +9 -0
  436. package/shared/skills-router.d.ts +17 -0
  437. package/shared/skills-router.js +63 -0
  438. package/shared/sra-import.d.ts +34 -0
  439. package/shared/sra-import.js +206 -0
  440. package/shared/state-dir.d.ts +21 -0
  441. package/shared/state-dir.js +240 -0
  442. package/shared/team-dispatch-contract.d.ts +28 -0
  443. package/shared/team-dispatch-contract.js +1 -0
  444. package/shared/truncate-middle.d.ts +11 -0
  445. package/shared/truncate-middle.js +68 -0
  446. package/shared/version-compare.d.ts +10 -0
  447. package/shared/version-compare.js +74 -0
  448. package/shared/whats-new.d.ts +31 -0
  449. package/shared/whats-new.js +83 -0
  450. package/shared/wsl.d.ts +10 -0
  451. 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
+ }