zettcode 0.1.11__tar.gz → 0.1.12__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 (305) hide show
  1. {zettcode-0.1.11 → zettcode-0.1.12}/PKG-INFO +1 -1
  2. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/__init__.py +1 -1
  3. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/tips.py +1 -1
  4. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/commands.py +20 -0
  5. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/app.py +27 -10
  6. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/messaging.py +9 -89
  7. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/shell.py +4 -0
  8. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/config.py +2 -2
  9. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/mentions.py +11 -10
  10. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/relay.py +3 -3
  11. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/sdk/client.py +1 -1
  12. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/server/app.py +1 -1
  13. {zettcode-0.1.11 → zettcode-0.1.12}/.gitignore +0 -0
  14. {zettcode-0.1.11 → zettcode-0.1.12}/README.md +0 -0
  15. {zettcode-0.1.11 → zettcode-0.1.12}/pyproject.toml +0 -0
  16. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/_compat.py +0 -0
  17. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/__init__.py +0 -0
  18. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/__init__.py +0 -0
  19. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/agent/__init__.py +0 -0
  20. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/agent/commands.py +0 -0
  21. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/agent/settings.py +0 -0
  22. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/__init__.py +0 -0
  23. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/changes.py +0 -0
  24. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/effort_gauge.py +0 -0
  25. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/entries.py +0 -0
  26. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/presenters.py +0 -0
  27. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/projection.py +0 -0
  28. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/pulse.py +0 -0
  29. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/rendering.py +0 -0
  30. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/replay.py +0 -0
  31. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/shimmer.py +0 -0
  32. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/stops.py +0 -0
  33. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/text.py +0 -0
  34. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/transcript/__init__.py +0 -0
  35. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/transcript/fold.py +0 -0
  36. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/transcript/log.py +0 -0
  37. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/transcript/oneoff.py +0 -0
  38. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/transcript/stream.py +0 -0
  39. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/agent/conversation/transcript/turn.py +0 -0
  40. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/brand.py +0 -0
  41. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/commandline/__init__.py +0 -0
  42. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/commandline/arguments.py +0 -0
  43. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/commandline/completion.py +0 -0
  44. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/commandline/history.py +0 -0
  45. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/commandline/runner.py +0 -0
  46. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/pets/__init__.py +0 -0
  47. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/pets/cat.py +0 -0
  48. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/pets/chibi.py +0 -0
  49. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/pets/dog.py +0 -0
  50. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/pets/kangaroo.py +0 -0
  51. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/pets/model.py +0 -0
  52. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/pets/penguin.py +0 -0
  53. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/pets/polarbear.py +0 -0
  54. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/pets/scarfy.py +0 -0
  55. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/pets/seagull.py +0 -0
  56. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/pets/slime.py +0 -0
  57. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/pets/snowbear.py +0 -0
  58. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/pets/sprout.py +0 -0
  59. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/pets/tiger.py +0 -0
  60. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/pets/whale.py +0 -0
  61. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/plugins/__init__.py +0 -0
  62. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/plugins/builtin_commands.py +0 -0
  63. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/plugins/builtins.py +0 -0
  64. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/plugins/container.py +0 -0
  65. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/plugins/host.py +0 -0
  66. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/plugins/loader.py +0 -0
  67. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/plugins/mixins.py +0 -0
  68. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/plugins/plugin.py +0 -0
  69. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/plugins/state.py +0 -0
  70. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/__init__.py +0 -0
  71. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/bars.py +0 -0
  72. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/boot.py +0 -0
  73. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/clipboard.py +0 -0
  74. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/commands.py +0 -0
  75. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/config_form.py +0 -0
  76. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/demo.py +0 -0
  77. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/followups.py +0 -0
  78. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/gc_policy.py +0 -0
  79. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/keys.py +0 -0
  80. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/labels.py +0 -0
  81. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/notices.py +0 -0
  82. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/providers.py +0 -0
  83. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/sessions.py +0 -0
  84. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/settings.py +0 -0
  85. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/turns.py +0 -0
  86. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/updating.py +0 -0
  87. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/__init__.py +0 -0
  88. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/approval_page.py +0 -0
  89. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/ask_page.py +0 -0
  90. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/bash_page.py +0 -0
  91. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/completer.py +0 -0
  92. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/composer.py +0 -0
  93. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/context_charts.py +0 -0
  94. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/context_page.py +0 -0
  95. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/effort_page.py +0 -0
  96. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/follow_up.py +0 -0
  97. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/form_page.py +0 -0
  98. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/input_page.py +0 -0
  99. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/model_page.py +0 -0
  100. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/model_settings_page.py +0 -0
  101. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/panel.py +0 -0
  102. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/pet.py +0 -0
  103. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/pet_page.py +0 -0
  104. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/root.py +0 -0
  105. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/self_compact_page.py +0 -0
  106. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/send_kind_page.py +0 -0
  107. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/sessions_page.py +0 -0
  108. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/statusline_page.py +0 -0
  109. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/steering.py +0 -0
  110. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/transcript.py +0 -0
  111. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/update_page.py +0 -0
  112. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/usage_charts.py +0 -0
  113. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/usage_page.py +0 -0
  114. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/app/ui/widgets/welcome.py +0 -0
  115. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/broker/__init__.py +0 -0
  116. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/broker/__main__.py +0 -0
  117. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/broker/client.py +0 -0
  118. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/broker/files.py +0 -0
  119. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/broker/finder.py +0 -0
  120. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/broker/hub.py +0 -0
  121. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/broker/log.py +0 -0
  122. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/broker/models.py +0 -0
  123. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/broker/process.py +0 -0
  124. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/broker/protocol.py +0 -0
  125. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/broker/roster.py +0 -0
  126. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/broker/runner.py +0 -0
  127. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/broker/server.py +0 -0
  128. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/broker/text.py +0 -0
  129. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/broker/web.py +0 -0
  130. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/budget.py +0 -0
  131. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/cli.py +0 -0
  132. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/collector.py +0 -0
  133. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/config_edit.py +0 -0
  134. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/daemon/__init__.py +0 -0
  135. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/daemon/__main__.py +0 -0
  136. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/daemon/cli.py +0 -0
  137. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/daemon/process.py +0 -0
  138. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/daemon/run.py +0 -0
  139. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/daemon/state.py +0 -0
  140. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/doctor.py +0 -0
  141. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/__init__.py +0 -0
  142. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/agent/__init__.py +0 -0
  143. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/agent/base.py +0 -0
  144. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/agent/context.py +0 -0
  145. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/agent/exports.py +0 -0
  146. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/agent/interaction.py +0 -0
  147. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/agent/sessions.py +0 -0
  148. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/agent/settings.py +0 -0
  149. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/agent/turns.py +0 -0
  150. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/core.py +0 -0
  151. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/dialogue/__init__.py +0 -0
  152. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/dialogue/ask.py +0 -0
  153. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/dialogue/side.py +0 -0
  154. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/dialogue/title.py +0 -0
  155. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/export/__init__.py +0 -0
  156. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/export/trace/script.js +0 -0
  157. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/export/trace/style.css +0 -0
  158. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/export/trace/template.html +0 -0
  159. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/extensions/__init__.py +0 -0
  160. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/extensions/capabilities.py +0 -0
  161. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/extensions/compaction.py +0 -0
  162. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/extensions/context.py +0 -0
  163. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/extensions/files.py +0 -0
  164. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/extensions/harness.py +0 -0
  165. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/extensions/harness_switch.py +0 -0
  166. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/extensions/mcp.py +0 -0
  167. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/extensions/mcp_notice.py +0 -0
  168. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/extensions/messaging.py +0 -0
  169. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/extensions/relay.py +0 -0
  170. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/extensions/subagents.py +0 -0
  171. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/extensions/tool_output.py +0 -0
  172. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/extensions/usage.py +0 -0
  173. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/messenger.py +0 -0
  174. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/permission/__init__.py +0 -0
  175. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/permission/approval.py +0 -0
  176. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/permission/readonly.py +0 -0
  177. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/permission/sandbox.py +0 -0
  178. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/pipeline.py +0 -0
  179. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/plugins/__init__.py +0 -0
  180. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/plugins/builtin_agent.py +0 -0
  181. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/plugins/container.py +0 -0
  182. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/plugins/extension.py +0 -0
  183. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/plugins/loader.py +0 -0
  184. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/plugins/mixins.py +0 -0
  185. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/plugins/plugin.py +0 -0
  186. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/plugins/services.py +0 -0
  187. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/plugins/state.py +0 -0
  188. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/server_tools.py +0 -0
  189. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/storage/__init__.py +0 -0
  190. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/storage/layout.py +0 -0
  191. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/storage/ledger.py +0 -0
  192. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/storage/locks.py +0 -0
  193. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/storage/metadata.py +0 -0
  194. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/storage/records.py +0 -0
  195. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/storage/store.py +0 -0
  196. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/tools/__init__.py +0 -0
  197. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/tools/boundary.py +0 -0
  198. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/tools/files.py +0 -0
  199. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/tools/images.py +0 -0
  200. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/tools/output.py +0 -0
  201. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/tools/shell.py +0 -0
  202. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/engine/tools/workspace.py +0 -0
  203. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/error_text.py +0 -0
  204. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/filelock.py +0 -0
  205. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/oneshot.py +0 -0
  206. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/paths.py +0 -0
  207. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/prompt.py +0 -0
  208. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/providers.py +0 -0
  209. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/py.typed +0 -0
  210. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/registry.py +0 -0
  211. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/sdk/__init__.py +0 -0
  212. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/sdk/errors.py +0 -0
  213. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/sdk/events.py +0 -0
  214. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/sdk/handlers.py +0 -0
  215. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/sdk/models.py +0 -0
  216. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/sdk/session.py +0 -0
  217. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/sdk/translation.py +0 -0
  218. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/sdk/types.py +0 -0
  219. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/server/__init__.py +0 -0
  220. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/server/cli.py +0 -0
  221. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/server/runner.py +0 -0
  222. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/server/schemas.py +0 -0
  223. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/server/serialize.py +0 -0
  224. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/server/settings.py +0 -0
  225. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/statusline.py +0 -0
  226. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/__init__.py +0 -0
  227. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/capabilities.py +0 -0
  228. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/clipboard.py +0 -0
  229. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/console.py +0 -0
  230. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/core/__init__.py +0 -0
  231. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/core/app.py +0 -0
  232. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/core/events.py +0 -0
  233. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/core/focus.py +0 -0
  234. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/core/geometry.py +0 -0
  235. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/core/host.py +0 -0
  236. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/core/keymap.py +0 -0
  237. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/core/scheduler.py +0 -0
  238. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/core/screen.py +0 -0
  239. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/core/theme.py +0 -0
  240. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/core/widget.py +0 -0
  241. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/gallery.py +0 -0
  242. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/glyphs.py +0 -0
  243. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/input/__init__.py +0 -0
  244. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/input/bridge.py +0 -0
  245. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/input/decoder.py +0 -0
  246. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/input/events.py +0 -0
  247. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/input/keys.py +0 -0
  248. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/input/reader.py +0 -0
  249. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/layout/__init__.py +0 -0
  250. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/layout/box.py +0 -0
  251. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/layout/overlay.py +0 -0
  252. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/layout/scroll.py +0 -0
  253. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/layout/solver.py +0 -0
  254. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/layout/spacing.py +0 -0
  255. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/render/__init__.py +0 -0
  256. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/render/ansi.py +0 -0
  257. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/render/canvas.py +0 -0
  258. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/render/code.py +0 -0
  259. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/render/color.py +0 -0
  260. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/render/renderer.py +0 -0
  261. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/render/rich_text.py +0 -0
  262. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/render/style.py +0 -0
  263. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/render/sweep.py +0 -0
  264. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/render/text.py +0 -0
  265. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/runner.py +0 -0
  266. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/terminal.py +0 -0
  267. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/testing/__init__.py +0 -0
  268. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/testing/harness.py +0 -0
  269. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/testing/snapshot.py +0 -0
  270. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/theme_file.py +0 -0
  271. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/themes/ayu-mirage.toml +0 -0
  272. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/themes/catppuccin-latte.toml +0 -0
  273. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/themes/catppuccin-mocha.toml +0 -0
  274. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/themes/dracula.toml +0 -0
  275. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/themes/github-dimmed.toml +0 -0
  276. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/themes/github-light.toml +0 -0
  277. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/themes/gruvbox-dark.toml +0 -0
  278. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/themes/gruvbox-light.toml +0 -0
  279. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/themes/kanagawa.toml +0 -0
  280. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/themes/nord.toml +0 -0
  281. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/themes/one-dark.toml +0 -0
  282. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/themes/rose-pine-dawn.toml +0 -0
  283. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/themes/rose-pine.toml +0 -0
  284. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/themes/solarized-dark.toml +0 -0
  285. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/themes/solarized-light.toml +0 -0
  286. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/themes/tokyo-night.toml +0 -0
  287. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/widgets/__init__.py +0 -0
  288. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/widgets/collapsible.py +0 -0
  289. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/widgets/completion.py +0 -0
  290. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/widgets/console.py +0 -0
  291. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/widgets/dialog.py +0 -0
  292. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/widgets/diff.py +0 -0
  293. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/widgets/list.py +0 -0
  294. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/widgets/list_page.py +0 -0
  295. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/widgets/markdown.py +0 -0
  296. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/widgets/progress.py +0 -0
  297. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/widgets/rich_text.py +0 -0
  298. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/widgets/status.py +0 -0
  299. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/widgets/table.py +0 -0
  300. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/widgets/tasks.py +0 -0
  301. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/widgets/text.py +0 -0
  302. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/widgets/textarea.py +0 -0
  303. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/tui/widgets/toast.py +0 -0
  304. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/update.py +0 -0
  305. {zettcode-0.1.11 → zettcode-0.1.12}/src/zettcode/wording.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: zettcode
3
- Version: 0.1.11
3
+ Version: 0.1.12
4
4
  Summary: A focused terminal coding agent powered by zett-agent
5
5
  Project-URL: Repository, https://github.com/Chang-LeHung/zettcode
6
6
  Project-URL: Issues, https://github.com/Chang-LeHung/zettcode/issues
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING
7
7
 
8
8
  #: The one place the version is written. ``pyproject.toml`` reads it from here
9
9
  #: and the release workflow checks the tag against it, so a bump is one edit.
10
- __version__ = "0.1.11"
10
+ __version__ = "0.1.12"
11
11
 
12
12
  #: Where each public name lives; read by :func:`__getattr__` on first use.
13
13
  _EXPORTS = {
@@ -22,7 +22,7 @@ TIPS: tuple[str, ...] = (
22
22
  "Type while it works: your message can steer this request, or wait and follow it.",
23
23
  "Press Shift-Enter (or Alt-Enter) to add a line to your message instead of sending it.",
24
24
  "Type @ to mention a file or a skill. A file is read when the message is sent.",
25
- "Start a message with @name to send it to another running ZettCode session.",
25
+ "Type @ to mention another running ZettCode session; the model can pass your message to it.",
26
26
  "/btw <question> is answered on the side and never joins the conversation.",
27
27
  "/bash opens a full-screen command line: your shell's history, Tab completion, and coloured output.",
28
28
  "In /bash, Tab completes git subcommands and branches, and Ctrl-C stops a running command.",
@@ -101,6 +101,26 @@ class CommandHost(Protocol):
101
101
  async def run_side_question(self, question: str) -> None:
102
102
  """Answer a question that is kept out of the conversation."""
103
103
 
104
+ async def prompt(self, text: str) -> bool:
105
+ """Run ``text`` as one turn of this conversation, exactly as if the reader had typed it.
106
+
107
+ The text reaches the model the way a draft does: echoed in the transcript as the reader's own message,
108
+ stored in the session, streamed as it is answered, and settled by the same row. ``@`` references are
109
+ resolved; pictures the text cannot carry are not part of it.
110
+
111
+ The turn is awaited, so the command that asked for it holds the request slot until it has been answered,
112
+ and the row the shell shows is the command's own.
113
+
114
+ Args:
115
+ text: What to ask. Its own text is what the reader sees, so a command that builds the text should
116
+ write it as a reader would have.
117
+
118
+ Returns:
119
+ Whether a turn was started and has ended. A request already running, a blank text and a session held
120
+ by another process answer ``False``, and nothing is started. A command that means to be usable
121
+ mid-request says ``when_busy="queue"``, and this then runs when the request in flight has ended.
122
+ """
123
+
104
124
  def change_model(self, name: ModelInfo | str) -> ModelInfo:
105
125
  """Switch models and announce the change in the transcript."""
106
126
 
@@ -135,8 +135,8 @@ class ZettCodeApp(
135
135
  # The plugins' commands reach the shell through the context they are run with, and the words their menus
136
136
  # offer through this link; the shell is built enough by now that either may follow it.
137
137
  agent.attach_host(self)
138
- #: The line to the other sessions, when the configuration has one; the session names it offers
139
- #: for ``@`` come first, because a draft that begins with one is a message to that session.
138
+ #: The line to the other sessions, when the configuration has one; a session name it offers for ``@``
139
+ #: comes first, so it wins over a file or plugin resource of the same name.
140
140
  self.messenger: SessionLink | None = agent.messenger
141
141
  self.mentions = MentionRegistry(
142
142
  agent.plugin_mentions
@@ -254,7 +254,6 @@ class ZettCodeApp(
254
254
  self._follow_ups_held = False
255
255
  self._turn_running = False
256
256
  self._inbox: list[Relayed] = []
257
- self._sends: set[asyncio.Task[None]] = set()
258
257
  self._lock_watch: asyncio.Task[None] | None = None
259
258
  self._lock_said: str | None = None
260
259
  self._messaging_live = False
@@ -660,6 +659,26 @@ class ZettCodeApp(
660
659
  if outcome:
661
660
  self._settle_wait(outcome)
662
661
 
662
+ async def prompt(self, text: str) -> bool:
663
+ """Run ``text`` as one turn of this conversation, as if the reader had typed it.
664
+
665
+ The draft path is taken whole — the echo, ``@`` references, the transcript, the session, the row the
666
+ request ends on — rather than a run of its own, so a command that asks the model something produces a turn
667
+ a reader could have produced. What the command decides is the text; everything after that is the ordinary
668
+ one.
669
+
670
+ The turn is awaited, so the request slot stays the command's until the answer is in: a second draft cannot
671
+ start behind it, and the command reports what it did once it is done.
672
+ """
673
+ if not text.strip():
674
+ return False
675
+ if self.client.session.locked:
676
+ self.flash("this session is open in another ZettCode process; it is read-only here", level="error")
677
+ return False
678
+ parts = mention_parts((text,), self.mentions)
679
+ await self._run_prompt(text, parts, mention_hint(parts, self.mentions), advance=False)
680
+ return True
681
+
663
682
  async def run_continue(self) -> None:
664
683
  """Pick up the request that was interrupted, where it stopped, as a turn of the conversation.
665
684
 
@@ -821,12 +840,6 @@ class ZettCodeApp(
821
840
  # typed, and this is for what arrives some other way (a draft that was there already, a recall).
822
841
  self.flash("this session is open in another ZettCode process; it is read-only here", level="error")
823
842
  return False
824
- if not command:
825
- # A draft that begins with the name of an active session is a message to it: sent whatever this
826
- # session is doing, because it is not a request to this session's model.
827
- sent = self._send_session_message(self.composer.parts())
828
- if sent is not None:
829
- return sent
830
843
  if self._busy:
831
844
  if command:
832
845
  # Which commands are taken mid-request is the command's own
@@ -971,6 +984,7 @@ class ZettCodeApp(
971
984
  hint: str | None = None,
972
985
  *,
973
986
  relayed: Relayed | None = None,
987
+ advance: bool = True,
974
988
  ) -> None:
975
989
  """Stream one agent turn, keeping the task panel and transcript current.
976
990
 
@@ -1056,7 +1070,10 @@ class ZettCodeApp(
1056
1070
  self._note = None
1057
1071
  self._refresh_tasks()
1058
1072
  self.app.invalidate()
1059
- self._start_next_command()
1073
+ if advance:
1074
+ # A turn a command asked for leaves this to the command: it is still running, and what was queued
1075
+ # waits for it to end, not for the turn inside it.
1076
+ self._start_next_command()
1060
1077
 
1061
1078
  def _set_busy(self, busy: bool) -> None:
1062
1079
  """Track in-flight work and keep the frame loop alive while it runs.
@@ -1,8 +1,8 @@
1
- """Messages between sessions: sending one with ``@``, receiving one as a turn.
1
+ """Messages between sessions: receiving one as a turn, and what the broker knows about this one.
2
2
 
3
3
  Another ZettCode session reaches this one through the broker (:mod:`zettcode.broker`), and this
4
- one reaches it. The shell's part is small, because a received message is meant to behave like one the
5
- reader typed:
4
+ one reaches it through the model. The shell's receiving part is small, because a message is meant to
5
+ behave like one the reader typed:
6
6
 
7
7
  * **Idle** — it starts a turn at once, drawn as a message from that session and not as the reader's
8
8
  own, and everything after is an ordinary turn: the wait row, the answer, the summary of what
@@ -10,9 +10,10 @@ reader typed:
10
10
  * **Busy** — it waits, and starts when the request in flight ends, after what the reader queued. It
11
11
  never steers the request: it came from somebody else, and "change what you are doing" is the
12
12
  reader's to say.
13
- * **Sending** — a draft that *begins* with ``@name`` of an active session is a message to it, not a
14
- request to the model: it is sent, whatever this session is doing, and drawn as sent. A session named
15
- later in the draft is only a reference the model is told about.
13
+
14
+ The shell sends nothing itself. ``@name`` in a draft is resolved like any other reference
15
+ (:mod:`zettcode.mentions`): the model reads which session it means and how to reach it, and calls
16
+ ``send_message`` itself when the request calls for one.
16
17
 
17
18
  The session also tells the broker what it is — its id, title, and whether it is busy — whenever one
18
19
  of them changes, so the others see a current list. None of this can fail the shell: with no broker the
@@ -22,29 +23,20 @@ shell is exactly what it was.
22
23
  from __future__ import annotations
23
24
 
24
25
  import asyncio
25
- from collections.abc import Sequence
26
26
  from typing import TYPE_CHECKING
27
27
 
28
- from zettcode.broker.client import Receipt, SendError
29
- from zettcode.broker.protocol import MAX_TEXT, VIA_USER
30
- from zettcode.mentions import MENTION
31
- from zettcode.prompt import PromptPart, carries_image, plain_text
28
+ from zettcode.broker.client import Receipt
32
29
  from zettcode.relay import Relayed
33
30
 
34
31
  from .shell import ShellState
35
32
 
36
33
  if TYPE_CHECKING: # pragma: no cover - annotations only
37
- from zettcode.broker.client import RosterEntry
38
-
39
34
  from ..agent.agent import SessionLink
40
35
 
41
36
  #: Messages from other sessions that may wait for a request to end. A session that is flooded refuses
42
37
  #: the rest, and the sender is told why, instead of this shell holding an unbounded list.
43
38
  MAX_INBOX = 16
44
39
 
45
- #: Characters that may separate ``@name`` from the message it addresses: ``@bob: hi`` and ``@bob, hi``.
46
- _SEPARATORS = " \t\r\n,:;"
47
-
48
40
  #: Characters of a waiting message that the notice repeats, so the reader can tell which one it is.
49
41
  _PREVIEW = 60
50
42
 
@@ -56,8 +48,6 @@ class MessagingMixin(ShellState):
56
48
  messenger: SessionLink | None
57
49
  #: Messages from other sessions waiting for the request in flight to end, oldest first.
58
50
  _inbox: list[Relayed]
59
- #: Sends in flight, held so they are not collected and can be cancelled on exit.
60
- _sends: set[asyncio.Task[None]]
61
51
  #: Whether the connection has been started; before that there is no loop to tell it anything on.
62
52
  _messaging_live: bool
63
53
  #: The session id the broker was last told about, to notice a switch.
@@ -82,14 +72,9 @@ class MessagingMixin(ShellState):
82
72
  self._messaging_start = asyncio.ensure_future(messenger.start())
83
73
 
84
74
  async def _stop_messaging(self) -> None:
85
- """Leave the broker and end what is still being sent. Nothing here may delay leaving."""
75
+ """Leave the broker. Nothing here may delay leaving."""
86
76
  messenger = self.messenger
87
77
  self._messaging_live = False
88
- sends = tuple(self._sends)
89
- for send in sends:
90
- send.cancel()
91
- if sends:
92
- await asyncio.gather(*sends, return_exceptions=True)
93
78
  start = self._messaging_start
94
79
  if start is not None:
95
80
  await asyncio.gather(start, return_exceptions=True)
@@ -179,68 +164,3 @@ class MessagingMixin(ShellState):
179
164
  return False
180
165
  self._start_relayed(self._inbox.pop(0))
181
166
  return True
182
-
183
- # -- sending ----------------------------------------------------------------------------------
184
- def _session_message(self, parts: Sequence[PromptPart]) -> tuple[str, str] | None:
185
- """Return ``(name, text)`` when a draft is addressed to an active session, else ``None``.
186
-
187
- A draft is addressed when its first word is ``@name`` and ``name`` is the handle of a session
188
- that is active now. Anything else — a name nobody has, a name in the middle, a file path — is an
189
- ordinary draft for the model.
190
- """
191
- messenger = self.messenger
192
- if messenger is None:
193
- return None
194
- text = plain_text(parts).lstrip()
195
- match = MENTION.match(text)
196
- if match is None:
197
- return None
198
- peer = messenger.find(match.group(1))
199
- if peer is None:
200
- return None
201
- return peer.handle, text[match.end() :].lstrip(_SEPARATORS).rstrip()
202
-
203
- def _send_session_message(self, parts: Sequence[PromptPart]) -> bool | None:
204
- """Send a draft addressed to a session, or say why it cannot be.
205
-
206
- Returns:
207
- ``None`` when the draft is not addressed to a session and the caller should treat it as any
208
- other; otherwise what :meth:`submit` returns — ``True`` when the draft was taken and the
209
- composer should clear, ``False`` when it was not and the draft stays to be fixed.
210
- """
211
- found = self._session_message(parts)
212
- if found is None:
213
- return None
214
- name, text = found
215
- if carries_image(parts):
216
- self.flash(f"a message to @{name} is text only", level="error")
217
- return False
218
- if not text:
219
- self.flash(f"write the message after @{name}", level="error")
220
- return False
221
- if len(text) > MAX_TEXT:
222
- self.flash(f"a message is limited to {MAX_TEXT} characters; this is {len(text)}", level="error")
223
- return False
224
- peer = self.messenger.find(name) if self.messenger is not None else None
225
- if peer is None: # went away between the two lookups: nothing was addressed
226
- return None
227
- task = asyncio.create_task(self._deliver(peer, text))
228
- self._sends.add(task)
229
- task.add_done_callback(self._sends.discard)
230
- return True
231
-
232
- async def _deliver(self, peer: RosterEntry, text: str) -> None:
233
- """Send one message and put the outcome in the conversation: the message, or why it was not taken."""
234
- messenger = self.messenger
235
- if messenger is None:
236
- return
237
- try:
238
- delivered = await messenger.send(peer.session_id, text, via=VIA_USER)
239
- except SendError as error:
240
- self.transcript.error(f"not sent to @{peer.handle}: {error}")
241
- except Exception as error: # noqa: BLE001 - a bug here must not be silent, and must not end the shell
242
- self.transcript.error(f"not sent to @{peer.handle}: {type(error).__name__}")
243
- else:
244
- note = "queued" if delivered.disposition == "queued" else "delivered"
245
- self.transcript.sent_message(delivered.to.handle, text, note)
246
- self.app.invalidate()
@@ -246,6 +246,10 @@ class ShellState:
246
246
  def submit(self, value: str, *, command: bool | None = None) -> bool | None:
247
247
  """Start a turn, steer the running one, or run a slash command (``command``: whether the draft is one)."""
248
248
 
249
+ async def prompt(self, text: str) -> bool:
250
+ """Run ``text`` as one turn, as if the reader had typed it; whether one was started and has ended."""
251
+ return False
252
+
249
253
  def flash(self, message: str, *, seconds: float = FLASH_SECONDS, level: str = "info") -> None:
250
254
  """Say something in the row above the composer, for a few seconds."""
251
255
 
@@ -461,8 +461,8 @@ class ZettCodeConfig:
461
461
  shell_network: Let a command that runs in the ``safe`` sandbox use the network. Off by default; ``[shell]
462
462
  network = true`` turns it on. ``full`` always has the network, and so does a machine with no sandbox.
463
463
  sessions_enabled: Register with the background session broker, so this session can be
464
- messaged by the others and message them: ``@name`` in the composer and the
465
- ``send_message`` and ``list_sessions`` tools.
464
+ messaged by the others and message them: ``@name`` in the composer names a
465
+ session for the model, and the ``send_message`` and ``list_sessions`` tools reach it.
466
466
  update_enabled: Ask PyPI, in the background, whether a newer release
467
467
  exists, and offer it on a later start.
468
468
  update_file: Where that check leaves what it found; one small JSON file.
@@ -173,9 +173,9 @@ class SkillMentions(MentionProvider):
173
173
  class SessionMentions(MentionProvider):
174
174
  """The other ZettCode sessions that are running, addressed by the name they are listed under.
175
175
 
176
- A draft that *begins* with one of these is a message to that session and is sent there instead of
177
- to the model (see :mod:`zettcode.app.ui.messaging`). A session named further along is only a
178
- reference, which tells the model what the name means and that it can send a message to it.
176
+ A session named anywhere in a draft is a reference like any other: the model reads ``session "name"``
177
+ and is told how to reach that session, and decides for itself whether the request calls for a message.
178
+ The shell sends nothing itself (see :mod:`zettcode.app.ui.messaging`).
179
179
 
180
180
  Candidates are read from what the connection last reported — a list in memory — and never from
181
181
  the broker: completion runs on every keystroke after an ``@``.
@@ -240,11 +240,11 @@ class SessionMentions(MentionProvider):
240
240
  return next((peer for peer in self._peers() if peer.handle == name), None)
241
241
 
242
242
  def expand(self, name: str) -> str | None:
243
- """Tell the model what the reader's ``@`` meant, for a draft that names a session inside a sentence.
243
+ """Tell the model what the reader's ``@`` meant and how to reach that session.
244
244
 
245
245
  Nothing is sent from here: the model is told which session the words mean and how to reach it, and
246
- decides for itself whether the request calls for a message. A draft that *begins* with the name
247
- never gets this far, because the shell sends it to that session itself.
246
+ decides for itself whether the request calls for a message. A draft that begins with the name is no
247
+ different from one that names the session inside a sentence.
248
248
  """
249
249
  peer = self._find(name)
250
250
  if peer is None:
@@ -254,10 +254,11 @@ class SessionMentions(MentionProvider):
254
254
  return (
255
255
  f'In the message above, `session "{name}"` is what the user wrote as @{name}. In ZettCode an @ before a '
256
256
  f"name means another ZettCode session that is running right now, not a file or a skill. This one works{where}.{about}\n"
257
- f"If the user's request is for that session to do or know something, send it a message with the "
258
- f'send_message tool: send_message(to="{peer.session_id}", message=...). Write the message so the '
259
- f"other session can act on it without having seen this conversation. If the request is only about the "
260
- f"session (what it is doing, where it is), list_sessions answers that. Do not send anything the "
257
+ f"When the user's message is addressed to that session — the @ begins the message — what follows the name "
258
+ f"is the message to pass on. When the session is only mentioned, decide whether the request is for it. "
259
+ f'To send, call the send_message tool: send_message(to="{peer.session_id}", message=...). Write the '
260
+ f"message so the other session can act on it without having seen this conversation. If the request is only "
261
+ f"about the session (what it is doing, where it is), list_sessions answers that. Do not send anything the "
261
262
  f"user did not ask you to pass on."
262
263
  )
263
264
 
@@ -1,8 +1,8 @@
1
1
  """A message from another session: who sent it, what the model reads, and how it is stored.
2
2
 
3
- Another ZettCode session on the same machine can send this one a message, either because its reader
4
- typed ``@name …`` there or because its agent used the ``send_message`` tool. In *this* session the
5
- message is handled like one the reader typed — it is a turn, it is stored, it is restored with the
3
+ Another ZettCode session on the same machine can send this one a message because its agent used the
4
+ ``send_message`` tool. In *this* session the message is handled like one the reader typed — it is a
5
+ turn, it is stored, it is restored with the
6
6
  session — with three differences, all of them here:
7
7
 
8
8
  * the model is told where it came from, so it does not mistake a colleague's request for its own
@@ -628,7 +628,7 @@ class Client:
628
628
  )
629
629
 
630
630
  async def send(self, to: str, text: str) -> Delivered:
631
- """Send a message to another ZettCode session, as the terminal's ``@name`` draft does.
631
+ """Send a message to another ZettCode session without going through this session's model.
632
632
 
633
633
  Args:
634
634
  to: The session's handle, or its id.
@@ -570,7 +570,7 @@ def create_app(settings: Settings, *, client_factory: ClientFactory | None = Non
570
570
 
571
571
  @protected.post("/sessions/current/send", response_model=SentOut, tags=["sessions"])
572
572
  async def send_message(body: SendIn, runner: RunnerDep) -> SentOut:
573
- """Send a message to another ZettCode session, as a draft beginning with ``@name`` does."""
573
+ """Send a message to another ZettCode session explicitly, without a model turn."""
574
574
  to, disposition = await runner.send(body.to, body.text)
575
575
  return SentOut(to=to, disposition=disposition)
576
576
 
File without changes
File without changes
File without changes