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
data/docs/configuration.md
CHANGED
|
@@ -64,11 +64,9 @@ model_aliases:
|
|
|
64
64
|
small: your-small-model-id
|
|
65
65
|
tiny: small-box:your-small-model-id # alias may be bare or host:model (hybrid)
|
|
66
66
|
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
# the in-process REPL; --no-shared does for one run.
|
|
67
|
+
# A session runs in a background worker; chi attaches to it, and the web UI
|
|
68
|
+
# shares it.
|
|
70
69
|
session:
|
|
71
|
-
shared: true
|
|
72
70
|
# idle_exit_minutes: 30 # an unused worker exits after this (0 = never)
|
|
73
71
|
# project_env: mise # a worker's tools get its folder's `mise env` (off: the starter's env)
|
|
74
72
|
# keep_empty: false # true keeps sessions nothing happened in (default: deleted when left)
|
|
@@ -172,7 +170,6 @@ or `chi web` restart.
|
|
|
172
170
|
| `system_one.host`, `system_one.model`, `system_one.timeout_ms`, `system_one.runs`, `system_one.judgments` | Next tool call or approval | Each ask reads them. |
|
|
173
171
|
| `hooks`, `bundles` | Worker start | Hooks, plugins and their bundle settings load with the engine. |
|
|
174
172
|
| `recap.*` | Worker start | The engine builds its recap job at start, and again when it binds a session with its own `recap.enabled` or `recap.sentences` (`--set`, see "Per-session settings") and when `/recap on\|off\|default` changes the session's own value. |
|
|
175
|
-
| `session.shared` | Next `chi` invocation | It selects attached-worker or in-process mode for that invocation. |
|
|
176
173
|
| `session.idle_exit_minutes`, `session.project_env`, `session.carry_last_answer`, `session.keep_empty`, `session.max_children`, `session.delegate_reports`, `session.task_wakes`, `session.max_wakes` | Worker start or next matching lifecycle decision | Existing workers keep their idle-exit timer and project env; the other values are read for the next corresponding decision. |
|
|
177
174
|
| `session.retention_*`, `session.max_count`, `session.keep_status`, `session.sweep_interval_hours` | Next retention sweep | `chi web` sweeps on its configured schedule and on `GET /api/sessions`; `chi sessions prune` uses the values for that invocation. The sweep interval is checked on the next hub tick. |
|
|
178
175
|
| `web.*` except `web.participants.*` | `chi web` start | Restart `chi web` to change its bind address, port or page settings. |
|
|
@@ -355,10 +352,8 @@ For models on that host, chi uses the chat loop (its own OpenAI chat adapter): i
|
|
|
355
352
|
takes the OpenAI base (`url:`, else `http://HOST:PORT/v1`) and streams messages plus
|
|
356
353
|
function schemas from `/v1/chat/completions`; the model's reasoning (`reasoning_content`)
|
|
357
354
|
shows as thinking. This works with Splash and with llama.cpp servers that
|
|
358
|
-
expose the OpenAI-compatible chat endpoint.
|
|
359
|
-
|
|
360
|
-
bodies are not logged. The interactive REPL, `--prompt`,
|
|
361
|
-
workers, and resumed sessions all use the loop of the model's host. Hosts without
|
|
355
|
+
expose the OpenAI-compatible chat endpoint. Workers, `--non-interactive` one-shots and resumed
|
|
356
|
+
sessions all use the loop of the model's host. Hosts without
|
|
362
357
|
a chat `api:` keep using llama.cpp's native `/completion`.
|
|
363
358
|
|
|
364
359
|
### API keys from the Keychain
|
|
@@ -655,7 +650,7 @@ models:
|
|
|
655
650
|
it, a delegate child starts without it (it runs at its model's level). Set it with:
|
|
656
651
|
- `/thinking LEVEL` in the session (`/thinking default` unsets it, `/thinking` alone shows the level and where it
|
|
657
652
|
came from);
|
|
658
|
-
- `chi --thinking LEVEL` on a run that starts or resumes a session (
|
|
653
|
+
- `chi --thinking LEVEL` on a run that starts or resumes a session (plain `chi`, `-p`, `--resume`, an attached one);
|
|
659
654
|
- `chi send --new --thinking LEVEL`;
|
|
660
655
|
- the web's ⚙ panel in the session bar (its Thinking row), and the start page's ⚙ panel (`POST /api/sessions` takes `"thinking": "low"`;
|
|
661
656
|
anything that isn't a level answers `400 invalid_thinking`).
|
|
@@ -849,15 +844,15 @@ first: an unknown host or a bad alias is an error at once, not a question.
|
|
|
849
844
|
a full-prefix change also says preserved thinking in replies is dropped from then on.
|
|
850
845
|
- `Nothing changed yet: send /thinking low --yes to apply.` names the line that applies it.
|
|
851
846
|
- `--yes` (or `-y`, anywhere after the command) applies it at once.
|
|
852
|
-
- The
|
|
847
|
+
- The attached terminal puts the `--yes` line in the next input: Enter applies it.
|
|
853
848
|
- The web's reply bubble has an **Apply (restarts the cache)** button. The session bar's ⚙ panel is already an
|
|
854
849
|
explicit choice: a tap sends the `--yes` line, and its note says what a change costs. Command bubbles are page-only, so
|
|
855
850
|
the button is gone after a reload.
|
|
856
851
|
- Only a person at a web tab or a chi TUI is asked. `chi send ID "/thinking low"`,
|
|
857
|
-
`chi -p "/thinking low" --non-interactive`,
|
|
852
|
+
`chi -p "/thinking low" --non-interactive`, a delegate, a plugin and a card's action
|
|
858
853
|
apply directly, since nobody would see the question; so do the start flags (`chi --thinking`, `--model` and
|
|
859
854
|
`--llm-context` on a resumed or attached session send `/thinking LEVEL --yes`, `/model NAME --yes` and
|
|
860
|
-
`/llm-context … --yes`).
|
|
855
|
+
`/llm-context … --yes`).
|
|
861
856
|
|
|
862
857
|
## Llama HTTP Timeouts
|
|
863
858
|
|
|
@@ -1107,7 +1102,7 @@ retry:
|
|
|
1107
1102
|
`retry.empty_answer` is a different thing: the request worked, but the model's answer had no visible text and no
|
|
1108
1103
|
tool calls (thinking only, or nothing; a thinking loop cut by the provider's output cap looks like this). chi then
|
|
1109
1104
|
asks again in the same turn with a hidden note ("your last reply had no visible answer…"), at `temperature: 0.6`
|
|
1110
|
-
unless `sampling:` sets one; the
|
|
1105
|
+
unless `sampling:` sets one; the terminal prints `↻ empty answer, asking again (1/1)` and the web shows it as a row of
|
|
1111
1106
|
the step. `retry.empty_answer` (default `1`, at most `3`, env `SAMAGOTCHI_RETRY_EMPTY_ANSWER`, no CLI flag) is how
|
|
1112
1107
|
many times per turn; `0` ends the turn at the empty answer as before. An answer cut because the context is full
|
|
1113
1108
|
(90 % or more) is not retried. When the retries run out the turn ends with no answer: every UI shows one muted line,
|
|
@@ -1209,8 +1204,8 @@ The turn's prompt and its completed tool calls stay in the session.
|
|
|
1209
1204
|
|
|
1210
1205
|
## Debug Log File
|
|
1211
1206
|
|
|
1212
|
-
Every `chi` process (the
|
|
1213
|
-
workers, `chi web`) appends tagged records to one log file, so you can see
|
|
1207
|
+
Every `chi` process (the attached terminal, a `--non-interactive` one-shot,
|
|
1208
|
+
the background workers, `chi web`) appends tagged records to one log file, so you can see
|
|
1214
1209
|
what happened in a session, what went over the wire and why something
|
|
1215
1210
|
failed, without `--verbose`.
|
|
1216
1211
|
|
|
@@ -1248,9 +1243,10 @@ One record is one line, plus indented payload lines at debug level:
|
|
|
1248
1243
|
space, quote or `=` is a JSON string, so a record never spans lines;
|
|
1249
1244
|
control characters (terminal colours in tool output) are escaped.
|
|
1250
1245
|
- Tags: `turn` (a session's event trail), `http` (model requests),
|
|
1251
|
-
`worker`, `bridge`, `web`, `attached`, `
|
|
1246
|
+
`worker`, `bridge`, `web`, `attached`, `idle`, `recap`, `hooks`,
|
|
1252
1247
|
`plugins`, `guardrails`, `config`, `memory`, `model` (debug dumps), `context` (attached context fetches),
|
|
1253
|
-
`broadcast` (chi broadcast's triage)
|
|
1248
|
+
`broadcast` (chi broadcast's triage), `toolkit`, `system_one`, `prompt` (a system prompt section left out
|
|
1249
|
+
after an error).
|
|
1254
1250
|
- The format is parsed by `Samagotchi::LogLine` (`parse`, `each_record`);
|
|
1255
1251
|
keep tools that read it on that parser.
|
|
1256
1252
|
|
|
@@ -1259,7 +1255,9 @@ What each level adds:
|
|
|
1259
1255
|
- `error`: crashes (a worker, a bridge connection, the idle scheduler, a
|
|
1260
1256
|
recap) with the first 20 backtrace frames; failed model requests.
|
|
1261
1257
|
- `warn`: retries (429, 5xx, network), failed turns, hook and guardrail
|
|
1262
|
-
problems, config warnings
|
|
1258
|
+
problems, config warnings, and a fallback after an error (a prompt section or a
|
|
1259
|
+
Bridge reply chi left out, `Degrade`): once per process for each kind, later ones
|
|
1260
|
+
at `debug`. Warnings `chi` prints on stderr are logged too,
|
|
1263
1261
|
with the same text as `msg=`; a worker's (its stderr goes nowhere) now
|
|
1264
1262
|
only reach the file.
|
|
1265
1263
|
- `info`: turns, generations and tool calls with sizes and times (never the
|
|
@@ -1270,8 +1268,12 @@ What each level adds:
|
|
|
1270
1268
|
and results, context status), probes and model lists, every web API and
|
|
1271
1269
|
bridge request.
|
|
1272
1270
|
|
|
1273
|
-
`-v`/`--verbose`
|
|
1274
|
-
|
|
1271
|
+
`-v`/`--verbose` logs at `debug`, in this chi and in a worker it starts
|
|
1272
|
+
(a worker that was already running keeps its own level, and a one-line note
|
|
1273
|
+
says so: one `chi web` started may log to another file). The attached
|
|
1274
|
+
terminal shows the session's records (by `sid`) dim, above the prompt, as
|
|
1275
|
+
they come; a `--non-interactive` one-shot prints every record to stderr
|
|
1276
|
+
as well.
|
|
1275
1277
|
|
|
1276
1278
|
### Rotation and secrets
|
|
1277
1279
|
|
|
@@ -1476,8 +1478,8 @@ work tree top when inside it, else with `~`.
|
|
|
1476
1478
|
every request`). The files are saved in the session (`appended_prompt_files`),
|
|
1477
1479
|
so a respawned worker builds the same prompt, and a fork or a continue keeps
|
|
1478
1480
|
them; a delegate child doesn't. With `--attach` or `--resume` the flag is
|
|
1479
|
-
ignored with one line, as `--memory` is; a
|
|
1480
|
-
adds them for that run only.
|
|
1481
|
+
ignored with one line, as `--memory` is; a `--resume ID --non-interactive`
|
|
1482
|
+
one-shot adds them for that run only.
|
|
1481
1483
|
- The files' text is read at each prompt build, as AGENTS.md's is: an edit is
|
|
1482
1484
|
picked up by a new process or a rebuild (`/model`). `/stats` lists the files
|
|
1483
1485
|
in the prompt on its `prompt files:` line.
|
|
@@ -1501,17 +1503,18 @@ toolkit:
|
|
|
1501
1503
|
|
|
1502
1504
|
| toolkit | declares |
|
|
1503
1505
|
|---|---|
|
|
1504
|
-
| `full` | every built-in; the default |
|
|
1506
|
+
| `full` | every built-in and every plugin tool (a bundle's `chi.tool`, such as `web_search`); the default |
|
|
1505
1507
|
| `lean` | `execute`, `read`, `write`, `edit`, `memory_read`, `memory_write`, `task_create`, `task_wait`, `task_get`, `task_list`, `task_stop`, `ask_user_question`, `web_fetch`, `context_read` |
|
|
1506
1508
|
| `child` | `lean` plus `send_note`, without `memory_write`, `delegate`, `delegate_result` |
|
|
1507
1509
|
| `coordinator` | `lean` plus `delegate`, `delegate_result`, `list_sessions`, `objectives` (declared by the coordinator bundle) |
|
|
1508
1510
|
| `coding` | `child`, with the prompt text `- Be concise in your responses` / `- Show file paths clearly when working with files` (declared by the coordinator bundle, for build and fix children) |
|
|
1509
1511
|
| `review` | `read`, `memory_read` (not `forget_outputs`, even under the `forget` layer): what a [participant's](internals/participants.md#turns) turn may call, whatever the session declares (the review boundary, [guardrails](guardrails.md#a-participants-review-boundary)). Reserved: `toolkit.kits.review` is refused ("review is reserved for participants' turns"), and a bundle can't declare it. A session may run on it too, as any kit |
|
|
1510
1512
|
|
|
1511
|
-
- **Plugin tools**
|
|
1512
|
-
|
|
1513
|
-
`
|
|
1514
|
-
|
|
1513
|
+
- **Plugin tools** (a bundle's `chi.tool`, such as web-search's `web_search`) follow a kit's list as built-ins do:
|
|
1514
|
+
`full` (and every kit on `base: full`, the `kits:` default) declares them, another kit leaves them **reachable**
|
|
1515
|
+
(below) unless its `remove:` names them, and `add:` declares one (`search: { base: lean, add: [web_search] }`).
|
|
1516
|
+
`forget_outputs` is declared in every toolkit under the `forget` layer, unless removed. A `kits:` entry takes
|
|
1517
|
+
`base:` (default `full`), `add:`, `remove:`, `prompt:`, `groups:` and `description:` (any other key, a typo or a `tools:` list, is ignored with a warning); it can't reuse a built-in's name
|
|
1515
1518
|
(`review`'s least of all: it is the participants' boundary). A bundle declares its own with `chi.toolkit`
|
|
1516
1519
|
([plugins](plugins.md#chitoolkitname-base-add--remove--prompt-nil-groups-nil-description-nil)).
|
|
1517
1520
|
`description:` is optional classifier input, up to 300 characters; it is not included in the session prompt.
|
|
@@ -1565,7 +1568,7 @@ toolkit:
|
|
|
1565
1568
|
reaches none. A kit whose base doesn't resolve (an unknown name, a cycle) falls back to `full`, which reaches
|
|
1566
1569
|
every group.
|
|
1567
1570
|
A selector that matches no group in that load-time snapshot warns once. `find_tools` and `call_tool` are
|
|
1568
|
-
declared only when the toolkit reaches a source group or has a reachable built-in. Calls naming an unreached
|
|
1571
|
+
declared only when the toolkit reaches a source group or has a reachable tool (a built-in or a plugin tool). Calls naming an unreached
|
|
1569
1572
|
group are refused before guardrails and do not start its server. The first-run init wait is unchanged: it can
|
|
1570
1573
|
still block the first turn even when its server's group is unreached; `start: eager` and stale-cache refreshes
|
|
1571
1574
|
also still start. `/mcp` marks servers outside the current toolkit.
|
|
@@ -1599,17 +1602,21 @@ toolkit:
|
|
|
1599
1602
|
them with `/toolkit`. The configured default is always listed. A pick closes the list and shows a `tools: lean`
|
|
1600
1603
|
chip on the row (a dot on 🧰 on a phone); it is for that create only, never remembered. A review session is
|
|
1601
1604
|
always `chosen`: with review files set the list says it starts on the default unless you pick a kit.
|
|
1602
|
-
- **Left out: reachable or removed.** A built-in missing from a kit's base list (`lean` has no
|
|
1603
|
-
`kits:` entry on `base: lean` or `child` inherits that) is left out softly: it
|
|
1604
|
-
|
|
1605
|
-
|
|
1605
|
+
- **Left out: reachable or removed.** A built-in or plugin tool missing from a kit's base list (`lean` has no
|
|
1606
|
+
`delegate` and no `web_search`; a `kits:` entry on `base: lean` or `child` inherits that) is left out softly: it
|
|
1607
|
+
is **reachable**. `find_tools` searches it as group `chi` (`chi/delegate`, `chi/web_search`, with its
|
|
1608
|
+
inputSchema) beside the MCP servers' tools, and `call_tool(tool: "chi/delegate", args: {…})` runs it as `delegate`
|
|
1609
|
+
itself, through the usual guardrails, approvals and hooks ([plugins](plugins.md#chitool_sourcecall-acts_as-label-wait-prefix)). A
|
|
1606
1610
|
tool in a toolkit's `remove:`, a `--no-tool` or a `/toolkit remove` is **removed**: hard, also after an add, and
|
|
1607
1611
|
call_tool can't reach it (`child` removes `memory_write`, `delegate` and `delegate_result`, so a child on it
|
|
1608
|
-
starts no children). To keep a
|
|
1609
|
-
are declared when a toolkit has a reachable
|
|
1610
|
-
|
|
1612
|
+
starts no children). To keep a tool away from the model, name it under `remove:`. `find_tools` and `call_tool`
|
|
1613
|
+
are declared when a toolkit has a reachable tool or reaches a source group, as the tools stood when the
|
|
1614
|
+
session's worker started; `full` with no reachable source declares neither, and removing them there
|
|
1615
|
+
(`--no-tool call_tool`) leaves nothing out: no prompt line, no chip. A plugin tool that arrives later
|
|
1616
|
+
(`chi.replace_tools`) on a kit that declared neither stays out of reach for that worker, with one warn notice
|
|
1617
|
+
(`/toolkit add NAME` declares it).
|
|
1611
1618
|
- **On `api: anthropic`, reachable tools are callable by name.** Every tool `call_tool` reaches (the reachable
|
|
1612
|
-
built-ins, tools from reached MCP groups as `mcp_<server>_<tool>`) also goes in the request's tools deferred
|
|
1619
|
+
built-ins and plugin tools, tools from reached MCP groups as `mcp_<server>_<tool>`) also goes in the request's tools deferred
|
|
1613
1620
|
(`defer_loading`: Messages keeps them out of the model's view until something loads them, at a flat ~100 tokens).
|
|
1614
1621
|
A direct call of one (`list_sessions`, `mcp_fs_read`) runs, under the same guardrails, approvals and rows as
|
|
1615
1622
|
through `call_tool` (an MCP tool's call is `call_tool`'s). A `find_tools` hit **loads** its matches (up to 5):
|
|
@@ -2210,7 +2217,7 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
|
|
|
2210
2217
|
| `recap.timeout` | `30` | yes | Seconds a recap request may take. |
|
|
2211
2218
|
| `recap.min_user_turns` | `2` | yes | Prompts a session needs before it gets a recap. |
|
|
2212
2219
|
| `recap.sentences` | `2-4` | yes | Recap length, `N` or `N-M` (1–10). Per session too: `--set recap.sentences=3`. |
|
|
2213
|
-
| `broadcast.active_hours` | `8` | | `chi broadcast` reaches a session a worker or a
|
|
2220
|
+
| `broadcast.active_hours` | `8` | | `chi broadcast` reaches a session a worker or a one-turn `--non-interactive` run has, or whose last turn ended within this many hours. See [Broadcast](broadcast.md). |
|
|
2214
2221
|
| `broadcast.ticket_pattern` | `\b[A-Z][A-Z0-9]+-\d+\b` | | A Ruby regex for ticket ids; one in the note and in a session's branch (any case) or prompts delivers the broadcast there. See [Broadcast](broadcast.md). |
|
|
2215
2222
|
| `broadcast.triage_model` | recap's | | The model that judges a recipient no tag matched (a model ref, as `recap.model`); unset: the recap's model, else `default.model`. A ~9B dense instruct model or larger; 4B-class ones say yes to nearly everything. See [Broadcast](broadcast.md#triage). |
|
|
2216
2223
|
| `broadcast.triage_host_ref` | none | | A `hosts:` name for the triage model. |
|
|
@@ -2218,7 +2225,6 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
|
|
|
2218
2225
|
| `broadcast.triage_parallel` | `4` | | Triage requests at a time. |
|
|
2219
2226
|
| `broadcast.triage_deadline` | `20` | | Seconds triage may take in all; a recipient not judged by then gets the note unchecked. |
|
|
2220
2227
|
| `broadcast.threshold` | `0.5` | | The P(yes) a recipient needs, when the triage host gives logprobs; a plain yes or no is 1 or 0. |
|
|
2221
|
-
| `session.shared` | `true` | | Plain `chi` runs its session in a worker and attaches; `--no-shared` per run. |
|
|
2222
2228
|
| `session.idle_exit_minutes` | `30` | yes | An unused worker exits after this; `0` = never. |
|
|
2223
2229
|
| `session.project_env` | `mise` | | The commands a session's worker runs follow its project: `mise` runs them in the session folder's `mise env`, read once at worker start (`chi sessions restart` picks up a mise.toml edit); `off` doesn't ask mise: the tools keep the env of whoever started the worker (with a chi home, the home's `chi` still goes first on their PATH). See [Project env](sessions.md#project-env). |
|
|
2224
2230
|
| `session.carry_last_answer` | `true` | | A continue's carry note holds the previous session's last answer after its recap; `false`: the recap only. See [Session chains](sessions.md#session-chains). |
|
|
@@ -2250,7 +2256,7 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
|
|
|
2250
2256
|
| `log.file` | state dir | yes | See "Debug Log File". |
|
|
2251
2257
|
| `log.disable` | `false` | yes | No file logging. |
|
|
2252
2258
|
| `log.level` | `info` | yes | `debug`, `info`, `warn`, `error`. |
|
|
2253
|
-
| `status.line` | `on` | yes | The status row under the
|
|
2259
|
+
| `status.line` | `on` | yes | The status row under the attached terminal's prompt, `on` or `off`. |
|
|
2254
2260
|
| `context.status` | `true` | yes | Context-usage telemetry for the model. See [context telemetry](internals/context-telemetry.md). |
|
|
2255
2261
|
| `context.window_tokens` | server's, else 256000 | yes | Context window when the server doesn't report one. `models.<key>.window_tokens` and `hosts.<name>.window_tokens` come first; see "The context window". |
|
|
2256
2262
|
| `models.<key>.window_tokens`, `hosts.<name>.window_tokens` | none | | A model's or host's context window when the server and its model list report none. |
|
data/docs/context.md
CHANGED
|
@@ -617,13 +617,13 @@ show the answer as it is.
|
|
|
617
617
|
after a push links may be a few lines off until the next read. Without
|
|
618
618
|
`gh` or its login, nothing is linked.
|
|
619
619
|
- **Where it reads**: only in a session worker (the web, an attached chi),
|
|
620
|
-
whose answers the web shows while the session runs.
|
|
621
|
-
`chi -p`
|
|
620
|
+
whose answers the web shows while the session runs. A
|
|
621
|
+
`chi -p … --non-interactive` run reads nothing (no `gh api` calls), so its answers have no links,
|
|
622
622
|
in the web too; a scratch session never reads. The PR itself is still
|
|
623
623
|
attached and its context still reaches the model everywhere.
|
|
624
624
|
|
|
625
625
|
`line_links:` takes `false` (off), `true` (the default: a worker only) or
|
|
626
|
-
`always` (
|
|
626
|
+
`always` (a `chi -p … --non-interactive` run reads too, so its answers are linked when
|
|
627
627
|
you open the session in the web later):
|
|
628
628
|
|
|
629
629
|
```yaml
|
data/docs/desktop.md
CHANGED
|
@@ -80,7 +80,7 @@ models and aliases (from [`chi models`](cli.md#listing-the-models)), at most nin
|
|
|
80
80
|
The chooser needs a chi with `chi models`: an older helper works with a newer chi, not the reverse
|
|
81
81
|
(`chi desktop upgrade` after updating chi).
|
|
82
82
|
|
|
83
|
-
A session
|
|
83
|
+
A session a one-turn `chi -p … --non-interactive` run has isn't listed: it takes no notes or messages.
|
|
84
84
|
|
|
85
85
|
## Images
|
|
86
86
|
|
data/docs/guardrails.md
CHANGED
|
@@ -23,7 +23,7 @@ The UI shows the tool line first, then the approval under it.
|
|
|
23
23
|
|
|
24
24
|
## Ask
|
|
25
25
|
|
|
26
|
-
The
|
|
26
|
+
The attached TUI and the web show what would run, where and why:
|
|
27
27
|
|
|
28
28
|
```
|
|
29
29
|
Approve tool call?
|
|
@@ -65,8 +65,7 @@ card comes back through `ApprovalFacts.from_h` (string or symbol keys, missing f
|
|
|
65
65
|
|
|
66
66
|
Who answers:
|
|
67
67
|
|
|
68
|
-
-
|
|
69
|
-
- A shared session's worker (plain `chi` and `chi -p …` while `session.shared` is on, the default): any attached TUI or web page. With none attached,
|
|
68
|
+
- A session's worker (plain `chi`, `chi -p …`, the web): any attached TUI or web page. With none attached,
|
|
70
69
|
the approval waits (in the session file) and shows on attach.
|
|
71
70
|
- `-p … --non-interactive`: nobody; the call is denied ("No one to approve it
|
|
72
71
|
(non-interactive run)").
|
|
@@ -134,7 +133,7 @@ others come back to the parent's model with the block below. A dismissal
|
|
|
134
133
|
always passes (the hook decides what no answer means): the strategy is a
|
|
135
134
|
convention for an honest parent, not a barrier. This section is
|
|
136
135
|
the path for other agents, and for a chi parent that can't host the relay (a
|
|
137
|
-
`--
|
|
136
|
+
`--non-interactive` one-shot): there the model gets the question
|
|
138
137
|
block and the rules below. With the relay, the same rules still hold for a
|
|
139
138
|
parent model that answers its own relay card (a piped or marked `chi --attach`
|
|
140
139
|
on the parent): that answer is a parent agent's, checked by the parent and
|
|
@@ -197,10 +196,10 @@ value that isn't a level reads as `hard`.
|
|
|
197
196
|
config.yml's; `/parent-answers LEVEL` sets the session's own, `/parent-answers
|
|
198
197
|
default` drops it (config.yml's applies). Anyone may tighten it, a parent too
|
|
199
198
|
(`chi send ID -m "/parent-answers hard"`). Loosening it (toward `soft`, or
|
|
200
|
-
`default`) is yours alone: the line must come from you at
|
|
199
|
+
`default`) is yours alone: the line must come from you at an attached
|
|
201
200
|
terminal or the web composer. chi refuses it (`loosening parent_answers is the
|
|
202
201
|
user's: …`) from `chi send` (a parent's or yours: the worker can't tell them
|
|
203
|
-
apart), from a piped or marked `chi --attach
|
|
202
|
+
apart), from a piped or marked `chi --attach`, from `chi -p
|
|
204
203
|
--non-interactive`, from a delegate's follow-up and from a plugin; `chi answer`
|
|
205
204
|
has no command route. `--parent-answers` on `--attach`/`--resume` is ignored,
|
|
206
205
|
never sent as the command.
|
|
@@ -277,7 +276,7 @@ no running turn, pending question or approval, queued prompt, or running
|
|
|
277
276
|
background task; when one exists, stop it explicitly and then send a prompt.
|
|
278
277
|
|
|
279
278
|
A parent may also type its answer into chi itself: a piped `chi --attach ID`
|
|
280
|
-
or `printf '2\n' | chi
|
|
279
|
+
or `printf '2\n' | chi -p …`. So chi treats an answer as a
|
|
281
280
|
parent's, held to the same setting, when its input isn't a terminal or an
|
|
282
281
|
agent marker is set: `CLAUDECODE` (Claude Code), `AI_AGENT`, `CODEX_THREAD_ID`
|
|
283
282
|
(Codex CLI), or `SAMAGOTCHI_PARENT_SESSION`,
|
|
@@ -841,11 +840,13 @@ trade-off, when such a clash exists:
|
|
|
841
840
|
- deny rules cover both, which is the point.
|
|
842
841
|
|
|
843
842
|
A `call_tool` of one of chi's own tools (`chi/delegate`: a built-in the
|
|
844
|
-
session's toolkit leaves out, [toolkits](configuration.md#toolkits))
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
843
|
+
session's toolkit leaves out, [toolkits](configuration.md#toolkits)), or of
|
|
844
|
+
a bundle's `chi.tool` it leaves out (`chi/web_search`), is gated **as that
|
|
845
|
+
tool**: it is turned into the `delegate` call before the guardrails run, so
|
|
846
|
+
every rule on `delegate` (or `execute`'s `command:`, the `rm` check, a
|
|
847
|
+
delegate child's boundary, a plugin tool's `targets:`) gives the same
|
|
848
|
+
verdict as for a direct call, and an approval is keyed `delegate:…` either
|
|
849
|
+
way. `tool:
|
|
849
850
|
call_tool` rules no longer see such calls, only MCP calls and misses. A
|
|
850
851
|
`chi/` name the toolkit removes is refused before the gate, with no card.
|
|
851
852
|
|
|
@@ -1325,9 +1326,8 @@ allows), and how many got no judgment. `--threshold` overrides
|
|
|
1325
1326
|
for this run; `--dry-run` judges and prints without writing a line. Without a
|
|
1326
1327
|
client (`mode: off`, or no `host`) it exits 2 saying what to set.
|
|
1327
1328
|
|
|
1328
|
-
|
|
1329
|
-
questions per session,
|
|
1330
|
-
`cards.json` at all, so its asks are not replayable.
|
|
1329
|
+
A limit on what the replay can see: `CardStore` keeps only the last 20
|
|
1330
|
+
questions per session, so older asks are not replayable.
|
|
1331
1331
|
|
|
1332
1332
|
## The guardrails bundle
|
|
1333
1333
|
|
data/docs/hooks.md
CHANGED
|
@@ -97,14 +97,14 @@ class Watchful
|
|
|
97
97
|
def call(event)
|
|
98
98
|
case event[:type]
|
|
99
99
|
when :after_generation
|
|
100
|
-
# One line in the
|
|
100
|
+
# One line in the attached TUI and the web ("<bundle>> text",
|
|
101
101
|
# or "hook> text" for a config hook); level: :warn colours it.
|
|
102
102
|
# fallback_for: :display marks a line a UI that renders the answer's
|
|
103
103
|
# display may leave out (below).
|
|
104
104
|
event[:notify].call("the model repeated itself", level: :warn)
|
|
105
105
|
when :before_tool_call
|
|
106
|
-
# A single-select question through the question flow (
|
|
107
|
-
# TUI, web); returns {selected: [...], freeform:, selected_indices:}
|
|
106
|
+
# A single-select question through the question flow (the attached
|
|
107
|
+
# TUI, the web); returns {selected: [...], freeform:, selected_indices:}
|
|
108
108
|
# or nil when there is no one to ask (--non-interactive), the
|
|
109
109
|
# question was dismissed, or the options (or effects) were invalid.
|
|
110
110
|
# effects: says what an option does for a parent agent's answer (see
|
|
@@ -156,7 +156,7 @@ that a UI that already shows it can leave the line out. The main value is
|
|
|
156
156
|
links), for a UI that doesn't render that display. The hook never names a UI;
|
|
157
157
|
each decides by what it renders. The web with markdown on (`web.markdown` and
|
|
158
158
|
the commonmarker gem) leaves such a line out, live and after a reload; with
|
|
159
|
-
markdown off it shows, as it does in the
|
|
159
|
+
markdown off it shows, as it does in the attached TUI and
|
|
160
160
|
`chi -p`, which print the answer as text (in the web its http(s) URLs are
|
|
161
161
|
links, as in any hook's notice line). The log keeps the line with
|
|
162
162
|
`fallback_for=display`. `:editor` marks a line that names a file the viewer
|
|
@@ -304,8 +304,8 @@ end
|
|
|
304
304
|
kept), on a live turn and after a reload. Its copy button copies the
|
|
305
305
|
display text. The page learns about it from an `answer_display` event
|
|
306
306
|
that comes after `turn_completed` (the hooks run after the turn ended).
|
|
307
|
-
- **
|
|
308
|
-
and
|
|
307
|
+
- **The terminal** (the attached TUI, a `--non-interactive` one-shot) has printed the answer by then
|
|
308
|
+
and does not change it; use `event[:notify]` for something it should show.
|
|
309
309
|
|
|
310
310
|
A plugin gets the same from `chi.on(:after_turn) { |event, ctx| event[:present].call { … } }`.
|
|
311
311
|
|
|
@@ -569,7 +569,7 @@ the voter's own (a guard that wants the model to retry a corrected call:
|
|
|
569
569
|
`Retry with "…".`).
|
|
570
570
|
|
|
571
571
|
`event[:context]` is `{cwd:, repo_root:, branch:, session_id:, interface:, origin:}`
|
|
572
|
-
(`interface` is `:
|
|
572
|
+
(`interface` is `:worker` or `:non_interactive`). `event[:targets]` is
|
|
573
573
|
what the call acts on, resolved as the tools resolve it:
|
|
574
574
|
`{command:, paths:, cwd:, repo_root:, outside_repo:, git_dirs:}`. `outside_repo`
|
|
575
575
|
is measured from the session's repo, not the call's `cwd:`; `git_dirs` lists where
|
|
@@ -644,8 +644,9 @@ A `write`'s `content:` is the file text; an `edit` carries `old_text:` and
|
|
|
644
644
|
arguments whole on `args:` (string keys) instead, and its `content:` is those
|
|
645
645
|
arguments as JSON. An MCP tool's call is chi's `call_tool` (its tool index's, which the mcp
|
|
646
646
|
bundle runs): `tool: "call_tool"`, with `args: {"tool" => "<server>/<tool>", "args" => {…}}`.
|
|
647
|
-
A built-in reached through `call_tool` (`chi/delegate`, a tool the
|
|
648
|
-
declare) fires as that
|
|
647
|
+
A built-in or plugin tool reached through `call_tool` (`chi/delegate`, `chi/web_search`: a tool the
|
|
648
|
+
session's toolkit doesn't declare) fires as that tool, its call built as a direct call's, with
|
|
649
|
+
`call[:via] == "call_tool"`.
|
|
649
650
|
A hook that drops `via:` or rewrites the call to a tool the toolkit removes gets the call refused
|
|
650
651
|
by its name.
|
|
651
652
|
On a Messages host (`api: anthropic`), the model may call those tools by their own names (they
|
|
@@ -710,7 +711,7 @@ Notes:
|
|
|
710
711
|
- Ordering: bundle hooks fire by `(priority, bundle_name, hook_name)` (lower priority first), then plain `config.yml` hooks in registration order.
|
|
711
712
|
- Settings: a hook class with `initialize(settings = {})` gets the bundle's section of `config.yml` `bundles:` (see [Settings](#settings)).
|
|
712
713
|
- A bundle can also ship a `plugin.rb` whose `chi.on(event)` blocks are bundle hooks too, next to commands and tools; see [Plugins](plugins.md). There `ctx.notify`, `ctx.steer` and the other `ctx` helpers act as this page's `event[:notify]`… do for the event the block runs for.
|
|
713
|
-
- Shipped bundles (each in a profile: `core` or `dev`, see [Bundle profiles](memory.md#bundle-profiles-core-and-dev)): `chi bundle install guardrails` (rules, see [Guardrails](guardrails.md#the-guardrails-bundle)) and `chi bundle install known-names` (a hook, see [Guardrails](guardrails.md#the-known-names-bundle)), `chi bundle install source-links` (a hook: announces source refs, see [The source-links bundle](#the-source-links-bundle)), `chi bundle install btw` (a plugin: `/btw`, see [Bundles](bundles.md#the-btw-bundle)), `chi bundle install mcp` (a plugin: tools from MCP servers, see [Bundles](bundles.md#the-mcp-bundle)) `chi bundle install loop-guard` (a plugin: breaks tool-call loops, see [Bundles](bundles.md#the-loop-guard-bundle)), `chi bundle install check-in` (a plugin: checks on a long turn, see [Bundles](bundles.md#the-check-in-bundle)), `chi bundle install slow-commands` (a plugin: commands that ran past the execute limit are run as tasks next time, see [Bundles](bundles.md#the-slow-commands-bundle)), `chi bundle install agents-md` (a plugin: a nested AGENTS.md is steered into the conversation on the first touch under its directory, see [Bundles](bundles.md#the-agents-md-bundle)), `chi bundle install cost-cap` (a plugin: a per-session $ cap, `/cap`, see [Bundles](bundles.md#the-cost-cap-bundle)), `chi bundle install work-cap` (a plugin: a per-session cap on tool calls and active minutes, `/workcap`, see [Bundles](bundles.md#the-work-cap-bundle)), `chi bundle install ruby` (a plugin: an RSpec run with no examples at all, a hint, see [Bundles](bundles.md#the-ruby-bundle)) `chi bundle install skills` (a plugin: `/skill`, versions of skills, see [Bundles](bundles.md#the-skills-bundle)), `chi bundle install github-pr` (attached context: the branch's open PR, see [Bundles](bundles.md#the-github-pr-bundle)), `chi bundle install coordinator` (a plugin: parallel work in worktrees and child sessions, see [Bundles](bundles.md#the-coordinator-bundle)), `chi bundle install claude-peer` / `codex-peer` (a plugin: talk to a Claude Code or Codex session, see [Bundles](bundles.md#the-claude-peer-bundle))
|
|
714
|
+
- Shipped bundles (each but web-search in a profile: `core` or `dev`, see [Bundle profiles](memory.md#bundle-profiles-core-and-dev)): `chi bundle install guardrails` (rules, see [Guardrails](guardrails.md#the-guardrails-bundle)) and `chi bundle install known-names` (a hook, see [Guardrails](guardrails.md#the-known-names-bundle)), `chi bundle install source-links` (a hook: announces source refs, see [The source-links bundle](#the-source-links-bundle)), `chi bundle install btw` (a plugin: `/btw`, see [Bundles](bundles.md#the-btw-bundle)), `chi bundle install mcp` (a plugin: tools from MCP servers, see [Bundles](bundles.md#the-mcp-bundle)) `chi bundle install loop-guard` (a plugin: breaks tool-call loops, see [Bundles](bundles.md#the-loop-guard-bundle)), `chi bundle install check-in` (a plugin: checks on a long turn, see [Bundles](bundles.md#the-check-in-bundle)), `chi bundle install slow-commands` (a plugin: commands that ran past the execute limit are run as tasks next time, see [Bundles](bundles.md#the-slow-commands-bundle)), `chi bundle install agents-md` (a plugin: a nested AGENTS.md is steered into the conversation on the first touch under its directory, see [Bundles](bundles.md#the-agents-md-bundle)), `chi bundle install cost-cap` (a plugin: a per-session $ cap, `/cap`, see [Bundles](bundles.md#the-cost-cap-bundle)), `chi bundle install work-cap` (a plugin: a per-session cap on tool calls and active minutes, `/workcap`, see [Bundles](bundles.md#the-work-cap-bundle)), `chi bundle install ruby` (a plugin: an RSpec run with no examples at all, a hint, see [Bundles](bundles.md#the-ruby-bundle)) `chi bundle install skills` (a plugin: `/skill`, versions of skills, see [Bundles](bundles.md#the-skills-bundle)), `chi bundle install github-pr` (attached context: the branch's open PR, see [Bundles](bundles.md#the-github-pr-bundle)), `chi bundle install coordinator` (a plugin: parallel work in worktrees and child sessions, see [Bundles](bundles.md#the-coordinator-bundle)), `chi bundle install claude-peer` / `codex-peer` (a plugin: talk to a Claude Code or Codex session, see [Bundles](bundles.md#the-claude-peer-bundle)), `chi bundle install model-notes` (memories: reviewed model notes, see [Bundles](bundles.md#the-model-notes-bundle)) and `chi bundle install web-search` (a plugin: a `web_search` tool through your SearXNG, in no profile, see [Bundles](bundles.md#the-web-search-bundle)).
|
|
714
715
|
- Installing a bundle executes its hook code at `Engine` startup. Only install bundles you trust, as you would a gem. Hooks are **not** executed at install time (copy-only); they are `module_eval`'d at `Engine.new` inside per-bundle `Samagotchi::Bundles::<name>` namespaces (no top-level `require` collisions). Keep hook files side-effect-free at load time; do work in `#call` — top-level side effects (require, IO, `at_exit`, global assignment) run once per `Engine.new` (class redefinition is idempotent).
|
|
715
716
|
|
|
716
717
|
Lifecycle:
|
|
@@ -854,8 +855,8 @@ qualified ref:
|
|
|
854
855
|
|
|
855
856
|
**Where `{repo}` and `{host}` come from.** Without a named group that took
|
|
856
857
|
part, they come from `git remote get-url <remote>` run in the session's
|
|
857
|
-
working directory (the worker's;
|
|
858
|
-
current directory). `remote:` picks the remote, default `origin` — a fork
|
|
858
|
+
working directory (the worker's; a `--non-interactive` one-shot's is the
|
|
859
|
+
terminal's current directory). `remote:` picks the remote, default `origin` — a fork
|
|
859
860
|
sets `remote: upstream`. Git applies `insteadOf` rewrites and includes, and a
|
|
860
861
|
worktree reports its main repo's remote. The URL forms understood are
|
|
861
862
|
`https://`, `http://`, `ssh://`, `git://` (credentials and port dropped) and
|
data/docs/internals/anthropic.md
CHANGED
|
@@ -225,7 +225,7 @@ host refused it for that model.
|
|
|
225
225
|
`ChatLoop#tool_definitions(conversation, model:)` appends them (`Tools::DeferredTools.list`) only on such an
|
|
226
226
|
adapter, and only with at least one tool declared (Messages wants one). The list, per request:
|
|
227
227
|
|
|
228
|
-
1. the built-ins the toolkit reaches (`Registry#reachable_names`), in registry order;
|
|
228
|
+
1. the built-ins and plugin tools the toolkit reaches (`Registry#reachable_names`), in registry order;
|
|
229
229
|
2. the tool index's source tools while `call_tool` is declared (`ToolIndex#deferrable`, via
|
|
230
230
|
`Registry::Index#deferrable`): callable, the only tool with its acts_as, a name Messages takes, no registered
|
|
231
231
|
tool's name; by acts_as (`mcp_<server>_<tool>`), sorted by name;
|
|
@@ -270,11 +270,11 @@ sets `:tool_changes` for the model: the retry and every later request drop the s
|
|
|
270
270
|
|
|
271
271
|
A direct call of a deferred tool runs whether something loaded it or not (the API doesn't check): the Engine sets
|
|
272
272
|
`TurnSettings#loads_tools` from the turn's final model (after the before_turn hooks), and then `ToolRunner#run` and
|
|
273
|
-
`KernelLoop#dispatch` take a reachable built-in by its own name (`Registry#indexed`) through the normal gate, and wrap
|
|
273
|
+
`KernelLoop#dispatch` take a reachable built-in or plugin tool by its own name (`Registry#indexed`) through the normal gate, and wrap
|
|
274
274
|
an MCP tool's acts_as into the `call_tool` call it stands for (`ToolIndex#wrap`): `call_tool`'s row, rules and
|
|
275
275
|
approvals. A reload rebuilds that wrapped call from the saved one (`BuiltinCalls.wrap_saved`). Each such call is
|
|
276
276
|
logged (`tool_direct_deferred`, with `loaded:` whether the conversation had loaded it). On `api: openai` and the
|
|
277
|
-
native loop nothing is deferred, and a direct call of a reachable built-in is refused with the `call_tool` route as
|
|
277
|
+
native loop nothing is deferred, and a direct call of a reachable built-in or plugin tool is refused with the `call_tool` route as
|
|
278
278
|
before.
|
|
279
279
|
|
|
280
280
|
## 400 fallbacks
|
|
@@ -35,6 +35,9 @@ Three rules hold the shape:
|
|
|
35
35
|
conversation already holds. Ids are deterministic: `ctx-<name>-<serial>-<revision[0,12]>` (the serial makes A, B, A
|
|
36
36
|
three notes), `ctx-<name>-error-<error_since digits>`, `ctx-<name>-detached-<seen[0,12]>`.
|
|
37
37
|
- **Polling is not activity.** The poller and the absorb never hold a worker up (`WorkerIdleExit`).
|
|
38
|
+
- **The leave absorbs too.** A pass looks at the context and then checks whether it should leave; a change that
|
|
39
|
+
lands in that gap has no later pass (the poller stops with the worker), so `Worker#left` absorbs whatever is
|
|
40
|
+
pending as plain notes before the recap, as it does for a background task that finished in the same gap.
|
|
38
41
|
|
|
39
42
|
## ContextSources
|
|
40
43
|
|
|
@@ -122,11 +125,11 @@ name (or whose hint is its URL) is attached, *declined* while the declined marke
|
|
|
122
125
|
"PR", branch names ≥ 6 chars with `/ - _`, exact URLs, fenced code blanked). Only `OwnerPrompt` texts count
|
|
123
126
|
(`owner_texts`), so a participant's message, a steer, a note or a reminder never attaches anything; a review
|
|
124
127
|
session skips all of it.
|
|
125
|
-
- **Where it runs.** `before_turn` (from `Worker#run_prompt` before the absorb, and
|
|
128
|
+
- **Where it runs.** `before_turn` (from `Worker#run_prompt` before the absorb, and a `--non-interactive` one-shot's turn start, `TerminalUI`): attach
|
|
126
129
|
each proposed candidate the owner's prompts so far mention (`attach` writes a session `Source` through
|
|
127
130
|
`source_for`: a URL resolves through `ContextProviders`, else the candidate's own `cmd`), then `first_fetch` them
|
|
128
131
|
together within `FIRST_FETCH_SECONDS` (10) so the note lands in this turn; cut short, the fetch records nothing and
|
|
129
|
-
the poller tries soon. `after_propose` does the same for a late proposal.
|
|
132
|
+
the poller tries soon. `after_propose` does the same for a late proposal. A one-shot has no absorber running, so
|
|
130
133
|
`deliver_notes` runs one.
|
|
131
134
|
- Forks and continues take no candidates.
|
|
132
135
|
|
|
@@ -36,8 +36,7 @@ Behavior:
|
|
|
36
36
|
ends the wait when a person's message waits for the turn's next step (`Engine#input_waiting?`, run_turn's
|
|
37
37
|
`input_waiting:` probe through the kernel's `PeerView`): in a worker an input file that the next boundary merges
|
|
38
38
|
from the user, `chi send` or a delegating parent (`SessionInbox.steer_waiting?`; never a queued, image or
|
|
39
|
-
participant's one, never a plugin's steer or a child's report, never during a participant's turn)
|
|
40
|
-
(`--no-shared`) a line in its steering queue. The probe only peeks: the drain still merges the message at the
|
|
39
|
+
participant's one, never a plugin's steer or a child's report, never during a participant's turn). The probe only peeks: the drain still merges the message at the
|
|
41
40
|
next boundary. The result says `note: a message from the user is waiting; the task is STILL RUNNING.`, how to wait
|
|
42
41
|
again or stop it (`TaskWait::MESSAGE_WAITING_NOTE`), and `waited: Ns`; the task keeps running and stays watched
|
|
43
42
|
(below). A task that finished meanwhile gives its finished result, as after a Stop. A `chi -p` turn has no probe. The same probe ends a blocking `delegate_result` (and `delegate`'s wait) early; see [Sessions: Delegating](../sessions.md).
|
|
@@ -51,7 +50,7 @@ Behavior:
|
|
|
51
50
|
|
|
52
51
|
A task the model leaves running (it ends its turn: "I'll check when it's finished") is reported when it ends, so the result doesn't wait for the user's next message. `session.task_wakes` (env `SAMAGOTCHI_SESSION_TASK_WAKES`): `wake` (default), `queue` or `off` (no watch, as before).
|
|
53
52
|
|
|
54
|
-
- **The watch** (`TaskWatch`, `task_watch.rb`) is the Worker's (`WorkerTaskWakes`, `worker_task_wakes.rb`, which writes the notes; the Worker runs the wake turn), in memory:
|
|
53
|
+
- **The watch** (`TaskWatch`, `task_watch.rb`) is the Worker's (`WorkerTaskWakes`, `worker_task_wakes.rb`, which writes the notes; the Worker runs the wake turn), in memory: a `--non-interactive` one-shot has none, and a crash loses it (those tasks behave as before). The Worker's Engine observer feeds it the raw `:tool_call_completed` events, the input `Bridge#own_task_ids` reads too. A `task_create` result and an `execute` hand-over add the task (`TaskRuntime.created_id`: a `task_id:` the command printed doesn't count), the hand-over's limit kept for the note. What shows the model the end removes it: a `task_wait` with a final status, or with `wait_result: pattern_matched` (the model chose its own "done" marker); a `task_get` with a final status; any `task_stop`. `task_list` and a `read` of `output.log` don't: a model that read the log still gets the wake once. Tasks a tool starts inside an anytime command (`/btw` and the like) reach the same observer, so they are watched too.
|
|
55
54
|
- **The idle tick**: with something watched, each idle pass (after delegate reports, before due reminders) refreshes the watched records (`TaskRuntime.get_record`, one `ps` per running task) and takes the finished ones, keeping only the ids the conversation still holds (`TaskRuntime.created_ids_in`: a `!rollback` or a restored turn may have dropped the result that started one; an LLM context strategy can't, since its stale and forget stubs are drawn in the request, never written over the saved result). A note that can't be written puts its task back for the next pass. An empty watch costs nothing. A task that finishes during a turn and is still unseen when the turn ends wakes on the next idle pass. One the user stopped with the web's stop-task button is dropped without a note.
|
|
56
55
|
- **The note** is a context note under its own header (`ContextNote::TASK_HEADER`; `TaskFinishNote`), `note_id: task-<id>`, `source: task <id8>`:
|
|
57
56
|
|
|
@@ -70,6 +69,6 @@ A task the model leaves running (it ends its turn: "I'll check when it's finishe
|
|
|
70
69
|
- **A plain note instead** (the same text without the last two lines, no turn start), written at once, with `queue`, the budget spent (the UIs hear `N finished background tasks noted; the model reads them with your next message` once per human input, the flag shared with the other wakes), wakes paused, a continue offer waiting, input queued, an exit asked for, or a **note-only** task: one started or waited on in a turn the user stopped (reasons `user`, `manual`, `ctrl_c`), since a wake telling the model to carry on would undo that Stop. A prompt's turn takes the finished ones as plain notes first.
|
|
71
70
|
- **Failure**: a wake turn that fails and kept nothing, or is stopped and restored, turns its note back into the plain one (`WorkerInbound#unmark_wake`, as for a context wake) and pauses wakes until the user writes.
|
|
72
71
|
- **Idle exit**: with `wake`, a watched task holds the worker's idle exit (`WorkerIdleExit`'s `:background_tasks`) until its wake is taken: while it runs, and once it finished until the next idle pass takes it (`TaskWatch#holding`, in memory, no `ps`), so one that ends between the pass's look and the exit's check still wakes the session. A still-running one holds `/exit` too (`TaskWatch#running`, read once per request by `WorkerIdleExit#request_hold`; the 409 lists the tasks, `{id, command, age_seconds}`, with a `hint` on stopping them, and the attached terminal names them and says to use the web's stop-task button or ask for `task_stop`), as reminders do. When the only holds are ones the user can stop, the 409 carries `holds` too and the terminal asks whether to stop them all (`HoldStop`, which stops these tasks the same way `task_stop` does). A note-only task holds neither (no wake would come for it), nor does one whose note, or the look for it (the conversation's task ids unreadable), failed 3 times (`TaskWatch::MAX_FAILURES`: it is still tried at each look, but can't keep the worker up for good). With `queue` nothing holds: nothing would wake for the end, so keeping the worker up isn't worth it. As the worker leaves (any exit but a stop, a signal or an `/exit --delete`), the tasks that finished since the last look are written as plain notes; a task that ends after the worker left gets no note (the model sees its end at its next look, as before). A forgotten dev server keeps the worker up until it is stopped; a crash of that server wakes the session.
|
|
73
|
-
- **The tool descriptions**: a worker's `execute` and `task_create` say "if you end your turn before it finishes, chi tells you when it does" (left out under `off` and in
|
|
72
|
+
- **The tool descriptions**: a worker's `execute` and `task_create` say "if you end your turn before it finishes, chi tells you when it does" (left out under `off` and in a `--non-interactive` one-shot), fixed when the worker builds its Engine.
|
|
74
73
|
|
|
75
74
|
Work that needs a model, not a shell command, goes to a child chi session instead: `delegate` / `delegate_result` have the same shape (start, then wait) and return only the child's final reply; see [Sessions: Delegating](../sessions.md#delegating).
|
|
@@ -30,6 +30,10 @@ model. The TUI is untouched: it shows a fence as source.
|
|
|
30
30
|
clone arrives with both. On failure the `<pre>` stays, marked `data-block-failed`, with one muted line: an attribute CSS
|
|
31
31
|
shows, never markup, and not in the bubble's `textContent`, which the page's dedupe and an annotation read (`copy.js`'s rule). CSS content can't be selected, so a click on the line copies its words.
|
|
32
32
|
The toggle has an icon and an `aria-label`, no text (`copy.js`'s rule). A jump into the source shows it (`revealInHistory`).
|
|
33
|
+
For a screen reader: the hidden `<pre>` isn't read, so the `<img>`'s alt names the toggle that shows it ("mermaid
|
|
34
|
+
diagram, its source under the Show the source button"). The natural size, a click on the picture, is also a
|
|
35
|
+
`.block-zoom` button (icon, `aria-label`, `aria-pressed`) for the keyboard: before the toggle, shown on keyboard focus
|
|
36
|
+
only, and taking no pointer while hidden, so the mouse's view is as it was.
|
|
33
37
|
6. Cache: a `Map` on `lang + theme + source`, about 200 entries, holding the `data:` URL; a later `bubble.innerHTML` wipes the
|
|
34
38
|
figure and the observer redraws in the same pass. No `crypto.subtle`: it's undefined on a plain-http LAN address. A system
|
|
35
39
|
theme change redraws through the figure (a hidden `<pre>` never intersects).
|
data/docs/internals/bridge.md
CHANGED
|
@@ -98,11 +98,11 @@ the "asking again" and steer-cut rows, ✂ rows and questions (with how they wer
|
|
|
98
98
|
`chi guardrails replay` reads. A plain upgrade doesn't reach this: a worker already running seeded before and keeps its own version 1
|
|
99
99
|
file, and a spawn runs the newest chi installed (`SessionManager.worker_command`), unless that activation raises LoadError: the boot
|
|
100
100
|
line then falls back to the spawner's or the home's pinned version, which can be older. Otherwise it takes an older process starting
|
|
101
|
-
on a session a newer one wrote: a downgrade, a chi home whose newest chi is older (`home_older_than_spawner`), or a
|
|
101
|
+
on a session a newer one wrote: a downgrade, a chi home whose newest chi is older (`home_older_than_spawner`), or a `bin/chi`
|
|
102
102
|
from an older gem or checkout on the same state dir. Nothing on this side stops
|
|
103
103
|
it: an older chi can't be changed, and having it read the file would mean writing fake `turns` counters into every file.
|
|
104
104
|
- **A killed turn needs nothing special.** `TurnCheckpoint` keeps a running turn's prompt in the session file, with its id from
|
|
105
|
-
the start (`Worker#run_engine_turn` settles it first), so it counts as any opening. A
|
|
105
|
+
the start (`Worker#run_engine_turn` settles it first), so it counts as any opening. A `--non-interactive` one-shot keeps no running turn in the
|
|
106
106
|
session, so a kill takes the prompt along and there's nothing to count.
|
|
107
107
|
|
|
108
108
|
## SSEWriter
|
|
@@ -401,7 +401,7 @@ for client discovery, with the Bridge's secret (see above).
|
|
|
401
401
|
**Worker link.** Every client asks `WorkerLink` (`worker_link.rb`) whether a session's worker can be reached, never
|
|
402
402
|
reading `bridge.json` or probing a port itself: `WorkerLink.resolve(session_dir, unlink:, owner:)` answers a typed
|
|
403
403
|
outcome. The rule is the owner lock plus the sidecar's `pid`; there is no port probe (a port can be reused by another
|
|
404
|
-
session's Bridge, a free lock is the kernel's proof the worker died). In order: no lock holder, or a
|
|
404
|
+
session's Bridge, a free lock is the kernel's proof the worker died). In order: no lock holder, or a one-shot's (kind `"tui"`): a
|
|
405
405
|
sidecar is `Stale(:lock_free)` (removed with `unlink: true`, only if it is still the file that was read and the lock,
|
|
406
406
|
read again just before, is still free: a worker that took it meanwhile is resolved as that worker), else `None`;
|
|
407
407
|
a holder with no kind yet: `Starting`; a leaving worker: `Leaving` (marked before its Bridge stops, on every way out:
|
|
@@ -55,7 +55,7 @@ session's first counted turn. A
|
|
|
55
55
|
hook's switch (`before_turn`) sends nothing: its turn's first request counts on the new model anyway.
|
|
56
56
|
|
|
57
57
|
A process that loads the session on another model than its last turn ran on (`Engine#session=`:
|
|
58
|
-
`SessionMetrics#last_model_ref` differs from `effective_model_ref`; a `--resume`'d
|
|
58
|
+
`SessionMetrics#last_model_ref` differs from `effective_model_ref`; a `--resume`'d one-shot with `--model`, a worker woken
|
|
59
59
|
after a `/model` with no turn since) drops the old window (`forget_model_reports!`) and calls
|
|
60
60
|
`announce_switched_context!(configured_only: true)`: the configured window only
|
|
61
61
|
(`EffectiveModel#configured_context_window`, no `/props` GET or model list, since a worker runs it before its Bridge
|