krons 0.1.1__tar.gz → 0.2.1__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 (275) hide show
  1. {krons-0.1.1 → krons-0.2.1}/.gitignore +2 -0
  2. {krons-0.1.1 → krons-0.2.1}/CLAUDE.md +120 -6
  3. {krons-0.1.1 → krons-0.2.1}/PKG-INFO +15 -1
  4. krons-0.2.1/cookbooks/007_fan_out_in.py +227 -0
  5. krons-0.2.1/cookbooks/test_conduct.py +72 -0
  6. {krons-0.1.1 → krons-0.2.1}/pyproject.toml +16 -1
  7. krons-0.2.1/src/krons/__init__.py +49 -0
  8. krons-0.2.1/src/krons/agent/__init__.py +144 -0
  9. krons-0.2.1/src/krons/agent/mcps/__init__.py +14 -0
  10. krons-0.2.1/src/krons/agent/mcps/loader.py +287 -0
  11. krons-0.2.1/src/krons/agent/mcps/wrapper.py +799 -0
  12. krons-0.2.1/src/krons/agent/message/__init__.py +20 -0
  13. krons-0.2.1/src/krons/agent/message/action.py +69 -0
  14. krons-0.2.1/src/krons/agent/message/assistant.py +52 -0
  15. krons-0.2.1/src/krons/agent/message/common.py +49 -0
  16. krons-0.2.1/src/krons/agent/message/instruction.py +130 -0
  17. krons-0.2.1/src/krons/agent/message/prepare_msg.py +187 -0
  18. krons-0.2.1/src/krons/agent/message/role.py +53 -0
  19. krons-0.2.1/src/krons/agent/message/system.py +53 -0
  20. krons-0.2.1/src/krons/agent/operations/__init__.py +82 -0
  21. krons-0.2.1/src/krons/agent/operations/act.py +100 -0
  22. krons-0.2.1/src/krons/agent/operations/generate.py +145 -0
  23. krons-0.2.1/src/krons/agent/operations/llm_reparse.py +89 -0
  24. krons-0.2.1/src/krons/agent/operations/operate.py +247 -0
  25. krons-0.2.1/src/krons/agent/operations/parse.py +243 -0
  26. krons-0.2.1/src/krons/agent/operations/react.py +286 -0
  27. krons-0.2.1/src/krons/agent/operations/specs.py +235 -0
  28. krons-0.2.1/src/krons/agent/operations/structure.py +151 -0
  29. krons-0.2.1/src/krons/agent/operations/utils.py +79 -0
  30. krons-0.2.1/src/krons/agent/providers/__init__.py +17 -0
  31. krons-0.2.1/src/krons/agent/providers/anthropic_messages.py +146 -0
  32. krons-0.2.1/src/krons/agent/providers/claude_code.py +276 -0
  33. krons-0.2.1/src/krons/agent/providers/gemini.py +268 -0
  34. krons-0.2.1/src/krons/agent/providers/match.py +75 -0
  35. krons-0.2.1/src/krons/agent/providers/oai_chat.py +174 -0
  36. krons-0.2.1/src/krons/agent/third_party/anthropic_models.py +154 -0
  37. krons-0.2.1/src/krons/agent/third_party/claude_code.py +682 -0
  38. krons-0.2.1/src/krons/agent/third_party/gemini_models.py +508 -0
  39. krons-0.2.1/src/krons/agent/third_party/openai_models.py +295 -0
  40. krons-0.2.1/src/krons/agent/tool.py +291 -0
  41. krons-0.2.1/src/krons/core/__init__.py +127 -0
  42. krons-0.2.1/src/krons/core/base/__init__.py +121 -0
  43. {krons-0.1.1/src/krons/core → krons-0.2.1/src/krons/core/base}/broadcaster.py +7 -3
  44. {krons-0.1.1/src/krons/core → krons-0.2.1/src/krons/core/base}/element.py +13 -5
  45. {krons-0.1.1/src/krons/core → krons-0.2.1/src/krons/core/base}/event.py +39 -6
  46. {krons-0.1.1/src/krons/core → krons-0.2.1/src/krons/core/base}/eventbus.py +3 -1
  47. {krons-0.1.1/src/krons/core → krons-0.2.1/src/krons/core/base}/flow.py +11 -4
  48. {krons-0.1.1/src/krons/core → krons-0.2.1/src/krons/core/base}/graph.py +24 -8
  49. {krons-0.1.1/src/krons/core → krons-0.2.1/src/krons/core/base}/node.py +44 -19
  50. {krons-0.1.1/src/krons/core → krons-0.2.1/src/krons/core/base}/pile.py +22 -8
  51. {krons-0.1.1/src/krons/core → krons-0.2.1/src/krons/core/base}/processor.py +21 -7
  52. {krons-0.1.1/src/krons/core → krons-0.2.1/src/krons/core/base}/progression.py +3 -1
  53. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/specs/__init__.py +0 -5
  54. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/specs/adapters/dataclass_field.py +16 -8
  55. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/specs/adapters/pydantic_adapter.py +11 -5
  56. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/specs/adapters/sql_ddl.py +14 -8
  57. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/specs/catalog/__init__.py +2 -2
  58. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/specs/catalog/_audit.py +2 -2
  59. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/specs/catalog/_common.py +2 -2
  60. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/specs/catalog/_content.py +4 -4
  61. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/specs/catalog/_enforcement.py +3 -3
  62. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/specs/factory.py +5 -5
  63. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/specs/operable.py +8 -2
  64. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/specs/protocol.py +4 -2
  65. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/specs/spec.py +23 -11
  66. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/types/base.py +4 -2
  67. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/types/db_types.py +2 -2
  68. {krons-0.1.1 → krons-0.2.1}/src/krons/errors.py +13 -13
  69. {krons-0.1.1 → krons-0.2.1}/src/krons/protocols.py +9 -4
  70. krons-0.2.1/src/krons/resource/__init__.py +89 -0
  71. {krons-0.1.1/src/krons/services → krons-0.2.1/src/krons/resource}/backend.py +48 -22
  72. {krons-0.1.1/src/krons/services → krons-0.2.1/src/krons/resource}/endpoint.py +28 -14
  73. {krons-0.1.1/src/krons/services → krons-0.2.1/src/krons/resource}/hook.py +20 -7
  74. {krons-0.1.1/src/krons/services → krons-0.2.1/src/krons/resource}/imodel.py +46 -28
  75. {krons-0.1.1/src/krons/services → krons-0.2.1/src/krons/resource}/registry.py +26 -24
  76. {krons-0.1.1/src/krons/services → krons-0.2.1/src/krons/resource}/utilities/rate_limited_executor.py +7 -3
  77. {krons-0.1.1/src/krons/services → krons-0.2.1/src/krons/resource}/utilities/rate_limiter.py +3 -1
  78. {krons-0.1.1/src/krons/services → krons-0.2.1/src/krons/resource}/utilities/resilience.py +15 -5
  79. krons-0.2.1/src/krons/resource/utilities/token_calculator.py +185 -0
  80. {krons-0.1.1 → krons-0.2.1}/src/krons/session/__init__.py +12 -17
  81. krons-0.2.1/src/krons/session/constraints.py +70 -0
  82. {krons-0.1.1 → krons-0.2.1}/src/krons/session/exchange.py +11 -3
  83. {krons-0.1.1 → krons-0.2.1}/src/krons/session/message.py +3 -1
  84. krons-0.2.1/src/krons/session/registry.py +35 -0
  85. {krons-0.1.1 → krons-0.2.1}/src/krons/session/session.py +165 -174
  86. krons-0.2.1/src/krons/utils/__init__.py +85 -0
  87. krons-0.2.1/src/krons/utils/_function_arg_parser.py +99 -0
  88. krons-0.2.1/src/krons/utils/_pythonic_function_call.py +249 -0
  89. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/_to_list.py +9 -3
  90. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/_utils.py +6 -2
  91. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/concurrency/_async_call.py +4 -2
  92. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/concurrency/_errors.py +3 -1
  93. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/concurrency/_patterns.py +3 -1
  94. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/concurrency/_resource_tracker.py +6 -2
  95. krons-0.2.1/src/krons/utils/display.py +257 -0
  96. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/fuzzy/__init__.py +6 -1
  97. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/fuzzy/_fuzzy_match.py +14 -8
  98. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/fuzzy/_string_similarity.py +3 -1
  99. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/fuzzy/_to_dict.py +3 -1
  100. krons-0.2.1/src/krons/utils/schemas/__init__.py +26 -0
  101. krons-0.2.1/src/krons/utils/schemas/_breakdown_pydantic_annotation.py +131 -0
  102. krons-0.2.1/src/krons/utils/schemas/_formatter.py +72 -0
  103. krons-0.2.1/src/krons/utils/schemas/_minimal_yaml.py +151 -0
  104. krons-0.2.1/src/krons/utils/schemas/_typescript.py +153 -0
  105. krons-0.2.1/src/krons/utils/validators/__init__.py +3 -0
  106. krons-0.2.1/src/krons/utils/validators/_validate_image_url.py +56 -0
  107. krons-0.2.1/src/krons/work/__init__.py +115 -0
  108. krons-0.2.1/src/krons/work/engine.py +333 -0
  109. krons-0.2.1/src/krons/work/form.py +242 -0
  110. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/work}/operations/__init__.py +7 -4
  111. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/work}/operations/builder.py +1 -1
  112. {krons-0.1.1/src/krons/enforcement → krons-0.2.1/src/krons/work/operations}/context.py +36 -5
  113. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/work}/operations/flow.py +13 -5
  114. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/work}/operations/node.py +45 -43
  115. krons-0.2.1/src/krons/work/operations/registry.py +103 -0
  116. krons-0.2.1/src/krons/work/report.py +268 -0
  117. krons-0.2.1/src/krons/work/rules/__init__.py +47 -0
  118. {krons-0.1.1/src/krons/enforcement → krons-0.2.1/src/krons/work/rules}/common/boolean.py +3 -1
  119. {krons-0.1.1/src/krons/enforcement → krons-0.2.1/src/krons/work/rules}/common/choice.py +9 -3
  120. {krons-0.1.1/src/krons/enforcement → krons-0.2.1/src/krons/work/rules}/common/number.py +3 -1
  121. {krons-0.1.1/src/krons/enforcement → krons-0.2.1/src/krons/work/rules}/common/string.py +9 -3
  122. {krons-0.1.1/src/krons/enforcement → krons-0.2.1/src/krons/work/rules}/rule.py +1 -1
  123. {krons-0.1.1/src/krons/enforcement → krons-0.2.1/src/krons/work/rules}/validator.py +20 -5
  124. krons-0.2.1/src/krons/work/worker.py +266 -0
  125. krons-0.2.1/tests/agent/test_mcps_unit.py +216 -0
  126. krons-0.2.1/tests/agent/test_message.py +220 -0
  127. krons-0.2.1/tests/agent/test_providers_e2e.py +339 -0
  128. krons-0.2.1/tests/agent/test_providers_unit.py +276 -0
  129. krons-0.2.1/tests/agent/test_tool_unit.py +293 -0
  130. {krons-0.1.1 → krons-0.2.1}/tests/conftest.py +7 -3
  131. {krons-0.1.1 → krons-0.2.1}/tests/core/test_broadcaster.py +1 -2
  132. {krons-0.1.1 → krons-0.2.1}/tests/core/test_error_paths.py +13 -13
  133. {krons-0.1.1 → krons-0.2.1}/tests/core/test_event.py +2 -3
  134. {krons-0.1.1 → krons-0.2.1}/tests/core/test_event_status_race.py +2 -2
  135. {krons-0.1.1 → krons-0.2.1}/tests/core/test_flow_edge_cases.py +9 -3
  136. {krons-0.1.1 → krons-0.2.1}/tests/core/test_node.py +12 -11
  137. {krons-0.1.1 → krons-0.2.1}/tests/core/test_processor.py +1 -2
  138. {krons-0.1.1 → krons-0.2.1}/tests/core/test_processor_security.py +49 -19
  139. {krons-0.1.1 → krons-0.2.1}/tests/core/test_serialization_roundtrip.py +59 -18
  140. {krons-0.1.1 → krons-0.2.1}/tests/core/test_thread_safety_stress.py +4 -2
  141. {krons-0.1.1 → krons-0.2.1}/tests/operations/test_builder.py +23 -7
  142. {krons-0.1.1 → krons-0.2.1}/tests/operations/test_flow.py +22 -17
  143. {krons-0.1.1 → krons-0.2.1}/tests/operations/test_node.py +7 -45
  144. {krons-0.1.1 → krons-0.2.1}/tests/operations/test_op_flow.py +103 -79
  145. {krons-0.1.1 → krons-0.2.1}/tests/operations/test_op_node.py +6 -12
  146. {krons-0.1.1 → krons-0.2.1}/tests/operations/test_registry.py +28 -37
  147. {krons-0.1.1/tests/services → krons-0.2.1/tests/resource}/test_backend.py +74 -59
  148. {krons-0.1.1/tests/services → krons-0.2.1/tests/resource}/test_endpoint.py +8 -4
  149. {krons-0.1.1/tests/services → krons-0.2.1/tests/resource}/test_hook.py +38 -18
  150. {krons-0.1.1/tests/services → krons-0.2.1/tests/resource}/test_imodel.py +11 -9
  151. {krons-0.1.1/tests/services → krons-0.2.1/tests/resource}/test_registry.py +21 -21
  152. {krons-0.1.1/tests/services → krons-0.2.1/tests/resource}/utilities/test_header_factory.py +4 -2
  153. {krons-0.1.1/tests/services → krons-0.2.1/tests/resource}/utilities/test_rate_limiter.py +4 -4
  154. {krons-0.1.1/tests/services → krons-0.2.1/tests/resource}/utilities/test_resilience.py +22 -8
  155. {krons-0.1.1/tests/services → krons-0.2.1/tests/resource}/utilities/test_resilience_retry.py +23 -13
  156. {krons-0.1.1 → krons-0.2.1}/tests/rules/test_base.py +5 -2
  157. {krons-0.1.1 → krons-0.2.1}/tests/rules/test_builtin.py +1 -1
  158. {krons-0.1.1 → krons-0.2.1}/tests/rules/test_registry.py +5 -3
  159. {krons-0.1.1 → krons-0.2.1}/tests/rules/test_rule_params_edge_cases.py +1 -1
  160. {krons-0.1.1 → krons-0.2.1}/tests/rules/test_rules_comprehensive.py +18 -8
  161. {krons-0.1.1 → krons-0.2.1}/tests/rules/test_validator.py +12 -6
  162. {krons-0.1.1 → krons-0.2.1}/tests/session/test_exchange.py +3 -1
  163. {krons-0.1.1 → krons-0.2.1}/tests/session/test_message.py +21 -7
  164. {krons-0.1.1 → krons-0.2.1}/tests/session/test_message_edge_cases.py +6 -2
  165. {krons-0.1.1 → krons-0.2.1}/tests/session/test_session.py +34 -27
  166. {krons-0.1.1 → krons-0.2.1}/tests/session/test_session_edge_cases.py +17 -20
  167. {krons-0.1.1 → krons-0.2.1}/tests/specs/test_catalog.py +2 -2
  168. {krons-0.1.1 → krons-0.2.1}/tests/specs/test_factory.py +8 -8
  169. {krons-0.1.1 → krons-0.2.1}/tests/test_protocols.py +4 -1
  170. {krons-0.1.1 → krons-0.2.1}/tests/types/conftest.py +5 -3
  171. {krons-0.1.1 → krons-0.2.1}/tests/types/spec_adapters/test_adapters_py311.py +12 -6
  172. {krons-0.1.1 → krons-0.2.1}/tests/types/spec_adapters/test_protocol.py +1 -1
  173. {krons-0.1.1 → krons-0.2.1}/tests/types/spec_adapters/test_pydantic_field.py +14 -6
  174. {krons-0.1.1 → krons-0.2.1}/tests/types/spec_adapters/test_sql_ddl_specs.py +13 -7
  175. {krons-0.1.1 → krons-0.2.1}/tests/types/test_db_types.py +1 -1
  176. {krons-0.1.1 → krons-0.2.1}/tests/types/test_identity.py +1 -1
  177. {krons-0.1.1 → krons-0.2.1}/tests/types/test_model.py +1 -1
  178. {krons-0.1.1 → krons-0.2.1}/tests/types/test_operable.py +9 -5
  179. {krons-0.1.1 → krons-0.2.1}/tests/types/test_sentinel.py +1 -1
  180. {krons-0.1.1 → krons-0.2.1}/tests/types/test_spec.py +15 -13
  181. {krons-0.1.1 → krons-0.2.1}/tests/types/test_types.py +7 -3
  182. {krons-0.1.1 → krons-0.2.1}/tests/types/test_types_py311.py +9 -2
  183. {krons-0.1.1 → krons-0.2.1}/tests/utils/concurrency/test_async_call.py +3 -1
  184. {krons-0.1.1 → krons-0.2.1}/tests/utils/concurrency/test_cancel.py +3 -1
  185. {krons-0.1.1 → krons-0.2.1}/tests/utils/concurrency/test_patterns.py +16 -5
  186. {krons-0.1.1 → krons-0.2.1}/tests/utils/concurrency/test_run_async.py +3 -1
  187. {krons-0.1.1 → krons-0.2.1}/tests/utils/test_fuzzy_json.py +3 -1
  188. {krons-0.1.1 → krons-0.2.1}/tests/utils/test_fuzzy_match.py +9 -3
  189. {krons-0.1.1 → krons-0.2.1}/tests/utils/test_hash.py +3 -1
  190. {krons-0.1.1 → krons-0.2.1}/tests/utils/test_json_dump.py +3 -1
  191. {krons-0.1.1 → krons-0.2.1}/tests/utils/test_string_similarity.py +12 -4
  192. {krons-0.1.1 → krons-0.2.1}/tests/utils/test_to_dict.py +3 -1
  193. krons-0.2.1/tests/work/__init__.py +4 -0
  194. krons-0.2.1/tests/work/test_engine.py +817 -0
  195. krons-0.2.1/tests/work/test_form.py +473 -0
  196. krons-0.2.1/tests/work/test_report.py +550 -0
  197. krons-0.2.1/tests/work/test_worker.py +665 -0
  198. krons-0.2.1/uv.lock +2628 -0
  199. krons-0.1.1/src/krons/core/__init__.py +0 -145
  200. krons-0.1.1/src/krons/enforcement/__init__.py +0 -57
  201. krons-0.1.1/src/krons/enforcement/policy.py +0 -80
  202. krons-0.1.1/src/krons/enforcement/service.py +0 -370
  203. krons-0.1.1/src/krons/operations/registry.py +0 -92
  204. krons-0.1.1/src/krons/services/__init__.py +0 -81
  205. krons-0.1.1/src/krons/specs/phrase.py +0 -405
  206. krons-0.1.1/src/krons/utils/__init__.py +0 -40
  207. krons-0.1.1/tests/enforcement/test_service.py +0 -595
  208. krons-0.1.1/tests/specs/test_phrase.py +0 -633
  209. krons-0.1.1/uv.lock +0 -641
  210. {krons-0.1.1 → krons-0.2.1}/.github/workflows/release.yml +0 -0
  211. {krons-0.1.1 → krons-0.2.1}/.python-version +0 -0
  212. {krons-0.1.1 → krons-0.2.1}/LICENSE +0 -0
  213. {krons-0.1.1 → krons-0.2.1}/README.md +0 -0
  214. {krons-0.1.1/tests → krons-0.2.1/src/krons/agent/third_party}/__init__.py +0 -0
  215. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core/specs/adapters}/__init__.py +0 -0
  216. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/specs/adapters/_utils.py +0 -0
  217. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/specs/adapters/factory.py +0 -0
  218. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/types/__init__.py +0 -0
  219. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/types/_sentinel.py +0 -0
  220. {krons-0.1.1/src/krons → krons-0.2.1/src/krons/core}/types/identity.py +0 -0
  221. {krons-0.1.1 → krons-0.2.1}/src/krons/py.typed +0 -0
  222. {krons-0.1.1/src/krons/services → krons-0.2.1/src/krons/resource}/utilities/__init__.py +0 -0
  223. {krons-0.1.1/src/krons/services → krons-0.2.1/src/krons/resource}/utilities/header_factory.py +0 -0
  224. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/_hash.py +0 -0
  225. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/_json_dump.py +0 -0
  226. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/_lazy_init.py +0 -0
  227. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/_to_num.py +0 -0
  228. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/concurrency/__init__.py +0 -0
  229. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/concurrency/_cancel.py +0 -0
  230. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/concurrency/_primitives.py +0 -0
  231. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/concurrency/_priority_queue.py +0 -0
  232. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/concurrency/_run_async.py +0 -0
  233. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/concurrency/_task.py +0 -0
  234. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/concurrency/_utils.py +0 -0
  235. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/fuzzy/_extract_json.py +0 -0
  236. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/fuzzy/_fuzzy_json.py +0 -0
  237. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/sql/__init__.py +0 -0
  238. {krons-0.1.1 → krons-0.2.1}/src/krons/utils/sql/_sql_validation.py +0 -0
  239. {krons-0.1.1/src/krons/enforcement → krons-0.2.1/src/krons/work/rules}/common/__init__.py +0 -0
  240. {krons-0.1.1/src/krons/enforcement → krons-0.2.1/src/krons/work/rules}/common/mapping.py +0 -0
  241. {krons-0.1.1/src/krons/enforcement → krons-0.2.1/src/krons/work/rules}/common/model.py +0 -0
  242. {krons-0.1.1/src/krons/enforcement → krons-0.2.1/src/krons/work/rules}/registry.py +0 -0
  243. {krons-0.1.1/tests/core → krons-0.2.1/tests}/__init__.py +0 -0
  244. {krons-0.1.1/src/krons/specs/adapters → krons-0.2.1/tests/agent}/__init__.py +0 -0
  245. {krons-0.1.1/tests/enforcement → krons-0.2.1/tests/core}/__init__.py +0 -0
  246. {krons-0.1.1 → krons-0.2.1}/tests/core/test_element.py +0 -0
  247. {krons-0.1.1 → krons-0.2.1}/tests/core/test_eventbus.py +0 -0
  248. {krons-0.1.1 → krons-0.2.1}/tests/core/test_flow.py +0 -0
  249. {krons-0.1.1 → krons-0.2.1}/tests/core/test_graph.py +0 -0
  250. {krons-0.1.1 → krons-0.2.1}/tests/core/test_graph_event_loop.py +0 -0
  251. {krons-0.1.1 → krons-0.2.1}/tests/core/test_pile.py +0 -0
  252. {krons-0.1.1 → krons-0.2.1}/tests/core/test_pile_edge_cases.py +0 -0
  253. {krons-0.1.1 → krons-0.2.1}/tests/core/test_progression.py +0 -0
  254. {krons-0.1.1 → krons-0.2.1}/tests/core/test_progression_edge_cases.py +0 -0
  255. {krons-0.1.1 → krons-0.2.1}/tests/operations/__init__.py +0 -0
  256. {krons-0.1.1/tests/rules → krons-0.2.1/tests/resource}/__init__.py +0 -0
  257. {krons-0.1.1/tests/services → krons-0.2.1/tests/resource/utilities}/__init__.py +0 -0
  258. {krons-0.1.1/tests/services/utilities → krons-0.2.1/tests/rules}/__init__.py +0 -0
  259. {krons-0.1.1 → krons-0.2.1}/tests/session/__init__.py +0 -0
  260. {krons-0.1.1 → krons-0.2.1}/tests/specs/__init__.py +0 -0
  261. {krons-0.1.1 → krons-0.2.1}/tests/types/__init__.py +0 -0
  262. {krons-0.1.1 → krons-0.2.1}/tests/types/spec_adapters/__init__.py +0 -0
  263. {krons-0.1.1 → krons-0.2.1}/tests/utils/__init__.py +0 -0
  264. {krons-0.1.1 → krons-0.2.1}/tests/utils/concurrency/__init__.py +0 -0
  265. {krons-0.1.1 → krons-0.2.1}/tests/utils/concurrency/test_errors.py +0 -0
  266. {krons-0.1.1 → krons-0.2.1}/tests/utils/concurrency/test_primitives.py +0 -0
  267. {krons-0.1.1 → krons-0.2.1}/tests/utils/concurrency/test_priority_queue.py +0 -0
  268. {krons-0.1.1 → krons-0.2.1}/tests/utils/concurrency/test_task.py +0 -0
  269. {krons-0.1.1 → krons-0.2.1}/tests/utils/concurrency/test_utils.py +0 -0
  270. {krons-0.1.1 → krons-0.2.1}/tests/utils/test_extract_json.py +0 -0
  271. {krons-0.1.1 → krons-0.2.1}/tests/utils/test_lazy_init.py +0 -0
  272. {krons-0.1.1 → krons-0.2.1}/tests/utils/test_sql_validation.py +0 -0
  273. {krons-0.1.1 → krons-0.2.1}/tests/utils/test_to_list.py +0 -0
  274. {krons-0.1.1 → krons-0.2.1}/tests/utils/test_to_num.py +0 -0
  275. {krons-0.1.1 → krons-0.2.1}/tests/utils/test_utils.py +0 -0
@@ -205,3 +205,5 @@ cython_debug/
205
205
  marimo/_static/
206
206
  marimo/_lsp/
207
207
  __marimo__/
208
+
209
+ *.DS_Store
@@ -7,7 +7,8 @@
7
7
  - **Spec/Operable**: Type-safe field definitions with validation, defaults, and DB metadata
8
8
  - **Node**: Polymorphic content containers with DB serialization
9
9
  - **Services**: Unified service interfaces (iModel, KronService) with hooks and rate limiting
10
- - **Enforcement**: Policy evaluation and action handlers with typed I/O
10
+ - **Work**: Declarative workflow orchestration with Report/Worker pattern
11
+ - **Rules**: Validation rules and enforcement
11
12
 
12
13
  ## Architecture
13
14
 
@@ -17,10 +18,11 @@ krons/
17
18
  ├── specs/ # Spec definitions, Operable composition, adapters
18
19
  │ ├── catalog/ # Pre-built specs (Content, Audit, Common, Enforcement)
19
20
  │ └── adapters/ # Pydantic, SQL DDL, Dataclass adapters
21
+ ├── work/ # Workflow orchestration (Report, Worker, Form, Phrase)
20
22
  ├── services/ # Service backends, iModel, hooks, rate limiting
21
- ├── enforcement/ # Policy protocols, KronService, action decorators
23
+ ├── operations/ # Operation builders, context, registry
24
+ ├── rules/ # Validation rules, registry, common validators
22
25
  ├── types/ # Base types, sentinels, DB types (FK, Vector)
23
- ├── operations/ # Operation builders and registry
24
26
  ├── protocols.py # Runtime-checkable protocols with @implements
25
27
  └── utils/ # Fuzzy matching, SQL utilities, helpers
26
28
  ```
@@ -192,6 +194,109 @@ fk_meta = extract_kron_db_meta(field_info, metas="FK")
192
194
  vec_meta = extract_kron_db_meta(field_info, metas="Vector")
193
195
  ```
194
196
 
197
+ ### 7. Work (Workflow Orchestration)
198
+
199
+ Two complementary patterns at different abstraction levels:
200
+
201
+ **Report** (artifact state) - Declarative workflow definition:
202
+
203
+ ```python
204
+ from krons.work import Report
205
+
206
+ class HiringBriefReport(Report):
207
+ """Multi-step workflow with typed outputs as class attributes."""
208
+
209
+ # Typed output fields
210
+ role_classification: RoleClassification | None = None
211
+ strategic_context: StrategicContext | None = None
212
+ executive_summary: ExecutiveSummary | None = None
213
+
214
+ # Overall contract
215
+ assignment: str = "job_input, market_context -> executive_summary"
216
+
217
+ # Form assignments with branch/resource hints
218
+ # DSL: "branch: inputs -> outputs | resource"
219
+ form_assignments: list[str] = [
220
+ # Same branch = sequential execution
221
+ "classifier: job_input -> role_classification | api:fast",
222
+ "classifier: role_classification -> extracted_skills | api:fast",
223
+
224
+ # Different branches = parallel execution
225
+ "strategist: job_input, role_classification -> strategic_context | api:synthesis",
226
+ "writer: strategic_context -> executive_summary | api:reasoning",
227
+ ]
228
+
229
+ # Execute workflow
230
+ report = HiringBriefReport()
231
+ report.initialize(job_input="...", market_context="...")
232
+
233
+ while not report.is_complete():
234
+ for form in report.next_forms(): # Data-driven scheduling
235
+ result = await execute_form(form) # Route to Worker via form.resource
236
+ form.set_output(result)
237
+ report.complete_form(form)
238
+
239
+ output = report.get_deliverable()
240
+ ```
241
+
242
+ **Worker** (execution capability) - Functional station with @work methods:
243
+
244
+ ```python
245
+ from krons.work import Worker, WorkerEngine, work, worklink
246
+
247
+ class ClassifierWorker(Worker):
248
+ """Execution capability with internal DAG for retries."""
249
+
250
+ name = "classifier"
251
+
252
+ @work(assignment="job_input -> role_classification", capacity=2)
253
+ async def classify_role(self, job_input, **kwargs):
254
+ result = await self.llm.chat(**kwargs)
255
+ return result.role_classification
256
+
257
+ @work(assignment="code -> execution_result")
258
+ async def execute_code(self, code):
259
+ error = run_code(code)
260
+ return code, error
261
+
262
+ # Conditional edge for retry loops
263
+ @worklink(from_="execute_code", to_="debug_code")
264
+ async def maybe_debug(self, result):
265
+ code, error = result
266
+ if error: # Only follow if error
267
+ return {"code": code, "error": error}
268
+ return None # Skip edge
269
+
270
+ # Execute with engine
271
+ engine = WorkerEngine(worker=ClassifierWorker())
272
+ task = await engine.add_task(task_function="classify_role", job_input="...")
273
+ await engine.execute()
274
+ ```
275
+
276
+ **Key concepts:**
277
+
278
+ | Component | Role | State |
279
+ |-----------|------|-------|
280
+ | **Report** | Work order / artifact | Stateful (tracks one job) |
281
+ | **Worker** | Station / capability | Stateless (handles many jobs) |
282
+ | **Form** | Unit of work | Stateful (one assignment) |
283
+ | **Phrase** | Typed I/O signature | Definition only |
284
+
285
+ **Form assignment DSL:**
286
+
287
+ ```
288
+ "branch: inputs -> outputs | resource"
289
+
290
+ Examples:
291
+ "a, b -> c" # Simple (no branch, no resource)
292
+ "classifier: job -> role | api:fast" # Full (branch + resource)
293
+ "writer: context -> summary" # Branch only
294
+ ```
295
+
296
+ - **branch**: Groups forms for sequential execution (same branch = sequential)
297
+ - **resource**: Hint for routing to Worker capabilities (e.g., `api:fast`, `api:reasoning`)
298
+ - Forms without branch execute in parallel based on data availability
299
+
195
300
  ## Testing Patterns
196
301
 
197
302
  ### Test Structure
@@ -200,8 +305,10 @@ vec_meta = extract_kron_db_meta(field_info, metas="Vector")
200
305
  tests/
201
306
  ├── core/ # Node, Element, Event tests
202
307
  ├── specs/ # Spec, Operable, Catalog tests
308
+ ├── work/ # Report, Worker, Form, Phrase, Engine tests
203
309
  ├── services/ # iModel, hook tests
204
- ├── enforcement/ # KronService, policy tests
310
+ ├── operations/ # Operation, context tests
311
+ ├── rules/ # Validation rule tests
205
312
  └── utils/ # Utility function tests
206
313
  ```
207
314
 
@@ -251,11 +358,17 @@ class MockPolicyEngine:
251
358
  5. **compose_structure frozen param**: Currently broken in PydanticSpecAdapter (doesn't accept
252
359
  `frozen` kwarg).
253
360
 
361
+ 6. **Report vs Worker**: They operate at different levels:
362
+ - Report = declarative workflow (WHAT to do) - subclass and define `form_assignments`
363
+ - Worker = execution capability (HOW to do it) - has `@work` methods
364
+ - Forms without explicit branch run in parallel; same branch = sequential
365
+
366
+ 7. **Form assignment DSL**: The full format is `"branch: inputs -> outputs | resource"`.
367
+ All parts except `inputs -> outputs` are optional.
368
+
254
369
  ## Running Tests
255
370
 
256
371
  ```bash
257
- cd libs/krons
258
-
259
372
  # All tests
260
373
  uv run pytest tests/ -q
261
374
 
@@ -263,6 +376,7 @@ uv run pytest tests/ -q
263
376
  uv run pytest tests/ --cov=krons --cov-report=term-missing
264
377
 
265
378
  # Specific module
379
+ uv run pytest tests/work/test_report.py -v
266
380
  uv run pytest tests/specs/test_catalog.py -v
267
381
 
268
382
  # Single test
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: krons
3
- Version: 0.1.1
3
+ Version: 0.2.1
4
4
  Summary: Spec-based composable framework for building type-safe systems
5
5
  Project-URL: Homepage, https://github.com/khive-ai/krons
6
6
  Project-URL: Repository, https://github.com/khive-ai/krons
@@ -22,7 +22,21 @@ Requires-Dist: anyio>=4.10.0
22
22
  Requires-Dist: httpx>=0.26.0
23
23
  Requires-Dist: orjson>=3.10.0
24
24
  Requires-Dist: pydantic>=2.10.0
25
+ Requires-Dist: pyyaml>=6.0.3
25
26
  Requires-Dist: rapidfuzz>=3.10.0
27
+ Provides-Extra: agent
28
+ Requires-Dist: fastmcp>=2.14.4; extra == 'agent'
29
+ Requires-Dist: tiktoken>=0.11.0; extra == 'agent'
30
+ Provides-Extra: all
31
+ Requires-Dist: fastmcp>=2.14.4; extra == 'all'
32
+ Requires-Dist: rich>=13.0; extra == 'all'
33
+ Requires-Dist: tiktoken>=0.11.0; extra == 'all'
34
+ Provides-Extra: display
35
+ Requires-Dist: rich>=13.0; extra == 'display'
36
+ Provides-Extra: fastmcp
37
+ Requires-Dist: fastmcp>=2.14.4; extra == 'fastmcp'
38
+ Provides-Extra: tiktoken
39
+ Requires-Dist: tiktoken>=0.11.0; extra == 'tiktoken'
26
40
  Description-Content-Type: text/markdown
27
41
 
28
42
  # krons
@@ -0,0 +1,227 @@
1
+ # Copyright (c) 2025 - 2026, HaiyangLi <quantocean.li at gmail dot com>
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """Fan-out / fan-in pattern with Claude Code.
5
+
6
+ Pattern:
7
+ 1. Register resources and operations with session
8
+ 2. session.conduct() -> orchestrator produces structured Instruct tasks
9
+ 3. Tasks fan out to parallel Claude Code instances via session.conduct()
10
+ 4. Results fan in and are synthesized by orchestrator via session.conduct()
11
+
12
+ Usage:
13
+ uv run python cookbooks/007_fan_out_in.py
14
+ uv run python cookbooks/007_fan_out_in.py --simple # Quick smoke test
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import sys
20
+
21
+ import anyio
22
+ from pydantic import BaseModel, Field
23
+
24
+ from krons.agent.operations import GenerateParams, Instruct, ReturnAs
25
+ from krons.agent.providers.claude_code import (
26
+ ClaudeCodeEndpoint,
27
+ create_claude_code_config,
28
+ )
29
+ from krons.resource import iModel
30
+ from krons.session import Session, SessionConfig
31
+ from krons.utils.display import Timer, as_readable, display, phase, status
32
+ from krons.utils.fuzzy import extract_json, fuzzy_validate_mapping
33
+
34
+ CC_WORKSPACE = ".khive/workspace"
35
+ VERBOSE = True
36
+
37
+
38
+ # ---------------------------------------------------------------------------
39
+ # Claude Code factory
40
+ # ---------------------------------------------------------------------------
41
+
42
+
43
+ def create_cc(name: str, subdir: str, **kwargs) -> iModel:
44
+ """Create a Claude Code iModel for a workspace subdirectory."""
45
+ config = create_claude_code_config(name=name)
46
+ config.update({"ws": f"{CC_WORKSPACE}/{subdir}", **kwargs})
47
+ endpoint = ClaudeCodeEndpoint(config=config)
48
+ return iModel(backend=endpoint)
49
+
50
+
51
+ # ---------------------------------------------------------------------------
52
+ # Structured output models
53
+ # ---------------------------------------------------------------------------
54
+
55
+
56
+ class InvestigationPlan(BaseModel):
57
+ """Orchestrator's structured plan: analysis + parallel Instruct tasks."""
58
+
59
+ analysis: str = Field(description="Initial analysis of the codebase")
60
+ research_tasks: list[Instruct] = Field(
61
+ description="Three parallel research instructions for deeper investigation",
62
+ )
63
+
64
+
65
+ # ---------------------------------------------------------------------------
66
+ # Prompts
67
+ # ---------------------------------------------------------------------------
68
+
69
+ PLAN_PROMPT = (
70
+ "Investigate the codebase in the specified directory. "
71
+ "Glance over the key components, pay attention to architecture, "
72
+ "design patterns, and notable features. "
73
+ "Produce three parallel research instructions for deeper investigation."
74
+ )
75
+
76
+ PLAN_PROMPT_SIMPLE = (
77
+ "List the top 3 things to investigate in a Python project. "
78
+ "For each, provide a short research instruction."
79
+ )
80
+
81
+ SYNTHESIS_PROMPT = """\
82
+ Synthesize the information from the researcher branches into a cohesive overview:
83
+ 1. Key components and their roles
84
+ 2. Architectural patterns used
85
+ 3. Design patterns and notable features
86
+ """
87
+
88
+ SYNTHESIS_PROMPT_SIMPLE = """\
89
+ Combine the researcher findings into a brief 2-3 sentence summary.
90
+ """
91
+
92
+
93
+ # ---------------------------------------------------------------------------
94
+ # Helpers
95
+ # ---------------------------------------------------------------------------
96
+
97
+
98
+ def parse_plan(text: str) -> InvestigationPlan:
99
+ """Extract InvestigationPlan from LLM text output."""
100
+ target_keys = list(InvestigationPlan.model_fields.keys())
101
+ extracted = extract_json(text, fuzzy_parse=True)
102
+ if not extracted:
103
+ raise ValueError("Failed to extract JSON from orchestrator response")
104
+
105
+ block = extracted[0] if isinstance(extracted, list) else extracted
106
+ validated = fuzzy_validate_mapping(block, target_keys)
107
+ return InvestigationPlan.model_validate(validated)
108
+
109
+
110
+ # ---------------------------------------------------------------------------
111
+ # Main
112
+ # ---------------------------------------------------------------------------
113
+
114
+
115
+ async def main(simple: bool = False):
116
+ plan_prompt = PLAN_PROMPT_SIMPLE if simple else PLAN_PROMPT
117
+ synth_prompt = SYNTHESIS_PROMPT_SIMPLE if simple else SYNTHESIS_PROMPT
118
+ context = ["lionagi"] if not simple else None
119
+
120
+ # --- 1. Create resources ---
121
+ orc_model = create_cc("orchestrator", "orchestrator")
122
+
123
+ # --- 2. Create session with resources and operations ---
124
+ session = Session(
125
+ config=SessionConfig(
126
+ default_branch_name="orchestrator",
127
+ default_gen_model="orchestrator",
128
+ shared_resources={"orchestrator"},
129
+ )
130
+ )
131
+ session.resources.register(orc_model)
132
+
133
+ orc_branch = session.default_branch
134
+ status(
135
+ f"Session ready: {len(session.resources)} resources, branch={orc_branch.name}"
136
+ )
137
+
138
+ # --- 3. Phase 1: Plan (structured output from orchestrator) ---
139
+ phase("Phase 1: Planning")
140
+
141
+ plan_op = await session.conduct(
142
+ "generate",
143
+ orc_branch,
144
+ GenerateParams(
145
+ primary=plan_prompt,
146
+ context=context,
147
+ request_model=InvestigationPlan,
148
+ imodel="orchestrator",
149
+ return_as=ReturnAs.TEXT,
150
+ ),
151
+ verbose=VERBOSE,
152
+ )
153
+ plan = parse_plan(plan_op.execution.response)
154
+
155
+ status(f"Analysis: {plan.analysis[:120]}...")
156
+ status(f"Research tasks: {len(plan.research_tasks)}")
157
+ for i, task in enumerate(plan.research_tasks):
158
+ status(f" [{i + 1}] {(task.instruction or '')[:80]}...")
159
+
160
+ if VERBOSE:
161
+ display(
162
+ as_readable(plan, format_curly=True),
163
+ title="Investigation Plan",
164
+ )
165
+
166
+ # --- 4. Phase 2: Fan-out (parallel research) ---
167
+ phase("Phase 2: Parallel Research")
168
+ results: list[str | None] = [None] * len(plan.research_tasks)
169
+
170
+ async def run_research(idx: int, task: Instruct) -> None:
171
+ name = f"researcher_{idx}"
172
+ researcher = create_cc(name, name)
173
+ session.resources.register(researcher, update=True)
174
+ branch = session.create_branch(name=name, resources={name})
175
+
176
+ instruction = task.instruction or ""
177
+ if task.guidance:
178
+ instruction += f"\n\nGuidance: {task.guidance}"
179
+
180
+ op = await session.conduct(
181
+ "generate",
182
+ branch,
183
+ GenerateParams(
184
+ primary=instruction,
185
+ context=task.context,
186
+ imodel=name,
187
+ return_as=ReturnAs.TEXT,
188
+ ),
189
+ verbose=VERBOSE,
190
+ )
191
+ results[idx] = op.execution.response
192
+ status(
193
+ f"Researcher {idx + 1} done ({len(results[idx] or '')} chars)",
194
+ style="success",
195
+ )
196
+
197
+ async with anyio.create_task_group() as tg:
198
+ for i, task in enumerate(plan.research_tasks):
199
+ tg.start_soon(run_research, i, task)
200
+
201
+ # --- 5. Phase 3: Fan-in (synthesis) ---
202
+ phase("Phase 3: Synthesis")
203
+ research_context = [
204
+ f"--- Researcher {i + 1} ---\n{r}"
205
+ for i, r in enumerate(results)
206
+ if r is not None
207
+ ]
208
+
209
+ synth_op = await session.conduct(
210
+ "generate",
211
+ orc_branch,
212
+ GenerateParams(
213
+ primary=synth_prompt,
214
+ context=research_context,
215
+ imodel="orchestrator",
216
+ return_as=ReturnAs.TEXT,
217
+ ),
218
+ verbose=VERBOSE,
219
+ )
220
+
221
+ phase("Final Synthesis")
222
+ print(synth_op.execution.response)
223
+
224
+
225
+ if __name__ == "__main__":
226
+ simple = "--simple" in sys.argv
227
+ anyio.run(main, simple)
@@ -0,0 +1,72 @@
1
+ # Copyright (c) 2025 - 2026, HaiyangLi <quantocean.li at gmail dot com>
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ """Minimal test: one structured output via session.conduct("structure").
5
+
6
+ Usage:
7
+ uv run python cookbooks/test_conduct.py
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import anyio
13
+ from pydantic import BaseModel, Field
14
+
15
+ from krons.agent.operations import GenerateParams, Instruct, StructureParams
16
+ from krons.agent.providers.claude_code import (
17
+ ClaudeCodeEndpoint,
18
+ create_claude_code_config,
19
+ )
20
+ from krons.core.specs import Operable
21
+ from krons.resource import iModel
22
+ from krons.session import Session, SessionConfig
23
+ from krons.utils.display import as_readable, display, phase, status
24
+ from krons.work.rules.validator import Validator
25
+
26
+
27
+ class Plan(BaseModel):
28
+ analysis: str = Field(description="One sentence analysis")
29
+ tasks: list[Instruct] = Field(description="Two short research tasks")
30
+
31
+
32
+ async def main():
33
+ # Setup
34
+ config = create_claude_code_config(name="test")
35
+ endpoint = ClaudeCodeEndpoint(config=config)
36
+ model = iModel(backend=endpoint)
37
+
38
+ session = Session(
39
+ config=SessionConfig(
40
+ default_branch_name="main",
41
+ default_gen_model="test",
42
+ shared_resources={"test"},
43
+ )
44
+ )
45
+ session.resources.register(model)
46
+
47
+ phase("Structured Output Test (structure operation)")
48
+
49
+ operable = Operable.from_structure(Plan)
50
+
51
+ op = await session.conduct(
52
+ "structure",
53
+ params=StructureParams(
54
+ generate_params=GenerateParams(
55
+ primary="Name two colors. For each, give a one-sentence research task.",
56
+ request_model=Plan,
57
+ imodel="test",
58
+ ),
59
+ validator=Validator(),
60
+ operable=operable,
61
+ strict=False,
62
+ ),
63
+ verbose=True,
64
+ )
65
+
66
+ result = op.execution.response
67
+ status(f"Result type: {type(result).__name__}")
68
+ display(as_readable(result, format_curly=True), title="Structured Output")
69
+
70
+
71
+ if __name__ == "__main__":
72
+ anyio.run(main)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "krons"
3
- version = "0.1.1"
3
+ version = "0.2.1"
4
4
  description = "Spec-based composable framework for building type-safe systems"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -25,6 +25,7 @@ dependencies = [
25
25
  "httpx>=0.26.0",
26
26
  "orjson>=3.10.0",
27
27
  "pydantic>=2.10.0",
28
+ "pyyaml>=6.0.3",
28
29
  "rapidfuzz>=3.10.0",
29
30
  ]
30
31
 
@@ -41,6 +42,8 @@ dev = [
41
42
  "pytest>=8.0.0",
42
43
  "pytest-asyncio>=0.24.0",
43
44
  "hypothesis>=6.150.2",
45
+ "ipykernel>=7.1.0",
46
+ "ipywidgets>=8.1.8",
44
47
  ]
45
48
 
46
49
  [build-system]
@@ -58,4 +61,16 @@ markers = [
58
61
  "unit: marks tests as unit tests",
59
62
  "integration: marks tests as integration tests",
60
63
  "slow: marks tests as slow",
64
+ "property: marks tests as property-based tests (hypothesis)",
61
65
  ]
66
+
67
+ [project.optional-dependencies]
68
+ tiktoken = ["tiktoken>=0.11.0",]
69
+ fastmcp = ["fastmcp>=2.14.4",]
70
+
71
+ display = ["rich>=13.0"]
72
+
73
+ agent = [
74
+ "krons[tiktoken, fastmcp]",
75
+ ]
76
+ all = ["krons[agent, display]"]
@@ -0,0 +1,49 @@
1
+ # Copyright (c) 2025 - 2026, HaiyangLi <quantocean.li at gmail dot com>
2
+ # SPDX-License-Identifier: Apache-2.0
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import TYPE_CHECKING
7
+
8
+ # Lazy module re-exports via __getattr__
9
+ _MODULE_ALIASES: dict[str, str] = {
10
+ "types": "krons.core.types",
11
+ "specs": "krons.core.specs",
12
+ "session": "krons.session",
13
+ "operations": "krons.work.operations",
14
+ "agent": "krons.agent",
15
+ "resource": "krons.resource",
16
+ "work": "krons.work",
17
+ }
18
+
19
+ _LOADED_MODULES: dict[str, object] = {}
20
+
21
+
22
+ def __getattr__(name: str) -> object:
23
+ """Lazy load aliased modules."""
24
+ if name in _LOADED_MODULES:
25
+ return _LOADED_MODULES[name]
26
+
27
+ if name in _MODULE_ALIASES:
28
+ from importlib import import_module
29
+
30
+ module = import_module(_MODULE_ALIASES[name])
31
+ _LOADED_MODULES[name] = module
32
+ return module
33
+
34
+ raise AttributeError(f"module 'krons' has no attribute {name!r}")
35
+
36
+
37
+ def __dir__() -> list[str]:
38
+ """List available attributes."""
39
+ return list(_MODULE_ALIASES.keys())
40
+
41
+
42
+ if TYPE_CHECKING:
43
+ from krons import agent as agent
44
+ from krons import resource as resource
45
+ from krons import session as session
46
+ from krons import work as work
47
+ from krons.core import specs as specs
48
+ from krons.core import types as types
49
+ from krons.work import operations as operations