samagotchi 0.140.0 → 0.142.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 +64 -1
- data/README.md +1 -1
- data/bin/chi +2 -0
- data/docs/architecture.md +14 -285
- data/docs/bundle-authoring.md +1 -0
- data/docs/cli.md +15 -7
- data/docs/configuration.md +57 -18
- data/docs/context.md +68 -6
- data/docs/guardrails.md +20 -11
- data/docs/hooks.md +9 -7
- data/docs/internals/attached-context.md +226 -0
- data/docs/internals/background-tasks.md +1 -1
- data/docs/internals/bridge.md +380 -0
- data/docs/internals/bundles.md +1 -0
- data/docs/internals/context-telemetry.md +1 -1
- data/docs/internals/engine.md +24 -18
- data/docs/internals/participants.md +13 -12
- data/docs/internals/remote-sessions.md +5 -2
- data/docs/internals/{tool-guardrails.md → tool-output-limits.md} +21 -2
- data/docs/memory.md +31 -1
- data/docs/plugins.md +162 -2
- data/docs/sessions.md +53 -28
- data/docs/system-one.md +122 -0
- data/docs/testing.md +3 -1
- data/lib/samagotchi/bridge.rb +2 -0
- data/lib/samagotchi/bundles/check-in/manifest.yml +6 -3
- data/lib/samagotchi/bundles/check-in/plugin.rb +101 -4
- data/lib/samagotchi/bundles/claude-peer/manifest.yml +2 -2
- data/lib/samagotchi/bundles/claude-peer/scripts/chi-peer-SKILL.md +14 -0
- data/lib/samagotchi/bundles/codex-peer/manifest.yml +2 -2
- data/lib/samagotchi/bundles/codex-peer/scripts/chi-peer-SKILL.md +15 -1
- data/lib/samagotchi/bundles/core/manifest.yml +2 -2
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +2 -2
- 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 +1 -1
- data/lib/samagotchi/config.rb +74 -14
- data/lib/samagotchi/context_candidate_choice.rb +8 -0
- data/lib/samagotchi/context_candidates_log.rb +25 -1
- data/lib/samagotchi/context_command.rb +32 -9
- data/lib/samagotchi/context_judge.rb +6 -2
- data/lib/samagotchi/context_move.rb +31 -0
- data/lib/samagotchi/context_sources.rb +248 -10
- data/lib/samagotchi/engine.rb +68 -7
- data/lib/samagotchi/extension_load.rb +3 -2
- 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/judgments_command.rb +155 -0
- data/lib/samagotchi/kernel_loop.rb +13 -1
- data/lib/samagotchi/llm/chat_loop.rb +74 -10
- data/lib/samagotchi/llm/errors.rb +7 -5
- data/lib/samagotchi/llm/stray_tool_call.rb +61 -0
- data/lib/samagotchi/log_line.rb +1 -1
- data/lib/samagotchi/memory_bundle/entry_size.rb +100 -0
- data/lib/samagotchi/plugin/api.rb +33 -2
- data/lib/samagotchi/plugin/attached_context.rb +14 -2
- data/lib/samagotchi/plugin/context.rb +54 -1
- data/lib/samagotchi/plugin/judgments.rb +72 -0
- data/lib/samagotchi/plugin/loader.rb +3 -2
- data/lib/samagotchi/plugin/peer.rb +53 -0
- data/lib/samagotchi/plugin/sessions.rb +9 -2
- data/lib/samagotchi/run_options.rb +1 -0
- data/lib/samagotchi/self_report.rb +36 -0
- data/lib/samagotchi/send_command.rb +3 -1
- data/lib/samagotchi/session.rb +4 -0
- data/lib/samagotchi/session_access/contract.rb +3 -1
- data/lib/samagotchi/session_access/local.rb +24 -3
- data/lib/samagotchi/session_access/remote.rb +19 -8
- data/lib/samagotchi/session_access.rb +26 -6
- data/lib/samagotchi/session_commands.rb +3 -0
- data/lib/samagotchi/session_continue.rb +9 -2
- data/lib/samagotchi/session_manager.rb +13 -2
- 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 +260 -0
- data/lib/samagotchi/system_one/log.rb +4 -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/testing/boundary.rb +5 -1
- data/lib/samagotchi/testing/bundle.rb +9 -2
- data/lib/samagotchi/testing/host.rb +2 -1
- data/lib/samagotchi/testing/judgments.rb +185 -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/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +14 -8
- data/lib/samagotchi/web/public/chains.js +10 -2
- data/lib/samagotchi/web/session_access_routes.rb +7 -2
- data/lib/samagotchi/web/session_create.rb +1 -0
- metadata +17 -2
data/docs/configuration.md
CHANGED
|
@@ -149,7 +149,7 @@ or `chi web` restart.
|
|
|
149
149
|
|---|---|---|
|
|
150
150
|
| `default.model` | New session | Existing sessions keep their saved model; change one with `/model`. |
|
|
151
151
|
| `default.input`, `no_default_input`, `no_interrupt` | Next CLI run | These set up that run's first prompt or turn limit. |
|
|
152
|
-
| `default.max_tokens`, `max_tool_output_chars`, `read.*`, `execute.*` except `execute.description`, `image.*` | Next matching request or tool use | An operation already in progress keeps its resolved limits. |
|
|
152
|
+
| `default.max_tokens`, `max_tool_output_chars`, `memory.read_max_chars`, `read.*`, `execute.*` except `execute.description`, `image.*` | Next matching request or tool use | An operation already in progress keeps its resolved limits. |
|
|
153
153
|
| `server.host`, `server.port`, `hosts.*` | Worker start | Includes host addresses, APIs, declared models and host-level settings. The web model picker refreshes `hosts:` on file change; a running worker does not. |
|
|
154
154
|
| `server.open_timeout`, `server.read_timeout`, `server.first_token_timeout` | Worker start | The worker's clients retain the timeouts set when they were built. |
|
|
155
155
|
| `model_aliases` | Next model lookup | A session keeps its resolved model unless it is explicitly switched. |
|
|
@@ -166,9 +166,10 @@ or `chi web` restart.
|
|
|
166
166
|
| `toolkit.kits`, `execute.description`, `llm_context.policy` | Worker start | These are held by the engine's toolkit or tool registry. |
|
|
167
167
|
| `toolkit.suggest.mode`, `toolkit.suggest.threshold`, `toolkit.suggest.defer_turns`, `toolkit.suggest.defer_tokens` | Worker start | Suggestion settings are config-only and are resolved when the worker runs its audit or on-mode ask. |
|
|
168
168
|
| `skip_agents_md` | Prompt rebuild | A running engine caches its built prompt; a new worker or `/model` rebuild uses the changed value. |
|
|
169
|
-
| `context.*` except `context.every_seconds`, `llm_context.*` except `llm_context.policy`, `steer.cut_after`, `memory.index_warn_tokens`, `status.line`, `web.participants.*` | Next matching turn, display, poll or write | Readers resolve these settings when they perform that operation. Session-specific LLM context and thinking values remain saved in the session. A participant's grant keeps the expiry and cap it was issued with; `web.participants.*` is what the next invite gets, and the cap and queue of a grant from before caps. |
|
|
169
|
+
| `context.*` except `context.every_seconds`, `llm_context.*` except `llm_context.policy`, `steer.cut_after`, `memory.index_warn_tokens`, `memory.entry_warn_tokens`, `status.line`, `web.participants.*` | Next matching turn, display, poll or write | Readers resolve these settings when they perform that operation. Session-specific LLM context and thinking values remain saved in the session. A participant's grant keeps the expiry and cap it was issued with; `web.participants.*` is what the next invite gets, and the cap and queue of a grant from before caps. |
|
|
170
170
|
| `context.every_seconds` | Next context poll | The poller checks it for each source that has no explicit interval. |
|
|
171
171
|
| `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. |
|
|
172
|
+
| `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. |
|
|
172
173
|
| `hooks`, `bundles` | Worker start | Hooks, plugins and their bundle settings load with the engine. |
|
|
173
174
|
| `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. |
|
|
174
175
|
| `session.shared` | Next `chi` invocation | It selects attached-worker or in-process mode for that invocation. |
|
|
@@ -185,7 +186,7 @@ or `chi web` restart.
|
|
|
185
186
|
|
|
186
187
|
### Per-session settings
|
|
187
188
|
|
|
188
|
-
A few core keys can be set for one session: `chi --set KEY=VALUE` (or `chi send --new --set KEY=VALUE`, or
|
|
189
|
+
A few core keys can be set for one session (the registry entries marked `session: true`, `Config::Entry#session?`): `chi --set KEY=VALUE` (or `chi send --new --set KEY=VALUE`, or
|
|
189
190
|
`POST /api/sessions` `settings`) at its creation, and `recap.enabled` later too with `/recap on`, `/recap off` or `/recap default`
|
|
190
191
|
(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
|
|
191
192
|
worker respawn, a continue and a fork keep it; a delegate child starts without it, on config.yml's. It goes first:
|
|
@@ -1526,14 +1527,14 @@ toolkit:
|
|
|
1526
1527
|
`undecided` for greetings or check-ins. Only a default-origin session with at least two described candidates is
|
|
1527
1528
|
asked; chosen or inherited toolkits, delegate children, review sessions and participant turns are skipped. After
|
|
1528
1529
|
`undecided`, later owner prompts may be asked again within both defer limits. The default is `off`. System 1's
|
|
1529
|
-
host, model and timeout come from `
|
|
1530
|
+
host, model and timeout come from `system_one.*`; the suggestion threshold and defer limits are its own
|
|
1530
1531
|
settings below. Suggestions are
|
|
1531
1532
|
kept in `toolkit_suggestions.jsonl`; `chi toolkit report` grades them against the session's tool calls. One ask
|
|
1532
1533
|
runs at a time (a turn that comes while one still runs goes unasked, with an info line), and a kit that doesn't
|
|
1533
1534
|
resolve (a missing base, a cycle) is left out of the candidates. A worker that exits waits about a second for a
|
|
1534
1535
|
running ask, so a one-shot `-p --non-interactive` run may end before its suggestion is logged.
|
|
1535
1536
|
- **On mode.** `toolkit.suggest.mode: on` asks the same question on the same eligible turns, but the turn waits for
|
|
1536
|
-
the answer before it declares any tool, for at most `
|
|
1537
|
+
the answer before it declares any tool, for at most `system_one.timeout_ms` from the ask's start (it
|
|
1537
1538
|
starts right after the turn does, so plugin and MCP start-up hide most of it). A described kit picked with
|
|
1538
1539
|
confidence at or above `toolkit.suggest.threshold` becomes the session's toolkit, saved with `toolkit_origin:
|
|
1539
1540
|
suggested`, and the turn's first request declares it; the notice reads
|
|
@@ -1541,7 +1542,7 @@ toolkit:
|
|
|
1541
1542
|
pick, `undecided`, no answer, a failing System 1, which warns once as `[samagotchi:toolkit]`) keeps the default
|
|
1542
1543
|
kit. No answer in time or none at all says so in a warn notice, `toolkit: full (no suggestion: System 1 took over
|
|
1543
1544
|
5000 ms) · /toolkit to change` (or `System 1 gave no answer`), and a timeout logs a `toolkit_suggest_timeout`
|
|
1544
|
-
warning: a slow System 1 host wants a larger `
|
|
1545
|
+
warning: a slow System 1 host wants a larger `system_one.timeout_ms`. A Stop during the wait (or before it, even with the answer in) ends the turn before its first request, and
|
|
1545
1546
|
nothing switches. After an `undecided`, a later prompt within the defer limits may switch the session; that notice
|
|
1546
1547
|
says `from this turn`, and that request re-reads the whole prompt (system prompt, tools and conversation, which
|
|
1547
1548
|
the defer limits keep small) without `/toolkit`'s cache confirmation. `/toolkit` or `ctx.use_toolkit` marks the
|
|
@@ -1591,7 +1592,7 @@ toolkit:
|
|
|
1591
1592
|
- **A new web chat: 🧰.** The start page's 🧰 (beside ⚙; a bottom sheet on a phone) picks the new session's toolkit.
|
|
1592
1593
|
**Auto**, checked by default, sends nothing: the session is on `toolkit.default` with `toolkit_origin: default`, so
|
|
1593
1594
|
with `toolkit.suggest.mode: on` its first message may switch it to a suggested kit. Auto is listed only when that
|
|
1594
|
-
can happen as far as chi web can tell (on mode, a `
|
|
1595
|
+
can happen as far as chi web can tell (on mode, a `system_one.host`, two described config kits); bundle
|
|
1595
1596
|
and memory kits can add candidates it doesn't see. A kit picked is sent as `POST /api/sessions`'s `toolkit` and
|
|
1596
1597
|
saved as `chosen`: no suggestion, the default kit included (its row says `default`); without Auto, the default's
|
|
1597
1598
|
row is the one checked and sends nothing. The list holds the built-ins and config kits a new session can start
|
|
@@ -1640,6 +1641,21 @@ toolkit:
|
|
|
1640
1641
|
changes what saved sessions on it send after an upgrade: the next request reads the prompt again once, as a
|
|
1641
1642
|
tool's schema change does. So can an edit to `toolkit.kits`: a session's worker reads the kits once when it starts, so a running session keeps its
|
|
1642
1643
|
tools and the edit reaches it at its next worker start (after an idle exit, `chi sessions restart`).
|
|
1644
|
+
- **Why a session is on its kit: `toolkit_origin`.** The session file records `default` (the configured kit),
|
|
1645
|
+
`chosen` (a user-selected kit, `/toolkit` and `ctx.use_toolkit` included), `inherited` (a continue or a fork) or
|
|
1646
|
+
`suggested` (`toolkit.suggest.mode: on` switched it). A file from before the field reads as `chosen`, so an old
|
|
1647
|
+
default session is never newly suggested a kit; only a `default` session is ever suggested one. A delegate child
|
|
1648
|
+
gets the kit its `delegate` call names (`toolkit:`, any kit the parent knows), else `toolkit.child`; a kit its worker
|
|
1649
|
+
can't resolve declares every tool.
|
|
1650
|
+
- **Loads saved in the conversation.** A `find_tools` result on a chat host saves the tools its hit loads as
|
|
1651
|
+
`loaded: ["list_sessions", "mcp_fs_read"]` on its `tool_response` entry (wire names: a built-in's, an MCP tool's
|
|
1652
|
+
`mcp_<server>_<tool>`), whatever the host: a later turn on `api: anthropic` (after a `/model` switch, a resume) sends
|
|
1653
|
+
the load as references, any other host ignores it. A plugin's `ctx.load_tools` leaves a system entry
|
|
1654
|
+
`{"role": "system", "kind": "tool_change", "content": "[tools loaded: delegate (bundle)]", "tool_change": {"add":
|
|
1655
|
+
["delegate"], "by": "bundle"}}` after the turn's user message, only on a host that takes tool changes
|
|
1656
|
+
(`api: anthropic`; [wire details](internals/anthropic.md#tool-changes-ctxload_tools)). It is never the system head,
|
|
1657
|
+
counts as conversation, is hidden in the web and TUI histories and goes to no other wire. Either way the names stay
|
|
1658
|
+
deferred for the session's life, across respawns, `--resume` and forks.
|
|
1643
1659
|
|
|
1644
1660
|
## LLM context: the forget layer
|
|
1645
1661
|
|
|
@@ -1762,7 +1778,7 @@ its own). The first target is a long session such as a day-long coordinator, whe
|
|
|
1762
1778
|
|
|
1763
1779
|
`context.candidates.mode: audit` asks System 1 whether a session's work is about each candidate a bundle proposed
|
|
1764
1780
|
(see [The judge](context.md#the-judge)); `on` also acts on the answer: attaches at `threshold`, a card from
|
|
1765
|
-
`offer_threshold` ([On mode](context.md#on-mode)). It uses `
|
|
1781
|
+
`offer_threshold` ([On mode](context.md#on-mode)). It uses `system_one.host` and `.model`, but its own mode: System
|
|
1766
1782
|
1's `mode` (approvals) can stay `off`. Jev, the hosted judgment model, is served through OpenRouter
|
|
1767
1783
|
(`/api/v1/systemone`), so OpenRouter can be the System 1 host:
|
|
1768
1784
|
|
|
@@ -1772,10 +1788,9 @@ hosts:
|
|
|
1772
1788
|
url: https://openrouter.ai/api/v1
|
|
1773
1789
|
api: openai
|
|
1774
1790
|
api_key_env: OPENROUTER_API_KEY
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
model: typesafe/jev-1.14
|
|
1791
|
+
system_one:
|
|
1792
|
+
host: openrouter
|
|
1793
|
+
model: typesafe/jev-1.14
|
|
1779
1794
|
context:
|
|
1780
1795
|
candidates:
|
|
1781
1796
|
mode: audit
|
|
@@ -1785,6 +1800,27 @@ A remote host is asked beside the turn. A System 1 host that is the model's own
|
|
|
1785
1800
|
turn's last request, and the turn-end warm-up follows them, re-warming the prefix they evicted (see
|
|
1786
1801
|
[prompt caching](internals/prompt-caching.md)).
|
|
1787
1802
|
|
|
1803
|
+
|
|
1804
|
+
## Bundles' judgments
|
|
1805
|
+
|
|
1806
|
+
A bundle can declare its own cheap System 1 questions ([Judgments](plugins.md#judgments-chijudgment-and-ctxjudge)). Each is off until
|
|
1807
|
+
you set its mode under its full name, `<bundle>.<name>`, in `system_one.judgments`:
|
|
1808
|
+
|
|
1809
|
+
```yaml
|
|
1810
|
+
system_one:
|
|
1811
|
+
host: splash
|
|
1812
|
+
judgments:
|
|
1813
|
+
check-in.progress:
|
|
1814
|
+
mode: audit # off (the default) | audit | on
|
|
1815
|
+
host: openrouter # optional, this judgment alone; else system_one.host
|
|
1816
|
+
model: typesafe/jev-1.14 # optional, this judgment alone; else system_one.model, or on a host of its own that host's first model
|
|
1817
|
+
```
|
|
1818
|
+
|
|
1819
|
+
A name no loaded bundle declares gets a notice at the session's first turn. Core's judgments (`approvals.*`, `context.*`, `guardrails.*`,
|
|
1820
|
+
`toolkit.*`) take no settings here and warn if named: `SystemOne::Record` and `Similar` don't look at the model, so a
|
|
1821
|
+
model swapped for approvals alone would inherit a clean record and could auto-allow.
|
|
1822
|
+
A bundle's judgments on a hosted host aren't under any cost cap: they stay out of the session's spend, as all of System 1's do.
|
|
1823
|
+
|
|
1788
1824
|
## Opening files from chi web
|
|
1789
1825
|
|
|
1790
1826
|
In `chi web`, a file tool's row (read, edit, write) and the lines of its diff are refs to that file. They look like
|
|
@@ -2190,12 +2226,13 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
|
|
|
2190
2226
|
| `guardrails.small_models` | `auto` | | Which models get the `models: small` rules: `auto` (32B or less by the size in the name, an MoE's active size), a list of globs, or `[]`. config.yml only. See [Guardrails](guardrails.md#rules-for-some-models). |
|
|
2191
2227
|
| `guardrails.parent_answers` | `soft` | | Which options a parent agent (`chi answer`) may pick on a session's question, by their effect: `hard` (a deny, a Stop, a plain answer), `standard` (+ Continue and other spending options), `soft` (+ "Allow once"); never a wider scope or an allow on chi's own config. An invalid value warns and is `hard`. A session's own `parent_answers` (`chi --parent-answers`, `/parent-answers`) may only tighten it. Replaces `guardrails.parent_approvals` and `turn.parent_continue`. config.yml only: no environment variable (a parent can still pick the config dir with `XDG_CONFIG_HOME`). A convention for an honest parent, not a security boundary. See [Guardrails](guardrails.md#answers-from-a-parent-agent). |
|
|
2192
2228
|
| `guardrails.system_one.mode` | `off` | | System 1, the cheap judgment that answers an ask before the user sees it: `off` (the default), `audit` (judge every ask, log it, never decide), `on` (also allowed to allow a call once, behind the gates in [System 1](guardrails.md#system-1-experimental)). An unknown value warns and is `off`. Experimental. config.yml only. See [Guardrails](guardrails.md#system-1-experimental). |
|
|
2193
|
-
| `guardrails.system_one.host` | none | | The `hosts:` entry serving `/v1/systemone` (a local Splash, or the hosted Jev API); unset: no judgment. config.yml only. |
|
|
2194
|
-
| `guardrails.system_one.model` | none | | The judgment model; unset uses the host's first model, and a host that names none has no judgment. config.yml only. |
|
|
2195
2229
|
| `guardrails.system_one.rules` | none | | The rule ids System 1 may answer for in `on` mode: `"git-push|git-rebase"` or a YAML list, parsed like `guardrails.small_models`. Nothing is auto-allowed while it is unset; the `chi-answer-piped`, `chi-answer-http` and `chi-broadcast` rules never are, however they are listed. config.yml only. |
|
|
2196
2230
|
| `guardrails.system_one.threshold` | `0.8` | | The probability an auto-allow needs. config.yml only. |
|
|
2197
2231
|
| `guardrails.system_one.min_record` | `5` | | How many clean records a rule needs before System 1 is trusted for it: labelled asks of that rule at the threshold the user allowed. config.yml only. |
|
|
2198
|
-
| `
|
|
2232
|
+
| `system_one.host` | none | | The `hosts:` entry serving `/v1/systemone` (a local Splash, or the hosted Jev API); unset: no judgment, by any use of System 1, so nothing auto-allows and every ask goes to the user. Moved from `guardrails.system_one.host`: while any of host, model and timeout_ms is still under `guardrails.system_one`, chi warns and nothing is judged. config.yml only. |
|
|
2233
|
+
| `system_one.model` | none | | The judgment model; unset uses the host's first model, and a host that names none has no judgment. Moved from `guardrails.system_one.model`. config.yml only. |
|
|
2234
|
+
| `system_one.timeout_ms` | `2000` | | Milliseconds one judgment may take; a slower one is no judgment and the ask goes to the user. Moved from `guardrails.system_one.timeout_ms`. config.yml only. |
|
|
2235
|
+
| `system_one.runs` | `on` | | Save each System 1 ask and what came of it, under the state dir, as long as sessions last (see [System 1's saved runs](system-one.md)); `off` stops saving. config.yml only. |
|
|
2199
2236
|
| `log.file` | state dir | yes | See "Debug Log File". |
|
|
2200
2237
|
| `log.disable` | `false` | yes | No file logging. |
|
|
2201
2238
|
| `log.level` | `info` | yes | `debug`, `info`, `warn`, `error`. |
|
|
@@ -2218,13 +2255,13 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
|
|
|
2218
2255
|
| `context.every_seconds` | `300` | | How often an attached context source's command runs when it has no `--every` (seconds, at least 30). See [Attached context](context.md). |
|
|
2219
2256
|
| `context.wake` | `true` | yes | A source whose update says `wake: true` may start a turn in a live, idle session (one per source per 10 minutes, a peer source up to `context.peer_turns` in a row, within `session.max_wakes`); so may a session's pushed source's first push. `false`: updates wait as notes for the next turn. See [Waking](context.md#waking). |
|
|
2220
2257
|
| `context.peer_turns` | `3` | yes | Wake turns in a row a peer agent's answers may start (a peer bundle's outbox source, such as `codex-outbox`), with no message typed in the web or a TUI in between (`chi send` doesn't count); within `session.max_wakes`. Then answers wait as notes, and the UIs hear it once. An answer whose summary ends its verdicts with `VERDICT OK` wakes once to close the round, counted toward `session.max_wakes` only. `0`: a peer source wakes like any other. See [Waking](context.md#waking). |
|
|
2221
|
-
| `context.candidates.mode` | `off` | | The candidate judge: `off` (the default: no conversation text goes to a System 1 host), `audit` (asks System 1 whether the session's work is about each proposed candidate and logs it in `context_candidates.jsonl`; attaches nothing), `on` (the same asks, acted on: attached at `threshold`, a card from `offer_threshold`; see [On mode](context.md#on-mode)). Its host and model are `
|
|
2258
|
+
| `context.candidates.mode` | `off` | | The candidate judge: `off` (the default: no conversation text goes to a System 1 host), `audit` (asks System 1 whether the session's work is about each proposed candidate and logs it in `context_candidates.jsonl`; attaches nothing), `on` (the same asks, acted on: attached at `threshold`, a card from `offer_threshold`; see [On mode](context.md#on-mode)). Its host and model are `system_one.host` / `.model`. config.yml only. See [The judge](context.md#the-judge). |
|
|
2222
2259
|
| `context.candidates.threshold` | `0.8` | | The probability at which the judge attaches a candidate in on mode (a judged line's `would: attach`). config.yml only. |
|
|
2223
2260
|
| `context.candidates.offer_threshold` | `0.6` | | The probability from which on mode shows the candidate's card, once (`would: offer`, below `threshold`). config.yml only. |
|
|
2224
2261
|
| `context.candidates.reask_after_turns` | `3` | | Owner prompts after a judged ask before the judge's one re-ask of that candidate. config.yml only. |
|
|
2225
2262
|
| `toolkit.default` | `full` | | The toolkit a new session is stored on: the tools its prompts declare (`full`, `lean`, `child`, one of `toolkit.kits` or a bundle's). `chi --toolkit` picks one per session. See "Toolkits". |
|
|
2226
2263
|
| `toolkit.child` | `full` | | A delegate child's toolkit, unless its `delegate` call names one (`toolkit:`). See "Toolkits". |
|
|
2227
|
-
| `toolkit.suggest.mode` | `off` | | `audit` asks System 1 for a toolkit suggestion without changing the session; `on` waits for it (up to `
|
|
2264
|
+
| `toolkit.suggest.mode` | `off` | | `audit` asks System 1 for a toolkit suggestion without changing the session; `on` waits for it (up to `system_one.timeout_ms`) on an eligible turn and starts the session on a confidently suggested kit (Toolkits, On mode). The System 1 host, model and timeout come from `system_one.*`. config.yml only. |
|
|
2228
2265
|
| `toolkit.suggest.threshold` | `0.5` | | Minimum confidence for a non-default toolkit suggestion. config.yml only. |
|
|
2229
2266
|
| `toolkit.suggest.defer_turns` | `3` | | Maximum owner-prompt number for another ask after `undecided` (the owner's own prompts: not steers, notes, reminders or `!` command output). config.yml only. |
|
|
2230
2267
|
| `toolkit.suggest.defer_tokens` | `5000` | | Maximum estimated conversation size for another ask after `undecided` (characters / 4). config.yml only. |
|
|
@@ -2235,7 +2272,9 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
|
|
|
2235
2272
|
| `hosts.<name>.models.<id>.served` | none | | The models the host may answer that id with (exact ids, any case), or `any`: no served-model warning for those. See "Models a host serves but doesn't list". |
|
|
2236
2273
|
| `hosts.<name>.models.<id>.price` | none | | The model's price on that host, USD per 1M tokens: `input`, `output`, optional `cache_read` and `cache_write`. Estimates the cost a provider doesn't report. See "Prices". |
|
|
2237
2274
|
| `hosts.<name>.remote` | by address | | `true`/`false`: treat the host as a remote provider or a local server. See "Remote or local". |
|
|
2238
|
-
| `
|
|
2275
|
+
| `memory.entry_warn_tokens` | `5000` | yes | The tokens one memory may hold (every read of it pays them; about 20K characters at the default `context.chars_per_token`) before a `memory_write`, `write` or `edit` that takes it over gets a note, once per crossing, asking the model to tighten it; `0`: no note. A memory that did not exist counts as 0 tokens. Not for a description-only write, a remove, `index`, a model overlay or a `model_notes_*` memory. Independent of `memory.index_warn_tokens`. See [Memory](memory.md). |
|
|
2276
|
+
| `memory.read_max_chars` | `0` | yes | The most characters one `memory_read` result holds. `0`: no fixed cap, a memory is read whole (`max_tool_output_chars` does not apply to `memory_read`, in any form: one name, a comma list, a blank name). A positive value cuts a longer result and ends it with `[cut: N of M chars; the rest is in the memory's .md file in <dir>: read it with start_line]`. `memory:` is its own section. See [Memory](memory.md). |
|
|
2277
|
+
| `max_tool_output_chars` | `10000` | yes | Characters of each tool output kept in the conversation (both loops). A longer one is cut: an `execute` result keeps its first and last lines with a gap line between them and is saved whole in the session's folder (`sessions/<id>/tool_output/`; the gap line names the file, and the model reads it there instead of running the command again); any other tool's keeps its head and ends with `[cut: N of M chars; read it in parts]`. `memory_read` is the exception: `memory.read_max_chars` caps it. See [Tool output guardrails](internals/tool-output-limits.md#the-runners-cut-max_tool_output_chars). A top-level key (see below). |
|
|
2239
2278
|
| `cache.warmup` | `auto` | | `auto`: after a turn, send the next turn's prompt (up to the next message) to a local llama.cpp host on the native loop, so the next turn prefills only its message; never a remote or `api: openai` host. `off`: never. See [prompt caching](internals/prompt-caching.md#the-turn-end-warm-up). |
|
|
2240
2279
|
| `cache.ttl` | `5m` | | How long Anthropic keeps the prompt-cache breakpoints of a Claude model on a remote host: `5m` (Anthropic's default) or `1h`. A 1h cache write costs 2× the input price (5m: 1.25×), a read the same 0.1×. See "Remote or local". |
|
|
2241
2280
|
| `cache.key` | `off` | | `session`: send the session id as `prompt_cache_key` to OpenAI's API and OpenRouter, which route and cache by it; no other host gets it. `off`: none. |
|
data/docs/context.md
CHANGED
|
@@ -89,8 +89,8 @@ web) without touching the other sessions.
|
|
|
89
89
|
## The commands
|
|
90
90
|
|
|
91
91
|
```
|
|
92
|
-
chi context add NAME (--cmd CMD | --push) [--every SECONDS] [--why TEXT] [--hint TEXT] TARGET
|
|
93
|
-
chi context add URL [--why TEXT] [--hint TEXT] TARGET a URL an installed bundle's provider knows
|
|
92
|
+
chi context add NAME (--cmd CMD | --push) [--every SECONDS] [--why TEXT] [--hint TEXT] [--[no-]movable] TARGET
|
|
93
|
+
chi context add URL [--why TEXT] [--hint TEXT] [--[no-]movable] TARGET a URL an installed bundle's provider knows
|
|
94
94
|
chi context push NAME [-m TEXT] [TARGET] new text for a --push source (stdin without -m)
|
|
95
95
|
chi context ls [TARGET] [--format json]
|
|
96
96
|
chi context show NAME [--json] [TARGET]
|
|
@@ -313,6 +313,68 @@ lower.) Every other source keeps the line above. `context.peer_turns: 0` turns
|
|
|
313
313
|
the loop off: a peer source then wakes like any other. `context_read` names a
|
|
314
314
|
peer source in its header (`Peer: your peer agent Codex's answers …`).
|
|
315
315
|
|
|
316
|
+
## Movable context
|
|
317
|
+
|
|
318
|
+
A session's own sources go with it to the next session of its work: the next
|
|
319
|
+
link of a [chain](sessions.md#session-chains) (`chi send --new --continues`,
|
|
320
|
+
the web's Continue →) and a bundle's fork
|
|
321
|
+
([`ctx.sessions.fork`](plugins.md#other-sessions-ctxsessions)). A topic switch goes
|
|
322
|
+
to a fresh session with its own context, never through a continue.
|
|
323
|
+
|
|
324
|
+
Each source says whether it moves:
|
|
325
|
+
|
|
326
|
+
- **A command source moves** (`--cmd`, a URL's source): chi runs its command
|
|
327
|
+
itself, in the new session too.
|
|
328
|
+
- **A pushed source stays** (`--push`): its writer (`pbpaste | chi context
|
|
329
|
+
push notes <id>`, a peer agent) holds the old session's id.
|
|
330
|
+
- `chi context add … --movable` / `--no-movable` (either kind) says it
|
|
331
|
+
outright: `--movable` on a pushed source means its writer finds the new id
|
|
332
|
+
itself. A bundle passes `movable:` to `ctx.context.attach`.
|
|
333
|
+
|
|
334
|
+
What moves is copied before the new session's worker starts: the source, its
|
|
335
|
+
last text (so its first note quotes it without waiting for a fetch, and its
|
|
336
|
+
interval holds), and the session's choices: every removed (declined) and
|
|
337
|
+
muted marker. Candidates don't move. The new link gets the usual *Attached*
|
|
338
|
+
note for each source; a fork starts where its seed's notes left off, so a
|
|
339
|
+
source that changed since gets an *Updated* note. A second chi note, after
|
|
340
|
+
the chain's, names what moved and what stayed:
|
|
341
|
+
|
|
342
|
+
> Context moved with this session: pr-412, ci. Left with 3f2a9c1e: notes (pushed).
|
|
343
|
+
|
|
344
|
+
`chi send --new --continues` and the web's Continue toast say the same in a
|
|
345
|
+
few words, and `chi context ls` marks a session's source that stays
|
|
346
|
+
(`· stays`; `--format json` has the derived `movable`). Project sources are
|
|
347
|
+
shared already, and a review fork (`chi review --from`) and a delegate take none.
|
|
348
|
+
|
|
349
|
+
A push into a continued session is refused, nothing written, with the next
|
|
350
|
+
link's id (`chi context push` exits 1; chi web answers 410
|
|
351
|
+
`context_continued`):
|
|
352
|
+
|
|
353
|
+
```
|
|
354
|
+
chi context push: 3f2a9c1e was continued as 7b1d…: notes stays with the session it was attached to; attach it to 7b1d… (or pair again with 7b1d…) and push there
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
An archived session that nobody continued takes pushes as before.
|
|
358
|
+
|
|
359
|
+
### Peers and a continue
|
|
360
|
+
|
|
361
|
+
A peer's pairing ([claude-peer, codex-peer](plugins.md#the-claude-peer-bundle):
|
|
362
|
+
`<peer>-inbox` and `<peer>-outbox`, both pushed) stays with the old session,
|
|
363
|
+
and the peer pairs again with the new one. The continue keeps what stayed in
|
|
364
|
+
the new session (`left.json` beside its sources: each name, its last pushed
|
|
365
|
+
hint, the old id; `ctx.context.left_behind`), and the new session's worker
|
|
366
|
+
posts the peer one `moved` message to the inbox left behind, once:
|
|
367
|
+
`chi <old> · moved · continued as <new id>`. The peer's chi-peer skill pairs
|
|
368
|
+
again with the new id, the same way it paired before. Without an inbox (an
|
|
369
|
+
outbox-only pairing through a remote), or when the message didn't arrive, the
|
|
370
|
+
peer's next push to the old id is refused with the new id, and the skill pairs
|
|
371
|
+
again and pushes there. The second note says which, not whether it worked:
|
|
372
|
+
|
|
373
|
+
> Left with 3f2a9c1e: claude-inbox, claude-outbox (Claude's pairing: chi asks Claude to pair again with this session).
|
|
374
|
+
|
|
375
|
+
or, for an outbox alone, `codex-outbox (Codex pairs again when it next
|
|
376
|
+
answers)`. A fork takes no pairing and posts nothing.
|
|
377
|
+
|
|
316
378
|
## Candidates
|
|
317
379
|
|
|
318
380
|
A bundle can **propose** a source instead of attaching it
|
|
@@ -359,8 +421,8 @@ review session refuses them); an unknown name, or one attached or declined
|
|
|
359
421
|
already, is an error that says so. They are what the judge's card's buttons
|
|
360
422
|
run (see [On mode](#on-mode)).
|
|
361
423
|
|
|
362
|
-
Forks and continues
|
|
363
|
-
|
|
424
|
+
Forks and continues take no candidates: the bundle proposes again in the
|
|
425
|
+
new session (see [Movable context](#movable-context) for the sources).
|
|
364
426
|
|
|
365
427
|
### The judge
|
|
366
428
|
|
|
@@ -370,8 +432,8 @@ about each proposed candidate — "Is this session's work about the
|
|
|
370
432
|
candidate?" — and logs the answer, so you can see how it would do before
|
|
371
433
|
anything depends on it; `audit` attaches nothing. With `on` the answer acts
|
|
372
434
|
([On mode](#on-mode)). The default is `off`: no conversation text goes to a System 1 host unless you
|
|
373
|
-
turn this on, whatever `guardrails.system_one` says. The host and model are
|
|
374
|
-
`
|
|
435
|
+
turn this on, whatever `guardrails.system_one.mode` says. The host and model are
|
|
436
|
+
`system_one.host` / `.model` (Jev through OpenRouter: see
|
|
375
437
|
[The candidate judge](configuration.md#the-candidate-judge)).
|
|
376
438
|
|
|
377
439
|
```yaml
|
data/docs/guardrails.md
CHANGED
|
@@ -283,8 +283,9 @@ Built in, for `write`, `edit` and `memory_write` (symlinks resolved; see
|
|
|
283
283
|
[How paths resolve](#how-paths-resolve)):
|
|
284
284
|
|
|
285
285
|
- deny: the approval store's dir, installed bundles (`memories/.bundles/`),
|
|
286
|
-
|
|
287
|
-
source's command runs later, outside the gate; `chi context` changes them)
|
|
286
|
+
[attached context](context.md) sources (`<state dir>/context/`: a
|
|
287
|
+
source's command runs later, outside the gate; `chi context` changes them)
|
|
288
|
+
and [System 1's saved runs](system-one.md) (`<state dir>/system_one/`);
|
|
288
289
|
- ask (once or for the session): `config.yml`, `home.json` (the [chi home](configuration.md#the-chi-home): the Ruby every chi launch boots), the chi shim (`$XDG_STATE_HOME/samagotchi/bin/chi`, the `chi` every tool runs: `chi-shim`) and the plain hooks dir.
|
|
289
290
|
|
|
290
291
|
`execute` can still reach them; the guardrails bundle asks about shell
|
|
@@ -745,7 +746,7 @@ repo is in count as outside.
|
|
|
745
746
|
|
|
746
747
|
`touches: chi_dirs` (shell tools) matches a command that names a path in chi's
|
|
747
748
|
own folders: the config folder (`config.yml`, `memories/`), the hooks folder,
|
|
748
|
-
the approval store, installed bundles (`memories/.bundles`), the session repo's
|
|
749
|
+
the approval store, attached context, System 1's saved runs, installed bundles (`memories/.bundles`), the session repo's
|
|
749
750
|
git hooks (`core.hooksPath` honoured) or any `.git/hooks` (a submodule's `.git/modules/<name>/hooks` too)
|
|
750
751
|
outside a tmp folder.
|
|
751
752
|
Each word is read as a path against the folder the command is in by then
|
|
@@ -1045,25 +1046,32 @@ reaching them — see [Answering for the user](#answering-for-the-user-on-mode).
|
|
|
1045
1046
|
`Samagotchi::SystemOne` (lib/samagotchi/system_one/) speaks the TypeSafe
|
|
1046
1047
|
"System One" judgment API: `POST <host>/v1/systemone` with
|
|
1047
1048
|
`{model, state, questions}` and answers of type `noul` (a probability),
|
|
1048
|
-
`choice` or `score`.
|
|
1049
|
+
`choice` or `score`. A `score` answer's number is not a level: it is the
|
|
1050
|
+
probability-weighted level index (Σ index × its probability), so a two-level
|
|
1051
|
+
question's score is exactly the probability of its second level. The response
|
|
1052
|
+
also sends `legend` (level index => name) and `probabilities` per index; chi
|
|
1053
|
+
keeps the score as sent and reads the level with the most probability as
|
|
1054
|
+
`answer.level`. A score that isn't a number, or is past the last level, is no
|
|
1055
|
+
answer. The host is a `hosts:` entry with `api: openai` — a
|
|
1049
1056
|
local Splash server, or the hosted Jev API — and its key is that host's own
|
|
1050
1057
|
`api_key_env:`.
|
|
1051
1058
|
|
|
1052
1059
|
```yaml
|
|
1060
|
+
system_one: # what every use of System 1 shares
|
|
1061
|
+
host: splash # the hosts: entry serving /v1/systemone
|
|
1062
|
+
model: Qwen3.8-Flash-Next-GSQ-RCO-GGUF:Q2_0 # else the host's first model
|
|
1063
|
+
timeout_ms: 2000 # a slower judgment is no judgment
|
|
1053
1064
|
guardrails:
|
|
1054
|
-
system_one:
|
|
1065
|
+
system_one: # approvals' own
|
|
1055
1066
|
mode: off # off (default) | audit (judge, log, never decide) | on
|
|
1056
|
-
host: splash # the hosts: entry serving /v1/systemone
|
|
1057
|
-
model: Qwen3.8-Flash-Next-GSQ-RCO-GGUF:Q2_0 # else the host's first model
|
|
1058
1067
|
rules: [git-push, git-rebase] # the rule ids it may answer for
|
|
1059
1068
|
threshold: 0.8 # the probability an auto-allow needs
|
|
1060
1069
|
min_record: 5 # clean records a rule needs before it is trusted
|
|
1061
|
-
timeout_ms: 2000 # a slower judgment is no judgment
|
|
1062
1070
|
```
|
|
1063
1071
|
|
|
1064
|
-
`SystemOne::Client.from_config(host_registry:
|
|
1072
|
+
`SystemOne::Client.from_config(host_registry:, mode:)` reads these on every
|
|
1065
1073
|
call, so a config.yml change takes effect on the next ask. `mode: off` (the
|
|
1066
|
-
default) or no `host` means there is no client at all. Anything else is no
|
|
1074
|
+
default) or no `system_one.host` means there is no client at all. Anything else is no
|
|
1067
1075
|
judgment and one `Log.warn(:guardrails, "system_one_failed", …)`: an
|
|
1068
1076
|
unreachable host, a host that isn't `api: openai`, a status other than 200,
|
|
1069
1077
|
a body that isn't JSON, a question with no answer, a request longer than
|
|
@@ -1118,7 +1126,8 @@ offers no examples. A participant's turn (review or pair) is never judged:
|
|
|
1118
1126
|
there is no user there to ask, and its asks go to the owner. The judgment runs before the card appears, so an ask is
|
|
1119
1127
|
slower by up to `timeout_ms` (2000 ms by default).
|
|
1120
1128
|
|
|
1121
|
-
Each
|
|
1129
|
+
Each ask is also saved whole, with what the user answered, as a [run](system-one.md); the line's `id` is the
|
|
1130
|
+
run's. Each judgment is a line in `judgments.jsonl`, beside `approvals.json` in
|
|
1122
1131
|
`$XDG_STATE_HOME/samagotchi/guardrails/` (`SystemOne::Log`): 0600, appended
|
|
1123
1132
|
under an exclusive `flock` (several workers write it), and rewritten —
|
|
1124
1133
|
keeping the newest half of its lines — once it passes 5 MB. A line is never
|
data/docs/hooks.md
CHANGED
|
@@ -62,7 +62,7 @@ The plugin class must respond to `#call(event)` — duck-typed, no base class re
|
|
|
62
62
|
| `:generation_progress` | While the response streams, in batches (see [Watching the stream](#watching-the-stream)) | `{ type: :generation_progress, iteration: N, thinking: "..." (new since the last fire), text: "..." (new visible text), thinking_chars: N, text_chars: N (this generation so far), elapsed_ms: N, restarted: true (only on the first fire after a dropped stream was asked again) }` |
|
|
63
63
|
| `:before_tool_call` | Before tool dispatch (and before `tool_call_started`) | `{ type: :before_tool_call, iteration: N, call: {...}, params: "...", guardrail: Verdict, context: {...}, targets: {...}, blocked: false, block_reason: nil }` |
|
|
64
64
|
| `:question_asked` | A question of the model's own (`ask_user_question`) or an approval of its tool call is about to open, on the turn's thread (not a hook's or plugin's `ask_user`, a relayed delegate approval, the step-limit question, a `--non-interactive` run or a stored approval: none of those fire it) | `{ type: :question_asked, kind: "question" \| "approval", text: "..." (the reply that asks, as the asking slot shows it: no thoughts, no tool-call markup), question: {...} (the question's fields, frozen: question/options/header…, an approval's approval: facts too; no id yet), refs: (see [Refs on a question](#refs-on-a-question-display-only)) }` |
|
|
65
|
-
| `:after_tool_call` | After tool execution | `{ type: :after_tool_call, iteration: N, tool: "read", output: "..." (the text the model gets: capped at max_tool_output_chars; a cut one has a [cut: …] line, at its end, or for an execute result between its head and its tail, the whole output saved in the session's tool_output/ folder), status: "ok" \| "error" \| "blocked" \| "stopped", call: {...} (the call that ran, as the gate left it), elapsed_ms: N (the tool's own run, without an approval wait; nil for a denied call), result: "..." (the tool's own text: no "[read]" lead, no "ran as:" line, never cut) }`; `stopped` is a `task_wait` the turn's Stop ended (the task runs on) or whose task was stopped. Match on `result:`, not `output:`: it is what the tool said (`Error: command timed out after 120s` for an execute killed at its limit in kill mode, `Samagotchi::Tools::Execute::TIMEOUT_PREFIX`; `Still running at the 120s limit: …` for one handed over as a task, `Samagotchi::Tools::Execute::HANDED_OVER`) |
|
|
65
|
+
| `:after_tool_call` | After tool execution | `{ type: :after_tool_call, iteration: N, tool: "read", output: "..." (the text the model gets: capped at max_tool_output_chars (a memory_read's cap is memory.read_max_chars, 0: none); a cut one has a [cut: …] line, at its end, or for an execute result between its head and its tail, the whole output saved in the session's tool_output/ folder), status: "ok" \| "error" \| "blocked" \| "stopped", call: {...} (the call that ran, as the gate left it), elapsed_ms: N (the tool's own run, without an approval wait; nil for a denied call), result: "..." (the tool's own text: no "[read]" lead, no "ran as:" line, never cut) }`; `stopped` is a `task_wait` the turn's Stop ended (the task runs on) or whose task was stopped. Match on `result:`, not `output:`: it is what the tool said (`Error: command timed out after 120s` for an execute killed at its limit in kill mode, `Samagotchi::Tools::Execute::TIMEOUT_PREFIX`; `Still running at the 120s limit: …` for one handed over as a task, `Samagotchi::Tools::Execute::HANDED_OVER`) |
|
|
66
66
|
| `:session_end` | After each turn `:after_turn` fires for, after it (turn-level lifecycle) | `{ type: :session_end, session_id: "..." }` |
|
|
67
67
|
|
|
68
68
|
Every event also carries the hook runtime (next section): `hook:` (the label
|
|
@@ -76,12 +76,14 @@ cheap).
|
|
|
76
76
|
|
|
77
77
|
## Participants' turns
|
|
78
78
|
|
|
79
|
-
A [participant's](internals/participants.md#turns) review turn fires the same hooks, and
|
|
80
|
-
`author: "p:<id>"` (the owner's carry no `author:` key, as before); a plugin reads the
|
|
81
|
-
(nil for the owner's turns and between turns).
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
79
|
+
A [participant's](internals/participants.md#turns) turn — a review turn or a pair turn — fires the same hooks, and
|
|
80
|
+
every event of it carries `author: "p:<id>"` (the owner's carry no `author:` key, as before); a plugin reads the
|
|
81
|
+
same as `ctx.turn_author` (nil for the owner's turns and between turns). The mode does not change that: `author:`
|
|
82
|
+
comes from who wrote the turn, not from `ParticipantMode`. **A hook runs with the owner's power whatever the
|
|
83
|
+
author**: it may read, write and run what the owner's chi may, and `notify`, `ask_user` and `steer` reach the
|
|
84
|
+
owner. A bundle that acts on a turn's text (a prompt rewrite, a fetch of what it names) should check `author:`
|
|
85
|
+
first: the text may be a participant's. The participant's boundary (the review or pair one) checks a tool call
|
|
86
|
+
after the hooks, on the call they leave.
|
|
85
87
|
|
|
86
88
|
## What a hook can do: the runtime
|
|
87
89
|
|