samagotchi 0.101.0 → 0.103.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 +67 -1
- data/bin/chi +31 -196
- data/docs/architecture.md +62 -6
- data/docs/cli.md +154 -7
- data/docs/configuration.md +32 -5
- data/docs/guardrails.md +2 -1
- data/docs/internals/bundles.md +1 -1
- data/docs/internals/engine.md +1 -1
- data/docs/internals/llm-context-forget.md +25 -1
- data/docs/internals/prompt-caching.md +8 -1
- data/docs/internals/remote-sessions.md +303 -0
- data/docs/internals/working-set.md +174 -15
- data/docs/plugins.md +32 -5
- data/docs/sessions.md +38 -14
- data/docs/sub-agent.md +8 -0
- data/docs/testing.md +7 -0
- data/lib/samagotchi/answer_command.rb +27 -25
- data/lib/samagotchi/bridge/turn_accumulator.rb +16 -0
- data/lib/samagotchi/bridge.rb +33 -7
- data/lib/samagotchi/bridge_client/event_stream.rb +81 -12
- data/lib/samagotchi/bridge_client/stream_request.rb +77 -0
- data/lib/samagotchi/bridge_client.rb +22 -38
- data/lib/samagotchi/bundle_command/check.rb +5 -1
- data/lib/samagotchi/bundle_command/try/command.rb +6 -6
- data/lib/samagotchi/bundle_command/try/sandbox.rb +3 -3
- data/lib/samagotchi/bundle_command.rb +4 -4
- data/lib/samagotchi/bundles/claude-peer/manifest.yml +2 -2
- data/lib/samagotchi/bundles/claude-peer/scripts/pair +30 -1
- data/lib/samagotchi/bundles/codex-peer/manifest.yml +2 -2
- data/lib/samagotchi/bundles/codex-peer/scripts/pair +49 -4
- data/lib/samagotchi/bundles/guardrails/guardrails/rules.yml +7 -4
- data/lib/samagotchi/bundles/guardrails/manifest.yml +1 -1
- data/lib/samagotchi/bundles/system/manifest.yml +1 -1
- data/lib/samagotchi/chi_invocation.rb +43 -0
- data/lib/samagotchi/cli/command.rb +3 -2
- data/lib/samagotchi/cli/flags.rb +5 -0
- data/lib/samagotchi/cli/parent_wait.rb +22 -13
- data/lib/samagotchi/commands/registry.rb +12 -1
- data/lib/samagotchi/config.rb +89 -53
- data/lib/samagotchi/context_command.rb +80 -73
- data/lib/samagotchi/engine.rb +138 -15
- data/lib/samagotchi/idle_recap.rb +7 -1
- data/lib/samagotchi/idle_scheduler.rb +16 -1
- data/lib/samagotchi/kernel_loop.rb +13 -1
- data/lib/samagotchi/launch_mode.rb +22 -0
- data/lib/samagotchi/llm_context_edit.rb +32 -10
- data/lib/samagotchi/llm_context_forget.rb +24 -8
- data/lib/samagotchi/llm_context_view.rb +19 -7
- data/lib/samagotchi/memory_bundle/installer.rb +2 -0
- data/lib/samagotchi/memory_bundle/manifest.rb +13 -2
- data/lib/samagotchi/message_parts.rb +8 -3
- data/lib/samagotchi/note_command.rb +8 -7
- data/lib/samagotchi/parent_report.rb +55 -43
- data/lib/samagotchi/plugin/context.rb +2 -2
- data/lib/samagotchi/prompt.rb +1 -1
- data/lib/samagotchi/recap_command.rb +68 -0
- data/lib/samagotchi/recap_setup.rb +27 -14
- data/lib/samagotchi/remote_command.rb +170 -0
- data/lib/samagotchi/remotes.rb +130 -0
- data/lib/samagotchi/run_options.rb +233 -0
- data/lib/samagotchi/send_command/busy.rb +58 -0
- data/lib/samagotchi/send_command/parent_gate.rb +23 -10
- data/lib/samagotchi/send_command/wait_target.rb +16 -3
- data/lib/samagotchi/send_command.rb +186 -118
- data/lib/samagotchi/session.rb +48 -16
- data/lib/samagotchi/session_access/contract.rb +129 -0
- data/lib/samagotchi/session_access/dispatch.rb +133 -0
- data/lib/samagotchi/session_access/http_transport.rb +109 -0
- data/lib/samagotchi/session_access/local.rb +349 -0
- data/lib/samagotchi/session_access/remote.rb +638 -0
- data/lib/samagotchi/session_access/web_bridge_client.rb +181 -0
- data/lib/samagotchi/session_access.rb +141 -0
- data/lib/samagotchi/session_archive_command.rb +9 -5
- data/lib/samagotchi/session_commands.rb +53 -6
- data/lib/samagotchi/session_config.rb +411 -0
- data/lib/samagotchi/session_delete_command.rb +8 -4
- data/lib/samagotchi/session_manager.rb +7 -2
- data/lib/samagotchi/session_setup.rb +15 -10
- data/lib/samagotchi/sessions_command.rb +42 -16
- data/lib/samagotchi/terminal_ui/attach_launcher.rb +54 -18
- data/lib/samagotchi/terminal_ui/attach_link.rb +104 -0
- data/lib/samagotchi/terminal_ui/attached_loop.rb +82 -76
- data/lib/samagotchi/terminal_ui/formatting.rb +10 -3
- data/lib/samagotchi/terminal_ui/local_link.rb +130 -0
- data/lib/samagotchi/terminal_ui/version_lines.rb +5 -3
- data/lib/samagotchi/terminal_ui.rb +8 -6
- data/lib/samagotchi/testing/bundle.rb +3 -2
- data/lib/samagotchi/title_command.rb +9 -11
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +387 -79
- data/lib/samagotchi/web/public/app.js +2 -0
- data/lib/samagotchi/web/public/commands.js +8 -2
- data/lib/samagotchi/web/public/data.js +6 -4
- data/lib/samagotchi/web/public/index.html +37 -3
- data/lib/samagotchi/web/public/stage_frame.js +7 -2
- data/lib/samagotchi/web/public/stage_view.js +3 -2
- data/lib/samagotchi/web/public/stream_handlers.js +7 -1
- data/lib/samagotchi/web/public/turn_html.js +5 -0
- data/lib/samagotchi/web/public/working_set.js +247 -24
- data/lib/samagotchi/web/public/working_set_rail.js +39 -12
- data/lib/samagotchi/web/public/working_set_view.js +198 -25
- data/lib/samagotchi/web/session_access_routes.rb +343 -0
- data/lib/samagotchi/worker.rb +9 -6
- data/lib/samagotchi/worker_sidecar.rb +12 -5
- data/lib/samagotchi/working_set.rb +132 -20
- data/lib/samagotchi/working_set_command.rb +222 -0
- data/lib/samagotchi/working_set_pin.rb +34 -0
- data/lib/samagotchi/working_set_steer.rb +414 -0
- metadata +23 -2
- data/lib/samagotchi/bundle_session_settings.rb +0 -251
data/docs/cli.md
CHANGED
|
@@ -16,11 +16,13 @@
|
|
|
16
16
|
- `chi web --web-markdown` — opt in to sanitized Markdown rendering for completed assistant messages, the stage's asking text and a resolved question card's reply
|
|
17
17
|
- `chi web --web-view turn` — draw turns with the turn view (each turn as one block of steps, the running one at the bottom of the history) instead of the default stage view (the running turn pinned above the composer); `?view=stage|turn` on the page URL overrides it (see [Web views](#web-views))
|
|
18
18
|
- `chi web --web-working-set` — the stage view's working set (experimental, off by default; `--no-web-working-set` turns a config's `web.working_set: true` off): what each turn brought into the model's context, in the stage and under each finished turn; `?working_set=1|0` on the page URL overrides it (see [The working set](#the-working-set))
|
|
19
|
-
- `chi sessions list|stop|restart|archive|unarchive|delete|prune|clean|stats` — manage persisted sessions; `list` shows this git project's, `list --scope=all` every one, a delegated session with `↳ <parent>`, `list --archived` the archived ones too (see [Sessions](sessions.md)). `stats ID` prints a session's cost, tokens and progress (a live worker's `GET stats`, else its saved `analytics.json
|
|
19
|
+
- `chi sessions list|stop|restart|archive|unarchive|delete|prune|clean|stats` — manage persisted sessions; `list` shows this git project's, `list --scope=all` every one, a delegated session with `↳ <parent>`, `list --archived` the archived ones too (see [Sessions](sessions.md)). `stats ID` prints a session's cost, tokens and progress, and its own settings (`--set`; a `settings:` line), from a live worker's `GET stats`, else its saved `analytics.json` (`--format json` one object, with `settings`), without running a model turn. A usage error (an unknown subcommand or flag, a flag missing its value, `stop`, `restart` or `delete` with no ids, a bad `list --format` or `--scope`, a `--days`, `--keep` or `--limit` that isn't a whole number) exits 2; an unknown or refused session exits 1
|
|
20
20
|
- `chi note [--source NAME] [-m TEXT] (ID|PREFIX)... | --all` — add a context note (TEXT or stdin) to sessions: background the model sees on its next turn; it starts no turn (see [Sessions: Context notes](sessions.md#context-notes))
|
|
21
21
|
- `chi context <add|push|ls|show|refresh|rm|mute|unmute> …` — attach live external text to sessions (a command chi runs every so often, or text pushed in); the model gets a short note when it changes and reads it with `context_read`; `chi context add <URL>` attaches a GitHub PR with the github-pr bundle (see [Attached context](context.md))
|
|
22
22
|
- `chi send [-m TEXT] [--image PATH]... [--cut | --queue] (ID|PREFIX)...` — send a message to sessions as if typed there: a turn starts (or a running one takes it at its next step; `--cut` asks it to cut long thinking for it; `--queue` runs it as a turn of its own after the running one, and `--queue --wait` waits for that turn's own answer); piped stdin goes above `-m` as quoted context, and `--image` attaches images (see [Sessions: Sending a message](sessions.md#sending-a-message)); `--new` starts a session with it instead (`--new --continues ID|last:ID` the next link of that session's chain, see [Session chains](sessions.md#session-chains)), and `--wait` prints the answer (`--wait ID` with no message waits for the next reply without sending — to an idle session whose last turn already ended, that ended turn's reply or end comes back; `--wait --turn T ID` with no message waits for exactly turn T, whether running, queued or last ended; `--format json` includes its `turn_id` when known, for waiting again after exit 3 or 4; exit 3 a question waits, its options on stderr; exit 4 `--timeout` passed with the turn still running; see [Starting a session](sessions.md#starting-a-session))
|
|
23
23
|
- `chi answer ID --question QID (--option N|LABEL)... [--text T] [--timeout S] [--format json]` (or `--dismiss`) — answer the question a session waits on (the one `chi send --wait` exited 3 with), then wait and print what comes next as `chi send --wait` does: the reply (0), the next question (3), still running after `--timeout` (4). `--option` is 1-based or the label, repeated on a multi-select question; `--text` is free text, or a Deny's reason; `--dismiss` leaves it unanswered and the model finishes its reply. A question no longer open (the web answered first) isn't answered again: it waits for that turn's reply. An option the question doesn't offer exits 2; a worker that is gone exits 1 (`send the task again: chi send --wait -m "…" ID`). What a parent may pick follows `guardrails.parent_answers`, or the session's own `parent_answers` when stricter (`soft`, the default: a deny, a plain answer, Continue and "Allow once"; `standard`: no allow; `hard`: no Continue either), never a wider scope or an allow on chi's own config; a refused pick exits 1 with what to answer instead (a convention, not a security boundary; see [Guardrails](guardrails.md#answers-from-a-parent-agent))
|
|
24
|
+
- `chi remote add NAME URL [--token-file F | --token-stdin]`, `chi remote ls`, `chi remote rm NAME` — the other machines whose chi web the session commands reach with `--remote NAME` (see [Remote sessions](#remote-sessions))
|
|
25
|
+
- `chi --remote NAME send|answer|note|context|sessions …` (or `CHI_REMOTE=NAME`) — those commands on that machine's sessions, through its chi web (see [Remote sessions](#remote-sessions))
|
|
24
26
|
- `chi memory ls [--scope project|system] [--format text|json]` — the memories of both scopes (this folder's project first), one row each: scope, name, size, date, `from <bundle>`, the index description and its model overlays; `chi memory rm NAME [--scope project|system]` removes one as `memory_write` with `remove: true` does: its file and model overlays go to the bundle trash (the line names the dir; `chi bundle trash` lists and empties it) and its index line is dropped. NAME in both scopes without `--scope` exits 2 naming both; a missing NAME or a bundle's memory (`chi bundle uninstall` removes it) exits 1 (see [Memory: Listing and removing from the shell](memory.md#listing-and-removing-from-the-shell))
|
|
25
27
|
- `chi desktop install|upgrade|uninstall|status` — the macOS "Send to chi" helper: a Service and a ⌃⌥⌘N hotkey that send text or images to a session (live, stopped or new) as a message (⏎, a turn runs) or a context note (⌘⏎) (see [Desktop helper](desktop.md))
|
|
26
28
|
- `chi models [--format text|json] [--timeout S] [TEXT]` — list the models every configured host offers, as the names `--model` takes (see [Listing the models](#listing-the-models))
|
|
@@ -278,11 +280,12 @@ controls exit behavior (`--non-interactive`); `--resume` composes with both.
|
|
|
278
280
|
| `--shared` | Run the session (new, or `--resume`'s) in a background worker and attach to it: the default, and the way to get it when `session.shared` is off. See [Sharing a session](#sharing-a-session). |
|
|
279
281
|
| `--no-shared` | Run the plain in-process REPL for this run. |
|
|
280
282
|
| `--attach SESSION_ID` | Attach to a session's worker, waking one if it has exited. |
|
|
283
|
+
| `--remote NAME` | Before a session command (`chi --remote studio send …`): run it against that remote's sessions, through its chi web; beats `CHI_REMOTE`. Commands and flags that run on this machine only exit 2. See [Remote sessions](#remote-sessions). |
|
|
281
284
|
| `--model NAME` | Use this model for the run (overrides the configured default and a resumed session's model). |
|
|
282
285
|
| `--thinking LEVEL` | This session's own thinking level: `off`, `low`, `medium` or `high`, saved in the session and first in the order (`default` unsets it). On `--resume` or `--attach` it is set at once, through a `/thinking` on an attached worker. With `chi web` it is instead the default for the sessions its workers run, below a session's own (as env `SAMAGOTCHI_THINKING_LEVEL` is). See "Thinking" in configuration.md. |
|
|
283
286
|
| `--toolkit NAME`, `--tool NAME`, `--no-tool NAME` | A new session's toolkit, the tools its prompts declare: `full` (every built-in), `lean`, `child`, one of config's `toolkit.kits` or a bundle's (without `--toolkit`: config's `toolkit.default`), with tools declared on top of it (`--tool`) or left out (`--no-tool`); both repeatable, a comma list too. Saved in the session as given; a respawned worker, a fork and a continue keep it. Ignored with `--attach`/`--resume` (one line, as `--memory`); `/toolkit` changes it. Not on `chi web` (config's `toolkit.default` covers its sessions). See [Toolkits](configuration.md#toolkits). |
|
|
284
287
|
| `--parent-answers LEVEL` | A new session's own `guardrails.parent_answers` (`hard`, `standard` or `soft`): what a parent agent may answer on its questions. It may only tighten config.yml's: the stricter of the two applies, and a looser one is saved as given with a warning (`--parent-answers soft is looser than config.yml's guardrails.parent_answers: standard; standard applies`). Saved in the session; a respawned worker, a fork and a continue keep it, a `delegate` child takes its parent's. Ignored with `--attach`/`--resume` (one line, as `--toolkit`); refused with `chi web`. Only you loosen it: `/parent-answers` in the terminal or the web. See [Guardrails](guardrails.md#answers-from-a-parent-agent). |
|
|
285
|
-
| `--set
|
|
288
|
+
| `--set KEY=VALUE` | A setting for a new session (repeatable; a later one of the same key wins). A core key a session may set (`recap.enabled`, `recap.sentences`) is typed by the key and goes first, over the CLI, env and config.yml: `--set recap.enabled=false` turns the idle recap off for this session; a value it doesn't take (`recap.enabled=maybe`) or a key no session sets (`guardrails.mode`) is an error, exit 2 (see [Per-session settings](configuration.md#per-session-settings)). Any other name is a bundle's `BUNDLE.KEY`, laid over config.yml's `bundles: <bundle>:` in what the bundle's plugin reads (`ctx.settings`): `--set cost-cap.cap=3`. The value is typed simply: an integer (`3`, `-1`) or a decimal (`2.5`, `.5`, `1e1`) is a number, `true`, `yes` and `on` are true, `false`, `no` and `off` are false (any case, as in config.yml), anything else stays the text as given (`010`, `0x10`, `3:30`, `y`, `null`), and so does every value of a key the manifest types `string`. For cost-cap, `0`, a negative number or `off` is no cap. Only a key the installed bundle's manifest lists in `session_settings:` is taken, with a value of the type it gives the key: a bundle that isn't installed (or whose plugin doesn't load), a key it doesn't list (a record installed before the bundle listed it says to run `chi update` or `chi bundle upgrade NAME`), a value of another type (`--set cost-cap.cap=five`), or another shape (`--set foo`, `--set a.b.c=1`) is an error naming what it takes, exit 2, nothing started. Saved in the session; a respawned worker, a fork and a continue keep it, a delegate child doesn't. Ignored with `--attach`/`--resume` (one line, as `--toolkit`); refused with `chi web`. See [Per-session settings](plugins.md#per-session-settings). |
|
|
286
289
|
| `--profile NAME` | Prompt profile (`qwen36` or `gemma4`) for every model in this run, over config and the server's template (same as `--model-profile`, env `SAMAGOTCHI_MODEL_PROFILE`). See "Prompt profile" in configuration.md. |
|
|
287
290
|
| `--memory NAME` | Preload a memory entry into the system prompt (repeatable; a comma list too). Merged under the config.yml `memories:` baseline. Works attached: the list is stored on the session, so its worker builds the same prompt on every respawn. |
|
|
288
291
|
| `--append-system-prompt FILE` | Put `FILE`'s text into the system prompt, after AGENTS.md and before the memory indexes (repeatable; relative to the shell's directory). Added after the config.yml `append_system_prompt:` list. A missing or unreadable file is an error, exit 1. Saved in the session, so a respawned worker, a fork and a continue keep it; ignored with `--attach`/`--resume` (one line, as `--memory`). See [Appended prompt files](configuration.md#appended-prompt-files). |
|
|
@@ -500,7 +503,8 @@ a terminal, headed by `<id8> <status> <model>`: a live worker answers its
|
|
|
500
503
|
Bridge's `GET stats`, else the snapshot is rebuilt from the session's saved
|
|
501
504
|
`analytics.json` (never starting a worker; a worker that does not answer in
|
|
502
505
|
time falls back to the disk and the line says so). `--format json` prints one
|
|
503
|
-
object `{session_id, status, live, metrics}
|
|
506
|
+
object `{session_id, status, live, metrics, settings}` (`settings`: the session's own `--set` values, flat as
|
|
507
|
+
`--set` names them, `{}` for none); a session with no
|
|
504
508
|
`analytics.json` yet reports `metrics: null` (`(no metrics yet)` in text) and
|
|
505
509
|
exits 0. An unknown or ambiguous id exits 1 with `chi sessions stop`'s
|
|
506
510
|
wording. See [Sessions](sessions.md#cli).
|
|
@@ -602,6 +606,48 @@ the same values on its `llm context:` line. The web's info bar shows them as the
|
|
|
602
606
|
through `/llm-context`. See "LLM context: a session's own strategy" in
|
|
603
607
|
[configuration.md](configuration.md).
|
|
604
608
|
|
|
609
|
+
`/working-set` lists the session's working set as the model has it (see [The
|
|
610
|
+
working set](#the-working-set)): each file, memory and URL its turns read,
|
|
611
|
+
edited or fetched, with its state (`stale`, `forgotten`, `dropped`) and what it
|
|
612
|
+
costs as sent, then the changes the next turn starts with. `/working-set drop
|
|
613
|
+
KEY…` takes an item out of what the model is sent: every read of it still sent
|
|
614
|
+
goes as a stub, `(dropped by the user) — read it again if the task needs it`,
|
|
615
|
+
whatever the LLM context strategy (none too), and stays dropped across a
|
|
616
|
+
strategy switch. KEY is a path (absolute or the session folder's), `memory:NAME`
|
|
617
|
+
or a URL; quote one with spaces (`"my notes/a b.md"`, as the listing shows it). `/working-set undrop KEY…` sends it whole again. A change applies
|
|
618
|
+
from the next turn's first request, which also carries one line telling the
|
|
619
|
+
model what you dropped or brought back (so it doesn't read it again by reflex);
|
|
620
|
+
the reply says what it frees and the tail the server reads again (a drop changes
|
|
621
|
+
history, so the prompt cache breaks there once). An undrop before that request
|
|
622
|
+
undoes the drop for free. Refused, with why: an item only edited or written
|
|
623
|
+
(nothing of it to drop), one not in the set, an older session's output chi
|
|
624
|
+
can't name. The model can't undo a drop (`forget_outputs` refuses it). One
|
|
625
|
+
`memory_read` of several names is one output: dropping, undropping or pinning
|
|
626
|
+
one name acts on that read whole, so on the others too, and the reply names
|
|
627
|
+
them (`; the same memory_read holds memory tone: dropped with it`).
|
|
628
|
+
`/working-set pin KEY…` keeps an item from the model's `forget_outputs` (the
|
|
629
|
+
[forget layer](configuration.md#llm-context-the-forget-layer)): it refuses every
|
|
630
|
+
read of it, those to come too, until `/working-set unpin KEY…`. A pin sends
|
|
631
|
+
nothing different (no cache break) and is saved with the session, so a respawn,
|
|
632
|
+
a continue and a fork keep it, across a strategy switch too; under a strategy
|
|
633
|
+
without forget it waits until one has it (the reply says so). The listing marks
|
|
634
|
+
it `pinned`, and under forget the next turn's line tells the model. A pinned
|
|
635
|
+
item can't be dropped until you unpin it; a pin takes back a drop not yet sent
|
|
636
|
+
(free), and asks you to undrop one already sent first.
|
|
637
|
+
`/working-set add KEY…` brings an item in: the next turn's line asks the
|
|
638
|
+
model to load it before anything else (`read` for a file, `memory_read` for
|
|
639
|
+
`memory:NAME`, `web_fetch` for an http(s) URL), so it costs the model one tool
|
|
640
|
+
call, and what it reads is an ordinary read (droppable, forgettable, in the
|
|
641
|
+
set). Nothing is sent until then, and `/working-set unadd KEY…` takes an add
|
|
642
|
+
back for free. On a stale or forgotten item it is a refresh (the model reads
|
|
643
|
+
it again). Refused, with why: a file that isn't there (or a directory), a
|
|
644
|
+
memory neither scope holds, a URL that isn't http(s), an item the model is
|
|
645
|
+
sent whole now, a dropped one (undrop it instead), one already added. The
|
|
646
|
+
listing's last line names the pending adds (`added lib/tax.rb`). The model is
|
|
647
|
+
asked, not forced: a model may skip the read.
|
|
648
|
+
`/working-set` alone answers mid-turn too; `drop`/`undrop`/`pin`/`unpin`/`add`/`unadd`
|
|
649
|
+
sent while a turn runs wait for the turn's end.
|
|
650
|
+
|
|
605
651
|
`/thinking` shows the thinking level the next turn runs at and where it came
|
|
606
652
|
from (`session`, `--thinking` / `SAMAGOTCHI_THINKING_LEVEL`, `models: <key>`,
|
|
607
653
|
`hosts.<name>`, `thinking.level`). `/thinking off|low|medium|high` sets the
|
|
@@ -741,7 +787,7 @@ REPL alike:
|
|
|
741
787
|
`cutting in…`, then `steered`. A phone shows the short form (`↳ after this
|
|
742
788
|
thinking · 0:42`) with `cut in` and `queue` chips.
|
|
743
789
|
- Commands answer, wait or are refused, by what they do:
|
|
744
|
-
- At once, beside the turn: `/stats`, `/recap
|
|
790
|
+
- At once, beside the turn: `/stats`, `/recap` (and `/recap on|off|default`), `/help`, `/context`, a
|
|
745
791
|
plugin's anytime command (`/btw`), and the commands that only show
|
|
746
792
|
something: `/models` (with a filter too), and `/model`, `/llm-context`,
|
|
747
793
|
`/thinking`, `/reasoning-history` and `/guardrails` alone.
|
|
@@ -987,7 +1033,28 @@ turn view ignores it.
|
|
|
987
1033
|
(used, preloaded, muted) and URLs, each with what was done to it (`read ×2`,
|
|
988
1034
|
`read, edited`, `preloaded`) and a step tag (`s3`, `t2·s3` in the session's
|
|
989
1035
|
scope, `prompt` for a preloaded memory) that goes to the call. A file opens
|
|
990
|
-
as its row's title does, a URL in a new tab.
|
|
1036
|
+
as its row's title does, a URL in a new tab. A row (on hover or keyboard
|
|
1037
|
+
focus) and a folded group offer `drop`, which runs `/working-set drop` (see
|
|
1038
|
+
`/working-set` above); a dropped row offers `undrop`, and so does one whose
|
|
1039
|
+
drop holds on some reads only (`part dropped` in `/working-set`'s listing:
|
|
1040
|
+
an older read stubbed by the stale layer, or a newer read sent). Between turns it
|
|
1041
|
+
applies at once; while a turn runs it waits for the turn's end: the row is
|
|
1042
|
+
dashed with `drops next turn` and an `undo`, and the list's foot counts the
|
|
1043
|
+
pending changes (`undo all`), in every tab and after a reload; the stage's
|
|
1044
|
+
rail dashes the item's line too (its tooltip says `drops next turn`). On a phone a
|
|
1045
|
+
tap on a row opens its actions under it. `drop` is greyed, with why on
|
|
1046
|
+
hover, for an item only edited, a memory only preloaded, or one already
|
|
1047
|
+
forgotten. Under a strategy with forget a row also offers `pin`
|
|
1048
|
+
(`/working-set pin`, pending as `applies next turn` mid-turn); a pinned row
|
|
1049
|
+
shows `📌` and `unpin` (under any strategy), and its `drop` is greyed until
|
|
1050
|
+
you unpin it.
|
|
1051
|
+
The list's foot (a phone's last row) takes a path, `memory:NAME` or a URL
|
|
1052
|
+
and runs `/working-set add`: the item shows as a dashed `+ name` row with
|
|
1053
|
+
`reads next turn` and an `undo` until the next turn starts (the model is then
|
|
1054
|
+
asked to read it, and it joins the set as the model reads it). A stale or
|
|
1055
|
+
forgotten row offers `refresh`, the same add of it. While a turn runs the
|
|
1056
|
+
stage's rail shows an add queued for its end as a dashed `+ name · next turn`
|
|
1057
|
+
line.
|
|
991
1058
|
|
|
992
1059
|
Only `read`, `edit`, `write`, `memory_read`, `memory_write` and `web_fetch`
|
|
993
1060
|
calls that succeeded count; files an `execute` touched don't.
|
|
@@ -1000,6 +1067,11 @@ the same live, after a reload and in every tab:
|
|
|
1000
1067
|
write doesn't make a file stale. Chi checks at each request and when you
|
|
1001
1068
|
open the list, so a file changed while the session sat idle shows stale on
|
|
1002
1069
|
opening it. A turn saved before chi kept that record shows its files live.
|
|
1070
|
+
- **dropped** (struck): you dropped it (`/working-set drop`, the list's
|
|
1071
|
+
`drop`); its reads are sent as stubs under every strategy until you undrop
|
|
1072
|
+
it.
|
|
1073
|
+
- **pinned** (`📌`): you pinned it (`/working-set pin`, the list's `pin`);
|
|
1074
|
+
`forget_outputs` refuses it.
|
|
1003
1075
|
- **forgotten** (struck): nothing of the item's content is still sent to the
|
|
1004
1076
|
model under the session's current LLM context strategy (`forget_outputs`
|
|
1005
1077
|
forgot every read of it; see
|
|
@@ -1007,7 +1079,7 @@ the same live, after a reload and in every tab:
|
|
|
1007
1079
|
forget kept count as still sent. Under `/llm-context strategy none` nothing
|
|
1008
1080
|
is cut, so a forgotten item is live again from the next request.
|
|
1009
1081
|
|
|
1010
|
-
The list says `stale` or `
|
|
1082
|
+
The list says `stale`, `forgotten` or `dropped` in words with each row, and how many
|
|
1011
1083
|
tokens its content costs as sent (`~1.2k`); a folded group shows its forgotten
|
|
1012
1084
|
members as `✂n`, and a footprint adds `n forgotten`.
|
|
1013
1085
|
|
|
@@ -1082,6 +1154,72 @@ access token", paste the token there (the part of the link after
|
|
|
1082
1154
|
- On http the browser has no notifications: the bell is hidden on the phone,
|
|
1083
1155
|
and the tab title still counts what needs you.
|
|
1084
1156
|
|
|
1157
|
+
### Remote sessions
|
|
1158
|
+
|
|
1159
|
+
The session commands also reach another machine's sessions, through its chi
|
|
1160
|
+
web on the LAN: the sessions, workers, memories and code stay there. On the
|
|
1161
|
+
other machine (the Studio), run `chi web --web-host lan`; on this one (the
|
|
1162
|
+
laptop), add it once with its token (the part of its LAN link after
|
|
1163
|
+
`token=`, or its `$XDG_STATE_HOME/samagotchi/web-token` file):
|
|
1164
|
+
|
|
1165
|
+
```sh
|
|
1166
|
+
chi remote add studio http://192.168.1.29:4567 --token-file studio-token
|
|
1167
|
+
# studio http://192.168.1.29:4567 chi 0.103.0, LAN 192.168.1.29
|
|
1168
|
+
chi --remote studio send --new --dir /Users/me/projects/app --wait -m "fix the flaky spec"
|
|
1169
|
+
chi --remote studio answer ab12 --question q1 --option 1
|
|
1170
|
+
CHI_REMOTE=studio chi context push claude-outbox ab12 < answer.json
|
|
1171
|
+
```
|
|
1172
|
+
|
|
1173
|
+
- `chi remote add NAME URL` checks the address and token first (`GET
|
|
1174
|
+
/api/info`) and says what it found: the chi it runs and its LAN address, or
|
|
1175
|
+
why not: nothing answers (start chi web there with `web.host: lan`), the
|
|
1176
|
+
token was refused, or a hostname (chi web answers its LAN IP only: use the
|
|
1177
|
+
IP, which changes with the Wi-Fi), or a chi too old for remote sessions.
|
|
1178
|
+
Nothing is saved then. The remotes live in
|
|
1179
|
+
`$XDG_CONFIG_HOME/samagotchi/remotes.yml` (0600), their tokens inside: a
|
|
1180
|
+
token runs commands as you there. `chi remote ls` (`--format json`) never
|
|
1181
|
+
prints one; `chi remote add` of an existing name replaces it.
|
|
1182
|
+
- `--remote NAME` goes before the command (`chi --remote studio note …`),
|
|
1183
|
+
`CHI_REMOTE=NAME` in the environment does the same for every command a
|
|
1184
|
+
script runs; the flag beats the variable, and there is no default remote.
|
|
1185
|
+
An unknown name exits 2.
|
|
1186
|
+
- What runs there: `send` (every form: `--new`, `--wait`, `--queue`,
|
|
1187
|
+
`--cut`, `--image`, a session command), `answer`, `note`, a session's
|
|
1188
|
+
`context add|push|ls|show|rm` and `sessions
|
|
1189
|
+
list|stop|restart|archive|unarchive|delete`. The rest runs on this machine
|
|
1190
|
+
only and exits 2 with `chi <command> runs on this machine only: run it on
|
|
1191
|
+
<remote>`: `context refresh|mute|unmute` and `--project`, `sessions
|
|
1192
|
+
stats|prune|clean`, `broadcast`, the machine's own commands (`bundle`,
|
|
1193
|
+
`memory`, `update`, …) and a session in this terminal (`--attach`, `-p`,
|
|
1194
|
+
`--resume`: remote attach comes later). `chi remote`, `--version` and
|
|
1195
|
+
`--help` always run here.
|
|
1196
|
+
- `send --new` needs `--dir`, that machine's absolute path (no folder of this
|
|
1197
|
+
one is guessed); `--toolkit`, `--set` and `--parent-answers` are checked
|
|
1198
|
+
against that machine's config and bundles. `sessions list` lists every
|
|
1199
|
+
project's sessions there (`--scope=project --cwd DIR` one project's) in the
|
|
1200
|
+
`--live` text form. `context` takes the session ids (no default session),
|
|
1201
|
+
and a URL resolves through that machine's bundles.
|
|
1202
|
+
- That machine's config.yml decides what it checks for `send` and `answer`:
|
|
1203
|
+
a parent agent's `/continue yes` and options (`guardrails.parent_answers`),
|
|
1204
|
+
a loosening `/parent-answers`, images to a model that can't see them. Each
|
|
1205
|
+
target refuses its own (`ab12 refused: …`), where this machine refuses
|
|
1206
|
+
before sending to any.
|
|
1207
|
+
- Waits run on the server, 30 s at a time, until the answer or your own
|
|
1208
|
+
`--timeout`; exit codes are the same. The commands chi prints for what to
|
|
1209
|
+
do next work as pasted: they carry `--remote NAME` (`chi --remote studio
|
|
1210
|
+
answer ab12 --question q1 --option N`, `chi --remote studio send --wait …`),
|
|
1211
|
+
under `CHI_REMOTE` too. What runs on this machine only isn't offered:
|
|
1212
|
+
where chi says `chi --attach ID` here it names that machine's chi web
|
|
1213
|
+
(`chi web on studio shows it`), and `sessions stop` names no `chi
|
|
1214
|
+
--resume`.
|
|
1215
|
+
- A remote that can't be reached names itself and why (`chi send: remote
|
|
1216
|
+
studio (http://192.168.1.29:4567): connection refused`, exit 1; with
|
|
1217
|
+
`--format json` an `error` object).
|
|
1218
|
+
- Plain http, the same as the phone's: use it on your home network.
|
|
1219
|
+
|
|
1220
|
+
How it works inside (`SessionAccess`, chi web's routes, where each check runs):
|
|
1221
|
+
[internals/remote-sessions.md](internals/remote-sessions.md).
|
|
1222
|
+
|
|
1085
1223
|
## Runtime Model Switch (Assist Mode)
|
|
1086
1224
|
|
|
1087
1225
|
In interactive assist mode, you can switch the request model without restarting:
|
|
@@ -1120,7 +1258,16 @@ working on, what came of it and what is still open, not a turn-by-turn log.
|
|
|
1120
1258
|
after it (`recap (before the last 2 turns)>`). One written while you sit at
|
|
1121
1259
|
the prompt prints there.
|
|
1122
1260
|
- `/recap` shows the saved one and asks for a new one when the chat moved on
|
|
1123
|
-
(`writing a recap…`, then the recap when it comes).
|
|
1261
|
+
(`writing a recap…`, then the recap when it comes). The web answers the bare
|
|
1262
|
+
`/recap` with a note (the recap shows there by itself).
|
|
1263
|
+
- `/recap off` and `/recap on` turn the recap off or on for this session, at
|
|
1264
|
+
once and in any UI (the web composer too, and mid-turn); `/recap default`
|
|
1265
|
+
goes back to config.yml's. The reply says both: `recap: off (this session;
|
|
1266
|
+
config.yml: on)`. It is the session's own `recap.enabled`, as `--set
|
|
1267
|
+
recap.enabled=false` gives it (see [Per-session
|
|
1268
|
+
settings](configuration.md#per-session-settings)): saved with the session,
|
|
1269
|
+
so a respawned worker keeps it, and it beats config.yml (`/recap on` under
|
|
1270
|
+
`recap: false` turns it on here). With it off, `/recap` says so.
|
|
1124
1271
|
- One written while a continue offer waits (the last turn ran out of steps)
|
|
1125
1272
|
says that turn stopped before the task was finished. Answering `no` counts
|
|
1126
1273
|
as activity, so the next recap no longer says so.
|
data/docs/configuration.md
CHANGED
|
@@ -119,7 +119,8 @@ own subsystems: `hosts:` (below), `models:` (see "Prompt profile" and
|
|
|
119
119
|
`guardrails:` (see [Guardrails](guardrails.md)), `bundles:` (a bundle's
|
|
120
120
|
settings for its hooks, see [Hooks: Settings](hooks.md#settings); a session's own
|
|
121
121
|
`--set BUNDLE.KEY=VALUE`, for a key the bundle's manifest lists in `session_settings:`, lays over them, see
|
|
122
|
-
[Per-session settings](plugins.md#per-session-settings)
|
|
122
|
+
[Per-session settings](plugins.md#per-session-settings); `--set KEY=VALUE` does the same for a few core keys,
|
|
123
|
+
see "Per-session settings" below),
|
|
123
124
|
`memories:` and `append_system_prompt:` (see "Appended prompt files"). Their entry names (host names, model ids, aliases) are yours to
|
|
124
125
|
choose. The `memories:` list is the persistent baseline for preloaded memory entries —
|
|
125
126
|
the same name shape as `--memory` (bare name or `scope/name`), merged under
|
|
@@ -160,7 +161,7 @@ or `chi web` restart.
|
|
|
160
161
|
| `context.every_seconds` | Next context poll | The poller checks it for each source that has no explicit interval. |
|
|
161
162
|
| `guardrails.*` | Next tool call or approval | Guardrail rules and disable lists are reloaded when the config file changes; rule settings take effect on that evaluation. `guardrails.parent_answers` is checked when a parent answers a question. |
|
|
162
163
|
| `hooks`, `bundles` | Worker start | Hooks, plugins and their bundle settings load with the engine. |
|
|
163
|
-
| `recap.*` | Worker start | The engine builds its recap job
|
|
164
|
+
| `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. |
|
|
164
165
|
| `session.shared` | Next `chi` invocation | It selects attached-worker or in-process mode for that invocation. |
|
|
165
166
|
| `session.idle_exit_minutes`, `session.keep_empty`, `session.max_children`, `session.delegate_reports`, `session.task_wakes`, `session.max_wakes` | Worker start or next matching lifecycle decision | Existing workers keep their idle-exit timer; the other values are read for the next corresponding decision. |
|
|
166
167
|
| `session.retention_*`, `session.max_count`, `session.keep_status`, `session.sweep_interval_hours` | Next retention sweep | `chi web` sweeps on its configured schedule and on `GET /api/sessions`; `chi sessions prune` uses the values for that invocation. The sweep interval is checked on the next hub tick. |
|
|
@@ -172,6 +173,26 @@ or `chi web` restart.
|
|
|
172
173
|
| `kitty.*`, `tmux.*` | `chi desktop upgrade` or desktop update | These settings are copied into the desktop helper's launch file. |
|
|
173
174
|
| `model.profile`, `reasoning_history` | Not from config.yml | These are CLI/environment settings; `models:` and `hosts:` provide their model- and host-specific forms. |
|
|
174
175
|
|
|
176
|
+
### Per-session settings
|
|
177
|
+
|
|
178
|
+
A few core keys can be set for one session: `chi --set KEY=VALUE` (or `chi send --new --set KEY=VALUE`, or
|
|
179
|
+
`POST /api/sessions` `settings`) at its creation, and `recap.enabled` later too with `/recap on`, `/recap off` or `/recap default`
|
|
180
|
+
(at once; `default` removes the session's own value, see [Session recap](cli.md#session-recap)). The value is saved in the session file (`config_settings`), so a
|
|
181
|
+
worker respawn, a continue and a fork keep it; a delegate child starts without it, on config.yml's. It goes first:
|
|
182
|
+
|
|
183
|
+
session (--set) → CLI → environment → config.yml → default
|
|
184
|
+
|
|
185
|
+
- `recap.enabled`: `true` or `false` (also `yes`/`on`/`1`, `no`/`off`/`0`); `true` turns the recap on for the
|
|
186
|
+
session even under `recap: false`.
|
|
187
|
+
- `recap.sentences`: `N` or `N-M` (1–10).
|
|
188
|
+
|
|
189
|
+
A value is typed by its key and checked when given: `--set recap.enabled=maybe` is refused (`recap.enabled takes
|
|
190
|
+
true or false, not "maybe"`), and so is a key no session sets (`--set guardrails.mode=strict`: not a session
|
|
191
|
+
setting). The same `--set` takes a bundle's `BUNDLE.KEY` (see [Per-session settings](plugins.md#per-session-settings));
|
|
192
|
+
a name that is a core key is always the core one, and a bundle can't offer a setting whose `NAME.KEY` is one.
|
|
193
|
+
`/stats` lists a session's own values (`settings: recap.enabled=false · cost-cap.cap=3`), and `chi sessions stats
|
|
194
|
+
--format json` has them as `settings`.
|
|
195
|
+
|
|
175
196
|
### Environment variables
|
|
176
197
|
|
|
177
198
|
Every setting has an environment variable (except the ones the table marks
|
|
@@ -1464,7 +1485,13 @@ models:
|
|
|
1464
1485
|
forget keeps lines of it; a keep outside the output's lines, of a big file's head/tail preview, or holding all of
|
|
1465
1486
|
it; an output too small for a stub to free anything; one already stubbed or forgotten; an unknown id, or one whose
|
|
1466
1487
|
output chi can't pair with its call for sure (it shows no id). The system prompt, user messages, steers and context
|
|
1467
|
-
notes have no ids.
|
|
1488
|
+
notes have no ids. An output you dropped, or one of an item you pinned, is refused too (`dropped by the user`,
|
|
1489
|
+
`pinned by the user: it stays in your context`).
|
|
1490
|
+
- **Pins.** `/working-set pin KEY…` (or the web list's `pin`, shown under forget only) keeps an item from the model's
|
|
1491
|
+
forget: every read of it, those to come too, until `/working-set unpin KEY…` (see [cli.md](cli.md#the-working-set)).
|
|
1492
|
+
It sends nothing different, so the prompt cache holds. The pins are the session's own (`working_set_pins` in the
|
|
1493
|
+
session file): a respawned worker, a continue and a fork keep them, across a strategy switch too; a delegate child
|
|
1494
|
+
starts without. Stale stubs still apply to a pinned file's superseded reads (its newer read is sent).
|
|
1468
1495
|
- **When stubs reach the prompt** follows `llm_context.apply`, as `stale`'s do: the result says
|
|
1469
1496
|
`applied, the next request sends the stubs (frees …)` or `staged until the turn ends`.
|
|
1470
1497
|
- **Restore.** `forget_outputs(restore: ["t45"])` brings a forgotten output back with the next request, whatever
|
|
@@ -1589,14 +1616,14 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
|
|
|
1589
1616
|
| `server.open_timeout` | `10` | yes | Connection timeout, seconds. |
|
|
1590
1617
|
| `server.read_timeout` | `600` | yes | Read timeout, seconds. |
|
|
1591
1618
|
| `server.first_token_timeout` | 120 remote, off local | | Seconds to the first token; `0` = off. `hosts.<name>.first_token_timeout` wins. |
|
|
1592
|
-
| `recap.enabled` | on | | `false` (or `recap: false`) turns the idle recap off. |
|
|
1619
|
+
| `recap.enabled` | on | | `false` (or `recap: false`) turns the idle recap off. Per session too: `--set recap.enabled=false`, or `/recap off` in the session (see "Per-session settings"). |
|
|
1593
1620
|
| `recap.model` | session's | yes | Model that writes the recap: an id, alias or `host:model` (the host then picks where to ask; a bare id goes where a bare `--model` goes). |
|
|
1594
1621
|
| `recap.host_ref` | session's | yes | A `hosts:` name to ask (`host:` is accepted too, also a host name, not a model ref). |
|
|
1595
1622
|
| `recap.base_url` | none | yes | An OpenAI API base to ask instead (`http://h:8081/v1`). |
|
|
1596
1623
|
| `recap.inactivity` | `180` | yes | Idle seconds before a recap. |
|
|
1597
1624
|
| `recap.timeout` | `30` | yes | Seconds a recap request may take. |
|
|
1598
1625
|
| `recap.min_user_turns` | `2` | yes | Prompts a session needs before it gets a recap. |
|
|
1599
|
-
| `recap.sentences` | `2-4` | yes | Recap length, `N` or `N-M` (1–10). |
|
|
1626
|
+
| `recap.sentences` | `2-4` | yes | Recap length, `N` or `N-M` (1–10). Per session too: `--set recap.sentences=3`. |
|
|
1600
1627
|
| `broadcast.active_hours` | `8` | | `chi broadcast` reaches a session a worker or a chi REPL runs, or whose last turn ended within this many hours. See [Broadcast](broadcast.md). |
|
|
1601
1628
|
| `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). |
|
|
1602
1629
|
| `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). |
|
data/docs/guardrails.md
CHANGED
|
@@ -582,7 +582,8 @@ hooks, approvals, bundles or attached context sources, or a `.git/hooks`
|
|
|
582
582
|
(`shell-touches-chi`, `touches: chi_dirs`), unless they only read
|
|
583
583
|
(`skip_read_only`); `chi context add … --cmd` (`chi-context-cmd`, once at a
|
|
584
584
|
time: a command chi will run every few minutes outside the guardrails; see
|
|
585
|
-
[Attached context](context.md#safety)
|
|
585
|
+
[Attached context](context.md#safety); the rule also sees `chi --remote NAME context add`, and a remote's `context push`
|
|
586
|
+
reaches its session with the trust of its chi web token, as your own `chi context push` does there); `chi broadcast` (`chi-broadcast`,
|
|
586
587
|
once at a time and only by the user: it is the user's command, which also
|
|
587
588
|
refuses to run inside a session; see [Broadcast](broadcast.md)); removing a
|
|
588
589
|
memory with `memory_write remove: true` (`memory-remove`, once at a time:
|
data/docs/internals/bundles.md
CHANGED
|
@@ -32,7 +32,7 @@ user-facing side (what a bundle owns, profiles, needs) is in [memory.md](../memo
|
|
|
32
32
|
| `SourceIdentity` (source.rb) | what a source is, for comparing two installs of one name: `shipped:<dir>` (any gem or checkout), `git:<url>` (no `#ref`, trailing `/` or `.git`), `path:<realpath>`; nil when it can't place one |
|
|
33
33
|
| `PreparedSource` (source.rb) | one source normalized once (dir, owned, git commit): `BundleCommand` reads the bundle's name from its `manifest.yml` (the name for every kind of source, a git URL too) and hands it to the Installer (`prepared:`), so a source is cloned or extracted once per command |
|
|
34
34
|
| `MemoryFiles` | which top-level `*.md` files of a bundle are its memories (`.select`: the `files:` list, else all but README/LICENSE and the like), `.unshipped` (listed, not shipped) and `.symlinks` (skipped: a symlink is never a memory) |
|
|
35
|
-
| `Manifest` | parses/validates `manifest.yml` (`name:` is the bundle's name and a directory under `.bundles/`, so a path such as `../x`, `a/b`, `.` or `..` is refused: `name: … is not a bundle name`; YAML that doesn't parse or isn't a mapping is a `ValidationError` too; files, hooks as `BundleHook`, plugin, scripts, needs, requires_chi, includes, context_providers, session_settings: the keys a session's `chi --set` may give, a list or a mapping of them to types (`SESSION_SETTING_TYPES`), recorded at install in `manifest.json` (`session_settings`, `session_setting_types`) for `
|
|
35
|
+
| `Manifest` | parses/validates `manifest.yml` (`name:` is the bundle's name and a directory under `.bundles/`, so a path such as `../x`, `a/b`, `.` or `..` is refused: `name: … is not a bundle name`; YAML that doesn't parse or isn't a mapping is a `ValidationError` too; files, hooks as `BundleHook`, plugin, scripts, needs, requires_chi, includes, context_providers, session_settings: the keys a session's `chi --set` may give, a list or a mapping of them to types (`SESSION_SETTING_TYPES`), recorded at install in `manifest.json` (`session_settings`, `session_setting_types`) for `SessionConfig` to check) and writes it (`Manifest.write`, for Builder). It ignores `requires_chi_next`, a shipped bundle's release-time marker (`script/release/release_tools.rb`, docs/releasing.md) |
|
|
36
36
|
| `Installer` | one install/upgrade/dry run of one bundle (below) |
|
|
37
37
|
| `AssetInstaller` | the parts that live in `.bundles/<name>/`: hooks, guardrail rules, plugin, scripts; returns `AssetInstaller::Installed` (the paths the record keeps) |
|
|
38
38
|
| `Merger` | 3-way classification per file: `install`, `fast_forward`, `keep`, `noop`, `conflict` |
|
data/docs/internals/engine.md
CHANGED
|
@@ -509,7 +509,7 @@ Turn events (the main ones; `Events` in `events.rb` keeps the shared sets):
|
|
|
509
509
|
| `:question_requested` / `:question_answered` / `:question_cancelled` | `QuestionDesk`: a question is asked (an approval, `ask_user_question`, a hook's or plugin's `ask_user`, the step-limit Continue), answered, or withdrawn | `{pending_question}` / `{id, answer}` / `{id, reason}` |
|
|
510
510
|
| `:plugin_init_wait` | a turn waits for plugin init tasks that provide tools (`PluginTasks`) | `{tasks: [{bundle, id, label}]}` |
|
|
511
511
|
| `:used_memories_updated` | a memory was read, and after the loop | `{used_memory_names, read_names?}` |
|
|
512
|
-
| `:working_set_updated` | after `:generation_started` (the running turn's conversation, the turn's strategy) and once in `end_turn` before the end event (the kept messages), only when the set differs from the last one emitted for the session and is not empty: a tool-free turn emits none (`Engine#emit_working_set`) | `{items: [{kind, key, state, tokens, ops, count, first_id}], at}` (`WorkingSet`, internals/working-set.md) |
|
|
512
|
+
| `:working_set_updated` | after `:generation_started` (the running turn's conversation, the turn's strategy) and once in `end_turn` before the end event (the kept messages), only when the set differs from the last one emitted for the session and is not empty, except one empty event when the last one had items (a rollback emptied the set): a tool-free turn emits none (`Engine#emit_working_set`); between turns `Engine#announce_working_set` announces it under the next turn's strategy (after a `/working-set` op, held behind the command's `command_ran`) | `{items: [{kind, key, state (live\|stale\|forgotten\|dropped), tokens, ops, count, first_id, pinned, has_drop}], at, adds?: [{kind, key, label}]}` (`WorkingSet`, internals/working-set.md; `adds` while the user's adds are pending) |
|
|
513
513
|
| `:hook_notice` | a hook's or plugin's notify during a turn | `{hook, text, level}` (announced with `between_turns: true` outside a turn) |
|
|
514
514
|
| `:guardrail_warning` | a guardrails line during a turn | `{message}` |
|
|
515
515
|
| `:turn_completed` | normal end | Engine event `{result, turn_summary, display_pending}`; the Bridge's SSE frame omits `result` (answer remains in `turn_summary.output`) |
|
|
@@ -35,12 +35,36 @@ lets the model forget its own tool outputs. The pieces:
|
|
|
35
35
|
- **Restore** takes the forget off the entry (`LLMContextEdit.remove`, a new edits Hash); the next request sends the
|
|
36
36
|
output again, outside the apply rule (the result says the tail the server reads again, and `ContextStatus#edited!`
|
|
37
37
|
restarts the estimate). A read whose stub was sent isn't restored; a staged one is.
|
|
38
|
+
- **Which edits are sent** is one predicate, `LLMContextView#sends?(edit)`: applied, and its kind one of the view's
|
|
39
|
+
layers or `:drop`. The view stubs by it and `WorkingSet` decides "still sent" by it. The view's layers are
|
|
40
|
+
`LLMContextStrategy::LAYERS` (`stale`, `forget`), the strategy's words, not `LLMContextEdit::KINDS`: whatever a
|
|
41
|
+
caller passes as the strategy, an edit kind no strategy names never becomes a layer that `apply_llm_context!` would
|
|
42
|
+
hand to `LLMContextApply.run!`. Under none the view has no layers and, unless an entry holds a drop
|
|
43
|
+
(`LLMContextEdit.drop_on?`, one scan), `#messages` returns the Array it was given.
|
|
44
|
+
- **The user's layer, always on**: an edit of kind `:drop` (`by: "user"`, the working set's drop:
|
|
45
|
+
[working-set.md](working-set.md#steering-drop-and-undrop)) is sent under every strategy, none included, as
|
|
46
|
+
`(dropped by the user) — read it again if the task needs it` after the output's lead. It is saved applied by the
|
|
47
|
+
op itself, between turns, so the apply rule never holds it and no `[#id]` is added under none. One edit per id: a
|
|
48
|
+
drop on an output that holds another edit keeps that edit's saved Hash in its `was:`, and an undrop puts it back.
|
|
49
|
+
`LLMContextApply.measure`/`.weigh` and `#restored_line` build their views from the same class, so a drop counts in
|
|
50
|
+
them under none too. The model can't take a drop off: forget_outputs refuses to forget or restore a dropped
|
|
51
|
+
output, in `WorkingSetSteer::Refused`'s words ("dropped by the user").
|
|
52
|
+
- **The user's pins**: an item the user pinned in the working set (`Session#working_set_pins`,
|
|
53
|
+
[working-set.md](working-set.md#steering-pin-and-unpin)) is guarded against the model's forget only: forget_outputs
|
|
54
|
+
refuses an output of it, in `WorkingSetSteer::Refused`'s words ("pinned by the user: it stays in your context").
|
|
55
|
+
`KernelLoop#forget_outputs` passes `LLMContextForget.call` a `pinned:` callable (`WorkingSet.pinned_ids` of the
|
|
56
|
+
session's pins, by the projection's pairing, the Engine's `working_set_pins`), asked once per forget; a pin covers
|
|
57
|
+
the item's later reads too. A restore of a pinned item's forget (one from before the pin) goes through. Stale stubs
|
|
58
|
+
still apply (a superseded read's newer read is sent, so the pin holds in substance), and a pin changes nothing that
|
|
59
|
+
is sent: no cache break.
|
|
38
60
|
- **Stubs** (`LLMContextView`): `(forgotten) <note>` on the first output of a call's forgets in a row, `(forgotten
|
|
39
61
|
with tN: see its note)` on the rest (a run of the same note, author and stamp, no other output between them),
|
|
40
62
|
` [restore: tN]` on a non-read with a stored id, then each kept range under `lines A-B kept:`. The entry keeps its
|
|
41
63
|
role, tool_call_id and images bookkeeping as for stale, and a find_tools result's `loaded:`, so a Messages host
|
|
42
64
|
still gets the load's references (anthropic.md, "Deferred tools"); only the sibling text is the stub.
|
|
43
|
-
- **Offers**: `ContextStatus` (internals/context-telemetry.md).
|
|
65
|
+
- **Offers**: `ContextStatus` (internals/context-telemetry.md). Its `TIDY`/`COMPACT` lines name no output, so a pin
|
|
66
|
+
has nothing to take out of them; the refusal is how the model learns of one if it tries (and the turn-start
|
|
67
|
+
`working_set` note says it was pinned).
|
|
44
68
|
- **Persistence**: the edits ride on the entries (plan D3), so `--resume`, rollbacks and forks keep the stubs and the
|
|
45
69
|
restores; `/stats` counts each applied batch's re-prefill.
|
|
46
70
|
- **Measuring**: the bench's `forget_outputs` strategy (internals/llm-context-bench.md).
|
|
@@ -56,7 +56,7 @@ right-hand column happens.
|
|
|
56
56
|
│ ◄── remote Claude / Messages: breakpoint #2 on the last │ the image cut moving
|
|
57
57
|
│ non-system message (Messages: its last user block) │ a batch, history
|
|
58
58
|
├──────────────────────────────────────────────────────────────────────────┤ sanitizing (thinking
|
|
59
|
-
│ tail notes reminders, turn notes, steers
|
|
59
|
+
│ tail notes reminders, turn notes, steers, the working-set note │ stripped per spec)
|
|
60
60
|
│ new prompt this turn's user message │
|
|
61
61
|
└──────────────────────────────────────────────────────────────────────────┘
|
|
62
62
|
```
|
|
@@ -108,6 +108,13 @@ right-hand column happens.
|
|
|
108
108
|
loaded name stays deferred, a stub once its source is gone), so the request after a load reads the whole prefix;
|
|
109
109
|
the loaded definitions and the new result are the fresh part. A plugin's
|
|
110
110
|
`ctx.load_tools` only appends (a system `tool_addition` after the user message, never a change to `tools`).
|
|
111
|
+
- A user's working-set drop (`/working-set drop`, [working-set.md](working-set.md#steering-drop-and-undrop)) changes
|
|
112
|
+
an earlier entry: it is saved between turns, so the next turn's first request is the first to send the stub and
|
|
113
|
+
the cache breaks there once, from the dropped output's entry on (the command's reply says that tail; an undrop
|
|
114
|
+
after the drop was sent breaks it once more). It is sent under every strategy, so it is the one edit that rewrites
|
|
115
|
+
history under `none`; without a drop, `none` sends the stored Array as it is. Its `working_set` note (what the user
|
|
116
|
+
dropped or brought back) is a tail system message after the history, one per turn with ops, escaped as context
|
|
117
|
+
notes are (`Prompt::ESCAPED_KINDS`). The turn-end warm-up never warms a drop sent after the turn: it misses once.
|
|
111
118
|
- Side requests (idle recap, `/btw`) go through `IdleClient` with their own short system prompt. On a current
|
|
112
119
|
llama.cpp they don't evict the session (see the server settings below); chi never pins them to a slot.
|
|
113
120
|
|