samagotchi 0.56.0 → 0.58.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 +54 -1
- data/README.md +1 -1
- data/docs/cli.md +1 -1
- data/docs/configuration.md +5 -3
- data/docs/hooks.md +2 -2
- data/docs/memory.md +1 -1
- data/docs/plugins.md +63 -0
- data/docs/sessions.md +2 -2
- data/lib/samagotchi/bootstrap/config_writer.rb +2 -0
- data/lib/samagotchi/bootstrap/probe.rb +3 -0
- data/lib/samagotchi/bootstrap_command.rb +2 -0
- data/lib/samagotchi/broadcast_command.rb +2 -0
- data/lib/samagotchi/bundles/core/manifest.yml +2 -1
- data/lib/samagotchi/bundles/mcp/manifest.yml +2 -2
- data/lib/samagotchi/bundles/mcp/plugin.rb +15 -1
- data/lib/samagotchi/bundles/slow-commands/manifest.yml +11 -0
- data/lib/samagotchi/bundles/slow-commands/plugin.rb +320 -0
- data/lib/samagotchi/bundles/system/manifest.yml +2 -2
- data/lib/samagotchi/bundles/system/memory_guide.md +1 -1
- data/lib/samagotchi/context_command.rb +2 -0
- data/lib/samagotchi/desktop/macos.rb +3 -0
- data/lib/samagotchi/engine.rb +2 -2
- data/lib/samagotchi/gem_update.rb +3 -0
- data/lib/samagotchi/guardrails/shell_lex.rb +8 -0
- data/lib/samagotchi/guardrails/shell_paths.rb +3 -0
- data/lib/samagotchi/host_registry.rb +15 -0
- data/lib/samagotchi/installed_versions.rb +3 -0
- data/lib/samagotchi/model_profile.rb +14 -3
- data/lib/samagotchi/plugin/context.rb +5 -0
- data/lib/samagotchi/redacted_inspect.rb +43 -0
- data/lib/samagotchi/session_commands.rb +5 -4
- data/lib/samagotchi/terminal_ui/attached_loop.rb +13 -8
- data/lib/samagotchi/terminal_ui.rb +14 -5
- data/lib/samagotchi/tool_runner.rb +14 -3
- data/lib/samagotchi/tools/execute.rb +3 -2
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +14 -6
- data/lib/samagotchi/web/public/app.js +31 -14
- data/lib/samagotchi/web/public/index.html +32 -5
- data/lib/samagotchi/web/public/question_card.js +5 -3
- data/lib/samagotchi/web/public/question_cards.js +21 -1
- data/lib/samagotchi/web/public/read_view.js +74 -0
- data/lib/samagotchi/web/public/ref_menu.js +188 -0
- data/lib/samagotchi/web/public/ref_scan.js +216 -0
- data/lib/samagotchi/web/public/refs.js +283 -54
- data/lib/samagotchi/web/public/stage_view.js +21 -0
- data/lib/samagotchi/web/public/turn_html.js +4 -2
- data/lib/samagotchi/web/public/turn_view.js +5 -3
- data/lib/samagotchi/web/token.rb +3 -0
- data/lib/samagotchi/worker.rb +2 -2
- metadata +7 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6049cfd1f36b6aa77b8dfdfd9d437693f1d0c08257eb327838ccf9134db9fa27
|
|
4
|
+
data.tar.gz: d10c78ddf6e21e39689c3012214d4362844e090cac3624863a7a26195cc09769
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 240fb5fc4a45ae15088c690cc45c034f8ced20a9b6844649719ebfe25b9cf8801ecc90601d2c464d8e24a9fe75feadc0d541c8fe3e2c9784a2bd65fce9c31767
|
|
7
|
+
data.tar.gz: 64353c0287ded0c59631dec4af9319592d354ae1dc6c3d8609c65fffb60554a841453c060580233884cc1d8f4ed93dcfdb4434bd2fe5bfedeb57fb9f51c9cc3a
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,57 @@ and commands may change between minor versions. How releases are made:
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.58.0] - 2026-10-11
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Web: a ref's menu. A ref with more than one target shows a `▾` on hover (a tap on a touch screen, ↓ on a focused
|
|
16
|
+
ref) with **Open in editor**, **Show in turn**, **Copy path** and, on github-pr's link, **PR diff**; the plain click
|
|
17
|
+
stays the first. **Show in turn** scrolls to the row that touched the file, opens its diff at the line and flashes
|
|
18
|
+
it. It needs no editor, so a phone on the LAN now gets an answer's refs too.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- A session with its own model resumes when the config names no default.model: `chi send`, attached, the plain REPL
|
|
23
|
+
(`chi --resume ID --no-shared`) and `-p … --non-interactive` crashed with "no model configured". `/model clear`
|
|
24
|
+
then says there is no default.
|
|
25
|
+
- Web: the LAN access token no longer shows when the web app or a stream proxy is inspected (an error message, a log
|
|
26
|
+
line).
|
|
27
|
+
|
|
28
|
+
## [0.57.0] - 2026-10-11
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- Web: more refs. An edit or write approval card's path opens the current file at the first line the dry run
|
|
33
|
+
changes, and its diff lines open at theirs, so you can look at the real file before you approve. A read of a whole
|
|
34
|
+
file or a range (not the head/tail preview of a big file) gets a closed `lines 40–60` under its output: opened, it
|
|
35
|
+
shows the text as read with line numbers, and a click on a line opens the file there.
|
|
36
|
+
- Web: a `path:line` in an answer or a step's text opens that file in your editor too, when it names a file the
|
|
37
|
+
session's tool rows touched (`app.js:12` names the one touched file ending in it). It links once the turn has ended
|
|
38
|
+
and after a reload; code blocks, links (a PR link stays the PR's) and files the session didn't touch stay text.
|
|
39
|
+
- The `slow-commands` bundle, in the `core` profile: an `execute` that ran past its limit (or 80% of it) is remembered
|
|
40
|
+
for the project, and the next identical `execute` is denied once a turn with advice to start it with `task_create`
|
|
41
|
+
and wait with `task_wait` (with how long a finished task of it took, when there is one). `mode: notify` only warns;
|
|
42
|
+
`/slow-commands` lists a project's entries, `/slow-commands forget <n|all>` drops them. See "The slow-commands
|
|
43
|
+
bundle" in docs/plugins.md.
|
|
44
|
+
- `after_tool_call` hooks get `call:` (the call that ran), `elapsed_ms:` (the tool's run, nil for a denied call) and
|
|
45
|
+
`result:` (the tool's own text, without the `[tool]` lead, the `ran as:` line or the cap).
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
- More of chi's internal objects (commands, bootstrap, gem update, the web token) no longer print the environment or
|
|
50
|
+
the web token when inspected, e.g. in a failing spec's output or a debugger: they show every other instance variable
|
|
51
|
+
but the ones holding the environment or token, which print `[redacted]`.
|
|
52
|
+
- The host registry and a plugin's `ctx` no longer print the environment (API keys included) when inspected, e.g.
|
|
53
|
+
in a failing spec's output or a debugger: they show host names, addresses, key variable names and models.
|
|
54
|
+
- `chi --resume ID --thinking X` whose `/thinking` fails (or waits for the running turn) now says `--thinking`, not
|
|
55
|
+
"could not set the --llm-context".
|
|
56
|
+
- The MCP bundle no longer prints a server's env values or command arguments (e.g. a token) when inspected; a failing
|
|
57
|
+
spec or a debugger now shows only the server's state, the command's first word and the env's variable names.
|
|
58
|
+
|
|
59
|
+
Update with `chi update`: it installs slow-commands (new in `core`, core 0.2.0) and updates mcp (0.6.2: no env
|
|
60
|
+
values when inspected). Then restart `chi web`.
|
|
61
|
+
|
|
11
62
|
## [0.56.0] - 2026-10-11
|
|
12
63
|
|
|
13
64
|
### Added
|
|
@@ -2489,7 +2540,9 @@ and long-lived sessions.
|
|
|
2489
2540
|
- A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
|
|
2490
2541
|
hotkey panel that send selected text or the clipboard to your sessions.
|
|
2491
2542
|
|
|
2492
|
-
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.
|
|
2543
|
+
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.58.0...HEAD
|
|
2544
|
+
[0.58.0]: https://github.com/dm1try/samagotchi/compare/v0.57.0...v0.58.0
|
|
2545
|
+
[0.57.0]: https://github.com/dm1try/samagotchi/compare/v0.56.0...v0.57.0
|
|
2493
2546
|
[0.56.0]: https://github.com/dm1try/samagotchi/compare/v0.55.0...v0.56.0
|
|
2494
2547
|
[0.55.0]: https://github.com/dm1try/samagotchi/compare/v0.54.0...v0.55.0
|
|
2495
2548
|
[0.54.0]: https://github.com/dm1try/samagotchi/compare/v0.53.0...v0.54.0
|
data/README.md
CHANGED
|
@@ -76,7 +76,7 @@ It finds out whether the server is llama.cpp or OpenAI-compatible, picks the
|
|
|
76
76
|
model (or asks, when there are several), sends one test request and writes
|
|
77
77
|
the config. With a config already there, it adds a `hosts:` entry and keeps
|
|
78
78
|
the rest of the file. Then it installs the `core` bundles (loop-guard,
|
|
79
|
-
check-in, guardrails: the safety set for a small local model) and, on a
|
|
79
|
+
check-in, guardrails, slow-commands: the safety set for a small local model) and, on a
|
|
80
80
|
terminal, offers `dev` (known-names, mcp, btw, skills, source-links). Edit the
|
|
81
81
|
file later as in [Configure](#configure); `chi bootstrap --help` has the
|
|
82
82
|
options.
|
data/docs/cli.md
CHANGED
|
@@ -64,7 +64,7 @@ chi bootstrap # try localhost 8080, 11434, 1234, 8000
|
|
|
64
64
|
or with anchors gets the lines printed to paste instead. `--dry-run` shows
|
|
65
65
|
what it would write.
|
|
66
66
|
- **The bundles.** Unless writing the config failed, it installs the system
|
|
67
|
-
bundle and the `core` profile (loop-guard, check-in, guardrails), one line
|
|
67
|
+
bundle and the `core` profile (loop-guard, check-in, guardrails, slow-commands), one line
|
|
68
68
|
each; on a terminal it then asks `Also install dev (known-names, mcp, btw,
|
|
69
69
|
skills, source-links)? [y/N]`. Run again, it installs nothing new: a bundle
|
|
70
70
|
you uninstalled stays out ([Bundle profiles](memory.md#bundle-profiles-core-and-dev)).
|
data/docs/configuration.md
CHANGED
|
@@ -1191,9 +1191,11 @@ web:
|
|
|
1191
1191
|
and Chrome ask per origin, so another `chi web` port asks again), and VS Code asks to allow opening local files.
|
|
1192
1192
|
- It opens the file as it is now, not the agent's copy: after later edits a diff line's number can point elsewhere,
|
|
1193
1193
|
and a file in a worktree that was removed won't open. The row's diff and output are what the agent saw.
|
|
1194
|
-
- Only a viewer on this machine
|
|
1195
|
-
(`web.host: lan`, the LAN URL or its QR code opened here). A phone
|
|
1196
|
-
|
|
1194
|
+
- Only a viewer on this machine opens the editor: a loopback request, or this machine on its own LAN address
|
|
1195
|
+
(`web.host: lan`, the LAN URL or its QR code opened here). A phone gets the targets that need no editor (an
|
|
1196
|
+
answer's ref shows its row in the turn; see the refs in [sessions.md](sessions.md)); a row's title stays plain
|
|
1197
|
+
text there. An SSH tunnel or a local reverse proxy (`tailscale serve`) looks like loopback, so a remote viewer
|
|
1198
|
+
behind one gets an editor target that can't open.
|
|
1197
1199
|
- `GET /api/sessions/:id` (and its `?tail=1`) carries `capabilities.editor` (true for such a viewer) and
|
|
1198
1200
|
`editor_url`, the template (null otherwise).
|
|
1199
1201
|
|
data/docs/hooks.md
CHANGED
|
@@ -56,7 +56,7 @@ The plugin class must respond to `#call(event)` — duck-typed, no base class re
|
|
|
56
56
|
| `:after_generation` | After LLM returns (both loops); not after a generation a hook cut (`stop_generation`) | `{ type: :after_generation, iteration: N, response: "...", messages: [...] (the conversation as sent) }` |
|
|
57
57
|
| `:generation_progress` | While the response streams, in batches (see [Watching the stream](#watching-the-stream)) | `{ type: :generation_progress, iteration: N, thinking: "..." (new since the last fire), text: "..." (new visible text), thinking_chars: N, text_chars: N (this generation so far), elapsed_ms: N, restarted: true (only on the first fire after a dropped stream was asked again) }` |
|
|
58
58
|
| `:before_tool_call` | Before tool dispatch (and before `tool_call_started`) | `{ type: :before_tool_call, iteration: N, call: {...}, params: "...", guardrail: Verdict, context: {...}, targets: {...}, blocked: false, block_reason: nil }` |
|
|
59
|
-
| `:after_tool_call` | After tool execution | `{ type: :after_tool_call, iteration: N, tool: "read", output: "..." (the text the model gets: capped at max_tool_output_chars, a cut one ending in a [cut: N of M chars …] line), status: "ok" \| "error" \| "blocked" \| "stopped" }`; `stopped` is a `task_wait` the turn's Stop ended (the task runs on) or whose task was stopped |
|
|
59
|
+
| `:after_tool_call` | After tool execution | `{ type: :after_tool_call, iteration: N, tool: "read", output: "..." (the text the model gets: capped at max_tool_output_chars, a cut one ending in a [cut: N of M chars …] line), status: "ok" \| "error" \| "blocked" \| "stopped", call: {...} (the call that ran, as the gate left it), elapsed_ms: N (the tool's own run, without an approval wait; nil for a denied call), result: "..." (the tool's own text: no "[read]" lead, no "ran as:" line, never cut) }`; `stopped` is a `task_wait` the turn's Stop ended (the task runs on) or whose task was stopped. Match on `result:`, not `output:`: it is what the tool said (`Error: command timed out after 120s` for an execute killed at its limit, `Samagotchi::Tools::Execute::TIMEOUT_PREFIX`) |
|
|
60
60
|
| `:session_end` | After each turn `:after_turn` fires for, after it (turn-level lifecycle) | `{ type: :session_end, session_id: "..." }` |
|
|
61
61
|
|
|
62
62
|
Every event also carries the hook runtime (next section): `hook:` (the label
|
|
@@ -507,7 +507,7 @@ Notes:
|
|
|
507
507
|
- Ordering: bundle hooks fire by `(priority, bundle_name, hook_name)` (lower priority first), then plain `config.yml` hooks in registration order.
|
|
508
508
|
- Settings: a hook class with `initialize(settings = {})` gets the bundle's section of `config.yml` `bundles:` (see [Settings](#settings)).
|
|
509
509
|
- 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). There `ctx.notify`, `ctx.steer` and the other `ctx` helpers act as this page's `event[:notify]`… do for the event the block runs for.
|
|
510
|
-
- 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)).
|
|
510
|
+
- 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)), `chi bundle install slow-commands` (a plugin: commands that ran past the execute limit are run as tasks next time, see [Plugins](plugins.md#the-slow-commands-bundle)) and `chi bundle install skills` (a plugin: `/skill`, versions of skills, see [Plugins](plugins.md#the-skills-bundle)).
|
|
511
511
|
- 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).
|
|
512
512
|
|
|
513
513
|
Lifecycle:
|
data/docs/memory.md
CHANGED
|
@@ -269,7 +269,7 @@ profiles, meta bundles that install a set of them:
|
|
|
269
269
|
|
|
270
270
|
| profile | bundles |
|
|
271
271
|
|---|---|
|
|
272
|
-
| `core` | loop-guard, check-in, guardrails: the recommended safety set |
|
|
272
|
+
| `core` | loop-guard, check-in, guardrails, slow-commands: the recommended safety set |
|
|
273
273
|
| `dev` | known-names, mcp, btw, skills, source-links, github-pr, coordinator |
|
|
274
274
|
|
|
275
275
|
`chi bootstrap` installs `core` and, on a terminal, offers `dev`. An existing
|
data/docs/plugins.md
CHANGED
|
@@ -1171,6 +1171,69 @@ and goes with the session; another session starts from config.yml:
|
|
|
1171
1171
|
| `/checkin mode ask` / `nudge` / `notify` | the mode |
|
|
1172
1172
|
| `/checkin nudge` / `later` / `stop` | the card's actions, also by hand |
|
|
1173
1173
|
|
|
1174
|
+
## The slow-commands bundle
|
|
1175
|
+
|
|
1176
|
+
`chi bundle install slow-commands` installs the bundle shipped with chi (`chi
|
|
1177
|
+
bootstrap` installs it with the `core` profile). It is written only against
|
|
1178
|
+
this API (`lib/samagotchi/bundles/slow-commands/plugin.rb`), with `chi.on`
|
|
1179
|
+
hooks and a `/slow-commands` command, and has no memory file.
|
|
1180
|
+
|
|
1181
|
+
`execute` kills a command at its limit (120 s, `execute.timeout_sec`). A full
|
|
1182
|
+
test suite or a build takes longer, so the model loses those two minutes,
|
|
1183
|
+
then starts it again with `task_create` and `task_wait`; the next session
|
|
1184
|
+
does the same. slow-commands remembers such a command for the project:
|
|
1185
|
+
|
|
1186
|
+
- An `execute` that **timed out**, or ran **`warn_ratio` of the limit** or
|
|
1187
|
+
longer (default 0.8, so 96 s of 120), is recorded as slow. A later run of
|
|
1188
|
+
it under half the limit **with exit 0** drops the entry; a fast failure (a
|
|
1189
|
+
load error) doesn't, since it says nothing about how long the suite takes.
|
|
1190
|
+
Nor does one through a filter the key leaves out (`rspec 2>&1 | tail -30`
|
|
1191
|
+
exits with `tail`'s 0, and a `| head` can end it early).
|
|
1192
|
+
A call the user stopped, or that was denied, records nothing.
|
|
1193
|
+
- The **next identical `execute`** is **denied** with advice:
|
|
1194
|
+
|
|
1195
|
+
```
|
|
1196
|
+
[execute] Error: denied by guardrail (bundle slow-commands): slow command. The user was not asked. This command ran past the execute limit (120 s) here on 2026-10-09. Start it with task_create, then task_wait (timeout 600). A finished task of this command took 4m10s.
|
|
1197
|
+
```
|
|
1198
|
+
|
|
1199
|
+
The last sentence is there when a task of the same command (and cwd)
|
|
1200
|
+
finished with exit 0 in the project's `tmp/tasks`; a longer one raises the
|
|
1201
|
+
`task_wait` timeout it suggests. The user sees one line:
|
|
1202
|
+
`slow-commands> execute bundle exec rspec: ran past 120 s before, denied`.
|
|
1203
|
+
- **Once per command per turn**: the same `execute` again in that turn runs.
|
|
1204
|
+
The model insisting is a signal (maybe the suite got faster), and that run
|
|
1205
|
+
records the new truth. A new turn denies again.
|
|
1206
|
+
|
|
1207
|
+
A command is the same when its project and its text match. The project is
|
|
1208
|
+
the session's git checkout (else its working directory). The text is the
|
|
1209
|
+
command's words and operators (`a && b` is not `a b`), the call's `cwd:`
|
|
1210
|
+
folded in as `cd web && …`, and a trailing `2>&1`, `| tail …`, `| head …`
|
|
1211
|
+
or `| grep …` dropped (they don't change how long it runs). Arguments stay:
|
|
1212
|
+
`bundle exec rspec` (the suite) is slow, `bundle exec rspec
|
|
1213
|
+
spec/foo_spec.rb` isn't the same command, and stays the fast path. A
|
|
1214
|
+
command that ends inside a quote, `$(` or heredoc is never recorded, nor
|
|
1215
|
+
one with a `$(…)`, backticks or a heredoc `cat` or `tee` reads (the key
|
|
1216
|
+
can't tell two of those apart). A heredoc script (`python3 - <<'EOF'`)
|
|
1217
|
+
keeps its body in the key, so each script is its own command.
|
|
1218
|
+
|
|
1219
|
+
Entries live in `$XDG_STATE_HOME/samagotchi/plugins/slow-commands/commands.json`,
|
|
1220
|
+
shared by every session (another session's record counts at once) and
|
|
1221
|
+
forgotten after `ttl_days`.
|
|
1222
|
+
|
|
1223
|
+
```yaml
|
|
1224
|
+
# config.yml
|
|
1225
|
+
bundles:
|
|
1226
|
+
slow-commands:
|
|
1227
|
+
mode: deny # deny | notify (warn the user, never deny)
|
|
1228
|
+
warn_ratio: 0.8 # a run this share of the execute limit or longer is slow
|
|
1229
|
+
ttl_days: 14 # entries older than this are forgotten
|
|
1230
|
+
```
|
|
1231
|
+
|
|
1232
|
+
| | |
|
|
1233
|
+
|---|---|
|
|
1234
|
+
| `/slow-commands` | this project's slow commands, numbered |
|
|
1235
|
+
| `/slow-commands forget 2` / `forget all` | drop one (a wrong entry) or all of them |
|
|
1236
|
+
|
|
1174
1237
|
## The skills bundle
|
|
1175
1238
|
|
|
1176
1239
|
`chi bundle install skills` (or the `dev` profile) installs the bundle shipped with chi
|
data/docs/sessions.md
CHANGED
|
@@ -68,9 +68,9 @@ chi sessions clean --dry-run --days 7 # test sessions older than 7 da
|
|
|
68
68
|
**Ordering:**
|
|
69
69
|
|
|
70
70
|
- `Session.list` / `SessionManager.list_sessions` / `GET /api/sessions?sort=&order=&limit=&offset=` default to `updated_at desc` (newest activity first). Also supports `created_at`, `asc`. `X-Total-Count` header when paginated.
|
|
71
|
-
- Web UI (`chi web`): the page's scope is in its URL. Started in a git repo, `chi web` opens `/?dir=<that folder>`: that project's sessions, and new chats start in that folder; the header chip says `<project> · all`, and `all` opens the same place without `?dir` (every session; a new chat there starts in the server's own folder, shown on the start page, and cards name their folder; `← <project>` goes back to the project view it came from, or to the server's own project). One server serves every project: a second `chi web` (from another repo) finds it through `GET /api/info` and prints (with `--open`, opens) its page for its own folder instead of starting another. The 3 latest sessions sit above the chat (`hide ▴` puts them away behind a `▾ N sessions` pill, remembered; in a window under 900 px wide, where the strip scrolls sideways, `▴ hide` sits in the top bar where the pill comes back; a window under 700 px tall hides them by itself, so a question or approval card in the stage keeps room, and the pill shows them until the window is tall again). A parent and its delegates are one card there: its `▸ 2 delegates · 1 live · 1 waiting` chip (the waiting part in the attention colour, the card's border too) opens their rows in a popover under it (a row per delegate at any depth, indented; a click opens that one, Esc or a click outside closes it, and the strip's order holds while it is open). A fork is a card of its own, and so is a delegate whose parent isn't listed (archived, another project), with its `↳ <parent>` chip; the pill counts cards, and every live or waiting session. "All sessions" (or `/`) opens every session at `#/sessions`, with a search over preview, id and status (Esc or Back returns). There a parent's card holds its delegates the same way, the chip opening their rows inside the card (each with an archive button on hover), open by itself while one of them waits on you; the count says `14 (+7 delegates)`, a search that matches a delegate shows its parent's card with the list open and the matching rows marked, and select mode picks cards (archiving a parent takes its delegates). The open session is in the URL (`#/s/<id>`), so a reload or a copied link opens it again; the chi logo top left goes back to the empty start for a new chat. The message box grows with its text; drag its top edge to keep it taller (double-click resets). A file in a tool row (a read, edit or write's title) and its diff lines are refs: hover one and click to open the current file in your editor at that line, on this machine only (`web.editor`; the browser's "always allow" for the page's port and VS Code's "allow opening local files" are asked once; see [Opening files from chi web](configuration.md#opening-files-from-chi-web)). The info bar copies `chi --attach <id>` for a terminal. A session whose prompt carried model notes has a `notes: deepseek` chip there (the names without `model_notes_`; the tooltip lists each with its scope and size; `session.prompt_notes` in `GET /api/sessions/:id`, the live worker's, else the session file's; see [Memory](memory.md#model-notes)). Its `llm ctx` chip names the LLM context strategy the next turn runs under (`llm ctx stale,forget · session`, in the accent colour when the session set its own; the tooltip gives each value's source), and its popover sets the session's own strategy, apply rule and budget, or follows the model again, by running `/llm-context` in the worker (the reply shows as a command bubble); `GET /api/sessions/:id`'s `session.llm_context` carries the values (the live worker's, else resolved from the session file), and a `command_ran` that changed them carries the new ones. Its `think` chip does the same for the thinking level (`/thinking`), and on a chat host its `reasoning` chip for the reasoning history (`reasoning all · session`, `/reasoning-history`; `session.reasoning_history`, null on a native host). A `/thinking` or `/reasoning-history` change that restarts the prompt cache asks first: the reply bubble has an **Apply (restarts the cache)** button that sends its `--yes` line (the chips' Set sends that line itself; see [Changes that restart the prompt cache](configuration.md#changes-that-restart-the-prompt-cache)). It shows the context used (`ctx 42%`) and the last generation's decode speed (`87 tok/s`, `~64 tok/s` when estimated; see `/stats` in [cli.md](cli.md#session-commands)); hovering `ctx` there or on a session card lists the session's tokens in (summed over every request, with the cached share), out (with reasoning) and the cost when the provider reports one, for this session only (one estimated from a `hosts.<name>.models` price shows with `~`: `cost: ~$0.54 ($0.42 reported, ~$0.12 estimated)`), and the memory indexes its prompt holds (`memory index: ~3.4k tokens in this session's prompt (system 2.0k, project 1.4k)`; see [Memory](memory.md#the-indexs-size)). The start page's model picker (bottom left of the composer, the chosen name `▾`) chooses the model a new chat starts on; it opens a search list over the conversation (↓ or a click; downward when the start page leaves no room above): an empty search shows the last 5 picks (Recent) and then each host's model ids A–Z under the host, the default host first (an id the host declares under `hosts.<name>.models` first, noted `config`, its tooltip `served by config (hosts.<name>.models)`; a host that failed to list its models keeps those ids, greyed and noted `host down`, still pickable; a remembered or default model stays chosen even then, greyed on the button); typed words must each match the host or the id (a substring, else for 3+ characters the letters in order, so `deepseek4.1 fla` finds `openrouter · deepseek/deepseek-v4.1-flash`), ⏎ picks, Esc/Tab close, a pick puts the focus in the message box: `GET /api/models` lists the hosts' models as chi spells them (`{default, models: [{name, host, id, configured?, unavailable?, sampling?, llm_context?}], warning?}`; `configured: true` marks an id declared under `hosts.<name>.models`, listed first under its host, `unavailable: true` one whose host failed to list its models; `sampling` is the model's configured request parameters as `/model` words them, the row's tooltip in the list; bare for the default host, `host:model` for the others, from the same cached lists as `/models`, a bounded wait, a host that is down noted in `warning`), and `POST /api/sessions` takes `model` (blank means the default). Beside it, an `llm ctx` chip shows the LLM context the picked model starts under (each `GET /api/models` row's `llm_context`, as the info bar's); its form sets one for the new chat only, sent as `POST /api/sessions`'s `llm_context` and never remembered (see [configuration.md](configuration.md#llm-context-a-sessions-own-strategy)). The start page's first message goes as later ones do: `POST /api/sessions` with `idle: true` and `preview` (the message, which names the session until its turn is saved), then `POST /api/sessions/:id/turn`, so a failed first turn puts its prompt back in the composer too; a first line that is one of the new session's commands (`/model …`) goes to `POST /api/sessions/:id/command` instead, as later ones do. The browser remembers the last model picked; a running session's model is in the info bar and changes only with `/model`. In the composer a line is a command when it is `!…` or starts with one of the session's command names (its `commands` list; `/stats`, `/exit` and the other terminal words get the page's own reply); an unknown `/word` (`/usr/bin/env is missing`) goes to the model as a prompt, as in a terminal; a one-word `/word` no command answers (`/modle`) is not sent: the page shows `Unknown command /modle. Did you mean /model? /help lists the commands.` (the "Did you mean" part only when a name is close). A `POST /turn` (or a session's first `prompt`) that is one of the worker's commands runs as that command too (`202 {status, command_id, session_id}`), so `chi send`, `chi -p` and a page whose command list lags all get the command. ↑/↓ in the composer walk the prompt history the TUI's ↑ walks ([Persistent Prompt History](cli.md#persistent-prompt-history)): ↑ with the caret on the first line, ↓ from the last line while browsing, ↓ past the newest brings the draft back; `GET /api/history` returns `{entries: [...]}`, oldest first (the page fetches it on focus and after each send). The server records a sent line in the routes once the worker took it (`POST /turn` accepted, a `!command` through `POST /command` or as a session's first line); `"history": false` in the body keeps it out (an image-only send).
|
|
71
|
+
- Web UI (`chi web`): the page's scope is in its URL. Started in a git repo, `chi web` opens `/?dir=<that folder>`: that project's sessions, and new chats start in that folder; the header chip says `<project> · all`, and `all` opens the same place without `?dir` (every session; a new chat there starts in the server's own folder, shown on the start page, and cards name their folder; `← <project>` goes back to the project view it came from, or to the server's own project). One server serves every project: a second `chi web` (from another repo) finds it through `GET /api/info` and prints (with `--open`, opens) its page for its own folder instead of starting another. The 3 latest sessions sit above the chat (`hide ▴` puts them away behind a `▾ N sessions` pill, remembered; in a window under 900 px wide, where the strip scrolls sideways, `▴ hide` sits in the top bar where the pill comes back; a window under 700 px tall hides them by itself, so a question or approval card in the stage keeps room, and the pill shows them until the window is tall again). A parent and its delegates are one card there: its `▸ 2 delegates · 1 live · 1 waiting` chip (the waiting part in the attention colour, the card's border too) opens their rows in a popover under it (a row per delegate at any depth, indented; a click opens that one, Esc or a click outside closes it, and the strip's order holds while it is open). A fork is a card of its own, and so is a delegate whose parent isn't listed (archived, another project), with its `↳ <parent>` chip; the pill counts cards, and every live or waiting session. "All sessions" (or `/`) opens every session at `#/sessions`, with a search over preview, id and status (Esc or Back returns). There a parent's card holds its delegates the same way, the chip opening their rows inside the card (each with an archive button on hover), open by itself while one of them waits on you; the count says `14 (+7 delegates)`, a search that matches a delegate shows its parent's card with the list open and the matching rows marked, and select mode picks cards (archiving a parent takes its delegates). The open session is in the URL (`#/s/<id>`), so a reload or a copied link opens it again; the chi logo top left goes back to the empty start for a new chat. The message box grows with its text; drag its top edge to keep it taller (double-click resets). A file in a tool row (a read, edit or write's title) and its diff lines are refs: hover one and click to open the current file in your editor at that line, on this machine only (`web.editor`; the browser's "always allow" for the page's port and VS Code's "allow opening local files" are asked once; see [Opening files from chi web](configuration.md#opening-files-from-chi-web)). So are an edit or write approval card's paths (at the first line the dry run changes) and its diff lines, before you approve, and the numbered lines of a read's output: `lines 40–60` under it opens the text as read, numbered, when the output is the file as-is (not the head/tail preview of a big file, an error or an image; no numbers after a cut). A `path:line` (or `path:line-line`) in an answer or a step's text is one too once the turn has ended, and after a reload, when it names a file a row or card on the page touched: the path under the session's folder, else the one touched file it ends with (a bare `app.js:12` when only one touched file is named so; two such files make it none); a path without a line counts only when it is a touched file exactly. Code blocks, links, bare URLs and inline code with more than the ref in it are left alone, and a file the session didn't touch is never linked; a link whose text is such a ref alone (github-pr's link to the PR diff) becomes a ref too, and its plain click still goes to the PR. A ref with more than one target shows a small `▾` on hover: it opens a menu of them, in order **PR diff** (a link's), **Open in editor**, **Show in turn** and **Copy path**, and a plain click runs the first (a link's own, else the editor, else show in turn). **Show in turn** scrolls to the tool row that touched the file (looked for in the ref's own step, then its own turn, then the rows before it, then the nearest after it; there, the last one showing the ref's line, else the last one), opens the steps, the stage's steps and the row's diff (or the read showing the line) and flashes the row and the line; an answer's or a step's ref and an approval card's path have it, not a row's own title or lines. It needs no editor, so a viewer without one (a phone on the LAN) gets an answer's refs with **Show in turn** as their click; a row's title and diff lines stay plain text there. On a touch screen a tap opens the menu. A ref with a target is a tab stop: Enter runs its first target, ↓ opens the menu (↑/↓ move, Enter picks, Esc closes back to the ref). Diff and read lines have their click only. The info bar copies `chi --attach <id>` for a terminal. A session whose prompt carried model notes has a `notes: deepseek` chip there (the names without `model_notes_`; the tooltip lists each with its scope and size; `session.prompt_notes` in `GET /api/sessions/:id`, the live worker's, else the session file's; see [Memory](memory.md#model-notes)). Its `llm ctx` chip names the LLM context strategy the next turn runs under (`llm ctx stale,forget · session`, in the accent colour when the session set its own; the tooltip gives each value's source), and its popover sets the session's own strategy, apply rule and budget, or follows the model again, by running `/llm-context` in the worker (the reply shows as a command bubble); `GET /api/sessions/:id`'s `session.llm_context` carries the values (the live worker's, else resolved from the session file), and a `command_ran` that changed them carries the new ones. Its `think` chip does the same for the thinking level (`/thinking`), and on a chat host its `reasoning` chip for the reasoning history (`reasoning all · session`, `/reasoning-history`; `session.reasoning_history`, null on a native host). A `/thinking` or `/reasoning-history` change that restarts the prompt cache asks first: the reply bubble has an **Apply (restarts the cache)** button that sends its `--yes` line (the chips' Set sends that line itself; see [Changes that restart the prompt cache](configuration.md#changes-that-restart-the-prompt-cache)). It shows the context used (`ctx 42%`) and the last generation's decode speed (`87 tok/s`, `~64 tok/s` when estimated; see `/stats` in [cli.md](cli.md#session-commands)); hovering `ctx` there or on a session card lists the session's tokens in (summed over every request, with the cached share), out (with reasoning) and the cost when the provider reports one, for this session only (one estimated from a `hosts.<name>.models` price shows with `~`: `cost: ~$0.54 ($0.42 reported, ~$0.12 estimated)`), and the memory indexes its prompt holds (`memory index: ~3.4k tokens in this session's prompt (system 2.0k, project 1.4k)`; see [Memory](memory.md#the-indexs-size)). The start page's model picker (bottom left of the composer, the chosen name `▾`) chooses the model a new chat starts on; it opens a search list over the conversation (↓ or a click; downward when the start page leaves no room above): an empty search shows the last 5 picks (Recent) and then each host's model ids A–Z under the host, the default host first (an id the host declares under `hosts.<name>.models` first, noted `config`, its tooltip `served by config (hosts.<name>.models)`; a host that failed to list its models keeps those ids, greyed and noted `host down`, still pickable; a remembered or default model stays chosen even then, greyed on the button); typed words must each match the host or the id (a substring, else for 3+ characters the letters in order, so `deepseek4.1 fla` finds `openrouter · deepseek/deepseek-v4.1-flash`), ⏎ picks, Esc/Tab close, a pick puts the focus in the message box: `GET /api/models` lists the hosts' models as chi spells them (`{default, models: [{name, host, id, configured?, unavailable?, sampling?, llm_context?}], warning?}`; `configured: true` marks an id declared under `hosts.<name>.models`, listed first under its host, `unavailable: true` one whose host failed to list its models; `sampling` is the model's configured request parameters as `/model` words them, the row's tooltip in the list; bare for the default host, `host:model` for the others, from the same cached lists as `/models`, a bounded wait, a host that is down noted in `warning`), and `POST /api/sessions` takes `model` (blank means the default). Beside it, an `llm ctx` chip shows the LLM context the picked model starts under (each `GET /api/models` row's `llm_context`, as the info bar's); its form sets one for the new chat only, sent as `POST /api/sessions`'s `llm_context` and never remembered (see [configuration.md](configuration.md#llm-context-a-sessions-own-strategy)). The start page's first message goes as later ones do: `POST /api/sessions` with `idle: true` and `preview` (the message, which names the session until its turn is saved), then `POST /api/sessions/:id/turn`, so a failed first turn puts its prompt back in the composer too; a first line that is one of the new session's commands (`/model …`) goes to `POST /api/sessions/:id/command` instead, as later ones do. The browser remembers the last model picked; a running session's model is in the info bar and changes only with `/model`. In the composer a line is a command when it is `!…` or starts with one of the session's command names (its `commands` list; `/stats`, `/exit` and the other terminal words get the page's own reply); an unknown `/word` (`/usr/bin/env is missing`) goes to the model as a prompt, as in a terminal; a one-word `/word` no command answers (`/modle`) is not sent: the page shows `Unknown command /modle. Did you mean /model? /help lists the commands.` (the "Did you mean" part only when a name is close). A `POST /turn` (or a session's first `prompt`) that is one of the worker's commands runs as that command too (`202 {status, command_id, session_id}`), so `chi send`, `chi -p` and a page whose command list lags all get the command. ↑/↓ in the composer walk the prompt history the TUI's ↑ walks ([Persistent Prompt History](cli.md#persistent-prompt-history)): ↑ with the caret on the first line, ↓ from the last line while browsing, ↓ past the newest brings the draft back; `GET /api/history` returns `{entries: [...]}`, oldest first (the page fetches it on focus and after each send). The server records a sent line in the routes once the worker took it (`POST /turn` accepted, a `!command` through `POST /command` or as a session's first line); `"history": false` in the body keeps it out (an image-only send).
|
|
72
72
|
- Web notifications: when a session needs you while the tab is hidden or behind another window (a question or a guardrail approval, a plugin card with buttons during a turn such as check-in's, a failed turn, a turn done after 10 s or more; a delegated session only for its questions, a canceled turn never), the page title counts it, `(N) Chi`, until you come back to the tab. The bell at the right of the top bar also turns on OS notifications ("needs an answer", "needs approval", "needs you", "turn failed", "done in 42 s", under the session's first message; a click opens the session): its first click asks the browser for the permission, and the choice is kept per browser. Any session of the page's scope counts, not only the open one. What was already so when the page loaded never counts, and several chi tabs show one notification per event. A chi tab in front tells the others (a `BroadcastChannel`), so while you look at one, the ones behind neither notify nor count what it shows; coming back to a tab clears those events in the other tabs' titles too. The hub's `session` frames carry what this needs: `pending_question` (`{id, kind}`, kind `question` or `approval`), `pending_card` (`{id, bundle}`: the running turn's card with actions, from `pending_card.json` that the worker keeps in the session's folder while one is open; a card without actions, such as loop-guard's, never counts) and `last_turn` (`{outcome, ended_at, seconds, origin}`, saved in the session's JSON as the engine ends each turn).
|
|
73
|
-
- The web frontend is a zero-build ES-module stack in `lib/samagotchi/web/public/`: `data.js` (retrieval, typed SSE `openStream` for a session and `openEvents` for the session list), `sessions_list.js` (the list as a pure reducer over `GET /api/events`: a snapshot replaces it, an upsert keeps a known card in place, a removal drops it; `families()` groups delegates under their parent), `families_view.js` (a parent's family chip, its rows and the strip's popover), `chains.js` (session chains: the links from `continues`, the cards folded into the latest link's, its `day N` chip and rows, the info bar's ← → links, a refused continue), `notify.js` (which of those events needs the user, for the notifications), `app.js` (presentation/state; a session with no live stream gets one when its `session` event says `bridge_up`, after one re-read: `event_seq` starts over in each worker, so a dropped stream is never resumed with its old cursor), `format.js` (pure formatters such as `previewOf`), `prompt_history.js` (the composer's ↑/↓ over the shared prompt history), `commands.js` (session commands: which composer lines are commands, the page's own replies, a `command_ran`'s bubble, the page's command bubbles kept across a re-read), `composer.js` (where a composer line goes, `routeComposerLine`: /cut and /queue, the start page's own replies, a session command or `!cmd`, a typo hint, else a turn; and a session command sent from the composer or a chip), `restore.js` (a failed turn's prompt handed back into this tab's composer, before or after its `/turn` ack), `lists_view.js` (the session lists: the strip and its pill, all sessions with its search, count and select mode, their cards, the family and chain popovers; the cards, tile and pill as lit-html templates, drawn as new nodes each time, and the lists' orders as pure functions), `stream_handlers.js` (the open session's stream event handlers, by event type, over the page's views and hooks; a snapshot replays through them too), `info_bar.js` (the session bar's info line as a lit-html template over a plain model: id, status, model, chain links, chips, ctx, speed, clock, worker badge), `session_state.js` (what the page knows about the open session: its empty state, and `sessionStateFrom`, the fields a `GET /sessions/:id` payload sets), `refs.js` (refs: a kind and a target on the element, the
|
|
73
|
+
- The web frontend is a zero-build ES-module stack in `lib/samagotchi/web/public/`: `data.js` (retrieval, typed SSE `openStream` for a session and `openEvents` for the session list), `sessions_list.js` (the list as a pure reducer over `GET /api/events`: a snapshot replaces it, an upsert keeps a known card in place, a removal drops it; `families()` groups delegates under their parent), `families_view.js` (a parent's family chip, its rows and the strip's popover), `chains.js` (session chains: the links from `continues`, the cards folded into the latest link's, its `day N` chip and rows, the info bar's ← → links, a refused continue), `notify.js` (which of those events needs the user, for the notifications), `app.js` (presentation/state; a session with no live stream gets one when its `session` event says `bridge_up`, after one re-read: `event_seq` starts over in each worker, so a dropped stream is never resumed with its old cursor), `format.js` (pure formatters such as `previewOf`), `prompt_history.js` (the composer's ↑/↓ over the shared prompt history), `commands.js` (session commands: which composer lines are commands, the page's own replies, a `command_ran`'s bubble, the page's command bubbles kept across a re-read), `composer.js` (where a composer line goes, `routeComposerLine`: /cut and /queue, the start page's own replies, a session command or `!cmd`, a typo hint, else a turn; and a session command sent from the composer or a chip), `restore.js` (a failed turn's prompt handed back into this tab's composer, before or after its `/turn` ack), `lists_view.js` (the session lists: the strip and its pill, all sessions with its search, count and select mode, their cards, the family and chain popovers; the cards, tile and pill as lit-html templates, drawn as new nodes each time, and the lists' orders as pure functions), `stream_handlers.js` (the open session's stream event handlers, by event type, over the page's views and hooks; a snapshot replays through them too), `info_bar.js` (the session bar's info line as a lit-html template over a plain model: id, status, model, chain links, chips, ctx, speed, clock, worker badge), `session_state.js` (what the page knows about the open session: its empty state, and `sessionStateFrom`, the fields a `GET /sessions/:id` payload sets), `refs.js` (refs: a kind and a target on the element, the targets per kind, show in turn, one delegated set of click, key and hover listeners), `ref_menu.js` (a ref's `▾` and its menu). Unit-tested via `npm test` (`node --test spec/web/public/*.test.js`).
|
|
74
74
|
- Selecting a session in the web UI is read-only: `GET /api/sessions/:id` never spawns a worker (it reads a live worker's snapshot when one runs, else the session file). A prompt (`POST /turn`) or a command (`POST /command`) wakes the worker, and `/stream` briefly waits for a freshly-spawned bridge before answering. A caught-up SSE reconnect holds the stream open; `reset` markers are only sent for reconnects behind the ring window, or with a cursor from another worker (event ids are `<event_seq>-<epoch>`, one epoch per worker).
|
|
75
75
|
|
|
76
76
|
**Test-session hygiene:**
|
|
@@ -7,6 +7,7 @@ require "ipaddr"
|
|
|
7
7
|
require_relative "../atomic_file"
|
|
8
8
|
require_relative "../config"
|
|
9
9
|
require_relative "../yaml_lines"
|
|
10
|
+
require_relative "../redacted_inspect"
|
|
10
11
|
|
|
11
12
|
module Samagotchi
|
|
12
13
|
module Bootstrap
|
|
@@ -17,6 +18,7 @@ module Samagotchi
|
|
|
17
18
|
# other key must be unchanged, or the backup goes back.
|
|
18
19
|
class ConfigWriter
|
|
19
20
|
include YAMLLines
|
|
21
|
+
include RedactedInspect
|
|
20
22
|
|
|
21
23
|
class Error < StandardError
|
|
22
24
|
end
|
|
@@ -9,6 +9,7 @@ require_relative "../version"
|
|
|
9
9
|
require_relative "../llm/openai_chat"
|
|
10
10
|
require_relative "../llm/http"
|
|
11
11
|
require_relative "../llm/api_key"
|
|
12
|
+
require_relative "../redacted_inspect"
|
|
12
13
|
|
|
13
14
|
module Samagotchi
|
|
14
15
|
module Bootstrap
|
|
@@ -17,6 +18,8 @@ module Samagotchi
|
|
|
17
18
|
# API key, or nothing. One attempt per request, no retries: a refused
|
|
18
19
|
# port answers at once instead of after chi's usual backoff.
|
|
19
20
|
class Probe
|
|
21
|
+
include RedactedInspect
|
|
22
|
+
|
|
20
23
|
# The ports `chi bootstrap` with no target tries on localhost.
|
|
21
24
|
LOCAL_PORTS = { 8080 => "llama.cpp", 11_434 => "Ollama", 1234 => "LM Studio", 8000 => "vLLM" }.freeze
|
|
22
25
|
DEFAULT_PORT = 8080
|
|
@@ -9,6 +9,7 @@ require_relative "bootstrap/config_writer"
|
|
|
9
9
|
require_relative "bootstrap/bundles"
|
|
10
10
|
require_relative "cli/command"
|
|
11
11
|
require_relative "cli/flags"
|
|
12
|
+
require_relative "redacted_inspect"
|
|
12
13
|
|
|
13
14
|
module Samagotchi
|
|
14
15
|
# `chi bootstrap [TARGET]`: the first setup. It names a model server, works
|
|
@@ -19,6 +20,7 @@ module Samagotchi
|
|
|
19
20
|
# Prompts only on a terminal; a script gets the list and exit 2 instead.
|
|
20
21
|
class BootstrapCommand
|
|
21
22
|
include CLI::Command
|
|
23
|
+
include RedactedInspect
|
|
22
24
|
|
|
23
25
|
USAGE = <<~TEXT
|
|
24
26
|
Usage: chi bootstrap [TARGET] [--name NAME] [--model ID] [--key-env VAR] [--no-test] [--dry-run]
|
|
@@ -13,6 +13,7 @@ require_relative "broadcast/triage_model"
|
|
|
13
13
|
require_relative "broadcast/triage_log"
|
|
14
14
|
require_relative "cli/command"
|
|
15
15
|
require_relative "cli/flags"
|
|
16
|
+
require_relative "redacted_inspect"
|
|
16
17
|
|
|
17
18
|
module Samagotchi
|
|
18
19
|
# `chi broadcast`: share a note with every session it may concern,
|
|
@@ -25,6 +26,7 @@ module Samagotchi
|
|
|
25
26
|
# skipped. For the user only: refused inside a chi session.
|
|
26
27
|
class BroadcastCommand
|
|
27
28
|
include CLI::Command
|
|
29
|
+
include RedactedInspect
|
|
28
30
|
|
|
29
31
|
SOURCE = "broadcast"
|
|
30
32
|
# Bytes kept free under SessionInbox's 16 KiB for the line each note
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: mcp
|
|
3
|
-
version: 0.6.
|
|
3
|
+
version: 0.6.2
|
|
4
4
|
scope: system
|
|
5
5
|
description: "Tools from MCP servers (stdio): each server in config.yml bundles: mcp: servers: runs for the session; the model searches their tools with find_mcp_tools and calls one with mcp_call (<server>/<tool>); /mcp lists them"
|
|
6
6
|
trust_level: reviewed
|
|
7
7
|
files: {}
|
|
8
8
|
plugin:
|
|
9
9
|
file: plugin.rb
|
|
10
|
-
sha256: sha256:
|
|
10
|
+
sha256: sha256:8b2c0cf8a48d99b32e320506676d93d8069f9fda4a2ee101112073780a5c4f8a
|
|
11
11
|
# 0.37.0 (published 2026-10-07) is the release whose targets: take acts_as:, args: and label:.
|
|
12
12
|
requires_chi: ">= 0.37.0"
|
|
@@ -276,7 +276,15 @@ class Plugin
|
|
|
276
276
|
# +client+ is the running process's (a restart replaces it; the
|
|
277
277
|
# service's stop closes the current one), +restarts+ how many it had.
|
|
278
278
|
Server = Struct.new(:name, :config, :service, :state, :error, :tools, :listed, :timeout, :cwd, :command, :env,
|
|
279
|
-
:digest, :cached_at, :relist, :relisting, :client, :restarts, :tokens, :info, keyword_init: true)
|
|
279
|
+
:digest, :cached_at, :relist, :relisting, :client, :restarts, :tokens, :info, keyword_init: true) do
|
|
280
|
+
# Its state and tools, never the env (values often a token) or the
|
|
281
|
+
# command's args (which may carry a token too): a failing spec or a
|
|
282
|
+
# debugger would print those. `command` and `env` are nil before the
|
|
283
|
+
# server is resolved.
|
|
284
|
+
def inspect = "#<Server #{name} state=#{state} command=#{Array(command).first} env=#{(env || {}).keys.join(",")} tools=#{Array(tools).size}>"
|
|
285
|
+
|
|
286
|
+
def pretty_print(printer) = printer.text(inspect)
|
|
287
|
+
end
|
|
280
288
|
|
|
281
289
|
def initialize(settings = {})
|
|
282
290
|
@settings = settings
|
|
@@ -291,6 +299,12 @@ class Plugin
|
|
|
291
299
|
@left_out_lock = Mutex.new
|
|
292
300
|
end
|
|
293
301
|
|
|
302
|
+
# The servers' names, never @settings (the raw servers config, env values
|
|
303
|
+
# included): a failing spec prints the plugin.
|
|
304
|
+
def inspect = "#<Plugin mcp servers=#{@servers.map(&:name).join(",")}>"
|
|
305
|
+
|
|
306
|
+
def pretty_print(printer) = printer.text(inspect)
|
|
307
|
+
|
|
294
308
|
# Each server's tools come from its cache (tools-<server>.json in the
|
|
295
309
|
# bundle's data dir, keyed by a digest of its config) when there is one:
|
|
296
310
|
# the server then starts on the first call of one of its tools (start:
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: slow-commands
|
|
3
|
+
version: 0.1.0
|
|
4
|
+
scope: system
|
|
5
|
+
description: "Remembers commands that ran past the execute limit in a project; the next identical execute is denied once a turn with advice to run it with task_create and task_wait; /slow-commands lists and forgets them"
|
|
6
|
+
trust_level: reviewed
|
|
7
|
+
plugin:
|
|
8
|
+
file: plugin.rb
|
|
9
|
+
sha256: sha256:00a35b8f930d8f99a908b99c5eb3a5e97b1209c63c616037072580d92d3c8639
|
|
10
|
+
# after_tool_call's call:, elapsed_ms: and result: (0.57.0)
|
|
11
|
+
requires_chi: ">= 0.57.0"
|