samagotchi 0.50.0 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +89 -1
- data/README.md +1 -1
- data/bin/chi +11 -3
- data/docs/architecture.md +1 -1
- data/docs/cli.md +40 -11
- data/docs/configuration.md +63 -21
- data/docs/hooks.md +3 -2
- data/docs/internals/engine.md +4 -2
- data/docs/memory.md +11 -0
- data/docs/plugins.md +2 -1
- data/docs/sessions.md +10 -5
- data/docs/testing.md +6 -1
- data/lib/samagotchi/bootstrap/config_writer.rb +6 -37
- data/lib/samagotchi/bridge/ring_buffer.rb +0 -5
- data/lib/samagotchi/bridge/turn_accumulator.rb +3 -1
- data/lib/samagotchi/bridge.rb +29 -17
- data/lib/samagotchi/bridge_client.rb +3 -1
- data/lib/samagotchi/broadcast/recipients.rb +1 -2
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +1 -1
- data/lib/samagotchi/bundles/system/manifest.yml +2 -2
- data/lib/samagotchi/children_status.rb +1 -1
- data/lib/samagotchi/commands/registry.rb +2 -1
- data/lib/samagotchi/config.rb +50 -182
- data/lib/samagotchi/config_text_edit.rb +8 -32
- data/lib/samagotchi/context_sources.rb +0 -7
- data/lib/samagotchi/context_window.rb +2 -2
- data/lib/samagotchi/continue_offer.rb +5 -4
- data/lib/samagotchi/cut_policy.rb +0 -1
- data/lib/samagotchi/delivery.rb +56 -0
- data/lib/samagotchi/engine.rb +48 -17
- data/lib/samagotchi/engine_peers.rb +1 -0
- data/lib/samagotchi/host_config.rb +183 -0
- data/lib/samagotchi/host_registry.rb +21 -13
- data/lib/samagotchi/kernel_loop.rb +28 -2
- data/lib/samagotchi/last_turn.rb +48 -0
- data/lib/samagotchi/live_versions.rb +0 -6
- data/lib/samagotchi/llm/native_tool_normalizer.rb +2 -7
- data/lib/samagotchi/llm_context_override.rb +3 -22
- data/lib/samagotchi/llm_context_strategy.rb +50 -16
- data/lib/samagotchi/memory_bundle/placeholder.rb +0 -5
- data/lib/samagotchi/model_profile.rb +4 -4
- data/lib/samagotchi/model_settings.rb +46 -0
- data/lib/samagotchi/plugin/context.rb +3 -3
- data/lib/samagotchi/plugin/sessions.rb +2 -2
- data/lib/samagotchi/queued_command.rb +35 -0
- data/lib/samagotchi/reminder_store.rb +0 -27
- data/lib/samagotchi/reply_wait.rb +11 -13
- data/lib/samagotchi/sampling_settings.rb +4 -4
- data/lib/samagotchi/self_report.rb +8 -5
- data/lib/samagotchi/send_command.rb +92 -21
- data/lib/samagotchi/session.rb +45 -24
- data/lib/samagotchi/session_commands.rb +41 -3
- data/lib/samagotchi/session_inbox.rb +36 -7
- data/lib/samagotchi/session_manager.rb +21 -10
- data/lib/samagotchi/session_metrics.rb +25 -6
- data/lib/samagotchi/session_setup.rb +30 -0
- data/lib/samagotchi/sessions_command.rb +1 -1
- data/lib/samagotchi/steer_cut.rb +21 -10
- data/lib/samagotchi/terminal_ui/attach_launcher.rb +10 -4
- data/lib/samagotchi/terminal_ui/attached_loop.rb +48 -1
- data/lib/samagotchi/terminal_ui/formatting.rb +69 -4
- data/lib/samagotchi/terminal_ui.rb +56 -8
- data/lib/samagotchi/thinking.rb +67 -7
- data/lib/samagotchi/thinking_command.rb +72 -0
- data/lib/samagotchi/tool_activity.rb +3 -0
- data/lib/samagotchi/tool_runner.rb +2 -0
- data/lib/samagotchi/tools/builtin_calls.rb +33 -0
- data/lib/samagotchi/tools/builtins.rb +7 -2
- data/lib/samagotchi/tools/peers.rb +3 -2
- data/lib/samagotchi/tools/task_runtime.rb +1 -1
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/vision_support.rb +4 -3
- data/lib/samagotchi/web/app.rb +59 -7
- data/lib/samagotchi/web/message_parts.rb +3 -0
- data/lib/samagotchi/web/public/activity.js +6 -0
- data/lib/samagotchi/web/public/app.js +199 -48
- data/lib/samagotchi/web/public/data.js +8 -4
- data/lib/samagotchi/web/public/format.js +8 -4
- data/lib/samagotchi/web/public/index.html +25 -20
- data/lib/samagotchi/web/public/kept_commands.js +52 -0
- data/lib/samagotchi/web/public/model_pick.js +3 -1
- data/lib/samagotchi/web/public/session_title.js +26 -0
- data/lib/samagotchi/web/public/strip.js +4 -6
- data/lib/samagotchi/web/public/thinking_chip.js +169 -0
- data/lib/samagotchi/web/public/timing.js +15 -0
- data/lib/samagotchi/web/public/turn_events.js +26 -2
- data/lib/samagotchi/web/public/turn_html.js +3 -1
- data/lib/samagotchi/web/public/turn_view.js +328 -310
- data/lib/samagotchi/web/public/vendor/lit-html.js +8 -0
- data/lib/samagotchi/web/session_summary.rb +1 -1
- data/lib/samagotchi/worker.rb +44 -43
- data/lib/samagotchi/worker_inbound.rb +10 -7
- data/lib/samagotchi/worker_wakes.rb +0 -2
- data/lib/samagotchi/yaml_lines.rb +72 -0
- data/lib/samagotchi.rb +1 -0
- metadata +13 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 98660ef911df1f010a45ded3190d0017793fc4e01c1229860ab523a7d2825ffd
|
|
4
|
+
data.tar.gz: 2c1eab9152f8fba6efa7507d17dde3deb54a0e5bbd26a56a61f23f54d3d38a15
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d85715550f95d875ced247c2642c04183507fbb2d85bf6f1201e62a3fb2471ba27c27a110dee57046a716a38e139acd8aaf5dbc7ea0ded2051c486056bb115fb
|
|
7
|
+
data.tar.gz: 25591f45d69afe94d02ebabfce8887cfb126bf0c1b21ed74cc172ecbcfe881990f3431f43cde1b5b2c8801684102f628afbf4446bc2acab77fc50ef2ca6ff7f7
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,92 @@ and commands may change between minor versions. How releases are made:
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.52.0] - 2026-10-10
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `/thinking` shows the thinking level the next turn runs at and where it came from; `/thinking off|low|medium|high`
|
|
16
|
+
sets the session's own level (saved with the session, first in the order), `/thinking default` unsets it. A change
|
|
17
|
+
waits for the turn's end, and the reply says what it costs the prompt cache. `/model` and `/stats` show
|
|
18
|
+
`low (session)`; a continue and a plugin's fork keep the level, a delegate starts without it.
|
|
19
|
+
- Web: a `think` chip in the session bar and on the start page shows the thinking level (`think low · session`); a
|
|
20
|
+
click opens a level select that runs `/thinking` (the start page's choice is for the next new chat only).
|
|
21
|
+
- `chi send --new --thinking LEVEL` starts the session with its own thinking level (refused beside `--continues`,
|
|
22
|
+
which takes the previous link's).
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- `chi --thinking LEVEL` on a run that starts or resumes a session sets that session's own level (saved, above
|
|
27
|
+
`SAMAGOTCHI_THINKING_LEVEL`) instead of the process's; `default` unsets it. `chi web --thinking` stays a default for
|
|
28
|
+
the workers it spawns, below a session's own.
|
|
29
|
+
- `llm_context.budget_tokens` (and a model's or host's `llm_context_budget_tokens`) takes `64k` and `off` like
|
|
30
|
+
`/llm-context budget`, with the same range (4k to 10M); a budget out of range warns and is ignored.
|
|
31
|
+
- Web: a turn's step is one lit-html template for the live page and a reload (lit-html 3.3.3 vendored in
|
|
32
|
+
`web/public/vendor/`, no build step). A live step no longer keeps an empty narration element, and a plain answer's
|
|
33
|
+
thinking block is drawn fresh from the same template.
|
|
34
|
+
|
|
35
|
+
### Fixed
|
|
36
|
+
|
|
37
|
+
- Web: the info bar's session clock ticks during a session's first turn (a session started from the start page
|
|
38
|
+
showed a frozen `session 0ms`).
|
|
39
|
+
- Web: `/cut TEXT` in the composer sends TEXT as a cut, as in a terminal and `chi send --cut` (it was refused as "not a
|
|
40
|
+
session command"); with no turn running it is TEXT as a normal turn. `/queue TEXT` during a turn says it isn't
|
|
41
|
+
available in the web yet, instead of that error.
|
|
42
|
+
- Web: command bubbles (`/help`, `/model`, `/llm-context` …) stay in place when a `!cmd` or `!rollback` redraws the
|
|
43
|
+
conversation; a reload still drops them (only a `!cmd`'s output is saved).
|
|
44
|
+
- Web: a reloaded turn whose messages carry no parts keeps "(called as bash)" on its rows (from the tool records).
|
|
45
|
+
- A turn posted with `delivery: "queue"` (the Bridge's and the web's `POST …/turn`) is refused with 400
|
|
46
|
+
`delivery_unavailable` until queueing is built, instead of being acked as queued and then merged at the running
|
|
47
|
+
turn's next step. No chi client sends it.
|
|
48
|
+
- An attached terminal runs `/queue TEXT` at the open prompt (no turn running) as a turn, as the plain REPL and the
|
|
49
|
+
docs say, instead of refusing it; it is refused only while a turn runs.
|
|
50
|
+
- A `hosts:` entry that fails while it is read is ignored on its own with a warning naming it, instead of every host
|
|
51
|
+
silently dropping (in the process and in its workers); a failure outside the entries is logged.
|
|
52
|
+
- A `/thinking`, `/model` or `/llm-context` queued during a turn no longer lets the turn-end warm-up prefill a prompt
|
|
53
|
+
the change then throws away.
|
|
54
|
+
|
|
55
|
+
## [0.51.0] - 2026-10-10
|
|
56
|
+
|
|
57
|
+
### Added
|
|
58
|
+
|
|
59
|
+
- `/cut TEXT` in a terminal (plain REPL and attached) and `chi send --cut` ask the running turn for a cut: a
|
|
60
|
+
generation that has streamed only thinking for `steer.cut_after` seconds is cut (now, or once its thinking passes
|
|
61
|
+
that) and the step starts again with the message. The terminal's dim line and `chi send`'s line say what happened
|
|
62
|
+
(`cut in now`, `cuts in once the thinking passes 20 s`, `cutting is off; goes in at the next step`); `--format json`
|
|
63
|
+
adds `delivery` and `cut`. The Bridge's 202 for a turn names its `delivery` and, for a cut, the `cut` outcome.
|
|
64
|
+
- `/queue TEXT` in the plain REPL runs TEXT as a turn of its own after the running one.
|
|
65
|
+
- A tool call named `bash` (any case) with `execute`'s arguments runs as `execute`; the logs keep the model's
|
|
66
|
+
own spelling, and the tool row says "(called as bash)" (TUI and web, live and after a reload). `/stats` and
|
|
67
|
+
`chi sessions stats` count such calls by that spelling (`called as: bash=N`, `tool_calls_aliased` in JSON).
|
|
68
|
+
|
|
69
|
+
### Changed
|
|
70
|
+
|
|
71
|
+
- A message sent during a running turn (typed in a terminal, from the web, `chi send`, a parent agent) goes in at the
|
|
72
|
+
turn's next step and no longer cuts the model's thinking; only a message sent as a cut does. A line typed mid-turn
|
|
73
|
+
says `(goes in at the next step)`, and `chi send` says `sent (goes in at the running turn's next step)`.
|
|
74
|
+
- A call for a tool that doesn't exist no longer gets its own wrong name quoted back (seeing it again made the
|
|
75
|
+
model call it again): shell-like names (`bash`, `shell`, …) get `Error: no such tool. Shell commands run with
|
|
76
|
+
execute (same arguments).` when `execute` is offered this turn, every other name the available-tools list; the
|
|
77
|
+
UI's tool row and the logs still show what the model called.
|
|
78
|
+
- Web: the open session's title shows once: in its strip card, or in the top bar when the strip is hidden or the
|
|
79
|
+
session has no card there (an older session, a folded delegate). The composer footer's first-message line and the
|
|
80
|
+
banner that appeared at 20 % context are gone; the memory names stay in the footer's `mem N` chip and tooltip.
|
|
81
|
+
- Web: the browser tab names the open session (`<first message> · Chi · <project>`), so several chi tabs read apart.
|
|
82
|
+
- Web: the hidden strip's "▾ N sessions" pill counts sessions, the number the "All sessions →" tile shows (it
|
|
83
|
+
counted cards, so a folded family made the two disagree).
|
|
84
|
+
- Web: the composer's "Cancel" reads "Stop turn" and the footer's "stop" reads "stop session", each saying what it
|
|
85
|
+
ends.
|
|
86
|
+
- Web: a toast (the update notice, an archive's Undo) sits above the open session's composer card instead of over
|
|
87
|
+
its footer, so it never covers the footer's controls (a worker badge's Restart); it follows the card as it grows.
|
|
88
|
+
|
|
89
|
+
### Fixed
|
|
90
|
+
|
|
91
|
+
- A bare model id, or an alias with a bare target (`splash: incoai/Qwen3.8-27B-Splash`), in a process that hasn't
|
|
92
|
+
listed the hosts yet (`chi --model splash`, `chi send --new --model splash`, a delegate's worker) goes to the host
|
|
93
|
+
whose saved list (`model_lists.json`, at most a week old) has it, instead of always the default host.
|
|
94
|
+
- `chi sessions stats ID --format json` for a session no worker runs lists its newest saved turn (and that turn's
|
|
95
|
+
tool calls) in `turn_records` / `tool_records`, as a live worker's snapshot does, instead of empty lists.
|
|
96
|
+
|
|
11
97
|
## [0.50.0] - 2026-10-09
|
|
12
98
|
|
|
13
99
|
### Added
|
|
@@ -2301,7 +2387,9 @@ and long-lived sessions.
|
|
|
2301
2387
|
- A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
|
|
2302
2388
|
hotkey panel that send selected text or the clipboard to your sessions.
|
|
2303
2389
|
|
|
2304
|
-
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.
|
|
2390
|
+
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.52.0...HEAD
|
|
2391
|
+
[0.52.0]: https://github.com/dm1try/samagotchi/compare/v0.51.0...v0.52.0
|
|
2392
|
+
[0.51.0]: https://github.com/dm1try/samagotchi/compare/v0.50.0...v0.51.0
|
|
2305
2393
|
[0.50.0]: https://github.com/dm1try/samagotchi/compare/v0.49.0...v0.50.0
|
|
2306
2394
|
[0.49.0]: https://github.com/dm1try/samagotchi/compare/v0.48.0...v0.49.0
|
|
2307
2395
|
[0.48.0]: https://github.com/dm1try/samagotchi/compare/v0.47.0...v0.48.0
|
data/README.md
CHANGED
|
@@ -210,7 +210,7 @@ once, for the session, for the repo, or for the whole rule in the repo.
|
|
|
210
210
|
- [Hooks](docs/hooks.md): plugin hooks and bundle hooks
|
|
211
211
|
- [Plugins](docs/plugins.md): bundle plugins (commands, tools, hooks)
|
|
212
212
|
- [Architecture](docs/architecture.md): Engine, TerminalUI, bridge, web
|
|
213
|
-
- Internals: [Gemma 4 contract](docs/internals/gemma4-contract.md), [context telemetry](docs/internals/context-telemetry.md), [tool output limits](docs/internals/tool-guardrails.md), [background tasks](docs/internals/background-tasks.md)
|
|
213
|
+
- Internals: [Engine](docs/internals/engine.md), [Gemma 4 contract](docs/internals/gemma4-contract.md), [prompt caching](docs/internals/prompt-caching.md), [context telemetry](docs/internals/context-telemetry.md), [LLM context: forget layer](docs/internals/llm-context-forget.md), [replay benchmark](docs/internals/llm-context-bench.md), [live runs](docs/internals/llm-context-live.md), [tool output limits](docs/internals/tool-guardrails.md), [background tasks](docs/internals/background-tasks.md)
|
|
214
214
|
|
|
215
215
|
## Development
|
|
216
216
|
|
data/bin/chi
CHANGED
|
@@ -116,8 +116,9 @@ parser = OptionParser.new do |opts|
|
|
|
116
116
|
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 }
|
|
117
117
|
opts.on("--profile NAME", Samagotchi::ModelProfile::NAMES,
|
|
118
118
|
"Prompt profile for every model in this run (#{Samagotchi::ModelProfile::NAMES.join("|")}) [alias for --model-profile]") { |p| cli_overrides["model.profile"] = p }
|
|
119
|
-
opts.on("--thinking LEVEL", Samagotchi::Thinking::LEVELS.map(&:to_s), "
|
|
120
|
-
"
|
|
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
|
+
"before the model's; default unsets it; /thinking changes it). With chi web: the default for the sessions " \
|
|
121
|
+
"its workers run, below a session's own [env SAMAGOTCHI_THINKING_LEVEL]") { |l| options[:thinking] = l }
|
|
121
122
|
opts.on("--memory NAME", "Preload a memory entry into the system prompt (repeatable; a comma list too)") { |m| (options[:memories] ||= []) << m }
|
|
122
123
|
opts.on("--mute NAME", "Hide a memory from this session: not in the prompt, refused by memory_read (repeatable; a comma list too)") { |m| (options[:muted] ||= []) << m }
|
|
123
124
|
opts.on("--llm-context LAYERS", "This session's own LLM context strategy: none, or layers such as stale,forget (saved in the session, " \
|
|
@@ -198,6 +199,11 @@ if scratch
|
|
|
198
199
|
end
|
|
199
200
|
end
|
|
200
201
|
|
|
202
|
+
# --thinking: chi web's is a default for the workers it spawns (below a
|
|
203
|
+
# session's own level); a session-starting run's is that session's own
|
|
204
|
+
# (TerminalUI, AttachLauncher), not a process setting.
|
|
205
|
+
cli_overrides["thinking.level"] = options[:thinking] if web && options[:thinking]
|
|
206
|
+
|
|
201
207
|
# Apply CLI precedence: CLI > ENV > file. Workers this chi spawns get
|
|
202
208
|
# these through their env (Config.cli_env).
|
|
203
209
|
Samagotchi::Config.reload!(cli_overrides: cli_overrides) unless cli_overrides.empty?
|
|
@@ -333,7 +339,8 @@ if launch == :attached
|
|
|
333
339
|
default_input: !options[:no_default_input],
|
|
334
340
|
memories: options[:memories] || [],
|
|
335
341
|
muted_memories: options[:muted] || [],
|
|
336
|
-
llm_context: options[:llm_context] || {}
|
|
342
|
+
llm_context: options[:llm_context] || {},
|
|
343
|
+
thinking: options[:thinking])
|
|
337
344
|
rescue Samagotchi::TerminalUI::AttachLauncher::Error => e
|
|
338
345
|
warn "Error: #{e.message}"
|
|
339
346
|
exit 1
|
|
@@ -351,6 +358,7 @@ begin
|
|
|
351
358
|
memories: options[:memories] || [],
|
|
352
359
|
muted_memories: options[:muted] || [],
|
|
353
360
|
llm_context: options[:llm_context] || {},
|
|
361
|
+
thinking: options[:thinking],
|
|
354
362
|
non_interactive: options[:non_interactive],
|
|
355
363
|
model_name: options[:model],
|
|
356
364
|
scratch: scratch
|
data/docs/architecture.md
CHANGED
|
@@ -65,7 +65,7 @@ TerminalUI ── delegates ──▶ Engine
|
|
|
65
65
|
```
|
|
66
66
|
|
|
67
67
|
- **`Engine`** (`lib/samagotchi/engine.rb`) owns *all* agent logic and knows nothing
|
|
68
|
-
about the terminal.
|
|
68
|
+
about the terminal ([internals/engine.md](internals/engine.md)).
|
|
69
69
|
- **`TerminalUI`** (`lib/samagotchi/terminal_ui.rb`) owns the REPL and rendering; it
|
|
70
70
|
delegates all core work to an `Engine`.
|
|
71
71
|
- The `on_event:` seam on `run_turn` exposes raw `KernelLoop` events plus the
|
data/docs/cli.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
- `chi sessions list|stop|restart|archive|unarchive|delete|prune|clean|stats` — 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)). `stats ID` prints a session's cost, tokens and progress (a live worker's `GET stats`, else its saved `analytics.json`; `--format json` one object) without running a model turn. A usage error (an unknown subcommand or flag, a flag missing its value, `stop`, `restart` 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
20
|
- `chi context <add|push|ls|show|refresh|rm|mute|unmute> …` — attach live external text to sessions (a command chi runs every so often, or text pushed in); the model gets a short note when it changes and reads it with `context_read`; `chi context add <URL>` attaches a GitHub PR with the github-pr bundle (see [Attached context](context.md))
|
|
21
|
-
- `chi send [-m TEXT] [--image PATH]... (ID|PREFIX)...` — send a message to sessions as if typed there: a turn starts (or a running one
|
|
21
|
+
- `chi send [-m TEXT] [--image PATH]... [--cut] (ID|PREFIX)...` — send a message to sessions as if typed there: a turn starts (or a running one takes it at its next step; `--cut` asks it to cut long thinking for it); 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 (`--new --continues ID|last:ID` the next link of that session's chain, see [Session chains](sessions.md#session-chains)), 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))
|
|
22
22
|
- `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))
|
|
23
23
|
- `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))
|
|
24
24
|
- `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))
|
|
@@ -186,7 +186,7 @@ controls exit behavior (`--non-interactive`); `--resume` composes with both.
|
|
|
186
186
|
| `--no-shared` | Run the plain in-process REPL for this run. |
|
|
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
|
-
| `--thinking LEVEL` |
|
|
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
190
|
| `--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
191
|
| `--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
192
|
| `--mute NAME` | Hide a memory from this session (repeatable; a comma list too): its index line is not in the prompt, `memory_read` refuses it, the identity auto-load skips it, and it is dropped from the preloads (config baseline or `--memory`). A name matches in both scopes (`gh-helper`, `project/gh-helper` and `gh-helper.md` all hide `gh-helper`). Nothing on disk changes. See [Muting a memory](#muting-a-memory). |
|
|
@@ -289,11 +289,13 @@ to it (`session.shared`, default `true`). A worker's session can have any number
|
|
|
289
289
|
of UIs at once: the Web UI and attached terminals (`chi`, `--resume`,
|
|
290
290
|
`--attach`). They all see the same turns as they happen, and any of them can send
|
|
291
291
|
a prompt, also while a turn runs (it merges into that turn as steering: the model
|
|
292
|
-
is told it is a steer and who sent it). Steering waits for the model's next step
|
|
293
|
-
|
|
294
|
-
|
|
292
|
+
is told it is a steer and who sent it). Steering waits for the model's next step
|
|
293
|
+
and cuts nothing. `/cut TEXT` in a terminal or the web composer (or `chi send --cut`) asks for a cut:
|
|
294
|
+
once the model has streamed only thinking for `steer.cut_after` seconds (default
|
|
295
|
+
20), now or while your message waits, that generation is cut and the step starts again with your message, marked
|
|
295
296
|
`↪ cut in for your message` (see
|
|
296
|
-
[configuration.md](configuration.md#
|
|
297
|
+
[configuration.md](configuration.md#messages-during-a-running-turn-steer-and-cut)). The web
|
|
298
|
+
composer has no `/queue` yet (it says so and sends nothing). The
|
|
297
299
|
first answer to an `ask_user_question` wins; the other UIs close their widget.
|
|
298
300
|
An empty answer dismisses the question in every UI. The model is then told
|
|
299
301
|
not to go ahead with what it asked about, or change anything else, and to wait.
|
|
@@ -481,24 +483,51 @@ the same values on its `llm context:` line. The web's info bar shows them as the
|
|
|
481
483
|
through `/llm-context`. See "LLM context: a session's own strategy" in
|
|
482
484
|
[configuration.md](configuration.md).
|
|
483
485
|
|
|
486
|
+
`/thinking` shows the thinking level the next turn runs at and where it came
|
|
487
|
+
from (`session`, `--thinking` / `SAMAGOTCHI_THINKING_LEVEL`, `models: <key>`,
|
|
488
|
+
`hosts.<name>`, `thinking.level`). `/thinking off|low|medium|high` sets the
|
|
489
|
+
session's own level, which comes first; `/thinking default` unsets it. It is
|
|
490
|
+
saved with the session (a `--resume`, a continue and `/model` keep it) and
|
|
491
|
+
applies from the next turn's start; the reply says what the change costs the
|
|
492
|
+
prompt cache: the whole prompt on native Gemma 4 (its `<|think|>` starts the
|
|
493
|
+
prompt), only the tail on native Qwen, and other request fields on a chat host
|
|
494
|
+
(a local llama.cpp or Splash server keeps its cache; a hosted API such as
|
|
495
|
+
OpenRouter → Claude or OpenAI may restart its own). `/thinking` alone answers
|
|
496
|
+
mid-turn too; a change sent while a turn runs waits for the turn's end, and
|
|
497
|
+
skips that turn's warm-up. `/stats` shows the level on its `thinking:` line.
|
|
498
|
+
See "Thinking" in [configuration.md](configuration.md).
|
|
499
|
+
|
|
484
500
|
### Typing during a turn
|
|
485
501
|
|
|
486
502
|
The prompt stays open while a turn runs, in an attached terminal and in the plain
|
|
487
503
|
REPL alike:
|
|
488
504
|
|
|
489
505
|
- A line you submit merges into the running turn at its next step (after the
|
|
490
|
-
current tool call or answer)
|
|
491
|
-
|
|
506
|
+
current tool call or answer): `(goes in at the next step)` at once, then
|
|
507
|
+
`(1 message merged into the running turn)` when it does. It never cuts the
|
|
508
|
+
model's thinking. An answer the model finished just before the merge is printed first.
|
|
492
509
|
A line that comes after the turn's last step runs as the next turn, and so
|
|
493
510
|
does one sent after Ctrl-C: it doesn't merge into the turn being cancelled.
|
|
494
511
|
Reminder turns take merged lines too.
|
|
512
|
+
- `/cut TEXT` asks for a cut: a generation that has streamed only thinking for
|
|
513
|
+
`steer.cut_after` seconds is cut and the step starts again with TEXT. The dim
|
|
514
|
+
line says what happened: `(cut in now)`, `(cuts in once the thinking passes
|
|
515
|
+
20 s)` (it waits for the thinking; the step may end first) or `(cutting is
|
|
516
|
+
off; goes in at the next step)` (`steer.cut_after: 0`, or no generation to
|
|
517
|
+
cut).
|
|
518
|
+
- `/queue TEXT` (plain REPL only) runs TEXT as a turn of its own after this
|
|
519
|
+
one: `(queued: runs after this turn)`. An attached terminal says
|
|
520
|
+
`(/queue isn't available in an attached session yet)` and sends nothing (the web
|
|
521
|
+
composer: `(/queue isn't available in the web yet; …)`).
|
|
522
|
+
At the open prompt (no turn running) `/cut TEXT` and `/queue TEXT` run TEXT
|
|
523
|
+
as a normal prompt.
|
|
495
524
|
- Commands answer, wait or are refused, by what they do:
|
|
496
525
|
- At once, beside the turn: `/stats`, `/recap`, `/help`, `/context`, a
|
|
497
526
|
plugin's anytime command (`/btw`), and the commands that only show
|
|
498
|
-
something: `/models` (with a filter too), and `/model`, `/llm-context
|
|
499
|
-
`/guardrails` alone.
|
|
527
|
+
something: `/models` (with a filter too), and `/model`, `/llm-context`,
|
|
528
|
+
`/thinking` and `/guardrails` alone.
|
|
500
529
|
- After the turn: the ones that change something (`/model X`, `/model clear`,
|
|
501
|
-
`/llm-context strategy …`, `/guardrails revoke N`) and `!cmd`, whose output
|
|
530
|
+
`/llm-context strategy …`, `/thinking LEVEL`, `/guardrails revoke N`) and `!cmd`, whose output
|
|
502
531
|
joins the conversation. `(queued: runs after this turn)` says so at once (the
|
|
503
532
|
web draws the command's bubble dashed, with the same words, until it ran);
|
|
504
533
|
when the turn ends they run in the order sent, a prompt sent before one
|
data/docs/configuration.md
CHANGED
|
@@ -470,13 +470,38 @@ models:
|
|
|
470
470
|
- `default` sends nothing: the provider's or the chat template's own default, which is chi's behaviour without the
|
|
471
471
|
setting. For some hybrid models that default is *no* thinking (DeepSeek V3.1 on OpenRouter); `medium` turns it on.
|
|
472
472
|
`on` isn't a level.
|
|
473
|
-
- Order, first set wins:
|
|
474
|
-
`
|
|
475
|
-
level warns once and counts as unset.
|
|
476
|
-
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
- `/
|
|
473
|
+
- Order, first set wins: the session's own level, then the process default (`chi web --thinking LEVEL`, or
|
|
474
|
+
`SAMAGOTCHI_THINKING_LEVEL`), then the `models:` entry (found the way `profile:` is), then the `hosts:` entry, then
|
|
475
|
+
`thinking.level` in the file, then `default`. Anything else than a level warns once and counts as unset.
|
|
476
|
+
- **A session's own level** (`off`, `low`, `medium`, `high`) is saved in the session (its file's `"thinking"`), so a
|
|
477
|
+
`--resume`, a respawned worker, a continue (the next link of a chain) and `/model` keep it; a plugin's fork copies
|
|
478
|
+
it, a delegate child starts without it (it runs at its model's level). Set it with:
|
|
479
|
+
- `/thinking LEVEL` in the session (`/thinking default` unsets it, `/thinking` alone shows the level and where it
|
|
480
|
+
came from);
|
|
481
|
+
- `chi --thinking LEVEL` on a run that starts or resumes a session (the REPL, `-p`, `--resume`, an attached one);
|
|
482
|
+
- `chi send --new --thinking LEVEL`;
|
|
483
|
+
- the web's `think` chip, in the session bar and on the start page (`POST /api/sessions` takes `"thinking": "low"`;
|
|
484
|
+
anything that isn't a level answers `400 invalid_thinking`).
|
|
485
|
+
- `chi web --thinking LEVEL` is the default for the sessions its workers run, below a session's own. A session started
|
|
486
|
+
with only the env (`SAMAGOTCHI_THINKING_LEVEL=low chi send --new …`) loses the level when a worker without that env
|
|
487
|
+
(`chi web`'s, say) wakes it later: use `chi send --new --thinking low`.
|
|
488
|
+
- A level is fixed for a turn: a change takes effect at the next turn's start. `/thinking LEVEL` sent while a turn
|
|
489
|
+
runs waits for its end (as `/model X` does), and holds off that turn's warm-up. What a change costs the prompt cache,
|
|
490
|
+
which `/thinking` says:
|
|
491
|
+
|
|
492
|
+
| Backend | A level change |
|
|
493
|
+
|---|---|
|
|
494
|
+
| native Qwen (`qwen36`) | only the prompt's tail (the empty thought after the cue) |
|
|
495
|
+
| native Gemma 4 (`gemma4`) | the whole prompt is read again: `<\|think\|>` starts the system prompt |
|
|
496
|
+
| chat host, local llama.cpp or Splash | other request fields; the server keeps its cache (a Gemma template re-reads it all) |
|
|
497
|
+
| chat host, OpenRouter → Claude | thinking params are part of the cached prefix: the cache restarts |
|
|
498
|
+
| chat host, OpenRouter → OpenAI reasoning | an effort change rewrites hidden instructions: the prefix is lost |
|
|
499
|
+
|
|
500
|
+
- `models:` levels and `thinking.level` in the file are read every turn; a host's `thinking:` reaches a worker when
|
|
501
|
+
it starts, as its `sampling:` does (`chi sessions stop` to change it).
|
|
502
|
+
- `/model` shows the level and where it came from (`thinking: off (models: qwen3.6-35b-a3b)`, `thinking: low
|
|
503
|
+
(session)`), `/stats` on its `thinking:` line, `chi self` too (a session's own level first when run from its
|
|
504
|
+
commands).
|
|
480
505
|
- The idle recap and plugins' side questions always ask with thinking off, whatever the level.
|
|
481
506
|
|
|
482
507
|
What each backend gets:
|
|
@@ -594,8 +619,10 @@ to the default host. A host's declared ids (`hosts.<name>.models`, below) count
|
|
|
594
619
|
as listed. Only exact ids count, never a substring: `/model gemma` on
|
|
595
620
|
a box that lists `gemma-4-26b` needs `box:gemma` or the exact id. The lists are
|
|
596
621
|
known only after `/models` ran (nothing is fetched before the first turn), so
|
|
597
|
-
until then an unqualified name
|
|
598
|
-
|
|
622
|
+
until then an unqualified name (or an alias's unqualified target) goes by the
|
|
623
|
+
lists last saved on disk (`model_lists.json`, what `chi models`, `/models` or the
|
|
624
|
+
web last listed, at most a week old) the same way, else to the default host,
|
|
625
|
+
unless a host declares it; use `host:model` to pin one. A **remote** host (an `https` url or a public address; see "Remote or local") keeps its model
|
|
599
626
|
list for 10 minutes (60s for local hosts).
|
|
600
627
|
|
|
601
628
|
**The context window** (what the context status and `ctx=` count against) comes from,
|
|
@@ -763,14 +790,6 @@ no made-up answer is saved.
|
|
|
763
790
|
A generation a plugin cuts while it streams (loop-guard's thinking watch, or any `stop_generation`,
|
|
764
791
|
[hooks.md](hooks.md#watching-the-stream)) uses the same budget: `↻ cut by loop-guard, asking again (1/1)`, and with
|
|
765
792
|
none left the turn ends as cancelled (hook).
|
|
766
|
-
A message for a running turn waits for the next step, unless the model's generation has streamed only thinking for
|
|
767
|
-
`steer.cut_after` seconds (default `20`, env `SAMAGOTCHI_STEER_CUT_AFTER`, `0` = never; counted from its first
|
|
768
|
-
thinking token, so the wait for a busy server doesn't count) and still does: then a message from you (the terminal,
|
|
769
|
-
the web), `chi send` or a parent agent cuts that generation, and the model starts the step again with the message
|
|
770
|
-
(`↪ cut in for your message`). A message that comes earlier cuts once the thinking passes `steer.cut_after`, unless
|
|
771
|
-
the step ends first and the message goes in there. Such a cut spends no `retry.empty_answer` attempt and sends no
|
|
772
|
-
hidden note; only the cut thinking is lost. A generation that has streamed visible text or a tool call is never cut,
|
|
773
|
-
and a plugin's steer (`ctx.steer`, `ctx.sessions.send`) never cuts.
|
|
774
793
|
|
|
775
794
|
Assist-mode UX:
|
|
776
795
|
|
|
@@ -779,6 +798,29 @@ Assist-mode UX:
|
|
|
779
798
|
`stream dropped`), the wait, and the retry of all there will be.
|
|
780
799
|
- If retry attempts are exhausted, the submitted prompt is restored into the input editor so you can edit and resubmit.
|
|
781
800
|
|
|
801
|
+
## Messages during a running turn: steer and cut
|
|
802
|
+
|
|
803
|
+
A message for a running turn goes in at its next step and cuts nothing. A message sent as a cut (`/cut TEXT` in a
|
|
804
|
+
terminal or the web composer, `chi send --cut`) cuts the model's generation once it has streamed only thinking for `steer.cut_after`
|
|
805
|
+
seconds (default `20`, env `SAMAGOTCHI_STEER_CUT_AFTER`, `0` = never; counted from its first thinking token, so the
|
|
806
|
+
wait for a busy server doesn't count) and still does, and the model starts the step again with the message
|
|
807
|
+
(`↪ cut in for your message`). One that comes earlier cuts once the thinking passes `steer.cut_after`, unless the
|
|
808
|
+
step ends first and the message goes in there. A plain message (from the web composer, `chi send` without `--cut`,
|
|
809
|
+
a parent agent) never asks for a cut. Such a cut spends no `retry.empty_answer` attempt and sends no hidden note; only
|
|
810
|
+
the cut thinking is lost. A generation that has streamed visible text or a tool call is never cut, and a plugin's steer
|
|
811
|
+
(`ctx.steer`, `ctx.sessions.send`) never cuts.
|
|
812
|
+
|
|
813
|
+
`/queue TEXT` in the plain REPL runs TEXT as a turn of its own after the running one; the attached terminal and the web
|
|
814
|
+
don't queue yet, and a request that asks the Bridge or the web for `delivery: "queue"` is refused (400
|
|
815
|
+
`delivery_unavailable`).
|
|
816
|
+
|
|
817
|
+
`steer.cut_after` (default `20`, env `SAMAGOTCHI_STEER_CUT_AFTER`):
|
|
818
|
+
|
|
819
|
+
```yaml
|
|
820
|
+
steer:
|
|
821
|
+
cut_after: 20 # seconds of thinking-only output before a cut may land; 0 = never
|
|
822
|
+
```
|
|
823
|
+
|
|
782
824
|
## Server errors
|
|
783
825
|
|
|
784
826
|
An error status or a server's error event fails the turn with the server's
|
|
@@ -959,7 +1001,7 @@ without it: no ids, no tool, the same prompt.
|
|
|
959
1001
|
|
|
960
1002
|
```yaml
|
|
961
1003
|
llm_context:
|
|
962
|
-
budget_tokens:
|
|
1004
|
+
budget_tokens: 64k # optional (64000, 64k; 4k to 10M): the [CONTEXT: …] lines count against 64k, not the window
|
|
963
1005
|
models:
|
|
964
1006
|
deepseek-v4.1-flash:
|
|
965
1007
|
llm_context_strategy: [stale, forget]
|
|
@@ -1120,7 +1162,7 @@ described in their own sections.
|
|
|
1120
1162
|
| `models.<key>.llm_context_apply`, `hosts.<name>.llm_context_apply` | none | | A model's or host's `llm_context.apply`. |
|
|
1121
1163
|
| `llm_context.stale_edits` | `false` | | Opt-in, experimental: `stale` also stubs a read that a later successful edit or write of the file superseded. |
|
|
1122
1164
|
| `llm_context.protect_steps` | `3` | | `stale` never stubs a read of a file edited or written in the last N steps (tool batches; counted across the whole conversation, not only the turn). `0` turns the protection off. Under `forget`, `forget_outputs` refuses an output from the last N steps, and a read of a file edited or written in them unless the forget keeps some of its lines. |
|
|
1123
|
-
| `llm_context.budget_tokens` | none | | A soft context budget in tokens (
|
|
1165
|
+
| `llm_context.budget_tokens` | none | | A soft context budget in tokens (`64000` or `64k`, 4k to 10M, as `/llm-context budget` takes it); off when unset, `0` or `off`; a value out of range warns and is ignored. When set, the context status buckets (`context.status_thresholds`) count against it instead of the window (the smaller of the two), so the `[CONTEXT: …]` lines, `payoff`'s top bucket and, under `forget`, the `forget_outputs` offers come under it. A session's own (`--llm-context-budget`, `/llm-context budget`; `off` there wins over the model's), then `models.<key>.llm_context_budget_tokens` and `hosts.<name>.llm_context_budget_tokens` come first. |
|
|
1124
1166
|
| `models.<key>.llm_context_budget_tokens`, `hosts.<name>.llm_context_budget_tokens` | none | | A model's or host's `llm_context.budget_tokens`. |
|
|
1125
1167
|
| `llm_context.policy` | the subtask-boundaries line | | The sentence `forget_outputs`' description carries (the `forget` layer): "Tidy at subtask boundaries: once a subtask is done, forget its tool outputs and note what it established; keep anything you'll still edit against." Blank: none. Read when a session starts. |
|
|
1126
1168
|
| `context.chars_per_token` | `4.0` | yes | Estimate ratio when the server reports no usage. |
|
|
@@ -1130,7 +1172,7 @@ described in their own sections.
|
|
|
1130
1172
|
| `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). |
|
|
1131
1173
|
| `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`); `false`: updates wait as notes for the next turn. See [Waking](context.md#waking). |
|
|
1132
1174
|
| `thinking.turn_preamble` | `true` | yes | Ask a `qwen36` model to open its thinking with a short `TURN:` line (the step label). |
|
|
1133
|
-
| `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. See "Thinking". |
|
|
1175
|
+
| `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". |
|
|
1134
1176
|
| `models.<key>.thinking`, `hosts.<name>.thinking` | none | | A model's or host's level. See "Thinking". |
|
|
1135
1177
|
| `hosts.<name>.models` | none | | The model ids a host serves whatever its `/v1/models` lists: a map of ids, or a plain list. See "Models a host serves but doesn't list". |
|
|
1136
1178
|
| `hosts.<name>.models.<id>.served` | none | | The models the host may answer that id with (exact ids, any case), or `any`: no served-model warning for those. See "Models a host serves but doesn't list". |
|
|
@@ -1144,7 +1186,7 @@ described in their own sections.
|
|
|
1144
1186
|
| `retry.base_delay` | `0.5` | yes | |
|
|
1145
1187
|
| `retry.max_delay` | `8.0` | yes | |
|
|
1146
1188
|
| `retry.empty_answer` | `1` | | Times a turn asks again after an empty answer (at most 3, `0` = off). See "Llama Network Retry Behavior". |
|
|
1147
|
-
| `steer.cut_after` | `20` | | Seconds a generation must have streamed only thinking before a message
|
|
1189
|
+
| `steer.cut_after` | `20` | | Seconds a generation must have streamed only thinking before a message sent as a cut (`/cut TEXT`, `chi send --cut`) cuts it; other messages go in at the next step; `0` = never. See [Messages during a running turn](#messages-during-a-running-turn-steer-and-cut). |
|
|
1148
1190
|
| `turn.max_iterations` | `100` | | A turn's step limit: model ↔ tool rounds before it stops and asks to continue (an integer ≥ 1). `--no-interrupt` turns get the larger of 1000 and this. See [CLI: Iteration Limit Behavior](cli.md#iteration-limit-behavior). |
|
|
1149
1191
|
| `turn.parent_continue` | `true` | | Whether a parent agent (`chi answer`) may answer Continue to a session's step-limit question; `false`: Stop only. config.yml only: no environment variable. See [Sub-agent](sub-agent.md) and [Guardrails](guardrails.md#approvals-from-a-parent-agent). |
|
|
1150
1192
|
| `update.gem` | `true` | | `false`: `chi update` never installs a newer gem (`--no-gem` for one run). See [CLI: Updating](cli.md#updating). |
|
data/docs/hooks.md
CHANGED
|
@@ -220,8 +220,9 @@ What a cut is, in detail:
|
|
|
220
220
|
chunk). A client that joins the running turn still sees its thinking in
|
|
221
221
|
the turn so far until the turn ends.
|
|
222
222
|
- A Stop from the user right after a cut is a plain cancel.
|
|
223
|
-
- A cut for a message (a
|
|
224
|
-
a generation that has streamed only thinking for `steer.cut_after`
|
|
223
|
+
- A cut for a message (one sent as a cut, `/cut TEXT` or `chi send --cut`,
|
|
224
|
+
into a generation that has streamed only thinking for `steer.cut_after`
|
|
225
|
+
seconds; other messages go in at the next step and never cut)
|
|
225
226
|
is not a plugin's cut: it sends no hidden note and spends no retry; the
|
|
226
227
|
message goes in and the UIs print `↪ cut in for your message`. The
|
|
227
228
|
stream's `generation_completed` says `stopped_by: "steer"`, and
|
data/docs/internals/engine.md
CHANGED
|
@@ -203,7 +203,7 @@ One iteration with tool calls:
|
|
|
203
203
|
title, view}.
|
|
204
204
|
- Settles an ask verdict after `:tool_call_started`, so a UI shows the tool line and then the approval.
|
|
205
205
|
- Dispatches through `KernelLoop#dispatch_tool_call`, or writes the denial text. Unknown tools are answered in
|
|
206
|
-
`KernelLoop#dispatch` ("Error:
|
|
206
|
+
`KernelLoop#dispatch` ("Error: no such tool ...", never repeating the wrong name).
|
|
207
207
|
- For edit and write: diffs the file before and after, and refreshes a memory's index line
|
|
208
208
|
(`MemoryBundle::IndexSync`).
|
|
209
209
|
- Attaches returned images, at most `MAX_IMAGES_PER_RESULT` (4) per result.
|
|
@@ -262,7 +262,9 @@ end
|
|
|
262
262
|
|
|
263
263
|
### GenerationPhase
|
|
264
264
|
|
|
265
|
-
What the running generation streams, fed by the Engine's stream handler: whether a
|
|
265
|
+
What the running generation streams, fed by the Engine's stream handler: whether a steer sent as a cut (delivery
|
|
266
|
+
`cut`: `/cut TEXT`, `chi send --cut`) may cut it. `SteerCut#cut_for_steer` answers `:now`, `:waits` or `:off`; the
|
|
267
|
+
Bridge acks it as `cut`.
|
|
266
268
|
A generation is cuttable while it has streamed only thinking, is still thinking, and has thought for at least
|
|
267
269
|
`steer.cut_after` seconds (`cuttable?(min_age)`). It has `started!`, `chunk!`, `retrying!` and `finished!`.
|
|
268
270
|
|
data/docs/memory.md
CHANGED
|
@@ -250,6 +250,17 @@ for a file another bundle installed.
|
|
|
250
250
|
installed bundle owns is left out with a line (`Left out identity.md:
|
|
251
251
|
installed by bundle samagotchi-system (name it to include it)`); naming it
|
|
252
252
|
in `FILES...` includes it.
|
|
253
|
+
`--name NAME`, `--version VER` and `--description DESC` go into the
|
|
254
|
+
bundle's manifest, and `--out PATH` picks where it goes (`.zip`, `.tar.gz`,
|
|
255
|
+
`.tgz` or `.tar` gives that archive, any other path a directory; default
|
|
256
|
+
`<name>.zip`).
|
|
257
|
+
- **An upgrade that meets your edits.** `chi bundle upgrade` updates a file
|
|
258
|
+
you didn't edit and keeps one you did (a conflict), then asks on a
|
|
259
|
+
terminal whether to launch a chi session that merges the two (`/exit`
|
|
260
|
+
when done). `--agent` launches it without asking, `--no-agent` never does;
|
|
261
|
+
with neither and no terminal it keeps your edits and says so. Kept edits
|
|
262
|
+
exit 2; `chi bundle diff NAME FILE` shows the base, and `--force` takes
|
|
263
|
+
the bundle's version.
|
|
253
264
|
|
|
254
265
|
## Bundle profiles: core and dev
|
|
255
266
|
|
data/docs/plugins.md
CHANGED
|
@@ -140,7 +140,8 @@ end
|
|
|
140
140
|
|
|
141
141
|
`/help` (itself an anytime command) lists every command the session knows:
|
|
142
142
|
chi's own, each bundle's with the bundle's name, and the terminal UIs' own
|
|
143
|
-
(`/stats`, `/exit`, `/detach` …), marked
|
|
143
|
+
(`/stats`, `/exit`, `/detach` …), marked where they run: `terminal only`,
|
|
144
|
+
`attached only`, `repl only`, or `terminal and web only` (`/cut`).
|
|
144
145
|
It works in all three UIs.
|
|
145
146
|
|
|
146
147
|
### `chi.tool(name, description, params:, schema:, label:, preview:, targets:) { |args, ctx| … }`
|
data/docs/sessions.md
CHANGED
|
@@ -43,7 +43,7 @@ chi sessions clean [--dry-run] [--days N] # test sessions: all, or o
|
|
|
43
43
|
chi sessions stats ID [--format text|json] # cost, tokens and progress, without a model turn
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
`chi sessions stats ID` prints what `/stats` prints in a terminal, headed by `<id8> <status> <model>`: a live worker answers its Bridge's `GET stats` (no model call, and the command never starts a worker); without one, or when the worker does not answer in time, the same snapshot is rebuilt from the session's saved `analytics.json` (and the line says so when a worker was live). `--format json` prints one object `{session_id, status, live, metrics}` with the snapshot as `metrics
|
|
46
|
+
`chi sessions stats ID` prints what `/stats` prints in a terminal, headed by `<id8> <status> <model>`: a live worker answers its Bridge's `GET stats` (no model call, and the command never starts a worker); without one, or when the worker does not answer in time, the same snapshot is rebuilt from the session's saved `analytics.json` (and the line says so when a worker was live). `--format json` prints one object `{session_id, status, live, metrics}` with the snapshot as `metrics` (its `turn_records` / `tool_records` are the recent ones only: a live worker's newest finished turn and any not yet saved, the disk snapshot's newest saved turn; `analytics.json` holds them all); a session with no `analytics.json` yet reports `metrics: null` (`(no metrics yet)` in text) and still exits 0. An unknown or ambiguous id exits 1 with the same wording `chi sessions stop` uses.
|
|
47
47
|
|
|
48
48
|
A session whose worker waits for an answer (a question, a hook's question, a guardrail approval, or the step-limit question of a turn that ran out of steps, which waits between turns) reads `waiting` in the status column, and json has `waiting: "question"|"approval"|"hook"|"continue"` and `waiting_id`, the question's id for `chi answer --question` (both null otherwise). A delegate's approval its parent relayed to its own card (see *Delegating*) reads `waiting (in parent ab12)`, json `relayed_to: "ab12…"` (the parent's short id, else null). Only while a worker runs it: a question a dead worker left in the file shows the saved status. Answer it with `chi answer`, the web or `chi --attach ID`; `chi sessions stop ID` frees the worker instead.
|
|
49
49
|
|
|
@@ -149,20 +149,21 @@ cancel during its thinking, it said it had never been asked.
|
|
|
149
149
|
`chi send` is the other half of `chi note`: the text goes in as your message, the same as typing it in the attached terminal or the web composer, so a turn runs.
|
|
150
150
|
|
|
151
151
|
```sh
|
|
152
|
-
chi send [-m TEXT] [--image PATH]... (ID|PREFIX)...
|
|
152
|
+
chi send [-m TEXT] [--image PATH]... [--cut] (ID|PREFIX)...
|
|
153
153
|
chi send -m "is this the same bug?" 3fa2 # a message
|
|
154
|
+
chi send --cut -m "stop, wrong file" 3fa2 # cut the running turn's long thinking
|
|
154
155
|
chi send --image shot.png -m "why is this red?" 3fa2 # with an image
|
|
155
156
|
pbpaste | chi send -m "is this the same bug?" 3fa2 # the clipboard quoted above the message
|
|
156
157
|
pbpaste | chi send 3fa2 # the clipboard is the message
|
|
157
158
|
```
|
|
158
159
|
|
|
159
160
|
- With both stdin and `-m`, stdin is context: each line becomes a `> ` quote (as web annotations quote a selection), then a blank line, then the message. With only one, it goes in as is. A terminal on stdin is ignored. Both empty is a usage error; over 16 KiB is refused, never cut.
|
|
160
|
-
- It goes through the same path as the web composer (`SessionManager.deliver_turn`): the worker's Bridge when it is up, so every attached UI shows it as your message (client id `cli:send`, shown like any user message); the input file when the worker is on its way out. During a running turn it is merged into that turn at the next step, like a message typed then.
|
|
161
|
+
- It goes through the same path as the web composer (`SessionManager.deliver_turn`): the worker's Bridge when it is up, so every attached UI shows it as your message (client id `cli:send`, shown like any user message); the input file when the worker is on its way out. During a running turn it is merged into that turn at the next step, like a message typed then, and cuts nothing. `--cut` asks for a cut, as `/cut TEXT` in a terminal does: a generation that has streamed only thinking for `steer.cut_after` seconds is cut (now, or once its thinking passes that), and the step starts again with the message. To an idle session `--cut` sends normally. `--cut --wait` into a running turn is refused like any `--wait` there (`busy: a turn is running`).
|
|
161
162
|
- A session with no worker gets one started (like `--attach` or the web composer). A session open in a plain REPL (`--no-shared`) refuses it. Only sessions on this machine: Bridges listen on 127.0.0.1.
|
|
162
163
|
- Fire and forget: it returns once the message is queued and never prints the answer; that shows in whatever is attached. A guardrail "ask" waits for a UI to answer it, so with nothing attached the turn stalls there until one attaches (`chi --attach ID`).
|
|
163
164
|
- `--image PATH` (repeatable, up to 20) sends images with the message, as the web composer's chips do. Each file is read once before anything is sent (bmp, tiff and heic converted to png, large ones downscaled, as for `@path`); a missing file or one that isn't an image stops the send (exit 1; with `--wait --format json`, an `error` line) and nothing is sent; more than 20 is a usage error (exit 2). Each session gets its own copy in `<session>/images/`. Text is still required (`-m` or stdin; context alone counts), also with `--wait`. The session's model must see images: one known not to (the host's model list says text-only, `vision: false` in `models:` or `hosts:`, a native llama.cpp without a vision model) is refused before anything is sent, with the reason (`<id> refused: gemma can't take images (…); send text only or switch the model (/model)`); the other sessions of the same send still get it, and the exit is 1. With `--new` nothing is started. When chi can't tell (a native host whose `/props` doesn't answer, a list that doesn't say), the message is sent as before and a text-only model fails the turn in the session. A running turn doesn't take images mid-turn: the message runs as the next turn, and the line says `(runs after the current turn)`. With `--new` the session starts idle with the message as its preview, then the message goes in as its first turn once its worker is up (`<id> started with 1 image`); if the worker doesn't come up in 5 s the session is kept, with its id on the `failed:` line.
|
|
164
165
|
- A session command runs as the command, as typed in a terminal: `chi send -m "/model qwen" 3fa2` switches the model (`sent as a session command`), and the model never sees the line. What counts is what the session's worker knows (the built-ins, `!cmd`, its bundles' commands); an unknown `/word` (`/usr/bin/env is missing`) goes in as a message. A UI-only command (`/stats`, `/exit`, `/recap`, …) is refused instead: the worker doesn't run it, so the line would reach the model as a prompt (`<id> refused: /stats is a terminal and web command; use chi sessions stats <id8>`; exit 1, and nothing is sent). With `--new` a line starting with `/` or `!` starts the session idle and goes to its worker once it is up, which decides. With `--wait` a command has no reply to wait for: exit 0 at once, `<id> sent as a session command: no reply to wait for …` on stderr (`{"status":"command",…,"detail":…}` with `--format json`); its output shows where the session is open.
|
|
165
|
-
- One line per session: `sent`, `sent as a session command`, `sent with 2 images`, `sent (the running turn
|
|
166
|
+
- One line per session: `sent`, `sent as a session command`, `sent with 2 images`, `sent (goes in at the running turn's next step)`, with `--cut` `sent (cut in now)`, `sent (cuts in once the thinking passes 20 s)` or `sent (cutting is off; goes in at the next step)`, `sent (started its worker)`, `refused: …` or `failed: …`. Exit 0 when all were sent, 1 when any was refused, failed or not found, 2 for a usage error. There is no `--all`.
|
|
166
167
|
|
|
167
168
|
The Automator action above works for messages too: swap its last line for `pbpaste | "$chi" send -m "what do you make of this?" $(print -r -- "$picked" | cut -f1)`.
|
|
168
169
|
|
|
@@ -180,12 +181,16 @@ chi send --new --continues last:3fa2 -m "new day" # the next link of 3fa2's
|
|
|
180
181
|
```
|
|
181
182
|
|
|
182
183
|
- `--new` takes no ids (one new session per call). `--dir DIR` is its folder, and so its project (default the current one); `--model M` its model (default the configured one). A host config.yml doesn't have is refused before anything is spawned (exit 1). A `host:id` whose id that host's last saved list doesn't have (`~/.local/state/samagotchi/model_lists.json`, what `chi models` / `/models` / the web last listed for that host, at most a week old) gets one warning line on stderr, with up to three close ids as a hint (`did you mean: gemma-small?`), and the session starts anyway: some hosts serve ids they don't list (a one-model llama.cpp server takes any name). A saved list older than 10 minutes that misses the id is re-listed once (that one host, capped at 5 s) before the warning: the list can be stale by content, e.g. a one-model server reloaded with another model; a list re-listed that way names the typed id, nothing is said. A list younger than 10 minutes is taken at its word (a host that serves ids it never lists would otherwise pay the re-list on every launch). A re-list that fails, times out or lists nothing falls back to the saved list. An id its host declares under `hosts.<name>.models` (see [configuration](configuration.md#models-a-host-serves-but-doesnt-list)) is never warned about or re-listed. A bare id (no `host:`) isn't checked that way — routing sends it to whichever host lists or declares it — and with no saved list for the host nothing is said; a wrong id then fails the worker's first turn.
|
|
184
|
+
- `--thinking LEVEL` (with `--new`) gives the new session its own thinking level (`off`, `low`, `medium`, `high`;
|
|
185
|
+
`default` gives it none), saved in it and first in the order. A level from the env
|
|
186
|
+
(`SAMAGOTCHI_THINKING_LEVEL=low chi send --new …`) only reaches the worker this call spawns: when the session's
|
|
187
|
+
worker is woken later (by `chi web`, a delivery) without that env, the level is gone. Use `--thinking`.
|
|
183
188
|
- `--new` prints `<full id> started` on stdout. With `--wait` that line (and the `sent` line for an existing session) goes to stderr, so stdout is exactly the answer: the reply of the turn the message started, in full. It is the worker's `output/` file, the same one `delegate` reads; a turn that ends with only tool calls and no text has none.
|
|
184
189
|
- `--wait` takes one session: `--new` or one id. A session with a running turn is refused (`busy: a turn is running; wait or attach`, exit 1): the message would run after it, and its reply would come back as the answer.
|
|
185
190
|
- Exit codes with `--wait`: 0 answered; 3 the turn waits for an answer from you (a question, a hook's question, a guardrail approval, or the step-limit question: see [chi as a sub-agent](sub-agent.md#the-step-limit)), and the session keeps waiting: stderr gets the whole question (`waiting for an answer (approval): …`, its text, the numbered options, whether free text is allowed) and how to answer it (`chi answer ID --question QID --option N`, or `chi --attach ID`, the web); 4 `--timeout S` passed with the turn still running (it goes on; wait again); 1 the turn ended without a reply (canceled, failed or empty), the worker failed or vanished, or the session was stopped; 130 on Ctrl-C, which leaves the turn running (`still running: chi --attach ID`). There is no default timeout.
|
|
186
191
|
- `--format json` (with `--wait`) prints one JSON object on stdout however the wait ends, for a script or a parent agent (see [chi as a sub-agent](sub-agent.md)): `{"status":"answered","session_id":…,"text":…}`; `{"status":"question",…,"question":{"id","kind","header","text","options","multi_select","allow_freeform","approval"},"answer_with":"chi answer ID --question QID --option N"}`, where `kind` is `question` (the model's), `hook` or `approval` (with the tool, command, cwd, rule, reason and offered scopes); `{"status":"running",…,"detail":…}` (exit 4, and on Ctrl-C); and `failed`, `canceled`, `no_answer`, `error`, `worker_gone` or `stopped` with a `detail` line (exit 1); `failed` adds `error_kind` and `retryable`, `canceled` adds `cancel_reason` and `stopped_by`, when known. A failure before the wait (no such session, a busy one) is `error` too. A usage error (exit 2) prints no JSON.
|
|
187
192
|
- `--wait ID` with no message (no `-m`, nothing piped) sends nothing: it waits for the session's next reply and prints and exits as above. A running session is fine (that is the point), so after exit 3, 4 or 130 an agent waits again this way while the user answers in the web or `chi --attach`; the question pending when it starts doesn't count again. An idle session with no worker waits until something wakes one. With `--new`, or two ids, it is a usage error (exit 2).
|
|
188
|
-
- `--continues ID` (with `--new`) starts the next link of that session's chain instead (see *Session chains*): in its folder, on its model and
|
|
193
|
+
- `--continues ID` (with `--new`) starts the next link of that session's chain instead (see *Session chains*): in its folder, on its model, LLM context and thinking level, so `--dir`, `--model`, `--llm-context*` and `--thinking` are refused beside it; `ID` may be a prefix or `last:ID`, the chain's latest link. Without a message the new link starts idle; `--wait` then has nothing to wait for (a usage error). It prints `<full id> started (continues 3fa2c1d0)`, with `; 2 delegates moved` inside the parentheses when open delegates moved to it; a refusal (continued already, naming the next link; an open delegate that can't move; a turn running) is `chi send: refused: …`, exit 1.
|
|
189
194
|
- The 16 KiB cap applies: `git diff | chi send --new …` on a big diff is refused; name the branch in the message instead and let the session read it.
|
|
190
195
|
- A session nobody attaches to stalls at its first question or guardrail ask until someone answers it (`chi answer`, the web, `chi --attach`), and its worker stays up meanwhile: it doesn't idle-exit while a question waits (`chi sessions list` shows it as `waiting`; `chi sessions stop ID` frees it). Otherwise it idle-exits after `session.idle_exit_minutes` like any worker. These are ordinary sessions: delete them like any other.
|
|
191
196
|
|
data/docs/testing.md
CHANGED
|
@@ -15,6 +15,10 @@ bug) or a broken spec: fix it, or disable that line with the reason (`# rubocop:
|
|
|
15
15
|
No spec reads your own setup. The suite points `XDG_CONFIG_HOME` and `XDG_STATE_HOME` at temp folders,
|
|
16
16
|
clears your `SAMAGOTCHI_*` environment, and keeps every spec off the network (WebMock).
|
|
17
17
|
|
|
18
|
+
The web specs run in plain node. One that renders with lit-html (`turn_view.js`, `vendor/lit-html.js`) needs a DOM:
|
|
19
|
+
`import "./dom_shim.js";` as its first import (happy-dom, a devDependency only, never shipped; about 0.3 s per spec
|
|
20
|
+
file that loads it). The shim's `htmlOf(nodes)` and `canon(html)` compare markup without lit's marker comments.
|
|
21
|
+
|
|
18
22
|
## Worktrees
|
|
19
23
|
|
|
20
24
|
Each fix or feature gets its own worktree next to the main checkout:
|
|
@@ -70,7 +74,8 @@ a model that follows tool instructions: a small model may fail some of them.
|
|
|
70
74
|
strategies (freed tokens, wrongly forgotten outputs, one-step re-reads, re-prefilled tokens; for `forget_outputs` a
|
|
71
75
|
model's answers to chi's offer, with its call rate and notes). It is opt-in: the suite
|
|
72
76
|
runs its code only on small synthetic sessions, never on yours. See
|
|
73
|
-
[internals/llm-context-bench.md](internals/llm-context-bench.md).
|
|
77
|
+
[internals/llm-context-bench.md](internals/llm-context-bench.md). `script/llm_context_live.rb` runs chi itself on
|
|
78
|
+
real tasks under each strategy (needs a model; costs tokens): [internals/llm-context-live.md](internals/llm-context-live.md).
|
|
74
79
|
|
|
75
80
|
## The model notes report
|
|
76
81
|
|