zettcode 0.3.0__tar.gz → 0.3.2__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 (370) hide show
  1. {zettcode-0.3.0 → zettcode-0.3.2}/PKG-INFO +9 -2
  2. {zettcode-0.3.0 → zettcode-0.3.2}/pyproject.toml +15 -2
  3. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/__init__.py +1 -1
  4. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/agent/commands.py +41 -5
  5. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/entries.py +4 -0
  6. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/presenters.py +4 -0
  7. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/projection.py +56 -7
  8. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/rendering.py +38 -1
  9. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/text.py +8 -5
  10. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/tips.py +1 -1
  11. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/transcript/oneoff.py +2 -2
  12. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/transcript/stream.py +11 -2
  13. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/transcript/turn.py +10 -3
  14. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/commands.py +23 -1
  15. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/plugins/builtin_commands.py +57 -69
  16. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/plugins/builtins.py +4 -1
  17. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/plugins/groups.py +51 -0
  18. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/plugins/loader.py +36 -11
  19. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/plugins/plugin.py +7 -0
  20. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/plugins/state.py +4 -2
  21. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/app.py +272 -29
  22. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/bars.py +11 -7
  23. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/boot.py +8 -3
  24. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/commands.py +78 -58
  25. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/followups.py +17 -13
  26. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/keys.py +21 -3
  27. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/labels.py +19 -3
  28. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/messaging.py +57 -4
  29. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/notices.py +2 -1
  30. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/providers.py +21 -10
  31. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/sessions.py +8 -4
  32. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/settings.py +3 -3
  33. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/shell.py +43 -1
  34. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/__init__.py +8 -0
  35. zettcode-0.3.2/src/zettcode/app/ui/widgets/document_page.py +159 -0
  36. zettcode-0.3.2/src/zettcode/app/ui/widgets/harness_page.py +47 -0
  37. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/plugins_page.py +3 -3
  38. zettcode-0.3.2/src/zettcode/app/ui/widgets/sessions_page.py +512 -0
  39. zettcode-0.3.2/src/zettcode/broker/cli.py +236 -0
  40. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/broker/client.py +169 -19
  41. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/broker/files.py +2 -0
  42. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/broker/finder.py +2 -5
  43. zettcode-0.3.2/src/zettcode/broker/hub.py +723 -0
  44. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/broker/models.py +46 -2
  45. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/broker/process.py +2 -49
  46. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/broker/protocol.py +74 -16
  47. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/broker/runner.py +33 -5
  48. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/broker/server.py +5 -7
  49. zettcode-0.3.2/src/zettcode/broker/store.py +330 -0
  50. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/broker/web.py +67 -1
  51. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/cli.py +134 -6
  52. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/config.py +121 -36
  53. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/config_edit.py +73 -15
  54. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/daemon/__init__.py +4 -1
  55. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/daemon/cli.py +281 -42
  56. zettcode-0.3.2/src/zettcode/daemon/companions.py +62 -0
  57. zettcode-0.3.2/src/zettcode/daemon/process.py +486 -0
  58. zettcode-0.3.2/src/zettcode/daemon/run.py +639 -0
  59. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/daemon/state.py +53 -0
  60. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/agent/context.py +30 -6
  61. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/agent/exports.py +8 -2
  62. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/agent/sessions.py +1 -1
  63. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/agent/settings.py +33 -14
  64. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/agent/turns.py +30 -3
  65. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/core.py +62 -11
  66. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/dialogue/side.py +1 -1
  67. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/export/__init__.py +0 -5
  68. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/extensions/compaction.py +74 -3
  69. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/extensions/harness.py +11 -2
  70. zettcode-0.3.2/src/zettcode/engine/extensions/messaging.py +279 -0
  71. zettcode-0.3.2/src/zettcode/engine/extensions/usage.py +348 -0
  72. zettcode-0.3.2/src/zettcode/engine/harness_paths.py +74 -0
  73. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/messenger.py +38 -9
  74. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/plugins/__init__.py +1 -2
  75. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/plugins/builtin_agent.py +41 -10
  76. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/plugins/commands.py +22 -1
  77. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/plugins/context.py +6 -1
  78. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/plugins/groups.py +0 -6
  79. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/plugins/loader.py +107 -20
  80. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/plugins/plugin.py +2 -2
  81. zettcode-0.3.2/src/zettcode/engine/plugins/settings.py +117 -0
  82. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/plugins/switches.py +216 -75
  83. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/storage/store.py +95 -30
  84. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/__init__.py +2 -2
  85. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/app.py +42 -2
  86. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/backend.py +57 -2
  87. zettcode-0.3.2/src/zettcode/gateway/cli.py +494 -0
  88. zettcode-0.3.2/src/zettcode/gateway/runfile.py +145 -0
  89. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/service.py +205 -23
  90. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/app.css +617 -20
  91. zettcode-0.3.2/src/zettcode/gateway/static/app.js +3 -0
  92. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/index.html +1 -1
  93. zettcode-0.3.2/src/zettcode/logging_setup.py +196 -0
  94. zettcode-0.3.2/src/zettcode/modes.py +18 -0
  95. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/paths.py +36 -0
  96. zettcode-0.3.2/src/zettcode/plugin_cli.py +742 -0
  97. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/plugin_tui.py +29 -0
  98. zettcode-0.3.2/src/zettcode/processes.py +126 -0
  99. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/relay.py +24 -2
  100. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/sdk/__init__.py +3 -0
  101. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/sdk/client.py +109 -14
  102. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/sdk/daemons.py +62 -2
  103. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/sdk/models.py +10 -12
  104. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/sdk/session.py +70 -13
  105. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/sdk/translation.py +1 -0
  106. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/sdk/types.py +49 -9
  107. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/server/app.py +20 -11
  108. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/server/runner.py +108 -87
  109. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/server/schemas.py +14 -2
  110. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/server/serialize.py +1 -0
  111. zettcode-0.3.2/src/zettcode/table.py +95 -0
  112. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/core/app.py +92 -0
  113. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/input/bridge.py +3 -0
  114. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/input/decoder.py +11 -0
  115. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/input/events.py +5 -0
  116. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/layout/scroll.py +82 -7
  117. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/runner.py +4 -1
  118. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/terminal.py +12 -1
  119. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/widgets/markdown.py +17 -8
  120. zettcode-0.3.0/src/zettcode/app/ui/widgets/sessions_page.py +0 -268
  121. zettcode-0.3.0/src/zettcode/broker/hub.py +0 -349
  122. zettcode-0.3.0/src/zettcode/daemon/process.py +0 -270
  123. zettcode-0.3.0/src/zettcode/daemon/run.py +0 -343
  124. zettcode-0.3.0/src/zettcode/engine/extensions/messaging.py +0 -130
  125. zettcode-0.3.0/src/zettcode/engine/extensions/usage.py +0 -184
  126. zettcode-0.3.0/src/zettcode/gateway/cli.py +0 -138
  127. zettcode-0.3.0/src/zettcode/gateway/static/app.js +0 -3
  128. {zettcode-0.3.0 → zettcode-0.3.2}/.gitignore +0 -0
  129. {zettcode-0.3.0 → zettcode-0.3.2}/README.md +0 -0
  130. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/_compat.py +0 -0
  131. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/__init__.py +0 -0
  132. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/__init__.py +0 -0
  133. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/agent/__init__.py +0 -0
  134. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/agent/settings.py +0 -0
  135. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/__init__.py +0 -0
  136. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/changes.py +0 -0
  137. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/effort_gauge.py +0 -0
  138. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/pulse.py +0 -0
  139. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/replay.py +0 -0
  140. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/shimmer.py +0 -0
  141. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/stops.py +0 -0
  142. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/transcript/__init__.py +0 -0
  143. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/transcript/fold.py +0 -0
  144. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/agent/conversation/transcript/log.py +0 -0
  145. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/brand.py +0 -0
  146. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/commandline/__init__.py +0 -0
  147. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/commandline/arguments.py +0 -0
  148. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/commandline/completion.py +0 -0
  149. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/commandline/history.py +0 -0
  150. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/commandline/runner.py +0 -0
  151. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/pets/__init__.py +0 -0
  152. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/pets/cat.py +0 -0
  153. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/pets/chibi.py +0 -0
  154. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/pets/dog.py +0 -0
  155. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/pets/kangaroo.py +0 -0
  156. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/pets/model.py +0 -0
  157. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/pets/penguin.py +0 -0
  158. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/pets/polarbear.py +0 -0
  159. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/pets/scarfy.py +0 -0
  160. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/pets/seagull.py +0 -0
  161. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/pets/slime.py +0 -0
  162. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/pets/snowbear.py +0 -0
  163. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/pets/sprout.py +0 -0
  164. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/pets/tiger.py +0 -0
  165. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/pets/whale.py +0 -0
  166. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/plugins/__init__.py +0 -0
  167. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/plugins/host.py +0 -0
  168. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/plugins/mixins.py +0 -0
  169. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/__init__.py +0 -0
  170. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/clipboard.py +0 -0
  171. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/config_form.py +0 -0
  172. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/demo.py +0 -0
  173. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/gc_policy.py +0 -0
  174. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/turns.py +0 -0
  175. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/updating.py +0 -0
  176. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/approval_page.py +0 -0
  177. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/ask_page.py +0 -0
  178. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/bash_page.py +0 -0
  179. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/completer.py +0 -0
  180. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/composer.py +0 -0
  181. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/context_charts.py +0 -0
  182. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/context_page.py +0 -0
  183. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/effort_page.py +0 -0
  184. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/follow_up.py +0 -0
  185. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/form_page.py +0 -0
  186. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/input_page.py +0 -0
  187. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/model_page.py +0 -0
  188. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/model_settings_page.py +0 -0
  189. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/panel.py +0 -0
  190. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/pet.py +0 -0
  191. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/pet_page.py +0 -0
  192. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/root.py +0 -0
  193. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/self_compact_page.py +0 -0
  194. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/send_kind_page.py +0 -0
  195. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/statusline_page.py +0 -0
  196. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/steering.py +0 -0
  197. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/switch.py +0 -0
  198. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/transcript.py +0 -0
  199. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/update_page.py +0 -0
  200. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/usage_charts.py +0 -0
  201. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/usage_page.py +0 -0
  202. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/app/ui/widgets/welcome.py +0 -0
  203. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/broker/__init__.py +0 -0
  204. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/broker/__main__.py +0 -0
  205. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/broker/log.py +0 -0
  206. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/broker/roster.py +0 -0
  207. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/broker/text.py +0 -0
  208. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/budget.py +0 -0
  209. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/collector.py +0 -0
  210. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/daemon/__main__.py +0 -0
  211. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/doctor.py +0 -0
  212. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/__init__.py +0 -0
  213. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/agent/__init__.py +0 -0
  214. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/agent/base.py +0 -0
  215. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/agent/interaction.py +0 -0
  216. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/dialogue/__init__.py +0 -0
  217. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/dialogue/ask.py +0 -0
  218. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/dialogue/title.py +0 -0
  219. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/export/trace/script.js +0 -0
  220. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/export/trace/style.css +0 -0
  221. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/export/trace/template.html +0 -0
  222. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/extensions/__init__.py +0 -0
  223. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/extensions/capabilities.py +0 -0
  224. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/extensions/context.py +0 -0
  225. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/extensions/files.py +0 -0
  226. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/extensions/harness_switch.py +0 -0
  227. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/extensions/mcp.py +0 -0
  228. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/extensions/mcp_notice.py +0 -0
  229. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/extensions/relay.py +0 -0
  230. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/extensions/subagents.py +0 -0
  231. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/extensions/tool_output.py +0 -0
  232. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/extensions/truncation.py +0 -0
  233. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/permission/__init__.py +0 -0
  234. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/permission/approval.py +0 -0
  235. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/permission/readonly.py +0 -0
  236. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/permission/sandbox.py +0 -0
  237. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/pipeline.py +0 -0
  238. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/plugins/extension.py +0 -0
  239. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/plugins/mixins.py +0 -0
  240. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/plugins/services.py +0 -0
  241. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/plugins/state.py +0 -0
  242. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/server_tools.py +0 -0
  243. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/storage/__init__.py +0 -0
  244. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/storage/layout.py +0 -0
  245. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/storage/ledger.py +0 -0
  246. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/storage/locks.py +0 -0
  247. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/storage/metadata.py +0 -0
  248. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/storage/records.py +0 -0
  249. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/tools/__init__.py +0 -0
  250. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/tools/boundary.py +0 -0
  251. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/tools/files.py +0 -0
  252. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/tools/images.py +0 -0
  253. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/tools/output.py +0 -0
  254. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/tools/shell.py +0 -0
  255. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/engine/tools/workspace.py +0 -0
  256. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/error_text.py +0 -0
  257. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/filelock.py +0 -0
  258. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/chat.py +0 -0
  259. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/settings.py +0 -0
  260. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_AMS-Regular-U6PRYMIZ.woff2 +0 -0
  261. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_Caligraphic-Bold-5QL5CMTE.woff2 +0 -0
  262. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_Caligraphic-Regular-KX5MEWCF.woff2 +0 -0
  263. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_Fraktur-Bold-2QVFK6NQ.woff2 +0 -0
  264. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_Fraktur-Regular-2PEIFJSJ.woff2 +0 -0
  265. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_Main-Bold-YP5VVQRP.woff2 +0 -0
  266. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_Main-BoldItalic-N4V3DX7S.woff2 +0 -0
  267. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_Main-Italic-RELBIK7M.woff2 +0 -0
  268. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_Main-Regular-ARRPAO67.woff2 +0 -0
  269. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_Math-BoldItalic-K4WTGH3J.woff2 +0 -0
  270. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_Math-Italic-6KGCHLFN.woff2 +0 -0
  271. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_SansSerif-Bold-RRNVJFFW.woff2 +0 -0
  272. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_SansSerif-Italic-HMPFTM52.woff2 +0 -0
  273. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_SansSerif-Regular-XIQ62X4E.woff2 +0 -0
  274. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_Script-Regular-APUWIHLP.woff2 +0 -0
  275. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_Size1-Regular-5LRUTBFT.woff2 +0 -0
  276. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_Size2-Regular-LELKET5D.woff2 +0 -0
  277. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_Size3-Regular-WQRQ47UD.woff2 +0 -0
  278. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_Size4-Regular-CDMV7U5C.woff2 +0 -0
  279. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/assets/KaTeX_Typewriter-Regular-VBYJ4NRC.woff2 +0 -0
  280. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/chunks/chunk-Y6SLVHK3.js +0 -0
  281. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/chunks/render-G6G3TSB3.js +0 -0
  282. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/icon.png +0 -0
  283. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/logo.svg +0 -0
  284. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/gateway/static/manifest.webmanifest +0 -0
  285. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/mentions.py +0 -0
  286. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/oneshot.py +0 -0
  287. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/prompt.py +0 -0
  288. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/providers.py +0 -0
  289. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/py.typed +0 -0
  290. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/registry.py +0 -0
  291. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/sdk/commands.py +0 -0
  292. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/sdk/errors.py +0 -0
  293. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/sdk/events.py +0 -0
  294. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/sdk/handlers.py +0 -0
  295. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/server/__init__.py +0 -0
  296. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/server/cli.py +0 -0
  297. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/server/settings.py +0 -0
  298. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/statusline.py +0 -0
  299. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/__init__.py +0 -0
  300. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/capabilities.py +0 -0
  301. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/clipboard.py +0 -0
  302. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/console.py +0 -0
  303. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/core/__init__.py +0 -0
  304. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/core/events.py +0 -0
  305. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/core/focus.py +0 -0
  306. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/core/geometry.py +0 -0
  307. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/core/host.py +0 -0
  308. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/core/keymap.py +0 -0
  309. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/core/scheduler.py +0 -0
  310. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/core/screen.py +0 -0
  311. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/core/theme.py +0 -0
  312. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/core/widget.py +0 -0
  313. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/gallery.py +0 -0
  314. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/glyphs.py +0 -0
  315. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/input/__init__.py +0 -0
  316. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/input/keys.py +0 -0
  317. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/input/reader.py +0 -0
  318. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/layout/__init__.py +0 -0
  319. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/layout/box.py +0 -0
  320. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/layout/overlay.py +0 -0
  321. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/layout/solver.py +0 -0
  322. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/layout/spacing.py +0 -0
  323. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/render/__init__.py +0 -0
  324. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/render/ansi.py +0 -0
  325. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/render/canvas.py +0 -0
  326. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/render/code.py +0 -0
  327. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/render/color.py +0 -0
  328. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/render/renderer.py +0 -0
  329. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/render/rich_text.py +0 -0
  330. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/render/style.py +0 -0
  331. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/render/sweep.py +0 -0
  332. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/render/text.py +0 -0
  333. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/testing/__init__.py +0 -0
  334. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/testing/harness.py +0 -0
  335. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/testing/snapshot.py +0 -0
  336. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/theme_file.py +0 -0
  337. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/themes/ayu-mirage.toml +0 -0
  338. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/themes/catppuccin-latte.toml +0 -0
  339. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/themes/catppuccin-mocha.toml +0 -0
  340. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/themes/dracula.toml +0 -0
  341. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/themes/github-dimmed.toml +0 -0
  342. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/themes/github-light.toml +0 -0
  343. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/themes/gruvbox-dark.toml +0 -0
  344. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/themes/gruvbox-light.toml +0 -0
  345. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/themes/kanagawa.toml +0 -0
  346. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/themes/nord.toml +0 -0
  347. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/themes/one-dark.toml +0 -0
  348. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/themes/rose-pine-dawn.toml +0 -0
  349. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/themes/rose-pine.toml +0 -0
  350. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/themes/solarized-dark.toml +0 -0
  351. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/themes/solarized-light.toml +0 -0
  352. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/themes/tokyo-night.toml +0 -0
  353. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/widgets/__init__.py +0 -0
  354. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/widgets/collapsible.py +0 -0
  355. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/widgets/completion.py +0 -0
  356. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/widgets/console.py +0 -0
  357. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/widgets/dialog.py +0 -0
  358. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/widgets/diff.py +0 -0
  359. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/widgets/list.py +0 -0
  360. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/widgets/list_page.py +0 -0
  361. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/widgets/progress.py +0 -0
  362. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/widgets/rich_text.py +0 -0
  363. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/widgets/status.py +0 -0
  364. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/widgets/table.py +0 -0
  365. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/widgets/tasks.py +0 -0
  366. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/widgets/text.py +0 -0
  367. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/widgets/textarea.py +0 -0
  368. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/tui/widgets/toast.py +0 -0
  369. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/update.py +0 -0
  370. {zettcode-0.3.0 → zettcode-0.3.2}/src/zettcode/wording.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: zettcode
3
- Version: 0.3.0
3
+ Version: 0.3.2
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,14 @@ 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.2
38
+ Requires-Dist: zett-agent<0.3,>=0.2.4
39
+ Provides-Extra: all
40
+ Requires-Dist: zettcode-monitor<0.3,>=0.2; extra == 'all'
41
+ Requires-Dist: zettcode-recap<0.2,>=0.1; extra == 'all'
42
+ Provides-Extra: monitor
43
+ Requires-Dist: zettcode-monitor<0.3,>=0.2; extra == 'monitor'
44
+ Provides-Extra: recap
45
+ Requires-Dist: zettcode-recap<0.2,>=0.1; extra == 'recap'
39
46
  Description-Content-Type: text/markdown
40
47
 
41
48
  # ZettCode
@@ -61,13 +61,26 @@ dependencies = [
61
61
  # now leans on: a hosted tool the provider left open is closed with
62
62
  # ``output={"status": "not_reported"}`` instead of leaving the step waiting, and
63
63
  # `engine/server_tools.describe_output` reads that to say so.
64
- "zett-agent>=0.2.2,<0.3",
64
+ "zett-agent>=0.2.4,<0.3",
65
65
  ]
66
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
+ # `/recap` for a reader who steps away. Published on its own line like the one above, and an extra for the same
76
+ # reason: a hard edge would tie the two releases together.
77
+ recap = ["zettcode-recap>=0.1,<0.2"]
78
+ # Every plugin this repository publishes, for a reader who wants the lot: `pip install "zettcode[all]"`.
79
+ all = ["zettcode-monitor>=0.2,<0.3", "zettcode-recap>=0.1,<0.2"]
80
+
67
81
  [project.scripts]
68
82
  zettcode = "zettcode.cli:main"
69
83
  zettcode-server = "zettcode.server.cli:main"
70
- zettcode-gateway = "zettcode.gateway.cli:main"
71
84
 
72
85
  [project.urls]
73
86
  Repository = "https://github.com/Chang-LeHung/zettcode"
@@ -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.3.0"
10
+ __version__ = "0.3.2"
11
11
 
12
12
  #: Where each public name lives; read by :func:`__getattr__` on first use.
13
13
  _EXPORTS = {
@@ -7,6 +7,9 @@ agent is where the shell asks for everything the runtime loaded.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
+ import asyncio
11
+ from collections.abc import Callable
12
+
10
13
  from zettcode.app.commands import Command, CommandContext, CommandHost, CommandResult
11
14
  from zettcode.app.pets import PetCatalog, bundled_pets
12
15
  from zettcode.app.plugins.host import HOST_LINK, HostLink
@@ -116,6 +119,21 @@ class CommandsMixin(AppearanceMixin, ExportsMixin):
116
119
  """Return the header and status rows plugins asked the shell to draw."""
117
120
  return self.terminal_plugins.rows
118
121
 
122
+ @property
123
+ def plugin_focus_handlers(self) -> tuple[Callable[[bool], None], ...]:
124
+ """Return the handlers told when the terminal window gains or loses focus."""
125
+ return self.terminal_plugins.focus_handlers
126
+
127
+ @property
128
+ def plugin_turn_start_handlers(self) -> tuple[Callable[[], None], ...]:
129
+ """Return the handlers told when a turn of the conversation starts."""
130
+ return self.terminal_plugins.turn_start_handlers
131
+
132
+ @property
133
+ def plugin_turn_end_handlers(self) -> tuple[Callable[[bool], None], ...]:
134
+ """Return the handlers told when a turn of the conversation ends."""
135
+ return self.terminal_plugins.turn_end_handlers
136
+
119
137
  async def _command_new(self, context: CommandContext) -> CommandResult:
120
138
  """Start a fresh session."""
121
139
  session_id = self.new_session()
@@ -218,10 +236,28 @@ class CommandsMixin(AppearanceMixin, ExportsMixin):
218
236
  async def _command_compact(self, context: CommandContext) -> CommandResult:
219
237
  """Summarize the conversation now, whatever its size.
220
238
 
221
- The row and the outcome notice arrive while this await runs: the pass
222
- dispatches through the same event dispatcher a turn does, so the
223
- transcript animates ``Compacting`` and ends with ``Context compaction
224
- applied`` (or ``skipped`` when the summary would not be smaller).
239
+ The row is opened only after the refusal check. That check may read a
240
+ cold session on a worker, so the interface stays live while it runs;
241
+ once the row is up, the shell draws it before the pass starts. What
242
+ follows the first ``await`` is still synchronous token counting and the
243
+ summary request, so a row created only by ``CompactionStarted`` would
244
+ reach the screen after the work it was meant to explain.
225
245
  """
226
- await self.compact()
246
+ host = context.host
247
+ if host is not None:
248
+ await self.ensure_compactable()
249
+ host.transcript.start_compaction()
250
+ try:
251
+ if host is not None:
252
+ await host.wait_for_frame()
253
+ await self._compact_pass()
254
+ else:
255
+ await self.compact()
256
+ except asyncio.CancelledError:
257
+ if host is not None:
258
+ host.transcript.complete_compaction("Compacting stopped")
259
+ raise
260
+ finally:
261
+ if host is not None:
262
+ host.transcript.complete_compaction()
227
263
  return CommandResult()
@@ -321,6 +321,8 @@ class MessageEntry(ToolEntry):
321
321
 
322
322
  to: str = ""
323
323
  message: str = ""
324
+ #: The group chat it was written to (without the ``#``), or empty for one session: the row says which.
325
+ group: str = ""
324
326
 
325
327
  @property
326
328
  def foldable(self) -> bool:
@@ -378,3 +380,5 @@ class RelayEntry(Entry):
378
380
  by_agent: bool = True
379
381
  external: bool = False
380
382
  note: str = ""
383
+ #: The group chat it was written in (without the ``#``); empty for a one-to-one message.
384
+ group: str = ""
@@ -440,6 +440,10 @@ class RelayPresenter(Presenter[RelayEntry]):
440
440
  else f"\u2190 {'agent' if entry.by_agent else 'user'} of session {peer}"
441
441
  )
442
442
  detail = ""
443
+ if entry.group:
444
+ # A group message is read by every member: say so in the header, where a one-to-one message has nothing.
445
+ label += f" in #{terminal_safe(entry.group)}"
446
+ detail = f" {SEPARATOR} to the whole group"
443
447
 
444
448
  def row(*spans: Span) -> TextLine:
445
449
  used = sum(display_width(span.text) for span in spans)
@@ -7,6 +7,7 @@ terminal shows and what the SDK reports cannot drift apart.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
+ import logging
10
11
  from collections.abc import Callable
11
12
 
12
13
  from zett_agent.dispatcher import AgentEventDispatcher
@@ -55,6 +56,9 @@ class _NobodyAnswers:
55
56
  raise RuntimeError("This projector has no agent to carry an answer to")
56
57
 
57
58
 
59
+ logger = logging.getLogger("zettcode.terminal")
60
+
61
+
58
62
  class TranscriptProjector(AgentEventDispatcher):
59
63
  """Turn AgentClient callbacks into transcript blocks.
60
64
 
@@ -71,8 +75,9 @@ class TranscriptProjector(AgentEventDispatcher):
71
75
  on_approval: Callable[[ApprovalRequested], None] | None = None,
72
76
  on_ask: Callable[[QuestionAsked], None] | None = None,
73
77
  on_usage: Callable[[Usage, str], None] | None = None,
74
- on_steering_started: Callable[[str], None] | None = None,
78
+ on_steering_started: Callable[[str], Relayed | str | None] | None = None,
75
79
  on_steering_interrupted: Callable[[str], None] | None = None,
80
+ side_running: Callable[[], bool] | None = None,
76
81
  ) -> None:
77
82
  """Send projected events to ``transcript``; approvals go to ``on_approval``.
78
83
 
@@ -89,9 +94,18 @@ class TranscriptProjector(AgentEventDispatcher):
89
94
  the shell can refresh the status line without polling the store.
90
95
  on_steering_started: Called with the text of a queued steering
91
96
  message the agent just adopted, so the shell can drop it from
92
- the pending queue before the row is echoed.
97
+ the pending queue before the row is echoed. What it returns, when
98
+ it is a :class:`~zettcode.relay.Relayed`, is drawn as that session's
99
+ message — what its sender wrote, not the line the model reads; a
100
+ string is drawn as the row's text.
93
101
  on_steering_interrupted: Called with the text of a steering message
94
102
  a newer one superseded, so the shell can drop it too.
103
+ side_running: Asked whether the run in flight is a side question — a question the model answers without
104
+ the exchange joining the conversation. Such a run is drawn only when the shell began it, so a
105
+ plugin that asks one for itself (a recap) is not also drawn as an answer of its own. ``None``
106
+ treats every run as one the shell began. Two plain flags are enough here because the SDK runs one
107
+ request at a time: a second run is refused while one is in flight, so a plugin's ask cannot
108
+ overlap a turn the shell began and a stale answer cannot be drawn as a turn's.
95
109
  """
96
110
  self.transcript = transcript
97
111
  self.bridge: Bridge = bridge if bridge is not None else _NobodyAnswers()
@@ -103,27 +117,58 @@ class TranscriptProjector(AgentEventDispatcher):
103
117
  #: What a hosted tool's input has streamed so far, by call. The translation fills it as fragments arrive
104
118
  #: and reads the query out of it when the call reports back.
105
119
  self._hosted: dict[str, str] = {}
120
+ #: Whether the shell began the run in flight, and so draws it.
121
+ self._begun = False
122
+ #: Whether a run nobody began has been reported as left out, so dropping one is a line and not one per
123
+ #: token; it is cleared when a turn is begun, so it reports the next run that way too.
124
+ self._reported_side = False
125
+ self._side_running = side_running
106
126
 
107
127
  def begin_turn(self, prompt: str, *, side: bool = False, relayed: Relayed | None = None) -> None:
108
- """Echo the prompt into the transcript before the run starts.
128
+ """Begin drawing one run into the transcript, echoing the prompt that asked for it.
129
+
130
+ The shell begins a turn before every run it shows on screen, which is also how a side question it asked
131
+ is told apart from one a plugin asked for itself: see ``side_running``.
109
132
 
110
133
  Args:
111
- prompt: What the reader asked.
134
+ prompt: What the reader asked, echoed as the turn's first row.
112
135
  side: The question is a side one, which the transcript shows as such.
113
136
  relayed: The turn answers a message another session sent, which the transcript shows as that.
114
137
  """
138
+ # The row first, the flag second: an echo that raises leaves no run to draw, and a flag set before it would
139
+ # stay set, drawing every later side question as an answer of its own.
115
140
  if relayed is None:
116
141
  self.transcript.begin_turn(prompt, side=side)
117
142
  else:
118
143
  self.transcript.begin_turn(prompt, side=side, relayed=relayed)
144
+ self._begun = True
145
+ self._reported_side = False
146
+
147
+ def end_turn(self) -> None:
148
+ """Stop drawing, once the run the shell began has ended.
149
+
150
+ Nothing of whatever runs next is drawn until a turn is begun for it, which is what keeps a plugin's own
151
+ side question out of the transcript between turns.
152
+ """
153
+ self._begun = False
119
154
 
120
155
  async def dispatch(self, event: AgentEvent) -> None:
121
156
  """Note that the agent spoke, then draw what the event stands for.
122
157
 
123
158
  Every event ends a silence, whichever it is: the waiting row counts how long it has been since the model
124
159
  last said anything, and has to be told each time it does.
160
+
161
+ A side question the shell did not begin is left out: its answer would land under the last turn's line as a
162
+ reply nobody asked for, and whoever asked for it — a plugin's recap — draws the part it wants itself. The
163
+ drop is logged, a line rather than one per token, so an answer that is missing from the transcript can be
164
+ explained from the log.
125
165
  """
126
166
  self.transcript.heard()
167
+ if not self._begun and self._side_running is not None and self._side_running():
168
+ if not self._reported_side:
169
+ self._reported_side = True
170
+ logger.debug("projector.side_not_drawn", extra={"session": event.session_id})
171
+ return
127
172
  for translated in translate(event, self.bridge, self._hosted):
128
173
  self.project(translated, event)
129
174
 
@@ -147,8 +192,10 @@ class TranscriptProjector(AgentEventDispatcher):
147
192
  case ReasoningFinished():
148
193
  transcript.complete_thinking()
149
194
  case ToolStarted(call_id=call_id, name=name, arguments=arguments):
195
+ logger.debug("tool.start", extra={"tool": name, "call": call_id})
150
196
  transcript.start_tool(call_id, name, arguments)
151
197
  case ToolFinished(call_id=call_id, name=name, output=output, ok=ok, skipped=skipped):
198
+ logger.debug("tool.finish", extra={"tool": name, "call": call_id, "ok": ok, "skipped": skipped})
152
199
  self._tool_finished(call_id, name, output, ok, skipped)
153
200
  case ServerToolStarted(call_id=call_id, name=name, arguments=arguments):
154
201
  transcript.start_tool(call_id, name, arguments)
@@ -174,9 +221,11 @@ class TranscriptProjector(AgentEventDispatcher):
174
221
  if stop is not None and not stop.ok:
175
222
  transcript.error(stop_text(stop))
176
223
  case Steered(text=text):
177
- if self.on_steering_started is not None:
178
- self.on_steering_started(text)
179
- transcript.begin_turn(text)
224
+ adopted = self.on_steering_started(text) if self.on_steering_started is not None else None
225
+ if isinstance(adopted, Relayed):
226
+ transcript.begin_turn(adopted.text, relayed=adopted)
227
+ else:
228
+ transcript.begin_turn(adopted or text)
180
229
  case SteeringFinished():
181
230
  transcript.complete_thinking()
182
231
  case SteeringInterrupted(text=text):
@@ -667,7 +667,10 @@ class MessageTool(ToolHandler):
667
667
  names = frozenset({"send_message"})
668
668
 
669
669
  def describe(self, name: str, arguments: Mapping[str, object]) -> ToolRow:
670
- """Say who it is for; an id is shortened to its end, which is how a session is named without a title."""
670
+ """Say who it is for: a group as ``#name`` (everyone in it reads it), a session by name or the end of its id."""
671
+ group = argument(arguments, "group")
672
+ if group:
673
+ return ToolRow(f"Message to group #{group.lstrip('#')}")
671
674
  return ToolRow(f"Message to {self._recipient(argument(arguments, 'to', '(unknown)'))}")
672
675
 
673
676
  @staticmethod
@@ -686,11 +689,43 @@ class MessageTool(ToolHandler):
686
689
  tool=name,
687
690
  title=self.describe(name, arguments).title,
688
691
  to=argument(arguments, "to"),
692
+ group=argument(arguments, "group").lstrip("#"),
689
693
  message=limit_output(message) if isinstance(message, str) else "",
690
694
  started_at=started_at,
691
695
  )
692
696
 
693
697
 
698
+ class HistoryTool(ToolHandler):
699
+ """``message_history``: what was said before, with one session or in a group."""
700
+
701
+ names = frozenset({"message_history"})
702
+
703
+ def describe(self, name: str, arguments: Mapping[str, object]) -> ToolRow:
704
+ """Say whose history it reads: a group's as ``#name``, one session's by name or the end of its id."""
705
+ group = argument(arguments, "group")
706
+ if group:
707
+ return ToolRow(f"Read history of group #{group.lstrip('#')}")
708
+ session = argument(arguments, "session")
709
+ if session:
710
+ return ToolRow(f"Read history with {MessageTool._recipient(session)}")
711
+ return ToolRow("Read message history")
712
+
713
+
714
+ class GroupTool(ToolHandler):
715
+ """``manage_group``: make or change a group chat."""
716
+
717
+ names = frozenset({"manage_group"})
718
+
719
+ _VERBS = {"create": "Create", "add": "Add to", "remove": "Remove from", "leave": "Leave", "delete": "Delete"}
720
+
721
+ def describe(self, name: str, arguments: Mapping[str, object]) -> ToolRow:
722
+ """Say what is done to which group, and with whom."""
723
+ verb = self._VERBS.get(argument(arguments, "action"), "Change")
724
+ members = argument(arguments, "members")
725
+ with_ = f" with {members}" if members and verb in ("Create", "Add to", "Remove from") else ""
726
+ return ToolRow(f"{verb} group #{argument(arguments, 'group').lstrip('#') or '(unknown)'}{with_}")
727
+
728
+
694
729
  class NamespacedTool(ToolHandler):
695
730
  """A tool whose name carries where it came from: ``server__tool``, so the row names the server.
696
731
 
@@ -785,6 +820,8 @@ DEFAULT_RENDERERS = Renderers(
785
820
  SkillTool(),
786
821
  TaskTool(),
787
822
  MessageTool(),
823
+ HistoryTool(),
824
+ GroupTool(),
788
825
  NamespacedTool(),
789
826
  FallbackTool(),
790
827
  ReasoningHandler(),
@@ -245,11 +245,14 @@ def calls_text(usage: Usage) -> str | None:
245
245
 
246
246
 
247
247
  def speed_text(usage: Usage) -> str | None:
248
- """Say how fast the model generated, when it can be told and is at least one token a second."""
249
- if usage.requests and usage.seconds > 0 and usage.output_tokens > 0:
250
- rate = usage.output_tokens / usage.seconds
251
- if rate >= 1:
252
- return f"{rate:.1f} tok/s" if rate < 10 else f"{rate:.0f} tok/s"
248
+ """Say how fast the model generated, when it can be told and is at least one token a second.
249
+
250
+ Reads :attr:`Usage.output_rate`, which leaves the opening chunk's tokens out of the numerator, so this line and the
251
+ status line a reader sees above it never disagree.
252
+ """
253
+ rate = usage.output_rate
254
+ if usage.requests and rate is not None and rate >= 1:
255
+ return f"{rate:.1f} tok/s" if rate < 10 else f"{rate:.0f} tok/s"
253
256
  return None
254
257
 
255
258
 
@@ -44,7 +44,7 @@ TIPS: tuple[str, ...] = (
44
44
  "/usage shows tokens, cache and cost by day, model and session; /resume lists them per session.",
45
45
  "/statusline chooses what the header and the status line show, and turns the pet on.",
46
46
  "/pet picks a companion that sleeps while idle and works while a request runs; drag it anywhere.",
47
- "/self-compact lets the model decide for itself when to compact this session.",
47
+ "/plugins can turn self-compaction on or off: the model decides when to compact its own context.",
48
48
  "zettcode resume <id> opens a stored session; zettcode update installs the newest release.",
49
49
  "zettcode doctor checks every configured model from a shell, with no session started.",
50
50
  "zettcode --full skips every shell question and the sandbox for that run; /permission can change it later.",
@@ -31,8 +31,8 @@ class OneOffMixin(EntryLog):
31
31
  self._add(TextEntry(id=self._next_id, kind=EntryKind.ANNOUNCEMENT, text=text))
32
32
 
33
33
  def model_changed(self, previous: str, current: str) -> None:
34
- """Announce a model change in the conversation."""
35
- self.announce(f"Model changed from {previous} to {current}.")
34
+ """Announce a model change in the conversation; an empty previous is the first model there has been."""
35
+ self.announce(f"Model changed from {previous} to {current}." if previous else f"Model set to {current}.")
36
36
 
37
37
  def permission_changed(self, previous: str, current: str) -> None:
38
38
  """Announce a shell-permission change the way a model change is shown."""
@@ -87,11 +87,20 @@ class StreamMixin(TurnMixin):
87
87
  row.text += delta
88
88
  self._changed(row)
89
89
 
90
- def complete_compaction(self) -> None:
91
- """Close the running compaction row and stamp its elapsed time."""
90
+ def complete_compaction(self, text: str = "") -> None:
91
+ """Close the running compaction row and stamp its elapsed time.
92
+
93
+ Args:
94
+ text: What to leave in place of the streamed summary, when the pass
95
+ ended before writing one — a stop, for example. The row is
96
+ expanded so the note is on its face rather than in a fold.
97
+ """
92
98
  row = self._last(ThinkingEntry)
93
99
  if row is None or row.title != COMPACTING_LABEL or not row.running:
94
100
  return
101
+ if text:
102
+ row.text = text
103
+ row.expanded = True
95
104
  row.finish(EntryStatus.COMPLETED, self.clock())
96
105
  self._changed(row)
97
106
 
@@ -75,6 +75,7 @@ class TurnMixin(EntryLog):
75
75
  outgoing=False,
76
76
  by_agent=relayed.by_agent,
77
77
  external=relayed.via == VIA_EXTERNAL,
78
+ group=relayed.group,
78
79
  )
79
80
  )
80
81
 
@@ -104,9 +105,15 @@ class TurnMixin(EntryLog):
104
105
  self.user_message(prompt, side=side)
105
106
  self.wait_for_model()
106
107
 
107
- def wait_for_model(self) -> bool:
108
+ def wait_for_model(self, title: str = PROCESSING, *, tips: bool = True) -> bool:
108
109
  """Open the pinned wait row for a request, and report whether it opened.
109
110
 
111
+ Args:
112
+ title: What the row says is happening; ``Processing`` for a request, and a command's own words
113
+ (``Recap · writing``) for work it started that will take a while.
114
+ tips: Attach the deck's tip for a model request. A command's row is about the command, so it takes
115
+ none.
116
+
110
117
  Nothing is known about the response yet, and a call can take seconds, so
111
118
  the row goes up as soon as the request starts and stays for all of it:
112
119
  the loop calls the model again after every tool batch, and one row that
@@ -123,8 +130,8 @@ class TurnMixin(EntryLog):
123
130
  if any(self._is_open_row(entry) for entry in self.entries):
124
131
  return False
125
132
  self._turn_from = len(self.entries)
126
- tips = self.tip_deck.next() if self.tip_deck is not None else ()
127
- self._add(WaitEntry(id=self._next_id, title=PROCESSING, started_at=self.clock(), tips=tips))
133
+ shown = self.tip_deck.next() if tips and self.tip_deck is not None else ()
134
+ self._add(WaitEntry(id=self._next_id, title=title, started_at=self.clock(), tips=shown))
128
135
  return True
129
136
 
130
137
  def summarize_changes(self, workspace: PurePath | None = None) -> bool:
@@ -77,6 +77,14 @@ class CommandHost(Protocol):
77
77
  def request_layout(self) -> None:
78
78
  """Ask for the screen to be laid out again, after a change in what it shows."""
79
79
 
80
+ async def wait_for_frame(self) -> None:
81
+ """Wait until the next frame has reached the terminal.
82
+
83
+ A command that has just put a row on screen and is about to start work
84
+ with no ``await`` in it asks for this first: the row is drawn before the
85
+ work begins instead of after it.
86
+ """
87
+
80
88
  def offer(self, command: str) -> None:
81
89
  """Put ``command`` and a space in the draft, which opens the menu of the words it takes."""
82
90
 
@@ -101,7 +109,7 @@ class CommandHost(Protocol):
101
109
  async def run_side_question(self, question: str) -> None:
102
110
  """Answer a question that is kept out of the conversation."""
103
111
 
104
- async def prompt(self, text: str, *, shown: str | None = None) -> bool:
112
+ async def prompt(self, text: str, *, shown: str | None = None, cache: str | None = None) -> bool:
105
113
  """Run ``text`` as one turn of this conversation, exactly as if the reader had typed it.
106
114
 
107
115
  The text reaches the model the way a draft does: stored in the session, streamed as it is answered, and
@@ -120,6 +128,11 @@ class CommandHost(Protocol):
120
128
  shown: What the transcript shows, when it differs from ``text``. The message the session keeps is
121
129
  the same either way: ``text`` is in it, and ``shown`` is what a reader is shown of it.
122
130
 
131
+ cache: How the turn may use the provider's prompt cache: ``"read_write"`` (the default) extends it for
132
+ the next turn, ``"read"`` reuses a warm prefix without marking this turn's new content, and
133
+ ``"off"`` sends no cache directive. A one-off report a reader is not going to follow is worth
134
+ ``"read"``.
135
+
123
136
  Returns:
124
137
  Whether a turn was started and has ended. A request already running, a blank text and a session held
125
138
  by another process answer ``False``, and nothing is started. A command that means to be usable
@@ -199,12 +212,21 @@ class CommandResult:
199
212
  relayout: Re-measure the widget tree after applying the result. Set it
200
213
  when the change affects geometry a fixed-height row already
201
214
  measured, such as the header showing a new model name.
215
+ intro: What a result that is still working is doing, in one line. The shell shows it in the pinned row
216
+ while it waits, which is how a command that takes seconds says so.
217
+ run: The rest of a result, when the command has not finished. The shell opens the pinned row with
218
+ ``intro``, awaits this, and shows what it returned; Ctrl-C, Escape, sending the next line, and Enter on an
219
+ empty draft stop it — a command that is working does not hold the composer. A command returns
220
+ this instead of awaiting the slow part itself, so the reader sees it working. The other fields of
221
+ *this* result are then names to the wind: only what ``run`` answers is applied.
202
222
  """
203
223
 
204
224
  message: str = ""
205
225
  notification: str | None = None
206
226
  widget: Widget | None = None
207
227
  relayout: bool = False
228
+ intro: str = ""
229
+ run: Awaitable[CommandResult] | None = None
208
230
 
209
231
 
210
232
  @dataclass(frozen=True, slots=True)