pen-stack 6.4.1__tar.gz → 6.4.3__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.3}/CHANGELOG.md +46 -0
  2. {pen_stack-6.4.1 → pen_stack-6.4.3}/CITATION.cff +2 -2
  3. {pen_stack-6.4.1 → pen_stack-6.4.3}/PKG-INFO +2 -2
  4. {pen_stack-6.4.1 → pen_stack-6.4.3}/README.md +1 -1
  5. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/__init__.py +1 -1
  6. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/web/llm.py +62 -15
  7. pen_stack-6.4.3/pen_stack/web/tools.py +197 -0
  8. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack.egg-info/PKG-INFO +2 -2
  9. {pen_stack-6.4.1 → pen_stack-6.4.3}/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.3}/LICENSE +0 -0
  12. {pen_stack-6.4.1 → pen_stack-6.4.3}/MANIFEST.in +0 -0
  13. {pen_stack-6.4.1 → pen_stack-6.4.3}/bench/run.py +0 -0
  14. {pen_stack-6.4.1 → pen_stack-6.4.3}/benchmarks/genome_writing_bench/LEADERBOARD.md +0 -0
  15. {pen_stack-6.4.1 → pen_stack-6.4.3}/benchmarks/genome_writing_bench/README.md +0 -0
  16. {pen_stack-6.4.1 → pen_stack-6.4.3}/benchmarks/genome_writing_bench/SHA256SUMS +0 -0
  17. {pen_stack-6.4.1 → pen_stack-6.4.3}/benchmarks/genome_writing_bench/SUBMISSIONS.md +0 -0
  18. {pen_stack-6.4.1 → pen_stack-6.4.3}/benchmarks/genome_writing_bench/tasks.yaml +0 -0
  19. {pen_stack-6.4.1 → pen_stack-6.4.3}/benchmarks/genome_writing_challenge/README.md +0 -0
  20. {pen_stack-6.4.1 → pen_stack-6.4.3}/benchmarks/genome_writing_challenge/SUBMISSIONS.md +0 -0
  21. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/antipeg.yaml +0 -0
  22. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/atlas_families.yaml +0 -0
  23. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/bridge_offtarget_profile.yaml +0 -0
  24. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/capsid_epitope_oracle.yaml +0 -0
  25. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/capsid_sequences.fasta +0 -0
  26. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/cargo_polish.yaml +0 -0
  27. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/cell_types.yaml +0 -0
  28. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/datasets.yaml +0 -0
  29. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/delivery_constraints.yaml +0 -0
  30. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/delivery_rules.yaml +0 -0
  31. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/delivery_vehicles.yaml +0 -0
  32. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/gates_v3.yaml +0 -0
  33. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/genotoxicity_oracle.yaml +0 -0
  34. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/gsh_validated_heldout.yaml +0 -0
  35. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/intent_weights.yaml +0 -0
  36. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/known_unknowns.yaml +0 -0
  37. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/llm.yaml +0 -0
  38. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/metric_guide.yaml +0 -0
  39. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/monitor_queries.yaml +0 -0
  40. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/oracles/execution.yaml +0 -0
  41. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/oracles/scope_cards.yaml +0 -0
  42. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/rules/delivery.yaml +0 -0
  43. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/rules/fold.yaml +0 -0
  44. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/rules/multiplex.yaml +0 -0
  45. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/rules/payload.yaml +0 -0
  46. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/rules/reachability.yaml +0 -0
  47. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/safety/hazard_registry.yaml +0 -0
  48. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/safety/policy.yaml +0 -0
  49. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/safety/probes.yaml +0 -0
  50. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/score_axes.yaml +0 -0
  51. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/seroprevalence.yaml +0 -0
  52. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/target_sites.yaml +0 -0
  53. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/universe_crosswalk.yaml +0 -0
  54. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/write_types.yaml +0 -0
  55. {pen_stack-6.4.1 → pen_stack-6.4.3}/configs/wtkb_curated.yaml +0 -0
  56. {pen_stack-6.4.1 → pen_stack-6.4.3}/data/curated/bridge_offtarget_energetics.json +0 -0
  57. {pen_stack-6.4.1 → pen_stack-6.4.3}/data/curated/bridge_offtarget_profile_measured.parquet +0 -0
  58. {pen_stack-6.4.1 → pen_stack-6.4.3}/data/curated/gene_coords.parquet +0 -0
  59. {pen_stack-6.4.1 → pen_stack-6.4.3}/data/curated/unified_editor_universe.parquet +0 -0
  60. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/BACKLOG.md +0 -0
  61. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/DEPLOY.md +0 -0
  62. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/INFRA.md +0 -0
  63. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/MCP.md +0 -0
  64. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/RELEASING.md +0 -0
  65. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/REPRO.md +0 -0
  66. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/STABILITY.md +0 -0
  67. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/agent.md +0 -0
  68. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/alphagenome_feasibility.md +0 -0
  69. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/autonomy.md +0 -0
  70. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/benchmark_circularity.md +0 -0
  71. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/biosecurity.md +0 -0
  72. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/build_interface.md +0 -0
  73. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/cards/atlas.md +0 -0
  74. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/cards/durability.md +0 -0
  75. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/cards/safety.md +0 -0
  76. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/challenge.md +0 -0
  77. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/closed_loop.md +0 -0
  78. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/co_scientist.md +0 -0
  79. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/co_scientist_loop.md +0 -0
  80. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/delivery.md +0 -0
  81. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/delivery_immunology.md +0 -0
  82. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/digital_twin.md +0 -0
  83. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/dissemination.md +0 -0
  84. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/environment.md +0 -0
  85. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/experiment_design.md +0 -0
  86. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/generative_design.md +0 -0
  87. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/index.md +0 -0
  88. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/integrations.md +0 -0
  89. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/live_oracles.md +0 -0
  90. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/mechanistic_constraints.md +0 -0
  91. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/oracles.md +0 -0
  92. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/positioning.md +0 -0
  93. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/private_data_formats.md +0 -0
  94. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/quickstart.md +0 -0
  95. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/responsible_use.md +0 -0
  96. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/rules.md +0 -0
  97. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/scope.md +0 -0
  98. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/scorecard.md +0 -0
  99. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/tutorials/compare-families.md +0 -0
  100. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/tutorials/score-deliverability.md +0 -0
  101. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/tutorials/where-can-i-write.md +0 -0
  102. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/tutorials/which-writer-reaches-locus.md +0 -0
  103. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/uncertainty.md +0 -0
  104. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/verify.md +0 -0
  105. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/world_model.md +0 -0
  106. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/writer_verification.md +0 -0
  107. {pen_stack-6.4.1 → pen_stack-6.4.3}/docs/wtkb.md +0 -0
  108. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/_resources.py +0 -0
  109. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/active/__init__.py +0 -0
  110. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/active/acquire.py +0 -0
  111. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/active/design.py +0 -0
  112. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/active/validate.py +0 -0
  113. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/adapt/__init__.py +0 -0
  114. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/adapt/finetune.py +0 -0
  115. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/adapt/ingest.py +0 -0
  116. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/adapt/pipeline.py +0 -0
  117. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/adapt/recalibrate.py +0 -0
  118. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/adapt/report.py +0 -0
  119. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/__init__.py +0 -0
  120. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/cite.py +0 -0
  121. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/co_scientist.py +0 -0
  122. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/epistemic.py +0 -0
  123. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/guardrails.py +0 -0
  124. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/mcp_server.py +0 -0
  125. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/orchestrator.py +0 -0
  126. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/orchestrator_live.py +0 -0
  127. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/pen_agent.py +0 -0
  128. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/scope.py +0 -0
  129. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/agent/tools.py +0 -0
  130. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/api/__init__.py +0 -0
  131. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/api/manifest.py +0 -0
  132. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/atlas/__init__.py +0 -0
  133. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/atlas/build_wtkb.py +0 -0
  134. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/atlas/crosslink.py +0 -0
  135. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/atlas/expand.py +0 -0
  136. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/atlas/schema.py +0 -0
  137. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/atlas/scorecard.py +0 -0
  138. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/atlas/universe.py +0 -0
  139. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/atlas/variant_propose.py +0 -0
  140. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/atlas/writer_verify.py +0 -0
  141. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/__init__.py +0 -0
  142. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/activity.py +0 -0
  143. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/cli.py +0 -0
  144. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/fold_qc.py +0 -0
  145. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/guide_qc.py +0 -0
  146. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/ingest.py +0 -0
  147. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/offtarget.py +0 -0
  148. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/offtarget_energetics.py +0 -0
  149. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/ortholog_screen.py +0 -0
  150. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/bridge/pipeline.py +0 -0
  151. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/build/__init__.py +0 -0
  152. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/build/ingest.py +0 -0
  153. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/build/protocol.py +0 -0
  154. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/build/simlab.py +0 -0
  155. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/cli.py +0 -0
  156. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/data/__init__.py +0 -0
  157. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/data/encode.py +0 -0
  158. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/data/genome.py +0 -0
  159. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/data/ingest_chromatin.py +0 -0
  160. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/data/ingest_integration.py +0 -0
  161. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/data/ingest_safety_annot.py +0 -0
  162. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/data/ingest_trip.py +0 -0
  163. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/design/__init__.py +0 -0
  164. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/design/generate.py +0 -0
  165. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/design/pareto.py +0 -0
  166. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/design/space.py +0 -0
  167. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/env/__init__.py +0 -0
  168. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/env/genome_writing_env.py +0 -0
  169. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/env/policies.py +0 -0
  170. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/graph/__init__.py +0 -0
  171. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/graph/build.py +0 -0
  172. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/graph/cell_types.py +0 -0
  173. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/graph/ingest.py +0 -0
  174. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/graph/query.py +0 -0
  175. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/graph/schema.py +0 -0
  176. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/loop/__init__.py +0 -0
  177. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/loop/continual.py +0 -0
  178. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/loop/cycle.py +0 -0
  179. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/loop/drift.py +0 -0
  180. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/mech/__init__.py +0 -0
  181. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/mech/classify_atlas.py +0 -0
  182. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/mech/whitelist.py +0 -0
  183. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/monitor/__init__.py +0 -0
  184. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/monitor/europepmc.py +0 -0
  185. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/monitor/run.py +0 -0
  186. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/monitor/triage.py +0 -0
  187. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/__init__.py +0 -0
  188. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/cache.py +0 -0
  189. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/energetics.py +0 -0
  190. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/genome.py +0 -0
  191. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/protein_design.py +0 -0
  192. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/rna.py +0 -0
  193. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/schema.py +0 -0
  194. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/status.py +0 -0
  195. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/structure.py +0 -0
  196. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/oracles/vcell.py +0 -0
  197. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/__init__.py +0 -0
  198. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/antipeg_oracle.py +0 -0
  199. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/capsid_epitope_oracle.py +0 -0
  200. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/cargo.py +0 -0
  201. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/cargo_polish.py +0 -0
  202. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/delivery.py +0 -0
  203. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/delivery_constraints.py +0 -0
  204. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/delivery_immunology.py +0 -0
  205. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/delivery_vehicles.py +0 -0
  206. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/genotoxicity_oracle.py +0 -0
  207. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/immune_profile.py +0 -0
  208. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/innate_sensing.py +0 -0
  209. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/multiplex.py +0 -0
  210. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/optimize.py +0 -0
  211. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/pipeline.py +0 -0
  212. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/report.py +0 -0
  213. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/router.py +0 -0
  214. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/seroprevalence_oracle.py +0 -0
  215. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/planner/target_site.py +0 -0
  216. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/rag/__init__.py +0 -0
  217. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/rag/index.py +0 -0
  218. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/rag/llm.py +0 -0
  219. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/rag/qa.py +0 -0
  220. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/rules/__init__.py +0 -0
  221. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/rules/evaluators.py +0 -0
  222. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/rules/loader.py +0 -0
  223. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/rules/schema.py +0 -0
  224. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/rules/solver.py +0 -0
  225. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/safety/__init__.py +0 -0
  226. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/safety/audit.py +0 -0
  227. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/safety/gate.py +0 -0
  228. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/safety/policy.py +0 -0
  229. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/safety/redteam.py +0 -0
  230. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/safety/registry.py +0 -0
  231. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/safety/screen.py +0 -0
  232. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/score/__init__.py +0 -0
  233. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/score/recalibrate.py +0 -0
  234. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/score/therapeutic.py +0 -0
  235. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/server/__init__.py +0 -0
  236. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/server/api.py +0 -0
  237. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/twin/__init__.py +0 -0
  238. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/twin/calibrate.py +0 -0
  239. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/twin/mechanistic.py +0 -0
  240. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/twin/outcome.py +0 -0
  241. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/ui/__init__.py +0 -0
  242. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/ui/app.py +0 -0
  243. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/__init__.py +0 -0
  244. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/adapt_demo.py +0 -0
  245. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/agent_eval.py +0 -0
  246. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/bench_adversarial_tasks.py +0 -0
  247. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/bench_coscientist_tasks.py +0 -0
  248. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/bench_graph_tasks.py +0 -0
  249. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/bench_rule_tasks.py +0 -0
  250. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/bench_trust_tasks.py +0 -0
  251. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/bench_writetype_tasks.py +0 -0
  252. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/blind_gsh_discovery.py +0 -0
  253. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/cargo_directionality.py +0 -0
  254. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/closed_loop.py +0 -0
  255. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/durability_baselines.py +0 -0
  256. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/experiment_design.py +0 -0
  257. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/forward_hypotheses.py +0 -0
  258. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/generative_design.py +0 -0
  259. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/guide_qc_demo.py +0 -0
  260. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/immune_calibration.py +0 -0
  261. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/intent_specification.py +0 -0
  262. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/offtarget_energetics_eval.py +0 -0
  263. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/out_of_scope_refusal.py +0 -0
  264. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/outcome_calibration.py +0 -0
  265. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/outcome_prediction.py +0 -0
  266. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/paper3_benchmark.py +0 -0
  267. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/paper4_real_validation.py +0 -0
  268. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/paper4_validation.py +0 -0
  269. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/protocol_safety.py +0 -0
  270. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/safety_screening.py +0 -0
  271. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/selective_prediction.py +0 -0
  272. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/seq_vs_measured.py +0 -0
  273. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/target_site_controls.py +0 -0
  274. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/uncertainty_eval.py +0 -0
  275. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/ungrounded_baseline.py +0 -0
  276. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/within_locus_ranking.py +0 -0
  277. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/validate/writer_recovery.py +0 -0
  278. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/verify/__init__.py +0 -0
  279. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/verify/schema.py +0 -0
  280. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/verify/service.py +0 -0
  281. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/web/__init__.py +0 -0
  282. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/web/guide.py +0 -0
  283. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/web/router.py +0 -0
  284. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/web/server.py +0 -0
  285. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/__init__.py +0 -0
  286. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/chromatin_seq.py +0 -0
  287. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/durability.py +0 -0
  288. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/export_tracks.py +0 -0
  289. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/features.py +0 -0
  290. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/gsh_baseline.py +0 -0
  291. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/mesh_features.py +0 -0
  292. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/ood.py +0 -0
  293. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/providers.py +0 -0
  294. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/safety.py +0 -0
  295. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/structure3d.py +0 -0
  296. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/uncertainty.py +0 -0
  297. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack/wgenome/writability.py +0 -0
  298. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack.egg-info/SOURCES.txt +0 -0
  299. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack.egg-info/dependency_links.txt +0 -0
  300. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack.egg-info/entry_points.txt +0 -0
  301. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack.egg-info/requires.txt +0 -0
  302. {pen_stack-6.4.1 → pen_stack-6.4.3}/pen_stack.egg-info/top_level.txt +0 -0
  303. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_phase0.json +0 -0
  304. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_phase1_5.json +0 -0
  305. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_phase2.json +0 -0
  306. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_phase3.json +0 -0
  307. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_a.json +0 -0
  308. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_acq.json +0 -0
  309. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_aldesign.json +0 -0
  310. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_alvalidate.json +0 -0
  311. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_atlas.json +0 -0
  312. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_b.json +0 -0
  313. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_ba.json +0 -0
  314. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_ba_v33.json +0 -0
  315. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_ba_v45.json +0 -0
  316. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_bench.json +0 -0
  317. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_c.json +0 -0
  318. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_cal.json +0 -0
  319. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_calib.json +0 -0
  320. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_challenge.json +0 -0
  321. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_chat.json +0 -0
  322. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_cite.json +0 -0
  323. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_continual.json +0 -0
  324. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_cosci2.json +0 -0
  325. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_crit.json +0 -0
  326. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_ct.json +0 -0
  327. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_d.json +0 -0
  328. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_drift.json +0 -0
  329. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_e.json +0 -0
  330. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_env.json +0 -0
  331. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_ep.json +0 -0
  332. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_epitope.json +0 -0
  333. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_f.json +0 -0
  334. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_frontend.json +0 -0
  335. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_g.json +0 -0
  336. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_gen.json +0 -0
  337. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_genotox.json +0 -0
  338. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_graph.json +0 -0
  339. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_h.json +0 -0
  340. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_hybrid.json +0 -0
  341. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_immune.json +0 -0
  342. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_ingest.json +0 -0
  343. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_innate.json +0 -0
  344. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_loop.json +0 -0
  345. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_manifest.json +0 -0
  346. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_mc.json +0 -0
  347. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_mcp.json +0 -0
  348. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_mech.json +0 -0
  349. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_mon.json +0 -0
  350. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_o.json +0 -0
  351. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_openapi.json +0 -0
  352. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_orch.json +0 -0
  353. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_outcome.json +0 -0
  354. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_pareto.json +0 -0
  355. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_peg.json +0 -0
  356. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_plan.json +0 -0
  357. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_policy.json +0 -0
  358. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_profile.json +0 -0
  359. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_proto.json +0 -0
  360. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_r.json +0 -0
  361. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_redteam.json +0 -0
  362. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_route.json +0 -0
  363. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_screen.json +0 -0
  364. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_seroprev.json +0 -0
  365. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_simlab.json +0 -0
  366. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_twincal.json +0 -0
  367. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_uq.json +0 -0
  368. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_v.json +0 -0
  369. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_vcell.json +0 -0
  370. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/SHA256_LOCK_ws_wv.json +0 -0
  371. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/paper1.yaml +0 -0
  372. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/paper2.yaml +0 -0
  373. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/paper3.yaml +0 -0
  374. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/paper4.yaml +0 -0
  375. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/phase0.yaml +0 -0
  376. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_a.yaml +0 -0
  377. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_acq.yaml +0 -0
  378. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_aldesign.yaml +0 -0
  379. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_alvalidate.yaml +0 -0
  380. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_atlas.yaml +0 -0
  381. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_b.yaml +0 -0
  382. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_ba.yaml +0 -0
  383. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_ba_v33.yaml +0 -0
  384. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_ba_v45.yaml +0 -0
  385. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_bench.yaml +0 -0
  386. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_c.yaml +0 -0
  387. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_cal.yaml +0 -0
  388. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_calib.yaml +0 -0
  389. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_challenge.yaml +0 -0
  390. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_chat.yaml +0 -0
  391. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_cite.yaml +0 -0
  392. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_continual.yaml +0 -0
  393. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_cosci2.yaml +0 -0
  394. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_crit.yaml +0 -0
  395. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_ct.yaml +0 -0
  396. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_d.yaml +0 -0
  397. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_drift.yaml +0 -0
  398. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_e.yaml +0 -0
  399. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_env.yaml +0 -0
  400. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_ep.yaml +0 -0
  401. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_epitope.yaml +0 -0
  402. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_f.yaml +0 -0
  403. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_frontend.yaml +0 -0
  404. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_g.yaml +0 -0
  405. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_gen.yaml +0 -0
  406. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_genotox.yaml +0 -0
  407. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_graph.yaml +0 -0
  408. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_h.yaml +0 -0
  409. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_hybrid.yaml +0 -0
  410. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_immune.yaml +0 -0
  411. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_ingest.yaml +0 -0
  412. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_innate.yaml +0 -0
  413. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_loop.yaml +0 -0
  414. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_manifest.yaml +0 -0
  415. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_mc.yaml +0 -0
  416. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_mcp.yaml +0 -0
  417. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_mech.yaml +0 -0
  418. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_mon.yaml +0 -0
  419. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_o.yaml +0 -0
  420. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_openapi.yaml +0 -0
  421. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_orch.yaml +0 -0
  422. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_outcome.yaml +0 -0
  423. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_pareto.yaml +0 -0
  424. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_peg.yaml +0 -0
  425. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_plan.yaml +0 -0
  426. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_policy.yaml +0 -0
  427. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_profile.yaml +0 -0
  428. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_proto.yaml +0 -0
  429. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_r.yaml +0 -0
  430. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_redteam.yaml +0 -0
  431. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_route.yaml +0 -0
  432. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_screen.yaml +0 -0
  433. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_seroprev.yaml +0 -0
  434. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_simlab.yaml +0 -0
  435. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_twincal.yaml +0 -0
  436. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_uq.yaml +0 -0
  437. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_v.yaml +0 -0
  438. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_vcell.yaml +0 -0
  439. {pen_stack-6.4.1 → pen_stack-6.4.3}/prereg/ws_wv.yaml +0 -0
  440. {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p1_build_atlas.py +0 -0
  441. {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p1_build_durability.py +0 -0
  442. {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p1_export_tracks.py +0 -0
  443. {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p1_safety_concordance.py +0 -0
  444. {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p1_train_safety.py +0 -0
  445. {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p1_validation_report.py +0 -0
  446. {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p2_build_atlas.py +0 -0
  447. {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p3_benchmark_report.py +0 -0
  448. {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p4_genome_scan.py +0 -0
  449. {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p52_build_genotox_oracle.py +0 -0
  450. {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/p53_build_epitope_oracle.py +0 -0
  451. {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/ws_b_report.py +0 -0
  452. {pen_stack-6.4.1 → pen_stack-6.4.3}/scripts/ws_c_report.py +0 -0
  453. {pen_stack-6.4.1 → pen_stack-6.4.3}/setup.cfg +0 -0
@@ -3,6 +3,52 @@
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.3] - 2026-06-12 - Chat vehicle-parse fix (AAVS1 no longer hijacks the vehicle)
7
+
8
+ **PATCH.** `web/tools.py::parse_goal` matched the delivery vehicle by substring, so the safe-harbour nickname
9
+ **AAVS1** (which contains "aav") wrongly selected the **AAV** vehicle even when the user said *lentivirus* or
10
+ *LNP* — producing the wrong immune profile (e.g. genotoxicity 1.0 instead of lentivirus's 0.481). Fix: strip the
11
+ safe-harbour nicknames (AAVS1/H11/HIPP11/ROSA26) from the vehicle-search text before matching, so the user's
12
+ stated vehicle wins. Explicit "AAV" still selects AAV. New test locks all three cases.
13
+
14
+ ## [6.4.2] - 2026-06-12 - Co-Scientist chat: real writer recommendations + self-explanatory values
15
+
16
+ **PATCH — fixes a stale/confusing chat experience reported from the live app.** Three problems, three fixes:
17
+
18
+ 1. **The chat dossier was generic and barely changed per query.** `web/tools.py::parse_goal` hardcoded
19
+ `chrom="chr19"` (so a goal about ITGB2 — chr21 — was mis-located) and `run_tools` never called the planner,
20
+ so a "which writer can integrate N kb in GENE" question got a vehicle-keyed immune profile but **no named
21
+ writer**. Fix: `parse_goal` now resolves the gene's REAL chromosome (`planner.gene_region`, atlas-gated,
22
+ falls back offline), and `run_tools` runs the planner and attaches a `plan` block — the recommended writer
23
+ family, top site, safety/durability/score, cargo-capacity fit, and delivery — all engine-computed. Honest
24
+ when the gene isn't in the atlas ("not found — check the HGNC symbol"); never fabricated.
25
+ 2. **The LLM narration produced `[unverified]` spam.** With no writer in the tool results, the model invented
26
+ one (`AAV9-CRISPR` + made-up numbers) and the grounding guard struck them all. Fix: the system prompt now
27
+ forbids inventing a writer/vehicle/table/number, and if the guard still strikes ≥2 numbers the reply falls
28
+ back to the fully-grounded deterministic narration — so the spam never reaches the user.
29
+ 3. **Values + the "⚠ extrapolating" badge weren't self-explanatory.** Each immune axis now carries a
30
+ plain-language `meaning` ("0.55 on a 0–1 scale — moderate; higher = fewer patients excluded …; this is a
31
+ proxy, not validated against a measured outcome"), the deterministic narration uses it, and
32
+ `ImmuneProfileCard` renders it plus a one-line legend explaining that "extrapolating" = a proxy estimate.
33
+
34
+ ### Added
35
+ - `tests/unit/test_ws_chat.py` — chrom resolution, axis-meaning, real writer plan, and the no-`[unverified]`-spam
36
+ fallback are locked by new tests.
37
+
38
+ ## [6.4.1] - 2026-06-12 - Defence-in-depth: pre-route safety screen
39
+
40
+ **PATCH.** The grounded co-scientist chat ran the Guardian (biosecurity gate) only in the *design* lane (via
41
+ `run_tools`); a hazardous request with no design signal (no vehicle/locus) routed to `general`/`explain`/`meta`
42
+ and was never screened. Fix: `pen_stack/web/llm.py::_pre_route_safety` runs the Guardian — framing-stripped, the
43
+ authority on the decision — at the **top** of `grounded_reply()`, before lane routing. A `refuse`/`escalate`
44
+ verdict short-circuits to a clear decline (`mode="safety"`, with the decision in `tool_results`). Benign
45
+ hazard-adjacent biology (vaccines, generic pathogen questions) is **not** blocked — the Guardian clears it and
46
+ routing continues. A broad regex only decides *whether* to invoke the Guardian; a false trigger is harmless.
47
+
48
+ ### Added
49
+ - `tests/unit/test_ws_chat.py` — `test_pre_route_safety_screens_a_hazardous_general_query` and
50
+ `test_pre_route_safety_does_not_over_refuse_benign_questions` lock both sides of the screen.
51
+
6
52
  ## [6.4.0] - 2026-06-12 - v6.4.0: Live Oracles (the foundation models actually execute)
7
53
 
8
54
  **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.3
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.3
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.3-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.3-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.3"
@@ -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,197 @@
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
+ # safe-harbour locus nicknames whose text collides with a vehicle keyword (AAVS1 ⊃ "aav") — stripped before
32
+ # vehicle matching so the user's stated vehicle (e.g. lentivirus) is not overridden by the locus name.
33
+ _SAFE_HARBOUR_RE = re.compile(r"\b(aavs1|h11|hipp11|rosa26)\b", re.I)
34
+
35
+
36
+ def _first(text: str, table: dict, default):
37
+ low = text.lower()
38
+ for key, val in table.items():
39
+ if key in low:
40
+ return val
41
+ return default
42
+
43
+
44
+ def _resolve_chrom(gene: str) -> str | None:
45
+ """The real chromosome for a gene symbol (or a safe-harbour nickname like AAVS1); None if not resolvable
46
+ offline. So a chat goal about ITGB2 carries chr21, not a hardcoded default. Atlas-gated — never fabricates."""
47
+ try:
48
+ from pen_stack.planner.optimize import gene_region, resolve_gene
49
+ reg = gene_region(resolve_gene(gene))
50
+ return reg[0] if reg else None
51
+ except Exception: # noqa: BLE001 - data/atlas absent (CI/offline) -> caller falls back to the default
52
+ return None
53
+
54
+
55
+ def parse_goal(message: str) -> dict:
56
+ """Best-effort parse of a plain-language goal into a Design/Goal dict. The engine grounds everything; this
57
+ just picks a starting point (with sensible defaults) so the tools can run."""
58
+ cargo = 3000
59
+ if (m := _KB_RE.search(message)):
60
+ cargo = int(float(m.group(1)) * 1000)
61
+ elif (m := _BP_RE.search(message)):
62
+ cargo = int(m.group(1))
63
+ genes = [g for g in _GENE_RE.findall(message) if g not in _GENE_STOP]
64
+ gene = genes[0] if genes else "AAVS1"
65
+ # Vehicle matching is substring-based (so "lentivir" catches "lentiviral"); but the safe-harbour nickname
66
+ # "AAVS1" contains "aav", which would wrongly match the AAV vehicle even when the user said lentivirus/LNP.
67
+ # Strip those nicknames from the vehicle-search text first so the stated vehicle wins.
68
+ veh_text = _SAFE_HARBOUR_RE.sub(" ", message.lower())
69
+ return {"write_type": "insertion", "gene": gene, "chrom": _resolve_chrom(gene) or "chr19",
70
+ "edit_intent": _first(message, _INTENTS, "safe_harbour_insertion"),
71
+ "delivery_vehicle": _first(veh_text, _VEHICLES, "AAV_single"), "cargo_bp": cargo,
72
+ "cell_type": _first(message, _CELLS, "k562"),
73
+ # the user's plain-language goal IS the cargo-function description the Guardian must screen — so a
74
+ # message like "express a ricin toxin" is biosecurity-screened, not silently passed as benign.
75
+ "cargo_function": message.strip()}
76
+
77
+
78
+ # the chat's free-text intents map onto the planner's EditIntent enum (landing-pad/regulatory have nearest valid).
79
+ _INTENT_TO_ENUM = {"safe_harbour_insertion": "safe_harbour_insertion",
80
+ "high_durability_insertion": "high_durability_insertion",
81
+ "knock_in_with_disruption": "knock_in_with_disruption",
82
+ "regulatory_element_excision": "regulatory_excision",
83
+ "repeat_excision": "repeat_excision",
84
+ "landing_pad_insertion": "safe_harbour_insertion"}
85
+
86
+ # plain-language reading of each immune axis (0–1, higher = safer) — so a value is self-explanatory in the reply.
87
+ _AXIS_DIRECTION = {
88
+ "genotoxicity": "higher = safer (less integration-site oncogene risk; 1.0 = episomal / non-integrating)",
89
+ "cd8_epitope": "higher = fewer strong CD8/MHC-I capsid epitopes (1.0 = non-viral / none)",
90
+ "innate": "higher = less innate (CpG/TLR9, RIG-I) sensing of the delivered cargo",
91
+ "preexisting_nab": "higher = fewer patients excluded by pre-existing neutralizing antibodies to the vector",
92
+ "anti_peg": "higher = lower pre-existing anti-PEG barrier to re-dosing (PEGylated vehicles only)",
93
+ }
94
+
95
+
96
+ def _band_word(v: float) -> str:
97
+ return "favourable" if v >= 0.7 else ("moderate" if v >= 0.4 else "a concern")
98
+
99
+
100
+ def axis_meaning(name: str, value, validation: str | None) -> str:
101
+ """A self-explanatory, plain-language reading of one immune axis: what the number means + the proxy caveat."""
102
+ if value is None:
103
+ return "out of scope for this design — not predicted (no applicable mechanism)."
104
+ direction = _AXIS_DIRECTION.get(name, "0–1, higher = safer")
105
+ s = f"{float(value):.2f} on a 0–1 scale — {_band_word(float(value))}; {direction}."
106
+ if validation and ("proxy" in validation.lower() or "not outcome-validated" in validation.lower()):
107
+ s += (" This is a mechanistically/population-computed PROXY — it is not validated against a measured "
108
+ "clinical outcome, so read it as a directional estimate, not a guaranteed result.")
109
+ return s
110
+
111
+
112
+ def _run_planner(design: dict) -> dict[str, Any]:
113
+ """The ACTUAL writer/site recommendation for the goal (atlas-gated). This is what makes a 'which writer can
114
+ integrate N kb in GENE' question real: a named writer family, the top site, cargo-capacity fit, delivery —
115
+ all engine-computed. Honest when the gene isn't in the atlas or the atlas isn't mounted; NEVER fabricates."""
116
+ try:
117
+ from pen_stack.planner.optimize import EditIntent
118
+ from pen_stack.planner.pipeline import plan_write
119
+ except Exception: # noqa: BLE001
120
+ return {"available": False, "why": "planner unavailable in this environment"}
121
+ intent = _INTENT_TO_ENUM.get(design.get("edit_intent"), "safe_harbour_insertion")
122
+ try:
123
+ plans = plan_write(design["gene"], EditIntent(intent), int(design["cargo_bp"]),
124
+ design.get("cell_type", "k562"), k=3)
125
+ except FileNotFoundError:
126
+ return {"available": False, "why": "writability atlas not mounted (planner runs on the live app only)"}
127
+ except Exception as e: # noqa: BLE001 - never let a planner error break the chat dossier
128
+ return {"available": False, "why": f"planner error: {type(e).__name__}"}
129
+ if not plans:
130
+ return {"available": True, "found": False, "gene": design["gene"],
131
+ "why": (f"no writable plan for '{design['gene']}' — it is not in the writability atlas. Check it "
132
+ "is an HGNC gene symbol or a known safe-harbour nickname (AAVS1, H11/HIPP11).")}
133
+ top = plans[0]
134
+ cargo = top.get("cargo") or {}
135
+ return {"available": True, "found": True, "n_plans": len(plans),
136
+ "recommended_writer": top.get("writer"), "site": top.get("site"),
137
+ "safety": top.get("safety"), "durability": top.get("durability"), "score": top.get("score"),
138
+ "writer_activity": top.get("writer_activity"), "reachability_tier": top.get("reachability_tier"),
139
+ "cargo_capacity_bp": cargo.get("cargo_capacity_bp"), "assembled_bp": cargo.get("assembled_bp"),
140
+ "cargo_fits_single_vector": cargo.get("size_ok"),
141
+ "delivery": (top.get("delivery") or {}).get("delivery"),
142
+ # distinct OTHER writer families (the top-k can repeat one family across sites — don't list it twice)
143
+ "alternative_writers": sorted({p.get("writer") for p in plans[1:]
144
+ if p.get("writer") and p.get("writer") != top.get("writer")})}
145
+
146
+
147
+ def run_tools(message: str, history: list | None = None) -> dict[str, Any]:
148
+ """Run the validated engine over a plain-language message and return a grounded dossier. EVERY number here
149
+ is computed by the engine (verify / planner / scope) — no fabrication, no LLM."""
150
+ from pen_stack.agent.scope import match_scope
151
+ from pen_stack.verify import verify
152
+
153
+ design = parse_goal(message)
154
+ v = verify(dict(design), question=message)
155
+ imm = v.immune_profile or {}
156
+ axes = {k: {"value": a.get("value"), "uncertainty": a.get("uncertainty"),
157
+ "validation": a.get("validation"), "in_scope": a.get("in_scope"),
158
+ "meaning": axis_meaning(k, a.get("value"), a.get("validation"))}
159
+ for k, a in (imm.get("axes") or {}).items()}
160
+ oos = match_scope(message) # is the QUESTION out of scope (a known-unknown)?
161
+ return {
162
+ "parsed_design": design,
163
+ "plan": _run_planner(design), # the actual writer/site recommendation (atlas-gated)
164
+ "verdict": {"legal": v.legal, "confidence": v.confidence, "interval": v.interval,
165
+ "epistemic_status": v.epistemic_status,
166
+ "violations": [x.get("rule_id") for x in v.violations]},
167
+ "safety": {"decision": (v.safety.decision if v.safety else None),
168
+ "reason": (v.safety.reason if v.safety else None)},
169
+ "immune_profile": {"axes": axes, "collapsed_score": imm.get("collapsed_score"),
170
+ "known_unknowns": imm.get("known_unknowns")},
171
+ "scope": ({"out_of_scope": True, "id": oos["id"], "title": oos["title"], "why": oos.get("deferral")}
172
+ if oos else {"out_of_scope": False}),
173
+ "disclaimer": "Decision-support only; not a clinical directive. Every number is tool-sourced.",
174
+ }
175
+
176
+
177
+ _NUM_RE = re.compile(r"-?\d+(?:\.\d+)?")
178
+
179
+
180
+ def extract_grounded_numbers(tool_results: dict) -> set[str]:
181
+ """The allow-list: every numeric string that appears in the engine's tool results. The grounding guard
182
+ permits the LLM to cite ONLY these; any other number it emits is stripped."""
183
+ import json
184
+ text = json.dumps(tool_results, default=str)
185
+ grounded = set(_NUM_RE.findall(text))
186
+ # also allow the common normalised forms (e.g. 0.5 / .5 / 50%) of each grounded value
187
+ extra = set()
188
+ for n in list(grounded):
189
+ try:
190
+ f = float(n)
191
+ except ValueError:
192
+ continue
193
+ extra.add(str(int(f)) if f.is_integer() else str(f))
194
+ extra.add(f"{f:.2f}")
195
+ if 0 <= f <= 1:
196
+ extra.add(str(round(f * 100))) # percent form of a [0,1] score
197
+ 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.3
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.3-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.3"
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