samagotchi 0.62.0 → 0.64.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 +78 -1
- data/README.md +4 -1
- data/bin/chi +3 -2
- data/docs/architecture.md +12 -2
- data/docs/cli.md +20 -9
- data/docs/configuration.md +16 -9
- data/docs/context.md +13 -3
- data/docs/hooks.md +6 -3
- data/docs/internals/engine.md +10 -3
- data/docs/memory.md +1 -1
- data/docs/plugins.md +96 -2
- data/docs/releasing.md +1 -1
- data/docs/sessions.md +11 -8
- data/docs/sub-agent.md +27 -4
- data/docs/testing.md +13 -4
- data/lib/samagotchi/answer_command.rb +5 -1
- data/lib/samagotchi/answer_refs.rb +18 -26
- data/lib/samagotchi/bridge/turn_accumulator.rb +1 -0
- data/lib/samagotchi/bridge.rb +41 -5
- data/lib/samagotchi/bundles/claude-peer/manifest.yml +22 -0
- data/lib/samagotchi/bundles/claude-peer/plugin.rb +406 -0
- data/lib/samagotchi/bundles/claude-peer/scripts/chi-peer-SKILL.md +104 -0
- data/lib/samagotchi/bundles/claude-peer/scripts/pair +66 -0
- data/lib/samagotchi/bundles/claude-peer/skill_claude_peer.md +60 -0
- data/lib/samagotchi/bundles/dev/manifest.yml +2 -1
- data/lib/samagotchi/bundles/loop-guard/manifest.yml +2 -2
- data/lib/samagotchi/bundles/loop-guard/plugin.rb +17 -6
- data/lib/samagotchi/bundles/slow-commands/manifest.yml +2 -2
- data/lib/samagotchi/bundles/slow-commands/plugin.rb +5 -3
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +1 -1
- data/lib/samagotchi/bundles/system/manifest.yml +3 -3
- data/lib/samagotchi/bundles/system/memory_guide.md +1 -1
- data/lib/samagotchi/child_reports.rb +7 -2
- data/lib/samagotchi/cli/parent_wait.rb +9 -2
- data/lib/samagotchi/composer_route.rb +90 -0
- data/lib/samagotchi/config.rb +6 -3
- data/lib/samagotchi/context_absorber.rb +25 -6
- data/lib/samagotchi/continue_offer.rb +17 -3
- data/lib/samagotchi/delivery.rb +13 -8
- data/lib/samagotchi/engine.rb +2 -0
- data/lib/samagotchi/host_config.rb +6 -6
- data/lib/samagotchi/host_model.rb +2 -2
- data/lib/samagotchi/idle_recap.rb +2 -3
- data/lib/samagotchi/image_store.rb +8 -0
- data/lib/samagotchi/interrupted_turn.rb +7 -3
- data/lib/samagotchi/last_turn.rb +5 -2
- data/lib/samagotchi/pending_input_queue.rb +10 -3
- data/lib/samagotchi/plugin/attached_context.rb +7 -4
- data/lib/samagotchi/plugin/sessions.rb +27 -5
- data/lib/samagotchi/recap_setup.rb +3 -3
- data/lib/samagotchi/reply_wait.rb +171 -6
- data/lib/samagotchi/row_refs.rb +3 -4
- data/lib/samagotchi/send_command.rb +63 -19
- data/lib/samagotchi/session.rb +12 -0
- data/lib/samagotchi/session_commands.rb +1 -1
- data/lib/samagotchi/session_inbox.rb +63 -7
- data/lib/samagotchi/session_manager.rb +49 -20
- data/lib/samagotchi/terminal_ui/attach_launcher.rb +6 -3
- data/lib/samagotchi/terminal_ui/attached_loop.rb +73 -35
- data/lib/samagotchi/terminal_ui/formatting.rb +3 -19
- data/lib/samagotchi/terminal_ui.rb +103 -68
- data/lib/samagotchi/tool_activity.rb +5 -5
- data/lib/samagotchi/tools/delegate.rb +28 -1
- data/lib/samagotchi/tools/delegate_cursor.rb +9 -4
- data/lib/samagotchi/tools/delegate_wait.rb +17 -5
- data/lib/samagotchi/tools/execute.rb +33 -8
- data/lib/samagotchi/turn_checkpoint.rb +9 -3
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +6 -4
- data/lib/samagotchi/web/public/activity.js +2 -2
- data/lib/samagotchi/web/public/app.js +53 -91
- data/lib/samagotchi/web/public/card.js +73 -23
- data/lib/samagotchi/web/public/chains.js +6 -4
- data/lib/samagotchi/web/public/command_view.js +39 -35
- data/lib/samagotchi/web/public/commands.js +8 -8
- data/lib/samagotchi/web/public/composer.js +3 -3
- data/lib/samagotchi/web/public/copy.js +7 -15
- data/lib/samagotchi/web/public/diff_view.js +20 -23
- data/lib/samagotchi/web/public/format.js +0 -48
- data/lib/samagotchi/web/public/images.js +12 -16
- data/lib/samagotchi/web/public/question_cards.js +3 -2
- data/lib/samagotchi/web/public/read_view.js +10 -9
- data/lib/samagotchi/web/public/scope.js +23 -0
- data/lib/samagotchi/web/public/timing.js +2 -8
- data/lib/samagotchi/web/public/turn_html.js +119 -59
- data/lib/samagotchi/web/public/turn_model.js +13 -3
- data/lib/samagotchi/web/public/turn_view.js +73 -116
- data/lib/samagotchi/web/public/vendor/directive.js +8 -0
- data/lib/samagotchi/web/public/vendor/directives/unsafe-html.js +8 -0
- data/lib/samagotchi/worker.rb +53 -35
- data/lib/samagotchi/worker_inbound.rb +12 -11
- metadata +9 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 61245b8998baef2bbb4c29cabc648314b4cc124cf9233099948cb7cf66dc890e
|
|
4
|
+
data.tar.gz: ac4e8fa165b5cc59d683b1541a3758e03f60a1d14b7fd9cded087770fda162de
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 717ca2421c97d4f93493891e4f695528a0b8495ab11a2b47c348b29c726004abb32e638ab21b3e6af62676acfc4cc31c45527bdb9dfe72a9a270b3f7bfda3e02
|
|
7
|
+
data.tar.gz: 4916e876cf70f3b1b03e8c7554a127019e08e3b6f2e991305c8fe63ce1f6cf399155c43167ff36382c09eb2774792a4783ccbe39109702df6bd4efbe67a1ff53
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,81 @@ and commands may change between minor versions. How releases are made:
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.64.0] - 2026-10-13
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `/queue TEXT` during a running turn now works in an attached terminal and the web composer too, as it did in the
|
|
16
|
+
plain REPL: TEXT runs as a turn of its own after the running one, instead of merging into it (`(queued: runs after
|
|
17
|
+
this turn)`). A session whose worker is older than this says to restart it rather than steering the turn. See
|
|
18
|
+
docs/cli.md, "Typing during a turn".
|
|
19
|
+
- `chi send --queue`: the message runs as a turn of its own after the running turn instead of merging into it, and
|
|
20
|
+
`chi send --queue --wait` waits for that turn's own answer (a running turn no longer refuses it; the running turn's
|
|
21
|
+
reply, end and questions are not the answer). Each turn's reply file now carries its message's id. See
|
|
22
|
+
docs/sessions.md, "Sending a message", and docs/sub-agent.md.
|
|
23
|
+
- Plugins: `ctx.sessions.send(id, text, delivery: :queue)` runs the message as a turn of its own after the session's
|
|
24
|
+
running turn; `delivery: :cut` raises (plugins never cut). See docs/plugins.md.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Plain REPL (`--no-shared`): the lines a turn left over (a steer it didn't merge, `/queue` lines, image lines, commands
|
|
29
|
+
that wait) run in the order you sent them; before, an unmerged steer ran ahead of a `/queue` line typed earlier.
|
|
30
|
+
- A message with images sent during a running turn no longer holds back the text lines sent after it: they go in at
|
|
31
|
+
the turn's next step as usual, and the image message still runs as a turn of its own after it.
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- Stopping a session mid-turn (`chi sessions stop`, the web's stop, a parent stopping its delegate) cancels the turn
|
|
36
|
+
with its own reason, `session_stopped`, instead of the user's Stop: the turn's line reads "canceled (session
|
|
37
|
+
stopped)", a command it was running "stopped as the session was stopped" (slow-commands 0.2.2 counts it as
|
|
38
|
+
stopped, not slow), and `chi send --wait --format json` reports `cancel_reason: session_stopped`.
|
|
39
|
+
- A delegate (and any worker a session's own commands start, such as `chi send --new`) reads `hosts:` from config.yml
|
|
40
|
+
as it starts, so it sees a host added or changed after its parent's worker started; before, it got the parent's
|
|
41
|
+
start-time hosts through `SAMAGOTCHI_HOSTS_JSON`, which workers no longer inherit (one set in your shell
|
|
42
|
+
applies to that chi process only; put hosts in config.yml).
|
|
43
|
+
- Web: a step that only called plugin tools with a verb label is titled by the label ("echoing"), not "working
|
|
44
|
+
with echoing"; among other tools such a call is named by its tool ("working with read, echo").
|
|
45
|
+
- Web: a send from a stale tab into a session deleted meanwhile goes back to the start page with a "was not
|
|
46
|
+
found" notice, the line kept in the composer, instead of an alert.
|
|
47
|
+
- `chi send`, the web composer and a plugin's `ctx.sessions.send` now report a message the session's worker refuses
|
|
48
|
+
(a 4xx answer such as a delivery it doesn't run) instead of writing it as an input file the worker would merge
|
|
49
|
+
anyway; a body over the worker's size limit still goes in as a file, as before.
|
|
50
|
+
- `chi answer`, `chi send --wait ID` (no message) and `delegate_result` wait for the reply of the turn they are about
|
|
51
|
+
(the answered turn, the running turn, the follow-up's turn), not a newer one queued behind it; a Continue of the
|
|
52
|
+
step-limit question gets the continued turn's reply, and a Stop ends the wait as `not_continued`. Workers older
|
|
53
|
+
than this keep the old behaviour.
|
|
54
|
+
|
|
55
|
+
## [0.63.0] - 2026-10-13
|
|
56
|
+
|
|
57
|
+
### Added
|
|
58
|
+
|
|
59
|
+
- The claude-peer bundle (in the dev profile): pair a chi session with a Claude Code session you work with, and
|
|
60
|
+
the `claude_send` tool sends Claude a part of the work (a plan step, a diff, a finding) to validate, as context or
|
|
61
|
+
with a question; Claude's answer comes back as the `claude-review` context. `/claude validate|context <text>` and
|
|
62
|
+
`/claude last` send without the model, `/claude` shows whether a Claude is paired, and `/claude install-skill`
|
|
63
|
+
installs Claude's `chi-peer` skill, which pairs ("pair with chi <id>"). See docs/plugins.md, "The claude-peer
|
|
64
|
+
bundle".
|
|
65
|
+
- Plugins: `ctx.context.list` gives each source's last `summary:` too. See docs/plugins.md, "Attached context".
|
|
66
|
+
|
|
67
|
+
### Fixed
|
|
68
|
+
|
|
69
|
+
- Attached context: a source added with `chi context add NAME --push` now wakes an idle session when its first
|
|
70
|
+
push asks to (`"wake": true`); before, only later pushes could.
|
|
71
|
+
- Plugins: `ctx.context.list` returns `[]` for a new session whose file isn't saved yet (a plugin command before
|
|
72
|
+
the first turn) instead of raising "Session not found".
|
|
73
|
+
- An after_turn hook's file refs on the answer (`event[:refs]`) must be under the session's folder, as a plugin
|
|
74
|
+
tool row's are: a file elsewhere is refused (and logged), and a stored one is dropped when the answer is shown.
|
|
75
|
+
- loop-guard (0.3.8): the card for a turn it stopped prints under the blocked call's row in the terminal, not
|
|
76
|
+
before it.
|
|
77
|
+
- Attached TUI: joining a session whose last turn was images alone (sent from the web) names each image once, not
|
|
78
|
+
twice.
|
|
79
|
+
- Attached TUI: text you had typed when the step-limit question opened comes back at the prompt once you answer
|
|
80
|
+
it, instead of being lost.
|
|
81
|
+
- `chi -p … --non-interactive` says so on stderr and exits 1 when the iteration limit cuts the turn short, instead
|
|
82
|
+
of exiting 0 with the partial text as if it were the answer.
|
|
83
|
+
- `chi --no-shared --resume` drops a question the session's last worker saved and died with, as an attached resume
|
|
84
|
+
does, instead of keeping it in the session file.
|
|
85
|
+
|
|
11
86
|
## [0.62.0] - 2026-10-13
|
|
12
87
|
|
|
13
88
|
### Added
|
|
@@ -2719,7 +2794,9 @@ and long-lived sessions.
|
|
|
2719
2794
|
- A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
|
|
2720
2795
|
hotkey panel that send selected text or the clipboard to your sessions.
|
|
2721
2796
|
|
|
2722
|
-
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.
|
|
2797
|
+
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.64.0...HEAD
|
|
2798
|
+
[0.64.0]: https://github.com/dm1try/samagotchi/compare/v0.63.0...v0.64.0
|
|
2799
|
+
[0.63.0]: https://github.com/dm1try/samagotchi/compare/v0.62.0...v0.63.0
|
|
2723
2800
|
[0.62.0]: https://github.com/dm1try/samagotchi/compare/v0.61.0...v0.62.0
|
|
2724
2801
|
[0.61.0]: https://github.com/dm1try/samagotchi/compare/v0.60.0...v0.61.0
|
|
2725
2802
|
[0.60.0]: https://github.com/dm1try/samagotchi/compare/v0.59.0...v0.60.0
|
data/README.md
CHANGED
|
@@ -150,7 +150,10 @@ task to a child session that runs in parallel and reports back only its final
|
|
|
150
150
|
reply (a normal session: `chi --attach <id>` steers it). With the `coordinator`
|
|
151
151
|
bundle (in `dev`), `/coordinate <goal>` has chi split the work into tasks, each
|
|
152
152
|
in a child session in its own git worktree, check each report and ask you
|
|
153
|
-
before each merge; `/children` shows the children and their state.
|
|
153
|
+
before each merge; `/children` shows the children and their state. With the
|
|
154
|
+
`claude-peer` bundle (in `dev`), chi sends a part of its work to a Claude Code
|
|
155
|
+
session you pair it with, to validate or as context, and Claude answers back
|
|
156
|
+
into chi (`/claude`).
|
|
154
157
|
|
|
155
158
|
`chi send` is the other half: the text goes in as your message, the same as
|
|
156
159
|
typing it in the attached terminal or the web composer, and a turn runs. Piped
|
data/bin/chi
CHANGED
|
@@ -386,8 +386,9 @@ begin
|
|
|
386
386
|
scratch: scratch
|
|
387
387
|
).run
|
|
388
388
|
# `chi -p X </dev/null`: the turn failed (nothing was sent again); -p
|
|
389
|
-
# --non-interactive: the turn ended with no answer
|
|
390
|
-
|
|
389
|
+
# --non-interactive: the turn ended with no answer or at the iteration
|
|
390
|
+
# limit, or Ctrl-C cancelled it.
|
|
391
|
+
exit 1 if %i[turn_failed empty_answer iteration_limit].include?(ended)
|
|
391
392
|
exit Samagotchi::CLI::Exit::INTERRUPTED if ended == :interrupted
|
|
392
393
|
rescue Samagotchi::TerminalUI::SessionBusy, Samagotchi::TerminalUI::SessionNotFound, Samagotchi::ModelProfile::MissingModel => e
|
|
393
394
|
warn "Error: #{e.message}"
|
data/docs/architecture.md
CHANGED
|
@@ -73,6 +73,12 @@ TerminalUI ── delegates ──▶ Engine
|
|
|
73
73
|
delegates all core work to an `Engine`.
|
|
74
74
|
- The `on_event:` seam on `run_turn` exposes raw `KernelLoop` events plus the
|
|
75
75
|
higher-level turn events, so any new UI can render without terminal coupling.
|
|
76
|
+
- **`ComposerRoute`** (`lib/samagotchi/composer_route.rb`) decides where a line typed at
|
|
77
|
+
either terminal's prompt goes (the REPL's `steer_line` / `run_input_line`, the attached
|
|
78
|
+
`AttachedLoop#submit`): `/cut TEXT` / `/queue TEXT`, a terminal's own word, a session
|
|
79
|
+
command, a typo, a prompt. Each UI keeps what it does with the route. The web's
|
|
80
|
+
`composer.js#routeComposerLine` decides the same; `spec/fixtures/composer_routes.json`
|
|
81
|
+
holds the cases both answer (see [testing.md](testing.md)).
|
|
76
82
|
|
|
77
83
|
## Request / turn flow
|
|
78
84
|
|
|
@@ -369,7 +375,11 @@ session every 10 s, so a `kill -9` shows within a second. The summary (`Web::Ses
|
|
|
369
375
|
shared with `/api/sessions` and the session view) carries `owner`, `project_root` and
|
|
370
376
|
`bridge_up` (the sidecar is there *and* the lock is held: the page attaches its stream on it), and
|
|
371
377
|
from that sidecar `worker_version` (the chi the worker runs) and `worker_features`
|
|
372
|
-
(`Bridge::FEATURES`, what a client may ask of it beyond the routes
|
|
378
|
+
(`Bridge::FEATURES`, what a client may ask of it beyond the routes: `restart`, `task_stop`, `reparent`,
|
|
379
|
+
`delivery`, a worker that queues a message for after the turn; `SessionManager.deliver_turn` and
|
|
380
|
+
the attached TUI refuse a queue to a worker without it, which would steer; `turn_ids`, a worker whose running
|
|
381
|
+
turn's mark, continue turn and Stop carry the turn's `enqueued_id`, which `chi answer`, `chi send --wait` with no
|
|
382
|
+
message and `delegate_result` rely on through `ReplyWait.own_turn`). A worker's Bridge snapshot
|
|
373
383
|
names its `chi_version` too.
|
|
374
384
|
`POST/DELETE /api/sessions` and `/stop` rescan the session before answering (`SessionHub#touch`).
|
|
375
385
|
`GET /api/sessions` is the hub's projection too (its sort, paging and total). `Server` always builds
|
|
@@ -448,7 +458,7 @@ stream events (server counts, else a chars/4 estimate). Model lists are `LLM::Mo
|
|
|
448
458
|
server (`/props`), then the host's model list, then config.
|
|
449
459
|
|
|
450
460
|
**Keys.** A host's API key comes only from the environment variable its
|
|
451
|
-
`api_key_env:` names; it never reaches config.yml, `
|
|
461
|
+
`api_key_env:` names; it never reaches config.yml, `SAMAGOTCHI_HOSTS_JSON`, `chi self`,
|
|
452
462
|
logs or events.
|
|
453
463
|
|
|
454
464
|
## Other components
|
data/docs/cli.md
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
- `chi sessions list|stop|restart|archive|unarchive|delete|prune|clean|stats` — manage persisted sessions; `list` shows this git project's, `list --scope=all` every one, a delegated session with `↳ <parent>`, `list --archived` the archived ones too (see [Sessions](sessions.md)). `stats ID` prints a session's cost, tokens and progress (a live worker's `GET stats`, else its saved `analytics.json`; `--format json` one object) without running a model turn. A usage error (an unknown subcommand or flag, a flag missing its value, `stop`, `restart` or `delete` with no ids, a bad `list --format` or `--scope`) exits 2; an unknown or refused session exits 1
|
|
19
19
|
- `chi note [--source NAME] [-m TEXT] (ID|PREFIX)... | --all` — add a context note (TEXT or stdin) to sessions: background the model sees on its next turn; it starts no turn (see [Sessions: Context notes](sessions.md#context-notes))
|
|
20
20
|
- `chi context <add|push|ls|show|refresh|rm|mute|unmute> …` — attach live external text to sessions (a command chi runs every so often, or text pushed in); the model gets a short note when it changes and reads it with `context_read`; `chi context add <URL>` attaches a GitHub PR with the github-pr bundle (see [Attached context](context.md))
|
|
21
|
-
- `chi send [-m TEXT] [--image PATH]... [--cut] (ID|PREFIX)...` — send a message to sessions as if typed there: a turn starts (or a running one takes it at its next step; `--cut` asks it to cut long thinking for it); piped stdin goes above `-m` as quoted context, and `--image` attaches images (see [Sessions: Sending a message](sessions.md#sending-a-message)); `--new` starts a session with it instead (`--new --continues ID|last:ID` the next link of that session's chain, see [Session chains](sessions.md#session-chains)), and `--wait` prints the answer (`--wait ID` with no message waits for the next reply without sending; `--format json` prints one JSON object instead; exit 3 a question waits, its options on stderr; exit 4 `--timeout` passed with the turn still running; see [Starting a session](sessions.md#starting-a-session))
|
|
21
|
+
- `chi send [-m TEXT] [--image PATH]... [--cut | --queue] (ID|PREFIX)...` — send a message to sessions as if typed there: a turn starts (or a running one takes it at its next step; `--cut` asks it to cut long thinking for it; `--queue` runs it as a turn of its own after the running one, and `--queue --wait` waits for that turn's own answer); piped stdin goes above `-m` as quoted context, and `--image` attaches images (see [Sessions: Sending a message](sessions.md#sending-a-message)); `--new` starts a session with it instead (`--new --continues ID|last:ID` the next link of that session's chain, see [Session chains](sessions.md#session-chains)), and `--wait` prints the answer (`--wait ID` with no message waits for the next reply without sending; `--format json` prints one JSON object instead; exit 3 a question waits, its options on stderr; exit 4 `--timeout` passed with the turn still running; see [Starting a session](sessions.md#starting-a-session))
|
|
22
22
|
- `chi answer ID --question QID (--option N|LABEL)... [--text T] [--timeout S] [--format json]` (or `--dismiss`) — answer the question a session waits on (the one `chi send --wait` exited 3 with), then wait and print what comes next as `chi send --wait` does: the reply (0), the next question (3), still running after `--timeout` (4). `--option` is 1-based or the label, repeated on a multi-select question; `--text` is free text, or a Deny's reason; `--dismiss` leaves it unanswered and the model finishes its reply. A question no longer open (the web answered first) isn't answered again: it waits for that turn's reply. An option the question doesn't offer exits 2; a worker that is gone exits 1 (`send the task again: chi send --wait -m "…" ID`). An approval can be denied, not allowed, unless `guardrails.parent_approvals: once` (a convention, not a security boundary; see [Guardrails](guardrails.md#approvals-from-a-parent-agent))
|
|
23
23
|
- `chi desktop install|upgrade|uninstall|status` — the macOS "Send to chi" helper: a Service and a ⌃⌥⌘N hotkey that send text or images to a session (live, stopped or new) as a message (⏎, a turn runs) or a context note (⌘⏎) (see [Desktop helper](desktop.md))
|
|
24
24
|
- `chi models [--format text|json] [--timeout S] [TEXT]` — list the models every configured host offers, as the names `--model` takes (see [Listing the models](#listing-the-models))
|
|
@@ -250,7 +250,10 @@ Notes:
|
|
|
250
250
|
retry lines and hooks' notices as they come, the after_turn hooks' ones
|
|
251
251
|
(source-links' `sources:`) after the answer; and the error of a failed turn,
|
|
252
252
|
then `chi: the session is kept with your prompt; continue it with: chi
|
|
253
|
-
--resume ID` (exit 1).
|
|
253
|
+
--resume ID` (exit 1). A turn the iteration limit (`turn.max_iterations`)
|
|
254
|
+
cut short prints what the model wrote so far, then `chi: the iteration limit
|
|
255
|
+
(N) was reached before the turn finished; continue it with: chi --resume ID`
|
|
256
|
+
on stderr (exit 1). Ctrl-C saves the session with the prompt and a
|
|
254
257
|
cancel note and exits 130.
|
|
255
258
|
Nobody can answer there: an approval is denied, and a question the model
|
|
256
259
|
asks (`ask_user_question`) is dismissed at once (one line on stderr), never
|
|
@@ -295,8 +298,8 @@ and cuts nothing. `/cut TEXT` in a terminal or the web composer (or `chi send --
|
|
|
295
298
|
once the model has streamed only thinking for `steer.cut_after` seconds (per host/model: `steer_cut_after`; default
|
|
296
299
|
20), now or while your message waits, that generation is cut and the step starts again with your message, marked
|
|
297
300
|
`↪ cut in for your message` (see
|
|
298
|
-
[configuration.md](configuration.md#messages-during-a-running-turn-steer-and-cut)).
|
|
299
|
-
composer
|
|
301
|
+
[configuration.md](configuration.md#messages-during-a-running-turn-steer-and-cut)). `/queue TEXT`
|
|
302
|
+
(terminal or web composer, or `chi send --queue`) keeps TEXT for after the turn: it runs as a turn of its own once the running one ends. The
|
|
300
303
|
first answer to an `ask_user_question` wins; the other UIs close their widget.
|
|
301
304
|
An empty answer dismisses the question in every UI. The model is then told
|
|
302
305
|
not to go ahead with what it asked about, or change anything else, and to wait.
|
|
@@ -538,10 +541,18 @@ REPL alike:
|
|
|
538
541
|
20 s)` (it waits for the thinking; the step may end first) or `(cutting is
|
|
539
542
|
off; goes in at the next step)` (`steer.cut_after` is 0, per host/model:
|
|
540
543
|
`steer_cut_after`, or no generation to cut).
|
|
541
|
-
- `/queue TEXT`
|
|
542
|
-
|
|
543
|
-
`(
|
|
544
|
-
|
|
544
|
+
- `/queue TEXT` runs TEXT as a turn of its own after this one, in an attached
|
|
545
|
+
terminal, the plain REPL and the web composer alike: `(queued: runs after this
|
|
546
|
+
turn)` (the web draws it as a queued bubble until its turn starts). It never
|
|
547
|
+
merges into the running turn, and a plain line sent after it still does.
|
|
548
|
+
Several queued lines run one turn each, in the order sent; they still run
|
|
549
|
+
after a Ctrl-C or a failed turn, and a queued line drops a continue offer as
|
|
550
|
+
any prompt does. Other terminals show someone else's queued line with the same
|
|
551
|
+
note. A session whose worker is older than queueing would steer it instead, so
|
|
552
|
+
it is not sent: `(/queue needs a newer worker: restart it with chi sessions
|
|
553
|
+
restart, then send again)` (the web shows the refusal under the line). A line
|
|
554
|
+
with images works the same way: it waits for the turn's end, and the lines
|
|
555
|
+
sent after it still go in at the next step.
|
|
545
556
|
At the open prompt (no turn running) `/cut TEXT` and `/queue TEXT` run TEXT
|
|
546
557
|
as a normal prompt.
|
|
547
558
|
- Commands answer, wait or are refused, by what they do:
|
|
@@ -833,7 +844,7 @@ Notes:
|
|
|
833
844
|
- Without `--default` the command is session-scoped and does not rewrite config files.
|
|
834
845
|
- With `--default` the new default is written to `~/.config/samagotchi/config.yml` (honoring `XDG_CONFIG_HOME`) and takes effect for all new sessions; the current session's effective model is also updated immediately. Bare aliases and `host:model` are both valid.
|
|
835
846
|
- `--default` and `--alias` change only their one key's line in `config.yml` (`default.model`, or the alias under `model_aliases:`): comments and the rest of the file stay as written, a symlinked file is written through and keeps its mode. When the edited text wouldn't read back as the expected settings (a flow-style section, anchors), chi writes the whole file out from the parsed data instead, which drops its comments.
|
|
836
|
-
-
|
|
847
|
+
- A worker reads `hosts:` from config.yml when it starts (not from the chi that started it).
|
|
837
848
|
- The idle recap uses the session's current model (a switch counts from the next recap), unless `recap: {host_ref, model}` pins one.
|
|
838
849
|
|
|
839
850
|
## Session recap
|
data/docs/configuration.md
CHANGED
|
@@ -106,7 +106,10 @@ Behavior:
|
|
|
106
106
|
at its start until it is stopped; the web's model picker lists a `hosts:`
|
|
107
107
|
edit at once). A worker gets the CLI flags of the chi that started it
|
|
108
108
|
through its environment.
|
|
109
|
-
-
|
|
109
|
+
- A worker reads `hosts:` from the file as it starts, as every other setting, so a delegate (or any session
|
|
110
|
+
started later) sees a host added after its parent started. `SAMAGOTCHI_HOSTS_JSON` (a JSON object in the shape
|
|
111
|
+
of `hosts:`) replaces the file's `hosts:` for the process it is set on, the way specs use it; no worker inherits
|
|
112
|
+
it (`SessionManager.spawn_options` unsets it).
|
|
110
113
|
|
|
111
114
|
This lets you run `chi` without repeating common defaults such as model
|
|
112
115
|
and llama host/port on every invocation.
|
|
@@ -372,7 +375,7 @@ system prompt stays the same; a server that swaps models between turns goes unno
|
|
|
372
375
|
`server (chat_template)`, `name`, `default`); `chi self` shows what config says without asking the server. A chat
|
|
373
376
|
host (`api: openai`) formats nothing itself: its profile comes from the name and only strips thought tags.
|
|
374
377
|
|
|
375
|
-
Workers
|
|
378
|
+
Workers read `hosts:` (with `profile:`) and `models:` from the config file.
|
|
376
379
|
`--profile` reaches the worker a chi starts, but a worker that another process wakes later (`chi web`, `--attach`
|
|
377
380
|
after an idle exit) gets that process's environment, so put a lasting choice in config.
|
|
378
381
|
|
|
@@ -416,8 +419,8 @@ models:
|
|
|
416
419
|
|
|
417
420
|
`/model` shows what applies (`sampling: temperature=0.6 presence_penalty=1.5 (hosts.work)`), and each request's
|
|
418
421
|
`stream` line in the debug log carries a `sampling=` field with what was sent. The fields are read every turn, so a
|
|
419
|
-
`/model` switch takes the new model's. A worker
|
|
420
|
-
|
|
422
|
+
`/model` switch takes the new model's. A worker reads `hosts:` (with `sampling:`) from the config file when it
|
|
423
|
+
starts, and `models:` each turn: after changing a host's `sampling:`,
|
|
421
424
|
stop the session's worker (`chi sessions stop`) for it to take effect.
|
|
422
425
|
|
|
423
426
|
## Prices
|
|
@@ -907,7 +910,8 @@ that model's cut wait (`steer.cut_after` seconds, default `20`, `0` = never; cou
|
|
|
907
910
|
so the wait for a busy server doesn't count) and still does, and the model starts the step again with the message
|
|
908
911
|
(`↪ cut in for your message`). One that comes earlier cuts once the thinking passes it, unless the step ends first
|
|
909
912
|
and the message goes in there. A plain message (from the web composer, `chi send` without `--cut`, a parent agent)
|
|
910
|
-
never asks for a cut
|
|
913
|
+
never asks for a cut, nor does a queued one (`/queue TEXT`, `chi send --queue`, `ctx.sessions.send(…, delivery:
|
|
914
|
+
:queue)`), which runs as a turn of its own after the running one. Such a cut spends no `retry.empty_answer` attempt and sends no hidden note; only the cut
|
|
911
915
|
thinking is lost. A generation that has streamed visible text or a tool call is never cut, and a plugin's steer
|
|
912
916
|
(`ctx.steer`, `ctx.sessions.send`) never cuts.
|
|
913
917
|
|
|
@@ -931,9 +935,12 @@ models:
|
|
|
931
935
|
|
|
932
936
|
`/model` shows the resolved wait (`; steer cut: after 60 s (models: qwen)`, `; steer cut: never (hosts.splash)`).
|
|
933
937
|
|
|
934
|
-
`/queue TEXT` in the
|
|
935
|
-
|
|
936
|
-
`
|
|
938
|
+
`/queue TEXT` (in a terminal or the web composer) runs TEXT as a turn of its own after the running one: the request
|
|
939
|
+
carries `delivery: "queue"`, the input file keeps it, and the worker's drain leaves the file for after the turn. The
|
|
940
|
+
Bridge acks `delivery: "queue"` while a turn runs (to an idle session the message is simply the next turn:
|
|
941
|
+
`next_step`). A worker whose sidecar doesn't name the `delivery` feature (older than queueing) would steer it, so the
|
|
942
|
+
sender refuses it (`409 delivery_unavailable`, "restart it"), and so does a worker that answers the request with a
|
|
943
|
+
400 `delivery_unavailable`.
|
|
937
944
|
|
|
938
945
|
## Server errors
|
|
939
946
|
|
|
@@ -1350,7 +1357,7 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
|
|
|
1350
1357
|
| `memory.index_warn_tokens` | `2500` | yes | The tokens one scope's memory index (project or system; sent with every prompt) may hold before a `memory_write`, `write` or `edit` that takes it over gets a note asking the model to tighten long index descriptions; `0`: no note. Estimated as characters / `context.chars_per_token`. `memory:` is its own section, not the `memories:` preload list. See [Memory](memory.md). |
|
|
1351
1358
|
| `context.status_thresholds` | `20,40,60,80` | yes | Percentages that put the estimate into a bucket; the rise into a guidance bucket (from the second up) leaves the model its own `[CONTEXT: …]` line, and the bucket decides the guidance. |
|
|
1352
1359
|
| `context.every_seconds` | `300` | | How often an attached context source's command runs when it has no `--every` (seconds, at least 30). See [Attached context](context.md). |
|
|
1353
|
-
| `context.wake` | `true` | yes | A source whose update says `wake: true` may start a turn in a live, idle session (one per source per 10 minutes, within `session.max_wakes`); `false`: updates wait as notes for the next turn. See [Waking](context.md#waking). |
|
|
1360
|
+
| `context.wake` | `true` | yes | A source whose update says `wake: true` may start a turn in a live, idle session (one per source per 10 minutes, within `session.max_wakes`); so may a session's pushed source's first push. `false`: updates wait as notes for the next turn. See [Waking](context.md#waking). |
|
|
1354
1361
|
| `thinking.turn_preamble` | `true` | yes | Ask a `qwen36` model to open its thinking with a short `TURN:` line (the step label). |
|
|
1355
1362
|
| `reasoning_history` | `turn` | | Which of its reasoning a chat host's model gets back: `drop`, `turn`, `all`; environment only (`SAMAGOTCHI_REASONING_HISTORY`), below a session's own level (`/reasoning-history`), above the `models:`/`hosts:` entries. See "Reasoning history". |
|
|
1356
1363
|
| `thinking.level` | `default` | `--thinking` | `off`, `low`, `medium`, `high` or `default` for every model; the flag and env outrank the `models:`/`hosts:` entries, the file's value doesn't; a session's own level outranks them all, and `--thinking` sets it on a session run (on `chi web` it sets this). See "Thinking". |
|
data/docs/context.md
CHANGED
|
@@ -123,6 +123,11 @@ chi context add clip --push --project
|
|
|
123
123
|
pbpaste | chi context push clip --project
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
+
A Claude Code session can be a pushed source too: the
|
|
127
|
+
[claude-peer bundle](plugins.md#the-claude-peer-bundle)'s `pair` script attaches
|
|
128
|
+
`claude-inbox` (Claude's inbox socket as the hint) and `claude-review`, where
|
|
129
|
+
Claude pushes its answers as JSON with `wake: true`.
|
|
130
|
+
|
|
126
131
|
### A stream: chat, a log, a feed
|
|
127
132
|
|
|
128
133
|
A source's text is a snapshot, not a log: each run replaces it. For
|
|
@@ -233,6 +238,10 @@ you're away from the terminal. All of these must hold:
|
|
|
233
238
|
- the source asked (`"wake": true`; a plain-text source never does), in
|
|
234
239
|
any change since the session last heard of it: a later change that doesn't
|
|
235
240
|
ask doesn't take the wake back;
|
|
241
|
+
- it's a change, not the source's first text, except for a session's pushed
|
|
242
|
+
source (`chi context add NAME --push`, attached empty): its first push can
|
|
243
|
+
wake too. A command or URL source's first fetch runs as you attach it, and a
|
|
244
|
+
project source's first text may predate the session, so neither wakes;
|
|
236
245
|
- `context.wake` is on (default `true`);
|
|
237
246
|
- the session's worker is up and idle: no turn running, nothing queued, no
|
|
238
247
|
question or continue offer waiting, not just started;
|
|
@@ -242,15 +251,16 @@ you're away from the terminal. All of these must hold:
|
|
|
242
251
|
- the source hasn't woken the session in the last 10 minutes.
|
|
243
252
|
|
|
244
253
|
Otherwise the change arrives as a note, as above. The turn starts with the
|
|
245
|
-
update note, whose last
|
|
254
|
+
update note, or, for a session's pushed source, its attached note, whose last
|
|
255
|
+
line becomes:
|
|
246
256
|
|
|
247
257
|
> chi started this turn because the source changed; your user didn't write anything. Tell your user in a sentence or two what changed and what it may mean for their work. Don't run commands, change files or reply on the source for it: wait for your user to ask.
|
|
248
258
|
|
|
249
259
|
Every UI labels the turn `context <name> changed`, and the web's
|
|
250
260
|
notifications count it as a finished turn however short.
|
|
251
261
|
|
|
252
|
-
A wake turn that fails goes back to before it and keeps
|
|
253
|
-
plain
|
|
262
|
+
A wake turn that fails goes back to before it and keeps its note as a
|
|
263
|
+
plain one; the model reads `[SYSTEM: the previous turn failed before any
|
|
254
264
|
answer: … The wake turn for the change in attached context <name> was not
|
|
255
265
|
answered; chi starts no other wake turn until the user writes.]`.
|
|
256
266
|
|
data/docs/hooks.md
CHANGED
|
@@ -301,9 +301,12 @@ end
|
|
|
301
301
|
keywords only (String or Symbol keys inside `target` and `actions`).
|
|
302
302
|
- `match`: the text in the answer the ref stands for (at most 500 characters).
|
|
303
303
|
- `kind`: a short lowercase word. `"file"` takes `target: {path:, line:, end_line:}`
|
|
304
|
-
(the lines optional);
|
|
305
|
-
folder,
|
|
306
|
-
`
|
|
304
|
+
(the lines optional); the path is absolute or relative to the session's
|
|
305
|
+
folder, and must be **under** it (its own spelling or its real path:
|
|
306
|
+
`/tmp/x` or `/private/tmp/x`), as a plugin tool row's file refs
|
|
307
|
+
([plugins.md](plugins.md)): a file elsewhere is refused. It is kept under
|
|
308
|
+
the folder's real path. Any other kind (`"issue"`, `"commit"`, …) takes
|
|
309
|
+
`target: {id:}`.
|
|
307
310
|
- `label`: what a UI shows for it (optional, at most 200 characters).
|
|
308
311
|
- `actions`: links a UI offers on top of the kind's own, each
|
|
309
312
|
`{id:, label:, url:}`; http(s) URLs only, without userinfo (`user:pass@`),
|
data/docs/internals/engine.md
CHANGED
|
@@ -54,8 +54,10 @@ Engine#run_turn(session, prompt, on_event:, max_iterations:, cancel_controller:,
|
|
|
54
54
|
│
|
|
55
55
|
├── complete_turn(turn, result)
|
|
56
56
|
│ # end_turn(turn, "completed" | "canceled"): under the event lock the session gets the kept
|
|
57
|
-
│ # messages, goes IDLE, records last_turn
|
|
58
|
-
│ #
|
|
57
|
+
│ # messages, goes IDLE, records last_turn (enqueued_id: the turn's input, from its origin;
|
|
58
|
+
│ # a continue turn's origin carries its offer turn's, ContinueOffer#run_continue_turn),
|
|
59
|
+
│ # and :turn_completed {result, turn_summary, display_pending} or :turn_canceled
|
|
60
|
+
│ # {cancellation_reason, cancelled_by, duration_ms} is emitted
|
|
59
61
|
│ # then the :after_turn hook (status, messages, present: the AnswerDisplay presenter,
|
|
60
62
|
│ # refs: the AnswerRefs adder)
|
|
61
63
|
│ # store_answer_display (one replace of the answer: `display` and/or `display_refs`;
|
|
@@ -521,7 +523,12 @@ Both loops:
|
|
|
521
523
|
- **A cut generation** (`after_cut`): a generation a plugin cut (`stop_generation`) spends the same retry budget
|
|
522
524
|
with its own nudge; when it is used up, the turn ends cancelled (`:hook`). A steer's cut asks again with the
|
|
523
525
|
steer and spends nothing.
|
|
524
|
-
- **Steering**: queued input goes in at iteration boundaries (`Steer.inject!`, `:pending_input_merged`).
|
|
526
|
+
- **Steering**: queued input goes in at iteration boundaries (`Steer.inject!`, `:pending_input_merged`). A
|
|
527
|
+
worker's drain (`Worker#pending_input_drain`) takes every input file in name order except the ones
|
|
528
|
+
`SessionInbox.waits_for_turn_end?` leaves for a turn of their own (`delivery: queue`, or images): those are
|
|
529
|
+
skipped, not a barrier, so a line sent after them still merges. Only the files it claims reach
|
|
530
|
+
`:input_merged`, the failure hand-back (`@merged_this_turn`) and the checkpoint; the skipped ones run after the
|
|
531
|
+
turn, one turn each, in name order (`Worker#run_input_files`).
|
|
525
532
|
- **Context status**: an estimate of the window's fill before each request,
|
|
526
533
|
emitted on every request (`:context_status`) so the UI always has a current
|
|
527
534
|
number, with a guidance line on the tail only when it rises far enough.
|
data/docs/memory.md
CHANGED
|
@@ -270,7 +270,7 @@ profiles, meta bundles that install a set of them:
|
|
|
270
270
|
| profile | bundles |
|
|
271
271
|
|---|---|
|
|
272
272
|
| `core` | loop-guard, check-in, guardrails, slow-commands, agents-md: the recommended safety set |
|
|
273
|
-
| `dev` | known-names, mcp, btw, skills, source-links, github-pr, coordinator |
|
|
273
|
+
| `dev` | known-names, mcp, btw, skills, source-links, github-pr, coordinator, claude-peer |
|
|
274
274
|
|
|
275
275
|
`chi bootstrap` installs `core` and, on a terminal, offers `dev`. An existing
|
|
276
276
|
install gets neither by itself: `chi bundle install core` (or `dev`).
|
data/docs/plugins.md
CHANGED
|
@@ -675,6 +675,7 @@ llama.cpp serves on a native host too.
|
|
|
675
675
|
id = ctx.sessions.fork(messages: ctx.messages + [{ role: "user", content: q }, { role: "model", content: a }],
|
|
676
676
|
title: "btw: #{q}")
|
|
677
677
|
ctx.sessions.send(id, "go on from here")
|
|
678
|
+
ctx.sessions.send(id, "then the docs", delivery: :queue) # after its running turn
|
|
678
679
|
ctx.sessions.read(id) # => {id:, title:, status:, parent_id:, running:, messages:}
|
|
679
680
|
ctx.sessions.children # => [{id:, short_id:, state:, branch:, last_reply:, reported:, ...}]
|
|
680
681
|
ctx.sessions.stop(id) # one of this session's own children
|
|
@@ -695,6 +696,12 @@ ctx.sessions.stop(id) # one of this session's own children
|
|
|
695
696
|
5 s for the session's worker, so call it from an anytime command or a
|
|
696
697
|
thread of your own, never from a tool or hook of a running turn. A
|
|
697
698
|
session open in a chi REPL can't take it.
|
|
699
|
+
- A running turn takes it at its next step. `delivery: :queue` runs it as
|
|
700
|
+
a turn of its own after that turn instead (as `/queue TEXT` does; to an
|
|
701
|
+
idle session it simply runs). A plugin never cuts the running turn's
|
|
702
|
+
thinking: `delivery: :cut` raises `Samagotchi::Plugin::Sessions::Error`,
|
|
703
|
+
as does an unknown value. A worker older than queueing refuses a queue
|
|
704
|
+
(it would steer): `Error` with its reason; nothing is sent.
|
|
698
705
|
- `read(id)` gives a session now: from its worker when one runs (with a
|
|
699
706
|
running turn so far, `running: true`), else as saved. `messages` has no
|
|
700
707
|
system prompt.
|
|
@@ -722,7 +729,7 @@ ctx.sessions.stop(id) # one of this session's own children
|
|
|
722
729
|
```ruby
|
|
723
730
|
ctx.context.attach(url: "https://github.com/acme/app/pull/42", why: "branch feat/x has open PR #42")
|
|
724
731
|
ctx.context.attach(name: "ci", cmd: "bin/ci-status", why: "this branch's CI", every_seconds: 120)
|
|
725
|
-
ctx.context.list # => [{name:, scope:, why:, hint:, fetched_at:, error:, provider:}]
|
|
732
|
+
ctx.context.list # => [{name:, scope:, why:, hint:, fetched_at:, error:, provider:, summary:}]
|
|
726
733
|
ctx.context.decline(url: "https://github.com/acme/app/pull/42") # => "pr-42"
|
|
727
734
|
ctx.context.mark_offered("pr-42", "https://github.com/acme/app/pull/42", why: "branch feat/x has open PR #42")
|
|
728
735
|
ctx.context.offered("pr-42") # => #<data Offer name="pr-42", hint="https://…", why="branch…", at="2026-10-08T…Z">, or nil
|
|
@@ -756,7 +763,10 @@ gets a note when its text changes.
|
|
|
756
763
|
- The source is the session's (not the project's), marked
|
|
757
764
|
`added_by: plugin:<bundle>`. It is plugin code, so it isn't asked about as
|
|
758
765
|
the agent's `chi context add --cmd` is.
|
|
759
|
-
- `list` gives the session's sources, its own then its project's
|
|
766
|
+
- `list` gives the session's sources, its own then its project's (`[]` before
|
|
767
|
+
the session has an id or its file is saved: a command before the first turn). `hint` is the
|
|
768
|
+
one the last text gave (else the source's), `summary` that text's summary
|
|
769
|
+
(nil before any text; chi 0.63.0).
|
|
760
770
|
- Two attaches of one source at once (a double click) both return its
|
|
761
771
|
name.
|
|
762
772
|
- `attach`, `decline` and `mark_offered` raise
|
|
@@ -1504,6 +1514,90 @@ by themselves there.
|
|
|
1504
1514
|
reason: merging a child's branch
|
|
1505
1515
|
```
|
|
1506
1516
|
|
|
1517
|
+
## The claude-peer bundle
|
|
1518
|
+
|
|
1519
|
+
```sh
|
|
1520
|
+
chi bundle install claude-peer # in the dev profile
|
|
1521
|
+
```
|
|
1522
|
+
|
|
1523
|
+
A live link between a chi session and a Claude Code session you already work
|
|
1524
|
+
with: chi sends it a part of the work (a plan step, a diff, a finding) to
|
|
1525
|
+
validate or as context, and Claude's answer comes back into chi. This is
|
|
1526
|
+
Claude with the context it has, not a fresh review run. Nothing runs until a
|
|
1527
|
+
Claude pairs; without one the bundle costs one tool declaration.
|
|
1528
|
+
|
|
1529
|
+
- **Pairing is Claude's.** chi can't see Claude's inbox, so Claude attaches
|
|
1530
|
+
two pushed sources to the chi session: `claude-inbox`, whose hint is its
|
|
1531
|
+
per-session inbox socket (`CLAUDE_CODE_MESSAGING_SOCKET`, Claude Code
|
|
1532
|
+
2.1.224 or later), and `claude-review`, where its answers come. You never
|
|
1533
|
+
run that yourself:
|
|
1534
|
+
1. `/claude install-skill` (once) shows a card with the target,
|
|
1535
|
+
`$CLAUDE_CONFIG_DIR/skills/chi-peer/SKILL.md` (else `~/.claude/…`),
|
|
1536
|
+
whether one is there and, when it differs, a short diff; its Install (or
|
|
1537
|
+
Replace) button runs `/claude install-skill --yes`, which copies the
|
|
1538
|
+
bundle's `scripts/chi-peer-SKILL.md` there with the installed
|
|
1539
|
+
`scripts/pair` path written in. The copy happens inside chi, where no
|
|
1540
|
+
guardrail sees it: the button is your consent. The card also offers the
|
|
1541
|
+
Claude Code permission rule `Read(~/.local/state/samagotchi/plugins/claude-peer/**)`
|
|
1542
|
+
(your state folder), so Claude isn't asked before reading a long part's
|
|
1543
|
+
file; chi never writes Claude's settings.
|
|
1544
|
+
2. In Claude Code you say "pair with chi <id or chi web URL>". The skill runs
|
|
1545
|
+
`sh <bundle>/scripts/pair <id or URL>`, which attaches `claude-inbox` (the
|
|
1546
|
+
socket as its hint, pushed as JSON so a re-pair after a Claude restart
|
|
1547
|
+
refreshes it) and `claude-review`, and lists the session's context. A
|
|
1548
|
+
re-pair keeps Claude's last answer.
|
|
1549
|
+
3. Claude answers a validate or a question with a JSON push to
|
|
1550
|
+
`claude-review` (`text`, `summary`, `wake: true`), which wakes chi, the
|
|
1551
|
+
first answer too (on chi 0.62 the first one arrives as a note without
|
|
1552
|
+
waking); the model reads it with `context_read claude-review`.
|
|
1553
|
+
- **The tool `claude_send(kind, title, text)`**, `kind` being `validate`,
|
|
1554
|
+
`context` or `question`, writes one JSON line to that socket. The message
|
|
1555
|
+
starts with a header, `chi <id> · <kind> · <title>`. A text up to 4,000
|
|
1556
|
+
characters follows inline; a longer one goes to a file,
|
|
1557
|
+
`$XDG_STATE_HOME/samagotchi/plugins/claude-peer/sessions/<id>/<HHMMSS>-<slug>.md`
|
|
1558
|
+
(removed with the session), and the message names it (`payload: <path>`).
|
|
1559
|
+
The tool is declared whether or not a Claude is paired, so pairing doesn't
|
|
1560
|
+
change the prompt. Its errors say what to do: no `claude-inbox` (pair from
|
|
1561
|
+
Claude), the socket gone or refusing (that Claude session ended or
|
|
1562
|
+
restarted: pair again), a text over 200,000 characters.
|
|
1563
|
+
- **`/claude`** (an anytime command, so it works mid-turn) sends without
|
|
1564
|
+
the model:
|
|
1565
|
+
- `/claude` alone shows a card: paired or not, the socket and whether its
|
|
1566
|
+
file is there (it goes when that Claude session ends), the last send, and
|
|
1567
|
+
Claude's last answer (the `claude-review` summary).
|
|
1568
|
+
- `/claude validate <text>` and `/claude context <text>` send the text as it
|
|
1569
|
+
is, titled by its first line: paste a selection after them.
|
|
1570
|
+
- `/claude last` sends the model's last completed answer as context. Mid-turn
|
|
1571
|
+
it is the answer before the running turn, never the turn's partial text;
|
|
1572
|
+
a cancelled or failed turn has no answer, so the one before it goes.
|
|
1573
|
+
- **The skill.** `skill_claude_peer` (system scope) tells the model to send
|
|
1574
|
+
only when you ask, to pick the part rather than the session, not to wait
|
|
1575
|
+
or poll, and how to read the `claude-review` answer (a validate ends with
|
|
1576
|
+
`VERDICT: OK`, `CHANGES` or `UNSURE`).
|
|
1577
|
+
- **Asking first.** The tool has no `targets:`, so guardrails match it by name
|
|
1578
|
+
only: to be asked before each send, add a rule ([Rules in
|
|
1579
|
+
config.yml](guardrails.md#rules-in-configyml)):
|
|
1580
|
+
|
|
1581
|
+
```yaml
|
|
1582
|
+
guardrails:
|
|
1583
|
+
rules:
|
|
1584
|
+
- id: claude-send
|
|
1585
|
+
tool: claude_send
|
|
1586
|
+
verdict: ask
|
|
1587
|
+
reason: sends session content to Claude Code
|
|
1588
|
+
```
|
|
1589
|
+
|
|
1590
|
+
- **Limits.** The socket's format (`{"type":"user","message":{"role":"user","content":"…"}}`)
|
|
1591
|
+
is undocumented: it was found in, and checked on, Claude Code 2.1.296. A
|
|
1592
|
+
line Claude Code can't parse is dropped silently, so a Claude Code update
|
|
1593
|
+
could break this without an error. No auth line is sent: it is optional on
|
|
1594
|
+
macOS and Linux, and native Windows, which needs it, isn't supported.
|
|
1595
|
+
Claude's `crossSessionInbound` setting decides what it does with a peer
|
|
1596
|
+
message: an interactive auto-mode session delivers it; a session that
|
|
1597
|
+
bypasses permissions holds it for approval (in `-p` it expires after 5
|
|
1598
|
+
minutes). It is local only (a Unix socket), but it does hand session
|
|
1599
|
+
content to another agent: only what the model or you picked.
|
|
1600
|
+
|
|
1507
1601
|
## Shutdown
|
|
1508
1602
|
|
|
1509
1603
|
When the REPL exits, or a session's worker exits (an idle exit, `/exit`, a
|
data/docs/releasing.md
CHANGED
|
@@ -13,7 +13,7 @@ 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, github-pr, coordinator, and the profiles core and dev) has its own semver in its
|
|
16
|
+
loop-guard, mcp, skills, source-links, github-pr, coordinator, claude-peer, 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:`
|