zettcode 0.1.7__tar.gz → 0.1.9__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 (279) hide show
  1. zettcode-0.1.9/.gitignore +30 -0
  2. zettcode-0.1.9/PKG-INFO +60 -0
  3. zettcode-0.1.9/README.md +23 -0
  4. {zettcode-0.1.7 → zettcode-0.1.9}/pyproject.toml +3 -48
  5. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/__init__.py +1 -1
  6. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/_compat.py +1 -1
  7. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/agent/__init__.py +2 -1
  8. zettcode-0.1.9/src/zettcode/app/agent/agent/commands.py +214 -0
  9. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/agent/sessions.py +13 -2
  10. zettcode-0.1.9/src/zettcode/app/agent/agent/settings.py +327 -0
  11. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/agent/turns.py +88 -9
  12. zettcode-0.1.9/src/zettcode/app/agent/conversation/effort_gauge.py +126 -0
  13. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/conversation/entries.py +6 -3
  14. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/conversation/presenters.py +134 -57
  15. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/conversation/projection.py +11 -0
  16. zettcode-0.1.9/src/zettcode/app/agent/conversation/pulse.py +116 -0
  17. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/conversation/rendering.py +13 -9
  18. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/conversation/replay.py +7 -2
  19. zettcode-0.1.9/src/zettcode/app/agent/conversation/shimmer.py +256 -0
  20. zettcode-0.1.9/src/zettcode/app/agent/conversation/stops.py +57 -0
  21. zettcode-0.1.9/src/zettcode/app/agent/conversation/text.py +230 -0
  22. zettcode-0.1.9/src/zettcode/app/agent/conversation/tips.py +76 -0
  23. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/conversation/transcript/__init__.py +5 -1
  24. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/conversation/transcript/stream.py +1 -1
  25. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/conversation/transcript/turn.py +10 -4
  26. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/dialogue/mentions.py +4 -3
  27. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/dialogue/relay.py +1 -1
  28. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/dialogue/title.py +10 -2
  29. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/errors.py +95 -3
  30. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/extensions/capabilities.py +8 -2
  31. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/extensions/compaction.py +44 -2
  32. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/extensions/context.py +7 -2
  33. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/extensions/mcp.py +56 -7
  34. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/extensions/mcp_notice.py +15 -3
  35. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/extensions/messaging.py +3 -2
  36. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/extensions/messenger.py +4 -3
  37. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/extensions/subagents.py +111 -13
  38. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/extensions/usage.py +32 -13
  39. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/permission/__init__.py +2 -1
  40. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/runtime.py +137 -126
  41. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/storage/__init__.py +16 -4
  42. zettcode-0.1.9/src/zettcode/app/agent/storage/layout.py +161 -0
  43. zettcode-0.1.9/src/zettcode/app/agent/storage/ledger.py +651 -0
  44. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/storage/locks.py +59 -7
  45. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/storage/metadata.py +4 -1
  46. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/storage/records.py +1 -1
  47. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/storage/store.py +112 -35
  48. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/brand.py +15 -5
  49. zettcode-0.1.9/src/zettcode/app/commandline/__init__.py +47 -0
  50. zettcode-0.1.9/src/zettcode/app/commandline/arguments.py +169 -0
  51. zettcode-0.1.9/src/zettcode/app/commandline/completion.py +262 -0
  52. zettcode-0.1.9/src/zettcode/app/commandline/history.py +305 -0
  53. zettcode-0.1.9/src/zettcode/app/commandline/runner.py +365 -0
  54. zettcode-0.1.9/src/zettcode/app/commands.py +277 -0
  55. zettcode-0.1.9/src/zettcode/app/pets/__init__.py +62 -0
  56. zettcode-0.1.9/src/zettcode/app/pets/cat.py +52 -0
  57. zettcode-0.1.9/src/zettcode/app/pets/chibi.py +55 -0
  58. zettcode-0.1.9/src/zettcode/app/pets/dog.py +52 -0
  59. zettcode-0.1.9/src/zettcode/app/pets/kangaroo.py +58 -0
  60. zettcode-0.1.9/src/zettcode/app/pets/model.py +191 -0
  61. zettcode-0.1.9/src/zettcode/app/pets/penguin.py +60 -0
  62. zettcode-0.1.9/src/zettcode/app/pets/polarbear.py +50 -0
  63. zettcode-0.1.9/src/zettcode/app/pets/scarfy.py +61 -0
  64. zettcode-0.1.9/src/zettcode/app/pets/seagull.py +63 -0
  65. zettcode-0.1.9/src/zettcode/app/pets/slime.py +56 -0
  66. zettcode-0.1.9/src/zettcode/app/pets/snowbear.py +55 -0
  67. zettcode-0.1.9/src/zettcode/app/pets/sprout.py +64 -0
  68. zettcode-0.1.9/src/zettcode/app/pets/tiger.py +52 -0
  69. zettcode-0.1.9/src/zettcode/app/pets/whale.py +54 -0
  70. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/app.py +150 -30
  71. zettcode-0.1.9/src/zettcode/app/ui/bars.py +286 -0
  72. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/boot.py +15 -4
  73. zettcode-0.1.9/src/zettcode/app/ui/commands.py +282 -0
  74. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/config_form.py +88 -3
  75. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/demo.py +7 -6
  76. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/followups.py +4 -3
  77. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/gc_policy.py +1 -1
  78. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/keys.py +76 -7
  79. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/labels.py +86 -18
  80. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/messaging.py +5 -3
  81. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/notices.py +2 -1
  82. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/providers.py +119 -26
  83. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/sessions.py +5 -4
  84. zettcode-0.1.9/src/zettcode/app/ui/settings.py +160 -0
  85. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/shell.py +23 -9
  86. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/updating.py +4 -3
  87. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/widgets/__init__.py +29 -0
  88. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/widgets/approval_page.py +4 -3
  89. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/widgets/ask_page.py +4 -4
  90. zettcode-0.1.9/src/zettcode/app/ui/widgets/bash_page.py +91 -0
  91. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/widgets/completer.py +41 -8
  92. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/widgets/composer.py +52 -14
  93. zettcode-0.1.9/src/zettcode/app/ui/widgets/context_charts.py +283 -0
  94. zettcode-0.1.9/src/zettcode/app/ui/widgets/context_page.py +418 -0
  95. zettcode-0.1.9/src/zettcode/app/ui/widgets/effort_page.py +295 -0
  96. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/widgets/follow_up.py +3 -3
  97. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/widgets/input_page.py +3 -3
  98. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/widgets/model_page.py +3 -3
  99. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/widgets/panel.py +1 -1
  100. zettcode-0.1.9/src/zettcode/app/ui/widgets/pet.py +222 -0
  101. zettcode-0.1.9/src/zettcode/app/ui/widgets/pet_page.py +347 -0
  102. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/widgets/root.py +14 -7
  103. zettcode-0.1.9/src/zettcode/app/ui/widgets/self_compact_page.py +409 -0
  104. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/widgets/send_kind_page.py +4 -3
  105. zettcode-0.1.9/src/zettcode/app/ui/widgets/sessions_page.py +268 -0
  106. zettcode-0.1.9/src/zettcode/app/ui/widgets/statusline_page.py +437 -0
  107. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/widgets/steering.py +4 -3
  108. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/widgets/transcript.py +5 -5
  109. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/widgets/update_page.py +2 -2
  110. zettcode-0.1.9/src/zettcode/app/ui/widgets/usage_charts.py +464 -0
  111. zettcode-0.1.9/src/zettcode/app/ui/widgets/usage_page.py +273 -0
  112. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/widgets/welcome.py +5 -2
  113. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/cli.py +116 -25
  114. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/config.py +267 -22
  115. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/config_edit.py +88 -0
  116. zettcode-0.1.9/src/zettcode/doctor.py +167 -0
  117. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/paths.py +9 -0
  118. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/plugins/__init__.py +14 -0
  119. zettcode-0.1.9/src/zettcode/plugins/builtin_agent.py +383 -0
  120. zettcode-0.1.9/src/zettcode/plugins/builtin_commands.py +420 -0
  121. zettcode-0.1.9/src/zettcode/plugins/builtins.py +187 -0
  122. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/plugins/container.py +279 -6
  123. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/plugins/extension.py +4 -1
  124. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/plugins/loader.py +68 -6
  125. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/plugins/plugin.py +68 -3
  126. zettcode-0.1.9/src/zettcode/plugins/services.py +97 -0
  127. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/plugins/state.py +63 -4
  128. zettcode-0.1.9/src/zettcode/py.typed +1 -0
  129. zettcode-0.1.9/src/zettcode/sdk/__init__.py +165 -0
  130. zettcode-0.1.9/src/zettcode/sdk/_translate.py +263 -0
  131. zettcode-0.1.9/src/zettcode/sdk/client.py +865 -0
  132. zettcode-0.1.9/src/zettcode/sdk/errors.py +104 -0
  133. zettcode-0.1.9/src/zettcode/sdk/events.py +348 -0
  134. zettcode-0.1.9/src/zettcode/sdk/handlers.py +124 -0
  135. zettcode-0.1.9/src/zettcode/sdk/models.py +140 -0
  136. zettcode-0.1.9/src/zettcode/sdk/session.py +945 -0
  137. zettcode-0.1.9/src/zettcode/sdk/types.py +517 -0
  138. zettcode-0.1.9/src/zettcode/statusline.py +141 -0
  139. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/__init__.py +30 -2
  140. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/core/events.py +1 -1
  141. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/core/theme.py +26 -6
  142. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/core/widget.py +8 -0
  143. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/input/events.py +2 -1
  144. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/layout/spacing.py +2 -1
  145. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/render/__init__.py +4 -0
  146. zettcode-0.1.9/src/zettcode/tui/render/ansi.py +371 -0
  147. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/render/text.py +13 -1
  148. zettcode-0.1.9/src/zettcode/tui/theme_file.py +290 -0
  149. zettcode-0.1.9/src/zettcode/tui/themes/ayu-mirage.toml +44 -0
  150. zettcode-0.1.9/src/zettcode/tui/themes/catppuccin-latte.toml +44 -0
  151. zettcode-0.1.9/src/zettcode/tui/themes/catppuccin-mocha.toml +44 -0
  152. zettcode-0.1.9/src/zettcode/tui/themes/dracula.toml +44 -0
  153. zettcode-0.1.9/src/zettcode/tui/themes/github-dimmed.toml +44 -0
  154. zettcode-0.1.9/src/zettcode/tui/themes/github-light.toml +44 -0
  155. zettcode-0.1.9/src/zettcode/tui/themes/gruvbox-dark.toml +44 -0
  156. zettcode-0.1.9/src/zettcode/tui/themes/gruvbox-light.toml +44 -0
  157. zettcode-0.1.9/src/zettcode/tui/themes/kanagawa.toml +44 -0
  158. zettcode-0.1.9/src/zettcode/tui/themes/nord.toml +44 -0
  159. zettcode-0.1.9/src/zettcode/tui/themes/one-dark.toml +44 -0
  160. zettcode-0.1.9/src/zettcode/tui/themes/rose-pine-dawn.toml +44 -0
  161. zettcode-0.1.9/src/zettcode/tui/themes/rose-pine.toml +44 -0
  162. zettcode-0.1.9/src/zettcode/tui/themes/solarized-dark.toml +44 -0
  163. zettcode-0.1.9/src/zettcode/tui/themes/solarized-light.toml +44 -0
  164. zettcode-0.1.9/src/zettcode/tui/themes/tokyo-night.toml +44 -0
  165. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/widgets/__init__.py +3 -0
  166. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/widgets/collapsible.py +2 -1
  167. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/widgets/completion.py +6 -0
  168. zettcode-0.1.9/src/zettcode/tui/widgets/console.py +442 -0
  169. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/widgets/list.py +52 -10
  170. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/widgets/list_page.py +11 -3
  171. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/widgets/markdown.py +2 -1
  172. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/widgets/status.py +20 -0
  173. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/widgets/tasks.py +2 -1
  174. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/widgets/text.py +2 -1
  175. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/widgets/textarea.py +25 -1
  176. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/widgets/toast.py +2 -1
  177. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/update.py +78 -0
  178. zettcode-0.1.7/.gitignore +0 -22
  179. zettcode-0.1.7/LICENSE +0 -21
  180. zettcode-0.1.7/PKG-INFO +0 -149
  181. zettcode-0.1.7/README.md +0 -112
  182. zettcode-0.1.7/README.zh-CN.md +0 -106
  183. zettcode-0.1.7/src/zettcode/app/agent/agent/commands.py +0 -86
  184. zettcode-0.1.7/src/zettcode/app/agent/agent/settings.py +0 -164
  185. zettcode-0.1.7/src/zettcode/app/agent/conversation/rows.py +0 -437
  186. zettcode-0.1.7/src/zettcode/app/commands.py +0 -151
  187. zettcode-0.1.7/src/zettcode/app/ui/commands.py +0 -311
  188. zettcode-0.1.7/src/zettcode/app/ui/rows.py +0 -170
  189. zettcode-0.1.7/src/zettcode/app/ui/settings.py +0 -102
  190. zettcode-0.1.7/src/zettcode/app/ui/widgets/context_page.py +0 -114
  191. zettcode-0.1.7/src/zettcode/app/ui/widgets/sessions_page.py +0 -84
  192. zettcode-0.1.7/src/zettcode/plugins/builtins.py +0 -82
  193. zettcode-0.1.7/src/zettcode/tui/theme_file.py +0 -102
  194. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/__init__.py +0 -0
  195. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/__init__.py +0 -0
  196. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/agent/base.py +0 -0
  197. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/agent/context.py +0 -0
  198. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/agent/exports.py +0 -0
  199. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/agent/interaction.py +0 -0
  200. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/agent/prompt.py +0 -0
  201. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/conversation/__init__.py +0 -0
  202. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/conversation/changes.py +0 -0
  203. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/conversation/transcript/fold.py +0 -0
  204. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/conversation/transcript/log.py +0 -0
  205. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/conversation/transcript/oneoff.py +0 -0
  206. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/dialogue/__init__.py +0 -0
  207. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/dialogue/ask.py +0 -0
  208. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/dialogue/side.py +0 -0
  209. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/export/__init__.py +0 -0
  210. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/export/trace/script.js +0 -0
  211. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/export/trace/style.css +0 -0
  212. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/export/trace/template.html +0 -0
  213. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/extensions/__init__.py +0 -0
  214. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/extensions/tool_output.py +0 -0
  215. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/permission/approval.py +0 -0
  216. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/agent/permission/readonly.py +0 -0
  217. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/pipeline.py +0 -0
  218. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/registry.py +0 -0
  219. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/__init__.py +0 -0
  220. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/app/ui/clipboard.py +0 -0
  221. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/broker/__init__.py +0 -0
  222. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/broker/__main__.py +0 -0
  223. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/broker/client.py +0 -0
  224. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/broker/files.py +0 -0
  225. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/broker/finder.py +0 -0
  226. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/broker/hub.py +0 -0
  227. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/broker/log.py +0 -0
  228. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/broker/models.py +0 -0
  229. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/broker/process.py +0 -0
  230. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/broker/protocol.py +0 -0
  231. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/broker/roster.py +0 -0
  232. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/broker/runner.py +0 -0
  233. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/broker/server.py +0 -0
  234. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/broker/text.py +0 -0
  235. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/broker/web.py +0 -0
  236. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/collector.py +0 -0
  237. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/filelock.py +0 -0
  238. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/plugins/mixins.py +0 -0
  239. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/providers.py +0 -0
  240. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/capabilities.py +0 -0
  241. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/clipboard.py +0 -0
  242. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/console.py +0 -0
  243. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/core/__init__.py +0 -0
  244. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/core/app.py +0 -0
  245. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/core/focus.py +0 -0
  246. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/core/geometry.py +0 -0
  247. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/core/host.py +0 -0
  248. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/core/keymap.py +0 -0
  249. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/core/scheduler.py +0 -0
  250. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/core/screen.py +0 -0
  251. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/gallery.py +0 -0
  252. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/glyphs.py +0 -0
  253. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/input/__init__.py +0 -0
  254. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/input/bridge.py +0 -0
  255. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/input/decoder.py +0 -0
  256. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/input/keys.py +0 -0
  257. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/input/reader.py +0 -0
  258. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/layout/__init__.py +0 -0
  259. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/layout/box.py +0 -0
  260. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/layout/overlay.py +0 -0
  261. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/layout/scroll.py +0 -0
  262. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/layout/solver.py +0 -0
  263. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/render/canvas.py +0 -0
  264. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/render/code.py +0 -0
  265. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/render/color.py +0 -0
  266. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/render/renderer.py +0 -0
  267. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/render/rich_text.py +0 -0
  268. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/render/style.py +0 -0
  269. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/render/sweep.py +0 -0
  270. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/runner.py +0 -0
  271. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/terminal.py +0 -0
  272. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/testing/__init__.py +0 -0
  273. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/testing/harness.py +0 -0
  274. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/testing/snapshot.py +0 -0
  275. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/widgets/dialog.py +0 -0
  276. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/widgets/diff.py +0 -0
  277. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/widgets/progress.py +0 -0
  278. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/widgets/rich_text.py +0 -0
  279. {zettcode-0.1.7 → zettcode-0.1.9}/src/zettcode/tui/widgets/table.py +0 -0
@@ -0,0 +1,30 @@
1
+ **/.venv/
2
+ **/__pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ build/
6
+ dist/
7
+ .smoke/
8
+ .coverage
9
+ **/.pytest_cache/
10
+ **/.ruff_cache/
11
+ .zett-tool-output/
12
+ .env
13
+ .DS_Store
14
+
15
+ # viztracer writes its trace here by default.
16
+ /result.json
17
+
18
+ # The docs site: dependencies, build output, and VitePress' cache.
19
+ node_modules/
20
+ docs/.vitepress/dist/
21
+ docs/.vitepress/cache/
22
+ docs/.vitepress/.temp/
23
+
24
+ # The TypeScript client is built from its sources; only dist/ is published.
25
+ clients/typescript/dist/
26
+
27
+ # The palette gallery is generated by `make palettes`: every widget painted in
28
+ # every palette, written by docs/scripts/palette_gallery.py. Regenerate it
29
+ # rather than committing a 2 MB copy that goes stale the moment a colour moves.
30
+ docs/internal/palettes.html
@@ -0,0 +1,60 @@
1
+ Metadata-Version: 2.5
2
+ Name: zettcode
3
+ Version: 0.1.9
4
+ Summary: A focused terminal coding agent powered by zett-agent
5
+ Project-URL: Repository, https://github.com/Chang-LeHung/zettcode
6
+ Project-URL: Issues, https://github.com/Chang-LeHung/zettcode/issues
7
+ Author: Chang-LeHung
8
+ License-Expression: MIT
9
+ Keywords: agent,coding-agent,llm,terminal,tui
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Environment :: Console
12
+ Classifier: Operating System :: MacOS
13
+ Classifier: Operating System :: Microsoft :: Windows :: Windows 10
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
20
+ Classifier: Programming Language :: Python :: 3.15
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: exceptiongroup<2,>=1.2; python_version < '3.11'
24
+ Requires-Dist: httpx<1,>=0.27
25
+ Requires-Dist: pydantic<3,>=2
26
+ Requires-Dist: pyperclip<2,>=1.9
27
+ Requires-Dist: setproctitle<2,>=1.3.7; os_name == 'posix'
28
+ Requires-Dist: starlette<2,>=0.48
29
+ Requires-Dist: tiktoken<1,>=0.12
30
+ Requires-Dist: tomli<3,>=2; python_version < '3.11'
31
+ Requires-Dist: truststore<1,>=0.10
32
+ Requires-Dist: uvicorn<1,>=0.35
33
+ Requires-Dist: wcwidth<1,>=0.2
34
+ Requires-Dist: websockets<17,>=15
35
+ Requires-Dist: zett-agent<0.2,>=0.1.17
36
+ Description-Content-Type: text/markdown
37
+
38
+ # ZettCode
39
+
40
+ A focused terminal coding agent powered by [zett-agent](https://pypi.org/project/zett-agent/): it reads and edits your
41
+ code, runs commands with your permission, and is driven by a model you choose. The repository also ships a Python SDK
42
+ and an HTTP server.
43
+
44
+ ```bash
45
+ pip install zettcode
46
+ zettcode
47
+ ```
48
+
49
+ - [The guide](https://chang-lehung.github.io/zettcode/) — commands, keys, plugins, configuration
50
+ - [The SDK](https://chang-lehung.github.io/zettcode/sdk/) — drive the agent from Python
51
+ - [The server](https://chang-lehung.github.io/zettcode/server/) — drive it over HTTP
52
+
53
+ The full README, with the screenshots, is at the root of the repository.
54
+
55
+ ```python
56
+ import zettcode.sdk as zc
57
+
58
+ result = await zc.prompt("summarize the README", permission="deny")
59
+ print(result.text)
60
+ ```
@@ -0,0 +1,23 @@
1
+ # ZettCode
2
+
3
+ A focused terminal coding agent powered by [zett-agent](https://pypi.org/project/zett-agent/): it reads and edits your
4
+ code, runs commands with your permission, and is driven by a model you choose. The repository also ships a Python SDK
5
+ and an HTTP server.
6
+
7
+ ```bash
8
+ pip install zettcode
9
+ zettcode
10
+ ```
11
+
12
+ - [The guide](https://chang-lehung.github.io/zettcode/) — commands, keys, plugins, configuration
13
+ - [The SDK](https://chang-lehung.github.io/zettcode/sdk/) — drive the agent from Python
14
+ - [The server](https://chang-lehung.github.io/zettcode/server/) — drive it over HTTP
15
+
16
+ The full README, with the screenshots, is at the root of the repository.
17
+
18
+ ```python
19
+ import zettcode.sdk as zc
20
+
21
+ result = await zc.prompt("summarize the README", permission="deny")
22
+ print(result.text)
23
+ ```
@@ -5,7 +5,6 @@ readme = "README.md"
5
5
  requires-python = ">=3.10"
6
6
  dynamic = ["version"]
7
7
  license = "MIT"
8
- license-files = ["LICENSE"]
9
8
  authors = [{name = "Chang-LeHung"}]
10
9
  keywords = ["agent", "llm", "tui", "terminal", "coding-agent"]
11
10
  classifiers = [
@@ -19,6 +18,7 @@ classifiers = [
19
18
  "Programming Language :: Python :: 3.12",
20
19
  "Programming Language :: Python :: 3.13",
21
20
  "Programming Language :: Python :: 3.14",
21
+ "Programming Language :: Python :: 3.15",
22
22
  "Typing :: Typed",
23
23
  ]
24
24
  dependencies = [
@@ -53,7 +53,7 @@ dependencies = [
53
53
  # than it did. An exact pin would make zettcode fight every other tool that
54
54
  # shares the dependency — including an upgrade of zett-agent itself, which pip
55
55
  # would downgrade to satisfy it.
56
- "zett-agent>=0.1.15,<0.2",
56
+ "zett-agent>=0.1.17,<0.2",
57
57
  ]
58
58
 
59
59
  [project.scripts]
@@ -75,53 +75,8 @@ path = "src/zettcode/__init__.py"
75
75
  packages = ["src/zettcode"]
76
76
 
77
77
  [tool.hatch.build.targets.sdist]
78
- include = ["/src", "/README.md", "/README.zh-CN.md", "/LICENSE", "/pyproject.toml"]
79
-
80
- [dependency-groups]
81
- dev = [
82
- # Unlike the conditional runtime deps, these are installed on every
83
- # interpreter so mypy can check the 3.10 branch of ``_compat`` for real.
84
- "exceptiongroup>=1.2,<2",
85
- "mypy>=1.14,<2",
86
- "pytest>=8,<9",
87
- "pytest-asyncio>=0.26,<2",
88
- "pytest-cov>=7.1.0",
89
- "ruff>=0.11,<1",
90
- "tomli>=2,<3",
91
- "viztracer>=1.1.1",
92
- ]
78
+ include = ["/src", "/README.md", "/pyproject.toml"]
93
79
 
94
80
  [tool.pytest.ini_options]
95
81
  testpaths = ["tests"]
96
82
  asyncio_mode = "auto"
97
-
98
- [tool.mypy]
99
- python_version = "3.10"
100
- files = ["src", "perf"]
101
- # Check the pydantic storage models as models, not as plain classes.
102
- plugins = ["pydantic.mypy"]
103
- warn_unused_ignores = true
104
- warn_redundant_casts = true
105
- no_implicit_optional = true
106
-
107
- [[tool.mypy.overrides]]
108
- # Ships no stubs; the runner only calls the two documented functions.
109
- module = ["pyperclip"]
110
- ignore_missing_imports = true
111
-
112
- [tool.ruff]
113
- line-length = 120
114
- target-version = "py310"
115
- src = ["src", "tests", "perf"]
116
-
117
- [tool.ruff.lint]
118
- select = ["E", "F", "I", "UP", "B"]
119
- ignore = ["E501"]
120
-
121
- [tool.ruff.lint.isort]
122
- known-first-party = ["zettcode"]
123
-
124
- [tool.ruff.format]
125
- quote-style = "double"
126
- indent-style = "space"
127
- line-ending = "lf"
@@ -7,7 +7,7 @@ from typing import TYPE_CHECKING
7
7
 
8
8
  #: The one place the version is written. ``pyproject.toml`` reads it from here
9
9
  #: and the release workflow checks the tag against it, so a bump is one edit.
10
- __version__ = "0.1.7"
10
+ __version__ = "0.1.9"
11
11
 
12
12
  #: Where each public name lives; read by :func:`__getattr__` on first use.
13
13
  _EXPORTS = {
@@ -1,6 +1,6 @@
1
1
  """Small shims for the language versions this project supports.
2
2
 
3
- ZettCode runs on Python 3.10 through 3.14, so the few things the standard
3
+ ZettCode runs on Python 3.10 through 3.15, so the few things the standard
4
4
  library grew in between live here instead of behind version checks at each use.
5
5
  """
6
6
 
@@ -22,7 +22,8 @@ is the composition and the lifecycle — building the runtime, and closing it.
22
22
 
23
23
  from __future__ import annotations
24
24
 
25
- from ....config import ZettCodeConfig
25
+ from zettcode.config import ZettCodeConfig
26
+
26
27
  from ..runtime import ZettCodeRuntime
27
28
  from .commands import CommandsMixin
28
29
  from .context import ContextMixin
@@ -0,0 +1,214 @@
1
+ """What the agent offers the shell to run and to show: its own commands, and what plugins added.
2
+
3
+ The commands report through transcript Markdown; commands that present a widget belong to
4
+ the shell, which owns the widgets. The plugin contributions are listed here because the
5
+ agent is where the shell asks for everything the runtime loaded.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from zettcode.app.commands import Command, CommandContext, CommandHost, CommandResult
11
+ from zettcode.app.pets import PetCatalog, bundled_pets
12
+ from zettcode.plugins import UiRow
13
+ from zettcode.plugins.services import HOST_LINK, HostLink
14
+ from zettcode.providers import BUILTIN_PROVIDERS, Provider, merge_providers
15
+ from zettcode.statusline import NAMES, unknown
16
+ from zettcode.tui import ThemeCatalog
17
+
18
+ from ..conversation.presenters import Presenters
19
+ from ..dialogue.mentions import MentionProvider
20
+ from .exports import ExportsMixin
21
+
22
+ #: The words ``/self-compact`` takes after it, or after ``global``.
23
+ _SELF_COMPACT_WORDS = frozenset({"on", "off", "default"})
24
+
25
+
26
+ class CommandsMixin(ExportsMixin):
27
+ """The ``/new``, ``/compact``, and ``/export`` commands, and the plugins' commands, mentions, and rows."""
28
+
29
+ @property
30
+ def commands(self) -> tuple[Command, ...]:
31
+ """Return the conversation commands the agent owns.
32
+
33
+ They report through transcript Markdown; commands that present a widget
34
+ (such as the model picker) belong to the shell, which owns the widgets.
35
+ """
36
+ return (
37
+ Command("/new", "start a fresh session", "agent", self._command_new),
38
+ Command("/compact", "summarize the context now", "agent", self._command_compact),
39
+ Command("/export", "write this session to an HTML file: /export [path]", "agent", self._command_export),
40
+ )
41
+
42
+ def attach_host(self, host: CommandHost) -> None:
43
+ """Tell the plugins where the shell is, so the words their commands offer can follow the session."""
44
+ link = self.runtime.plugins.find(HOST_LINK, HostLink)
45
+ if link is not None:
46
+ link.attach(host)
47
+
48
+ @property
49
+ def plugin_commands(self) -> tuple[Command, ...]:
50
+ """Return the commands the installed plugins registered, not counting the built-in ones.
51
+
52
+ The shell keeps them after everything built in, so a plugin cannot take over a built-in name;
53
+ plugin-versus-plugin duplicates are rejected when the plugins load.
54
+ """
55
+ return self.runtime.plugins.installed_commands
56
+
57
+ @property
58
+ def builtin_plugin_commands(self) -> tuple[Command, ...]:
59
+ """Return the commands the program's own plugins registered: ``/resume``, ``/model``, ``/usage`` and the rest."""
60
+ return self.runtime.plugins.builtin_commands
61
+
62
+ @property
63
+ def plugin_mentions(self) -> tuple[MentionProvider, ...]:
64
+ """Return the ``@`` resource providers the installed plugins registered."""
65
+ return self.runtime.plugins.mentions
66
+
67
+ @property
68
+ def providers(self) -> tuple[Provider, ...]:
69
+ """Return the hosts ``/config`` can offer: the built-in ones, then the plugins' own."""
70
+ return merge_providers(BUILTIN_PROVIDERS, self.runtime.plugins.providers)
71
+
72
+ @property
73
+ def themes(self) -> ThemeCatalog:
74
+ """Return every palette that can be asked for by name: the plugins', then the reader's own files.
75
+
76
+ Built fresh each time it is asked: it lists names and builds nothing, and the reader's directory may have
77
+ gained a file since the last look.
78
+ """
79
+ return ThemeCatalog(self.runtime.plugins.themes, self.runtime.config.themes_dir)
80
+
81
+ @property
82
+ def pets(self) -> PetCatalog:
83
+ """Return every pet that can be asked for by name: the built-in plugin's, then any other plugin's.
84
+
85
+ A plugin bundle that offers none (one built by hand, without the built-in plugin) gets the program's own.
86
+ """
87
+ sources = self.runtime.plugins.pets
88
+ return PetCatalog(sources or bundled_pets())
89
+
90
+ @property
91
+ def plugin_failures(self) -> tuple[str, ...]:
92
+ """Return one message per plugin that could not be loaded or activated."""
93
+ return self.runtime.plugins.failures
94
+
95
+ @property
96
+ def presenters(self) -> Presenters:
97
+ """Return how this run draws entries: the built-in presenters, and any a plugin outranks them with.
98
+
99
+ Built from the bundle rather than from a module-level default for the same reason the palettes are: the
100
+ program's own rows are registered through the plugin route, so a plugin that draws a kind or a family of
101
+ rows reaches the transcript without a second path only plugins could not take.
102
+ """
103
+ return Presenters(self.runtime.plugins.presenters)
104
+
105
+ @property
106
+ def plugin_rows(self) -> tuple[UiRow, ...]:
107
+ """Return the header and status rows plugins asked the shell to draw."""
108
+ return self.runtime.plugins.rows
109
+
110
+ async def _command_new(self, context: CommandContext) -> CommandResult:
111
+ """Start a fresh session."""
112
+ session_id = self.new_session()
113
+ return CommandResult(notification=f"started session {session_id[:8]}")
114
+
115
+ async def _command_export(self, context: CommandContext) -> CommandResult:
116
+ """Write the session and its context to one HTML file.
117
+
118
+ The path argument is optional; without it the file lands in the
119
+ workspace, named after the session, and the result says where.
120
+ """
121
+ path = await self.export_session(context.argument.strip() or None)
122
+ return CommandResult(message=f"Exported to `{path}`")
123
+
124
+ def self_compaction_summary(self) -> str:
125
+ """Say in one line whether self-compaction is on here, where that comes from, and what the default is."""
126
+ state = "on" if self.self_compaction else "off"
127
+ default = "on" if self.self_compaction_default else "off"
128
+ if self.self_compaction_choice is None:
129
+ return f"Self-compaction is {state}: this session follows the default ({default})"
130
+ return f"Self-compaction is {state}: this session chose it; the default is {default}"
131
+
132
+ def self_compact_report(self) -> str:
133
+ """Say what self-compaction is, whether it is on here, and where that comes from."""
134
+ choice = self.self_compaction_choice
135
+ default = "on" if self.self_compaction_default else "off"
136
+ state = "on" if self.self_compaction else "off"
137
+ source = (
138
+ f"this session chose it; the config default is **{default}**"
139
+ if choice is not None
140
+ else "following the config default (`[compaction] self`)"
141
+ )
142
+ return (
143
+ f"Self-compaction is **{state}** ({source}).\n\n"
144
+ "When on, the model gets a `compact_context` tool and can summarize its own earlier conversation "
145
+ "when it judges the detail is no longer needed. The current turn is always kept. "
146
+ "`/compact` and the automatic threshold work either way.\n\n"
147
+ "`/self-compact on|off` sets this session; `default` hands it back to the config; "
148
+ "`global on|off` changes the default for every session that has not chosen."
149
+ )
150
+
151
+ def change_self_compaction(self, argument: str) -> str:
152
+ """Read or change self-compaction from the words ``/self-compact`` takes, and say what now applies.
153
+
154
+ ``on``, ``off`` and ``default`` set this session's own choice; ``global on`` and ``global off``
155
+ write the default for every session into the config file. A failed write is never reported as saved.
156
+
157
+ Raises:
158
+ ValueError: The words are not ones the command takes, or the change could not be saved.
159
+ """
160
+ words = argument.lower().split()
161
+ scope = "session"
162
+ if words[:1] == ["global"]:
163
+ scope, words = "global", words[1:]
164
+ usage = ValueError("Usage: /self-compact [on|off|default] or /self-compact global on|off")
165
+ if len(words) > 1 or (scope == "global" and not words) or (words and words[0] not in _SELF_COMPACT_WORDS):
166
+ raise usage
167
+ if scope == "global":
168
+ if words[0] == "default":
169
+ raise usage
170
+ self.remember_self_compaction(words[0] == "on")
171
+ elif words:
172
+ self.use_self_compaction({"on": True, "off": False, "default": None}[words[0]])
173
+ return self.self_compact_report()
174
+
175
+ def statusline_summary(self) -> str:
176
+ """Say in one line which parts of the header and the status line are hidden."""
177
+ hidden = [name for name in NAMES if name in self.statusline_hidden]
178
+ if not hidden:
179
+ return "Status line: every part is shown"
180
+ return f"Status line: {len(hidden)} of {len(NAMES)} parts hidden ({', '.join(hidden)})"
181
+
182
+ def change_statusline(self, argument: str) -> str:
183
+ """Hide or show parts of the header and status line from the words ``/statusline`` takes.
184
+
185
+ ``hide <name>...`` and ``show <name>...`` change the named parts, and ``show all`` brings every one
186
+ back. The change is written to ``[statusline]`` in the config file, and nothing is changed when any
187
+ name is not a part.
188
+
189
+ Raises:
190
+ ValueError: The words are not ones the command takes, or the change could not be saved.
191
+ """
192
+ words = argument.lower().split()
193
+ usage = ValueError(
194
+ f"Usage: /statusline [hide|show] <part>... or /statusline show all; parts: {', '.join(NAMES)}"
195
+ )
196
+ if len(words) < 2 or words[0] not in {"hide", "show"}:
197
+ raise usage
198
+ visible = words[0] == "show"
199
+ names = list(NAMES) if visible and words[1:] == ["all"] else words[1:]
200
+ if stray := unknown(names):
201
+ raise ValueError(f"No such part: {', '.join(stray)}. Parts: {', '.join(NAMES)}")
202
+ self.remember_statusline({name: visible for name in names})
203
+ return self.statusline_summary()
204
+
205
+ async def _command_compact(self, context: CommandContext) -> CommandResult:
206
+ """Summarize the conversation now, whatever its size.
207
+
208
+ The row and the outcome notice arrive while this await runs: the pass
209
+ dispatches through the same event dispatcher a turn does, so the
210
+ transcript animates ``Compacting`` and ends with ``Context compaction
211
+ applied`` (or ``skipped`` when the summary would not be smaller).
212
+ """
213
+ await self.compact()
214
+ return CommandResult()
@@ -8,15 +8,17 @@ the rest of the shell does.
8
8
 
9
9
  from __future__ import annotations
10
10
 
11
+ from collections.abc import Sequence
11
12
  from dataclasses import dataclass
12
13
  from pathlib import Path
13
14
 
14
15
  from zett_agent.messages import AssistantMessage, UserMessage
16
+ from zett_agent.model import ReasoningEffort
15
17
 
16
18
  from ..conversation.replay import Replayed, replay
17
19
  from ..conversation.transcript import Transcript
18
20
  from ..dialogue.title import summarize_title
19
- from ..storage import Session, SessionInfo
21
+ from ..storage import Session, SessionInfo, SessionStats
20
22
  from .context import ContextMixin
21
23
 
22
24
  #: The name a session shows before the agent has given it a title.
@@ -170,6 +172,13 @@ class SessionsMixin(ContextMixin):
170
172
  """List persisted session metadata by recent activity."""
171
173
  return await self.runtime.persistence.list_sessions(limit=limit)
172
174
 
175
+ async def session_stats(self, sessions: Sequence[SessionInfo]) -> dict[str, SessionStats]:
176
+ """Return the entry count and token totals of listed sessions, keyed by session id.
177
+
178
+ A session missing from the answer has no totals to show.
179
+ """
180
+ return await self.runtime.persistence.session_stats([session.session_id for session in sessions])
181
+
173
182
  async def rename_session(self, title: str) -> str:
174
183
  """Store a display title for the active session and return it cleaned.
175
184
 
@@ -218,7 +227,9 @@ class SessionsMixin(ContextMixin):
218
227
  if not question or not answer:
219
228
  return None
220
229
  await self.runtime.start()
221
- title = await summarize_title(self.runtime.provider, question=question, answer=answer)
230
+ # A level the model takes, so naming the session cannot be what fails on an endpoint that lacks `low`.
231
+ effort = self.runtime.active_model.accepted_effort(ReasoningEffort.LOW)
232
+ title = await summarize_title(self.runtime.provider, question=question, answer=answer, effort=effort)
222
233
  if title is None:
223
234
  return None
224
235
  await self.runtime.persistence.set_title(session_id, title)