samagotchi 0.152.0 → 0.154.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 +45 -1
- data/README.md +2 -3
- data/bin/chi +23 -24
- data/docs/architecture.md +30 -33
- data/docs/broadcast.md +5 -5
- data/docs/bundle-authoring.md +1 -1
- data/docs/bundles.md +93 -12
- data/docs/cli.md +83 -99
- data/docs/configuration.md +46 -40
- data/docs/context.md +3 -3
- data/docs/desktop.md +1 -1
- data/docs/guardrails.md +15 -15
- data/docs/hooks.md +13 -12
- data/docs/internals/anthropic.md +3 -3
- data/docs/internals/attached-context.md +5 -2
- data/docs/internals/background-tasks.md +3 -4
- data/docs/internals/block-renderers.md +4 -0
- data/docs/internals/bridge.md +3 -3
- data/docs/internals/context-telemetry.md +1 -1
- data/docs/internals/engine.md +42 -12
- data/docs/internals/objectives.md +6 -5
- data/docs/internals/participants.md +8 -8
- data/docs/internals/prompt-caching.md +8 -4
- data/docs/internals/remote-sessions.md +7 -7
- data/docs/internals/web-frontend.md +20 -17
- data/docs/internals/web-push.md +1 -1
- data/docs/memory.md +2 -2
- data/docs/plugins.md +52 -43
- data/docs/releasing.md +1 -1
- data/docs/sessions.md +25 -25
- data/docs/sub-agent.md +2 -2
- data/docs/system-one.md +1 -1
- data/docs/testing.md +18 -8
- data/lib/samagotchi/answer_text.rb +34 -0
- data/lib/samagotchi/bridge.rb +43 -32
- data/lib/samagotchi/bridge_client.rb +10 -9
- data/lib/samagotchi/broadcast/recipients.rb +2 -2
- data/lib/samagotchi/broadcast/triage.rb +1 -1
- data/lib/samagotchi/broadcast_command.rb +5 -5
- data/lib/samagotchi/bundle_command/try/command.rb +1 -1
- data/lib/samagotchi/bundle_command/try/run.rb +1 -1
- data/lib/samagotchi/bundle_command.rb +2 -2
- data/lib/samagotchi/bundles/decision-card/manifest.yml +4 -4
- data/lib/samagotchi/bundles/decision-card/plugin.rb +1 -9
- data/lib/samagotchi/bundles/dev/manifest.yml +2 -2
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +3 -4
- data/lib/samagotchi/bundles/system/manifest.yml +3 -3
- data/lib/samagotchi/bundles/system/self_map.md +1 -1
- data/lib/samagotchi/bundles/web-search/manifest.yml +13 -0
- data/lib/samagotchi/bundles/web-search/plugin.rb +174 -0
- data/lib/samagotchi/child_move.rb +3 -3
- data/lib/samagotchi/commands/registry.rb +6 -6
- data/lib/samagotchi/composer_route.rb +4 -5
- data/lib/samagotchi/config.rb +14 -12
- data/lib/samagotchi/context_candidate_choice.rb +1 -1
- data/lib/samagotchi/context_candidates.rb +4 -3
- data/lib/samagotchi/continue_offer.rb +1 -1
- data/lib/samagotchi/debug_log.rb +1 -1
- data/lib/samagotchi/degrade.rb +75 -0
- data/lib/samagotchi/desktop/macos/ChiRunner.swift +2 -2
- data/lib/samagotchi/engine/cards.rb +87 -0
- data/lib/samagotchi/engine/context_judge.rb +138 -0
- data/lib/samagotchi/engine/events.rb +131 -0
- data/lib/samagotchi/engine/guardrails.rb +87 -0
- data/lib/samagotchi/engine/hooks.rb +119 -0
- data/lib/samagotchi/engine/messages.rb +124 -0
- data/lib/samagotchi/engine/model_settings.rb +127 -0
- data/lib/samagotchi/engine/models.rb +261 -0
- data/lib/samagotchi/engine/objectives.rb +116 -0
- data/lib/samagotchi/engine/participants.rb +86 -0
- data/lib/samagotchi/engine/plugins.rb +208 -0
- data/lib/samagotchi/engine/questions.rb +98 -0
- data/lib/samagotchi/engine/recap.rb +130 -0
- data/lib/samagotchi/engine/session.rb +131 -0
- data/lib/samagotchi/engine/side_requests.rb +135 -0
- data/lib/samagotchi/engine/steering.rb +134 -0
- data/lib/samagotchi/engine/toolkit.rb +269 -0
- data/lib/samagotchi/engine/turn_end.rb +180 -0
- data/lib/samagotchi/engine/turn_helpers.rb +160 -0
- data/lib/samagotchi/engine/verify.rb +153 -0
- data/lib/samagotchi/engine.rb +63 -2642
- data/lib/samagotchi/grants.rb +575 -0
- data/lib/samagotchi/guardrail_wiring.rb +2 -2
- data/lib/samagotchi/guardrails/context.rb +2 -3
- data/lib/samagotchi/guardrails/parent_approvals.rb +2 -2
- data/lib/samagotchi/guardrails/parent_policy.rb +2 -2
- data/lib/samagotchi/guardrails/review_boundary.rb +4 -3
- data/lib/samagotchi/hook_routes.rb +3 -3
- data/lib/samagotchi/idle_recap.rb +4 -5
- data/lib/samagotchi/idle_reminders.rb +3 -4
- data/lib/samagotchi/iteration_limit.rb +2 -2
- data/lib/samagotchi/kernel_loop.rb +10 -11
- data/lib/samagotchi/launch_mode.rb +13 -32
- data/lib/samagotchi/level_setting_command.rb +3 -2
- data/lib/samagotchi/llm_context_command.rb +3 -2
- data/lib/samagotchi/llm_context_stale.rb +2 -2
- data/lib/samagotchi/log.rb +7 -7
- data/lib/samagotchi/log_line.rb +1 -1
- data/lib/samagotchi/log_path.rb +1 -1
- data/lib/samagotchi/message_parts.rb +3 -2
- data/lib/samagotchi/note_command.rb +1 -1
- data/lib/samagotchi/note_delivery.rb +18 -8
- data/lib/samagotchi/objectives.rb +24 -3
- data/lib/samagotchi/objectives_check.rb +8 -27
- data/lib/samagotchi/objectives_command.rb +4 -69
- data/lib/samagotchi/objectives_edit.rb +87 -0
- data/lib/samagotchi/output_formatter.rb +7 -1
- data/lib/samagotchi/owner_lock.rb +7 -1
- data/lib/samagotchi/parent_answers_command.rb +5 -4
- data/lib/samagotchi/participant_exposure.rb +42 -0
- data/lib/samagotchi/participant_mode.rb +1 -1
- data/lib/samagotchi/participant_name.rb +1 -1
- data/lib/samagotchi/participants.rb +17 -17
- data/lib/samagotchi/participants_watch.rb +1 -1
- data/lib/samagotchi/pending_input_queue.rb +2 -2
- data/lib/samagotchi/plugin/api.rb +3 -2
- data/lib/samagotchi/plugin/context.rb +21 -7
- data/lib/samagotchi/plugin/peer.rb +2 -1
- data/lib/samagotchi/plugin/sessions.rb +6 -4
- data/lib/samagotchi/plugin_tasks.rb +1 -2
- data/lib/samagotchi/private_files.rb +29 -0
- data/lib/samagotchi/project_env.rb +2 -2
- data/lib/samagotchi/question_desk.rb +8 -7
- data/lib/samagotchi/recap_command.rb +2 -1
- data/lib/samagotchi/recap_setup.rb +1 -1
- data/lib/samagotchi/recap_store.rb +1 -1
- data/lib/samagotchi/redact.rb +86 -0
- data/lib/samagotchi/reminder_queue.rb +2 -2
- data/lib/samagotchi/remote_attach_command.rb +1 -1
- data/lib/samagotchi/reply_wait/poll.rb +2 -2
- data/lib/samagotchi/review_start.rb +1 -1
- data/lib/samagotchi/run_options.rb +6 -6
- data/lib/samagotchi/self_report.rb +42 -39
- data/lib/samagotchi/send_command/busy.rb +1 -1
- data/lib/samagotchi/send_command.rb +4 -1
- data/lib/samagotchi/session.rb +11 -2
- data/lib/samagotchi/session_access/contract.rb +3 -3
- data/lib/samagotchi/session_access/local.rb +9 -9
- data/lib/samagotchi/session_access/remote.rb +17 -7
- data/lib/samagotchi/session_access.rb +4 -4
- data/lib/samagotchi/session_archive_command.rb +2 -2
- data/lib/samagotchi/session_chain.rb +8 -6
- data/lib/samagotchi/session_commands.rb +45 -48
- data/lib/samagotchi/session_continue.rb +1 -1
- data/lib/samagotchi/session_delete_command.rb +4 -4
- data/lib/samagotchi/session_manager.rb +59 -36
- data/lib/samagotchi/session_metrics.rb +2 -2
- data/lib/samagotchi/session_model_settings.rb +3 -2
- data/lib/samagotchi/session_participants.rb +1 -1
- data/lib/samagotchi/session_retention.rb +3 -3
- data/lib/samagotchi/sessions_command.rb +4 -4
- data/lib/samagotchi/share_command.rb +10 -10
- data/lib/samagotchi/steer.rb +1 -1
- data/lib/samagotchi/system_prompt.rb +102 -109
- data/lib/samagotchi/task_watch.rb +1 -1
- data/lib/samagotchi/terminal_ui/attach_launcher.rb +72 -10
- data/lib/samagotchi/terminal_ui/attached_loop/composer.rb +12 -9
- data/lib/samagotchi/terminal_ui/attached_loop/exit_commands.rb +16 -0
- data/lib/samagotchi/terminal_ui/attached_loop/questions.rb +2 -3
- data/lib/samagotchi/terminal_ui/attached_loop.rb +17 -8
- data/lib/samagotchi/terminal_ui/attached_view.rb +2 -3
- data/lib/samagotchi/terminal_ui/event_renderer.rb +3 -5
- data/lib/samagotchi/terminal_ui/formatting.rb +7 -7
- data/lib/samagotchi/terminal_ui/input_support.rb +1 -7
- data/lib/samagotchi/terminal_ui/line_reader.rb +2 -2
- data/lib/samagotchi/terminal_ui/live_region.rb +2 -2
- data/lib/samagotchi/terminal_ui/log_tail.rb +135 -0
- data/lib/samagotchi/terminal_ui/one_shot_sink.rb +2 -2
- data/lib/samagotchi/terminal_ui/plain_surface.rb +1 -1
- data/lib/samagotchi/terminal_ui/question_prompt.rb +4 -5
- data/lib/samagotchi/terminal_ui/reline_seam.rb +3 -3
- data/lib/samagotchi/terminal_ui/status_row.rb +7 -8
- data/lib/samagotchi/terminal_ui.rb +142 -1387
- data/lib/samagotchi/title_command.rb +2 -2
- data/lib/samagotchi/tool_runner.rb +14 -16
- data/lib/samagotchi/toolkit.rb +8 -5
- data/lib/samagotchi/toolkit_binding.rb +30 -5
- data/lib/samagotchi/toolkit_command.rb +2 -1
- data/lib/samagotchi/toolkit_report_command.rb +9 -5
- data/lib/samagotchi/toolkit_suggest.rb +12 -6
- data/lib/samagotchi/tools/builtin_calls.rb +14 -10
- data/lib/samagotchi/tools/deferred_tools.rb +2 -2
- data/lib/samagotchi/tools/delegate.rb +3 -1
- data/lib/samagotchi/tools/delegate_cursor.rb +3 -3
- data/lib/samagotchi/tools/delegate_wait.rb +1 -1
- data/lib/samagotchi/tools/objectives_tool.rb +2 -2
- data/lib/samagotchi/tools/registry.rb +44 -20
- data/lib/samagotchi/tools/send_note.rb +2 -1
- data/lib/samagotchi/tools/tool_index.rb +27 -21
- data/lib/samagotchi/turn_endings.rb +2 -2
- data/lib/samagotchi/turn_flow.rb +4 -5
- data/lib/samagotchi/update_hint.rb +2 -2
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +39 -50
- data/lib/samagotchi/web/participant_access.rb +2 -2
- data/lib/samagotchi/web/participant_review.rb +5 -5
- data/lib/samagotchi/web/participant_stream_filter.rb +8 -8
- data/lib/samagotchi/web/participant_view.rb +2 -82
- data/lib/samagotchi/web/participants_admin.rb +1 -1
- data/lib/samagotchi/web/public/app.js +61 -357
- data/lib/samagotchi/web/public/block_host.js +35 -8
- data/lib/samagotchi/web/public/block_renderers.css +9 -5
- data/lib/samagotchi/web/public/chains.js +2 -2
- data/lib/samagotchi/web/public/claim_link.js +4 -4
- data/lib/samagotchi/web/public/composer_mode.js +110 -0
- data/lib/samagotchi/web/public/format.js +3 -3
- data/lib/samagotchi/web/public/owner_claim.js +81 -0
- data/lib/samagotchi/web/public/owner_verify.js +112 -0
- data/lib/samagotchi/web/public/session_fields.js +3 -3
- data/lib/samagotchi/web/public/strip_grip.js +3 -3
- data/lib/samagotchi/web/public/strip_hidden.js +65 -0
- data/lib/samagotchi/web/public/version_notices.js +132 -0
- data/lib/samagotchi/web/push/store.rb +1 -1
- data/lib/samagotchi/web/session_access_routes.rb +5 -4
- data/lib/samagotchi/web/session_control.rb +12 -8
- data/lib/samagotchi/web/session_create.rb +1 -1
- data/lib/samagotchi/web/session_hub.rb +2 -2
- data/lib/samagotchi/web/session_show.rb +2 -2
- data/lib/samagotchi/web/session_summary.rb +1 -1
- data/lib/samagotchi/worker.rb +106 -9
- data/lib/samagotchi/worker_link.rb +3 -3
- data/lib/samagotchi/working_set_command.rb +3 -2
- metadata +36 -6
- data/lib/samagotchi/terminal_ui/between_turns.rb +0 -138
- data/lib/samagotchi/terminal_ui/repl_input.rb +0 -152
- data/lib/samagotchi/web/grants.rb +0 -575
- data/lib/samagotchi/web/participant_exposure.rb +0 -44
- data/lib/samagotchi/web/private_files.rb +0 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 43fce920e24b60bc0bb9b5211dbc894f272e4f82ea55e6568698b533083f0c66
|
|
4
|
+
data.tar.gz: 5af718499f366587969cef9318c16e271fa343a69362f1ba5ed227992d2ab4d8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5f3a52420f92d4335f0c156cd208b5b40d8b952a692861d2ca0e6672caea3d66bb033f6e04f0c3288fe6415ae48c63d8489ef8be6cd24c57086861b3117c7746
|
|
7
|
+
data.tar.gz: d9a8a9c3cc79549bf476f43900669ac68e212ed419c76ec362b8e9483d937eb70537f00b9d63a3e904417205dd42db069662e13127ee3cadf710b18bdc4fa058
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,48 @@ and commands may change between minor versions. How releases are made:
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.154.0] - 2026-11-07
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- `chi scratch` in a terminal runs attached, on a session worker of its own that saves nothing (no memory writes, no delegates, approvals for this session only). Ctrl-D, `/detach`, `/exit` or a second Ctrl-C ends it: the worker stops and the session is deleted ("Scratch session deleted."); a worker whose terminal is gone (a killed terminal) deletes it too. `chi send`, chi web and `chi --attach`/`--resume` refuse a scratch session, in one wording. `chi scratch -p TEXT --non-interactive` still runs its turn in this process.
|
|
16
|
+
- `chi -v` runs attached: the terminal shows the session's debug records (raw LLM responses, tool calls and results) above the prompt, and a worker this chi starts logs at debug. A worker that was already running keeps the level it started with, and one line says so. With `--non-interactive`, `-v` still mirrors the records to stderr.
|
|
17
|
+
- A session a `chi -p … --non-interactive` run has open is refused as "in use by a one-turn chi --non-interactive run or an older chi's terminal" (`chi send`, `chi note`, `chi sessions delete`/`archive`, chi web, the delegate tools), not as "open in a chi REPL".
|
|
18
|
+
- `chi bundle upgrade --agent` merges the conflicts in the attached terminal.
|
|
19
|
+
- chi now writes a warning to the debug log when part of its work falls back after an error (a system prompt section left out, a Bridge route or a chi web answer given empty, a session command's lookup) instead of dropping it silently: once per process for each kind, later ones at `debug`.
|
|
20
|
+
|
|
21
|
+
### Removed
|
|
22
|
+
|
|
23
|
+
- The in-process REPL: an interactive `chi` (plain, `-p`, `--resume`, `--attach`) always runs attached to its session's worker; `--non-interactive` runs its one turn in this process as before.
|
|
24
|
+
- The `--shared` and `--no-shared` flags (they are an unknown option now) and the `session.shared` setting (config.yml warns "removed: chi always runs attached"; `SAMAGOTCHI_SESSION_SHARED` is ignored).
|
|
25
|
+
- The `:repl` interface: a hook event's `interface` and a plugin's `ctx.frontend` are never `:repl` (a bundle that checks for it never matches).
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- `chi scratch`: a terminal whose stream drops and reconnects no longer has its turn cancelled and its session deleted by the worker; the worker waits about 25 s with no stream before it takes the terminal as gone.
|
|
30
|
+
- chi web: a diagram drawn by a block renderer is usable without a mouse: a screen reader hears that its source is behind the "Show the source" button, and a button that shows on keyboard focus zooms it to full size and back.
|
|
31
|
+
- The objectives tool refuses a proposal its card's Apply would refuse (an add or reword whose text is only a status like `[done]` or only `(done when: …)`, or `done` for a dropped objective), so the model hears why instead of leaving a card that can't apply.
|
|
32
|
+
- A thought the model never closed no longer reaches the continue note's "Its last answer" or decision-card's `/short`: both read the answer as you saw it, with the one rule a Check this record uses (a bundle gets it as `ctx.last_answer`). decision-card 0.2.1.
|
|
33
|
+
- chi web: a reply that is only a thought the model never closed is no longer shown as an answer or taken as the session's last answer.
|
|
34
|
+
|
|
35
|
+
Update with `chi update` (decision-card 0.2.1, the dev profile 0.7.7).
|
|
36
|
+
|
|
37
|
+
## [0.153.0] - 2026-11-06
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- A `web-search` bundle (`chi bundle install web-search`, in no profile): a `web_search` tool that searches the web through your own SearXNG and returns titles, URLs and snippets for `web_fetch` to read. It is declared on `full` and kits on `base: full`; on kits built on `lean` or `child` it is reachable (`find_tools` finds `chi/web_search`, `call_tool` runs it); `add: [web_search]` in a kit declares it. [docs/bundles.md](docs/bundles.md#the-web-search-bundle)
|
|
42
|
+
|
|
43
|
+
### Changed
|
|
44
|
+
|
|
45
|
+
- A bundle's tools (`chi.tool`) follow a toolkit's tool list as built-ins do: `full`, the kits on `base: full` (the `kits:` default) and a kit's `add:` declare them, any other kit leaves them reachable through `find_tools` / `call_tool` (deferred on `api: anthropic`), and `remove:` removes them. [docs/configuration.md](docs/configuration.md#toolkits)
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
- An attached context change that arrives while the worker's leaving pass is already in flight is saved as a note before the worker stops, instead of being dropped with its poller.
|
|
50
|
+
|
|
51
|
+
Update with `chi update` (no installed bundle's version moved; `chi bundle install web-search` adds the new one).
|
|
52
|
+
|
|
11
53
|
## [0.152.0] - 2026-11-06
|
|
12
54
|
|
|
13
55
|
### Added
|
|
@@ -5717,7 +5759,9 @@ and long-lived sessions.
|
|
|
5717
5759
|
- A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
|
|
5718
5760
|
hotkey panel that send selected text or the clipboard to your sessions.
|
|
5719
5761
|
|
|
5720
|
-
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.
|
|
5762
|
+
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.154.0...HEAD
|
|
5763
|
+
[0.154.0]: https://github.com/dm1try/samagotchi/compare/v0.153.0...v0.154.0
|
|
5764
|
+
[0.153.0]: https://github.com/dm1try/samagotchi/compare/v0.152.0...v0.153.0
|
|
5721
5765
|
[0.152.0]: https://github.com/dm1try/samagotchi/compare/v0.151.0...v0.152.0
|
|
5722
5766
|
[0.151.0]: https://github.com/dm1try/samagotchi/compare/v0.150.0...v0.151.0
|
|
5723
5767
|
[0.150.0]: https://github.com/dm1try/samagotchi/compare/v0.149.0...v0.150.0
|
data/README.md
CHANGED
|
@@ -119,7 +119,6 @@ hosts:
|
|
|
119
119
|
|
|
120
120
|
```sh
|
|
121
121
|
chi # interactive session the web UI can join too
|
|
122
|
-
chi --no-shared # the plain in-process REPL
|
|
123
122
|
chi -p "explain lib/" --non-interactive # one turn, print the answer, exit
|
|
124
123
|
chi --resume <session-id> # continue a saved session
|
|
125
124
|
chi web --open # web UI: this project's sessions (--scope=all: every one)
|
|
@@ -200,7 +199,7 @@ Rules come from `config.yml` (`guardrails:`), installed bundles and hooks;
|
|
|
200
199
|
the `guardrails` bundle (in `core`, which `chi bootstrap` installs; else
|
|
201
200
|
`chi bundle install guardrails`) adds a default set (asks before `git push`,
|
|
202
201
|
history rewrites, wide `rm -rf`, `curl | sh`, writes outside the repo; denies
|
|
203
|
-
writes to `.git/hooks`). You answer in the
|
|
202
|
+
writes to `.git/hooks`). You answer in the attached TUI or the web:
|
|
204
203
|
once, for the session, for the repo, or for the whole rule in the repo.
|
|
205
204
|
`/guardrails` lists the rules and your approvals. See [Guardrails](docs/guardrails.md).
|
|
206
205
|
|
|
@@ -209,7 +208,7 @@ once, for the session, for the repo, or for the whole rule in the repo.
|
|
|
209
208
|
- [CHANGELOG](CHANGELOG.md): what changed in each release
|
|
210
209
|
- [Releasing](docs/releasing.md): versions, the changelog, how a release is published
|
|
211
210
|
|
|
212
|
-
- [CLI and
|
|
211
|
+
- [CLI and terminal](docs/cli.md): flags, sharing a session, web UI, `/model`, status line
|
|
213
212
|
- [Configuration](docs/configuration.md): `config.yml`, hosts, model server APIs, timeouts, retries, logs
|
|
214
213
|
- [Memory](docs/memory.md): scopes, model-specific overlays and model notes
|
|
215
214
|
- [Sessions](docs/sessions.md): storage, retention, `chi sessions`
|
data/bin/chi
CHANGED
|
@@ -139,7 +139,8 @@ if ARGV[0] == "web" && ARGV[1] == "push"
|
|
|
139
139
|
end
|
|
140
140
|
|
|
141
141
|
# `chi scratch`: the run options below, for a session that leaves nothing
|
|
142
|
-
# behind (TerminalUI
|
|
142
|
+
# behind (AttachLauncher scratch:; TerminalUI's with --non-interactive). The
|
|
143
|
+
# word goes before OptionParser sees it.
|
|
143
144
|
scratch = ARGV[0] == "scratch"
|
|
144
145
|
ARGV.shift if scratch
|
|
145
146
|
|
|
@@ -192,7 +193,7 @@ end
|
|
|
192
193
|
|
|
193
194
|
# A scratch session is new, runs here and goes when it ends.
|
|
194
195
|
if scratch
|
|
195
|
-
flag = { resume: "--resume", attach: "--attach"
|
|
196
|
+
flag = { resume: "--resume", attach: "--attach" }.find { |key, _flag| options[key] }&.last
|
|
196
197
|
if flag
|
|
197
198
|
warn "Error: chi scratch starts a new session in this terminal and keeps nothing; it can't take #{flag}"
|
|
198
199
|
exit 1
|
|
@@ -203,15 +204,19 @@ end
|
|
|
203
204
|
# session's own level); a session-starting run's is that session's own
|
|
204
205
|
# (TerminalUI, AttachLauncher), not a process setting.
|
|
205
206
|
cli_overrides["thinking.level"] = options[:thinking] if web && options[:thinking]
|
|
207
|
+
# -v: debug records, in this process and in a worker it spawns (Config.cli_env).
|
|
208
|
+
cli_overrides["log.level"] = "debug" if options[:verbose]
|
|
206
209
|
|
|
207
210
|
# Apply CLI precedence: CLI > ENV > file. Workers this chi spawns get
|
|
208
211
|
# these through their env (Config.cli_env).
|
|
209
212
|
Samagotchi::Config.reload!(cli_overrides: cli_overrides) unless cli_overrides.empty?
|
|
210
213
|
|
|
211
214
|
# The debug log (Samagotchi::LogPath, level log.level) for every command.
|
|
212
|
-
# -v
|
|
215
|
+
# -v: debug records; a --non-interactive one-shot mirrors each to stderr,
|
|
216
|
+
# the attached TUI shows its session's above the live region (LogTail).
|
|
213
217
|
require "samagotchi/log"
|
|
214
|
-
Samagotchi::Log.configure(level: options[:verbose] ? :debug : nil,
|
|
218
|
+
Samagotchi::Log.configure(level: options[:verbose] ? :debug : nil,
|
|
219
|
+
mirror: options[:verbose] && options[:non_interactive])
|
|
215
220
|
|
|
216
221
|
# chi --remote NAME --attach ID (--resume ID, --dir DIR, -p TEXT): the
|
|
217
222
|
# attached TUI on that machine's session. Nothing below runs: it checks
|
|
@@ -274,8 +279,9 @@ rescue Samagotchi::Session::AmbiguousId => e
|
|
|
274
279
|
exit 1
|
|
275
280
|
end
|
|
276
281
|
|
|
277
|
-
# A `chi scratch` session
|
|
278
|
-
# one if opened again;
|
|
282
|
+
# A `chi scratch` session is its terminal's (a live one), or one a killed
|
|
283
|
+
# terminal left behind, which would turn into a kept one if opened again;
|
|
284
|
+
# the next sweep deletes it.
|
|
279
285
|
%i[resume attach].each do |key|
|
|
280
286
|
next unless options[key]
|
|
281
287
|
|
|
@@ -286,8 +292,7 @@ end
|
|
|
286
292
|
end
|
|
287
293
|
next unless leftover
|
|
288
294
|
|
|
289
|
-
|
|
290
|
-
warn "Error: #{Samagotchi::LaunchMode::SCRATCH_LEFTOVER}"
|
|
295
|
+
warn "Error: #{Samagotchi::Session::SCRATCH_REFUSED}"
|
|
291
296
|
exit 1
|
|
292
297
|
end
|
|
293
298
|
|
|
@@ -395,12 +400,7 @@ end
|
|
|
395
400
|
|
|
396
401
|
chi_update_hint.call
|
|
397
402
|
require "samagotchi/launch_mode"
|
|
398
|
-
launch
|
|
399
|
-
[:repl, nil]
|
|
400
|
-
else
|
|
401
|
-
Samagotchi::LaunchMode.resolve(options, shared_config: Samagotchi::Config.get("session.shared"))
|
|
402
|
-
end
|
|
403
|
-
warn launch_note if launch_note
|
|
403
|
+
launch = Samagotchi::LaunchMode.resolve(options)
|
|
404
404
|
|
|
405
405
|
if launch == :attached
|
|
406
406
|
error = Samagotchi::LaunchMode.attached_conflict(options)
|
|
@@ -411,7 +411,7 @@ if launch == :attached
|
|
|
411
411
|
|
|
412
412
|
require "samagotchi/terminal_ui/attach_launcher"
|
|
413
413
|
begin
|
|
414
|
-
ended = Samagotchi::TerminalUI::AttachLauncher.run(attach: options[:attach],
|
|
414
|
+
ended = Samagotchi::TerminalUI::AttachLauncher.run(attach: options[:attach], resume: options[:resume],
|
|
415
415
|
prompt: options[:prompt], model: options[:model],
|
|
416
416
|
no_interrupt: !!options[:no_interrupt],
|
|
417
417
|
default_input: !options[:no_default_input],
|
|
@@ -423,7 +423,8 @@ if launch == :attached
|
|
|
423
423
|
toolkit: options[:toolkit_value],
|
|
424
424
|
toolkit_flags: options[:toolkit_flags] || [],
|
|
425
425
|
settings: options[:settings],
|
|
426
|
-
parent_answers: options[:parent_answers]
|
|
426
|
+
parent_answers: options[:parent_answers],
|
|
427
|
+
scratch: scratch, verbose: options[:verbose])
|
|
427
428
|
rescue Samagotchi::TerminalUI::AttachLauncher::Error, Samagotchi::Toolkit::Refused => e
|
|
428
429
|
warn "Error: #{e.message}"
|
|
429
430
|
exit 1
|
|
@@ -435,13 +436,13 @@ if launch == :attached
|
|
|
435
436
|
exit Samagotchi::TerminalUI::AttachLauncher.exit_status(ended)
|
|
436
437
|
end
|
|
437
438
|
|
|
439
|
+
# --non-interactive: the -p prompt's one turn in this process (TerminalUI,
|
|
440
|
+
# the one-shot host); bare, a no-op that creates no session.
|
|
438
441
|
require "samagotchi/cli/exit"
|
|
439
442
|
begin
|
|
440
443
|
ended = Samagotchi::TerminalUI.new(
|
|
441
444
|
prompt: options[:prompt],
|
|
442
445
|
session_id: options[:resume],
|
|
443
|
-
no_interrupt: options[:no_interrupt] || options[:non_interactive],
|
|
444
|
-
no_default_input: options[:no_default_input],
|
|
445
446
|
memories: options[:memories] || [],
|
|
446
447
|
muted_memories: options[:muted] || [],
|
|
447
448
|
llm_context: options[:llm_context] || {},
|
|
@@ -451,13 +452,11 @@ begin
|
|
|
451
452
|
toolkit_flags: options[:toolkit_flags] || [],
|
|
452
453
|
settings: options[:settings],
|
|
453
454
|
parent_answers: options[:parent_answers],
|
|
454
|
-
non_interactive: options[:non_interactive],
|
|
455
455
|
model_name: options[:model],
|
|
456
456
|
scratch: scratch
|
|
457
457
|
).run
|
|
458
|
-
#
|
|
459
|
-
#
|
|
460
|
-
# limit, or Ctrl-C cancelled it.
|
|
458
|
+
# The turn failed, ended with no answer or at the iteration limit, or
|
|
459
|
+
# Ctrl-C cancelled it.
|
|
461
460
|
exit 1 if %i[turn_failed empty_answer iteration_limit].include?(ended)
|
|
462
461
|
exit Samagotchi::CLI::Exit::INTERRUPTED if ended == :interrupted
|
|
463
462
|
rescue Samagotchi::TerminalUI::SessionBusy, Samagotchi::TerminalUI::SessionNotFound, Samagotchi::ModelProfile::MissingModel,
|
|
@@ -465,11 +464,11 @@ rescue Samagotchi::TerminalUI::SessionBusy, Samagotchi::TerminalUI::SessionNotFo
|
|
|
465
464
|
warn "Error: #{e.message}"
|
|
466
465
|
exit 1
|
|
467
466
|
rescue Samagotchi::LLM::ProviderError => e
|
|
468
|
-
#
|
|
467
|
+
# The turn failed; its prompt is in the session.
|
|
469
468
|
warn "Error: #{e.summary}"
|
|
470
469
|
exit 1
|
|
471
470
|
rescue Samagotchi::ImageStore::Error => e
|
|
472
|
-
#
|
|
471
|
+
# An @path image that can't be used.
|
|
473
472
|
warn "Error: #{e.message}"
|
|
474
473
|
exit 1
|
|
475
474
|
end
|
data/docs/architecture.md
CHANGED
|
@@ -10,9 +10,9 @@ A compact visual overview of the current architecture, then the core API in pros
|
|
|
10
10
|
│ │
|
|
11
11
|
┌────────────────────┴────────────────────┐ ┌───────────────┴────────────────┐
|
|
12
12
|
│ TerminalUI │ │ Web::App (Rack) │ ← browser UI
|
|
13
|
-
│
|
|
13
|
+
│ interactive: AttachLauncher + │ │ session hub · /api/events │
|
|
14
14
|
│ AttachedLoop, no Engine │ │ /api/* · reads session files │
|
|
15
|
-
│
|
|
15
|
+
│ --non-interactive: its own Engine │ └───────────────┬────────────────┘
|
|
16
16
|
└────────────────────┬────────────────────┘ │ App#relay, bridge_get,
|
|
17
17
|
│ BridgeClient (SSE + HTTP) │ /stream proxy
|
|
18
18
|
└────────────────────┬────────────────────┘
|
|
@@ -60,7 +60,7 @@ A compact visual overview of the current architecture, then the core API in pros
|
|
|
60
60
|
|
|
61
61
|
```
|
|
62
62
|
TerminalUI ── delegates ──▶ Engine
|
|
63
|
-
(
|
|
63
|
+
(one-shot host) (pure logic, no terminal)
|
|
64
64
|
▲ │
|
|
65
65
|
└────────── on_event: ◀────────────┘
|
|
66
66
|
(turn_started, turn_completed,
|
|
@@ -69,13 +69,14 @@ TerminalUI ── delegates ──▶ Engine
|
|
|
69
69
|
|
|
70
70
|
- **`Engine`** (`lib/samagotchi/engine.rb`) owns *all* agent logic and knows nothing
|
|
71
71
|
about the terminal ([internals/engine.md](internals/engine.md)).
|
|
72
|
-
- **`TerminalUI`** (`lib/samagotchi/terminal_ui.rb`)
|
|
73
|
-
|
|
72
|
+
- **`TerminalUI`** (`lib/samagotchi/terminal_ui.rb`) is the one-shot host: `chi -p TEXT
|
|
73
|
+
--non-interactive` (with `--resume ID` or `chi scratch` too) runs that one turn in this
|
|
74
|
+
process, its answer alone on stdout, and delegates all core work to its `Engine`. Every
|
|
75
|
+
interactive terminal is the attached one (`TerminalUI::AttachLauncher`, `AttachedLoop`).
|
|
74
76
|
- The `on_event:` seam on `run_turn` exposes raw `KernelLoop` events plus the
|
|
75
77
|
higher-level turn events, so any new UI can render without terminal coupling.
|
|
76
78
|
- **`ComposerRoute`** (`lib/samagotchi/composer_route.rb`) decides where a line typed at
|
|
77
|
-
|
|
78
|
-
`AttachedLoop::Composer#submit`): `/cut TEXT` / `/queue TEXT`, a terminal's own word, a session
|
|
79
|
+
the attached terminal's prompt goes (`AttachedLoop::Composer#submit`): `/cut TEXT` / `/queue TEXT`, a terminal's own word, a session
|
|
79
80
|
command, a typo, a prompt. Each UI keeps what it does with the route. The web's
|
|
80
81
|
`composer.js#routeComposerLine` decides the same; `spec/fixtures/composer_routes.json`
|
|
81
82
|
holds the cases both answer (see [testing.md](testing.md)).
|
|
@@ -90,7 +91,7 @@ bin/chi ─▶ TerminalUI ─▶ Engine#run_turn ─▶ KernelLoop / ChatLoop
|
|
|
90
91
|
(turn started / completed / canceled)
|
|
91
92
|
```
|
|
92
93
|
|
|
93
|
-
That is
|
|
94
|
+
That is a `--non-interactive` one-shot; interactive, the session's `Worker` calls `Engine#run_turn` and the
|
|
94
95
|
terminal renders the events it gets over the Bridge.
|
|
95
96
|
|
|
96
97
|
## Layers at a glance
|
|
@@ -98,7 +99,7 @@ terminal renders the events it gets over the Bridge.
|
|
|
98
99
|
| Layer | Class(es) | Responsibility |
|
|
99
100
|
|-------|-----------|----------------|
|
|
100
101
|
| Core | `Samagotchi::Engine` | System prompt, memory injection, tool declarations, session lifecycle, model↔tool loop (`run_turn`). No terminal coupling. |
|
|
101
|
-
| UI | `Samagotchi::TerminalUI` |
|
|
102
|
+
| UI | `Samagotchi::TerminalUI`, `TerminalUI::AttachLauncher` / `AttachedLoop` | `TerminalUI`: the `--non-interactive` one-shot host, its own `Engine`. The attached TUI (every interactive `chi`): a client of the session worker's Bridge, no `Engine`. |
|
|
102
103
|
| Model loops | `KernelLoop` (via `LLM::NativeBackend`), `LLM::ChatLoop` | The model↔tool loop: raw prompt, or a chat API (OpenAI chat, Anthropic Messages), chosen per host (see below). |
|
|
103
104
|
| Adapters | `Samagotchi::Client`, `LLM::OpenAIChat`, `LLM::AnthropicMessages`, `LLM::HTTP` | Raw-prompt servers, the OpenAI chat API, Anthropic's Messages API, and the HTTP they share. |
|
|
104
105
|
| Tools | `lib/samagotchi/tools/*` | Execute, read, edit, write, memory, task_*, web_fetch, delegate/delegate_result, ask_user_question, reminders, send_note, list_sessions, context_read, objectives, forget_outputs, find_tools/call_tool (the tool index), plus runtime/output-guardrails. |
|
|
@@ -109,29 +110,27 @@ terminal renders the events it gets over the Bridge.
|
|
|
109
110
|
|
|
110
111
|
## Entry points
|
|
111
112
|
|
|
112
|
-
- `LaunchMode.resolve` picks the terminal's mode
|
|
113
|
-
|
|
114
|
-
`session.shared: false`, `--non-interactive` and `--verbose` run the REPL. `--memory`
|
|
113
|
+
- `LaunchMode.resolve` picks the terminal's mode: `:attached` (plain `bin/chi`, `-p`,
|
|
114
|
+
`--resume ID`, `--attach ID`, `-v`) or `:one_shot` (`--non-interactive`). `--memory`
|
|
115
115
|
and `--mute` are session fields (`preloaded_memory_names`, `muted_memory_names`) the
|
|
116
116
|
worker reads when it builds its `Engine`; `MutedMemories` filters the prompt's index and
|
|
117
117
|
the kernel's `memory_read`.
|
|
118
|
-
-
|
|
119
|
-
|
|
120
|
-
- Attached (`bin/chi`, `--attach ID`, `--
|
|
118
|
+
- One-shot → builds `TerminalUI`: the `-p` prompt's one turn (a new session, `--resume ID`'s or
|
|
119
|
+
`chi scratch`'s) under an `OwnerLock` of kind `"tui"`; bare `--non-interactive` is a no-op.
|
|
120
|
+
- Attached (`bin/chi`, `-p`, `--attach ID`, `--resume ID`) → `TerminalUI::AttachLauncher`: no `Engine`
|
|
121
121
|
and no `OwnerLock`; finds or starts the session's worker and runs `TerminalUI::AttachedLoop`
|
|
122
122
|
as a client of its Bridge (`BridgeClient#follow`, `post_turn`, `post_command`, `cancel`, `answer`,
|
|
123
123
|
`dismiss_question`; opening the SSE connection answers `BridgeClient::StreamOpen::Opened`, `Refused` or `Unreachable`). The worker runs in the session's `working_directory`, so `!cmd` and
|
|
124
124
|
the tools don't depend on where the terminal attached from.
|
|
125
125
|
- `bin/chi web` → builds `Web::Server` (Rack+WEBrick on `127.0.0.1:4567`, `--port`/`SAMAGOTCHI_WEB_PORT`, `--open`;
|
|
126
126
|
`web.host: lan` adds a LAN address, `--new-token` replaces its access token).
|
|
127
|
-
- `bin/chi sessions {list,stop,restart,archive,unarchive,delete,prune,clean,stats}` (`SessionsCommand`; not the
|
|
127
|
+
- `bin/chi sessions {list,stop,restart,archive,unarchive,delete,prune,clean,stats}` (`SessionsCommand`; not the session's `SessionCommands`) → retention & ordering (`SessionRetention`, `updated_at desc`, dry-run, test-only); `stop` is `SessionManager.stop_session(wait:)`, which waits for the worker to release `owner.lock`; `delete` (`SessionDeleteCommand`) is `SessionManager.delete_session(stop:)`, which the TUI's `/exit --delete` and the web's `DELETE /api/sessions/:id` use too.
|
|
128
128
|
- Session commands (`chi send`, `answer`, `note`, a session's `context`, `sessions list/stop/restart/archive/unarchive/delete`) →
|
|
129
129
|
`SessionAccess` (`session_access.rb`): what they ask of a session goes through one backend object,
|
|
130
130
|
`SessionAccess::Local` (`session_access/local.rb`) on this machine's files and workers; a `--wait`'s pre-send state
|
|
131
131
|
(cursor, baseline, own turn) is `prepare_wait`'s `WaitStart`. `SessionAccess::Contract` lists which commands and flags
|
|
132
132
|
run against a remote and which on this machine only.
|
|
133
|
-
-
|
|
134
|
-
otherwise the session's `Worker` (`lib/samagotchi/worker.rb`, spawned by `SessionManager`) builds it.
|
|
133
|
+
- A one-shot's `TerminalUI` builds the `Engine` in process; otherwise the session's `Worker` (`lib/samagotchi/worker.rb`, spawned by `SessionManager`) builds it.
|
|
135
134
|
|
|
136
135
|
## How chi launches itself
|
|
137
136
|
|
|
@@ -193,7 +192,7 @@ log's last 4 MB). Rows: [configuration.md](configuration.md#checking-it-chi-home
|
|
|
193
192
|
## Session retention & ordering
|
|
194
193
|
|
|
195
194
|
- **Files:** `~/.local/state/samagotchi/sessions/<uuid>.json` + `<uuid>/input|output|owner.lock|bridge.json` (XDG-aware), and the `<uuid>/stopped` and `<uuid>/archived` marker files.
|
|
196
|
-
- **Single owner:** the process running a session's Engine (worker or
|
|
195
|
+
- **Single owner:** the process running a session's Engine (its worker, or a `--non-interactive` one-shot) holds a flock on `owner.lock` (`OwnerLock`); a second owner backs off, and the web answers 409 for a session a one-shot owns.
|
|
197
196
|
- **Status:** `status` is turn state (`idle`/`running`); liveness is the lock. A stop is the `<uuid>/stopped` marker (`Session.mark_stopped`; a resume removes it), which `Session.load` and `.summary_from_file` read as status `stopped`, so no other process rewrites a worker's session file.
|
|
198
197
|
- **Retention:** 14 days / 500 cap (env `SAMAGOTCHI_SESSION_RETENTION_DAYS`/`MAX_COUNT`, optional `KEEP_STATUS`), live-owner guard, only when `*.json` present; lazy sweep at most once per `session.sweep_interval_hours` (default 24) from the session hub's full-probe tick (and on `GET /api/sessions`, which the page no longer calls), manual via `bin/chi sessions prune --dry-run`.
|
|
199
198
|
- **Ordering:** `Session.list(sort:,order:,limit:,offset:)` and `GET /api/sessions?sort=&order=&limit=&offset=` default `updated_at desc`; Web UI sort/filter/pagination.
|
|
@@ -207,22 +206,20 @@ agent logic and can be used without any terminal rendering; the UI is a thin lay
|
|
|
207
206
|
| Layer | Class | Responsibility |
|
|
208
207
|
|-------|-------|----------------|
|
|
209
208
|
| Core | `Samagotchi::Engine` | System prompt, memory injection, tool declarations, session lifecycle, the model↔tool loop (`run_turn`). No terminal coupling. |
|
|
210
|
-
| UI | `Samagotchi::TerminalUI` |
|
|
209
|
+
| UI | `Samagotchi::TerminalUI`, `TerminalUI::AttachLauncher` / `AttachedLoop` | The attached TUI: rendering (ANSI, spinner, status line) and the terminal's own commands over the worker's Bridge. `TerminalUI`: the `--non-interactive` one-shot host, which delegates all core work to its `Engine`. |
|
|
211
210
|
| Model loops and adapters | `KernelLoop`, `LLM::ChatLoop`, `Samagotchi::Client`, `LLM::OpenAIChat`, `LLM::AnthropicMessages`, `LLM::HTTP` | The model↔tool loops and the HTTP adapters they talk through (see "Model loops and adapters"). |
|
|
212
211
|
| Bridge (SSE/HTTP) ([internals/bridge.md](internals/bridge.md)) | `Samagotchi::Bridge`, `SessionManager` | The **single live client transport**: an SSE read stream + HTTP POST turn/cancel/answer surface that attaches to a worker's existing `Engine` via `Engine#subscribe`. Every session worker starts it (bound `127.0.0.1`, localhost-only), and it answers only a request that names its secret (`X-Chi-Bridge`, from the worker's 0600 `bridge.json`). |
|
|
213
212
|
| Web (Rack) | `Samagotchi::Web::App`, `SessionManager` | Single-port `127.0.0.1:4567` control plane via `rack`+`webrick` (serve `index.html` + `/api/*`). With `web.host: lan` (or one of this machine's IPv4 addresses) the server also listens on that address, where every request needs the access token, this machine's on loopback too (`App#token_gate`: the `chi_token` cookie, on loopback a value derived from the token (`Token.loopback_cookie`), or `Authorization: Bearer`; chi web prints a `Local:` link with a one-time code; [CLI](cli.md#chi-web-on-your-phone)). Experimental: behind an auth proxy (`web.public_url`, `Web::ProxyGate`) every request needs the token, this machine's too, and one that came through the proxy also the configured identity header for the owner ([configuration.md](configuration.md#behind-an-auth-proxy)). A request goes through, in order: the Host check (`local_host_header?`: loopback names, the LAN address, the public host), the cross-site check (`cross_site?`: Origin this server's or the public origin, no `Sec-Fetch-Site` cross-site/same-site on a POST or `/api`), the frame check (`framed?`: a frame/embed/object load from another origin, 403 `framed`; every HTML answer carries `X-Frame-Options: DENY` and `frame-ancestors 'none'; frame-src 'none'`, so a page's own frames can't navigate either), the token gate (`token_gate` → `principal_for`, `proxied_principal` for a proxied request; the `?token=` / `POST /api/token` trade sets the cookie, `__Host-chi_token` through the proxy; with `web.public_url` a direct request takes Bearer only, and with it or LAN mode chi's token-less `/api/info` probe gets `probe_info`), then the route's permission (`Principal#permits?`). It talks to each worker's Bridge (`App#relay`, `bridge_get`; `/stream` proxies it) and reads the session files; no `Engine` of its own. Markdown (`web.markdown`) is rendered here only (`Web::MarkdownRenderer`): the answers it serves, card bodies, and `POST /api/render` for a text the page already shows. `App` keeps the gate, `ROUTES` and the helpers the handlers share; its handlers live in mixins it includes, each a `Web::` module with private methods: `SessionAccessRoutes` (`web/session_access_routes.rb`), `ParticipantAccess` (`web/participant_access.rb`), `ParticipantsAdmin` (`web/participants_admin.rb`), `ParticipantReview` (`web/participant_review.rb`), `MessageDisplay` (`web/message_display.rb`), `SessionShow` (`web/session_show.rb`), `SessionCreate` (`web/session_create.rb`), `SessionControl` (`web/session_control.rb`), `PushRoutes` (`web/push_routes.rb`) and `ProxyGate` (`web/proxy_gate.rb`). Exact paths in `GATE_FREE_PATHS` are served before the token gate (the fragment trade's script, and the Home Screen app's manifest, service worker and icons). `bin/chi web` entrypoint. |
|
|
214
213
|
| Sessions | `Samagotchi::Session`, `SessionManager` | File-based `~/.local/state/samagotchi/sessions/<uuid>.json` + sidecar `input/`/`output/`; retention (14d/500) + ordering (`updated_at desc`). |
|
|
215
214
|
|
|
216
|
-
- `bin/chi` in
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
then either exits (`--non-interactive`) or drops into the REPL carrying the
|
|
220
|
-
post-turn conversation.
|
|
215
|
+
- `bin/chi` in one-shot mode (`--non-interactive`, see `LaunchMode` above) builds `TerminalUI`.
|
|
216
|
+
`TerminalUI#run` builds the session (new, `--resume`'s or `chi scratch`'s), runs the `-p`
|
|
217
|
+
turn with the answer alone on stdout and the session lines on stderr, and exits.
|
|
221
218
|
- A session's `Worker` (spawned by `SessionManager`) builds its `Engine` (no terminal rendering).
|
|
222
|
-
- `bin/chi` in attached mode (
|
|
219
|
+
- `bin/chi` in attached mode (every interactive run) builds no `Engine`: `TerminalUI::AttachLauncher` finds
|
|
223
220
|
or starts the worker, and `TerminalUI::AttachedLoop` is a client of its Bridge
|
|
224
221
|
(`BridgeClient#follow` for events, `post_turn`/`cancel`/`answer` for input). It
|
|
225
|
-
renders through
|
|
222
|
+
renders through `EventRenderer`, on an `AttachedView` that
|
|
226
223
|
draws on a `Screen`: a live region at the bottom of the terminal (activity row,
|
|
227
224
|
prompt, status/notes/hints) under normal scrollback. From a turn's 3rd tool call the
|
|
228
225
|
activity slot gets a second, dim row: the turn's tool tally (`TurnTally`, seeded from the
|
|
@@ -449,7 +446,7 @@ calling the `SessionAccess::Local` operation of the same name, so a remote backe
|
|
|
449
446
|
token-gated like the rest (LAN mode: `Authorization: Bearer`), and one that takes an id is a `:session` route (an id
|
|
450
447
|
that isn't one, or no such session: 404 `not_found`). Every `ROUTES` row also names who may use it, checked against
|
|
451
448
|
the request's `Web::Principal` (`App#principal_for`): `owner`, or `participant` for a route a participant may use too.
|
|
452
|
-
A participant (a `
|
|
449
|
+
A participant (a `Grants` token for one session) may use the page (`GET /`), `GET /api/info`, their session's
|
|
453
450
|
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
|
|
454
451
|
[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
|
|
455
452
|
model: [internals/remote-sessions.md](internals/remote-sessions.md).
|
|
@@ -472,7 +469,7 @@ model: [internals/remote-sessions.md](internals/remote-sessions.md).
|
|
|
472
469
|
| `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 |
|
|
473
470
|
| `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) | |
|
|
474
471
|
| `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 | |
|
|
475
|
-
| `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` (`
|
|
472
|
+
| `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` (`Session::SCRATCH_REFUSED`); 504 `worker_timeout` (`LaunchMode::BRIDGE_LATE`); 400 `invalid_wait`; 404 |
|
|
476
473
|
| `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 |
|
|
477
474
|
| `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` |
|
|
478
475
|
| `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 |
|
|
@@ -593,7 +590,7 @@ credit held by in-flight requests, retried after 20 s), `OutOfCredits` (any othe
|
|
|
593
590
|
402), `ServerError`, `AuthError`, `BadRequest` (`context_overflow?`) or `ProtocolError`. Engine keeps the turn's
|
|
594
591
|
conversation (the prompt plus completed tool iterations) and emits
|
|
595
592
|
`:turn_failed` with `error_kind:`, `retryable:`, `host:` and a one-line
|
|
596
|
-
`summary:`, which the
|
|
593
|
+
`summary:`, which the attached TUI, a one-shot's stderr and the web show.
|
|
597
594
|
|
|
598
595
|
**Usage and models.** A turn's token counts are `SessionMetrics`', read from the
|
|
599
596
|
stream events (server counts, else a chars/4 estimate). Model lists are `LLM::ModelInfo`
|
|
@@ -628,15 +625,15 @@ A map of the parts the overview above doesn't name (paths under `lib/samagotchi/
|
|
|
628
625
|
| Worker link | `worker_link.rb` (`WorkerLink`: `Live`, `Starting`, `Leaving`, `Skew`, `Stale`, `None`), `worker_sidecar.rb` (`WorkerSidecar`) | Whether a session's worker can be reached and the client for it: every Bridge client asks `WorkerLink.resolve` / `.client` / `.await` (*Worker link* above); `WorkerSidecar` is the `bridge.json` it reads. |
|
|
629
626
|
| Session access | `session_access.rb` (`SessionAccess`: `NewSession`, `SetupWords`, `WaitStart`, `SessionView`, `Created`, `ContextEntry`, `ContextPush`, the errors `RemoteFailed`, `RemoteTooOld`, `Refused`, `BadRequest`), `session_access/local.rb` (`SessionAccess::Local`), `session_access/remote.rb` (`SessionAccess::Remote`), `session_access/http_transport.rb`, `session_access/web_bridge_client.rb` (`SessionAccess::WebBridgeClient`, a remote attach's worker through chi web), `session_access/dispatch.rb` (`SessionAccess::Dispatch`), `session_access/contract.rb` (`SessionAccess::Contract`), `remotes.rb` (`Remotes`), `remote_command.rb` (`chi remote`), `web/session_access_routes.rb` (`Web::SessionAccessRoutes`) | The seam between the session commands and where the sessions live: `Local` is this machine's (the `SessionManager`, `ReplyWait`, `NoteDelivery`, `ContextSources` and Bridge calls the commands made); `Remote` is another machine's, the same methods as requests to its chi web's routes of the same name (Bearer token, `HTTPTransport`), results as Local's value objects and refusals as the classes Local's callees raise, built from the fields chi web sends; waits and prefixes run on the server, a wait asked again per 30 s slice. `Dispatch` is bin/chi's front for `--remote NAME` / `CHI_REMOTE` (taken off before the subcommand lookup): it refuses what the contract table lists `local_only` (exit 2, one message) and runs the command with a `Remote` on the `Remotes` entry (`$XDG_CONFIG_HOME/samagotchi/remotes.yml`, 0600). chi web's routes answer from `Local` (*chi web's routes for the session commands* above). Checks that read this machine's config or files (vision, parent answers, the cwd's project, `SAMAGOTCHI_PARENT_SESSION`) stay in the commands and are skipped under a `Remote`, where chi web's create, turn and wait routes run the vision, parent-answers and busy checks with the server's config and the worker checks a parent's answer. See [internals/remote-sessions.md](internals/remote-sessions.md). |
|
|
630
627
|
| Idle work | `idle_scheduler.rb` (`IdleScheduler`), `idle_recap.rb` (`IdleRecap`), `idle_reminders.rb` | One poller for an idle session's recap and reminders. |
|
|
631
|
-
| Attached context | `context_sources.rb` (`ContextSources`), `context_poller.rb`, `context_absorber.rb`, `context_candidates.rb` (`ContextCandidates`: proposed sources, attached on the owner's mention at a turn's start, before the worker's absorb, or in
|
|
628
|
+
| Attached context | `context_sources.rb` (`ContextSources`), `context_poller.rb`, `context_absorber.rb`, `context_candidates.rb` (`ContextCandidates`: proposed sources, attached on the owner's mention at a turn's start, before the worker's absorb, or in a one-shot's turn; `owner_prompt.rb` decides whose words count), `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)). |
|
|
632
629
|
| 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)). |
|
|
633
630
|
| 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)). |
|
|
634
631
|
| 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)). |
|
|
635
632
|
| chi web's routes | `web/app.rb` (`Web::App`: the gate, `ROUTES`, the shared helpers), `web/message_display.rb` (`Web::MessageDisplay`: saved messages and cards as display items, `POST /api/render`), `web/session_show.rb` (`Web::SessionShow`: `GET /api/sessions/:id` with its `cards`, `tail` and `timing` re-reads, `GET …/output`, the timing payload), `web/session_create.rb` (`Web::SessionCreate`: `POST /api/sessions`, a continue, a review fork), `web/session_control.rb` (`Web::SessionControl`: answer, command, turn and images, cancel, stop, restart, archive, delete), `web/push_routes.rb` (`Web::PushRoutes`: `/api/push`, its subscriptions, prefs, test and presence), `web/block_renderer_routes.rb` (`Web::BlockRendererRoutes`: `GET /api/block_renderers` and one renderer's file, [internals/block-renderers.md](internals/block-renderers.md)) | The handlers `App`'s `ROUTES` name, in the file that holds their kind; each mixin is included in `App` and reads its helpers and ivars, so `app.send(:handle_show, …)` and the specs that stub `App` methods work as before. |
|
|
636
|
-
| Web Push | `web/push.rb` (`Web::Push`: `Runtime`, `preflight!`, the `web.push.*` settings), `web/push/crypto.rb` (`Push::Crypto`: RFC 8291 aes128gcm, RFC 8292 VAPID), `web/push/store.rb` (`Push::Store`, `Store::Sub`: `web-push/vapid.pem`, `subscriptions.json`), `web/push/client.rb` (`Push::Client`, `Client::Result`), `web/push/attention.rb` (`Push::Attention`: notify.js's rules in Ruby, shared cases in `spec/fixtures/attention_cases.json`), `web/push/presence.rb` (`Push::Presence`: the pages' "in front" reports), `web/push/notifier.rb` (`Push::Notifier`, a hub subscriber with a sender thread), `web/push_routes.rb` (`Web::PushRoutes`), `
|
|
633
|
+
| Web Push | `web/push.rb` (`Web::Push`: `Runtime`, `preflight!`, the `web.push.*` settings), `web/push/crypto.rb` (`Push::Crypto`: RFC 8291 aes128gcm, RFC 8292 VAPID), `web/push/store.rb` (`Push::Store`, `Store::Sub`: `web-push/vapid.pem`, `subscriptions.json`), `web/push/client.rb` (`Push::Client`, `Client::Result`), `web/push/attention.rb` (`Push::Attention`: notify.js's rules in Ruby, shared cases in `spec/fixtures/attention_cases.json`), `web/push/presence.rb` (`Push::Presence`: the pages' "in front" reports), `web/push/notifier.rb` (`Push::Notifier`, a hub subscriber with a sender thread), `web/push_routes.rb` (`Web::PushRoutes`), `private_files.rb` (`PrivateFiles`, shared with the grants), `public/sw.js`, `public/push.js`, `public/manifest.webmanifest` | chi web pushes what the tab notifies for (approvals, questions, step-limit questions, cards, failed turns; done turns to a device that asks) to the owner's Home Screen app over an https public URL: the Notifier turns the hub's new attentions into show pushes after a hold, unless a chi web page is in front, at most 30 an hour, with one retry and a restart catch-up (`sent.json`) (each carries the keys still open, and the badge; no close push: every push shows a notification); the bell (`notifications.js`) subscribes the device, and the service worker and the page close the answered ones. See [internals/web-push.md](internals/web-push.md). |
|
|
637
634
|
| chi web's updates | `web/update_check.rb` (`Web::UpdateCheck`), `web/update_ops.rb` (`Web::UpdateOps`), `web/auto_update.rb` (`Web::AutoUpdate`) | The RubyGems checker and its `update_check.json`; the install, worker-restart and chi-web-restart operations (one at a time) the owner's routes and AutoUpdate share; `update.auto: install`'s thread (install when no turn runs, then idle workers and chi web). See [configuration](configuration.md#unattended-updates-updateauto). |
|
|
638
635
|
| Behind an auth proxy | `web/public_url.rb` (`Web::PublicUrl`: `web.public_url`, `web.proxy.identity_header`, `web.proxy.owner`), `web/proxy_gate.rb` (`Web::ProxyGate`, mixed into App; `RawHeaderNames`, prepended to WEBrick's request by `Web::Server`), `web/link_codes.rb` (`Web::LinkCodes`: the links' one-time codes), `proxy_trace.rb` (`ProxyTrace`), `web/public/token_fragment.js`, `web/public/auth_lost.js`, `web/tunnel.rb` + `web/tunnel/tailscale.rb` (`Web::Tunnel`, `Web::Tunnel::Tailscale`: `chi web --tunnel tailscale`, Tailscale Serve set for one run and the PublicUrl it makes) | Experimental: chi web behind an auth proxy. Every request needs the token; a proxied one's owner also needs the identity header (one clean value under one name); the `__Host-` cookie, an absolute `Location` from the public URL, https links with a one-time code in the fragment; the page's lost-sign-in banner. Bridges refuse proxy traces ([configuration.md](configuration.md#behind-an-auth-proxy)). |
|
|
639
|
-
| Participants | `participants.rb` (`Participants`, the `participants.json` sidecar), `share_command.rb` (`ShareCommand`, `chi share`), `session_participants.rb`, `participants_watch.rb`, `
|
|
636
|
+
| Participants | `participants.rb` (`Participants`, the `participants.json` sidecar), `share_command.rb` (`ShareCommand`, `chi share`), `session_participants.rb`, `participants_watch.rb`, `grants.rb` (`Grants`), `participant_exposure.rb` (`ParticipantExposure`), `web/principal.rb` (`Web::Principal`), `web/participant_access.rb` (`Web::ParticipantAccess`: the participant's gate and routes), `web/participants_admin.rb` (`Web::ParticipantsAdmin`: the owner's invite, list, revoke, cap and Apply routes), `web/participant_review.rb` + `web/participant_view.rb` (`Web::ParticipantReview`, `Web::ParticipantView`), `redact.rb` (`Redact`, `Redact::Viewer`: the redaction every string a participant gets passes, also the Engine's Verify quote check), `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)). |
|
|
640
637
|
| 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'. |
|
|
641
638
|
| Images / vision | `image_store.rb` (`ImageStore`), `vision_support.rb`, `vision_context.rb` | Stored prompt images and whether a model takes them. |
|
|
642
639
|
| 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/broadcast.md
CHANGED
|
@@ -24,7 +24,7 @@ selection: see [Desktop helper](desktop.md#everyone-it-concerns).
|
|
|
24
24
|
|
|
25
25
|
The recipients are your own sessions, in every project:
|
|
26
26
|
|
|
27
|
-
- a worker or a chi
|
|
27
|
+
- a worker or a one-turn `chi --non-interactive` run runs them now, or their last turn ended within
|
|
28
28
|
`broadcast.active_hours` (default 8). A session with no turn yet counts
|
|
29
29
|
by its last save;
|
|
30
30
|
- not a delegate child: its parent relays what concerns it (the
|
|
@@ -32,8 +32,8 @@ The recipients are your own sessions, in every project:
|
|
|
32
32
|
- not a `chi scratch` session, not an archived one, and not a test run
|
|
33
33
|
(unless `chi broadcast` itself runs as one, `SAMAGOTCHI_ENV=test`).
|
|
34
34
|
|
|
35
|
-
A session
|
|
36
|
-
listed as skipped, "
|
|
35
|
+
A session a one-turn `chi -p … --non-interactive` run has can't take notes: it
|
|
36
|
+
is listed as skipped, "in use by a one-turn chi --non-interactive run or an older chi's terminal".
|
|
37
37
|
|
|
38
38
|
## Tags: which sessions it concerns
|
|
39
39
|
|
|
@@ -101,7 +101,7 @@ broadcast b-7f3a1c9e "payments API returns 500 since 14:00 (PAY-123)"
|
|
|
101
101
|
5e1f0a77 delivered model: yes (p 0.91)
|
|
102
102
|
77aa0b3c delivered unchecked: triage deadline
|
|
103
103
|
c0ffee12 skipped model: no
|
|
104
|
-
d00dad00 skipped
|
|
104
|
+
d00dad00 skipped in use by a one-turn chi --non-interactive run or an older chi's terminal
|
|
105
105
|
delivered 4 · skipped 2 · 1 unchecked: triage deadline
|
|
106
106
|
```
|
|
107
107
|
|
|
@@ -193,7 +193,7 @@ any shell command that runs it, once at a time, and only you may allow it
|
|
|
193
193
|
|
|
194
194
|
| Setting | Default | What it does |
|
|
195
195
|
|---|---|---|
|
|
196
|
-
| `broadcast.active_hours` | `8` | A session with no worker or
|
|
196
|
+
| `broadcast.active_hours` | `8` | A session with no worker or one-turn run counts when its last turn ended within this many hours. |
|
|
197
197
|
| `broadcast.ticket_pattern` | `\b[A-Z][A-Z0-9]+-\d+\b` | A Ruby regex for ticket ids; an invalid one warns and the default is used. |
|
|
198
198
|
| `broadcast.triage_model` | recap's, else `default.model` | The triage model, a model ref (`box:qwen3.5-9b`). |
|
|
199
199
|
| `broadcast.triage_host_ref` | none | A `hosts:` name for it. |
|
data/docs/bundle-authoring.md
CHANGED
|
@@ -119,7 +119,7 @@ end
|
|
|
119
119
|
`questions` record effects; `bundle.answer` / `model_answers` provide `ask_user` / `ask_model` answers.
|
|
120
120
|
`bundle.judgments` answers `ctx.judge` and records its asks and runs (each judgment is on in the kit, off in chi until the user sets it);
|
|
121
121
|
`bundle.http.stub` stubs `ctx.http`; `bundle.question_asked(text, question:)` fires `:question_asked` and returns
|
|
122
|
-
the refs; `sandbox.config`, `sandbox.fake_executable` and `bundle.install` cover setup.
|
|
122
|
+
the refs; `sandbox.config`, `sandbox.fake_executable` and `bundle.install` cover setup. The kit's `ctx.declared_tools` is every tool, as on `full`; in chi a `chi.tool` is declared only on `full` and by a kit's `add:`, and on `lean` or `child` the model reaches it with `find_tools` (`chi/<name>`) and `call_tool` ([Toolkits](configuration.md#toolkits)), so `bundle.set(declared_tools: %w[read])` tests that case and the tool's description should be easy to search.
|
|
123
123
|
The full list is in [plugins.md, Testing a bundle](plugins.md#testing-a-bundle-experimental). The kit is
|
|
124
124
|
experimental (it may change before chi 1.0). A failing matcher that meets `ENV` prints `#<the process ENV, contents not shown>`, not your keys. If a test needs a fake of chi's own (a stub of a `Samagotchi::`
|
|
125
125
|
class, a hand-built `ctx`), that is a gap in the kit: say so to the user rather than reaching into chi.
|