tactus 0.22.0__tar.gz → 0.23.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 (551) hide show
  1. {tactus-0.22.0 → tactus-0.23.0}/CHANGELOG.md +8 -0
  2. {tactus-0.22.0 → tactus-0.23.0}/PKG-INFO +1 -1
  3. {tactus-0.22.0 → tactus-0.23.0}/SPECIFICATION.md +66 -0
  4. tactus-0.23.0/examples/01-basics-hello-world.tac +8 -0
  5. tactus-0.23.0/examples/test-raw-module.tac +11 -0
  6. tactus-0.23.0/examples/test-raw-streaming.tac +11 -0
  7. tactus-0.23.0/features/57_chat_assistant.feature +60 -0
  8. tactus-0.23.0/features/steps/chat_assistant_steps.py +160 -0
  9. {tactus-0.22.0 → tactus-0.23.0}/features/steps/support/harnesses.py +158 -0
  10. {tactus-0.22.0 → tactus-0.23.0}/pyproject.toml +1 -1
  11. {tactus-0.22.0 → tactus-0.23.0}/tactus/__init__.py +1 -1
  12. {tactus-0.22.0 → tactus-0.23.0}/tactus/dspy/agent.py +2 -0
  13. {tactus-0.22.0 → tactus-0.23.0}/tactus/dspy/config.py +66 -0
  14. {tactus-0.22.0 → tactus-0.23.0}/tactus/dspy/module.py +136 -1
  15. {tactus-0.22.0 → tactus-0.23.0}/tactus/ide/server.py +66 -54
  16. {tactus-0.22.0 → tactus-0.23.0}/tactus/sandbox/container_runner.py +76 -0
  17. {tactus-0.22.0 → tactus-0.23.0}/tactus/sandbox/docker_manager.py +103 -3
  18. tactus-0.23.0/tactus-ide/backend/assistant_service.py +444 -0
  19. tactus-0.23.0/tactus-ide/backend/assistant_tools.py +397 -0
  20. tactus-0.23.0/tactus-ide/backend/chat_server.py +339 -0
  21. tactus-0.23.0/tactus-ide/backend/text_editor_tool.py +172 -0
  22. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/package-lock.json +0 -154
  23. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/App.tsx +3 -3
  24. tactus-0.23.0/tactus-ide/frontend/src/components/ChatSidebar.tsx +24 -0
  25. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ResultsSidebar.tsx +64 -43
  26. tactus-0.23.0/tactus-ide/frontend/src/components/chat/ChatInterface.tsx +96 -0
  27. tactus-0.23.0/tactus-ide/frontend/src/components/chat/MessageInput.tsx +84 -0
  28. tactus-0.23.0/tactus-ide/frontend/src/components/chat/MessageList.tsx +125 -0
  29. tactus-0.23.0/tactus-ide/frontend/src/hooks/useChatSSE.ts +204 -0
  30. tactus-0.22.0/examples/01-basics-hello-world.tac +0 -9
  31. tactus-0.22.0/tactus-ide/frontend/src/components/ChatSidebar.tsx +0 -195
  32. {tactus-0.22.0 → tactus-0.23.0}/.claude/agents.md +0 -0
  33. {tactus-0.22.0 → tactus-0.23.0}/.github/workflows/desktop-release.yml +0 -0
  34. {tactus-0.22.0 → tactus-0.23.0}/.github/workflows/release.yml +0 -0
  35. {tactus-0.22.0 → tactus-0.23.0}/.gitignore +0 -0
  36. {tactus-0.22.0 → tactus-0.23.0}/.tactus/config.yml.example +0 -0
  37. {tactus-0.22.0 → tactus-0.23.0}/AGENTS.md +0 -0
  38. {tactus-0.22.0 → tactus-0.23.0}/IMPLEMENTATION.md +0 -0
  39. {tactus-0.22.0 → tactus-0.23.0}/LICENSE +0 -0
  40. {tactus-0.22.0 → tactus-0.23.0}/Makefile +0 -0
  41. {tactus-0.22.0 → tactus-0.23.0}/README.md +0 -0
  42. {tactus-0.22.0 → tactus-0.23.0}/SPECIFICATION.md.bak +0 -0
  43. {tactus-0.22.0 → tactus-0.23.0}/TECHNICAL_DEBT.md +0 -0
  44. {tactus-0.22.0 → tactus-0.23.0}/behave.ini +0 -0
  45. {tactus-0.22.0 → tactus-0.23.0}/docs/AGENTS.md +0 -0
  46. {tactus-0.22.0 → tactus-0.23.0}/docs/BDD_TESTING.md +0 -0
  47. {tactus-0.22.0 → tactus-0.23.0}/docs/CONFIGURATION.md +0 -0
  48. {tactus-0.22.0 → tactus-0.23.0}/docs/DURABILITY.md +0 -0
  49. {tactus-0.22.0 → tactus-0.23.0}/docs/FILE_IO.md +0 -0
  50. {tactus-0.22.0 → tactus-0.23.0}/docs/SANDBOXING.md +0 -0
  51. {tactus-0.22.0 → tactus-0.23.0}/docs/STREAMING.md +0 -0
  52. {tactus-0.22.0 → tactus-0.23.0}/docs/TOOLS.md +0 -0
  53. {tactus-0.22.0 → tactus-0.23.0}/docs/TOOL_ROADMAP.md +0 -0
  54. {tactus-0.22.0 → tactus-0.23.0}/examples/.tactus/config.yml +0 -0
  55. {tactus-0.22.0 → tactus-0.23.0}/examples/02-basics-simple-logic.tac +0 -0
  56. {tactus-0.22.0 → tactus-0.23.0}/examples/03-basics-parameters.tac +0 -0
  57. {tactus-0.22.0 → tactus-0.23.0}/examples/04-basics-simple-agent.tac +0 -0
  58. {tactus-0.22.0 → tactus-0.23.0}/examples/05-basics-multi-model.tac +0 -0
  59. {tactus-0.22.0 → tactus-0.23.0}/examples/06-basics-streaming.tac +0 -0
  60. {tactus-0.22.0 → tactus-0.23.0}/examples/07-basics-bedrock.tac +0 -0
  61. {tactus-0.22.0 → tactus-0.23.0}/examples/08-basics-models.tac +0 -0
  62. {tactus-0.22.0 → tactus-0.23.0}/examples/09-basics-google-gemini.tac +0 -0
  63. {tactus-0.22.0 → tactus-0.23.0}/examples/10-feature-state.tac +0 -0
  64. {tactus-0.22.0 → tactus-0.23.0}/examples/11-feature-message-history.tac +0 -0
  65. {tactus-0.22.0 → tactus-0.23.0}/examples/12-feature-structured-output.tac +0 -0
  66. {tactus-0.22.0 → tactus-0.23.0}/examples/13-feature-session.tac +0 -0
  67. {tactus-0.22.0 → tactus-0.23.0}/examples/14-feature-per-turn-tools-simple.tac +0 -0
  68. {tactus-0.22.0 → tactus-0.23.0}/examples/14-feature-per-turn-tools.tac +0 -0
  69. {tactus-0.22.0 → tactus-0.23.0}/examples/15-feature-local-tools.tac +0 -0
  70. {tactus-0.22.0 → tactus-0.23.0}/examples/16-feature-toolsets-advanced.tac +0 -0
  71. {tactus-0.22.0 → tactus-0.23.0}/examples/17-feature-toolsets-dsl.tac +0 -0
  72. {tactus-0.22.0 → tactus-0.23.0}/examples/18-feature-lua-tools-individual.tac +0 -0
  73. {tactus-0.22.0 → tactus-0.23.0}/examples/18-feature-lua-tools-inline.tac +0 -0
  74. {tactus-0.22.0 → tactus-0.23.0}/examples/18-feature-lua-tools-toolset.tac +0 -0
  75. {tactus-0.22.0 → tactus-0.23.0}/examples/19-feature-direct-tool-calls.tac +0 -0
  76. {tactus-0.22.0 → tactus-0.23.0}/examples/20-bdd-complete.tac +0 -0
  77. {tactus-0.22.0 → tactus-0.23.0}/examples/20-bdd-complete.tac.bak +0 -0
  78. {tactus-0.22.0 → tactus-0.23.0}/examples/20-bdd-complete.tac.bak2 +0 -0
  79. {tactus-0.22.0 → tactus-0.23.0}/examples/21-bdd-passing.tac +0 -0
  80. {tactus-0.22.0 → tactus-0.23.0}/examples/21-bdd-passing.tac.bak +0 -0
  81. {tactus-0.22.0 → tactus-0.23.0}/examples/21-bdd-passing.tac.bak2 +0 -0
  82. {tactus-0.22.0 → tactus-0.23.0}/examples/30-eval-simple.tac +0 -0
  83. {tactus-0.22.0 → tactus-0.23.0}/examples/31-eval-demo.tac +0 -0
  84. {tactus-0.22.0 → tactus-0.23.0}/examples/32-eval-success-rate.tac +0 -0
  85. {tactus-0.22.0 → tactus-0.23.0}/examples/33-eval-thresholds.tac +0 -0
  86. {tactus-0.22.0 → tactus-0.23.0}/examples/34-eval-dataset.jsonl +0 -0
  87. {tactus-0.22.0 → tactus-0.23.0}/examples/34-eval-dataset.tac +0 -0
  88. {tactus-0.22.0 → tactus-0.23.0}/examples/35-eval-trace.tac +0 -0
  89. {tactus-0.22.0 → tactus-0.23.0}/examples/35-eval-trace.tac.bak +0 -0
  90. {tactus-0.22.0 → tactus-0.23.0}/examples/35-eval-trace.tac.bak2 +0 -0
  91. {tactus-0.22.0 → tactus-0.23.0}/examples/36-eval-advanced.tac +0 -0
  92. {tactus-0.22.0 → tactus-0.23.0}/examples/37-eval-comprehensive.tac +0 -0
  93. {tactus-0.22.0 → tactus-0.23.0}/examples/37-eval-comprehensive.tac.bak +0 -0
  94. {tactus-0.22.0 → tactus-0.23.0}/examples/37-eval-comprehensive.tac.bak2 +0 -0
  95. {tactus-0.22.0 → tactus-0.23.0}/examples/39-model-simple.tac +0 -0
  96. {tactus-0.22.0 → tactus-0.23.0}/examples/40-mcp-test.tac +0 -0
  97. {tactus-0.22.0 → tactus-0.23.0}/examples/40-model-text-classifier.tac +0 -0
  98. {tactus-0.22.0 → tactus-0.23.0}/examples/41-mcp-simple.tac +0 -0
  99. {tactus-0.22.0 → tactus-0.23.0}/examples/41-model-pytorch.tac +0 -0
  100. {tactus-0.22.0 → tactus-0.23.0}/examples/43-sub-procedure-simple.tac +0 -0
  101. {tactus-0.22.0 → tactus-0.23.0}/examples/44-sub-procedure-composition.tac +0 -0
  102. {tactus-0.22.0 → tactus-0.23.0}/examples/45-sub-procedure-recursive.tac +0 -0
  103. {tactus-0.22.0 → tactus-0.23.0}/examples/46-checkpoint-explicit.tac +0 -0
  104. {tactus-0.22.0 → tactus-0.23.0}/examples/47-checkpoint-expensive-ops.tac +0 -0
  105. {tactus-0.22.0 → tactus-0.23.0}/examples/48-script-mode-simple.tac +0 -0
  106. {tactus-0.22.0 → tactus-0.23.0}/examples/50-inputs-showcase.tac +0 -0
  107. {tactus-0.22.0 → tactus-0.23.0}/examples/51-inputs-calculator.tac +0 -0
  108. {tactus-0.22.0 → tactus-0.23.0}/examples/52-file-io-basics.tac +0 -0
  109. {tactus-0.22.0 → tactus-0.23.0}/examples/53-tsv-file-io.tac +0 -0
  110. {tactus-0.22.0 → tactus-0.23.0}/examples/54-json-file-io.tac +0 -0
  111. {tactus-0.22.0 → tactus-0.23.0}/examples/55-parquet-file-io.tac +0 -0
  112. {tactus-0.22.0 → tactus-0.23.0}/examples/56-hdf5-file-io.tac +0 -0
  113. {tactus-0.22.0 → tactus-0.23.0}/examples/57-excel-file-io.tac +0 -0
  114. {tactus-0.22.0 → tactus-0.23.0}/examples/58-text-file-io.tac +0 -0
  115. {tactus-0.22.0 → tactus-0.23.0}/examples/60-tool-sources.tac +0 -0
  116. {tactus-0.22.0 → tactus-0.23.0}/examples/61-inline-toolset-lua.tac +0 -0
  117. {tactus-0.22.0 → tactus-0.23.0}/examples/62-mcp-toolset-by-server.tac +0 -0
  118. {tactus-0.22.0 → tactus-0.23.0}/examples/63-toolset-import-from-file.tac +0 -0
  119. {tactus-0.22.0 → tactus-0.23.0}/examples/64-require-modules.tac +0 -0
  120. {tactus-0.22.0 → tactus-0.23.0}/examples/65-optional-state-demo.tac +0 -0
  121. {tactus-0.22.0 → tactus-0.23.0}/examples/70-mocking-static.tac +0 -0
  122. {tactus-0.22.0 → tactus-0.23.0}/examples/71-mocking-temporal.tac +0 -0
  123. {tactus-0.22.0 → tactus-0.23.0}/examples/72-mocking-conditional.tac +0 -0
  124. {tactus-0.22.0 → tactus-0.23.0}/examples/99-misc-test-loading.tac +0 -0
  125. {tactus-0.22.0 → tactus-0.23.0}/examples/README.md +0 -0
  126. {tactus-0.22.0 → tactus-0.23.0}/examples/app_config.ini +0 -0
  127. {tactus-0.22.0 → tactus-0.23.0}/examples/data/sample.csv +0 -0
  128. {tactus-0.22.0 → tactus-0.23.0}/examples/demo_output.json +0 -0
  129. {tactus-0.22.0 → tactus-0.23.0}/examples/helpers/math_module.tac +0 -0
  130. {tactus-0.22.0 → tactus-0.23.0}/examples/helpers/product.tac +0 -0
  131. {tactus-0.22.0 → tactus-0.23.0}/examples/helpers/string_module.tac +0 -0
  132. {tactus-0.22.0 → tactus-0.23.0}/examples/helpers/sum.tac +0 -0
  133. {tactus-0.22.0 → tactus-0.23.0}/examples/helpers/text_tools.tac +0 -0
  134. {tactus-0.22.0 → tactus-0.23.0}/examples/inventory_summary.tsv +0 -0
  135. {tactus-0.22.0 → tactus-0.23.0}/examples/mock-config.json +0 -0
  136. {tactus-0.22.0 → tactus-0.23.0}/examples/models/README.md +0 -0
  137. {tactus-0.22.0 → tactus-0.23.0}/examples/models/create_sentiment_model.py +0 -0
  138. {tactus-0.22.0 → tactus-0.23.0}/examples/output_summary.txt +0 -0
  139. {tactus-0.22.0 → tactus-0.23.0}/examples/tools/calculations.py +0 -0
  140. {tactus-0.22.0 → tactus-0.23.0}/examples/tools/data_analysis.py +0 -0
  141. {tactus-0.22.0 → tactus-0.23.0}/examples/tools/search.py +0 -0
  142. {tactus-0.22.0 → tactus-0.23.0}/examples/with_dependencies/README.md +0 -0
  143. {tactus-0.22.0 → tactus-0.23.0}/examples/with_dependencies/simple_http_test.tac +0 -0
  144. {tactus-0.22.0 → tactus-0.23.0}/examples/with_dependencies/time_lookup.tac +0 -0
  145. {tactus-0.22.0 → tactus-0.23.0}/features/01_state_management.feature +0 -0
  146. {tactus-0.22.0 → tactus-0.23.0}/features/02_checkpointing.feature +0 -0
  147. {tactus-0.22.0 → tactus-0.23.0}/features/03_human_in_the_loop.feature +0 -0
  148. {tactus-0.22.0 → tactus-0.23.0}/features/04_control_flow.feature +0 -0
  149. {tactus-0.22.0 → tactus-0.23.0}/features/05_tool_integration.feature +0 -0
  150. {tactus-0.22.0 → tactus-0.23.0}/features/06_retry_logic.feature +0 -0
  151. {tactus-0.22.0 → tactus-0.23.0}/features/07_file_operations.feature +0 -0
  152. {tactus-0.22.0 → tactus-0.23.0}/features/08_agent_primitives.feature +0 -0
  153. {tactus-0.22.0 → tactus-0.23.0}/features/09_workflow_execution.feature +0 -0
  154. {tactus-0.22.0 → tactus-0.23.0}/features/10_lua_integration.feature +0 -0
  155. {tactus-0.22.0 → tactus-0.23.0}/features/11_storage_backends.feature +0 -0
  156. {tactus-0.22.0 → tactus-0.23.0}/features/12_json_operations.feature +0 -0
  157. {tactus-0.22.0 → tactus-0.23.0}/features/13_logging.feature +0 -0
  158. {tactus-0.22.0 → tactus-0.23.0}/features/14_stage_and_step_tracking.feature +0 -0
  159. {tactus-0.22.0 → tactus-0.23.0}/features/15_procedure_calls.feature +0 -0
  160. {tactus-0.22.0 → tactus-0.23.0}/features/16_session_management.feature +0 -0
  161. {tactus-0.22.0 → tactus-0.23.0}/features/17_lua_dsl_validation.feature +0 -0
  162. {tactus-0.22.0 → tactus-0.23.0}/features/18_example_procedures.feature +0 -0
  163. {tactus-0.22.0 → tactus-0.23.0}/features/19_ide_server.feature +0 -0
  164. {tactus-0.22.0 → tactus-0.23.0}/features/20_parameters.feature +0 -0
  165. {tactus-0.22.0 → tactus-0.23.0}/features/21_outputs.feature +0 -0
  166. {tactus-0.22.0 → tactus-0.23.0}/features/23_prompts.feature +0 -0
  167. {tactus-0.22.0 → tactus-0.23.0}/features/24_bdd_specifications.feature +0 -0
  168. {tactus-0.22.0 → tactus-0.23.0}/features/25_bdd_custom_steps.feature +0 -0
  169. {tactus-0.22.0 → tactus-0.23.0}/features/26_bdd_evaluation.feature +0 -0
  170. {tactus-0.22.0 → tactus-0.23.0}/features/27_default_settings.feature +0 -0
  171. {tactus-0.22.0 → tactus-0.23.0}/features/28_custom_prompts.feature +0 -0
  172. {tactus-0.22.0 → tactus-0.23.0}/features/29_execution_settings.feature +0 -0
  173. {tactus-0.22.0 → tactus-0.23.0}/features/30_session_filters.feature +0 -0
  174. {tactus-0.22.0 → tactus-0.23.0}/features/31_matchers.feature +0 -0
  175. {tactus-0.22.0 → tactus-0.23.0}/features/32_result_object.feature +0 -0
  176. {tactus-0.22.0 → tactus-0.23.0}/features/33_output_type.feature +0 -0
  177. {tactus-0.22.0 → tactus-0.23.0}/features/42_model_primitive.feature +0 -0
  178. {tactus-0.22.0 → tactus-0.23.0}/features/43_sub_procedure_checkpointing.feature +0 -0
  179. {tactus-0.22.0 → tactus-0.23.0}/features/46_explicit_checkpoint.feature +0 -0
  180. {tactus-0.22.0 → tactus-0.23.0}/features/48_script_mode.feature +0 -0
  181. {tactus-0.22.0 → tactus-0.23.0}/features/51_dspy_lm_config.feature +0 -0
  182. {tactus-0.22.0 → tactus-0.23.0}/features/52_dspy_signature.feature +0 -0
  183. {tactus-0.22.0 → tactus-0.23.0}/features/53_dspy_module.feature +0 -0
  184. {tactus-0.22.0 → tactus-0.23.0}/features/54_dspy_history.feature +0 -0
  185. {tactus-0.22.0 → tactus-0.23.0}/features/55_dspy_prediction.feature +0 -0
  186. {tactus-0.22.0 → tactus-0.23.0}/features/56_dspy_agent.feature +0 -0
  187. {tactus-0.22.0 → tactus-0.23.0}/features/documentation/IDE_SERVER_BEHAVIOR.md +0 -0
  188. {tactus-0.22.0 → tactus-0.23.0}/features/documentation/Lua DSL/README.md +0 -0
  189. {tactus-0.22.0 → tactus-0.23.0}/features/environment.py +0 -0
  190. {tactus-0.22.0 → tactus-0.23.0}/features/steps/agent_primitives_steps.py +0 -0
  191. {tactus-0.22.0 → tactus-0.23.0}/features/steps/checkpointing_steps.py +0 -0
  192. {tactus-0.22.0 → tactus-0.23.0}/features/steps/control_flow_steps.py +0 -0
  193. {tactus-0.22.0 → tactus-0.23.0}/features/steps/dspy_agent_steps.py +0 -0
  194. {tactus-0.22.0 → tactus-0.23.0}/features/steps/dspy_history_steps.py +0 -0
  195. {tactus-0.22.0 → tactus-0.23.0}/features/steps/dspy_lm_steps.py +0 -0
  196. {tactus-0.22.0 → tactus-0.23.0}/features/steps/dspy_module_steps.py +0 -0
  197. {tactus-0.22.0 → tactus-0.23.0}/features/steps/dspy_prediction_steps.py +0 -0
  198. {tactus-0.22.0 → tactus-0.23.0}/features/steps/dspy_signature_steps.py +0 -0
  199. {tactus-0.22.0 → tactus-0.23.0}/features/steps/example_procedures_steps.py +0 -0
  200. {tactus-0.22.0 → tactus-0.23.0}/features/steps/file_operations_steps.py +0 -0
  201. {tactus-0.22.0 → tactus-0.23.0}/features/steps/human_in_the_loop_steps.py +0 -0
  202. {tactus-0.22.0 → tactus-0.23.0}/features/steps/ide_server_steps.py +0 -0
  203. {tactus-0.22.0 → tactus-0.23.0}/features/steps/json_operations_steps.py +0 -0
  204. {tactus-0.22.0 → tactus-0.23.0}/features/steps/logging_steps.py +0 -0
  205. {tactus-0.22.0 → tactus-0.23.0}/features/steps/lua_dsl_validation_steps.py +0 -0
  206. {tactus-0.22.0 → tactus-0.23.0}/features/steps/lua_integration_steps.py +0 -0
  207. {tactus-0.22.0 → tactus-0.23.0}/features/steps/mocking_steps.py +0 -0
  208. {tactus-0.22.0 → tactus-0.23.0}/features/steps/procedure_calls_steps.py +0 -0
  209. {tactus-0.22.0 → tactus-0.23.0}/features/steps/result_and_output_steps.py +0 -0
  210. {tactus-0.22.0 → tactus-0.23.0}/features/steps/retry_logic_steps.py +0 -0
  211. {tactus-0.22.0 → tactus-0.23.0}/features/steps/session_management_steps.py +0 -0
  212. {tactus-0.22.0 → tactus-0.23.0}/features/steps/stage_tracking_steps.py +0 -0
  213. {tactus-0.22.0 → tactus-0.23.0}/features/steps/state_management_steps.py +0 -0
  214. {tactus-0.22.0 → tactus-0.23.0}/features/steps/storage_backend_steps.py +0 -0
  215. {tactus-0.22.0 → tactus-0.23.0}/features/steps/support/__init__.py +0 -0
  216. {tactus-0.22.0 → tactus-0.23.0}/features/steps/tool_integration_steps.py +0 -0
  217. {tactus-0.22.0 → tactus-0.23.0}/features/steps/workflow_execution_steps.py +0 -0
  218. {tactus-0.22.0 → tactus-0.23.0}/scripts/audit_examples_mocking.py +0 -0
  219. {tactus-0.22.0 → tactus-0.23.0}/scripts/convert_examples.py +0 -0
  220. {tactus-0.22.0 → tactus-0.23.0}/start-web-ide.sh +0 -0
  221. {tactus-0.22.0 → tactus-0.23.0}/tactus/adapters/__init__.py +0 -0
  222. {tactus-0.22.0 → tactus-0.23.0}/tactus/adapters/cli_hitl.py +0 -0
  223. {tactus-0.22.0 → tactus-0.23.0}/tactus/adapters/cli_log.py +0 -0
  224. {tactus-0.22.0 → tactus-0.23.0}/tactus/adapters/file_storage.py +0 -0
  225. {tactus-0.22.0 → tactus-0.23.0}/tactus/adapters/http_callback_log.py +0 -0
  226. {tactus-0.22.0 → tactus-0.23.0}/tactus/adapters/ide_log.py +0 -0
  227. {tactus-0.22.0 → tactus-0.23.0}/tactus/adapters/lua_tools.py +0 -0
  228. {tactus-0.22.0 → tactus-0.23.0}/tactus/adapters/mcp.py +0 -0
  229. {tactus-0.22.0 → tactus-0.23.0}/tactus/adapters/mcp_manager.py +0 -0
  230. {tactus-0.22.0 → tactus-0.23.0}/tactus/adapters/memory.py +0 -0
  231. {tactus-0.22.0 → tactus-0.23.0}/tactus/adapters/plugins.py +0 -0
  232. {tactus-0.22.0 → tactus-0.23.0}/tactus/backends/http_backend.py +0 -0
  233. {tactus-0.22.0 → tactus-0.23.0}/tactus/backends/model_backend.py +0 -0
  234. {tactus-0.22.0 → tactus-0.23.0}/tactus/backends/pytorch_backend.py +0 -0
  235. {tactus-0.22.0 → tactus-0.23.0}/tactus/cli/__init__.py +0 -0
  236. {tactus-0.22.0 → tactus-0.23.0}/tactus/cli/app.py +0 -0
  237. {tactus-0.22.0 → tactus-0.23.0}/tactus/cli/commands/__init__.py +0 -0
  238. {tactus-0.22.0 → tactus-0.23.0}/tactus/core/__init__.py +0 -0
  239. {tactus-0.22.0 → tactus-0.23.0}/tactus/core/config_manager.py +0 -0
  240. {tactus-0.22.0 → tactus-0.23.0}/tactus/core/dependencies/__init__.py +0 -0
  241. {tactus-0.22.0 → tactus-0.23.0}/tactus/core/dependencies/registry.py +0 -0
  242. {tactus-0.22.0 → tactus-0.23.0}/tactus/core/dsl_stubs.py +0 -0
  243. {tactus-0.22.0 → tactus-0.23.0}/tactus/core/exceptions.py +0 -0
  244. {tactus-0.22.0 → tactus-0.23.0}/tactus/core/execution_context.py +0 -0
  245. {tactus-0.22.0 → tactus-0.23.0}/tactus/core/lua_sandbox.py +0 -0
  246. {tactus-0.22.0 → tactus-0.23.0}/tactus/core/message_history_manager.py +0 -0
  247. {tactus-0.22.0 → tactus-0.23.0}/tactus/core/mocking.py +0 -0
  248. {tactus-0.22.0 → tactus-0.23.0}/tactus/core/output_validator.py +0 -0
  249. {tactus-0.22.0 → tactus-0.23.0}/tactus/core/registry.py +0 -0
  250. {tactus-0.22.0 → tactus-0.23.0}/tactus/core/runtime.py +0 -0
  251. {tactus-0.22.0 → tactus-0.23.0}/tactus/core/template_resolver.py +0 -0
  252. {tactus-0.22.0 → tactus-0.23.0}/tactus/core/yaml_parser.py +0 -0
  253. {tactus-0.22.0 → tactus-0.23.0}/tactus/docker/Dockerfile +0 -0
  254. {tactus-0.22.0 → tactus-0.23.0}/tactus/docker/entrypoint.sh +0 -0
  255. {tactus-0.22.0 → tactus-0.23.0}/tactus/dspy/__init__.py +0 -0
  256. {tactus-0.22.0 → tactus-0.23.0}/tactus/dspy/history.py +0 -0
  257. {tactus-0.22.0 → tactus-0.23.0}/tactus/dspy/prediction.py +0 -0
  258. {tactus-0.22.0 → tactus-0.23.0}/tactus/dspy/signature.py +0 -0
  259. {tactus-0.22.0 → tactus-0.23.0}/tactus/ide/__init__.py +0 -0
  260. {tactus-0.22.0 → tactus-0.23.0}/tactus/ide/coding_assistant.py +0 -0
  261. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/__init__.py +0 -0
  262. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/control.py +0 -0
  263. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/file.py +0 -0
  264. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/handles.py +0 -0
  265. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/human.py +0 -0
  266. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/json.py +0 -0
  267. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/log.py +0 -0
  268. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/message_history.py +0 -0
  269. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/model.py +0 -0
  270. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/procedure.py +0 -0
  271. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/procedure_callable.py +0 -0
  272. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/retry.py +0 -0
  273. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/session.py +0 -0
  274. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/stage.py +0 -0
  275. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/state.py +0 -0
  276. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/step.py +0 -0
  277. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/system.py +0 -0
  278. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/tool.py +0 -0
  279. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/tool_handle.py +0 -0
  280. {tactus-0.22.0 → tactus-0.23.0}/tactus/primitives/toolset.py +0 -0
  281. {tactus-0.22.0 → tactus-0.23.0}/tactus/protocols/__init__.py +0 -0
  282. {tactus-0.22.0 → tactus-0.23.0}/tactus/protocols/chat_recorder.py +0 -0
  283. {tactus-0.22.0 → tactus-0.23.0}/tactus/protocols/config.py +0 -0
  284. {tactus-0.22.0 → tactus-0.23.0}/tactus/protocols/cost.py +0 -0
  285. {tactus-0.22.0 → tactus-0.23.0}/tactus/protocols/hitl.py +0 -0
  286. {tactus-0.22.0 → tactus-0.23.0}/tactus/protocols/log_handler.py +0 -0
  287. {tactus-0.22.0 → tactus-0.23.0}/tactus/protocols/models.py +0 -0
  288. {tactus-0.22.0 → tactus-0.23.0}/tactus/protocols/result.py +0 -0
  289. {tactus-0.22.0 → tactus-0.23.0}/tactus/protocols/storage.py +0 -0
  290. {tactus-0.22.0 → tactus-0.23.0}/tactus/providers/__init__.py +0 -0
  291. {tactus-0.22.0 → tactus-0.23.0}/tactus/providers/base.py +0 -0
  292. {tactus-0.22.0 → tactus-0.23.0}/tactus/providers/bedrock.py +0 -0
  293. {tactus-0.22.0 → tactus-0.23.0}/tactus/providers/google.py +0 -0
  294. {tactus-0.22.0 → tactus-0.23.0}/tactus/providers/openai.py +0 -0
  295. {tactus-0.22.0 → tactus-0.23.0}/tactus/sandbox/__init__.py +0 -0
  296. {tactus-0.22.0 → tactus-0.23.0}/tactus/sandbox/config.py +0 -0
  297. {tactus-0.22.0 → tactus-0.23.0}/tactus/sandbox/entrypoint.py +0 -0
  298. {tactus-0.22.0 → tactus-0.23.0}/tactus/sandbox/protocol.py +0 -0
  299. {tactus-0.22.0 → tactus-0.23.0}/tactus/stdlib/__init__.py +0 -0
  300. {tactus-0.22.0 → tactus-0.23.0}/tactus/stdlib/io/__init__.py +0 -0
  301. {tactus-0.22.0 → tactus-0.23.0}/tactus/stdlib/io/csv.py +0 -0
  302. {tactus-0.22.0 → tactus-0.23.0}/tactus/stdlib/io/excel.py +0 -0
  303. {tactus-0.22.0 → tactus-0.23.0}/tactus/stdlib/io/file.py +0 -0
  304. {tactus-0.22.0 → tactus-0.23.0}/tactus/stdlib/io/hdf5.py +0 -0
  305. {tactus-0.22.0 → tactus-0.23.0}/tactus/stdlib/io/json.py +0 -0
  306. {tactus-0.22.0 → tactus-0.23.0}/tactus/stdlib/io/parquet.py +0 -0
  307. {tactus-0.22.0 → tactus-0.23.0}/tactus/stdlib/io/tsv.py +0 -0
  308. {tactus-0.22.0 → tactus-0.23.0}/tactus/stdlib/loader.py +0 -0
  309. {tactus-0.22.0 → tactus-0.23.0}/tactus/stdlib/tac/tactus/tools/done.tac +0 -0
  310. {tactus-0.22.0 → tactus-0.23.0}/tactus/stdlib/tac/tactus/tools/log.tac +0 -0
  311. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/README.md +0 -0
  312. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/__init__.py +0 -0
  313. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/behave_integration.py +0 -0
  314. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/context.py +0 -0
  315. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/eval_models.py +0 -0
  316. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/evaluation_runner.py +0 -0
  317. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/evaluators.py +0 -0
  318. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/events.py +0 -0
  319. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/gherkin_parser.py +0 -0
  320. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/mock_agent.py +0 -0
  321. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/mock_dependencies.py +0 -0
  322. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/mock_hitl.py +0 -0
  323. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/mock_registry.py +0 -0
  324. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/mock_tools.py +0 -0
  325. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/models.py +0 -0
  326. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/pydantic_eval_runner.py +0 -0
  327. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/steps/__init__.py +0 -0
  328. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/steps/builtin.py +0 -0
  329. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/steps/custom.py +0 -0
  330. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/steps/registry.py +0 -0
  331. {tactus-0.22.0 → tactus-0.23.0}/tactus/testing/test_runner.py +0 -0
  332. {tactus-0.22.0 → tactus-0.23.0}/tactus/tracing/__init__.py +0 -0
  333. {tactus-0.22.0 → tactus-0.23.0}/tactus/tracing/trace_manager.py +0 -0
  334. {tactus-0.22.0 → tactus-0.23.0}/tactus/utils/__init__.py +0 -0
  335. {tactus-0.22.0 → tactus-0.23.0}/tactus/utils/cost_calculator.py +0 -0
  336. {tactus-0.22.0 → tactus-0.23.0}/tactus/utils/model_pricing.py +0 -0
  337. {tactus-0.22.0 → tactus-0.23.0}/tactus/utils/safe_file_library.py +0 -0
  338. {tactus-0.22.0 → tactus-0.23.0}/tactus/utils/safe_libraries.py +0 -0
  339. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/LuaLexerBase.py +0 -0
  340. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/LuaParserBase.py +0 -0
  341. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/README.md +0 -0
  342. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/__init__.py +0 -0
  343. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/error_listener.py +0 -0
  344. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/generated/LuaLexer.interp +0 -0
  345. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/generated/LuaLexer.py +0 -0
  346. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/generated/LuaLexer.tokens +0 -0
  347. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/generated/LuaLexerBase.py +0 -0
  348. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/generated/LuaParser.interp +0 -0
  349. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/generated/LuaParser.py +0 -0
  350. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/generated/LuaParser.tokens +0 -0
  351. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/generated/LuaParserBase.py +0 -0
  352. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/generated/LuaParserVisitor.py +0 -0
  353. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/generated/__init__.py +0 -0
  354. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/grammar/LuaLexer.g4 +0 -0
  355. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/grammar/LuaParser.g4 +0 -0
  356. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/semantic_visitor.py +0 -0
  357. {tactus-0.22.0 → tactus-0.23.0}/tactus/validation/validator.py +0 -0
  358. {tactus-0.22.0 → tactus-0.23.0}/tactus-desktop/.gitignore +0 -0
  359. {tactus-0.22.0 → tactus-0.23.0}/tactus-desktop/README.md +0 -0
  360. {tactus-0.22.0 → tactus-0.23.0}/tactus-desktop/RUN_ELECTRON.md +0 -0
  361. {tactus-0.22.0 → tactus-0.23.0}/tactus-desktop/SETUP_COMPLETE.md +0 -0
  362. {tactus-0.22.0 → tactus-0.23.0}/tactus-desktop/backend/hook-lupa.py +0 -0
  363. {tactus-0.22.0 → tactus-0.23.0}/tactus-desktop/backend/tactus_backend.spec +0 -0
  364. {tactus-0.22.0 → tactus-0.23.0}/tactus-desktop/package-lock.json +0 -0
  365. {tactus-0.22.0 → tactus-0.23.0}/tactus-desktop/package.json +0 -0
  366. {tactus-0.22.0 → tactus-0.23.0}/tactus-desktop/preload/preload.ts +0 -0
  367. {tactus-0.22.0 → tactus-0.23.0}/tactus-desktop/preload/tsconfig.json +0 -0
  368. {tactus-0.22.0 → tactus-0.23.0}/tactus-desktop/rebuild-and-test.sh +0 -0
  369. {tactus-0.22.0 → tactus-0.23.0}/tactus-desktop/scripts/build-backend.js +0 -0
  370. {tactus-0.22.0 → tactus-0.23.0}/tactus-desktop/scripts/build-frontend.js +0 -0
  371. {tactus-0.22.0 → tactus-0.23.0}/tactus-desktop/src/backend-manager.ts +0 -0
  372. {tactus-0.22.0 → tactus-0.23.0}/tactus-desktop/src/main.ts +0 -0
  373. {tactus-0.22.0 → tactus-0.23.0}/tactus-desktop/src/menu.ts +0 -0
  374. {tactus-0.22.0 → tactus-0.23.0}/tactus-desktop/tsconfig.json +0 -0
  375. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/ARCHITECTURE.md +0 -0
  376. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/CHANGELOG.md +0 -0
  377. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/DEV_MODE.md +0 -0
  378. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/QUICK_START.md +0 -0
  379. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/README.md +0 -0
  380. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/RESTART_INSTRUCTIONS.md +0 -0
  381. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/TROUBLESHOOTING.md +0 -0
  382. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/backend/README.md +0 -0
  383. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/backend/events.py +0 -0
  384. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/backend/logging_capture.py +0 -0
  385. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/backend/lsp_server.py +0 -0
  386. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/backend/requirements.txt +0 -0
  387. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/backend/tactus_lsp_handler.py +0 -0
  388. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/backend/test_lsp_server.py +0 -0
  389. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/dev.sh +0 -0
  390. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/.storybook/main.ts +0 -0
  391. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/.storybook/preview.ts +0 -0
  392. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/.storybook/vitest.setup.ts +0 -0
  393. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/README.md +0 -0
  394. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/demo.ts +0 -0
  395. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/index.html +0 -0
  396. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/jest.config.js +0 -0
  397. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/package.json +0 -0
  398. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/postcss.config.js +0 -0
  399. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/Editor.tsx +0 -0
  400. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/LSPClient.ts +0 -0
  401. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/LSPClientHTTP.ts +0 -0
  402. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/TactusLanguage.ts +0 -0
  403. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/commands/registry.ts +0 -0
  404. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/CheckpointSummary.tsx +0 -0
  405. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/CollapsibleRun.tsx +0 -0
  406. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/Duration.stories.tsx +0 -0
  407. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/Duration.tsx +0 -0
  408. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/FileTree.stories.tsx +0 -0
  409. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/FileTree.tsx +0 -0
  410. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/MessageFeed.tsx +0 -0
  411. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ProcedureInputsDisplay.stories.tsx +0 -0
  412. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ProcedureInputsDisplay.tsx +0 -0
  413. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ProcedureInputsModal.stories.tsx +0 -0
  414. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ProcedureInputsModal.tsx +0 -0
  415. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ProcedureTab.stories.tsx +0 -0
  416. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ProcedureTab.tsx +0 -0
  417. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ResizeHandle.tsx +0 -0
  418. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ResultsSidebar.stories.tsx +0 -0
  419. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/TestOptionsModal.tsx +0 -0
  420. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/Timestamp.stories.tsx +0 -0
  421. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/Timestamp.tsx +0 -0
  422. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/TopMenuBar.stories.tsx +0 -0
  423. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/debugger/CheckpointDetails.stories.tsx +0 -0
  424. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/debugger/CheckpointDetails.tsx +0 -0
  425. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/debugger/CheckpointList.stories.tsx +0 -0
  426. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/debugger/CheckpointList.tsx +0 -0
  427. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/debugger/DebuggerPanel.stories.tsx +0 -0
  428. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/debugger/DebuggerPanel.tsx +0 -0
  429. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/debugger/RunSelector.stories.tsx +0 -0
  430. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/debugger/RunSelector.tsx +0 -0
  431. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/debugger/StatisticsPanel.stories.tsx +0 -0
  432. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/debugger/StatisticsPanel.tsx +0 -0
  433. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/AgentStreamingComponent.tsx +0 -0
  434. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/BaseEventComponent.tsx +0 -0
  435. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/CheckpointEventComponent.tsx +0 -0
  436. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/CollapsibleTestScenario.tsx +0 -0
  437. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/ContainerStatusEventComponent.tsx +0 -0
  438. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/CostEventComponent.stories.tsx +0 -0
  439. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/CostEventComponent.tsx +0 -0
  440. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/EvaluationEventComponent.stories.tsx +0 -0
  441. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/EvaluationEventComponent.tsx +0 -0
  442. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/EventRenderer.tsx +0 -0
  443. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/ExecutionEventComponent.stories.tsx +0 -0
  444. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/ExecutionEventComponent.tsx +0 -0
  445. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/ExecutionSummaryEventComponent.stories.tsx +0 -0
  446. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/ExecutionSummaryEventComponent.tsx +0 -0
  447. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/LoadingEventComponent.stories.tsx +0 -0
  448. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/LoadingEventComponent.tsx +0 -0
  449. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/LogCluster.tsx +0 -0
  450. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/LogEventComponent.stories.tsx +0 -0
  451. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/LogEventComponent.tsx +0 -0
  452. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/OutputEventComponent.stories.tsx +0 -0
  453. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/OutputEventComponent.tsx +0 -0
  454. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/TestEventComponent.stories.tsx +0 -0
  455. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/TestEventComponent.tsx +0 -0
  456. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/TestProgressContainer.tsx +0 -0
  457. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/ToolCallEventComponent.tsx +0 -0
  458. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/ValidationEventComponent.stories.tsx +0 -0
  459. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/events/ValidationEventComponent.tsx +0 -0
  460. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/metadata/AgentsSection.tsx +0 -0
  461. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/metadata/EvaluationsSection.tsx +0 -0
  462. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/metadata/MetadataSections.stories.tsx +0 -0
  463. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/metadata/OutputsSection.tsx +0 -0
  464. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/metadata/ParametersSection.tsx +0 -0
  465. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/metadata/SpecificationsSection.tsx +0 -0
  466. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/metadata/StagesSection.tsx +0 -0
  467. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/metadata/ToolsSection.tsx +0 -0
  468. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/scenarios/EvaluateScenarios.stories.tsx +0 -0
  469. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/scenarios/RunScenarios.stories.tsx +0 -0
  470. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/scenarios/TestScenarios.stories.tsx +0 -0
  471. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/scenarios/ValidationScenarios.stories.tsx +0 -0
  472. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/theme-provider.tsx +0 -0
  473. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ui/ai/conversation.tsx +0 -0
  474. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ui/ai/message.tsx +0 -0
  475. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ui/ai/prompt-input.tsx +0 -0
  476. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ui/button.tsx +0 -0
  477. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ui/dialog.tsx +0 -0
  478. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ui/input.tsx +0 -0
  479. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ui/logo.stories.tsx +0 -0
  480. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ui/logo.tsx +0 -0
  481. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ui/menubar.tsx +0 -0
  482. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ui/scroll-area.tsx +0 -0
  483. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ui/separator.tsx +0 -0
  484. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/components/ui/tabs.tsx +0 -0
  485. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/hooks/useEventStream.ts +0 -0
  486. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/hooks/useTracing.ts +0 -0
  487. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/index.css +0 -0
  488. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/lib/utils.ts +0 -0
  489. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/main.tsx +0 -0
  490. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/types/events.ts +0 -0
  491. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/types/metadata.ts +0 -0
  492. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/types/results.ts +0 -0
  493. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/types/tracing.ts +0 -0
  494. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/validation/TactusValidator.ts +0 -0
  495. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/validation/generated/LuaParser.interp +0 -0
  496. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/validation/generated/LuaParser.tokens +0 -0
  497. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/validation/types.ts +0 -0
  498. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/src/vite-env.d.ts +0 -0
  499. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/tailwind.config.js +0 -0
  500. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/tsconfig.json +0 -0
  501. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/tsconfig.node.json +0 -0
  502. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/vite.config.ts +0 -0
  503. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/frontend/vitest.shims.d.ts +0 -0
  504. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/package.json +0 -0
  505. {tactus-0.22.0 → tactus-0.23.0}/tactus-ide/start-dev.sh +0 -0
  506. {tactus-0.22.0 → tactus-0.23.0}/tests/__init__.py +0 -0
  507. {tactus-0.22.0 → tactus-0.23.0}/tests/adapters/__init__.py +0 -0
  508. {tactus-0.22.0 → tactus-0.23.0}/tests/adapters/test_lua_tools_adapter.py +0 -0
  509. {tactus-0.22.0 → tactus-0.23.0}/tests/adapters/test_plugins.py +0 -0
  510. {tactus-0.22.0 → tactus-0.23.0}/tests/cli/__init__.py +0 -0
  511. {tactus-0.22.0 → tactus-0.23.0}/tests/cli/test_cli.py +0 -0
  512. {tactus-0.22.0 → tactus-0.23.0}/tests/cli/test_cli_inputs.py +0 -0
  513. {tactus-0.22.0 → tactus-0.23.0}/tests/conftest.py +0 -0
  514. {tactus-0.22.0 → tactus-0.23.0}/tests/core/__init__.py +0 -0
  515. {tactus-0.22.0 → tactus-0.23.0}/tests/core/test_config_manager.py +0 -0
  516. {tactus-0.22.0 → tactus-0.23.0}/tests/core/test_determinism_safety.py +0 -0
  517. {tactus-0.22.0 → tactus-0.23.0}/tests/core/test_lua_sandbox_security.py +0 -0
  518. {tactus-0.22.0 → tactus-0.23.0}/tests/core/test_runtime_inputs.py +0 -0
  519. {tactus-0.22.0 → tactus-0.23.0}/tests/core/test_script_mode.py +0 -0
  520. {tactus-0.22.0 → tactus-0.23.0}/tests/dspy/__init__.py +0 -0
  521. {tactus-0.22.0 → tactus-0.23.0}/tests/dspy/test_module_parameter.py +0 -0
  522. {tactus-0.22.0 → tactus-0.23.0}/tests/dspy/test_streaming.py +0 -0
  523. {tactus-0.22.0 → tactus-0.23.0}/tests/fixtures/__init__.py +0 -0
  524. {tactus-0.22.0 → tactus-0.23.0}/tests/fixtures/test_mcp_server.py +0 -0
  525. {tactus-0.22.0 → tactus-0.23.0}/tests/integration/test_named_procedures.py +0 -0
  526. {tactus-0.22.0 → tactus-0.23.0}/tests/mocks/__init__.py +0 -0
  527. {tactus-0.22.0 → tactus-0.23.0}/tests/mocks/llm_mocks.py +0 -0
  528. {tactus-0.22.0 → tactus-0.23.0}/tests/primitives/test_checkpoint_primitive.py +0 -0
  529. {tactus-0.22.0 → tactus-0.23.0}/tests/primitives/test_retry_primitive.py +0 -0
  530. {tactus-0.22.0 → tactus-0.23.0}/tests/primitives/test_state_primitive.py +0 -0
  531. {tactus-0.22.0 → tactus-0.23.0}/tests/primitives/test_system_alert.py +0 -0
  532. {tactus-0.22.0 → tactus-0.23.0}/tests/primitives/test_tool_primitive.py +0 -0
  533. {tactus-0.22.0 → tactus-0.23.0}/tests/primitives/test_toolset_dsl.py +0 -0
  534. {tactus-0.22.0 → tactus-0.23.0}/tests/stdlib/__init__.py +0 -0
  535. {tactus-0.22.0 → tactus-0.23.0}/tests/stdlib/test_loader.py +0 -0
  536. {tactus-0.22.0 → tactus-0.23.0}/tests/stdlib/test_require_python.py +0 -0
  537. {tactus-0.22.0 → tactus-0.23.0}/tests/test_checkpoints_integration.py +0 -0
  538. {tactus-0.22.0 → tactus-0.23.0}/tests/test_mcp_integration.py +0 -0
  539. {tactus-0.22.0 → tactus-0.23.0}/tests/test_tracing.py +0 -0
  540. {tactus-0.22.0 → tactus-0.23.0}/tests/testing/__init__.py +0 -0
  541. {tactus-0.22.0 → tactus-0.23.0}/tests/testing/conftest.py +0 -0
  542. {tactus-0.22.0 → tactus-0.23.0}/tests/testing/test_all_examples.py +0 -0
  543. {tactus-0.22.0 → tactus-0.23.0}/tests/testing/test_e2e.py +0 -0
  544. {tactus-0.22.0 → tactus-0.23.0}/tests/testing/test_gherkin_parser.py +0 -0
  545. {tactus-0.22.0 → tactus-0.23.0}/tests/testing/test_integration.py +0 -0
  546. {tactus-0.22.0 → tactus-0.23.0}/tests/testing/test_models.py +0 -0
  547. {tactus-0.22.0 → tactus-0.23.0}/tests/testing/test_runtime_integration.py +0 -0
  548. {tactus-0.22.0 → tactus-0.23.0}/tests/testing/test_step_registry.py +0 -0
  549. {tactus-0.22.0 → tactus-0.23.0}/tests/utils/__init__.py +0 -0
  550. {tactus-0.22.0 → tactus-0.23.0}/tests/utils/test_safe_file_library.py +0 -0
  551. {tactus-0.22.0 → tactus-0.23.0}/tests/validation/__init__.py +0 -0
@@ -2,6 +2,14 @@
2
2
 
3
3
  <!-- version list -->
4
4
 
5
+ ## v0.23.0 (2026-01-11)
6
+
7
+ ### Features
8
+
9
+ - Add AI chat assistant to Tactus IDE with streaming and tool execution
10
+ ([`81f544a`](https://github.com/AnthusAI/Tactus/commit/81f544a16e86a8f26b328583b432ad2f1ad26504))
11
+
12
+
5
13
  ## v0.22.0 (2026-01-10)
6
14
 
7
15
  ### Features
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tactus
3
- Version: 0.22.0
3
+ Version: 0.23.0
4
4
  Summary: Tactus: Lua-based DSL for agentic workflows
5
5
  Project-URL: Homepage, https://github.com/AnthusAI/Tactus
6
6
  Project-URL: Documentation, https://github.com/AnthusAI/Tactus/tree/main/docs
@@ -1633,6 +1633,72 @@ bedrock_agent = Agent {
1633
1633
  }
1634
1634
  ```
1635
1635
 
1636
+ ### Module Configuration
1637
+
1638
+ Agents can specify which DSPy module strategy to use via the `module` parameter. This controls how prompts are formatted and whether reasoning steps are included.
1639
+
1640
+ **Default module** (Predict):
1641
+
1642
+ ```lua
1643
+ local done = require("tactus.tools.done")
1644
+
1645
+ simple_agent = Agent {
1646
+ provider = "openai",
1647
+ model = "gpt-4o-mini",
1648
+ system_prompt = "You are a helpful assistant.",
1649
+ tools = {done}
1650
+ }
1651
+ ```
1652
+
1653
+ **ChainOfThought module** (adds reasoning steps):
1654
+
1655
+ ```lua
1656
+ local done = require("tactus.tools.done")
1657
+
1658
+ thinking_agent = Agent {
1659
+ provider = "openai",
1660
+ model = "gpt-4o",
1661
+ module = "ChainOfThought",
1662
+ system_prompt = "You are a careful analyst.",
1663
+ tools = {done}
1664
+ }
1665
+ ```
1666
+
1667
+ **Raw module** (minimal formatting for cost optimization):
1668
+
1669
+ ```lua
1670
+ local done = require("tactus.tools.done")
1671
+
1672
+ efficient_agent = Agent {
1673
+ provider = "openai",
1674
+ model = "gpt-4o-mini",
1675
+ module = "Raw",
1676
+ system_prompt = "You are concise.",
1677
+ tools = {done}
1678
+ }
1679
+ ```
1680
+
1681
+ **Available module options:**
1682
+
1683
+ - **`"Predict"`** (default): Simple prediction without reasoning traces. Uses DSPy's standard field delimiters (~300-400 characters overhead per call).
1684
+
1685
+ - **`"ChainOfThought"`**: Adds step-by-step reasoning before generating the final response. Useful for complex tasks requiring explicit reasoning. Increases token usage due to reasoning output (~500-2000 additional tokens depending on complexity).
1686
+
1687
+ - **`"Raw"`**: Minimal formatting with direct LM calls. No DSPy delimiter overhead. Best for simple interactions, cost optimization, or when prompt space is constrained.
1688
+
1689
+ **Token overhead comparison:**
1690
+
1691
+ For a simple "Hello, World!" interaction:
1692
+ - **Raw**: 29 tokens total (20 prompt + 9 completion)
1693
+ - **Predict**: 230 tokens total (210 prompt + 19 completion)
1694
+ - **Difference**: ~8x more tokens with Predict due to delimiter formatting
1695
+
1696
+ **When to use each module:**
1697
+
1698
+ - Use **`Raw`** for simple interactions, high-volume API calls, or when minimizing cost is a priority
1699
+ - Use **`Predict`** when you need structured outputs or are using DSPy's optimization features (bootstrapping, etc.)
1700
+ - Use **`ChainOfThought`** for complex reasoning tasks where you want to see the agent's thought process
1701
+
1636
1702
  ---
1637
1703
 
1638
1704
  ## DSPy Integration
@@ -0,0 +1,8 @@
1
+ World = Agent {
2
+ provider = "openai",
3
+ model = "gpt-4o-mini",
4
+ system_prompt = "Your name is World.",
5
+ module = "Raw" -- No DSPy delimiter formatting
6
+ }
7
+
8
+ return World("Hello, World!")
@@ -0,0 +1,11 @@
1
+ -- Test Raw module with minimal formatting
2
+ WorldRaw = Agent {
3
+ provider = "openai",
4
+ model = "gpt-4o-mini",
5
+ system_prompt = "Your name is World.",
6
+ module = "Raw" -- Use raw module for minimal overhead
7
+ }
8
+
9
+ return {
10
+ WorldRaw("Hello, World!")
11
+ }
@@ -0,0 +1,11 @@
1
+ -- Test Raw module streaming
2
+ Story = Agent {
3
+ provider = "openai",
4
+ model = "gpt-4o-mini",
5
+ system_prompt = "You are a storyteller.",
6
+ module = "Raw" -- Use raw module for minimal overhead
7
+ }
8
+
9
+ return {
10
+ Story("Tell me a very short story about a robot.")
11
+ }
@@ -0,0 +1,60 @@
1
+ Feature: Chat Assistant with File Tools
2
+ As a developer using the Tactus IDE
3
+ I want to chat with an AI assistant that can read files
4
+ So that I can get help understanding my codebase
5
+
6
+ Background:
7
+ Given a workspace at "examples/"
8
+ And the chat assistant is configured with:
9
+ | parameter | value |
10
+ | provider | openai |
11
+ | model | gpt-4o |
12
+ | temperature | 0.7 |
13
+
14
+ Scenario: Basic chat without tools
15
+ When I send the message "Hello"
16
+ Then the assistant should respond
17
+ And the response should contain text
18
+ And no tools should be called
19
+
20
+ Scenario: View a file
21
+ When I send the message "Show me 01-basics-hello-world.tac"
22
+ Then the assistant should call tool "str_replace_based_edit_tool"
23
+ And the tool should be called with:
24
+ | parameter | value |
25
+ | command | view |
26
+ | path | 01-basics-hello-world.tac |
27
+ And the tool result should contain line numbers
28
+ And the response should describe the file contents
29
+
30
+ Scenario: List directory contents
31
+ When I send the message "What files are in this directory?"
32
+ Then the assistant should call tool "str_replace_based_edit_tool"
33
+ And the tool should be called with:
34
+ | parameter | value |
35
+ | command | view |
36
+ | path | . |
37
+ And the tool result should show directories and files
38
+ And the response should list the files
39
+
40
+ Scenario: View specific line range
41
+ Given a workspace at "."
42
+ And the chat assistant is configured with:
43
+ | parameter | value |
44
+ | provider | openai |
45
+ | model | gpt-4o |
46
+ | temperature | 0.7 |
47
+ When I send the message "Show me lines 10-20 of tactus/validation/validator.py"
48
+ Then the assistant should call tool "str_replace_based_edit_tool"
49
+ And the tool should be called with:
50
+ | parameter | value |
51
+ | command | view |
52
+ | path | tactus/validation/validator.py |
53
+ | view_range | [10, 20] |
54
+ And the tool result should contain exactly 11 lines
55
+
56
+ Scenario: Security - reject path outside workspace
57
+ When I send the message "Show me /etc/passwd"
58
+ Then the assistant should call tool "str_replace_based_edit_tool"
59
+ And the tool result should contain "Error"
60
+ And the tool result should contain "outside workspace"
@@ -0,0 +1,160 @@
1
+ """
2
+ Step definitions for chat assistant feature.
3
+ """
4
+
5
+ from behave import given, when, then
6
+ from features.steps.support.harnesses import ChatAssistantHarness
7
+ import ast
8
+
9
+
10
+ @given('a workspace at "{path}"')
11
+ def step_workspace_at(context, path):
12
+ """Set up workspace for chat assistant."""
13
+ context.workspace_root = path
14
+ context.chat = ChatAssistantHarness(workspace_root=path)
15
+
16
+
17
+ @given("the chat assistant is configured with:")
18
+ def step_configure_assistant(context):
19
+ """Configure assistant with parameters from table."""
20
+ config = {row["parameter"]: row["value"] for row in context.table}
21
+ context.chat.configure(config)
22
+
23
+
24
+ @when('I send the message "{message}"')
25
+ def step_send_message(context, message):
26
+ """Send a message to the assistant."""
27
+ context.chat.send_message(message)
28
+
29
+
30
+ @then("the assistant should respond")
31
+ def step_assistant_responds(context):
32
+ """Verify assistant generated a response."""
33
+ assert context.chat.has_response(), "No response from assistant"
34
+
35
+
36
+ @then("the response should contain text")
37
+ def step_response_contains_text(context):
38
+ """Verify response has text content."""
39
+ response = context.chat.get_response()
40
+ assert response, "Response is empty"
41
+ assert len(response.strip()) > 0, "Response contains no text"
42
+
43
+
44
+ @then("no tools should be called")
45
+ def step_no_tools_called(context):
46
+ """Verify no tools were invoked."""
47
+ tool_calls = context.chat.get_tool_calls()
48
+ assert len(tool_calls) == 0, f"Expected no tool calls, but got: {tool_calls}"
49
+
50
+
51
+ @then('the assistant should call tool "{tool_name}"')
52
+ def step_tool_called(context, tool_name):
53
+ """Verify a specific tool was called."""
54
+ assert context.chat.tool_was_called(
55
+ tool_name
56
+ ), f"Tool '{tool_name}' was not called. Called: {context.chat.get_tool_calls()}"
57
+
58
+
59
+ @then("the tool should be called with:")
60
+ def step_tool_called_with(context):
61
+ """Verify tool was called with specific parameters."""
62
+ expected_params = {row["parameter"]: row["value"] for row in context.table}
63
+
64
+ # Get the last tool call
65
+ tool_calls = context.chat.get_tool_calls()
66
+ assert len(tool_calls) > 0, "No tool calls found"
67
+
68
+ last_call = tool_calls[-1]
69
+ actual_params = last_call["params"]
70
+
71
+ for param, expected_value in expected_params.items():
72
+ assert (
73
+ param in actual_params
74
+ ), f"Parameter '{param}' not found in tool call. Available: {actual_params.keys()}"
75
+
76
+ actual_value = actual_params[param]
77
+
78
+ # Handle list parameters (like view_range)
79
+ if expected_value.startswith("["):
80
+ expected_value = ast.literal_eval(expected_value)
81
+
82
+ assert (
83
+ actual_value == expected_value
84
+ ), f"Parameter '{param}': expected {expected_value}, got {actual_value}"
85
+
86
+
87
+ @then("the tool result should contain line numbers")
88
+ def step_tool_result_has_line_numbers(context):
89
+ """Verify tool result includes line numbers."""
90
+ tool_calls = context.chat.get_tool_calls()
91
+ assert len(tool_calls) > 0, "No tool calls found"
92
+
93
+ last_call = tool_calls[-1]
94
+ result = last_call["result"]
95
+
96
+ # Check for line number format: "1: content"
97
+ lines = result.split("\n")
98
+ has_line_numbers = any(
99
+ line.strip() and line.split(":")[0].strip().isdigit() for line in lines if ":" in line
100
+ )
101
+
102
+ assert has_line_numbers, f"Tool result does not contain line numbers:\n{result}"
103
+
104
+
105
+ @then("the response should describe the file contents")
106
+ def step_response_describes_file(context):
107
+ """Verify response mentions file contents."""
108
+ response = context.chat.get_response()
109
+ assert response, "No response from assistant"
110
+ # Basic check - response should be non-empty
111
+ assert len(response.strip()) > 0, "Response is empty"
112
+
113
+
114
+ @then("the tool result should show directories and files")
115
+ def step_tool_result_shows_dirs_and_files(context):
116
+ """Verify tool result shows directory listing."""
117
+ tool_calls = context.chat.get_tool_calls()
118
+ assert len(tool_calls) > 0, "No tool calls found"
119
+
120
+ last_call = tool_calls[-1]
121
+ result = last_call["result"]
122
+
123
+ # Check for [DIR] or [FILE] markers
124
+ has_markers = "[DIR]" in result or "[FILE]" in result
125
+ assert has_markers, f"Tool result does not show directory markers:\n{result}"
126
+
127
+
128
+ @then("the response should list the files")
129
+ def step_response_lists_files(context):
130
+ """Verify response mentions files."""
131
+ response = context.chat.get_response()
132
+ assert response, "No response from assistant"
133
+ assert len(response.strip()) > 0, "Response is empty"
134
+
135
+
136
+ @then("the tool result should contain exactly {count:d} lines")
137
+ def step_tool_result_line_count(context, count):
138
+ """Verify tool result has specific number of lines."""
139
+ tool_calls = context.chat.get_tool_calls()
140
+ assert len(tool_calls) > 0, "No tool calls found"
141
+
142
+ last_call = tool_calls[-1]
143
+ result = last_call["result"]
144
+
145
+ lines = [line for line in result.split("\n") if line.strip()]
146
+ actual_count = len(lines)
147
+
148
+ assert actual_count == count, f"Expected {count} lines, got {actual_count}:\n{result}"
149
+
150
+
151
+ @then('the tool result should contain "{text}"')
152
+ def step_tool_result_contains(context, text):
153
+ """Verify tool result contains specific text."""
154
+ tool_calls = context.chat.get_tool_calls()
155
+ assert len(tool_calls) > 0, "No tool calls found"
156
+
157
+ last_call = tool_calls[-1]
158
+ result = last_call["result"]
159
+
160
+ assert text in result, f"Tool result does not contain '{text}':\n{result}"
@@ -515,3 +515,161 @@ class FakeSessionStore:
515
515
  for session in self.sessions.values()
516
516
  if session.context.get("task_type") == task_type
517
517
  ]
518
+
519
+
520
+ # ---------------------------------------------------------------------------
521
+ # Chat assistant helpers
522
+ # ---------------------------------------------------------------------------
523
+
524
+
525
+ class ChatAssistantHarness:
526
+ """
527
+ Test harness for chat assistant that captures tool calls and responses.
528
+
529
+ Mocks the LLM to return deterministic tool calls based on message patterns,
530
+ but calls the actual tool implementations to verify they work correctly.
531
+ """
532
+
533
+ def __init__(self, workspace_root: str):
534
+ import os
535
+
536
+ # Convert to absolute path if relative
537
+ if not os.path.isabs(workspace_root):
538
+ # Resolve relative to project root (where behave is run from)
539
+ workspace_root = os.path.abspath(workspace_root)
540
+ self.workspace_root = workspace_root
541
+ self.config = {}
542
+ self.messages = []
543
+ self.tool_calls = []
544
+ self.response = None
545
+
546
+ def configure(self, config: Dict[str, Any]):
547
+ """Configure assistant (provider, model, etc.)"""
548
+ self.config = config
549
+
550
+ def send_message(self, message: str):
551
+ """
552
+ Send message and simulate assistant behavior.
553
+
554
+ For testing, we mock the LLM's decision-making but call real tools.
555
+ """
556
+ self.messages.append({"role": "user", "content": message})
557
+
558
+ # Mock LLM behavior based on message patterns
559
+ message_lower = message.lower()
560
+
561
+ if "hello" in message_lower and "show" not in message_lower:
562
+ # Simple greeting - no tools
563
+ self.response = "Hello! How can I help you today?"
564
+
565
+ elif "show me" in message_lower or "what files" in message_lower:
566
+ # File operation - simulate tool call
567
+ self._simulate_file_tool_call(message)
568
+
569
+ else:
570
+ # Default response
571
+ self.response = "I'm not sure how to help with that."
572
+
573
+ def _simulate_file_tool_call(self, message: str):
574
+ """
575
+ Simulate LLM deciding to call the file tool.
576
+
577
+ Parse the message to extract file path and determine command,
578
+ then call the actual tool implementation.
579
+ """
580
+ # Extract file path from message
581
+ # Simple pattern matching for testing
582
+ import re
583
+
584
+ # Check for line range first (more specific pattern)
585
+ view_range = None
586
+ range_match = re.search(r"lines (\d+)-(\d+) of (.+)", message.lower())
587
+ if range_match:
588
+ start = int(range_match.group(1))
589
+ end = int(range_match.group(2))
590
+ view_range = [start, end]
591
+ path = range_match.group(3).strip()
592
+ else:
593
+ # Pattern: "show me <path>" or "what files in <path>"
594
+ path_match = re.search(r"show me ([^\s]+)", message.lower())
595
+ if not path_match:
596
+ # Pattern: "what files are in this directory"
597
+ if "in this directory" in message.lower():
598
+ path = "."
599
+ else:
600
+ # Pattern: "what files are in the <path> directory"
601
+ path_match = re.search(r"in the ([^\s]+) directory", message.lower())
602
+ if path_match:
603
+ # Add trailing slash for directory
604
+ path = path_match.group(1) + "/"
605
+ else:
606
+ self.response = "I couldn't find a file path in your message."
607
+ return
608
+ else:
609
+ path = path_match.group(1)
610
+
611
+ # Determine if it's a directory or file
612
+ command = "view"
613
+
614
+ # Call the actual tool (will be implemented later)
615
+ try:
616
+ # For now, just record the call - tool implementation comes next
617
+ tool_result = self._call_tool(command, path, view_range)
618
+
619
+ self.tool_calls.append(
620
+ {
621
+ "tool": "str_replace_based_edit_tool",
622
+ "params": {
623
+ "command": command,
624
+ "path": path,
625
+ **({"view_range": view_range} if view_range else {}),
626
+ },
627
+ "result": tool_result,
628
+ }
629
+ )
630
+
631
+ # Generate response based on tool result
632
+ if "Error" in tool_result:
633
+ self.response = f"I encountered an error: {tool_result}"
634
+ else:
635
+ self.response = f"Here's what I found:\n\n{tool_result}"
636
+
637
+ except Exception as e:
638
+ self.response = f"Error calling tool: {str(e)}"
639
+
640
+ def _call_tool(self, command: str, path: str, view_range: Optional[List[int]] = None) -> str:
641
+ """
642
+ Call the actual tool implementation.
643
+ """
644
+ # Import the real tool
645
+ import sys
646
+ import os
647
+
648
+ backend_path = os.path.join(
649
+ os.path.dirname(__file__), "..", "..", "..", "tactus-ide", "backend"
650
+ )
651
+ if backend_path not in sys.path:
652
+ sys.path.insert(0, backend_path)
653
+
654
+ from text_editor_tool import str_replace_based_edit_tool
655
+
656
+ # Call the real tool
657
+ return str_replace_based_edit_tool(
658
+ workspace_root=self.workspace_root, command=command, path=path, view_range=view_range
659
+ )
660
+
661
+ def has_response(self) -> bool:
662
+ """Check if assistant generated a response."""
663
+ return self.response is not None
664
+
665
+ def get_response(self) -> str:
666
+ """Get the assistant's response."""
667
+ return self.response or ""
668
+
669
+ def get_tool_calls(self) -> List[Dict[str, Any]]:
670
+ """Get list of tool calls made."""
671
+ return self.tool_calls
672
+
673
+ def tool_was_called(self, tool_name: str) -> bool:
674
+ """Check if a specific tool was called."""
675
+ return any(call["tool"] == tool_name for call in self.tool_calls)
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "tactus"
7
- version = "0.22.0"
7
+ version = "0.23.0"
8
8
  description = "Tactus: Lua-based DSL for agentic workflows"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -5,7 +5,7 @@ Tactus provides a declarative workflow engine for AI agents with pluggable
5
5
  backends for storage, HITL, and chat recording.
6
6
  """
7
7
 
8
- __version__ = "0.22.0"
8
+ __version__ = "0.23.0"
9
9
 
10
10
  # Core exports
11
11
  from tactus.core.runtime import TactusRuntime
@@ -82,6 +82,7 @@ class DSPyAgentHandle:
82
82
  module: DSPy module type to use (default: "Predict"). Options:
83
83
  - "Predict": Simple pass-through prediction (no reasoning traces)
84
84
  - "ChainOfThought": Adds step-by-step reasoning before response
85
+ - "Raw": Minimal formatting, direct LM calls (lowest token overhead)
85
86
  initial_message: Initial message to send on first turn if no inject
86
87
  registry: Optional Registry instance for accessing mocks
87
88
  mock_manager: Optional MockManager instance for checking mocks
@@ -288,6 +289,7 @@ class DSPyAgentHandle:
288
289
  mapping = {
289
290
  "Predict": "predict",
290
291
  "ChainOfThought": "chain_of_thought",
292
+ "Raw": "raw",
291
293
  # Future modules can be added here:
292
294
  # "ReAct": "react",
293
295
  # "ProgramOfThought": "program_of_thought",
@@ -131,3 +131,69 @@ def reset_lm_configuration() -> None:
131
131
  _current_lm = None
132
132
  # Also reset DSPy's global configuration
133
133
  dspy.configure(lm=None)
134
+
135
+
136
+ def create_lm(
137
+ model: str,
138
+ api_key: Optional[str] = None,
139
+ api_base: Optional[str] = None,
140
+ temperature: float = 0.7,
141
+ max_tokens: Optional[int] = None,
142
+ model_type: Optional[str] = None,
143
+ **kwargs: Any,
144
+ ) -> dspy.LM:
145
+ """
146
+ Create a Language Model instance WITHOUT setting it as global default.
147
+
148
+ This is useful for creating LMs in async contexts where dspy.configure()
149
+ cannot be called (e.g., in different event loops or async tasks).
150
+
151
+ Use with dspy.context(lm=...) to set the LM for a specific scope:
152
+ lm = create_lm("openai/gpt-4o")
153
+ with dspy.context(lm=lm):
154
+ # Use DSPy operations here
155
+
156
+ Args:
157
+ model: Model identifier in LiteLLM format (e.g., "openai/gpt-4o")
158
+ api_key: API key (optional, can use environment variables)
159
+ api_base: Custom API base URL (optional)
160
+ temperature: Sampling temperature (default: 0.7)
161
+ max_tokens: Maximum tokens in response (optional)
162
+ model_type: Model type (e.g., "chat", "responses" for reasoning models)
163
+ **kwargs: Additional LiteLLM parameters
164
+
165
+ Returns:
166
+ dspy.LM instance (not configured globally)
167
+ """
168
+ # Validate model parameter
169
+ if model is None or not model:
170
+ raise ValueError("model is required for LM configuration")
171
+
172
+ if not isinstance(model, str) or not model.startswith(
173
+ ("openai/", "anthropic/", "bedrock/", "gemini/", "ollama/")
174
+ ):
175
+ # Check if it's at least formatted correctly
176
+ if "/" not in model:
177
+ raise ValueError(
178
+ f"Invalid model format: {model}. Expected format like 'provider/model-name'"
179
+ )
180
+
181
+ # Build configuration
182
+ lm_kwargs = {
183
+ "temperature": temperature,
184
+ # IMPORTANT: Disable caching to enable streaming
185
+ "cache": False,
186
+ **kwargs,
187
+ }
188
+
189
+ if api_key:
190
+ lm_kwargs["api_key"] = api_key
191
+ if api_base:
192
+ lm_kwargs["api_base"] = api_base
193
+ if max_tokens:
194
+ lm_kwargs["max_tokens"] = max_tokens
195
+ if model_type:
196
+ lm_kwargs["model_type"] = model_type
197
+
198
+ # Create LM without setting as global default
199
+ return dspy.LM(model, **lm_kwargs)