samagotchi 0.122.0 → 0.124.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 +58 -1
- data/bin/chi +13 -2
- data/docs/architecture.md +36 -7
- data/docs/cli.md +48 -3
- data/docs/configuration.md +31 -6
- data/docs/desktop.md +51 -3
- data/docs/guardrails.md +112 -15
- data/docs/internals/engine.md +11 -4
- data/docs/internals/participants.md +67 -17
- data/docs/internals/prompt-caching.md +19 -7
- data/docs/internals/web-push.md +7 -3
- data/docs/sessions.md +7 -3
- data/docs/testing.md +2 -1
- data/lib/samagotchi/bridge.rb +4 -1
- data/lib/samagotchi/bundles/system/manifest.yml +1 -1
- data/lib/samagotchi/config.rb +7 -1
- data/lib/samagotchi/delivery.rb +7 -0
- data/lib/samagotchi/desktop/macos/App.swift +9 -1
- data/lib/samagotchi/desktop/macos/ChiRunner.swift +129 -17
- data/lib/samagotchi/desktop/macos/Images.swift +4 -3
- data/lib/samagotchi/desktop/macos/Panel.swift +315 -80
- data/lib/samagotchi/desktop/macos/Sessions.swift +272 -0
- data/lib/samagotchi/engine.rb +200 -29
- data/lib/samagotchi/gem_update.rb +41 -4
- data/lib/samagotchi/guardrail_wiring.rb +45 -12
- data/lib/samagotchi/guardrails/approval.rb +42 -4
- data/lib/samagotchi/guardrails/gate.rb +1 -1
- data/lib/samagotchi/guardrails/pair_boundary.rb +172 -0
- data/lib/samagotchi/guardrails/pair_command.rb +365 -0
- data/lib/samagotchi/guardrails/review_paths.rb +41 -7
- data/lib/samagotchi/guardrails/shell_lex.rb +7 -5
- data/lib/samagotchi/guardrails/shell_paths.rb +10 -1
- data/lib/samagotchi/guardrails/verdict.rb +7 -0
- data/lib/samagotchi/guardrails.rb +1 -0
- data/lib/samagotchi/guardrails_command.rb +6 -1
- data/lib/samagotchi/kernel_loop.rb +20 -0
- data/lib/samagotchi/llm/chat_loop.rb +4 -0
- data/lib/samagotchi/participant_mode.rb +77 -3
- data/lib/samagotchi/participants.rb +7 -5
- data/lib/samagotchi/review_apply.rb +10 -2
- data/lib/samagotchi/review_start.rb +38 -10
- data/lib/samagotchi/run_options.rb +43 -2
- data/lib/samagotchi/session_access/local.rb +5 -2
- data/lib/samagotchi/session_manager.rb +11 -0
- data/lib/samagotchi/session_participants.rb +18 -3
- data/lib/samagotchi/share_command.rb +2 -2
- data/lib/samagotchi/steer.rb +7 -1
- data/lib/samagotchi/system_one/client.rb +10 -2
- data/lib/samagotchi/system_prompt.rb +20 -4
- data/lib/samagotchi/terminal_ui/question_prompt.rb +17 -1
- data/lib/samagotchi/tool_runner.rb +10 -1
- data/lib/samagotchi/toolkit_binding.rb +34 -0
- data/lib/samagotchi/toolkit_report_command.rb +6 -4
- data/lib/samagotchi/toolkit_suggest.rb +181 -15
- data/lib/samagotchi/tools/builtins.rb +14 -1
- data/lib/samagotchi/update_command.rb +47 -19
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +204 -9
- data/lib/samagotchi/web/grants.rb +17 -5
- data/lib/samagotchi/web/message_display.rb +6 -1
- data/lib/samagotchi/web/participant_review.rb +3 -1
- data/lib/samagotchi/web/participant_view.rb +12 -4
- data/lib/samagotchi/web/participants_admin.rb +10 -6
- data/lib/samagotchi/web/public/app.js +73 -10
- data/lib/samagotchi/web/public/data.js +17 -0
- data/lib/samagotchi/web/public/delivery_line.js +8 -6
- data/lib/samagotchi/web/public/index.html +21 -2
- data/lib/samagotchi/web/public/participant.html +1 -1
- data/lib/samagotchi/web/public/participant.js +10 -12
- data/lib/samagotchi/web/public/participant_model.js +20 -8
- data/lib/samagotchi/web/public/participant_names.js +29 -8
- data/lib/samagotchi/web/public/participants_panel.js +17 -3
- data/lib/samagotchi/web/public/question_card.js +12 -2
- data/lib/samagotchi/web/public/question_cards.js +7 -0
- data/lib/samagotchi/web/public/stream_handlers.js +6 -2
- data/lib/samagotchi/web/public/toasts.js +13 -5
- data/lib/samagotchi/web/public/tools_overview.js +42 -0
- data/lib/samagotchi/web/public/turn_events.js +5 -4
- data/lib/samagotchi/web/public/turn_html.js +2 -2
- data/lib/samagotchi/web/public/turn_view.js +6 -4
- data/lib/samagotchi/web/public/update_notice.js +33 -4
- data/lib/samagotchi/web/public/update_sheet.js +158 -0
- data/lib/samagotchi/web/push/notifier.rb +28 -1
- data/lib/samagotchi/web/server.rb +121 -8
- data/lib/samagotchi/web/session_access_routes.rb +2 -1
- data/lib/samagotchi/web/session_hub.rb +52 -3
- data/lib/samagotchi/web/tunnel/tailscale.rb +8 -2
- data/lib/samagotchi/web/update_check.rb +186 -0
- data/lib/samagotchi/worker.rb +44 -6
- 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: e85ff88270cf7569d21ff95bba739199ceb3c53d32675b1648a18363ca5482cd
|
|
4
|
+
data.tar.gz: d9f539982d7c41b94ad40d17368041f2debed8040ebc48a27776486fe2476dc9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e3496206b25f1d329a3a915c075cefe5e0c3e1fc40b71694128c7d3fee633fc65447686efbb4c8f4f19e0872998cecb2f269db67a7428b9b519a9cc5b6469bd3
|
|
7
|
+
data.tar.gz: 3ed82d4bb9b01288f391e2d68d9eba5b71492fa66f411d7a5fd25276ac68425f8432979fd0d4d59882b741a2dfa79bcb0810fc74e3b2c7e66bb590e0c5bdbccc
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,61 @@ and commands may change between minor versions. How releases are made:
|
|
|
7
7
|
[docs/releasing.md](docs/releasing.md).
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.124.0] - 2026-10-27
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- The desktop helper's panel lists and sends to the sessions of the remotes `chi remote add` keeps: a section per
|
|
15
|
+
remote ("on studio") after this Mac's rows, each row tagged with its machine, "→ studio" in the hint while one is
|
|
16
|
+
ticked, one call per machine at once; a remote that doesn't answer within 4 s or refuses shows one line, and this
|
|
17
|
+
Mac's rows don't wait for it. A remote with a session also gets a "New session on studio in <folder>" row, in its
|
|
18
|
+
newest session's folder, on that remote's default model. The Everyone row, the model chooser and agents stay on
|
|
19
|
+
this Mac.
|
|
20
|
+
- chi web updates chi from its page: the notice's **Update** installs the release it names in a progress sheet
|
|
21
|
+
that lists kept edits, then offers **Restart workers** (each worker on an older chi; a busy one says what holds
|
|
22
|
+
it) and **Restart chi web** (in place: same port, token, flags and Tailscale Serve). `chi update --to VERSION`
|
|
23
|
+
installs an exact release; `--json` prints its rows as JSON.
|
|
24
|
+
|
|
25
|
+
Update with `chi update` (it rebuilds the desktop helper), then restart `chi web` and the session workers
|
|
26
|
+
(`chi sessions restart`). From the next release on, chi web's Update button does this from its page.
|
|
27
|
+
|
|
28
|
+
## [0.123.0] - 2026-10-27
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- chi web checks RubyGems daily and shows the newest aged, stable release newer than both the running and installed
|
|
33
|
+
chi, with its RubyGems page and a command to copy (`chi update`, or the blocker-specific command).
|
|
34
|
+
- chi web sends one 24-hour phone push per newly available chi; it skips the push when a chi web page is in front.
|
|
35
|
+
- `toolkit.suggest.mode: on` starts a new session on the kit System 1 suggests for its first prompt (or a later one
|
|
36
|
+
after `undecided`), waiting at most System 1's timeout before the first request, with a
|
|
37
|
+
`toolkit: NAME (suggested, p=…) · /toolkit to change` notice; anything uncertain keeps the default kit.
|
|
38
|
+
- Pair access for a shared working session: a participant invited to a working session (not a `chi review`
|
|
39
|
+
session) while its worker runs pair turns gets turns that act with the session's tools under the owner's
|
|
40
|
+
guardrails; the grant is stamped `mode: pair` only when the share warning said so. Existing grants, and any invited
|
|
41
|
+
while the worker was older, stay read-only review participants until they are invited again. A pair turn's asks
|
|
42
|
+
reach the owner's card naming who asks, with Allow once only, and System 1 never answers them; a write outside the
|
|
43
|
+
project always asks, whatever the owner approved before. A new core check (`pair-boundary`) denies reading or
|
|
44
|
+
writing secrets and chi's own files (keys, shell startup files, histories, credential files; by a file tool, a
|
|
45
|
+
command, a glob, a variable or a script it hands a shell), running chi, backgrounding commands, reading the
|
|
46
|
+
environment or the keychain, git settings that run commands, sweeping home with find/tar/cp/grep, the owner-voiced
|
|
47
|
+
tools (delegate, reminders, notes, background tasks,
|
|
48
|
+
forgetting outputs, loading tools, memories) and the model's own question. A pair turn stops mid-turn once the
|
|
49
|
+
participant's cap is passed or their link is revoked or expires. A pair turn's commands run without
|
|
50
|
+
secret-named environment variables and are killed at the execute limit, and one that writes outside the project
|
|
51
|
+
asks the owner. Workers name a `pair_turns` feature.
|
|
52
|
+
- Participant turns show a one-line tools overview with expandable, names-only rows that update as a turn runs.
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
|
|
56
|
+
- Sharing a working session warns what a participant's turns may do there (writes included, asks to you) instead of
|
|
57
|
+
only how many tool outputs they can quote. Its participant messages carry a pair header, a `pair` badge and no
|
|
58
|
+
Apply; review sessions, their messages and their headers are unchanged. The system prompt's participants line,
|
|
59
|
+
the cap notice, the participant page's note and the participants panel follow the session's mode; the delivery
|
|
60
|
+
line says "after their turn".
|
|
61
|
+
|
|
62
|
+
Update with `chi update`, then restart `chi web` and the session workers (`chi sessions restart`); a participant
|
|
63
|
+
invited before this release stays a review participant until invited again.
|
|
64
|
+
|
|
10
65
|
## [0.122.0] - 2026-10-27
|
|
11
66
|
|
|
12
67
|
### Added
|
|
@@ -4658,7 +4713,9 @@ and long-lived sessions.
|
|
|
4658
4713
|
- A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
|
|
4659
4714
|
hotkey panel that send selected text or the clipboard to your sessions.
|
|
4660
4715
|
|
|
4661
|
-
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.
|
|
4716
|
+
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.124.0...HEAD
|
|
4717
|
+
[0.124.0]: https://github.com/dm1try/samagotchi/compare/v0.123.0...v0.124.0
|
|
4718
|
+
[0.123.0]: https://github.com/dm1try/samagotchi/compare/v0.122.0...v0.123.0
|
|
4662
4719
|
[0.122.0]: https://github.com/dm1try/samagotchi/compare/v0.121.1...v0.122.0
|
|
4663
4720
|
[0.121.1]: https://github.com/dm1try/samagotchi/compare/v0.121.0...v0.121.1
|
|
4664
4721
|
[0.121.0]: https://github.com/dm1try/samagotchi/compare/v0.120.0...v0.121.0
|
data/bin/chi
CHANGED
|
@@ -18,6 +18,15 @@ rescue Samagotchi::ConfigFile::RemovedApi => e
|
|
|
18
18
|
end
|
|
19
19
|
require "samagotchi"
|
|
20
20
|
|
|
21
|
+
# A web self-restart re-enters through the installed wrapper: its marker and
|
|
22
|
+
# the worker starters to adopt leave ENV before anything spawns with it
|
|
23
|
+
# (Server.take_restart_env!); a restarted chi web skips the first-run hint.
|
|
24
|
+
web_restarted = false
|
|
25
|
+
if ENV.key?("SAMAGOTCHI_WEB_RESTARTED") || ENV.key?("SAMAGOTCHI_WEB_ADOPT_PIDS")
|
|
26
|
+
require "samagotchi/web/server"
|
|
27
|
+
web_restarted = Samagotchi::Web::Server.take_restart_env!
|
|
28
|
+
end
|
|
29
|
+
|
|
21
30
|
# `chi self`: print version, source dir, config/memory/session paths, model and
|
|
22
31
|
# bundles (read-only, offline) — lets the agent find its own code and state.
|
|
23
32
|
if ARGV[0] == "self"
|
|
@@ -221,7 +230,7 @@ chi_update_hint = lambda do
|
|
|
221
230
|
end
|
|
222
231
|
|
|
223
232
|
if options[:web]
|
|
224
|
-
chi_update_hint.call
|
|
233
|
+
chi_update_hint.call unless web_restarted
|
|
225
234
|
require "samagotchi/web/server"
|
|
226
235
|
port = options[:web_port] || Samagotchi::Config.get("web.port")
|
|
227
236
|
markdown = options[:web_markdown]
|
|
@@ -233,7 +242,9 @@ if options[:web]
|
|
|
233
242
|
annotate_presets: Samagotchi::Config.get("web.annotate_presets"),
|
|
234
243
|
editor: Samagotchi::Config.get("web.editor"),
|
|
235
244
|
start_dir: options[:start_dir] || Samagotchi::Config.get("web.start_dir"),
|
|
236
|
-
start_dir_from: options[:start_dir] ? :flag : :config
|
|
245
|
+
start_dir_from: options[:start_dir] ? :flag : :config,
|
|
246
|
+
restarted: web_restarted,
|
|
247
|
+
restart_argv: Samagotchi::RunOptions.web_restart_argv(options))
|
|
237
248
|
end
|
|
238
249
|
|
|
239
250
|
# --resume and --attach take a unique id prefix too, like git.
|
data/docs/architecture.md
CHANGED
|
@@ -533,12 +533,41 @@ The snapshot names chi's version: a tab served by another one (its `<body data-v
|
|
|
533
533
|
reload in a toast, since an open tab keeps its old JS across a `chi web` upgrade. It also names
|
|
534
534
|
`installed`, the newest chi on this machine (`InstalledVersions`: the highest `samagotchi-<v>.gemspec`
|
|
535
535
|
in RubyGems' specification dirs, or a checkout's `version.rb` on disk; looked at again on each full
|
|
536
|
-
probe, every 10 s)
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
536
|
+
probe, every 10 s). A `chi` frame and the `GET /api/events` snapshot carry the full state
|
|
537
|
+
`{version, installed, available, update_blocked}`; `/api/info` and the session-command wake response
|
|
538
|
+
carry the same fields. `SessionHub#update_available` publishes the checker result under the hub's
|
|
539
|
+
Monitor, but fetching never holds it.
|
|
540
|
+
|
|
541
|
+
`Web::UpdateCheck` runs its own thread beside the hub. When `update.notify: on` (the default), its
|
|
542
|
+
first RubyGems check is about 30 seconds after chi web starts, then it checks at `update.check_hours`
|
|
543
|
+
intervals. A stable release is available only after `update.min_age_hours` and when newer than both
|
|
544
|
+
the running and newest installed chi. Its atomic, mode-0600
|
|
545
|
+
`$XDG_STATE_HOME/samagotchi/update_check.json` keeps the check cooldown and versions already
|
|
546
|
+
announced across restarts. `update.notify: off` starts no checker thread and makes no network
|
|
547
|
+
request. `update_blocked` names why the local gem cannot be updated in place (checkout, Bundler, or
|
|
548
|
+
an unwritable gem directory). The page tells the owner to run `chi update`; a newer `installed`
|
|
549
|
+
version than the server itself still means restart chi web (its workers already run the newest chi,
|
|
550
|
+
[Sessions](sessions.md)). `GET /api/info` also names `newest_workers`; next to `cwd` (the folder chi
|
|
551
|
+
web runs in) it names `start_dir`, the folder it starts in (`web.start_dir`, `--start-dir`;
|
|
552
|
+
`Web::StartDir`), which a second `chi web` given another one mentions.
|
|
553
|
+
|
|
554
|
+
The owner's update routes publish progress and results as `chi_update` events with an
|
|
555
|
+
`operation` (`install` or `workers`), `state`, and `rows`; install events also name the target
|
|
556
|
+
`version` and may include a progress line. The hub retains the latest event for a reconnecting
|
|
557
|
+
tab's snapshot. `POST /api/update` decides itself whether this install can take a gem
|
|
558
|
+
(`UpdateCheck.blocked_reason`: a checkout, Bundler, an unwritable gem dir → 409 `update_blocked`), so
|
|
559
|
+
it holds with `update.notify: off`; a child that prints no rows fails with its own last line, and a
|
|
560
|
+
done install refreshes the hub's installed version (`SessionHub#refresh_installed`) before its `done`.
|
|
561
|
+
Worker restarts target every worker older than the version installed now (refreshed the same way)
|
|
562
|
+
and publish per-session results. Only one of an install, a worker restart pass and a web restart runs
|
|
563
|
+
at a time (409 `update_running`). `POST /api/update/restart_web` (409 `restart_unavailable` without a
|
|
564
|
+
chi wrapper to exec) schedules WEBrick shutdown only after its response is sent; after the hub and
|
|
565
|
+
listener stop, `Server` execs the RubyGems chi wrapper with every flag chi web was started with, as
|
|
566
|
+
`RunOptions`' parser recorded them (`RunOptions.web_restart_argv`), but the one-shot `--open` and
|
|
567
|
+
`--new-token`. The re-entry (`SAMAGOTCHI_WEB_RESTARTED`, taken out of ENV first by
|
|
568
|
+
`Server.take_restart_env!`) skips the first-run update hint, adopts live worker-starter PIDs (workers
|
|
569
|
+
themselves are detached), and adopts an exact Tailscale Serve handler. A failed exec, or a restarted
|
|
570
|
+
chi web that doesn't start for any reason, turns off only that exact adopted handler.
|
|
542
571
|
|
|
543
572
|
The page's scope is in its URL, and the server picks the default: `?dir=<folder>` is that folder's
|
|
544
573
|
project (the list, the events stream and a new chat follow it), `?scope=all` every session, and a bare
|
|
@@ -775,4 +804,4 @@ A map of the parts the overview above doesn't name (paths under `lib/samagotchi/
|
|
|
775
804
|
| Stores | `reminder_store.rb` (`ReminderStore`), `recap_store.rb` (`RecapStore`) | A session's reminders, and its idle recap (`recap.json`). |
|
|
776
805
|
| Setup and update | `bootstrap_command.rb`, `bootstrap/`, `update_command.rb`, `gem_update.rb` | `chi bootstrap` (first config.yml, system bundle, core profile) and `chi update` (the gem, shipped bundles, the macOS helper). |
|
|
777
806
|
| Process groups | `process_group.rb` (`ProcessGroup`) | A command in a process group of its own, stopped as a group (TERM, grace, KILL): execute, tasks, context fetches, the mcp bundle's servers. |
|
|
778
|
-
| Desktop | `desktop.rb`, `desktop/macos.rb`, `desktop_command.rb`, `desktop/macos/*.swift` | `chi desktop`, the macOS "Send to chi" helper ([desktop.md](desktop.md)). The helper's Swift side: the panel (`Panel.swift`), the chi CLI runner (`ChiRunner.swift`), agent terminals behind `AgentTerminal` (`AgentTerminal.swift`, `Kitty.swift`, `Tmux.swift`; `ChiHelper --agents` is their CLI seam). |
|
|
807
|
+
| Desktop | `desktop.rb`, `desktop/macos.rb`, `desktop_command.rb`, `desktop/macos/*.swift` | `chi desktop`, the macOS "Send to chi" helper ([desktop.md](desktop.md)). The helper's Swift side: the panel (`Panel.swift`), the chi CLI runner (`ChiRunner.swift`), the session rows and the chi calls a send makes (`Sessions.swift`: `SendPlan`; `ChiHelper --sessions` and `--send-plan` are their CLI seams), agent terminals behind `AgentTerminal` (`AgentTerminal.swift`, `Kitty.swift`, `Tmux.swift`; `ChiHelper --agents` is their CLI seam). |
|
data/docs/cli.md
CHANGED
|
@@ -27,18 +27,18 @@
|
|
|
27
27
|
- `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`). What a parent may pick follows `guardrails.parent_answers`, or the session's own `parent_answers` when stricter (`soft`, the default: a deny, a plain answer, Continue and "Allow once"; `standard`: no allow; `hard`: no Continue either), never a wider scope or an allow on chi's own config; a refused pick exits 1 with what to answer instead (a convention, not a security boundary; see [Guardrails](guardrails.md#answers-from-a-parent-agent))
|
|
28
28
|
- `chi remote add NAME URL [--token-file F | --token-stdin]`, `chi remote ls`, `chi remote rm NAME` — the other machines whose chi web the session commands reach with `--remote NAME` (see [Remote sessions](#remote-sessions))
|
|
29
29
|
- `chi review FILE… [--dir DIR] [--model M]`, `chi review --from ID [FILE…]` — start a review session to invite participants to: for files of a project, or as a review of a working session that starts from its recap, not its transcript (see [Review sessions](#review-sessions))
|
|
30
|
-
- `chi share ID --name NAME [--identity EMAIL] [--agent] [--expires 3d] [--cap 2]`, `chi share ls [ID]`, `chi share cap ID P DOLLARS`, `chi share revoke ID [P] | --all` — invite a participant to
|
|
30
|
+
- `chi share ID --name NAME [--identity EMAIL] [--agent] [--expires 3d] [--cap 2]`, `chi share ls [ID]`, `chi share cap ID P DOLLARS`, `chi share revoke ID [P] | --all` — invite a participant to a session through chi web on the LAN (read-only review turns in a review session, pair turns that act under your guardrails in a working session) (a link of their own, 7 days, a $2 spending cap), list them, change one's cap, revoke them (see [Participants](#participants))
|
|
31
31
|
- `chi --remote NAME send|answer|note|context|sessions|share|review …` (or `CHI_REMOTE=NAME`) — those commands on that machine's sessions, through its chi web (see [Remote sessions](#remote-sessions))
|
|
32
32
|
- `chi --remote NAME --attach ID` (`--resume ID`, `--dir DIR [-p TEXT]`) — the attached TUI on a session of that machine (see [Remote sessions](#remote-sessions))
|
|
33
33
|
- `chi memory ls [--scope project|system] [--format text|json]` — the memories of both scopes (this folder's project first), one row each: scope, name, size, date, `from <bundle>`, the index description and its model overlays; `chi memory rm NAME [--scope project|system]` removes one as `memory_write` with `remove: true` does: its file and model overlays go to the bundle trash (the line names the dir; `chi bundle trash` lists and empties it) and its index line is dropped. NAME in both scopes without `--scope` exits 2 naming both; a missing NAME or a bundle's memory (`chi bundle uninstall` removes it) exits 1 (see [Memory: Listing and removing from the shell](memory.md#listing-and-removing-from-the-shell))
|
|
34
34
|
- `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))
|
|
35
35
|
- `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))
|
|
36
36
|
- `chi self` — print version, source dir (checkout or installed gem), config/memory/session paths, model/host and bundles. Run by a session's `execute`, the `model` row (and host, loop, profile, thinking, served model) is **that session's** model, labelled `(this session <id8>; default …)`; elsewhere it is the default, `(default)`. `model key` is the memory overlay key (`<name>.<key>.md`), and `model notes` the model notes a prompt of that model loads (`model_notes_deepseek (system, 612 chars)`, `none`; inside a session its `--mute` list is left out). `chi self --model` prints only the model a new session starts on, an alias resolved to its target (the desktop helper's hint)
|
|
37
|
-
- `chi update [--dry-run] [--no-gem] [--no-bundles] [--no-desktop]` — update an installed chi: the gem, the system bundle, the shipped bundles you installed and the desktop helper, in one table (see [Updating](#updating))
|
|
37
|
+
- `chi update [--dry-run] [--to VERSION] [--json] [--no-gem] [--no-bundles] [--no-desktop]` — update an installed chi: the gem, the system bundle, the shipped bundles you installed and the desktop helper, in one table (see [Updating](#updating))
|
|
38
38
|
- `chi bundle install|upgrade|uninstall|status|diff|list|build|trash|new|test|check` — manage memory bundles (see [Bundle hooks](hooks.md#bundle-hooks-unified-workflow-bundle)); `list` shows the installed ones and the ones shipped with chi, which `install <name>` installs; `core` and `dev` are profiles that install a set of them (see [Bundle profiles](memory.md#bundle-profiles-core-and-dev), [Guardrails](guardrails.md), [Plugins](plugins.md#the-btw-bundle), [the mcp bundle](plugins.md#the-mcp-bundle) [the loop-guard bundle](plugins.md#the-loop-guard-bundle), [the check-in bundle](plugins.md#the-check-in-bundle) and [the skills bundle](plugins.md#the-skills-bundle)). `trash` lists and empties the bundle trash (moved files from uninstalls/upgrades). A usage error (an unknown subcommand or flag, a missing argument, a bad `build --scope`) exits 2, as for the other subcommands. Plain `chi` and `chi web` exit 1 on an unknown flag, a flag missing its value or a stray argument. `new`, `test` and `check` are for writing one: see [Writing a bundle](#writing-a-bundle-new-test-check)
|
|
39
39
|
|
|
40
40
|
- `chi guardrails replay [--dry-run] [--threshold T] [--model M]` — seed System 1's record from the approvals this machine's sessions already answered: each ask is judged by the configured model and written to `guardrails/judgments.jsonl` with the answer the user gave, then a per-rule table prints (asks, allowed, denied, would-be auto-allows, false allows, asks with no judgment). A parent agent's answer is not the user's: only its deny is replayed, and only where the parent could have allowed. `--dry-run` judges without writing, `--threshold` overrides `guardrails.system_one.threshold` for the table, `--model` the configured model for this run. Exit 2 when `guardrails.system_one.mode` is off or no `host` is set; a usage error exits 2 (see [System 1](guardrails.md#system-1-experimental))
|
|
41
|
-
- `chi toolkit report [--days N]` — grade toolkit
|
|
41
|
+
- `chi toolkit report [--days N]` — grade toolkit suggestions: the kit on mode would have run (the suggested kit when it would have applied it, else the default; an on-mode line's `did` says what it ran) against the tools the session called from that turn on (only that turn when the toolkit was chosen since), `call_tool` targets included, for both the chat and the raw-prompt loop. Each is *covers*, *short of a tool* (naming the tools), *wider than needed* (a narrower kit offered in the same ask would also have covered) or *ungradable* (calls it can't read back); then a per-kit tally with what on mode would have done. Includes archived sessions and caps the report at 14 days and 500 sessions. Reads `toolkit_suggestions.jsonl` (each ask logs every offered kit's rules, expanded against the called tools here); it never runs the model or changes a session. Local only; `--days` must be a positive whole number.
|
|
42
42
|
|
|
43
43
|
- `chi bundle try PATH -p TEXT [options]` — install a bundle in a temporary, filtered sandbox, run one real model turn,
|
|
44
44
|
and print its trace (see [Trying a bundle](#trying-a-bundle)).
|
|
@@ -233,6 +233,30 @@ Also shipped, not installed: dev (mcp, skills, source-links) (chi bundle install
|
|
|
233
233
|
done
|
|
234
234
|
```
|
|
235
235
|
|
|
236
|
+
- `--to VERSION` installs exactly that gem version instead of checking for the
|
|
237
|
+
newest release. It must be newer than the running chi; an equal or older
|
|
238
|
+
version is refused.
|
|
239
|
+
- `--json` writes the component rows as one JSON array on stdout. Each row has
|
|
240
|
+
`component`, `from`, `to`, `status`, `failed` and `kept` fields; `kept` is an
|
|
241
|
+
array of edited filenames. Install progress is written to stderr.
|
|
242
|
+
|
|
243
|
+
- **chi web update notices.** By default, chi web checks RubyGems about 30
|
|
244
|
+
seconds after it starts and every 24 hours after that. Once a stable release
|
|
245
|
+
is at least 24 hours old and newer than both the running chi and the newest
|
|
246
|
+
installed one, the owner's page shows its version and a link to that
|
|
247
|
+
RubyGems release. When the install is unblocked, **Update** installs that
|
|
248
|
+
exact release and opens a progress sheet with component results and any
|
|
249
|
+
edited files kept. A blocked install instead shows the reason and a command
|
|
250
|
+
to copy. After installation, the notice and the sheet's footer offer
|
|
251
|
+
**Restart workers** and **Restart chi web**; the sheet reports worker results
|
|
252
|
+
(a busy worker's refusal says what holds it) and waits for chi web to
|
|
253
|
+
reconnect, and keeps the other restart in reach. The notice is once per version per browser: a reload or a chi
|
|
254
|
+
web restart doesn't repeat it. `update.notify: off` disables checking;
|
|
255
|
+
`update.check_hours` and
|
|
256
|
+
`update.min_age_hours` tune the interval and release age (see
|
|
257
|
+
[configuration](configuration.md#all-settings)). If the gem can't be updated
|
|
258
|
+
in place, the notice gives the blocker and its command instead.
|
|
259
|
+
|
|
236
260
|
- **The gem.** It asks rubygems.org for the newest samagotchi (5 s timeout)
|
|
237
261
|
and, when that's newer, runs `gem install samagotchi` with the gem command
|
|
238
262
|
of the Ruby chi runs on (the real one, not a mise/rbenv/asdf shim; a chi
|
|
@@ -1314,6 +1338,8 @@ CHI_REMOTE=studio chi context push claude-outbox ab12 < answer.json
|
|
|
1314
1338
|
studio (http://192.168.1.29:4567): connection refused`, exit 1; with
|
|
1315
1339
|
`--format json` an `error` object).
|
|
1316
1340
|
- Plain http, the same as the phone's: use it on your home network.
|
|
1341
|
+
- The macOS desktop helper's panel lists and sends to a remote's sessions too: see
|
|
1342
|
+
[Desktop helper: Remote sessions](desktop.md#remote-sessions).
|
|
1317
1343
|
|
|
1318
1344
|
A session there in this terminal (the attached TUI, through that machine's
|
|
1319
1345
|
chi web; its worker stays there):
|
|
@@ -1423,6 +1449,25 @@ chi share revoke --all # every link to every session
|
|
|
1423
1449
|
share cap` raises it. `/stats` shows each participant's spend. Their turns go only to a worker with the review
|
|
1424
1450
|
boundary: restart an older one. While your turn waits at the step limit (Continue or Stop), their turns wait too
|
|
1425
1451
|
(still queued, still counted): they run, in order, once you continue, stop or send a turn of your own.
|
|
1452
|
+
- **Pair turns: sharing a working session.** The read-only review turn is a review session's (`chi review`). Invite
|
|
1453
|
+
someone to a working session instead and their turns **act** in it with its tools, writes included, under your
|
|
1454
|
+
guardrails: what your rules allow runs, what they ask about comes to you as "Sam's turn asks: approve tool call?"
|
|
1455
|
+
with **Allow once** only (nothing session-wide or repo-wide is approved from their calls), and System 1 never
|
|
1456
|
+
answers for you. A write outside the project always asks you, whatever you approved before. It can't read or
|
|
1457
|
+
write secrets or chi's own files (keys, shell startup files, histories, credential files; by a file tool, a
|
|
1458
|
+
command, a glob or a variable), run chi, leave a command running in the background, read the environment or the
|
|
1459
|
+
keychain, sweep your home with find/tar/cp/grep, delegate, set or cancel reminders, send notes, start or stop
|
|
1460
|
+
background tasks, load tools, forget outputs or write memories, and it asks its questions in its reply, not on a
|
|
1461
|
+
card. Its commands run without your secret-named environment variables (`KEY`, `TOKEN`, `SECRET`, `PASS`,
|
|
1462
|
+
`CREDENTIAL`, `AUTH` in the name) and are killed at the execute limit rather than left running; a command that
|
|
1463
|
+
writes outside the project asks you, as a write does (the tmp dirs aside). A pair turn stops mid-turn once their
|
|
1464
|
+
cap is passed or their link ends. `chi share` and the panel's Invite
|
|
1465
|
+
warn you with what their turns may do and how many tool outputs the history holds, and only a link issued with
|
|
1466
|
+
that warning acts: links from before, or issued while the session's worker predated pair turns (the warning says
|
|
1467
|
+
so), stay read-only until you restart the worker and invite them again. Their messages carry a `pair` badge and no
|
|
1468
|
+
Apply (they acted already). This is an accident guard for a trusted team, not containment: a pair turn can still
|
|
1469
|
+
send what it reads out through `web_fetch` or your MCP tools. A link without `--identity` gets the same access:
|
|
1470
|
+
share it only with people you'd let type in your session.
|
|
1426
1471
|
- **The participant page.** Their link opens a page of its own (never yours): the session's title and status, who
|
|
1427
1472
|
else takes part, and the conversation as a participant sees it: every prompt and reply with who wrote it, each
|
|
1428
1473
|
step's tools as one line (`read docs/plan.md`: the tool and a project file's path, never arguments or output), and
|
data/docs/configuration.md
CHANGED
|
@@ -163,7 +163,7 @@ or `chi web` restart.
|
|
|
163
163
|
| `append_system_prompt` | Worker start for the file list | The config list is not saved in the session file. File text is read when the prompt is built; `/model` rebuilds it, while an already-built prompt is cached. CLI `--append-system-prompt` files are saved in the session. |
|
|
164
164
|
| `toolkit.default`, `toolkit.child` | Session creation | The chosen toolkit is saved; changing the defaults does not move existing sessions. |
|
|
165
165
|
| `toolkit.kits`, `execute.description`, `llm_context.policy` | Worker start | These are held by the engine's toolkit or tool registry. |
|
|
166
|
-
| `toolkit.suggest.mode`, `toolkit.suggest.threshold`, `toolkit.suggest.defer_turns`, `toolkit.suggest.defer_tokens` | Worker start | Suggestion settings are config-only and are resolved when the worker runs its audit. |
|
|
166
|
+
| `toolkit.suggest.mode`, `toolkit.suggest.threshold`, `toolkit.suggest.defer_turns`, `toolkit.suggest.defer_tokens` | Worker start | Suggestion settings are config-only and are resolved when the worker runs its audit or on-mode ask. |
|
|
167
167
|
| `skip_agents_md` | Prompt rebuild | A running engine caches its built prompt; a new worker or `/model` rebuild uses the changed value. |
|
|
168
168
|
| `context.*` except `context.every_seconds`, `llm_context.*` except `llm_context.policy`, `steer.cut_after`, `memory.index_warn_tokens`, `status.line`, `web.participants.*` | Next matching turn, display, poll or write | Readers resolve these settings when they perform that operation. Session-specific LLM context and thinking values remain saved in the session. A participant's grant keeps the expiry and cap it was issued with; `web.participants.*` is what the next invite gets, and the cap and queue of a grant from before caps. |
|
|
169
169
|
| `context.every_seconds` | Next context poll | The poller checks it for each source that has no explicit interval. |
|
|
@@ -177,7 +177,8 @@ or `chi web` restart.
|
|
|
177
177
|
| `log.*` | Process start | Each process holds its configured log sink and level after first use; restart that process. |
|
|
178
178
|
| `history.file` | Next history access | The TUI and web composer resolve its path when they read or append history. |
|
|
179
179
|
| `broadcast.*` | Next `chi broadcast` | Each broadcast invocation reads its targeting and triage settings. |
|
|
180
|
-
| `update
|
|
180
|
+
| `update.gem`, `update.bundles`, `update.desktop` | Next `chi update` | The command reads which components to update when it runs. |
|
|
181
|
+
| `update.notify`, `update.check_hours`, `update.min_age_hours` | `chi web` start | The update checker starts with chi web; restart it to apply changes. |
|
|
181
182
|
| `kitty.*`, `tmux.*` | `chi desktop upgrade` or desktop update | These settings are copied into the desktop helper's launch file. |
|
|
182
183
|
| `model.profile`, `reasoning_history` | Not from config.yml | These are CLI/environment settings; `models:` and `hosts:` provide their model- and host-specific forms. |
|
|
183
184
|
|
|
@@ -1432,13 +1433,34 @@ toolkit:
|
|
|
1432
1433
|
prompt, without changing its toolkit or delaying the turn. It offers the configured default as the fallback and
|
|
1433
1434
|
`undecided` for greetings or check-ins. Only a default-origin session with at least two described candidates is
|
|
1434
1435
|
asked; chosen or inherited toolkits, delegate children, review sessions and participant turns are skipped. After
|
|
1435
|
-
`undecided`, later owner prompts may be asked again within both defer limits.
|
|
1436
|
-
|
|
1437
|
-
|
|
1436
|
+
`undecided`, later owner prompts may be asked again within both defer limits. The default is `off`. System 1's
|
|
1437
|
+
host, model and timeout come from `guardrails.system_one.*`; the suggestion threshold and defer limits are its own
|
|
1438
|
+
settings below. Suggestions are
|
|
1438
1439
|
kept in `toolkit_suggestions.jsonl`; `chi toolkit report` grades them against the session's tool calls. One ask
|
|
1439
1440
|
runs at a time (a turn that comes while one still runs goes unasked, with an info line), and a kit that doesn't
|
|
1440
1441
|
resolve (a missing base, a cycle) is left out of the candidates. A worker that exits waits about a second for a
|
|
1441
1442
|
running ask, so a one-shot `-p --non-interactive` run may end before its suggestion is logged.
|
|
1443
|
+
- **On mode.** `toolkit.suggest.mode: on` asks the same question on the same eligible turns, but the turn waits for
|
|
1444
|
+
the answer before it declares any tool, for at most `guardrails.system_one.timeout_ms` from the ask's start (it
|
|
1445
|
+
starts right after the turn does, so plugin and MCP start-up hide most of it). A described kit picked with
|
|
1446
|
+
confidence at or above `toolkit.suggest.threshold` becomes the session's toolkit, saved with `toolkit_origin:
|
|
1447
|
+
suggested`, and the turn's first request declares it; the notice reads
|
|
1448
|
+
`toolkit: web (suggested, p=0.71) · /toolkit to change`. Anything else (the default, a low-confidence or unknown
|
|
1449
|
+
pick, `undecided`, no answer, a failing System 1, which warns once as `[samagotchi:toolkit]`) keeps the default
|
|
1450
|
+
kit. A Stop during the wait (or before it, even with the answer in) ends the turn before its first request, and
|
|
1451
|
+
nothing switches. After an `undecided`, a later prompt within the defer limits may switch the session; that notice
|
|
1452
|
+
says `from this turn`, and that request re-reads the whole prompt (system prompt, tools and conversation, which
|
|
1453
|
+
the defer limits keep small) without `/toolkit`'s cache confirmation. `/toolkit` or `ctx.use_toolkit` marks the
|
|
1454
|
+
toolkit chosen, and nothing is suggested after that; a `/toolkit` sent during a turn runs after it and replaces
|
|
1455
|
+
that turn's suggestion. When System 1's host is the model's own local server (the same `hosts:` name or server
|
|
1456
|
+
root, as audit checks), on mode asks only on a session's first turn, never on the deferred path, and sends no
|
|
1457
|
+
warm-up (one info line). Otherwise an idle worker for an eligible session sends one warm-up ask with the real
|
|
1458
|
+
question and a stand-in prompt so the server has its prefix cached; its answer is dropped and never logged, and
|
|
1459
|
+
the first real ask stops a warm-up still running. Each answered ask's line in `toolkit_suggestions.jsonl` adds
|
|
1460
|
+
`did`: `suggested`, `default`, `defer`, `chosen` (the toolkit was no longer the default), `unavailable` (the
|
|
1461
|
+
picked kit was gone), `timeout` or `canceled` (written when the late answer arrives; a Stop after a timeout
|
|
1462
|
+
doesn't cut it), or `failed` (the turn failed or the switch couldn't be saved); `chi toolkit report` grades
|
|
1463
|
+
on-mode lines by what they did.
|
|
1442
1464
|
- **Tool-index groups.** `groups:` selects source groups by name or glob (`chrome`, `db*`; any case, `{a,b}` braces,
|
|
1443
1465
|
as the guardrails rules' `models:`); for the MCP bundle each group is a server. Anything but a name or a list (a
|
|
1444
1466
|
map such as `{chrome: true}`) refuses every new session on that kit or on a kit built on it, on every surface (chi,
|
|
@@ -2003,7 +2025,7 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
|
|
|
2003
2025
|
| `context.peer_turns` | `3` | yes | Wake turns in a row a peer agent's answers may start (a peer bundle's outbox source, such as `codex-outbox`), with no message typed in the web or a TUI in between (`chi send` doesn't count); within `session.max_wakes`. Then answers wait as notes, and the UIs hear it once. An answer whose summary ends its verdicts with `VERDICT OK` wakes once to close the round, counted toward `session.max_wakes` only. `0`: a peer source wakes like any other. See [Waking](context.md#waking). |
|
|
2004
2026
|
| `toolkit.default` | `full` | | The toolkit a new session is stored on: the tools its prompts declare (`full`, `lean`, `child`, one of `toolkit.kits` or a bundle's). `chi --toolkit` picks one per session. See "Toolkits". |
|
|
2005
2027
|
| `toolkit.child` | `full` | | A delegate child's toolkit, unless its `delegate` call names one (`toolkit:`). See "Toolkits". |
|
|
2006
|
-
| `toolkit.suggest.mode` | `off` | | `audit` asks System 1 for a toolkit suggestion without changing the session; `on`
|
|
2028
|
+
| `toolkit.suggest.mode` | `off` | | `audit` asks System 1 for a toolkit suggestion without changing the session; `on` waits for it (up to `guardrails.system_one.timeout_ms`) on an eligible turn and starts the session on a confidently suggested kit (Toolkits, On mode). The System 1 host, model and timeout come from `guardrails.system_one.*`. config.yml only. |
|
|
2007
2029
|
| `toolkit.suggest.threshold` | `0.5` | | Minimum confidence for a non-default toolkit suggestion. config.yml only. |
|
|
2008
2030
|
| `toolkit.suggest.defer_turns` | `3` | | Maximum owner-prompt number for another ask after `undecided` (the owner's own prompts: not steers, notes, reminders or `!` command output). config.yml only. |
|
|
2009
2031
|
| `toolkit.suggest.defer_tokens` | `5000` | | Maximum estimated conversation size for another ask after `undecided` (characters / 4). config.yml only. |
|
|
@@ -2028,6 +2050,9 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
|
|
|
2028
2050
|
| `update.gem` | `true` | | `false`: `chi update` never installs a newer gem (`--no-gem` for one run). See [CLI: Updating](cli.md#updating). |
|
|
2029
2051
|
| `update.bundles` | `true` | | `false`: `chi update` leaves the shipped bundles to `chi bundle upgrade` (`--no-bundles`). |
|
|
2030
2052
|
| `update.desktop` | `true` | | `false`: `chi update` leaves the desktop helper alone (`--no-desktop`). |
|
|
2053
|
+
| `update.notify` | `on` | | `on`: chi web checks RubyGems once a day and shows aged stable releases newer than both this chi and the newest installed chi; `off`: no thread or network check. |
|
|
2054
|
+
| `update.check_hours` | `24` | | Hours between RubyGems checks (at least 1). The first check starts about 30 seconds after chi web starts. |
|
|
2055
|
+
| `update.min_age_hours` | `24` | | A release must be this old before chi web offers it; `0` disables the age delay. |
|
|
2031
2056
|
| `kitty.listen_on` | none | | The desktop helper's kitty socket, copied from `kitty.conf`; unset = no kitty targets. See "Desktop helper: agents in kitty". |
|
|
2032
2057
|
| `kitty.binary` | `/Applications/kitty.app/Contents/MacOS/kitty` | | The kitty the helper runs `kitty @` with. |
|
|
2033
2058
|
| `kitty.agents` | `claude\|codex\|gemini\|aider\|opencode\|cursor-agent\|amp\|goose` | | Foreground programs whose kitty windows the helper lists, `\|`-separated (a YAML list works too); `*` lists every window. |
|
data/docs/desktop.md
CHANGED
|
@@ -41,7 +41,7 @@ alias as the model it names).
|
|
|
41
41
|
|
|
42
42
|
### Everyone it concerns
|
|
43
43
|
|
|
44
|
-
The second row, **Everyone it concerns** (⌘B), shares a note with every session it may concern instead of the ones
|
|
44
|
+
The second row, **Everyone it concerns (this Mac)** (⌘B), shares a note with every session it may concern instead of the ones
|
|
45
45
|
you pick: ⌘⏎ (the **Broadcast** button) runs [`chi broadcast`](broadcast.md) with the line, then the text, on stdin.
|
|
46
46
|
chi picks the sessions (a shared ticket, PR or link, else a small triage model), so the preselected session doesn't
|
|
47
47
|
count: the row is ticked alone, like the new row. Write it the way you'd tell a colleague: a first line such as
|
|
@@ -51,6 +51,8 @@ count: the row is ticked alone, like the new row. Write it the way you'd tell a
|
|
|
51
51
|
skipped 2 · 1 unchecked: triage deadline`), and **stays open**: Esc closes. Which sessions got it and why:
|
|
52
52
|
`chi broadcast log` in a terminal.
|
|
53
53
|
- A broadcast is a text note: ⏎ and images beep. The source field and Send are hidden (its source is `broadcast`).
|
|
54
|
+
- It reaches this Mac's sessions only (`chi broadcast` runs on this machine only), never a
|
|
55
|
+
[remote's](#remote-sessions).
|
|
54
56
|
- It may take `broadcast.triage_deadline` (20 s) and 10 s more before the helper stops it ("the note may be partly
|
|
55
57
|
delivered"). The helper reads that from its launch file: after changing the deadline, run `chi update` (or
|
|
56
58
|
`chi desktop upgrade`).
|
|
@@ -106,6 +108,46 @@ thumbnails sit under the message line, 48 px high, the file name as a tooltip; h
|
|
|
106
108
|
the panel closes; files from Finder are sent as they are, never touched. A send with images may take up to 30 s
|
|
107
109
|
(converting, a worker starting) before the panel gives up.
|
|
108
110
|
|
|
111
|
+
## Remote sessions
|
|
112
|
+
|
|
113
|
+
The panel also lists the sessions of the other machines `chi remote add` keeps (see
|
|
114
|
+
[Remote sessions](cli.md#remote-sessions)), say the Studio's from a laptop: select text or take a screenshot here,
|
|
115
|
+
and it lands in a session running there.
|
|
116
|
+
|
|
117
|
+
- **Which:** every remote in `chi remote ls` that has a token, asked at each open (a remote you don't want in the
|
|
118
|
+
panel: `chi remote rm` it). No `chi update` or `chi desktop upgrade` after adding one.
|
|
119
|
+
- **Where:** after this Mac's rows, one section per remote under an **on studio** divider: its live sessions, then up
|
|
120
|
+
to 3 recent ones, as for this Mac. Each row carries a grey **studio** tag and its whole folder path (that machine's
|
|
121
|
+
home may differ from this one's). ⌘1…⌘9 number this Mac's rows first, then the remotes' in order; past nine, a row
|
|
122
|
+
is clicked.
|
|
123
|
+
- **Not twice:** a session shows once, where it is listed first (this Mac, then the remotes in `chi remote ls` order),
|
|
124
|
+
so two remotes for the same Studio (its LAN address and its ts.net name), or a remote that is this Mac, don't
|
|
125
|
+
list or send to it twice.
|
|
126
|
+
- **Wrong machine:** both machines likely have the same paths. While a remote's row is ticked the message line's hint
|
|
127
|
+
ends in **→ studio**, and **→ studio** shows beside Send (it stays there while you type). The last choice is
|
|
128
|
+
preselected while it is still live, a remote's row too; the "only one live session" rule picks this Mac's only,
|
|
129
|
+
never a remote's. A remote that answers after this Mac's list never changes what is ticked: it is preselected only
|
|
130
|
+
when nothing is (so a remembered remote row is ticked when the remote answers first, or when this Mac has live
|
|
131
|
+
sessions but none was remembered).
|
|
132
|
+
- **Keys:** ⏎ and ⌘⏎ work as for this Mac's sessions (`chi --remote studio send …`, `chi --remote studio note …`;
|
|
133
|
+
a note to a stopped session there waits for its next start). Images go along (`--image`: chi uploads them). With
|
|
134
|
+
rows of several machines ticked, the helper runs one call per machine at once and shows one line: `sent · studio:
|
|
135
|
+
sent`. ⌥⏎ (paste) beeps on a remote's row as on this Mac's.
|
|
136
|
+
- **Down or refused:** this Mac's rows show as soon as they are in; each remote's fill in as it answers. A remote's
|
|
137
|
+
two lists run at once and get 4 s together: one that doesn't answer shows **studio: no answer within 4 s** under
|
|
138
|
+
its divider, one that chi gives up on by itself (connection refused, the token refused) the first line of chi's
|
|
139
|
+
error. The previous open's rows for it are never reused: a stale row could send to a stopped worker.
|
|
140
|
+
- **New session there:** a remote with a session has its own new row under its divider, **New session on studio in
|
|
141
|
+
proj**: the folder is that remote's most recently updated session's (live first, then recent), whole and not
|
|
142
|
+
checked on this Mac. A remote with no session has no new row (never a guess at its home). It is ticked alone like
|
|
143
|
+
this Mac's new row (ticking it clears the sessions, and the reverse), is never preselected and has no ⌘ number (⌘0
|
|
144
|
+
stays this Mac's). ⏎ runs `chi --remote studio send --new --dir <that folder>` with the message and images, and
|
|
145
|
+
shows `studio: started 1a2b3c4d…` for 3 s. It starts on the remote's default model, never `--model`: its hint
|
|
146
|
+
says **remote default**, and ⌘M or a click on it beeps with "Model choice is for this Mac's sessions" (`chi models`
|
|
147
|
+
answers for this Mac's hosts).
|
|
148
|
+
- **What stays on this Mac:** the model chooser, the **Everyone it concerns** row (`chi broadcast` is local only)
|
|
149
|
+
and the agents in kitty and tmux.
|
|
150
|
+
|
|
109
151
|
## Agents in kitty and tmux
|
|
110
152
|
|
|
111
153
|
The panel can also send to agent CLIs (claude, codex, gemini, aider, …) running in
|
|
@@ -276,8 +318,14 @@ The helper uses only these commands, so it could ship on its own later:
|
|
|
276
318
|
(`delivered N · skipped M…`); exit 0 when it ran, 1 when refused or a delivery failed.
|
|
277
319
|
- `chi self --model` → the model a new session starts on (the panel's hint).
|
|
278
320
|
- `chi models --format json` → the models the hosts offer (the new-session model picker).
|
|
279
|
-
|
|
280
|
-
|
|
321
|
+
- `chi remote ls --format json` → `[{name, url, token}]` (never the token itself): the remotes whose sessions the
|
|
322
|
+
panel lists. A name goes on only when it has a token and passes chi's own rule (letters, digits, `_`, `-`, not
|
|
323
|
+
starting with `-`). An older chi without `chi remote` lists none.
|
|
324
|
+
- `chi --remote NAME sessions list …`, `chi --remote NAME send …` and `chi --remote NAME note …`: the same calls on a
|
|
325
|
+
remote's sessions. `CHI_REMOTE` is taken out of the helper's environment, so this Mac's calls never go remote.
|
|
326
|
+
|
|
327
|
+
Each call is stopped after 10 s, a remote's two lists after 4 s, a send of text to a remote after 15 s (waking a
|
|
328
|
+
stopped worker there), a send with images after 30 s, a broadcast after the launch file's
|
|
281
329
|
`broadcast_timeout` (`broadcast.triage_deadline` + 10 s; 30 s when it has none). A stopped `chi note` or `chi
|
|
282
330
|
broadcast` says the note may be partly delivered.
|
|
283
331
|
|
data/docs/guardrails.md
CHANGED
|
@@ -315,8 +315,11 @@ ordinary sessions don't get it. Like the rules it reads command text: a
|
|
|
315
315
|
|
|
316
316
|
## A participant's review boundary
|
|
317
317
|
|
|
318
|
-
Built in, for a [participant's](internals/participants.md#turns) turns
|
|
319
|
-
turn whose author is `p:<id
|
|
318
|
+
Built in, for a [participant's](internals/participants.md#turns) turns in a
|
|
319
|
+
**review session** only (a turn whose author is `p:<id>`, in a session with a
|
|
320
|
+
`review:` key: `ParticipantMode.of` is `:review`; a working session's
|
|
321
|
+
participant turns get the [pair boundary](#a-participants-pair-boundary)
|
|
322
|
+
instead), in every mode (also with
|
|
320
323
|
`guardrails.enabled: false`) and without the guardrails bundle:
|
|
321
324
|
`Guardrails::ReviewBoundary`, rule `review-boundary`, source `core`. It comes
|
|
322
325
|
right after the load-failure check (rules that don't load deny every call) and before the hooks' asks, the
|
|
@@ -359,6 +362,96 @@ config can't widen the boundary. Hooks still run on a participant's turn, with
|
|
|
359
362
|
the owner's power ([hooks](hooks.md#participants-turns)); a hook that rewrites a
|
|
360
363
|
call is checked on the call it leaves.
|
|
361
364
|
|
|
365
|
+
## A participant's pair boundary
|
|
366
|
+
|
|
367
|
+
A participant's turn in a **working session** acts with the session's tools
|
|
368
|
+
under the owner's guardrails, as the owner's own turns do: the rules, the
|
|
369
|
+
protected paths and every approval the owner already granted apply, and an ask
|
|
370
|
+
goes to the owner. It is a pair turn only when all three hold
|
|
371
|
+
(`ParticipantMode.for_turn`, decided once at the turn's start:
|
|
372
|
+
`GuardrailWiring#turn_mode`): the session has no `review:` key, its worker
|
|
373
|
+
names the `pair_turns` feature, and the participant's grant was issued with
|
|
374
|
+
`mode: pair`, which chi share and the web's Invite stamp only when the warning
|
|
375
|
+
they showed said their turns act. A grant from before, or one issued while an
|
|
376
|
+
older worker held the session, keeps review turns until it is invited again.
|
|
377
|
+
|
|
378
|
+
`Guardrails::PairBoundary` (rule `pair-boundary`, source `core`) sits in the
|
|
379
|
+
review boundary's seat in `GuardrailWiring#checks`, in every mode (also with
|
|
380
|
+
`guardrails.enabled: false`), and **denies, never asks**, each with its own
|
|
381
|
+
message:
|
|
382
|
+
|
|
383
|
+
- secrets, read or written: the review boundary's denylist (chi's state and
|
|
384
|
+
config dirs, so `grants.json`, the web token and every `participants.json`;
|
|
385
|
+
`~/.ssh`, `~/.gnupg`, `~/.aws`, `~/.config/gh`, `~/.config/fish`,
|
|
386
|
+
`~/.config/git`, `~/Library/LaunchAgents`, `~/.local/bin`; the home's shell
|
|
387
|
+
startup files, histories, `.gitconfig`, `.netrc`, `.npmrc`, `.pypirc`, `.claude.json`;
|
|
388
|
+
the project's secret files, any config under `.git`) on every file tool's
|
|
389
|
+
path (`read`, `write`, `edit`, a plugin's `targets:` paths) and on the paths
|
|
390
|
+
a command names (`execute`, and any call whose targets carry a command),
|
|
391
|
+
each word resolved as `touches: chi_dirs` resolves it; a glob that matches a
|
|
392
|
+
secret or whose fixed part leads into a denied folder (`~/.ss?/id_*`,
|
|
393
|
+
`~/.config/*/config.yml`, `.en?`), a `$var` word whose fixed part does
|
|
394
|
+
(`~/.ss${d}h`), and a text match for the rest;
|
|
395
|
+
- sweeping a folder that holds secrets: `find`, `tar`, `cp`, `rsync`, `scp`,
|
|
396
|
+
`zip`, `ditto`, `grep`/`rg`/`ag` and the like given home or any folder above
|
|
397
|
+
a denied one;
|
|
398
|
+
- the environment and the keychain: `env` or `printenv` alone, `set`,
|
|
399
|
+
`export`, `declare` or `typeset` that list, `launchctl getenv`, `ps e`,
|
|
400
|
+
`security`, `/proc/*/environ`. On top of it, a pair turn's `execute` runs
|
|
401
|
+
without every variable whose name matches `KEY|TOKEN|SECRET|PASS|CREDENTIAL|AUTH`
|
|
402
|
+
(`PairBoundary::SECRET_ENV_NAME`, case-insensitive), and a command still
|
|
403
|
+
running at the limit is killed, never handed over as a background task;
|
|
404
|
+
- git settings that make git run a command (`core.hooksPath`,
|
|
405
|
+
`core.fsmonitor`, `core.sshCommand`, `core.pager`, `core.editor`, a `!`
|
|
406
|
+
alias) by `git config` or `git -c`, and `git config --global`/`--system`;
|
|
407
|
+
- running chi, by any wrapper (`bin/chi`, `bundle exec chi`, `ruby … bin/chi`,
|
|
408
|
+
`env … chi`, `watch`, `npx`, `sh -c 'chi …'`, `$(chi …)`), in an assignment's
|
|
409
|
+
value or an alias, a function, after `-exec`/`-c`/`-e`, but `chi --version` and
|
|
410
|
+
`chi --help`: its commands act in the owner's name (shares, caps, sends,
|
|
411
|
+
answers, sessions);
|
|
412
|
+
- leaving something running: `&` at a command's end, `nohup`, `setsid`,
|
|
413
|
+
`disown`, `screen -d…`, `tmux new -d`;
|
|
414
|
+
- owner-voiced tools, which act in the owner's name beyond the turn:
|
|
415
|
+
`delegate` and any `delegate_*` tool, `register_reminder`,
|
|
416
|
+
`cancel_reminder`, `send_note`, `forget_outputs`, `task_create`,
|
|
417
|
+
`task_stop`, `find_tools` (it loads tools into the session), and
|
|
418
|
+
`memory_write` (a memory reaches the owner's later sessions);
|
|
419
|
+
- `ask_user_question`, with the advice to ask in the reply: nobody on the
|
|
420
|
+
participant's side answers a card.
|
|
421
|
+
|
|
422
|
+
A script a command hands on is read too (`Guardrails::PairCommand`): one a
|
|
423
|
+
shell runs (`sh -c`, `eval`, a heredoc or here-string) as a command of its
|
|
424
|
+
own, one another interpreter runs (`ruby -e`, `python - <<EOF`) by its text
|
|
425
|
+
(chi, secrets, the environment, detaching). A heredoc written as data (`cat`,
|
|
426
|
+
`tee`, `git commit -F -`) is not looked into: where it goes is checked. A
|
|
427
|
+
command the checks can't read is refused, never let through on a text match.
|
|
428
|
+
|
|
429
|
+
And it **asks the owner** before a `write` or `edit` outside the session's
|
|
430
|
+
project root, and before a command that writes there (a redirection's target,
|
|
431
|
+
`tee`'s files, a `cp`/`mv`/`ln`/`install`/`rsync`/`ditto` destination, or a
|
|
432
|
+
target it can't resolve), every time: the ask is fresh (`Verdict#fresh_ask`: no
|
|
433
|
+
stored approval answers it, and no rule lowers it), so it comes even in auto
|
|
434
|
+
mode. The tmp dirs (`Dir.tmpdir`, `$TMPDIR`, `/tmp`, `/private/tmp`) and
|
|
435
|
+
`/dev/null` neither ask nor deny.
|
|
436
|
+
|
|
437
|
+
A participant's ask names them on the owner's card ("Sam's turn asks: approve
|
|
438
|
+
tool call?", `approval.author`/`author_name`, `approval.asked_by`, the same
|
|
439
|
+
line in the plain question: `Approval::ASKED_BY`) and offers **Allow once**
|
|
440
|
+
only: no session, repo or rule approval is made from a participant's call.
|
|
441
|
+
System 1 never judges it, and `chi guardrails replay` skips it. A pair turn
|
|
442
|
+
stops before its next generation once the participant passed their spending
|
|
443
|
+
cap or their link was revoked or expired
|
|
444
|
+
([participants](internals/participants.md#limits-and-the-cap)).
|
|
445
|
+
|
|
446
|
+
The trust model is a team's (everyone invited is trusted): the boundary is an
|
|
447
|
+
**accident guard** for secrets and the owner's voice, not containment of a
|
|
448
|
+
hostile participant; a review session is. A pair turn can still reach, through
|
|
449
|
+
the owner's rules: `web_fetch` (an open egress path: what the turn can read it
|
|
450
|
+
can send out in a URL), plugin and MCP tools, `memory_read` and `context_read`,
|
|
451
|
+
`list_sessions` (the owner's other sessions' titles), and anything a script
|
|
452
|
+
does that the command checks don't read (a script file's body, an interpreter's
|
|
453
|
+
`-e` code). Share a working session only with people you would let type in it.
|
|
454
|
+
|
|
362
455
|
## Rules in config.yml
|
|
363
456
|
|
|
364
457
|
```yaml
|
|
@@ -666,14 +759,16 @@ a body that isn't JSON, a question with no answer, a request longer than
|
|
|
666
759
|
`timeout_ms`. A nil means "ask the user", which is what the caller would
|
|
667
760
|
have done anyway.
|
|
668
761
|
|
|
669
|
-
Toolkit suggestions are a
|
|
670
|
-
`toolkit.suggest.mode` is `audit`
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
and
|
|
676
|
-
|
|
762
|
+
Toolkit suggestions are a second use of System 1. When
|
|
763
|
+
`toolkit.suggest.mode` is `audit` or `on`, the engine asks a `choice`
|
|
764
|
+
question from a background thread after the turn starts. This uses the same
|
|
765
|
+
configured host, model and timeout, but is gated by `toolkit.suggest.mode`,
|
|
766
|
+
not `guardrails.system_one.mode`. Audit never changes the session's toolkit;
|
|
767
|
+
on mode's turn waits for the answer (at most `timeout_ms`, cut by a Stop)
|
|
768
|
+
and may start the session on the suggested kit (configuration.md, Toolkits).
|
|
769
|
+
Its warnings use the `toolkit` tag and its records are written to
|
|
770
|
+
`toolkit_suggestions.jsonl` beside the guardrail logs. To avoid competing
|
|
771
|
+
with the session's model, audit skips (one info line) when
|
|
677
772
|
the System 1 host is the model's local host: the same `hosts:` name, or
|
|
678
773
|
another entry for the same server root (scheme, host and port). There is no
|
|
679
774
|
reliable single-slot signal, so this host-identity check is the available
|
|
@@ -697,8 +792,8 @@ user's **earlier asks they answered** (`SystemOne::Similar`) — this rule in
|
|
|
697
792
|
this repository first, then this rule anywhere, then any answered ask, ranked
|
|
698
793
|
by how many words the logged call shares with this one, ties going to the
|
|
699
794
|
newest. Reading the log is best-effort, so a log that cannot be read simply
|
|
700
|
-
offers no examples. A participant's review
|
|
701
|
-
user there to ask. The judgment runs before the card appears, so an ask is
|
|
795
|
+
offers no examples. A participant's turn (review or pair) is never judged:
|
|
796
|
+
there is no user there to ask, and its asks go to the owner. The judgment runs before the card appears, so an ask is
|
|
702
797
|
slower by up to `timeout_ms` (2000 ms by default).
|
|
703
798
|
|
|
704
799
|
Each judgment is a line in `judgments.jsonl`, beside `approvals.json` in
|
|
@@ -985,9 +1080,11 @@ send; [internals/participants.md](internals/participants.md#apply)). A quote
|
|
|
985
1080
|
lowers the risk, it doesn't remove it: your line still asks the model to act
|
|
986
1081
|
on what it quotes, with your tools, so read the quote before you send it.
|
|
987
1082
|
|
|
988
|
-
Within its own turn a participant's text can't act: the
|
|
989
|
-
leaves it reads of the project only, and an injection that
|
|
990
|
-
model still meets the boundary (containment over detection).
|
|
1083
|
+
Within its own turn in a review session a participant's text can't act: the
|
|
1084
|
+
review boundary leaves it reads of the project only, and an injection that
|
|
1085
|
+
convinces the model still meets the boundary (containment over detection). In
|
|
1086
|
+
a working session it acts, by design ([the pair boundary](#a-participants-pair-boundary)):
|
|
1087
|
+
invite only people you would let type in your session, under your guardrails. What it can do
|
|
991
1088
|
is make the model *say* what the history holds: a review turn's model sees the
|
|
992
1089
|
whole conversation, earlier tool outputs included, so start a review in a
|
|
993
1090
|
fresh session (or a continue), not on a working session full of outputs. A
|