samagotchi 0.6.0 → 0.8.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 +72 -1
- data/README.md +7 -3
- data/docs/architecture.md +4 -4
- data/docs/cli.md +19 -4
- data/docs/configuration.md +20 -0
- data/docs/desktop.md +49 -0
- data/docs/guardrails.md +2 -2
- data/docs/hooks.md +2 -2
- data/docs/internals/context-telemetry.md +3 -1
- data/docs/memory.md +48 -1
- data/docs/plugins.md +7 -5
- data/docs/releasing.md +5 -1
- data/docs/sessions.md +2 -2
- data/lib/samagotchi/archive_store.rb +3 -3
- data/lib/samagotchi/atomic_file.rb +34 -0
- data/lib/samagotchi/bootstrap/bundles.rb +37 -0
- data/lib/samagotchi/bootstrap/config_writer.rb +4 -6
- data/lib/samagotchi/bootstrap/probe.rb +9 -5
- data/lib/samagotchi/bootstrap_command.rb +78 -8
- data/lib/samagotchi/bridge/pending_card.rb +2 -3
- data/lib/samagotchi/bridge.rb +56 -116
- data/lib/samagotchi/bridge_client.rb +7 -11
- data/lib/samagotchi/bundle_command.rb +81 -21
- data/lib/samagotchi/bundle_needs.rb +2 -11
- data/lib/samagotchi/bundles/core/manifest.yml +11 -0
- data/lib/samagotchi/bundles/dev/manifest.yml +13 -0
- data/lib/samagotchi/bundles/system/identity.md +1 -0
- data/lib/samagotchi/bundles/system/manifest.yml +3 -3
- data/lib/samagotchi/bundles/system/memory_guide.md +3 -2
- data/lib/samagotchi/client.rb +3 -23
- data/lib/samagotchi/config.rb +11 -11
- data/lib/samagotchi/context_note.rb +1 -1
- data/lib/samagotchi/context_status.rb +255 -0
- data/lib/samagotchi/desktop/macos/App.swift +5 -1
- data/lib/samagotchi/desktop/macos/ChiRunner.swift +7 -63
- data/lib/samagotchi/desktop/macos/Images.swift +38 -3
- data/lib/samagotchi/desktop/macos/Kitty.swift +346 -0
- data/lib/samagotchi/desktop/macos/Panel.swift +233 -55
- data/lib/samagotchi/desktop/macos/ProcessRunner.swift +74 -0
- data/lib/samagotchi/desktop/macos.rb +25 -11
- data/lib/samagotchi/desktop_command.rb +1 -1
- data/lib/samagotchi/engine.rb +6 -7
- data/lib/samagotchi/guardrail_wiring.rb +2 -5
- data/lib/samagotchi/guardrails/approvals.rb +2 -7
- data/lib/samagotchi/hooks/bundle_loader.rb +6 -3
- data/lib/samagotchi/hooks/loader.rb +2 -1
- data/lib/samagotchi/hooks/registry.rb +21 -0
- data/lib/samagotchi/image_store.rb +2 -3
- data/lib/samagotchi/kernel_loop.rb +21 -283
- data/lib/samagotchi/live_versions.rb +9 -13
- data/lib/samagotchi/llm/chat_loop.rb +1 -1
- data/lib/samagotchi/llm/native_backend.rb +8 -8
- data/lib/samagotchi/llm/openai_chat.rb +4 -17
- data/lib/samagotchi/llm/utf8_scrub.rb +32 -0
- data/lib/samagotchi/log_path.rb +2 -3
- data/lib/samagotchi/memory_bundle/index_updater.rb +2 -11
- data/lib/samagotchi/memory_bundle/installer.rb +5 -7
- data/lib/samagotchi/memory_bundle/listing.rb +27 -20
- data/lib/samagotchi/memory_bundle/manifest.rb +32 -1
- data/lib/samagotchi/memory_bundle/profile.rb +145 -0
- data/lib/samagotchi/memory_bundle/provenance.rb +42 -53
- data/lib/samagotchi/memory_bundle/shipped_update.rb +52 -27
- data/lib/samagotchi/memory_bundle/source.rb +2 -0
- data/lib/samagotchi/memory_bundle/status.rb +1 -2
- data/lib/samagotchi/memory_bundle.rb +1 -0
- data/lib/samagotchi/note_command.rb +5 -4
- data/lib/samagotchi/owner_lock.rb +0 -6
- data/lib/samagotchi/paths.rb +32 -0
- data/lib/samagotchi/plugin/api.rb +3 -6
- data/lib/samagotchi/plugin/context.rb +2 -3
- data/lib/samagotchi/plugin/loader.rb +1 -1
- data/lib/samagotchi/question_desk.rb +3 -2
- data/lib/samagotchi/recap_store.rb +2 -2
- data/lib/samagotchi/reply_wait.rb +2 -1
- data/lib/samagotchi/self_report.rb +2 -3
- data/lib/samagotchi/send_command.rb +3 -2
- data/lib/samagotchi/session.rb +41 -143
- data/lib/samagotchi/session_inbox.rb +175 -0
- data/lib/samagotchi/session_manager.rb +62 -363
- data/lib/samagotchi/session_metrics.rb +2 -3
- data/lib/samagotchi/session_retention.rb +259 -0
- data/lib/samagotchi/sessions_command.rb +3 -2
- data/lib/samagotchi/terminal_ui/attached_loop.rb +1 -2
- data/lib/samagotchi/terminal_ui/input_support.rb +2 -9
- data/lib/samagotchi/terminal_ui.rb +10 -20
- data/lib/samagotchi/tools/memory.rb +3 -2
- data/lib/samagotchi/tools/send_note.rb +4 -3
- data/lib/samagotchi/tools/task_runtime.rb +2 -3
- data/lib/samagotchi/turn_note.rb +13 -2
- data/lib/samagotchi/update_command.rb +10 -3
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +44 -76
- data/lib/samagotchi/web/info_probe.rb +33 -0
- data/lib/samagotchi/web/public/app.js +1 -1
- data/lib/samagotchi/web/public/index.html +1 -0
- data/lib/samagotchi/web/public/stage_model.js +22 -3
- data/lib/samagotchi/web/public/stage_view.js +25 -10
- data/lib/samagotchi/web/server.rb +8 -11
- data/lib/samagotchi/web/session_hub.rb +8 -7
- data/lib/samagotchi/web/session_summary.rb +4 -3
- data/lib/samagotchi/web/token.rb +2 -5
- data/lib/samagotchi/worker.rb +34 -20
- data/lib/samagotchi/worker_sidecar.rb +42 -0
- metadata +15 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4db526cc3c79a4e5aa8061f124ff09e65e289415074fcb51c71c25ccee1ab0c9
|
|
4
|
+
data.tar.gz: c30c2094cb357df9fe4e5618956e0c1a559ea0a2765709605488d03c5febbb87
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2ad7fd71ecfee430c9dec6d9b51971b1e7fdc38c099c5f4a868ad07add50a21ac293f6c686e5f8aca9b0d3bd919ee49e1d79336ac1fe1ac4926d6ace2de24adb
|
|
7
|
+
data.tar.gz: 5856a9fe2fcb23b4802626ee918e7015cc718d74b4d17828265377a606cb76fdf80badb3f5d0e314c76bcd3ceb4629fc3f79ab735275fd102c1a2895b54b4f3d
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,75 @@ and commands may change between minor versions. How releases are made:
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.8.0] - 2026-10-01
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Bundle profiles: `core` (loop-guard, check-in, guardrails) and `dev` (known-names, mcp, btw, skills,
|
|
16
|
+
source-links) install a set of the shipped bundles, `chi bundle install core`. A bundle you uninstall stays
|
|
17
|
+
out when the profile is installed or upgraded again, and `chi update` installs a bundle a new chi adds to a
|
|
18
|
+
profile you have. `chi bundle uninstall core` removes its bundles, `chi bundle list` and `status` show them.
|
|
19
|
+
- `chi bootstrap` installs the system bundle and the `core` bundles, and on a terminal offers `dev`. Existing
|
|
20
|
+
installs get neither by themselves.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- Dismissing a question (Esc, an empty answer, or no one to answer in `-p`) now tells the model not to do what it
|
|
25
|
+
asked about, or anything else that changes files or state, and to wait, instead of "go on with your best
|
|
26
|
+
judgement".
|
|
27
|
+
- The system bundle tells chi to stop and ask on anything unexpected while following a skill (a failing check,
|
|
28
|
+
output that differs from a step, a warning the skill doesn't mention) instead of improvising a fix.
|
|
29
|
+
- `chi bundle list` and `chi update`'s footer list the shipped bundles that aren't installed under their
|
|
30
|
+
profile (`core (…)`, `dev (…)`).
|
|
31
|
+
- `chi bundle status` without a name lists a bundle whose `manifest.json` doesn't parse as unreadable instead of
|
|
32
|
+
failing.
|
|
33
|
+
- The web stage view flashes a plugin's nudge ("↪ check-in nudged the model") and a hook's info notice in its
|
|
34
|
+
trail for about 4 s, as it already did a warn notice, so they are seen while the turn runs with the block
|
|
35
|
+
closed. chi's own "asking again" row does not flash (the status line says it).
|
|
36
|
+
- A turn a hook stops (loop-guard, check-in's Stop) tells the model which hook stopped it and why.
|
|
37
|
+
- With custom `context.status_thresholds`, the context guidance follows them (the top bucket reads critical)
|
|
38
|
+
instead of calling every bucket healthy.
|
|
39
|
+
|
|
40
|
+
### Removed
|
|
41
|
+
|
|
42
|
+
- `POST /api/sessions/:id/answer` no longer takes the unused aliases `question_id`, `selection`, `other` or
|
|
43
|
+
a nested `answer` object: only `id`, `selected` and `freeform`, as the web page sends them.
|
|
44
|
+
- Sessions no longer write plain-text input for a worker from before 2026-09-23 (one that advertises no input
|
|
45
|
+
format) or refuse images to one that predates them (`images_unsupported`), and a worker no longer reads
|
|
46
|
+
`input/*.txt`: every chi since then writes and reads JSON input. A worker that old never exits by itself;
|
|
47
|
+
restart it (`chi sessions stop ID`, then send to it) before upgrading.
|
|
48
|
+
- A worker from before the owner lock (2026-09-22) no longer counts as a session's owner through its pid file:
|
|
49
|
+
only the lock holder does, so a stale pid file whose pid was reused no longer makes a session look busy.
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- A hook or plugin that fails while watching the stream is logged once a minute instead of on every batch
|
|
54
|
+
(it printed to stderr on every fire).
|
|
55
|
+
- The web stage view's headline skips lines inside a code fence.
|
|
56
|
+
- A question left open when a session's worker died no longer stays pending: the web and the hub stop showing
|
|
57
|
+
it (and the "needs you" badge) at once, and the next worker drops it.
|
|
58
|
+
|
|
59
|
+
### Security
|
|
60
|
+
|
|
61
|
+
- Session ids from outside (the worker's Bridge, the web routes, `chi send` / `note` / `sessions`,
|
|
62
|
+
`--resume` / `--attach`, the delegate and send_note tools) are checked in one place, so an id such as
|
|
63
|
+
`../x` can no longer reach a path outside the sessions folder. The Bridge answers 400, the web 404.
|
|
64
|
+
|
|
65
|
+
Update with `chi update`. New bundles: `core` 0.1.0 and `dev` 0.1.0 (install with `chi bundle install core`,
|
|
66
|
+
`chi bundle install dev`); existing installs don't get them by themselves.
|
|
67
|
+
|
|
68
|
+
## [0.7.0] - 2026-09-30
|
|
69
|
+
|
|
70
|
+
### Added
|
|
71
|
+
|
|
72
|
+
- The desktop helper also sends to agent CLIs (claude, codex, …) in kitty windows: with `kitty.listen_on` copied
|
|
73
|
+
from kitty.conf (and `chi desktop upgrade`), the panel lists them in a "kitty" group next to the sessions; ⏎
|
|
74
|
+
pastes the quote, message and image paths and presses Enter, the new ⌥⏎ only pastes (so several screenshots
|
|
75
|
+
can pile up), and one send can go to chi sessions and kitty windows together. ⌥⏎ no longer inserts a newline
|
|
76
|
+
in the panel (⇧⏎ does). See docs/desktop.md "Agents in kitty".
|
|
77
|
+
|
|
78
|
+
Update with `chi update`. No bundle versions changed.
|
|
79
|
+
|
|
11
80
|
## [0.6.0] - 2026-09-30
|
|
12
81
|
|
|
13
82
|
### Added
|
|
@@ -393,7 +462,9 @@ and long-lived sessions.
|
|
|
393
462
|
- A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
|
|
394
463
|
hotkey panel that send selected text or the clipboard to your sessions.
|
|
395
464
|
|
|
396
|
-
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.
|
|
465
|
+
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.8.0...HEAD
|
|
466
|
+
[0.8.0]: https://github.com/dm1try/samagotchi/compare/v0.7.0...v0.8.0
|
|
467
|
+
[0.7.0]: https://github.com/dm1try/samagotchi/compare/v0.6.0...v0.7.0
|
|
397
468
|
[0.6.0]: https://github.com/dm1try/samagotchi/compare/v0.5.1...v0.6.0
|
|
398
469
|
[0.5.1]: https://github.com/dm1try/samagotchi/compare/v0.5.0...v0.5.1
|
|
399
470
|
[0.5.0]: https://github.com/dm1try/samagotchi/compare/v0.4.0...v0.5.0
|
data/README.md
CHANGED
|
@@ -67,8 +67,11 @@ chi bootstrap # look on this machine's usual ports
|
|
|
67
67
|
It finds out whether the server is llama.cpp or OpenAI-compatible, picks the
|
|
68
68
|
model (or asks, when there are several), sends one test request and writes
|
|
69
69
|
the config. With a config already there, it adds a `hosts:` entry and keeps
|
|
70
|
-
the rest of the file.
|
|
71
|
-
|
|
70
|
+
the rest of the file. Then it installs the `core` bundles (loop-guard,
|
|
71
|
+
check-in, guardrails: the safety set for a small local model) and, on a
|
|
72
|
+
terminal, offers `dev` (known-names, mcp, btw, skills, source-links). Edit the
|
|
73
|
+
file later as in [Configure](#configure); `chi bootstrap --help` has the
|
|
74
|
+
options.
|
|
72
75
|
|
|
73
76
|
## Configure
|
|
74
77
|
|
|
@@ -154,7 +157,8 @@ Command Line Tools. See [Desktop helper](docs/desktop.md).
|
|
|
154
157
|
|
|
155
158
|
Every tool call the model makes can be allowed, denied, or put to you first.
|
|
156
159
|
Rules come from `config.yml` (`guardrails:`), installed bundles and hooks;
|
|
157
|
-
`
|
|
160
|
+
the `guardrails` bundle (in `core`, which `chi bootstrap` installs; else
|
|
161
|
+
`chi bundle install guardrails`) adds a default set (asks before `git push`,
|
|
158
162
|
history rewrites, wide `rm -rf`, `curl | sh`, writes outside the repo; denies
|
|
159
163
|
writes to `.git/hooks`). You answer in the REPL, the attached TUI or the web:
|
|
160
164
|
once, for the session, for the repo, or for the whole rule in the repo.
|
data/docs/architecture.md
CHANGED
|
@@ -92,7 +92,7 @@ bin/chi ─▶ TerminalUI ─▶ Engine#run_turn ─▶ KernelLoop ──┬─
|
|
|
92
92
|
| Tools | `lib/samagotchi/tools/*` | Execute, read, edit, write, memory, task_*, web_fetch, plus runtime/output-guardrails. |
|
|
93
93
|
| Background | `Samagotchi::SessionManager` | Builds `Engine` directly (no terminal rendering) for workers. |
|
|
94
94
|
| Web | `Samagotchi::Web::App`, `Samagotchi::Web::Server`, `Samagotchi::Web::SessionHub` | Rack+WEBrick single-port `127.0.0.1:4567` (index.html + `/api/*` + SSE). The hub is chi web's projection of the session list, pushed to every tab over `GET /api/events`. |
|
|
95
|
-
| Sessions | `Samagotchi::Session`, `SessionManager` | File `sessions/<uuid>.json` + sidecar `input/`/`output
|
|
95
|
+
| Sessions | `Samagotchi::Session`, `SessionManager` | File `sessions/<uuid>.json` + sidecar `input/`/`output/`; retention 14d/500, `updated_at desc`, lazy sweep. |
|
|
96
96
|
|
|
97
97
|
## Entry points
|
|
98
98
|
|
|
@@ -110,12 +110,12 @@ bin/chi ─▶ TerminalUI ─▶ Engine#run_turn ─▶ KernelLoop ──┬─
|
|
|
110
110
|
`dismiss_question`). The worker runs in the session's `working_directory`, so `!cmd` and
|
|
111
111
|
the tools don't depend on where the terminal attached from.
|
|
112
112
|
- `bin/chi web` → builds `Web::Server` (Rack+WEBrick on `127.0.0.1:4567`, `--port`/`SAMAGOTCHI_WEB_PORT`, `--open`).
|
|
113
|
-
- `bin/chi sessions {list,stop,delete,prune,clean}` (`SessionsCommand`; not the REPL's `SessionCommands`) → retention & ordering (`
|
|
113
|
+
- `bin/chi sessions {list,stop,delete,prune,clean}` (`SessionsCommand`; not the REPL's `SessionCommands`) → retention & ordering (`SessionRetention`, `updated_at desc`, dry-run, test-only); `stop` is `SessionManager.stop_session(wait:)`, which waits for the worker to release `owner.lock`; `delete` (`SessionDeleteCommand`) is `SessionManager.delete_session(stop:)`, which the TUI's `/exit --delete` and the web's `DELETE /api/sessions/:id` use too.
|
|
114
114
|
- `--prompt`, `--non-interactive`, and `SessionManager` workers build `Engine` directly.
|
|
115
115
|
|
|
116
116
|
## Session retention & ordering
|
|
117
117
|
|
|
118
|
-
- **Files:** `~/.local/state/samagotchi/sessions/<uuid>.json` + `<uuid>/input|output|
|
|
118
|
+
- **Files:** `~/.local/state/samagotchi/sessions/<uuid>.json` + `<uuid>/input|output|owner.lock|bridge.json` (XDG-aware).
|
|
119
119
|
- **Single owner:** the process running a session's Engine (worker or in-process TUI) holds a flock on `owner.lock` (`OwnerLock`); a second owner backs off, and the web answers 409 for a TUI-owned session.
|
|
120
120
|
- **Status:** `status` is turn state (`idle`/`running`); liveness is the lock.
|
|
121
121
|
- **Retention:** 14 days / 500 cap (env `SAMAGOTCHI_SESSION_RETENTION_DAYS`/`MAX_COUNT`, optional `KEEP_STATUS`), live-owner guard, only when `*.json` present; lazy sweep ≤1/24h from the session hub's full-probe tick (and on `GET /api/sessions`, which the page no longer calls) & `Dashboard#render_list`, manual via `bin/chi sessions prune --dry-run`.
|
|
@@ -134,7 +134,7 @@ agent logic and can be used without any terminal rendering; the UI is a thin lay
|
|
|
134
134
|
| Model loops and adapters | `KernelLoop`, `LLM::ChatLoop`, `Samagotchi::Client`, `LLM::OpenAIChat`, `LLM::HTTP` | The model↔tool loops and the HTTP adapters they talk through (see "Model loops and adapters"). |
|
|
135
135
|
| Bridge (SSE/HTTP) | `Samagotchi::Bridge`, `SessionManager` | The **single live client transport**: an SSE read stream + HTTP POST turn/cancel/answer surface that attaches to a worker's existing `Engine` via `Engine#subscribe`. Every session worker starts it (bound `127.0.0.1`, no auth, localhost-only). |
|
|
136
136
|
| Web (Rack) | `Samagotchi::Web::App`, `SessionManager` | Single-port `127.0.0.1:4567` control plane via `rack`+`webrick` (serve `index.html` + `/api/*`; `/stream` proxies each session's Bridge). `bin/chi web` entrypoint. |
|
|
137
|
-
| Sessions | `Samagotchi::Session`, `SessionManager` | File-based `~/.local/state/samagotchi/sessions/<uuid>.json` + sidecar `input/`/`output
|
|
137
|
+
| Sessions | `Samagotchi::Session`, `SessionManager` | File-based `~/.local/state/samagotchi/sessions/<uuid>.json` + sidecar `input/`/`output/`; retention (14d/500) + ordering (`updated_at desc`). |
|
|
138
138
|
|
|
139
139
|
- `bin/chi` in REPL mode (see `LaunchMode` above) builds `TerminalUI`. `TerminalUI#run` is the single
|
|
140
140
|
dispatch for the REPL, `-p`/`--prompt`, `--non-interactive`, and `--resume`: it
|
data/docs/cli.md
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
- `chi desktop install|upgrade|uninstall|status` — the macOS "Send to chi" helper: a Service and a ⌃⌥⌘N hotkey that send text to live sessions as context notes (see [Desktop helper](desktop.md))
|
|
22
22
|
- `chi self` — print version, source dir (checkout or installed gem), config/memory/session paths, model/host and bundles; `chi self --model` prints only the model a new session starts on (the desktop helper's hint)
|
|
23
23
|
- `chi update [--dry-run] [--no-gem] [--no-bundles] [--no-desktop]` — update an installed chi: the gem, the system bundle, the shipped bundles you installed and the desktop helper, in one table (see [Updating](#updating))
|
|
24
|
-
- `chi bundle install|upgrade|uninstall|status|diff|list|build` — manage memory bundles (see [Bundle hooks](hooks.md#bundle-hooks-unified-workflow-bundle)); `list` shows the installed ones and the ones shipped with chi, which `install <name>` installs (see [Guardrails](guardrails.md), [Plugins](plugins.md#the-btw-bundle), [the mcp bundle](plugins.md#the-mcp-bundle) [the loop-guard bundle](plugins.md#the-loop-guard-bundle), [the check-in bundle](plugins.md#the-check-in-bundle) and [the skills bundle](plugins.md#the-skills-bundle))
|
|
24
|
+
- `chi bundle install|upgrade|uninstall|status|diff|list|build` — manage memory bundles (see [Bundle hooks](hooks.md#bundle-hooks-unified-workflow-bundle)); `list` shows the installed ones and the ones shipped with chi, which `install <name>` installs; `core` and `dev` are profiles that install a set of them (see [Bundle profiles](memory.md#bundle-profiles-core-and-dev), [Guardrails](guardrails.md), [Plugins](plugins.md#the-btw-bundle), [the mcp bundle](plugins.md#the-mcp-bundle) [the loop-guard bundle](plugins.md#the-loop-guard-bundle), [the check-in bundle](plugins.md#the-check-in-bundle) and [the skills bundle](plugins.md#the-skills-bundle))
|
|
25
25
|
|
|
26
26
|
### First setup
|
|
27
27
|
|
|
@@ -58,6 +58,13 @@ chi bootstrap # try localhost 8080, 11434, 1234, 8000
|
|
|
58
58
|
none. A server already in the file writes nothing; a file in YAML flow style
|
|
59
59
|
or with anchors gets the lines printed to paste instead. `--dry-run` shows
|
|
60
60
|
what it would write.
|
|
61
|
+
- **The bundles.** Unless writing the config failed, it installs the system
|
|
62
|
+
bundle and the `core` profile (loop-guard, check-in, guardrails), one line
|
|
63
|
+
each; on a terminal it then asks `Also install dev (known-names, mcp, btw,
|
|
64
|
+
skills, source-links)? [y/N]`. Run again, it installs nothing new: a bundle
|
|
65
|
+
you uninstalled stays out ([Bundle profiles](memory.md#bundle-profiles-core-and-dev)).
|
|
66
|
+
A bundle that fails to install makes the exit 1. `--dry-run` says what it
|
|
67
|
+
would install.
|
|
61
68
|
|
|
62
69
|
### Updating
|
|
63
70
|
|
|
@@ -69,10 +76,11 @@ chi (gem) 0.2.0 0.3.0 updated
|
|
|
69
76
|
system bundle 0.2.0 0.3.0 updated (kept your edits in identity.md: chi bundle diff samagotchi-system identity.md)
|
|
70
77
|
btw 0.1.1 up to date
|
|
71
78
|
known-names 0.1.0 0.1.1 updated
|
|
79
|
+
core 0.1.0 0.2.0 updated (+ new-bundle)
|
|
72
80
|
infra_tools 1.0.0 skipped (not from chi)
|
|
73
81
|
Chi Helper 0.2.0 up to date (launch file refreshed)
|
|
74
82
|
workers 2 live on 0.2.0: they move to 0.3.0 at idle exit (30 min) or chi sessions stop 2ea8c1f0 91b0d2aa
|
|
75
|
-
Also shipped, not installed:
|
|
83
|
+
Also shipped, not installed: dev (mcp, skills, source-links) (chi bundle install NAME)
|
|
76
84
|
done
|
|
77
85
|
```
|
|
78
86
|
|
|
@@ -97,6 +105,10 @@ done
|
|
|
97
105
|
A bundle of the same name from elsewhere (a zip, git) is skipped ("not from
|
|
98
106
|
chi"), a newer installed one is left, one whose new version needs a newer
|
|
99
107
|
chi is skipped, and bundles you didn't install stay uninstalled.
|
|
108
|
+
- **Profiles** (`core`, `dev`): an installed one installs the bundles a new
|
|
109
|
+
chi added to it (`updated (+ name)`), even at the same version, and retries
|
|
110
|
+
one that failed before; one you uninstalled stays out. See [Bundle
|
|
111
|
+
profiles](memory.md#bundle-profiles-core-and-dev).
|
|
100
112
|
- **The desktop helper** (macOS) is rebuilt and restarted only when its Swift
|
|
101
113
|
sources changed (or the Ruby it runs moved); otherwise only its launch file
|
|
102
114
|
is refreshed. See [Desktop helper](desktop.md).
|
|
@@ -206,7 +218,8 @@ of UIs at once: the Web UI and attached terminals (`chi`, `--resume`,
|
|
|
206
218
|
`--attach`). They all see the same turns as they happen, and any of them can send
|
|
207
219
|
a prompt, also while a turn runs (it merges into that turn as steering). The
|
|
208
220
|
first answer to an `ask_user_question` wins; the other UIs close their widget.
|
|
209
|
-
An empty answer dismisses the question in every UI.
|
|
221
|
+
An empty answer dismisses the question in every UI. The model is then told
|
|
222
|
+
not to go ahead with what it asked about, or change anything else, and to wait.
|
|
210
223
|
|
|
211
224
|
In an attached terminal:
|
|
212
225
|
|
|
@@ -445,7 +458,9 @@ card, so it stays on screen without scrolling: a status row (what it is
|
|
|
445
458
|
doing, the step, the elapsed time), your prompt on one line, the newest
|
|
446
459
|
narration sentence (else the newest thinking one, in italics; click it for
|
|
447
460
|
the step's reasoning), the running tool with what it does, and the last
|
|
448
|
-
three calls. A
|
|
461
|
+
three calls. A plugin's or hook's notice and a plugin's nudge ("check-in
|
|
462
|
+
nudged the model") show ahead of those calls for about 4 s, as their rows
|
|
463
|
+
wait in the closed block. A card that needs you (a question, an approval, check-in)
|
|
449
464
|
sits in the stage too. The chip under it, `N steps · M tool calls` with one
|
|
450
465
|
tick per call, opens the turn view's block in place (newest step first
|
|
451
466
|
while it runs; a tick opens its step). `▾` folds the stage to its status
|
data/docs/configuration.md
CHANGED
|
@@ -687,6 +687,23 @@ hosts:
|
|
|
687
687
|
`models:` wins over `hosts:`. On a native host, `vision: true` skips only the
|
|
688
688
|
modalities check: without a media marker the prompt can't carry an image.
|
|
689
689
|
|
|
690
|
+
## Desktop helper: agents in kitty
|
|
691
|
+
|
|
692
|
+
The desktop helper (`chi desktop`) can also paste into agent CLIs (claude,
|
|
693
|
+
codex, …) running in [kitty](https://sw.kovidgoyal.net/kitty/) windows. It
|
|
694
|
+
lists them only with `kitty.listen_on` set: copy the value of `listen_on` from
|
|
695
|
+
your `kitty.conf` as it is (kitty must also have `allow_remote_control` on).
|
|
696
|
+
|
|
697
|
+
```yaml
|
|
698
|
+
kitty:
|
|
699
|
+
listen_on: unix:/tmp/kitty.${KITTY_PID} # as in kitty.conf; unset = no kitty targets
|
|
700
|
+
binary: /Applications/kitty.app/Contents/MacOS/kitty # the default
|
|
701
|
+
agents: [claude, codex] # the programs listed; "*" = every window
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
The helper reads these from its launch file: after editing them run
|
|
705
|
+
`chi desktop upgrade` (or `chi update`). See [Desktop helper](desktop.md#agents-in-kitty).
|
|
706
|
+
|
|
690
707
|
## Project specific description
|
|
691
708
|
|
|
692
709
|
If an AGENT.md file is present in the project root, samagotchi injects its
|
|
@@ -756,6 +773,9 @@ described in their own sections.
|
|
|
756
773
|
| `update.gem` | `true` | | `false`: `chi update` never installs a newer gem (`--no-gem` for one run). See [CLI: Updating](cli.md#updating). |
|
|
757
774
|
| `update.bundles` | `true` | | `false`: `chi update` leaves the shipped bundles to `chi bundle upgrade` (`--no-bundles`). |
|
|
758
775
|
| `update.desktop` | `true` | | `false`: `chi update` leaves the desktop helper alone (`--no-desktop`). |
|
|
776
|
+
| `kitty.listen_on` | none | | The desktop helper's kitty socket, copied from `kitty.conf`; unset = no kitty targets. See "Desktop helper: agents in kitty". |
|
|
777
|
+
| `kitty.binary` | `/Applications/kitty.app/Contents/MacOS/kitty` | | The kitty the helper runs `kitty @` with. |
|
|
778
|
+
| `kitty.agents` | `claude\|codex\|gemini\|aider\|opencode\|cursor-agent\|amp\|goose` | | Foreground programs whose kitty windows the helper lists, `\|`-separated (a YAML list works too); `*` lists every window. |
|
|
759
779
|
| `read.truncate_at_bytes` | `65536` | yes | A `read` result larger than this is cut to a preview. |
|
|
760
780
|
| `read.preview_bytes` | `12288` | yes | Size of that preview. |
|
|
761
781
|
| `read.hard_max_bytes` | `2097152` | yes | Largest file `read` opens. |
|
data/docs/desktop.md
CHANGED
|
@@ -20,6 +20,8 @@ sessions. Keys:
|
|
|
20
20
|
- **⏎** sends a message: `chi send -m <the line>` with the text as quoted context above it. With the line empty,
|
|
21
21
|
the text is the message.
|
|
22
22
|
- **⌘⏎** sends a note: `chi note`, the line (if any) then the text.
|
|
23
|
+
- **⌥⏎** pastes into an agent in a kitty window without pressing Enter (see [Agents in kitty](#agents-in-kitty));
|
|
24
|
+
it is not a newline.
|
|
23
25
|
- **⇧⏎** is a newline, in either field. Esc closes.
|
|
24
26
|
|
|
25
27
|
The list shows the sessions a worker runs now, then, under a "recent" divider, up to 3 stopped ones (dimmed, with
|
|
@@ -64,6 +66,53 @@ thumbnails sit under the message line, 48 px high, the file name as a tooltip; h
|
|
|
64
66
|
the panel closes; files from Finder are sent as they are, never touched. A send with images may take up to 30 s
|
|
65
67
|
(converting, a worker starting) before the panel gives up.
|
|
66
68
|
|
|
69
|
+
## Agents in kitty
|
|
70
|
+
|
|
71
|
+
The panel can also send to agent CLIs (claude, codex, gemini, aider, …) running in
|
|
72
|
+
[kitty](https://sw.kovidgoyal.net/kitty/) windows, next to chi sessions. kitty only, through its remote control;
|
|
73
|
+
off until you set it up:
|
|
74
|
+
|
|
75
|
+
1. In `kitty.conf`, remote control on and a socket: `allow_remote_control yes` and a `listen_on unix:…` line.
|
|
76
|
+
2. Copy that `listen_on` value as it is into chi's config.yml:
|
|
77
|
+
|
|
78
|
+
```yaml
|
|
79
|
+
kitty:
|
|
80
|
+
listen_on: unix:/tmp/kitty.${KITTY_PID}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`kitty.agents` picks the programs listed (default `claude|codex|gemini|aider|opencode|cursor-agent|amp|goose`, a
|
|
84
|
+
YAML list works too; `"*"` lists every window, shells too), `kitty.binary` the kitty to run (default
|
|
85
|
+
`/Applications/kitty.app/Contents/MacOS/kitty`). See [Configuration](configuration.md#desktop-helper-agents-in-kitty).
|
|
86
|
+
3. Run `chi desktop upgrade` (or `chi update`): the helper reads these from its launch file, so **after every edit of
|
|
87
|
+
`kitty:`** run it again. No rebuild when only the settings changed.
|
|
88
|
+
|
|
89
|
+
The helper finds the sockets itself: kitty adds `-<pid>` to a `listen_on` path from kitty.conf (and fills in
|
|
90
|
+
`{kitty_pid}`), `~` and variables the helper knows are expanded, others (`${KITTY_PID}`) stay as written, as kitty
|
|
91
|
+
keeps them. Several kitty instances all count. Only `unix:` sockets work.
|
|
92
|
+
|
|
93
|
+
The panel lists the windows whose foreground program is one of the agents under a **kitty** divider, between live
|
|
94
|
+
and recent sessions: "claude · <its title>", the folder below, a terminal icon, and a ⌘ number like the sessions.
|
|
95
|
+
With the settings on but no agent running, one dim line says "no agents in kitty". A window counts when a foreground
|
|
96
|
+
process's program is an agent, also as the script of `node …/codex` (that match is untested for codex and gemini).
|
|
97
|
+
|
|
98
|
+
- **⏎** pastes into every chosen window and presses Enter: the text as a `> ` quote, a blank line, your message, then
|
|
99
|
+
one image path per line. Chosen together with chi sessions, each gets the message and the panel shows one line
|
|
100
|
+
("Sent. · sent to claude · samagotchi").
|
|
101
|
+
- **⌥⏎** only pastes (no Enter), and only when every chosen target is a kitty window: build up several screenshots,
|
|
102
|
+
each with its own line, then press Enter in kitty. The footer shows **Paste ⌥⏎** instead of Note then.
|
|
103
|
+
- **⌘⏎** with a kitty window chosen beeps: "Notes are for chi sessions". ⌥⏎ with a chi session: "Paste only is
|
|
104
|
+
for kitty windows".
|
|
105
|
+
- **Images:** a screenshot or other image data is copied to `$TMPDIR/chi-helper-sent/<uuid>.png` (or `.tiff`) and
|
|
106
|
+
its path pasted; the agent reads it from there (claude attaches it as `[Image #1]`). These files are deleted a week
|
|
107
|
+
later, at the helper's next start. Finder files are pasted with their own path, `'…'`-quoted when they hold spaces.
|
|
108
|
+
- The paste arrives as one bracketed paste, so the agent takes newlines as text (claude shows long ones as
|
|
109
|
+
`[Pasted text]`). With `agents: "*"` beware of a program that doesn't turn bracketed paste on: the newlines would
|
|
110
|
+
act as Enter.
|
|
111
|
+
- No delivery confirmation: before pasting, the helper checks the window is still there ("window closed" if not);
|
|
112
|
+
it can't know whether the agent read it. The 16 KB cap is chi's; a kitty window takes more.
|
|
113
|
+
- Focus goes back to the app you came from, as for sessions. The last choice (kitty windows too) is preselected
|
|
114
|
+
while listed; ⌘ numbers shift as windows come and go.
|
|
115
|
+
|
|
67
116
|
## Install
|
|
68
117
|
|
|
69
118
|
```sh
|
data/docs/guardrails.md
CHANGED
|
@@ -146,7 +146,7 @@ without it.
|
|
|
146
146
|
## The guardrails bundle
|
|
147
147
|
|
|
148
148
|
```sh
|
|
149
|
-
chi bundle install guardrails
|
|
149
|
+
chi bundle install guardrails # chi bootstrap installs it, with the core profile
|
|
150
150
|
```
|
|
151
151
|
|
|
152
152
|
installs a default rule set plus a short memory telling the model not to
|
|
@@ -164,7 +164,7 @@ denies every call until the bundle is reinstalled.
|
|
|
164
164
|
## The known-names bundle
|
|
165
165
|
|
|
166
166
|
```sh
|
|
167
|
-
chi bundle install known-names
|
|
167
|
+
chi bundle install known-names # or chi bundle install dev
|
|
168
168
|
```
|
|
169
169
|
|
|
170
170
|
installs one `before_tool_call` hook and a short memory. A local model that
|
data/docs/hooks.md
CHANGED
|
@@ -416,7 +416,7 @@ Notes:
|
|
|
416
416
|
- Ordering: bundle hooks fire by `(priority, bundle_name, hook_name)` (lower priority first), then plain `config.yml` hooks in registration order.
|
|
417
417
|
- Settings: a hook class with `initialize(settings = {})` gets the bundle's section of `config.yml` `bundles:` (see [Settings](#settings)).
|
|
418
418
|
- A bundle can also ship a `plugin.rb` whose `chi.on(event)` blocks are bundle hooks too, next to commands and tools; see [Plugins](plugins.md).
|
|
419
|
-
- Shipped bundles: `chi bundle install guardrails` (rules, see [Guardrails](guardrails.md#the-guardrails-bundle)) and `chi bundle install known-names` (a hook, see [Guardrails](guardrails.md#the-known-names-bundle)), `chi bundle install source-links` (a hook: announces source refs, see [The source-links bundle](#the-source-links-bundle)), `chi bundle install btw` (a plugin: `/btw`, see [Plugins](plugins.md#the-btw-bundle)), `chi bundle install mcp` (a plugin: tools from MCP servers, see [Plugins](plugins.md#the-mcp-bundle)) `chi bundle install loop-guard` (a plugin: breaks tool-call loops, see [Plugins](plugins.md#the-loop-guard-bundle)), `chi bundle install check-in` (a plugin: checks on a long turn, see [Plugins](plugins.md#the-check-in-bundle)) and `chi bundle install skills` (a plugin: `/skill`, versions of skills, see [Plugins](plugins.md#the-skills-bundle)).
|
|
419
|
+
- Shipped bundles (each in a profile: `core` or `dev`, see [Bundle profiles](memory.md#bundle-profiles-core-and-dev)): `chi bundle install guardrails` (rules, see [Guardrails](guardrails.md#the-guardrails-bundle)) and `chi bundle install known-names` (a hook, see [Guardrails](guardrails.md#the-known-names-bundle)), `chi bundle install source-links` (a hook: announces source refs, see [The source-links bundle](#the-source-links-bundle)), `chi bundle install btw` (a plugin: `/btw`, see [Plugins](plugins.md#the-btw-bundle)), `chi bundle install mcp` (a plugin: tools from MCP servers, see [Plugins](plugins.md#the-mcp-bundle)) `chi bundle install loop-guard` (a plugin: breaks tool-call loops, see [Plugins](plugins.md#the-loop-guard-bundle)), `chi bundle install check-in` (a plugin: checks on a long turn, see [Plugins](plugins.md#the-check-in-bundle)) and `chi bundle install skills` (a plugin: `/skill`, versions of skills, see [Plugins](plugins.md#the-skills-bundle)).
|
|
420
420
|
- Installing a bundle executes its hook code at `Engine` startup. Only install bundles you trust, as you would a gem. Hooks are **not** executed at install time (copy-only); they are `module_eval`'d at `Engine.new` inside per-bundle `Samagotchi::Bundles::<name>` namespaces (no top-level `require` collisions). Keep hook files side-effect-free at load time; do work in `#call` — top-level side effects (require, IO, `at_exit`, global assignment) run once per `Engine.new` (class redefinition is idempotent).
|
|
421
421
|
|
|
422
422
|
Lifecycle:
|
|
@@ -428,7 +428,7 @@ Lifecycle:
|
|
|
428
428
|
## The source-links bundle
|
|
429
429
|
|
|
430
430
|
```sh
|
|
431
|
-
chi bundle install source-links
|
|
431
|
+
chi bundle install source-links # or chi bundle install dev
|
|
432
432
|
```
|
|
433
433
|
|
|
434
434
|
installs one `after_turn` hook and a short memory. When the model's answer
|
|
@@ -25,7 +25,9 @@ Emission behavior:
|
|
|
25
25
|
synthetic char-based estimate (`src=estimate`). The guidance text is dynamic
|
|
26
26
|
and escalates with the bucket: healthy → proceed normally; moderate → prefer
|
|
27
27
|
targeted/range reads; elevated → be concise, avoid large re-reads; critical →
|
|
28
|
-
summarize aggressively and delegate broad work to subagents.
|
|
28
|
+
summarize aggressively and delegate broad work to subagents. With custom
|
|
29
|
+
`context.status_thresholds` the top bucket reads critical, the one below it
|
|
30
|
+
elevated, the next moderate, and the first bucket (and the rest below) healthy.
|
|
29
31
|
|
|
30
32
|
Configuration:
|
|
31
33
|
|
data/docs/memory.md
CHANGED
|
@@ -47,7 +47,9 @@ chi is told about skills by the system bundle (`identity.md`, every turn, and
|
|
|
47
47
|
one ("I like how we did that") it may ask first.
|
|
48
48
|
- **Following.** The skill's index line (the `description:` of its
|
|
49
49
|
`memory_write`, "Release a new version of this repo: …") is in every
|
|
50
|
-
prompt, so next time chi reads the skill and follows it.
|
|
50
|
+
prompt, so next time chi reads the skill and follows it. When something
|
|
51
|
+
looks unexpected on the way (a failing check, a warning the skill doesn't
|
|
52
|
+
mention), chi stops, says what it saw and asks how to go on.
|
|
51
53
|
- **Updating.** When a step turned out different (a renamed script, an extra
|
|
52
54
|
step), chi fixes the skill in the same turn: those steps changed, the rest
|
|
53
55
|
kept, a dated Changelog line added. No confirmation.
|
|
@@ -74,6 +76,51 @@ skills`, [docs/plugins.md](plugins.md#the-skills-bundle)) adds `/skill save`,
|
|
|
74
76
|
one-line diff after each update, and nudges a model that skips a failing
|
|
75
77
|
step instead of fixing the skill.
|
|
76
78
|
|
|
79
|
+
## Bundle profiles: core and dev
|
|
80
|
+
|
|
81
|
+
Every bundle chi ships (the system bundle aside) belongs to one of two
|
|
82
|
+
profiles, meta bundles that install a set of them:
|
|
83
|
+
|
|
84
|
+
| profile | bundles |
|
|
85
|
+
|---|---|
|
|
86
|
+
| `core` | loop-guard, check-in, guardrails: the recommended safety set |
|
|
87
|
+
| `dev` | known-names, mcp, btw, skills, source-links |
|
|
88
|
+
|
|
89
|
+
`chi bootstrap` installs `core` and, on a terminal, offers `dev`. An existing
|
|
90
|
+
install gets neither by itself: `chi bundle install core` (or `dev`).
|
|
91
|
+
|
|
92
|
+
A profile's `manifest.yml` has `includes:` (bundle names) and nothing else to
|
|
93
|
+
install; its dir holds only that file. What installing it does follows one
|
|
94
|
+
rule:
|
|
95
|
+
|
|
96
|
+
installed now = the profile's bundles − the ones it recorded − the ones installed
|
|
97
|
+
|
|
98
|
+
- **First install:** each bundle that isn't installed is installed (from the
|
|
99
|
+
bundles chi ships, never a same-named dir in the cwd); one you installed by
|
|
100
|
+
hand is recorded without reinstalling it.
|
|
101
|
+
- **Again, or `chi bundle upgrade core`:** only a bundle new to the profile
|
|
102
|
+
is installed. One you uninstalled (`chi bundle uninstall check-in`) was
|
|
103
|
+
recorded, so it stays out; re-running `chi bootstrap` changes nothing.
|
|
104
|
+
- **A new chi adds a bundle to a profile:** `chi update` installs just that
|
|
105
|
+
one ("core … updated (+ name)"). A bundle dropped from a profile stays
|
|
106
|
+
installed.
|
|
107
|
+
- **A bundle this chi is too old for** (its `requires_chi`) is skipped with a
|
|
108
|
+
note, and one that fails is reported (exit 1); neither is recorded, so the
|
|
109
|
+
next `chi update` or install tries again.
|
|
110
|
+
- The profile never pins its bundles' versions: each one upgrades on its own,
|
|
111
|
+
as before (`chi update`, `chi bundle upgrade NAME`).
|
|
112
|
+
- Only the profiles chi ships expand: `includes:` in a bundle from anywhere
|
|
113
|
+
else is ignored.
|
|
114
|
+
|
|
115
|
+
`chi bundle uninstall core` uninstalls each bundle it recorded that is still
|
|
116
|
+
installed (one you installed by hand before core too), then core. A bundle
|
|
117
|
+
with an edited memory file is kept and named (`--force` removes it), the
|
|
118
|
+
rest go, and core stays until it is gone (exit 1). `--scope project` is
|
|
119
|
+
refused for a profile. `chi bundle list` shows an installed profile's
|
|
120
|
+
bundles (`includes=`) and the ones you left out (`left out=`), a profile not
|
|
121
|
+
installed with its bundles on its line, and `chi bundle status core` lists
|
|
122
|
+
each one.
|
|
123
|
+
|
|
77
124
|
## Bundles that need outside commands
|
|
78
125
|
|
|
79
126
|
A bundle's memory can rely on a command chi doesn't ship, such as a GitHub
|
data/docs/plugins.md
CHANGED
|
@@ -596,7 +596,7 @@ ctx.sessions.read(id) # => {id:, title:, status:, parent_id:, running:, message
|
|
|
596
596
|
|
|
597
597
|
## The btw bundle
|
|
598
598
|
|
|
599
|
-
`chi bundle install btw` installs the bundle shipped with chi. It is written
|
|
599
|
+
`chi bundle install btw` (or the `dev` profile) installs the bundle shipped with chi. It is written
|
|
600
600
|
only against this API (`lib/samagotchi/bundles/btw/plugin.rb`).
|
|
601
601
|
|
|
602
602
|
- `/btw <question>` asks the session's model a side question about the
|
|
@@ -621,7 +621,7 @@ only against this API (`lib/samagotchi/bundles/btw/plugin.rb`).
|
|
|
621
621
|
|
|
622
622
|
## The mcp bundle
|
|
623
623
|
|
|
624
|
-
`chi bundle install mcp` installs the bundle shipped with chi. It is written
|
|
624
|
+
`chi bundle install mcp` (or the `dev` profile) installs the bundle shipped with chi. It is written
|
|
625
625
|
only against this API (`lib/samagotchi/bundles/mcp/plugin.rb`), and adds
|
|
626
626
|
tools from [MCP](https://modelcontextprotocol.io) servers. It has no memory
|
|
627
627
|
file, so it costs the prompt nothing but its tools. Stdio servers only, for
|
|
@@ -733,7 +733,8 @@ bundles:
|
|
|
733
733
|
|
|
734
734
|
## The loop-guard bundle
|
|
735
735
|
|
|
736
|
-
`chi bundle install loop-guard` installs the bundle shipped with chi
|
|
736
|
+
`chi bundle install loop-guard` installs the bundle shipped with chi (`chi
|
|
737
|
+
bootstrap` installs it with the `core` profile). It is
|
|
737
738
|
written only against this API (`lib/samagotchi/bundles/loop-guard/plugin.rb`),
|
|
738
739
|
with `chi.on` hooks, and has no memory file. It guards two kinds of loop:
|
|
739
740
|
repeated tool calls (below) and [thinking that repeats
|
|
@@ -853,7 +854,8 @@ Not caught (yet):
|
|
|
853
854
|
|
|
854
855
|
## The check-in bundle
|
|
855
856
|
|
|
856
|
-
`chi bundle install check-in` installs the bundle shipped with chi
|
|
857
|
+
`chi bundle install check-in` installs the bundle shipped with chi (`chi
|
|
858
|
+
bootstrap` installs it with the `core` profile). It is
|
|
857
859
|
written only against this API (`lib/samagotchi/bundles/check-in/plugin.rb`):
|
|
858
860
|
`chi.on` hooks, `ctx.card`, `ctx.steer`, `ctx.stop_turn` and one anytime
|
|
859
861
|
command. It has no memory file.
|
|
@@ -911,7 +913,7 @@ with a line and carry on unchanged.
|
|
|
911
913
|
|
|
912
914
|
## The skills bundle
|
|
913
915
|
|
|
914
|
-
`chi bundle install skills` installs the bundle shipped with chi
|
|
916
|
+
`chi bundle install skills` (or the `dev` profile) installs the bundle shipped with chi
|
|
915
917
|
(`lib/samagotchi/bundles/skills/plugin.rb`): one anytime command, `chi.on`
|
|
916
918
|
hooks, `ctx.sessions.send`, `ctx.notify` and `event[:steer]`. It has no memory
|
|
917
919
|
file; skills themselves work without it ([docs/memory.md](memory.md#skills)).
|
data/docs/releasing.md
CHANGED
|
@@ -13,13 +13,17 @@ can run the whole release; the user approves the notes before the tag and the
|
|
|
13
13
|
and `rake release:check` enforce it). The system bundle upgrades itself when
|
|
14
14
|
chi starts.
|
|
15
15
|
- **Every other shipped bundle** (btw, check-in, guardrails, known-names,
|
|
16
|
-
loop-guard, mcp, skills, source-links) has its own semver in its
|
|
16
|
+
loop-guard, mcp, skills, source-links, and the profiles core and dev) has its own semver in its
|
|
17
17
|
`manifest.yml` and, when it needs a newer chi, a `requires_chi:` line. They never upgrade by themselves: users run
|
|
18
18
|
`chi update` (all of them, with the gem) or `chi bundle upgrade NAME`. So:
|
|
19
19
|
- a change to a bundle's files bumps that bundle's `version:`
|
|
20
20
|
(`rake bundles:check` fails otherwise, once there's a tag to compare with);
|
|
21
21
|
- a bundle that uses something new in chi raises its `requires_chi` in the
|
|
22
22
|
same change; `release:bump` never touches `requires_chi`;
|
|
23
|
+
- a new shipped bundle joins a profile (`includes:` in `core` or `dev`; a
|
|
24
|
+
spec checks every bundle is in exactly one), and that profile's version
|
|
25
|
+
goes up: `chi update` then installs only the new bundle for those who
|
|
26
|
+
have the profile;
|
|
23
27
|
- the release notes say "run `chi update`" and name the bundles whose
|
|
24
28
|
version moved (what changed in each), not per-bundle upgrade steps.
|
|
25
29
|
- Pre-1.0: config and commands may change in a minor version (0.2 → 0.3); a
|
data/docs/sessions.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Sessions
|
|
2
2
|
|
|
3
|
-
Sessions are plain files — no DB. Each session is `~/.local/state/samagotchi/sessions/<uuid>.json` (XDG-aware via `XDG_STATE_HOME`) plus a sidecar dir `<uuid>/` with `input/`/`notes/`/`output/`/`
|
|
3
|
+
Sessions are plain files — no DB. Each session is `~/.local/state/samagotchi/sessions/<uuid>.json` (XDG-aware via `XDG_STATE_HOME`) plus a sidecar dir `<uuid>/` with `input/`/`notes/`/`output/`/`owner.lock`/`bridge.json` (`Session.session_dir`). Besides the messages and turn state, the JSON keeps `used_memory_names` (what the session read), `preloaded_memory_names` (`--memory`) and `muted_memory_names` (`--mute`), the last two written before the worker starts so a respawn builds the same prompt, and `parent_id`, the session that delegated this one (see *Delegating*; `null` otherwise); files from before those keys read as empty lists and no parent.
|
|
4
4
|
|
|
5
5
|
**Retention (file-based, opt-out via env):**
|
|
6
6
|
|
|
@@ -15,7 +15,7 @@ A session is deleted if **expired by age OR overflow by count** (unless `keep_st
|
|
|
15
15
|
|
|
16
16
|
**Worker idle exit:** a background worker (plain `chi`, Web UI sessions) exits after `SAMAGOTCHI_SESSION_IDLE_EXIT_MINUTES` (`session.idle_exit_minutes`, default `30`, `0`=never) with no turn running or queued, no client on its stream (an open web tab or attached terminal keeps it), no reminder registered and no continue offer waiting for an answer. It removes `bridge.json` and frees `owner.lock`; the next prompt or `--attach` wakes a new worker (`WorkerIdleExit`, `SessionManager.run_session_loop`). A client can also ask the worker to exit right away (Bridge `POST /session/:id/exit`; `/exit` in an attached terminal does, `/detach` doesn't): the same rules apply without the timeout (even with `0`), and the asker's own stream doesn't count. The reply names what keeps it up. The session's status stays as it was, not `stopped`.
|
|
17
17
|
|
|
18
|
-
**Empty sessions:** a session nothing happened in is deleted when it is left, so `chi` → `/exit` leaves no trace. Empty means: no messages, no turn tried (a failed turn leaves a `last_prompt` and keeps it), no memory used, no pending question, nothing in `input/`, `notes/` or `images/`, nothing in its dir beyond the worker's skeleton, mode `assist`, and the default model. A session on another model (`/model`, `--model`) counts as prepared and stays (`SessionManager.
|
|
18
|
+
**Empty sessions:** a session nothing happened in is deleted when it is left, so `chi` → `/exit` leaves no trace. Empty means: no messages, no turn tried (a failed turn leaves a `last_prompt` and keeps it), no memory used, no pending question, nothing in `input/`, `notes/` or `images/`, nothing in its dir beyond the worker's skeleton, mode `assist`, and the default model. A session on another model (`/model`, `--model`) counts as prepared and stays (`SessionManager.discardable?`). The worker checks as it idle-exits or leaves on a client's request and deletes it once its lock is free, checking again first. The attached `/exit` then says `Detached; the session was empty, so it is discarded.` The REPL (`--no-shared`) does the same at `/exit` or Ctrl-D. The retention sweep catches those killed first: empty sessions over an hour old with no live owner, whatever `DAYS`/`MAX`, and skeleton-only dirs with no `*.json`. `session.keep_empty: true` (env `SAMAGOTCHI_SESSION_KEEP_EMPTY`) turns all of it off.
|
|
19
19
|
|
|
20
20
|
**Attached by default:** plain `chi` and `chi --resume ID` run their session in a background worker and attach to it (`session.shared`, default `true`; env `SAMAGOTCHI_SESSION_SHARED`). The worker runs in the session's `working_directory`, so `!commands` and tools see the directory the session was started in. `session.shared: false` keeps the in-process REPL, `--no-shared` does for one run; a session the REPL has open can't be attached ("close it there first"). See [CLI: Sharing a session](cli.md#sharing-a-session).
|
|
21
21
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
require "json"
|
|
4
4
|
require "fileutils"
|
|
5
5
|
require "time"
|
|
6
|
+
require_relative "atomic_file"
|
|
6
7
|
|
|
7
8
|
require_relative "session"
|
|
8
9
|
|
|
@@ -76,13 +77,12 @@ module Samagotchi
|
|
|
76
77
|
end
|
|
77
78
|
|
|
78
79
|
def self.write(session_id, record, state_dir:)
|
|
79
|
-
return false unless File.exist?(
|
|
80
|
+
return false unless File.exist?(Session.session_file(session_id, state_dir: state_dir))
|
|
80
81
|
|
|
81
82
|
dir = Session.session_dir(session_id, state_dir: state_dir)
|
|
82
83
|
FileUtils.mkdir_p(dir)
|
|
83
84
|
path = File.join(dir, FILE)
|
|
84
|
-
|
|
85
|
-
File.rename("#{path}.tmp", path)
|
|
85
|
+
AtomicFile.write(path, JSON.generate(record))
|
|
86
86
|
true
|
|
87
87
|
end
|
|
88
88
|
private_class_method :write
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "securerandom"
|
|
5
|
+
|
|
6
|
+
module Samagotchi
|
|
7
|
+
# Replace a file whole: the content goes to a temporary file next to it
|
|
8
|
+
# (<path>.<pid>.<random>.tmp, unique per process and call, so a worker and
|
|
9
|
+
# a CLI writing the same file never share one), which is then renamed over
|
|
10
|
+
# +path+. A reader sees the old file or the new one, never half of one, and
|
|
11
|
+
# two writers racing leave one of the two contents. The temporary name ends
|
|
12
|
+
# in .tmp, so globs for *.json or *.md never list it. On failure it is
|
|
13
|
+
# removed and the error raised; +path+ is left as it was.
|
|
14
|
+
module AtomicFile
|
|
15
|
+
module_function
|
|
16
|
+
|
|
17
|
+
# @param path [String] the file to replace (its folder must exist)
|
|
18
|
+
# @param content [String] written as-is, byte for byte
|
|
19
|
+
# @param perm [Integer, nil] the new file's mode; nil = a fresh file's
|
|
20
|
+
# (0666 less the umask), like File.write
|
|
21
|
+
# @return [String] +path+
|
|
22
|
+
def write(path, content, perm: nil)
|
|
23
|
+
tmp = "#{path}.#{Process.pid}.#{SecureRandom.hex(4)}.tmp"
|
|
24
|
+
File.open(tmp, File::WRONLY | File::CREAT | File::EXCL | File::BINARY, perm || 0o666) do |io|
|
|
25
|
+
io.write(content)
|
|
26
|
+
end
|
|
27
|
+
File.chmod(perm, tmp) if perm
|
|
28
|
+
File.rename(tmp, path)
|
|
29
|
+
path
|
|
30
|
+
ensure
|
|
31
|
+
FileUtils.rm_f(tmp) if tmp && File.exist?(tmp)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../memory_bundle/manifest"
|
|
4
|
+
require_relative "../memory_bundle/profile"
|
|
5
|
+
require_relative "../memory_bundle/source"
|
|
6
|
+
require_relative "../memory_bundle/system_bundle"
|
|
7
|
+
|
|
8
|
+
module Samagotchi
|
|
9
|
+
module Bootstrap
|
|
10
|
+
# chi bootstrap's bundles step: the system bundle and the shipped
|
|
11
|
+
# profiles, into the config dir the process ENV names (Provenance and
|
|
12
|
+
# the Installer resolve their paths from it, not from bootstrap's env:).
|
|
13
|
+
# A seam: in-process specs pass their own.
|
|
14
|
+
class Bundles
|
|
15
|
+
def initialize(shipped_dir: MemoryBundle::SourceNormalizer::SHIPPED_DIR)
|
|
16
|
+
@shipped_dir = shipped_dir
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# @return [MemoryBundle::SystemBundle::Result]
|
|
20
|
+
def sync_system(dry_run: false) = MemoryBundle::SystemBundle.sync(dry_run: dry_run)
|
|
21
|
+
|
|
22
|
+
# @return [MemoryBundle::Profile::InstallResult]
|
|
23
|
+
def install(profile, dry_run: false)
|
|
24
|
+
MemoryBundle::Profile.install(dir(profile), shipped_dir: @shipped_dir, dry_run: dry_run)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# The members installing +profile+ would install now.
|
|
28
|
+
def to_install(profile)
|
|
29
|
+
MemoryBundle::Profile.to_install(MemoryBundle::Manifest.read(dir: dir(profile)))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def dir(profile) = File.join(@shipped_dir, profile)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|