samagotchi 0.152.0 → 0.154.0

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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -1
  3. data/README.md +2 -3
  4. data/bin/chi +23 -24
  5. data/docs/architecture.md +30 -33
  6. data/docs/broadcast.md +5 -5
  7. data/docs/bundle-authoring.md +1 -1
  8. data/docs/bundles.md +93 -12
  9. data/docs/cli.md +83 -99
  10. data/docs/configuration.md +46 -40
  11. data/docs/context.md +3 -3
  12. data/docs/desktop.md +1 -1
  13. data/docs/guardrails.md +15 -15
  14. data/docs/hooks.md +13 -12
  15. data/docs/internals/anthropic.md +3 -3
  16. data/docs/internals/attached-context.md +5 -2
  17. data/docs/internals/background-tasks.md +3 -4
  18. data/docs/internals/block-renderers.md +4 -0
  19. data/docs/internals/bridge.md +3 -3
  20. data/docs/internals/context-telemetry.md +1 -1
  21. data/docs/internals/engine.md +42 -12
  22. data/docs/internals/objectives.md +6 -5
  23. data/docs/internals/participants.md +8 -8
  24. data/docs/internals/prompt-caching.md +8 -4
  25. data/docs/internals/remote-sessions.md +7 -7
  26. data/docs/internals/web-frontend.md +20 -17
  27. data/docs/internals/web-push.md +1 -1
  28. data/docs/memory.md +2 -2
  29. data/docs/plugins.md +52 -43
  30. data/docs/releasing.md +1 -1
  31. data/docs/sessions.md +25 -25
  32. data/docs/sub-agent.md +2 -2
  33. data/docs/system-one.md +1 -1
  34. data/docs/testing.md +18 -8
  35. data/lib/samagotchi/answer_text.rb +34 -0
  36. data/lib/samagotchi/bridge.rb +43 -32
  37. data/lib/samagotchi/bridge_client.rb +10 -9
  38. data/lib/samagotchi/broadcast/recipients.rb +2 -2
  39. data/lib/samagotchi/broadcast/triage.rb +1 -1
  40. data/lib/samagotchi/broadcast_command.rb +5 -5
  41. data/lib/samagotchi/bundle_command/try/command.rb +1 -1
  42. data/lib/samagotchi/bundle_command/try/run.rb +1 -1
  43. data/lib/samagotchi/bundle_command.rb +2 -2
  44. data/lib/samagotchi/bundles/decision-card/manifest.yml +4 -4
  45. data/lib/samagotchi/bundles/decision-card/plugin.rb +1 -9
  46. data/lib/samagotchi/bundles/dev/manifest.yml +2 -2
  47. data/lib/samagotchi/bundles/system/config_modification_protocol.md +3 -4
  48. data/lib/samagotchi/bundles/system/manifest.yml +3 -3
  49. data/lib/samagotchi/bundles/system/self_map.md +1 -1
  50. data/lib/samagotchi/bundles/web-search/manifest.yml +13 -0
  51. data/lib/samagotchi/bundles/web-search/plugin.rb +174 -0
  52. data/lib/samagotchi/child_move.rb +3 -3
  53. data/lib/samagotchi/commands/registry.rb +6 -6
  54. data/lib/samagotchi/composer_route.rb +4 -5
  55. data/lib/samagotchi/config.rb +14 -12
  56. data/lib/samagotchi/context_candidate_choice.rb +1 -1
  57. data/lib/samagotchi/context_candidates.rb +4 -3
  58. data/lib/samagotchi/continue_offer.rb +1 -1
  59. data/lib/samagotchi/debug_log.rb +1 -1
  60. data/lib/samagotchi/degrade.rb +75 -0
  61. data/lib/samagotchi/desktop/macos/ChiRunner.swift +2 -2
  62. data/lib/samagotchi/engine/cards.rb +87 -0
  63. data/lib/samagotchi/engine/context_judge.rb +138 -0
  64. data/lib/samagotchi/engine/events.rb +131 -0
  65. data/lib/samagotchi/engine/guardrails.rb +87 -0
  66. data/lib/samagotchi/engine/hooks.rb +119 -0
  67. data/lib/samagotchi/engine/messages.rb +124 -0
  68. data/lib/samagotchi/engine/model_settings.rb +127 -0
  69. data/lib/samagotchi/engine/models.rb +261 -0
  70. data/lib/samagotchi/engine/objectives.rb +116 -0
  71. data/lib/samagotchi/engine/participants.rb +86 -0
  72. data/lib/samagotchi/engine/plugins.rb +208 -0
  73. data/lib/samagotchi/engine/questions.rb +98 -0
  74. data/lib/samagotchi/engine/recap.rb +130 -0
  75. data/lib/samagotchi/engine/session.rb +131 -0
  76. data/lib/samagotchi/engine/side_requests.rb +135 -0
  77. data/lib/samagotchi/engine/steering.rb +134 -0
  78. data/lib/samagotchi/engine/toolkit.rb +269 -0
  79. data/lib/samagotchi/engine/turn_end.rb +180 -0
  80. data/lib/samagotchi/engine/turn_helpers.rb +160 -0
  81. data/lib/samagotchi/engine/verify.rb +153 -0
  82. data/lib/samagotchi/engine.rb +63 -2642
  83. data/lib/samagotchi/grants.rb +575 -0
  84. data/lib/samagotchi/guardrail_wiring.rb +2 -2
  85. data/lib/samagotchi/guardrails/context.rb +2 -3
  86. data/lib/samagotchi/guardrails/parent_approvals.rb +2 -2
  87. data/lib/samagotchi/guardrails/parent_policy.rb +2 -2
  88. data/lib/samagotchi/guardrails/review_boundary.rb +4 -3
  89. data/lib/samagotchi/hook_routes.rb +3 -3
  90. data/lib/samagotchi/idle_recap.rb +4 -5
  91. data/lib/samagotchi/idle_reminders.rb +3 -4
  92. data/lib/samagotchi/iteration_limit.rb +2 -2
  93. data/lib/samagotchi/kernel_loop.rb +10 -11
  94. data/lib/samagotchi/launch_mode.rb +13 -32
  95. data/lib/samagotchi/level_setting_command.rb +3 -2
  96. data/lib/samagotchi/llm_context_command.rb +3 -2
  97. data/lib/samagotchi/llm_context_stale.rb +2 -2
  98. data/lib/samagotchi/log.rb +7 -7
  99. data/lib/samagotchi/log_line.rb +1 -1
  100. data/lib/samagotchi/log_path.rb +1 -1
  101. data/lib/samagotchi/message_parts.rb +3 -2
  102. data/lib/samagotchi/note_command.rb +1 -1
  103. data/lib/samagotchi/note_delivery.rb +18 -8
  104. data/lib/samagotchi/objectives.rb +24 -3
  105. data/lib/samagotchi/objectives_check.rb +8 -27
  106. data/lib/samagotchi/objectives_command.rb +4 -69
  107. data/lib/samagotchi/objectives_edit.rb +87 -0
  108. data/lib/samagotchi/output_formatter.rb +7 -1
  109. data/lib/samagotchi/owner_lock.rb +7 -1
  110. data/lib/samagotchi/parent_answers_command.rb +5 -4
  111. data/lib/samagotchi/participant_exposure.rb +42 -0
  112. data/lib/samagotchi/participant_mode.rb +1 -1
  113. data/lib/samagotchi/participant_name.rb +1 -1
  114. data/lib/samagotchi/participants.rb +17 -17
  115. data/lib/samagotchi/participants_watch.rb +1 -1
  116. data/lib/samagotchi/pending_input_queue.rb +2 -2
  117. data/lib/samagotchi/plugin/api.rb +3 -2
  118. data/lib/samagotchi/plugin/context.rb +21 -7
  119. data/lib/samagotchi/plugin/peer.rb +2 -1
  120. data/lib/samagotchi/plugin/sessions.rb +6 -4
  121. data/lib/samagotchi/plugin_tasks.rb +1 -2
  122. data/lib/samagotchi/private_files.rb +29 -0
  123. data/lib/samagotchi/project_env.rb +2 -2
  124. data/lib/samagotchi/question_desk.rb +8 -7
  125. data/lib/samagotchi/recap_command.rb +2 -1
  126. data/lib/samagotchi/recap_setup.rb +1 -1
  127. data/lib/samagotchi/recap_store.rb +1 -1
  128. data/lib/samagotchi/redact.rb +86 -0
  129. data/lib/samagotchi/reminder_queue.rb +2 -2
  130. data/lib/samagotchi/remote_attach_command.rb +1 -1
  131. data/lib/samagotchi/reply_wait/poll.rb +2 -2
  132. data/lib/samagotchi/review_start.rb +1 -1
  133. data/lib/samagotchi/run_options.rb +6 -6
  134. data/lib/samagotchi/self_report.rb +42 -39
  135. data/lib/samagotchi/send_command/busy.rb +1 -1
  136. data/lib/samagotchi/send_command.rb +4 -1
  137. data/lib/samagotchi/session.rb +11 -2
  138. data/lib/samagotchi/session_access/contract.rb +3 -3
  139. data/lib/samagotchi/session_access/local.rb +9 -9
  140. data/lib/samagotchi/session_access/remote.rb +17 -7
  141. data/lib/samagotchi/session_access.rb +4 -4
  142. data/lib/samagotchi/session_archive_command.rb +2 -2
  143. data/lib/samagotchi/session_chain.rb +8 -6
  144. data/lib/samagotchi/session_commands.rb +45 -48
  145. data/lib/samagotchi/session_continue.rb +1 -1
  146. data/lib/samagotchi/session_delete_command.rb +4 -4
  147. data/lib/samagotchi/session_manager.rb +59 -36
  148. data/lib/samagotchi/session_metrics.rb +2 -2
  149. data/lib/samagotchi/session_model_settings.rb +3 -2
  150. data/lib/samagotchi/session_participants.rb +1 -1
  151. data/lib/samagotchi/session_retention.rb +3 -3
  152. data/lib/samagotchi/sessions_command.rb +4 -4
  153. data/lib/samagotchi/share_command.rb +10 -10
  154. data/lib/samagotchi/steer.rb +1 -1
  155. data/lib/samagotchi/system_prompt.rb +102 -109
  156. data/lib/samagotchi/task_watch.rb +1 -1
  157. data/lib/samagotchi/terminal_ui/attach_launcher.rb +72 -10
  158. data/lib/samagotchi/terminal_ui/attached_loop/composer.rb +12 -9
  159. data/lib/samagotchi/terminal_ui/attached_loop/exit_commands.rb +16 -0
  160. data/lib/samagotchi/terminal_ui/attached_loop/questions.rb +2 -3
  161. data/lib/samagotchi/terminal_ui/attached_loop.rb +17 -8
  162. data/lib/samagotchi/terminal_ui/attached_view.rb +2 -3
  163. data/lib/samagotchi/terminal_ui/event_renderer.rb +3 -5
  164. data/lib/samagotchi/terminal_ui/formatting.rb +7 -7
  165. data/lib/samagotchi/terminal_ui/input_support.rb +1 -7
  166. data/lib/samagotchi/terminal_ui/line_reader.rb +2 -2
  167. data/lib/samagotchi/terminal_ui/live_region.rb +2 -2
  168. data/lib/samagotchi/terminal_ui/log_tail.rb +135 -0
  169. data/lib/samagotchi/terminal_ui/one_shot_sink.rb +2 -2
  170. data/lib/samagotchi/terminal_ui/plain_surface.rb +1 -1
  171. data/lib/samagotchi/terminal_ui/question_prompt.rb +4 -5
  172. data/lib/samagotchi/terminal_ui/reline_seam.rb +3 -3
  173. data/lib/samagotchi/terminal_ui/status_row.rb +7 -8
  174. data/lib/samagotchi/terminal_ui.rb +142 -1387
  175. data/lib/samagotchi/title_command.rb +2 -2
  176. data/lib/samagotchi/tool_runner.rb +14 -16
  177. data/lib/samagotchi/toolkit.rb +8 -5
  178. data/lib/samagotchi/toolkit_binding.rb +30 -5
  179. data/lib/samagotchi/toolkit_command.rb +2 -1
  180. data/lib/samagotchi/toolkit_report_command.rb +9 -5
  181. data/lib/samagotchi/toolkit_suggest.rb +12 -6
  182. data/lib/samagotchi/tools/builtin_calls.rb +14 -10
  183. data/lib/samagotchi/tools/deferred_tools.rb +2 -2
  184. data/lib/samagotchi/tools/delegate.rb +3 -1
  185. data/lib/samagotchi/tools/delegate_cursor.rb +3 -3
  186. data/lib/samagotchi/tools/delegate_wait.rb +1 -1
  187. data/lib/samagotchi/tools/objectives_tool.rb +2 -2
  188. data/lib/samagotchi/tools/registry.rb +44 -20
  189. data/lib/samagotchi/tools/send_note.rb +2 -1
  190. data/lib/samagotchi/tools/tool_index.rb +27 -21
  191. data/lib/samagotchi/turn_endings.rb +2 -2
  192. data/lib/samagotchi/turn_flow.rb +4 -5
  193. data/lib/samagotchi/update_hint.rb +2 -2
  194. data/lib/samagotchi/version.rb +1 -1
  195. data/lib/samagotchi/web/app.rb +39 -50
  196. data/lib/samagotchi/web/participant_access.rb +2 -2
  197. data/lib/samagotchi/web/participant_review.rb +5 -5
  198. data/lib/samagotchi/web/participant_stream_filter.rb +8 -8
  199. data/lib/samagotchi/web/participant_view.rb +2 -82
  200. data/lib/samagotchi/web/participants_admin.rb +1 -1
  201. data/lib/samagotchi/web/public/app.js +61 -357
  202. data/lib/samagotchi/web/public/block_host.js +35 -8
  203. data/lib/samagotchi/web/public/block_renderers.css +9 -5
  204. data/lib/samagotchi/web/public/chains.js +2 -2
  205. data/lib/samagotchi/web/public/claim_link.js +4 -4
  206. data/lib/samagotchi/web/public/composer_mode.js +110 -0
  207. data/lib/samagotchi/web/public/format.js +3 -3
  208. data/lib/samagotchi/web/public/owner_claim.js +81 -0
  209. data/lib/samagotchi/web/public/owner_verify.js +112 -0
  210. data/lib/samagotchi/web/public/session_fields.js +3 -3
  211. data/lib/samagotchi/web/public/strip_grip.js +3 -3
  212. data/lib/samagotchi/web/public/strip_hidden.js +65 -0
  213. data/lib/samagotchi/web/public/version_notices.js +132 -0
  214. data/lib/samagotchi/web/push/store.rb +1 -1
  215. data/lib/samagotchi/web/session_access_routes.rb +5 -4
  216. data/lib/samagotchi/web/session_control.rb +12 -8
  217. data/lib/samagotchi/web/session_create.rb +1 -1
  218. data/lib/samagotchi/web/session_hub.rb +2 -2
  219. data/lib/samagotchi/web/session_show.rb +2 -2
  220. data/lib/samagotchi/web/session_summary.rb +1 -1
  221. data/lib/samagotchi/worker.rb +106 -9
  222. data/lib/samagotchi/worker_link.rb +3 -3
  223. data/lib/samagotchi/working_set_command.rb +3 -2
  224. metadata +36 -6
  225. data/lib/samagotchi/terminal_ui/between_turns.rb +0 -138
  226. data/lib/samagotchi/terminal_ui/repl_input.rb +0 -152
  227. data/lib/samagotchi/web/grants.rb +0 -575
  228. data/lib/samagotchi/web/participant_exposure.rb +0 -44
  229. data/lib/samagotchi/web/private_files.rb +0 -31
data/docs/plugins.md CHANGED
@@ -96,9 +96,8 @@ This adds a slash command. `name` is `/name`: a lowercase letter first, then a
96
96
  block returns the text to show (a String), or nil to show nothing. If the
97
97
  block raises, the user sees `/name: <error>`.
98
98
 
99
- A plugin command works in all three UIs:
99
+ A plugin command works in both UIs, and in a `chi -p "/hello" --non-interactive` one-shot:
100
100
 
101
- - **REPL** (`chi --no-shared`): typed at the prompt, and Tab completes it.
102
101
  - **Attached TUI** (the default `chi`): the worker's snapshot names the
103
102
  session's commands, so the TUI sends `/hello` to the worker and Tab
104
103
  completes it. It needs a worker started after the bundle was installed.
@@ -107,7 +106,7 @@ A plugin command works in all three UIs:
107
106
  runs it too.
108
107
 
109
108
  A line that is not a known command keeps its old meaning: in the terminal
110
- UIs `/foo` goes to the model as a prompt; the web refuses it and names the
109
+ `/foo` goes to the model as a prompt; the web refuses it and names the
111
110
  commands it knows.
112
111
 
113
112
  Any whitespace may follow the name, a newline too: `/hello` on its own line
@@ -117,7 +116,7 @@ blank lines and indentation as typed; only the ends are stripped).
117
116
  #### `anytime: true`
118
117
 
119
118
  A plugin's normal command waits for its turn: typed while a turn runs, it is
120
- refused as busy (the REPL puts it back into the prompt, the web into an empty composer; a plugin command can't
119
+ refused as busy (the web puts it back into an empty composer; a plugin command can't
121
120
  ask to run after the turn, as the built-in `/model X` does). An `anytime: true` command
122
121
  runs **at once, on its own thread, beside the running turn**, and the turn
123
122
  goes on:
@@ -128,9 +127,6 @@ goes on:
128
127
  then its cards and notices **as it shows them**, and its output (the
129
128
  `command_ran`) when it finishes. So a card can say "working…" first and
130
129
  be replaced by the result.
131
- - In the REPL, typed while a turn runs, it starts on a thread. Its cards
132
- print as it shows them, above the live region; its output prints there
133
- too while the turn runs, or at the prompt once the turn has ended.
134
130
 
135
131
  Between turns an anytime command runs like any other, except that its
136
132
  cards print as it shows them rather than after its output.
@@ -142,8 +138,8 @@ running turn: they are not rows of the turn's step, and their events carry
142
138
  Its block runs on another thread than the turn, so it must be thread-safe:
143
139
 
144
140
  - Read the conversation through `ctx.messages`, a frozen copy. While a turn
145
- runs, a worker's holds that turn so far; the REPL's is the conversation
146
- **before** that turn (`ctx.messages_partial?` says so).
141
+ runs, a worker's holds that turn so far; a `--non-interactive` one-shot's
142
+ is the conversation **before** that turn (`ctx.messages_partial?` says so).
147
143
  - Show things only through `ctx` (`ctx.card`, `ctx.notify`), and return
148
144
  the text to show.
149
145
  - Keep your own state (instance variables) behind a `Mutex` if two
@@ -171,8 +167,7 @@ thinking is dropped). The TUI prefills `line --yes`, the web shows an Apply butt
171
167
  then runs again with `--yes` in `args` (drop it from your words). Outside a command (a hook, an init task, your
172
168
  own thread) it is always nil. A plain String or nil return is unchanged.
173
169
 
174
- A command that asks must not be `anytime: true`: the REPL shows only an anytime command's text, so its Apply would
175
- be lost. Needs chi 0.132.0.
170
+ A command that asks must not be `anytime: true`: its Apply would be lost. Needs chi 0.132.0.
176
171
 
177
172
  ```ruby
178
173
  chi.command "/tone", "show or set this session's tone" do |args, ctx|
@@ -191,8 +186,8 @@ end
191
186
  `/help` (itself an anytime command) lists every command the session knows:
192
187
  chi's own, each bundle's with the bundle's name, and the terminal UIs' own
193
188
  (`/stats`, `/exit`, `/detach` …), marked where they run: `terminal only`,
194
- `attached only`, `repl only`, or `terminal and web only` (`/cut`).
195
- It works in all three UIs.
189
+ `web only`, or `terminal and web only` (`/cut`).
190
+ It works in both UIs.
196
191
 
197
192
  ### `chi.annotation(label, command:, template: "{quote}", send: false)`
198
193
 
@@ -244,8 +239,13 @@ Web only: the terminals type the command (`/claude validate <text>`).
244
239
 
245
240
  ### `chi.tool(name, description, params:, schema:, label:, preview:, targets:, refs:) { |args, ctx| … }`
246
241
 
247
- This adds a tool the model can call. It is declared in the system prompt and
248
- in the chat path's `tools:`, after chi's own tools.
242
+ This adds a tool the model can call. A session's toolkit decides whether it
243
+ is declared, as for a built-in ([Toolkits](configuration.md#toolkits)): `full`
244
+ (and any kit on `base: full`) declares it, in the system prompt and in the
245
+ chat path's `tools:`, after chi's own tools; a kit that doesn't list it (`lean`,
246
+ `child`) leaves it **reachable**, found with `find_tools` as `chi/<name>` and
247
+ run by `call_tool` as the tool itself ([the chi group](#chitool_sourcecall-acts_as-label-wait-prefix));
248
+ `add:` declares it there, `remove:` takes it away.
249
249
 
250
250
  - `name`: a–z first, then a–z, 0–9 and `_`, up to 48 characters.
251
251
  - `params`: `{ name => property }`, where a property is a JSON Schema
@@ -462,6 +462,12 @@ end
462
462
  tool raises `ArgumentError` at once, as `chi.tool` does, and nothing is
463
463
  staged.
464
464
  - Inside `register`, use `chi.tool`: `replace_tools` raises there.
465
+ - Whether the session needs `find_tools` and `call_tool` is set by the tools
466
+ registered when its worker started. A tool a set adds on a kit that
467
+ leaves it out is reachable when the kit declares them anyway (`lean` does,
468
+ for its left-out built-ins); on a kit that declares neither it stays out
469
+ of reach until the user declares it (`/toolkit add NAME`), with one warn
470
+ notice by your bundle's name.
465
471
 
466
472
  ### `chi.on(event, priority: 100) { |event, ctx| … }`
467
473
 
@@ -548,8 +554,8 @@ end
548
554
  so far run, and the next `value` tries again.
549
555
  - `service.running?`, `service.state` (`:idle`, `:running`, `:stopped`) and
550
556
  `service.stop`.
551
- - The services stop when chi leaves: the REPL exits, or the session's
552
- worker exits (an idle exit, `/exit`, a crash, TERM). See
557
+ - The services stop when chi leaves: the session's worker exits (an idle
558
+ exit, `/exit`, a crash, TERM), or a `--non-interactive` one-shot ends. See
553
559
  [Shutdown](#shutdown). A stopped service never starts again; `value`
554
560
  raises `Samagotchi::Plugin::Service::Stopped`.
555
561
  - A plugin whose load fails after it started services has them stopped.
@@ -565,7 +571,7 @@ repo, logging in, starting a server for the first time. `register` itself
565
571
  should return at once (everything in it runs before the session's UI is
566
572
  up), so it hands the slow part to `chi.init`. The block runs **on its own
567
573
  thread** once the session can show it: in a worker right after its Bridge
568
- is up (so a new web chat opens at once), in the REPL at its first prompt.
574
+ is up (so a new web chat opens at once), in a `--non-interactive` one-shot as its turn starts.
569
575
 
570
576
  ```ruby
571
577
  class Plugin
@@ -589,7 +595,7 @@ end
589
595
 
590
596
  - **What the UIs show.** Every UI shows a running task (web: a spinner line
591
597
  over the composer, `mcp · Starting MCP server chrome (first run, saving
592
- its tools)…`; the attached TUI: its activity row; the REPL: a line) and a
598
+ its tools)…`; the attached TUI: its activity row) and a
593
599
  line when it is done: `✓` and what the block returned, a short summary
594
600
  (`chrome ready, 3 tools`), or `<label>: done` for anything else. A UI that
595
601
  joins while it runs sees it too.
@@ -684,11 +690,12 @@ two tools in all, whatever the number of sources: `find_tools(query,
684
690
  group?)` (a keyword search that answers each match's `inputSchema`) and
685
691
  `call_tool(tool, args)` (calls one as `<group>/<tool>`; its row label is the
686
692
  first source's `label`). The index has one group of its own, **`chi`**: the
687
- built-ins the session's toolkit leaves out but doesn't remove
688
- ([toolkits](configuration.md#toolkits)), each with its registered schema.
693
+ built-ins and plugin tools (`chi.tool`) the session's toolkit leaves out but
694
+ doesn't remove ([toolkits](configuration.md#toolkits)), each with its
695
+ registered schema; never `find_tools` and `call_tool` themselves.
689
696
  The two tools are registered once, after the plugins load, and declared per
690
- toolkit when it reaches a source group or has a reachable built-in (`full`
691
- with no source declares neither). Nothing a
697
+ toolkit when it reaches a source group or has a reachable tool, as the tools
698
+ stood then (`full` with no source declares neither). Nothing a
692
699
  source publishes later changes the request's tools, only what a search
693
700
  answers, so the prompt cache keeps: the search tool's description lists the
694
701
  source groups as they were at load, and a `chi` line first, per toolkit.
@@ -703,24 +710,25 @@ session's toolkit. A source's
703
710
  group named `chi`, or named like an earlier group, is left out of search,
704
711
  lookup and the description, with one warning per name.
705
712
 
706
- **A built-in through `call_tool` runs as that built-in.** `call_tool(tool:
713
+ **A chi tool through `call_tool` runs as that tool.** `call_tool(tool:
707
714
  "chi/delegate", args: {…})` (or a bare `delegate` while it's reachable) is
708
715
  turned into the `delegate` call itself before the guardrails, so rules on
709
716
  `delegate`, approvals (keyed `delegate:…`, the same as a direct call's),
710
717
  `before_tool_call`/`after_tool_call` hooks and the row see `delegate`, with
711
718
  `call[:via] == "call_tool"`; the row reads "delegate (via call_tool)", live
712
- and after a reload. `tool: call_tool` rules see MCP calls and misses only. A
713
- `chi/` name the toolkit removes or declares, or any built-in once
714
- `call_tool` itself is removed, is refused by its name before the guardrails
715
- (no hook, no card).
719
+ and after a reload. A plugin tool's the same: `chi/web_search` runs as
720
+ `web_search`, with its `label:`, `preview:` and `targets:`. `tool:
721
+ call_tool` rules see MCP calls and misses only. A `chi/` name the toolkit
722
+ removes or declares, or any chi tool once `call_tool` itself is removed, is
723
+ refused by its name before the guardrails (no hook, no card).
716
724
 
717
725
  **On a Messages host the model calls them by name too.** With `api:
718
726
  anthropic` (not Fireworks' dialect), every tool `call_tool` reaches goes in
719
727
  the request's tools deferred (`defer_loading`: its name only, until
720
- something loads it): a built-in by its name, a source's tool by its
728
+ something loads it): a built-in or plugin tool by its name, a source's tool by its
721
729
  `acts_as` (`mcp_fs_read`; one with a refusal, an acts_as two tools share, or
722
730
  a name another tool has stays out). A direct call of one runs whether
723
- anything loaded it or not: a built-in as itself (as if declared), a
731
+ anything loaded it or not: a built-in or plugin tool as itself (as if declared), a
724
732
  source's tool as the `call_tool` call it stands for, so its rules,
725
733
  approvals, hooks and row are `call_tool`'s. Elsewhere such a call is
726
734
  refused with the `call_tool` route, as before.
@@ -806,7 +814,7 @@ A cheap System 1 question this bundle asks with `ctx.judge` or `ctx.judge_later`
806
814
  The plugin's context (the `ctx` its handlers get), for `register` itself:
807
815
  its settings, log and data_dir, for example. A `ctx.notify` or `ctx.card`
808
816
  while chi starts (inside `register`) is shown once the session's UI can show
809
- it (a worker's Bridge is up, the REPL's first prompt), after the plugins'
817
+ it (a worker's Bridge is up), after the plugins'
810
818
  load warnings; a UI that joins later still gets it.
811
819
 
812
820
  ### Names
@@ -835,7 +843,7 @@ full label, `plugin.rb (bundle my-bundle)`.
835
843
  | `ctx.scratch?` | whether the session is a `chi scratch` one (deleted when it ends) |
836
844
  | `ctx.delegate?` | whether the session is a delegate child: a task another session handed over |
837
845
  | `ctx.review?` | whether the session is a review session (`chi review`): one its participants read, not the owner's working session |
838
- | `ctx.frontend` | what runs the session, read now: `:repl` (the REPL, `-p` without `--non-interactive`), `:one_shot` (`-p --non-interactive`: one turn, then exit) or `:worker` (a session worker: the web, an attached TUI). Only a worker's answers reach the web while it runs; a REPL's show there once the session is opened. Until the host sets it (while the plugins load, inside `Engine.new`) it reads `:one_shot`, so read it in hooks and init tasks, not in `register` |
846
+ | `ctx.frontend` | what runs the session, read now: `:one_shot` (`-p --non-interactive`: one turn, then exit) or `:worker` (a session worker: the web, an attached TUI). There is no `:repl` any more: a bundle that checks for it never matches. Only a worker's answers reach the web while it runs; a one-shot's show there once the session is opened. Until the host sets it (while the plugins load, inside `Engine.new`) it reads `:one_shot`, so read it in hooks and init tasks, not in `register` |
839
847
  | `ctx.continues` | the id of the session this one continues (a [continue](sessions.md#session-chains)'s new link), else nil |
840
848
  | `ctx.fork?` | whether the session is a fork (`ctx.sessions.fork`): it has a parent but isn't a delegate child, and started from a conversation, so its first prompt isn't its first user message |
841
849
  | `ctx.model` | the model the session runs on now: its resolved ref (`host:id`), right after `/model` too |
@@ -848,8 +856,9 @@ full label, `plugin.rb (bundle my-bundle)`.
848
856
  | `ctx.data_dir` | `$XDG_STATE_HOME/samagotchi/plugins/<bundle>/`, created on first use: what the bundle keeps across sessions |
849
857
  | `ctx.session_dir` | this session's own folder there, created on use, read now; nil with no session yet. `ctx.session_dir(create: false)` is the same path with no folder made, for a read, so a session that saved nothing leaves none (a `File.read` of what isn't there raises `Errno::ENOENT`); needs chi newer than 0.143.0. chi removes it when the session is deleted, discarded or pruned, so don't build the path yourself. A `sessions/<id>.json` an older version of your bundle kept moves in as `state.json` (chi still removes one left there). Needs chi newer than 0.142.0 |
850
858
  | `ctx.log` | `ctx.log.info(:event, key: value)`: debug-log records tagged `plugins`, with `bundle=<bundle>` |
851
- | `ctx.messages` | the conversation, as a frozen copy, without the system prompt. While a turn runs, a session worker's (attached, web) adds that turn so far: its prompt, the model's text and the lines merged into it (no tool calls or thinking); the REPL's is the conversation before that turn. An entry may carry `at`, when it happened (docs/sessions.md, Message times) |
852
- | `ctx.messages_partial?` | whether `ctx.messages` leaves out a running turn (the REPL mid-turn), so a plugin can say what its answer is about |
859
+ | `ctx.messages` | the conversation, as a frozen copy, without the system prompt. While a turn runs, a session worker's (attached, web) adds that turn so far: its prompt, the model's text and the lines merged into it (no tool calls or thinking); a `--non-interactive` one-shot's is the conversation before that turn. An entry may carry `at`, when it happened (docs/sessions.md, Message times) |
860
+ | `ctx.last_answer` | the newest answer's text as the user saw it: the newest answer bubble of `ctx.messages`, its thoughts (one cut off before it closed too) and tool-call markup taken out, the rule chi's continue note uses. nil when there is none or it was a thought alone; mid-turn, a worker's can be the running turn's interim text. Needs chi newer than 0.153.0 |
861
+ | `ctx.messages_partial?` | whether `ctx.messages` leaves out a running turn (a `--non-interactive` one-shot mid-turn), so a plugin can say what its answer is about |
853
862
  | `ctx.prompt_files` | the files whose text is in the session's system prompt now: the AGENTS.md chain (the work tree top down to the cwd) and the appended files (`append_system_prompt:`, `--append-system-prompt`); absolute paths, in prompt order, `[]` before the first build |
854
863
  | `ctx.objectives` | the session's [objectives](internals/objectives.md), read now, as a frozen value: `empty?`, `items` (each `id`, `text`, `status` `"active"`, `"done"` or `"dropped"`, `done_when`), `find(id)`, `text` (the lines the model is shown) and `moves` (the latest move per id; mid-turn, a worker's leaves the running turn's out). An edit not yet absorbed counts. Empty when the session has none. Use its methods, not its class. Needs chi newer than 0.148.0 |
855
864
  | `ctx.notify(text, level: :info, fallback_for: nil)` | one line to the user, labelled by the bundle (`my-bundle> …`). Every UI shows it, during a turn (a tool, a hook) or between turns (a command). `fallback_for: :display` marks a line that repeats the answer's display (`event[:present]`), which a UI that renders the display leaves out (`fallback_for: :editor`: a file the viewer could open from a ref in its editor): see [Hooks](hooks.md#what-a-hook-can-do-the-runtime). The keyword needs chi 0.35.0 (it raises on an older chi) |
@@ -865,9 +874,9 @@ full label, `plugin.rb (bundle my-bundle)`.
865
874
  | `ctx.http` | an HTTP client for calling services (`get`, `post_json`, `request`): see [Calling services](#calling-services-ctxhttp) |
866
875
  | `ctx.sessions` | fork, send to, read, list and stop other sessions: see [Other sessions](#other-sessions-ctxsessions) |
867
876
  | `ctx.toolkit` | the name of the session's toolkit (`full` for a session from before toolkits); nil with no session |
868
- | `ctx.declared_tools` | the tool names the session's next turn declares (its toolkit resolved, with its own adds and removes): ask this, not the name, whether a tool is there |
877
+ | `ctx.declared_tools` | the tool names the session's next turn declares (its toolkit resolved, with its own adds and removes): ask this, not the name, whether a tool is there. Your bundle's own `chi.tool` tools are in it only on a kit that declares them (`full`, an `add:`); elsewhere they are reachable, not declared (the testing kit answers every tool, as on full, unless `bundle.set(declared_tools: […])`) |
869
878
  | `ctx.use_toolkit(name)` | put the session on toolkit `name` (yours from `chi.toolkit`, or chi's): saved, applied at the next turn's start and announced (`toolkit> toolkit: NAME from the next turn's start`, the web's tools chip follows). Only before the session's first model reply and outside a turn (the declared tools start every request, and a plugin doesn't ask first); otherwise it returns false and changes nothing: tell the user to run `/toolkit NAME`. An unknown name raises ArgumentError. Needs chi 0.66.0 |
870
- | `ctx.load_tools(names)` | load tools the session reaches but doesn't declare, so the model calls them by name: built-ins (`"delegate"`, `"chi/delegate"`), MCP tools (`"mcp_fs_read"`, `"fs/read"`). Returns the names it staged (the wire names); a declared tool, one loaded before, a removed or unknown one is skipped (an unknown one logged), so calling it every turn is fine. From a `before_turn` hook it loads for that turn; anywhere else (mid-turn too) from the next turn's start. Call it from a hook: before the session's first turn its toolkit isn't applied yet and nothing resolves. **A hint, not access**: the tools keep their guardrails and approvals, and only a host that takes tool changes (`api: anthropic`, not Fireworks) gets one, as a system message after the user's (`[tools loaded: delegate (your-bundle)]` in the session, a notice line live); elsewhere nothing is sent or saved, and the tools stay reachable with `call_tool`. A hook may switch the model after you call it, so the return value can't promise delivery. Raises ArgumentError for anything but an Array. Needs chi 0.99.0 |
879
+ | `ctx.load_tools(names)` | load tools the session reaches but doesn't declare, so the model calls them by name: built-ins and plugin tools (`"delegate"`, `"chi/delegate"`, `"chi/web_search"`), MCP tools (`"mcp_fs_read"`, `"fs/read"`). Returns the names it staged (the wire names); a declared tool, one loaded before, a removed or unknown one is skipped (an unknown one logged), so calling it every turn is fine. From a `before_turn` hook it loads for that turn; anywhere else (mid-turn too) from the next turn's start. Call it from a hook: before the session's first turn its toolkit isn't applied yet and nothing resolves. **A hint, not access**: the tools keep their guardrails and approvals, and only a host that takes tool changes (`api: anthropic`, not Fireworks) gets one, as a system message after the user's (`[tools loaded: delegate (your-bundle)]` in the session, a notice line live); elsewhere nothing is sent or saved, and the tools stay reachable with `call_tool`. A hook may switch the model after you call it, so the return value can't promise delivery. Raises ArgumentError for anything but an Array. Needs chi 0.99.0 |
871
880
  | `ctx.context` | attach outside text to this session and list it: see [Attached context](#attached-context-ctxcontext) |
872
881
  | `ctx.cache_confirm(line, changed:)` | inside a command: nil to apply a prompt-cache-restarting change now, or a confirm to return as `[text, confirm]`: see [Commands that ask](#commands-that-ask-ctxcache_confirm). Needs chi 0.132.0 |
873
882
 
@@ -895,7 +904,7 @@ keeps JSON numbers and booleans. A name that is a core session setting is always
895
904
  ## Cards
896
905
 
897
906
  A card is a small framed message with buttons: a title, a body and
898
- actions. Core draws it in all three UIs; a plugin has no JS or CSS of its
907
+ actions. Core draws it in both UIs (the attached TUI and the web); a plugin has no JS or CSS of its
899
908
  own.
900
909
 
901
910
  ```ruby
@@ -933,7 +942,6 @@ Where it shows:
933
942
 
934
943
  | | during a turn (a tool, a hook) | between turns (a command) |
935
944
  |---|---|---|
936
- | REPL | where it happens, above the live region | at the prompt, after the command's output |
937
945
  | attached TUI | where it happens | as it arrives |
938
946
  | web | a row of the running step | between the turns |
939
947
 
@@ -1218,7 +1226,7 @@ ctx.sessions.review(subjects: ["docs/plan.md"], seed: handoff) # => a review se
1218
1226
  prefix); it runs as a turn, and a stopped session is woken. It waits up to
1219
1227
  5 s for the session's worker, so call it from an anytime command or a
1220
1228
  thread of your own, never from a tool or hook of a running turn. A
1221
- session open in a chi REPL can't take it.
1229
+ session a `--non-interactive` one-shot has can't take it.
1222
1230
  - A running turn takes it at its next step. `delivery: :queue` runs it as
1223
1231
  a turn of its own after that turn instead (as `/queue TEXT` does; to an
1224
1232
  idle session it simply runs). A plugin never cuts the running turn's
@@ -1497,6 +1505,7 @@ bundle.judgments.runs("progress").last["asked"] # the runs as chi saves
1497
1505
  bundle.sessions.on(:children) { |all:| [] } # ctx.sessions, scripted and recorded
1498
1506
  bundle.http.stub(:post, "http://127.0.0.1:9000/classify", json: { label: "safe" }) # ctx.http, never the network
1499
1507
  bundle.set(running: false) # the session's values: cwd:, messages:, cancelled: …
1508
+ bundle.set(declared_tools: %w[read]) # ctx.declared_tools (default: every tool, as on full)
1500
1509
  bundle.set(cost: 2.5) # ctx.cost: reported USD, or { reported:, estimated:, side:, known: }
1501
1510
  bundle.set(usage: { tool_calls: 40, active_ms: 600_000 }) # ctx.usage: { turns:, tool_calls:, tool_calls_by_tool:, steps:, active_ms:, tool_ms_by_tool: }
1502
1511
  bundle.set(turn_author: "p:p_0123abcd") # a participant's turn: ctx.turn_author, and author: on every event
@@ -1581,8 +1590,8 @@ docs/testing.md). **Minitest:** `require "samagotchi/testing/minitest"` and subc
1581
1590
 
1582
1591
  ## Shutdown
1583
1592
 
1584
- When the REPL exits, or a session's worker exits (an idle exit, `/exit`, a
1585
- crash, TERM), chi shuts the session's Engine down:
1593
+ When a session's worker exits (an idle exit, `/exit`, a crash, TERM), or a
1594
+ `--non-interactive` one-shot ends, chi shuts the session's Engine down:
1586
1595
 
1587
1596
  1. The idle jobs (reminders, the recap) stop.
1588
1597
  2. The plugins' init tasks are cancelled (`ctx.cancelled?` turns true).
@@ -1593,8 +1602,8 @@ crash, TERM), chi shuts the session's Engine down:
1593
1602
 
1594
1603
  ## Loading, and when it fails
1595
1604
 
1596
- Plugins load when a session starts (`Engine.new`, in the REPL or a
1597
- worker), after the bundle hooks. The steps are:
1605
+ Plugins load when a session starts (`Engine.new`, in a worker or a
1606
+ `--non-interactive` one-shot), after the bundle hooks. The steps are:
1598
1607
 
1599
1608
  1. The file must match the sha256 recorded at install (not checked for a bundle installed with `--dev`).
1600
1609
  2. chi must meet `requires_chi`. A bundle whose `requires_chi` this chi doesn't
data/docs/releasing.md CHANGED
@@ -13,7 +13,7 @@ can run the whole release; the user approves the notes before the tag and the
13
13
  and `rake release:check` enforce it). The system bundle upgrades itself when
14
14
  chi starts.
15
15
  - **Every other shipped bundle** (btw, check-in, guardrails, known-names,
16
- loop-guard, mcp, skills, slow-commands, agents-md, cost-cap, work-cap, source-links, github-pr, coordinator, claude-peer, codex-peer, ruby, model-notes, decision-card, and the profiles core and dev) has its own semver in its
16
+ loop-guard, mcp, skills, slow-commands, agents-md, cost-cap, work-cap, source-links, github-pr, coordinator, claude-peer, codex-peer, ruby, model-notes, decision-card, web-search, and the profiles core and dev) has its own semver in its
17
17
  `manifest.yml` and, when it needs a newer chi, a `requires_chi:` line. They never upgrade by themselves: users run
18
18
  `chi update` (all of them, with the gem) or `chi bundle upgrade NAME`. So:
19
19
  - a change to a bundle's files bumps that bundle's `version:`