samagotchi 0.141.0 → 0.143.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 +111 -7
- data/README.md +2 -1
- data/bin/chi +2 -0
- data/docs/architecture.md +25 -289
- data/docs/bundle-authoring.md +7 -6
- data/docs/bundles.md +1232 -0
- data/docs/cli.md +51 -13
- data/docs/configuration.md +68 -30
- data/docs/context.md +12 -12
- data/docs/guardrails.md +27 -20
- data/docs/hooks.md +28 -9
- data/docs/internals/attached-context.md +225 -0
- data/docs/internals/background-tasks.md +1 -1
- data/docs/internals/bridge.md +380 -0
- data/docs/internals/bundles.md +6 -3
- data/docs/internals/context-telemetry.md +1 -1
- data/docs/internals/engine.md +31 -19
- data/docs/internals/participants.md +13 -12
- data/docs/internals/prompt-caching.md +12 -12
- data/docs/internals/remote-sessions.md +1 -1
- data/docs/internals/{tool-guardrails.md → tool-output-limits.md} +21 -2
- data/docs/internals/web-push.md +1 -1
- data/docs/memory.md +33 -3
- data/docs/plugins.md +127 -1231
- data/docs/sessions.md +51 -27
- data/docs/sub-agent.md +6 -6
- data/docs/system-one.md +122 -0
- data/docs/testing.md +3 -1
- data/lib/samagotchi/bridge.rb +2 -0
- data/lib/samagotchi/bundle_command/api_calls.rb +132 -0
- data/lib/samagotchi/bundle_command/check.rb +50 -34
- data/lib/samagotchi/bundle_command/installed_check.rb +86 -0
- data/lib/samagotchi/bundle_command/scaffold.rb +1 -0
- data/lib/samagotchi/bundle_settings_check.rb +58 -0
- data/lib/samagotchi/bundles/agents-md/manifest.yml +4 -2
- data/lib/samagotchi/bundles/agents-md/plugin.rb +1 -1
- data/lib/samagotchi/bundles/btw/manifest.yml +4 -2
- data/lib/samagotchi/bundles/btw/plugin.rb +1 -1
- data/lib/samagotchi/bundles/check-in/manifest.yml +7 -3
- data/lib/samagotchi/bundles/check-in/plugin.rb +101 -11
- data/lib/samagotchi/bundles/claude-peer/manifest.yml +3 -3
- data/lib/samagotchi/bundles/claude-peer/plugin.rb +1 -1
- data/lib/samagotchi/bundles/claude-peer/scripts/pair +1 -1
- data/lib/samagotchi/bundles/codex-peer/manifest.yml +2 -2
- data/lib/samagotchi/bundles/codex-peer/plugin.rb +1 -1
- data/lib/samagotchi/bundles/coordinator/manifest.yml +2 -2
- data/lib/samagotchi/bundles/coordinator/plugin.rb +1 -1
- data/lib/samagotchi/bundles/core/manifest.yml +2 -2
- data/lib/samagotchi/bundles/cost-cap/manifest.yml +6 -3
- data/lib/samagotchi/bundles/cost-cap/plugin.rb +5 -7
- data/lib/samagotchi/bundles/github-pr/manifest.yml +3 -1
- data/lib/samagotchi/bundles/known-names/manifest.yml +3 -1
- data/lib/samagotchi/bundles/loop-guard/manifest.yml +4 -2
- data/lib/samagotchi/bundles/loop-guard/plugin.rb +1 -1
- data/lib/samagotchi/bundles/mcp/manifest.yml +4 -2
- data/lib/samagotchi/bundles/mcp/plugin.rb +2 -2
- data/lib/samagotchi/bundles/ruby/manifest.yml +2 -2
- data/lib/samagotchi/bundles/ruby/plugin.rb +1 -1
- data/lib/samagotchi/bundles/skills/manifest.yml +4 -2
- data/lib/samagotchi/bundles/skills/plugin.rb +1 -1
- data/lib/samagotchi/bundles/slow-commands/manifest.yml +4 -2
- data/lib/samagotchi/bundles/slow-commands/plugin.rb +1 -1
- data/lib/samagotchi/bundles/source-links/manifest.yml +3 -1
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +3 -3
- data/lib/samagotchi/bundles/system/manifest.yml +4 -4
- data/lib/samagotchi/bundles/system/memory_guide.md +1 -1
- data/lib/samagotchi/bundles/system/self_map.md +3 -3
- data/lib/samagotchi/bundles/work-cap/manifest.yml +6 -3
- data/lib/samagotchi/bundles/work-cap/plugin.rb +11 -13
- data/lib/samagotchi/config.rb +74 -14
- data/lib/samagotchi/context_candidate_choice.rb +20 -3
- data/lib/samagotchi/context_candidates.rb +4 -1
- data/lib/samagotchi/context_candidates_log.rb +36 -1
- data/lib/samagotchi/context_judge.rb +6 -2
- data/lib/samagotchi/context_status.rb +1 -2
- data/lib/samagotchi/engine.rb +96 -70
- data/lib/samagotchi/extension_load.rb +11 -4
- data/lib/samagotchi/guardrail_wiring.rb +36 -31
- data/lib/samagotchi/guardrails/chi_dirs.rb +7 -4
- data/lib/samagotchi/guardrails/path_resolve.rb +1 -1
- data/lib/samagotchi/guardrails/protected_paths.rb +7 -4
- data/lib/samagotchi/guardrails_command.rb +3 -3
- data/lib/samagotchi/hook_routes.rb +37 -2
- data/lib/samagotchi/hooks/bundle_loader.rb +20 -4
- data/lib/samagotchi/hooks/registry.rb +23 -5
- data/lib/samagotchi/host_model.rb +3 -3
- data/lib/samagotchi/host_registry.rb +14 -7
- data/lib/samagotchi/judgments_command.rb +155 -0
- data/lib/samagotchi/kernel_loop.rb +20 -3
- data/lib/samagotchi/llm/chat_loop.rb +79 -10
- data/lib/samagotchi/llm/errors.rb +7 -5
- data/lib/samagotchi/llm/stray_tool_call.rb +68 -0
- data/lib/samagotchi/log_line.rb +1 -1
- data/lib/samagotchi/memory_bundle/builder.rb +4 -2
- data/lib/samagotchi/memory_bundle/entry_size.rb +100 -0
- data/lib/samagotchi/memory_bundle/installed_bundle.rb +9 -2
- data/lib/samagotchi/memory_bundle/installed_copy.rb +92 -0
- data/lib/samagotchi/memory_bundle/installer.rb +2 -0
- data/lib/samagotchi/memory_bundle/manifest.rb +23 -13
- data/lib/samagotchi/memory_bundle/provenance.rb +4 -1
- data/lib/samagotchi/memory_bundle/shipped_update.rb +13 -1
- data/lib/samagotchi/model_catalog.rb +3 -2
- data/lib/samagotchi/models_command.rb +1 -1
- data/lib/samagotchi/plugin/api.rb +41 -7
- data/lib/samagotchi/plugin/context.rb +59 -1
- data/lib/samagotchi/plugin/judgments.rb +72 -0
- data/lib/samagotchi/plugin/loader.rb +6 -3
- data/lib/samagotchi/plugin/peer.rb +17 -22
- data/lib/samagotchi/plugin_session_state.rb +32 -5
- data/lib/samagotchi/prompt_warmup.rb +1 -9
- data/lib/samagotchi/run_options.rb +1 -0
- data/lib/samagotchi/self_report.rb +36 -0
- data/lib/samagotchi/session_access/contract.rb +1 -1
- data/lib/samagotchi/session_access/local.rb +1 -1
- data/lib/samagotchi/session_access/remote.rb +3 -3
- data/lib/samagotchi/session_archive_command.rb +3 -3
- data/lib/samagotchi/session_commands.rb +7 -4
- data/lib/samagotchi/session_config.rb +9 -3
- data/lib/samagotchi/session_continue.rb +1 -1
- data/lib/samagotchi/session_manager.rb +25 -5
- data/lib/samagotchi/session_retention.rb +10 -0
- data/lib/samagotchi/side_judgments.rb +12 -1
- data/lib/samagotchi/system_one/answer.rb +63 -11
- data/lib/samagotchi/system_one/approval_label.rb +56 -0
- data/lib/samagotchi/system_one/approval_question.rb +2 -0
- data/lib/samagotchi/system_one/client.rb +43 -17
- data/lib/samagotchi/system_one/fake_backend.rb +1 -1
- data/lib/samagotchi/system_one/http_backend.rb +12 -4
- data/lib/samagotchi/system_one/judgment_settings.rb +61 -0
- data/lib/samagotchi/system_one/judgments.rb +243 -0
- data/lib/samagotchi/system_one/log.rb +25 -2
- data/lib/samagotchi/system_one/recording.rb +117 -0
- data/lib/samagotchi/system_one/runs.rb +179 -0
- data/lib/samagotchi/system_one/runs_store.rb +230 -0
- data/lib/samagotchi/system_one.rb +9 -0
- data/lib/samagotchi/terminal_ui/attach_link.rb +2 -2
- data/lib/samagotchi/terminal_ui/attached_loop/exit_commands.rb +1 -1
- data/lib/samagotchi/terminal_ui/attached_loop.rb +1 -1
- data/lib/samagotchi/terminal_ui/local_link.rb +1 -1
- data/lib/samagotchi/terminal_ui/remote_link.rb +1 -2
- data/lib/samagotchi/testing/bundle.rb +9 -2
- data/lib/samagotchi/testing/host.rb +2 -1
- data/lib/samagotchi/testing/judgments.rb +176 -0
- data/lib/samagotchi/testing/recorder.rb +5 -0
- data/lib/samagotchi/testing/recording_api.rb +1 -1
- data/lib/samagotchi/tool_output_cut.rb +26 -6
- data/lib/samagotchi/tool_response.rb +1 -1
- data/lib/samagotchi/tool_runner.rb +63 -4
- data/lib/samagotchi/toolkit_suggest.rb +11 -2
- data/lib/samagotchi/tools/memory.rb +28 -0
- data/lib/samagotchi/turn_note.rb +9 -0
- data/lib/samagotchi/update_command.rb +49 -16
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +1 -0
- data/lib/samagotchi/web/auto_update.rb +79 -49
- data/lib/samagotchi/web/auto_update_record.rb +59 -0
- data/lib/samagotchi/web/public/app.js +8 -4
- data/lib/samagotchi/web/public/hash_intent.js +24 -0
- data/lib/samagotchi/web/public/index.html +3 -1
- data/lib/samagotchi/web/public/revise_stack.js +14 -5
- data/lib/samagotchi/web/session_control.rb +3 -3
- data/lib/samagotchi/web/update_check.rb +7 -6
- data/lib/samagotchi/web/update_ops.rb +80 -25
- metadata +23 -2
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# Participants: identity and routes
|
|
2
2
|
|
|
3
3
|
A session's owner can invite **participants** to review it: people, or agents, who reach that one session through
|
|
4
|
-
chi web with a link of their own. This page is the identity layer
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
experimental. An agent participant waits on its own turns' replies through the API. The
|
|
8
|
-
"participants" and "
|
|
4
|
+
chi web with a link of their own. This page is the identity layer: grants, the gate, the routes a participant
|
|
5
|
+
reaches, and what is written where; and their **turns**: review turns queued, read-only, limited and capped, pair
|
|
6
|
+
turns that act under the owner's guardrails; and what they **see**: their page and its reads, all through one
|
|
7
|
+
filter. Participants are experimental. An agent participant waits on its own turns' replies through the API. The
|
|
8
|
+
code and docs say "participants", "review" and "pair", never "shared": `session.shared` already means "attached by
|
|
9
|
+
default".
|
|
9
10
|
|
|
10
11
|
Participants reach chi web on the **LAN** (`web.host: lan`), or **behind an auth proxy** with `web.public_url`
|
|
11
12
|
(experimental; docs/configuration.md, "Behind an auth proxy"): there every request needs a token, this machine's too,
|
|
@@ -43,9 +44,9 @@ participant** and **one role**, until it expires or is revoked.
|
|
|
43
44
|
newer chi's: read (its links work, but a grant with keys this chi doesn't know is left out: such a key may
|
|
44
45
|
restrict it, so it fails closed) and never written (`Grants::Newer`, one message saying to update chi; a revoke
|
|
45
46
|
the gate can't write still refuses the grant). A grant's keys this chi doesn't know are kept on a rewrite
|
|
46
|
-
(`Grant#extra`), so a field a later chi adds the omit-if-default way survives.
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
(`Grant#extra`), so a field a later chi adds the omit-if-default way survives. **Mixed installs** on one state dir
|
|
48
|
+
are a hazard: an older chi that doesn't know `identity` unbinds every grant when it rewrites the store, and one
|
|
49
|
+
that doesn't keep a grant's unknown keys drops them. A stored `identity` that isn't one
|
|
49
50
|
`#issue` would write (`false`, `""`, a list, upper case) makes the store Corrupt (fails closed). Readers follow its mtime (a `chi share revoke` in another process takes effect at chi web's next request).
|
|
50
51
|
`last_used_at` is written at most once a minute per grant. **chi web refuses to start** when that folder or file can
|
|
51
52
|
be read by another user, or isn't this user's (`Grants.check_permissions!`, one message naming the `chmod`). Only
|
|
@@ -648,7 +649,7 @@ invited to has none: its files stay byte for byte as they were.
|
|
|
648
649
|
expires_at, removed_at, cap}]}`, rewritten by whoever changes a grant (chi web, `chi share`), inside the grants
|
|
649
650
|
store's lock (`Grants#update`'s `on_write`), so two writers can't finish out of order and lose a revoke here. Never
|
|
650
651
|
a token or a digest.
|
|
651
|
-
The worker reads it with `Participants.read` (
|
|
652
|
+
The worker reads it with `Participants.read` (`SessionParticipants#refresh!`, for the system prompt's participants line).
|
|
652
653
|
- **`participants.jsonl`** (0600, append-only, `Participants.log`): one JSON line per event, with `at`: `issued`
|
|
653
654
|
(participant, name, kind, role, expires_at), `revoked` (participant, by), `expired` (the first refused use after the
|
|
654
655
|
expiry, once per chi web), `peer` (the first use and each new peer since chi web started: ip, user agent; behind a
|
|
@@ -668,8 +669,8 @@ invited to has none: its files stay byte for byte as they were.
|
|
|
668
669
|
|
|
669
670
|
What a participant writes is marked as theirs everywhere it is kept or sent. The
|
|
670
671
|
author is `"p:<id>"` (`Grant#author`), stamped by chi web from the principal and never taken from a body. Absent
|
|
671
|
-
means the owner:
|
|
672
|
-
|
|
672
|
+
means the owner: a turn or session with no author keys reads as the owner's and gains none on save
|
|
673
|
+
(`spec/turn_author_record_spec.rb`).
|
|
673
674
|
|
|
674
675
|
- **Messages.** A participant's opening message is saved raw with `author`, `author_name` (the name the sidecar gave
|
|
675
676
|
the participant when it was written) and `participant_mode` (`ParticipantMode.of` when `Engine#run_turn` builds it, before any request carries it). Merged input and
|
|
@@ -741,7 +742,7 @@ participant TUI in v1.
|
|
|
741
742
|
worker announces `:participants_changed` (`Engine#announce_participants`: `{participants: [{id, name, kind}],
|
|
742
743
|
joined: […], left: [{…, reason: revoked | expired}]}`) to every UI; its start announces nobody. "Joined" means
|
|
743
744
|
invited: participants have no presence of their own yet. `session_state_snapshot`'s `participants` names who is in
|
|
744
|
-
the review now, so a UI that joins has them. Owner frames only: no participant stream carries them (
|
|
745
|
+
the review now, so a UI that joins has them. Owner frames only: no participant stream carries them (`Web::ParticipantView`).
|
|
745
746
|
- **Lines.** A participant's prompt (`turn_enqueued`'s `author`, an origin's, a queued row's, a saved message's
|
|
746
747
|
`author` / `author_name`) is led by their name (`Formatting#participant_prefix`: `Alex ▸ review>`, cleaned by
|
|
747
748
|
`ParticipantName`, the id when the TUI knows no name), in a colour picked from their id. Each change is one line per
|
|
@@ -80,19 +80,18 @@ right-hand column happens.
|
|
|
80
80
|
`toolkit.suggest.defer_turns` / `defer_tokens`) is engine-side, so `/toolkit`'s CacheConfirm doesn't apply: it
|
|
81
81
|
changes the declared tools at the front of the request, and the server re-reads **system prompt + tools +
|
|
82
82
|
conversation** on that turn. The defer bound keeps that conversation small; its notice says "from this turn".
|
|
83
|
-
When System 1 runs on the model's own single-slot local server (`Engine#system_one_shares_local_host?`), any
|
|
84
|
-
ask takes the slot and evicts its cached prefix: a first-turn ask costs that cache even when nothing switches
|
|
85
|
-
(there is little cached on a session's first turn), so on mode asks only there, skips the deferred path, and
|
|
86
|
-
sends no worker warm-up.
|
|
87
83
|
- The candidate judge (`context.candidates.mode`, `ContextJudge`) adds nothing to the model's request: its asks
|
|
88
84
|
go to System 1 only, and nothing it answers enters the running turn's history: on mode's attach writes only the
|
|
89
|
-
session's context folder, and its note comes with the between-turn delivery (`ContextAbsorber`), never mid-turn.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
85
|
+
session's context folder, and its note comes with the between-turn delivery (`ContextAbsorber`), never mid-turn.
|
|
86
|
+
Its asks start beside the turn on any host. Bounded: at most two judged asks per candidate (one re-ask after
|
|
87
|
+
`reask_after_turns` owner prompts), plus a deferred ask per owner prompt before a task is stated.
|
|
88
|
+
- System 1's asks (`SystemOne::Client`, `/v1/systemone`) go beside the turn on any host, the model's own Splash too,
|
|
89
|
+
because they don't cost the session its cached prompt there. Measured 2026-11-01 on a local Splash with a 60k-token
|
|
90
|
+
session: the next request still had 60,064 tokens cached in 0.13 s after 3 asks (0.09 s with none), and 60,160 cached
|
|
91
|
+
in 0.18 s after 10 asks during generation; asks answered in 108-158 ms during a turn, and those 10 took the
|
|
92
|
+
generation from 2.87 s to 4.43 s. An earlier guard skipped or deferred them on the assumption that an ask takes the
|
|
93
|
+
slot and evicts the prefix, never measured there; don't rebuild it from that assumption. Not measured: a prefix past
|
|
94
|
+
Splash's cache capacity (262144 tokens).
|
|
96
95
|
- A kit's prompt text (`toolkit.kits.<name>.prompt:`, `chi.toolkit … prompt:`) sits after the appended files and
|
|
97
96
|
before the memory indexes (`SystemPrompt#kit_prompt_section`): one prefix per kit, never per-session text, so
|
|
98
97
|
sessions on the kit share it; a memory write (the indexes after it) doesn't move it. It is part of the
|
|
@@ -275,7 +274,8 @@ is prefilled once. The native and the chat loop use the same plan.
|
|
|
275
274
|
## Server-side settings worth knowing
|
|
276
275
|
|
|
277
276
|
- **Splash:** `--persistent-cache` (with `--max-cache-disk`) keeps restore points across restarts. Splash learns
|
|
278
|
-
a new branch point on the 2nd session, so the 3rd one gets the full reuse.
|
|
277
|
+
a new branch point on the 2nd session, so the 3rd one gets the full reuse. System 1's asks on the same server leave
|
|
278
|
+
a session's cached prefix alone (measured 2026-11-01, above).
|
|
279
279
|
- **llama.cpp:** `--cache-ram` (the host-memory prompt cache, 8 GiB by default in recent builds) is what keeps
|
|
280
280
|
sessions apart: when a task starts on a slot, the idle slots' state goes to that cache, and an unpinned request
|
|
281
281
|
restores the best match on whatever slot it gets. So a recap or another session between two turns costs
|
|
@@ -7,7 +7,7 @@ sessions, workers, memories and code stay on that machine; this one only sends r
|
|
|
7
7
|
User-facing behaviour is in [cli.md](../cli.md#remote-sessions). This page is the code's shape, for changing it.
|
|
8
8
|
|
|
9
9
|
**The one rule:** the remote side is that machine's **chi web**, never a worker's Bridge. Bridges stay on loopback,
|
|
10
|
-
and answer only a request that names their secret from `bridge.json` ([
|
|
10
|
+
and answer only a request that names their secret from `bridge.json` ([bridge.md](bridge.md)).
|
|
11
11
|
chi web answers each request by calling the same `SessionAccess::Local` operation the CLI would call there.
|
|
12
12
|
|
|
13
13
|
## The seam: `SessionAccess`
|
|
@@ -58,7 +58,7 @@ Implementation note:
|
|
|
58
58
|
|
|
59
59
|
## The runner's cut (`max_tool_output_chars`)
|
|
60
60
|
|
|
61
|
-
After a tool's own limits, `ToolRunner` caps every result at `max_tool_output_chars` (default `10000`
|
|
61
|
+
After a tool's own limits, `ToolRunner` caps every result but a `memory_read`'s at `max_tool_output_chars` (default `10000`
|
|
62
62
|
characters, both loops). How a longer one is cut is the tool's policy, `ToolOutputCut::POLICIES` (by the
|
|
63
63
|
built-in name; a tool with none gets `:head`):
|
|
64
64
|
|
|
@@ -67,7 +67,26 @@ built-in name; a tool with none gets `:head`):
|
|
|
67
67
|
| `execute` | `:head_tail_file` | a short head, a long tail, one gap line between them; the whole output saved in the session's folder |
|
|
68
68
|
| `task_wait` | `:head_tail_ref` | the same head and tail, the gap line naming the task's log (`output_path`, `Tools::RefText`); nothing saved |
|
|
69
69
|
| `delegate`, `delegate_result` | `:head_tail_ref` | the same, the gap line naming the child's reply file (its `output/<timestamp>.txt`); nothing saved |
|
|
70
|
-
| `read` and every other tool (`task_get`/`task_list`/`task_create`/`task_stop`, `web_fetch`, `
|
|
70
|
+
| `read` and every other tool (`task_get`/`task_list`/`task_create`/`task_stop`, `web_fetch`, `context_read`, plugin and MCP tools) | `:head` | the first chars, then `[cut: N of M chars; read it in parts]` |
|
|
71
|
+
| `memory_read` | `:head`, its own cap | not cut by `max_tool_output_chars`; see below |
|
|
72
|
+
|
|
73
|
+
`memory_read` is the one tool `max_tool_output_chars` does not cap, in any form (a name, a comma list, a blank name for the
|
|
74
|
+
indexes): a memory is read whole, since a model that needs the skill pays for all of it anyway. `ToolRunner#output_cap`
|
|
75
|
+
resolves the cap per call, so both loops get it (they both come through `ToolResponse.run_batch`). Two things can still
|
|
76
|
+
cut a `memory_read` result, both with the same `:head` cut and a line that says where the rest is, since `memory_read`
|
|
77
|
+
has no parts to read:
|
|
78
|
+
|
|
79
|
+
- `memory.read_max_chars` (default `0`: none). A positive value caps the result at that many characters.
|
|
80
|
+
- The overflow guard, with `memory.read_max_chars` at 0: a result over half the model's context window (in characters,
|
|
81
|
+
tokens x `context.chars_per_token`) is cut. A context overflow is a hard failure (`LLM::FailedTurn.keeps_work?` is
|
|
82
|
+
false), so the turn's work rolls back and a retry would repeat the same read. The window is the loop's own:
|
|
83
|
+
`KernelLoop#tool_output_window` (the client's probe, the turn's `window_tokens` setting), or for `LLM::ChatLoop` its
|
|
84
|
+
adapter's model list too (`ToolRunner.new(kernel, window:)`). An unknown window (`ContextWindow` falling back to
|
|
85
|
+
its default) has no guard. It checks this one result, not the context already used.
|
|
86
|
+
|
|
87
|
+
The line is `[cut: N of M chars; the rest is in the memory's .md file in <the memories dir(s) searched>: read it with
|
|
88
|
+
start_line]` ("the memories' .md files" for a comma list or the indexes). `ToolOutputCut::HEAD_CUT` does not match it.
|
|
89
|
+
`read` on a memory's file keeps the `max_tool_output_chars` cut: it has line ranges.
|
|
71
90
|
|
|
72
91
|
`:head_tail_file` is for output whose end matters: a test run's summary, an error, the `exit:` line. The head is
|
|
73
92
|
`execute.cut_head_chars` at most (default `1000`) and never more than a quarter of the room; the tail gets
|
data/docs/internals/web-push.md
CHANGED
|
@@ -188,7 +188,7 @@ from saying it twice across restarts). The key's `chi:` prefix keeps sw.js from
|
|
|
188
188
|
|
|
189
189
|
`updated` is sent by the **restarted** chi web (its AutoUpdate finds the run at `phase: web`): the exec ends the
|
|
190
190
|
old process, and `Notifier#stop` does not flush the shows still in their hold, so a push queued just before it would
|
|
191
|
-
be lost. Sending from the new process also proves it came up.
|
|
191
|
+
be lost. Sending from the new process also proves it came up. The workers left then restart without another show.
|
|
192
192
|
|
|
193
193
|
**Seeding**: `Notifier#start(hub)` subscribes with `snapshot: true` and takes the summaries as the seed, so what is
|
|
194
194
|
open already never pushes. The hub's projection is empty before its first scan, so `Server.start` runs `hub.scan`
|
data/docs/memory.md
CHANGED
|
@@ -182,7 +182,7 @@ Working habits for this model:
|
|
|
182
182
|
`model_notes_deepseek` (`models: *deepseek-v4*`) and `model_notes_tone`
|
|
183
183
|
(`models: *`, concise answers for every model). Each note's provenance
|
|
184
184
|
and evidence is in the bundle's `notes/README.md`, and its spec pins which
|
|
185
|
-
model ids load which note ([
|
|
185
|
+
model ids load which note ([Bundles](bundles.md#the-model-notes-bundle)).
|
|
186
186
|
- **Your own note of the same name wins.** The install skips a
|
|
187
187
|
`model_notes_<name>.md` you already have and never owns it, so your note
|
|
188
188
|
is what loads, the two never stack, and upgrades leave it alone.
|
|
@@ -225,7 +225,7 @@ memory's line are left out), so their size is paid on every request. Nothing cap
|
|
|
225
225
|
or trims them; chi measures them (tokens estimated as characters /
|
|
226
226
|
`context.chars_per_token`) and shows the size:
|
|
227
227
|
|
|
228
|
-
- `chi self` has a `memory index` row:
|
|
228
|
+
- `chi self` has a `memory index` row (and a `big memories` row, see [A memory's size](#a-memorys-size)):
|
|
229
229
|
`system ~2.0k tokens (54 lines), project ~1.4k tokens (35 lines)`, with
|
|
230
230
|
`, over 2500` inside the parentheses for a scope over
|
|
231
231
|
`memory.index_warn_tokens`.
|
|
@@ -252,6 +252,36 @@ may each get it. Bundle installs and uninstalls and hand edits
|
|
|
252
252
|
change index lines without a note; `chi self`'s row shows where an index
|
|
253
253
|
stands.
|
|
254
254
|
|
|
255
|
+
## A memory's size
|
|
256
|
+
|
|
257
|
+
`memory_read` returns a memory whole: `max_tool_output_chars` does not cut it (see [Tool output
|
|
258
|
+
guardrails](internals/tool-output-limits.md#the-runners-cut-max_tool_output_chars)), so each read of a memory pays its
|
|
259
|
+
size. Two limits stay:
|
|
260
|
+
|
|
261
|
+
- `memory.read_max_chars` (default `0`, no fixed cap) caps a `memory_read` result at that many characters.
|
|
262
|
+
- An overflow guard: with that at 0 and a known context window, a result over half the window is cut. A context
|
|
263
|
+
overflow fails the turn and rolls its work back, and a retry repeats the read. With an unknown window there is no guard.
|
|
264
|
+
It checks one result, not the context already used.
|
|
265
|
+
|
|
266
|
+
A cut result ends with `[cut: N of M chars; the rest is in the memory's .md file in <dir>: read it with start_line]`;
|
|
267
|
+
`read` the file with `start_line` for the rest.
|
|
268
|
+
|
|
269
|
+
`memory.entry_warn_tokens` (default `5000`, about 20K characters at the default `context.chars_per_token`; `0` turns it
|
|
270
|
+
off) is the size a write may take one memory to before the model is told. When a `memory_write` with content, or a
|
|
271
|
+
`write`/`edit` on a memory file, takes a memory from at or under the limit to over it, the tool result ends with one note
|
|
272
|
+
(a memory that did not exist counts as 0, so a first big write gets it):
|
|
273
|
+
|
|
274
|
+
> Note: memory 'NAME' is now ~N tokens (over memory.entry_warn_tokens 5000), paid on each read. When you next have a moment, tighten it; split out only parts that are read separately, since each new memory adds an index line to every prompt. Don't remove or merge memories unless the user asks.
|
|
275
|
+
|
|
276
|
+
Once per crossing, as the index's note: a write while the memory is already over gets none. None for a
|
|
277
|
+
description-only write, a remove, the verbatim `index` write, a model overlay, a `model_notes_*` memory (it has its own
|
|
278
|
+
warning), a bundle install or a hand edit. It is independent of `memory.index_warn_tokens`: with that at 0 this note
|
|
279
|
+
still comes, and a write that crosses both has both notes. Nothing is said when a memory is read: that would repeat on
|
|
280
|
+
every read. `chi self` has a `big memories` row instead: the memories over the limit in both scopes, largest first
|
|
281
|
+
(5, then `+N more`), `none` when there are none, a bundle's tagged `from <bundle>` (the shipped
|
|
282
|
+
`config_modification_protocol` is over on every install; the fix is upstream's). This is how a
|
|
283
|
+
memory already over the limit gets seen.
|
|
284
|
+
|
|
255
285
|
## Skills
|
|
256
286
|
|
|
257
287
|
A **skill** is a memory named `skill_<name>` that holds the steps of a
|
|
@@ -290,7 +320,7 @@ A skill is plain Markdown, no frontmatter:
|
|
|
290
320
|
|
|
291
321
|
It is a memory like any other: `memory_read`, `chi --mute skill_release`,
|
|
292
322
|
`chi bundle build` share it. The optional `skills` bundle (`chi bundle install
|
|
293
|
-
skills`, [docs/
|
|
323
|
+
skills`, [docs/bundles.md](bundles.md#the-skills-bundle)) adds `/skill save`,
|
|
294
324
|
`/skill list`, `/skill show`, `/skill diff`, keeps older versions with a
|
|
295
325
|
one-line diff after each update, and nudges a model that skips a failing
|
|
296
326
|
step instead of fixing the skill.
|