samagotchi 0.65.0 → 0.66.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +48 -1
  3. data/README.md +2 -1
  4. data/bin/chi +51 -2
  5. data/docs/cli.md +11 -1
  6. data/docs/configuration.md +51 -7
  7. data/docs/context.md +4 -1
  8. data/docs/internals/engine.md +4 -1
  9. data/docs/internals/prompt-caching.md +10 -2
  10. data/docs/memory.md +1 -1
  11. data/docs/plugins.md +163 -2
  12. data/docs/releasing.md +1 -1
  13. data/docs/sessions.md +10 -0
  14. data/docs/sub-agent.md +3 -1
  15. data/lib/samagotchi/bundles/claude-peer/manifest.yml +2 -2
  16. data/lib/samagotchi/bundles/claude-peer/plugin.rb +18 -1
  17. data/lib/samagotchi/bundles/codex-peer/manifest.yml +21 -0
  18. data/lib/samagotchi/bundles/codex-peer/plugin.rb +464 -0
  19. data/lib/samagotchi/bundles/codex-peer/scripts/chi-peer-SKILL.md +131 -0
  20. data/lib/samagotchi/bundles/codex-peer/scripts/pair +92 -0
  21. data/lib/samagotchi/bundles/codex-peer/skill_codex_peer.md +68 -0
  22. data/lib/samagotchi/bundles/coordinator/manifest.yml +5 -5
  23. data/lib/samagotchi/bundles/coordinator/plugin.rb +24 -2
  24. data/lib/samagotchi/bundles/dev/manifest.yml +2 -1
  25. data/lib/samagotchi/bundles/system/delegated.md +2 -1
  26. data/lib/samagotchi/bundles/system/manifest.yml +3 -3
  27. data/lib/samagotchi/bundles/system/memory_guide.md +1 -1
  28. data/lib/samagotchi/cache_confirm.rb +9 -6
  29. data/lib/samagotchi/commands/registry.rb +25 -8
  30. data/lib/samagotchi/config.rb +8 -1
  31. data/lib/samagotchi/engine.rb +112 -4
  32. data/lib/samagotchi/kernel_loop.rb +9 -1
  33. data/lib/samagotchi/llm/anthropic_assembly.rb +4 -2
  34. data/lib/samagotchi/plugin/api.rb +80 -7
  35. data/lib/samagotchi/plugin/context.rb +22 -1
  36. data/lib/samagotchi/plugin/loader.rb +3 -2
  37. data/lib/samagotchi/send_command.rb +23 -7
  38. data/lib/samagotchi/session.rb +18 -1
  39. data/lib/samagotchi/session_commands.rb +16 -1
  40. data/lib/samagotchi/session_manager.rb +4 -1
  41. data/lib/samagotchi/session_setup.rb +14 -7
  42. data/lib/samagotchi/system_prompt.rb +16 -1
  43. data/lib/samagotchi/terminal_ui/attach_launcher.rb +19 -9
  44. data/lib/samagotchi/terminal_ui.rb +18 -2
  45. data/lib/samagotchi/tool_runner.rb +13 -1
  46. data/lib/samagotchi/toolkit.rb +246 -0
  47. data/lib/samagotchi/toolkit_command.rb +117 -0
  48. data/lib/samagotchi/tools/registry.rb +43 -6
  49. data/lib/samagotchi/version.rb +1 -1
  50. data/lib/samagotchi/web/app.rb +12 -0
  51. data/lib/samagotchi/web/public/annotate_actions.js +109 -0
  52. data/lib/samagotchi/web/public/annotate_bar.js +84 -2
  53. data/lib/samagotchi/web/public/app.js +21 -7
  54. data/lib/samagotchi/web/public/commands.js +4 -2
  55. data/lib/samagotchi/web/public/index.html +9 -0
  56. data/lib/samagotchi/web/public/info_bar.js +5 -3
  57. data/lib/samagotchi/web/public/session_state.js +3 -0
  58. data/lib/samagotchi/web/public/stream_handlers.js +13 -0
  59. data/lib/samagotchi/web/public/toolkit_chip.js +29 -0
  60. data/lib/samagotchi/worker.rb +2 -0
  61. metadata +10 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 43045b7872d6b50ba24cc2ef1678ac6708346aac9236a5764593660e4e81fc6f
4
- data.tar.gz: dad96a9922329ea3294d7bf04159b3c829796bdfda03d42eef724bbee4a3e4e7
3
+ metadata.gz: 0f78cadf95c501357ce71b15b672dee0a4558016b995a1948532924168580f86
4
+ data.tar.gz: 583f9594eed0ed2febe41390e9c2e0dfbfbde2df72c92f6eab5d8d6a3cf8d776
5
5
  SHA512:
6
- metadata.gz: 4a35a44e9905f8c3411a656383c8a7f2493318732b959d526cdcbdc5dba90e71e874fe678057d3b060878dc5e4560398683790dade299258bd43cf541e0b62cf
7
- data.tar.gz: 9dc56d9cd85b5c5f323315f99bc7c2f4e7580927966f41723a833db561dd5d6c0b3a8720c92ea32e1fafbb3b75e9eb0e93e57e9c4784e1a9983f95e7ac6f17c6
6
+ metadata.gz: 312f72d24bbfc6543d0899f164e69798d068916cef8728242d54893a783c3da7efceb2cbbfeac0337790596984af221e78f5c98541a910945266a2a1122e6ef7
7
+ data.tar.gz: 347d52314752545c6fbc37e7b1a7186faadb6fa2170f91e00b35b8101c76ed9b0ee80b8b954ea927382583c97511dae2fd0a6eca28b694778657f1b654c81226
data/CHANGELOG.md CHANGED
@@ -8,6 +8,52 @@ and commands may change between minor versions. How releases are made:
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [0.66.0] - 2026-10-14
12
+
13
+ ### Added
14
+
15
+ - **Plugin annotation actions** (web): `chi.annotation(label, command:, template:, send:)` adds a pill to the
16
+ selection bar that prepares a command line of the plugin from the selected text (`{quote}`, `{text}`, `{source}`):
17
+ it fills the composer (an earlier draft stays under the quote), or with `send: true` sends at once. Up to 3 inline,
18
+ the rest behind `⋯`. See docs/plugins.md, `chi.annotation`.
19
+ - claude-peer 0.2.0: **Confirm with Claude** in the web's selection bar fills `/claude validate` with the quote;
20
+ `/claude validate|context` titles a quote by its first quoted line, not its `From …:` label.
21
+ - The codex-peer bundle (in the dev profile): claude-peer for an OpenAI Codex CLI session. Codex pairs (its
22
+ `chi-peer` skill, installed by `/codex install-skill`), the `codex_send` tool and `/codex validate|context|last`
23
+ send it a part of the work with `codex queue` (killed after 10 s, never a hang), and Codex's answer comes back as
24
+ the `codex-review` context; **Confirm with Codex** in the web's selection bar. Codex's sandbox needs chi's
25
+ context folder in `writable_roots` (the install card shows the line). See docs/plugins.md, "The codex-peer bundle".
26
+ - **Toolkits**: a session's toolkit names the tools its prompts declare. chi ships `full` (every built-in, the
27
+ default), `lean` (the file, shell, memory, task, question, `web_fetch` and `context_read` tools: ~1.4k tokens
28
+ fewer) and `child` (`lean` with `send_note`, without `memory_write`); config's `toolkit.kits` and bundles
29
+ (`chi.toolkit`) add their own. Pick one with `toolkit.default` / `toolkit.child`, `chi --toolkit NAME` (with
30
+ `--tool` / `--no-tool`, also on `chi send --new`) or `/toolkit NAME | add TOOL | remove TOOL`. It is saved in the
31
+ session when it is created and kept by continues and forks; a change applies at the next turn's start. A left-out
32
+ tool is named in one prompt line, and a call to it is refused before the guardrails. The web shows a session not
33
+ on `full` as a `tools` chip. See docs/configuration.md, "Toolkits".
34
+ - Plugins: `ctx.toolkit` and `ctx.use_toolkit(name)` (before the session's first reply). The coordinator bundle
35
+ (0.4.0) declares the `coordinator` toolkit (`lean` + `delegate`, `delegate_result`, `list_sessions`), and
36
+ `/coordinate` puts a fresh session on it.
37
+
38
+ ### Changed
39
+
40
+ - A command's name may be followed by any whitespace, a newline too: `/foo` on its own line with text under it
41
+ runs `/foo` with that text, in the terminals and the web (before, only a space did).
42
+ - Web: a command refused as busy mid-turn comes back into the composer (it was cleared when sent).
43
+ - A delegate child is told to report what should change (a wrong memory or skill, instructions that led it astray)
44
+ in its reply instead of writing memories (`delegated.md`).
45
+
46
+ ### Fixed
47
+
48
+ - `dialect: fireworks` hosts: the web's ctx meter no longer reads 0% while an answer streams (Fireworks' first stream
49
+ event carries all-zero usage; its counts come at the end).
50
+
51
+ **Still open:** the Claude 5.x "bound to a different conversation" 400 on Anthropic Messages hosts (see 0.65.0); its
52
+ recovery comes in a later release.
53
+
54
+ Update with `chi update` (the system bundle moves with the gem; claude-peer 0.2.0, coordinator 0.4.0, and the dev
55
+ profile 0.5.0 with the new codex-peer 0.1.0).
56
+
11
57
  ## [0.65.0] - 2026-10-13
12
58
 
13
59
  ### Added
@@ -2833,7 +2879,8 @@ and long-lived sessions.
2833
2879
  - A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
2834
2880
  hotkey panel that send selected text or the clipboard to your sessions.
2835
2881
 
2836
- [Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.65.0...HEAD
2882
+ [Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.66.0...HEAD
2883
+ [0.66.0]: https://github.com/dm1try/samagotchi/compare/v0.65.0...v0.66.0
2837
2884
  [0.65.0]: https://github.com/dm1try/samagotchi/compare/v0.64.0...v0.65.0
2838
2885
  [0.64.0]: https://github.com/dm1try/samagotchi/compare/v0.63.0...v0.64.0
2839
2886
  [0.63.0]: https://github.com/dm1try/samagotchi/compare/v0.62.0...v0.63.0
data/README.md CHANGED
@@ -153,7 +153,8 @@ in a child session in its own git worktree, check each report and ask you
153
153
  before each merge; `/children` shows the children and their state. With the
154
154
  `claude-peer` bundle (in `dev`), chi sends a part of its work to a Claude Code
155
155
  session you pair it with, to validate or as context, and Claude answers back
156
- into chi (`/claude`).
156
+ into chi (`/claude`); the `codex-peer` bundle (in `dev`) does the same with an
157
+ OpenAI Codex CLI session (`/codex`).
157
158
 
158
159
  `chi send` is the other half: the text goes in as your message, the same as
159
160
  typing it in the attached terminal or the web composer, and a turn runs. Piped
data/bin/chi CHANGED
@@ -82,7 +82,7 @@ ARGV.each do |arg|
82
82
  base = arg.split("=", 2).first
83
83
  # If dashed version is a known flag (registry or hardcoded), reject underscore variant
84
84
  dashed = base.tr("_", "-")
85
- known = %w[--prompt --resume --attach --shared --no-shared --model --thinking --memory --mute --append-system-prompt --llm-context --llm-context-apply --llm-context-budget --no-interrupt --non-interactive --verbose --no-default-input --port --open --new-token] + Samagotchi::Config.cli_entries.map(&:cli_flag)
85
+ known = %w[--prompt --resume --attach --shared --no-shared --model --thinking --toolkit --tool --no-tool --memory --mute --append-system-prompt --llm-context --llm-context-apply --llm-context-budget --no-interrupt --non-interactive --verbose --no-default-input --port --open --new-token] + Samagotchi::Config.cli_entries.map(&:cli_flag)
86
86
  if known.include?(dashed) || known.include?(base)
87
87
  warn "Unknown option: #{arg} (did you mean #{dashed}?)"
88
88
  exit 1
@@ -119,6 +119,12 @@ parser = OptionParser.new do |opts|
119
119
  opts.on("--thinking LEVEL", Samagotchi::Thinking::LEVELS.map(&:to_s), "This session's own thinking level: off|low|medium|high|default (saved in the session, " \
120
120
  "before the model's; default unsets it; /thinking changes it). With chi web: the default for the sessions " \
121
121
  "its workers run, below a session's own [env SAMAGOTCHI_THINKING_LEVEL]") { |l| options[:thinking] = l }
122
+ opts.on("--toolkit NAME", "This session's toolkit, the tools its prompts declare: full|lean|child|a config or bundle one " \
123
+ "(saved in the session; config toolkit.default; /toolkit changes it)") { |n| options[:toolkit] = n }
124
+ opts.on("--tool NAME", "Also declare this tool on top of the toolkit (repeatable; a comma list too)") { |t| (options[:tools] ||= []) << t }
125
+ # --no-tool is taken out of ARGV before parse! (OptionParser would read it
126
+ # as --tool's negation); this is its help line.
127
+ opts.separator " --no-tool NAME Leave this tool out of the toolkit (repeatable; a comma list too)"
122
128
  opts.on("--memory NAME", "Preload a memory entry into the system prompt (repeatable; a comma list too)") { |m| (options[:memories] ||= []) << m }
123
129
  opts.on("--append-system-prompt FILE", "Put FILE's text into the system prompt, after AGENTS.md (repeatable; saved in the session; " \
124
130
  "config append_system_prompt: is the baseline)") { |f| (options[:append_system_prompt] ||= []) << f }
@@ -171,6 +177,34 @@ parser = OptionParser.new do |opts|
171
177
  opts.on("--version", "Print chi's version") { puts "chi #{Samagotchi::VERSION}"; exit }
172
178
  opts.on("-h", "--help", "Show help") { puts opts; exit }
173
179
  end
180
+ # --no-tool NAME (or --no-tool=NAME), up to a "--". A flag that takes a
181
+ # value (-p, --model …: the parser's own list) keeps its next word, so
182
+ # `chi -p "--no-tool=x is broken"` is a prompt. A tool name never starts
183
+ # with "-": `--no-tool --` or `--no-tool --model` is a missing argument.
184
+ takes_value = parser.instance_variable_get(:@stack).flat_map do |list|
185
+ list.list.select { |switch| switch.is_a?(OptionParser::Switch::RequiredArgument) }.flat_map { |switch| switch.long + switch.short }
186
+ end
187
+ missing_no_tool = lambda do
188
+ warn "Error: missing argument: --no-tool (see chi --help)"
189
+ exit 1
190
+ end
191
+ index = 0
192
+ while index < ARGV.size && ARGV[index] != "--"
193
+ arg = ARGV[index]
194
+ if arg == "--no-tool"
195
+ value = ARGV[index + 1]
196
+ missing_no_tool.call if value.nil? || value.start_with?("-")
197
+ (options[:no_tools] ||= []) << ARGV.delete_at(index + 1)
198
+ ARGV.delete_at(index)
199
+ elsif arg.start_with?("--no-tool=")
200
+ value = arg.delete_prefix("--no-tool=")
201
+ missing_no_tool.call if value.empty? || value.start_with?("-")
202
+ options[:no_tools] = (options[:no_tools] || []) << value
203
+ ARGV.delete_at(index)
204
+ else
205
+ index += takes_value.include?(arg) ? 2 : 1
206
+ end
207
+ end
174
208
  begin
175
209
  parser.parse!
176
210
  rescue OptionParser::ParseError => e
@@ -327,6 +361,17 @@ if options[:append_system_prompt]
327
361
  end.uniq
328
362
  end
329
363
 
364
+ # --toolkit/--tool/--no-tool: a new session's toolkit, as named (the
365
+ # worker resolves it; a bundle's toolkit isn't known here).
366
+ if options[:toolkit] || options[:tools] || options[:no_tools]
367
+ require "samagotchi/toolkit"
368
+ # The flags as given, for the line an existing session's run prints.
369
+ options[:toolkit_flags] = [("--toolkit" if options[:toolkit]), ("--tool" if options[:tools]),
370
+ ("--no-tool" if options[:no_tools])].compact
371
+ options[:toolkit_value] = Samagotchi::Toolkit.from_flags(name: options[:toolkit], add: options[:tools],
372
+ remove: options[:no_tools])
373
+ end
374
+
330
375
  chi_update_hint.call
331
376
  require "samagotchi/launch_mode"
332
377
  launch, launch_note = if scratch
@@ -361,7 +406,9 @@ if launch == :attached
361
406
  muted_memories: options[:muted] || [],
362
407
  llm_context: options[:llm_context] || {},
363
408
  thinking: options[:thinking],
364
- appended_prompt_files: options[:appended_prompt_files] || [])
409
+ appended_prompt_files: options[:appended_prompt_files] || [],
410
+ toolkit: options[:toolkit_value],
411
+ toolkit_flags: options[:toolkit_flags] || [])
365
412
  rescue Samagotchi::TerminalUI::AttachLauncher::Error => e
366
413
  warn "Error: #{e.message}"
367
414
  exit 1
@@ -381,6 +428,8 @@ begin
381
428
  llm_context: options[:llm_context] || {},
382
429
  thinking: options[:thinking],
383
430
  appended_prompt_files: options[:appended_prompt_files] || [],
431
+ toolkit: options[:toolkit_value],
432
+ toolkit_flags: options[:toolkit_flags] || [],
384
433
  non_interactive: options[:non_interactive],
385
434
  model_name: options[:model],
386
435
  scratch: scratch
data/docs/cli.md CHANGED
@@ -187,6 +187,7 @@ controls exit behavior (`--non-interactive`); `--resume` composes with both.
187
187
  | `--attach SESSION_ID` | Attach to a session's worker, waking one if it has exited. |
188
188
  | `--model NAME` | Use this model for the run (overrides the configured default and a resumed session's model). |
189
189
  | `--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. |
190
+ | `--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). |
190
191
  | `--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. |
191
192
  | `--memory NAME` | Preload a memory entry into the system prompt (repeatable; a comma list too). Merged under the config.yml `memories:` baseline. Works attached: the list is stored on the session, so its worker builds the same prompt on every respawn. |
192
193
  | `--append-system-prompt FILE` | Put `FILE`'s text into the system prompt, after AGENTS.md and before the memory indexes (repeatable; relative to the shell's directory). Added after the config.yml `append_system_prompt:` list. A missing or unreadable file is an error, exit 1. Saved in the session, so a respawned worker, a fork and a continue keep it; ignored with `--attach`/`--resume` (one line, as `--memory`). See [Appended prompt files](configuration.md#appended-prompt-files). |
@@ -513,7 +514,16 @@ reasoning of the turn it continues. `/stats` shows it on its `reasoning history:
513
514
  line, the web as the `reasoning` chip. See "Reasoning history" in
514
515
  [configuration.md](configuration.md).
515
516
 
516
- A `/thinking` or `/reasoning-history` change that restarts the prompt cache
517
+ `/toolkit` lists the tools the next turn declares (each with its estimated
518
+ tokens), the session's toolkit and the tools it leaves out. `/toolkit NAME` puts
519
+ the session on another toolkit (dropping its own adds and removes); `/toolkit
520
+ add TOOL` and `/toolkit remove TOOL` change one tool (a tool's name keeps its
521
+ case). The change is saved at once and applies from the next turn's start; a
522
+ running turn keeps its tools. `/toolkit` alone answers mid-turn too; a change
523
+ sent while a turn runs waits for the turn's end. The web shows a session that
524
+ isn't on `full` as the `tools` chip. See [Toolkits](configuration.md#toolkits).
525
+
526
+ A `/thinking`, `/reasoning-history` or `/toolkit` change that restarts the prompt cache
517
527
  asks first: the reply says what it costs, changes nothing, and names the line
518
528
  that applies it (`/thinking low --yes`). `--yes` (or `-y`) applies it at once.
519
529
  The terminal puts that line in the next input (Enter applies it); the web's
@@ -319,11 +319,11 @@ hosts:
319
319
 
320
320
  `auth:` works on any host (`bearer` is the default everywhere but `api: anthropic`).
321
321
 
322
- **Known limit (until the next release):** Claude 5.x models (Opus, Sonnet and Haiku 5.5, Fable 5.1) bind each
322
+ **Known limit (until a later release):** Claude 5.x models (Opus, Sonnet and Haiku 5.5, Fable 5.1) bind each
323
323
  thinking block to the request it came from. When the history before a block changes (the system prompt rebuilt after a
324
324
  worker restart or a `/model` switch, a changed tool set, the forget layer's edits), such a model may answer HTTP 400
325
325
  "… bound to a different conversation" and the turn fails with that error; `/reasoning-history turn` makes it rarer.
326
- The next release sends Anthropic's binding controls and recovers from that 400. Fireworks, Splash and other
326
+ A later release sends Anthropic's binding controls and recovers from that 400. Fireworks, Splash and other
327
327
  non-Claude models are not affected.
328
328
 
329
329
  The loop, tools, events, hooks and UIs are the chat loop's, as for `api: openai`; only the wire differs: the
@@ -739,10 +739,10 @@ models:
739
739
 
740
740
  ## Changes that restart the prompt cache
741
741
 
742
- `/thinking` and `/reasoning-history` ask before a change that makes the server read the conversation again: Gemma's
743
- thinking switch, a remote chat host's thinking fields ("Thinking"), and a change to or from `all` once an earlier reply
744
- has thinking ("Reasoning history"). They ask only when the level the next turn runs under really changes and the
745
- session has a model reply.
742
+ `/thinking`, `/reasoning-history` and `/toolkit` ask before a change that makes the server read the conversation again:
743
+ Gemma's thinking switch, a remote chat host's thinking fields ("Thinking"), a change to or from `all` once an earlier
744
+ reply has thinking ("Reasoning history"), and any change to the declared tools, which start every request
745
+ ("Toolkits"). They ask only when what the next turn runs under really changes and the session has a model reply.
746
746
 
747
747
  - The reply says what it costs and changes nothing yet: `Nothing changed yet: send /thinking low --yes to apply.`
748
748
  - `--yes` (or `-y`, anywhere after the command) applies it at once.
@@ -1276,6 +1276,48 @@ work tree top when inside it, else with `~`.
1276
1276
  picked up by a new process or a rebuild (`/model`). `/stats` lists the files
1277
1277
  in the prompt on its `prompt files:` line.
1278
1278
 
1279
+ ## Toolkits
1280
+
1281
+ A session's **toolkit** names the tools its prompts declare. The declared tools are the front of every request (the
1282
+ `tools` field, or the native template's declarations), so a session that declares fewer sends a shorter prompt, and
1283
+ sessions on the same toolkit share that prefix in a prompt cache.
1284
+
1285
+ ```yaml
1286
+ toolkit:
1287
+ default: lean # the sessions you start (unset: full)
1288
+ child: child # delegate children (unset: full)
1289
+ kits: # your own, on a base
1290
+ review: { base: lean, add: [web_fetch, delegate], remove: [memory_write] }
1291
+ ```
1292
+
1293
+ | toolkit | declares |
1294
+ |---|---|
1295
+ | `full` | every built-in; the default |
1296
+ | `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` |
1297
+ | `child` | `lean` plus `send_note`, without `memory_write` |
1298
+ | `coordinator` | `lean` plus `delegate`, `delegate_result`, `list_sessions` (declared by the coordinator bundle) |
1299
+
1300
+ - **Plugin tools** are declared in every toolkit unless its `remove:` names them, and so is `forget_outputs` under
1301
+ the `forget` layer. A `kits:` entry takes `base:` (default `full`), `add:` and `remove:`; it can't reuse a
1302
+ built-in's name. A bundle declares its own with `chi.toolkit` ([plugins](plugins.md#chitoolkitname-base-add--remove-)).
1303
+ - **Saved at creation, never re-resolved from config.** A session stores its toolkit as named
1304
+ (`"toolkit": {"name": "lean", "add": [], "remove": []}`) when it is created: `toolkit.default`, `toolkit.child`
1305
+ for a delegate child, or `chi --toolkit NAME` / `--tool` / `--no-tool` ([cli](cli.md)). Changing `toolkit.default`
1306
+ doesn't move existing sessions, not even when their worker is woken again; a session from before toolkits is on
1307
+ `full`. A continue and a fork keep the toolkit. `/toolkit` shows and changes it.
1308
+ - **Not loaded.** When a toolkit leaves tools out, one line after the base prompt names them (`Not loaded in this
1309
+ session (toolkit lean): …. Only the user can load one (/toolkit add <name>).`). A call to one is refused before
1310
+ the guardrails (no approval card): `Error: delegate isn't loaded in this session (toolkit lean) …`.
1311
+ - **Applied at a turn's start.** A change (`/toolkit`, a plugin's `ctx.use_toolkit`) is saved at once and the next
1312
+ turn declares it; once the session has a model reply, `/toolkit` asks first ("Changes that restart the prompt
1313
+ cache").
1314
+ - **A name that doesn't resolve** (a typo, a bundle that was uninstalled) declares every tool for that process,
1315
+ with one `toolkit>` warning at the first turn; the saved name stays, so the bundle's return brings it back.
1316
+ - **Versions.** A toolkit's definition can change with chi or a bundle, which changes what saved sessions on it
1317
+ declare after an upgrade: the next request reads the prompt again once, as a tool's schema change does. So can
1318
+ an edit to `toolkit.kits`: a session's worker reads the kits once when it starts, so a running session keeps its
1319
+ tools and the edit reaches it at its next worker start (after an idle exit, `chi sessions restart`).
1320
+
1279
1321
  ## LLM context: the forget layer
1280
1322
 
1281
1323
  Experimental, off by default. With `forget` in a model's `llm_context.strategy` (next to `stale`), chi offers that
@@ -1415,7 +1457,7 @@ Every setting below takes the three forms described in
1415
1457
  marked config.yml only) and, where the CLI
1416
1458
  column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
1417
1459
  `model_aliases:`, `hooks:`, `guardrails:` rules, `bundles:`, `memories:`,
1418
- `append_system_prompt:`) are described in their own sections.
1460
+ `append_system_prompt:`, `toolkit.kits`) are described in their own sections.
1419
1461
 
1420
1462
  | Setting | Default | CLI | What it does |
1421
1463
  |---|---|---|---|
@@ -1483,6 +1525,8 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
1483
1525
  | `context.status_thresholds` | `20,40,60,80` | yes | Percentages that put the estimate into a bucket; the rise into a guidance bucket (from the second up) leaves the model its own `[CONTEXT: …]` line, and the bucket decides the guidance. |
1484
1526
  | `context.every_seconds` | `300` | | How often an attached context source's command runs when it has no `--every` (seconds, at least 30). See [Attached context](context.md). |
1485
1527
  | `context.wake` | `true` | yes | A source whose update says `wake: true` may start a turn in a live, idle session (one per source per 10 minutes, within `session.max_wakes`); so may a session's pushed source's first push. `false`: updates wait as notes for the next turn. See [Waking](context.md#waking). |
1528
+ | `toolkit.default` | `full` | | The toolkit a new session is stored on: the tools its prompts declare (`full`, `lean`, `child`, one of `toolkit.kits` or a bundle's). `chi --toolkit` picks one per session. See "Toolkits". |
1529
+ | `toolkit.child` | `full` | | A delegate child's toolkit. See "Toolkits". |
1486
1530
  | `thinking.turn_preamble` | `true` | yes | Ask a `qwen36` model to open its thinking with a short `TURN:` line (the step label). |
1487
1531
  | `reasoning_history` | `turn` | | Which of its reasoning a chat host's model gets back: `drop`, `turn`, `all`; environment only (`SAMAGOTCHI_REASONING_HISTORY`), below a session's own level (`/reasoning-history`), above the `models:`/`hosts:` entries. See "Reasoning history". |
1488
1532
  | `thinking.level` | `default` | `--thinking` | `off`, `low`, `medium`, `high` or `default` for every model; the flag and env outrank the `models:`/`hosts:` entries, the file's value doesn't; a session's own level outranks them all, and `--thinking` sets it on a session run (on `chi web` it sets this). See "Thinking". |
data/docs/context.md CHANGED
@@ -126,7 +126,10 @@ pbpaste | chi context push clip --project
126
126
  A Claude Code session can be a pushed source too: the
127
127
  [claude-peer bundle](plugins.md#the-claude-peer-bundle)'s `pair` script attaches
128
128
  `claude-inbox` (Claude's inbox socket as the hint) and `claude-review`, where
129
- Claude pushes its answers as JSON with `wake: true`.
129
+ Claude pushes its answers as JSON with `wake: true`. The
130
+ [codex-peer bundle](plugins.md#the-codex-peer-bundle)'s does the same for a
131
+ Codex session: `codex-inbox` (Codex's thread id as the hint) and
132
+ `codex-review`.
130
133
 
131
134
  ### A stream: chat, a log, a feed
132
135
 
@@ -35,7 +35,8 @@ Engine#run_turn(session, prompt, on_event:, max_iterations:, cancel_controller:,
35
35
  │ # images (only when there are any), cut_after (the model's cut wait, s)}
36
36
  │ # refresh_profile! (may ask the server's /props once)
37
37
  │ # turn.settings = LLM::TurnSettings (vision, sampling, thinking; model_name is set in generate)
38
- │ # announces guardrail load failures, starts and awaits plugin init tasks, applies staged tools
38
+ │ # announces guardrail load failures, starts and awaits plugin init tasks, applies staged tools,
39
+ │ # then the session's toolkit (apply_toolkit!: Registry#toolkit, a prompt rebuild when it changed)
39
40
  │ # fires the :session_start hook on the Engine's first turn, then the :before_turn hook
40
41
  │ # (a frozen copy of the history, the prompt, and resumed)
41
42
  │ # turn_messages: system head + history + due reminders (:reminder_injected) + the user message
@@ -227,6 +228,8 @@ One iteration with tool calls:
227
228
  → :tool_dispatch_started {iteration, call_count}
228
229
  → for each call: ToolRunner#run(call, iteration:, call_index:, call_count:,
229
230
  on_stream_event:, max_tool_output_chars:)
231
+ → a tool the session's toolkit leaves out (Registry#left_out) skips the gate: no hooks, no card;
232
+ KernelLoop#dispatch answers it with the not-loaded error
230
233
  → Guardrails::Gate#evaluate (the before_tool_call hooks, then the core checks)
231
234
  → :tool_call_started
232
235
  → an ask verdict: Gate#settle_ask (asks the user, records waited_ms)
@@ -22,11 +22,13 @@ right-hand column happens.
22
22
  ```
23
23
  shared by every session ────────────────────────────────────────────────── breaks it
24
24
  ┌──────────────────────────────────────────────────────────────────────────┐
25
- │ tools built-ins, then plugin tools sorted by (bundle, name) │ a tool added/changed,
26
- │ (chat: the `tools` field; native: the model's template) │ /model, a profile change
25
+ │ tools built-ins, then plugin tools sorted by (bundle, name), │ a tool added/changed,
26
+ │ those the session's toolkit declares (per toolkit) │ /model, a profile change,
27
+ │ (chat: the `tools` field; native: the model's template) │ a toolkit change
27
28
  ├──────────────────────────────────────────────────────────────────────────┤
28
29
  │ SYSTEM, stable part SystemPrompt#system_prompt_with_index
29
30
  │ [native thinking token] base prompt │
31
+ │ not-loaded line (only when the toolkit leaves tools out) │
30
32
  │ rg guidance │ /thinking level,
31
33
  │ system identity (+ model overlay) │ /model, editing a
32
34
  │ model notes (model_notes_* matching the model) │ memory / AGENTS.md
@@ -57,6 +59,12 @@ right-hand column happens.
57
59
  - The system prompt is built **once per loop** and rebuilt only on `@prompt_builder.reset!`: a model switch
58
60
  (`Engine#switch_model!`), a plugin's `tools_changed!`, or a profile change. A per-turn rebuild would
59
61
  break everything below it.
62
+ - The tools layer is shared **per toolkit** (configuration.md, "Toolkits"): sessions on the same toolkit share it,
63
+ and a chain's links keep theirs. A session's toolkit is saved when it is created and never resolved again from
64
+ config. A change (`/toolkit`, `ctx.use_toolkit`) is applied only at a turn's start (`Engine#apply_toolkit!`,
65
+ beside `apply_staged_tools!`, then `reset!`): the chat loop builds `tools:` per request, so a mid-turn change
66
+ would differ from the turn's prompt. Under `full` nothing is filtered and no line is added: the bytes are what
67
+ they were before toolkits.
60
68
  - On remote Claude, `LLM::PromptCache.mark` sends the system message as two text parts. The stable part carries
61
69
  breakpoint #1, so a **new session** reads it from the cache. The last non-system message carries breakpoint
62
70
  #2, so the **next request in the session** reads everything up to it. Mid and tail system messages never take
data/docs/memory.md CHANGED
@@ -270,7 +270,7 @@ profiles, meta bundles that install a set of them:
270
270
  | profile | bundles |
271
271
  |---|---|
272
272
  | `core` | loop-guard, check-in, guardrails, slow-commands, agents-md: the recommended safety set |
273
- | `dev` | known-names, mcp, btw, skills, source-links, github-pr, coordinator, claude-peer |
273
+ | `dev` | known-names, mcp, btw, skills, source-links, github-pr, coordinator, claude-peer, codex-peer |
274
274
 
275
275
  `chi bootstrap` installs `core` and, on a terminal, offers `dev`. An existing
276
276
  install gets neither by itself: `chi bundle install core` (or `dev`).
data/docs/plugins.md CHANGED
@@ -92,10 +92,14 @@ A line that is not a known command keeps its old meaning: in the terminal
92
92
  UIs `/foo` goes to the model as a prompt; the web refuses it and names the
93
93
  commands it knows.
94
94
 
95
+ Any whitespace may follow the name, a newline too: `/hello` on its own line
96
+ with text under it runs `/hello` with that text as `args` (its inner lines,
97
+ blank lines and indentation as typed; only the ends are stripped).
98
+
95
99
  #### `anytime: true`
96
100
 
97
101
  A plugin's normal command waits for its turn: typed while a turn runs, it is
98
- refused as busy (the REPL puts it back into the prompt; a plugin command can't
102
+ refused as busy (the REPL puts it back into the prompt, the web into an empty composer; a plugin command can't
99
103
  ask to run after the turn, as the built-in `/model X` does). An `anytime: true` command
100
104
  runs **at once, on its own thread, beside the running turn**, and the turn
101
105
  goes on:
@@ -144,6 +148,54 @@ chi's own, each bundle's with the bundle's name, and the terminal UIs' own
144
148
  `attached only`, `repl only`, or `terminal and web only` (`/cut`).
145
149
  It works in all three UIs.
146
150
 
151
+ ### `chi.annotation(label, command:, template: "{quote}", send: false)`
152
+
153
+ This adds a pill to the web's selection bar. Selecting text in an answer, a
154
+ thinking block, a step, a tool row or your own message shows
155
+ `[ Annotate | <presets> ┊ <plugin pills> ]`; a plugin pill prepares a command
156
+ line of the plugin from the selection:
157
+
158
+ ```ruby
159
+ chi.command "/claude", "…", anytime: true do |args, ctx| … end
160
+ chi.annotation "Confirm with Claude", command: "/claude validate"
161
+ ```
162
+
163
+ Clicking it on a thinking line fills the composer with
164
+
165
+ ```
166
+ /claude validate
167
+ From your thinking:
168
+ > the quoted line
169
+
170
+ ```
171
+
172
+ and the caret goes under the quote, for your note; Send runs `/claude` with
173
+ everything after the name as `args`.
174
+
175
+ - `label`: the pill's text, 1–40 characters. Its tooltip adds the bundle.
176
+ - `command:`: `/name` or `/name words`, and `/name` must be a command **this
177
+ plugin** registers (checked once `register` returns, so the order of the
178
+ calls doesn't matter; another one's is a load error). At most 5 per plugin.
179
+ - `template:` goes on the lines under the command line, filled in the
180
+ browser: `{quote}` the labelled quote block Annotate makes, `{text}` the
181
+ selection alone, `{source}` where it came from (`answer`, `thinking`,
182
+ `step`, `tool`, `user`). Another `{x}` stays as written. Each line's
183
+ trailing blanks and the blank lines at either end are dropped, and
184
+ indentation is kept (code stays indented).
185
+ - **Fill** (the default) never sends: an empty composer gets the line; a
186
+ composer that already starts with the same command line gets the quote
187
+ appended (several selections collect under one command); any other draft
188
+ stays, under the command line and the quote.
189
+ - `send: true` sends the line at once (the pill is marked ↗). While a turn
190
+ runs, such a pill is disabled unless its command is `anytime`: it would
191
+ come back busy. A fill pill always works; a typed command refused as busy
192
+ comes back into the composer.
193
+
194
+ Pills ride on their command's listing (the worker's snapshot): a muted
195
+ bundle's go with its command, and a session's pills need a worker started
196
+ after the bundle was installed. Up to 3 show inline, the rest behind `⋯`.
197
+ Web only: the terminals type the command (`/claude validate <text>`).
198
+
147
199
  ### `chi.tool(name, description, params:, schema:, label:, preview:, targets:, refs:) { |args, ctx| … }`
148
200
 
149
201
  This adds a tool the model can call. It is declared in the system prompt and
@@ -512,6 +564,21 @@ end
512
564
  - Each task runs once per session start. A `-p … --non-interactive` run
513
565
  starts them with its turn and shows nothing but the answer.
514
566
 
567
+ ### `chi.toolkit(name, base:, add: [], remove: [])`
568
+
569
+ A toolkit sessions can be put on: `base`'s tools (`full`, `lean`, `child`, a
570
+ config or bundle toolkit) with `add` declared and `remove` left out. Users pick
571
+ it like chi's own (`chi --toolkit NAME`, `toolkit.default`, `/toolkit NAME`),
572
+ and the plugin with `ctx.use_toolkit` (below). Names are `a-z`, `0-9`, `_` and
573
+ `-`; one chi has (`full`, `lean`, `child`) or another bundle declared is a
574
+ load error. A session saved on a toolkit whose bundle is gone declares every
575
+ tool (full) until it comes back, with a warning at its first turn. See
576
+ [Toolkits](configuration.md#toolkits).
577
+
578
+ ```ruby
579
+ chi.toolkit "review", base: "lean", add: %w[delegate delegate_result]
580
+ ```
581
+
515
582
  ### `chi.ctx`
516
583
 
517
584
  The plugin's context (the `ctx` its handlers get), for `register` itself:
@@ -564,6 +631,9 @@ full label, `plugin.rb (bundle my-bundle)`.
564
631
  | `ctx.stop_generation(reason)` | cut the generation that is streaming: the turn goes on and the model is asked again ([hooks.md](hooks.md#watching-the-stream)). Shows nothing: post your own notice. True when it cut one now |
565
632
  | `ctx.ask_model(messages:, prompt:, …)` | a side answer from the session's model: see [Side answers](#side-answers-ctxask_model) |
566
633
  | `ctx.sessions` | fork, send to, read, list and stop other sessions: see [Other sessions](#other-sessions-ctxsessions) |
634
+ | `ctx.toolkit` | the name of the session's toolkit (`full` for a session from before toolkits); nil with no session |
635
+ | `ctx.declared_tools` | the tool names the session's next turn declares (its toolkit resolved, with its own adds and removes): ask this, not the name, whether a tool is there |
636
+ | `ctx.use_toolkit(name)` | put the session on toolkit `name` (yours from `chi.toolkit`, or chi's): saved, applied at the next turn's start and announced (`toolkit> toolkit: NAME from the next turn's start`, the web's tools chip follows). Only before the session's first model reply and outside a turn (the declared tools start every request, and a plugin doesn't ask first); otherwise it returns false and changes nothing: tell the user to run `/toolkit NAME`. An unknown name raises ArgumentError. Needs chi 0.66.0 |
567
637
  | `ctx.context` | attach outside text to this session and list it: see [Attached context](#attached-context-ctxcontext) |
568
638
 
569
639
  The Engine itself is never handed to a plugin.
@@ -1486,6 +1556,13 @@ by themselves there.
1486
1556
  report ending with the `/coordinate resume <slug>` to pick it up next
1487
1557
  time. It starts, merges and stops nothing: running children stay running
1488
1558
  and are named in the report.
1559
+ - **The coordinator toolkit.** The bundle declares `coordinator`: `lean`
1560
+ plus `delegate`, `delegate_result` and `list_sessions`
1561
+ ([Toolkits](configuration.md#toolkits)). `/coordinate` puts a fresh session
1562
+ on it before it asks the model (`ctx.use_toolkit`). When chi refuses (a reply
1563
+ or a turn already there) and the session's toolkit has no `delegate`, the
1564
+ reply says to run `/toolkit coordinator`. A continue of a coordinator link
1565
+ keeps the toolkit.
1489
1566
  - **`/children [all]`** shows the session's children as a card, newest first,
1490
1567
  one line each: `` `ab12cd34` · running · fix/flaky · "fix the flaky spec" ``,
1491
1568
  `` `9a8b7c6d` · done · feat/x · reported · "All 12 specs pass" `` (`not
@@ -1566,7 +1643,13 @@ Claude pairs; without one the bundle costs one tool declaration.
1566
1643
  file is there (it goes when that Claude session ends), the last send, and
1567
1644
  Claude's last answer (the `claude-review` summary).
1568
1645
  - `/claude validate <text>` and `/claude context <text>` send the text as it
1569
- is, titled by its first line: paste a selection after them.
1646
+ is, titled by its first line (past a web quote's `From …:` label and a
1647
+ quoted line's `> `): paste a selection after them.
1648
+ - In the web, selecting text offers **Confirm with Claude**
1649
+ ([`chi.annotation`](#chiannotationlabel-command-template-quote-send-false)):
1650
+ it fills the composer with `/claude validate` and the quote, for you to
1651
+ add what needs confirming. On a chi without `chi.annotation` (0.62–0.65)
1652
+ the bundle loads without the pill.
1570
1653
  - `/claude last` sends the model's last completed answer as context. Mid-turn
1571
1654
  it is the answer before the running turn, never the turn's partial text;
1572
1655
  a cancelled or failed turn has no answer, so the one before it goes.
@@ -1598,6 +1681,84 @@ Claude pairs; without one the bundle costs one tool declaration.
1598
1681
  minutes). It is local only (a Unix socket), but it does hand session
1599
1682
  content to another agent: only what the model or you picked.
1600
1683
 
1684
+ ## The codex-peer bundle
1685
+
1686
+ ```sh
1687
+ chi bundle install codex-peer # in the dev profile
1688
+ ```
1689
+
1690
+ The [claude-peer bundle](#the-claude-peer-bundle) for an OpenAI Codex CLI
1691
+ session: chi sends the Codex session you work with a part of the work to
1692
+ validate or as context, and Codex's answer comes back into chi. It is a copy
1693
+ of claude-peer with the transport swapped; what differs is below, the rest
1694
+ (kinds, payload files, the last send, `/codex last`, the status card, the
1695
+ skill install card) works the same.
1696
+
1697
+ - **Sending is `codex queue`.** `codex_send(kind, title, text)` and
1698
+ `/codex validate|context <text>` run
1699
+ `codex queue --thread <uuid> --message <text>` (an argv, no shell), which
1700
+ wakes an idle Codex TUI: the message runs there as a new user turn (checked
1701
+ on Codex 0.162.1; a message queued mid-turn runs after the turn). The
1702
+ thread is the `codex-inbox` context's hint, Codex's `CODEX_THREAD_ID`, which
1703
+ must look like a UUID. `codex` is `$CODEX`, else `codex` on the PATH: a
1704
+ worker started by `chi web` or launchd may have another PATH than your
1705
+ shell, so set `CODEX=` where chi runs if the error says there's no codex.
1706
+ The tool and the anytime `/codex` wait for `codex queue` up to 10 s; a
1707
+ queue that takes longer is killed (its process group) and the send fails
1708
+ with a timeout error, never a hang. Other errors say what to do: no
1709
+ `codex-inbox` (pair from Codex), a thread Codex has no record of (that
1710
+ Codex session is gone: pair again), an ephemeral thread
1711
+ (`codex exec --ephemeral` can't take queued messages), or `codex queue`'s
1712
+ first error line. A Codex session that was closed (`/quit`) is not gone:
1713
+ Codex's app-server still takes the queue (exit 0), chi says sent, and no
1714
+ answer comes; the message waits for that session (`codex resume` there, or
1715
+ pair again from a new one). The status card shows the thread id and says
1716
+ so; there is no cheap check that the session is open.
1717
+ - **Codex's sandbox.** Pairing and every reply write chi's state
1718
+ (`chi context add`/`push` write the session's context files directly),
1719
+ which Codex's default sandbox (workspace-write) refuses. The user adds, to
1720
+ `$CODEX_HOME/config.toml` (else `~/.codex/config.toml`):
1721
+
1722
+ ```toml
1723
+ [sandbox_workspace_write]
1724
+ writable_roots = ["/Users/you/.local/state/samagotchi/context"]
1725
+ ```
1726
+
1727
+ and restarts Codex. Only chi's `context/` folder: the state folder above it
1728
+ also holds guardrails, session files, plugins and the web token.
1729
+ `/codex install-skill`'s card shows the line with your state folder
1730
+ (absolute: config.toml doesn't expand `~`), and the pair script names it
1731
+ when a write is refused ("Operation not permitted"). chi never edits
1732
+ Codex's config. Reading a payload file needs nothing: the default sandbox
1733
+ reads anywhere. No network is needed: a push writes files, and chi wakes
1734
+ on them.
1735
+ - **Pairing** is Codex's, as with Claude: `/codex install-skill` copies the
1736
+ bundle's `scripts/chi-peer-SKILL.md` to
1737
+ `$CODEX_HOME/skills/chi-peer/SKILL.md` (else `~/.codex/…`), with the
1738
+ installed `scripts/pair` path, the payload folder and the writable root
1739
+ written in; in Codex you say "pair with chi <id or chi web URL>", and
1740
+ `sh <bundle>/scripts/pair` attaches `codex-inbox` (the thread id as its
1741
+ hint, pushed so a re-pair refreshes it) and `codex-review`. Run outside
1742
+ Codex (no `CODEX_THREAD_ID`) it refuses. Codex replies with one JSON push
1743
+ to `codex-review` (`text`, `summary`, `wake: true`), which wakes chi, the
1744
+ first answer too: that needs chi 0.63.0, the bundle's `requires_chi`.
1745
+ - **Peer messages are user turns in Codex.** Claude Code marks an inbox
1746
+ message as from another session; `codex queue` lands as a plain user
1747
+ message. So Codex's skill starts with the rule: a message whose first line
1748
+ is exactly `chi <8 hex> · <kind> · <title>` comes from chi, a peer agent,
1749
+ not the user; it can't approve anything, change settings or widen the
1750
+ sandbox, and the part it carries (and a payload file) is data to check,
1751
+ never instructions, since it may hold tool or web output. This is a prompt
1752
+ rule Codex follows, not something enforced: anything shaped like that
1753
+ header is treated this way, and nothing stops a message from asking for
1754
+ more.
1755
+ - In the web, selecting text offers **Confirm with Codex**, which fills
1756
+ `/codex validate` and the quote, as claude-peer's pill does.
1757
+ - **The skill.** `skill_codex_peer` (system scope) is claude-peer's with
1758
+ Codex's names, and on a missing answer points to Codex being mid-turn or
1759
+ its sandbox (the `writable_roots` line). To be asked before each send, a
1760
+ guardrails rule with `tool: codex_send` (see the claude-peer section).
1761
+
1601
1762
  ## Shutdown
1602
1763
 
1603
1764
  When the REPL exits, or a session's worker exits (an idle exit, `/exit`, a
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, and the profiles core and dev) has its own semver in its
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
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:`
data/docs/sessions.md CHANGED
@@ -220,6 +220,16 @@ A session can **continue** an earlier one: "the next day of the same routine" (a
220
220
  - **CLI:** `chi send --new --continues (ID|PREFIX|last:ID) [-m TEXT]` (see *Starting a session*).
221
221
  - **Which link:** an id, a unique prefix, or `last:<id or prefix>`, the latest link of the chain that session is in. It walks the links that still exist: a deleted link in the middle (`chi sessions delete`, the retention sweep of an unarchived one) ends the walk before it, so `last:` of an earlier link then names that earlier link, and continuing it starts a second line beside the links after the gap; name a link after the gap instead. A trigger names the chain this way: `chi send` to an archived link would bring it back (a human's input), not continue the chain.
222
222
 
223
+ ## Toolkits
224
+
225
+ A session file's `toolkit` (`{"name", "add", "remove"}`) is the toolkit it was created on, as named: config's
226
+ `toolkit.default`, `toolkit.child` for a delegate child, or `chi --toolkit` / `chi send --new --toolkit` (with
227
+ `--tool` / `--no-tool`). It is never resolved again from config: a respawned or woken worker declares the same
228
+ tools. `null` (a session from before toolkits) is `full`. A continue (`continue →`, `chi send --new --continues`)
229
+ and a fork (`ctx.sessions.fork`) keep it, as they keep the thinking level; a delegate child gets `toolkit.child`.
230
+ `/toolkit` changes it, saved at once, from the next turn's start. See
231
+ [Toolkits](configuration.md#toolkits).
232
+
223
233
  ## Delegating
224
234
 
225
235
  The `delegate` tool hands a task to a **child session**: an ordinary chi session in a worker of its own, started in the parent's `working_directory` with the task, verbatim, as its first user message, on the parent's model unless the call names one (`model:` takes a name or an alias, resolved as `--model` is; an unknown host comes back as the tool's `Error:` line; an id the host's saved model list doesn't have starts the child with a `Warning: host 'h' doesn't list model 'x' (did you mean: …?)` line in the result). Children run in parallel and only their **final reply** comes back to the parent: the child's newest `output/<timestamp>.txt` file, which the worker writes at the end of each turn that produced visible text, cut head-and-tail like an `execute` result. Nothing of the child's trace enters the parent's context. Nothing is hidden: a child shows in `chi sessions list` (`↳ <parent>`), the web (folded into its parent's card on the strip and in all sessions, whose `▸ N delegates` chip lists the delegates at any depth, see Web UI above; a card of its own with a `↳` chip when its parent isn't listed; `delegated by` in its info bar; the parent's info bar carries a `⑂ N` chip counting its delegates, with how many run, wait, are done, failed, stopped or idle in the tooltip and the waiting ones named in the attention colour; a click opens all sessions), the terminal's status row (a parent's `⑂ 2 running · 1 waiting`, see [Status Line](cli.md#status-line)) and the `list_sessions` tool (`child`; the parent shows as `parent` in the child's own list); the user can `chi --attach <child id>` and steer it mid-run, and `chi send` and `send_note` reach it like any session.