samagotchi 0.103.0 → 0.105.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 +76 -1
- data/bin/chi +39 -2
- data/docs/architecture.md +36 -24
- data/docs/cli.md +97 -21
- data/docs/configuration.md +1 -1
- data/docs/guardrails.md +15 -1
- data/docs/internals/anthropic.md +2 -1
- data/docs/internals/context-telemetry.md +6 -2
- data/docs/internals/engine.md +22 -1
- data/docs/internals/participants.md +167 -0
- data/docs/internals/prompt-caching.md +8 -0
- data/docs/internals/remote-sessions.md +153 -27
- data/docs/internals/working-set.md +4 -3
- data/docs/plugins.md +7 -1
- data/docs/sessions.md +4 -2
- data/docs/sub-agent.md +8 -0
- data/docs/testing.md +89 -4
- data/lib/samagotchi/bridge/turn_accumulator.rb +2 -1
- data/lib/samagotchi/bridge.rb +17 -10
- data/lib/samagotchi/bridge_client/event_stream.rb +9 -4
- data/lib/samagotchi/bridge_client.rb +4 -1
- data/lib/samagotchi/bundles/system/manifest.yml +1 -1
- data/lib/samagotchi/chi_invocation.rb +13 -16
- data/lib/samagotchi/config.rb +1 -1
- data/lib/samagotchi/context_status.rb +15 -2
- data/lib/samagotchi/engine.rb +52 -8
- data/lib/samagotchi/guardrail_wiring.rb +7 -1
- data/lib/samagotchi/idle_recap.rb +1 -1
- data/lib/samagotchi/idle_scheduler.rb +34 -9
- data/lib/samagotchi/kernel_loop.rb +8 -1
- data/lib/samagotchi/last_turn.rb +5 -2
- data/lib/samagotchi/launch_mode.rb +4 -0
- data/lib/samagotchi/llm/chat_loop.rb +9 -1
- data/lib/samagotchi/owner_lock.rb +65 -5
- data/lib/samagotchi/participant_name.rb +21 -0
- data/lib/samagotchi/participants.rb +122 -0
- data/lib/samagotchi/queued_command.rb +9 -5
- data/lib/samagotchi/remote_attach_command.rb +119 -0
- data/lib/samagotchi/run_options.rb +14 -2
- data/lib/samagotchi/send_command.rb +1 -1
- data/lib/samagotchi/session.rb +8 -4
- data/lib/samagotchi/session_access/contract.rb +42 -4
- data/lib/samagotchi/session_access/dispatch.rb +29 -10
- data/lib/samagotchi/session_access/local.rb +52 -1
- data/lib/samagotchi/session_access/remote.rb +112 -16
- data/lib/samagotchi/session_access/web_bridge_client.rb +60 -3
- data/lib/samagotchi/session_access.rb +85 -3
- data/lib/samagotchi/session_continue.rb +1 -1
- data/lib/samagotchi/session_inbox.rb +18 -9
- data/lib/samagotchi/session_manager.rb +21 -5
- data/lib/samagotchi/session_metrics.rb +7 -1
- data/lib/samagotchi/session_participants.rb +59 -0
- data/lib/samagotchi/sessions_command.rb +14 -11
- data/lib/samagotchi/share_command.rb +174 -0
- data/lib/samagotchi/steer.rb +83 -10
- data/lib/samagotchi/system_prompt.rb +21 -2
- data/lib/samagotchi/terminal_ui/attach_launcher.rb +17 -4
- data/lib/samagotchi/terminal_ui/attach_link.rb +10 -0
- data/lib/samagotchi/terminal_ui/attached_loop.rb +117 -29
- data/lib/samagotchi/terminal_ui/formatting.rb +13 -1
- data/lib/samagotchi/terminal_ui/local_link.rb +5 -0
- data/lib/samagotchi/terminal_ui/reline_seam.rb +21 -1
- data/lib/samagotchi/terminal_ui/remote_connector.rb +66 -0
- data/lib/samagotchi/terminal_ui/remote_link.rb +245 -0
- data/lib/samagotchi/terminal_ui/version_lines.rb +23 -0
- data/lib/samagotchi/terminal_ui.rb +4 -1
- data/lib/samagotchi/testing/waiting.rb +92 -9
- data/lib/samagotchi/tools/task_runtime.rb +17 -3
- data/lib/samagotchi/turn_checkpoint.rb +5 -2
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +187 -74
- data/lib/samagotchi/web/grants.rb +303 -0
- data/lib/samagotchi/web/participant_access.rb +286 -0
- data/lib/samagotchi/web/principal.rb +29 -0
- data/lib/samagotchi/web/public/index.html +6 -5
- data/lib/samagotchi/web/public/working_set.js +6 -4
- data/lib/samagotchi/web/public/working_set_view.js +1 -1
- data/lib/samagotchi/web/server.rb +3 -1
- data/lib/samagotchi/web/session_access_routes.rb +120 -4
- data/lib/samagotchi/worker.rb +44 -11
- data/lib/samagotchi/worker_inbound.rb +5 -2
- metadata +12 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e0920f477e95206617da7d0d43e39dc981b071b34f456c60a7b669e4836875be
|
|
4
|
+
data.tar.gz: 1f9219bb12b92c2178ab15d57fddfaaee3654972d9b90e2ce11b8265ddab90cf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5c7625cff1523fe72e997ec3265465f9385b7a7fe59e8a3f6b097f8015e84f1b32441582218c75b4145f2f8ffb2b58ea7909c30f65efd4dbac114650c4b65f55
|
|
7
|
+
data.tar.gz: 3f2afdc87fa1a48efebbc14138016f2da28502e74b3a69d59ddd2c1ae5c37041af2192a0a071cf78325a14c2f630afe28e42cfed03b625f103fbf88f2037533c
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,79 @@ and commands may change between minor versions. How releases are made:
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.105.0] - 2026-10-23
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Web: the stage view's **working set** is on by default (no longer experimental). Beside the running turn a rail
|
|
16
|
+
shows the files, memories and URLs it brought into the model's context; each finished turn gets a footprint line;
|
|
17
|
+
the session bar's `● set N` chip (in place of the mem chip) opens the session's list, where rows say whether the
|
|
18
|
+
model still has them (stale, forgotten, dropped) and offer drop, pin, add and refresh. Turn it off with
|
|
19
|
+
`web.working_set: false`, `chi web --no-web-working-set` or `?working_set=0` (the mem chip comes back). See
|
|
20
|
+
[docs/cli.md](docs/cli.md#the-working-set).
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- `chi --remote NAME sessions list` shows test sessions, marked `[test]`, as the local list does.
|
|
25
|
+
- The attached terminal echoes a command typed after `/unqueue`'s text (`fix it/unqueue`) as the command alone, not the
|
|
26
|
+
whole line.
|
|
27
|
+
- Attaching to a session whose last turn was stopped or canceled shows the turn's canceled line, not the text it
|
|
28
|
+
streamed before the stop as if it were the answer.
|
|
29
|
+
|
|
30
|
+
Update with `chi update`.
|
|
31
|
+
|
|
32
|
+
## [0.104.0] - 2026-10-23
|
|
33
|
+
|
|
34
|
+
### Added
|
|
35
|
+
|
|
36
|
+
- Remote attach: `chi --remote NAME --attach ID` (and `--resume ID`, `-p TEXT`, or `--dir DIR` for a new session
|
|
37
|
+
there, that machine's absolute path) runs the attached TUI on another machine's session, through its chi web:
|
|
38
|
+
the same lines and exit codes, every printed command spelled with `--remote NAME`, `@path` images from this
|
|
39
|
+
machine, a "(reconnecting…)" / "(reconnected)" line for about a minute of reconnects, and the version line naming
|
|
40
|
+
both machines. `--non-interactive`, `--verbose`, `--no-shared`, `--memory`, `--mute` and
|
|
41
|
+
`--append-system-prompt` stay this machine's (exit 2). `--dir` without `--remote` exits 2. `/exit`,
|
|
42
|
+
`/exit --delete`, `/archive`, `/stats` and `/recap` work through a remote as here, the status row counts the
|
|
43
|
+
session's delegates there, and a join shows the steps' durations and a failed turn's line from that machine's
|
|
44
|
+
saved records. See [docs/cli.md](docs/cli.md#remote-sessions).
|
|
45
|
+
- Participants (experimental: identity only, the review itself comes in a later release): `chi share ID --name NAME
|
|
46
|
+
[--agent] [--expires 3d]` invites someone, or an agent, to review a session through chi web on the LAN with a link
|
|
47
|
+
of their own, printed once with a QR code (chi keeps only a digest of its token). `chi share ls [ID]` lists
|
|
48
|
+
participants and `chi share revoke ID [P] | --all` ends links; all three work through `--remote`. A link works for 7
|
|
49
|
+
days (1 hour to 30 days), until it is revoked, or until the session is archived or deleted. For now it opens a page
|
|
50
|
+
with the session's title and status; every other route answers it 403, and another session 404. chi web refuses to
|
|
51
|
+
start when another user can read its grants store. Through `--remote`, a revoked, expired or participant's token
|
|
52
|
+
gets its own message instead of "the token was refused". See [docs/cli.md](docs/cli.md#participants).
|
|
53
|
+
- The bundle test kit (experimental) waits with `wait_for(what) { … }`, which raises
|
|
54
|
+
`Samagotchi::Testing::Waiting::TimedOut` naming what it waited for (and, with `seen:`, what it last saw), plus
|
|
55
|
+
`join!` and `pop!` with the same message. Every helper deadline is multiplied by `SPEC_TIME_SCALE` (default 1).
|
|
56
|
+
See [docs/plugins.md](docs/plugins.md#testing-a-bundle-experimental).
|
|
57
|
+
|
|
58
|
+
### Changed
|
|
59
|
+
|
|
60
|
+
- The bundle test kit's `wait_until!` is renamed `wait_for`, with no alias: a bundle spec that calls
|
|
61
|
+
`wait_until!` changes the name (same arguments).
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
|
|
65
|
+
- `chi --attach ID` (and `--resume ID`, and `chi --remote NAME --attach ID`) right after `/exit`, while the leaving
|
|
66
|
+
worker still writes its recap, waits for it with `(waiting for the session to finish its recap…)` (up to
|
|
67
|
+
`recap.timeout` plus 5 s) and then attaches, instead of failing "did not start its Bridge in time". A Ctrl-C while
|
|
68
|
+
an attach connects exits 130 without a backtrace.
|
|
69
|
+
- `/queue TEXT` (or `/cut TEXT`) with no turn running says `(nothing is running: sent now)` in the REPL and the
|
|
70
|
+
attached terminal, instead of sending without a word.
|
|
71
|
+
- The ctx % no longer dips at the start of each turn on a chat host (`api: openai` / `anthropic`): the turn's first
|
|
72
|
+
estimate, made before the host counts anything, left out the tool schemas; it now uses the last count's chars per
|
|
73
|
+
token.
|
|
74
|
+
- Attached terminal: a command typed right after the text `/unqueue` put back in the input (`/unqueue` again,
|
|
75
|
+
`/queue TEXT`) runs as the command, with the text kept in the input, instead of going out joined to the text as
|
|
76
|
+
one prompt.
|
|
77
|
+
- `chi --remote NAME sessions stop` waits up to 10 s there for the worker to let go, as `chi sessions stop` does
|
|
78
|
+
here (chi web waited 2 s, so a slow worker read "still shutting down" more often); the page's Stop still waits 2 s.
|
|
79
|
+
- The terminal's status line, cut to the terminal's width, ends at a word with `…` instead of mid-word (in a pipe
|
|
80
|
+
too).
|
|
81
|
+
|
|
82
|
+
Update with `chi update`.
|
|
83
|
+
|
|
11
84
|
## [0.103.0] - 2026-10-23
|
|
12
85
|
|
|
13
86
|
### Added
|
|
@@ -3892,7 +3965,9 @@ and long-lived sessions.
|
|
|
3892
3965
|
- A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
|
|
3893
3966
|
hotkey panel that send selected text or the clipboard to your sessions.
|
|
3894
3967
|
|
|
3895
|
-
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.
|
|
3968
|
+
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.105.0...HEAD
|
|
3969
|
+
[0.105.0]: https://github.com/dm1try/samagotchi/compare/v0.104.0...v0.105.0
|
|
3970
|
+
[0.104.0]: https://github.com/dm1try/samagotchi/compare/v0.103.0...v0.104.0
|
|
3896
3971
|
[0.103.0]: https://github.com/dm1try/samagotchi/compare/v0.102.0...v0.103.0
|
|
3897
3972
|
[0.102.0]: https://github.com/dm1try/samagotchi/compare/v0.101.0...v0.102.0
|
|
3898
3973
|
[0.101.0]: https://github.com/dm1try/samagotchi/compare/v0.100.1...v0.101.0
|
data/bin/chi
CHANGED
|
@@ -52,6 +52,8 @@ SUBCOMMANDS = {
|
|
|
52
52
|
"send" => ["samagotchi/send_command", "SendCommand"],
|
|
53
53
|
# answer the question a session waits on, then wait (--question, --option)
|
|
54
54
|
"answer" => ["samagotchi/answer_command", "AnswerCommand"],
|
|
55
|
+
# invite participants to review a session, list them, revoke them
|
|
56
|
+
"share" => ["samagotchi/share_command", "ShareCommand"],
|
|
55
57
|
# find a model server and write config.yml (--model, --name)
|
|
56
58
|
"bootstrap" => ["samagotchi/bootstrap_command", "BootstrapCommand"],
|
|
57
59
|
# bring an installed chi up to date
|
|
@@ -69,7 +71,10 @@ SUBCOMMANDS = {
|
|
|
69
71
|
}.freeze
|
|
70
72
|
# chi --remote NAME <command> (or CHI_REMOTE=NAME): the session commands
|
|
71
73
|
# against another machine's chi web; what runs here only is refused (exit 2).
|
|
72
|
-
# chi remote, --version and --help run here as always.
|
|
74
|
+
# chi remote, --version and --help run here as always. Run flags under a
|
|
75
|
+
# remote (--attach, --resume, -p, --dir: a session there in this terminal)
|
|
76
|
+
# are parsed below and go to RemoteAttachCommand before this machine's
|
|
77
|
+
# checks; the local-only ones are refused here.
|
|
73
78
|
require "samagotchi/session_access/dispatch"
|
|
74
79
|
begin
|
|
75
80
|
remote_run = Samagotchi::SessionAccess::Dispatch.from(ARGV)
|
|
@@ -78,7 +83,12 @@ rescue Samagotchi::SessionAccess::Dispatch::Usage => e
|
|
|
78
83
|
exit 2
|
|
79
84
|
end
|
|
80
85
|
ARGV.replace(remote_run.argv)
|
|
81
|
-
|
|
86
|
+
remote_attach = remote_run.remote? && !remote_run.here? && remote_run.run_flags?
|
|
87
|
+
if remote_attach && (refusal = remote_run.refusal)
|
|
88
|
+
warn "Error: #{refusal}"
|
|
89
|
+
exit 2
|
|
90
|
+
end
|
|
91
|
+
exit remote_run.run(SUBCOMMANDS) if remote_run.remote? && !remote_run.here? && !remote_attach
|
|
82
92
|
|
|
83
93
|
if (path, klass = SUBCOMMANDS[ARGV[0]])
|
|
84
94
|
require path
|
|
@@ -152,6 +162,29 @@ Samagotchi::Config.reload!(cli_overrides: cli_overrides) unless cli_overrides.em
|
|
|
152
162
|
require "samagotchi/log"
|
|
153
163
|
Samagotchi::Log.configure(level: options[:verbose] ? :debug : nil, mirror: options[:verbose])
|
|
154
164
|
|
|
165
|
+
# chi --remote NAME --attach ID (--resume ID, --dir DIR, -p TEXT): the
|
|
166
|
+
# attached TUI on that machine's session. Nothing below runs: it checks
|
|
167
|
+
# this machine's sessions, memories and files.
|
|
168
|
+
if remote_attach
|
|
169
|
+
unless ARGV.empty?
|
|
170
|
+
warn "Error: unknown command #{ARGV.first} (see chi --help)"
|
|
171
|
+
exit 1
|
|
172
|
+
end
|
|
173
|
+
require "samagotchi/remote_attach_command"
|
|
174
|
+
begin
|
|
175
|
+
entry = Samagotchi::Remotes.find(Samagotchi::Remotes.check_name!(remote_run.remote))
|
|
176
|
+
rescue Samagotchi::Remotes::Invalid => e
|
|
177
|
+
warn "Error: #{e.message}"
|
|
178
|
+
exit 2
|
|
179
|
+
end
|
|
180
|
+
exit Samagotchi::RemoteAttachCommand.new(options, access: Samagotchi::SessionAccess::Remote.new(entry)).run
|
|
181
|
+
end
|
|
182
|
+
# --dir names a folder on a remote; here the session starts in this one.
|
|
183
|
+
if options[:dir]
|
|
184
|
+
warn "Error: --dir goes with --remote; cd there instead"
|
|
185
|
+
exit 2
|
|
186
|
+
end
|
|
187
|
+
|
|
155
188
|
# `chi web`: its flags (--port, --open, --web-view…) went through the parser above.
|
|
156
189
|
options[:web] = true if web
|
|
157
190
|
|
|
@@ -340,6 +373,10 @@ if launch == :attached
|
|
|
340
373
|
rescue Samagotchi::TerminalUI::AttachLauncher::Error => e
|
|
341
374
|
warn "Error: #{e.message}"
|
|
342
375
|
exit 1
|
|
376
|
+
rescue Interrupt
|
|
377
|
+
# Ctrl-C while it connects (a wait for a leaving worker, a late Bridge):
|
|
378
|
+
# no backtrace.
|
|
379
|
+
exit Samagotchi::CLI::Exit::INTERRUPTED
|
|
343
380
|
end
|
|
344
381
|
exit Samagotchi::TerminalUI::AttachLauncher.exit_status(ended)
|
|
345
382
|
end
|
data/docs/architecture.md
CHANGED
|
@@ -469,7 +469,8 @@ Every route goes through `Bridge#dispatch`: an id other than the bridge's own se
|
|
|
469
469
|
The per-session port is OS-assigned (bound to `0`) and published to a `bridge.json` sidecar
|
|
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
|
-
`GET /api/sessions/:id/output`)
|
|
472
|
+
`GET /api/sessions/:id/output`), as a byte pipe for the owner: `App#proxy_stream_body` gives `ProxyStreamBody` the
|
|
473
|
+
request principal's frame filter (`App#stream_filter`, nil for the owner, the only principal today). Resume/ring-buffer state is **in-memory**, per worker: a cursor from
|
|
473
474
|
an earlier worker gets the `reset` snapshot, not a replay.
|
|
474
475
|
|
|
475
476
|
**Session hub.** The cross-session layer (which sessions exist, who owns them, what changed)
|
|
@@ -510,35 +511,46 @@ chi web (the remote-sessions plan; `SessionAccess::Contract` says which commands
|
|
|
510
511
|
cover most of it; `Web::SessionAccessRoutes` (`web/session_access_routes.rb`, in `App`'s `ROUTES`) adds the rest, each
|
|
511
512
|
calling the `SessionAccess::Local` operation of the same name, so a remote backend maps 1:1 onto them. Every one is
|
|
512
513
|
token-gated like the rest (LAN mode: `Authorization: Bearer`), and one that takes an id is a `:session` route (an id
|
|
513
|
-
that isn't one, or no such session: 404 `not_found`).
|
|
514
|
+
that isn't one, or no such session: 404 `not_found`). Every `ROUTES` row also names who may use it, checked against
|
|
515
|
+
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 three: `GET /`, `GET /api/info` and `GET /api/sessions/:id`,
|
|
517
|
+
each answered as theirs; every other route is `owner` (Permission below, and the participants routes in
|
|
518
|
+
[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
|
|
514
519
|
model: [internals/remote-sessions.md](internals/remote-sessions.md).
|
|
515
520
|
|
|
516
|
-
| Route | Body / params | Answer | Refusals |
|
|
517
|
-
|
|
518
|
-
| `GET /api/sessions/resolve?id=` | an id or a unique prefix | 200 `{id}` | 404 `not_found`; 400 `ambiguous_id` with `ids` |
|
|
519
|
-
| `GET /api/sessions/summaries` | `dir` (a folder: its project; none: every project), `cwd`, `live=1`, `archived=1`, `tests=0`, `sort`, `order`, `limit` | 200: the rows `chi sessions list` prints (`SessionManager.session_summaries`: `status`, `live`, `busy`, `owner`, `recap`, `ctx_pct`, `waiting`, `waiting_id`, `title`, `preview`, …) | 400 `invalid_dir`, `invalid_cwd` |
|
|
520
|
-
| `POST /api/sessions` (more fields) | `toolkit`, `tools`, `no_tools`, `parent_answers`; `client_id` (`cli:send`/`cli:answer`: the first line is checked as chi send checks it); `check_vision: true` (images follow) | 201 adds `model_warning` and `parent_answers_warning` (a level looser than config.yml's, capped) | 400 `invalid_toolkit`, `invalid_parent_answers`, `invalid_continues` (beside `continues`); 403 `parent_answers_refused`, `parent_continue_refused`; 422 `no_vision` |
|
|
521
|
-
| `POST /api/sessions/:id/turn` (more fields) | `client_id` as above; `check_vision: true` with `images` | as before | 403 `parent_answers_refused`, `parent_continue_refused`; 422 `no_vision` |
|
|
522
|
-
| `POST /api/sessions/:id/wait/prepare` | `question_id` (absent: the pending one; `null`: none), `turn`, `created`, `busy` (+ `queue`) | 200 `{session_id, cursor, baseline: {messages, question_id, last_turn}, own_turn, known_turn, running, live, settled, last_turn_id}` (`WaitStart`) | 409 `busy`, `waiting` (`SendCommand::Busy`'s words, and its fields: `question`, `parent_strategy`) |
|
|
523
|
-
| `POST /api/sessions/:id/wait` | `cursor`, `baseline`, `turn`, `queued`, `ended_before`, `owner_grace`, `timeout` (capped at 30 s) | 200: `ReplyWait::Result`'s fields (`status`: `done`, `waiting_for_answer`, `no_reply`, `error`, `stopped`, `worker_gone`, or `timeout`: ask again; `text`, `file`, `question` as `PendingQuestion#to_file`, `outcome`, `limit`, `error_kind`, `retryable`, `cancel_reason`, `stopped_by`, `kept_steps`, `turn_id`, `origin`; never the session) and `parent_strategy` `{name, allowed, origin}` (`ParentReport.strategy_for`, this server's config.yml) | 400 `invalid_wait`; 503 `server_stopping` |
|
|
524
|
-
| `POST /api/sessions/:id/notes` | `text`, `source` (default `cli`, never `broadcast`) | 200 `{status: queued \| waits, session_id, queued}` (`NoteDelivery`) | 409 `owned_by_tui` (`status: refused`); 400 `note_rejected`, `missing_fields`, `invalid_source` |
|
|
525
|
-
| `POST /api/sessions/:id/context` (more bodies) | `{name, push: true, why, hint}` or `{name, cmd, every, why, hint}` beside the page's `{url, why, hint}`; `added_by` `cli`/`agent` (else `web`) | 201 `{status: attached, name}` | 400 `invalid_source`; 409 `exists` |
|
|
526
|
-
| `POST /api/sessions/:id/context/:name/push` | `text`, `summary`, `wake`, `hint` (a source's JSON output, checked the same way) | 200 `{status: pushed, name, before, revision, changed}` | 404 `not_found`; 409 `not_push`; 400 `invalid_text` |
|
|
527
|
-
| `DELETE /api/sessions/:id/context/:name?own=1` | | 200 `{status: detached}`: the session's own only | 409 `project_source` (chi context rm's words); 404 |
|
|
528
|
-
| `POST /api/sessions/:id/stop` | | 200 `{status: stopped, session_id, released}`: `released`, whether the worker let go within `STOP_WAIT_SECONDS
|
|
529
|
-
| `DELETE /api/sessions/:id?force=` | `force=true` stops a live worker first (the page sends it) | as before | 409 `worker_running` without it |
|
|
530
|
-
| `GET /api/sessions/:id?brief=1` | | 200 `{brief, session: {id, status (the file's), title, display_title, project_root, working_directory, model_name, model_typed, parent_answers, owner}, pending_question}` (the file's; no messages): `Remote#show` | 404 |
|
|
531
|
-
| `POST /api/sessions/:id/turn` (its 202) | | the ack with `via`: `bridge` or `file` (the input file: a remote `chi send --wait` follows that turn) | |
|
|
532
|
-
| `GET /api/sessions/:id/context?full=1`, `…/context/:name?full=1` | | each row with `source` (`Source#to_h`, its command too; the page's rows never carry one), `snapshot` (`Snapshot#to_h`, no text) and `shadowed`, shadowed project sources included | |
|
|
533
|
-
| `POST /api/sessions/:id/wake` | `wait` (seconds, default and most 10: `WAKE_MAX_WAIT`) | 200 `{session_id, woke, worker: {epoch, started_at, version, features}, model_name, default_model, parent_strategy, installed}`: the live worker as is (`woke: false`), else resumed and its Bridge waited for (`Local#wake`); `default_model` and `installed` are this machine's | 409 `owned_by_tui`, `scratch` (`LaunchMode::SCRATCH_LEFTOVER`); 504 `worker_timeout` (`LaunchMode::BRIDGE_LATE`); 400 `invalid_wait`; 404 |
|
|
534
|
-
| `GET /api/sessions/:id/worker` | | 200 `{session_id, live, stopped, owner: worker \| tui \| null, worker: {epoch, started_at, version, features} \| null, parent_strategy}` (`Local#worker`; never starts one) | 404 |
|
|
535
|
-
| `POST /api/sessions/:id/
|
|
536
|
-
| `POST /api/sessions/:id/
|
|
521
|
+
| Route | Permission | Body / params | Answer | Refusals |
|
|
522
|
+
|---|---|---|---|---|
|
|
523
|
+
| `GET /api/sessions/resolve?id=` | owner | an id or a unique prefix | 200 `{id}` | 404 `not_found`; 400 `ambiguous_id` with `ids` |
|
|
524
|
+
| `GET /api/sessions/summaries` | owner | `dir` (a folder: its project; none: every project), `cwd`, `live=1`, `archived=1`, `tests=0`, `sort`, `order`, `limit` | 200: the rows `chi sessions list` prints (`SessionManager.session_summaries`: `status`, `live`, `busy`, `owner`, `recap`, `ctx_pct`, `waiting`, `waiting_id`, `title`, `preview`, …) | 400 `invalid_dir`, `invalid_cwd` |
|
|
525
|
+
| `POST /api/sessions` (more fields) | owner | `toolkit`, `tools`, `no_tools`, `parent_answers`; `client_id` (`cli:send`/`cli:answer`: the first line is checked as chi send checks it); `check_vision: true` (images follow) | 201 adds `model_warning` and `parent_answers_warning` (a level looser than config.yml's, capped) | 400 `invalid_toolkit`, `invalid_parent_answers`, `invalid_continues` (beside `continues`); 403 `parent_answers_refused`, `parent_continue_refused`; 422 `no_vision` |
|
|
526
|
+
| `POST /api/sessions/:id/turn` (more fields) | owner | `client_id` as above; `check_vision: true` with `images` | as before | 403 `parent_answers_refused`, `parent_continue_refused`; 422 `no_vision` |
|
|
527
|
+
| `POST /api/sessions/:id/wait/prepare` | owner | `question_id` (absent: the pending one; `null`: none), `turn`, `created`, `busy` (+ `queue`) | 200 `{session_id, cursor, baseline: {messages, question_id, last_turn}, own_turn, known_turn, running, live, settled, last_turn_id}` (`WaitStart`) | 409 `busy`, `waiting` (`SendCommand::Busy`'s words, and its fields: `question`, `parent_strategy`) |
|
|
528
|
+
| `POST /api/sessions/:id/wait` | owner | `cursor`, `baseline`, `turn`, `queued`, `ended_before`, `owner_grace`, `timeout` (capped at 30 s) | 200: `ReplyWait::Result`'s fields (`status`: `done`, `waiting_for_answer`, `no_reply`, `error`, `stopped`, `worker_gone`, or `timeout`: ask again; `text`, `file`, `question` as `PendingQuestion#to_file`, `outcome`, `limit`, `error_kind`, `retryable`, `cancel_reason`, `stopped_by`, `kept_steps`, `turn_id`, `origin`; never the session) and `parent_strategy` `{name, allowed, origin}` (`ParentReport.strategy_for`, this server's config.yml) | 400 `invalid_wait`; 503 `server_stopping` |
|
|
529
|
+
| `POST /api/sessions/:id/notes` | owner | `text`, `source` (default `cli`, never `broadcast`) | 200 `{status: queued \| waits, session_id, queued}` (`NoteDelivery`) | 409 `owned_by_tui` (`status: refused`); 400 `note_rejected`, `missing_fields`, `invalid_source` |
|
|
530
|
+
| `POST /api/sessions/:id/context` (more bodies) | owner | `{name, push: true, why, hint}` or `{name, cmd, every, why, hint}` beside the page's `{url, why, hint}`; `added_by` `cli`/`agent` (else `web`) | 201 `{status: attached, name}` | 400 `invalid_source`; 409 `exists` |
|
|
531
|
+
| `POST /api/sessions/:id/context/:name/push` | owner | `text`, `summary`, `wake`, `hint` (a source's JSON output, checked the same way) | 200 `{status: pushed, name, before, revision, changed}` | 404 `not_found`; 409 `not_push`; 400 `invalid_text` |
|
|
532
|
+
| `DELETE /api/sessions/:id/context/:name?own=1` | owner | | 200 `{status: detached}`: the session's own only | 409 `project_source` (chi context rm's words); 404 |
|
|
533
|
+
| `POST /api/sessions/:id/stop` | owner | `wait?` (seconds, capped at `SessionManager::STOP_WAIT`, 10: `chi --remote NAME sessions stop` sends it) | 200 `{status: stopped, session_id, released}`: `released`, whether the worker let go within the wait (without one `STOP_WAIT_SECONDS`, 2 s: the page's Stop) | 409 `owned_by_tui` |
|
|
534
|
+
| `DELETE /api/sessions/:id?force=` | owner | `force=true` stops a live worker first (the page sends it) | as before | 409 `worker_running` without it |
|
|
535
|
+
| `GET /api/sessions/:id?brief=1` | participant (theirs: title, status, participants) | | 200 `{brief, session: {id, status (the file's), title, display_title, project_root, working_directory, model_name, model_typed, parent_answers, owner}, pending_question}` (the file's; no messages): `Remote#show` | 404 |
|
|
536
|
+
| `POST /api/sessions/:id/turn` (its 202) | owner | | the ack with `via`: `bridge` or `file` (the input file: a remote `chi send --wait` follows that turn) | |
|
|
537
|
+
| `GET /api/sessions/:id/context?full=1`, `…/context/:name?full=1` | owner | | each row with `source` (`Source#to_h`, its command too; the page's rows never carry one), `snapshot` (`Snapshot#to_h`, no text) and `shadowed`, shadowed project sources included | |
|
|
538
|
+
| `POST /api/sessions/:id/wake` | owner | `wait` (seconds, default and most 10: `WAKE_MAX_WAIT`) | 200 `{session_id, woke, worker: {epoch, started_at, version, features}, model_name, default_model, parent_strategy, installed}`: the live worker as is (`woke: false`), else resumed and its Bridge waited for (`Local#wake`); `default_model` and `installed` are this machine's | 409 `owned_by_tui`, `scratch` (`LaunchMode::SCRATCH_LEFTOVER`); 504 `worker_timeout` (`LaunchMode::BRIDGE_LATE`); 400 `invalid_wait`; 404 |
|
|
539
|
+
| `GET /api/sessions/:id/worker` | owner | | 200 `{session_id, live, stopped, owner: worker \| tui \| null, worker: {epoch, started_at, version, features} \| null, default_model, parent_strategy}` (`Local#worker`; never starts one; `default_model` is this machine's) | 404 |
|
|
540
|
+
| `POST /api/sessions/:id/exit` | owner | `client_id` (passed on only when it is an attached TUI's, `tui:…`; any other is dropped), `delete` | 200 the worker's `{status: exiting, session_id, discard}` (`BridgeClient#request_exit`; never wakes one) | 409 `held` with the worker's `{status, reason, tasks, hint}`; 503 `not_live`; 501 `not_supported`; 504 `worker_timeout` |
|
|
541
|
+
| `POST /api/sessions/:id/recap` | owner | | 200 the worker's `{enabled, saved, request, min_user_turns, session_off}` (`BridgeClient#request_recap`) | 503, 501, 504 as every relay |
|
|
542
|
+
| `GET /api/sessions/:id/stats` | owner | | 200 `{session_id, metrics}`: the worker's stats, its state's metrics for a worker from before the stats route | 503 `not_live`; 504 `worker_timeout`; 502 `bridge_error` |
|
|
543
|
+
| `GET /api/sessions/:id/children/counts` | owner | | 200 `{running, waiting, unreported}` (`ChildrenStatus.counts`, the delegates' files here) | 404 |
|
|
544
|
+
| `GET /api/sessions/:id/turns/:turn_id/records` | owner | | 200 `{tool_records: […], turn_record: {…} \| null}` (`SessionMetrics.saved_tool_records` / `saved_turn_record`, as saved) | 400 `invalid_turn_id`; 404 |
|
|
545
|
+
| `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
|
+
| `POST /api/sessions/:id/command` (its 501) | owner | | | 501 `not_supported` for a builtin the worker lacks adds `command` (`"/title"`) and `message` |
|
|
547
|
+
| `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 |
|
|
537
548
|
|
|
538
549
|
`GET /api/info` names the feature `session_commands` (`SessionAccessRoutes::FEATURE`): `SessionAccess::Remote` asks it
|
|
539
550
|
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
|
|
540
551
|
old for this command through --remote: update chi there (chi update)". The feature `remote_attach`
|
|
541
|
-
(`SessionAccessRoutes::ATTACH_FEATURE`) names the
|
|
552
|
+
(`SessionAccessRoutes::ATTACH_FEATURE`) names the routes a remote attached TUI needs: wake, worker, exit, recap, stats,
|
|
553
|
+
children counts and a turn's records. Their refusals carry `{error, detail, message}`.
|
|
542
554
|
|
|
543
555
|
`POST …/answer` passes on `client_id` only when it is chi answer's (`cli:answer`), so the worker holds the answer to
|
|
544
556
|
its `guardrails.parent_answers`; any other is dropped, so a forwarded id can only tighten. The worker's 403
|
data/docs/cli.md
CHANGED
|
@@ -15,14 +15,16 @@
|
|
|
15
15
|
- `chi web --web-host lan` — the Web UI on your home network too, for your phone: a link with an access token and its QR code (see [chi web on your phone](#chi-web-on-your-phone)); `chi web --new-token` replaces the token
|
|
16
16
|
- `chi web --web-markdown` — opt in to sanitized Markdown rendering for completed assistant messages, the stage's asking text and a resolved question card's reply
|
|
17
17
|
- `chi web --web-view turn` — draw turns with the turn view (each turn as one block of steps, the running one at the bottom of the history) instead of the default stage view (the running turn pinned above the composer); `?view=stage|turn` on the page URL overrides it (see [Web views](#web-views))
|
|
18
|
-
- `chi web --web-working-set` — the stage view's working set (
|
|
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
21
|
- `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
22
|
- `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
23
|
- `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
24
|
- `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 --
|
|
25
|
+
- `chi share ID --name NAME [--agent] [--expires 3d]`, `chi share ls [ID]`, `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), list them, revoke them (see [Participants](#participants))
|
|
26
|
+
- `chi --remote NAME send|answer|note|context|sessions|share …` (or `CHI_REMOTE=NAME`) — those commands on that machine's sessions, through its chi web (see [Remote sessions](#remote-sessions))
|
|
27
|
+
- `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))
|
|
26
28
|
- `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))
|
|
27
29
|
- `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))
|
|
28
30
|
- `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))
|
|
@@ -280,7 +282,8 @@ controls exit behavior (`--non-interactive`); `--resume` composes with both.
|
|
|
280
282
|
| `--shared` | Run the session (new, or `--resume`'s) in a background worker and attach to it: the default, and the way to get it when `session.shared` is off. See [Sharing a session](#sharing-a-session). |
|
|
281
283
|
| `--no-shared` | Run the plain in-process REPL for this run. |
|
|
282
284
|
| `--attach SESSION_ID` | Attach to a session's worker, waking one if it has exited. |
|
|
283
|
-
| `--remote NAME` | Before a session command (`chi --remote studio send …`): run it against that remote's sessions, through its chi web; beats `CHI_REMOTE`. Commands and flags that run on this machine only exit 2. See [Remote sessions](#remote-sessions). |
|
|
285
|
+
| `--remote NAME` | Before a session command (`chi --remote studio send …`) or the run flags (`chi --remote studio --attach ID`, `--resume ID`, `--dir DIR -p TEXT`): run it against that remote's sessions, through its chi web; beats `CHI_REMOTE`. Commands and flags that run on this machine only exit 2. See [Remote sessions](#remote-sessions). |
|
|
286
|
+
| `--dir DIR` | With `--remote`: a new session's folder on that machine, its absolute path (required for a new session there). Without `--remote` it exits 2: cd there instead. |
|
|
284
287
|
| `--model NAME` | Use this model for the run (overrides the configured default and a resumed session's model). |
|
|
285
288
|
| `--thinking LEVEL` | This session's own thinking level: `off`, `low`, `medium` or `high`, saved in the session and first in the order (`default` unsets it). On `--resume` or `--attach` it is set at once, through a `/thinking` on an attached worker. With `chi web` it is instead the default for the sessions its workers run, below a session's own (as env `SAMAGOTCHI_THINKING_LEVEL` is). See "Thinking" in configuration.md. |
|
|
286
289
|
| `--toolkit NAME`, `--tool NAME`, `--no-tool NAME` | A new session's toolkit, the tools its prompts declare: `full` (every built-in), `lean`, `child`, one of config's `toolkit.kits` or a bundle's (without `--toolkit`: config's `toolkit.default`), with tools declared on top of it (`--tool`) or left out (`--no-tool`); both repeatable, a comma list too. Saved in the session as given; a respawned worker, a fork and a continue keep it. Ignored with `--attach`/`--resume` (one line, as `--memory`); `/toolkit` changes it. Not on `chi web` (config's `toolkit.default` covers its sessions). See [Toolkits](configuration.md#toolkits). |
|
|
@@ -750,12 +753,14 @@ REPL alike:
|
|
|
750
753
|
with images works the same way: it waits for the turn's end, and the lines
|
|
751
754
|
sent after it still go in at the next step.
|
|
752
755
|
At the open prompt (no turn running) `/cut TEXT` and `/queue TEXT` run TEXT
|
|
753
|
-
as a normal prompt
|
|
756
|
+
as a normal prompt, and the terminals say so: `(nothing is running: sent now)`.
|
|
754
757
|
- `/unqueue` (attached terminal) takes back the newest line this terminal
|
|
755
758
|
queued that hasn't started: `removed: <its first 60 characters>`, and its text
|
|
756
759
|
goes back into the input to change and send again (not over typed text: then
|
|
757
760
|
it is in the input history, ↑; not with input from a pipe; its images don't
|
|
758
|
-
come back), then the one before it on the next `/unqueue
|
|
761
|
+
come back), then the one before it on the next `/unqueue`. A command typed
|
|
762
|
+
after that text without clearing it (`fix it/unqueue`, `fix it/queue more`)
|
|
763
|
+
runs as the command (the scrollback shows the command alone), and the text stays in the input. `(nothing queued: …)` when none waits,
|
|
759
764
|
`(not removed: it already started)` when its turn won the race, `(not removed:
|
|
760
765
|
it is already gone)` when another client removed it. Other terminals that
|
|
761
766
|
showed it say `(a queued message was removed)`; the web drops its bubble. A
|
|
@@ -1005,12 +1010,12 @@ made (up to 120 lines or 8 KB), live and after a reload (see
|
|
|
1005
1010
|
|
|
1006
1011
|
### The working set
|
|
1007
1012
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
turn view ignores it.
|
|
1013
|
+
The stage view shows what a turn brought into the model's context: the files
|
|
1014
|
+
it read, edited or wrote, the memories it read or wrote and the URLs it
|
|
1015
|
+
fetched. It is on by default; `web.working_set: false` (or `chi web
|
|
1016
|
+
--no-web-working-set`, `SAMAGOTCHI_WEB_WORKING_SET=false`; `?working_set=0` on
|
|
1017
|
+
the page URL for one page load, `?working_set=1` back on) turns it off, and the
|
|
1018
|
+
session bar shows the `mem N` chip again. The turn view ignores it.
|
|
1014
1019
|
|
|
1015
1020
|
- **The rail**, on the right of the stage's live slots: the running turn's set
|
|
1016
1021
|
in four lines, folded as it grows (files under one folder become
|
|
@@ -1022,8 +1027,10 @@ turn view ignores it.
|
|
|
1022
1027
|
on a phone it isn't shown.
|
|
1023
1028
|
- **The footprint**, one line under each finished turn's block, live and after
|
|
1024
1029
|
a reload: `touched …/web/public/ · 4 files · ◆ 2 memories s2–10 · +1 url ·
|
|
1025
|
-
edited 1
|
|
1026
|
-
|
|
1030
|
+
edited 1`, then `n stale`, `n forgotten` or `n dropped` while some are (a file
|
|
1031
|
+
changed after the turn read it, even at its very end, says so here). A step
|
|
1032
|
+
tag (`s2`) goes to that call's row; the rest opens the turn's list. A turn
|
|
1033
|
+
that touched nothing has none.
|
|
1027
1034
|
- **The `set N` chip** in the session bar, in place of the `mem N` chip (whose
|
|
1028
1035
|
tooltip goes too): the session's whole set, preloaded (`--memory`) and muted
|
|
1029
1036
|
(`--mute`) memories included; its dot lights when the set changed since you
|
|
@@ -1189,10 +1196,9 @@ CHI_REMOTE=studio chi context push claude-outbox ab12 < answer.json
|
|
|
1189
1196
|
list|stop|restart|archive|unarchive|delete`. The rest runs on this machine
|
|
1190
1197
|
only and exits 2 with `chi <command> runs on this machine only: run it on
|
|
1191
1198
|
<remote>`: `context refresh|mute|unmute` and `--project`, `sessions
|
|
1192
|
-
stats|prune|clean`, `broadcast
|
|
1193
|
-
`memory`, `update`, …)
|
|
1194
|
-
|
|
1195
|
-
`--help` always run here.
|
|
1199
|
+
stats|prune|clean`, `broadcast` and the machine's own commands (`bundle`,
|
|
1200
|
+
`memory`, `update`, …). `chi remote`, `--version` and `--help` always run
|
|
1201
|
+
here.
|
|
1196
1202
|
- `send --new` needs `--dir`, that machine's absolute path (no folder of this
|
|
1197
1203
|
one is guessed); `--toolkit`, `--set` and `--parent-answers` are checked
|
|
1198
1204
|
against that machine's config and bundles. `sessions list` lists every
|
|
@@ -1208,18 +1214,88 @@ CHI_REMOTE=studio chi context push claude-outbox ab12 < answer.json
|
|
|
1208
1214
|
`--timeout`; exit codes are the same. The commands chi prints for what to
|
|
1209
1215
|
do next work as pasted: they carry `--remote NAME` (`chi --remote studio
|
|
1210
1216
|
answer ab12 --question q1 --option N`, `chi --remote studio send --wait …`),
|
|
1211
|
-
under `CHI_REMOTE` too
|
|
1212
|
-
|
|
1213
|
-
(`chi web on studio shows it`), and `sessions stop` names no `chi
|
|
1214
|
-
--resume`.
|
|
1217
|
+
under `CHI_REMOTE` too, an attach as well (`chi --remote studio --attach
|
|
1218
|
+
ab12 shows it`, `sessions stop`'s `chi --remote studio --resume ab12`).
|
|
1215
1219
|
- A remote that can't be reached names itself and why (`chi send: remote
|
|
1216
1220
|
studio (http://192.168.1.29:4567): connection refused`, exit 1; with
|
|
1217
1221
|
`--format json` an `error` object).
|
|
1218
1222
|
- Plain http, the same as the phone's: use it on your home network.
|
|
1219
1223
|
|
|
1224
|
+
A session there in this terminal (the attached TUI, through that machine's
|
|
1225
|
+
chi web; its worker stays there):
|
|
1226
|
+
|
|
1227
|
+
```sh
|
|
1228
|
+
chi --remote studio --attach ab12 # attach (wakes its worker if it exited)
|
|
1229
|
+
chi --remote studio --resume ab12 -p "go on" # resume there, run one turn, stay attached
|
|
1230
|
+
chi --remote studio --dir /Users/me/app -p "hi" # a new session there (--dir required), one turn, stay
|
|
1231
|
+
```
|
|
1232
|
+
|
|
1233
|
+
- The same TUI and exit codes (0 detached, 3 a question left waiting with
|
|
1234
|
+
input from a pipe, 1 otherwise), every printed command with `--remote
|
|
1235
|
+
studio`. Prompts, session commands, `/model`, `!cmd` (a shell command **on
|
|
1236
|
+
that machine**, under its guardrails), `/queue`, `/cut`, `/unqueue`,
|
|
1237
|
+
`/detach`, `/exit`, `/exit --delete`, `/archive`, `/stats` and `/recap` work
|
|
1238
|
+
as here, on that machine's session (`/exit --delete` deletes it there).
|
|
1239
|
+
The status row's children segment counts its delegates there, and a join
|
|
1240
|
+
shows the steps' durations and a failed or canceled turn's line from its saved records
|
|
1241
|
+
there. A chi web too old for one of them says so in one line ("remote
|
|
1242
|
+
studio runs chi X, too old for this command through --remote: update chi
|
|
1243
|
+
there").
|
|
1244
|
+
- A new session needs `--dir`, that machine's absolute path; `--model`,
|
|
1245
|
+
`--thinking`, `--llm-context*`, `--toolkit`, `--tool`, `--no-tool`, `--set`
|
|
1246
|
+
and `--parent-answers` are checked there (exit 2 for a bad one). With
|
|
1247
|
+
`--attach`/`--resume` they work as here (`--model`, `--thinking`,
|
|
1248
|
+
`--llm-context*` change the session; the others are named as ignored).
|
|
1249
|
+
`--non-interactive` (use `chi --remote studio send --new --dir D -m TEXT
|
|
1250
|
+
--wait`), `--verbose`, `--no-shared`, `--memory`, `--mute` and
|
|
1251
|
+
`--append-system-prompt` run on this machine only (exit 2).
|
|
1252
|
+
- `@path` images are this machine's files (read and downscaled here, then
|
|
1253
|
+
uploaded); Tab completes `/` commands and `@path`, not memory names. The
|
|
1254
|
+
prompt history (↑) and `no_interrupt` / `no_default_input` are this
|
|
1255
|
+
terminal's.
|
|
1256
|
+
- When chi web can't be reached the TUI says "(lost the connection to
|
|
1257
|
+
studio; reconnecting…)" and tries for about a minute, then "(reconnected)";
|
|
1258
|
+
what it missed is replayed or resynced. A prompt typed meanwhile is usually
|
|
1259
|
+
accepted there and runs when a worker reads it. A worker that went away
|
|
1260
|
+
(not stopped) is woken again. The end is worded by why: "The session was
|
|
1261
|
+
stopped on studio. Resume it with: chi --remote studio --resume ab12", the
|
|
1262
|
+
token refused, chi web unreachable, the worker gone, the session deleted.
|
|
1263
|
+
- The version line names both machines: the worker older than what studio
|
|
1264
|
+
has installed (`chi --remote studio sessions restart ab12`), this terminal
|
|
1265
|
+
older than studio (`chi update` here).
|
|
1266
|
+
|
|
1220
1267
|
How it works inside (`SessionAccess`, chi web's routes, where each check runs):
|
|
1221
1268
|
[internals/remote-sessions.md](internals/remote-sessions.md).
|
|
1222
1269
|
|
|
1270
|
+
### Participants
|
|
1271
|
+
|
|
1272
|
+
A session's owner can invite **participants**, people or agents, to review it through chi web on the LAN
|
|
1273
|
+
(`web.host: lan`), each with a link of their own that reaches that one session:
|
|
1274
|
+
|
|
1275
|
+
```sh
|
|
1276
|
+
chi share 3f9a1c2e --name Alex
|
|
1277
|
+
# p_7c01d2aa Alex (human, reviewer) · session 3f9a1c2e · expires 2026-10-30 14:02
|
|
1278
|
+
# http://192.168.1.29:4567/?token=chp_…#s=3f9a1c2e-…
|
|
1279
|
+
# (shown once: chi keeps only its digest; chi share revoke 3f9a1c2e p_7c01d2aa ends it)
|
|
1280
|
+
chi share ls # every session's participants (--format json; never a token)
|
|
1281
|
+
chi share revoke 3f9a1c2e # every link to that session; add a participant id for one
|
|
1282
|
+
chi share revoke --all # every link to every session
|
|
1283
|
+
```
|
|
1284
|
+
|
|
1285
|
+
- 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
|
+
participant; opening it sets their browser's cookie, as the owner's LAN link does. `--agent` marks an agent
|
|
1287
|
+
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, or until the session is
|
|
1289
|
+
archived or deleted. A revoked or expired link shows "This review link has expired or was revoked."
|
|
1290
|
+
- For now a participant sees a page with the session's title and status; comments and the review itself come in
|
|
1291
|
+
a later chi. They can't reach anything else: every other route answers them 403, another session 404.
|
|
1292
|
+
- `chi --remote studio share …` invites on studio: the link names studio's LAN address. Locally, the link names
|
|
1293
|
+
this machine's chi web; without one on the LAN, chi says the link opens on this machine only.
|
|
1294
|
+
- The owner's own token and the `chi web --new-token` rotation are separate: rotating it ends no participant's link.
|
|
1295
|
+
|
|
1296
|
+
How it works inside (grants, the gate, the routes, the audit trail):
|
|
1297
|
+
[internals/participants.md](internals/participants.md).
|
|
1298
|
+
|
|
1223
1299
|
## Runtime Model Switch (Assist Mode)
|
|
1224
1300
|
|
|
1225
1301
|
In interactive assist mode, you can switch the request model without restarting:
|
data/docs/configuration.md
CHANGED
|
@@ -1716,7 +1716,7 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
|
|
|
1716
1716
|
| `web.host` | `127.0.0.1` | yes | `127.0.0.1`, `::1` or `localhost`; `lan` (this machine's private IPv4 address) or one of its IPv4 addresses also opens `chi web` to the network, with an access token (`chi web --new-token` replaces it). Anything else binds `127.0.0.1` with a warning. See [CLI: chi web on your phone](cli.md#chi-web-on-your-phone). |
|
|
1717
1717
|
| `web.markdown` | `false` | yes | Render answers as Markdown in `chi web`. |
|
|
1718
1718
|
| `web.view` | `stage` | yes | How `chi web` draws a turn: `stage` (the running turn pinned above the composer) or `turn` (one block per turn in the history). See [CLI](cli.md#web-views). |
|
|
1719
|
-
| `web.working_set` | `
|
|
1719
|
+
| `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
1720
|
| `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
1721
|
| `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). |
|
|
1722
1722
|
| `history.file` | state dir | | Prompt history path, shared by the TUI and the web composer. |
|
data/docs/guardrails.md
CHANGED
|
@@ -595,7 +595,8 @@ only by the user: these change a model's system prompt for later sessions (see
|
|
|
595
595
|
[Model notes](memory.md#model-notes); a shell command reaches them only under
|
|
596
596
|
`shell-touches-chi`'s text heuristic); and answering chi's
|
|
597
597
|
questions around `chi answer`: `chi --attach` or `chi -p` with stdin from a
|
|
598
|
-
pipe, a here-string or a file, not `/dev/null` (`chi-answer-piped
|
|
598
|
+
pipe, a here-string or a file, not `/dev/null` (`chi-answer-piped`, which also sees
|
|
599
|
+
`chi --remote NAME --attach|--resume|-p` and `CHI_REMOTE=NAME chi …`), and `curl`/`wget` to a
|
|
599
600
|
session's `/answer` route (`chi-answer-http`), both once or for the session, and only by the
|
|
600
601
|
user (a parent agent allowing one would answer as the user).
|
|
601
602
|
`chi answer` itself isn't asked about. It denies writes into `.git/hooks`.
|
|
@@ -690,6 +691,19 @@ else goes to chi's own rules. It needs nah installed; see its README for the set
|
|
|
690
691
|
- Every load failure is shown once, at the start of the first turn
|
|
691
692
|
(`guardrails> …` in the terminal, a red line on the web).
|
|
692
693
|
|
|
694
|
+
## Participants' text in later turns
|
|
695
|
+
|
|
696
|
+
A participant's message (a review contribution, see
|
|
697
|
+
[internals/participants.md](internals/participants.md#attribution)) stays in
|
|
698
|
+
the history, so later **owner** turns read it. chi marks it: the wire header
|
|
699
|
+
above it says it is a participant's and not the owner's instruction, and the
|
|
700
|
+
system prompt says the same while the session has participants. Owner turns
|
|
701
|
+
keep every guardrail. But an owner turn that says "do what Alex said" hands
|
|
702
|
+
Alex's text the owner's tools: the model will act on it as the owner asked.
|
|
703
|
+
That is the residual risk, documented rather than engineered away: on a
|
|
704
|
+
session with sensitive tools, quote the proposal you want applied in your own
|
|
705
|
+
turn instead.
|
|
706
|
+
|
|
693
707
|
## Limits
|
|
694
708
|
|
|
695
709
|
Text matching on shell commands stops accidents, not a model set on getting
|
data/docs/internals/anthropic.md
CHANGED
|
@@ -133,7 +133,8 @@ append-only within a process, so what changes the prefix is:
|
|
|
133
133
|
1. **A system-prompt rebuild** (`SystemPrompt#build` is cached per process and key; `PromptBuilder#reset!` drops it):
|
|
134
134
|
a new process (`--resume`, a respawn after `session.idle_exit_minutes`, a crash) after a memory index line, an
|
|
135
135
|
AGENTS.md, an appended file, a model note or a bundle memory changed; `/model` (the tail's `Model:` line, its
|
|
136
|
-
overlays and notes); `/thinking` (another key); a toolkit or profile change; a plugin's `tools_changed
|
|
136
|
+
overlays and notes); `/thinking` (another key); a toolkit or profile change; a plugin's `tools_changed!`; the
|
|
137
|
+
session's first participant joining or last leaving (the tail's participants line, participants.md).
|
|
137
138
|
2. **A tool set change**: the same triggers, plus MCP lines that differ on a respawn.
|
|
138
139
|
3. **History edits by design**: the `llm_context` `stale`/`forget` stubs, and the image cut moving a batch.
|
|
139
140
|
|
|
@@ -12,8 +12,12 @@ resumed session's line already names):
|
|
|
12
12
|
|
|
13
13
|
Both loops do this, before each request (`ContextStatus`, one per turn). The
|
|
14
14
|
chat loop's estimate counts the conversation's text and tool calls, not the
|
|
15
|
-
tool schemas it sends, until the host's first count (its `usage`).
|
|
16
|
-
|
|
15
|
+
tool schemas it sends, until the host's first count (its `usage`). So that a
|
|
16
|
+
turn's first request doesn't dip below the last turn's count (the schemas are
|
|
17
|
+
thousands of tokens), the loop keeps the last counted request's chars per token
|
|
18
|
+
(`ContextStatus#counted_chars_per_token`, on `KernelLoop` / `ChatLoop`) and the
|
|
19
|
+
next turn's estimate before its first count uses it instead of
|
|
20
|
+
`context.chars_per_token`. The event carries a `status` string using this prefix:
|
|
17
21
|
|
|
18
22
|
`CONTEXT_STATUS ...`
|
|
19
23
|
|
data/docs/internals/engine.md
CHANGED
|
@@ -518,7 +518,28 @@ Turn events (the main ones; `Events` in `events.rb` keeps the shared sets):
|
|
|
518
518
|
| `:answer_display` | after the after_turn hooks (observers only, not the sink) | `{display, refs?}` (display a string, or nil; refs the count of the answer's refs, only when a hook attached some) |
|
|
519
519
|
|
|
520
520
|
The boundary events (`:turn_started`, `:turn_completed`, `:turn_canceled`, `:turn_failed`) also carry `origin`
|
|
521
|
-
when the turn was queued with one
|
|
521
|
+
when the turn was queued with one: `{client_id:, enqueued_id:}`, plus `author:` when someone other than the session's
|
|
522
|
+
owner wrote it (its input file's `author`, stamped by chi web from the request's `Web::Principal`). An owner's turn
|
|
523
|
+
has no `author` anywhere: not in its input file, its origin or the session. `GuardrailWiring#begin_turn` gets the
|
|
524
|
+
origin, and `#turn_author` reads its author for the gate's checks.
|
|
525
|
+
|
|
526
|
+
Where a participant's author (`"p:<id>"`; `Steer.participant_author?`: any author but none or `"owner"`) goes:
|
|
527
|
+
|
|
528
|
+
- **The opening message**: `{role: "user", content:, turn_id:, author:, author_name:}` (`Engine#author_fields`;
|
|
529
|
+
the name from `participants.json` at the turn's start, `SessionParticipants`, left out when the sidecar doesn't
|
|
530
|
+
name the participant). Merged input and steers carry the same two keys (`Steer::Line#author`, grouped by sender
|
|
531
|
+
and author so a participant's lines never share the owner's message). Assistant and tool messages carry none:
|
|
532
|
+
`Steer.author_at(messages, index)` resolves the turn's author by position. `ChatLoop#plain` keeps the keys
|
|
533
|
+
(`Steer::AUTHOR_KEYS`).
|
|
534
|
+
- **The wire**: such a message goes out under the review-contribution header (`Steer.header` /
|
|
535
|
+
`Steer.review_header`, both loops through `Steer.wire_text`; see prompt-caching.md). Saved raw.
|
|
536
|
+
- **`Session#last_turn.author`** (`LastTurn#origin` stays the category) and the turn's analytics record's `author`
|
|
537
|
+
(`SessionMetrics`).
|
|
538
|
+
- **Commands and queued turns**: `QueuedCommand#author` (a `/word` input file, `WorkerInbound#take_input`, or a
|
|
539
|
+
`/word` POST /turn, `Bridge#queue_command_request`), in `command_queued`; `turn_enqueued`; the accumulator's
|
|
540
|
+
queued rows.
|
|
541
|
+
- **The system prompt**: `Engine#refresh_participants!` at a turn's start (after `apply_toolkit!`) rebuilds the
|
|
542
|
+
prompt when the session gains its first active participant or loses its last (the participants line).
|
|
522
543
|
|
|
523
544
|
### Hook points (not events)
|
|
524
545
|
|