zettcode 0.2.0__tar.gz → 0.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (350) hide show
  1. {zettcode-0.2.0 → zettcode-0.3.1}/.gitignore +4 -0
  2. {zettcode-0.2.0 → zettcode-0.3.1}/PKG-INFO +4 -2
  3. {zettcode-0.2.0 → zettcode-0.3.1}/pyproject.toml +20 -2
  4. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/__init__.py +1 -1
  5. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/agent/commands.py +3 -3
  6. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/rendering.py +76 -66
  7. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/commands.py +1 -1
  8. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/pets/model.py +1 -1
  9. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/plugins/__init__.py +24 -22
  10. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/plugins/builtin_commands.py +218 -43
  11. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/plugins/builtins.py +28 -20
  12. zettcode-0.2.0/src/zettcode/app/plugins/container.py → zettcode-0.3.1/src/zettcode/app/plugins/groups.py +48 -31
  13. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/plugins/loader.py +102 -32
  14. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/plugins/mixins.py +1 -1
  15. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/plugins/plugin.py +11 -6
  16. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/app.py +3 -0
  17. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/messaging.py +25 -1
  18. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/notices.py +1 -0
  19. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/__init__.py +5 -0
  20. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/approval_page.py +11 -2
  21. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/model_settings_page.py +2 -3
  22. zettcode-0.3.1/src/zettcode/app/ui/widgets/plugins_page.py +428 -0
  23. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/self_compact_page.py +9 -54
  24. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/statusline_page.py +3 -33
  25. zettcode-0.3.1/src/zettcode/app/ui/widgets/switch.py +87 -0
  26. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/broker/client.py +104 -6
  27. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/broker/hub.py +76 -2
  28. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/broker/models.py +5 -0
  29. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/broker/protocol.py +14 -3
  30. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/broker/roster.py +7 -1
  31. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/config.py +30 -1
  32. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/config_edit.py +17 -0
  33. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/daemon/run.py +27 -5
  34. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/core.py +7 -5
  35. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/extensions/subagents.py +2 -2
  36. zettcode-0.3.1/src/zettcode/engine/extensions/truncation.py +156 -0
  37. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/messenger.py +78 -4
  38. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/permission/sandbox.py +9 -3
  39. zettcode-0.3.1/src/zettcode/engine/plugins/__init__.py +73 -0
  40. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/plugins/builtin_agent.py +115 -91
  41. zettcode-0.3.1/src/zettcode/engine/plugins/commands.py +131 -0
  42. zettcode-0.2.0/src/zettcode/engine/plugins/container.py → zettcode-0.3.1/src/zettcode/engine/plugins/context.py +31 -8
  43. zettcode-0.3.1/src/zettcode/engine/plugins/groups.py +80 -0
  44. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/plugins/loader.py +64 -30
  45. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/plugins/plugin.py +55 -11
  46. zettcode-0.3.1/src/zettcode/engine/plugins/switches.py +412 -0
  47. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/server_tools.py +24 -2
  48. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/storage/__init__.py +1 -1
  49. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/tools/boundary.py +7 -2
  50. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/tools/files.py +42 -47
  51. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/tools/output.py +12 -5
  52. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/tools/shell.py +20 -10
  53. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/tools/workspace.py +80 -77
  54. zettcode-0.3.1/src/zettcode/gateway/__init__.py +22 -0
  55. zettcode-0.3.1/src/zettcode/gateway/app.py +389 -0
  56. zettcode-0.3.1/src/zettcode/gateway/backend.py +291 -0
  57. zettcode-0.3.1/src/zettcode/gateway/chat.py +128 -0
  58. zettcode-0.3.1/src/zettcode/gateway/cli.py +138 -0
  59. zettcode-0.3.1/src/zettcode/gateway/service.py +811 -0
  60. zettcode-0.3.1/src/zettcode/gateway/settings.py +62 -0
  61. zettcode-0.3.1/src/zettcode/gateway/static/app.css +753 -0
  62. zettcode-0.3.1/src/zettcode/gateway/static/app.js +3 -0
  63. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_AMS-Regular-U6PRYMIZ.woff2 +0 -0
  64. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_Caligraphic-Bold-5QL5CMTE.woff2 +0 -0
  65. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_Caligraphic-Regular-KX5MEWCF.woff2 +0 -0
  66. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_Fraktur-Bold-2QVFK6NQ.woff2 +0 -0
  67. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_Fraktur-Regular-2PEIFJSJ.woff2 +0 -0
  68. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_Main-Bold-YP5VVQRP.woff2 +0 -0
  69. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_Main-BoldItalic-N4V3DX7S.woff2 +0 -0
  70. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_Main-Italic-RELBIK7M.woff2 +0 -0
  71. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_Main-Regular-ARRPAO67.woff2 +0 -0
  72. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_Math-BoldItalic-K4WTGH3J.woff2 +0 -0
  73. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_Math-Italic-6KGCHLFN.woff2 +0 -0
  74. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_SansSerif-Bold-RRNVJFFW.woff2 +0 -0
  75. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_SansSerif-Italic-HMPFTM52.woff2 +0 -0
  76. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_SansSerif-Regular-XIQ62X4E.woff2 +0 -0
  77. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_Script-Regular-APUWIHLP.woff2 +0 -0
  78. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_Size1-Regular-5LRUTBFT.woff2 +0 -0
  79. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_Size2-Regular-LELKET5D.woff2 +0 -0
  80. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_Size3-Regular-WQRQ47UD.woff2 +0 -0
  81. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_Size4-Regular-CDMV7U5C.woff2 +0 -0
  82. zettcode-0.3.1/src/zettcode/gateway/static/assets/KaTeX_Typewriter-Regular-VBYJ4NRC.woff2 +0 -0
  83. zettcode-0.3.1/src/zettcode/gateway/static/chunks/chunk-Y6SLVHK3.js +1 -0
  84. zettcode-0.3.1/src/zettcode/gateway/static/chunks/render-G6G3TSB3.js +341 -0
  85. zettcode-0.3.1/src/zettcode/gateway/static/icon.png +0 -0
  86. zettcode-0.3.1/src/zettcode/gateway/static/index.html +23 -0
  87. zettcode-0.3.1/src/zettcode/gateway/static/logo.svg +205 -0
  88. zettcode-0.3.1/src/zettcode/gateway/static/manifest.webmanifest +11 -0
  89. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/paths.py +4 -0
  90. zettcode-0.3.1/src/zettcode/plugin_tui.py +104 -0
  91. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/relay.py +3 -0
  92. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/sdk/__init__.py +11 -0
  93. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/sdk/client.py +293 -10
  94. zettcode-0.3.1/src/zettcode/sdk/commands.py +9 -0
  95. zettcode-0.3.1/src/zettcode/sdk/daemons.py +153 -0
  96. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/sdk/errors.py +13 -0
  97. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/sdk/events.py +10 -2
  98. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/sdk/session.py +51 -24
  99. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/sdk/translation.py +25 -2
  100. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/sdk/types.py +184 -0
  101. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/server/app.py +67 -2
  102. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/server/runner.py +41 -0
  103. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/server/schemas.py +90 -1
  104. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/server/serialize.py +7 -1
  105. zettcode-0.2.0/src/zettcode/engine/plugins/__init__.py +0 -64
  106. {zettcode-0.2.0 → zettcode-0.3.1}/README.md +0 -0
  107. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/_compat.py +0 -0
  108. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/__init__.py +0 -0
  109. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/__init__.py +0 -0
  110. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/agent/__init__.py +0 -0
  111. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/agent/settings.py +0 -0
  112. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/__init__.py +0 -0
  113. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/changes.py +0 -0
  114. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/effort_gauge.py +0 -0
  115. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/entries.py +0 -0
  116. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/presenters.py +0 -0
  117. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/projection.py +0 -0
  118. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/pulse.py +0 -0
  119. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/replay.py +0 -0
  120. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/shimmer.py +0 -0
  121. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/stops.py +0 -0
  122. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/text.py +0 -0
  123. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/tips.py +0 -0
  124. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/transcript/__init__.py +0 -0
  125. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/transcript/fold.py +0 -0
  126. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/transcript/log.py +0 -0
  127. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/transcript/oneoff.py +0 -0
  128. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/transcript/stream.py +0 -0
  129. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/agent/conversation/transcript/turn.py +0 -0
  130. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/brand.py +0 -0
  131. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/commandline/__init__.py +0 -0
  132. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/commandline/arguments.py +0 -0
  133. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/commandline/completion.py +0 -0
  134. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/commandline/history.py +0 -0
  135. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/commandline/runner.py +0 -0
  136. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/pets/__init__.py +0 -0
  137. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/pets/cat.py +0 -0
  138. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/pets/chibi.py +0 -0
  139. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/pets/dog.py +0 -0
  140. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/pets/kangaroo.py +0 -0
  141. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/pets/penguin.py +0 -0
  142. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/pets/polarbear.py +0 -0
  143. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/pets/scarfy.py +0 -0
  144. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/pets/seagull.py +0 -0
  145. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/pets/slime.py +0 -0
  146. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/pets/snowbear.py +0 -0
  147. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/pets/sprout.py +0 -0
  148. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/pets/tiger.py +0 -0
  149. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/pets/whale.py +0 -0
  150. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/plugins/host.py +0 -0
  151. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/plugins/state.py +0 -0
  152. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/__init__.py +0 -0
  153. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/bars.py +0 -0
  154. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/boot.py +0 -0
  155. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/clipboard.py +0 -0
  156. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/commands.py +0 -0
  157. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/config_form.py +0 -0
  158. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/demo.py +0 -0
  159. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/followups.py +0 -0
  160. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/gc_policy.py +0 -0
  161. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/keys.py +0 -0
  162. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/labels.py +0 -0
  163. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/providers.py +0 -0
  164. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/sessions.py +0 -0
  165. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/settings.py +0 -0
  166. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/shell.py +0 -0
  167. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/turns.py +0 -0
  168. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/updating.py +0 -0
  169. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/ask_page.py +0 -0
  170. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/bash_page.py +0 -0
  171. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/completer.py +0 -0
  172. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/composer.py +0 -0
  173. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/context_charts.py +0 -0
  174. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/context_page.py +0 -0
  175. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/effort_page.py +0 -0
  176. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/follow_up.py +0 -0
  177. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/form_page.py +0 -0
  178. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/input_page.py +0 -0
  179. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/model_page.py +0 -0
  180. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/panel.py +0 -0
  181. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/pet.py +0 -0
  182. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/pet_page.py +0 -0
  183. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/root.py +0 -0
  184. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/send_kind_page.py +0 -0
  185. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/sessions_page.py +0 -0
  186. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/steering.py +0 -0
  187. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/transcript.py +0 -0
  188. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/update_page.py +0 -0
  189. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/usage_charts.py +0 -0
  190. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/usage_page.py +0 -0
  191. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/app/ui/widgets/welcome.py +0 -0
  192. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/broker/__init__.py +0 -0
  193. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/broker/__main__.py +0 -0
  194. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/broker/files.py +0 -0
  195. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/broker/finder.py +0 -0
  196. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/broker/log.py +0 -0
  197. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/broker/process.py +0 -0
  198. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/broker/runner.py +0 -0
  199. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/broker/server.py +0 -0
  200. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/broker/text.py +0 -0
  201. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/broker/web.py +0 -0
  202. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/budget.py +0 -0
  203. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/cli.py +0 -0
  204. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/collector.py +0 -0
  205. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/daemon/__init__.py +0 -0
  206. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/daemon/__main__.py +0 -0
  207. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/daemon/cli.py +0 -0
  208. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/daemon/process.py +0 -0
  209. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/daemon/state.py +0 -0
  210. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/doctor.py +0 -0
  211. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/__init__.py +0 -0
  212. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/agent/__init__.py +0 -0
  213. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/agent/base.py +0 -0
  214. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/agent/context.py +0 -0
  215. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/agent/exports.py +0 -0
  216. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/agent/interaction.py +0 -0
  217. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/agent/sessions.py +0 -0
  218. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/agent/settings.py +0 -0
  219. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/agent/turns.py +0 -0
  220. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/dialogue/__init__.py +0 -0
  221. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/dialogue/ask.py +0 -0
  222. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/dialogue/side.py +0 -0
  223. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/dialogue/title.py +0 -0
  224. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/export/__init__.py +0 -0
  225. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/export/trace/script.js +0 -0
  226. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/export/trace/style.css +0 -0
  227. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/export/trace/template.html +0 -0
  228. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/extensions/__init__.py +0 -0
  229. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/extensions/capabilities.py +0 -0
  230. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/extensions/compaction.py +0 -0
  231. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/extensions/context.py +0 -0
  232. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/extensions/files.py +0 -0
  233. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/extensions/harness.py +0 -0
  234. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/extensions/harness_switch.py +0 -0
  235. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/extensions/mcp.py +0 -0
  236. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/extensions/mcp_notice.py +0 -0
  237. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/extensions/messaging.py +0 -0
  238. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/extensions/relay.py +0 -0
  239. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/extensions/tool_output.py +0 -0
  240. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/extensions/usage.py +0 -0
  241. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/permission/__init__.py +0 -0
  242. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/permission/approval.py +0 -0
  243. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/permission/readonly.py +0 -0
  244. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/pipeline.py +0 -0
  245. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/plugins/extension.py +0 -0
  246. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/plugins/mixins.py +0 -0
  247. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/plugins/services.py +0 -0
  248. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/plugins/state.py +0 -0
  249. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/storage/layout.py +0 -0
  250. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/storage/ledger.py +0 -0
  251. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/storage/locks.py +0 -0
  252. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/storage/metadata.py +0 -0
  253. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/storage/records.py +0 -0
  254. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/storage/store.py +0 -0
  255. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/tools/__init__.py +0 -0
  256. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/engine/tools/images.py +0 -0
  257. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/error_text.py +0 -0
  258. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/filelock.py +0 -0
  259. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/mentions.py +0 -0
  260. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/oneshot.py +0 -0
  261. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/prompt.py +0 -0
  262. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/providers.py +0 -0
  263. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/py.typed +0 -0
  264. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/registry.py +0 -0
  265. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/sdk/handlers.py +0 -0
  266. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/sdk/models.py +0 -0
  267. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/server/__init__.py +0 -0
  268. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/server/cli.py +0 -0
  269. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/server/settings.py +0 -0
  270. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/statusline.py +0 -0
  271. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/__init__.py +0 -0
  272. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/capabilities.py +0 -0
  273. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/clipboard.py +0 -0
  274. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/console.py +0 -0
  275. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/core/__init__.py +0 -0
  276. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/core/app.py +0 -0
  277. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/core/events.py +0 -0
  278. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/core/focus.py +0 -0
  279. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/core/geometry.py +0 -0
  280. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/core/host.py +0 -0
  281. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/core/keymap.py +0 -0
  282. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/core/scheduler.py +0 -0
  283. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/core/screen.py +0 -0
  284. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/core/theme.py +0 -0
  285. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/core/widget.py +0 -0
  286. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/gallery.py +0 -0
  287. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/glyphs.py +0 -0
  288. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/input/__init__.py +0 -0
  289. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/input/bridge.py +0 -0
  290. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/input/decoder.py +0 -0
  291. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/input/events.py +0 -0
  292. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/input/keys.py +0 -0
  293. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/input/reader.py +0 -0
  294. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/layout/__init__.py +0 -0
  295. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/layout/box.py +0 -0
  296. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/layout/overlay.py +0 -0
  297. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/layout/scroll.py +0 -0
  298. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/layout/solver.py +0 -0
  299. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/layout/spacing.py +0 -0
  300. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/render/__init__.py +0 -0
  301. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/render/ansi.py +0 -0
  302. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/render/canvas.py +0 -0
  303. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/render/code.py +0 -0
  304. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/render/color.py +0 -0
  305. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/render/renderer.py +0 -0
  306. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/render/rich_text.py +0 -0
  307. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/render/style.py +0 -0
  308. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/render/sweep.py +0 -0
  309. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/render/text.py +0 -0
  310. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/runner.py +0 -0
  311. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/terminal.py +0 -0
  312. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/testing/__init__.py +0 -0
  313. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/testing/harness.py +0 -0
  314. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/testing/snapshot.py +0 -0
  315. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/theme_file.py +0 -0
  316. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/themes/ayu-mirage.toml +0 -0
  317. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/themes/catppuccin-latte.toml +0 -0
  318. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/themes/catppuccin-mocha.toml +0 -0
  319. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/themes/dracula.toml +0 -0
  320. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/themes/github-dimmed.toml +0 -0
  321. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/themes/github-light.toml +0 -0
  322. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/themes/gruvbox-dark.toml +0 -0
  323. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/themes/gruvbox-light.toml +0 -0
  324. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/themes/kanagawa.toml +0 -0
  325. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/themes/nord.toml +0 -0
  326. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/themes/one-dark.toml +0 -0
  327. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/themes/rose-pine-dawn.toml +0 -0
  328. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/themes/rose-pine.toml +0 -0
  329. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/themes/solarized-dark.toml +0 -0
  330. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/themes/solarized-light.toml +0 -0
  331. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/themes/tokyo-night.toml +0 -0
  332. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/widgets/__init__.py +0 -0
  333. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/widgets/collapsible.py +0 -0
  334. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/widgets/completion.py +0 -0
  335. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/widgets/console.py +0 -0
  336. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/widgets/dialog.py +0 -0
  337. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/widgets/diff.py +0 -0
  338. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/widgets/list.py +0 -0
  339. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/widgets/list_page.py +0 -0
  340. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/widgets/markdown.py +0 -0
  341. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/widgets/progress.py +0 -0
  342. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/widgets/rich_text.py +0 -0
  343. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/widgets/status.py +0 -0
  344. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/widgets/table.py +0 -0
  345. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/widgets/tasks.py +0 -0
  346. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/widgets/text.py +0 -0
  347. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/widgets/textarea.py +0 -0
  348. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/tui/widgets/toast.py +0 -0
  349. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/update.py +0 -0
  350. {zettcode-0.2.0 → zettcode-0.3.1}/src/zettcode/wording.py +0 -0
@@ -24,6 +24,10 @@ docs/.vitepress/.temp/
24
24
  # The TypeScript client is built from its sources; only dist/ is published.
25
25
  clients/typescript/dist/
26
26
 
27
+ # The gateway's page is built from clients/gateway into the package when a distribution is made (`make gateway-ui`);
28
+ # the build output is not kept in the repository. pyproject's `artifacts` puts it in the wheel all the same.
29
+ zettcode/src/zettcode/gateway/static/
30
+
27
31
  # The palette gallery is generated by `make palettes`: every widget painted in
28
32
  # every palette, written by docs/scripts/palette_gallery.py. Regenerate it
29
33
  # rather than committing a 2 MB copy that goes stale the moment a colour moves.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: zettcode
3
- Version: 0.2.0
3
+ Version: 0.3.1
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
@@ -35,7 +35,9 @@ Requires-Dist: truststore<1,>=0.10
35
35
  Requires-Dist: uvicorn<1,>=0.35
36
36
  Requires-Dist: wcwidth<1,>=0.2
37
37
  Requires-Dist: websockets<17,>=15
38
- Requires-Dist: zett-agent<0.3,>=0.2.1
38
+ Requires-Dist: zett-agent<0.3,>=0.2.2
39
+ Provides-Extra: monitor
40
+ Requires-Dist: zettcode-monitor<0.3,>=0.2; extra == 'monitor'
39
41
  Description-Content-Type: text/markdown
40
42
 
41
43
  # ZettCode
@@ -57,13 +57,26 @@ dependencies = [
57
57
  # tiktoken, and httpx behind their first use, so warming a runtime costs less
58
58
  # than it did. An exact pin would make zettcode fight every other tool that
59
59
  # shares the dependency — including an upgrade of zett-agent itself, which pip
60
- # would downgrade to satisfy it.
61
- "zett-agent>=0.2.1,<0.3",
60
+ # would downgrade to satisfy it. 0.2.2 is the floor for one reason this program
61
+ # now leans on: a hosted tool the provider left open is closed with
62
+ # ``output={"status": "not_reported"}`` instead of leaving the step waiting, and
63
+ # `engine/server_tools.describe_output` reads that to say so.
64
+ "zett-agent>=0.2.2,<0.3",
62
65
  ]
63
66
 
67
+ [project.optional-dependencies]
68
+ # A plugin this repository ships, for reading what a session actually sent and received: `pip install
69
+ # "zettcode[monitor]"` adds `/monitor`. It is an extra and not a dependency on purpose — it records every model
70
+ # request and response unredacted, which is a debugging tool and not something to hand everyone — and a hard edge
71
+ # would tie the two projects' releases together: a new minor line of zettcode would need a monitor published first
72
+ # that accepts it (its range has to allow both lines in between), or `pip install zettcode==<new>` would fail to
73
+ # resolve, and so would the environment the VS Code extension builds for itself.
74
+ monitor = ["zettcode-monitor>=0.2,<0.3"]
75
+
64
76
  [project.scripts]
65
77
  zettcode = "zettcode.cli:main"
66
78
  zettcode-server = "zettcode.server.cli:main"
79
+ zettcode-gateway = "zettcode.gateway.cli:main"
67
80
 
68
81
  [project.urls]
69
82
  Repository = "https://github.com/Chang-LeHung/zettcode"
@@ -77,6 +90,11 @@ build-backend = "hatchling.build"
77
90
  # Single source of truth: the version is written once, in the package.
78
91
  path = "src/zettcode/__init__.py"
79
92
 
93
+ # The gateway's page is built (make gateway-ui) into the package and is not in git; hatchling skips what git ignores
94
+ # unless it is named here.
95
+ [tool.hatch.build]
96
+ artifacts = ["src/zettcode/gateway/static/**"]
97
+
80
98
  [tool.hatch.build.targets.wheel]
81
99
  packages = ["src/zettcode"]
82
100
 
@@ -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.2.0"
10
+ __version__ = "0.3.1"
11
11
 
12
12
  #: Where each public name lives; read by :func:`__getattr__` on first use.
13
13
  _EXPORTS = {
@@ -10,7 +10,7 @@ from __future__ import annotations
10
10
  from zettcode.app.commands import Command, CommandContext, CommandHost, CommandResult
11
11
  from zettcode.app.pets import PetCatalog, bundled_pets
12
12
  from zettcode.app.plugins.host import HOST_LINK, HostLink
13
- from zettcode.app.plugins.plugin import TerminalPlugins
13
+ from zettcode.app.plugins.plugin import TuiPlugins
14
14
  from zettcode.app.plugins.state import TurnSegment, UiRow
15
15
  from zettcode.engine.agent.exports import ExportsMixin
16
16
  from zettcode.providers import BUILTIN_PROVIDERS, Provider, merge_providers
@@ -28,10 +28,10 @@ class CommandsMixin(AppearanceMixin, ExportsMixin):
28
28
  """The ``/new``, ``/compact``, and ``/export`` commands, and the plugins' commands, mentions, and rows."""
29
29
 
30
30
  @property
31
- def terminal_plugins(self) -> TerminalPlugins:
31
+ def terminal_plugins(self) -> TuiPlugins:
32
32
  """Return what the plugins contributed to the terminal; the engine was built with the terminal's loader."""
33
33
  plugins = self.runtime.plugins
34
- if not isinstance(plugins, TerminalPlugins):
34
+ if not isinstance(plugins, TuiPlugins):
35
35
  raise TypeError("This agent's engine was not built with the terminal's plugin loader")
36
36
  return plugins
37
37
 
@@ -59,7 +59,7 @@ def is_picture_path(path: str) -> bool:
59
59
 
60
60
  #: The hint a bounded read leaves behind; it is advice for the model, not the
61
61
  #: reader, and the row already shows the text it applies to.
62
- CONTINUATION = re.compile(r"\n?\.\.\. \[(?:line truncated|more lines)[^\]]*\]\s*\Z")
62
+ CONTINUATION = re.compile(r"\n?\.\.\. \[(?:line truncated|output truncated|more lines)[^\]]*\]\s*\Z")
63
63
 
64
64
  #: Wrapper tags a model may leave around its reasoning channel.
65
65
  REASONING_TAGS = re.compile(r"</?(?:think(?:ing)?|reasoning)>|<\|(?:thinking|reasoning)\|>", re.IGNORECASE)
@@ -229,38 +229,48 @@ def argument(arguments: Mapping[str, object], key: str, default: str = "") -> st
229
229
  return default
230
230
 
231
231
 
232
- def edited_lines(edits: object) -> tuple[int, int]:
232
+ def edit_changes(arguments: Mapping[str, object]) -> tuple[tuple[str, str], ...]:
233
+ """Return the text pairs an ``edit`` call replaces: one pair now, a list of them from an older transcript.
234
+
235
+ A call carries ``old_string`` and ``new_string``. A session stored before that shape gave the arguments an
236
+ ``edits`` list, and it is read the same way so the row of an old transcript still shows its diff. Anything
237
+ that is not two strings is dropped rather than guessed at.
238
+ """
239
+ legacy = arguments.get("edits")
240
+ if isinstance(legacy, Sequence) and not isinstance(legacy, str):
241
+ return tuple(
242
+ (edit["old_text"], edit["new_text"])
243
+ for edit in legacy
244
+ if isinstance(edit, Mapping)
245
+ and isinstance(edit.get("old_text"), str)
246
+ and isinstance(edit.get("new_text"), str)
247
+ )
248
+ old_string, new_string = arguments.get("old_string"), arguments.get("new_string")
249
+ if isinstance(old_string, str) and isinstance(new_string, str):
250
+ return ((old_string, new_string),)
251
+ return ()
252
+
253
+
254
+ def edited_lines(changes: Sequence[tuple[str, str]]) -> tuple[int, int]:
233
255
  """Return the lines an ``edit`` call adds and removes.
234
256
 
235
- The tool answers ``Replaced 2 occurrences in app.py``, which says how many
236
- edits landed but not how large they were: a two-line fix and a two-hundred-line
237
- rewrite read the same. The edits carry both texts, so the difference is counted
238
- here — with :class:`difflib.SequenceMatcher` rather than by subtracting lengths,
239
- because a replacement that keeps the lines around it should not read as a
240
- rewrite, and that is what a reader comparing two rows is looking at.
257
+ The tool answers ``Replaced 1 occurrence in app.py``, which says that a change landed but not how large it was:
258
+ a two-line fix and a two-hundred-line rewrite read the same. The call carries both texts, so the difference is
259
+ counted here — with :class:`difflib.SequenceMatcher` rather than by subtracting lengths, because a replacement
260
+ that keeps the lines around it should not read as a rewrite, and that is what a reader comparing two rows is
261
+ looking at.
241
262
 
242
263
  Args:
243
- edits: The call's ``edits`` argument, as the model sent it: a sequence of
244
- mappings with ``old_text`` and ``new_text``. Anything else is ignored
245
- rather than guessed at.
264
+ changes: The old and new text of each change, as :func:`edit_changes` reads them.
246
265
 
247
266
  Returns:
248
- ``(added, removed)`` across every edit; ``(0, 0)`` when there is nothing
249
- countable, which is also what a call with no edits at all answers.
267
+ ``(added, removed)`` across every change; ``(0, 0)`` when the call carried nothing countable.
250
268
 
251
- An edit that replaces every occurrence counts once, not once per occurrence:
252
- how many the file holds is something only the file knows, and the tool's own
253
- answer is where that number appears.
269
+ A change that replaces every occurrence counts once, not once per occurrence: how many the file holds is
270
+ something only the file knows, and the tool's own answer is where that number appears.
254
271
  """
255
- if not isinstance(edits, Sequence) or isinstance(edits, str):
256
- return (0, 0)
257
272
  added = removed = 0
258
- for edit in edits:
259
- if not isinstance(edit, Mapping):
260
- continue
261
- old_text, new_text = edit.get("old_text"), edit.get("new_text")
262
- if not isinstance(old_text, str) or not isinstance(new_text, str):
263
- continue
273
+ for old_text, new_text in changes:
264
274
  matcher = difflib.SequenceMatcher(None, old_text.splitlines(), new_text.splitlines())
265
275
  for tag, old_start, old_end, new_start, new_end in matcher.get_opcodes():
266
276
  if tag in ("replace", "delete"):
@@ -270,31 +280,24 @@ def edited_lines(edits: object) -> tuple[int, int]:
270
280
  return (added, removed)
271
281
 
272
282
 
273
- def edit_diff(edits: object, *, limit: int = MAX_DIFF_ROWS) -> tuple[DiffRow, ...]:
283
+ def edit_diff(changes: Sequence[tuple[str, str]], *, limit: int = MAX_DIFF_ROWS) -> tuple[DiffRow, ...]:
274
284
  """Return the rows of the change an ``edit`` call makes.
275
285
 
276
- Each edit carries the text it replaces and the text it puts there, so the change can be shown
286
+ Each change carries the text it replaces and the text it puts there, so the change can be shown
277
287
  without reading the file: the lines that differ, and one unchanged line around each so it reads in
278
- place. Edits are told apart by a labelled break when there is more than one, and what is kept is
279
- cut at ``limit`` rows with a last row saying so, because the arguments can be a whole file.
288
+ place. Changes are told apart by a labelled break when there is more than one — which only an older
289
+ transcript has — and what is kept is cut at ``limit`` rows with a last row saying so, because the
290
+ arguments can be a whole file.
280
291
 
281
292
  Args:
282
- edits: The call's ``edits`` argument as the model sent it; anything that is not a mapping with
283
- two texts is skipped rather than guessed at, exactly as :func:`edited_lines` does.
293
+ changes: The old and new text of each change, as :func:`edit_changes` reads them.
284
294
  limit: Most rows to keep.
285
295
  """
286
- if not isinstance(edits, Sequence) or isinstance(edits, str):
287
- return ()
288
- usable = [
289
- edit
290
- for edit in edits
291
- if isinstance(edit, Mapping) and isinstance(edit.get("old_text"), str) and isinstance(edit.get("new_text"), str)
292
- ]
293
296
  rows: list[DiffRow] = []
294
- for number, edit in enumerate(usable, start=1):
295
- if len(usable) > 1:
296
- rows.append(("@", f"edit {number} of {len(usable)}"))
297
- old_lines, new_lines = edit["old_text"].splitlines(), edit["new_text"].splitlines()
297
+ for number, (old_text, new_text) in enumerate(changes, start=1):
298
+ if len(changes) > 1:
299
+ rows.append(("@", f"edit {number} of {len(changes)}"))
300
+ old_lines, new_lines = old_text.splitlines(), new_text.splitlines()
298
301
  opcodes = difflib.SequenceMatcher(None, old_lines, new_lines).get_opcodes()
299
302
  for index, (tag, old_start, old_end, new_start, new_end) in enumerate(opcodes):
300
303
  if tag == "equal":
@@ -382,6 +385,15 @@ def _value_text(value: object) -> str:
382
385
  return str(value)
383
386
 
384
387
 
388
+ def file_path(arguments: Mapping[str, object], default: str = "") -> str:
389
+ """Return the path a file tool was called for.
390
+
391
+ ``file_path`` is the argument now; a session stored before the rename carries ``path``, and is still shown the
392
+ file it changed rather than as an unknown one.
393
+ """
394
+ return argument(arguments, "file_path") or argument(arguments, "path") or default
395
+
396
+
385
397
  def language_for_path(path: str) -> str | None:
386
398
  """Return the scanner for a file's contents, or ``None`` when it is not code."""
387
399
  language = language_for(Path(path).suffix.lstrip("."))
@@ -395,7 +407,7 @@ class ReadTool(ToolHandler):
395
407
 
396
408
  def describe(self, name: str, arguments: Mapping[str, object]) -> ToolRow:
397
409
  """Show the file, and highlight the body as the language it is."""
398
- path = argument(arguments, "path", "(unknown file)")
410
+ path = file_path(arguments, "(unknown file)")
399
411
  return ToolRow(f"Read {path}", language_for_path(path))
400
412
 
401
413
  def open_tool(
@@ -403,7 +415,7 @@ class ReadTool(ToolHandler):
403
415
  ) -> ToolEntry:
404
416
  """A read row, which knows the language its body is in, and whether the file is a picture."""
405
417
  row = self.describe(name, arguments)
406
- path = argument(arguments, "path")
418
+ path = file_path(arguments)
407
419
  return ReadEntry(
408
420
  id=id,
409
421
  call_id=call_id,
@@ -437,7 +449,7 @@ class WriteTool(ToolHandler):
437
449
 
438
450
  def describe(self, name: str, arguments: Mapping[str, object]) -> ToolRow:
439
451
  """Show the file being written: the row is the call, so it says what the call does, not what it did."""
440
- return ToolRow(f"Write {argument(arguments, 'path', '(unknown file)')}")
452
+ return ToolRow(f"Write {file_path(arguments, '(unknown file)')}")
441
453
 
442
454
  def open_tool(
443
455
  self, name: str, arguments: Mapping[str, object], *, id: int, call_id: str, started_at: float
@@ -449,7 +461,7 @@ class WriteTool(ToolHandler):
449
461
  call_id=call_id,
450
462
  tool=name,
451
463
  title=self.describe(name, arguments).title,
452
- path=argument(arguments, "path"),
464
+ path=file_path(arguments),
453
465
  added=written_lines(content),
454
466
  diff=write_diff(content),
455
467
  started_at=started_at,
@@ -457,25 +469,21 @@ class WriteTool(ToolHandler):
457
469
 
458
470
 
459
471
  class EditTool(ToolHandler):
460
- """``edit``: the file and how many edits the call carries.
472
+ """``edit``: the file, and the size of the one change the call makes.
461
473
 
462
- A session stored before the tool was renamed calls it ``replace_in_file``, and is still shown the same way.
474
+ A session stored before the tool was renamed calls it ``replace_in_file``, and is still shown the same way; one
475
+ stored before a call carried a single change gave it a list under ``edits``, and that list is read too.
463
476
  """
464
477
 
465
478
  names = frozenset({"edit", "replace_in_file"})
466
479
 
467
480
  def describe(self, name: str, arguments: Mapping[str, object]) -> ToolRow:
468
- """Count the edits, and the lines they add and remove, before they land.
481
+ """Show the file; the size of the change is drawn beside the title, before it lands.
469
482
 
470
- A multi-edit call and a one-line call are both worth seeing in advance, and
471
- so is the size of the change: `+12 -3` beside the file says whether this is
472
- a tweak or a rewrite, which is the question a reader asks the row.
483
+ `+12 -3` beside the file says whether this is a tweak or a rewrite, which is the question a reader asks
484
+ the row. One change a call is why the title counts nothing: a count of one is noise on every row.
473
485
  """
474
- path = argument(arguments, "path", "(unknown file)")
475
- edits = arguments.get("edits")
476
- count = len(edits) if isinstance(edits, Sequence) and not isinstance(edits, str) else 0
477
- suffix = f" ({count} edit{'' if count == 1 else 's'})" if count else ""
478
- return ToolRow(f"Edited {path}{suffix}")
486
+ return ToolRow(f"Edit {file_path(arguments, '(unknown file)')}")
479
487
 
480
488
  def open_tool(
481
489
  self, name: str, arguments: Mapping[str, object], *, id: int, call_id: str, started_at: float
@@ -485,17 +493,17 @@ class EditTool(ToolHandler):
485
493
  The counts ride outside the title: they are drawn in the colours an addition and a
486
494
  deletion get, which a title made of one string cannot be.
487
495
  """
488
- edits = arguments.get("edits")
489
- added, removed = edited_lines(edits)
496
+ changes = edit_changes(arguments)
497
+ added, removed = edited_lines(changes)
490
498
  return EditEntry(
491
499
  id=id,
492
500
  call_id=call_id,
493
501
  tool=name,
494
502
  title=self.describe(name, arguments).title,
495
- path=argument(arguments, "path"),
503
+ path=file_path(arguments),
496
504
  added=added,
497
505
  removed=removed,
498
- diff=edit_diff(edits),
506
+ diff=edit_diff(changes),
499
507
  started_at=started_at,
500
508
  )
501
509
 
@@ -507,7 +515,7 @@ class DeleteTool(ToolHandler):
507
515
 
508
516
  def describe(self, name: str, arguments: Mapping[str, object]) -> ToolRow:
509
517
  """Show the file being removed."""
510
- return ToolRow(f"Deleted {argument(arguments, 'path', '(unknown file)')}")
518
+ return ToolRow(f"Deleted {file_path(arguments, '(unknown file)')}")
511
519
 
512
520
  def open_tool(
513
521
  self, name: str, arguments: Mapping[str, object], *, id: int, call_id: str, started_at: float
@@ -518,19 +526,21 @@ class DeleteTool(ToolHandler):
518
526
  call_id=call_id,
519
527
  tool=name,
520
528
  title=self.describe(name, arguments).title,
521
- path=argument(arguments, "path"),
529
+ path=file_path(arguments),
522
530
  started_at=started_at,
523
531
  )
524
532
 
525
533
 
526
534
  class ShellTool(ToolHandler):
527
- """``bash``: the command itself, which is the whole story."""
535
+ """``bash``: the command itself, and the directory it runs in when the call names one."""
528
536
 
529
537
  names = frozenset({"bash"})
530
538
 
531
539
  def describe(self, name: str, arguments: Mapping[str, object]) -> ToolRow:
532
- """Show the command, not a JSON copy of it."""
533
- return ToolRow(f"Ran {argument(arguments, 'command', '(unknown command)')}")
540
+ """Show the command, not a JSON copy of it, and where it runs when that is not the workspace."""
541
+ command = argument(arguments, "command", "(unknown command)")
542
+ where = argument(arguments, "work_dir")
543
+ return ToolRow(f"Bash {command}" + (f" {SEPARATOR} {where}" if where else ""))
534
544
 
535
545
 
536
546
  class SearchTool(ToolHandler):
@@ -543,7 +553,7 @@ class SearchTool(ToolHandler):
543
553
  pattern = argument(arguments, "pattern")
544
554
  if name == "glob":
545
555
  return ToolRow(f"Listed {pattern}" if pattern else "Listed files")
546
- where = argument(arguments, "path")
556
+ where = argument(arguments, "file_pattern") or argument(arguments, "path")
547
557
  return ToolRow(f"Searched {pattern or 'the workspace'}" + (f" in {where}" if where else ""))
548
558
 
549
559
 
@@ -252,7 +252,7 @@ class Command:
252
252
  its sessions, or the model, and is handled by
253
253
  :class:`~zettcode.app.agent.TerminalAgent`; ``"plugin"`` comes from
254
254
  an installed plugin, registered through
255
- :class:`~zettcode.app.plugins.TerminalContainer`.
255
+ :class:`~zettcode.engine.plugins.context.PluginContext`.
256
256
  handler: Runs the command with a :class:`CommandContext` — the trimmed
257
257
  argument and the UI surfaces it may write to while it works — and
258
258
  returns a :class:`CommandResult` describing anything left to show.
@@ -7,7 +7,7 @@ it, moves it, and picks the mood.
7
7
 
8
8
  Pets are offered the way palettes are: a :class:`PetSource` names one and builds it only when it is asked for,
9
9
  the built-in plugin offers the ones that ship, and any other plugin offers its own through
10
- ``PluginContainer.register_pet``. :class:`PetCatalog` is what ``/pet`` and the start-up read.
10
+ ``ctx.tui.register_pet``. :class:`PetCatalog` is what ``/pet`` and the start-up read.
11
11
 
12
12
  Every frame of a pet is the same size, and its height is an even number of pixel rows: two pixels fit in
13
13
  one terminal cell.
@@ -1,22 +1,20 @@
1
- """The terminal's plugin support: what a plugin adds beyond the agent.
1
+ """The terminal's plugins: the row hooks, the groups a shell plugin registers into, and the bundle it produces.
2
2
 
3
- A plugin that adds a command, a palette, a pet, a presenter or a header or status segment::
3
+ A plugin subclasses :class:`~zettcode.engine.plugins.Plugin` (or :class:`TuiPlugin` to override a row hook) and
4
+ registers through ``ctx``::
4
5
 
5
- from zettcode.app.plugins import CommandResult, TerminalContainer, TerminalPlugin
6
+ from zettcode.app.plugins import TuiPlugin
7
+ from zettcode.engine.plugins import PluginContext
6
8
 
7
- class Greeter(TerminalPlugin):
9
+ class Greeter(TuiPlugin):
8
10
  name = "greeter"
9
11
 
10
- def activate(self, container: TerminalContainer) -> None:
11
- container.register_command("greet", "say hello", self.greet)
12
+ def activate(self, ctx: PluginContext) -> None:
13
+ ctx.tui.register_command("greet", "say hello", self.greet)
12
14
 
13
- async def greet(self, context) -> CommandResult:
14
- return CommandResult(notification=f"hello {context.argument}".strip())
15
-
16
- It is built on :mod:`zettcode.engine.plugins`, which has what the agent uses. The pieces live in
17
- :mod:`~zettcode.app.plugins.state` (the snapshot and segment types), :mod:`~zettcode.app.plugins.mixins` (the row
18
- hooks), :mod:`~zettcode.app.plugins.container`, :mod:`~zettcode.app.plugins.plugin`, and
19
- :mod:`~zettcode.app.plugins.loader`. Names load on first use, so importing one does not build the rest.
15
+ :class:`TuiPlugin` is the engine's base class with :class:`PluginUiMixin`; the shell's commands, palettes, pets,
16
+ presenters and rows live in :class:`~zettcode.app.plugins.groups.TuiGroups`, which is ``ctx.tui`` — ``None`` in a
17
+ process without a terminal, which is why a shell-only plugin starts ``activate`` with ``if ctx.tui is None: return``.
20
18
  """
21
19
 
22
20
  from __future__ import annotations
@@ -39,10 +37,11 @@ _EXPORTS = {
39
37
  "UiSegment": ".state",
40
38
  "UiSide": ".state",
41
39
  "PluginUiMixin": ".mixins",
42
- "TerminalContainer": ".container",
43
- "UiSlots": ".container",
44
- "TerminalPlugin": ".plugin",
45
- "TerminalPlugins": ".plugin",
40
+ "PluginContext": "zettcode.engine.plugins.context",
41
+ "UiSlots": ".groups",
42
+ "TuiGroups": ".groups",
43
+ "TuiPlugin": ".plugin",
44
+ "TuiPlugins": ".plugin",
46
45
  "TerminalLoader": ".loader",
47
46
  "load_terminal_plugins": ".loader",
48
47
  "HOST_LINK": ".host",
@@ -51,12 +50,14 @@ _EXPORTS = {
51
50
 
52
51
 
53
52
  if TYPE_CHECKING: # pragma: no cover - for type checkers, not the runtime
53
+ from zettcode.engine.plugins.context import PluginContext
54
+
54
55
  from .builtins import BUILTIN_PLUGINS
55
- from .container import TerminalContainer, UiSlots
56
+ from .groups import UiSlots
56
57
  from .host import HOST_LINK, HostLink
57
58
  from .loader import TerminalLoader, load_terminal_plugins
58
59
  from .mixins import PluginUiMixin
59
- from .plugin import TerminalPlugin, TerminalPlugins
60
+ from .plugin import TuiPlugin, TuiPlugins
60
61
  from .state import (
61
62
  Activity,
62
63
  ActivityState,
@@ -95,12 +96,13 @@ __all__ = [
95
96
  "HostLink",
96
97
  "ModelState",
97
98
  "PluginUiMixin",
99
+ "TuiGroups",
98
100
  "SessionState",
99
101
  "ShellContext",
100
- "TerminalContainer",
102
+ "PluginContext",
101
103
  "TerminalLoader",
102
- "TerminalPlugin",
103
- "TerminalPlugins",
104
+ "TuiPlugin",
105
+ "TuiPlugins",
104
106
  "UiBuilder",
105
107
  "UiRegion",
106
108
  "UiRow",