samagotchi 0.105.0 → 0.107.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 +168 -1
- data/bin/chi +2 -0
- data/docs/architecture.md +10 -3
- data/docs/cli.md +90 -9
- data/docs/configuration.md +9 -5
- data/docs/guardrails.md +63 -1
- data/docs/hooks.md +9 -0
- data/docs/internals/engine.md +22 -4
- data/docs/internals/participants.md +319 -45
- data/docs/internals/prompt-caching.md +2 -1
- data/docs/internals/remote-sessions.md +12 -5
- data/docs/internals/working-set.md +8 -2
- data/docs/plugins.md +31 -1
- data/docs/releasing.md +2 -0
- data/docs/sessions.md +36 -1
- data/docs/sub-agent.md +2 -0
- data/docs/testing.md +5 -1
- data/lib/samagotchi/bridge/turn_accumulator.rb +19 -3
- data/lib/samagotchi/bridge.rb +39 -1
- data/lib/samagotchi/bridge_client/stream_request.rb +10 -3
- data/lib/samagotchi/bridge_client.rb +4 -1
- data/lib/samagotchi/bundles/coordinator/manifest.yml +6 -5
- data/lib/samagotchi/bundles/coordinator/plugin.rb +85 -5
- data/lib/samagotchi/bundles/coordinator/skill_coordinator.md +18 -0
- data/lib/samagotchi/bundles/dev/manifest.yml +2 -2
- data/lib/samagotchi/bundles/system/manifest.yml +1 -1
- data/lib/samagotchi/client_id.rb +21 -9
- data/lib/samagotchi/config.rb +16 -1
- data/lib/samagotchi/continue_offer.rb +60 -3
- data/lib/samagotchi/delivery.rb +10 -0
- data/lib/samagotchi/engine.rb +62 -6
- data/lib/samagotchi/guardrail_wiring.rb +13 -3
- data/lib/samagotchi/guardrails/review_boundary.rb +97 -0
- data/lib/samagotchi/guardrails/review_paths.rb +93 -0
- data/lib/samagotchi/guardrails.rb +1 -0
- data/lib/samagotchi/hook_routes.rb +5 -2
- data/lib/samagotchi/hooks/registry.rb +12 -3
- data/lib/samagotchi/idle_recap.rb +9 -0
- data/lib/samagotchi/model_profile.rb +5 -3
- data/lib/samagotchi/parent_report.rb +39 -5
- data/lib/samagotchi/participant_messages.rb +27 -0
- data/lib/samagotchi/participants.rb +46 -9
- data/lib/samagotchi/participants_watch.rb +90 -0
- data/lib/samagotchi/plugin/api.rb +2 -1
- data/lib/samagotchi/plugin/context.rb +7 -1
- data/lib/samagotchi/plugin/sessions.rb +25 -0
- data/lib/samagotchi/plugin/side_question.rb +9 -1
- data/lib/samagotchi/remote_command.rb +1 -1
- data/lib/samagotchi/remotes.rb +23 -8
- data/lib/samagotchi/reply_wait/poll.rb +18 -3
- data/lib/samagotchi/review_apply.rb +123 -0
- data/lib/samagotchi/review_command.rb +121 -0
- data/lib/samagotchi/review_start.rb +161 -0
- data/lib/samagotchi/run_options.rb +18 -3
- data/lib/samagotchi/send_command/wait_target.rb +5 -0
- data/lib/samagotchi/send_command.rb +43 -3
- data/lib/samagotchi/session.rb +33 -5
- data/lib/samagotchi/session_access/contract.rb +12 -2
- data/lib/samagotchi/session_access/dispatch.rb +8 -2
- data/lib/samagotchi/session_access/http_transport.rb +10 -1
- data/lib/samagotchi/session_access/local.rb +50 -11
- data/lib/samagotchi/session_access/remote.rb +34 -3
- data/lib/samagotchi/session_access/web_bridge_client.rb +1 -1
- data/lib/samagotchi/session_access.rb +29 -13
- data/lib/samagotchi/session_chain.rb +10 -2
- data/lib/samagotchi/session_commands.rb +6 -0
- data/lib/samagotchi/session_continue.rb +4 -3
- data/lib/samagotchi/session_inbox.rb +31 -5
- data/lib/samagotchi/session_manager.rb +68 -6
- data/lib/samagotchi/session_metrics/side_totals.rb +21 -7
- data/lib/samagotchi/session_metrics.rb +88 -1
- data/lib/samagotchi/session_review.rb +28 -0
- data/lib/samagotchi/share_command.rb +68 -23
- data/lib/samagotchi/side_usage.rb +4 -2
- data/lib/samagotchi/steer.rb +37 -1
- data/lib/samagotchi/terminal_ui/attach_link.rb +5 -0
- data/lib/samagotchi/terminal_ui/attached_loop.rb +58 -8
- data/lib/samagotchi/terminal_ui/formatting.rb +66 -5
- data/lib/samagotchi/terminal_ui/local_link.rb +7 -0
- data/lib/samagotchi/terminal_ui/participant_commands.rb +77 -0
- data/lib/samagotchi/terminal_ui/remote_link.rb +6 -2
- data/lib/samagotchi/terminal_ui/status_row.rb +11 -2
- data/lib/samagotchi/terminal_ui.rb +5 -0
- data/lib/samagotchi/testing/host.rb +4 -2
- data/lib/samagotchi/testing/recorder.rb +3 -1
- data/lib/samagotchi/testing/sessions.rb +10 -2
- data/lib/samagotchi/toolkit.rb +21 -3
- data/lib/samagotchi/turn_checkpoint.rb +13 -0
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +151 -41
- data/lib/samagotchi/web/grants.rb +172 -18
- data/lib/samagotchi/web/participant_access.rb +273 -46
- data/lib/samagotchi/web/participant_review.rb +208 -0
- data/lib/samagotchi/web/participant_stream_filter.rb +258 -0
- data/lib/samagotchi/web/participant_view.rb +426 -0
- data/lib/samagotchi/web/public/app.js +75 -6
- data/lib/samagotchi/web/public/bubbles.js +3 -3
- data/lib/samagotchi/web/public/chip_popover.js +10 -3
- data/lib/samagotchi/web/public/data.js +30 -2
- data/lib/samagotchi/web/public/delivery_line.js +79 -15
- data/lib/samagotchi/web/public/index.html +97 -27
- data/lib/samagotchi/web/public/info_bar.js +17 -6
- data/lib/samagotchi/web/public/info_bar_model.js +15 -6
- data/lib/samagotchi/web/public/live_turn.js +3 -0
- data/lib/samagotchi/web/public/participant.html +172 -0
- data/lib/samagotchi/web/public/participant.js +447 -0
- data/lib/samagotchi/web/public/participant_model.js +220 -0
- data/lib/samagotchi/web/public/participant_names.js +53 -0
- data/lib/samagotchi/web/public/participants_panel.js +379 -0
- data/lib/samagotchi/web/public/question_cards.js +1 -0
- data/lib/samagotchi/web/public/review_apply.js +34 -0
- data/lib/samagotchi/web/public/review_start.js +101 -0
- data/lib/samagotchi/web/public/send_turn.js +25 -1
- data/lib/samagotchi/web/public/session_actions.js +44 -3
- data/lib/samagotchi/web/public/session_state.js +3 -0
- data/lib/samagotchi/web/public/start_page.js +38 -2
- data/lib/samagotchi/web/public/stream_handlers.js +18 -1
- data/lib/samagotchi/web/public/tokens.css +12 -0
- data/lib/samagotchi/web/public/turn_events.js +16 -5
- data/lib/samagotchi/web/public/turn_html.js +9 -4
- data/lib/samagotchi/web/public/turn_view.js +7 -1
- data/lib/samagotchi/web/public/working_set_view.js +3 -3
- data/lib/samagotchi/web/qr.rb +14 -0
- data/lib/samagotchi/web/running.rb +52 -0
- data/lib/samagotchi/web/server.rb +13 -0
- data/lib/samagotchi/web/session_access_routes.rb +3 -0
- data/lib/samagotchi/web/session_summary.rb +3 -0
- data/lib/samagotchi/worker.rb +121 -7
- data/lib/samagotchi/worker_inbound.rb +12 -4
- metadata +22 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 58e0f763c1974428c1144b884f63a9529fe5b1a973b1443e44b0a4e1a3f3cf48
|
|
4
|
+
data.tar.gz: 6c6a8a429e01a837329065239819bf69568e1455425bcf60e1badae8208553be
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b0fe1b18a4b7defe96fc787ab753e9cfbf6c62eebddac74d05d74b972439b87737a9b5e6192c1d026dc2add3702b1659be6846ebaa894bd87e65dc5ccdffd823
|
|
7
|
+
data.tar.gz: 7a74e55c15487d3ccdac35ecaaec567b5feefab39312712b2ed07befdbfd9def1b5e4685170cc28550fd89f47eca839e5aa06a3c4128ae426e89962f69f0c1db
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,171 @@ and commands may change between minor versions. How releases are made:
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.107.0] - 2026-10-24
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Review sessions for participants (experimental). `chi review docs/plan.md …` starts a session made to be shared:
|
|
16
|
+
its subjects are project files (a secret such as `.env` or a key, a file outside the project or chi's own is
|
|
17
|
+
refused with one message), it starts idle, and it prints the `chi share` line to invite someone. `chi review --from
|
|
18
|
+
ID` starts a review of a working session from its recap, never its transcript; that session keeps running,
|
|
19
|
+
unarchived. Both work with `chi --remote NAME` (`--dir` there). On the web, the start page's `review…` chip takes
|
|
20
|
+
the files, and the participants panel's **Start a review of it** starts a review of the session. Sharing a session that isn't a review
|
|
21
|
+
session still works, with a warning naming how many tool outputs its history holds (`chi share`, the Invite
|
|
22
|
+
dialog). Participants read the subjects as the **Plan**: rendered beside the thread on a wide screen, a Plan |
|
|
23
|
+
Thread toggle on a phone, checked again on every read and redacted. Under each participant's review turn the owner
|
|
24
|
+
gets **Apply**: it puts an owner's turn in the composer, not sent, that quotes the proposal line by line, so the
|
|
25
|
+
participant's words never reach the model as the owner's instruction; sent while a turn runs, it queues as the
|
|
26
|
+
owner's own turn (the delivery line says so; during the owner's own turn Esc steers it in instead), never merged into the running one. Plugins get `ctx.sessions.review(subjects:,
|
|
27
|
+
seed:)`. The coordinator bundle (0.6.0) adds an experimental sprint review: `/coordinate review [handoff]` opens a
|
|
28
|
+
review of the coordinator session seeded with its handoff, where a participant's new context gets a hand-off diff
|
|
29
|
+
and the owner's Apply updates the handoff. See [docs/cli.md](docs/cli.md#review-sessions) and
|
|
30
|
+
[docs/sessions.md](docs/sessions.md#review-sessions).
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
|
|
34
|
+
- `chi`'s run flags take their whole name only, as the subcommands' flags always have: an abbreviation such as
|
|
35
|
+
`--verb` or `--mem x` is now an invalid option (exit 1) instead of `--verbose` / `--memory x`. Under `chi --remote`,
|
|
36
|
+
an abbreviated local-only flag could slip past the "runs on this machine only" check and be dropped silently.
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- The owner's Continue on the step-limit offer of a participant whose turn is refused since (revoked, expired, at
|
|
41
|
+
their cap) ends the offer as a Stop does, with a notice: no "resumed" for a turn that never runs, and idle exit,
|
|
42
|
+
`/exit` and that participant's queued turns no longer wait on an offer nobody can answer.
|
|
43
|
+
- A participant's queued turn the worker refuses (revoked, expired, at their cap) ends their `chi send --wait` (and
|
|
44
|
+
chi web's participant wait) at once: status `refused`, exit 1, `cancel_reason` `revoked` or `cap_reached`, instead
|
|
45
|
+
of waiting to the timeout (none by default).
|
|
46
|
+
- The owner's input while a participant's review turn runs queues as the owner's own turn after it, from every
|
|
47
|
+
client (the web, the attached TUI, `chi send` without `--queue`), never steered or cut into their read-only turn;
|
|
48
|
+
each says it was queued (the web's delivery line, the TUI's note, `chi send`'s line), and `chi send --wait` waits
|
|
49
|
+
for that turn's own answer.
|
|
50
|
+
- Participants: an agent participant's `chi send --wait` no longer reads its reply unfiltered: the reply is
|
|
51
|
+
redacted as the page shows it (secret shapes, the owner's token, host paths, no thinking or tool calls), and a
|
|
52
|
+
failed turn says "this turn failed: the session's owner can see why", never the error's own words.
|
|
53
|
+
- Participants: a secret on a long streamed line (`password: …`, an `Authorization:` header, an `sk-…` key, a
|
|
54
|
+
private key's BEGIN line) is no longer sent unredacted when the participant's stream cuts the line at 400
|
|
55
|
+
characters; a line with no space for 1600 characters ends in `[redacted]`. A private key block that begins on the
|
|
56
|
+
line where another one ends is withheld too.
|
|
57
|
+
- Participants: a participant's link opened on chi web's own machine (or `chi share` on a chi web not on the LAN,
|
|
58
|
+
whose link opens on that machine only) is the participant, no longer the owner: a `chp_` token is the participant's
|
|
59
|
+
from any peer, an ended one is refused, and the owner's browser that opened a participant link is the owner's again
|
|
60
|
+
once that link ends.
|
|
61
|
+
- Participants: each refusal has one wording wherever it is met: a participant's `chi --remote` refused an
|
|
62
|
+
owner's command says "a participant can't do that: only the session's owner can", as chi web does, and an ended
|
|
63
|
+
link's page says whether it was revoked or expired.
|
|
64
|
+
- Participants: chi web and `chi share` revoking (inviting, changing a cap) at the same moment can no longer leave
|
|
65
|
+
`participants.json` reading a revoked participant as active, so the worker refuses their queued turns: the file
|
|
66
|
+
is written under the grants store's lock.
|
|
67
|
+
- Participants: a grants store (`web-grants/grants.json`) that doesn't read is no longer overwritten by the next
|
|
68
|
+
invite, which kept only the new grant and dropped every other link's; `chi share` (exit 1) and chi web refuse invite,
|
|
69
|
+
ls, cap and revoke with one line naming the file, and a grant row without its digest or expiry no longer
|
|
70
|
+
makes every participant request a 500.
|
|
71
|
+
- Participants: revoking a participant (or their link expiring) ends their `chi send --wait` already waiting at chi
|
|
72
|
+
web, refused as their link's end, instead of handing them the reply that came within the next 30 s.
|
|
73
|
+
- Participants: an agent participant's `chi send --wait` no longer gets the owner's wait state from chi web (the
|
|
74
|
+
owner's newest reply file, message count, pending question, running turn), only what waiting on its own turn needs.
|
|
75
|
+
- Participants: a reply that opens a private key block and never closes it is withheld to that turn's end, no
|
|
76
|
+
longer hiding every later turn's reply from the participant's stream until they reload.
|
|
77
|
+
- Participants: two of a participant's turns sent at once while no worker runs the session can no longer both pass
|
|
78
|
+
their queue limit.
|
|
79
|
+
- `chi --remote NAME` words a value the remote's chi web refuses (`sessions list --cwd rel/dir`, `note --source " "`)
|
|
80
|
+
as one line naming the command, exit 2, instead of a Ruby backtrace; its other refusals are one line, exit 1.
|
|
81
|
+
- A remote whose address answers something other than HTTP (another server on that port, an SSH banner), or a
|
|
82
|
+
request that can't be written in time, is the one "remote NAME (URL): …" failure line, exit 1 (`chi --remote`,
|
|
83
|
+
`chi remote add`'s check; the attached TUI reads it as unreachable), instead of a Ruby backtrace.
|
|
84
|
+
- The attached TUI on a remote whose host went away (no answer at all, not a refused connection) gives up each
|
|
85
|
+
stream reconnect after 5 s, so it ends within about two minutes instead of about seventeen.
|
|
86
|
+
- `chi --remote NAME context push` with a name that isn't a source's (`a/b`, `Bad Name`) gives the name rule, as
|
|
87
|
+
it does locally, instead of "too old for this command … update chi there" or a URI error.
|
|
88
|
+
- A remote at an IPv6 address (`chi remote add box http://[::1]:4567`) connects: its commands and its attached
|
|
89
|
+
TUI's stream used the bracketed host as a name to look up ("can't find host").
|
|
90
|
+
- Two `chi remote add` / `chi remote rm` at once keep each other's change: `remotes.yml` is rewritten under a lock.
|
|
91
|
+
- `chi --help` and `chi remote --help` list `review` (and `share`) among the commands `chi --remote NAME` runs, and
|
|
92
|
+
`-p` says it stays in the session (the attached TUI by default, the REPL with `--no-shared`), not "in the REPL".
|
|
93
|
+
- The participant page sends a comment once: a second Enter (or Send) while it is being posted no longer sends it
|
|
94
|
+
again as a second review turn.
|
|
95
|
+
- The participant page that can't reach chi web (a network blip, a 5xx) says "Can't reach the session — retrying…" and
|
|
96
|
+
reads again with a backoff (3 s, doubling, at most 30 s), at open and after a turn boundary, instead of a blank or
|
|
97
|
+
silently stale page; a comment that can't be sent says so and stays in the box.
|
|
98
|
+
- The participant page's live reply no longer loses the text that streams while the page re-reads the review (another
|
|
99
|
+
participant's comment queuing mid-turn left a hole until the turn ended): it streams again from the re-read's
|
|
100
|
+
cursor, and an older re-read answering last no longer overwrites a newer one.
|
|
101
|
+
- The participant page shows chi web's own words for a refused comment or take-back ("A participant can't do that:
|
|
102
|
+
only the session's owner can.", too many turns, the spending cap) and for an ended link (revoked or expired),
|
|
103
|
+
instead of its own second wording.
|
|
104
|
+
- The participant page keeps a tool list the reader opened or closed as they left it when the thread is read again or
|
|
105
|
+
the reply streams, and no longer redraws the messages it already shows (a text selection in them stays).
|
|
106
|
+
- The participant page on a phone is the visible viewport's height (100dvh), so its composer sits above the browser's
|
|
107
|
+
URL bar instead of under it.
|
|
108
|
+
- The participant page no longer adds the next turn's first words to the reply of the turn that just ended while it
|
|
109
|
+
reads the review again (a queued turn starting): the live reply waits for the re-read, whose stream replays them.
|
|
110
|
+
- The web info bar's session clock, speed and ctx chips keep one width while a turn runs (tabular digits in a
|
|
111
|
+
reserved width), and the speed and the clock show from the session's start as muted placeholders: a tick or the
|
|
112
|
+
first generation's speed no longer widens the line and wraps a chip onto a new row.
|
|
113
|
+
- The participants panel drops an Invite, Cap or Revoke answered after it closed or another session opened: a
|
|
114
|
+
one-time link no longer shows the next time a panel opens, under another session, and a failed list no longer
|
|
115
|
+
keeps the previous session's working-session warning.
|
|
116
|
+
- The participants panel's **Start a review of it** closes the panel only once the review is made; a refusal stays in
|
|
117
|
+
the open panel, said there, instead of the panel closing on a toast.
|
|
118
|
+
- The participants panel's Revoke asks first (`Revoke Alex's link? …`), as a delete does: it can't be undone, and on
|
|
119
|
+
a phone's sheet it sat one tap away beside Cap.
|
|
120
|
+
- A participant's older prompt drawn before the participants list came (no name saved with it) no longer says
|
|
121
|
+
"a participant" until the page is reloaded: its badge takes the name when the list comes.
|
|
122
|
+
- The start page's `review…` chip can name a file with a space in its path: in double quotes (`"docs/my plan.md"`);
|
|
123
|
+
an unclosed quote is said in its form. A file Start refuses (a secret, outside the project) is said in that form,
|
|
124
|
+
opened again at the files chosen, instead of a blocking alert.
|
|
125
|
+
- The participants panel no longer closes on its own Cap or Invite another (the button, redrawn away by its own
|
|
126
|
+
click, read as a click outside the panel).
|
|
127
|
+
|
|
128
|
+
Update with `chi update` (the system bundle 0.107.0, the coordinator bundle 0.6.0, the dev profile 0.7.2), then restart `chi web`.
|
|
129
|
+
|
|
130
|
+
## [0.106.0] - 2026-10-23
|
|
131
|
+
|
|
132
|
+
### Added
|
|
133
|
+
|
|
134
|
+
- Participants (experimental) can now ask questions and comment: a participant's message through chi web is a
|
|
135
|
+
review turn of its own, queued behind the running one, that the model answers read-only. It may read the session's
|
|
136
|
+
project (never its secret files, `~/.ssh` or chi's own state and config) and project memories, and proposes
|
|
137
|
+
changes as text for the owner to apply; writes, commands, fetches, notes, reminders, plugin and MCP tools are
|
|
138
|
+
denied, never asked about, and every approval or question stays the owner's. A line starting `!` or `/` and images
|
|
139
|
+
are refused. Each participant gets one running and 3 waiting turns (`web.participants.queue`) and a spending cap,
|
|
140
|
+
`chi share --cap DOLLARS` (default $2, `web.participants.cap`); at the cap their next comment is refused and the
|
|
141
|
+
owner gets a notice, and `chi share cap ID P DOLLARS` raises it. `/stats` shows each participant's spend, their
|
|
142
|
+
turns and the side requests they caused. Hooks see `author:` on a participant's turn and plugins
|
|
143
|
+
`ctx.turn_author`; a hook still runs with the owner's power. A participant now sees the conversation and comments
|
|
144
|
+
from their page (desktop and phone): prompts and replies with who wrote each, one-line tool rows (the tool and a
|
|
145
|
+
project file's path, never its arguments or output), the reply streaming in, their own queued comments (Take back),
|
|
146
|
+
"waiting for the owner" while you answer a question, and every refusal in words. They never see tool output,
|
|
147
|
+
thinking, approvals, questions, memories, notes, the working set, cost or host paths: one server-side filter covers
|
|
148
|
+
their stream and reads, and redacts secret-shaped text as well. On your page, a participant's prompts carry their
|
|
149
|
+
name, and the info bar's participants chip opens a panel to invite (a link to copy, share or scan), see each one's
|
|
150
|
+
spend, turns and last visit, change a cap, revoke, and open the participant view: their page for this session, as
|
|
151
|
+
they see it (an agent participant can wait on its turns' replies through chi web's API). The `review`
|
|
152
|
+
toolkit is new and reserved: config's `toolkit.kits.review` is refused. A session whose worker predates this
|
|
153
|
+
release takes no participant's turn until it is restarted. In the attached terminal a participant's message reads
|
|
154
|
+
`Alex ▸ review> …`, a line says when someone is invited, revoked or expires, the status row counts them, and
|
|
155
|
+
`/share` and `/participants` (list, revoke, cap) run `chi share` on the session there. See [docs/cli.md](docs/cli.md#participants) and
|
|
156
|
+
[docs/guardrails.md](docs/guardrails.md#a-participants-review-boundary).
|
|
157
|
+
|
|
158
|
+
### Fixed
|
|
159
|
+
|
|
160
|
+
- Web, dark theme: the working set's rail (its rule and count), the list's header rows (`files · 2`) and its ops
|
|
161
|
+
text and buttons (drop, pin, add) are easier to read.
|
|
162
|
+
- Web: `!rollback` (and a canceled continue) updates the working set at once; one that emptied it no longer leaves
|
|
163
|
+
the dropped items in the rail and the list until the next turn.
|
|
164
|
+
- `chi share`'s link names the port the running chi web listens on (recorded in `$XDG_STATE_HOME/samagotchi/web.json`),
|
|
165
|
+
not only config's `web.port`.
|
|
166
|
+
- A participant's `chi --remote NAME send -m` prints the review turn's id, and `send --wait -m` waits on it instead of
|
|
167
|
+
being refused as busy; a participant's turn held for the owner reads `waiting_for_owner` (exit 3).
|
|
168
|
+
- `chi send --wait --format json`: a turn still running names the wait on its turn id.
|
|
169
|
+
- The attached terminal no longer announces your own `/share` or `/participants revoke` a second time.
|
|
170
|
+
- Web: the session bar's first line keeps one width through a turn: the status word takes the same room for idle and
|
|
171
|
+
running, the working set's chip shows from the session's start (`set 0`), and every chip is one height. A short
|
|
172
|
+
folder path no longer hops between the bar's rows mid-turn and moves the stage.
|
|
173
|
+
|
|
174
|
+
Update with `chi update`.
|
|
175
|
+
|
|
11
176
|
## [0.105.0] - 2026-10-23
|
|
12
177
|
|
|
13
178
|
### Changed
|
|
@@ -3965,7 +4130,9 @@ and long-lived sessions.
|
|
|
3965
4130
|
- A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
|
|
3966
4131
|
hotkey panel that send selected text or the clipboard to your sessions.
|
|
3967
4132
|
|
|
3968
|
-
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.
|
|
4133
|
+
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.107.0...HEAD
|
|
4134
|
+
[0.107.0]: https://github.com/dm1try/samagotchi/compare/v0.106.0...v0.107.0
|
|
4135
|
+
[0.106.0]: https://github.com/dm1try/samagotchi/compare/v0.105.0...v0.106.0
|
|
3969
4136
|
[0.105.0]: https://github.com/dm1try/samagotchi/compare/v0.104.0...v0.105.0
|
|
3970
4137
|
[0.104.0]: https://github.com/dm1try/samagotchi/compare/v0.103.0...v0.104.0
|
|
3971
4138
|
[0.103.0]: https://github.com/dm1try/samagotchi/compare/v0.102.0...v0.103.0
|
data/bin/chi
CHANGED
|
@@ -54,6 +54,8 @@ SUBCOMMANDS = {
|
|
|
54
54
|
"answer" => ["samagotchi/answer_command", "AnswerCommand"],
|
|
55
55
|
# invite participants to review a session, list them, revoke them
|
|
56
56
|
"share" => ["samagotchi/share_command", "ShareCommand"],
|
|
57
|
+
# a review session for files, or of a session (--from, --dir, --model)
|
|
58
|
+
"review" => ["samagotchi/review_command", "ReviewCommand"],
|
|
57
59
|
# find a model server and write config.yml (--model, --name)
|
|
58
60
|
"bootstrap" => ["samagotchi/bootstrap_command", "BootstrapCommand"],
|
|
59
61
|
# bring an installed chi up to date
|
data/docs/architecture.md
CHANGED
|
@@ -470,7 +470,10 @@ The per-session port is OS-assigned (bound to `0`) and published to a `bridge.js
|
|
|
470
470
|
for client discovery. `chi web`'s `GET /api/sessions/:id/stream` proxies this bridge
|
|
471
471
|
(503 `not_live` when the worker is not running; full history of any session is served by
|
|
472
472
|
`GET /api/sessions/:id/output`), as a byte pipe for the owner: `App#proxy_stream_body` gives `ProxyStreamBody` the
|
|
473
|
-
request
|
|
473
|
+
request's frame filter (`App#stream_filter`: nil for the owner; for a participant, and the owner's participant view
|
|
474
|
+
`?as=participant`, a `Web::ParticipantStreamFilter`, which cuts the Bridge's chunks into frames and writes each again
|
|
475
|
+
from `Web::ParticipantView.event`'s allowlist, dropping every other event; only `from_seq` and `snapshot` reach the
|
|
476
|
+
Bridge; [internals/participants.md](internals/participants.md#what-a-participant-sees)). Resume/ring-buffer state is **in-memory**, per worker: a cursor from
|
|
474
477
|
an earlier worker gets the `reset` snapshot, not a replay.
|
|
475
478
|
|
|
476
479
|
**Session hub.** The cross-session layer (which sessions exist, who owns them, what changed)
|
|
@@ -513,8 +516,8 @@ calling the `SessionAccess::Local` operation of the same name, so a remote backe
|
|
|
513
516
|
token-gated like the rest (LAN mode: `Authorization: Bearer`), and one that takes an id is a `:session` route (an id
|
|
514
517
|
that isn't one, or no such session: 404 `not_found`). Every `ROUTES` row also names who may use it, checked against
|
|
515
518
|
the request's `Web::Principal` (`App#principal_for`): `owner`, or `participant` for a route a participant may use too.
|
|
516
|
-
A participant (a `Web::Grants` token for one session) may use
|
|
517
|
-
each answered as theirs; every other route is `owner` (Permission below, and the participants routes in
|
|
519
|
+
A participant (a `Web::Grants` token for one session) may use the page (`GET /`), `GET /api/info`, their session's
|
|
520
|
+
show, review and filtered stream, and their turns and waits, each answered as theirs; every other route is `owner` (Permission below, and the participants routes in
|
|
518
521
|
[internals/participants.md](internals/participants.md)); `spec/web/principal_spec.rb` fails on a row without one. How a request flows end to end, which checks run where and the trust
|
|
519
522
|
model: [internals/remote-sessions.md](internals/remote-sessions.md).
|
|
520
523
|
|
|
@@ -545,6 +548,8 @@ model: [internals/remote-sessions.md](internals/remote-sessions.md).
|
|
|
545
548
|
| `POST /api/sessions/:id/turn` (`no_interrupt`) | owner | `no_interrupt: true`: the turn runs with the raised iteration limit (`SessionManager.deliver_turn(no_interrupt:)`, to the Bridge or the input file) | as before | |
|
|
546
549
|
| `POST /api/sessions/:id/command` (its 501) | owner | | | 501 `not_supported` for a builtin the worker lacks adds `command` (`"/title"`) and `message` |
|
|
547
550
|
| `GET`/`POST`/`DELETE /api/sessions/:id/participants`, `GET`/`DELETE /api/participants` | owner | `chi share`'s (`name`, `kind`, `ttl`; `?id=p_…`) | the grants, never a token but POST's own once ([internals/participants.md](internals/participants.md)) | 400 `invalid_participant`; 404 |
|
|
551
|
+
| `GET /api/sessions/:id/apply[?turn=T]` | owner | `turn`: a turn id or a prefix (none: the latest review turn with a reply) | 200 `{text, turn_id, author, author_name}` (`ReviewApply`): the owner's Apply of a participant's review turn, for the composer, never sent ([internals/participants.md](internals/participants.md)) | 404 `not_found`; 400 `not_review_turn`; 409 `no_reply` |
|
|
552
|
+
| `POST /api/sessions` (`review`) | owner | `review: {subjects: [project files], from: a session id}`; never with `prompt`, `preview`, `idle` or `continues` (a `from` takes the source's folder and setup) | 201 as a create's: an idle review session (`ReviewStart.start`, or `ReviewStart.fork` with `from`) | 400 `invalid_review` (`ReviewStart::Refused`'s words: a subject, a gone folder); with `from` also 404 `not_found`, 400 `ambiguous_id` |
|
|
548
553
|
|
|
549
554
|
`GET /api/info` names the feature `session_commands` (`SessionAccessRoutes::FEATURE`): `SessionAccess::Remote` asks it
|
|
550
555
|
once before its first request, and a chi web without it (or `serve_static`'s 404 for a path no route takes) is "remote NAME runs chi X, too
|
|
@@ -689,6 +694,8 @@ A map of the parts the overview above doesn't name (paths under `lib/samagotchi/
|
|
|
689
694
|
| Attached context | `context_sources.rb` (`ContextSources`), `context_poller.rb`, `context_absorber.rb`, `peer_sources.rb` (`PeerSources`: the sources peer bundles declare) | External sources a session keeps fresh, and the peer loop's wakes ([context.md](context.md)). |
|
|
690
695
|
| LLM context strategies | `llm_context_strategy.rb`, `llm_context_view.rb`, `llm_context_stale.rb`, `llm_context_forget.rb` | What a request sends of the history (none, stale, forget) ([internals/llm-context-forget.md](internals/llm-context-forget.md)). |
|
|
691
696
|
| Session chains | `session_continue.rb` (`SessionContinue`), `session_chain.rb` (`SessionChain`) | A session that continues an earlier one. `SessionChain.opener_for` gives the text web `continue →` prefills in the new link: the previous link's own first typed message when that link continues one itself, else nil (the 201's `opener`; not saved) ([sessions.md](sessions.md)). |
|
|
697
|
+
| Review sessions | `review_start.rb` (`ReviewStart`), `session_review.rb` (`SessionReview`, the session's `review` field), `review_command.rb` (`ReviewCommand`, `chi review`), `review_apply.rb` (`ReviewApply`) | A session made to be shared: idle, its subjects project files checked by `Guardrails::ReviewPaths`, a fork of a working session from its recap (`--from`); Apply quotes a participant's review turn into the owner's composer ([sessions.md](sessions.md#review-sessions), [internals/participants.md](internals/participants.md)). |
|
|
698
|
+
| Participants | `participants.rb` (`Participants`, the `participants.json` sidecar), `share_command.rb` (`ShareCommand`, `chi share`), `session_participants.rb`, `participants_watch.rb`, `web/grants.rb` (`Web::Grants`), `web/principal.rb` (`Web::Principal`), `web/participant_access.rb` (`Web::ParticipantAccess`), `web/participant_review.rb` + `web/participant_view.rb` (`Web::ParticipantReview`, `Web::ParticipantView`), `web/participant_stream_filter.rb`, `guardrails/review_boundary.rb` + `guardrails/review_paths.rb` | The people or agents an owner invites to review one session through chi web: their grants and gate, what they read, their read-only review turns ([internals/participants.md](internals/participants.md), [guardrails.md](guardrails.md)). |
|
|
692
699
|
| Analytics | `analytics_file.rb` (`AnalyticsFile`), `session_metrics/totals.rb` (`SessionMetrics::Totals`), `session_metrics/model_segment.rb` (`SessionMetrics::ModelSegment`) | A session's `analytics.json` (`SessionMetrics#persist`); the sums over its turn records (`Totals`: tokens, cost, speeds, kept as running counters) and their split per model after a `/model` switch (`ModelSegment`: a run of turns on one model, the `model_segments` rows); `session_metrics/cost.rb` (`SessionMetrics::Cost`): the spend `ctx.cost` reads; `session_metrics/usage.rb` (`SessionMetrics::Usage`): the work `ctx.usage` reads. `session_metrics/side_totals.rb` (`SessionMetrics::SideTotals`) and `side_usage.rb` (`SideUsage`): the side requests' sums, apart from the turns'. |
|
|
693
700
|
| Images / vision | `image_store.rb` (`ImageStore`), `vision_support.rb`, `vision_context.rb` | Stored prompt images and whether a model takes them. |
|
|
694
701
|
| Toolkits | `toolkit.rb` (`Toolkit`), `toolkit_binding.rb` (`ToolkitBinding`, the Engine's seam), `toolkit_command.rb` (`/toolkit`), `tools/registry.rb` (`Registry#toolkit`, `#left_out_names`), `web/public/toolkit_chip.js` | Which registered tools a session's prompts declare: saved in the session, resolved at a turn's start (`Engine#apply_toolkit!`), named in the prompt's not-loaded line, a kit's prompt text after the appended files; `chi.toolkit` and `ctx.use_toolkit` for plugins ([configuration.md](configuration.md#toolkits)). |
|
data/docs/cli.md
CHANGED
|
@@ -18,12 +18,14 @@
|
|
|
18
18
|
- `chi web --no-web-working-set` — turns the stage view's working set off (on by default; `--web-working-set` turns a config's `web.working_set: false` back on): what each turn brought into the model's context, in the stage and under each finished turn; `?working_set=1|0` on the page URL overrides it (see [The working set](#the-working-set))
|
|
19
19
|
- `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, and its own settings (`--set`; a `settings:` line), from a live worker's `GET stats`, else its saved `analytics.json` (`--format json` one object, with `settings`), 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`, a `--days`, `--keep` or `--limit` that isn't a whole number) exits 2; an unknown or refused session exits 1
|
|
20
20
|
- `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))
|
|
21
|
+
- `chi broadcast [-m TEXT] [--all] [--dry-run]`, `chi broadcast log [--last N] [--format json]`, `chi broadcast deliver BROADCAST_ID (ID|PREFIX)...` — share one piece of information (TEXT or stdin) with every session it may concern, as a context note from `broadcast` (see [Broadcast](broadcast.md))
|
|
21
22
|
- `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))
|
|
22
23
|
- `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 — to an idle session whose last turn already ended, that ended turn's reply or end comes back; `--wait --turn T ID` with no message waits for exactly turn T, whether running, queued or last ended; `--format json` includes its `turn_id` when known, for waiting again after exit 3 or 4; 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))
|
|
23
24
|
- `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))
|
|
24
25
|
- `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))
|
|
25
|
-
- `chi
|
|
26
|
-
- `chi --
|
|
26
|
+
- `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))
|
|
27
|
+
- `chi share ID --name NAME [--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 review a session through chi web on the LAN (a link of their own, 7 days, a $2 spending cap), list them, change one's cap, revoke them (see [Participants](#participants))
|
|
28
|
+
- `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))
|
|
27
29
|
- `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))
|
|
28
30
|
- `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))
|
|
29
31
|
- `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))
|
|
@@ -273,6 +275,8 @@ bundles or the helper can be updated.
|
|
|
273
275
|
|
|
274
276
|
Samagotchi exposes one flag that feeds a prompt (`-p`, `--prompt`) and one that
|
|
275
277
|
controls exit behavior (`--non-interactive`); `--resume` composes with both.
|
|
278
|
+
Every flag takes its whole name, as the subcommands' flags do: an abbreviation (`--verb`, `--mem x`) is an
|
|
279
|
+
invalid option (exit 1).
|
|
276
280
|
|
|
277
281
|
| Flag | Purpose |
|
|
278
282
|
|------|---------|
|
|
@@ -1274,10 +1278,11 @@ A session's owner can invite **participants**, people or agents, to review it th
|
|
|
1274
1278
|
|
|
1275
1279
|
```sh
|
|
1276
1280
|
chi share 3f9a1c2e --name Alex
|
|
1277
|
-
# p_7c01d2aa Alex (human, reviewer) · session 3f9a1c2e · expires 2026-10-30 14:02
|
|
1281
|
+
# p_7c01d2aa Alex (human, reviewer) · session 3f9a1c2e · expires 2026-10-30 14:02 · cap $2.00
|
|
1278
1282
|
# http://192.168.1.29:4567/?token=chp_…#s=3f9a1c2e-…
|
|
1279
1283
|
# (shown once: chi keeps only its digest; chi share revoke 3f9a1c2e p_7c01d2aa ends it)
|
|
1280
1284
|
chi share ls # every session's participants (--format json; never a token)
|
|
1285
|
+
chi share cap 3f9a1c2e p_7c01d2aa 5 # Alex's turns may cost you $5, from their next one
|
|
1281
1286
|
chi share revoke 3f9a1c2e # every link to that session; add a participant id for one
|
|
1282
1287
|
chi share revoke --all # every link to every session
|
|
1283
1288
|
```
|
|
@@ -1285,13 +1290,88 @@ chi share revoke --all # every link to every session
|
|
|
1285
1290
|
- The link is printed once (with a QR code at a terminal): chi keeps only a digest of its token. Send it to the
|
|
1286
1291
|
participant; opening it sets their browser's cookie, as the owner's LAN link does. `--agent` marks an agent
|
|
1287
1292
|
participant, which uses the token as `Authorization: Bearer` (e.g. `chi --remote` with that token).
|
|
1288
|
-
- It works for 7 days (`--expires Nh` or `Nd`, 1 hour to 30 days), until it is revoked,
|
|
1289
|
-
archived or deleted. A revoked
|
|
1290
|
-
|
|
1291
|
-
|
|
1293
|
+
- It works for 7 days (`--expires Nh` or `Nd`, 1 hour to 30 days; `web.participants.expires`), until it is revoked,
|
|
1294
|
+
or until the session is archived or deleted. A revoked link shows "This review link was revoked by the session's
|
|
1295
|
+
owner.", an expired one "This review link expired: ask the session's owner for a new one."
|
|
1296
|
+
- **Review turns (experimental).** A participant can ask questions and comment through chi web's `POST …/turn`: each
|
|
1297
|
+
message is a turn of its own, queued behind the running one, that the model answers **read-only**. It may read the
|
|
1298
|
+
project (never its secret files or chi's own), and proposes changes as text or a diff for you to apply; it can't
|
|
1299
|
+
write, run, fetch, send notes or set reminders, and any approval or question is yours. A line starting `!` or `/`
|
|
1300
|
+
and images are refused. One running and 3 waiting turns per participant (`web.participants.queue`). You pay: each
|
|
1301
|
+
participant has a spending cap (`--cap DOLLARS`, default $2, `web.participants.cap`), counted from their turns'
|
|
1302
|
+
cost and the side requests they cause; at the cap their next comment is refused and you get a notice, and `chi
|
|
1303
|
+
share cap` raises it. `/stats` shows each participant's spend. Their turns go only to a worker with the review
|
|
1304
|
+
boundary: restart an older one. While your turn waits at the step limit (Continue or Stop), their turns wait too
|
|
1305
|
+
(still queued, still counted): they run, in order, once you continue, stop or send a turn of your own.
|
|
1306
|
+
- **The participant page.** Their link opens a page of its own (never yours): the session's title and status, who
|
|
1307
|
+
else takes part, and the conversation as a participant sees it: every prompt and reply with who wrote it, each
|
|
1308
|
+
step's tools as one line (`read docs/plan.md`: the tool and a project file's path, never arguments or output), and
|
|
1309
|
+
the reply streaming in while a turn runs. A "Comment or ask" box sends their comment; their queued comments show
|
|
1310
|
+
with **Take back**; "waiting for the owner" shows while you answer a question or an approval of the turn; a refused
|
|
1311
|
+
comment (a `!` or `/` line, too many waiting, their cap reached) says why. On a phone it is the thread first with
|
|
1312
|
+
the box pinned, tool lines folded. They never see tool output or arguments, thinking, approvals or questions,
|
|
1313
|
+
memories, context notes, plugin and delegate input, cards, command output, the working set, cost, timing or host
|
|
1314
|
+
paths; secret-shaped text (API keys, tokens, private keys) is redacted too. An agent participant can wait on its
|
|
1315
|
+
turns' replies through the API, or with chi: its link's token in a remote, `chi --remote NAME send --wait -m "…" ID`,
|
|
1316
|
+
or `send -m`, which prints the turn id `--wait --turn T` takes; that wait exits 3 with `waiting_for_owner` while
|
|
1317
|
+
their turn waits on you (a question, an approval, your step-limit Continue) and 1 with `refused` (`cancel_reason`
|
|
1318
|
+
`revoked` or `cap_reached`) when their queued turn is refused before it runs. They can't reach anything else: every other route answers them 403, another
|
|
1319
|
+
session 404.
|
|
1320
|
+
- **Apply.** Under each of a participant's review turns that has a reply, your page shows **Apply** (full width on a
|
|
1321
|
+
phone): it puts an Apply turn in your composer, not sent, `Apply this proposal from Alex's review turn 3f2a1c0d, as
|
|
1322
|
+
far as it fits the plan. It's quoted: …` and the reply quoted line by line. Read it, edit it, send it: the
|
|
1323
|
+
participant's text reaches the model as a quote under your line, never as your instruction. Don't send "do what
|
|
1324
|
+
Alex said" instead: that hands their words your tools.
|
|
1325
|
+
- **Your side of it.** In the session bar, the participants chip (`invite` while nobody has a link, then `2
|
|
1326
|
+
participants`, with a green dot while one is on their page) opens the panel: each participant's state, last visit,
|
|
1327
|
+
spend against their cap, turns and expiry, with **Cap** and **Revoke** (revoking ends their page and stream and
|
|
1328
|
+
drops their queued comments). **Invite** takes a name, person or agent, the expiry and a cap, and shows the link
|
|
1329
|
+
once, with Copy, Share (a phone's share sheet) and its QR code. **Participant view ↗** opens their page for this
|
|
1330
|
+
session in a new tab, read-only, through the same server filter: exactly what they see. A participant's prompt
|
|
1331
|
+
shows their name in their colour, live and after a reload.
|
|
1292
1332
|
- `chi --remote studio share …` invites on studio: the link names studio's LAN address. Locally, the link names
|
|
1293
|
-
this machine's chi web
|
|
1333
|
+
this machine's running chi web, on the port it listens on (it records it in `$XDG_STATE_HOME/samagotchi/web.json`;
|
|
1334
|
+
`web.port` when none answers there); without one on the LAN, chi says the link opens on this machine only (as
|
|
1335
|
+
the participant there too: a participant's link never opens as the owner).
|
|
1294
1336
|
- The owner's own token and the `chi web --new-token` rotation are separate: rotating it ends no participant's link.
|
|
1337
|
+
- **In the attached terminal.** A participant's message reads `Alex ▸ review> is step 3 right?`, the name in that
|
|
1338
|
+
participant's own colour (queued, running, and the last exchange when you join); yours stay as they were. A line
|
|
1339
|
+
says when someone is invited, revoked or their link expires (`participants> Alex joined (p_7c01d2aa)`,
|
|
1340
|
+
`participants> Alex left: their link was revoked`), within a few seconds and between turns, and the status row
|
|
1341
|
+
counts who is in the review (`2 participants`). The same commands work from there, on the attached session:
|
|
1342
|
+
`/share NAME [--agent] [--expires 3d] [--cap 2]` (the link and its QR code, once), `/participants` (the list),
|
|
1343
|
+
`/participants revoke [P]` (one participant, or everyone) and `/participants cap P DOLLARS`. They are `chi share`'s
|
|
1344
|
+
own, so under `chi --remote NAME` they reach that machine's chi web, as `chi --remote NAME share` does; an invite or
|
|
1345
|
+
a revoke made with them isn't announced again by the `participants>` line. The REPL
|
|
1346
|
+
(`--no-shared`) answers them with a note: a participant's turns run on the session's worker.
|
|
1347
|
+
|
|
1348
|
+
#### Review sessions
|
|
1349
|
+
|
|
1350
|
+
Share a **review session**, not a working one: a working session's history holds its tool outputs, and a review
|
|
1351
|
+
turn's model may quote them. `chi review` starts one, idle:
|
|
1352
|
+
|
|
1353
|
+
```sh
|
|
1354
|
+
chi review docs/plan.md docs/spec.md # a fresh session in this project for those files
|
|
1355
|
+
# 4b7e9a01-… review · docs/plan.md, docs/spec.md · idle
|
|
1356
|
+
# open it: chi --attach 4b7e9a01 or the web
|
|
1357
|
+
# invite someone: chi share 4b7e9a01 --name NAME
|
|
1358
|
+
chi review --from 3f9a1c2e docs/plan.md # a review of a working session: its recap, not its transcript
|
|
1359
|
+
```
|
|
1360
|
+
|
|
1361
|
+
- Each FILE is a **subject**: a file in the session's project (the git work tree of its folder, `--dir`, else
|
|
1362
|
+
here), relative to that folder or absolute. A missing file, a folder, a file outside the project, a secret
|
|
1363
|
+
(`.env*`, keys and certificates, credentials files, a git config) or chi's own refuses the review with one line:
|
|
1364
|
+
`chi review: .env can't be a review subject: …`. At most 20.
|
|
1365
|
+
- `--from ID` (an id, a prefix, `last:ID`) starts a **review fork**: a new session in ID's folder, on its model
|
|
1366
|
+
(`--model` to change it) and setup, seeded with a fresh recap of ID (its live worker is asked first, as a continue
|
|
1367
|
+
asks), never its transcript. ID keeps running, unarchived, its delegates its own. Its FILEs are relative to ID's
|
|
1368
|
+
folder.
|
|
1369
|
+
- `chi --remote NAME review … --dir /path/there` starts it on that machine; its chi web checks the files there.
|
|
1370
|
+
- Sharing a working session (one `chi review` didn't start) still works, with a warning on stderr that says how many
|
|
1371
|
+
tool outputs its history holds: `chi share: warning: 3f9a1c2e is a working session, not a review session: a
|
|
1372
|
+
participant's review turn can quote what its history holds (42 tool outputs so far). … (chi review --from 3f9a1c2e
|
|
1373
|
+
starts one)`. On the web the participants panel shows the same words before its Invite, with **Start a review of
|
|
1374
|
+
it**, and again above the link an invite gives.
|
|
1295
1375
|
|
|
1296
1376
|
How it works inside (grants, the gate, the routes, the audit trail):
|
|
1297
1377
|
[internals/participants.md](internals/participants.md).
|
|
@@ -1417,7 +1497,7 @@ changes:
|
|
|
1417
1497
|
|
|
1418
1498
|
```
|
|
1419
1499
|
status> Coordinator | model=Qwen3.6-35B | ↳ 3f2a1c9e | ctx=12.3% (under20) | mem: notes, cli_usage | muted: gh-helper
|
|
1420
|
-
status> model=Qwen3.6-35B | ⑂ 2 running · 1 waiting | ctx=8.1% (under20) | mem: skill_coordinator
|
|
1500
|
+
status> model=Qwen3.6-35B | ⑂ 2 running · 1 waiting | 2 participants | ctx=8.1% (under20) | mem: skill_coordinator
|
|
1421
1501
|
```
|
|
1422
1502
|
|
|
1423
1503
|
- The session's title first, when it has one (`/title`; not the first message a session without one is named by),
|
|
@@ -1434,6 +1514,7 @@ status> model=Qwen3.6-35B | ⑂ 2 running · 1 waiting | ctx=8.1% (under20) | me
|
|
|
1434
1514
|
ends and when a delegate's report merges into a turn, the REPL whenever it draws the row. A
|
|
1435
1515
|
child that starts waiting on an approval (which doesn't wake the parent) shows at the next of
|
|
1436
1516
|
those.
|
|
1517
|
+
- `N participants`: who is in the session's review now ([Participants](#participants)), attached only.
|
|
1437
1518
|
- `ctx=`: the kernel's context estimate and its bucket, updated during a turn (on hosts that
|
|
1438
1519
|
report none, `api: openai`, at the turn's end).
|
|
1439
1520
|
- `mem:`: the memories the session read, with its `--memory` list; `muted:` its `--mute` list.
|
data/docs/configuration.md
CHANGED
|
@@ -157,7 +157,7 @@ or `chi web` restart.
|
|
|
157
157
|
| `toolkit.default`, `toolkit.child` | Session creation | The chosen toolkit is saved; changing the defaults does not move existing sessions. |
|
|
158
158
|
| `toolkit.kits`, `execute.description`, `llm_context.policy` | Worker start | These are held by the engine's toolkit or tool registry. |
|
|
159
159
|
| `skip_agents_md` | Prompt rebuild | A running engine caches its built prompt; a new worker or `/model` rebuild uses the changed value. |
|
|
160
|
-
| `context.*` except `context.every_seconds`, `llm_context.*` except `llm_context.policy`, `steer.cut_after`, `memory.index_warn_tokens`, `status.line` | 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. |
|
|
160
|
+
| `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. |
|
|
161
161
|
| `context.every_seconds` | Next context poll | The poller checks it for each source that has no explicit interval. |
|
|
162
162
|
| `guardrails.*` | Next tool call or approval | Guardrail rules and disable lists are reloaded when the config file changes; rule settings take effect on that evaluation. `guardrails.parent_answers` is checked when a parent answers a question. |
|
|
163
163
|
| `hooks`, `bundles` | Worker start | Hooks, plugins and their bundle settings load with the engine. |
|
|
@@ -165,7 +165,7 @@ or `chi web` restart.
|
|
|
165
165
|
| `session.shared` | Next `chi` invocation | It selects attached-worker or in-process mode for that invocation. |
|
|
166
166
|
| `session.idle_exit_minutes`, `session.keep_empty`, `session.max_children`, `session.delegate_reports`, `session.task_wakes`, `session.max_wakes` | Worker start or next matching lifecycle decision | Existing workers keep their idle-exit timer; the other values are read for the next corresponding decision. |
|
|
167
167
|
| `session.retention_*`, `session.max_count`, `session.keep_status`, `session.sweep_interval_hours` | Next retention sweep | `chi web` sweeps on its configured schedule and on `GET /api/sessions`; `chi sessions prune` uses the values for that invocation. The sweep interval is checked on the next hub tick. |
|
|
168
|
-
| `web.*` | `chi web` start | Restart `chi web` to change its bind address, port or page settings. |
|
|
168
|
+
| `web.*` except `web.participants.*` | `chi web` start | Restart `chi web` to change its bind address, port or page settings. |
|
|
169
169
|
| `log.*` | Process start | Each process holds its configured log sink and level after first use; restart that process. |
|
|
170
170
|
| `history.file` | Next history access | The TUI and web composer resolve its path when they read or append history. |
|
|
171
171
|
| `broadcast.*` | Next `chi broadcast` | Each broadcast invocation reads its targeting and triage settings. |
|
|
@@ -1391,7 +1391,7 @@ toolkit:
|
|
|
1391
1391
|
default: lean # the sessions you start (unset: full)
|
|
1392
1392
|
child: child # delegate children (unset: full)
|
|
1393
1393
|
kits: # your own, on a base
|
|
1394
|
-
|
|
1394
|
+
audit: { base: lean, add: [web_fetch, delegate], remove: [memory_write] }
|
|
1395
1395
|
terse: { base: lean, prompt: "- Be concise in your responses" }
|
|
1396
1396
|
```
|
|
1397
1397
|
|
|
@@ -1402,13 +1402,14 @@ toolkit:
|
|
|
1402
1402
|
| `child` | `lean` plus `send_note`, without `memory_write`, `delegate`, `delegate_result` |
|
|
1403
1403
|
| `coordinator` | `lean` plus `delegate`, `delegate_result`, `list_sessions` (declared by the coordinator bundle) |
|
|
1404
1404
|
| `coding` | `child`, with the prompt text `- Be concise in your responses` / `- Show file paths clearly when working with files` (declared by the coordinator bundle, for build and fix children) |
|
|
1405
|
+
| `review` | `read`, `memory_read` (not `forget_outputs`, even under the `forget` layer): what a [participant's](internals/participants.md#turns) turn may call, whatever the session declares (the review boundary, [guardrails](guardrails.md#a-participants-review-boundary)). Reserved: `toolkit.kits.review` is refused ("review is reserved for participants' turns"), and a bundle can't declare it. A session may run on it too, as any kit |
|
|
1405
1406
|
|
|
1406
1407
|
- **Plugin tools** are declared in every toolkit unless its `remove:` names them, and so is `forget_outputs` under
|
|
1407
1408
|
the `forget` layer. A `kits:` entry takes `base:` (default `full`), `add:`, `remove:` and `prompt:` (any other
|
|
1408
|
-
key, a typo or a `tools:` list, is ignored with a warning); it can't reuse a built-in's name. A bundle declares its own with `chi.toolkit` ([plugins](plugins.md#chitoolkitname-base-add--remove--prompt-nil)).
|
|
1409
|
+
key, a typo or a `tools:` list, is ignored with a warning); it can't reuse a built-in's name (`review`'s least of all: it is the participants' boundary). A bundle declares its own with `chi.toolkit` ([plugins](plugins.md#chitoolkitname-base-add--remove--prompt-nil)).
|
|
1409
1410
|
- **A kit's prompt text.** `prompt:` (text, several lines allowed) goes into the system prompt of the sessions on
|
|
1410
1411
|
that kit, verbatim, with no header: after AGENTS.md and the appended files, before the memory indexes. A kit
|
|
1411
|
-
without one inherits its base's (`
|
|
1412
|
+
without one inherits its base's (`audit: {base: terse, add: [web_fetch]}` stays concise); `prompt: ""` clears
|
|
1412
1413
|
it; a session's own `--tool` / `/toolkit add` never touch it. Sessions on the same kit share it in the prompt
|
|
1413
1414
|
cache; `full` and every kit without a prompt send the bytes they sent before. Keep it to a line or two that
|
|
1414
1415
|
fits the kit's work (the coordinator bundle's `coding`: be concise): one that isn't text or is over 2000
|
|
@@ -1719,6 +1720,9 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
|
|
|
1719
1720
|
| `web.working_set` | `true` | yes | The stage view's working set: the files, memories and URLs a turn brought into the model's context, in a rail in the stage, a footprint line under each finished turn and a `set N` chip in place of the mem chip; `false` brings the mem chip back. `?working_set=1` or `?working_set=0` on the page URL overrides it for that page load; the turn view ignores it. See [CLI: the working set](cli.md#the-working-set). |
|
|
1720
1721
|
| `web.annotate_presets` | `Agreed\|Could you please elaborate?` | yes | Quick replies next to Annotate in `chi web`, `\|`-separated (a YAML list works too); `""` in the file or on the CLI leaves only Annotate (an empty env value means the default). See [CLI](cli.md#web-annotate-presets). |
|
|
1721
1722
|
| `web.editor` | `vscode` | yes | What a file ref in `chi web` (a file tool row's title, a diff line) opens, for a viewer on this machine: a preset (`vscode`, `vscode-insiders`, `cursor`, `zed`) or a URL template with `{path}` and an optional `{line}`; `none` turns refs off. See [Opening files from chi web](#opening-files-from-chi-web). |
|
|
1723
|
+
| `web.participants.expires` | `7d` | | How long a participant's link lasts unless `chi share --expires` says otherwise: `Nh` or `Nd`, 1h to 30d. See [participants](internals/participants.md). |
|
|
1724
|
+
| `web.participants.cap` | `2.0` | | What a participant's turns, and the side calls they cause, may cost you in dollars unless `chi share --cap` says otherwise ($0 to $1000); at the cap their next comment is refused (429 `cap_reached`) and you get a notice. `chi share cap ID P DOLLARS` changes one grant's. |
|
|
1725
|
+
| `web.participants.queue` | `3` | | How many of a participant's turns may wait behind their running one (0 to 20); one more is refused (429 `too_many_turns`). |
|
|
1722
1726
|
| `history.file` | state dir | | Prompt history path, shared by the TUI and the web composer. |
|
|
1723
1727
|
| `no_interrupt` | `false` | `--no-interrupt` | Raise the step limit of a turn to 1000 (or `turn.max_iterations`, when larger); a top-level key. |
|
|
1724
1728
|
| `no_default_input` | `false` | `--no-default-input` | Don't pre-fill `default.input`; a top-level key. |
|
data/docs/guardrails.md
CHANGED
|
@@ -298,6 +298,52 @@ card still names `child-boundary`. The parent (never a delegate), forks and
|
|
|
298
298
|
ordinary sessions don't get it. Like the rules it reads command text: a
|
|
299
299
|
`sh -c` body, a script or `$(…)` isn't looked into.
|
|
300
300
|
|
|
301
|
+
## A participant's review boundary
|
|
302
|
+
|
|
303
|
+
Built in, for a [participant's](internals/participants.md#turns) turns only (a
|
|
304
|
+
turn whose author is `p:<id>`), in every mode (also with
|
|
305
|
+
`guardrails.enabled: false`) and without the guardrails bundle:
|
|
306
|
+
`Guardrails::ReviewBoundary`, rule `review-boundary`, source `core`. It comes
|
|
307
|
+
right after the load-failure check (rules that don't load deny every call) and before the hooks' asks, the
|
|
308
|
+
protected paths and the rules (`GuardrailWiring#checks`; a deny is sticky, so no later vote turns it into a
|
|
309
|
+
question), and it
|
|
310
|
+
**denies, never asks**: nobody on the participant's side may approve, and the
|
|
311
|
+
owner's approval would give the participant's text the owner's tools. A
|
|
312
|
+
participant's turn may call only the `review` kit's tools:
|
|
313
|
+
|
|
314
|
+
- `read`, under the session's project root (the work tree top of its folder)
|
|
315
|
+
and never a secret: chi's state and config dirs (sessions, the grants, the
|
|
316
|
+
web token, memories and their `.bundles`, `config.yml`, hooks), `~/.ssh`,
|
|
317
|
+
`~/.gnupg`, `~/.aws`, and the project's `.env*`, `.netrc`, `*.pem`, `*.key`,
|
|
318
|
+
`*.p12`, anything named `credentials` (`.git-credentials` too), `.npmrc`,
|
|
319
|
+
`.pypirc`, ssh keys (`id_rsa`, `id_ed25519`, …), `*.tfstate`,
|
|
320
|
+
`secrets.yml`/`.json`, `.docker/config.json`, `.kube/config` and any config
|
|
321
|
+
under `.git` (a submodule's too). The path is checked as given and as it
|
|
322
|
+
resolves, so a symlink in the project to `~/.ssh` is `~/.ssh`. A session
|
|
323
|
+
whose folder is the home folder or above it reads nothing.
|
|
324
|
+
- `memory_read` with `scope: project` (a read without a scope would fall back
|
|
325
|
+
to the system memories). The prompt's preloaded memories and the memory
|
|
326
|
+
index are in the system prompt anyway: a review session should start
|
|
327
|
+
without personal ones.
|
|
328
|
+
|
|
329
|
+
Everything else is denied with "a participant's review turn may only read …",
|
|
330
|
+
and the model is told to propose the change as text or a diff for the owner
|
|
331
|
+
to apply: `write`, `edit`, `memory_write`, `execute`, the task tools,
|
|
332
|
+
`delegate`, `send_note`, `list_sessions`, `register_reminder` (it would fire
|
|
333
|
+
an owner turn later), `web_fetch`, `context_read`, `ask_user_question`,
|
|
334
|
+
`forget_outputs` (it would replace the owner's earlier tool outputs with the
|
|
335
|
+
participant's note, which later owner turns would read as their own), and
|
|
336
|
+
every plugin or MCP tool, including through `call_tool` (a built-in reached
|
|
337
|
+
through `call_tool` is checked as that built-in). The session's declared tools
|
|
338
|
+
stay its own (no prompt rebuild per author), so the model may still try a
|
|
339
|
+
write; the turn's header ("This turn may only read") keeps that rare.
|
|
340
|
+
|
|
341
|
+
The kit is `Toolkit::REVIEW`, a frozen built-in: config's `toolkit.kits.review`
|
|
342
|
+
is refused with one message ([configuration](configuration.md#toolkits)), so
|
|
343
|
+
config can't widen the boundary. Hooks still run on a participant's turn, with
|
|
344
|
+
the owner's power ([hooks](hooks.md#participants-turns)); a hook that rewrites a
|
|
345
|
+
call is checked on the call it leaves.
|
|
346
|
+
|
|
301
347
|
## Rules in config.yml
|
|
302
348
|
|
|
303
349
|
```yaml
|
|
@@ -702,7 +748,23 @@ keep every guardrail. But an owner turn that says "do what Alex said" hands
|
|
|
702
748
|
Alex's text the owner's tools: the model will act on it as the owner asked.
|
|
703
749
|
That is the residual risk, documented rather than engineered away: on a
|
|
704
750
|
session with sensitive tools, quote the proposal you want applied in your own
|
|
705
|
-
turn instead
|
|
751
|
+
turn instead: the web's **Apply** under a review turn does that (it puts your
|
|
752
|
+
line and the reply quoted line by line in your composer, for you to read and
|
|
753
|
+
send; [internals/participants.md](internals/participants.md#apply)). A quote
|
|
754
|
+
lowers the risk, it doesn't remove it: your line still asks the model to act
|
|
755
|
+
on what it quotes, with your tools, so read the quote before you send it.
|
|
756
|
+
|
|
757
|
+
Within its own turn a participant's text can't act: the review boundary
|
|
758
|
+
leaves it reads of the project only, and an injection that convinces the
|
|
759
|
+
model still meets the boundary (containment over detection). What it can do
|
|
760
|
+
is make the model *say* what the history holds: a review turn's model sees the
|
|
761
|
+
whole conversation, earlier tool outputs included, so start a review in a
|
|
762
|
+
fresh session (or a continue), not on a working session full of outputs. A
|
|
763
|
+
line of a participant's text that looks like one of chi's own headers goes
|
|
764
|
+
out quoted (`(quoted) [Steer from the user, …]`), under the real one, in any
|
|
765
|
+
case and after any spaces (chi's context-note and task frames too). The owner's
|
|
766
|
+
due reminders wait for the owner's next turn: they never go into a
|
|
767
|
+
participant's.
|
|
706
768
|
|
|
707
769
|
## Limits
|
|
708
770
|
|
data/docs/hooks.md
CHANGED
|
@@ -74,6 +74,15 @@ of the hook about to run) and the callables `notify:`, `ask_user:`,
|
|
|
74
74
|
undefined behaviour. `:before_tool_call` carries no messages (the gate stays
|
|
75
75
|
cheap).
|
|
76
76
|
|
|
77
|
+
## Participants' turns
|
|
78
|
+
|
|
79
|
+
A [participant's](internals/participants.md#turns) review turn fires the same hooks, and every event of it carries
|
|
80
|
+
`author: "p:<id>"` (the owner's carry no `author:` key, as before); a plugin reads the same as `ctx.turn_author`
|
|
81
|
+
(nil for the owner's turns and between turns). **A hook runs with the owner's power whatever the author**: it may
|
|
82
|
+
read, write and run what the owner's chi may, and `notify`, `ask_user` and `steer` reach the owner. A bundle that
|
|
83
|
+
acts on a turn's text (a prompt rewrite, a fetch of what it names) should check `author:` first: the text may be a
|
|
84
|
+
participant's. The review boundary checks a tool call after the hooks, on the call they leave.
|
|
85
|
+
|
|
77
86
|
## What a hook can do: the runtime
|
|
78
87
|
|
|
79
88
|
Besides reading (and, on `:before_tool_call`, voting on) its event, a hook
|