steerable-agent-runtime 0.6.73__tar.gz → 0.6.74__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 (163) hide show
  1. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/PKG-INFO +1 -1
  2. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/pyproject.toml +1 -1
  3. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/native_bridge.py +3 -1
  4. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/pseudo.py +97 -5
  5. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime.egg-info/PKG-INFO +1 -1
  6. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_stream_strip.py +65 -0
  7. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/README.md +0 -0
  8. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/setup.cfg +0 -0
  9. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/__init__.py +0 -0
  10. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/antihallucination.py +0 -0
  11. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/approval.py +0 -0
  12. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/approval_policy.py +0 -0
  13. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/ask_user.py +0 -0
  14. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/branch.py +0 -0
  15. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/cache_control.py +0 -0
  16. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/calibration.py +0 -0
  17. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/compaction.py +0 -0
  18. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/compaction_policy.py +0 -0
  19. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/config.py +0 -0
  20. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/default.harness.json +0 -0
  21. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/default.harness.yaml +0 -0
  22. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/delegation_gate.py +0 -0
  23. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/errors.py +0 -0
  24. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/gateway_catalog.py +0 -0
  25. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/handoff.py +0 -0
  26. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/harness.py +0 -0
  27. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/harness_spec.py +0 -0
  28. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/history.py +0 -0
  29. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/hooks.py +0 -0
  30. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/__init__.py +0 -0
  31. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/anthropic_native.py +0 -0
  32. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/compat.py +0 -0
  33. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/errors.py +0 -0
  34. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/google_genai.py +0 -0
  35. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/openai_compat.py +0 -0
  36. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/openai_responses.py +0 -0
  37. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/parts.py +0 -0
  38. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/presets.py +0 -0
  39. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/llm/system_proxy.py +0 -0
  40. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/loop.py +0 -0
  41. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/maintenance.py +0 -0
  42. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/mcp.py +0 -0
  43. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/mcp_server.py +0 -0
  44. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/model_catalog.py +0 -0
  45. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/model_info.py +0 -0
  46. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/model_resolve.py +0 -0
  47. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/observation_aging.py +0 -0
  48. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/orchestration.py +0 -0
  49. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/otel.py +0 -0
  50. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/plugins.py +0 -0
  51. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/pool.py +0 -0
  52. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/pricing.py +0 -0
  53. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/recording.py +0 -0
  54. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/reminders.py +0 -0
  55. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/replay.py +0 -0
  56. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/resume.py +0 -0
  57. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/retry.py +0 -0
  58. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/sandboxed.py +0 -0
  59. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/skills.py +0 -0
  60. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/spill.py +0 -0
  61. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/storage/__init__.py +0 -0
  62. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/storage/in_memory.py +0 -0
  63. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/storage/sqlalchemy_store.py +0 -0
  64. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/storage/sqlite_store.py +0 -0
  65. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/storage/write_lease.py +0 -0
  66. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/subagent.py +0 -0
  67. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/todo.py +0 -0
  68. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/tokens.py +0 -0
  69. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/tool_presentation.py +0 -0
  70. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/tool_schema.py +0 -0
  71. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/tool_search.py +0 -0
  72. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/tools.py +0 -0
  73. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/tracing.py +0 -0
  74. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/transport/__init__.py +0 -0
  75. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/transport/fastapi_sse.py +0 -0
  76. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/transport/stdio_jsonrpc.py +0 -0
  77. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime/world_state.py +0 -0
  78. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime.egg-info/SOURCES.txt +0 -0
  79. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime.egg-info/dependency_links.txt +0 -0
  80. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime.egg-info/requires.txt +0 -0
  81. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/src/steerable_agent_runtime.egg-info/top_level.txt +0 -0
  82. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_antihallucination.py +0 -0
  83. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_approval.py +0 -0
  84. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_approval_policy.py +0 -0
  85. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_ask_user.py +0 -0
  86. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_branch.py +0 -0
  87. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_cache_control.py +0 -0
  88. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_cache_instrumentation.py +0 -0
  89. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_calibration.py +0 -0
  90. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_compaction.py +0 -0
  91. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_compaction_policy.py +0 -0
  92. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_compaction_strategy.py +0 -0
  93. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_config.py +0 -0
  94. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_content_parts.py +0 -0
  95. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_delegation_gate.py +0 -0
  96. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_error_taxonomy.py +0 -0
  97. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_fragment_bounds.py +0 -0
  98. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_gateway_catalog.py +0 -0
  99. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_golden.py +0 -0
  100. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_handoff.py +0 -0
  101. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_harness.py +0 -0
  102. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_harness_spec.py +0 -0
  103. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_history.py +0 -0
  104. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_history_persistence.py +0 -0
  105. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_hooks.py +0 -0
  106. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_in_memory_storage.py +0 -0
  107. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_llm_gemini_wire.py +0 -0
  108. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_llm_responses_wire.py +0 -0
  109. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_llm_stream_e2e.py +0 -0
  110. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_llm_wire_helpers.py +0 -0
  111. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_long_session.py +0 -0
  112. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_loop.py +0 -0
  113. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_loop_cancellation.py +0 -0
  114. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_loop_replay.py +0 -0
  115. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_loop_sandbox_event.py +0 -0
  116. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_loop_terminal.py +0 -0
  117. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_maintenance.py +0 -0
  118. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_mcp.py +0 -0
  119. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_mcp_server.py +0 -0
  120. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_model_catalog.py +0 -0
  121. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_model_equivalence.py +0 -0
  122. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_model_info.py +0 -0
  123. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_model_resolve.py +0 -0
  124. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_native_default.py +0 -0
  125. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_observation_aging.py +0 -0
  126. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_orchestration.py +0 -0
  127. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_otel.py +0 -0
  128. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_parallel_tools.py +0 -0
  129. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_plugins.py +0 -0
  130. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_provider_compat.py +0 -0
  131. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_provider_presets.py +0 -0
  132. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_pseudo.py +0 -0
  133. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_pyo3_api_surface.py +0 -0
  134. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_recording.py +0 -0
  135. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_reminders.py +0 -0
  136. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_replay_crosslang.py +0 -0
  137. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_resume.py +0 -0
  138. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_retry_hooks.py +0 -0
  139. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_safety_gate.py +0 -0
  140. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_sandboxed.py +0 -0
  141. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_skills.py +0 -0
  142. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_soft_timeout.py +0 -0
  143. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_spill.py +0 -0
  144. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_sqlite_storage.py +0 -0
  145. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_steer.py +0 -0
  146. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_storage_contract.py +0 -0
  147. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_subagent.py +0 -0
  148. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_system_proxy.py +0 -0
  149. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_todo.py +0 -0
  150. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_todo_gate.py +0 -0
  151. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_tokens.py +0 -0
  152. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_tool_exposure.py +0 -0
  153. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_tool_hygiene.py +0 -0
  154. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_tool_presentation.py +0 -0
  155. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_tool_router.py +0 -0
  156. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_tool_schema.py +0 -0
  157. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_tool_timeout.py +0 -0
  158. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_trace_recorder.py +0 -0
  159. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_transport_jsonrpc.py +0 -0
  160. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_transport_sse.py +0 -0
  161. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_usage_attribution.py +0 -0
  162. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_world_state.py +0 -0
  163. {steerable_agent_runtime-0.6.73 → steerable_agent_runtime-0.6.74}/tests/test_write_lease.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: steerable-agent-runtime
3
- Version: 0.6.73
3
+ Version: 0.6.74
4
4
  Summary: Steerable agent runtime: LLM, tool, storage, and transport adapters.
5
5
  License: Apache-2.0
6
6
  Requires-Python: >=3.10
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "steerable-agent-runtime"
3
- version = "0.6.73"
3
+ version = "0.6.74"
4
4
  description = "Steerable agent runtime: LLM, tool, storage, and transport adapters."
5
5
  license = { text = "Apache-2.0" }
6
6
  readme = "README.md"
@@ -33,6 +33,7 @@ from steerable_agent_runtime.loop import (
33
33
  from steerable_agent_runtime.pseudo import (
34
34
  PseudoStreamStripper,
35
35
  extract_inline_tool_calls,
36
+ reply_text_from_call,
36
37
  split_trailing_high_surrogate,
37
38
  )
38
39
  from steerable_agent_runtime.replay import build_step_decision_event
@@ -316,7 +317,8 @@ async def _collect_stream(
316
317
  send("chunk", chunk={"finish_reason": cut_finish_reason})
317
318
  elif not tool_seen:
318
319
  recovered, _ = extract_inline_tool_calls("".join(raw_content))
319
- for index, call in enumerate(recovered):
320
+ actionable = [call for call in recovered if reply_text_from_call(call) is None]
321
+ for index, call in enumerate(actionable):
320
322
  send(
321
323
  "chunk",
322
324
  chunk={
@@ -8,7 +8,8 @@ regress into prose) emit tool *intent* as text instead of a structured
8
8
  - DeepSeek XML: ``<function=tool><parameter=k>v</parameter></function>``
9
9
  - Markdown pseudo: ``[Tool call: NAME]\\n{"k": "v"}``
10
10
  - JSON tool object: a fenced or ``<tool_call>`` ``{"name", "arguments"}``
11
- object (Qwen / Ollama), or a message that is only that object
11
+ object (Qwen / Ollama), or a message that is only that object.
12
+ ``{"name": "text", "arguments": {"value": "..."}}`` is a reply, not a tool.
12
13
 
13
14
  If the loop treated that text as a final answer it would end the turn having
14
15
  never run the tool — the model "meant" to act but the loop stopped. This module
@@ -202,6 +203,31 @@ def _coerce_arguments(raw: Any) -> dict[str, Any] | None:
202
203
  return None
203
204
 
204
205
 
206
+ _REPLY_TOOL_NAMES = frozenset({"text"})
207
+ _REPLY_ARG_KEYS = ("value", "content", "text", "message")
208
+
209
+
210
+ def reply_text_from_call(call: dict[str, Any]) -> str | None:
211
+ """Return the visible reply when a model wrapped plain text as a tool.
212
+
213
+ Some local models emit ``{"name": "text", "arguments": {"value": "..."}}``
214
+ instead of a message. ``None`` means the call is a real tool.
215
+
216
+ @param call: one ``{"name", "arguments"}`` object from inline recovery.
217
+ """
218
+ name = call.get("name")
219
+ if not isinstance(name, str) or name.strip().lower() not in _REPLY_TOOL_NAMES:
220
+ return None
221
+ arguments = call.get("arguments") or {}
222
+ if not isinstance(arguments, dict):
223
+ return ""
224
+ for key in _REPLY_ARG_KEYS:
225
+ value = arguments.get(key)
226
+ if isinstance(value, str):
227
+ return value
228
+ return ""
229
+
230
+
205
231
  def _calls_from_payload(payload: Any) -> list[dict[str, Any]] | None:
206
232
  """Accept one tool-call object or a list of them. Anything else is text."""
207
233
  items = payload if isinstance(payload, list) else [payload]
@@ -600,8 +626,72 @@ class _JsonFenceFilter:
600
626
  return tail
601
627
 
602
628
 
629
+ class _BareToolJsonFilter:
630
+ """Hold a message that is only a tool-call JSON object.
631
+
632
+ A real tool object is dropped so recovery can execute it. A ``text``
633
+ reply object is replaced by its value. Any other leading ``{`` or ``[``
634
+ is released once it is clearly not that object.
635
+ """
636
+
637
+ def __init__(self) -> None:
638
+ self._buf = ""
639
+ self._passthrough = False
640
+
641
+ def feed(self, chunk: str) -> str:
642
+ if self._passthrough:
643
+ return chunk or ""
644
+ self._buf += chunk or ""
645
+ return self._decide(final=False)
646
+
647
+ def flush(self) -> str:
648
+ if self._passthrough:
649
+ return ""
650
+ return self._decide(final=True)
651
+
652
+ def _release(self, text: str) -> str:
653
+ self._passthrough = True
654
+ self._buf = ""
655
+ return text
656
+
657
+ def _decide(self, final: bool) -> str:
658
+ text = self._buf
659
+ stripped = text.lstrip()
660
+ if not stripped:
661
+ if final:
662
+ return self._release(text)
663
+ return ""
664
+ if stripped[0] not in "{[":
665
+ return self._release(text)
666
+ if len(text) > _FENCE_HOLD_CAP and not final:
667
+ return self._release(text)
668
+ try:
669
+ payload, end = json.JSONDecoder().raw_decode(stripped)
670
+ except json.JSONDecodeError as exc:
671
+ # An error at the end, or an open string, can still become JSON.
672
+ # `{not` and `[Tool call:` cannot, so they must stream immediately.
673
+ incomplete = exc.pos >= len(stripped) or "Unterminated" in exc.msg
674
+ if incomplete and not final and len(text) <= _FENCE_HOLD_CAP:
675
+ return ""
676
+ return self._release(text)
677
+ rest = stripped[end:]
678
+ if rest.strip():
679
+ return self._release(text)
680
+ calls = _calls_from_payload(payload)
681
+ if not calls:
682
+ return self._release(text)
683
+ if not final:
684
+ return ""
685
+ self._passthrough = True
686
+ self._buf = ""
687
+ replies = [reply_text_from_call(call) for call in calls]
688
+ if all(reply is not None for reply in replies):
689
+ return "".join(replies)
690
+ return ""
691
+
692
+
603
693
  class PseudoStreamStripper:
604
- """Display-path filter chaining the fn-echo, JSON-fence, and markdown filters.
694
+ """Display-path filter chaining the fn-echo, JSON, and markdown filters.
605
695
 
606
696
  Usage: ``feed`` each raw content delta, emit what comes back; at stream
607
697
  end, ``flush`` once and emit the tail. Text held back to detect markers
@@ -611,11 +701,13 @@ class PseudoStreamStripper:
611
701
  def __init__(self) -> None:
612
702
  self._fn = _FnEchoFilter()
613
703
  self._fence = _JsonFenceFilter()
704
+ self._bare = _BareToolJsonFilter()
614
705
  self._md = _MdCallFilter()
615
706
 
616
707
  def feed(self, chunk: str) -> str:
617
- return self._md.feed(self._fence.feed(self._fn.feed(chunk)))
708
+ return self._md.feed(self._bare.feed(self._fence.feed(self._fn.feed(chunk))))
618
709
 
619
710
  def flush(self) -> str:
620
- mid = self._fence.feed(self._fn.flush()) + self._fence.flush()
621
- return self._md.feed(mid) + self._md.flush()
711
+ fenced = self._fence.feed(self._fn.flush()) + self._fence.flush()
712
+ bare = self._bare.feed(fenced) + self._bare.flush()
713
+ return self._md.feed(bare) + self._md.flush()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: steerable-agent-runtime
3
- Version: 0.6.73
3
+ Version: 0.6.74
4
4
  Summary: Steerable agent runtime: LLM, tool, storage, and transport adapters.
5
5
  License: Apache-2.0
6
6
  Requires-Python: >=3.10
@@ -99,6 +99,25 @@ def test_stripper_keeps_a_fenced_config() -> None:
99
99
  assert _strip([text]) == text
100
100
 
101
101
 
102
+ def test_stripper_shows_a_text_tool_as_the_reply() -> None:
103
+ # Local models wrap the answer as {"name":"text","arguments":{"value":...}}.
104
+ text = '{"name": "text", "arguments": {"value": "Hello, World!"}}'
105
+ assert _strip(['{"name": "te', 'xt", "arguments": {"value": "Hello, World!"}}']) == (
106
+ "Hello, World!"
107
+ )
108
+ assert _strip([text]) == "Hello, World!"
109
+
110
+
111
+ def test_stripper_hides_a_bare_tool_object() -> None:
112
+ text = '{"name": "get_weather", "arguments": {"city": "Berlin"}}'
113
+ assert _strip([text[:12], text[12:]]) == ""
114
+
115
+
116
+ def test_stripper_keeps_json_that_is_not_a_tool() -> None:
117
+ text = '{"host": "127.0.0.1"}'
118
+ assert _strip([text]) == text
119
+
120
+
102
121
  def test_stripper_holds_partial_opener_then_releases() -> None:
103
122
  # "[Tool" at a chunk end is held back; when the next chunk shows it was
104
123
  # NOT an opener, the held text is released.
@@ -241,6 +260,52 @@ async def test_loop_display_stream_strips_json_fence_but_recovers_call() -> None
241
260
  assert events[-1].data["status"] == "completed"
242
261
 
243
262
 
263
+ @pytest.mark.asyncio
264
+ async def test_loop_display_stream_shows_text_tool_as_reply() -> None:
265
+ # The whole message is a fake text tool. Show the value, do not call it.
266
+ payload = '{"name": "text", "arguments": {"value": "Hello, World!"}}'
267
+ provider = make_provider([{"content_pieces": [payload[:20], payload[20:]]}])
268
+ router = ToolRouter()
269
+ executed: list[str] = []
270
+
271
+ async def text(value: str) -> str:
272
+ executed.append(value)
273
+ return value
274
+
275
+ router.register(text)
276
+ loop = CoreLoop(provider, RouterToolExecutor(router))
277
+ events = await collect(loop.run([LLMMessage.text_of("user", "hello")]))
278
+
279
+ displayed = "".join(e.data["delta"] for e in events if e.kind == "content_delta")
280
+ assert displayed == "Hello, World!"
281
+ assert executed == []
282
+ assert events[-1].data["status"] == "completed"
283
+
284
+
285
+ @pytest.mark.asyncio
286
+ async def test_loop_display_stream_strips_bare_tool_json_but_recovers_call() -> None:
287
+ payload = '{"name": "get_weather", "arguments": {"city": "Berlin"}}'
288
+ provider = make_provider(
289
+ [{"content_pieces": [payload[:18], payload[18:]]}, {"content": "It is sunny."}]
290
+ )
291
+ router = ToolRouter()
292
+ executed: list[str] = []
293
+
294
+ async def get_weather(city: str) -> str:
295
+ executed.append(city)
296
+ return "sunny"
297
+
298
+ router.register(get_weather)
299
+ loop = CoreLoop(provider, RouterToolExecutor(router))
300
+ events = await collect(loop.run([LLMMessage.text_of("user", "weather?")]))
301
+
302
+ displayed = "".join(e.data["delta"] for e in events if e.kind == "content_delta")
303
+ assert "get_weather" not in displayed
304
+ assert "{" not in displayed.split("It is sunny.")[0]
305
+ assert "It is sunny." in displayed
306
+ assert executed == ["Berlin"]
307
+
308
+
244
309
  @pytest.mark.asyncio
245
310
  async def test_loop_display_stream_strips_echo_blocks() -> None:
246
311
  pieces = ["answer: ", "<function_results>{'fake': 1}</function_results>", " 42"]