samagotchi 0.2.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 +7 -0
- data/CHANGELOG.md +43 -0
- data/LICENSE +21 -0
- data/README.md +126 -0
- data/bin/chi +1140 -0
- data/docs/architecture.md +299 -0
- data/docs/cli.md +490 -0
- data/docs/configuration.md +494 -0
- data/docs/desktop.md +97 -0
- data/docs/guardrails.md +218 -0
- data/docs/hooks.md +309 -0
- data/docs/internals/background-tasks.md +26 -0
- data/docs/internals/context-telemetry.md +36 -0
- data/docs/internals/gemma4-contract.md +23 -0
- data/docs/internals/tool-guardrails.md +45 -0
- data/docs/memory.md +85 -0
- data/docs/plugins.md +819 -0
- data/docs/releasing.md +135 -0
- data/docs/sessions.md +155 -0
- data/lib/samagotchi/bridge/bounded_queue.rb +70 -0
- data/lib/samagotchi/bridge/card_store.rb +126 -0
- data/lib/samagotchi/bridge/event_id.rb +25 -0
- data/lib/samagotchi/bridge/ring_buffer.rb +63 -0
- data/lib/samagotchi/bridge/sse_writer.rb +248 -0
- data/lib/samagotchi/bridge/turn_accumulator.rb +189 -0
- data/lib/samagotchi/bridge.rb +993 -0
- data/lib/samagotchi/bridge_client/event_stream.rb +158 -0
- data/lib/samagotchi/bridge_client/sse_parser.rb +51 -0
- data/lib/samagotchi/bridge_client.rb +330 -0
- data/lib/samagotchi/bundle_needs.rb +97 -0
- data/lib/samagotchi/bundles/btw/manifest.yml +10 -0
- data/lib/samagotchi/bundles/btw/plugin.rb +100 -0
- data/lib/samagotchi/bundles/guardrails/guardrails/rules.yml +82 -0
- data/lib/samagotchi/bundles/guardrails/guardrails.md +14 -0
- data/lib/samagotchi/bundles/guardrails/manifest.yml +8 -0
- data/lib/samagotchi/bundles/known-names/hooks/known_names.rb +210 -0
- data/lib/samagotchi/bundles/known-names/known_names.md +3 -0
- data/lib/samagotchi/bundles/known-names/manifest.yml +14 -0
- data/lib/samagotchi/bundles/loop-guard/manifest.yml +10 -0
- data/lib/samagotchi/bundles/loop-guard/plugin.rb +158 -0
- data/lib/samagotchi/bundles/mcp/manifest.yml +11 -0
- data/lib/samagotchi/bundles/mcp/plugin.rb +631 -0
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +149 -0
- data/lib/samagotchi/bundles/system/delegated.md +10 -0
- data/lib/samagotchi/bundles/system/identity.md +7 -0
- data/lib/samagotchi/bundles/system/manifest.yml +11 -0
- data/lib/samagotchi/bundles/system/memory_guide.md +107 -0
- data/lib/samagotchi/bundles/system/self_map.md +55 -0
- data/lib/samagotchi/cancellation_controller.rb +78 -0
- data/lib/samagotchi/client.rb +429 -0
- data/lib/samagotchi/commands/registry.rb +112 -0
- data/lib/samagotchi/config.rb +910 -0
- data/lib/samagotchi/context_note.rb +77 -0
- data/lib/samagotchi/context_quote.rb +21 -0
- data/lib/samagotchi/context_usage.rb +66 -0
- data/lib/samagotchi/context_window.rb +76 -0
- data/lib/samagotchi/debug_log.rb +110 -0
- data/lib/samagotchi/desktop/macos/App.swift +102 -0
- data/lib/samagotchi/desktop/macos/ChiRunner.swift +201 -0
- data/lib/samagotchi/desktop/macos/Hotkey.swift +42 -0
- data/lib/samagotchi/desktop/macos/Info.plist.erb +42 -0
- data/lib/samagotchi/desktop/macos/Panel.swift +383 -0
- data/lib/samagotchi/desktop/macos.rb +255 -0
- data/lib/samagotchi/desktop.rb +21 -0
- data/lib/samagotchi/desktop_command.rb +143 -0
- data/lib/samagotchi/engine.rb +2807 -0
- data/lib/samagotchi/guardrails/approval.rb +125 -0
- data/lib/samagotchi/guardrails/approvals.rb +177 -0
- data/lib/samagotchi/guardrails/context.rb +71 -0
- data/lib/samagotchi/guardrails/gate.rb +125 -0
- data/lib/samagotchi/guardrails/load_failures.rb +46 -0
- data/lib/samagotchi/guardrails/protected_paths.rb +77 -0
- data/lib/samagotchi/guardrails/rules.rb +199 -0
- data/lib/samagotchi/guardrails/targets.rb +119 -0
- data/lib/samagotchi/guardrails/verdict.rb +134 -0
- data/lib/samagotchi/guardrails.rb +18 -0
- data/lib/samagotchi/hooks/bundle_loader.rb +158 -0
- data/lib/samagotchi/hooks/loader.rb +162 -0
- data/lib/samagotchi/hooks/registry.rb +261 -0
- data/lib/samagotchi/hooks.rb +30 -0
- data/lib/samagotchi/host_registry.rb +315 -0
- data/lib/samagotchi/idle_client.rb +147 -0
- data/lib/samagotchi/idle_recap.rb +549 -0
- data/lib/samagotchi/idle_reminders.rb +101 -0
- data/lib/samagotchi/idle_scheduler.rb +76 -0
- data/lib/samagotchi/image_store.rb +393 -0
- data/lib/samagotchi/installed_gem.rb +38 -0
- data/lib/samagotchi/kernel_loop.rb +1017 -0
- data/lib/samagotchi/launch_mode.rb +34 -0
- data/lib/samagotchi/llm/backend.rb +28 -0
- data/lib/samagotchi/llm/chat_loop.rb +450 -0
- data/lib/samagotchi/llm/errors.rb +329 -0
- data/lib/samagotchi/llm/http.rb +412 -0
- data/lib/samagotchi/llm/model_result.rb +72 -0
- data/lib/samagotchi/llm/native_backend.rb +50 -0
- data/lib/samagotchi/llm/native_tool_normalizer.rb +277 -0
- data/lib/samagotchi/llm/openai_chat.rb +403 -0
- data/lib/samagotchi/llm/usage.rb +79 -0
- data/lib/samagotchi/log.rb +200 -0
- data/lib/samagotchi/log_line.rb +127 -0
- data/lib/samagotchi/log_path.rb +31 -0
- data/lib/samagotchi/log_subscriber.rb +163 -0
- data/lib/samagotchi/memory_bundle/builder.rb +364 -0
- data/lib/samagotchi/memory_bundle/index_updater.rb +123 -0
- data/lib/samagotchi/memory_bundle/installer.rb +528 -0
- data/lib/samagotchi/memory_bundle/listing.rb +72 -0
- data/lib/samagotchi/memory_bundle/manifest.rb +225 -0
- data/lib/samagotchi/memory_bundle/merger.rb +52 -0
- data/lib/samagotchi/memory_bundle/placeholder.rb +37 -0
- data/lib/samagotchi/memory_bundle/provenance.rb +257 -0
- data/lib/samagotchi/memory_bundle/source.rb +153 -0
- data/lib/samagotchi/memory_bundle/status.rb +107 -0
- data/lib/samagotchi/memory_bundle/system_bundle.rb +161 -0
- data/lib/samagotchi/memory_bundle/uninstaller.rb +128 -0
- data/lib/samagotchi/memory_bundle.rb +17 -0
- data/lib/samagotchi/memory_paths.rb +101 -0
- data/lib/samagotchi/model_overlay.rb +53 -0
- data/lib/samagotchi/model_profile.rb +309 -0
- data/lib/samagotchi/muted_memories.rb +66 -0
- data/lib/samagotchi/note_command.rb +163 -0
- data/lib/samagotchi/output_formatter.rb +100 -0
- data/lib/samagotchi/owner_lock.rb +110 -0
- data/lib/samagotchi/pending_input_queue.rb +48 -0
- data/lib/samagotchi/plugin/api.rb +362 -0
- data/lib/samagotchi/plugin/context.rb +193 -0
- data/lib/samagotchi/plugin/loader.rb +126 -0
- data/lib/samagotchi/plugin/service.rb +117 -0
- data/lib/samagotchi/plugin/sessions.rb +150 -0
- data/lib/samagotchi/plugin/side_question.rb +60 -0
- data/lib/samagotchi/plugin/tool_result.rb +24 -0
- data/lib/samagotchi/project_scope.rb +25 -0
- data/lib/samagotchi/prompt.rb +119 -0
- data/lib/samagotchi/prompt_literal_guard.rb +70 -0
- data/lib/samagotchi/recap_store.rb +92 -0
- data/lib/samagotchi/reminder_store.rb +165 -0
- data/lib/samagotchi/self_report.rb +195 -0
- data/lib/samagotchi/send_command.rb +170 -0
- data/lib/samagotchi/served_model.rb +32 -0
- data/lib/samagotchi/session.rb +508 -0
- data/lib/samagotchi/session_commands.rb +527 -0
- data/lib/samagotchi/session_delete_command.rb +105 -0
- data/lib/samagotchi/session_manager.rb +1049 -0
- data/lib/samagotchi/session_metrics.rb +466 -0
- data/lib/samagotchi/session_observer.rb +117 -0
- data/lib/samagotchi/terminal_ui/attach_launcher.rb +118 -0
- data/lib/samagotchi/terminal_ui/attached_loop.rb +1037 -0
- data/lib/samagotchi/terminal_ui/attached_view.rb +264 -0
- data/lib/samagotchi/terminal_ui/event_renderer.rb +192 -0
- data/lib/samagotchi/terminal_ui/formatting.rb +291 -0
- data/lib/samagotchi/terminal_ui/image_input.rb +36 -0
- data/lib/samagotchi/terminal_ui/input_support.rb +324 -0
- data/lib/samagotchi/terminal_ui/legacy_surface.rb +111 -0
- data/lib/samagotchi/terminal_ui/line_reader.rb +113 -0
- data/lib/samagotchi/terminal_ui/live_region.rb +36 -0
- data/lib/samagotchi/terminal_ui/plain_surface.rb +51 -0
- data/lib/samagotchi/terminal_ui/question_prompt.rb +153 -0
- data/lib/samagotchi/terminal_ui/question_slot.rb +131 -0
- data/lib/samagotchi/terminal_ui/reline_seam.rb +216 -0
- data/lib/samagotchi/terminal_ui/repl_input.rb +138 -0
- data/lib/samagotchi/terminal_ui/screen.rb +316 -0
- data/lib/samagotchi/terminal_ui/surface.rb +47 -0
- data/lib/samagotchi/terminal_ui/thinking_line.rb +101 -0
- data/lib/samagotchi/terminal_ui.rb +1992 -0
- data/lib/samagotchi/thinking_ticker.rb +110 -0
- data/lib/samagotchi/thought_stream_splitter.rb +149 -0
- data/lib/samagotchi/token_usage.rb +88 -0
- data/lib/samagotchi/tool_activity.rb +216 -0
- data/lib/samagotchi/tool_call_parser.rb +637 -0
- data/lib/samagotchi/tool_declarations.rb +561 -0
- data/lib/samagotchi/tool_runner.rb +211 -0
- data/lib/samagotchi/tools/args.rb +259 -0
- data/lib/samagotchi/tools/ask_user_question.rb +152 -0
- data/lib/samagotchi/tools/builtins.rb +122 -0
- data/lib/samagotchi/tools/cancel_reminder.rb +21 -0
- data/lib/samagotchi/tools/delegate.rb +167 -0
- data/lib/samagotchi/tools/delegate_result.rb +53 -0
- data/lib/samagotchi/tools/delegate_wait.rb +153 -0
- data/lib/samagotchi/tools/edit.rb +155 -0
- data/lib/samagotchi/tools/execute.rb +214 -0
- data/lib/samagotchi/tools/list_reminders.rb +20 -0
- data/lib/samagotchi/tools/list_sessions.rb +74 -0
- data/lib/samagotchi/tools/memory.rb +256 -0
- data/lib/samagotchi/tools/output_guardrails.rb +93 -0
- data/lib/samagotchi/tools/peers.rb +18 -0
- data/lib/samagotchi/tools/read.rb +182 -0
- data/lib/samagotchi/tools/register_reminder.rb +53 -0
- data/lib/samagotchi/tools/registry.rb +60 -0
- data/lib/samagotchi/tools/send_note.rb +49 -0
- data/lib/samagotchi/tools/task_create.rb +29 -0
- data/lib/samagotchi/tools/task_get.rb +39 -0
- data/lib/samagotchi/tools/task_list.rb +43 -0
- data/lib/samagotchi/tools/task_runtime.rb +311 -0
- data/lib/samagotchi/tools/task_stop.rb +29 -0
- data/lib/samagotchi/tools/task_wait.rb +104 -0
- data/lib/samagotchi/tools/tool_path.rb +18 -0
- data/lib/samagotchi/tools/web_fetch.rb +163 -0
- data/lib/samagotchi/tools/write.rb +26 -0
- data/lib/samagotchi/turn_flow.rb +242 -0
- data/lib/samagotchi/turn_note.rb +76 -0
- data/lib/samagotchi/turn_tally.rb +101 -0
- data/lib/samagotchi/version.rb +7 -0
- data/lib/samagotchi/vision_context.rb +132 -0
- data/lib/samagotchi/vision_support.rb +109 -0
- data/lib/samagotchi/web/app.rb +1349 -0
- data/lib/samagotchi/web/markdown_renderer.rb +107 -0
- data/lib/samagotchi/web/message_parts.rb +169 -0
- data/lib/samagotchi/web/public/activity.js +100 -0
- data/lib/samagotchi/web/public/annotations.js +67 -0
- data/lib/samagotchi/web/public/app.js +2382 -0
- data/lib/samagotchi/web/public/card.js +74 -0
- data/lib/samagotchi/web/public/chat_view.js +360 -0
- data/lib/samagotchi/web/public/chunk_router.js +25 -0
- data/lib/samagotchi/web/public/command_complete.js +39 -0
- data/lib/samagotchi/web/public/composer_size.js +19 -0
- data/lib/samagotchi/web/public/copy.js +142 -0
- data/lib/samagotchi/web/public/ctx.js +35 -0
- data/lib/samagotchi/web/public/data.js +256 -0
- data/lib/samagotchi/web/public/format.js +232 -0
- data/lib/samagotchi/web/public/hold.js +78 -0
- data/lib/samagotchi/web/public/images.js +77 -0
- data/lib/samagotchi/web/public/index.html +568 -0
- data/lib/samagotchi/web/public/init_row.js +60 -0
- data/lib/samagotchi/web/public/model_pick.js +23 -0
- data/lib/samagotchi/web/public/question_card.js +100 -0
- data/lib/samagotchi/web/public/route.js +17 -0
- data/lib/samagotchi/web/public/scope.js +36 -0
- data/lib/samagotchi/web/public/scroll.js +24 -0
- data/lib/samagotchi/web/public/sentences.js +88 -0
- data/lib/samagotchi/web/public/sessions_list.js +60 -0
- data/lib/samagotchi/web/public/strip.js +25 -0
- data/lib/samagotchi/web/public/tally.js +37 -0
- data/lib/samagotchi/web/public/thinking_ticker.js +79 -0
- data/lib/samagotchi/web/public/timing.js +185 -0
- data/lib/samagotchi/web/public/turn_events.js +209 -0
- data/lib/samagotchi/web/public/turn_model.js +204 -0
- data/lib/samagotchi/web/public/turn_view.js +587 -0
- data/lib/samagotchi/web/server.rb +183 -0
- data/lib/samagotchi/web/session_hub.rb +329 -0
- data/lib/samagotchi/web/session_summary.rb +85 -0
- data/lib/samagotchi/worker.rb +635 -0
- data/lib/samagotchi/worker_idle_exit.rb +87 -0
- data/lib/samagotchi.rb +12 -0
- metadata +374 -0
data/docs/cli.md
ADDED
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
# CLI and REPL
|
|
2
|
+
|
|
3
|
+
## Commands
|
|
4
|
+
|
|
5
|
+
- `chi` — start a session in a background worker and attach the terminal to it, so the Web UI (or another terminal) can share it (see [Sharing a session](#sharing-a-session))
|
|
6
|
+
- `chi -p "your prompt"` — run a prompt, then stay attached
|
|
7
|
+
- `chi -p "your prompt" --non-interactive` — run a prompt, print the answer, exit
|
|
8
|
+
- `chi --resume <session-id>` — resume a prior session (in its worker)
|
|
9
|
+
- `chi --no-shared [--resume <session-id>]` — the plain in-process REPL instead, for this run
|
|
10
|
+
- `chi --attach <session-id>` — attach the terminal to a session's worker (e.g. one started from the Web UI), waking one if it has exited
|
|
11
|
+
- A session id can be shortened to any unique prefix (like git): `chi --attach 2ea8`. `--resume`, `--attach`, `sessions stop` and `sessions delete` take one; an ambiguous prefix lists the sessions it matches.
|
|
12
|
+
- `chi web [--port 4567] [--open] [--scope=all]` — start the Web UI (single localhost port session control plane) on this git project's sessions (`--scope=all`, or a folder in no repo: every session); if a chi web already runs on the port, print (with `--open`, open) its page for this folder and exit. Something else on the port (an older chi web too) exits 1 with "port N is in use"
|
|
13
|
+
- `chi web --web-markdown` — opt in to sanitized Markdown rendering for completed assistant messages
|
|
14
|
+
- `chi web --no-web-turn-view` — show turns as the classic row of bubbles instead of the default turn view (each turn as one block of steps, the running one at the bottom); `?view=turn|chat` on the page URL overrides it (see [Web turn view](#web-turn-view))
|
|
15
|
+
- `chi sessions list|stop|delete|prune|clean` — manage persisted sessions; `list` shows this git project's, `list --scope=all` every one, a delegated session with `↳ <parent>` (see [Sessions](sessions.md))
|
|
16
|
+
- `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))
|
|
17
|
+
- `chi send [-m TEXT] (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 (see [Sessions: Sending a message](sessions.md#sending-a-message))
|
|
18
|
+
- `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))
|
|
19
|
+
- `chi self` — print version, source dir (checkout or installed gem), config/memory/session paths, model/host and bundles
|
|
20
|
+
- `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 (see [Guardrails](guardrails.md), [Plugins](plugins.md#the-btw-bundle), [the mcp bundle](plugins.md#the-mcp-bundle) and [the loop-guard bundle](plugins.md#the-loop-guard-bundle))
|
|
21
|
+
|
|
22
|
+
## Flags
|
|
23
|
+
|
|
24
|
+
Samagotchi exposes one flag that feeds a prompt (`-p`, `--prompt`) and one that
|
|
25
|
+
controls exit behavior (`--non-interactive`); `--resume` composes with both.
|
|
26
|
+
|
|
27
|
+
| Flag | Purpose |
|
|
28
|
+
|------|---------|
|
|
29
|
+
| `-p`, `--prompt TEXT` | Feed `TEXT` as the first turn (also prefill-equivalent; `-p` feeds **and** runs). |
|
|
30
|
+
| `--non-interactive` | Run a single turn then exit the REPL (sets a high iteration cap; implies `--no-interrupt`). Harmless no-op when given without `-p`. |
|
|
31
|
+
| `--resume SESSION_ID` | Load a prior session's history instead of creating a fresh one. |
|
|
32
|
+
| `--shared` | Run the session (new, or `--resume`'s) in a background worker and attach to it: the default, and the way to get it when `session.shared` is off. See [Sharing a session](#sharing-a-session). |
|
|
33
|
+
| `--no-shared` | Run the plain in-process REPL for this run. |
|
|
34
|
+
| `--attach SESSION_ID` | Attach to a session's worker, waking one if it has exited. |
|
|
35
|
+
| `--model NAME` | Use this model for the run (overrides the configured default and a resumed session's model). |
|
|
36
|
+
| `--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. |
|
|
37
|
+
| `--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. |
|
|
38
|
+
| `--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). |
|
|
39
|
+
| `--no-interrupt` | Raise the tool-call limit to 1000 iterations for long tasks. |
|
|
40
|
+
| `--no-default-input` | Skip prefilling the first REPL line from `SAMAGOTCHI_DEFAULT_INPUT`. |
|
|
41
|
+
| `-v`, `--verbose` | Log at debug level (raw LLM responses, tool call/result payloads) and print every log record to stderr too. |
|
|
42
|
+
| `--version` | Print `chi <version>` and exit (`chi self` shows it with the paths). |
|
|
43
|
+
|
|
44
|
+
Every setting in the config registry (`lib/samagotchi/config.rb`) that exposes a CLI
|
|
45
|
+
flag also works as `--kebab-case VALUE`, e.g. `--server-host`, `--server-port`,
|
|
46
|
+
`--read-truncate-at-bytes`. `chi --help` lists them all.
|
|
47
|
+
|
|
48
|
+
**Which loop runs.** There is no backend flag: the model's host decides. A host with
|
|
49
|
+
`api: openai` in config.yml is driven through the OpenAI chat API (streamed; a remote
|
|
50
|
+
provider via `url:` and `api_key_env:`); every other host gets chi's own raw-prompt loop. `/model` and `--model host:model` switch hosts, and
|
|
51
|
+
the loop with them. See [Configuration](configuration.md) (`hosts:` and `api:`).
|
|
52
|
+
`--backend`, `SAMAGOTCHI_BACKEND` and a `backend:` key were removed; chi says so if
|
|
53
|
+
it sees one.
|
|
54
|
+
|
|
55
|
+
### Entrypoint scenarios
|
|
56
|
+
|
|
57
|
+
| Command | Behavior |
|
|
58
|
+
|---------|----------|
|
|
59
|
+
| `chi` | Start a fresh session in a worker and attach to it. |
|
|
60
|
+
| `chi -p "refactor this"` | Start a session in a worker, send the prompt, **stay attached**. |
|
|
61
|
+
| `chi -p "refactor this" --non-interactive` | Run one turn in this process, save, **exit** (no REPL, no worker). |
|
|
62
|
+
| `chi --non-interactive` | Harmless no-op exit; no session created, no error. |
|
|
63
|
+
| `chi --resume ID` | Resume session `ID` in a worker (or join the worker already running it) and attach. |
|
|
64
|
+
| `chi --resume ID -p "next step" --non-interactive` | Resume `ID`, run the prompt, save, exit. |
|
|
65
|
+
| `chi --resume ID -p "next step"` | Resume `ID`, send the prompt, **stay attached** to that session. |
|
|
66
|
+
| `chi --no-shared [...]` | The same, in the plain in-process REPL. |
|
|
67
|
+
|
|
68
|
+
Notes:
|
|
69
|
+
|
|
70
|
+
- `-p` always feeds **and** runs the prompt; there is no feed-and-edit variant. To
|
|
71
|
+
prefill (edit, not execute) the first REPL line, use the
|
|
72
|
+
`SAMAGOTCHI_DEFAULT_INPUT` environment variable instead.
|
|
73
|
+
- Prompt history is persisted per session; `--resume` preserves prior messages as
|
|
74
|
+
turn context (a `-p` run on a resumed session never clobbers existing history).
|
|
75
|
+
- Non-interactive runs (`-p` with `--non-interactive`, or bare `--non-interactive`)
|
|
76
|
+
print only the final result output — no spinner, status line, or REPL.
|
|
77
|
+
|
|
78
|
+
### Sharing a session
|
|
79
|
+
|
|
80
|
+
Plain `chi` runs the session in a background worker and attaches the terminal
|
|
81
|
+
to it (`session.shared`, default `true`). A worker's session can have any number
|
|
82
|
+
of UIs at once: the Web UI and attached terminals (`chi`, `--resume`,
|
|
83
|
+
`--attach`). They all see the same turns as they happen, and any of them can send
|
|
84
|
+
a prompt, also while a turn runs (it merges into that turn as steering). The
|
|
85
|
+
first answer to an `ask_user_question` wins; the other UIs close their widget.
|
|
86
|
+
An empty answer dismisses the question in every UI.
|
|
87
|
+
|
|
88
|
+
In an attached terminal:
|
|
89
|
+
|
|
90
|
+
- Ctrl-C cancels the running turn (whoever started it) and leaves what you typed
|
|
91
|
+
in the prompt. At an idle prompt it clears the line; a second Ctrl-C within
|
|
92
|
+
2 s, Ctrl-D or `/detach` detaches. The worker keeps running; the detach line
|
|
93
|
+
prints `chi --attach ID` to come back.
|
|
94
|
+
- `/exit` (also `/quit`, `exit`) detaches and asks the worker to exit now, so it
|
|
95
|
+
doesn't wait out the idle timeout. It stays up while something still needs
|
|
96
|
+
it, and the detach line says what: a turn is running (Ctrl-C cancels it
|
|
97
|
+
first), prompts are queued, a continue offer is pending, another UI is
|
|
98
|
+
attached, or reminders are set. A web tab you just closed can count as
|
|
99
|
+
attached for about 30 s. When the worker exits, `chi --resume ID` or
|
|
100
|
+
`chi --attach ID` starts a new one with the conversation. Before it exits
|
|
101
|
+
(here and on the idle exit) the worker writes the session's recap if
|
|
102
|
+
anything new was said, which takes a few seconds; the terminal doesn't
|
|
103
|
+
wait, and an attach or `chi send` meanwhile starts the next worker once it
|
|
104
|
+
is gone. A worker from an
|
|
105
|
+
older chi can't be asked; the line says to use `chi sessions stop ID`.
|
|
106
|
+
- `/exit --delete` (also `/quit --delete`, `exit --delete`) does the same and,
|
|
107
|
+
once the worker has agreed to exit (without writing a recap), deletes the session for good. When the
|
|
108
|
+
worker stays up, nothing is deleted and the line says why.
|
|
109
|
+
- The prompt stays open while a turn runs; see [Typing during a turn](#typing-during-a-turn).
|
|
110
|
+
- `/model`, `/models`, `/guardrails`, `/continue`, `!rollback` and `!commands` run in the
|
|
111
|
+
worker, and every UI sees their output; `/stats` and `/recap` work too. The
|
|
112
|
+
Web UI's composer takes the same commands.
|
|
113
|
+
- `!commands` and the model's tools run in the session's directory (where it
|
|
114
|
+
was started), whichever terminal you attach from.
|
|
115
|
+
- `-p` sends its prompt once attached, `--model` switches the worker's model
|
|
116
|
+
first, and `--no-interrupt` applies to each prompt this terminal sends.
|
|
117
|
+
- History, completion and the idle status line work as in the REPL.
|
|
118
|
+
- The attached view needs reline 0.6.x to draw around the open prompt; with
|
|
119
|
+
another version it prints plainly.
|
|
120
|
+
|
|
121
|
+
A worker nobody uses exits after `session.idle_exit_minutes` (30 by default, `0`
|
|
122
|
+
for never): no turn running or queued, no UI attached (an open web tab or an
|
|
123
|
+
attached terminal counts, even an idle one) and no reminder registered. The next
|
|
124
|
+
prompt or `--attach` wakes a new worker with the conversation intact; `/stats`
|
|
125
|
+
counters start over (the recap is saved with the session).
|
|
126
|
+
|
|
127
|
+
A session you leave with nothing in it (no prompt sent, no `/model` switch, no
|
|
128
|
+
note or image) is deleted as its worker exits, and `/exit` says so; set
|
|
129
|
+
`session.keep_empty: true` to keep such sessions. See
|
|
130
|
+
[Sessions](sessions.md).
|
|
131
|
+
|
|
132
|
+
`chi sessions stop ID...` stops each session's worker and waits for it to exit, so
|
|
133
|
+
a `chi --resume ID` after it starts a fresh one. A worker still running an
|
|
134
|
+
older chi (from before an upgrade) takes turns but not commands; the attached
|
|
135
|
+
terminal and the Web UI say so, with that restart line.
|
|
136
|
+
|
|
137
|
+
`chi sessions delete [--force] ID...` deletes sessions for good: the
|
|
138
|
+
session file and its whole directory (notes, images, queued input). Each id
|
|
139
|
+
(or unique prefix) gets one line: `deleted`, or `refused` with the reason. A
|
|
140
|
+
session whose worker runs is refused unless `--force` stops the worker first;
|
|
141
|
+
one open in a plain REPL is always refused ("close it there first"). Exit
|
|
142
|
+
status: 0 when all are gone, 1 when any was refused or unknown, 2 on a usage
|
|
143
|
+
error. The Web UI deletes too: `delete` in the info bar, or the ✕ on a card in
|
|
144
|
+
All sessions; it asks first and stops a live worker.
|
|
145
|
+
|
|
146
|
+
**The plain REPL.** Some launches run the session in this process instead, with
|
|
147
|
+
no worker:
|
|
148
|
+
|
|
149
|
+
- `--no-shared`, for one run, or `session.shared: false` in the config
|
|
150
|
+
(`SAMAGOTCHI_SESSION_SHARED=0`), for every run.
|
|
151
|
+
- `--non-interactive`, a one-shot with no REPL.
|
|
152
|
+
- `--verbose`, which attached mode can't honor (the worker prints nothing). It
|
|
153
|
+
prints a one-line note, `(session.shared: --verbose runs in a plain REPL)`.
|
|
154
|
+
|
|
155
|
+
A session the REPL has open can't be shared: `--resume` and `--attach` on it say
|
|
156
|
+
"close it there first", and the Web UI shows it read-only. `--attach`/`--shared`
|
|
157
|
+
can't be combined with `--non-interactive` or `--verbose`.
|
|
158
|
+
|
|
159
|
+
### Muting a memory
|
|
160
|
+
|
|
161
|
+
`chi --mute NAME` runs a session without a memory: for a memory whose
|
|
162
|
+
description mixes the context for a small model, or one a bundle owns
|
|
163
|
+
(`gh-helper`, `jira-manager`) that is not worth editing locally. The memory's
|
|
164
|
+
file and index line stay as they are; only this session doesn't see it.
|
|
165
|
+
|
|
166
|
+
- `--memory` and `--mute` are session fields (`preloaded_memory_names`,
|
|
167
|
+
`muted_memory_names` in the session's JSON), written before the worker
|
|
168
|
+
starts. A worker respawned by `--resume`, `--attach` or `chi send` builds
|
|
169
|
+
the same prompt, and a REPL `--resume` of that session keeps the lists too
|
|
170
|
+
(merged with the flags it is given).
|
|
171
|
+
- A mute wins: `--mute user_preferences` drops that config baseline entry for
|
|
172
|
+
one session, and `--memory x --mute x` is a mute (one warning line).
|
|
173
|
+
- Names are checked before the launch, warnings only: `Warning: --memory 'x'
|
|
174
|
+
not found`, `Warning: --mute 'x' matches no memory`, `Warning: 'x' is both
|
|
175
|
+
--memory and --mute; muted`.
|
|
176
|
+
- `--attach ID` or `--resume ID` with either flag: the session's prompt is
|
|
177
|
+
already built, so the flags are ignored with one line,
|
|
178
|
+
`(--mute applies to a new session; <id>'s prompt is already built)`.
|
|
179
|
+
- The status row shows `mem: <used and preloaded>` and `muted: <names>`; the
|
|
180
|
+
Web UI's info-bar tooltip shows `memories: … · preloaded: … · muted: …`.
|
|
181
|
+
- The `read` tool on `memories/<name>.md` is not refused (a guardrails rule
|
|
182
|
+
can protect the path if wanted).
|
|
183
|
+
|
|
184
|
+
### Typing during a turn
|
|
185
|
+
|
|
186
|
+
The prompt stays open while a turn runs, in an attached terminal and in the plain
|
|
187
|
+
REPL alike:
|
|
188
|
+
|
|
189
|
+
- A line you submit merges into the running turn at its next step (after the
|
|
190
|
+
current tool call or answer), and `(1 message merged into the running turn)`
|
|
191
|
+
says so. An answer the model finished just before the merge is printed first.
|
|
192
|
+
A line that comes after the turn's last step runs as the next turn, and so
|
|
193
|
+
does one sent after Ctrl-C: it doesn't merge into the turn being cancelled.
|
|
194
|
+
Reminder turns take merged lines too.
|
|
195
|
+
- `/stats` and `/recap` answer at once. Other commands (`!cmd`, `/model`,
|
|
196
|
+
`!rollback`, `/continue`, `/guardrails`) say `busy: wait for the turn to end`
|
|
197
|
+
and go back into the prompt, so Enter runs them once the turn ends.
|
|
198
|
+
- A question (`ask_user_question`, a guardrails approval) turns the prompt into
|
|
199
|
+
a yellow `? ` and lists its choices under it, fitted to the terminal; only a
|
|
200
|
+
line submitted there answers it (a number, `1,3`, a label, `y`/`n` for an
|
|
201
|
+
approval, `; text` for a reason; Enter alone dismisses it), and what you had
|
|
202
|
+
typed comes back once it closes. The choices then go, and one line stays:
|
|
203
|
+
`? Pick a fruit → Banana`.
|
|
204
|
+
- Ctrl-C cancels the turn and keeps what you typed.
|
|
205
|
+
- In the plain REPL, Ctrl-D on an empty prompt (or `exit`, `/exit`) mid-turn
|
|
206
|
+
exits once the turn ends: `(exits after this turn; Ctrl-C cancels it)`
|
|
207
|
+
(`/exit --delete` deletes the session then too). In an
|
|
208
|
+
attached terminal it detaches at once and the turn goes on in the worker
|
|
209
|
+
(`/exit` then says the worker stays up: a turn is running).
|
|
210
|
+
|
|
211
|
+
With stdin that isn't a terminal (a pipe), the REPL reads a line only between
|
|
212
|
+
turns.
|
|
213
|
+
|
|
214
|
+
### Images
|
|
215
|
+
|
|
216
|
+
A model that can see images gets them three ways:
|
|
217
|
+
|
|
218
|
+
- **`@path` in a prompt** (REPL, attached terminal, `-p`): `what's wrong in
|
|
219
|
+
@shot.png?`, `@~/Desktop/a.jpg`, `@"my shot.png"`. Each `@` token that names an
|
|
220
|
+
image file (png, jpeg, gif, webp; bmp, tiff and heic are converted) goes with
|
|
221
|
+
the prompt, and a dim line shows it: `[image shot.png 1280×800 · ~1.3k tokens]`.
|
|
222
|
+
The prompt text stays as typed; an `@` token that isn't an image (a source
|
|
223
|
+
file, a missing path, an email address) is just text. A line with images typed
|
|
224
|
+
while a turn runs waits for the next turn (steering merges text only).
|
|
225
|
+
- **A path in plain words**: "check /home/me/shot.png and describe it". The
|
|
226
|
+
model calls `read` on it and sees the picture; the tool line ends in
|
|
227
|
+
`→ image 1280×800`.
|
|
228
|
+
- **The Web UI**: paste or drop images into the composer. Each shows as a chip
|
|
229
|
+
(× removes it) and is sent with the message; an image alone is sent as
|
|
230
|
+
`[image: name]`. Messages show thumbnails; a click opens one full size.
|
|
231
|
+
|
|
232
|
+
Images are downscaled to a 1568 px long side (with `sips` on macOS or
|
|
233
|
+
ImageMagick; without either, a larger image is refused with a hint) and stored
|
|
234
|
+
next to the session in `<session>/images/`, and the session file keeps small
|
|
235
|
+
references to them. Each request sends the newest 20 images of the conversation;
|
|
236
|
+
older ones become a line like `[image shot.png 1280×800 not sent: only the
|
|
237
|
+
newest 20 images are sent]`.
|
|
238
|
+
|
|
239
|
+
A model that can't see images (a text-only model, llama.cpp without
|
|
240
|
+
`--mmproj`, an mlx host) refuses a turn with images before sending it:
|
|
241
|
+
`host main can't take images: …; send text only, or pick a model that can see
|
|
242
|
+
images (/model)`, and the typed text (and the web's chips) come back. When chi
|
|
243
|
+
can't tell beforehand, the provider's refusal gives the same line. Images already
|
|
244
|
+
in the conversation go as placeholder lines after a switch to such a model.
|
|
245
|
+
Settings: `image.*` and `vision:` in [Configuration](configuration.md#images).
|
|
246
|
+
|
|
247
|
+
### Web Markdown rendering
|
|
248
|
+
|
|
249
|
+
Web responses are escaped text by default. To render completed assistant
|
|
250
|
+
responses as HTML, enable the renderer for the web server (it uses
|
|
251
|
+
`commonmarker`, which `bundle install` pulls in from the Gemfile; outside
|
|
252
|
+
Bundler, `gem install commonmarker`):
|
|
253
|
+
|
|
254
|
+
```sh
|
|
255
|
+
chi web --web-markdown
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
The setting also supports `SAMAGOTCHI_WEB_MARKDOWN=true` or the global config:
|
|
259
|
+
|
|
260
|
+
```yaml
|
|
261
|
+
web:
|
|
262
|
+
markdown: true
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Only finalized assistant messages are rendered; user messages and live streaming
|
|
266
|
+
chunks remain escaped text. Generated HTML is sanitized, raw HTML in model output
|
|
267
|
+
is not trusted, and unsafe links are removed. If Markdown is enabled without
|
|
268
|
+
commonmarker installed, Chi Web keeps the normal escaped-text display and shows a
|
|
269
|
+
warning explaining how to install the optional gem.
|
|
270
|
+
|
|
271
|
+
Every prompt and answer has a copy button (on hover; always shown, dimmed, on a
|
|
272
|
+
touch screen), and so does each code block of a rendered answer. An answer
|
|
273
|
+
copies its Markdown source, not the rendered text; a code block copies just
|
|
274
|
+
its code; a prompt copies the text as you typed it.
|
|
275
|
+
|
|
276
|
+
### Web turn view
|
|
277
|
+
|
|
278
|
+
The turn view, the default, shows a turn as *one block* where the work
|
|
279
|
+
happens (the classic chat view renders a turn with tool calls as a row of
|
|
280
|
+
bubbles: one thinking block, one activity panel and one answer bubble per
|
|
281
|
+
generation; `web.turn_view: false` brings it back). The running
|
|
282
|
+
generation is the live part at the bottom (its thinking, its narration, its
|
|
283
|
+
tool rows), the earlier ones stack above it collapsed to one line each
|
|
284
|
+
(their narration's first line, else `working with <tools>`, and a call
|
|
285
|
+
count), expandable for inspection. The live thinking is one line: the
|
|
286
|
+
newest complete sentence, changing at most once per 1.5 s. Click it for the
|
|
287
|
+
full text; a peek is per step (the next step's thinking starts closed
|
|
288
|
+
again). When a step ends its thinking closes to a plain `thinking` line
|
|
289
|
+
(unless you opened it); a step that only thought shows that line alone. The
|
|
290
|
+
live narration is a box of about three lines that fills sentence by
|
|
291
|
+
sentence (a sentence shows once it is complete) and scrolls to the newest
|
|
292
|
+
when full. When the turn ends the block collapses to its summary (`3 steps
|
|
293
|
+
· 8 tool calls · execute ×7 · read ×1`) and the answer expands from the
|
|
294
|
+
box into a normal bubble under it (Markdown, annotate). A plain answer
|
|
295
|
+
without tools ends exactly as it does today. Rows the code collapses stay
|
|
296
|
+
as you toggled them. A reloaded session shows the same block
|
|
297
|
+
from the saved messages (each step's thinking, narration, tool parameters
|
|
298
|
+
and output, the output capped at 2000 characters) and the timing records
|
|
299
|
+
(status and duration per row). On an `api: openai` host the model's
|
|
300
|
+
reasoning is saved with each step for this (never sent back to the model);
|
|
301
|
+
steps saved before that have none, so they show no thinking.
|
|
302
|
+
|
|
303
|
+
```sh
|
|
304
|
+
chi web --no-web-turn-view # the classic chat view; --web-turn-view is the default
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
The setting also supports `SAMAGOTCHI_WEB_TURN_VIEW=false` or the global config:
|
|
308
|
+
|
|
309
|
+
```yaml
|
|
310
|
+
web:
|
|
311
|
+
turn_view: false
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
`?view=chat` on the page URL forces the classic chat view for that page load
|
|
315
|
+
and `?view=turn` the turn view, whatever the config says; the parameter is dropped
|
|
316
|
+
when you switch between the project and all-sessions views. The terminal
|
|
317
|
+
UIs are not affected.
|
|
318
|
+
|
|
319
|
+
## Runtime Model Switch (Assist Mode)
|
|
320
|
+
|
|
321
|
+
In interactive assist mode, you can switch the request model without restarting:
|
|
322
|
+
|
|
323
|
+
- `/model <name>`: set a session-scoped model override.
|
|
324
|
+
- `/model host:model` or `/model host/alias`: qualified host routing (`host:alias` expands alias bare, alias may itself be `host:model` — hybrid).
|
|
325
|
+
- `/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).
|
|
326
|
+
- `/model <name> --alias <alias>`: create alias for current effective model (alias value may be bare or `host:model`).
|
|
327
|
+
- `/model`: show the effective model (and default when diverged: `runtime model: <effective> (default: <default>, profile=<name>, <source>)`, e.g. `profile=qwen36, server (chat_template)`).
|
|
328
|
+
- `/model clear` (or `default`/`none`/`off`): clear the session override, reverting to the configured default.
|
|
329
|
+
- `/guardrails`: the guardrail rules (by source), what failed to load, and your stored approvals, numbered; `/guardrails revoke N` removes approval N (see [Guardrails](guardrails.md)).
|
|
330
|
+
- `/models`: list model ids aggregated across all `hosts:` (grouped `host (host:port):` with per-host `unreachable` warnings, e.g. an unset `api_key_env`; lists cached 60s, 10 minutes for a remote host; lazy — no startup prefill). At most 20 ids per host, then `… and N more`; `/models <text>` lists every id containing `<text>` (any case), e.g. `/models qwen` on OpenRouter.
|
|
331
|
+
|
|
332
|
+
Notes:
|
|
333
|
+
|
|
334
|
+
- The switch updates the request `model` field, routes to the matching host (`HostRegistry`, `lib/samagotchi/host_registry.rb:72`), and resolves the prompt profile again (config, the server's chat template, the name; see "Prompt profile" in configuration.md).
|
|
335
|
+
- Without `--default` the command is session-scoped and does not rewrite config files.
|
|
336
|
+
- With `--default` the new default is written to `~/.config/samagotchi/config.yml` (honoring `XDG_CONFIG_HOME`) and takes effect for all new sessions; the current session's effective model is also updated immediately. Bare aliases and `host:model` are both valid.
|
|
337
|
+
- Worker sessions inherit `hosts:` via `SAMAGOTCHI_HOSTS_JSON`.
|
|
338
|
+
- The idle recap uses the session's current model (a switch counts from the next recap), unless `recap: {host_ref, model}` pins one.
|
|
339
|
+
|
|
340
|
+
## Session recap
|
|
341
|
+
|
|
342
|
+
A short recap of the session, for when you come back to it: what you were
|
|
343
|
+
working on, what came of it and what is still open, not a turn-by-turn log.
|
|
344
|
+
|
|
345
|
+
- It is written once the session has sat idle for `recap.inactivity` (180 s)
|
|
346
|
+
after at least `recap.min_user_turns` (2) prompts, and as a worker (or the
|
|
347
|
+
REPL) exits, when something new was said since the last one. Each one
|
|
348
|
+
builds on the previous recap, so it only sends what is new.
|
|
349
|
+
- It is saved with the session (`<session>/recap.json`) and shown as a dim
|
|
350
|
+
`recap>` block when you attach or `--resume`, noting how many turns came
|
|
351
|
+
after it (`recap (before the last 2 turns)>`). One written while you sit at
|
|
352
|
+
the prompt prints there.
|
|
353
|
+
- `/recap` shows the saved one and asks for a new one when the chat moved on
|
|
354
|
+
(`writing a recap…`, then the recap when it comes).
|
|
355
|
+
- One written while a continue offer waits (the last turn ran out of steps)
|
|
356
|
+
says that turn stopped before the task was finished. Answering `no` counts
|
|
357
|
+
as activity, so the next recap no longer says so.
|
|
358
|
+
- It is 2-4 sentences; `recap.sentences` sets another length (`3`, `5-7`,
|
|
359
|
+
up to 10; `--recap-sentences`, `SAMAGOTCHI_RECAP_SENTENCES`). A change
|
|
360
|
+
shows from the next recap written, and updates keep to it.
|
|
361
|
+
- It uses the session's own model unless `recap:` names one;
|
|
362
|
+
`recap: false` turns it off. See configuration.md.
|
|
363
|
+
|
|
364
|
+
## Tool Activity Log
|
|
365
|
+
|
|
366
|
+
Samagotchi now prints a concise, human-friendly tool activity log in normal
|
|
367
|
+
chat output. Each tool call is summarized as:
|
|
368
|
+
|
|
369
|
+
`tool> <action> (<tool> <param-preview>): <status>`
|
|
370
|
+
|
|
371
|
+
Examples:
|
|
372
|
+
|
|
373
|
+
- `tool> reading file (read path="README.md"): ok`
|
|
374
|
+
- `tool> running command (execute command="bundle exec rspec spec/..." ): error`
|
|
375
|
+
|
|
376
|
+
Parameter previews are normalized to one line and truncated to keep output concise.
|
|
377
|
+
|
|
378
|
+
This is separate from verbose mode:
|
|
379
|
+
|
|
380
|
+
- Default output shows short activity status lines only.
|
|
381
|
+
- `-v/--verbose` prints the debug log's records (raw LLM responses and full
|
|
382
|
+
tool call/result payloads among them) to stderr as well; see
|
|
383
|
+
[Debug Log File](configuration.md#debug-log-file).
|
|
384
|
+
|
|
385
|
+
## Persistent Prompt History
|
|
386
|
+
|
|
387
|
+
Assist mode keeps a small persistent prompt history across restarts.
|
|
388
|
+
|
|
389
|
+
- Default history file: `$XDG_STATE_HOME/samagotchi/history.json`
|
|
390
|
+
- XDG fallback when unset: `~/.local/state/samagotchi/history.json`
|
|
391
|
+
- Optional override: `SAMAGOTCHI_HISTORY_FILE=/custom/path/history.json`
|
|
392
|
+
- Stored entries: most recent `20` prompts
|
|
393
|
+
- Format: JSON array of prompt strings
|
|
394
|
+
|
|
395
|
+
Behavior details:
|
|
396
|
+
|
|
397
|
+
- Prompt history is loaded on startup before the first `>` prompt.
|
|
398
|
+
- Only real user prompts are persisted.
|
|
399
|
+
- Continue-flow inputs (`yes`, `no`, `no, <reason>`, `/continue`) are not persisted as prompts.
|
|
400
|
+
- In assist mode, pressing `Tab` on an `@`-prefixed token (for example `@lib/sama`) completes project file and directory paths while preserving the `@` prefix.
|
|
401
|
+
- Press `Tab` twice to cycle/show multiple matching candidates, similar to IRB completion behavior.
|
|
402
|
+
- History read/write errors are ignored so the session continues uninterrupted.
|
|
403
|
+
|
|
404
|
+
## Status Line
|
|
405
|
+
|
|
406
|
+
Assist mode can render a compact generalized status line that can include mode,
|
|
407
|
+
context estimate, and active memory hints.
|
|
408
|
+
|
|
409
|
+
Behavior:
|
|
410
|
+
|
|
411
|
+
- A static status line is printed before the next `>` prompt in assist mode.
|
|
412
|
+
- During spinner rendering, status details are rendered in the spinner block.
|
|
413
|
+
- When llama.cpp streaming payload includes usage fields, status prefers server-derived token telemetry (`p`, `c`, `t`) and context percent.
|
|
414
|
+
- If server usage fields are absent, status falls back to the `:context_status` estimate telemetry.
|
|
415
|
+
- When a memory is loaded between tool rounds, the spinner line includes a `loaded: <memory>` notification immediately after the spinner frame.
|
|
416
|
+
- After responses, memory details are shown via the same unified `status>` line.
|
|
417
|
+
- The legacy standalone `memories>` summary line is no longer emitted.
|
|
418
|
+
- With `--mute`, the sticky and idle rows add `muted: <names>` after `mem:` (the
|
|
419
|
+
spinner row doesn't). Attached, `mem:` shows the used memories and the
|
|
420
|
+
session's `--memory` list before the first turn records them.
|
|
421
|
+
|
|
422
|
+
Configuration:
|
|
423
|
+
|
|
424
|
+
- `SAMAGOTCHI_STATUS_LINE` (default `on`): set to `off`, `false`, or `0` to disable status-line rendering.
|
|
425
|
+
- `SAMAGOTCHI_STATUS_WIDTH_MODE` (default `terminal_cap`): one of `terminal_cap`, `fixed`.
|
|
426
|
+
- `SAMAGOTCHI_STATUS_MAX_WIDTH` (default `160`): maximum width used by `terminal_cap`.
|
|
427
|
+
- `SAMAGOTCHI_STATUS_FIXED_WIDTH` (default `120`): fixed width used by `fixed` mode.
|
|
428
|
+
|
|
429
|
+
Width mode behavior:
|
|
430
|
+
|
|
431
|
+
- `terminal_cap`: use `min(terminal_columns, SAMAGOTCHI_STATUS_MAX_WIDTH)`, single-line with `+N` overflow indicator.
|
|
432
|
+
- `fixed`: use `SAMAGOTCHI_STATUS_FIXED_WIDTH`, single-line with `+N` overflow indicator.
|
|
433
|
+
|
|
434
|
+
Notes:
|
|
435
|
+
|
|
436
|
+
- Spinner rendering remains app-managed to keep cursor cleanup deterministic.
|
|
437
|
+
- Raw terminal auto-wrap is intentionally avoided in the spinner region.
|
|
438
|
+
|
|
439
|
+
## Tool Tally
|
|
440
|
+
|
|
441
|
+
A long, tool-heavy turn says what it has been doing. From the turn's 3rd tool call,
|
|
442
|
+
a dim row under the activity row tallies its calls, with no model call:
|
|
443
|
+
|
|
444
|
+
```
|
|
445
|
+
12 tool calls (2 failed) · execute ×7 · read_file ×3 · edit_file ×2 · last: execute command=bundle exec rspec
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
It lists the top 3 tools by count (ties go to the tool used first), the failed calls
|
|
449
|
+
(a call a guardrail or an approval blocked counts as a call, not as failed) and the last
|
|
450
|
+
call with its parameters, cut to the terminal width. It starts over with each turn.
|
|
451
|
+
|
|
452
|
+
- Attached mode: the second row of the activity slot, shown while the slot is (the
|
|
453
|
+
model generating or a tool running). Joining a turn mid-way seeds it from the turn so far.
|
|
454
|
+
- The REPL (`--no-shared`): a row under the spinner row. The spinner stops while tools
|
|
455
|
+
run (the `tool>` lines show them), so the tally shows while the model generates
|
|
456
|
+
between tool rounds; the spinner block is one row taller from then on.
|
|
457
|
+
- The web: the activity panel's summary reads `activity · 12 tool calls (2 failed) · execute ×7 · …`
|
|
458
|
+
(without `last:`: the rows show it).
|
|
459
|
+
|
|
460
|
+
## Thinking Spinner Sentence
|
|
461
|
+
|
|
462
|
+
While the model generates, the spinner row shows the newest complete sentence of its thinking
|
|
463
|
+
(`model> thinking · <sentence> |` in the REPL, `| thinking · <sentence>` in attached mode), or of its
|
|
464
|
+
answer (`writing ·`), like the web's thinking ticker: the same sentence rules (a list number such as
|
|
465
|
+
`118.` is no sentence end; a newline is one), and the row changes at most once every 1.5 s so it
|
|
466
|
+
doesn't flicker. A long sentence is cut with `…`; a Qwen `TURN:` prefix and inline markdown are left
|
|
467
|
+
out. Before the first sentence the row reads `thinking...`. With `TERM=dumb` there is no spinner row.
|
|
468
|
+
|
|
469
|
+
- When a memory entry is loaded during thinking, the spinner line also shows a compact inline preview
|
|
470
|
+
of that tool call (for example `tool: memory_read(name=...)`) for live visibility before end-of-turn
|
|
471
|
+
tool logs; the sentence gets the room left.
|
|
472
|
+
|
|
473
|
+
## Thinking-Phase Cancellation
|
|
474
|
+
|
|
475
|
+
During assist-mode thinking (while the spinner is active), you can cancel an in-flight model request without exiting the process:
|
|
476
|
+
|
|
477
|
+
- Press `Ctrl-C` to cancel the active request.
|
|
478
|
+
|
|
479
|
+
Behavior notes:
|
|
480
|
+
|
|
481
|
+
- Cancellation returns control to the prompt immediately; what you typed there stays.
|
|
482
|
+
- Partial model output from the canceled request is not committed as a completed model turn.
|
|
483
|
+
|
|
484
|
+
## Iteration Limit Behavior
|
|
485
|
+
|
|
486
|
+
- `max_iterations` remains a hard safety cap on tool-call rounds.
|
|
487
|
+
- Tool side effects that already ran before the cap are not rolled back.
|
|
488
|
+
- `Samagotchi::KernelLoop#run` now returns a resumable result object with the visible output plus the accumulated conversation.
|
|
489
|
+
- If the cap is reached while tool calls are still pending, the result is marked resumable so callers can continue from the saved conversation instead of restarting from scratch.
|
|
490
|
+
- In assist mode, the CLI then asks at the `? ` prompt, with the choices listed under it: `yes` (Enter alone, or `/continue`) resumes, `no` cancels, and `no, <explanation>` cancels while keeping the reason in conversation context. Once answered, one line stays: `? The turn ran out of iterations. Continue it? → no, too slow`.
|