samagotchi 0.11.0 → 0.13.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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +90 -1
  3. data/README.md +20 -7
  4. data/bin/chi +10 -4
  5. data/docs/architecture.md +8 -6
  6. data/docs/cli.md +59 -11
  7. data/docs/configuration.md +29 -8
  8. data/docs/desktop.md +34 -8
  9. data/docs/guardrails.md +73 -7
  10. data/docs/hooks.md +13 -9
  11. data/docs/memory.md +5 -3
  12. data/docs/plugins.md +4 -4
  13. data/docs/sessions.md +9 -6
  14. data/docs/sub-agent.md +128 -0
  15. data/lib/samagotchi/answer_command.rb +220 -0
  16. data/lib/samagotchi/bridge.rb +15 -5
  17. data/lib/samagotchi/bridge_client.rb +8 -3
  18. data/lib/samagotchi/bundle_command.rb +18 -10
  19. data/lib/samagotchi/bundles/guardrails/guardrails/rules.yml +19 -0
  20. data/lib/samagotchi/bundles/guardrails/manifest.yml +2 -2
  21. data/lib/samagotchi/bundles/system/config_modification_protocol.md +5 -5
  22. data/lib/samagotchi/bundles/system/manifest.yml +2 -2
  23. data/lib/samagotchi/cli/command.rb +3 -2
  24. data/lib/samagotchi/cli/exit.rb +23 -0
  25. data/lib/samagotchi/cli/parent_wait.rb +82 -0
  26. data/lib/samagotchi/config.rb +50 -18
  27. data/lib/samagotchi/desktop/macos/App.swift +5 -1
  28. data/lib/samagotchi/desktop/macos/ChiRunner.swift +19 -0
  29. data/lib/samagotchi/desktop/macos/Models.swift +238 -0
  30. data/lib/samagotchi/desktop/macos/Panel.swift +301 -42
  31. data/lib/samagotchi/desktop_command.rb +5 -3
  32. data/lib/samagotchi/engine.rb +480 -491
  33. data/lib/samagotchi/guardrails/parent_approvals.rb +155 -0
  34. data/lib/samagotchi/hooks/bundle_loader.rb +10 -3
  35. data/lib/samagotchi/host_registry.rb +26 -3
  36. data/lib/samagotchi/idle_reminders.rb +13 -36
  37. data/lib/samagotchi/kernel_loop.rb +82 -120
  38. data/lib/samagotchi/llm/chat_loop.rb +17 -38
  39. data/lib/samagotchi/llm/model_result.rb +14 -20
  40. data/lib/samagotchi/llm/native_backend.rb +3 -21
  41. data/lib/samagotchi/llm/openai_chat.rb +10 -2
  42. data/lib/samagotchi/llm/turn_settings.rb +19 -0
  43. data/lib/samagotchi/llm/usage.rb +2 -57
  44. data/lib/samagotchi/memory_bundle/installer.rb +13 -1
  45. data/lib/samagotchi/memory_bundle/status.rb +9 -1
  46. data/lib/samagotchi/memory_paths.rb +8 -0
  47. data/lib/samagotchi/model_catalog.rb +101 -0
  48. data/lib/samagotchi/model_profile.rb +31 -17
  49. data/lib/samagotchi/models_command.rb +112 -0
  50. data/lib/samagotchi/parent_report.rb +213 -0
  51. data/lib/samagotchi/plugin/api.rb +15 -2
  52. data/lib/samagotchi/prompt_literal_guard.rb +4 -2
  53. data/lib/samagotchi/question_desk.rb +29 -2
  54. data/lib/samagotchi/recap_setup.rb +15 -5
  55. data/lib/samagotchi/reminder_queue.rb +73 -0
  56. data/lib/samagotchi/reminder_store.rb +3 -3
  57. data/lib/samagotchi/reply_wait.rb +5 -1
  58. data/lib/samagotchi/self_report.rb +8 -1
  59. data/lib/samagotchi/send_command.rb +51 -76
  60. data/lib/samagotchi/session.rb +15 -0
  61. data/lib/samagotchi/session_commands.rb +5 -5
  62. data/lib/samagotchi/session_manager.rb +36 -13
  63. data/lib/samagotchi/sessions_command.rb +13 -3
  64. data/lib/samagotchi/system_prompt.rb +10 -2
  65. data/lib/samagotchi/terminal_ui/attach_launcher.rb +33 -5
  66. data/lib/samagotchi/terminal_ui/attached_loop.rb +17 -5
  67. data/lib/samagotchi/terminal_ui.rb +23 -29
  68. data/lib/samagotchi/thinking.rb +1 -1
  69. data/lib/samagotchi/thought_stream_splitter.rb +4 -4
  70. data/lib/samagotchi/tool_call_parser.rb +5 -6
  71. data/lib/samagotchi/tool_runner.rb +4 -6
  72. data/lib/samagotchi/tools/builtin_calls.rb +14 -3
  73. data/lib/samagotchi/tools/builtins.rb +15 -3
  74. data/lib/samagotchi/tools/delegate_wait.rb +17 -5
  75. data/lib/samagotchi/tools/execute.rb +8 -8
  76. data/lib/samagotchi/tools/task_create.rb +3 -2
  77. data/lib/samagotchi/tools/task_runtime.rb +7 -2
  78. data/lib/samagotchi/turn_flow.rb +4 -4
  79. data/lib/samagotchi/turn_state.rb +126 -0
  80. data/lib/samagotchi/used_memories.rb +88 -0
  81. data/lib/samagotchi/version.rb +1 -1
  82. data/lib/samagotchi/vision_support.rb +4 -4
  83. data/lib/samagotchi/web/app.rb +17 -35
  84. data/lib/samagotchi/web/message_parts.rb +2 -1
  85. data/lib/samagotchi/web/server.rb +2 -1
  86. data/lib/samagotchi/web/session_hub.rb +0 -4
  87. data/lib/samagotchi/web/session_summary.rb +8 -16
  88. data/lib/samagotchi/worker.rb +1 -1
  89. metadata +14 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1f0638cac2109278f683d0b6caeca1bb633786721471c1f366f2cf3bc459b43f
4
- data.tar.gz: 988b05ee21480227d4a1ed814457dd26af11ed71bf21b3d6c62f863e07f18e1a
3
+ metadata.gz: 2e7f8dcd85ecceeec6695f9ee969633b68c9f4900c597af6ffc87e9b8ee15168
4
+ data.tar.gz: d26fd005c3e98b1177de659b41e94ae09a22d19159d74215475db6b5e1777ff3
5
5
  SHA512:
6
- metadata.gz: b7905a2a451c696ca057def8388137bc6a03523bd43a8e9405d2086a99b979f4804fe51c3bb3fd7fc444ade5ea7909339a29c99a3373b9ad3b254d8702210906
7
- data.tar.gz: ce71c8a3a069014d738088e50372c9f50553bd9da12613a756c5941498ba2f3024f9954d2bbe672b10b6c0b00cc5c80cd296858ec84a1425e4ef3713e84cf0fd
6
+ metadata.gz: aeb8f2f124a5c21f0f5a58ea99c8f473bbde98716ee9abb47478697971a079c32dfd7c36097de58702ec2104ecb8f6846f403019eb9ac66a772c6a14c7b89c0c
7
+ data.tar.gz: 1ae7b930ce05499c42e1d2cea7a140261d6d0240291ecbdd58394fe8da5649452f969ecccc219317b77baaa686dddf77107313d2c469aa4c4c3277aa997873fd
data/CHANGELOG.md CHANGED
@@ -8,6 +8,93 @@ and commands may change between minor versions. How releases are made:
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [0.13.0] - 2026-10-02
12
+
13
+ ### Added
14
+
15
+ - `chi send --wait --format json` prints one JSON object on stdout however the wait ends: the reply, a question with its
16
+ options and the `chi answer` command for it, or the status and a detail line, for scripts and parent agents.
17
+ - `chi answer ID --question QID --option N` (or `--text`, `--dismiss`) answers the question a session waits on and
18
+ waits for what comes next, as `chi send --wait` does, so an agent running chi as a sub-agent can answer chi's
19
+ questions itself. `chi send --wait -m` to a session waiting for an answer is refused with the `chi answer` command
20
+ for it.
21
+ - An approval answered with `chi answer` can be denied, not allowed: allowing is the user's (the web, `chi --attach`).
22
+ `guardrails.parent_approvals: once` in config.yml (no environment variable) lets "Allow
23
+ once" through, never a wider scope, and never on a call that changes chi's own config, hooks or guardrail rules; an approval whose offered scopes are missing lets only Deny through. The worker
24
+ checks it again with its own config (`chi answer` marks its answers); the web and `chi --attach` keep every scope.
25
+ - `chi sessions list` shows a session waiting for an answer as `waiting`; `--format json` has `waiting` (`question`,
26
+ `approval` or `hook`) and `waiting_id`, the question's id for `chi answer --question`.
27
+ - docs/sub-agent.md: running chi as another agent's sub-agent, with instructions to paste into its `CLAUDE.md` /
28
+ `AGENTS.md`.
29
+
30
+ ### Changed
31
+
32
+ - The `guardrails` bundle (0.3.0) asks before answering chi's questions around `chi answer`: `chi --attach` / `chi -p`
33
+ with stdin from a pipe, a here-string or a file, and `curl`/`wget` to a session's `/answer` route.
34
+ - An answer typed into `chi --attach` or the REPL counts as a parent agent's, held to `guardrails.parent_approvals`,
35
+ when stdin isn't a terminal or `CLAUDECODE`, `AI_AGENT` or `SAMAGOTCHI_PARENT_SESSION` is set: a piped `y` no
36
+ longer allows an approval at any scope. chi's `execute` and `task_create` export `SAMAGOTCHI_PARENT_SESSION` (the
37
+ session's id) into the commands they run, and the model's `task_create` env can't set it.
38
+ - `guardrails.enabled` and `guardrails.small_models` are config.yml only: `SAMAGOTCHI_GUARDRAILS_ENABLED=false` no
39
+ longer switches guardrails off, and a worker unsets every `SAMAGOTCHI_GUARDRAILS_*` it inherits, so a parent agent
40
+ starting or waking a session can't switch its guardrails off. `XDG_CONFIG_HOME` still picks the config dir.
41
+ - `chi send --wait` exits 4 (not 1) when `--timeout` passes with the turn still running.
42
+ - Attached `chi -p` from a script exits 3 (not 1) when it leaves a question or an approval waiting for an answer,
43
+ and prints the whole question on stderr with the `chi answer` command for it, as `chi send --wait` does.
44
+ - For an approval, the question block, `answer_with` and the refusal tell a parent agent to deny it and tell its
45
+ user (`--option Deny --text WHY`), not `--option N` or `chi --attach`; other questions keep their wording.
46
+ - `chi send --wait`'s exit 3 prints the whole question on stderr: its text, numbered options and how to answer it.
47
+ - `delegate` / `delegate_result` on a child waiting for an answer give the whole question: its text, numbered options
48
+ and the `chi answer` command for it.
49
+ - `chi send --image` with a missing file or one that isn't an image exits 1 (not 2, which is for usage errors), and
50
+ with `--wait --format json` prints the JSON error line.
51
+
52
+ ### Fixed
53
+
54
+ - The web no longer notifies about, or draws, a question a chi REPL is asking: the web can't answer it there.
55
+ - `delegate_result` on a child whose worker died says `worker_gone` instead of waiting for, or reporting, a question
56
+ nobody can answer; `chi send --wait` and `chi answer` no longer report such a question as waiting either.
57
+ - Answers from Splash no longer start with two blank lines (a workaround until Splash drops them itself).
58
+ - An error in chi's work after a turn's answer no longer also reports the answered turn as failed.
59
+ - A Stop while a turn is still starting (chi asking the model server about the model) now ends the turn there: no before-turn hooks run and a due reminder stays due.
60
+ - `recap.model` set to a model id without a host no longer turns the recap off: it asks the host a bare `--model` goes to.
61
+ - AGENT.md is found at the top of the git work tree when chi runs in a subdirectory (one in the current directory still wins).
62
+ - `execute.timeout_sec` in config.yml (or `--execute-timeout-sec`) sets how long an `execute` command may run, like `SAMAGOTCHI_EXECUTE_TIMEOUT_SEC`; it no longer warns as an unknown key.
63
+ - A bundle's `requires_chi` now applies to its hooks too, not only its plugin: on a too-old chi they don't load, with a notice.
64
+ - `chi bundle install` and `chi bundle status` say when a bundle's hooks won't load because chi is older than its `requires_chi`, also for a bundle without a plugin.
65
+ - A model named with a disabled host's prefix (`box:model` where `hosts.box` has `enabled: false`) is refused with "host 'box' is disabled" instead of going to the default host as a model id.
66
+ - A Stop while a turn is still starting no longer uses up the warning about guardrails or plugins that failed to load: the next turn shows it.
67
+ - A plugin or MCP tool call reads the same in logs, rows and hooks' `content` whatever the model format or Ruby version: its arguments as JSON (Qwen calls showed Ruby's Hash#inspect, which changed in Ruby 3.4). A plugin tool returning a Hash or Array gives the model JSON too.
68
+
69
+ Update with `chi update` (bundles: guardrails 0.3.0).
70
+
71
+ ## [0.12.0] - 2026-10-02
72
+
73
+ ### Added
74
+
75
+ - `chi models [--format json] [--timeout S] [TEXT]` lists the names `--model` takes from every host (default first,
76
+ then `host:id`, then aliases), waiting at most 4 s for the hosts; exit 1 when no host listed.
77
+ - The desktop helper chooses the model for a new session: ⌘M (or a click on the new row's model) opens a searchable
78
+ chooser of every host's models and aliases; the pick and five recent ones are remembered. Run `chi desktop upgrade`.
79
+
80
+ ### Changed
81
+
82
+ - `chi self --model` prints an alias default as the ref it resolves to (`box:gemma-small`, not `small`).
83
+
84
+ ### Fixed
85
+
86
+ - A failed turn's messages and its `turn_failed` event reach a joining client together (a snapshot taken in between
87
+ could show one without the other).
88
+ - A Ctrl-C while an `after_turn` or `session_end` hook runs no longer cancels the turn that already answered: the
89
+ answer stays in the session (it was replaced with a cancel note and a second end event).
90
+ - A web Stop sent right as a turn starts (while chi asks the model server for its settings, a first turn's /props
91
+ probe) cancels the turn at once; it used to answer "not running" and the turn ran on. A failed probe there now ends
92
+ the turn as failed instead of leaving the session marked running.
93
+ - Reminders keep firing on their own while a session is idle. A reminder that a prompt's turn delivered just as the
94
+ idle check found it due, or a reminder turn that failed before it started, could stop them until the next prompt.
95
+
96
+ Update with `chi update` (no bundle changes); run `chi desktop upgrade` for the desktop helper's model chooser.
97
+
11
98
  ## [0.11.0] - 2026-10-01
12
99
 
13
100
  ### Added
@@ -642,7 +729,9 @@ and long-lived sessions.
642
729
  - A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
643
730
  hotkey panel that send selected text or the clipboard to your sessions.
644
731
 
645
- [Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.11.0...HEAD
732
+ [Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.13.0...HEAD
733
+ [0.13.0]: https://github.com/dm1try/samagotchi/compare/v0.12.0...v0.13.0
734
+ [0.12.0]: https://github.com/dm1try/samagotchi/compare/v0.11.0...v0.12.0
646
735
  [0.11.0]: https://github.com/dm1try/samagotchi/compare/v0.10.1...v0.11.0
647
736
  [0.10.1]: https://github.com/dm1try/samagotchi/compare/v0.10.0...v0.10.1
648
737
  [0.10.0]: https://github.com/dm1try/samagotchi/compare/v0.9.0...v0.10.0
data/README.md CHANGED
@@ -14,9 +14,16 @@ command.
14
14
  ## Requirements
15
15
 
16
16
  - Ruby 3.3+
17
- - A model server: [llama.cpp](https://github.com/ggml-org/llama.cpp) `llama-server`
18
- by default; mlx-lm, oMLX and OpenAI-compatible servers also work
19
- (see [Configuration](docs/configuration.md#model-server-transport))
17
+ - A model server: any OpenAI-compatible chat completions server, or
18
+ [llama.cpp](https://github.com/ggml-org/llama.cpp)'s `llama-server` through
19
+ its own API (the native path, and the default)
20
+
21
+ Tested: llama.cpp native (Qwen, Gemma 4), [Splash](https://github.com/incoai/splash)
22
+ over the OpenAI API, and [OpenRouter](https://openrouter.ai) (remote, OpenAI API
23
+ with `api_key_env`). vLLM, LM Studio, Ollama, mlx-lm, oMLX and other
24
+ OpenAI-compatible servers should work over the OpenAI API but are not tested;
25
+ mlx-lm and oMLX also have native transports, see
26
+ [Configuration](docs/configuration.md#model-server-transport).
20
27
 
21
28
  ## Install
22
29
 
@@ -59,7 +66,7 @@ Point chi at your model server; it works out the rest and writes
59
66
 
60
67
  ```sh
61
68
  chi bootstrap 192.168.1.29:8081 # llama.cpp on another machine
62
- chi bootstrap localhost:11434 # Ollama (any OpenAI-compatible server)
69
+ chi bootstrap localhost:11434 # any OpenAI-compatible server
63
70
  chi bootstrap https://openrouter.ai/api/v1 --key-env OPENROUTER_API_KEY
64
71
  chi bootstrap # look on this machine's usual ports
65
72
  ```
@@ -86,9 +93,8 @@ server:
86
93
  port: 8080
87
94
  ```
88
95
 
89
- `server:` is a llama.cpp `llama-server`. For any other OpenAI-compatible
90
- server (vLLM, LM Studio, Ollama, a gateway), name it under `hosts:` with
91
- `api: openai` instead:
96
+ `server:` is a llama.cpp `llama-server`. For an OpenAI-compatible server,
97
+ name it under `hosts:` with `api: openai` instead:
92
98
 
93
99
  ```yaml
94
100
  default:
@@ -145,6 +151,12 @@ stdin plus `-m` puts the stdin above the message as a `>` quote. `chi send --new
145
151
  starts a session with the message instead (it shows in the web at once), and
146
152
  `--wait` blocks and prints the answer.
147
153
 
154
+ Another agent (Claude Code, Codex) can run chi as a sub-agent the same way:
155
+ `chi send --new --wait --format json` returns chi's reply, or the question chi
156
+ stopped on, which the agent answers with `chi answer`. See
157
+ [chi as a sub-agent](docs/sub-agent.md) for a snippet to paste into its
158
+ `CLAUDE.md` / `AGENTS.md`.
159
+
148
160
  ### Send to chi (macOS)
149
161
 
150
162
  `chi desktop install` builds a small native helper: select text in any app →
@@ -173,6 +185,7 @@ once, for the session, for the repo, or for the whole rule in the repo.
173
185
  - [Configuration](docs/configuration.md): `config.yml`, hosts, model server transports, timeouts, retries, logs
174
186
  - [Memory](docs/memory.md): scopes and model-specific overlays
175
187
  - [Sessions](docs/sessions.md): storage, retention, `chi sessions`
188
+ - [chi as a sub-agent](docs/sub-agent.md): `chi send --wait --format json`, `chi answer`, instructions for a parent agent
176
189
  - [Desktop helper](docs/desktop.md): `chi desktop`, the macOS "Send to chi" Service and hotkey
177
190
  - [Guardrails](docs/guardrails.md): allow / ask / deny for tool calls, rules, approvals
178
191
  - [Hooks](docs/hooks.md): plugin hooks and bundle hooks
data/bin/chi CHANGED
@@ -23,13 +23,13 @@ if ARGV[0] == "self"
23
23
  if %w[-h --help help].include?(ARGV[1])
24
24
  puts "Usage: chi self [--model]"
25
25
  puts " Print version, source dir, config/memory/session paths, model/host and bundles."
26
- puts " --model print only the model a new session starts on (exit 1 when none is configured)"
26
+ puts " --model print only the model a new session starts on, an alias resolved (exit 1 when none is configured)"
27
27
  exit 0
28
28
  end
29
29
  require "samagotchi/self_report"
30
30
  # The desktop helper's hint for its "New session" row: offline and quick.
31
31
  if ARGV[1] == "--model"
32
- model = Samagotchi::SelfReport.model_name
32
+ model = Samagotchi::SelfReport.model_ref_name
33
33
  puts model if model
34
34
  exit(model ? 0 : 1)
35
35
  end
@@ -45,10 +45,14 @@ SUBCOMMANDS = {
45
45
  "note" => ["samagotchi/note_command", "NoteCommand"],
46
46
  # a message into sessions as the user's (-m)
47
47
  "send" => ["samagotchi/send_command", "SendCommand"],
48
+ # answer the question a session waits on, then wait (--question, --option)
49
+ "answer" => ["samagotchi/answer_command", "AnswerCommand"],
48
50
  # find a model server and write config.yml (--model, --name)
49
51
  "bootstrap" => ["samagotchi/bootstrap_command", "BootstrapCommand"],
50
52
  # bring an installed chi up to date
51
53
  "update" => ["samagotchi/update_command", "UpdateCommand"],
54
+ # the models --model takes, from every host (--format, --timeout)
55
+ "models" => ["samagotchi/models_command", "ModelsCommand"],
52
56
  # the native "Send to chi" helper
53
57
  "desktop" => ["samagotchi/desktop_command", "DesktopCommand"],
54
58
  # install, upgrade, uninstall, status, diff, list, build (--scope, --force, …)
@@ -92,8 +96,10 @@ parser = OptionParser.new do |opts|
92
96
  chi sessions <list|stop|archive|unarchive|delete|prune|clean>
93
97
  chi note [--source NAME] [-m TEXT] ID...|--all background context for sessions
94
98
  chi send [-m TEXT] ID...|--new a message into sessions, as if typed there
99
+ chi answer ID --question QID --option N answer a question a session waits on
95
100
  chi bundle <install|upgrade|uninstall|status|diff|list|build>
96
101
  chi desktop <install|upgrade|uninstall|status> macOS "Send to chi" helper
102
+ chi models [--format json] [TEXT] the models --model takes, from every host
97
103
  chi self version, paths, model and bundles
98
104
  chi update [--dry-run] update this chi, its bundles and the desktop helper
99
105
  (each subcommand takes --help)
@@ -103,7 +109,7 @@ parser = OptionParser.new do |opts|
103
109
  opts.on("--resume SESSION_ID", "Resume a previous session by its ID (or a unique prefix of it)") { |s| options[:resume] = s }
104
110
  opts.on("--attach SESSION_ID", "Attach to a session's worker, waking one if it has exited; ID or a unique prefix") { |s| options[:attach] = s }
105
111
  opts.on("--[no-]shared", "Run the session in a background worker and attach to it, so `chi web` can share it (the default, config session.shared; with --resume: that session); --no-shared runs a plain in-process REPL") { |v| options[:shared] = v }
106
- opts.on("--model NAME", "Model selector (runtime only, overrides --resume) [alias for --default-model]") { |m| options[:model] = m; cli_overrides["default.model"] = m }
112
+ opts.on("--model NAME", "Model for this run, not saved (overrides --resume's; config default.model) [env SAMAGOTCHI_DEFAULT_MODEL]") { |m| options[:model] = m; cli_overrides["default.model"] = m }
107
113
  opts.on("--profile NAME", Samagotchi::ModelProfile::NAMES,
108
114
  "Prompt profile for every model in this run (#{Samagotchi::ModelProfile::NAMES.join('|')}) [alias for --model-profile]") { |p| cli_overrides["model.profile"] = p }
109
115
  opts.on("--thinking LEVEL", Samagotchi::Thinking::LEVELS.map(&:to_s), "How much the model thinks this run: off|low|medium|high|default (outranks config.yml; " \
@@ -308,7 +314,7 @@ if launch == :attached
308
314
  warn "Error: #{e.message}"
309
315
  exit 1
310
316
  end
311
- exit(ended == :detached ? 0 : 1)
317
+ exit Samagotchi::TerminalUI::AttachLauncher.exit_status(ended)
312
318
  end
313
319
 
314
320
  begin
data/docs/architecture.md CHANGED
@@ -156,10 +156,10 @@ agent logic and can be used without any terminal rendering; the UI is a thin lay
156
156
  #### Using the core
157
157
 
158
158
  ```ruby
159
- engine = Samagotchi::Engine.new(mode: :assist, model_name: "gemma4", memories: [])
159
+ engine = Samagotchi::Engine.new(model_name: "gemma4", memories: [])
160
160
  session = Samagotchi::Session.new_session(mode: "assist", model_name: "gemma4", working_directory: Dir.pwd)
161
161
 
162
- engine.run_turn(session, "hello", on_event: nil) # => KernelLoop::Result (`.output`)
162
+ engine.run_turn(session, "hello", on_event: nil) # => LLM::ModelResult (`.output`)
163
163
  ```
164
164
 
165
165
  #### The `on_event` seam
@@ -169,7 +169,7 @@ forwards the raw `KernelLoop` events unchanged (the low-level contract) and adds
169
169
  higher-level events so UIs get clean turn boundaries without inferring them:
170
170
 
171
171
  - `:turn_started` — `{ session_id:, prompt: }`
172
- - `:turn_completed` — `{ result: }` (the final `KernelLoop::Result`)
172
+ - `:turn_completed` — `{ result: }` (the final `LLM::ModelResult`)
173
173
  - `:turn_canceled` — `{ cancellation_reason: }`
174
174
 
175
175
  Every event is a `Hash` with a `:type` symbol key; the sink must not raise (the Engine
@@ -223,7 +223,9 @@ process that already owns the `Engine`); every worker starts it, and it exposes:
223
223
  refused connection is `503 not_live`.
224
224
  - `POST /session/:id/cancel` — cancel the running turn; `202`, or `409` when none runs.
225
225
  - `POST /session/:id/answer` — answer the pending question; `200`, `409` when another client
226
- answered first or it is gone, `400` for an invalid selection.
226
+ answered first or it is gone, `400` for an invalid selection. With `client_id: "cli:answer"`
227
+ (`chi answer`, a parent agent) an allow on an approval beyond the worker's
228
+ `guardrails.parent_approvals` is `403 parent_approval_refused`, the question still open.
227
229
  - `POST /session/:id/question/dismiss` — leave the question unanswered (an approval: denied);
228
230
  `200`, or `409` when it is no longer pending.
229
231
  - `POST /session/:id/command` — a session command (`/model`, `/models`, `!rollback`, `!cmd`,
@@ -330,8 +332,8 @@ conversation (the prompt plus completed tool iterations) and emits
330
332
  `:turn_failed` with `error_kind:`, `retryable:`, `host:` and a one-line
331
333
  `summary:`, which the REPL, the attached TUI and the web show.
332
334
 
333
- **Usage and models.** `ModelResult#usage` is an `LLM::Usage` (server counts, else
334
- a chars/4 estimate, else zeros; never nil). Model lists are `LLM::ModelInfo`
335
+ **Usage and models.** A turn's token counts are `SessionMetrics`', read from the
336
+ stream events (server counts, else a chars/4 estimate). Model lists are `LLM::ModelInfo`
335
337
  (`HostRegistry#list_all_models`); the context window comes from the running
336
338
  server (`/props`), then the host's model list, then config.
337
339
 
data/docs/cli.md CHANGED
@@ -17,18 +17,20 @@
17
17
  - `chi web --web-view turn` — draw turns with the turn view (each turn as one block of steps, the running one at the bottom of the history) instead of the default stage view (the running turn pinned above the composer); `?view=stage|turn` on the page URL overrides it (see [Web views](#web-views))
18
18
  - `chi sessions list|stop|archive|unarchive|delete|prune|clean` — manage persisted sessions; `list` shows this git project's, `list --scope=all` every one, a delegated session with `↳ <parent>`, `list --archived` the archived ones too (see [Sessions](sessions.md)). A usage error (an unknown subcommand or flag, a flag missing its value, `stop` or `delete` with no ids, a bad `list --format` or `--scope`) exits 2; an unknown or refused session exits 1
19
19
  - `chi note [--source NAME] [-m TEXT] (ID|PREFIX)... | --all` — add a context note (TEXT or stdin) to sessions: background the model sees on its next turn; it starts no turn (see [Sessions: Context notes](sessions.md#context-notes))
20
- - `chi send [-m TEXT] [--image PATH]... (ID|PREFIX)...` — send a message to sessions as if typed there: a turn starts (or a running one picks it up); piped stdin goes above `-m` as quoted context, and `--image` attaches images (see [Sessions: Sending a message](sessions.md#sending-a-message)); `--new` starts a session with it instead, and `--wait` prints the answer (`--wait ID` with no message waits for the next reply without sending; see [Starting a session](sessions.md#starting-a-session))
21
- - `chi desktop install|upgrade|uninstall|status` — the macOS "Send to chi" helper: a Service and a ⌃⌥⌘N hotkey that send text to live sessions as context notes (see [Desktop helper](desktop.md))
22
- - `chi self` — print version, source dir (checkout or installed gem), config/memory/session paths, model/host and bundles; `chi self --model` prints only the model a new session starts on (the desktop helper's hint)
20
+ - `chi send [-m TEXT] [--image PATH]... (ID|PREFIX)...` — send a message to sessions as if typed there: a turn starts (or a running one picks it up); piped stdin goes above `-m` as quoted context, and `--image` attaches images (see [Sessions: Sending a message](sessions.md#sending-a-message)); `--new` starts a session with it instead, and `--wait` prints the answer (`--wait ID` with no message waits for the next reply without sending; `--format json` prints one JSON object instead; exit 3 a question waits, its options on stderr; exit 4 `--timeout` passed with the turn still running; see [Starting a session](sessions.md#starting-a-session))
21
+ - `chi answer ID --question QID (--option N|LABEL)... [--text T] [--timeout S] [--format json]` (or `--dismiss`) — answer the question a session waits on (the one `chi send --wait` exited 3 with), then wait and print what comes next as `chi send --wait` does: the reply (0), the next question (3), still running after `--timeout` (4). `--option` is 1-based or the label, repeated on a multi-select question; `--text` is free text, or a Deny's reason; `--dismiss` leaves it unanswered and the model finishes its reply. A question no longer open (the web answered first) isn't answered again: it waits for that turn's reply. An option the question doesn't offer exits 2; a worker that is gone exits 1 (`send the task again: chi send --wait -m "…" ID`). An approval can be denied, not allowed, unless `guardrails.parent_approvals: once` (a convention, not a security boundary; see [Guardrails](guardrails.md#approvals-from-a-parent-agent))
22
+ - `chi desktop install|upgrade|uninstall|status` — the macOS "Send to chi" helper: a Service and a ⌃⌥⌘N hotkey that send text or images to a session (live, stopped or new) as a message (⏎, a turn runs) or a context note (⌘⏎) (see [Desktop helper](desktop.md))
23
+ - `chi models [--format text|json] [--timeout S] [TEXT]` — list the models every configured host offers, as the names `--model` takes (see [Listing the models](#listing-the-models))
24
+ - `chi self` — print version, source dir (checkout or installed gem), config/memory/session paths, model/host and bundles; `chi self --model` prints only the model a new session starts on, an alias resolved to its target (the desktop helper's hint)
23
25
  - `chi update [--dry-run] [--no-gem] [--no-bundles] [--no-desktop]` — update an installed chi: the gem, the system bundle, the shipped bundles you installed and the desktop helper, in one table (see [Updating](#updating))
24
- - `chi bundle install|upgrade|uninstall|status|diff|list|build` — manage memory bundles (see [Bundle hooks](hooks.md#bundle-hooks-unified-workflow-bundle)); `list` shows the installed ones and the ones shipped with chi, which `install <name>` installs; `core` and `dev` are profiles that install a set of them (see [Bundle profiles](memory.md#bundle-profiles-core-and-dev), [Guardrails](guardrails.md), [Plugins](plugins.md#the-btw-bundle), [the mcp bundle](plugins.md#the-mcp-bundle) [the loop-guard bundle](plugins.md#the-loop-guard-bundle), [the check-in bundle](plugins.md#the-check-in-bundle) and [the skills bundle](plugins.md#the-skills-bundle)). A usage error (an unknown subcommand or flag, a missing argument, a bad `build --scope`) exits 2, as for every other command
26
+ - `chi bundle install|upgrade|uninstall|status|diff|list|build` — manage memory bundles (see [Bundle hooks](hooks.md#bundle-hooks-unified-workflow-bundle)); `list` shows the installed ones and the ones shipped with chi, which `install <name>` installs; `core` and `dev` are profiles that install a set of them (see [Bundle profiles](memory.md#bundle-profiles-core-and-dev), [Guardrails](guardrails.md), [Plugins](plugins.md#the-btw-bundle), [the mcp bundle](plugins.md#the-mcp-bundle) [the loop-guard bundle](plugins.md#the-loop-guard-bundle), [the check-in bundle](plugins.md#the-check-in-bundle) and [the skills bundle](plugins.md#the-skills-bundle)). A usage error (an unknown subcommand or flag, a missing argument, a bad `build --scope`) exits 2, as for the other subcommands. Plain `chi` and `chi web` exit 1 on an unknown flag, a flag missing its value or a stray argument
25
27
 
26
28
  ### First setup
27
29
 
28
30
  `chi bootstrap TARGET` names the model server and writes the config for it:
29
31
 
30
32
  ```sh
31
- chi bootstrap 192.168.1.29:8081 # host:port (port 8080 when none)
33
+ chi bootstrap 192.168.1.29:8081 # host:port (an IP or localhost: port 8080 when none; a domain: https 443, then http 80)
32
34
  chi bootstrap https://openrouter.ai/api/v1 --key-env OPENROUTER_API_KEY
33
35
  chi bootstrap # try localhost 8080, 11434, 1234, 8000
34
36
  ```
@@ -66,6 +68,29 @@ chi bootstrap # try localhost 8080, 11434, 1234, 8000
66
68
  A bundle that fails to install makes the exit 1. `--dry-run` says what it
67
69
  would install.
68
70
 
71
+ ### Listing the models
72
+
73
+ `chi models` asks every host in `hosts:` for its models, in parallel, and prints the names `--model` (and
74
+ `chi send --new --model`, `/model`) takes, one per line:
75
+
76
+ ```sh
77
+ $ chi models
78
+ gemma-4 # the default (default.model), first
79
+ splash:incoai/Qwen3.8-27B-Splash # another host's id: host:id
80
+ openrouter:qwen/qwen3.8-27b
81
+ small -> box:gemma-small # an alias and the ref it resolves to
82
+ $ chi models qwen # only the names containing "qwen" (any case)
83
+ ```
84
+
85
+ - A default-host id is bare, except one with a `:` (`qwen3:8b`), which is written `<host>:qwen3:8b` so it can't
86
+ read as a host name. An id that an alias of the same name hides is left out.
87
+ - Each run lists anew (no cache); `--timeout S` caps the wait for the hosts (default 4 s). A host that fails or doesn't
88
+ answer in time is noted on stderr (`chi models: box: no answer in 4 s`).
89
+ - `--format json` prints one object: `default` (and `default_typed` when it was set as an alias), `default_host`,
90
+ `models` (`name`, `host`, `id`), `aliases` (`name`, `ref`, `host`) and `warnings`. The desktop helper's model
91
+ chooser reads it.
92
+ - Exit 0 when any host listed its models, 1 when none did (the default is still printed), 2 on a usage error.
93
+
69
94
  ### Updating
70
95
 
71
96
  `chi update` brings an installed chi up to date and prints one table:
@@ -155,7 +180,8 @@ controls exit behavior (`--non-interactive`); `--resume` composes with both.
155
180
 
156
181
  Every setting in the config registry (`lib/samagotchi/config.rb`) that exposes a CLI
157
182
  flag also works as `--kebab-case VALUE`, e.g. `--server-host`, `--server-port`,
158
- `--read-truncate-at-bytes`. `chi --help` lists them all.
183
+ `--read-truncate-at-bytes`; an on/off setting is `--[no-]kebab-case` with no value
184
+ (`--context-status`, `--no-context-status`). `chi --help` lists them all.
159
185
 
160
186
  **Which loop runs.** There is no backend flag: the model's host decides. A host with
161
187
  `api: openai` in config.yml is driven through the OpenAI chat API (streamed; a remote
@@ -180,11 +206,22 @@ as an unknown key).
180
206
 
181
207
  Notes:
182
208
 
209
+ - Attached `chi -p …` with stdin not a terminal (a script, an agent's shell)
210
+ leaves when its input ends and the turn is done. Exit status: 0 the turn
211
+ ended, 3 a question or an approval waits for an answer (stderr gets the
212
+ whole question as `chi send --wait` prints it, with the
213
+ `chi answer ID --question QID --option N` command; the worker keeps it
214
+ open, and `chi answer` or the web answers it), 1 the turn failed or the
215
+ worker went away. For a
216
+ script, `chi send --new --wait` is the better entry point: stdout holds the
217
+ answer alone (see [Starting a session](sessions.md#starting-a-session)).
183
218
  - `-p` always feeds **and** runs the prompt; there is no feed-and-edit variant. To
184
219
  prefill (edit, not execute) the first REPL line, use the
185
220
  `SAMAGOTCHI_DEFAULT_INPUT` environment variable instead.
186
- - Prompt history is persisted per session; `--resume` preserves prior messages as
187
- turn context (a `-p` run on a resumed session never clobbers existing history).
221
+ - Prompt history (↑) is one file shared by every session (see [Persistent Prompt
222
+ History](#persistent-prompt-history)); `--resume` preserves the session's prior
223
+ messages as turn context (a `-p` run on a resumed session never clobbers its
224
+ conversation).
188
225
  - Non-interactive runs (`-p` with `--non-interactive`, or bare `--non-interactive`)
189
226
  print only the final result output — no spinner, status line, or REPL.
190
227
 
@@ -333,6 +370,16 @@ file and index line stay as they are; only this session doesn't see it.
333
370
  - The `read` tool on `memories/<name>.md` is not refused (a guardrails rule
334
371
  can protect the path if wanted).
335
372
 
373
+ ### Session commands
374
+
375
+ `/help` lists the commands this session takes, the installed bundles' too, each
376
+ with a line on what it does. `/stats` shows the session's numbers: turns, tool
377
+ calls (by tool, with errors), iterations, tokens in/out summed over every
378
+ request, generation latency, cancellations, retries, context used and window,
379
+ the prompt profile and the model the server says it ran. `/recap` is in
380
+ [Session recap](#session-recap), `/model` and `/models` in [Runtime Model
381
+ Switch](#runtime-model-switch-assist-mode).
382
+
336
383
  ### Typing during a turn
337
384
 
338
385
  The prompt stays open while a turn runs, in an attached terminal and in the plain
@@ -344,7 +391,8 @@ REPL alike:
344
391
  A line that comes after the turn's last step runs as the next turn, and so
345
392
  does one sent after Ctrl-C: it doesn't merge into the turn being cancelled.
346
393
  Reminder turns take merged lines too.
347
- - `/stats` and `/recap` answer at once. Other commands (`!cmd`, `/model`,
394
+ - `/stats` and `/recap` answer at once, and in an attached terminal so do `/help`
395
+ and a plugin's anytime command (`/btw`). Other commands (`!cmd`, `/model`, `/models`,
348
396
  `!rollback`, `/continue`, `/guardrails`) say `busy: wait for the turn to end`
349
397
  and go back into the prompt, so Enter runs them once the turn ends.
350
398
  - A question (`ask_user_question`, a guardrails approval) turns the prompt into
@@ -577,7 +625,7 @@ In interactive assist mode, you can switch the request model without restarting:
577
625
 
578
626
  - `/model <name>`: set a session-scoped model override.
579
627
  - `/model host:model` or `/model host:alias`: qualified host routing (only `:` names a host; `openai/gpt-4o` is a model id). `host:alias` applies the alias; an alias whose target names another host is refused (`alias 'tiny' names host 'box', not 'openrouter'`). Aliases apply once: an alias pointing to another alias sends that name as written.
580
- - `/model --default <name>`: set session model and persist as new default in `config.yml` (also updates `SAMAGOTCHI_DEFAULT_MODEL` for future sessions; supports `host:model` full ref).
628
+ - `/model --default <name>`: set session model and persist as new default (`default.model`) in `config.yml` for future sessions (supports `host:model` full ref). It writes only the file: an exported `SAMAGOTCHI_DEFAULT_MODEL` (or `--model`) still wins over it.
581
629
  - `/model <name> --alias <alias>`: create alias for current effective model (alias value may be bare or `host:model`).
582
630
  - `/model`: show the effective model (and default when diverged: `runtime model: <effective> (default: <default>, profile=<name>, <source>)`, e.g. `profile=qwen36, server (chat_template)`).
583
631
  - `/model clear` (or `default`/`none`/`off`): clear the session override, reverting to the configured default.
@@ -700,7 +748,7 @@ A long, tool-heavy turn says what it has been doing. From the turn's 3rd tool ca
700
748
  a dim row under the activity row tallies its calls, with no model call:
701
749
 
702
750
  ```
703
- 12 tool calls (2 failed) · execute ×7 · read_file ×3 · edit_file ×2 · last: execute command=bundle exec rspec
751
+ 12 tool calls (2 failed) · execute ×7 · read ×3 · edit ×2 · last: execute command=bundle exec rspec
704
752
  ```
705
753
 
706
754
  It lists the top 3 tools by count (ties go to the tool used first), the failed calls
@@ -49,8 +49,9 @@ hosts:
49
49
  # url:, else http://host:port/v1) with its api_key_env; base_url is an OpenAI API
50
50
  # base as given (e.g. http://h:8081/v1). model is a model ref like any other: an
51
51
  # alias is applied, and its host:/the alias's host picks the host when host_ref
52
- # is left out (a model naming another host than host_ref turns recap off with a
53
- # warning). recap: false turns it off.
52
+ # is left out; a model naming no host goes where a bare --model goes (the
53
+ # default host). A model naming another host than host_ref turns recap off with
54
+ # a warning. recap: false turns it off.
54
55
  recap:
55
56
  # host_ref: small-box
56
57
  # model: your-small-model-id
@@ -114,8 +115,9 @@ any per-run `--memory` values (config baseline first, deduped). A per-run
114
115
 
115
116
  ### Environment variables
116
117
 
117
- Every setting has an environment variable: `SAMAGOTCHI_` plus its dotted
118
- name in upper case, with `_` for each dot. `default.model` is
118
+ Every setting has an environment variable (except the ones the table marks
119
+ config.yml only): `SAMAGOTCHI_` plus its dotted name in upper case, with `_`
120
+ for each dot. `default.model` is
119
121
  `SAMAGOTCHI_DEFAULT_MODEL`, `server.read_timeout` is
120
122
  `SAMAGOTCHI_SERVER_READ_TIMEOUT`. Use them to override the file for one run or
121
123
  one shell (`SAMAGOTCHI_LOG_LEVEL=debug chi`); keep lasting choices in the file.
@@ -230,6 +232,18 @@ hosts:
230
232
  api_key_env: BOX_LLAMA_KEY
231
233
  ```
232
234
 
235
+ A host without `port:` (and without `url:`) uses port 8080. `enabled: false`
236
+ turns an entry off without deleting it: chi skips it as if it weren't there
237
+ (not in `chi models`, never the default host; `box:model` then counts as an
238
+ unknown host prefix, see [Llama Model Routing](#llama-model-routing)):
239
+
240
+ ```yaml
241
+ hosts:
242
+ box:
243
+ host: 192.0.2.20
244
+ enabled: false
245
+ ```
246
+
233
247
  For models on that host, chi uses the chat loop (its own OpenAI chat adapter): it
234
248
  takes the OpenAI base (`url:`, else `http://HOST:PORT/v1`) and streams messages plus
235
249
  function schemas from `/v1/chat/completions`; the model's reasoning (`reasoning_content`)
@@ -741,6 +755,10 @@ The helper reads these from its launch file: after editing them run
741
755
 
742
756
  If an AGENT.md file is present in the project root, samagotchi injects its
743
757
  contents into the system prompt under a "Project specific description:" section.
758
+ The project root is the top of the git work tree chi runs in (a linked
759
+ worktree's own checkout), or the current directory outside a repository. An
760
+ AGENT.md in the current directory is read instead when there is one; only one
761
+ file is read.
744
762
 
745
763
  To skip loading AGENT.md, set `skip_agent_md: true` at the top level of
746
764
  `config.yml` (env `SAMAGOTCHI_SKIP_AGENT_MD=true`).
@@ -749,7 +767,8 @@ To skip loading AGENT.md, set `skip_agent_md: true` at the top level of
749
767
 
750
768
  Every setting below takes the three forms described in
751
769
  [Environment variables](#environment-variables): a nested key in
752
- `config.yml`, `SAMAGOTCHI_<DOTTED_NAME>` in the environment and, where the CLI
770
+ `config.yml`, `SAMAGOTCHI_<DOTTED_NAME>` in the environment (not for one
771
+ marked config.yml only) and, where the CLI
753
772
  column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
754
773
  `model_aliases:`, `hooks:`, `guardrails:` rules, `bundles:`, `memories:`) are
755
774
  described in their own sections.
@@ -767,7 +786,7 @@ described in their own sections.
767
786
  | `server.read_timeout` | `600` | yes | Read timeout, seconds. |
768
787
  | `server.first_token_timeout` | 120 remote, off local | | Seconds to the first token; `0` = off. `hosts.<name>.first_token_timeout` wins. |
769
788
  | `recap.enabled` | on | | `false` (or `recap: false`) turns the idle recap off. |
770
- | `recap.model` | session's | yes | Model that writes the recap: an id, alias or `host:model` (the host then picks where to ask). |
789
+ | `recap.model` | session's | yes | Model that writes the recap: an id, alias or `host:model` (the host then picks where to ask; a bare id goes where a bare `--model` goes). |
771
790
  | `recap.host_ref` | session's | yes | A `hosts:` name to ask (`host:` is accepted too, also a host name, not a model ref). |
772
791
  | `recap.base_url` | none | yes | An OpenAI API base to ask instead (`http://h:8081/v1`). |
773
792
  | `recap.inactivity` | `180` | yes | Idle seconds before a recap. |
@@ -785,8 +804,9 @@ described in their own sections.
785
804
  | `image.max_side` | `1568` | | See "Images". |
786
805
  | `image.max_bytes` | `3750000` | | See "Images". |
787
806
  | `image.max_per_request` | `20` | | See "Images". |
788
- | `guardrails.enabled` | `true` | | See [Guardrails](guardrails.md). |
789
- | `guardrails.small_models` | `auto` | | Which models get the `models: small` rules: `auto` (32B or less by the size in the name, an MoE's active size), a list of globs, or `[]`. See [Guardrails](guardrails.md#rules-for-some-models). |
807
+ | `guardrails.enabled` | `true` | | `false`: no rules, and hooks' asks are dropped (a deny still applies). config.yml only: a worker unsets any `SAMAGOTCHI_GUARDRAILS_*` it inherits. See [Guardrails](guardrails.md). |
808
+ | `guardrails.small_models` | `auto` | | Which models get the `models: small` rules: `auto` (32B or less by the size in the name, an MoE's active size), a list of globs, or `[]`. config.yml only. See [Guardrails](guardrails.md#rules-for-some-models). |
809
+ | `guardrails.parent_approvals` | `off` | | What `chi answer` lets a parent agent allow on an approval: `off` (deny only) or `once` ("Allow once", never a wider scope). config.yml only: no environment variable (a parent can still pick the config dir with `XDG_CONFIG_HOME`). A convention for an honest parent, not a security boundary. See [Guardrails](guardrails.md#approvals-from-a-parent-agent). |
790
810
  | `log.file` | state dir | yes | See "Debug Log File". |
791
811
  | `log.disable` | `false` | yes | No file logging. |
792
812
  | `log.level` | `info` | yes | `debug`, `info`, `warn`, `error`. |
@@ -817,6 +837,7 @@ described in their own sections.
817
837
  | `execute.truncate_at_bytes` | `65536` | yes | The same for `execute` output. |
818
838
  | `execute.preview_bytes` | `12288` | yes | |
819
839
  | `execute.telemetry_threshold_pct` | `80` | yes | |
840
+ | `execute.timeout_sec` | `30` | yes | Seconds one `execute` command may run before it is stopped. |
820
841
  | `web.port` | `4567` | `--port` | `chi web`'s port. See [CLI](cli.md). |
821
842
  | `web.host` | `127.0.0.1` | yes | `127.0.0.1`, `::1` or `localhost`; `lan` (this machine's private IPv4 address) or one of its IPv4 addresses also opens `chi web` to the network, with an access token (`chi web --new-token` replaces it). Anything else binds `127.0.0.1` with a warning. See [CLI: chi web on your phone](cli.md#chi-web-on-your-phone). |
822
843
  | `web.markdown` | `false` | yes | Render answers as Markdown in `chi web`. |
data/docs/desktop.md CHANGED
@@ -35,8 +35,30 @@ The first row, **New session in <folder>** (⌘0), starts a session with the mes
35
35
  that of the most recently updated live session, else of the newest recent one, else your home folder. It is ticked
36
36
  alone (ticking it clears the sessions and the reverse) and is preselected when no session is live. ⌘⏎ on it beeps: a
37
37
  note needs a session. After the send the panel shows `started <id>…` for 3 s. On its right, in grey, the row names
38
- the model the new session starts on (`chi self --model`: `default.model` from config.yml, or its env override); it
39
- is left out when none is configured.
38
+ the model the new session starts on (`chi self --model`: `default.model` from config.yml, or its env override; an
39
+ alias as the model it names).
40
+
41
+ ### Choosing the model
42
+
43
+ Click the model on the new row, or press **⌘M**, to choose another one for the new session. The chooser takes the
44
+ place of the session list: a search field (focused), then **Default (<model>)** (`Default (small → box:gemma-small)`
45
+ for an alias), your recent picks and every host's
46
+ models and aliases (from [`chi models`](cli.md#listing-the-models)), at most nine at a time.
47
+
48
+ - Type to search: every word must match the host or the id; names starting with what you typed rank first. A name
49
+ no host lists is offered at the bottom as "not listed" and sent as typed (a host that is down, a model just added).
50
+ - ↑/↓ and ⏎, a click, or ⌘1…⌘9 pick; Esc (or ⌘M) closes the chooser only, a second Esc the panel. While an input
51
+ method is composing, ⏎, the arrows and Esc are its own.
52
+ - The row then shows the pick in the accent colour, and ⏎ sends `chi send --new --model <name>`, the name exactly as
53
+ the chooser shows it; Default sends no `--model`. An existing session keeps its model (switch it there with `/model`).
54
+ - The pick is remembered for the next open (and the last five in the recent list), for every folder. A remembered
55
+ model no host lists any more falls back to the default, with a note in the panel.
56
+ - The list is fetched again at each open; the previous one shows meanwhile ("Loading models…" the first time after
57
+ the helper starts). Hosts that failed or took over 4 s are named under the list; the default and the recent picks
58
+ still work.
59
+
60
+ The chooser needs a chi with `chi models`: an older helper works with a newer chi, not the reverse
61
+ (`chi desktop upgrade` after updating chi).
40
62
 
41
63
  A session open in a `chi --no-shared` REPL isn't listed: it takes no notes or messages.
42
64
 
@@ -83,8 +105,9 @@ off until you set it up:
83
105
  `kitty.agents` picks the programs listed (default `claude|codex|gemini|aider|opencode|cursor-agent|amp|goose`, a
84
106
  YAML list works too; `"*"` lists every window, shells too), `kitty.binary` the kitty to run (default
85
107
  `/Applications/kitty.app/Contents/MacOS/kitty`). See [Configuration](configuration.md#desktop-helper-agents-in-kitty).
86
- 3. Run `chi desktop upgrade` (or `chi update`): the helper reads these from its launch file, so **after every edit of
87
- `kitty:`** run it again. No rebuild when only the settings changed.
108
+ 3. Run `chi update` (or `chi desktop upgrade`): the helper reads these from its launch file, so **after every edit of
109
+ `kitty:`** run it again. `chi update` only rewrites the launch file when only the settings changed (no rebuild);
110
+ `chi desktop upgrade` always rebuilds.
88
111
 
89
112
  The helper finds the sockets itself: kitty adds `-<pid>` to a `listen_on` path from kitty.conf (and fills in
90
113
  `{kitty_pid}`), `~` and variables the helper knows are expanded, others (`${KITTY_PID}`) stay as written, as kitty
@@ -166,12 +189,15 @@ using it. `status` prints the baked dirs.
166
189
  The helper uses only these commands, so it could ship on its own later:
167
190
 
168
191
  - `chi sessions list --live --scope=all --format json` and `chi sessions list --limit 20 --scope=all --format json` →
169
- `[{id, short_id, desc, cwd, project, updated_at, live, busy, owner, recap}]` (it uses `id`, `desc`, `cwd`, `busy`, `updated_at`;
192
+ `[{id, short_id, desc, cwd, project, updated_at, live, busy, owner, recap, parent_id, archived, scratch, ctx_pct}]` (it uses `id`, `desc`, `cwd`, `busy`, `updated_at`;
170
193
  "recent" = rows of the second call that aren't live and have `owner: null`; only UUID-shaped ids go on to chi).
171
- - `chi send [-m LINE] ID...` with the text on stdin (or none) and `chi note --source NAME ID...` with the text on
172
- stdin → one line per session on stdout; exit 0 means all sent or queued, 1 means some were refused or failed.
194
+ - `chi send [-m LINE] [--image PATH]... ID...` and `chi send --new --dir DIR [--model NAME] [-m LINE] [--image PATH]...`
195
+ with the text on stdin (or none), and `chi note [--source NAME] ID...` with the text on stdin → one line per session
196
+ on stdout; exit 0 means all sent or queued, 1 means some were refused or failed.
197
+ - `chi self --model` → the model a new session starts on (the panel's hint).
198
+ - `chi models --format json` → the models the hosts offer (the new-session model picker).
173
199
 
174
- Each call is stopped after 10 s. A stopped `chi note` says the note may be partly delivered.
200
+ Each call is stopped after 10 s, a send with images after 30 s. A stopped `chi note` says the note may be partly delivered.
175
201
 
176
202
  ## Troubleshooting
177
203