samagotchi 0.134.0 → 0.135.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 +49 -1
- data/bin/chi +2 -3
- data/docs/architecture.md +6 -3
- data/docs/cli.md +39 -34
- data/docs/configuration.md +1 -2
- data/docs/guardrails.md +53 -9
- data/docs/internals/engine.md +6 -3
- data/docs/internals/participants.md +1 -1
- data/docs/internals/web-frontend.md +262 -0
- data/docs/sessions.md +2 -0
- data/docs/testing.md +27 -2
- data/lib/samagotchi/bridge.rb +11 -6
- data/lib/samagotchi/bridge_client.rb +8 -5
- data/lib/samagotchi/bundles/system/manifest.yml +1 -1
- data/lib/samagotchi/config.rb +0 -2
- data/lib/samagotchi/engine.rb +69 -12
- data/lib/samagotchi/guardrail_wiring.rb +8 -6
- data/lib/samagotchi/guardrails/approval.rb +41 -5
- data/lib/samagotchi/guardrails/implicit_writers.rb +19 -4
- data/lib/samagotchi/guardrails/pair_boundary.rb +5 -1
- data/lib/samagotchi/guardrails/pair_command.rb +387 -78
- data/lib/samagotchi/guardrails/parent_approvals.rb +12 -15
- data/lib/samagotchi/guardrails/read_only_shell.rb +24 -2
- data/lib/samagotchi/guardrails/shared_git.rb +1 -1
- data/lib/samagotchi/guardrails/shell_git_dirs.rb +1 -1
- data/lib/samagotchi/guardrails/shell_lex.rb +22 -0
- data/lib/samagotchi/guardrails_command.rb +13 -10
- data/lib/samagotchi/llm/chat_loop.rb +4 -3
- data/lib/samagotchi/message_parts.rb +1 -1
- data/lib/samagotchi/revise_anchor.rb +33 -0
- data/lib/samagotchi/session_inbox.rb +20 -12
- data/lib/samagotchi/session_manager.rb +13 -11
- data/lib/samagotchi/system_one/approval_question.rb +21 -16
- data/lib/samagotchi/system_one/self_protect.rb +13 -15
- data/lib/samagotchi/tools/delegate_relay.rb +6 -5
- data/lib/samagotchi/tools/delegate_wait.rb +5 -5
- data/lib/samagotchi/tools/execute.rb +6 -6
- data/lib/samagotchi/tools/output_guardrails.rb +9 -2
- data/lib/samagotchi/tools/read.rb +5 -5
- data/lib/samagotchi/turn_anchors.rb +49 -0
- data/lib/samagotchi/verify_anchor.rb +5 -5
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +2 -14
- data/lib/samagotchi/web/message_display.rb +17 -1
- data/lib/samagotchi/web/participant_access.rb +2 -2
- data/lib/samagotchi/web/participant_view.rb +8 -0
- data/lib/samagotchi/web/public/activity.js +1 -1
- data/lib/samagotchi/web/public/annotations.js +1 -1
- data/lib/samagotchi/web/public/answer_fold.js +3 -3
- data/lib/samagotchi/web/public/app.js +447 -340
- data/lib/samagotchi/web/public/bubbles.js +3 -3
- data/lib/samagotchi/web/public/cards_place.js +4 -2
- data/lib/samagotchi/web/public/composer.css +8 -0
- data/lib/samagotchi/web/public/context_chips.js +9 -6
- data/lib/samagotchi/web/public/data.js +4 -2
- data/lib/samagotchi/web/public/dock_reserve.js +13 -15
- data/lib/samagotchi/web/public/feature_gates.js +33 -0
- data/lib/samagotchi/web/public/history_reveal.js +8 -4
- data/lib/samagotchi/web/public/index.html +38 -9
- data/lib/samagotchi/web/public/latest.js +46 -0
- data/lib/samagotchi/web/public/lists_view.js +15 -13
- data/lib/samagotchi/web/public/live_timing.js +2 -0
- data/lib/samagotchi/web/public/markdown_place.js +1 -1
- data/lib/samagotchi/web/public/method_api.js +14 -0
- data/lib/samagotchi/web/public/notifications.js +4 -11
- data/lib/samagotchi/web/public/participant.js +11 -6
- data/lib/samagotchi/web/public/participant_names.js +1 -1
- data/lib/samagotchi/web/public/participants_panel.js +4 -2
- data/lib/samagotchi/web/public/project_picker.js +18 -22
- data/lib/samagotchi/web/public/question_cards.js +2 -2
- data/lib/samagotchi/web/public/restore.js +2 -0
- data/lib/samagotchi/web/public/revise_stack.js +372 -0
- data/lib/samagotchi/web/public/scope.js +12 -20
- data/lib/samagotchi/web/public/send_turn.js +29 -16
- data/lib/samagotchi/web/public/session_actions.js +15 -14
- data/lib/samagotchi/web/public/session_fields.js +152 -0
- data/lib/samagotchi/web/public/session_payload.js +35 -0
- data/lib/samagotchi/web/public/session_state.js +2 -0
- data/lib/samagotchi/web/public/session_store.js +191 -0
- data/lib/samagotchi/web/public/stage_model.js +4 -4
- data/lib/samagotchi/web/public/stage_view.js +11 -16
- data/lib/samagotchi/web/public/stream_handlers.js +88 -90
- data/lib/samagotchi/web/public/task_stop.js +1 -1
- data/lib/samagotchi/web/public/timing.js +2 -2
- data/lib/samagotchi/web/public/{turn_view.js → turn_block.js} +27 -92
- data/lib/samagotchi/web/public/turn_events.js +2 -2
- data/lib/samagotchi/web/public/turn_html.js +5 -3
- data/lib/samagotchi/web/public/turn_model.js +6 -6
- data/lib/samagotchi/web/public/verdict_fresh.js +2 -0
- data/lib/samagotchi/web/public/working_set_view.js +3 -1
- data/lib/samagotchi/web/server.rb +4 -4
- data/lib/samagotchi/web/session_control.rb +2 -2
- data/lib/samagotchi/worker.rb +9 -9
- data/lib/samagotchi/worker_inbound.rb +6 -5
- metadata +12 -3
- data/lib/samagotchi/web/public/hold.js +0 -78
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 93a0efdc96f935536f82621c8c70c078cf4d89a9a88ce2a6881b77a9e545de30
|
|
4
|
+
data.tar.gz: c8fd5b64ba27f5ad4d83369f58990cc53d449f6eb498050f32e2199216fcb9b6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 478a7f90b5f012fa5c26cc571743191dc78550b9227880d01957e922606f495f35a6d4f4e6dba65fe2edacc0274fb57a48b67377efd704ea7f4034e0a03c1bb0
|
|
7
|
+
data.tar.gz: 66e0a258aa80f18ddbee8d40430c77f19b74dc0b335a889379e795f3e38e21282ec283018b63563318e3b9fb03ee170143ae1e68ca1131fce923422d3ddeb248
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,53 @@ 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.135.0] - 2026-10-30
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Web: Revise on a finished answer: your next message rewrites it, and once that turn completes the earlier version
|
|
15
|
+
folds in place to one stub row ("v1 · its first line · N lines — superseded by v2", a click unfolds it); the newest
|
|
16
|
+
answer's "v2 · 1 earlier ▾" chip lists the versions by their prompts. Stacks show on every owner's page, live and
|
|
17
|
+
after a reload.
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Guardrails: a file or string check (`test -f x`, `[ -d dir ]`, `[ "$a" = b ]`) counts as read-only, so
|
|
22
|
+
`test -f x && cat x` no longer asks where `cat x` alone wouldn't.
|
|
23
|
+
|
|
24
|
+
### Removed
|
|
25
|
+
|
|
26
|
+
- Web: the turn view (`web.view: turn`, `chi web --web-view`, `SAMAGOTCHI_WEB_VIEW`, `?view=`): the stage is the only
|
|
27
|
+
view; finished turns still show as blocks of steps in the history. A config.yml that still sets `web.view` warns
|
|
28
|
+
about an unknown key; a chi web started with `--web-view` doesn't come back from an Update restart (start it again
|
|
29
|
+
without the flag).
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- Web: opening another session clears the previous one's init row, markdown tip and card actions at once; they no
|
|
34
|
+
longer stayed when the open failed.
|
|
35
|
+
- Web: a canceled turn's timing shows in the info bar right away, not at the next unrelated redraw.
|
|
36
|
+
- Web: the info bar follows a terminal taking the open session (its Continue and Stop, the title's ✎) at once, not at
|
|
37
|
+
the hub's next event.
|
|
38
|
+
- Web: Verify fresh on a verdict's card says it's unavailable as soon as the open session's worker goes, not at the
|
|
39
|
+
hub's next event.
|
|
40
|
+
- Web: a turn's end no longer shows the session idle while the next queued turn already runs (its late re-read wrote
|
|
41
|
+
the stored "idle" over "running").
|
|
42
|
+
- Web: a sign-in that lapsed behind an auth proxy is reported (the sign-in prompt) by the notification bell and the
|
|
43
|
+
push devices list too.
|
|
44
|
+
- Pair turns: a program is judged by its name in any case, as macOS's disk runs it: `GIT config …`, `CHI share`,
|
|
45
|
+
`NOHUP`, `PRINTENV` or `Bash -c` are refused like their lower-case names (System 1's memories exemption reads
|
|
46
|
+
`MAKE` or `RUBY x.rb` the same way). More spellings that hid a command are caught: fish's `--command S` and
|
|
47
|
+
`-C`/`--init-command S`, `PROMPT_COMMAND` and a `$(…)` kept in a value bash expands later (`PS4='$(…)' bash -x`),
|
|
48
|
+
the program behind `xcrun`, `arch`, `script`, `builtin`, `uv run`, `pnpm exec`, `bunx` and a wrapper's value option
|
|
49
|
+
(`env -u X`, `sudo -u root`), the command lines `env -S`, `ssh -o ProxyCommand=`, `hyperfine` and `parallel` run,
|
|
50
|
+
`php -B/-R/-E`, `perl -de`, `jruby`/`raku`/`julia -e`. git or chi behind a program chi doesn't know
|
|
51
|
+
(`some-runner git config …`, `chronic chi share x`) and a `$(…)` kept in a value now ask you (refused when what it
|
|
52
|
+
runs reads as git config or chi). `git add maintenance start` no longer asks you; `git lfs update` does.
|
|
53
|
+
|
|
54
|
+
Update with `chi update`, then restart `chi web` (without `--web-view`) and the session workers (`chi sessions
|
|
55
|
+
restart`). `chi home doctor` says what else to do.
|
|
56
|
+
|
|
10
57
|
## [0.134.0] - 2026-10-30
|
|
11
58
|
|
|
12
59
|
### Added
|
|
@@ -5143,7 +5190,8 @@ and long-lived sessions.
|
|
|
5143
5190
|
- A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
|
|
5144
5191
|
hotkey panel that send selected text or the clipboard to your sessions.
|
|
5145
5192
|
|
|
5146
|
-
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.
|
|
5193
|
+
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.135.0...HEAD
|
|
5194
|
+
[0.135.0]: https://github.com/dm1try/samagotchi/compare/v0.134.0...v0.135.0
|
|
5147
5195
|
[0.134.0]: https://github.com/dm1try/samagotchi/compare/v0.133.0...v0.134.0
|
|
5148
5196
|
[0.133.0]: https://github.com/dm1try/samagotchi/compare/v0.132.0...v0.133.0
|
|
5149
5197
|
[0.132.0]: https://github.com/dm1try/samagotchi/compare/v0.131.0...v0.132.0
|
data/bin/chi
CHANGED
|
@@ -234,7 +234,7 @@ if options[:dir]
|
|
|
234
234
|
exit 2
|
|
235
235
|
end
|
|
236
236
|
|
|
237
|
-
# `chi web`: its flags (--port, --open
|
|
237
|
+
# `chi web`: its flags (--port, --open…) went through the parser above.
|
|
238
238
|
options[:web] = true if web
|
|
239
239
|
|
|
240
240
|
# The first interactive start of a new installed chi says what `chi update` would update.
|
|
@@ -253,9 +253,8 @@ if options[:web]
|
|
|
253
253
|
port = options[:web_port] || Samagotchi::Config.get("web.port")
|
|
254
254
|
markdown = options[:web_markdown]
|
|
255
255
|
markdown = Samagotchi::Config.get("web.markdown") if markdown.nil?
|
|
256
|
-
view = Samagotchi::Config.get("web.view")
|
|
257
256
|
exit Samagotchi::Web::Server.launch(port: port, scope: options[:scope] || "project", open_browser: options[:web_open],
|
|
258
|
-
markdown: markdown,
|
|
257
|
+
markdown: markdown, new_token: options[:new_token], tunnel: options[:tunnel],
|
|
259
258
|
working_set: Samagotchi::Config.get("web.working_set"),
|
|
260
259
|
annotate_presets: Samagotchi::Config.get("web.annotate_presets"),
|
|
261
260
|
editor: Samagotchi::Config.get("web.editor"),
|
data/docs/architecture.md
CHANGED
|
@@ -236,6 +236,9 @@ agent logic and can be used without any terminal rendering; the UI is a thin lay
|
|
|
236
236
|
run's own prompts until their turns end; `#run` distinguishes an empty answer from a
|
|
237
237
|
prompt this terminal removed before it ran (both exit 1), as well as chi's other outcomes.
|
|
238
238
|
Without a capable terminal it falls back to `PlainSurface` (append-only).
|
|
239
|
+
- The browser's page (`lib/samagotchi/web/public/`: `app.js` for the owner, `participant.js` for a participant) is a
|
|
240
|
+
client of chi web: where its state lives, which redraw follows which write, its render idioms, stale-answer guards,
|
|
241
|
+
storage keys and streams are in [internals/web-frontend.md](internals/web-frontend.md).
|
|
239
242
|
|
|
240
243
|
#### Using the core
|
|
241
244
|
|
|
@@ -532,16 +535,16 @@ The page's frequent reads of `GET /api/sessions/:id` stay light too:
|
|
|
532
535
|
(`turn_model.js` `questionRefs`) before the card draws, so the stage's asking slot (`stage_slots.js`
|
|
533
536
|
`drawAsking(text, refs)`) pairs them on its raw draw and again after its markdown swap; the card pairs its
|
|
534
537
|
question text and its kept reply (`question_cards.js`); the step's closed `.gen-text` takes them
|
|
535
|
-
(`
|
|
538
|
+
(`turn_block.js`; on a reload its `data-answer-at`, the message's `display_refs`) and `linkTextRefs` pairs it at
|
|
536
539
|
the turn's end and after a reload. A closed step's text is markdown too with `web.markdown` on: a reload's
|
|
537
|
-
from its message's `html` (`
|
|
540
|
+
from its message's `html` (`turn_block.js` `textTemplate`), a live turn's from `POST /api/render` on its step's
|
|
538
541
|
first open after the turn's end (`markdown_place.js` `renderStepTexts`, which pairs its refs again after the
|
|
539
542
|
swap); the text as written stays on it as `data-copy-source` (`stepText`, a reloaded card's kept reply). A file ref outside the
|
|
540
543
|
session's folder is dropped, as a reload drops it: the folder `/tail` names, else (an older worker) the hub's
|
|
541
544
|
session list entry's, else (refs to check, no hub entry) the session file's; with no folder known at all, every
|
|
542
545
|
file ref is dropped rather than shown unchecked.
|
|
543
546
|
A plugin tool's row refs (`refs`, `RowRefs`) ride its tool events and, on a reload, its part (saved as
|
|
544
|
-
`tool_refs`); `
|
|
547
|
+
`tool_refs`); `turn_block.js` binds them to the row's title (`refs.js bindRowRefs`: the first on the title,
|
|
545
548
|
the others in its menu).
|
|
546
549
|
- `?timing=1`: the whole timing (`analytics.json` merged with the worker's `/state`) and
|
|
547
550
|
`turn_count`, when the page's merge came up short.
|
data/docs/cli.md
CHANGED
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
- `chi web --tunnel tailscale` — the Web UI over https on your tailnet, for your phone anywhere: Tailscale Serve (never Funnel) on this machine's `<machine>.<tailnet>.ts.net` name for this run, the owner your Tailscale login, turned off on exit (see [Tailscale Serve](configuration.md#tailscale-serve-your-tailnet)); `--tunnel` without `web` exits 2
|
|
18
18
|
- `chi web push ls [--format json]`, `chi web push rm ID`, `chi web push test [ID]` — the phones chi web pushes to (see [Phone notifications](configuration.md#phone-notifications-web-push)): list them (short id, label, origin, added, last delivery, done turns, last error; `stale` for one made on another public URL than chi web's now: never sent to), remove one, or send a test notification to ID or to every device not stale. chi web need not run; nothing printed holds a device's push address or keys. A usage error exits 2; a store others can read, a corrupt one, an unknown device or a failed test push exit 1. This machine only: `chi --remote X web push` exits 2
|
|
19
19
|
- `chi web --web-markdown` — opt in to sanitized Markdown rendering for completed assistant messages, the stage's asking text and a resolved question card's reply
|
|
20
|
-
- `chi web --web-view turn` — draw turns with the turn view (each turn as one block of steps, the running one at the bottom of the history) instead of the default stage view (the running turn pinned above the composer); `?view=stage|turn` on the page URL overrides it (see [Web views](#web-views))
|
|
21
20
|
- `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))
|
|
22
21
|
- `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
|
|
23
22
|
- `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))
|
|
@@ -1037,29 +1036,31 @@ the keys of sessions gone from the list are dropped when the list loads);
|
|
|
1037
1036
|
other viewers and the participant page are not affected. A jump into a
|
|
1038
1037
|
folded answer (a claim link, say) unfolds it for that page load.
|
|
1039
1038
|
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1039
|
+
**Revise** rewrites an answer and stacks the versions. A finished turn's
|
|
1040
|
+
final answer has a Revise button on its bottom edge (on hover; always shown
|
|
1041
|
+
on a touch screen), unless it's a Verify turn's answer, a canceled or failed
|
|
1042
|
+
turn's partial answer, or an earlier version in a stack. Revise arms the
|
|
1043
|
+
composer with a chip, "Revising: ‹its first line› ✕"; your next message (say
|
|
1044
|
+
"cut it shorter") is sent as a Revise of that answer. ✕, emptying the box or
|
|
1045
|
+
opening another session lets go of it. When the Revise turn completes with an
|
|
1046
|
+
answer, the earlier version folds where it was into one stub row,
|
|
1047
|
+
"▸ v1 · “its first line” · 84 lines — superseded by v2": its prompt, its
|
|
1048
|
+
steps and its answer, all behind it (a warn card, a verdict card and the
|
|
1049
|
+
recap stay on view). Click the stub to unfold that version in place (kept per
|
|
1050
|
+
browser, with the folded answers); a time divider between two folded stubs
|
|
1051
|
+
moves into the later stub. The newest version has a chip, "v3 · 2 earlier ▾",
|
|
1052
|
+
listing every version by its prompt; pick one to jump to its stub. Revise
|
|
1053
|
+
again on the newest version adds to the stack. Stacks are session data (the
|
|
1054
|
+
stamp is on the Revise prompt, see [sessions.md](sessions.md), Revise
|
|
1055
|
+
stacks): every owner's page shows them, live and after a reload; the
|
|
1056
|
+
participant page doesn't draw them yet. A session whose worker predates
|
|
1057
|
+
Revise (started by an older chi) disables the button and shows the chip as
|
|
1058
|
+
"Revise: restart this session to use Revise"; a message sent then is a plain
|
|
1059
|
+
one. A Revise turn that fails comes back into the composer with its chip.
|
|
1060
|
+
|
|
1061
|
+
### How the page draws a turn
|
|
1062
|
+
|
|
1063
|
+
**The stage** pins the running turn above the composer, in its own
|
|
1063
1064
|
card, so it stays on screen without scrolling: a status row (what it is
|
|
1064
1065
|
doing, the step, the elapsed time), your prompt on one line, the newest
|
|
1065
1066
|
narration sentence (else the newest thinking one, in italics; click it for
|
|
@@ -1078,8 +1079,8 @@ stage's bottom edge takes you to it. Once answered, the model's
|
|
|
1078
1079
|
question or its own approval keeps that reply (a delegate's relayed
|
|
1079
1080
|
approval, a hook's question and the step limit don't): two muted lines
|
|
1080
1081
|
under its `question → answer` line, the whole text when you open the card,
|
|
1081
|
-
|
|
1082
|
-
calls` with one tick per call, opens the turn
|
|
1082
|
+
live and after a reload. The chip at the stage's bottom edge, `N steps · M tool
|
|
1083
|
+
calls` with one tick per call, opens the turn block right above it (newest step first
|
|
1083
1084
|
while it runs; a tick opens its step). Below the chip, past a dashed rule, wait the
|
|
1084
1085
|
messages queued for after the turn and the commands queued for its end; they go into
|
|
1085
1086
|
the history after the turn's answer. `▾` folds the stage to its status
|
|
@@ -1094,10 +1095,11 @@ message moves it at once. The history is never scrolled while a turn runs, and o
|
|
|
1094
1095
|
follows the hand-off if you were at its end. The `/` command list opens
|
|
1095
1096
|
over the stage's lower edge.
|
|
1096
1097
|
|
|
1097
|
-
**The turn
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1098
|
+
**The turn block** shows a turn's work as *one block*: in the stage's cloud
|
|
1099
|
+
while the turn runs (newest step first), in the history after the hand-off
|
|
1100
|
+
and on a reload. The running
|
|
1101
|
+
generation is the live part on top (its thinking, its narration, its
|
|
1102
|
+
tool rows), the earlier ones below it collapsed to one line each
|
|
1101
1103
|
(their narration's first line, else their first call's title such as
|
|
1102
1104
|
`edit lib/a.rb`, and a call count), expandable for inspection. A tool row
|
|
1103
1105
|
says what the call did: a file's path relative to the session's folder, a
|
|
@@ -1116,8 +1118,8 @@ first line, so a mid-turn note reads without a click), and each run of steps
|
|
|
1116
1118
|
that only called tools or thought sits behind one `· N steps` line that
|
|
1117
1119
|
shows them; old turns open the same way after a reload. A turn whose steps
|
|
1118
1120
|
said nothing collapses the block to its summary (`3 steps
|
|
1119
|
-
· 8 tool calls · execute ×7 · read ×1`). Either way the answer
|
|
1120
|
-
|
|
1121
|
+
· 8 tool calls · execute ×7 · read ×1`). Either way the answer is a normal
|
|
1122
|
+
bubble under the block (Markdown, annotate). A plain answer
|
|
1121
1123
|
without tools ends exactly as it does today. Rows the code collapses stay
|
|
1122
1124
|
as you toggled them. A reloaded session shows the same block
|
|
1123
1125
|
from the saved messages (each step's thinking, narration, tool parameters
|
|
@@ -1130,7 +1132,10 @@ steps saved before that have none, so they show no thinking. An `edit` or
|
|
|
1130
1132
|
made (up to 120 lines or 8 KB), live and after a reload (see
|
|
1131
1133
|
[Guardrails](guardrails.md#ask) for the diff an approval shows first).
|
|
1132
1134
|
|
|
1133
|
-
|
|
1135
|
+
`web.view`, `chi web --web-view`, `SAMAGOTCHI_WEB_VIEW` and `?view=` were removed with the turn view (the
|
|
1136
|
+
stage is the only view; a config.yml that still sets `web.view:` warns about an unknown key).
|
|
1137
|
+
|
|
1138
|
+
**When things happened**, on the owner's page and on a participant's review page: where 10 minutes or
|
|
1134
1139
|
more of quiet passed between the end of one activity and the start of the next (a turn, a context
|
|
1135
1140
|
note, a reminder), the history has one dim centred line, `── 14:32 · 47 min later ──`; the date is
|
|
1136
1141
|
added when the day changed or the turn wasn't today (`── Sat 24 Oct 09:10 · 13 h later ──`).
|
|
@@ -1147,7 +1152,7 @@ it read, edited or wrote, the memories it read or wrote and the URLs it
|
|
|
1147
1152
|
fetched. It is on by default; `web.working_set: false` (or `chi web
|
|
1148
1153
|
--no-web-working-set`, `SAMAGOTCHI_WEB_WORKING_SET=false`; `?working_set=0` on
|
|
1149
1154
|
the page URL for one page load, `?working_set=1` back on) turns it off, and the
|
|
1150
|
-
session bar shows the `mem N` chip again.
|
|
1155
|
+
session bar shows the `mem N` chip again.
|
|
1151
1156
|
|
|
1152
1157
|
- **The rail**, on the right of the stage's live slots: the running turn's set
|
|
1153
1158
|
in four lines, folded as it grows (files under one folder become
|
data/docs/configuration.md
CHANGED
|
@@ -2184,8 +2184,7 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
|
|
|
2184
2184
|
| `web.port` | `4567` | `--port` | `chi web`'s port. See [CLI](cli.md). |
|
|
2185
2185
|
| `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). Anything else binds `127.0.0.1` with a warning. See [CLI: chi web on your phone](cli.md#chi-web-on-your-phone). |
|
|
2186
2186
|
| `web.markdown` | `false` | yes | Render answers as Markdown in `chi web`. |
|
|
2187
|
-
| `web.
|
|
2188
|
-
| `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 line under each finished turn 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; the turn view ignores it. See [CLI: the working set](cli.md#the-working-set). |
|
|
2187
|
+
| `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 line under each finished turn 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). |
|
|
2189
2188
|
| `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). |
|
|
2190
2189
|
| `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). |
|
|
2191
2190
|
| `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 repo 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). |
|
data/docs/guardrails.md
CHANGED
|
@@ -54,6 +54,11 @@ changed: `diff +3 −1` under the row on the web (closed, it survives a
|
|
|
54
54
|
reload) and ` +3 −1` at the end of the terminal's tool line. The model never
|
|
55
55
|
sees these diffs.
|
|
56
56
|
|
|
57
|
+
The card also carries the ask's facts as `approval:` (tool, command or paths, where, rule, scopes, preview…):
|
|
58
|
+
`Guardrails::Approval.payload` builds them as a `Guardrails::ApprovalFacts` value, which System 1 and the parent
|
|
59
|
+
checks read in the worker; the card and the session file get its `to_h`, the same hash every UI reads, and a saved
|
|
60
|
+
card comes back through `ApprovalFacts.from_h` (string or symbol keys, missing fields nil).
|
|
61
|
+
|
|
57
62
|
Who answers:
|
|
58
63
|
|
|
59
64
|
- REPL (`chi --no-shared`, with or without `-p`): at the `? ` prompt.
|
|
@@ -516,10 +521,36 @@ own, one another interpreter runs (`ruby -e`, `python - <<EOF`) by its text
|
|
|
516
521
|
variable a program runs through a shell (`PairCommand::COMMAND_ENV`:
|
|
517
522
|
`GIT_EDITOR`, `GIT_SEQUENCE_EDITOR`, `EDITOR`, `VISUAL`, `GIT_EXTERNAL_DIFF`,
|
|
518
523
|
`GIT_SSH_COMMAND`, `GIT_SSH`, `GIT_PROXY_COMMAND`, `GIT_PAGER`, `PAGER`,
|
|
519
|
-
`MANPAGER`, `GIT_ASKPASS`, `SSH_ASKPASS`), as a prefix,
|
|
520
|
-
read as `sh -c` would run it, for the refusals and for
|
|
524
|
+
`MANPAGER`, `GIT_ASKPASS`, `SSH_ASKPASS`, bash's `PROMPT_COMMAND`), as a prefix,
|
|
525
|
+
`env`'s or `export`'s: read as `sh -c` would run it, for the refusals and for
|
|
526
|
+
an outside write. An assigned value that keeps a `$(…)`, `${ …; }` or backticks
|
|
527
|
+
as text (`PS4='$(…)' bash -x`, `PS0`, `PS1`, `$'\x24(…)'`: the shell expands it
|
|
528
|
+
later), a prefix's, a bare assignment's or `export`'s (not `grep 'X=$(…)'`), is
|
|
529
|
+
refused for what its `$(…)` runs (`PS4='$(git config …)'`), and otherwise the
|
|
530
|
+
owner approves it (`PairBoundary::NET`: `CFLAGS='$(BASE)' make`).
|
|
531
|
+
The program is found behind the wrappers (`env`, `sudo`, `timeout`, `xargs`,
|
|
532
|
+
`nice`, `exec`, `command`, `builtin`, `xcrun`, `arch`, `script`'s file, `npx`,
|
|
533
|
+
`bunx`, `uvx`, and two-word ones: `uv`/`poetry`/`pdm`/`hatch`/`pipx run`,
|
|
534
|
+
`pnpm`/`yarn exec`/`dlx`, `npm exec`/`x`, `bun x`, `mise exec`/`x`) and their
|
|
535
|
+
value options (`env -u X`, `sudo -u root`, `timeout -s KILL 5`; `xcrun -r`,
|
|
536
|
+
`sudo -n` are flags). A command line a program takes as text is read as a
|
|
537
|
+
command: `env -S S` (`-vS S` too), `script -c S`, `npx`/`npm exec -c S`
|
|
538
|
+
(`--call`), `rsync -e S` (`--rsh`), ssh's (scp's, sftp's) `-o ProxyCommand=S`
|
|
539
|
+
(`LocalCommand`, `KnownHostsCommand`: they run here), `hyperfine`'s arguments,
|
|
540
|
+
`parallel`'s and `watch`'s. And as a net for a runner no list knows, any word
|
|
541
|
+
but the program's (a wrapper's option value too) that names git is read as git
|
|
542
|
+
from there, one naming chi before a chi command counts (`chronic chi share x`;
|
|
543
|
+
not `cp bin/chi /tmp/x`), and a command line starting with git or chi is read
|
|
544
|
+
as a command (`some-runner 'git config k v'`): a hit asks the owner
|
|
545
|
+
(`PairBoundary::NET`), it doesn't refuse, since chi can't tell whether that
|
|
546
|
+
word runs (`gh pr create --title 'git config fix'` asks too). A read stays a
|
|
547
|
+
read, and a command that only reads (`rg 'git config'`) isn't netted. A heredoc written as data (`cat`,
|
|
521
548
|
`tee`, `git commit -F -`) is not looked into: where it goes is checked. A
|
|
522
549
|
command the checks can't read is refused, never let through on a text match.
|
|
550
|
+
A program is told by its name in any case, on every disk (`GIT config`, `Chi
|
|
551
|
+
share`, `/bin/SH -c`, `NOHUP`: a disk that folds case, macOS's default, runs
|
|
552
|
+
them as git, chi, sh, nohup; `ShellLex.program_name`), and so is chi or git
|
|
553
|
+
config in a script's text.
|
|
523
554
|
|
|
524
555
|
And it **asks the owner** before a `write` or `edit` that may land outside the
|
|
525
556
|
session's project root, and before a command that may write there, every time:
|
|
@@ -552,7 +583,8 @@ rule lowers it), so it comes even in auto mode. A command fails closed
|
|
|
552
583
|
install -g`, `corepack enable`, `rustup`/`pyenv`/`rbenv`/`asdf`/`volta`/`nvm`
|
|
553
584
|
installs and globals, `code --install-extension`, `gh auth`/`config
|
|
554
585
|
set`/`extension install`, `launchctl` but its reading verbs, `git maintenance register`/`start`, `git
|
|
555
|
-
lfs install
|
|
586
|
+
lfs install`/`update` (git's words from its subcommand on: not `git add
|
|
587
|
+
maintenance start`), `scalar register`/`clone`; a project
|
|
556
588
|
install such as `npm install` or `bundle install` and build caches don't
|
|
557
589
|
count), and no shell or interpreter that reads its script from stdin: one
|
|
558
590
|
on the receiving end of a pipe whatever its options (`… | sh`, `| bash -x`,
|
|
@@ -560,8 +592,10 @@ rule lowers it), so it comes even in auto mode. A command fails closed
|
|
|
560
592
|
or with no script at all: chi can't see that script. A shell's `-e`, `-x`
|
|
561
593
|
and `-o …` are flags, only `-c` (or a cluster holding it, `-ec`, `-cx`)
|
|
562
594
|
hands it a script: the first word after its options (`sh -c -- S`, `bash -c
|
|
563
|
-
-x S`)
|
|
564
|
-
|
|
595
|
+
-x S`), and fish's `--command S` (`--command=S`) or `-C`/`--init-command S`,
|
|
596
|
+
a command it runs first (also attached, `-C'S'`, in a cluster, `-lC S`, and
|
|
597
|
+
by a prefix fish takes, `--comm`, `--init`); an interpreter's own code flag does (`python -c`, `ruby -e`,
|
|
598
|
+
`node -e`/`-p`/`--eval=`, `perl -e`/`-E`, `php -r`/`-B`/`-R`/`-E`, `jruby`/`raku`/`julia -e`), alone or in a cluster
|
|
565
599
|
(`ruby -we`, `perl -le`, `node -pe`, `python3 -Ic`, `python3 -c'…'`;
|
|
566
600
|
`PairCommand.cluster_code_at`: a letter that takes the rest of the word,
|
|
567
601
|
`ruby -rset`, holds no code). A script file it names (`sh
|
|
@@ -570,11 +604,15 @@ rule lowers it), so it comes even in auto mode. A command fails closed
|
|
|
570
604
|
Accepted gaps (an accident guard for a trusted team): a script file the turn
|
|
571
605
|
wrote and then runs; inline scripts read only by their text for paths
|
|
572
606
|
(`find … -exec sh -c '…'`, `flock … sh -c`, `awk 'BEGIN{system(…)}'`,
|
|
573
|
-
`deno eval`); a `GIT_CONFIG_*` set by `printf -v`
|
|
574
|
-
(
|
|
575
|
-
value a program runs (`LESSOPEN`,
|
|
607
|
+
`deno eval`); a `GIT_CONFIG_*` or a `COMMAND_ENV` variable set by `printf -v`
|
|
608
|
+
or `read` and exported (`printf -v GIT_PAGER …; export GIT_PAGER; git log`);
|
|
609
|
+
a variable outside `COMMAND_ENV` whose value a program runs (`LESSOPEN`,
|
|
610
|
+
`BROWSER`, a tool's own `*_EDITOR`; `GIT_EXEC_PATH` runs only a file the
|
|
611
|
+
turn wrote); an interpreter no list names (as `awk`); `npx -y`,
|
|
576
612
|
`npm exec`, `pnpm dlx`, `uvx`: they fetch into a cache, which doesn't count,
|
|
577
|
-
and run the package as in-project work
|
|
613
|
+
and run the package (judged as the program) as in-project work; a wrapper's
|
|
614
|
+
value option no list names (`uv run --with x git …` reads `x` as the program;
|
|
615
|
+
the net still reads the git or chi after it).
|
|
578
616
|
|
|
579
617
|
Anything else asks: `rm -rf ~/x`, `sed -i … ~/x`, `curl -o ~/x`, `dd of=~/x`,
|
|
580
618
|
`touch`/`mkdir` outside, `perl -pi … ~/x`, `cd .. && make`, `cd && make`, and
|
|
@@ -726,6 +764,11 @@ checked even after the database name, and only its read/display options are
|
|
|
726
764
|
allowed (unknown options and dot-command arguments are rejected). `gh` allows
|
|
727
765
|
only `run list|view`, `pr list|view|diff|checks`, `issue list|view` and
|
|
728
766
|
`repo view`; `--web`/`-w`, `gh api` and `pr checks --watch` are not read-only.
|
|
767
|
+
`test` and `[ … ]` (with its closing `]`) are read-only as one check, optionally after `!`: a file or string
|
|
768
|
+
test (`-e`, `-f`, `-d`, `-s`, `-r`, `-w`, `-x`, `-L`, `-h`, `-b`, `-c`, `-p`, `-S`, `-g`, `-u`, `-k`, `-O`, `-G`,
|
|
769
|
+
`-n`, `-z` with one operand) or a comparison (`=`, `==`, `!=`, `-eq`, `-ne`, `-lt`, `-le`, `-gt`, `-ge`), so
|
|
770
|
+
`test -f x && cat x` and `[ -d dir ] && ls dir` only read; `-a`/`-o`, `( … )`, any other operator or a missing
|
|
771
|
+
operand, and `[[ … ]]` are not.
|
|
729
772
|
`$(…)`, backticks, `$VAR` (other than `$HOME`, `$XDG_*`, `$TMPDIR` and `$PWD`
|
|
730
773
|
at a word's start), an env prefix, `sh -c`, `xargs`, `eval`, `sudo` and the
|
|
731
774
|
like make a command not read-only, as does a `task_create` `env:` that sets `BASH_ENV`, `ENV`, `CDPATH`, `IFS`,
|
|
@@ -873,6 +916,7 @@ every surface answers for the same paths):
|
|
|
873
916
|
| in a dir: `under?` (case-folded on a folding disk; `fold: false` exact), `strictly_under?`, `within?` (root resolved too) | `PathResolve` | `under?`: ShellPaths, ReviewPaths, PairCommand, ChildBoundary, SelfProtect's words; `Outside.within?` and RmTargets' `strictly_under?` fold only when the dir exists (a missing session root keeps the case it was typed in); `within?`: ProtectedPaths, ScratchWrites, ParentApprovals, SelfProtect's paths |
|
|
874
917
|
| tmp dirs and "not one the session is in" | `PathResolve.tmp_roots(session:)` (exact case) | Outside (`Outside.tmp_roots` is the list it and Targets read), RmTargets; `touches_chi?` takes all of them |
|
|
875
918
|
| a shell word's `~`, `$HOME`, XDG dirs, `$TMPDIR` | `PathResolve.expand_word` | ShellPaths::Walk, ShellGitDirs; PairCommand has its own `~`/`$HOME` (`expand_tilde`) |
|
|
919
|
+
| a program word's name: basename, case-folded on every disk (`GIT`, `/bin/SH`, `ſh`) | `ShellLex.program_name` / `.program?` | PairCommand, ImplicitWriters, SelfProtect's file runners, ShellGitDirs, SharedGit |
|
|
876
920
|
| chi's dirs | `ChiDirs.protected` / `.touches` / `.self_protect` / `.review` | ParentApprovals, Targets, SelfProtect, ReviewPaths (`ProtectedPaths` and `ScratchWrites` get theirs from the wiring) |
|
|
877
921
|
|
|
878
922
|
Left apart on purpose: `Targets.prompt_memory_path?` compares in any case on
|
data/docs/internals/engine.md
CHANGED
|
@@ -76,7 +76,10 @@ Engine#run_turn(session, prompt, on_event:, max_iterations:, cancel_controller:,
|
|
|
76
76
|
│ # messages, goes IDLE, records last_turn (enqueued_id: the turn's input, from its origin;
|
|
77
77
|
│ # a continue turn's origin carries its offer turn's, ContinueOffer#run_continue_turn),
|
|
78
78
|
│ # and :turn_completed {result, turn_summary, display_pending} or :turn_canceled
|
|
79
|
-
│ # {cancellation_reason, cancelled_by, duration_ms} is emitted
|
|
79
|
+
│ # {cancellation_reason, cancelled_by, duration_ms} is emitted; before the replace,
|
|
80
|
+
│ # stamp_opening puts a Verify turn's `verify` and a completed Revise turn's `revises`
|
|
81
|
+
│ # (re-targeted to its stack's top) on the opening message, metadata never sent, and
|
|
82
|
+
│ # :turn_completed carries `verdict` / `revises` when there are
|
|
80
83
|
│ # then the :after_turn hook (status, messages, present: the AnswerDisplay presenter,
|
|
81
84
|
│ # refs: the AnswerRefs adder)
|
|
82
85
|
│ # store_answer_display (one replace of the answer: `display` and/or `display_refs`;
|
|
@@ -330,7 +333,7 @@ One iteration with tool calls:
|
|
|
330
333
|
stored under the real path; at most `RowRefs::MAX_REFS` and `MAX_BYTES`). `ToolRowFields.for` can't build it (no plugins on a reload), so the run carries it
|
|
331
334
|
as `refs:`, saved with the result as `tool_refs` (`ToolResponse::SAVED_KEYS`, never sent), and
|
|
332
335
|
`MessageParts#tool_part` reads it back through `RowRefs.for_display` (checked again). The page binds them to the
|
|
333
|
-
row's title (`refs.js bindRowRefs`, from `
|
|
336
|
+
row's title (`refs.js bindRowRefs`, from `turn_block.js` live and on a reload): the first ref on it, the rest in its
|
|
334
337
|
menu.
|
|
335
338
|
- Settles an ask verdict after `:tool_call_started`, so a UI shows the tool line and then the approval.
|
|
336
339
|
- Dispatches through `KernelLoop#dispatch_tool_call`, or writes the denial text. Unknown tools are answered in
|
|
@@ -545,7 +548,7 @@ Turn events (the main ones; `Events` in `events.rb` keeps the shared sets):
|
|
|
545
548
|
| `:working_set_updated` | after `:generation_started` (the running turn's conversation, the turn's strategy) and once in `end_turn` before the end event (the kept messages), only when the set differs from the last one emitted for the session and is not empty, except one empty event when the last one had items (a rollback emptied the set): a tool-free turn emits none (`Engine#emit_working_set`); between turns `Engine#announce_working_set` announces it under the next turn's strategy (after a `/working-set` op, held behind the command's `command_ran`) | `{items: [{kind, key, state (live\|stale\|forgotten\|dropped), tokens, ops, count, first_id, pinned, has_drop}], at, adds?: [{kind, key, label}]}` (`WorkingSet`, internals/working-set.md; `adds` while the user's adds are pending) |
|
|
546
549
|
| `:hook_notice` | a hook's or plugin's notify during a turn | `{hook, text, level}` (announced with `between_turns: true` outside a turn) |
|
|
547
550
|
| `:guardrail_warning` | a guardrails line during a turn | `{message}` |
|
|
548
|
-
| `:turn_completed` | normal end | Engine event `{result, turn_summary, display_pending}
|
|
551
|
+
| `:turn_completed` | normal end | Engine event `{result, turn_summary, display_pending}`, plus `verdict` (a Verify turn's recorded verdict) and `revises` (a Revise turn's stamp, `{turn_id}`) when there are; the Bridge's SSE frame omits `result` (answer remains in `turn_summary.output`) |
|
|
549
552
|
| `:turn_canceled` | Stop, hook stop, Ctrl-C | `{cancellation_reason, cancelled_by?, duration_ms}` |
|
|
550
553
|
| `:turn_failed` | an error | `{error_class, message, duration_ms, error_kind?, retryable?, host?, summary?}` |
|
|
551
554
|
| `: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) |
|
|
@@ -583,7 +583,7 @@ message's `author_name`, `turn_started`'s, or the panel's list (`participant_nam
|
|
|
583
583
|
A review turn proposes; the owner applies (D4, §3 Apply-as-quote). A pair turn acted itself: no Apply under it
|
|
584
584
|
(`ReviewApply.review_turn?`: a participant's opening message not stamped `pair`; the route answers it
|
|
585
585
|
`not_review_turn`, and its "latest" skips it). Under each participant's review turn that has a
|
|
586
|
-
reply, the owner's page shows **Apply** (`review_apply.js`; drawn by the history render, `
|
|
586
|
+
reply, the owner's page shows **Apply** (`review_apply.js`; drawn by the history render, `turn_block.js`, and at a live
|
|
587
587
|
turn's end from its `turn_started`'s turn id and author, in the stage while the stage holds the turn; a full-width
|
|
588
588
|
button on a phone). It reads `GET …/apply?turn=T` and puts the answer in the composer, after whatever is typed there,
|
|
589
589
|
never sent: the owner reads it, edits it, sends it or not. While the composer holds it (`deliveryControl.hold()`,
|