samagotchi 0.106.0 → 0.108.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 +157 -1
- data/bin/chi +2 -0
- data/docs/architecture.md +6 -1
- data/docs/cli.md +45 -5
- data/docs/guardrails.md +8 -2
- data/docs/internals/engine.md +17 -6
- data/docs/internals/participants.md +129 -30
- data/docs/internals/remote-sessions.md +12 -5
- data/docs/internals/working-set.md +2 -1
- data/docs/memory.md +20 -0
- data/docs/plugins.md +38 -3
- data/docs/releasing.md +13 -6
- data/docs/sessions.md +39 -4
- data/docs/sub-agent.md +3 -1
- data/docs/testing.md +7 -1
- data/lib/samagotchi/bridge/turn_accumulator.rb +8 -1
- data/lib/samagotchi/bridge.rb +7 -2
- data/lib/samagotchi/bridge_client/stream_request.rb +10 -3
- data/lib/samagotchi/bundles/coordinator/manifest.yml +6 -5
- data/lib/samagotchi/bundles/coordinator/plugin.rb +85 -5
- data/lib/samagotchi/bundles/coordinator/skill_coordinator.md +18 -0
- data/lib/samagotchi/bundles/cost-cap/manifest.yml +2 -2
- data/lib/samagotchi/bundles/cost-cap/plugin.rb +51 -9
- data/lib/samagotchi/bundles/dev/manifest.yml +2 -2
- data/lib/samagotchi/bundles/system/manifest.yml +2 -2
- data/lib/samagotchi/bundles/system/memory_guide.md +1 -1
- data/lib/samagotchi/bundles/work-cap/manifest.yml +2 -2
- data/lib/samagotchi/bundles/work-cap/plugin.rb +50 -14
- data/lib/samagotchi/child_reports.rb +2 -1
- data/lib/samagotchi/cli/parent_wait.rb +1 -1
- data/lib/samagotchi/context_sources.rb +1 -6
- data/lib/samagotchi/continue_offer.rb +56 -7
- data/lib/samagotchi/engine.rb +36 -25
- data/lib/samagotchi/guardrail_wiring.rb +9 -6
- data/lib/samagotchi/guardrails/review_boundary.rb +6 -57
- data/lib/samagotchi/guardrails/review_paths.rb +93 -0
- data/lib/samagotchi/idle_scheduler.rb +17 -2
- data/lib/samagotchi/last_turn.rb +7 -0
- data/lib/samagotchi/log_subscriber.rb +2 -1
- data/lib/samagotchi/memory_orphans.rb +74 -0
- data/lib/samagotchi/memory_paths.rb +4 -4
- data/lib/samagotchi/model_profile.rb +5 -3
- data/lib/samagotchi/parent_report.rb +34 -4
- data/lib/samagotchi/participant_messages.rb +11 -0
- data/lib/samagotchi/participants.rb +20 -6
- data/lib/samagotchi/plugin/api.rb +2 -1
- data/lib/samagotchi/plugin/sessions.rb +25 -0
- data/lib/samagotchi/remote_command.rb +1 -1
- data/lib/samagotchi/remotes.rb +23 -8
- data/lib/samagotchi/reply_wait.rb +7 -2
- data/lib/samagotchi/review_apply.rb +123 -0
- data/lib/samagotchi/review_command.rb +121 -0
- data/lib/samagotchi/review_start.rb +161 -0
- data/lib/samagotchi/run_options.rb +17 -2
- data/lib/samagotchi/send_command/busy.rb +5 -2
- data/lib/samagotchi/send_command/wait_target.rb +18 -8
- data/lib/samagotchi/send_command.rb +2 -0
- data/lib/samagotchi/session.rb +33 -5
- data/lib/samagotchi/session_access/contract.rb +10 -2
- data/lib/samagotchi/session_access/dispatch.rb +8 -2
- data/lib/samagotchi/session_access/http_transport.rb +10 -1
- data/lib/samagotchi/session_access/local.rb +19 -2
- data/lib/samagotchi/session_access/remote.rb +15 -1
- data/lib/samagotchi/session_access/web_bridge_client.rb +1 -1
- data/lib/samagotchi/session_access.rb +23 -10
- data/lib/samagotchi/session_chain.rb +10 -2
- data/lib/samagotchi/session_continue.rb +4 -3
- data/lib/samagotchi/session_inbox.rb +12 -8
- data/lib/samagotchi/session_manager.rb +30 -8
- data/lib/samagotchi/session_metrics.rb +7 -6
- data/lib/samagotchi/session_participants.rb +3 -1
- data/lib/samagotchi/session_review.rb +28 -0
- data/lib/samagotchi/share_command.rb +7 -1
- data/lib/samagotchi/steer.rb +11 -1
- data/lib/samagotchi/terminal_ui/attached_loop.rb +34 -27
- data/lib/samagotchi/terminal_ui/formatting.rb +2 -2
- data/lib/samagotchi/terminal_ui/one_shot_sink.rb +5 -1
- data/lib/samagotchi/testing/sessions.rb +10 -2
- data/lib/samagotchi/tools/memory.rb +16 -9
- data/lib/samagotchi/turn_checkpoint.rb +13 -0
- data/lib/samagotchi/turn_origin.rb +68 -0
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +108 -21
- data/lib/samagotchi/web/grants.rb +92 -17
- data/lib/samagotchi/web/participant_access.rb +56 -110
- data/lib/samagotchi/web/participant_review.rb +45 -2
- data/lib/samagotchi/web/participant_stream_filter.rb +118 -17
- data/lib/samagotchi/web/participant_view.rb +19 -14
- data/lib/samagotchi/web/participants_admin.rb +129 -0
- data/lib/samagotchi/web/public/app.js +60 -5
- data/lib/samagotchi/web/public/chip_popover.js +10 -3
- data/lib/samagotchi/web/public/data.js +10 -2
- data/lib/samagotchi/web/public/delivery_line.js +79 -15
- data/lib/samagotchi/web/public/index.html +26 -0
- data/lib/samagotchi/web/public/info_bar.js +7 -3
- data/lib/samagotchi/web/public/info_bar_model.js +7 -1
- data/lib/samagotchi/web/public/live_turn.js +3 -0
- data/lib/samagotchi/web/public/participant.html +58 -5
- data/lib/samagotchi/web/public/participant.js +172 -25
- data/lib/samagotchi/web/public/participant_model.js +89 -24
- data/lib/samagotchi/web/public/participant_names.js +37 -5
- data/lib/samagotchi/web/public/participants_panel.js +86 -18
- data/lib/samagotchi/web/public/question_cards.js +1 -0
- data/lib/samagotchi/web/public/review_apply.js +34 -0
- data/lib/samagotchi/web/public/review_start.js +101 -0
- data/lib/samagotchi/web/public/send_turn.js +25 -1
- data/lib/samagotchi/web/public/session_actions.js +44 -3
- data/lib/samagotchi/web/public/session_state.js +3 -0
- data/lib/samagotchi/web/public/start_page.js +38 -2
- data/lib/samagotchi/web/public/stream_handlers.js +10 -0
- data/lib/samagotchi/web/public/turn_html.js +2 -1
- data/lib/samagotchi/web/public/turn_view.js +5 -0
- data/lib/samagotchi/web/session_summary.rb +3 -0
- data/lib/samagotchi/worker.rb +44 -27
- data/lib/samagotchi/worker_inbound.rb +12 -9
- data/lib/samagotchi/working_set.rb +3 -2
- metadata +11 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3f63eb88d80157a6b77f733031284c1a4e376528356f8d099ffe356b4cd7e6c1
|
|
4
|
+
data.tar.gz: eab92f98b12468b5c882e5ee80524fdf151a1b479fd232ea54f8318bf952c2e3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c5df88fe266c32153072be45890e6c12f54bcab0492d367cabc0f0066e6dec41bfc8d54e800f08c610979c3ec66131f7082dbc8691ea3854498ce603f553ffec
|
|
7
|
+
data.tar.gz: e324e458bc5801eec9bc0f170ab5b839fbc4633ca33ce026566cac121deed90b53149a1052a9ec4607f67a7e3d284b4201c08309b5e8eeb1181bd1ff3ec65e31
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,160 @@ and commands may change between minor versions. How releases are made:
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.108.0] - 2026-10-24
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- A project's memories copied from another machine or path (their folder keyed by the old path's hash) are no
|
|
16
|
+
longer missing without a word: when this project's folder is missing or empty and one of the same name with
|
|
17
|
+
another hash exists, chi warns at session start (`memory>`) and under a project `memory_read` miss, naming both
|
|
18
|
+
folders and the `mv` that fixes it. See docs/memory.md (Moving memories to another machine).
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- `chi send --wait` and `chi answer` left with the turn still running (`--timeout`, Ctrl-C) offer the command that waits
|
|
23
|
+
again: `chi send --wait --turn T ID`, or `chi send --wait ID` when the turn's id isn't known (with the attach that
|
|
24
|
+
shows it); under `--remote NAME` both carry it.
|
|
25
|
+
- `chi send --wait` into a running turn is refused with what to do next: `--queue` runs the message after it,
|
|
26
|
+
`chi send --wait ID` waits for it, `chi --attach ID` shows it (under `--remote NAME` the attach, no longer "open it
|
|
27
|
+
in chi web").
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- `chi -p … --non-interactive` prints the load warnings (a guardrail or plugin that failed to load, the orphaned
|
|
32
|
+
project memory folder) on stderr as `<label>> …` lines; it used to drop them.
|
|
33
|
+
- cost-cap 0.2.3, work-cap 0.1.3: a session's state file that doesn't read (not JSON) no longer drops its `/cap` or
|
|
34
|
+
`/workcap` cap silently: it is logged and you get one notice naming the file and the cap used instead (the
|
|
35
|
+
config's), and the next `/cap`, `/workcap` or Continue that writes over it says so instead of replacing it silently.
|
|
36
|
+
- `chi send --wait -m` to an idle session (and `--new --wait`) waits for its message's own turn: a worker that dies
|
|
37
|
+
in that turn ends the wait `canceled` (`worker_stopped`) once the next worker settles it, instead of sitting to
|
|
38
|
+
the timeout; the `turn_id` it reports was already that turn's.
|
|
39
|
+
- A `delegate` follow-up's wait (or delegate report) to an idle child no longer takes the child's previous turn's
|
|
40
|
+
reply for its own when it looks just as the follow-up's own turn starts.
|
|
41
|
+
- A scheduled job that dies of an error outside Ruby's StandardError (a stack overflow, a job calling `exit`) no
|
|
42
|
+
longer cuts chi's shutdown short: the rest of it (plugin tasks, MCP servers) still stops.
|
|
43
|
+
|
|
44
|
+
Update with `chi update` (the system bundle 0.108.0, the cost-cap bundle 0.2.3, the work-cap bundle 0.1.3), then restart `chi web`.
|
|
45
|
+
|
|
46
|
+
## [0.107.0] - 2026-10-24
|
|
47
|
+
|
|
48
|
+
### Added
|
|
49
|
+
|
|
50
|
+
- Review sessions for participants (experimental). `chi review docs/plan.md …` starts a session made to be shared:
|
|
51
|
+
its subjects are project files (a secret such as `.env` or a key, a file outside the project or chi's own is
|
|
52
|
+
refused with one message), it starts idle, and it prints the `chi share` line to invite someone. `chi review --from
|
|
53
|
+
ID` starts a review of a working session from its recap, never its transcript; that session keeps running,
|
|
54
|
+
unarchived. Both work with `chi --remote NAME` (`--dir` there). On the web, the start page's `review…` chip takes
|
|
55
|
+
the files, and the participants panel's **Start a review of it** starts a review of the session. Sharing a session that isn't a review
|
|
56
|
+
session still works, with a warning naming how many tool outputs its history holds (`chi share`, the Invite
|
|
57
|
+
dialog). Participants read the subjects as the **Plan**: rendered beside the thread on a wide screen, a Plan |
|
|
58
|
+
Thread toggle on a phone, checked again on every read and redacted. Under each participant's review turn the owner
|
|
59
|
+
gets **Apply**: it puts an owner's turn in the composer, not sent, that quotes the proposal line by line, so the
|
|
60
|
+
participant's words never reach the model as the owner's instruction; sent while a turn runs, it queues as the
|
|
61
|
+
owner's own turn (the delivery line says so; during the owner's own turn Esc steers it in instead), never merged into the running one. Plugins get `ctx.sessions.review(subjects:,
|
|
62
|
+
seed:)`. The coordinator bundle (0.6.0) adds an experimental sprint review: `/coordinate review [handoff]` opens a
|
|
63
|
+
review of the coordinator session seeded with its handoff, where a participant's new context gets a hand-off diff
|
|
64
|
+
and the owner's Apply updates the handoff. See [docs/cli.md](docs/cli.md#review-sessions) and
|
|
65
|
+
[docs/sessions.md](docs/sessions.md#review-sessions).
|
|
66
|
+
|
|
67
|
+
### Changed
|
|
68
|
+
|
|
69
|
+
- `chi`'s run flags take their whole name only, as the subcommands' flags always have: an abbreviation such as
|
|
70
|
+
`--verb` or `--mem x` is now an invalid option (exit 1) instead of `--verbose` / `--memory x`. Under `chi --remote`,
|
|
71
|
+
an abbreviated local-only flag could slip past the "runs on this machine only" check and be dropped silently.
|
|
72
|
+
|
|
73
|
+
### Fixed
|
|
74
|
+
|
|
75
|
+
- The owner's Continue on the step-limit offer of a participant whose turn is refused since (revoked, expired, at
|
|
76
|
+
their cap) ends the offer as a Stop does, with a notice: no "resumed" for a turn that never runs, and idle exit,
|
|
77
|
+
`/exit` and that participant's queued turns no longer wait on an offer nobody can answer.
|
|
78
|
+
- A participant's queued turn the worker refuses (revoked, expired, at their cap) ends their `chi send --wait` (and
|
|
79
|
+
chi web's participant wait) at once: status `refused`, exit 1, `cancel_reason` `revoked` or `cap_reached`, instead
|
|
80
|
+
of waiting to the timeout (none by default).
|
|
81
|
+
- The owner's input while a participant's review turn runs queues as the owner's own turn after it, from every
|
|
82
|
+
client (the web, the attached TUI, `chi send` without `--queue`), never steered or cut into their read-only turn;
|
|
83
|
+
each says it was queued (the web's delivery line, the TUI's note, `chi send`'s line), and `chi send --wait` waits
|
|
84
|
+
for that turn's own answer.
|
|
85
|
+
- Participants: an agent participant's `chi send --wait` no longer reads its reply unfiltered: the reply is
|
|
86
|
+
redacted as the page shows it (secret shapes, the owner's token, host paths, no thinking or tool calls), and a
|
|
87
|
+
failed turn says "this turn failed: the session's owner can see why", never the error's own words.
|
|
88
|
+
- Participants: a secret on a long streamed line (`password: …`, an `Authorization:` header, an `sk-…` key, a
|
|
89
|
+
private key's BEGIN line) is no longer sent unredacted when the participant's stream cuts the line at 400
|
|
90
|
+
characters; a line with no space for 1600 characters ends in `[redacted]`. A private key block that begins on the
|
|
91
|
+
line where another one ends is withheld too.
|
|
92
|
+
- Participants: a participant's link opened on chi web's own machine (or `chi share` on a chi web not on the LAN,
|
|
93
|
+
whose link opens on that machine only) is the participant, no longer the owner: a `chp_` token is the participant's
|
|
94
|
+
from any peer, an ended one is refused, and the owner's browser that opened a participant link is the owner's again
|
|
95
|
+
once that link ends.
|
|
96
|
+
- Participants: each refusal has one wording wherever it is met: a participant's `chi --remote` refused an
|
|
97
|
+
owner's command says "a participant can't do that: only the session's owner can", as chi web does, and an ended
|
|
98
|
+
link's page says whether it was revoked or expired.
|
|
99
|
+
- Participants: chi web and `chi share` revoking (inviting, changing a cap) at the same moment can no longer leave
|
|
100
|
+
`participants.json` reading a revoked participant as active, so the worker refuses their queued turns: the file
|
|
101
|
+
is written under the grants store's lock.
|
|
102
|
+
- Participants: a grants store (`web-grants/grants.json`) that doesn't read is no longer overwritten by the next
|
|
103
|
+
invite, which kept only the new grant and dropped every other link's; `chi share` (exit 1) and chi web refuse invite,
|
|
104
|
+
ls, cap and revoke with one line naming the file, and a grant row without its digest or expiry no longer
|
|
105
|
+
makes every participant request a 500.
|
|
106
|
+
- Participants: revoking a participant (or their link expiring) ends their `chi send --wait` already waiting at chi
|
|
107
|
+
web, refused as their link's end, instead of handing them the reply that came within the next 30 s.
|
|
108
|
+
- Participants: an agent participant's `chi send --wait` no longer gets the owner's wait state from chi web (the
|
|
109
|
+
owner's newest reply file, message count, pending question, running turn), only what waiting on its own turn needs.
|
|
110
|
+
- Participants: a reply that opens a private key block and never closes it is withheld to that turn's end, no
|
|
111
|
+
longer hiding every later turn's reply from the participant's stream until they reload.
|
|
112
|
+
- Participants: two of a participant's turns sent at once while no worker runs the session can no longer both pass
|
|
113
|
+
their queue limit.
|
|
114
|
+
- `chi --remote NAME` words a value the remote's chi web refuses (`sessions list --cwd rel/dir`, `note --source " "`)
|
|
115
|
+
as one line naming the command, exit 2, instead of a Ruby backtrace; its other refusals are one line, exit 1.
|
|
116
|
+
- A remote whose address answers something other than HTTP (another server on that port, an SSH banner), or a
|
|
117
|
+
request that can't be written in time, is the one "remote NAME (URL): …" failure line, exit 1 (`chi --remote`,
|
|
118
|
+
`chi remote add`'s check; the attached TUI reads it as unreachable), instead of a Ruby backtrace.
|
|
119
|
+
- The attached TUI on a remote whose host went away (no answer at all, not a refused connection) gives up each
|
|
120
|
+
stream reconnect after 5 s, so it ends within about two minutes instead of about seventeen.
|
|
121
|
+
- `chi --remote NAME context push` with a name that isn't a source's (`a/b`, `Bad Name`) gives the name rule, as
|
|
122
|
+
it does locally, instead of "too old for this command … update chi there" or a URI error.
|
|
123
|
+
- A remote at an IPv6 address (`chi remote add box http://[::1]:4567`) connects: its commands and its attached
|
|
124
|
+
TUI's stream used the bracketed host as a name to look up ("can't find host").
|
|
125
|
+
- Two `chi remote add` / `chi remote rm` at once keep each other's change: `remotes.yml` is rewritten under a lock.
|
|
126
|
+
- `chi --help` and `chi remote --help` list `review` (and `share`) among the commands `chi --remote NAME` runs, and
|
|
127
|
+
`-p` says it stays in the session (the attached TUI by default, the REPL with `--no-shared`), not "in the REPL".
|
|
128
|
+
- The participant page sends a comment once: a second Enter (or Send) while it is being posted no longer sends it
|
|
129
|
+
again as a second review turn.
|
|
130
|
+
- The participant page that can't reach chi web (a network blip, a 5xx) says "Can't reach the session — retrying…" and
|
|
131
|
+
reads again with a backoff (3 s, doubling, at most 30 s), at open and after a turn boundary, instead of a blank or
|
|
132
|
+
silently stale page; a comment that can't be sent says so and stays in the box.
|
|
133
|
+
- The participant page's live reply no longer loses the text that streams while the page re-reads the review (another
|
|
134
|
+
participant's comment queuing mid-turn left a hole until the turn ended): it streams again from the re-read's
|
|
135
|
+
cursor, and an older re-read answering last no longer overwrites a newer one.
|
|
136
|
+
- The participant page shows chi web's own words for a refused comment or take-back ("A participant can't do that:
|
|
137
|
+
only the session's owner can.", too many turns, the spending cap) and for an ended link (revoked or expired),
|
|
138
|
+
instead of its own second wording.
|
|
139
|
+
- The participant page keeps a tool list the reader opened or closed as they left it when the thread is read again or
|
|
140
|
+
the reply streams, and no longer redraws the messages it already shows (a text selection in them stays).
|
|
141
|
+
- The participant page on a phone is the visible viewport's height (100dvh), so its composer sits above the browser's
|
|
142
|
+
URL bar instead of under it.
|
|
143
|
+
- The participant page no longer adds the next turn's first words to the reply of the turn that just ended while it
|
|
144
|
+
reads the review again (a queued turn starting): the live reply waits for the re-read, whose stream replays them.
|
|
145
|
+
- The web info bar's session clock, speed and ctx chips keep one width while a turn runs (tabular digits in a
|
|
146
|
+
reserved width), and the speed and the clock show from the session's start as muted placeholders: a tick or the
|
|
147
|
+
first generation's speed no longer widens the line and wraps a chip onto a new row.
|
|
148
|
+
- The participants panel drops an Invite, Cap or Revoke answered after it closed or another session opened: a
|
|
149
|
+
one-time link no longer shows the next time a panel opens, under another session, and a failed list no longer
|
|
150
|
+
keeps the previous session's working-session warning.
|
|
151
|
+
- The participants panel's **Start a review of it** closes the panel only once the review is made; a refusal stays in
|
|
152
|
+
the open panel, said there, instead of the panel closing on a toast.
|
|
153
|
+
- The participants panel's Revoke asks first (`Revoke Alex's link? …`), as a delete does: it can't be undone, and on
|
|
154
|
+
a phone's sheet it sat one tap away beside Cap.
|
|
155
|
+
- A participant's older prompt drawn before the participants list came (no name saved with it) no longer says
|
|
156
|
+
"a participant" until the page is reloaded: its badge takes the name when the list comes.
|
|
157
|
+
- The start page's `review…` chip can name a file with a space in its path: in double quotes (`"docs/my plan.md"`);
|
|
158
|
+
an unclosed quote is said in its form. A file Start refuses (a secret, outside the project) is said in that form,
|
|
159
|
+
opened again at the files chosen, instead of a blocking alert.
|
|
160
|
+
- The participants panel no longer closes on its own Cap or Invite another (the button, redrawn away by its own
|
|
161
|
+
click, read as a click outside the panel).
|
|
162
|
+
|
|
163
|
+
Update with `chi update` (the system bundle 0.107.0, the coordinator bundle 0.6.0, the dev profile 0.7.2), then restart `chi web`.
|
|
164
|
+
|
|
11
165
|
## [0.106.0] - 2026-10-23
|
|
12
166
|
|
|
13
167
|
### Added
|
|
@@ -4011,7 +4165,9 @@ and long-lived sessions.
|
|
|
4011
4165
|
- A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
|
|
4012
4166
|
hotkey panel that send selected text or the clipboard to your sessions.
|
|
4013
4167
|
|
|
4014
|
-
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.
|
|
4168
|
+
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.108.0...HEAD
|
|
4169
|
+
[0.108.0]: https://github.com/dm1try/samagotchi/compare/v0.107.0...v0.108.0
|
|
4170
|
+
[0.107.0]: https://github.com/dm1try/samagotchi/compare/v0.106.0...v0.107.0
|
|
4015
4171
|
[0.106.0]: https://github.com/dm1try/samagotchi/compare/v0.105.0...v0.106.0
|
|
4016
4172
|
[0.105.0]: https://github.com/dm1try/samagotchi/compare/v0.104.0...v0.105.0
|
|
4017
4173
|
[0.104.0]: https://github.com/dm1try/samagotchi/compare/v0.103.0...v0.104.0
|
data/bin/chi
CHANGED
|
@@ -54,6 +54,8 @@ SUBCOMMANDS = {
|
|
|
54
54
|
"answer" => ["samagotchi/answer_command", "AnswerCommand"],
|
|
55
55
|
# invite participants to review a session, list them, revoke them
|
|
56
56
|
"share" => ["samagotchi/share_command", "ShareCommand"],
|
|
57
|
+
# a review session for files, or of a session (--from, --dir, --model)
|
|
58
|
+
"review" => ["samagotchi/review_command", "ReviewCommand"],
|
|
57
59
|
# find a model server and write config.yml (--model, --name)
|
|
58
60
|
"bootstrap" => ["samagotchi/bootstrap_command", "BootstrapCommand"],
|
|
59
61
|
# bring an installed chi up to date
|
data/docs/architecture.md
CHANGED
|
@@ -201,7 +201,8 @@ These are the Engine's raw events. The worker's SSE projection omits `call` from
|
|
|
201
201
|
`:tool_call_started` and `result` from `:turn_completed`; the answer remains in
|
|
202
202
|
`turn_summary.output`.
|
|
203
203
|
|
|
204
|
-
A turn started with `origin:` carries it on each of these (and `:turn_failed`)
|
|
204
|
+
A turn started with `origin:` (a `TurnOrigin`, or its Hash) carries it on each of these (and `:turn_failed`), as
|
|
205
|
+
the Hash `{client_id:, enqueued_id:, author:}` with only the fields set (`TurnOrigin#to_h`).
|
|
205
206
|
|
|
206
207
|
Every event is a `Hash` with a `:type` symbol key; the sink must not raise (the Engine
|
|
207
208
|
rescues sink errors). A new UI (web, API) supplies its own `on_event` and
|
|
@@ -548,6 +549,8 @@ model: [internals/remote-sessions.md](internals/remote-sessions.md).
|
|
|
548
549
|
| `POST /api/sessions/:id/turn` (`no_interrupt`) | owner | `no_interrupt: true`: the turn runs with the raised iteration limit (`SessionManager.deliver_turn(no_interrupt:)`, to the Bridge or the input file) | as before | |
|
|
549
550
|
| `POST /api/sessions/:id/command` (its 501) | owner | | | 501 `not_supported` for a builtin the worker lacks adds `command` (`"/title"`) and `message` |
|
|
550
551
|
| `GET`/`POST`/`DELETE /api/sessions/:id/participants`, `GET`/`DELETE /api/participants` | owner | `chi share`'s (`name`, `kind`, `ttl`; `?id=p_…`) | the grants, never a token but POST's own once ([internals/participants.md](internals/participants.md)) | 400 `invalid_participant`; 404 |
|
|
552
|
+
| `GET /api/sessions/:id/apply[?turn=T]` | owner | `turn`: a turn id or a prefix (none: the latest review turn with a reply) | 200 `{text, turn_id, author, author_name}` (`ReviewApply`): the owner's Apply of a participant's review turn, for the composer, never sent ([internals/participants.md](internals/participants.md)) | 404 `not_found`; 400 `not_review_turn`; 409 `no_reply` |
|
|
553
|
+
| `POST /api/sessions` (`review`) | owner | `review: {subjects: [project files], from: a session id}`; never with `prompt`, `preview`, `idle` or `continues` (a `from` takes the source's folder and setup) | 201 as a create's: an idle review session (`ReviewStart.start`, or `ReviewStart.fork` with `from`) | 400 `invalid_review` (`ReviewStart::Refused`'s words: a subject, a gone folder); with `from` also 404 `not_found`, 400 `ambiguous_id` |
|
|
551
554
|
|
|
552
555
|
`GET /api/info` names the feature `session_commands` (`SessionAccessRoutes::FEATURE`): `SessionAccess::Remote` asks it
|
|
553
556
|
once before its first request, and a chi web without it (or `serve_static`'s 404 for a path no route takes) is "remote NAME runs chi X, too
|
|
@@ -692,6 +695,8 @@ A map of the parts the overview above doesn't name (paths under `lib/samagotchi/
|
|
|
692
695
|
| Attached context | `context_sources.rb` (`ContextSources`), `context_poller.rb`, `context_absorber.rb`, `peer_sources.rb` (`PeerSources`: the sources peer bundles declare) | External sources a session keeps fresh, and the peer loop's wakes ([context.md](context.md)). |
|
|
693
696
|
| LLM context strategies | `llm_context_strategy.rb`, `llm_context_view.rb`, `llm_context_stale.rb`, `llm_context_forget.rb` | What a request sends of the history (none, stale, forget) ([internals/llm-context-forget.md](internals/llm-context-forget.md)). |
|
|
694
697
|
| Session chains | `session_continue.rb` (`SessionContinue`), `session_chain.rb` (`SessionChain`) | A session that continues an earlier one. `SessionChain.opener_for` gives the text web `continue →` prefills in the new link: the previous link's own first typed message when that link continues one itself, else nil (the 201's `opener`; not saved) ([sessions.md](sessions.md)). |
|
|
698
|
+
| Review sessions | `review_start.rb` (`ReviewStart`), `session_review.rb` (`SessionReview`, the session's `review` field), `review_command.rb` (`ReviewCommand`, `chi review`), `review_apply.rb` (`ReviewApply`) | A session made to be shared: idle, its subjects project files checked by `Guardrails::ReviewPaths`, a fork of a working session from its recap (`--from`); Apply quotes a participant's review turn into the owner's composer ([sessions.md](sessions.md#review-sessions), [internals/participants.md](internals/participants.md)). |
|
|
699
|
+
| Participants | `participants.rb` (`Participants`, the `participants.json` sidecar), `share_command.rb` (`ShareCommand`, `chi share`), `session_participants.rb`, `participants_watch.rb`, `web/grants.rb` (`Web::Grants`), `web/principal.rb` (`Web::Principal`), `web/participant_access.rb` (`Web::ParticipantAccess`: the participant's gate and routes), `web/participants_admin.rb` (`Web::ParticipantsAdmin`: the owner's invite, list, revoke, cap and Apply routes), `web/participant_review.rb` + `web/participant_view.rb` (`Web::ParticipantReview`, `Web::ParticipantView`), `web/participant_stream_filter.rb`, `guardrails/review_boundary.rb` + `guardrails/review_paths.rb` | The people or agents an owner invites to review one session through chi web: their grants and gate, what they read, their read-only review turns ([internals/participants.md](internals/participants.md), [guardrails.md](guardrails.md)). |
|
|
695
700
|
| Analytics | `analytics_file.rb` (`AnalyticsFile`), `session_metrics/totals.rb` (`SessionMetrics::Totals`), `session_metrics/model_segment.rb` (`SessionMetrics::ModelSegment`) | A session's `analytics.json` (`SessionMetrics#persist`); the sums over its turn records (`Totals`: tokens, cost, speeds, kept as running counters) and their split per model after a `/model` switch (`ModelSegment`: a run of turns on one model, the `model_segments` rows); `session_metrics/cost.rb` (`SessionMetrics::Cost`): the spend `ctx.cost` reads; `session_metrics/usage.rb` (`SessionMetrics::Usage`): the work `ctx.usage` reads. `session_metrics/side_totals.rb` (`SessionMetrics::SideTotals`) and `side_usage.rb` (`SideUsage`): the side requests' sums, apart from the turns'. |
|
|
696
701
|
| Images / vision | `image_store.rb` (`ImageStore`), `vision_support.rb`, `vision_context.rb` | Stored prompt images and whether a model takes them. |
|
|
697
702
|
| Toolkits | `toolkit.rb` (`Toolkit`), `toolkit_binding.rb` (`ToolkitBinding`, the Engine's seam), `toolkit_command.rb` (`/toolkit`), `tools/registry.rb` (`Registry#toolkit`, `#left_out_names`), `web/public/toolkit_chip.js` | Which registered tools a session's prompts declare: saved in the session, resolved at a turn's start (`Engine#apply_toolkit!`), named in the prompt's not-loaded line, a kit's prompt text after the appended files; `chi.toolkit` and `ctx.use_toolkit` for plugins ([configuration.md](configuration.md#toolkits)). |
|
data/docs/cli.md
CHANGED
|
@@ -18,12 +18,14 @@
|
|
|
18
18
|
- `chi web --no-web-working-set` — turns the stage view's working set off (on by default; `--web-working-set` turns a config's `web.working_set: false` back on): what each turn brought into the model's context, in the stage and under each finished turn; `?working_set=1|0` on the page URL overrides it (see [The working set](#the-working-set))
|
|
19
19
|
- `chi sessions list|stop|restart|archive|unarchive|delete|prune|clean|stats` — manage persisted sessions; `list` shows this git project's, `list --scope=all` every one, a delegated session with `↳ <parent>`, `list --archived` the archived ones too (see [Sessions](sessions.md)). `stats ID` prints a session's cost, tokens and progress, and its own settings (`--set`; a `settings:` line), from a live worker's `GET stats`, else its saved `analytics.json` (`--format json` one object, with `settings`), without running a model turn. A usage error (an unknown subcommand or flag, a flag missing its value, `stop`, `restart` or `delete` with no ids, a bad `list --format` or `--scope`, a `--days`, `--keep` or `--limit` that isn't a whole number) exits 2; an unknown or refused session exits 1
|
|
20
20
|
- `chi note [--source NAME] [-m TEXT] (ID|PREFIX)... | --all` — add a context note (TEXT or stdin) to sessions: background the model sees on its next turn; it starts no turn (see [Sessions: Context notes](sessions.md#context-notes))
|
|
21
|
+
- `chi broadcast [-m TEXT] [--all] [--dry-run]`, `chi broadcast log [--last N] [--format json]`, `chi broadcast deliver BROADCAST_ID (ID|PREFIX)...` — share one piece of information (TEXT or stdin) with every session it may concern, as a context note from `broadcast` (see [Broadcast](broadcast.md))
|
|
21
22
|
- `chi context <add|push|ls|show|refresh|rm|mute|unmute> …` — attach live external text to sessions (a command chi runs every so often, or text pushed in); the model gets a short note when it changes and reads it with `context_read`; `chi context add <URL>` attaches a GitHub PR with the github-pr bundle (see [Attached context](context.md))
|
|
22
23
|
- `chi send [-m TEXT] [--image PATH]... [--cut | --queue] (ID|PREFIX)...` — send a message to sessions as if typed there: a turn starts (or a running one takes it at its next step; `--cut` asks it to cut long thinking for it; `--queue` runs it as a turn of its own after the running one, and `--queue --wait` waits for that turn's own answer); piped stdin goes above `-m` as quoted context, and `--image` attaches images (see [Sessions: Sending a message](sessions.md#sending-a-message)); `--new` starts a session with it instead (`--new --continues ID|last:ID` the next link of that session's chain, see [Session chains](sessions.md#session-chains)), and `--wait` prints the answer (`--wait ID` with no message waits for the next reply without sending — to an idle session whose last turn already ended, that ended turn's reply or end comes back; `--wait --turn T ID` with no message waits for exactly turn T, whether running, queued or last ended; `--format json` includes its `turn_id` when known, for waiting again after exit 3 or 4; exit 3 a question waits, its options on stderr; exit 4 `--timeout` passed with the turn still running; see [Starting a session](sessions.md#starting-a-session))
|
|
23
24
|
- `chi answer ID --question QID (--option N|LABEL)... [--text T] [--timeout S] [--format json]` (or `--dismiss`) — answer the question a session waits on (the one `chi send --wait` exited 3 with), then wait and print what comes next as `chi send --wait` does: the reply (0), the next question (3), still running after `--timeout` (4). `--option` is 1-based or the label, repeated on a multi-select question; `--text` is free text, or a Deny's reason; `--dismiss` leaves it unanswered and the model finishes its reply. A question no longer open (the web answered first) isn't answered again: it waits for that turn's reply. An option the question doesn't offer exits 2; a worker that is gone exits 1 (`send the task again: chi send --wait -m "…" ID`). What a parent may pick follows `guardrails.parent_answers`, or the session's own `parent_answers` when stricter (`soft`, the default: a deny, a plain answer, Continue and "Allow once"; `standard`: no allow; `hard`: no Continue either), never a wider scope or an allow on chi's own config; a refused pick exits 1 with what to answer instead (a convention, not a security boundary; see [Guardrails](guardrails.md#answers-from-a-parent-agent))
|
|
24
25
|
- `chi remote add NAME URL [--token-file F | --token-stdin]`, `chi remote ls`, `chi remote rm NAME` — the other machines whose chi web the session commands reach with `--remote NAME` (see [Remote sessions](#remote-sessions))
|
|
26
|
+
- `chi review FILE… [--dir DIR] [--model M]`, `chi review --from ID [FILE…]` — start a review session to invite participants to: for files of a project, or as a review of a working session that starts from its recap, not its transcript (see [Review sessions](#review-sessions))
|
|
25
27
|
- `chi share ID --name NAME [--agent] [--expires 3d] [--cap 2]`, `chi share ls [ID]`, `chi share cap ID P DOLLARS`, `chi share revoke ID [P] | --all` — invite a participant to review a session through chi web on the LAN (a link of their own, 7 days, a $2 spending cap), list them, change one's cap, revoke them (see [Participants](#participants))
|
|
26
|
-
- `chi --remote NAME send|answer|note|context|sessions|share …` (or `CHI_REMOTE=NAME`) — those commands on that machine's sessions, through its chi web (see [Remote sessions](#remote-sessions))
|
|
28
|
+
- `chi --remote NAME send|answer|note|context|sessions|share|review …` (or `CHI_REMOTE=NAME`) — those commands on that machine's sessions, through its chi web (see [Remote sessions](#remote-sessions))
|
|
27
29
|
- `chi --remote NAME --attach ID` (`--resume ID`, `--dir DIR [-p TEXT]`) — the attached TUI on a session of that machine (see [Remote sessions](#remote-sessions))
|
|
28
30
|
- `chi memory ls [--scope project|system] [--format text|json]` — the memories of both scopes (this folder's project first), one row each: scope, name, size, date, `from <bundle>`, the index description and its model overlays; `chi memory rm NAME [--scope project|system]` removes one as `memory_write` with `remove: true` does: its file and model overlays go to the bundle trash (the line names the dir; `chi bundle trash` lists and empties it) and its index line is dropped. NAME in both scopes without `--scope` exits 2 naming both; a missing NAME or a bundle's memory (`chi bundle uninstall` removes it) exits 1 (see [Memory: Listing and removing from the shell](memory.md#listing-and-removing-from-the-shell))
|
|
29
31
|
- `chi desktop install|upgrade|uninstall|status` — the macOS "Send to chi" helper: a Service and a ⌃⌥⌘N hotkey that send text or images to a session (live, stopped or new) as a message (⏎, a turn runs) or a context note (⌘⏎) (see [Desktop helper](desktop.md))
|
|
@@ -273,6 +275,8 @@ bundles or the helper can be updated.
|
|
|
273
275
|
|
|
274
276
|
Samagotchi exposes one flag that feeds a prompt (`-p`, `--prompt`) and one that
|
|
275
277
|
controls exit behavior (`--non-interactive`); `--resume` composes with both.
|
|
278
|
+
Every flag takes its whole name, as the subcommands' flags do: an abbreviation (`--verb`, `--mem x`) is an
|
|
279
|
+
invalid option (exit 1).
|
|
276
280
|
|
|
277
281
|
| Flag | Purpose |
|
|
278
282
|
|------|---------|
|
|
@@ -1287,8 +1291,8 @@ chi share revoke --all # every link to every session
|
|
|
1287
1291
|
participant; opening it sets their browser's cookie, as the owner's LAN link does. `--agent` marks an agent
|
|
1288
1292
|
participant, which uses the token as `Authorization: Bearer` (e.g. `chi --remote` with that token).
|
|
1289
1293
|
- It works for 7 days (`--expires Nh` or `Nd`, 1 hour to 30 days; `web.participants.expires`), until it is revoked,
|
|
1290
|
-
or until the session is archived or deleted. A revoked
|
|
1291
|
-
|
|
1294
|
+
or until the session is archived or deleted. A revoked link shows "This review link was revoked by the session's
|
|
1295
|
+
owner.", an expired one "This review link expired: ask the session's owner for a new one."
|
|
1292
1296
|
- **Review turns (experimental).** A participant can ask questions and comment through chi web's `POST …/turn`: each
|
|
1293
1297
|
message is a turn of its own, queued behind the running one, that the model answers **read-only**. It may read the
|
|
1294
1298
|
project (never its secret files or chi's own), and proposes changes as text or a diff for you to apply; it can't
|
|
@@ -1309,8 +1313,15 @@ chi share revoke --all # every link to every session
|
|
|
1309
1313
|
memories, context notes, plugin and delegate input, cards, command output, the working set, cost, timing or host
|
|
1310
1314
|
paths; secret-shaped text (API keys, tokens, private keys) is redacted too. An agent participant can wait on its
|
|
1311
1315
|
turns' replies through the API, or with chi: its link's token in a remote, `chi --remote NAME send --wait -m "…" ID`,
|
|
1312
|
-
or `send -m`, which prints the turn id `--wait --turn T` takes
|
|
1316
|
+
or `send -m`, which prints the turn id `--wait --turn T` takes; that wait exits 3 with `waiting_for_owner` while
|
|
1317
|
+
their turn waits on you (a question, an approval, your step-limit Continue) and 1 with `refused` (`cancel_reason`
|
|
1318
|
+
`revoked` or `cap_reached`) when their queued turn is refused before it runs. They can't reach anything else: every other route answers them 403, another
|
|
1313
1319
|
session 404.
|
|
1320
|
+
- **Apply.** Under each of a participant's review turns that has a reply, your page shows **Apply** (full width on a
|
|
1321
|
+
phone): it puts an Apply turn in your composer, not sent, `Apply this proposal from Alex's review turn 3f2a1c0d, as
|
|
1322
|
+
far as it fits the plan. It's quoted: …` and the reply quoted line by line. Read it, edit it, send it: the
|
|
1323
|
+
participant's text reaches the model as a quote under your line, never as your instruction. Don't send "do what
|
|
1324
|
+
Alex said" instead: that hands their words your tools.
|
|
1314
1325
|
- **Your side of it.** In the session bar, the participants chip (`invite` while nobody has a link, then `2
|
|
1315
1326
|
participants`, with a green dot while one is on their page) opens the panel: each participant's state, last visit,
|
|
1316
1327
|
spend against their cap, turns and expiry, with **Cap** and **Revoke** (revoking ends their page and stream and
|
|
@@ -1320,7 +1331,8 @@ chi share revoke --all # every link to every session
|
|
|
1320
1331
|
shows their name in their colour, live and after a reload.
|
|
1321
1332
|
- `chi --remote studio share …` invites on studio: the link names studio's LAN address. Locally, the link names
|
|
1322
1333
|
this machine's running chi web, on the port it listens on (it records it in `$XDG_STATE_HOME/samagotchi/web.json`;
|
|
1323
|
-
`web.port` when none answers there); without one on the LAN, chi says the link opens on this machine only
|
|
1334
|
+
`web.port` when none answers there); without one on the LAN, chi says the link opens on this machine only (as
|
|
1335
|
+
the participant there too: a participant's link never opens as the owner).
|
|
1324
1336
|
- The owner's own token and the `chi web --new-token` rotation are separate: rotating it ends no participant's link.
|
|
1325
1337
|
- **In the attached terminal.** A participant's message reads `Alex ▸ review> is step 3 right?`, the name in that
|
|
1326
1338
|
participant's own colour (queued, running, and the last exchange when you join); yours stay as they were. A line
|
|
@@ -1333,6 +1345,34 @@ chi share revoke --all # every link to every session
|
|
|
1333
1345
|
a revoke made with them isn't announced again by the `participants>` line. The REPL
|
|
1334
1346
|
(`--no-shared`) answers them with a note: a participant's turns run on the session's worker.
|
|
1335
1347
|
|
|
1348
|
+
#### Review sessions
|
|
1349
|
+
|
|
1350
|
+
Share a **review session**, not a working one: a working session's history holds its tool outputs, and a review
|
|
1351
|
+
turn's model may quote them. `chi review` starts one, idle:
|
|
1352
|
+
|
|
1353
|
+
```sh
|
|
1354
|
+
chi review docs/plan.md docs/spec.md # a fresh session in this project for those files
|
|
1355
|
+
# 4b7e9a01-… review · docs/plan.md, docs/spec.md · idle
|
|
1356
|
+
# open it: chi --attach 4b7e9a01 or the web
|
|
1357
|
+
# invite someone: chi share 4b7e9a01 --name NAME
|
|
1358
|
+
chi review --from 3f9a1c2e docs/plan.md # a review of a working session: its recap, not its transcript
|
|
1359
|
+
```
|
|
1360
|
+
|
|
1361
|
+
- Each FILE is a **subject**: a file in the session's project (the git work tree of its folder, `--dir`, else
|
|
1362
|
+
here), relative to that folder or absolute. A missing file, a folder, a file outside the project, a secret
|
|
1363
|
+
(`.env*`, keys and certificates, credentials files, a git config) or chi's own refuses the review with one line:
|
|
1364
|
+
`chi review: .env can't be a review subject: …`. At most 20.
|
|
1365
|
+
- `--from ID` (an id, a prefix, `last:ID`) starts a **review fork**: a new session in ID's folder, on its model
|
|
1366
|
+
(`--model` to change it) and setup, seeded with a fresh recap of ID (its live worker is asked first, as a continue
|
|
1367
|
+
asks), never its transcript. ID keeps running, unarchived, its delegates its own. Its FILEs are relative to ID's
|
|
1368
|
+
folder.
|
|
1369
|
+
- `chi --remote NAME review … --dir /path/there` starts it on that machine; its chi web checks the files there.
|
|
1370
|
+
- Sharing a working session (one `chi review` didn't start) still works, with a warning on stderr that says how many
|
|
1371
|
+
tool outputs its history holds: `chi share: warning: 3f9a1c2e is a working session, not a review session: a
|
|
1372
|
+
participant's review turn can quote what its history holds (42 tool outputs so far). … (chi review --from 3f9a1c2e
|
|
1373
|
+
starts one)`. On the web the participants panel shows the same words before its Invite, with **Start a review of
|
|
1374
|
+
it**, and again above the link an invite gives.
|
|
1375
|
+
|
|
1336
1376
|
How it works inside (grants, the gate, the routes, the audit trail):
|
|
1337
1377
|
[internals/participants.md](internals/participants.md).
|
|
1338
1378
|
|
data/docs/guardrails.md
CHANGED
|
@@ -304,7 +304,9 @@ Built in, for a [participant's](internals/participants.md#turns) turns only (a
|
|
|
304
304
|
turn whose author is `p:<id>`), in every mode (also with
|
|
305
305
|
`guardrails.enabled: false`) and without the guardrails bundle:
|
|
306
306
|
`Guardrails::ReviewBoundary`, rule `review-boundary`, source `core`. It comes
|
|
307
|
-
|
|
307
|
+
right after the load-failure check (rules that don't load deny every call) and before the hooks' asks, the
|
|
308
|
+
protected paths and the rules (`GuardrailWiring#checks`; a deny is sticky, so no later vote turns it into a
|
|
309
|
+
question), and it
|
|
308
310
|
**denies, never asks**: nobody on the participant's side may approve, and the
|
|
309
311
|
owner's approval would give the participant's text the owner's tools. A
|
|
310
312
|
participant's turn may call only the `review` kit's tools:
|
|
@@ -746,7 +748,11 @@ keep every guardrail. But an owner turn that says "do what Alex said" hands
|
|
|
746
748
|
Alex's text the owner's tools: the model will act on it as the owner asked.
|
|
747
749
|
That is the residual risk, documented rather than engineered away: on a
|
|
748
750
|
session with sensitive tools, quote the proposal you want applied in your own
|
|
749
|
-
turn instead
|
|
751
|
+
turn instead: the web's **Apply** under a review turn does that (it puts your
|
|
752
|
+
line and the reply quoted line by line in your composer, for you to read and
|
|
753
|
+
send; [internals/participants.md](internals/participants.md#apply)). A quote
|
|
754
|
+
lowers the risk, it doesn't remove it: your line still asks the model to act
|
|
755
|
+
on what it quotes, with your tools, so read the quote before you send it.
|
|
750
756
|
|
|
751
757
|
Within its own turn a participant's text can't act: the review boundary
|
|
752
758
|
leaves it reads of the project only, and an injection that convinces the
|
data/docs/internals/engine.md
CHANGED
|
@@ -424,7 +424,7 @@ The turn as the Engine sees it (the KernelLoop has its own `Turn` struct):
|
|
|
424
424
|
Turn = Struct.new(:session, :prompt, :continue, :on_event, :controller, :origin, :started_at, :messages, :ended,
|
|
425
425
|
:settings, :id, :limit, :resumed, :resume_from) do
|
|
426
426
|
def resumed? = resumed ? true : false # a step-limit Continue
|
|
427
|
-
def tag(event) = origin ? event.merge(origin: origin) : event # boundary events carry the origin
|
|
427
|
+
def tag(event) = origin ? event.merge(origin: origin.to_h) : event # boundary events carry the origin's wire Hash
|
|
428
428
|
def elapsed = ... # seconds since started_at
|
|
429
429
|
end
|
|
430
430
|
```
|
|
@@ -519,11 +519,22 @@ Turn events (the main ones; `Events` in `events.rb` keeps the shared sets):
|
|
|
519
519
|
| `:turn_failed` | an error | `{error_class, message, duration_ms, error_kind?, retryable?, host?, summary?}` |
|
|
520
520
|
| `:answer_display` | after the after_turn hooks (observers only, not the sink) | `{display, refs?}` (display a string, or nil; refs the count of the answer's refs, only when a hook attached some) |
|
|
521
521
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
522
|
+
A turn's origin is a `TurnOrigin` (`lib/samagotchi/turn_origin.rb`, a `Data.define` of `client_id`, `enqueued_id`
|
|
523
|
+
and `author`): `SessionInbox.read_input` makes it from an input file's own fields, the wakes, reports and reminders
|
|
524
|
+
make theirs, and `Engine#run_turn`, the Worker and `GuardrailWiring` pass it on. Its readers say what callers ask
|
|
525
|
+
(`#participant?`, `#participant_author`, `#participant_id`, `#source` = `ClientId.origin`, `#human?` =
|
|
526
|
+
`ClientId.human?`). At a boundary it is turned once: `#to_h` writes the wire Hash (only the fields set, as before),
|
|
527
|
+
`TurnOrigin.from` reads a Hash back (symbol or string keys; nil for none). In-process an origin is never nil:
|
|
528
|
+
`TurnOrigin.of` gives `TurnOrigin::NONE` (no sender: an owner's turn with no client) for nil or `{}`, and NONE is
|
|
529
|
+
`run_turn(origin:)`'s default (a Hash is still read as one). NONE has no wire form (`#empty?`; `#wire` is nil, or
|
|
530
|
+
`#to_h` for any other origin), so the events and the hooks' `context` carry no origin for it, as before.
|
|
531
|
+
`LastTurn#author` stays the session file's string; `LastTurn#participant_author` reads it as
|
|
532
|
+
`TurnOrigin#participant_author` does. The boundary events (`:turn_started`, `:turn_completed`, `:turn_canceled`, `:turn_failed`) also carry
|
|
533
|
+
`origin` when the turn was queued with one: `{client_id:, enqueued_id:}`, plus `author:` when someone other than
|
|
534
|
+
the session's owner wrote it (its input file's `author`, stamped by chi web from the request's `Web::Principal`).
|
|
535
|
+
An owner's turn has no `author` anywhere: not in its input file, its origin or the session. `GuardrailWiring#begin_turn`
|
|
536
|
+
gets the origin, and `#turn_author` reads its participant author for the gate's checks; the guardrails `Context`
|
|
537
|
+
(the hooks' `context`) keeps the wire Hash.
|
|
527
538
|
|
|
528
539
|
Where a participant's author (`"p:<id>"`; `Steer.participant_author?`: any author but none or `"owner"`) goes:
|
|
529
540
|
|