@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,502 @@
1
+ /**
2
+ * The proposal core: arming, then validate -> record -> show -> approve -> write.
3
+ *
4
+ * The order is the design. A draft is assembled from session content that may
5
+ * include hostile tool output, so:
6
+ *
7
+ * 1. an unarmed proposal is refused -- only /lesson and the user-correction
8
+ * nudge arm one, which keeps "proposes at a cue, never in the background"
9
+ * true in code and not just in the prompt;
10
+ * 2. the composed BYTES are validated before anything is shown or stored;
11
+ * 3. activity records structured facts only, never the body: activity.jsonl
12
+ * sits in the project directory and feeds /feedback;
13
+ * 4. only then is the draft displayed, which is safe because the validator
14
+ * already ruled out URLs, fences and invisible characters;
15
+ * 5. the user answers. No UI means the approval control is absent, so the
16
+ * answer is refuse -- pi's no-op UI returns false/undefined, so a headless
17
+ * run cannot become an implicit yes;
18
+ * 6. the write is create-or-fail. Nothing here ever edits a lesson.
19
+ *
20
+ * ctx.ui.editor is NOT used: Orbit's renderer has no editor branch and pi's
21
+ * RPC editor only settles on a host response, so an editor call there hangs the
22
+ * command forever. The "let me edit it" branch stages a file and hands over
23
+ * the path.
24
+ */
25
+
26
+ import * as fs from "fs";
27
+ import { isLessonsEnabled, LESSONS_OFF_POINTER } from "./enabled";
28
+ import * as path from "path";
29
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
30
+ import { appendActivityEvent } from "../activity.js";
31
+ import { readOrbitVersion } from "../feedback.js";
32
+ import { getNotebookPath } from "../state.js";
33
+ import {
34
+ PROPOSABLE_NAMESPACES,
35
+ TRIGGER_KEYS,
36
+ identifyingProblems,
37
+ isValidLessonSlug,
38
+ parseLesson,
39
+ validateLessonMarkdown,
40
+ } from "../../../shared/lesson-rules.js";
41
+ import {
42
+ composeLessonMarkdown,
43
+ renderProposalPreview,
44
+ type LessonProposalInput,
45
+ } from "./compose.js";
46
+ import {
47
+ draftFilePath,
48
+ lessonFilePath,
49
+ parseLessonId,
50
+ readLessonFile,
51
+ writeNoClobber,
52
+ writeOverwrite,
53
+ } from "./paths.js";
54
+
55
+ export const ACTIVITY_SOURCE = "lesson-command";
56
+
57
+ export const SAVE_OPTION = "Save it";
58
+ export const STAGE_OPTION = "Keep it as a draft I can edit";
59
+ export const DISCARD_OPTION = "Discard it";
60
+
61
+ export type ProposalReason = "explicit" | "user_correction";
62
+
63
+ export type RejectReason =
64
+ | "lessons-off"
65
+ | "unarmed"
66
+ | "validator"
67
+ | "exists"
68
+ | "no-ui"
69
+ | "declined"
70
+ | "staged"
71
+ | "write-failed";
72
+
73
+ export type ProposeOutcome =
74
+ | { ok: true; id: string; path: string }
75
+ | { ok: false; reason: RejectReason; message: string; errors?: string[] };
76
+
77
+ /** The slice of ExtensionContext this module needs, so tests can script it. */
78
+ export interface ProposeUiContext {
79
+ hasUI: boolean;
80
+ ui: {
81
+ notify(message: string, level?: "info" | "warning" | "error"): void;
82
+ // `unknown` rather than `string | undefined`: a host may answer a selector
83
+ // with the chosen index instead of the string; chosenOption handles both.
84
+ select(title: string, options: string[]): Promise<unknown>;
85
+ confirm(title: string, message: string): Promise<boolean>;
86
+ };
87
+ }
88
+
89
+ /**
90
+ * The arming. Three properties, each closing a way a proposal could happen
91
+ * that nobody asked for:
92
+ *
93
+ * - it only counts inside an agent run that STARTED after it was armed. A
94
+ * /lesson typed while a run is streaming must not hand that run -- which may
95
+ * be mid-way through hostile tool output -- a licence to propose;
96
+ * - it expires when that run ends (the nudge's after two, so "offer, then the
97
+ * user says yes" fits), so an unused /lesson does not linger for a later
98
+ * turn to pick up;
99
+ * - one proposal reaches the user per arming. A validator rejection keeps it
100
+ * for one corrected retry, since the model is told it may fix and resubmit.
101
+ *
102
+ * Module-scoped and reset on session_start, so nothing carries across a
103
+ * session swap into a conversation the user never saw.
104
+ */
105
+ export interface Arming {
106
+ reason: ProposalReason;
107
+ live: boolean;
108
+ runsLeft: number;
109
+ retriesLeft: number;
110
+ armedAt: number;
111
+ }
112
+
113
+ let armed: Arming | null = null;
114
+
115
+ /**
116
+ * How long a /lesson arming may wait for its run to start. pi swallows a
117
+ * refused sendUserMessage (auth, preflight), so without a deadline the arming
118
+ * would go live in the user's NEXT, unrelated run instead.
119
+ */
120
+ export const EXPLICIT_ARMING_START_MS = 30_000;
121
+
122
+ /** Returns the arming so a caller can later disarm exactly that one. */
123
+ export function armLessonProposal(reason: ProposalReason, opts: { live?: boolean } = {}): Arming {
124
+ armed = {
125
+ reason,
126
+ live: opts.live === true,
127
+ runsLeft: reason === "user_correction" ? 2 : 1,
128
+ retriesLeft: 1,
129
+ armedAt: Date.now(),
130
+ };
131
+ return armed;
132
+ }
133
+
134
+ /** Disarm only if `arming` is still the current one -- never someone else's. */
135
+ export function disarmLessonProposal(arming: Arming): void {
136
+ if (armed === arming) armed = null;
137
+ }
138
+
139
+ export function peekLessonProposalArming(): ProposalReason | null {
140
+ return armed?.reason ?? null;
141
+ }
142
+
143
+ /** Consume the arming outright. */
144
+ export function takeLessonProposalArming(): ProposalReason | null {
145
+ const reason = armed?.reason ?? null;
146
+ armed = null;
147
+ return reason;
148
+ }
149
+
150
+ export function resetLessonProposalArming(): void {
151
+ armed = null;
152
+ }
153
+
154
+ export function lessonArmingRunStarted(): void {
155
+ if (!armed || armed.live) return;
156
+ // The nudge's arming waits for the user's next prompt, however long that
157
+ // takes; an explicit one is for the run /lesson itself starts.
158
+ if (armed.reason === "explicit" && Date.now() - armed.armedAt > EXPLICIT_ARMING_START_MS) {
159
+ armed = null;
160
+ return;
161
+ }
162
+ armed.live = true;
163
+ }
164
+
165
+ export function lessonArmingRunEnded(): void {
166
+ if (!armed || !armed.live) return;
167
+ armed.live = false;
168
+ armed.runsLeft -= 1;
169
+ if (armed.runsLeft <= 0) armed = null;
170
+ }
171
+
172
+ export function registerLessonArmingLifecycle(pi: ExtensionAPI): void {
173
+ pi.on("session_start", async () => resetLessonProposalArming());
174
+ pi.on("agent_start", async () => lessonArmingRunStarted());
175
+ pi.on("agent_end", async () => lessonArmingRunEnded());
176
+ }
177
+
178
+ function sessionDir(): string | null {
179
+ const notebook = getNotebookPath();
180
+ return notebook ? path.dirname(notebook) : null;
181
+ }
182
+
183
+ function record(kind: string, payload: Record<string, unknown>): void {
184
+ const dir = sessionDir();
185
+ if (!dir) return;
186
+ appendActivityEvent(dir, {
187
+ timestamp: new Date().toISOString(),
188
+ kind,
189
+ source: ACTIVITY_SOURCE,
190
+ payload,
191
+ });
192
+ }
193
+
194
+ /**
195
+ * Line numbers only. A validator message can quote the offending value, and
196
+ * echoing that into a log that feeds /feedback would give a rejected injection
197
+ * a second delivery -- the same rule the intake route follows.
198
+ */
199
+ function errorLines(errors: string[]): string {
200
+ const lines = new Set<string>();
201
+ for (const error of errors) lines.add(/^(\d+):/.exec(error)?.[1] ?? "?");
202
+ return [...lines].slice(0, 20).join(",");
203
+ }
204
+
205
+ function count(value: unknown): number {
206
+ return Array.isArray(value) ? value.length : 0;
207
+ }
208
+
209
+ function triggerSummary(input: LessonProposalInput): string {
210
+ const trigger = (input?.trigger ?? {}) as Record<string, unknown>;
211
+ return TRIGGER_KEYS.map((key) => `${key}=${count(trigger[key])}`).join(" ");
212
+ }
213
+
214
+ /**
215
+ * Problems with a proposed slug beyond its shape. The slug is model-chosen
216
+ * text that becomes a filename and an activity-row id, so it gets the same
217
+ * identifying-data checks as the lesson body, plus long digit runs (record
218
+ * numbers) -- a name spelled in lowercase words still needs the user's eye.
219
+ */
220
+ function slugProblems(slug: unknown): string[] {
221
+ if (!isValidLessonSlug(slug)) {
222
+ return ["0: slug must be lowercase words joined by hyphens, at most 80 characters"];
223
+ }
224
+ const out = identifyingProblems(slug as string, "slug").map((problem) => `0: ${problem}`);
225
+ if (/\d{5,}/.test(slug as string)) out.push("0: slug contains a long number");
226
+ return out;
227
+ }
228
+
229
+ /** An id fit for an activity row, or a fixed placeholder. */
230
+ function safeId(namespace: unknown, slug: unknown): string {
231
+ return parseLessonId(`${String(namespace)}/${String(slug)}`) && slugProblems(slug).length === 0
232
+ ? `${namespace}/${slug}`
233
+ : "(invalid)";
234
+ }
235
+
236
+ function reject(
237
+ id: string,
238
+ reason: RejectReason,
239
+ message: string,
240
+ errors?: string[],
241
+ ): ProposeOutcome {
242
+ record("lesson.rejected", {
243
+ id,
244
+ reason,
245
+ ...(errors ? { errorCount: errors.length, errorLines: errorLines(errors) } : {}),
246
+ });
247
+ return { ok: false, reason, message, ...(errors ? { errors } : {}) };
248
+ }
249
+
250
+ /**
251
+ * Resolve a selector answer. Interactive mode returns the chosen string; some
252
+ * hosts answer with the index instead (same handling as /connect). Anything
253
+ * else is a dismissal.
254
+ */
255
+ function chosenOption(answer: unknown, options: string[]): string | undefined {
256
+ if (typeof answer === "number") return options[answer];
257
+ if (typeof answer === "string") return options.find((o) => o === answer.trim());
258
+ return undefined;
259
+ }
260
+
261
+ function generatedBy(): string {
262
+ const version = readOrbitVersion();
263
+ return version && /^[A-Za-z0-9.-]{1,40}$/.test(version) ? `agent:loom/${version}` : "agent:loom";
264
+ }
265
+
266
+ function today(): string {
267
+ return new Date().toISOString().slice(0, 10);
268
+ }
269
+
270
+ function saveLesson(id: string, namespace: string, slug: string, markdown: string): ProposeOutcome {
271
+ const target = lessonFilePath(namespace, slug);
272
+ const written = writeNoClobber(target, markdown);
273
+ if (!written.ok) {
274
+ if (written.reason === "exists") {
275
+ return reject(
276
+ id,
277
+ "exists",
278
+ `A lesson already exists at ${id}. Nothing was changed -- lessons are never rewritten ` +
279
+ `in place. Propose it under a different slug if it is a different situation.`,
280
+ );
281
+ }
282
+ return reject(id, "write-failed", `Could not write the lesson (${written.detail ?? "error"}).`);
283
+ }
284
+ record("lesson.saved", { id, bytes: Buffer.byteLength(markdown, "utf-8") });
285
+ return { ok: true, id, path: target };
286
+ }
287
+
288
+ function rejectInvalid(id: string, errors: string[], retry: boolean): ProposeOutcome {
289
+ return reject(
290
+ id,
291
+ "validator",
292
+ `That draft does not satisfy the lesson schema, so nothing was shown or written ` +
293
+ `(each line is <line in the composed lesson>: <problem>):\n` +
294
+ errors.map((e) => ` - ${e}`).join("\n") +
295
+ (retry
296
+ ? `\nFix those and call lesson_propose once more, or tell the user it does not fit.`
297
+ : `\nThat was the last attempt for this /lesson. Tell the user it did not fit the schema.`),
298
+ errors,
299
+ );
300
+ }
301
+
302
+ export async function proposeLesson(
303
+ input: LessonProposalInput,
304
+ ctx: ProposeUiContext,
305
+ ): Promise<ProposeOutcome> {
306
+ const namespace = input?.namespace;
307
+ const slug = input?.slug;
308
+ const id = safeId(namespace, slug);
309
+
310
+ // Before the arming check: an off switch is the user's standing answer, and
311
+ // no /lesson or nudge should have armed anything while it is off.
312
+ if (!isLessonsEnabled()) {
313
+ return reject(id, "lessons-off", `${LESSONS_OFF_POINTER} Nothing was written; tell the user.`);
314
+ }
315
+
316
+ const arming = armed;
317
+ if (!arming || !arming.live) {
318
+ // Nothing the model chose reaches the log from an unarmed call: that
319
+ // would hand a background injection a free row.
320
+ return reject(
321
+ "(unarmed)",
322
+ "unarmed",
323
+ "Lessons are only proposed when the user asks for one. Nobody did -- if something here is " +
324
+ "worth keeping, tell the user they can run /lesson, and move on.",
325
+ );
326
+ }
327
+
328
+ // A failed draft keeps the arming for one corrected retry; anything that
329
+ // gets as far as the user consumes it.
330
+ const failValidation = (errors: string[]): ProposeOutcome => {
331
+ const retry = arming.retriesLeft > 0;
332
+ if (retry) arming.retriesLeft -= 1;
333
+ else armed = null;
334
+ return rejectInvalid(id, errors, retry);
335
+ };
336
+
337
+ // Checked before anything builds a path from them.
338
+ const problems: string[] = [];
339
+ if (typeof namespace !== "string" || !PROPOSABLE_NAMESPACES.includes(namespace as never)) {
340
+ problems.push(`0: namespace must be one of ${PROPOSABLE_NAMESPACES.join(", ")}`);
341
+ }
342
+ problems.push(...slugProblems(slug));
343
+ if (problems.length > 0) return failValidation(problems);
344
+
345
+ const markdown = composeLessonMarkdown(input, {
346
+ generatedBy: generatedBy(),
347
+ generatedAt: today(),
348
+ });
349
+ const validation = validateLessonMarkdown(markdown, { namespace: namespace as string });
350
+ if (!validation.ok) return failValidation(validation.errors);
351
+
352
+ armed = null;
353
+ const ns = namespace as string;
354
+ const sl = slug as string;
355
+ record("lesson.proposed", {
356
+ id,
357
+ // Validated by now, so these are enum values, not free text.
358
+ kind: String(input.kind),
359
+ stages: (input.stage as string[]).join(","),
360
+ trigger: triggerSummary(input),
361
+ bytes: Buffer.byteLength(markdown, "utf-8"),
362
+ armedBy: arming.reason,
363
+ });
364
+
365
+ if (!ctx.hasUI) {
366
+ return reject(
367
+ id,
368
+ "no-ui",
369
+ "A lesson is only written after the user approves it, and this session has no way to ask. " +
370
+ "Nothing was saved. Tell the user to run /lesson in Orbit or an interactive terminal.",
371
+ );
372
+ }
373
+
374
+ const options = [SAVE_OPTION, STAGE_OPTION, DISCARD_OPTION];
375
+ ctx.ui.notify(`Proposed lesson: ${id}\n\n${renderProposalPreview(markdown)}`, "info");
376
+ const choice = chosenOption(await ctx.ui.select(`Save this lesson as ${id}?`, options), options);
377
+
378
+ if (choice === SAVE_OPTION) return saveLesson(id, ns, sl, markdown);
379
+
380
+ if (choice === STAGE_OPTION) {
381
+ const draft = draftFilePath(ns, sl);
382
+ const staged = writeOverwrite(draft, markdown);
383
+ if (!staged.ok) {
384
+ return reject(id, "write-failed", `Could not write the draft (${staged.detail}).`);
385
+ }
386
+ ctx.ui.notify(
387
+ `Draft kept, not yet a lesson:\n ${draft}\n\n` +
388
+ `Edit it, then run /lesson save ${id} to check it against the schema and keep it.`,
389
+ "info",
390
+ );
391
+ return reject(
392
+ id,
393
+ "staged",
394
+ `The user kept the draft to edit. It is not a lesson yet. Do not propose it again -- they ` +
395
+ `will run /lesson save when they are done.`,
396
+ );
397
+ }
398
+
399
+ return reject(
400
+ id,
401
+ "declined",
402
+ "The user declined that lesson. Nothing was written. Do not propose it again unless they ask.",
403
+ );
404
+ }
405
+
406
+ /**
407
+ * Promote a staged draft the user has edited.
408
+ *
409
+ * The bytes are re-validated rather than trusted: a draft is an ordinary file,
410
+ * so between staging and committing anything could have happened to it -- the
411
+ * user's edit, another tool, a model with file access. The validator is the
412
+ * gate every time the bytes change.
413
+ */
414
+ export async function commitDraft(rawId: string, ctx: ProposeUiContext): Promise<ProposeOutcome> {
415
+ const parsed = parseLessonId(rawId);
416
+ if (!parsed || !PROPOSABLE_NAMESPACES.includes(parsed.namespace as never)) {
417
+ return reject("(invalid)", "validator", "That is not the id of a draft you can save.", [
418
+ "0: not <namespace>/<slug> in a proposable namespace",
419
+ ]);
420
+ }
421
+ const { namespace, slug } = parsed;
422
+ const id = `${namespace}/${slug}`;
423
+
424
+ const draft = draftFilePath(namespace, slug);
425
+ const read = readLessonFile(draft);
426
+ if (!read.ok) {
427
+ return reject(id, "validator", `No draft for ${id} (${read.detail}).`, ["0: draft missing"]);
428
+ }
429
+
430
+ const validation = validateLessonMarkdown(read.text, { namespace });
431
+ if (!validation.ok) {
432
+ return reject(
433
+ id,
434
+ "validator",
435
+ `That draft still does not satisfy the lesson schema, so nothing was saved ` +
436
+ `(each line is <line in the draft>: <problem>):\n` +
437
+ validation.errors.map((e) => ` - ${e}`).join("\n"),
438
+ validation.errors,
439
+ );
440
+ }
441
+
442
+ // The schema allows stable and verified because the corpus uses them, but
443
+ // standing is granted by a human reviewer in the corpus repo, never by an
444
+ // edit to a local file -- which a model with file access could make too.
445
+ const fm = (parseLesson(read.text).frontmatter ?? {}) as Record<string, unknown>;
446
+ const generated = fm.generated as { by?: unknown } | undefined;
447
+ const empty = (key: string) => Array.isArray(fm[key]) && (fm[key] as unknown[]).length === 0;
448
+ if (
449
+ fm.status !== "draft" ||
450
+ "verified" in fm ||
451
+ typeof generated?.by !== "string" ||
452
+ !generated.by.startsWith("agent:loom") ||
453
+ !empty("graduated_to") ||
454
+ !empty("upstream") ||
455
+ !empty("supersedes") ||
456
+ slugProblems(slug).length > 0
457
+ ) {
458
+ return reject(
459
+ id,
460
+ "validator",
461
+ "A local lesson keeps the fields Orbit set when it was drafted: status: draft, no verified " +
462
+ "entries, generated.by as written, and empty graduated_to, upstream and supersedes. " +
463
+ "Those are decided in review, not by editing the file. Put them back and run /lesson save again.",
464
+ ["0: brain-owned fields were edited"],
465
+ );
466
+ }
467
+
468
+ record("lesson.proposed", { id, bytes: Buffer.byteLength(read.text, "utf-8"), armedBy: "draft" });
469
+
470
+ if (!ctx.hasUI) return reject(id, "no-ui", "Saving a lesson needs an interactive session.");
471
+
472
+ ctx.ui.notify(`Draft ${id}\n\n${renderProposalPreview(read.text)}`, "info");
473
+ const approved = await ctx.ui.confirm(
474
+ `Save ${id} as a lesson?`,
475
+ "It passes the schema. Saved lessons are read back in later sessions and are never rewritten in place.",
476
+ );
477
+ if (approved !== true)
478
+ return reject(id, "declined", `Left the draft in place; nothing was saved.`);
479
+
480
+ // Re-read: the confirm can sit open for as long as the user likes, and the
481
+ // bytes that get saved must be the bytes that were checked and shown.
482
+ const again = readLessonFile(draft);
483
+ if (!again.ok || again.text !== read.text) {
484
+ return reject(
485
+ id,
486
+ "declined",
487
+ "The draft changed while you were deciding; nothing was saved. Run /lesson save again.",
488
+ );
489
+ }
490
+
491
+ const saved = saveLesson(id, namespace, slug, read.text);
492
+ if (saved.ok) {
493
+ // Cleared so it cannot be committed twice and /lesson drafts stops
494
+ // advertising work that is done.
495
+ try {
496
+ fs.rmSync(draft, { force: true });
497
+ } catch {
498
+ /* the lesson is written; a leftover draft is cosmetic */
499
+ }
500
+ }
501
+ return saved;
502
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Replaying recorded tool results through the lesson hint, for the Tier-1
3
+ * evals and for checking the hint by hand without a model.
4
+ *
5
+ * Same reasoning as `submission-replay.ts`: the hint fires on `tool_result`,
6
+ * which needs a real tool call, which needs a model turn, and the Tier-1
7
+ * scenarios exist to pin deterministic harness behaviour with no API key and
8
+ * no model in the loop. The file format and the containment check are reused
9
+ * from that module rather than re-derived -- one JSONL line per event,
10
+ * `{tool, args?, result, isError?}`, resolved inside the session directory
11
+ * only, both sides realpath'd so a symlink cannot point the replay elsewhere.
12
+ * Off unless LOOM_LESSON_REPLAY is set, and nothing registers it otherwise.
13
+ *
14
+ * Every replay writes a `lesson.replay` row first, so an activity log produced
15
+ * this way is never mistaken for one produced by real tool results. The hint
16
+ * each replayed result would have carried goes to stderr -- the only place a
17
+ * model-free run can show it.
18
+ */
19
+
20
+ import * as fs from "node:fs";
21
+ import { isLessonsEnabled } from "./enabled";
22
+ import * as path from "node:path";
23
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
24
+ import { readEnv } from "../../../shared/orbit-env.js";
25
+ import { appendActivityEvent } from "../activity";
26
+ import { decideHintForEvent, formatLessonHint, recordSurfacing } from "../lesson-hint";
27
+ import { getNotebookPath } from "../state";
28
+ import { parseReplayFile, resolveReplayPath, type ReplayEntry } from "../submission-replay";
29
+ import type { LessonToolResultContent } from "./pi-event-contract";
30
+
31
+ export function isLessonReplayEnabled(): boolean {
32
+ return !!readEnv("LESSON_REPLAY")?.trim();
33
+ }
34
+
35
+ /** A recorded pi tool result -> the content array the hint sees. Text only. */
36
+ export function replayContent(entry: ReplayEntry): LessonToolResultContent {
37
+ const result = entry.result as { content?: unknown } | undefined;
38
+ const blocks = Array.isArray(result?.content) ? result.content : [];
39
+ const out: LessonToolResultContent = [];
40
+ for (const raw of blocks) {
41
+ const block = raw as { type?: unknown; text?: unknown } | null;
42
+ if (block?.type === "text" && typeof block.text === "string") {
43
+ out.push({ type: "text", text: block.text });
44
+ }
45
+ }
46
+ return out;
47
+ }
48
+
49
+ export function registerLessonReplay(pi: ExtensionAPI): void {
50
+ pi.on("session_start", async () => {
51
+ const configured = readEnv("LESSON_REPLAY")?.trim();
52
+ if (!configured) return;
53
+ // Same answer the live surface gives: nothing read, written or armed while off.
54
+ if (!isLessonsEnabled()) return;
55
+
56
+ const notebookPath = getNotebookPath();
57
+ if (!notebookPath) return;
58
+ const sessionDir = path.dirname(notebookPath);
59
+
60
+ const file = resolveReplayPath(sessionDir, configured);
61
+ if (!file || !fs.existsSync(file)) return;
62
+
63
+ const entries = parseReplayFile(fs.readFileSync(file, "utf-8"));
64
+ appendActivityEvent(sessionDir, {
65
+ timestamp: new Date().toISOString(),
66
+ kind: "lesson.replay",
67
+ source: "lesson-replay",
68
+ // Relative to the REAL session dir: `file` is realpath'd, and against an
69
+ // unresolved root (macOS temp dirs sit behind /var -> /private/var) the
70
+ // relative path climbs out to the root and logs the full location.
71
+ payload: { file: path.relative(fs.realpathSync(sessionDir), file), entries: entries.length },
72
+ });
73
+
74
+ // Same once-per-lesson arming as the live hook, so a replayed retry loop
75
+ // behaves the way the real one does.
76
+ const armed = new Set<string>();
77
+ for (const entry of entries) {
78
+ const decision = decideHintForEvent(
79
+ { toolName: entry.tool, input: entry.args ?? {}, content: replayContent(entry) },
80
+ armed,
81
+ );
82
+ if (!decision) continue;
83
+ armed.add(decision.match.lesson.id);
84
+ recordSurfacing(decision.match, "tool_result");
85
+ // Not the entry's tool name: the replay file is arbitrary text.
86
+ console.error(`[loom lesson replay]\n${formatLessonHint(decision.match.lesson)}`);
87
+ }
88
+ });
89
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * The reproduction index: the one place ambient lesson injection is justified.
3
+ *
4
+ * A deterministic matcher reaches tool ids, hostnames, file extensions and the
5
+ * wording of a plan step. It cannot answer "is this a reproduction of published
6
+ * work?" -- and that is the motivating case with no failing tool result to
7
+ * hook. Deposited metadata being incomplete is not an error; it is a silently
8
+ * wrong answer three steps later.
9
+ *
10
+ * So, bounded: when the session looks like a reproduction, put the
11
+ * `reproduction/*` lesson TITLES AND IDS -- a handful of lines, rebuilt each
12
+ * turn on the per-turn channel, nothing in the cached prefix -- in front of
13
+ * the model and let it pull bodies with `lessons_search`.
14
+ *
15
+ * Records nothing. It is an index that re-renders every turn, and a
16
+ * `lesson.surfaced` row per listed title per turn would flood the activity log
17
+ * and overstate what happened. The surfacing is recorded when the model
18
+ * actually pulls a body.
19
+ */
20
+
21
+ import { parseMostRecentPlan } from "../init-gate.js";
22
+ import { recordStore } from "../record-store";
23
+ import { getNotebookPath } from "../state";
24
+ import { isLessonsEnabled } from "./enabled";
25
+ import { isMatchable } from "./matcher";
26
+ import { getLessonStore } from "./store";
27
+ import { collapse } from "./text";
28
+ import type { Lesson } from "./types";
29
+ import { wrapLessons } from "./wrapper";
30
+
31
+ /** customType for the per-turn lesson index, beside the two in context.ts. */
32
+ export const ORBIT_LESSONS_CONTEXT_TYPE = "loom-lessons-context";
33
+
34
+ const REPRODUCTION_PREFIX = "reproduction/";
35
+ const REPRODUCING = /reproduc/i;
36
+ const MAX_LINES = 10;
37
+
38
+ /**
39
+ * The stage heuristic, such as it is: there is no phase-like session state, so
40
+ * the only signals are what the user just said and what the latest plan is
41
+ * called.
42
+ */
43
+ export function looksLikeReproduction(lastUserText: string, planTitle: string | null): boolean {
44
+ return REPRODUCING.test(lastUserText) || REPRODUCING.test(planTitle ?? "");
45
+ }
46
+
47
+ export async function currentPlanTitle(): Promise<string | null> {
48
+ const notebook = getNotebookPath();
49
+ if (!notebook) return null;
50
+ try {
51
+ return parseMostRecentPlan((await recordStore(notebook).read()).content)?.title ?? null;
52
+ } catch {
53
+ return null;
54
+ }
55
+ }
56
+
57
+ export function renderReproductionIndex(lessons: readonly Lesson[]): string {
58
+ const rows = lessons
59
+ .filter((l) => l.id.startsWith(REPRODUCTION_PREFIX))
60
+ .filter(isMatchable)
61
+ .slice(0, MAX_LINES);
62
+ if (rows.length === 0) return "";
63
+
64
+ return wrapLessons(
65
+ [
66
+ "This session looks like a reproduction of published work. Lessons have",
67
+ "been recorded about reproductions; here are their ids and titles only.",
68
+ "",
69
+ ...rows.map((l) => `- ${l.id} -- ${collapse(l.title)}`),
70
+ "",
71
+ "Read one in full by passing its id as the lessons_search query before",
72
+ "relying on it. A title is not advice -- do not act on one without reading",
73
+ 'the lesson\'s "Check first" line.',
74
+ ].join("\n"),
75
+ );
76
+ }
77
+
78
+ export async function buildReproductionLessonsContext(lastUserText: string): Promise<string> {
79
+ // The store is empty with the switch off; gating here also spares the record read.
80
+ if (!isLessonsEnabled()) return "";
81
+ if (!looksLikeReproduction(lastUserText, await currentPlanTitle())) return "";
82
+ return renderReproductionIndex(getLessonStore().lessons);
83
+ }