samagotchi 0.151.0 → 0.153.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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -1
  3. data/docs/bundle-authoring.md +5 -5
  4. data/docs/bundles.md +84 -0
  5. data/docs/cli.md +35 -10
  6. data/docs/configuration.md +19 -14
  7. data/docs/guardrails.md +11 -5
  8. data/docs/hooks.md +4 -3
  9. data/docs/internals/anthropic.md +3 -3
  10. data/docs/internals/attached-context.md +3 -0
  11. data/docs/internals/block-renderers.md +3 -1
  12. data/docs/internals/bundles.md +48 -14
  13. data/docs/internals/engine.md +9 -5
  14. data/docs/internals/objectives.md +6 -4
  15. data/docs/internals/participants.md +1 -1
  16. data/docs/internals/prompt-caching.md +7 -3
  17. data/docs/internals/web-frontend.md +20 -17
  18. data/docs/memory.md +6 -1
  19. data/docs/plugins.md +80 -29
  20. data/docs/releasing.md +8 -3
  21. data/docs/sessions.md +2 -2
  22. data/docs/testing.md +2 -2
  23. data/lib/samagotchi/block_renderers.rb +9 -8
  24. data/lib/samagotchi/bundle_command/check.rb +43 -3
  25. data/lib/samagotchi/bundle_command/installed_check.rb +6 -2
  26. data/lib/samagotchi/bundle_command/try/sandbox.rb +11 -3
  27. data/lib/samagotchi/bundle_command.rb +88 -10
  28. data/lib/samagotchi/bundle_needs.rb +4 -6
  29. data/lib/samagotchi/bundle_settings_check.rb +14 -13
  30. data/lib/samagotchi/bundles/agents-md/manifest.yml +2 -2
  31. data/lib/samagotchi/bundles/btw/manifest.yml +2 -2
  32. data/lib/samagotchi/bundles/loop-guard/manifest.yml +2 -2
  33. data/lib/samagotchi/bundles/mcp/manifest.yml +2 -2
  34. data/lib/samagotchi/bundles/skills/manifest.yml +2 -2
  35. data/lib/samagotchi/bundles/slow-commands/manifest.yml +2 -2
  36. data/lib/samagotchi/bundles/system/manifest.yml +2 -2
  37. data/lib/samagotchi/bundles/system/memory_guide.md +3 -3
  38. data/lib/samagotchi/bundles/web-search/manifest.yml +13 -0
  39. data/lib/samagotchi/bundles/web-search/plugin.rb +174 -0
  40. data/lib/samagotchi/child_env.rb +15 -0
  41. data/lib/samagotchi/context_providers.rb +42 -10
  42. data/lib/samagotchi/engine.rb +37 -9
  43. data/lib/samagotchi/extension_load.rb +4 -3
  44. data/lib/samagotchi/guardrail_wiring.rb +28 -19
  45. data/lib/samagotchi/guardrails/approval.rb +2 -2
  46. data/lib/samagotchi/guardrails/review_boundary.rb +4 -3
  47. data/lib/samagotchi/guardrails/rules.rb +1 -0
  48. data/lib/samagotchi/guardrails/verdict.rb +20 -0
  49. data/lib/samagotchi/hooks/bundle_loader.rb +5 -7
  50. data/lib/samagotchi/llm_context_stale.rb +2 -2
  51. data/lib/samagotchi/memory_bundle/asset_installer.rb +11 -8
  52. data/lib/samagotchi/memory_bundle/builder.rb +36 -30
  53. data/lib/samagotchi/memory_bundle/bundle_hook.rb +3 -2
  54. data/lib/samagotchi/memory_bundle/file_digest.rb +31 -0
  55. data/lib/samagotchi/memory_bundle/file_standing.rb +2 -2
  56. data/lib/samagotchi/memory_bundle/installed_bundle.rb +55 -46
  57. data/lib/samagotchi/memory_bundle/installed_copy.rb +38 -15
  58. data/lib/samagotchi/memory_bundle/installed_manifest.rb +224 -0
  59. data/lib/samagotchi/memory_bundle/installer.rb +90 -16
  60. data/lib/samagotchi/memory_bundle/manifest.rb +146 -6
  61. data/lib/samagotchi/memory_bundle/manifest_standing.rb +171 -0
  62. data/lib/samagotchi/memory_bundle/merger.rb +7 -8
  63. data/lib/samagotchi/memory_bundle/profile.rb +3 -2
  64. data/lib/samagotchi/memory_bundle/provenance.rb +217 -18
  65. data/lib/samagotchi/memory_bundle/shas.rb +4 -3
  66. data/lib/samagotchi/memory_bundle/shipped_update.rb +52 -24
  67. data/lib/samagotchi/memory_bundle/status.rb +11 -10
  68. data/lib/samagotchi/memory_bundle/system_bundle.rb +13 -2
  69. data/lib/samagotchi/message_parts.rb +3 -2
  70. data/lib/samagotchi/plugin/loader.rb +1 -1
  71. data/lib/samagotchi/session_config.rb +5 -4
  72. data/lib/samagotchi/system_one/record.rb +10 -2
  73. data/lib/samagotchi/toolkit.rb +8 -5
  74. data/lib/samagotchi/toolkit_binding.rb +30 -5
  75. data/lib/samagotchi/toolkit_report_command.rb +9 -5
  76. data/lib/samagotchi/toolkit_suggest.rb +12 -6
  77. data/lib/samagotchi/tools/builtin_calls.rb +14 -10
  78. data/lib/samagotchi/tools/deferred_tools.rb +2 -2
  79. data/lib/samagotchi/tools/delegate.rb +20 -2
  80. data/lib/samagotchi/tools/registry.rb +44 -20
  81. data/lib/samagotchi/tools/tool_index.rb +27 -21
  82. data/lib/samagotchi/update_command.rb +13 -2
  83. data/lib/samagotchi/version.rb +1 -1
  84. data/lib/samagotchi/web/app.rb +2 -1
  85. data/lib/samagotchi/web/public/app.js +61 -357
  86. data/lib/samagotchi/web/public/composer_mode.js +110 -0
  87. data/lib/samagotchi/web/public/owner_claim.js +81 -0
  88. data/lib/samagotchi/web/public/owner_verify.js +112 -0
  89. data/lib/samagotchi/web/public/session_fields.js +3 -3
  90. data/lib/samagotchi/web/public/strip_grip.js +3 -3
  91. data/lib/samagotchi/web/public/strip_hidden.js +65 -0
  92. data/lib/samagotchi/web/public/version_notices.js +132 -0
  93. data/lib/samagotchi/web/session_access_routes.rb +2 -1
  94. data/lib/samagotchi/web/session_hub.rb +6 -0
  95. data/lib/samagotchi/worker.rb +6 -0
  96. metadata +11 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 24e1f2b0d0bd36a11e7fc2de449630696a6d7e3431c24b6def08a70dd3d27487
4
- data.tar.gz: 42ae4e62a4474d254ee294a887a856fde273d99f7a7d6b5adc961f08eaf2fb7d
3
+ metadata.gz: e9dd8da2f6f95806fccc0df5dad4844525947650b92589461adcb70b8bcdf2d7
4
+ data.tar.gz: a1457568c9d119b661d025be9e39989a33593666d668b328c3cd8528e451aa55
5
5
  SHA512:
6
- metadata.gz: fde71cbc5d64e63d34a0c85cae516c8f0f37aecda9d1b099803f023b00e31a95653374c3f8725fa82e4dcfd72aefd65b1de4d36f3a6823f0a55f6028a3a25aa4
7
- data.tar.gz: 5ead8e63f46e4b8fdb10ea3b19706c8733a0ff1e30d5f129fc3dd4e1595c92d13d6a0da424335963bcc1683e3309eb73958470951b02607b7255ba960b201405
6
+ metadata.gz: 6ed8fbbd7439b3ff95cb6eb251edf5635812cff0579928469a575bbb4bdc190e14ff6959a2f49eb627f127f711c1782c62c99379a2130433e057d0a55dd2e359
7
+ data.tar.gz: 82b22635aad92e8edb672e61ac0eed1c461c1c81330bfde64ac09cd8c969183e37f9420495cbb6b0fbe0624f9a552a17af9cb70a2707b0f483084d25fb10d936
data/CHANGELOG.md CHANGED
@@ -5,8 +5,51 @@ format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
5
5
  versions follow [Semantic Versioning](https://semver.org/); before 1.0, config
6
6
  and commands may change between minor versions. How releases are made:
7
7
  [docs/releasing.md](docs/releasing.md).
8
+
8
9
  ## [Unreleased]
9
10
 
11
+ ## [0.153.0] - 2026-11-06
12
+
13
+ ### Added
14
+
15
+ - A `web-search` bundle (`chi bundle install web-search`, in no profile): a `web_search` tool that searches the web through your own SearXNG and returns titles, URLs and snippets for `web_fetch` to read. It is declared on `full` and kits on `base: full`; on kits built on `lean` or `child` it is reachable (`find_tools` finds `chi/web_search`, `call_tool` runs it); `add: [web_search]` in a kit declares it. [docs/bundles.md](docs/bundles.md#the-web-search-bundle)
16
+
17
+ ### Changed
18
+
19
+ - A bundle's tools (`chi.tool`) follow a toolkit's tool list as built-ins do: `full`, the kits on `base: full` (the `kits:` default) and a kit's `add:` declare them, any other kit leaves them reachable through `find_tools` / `call_tool` (deferred on `api: anthropic`), and `remove:` removes them. [docs/configuration.md](docs/configuration.md#toolkits)
20
+
21
+ ### Fixed
22
+
23
+ - An attached context change that arrives while the worker's leaving pass is already in flight is saved as a note before the worker stops, instead of being dropped with its poller.
24
+
25
+ Update with `chi update` (no installed bundle's version moved; `chi bundle install web-search` adds the new one).
26
+
27
+ ## [0.152.0] - 2026-11-06
28
+
29
+ ### Added
30
+
31
+ - `chi bundle install` and `chi bundle check` name a manifest key this chi doesn't read (`unknown-key`); `chi bundle check` also warns (`requires-chi-floor`) when a manifest uses a key newer than its `requires_chi` admits. [docs/plugins.md](docs/plugins.md)
32
+ - `chi bundle status NAME` has a `Manifest:` line saying where the bundle's manifest is read from, and why.
33
+
34
+ ### Changed
35
+
36
+ - An installed bundle keeps its `manifest.yml` and chi reads it live, so a newer chi reads keys the installing chi didn't know, with no reinstall. Bundles installed earlier get theirs on `chi update` (shipped bundles, or a folder or archive that is still there; the system bundle at the next start); for one from git, `chi update` names the `chi bundle upgrade NAME` to run. [docs/internals/bundles.md](docs/internals/bundles.md)
37
+ - `chi bundle install` and `chi bundle upgrade` refuse a bundle whose `requires_chi` this chi doesn't meet, and write nothing; `--force` installs it anyway. [docs/plugins.md](docs/plugins.md)
38
+ - `chi bundle upgrade NAME` upgrades a bundle from a folder, archive or git URL from the source its record keeps: the name alone is enough.
39
+ - "Stable for bundles" now covers the manifest schema. [docs/plugins.md](docs/plugins.md)
40
+
41
+ ### Fixed
42
+
43
+ - `chi bundle diff NAME FILE` shows the bundle's new text for a file that stays behind, so the sections the "stays behind" line names can be read.
44
+ - `chi bundle try` no longer passes the user's dropped settings (guardrails, memories, hooks, log…) to its sandboxed chi through the environment.
45
+ - A bundle's `context_providers:` are skipped when this chi is older than the bundle's `requires_chi`.
46
+ - Reinstalling a bundle from the same source says what it did: `Up to date:` for unchanged files, and a last line naming what was rewritten and which of your memories were kept.
47
+ - A `files:` entry with no checksum no longer warns "Checksum mismatch" on every install.
48
+ - Objectives proposal cards no longer appear after a canceled or failed turn.
49
+ - A `delegate` child in a linked worktree is no longer told to re-delegate to the main checkout.
50
+
51
+ Update with `chi update` (agents-md 0.1.2, btw 0.1.4, loop-guard 0.3.10, mcp 0.7.6, skills 0.1.9, slow-commands 0.2.5: their `requires_chi` now names the chi that reads their settings).
52
+
10
53
  ## [0.151.0] - 2026-11-05
11
54
 
12
55
  ### Added
@@ -5690,7 +5733,9 @@ and long-lived sessions.
5690
5733
  - A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
5691
5734
  hotkey panel that send selected text or the clipboard to your sessions.
5692
5735
 
5693
- [Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.151.0...HEAD
5736
+ [Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.153.0...HEAD
5737
+ [0.153.0]: https://github.com/dm1try/samagotchi/compare/v0.152.0...v0.153.0
5738
+ [0.152.0]: https://github.com/dm1try/samagotchi/compare/v0.151.0...v0.152.0
5694
5739
  [0.151.0]: https://github.com/dm1try/samagotchi/compare/v0.150.0...v0.151.0
5695
5740
  [0.150.0]: https://github.com/dm1try/samagotchi/compare/v0.149.0...v0.150.0
5696
5741
  [0.149.0]: https://github.com/dm1try/samagotchi/compare/v0.148.0...v0.149.0
@@ -119,7 +119,7 @@ end
119
119
  `questions` record effects; `bundle.answer` / `model_answers` provide `ask_user` / `ask_model` answers.
120
120
  `bundle.judgments` answers `ctx.judge` and records its asks and runs (each judgment is on in the kit, off in chi until the user sets it);
121
121
  `bundle.http.stub` stubs `ctx.http`; `bundle.question_asked(text, question:)` fires `:question_asked` and returns
122
- the refs; `sandbox.config`, `sandbox.fake_executable` and `bundle.install` cover setup.
122
+ the refs; `sandbox.config`, `sandbox.fake_executable` and `bundle.install` cover setup. The kit's `ctx.declared_tools` is every tool, as on `full`; in chi a `chi.tool` is declared only on `full` and by a kit's `add:`, and on `lean` or `child` the model reaches it with `find_tools` (`chi/<name>`) and `call_tool` ([Toolkits](configuration.md#toolkits)), so `bundle.set(declared_tools: %w[read])` tests that case and the tool's description should be easy to search.
123
123
  The full list is in [plugins.md, Testing a bundle](plugins.md#testing-a-bundle-experimental). The kit is
124
124
  experimental (it may change before chi 1.0). A failing matcher that meets `ENV` prints `#<the process ENV, contents not shown>`, not your keys. If a test needs a fake of chi's own (a stub of a `Samagotchi::`
125
125
  class, a hand-built `ctx`), that is a gap in the kit: say so to the user rather than reaching into chi.
@@ -129,10 +129,10 @@ class, a hand-built `ctx`), that is a gap in the kit: say so to the user rather
129
129
  - **Shas.** Every file the manifest names has a sha256 line, and an edit makes it stale: the install then warns
130
130
  of a checksum mismatch, and a line with no sha installs unchecked. `chi bundle check --fix` rewrites them; run it
131
131
  after the last edit.
132
- - **requires_chi.** Use the chi the user runs (`chi --version`). `check` warns when the plugin calls something
133
- newer than the floor (`ctx.http` needs 0.80.0, `ctx.cost` 0.83.0, `ctx.usage` 0.100.0, `ctx.sessions.children` 0.34.0). A keyword added
134
- later counts too (`ctx.ask_model(model:)` 0.80.0, `ctx.sessions.send(delivery:)` 0.64.0, `chi.toolkit(prompt:)`
135
- 0.72.0, `chi.toolkit(groups:)` 0.121.0), when the call is written on `ctx` / `chi` itself (not through a variable).
132
+ - **requires_chi.** Use the chi the user runs (`chi --version`): a bundle whose `requires_chi` that chi doesn't
133
+ meet is now **refused at install** (`--force` is the only way past it), not just unloaded. `check` warns when the plugin calls something newer than the floor (`ctx.http` 0.80.0, `ctx.cost` 0.83.0, `ctx.usage` 0.100.0, `ctx.sessions.children` 0.34.0),
134
+ or a keyword added later (`ctx.ask_model(model:)` 0.80.0, `ctx.sessions.send(delivery:)` 0.64.0, `chi.toolkit(prompt:)` 0.72.0, `chi.toolkit(groups:)` 0.121.0), written on `ctx` / `chi` itself rather than through a variable,
135
+ or when the manifest itself uses a key newer than the floor (`block_renderers:` 0.151.0, `settings:` 0.143.0, `session_settings:` 0.84.0, `context_providers:` 0.32.0): an older chi ignores that key.
136
136
  - **A typo in an API call.** `check` warns (`api`) on a call or keyword this chi lacks, on any path; for a newer chi on purpose, guard it with `ctx.respond_to?(:name)`.
137
137
  - **trust_level.** Leave it out. `reviewed` / `trusted` are for someone who reviewed the bundle, not its author.
138
138
  - **Restart.** A session loads bundles when it starts: after an install or a settings change, the user starts a
data/docs/bundles.md CHANGED
@@ -225,6 +225,90 @@ bundles:
225
225
  tools, or with different args, stay distinct, since the tool is in the
226
226
  args).
227
227
 
228
+ ## The web-search bundle
229
+
230
+ `chi bundle install web-search` installs the bundle shipped with chi (`lib/samagotchi/bundles/web-search/plugin.rb`).
231
+ No profile includes it, since it needs a running [SearXNG](https://docs.searxng.org), so `chi update` lists it as
232
+ not installed until you install it. It gives the model one tool, `web_search`, which asks your SearXNG through its
233
+ JSON API. `web_fetch` fetches a URL but can't find one; `web_search` finds them, and the model then reads the ones it
234
+ wants with `web_fetch`. It is an ordinary plugin tool, so it works on every host and loop.
235
+
236
+ - **The tool:** `web_search(query, time_range?, page?)`. `time_range` is `day`, `week`, `month` or `year`; `page`
237
+ starts at 1. It returns plain text: a header, the first answer SearXNG gives (if any), a numbered list of results
238
+ (title, URL, and a snippet of at most 300 characters), and SearXNG's suggestions:
239
+
240
+ ```
241
+ SearXNG results for "ruby prism parser" (page 1):
242
+ 1. Prism — Ruby parser
243
+ https://github.com/ruby/prism
244
+ A portable, error-tolerant Ruby parser…
245
+ Suggestions: ruby prism gem, prism vs parser
246
+ ```
247
+
248
+ With nothing found it says `No results for "<query>".` and names the engines that failed. A SearXNG that can't be
249
+ reached, a 403 (JSON not enabled), a 429 (its limiter) or an answer that isn't JSON each give an `Error:` line that
250
+ says what to fix.
251
+
252
+ ```yaml
253
+ # config.yml
254
+ bundles:
255
+ web-search:
256
+ url: http://127.0.0.1:8888 # the SearXNG root
257
+ max_results: 8 # results listed, 1..20
258
+ timeout: 10 # seconds per read
259
+ # optional, sent only when set:
260
+ language: en
261
+ safesearch: 0 # 0, 1 or 2
262
+ categories: general # comma-separated
263
+ engines: duckduckgo,brave # comma-separated
264
+ ```
265
+
266
+ | key | default | |
267
+ |---|---|---|
268
+ | `url` | `http://127.0.0.1:8888` | the SearXNG root, `http` or `https` with a host; a trailing `/` is fine |
269
+ | `max_results` | `8` | clamped to 1..20 |
270
+ | `timeout` | `10` | seconds; a value that isn't a positive number uses the default |
271
+ | `language` | unset | unset (the default): SearXNG picks the language from the query; set it (e.g. `en`) to force one; dates and some results may follow your region |
272
+ | `safesearch` | unset | `0`, `1` or `2` |
273
+ | `categories`, `engines` | unset | comma-separated. SearXNG silently drops names it doesn't know, so a typo gives its defaults |
274
+
275
+ - **Privacy:** the query goes to your SearXNG and on to the engines it is configured with. No guardrail rule asks
276
+ about it by default; add one (`tool: web_search`, `verdict: ask`) if you want to approve each search. A review
277
+ turn can't call it.
278
+ - **Toolkits:** the tool follows a kit's list like a built-in ([Toolkits](configuration.md#toolkits)). `full` (and
279
+ any kit on `base: full`) declares it (about 150 tokens per request); on `lean`, `child` and the kits built on them
280
+ it is reachable, not declared: the model finds it with `find_tools` (`chi/web_search`) and calls it with
281
+ `call_tool` (on `api: anthropic` it goes deferred and is called by name). To declare it on such a kit, `add:` it
282
+ (or `/toolkit add web_search` in a session); to keep it away entirely, `remove:` it (or `/toolkit remove
283
+ web_search`).
284
+
285
+ ```yaml
286
+ toolkit:
287
+ default: search
288
+ kits:
289
+ search: { base: lean, add: [web_search] }
290
+ quiet: { base: full, remove: [web_search] }
291
+ ```
292
+
293
+ ### Running SearXNG locally
294
+
295
+ ```sh
296
+ mkdir -p searxng
297
+ cat > searxng/settings.yml <<'YAML'
298
+ use_default_settings: true
299
+ server:
300
+ secret_key: "change-me-to-a-random-string" # openssl rand -hex 32
301
+ limiter: false # a local instance: no rate limiting (else 429s)
302
+ search:
303
+ formats: [html, json] # json is off by default (else 403)
304
+ YAML
305
+ docker run -d --name searxng -p 127.0.0.1:8888:8080 -v ./searxng:/etc/searxng searxng/searxng
306
+ ```
307
+
308
+ The port is bound to loopback, not the LAN. Under colima, run it from a folder in your home directory: colima
309
+ shares only that with its VM by default, and elsewhere SearXNG starts on its own default settings (JSON off).
310
+ `curl 'http://127.0.0.1:8888/search?q=ruby&format=json'` should answer JSON.
311
+
228
312
  ## The loop-guard bundle
229
313
 
230
314
  `chi bundle install loop-guard` installs the bundle shipped with chi (`chi
data/docs/cli.md CHANGED
@@ -36,12 +36,12 @@
36
36
  - `chi self` — print version, source dir (checkout or installed gem), config/memory/session paths, model/host and bundles. Run by a session's `execute`, the `model` row (and host, loop, profile, thinking, served model) is **that session's** model, labelled `(this session <id8>; default …)`; elsewhere it is the default, `(default)`. `model key` is the memory overlay key (`<name>.<key>.md`), and `big memories` the memories over `memory.entry_warn_tokens` ([Memory](memory.md#a-memorys-size)), and `model notes` the model notes a prompt of that model loads (`model_notes_deepseek (system, 612 chars)`, `none`; inside a session its `--mute` list is left out). `chi self --model` prints only the model a new session starts on, an alias resolved to its target (the desktop helper's hint)
37
37
  - `chi update [--dry-run] [--to VERSION] [--json] [--no-gem] [--no-bundles] [--no-desktop]` — update an installed chi: the gem, the system bundle, the shipped bundles you installed and the desktop helper, and check the bundles from elsewhere, in one table (see [Updating](#updating))
38
38
  - `chi home`, `chi home set [--ruby PATH]`, `chi home doctor` — the chi home: the one Ruby (and its gem path) every chi launch boots, whoever launches it. `chi home` shows it (its Ruby, the newest chi in it, gem home and path, `mise`, when and by which chi it was set, and the chi you ran when that is another one; exit 1 with the problem when it can't boot chi), `set` records this chi's Ruby and gem path (or `--ruby PATH`'s own; it must have samagotchi), `doctor` checks it and what should follow it, one row each with `ok`, `warn` or `fail` and what to run (exit 1 on a `fail`; see [Checking it](configuration.md#checking-it-chi-home-doctor)). At a terminal, a chi on another Ruby than the home's says so in one dim line a day. This machine only (see [The chi home](configuration.md#the-chi-home))
39
- - `chi bundle install|upgrade|uninstall|status|diff|list|build|trash|new|test|check` — manage memory bundles (see [Bundle hooks](hooks.md#bundle-hooks-unified-workflow-bundle)); `list` shows the installed ones and the ones shipped with chi, which `install <name>` installs; `core` and `dev` are profiles that install a set of them (see [Bundle profiles](memory.md#bundle-profiles-core-and-dev), [Guardrails](guardrails.md), [Bundles](bundles.md#the-btw-bundle), [the mcp bundle](bundles.md#the-mcp-bundle) [the loop-guard bundle](bundles.md#the-loop-guard-bundle), [the check-in bundle](bundles.md#the-check-in-bundle) and [the skills bundle](bundles.md#the-skills-bundle)). `trash` lists and empties the bundle trash (moved files from uninstalls/upgrades). A usage error (an unknown subcommand or flag, a missing argument, a bad `build --scope`) exits 2, as for the other subcommands. Plain `chi` and `chi web` exit 1 on an unknown flag, a flag missing its value or a stray argument. `new`, `test` and `check` are for writing one: see [Writing a bundle](#writing-a-bundle-new-test-check)
39
+ - `chi bundle install|upgrade|uninstall|status|diff|list|build|trash|new|test|check` — manage memory bundles (see [Bundle hooks](hooks.md#bundle-hooks-unified-workflow-bundle)); `list` shows the installed ones and the ones shipped with chi, which `install <name>` installs; `upgrade <name>` upgrades an installed bundle by name from the source it came from (its folder, archive or git URL), `upgrade <source>` from a new one; `core` and `dev` are profiles that install a set of them (see [Bundle profiles](memory.md#bundle-profiles-core-and-dev), [Guardrails](guardrails.md), [Bundles](bundles.md#the-btw-bundle), [the mcp bundle](bundles.md#the-mcp-bundle) [the loop-guard bundle](bundles.md#the-loop-guard-bundle), [the check-in bundle](bundles.md#the-check-in-bundle) and [the skills bundle](bundles.md#the-skills-bundle)). `trash` lists and empties the bundle trash (moved files from uninstalls/upgrades). `install`/`upgrade` `--force` overwrites existing entries and also installs a bundle whose `requires_chi` this chi doesn't meet; without it such a bundle is refused (exit 1), nothing written. A plain `install` of a bundle that is already installed from the same source rewrites its hooks, rules, plugin, scripts and record: the summary lists a file that already holds the bundle's bytes as `Up to date: …`, one you wrote as `Skipped: …`, and ends with `NAME was already installed from SOURCE: its hooks, rules, plugin and scripts now match it and its record was rewritten` (plus, when a memory was kept, `your own copies of its memories were kept (chi bundle upgrade NAME merges the bundle's changes into them)`). A usage error (an unknown subcommand or flag, a missing argument, a bad `build --scope`) exits 2, as for the other subcommands. Plain `chi` and `chi web` exit 1 on an unknown flag, a flag missing its value or a stray argument. `new`, `test` and `check` are for writing one: see [Writing a bundle](#writing-a-bundle-new-test-check)
40
40
 
41
41
  - `chi guardrails replay [--dry-run] [--threshold T] [--model M]` — seed System 1's record from the approvals this machine's sessions already answered: each ask is judged by the configured model and written to `guardrails/judgments.jsonl` with the answer the user gave, then a per-rule table prints (asks, allowed, denied, would-be auto-allows, false allows, asks with no judgment). A parent agent's answer is not the user's: only its deny is replayed, and only where the parent could have allowed. `--dry-run` judges without writing, `--threshold` overrides `guardrails.system_one.threshold` for the table, `--model` the configured model for this run. Exit 2 when `guardrails.system_one.mode` is off or no `host` is set; a usage error exits 2 (see [System 1](guardrails.md#system-1-experimental))
42
42
  - `chi guardrails rule <id>` — print exactly `asks` when that rule is loaded, not disabled and votes `ask` in this `guardrails.mode` for every model; any other line says why not (no such rule, disabled, another mode, it denies, for some models only, rules that failed to load, `guardrails.enabled: false`). Exit 0 either way, 2 for a usage error. It reads config.yml and the installed bundles as a worker does (`GuardrailWiring#rules`) and takes the rule's id, never a command; the [coordinator skill](bundles.md#the-coordinator-bundle) reads it before it merges without asking
43
43
  - `chi judgments ls` / `chi judgments report NAME [--days N]` — what [System 1's saved runs](system-one.md) hold: each judgment's digests, runs, labels, failures, models and size; one judgment's counts, p histogram and labels by value. Reads only; local only; `--days` must be a positive whole number.
44
- - `chi toolkit report [--days N]` — grade toolkit suggestions: the kit on mode would have run (the suggested kit when it would have applied it, else the default; an on-mode line's `did` says what it ran) against the tools the session called from that turn on (only that turn when the toolkit was chosen since), `call_tool` targets included, for both the chat and the raw-prompt loop. Each is *covers*, *short of a tool* (naming the tools), *wider than needed* (a narrower kit offered in the same ask would also have covered) or *ungradable* (calls it can't read back); then a per-kit tally with what on mode would have done. Includes archived sessions and caps the report at 14 days and 500 sessions. Reads `toolkit_suggestions.jsonl` (each ask logs every offered kit's rules, expanded against the called tools here); it never runs the model or changes a session. Local only; `--days` must be a positive whole number.
44
+ - `chi toolkit report [--days N]` — grade toolkit suggestions: the kit on mode would have run (the suggested kit when it would have applied it, else the default; an on-mode line's `did` says what it ran) against the tools the session called from that turn on (only that turn when the toolkit was chosen since), `call_tool` targets included, for both the chat and the raw-prompt loop. Each is *covers*, *short of a tool* (naming the tools; a built-in or plugin tool the kit left reachable but didn't declare counts), *wider than needed* (a narrower kit offered in the same ask would also have covered) or *ungradable* (calls it can't read back); then a per-kit tally with what on mode would have done. Includes archived sessions and caps the report at 14 days and 500 sessions. Reads `toolkit_suggestions.jsonl` (each ask logs every offered kit's rules, expanded against the called tools here); it never runs the model or changes a session. Local only; `--days` must be a positive whole number.
45
45
 
46
46
  - `chi bundle try PATH -p TEXT [options]` — install a bundle in a temporary, filtered sandbox, run one real model turn,
47
47
  and print its trace (see [Trying a bundle](#trying-a-bundle)).
@@ -133,8 +133,10 @@ The loop for a bundle you're writing (the whole path: [bundle-authoring.md](bund
133
133
  chi's Ruby (minitest is one of chi's gems; rspec is optional). The tests run the bundle's code as written, with
134
134
  your environment (the kit sandboxes each test's HOME and XDG dirs, not the process).
135
135
  - `chi bundle install --dev PATH` installs the bundle in the local directory `PATH` (not a git URL, archive or name:
136
- exit 2) as a link to it: its plugin, hooks, rules and scripts are `PATH`'s own files, so an edit applies at the
137
- next worker or `chi web` restart with no re-install, and their sha256 isn't checked, for this bundle only (the
136
+ exit 2) as a link to it: its plugin, hooks, rules, scripts and `manifest.yml` are `PATH`'s own files, so an edit applies at the
137
+ next worker or `chi web` restart with no re-install (a manifest edit to `block_renderers:` shows in chi web's next
138
+ request; a new `scripts:`, `hooks:` or `plugin:` entry needs `chi bundle install --dev PATH` again, since the record
139
+ has no entry for it), and their sha256 isn't checked, for this bundle only (the
138
140
  record says `dev`; every other bundle is checked as before). Its memories are copied at install. `chi bundle
139
141
  status` shows `NAME vX dev → /abs/path`. `chi bundle upgrade NAME` refuses it (it follows its source already; to
140
142
  leave dev mode: `chi bundle uninstall NAME`, then `chi bundle install` without `--dev`), as does a plain install
@@ -147,9 +149,13 @@ The loop for a bundle you're writing (the whole path: [bundle-authoring.md](bund
147
149
  so run it (and `test`) on code you'd install. The codes, in order: `manifest` (a parse error, verbatim; the run
148
150
  stops), `missing-file`, `unlisted` (warn: a file an install leaves out, by `MemoryFiles`' rules, so a README
149
151
  isn't one), `sha` (a stale or placeholder sha, or a plugin / hook / script entry with no sha line; `--fix`
150
- rewrites the first two and prints `fixed sha FILE → sha256:…`), `requires-chi` (this chi doesn't satisfy it:
152
+ rewrites the first two and prints `fixed sha FILE → sha256:…`), `unknown-key` (warn: a manifest key this chi's
153
+ parser never reads, so it ignores it, one row per key at its line; the install keeps the manifest, so a newer
154
+ chi reads the key from it with no reinstall, and `chi update`'s re-check names it too), `requires-chi` (this chi doesn't satisfy it:
151
155
  error; missing on a bundle with code: warn), `requires-chi-floor` (warn: the plugin calls an API newer than the
152
- oldest chi `requires_chi` admits, from `Plugin::Api::SINCE`), `trust` (warn: `trust_level: reviewed|trusted` on a
156
+ oldest chi `requires_chi` admits, from `Plugin::Api::SINCE`, or the manifest itself uses a key newer than it, from
157
+ `Manifest::SINCE`; an installed bundle gets the unknown-key row but never the floor, as it's the author's fix),
158
+ `trust` (warn: `trust_level: reviewed|trusted` on a
153
159
  bundle chi doesn't ship), `clash` (warn: a name chi ships, or one installed from elsewhere; the sandboxed install
154
160
  then forces), `install` (each Installer warning, a `needs` one as warn, and each plugin or hook load failure at
155
161
  the source `file:line`; skipped while a placeholder sha is unfilled), `registered` (info: each command, tool,
@@ -313,7 +319,7 @@ Also shipped, not installed: dev (mcp, skills, source-links) (chi bundle install
313
319
  file in the bundle trash and `merged 0.8.0 into your FILE` in the row. A real
314
320
  conflict, no `git`, or no recorded base keeps your file untouched, and the row
315
321
  says `FILE stays behind 0.8.0:` with why and what it missed (up to three headings
316
- and a count, or `2 places`). `chi bundle diff NAME FILE` shows it; `chi bundle
322
+ and a count, or `2 places`). `chi bundle diff NAME FILE` shows it (the base, your file, then the bundle's new text); `chi bundle
317
323
  upgrade NAME --force` takes the bundle's, with a copy of yours in the bundle
318
324
  trash first. A later run retries such a file at the same version: its row reads
319
325
  `0.8.0 merged 0.8.0 into your FILE` when it merges, and names it as behind when
@@ -367,6 +373,26 @@ Also shipped, not installed: dev (mcp, skills, source-links) (chi bundle install
367
373
  update` again). A failed gem install checks against this chi, since it stays.
368
374
  - `--no-bundles` (`update.bundles: false`) leaves these out too: no check, no row
369
375
  but `bundles skipped`.
376
+ - **Bundles installed by an older chi** have no kept `manifest.yml` (it didn't
377
+ keep it), so chi reads what their install recorded, and a manifest
378
+ key that chi didn't know (`block_renderers:` before 0.151.0) is missing. Where
379
+ chi has the bundle's own manifest at the installed version, the update keeps it,
380
+ once: a shipped bundle from chi's shipped copy (`up to date (kept its manifest)`;
381
+ the system bundle at the next chi start), a bundle from a folder or a zip/tar
382
+ that is still there from it (a `kept its manifest` row; the archive is extracted
383
+ and the extract removed). Only when the source is what's installed: the same
384
+ name and version, the same manifest values as the install recorded (a key the
385
+ older chi didn't know aside), and the same memories, plugin, hooks, scripts and rules. A git source
386
+ isn't cloned: the row names the command,
387
+ `installed by an older chi, which didn't keep its manifest: chi bundle upgrade NAME reads it again from URL`.
388
+ A source that's gone or doesn't match gets its own row (why, and what to run: for
389
+ a gone one, `NAME's recorded source S is gone: chi bundle upgrade <path or URL of a copy>`). The
390
+ same goes for a record an older chi rewrote after a newer one kept the manifest
391
+ (`an older chi rewrote its record after the install, so its manifest.yml may be an
392
+ earlier version's`). `--dry-run` says `would keep its manifest` and writes nothing.
393
+ An edited `manifest.yml` isn't kept over: its row says so (`chi bundle upgrade
394
+ NAME`). These rows are warnings and fail nothing; `chi bundle status NAME` says the
395
+ same on its `Manifest:` line.
370
396
  - **Profiles** (`core`, `dev`): an installed one installs the bundles a new
371
397
  chi added to it (`updated (+ name)`), even at the same version, and retries
372
398
  one that failed before; one you uninstalled stays out. See [Bundle
@@ -425,7 +451,7 @@ invalid option (exit 1).
425
451
  | `--dir DIR` | With `--remote`: a new session's folder on that machine, its absolute path (required for a new session there). Without `--remote` it exits 2: cd there instead. |
426
452
  | `--model NAME` | Use this model for the run (overrides the configured default and a resumed session's model). |
427
453
  | `--thinking LEVEL` | This session's own thinking level: `off`, `low`, `medium` or `high`, saved in the session and first in the order (`default` unsets it). On `--resume` or `--attach` it is set at once, through a `/thinking` on an attached worker. With `chi web` it is instead the default for the sessions its workers run, below a session's own (as env `SAMAGOTCHI_THINKING_LEVEL` is). See "Thinking" in configuration.md. |
428
- | `--toolkit NAME`, `--tool NAME`, `--no-tool NAME` | A new session's toolkit, the tools its prompts declare: `full` (every built-in), `lean`, `child`, one of config's `toolkit.kits` or a bundle's (without `--toolkit`: config's `toolkit.default`), with tools declared on top of it (`--tool`) or left out (`--no-tool`); both repeatable, a comma list too. Saved in the session as given; a respawned worker, a fork and a continue keep it. Ignored with `--attach`/`--resume` (one line, as `--memory`); `/toolkit` changes it. Not on `chi web` (config's `toolkit.default` covers its sessions). See [Toolkits](configuration.md#toolkits). |
454
+ | `--toolkit NAME`, `--tool NAME`, `--no-tool NAME` | A new session's toolkit, the tools its prompts declare: `full` (every built-in and plugin tool), `lean`, `child`, one of config's `toolkit.kits` or a bundle's (without `--toolkit`: config's `toolkit.default`), with tools declared on top of it (`--tool`) or left out (`--no-tool`); both repeatable, a comma list too. Saved in the session as given; a respawned worker, a fork and a continue keep it. Ignored with `--attach`/`--resume` (one line, as `--memory`); `/toolkit` changes it. Not on `chi web` (config's `toolkit.default` covers its sessions). See [Toolkits](configuration.md#toolkits). |
429
455
  | `--parent-answers LEVEL` | A new session's own `guardrails.parent_answers` (`hard`, `standard` or `soft`): what a parent agent may answer on its questions. It may only tighten config.yml's: the stricter of the two applies, and a looser one is saved as given with a warning (`--parent-answers soft is looser than config.yml's guardrails.parent_answers: standard; standard applies`). Saved in the session; a respawned worker, a fork and a continue keep it, a `delegate` child takes its parent's. Ignored with `--attach`/`--resume` (one line, as `--toolkit`); refused with `chi web`. Only you loosen it: `/parent-answers` in the terminal or the web. See [Guardrails](guardrails.md#answers-from-a-parent-agent). |
430
456
  | `--set KEY=VALUE` | A setting for a new session (repeatable; a later one of the same key wins). A core key a session may set (`recap.enabled`, `recap.sentences`) is typed by the key and goes first, over the CLI, env and config.yml: `--set recap.enabled=false` turns the idle recap off for this session; a value it doesn't take (`recap.enabled=maybe`) or a key no session sets (`guardrails.mode`) is an error, exit 2 (see [Per-session settings](configuration.md#per-session-settings)). Any other name is a bundle's `BUNDLE.KEY`, laid over config.yml's `bundles: <bundle>:` in what the bundle's plugin reads (`ctx.settings`): `--set cost-cap.cap=3`. The value is typed simply: an integer (`3`, `-1`) or a decimal (`2.5`, `.5`, `1e1`) is a number, `true`, `yes` and `on` are true, `false`, `no` and `off` are false (any case, as in config.yml), anything else stays the text as given (`010`, `0x10`, `3:30`, `y`, `null`), and so does every value of a key the manifest types `string`. For cost-cap, `0`, a negative number or `off` is no cap. Only a key the installed bundle's manifest lists in `session_settings:` is taken, with a value of the type it gives the key: a bundle that isn't installed (or whose plugin doesn't load), a key it doesn't list (a record installed before the bundle listed it says to run `chi update` or `chi bundle upgrade NAME`), a value of another type (`--set cost-cap.cap=five`), or another shape (`--set foo`, `--set a.b.c=1`) is an error naming what it takes, exit 2, nothing started. Saved in the session; a respawned worker, a fork and a continue keep it, a delegate child doesn't. Ignored with `--attach`/`--resume` (one line, as `--toolkit`); refused with `chi web`. See [Per-session settings](plugins.md#per-session-settings). |
431
457
  | `--profile NAME` | Prompt profile (`qwen36` or `gemma4`) for every model in this run, over config and the server's template (same as `--model-profile`, env `SAMAGOTCHI_MODEL_PROFILE`). See "Prompt profile" in configuration.md. |
@@ -862,7 +888,7 @@ line, the web in the session bar's ⚙ panel (`⚙ reasoning all` once the sessi
862
888
 
863
889
  `/toolkit` lists the tools the next turn declares (each with its estimated
864
890
  tokens), the session's toolkit and the tools it leaves out: `reachable
865
- (find_tools): …` (built-ins the model reaches with find_tools and call_tool)
891
+ (find_tools): …` (built-ins and plugin tools the model reaches with find_tools and call_tool)
866
892
  and `removed: …` (only `/toolkit add` brings one back), or `not loaded: …` when
867
893
  the toolkit removes call_tool, and its kit's prompt text when it has one (`prompt: <first line> (+N lines, ~T
868
894
  tokens)`). `/toolkit remove TOOL` is hard: a tool the
@@ -907,7 +933,6 @@ the model's `objectives` tool); [Objectives](sessions.md#objectives) has what th
907
933
  - `/objectives done O1 [O2…]`, `/objectives drop O1 [O2…]`: mark items. An id it doesn't have refuses the whole line, and
908
934
  so does `done` on a dropped item. The model is shown a done item as `O1 [done] …` and no dropped one.
909
935
  - `/objectives undo O1 [O2…]`: back to active, after a mistaken `done` or `drop`.
910
-
911
936
  - `/objectives check O1`: an independent read of O1's latest move, one side request; the verdict comes as a card and as
912
937
  `check: …` under the move. Information only: it edits nothing, and it is refused for an objective with no move or a dropped one, and during a participant's turn. See [Objectives](sessions.md#objectives).
913
938
 
@@ -1501,17 +1501,18 @@ toolkit:
1501
1501
 
1502
1502
  | toolkit | declares |
1503
1503
  |---|---|
1504
- | `full` | every built-in; the default |
1504
+ | `full` | every built-in and every plugin tool (a bundle's `chi.tool`, such as `web_search`); the default |
1505
1505
  | `lean` | `execute`, `read`, `write`, `edit`, `memory_read`, `memory_write`, `task_create`, `task_wait`, `task_get`, `task_list`, `task_stop`, `ask_user_question`, `web_fetch`, `context_read` |
1506
1506
  | `child` | `lean` plus `send_note`, without `memory_write`, `delegate`, `delegate_result` |
1507
1507
  | `coordinator` | `lean` plus `delegate`, `delegate_result`, `list_sessions`, `objectives` (declared by the coordinator bundle) |
1508
1508
  | `coding` | `child`, with the prompt text `- Be concise in your responses` / `- Show file paths clearly when working with files` (declared by the coordinator bundle, for build and fix children) |
1509
1509
  | `review` | `read`, `memory_read` (not `forget_outputs`, even under the `forget` layer): what a [participant's](internals/participants.md#turns) turn may call, whatever the session declares (the review boundary, [guardrails](guardrails.md#a-participants-review-boundary)). Reserved: `toolkit.kits.review` is refused ("review is reserved for participants' turns"), and a bundle can't declare it. A session may run on it too, as any kit |
1510
1510
 
1511
- - **Plugin tools** are declared in every toolkit unless its `remove:` names them, and so is `forget_outputs` under
1512
- the `forget` layer. A `kits:` entry takes `base:` (default `full`), `add:`, `remove:`, `prompt:`, `groups:` and
1513
- `description:`
1514
- (any other key, a typo or a `tools:` list, is ignored with a warning); it can't reuse a built-in's name
1511
+ - **Plugin tools** (a bundle's `chi.tool`, such as web-search's `web_search`) follow a kit's list as built-ins do:
1512
+ `full` (and every kit on `base: full`, the `kits:` default) declares them, another kit leaves them **reachable**
1513
+ (below) unless its `remove:` names them, and `add:` declares one (`search: { base: lean, add: [web_search] }`).
1514
+ `forget_outputs` is declared in every toolkit under the `forget` layer, unless removed. A `kits:` entry takes
1515
+ `base:` (default `full`), `add:`, `remove:`, `prompt:`, `groups:` and `description:` (any other key, a typo or a `tools:` list, is ignored with a warning); it can't reuse a built-in's name
1515
1516
  (`review`'s least of all: it is the participants' boundary). A bundle declares its own with `chi.toolkit`
1516
1517
  ([plugins](plugins.md#chitoolkitname-base-add--remove--prompt-nil-groups-nil-description-nil)).
1517
1518
  `description:` is optional classifier input, up to 300 characters; it is not included in the session prompt.
@@ -1565,7 +1566,7 @@ toolkit:
1565
1566
  reaches none. A kit whose base doesn't resolve (an unknown name, a cycle) falls back to `full`, which reaches
1566
1567
  every group.
1567
1568
  A selector that matches no group in that load-time snapshot warns once. `find_tools` and `call_tool` are
1568
- declared only when the toolkit reaches a source group or has a reachable built-in. Calls naming an unreached
1569
+ declared only when the toolkit reaches a source group or has a reachable tool (a built-in or a plugin tool). Calls naming an unreached
1569
1570
  group are refused before guardrails and do not start its server. The first-run init wait is unchanged: it can
1570
1571
  still block the first turn even when its server's group is unreached; `start: eager` and stale-cache refreshes
1571
1572
  also still start. `/mcp` marks servers outside the current toolkit.
@@ -1599,17 +1600,21 @@ toolkit:
1599
1600
  them with `/toolkit`. The configured default is always listed. A pick closes the list and shows a `tools: lean`
1600
1601
  chip on the row (a dot on 🧰 on a phone); it is for that create only, never remembered. A review session is
1601
1602
  always `chosen`: with review files set the list says it starts on the default unless you pick a kit.
1602
- - **Left out: reachable or removed.** A built-in missing from a kit's base list (`lean` has no `delegate`; a
1603
- `kits:` entry on `base: lean` or `child` inherits that) is left out softly: it is **reachable**. `find_tools` searches it as group `chi` (`chi/delegate`, with its inputSchema) beside
1604
- the MCP servers' tools, and `call_tool(tool: "chi/delegate", args: {…})` runs it as `delegate` itself, through the
1605
- usual guardrails, approvals and hooks ([plugins](plugins.md#chitool_sourcecall-acts_as-label-wait-prefix)). A
1603
+ - **Left out: reachable or removed.** A built-in or plugin tool missing from a kit's base list (`lean` has no
1604
+ `delegate` and no `web_search`; a `kits:` entry on `base: lean` or `child` inherits that) is left out softly: it
1605
+ is **reachable**. `find_tools` searches it as group `chi` (`chi/delegate`, `chi/web_search`, with its
1606
+ inputSchema) beside the MCP servers' tools, and `call_tool(tool: "chi/delegate", args: {…})` runs it as `delegate`
1607
+ itself, through the usual guardrails, approvals and hooks ([plugins](plugins.md#chitool_sourcecall-acts_as-label-wait-prefix)). A
1606
1608
  tool in a toolkit's `remove:`, a `--no-tool` or a `/toolkit remove` is **removed**: hard, also after an add, and
1607
1609
  call_tool can't reach it (`child` removes `memory_write`, `delegate` and `delegate_result`, so a child on it
1608
- starts no children). To keep a built-in away from the model, name it under `remove:`. `find_tools` and `call_tool`
1609
- are declared when a toolkit has a reachable built-in or reaches a source group; `full` with no reachable source
1610
- declares neither, and removing them there (`--no-tool call_tool`) leaves nothing out: no prompt line, no chip.
1610
+ starts no children). To keep a tool away from the model, name it under `remove:`. `find_tools` and `call_tool`
1611
+ are declared when a toolkit has a reachable tool or reaches a source group, as the tools stood when the
1612
+ session's worker started; `full` with no reachable source declares neither, and removing them there
1613
+ (`--no-tool call_tool`) leaves nothing out: no prompt line, no chip. A plugin tool that arrives later
1614
+ (`chi.replace_tools`) on a kit that declared neither stays out of reach for that worker, with one warn notice
1615
+ (`/toolkit add NAME` declares it).
1611
1616
  - **On `api: anthropic`, reachable tools are callable by name.** Every tool `call_tool` reaches (the reachable
1612
- built-ins, tools from reached MCP groups as `mcp_<server>_<tool>`) also goes in the request's tools deferred
1617
+ built-ins and plugin tools, tools from reached MCP groups as `mcp_<server>_<tool>`) also goes in the request's tools deferred
1613
1618
  (`defer_loading`: Messages keeps them out of the model's view until something loads them, at a flat ~100 tokens).
1614
1619
  A direct call of one (`list_sessions`, `mcp_fs_read`) runs, under the same guardrails, approvals and rows as
1615
1620
  through `call_tool` (an MCP tool's call is `call_tool`'s). A `find_tools` hit **loads** its matches (up to 5):
data/docs/guardrails.md CHANGED
@@ -841,11 +841,13 @@ trade-off, when such a clash exists:
841
841
  - deny rules cover both, which is the point.
842
842
 
843
843
  A `call_tool` of one of chi's own tools (`chi/delegate`: a built-in the
844
- session's toolkit leaves out, [toolkits](configuration.md#toolkits)) is
845
- gated **as that tool**: it is turned into the `delegate` call before the
846
- guardrails run, so every rule on `delegate` (or `execute`'s `command:`, the
847
- `rm` check, a delegate child's boundary) gives the same verdict as for a
848
- direct call, and an approval is keyed `delegate:…` either way. `tool:
844
+ session's toolkit leaves out, [toolkits](configuration.md#toolkits)), or of
845
+ a bundle's `chi.tool` it leaves out (`chi/web_search`), is gated **as that
846
+ tool**: it is turned into the `delegate` call before the guardrails run, so
847
+ every rule on `delegate` (or `execute`'s `command:`, the `rm` check, a
848
+ delegate child's boundary, a plugin tool's `targets:`) gives the same
849
+ verdict as for a direct call, and an approval is keyed `delegate:…` either
850
+ way. `tool:
849
851
  call_tool` rules no longer see such calls, only MCP calls and misses. A
850
852
  `chi/` name the toolkit removes is refused before the gate, with no card.
851
853
 
@@ -1157,6 +1159,10 @@ the whole call:
1157
1159
 
1158
1160
  `decision` is what the judgment led to: `audit` (audit mode), `asked` (on
1159
1161
  mode, the user was asked), `auto` (on mode, System 1 answered for the user).
1162
+ When two rules ask about one call the line's `rule` is the one the card names
1163
+ and `other_rules` lists the rest (`Verdict#other_ask_rules`), so a second
1164
+ rule's record (`SystemOne::Record`) counts the ask too; a line with one rule
1165
+ has no `other_rules` at all.
1160
1166
  A judgment that never arrived is logged too, with `p` null, so the failures
1161
1167
  count as well. The label line, keyed by the judgment's id, is what the user
1162
1168
  did: `allow_once`, `allow_wide` (any scope wider than once), `deny`, or
data/docs/hooks.md CHANGED
@@ -644,8 +644,9 @@ A `write`'s `content:` is the file text; an `edit` carries `old_text:` and
644
644
  arguments whole on `args:` (string keys) instead, and its `content:` is those
645
645
  arguments as JSON. An MCP tool's call is chi's `call_tool` (its tool index's, which the mcp
646
646
  bundle runs): `tool: "call_tool"`, with `args: {"tool" => "<server>/<tool>", "args" => {…}}`.
647
- A built-in reached through `call_tool` (`chi/delegate`, a tool the session's toolkit doesn't
648
- declare) fires as that built-in, its call built as a direct call's, with `call[:via] == "call_tool"`.
647
+ A built-in or plugin tool reached through `call_tool` (`chi/delegate`, `chi/web_search`: a tool the
648
+ session's toolkit doesn't declare) fires as that tool, its call built as a direct call's, with
649
+ `call[:via] == "call_tool"`.
649
650
  A hook that drops `via:` or rewrites the call to a tool the toolkit removes gets the call refused
650
651
  by its name.
651
652
  On a Messages host (`api: anthropic`), the model may call those tools by their own names (they
@@ -710,7 +711,7 @@ Notes:
710
711
  - Ordering: bundle hooks fire by `(priority, bundle_name, hook_name)` (lower priority first), then plain `config.yml` hooks in registration order.
711
712
  - Settings: a hook class with `initialize(settings = {})` gets the bundle's section of `config.yml` `bundles:` (see [Settings](#settings)).
712
713
  - A bundle can also ship a `plugin.rb` whose `chi.on(event)` blocks are bundle hooks too, next to commands and tools; see [Plugins](plugins.md). There `ctx.notify`, `ctx.steer` and the other `ctx` helpers act as this page's `event[:notify]`… do for the event the block runs for.
713
- - Shipped bundles (each in a profile: `core` or `dev`, see [Bundle profiles](memory.md#bundle-profiles-core-and-dev)): `chi bundle install guardrails` (rules, see [Guardrails](guardrails.md#the-guardrails-bundle)) and `chi bundle install known-names` (a hook, see [Guardrails](guardrails.md#the-known-names-bundle)), `chi bundle install source-links` (a hook: announces source refs, see [The source-links bundle](#the-source-links-bundle)), `chi bundle install btw` (a plugin: `/btw`, see [Bundles](bundles.md#the-btw-bundle)), `chi bundle install mcp` (a plugin: tools from MCP servers, see [Bundles](bundles.md#the-mcp-bundle)) `chi bundle install loop-guard` (a plugin: breaks tool-call loops, see [Bundles](bundles.md#the-loop-guard-bundle)), `chi bundle install check-in` (a plugin: checks on a long turn, see [Bundles](bundles.md#the-check-in-bundle)), `chi bundle install slow-commands` (a plugin: commands that ran past the execute limit are run as tasks next time, see [Bundles](bundles.md#the-slow-commands-bundle)), `chi bundle install agents-md` (a plugin: a nested AGENTS.md is steered into the conversation on the first touch under its directory, see [Bundles](bundles.md#the-agents-md-bundle)), `chi bundle install cost-cap` (a plugin: a per-session $ cap, `/cap`, see [Bundles](bundles.md#the-cost-cap-bundle)), `chi bundle install work-cap` (a plugin: a per-session cap on tool calls and active minutes, `/workcap`, see [Bundles](bundles.md#the-work-cap-bundle)), `chi bundle install ruby` (a plugin: an RSpec run with no examples at all, a hint, see [Bundles](bundles.md#the-ruby-bundle)) `chi bundle install skills` (a plugin: `/skill`, versions of skills, see [Bundles](bundles.md#the-skills-bundle)), `chi bundle install github-pr` (attached context: the branch's open PR, see [Bundles](bundles.md#the-github-pr-bundle)), `chi bundle install coordinator` (a plugin: parallel work in worktrees and child sessions, see [Bundles](bundles.md#the-coordinator-bundle)), `chi bundle install claude-peer` / `codex-peer` (a plugin: talk to a Claude Code or Codex session, see [Bundles](bundles.md#the-claude-peer-bundle)) and `chi bundle install model-notes` (memories: reviewed model notes, see [Bundles](bundles.md#the-model-notes-bundle)).
714
+ - Shipped bundles (each but web-search in a profile: `core` or `dev`, see [Bundle profiles](memory.md#bundle-profiles-core-and-dev)): `chi bundle install guardrails` (rules, see [Guardrails](guardrails.md#the-guardrails-bundle)) and `chi bundle install known-names` (a hook, see [Guardrails](guardrails.md#the-known-names-bundle)), `chi bundle install source-links` (a hook: announces source refs, see [The source-links bundle](#the-source-links-bundle)), `chi bundle install btw` (a plugin: `/btw`, see [Bundles](bundles.md#the-btw-bundle)), `chi bundle install mcp` (a plugin: tools from MCP servers, see [Bundles](bundles.md#the-mcp-bundle)) `chi bundle install loop-guard` (a plugin: breaks tool-call loops, see [Bundles](bundles.md#the-loop-guard-bundle)), `chi bundle install check-in` (a plugin: checks on a long turn, see [Bundles](bundles.md#the-check-in-bundle)), `chi bundle install slow-commands` (a plugin: commands that ran past the execute limit are run as tasks next time, see [Bundles](bundles.md#the-slow-commands-bundle)), `chi bundle install agents-md` (a plugin: a nested AGENTS.md is steered into the conversation on the first touch under its directory, see [Bundles](bundles.md#the-agents-md-bundle)), `chi bundle install cost-cap` (a plugin: a per-session $ cap, `/cap`, see [Bundles](bundles.md#the-cost-cap-bundle)), `chi bundle install work-cap` (a plugin: a per-session cap on tool calls and active minutes, `/workcap`, see [Bundles](bundles.md#the-work-cap-bundle)), `chi bundle install ruby` (a plugin: an RSpec run with no examples at all, a hint, see [Bundles](bundles.md#the-ruby-bundle)) `chi bundle install skills` (a plugin: `/skill`, versions of skills, see [Bundles](bundles.md#the-skills-bundle)), `chi bundle install github-pr` (attached context: the branch's open PR, see [Bundles](bundles.md#the-github-pr-bundle)), `chi bundle install coordinator` (a plugin: parallel work in worktrees and child sessions, see [Bundles](bundles.md#the-coordinator-bundle)), `chi bundle install claude-peer` / `codex-peer` (a plugin: talk to a Claude Code or Codex session, see [Bundles](bundles.md#the-claude-peer-bundle)), `chi bundle install model-notes` (memories: reviewed model notes, see [Bundles](bundles.md#the-model-notes-bundle)) and `chi bundle install web-search` (a plugin: a `web_search` tool through your SearXNG, in no profile, see [Bundles](bundles.md#the-web-search-bundle)).
714
715
  - Installing a bundle executes its hook code at `Engine` startup. Only install bundles you trust, as you would a gem. Hooks are **not** executed at install time (copy-only); they are `module_eval`'d at `Engine.new` inside per-bundle `Samagotchi::Bundles::<name>` namespaces (no top-level `require` collisions). Keep hook files side-effect-free at load time; do work in `#call` — top-level side effects (require, IO, `at_exit`, global assignment) run once per `Engine.new` (class redefinition is idempotent).
715
716
 
716
717
  Lifecycle:
@@ -225,7 +225,7 @@ host refused it for that model.
225
225
  `ChatLoop#tool_definitions(conversation, model:)` appends them (`Tools::DeferredTools.list`) only on such an
226
226
  adapter, and only with at least one tool declared (Messages wants one). The list, per request:
227
227
 
228
- 1. the built-ins the toolkit reaches (`Registry#reachable_names`), in registry order;
228
+ 1. the built-ins and plugin tools the toolkit reaches (`Registry#reachable_names`), in registry order;
229
229
  2. the tool index's source tools while `call_tool` is declared (`ToolIndex#deferrable`, via
230
230
  `Registry::Index#deferrable`): callable, the only tool with its acts_as, a name Messages takes, no registered
231
231
  tool's name; by acts_as (`mcp_<server>_<tool>`), sorted by name;
@@ -270,11 +270,11 @@ sets `:tool_changes` for the model: the retry and every later request drop the s
270
270
 
271
271
  A direct call of a deferred tool runs whether something loaded it or not (the API doesn't check): the Engine sets
272
272
  `TurnSettings#loads_tools` from the turn's final model (after the before_turn hooks), and then `ToolRunner#run` and
273
- `KernelLoop#dispatch` take a reachable built-in by its own name (`Registry#indexed`) through the normal gate, and wrap
273
+ `KernelLoop#dispatch` take a reachable built-in or plugin tool by its own name (`Registry#indexed`) through the normal gate, and wrap
274
274
  an MCP tool's acts_as into the `call_tool` call it stands for (`ToolIndex#wrap`): `call_tool`'s row, rules and
275
275
  approvals. A reload rebuilds that wrapped call from the saved one (`BuiltinCalls.wrap_saved`). Each such call is
276
276
  logged (`tool_direct_deferred`, with `loaded:` whether the conversation had loaded it). On `api: openai` and the
277
- native loop nothing is deferred, and a direct call of a reachable built-in is refused with the `call_tool` route as
277
+ native loop nothing is deferred, and a direct call of a reachable built-in or plugin tool is refused with the `call_tool` route as
278
278
  before.
279
279
 
280
280
  ## 400 fallbacks
@@ -35,6 +35,9 @@ Three rules hold the shape:
35
35
  conversation already holds. Ids are deterministic: `ctx-<name>-<serial>-<revision[0,12]>` (the serial makes A, B, A
36
36
  three notes), `ctx-<name>-error-<error_since digits>`, `ctx-<name>-detached-<seen[0,12]>`.
37
37
  - **Polling is not activity.** The poller and the absorb never hold a worker up (`WorkerIdleExit`).
38
+ - **The leave absorbs too.** A pass looks at the context and then checks whether it should leave; a change that
39
+ lands in that gap has no later pass (the poller stops with the worker), so `Worker#left` absorbs whatever is
40
+ pending as plain notes before the recap, as it does for a background task that finished in the same gap.
38
41
 
39
42
  ## ContextSources
40
43
 
@@ -10,7 +10,9 @@ model. The TUI is untouched: it shows a fence as source.
10
10
  in document order and zips them with the fragment's `<pre>`s, setting `data-lang` after sanitize's walk. Only the first
11
11
  word, downcased, matching `BlockRenderers::LANG`. A count that differs sets nothing. **`web.markdown` off means no
12
12
  `<pre data-lang>`, so nothing draws.**
13
- 2. `BlockRenderers.for_installed` (manifest `block_renderers:`, read from provenance, so chi web needs no plugins) lists
13
+ 2. `BlockRenderers.for_installed` (manifest `block_renderers:`, read through `InstalledBundle#manifest`: the bundle's
14
+ kept `manifest.yml`, else its record's mirror, so chi web needs no plugins, and a bundle installed by a chi that
15
+ didn't know the key draws once a kept manifest is there) lists
14
16
  who draws what: `GET /api/block_renderers`, `:owner`. `GET /api/block_renderers/:bundle/:file` serves a file, `:owner`:
15
17
  the URL's names go through `BlockRenderers.script_path` (membership in a listed renderer's `load` before any path is
16
18
  made), then `Provenance.sha_matches?`; a changed file is 409 `renderer_changed`. ETag + `no-cache`, as static files.