samagotchi 0.147.0 → 0.149.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.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +32 -1
  3. data/README.md +2 -2
  4. data/docs/architecture.md +1 -1
  5. data/docs/bundles.md +64 -17
  6. data/docs/cli.md +18 -0
  7. data/docs/configuration.md +7 -3
  8. data/docs/guardrails.md +27 -5
  9. data/docs/hooks.md +2 -1
  10. data/docs/internals/objectives.md +264 -0
  11. data/docs/internals/web-frontend.md +4 -1
  12. data/docs/plugins.md +4 -2
  13. data/docs/releasing.md +1 -1
  14. data/docs/sessions.md +44 -2
  15. data/docs/system-one.md +18 -2
  16. data/lib/samagotchi/answer_tail.rb +1 -1
  17. data/lib/samagotchi/bundles/coordinator/guardrails/merge.yml +21 -0
  18. data/lib/samagotchi/bundles/coordinator/manifest.yml +9 -5
  19. data/lib/samagotchi/bundles/coordinator/plugin.rb +5 -3
  20. data/lib/samagotchi/bundles/coordinator/skill_coordinator.md +44 -13
  21. data/lib/samagotchi/bundles/decision-card/manifest.yml +13 -0
  22. data/lib/samagotchi/bundles/decision-card/plugin.rb +280 -0
  23. data/lib/samagotchi/bundles/dev/manifest.yml +3 -2
  24. data/lib/samagotchi/bundles/system/manifest.yml +1 -1
  25. data/lib/samagotchi/config.rb +12 -1
  26. data/lib/samagotchi/context_note.rb +17 -5
  27. data/lib/samagotchi/engine.rb +113 -6
  28. data/lib/samagotchi/engine_peers.rb +11 -0
  29. data/lib/samagotchi/guardrail_wiring.rb +11 -6
  30. data/lib/samagotchi/guardrails/approvals.rb +3 -1
  31. data/lib/samagotchi/guardrails/pair_boundary.rb +3 -2
  32. data/lib/samagotchi/guardrails/rules.rb +6 -1
  33. data/lib/samagotchi/guardrails/verdict.rb +21 -1
  34. data/lib/samagotchi/guardrails_command.rb +47 -3
  35. data/lib/samagotchi/llm/chat_loop.rb +1 -1
  36. data/lib/samagotchi/note_command.rb +1 -1
  37. data/lib/samagotchi/note_delivery.rb +6 -2
  38. data/lib/samagotchi/objectives.rb +395 -0
  39. data/lib/samagotchi/objectives_audit.rb +115 -0
  40. data/lib/samagotchi/objectives_check.rb +335 -0
  41. data/lib/samagotchi/objectives_command.rb +192 -0
  42. data/lib/samagotchi/plugin/api.rb +2 -1
  43. data/lib/samagotchi/plugin/context.rb +21 -1
  44. data/lib/samagotchi/run_options.rb +1 -0
  45. data/lib/samagotchi/send_command.rb +2 -1
  46. data/lib/samagotchi/session_chain.rb +91 -8
  47. data/lib/samagotchi/session_commands.rb +16 -1
  48. data/lib/samagotchi/session_continue.rb +6 -2
  49. data/lib/samagotchi/session_inbox.rb +14 -6
  50. data/lib/samagotchi/session_manager.rb +1 -1
  51. data/lib/samagotchi/steer.rb +24 -4
  52. data/lib/samagotchi/system_one/record.rb +17 -3
  53. data/lib/samagotchi/testing/boundary.rb +1 -0
  54. data/lib/samagotchi/testing/host.rb +1 -0
  55. data/lib/samagotchi/tool_activity.rb +17 -0
  56. data/lib/samagotchi/tool_declarations.rb +34 -0
  57. data/lib/samagotchi/tools/builtin_calls.rb +2 -1
  58. data/lib/samagotchi/tools/builtins.rb +4 -1
  59. data/lib/samagotchi/tools/objectives_tool.rb +75 -0
  60. data/lib/samagotchi/tools/peers.rb +3 -0
  61. data/lib/samagotchi/version.rb +1 -1
  62. data/lib/samagotchi/web/message_display.rb +9 -0
  63. data/lib/samagotchi/web/participant_view.rb +1 -1
  64. data/lib/samagotchi/web/public/app.js +10 -0
  65. data/lib/samagotchi/web/public/cards_place.js +20 -4
  66. data/lib/samagotchi/web/public/commands.js +4 -1
  67. data/lib/samagotchi/web/public/index.html +2 -0
  68. data/lib/samagotchi/web/public/objectives.css +80 -0
  69. data/lib/samagotchi/web/public/objectives_entry.js +22 -0
  70. data/lib/samagotchi/web/public/objectives_model.js +100 -0
  71. data/lib/samagotchi/web/public/objectives_moves.js +145 -0
  72. data/lib/samagotchi/web/public/objectives_strip.js +242 -0
  73. data/lib/samagotchi/web/public/objectives_wiring.js +62 -0
  74. data/lib/samagotchi/web/public/stream_handlers.js +6 -1
  75. data/lib/samagotchi/web/public/turn_html.js +4 -1
  76. data/lib/samagotchi/web/session_show.rb +12 -1
  77. data/lib/samagotchi/worker.rb +3 -0
  78. data/lib/samagotchi/worker_inbound.rb +16 -8
  79. metadata +16 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 29546fb5487086302ed4d68c168fa9e7f4f576bee252e59cfdaf08a4e74081b4
4
- data.tar.gz: 3c05877e89bccf719dbdf8f4287c8343222f897484c2f22a4c3245bdc92a2af1
3
+ metadata.gz: c93a3995e0d6192e52b21e25127bedc26e2442b9b45002ed6d3b758fc214c837
4
+ data.tar.gz: 303ef3878f850b90a9f506fb8a09c710a25e18aca6836e8aa59ee27498258146
5
5
  SHA512:
6
- metadata.gz: 953166016279e2b81ebe64c9642df2a245cc3d7fe4520850bf4023557b5d1b7fd620589fd7c49df66d458e59eeae32dffb3020e6286ea8be85edf2710c2bd7d4
7
- data.tar.gz: 6c01b8ac5e044770f34e4fd5057915116e8b56b81a6f505fac3259449911a02b091262aad2554417ebf0493068ace77b1636d3fb8187769225268d12540c5354
6
+ metadata.gz: da19432429657d32a29891f5ac57d2a987d9f19fb7b8c027f7b8038660e51b76bc4a066331e2ad3a49fe62747b7f8dd85b5cd8f9b59d47282d1ca9b4ab7aaa74
7
+ data.tar.gz: 33d8c08e66bf4eac732fabb7425a1ca00f3fabb8570418d5887c46db9ce0980466969437590908238861d67a267d139c9916e668bc546babf869cae3cc01f162
data/CHANGELOG.md CHANGED
@@ -7,6 +7,35 @@ and commands may change between minor versions. How releases are made:
7
7
  [docs/releasing.md](docs/releasing.md).
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.149.0] - 2026-11-04
11
+
12
+ ### Added
13
+
14
+ - `/objectives` gives a session a short list of what it is to achieve (`set A | B`, `add`, `done`, `drop`), kept in the conversation as a note; a continue carries it, and a session with no list gets no note and no prompt line. A first version to try. [docs/sessions.md](docs/sessions.md#objectives)
15
+ - The `objectives` tool lets the model record a move on an objective (a line, optional evidence): information only, it never changes the list, and `/objectives` shows each objective's latest. Declared on `full` and the coordinator toolkit, so a `full` request carries its schema with or without a list; other kits name it in the not-loaded line. The coordinator bundle's skill says a move or a `done` is never approval to merge. [docs/sessions.md](docs/sessions.md#objectives)
16
+ - The `decision-card` bundle (in the `dev` profile): in a session with objectives, a long answer ending in a question gets a card with a short summary and the question, which you answer in the composer. One side request per such answer; a session with no list makes none, and nothing enters the conversation. `chi bundle uninstall decision-card` takes it away. Plugins get `ctx.objectives`. [docs/bundles.md](docs/bundles.md#the-decision-card-bundle)
17
+ - The web shows a session's objectives in the conversation: a strip above it with the current list, each list note drawn as pills or a line of what you changed, and the model's moves as chips above an answer. A tap edits the list with no message or turn (`/objectives edit ID TEXT` is new, and the terminals keep the command), Not yet tells the agent in a message, and on a phone it is one line and a bottom sheet. The `objectives` tool gains `propose`: the model suggests a change and you apply it from a card; it never changes the list by itself. `decision-card`'s `/short` gives a long answer's short version on demand. A session with no list shows nothing new. The tool's schema changed, so the prompt cache misses once. [docs/sessions.md](docs/sessions.md#objectives)
18
+ - Check this (`/objectives check O2`, or the button in the web's list): on request, one side request reads an objective's latest move against what the session's tools returned and people said, and reports a verdict with reasons in a card. Information only: no status changes and nothing enters the conversation. `objectives.check_model` picks a cheaper model, whose host then gets up to 24,000 characters of tool output, known secret shapes redacted.
19
+ - `objectives.system_one: audit` (off by default) saves a System 1 yes/no per active objective on incoming messages and finished answers, for `chi judgments`; nothing acts on it. Its text goes to the System 1 host.
20
+
21
+ Update with `chi update` (coordinator 0.8.0, decision-card 0.2.0, dev profile 0.7.5). `chi update` installs decision-card for anyone on the dev profile; it does nothing in a session with no objectives.
22
+
23
+ ## [0.148.0] - 2026-11-04
24
+
25
+ ### Added
26
+
27
+ - `chi guardrails rule <id>` prints exactly `asks` when that rule votes `ask` on this machine now, and says why not otherwise. [docs/cli.md](docs/cli.md)
28
+ - `guardrails.system_one.repo_rules` (default `git-merge`): a rule's System 1 record per repository, so a clean record at home doesn't let System 1 answer it in an unknown project. [docs/guardrails.md](docs/guardrails.md#answering-for-the-user-on-mode)
29
+
30
+ ### Changed
31
+
32
+ - A continue's carry note now holds the previous session's last answer after its recap (its last 4000 characters when longer), so a wrap-up's exact shas, paths and next steps reach the next link without a paste; `session.carry_last_answer: false` keeps it recap-only. A note can no longer exceed its 16 KiB and fail the continue: a recap of many non-ASCII characters used to.
33
+ - The coordinator's merges are now guardrail approvals (bundle coordinator 0.7.0). A verified branch that can fast-forward merges with no question from the model: you get the new `git-merge` rule's card, and **Allow rule git-merge in this repo** lets later merges there pass, so trusted projects merge on green and an unknown one still asks. Anything less certain still gets the question, and with no grant a yes is followed by the card. The rule votes in every session on a machine with the coordinator bundle, in any repo, children and background builds too (a child's `git merge main` raises a card), and `--non-interactive` merges are now denied. `chi update` brings it. [docs/bundles.md](docs/bundles.md#the-coordinator-bundle)
34
+
35
+ ### Fixed
36
+
37
+ - A stored approval no longer covers another rule's ask on the same call. With rule `git-push` allowed in the repo, `git push && git reset --hard` ran with no ask, because an ask keeps the first rule's id. When a second rule asks about a call, no rule grant answers it, none is offered and System 1 doesn't auto-allow it; a session or repo approval for exactly that command still answers. [docs/guardrails.md](docs/guardrails.md#approvals)
38
+
10
39
  ## [0.147.0] - 2026-11-03
11
40
 
12
41
  ### Changed
@@ -5595,7 +5624,9 @@ and long-lived sessions.
5595
5624
  - A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
5596
5625
  hotkey panel that send selected text or the clipboard to your sessions.
5597
5626
 
5598
- [Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.147.0...HEAD
5627
+ [Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.149.0...HEAD
5628
+ [0.149.0]: https://github.com/dm1try/samagotchi/compare/v0.148.0...v0.149.0
5629
+ [0.148.0]: https://github.com/dm1try/samagotchi/compare/v0.147.0...v0.148.0
5599
5630
  [0.147.0]: https://github.com/dm1try/samagotchi/compare/v0.146.0...v0.147.0
5600
5631
  [0.146.0]: https://github.com/dm1try/samagotchi/compare/v0.145.0...v0.146.0
5601
5632
  [0.145.0]: https://github.com/dm1try/samagotchi/compare/v0.144.0...v0.145.0
data/README.md CHANGED
@@ -152,8 +152,8 @@ finds another session, `send_note` tells it something, and `delegate` hands a
152
152
  task to a child session that runs in parallel and reports back only its final
153
153
  reply (a normal session: `chi --attach <id>` steers it). With the `coordinator`
154
154
  bundle (in `dev`), `/coordinate <goal>` has chi split the work into tasks, each
155
- in a child session in its own git worktree, check each report and ask you
156
- before each merge; `/children` shows the children and their state. With the
155
+ in a child session in its own git worktree, check each report and merge under
156
+ an approval you give chi (once, or per repo), or ask you first; `/children` shows the children and their state. With the
157
157
  `claude-peer` bundle (in `dev`), chi sends a part of its work to a Claude Code
158
158
  session you pair it with, to validate or as context, and Claude answers back
159
159
  into chi (`/claude`); the `codex-peer` bundle (in `dev`) does the same with an
data/docs/architecture.md CHANGED
@@ -101,7 +101,7 @@ terminal renders the events it gets over the Bridge.
101
101
  | UI | `Samagotchi::TerminalUI` | REPL (Reline), rendering, REPL commands. Delegates all core work to an `Engine`. |
102
102
  | Model loops | `KernelLoop` (via `LLM::NativeBackend`), `LLM::ChatLoop` | The model↔tool loop: raw prompt, or a chat API (OpenAI chat, Anthropic Messages), chosen per host (see below). |
103
103
  | Adapters | `Samagotchi::Client`, `LLM::OpenAIChat`, `LLM::AnthropicMessages`, `LLM::HTTP` | Raw-prompt servers, the OpenAI chat API, Anthropic's Messages API, and the HTTP they share. |
104
- | Tools | `lib/samagotchi/tools/*` | Execute, read, edit, write, memory, task_*, web_fetch, delegate/delegate_result, ask_user_question, reminders, send_note, list_sessions, context_read, forget_outputs, find_tools/call_tool (the tool index), plus runtime/output-guardrails. |
104
+ | Tools | `lib/samagotchi/tools/*` | Execute, read, edit, write, memory, task_*, web_fetch, delegate/delegate_result, ask_user_question, reminders, send_note, list_sessions, context_read, objectives, forget_outputs, find_tools/call_tool (the tool index), plus runtime/output-guardrails. |
105
105
  | Bundles | `lib/samagotchi/memory_bundle/*`, `BundleCommand` | `chi bundle`: install/upgrade (3-way merge), build, uninstall; the install record under `.bundles/<name>/` that the hook, guardrail and plugin loaders read ([internals/bundles.md](internals/bundles.md)). |
106
106
  | Background | `Samagotchi::Worker`, `Samagotchi::SessionManager` | `SessionManager` forks/spawns one worker process per session; its `Worker` builds the `Engine` and `Bridge` (no terminal rendering) and runs queued turns. The spawned process (own process group) forks the worker, which `setsid`s and is reparented to init once its starter exits (it waits until the worker holds `owner.lock`, so the spawner's `Process.detach` waiter tells a starting worker): nothing that walks the spawner's process tree reaches it (`SAMAGOTCHI_WORKER_DETACH=1`, `detach_from_spawner`). The lock's pid is the worker's; `spawn`'s `child_pid` is its starter's. |
107
107
  | Web | `Samagotchi::Web::App`, `Samagotchi::Web::Server`, `Samagotchi::Web::SessionHub` | Rack+WEBrick on one port, `127.0.0.1:4567` (index.html + `/api/*` + SSE); with `web.host: lan` also on this machine's LAN address, behind an access token (`Web::Lan`, `Web::Token`). The hub is chi web's projection of the session list, pushed to every tab over `GET /api/events`. |
data/docs/bundles.md CHANGED
@@ -799,7 +799,7 @@ chi bundle install coordinator # in the dev profile
799
799
 
800
800
  chi as a coordinator of parallel work, the way you would run several agents
801
801
  yourself: split the work, one git worktree and child session per task, keep
802
- talking while they run, check each report, and merge only with your OK. It
802
+ talking while they run, check each report, and merge only when you allow it. It
803
803
  works in a session with a worker (plain `chi`, the web). A `--no-shared` REPL
804
804
  or `-p` gets no delegate reports, so the children's replies wouldn't come back
805
805
  by themselves there.
@@ -810,7 +810,7 @@ by themselves there.
810
810
  <branch>`), starts a child there with `delegate wait: false, cwd:`, tells
811
811
  you what started and ends its turn. When a child's report comes, it checks
812
812
  the branch (`git log`, `git diff --stat`, the tests the child names) before
813
- telling you. It asks before each merge (`ask_user_question`), checks that its
813
+ telling you. Before merging (**Merges** below) it checks that its
814
814
  own folder is on the default branch and that branch's log first (another
815
815
  agent may have merged the branch already), merges `--ff-only` and never
816
816
  pushes unless you ask. After a merge it stops the
@@ -856,9 +856,9 @@ by themselves there.
856
856
  diff in your turn there: the model brings the handoff up to date and
857
857
  re-plans in words, and starts, stops or merges nothing from the review
858
858
  session: the coordinator session picks the change up from the handoff,
859
- merges still asked. Experimental: it may go if it doesn't earn its place.
859
+ and merges as it always does. Experimental: it may go if it doesn't earn its place.
860
860
  - **The coordinator toolkit.** The bundle declares `coordinator`: `lean`
861
- plus `delegate`, `delegate_result` and `list_sessions`
861
+ plus `delegate`, `delegate_result`, `list_sessions` and `objectives` (the skill records moves with it)
862
862
  ([Toolkits](configuration.md#toolkits)). `/coordinate` puts a fresh session
863
863
  on it before it asks the model (`ctx.use_toolkit`). When chi refuses (a reply
864
864
  or a turn already there) and the session's toolkit has no `delegate`, the
@@ -883,19 +883,29 @@ by themselves there.
883
883
  guardrails mode and without the guardrails bundle ([a delegate child's
884
884
  boundary](guardrails.md#a-delegate-childs-boundary)); you answer on its
885
885
  card, or on the parent's while it waits.
886
- - **Merges.** The parent's merge in its own checkout is a plain `git
887
- merge`; to be asked before every merge, add a rule ([Rules in
888
- config.yml](guardrails.md#rules-in-configyml)):
889
-
890
- ```yaml
891
- guardrails:
892
- rules:
893
- - id: git-merge
894
- tool: shell
895
- command: '\bgit\s+merge\b'
896
- verdict: ask
897
- reason: merging a child's branch
898
- ```
886
+ - **Merges.** Your approval is chi's own card, not a question from the model. When its verdict on a branch is "done and
887
+ verified" (no doubts) and the branch can fast-forward, and `chi guardrails rule git-merge` prints `asks`, and it read the branch's whole diff in this session, not only the stat, the skill says `Merging <branch>: <verdict>` and runs
888
+ `git merge --ff-only <branch>` alone. You then get the approval card, or, if you allowed the rule in this repo, it
889
+ passes: home projects you trust merge on green, an unknown one still asks. Anything else (tests not run in this
890
+ session, a child that named none, a resumed session's old verdict, the rule off, doubts) goes back to a question
891
+ (`ask_user_question`). With no grant, a yes there is followed by chi's card on the merge, and declining the card is a
892
+ no. A Deny is saved and not retried: the model reports it and leaves the branch until you say otherwise. Under `--non-interactive` no one can answer, so a merge is
893
+ denied and it tells you. A parent agent may give Allow once under `guardrails.parent_answers: soft`, as it may answer
894
+ a question today.
895
+
896
+ The bundle's rule `git-merge` (`guardrails/merge.yml`) asks before every `git merge`, whatever the
897
+ branch, including `git -C`, `--git-dir=`, a `cd` before it and `sh -c`. It offers Allow once and **Allow rule git-merge
898
+ in this repo**: that grant holds for every later merge in the repository, in each of its worktrees and sessions,
899
+ and for nothing else. `git merge y && git push` still asks, once ([Approvals](guardrails.md#approvals)). `merge-base`,
900
+ `merge-tree`, `merge-file`, `mergetool` and a merge whose first word is `--abort`, `--continue` or `--quit` don't ask;
901
+ `git pull` and `gh pr merge` are not covered. It votes in every session on a machine with this bundle, in any repo,
902
+ children and background builds too, so a child's `git merge main` raises a card. A `--non-interactive` merge is denied,
903
+ with nobody to ask. It over-asks where the words are data (a commit message, `chi send -m`), and when merging the default branch into
904
+ a feature branch. `guardrails.disable: [git-merge]` switches it off. A grant goes by the repository chi is in, so
905
+ `git -C other merge` or `cd other && git merge` from a granted repo passes: an accident guard, as the rest
906
+ of the rules are. "Rule in this repo" tapped on a child's `git merge main` frees every merge there, as the rule's
907
+ reason says. A `git-merge` rule copied into config.yml votes first and keeps its behaviour; the old snippet,
908
+ `\bgit\s+merge\b`, also matches `git merge-base`, so drop your copy.
899
909
 
900
910
  ## The claude-peer bundle
901
911
 
@@ -1230,3 +1240,40 @@ for one model family, loaded into the prompt only for the models a note's
1230
1240
  through upgrades; other notes stack with the bundle's
1231
1241
  ([Model notes](memory.md#model-notes)).
1232
1242
 
1243
+ ## The decision-card bundle
1244
+
1245
+ `chi bundle install decision-card` (or the `dev` profile) installs the bundle shipped with chi. It is a try-out, written
1246
+ only against this API (`lib/samagotchi/bundles/decision-card/plugin.rb`). It does nothing in a session with no
1247
+ [objectives](sessions.md#objectives): no request, no card.
1248
+
1249
+ In a session with an active objective, an answer that is long and ends in a question gets a card when the turn is over:
1250
+ a short summary, the question, and the objective it concerns when the model names one. You answer in the composer.
1251
+ The card has no buttons, so it invents no options, and it is no "needs you": no push, no waiting mark.
1252
+
1253
+ - **The gate**: a completed owner's turn (not a stopped one, a participant's or a `--non-interactive` one), at least
1254
+ `min_chars` characters, a question mark that ends a word in the last `tail_chars` of the prose (code is left out). All of
1255
+ it is read before any request.
1256
+ - **The request**: one side answer (`ctx.ask_model`) about the turn's prompt and answer, with the list in its prompt. It
1257
+ counts in `/stats`' side requests under `decision-card`. If the model says the answer asks nothing, or leaves out the
1258
+ summary or the question, there is no card.
1259
+ - **It may be late.** It runs on a thread, so the turn's end doesn't wait, and a one-slot local server answers it after
1260
+ the running request. A new turn cancels it, which frees that slot, and a card still to come is dropped.
1261
+ - **A shown card folds** to a line (`Asked: …`) when your next turn starts (a reminder's, a child's report's or a participant's turn leaves it open): in the web a row in the same place; the terminals
1262
+ print it as an update.
1263
+ - **A failed request** is only logged (`no_card` in the debug log): no card, no notice, no push.
1264
+ - **`/short`**: the last answer's short version on demand, in a card, by the same side request. The web's objectives offer it
1265
+ under the newest long answer of a session with a list (Short version), between turns. Typed, it works in any session; during
1266
+ a turn it refuses, since that turn's answer isn't there yet.
1267
+ - **Nothing else changes**: nothing enters the conversation, so the next request is byte for byte what it would be
1268
+ without the bundle. The card lives in the session's `cards.json`, as any card. `chi bundle uninstall decision-card` takes
1269
+ it all away (a member of a profile you uninstall stays out); a session you have open needs `chi sessions restart ID`.
1270
+
1271
+ ```yaml
1272
+ bundles:
1273
+ decision-card:
1274
+ min_chars: 1200 # how long an answer is long
1275
+ tail_chars: 300 # how far from the end the question may be
1276
+ model: null # a cheap configured model ("host:id" or an alias); null is the session's own
1277
+ timeout: 120 # seconds the side request may take (a number)
1278
+ max_tokens: 300 # the side answer's limit
1279
+ ```
data/docs/cli.md CHANGED
@@ -39,6 +39,7 @@
39
39
  - `chi bundle install|upgrade|uninstall|status|diff|list|build|trash|new|test|check` — manage memory bundles (see [Bundle hooks](hooks.md#bundle-hooks-unified-workflow-bundle)); `list` shows the installed ones and the ones shipped with chi, which `install <name>` installs; `core` and `dev` are profiles that install a set of them (see [Bundle profiles](memory.md#bundle-profiles-core-and-dev), [Guardrails](guardrails.md), [Bundles](bundles.md#the-btw-bundle), [the mcp bundle](bundles.md#the-mcp-bundle) [the loop-guard bundle](bundles.md#the-loop-guard-bundle), [the check-in bundle](bundles.md#the-check-in-bundle) and [the skills bundle](bundles.md#the-skills-bundle)). `trash` lists and empties the bundle trash (moved files from uninstalls/upgrades). A usage error (an unknown subcommand or flag, a missing argument, a bad `build --scope`) exits 2, as for the other subcommands. Plain `chi` and `chi web` exit 1 on an unknown flag, a flag missing its value or a stray argument. `new`, `test` and `check` are for writing one: see [Writing a bundle](#writing-a-bundle-new-test-check)
40
40
 
41
41
  - `chi guardrails replay [--dry-run] [--threshold T] [--model M]` — seed System 1's record from the approvals this machine's sessions already answered: each ask is judged by the configured model and written to `guardrails/judgments.jsonl` with the answer the user gave, then a per-rule table prints (asks, allowed, denied, would-be auto-allows, false allows, asks with no judgment). A parent agent's answer is not the user's: only its deny is replayed, and only where the parent could have allowed. `--dry-run` judges without writing, `--threshold` overrides `guardrails.system_one.threshold` for the table, `--model` the configured model for this run. Exit 2 when `guardrails.system_one.mode` is off or no `host` is set; a usage error exits 2 (see [System 1](guardrails.md#system-1-experimental))
42
+ - `chi guardrails rule <id>` — print exactly `asks` when that rule is loaded, not disabled and votes `ask` in this `guardrails.mode` for every model; any other line says why not (no such rule, disabled, another mode, it denies, for some models only, rules that failed to load, `guardrails.enabled: false`). Exit 0 either way, 2 for a usage error. It reads config.yml and the installed bundles as a worker does (`GuardrailWiring#rules`) and takes the rule's id, never a command; the [coordinator skill](bundles.md#the-coordinator-bundle) reads it before it merges without asking
42
43
  - `chi judgments ls` / `chi judgments report NAME [--days N]` — what [System 1's saved runs](system-one.md) hold: each judgment's digests, runs, labels, failures, models and size; one judgment's counts, p histogram and labels by value. Reads only; local only; `--days` must be a positive whole number.
43
44
  - `chi toolkit report [--days N]` — grade toolkit suggestions: the kit on mode would have run (the suggested kit when it would have applied it, else the default; an on-mode line's `did` says what it ran) against the tools the session called from that turn on (only that turn when the toolkit was chosen since), `call_tool` targets included, for both the chat and the raw-prompt loop. Each is *covers*, *short of a tool* (naming the tools), *wider than needed* (a narrower kit offered in the same ask would also have covered) or *ungradable* (calls it can't read back); then a per-kit tally with what on mode would have done. Includes archived sessions and caps the report at 14 days and 500 sessions. Reads `toolkit_suggestions.jsonl` (each ask logs every offered kit's rules, expanded against the called tools here); it never runs the model or changes a session. Local only; `--days` must be a positive whole number.
44
45
 
@@ -866,6 +867,23 @@ mid-turn too, and the lists, cards, top bar and tab show it in place of the firs
866
867
  message; a continue's next link keeps it. In the web, `✎` next to the session's
867
868
  title (its strip card, or the top bar) does it. See [Session titles](sessions.md#session-titles).
868
869
 
870
+ <a id="session-objectives"></a>
871
+ `/objectives` shows what the session is to achieve, each objective's latest move under it (`last move: …`, recorded by
872
+ the model's `objectives` tool); [Objectives](sessions.md#objectives) has what that does.
873
+ - `/objectives set Two coffees | Stops before checkout (done when: no order is placed)`: a new list. Parts are split by
874
+ ` | ` or by lines; a part may end `(done when: …)`. It replaces the list, and ids go on from the old one's.
875
+ - `/objectives add TEXT`: one more (several with ` | `).
876
+ - `/objectives edit O1 TEXT`: a new text, the rest of the line whole (it may end `(done when: …)`); id and status stay.
877
+ - `/objectives done O1 [O2…]`, `/objectives drop O1 [O2…]`: mark items. An id it doesn't have refuses the whole line, and
878
+ so does `done` on a dropped item. The model is shown a done item as `O1 [done] …` and no dropped one.
879
+ - `/objectives undo O1 [O2…]`: back to active, after a mistaken `done` or `drop`.
880
+
881
+ - `/objectives check O1`: an independent read of O1's latest move, one side request; the verdict comes as a card and as
882
+ `check: …` under the move. Information only: it edits nothing, and it is refused for an objective with no move or a dropped one, and during a participant's turn. See [Objectives](sessions.md#objectives).
883
+
884
+ All of them run mid-turn too. An edit answers with the new list at once and goes into the conversation between turns.
885
+ Refused in a chi REPL's session (`--no-shared`), as `chi note` is; `/objectives` alone still answers there.
886
+
869
887
  A `/model`, `/llm-context`, `/thinking`, `/reasoning-history` or `/toolkit` change that restarts the prompt cache
870
888
  asks first: the reply says what it costs, changes nothing, and names the line
871
889
  that applies it (`/thinking low --yes`, `/model qwen --default --yes`). `--yes` (or `-y`) applies it at once.
@@ -166,14 +166,14 @@ 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`, `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. |
169
+ | `context.*` except `context.every_seconds`, `objectives.*`, `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
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. |
173
173
  | `hooks`, `bundles` | Worker start | Hooks, plugins and their bundle settings load with the engine. |
174
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. |
175
175
  | `session.shared` | Next `chi` invocation | It selects attached-worker or in-process mode for that invocation. |
176
- | `session.idle_exit_minutes`, `session.project_env`, `session.keep_empty`, `session.max_children`, `session.delegate_reports`, `session.task_wakes`, `session.max_wakes` | Worker start or next matching lifecycle decision | Existing workers keep their idle-exit timer and project env; the other values are read for the next corresponding decision. |
176
+ | `session.idle_exit_minutes`, `session.project_env`, `session.carry_last_answer`, `session.keep_empty`, `session.max_children`, `session.delegate_reports`, `session.task_wakes`, `session.max_wakes` | Worker start or next matching lifecycle decision | Existing workers keep their idle-exit timer and project env; the other values are read for the next corresponding decision. |
177
177
  | `session.retention_*`, `session.max_count`, `session.keep_status`, `session.sweep_interval_hours` | Next retention sweep | `chi web` sweeps on its configured schedule and on `GET /api/sessions`; `chi sessions prune` uses the values for that invocation. The sweep interval is checked on the next hub tick. |
178
178
  | `web.*` except `web.participants.*` | `chi web` start | Restart `chi web` to change its bind address, port or page settings. |
179
179
  | `log.*` | Process start | Each process holds its configured log sink and level after first use; restart that process. |
@@ -1504,7 +1504,7 @@ toolkit:
1504
1504
  | `full` | every built-in; the default |
1505
1505
  | `lean` | `execute`, `read`, `write`, `edit`, `memory_read`, `memory_write`, `task_create`, `task_wait`, `task_get`, `task_list`, `task_stop`, `ask_user_question`, `web_fetch`, `context_read` |
1506
1506
  | `child` | `lean` plus `send_note`, without `memory_write`, `delegate`, `delegate_result` |
1507
- | `coordinator` | `lean` plus `delegate`, `delegate_result`, `list_sessions` (declared by the coordinator bundle) |
1507
+ | `coordinator` | `lean` plus `delegate`, `delegate_result`, `list_sessions`, `objectives` (declared by the coordinator bundle) |
1508
1508
  | `coding` | `child`, with the prompt text `- Be concise in your responses` / `- Show file paths clearly when working with files` (declared by the coordinator bundle, for build and fix children) |
1509
1509
  | `review` | `read`, `memory_read` (not `forget_outputs`, even under the `forget` layer): what a [participant's](internals/participants.md#turns) turn may call, whatever the session declares (the review boundary, [guardrails](guardrails.md#a-participants-review-boundary)). Reserved: `toolkit.kits.review` is refused ("review is reserved for participants' turns"), and a bundle can't declare it. A session may run on it too, as any kit |
1510
1510
 
@@ -2221,6 +2221,7 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
2221
2221
  | `session.shared` | `true` | | Plain `chi` runs its session in a worker and attaches; `--no-shared` per run. |
2222
2222
  | `session.idle_exit_minutes` | `30` | yes | An unused worker exits after this; `0` = never. |
2223
2223
  | `session.project_env` | `mise` | | The commands a session's worker runs follow its project: `mise` runs them in the session folder's `mise env`, read once at worker start (`chi sessions restart` picks up a mise.toml edit); `off` doesn't ask mise: the tools keep the env of whoever started the worker (with a chi home, the home's `chi` still goes first on their PATH). See [Project env](sessions.md#project-env). |
2224
+ | `session.carry_last_answer` | `true` | | A continue's carry note holds the previous session's last answer after its recap; `false`: the recap only. See [Session chains](sessions.md#session-chains). |
2224
2225
  | `session.keep_empty` | `false` | | Keep sessions nothing happened in. |
2225
2226
  | `session.max_children` | `4` | | Running delegated sessions one session may have. |
2226
2227
  | `session.delegate_reports` | `wake` | | When a delegate child ends a turn, its reply reaches the parent: `wake` runs an idle parent's turn for it, `queue` waits for the parent's next turn, `off` leaves it to `delegate_result`. |
@@ -2239,6 +2240,7 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
2239
2240
  | `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). |
2240
2241
  | `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). |
2241
2242
  | `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. |
2243
+ | `guardrails.system_one.repo_rules` | `git-merge` | | The rule ids whose record goes by repository: only this repository's asks count toward a clean record, and the rule never qualifies outside one. Other rules keep one record, so a deny in one repo holds them back everywhere. Same grammar as `rules`; `[]` for none. config.yml only. |
2242
2244
  | `guardrails.system_one.threshold` | `0.8` | | The probability an auto-allow needs. config.yml only. |
2243
2245
  | `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. |
2244
2246
  | `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. |
@@ -2271,6 +2273,8 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
2271
2273
  | `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. |
2272
2274
  | `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. |
2273
2275
  | `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. |
2276
+ | `objectives.check_model` | none | | The model [Check this](sessions.md#objectives) asks, its one side request (a host:id, an alias, a host's model id); none: the session's own. **Each Check this sends that model's host up to 24,000 characters of the session's tool output and messages, off the machine with a hosted model even from a local session;** known key shapes and `token=`-style values are redacted first, by pattern only. config.yml only. |
2277
+ | `objectives.system_one` | `off` | | `audit`: System 1 answers a yes/no per active objective on what comes in and goes out, saved as runs and acted on by nothing; see [System 1's saved runs](system-one.md#objectives-in-audit). `off` (the default): no conversation text goes to a System 1 host for this. Its host and model are `system_one.host` / `.model`. config.yml only. |
2274
2278
  | `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". |
2275
2279
  | `toolkit.child` | `full` | | A delegate child's toolkit, unless its `delegate` call names one (`toolkit:`). See "Toolkits". |
2276
2280
  | `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. |
data/docs/guardrails.md CHANGED
@@ -6,7 +6,8 @@ loops (native and chat hosts). The verdict is **allow**, **ask** or
6
6
  voter. Of two asks the first one names the rule, except a rule only the
7
7
  user may allow (`chi-config`, `chi-home`, `chi-shim`, `chi-hooks`, `shell-touches-chi`,
8
8
  `chi-context-cmd`, `chi-broadcast`): it takes over, and only the scopes both asks offer
9
- are offered.
9
+ are offered. When a second YAML rule with another id asks about the same call (`git merge y && git push`), no
10
+ [rule grant](#approvals) answers it and none is offered.
10
11
 
11
12
  Who votes, in order:
12
13
 
@@ -103,7 +104,14 @@ stored with the repository's common git dir as `repo`, and the worktree as
103
104
  (samagotchi)`). An entry stored before chi 0.20 has no `repo` and still only
104
105
  matches its exact folder.
105
106
 
106
- A stored approval only relaxes an ask; a deny rule is never approvable.
107
+ A stored approval only relaxes an ask; a deny rule is never approvable. An ask names one rule, so on a call two YAML
108
+ rules ask about (`git merge y && git push`, `git push && git reset --hard`, `git merge y && rm -rf ~`;
109
+ `Verdict#second_rule_ask?`) no **rule** grant answers it, whichever rule it was given for, System 1 doesn't auto-allow
110
+ it, and it offers the scopes both rules allow, minus the rule. A session or repo approval for exactly that command still
111
+ answers it, since it is keyed on the whole command you saw: `git branch -D x && git push origin --delete x` offers
112
+ once, session and repo, while `git merge y && git push` offers once, as `git-merge` offers only once and its rule.
113
+ Two rules with one id (a config.yml copy of a bundle's rule) are one rule; a core ask followed by a rule's (the
114
+ [child boundary's](#a-delegate-childs-boundary)) keeps its own scopes.
107
115
  A file that doesn't parse is moved aside to `approvals.json.corrupt-<UTC time>`
108
116
  with one warning, and chi starts with no stored approvals (more asks, nothing lost).
109
117
  `/guardrails` lists the rules and approvals; `/guardrails revoke N` removes
@@ -521,7 +529,8 @@ message:
521
529
  - owner-voiced tools, which act in the owner's name beyond the turn:
522
530
  `delegate` and any `delegate_*` tool, `register_reminder`,
523
531
  `cancel_reminder`, `send_note`, `forget_outputs`, `task_create`,
524
- `task_stop`, `find_tools` (it loads tools into the session), and
532
+ `task_stop`, `find_tools` (it loads tools into the session), `objectives`
533
+ (a move on the owner's list), and
525
534
  `memory_write` (a memory reaches the owner's later sessions); and the tools
526
535
  that read the owner's background tasks and their output, `task_wait`,
527
536
  `task_get`, `task_list` (a pair turn starts none);
@@ -1075,6 +1084,7 @@ guardrails:
1075
1084
  system_one: # approvals' own
1076
1085
  mode: off # off (default) | audit (judge, log, never decide) | on
1077
1086
  rules: [git-push, git-rebase] # the rule ids it may answer for
1087
+ repo_rules: [git-merge] # the rules whose record goes by repository (the default)
1078
1088
  threshold: 0.8 # the probability an auto-allow needs
1079
1089
  min_record: 5 # clean records a rule needs before it is trusted
1080
1090
  ```
@@ -1177,7 +1187,8 @@ this is turned on.
1177
1187
  ### Answering for the user: `on` mode
1178
1188
 
1179
1189
  In `on` mode an ask is allowed **once**, without reaching the user, when every
1180
- gate below passes, in this order:
1190
+ gate below passes, in this order (a pair turn's fresh ask, or [a second rule's
1191
+ ask](#approvals) on the call, is judged and logged but always goes to the user):
1181
1192
 
1182
1193
  1. the mode is `on` (in `audit` the judgment decides nothing);
1183
1194
  2. the ask's rule is in `guardrails.system_one.rules` — `"git-push|git-rebase"`
@@ -1238,7 +1249,14 @@ gate below passes, in this order:
1238
1249
  or above the threshold that the user allowed, and none of its **last 20
1239
1250
  labelled** asks judged at the threshold that the user denied or later
1240
1251
  flagged as one System 1 should have asked about. Asks with no label, no
1241
- probability, or a `none` label count for neither side.
1252
+ probability, or a `none` label count for neither side. A rule in
1253
+ `guardrails.system_one.repo_rules` (default `git-merge`) counts only this
1254
+ repository's asks (the judgment's `repo`, its common git dir, so a repository's
1255
+ worktrees share it): a clean record at home doesn't let a merge pass in an
1256
+ unknown project. Outside a repository such a rule never qualifies, and lines
1257
+ written before `repo` was recorded, or by `chi guardrails replay`, are in no
1258
+ repository's record. `/guardrails` shows the rule's whole record, labelled
1259
+ `git-merge (per repo)`.
1242
1260
 
1243
1261
  Then the judgment's `decision` is `auto`, the verdict settles as
1244
1262
  `settle!(:allow, decided_by: "system one")` with scope `once`, and nothing is
@@ -1356,6 +1374,10 @@ discard uncommitted changes, and `git stash drop`/`clear`. They offer only
1356
1374
  them on every model, set `small_models` to `"*"`; to drop them, `[]` (or
1357
1375
  `disable:` the ids).
1358
1376
 
1377
+ The coordinator bundle (in `dev`, not here: everyone with the core profile would get a new ask) adds `git-merge`, which
1378
+ asks before every `git merge` ([the coordinator bundle](bundles.md#the-coordinator-bundle)). `chi guardrails rule <id>`
1379
+ says whether a rule asks on this machine now.
1380
+
1359
1381
  A bundle ships rules as `guardrails/*.yml` (the same `rules:` shape). Install
1360
1382
  records each file's sha256; a file changed afterwards, missing, or not parsing
1361
1383
  denies every call until the bundle is reinstalled.
data/docs/hooks.md CHANGED
@@ -56,7 +56,7 @@ The plugin class must respond to `#call(event)` — duck-typed, no base class re
56
56
  |-------|--------------|---------------|
57
57
  | `:session_start` | The first turn this chi process runs for the session (so again after `--resume`, a worker that idle-exited and woke, or a restart) | `{ type: :session_start, session_id: "..." }` |
58
58
  | `:before_turn` | Before each turn starts | `{ type: :before_turn, session_id: "...", prompt: "..." (nil on a continue), resumed: true \| false (true on a step-limit Continue, which resumes the run before it; false on every other turn, a reminder's or a wake's too), messages: [...] (the history before this turn) }` |
59
- | `:after_turn` | After a turn completed or was stopped once the model was asked (not after one that failed, was cut short by an interrupt signal (SIGINT), or was stopped before its `:before_turn` hooks) | `{ type: :after_turn, status: "completed" \| "canceled", present: (see [Presenting the answer](#presenting-the-answer-display-only)), refs: (see [Refs on the answer](#refs-on-the-answer-display-only)), messages: [...] (the conversation the turn stored; a cancelled or empty turn ends it with a `kind: turn_note` system message, and a context line is `kind: context`, see [sessions.md](sessions.md#notes-a-turn-leaves-for-the-model)) }` |
59
+ | `:after_turn` | After a turn completed or was stopped once the model was asked (not after one that failed, was cut short by an interrupt signal (SIGINT), or was stopped before its `:before_turn` hooks) | `{ type: :after_turn, status: "completed" \| "canceled", present: (see [Presenting the answer](#presenting-the-answer-display-only)), refs: (see [Refs on the answer](#refs-on-the-answer-display-only)), messages: [...] (the conversation the turn stored; a cancelled or empty turn ends it with a `kind: turn_note` system message, and a context line is `kind: context`, see [sessions.md](sessions.md#notes-a-turn-leaves-for-the-model); on the native loop the last model message still holds its thought block until the next turn), answer: "..." (the answer's text with no thought block; nil for a stopped turn or one that ran out of steps; needs chi newer than 0.148.0) }` |
60
60
  | `:before_generation` | Before each LLM API call (both loops), before its `:generation_started`: the hook's time (a question it waits on) isn't the generation's | `{ type: :before_generation, iteration: N }` |
61
61
  | `:after_generation` | After LLM returns (both loops); not after a generation a hook cut (`stop_generation`) | `{ type: :after_generation, iteration: N, response: "...", messages: [...] (the conversation as sent) }` |
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) }` |
@@ -636,6 +636,7 @@ stripped of surrounding whitespace, except file text, an edit's
636
636
  | `delegate` | `task` | — | `model`, `session`, `cwd`, `toolkit`, `wait`, `timeout` |
637
637
  | `delegate_result` | — | — | `session`, `timeout` |
638
638
  | `ask_user_question` | `question` | — | `question`, `options`, `header`, `multi_select`, `allow_freeform` |
639
+ | `objectives` | `note` | — | `action`, `id`, `note`, `evidence`, `change`, `text` |
639
640
  | `forget_outputs` | `note` | — | `ids`, `note`, `keep`, `restore` |
640
641
 
641
642
  A `write`'s `content:` is the file text; an `edit` carries `old_text:` and