samagotchi 0.74.0 → 0.76.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +68 -1
- data/docs/architecture.md +11 -4
- data/docs/cli.md +13 -1
- data/docs/configuration.md +4 -3
- data/docs/guardrails.md +9 -0
- data/docs/internals/anthropic.md +3 -1
- data/docs/internals/bundles.md +21 -7
- data/docs/internals/context-telemetry.md +22 -0
- data/docs/internals/engine.md +14 -3
- data/docs/internals/prompt-caching.md +2 -1
- data/docs/memory.md +48 -1
- data/docs/plugins.md +2 -1
- data/docs/releasing.md +6 -2
- data/docs/sessions.md +7 -5
- data/lib/samagotchi/bundle_command.rb +52 -39
- data/lib/samagotchi/bundles/codex-peer/manifest.yml +2 -2
- data/lib/samagotchi/bundles/codex-peer/plugin.rb +19 -2
- data/lib/samagotchi/bundles/core/manifest.yml +2 -2
- data/lib/samagotchi/bundles/dev/manifest.yml +2 -2
- data/lib/samagotchi/bundles/system/manifest.yml +1 -1
- data/lib/samagotchi/desktop/macos.rb +1 -2
- data/lib/samagotchi/effective_model.rb +5 -0
- data/lib/samagotchi/engine.rb +84 -4
- data/lib/samagotchi/kernel_loop.rb +13 -10
- data/lib/samagotchi/llm_context_override.rb +8 -6
- data/lib/samagotchi/memory_bundle/asset_installer.rb +34 -5
- data/lib/samagotchi/memory_bundle/builder.rb +11 -14
- data/lib/samagotchi/memory_bundle/installer.rb +140 -9
- data/lib/samagotchi/memory_bundle/manifest.rb +20 -1
- data/lib/samagotchi/memory_bundle/memory_files.rb +65 -0
- data/lib/samagotchi/memory_bundle/profile.rb +1 -1
- data/lib/samagotchi/memory_bundle/shipped_update.rb +2 -1
- data/lib/samagotchi/memory_bundle/source.rb +66 -3
- data/lib/samagotchi/memory_bundle/system_bundle.rb +4 -2
- data/lib/samagotchi/model_price.rb +4 -3
- data/lib/samagotchi/paths.rb +15 -1
- data/lib/samagotchi/session.rb +1 -1
- data/lib/samagotchi/session_chain.rb +2 -8
- data/lib/samagotchi/session_commands.rb +2 -0
- data/lib/samagotchi/session_manager.rb +1 -2
- data/lib/samagotchi/session_metrics/model_segment.rb +109 -0
- data/lib/samagotchi/session_metrics/totals.rb +127 -0
- data/lib/samagotchi/session_metrics.rb +118 -83
- data/lib/samagotchi/terminal_ui/formatting.rb +127 -10
- data/lib/samagotchi/terminal_ui/status_row.rb +3 -1
- data/lib/samagotchi/toolkit.rb +9 -8
- data/lib/samagotchi/toolkit_command.rb +2 -2
- data/lib/samagotchi/tools/builtins.rb +1 -5
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +3 -0
- data/lib/samagotchi/web/public/app.js +4 -3
- data/lib/samagotchi/web/public/chains.js +42 -21
- data/lib/samagotchi/web/public/composer.js +9 -5
- data/lib/samagotchi/web/public/ctx.js +73 -11
- data/lib/samagotchi/web/public/index.html +32 -35
- data/lib/samagotchi/web/public/info_bar.js +4 -4
- data/lib/samagotchi/web/public/info_bar_model.js +18 -8
- data/lib/samagotchi/web/public/lists_view.js +2 -2
- data/lib/samagotchi/web/public/question_cards.js +1 -1
- data/lib/samagotchi/web/public/session_state.js +7 -0
- data/lib/samagotchi/web/public/stage_slots.js +3 -3
- data/lib/samagotchi/web/public/stream_handlers.js +32 -1
- data/lib/samagotchi/web/public/timing.js +11 -2
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c2bec0eb4ac3d46dcca5ca46f2496c4c419d1373ea595fcceae35d5a96098899
|
|
4
|
+
data.tar.gz: b9be22b2481f7489457eceb98eb1ace0cc3b58988ab34790d0c40197dc586ccc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f8d1c6f29de97f66b9ece157418f02628939f98c9a94926bbde5536fd2e1d126ca42cfce51497290f32e225d0ac169b81eda6e4aa1836c1d4d7bdd94d1d4c929
|
|
7
|
+
data.tar.gz: 9811070c6def3fe473d2e5fba2a044c25e1d843c12097b738a14854e1a9c6064e84f1b77ae56960166fceac101eb06aca6cbcb03c148e3017c034594cdb246a0
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,71 @@ and commands may change between minor versions. How releases are made:
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.76.0] - 2026-10-17
|
|
12
|
+
|
|
13
|
+
### Security
|
|
14
|
+
|
|
15
|
+
- A bundle can no longer replace an installed one by taking its name. `chi bundle install` over a bundle from another
|
|
16
|
+
source is refused (`--force` too): `chi bundle upgrade <new source>` switches the source and says
|
|
17
|
+
`Source changes: <old> → <new>`. A name chi ships (`guardrails`, `samagotchi-system`, …, in any letter case)
|
|
18
|
+
installs only from chi's own copy unless `--force`, and a name that differs from an installed one only in case is
|
|
19
|
+
refused.
|
|
20
|
+
- A bundle's symlinked memory is no longer installed: a tar, zip or git repo whose `notes.md` linked to another file
|
|
21
|
+
(`~/.ssh/…`, an API key) copied that file into the memories the model reads. It is skipped with
|
|
22
|
+
`notes.md is a symlink (not installed)`; a linked hook or guardrail rule is skipped too, and a linked plugin or
|
|
23
|
+
script fails the install.
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
|
|
27
|
+
- `/stats`, `chi sessions stats` and the web's ctx tooltip split tokens, cost and speed per model after a `/model`
|
|
28
|
+
switch: a row per run of turns on one model, each switch's prefill under it, and a total.
|
|
29
|
+
- Docs: [chi-nah](https://github.com/dm1try/chi-nah), an external guard bundle, in docs/guardrails.md.
|
|
30
|
+
|
|
31
|
+
### Changed
|
|
32
|
+
|
|
33
|
+
- A bundle installed from a git URL is named by its manifest's `name:` (it took the repo's name), so a URL install and
|
|
34
|
+
a local copy are one bundle. One installed by URL before this under a different repo name: `chi bundle uninstall
|
|
35
|
+
<repo name>`, then install it again (chi says so).
|
|
36
|
+
- The `core` and `dev` profiles require the chi their members need (0.60.0 and 0.72.0).
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- After `/model` the ctx % no longer shows the old model's window: it uses the new model's window at once, or shows
|
|
41
|
+
`ctx ?` (tooltip `Context window unknown until the next request`) until the window is known. The same for a session
|
|
42
|
+
resumed or woken on another model.
|
|
43
|
+
- `/stats` no longer counts a `/model` to the same model on another host as a re-prefill.
|
|
44
|
+
- Web: switching sessions while a command or a restored prompt re-reads no longer draws it in the session just
|
|
45
|
+
opened; a command's answer no longer clears another session's draft; such failures are a toast, not an alert.
|
|
46
|
+
- `chi bundle install|upgrade`: an invalid or missing `manifest.yml` fails with one clear line (no backtrace, no
|
|
47
|
+
"falling back to install"); a manifest `name:` that is a path is refused; a plain re-install keeps the record of
|
|
48
|
+
files the bundle stopped shipping, so the upgrade it suggests trashes them; `chi bundle upgrade` with no source
|
|
49
|
+
prints the full usage line.
|
|
50
|
+
- codex-peer: a send no longer hangs when `codex queue` exits but leaves a process holding its output; it stops
|
|
51
|
+
after 10 s and says the message may already be queued.
|
|
52
|
+
|
|
53
|
+
Update with `chi update` (the system bundle 0.76.0, `core` 0.3.1, `dev` 0.5.1, `codex-peer` 0.3.2), then restart
|
|
54
|
+
`chi web`.
|
|
55
|
+
|
|
56
|
+
## [0.75.0] - 2026-10-17
|
|
57
|
+
|
|
58
|
+
### Changed
|
|
59
|
+
|
|
60
|
+
- Web: a session chain's links are numbered `#N` instead of `day N` (the chip `↩ #3`, `#2 of 3`; its popover's rows),
|
|
61
|
+
since a chain may be continued several times a day. The info bar's ← / → show the neighbour's time (`← 09:12`) when
|
|
62
|
+
it started on the open link's day, else its date, with its full start in the tooltip; a popover row adds its time
|
|
63
|
+
when another link of the chain started the same day. The carry note names the previous link's start with its time
|
|
64
|
+
(`This session continues 3fa2c1d0 (2026-10-17 09:12), …`).
|
|
65
|
+
|
|
66
|
+
### Fixed
|
|
67
|
+
|
|
68
|
+
- `chi bundle install` / `upgrade` no longer make a bundle repo's README, CHANGELOG, LICENSE, CONTRIBUTING,
|
|
69
|
+
CODE_OF_CONDUCT or SECURITY file a memory, and a manifest whose `files:` lists memories installs only those (an
|
|
70
|
+
unshipped name warns). An upgrade moves such a file an older install made a memory to the trash. Older chi still
|
|
71
|
+
installs every top-level `*.md` (`requires_chi` doesn't gate memories): a bundle for it too names its readme
|
|
72
|
+
`README.markdown`. `rake bundles:check` fails a shipped bundle's `*.md` that its `files:` doesn't list.
|
|
73
|
+
|
|
74
|
+
Update with `chi update` (the system bundle 0.75.0), then restart `chi web`.
|
|
75
|
+
|
|
11
76
|
## [0.74.0] - 2026-10-17
|
|
12
77
|
|
|
13
78
|
### Changed
|
|
@@ -3156,7 +3221,9 @@ and long-lived sessions.
|
|
|
3156
3221
|
- A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
|
|
3157
3222
|
hotkey panel that send selected text or the clipboard to your sessions.
|
|
3158
3223
|
|
|
3159
|
-
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.
|
|
3224
|
+
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.76.0...HEAD
|
|
3225
|
+
[0.76.0]: https://github.com/dm1try/samagotchi/compare/v0.75.0...v0.76.0
|
|
3226
|
+
[0.75.0]: https://github.com/dm1try/samagotchi/compare/v0.74.0...v0.75.0
|
|
3160
3227
|
[0.74.0]: https://github.com/dm1try/samagotchi/compare/v0.73.0...v0.74.0
|
|
3161
3228
|
[0.73.0]: https://github.com/dm1try/samagotchi/compare/v0.72.0...v0.73.0
|
|
3162
3229
|
[0.72.0]: https://github.com/dm1try/samagotchi/compare/v0.71.0...v0.72.0
|
data/docs/architecture.md
CHANGED
|
@@ -327,10 +327,16 @@ which `SessionMetrics#persist` rewrites after each turn; the web server merges t
|
|
|
327
327
|
The totals' `tokens` block adds the cached, cache-write and reasoning sums, the reported cost (`cost_sum`) and the
|
|
328
328
|
estimated one (`cost_estimate_sum`, from `hosts.<name>.models` prices; 0 for none), the decode time and tokens
|
|
329
329
|
behind the average speed (`avg_decode_tps`) and the last speeds (`last_decode_tps`,
|
|
330
|
-
`last_prefill_tps`, `tps_source`: `server` or `estimate`).
|
|
330
|
+
`last_prefill_tps`, `tps_source`: `server` or `estimate`). `model_segments` (always present) splits those sums
|
|
331
|
+
by model after a `/model` switch: one row per run of consecutive turns on one model (`SessionMetrics::ModelSegment`,
|
|
332
|
+
keyed on the turn records' `model_ref`), in order, with its turns, token and cost sums, speeds and the switch's prefill
|
|
333
|
+
(`prefill_tokens`, `prefill_cost_estimate`: the sums of its turn records' `switch_prefill_tokens` and
|
|
334
|
+
`switch_prefill_cost_estimate`, [sessions.md](sessions.md)); `[]` with one model, the oldest rows folded into one (`folded_segments`)
|
|
335
|
+
past 6; a running turn counts in its model's row (`ended_at` null). The rows add up to `tokens`. They are derived from
|
|
336
|
+
the records, never read back. Each `generation_completed` event
|
|
331
337
|
carries the generation's `speed` (`{decode_tps, source}`, null without one), its own prompt-cache
|
|
332
338
|
counts (`prompt_tokens`, `cached_tokens`, `cache_write_tokens`: null when the server reported no writes) and
|
|
333
|
-
those running `tokens`: the Engine closes the generation in `SessionMetrics#finish_generation` before relaying
|
|
339
|
+
those running `tokens` and `model_segments`: the Engine closes the generation in `SessionMetrics#finish_generation` before relaying
|
|
334
340
|
it, so the web's info bar updates per generation without parsing chunk payloads.
|
|
335
341
|
|
|
336
342
|
The page's frequent reads of `GET /api/sessions/:id` stay light too:
|
|
@@ -504,6 +510,7 @@ A map of the parts the overview above doesn't name (paths under `lib/samagotchi/
|
|
|
504
510
|
|---|---|---|
|
|
505
511
|
| Worker | `worker.rb` (`worker_idle_exit.rb`, `worker_inbound.rb`, `worker_signals.rb`) | A session's background process: builds the `Engine` and `Bridge`, runs queued turns, exits when idle. A spawned one traps TERM, HUP and INT (`WorkerSignals`, installed by `run_session_loop` when `SAMAGOTCHI_WORKER_PROCESS=1`): the running turn stops as a Stop with reason `signal`, is saved, and the worker leaves (`:signal`, exit 0; a new worker only for input already queued). A running turn is checkpointed at its iteration boundaries (`TurnCheckpoint`, the `interrupted_turn` mark), and the next worker settles a turn its predecessor died in (`TurnCheckpoint.settle`). The session is saved from the turn's thread, Bridge threads and a signal's thread; `Session#save` takes a per-session lock around its snapshot and write, so an older snapshot never lands over a newer one. |
|
|
506
512
|
| Config | `config.rb` | Every setting as an `Entry`: env, config.yml and CLI layers ([configuration.md](configuration.md)). |
|
|
513
|
+
| Paths | `paths.rb` (`Paths`) | The XDG base dirs chi's files live under, read from the env on every call: `Paths.state_home` (`$XDG_STATE_HOME`, else `~/.local/state`) and `Paths.config_home` (`$XDG_CONFIG_HOME`, else `~/.config`), both defaults under `Dir.home`; a blank variable counts as unset, a relative one is made absolute from the current directory. `Paths.tilde` shows a path with `~` for the home folder. `SessionManager` hands a spawned worker the spawner's `XDG_STATE_HOME` / `XDG_CONFIG_HOME`, when set, made absolute, so a relative one isn't read from the session's folder. |
|
|
507
514
|
| MemoryBundle | `memory_bundle.rb`, `memory_bundle/{installer,builder,installed_bundle,plugin_ref,bundle_hook}.rb` (`Installer`, `Builder`, `InstalledBundle`, `PluginRef`, `BundleHook`) | Installing, building and reading bundles of memories, hooks and plugins. |
|
|
508
515
|
| Plugin API | `plugin/api.rb` (`Plugin::Api`), `plugin/tool_spec.rb` (`Plugin::ToolSpec`: a checked tool declaration, how it is registered), `plugin/loader.rb` | What a bundle's plugin registers: tools, commands, annotation actions, hooks, services, toolkits, peer sources ([plugins.md](plugins.md)). |
|
|
509
516
|
| Peer bundles' base | `plugin/peer.rb` (`Plugin::Peer`) | What claude-peer and codex-peer share (the send tool, the command, the cards, payloads); each bundle's subclass sets its strings and transport ([plugins.md](plugins.md#the-claude-peer-bundle)). |
|
|
@@ -515,8 +522,8 @@ A map of the parts the overview above doesn't name (paths under `lib/samagotchi/
|
|
|
515
522
|
| Idle work | `idle_scheduler.rb` (`IdleScheduler`), `idle_recap.rb` (`IdleRecap`), `idle_reminders.rb` | One poller for an idle session's recap and reminders. |
|
|
516
523
|
| Attached context | `context_sources.rb` (`ContextSources`), `context_poller.rb`, `context_absorber.rb`, `peer_sources.rb` (`PeerSources`: the sources peer bundles declare) | External sources a session keeps fresh, and the peer loop's wakes ([context.md](context.md)). |
|
|
517
524
|
| LLM context strategies | `llm_context_strategy.rb`, `llm_context_view.rb`, `llm_context_stale.rb`, `llm_context_forget.rb` | What a request sends of the history (none, stale, forget) ([internals/llm-context-forget.md](internals/llm-context-forget.md)). |
|
|
518
|
-
| Session chains | `session_continue.rb` (`SessionContinue`), `session_chain.rb` (`SessionChain`) | A session that continues an earlier one. |
|
|
519
|
-
| Analytics | `analytics_file.rb` (`AnalyticsFile`) | A session's `analytics.json` (`SessionMetrics#persist`). |
|
|
525
|
+
| Session chains | `session_continue.rb` (`SessionContinue`), `session_chain.rb` (`SessionChain`) | A session that continues an earlier one. `SessionChain.opener_for` gives the text web `continue →` prefills in the new link: the previous link's own first typed message when that link continues one itself, else nil (the 201's `opener`; not saved) ([sessions.md](sessions.md)). |
|
|
526
|
+
| Analytics | `analytics_file.rb` (`AnalyticsFile`), `session_metrics/totals.rb` (`SessionMetrics::Totals`), `session_metrics/model_segment.rb` (`SessionMetrics::ModelSegment`) | A session's `analytics.json` (`SessionMetrics#persist`); the sums over its turn records (`Totals`: tokens, cost, speeds, kept as running counters) and their split per model after a `/model` switch (`ModelSegment`: a run of turns on one model, the `model_segments` rows). |
|
|
520
527
|
| Images / vision | `image_store.rb` (`ImageStore`), `vision_support.rb`, `vision_context.rb` | Stored prompt images and whether a model takes them. |
|
|
521
528
|
| Toolkits | `toolkit.rb` (`Toolkit`), `toolkit_binding.rb` (`ToolkitBinding`, the Engine's seam), `toolkit_command.rb` (`/toolkit`), `tools/registry.rb` (`Registry#toolkit`, `#left_out_names`), `web/public/toolkit_chip.js` | Which registered tools a session's prompts declare: saved in the session, resolved at a turn's start (`Engine#apply_toolkit!`), named in the prompt's not-loaded line, a kit's prompt text after the appended files; `chi.toolkit` and `ctx.use_toolkit` for plugins ([configuration.md](configuration.md#toolkits)). |
|
|
522
529
|
| Tool index | `tools/tool_index.rb` (`Tools::ToolIndex`) | `find_tools` and `call_tool`: the tools a session knows but doesn't declare one by one (an mcp bundle's servers through `chi.tool_source`, the built-ins its toolkit leaves out), searched and called by name ([plugins.md](plugins.md)). |
|
data/docs/cli.md
CHANGED
|
@@ -469,7 +469,17 @@ prompt (last, server)`); for other servers it is estimated from the first
|
|
|
469
469
|
streamed chunk to the end and marked `~` (`~64 tok/s out (last, estimate)`).
|
|
470
470
|
Generations under 8 tokens or 100 ms get no speed of their own, nor does a
|
|
471
471
|
reply without server token counts or a non-streamed one. A delegated child
|
|
472
|
-
keeps its own numbers: a parent's tokens and cost leave them out.
|
|
472
|
+
keeps its own numbers: a parent's tokens and cost leave them out.
|
|
473
|
+
|
|
474
|
+
After a `/model` switch (or a hook's) the tokens line becomes a `models:` block, a row per run of turns on one
|
|
475
|
+
model, in order (A → B → A is three rows): the model (`host:model` as asked), when (`10:43–now` while it runs; with
|
|
476
|
+
the day when the rows span several), turns, tokens in/out, its average speed and cost. Under the row of the model
|
|
477
|
+
switched to, `└ switch prefill 58.0k uncached prompt ≈$0.01` says what its first request prefilled on the new model
|
|
478
|
+
(the prompt less the cached reads; `≈` an estimate from the new model's configured price, left out without one). A
|
|
479
|
+
`total` row ends the block. The speed line names the model of its last speed, and the cost is from `each model's host
|
|
480
|
+
prices`. Past 6 rows the oldest fold into one `N earlier segments` row; a session's turns from before chi recorded
|
|
481
|
+
their model are one `earlier turns` row. One model: the output is unchanged. `chi sessions stats` prints the same,
|
|
482
|
+
also offline (from the saved records). `/recap` is in
|
|
473
483
|
[Session recap](#session-recap), `/model` and `/models` in [Runtime Model
|
|
474
484
|
Switch](#runtime-model-switch-assist-mode).
|
|
475
485
|
|
|
@@ -889,6 +899,8 @@ In interactive assist mode, you can switch the request model without restarting:
|
|
|
889
899
|
Notes:
|
|
890
900
|
|
|
891
901
|
- The switch updates the request `model` field, routes to the matching host (`HostRegistry#resolve`), and resolves the prompt profile again (config, the server's chat template, the name; see "Prompt profile" in configuration.md).
|
|
902
|
+
- The ctx % follows the switch at once: the last turn's count against the new model's window (the web's info bar, the attached TUI's status row, a client that joins later), recounted at the next generation. When the new window can't be resolved the web shows `ctx ?` and the attached TUI's status row drops its ctx until then ([context telemetry](internals/context-telemetry.md)).
|
|
903
|
+
- After a switch `/stats` (and the web's ctx tooltip) splits the session's tokens, cost and speed per model, with what each switch prefilled on the new model: the `models:` block ([Session commands](#session-commands)).
|
|
892
904
|
- Without `--default` the command is session-scoped and does not rewrite config files.
|
|
893
905
|
- With `--default` the new default is written to `~/.config/samagotchi/config.yml` (honoring `XDG_CONFIG_HOME`) and takes effect for all new sessions; the current session's effective model is also updated immediately. Bare aliases and `host:model` are both valid.
|
|
894
906
|
- `--default` and `--alias` change only their one key's line in `config.yml` (`default.model`, or the alias under `model_aliases:`): comments and the rest of the file stay as written, a symlinked file is written through and keeps its mode. When the edited text wouldn't read back as the expected settings (a flow-style section, anchors), chi writes the whole file out from the parsed data instead, which drops its comments.
|
data/docs/configuration.md
CHANGED
|
@@ -836,9 +836,10 @@ cache:
|
|
|
836
836
|
```
|
|
837
837
|
Each request's own counts are in the debug log's `generation_completed` line:
|
|
838
838
|
`prompt=` (its prompt tokens), `cached=` (read from the server's cache), `cache_write=` (written to it, when the
|
|
839
|
-
server reports writes) and `reprefill=` (of the previous request's prompt on the same model
|
|
840
|
-
prefilled again: an `llm_context` edit's cache break, or anything else that rewrote the prompt's
|
|
841
|
-
server reports a cached count, and not on the first request after a warm-up
|
|
839
|
+
server reports writes) and `reprefill=` (of the previous request's prompt on the same model through the same host,
|
|
840
|
+
what the server prefilled again: an `llm_context` edit's cache break, or anything else that rewrote the prompt's
|
|
841
|
+
middle; only when the server reports a cached count, and not on the first request after a warm-up; a `/model` to the
|
|
842
|
+
same model on another host is the switch's prefill, not a re-prefill); recap and side requests log theirs as `recap request_usage`.
|
|
842
843
|
|
|
843
844
|
## Llama Model Routing
|
|
844
845
|
|
data/docs/guardrails.md
CHANGED
|
@@ -568,6 +568,15 @@ next start.
|
|
|
568
568
|
The system prompt names the home directory once, with the advice to write
|
|
569
569
|
it as `~` or `$HOME`, so the model rarely has to spell it.
|
|
570
570
|
|
|
571
|
+
## An external guard: chi-nah
|
|
572
|
+
|
|
573
|
+
[chi-nah](https://github.com/dm1try/chi-nah) is a bundle kept in its own repo (`chi bundle install
|
|
574
|
+
https://github.com/dm1try/chi-nah`). Its `before_tool_call` hook asks
|
|
575
|
+
[nah](https://github.com/manuelschipper/nah)'s deterministic guards about every `execute`, `task_create`,
|
|
576
|
+
`read`, `write` and `edit`: a call nah blocks (secret exfiltration, `rm -rf ~`, `git reset --hard`,
|
|
577
|
+
`curl | sh`…) is denied with the guard's name and how the user pauses it (`nah nap <guard>`); everything
|
|
578
|
+
else goes to chi's own rules. It needs nah installed; see its README for the settings.
|
|
579
|
+
|
|
571
580
|
## Failing closed
|
|
572
581
|
|
|
573
582
|
- A config hook with `required: true`, or a bundle `before_tool_call` hook with
|
data/docs/internals/anthropic.md
CHANGED
|
@@ -38,7 +38,9 @@ Paths are under `lib/samagotchi/llm/`.
|
|
|
38
38
|
|
|
39
39
|
`AnthropicMessages.for(entry)` takes the host's API root, key variable, `auth:`, `dialect:` and remoteness;
|
|
40
40
|
`HostRegistry#adapter_for` keeps one per host for the worker's life, so what it learns from a 400 (below) lasts that
|
|
41
|
-
long. Each request posts `{base}/v1/messages` with `anthropic-version: 2023-06-01`, the key
|
|
41
|
+
long. Each request posts `{base}/v1/messages` with `anthropic-version: 2023-06-01`, the key (`LLM::ApiKey`: the
|
|
42
|
+
variable `api_key_env:` names, else, on macOS, the login Keychain's item for that name, `HostKeys`; configuration.md,
|
|
43
|
+
"API keys from the Keychain") in the header the host's
|
|
42
44
|
`auth:` names (else the dialect's: `x-api-key` on the standard one), and `Session-Id` (plus the dialect's
|
|
43
45
|
`session_header`) when the loop has a session id.
|
|
44
46
|
|
data/docs/internals/bundles.md
CHANGED
|
@@ -22,9 +22,12 @@ user-facing side (what a bundle owns, profiles, needs) is in [memory.md](../memo
|
|
|
22
22
|
|
|
23
23
|
| Piece | Role |
|
|
24
24
|
|---|---|
|
|
25
|
-
| `BundleCommand` (`lib/samagotchi/bundle_command.rb`) | `chi bundle install/upgrade/uninstall/status/diff/list/build/trash`; runs the Installer, prints its summary, runs the conflict agent step |
|
|
25
|
+
| `BundleCommand` (`lib/samagotchi/bundle_command.rb`) | `chi bundle install/upgrade/uninstall/status/diff/list/build/trash`; runs the Installer, prints its summary, runs the conflict agent step; refuses a source already installed under another name (`installed_under_another_name?`: a git URL bundle recorded under its repo's name before the manifest named it) |
|
|
26
26
|
| `SourceNormalizer` (source.rb) | source → local dir; a git/zip/tar extract is *owned* and removed after the install |
|
|
27
|
-
| `
|
|
27
|
+
| `SourceIdentity` (source.rb) | what a source is, for comparing two installs of one name: `shipped:<dir>` (any gem or checkout), `git:<url>` (no `#ref`, trailing `/` or `.git`), `path:<realpath>`; nil when it can't place one |
|
|
28
|
+
| `PreparedSource` (source.rb) | one source normalized once (dir, owned, git commit): `BundleCommand` reads the bundle's name from its `manifest.yml` (the name for every kind of source, a git URL too) and hands it to the Installer (`prepared:`), so a source is cloned or extracted once per command |
|
|
29
|
+
| `MemoryFiles` | which top-level `*.md` files of a bundle are its memories (`.select`: the `files:` list, else all but README/LICENSE and the like), `.unshipped` (listed, not shipped) and `.symlinks` (skipped: a symlink is never a memory) |
|
|
30
|
+
| `Manifest` | parses/validates `manifest.yml` (`name:` is the bundle's name and a directory under `.bundles/`, so a path such as `../x`, `a/b`, `.` or `..` is refused: `name: … is not a bundle name`; YAML that doesn't parse or isn't a mapping is a `ValidationError` too; files, hooks as `BundleHook`, plugin, scripts, needs, requires_chi, includes, context_providers) and writes it (`Manifest.write`, for Builder). It ignores `requires_chi_next`, a shipped bundle's release-time marker (`script/release/release_tools.rb`, docs/releasing.md) |
|
|
28
31
|
| `Installer` | one install/upgrade/dry run of one bundle (below) |
|
|
29
32
|
| `AssetInstaller` | the parts that live in `.bundles/<name>/`: hooks, guardrail rules, plugin, scripts; returns `AssetInstaller::Installed` (the paths the record keeps) |
|
|
30
33
|
| `Merger` | 3-way classification per file: `install`, `fast_forward`, `keep`, `noop`, `conflict` |
|
|
@@ -47,19 +50,30 @@ and checks the recorded sha256 (and `requires_chi`) before loading.
|
|
|
47
50
|
The order matters: warnings print in it, and a plugin/script the manifest names but the bundle lacks raises
|
|
48
51
|
`InstallError` after the memories are already copied.
|
|
49
52
|
|
|
50
|
-
1. `load_source`: normalize, read `manifest.yml` (strict: a missing/invalid one raises; non-strict: a warning).
|
|
51
|
-
2. `resolve_target`: scope = CLI > manifest > `system
|
|
52
|
-
|
|
53
|
+
1. `load_source`: normalize (or take the `PreparedSource` `BundleCommand` passed as `prepared:`), read `manifest.yml` (strict: a missing/invalid one raises; non-strict: a warning).
|
|
54
|
+
2. `resolve_target`: scope = CLI > manifest > `system`; then `check_source` against the existing record
|
|
55
|
+
(`SourceIdentity`, source.rb): a name differing from an installed one only in case raises (`force` too); a
|
|
56
|
+
shipped name (any case) from a source that isn't a dir in the Installer's `shipped_dir:` (default `SHIPPED_DIR`;
|
|
57
|
+
ShippedUpdate, Profile and SystemBundle pass theirs) raises unless `force` (a recorded source is matched by the
|
|
58
|
+
`SHIPPED_SOURCE` path pattern instead, for older gems); a plain install
|
|
59
|
+
over a record from another source raises (`force` too); an upgrade may switch sources and sets
|
|
60
|
+
`#source_change` (the summary's first line, `Source changes: old → new`). A source `SourceIdentity` can't place
|
|
61
|
+
(an older record's relative path) never refuses.
|
|
62
|
+
3. `install_memories`, per memory (`MemoryFiles.select`: a non-empty `files:` list, else every top-level `*.md` but
|
|
63
|
+
README, CHANGELOG, LICENSE and the like; a listed name not shipped warns; a symlink is skipped with a warning): an upgrade 3-way merges the files the bundle owns (`merge_memory`), a dry run only
|
|
53
64
|
classifies (`preview_memory`), a plain install writes new files and skips existing ones (`skip_existing`). A
|
|
54
65
|
same-name file the bundle didn't write is the user's: skipped and never recorded (`@owned`). Model overlays
|
|
55
66
|
(`<name>.<key>.md`) get no index line (`note_overlay`).
|
|
56
67
|
4. `AssetInstaller#run`: hooks (manifest-listed, else every `hooks/*.rb`), prune dropped hooks, hook checksums,
|
|
57
|
-
guardrails, plugin, scripts; each set replaces the previous version's. requires_chi warnings for hooks/rules.
|
|
68
|
+
guardrails, plugin, scripts; each set replaces the previous version's. requires_chi warnings for hooks/rules. A
|
|
69
|
+
file that is a symlink or resolves outside the bundle dir (`AssetInstaller#linked?`, a linked `hooks/` too) is
|
|
70
|
+
skipped with a warning (a hook or rule) or raises `InstallError` (the plugin, a script).
|
|
58
71
|
5. An upgrade's `prune_dropped_files`: unchanged → trash; edited → kept (`kept_pruned`); owned by another bundle → kept.
|
|
59
72
|
6. `verify_memory_checksums` (strict), `warn_missing_needs`.
|
|
60
73
|
7. `write_provenance` (manifest, not a dry run): bases come from the incoming file, except where the user's edit was
|
|
61
74
|
kept (kept/noop/conflict/kept_pruned, or a re-install's skipped edit): those keep the old base, so the next
|
|
62
|
-
upgrade still sees the edit.
|
|
75
|
+
upgrade still sees the edit. A plain install also keeps the record (the old base) of a file an earlier install
|
|
76
|
+
owned and this version doesn't ship, while it is on disk (`dropped_owned_files`), so the next upgrade prunes it.
|
|
63
77
|
8. `detect_placeholders`.
|
|
64
78
|
|
|
65
79
|
Results are `@results` (file → `{status:, reason:}`), summarised by `#summary`. An upgrade with conflicts keeps an
|
|
@@ -34,6 +34,28 @@ Emission behavior:
|
|
|
34
34
|
`context.status_thresholds` the top bucket reads critical, the one below it
|
|
35
35
|
elevated, the next moderate, and the first bucket (and the rest below) healthy.
|
|
36
36
|
|
|
37
|
+
After `/model` (`SessionCommands#switch_model`), the old model's percentage goes at once (`Engine#switch_model!`
|
|
38
|
+
clears the kept status), and `Engine#announce_switched_context!` sends a `:context_status` of its own, outside any
|
|
39
|
+
turn: the last turn's count (the old model's tokenizer's; the next generation replaces it) against the new model's
|
|
40
|
+
window (`EffectiveModel#context_window`, the lookup below: the running server first, one `/props` GET that a remote
|
|
41
|
+
chat host is spared; else the host's cached model list; else the configured window) and the session's budget,
|
|
42
|
+
with `context_window_tokens` / `context_window_source` (the web takes the window
|
|
43
|
+
from them) and `usage.source: "switch"`. It is kept for a client that joins later (`session_state_snapshot`'s
|
|
44
|
+
`context_status`). A window that doesn't resolve sends `usage: null` and `context_window_tokens: null`: the web shows
|
|
45
|
+
`ctx ?` and drops the old model's window (a chunk's usage then counts no %, until a `generation_started` brings a
|
|
46
|
+
window), and the attached TUI's status row drops its ctx until the next generation (the web's ctx tooltip then says
|
|
47
|
+
`Context window unknown until the next request` over the session's tokens, `ctxBarTip`, until that request starts;
|
|
48
|
+
plain `Context window unknown` after it, and for a session whose window lookup failed). Nothing is sent before the
|
|
49
|
+
session's first counted turn. A
|
|
50
|
+
hook's switch (`before_turn`) sends nothing: its turn's first request counts on the new model anyway.
|
|
51
|
+
|
|
52
|
+
A process that loads the session on another model than its last turn ran on (`Engine#session=`:
|
|
53
|
+
`SessionMetrics#last_model_ref` differs from `effective_model_ref`; a `--resume`'d REPL with `--model`, a worker woken
|
|
54
|
+
after a `/model` with no turn since) drops the old window (`forget_model_reports!`) and calls
|
|
55
|
+
`announce_switched_context!(configured_only: true)`: the configured window only
|
|
56
|
+
(`EffectiveModel#configured_context_window`, no `/props` GET or model list, since a worker runs it before its Bridge
|
|
57
|
+
serves); without one, no percentage until the first generation reports the window.
|
|
58
|
+
|
|
37
59
|
Under an LLM context strategy (`ContextStatus.new(llm_context:)`, the turn's `LLMContextStrategy::Resolved`, given by
|
|
38
60
|
both loops):
|
|
39
61
|
|
data/docs/internals/engine.md
CHANGED
|
@@ -131,6 +131,13 @@ The effective model doesn't resolve (a broken host config, HostRegistry#resolve
|
|
|
131
131
|
→ session_state_snapshot never raises (its model fields are nil); a turn, stats_snapshot,
|
|
132
132
|
chat_model? and system_prompt raise, as before
|
|
133
133
|
|
|
134
|
+
A session's toolkit doesn't resolve (an unknown kit name: a typo, a bundle's kit that isn't installed; a base that
|
|
135
|
+
doesn't resolve, a cycle), at a turn's start (ToolkitBinding#apply!):
|
|
136
|
+
→ that turn declares the full kit with the session's own added and removed tools on top (Toolkit#resolve's
|
|
137
|
+
fallback); the saved toolkit stays as it is, so a reinstalled bundle gets it back
|
|
138
|
+
→ each warning (that fallback, a config.yml kit with a problem: Toolkit.kits) logged once per process as
|
|
139
|
+
toolkit_unresolved (WARN), with a :guardrail_warning (label "toolkit") to the turn and the observers
|
|
140
|
+
|
|
134
141
|
Stopped before the model was asked (prepare_turn returned false):
|
|
135
142
|
→ stopped_before_model(turn) → end_turn(turn, "canceled")
|
|
136
143
|
→ nothing kept, :turn_canceled {cancellation_reason, cancelled_by, duration_ms}
|
|
@@ -460,11 +467,11 @@ Turn events (the main ones; `Events` in `events.rb` keeps the shared sets):
|
|
|
460
467
|
|---|---|---|
|
|
461
468
|
| `:turn_started` | `prepare_turn` | `{session_id, prompt, turn_id, continue?, resumed?, images?, cut_after}` (`resumed`: a step-limit Continue) |
|
|
462
469
|
| `:reminder_injected` | due reminders went into the turn | `{reminders}` |
|
|
463
|
-
| `:context_status` | every request (unless `context.status` is off), the current estimate before it | `{iteration, status, usage, bucket, source}` (internals/context-telemetry.md) |
|
|
464
|
-
| `:generation_started` | a request begins | `{iteration, context_window_tokens, context_window_source, price}` (`price`: the turn's `ModelPrice#to_h`, nil without one; native adds `profile`, `profile_source`) |
|
|
470
|
+
| `:context_status` | every request (unless `context.status` is off), the current estimate before it; and outside any turn after `/model` (or when a session is loaded on another model than its last turn ran on), to the observers only (`Engine#announce_switched_context!`) | `{iteration, status, usage, bucket, source}`; the switch's form has no `iteration`: `{usage, bucket, context_window_tokens, context_window_source}`, with `usage.source: "switch"`, and `usage` nil when the new window doesn't resolve (internals/context-telemetry.md) |
|
|
471
|
+
| `:generation_started` | a request begins | `{iteration, context_window_tokens, context_window_source, price, model_ref}` (`price`: the turn's `ModelPrice#to_h`, nil without one; `model_ref`: the turn's model, asked `host:model`, added by the Engine's stream handler from the model `#generate` resolved after the `before_turn` hooks, so a hook's switch is the one named; native adds `profile`, `profile_source`) |
|
|
465
472
|
| `:generation_chunk` | each streamed chunk | `{iteration, content, text, thinking, payload, tool_call?}` |
|
|
466
473
|
| `:generation_retrying` | the transport retries the request, or the chat loop asks again for a step whose stream dropped mid-answer (`restarted: true`: what the step streamed is void, and the web's live step, the TUI's activity line, the Bridge's TurnAccumulator and the stream hooks' watch start it over) | `{iteration, attempt, max_retries, next_delay, error_class, error_message, status, restarted?}` |
|
|
467
|
-
| `:generation_completed` | a generation ended, or was cut | `{iteration, content_length, thinking_chars, served_model, requested_model, finish_reason, ...}`; a cut adds `stopped_by`, `stop_reason`; the Engine adds `speed`, `tokens` |
|
|
474
|
+
| `:generation_completed` | a generation ended, or was cut | `{iteration, content_length, thinking_chars, served_model, requested_model, finish_reason, ...}`; a cut adds `stopped_by`, `stop_reason`; the Engine adds `speed`, `tokens`, `model_segments` |
|
|
468
475
|
| `:generation_cancelled` | the turn was cancelled mid-loop | `{iteration, reason, stopped_by}` |
|
|
469
476
|
| `:empty_answer_retry` | an empty, cut or malformed answer is asked again | `{iteration, attempt, of, finish_reason, thinking_chars, stopped_by?, malformed?}` |
|
|
470
477
|
| `:steer_cut` | a steer cut the generation | `{iteration, source}` |
|
|
@@ -526,6 +533,10 @@ Model switching, Engine#switch_model!(model_name, persist_default:, typed:):
|
|
|
526
533
|
thread finishes for the old model is neither kept nor applied: #store_profile)
|
|
527
534
|
→ sync_model_key!, @prompt_builder.reset! (the system prompt is built again)
|
|
528
535
|
→ sync_kernel_client! (the kernel talks to the new host), the context window cache dropped
|
|
536
|
+
→ the old model's ctx dropped: @metrics.forget_model_reports! (its window and profile reports) and
|
|
537
|
+
@last_context_status = nil; /model (SessionCommands#switch_model) then calls #announce_switched_context!
|
|
538
|
+
(the ctx % on the new model's window, before its first turn; Engine#session= does the configured-window-only
|
|
539
|
+
variant for a session loaded on another model; internals/context-telemetry.md)
|
|
529
540
|
→ the kernel is never rebuilt; the next turn's backend follows the new host's api
|
|
530
541
|
|
|
531
542
|
Model probes (per turn):
|
|
@@ -149,7 +149,8 @@ is prefilled once. The native and the chat loop use the same plan.
|
|
|
149
149
|
|
|
150
150
|
- The debug log's `generation_completed` line: `prompt=` (the request's prompt tokens), `cached=` (read from the
|
|
151
151
|
server's cache), `cache_write=` (written to it, where the provider reports writes) and `reprefill=` (of the last
|
|
152
|
-
request's prompt on the same model, what was prefilled again
|
|
152
|
+
request's prompt on the same model through the same host, what was prefilled again; a `/model` to the same bare
|
|
153
|
+
model on another host is the switch's prefill, not a re-prefill). The `http` line of a
|
|
153
154
|
remote Claude or Messages request shows `cache=on` (or `cache=1h`) when it carried breakpoints.
|
|
154
155
|
- The warm-up logs `model warmup` with `cached=`, `prefilled=` and `prefill_ms=`.
|
|
155
156
|
- `/stats`: "cached N (P%)", "cache writes" and "re-prefilled" for the session.
|
data/docs/memory.md
CHANGED
|
@@ -199,7 +199,7 @@ step instead of fixing the skill.
|
|
|
199
199
|
## What a bundle owns
|
|
200
200
|
|
|
201
201
|
A bundle owns the memory files it wrote, nothing else. `chi bundle install`
|
|
202
|
-
copies each of its
|
|
202
|
+
copies each of its memories into the scope dir; a file of the same name
|
|
203
203
|
that is already there is left as it is and reported `Skipped` (`--force`
|
|
204
204
|
overwrites it, and then the bundle owns it). A skipped file stays yours: the
|
|
205
205
|
bundle doesn't record it, so `chi bundle upgrade` never merges into it and
|
|
@@ -207,6 +207,53 @@ bundle doesn't record it, so `chi bundle upgrade` never merges into it and
|
|
|
207
207
|
for `chi bundle build` then installing the result on the same machine, and
|
|
208
208
|
for a file another bundle installed.
|
|
209
209
|
|
|
210
|
+
- **Which files are memories.** A manifest whose `files:` lists any: exactly
|
|
211
|
+
those (a listed name the bundle doesn't ship gets a warning). Without a
|
|
212
|
+
list (no `files:`, or `files: {}`): every top-level `*.md` except a repo's
|
|
213
|
+
meta files: README, CHANGELOG, LICENSE (or LICENCE), CONTRIBUTING,
|
|
214
|
+
CODE_OF_CONDUCT and SECURITY, in any case. So a bundle kept in its own git
|
|
215
|
+
repo can have a `README.md` without it becoming a memory. A listed file is
|
|
216
|
+
installed whatever its name (`chi bundle build` of your own `security.md`
|
|
217
|
+
lists it). Since chi 0.75.0. Older chi installs every top-level `*.md`
|
|
218
|
+
whatever `requires_chi` says (it gates hooks, rules and the plugin, not
|
|
219
|
+
memories), so a bundle for older chi too names its readme
|
|
220
|
+
`README.markdown` (or puts docs in a subdirectory).
|
|
221
|
+
`chi bundle upgrade` moves a README an older install made a memory to the
|
|
222
|
+
trash (unless you edited it, like any file a bundle stops shipping).
|
|
223
|
+
- **A symlink is never installed.** A memory that is a symlink is skipped
|
|
224
|
+
with `notes.md is a symlink (not installed)`, and so is a hook or guardrail
|
|
225
|
+
rule that is one or lives in a linked dir (`hooks/x.rb is a symlink (not
|
|
226
|
+
installed)`); a plugin or script that is one fails the install. A tar, zip
|
|
227
|
+
or git repo keeps links, and copying through one would install whatever
|
|
228
|
+
file it points at (`~/.ssh/…`, an API key) as a memory the model reads.
|
|
229
|
+
- **A bundle is its name and its source.** A bundle's name is its
|
|
230
|
+
manifest's `name:`, for every kind of source (a git URL too), and a
|
|
231
|
+
manifest can take any name, so the source decides who installs under it:
|
|
232
|
+
- `chi bundle install` of a source whose manifest names a bundle
|
|
233
|
+
installed from another source is refused, `--force` too (`btw is
|
|
234
|
+
installed from <source>; chi bundle upgrade <new source> switches its
|
|
235
|
+
source, or chi bundle uninstall btw first`);
|
|
236
|
+
- `chi bundle upgrade` may switch the source (a git URL and a local copy
|
|
237
|
+
of it are one bundle; an archive's file name carries its version), and
|
|
238
|
+
its output starts with `Source changes: <old> → <new>` (`--dry-run`
|
|
239
|
+
too);
|
|
240
|
+
- a name chi ships (`guardrails`, `btw`, `samagotchi-system`, …, in any
|
|
241
|
+
case) installs or upgrades only from chi's own copy (a dir in the
|
|
242
|
+
running chi's shipped bundles, not a fork's `lib/samagotchi/bundles/`),
|
|
243
|
+
unless `--force` (`guardrails is a bundle shipped with chi; <source>
|
|
244
|
+
isn't chi's copy of it`);
|
|
245
|
+
- a name that differs from an installed bundle's only in case is refused,
|
|
246
|
+
`--force` too (`BTW differs only in case from the installed btw`): on a
|
|
247
|
+
case-insensitive file system (macOS) it is that bundle's dir.
|
|
248
|
+
|
|
249
|
+
Sources compare as a git URL without its `#ref` (or a trailing `/` or
|
|
250
|
+
`.git`), a shipped bundle by its dir in any chi version, and a path with
|
|
251
|
+
symlinks resolved. A git URL bundle chi 0.75.0 or earlier installed under
|
|
252
|
+
the repo's name (`chi-foo` for `name: foo`) isn't installed again beside
|
|
253
|
+
it: `chi bundle install|upgrade <url>` says `<url> is installed as
|
|
254
|
+
chi-foo: chi bundle uninstall chi-foo, then install it again`. That match
|
|
255
|
+
is by the same source, so a record of `git@github.com:x/foo` isn't found
|
|
256
|
+
from `https://github.com/x/foo` (uninstall it by name).
|
|
210
257
|
- **The index says where a memory came from.** The `index.md` line of a file
|
|
211
258
|
a bundle installed ends in `· from <bundle>`, before the description:
|
|
212
259
|
`- **memory_guide** · system · 2026-10-03 · 5120 · from samagotchi-system —
|
data/docs/plugins.md
CHANGED
|
@@ -15,11 +15,12 @@ my-bundle/
|
|
|
15
15
|
manifest.yml
|
|
16
16
|
plugin.rb
|
|
17
17
|
identity.md # optional, like any bundle's memories, hooks/ and guardrails/
|
|
18
|
+
README.md # never a memory (nor CHANGELOG, LICENSE, ...): see memory.md#what-a-bundle-owns
|
|
18
19
|
```
|
|
19
20
|
|
|
20
21
|
```yaml
|
|
21
22
|
# manifest.yml
|
|
22
|
-
name: my-bundle
|
|
23
|
+
name: my-bundle # the bundle's name for every source (a git URL too): a plain name, no / (not . or ..)
|
|
23
24
|
version: 1.0.0
|
|
24
25
|
plugin:
|
|
25
26
|
file: plugin.rb
|
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, source-links, github-pr, coordinator, claude-peer, codex-peer, and the profiles core and dev) has its own semver in its
|
|
16
|
+
loop-guard, mcp, skills, slow-commands, agents-md, source-links, github-pr, coordinator, claude-peer, codex-peer, 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:`
|
|
@@ -30,6 +30,10 @@ can run the whole release; the user approves the notes before the tag and the
|
|
|
30
30
|
spec checks every bundle is in exactly one), and that profile's version
|
|
31
31
|
goes up: `chi update` then installs only the new bundle for those who
|
|
32
32
|
have the profile;
|
|
33
|
+
- a profile's `requires_chi` is its members' highest: a member's raised
|
|
34
|
+
floor (by hand, or by `release:bump` for a `requires_chi_next` marker)
|
|
35
|
+
raises its profile's too, with the profile's version (`bundles:check`
|
|
36
|
+
notes a profile below a member);
|
|
33
37
|
- the release notes say "run `chi update`" and name the bundles whose
|
|
34
38
|
version moved (what changed in each), not per-bundle upgrade steps.
|
|
35
39
|
- Pre-1.0: config and commands may change in a minor version (0.2 → 0.3); a
|
|
@@ -55,7 +59,7 @@ as the GitHub release body.
|
|
|
55
59
|
| Task | What it does |
|
|
56
60
|
| --- | --- |
|
|
57
61
|
| `rake bundles:sha` | Recomputes the sha256 lines of every shipped bundle manifest (`files:`, `hooks:`, `plugin:`). |
|
|
58
|
-
| `rake bundles:check` | Sha lines match; a bundle changed since the last `v*` tag has a higher version (skipped, with a note, before the first tag). Warns (doesn't fail) when such a bundle's `requires_chi` still admits the tagged chi: mark it `requires_chi_next` if the change uses anything new. Notes a bundle marked `requires_chi_next
|
|
62
|
+
| `rake bundles:check` | Sha lines match; every top-level `*.md` of a bundle is listed in its `files:` (an install takes only the listed ones); a bundle changed since the last `v*` tag has a higher version (skipped, with a note, before the first tag). Warns (doesn't fail) when such a bundle's `requires_chi` still admits the tagged chi: mark it `requires_chi_next` if the change uses anything new. Notes a bundle marked `requires_chi_next`, and a profile (`core`, `dev`) whose `requires_chi` is below a member's. The release workflow and `release:check` run it in full. |
|
|
59
63
|
| `rake 'bundles:check[ci]'` | The CI workflow's (every push and PR): the same, except the version check leaves out the system bundle, which is changed and unbumped between releases (`release:bump` moves its version with VERSION). A task argument, not `CI=1`: GitHub sets `CI` in the release workflow too. |
|
|
60
64
|
| `rake release:draft_changelog` | A draft Unreleased section from the commit subjects since the last tag, grouped. Stdout only. |
|
|
61
65
|
| `rake release:bump[X.Y.Z]` | VERSION, the system manifest, Gemfile.lock, CHANGELOG, and `requires_chi: ">= X.Y.Z"` for each bundle marked `requires_chi_next` (the marker dropped). No commit. |
|