krons 0.1.0__tar.gz → 0.2.0__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 (278) hide show
  1. {krons-0.1.0 → krons-0.2.0}/.gitignore +2 -0
  2. {krons-0.1.0 → krons-0.2.0}/CLAUDE.md +136 -22
  3. {krons-0.1.0 → krons-0.2.0}/PKG-INFO +19 -5
  4. {krons-0.1.0 → krons-0.2.0}/README.md +1 -1
  5. krons-0.2.0/cookbooks/007_fan_out_in.py +227 -0
  6. krons-0.2.0/cookbooks/test_conduct.py +72 -0
  7. {krons-0.1.0 → krons-0.2.0}/pyproject.toml +21 -6
  8. krons-0.2.0/src/krons/__init__.py +49 -0
  9. krons-0.2.0/src/krons/agent/__init__.py +144 -0
  10. krons-0.2.0/src/krons/agent/mcps/__init__.py +14 -0
  11. krons-0.2.0/src/krons/agent/mcps/loader.py +287 -0
  12. krons-0.2.0/src/krons/agent/mcps/wrapper.py +799 -0
  13. krons-0.2.0/src/krons/agent/message/__init__.py +20 -0
  14. krons-0.2.0/src/krons/agent/message/action.py +69 -0
  15. krons-0.2.0/src/krons/agent/message/assistant.py +52 -0
  16. krons-0.2.0/src/krons/agent/message/common.py +49 -0
  17. krons-0.2.0/src/krons/agent/message/instruction.py +130 -0
  18. krons-0.2.0/src/krons/agent/message/prepare_msg.py +187 -0
  19. krons-0.2.0/src/krons/agent/message/role.py +53 -0
  20. krons-0.2.0/src/krons/agent/message/system.py +53 -0
  21. krons-0.2.0/src/krons/agent/operations/__init__.py +82 -0
  22. krons-0.2.0/src/krons/agent/operations/act.py +100 -0
  23. krons-0.2.0/src/krons/agent/operations/generate.py +145 -0
  24. krons-0.2.0/src/krons/agent/operations/llm_reparse.py +89 -0
  25. krons-0.2.0/src/krons/agent/operations/operate.py +247 -0
  26. krons-0.2.0/src/krons/agent/operations/parse.py +243 -0
  27. krons-0.2.0/src/krons/agent/operations/react.py +286 -0
  28. krons-0.2.0/src/krons/agent/operations/specs.py +235 -0
  29. krons-0.2.0/src/krons/agent/operations/structure.py +151 -0
  30. krons-0.2.0/src/krons/agent/operations/utils.py +79 -0
  31. krons-0.2.0/src/krons/agent/providers/__init__.py +17 -0
  32. krons-0.2.0/src/krons/agent/providers/anthropic_messages.py +146 -0
  33. krons-0.2.0/src/krons/agent/providers/claude_code.py +276 -0
  34. krons-0.2.0/src/krons/agent/providers/gemini.py +268 -0
  35. krons-0.2.0/src/krons/agent/providers/match.py +75 -0
  36. krons-0.2.0/src/krons/agent/providers/oai_chat.py +174 -0
  37. krons-0.2.0/src/krons/agent/third_party/anthropic_models.py +154 -0
  38. krons-0.2.0/src/krons/agent/third_party/claude_code.py +682 -0
  39. krons-0.2.0/src/krons/agent/third_party/gemini_models.py +508 -0
  40. krons-0.2.0/src/krons/agent/third_party/openai_models.py +295 -0
  41. krons-0.2.0/src/krons/agent/tool.py +291 -0
  42. krons-0.2.0/src/krons/core/__init__.py +127 -0
  43. krons-0.2.0/src/krons/core/base/__init__.py +121 -0
  44. {krons-0.1.0/src/kronos/core → krons-0.2.0/src/krons/core/base}/broadcaster.py +7 -3
  45. {krons-0.1.0/src/kronos/core → krons-0.2.0/src/krons/core/base}/element.py +15 -7
  46. {krons-0.1.0/src/kronos/core → krons-0.2.0/src/krons/core/base}/event.py +41 -8
  47. {krons-0.1.0/src/kronos/core → krons-0.2.0/src/krons/core/base}/eventbus.py +4 -2
  48. {krons-0.1.0/src/kronos/core → krons-0.2.0/src/krons/core/base}/flow.py +14 -7
  49. {krons-0.1.0/src/kronos/core → krons-0.2.0/src/krons/core/base}/graph.py +27 -11
  50. {krons-0.1.0/src/kronos/core → krons-0.2.0/src/krons/core/base}/node.py +47 -22
  51. {krons-0.1.0/src/kronos/core → krons-0.2.0/src/krons/core/base}/pile.py +26 -12
  52. {krons-0.1.0/src/kronos/core → krons-0.2.0/src/krons/core/base}/processor.py +23 -9
  53. {krons-0.1.0/src/kronos/core → krons-0.2.0/src/krons/core/base}/progression.py +5 -3
  54. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/specs/__init__.py +0 -5
  55. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/specs/adapters/dataclass_field.py +16 -8
  56. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/specs/adapters/pydantic_adapter.py +11 -5
  57. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/specs/adapters/sql_ddl.py +16 -10
  58. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/specs/catalog/__init__.py +2 -2
  59. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/specs/catalog/_audit.py +3 -3
  60. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/specs/catalog/_common.py +2 -2
  61. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/specs/catalog/_content.py +5 -5
  62. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/specs/catalog/_enforcement.py +4 -4
  63. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/specs/factory.py +7 -7
  64. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/specs/operable.py +9 -3
  65. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/specs/protocol.py +4 -2
  66. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/specs/spec.py +25 -13
  67. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/types/base.py +7 -5
  68. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/types/db_types.py +2 -2
  69. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/types/identity.py +1 -1
  70. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/errors.py +13 -13
  71. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/protocols.py +9 -4
  72. krons-0.2.0/src/krons/resource/__init__.py +89 -0
  73. {krons-0.1.0/src/kronos/services → krons-0.2.0/src/krons/resource}/backend.py +50 -24
  74. {krons-0.1.0/src/kronos/services → krons-0.2.0/src/krons/resource}/endpoint.py +28 -14
  75. {krons-0.1.0/src/kronos/services → krons-0.2.0/src/krons/resource}/hook.py +22 -9
  76. {krons-0.1.0/src/kronos/services → krons-0.2.0/src/krons/resource}/imodel.py +50 -32
  77. {krons-0.1.0/src/kronos/services → krons-0.2.0/src/krons/resource}/registry.py +27 -25
  78. {krons-0.1.0/src/kronos/services → krons-0.2.0/src/krons/resource}/utilities/rate_limited_executor.py +10 -6
  79. {krons-0.1.0/src/kronos/services → krons-0.2.0/src/krons/resource}/utilities/rate_limiter.py +4 -2
  80. {krons-0.1.0/src/kronos/services → krons-0.2.0/src/krons/resource}/utilities/resilience.py +17 -7
  81. krons-0.2.0/src/krons/resource/utilities/token_calculator.py +185 -0
  82. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/session/__init__.py +12 -17
  83. krons-0.2.0/src/krons/session/constraints.py +70 -0
  84. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/session/exchange.py +14 -6
  85. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/session/message.py +4 -2
  86. krons-0.2.0/src/krons/session/registry.py +35 -0
  87. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/session/session.py +165 -174
  88. krons-0.2.0/src/krons/utils/__init__.py +85 -0
  89. krons-0.2.0/src/krons/utils/_function_arg_parser.py +99 -0
  90. krons-0.2.0/src/krons/utils/_pythonic_function_call.py +249 -0
  91. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/_to_list.py +9 -3
  92. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/_utils.py +9 -5
  93. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/concurrency/__init__.py +38 -38
  94. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/concurrency/_async_call.py +6 -4
  95. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/concurrency/_errors.py +3 -1
  96. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/concurrency/_patterns.py +3 -1
  97. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/concurrency/_resource_tracker.py +6 -2
  98. krons-0.2.0/src/krons/utils/display.py +257 -0
  99. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/fuzzy/__init__.py +6 -1
  100. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/fuzzy/_fuzzy_match.py +14 -8
  101. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/fuzzy/_string_similarity.py +3 -1
  102. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/fuzzy/_to_dict.py +3 -1
  103. krons-0.2.0/src/krons/utils/schemas/__init__.py +26 -0
  104. krons-0.2.0/src/krons/utils/schemas/_breakdown_pydantic_annotation.py +131 -0
  105. krons-0.2.0/src/krons/utils/schemas/_formatter.py +72 -0
  106. krons-0.2.0/src/krons/utils/schemas/_minimal_yaml.py +151 -0
  107. krons-0.2.0/src/krons/utils/schemas/_typescript.py +153 -0
  108. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/sql/_sql_validation.py +1 -1
  109. krons-0.2.0/src/krons/utils/validators/__init__.py +3 -0
  110. krons-0.2.0/src/krons/utils/validators/_validate_image_url.py +56 -0
  111. krons-0.2.0/src/krons/work/__init__.py +126 -0
  112. krons-0.2.0/src/krons/work/engine.py +333 -0
  113. krons-0.2.0/src/krons/work/form.py +305 -0
  114. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/work}/operations/__init__.py +7 -4
  115. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/work}/operations/builder.py +4 -4
  116. {krons-0.1.0/src/kronos/enforcement → krons-0.2.0/src/krons/work/operations}/context.py +37 -6
  117. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/work}/operations/flow.py +17 -9
  118. krons-0.2.0/src/krons/work/operations/node.py +103 -0
  119. krons-0.2.0/src/krons/work/operations/registry.py +103 -0
  120. {krons-0.1.0/src/kronos/specs → krons-0.2.0/src/krons/work}/phrase.py +131 -14
  121. {krons-0.1.0/src/kronos/enforcement → krons-0.2.0/src/krons/work}/policy.py +3 -3
  122. krons-0.2.0/src/krons/work/report.py +268 -0
  123. krons-0.2.0/src/krons/work/rules/__init__.py +47 -0
  124. {krons-0.1.0/src/kronos/enforcement → krons-0.2.0/src/krons/work/rules}/common/boolean.py +3 -1
  125. {krons-0.1.0/src/kronos/enforcement → krons-0.2.0/src/krons/work/rules}/common/choice.py +9 -3
  126. {krons-0.1.0/src/kronos/enforcement → krons-0.2.0/src/krons/work/rules}/common/number.py +3 -1
  127. {krons-0.1.0/src/kronos/enforcement → krons-0.2.0/src/krons/work/rules}/common/string.py +9 -3
  128. {krons-0.1.0/src/kronos/enforcement → krons-0.2.0/src/krons/work/rules}/rule.py +2 -2
  129. {krons-0.1.0/src/kronos/enforcement → krons-0.2.0/src/krons/work/rules}/validator.py +21 -6
  130. {krons-0.1.0/src/kronos/enforcement → krons-0.2.0/src/krons/work}/service.py +16 -7
  131. krons-0.2.0/src/krons/work/worker.py +266 -0
  132. krons-0.2.0/tests/agent/test_mcps_unit.py +216 -0
  133. krons-0.2.0/tests/agent/test_message.py +220 -0
  134. krons-0.2.0/tests/agent/test_providers_e2e.py +339 -0
  135. krons-0.2.0/tests/agent/test_providers_unit.py +276 -0
  136. krons-0.2.0/tests/agent/test_tool_unit.py +293 -0
  137. {krons-0.1.0 → krons-0.2.0}/tests/conftest.py +9 -5
  138. {krons-0.1.0 → krons-0.2.0}/tests/core/test_broadcaster.py +1 -2
  139. {krons-0.1.0 → krons-0.2.0}/tests/core/test_element.py +1 -1
  140. {krons-0.1.0 → krons-0.2.0}/tests/core/test_error_paths.py +15 -15
  141. {krons-0.1.0 → krons-0.2.0}/tests/core/test_event.py +2 -3
  142. {krons-0.1.0 → krons-0.2.0}/tests/core/test_event_status_race.py +2 -2
  143. {krons-0.1.0 → krons-0.2.0}/tests/core/test_eventbus.py +1 -1
  144. {krons-0.1.0 → krons-0.2.0}/tests/core/test_flow.py +2 -2
  145. {krons-0.1.0 → krons-0.2.0}/tests/core/test_flow_edge_cases.py +11 -5
  146. {krons-0.1.0 → krons-0.2.0}/tests/core/test_graph.py +2 -2
  147. {krons-0.1.0 → krons-0.2.0}/tests/core/test_graph_event_loop.py +1 -1
  148. {krons-0.1.0 → krons-0.2.0}/tests/core/test_node.py +12 -11
  149. {krons-0.1.0 → krons-0.2.0}/tests/core/test_pile.py +2 -2
  150. {krons-0.1.0 → krons-0.2.0}/tests/core/test_pile_edge_cases.py +1 -1
  151. {krons-0.1.0 → krons-0.2.0}/tests/core/test_processor.py +1 -2
  152. {krons-0.1.0 → krons-0.2.0}/tests/core/test_processor_security.py +51 -21
  153. {krons-0.1.0 → krons-0.2.0}/tests/core/test_progression.py +2 -2
  154. {krons-0.1.0 → krons-0.2.0}/tests/core/test_progression_edge_cases.py +1 -1
  155. {krons-0.1.0 → krons-0.2.0}/tests/core/test_serialization_roundtrip.py +59 -18
  156. {krons-0.1.0 → krons-0.2.0}/tests/core/test_thread_safety_stress.py +5 -3
  157. {krons-0.1.0 → krons-0.2.0}/tests/enforcement/test_service.py +34 -38
  158. {krons-0.1.0 → krons-0.2.0}/tests/operations/test_builder.py +24 -8
  159. {krons-0.1.0 → krons-0.2.0}/tests/operations/test_flow.py +23 -18
  160. {krons-0.1.0 → krons-0.2.0}/tests/operations/test_node.py +7 -45
  161. {krons-0.1.0 → krons-0.2.0}/tests/operations/test_op_flow.py +105 -81
  162. {krons-0.1.0 → krons-0.2.0}/tests/operations/test_op_node.py +7 -13
  163. {krons-0.1.0 → krons-0.2.0}/tests/operations/test_registry.py +28 -37
  164. {krons-0.1.0/tests/services → krons-0.2.0/tests/resource}/test_backend.py +75 -60
  165. {krons-0.1.0/tests/services → krons-0.2.0/tests/resource}/test_endpoint.py +9 -5
  166. {krons-0.1.0/tests/services → krons-0.2.0/tests/resource}/test_hook.py +39 -19
  167. {krons-0.1.0/tests/services → krons-0.2.0/tests/resource}/test_imodel.py +12 -10
  168. {krons-0.1.0/tests/services → krons-0.2.0/tests/resource}/test_registry.py +21 -21
  169. {krons-0.1.0/tests/services → krons-0.2.0/tests/resource}/utilities/test_header_factory.py +4 -2
  170. {krons-0.1.0/tests/services → krons-0.2.0/tests/resource}/utilities/test_rate_limiter.py +4 -4
  171. {krons-0.1.0/tests/services → krons-0.2.0/tests/resource}/utilities/test_resilience.py +23 -9
  172. {krons-0.1.0/tests/services → krons-0.2.0/tests/resource}/utilities/test_resilience_retry.py +24 -14
  173. {krons-0.1.0 → krons-0.2.0}/tests/rules/test_base.py +5 -2
  174. {krons-0.1.0 → krons-0.2.0}/tests/rules/test_builtin.py +1 -1
  175. {krons-0.1.0 → krons-0.2.0}/tests/rules/test_registry.py +5 -3
  176. {krons-0.1.0 → krons-0.2.0}/tests/rules/test_rule_params_edge_cases.py +1 -1
  177. {krons-0.1.0 → krons-0.2.0}/tests/rules/test_rules_comprehensive.py +18 -8
  178. {krons-0.1.0 → krons-0.2.0}/tests/rules/test_validator.py +12 -6
  179. {krons-0.1.0 → krons-0.2.0}/tests/session/test_exchange.py +4 -2
  180. {krons-0.1.0 → krons-0.2.0}/tests/session/test_message.py +23 -9
  181. {krons-0.1.0 → krons-0.2.0}/tests/session/test_message_edge_cases.py +8 -4
  182. {krons-0.1.0 → krons-0.2.0}/tests/session/test_session.py +36 -29
  183. {krons-0.1.0 → krons-0.2.0}/tests/session/test_session_edge_cases.py +19 -22
  184. {krons-0.1.0 → krons-0.2.0}/tests/specs/test_catalog.py +2 -2
  185. {krons-0.1.0 → krons-0.2.0}/tests/specs/test_factory.py +8 -8
  186. {krons-0.1.0 → krons-0.2.0}/tests/specs/test_phrase.py +4 -4
  187. {krons-0.1.0 → krons-0.2.0}/tests/test_protocols.py +10 -7
  188. {krons-0.1.0 → krons-0.2.0}/tests/types/conftest.py +5 -3
  189. {krons-0.1.0 → krons-0.2.0}/tests/types/spec_adapters/test_adapters_py311.py +12 -6
  190. {krons-0.1.0 → krons-0.2.0}/tests/types/spec_adapters/test_protocol.py +1 -1
  191. {krons-0.1.0 → krons-0.2.0}/tests/types/spec_adapters/test_pydantic_field.py +14 -6
  192. {krons-0.1.0 → krons-0.2.0}/tests/types/spec_adapters/test_sql_ddl_specs.py +13 -7
  193. {krons-0.1.0 → krons-0.2.0}/tests/types/test_db_types.py +1 -1
  194. {krons-0.1.0 → krons-0.2.0}/tests/types/test_identity.py +1 -1
  195. {krons-0.1.0 → krons-0.2.0}/tests/types/test_model.py +1 -1
  196. {krons-0.1.0 → krons-0.2.0}/tests/types/test_operable.py +9 -5
  197. {krons-0.1.0 → krons-0.2.0}/tests/types/test_sentinel.py +1 -1
  198. {krons-0.1.0 → krons-0.2.0}/tests/types/test_spec.py +15 -13
  199. {krons-0.1.0 → krons-0.2.0}/tests/types/test_types.py +7 -3
  200. {krons-0.1.0 → krons-0.2.0}/tests/types/test_types_py311.py +9 -2
  201. krons-0.2.0/tests/utils/concurrency/__init__.py +2 -0
  202. {krons-0.1.0 → krons-0.2.0}/tests/utils/concurrency/test_async_call.py +4 -2
  203. {krons-0.1.0 → krons-0.2.0}/tests/utils/concurrency/test_cancel.py +4 -2
  204. {krons-0.1.0 → krons-0.2.0}/tests/utils/concurrency/test_errors.py +2 -2
  205. {krons-0.1.0 → krons-0.2.0}/tests/utils/concurrency/test_patterns.py +17 -6
  206. {krons-0.1.0 → krons-0.2.0}/tests/utils/concurrency/test_primitives.py +1 -1
  207. {krons-0.1.0 → krons-0.2.0}/tests/utils/concurrency/test_priority_queue.py +1 -1
  208. {krons-0.1.0 → krons-0.2.0}/tests/utils/concurrency/test_run_async.py +4 -2
  209. {krons-0.1.0 → krons-0.2.0}/tests/utils/concurrency/test_task.py +1 -1
  210. {krons-0.1.0 → krons-0.2.0}/tests/utils/concurrency/test_utils.py +1 -1
  211. {krons-0.1.0 → krons-0.2.0}/tests/utils/test_extract_json.py +1 -1
  212. {krons-0.1.0 → krons-0.2.0}/tests/utils/test_fuzzy_json.py +4 -2
  213. {krons-0.1.0 → krons-0.2.0}/tests/utils/test_fuzzy_match.py +11 -5
  214. {krons-0.1.0 → krons-0.2.0}/tests/utils/test_hash.py +5 -3
  215. {krons-0.1.0 → krons-0.2.0}/tests/utils/test_json_dump.py +4 -2
  216. {krons-0.1.0 → krons-0.2.0}/tests/utils/test_lazy_init.py +1 -1
  217. {krons-0.1.0 → krons-0.2.0}/tests/utils/test_sql_validation.py +2 -2
  218. {krons-0.1.0 → krons-0.2.0}/tests/utils/test_string_similarity.py +13 -5
  219. {krons-0.1.0 → krons-0.2.0}/tests/utils/test_to_dict.py +4 -2
  220. {krons-0.1.0 → krons-0.2.0}/tests/utils/test_to_list.py +1 -1
  221. {krons-0.1.0 → krons-0.2.0}/tests/utils/test_to_num.py +1 -1
  222. {krons-0.1.0 → krons-0.2.0}/tests/utils/test_utils.py +1 -1
  223. krons-0.2.0/tests/work/__init__.py +4 -0
  224. krons-0.2.0/tests/work/test_engine.py +817 -0
  225. krons-0.2.0/tests/work/test_form.py +640 -0
  226. krons-0.2.0/tests/work/test_phrase.py +1141 -0
  227. krons-0.2.0/tests/work/test_report.py +550 -0
  228. krons-0.2.0/tests/work/test_worker.py +665 -0
  229. krons-0.2.0/uv.lock +2628 -0
  230. krons-0.1.0/src/kronos/core/__init__.py +0 -145
  231. krons-0.1.0/src/kronos/enforcement/__init__.py +0 -57
  232. krons-0.1.0/src/kronos/operations/node.py +0 -101
  233. krons-0.1.0/src/kronos/operations/registry.py +0 -92
  234. krons-0.1.0/src/kronos/services/__init__.py +0 -81
  235. krons-0.1.0/src/kronos/utils/__init__.py +0 -40
  236. krons-0.1.0/uv.lock +0 -641
  237. {krons-0.1.0 → krons-0.2.0}/.github/workflows/release.yml +0 -0
  238. {krons-0.1.0 → krons-0.2.0}/.python-version +0 -0
  239. {krons-0.1.0 → krons-0.2.0}/LICENSE +0 -0
  240. {krons-0.1.0/tests → krons-0.2.0/src/krons/agent/third_party}/__init__.py +0 -0
  241. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core/specs/adapters}/__init__.py +0 -0
  242. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/specs/adapters/_utils.py +0 -0
  243. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/specs/adapters/factory.py +0 -0
  244. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/types/__init__.py +0 -0
  245. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons/core}/types/_sentinel.py +0 -0
  246. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/py.typed +0 -0
  247. {krons-0.1.0/src/kronos/services → krons-0.2.0/src/krons/resource}/utilities/__init__.py +0 -0
  248. {krons-0.1.0/src/kronos/services → krons-0.2.0/src/krons/resource}/utilities/header_factory.py +0 -0
  249. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/_hash.py +0 -0
  250. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/_json_dump.py +0 -0
  251. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/_lazy_init.py +0 -0
  252. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/_to_num.py +0 -0
  253. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/concurrency/_cancel.py +0 -0
  254. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/concurrency/_primitives.py +0 -0
  255. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/concurrency/_priority_queue.py +0 -0
  256. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/concurrency/_run_async.py +0 -0
  257. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/concurrency/_task.py +0 -0
  258. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/concurrency/_utils.py +0 -0
  259. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/fuzzy/_extract_json.py +0 -0
  260. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/fuzzy/_fuzzy_json.py +0 -0
  261. {krons-0.1.0/src/kronos → krons-0.2.0/src/krons}/utils/sql/__init__.py +0 -0
  262. {krons-0.1.0/src/kronos/enforcement → krons-0.2.0/src/krons/work/rules}/common/__init__.py +0 -0
  263. {krons-0.1.0/src/kronos/enforcement → krons-0.2.0/src/krons/work/rules}/common/mapping.py +0 -0
  264. {krons-0.1.0/src/kronos/enforcement → krons-0.2.0/src/krons/work/rules}/common/model.py +0 -0
  265. {krons-0.1.0/src/kronos/enforcement → krons-0.2.0/src/krons/work/rules}/registry.py +0 -0
  266. {krons-0.1.0/tests/core → krons-0.2.0/tests}/__init__.py +0 -0
  267. {krons-0.1.0/src/kronos/specs/adapters → krons-0.2.0/tests/agent}/__init__.py +0 -0
  268. {krons-0.1.0/tests/enforcement → krons-0.2.0/tests/core}/__init__.py +0 -0
  269. {krons-0.1.0/tests/operations → krons-0.2.0/tests/enforcement}/__init__.py +0 -0
  270. {krons-0.1.0/tests/rules → krons-0.2.0/tests/operations}/__init__.py +0 -0
  271. {krons-0.1.0/tests/services → krons-0.2.0/tests/resource}/__init__.py +0 -0
  272. {krons-0.1.0/tests/services → krons-0.2.0/tests/resource}/utilities/__init__.py +0 -0
  273. {krons-0.1.0/tests/session → krons-0.2.0/tests/rules}/__init__.py +0 -0
  274. {krons-0.1.0/tests/types → krons-0.2.0/tests/session}/__init__.py +0 -0
  275. {krons-0.1.0 → krons-0.2.0}/tests/specs/__init__.py +0 -0
  276. {krons-0.1.0/tests/types/spec_adapters → krons-0.2.0/tests/types}/__init__.py +0 -0
  277. {krons-0.1.0/tests/utils → krons-0.2.0/tests/types/spec_adapters}/__init__.py +0 -0
  278. {krons-0.1.0/tests/utils/concurrency → krons-0.2.0/tests/utils}/__init__.py +0 -0
@@ -205,3 +205,5 @@ cython_debug/
205
205
  marimo/_static/
206
206
  marimo/_lsp/
207
207
  __marimo__/
208
+
209
+ *.DS_Store
@@ -1,26 +1,28 @@
1
- # kronos - Spec-Based Composable Framework
1
+ # krons - Spec-Based Composable Framework
2
2
 
3
3
  ## Overview
4
4
 
5
- **kronos** is a Python framework for building spec-based, composable systems. It provides:
5
+ **krons** is a Python framework for building spec-based, composable systems. It provides:
6
6
 
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
 
14
15
  ```
15
- kronos/
16
+ krons/
16
17
  ├── core/ # Foundation: Element, Node, Event, Flow, Graph, Pile
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
  ```
@@ -32,8 +34,8 @@ kronos/
32
34
  **Spec** defines a single field with type, name, default, validation, and DB metadata:
33
35
 
34
36
  ```python
35
- from kronos.specs import Spec, Operable
36
- from kronos.types.db_types import FK, VectorMeta
37
+ from krons.specs import Spec, Operable
38
+ from krons.types.db_types import FK, VectorMeta
37
39
 
38
40
  # Basic specs
39
41
  name_spec = Spec(str, name="name")
@@ -66,7 +68,7 @@ specs_list = operable.get_specs()
66
68
  Pre-built specs use BaseModel for field definitions:
67
69
 
68
70
  ```python
69
- from kronos.specs.catalog import ContentSpecs, AuditSpecs, CommonSpecs
71
+ from krons.specs.catalog import ContentSpecs, AuditSpecs, CommonSpecs
70
72
 
71
73
  # Get specs with customization
72
74
  content_specs = ContentSpecs.get_specs(dim=1536) # With vector dimension
@@ -95,8 +97,8 @@ class MySpecs(BaseModel):
95
97
  **Node** stores polymorphic content with DB serialization:
96
98
 
97
99
  ```python
98
- from kronos.core import Node
99
- from kronos.core.node import create_node, NodeConfig
100
+ from krons.core import Node
101
+ from krons.core.node import create_node, NodeConfig
100
102
 
101
103
  # Basic usage
102
104
  node = Node(content={"key": "value"})
@@ -129,7 +131,7 @@ restored = JobNode.from_dict(db_data, from_row=True) # Reconstructs content
129
131
  **iModel** - Unified service interface with rate limiting:
130
132
 
131
133
  ```python
132
- from kronos.services import Endpoint, EndpointConfig, iModel
134
+ from krons.services import Endpoint, EndpointConfig, iModel
133
135
 
134
136
  config = EndpointConfig(
135
137
  name="gpt-4",
@@ -147,7 +149,7 @@ response = await model.invoke({"messages": [...]})
147
149
  **KronService** - Action handlers with policy evaluation:
148
150
 
149
151
  ```python
150
- from kronos.enforcement import KronService, KronConfig, action, RequestContext
152
+ from krons.enforcement import KronService, KronConfig, action, RequestContext
151
153
 
152
154
  class MyService(KronService):
153
155
  @property
@@ -167,7 +169,7 @@ result = await service.call("user.create", {"name": "John"}, RequestContext(name
167
169
  Runtime-checkable protocols with signature validation:
168
170
 
169
171
  ```python
170
- from kronos.protocols import implements, Serializable, SignatureMismatchError
172
+ from krons.protocols import implements, Serializable, SignatureMismatchError
171
173
 
172
174
  @implements(Serializable, signature_check="error") # "error", "warn", "skip"
173
175
  class MyClass:
@@ -180,7 +182,7 @@ class MyClass:
180
182
  Foreign keys and vector embeddings for SQL DDL:
181
183
 
182
184
  ```python
183
- from kronos.types.db_types import FK, Vector, FKMeta, VectorMeta, extract_kron_db_meta
185
+ from krons.types.db_types import FK, Vector, FKMeta, VectorMeta, extract_kron_db_meta
184
186
 
185
187
  # In type annotations
186
188
  class Post(BaseModel):
@@ -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
 
@@ -235,11 +342,11 @@ class MockPolicyEngine:
235
342
  1. **Circular imports in catalog**: Use direct imports from submodules:
236
343
  ```python
237
344
  # Wrong
238
- from kronos.specs import Operable, Spec
345
+ from krons.specs import Operable, Spec
239
346
 
240
347
  # Right (in catalog files)
241
- from kronos.specs.operable import Operable
242
- from kronos.specs.spec import Spec
348
+ from krons.specs.operable import Operable
349
+ from krons.specs.spec import Spec
243
350
  ```
244
351
 
245
352
  2. **PolicyEngine/PolicyResolver are Protocols**: Can't instantiate directly, create mock classes.
@@ -251,18 +358,25 @@ 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/kronos
258
-
259
372
  # All tests
260
373
  uv run pytest tests/ -q
261
374
 
262
375
  # With coverage
263
- uv run pytest tests/ --cov=kronos --cov-report=term-missing
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,10 +1,10 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: krons
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Spec-based composable framework for building type-safe systems
5
- Project-URL: Homepage, https://github.com/khive-ai/kronos
6
- Project-URL: Repository, https://github.com/khive-ai/kronos
7
- Project-URL: Issues, https://github.com/khive-ai/kronos/issues
5
+ Project-URL: Homepage, https://github.com/khive-ai/krons
6
+ Project-URL: Repository, https://github.com/khive-ai/krons
7
+ Project-URL: Issues, https://github.com/khive-ai/krons/issues
8
8
  Author-email: HaiyangLi <quantocean.li@gmail.com>
9
9
  License-Expression: Apache-2.0
10
10
  License-File: LICENSE
@@ -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
@@ -46,7 +60,7 @@ pip install krons
46
60
  ## Quick Start
47
61
 
48
62
  ```python
49
- from kronos.specs import Spec, Operable
63
+ from krons.specs import Spec, Operable
50
64
 
51
65
  # Define specs
52
66
  name_spec = Spec(str, name="name")
@@ -19,7 +19,7 @@ pip install krons
19
19
  ## Quick Start
20
20
 
21
21
  ```python
22
- from kronos.specs import Spec, Operable
22
+ from krons.specs import Spec, Operable
23
23
 
24
24
  # Define specs
25
25
  name_spec = Spec(str, name="name")
@@ -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.0"
3
+ version = "0.2.0"
4
4
  description = "Spec-based composable framework for building type-safe systems"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -25,13 +25,14 @@ 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
 
31
32
  [project.urls]
32
- Homepage = "https://github.com/khive-ai/kronos"
33
- Repository = "https://github.com/khive-ai/kronos"
34
- Issues = "https://github.com/khive-ai/kronos/issues"
33
+ Homepage = "https://github.com/khive-ai/krons"
34
+ Repository = "https://github.com/khive-ai/krons"
35
+ Issues = "https://github.com/khive-ai/krons/issues"
35
36
 
36
37
  [dependency-groups]
37
38
  dev = [
@@ -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]
@@ -48,8 +51,8 @@ requires = ["hatchling"]
48
51
  build-backend = "hatchling.build"
49
52
 
50
53
  [tool.hatch.build.targets.wheel]
51
- packages = ["src/kronos"]
52
- artifacts = ["src/kronos/py.typed"]
54
+ packages = ["src/krons"]
55
+ artifacts = ["src/krons/py.typed"]
53
56
 
54
57
  [tool.pytest.ini_options]
55
58
  asyncio_mode = "strict"
@@ -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]"]