pen-stack 6.4.1__tar.gz → 6.4.2__tar.gz

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 (453) hide show
  1. {pen_stack-6.4.1 → pen_stack-6.4.2}/CHANGELOG.md +38 -0
  2. {pen_stack-6.4.1 → pen_stack-6.4.2}/CITATION.cff +2 -2
  3. {pen_stack-6.4.1 → pen_stack-6.4.2}/PKG-INFO +2 -2
  4. {pen_stack-6.4.1 → pen_stack-6.4.2}/README.md +1 -1
  5. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/__init__.py +1 -1
  6. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/web/llm.py +62 -15
  7. pen_stack-6.4.2/pen_stack/web/tools.py +190 -0
  8. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack.egg-info/PKG-INFO +2 -2
  9. {pen_stack-6.4.1 → pen_stack-6.4.2}/pyproject.toml +1 -1
  10. pen_stack-6.4.1/pen_stack/web/tools.py +0 -105
  11. {pen_stack-6.4.1 → pen_stack-6.4.2}/LICENSE +0 -0
  12. {pen_stack-6.4.1 → pen_stack-6.4.2}/MANIFEST.in +0 -0
  13. {pen_stack-6.4.1 → pen_stack-6.4.2}/bench/run.py +0 -0
  14. {pen_stack-6.4.1 → pen_stack-6.4.2}/benchmarks/genome_writing_bench/LEADERBOARD.md +0 -0
  15. {pen_stack-6.4.1 → pen_stack-6.4.2}/benchmarks/genome_writing_bench/README.md +0 -0
  16. {pen_stack-6.4.1 → pen_stack-6.4.2}/benchmarks/genome_writing_bench/SHA256SUMS +0 -0
  17. {pen_stack-6.4.1 → pen_stack-6.4.2}/benchmarks/genome_writing_bench/SUBMISSIONS.md +0 -0
  18. {pen_stack-6.4.1 → pen_stack-6.4.2}/benchmarks/genome_writing_bench/tasks.yaml +0 -0
  19. {pen_stack-6.4.1 → pen_stack-6.4.2}/benchmarks/genome_writing_challenge/README.md +0 -0
  20. {pen_stack-6.4.1 → pen_stack-6.4.2}/benchmarks/genome_writing_challenge/SUBMISSIONS.md +0 -0
  21. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/antipeg.yaml +0 -0
  22. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/atlas_families.yaml +0 -0
  23. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/bridge_offtarget_profile.yaml +0 -0
  24. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/capsid_epitope_oracle.yaml +0 -0
  25. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/capsid_sequences.fasta +0 -0
  26. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/cargo_polish.yaml +0 -0
  27. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/cell_types.yaml +0 -0
  28. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/datasets.yaml +0 -0
  29. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/delivery_constraints.yaml +0 -0
  30. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/delivery_rules.yaml +0 -0
  31. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/delivery_vehicles.yaml +0 -0
  32. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/gates_v3.yaml +0 -0
  33. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/genotoxicity_oracle.yaml +0 -0
  34. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/gsh_validated_heldout.yaml +0 -0
  35. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/intent_weights.yaml +0 -0
  36. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/known_unknowns.yaml +0 -0
  37. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/llm.yaml +0 -0
  38. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/metric_guide.yaml +0 -0
  39. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/monitor_queries.yaml +0 -0
  40. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/oracles/execution.yaml +0 -0
  41. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/oracles/scope_cards.yaml +0 -0
  42. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/rules/delivery.yaml +0 -0
  43. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/rules/fold.yaml +0 -0
  44. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/rules/multiplex.yaml +0 -0
  45. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/rules/payload.yaml +0 -0
  46. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/rules/reachability.yaml +0 -0
  47. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/safety/hazard_registry.yaml +0 -0
  48. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/safety/policy.yaml +0 -0
  49. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/safety/probes.yaml +0 -0
  50. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/score_axes.yaml +0 -0
  51. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/seroprevalence.yaml +0 -0
  52. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/target_sites.yaml +0 -0
  53. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/universe_crosswalk.yaml +0 -0
  54. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/write_types.yaml +0 -0
  55. {pen_stack-6.4.1 → pen_stack-6.4.2}/configs/wtkb_curated.yaml +0 -0
  56. {pen_stack-6.4.1 → pen_stack-6.4.2}/data/curated/bridge_offtarget_energetics.json +0 -0
  57. {pen_stack-6.4.1 → pen_stack-6.4.2}/data/curated/bridge_offtarget_profile_measured.parquet +0 -0
  58. {pen_stack-6.4.1 → pen_stack-6.4.2}/data/curated/gene_coords.parquet +0 -0
  59. {pen_stack-6.4.1 → pen_stack-6.4.2}/data/curated/unified_editor_universe.parquet +0 -0
  60. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/BACKLOG.md +0 -0
  61. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/DEPLOY.md +0 -0
  62. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/INFRA.md +0 -0
  63. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/MCP.md +0 -0
  64. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/RELEASING.md +0 -0
  65. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/REPRO.md +0 -0
  66. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/STABILITY.md +0 -0
  67. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/agent.md +0 -0
  68. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/alphagenome_feasibility.md +0 -0
  69. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/autonomy.md +0 -0
  70. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/benchmark_circularity.md +0 -0
  71. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/biosecurity.md +0 -0
  72. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/build_interface.md +0 -0
  73. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/cards/atlas.md +0 -0
  74. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/cards/durability.md +0 -0
  75. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/cards/safety.md +0 -0
  76. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/challenge.md +0 -0
  77. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/closed_loop.md +0 -0
  78. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/co_scientist.md +0 -0
  79. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/co_scientist_loop.md +0 -0
  80. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/delivery.md +0 -0
  81. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/delivery_immunology.md +0 -0
  82. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/digital_twin.md +0 -0
  83. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/dissemination.md +0 -0
  84. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/environment.md +0 -0
  85. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/experiment_design.md +0 -0
  86. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/generative_design.md +0 -0
  87. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/index.md +0 -0
  88. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/integrations.md +0 -0
  89. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/live_oracles.md +0 -0
  90. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/mechanistic_constraints.md +0 -0
  91. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/oracles.md +0 -0
  92. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/positioning.md +0 -0
  93. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/private_data_formats.md +0 -0
  94. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/quickstart.md +0 -0
  95. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/responsible_use.md +0 -0
  96. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/rules.md +0 -0
  97. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/scope.md +0 -0
  98. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/scorecard.md +0 -0
  99. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/tutorials/compare-families.md +0 -0
  100. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/tutorials/score-deliverability.md +0 -0
  101. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/tutorials/where-can-i-write.md +0 -0
  102. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/tutorials/which-writer-reaches-locus.md +0 -0
  103. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/uncertainty.md +0 -0
  104. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/verify.md +0 -0
  105. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/world_model.md +0 -0
  106. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/writer_verification.md +0 -0
  107. {pen_stack-6.4.1 → pen_stack-6.4.2}/docs/wtkb.md +0 -0
  108. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/_resources.py +0 -0
  109. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/active/__init__.py +0 -0
  110. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/active/acquire.py +0 -0
  111. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/active/design.py +0 -0
  112. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/active/validate.py +0 -0
  113. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/adapt/__init__.py +0 -0
  114. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/adapt/finetune.py +0 -0
  115. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/adapt/ingest.py +0 -0
  116. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/adapt/pipeline.py +0 -0
  117. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/adapt/recalibrate.py +0 -0
  118. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/adapt/report.py +0 -0
  119. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/agent/__init__.py +0 -0
  120. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/agent/cite.py +0 -0
  121. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/agent/co_scientist.py +0 -0
  122. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/agent/epistemic.py +0 -0
  123. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/agent/guardrails.py +0 -0
  124. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/agent/mcp_server.py +0 -0
  125. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/agent/orchestrator.py +0 -0
  126. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/agent/orchestrator_live.py +0 -0
  127. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/agent/pen_agent.py +0 -0
  128. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/agent/scope.py +0 -0
  129. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/agent/tools.py +0 -0
  130. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/api/__init__.py +0 -0
  131. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/api/manifest.py +0 -0
  132. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/atlas/__init__.py +0 -0
  133. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/atlas/build_wtkb.py +0 -0
  134. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/atlas/crosslink.py +0 -0
  135. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/atlas/expand.py +0 -0
  136. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/atlas/schema.py +0 -0
  137. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/atlas/scorecard.py +0 -0
  138. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/atlas/universe.py +0 -0
  139. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/atlas/variant_propose.py +0 -0
  140. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/atlas/writer_verify.py +0 -0
  141. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/bridge/__init__.py +0 -0
  142. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/bridge/activity.py +0 -0
  143. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/bridge/cli.py +0 -0
  144. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/bridge/fold_qc.py +0 -0
  145. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/bridge/guide_qc.py +0 -0
  146. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/bridge/ingest.py +0 -0
  147. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/bridge/offtarget.py +0 -0
  148. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/bridge/offtarget_energetics.py +0 -0
  149. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/bridge/ortholog_screen.py +0 -0
  150. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/bridge/pipeline.py +0 -0
  151. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/build/__init__.py +0 -0
  152. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/build/ingest.py +0 -0
  153. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/build/protocol.py +0 -0
  154. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/build/simlab.py +0 -0
  155. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/cli.py +0 -0
  156. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/data/__init__.py +0 -0
  157. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/data/encode.py +0 -0
  158. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/data/genome.py +0 -0
  159. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/data/ingest_chromatin.py +0 -0
  160. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/data/ingest_integration.py +0 -0
  161. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/data/ingest_safety_annot.py +0 -0
  162. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/data/ingest_trip.py +0 -0
  163. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/design/__init__.py +0 -0
  164. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/design/generate.py +0 -0
  165. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/design/pareto.py +0 -0
  166. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/design/space.py +0 -0
  167. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/env/__init__.py +0 -0
  168. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/env/genome_writing_env.py +0 -0
  169. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/env/policies.py +0 -0
  170. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/graph/__init__.py +0 -0
  171. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/graph/build.py +0 -0
  172. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/graph/cell_types.py +0 -0
  173. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/graph/ingest.py +0 -0
  174. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/graph/query.py +0 -0
  175. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/graph/schema.py +0 -0
  176. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/loop/__init__.py +0 -0
  177. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/loop/continual.py +0 -0
  178. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/loop/cycle.py +0 -0
  179. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/loop/drift.py +0 -0
  180. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/mech/__init__.py +0 -0
  181. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/mech/classify_atlas.py +0 -0
  182. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/mech/whitelist.py +0 -0
  183. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/monitor/__init__.py +0 -0
  184. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/monitor/europepmc.py +0 -0
  185. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/monitor/run.py +0 -0
  186. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/monitor/triage.py +0 -0
  187. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/oracles/__init__.py +0 -0
  188. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/oracles/cache.py +0 -0
  189. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/oracles/energetics.py +0 -0
  190. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/oracles/genome.py +0 -0
  191. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/oracles/protein_design.py +0 -0
  192. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/oracles/rna.py +0 -0
  193. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/oracles/schema.py +0 -0
  194. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/oracles/status.py +0 -0
  195. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/oracles/structure.py +0 -0
  196. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/oracles/vcell.py +0 -0
  197. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/__init__.py +0 -0
  198. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/antipeg_oracle.py +0 -0
  199. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/capsid_epitope_oracle.py +0 -0
  200. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/cargo.py +0 -0
  201. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/cargo_polish.py +0 -0
  202. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/delivery.py +0 -0
  203. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/delivery_constraints.py +0 -0
  204. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/delivery_immunology.py +0 -0
  205. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/delivery_vehicles.py +0 -0
  206. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/genotoxicity_oracle.py +0 -0
  207. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/immune_profile.py +0 -0
  208. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/innate_sensing.py +0 -0
  209. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/multiplex.py +0 -0
  210. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/optimize.py +0 -0
  211. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/pipeline.py +0 -0
  212. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/report.py +0 -0
  213. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/router.py +0 -0
  214. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/seroprevalence_oracle.py +0 -0
  215. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/planner/target_site.py +0 -0
  216. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/rag/__init__.py +0 -0
  217. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/rag/index.py +0 -0
  218. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/rag/llm.py +0 -0
  219. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/rag/qa.py +0 -0
  220. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/rules/__init__.py +0 -0
  221. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/rules/evaluators.py +0 -0
  222. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/rules/loader.py +0 -0
  223. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/rules/schema.py +0 -0
  224. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/rules/solver.py +0 -0
  225. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/safety/__init__.py +0 -0
  226. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/safety/audit.py +0 -0
  227. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/safety/gate.py +0 -0
  228. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/safety/policy.py +0 -0
  229. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/safety/redteam.py +0 -0
  230. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/safety/registry.py +0 -0
  231. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/safety/screen.py +0 -0
  232. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/score/__init__.py +0 -0
  233. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/score/recalibrate.py +0 -0
  234. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/score/therapeutic.py +0 -0
  235. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/server/__init__.py +0 -0
  236. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/server/api.py +0 -0
  237. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/twin/__init__.py +0 -0
  238. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/twin/calibrate.py +0 -0
  239. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/twin/mechanistic.py +0 -0
  240. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/twin/outcome.py +0 -0
  241. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/ui/__init__.py +0 -0
  242. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/ui/app.py +0 -0
  243. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/__init__.py +0 -0
  244. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/adapt_demo.py +0 -0
  245. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/agent_eval.py +0 -0
  246. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/bench_adversarial_tasks.py +0 -0
  247. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/bench_coscientist_tasks.py +0 -0
  248. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/bench_graph_tasks.py +0 -0
  249. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/bench_rule_tasks.py +0 -0
  250. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/bench_trust_tasks.py +0 -0
  251. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/bench_writetype_tasks.py +0 -0
  252. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/blind_gsh_discovery.py +0 -0
  253. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/cargo_directionality.py +0 -0
  254. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/closed_loop.py +0 -0
  255. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/durability_baselines.py +0 -0
  256. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/experiment_design.py +0 -0
  257. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/forward_hypotheses.py +0 -0
  258. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/generative_design.py +0 -0
  259. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/guide_qc_demo.py +0 -0
  260. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/immune_calibration.py +0 -0
  261. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/intent_specification.py +0 -0
  262. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/offtarget_energetics_eval.py +0 -0
  263. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/out_of_scope_refusal.py +0 -0
  264. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/outcome_calibration.py +0 -0
  265. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/outcome_prediction.py +0 -0
  266. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/paper3_benchmark.py +0 -0
  267. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/paper4_real_validation.py +0 -0
  268. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/paper4_validation.py +0 -0
  269. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/protocol_safety.py +0 -0
  270. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/safety_screening.py +0 -0
  271. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/selective_prediction.py +0 -0
  272. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/seq_vs_measured.py +0 -0
  273. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/target_site_controls.py +0 -0
  274. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/uncertainty_eval.py +0 -0
  275. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/ungrounded_baseline.py +0 -0
  276. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/within_locus_ranking.py +0 -0
  277. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/validate/writer_recovery.py +0 -0
  278. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/verify/__init__.py +0 -0
  279. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/verify/schema.py +0 -0
  280. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/verify/service.py +0 -0
  281. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/web/__init__.py +0 -0
  282. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/web/guide.py +0 -0
  283. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/web/router.py +0 -0
  284. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/web/server.py +0 -0
  285. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/wgenome/__init__.py +0 -0
  286. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/wgenome/chromatin_seq.py +0 -0
  287. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/wgenome/durability.py +0 -0
  288. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/wgenome/export_tracks.py +0 -0
  289. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/wgenome/features.py +0 -0
  290. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/wgenome/gsh_baseline.py +0 -0
  291. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/wgenome/mesh_features.py +0 -0
  292. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/wgenome/ood.py +0 -0
  293. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/wgenome/providers.py +0 -0
  294. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/wgenome/safety.py +0 -0
  295. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/wgenome/structure3d.py +0 -0
  296. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/wgenome/uncertainty.py +0 -0
  297. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack/wgenome/writability.py +0 -0
  298. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack.egg-info/SOURCES.txt +0 -0
  299. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack.egg-info/dependency_links.txt +0 -0
  300. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack.egg-info/entry_points.txt +0 -0
  301. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack.egg-info/requires.txt +0 -0
  302. {pen_stack-6.4.1 → pen_stack-6.4.2}/pen_stack.egg-info/top_level.txt +0 -0
  303. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_phase0.json +0 -0
  304. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_phase1_5.json +0 -0
  305. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_phase2.json +0 -0
  306. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_phase3.json +0 -0
  307. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_a.json +0 -0
  308. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_acq.json +0 -0
  309. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_aldesign.json +0 -0
  310. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_alvalidate.json +0 -0
  311. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_atlas.json +0 -0
  312. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_b.json +0 -0
  313. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_ba.json +0 -0
  314. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_ba_v33.json +0 -0
  315. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_ba_v45.json +0 -0
  316. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_bench.json +0 -0
  317. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_c.json +0 -0
  318. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_cal.json +0 -0
  319. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_calib.json +0 -0
  320. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_challenge.json +0 -0
  321. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_chat.json +0 -0
  322. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_cite.json +0 -0
  323. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_continual.json +0 -0
  324. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_cosci2.json +0 -0
  325. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_crit.json +0 -0
  326. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_ct.json +0 -0
  327. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_d.json +0 -0
  328. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_drift.json +0 -0
  329. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_e.json +0 -0
  330. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_env.json +0 -0
  331. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_ep.json +0 -0
  332. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_epitope.json +0 -0
  333. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_f.json +0 -0
  334. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_frontend.json +0 -0
  335. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_g.json +0 -0
  336. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_gen.json +0 -0
  337. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_genotox.json +0 -0
  338. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_graph.json +0 -0
  339. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_h.json +0 -0
  340. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_hybrid.json +0 -0
  341. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_immune.json +0 -0
  342. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_ingest.json +0 -0
  343. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_innate.json +0 -0
  344. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_loop.json +0 -0
  345. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_manifest.json +0 -0
  346. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_mc.json +0 -0
  347. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_mcp.json +0 -0
  348. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_mech.json +0 -0
  349. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_mon.json +0 -0
  350. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_o.json +0 -0
  351. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_openapi.json +0 -0
  352. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_orch.json +0 -0
  353. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_outcome.json +0 -0
  354. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_pareto.json +0 -0
  355. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_peg.json +0 -0
  356. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_plan.json +0 -0
  357. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_policy.json +0 -0
  358. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_profile.json +0 -0
  359. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_proto.json +0 -0
  360. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_r.json +0 -0
  361. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_redteam.json +0 -0
  362. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_route.json +0 -0
  363. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_screen.json +0 -0
  364. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_seroprev.json +0 -0
  365. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_simlab.json +0 -0
  366. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_twincal.json +0 -0
  367. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_uq.json +0 -0
  368. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_v.json +0 -0
  369. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_vcell.json +0 -0
  370. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/SHA256_LOCK_ws_wv.json +0 -0
  371. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/paper1.yaml +0 -0
  372. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/paper2.yaml +0 -0
  373. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/paper3.yaml +0 -0
  374. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/paper4.yaml +0 -0
  375. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/phase0.yaml +0 -0
  376. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_a.yaml +0 -0
  377. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_acq.yaml +0 -0
  378. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_aldesign.yaml +0 -0
  379. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_alvalidate.yaml +0 -0
  380. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_atlas.yaml +0 -0
  381. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_b.yaml +0 -0
  382. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_ba.yaml +0 -0
  383. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_ba_v33.yaml +0 -0
  384. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_ba_v45.yaml +0 -0
  385. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_bench.yaml +0 -0
  386. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_c.yaml +0 -0
  387. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_cal.yaml +0 -0
  388. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_calib.yaml +0 -0
  389. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_challenge.yaml +0 -0
  390. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_chat.yaml +0 -0
  391. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_cite.yaml +0 -0
  392. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_continual.yaml +0 -0
  393. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_cosci2.yaml +0 -0
  394. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_crit.yaml +0 -0
  395. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_ct.yaml +0 -0
  396. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_d.yaml +0 -0
  397. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_drift.yaml +0 -0
  398. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_e.yaml +0 -0
  399. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_env.yaml +0 -0
  400. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_ep.yaml +0 -0
  401. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_epitope.yaml +0 -0
  402. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_f.yaml +0 -0
  403. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_frontend.yaml +0 -0
  404. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_g.yaml +0 -0
  405. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_gen.yaml +0 -0
  406. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_genotox.yaml +0 -0
  407. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_graph.yaml +0 -0
  408. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_h.yaml +0 -0
  409. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_hybrid.yaml +0 -0
  410. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_immune.yaml +0 -0
  411. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_ingest.yaml +0 -0
  412. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_innate.yaml +0 -0
  413. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_loop.yaml +0 -0
  414. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_manifest.yaml +0 -0
  415. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_mc.yaml +0 -0
  416. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_mcp.yaml +0 -0
  417. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_mech.yaml +0 -0
  418. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_mon.yaml +0 -0
  419. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_o.yaml +0 -0
  420. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_openapi.yaml +0 -0
  421. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_orch.yaml +0 -0
  422. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_outcome.yaml +0 -0
  423. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_pareto.yaml +0 -0
  424. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_peg.yaml +0 -0
  425. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_plan.yaml +0 -0
  426. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_policy.yaml +0 -0
  427. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_profile.yaml +0 -0
  428. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_proto.yaml +0 -0
  429. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_r.yaml +0 -0
  430. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_redteam.yaml +0 -0
  431. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_route.yaml +0 -0
  432. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_screen.yaml +0 -0
  433. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_seroprev.yaml +0 -0
  434. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_simlab.yaml +0 -0
  435. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_twincal.yaml +0 -0
  436. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_uq.yaml +0 -0
  437. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_v.yaml +0 -0
  438. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_vcell.yaml +0 -0
  439. {pen_stack-6.4.1 → pen_stack-6.4.2}/prereg/ws_wv.yaml +0 -0
  440. {pen_stack-6.4.1 → pen_stack-6.4.2}/scripts/p1_build_atlas.py +0 -0
  441. {pen_stack-6.4.1 → pen_stack-6.4.2}/scripts/p1_build_durability.py +0 -0
  442. {pen_stack-6.4.1 → pen_stack-6.4.2}/scripts/p1_export_tracks.py +0 -0
  443. {pen_stack-6.4.1 → pen_stack-6.4.2}/scripts/p1_safety_concordance.py +0 -0
  444. {pen_stack-6.4.1 → pen_stack-6.4.2}/scripts/p1_train_safety.py +0 -0
  445. {pen_stack-6.4.1 → pen_stack-6.4.2}/scripts/p1_validation_report.py +0 -0
  446. {pen_stack-6.4.1 → pen_stack-6.4.2}/scripts/p2_build_atlas.py +0 -0
  447. {pen_stack-6.4.1 → pen_stack-6.4.2}/scripts/p3_benchmark_report.py +0 -0
  448. {pen_stack-6.4.1 → pen_stack-6.4.2}/scripts/p4_genome_scan.py +0 -0
  449. {pen_stack-6.4.1 → pen_stack-6.4.2}/scripts/p52_build_genotox_oracle.py +0 -0
  450. {pen_stack-6.4.1 → pen_stack-6.4.2}/scripts/p53_build_epitope_oracle.py +0 -0
  451. {pen_stack-6.4.1 → pen_stack-6.4.2}/scripts/ws_b_report.py +0 -0
  452. {pen_stack-6.4.1 → pen_stack-6.4.2}/scripts/ws_c_report.py +0 -0
  453. {pen_stack-6.4.1 → pen_stack-6.4.2}/setup.cfg +0 -0
@@ -3,6 +3,44 @@
3
3
  All notable changes to PEN-STACK are documented here. This file follows
4
4
  [Keep a Changelog](https://keepachangelog.com/) and the program's phase structure.
5
5
 
6
+ ## [6.4.2] - 2026-06-12 - Co-Scientist chat: real writer recommendations + self-explanatory values
7
+
8
+ **PATCH — fixes a stale/confusing chat experience reported from the live app.** Three problems, three fixes:
9
+
10
+ 1. **The chat dossier was generic and barely changed per query.** `web/tools.py::parse_goal` hardcoded
11
+ `chrom="chr19"` (so a goal about ITGB2 — chr21 — was mis-located) and `run_tools` never called the planner,
12
+ so a "which writer can integrate N kb in GENE" question got a vehicle-keyed immune profile but **no named
13
+ writer**. Fix: `parse_goal` now resolves the gene's REAL chromosome (`planner.gene_region`, atlas-gated,
14
+ falls back offline), and `run_tools` runs the planner and attaches a `plan` block — the recommended writer
15
+ family, top site, safety/durability/score, cargo-capacity fit, and delivery — all engine-computed. Honest
16
+ when the gene isn't in the atlas ("not found — check the HGNC symbol"); never fabricated.
17
+ 2. **The LLM narration produced `[unverified]` spam.** With no writer in the tool results, the model invented
18
+ one (`AAV9-CRISPR` + made-up numbers) and the grounding guard struck them all. Fix: the system prompt now
19
+ forbids inventing a writer/vehicle/table/number, and if the guard still strikes ≥2 numbers the reply falls
20
+ back to the fully-grounded deterministic narration — so the spam never reaches the user.
21
+ 3. **Values + the "⚠ extrapolating" badge weren't self-explanatory.** Each immune axis now carries a
22
+ plain-language `meaning` ("0.55 on a 0–1 scale — moderate; higher = fewer patients excluded …; this is a
23
+ proxy, not validated against a measured outcome"), the deterministic narration uses it, and
24
+ `ImmuneProfileCard` renders it plus a one-line legend explaining that "extrapolating" = a proxy estimate.
25
+
26
+ ### Added
27
+ - `tests/unit/test_ws_chat.py` — chrom resolution, axis-meaning, real writer plan, and the no-`[unverified]`-spam
28
+ fallback are locked by new tests.
29
+
30
+ ## [6.4.1] - 2026-06-12 - Defence-in-depth: pre-route safety screen
31
+
32
+ **PATCH.** The grounded co-scientist chat ran the Guardian (biosecurity gate) only in the *design* lane (via
33
+ `run_tools`); a hazardous request with no design signal (no vehicle/locus) routed to `general`/`explain`/`meta`
34
+ and was never screened. Fix: `pen_stack/web/llm.py::_pre_route_safety` runs the Guardian — framing-stripped, the
35
+ authority on the decision — at the **top** of `grounded_reply()`, before lane routing. A `refuse`/`escalate`
36
+ verdict short-circuits to a clear decline (`mode="safety"`, with the decision in `tool_results`). Benign
37
+ hazard-adjacent biology (vaccines, generic pathogen questions) is **not** blocked — the Guardian clears it and
38
+ routing continues. A broad regex only decides *whether* to invoke the Guardian; a false trigger is harmless.
39
+
40
+ ### Added
41
+ - `tests/unit/test_ws_chat.py` — `test_pre_route_safety_screens_a_hazardous_general_query` and
42
+ `test_pre_route_safety_does_not_over_refuse_benign_questions` lock both sides of the screen.
43
+
6
44
  ## [6.4.0] - 2026-06-12 - v6.4.0: Live Oracles (the foundation models actually execute)
7
45
 
8
46
  **The oracle mesh goes live.** The foundation-model adapters that were deferred contracts now run real backends —
@@ -1,8 +1,8 @@
1
1
  cff-version: 1.2.0
2
2
  message: "If you use PEN-STACK, please cite it as below."
3
3
  title: "PEN-STACK: open infrastructure for genome writing"
4
- version: 6.4.0
5
- date-released: 2026-06-11
4
+ version: 6.4.2
5
+ date-released: 2026-06-12
6
6
  authors:
7
7
  - family-names: "Mahaboob Ali"
8
8
  given-names: "Anees Ahmed"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pen-stack
3
- Version: 6.4.1
3
+ Version: 6.4.2
4
4
  Summary: Open infrastructure for genome writing: the Writable Genome atlas, the Writer Atlas, and the Write Planner.
5
5
  Author-email: Anees Ahmed Mahaboob Ali <ahmedaneesm@gmail.com>
6
6
  License: MIT
@@ -90,7 +90,7 @@ every design against rule-grounded mechanism, reports calibrated confidence, cit
90
90
  [![codecov](https://codecov.io/gh/ahmedanees-m/pen-stack/branch/main/graph/badge.svg)](https://codecov.io/gh/ahmedanees-m/pen-stack)
91
91
  [![License: MIT](https://img.shields.io/badge/License-MIT-informational.svg)](LICENSE)
92
92
  [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/)
93
- [![Version](https://img.shields.io/badge/version-6.4.0-blue.svg)](CHANGELOG.md)
93
+ [![Version](https://img.shields.io/badge/version-6.4.2-blue.svg)](CHANGELOG.md)
94
94
  [![Status](https://img.shields.io/badge/status-1.0%20First%20Stable-success.svg)](docs/STABILITY.md)
95
95
  [![Tests](https://img.shields.io/badge/tests-378%20passing-success.svg)](tests/)
96
96
  [![Lint: ruff](https://img.shields.io/badge/lint-ruff-purple.svg)](https://github.com/astral-sh/ruff)
@@ -15,7 +15,7 @@ every design against rule-grounded mechanism, reports calibrated confidence, cit
15
15
  [![codecov](https://codecov.io/gh/ahmedanees-m/pen-stack/branch/main/graph/badge.svg)](https://codecov.io/gh/ahmedanees-m/pen-stack)
16
16
  [![License: MIT](https://img.shields.io/badge/License-MIT-informational.svg)](LICENSE)
17
17
  [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/)
18
- [![Version](https://img.shields.io/badge/version-6.4.0-blue.svg)](CHANGELOG.md)
18
+ [![Version](https://img.shields.io/badge/version-6.4.2-blue.svg)](CHANGELOG.md)
19
19
  [![Status](https://img.shields.io/badge/status-1.0%20First%20Stable-success.svg)](docs/STABILITY.md)
20
20
  [![Tests](https://img.shields.io/badge/tests-378%20passing-success.svg)](tests/)
21
21
  [![Lint: ruff](https://img.shields.io/badge/lint-ruff-purple.svg)](https://github.com/astral-sh/ruff)
@@ -1,2 +1,2 @@
1
1
  """PEN-STACK v3.0 - open infrastructure for genome writing."""
2
- __version__ = "6.4.1"
2
+ __version__ = "6.4.2"
@@ -28,11 +28,13 @@ from pen_stack.web.tools import extract_grounded_numbers, run_tools
28
28
  # -------------------------------------------------------------------------------------- system prompts
29
29
  SYSTEM_GROUNDED = (
30
30
  "You are PEN-STACK's co-scientist for genome writing. The PEN-STACK ENGINE computed the TOOL RESULTS; you "
31
- "explain and route, but you MUST NOT state any number, score, or probability that is not present in the TOOL "
32
- "RESULTS or the METRIC GUIDE provided. For every key number, also explain what it MEANS using the metric "
33
- "guide its scale, whether higher is better, the reference band, and how it was computed so the user "
34
- "understands the value, not just sees it. Surface uncertainty and the scope ledger ('what I can't tell you'). "
35
- "Be concise, friendly, honest. Decision-support, not a clinical directive."
31
+ "explain and route, but you MUST NOT state any number, score, probability, writer/enzyme name, or delivery "
32
+ "vehicle that is not present in the TOOL RESULTS or the METRIC GUIDE provided. NEVER invent a recommendation, "
33
+ "a markdown table of made-up values, or a 'hypothetical' result if the engine did not compute something, "
34
+ "say so plainly. For every key number, explain what it MEANS in plain words its scale, whether higher is "
35
+ "better, the reference band, how it was computed — so the user understands the value, not just sees it. "
36
+ "Surface uncertainty and the scope ledger ('what I can't tell you'). Be concise, friendly, honest. "
37
+ "Decision-support, not a clinical directive."
36
38
  )
37
39
  SYSTEM_META = (
38
40
  "You are PEN-STACK's co-scientist explaining the SYSTEM ITSELF (coverage, methods, accuracy). Answer using "
@@ -84,12 +86,46 @@ def _fmt(x):
84
86
  return str(x)
85
87
 
86
88
 
89
+ def _narrate_plan(p: dict) -> str | None:
90
+ """The actual writer/site recommendation as a self-explanatory line (or an honest 'not found' note)."""
91
+ if not p or not p.get("available"):
92
+ return None
93
+ if not p.get("found"):
94
+ return f"**Recommended writer.** {p.get('why', 'no writable plan for this target.')}"
95
+ bits = [f"the top writer family is **{p['recommended_writer']}**"]
96
+ site = p.get("site") or {}
97
+ if site.get("chrom"):
98
+ bits.append(f"at **{site['chrom']}:bin{site.get('bin')}**")
99
+ nums = []
100
+ if p.get("safety") is not None:
101
+ nums.append(f"safety {_fmt(p['safety'])}")
102
+ if p.get("durability") is not None:
103
+ nums.append(f"durability {_fmt(p['durability'])}")
104
+ if p.get("score") is not None:
105
+ nums.append(f"plan score {_fmt(p['score'])}")
106
+ cap = p.get("cargo_capacity_bp")
107
+ fit = p.get("cargo_fits_single_vector")
108
+ cargo_note = ""
109
+ if cap is not None:
110
+ cargo_note = (f" Cargo capacity is **{cap} bp**; the assembled cassette "
111
+ f"({p.get('assembled_bp', '?')} bp) "
112
+ + ("fits a single vector" if fit else f"exceeds it → delivered as **{p.get('delivery', 'split/dual')}**")
113
+ + ".")
114
+ alts = p.get("alternative_writers") or []
115
+ alt_note = f" Alternatives: {', '.join(alts)}." if alts else ""
116
+ return (f"**Recommended writer.** For this target {', '.join(bits)} "
117
+ f"({'; '.join(nums)}).{cargo_note}{alt_note} (0–1 scores, higher = better.)")
118
+
119
+
87
120
  def _deterministic_narrate(tr: dict) -> str:
88
121
  d = tr["parsed_design"]
89
122
  v = tr["verdict"]
90
123
  lines = [f"I read your goal as a **{d['edit_intent'].replace('_', ' ')}** of **{d['gene']}** "
91
- f"(~{d['cargo_bp']} bp) by **{d['delivery_vehicle'].replace('_', ' ')}** in **{d['cell_type']}**. "
92
- f"Every number below is engine-computed."]
124
+ f"({d['chrom']}, ~{d['cargo_bp']} bp) by **{d['delivery_vehicle'].replace('_', ' ')}** "
125
+ f"in **{d['cell_type']}**. Every number below is engine-computed."]
126
+ plan_line = _narrate_plan(tr.get("plan") or {})
127
+ if plan_line:
128
+ lines.append(plan_line)
93
129
  legal = "legal" if v["legal"] else ("deferred" if v["legal"] is None else "ILLEGAL")
94
130
  line = f"**Verification.** The design is **{legal}** ({v['epistemic_status']})."
95
131
  if v["violations"]:
@@ -107,13 +143,14 @@ def _deterministic_narrate(tr: dict) -> str:
107
143
  lines.append(f"**Safety (Guardian).** **{s['decision']}** — {s.get('reason', '')}. {sd}")
108
144
  axes = (tr["immune_profile"].get("axes") or {})
109
145
  if axes:
110
- lines.append("**Immune-risk profile** (per-axis — never collapsed). Each is 0–1, higher = safer:")
146
+ lines.append("**Immune-risk profile** (per-axis — never collapsed; each is 0–1, higher = safer):")
111
147
  for name, a in axes.items():
112
148
  gd = guide_for(name) or {}
113
149
  val = _fmt(a["value"]) if a.get("value") is not None else "n/a"
114
150
  unc = f" ±{_fmt(a['uncertainty'])}" if a.get("uncertainty") is not None else ""
115
- meaning = (gd.get("means", "").split(".")[0]) if gd else ""
116
- lines.append(f" **{gd.get('label', name)}: {val}{unc}** {meaning}. _{a.get('validation', '')}_")
151
+ # prefer the self-explanatory engine 'meaning' (says what the value means + the proxy caveat in words)
152
+ meaning = a.get("meaning") or ((gd.get("means", "").split(".")[0]) if gd else "")
153
+ lines.append(f" • **{gd.get('label', name)}: {val}{unc}** — {meaning}")
117
154
  ku = tr["immune_profile"].get("known_unknowns") or []
118
155
  if ku:
119
156
  lines.append("**What I can't tell you** (measured, never predicted): " + ", ".join(str(x) for x in ku) + ".")
@@ -353,14 +390,24 @@ def grounded_reply(message: str, history: list | None = None, *, allow_llm: bool
353
390
  base = {"mode": mode, "provenance": "pen-stack", "grounded": True, "tool_results": tr,
354
391
  "angles": None, "facts": None}
355
392
  if allow_llm:
356
- prompt = (f"TOOL RESULTS (the only source of THIS design's numbers):\n{json.dumps(tr, default=str)}\n\n"
393
+ prompt = (f"TOOL RESULTS (the only source of THIS design's numbers — including the recommended writer in "
394
+ f"`plan`):\n{json.dumps(tr, default=str)}\n\n"
357
395
  f"METRIC GUIDE (use to EXPLAIN what each number means — scale, direction, reference band; you "
358
396
  f"may cite band thresholds):\n{json.dumps(guides, default=str)}\n"
359
397
  f"SAFETY DECISIONS:\n{json.dumps(bands)}\n\n{hist}USER: {message}\n\n"
360
- f"Reply with: (1) the engine's findings WITH the numbers, (2) what each key number MEANS using "
361
- f"the metric guide (scale, what's good/bad, reference range), (3) the uncertainty + scope ledger. "
362
- f"Use ONLY numbers in the tool results or the metric guide.")
398
+ f"Reply with: (1) the engine's findings WITH the numbers if the user asked which writer, lead "
399
+ f"with `plan.recommended_writer` and its site/scores (do NOT name any other writer or vehicle), "
400
+ f"(2) what each key number MEANS in plain words (scale, what's good/bad, reference range), "
401
+ f"(3) the uncertainty + scope ledger. STRICT: use ONLY numbers present in the tool results or the "
402
+ f"metric guide; do NOT invent a writer name, a vehicle, a table, or any number. If something "
403
+ f"isn't in the tool results, say it is not computed.")
363
404
  text, backend = _run_llm(prompt, SYSTEM_GROUNDED)
364
405
  if text:
365
- return {**base, "reply": _enforce_grounding(text, allow), "backend": backend}
406
+ cleaned = _enforce_grounding(text, allow)
407
+ # Defence against fabrication-spam: if the model invented a lot of numbers (the guard struck many),
408
+ # the narrated reply is unreadable ([unverified] everywhere) — fall back to the deterministic,
409
+ # fully-grounded narration so the user always gets a clean, traceable answer.
410
+ if cleaned.count(_UNVERIFIED) >= 2:
411
+ return {**base, "reply": _deterministic_narrate(tr), "backend": f"deterministic (guard:{backend})"}
412
+ return {**base, "reply": cleaned, "backend": backend}
366
413
  return {**base, "reply": _deterministic_narrate(tr), "backend": "deterministic"}
@@ -0,0 +1,190 @@
1
+ """Engine tool-runner for the grounded co-scientist (PEN-STACK v6.2, WS-CHAT support).
2
+
3
+ The chat NEVER sources a number. This module is where the ENGINE computes everything: it parses a plain-language
4
+ goal, runs the validated tools (verify -> legality + safety + calibrated confidence + immune profile; the scope
5
+ matcher for known-unknowns), and returns a structured "dossier" of grounded facts. `extract_grounded_numbers`
6
+ returns the allow-list of values the LLM is permitted to cite — anything else it emits is stripped by the
7
+ grounding guard. No LLM is involved here; this is deterministic.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import re
12
+ from typing import Any
13
+
14
+ # tiny, transparent keyword maps (the engine grounds everything; this only routes a plain-language goal).
15
+ _VEHICLES = {"aav": "AAV_single", "aav single": "AAV_single", "aav dual": "AAV_dual", "dual aav": "AAV_dual",
16
+ "lentivir": "lentivirus", "lnp": "lnp_mrna", "mrna": "lnp_mrna", "adenovir": "helper_dependent_adenovirus",
17
+ "hsv": "hsv_amplicon", "electroporat": "electroporation"}
18
+ _INTENTS = {"safe harbour": "safe_harbour_insertion", "safe harbor": "safe_harbour_insertion",
19
+ "knock-in": "knock_in_with_disruption", "knock in": "knock_in_with_disruption",
20
+ "knockin": "knock_in_with_disruption", "durab": "high_durability_insertion",
21
+ "excis": "regulatory_element_excision", "regulator": "regulatory_element_excision",
22
+ "landing pad": "landing_pad_insertion", "repeat": "repeat_excision"}
23
+ _CELLS = {"liver": "hepg2", "hepato": "hepg2", "hepg2": "hepg2", "hspc": "hspc", "stem cell": "h1_hesc",
24
+ "ipsc": "ipsc", "k562": "k562", "t cell": "cd8_t", "t-cell": "cd8_t", "car-t": "cd8_t", "pbmc": "pbmc"}
25
+ _GENE_RE = re.compile(r"\b([A-Z][A-Z0-9]{1,7})\b") # crude gene-symbol token (AAVS1, TRAC, FIX, ...)
26
+ _KB_RE = re.compile(r"(\d+(?:\.\d+)?)\s*kb", re.I)
27
+ _BP_RE = re.compile(r"(\d{3,6})\s*bp", re.I)
28
+ # uppercase tokens that look like a gene but are not one (jargon / vehicle / form abbreviations)
29
+ _GENE_STOP = {"DNA", "RNA", "MRNA", "AAV", "LNP", "HSV", "CAR", "RNP", "PCR", "WT", "KO", "KI", "ITR",
30
+ "ORF", "UTR", "CDS", "GFP", "ID", "QC", "VG", "MOI", "HLA", "MHC", "CRISPR", "CAS", "TF"}
31
+
32
+
33
+ def _first(text: str, table: dict, default):
34
+ low = text.lower()
35
+ for key, val in table.items():
36
+ if key in low:
37
+ return val
38
+ return default
39
+
40
+
41
+ def _resolve_chrom(gene: str) -> str | None:
42
+ """The real chromosome for a gene symbol (or a safe-harbour nickname like AAVS1); None if not resolvable
43
+ offline. So a chat goal about ITGB2 carries chr21, not a hardcoded default. Atlas-gated — never fabricates."""
44
+ try:
45
+ from pen_stack.planner.optimize import gene_region, resolve_gene
46
+ reg = gene_region(resolve_gene(gene))
47
+ return reg[0] if reg else None
48
+ except Exception: # noqa: BLE001 - data/atlas absent (CI/offline) -> caller falls back to the default
49
+ return None
50
+
51
+
52
+ def parse_goal(message: str) -> dict:
53
+ """Best-effort parse of a plain-language goal into a Design/Goal dict. The engine grounds everything; this
54
+ just picks a starting point (with sensible defaults) so the tools can run."""
55
+ cargo = 3000
56
+ if (m := _KB_RE.search(message)):
57
+ cargo = int(float(m.group(1)) * 1000)
58
+ elif (m := _BP_RE.search(message)):
59
+ cargo = int(m.group(1))
60
+ genes = [g for g in _GENE_RE.findall(message) if g not in _GENE_STOP]
61
+ gene = genes[0] if genes else "AAVS1"
62
+ return {"write_type": "insertion", "gene": gene, "chrom": _resolve_chrom(gene) or "chr19",
63
+ "edit_intent": _first(message, _INTENTS, "safe_harbour_insertion"),
64
+ "delivery_vehicle": _first(message, _VEHICLES, "AAV_single"), "cargo_bp": cargo,
65
+ "cell_type": _first(message, _CELLS, "k562"),
66
+ # the user's plain-language goal IS the cargo-function description the Guardian must screen — so a
67
+ # message like "express a ricin toxin" is biosecurity-screened, not silently passed as benign.
68
+ "cargo_function": message.strip()}
69
+
70
+
71
+ # the chat's free-text intents map onto the planner's EditIntent enum (landing-pad/regulatory have nearest valid).
72
+ _INTENT_TO_ENUM = {"safe_harbour_insertion": "safe_harbour_insertion",
73
+ "high_durability_insertion": "high_durability_insertion",
74
+ "knock_in_with_disruption": "knock_in_with_disruption",
75
+ "regulatory_element_excision": "regulatory_excision",
76
+ "repeat_excision": "repeat_excision",
77
+ "landing_pad_insertion": "safe_harbour_insertion"}
78
+
79
+ # plain-language reading of each immune axis (0–1, higher = safer) — so a value is self-explanatory in the reply.
80
+ _AXIS_DIRECTION = {
81
+ "genotoxicity": "higher = safer (less integration-site oncogene risk; 1.0 = episomal / non-integrating)",
82
+ "cd8_epitope": "higher = fewer strong CD8/MHC-I capsid epitopes (1.0 = non-viral / none)",
83
+ "innate": "higher = less innate (CpG/TLR9, RIG-I) sensing of the delivered cargo",
84
+ "preexisting_nab": "higher = fewer patients excluded by pre-existing neutralizing antibodies to the vector",
85
+ "anti_peg": "higher = lower pre-existing anti-PEG barrier to re-dosing (PEGylated vehicles only)",
86
+ }
87
+
88
+
89
+ def _band_word(v: float) -> str:
90
+ return "favourable" if v >= 0.7 else ("moderate" if v >= 0.4 else "a concern")
91
+
92
+
93
+ def axis_meaning(name: str, value, validation: str | None) -> str:
94
+ """A self-explanatory, plain-language reading of one immune axis: what the number means + the proxy caveat."""
95
+ if value is None:
96
+ return "out of scope for this design — not predicted (no applicable mechanism)."
97
+ direction = _AXIS_DIRECTION.get(name, "0–1, higher = safer")
98
+ s = f"{float(value):.2f} on a 0–1 scale — {_band_word(float(value))}; {direction}."
99
+ if validation and ("proxy" in validation.lower() or "not outcome-validated" in validation.lower()):
100
+ s += (" This is a mechanistically/population-computed PROXY — it is not validated against a measured "
101
+ "clinical outcome, so read it as a directional estimate, not a guaranteed result.")
102
+ return s
103
+
104
+
105
+ def _run_planner(design: dict) -> dict[str, Any]:
106
+ """The ACTUAL writer/site recommendation for the goal (atlas-gated). This is what makes a 'which writer can
107
+ integrate N kb in GENE' question real: a named writer family, the top site, cargo-capacity fit, delivery —
108
+ all engine-computed. Honest when the gene isn't in the atlas or the atlas isn't mounted; NEVER fabricates."""
109
+ try:
110
+ from pen_stack.planner.optimize import EditIntent
111
+ from pen_stack.planner.pipeline import plan_write
112
+ except Exception: # noqa: BLE001
113
+ return {"available": False, "why": "planner unavailable in this environment"}
114
+ intent = _INTENT_TO_ENUM.get(design.get("edit_intent"), "safe_harbour_insertion")
115
+ try:
116
+ plans = plan_write(design["gene"], EditIntent(intent), int(design["cargo_bp"]),
117
+ design.get("cell_type", "k562"), k=3)
118
+ except FileNotFoundError:
119
+ return {"available": False, "why": "writability atlas not mounted (planner runs on the live app only)"}
120
+ except Exception as e: # noqa: BLE001 - never let a planner error break the chat dossier
121
+ return {"available": False, "why": f"planner error: {type(e).__name__}"}
122
+ if not plans:
123
+ return {"available": True, "found": False, "gene": design["gene"],
124
+ "why": (f"no writable plan for '{design['gene']}' — it is not in the writability atlas. Check it "
125
+ "is an HGNC gene symbol or a known safe-harbour nickname (AAVS1, H11/HIPP11).")}
126
+ top = plans[0]
127
+ cargo = top.get("cargo") or {}
128
+ return {"available": True, "found": True, "n_plans": len(plans),
129
+ "recommended_writer": top.get("writer"), "site": top.get("site"),
130
+ "safety": top.get("safety"), "durability": top.get("durability"), "score": top.get("score"),
131
+ "writer_activity": top.get("writer_activity"), "reachability_tier": top.get("reachability_tier"),
132
+ "cargo_capacity_bp": cargo.get("cargo_capacity_bp"), "assembled_bp": cargo.get("assembled_bp"),
133
+ "cargo_fits_single_vector": cargo.get("size_ok"),
134
+ "delivery": (top.get("delivery") or {}).get("delivery"),
135
+ # distinct OTHER writer families (the top-k can repeat one family across sites — don't list it twice)
136
+ "alternative_writers": sorted({p.get("writer") for p in plans[1:]
137
+ if p.get("writer") and p.get("writer") != top.get("writer")})}
138
+
139
+
140
+ def run_tools(message: str, history: list | None = None) -> dict[str, Any]:
141
+ """Run the validated engine over a plain-language message and return a grounded dossier. EVERY number here
142
+ is computed by the engine (verify / planner / scope) — no fabrication, no LLM."""
143
+ from pen_stack.agent.scope import match_scope
144
+ from pen_stack.verify import verify
145
+
146
+ design = parse_goal(message)
147
+ v = verify(dict(design), question=message)
148
+ imm = v.immune_profile or {}
149
+ axes = {k: {"value": a.get("value"), "uncertainty": a.get("uncertainty"),
150
+ "validation": a.get("validation"), "in_scope": a.get("in_scope"),
151
+ "meaning": axis_meaning(k, a.get("value"), a.get("validation"))}
152
+ for k, a in (imm.get("axes") or {}).items()}
153
+ oos = match_scope(message) # is the QUESTION out of scope (a known-unknown)?
154
+ return {
155
+ "parsed_design": design,
156
+ "plan": _run_planner(design), # the actual writer/site recommendation (atlas-gated)
157
+ "verdict": {"legal": v.legal, "confidence": v.confidence, "interval": v.interval,
158
+ "epistemic_status": v.epistemic_status,
159
+ "violations": [x.get("rule_id") for x in v.violations]},
160
+ "safety": {"decision": (v.safety.decision if v.safety else None),
161
+ "reason": (v.safety.reason if v.safety else None)},
162
+ "immune_profile": {"axes": axes, "collapsed_score": imm.get("collapsed_score"),
163
+ "known_unknowns": imm.get("known_unknowns")},
164
+ "scope": ({"out_of_scope": True, "id": oos["id"], "title": oos["title"], "why": oos.get("deferral")}
165
+ if oos else {"out_of_scope": False}),
166
+ "disclaimer": "Decision-support only; not a clinical directive. Every number is tool-sourced.",
167
+ }
168
+
169
+
170
+ _NUM_RE = re.compile(r"-?\d+(?:\.\d+)?")
171
+
172
+
173
+ def extract_grounded_numbers(tool_results: dict) -> set[str]:
174
+ """The allow-list: every numeric string that appears in the engine's tool results. The grounding guard
175
+ permits the LLM to cite ONLY these; any other number it emits is stripped."""
176
+ import json
177
+ text = json.dumps(tool_results, default=str)
178
+ grounded = set(_NUM_RE.findall(text))
179
+ # also allow the common normalised forms (e.g. 0.5 / .5 / 50%) of each grounded value
180
+ extra = set()
181
+ for n in list(grounded):
182
+ try:
183
+ f = float(n)
184
+ except ValueError:
185
+ continue
186
+ extra.add(str(int(f)) if f.is_integer() else str(f))
187
+ extra.add(f"{f:.2f}")
188
+ if 0 <= f <= 1:
189
+ extra.add(str(round(f * 100))) # percent form of a [0,1] score
190
+ return grounded | extra
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pen-stack
3
- Version: 6.4.1
3
+ Version: 6.4.2
4
4
  Summary: Open infrastructure for genome writing: the Writable Genome atlas, the Writer Atlas, and the Write Planner.
5
5
  Author-email: Anees Ahmed Mahaboob Ali <ahmedaneesm@gmail.com>
6
6
  License: MIT
@@ -90,7 +90,7 @@ every design against rule-grounded mechanism, reports calibrated confidence, cit
90
90
  [![codecov](https://codecov.io/gh/ahmedanees-m/pen-stack/branch/main/graph/badge.svg)](https://codecov.io/gh/ahmedanees-m/pen-stack)
91
91
  [![License: MIT](https://img.shields.io/badge/License-MIT-informational.svg)](LICENSE)
92
92
  [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/)
93
- [![Version](https://img.shields.io/badge/version-6.4.0-blue.svg)](CHANGELOG.md)
93
+ [![Version](https://img.shields.io/badge/version-6.4.2-blue.svg)](CHANGELOG.md)
94
94
  [![Status](https://img.shields.io/badge/status-1.0%20First%20Stable-success.svg)](docs/STABILITY.md)
95
95
  [![Tests](https://img.shields.io/badge/tests-378%20passing-success.svg)](tests/)
96
96
  [![Lint: ruff](https://img.shields.io/badge/lint-ruff-purple.svg)](https://github.com/astral-sh/ruff)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pen-stack"
7
- version = "6.4.1"
7
+ version = "6.4.2"
8
8
  description = "Open infrastructure for genome writing: the Writable Genome atlas, the Writer Atlas, and the Write Planner."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1,105 +0,0 @@
1
- """Engine tool-runner for the grounded co-scientist (PEN-STACK v6.2, WS-CHAT support).
2
-
3
- The chat NEVER sources a number. This module is where the ENGINE computes everything: it parses a plain-language
4
- goal, runs the validated tools (verify -> legality + safety + calibrated confidence + immune profile; the scope
5
- matcher for known-unknowns), and returns a structured "dossier" of grounded facts. `extract_grounded_numbers`
6
- returns the allow-list of values the LLM is permitted to cite — anything else it emits is stripped by the
7
- grounding guard. No LLM is involved here; this is deterministic.
8
- """
9
- from __future__ import annotations
10
-
11
- import re
12
- from typing import Any
13
-
14
- # tiny, transparent keyword maps (the engine grounds everything; this only routes a plain-language goal).
15
- _VEHICLES = {"aav": "AAV_single", "aav single": "AAV_single", "aav dual": "AAV_dual", "dual aav": "AAV_dual",
16
- "lentivir": "lentivirus", "lnp": "lnp_mrna", "mrna": "lnp_mrna", "adenovir": "helper_dependent_adenovirus",
17
- "hsv": "hsv_amplicon", "electroporat": "electroporation"}
18
- _INTENTS = {"safe harbour": "safe_harbour_insertion", "safe harbor": "safe_harbour_insertion",
19
- "knock-in": "knock_in_with_disruption", "knock in": "knock_in_with_disruption",
20
- "knockin": "knock_in_with_disruption", "durab": "high_durability_insertion",
21
- "excis": "regulatory_element_excision", "regulator": "regulatory_element_excision",
22
- "landing pad": "landing_pad_insertion", "repeat": "repeat_excision"}
23
- _CELLS = {"liver": "hepg2", "hepato": "hepg2", "hepg2": "hepg2", "hspc": "hspc", "stem cell": "h1_hesc",
24
- "ipsc": "ipsc", "k562": "k562", "t cell": "cd8_t", "t-cell": "cd8_t", "car-t": "cd8_t", "pbmc": "pbmc"}
25
- _GENE_RE = re.compile(r"\b([A-Z][A-Z0-9]{1,7})\b") # crude gene-symbol token (AAVS1, TRAC, FIX, ...)
26
- _KB_RE = re.compile(r"(\d+(?:\.\d+)?)\s*kb", re.I)
27
- _BP_RE = re.compile(r"(\d{3,6})\s*bp", re.I)
28
-
29
-
30
- def _first(text: str, table: dict, default):
31
- low = text.lower()
32
- for key, val in table.items():
33
- if key in low:
34
- return val
35
- return default
36
-
37
-
38
- def parse_goal(message: str) -> dict:
39
- """Best-effort parse of a plain-language goal into a Design/Goal dict. The engine grounds everything; this
40
- just picks a starting point (with sensible defaults) so the tools can run."""
41
- cargo = 3000
42
- if (m := _KB_RE.search(message)):
43
- cargo = int(float(m.group(1)) * 1000)
44
- elif (m := _BP_RE.search(message)):
45
- cargo = int(m.group(1))
46
- genes = [g for g in _GENE_RE.findall(message) if g not in {"DNA", "RNA", "AAV", "LNP", "HSV", "CAR"}]
47
- gene = genes[0] if genes else "AAVS1"
48
- return {"write_type": "insertion", "gene": gene, "chrom": "chr19",
49
- "edit_intent": _first(message, _INTENTS, "safe_harbour_insertion"),
50
- "delivery_vehicle": _first(message, _VEHICLES, "AAV_single"), "cargo_bp": cargo,
51
- "cell_type": _first(message, _CELLS, "k562"),
52
- # the user's plain-language goal IS the cargo-function description the Guardian must screen — so a
53
- # message like "express a ricin toxin" is biosecurity-screened, not silently passed as benign.
54
- "cargo_function": message.strip()}
55
-
56
-
57
- def run_tools(message: str, history: list | None = None) -> dict[str, Any]:
58
- """Run the validated engine over a plain-language message and return a grounded dossier. EVERY number here
59
- is computed by the engine (verify / scope) — no fabrication, no LLM."""
60
- from pen_stack.agent.scope import match_scope
61
- from pen_stack.verify import verify
62
-
63
- design = parse_goal(message)
64
- v = verify(dict(design), question=message)
65
- imm = v.immune_profile or {}
66
- axes = {k: {"value": a.get("value"), "uncertainty": a.get("uncertainty"),
67
- "validation": a.get("validation"), "in_scope": a.get("in_scope")}
68
- for k, a in (imm.get("axes") or {}).items()}
69
- oos = match_scope(message) # is the QUESTION out of scope (a known-unknown)?
70
- return {
71
- "parsed_design": design,
72
- "verdict": {"legal": v.legal, "confidence": v.confidence, "interval": v.interval,
73
- "epistemic_status": v.epistemic_status,
74
- "violations": [x.get("rule_id") for x in v.violations]},
75
- "safety": {"decision": (v.safety.decision if v.safety else None),
76
- "reason": (v.safety.reason if v.safety else None)},
77
- "immune_profile": {"axes": axes, "collapsed_score": imm.get("collapsed_score"),
78
- "known_unknowns": imm.get("known_unknowns")},
79
- "scope": ({"out_of_scope": True, "id": oos["id"], "title": oos["title"], "why": oos.get("deferral")}
80
- if oos else {"out_of_scope": False}),
81
- "disclaimer": "Decision-support only; not a clinical directive. Every number is tool-sourced.",
82
- }
83
-
84
-
85
- _NUM_RE = re.compile(r"-?\d+(?:\.\d+)?")
86
-
87
-
88
- def extract_grounded_numbers(tool_results: dict) -> set[str]:
89
- """The allow-list: every numeric string that appears in the engine's tool results. The grounding guard
90
- permits the LLM to cite ONLY these; any other number it emits is stripped."""
91
- import json
92
- text = json.dumps(tool_results, default=str)
93
- grounded = set(_NUM_RE.findall(text))
94
- # also allow the common normalised forms (e.g. 0.5 / .5 / 50%) of each grounded value
95
- extra = set()
96
- for n in list(grounded):
97
- try:
98
- f = float(n)
99
- except ValueError:
100
- continue
101
- extra.add(str(int(f)) if f.is_integer() else str(f))
102
- extra.add(f"{f:.2f}")
103
- if 0 <= f <= 1:
104
- extra.add(str(round(f * 100))) # percent form of a [0,1] score
105
- return grounded | extra
File without changes
File without changes
File without changes
File without changes