samagotchi 0.170.0 → 0.171.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 (120) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -1
  3. data/docs/architecture.md +2 -2
  4. data/docs/cli.md +37 -23
  5. data/docs/configuration.md +1 -1
  6. data/docs/internals/block-renderers.md +2 -2
  7. data/docs/internals/bridge.md +5 -4
  8. data/docs/internals/context-telemetry.md +13 -1
  9. data/docs/internals/engine.md +4 -4
  10. data/docs/internals/inline-widgets.md +1 -1
  11. data/docs/internals/objectives.md +1 -1
  12. data/docs/internals/participants.md +235 -119
  13. data/docs/internals/web-frontend.md +164 -33
  14. data/docs/internals/working-set.md +69 -5
  15. data/docs/releasing.md +2 -1
  16. data/docs/sessions.md +6 -3
  17. data/lib/samagotchi/bridge/turn_accumulator.rb +8 -4
  18. data/lib/samagotchi/bridge.rb +39 -12
  19. data/lib/samagotchi/bundles/system/manifest.yml +1 -1
  20. data/lib/samagotchi/delivery.rb +13 -0
  21. data/lib/samagotchi/engine.rb +5 -1
  22. data/lib/samagotchi/event_fanout.rb +22 -1
  23. data/lib/samagotchi/grants.rb +5 -0
  24. data/lib/samagotchi/guardrails/review_paths.rb +2 -2
  25. data/lib/samagotchi/image_store.rb +51 -12
  26. data/lib/samagotchi/kernel_loop.rb +7 -1
  27. data/lib/samagotchi/llm/stray_tool_call.rb +7 -4
  28. data/lib/samagotchi/llm_context_stale.rb +4 -1
  29. data/lib/samagotchi/participant_messages.rb +39 -0
  30. data/lib/samagotchi/participant_revise.rb +61 -0
  31. data/lib/samagotchi/participant_uploads.rb +98 -0
  32. data/lib/samagotchi/reply_wait/poll.rb +5 -1
  33. data/lib/samagotchi/revise_anchor.rb +9 -0
  34. data/lib/samagotchi/send_command.rb +5 -0
  35. data/lib/samagotchi/session_inbox.rb +20 -12
  36. data/lib/samagotchi/session_manager/delivery.rb +7 -0
  37. data/lib/samagotchi/session_stores.rb +2 -0
  38. data/lib/samagotchi/tool_runner.rb +39 -5
  39. data/lib/samagotchi/tools/memory.rb +26 -7
  40. data/lib/samagotchi/turn_anchors.rb +0 -3
  41. data/lib/samagotchi/turn_stages/verify_anchors.rb +13 -1
  42. data/lib/samagotchi/verify_anchor.rb +6 -0
  43. data/lib/samagotchi/version.rb +1 -1
  44. data/lib/samagotchi/web/app.rb +42 -77
  45. data/lib/samagotchi/web/participant_access.rb +129 -26
  46. data/lib/samagotchi/web/participant_images.rb +185 -0
  47. data/lib/samagotchi/web/participant_review.rb +19 -138
  48. data/lib/samagotchi/web/participant_show.rb +240 -0
  49. data/lib/samagotchi/web/participant_stream_filter.rb +25 -2
  50. data/lib/samagotchi/web/participant_view.rb +331 -24
  51. data/lib/samagotchi/web/public/annotate_bar.css +1 -2
  52. data/lib/samagotchi/web/public/annotate_bar.js +1 -2
  53. data/lib/samagotchi/web/public/app.js +124 -88
  54. data/lib/samagotchi/web/public/block_host.js +7 -4
  55. data/lib/samagotchi/web/public/bubbles.js +9 -1
  56. data/lib/samagotchi/web/public/cards_offscreen.js +69 -0
  57. data/lib/samagotchi/web/public/chip_popover.js +5 -4
  58. data/lib/samagotchi/web/public/claim_link.js +4 -4
  59. data/lib/samagotchi/web/public/composer.css +4 -6
  60. data/lib/samagotchi/web/public/composer_mode.js +11 -6
  61. data/lib/samagotchi/web/public/composer_mount.js +4 -4
  62. data/lib/samagotchi/web/public/ctx.js +12 -3
  63. data/lib/samagotchi/web/public/delivery_line.js +34 -5
  64. data/lib/samagotchi/web/public/history_shape.js +5 -1
  65. data/lib/samagotchi/web/public/images.js +7 -1
  66. data/lib/samagotchi/web/public/index.html +40 -4
  67. data/lib/samagotchi/web/public/info_bar_model.js +2 -1
  68. data/lib/samagotchi/web/public/markdown_place.js +5 -4
  69. data/lib/samagotchi/web/public/owner_claim.js +3 -3
  70. data/lib/samagotchi/web/public/owner_verify.js +0 -2
  71. data/lib/samagotchi/web/public/participant_bar.js +108 -0
  72. data/lib/samagotchi/web/public/participant_main.js +289 -0
  73. data/lib/samagotchi/web/public/participant_model.js +5 -292
  74. data/lib/samagotchi/web/public/participant_names.js +6 -1
  75. data/lib/samagotchi/web/public/participant_revise.js +66 -0
  76. data/lib/samagotchi/web/public/participants_panel.js +3 -12
  77. data/lib/samagotchi/web/public/question_cards.js +14 -3
  78. data/lib/samagotchi/web/public/refs.js +53 -36
  79. data/lib/samagotchi/web/public/reload_check.js +68 -11
  80. data/lib/samagotchi/web/public/revise_stack.js +21 -10
  81. data/lib/samagotchi/web/public/send_turn.js +24 -6
  82. data/lib/samagotchi/web/public/session_fields.js +1 -1
  83. data/lib/samagotchi/web/public/session_state.js +12 -4
  84. data/lib/samagotchi/web/public/session_sync.js +9 -2
  85. data/lib/samagotchi/web/public/session_view.js +17 -7
  86. data/lib/samagotchi/web/public/stage_asking.js +6 -4
  87. data/lib/samagotchi/web/public/stage_owner_wait.js +35 -0
  88. data/lib/samagotchi/web/public/stage_view.js +18 -4
  89. data/lib/samagotchi/web/public/stage_waiting.js +74 -6
  90. data/lib/samagotchi/web/public/storage.js +22 -0
  91. data/lib/samagotchi/web/public/stream_handlers.js +2 -1
  92. data/lib/samagotchi/web/public/tokens.css +1 -1
  93. data/lib/samagotchi/web/public/turn_block.js +3 -2
  94. data/lib/samagotchi/web/public/turn_events.js +15 -6
  95. data/lib/samagotchi/web/public/turn_html.js +9 -2
  96. data/lib/samagotchi/web/public/turn_range.js +1 -2
  97. data/lib/samagotchi/web/public/verify_target.js +2 -4
  98. data/lib/samagotchi/web/public/viewer.js +59 -0
  99. data/lib/samagotchi/web/public/viewer_self.js +53 -0
  100. data/lib/samagotchi/web/public/working_set.js +11 -5
  101. data/lib/samagotchi/web/public/working_set_view.js +53 -8
  102. data/lib/samagotchi/web/public/ws_cards.js +89 -0
  103. data/lib/samagotchi/web/public/ws_peek.js +106 -0
  104. data/lib/samagotchi/web/public/ws_preview.css +126 -0
  105. data/lib/samagotchi/web/public/ws_reader.js +319 -0
  106. data/lib/samagotchi/web/public/ws_timeline.js +200 -0
  107. data/lib/samagotchi/web/public/ws_way_in.js +25 -0
  108. data/lib/samagotchi/web/session_control.rb +39 -14
  109. data/lib/samagotchi/web/session_create.rb +1 -1
  110. data/lib/samagotchi/web/session_show.rb +75 -28
  111. data/lib/samagotchi/web/viewer.rb +70 -0
  112. data/lib/samagotchi/web/working_set_routes.rb +132 -0
  113. data/lib/samagotchi/worker.rb +19 -14
  114. data/lib/samagotchi/working_set.rb +260 -8
  115. metadata +20 -6
  116. data/lib/samagotchi/web/public/participant.html +0 -218
  117. data/lib/samagotchi/web/public/participant.js +0 -719
  118. data/lib/samagotchi/web/public/participant_annotate.js +0 -76
  119. data/lib/samagotchi/web/public/review_history.js +0 -72
  120. data/lib/samagotchi/web/public/tools_overview.js +0 -42
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a13b61890c6a215efb8f13715a30637a12324d560eec91f1f827a849156453c9
4
- data.tar.gz: d839197f63a218e01cd48ee326f17f25c7b9027c906ccdf5cd78b855e31b05f8
3
+ metadata.gz: e45a5742b8855d69057bb153ca6cf956fa351408b583ae278564443cb7952f73
4
+ data.tar.gz: fa6dec2db1397d60c023f3bd939b5a36795c5482f59a42f768a99455829ee533
5
5
  SHA512:
6
- metadata.gz: fcef2226618d6d543b180c536a24cb8b2e669a1c5a309a424b7c64acede977ade18593a36af9da5cf0e15a1e5781502f8dae3b2f7c78d61b7193905dc4538c86
7
- data.tar.gz: f66ae46d4079588380f56a7fe6ffd7f222fc7eea442545f1200ce5598a8897681a9e3924ad3933189320870f6cf0e9ad18e389b763cf5f55db863c625a7433ab
6
+ metadata.gz: 37dac3cb11eed26b34929ba153e25294ece85fcb88fe12f3e3a4b3e4cce2d2ac881983cfb200c0c13febae830d6ca1ce74e7afffa61eb4a9cb7f2d0daf836633
7
+ data.tar.gz: 28e81c566c62bcee5da8fcbc234c08b4486a8d2bbf6d320f3c28dd85d9e28ab4a1f7ac961a2a00333700db345b206b24cb4e5f3901fb7306b35942c159d8fd3c
data/CHANGELOG.md CHANGED
@@ -8,6 +8,33 @@ and commands may change between minor versions. How releases are made:
8
8
 
9
9
  ## [Unreleased]
10
10
 
11
+ ## [0.171.0] - 2026-11-14
12
+
13
+ ### Added
14
+
15
+ - chi web: see what chi has of a working-set item: a row's ▸ in the working set list peeks at its latest edit or read, and "Open the timeline" (also a tool row's "timeline" and a file ref's "What chi has") shows every read as merged line slices over a strip of the file, every edit as its diff, slices chi no longer has greyed, and for a memory that changed since chi read it, the diff to what it says now.
16
+ - chi web: a participant can attach images to their own turn (paste, drop or the image button); they see their own thumbnails only, up to 40 images per invite, within their cap.
17
+ - chi web: a participant can Revise the answer to their own message (when no one else's turn came after it); the earlier version folds into a stack on every page, and a participant's page now draws Revise stacks too.
18
+
19
+ ### Changed
20
+
21
+ - chi web: the opt-in reload check (`web.reload_check`) now compares a turn's cards and answered questions, not just its rows: a card or question a reload places differently is a mismatch.
22
+ - chi web: a participant's message sent while their own turn runs now goes into that turn at its next step, as the owner's does; during someone else's turn it still waits and runs as their own turn after it.
23
+ - chi web: a participant's link now opens the main page: the same conversation, live turn box and composer as the owner's, with their name and link expiry at the top, their spending against the cap under the composer, Stop turn on their own turns, "waiting for the owner" (and for what kind of thing) when their turn needs you, and the composer giving way to one line at their limit, in a stopped session or once their link ended. Your messages read "owner" to them; they get none of your controls and no file panel.
24
+ - chi web: pressing Enter twice while a message is still being sent sends it once.
25
+
26
+ ### Removed
27
+
28
+ - chi web: the separate participant page (with its Plan view of a review's files) and the owner's "Participant view ↗" in the participants panel.
29
+
30
+ ### Fixed
31
+
32
+ - A reply that is only a tool call written as text, closing with `</tool_call>`, is asked again instead of taken as the answer.
33
+ - chi web: a memory in the working set is marked stale when its file, or the model overlay read with it, changed since chi read it.
34
+ - chi web: a page opened while a turn runs shows the context percent at once, not `ctx ?`.
35
+ - chi web: in the iPhone Home Screen app, the top bar no longer sits in iOS's blurred band under the clock.
36
+ - chi web: a question card folded in the turn box stays folded after a reload or when the page reconnects mid-turn.
37
+
11
38
  ## [0.170.0] - 2026-11-13
12
39
 
13
40
  ### Fixed
@@ -6289,7 +6316,8 @@ and long-lived sessions.
6289
6316
  - A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
6290
6317
  hotkey panel that send selected text or the clipboard to your sessions.
6291
6318
 
6292
- [Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.170.0...HEAD
6319
+ [Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.171.0...HEAD
6320
+ [0.171.0]: https://github.com/dm1try/samagotchi/compare/v0.170.0...v0.171.0
6293
6321
  [0.170.0]: https://github.com/dm1try/samagotchi/compare/v0.169.0...v0.170.0
6294
6322
  [0.169.0]: https://github.com/dm1try/samagotchi/compare/v0.168.0...v0.169.0
6295
6323
  [0.168.0]: https://github.com/dm1try/samagotchi/compare/v0.167.0...v0.168.0
data/docs/architecture.md CHANGED
@@ -240,8 +240,8 @@ agent logic and can be used without any terminal rendering; the UI is a thin lay
240
240
  run's own prompts until their turns end; `#run` distinguishes an empty answer from a
241
241
  prompt this terminal removed before it ran (both exit 1), as well as chi's other outcomes.
242
242
  Without a capable terminal it falls back to `PlainSurface` (append-only).
243
- - The browser's page (`lib/samagotchi/web/public/`: `app.js` for the owner, `participant.js` for a participant) is a
244
- client of chi web: where its state lives, which redraw follows which write, its render idioms, stale-answer guards,
243
+ - The browser's page (`lib/samagotchi/web/public/`: `app.js`, the owner's and a participant's alike, with
244
+ `participant_main.js` for what is a participant's) is a client of chi web: where its state lives, which redraw follows which write, its render idioms, stale-answer guards,
245
245
  storage keys and streams are in [internals/web-frontend.md](internals/web-frontend.md).
246
246
 
247
247
  #### Using the core
data/docs/cli.md CHANGED
@@ -1223,7 +1223,7 @@ threshold; its folded row shows its first line as typed). A delegate
1223
1223
  report itself starts folded. Folding is this browser's choice, kept per session in
1224
1224
  localStorage (`chi_answer_fold:<session id>`, the last 200 folded answers and prompts;
1225
1225
  the keys of sessions gone from the list are dropped when the list loads);
1226
- other viewers and the participant page are not affected. A jump into a
1226
+ other viewers (a participant's browser too) are not affected. A jump into a
1227
1227
  folded answer (a claim link, say) unfolds it for that page load.
1228
1228
 
1229
1229
  **Revise** rewrites an answer and stacks the versions. A finished turn's
@@ -1246,8 +1246,13 @@ version on view, and the chip names it, "v1 of 3"); › back to the newest. Swit
1246
1246
  page shows, and a reload shows the newest again. Revise
1247
1247
  again on the newest version adds to the stack. Stacks are session data (the
1248
1248
  stamp is on the Revise prompt, see [sessions.md](sessions.md), Revise
1249
- stacks): every owner's page shows them, live and after a reload; the
1250
- participant page doesn't draw them yet. A session whose worker predates
1249
+ stacks): every page shows them, live and after a reload, a participant's too.
1250
+ A participant (`chi share`) gets Revise on the answer to their own message
1251
+ only, while no one else's turn (the owner's, another participant's, a wake
1252
+ turn's) came after it; the owner's answers have no button on their page, and
1253
+ a closed composer shows none. A send that came too late is refused with
1254
+ "someone else's turn came after this answer: send a new message instead",
1255
+ the text and the chip kept. A session whose worker predates
1251
1256
  Revise (started by an older chi) disables the button and shows the chip as
1252
1257
  "Revise: restart this session to use Revise"; a message sent then is a plain
1253
1258
  one. A Revise turn that fails comes back into the composer with its chip.
@@ -1336,7 +1341,7 @@ made (up to 120 lines or 8 KB), live and after a reload (see
1336
1341
  `web.view`, `chi web --web-view`, `SAMAGOTCHI_WEB_VIEW` and `?view=` were removed with the turn view (the
1337
1342
  stage is the only view; a config.yml that still sets `web.view:` warns about an unknown key).
1338
1343
 
1339
- **When things happened**, on the owner's page and on a participant's review page: where 10 minutes or
1344
+ **When things happened**, on the owner's page and on a participant's: where 10 minutes or
1340
1345
  more of quiet passed between the end of one activity and the start of the next (a turn, a context
1341
1346
  note, a reminder), the history has one dim centred line, `── 14:32 · 47 min later ──`; the date is
1342
1347
  added when the day changed or the turn wasn't today (`── Sat 24 Oct 09:10 · 13 h later ──`).
@@ -1402,6 +1407,12 @@ session bar shows the `mem N` chip again.
1402
1407
  forgotten row offers `refresh`, the same add of it. While a turn runs the
1403
1408
  stage's rail shows an add queued for its end as a dashed `+ name · next turn`
1404
1409
  line.
1410
+ - **What chi has of an item**: a row's `▸` in the list peeks at it (its latest edit's diff, else its newest
1411
+ read's first lines); `Open the timeline` (also a tool row's `timeline` and a file ref's ▾ `What chi has`) puts
1412
+ the item's timeline over the stage, newest first: reads merged into line slices over a strip of the file, edits
1413
+ as diffs, what chi no longer has greyed (with `undrop` for your drop), a changed memory's `Now · diff`. `← Stage`
1414
+ or Esc goes back to the same place, ↑↓ step through the set; on a phone it is full screen (`‹ Set`, `‹ ›`).
1415
+ Yours only: a participant's list has no `▸`.
1405
1416
 
1406
1417
  Only `read`, `edit`, `write`, `memory_read`, `memory_write` and `web_fetch`
1407
1418
  calls that succeeded count; files an `execute` touched don't.
@@ -1442,13 +1453,11 @@ On a touch screen (a phone, a tablet) the bar sits just above the composer
1442
1453
  instead of next to the selection, where the system's own Copy menu and the
1443
1454
  selection handles would cover it; several pills wrap to a second row.
1444
1455
 
1445
- A review's participants get the same on their page (a `chi share` / `chi
1446
- review` link): selecting text in the Plan or the thread quotes it into
1447
- their comment box, labelled `From docs/plan.md:` or `From Sam's comment:`,
1448
- with the same presets. Not in the owner's participant view, and no plugin
1449
- pills there (a review turn refuses `/` lines).
1456
+ Participants get the same on their page (a `chi share` / `chi review`
1457
+ link, the main page): a selection quotes into their composer with the same
1458
+ presets; no plugin pills and no Copy link there.
1450
1459
 
1451
- After the presets, both pages have a built-in **Verify**: the quote, then
1460
+ After the presets comes a built-in **Verify**: the quote, then
1452
1461
  an ask to check the claim against the code and end on a line `Verdict:
1453
1462
  confirmed|refuted|unclear — <reason>`. Verify is offered only on text in a finished turn's final answer, the
1454
1463
  claim a verdict card can come from; elsewhere (an earlier reply, a step, a message, a line added mid-turn, a turn
@@ -1738,7 +1747,9 @@ chi share revoke --all # every link to every session
1738
1747
  message is a turn of its own, queued behind the running one, that the model answers **read-only**. It may read the
1739
1748
  project (never its secret files or chi's own), and proposes changes as text or a diff for you to apply; it can't
1740
1749
  write, run, fetch, send notes or set reminders, and any approval or question is yours. A line starting `!` or `/`
1741
- and images are refused. One running and 3 waiting turns per participant (`web.participants.queue`). You pay: each
1750
+ is refused. A participant can attach images to their own messages (paste, drop or the image button; png, jpeg, gif
1751
+ or webp), up to 40 per invite and within their cap, when the session's model takes images; they see only their own
1752
+ images, anyone else's as "N images". One running and 3 waiting turns per participant (`web.participants.queue`). You pay: each
1742
1753
  participant has a spending cap (`--cap DOLLARS`, default $2, `web.participants.cap`), counted from their turns'
1743
1754
  cost and the side requests they cause; at the cap their next comment is refused and you get a notice, and `chi
1744
1755
  share cap` raises it. `/stats` shows each participant's spend. Their turns go only to a worker with the review
@@ -1763,16 +1774,20 @@ chi share revoke --all # every link to every session
1763
1774
  Apply (they acted already). This is an accident guard for a trusted team, not containment: a pair turn can still
1764
1775
  send what it reads out through `web_fetch` or your MCP tools. A link without `--identity` gets the same access:
1765
1776
  share it only with people you'd let type in your session.
1766
- - **The participant page.** Their link opens a page of its own (never yours): the session's title and status, who
1767
- else takes part, and the conversation as a participant sees it: every prompt and reply with who wrote it, each
1768
- step's tools as one line (`read docs/plan.md`: the tool and a project file's path, never arguments or output), and
1769
- the reply streaming in while a turn runs. A "Comment or ask" box sends their comment (your composer's box, with its
1770
- grip to resize or fold it and ↑/↓ over their own earlier comments from that browser; no `/` list, no images yet);
1771
- their queued comments show with **Take back**; "waiting for the owner" shows while you answer a question or an approval of the turn; a refused
1772
- comment (a `!` or `/` line, too many waiting, their cap reached) says why. On a phone it is the thread first with
1773
- the box pinned, tool lines folded. They never see tool output or arguments, thinking, approvals or questions,
1774
- memories, context notes, plugin and delegate input, cards, command output, the working set, cost, timing or host
1775
- paths; secret-shaped text (API keys, tokens, private keys) is redacted too. An agent participant can wait on its
1777
+ - **Their page is the main page.** Their link opens the page you use, as theirs: the session's title, their name
1778
+ ("Alex · invited") and when the link ends, the conversation with the same folded steps, answers and live turn box
1779
+ (what chi says, which tools and files it touches, the working set's file names; never thinking, tool arguments or
1780
+ output), and the composer. Your messages read "owner" to them. Under the composer: the turn's status, the context's
1781
+ fill, the speed (the turn's time while one runs) and what they spent of the cap you set ("$0.42 of $2.00 used").
1782
+ While their own turn runs they can **Stop turn**, and a message goes in at its next step (Alt+Enter keeps it for
1783
+ after the turn); while yours or another participant's runs, it goes in as their own turn afterwards. When their
1784
+ turn waits for you (an approval, a question, your Continue) they see that it waits and for what kind of thing,
1785
+ never its content. At their cap, in a stopped session, or once their link ended the composer gives way to one line
1786
+ and the conversation stays readable. They get none of your controls: no new session, list, rename, model, images
1787
+ (yet), session menu, participants panel, cards or approvals, and no file panel (they ask chi to quote what they
1788
+ need). They never see tool output or arguments, thinking, approvals or questions, memories, context notes,
1789
+ plugin and delegate input, cards, command output, your cost or host paths; secret-shaped text (API keys, tokens,
1790
+ private keys) is redacted too. An agent participant can wait on its
1776
1791
  turns' replies through the API, or with chi: its link's token in a remote, `chi --remote NAME send --wait -m "…" ID`,
1777
1792
  or `send -m`, which prints the turn id `--wait --turn T` takes; that wait exits 3 with `waiting_for_owner` while
1778
1793
  their turn waits on you (a question, an approval, your step-limit Continue) and 1 with `refused` (`cancel_reason`
@@ -1787,8 +1802,7 @@ chi share revoke --all # every link to every session
1787
1802
  then `2 participants`, with a green dot while one is on their page) opens the panel: each participant's state, last visit,
1788
1803
  spend against their cap, turns and expiry, with **Cap** and **Revoke** (revoking ends their page and stream and
1789
1804
  drops their queued comments). **Invite** takes a name, person or agent, the expiry and a cap, and shows the link
1790
- once, with Copy, Share (a phone's share sheet) and its QR code. **Participant view ↗** opens their page for this
1791
- session in a new tab, read-only, through the same server filter: exactly what they see. A participant's prompt
1805
+ once, with Copy, Share (a phone's share sheet) and its QR code. A participant's prompt
1792
1806
  shows their name in their colour, live and after a reload.
1793
1807
  - `chi --remote studio share …` invites on studio: the link names studio's LAN address. Locally, the link names
1794
1808
  this machine's running chi web, on the port it listens on (it records it in `$XDG_STATE_HOME/samagotchi/web.json`;
@@ -2385,7 +2385,7 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
2385
2385
  | `web.host` | `127.0.0.1` | yes | `127.0.0.1`, `::1` or `localhost`; `lan` (this machine's private IPv4 address) or one of its IPv4 addresses also opens `chi web` to the network, with an access token, asked of this machine's 127.0.0.1 too (its `Local:` link carries a one-time code; `chi web --new-token` replaces the token; it is chi web's one access token, also used by `--tunnel` and `web.public_url`/a proxy). Anything else binds `127.0.0.1` with a warning. See [CLI: chi web on your phone](cli.md#chi-web-on-your-phone). |
2386
2386
  | `web.markdown` | `false` | yes | Render answers as Markdown in `chi web`. |
2387
2387
  | `web.working_set` | `true` | yes | The stage view's working set: the files, memories and URLs a turn brought into the model's context, in a rail in the stage, a footprint as each finished turn's block summary and a `set N` chip in place of the mem chip; `false` brings the mem chip back. `?working_set=1` or `?working_set=0` on the page URL overrides it for that page load. See [CLI: the working set](cli.md#the-working-set). |
2388
- | `web.reload_check` | `false` | yes | Opt-in: once each finished turn has moved into the history, the page reads the session again (one full read per turn end), draws that turn as a reload would and compares the two, strictly: a known divergence counts as a mismatch too. `chi web` logs one `reload_checked` line per check and a `reload_mismatch` line naming the kinds that differed, `excused` (the known divergences, `history_shape.js` `ACCEPTED`, that would explain them) and `explained` (all of them had one), with `turn_id`, `drawn` (the turn came back from its drawing, so a mismatch with `drawn: true` is a drawing bug), `undrawn` (how many of the reload's rows came without their turn's drawing, the page's plain fallback: 0 unless chi web missed a turn) and the worker's version: counts only, never content. The count is `grep -c reload_mismatch` over `grep -c reload_checked` in chi web's log, split by `excused`. Cards and answered question cards are left out: a reload places them after the render, which the check doesn't run (the e2e reload checks compare them). Every open owner tab runs the check; `chi web` logs a turn's first report only, and starts over when it restarts. |
2388
+ | `web.reload_check` | `false` | yes | Opt-in: once each finished turn has moved into the history, the page reads the session again (one full read per turn end), draws that turn as a reload would and compares the two, strictly: a known divergence counts as a mismatch too. `chi web` logs one `reload_checked` line per check and a `reload_mismatch` line naming the kinds that differed, `excused` (the known divergences, `history_shape.js` `ACCEPTED`, that would explain them) and `explained` (all of them had one), with `turn_id`, `drawn` (the turn came back from its drawing, so a mismatch with `drawn: true` is a drawing bug), `undrawn` (how many of the reload's rows came without their turn's drawing, the page's plain fallback: 0 unless chi web missed a turn) and the worker's version: counts only, never content. The count is `grep -c reload_mismatch` over `grep -c reload_checked` in chi web's log, split by `excused`. The turn's own cards and answered questions are compared too: a drawn turn already renders its plugin cards, and the snapshot's questions (and an undrawn turn's entries) are placed into the offscreen render by the page's own placer with inert hooks (`cards_offscreen.js`). A card counts as the same when it has the same id and title in the same place, a question when its summary line and the kept reply's plain line do; bodies, buttons, folds and the tag a card is drawn in are ignored. Only entries CardStore attributes to this turn (`of_turn`) are compared: a card of another turn or between turns is dropped by id, and a question the read gives no turn and no opening stamp for (an older worker) is left out on both sides. Between-turn cards and notices stay with the e2e reload checks. Every open owner tab runs the check; `chi web` logs a turn's first report only, and starts over when it restarts. |
2389
2389
  | `web.annotate_presets` | `Agreed\|Could you please elaborate?` | yes | Quick replies next to Annotate in `chi web`, `\|`-separated (a YAML list works too); `""` in the file or on the CLI leaves only Annotate (an empty env value means the default). See [CLI](cli.md#web-annotate-presets). |
2390
2390
  | `web.editor` | `vscode` | yes | What a file ref in `chi web` (a file tool row's title, a diff line) opens, for a viewer on this machine: a preset (`vscode`, `vscode-insiders`, `cursor`, `zed`) or a URL template with `{path}` and an optional `{line}`; `none` turns refs off. See [Opening files from chi web](#opening-files-from-chi-web). |
2391
2391
  | `web.start_dir` | the folder `chi web` runs in | | The folder `chi web` starts in (`chi web --start-dir DIR` for one run): its page (the printed link, a bare `/`, the LAN link and QR code, the Home Screen app) opens on that folder's project, outside a project (no repository or marked folder) on every session, and a new session from the all view starts there; the header's project picker switches projects. `~` and a relative path (resolved from your home folder) work; one that isn't a folder stops `chi web` from starting. `chi web` never changes its own folder. See [CLI](cli.md). |
@@ -18,8 +18,8 @@ model. The TUI is untouched: it shows a fence as source.
18
18
  made), then `Provenance#script_failure` (the check a context provider's scripts get too); a file with no recorded sha, a missing one or a changed one is 409
19
19
  `renderer_changed`, each with its own reason. ETag + `no-cache`, as static files. A `block_renderers:` list that
20
20
  doesn't parse is logged once per bundle and problem (each list request and file fetch reads it).
21
- 3. `block_watch.js` (`createBlockWatch`, made by `block_host.js`'s `createBlockHost`, wired in `app.js`; `participant.js`
22
- isn't) finds and watches the blocks; `block_host.js` is its frame drawer (`createFrameDrawer`). The watcher asks an
21
+ 3. `block_watch.js` (`createBlockWatch`, made by `block_host.js`'s `createBlockHost`, wired in `app.js`; a participant's page
22
+ gets the core drawers only, no bundle's renderers: `block_renderers`) finds and watches the blocks; `block_host.js` is its frame drawer (`createFrameDrawer`). The watcher asks an
23
23
  ordered list of drawers, core ones first (`inline_widgets.js` for `chi-ui`: [inline-widgets.md](inline-widgets.md)), then the frame drawer, which waits for `/api/block_renderers`;
24
24
  the first that `claims` the block's language draws it, and a language none claims is left alone. The contract
25
25
  (`claims`, `needsDraw`, `draw`, its `reconsider`, `sessionChanged`) is in the module's header. The shared DOM
@@ -57,8 +57,10 @@ Three other users: `replay_events(turn)` turns a `current_turn` back into the li
57
57
  `command_queued` per waiting command), which chi web's `GET /api/sessions/:id` sends as `replay` from the worker's
58
58
  snapshot (`from_wire` symbolizes it), so any worker's snapshot gives the page the same events
59
59
  (`spec/shared/turn_snapshot.json` pins it); `messages_of` gives a
60
- plugin's `ctx.messages` the turn so far (`Engine#running_turn_messages`); `participant_turn?` tells `POST /turn` that
61
- a participant's review turn runs, so the owner's input queues behind it.
60
+ plugin's `ctx.messages` the turn so far (`Engine#running_turn_messages`); `participant_author` (and
61
+ `participant_turn?`) tell `POST /turn` whose turn runs: input goes into it only from its own author
62
+ (`Bridge#same_author_steer?`), so the owner's input queues behind a participant's turn, and a participant's goes in at
63
+ the next step of their own turn only.
62
64
 
63
65
  ## TurnDrawings
64
66
 
@@ -618,8 +620,7 @@ successor from the old worker); chi web's `App#worker_link` / `#await_worker_lin
618
620
  `bridge_error` / `bridge_secret` for a refused Bridge; 200 only after the Bridge answers; full history of any session
619
621
  is served by `GET /api/sessions/:id/output`). It opens the Bridge before answering and gives the one opened socket to
620
622
  `App#proxy_stream_body` / `ProxyStreamBody`, which closes it when the body or hijacked client ends. The owner's stream
621
- is a byte pipe; the body gives it the request's frame filter (`App#stream_filter`: nil for the owner; for a participant, and the owner's participant view
622
- `?as=participant`, a `Web::ParticipantStreamFilter`, which cuts the Bridge's chunks into frames and writes each again
623
+ is a byte pipe; the body gives it the request's frame filter (`App#stream_filter`: nil for the owner; for a participant, a `Web::ParticipantStreamFilter`, which cuts the Bridge's chunks into frames and writes each again
623
624
  from `Web::ParticipantView.event`'s allowlist, dropping every other event; only `from_seq` and `snapshot` reach the
624
625
  Bridge; [internals/participants.md](participants.md#what-a-participant-sees)). Resume/ring-buffer state is **in-memory**, per worker: a cursor from
625
626
  an earlier worker gets the `reset` snapshot, not a replay.
@@ -24,7 +24,10 @@ next turn's estimate before its first count uses it instead of
24
24
  Emission behavior:
25
25
 
26
26
  - The estimate is emitted on every request (both loops, before each request),
27
- so the web and the attached TUI get a fresh number each time.
27
+ so the web and the attached TUI get a fresh number each time; the Engine also
28
+ keeps the last one it saw (`@last_context_status`, from the stream handler),
29
+ so a page or TUI that joins while a turn runs reads it from
30
+ `session_state_snapshot` at once instead of waiting for the turn to end.
28
31
  - The model's own `[CONTEXT: …]` line is still gated: it is left only on a rise
29
32
  into a guidance bucket (from the second threshold up), never on a fall, and
30
33
  not again for a bucket a resumed session's line already names.
@@ -54,6 +57,15 @@ plain `Context window unknown` after it, and for a session whose window lookup f
54
57
  session's first counted turn. A
55
58
  hook's switch (`before_turn`) sends nothing: its turn's first request counts on the new model anyway.
56
59
 
60
+ The owner's show (`Web::SessionShow#shown_session_json`) passes the snapshot's
61
+ `context_status` to the page as `session.context_status`, which
62
+ `session_state.js#sessionStateFrom` reads as the ctx percent before any stream
63
+ event (it wins over the saved fill). With a window known and no percent yet,
64
+ `ctxBarTip` says `No context count yet: the next request counts it`; while the
65
+ session's turn runs (`window.running`, the info bar's own flag) the tooltip's
66
+ title is `Context in use (this turn so far)` rather than `Context used after
67
+ the last turn`.
68
+
57
69
  A process that loads the session on another model than its last turn ran on (`Engine#session=`:
58
70
  `SessionMetrics#last_model_ref` differs from `effective_model_ref`; a `--resume`'d one-shot with `--model`, a worker woken
59
71
  after a `/model` with no turn since) drops the old window (`forget_model_reports!`) and calls
@@ -688,10 +688,10 @@ announced at once, marked `anytime: true`, **never as the running turn's events*
688
688
  Both loops, unless marked:
689
689
 
690
690
  - <a id="a-reply-that-is-only-a-tool-call"></a>**A reply that is only a tool call** (`LLM::StrayToolCall`, chat loop
691
- only): a reply with no tool calls whose whole text is one tool-call block closed by the tag that opened it
692
- (`<function=` by `</function>`, `<parameter=` by `</parameter>` or `</function>`, `<tool_call>` by
693
- `</tool_call>`; inner tags not needed) isn't an answer. Prose or a fence before it, or anything after the close,
694
- keeps it an answer. Nothing is guessed from the block and it never enters the history (`:generation_completed` and
691
+ only): a reply with no tool calls whose whole text is one tool-call block closed by the tag that opened it, or by
692
+ the wrapper's `</tool_call>` (`<function=` by `</function>` or `</tool_call>`, `<parameter=` by `</parameter>`, `</function>`
693
+ or `</tool_call>`, `<tool_call>` by `</tool_call>`; inner tags not needed) isn't an answer. Prose or a fence before it, or anything
694
+ after the close, keeps it an answer. Nothing is guessed from the block and it never enters the history (`:generation_completed` and
695
695
  `:after_generation` have already seen the text): `:generation_retrying`
696
696
  with `restarted` voids the streamed text, `:empty_answer_retry` with `malformed` goes out, and a hidden
697
697
  `TurnNote.malformed_retry` asks again at the retry temperature. Its own budget (`retry.empty_answer: 0` doesn't turn
@@ -136,7 +136,7 @@ same cleaned id (`rowId`, one rule for both), so what is drawn is what fills. A
136
136
  strings reach the page through `textContent` and attributes only, never `innerHTML`, so a step reading
137
137
  `<img src=x onerror=…>` shows as those characters.
138
138
 
139
- **What doesn't draw.** The participant page (`participant.js` wires no block host), `web.markdown` off (no
139
+ **What doesn't draw.** `web.markdown` off (no
140
140
  `data-lang`), a live answer while it streams (its YAML shows until the turn's end re-renders it as markdown), and
141
141
  anything outside `TEXTS`: cards, question replies, a lead-in bubble, delegate reports. The terminal shows the YAML.
142
142
 
@@ -213,7 +213,7 @@ command), which absorbs the file. It no longer waits for the fallback tick.
213
213
  (completed, canceled, failed, in `turnEnded`), `data-participant` for a participant's turn (from `turn_started`'s
214
214
  author live, the prompt's author in `drawnGroupPieces`), and each row's `data-tool` (also `turn_html.js
215
215
  toolRowTemplate`, so live and reloaded rows stay the same markup). A pair turn's `objectives` call is always denied,
216
- so without the participant stamp every pair turn that edits would get it. The participant page draws none of this.
216
+ so without the participant stamp every pair turn that edits would get it. A participant's page draws none of this (their show carries no objectives).
217
217
  - **The tracker** (`objectives_track.js`, a module of its own for the same reason): the list's row under an item it read
218
218
  says `tracker: no sign — nothing ran for it · 14:32` (`.obj-track`), with weight and the warn colour when it differs
219
219
  from the agent's last move's state (`trackDiffers`: a move to compare with, a reading other than no answer), faded when