samagotchi 0.132.0 → 0.134.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 +109 -1
- data/bin/chi +16 -0
- data/docs/architecture.md +28 -1
- data/docs/cli.md +12 -1
- data/docs/configuration.md +62 -15
- data/docs/desktop.md +2 -1
- data/docs/guardrails.md +107 -12
- data/docs/internals/background-tasks.md +1 -0
- data/docs/internals/participants.md +6 -5
- data/docs/internals/tool-guardrails.md +2 -0
- data/docs/plugins.md +2 -1
- data/docs/sessions.md +61 -6
- data/docs/testing.md +8 -3
- data/lib/samagotchi/bundles/mcp/manifest.yml +2 -2
- data/lib/samagotchi/bundles/mcp/plugin.rb +10 -3
- data/lib/samagotchi/bundles/system/manifest.yml +1 -1
- data/lib/samagotchi/chi_home.rb +12 -2
- data/lib/samagotchi/chi_shim.rb +77 -0
- data/lib/samagotchi/child_env.rb +5 -3
- data/lib/samagotchi/config.rb +2 -0
- data/lib/samagotchi/context_fetch.rb +4 -2
- data/lib/samagotchi/desktop/macos.rb +45 -4
- data/lib/samagotchi/engine.rb +13 -0
- data/lib/samagotchi/guardrails/implicit_writers.rb +143 -0
- data/lib/samagotchi/guardrails/pair_boundary.rb +27 -6
- data/lib/samagotchi/guardrails/pair_command.rb +546 -57
- data/lib/samagotchi/guardrails/parent_approvals.rb +1 -1
- data/lib/samagotchi/guardrails/read_only_shell.rb +34 -2
- data/lib/samagotchi/guardrails/review_paths.rb +7 -4
- data/lib/samagotchi/guardrails/shell_git_dirs.rb +6 -1
- data/lib/samagotchi/guardrails/shell_lex.rb +23 -0
- data/lib/samagotchi/guardrails/shell_paths.rb +3 -2
- data/lib/samagotchi/guardrails/targets.rb +8 -3
- data/lib/samagotchi/home_command.rb +33 -21
- data/lib/samagotchi/home_doctor.rb +210 -0
- data/lib/samagotchi/home_notice.rb +87 -0
- data/lib/samagotchi/hooks/loader.rb +5 -0
- data/lib/samagotchi/log_line.rb +12 -0
- data/lib/samagotchi/project_env.rb +352 -0
- data/lib/samagotchi/self_report.rb +35 -0
- data/lib/samagotchi/session_commands.rb +4 -2
- data/lib/samagotchi/session_manager.rb +18 -0
- data/lib/samagotchi/system_one/self_protect.rb +58 -2
- data/lib/samagotchi/testing/boundary.rb +3 -1
- data/lib/samagotchi/testing/waiting.rb +13 -0
- data/lib/samagotchi/toolkit_binding.rb +16 -0
- data/lib/samagotchi/tools/builtins.rb +13 -2
- data/lib/samagotchi/update_command.rb +7 -3
- data/lib/samagotchi/update_hint.rb +6 -1
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +50 -0
- data/lib/samagotchi/web/public/answer_fold.js +262 -0
- data/lib/samagotchi/web/public/answer_refs.js +2 -0
- data/lib/samagotchi/web/public/app.js +22 -2
- data/lib/samagotchi/web/public/cards_place.js +5 -2
- data/lib/samagotchi/web/public/chip_popover.js +77 -12
- data/lib/samagotchi/web/public/claim_link.js +4 -2
- data/lib/samagotchi/web/public/composer.css +13 -1
- data/lib/samagotchi/web/public/data.js +8 -4
- data/lib/samagotchi/web/public/history_reveal.js +23 -0
- data/lib/samagotchi/web/public/index.html +68 -14
- data/lib/samagotchi/web/public/level_chip.js +22 -38
- data/lib/samagotchi/web/public/llm_context_chip.js +28 -80
- data/lib/samagotchi/web/public/refs.js +6 -7
- data/lib/samagotchi/web/public/review_start.js +13 -57
- data/lib/samagotchi/web/public/send_turn.js +3 -2
- data/lib/samagotchi/web/public/start_page.js +117 -97
- data/lib/samagotchi/web/public/start_settings.js +309 -0
- data/lib/samagotchi/web/public/start_toolkit.js +158 -0
- data/lib/samagotchi/web/public/thinking_chip.js +9 -22
- data/lib/samagotchi/web/public/timing.js +5 -1
- data/lib/samagotchi/web/public/turn_html.js +12 -6
- data/lib/samagotchi/web/public/turn_range.js +32 -0
- data/lib/samagotchi/web/public/turn_view.js +21 -11
- data/lib/samagotchi/web/public/verdict_card.js +5 -13
- data/lib/samagotchi/web/public/verify_target.js +7 -10
- 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: 1400c2aa3cceb5a84b6aa0d5610021b5e5893415afa37f98e8c99efe291de436
|
|
4
|
+
data.tar.gz: eda23134194381fd10460ba0b026ae10aa5a8aaf674fbc9dc4504359b1acb86d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7029cb713c02b4c3099ade377bc57618ec6467c2a1f04a4b21c7ebd3fbb6e34705e1ad54fe547672becdc6553824b785c96222e310690fd707a12c0fb057e852
|
|
7
|
+
data.tar.gz: e7400f091254ba9ffdf04f14160e8d6816cbe264016f8be8558c9da091edcc33bf34ed93652c83b6647be728a349b31631db49fc26e483280deda75a7658e5e3
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,112 @@ 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.134.0] - 2026-10-30
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Web: a long answer (over ~25 lines or ~2,000 characters) gets a fold row above it: one click folds it to its first
|
|
15
|
+
line and its length, another opens it. Your folds are kept per browser across reloads; a jump into a folded answer
|
|
16
|
+
(a claim link, the working set, "View check") opens it.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Web: the start page's ⚙ and 🧰 buttons say whether their panel is open (`aria-expanded`) to screen readers.
|
|
21
|
+
- Web: an open sheet or popover is placed again when the window resizes (a rotation, a phone's keyboard): its header
|
|
22
|
+
no longer ends up above the screen, and a sheet turns back into a popover on a wide window.
|
|
23
|
+
- Web: on a phone the ⚙ panel's sheet may take 85 % of the screen, so with "Apply and budget" open its closing note is
|
|
24
|
+
no longer cut off at the bottom edge.
|
|
25
|
+
- Web: the ⚙ and 🧰 panels keep the keyboard focus while open (Tab wraps inside, `aria-modal`), their override chips
|
|
26
|
+
say whether the panel is open (`aria-expanded`), and Esc or × gives focus back to the chip that opened it.
|
|
27
|
+
- Web: "default" on the ⚙ panel's Context row now clears a set apply rule and budget too: the chip read "ctx apply
|
|
28
|
+
payoff · budget 64000" while the row showed default, and the new session got them.
|
|
29
|
+
- Web: on a touch screen a tapped row or link in the ⚙ and 🧰 panels (and the ⚙ and 🧰 buttons) no longer keeps its
|
|
30
|
+
hover tint.
|
|
31
|
+
- Web: a sheet's and a popover's × is a 36 px tap target on a touch screen (it was 22x16 px).
|
|
32
|
+
- Web: the Stop sheet's "+N more" no longer sits 74 px to the right (it shared a class with the ⚙ panel's row).
|
|
33
|
+
- Pair turns: a read-only mention of mise's config (`find . -name mise.toml && make`) no longer asks the owner; a
|
|
34
|
+
delete, move or write of it still does, and `.tool-versions` (mise reads it too) is covered the same way.
|
|
35
|
+
- `chi home doctor` checks the desktop helper only when the helper's chi reads this chi's config dir: a run with
|
|
36
|
+
`XDG_CONFIG_HOME` elsewhere no longer reports the installed helper against a home it never boots.
|
|
37
|
+
- The new-version hint ("… the desktop helper can be updated: chi update") follows the same rule: a run with
|
|
38
|
+
`XDG_CONFIG_HOME` elsewhere no longer hints about the installed helper.
|
|
39
|
+
- `chi home set` under `bundle exec` refuses to record its own Ruby, whose gem path is the bundle's, instead of making
|
|
40
|
+
it the home every launch boots (`--ruby` another Ruby still works).
|
|
41
|
+
- `chi update` under `bundle exec`, run on another Ruby than the chi home's, hands over to the home's chi outside the
|
|
42
|
+
bundle instead of loading the bundle into it (which failed noisily).
|
|
43
|
+
- `chi self`'s home row says when this chi runs on the home's Ruby and gems but isn't the home's newest chi (`this chi
|
|
44
|
+
is X`), as the terminal line and `chi home doctor` do.
|
|
45
|
+
- `chi home doctor` reports a worker that couldn't boot the chi home's chi (the debug log's newest `boot_failed` line
|
|
46
|
+
since the home was set, unless a worker started after it), with what to run.
|
|
47
|
+
- Toolkit suggestions (`toolkit.suggest.mode: on`): a suggestion that times out or gets no answer from System 1 now
|
|
48
|
+
says so in a notice (the session runs its default kit) and a timeout logs a warning, instead of falling back silently.
|
|
49
|
+
- Pair turns: a participant can no longer write git's config at all, so no setting makes git run a command later in
|
|
50
|
+
your name (a hook, a filter or driver, a helper, an include): `git config` only reads, parsed strictly (an unknown or
|
|
51
|
+
abbreviated option counts as a write), and `git -c`, `--config-env`, a clone's `-c`/`--config`, `--template` and
|
|
52
|
+
`GIT_TEMPLATE_DIR` are denied, as are a submodule's hooks (`.git/modules/*/hooks`) and the hook installers
|
|
53
|
+
(`pre-commit install`, `husky`, `lefthook install`). A command git runs from its text (`rebase --exec`,
|
|
54
|
+
`submodule foreach`) is checked like any command. Reading git's config (`git config --get`, `-l`) is no longer
|
|
55
|
+
refused.
|
|
56
|
+
- Pair turns: a command that writes outside the project with no word naming where now asks you like any other
|
|
57
|
+
outside write: global installs (`npm -g`, `pip install`, `gem install`, `brew`, `cargo`/`go install`, `pipx`,
|
|
58
|
+
`uv tool`), user config (`npm config set` writes `~/.npmrc`, `gh config set`), `defaults write`, `launchctl`; and
|
|
59
|
+
so does a shell or interpreter reading its script from stdin (`… | sh`, `| bash -x`, `curl … | bash`,
|
|
60
|
+
`sh < run.sh`). Global installs are recognised in any spelling (`npm i --location global`, `python3.12 -m pip`,
|
|
61
|
+
`npm_config_global=true`, `npm link`), with more tools (`deno`, `dotnet tool`, `corepack`, `bundle config set`).
|
|
62
|
+
- System 1 no longer auto-allows a command that names the memories folder and then climbs out of it by a script's
|
|
63
|
+
own calls (`python3 -c '…os.chdir(os.pardir)…'`, `ruby -e '…File.dirname(Dir.pwd)…'`) or runs a script it can't
|
|
64
|
+
see (a file, stdin, `| sh -x`, `python3.12 x.py`, `make`, `awk -f`): it could write `config.yml`. You decide
|
|
65
|
+
those; plain memory edits stay eligible.
|
|
66
|
+
- Guardrails: a `task_create` whose `env:` points git at another config (`GIT_CONFIG_GLOBAL`, `GIT_CONFIG_SYSTEM`,
|
|
67
|
+
`GIT_CONFIG_KEY_n`/`VALUE_n`) is no longer read-only, and a pair turn's command that sets one is denied like
|
|
68
|
+
`git -c`.
|
|
69
|
+
- Pair turns: more spellings that hid a git config write or a command are caught: a git global option that takes a
|
|
70
|
+
value (`--attr-source`, `--shallow-file`, one git adds later), `git-config` and the other dashed builtins, a script
|
|
71
|
+
after a shell's options (`sh -c -- S`, `bash -c -x S`) or in an option cluster (`ruby -we`, `perl -le`, `node -pe`,
|
|
72
|
+
`python3 -Ic`), and a command handed through an environment variable (`GIT_EDITOR`, `GIT_SSH_COMMAND`,
|
|
73
|
+
`GIT_EXTERNAL_DIFF`, `GIT_PAGER`, `EDITOR`, …). `git maintenance register/start`, `git lfs install` and
|
|
74
|
+
`scalar register` now ask you.
|
|
75
|
+
|
|
76
|
+
## [0.133.0] - 2026-10-30
|
|
77
|
+
|
|
78
|
+
### Added
|
|
79
|
+
|
|
80
|
+
- Sessions follow their project: a session's worker reads its folder's `mise env` once at start, and `execute`,
|
|
81
|
+
tasks, `!cmd`, MCP servers and context sources run in it, getting the project's Ruby (and node, …) whoever started
|
|
82
|
+
the worker (`session.project_env: mise`, the default; `off` skips mise). chi's own process keeps its
|
|
83
|
+
env (its keys, proxies and config never come from a project). The starter's GEM_HOME/GEM_PATH are dropped when the
|
|
84
|
+
Ruby changes. `chi` inside those tools stays the chi home's (a `chi` script in `$XDG_STATE_HOME/samagotchi/bin`). A
|
|
85
|
+
mise.toml edit needs `chi sessions restart`; a `mise env` that fails (an untrusted mise.toml) changes nothing and
|
|
86
|
+
logs one `project_env` warning. A session started from a tool for another folder never gets this one's env.
|
|
87
|
+
`chi self` shows `home` and `project env`. See docs/sessions.md (Project env).
|
|
88
|
+
- Pair turns: their `execute` runs without the project env; a command that runs `mise` or `direnv`, and a write to
|
|
89
|
+
mise's config (the owner's next worker start runs it), ask the owner; `.envrc` and mise's local files are secrets,
|
|
90
|
+
as `.env` is.
|
|
91
|
+
- Bundles: mcp 0.7.3 starts its servers in the session's project env.
|
|
92
|
+
- `chi home doctor` checks what should follow the chi home, one row each with `ok`/`warn`/`fail` and the command to
|
|
93
|
+
run: the chi you ran, the desktop helper's launch file (`chi desktop upgrade`), sessions saved by a newer chi than
|
|
94
|
+
the home's, the tools' `chi` script, a Gemfile.lock in this project that bundles samagotchi, mise, and the last
|
|
95
|
+
worker's `project_env` warning in this folder (e.g. `mise trust`). See docs/configuration.md (Checking it).
|
|
96
|
+
- A chi run at a terminal on another Ruby than the chi home's says so in one dim line, at most once a day
|
|
97
|
+
(`chi X on <ruby>; the chi home is <ruby> (chi Y) (chi home doctor)`).
|
|
98
|
+
- Web start page: a 🧰 beside ⚙ picks the new session's toolkit. **Auto** (the default, nothing sent) leaves the
|
|
99
|
+
first message to `toolkit.suggest.mode: on`'s suggestion when one can come, else `toolkit.default`; a picked kit is
|
|
100
|
+
sent (no suggestion then) and shows as a `tools: lean` chip (a dot on a phone). The list holds the built-in and
|
|
101
|
+
config kits a new session can start on; `GET /api/models` lists them as `toolkits`. A kit Start refuses is said in
|
|
102
|
+
the list. See docs/configuration.md (Toolkits).
|
|
103
|
+
|
|
104
|
+
### Changed
|
|
105
|
+
|
|
106
|
+
- Web start page: thinking, LLM context and review files move into one ⚙ panel beside the model picker (a bottom
|
|
107
|
+
sheet on a phone): each a row of segments that applies at once, the first one the picked model's own
|
|
108
|
+
(`default (high)`), the context's apply rule and budget under **Apply and budget ▸**, and **Review files…** as a
|
|
109
|
+
view of its own. Only what is set shows on the row, as accent chips (`think high`, `ctx stale`,
|
|
110
|
+
`review: README.md`) that open the panel; on a phone ⚙ carries a dot instead. Still for the next new session
|
|
111
|
+
only. See docs/configuration.md (A new web chat).
|
|
112
|
+
|
|
113
|
+
Update with `chi update` (mcp 0.7.3), then restart `chi web` and the session workers (`chi sessions restart`). If
|
|
114
|
+
you use the desktop helper, run `chi desktop upgrade` once; `chi home doctor` says what else to do.
|
|
115
|
+
|
|
10
116
|
## [0.132.0] - 2026-10-29
|
|
11
117
|
|
|
12
118
|
### Added
|
|
@@ -5037,7 +5143,9 @@ and long-lived sessions.
|
|
|
5037
5143
|
- A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
|
|
5038
5144
|
hotkey panel that send selected text or the clipboard to your sessions.
|
|
5039
5145
|
|
|
5040
|
-
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.
|
|
5146
|
+
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.134.0...HEAD
|
|
5147
|
+
[0.134.0]: https://github.com/dm1try/samagotchi/compare/v0.133.0...v0.134.0
|
|
5148
|
+
[0.133.0]: https://github.com/dm1try/samagotchi/compare/v0.132.0...v0.133.0
|
|
5041
5149
|
[0.132.0]: https://github.com/dm1try/samagotchi/compare/v0.131.0...v0.132.0
|
|
5042
5150
|
[0.131.0]: https://github.com/dm1try/samagotchi/compare/v0.130.1...v0.131.0
|
|
5043
5151
|
[0.130.1]: https://github.com/dm1try/samagotchi/compare/v0.130.0...v0.130.1
|
data/bin/chi
CHANGED
|
@@ -8,6 +8,14 @@ $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
|
|
|
8
8
|
require "samagotchi/utf8_default"
|
|
9
9
|
Samagotchi::Utf8Default.apply!
|
|
10
10
|
|
|
11
|
+
# A chi a session's tool runs: back to that worker's own env before anything
|
|
12
|
+
# reads config (its API keys and proxies, not the project's mise env), and
|
|
13
|
+
# the workers it starts never take the project's (docs/sessions.md, Project env).
|
|
14
|
+
if ENV.key?("SAMAGOTCHI_PROJECT_ENV_ORIG")
|
|
15
|
+
require "samagotchi/project_env"
|
|
16
|
+
Samagotchi::ProjectEnv.take_orig!
|
|
17
|
+
end
|
|
18
|
+
|
|
11
19
|
require "samagotchi/config"
|
|
12
20
|
|
|
13
21
|
begin
|
|
@@ -27,6 +35,14 @@ if ENV.key?("SAMAGOTCHI_WEB_RESTARTED") || ENV.key?("SAMAGOTCHI_WEB_ADOPT_PIDS")
|
|
|
27
35
|
web_restarted = Samagotchi::Web::Server.take_restart_env!
|
|
28
36
|
end
|
|
29
37
|
|
|
38
|
+
# A person's chi at a terminal on another Ruby than the chi home's: one dim
|
|
39
|
+
# line a day saying so (HomeNotice: never in a worker's tools, a script or
|
|
40
|
+
# machine output).
|
|
41
|
+
unless web_restarted
|
|
42
|
+
require "samagotchi/home_notice"
|
|
43
|
+
Samagotchi::HomeNotice.show
|
|
44
|
+
end
|
|
45
|
+
|
|
30
46
|
# `chi self`: print version, source dir, config/memory/session paths, model and
|
|
31
47
|
# bundles (read-only, offline) — lets the agent find its own code and state.
|
|
32
48
|
if ARGV[0] == "self"
|
data/docs/architecture.md
CHANGED
|
@@ -158,11 +158,38 @@ home's Ruby is gone, when no `samagotchi-*.gemspec` is under its gem path, and f
|
|
|
158
158
|
| `Desktop::MacOS#launch_config` | argv `CHI_BOOT + ["--"]`; `launch_ok?` / `launch_problem` check only absolute argv entries | `[ruby, wrapper or bin/chi]` |
|
|
159
159
|
| `InstalledVersions`, `Web::UpdateCheck` | the home's specification dirs and gem dir | the running process's |
|
|
160
160
|
|
|
161
|
-
Unchanged: `ContextProviders`' `{ruby}` (`RbConfig.ruby`: the home's ruby inside a home worker
|
|
161
|
+
Unchanged: `ContextProviders`' `{ruby}` (`RbConfig.ruby`: the home's ruby inside a home worker; it runs under the
|
|
162
|
+
project env below, so a `mise env` that sets GEM_HOME reaches it), `chi bundle
|
|
162
163
|
try/test/check` (`-I LIB_DIR`), hooks (in-process). The worker's boot string, `run_session_loop(id, state_dir:)` and
|
|
163
164
|
`SAMAGOTCHI_BOOT_FALLBACK` stay the cross-version contract (`spec/worker_contract_spec.rb`): the home only adds the
|
|
164
165
|
prelude in front. An older chi that wakes a session doesn't read home.json and starts its worker as before.
|
|
165
166
|
|
|
167
|
+
**The tools a worker runs** follow the project, not the boot, and only they do: a spawned worker
|
|
168
|
+
(`WORKER_PROCESS_ENV`; never an in-process loop) calls `ProjectEnv.compute` in `run_session_loop`, after the owner
|
|
169
|
+
lock and before `Worker.new`, and installs the result (`ProjectEnv.install!`; a frozen `ProjectEnv::Overlay`,
|
|
170
|
+
`{key => value or nil}`). Its ENV is never written: chi's own process (LLM calls, `HostKeys`, Net::HTTP proxies,
|
|
171
|
+
Config, Paths, hooks, plugins) keeps the spawner's env. The overlay is the session folder's `mise env -C DIR --json`
|
|
172
|
+
(mise's `__MISE_*`/`MISE_SHELL` unset; the spawner's GEM_HOME/GEM_PATH unset when the first `ruby` on PATH changes,
|
|
173
|
+
unless mise sets them; never `HOME`, `XDG_*`, `SAMAGOTCHI_*`, `CHI_REMOTE`, `RACK_ENV`, `CI`), `ChiShim`'s dir
|
|
174
|
+
(`$XDG_STATE_HOME/samagotchi/bin`, only a `chi` that execs `Record#boot_argv(run: CHI_BOOT)`) first on PATH when there
|
|
175
|
+
is a home, `SAMAGOTCHI_PROJECT_ENV` (`mise:<ruby>` or `none:<reason>`, for `chi self`) and
|
|
176
|
+
`SAMAGOTCHI_PROJECT_ENV_ORIG` (the worker's own value of each key it changes). It is merged into: `execute` and
|
|
177
|
+
`task_create` for the owner's turns (`Builtins.project_env`, less `TaskRuntime::SANITIZED_ENV_KEYS`; never a
|
|
178
|
+
participant's turn), `!cmd` (`SessionCommands#shell`), context sources (`ContextFetch`) and MCP servers (the mcp
|
|
179
|
+
bundle's `server_env`). A worker started from the worker inherits its clean ENV. A `chi` a tool runs (through the shim or not, a fresh
|
|
180
|
+
process with the tools' env) takes the worker's values back from the record in `bin/chi`, before anything reads
|
|
181
|
+
config (`ProjectEnv.take_orig!`): its own API keys and proxies, and the env of any worker it starts, are the
|
|
182
|
+
worker's (`ChildEnv` still drops the record and marker for a child). `restore_env` never returns a reserved key and
|
|
183
|
+
returns `DYLD_*`/`LD_*`/`RUBYOPT`/`RUBYLIB`/`RUBYGEMS_*`/`BUNDLE*` only as unsets. Hooks run in-process, with the
|
|
184
|
+
worker's env. Specs never reach a real mise: `SAMAGOTCHI_MISE_CLI` (only that path when set) points at a missing
|
|
185
|
+
file. User side: [sessions.md](sessions.md#project-env).
|
|
186
|
+
|
|
187
|
+
**Checking it.** `chi home doctor` (`HomeDoctor`) reads home.json and what should follow it, each condition's text
|
|
188
|
+
from its owner: `ChiHome.problem`, `HomeNotice.mismatch` (also the once-a-day notice `bin/chi` prints at a TTY on
|
|
189
|
+
another Ruby), `Desktop::MacOS#launch_problem` / `#home_problem`, `ChiShim.problem`, `ProjectEnv::NO_MISE` and
|
|
190
|
+
`ProjectEnv.last_warning` (the newest `project_env` / `project_env_applied` record for the folder, from the debug
|
|
191
|
+
log's last 4 MB). Rows: [configuration.md](configuration.md#checking-it-chi-home-doctor).
|
|
192
|
+
|
|
166
193
|
## Session retention & ordering
|
|
167
194
|
|
|
168
195
|
- **Files:** `~/.local/state/samagotchi/sessions/<uuid>.json` + `<uuid>/input|output|owner.lock|bridge.json` (XDG-aware), and the `<uuid>/stopped` and `<uuid>/archived` marker files.
|
data/docs/cli.md
CHANGED
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
- `chi models [--format text|json] [--timeout S] [TEXT]` — list the models every configured host offers, as the names `--model` takes (see [Listing the models](#listing-the-models))
|
|
36
36
|
- `chi self` — print version, source dir (checkout or installed gem), config/memory/session paths, model/host and bundles. Run by a session's `execute`, the `model` row (and host, loop, profile, thinking, served model) is **that session's** model, labelled `(this session <id8>; default …)`; elsewhere it is the default, `(default)`. `model key` is the memory overlay key (`<name>.<key>.md`), and `model notes` the model notes a prompt of that model loads (`model_notes_deepseek (system, 612 chars)`, `none`; inside a session its `--mute` list is left out). `chi self --model` prints only the model a new session starts on, an alias resolved to its target (the desktop helper's hint)
|
|
37
37
|
- `chi update [--dry-run] [--to VERSION] [--json] [--no-gem] [--no-bundles] [--no-desktop]` — update an installed chi: the gem, the system bundle, the shipped bundles you installed and the desktop helper, in one table (see [Updating](#updating))
|
|
38
|
-
- `chi home`, `chi home set [--ruby PATH]`, `chi home doctor` — the chi home: the one Ruby (and its gem path) every chi launch boots, whoever launches it. `chi home` shows it (its Ruby, the newest chi in it, gem home and path, `mise`, when and by which chi it was set, and the chi you ran when that is another one; exit 1 with the problem when it can't boot chi), `set` records this chi's Ruby and gem path (or `--ruby PATH`'s own; it must have samagotchi), `doctor` checks it
|
|
38
|
+
- `chi home`, `chi home set [--ruby PATH]`, `chi home doctor` — the chi home: the one Ruby (and its gem path) every chi launch boots, whoever launches it. `chi home` shows it (its Ruby, the newest chi in it, gem home and path, `mise`, when and by which chi it was set, and the chi you ran when that is another one; exit 1 with the problem when it can't boot chi), `set` records this chi's Ruby and gem path (or `--ruby PATH`'s own; it must have samagotchi), `doctor` checks it and what should follow it, one row each with `ok`, `warn` or `fail` and what to run (exit 1 on a `fail`; see [Checking it](configuration.md#checking-it-chi-home-doctor)). At a terminal, a chi on another Ruby than the home's says so in one dim line a day. This machine only (see [The chi home](configuration.md#the-chi-home))
|
|
39
39
|
- `chi bundle install|upgrade|uninstall|status|diff|list|build|trash|new|test|check` — manage memory bundles (see [Bundle hooks](hooks.md#bundle-hooks-unified-workflow-bundle)); `list` shows the installed ones and the ones shipped with chi, which `install <name>` installs; `core` and `dev` are profiles that install a set of them (see [Bundle profiles](memory.md#bundle-profiles-core-and-dev), [Guardrails](guardrails.md), [Plugins](plugins.md#the-btw-bundle), [the mcp bundle](plugins.md#the-mcp-bundle) [the loop-guard bundle](plugins.md#the-loop-guard-bundle), [the check-in bundle](plugins.md#the-check-in-bundle) and [the skills bundle](plugins.md#the-skills-bundle)). `trash` lists and empties the bundle trash (moved files from uninstalls/upgrades). A usage error (an unknown subcommand or flag, a missing argument, a bad `build --scope`) exits 2, as for the other subcommands. Plain `chi` and `chi web` exit 1 on an unknown flag, a flag missing its value or a stray argument. `new`, `test` and `check` are for writing one: see [Writing a bundle](#writing-a-bundle-new-test-check)
|
|
40
40
|
|
|
41
41
|
- `chi guardrails replay [--dry-run] [--threshold T] [--model M]` — seed System 1's record from the approvals this machine's sessions already answered: each ask is judged by the configured model and written to `guardrails/judgments.jsonl` with the answer the user gave, then a per-rule table prints (asks, allowed, denied, would-be auto-allows, false allows, asks with no judgment). A parent agent's answer is not the user's: only its deny is replayed, and only where the parent could have allowed. `--dry-run` judges without writing, `--threshold` overrides `guardrails.system_one.threshold` for the table, `--model` the configured model for this run. Exit 2 when `guardrails.system_one.mode` is off or no `host` is set; a usage error exits 2 (see [System 1](guardrails.md#system-1-experimental))
|
|
@@ -1026,6 +1026,17 @@ touch screen), and so does each code block of a rendered answer. An answer
|
|
|
1026
1026
|
copies its Markdown source, not the rendered text; a code block copies just
|
|
1027
1027
|
its code; a prompt copies the text as you typed it.
|
|
1028
1028
|
|
|
1029
|
+
A long answer (over 25 lines or 2,000 characters of its text) can be folded
|
|
1030
|
+
by hand: a slim "▾ N lines" row sits just above it once its turn has ended.
|
|
1031
|
+
Click it and the answer folds to one row, "▸ its first line · N lines" (the
|
|
1032
|
+
first line of what a hook presented in its place, else its first heading,
|
|
1033
|
+
else its first line); click again to unfold. A delegate report's reply
|
|
1034
|
+
folds the same way. Folding is this browser's choice, kept per session in
|
|
1035
|
+
localStorage (`chi_answer_fold:<session id>`, the last 200 folded answers;
|
|
1036
|
+
the keys of sessions gone from the list are dropped when the list loads);
|
|
1037
|
+
other viewers and the participant page are not affected. A jump into a
|
|
1038
|
+
folded answer (a claim link, say) unfolds it for that page load.
|
|
1039
|
+
|
|
1029
1040
|
### Web views
|
|
1030
1041
|
|
|
1031
1042
|
`web.view` picks how the page draws a turn: `stage` (the default: the
|
data/docs/configuration.md
CHANGED
|
@@ -70,6 +70,7 @@ model_aliases:
|
|
|
70
70
|
session:
|
|
71
71
|
shared: true
|
|
72
72
|
# idle_exit_minutes: 30 # an unused worker exits after this (0 = never)
|
|
73
|
+
# project_env: mise # a worker's tools get its folder's `mise env` (off: the starter's env)
|
|
73
74
|
# keep_empty: false # true keeps sessions nothing happened in (default: deleted when left)
|
|
74
75
|
# max_children: 4 # running sessions one session may have delegated at a time (the delegate tool)
|
|
75
76
|
# delegate_reports: wake # a child's reply reaches its parent by itself: wake (an idle parent runs a turn), queue, off
|
|
@@ -171,7 +172,7 @@ or `chi web` restart.
|
|
|
171
172
|
| `hooks`, `bundles` | Worker start | Hooks, plugins and their bundle settings load with the engine. |
|
|
172
173
|
| `recap.*` | Worker start | The engine builds its recap job at start, and again when it binds a session with its own `recap.enabled` or `recap.sentences` (`--set`, see "Per-session settings") and when `/recap on\|off\|default` changes the session's own value. |
|
|
173
174
|
| `session.shared` | Next `chi` invocation | It selects attached-worker or in-process mode for that invocation. |
|
|
174
|
-
| `session.idle_exit_minutes`, `session.keep_empty`, `session.max_children`, `session.delegate_reports`, `session.task_wakes`, `session.max_wakes` | Worker start or next matching lifecycle decision | Existing workers keep their idle-exit timer; the other values are read for the next corresponding decision. |
|
|
175
|
+
| `session.idle_exit_minutes`, `session.project_env`, `session.keep_empty`, `session.max_children`, `session.delegate_reports`, `session.task_wakes`, `session.max_wakes` | Worker start or next matching lifecycle decision | Existing workers keep their idle-exit timer and project env; the other values are read for the next corresponding decision. |
|
|
175
176
|
| `session.retention_*`, `session.max_count`, `session.keep_status`, `session.sweep_interval_hours` | Next retention sweep | `chi web` sweeps on its configured schedule and on `GET /api/sessions`; `chi sessions prune` uses the values for that invocation. The sweep interval is checked on the next hub tick. |
|
|
176
177
|
| `web.*` except `web.participants.*` | `chi web` start | Restart `chi web` to change its bind address, port or page settings. |
|
|
177
178
|
| `log.*` | Process start | Each process holds its configured log sink and level after first use; restart that process. |
|
|
@@ -655,7 +656,7 @@ models:
|
|
|
655
656
|
came from);
|
|
656
657
|
- `chi --thinking LEVEL` on a run that starts or resumes a session (the REPL, `-p`, `--resume`, an attached one);
|
|
657
658
|
- `chi send --new --thinking LEVEL`;
|
|
658
|
-
- the web's `think` chip
|
|
659
|
+
- the web's `think` chip in the session bar, and the start page's ⚙ panel (`POST /api/sessions` takes `"thinking": "low"`;
|
|
659
660
|
anything that isn't a level answers `400 invalid_thinking`).
|
|
660
661
|
- `chi web --thinking LEVEL` is the default for the sessions its workers run, below a session's own. A session started
|
|
661
662
|
with only the env (`SAMAGOTCHI_THINKING_LEVEL=low chi send --new …`) loses the level when a worker without that env
|
|
@@ -1374,7 +1375,8 @@ launches it. It is recorded in `~/.config/samagotchi/home.json` (`$XDG_CONFIG_HO
|
|
|
1374
1375
|
- **Setting it.** `chi home set` records the Ruby your `chi` runs on and its gem path (a chruby shell's
|
|
1375
1376
|
`GEM_HOME`/`GEM_PATH` are part of it: that is where its chi lives); `chi home set --ruby PATH` records another
|
|
1376
1377
|
Ruby with its own gem dirs. Either needs samagotchi installed there (`gem install samagotchi` with that Ruby
|
|
1377
|
-
first).
|
|
1378
|
+
first). Under `bundle exec`, `chi home set` refuses to record its own Ruby (the bundle's gem path is no home);
|
|
1379
|
+
`--ruby` another Ruby still works. Without a home.json, the first `chi bootstrap` or `chi update` records one, once (from an installed chi,
|
|
1378
1380
|
never under `bundle exec`): of the desktop helper's Ruby (when its newest chi is at least the one running) and the
|
|
1379
1381
|
Ruby chi runs on, the one with the newer chi. Nothing else ever overwrites it.
|
|
1380
1382
|
`chi home` shows it; `chi home doctor` checks it (see [CLI](cli.md#commands)).
|
|
@@ -1383,8 +1385,9 @@ launches it. It is recorded in `~/.config/samagotchi/home.json` (`$XDG_CONFIG_HO
|
|
|
1383
1385
|
runs the home's `chi update`, and installs into the home's gems with the home's `gem`), `chi bootstrap`'s
|
|
1384
1386
|
commonmarker install, chi web's "installed; restart" notice and the macOS helper after its next `chi desktop
|
|
1385
1387
|
install` or `upgrade`. The boot sets the home's gem path inside Ruby (`Gem.paths`), never in an environment
|
|
1386
|
-
variable: the commands a session runs (`execute`, tasks, `!cmd`, MCP servers)
|
|
1387
|
-
|
|
1388
|
+
variable: the commands a session runs (`execute`, tasks, `!cmd`, MCP servers) follow the session's project (its
|
|
1389
|
+
`mise env`, [Project env](sessions.md#project-env)), and their `chi` is the home's. How:
|
|
1390
|
+
[How chi launches itself](architecture.md#how-chi-launches-itself).
|
|
1388
1391
|
- **No home, or one that can't boot chi** (its Ruby gone after a Ruby upgrade, samagotchi uninstalled there, a
|
|
1389
1392
|
home.json that isn't JSON): each launch uses the Ruby that starts it, as without a home, and the log says why once
|
|
1390
1393
|
per process (`home_unusable`, `home_invalid`). After a Ruby upgrade: `gem install samagotchi` with the new Ruby,
|
|
@@ -1394,9 +1397,34 @@ launches it. It is recorded in `~/.config/samagotchi/home.json` (`$XDG_CONFIG_HO
|
|
|
1394
1397
|
`home_older_than_spawner`), but chi web's restart runs the newest chi it knows outside the home instead
|
|
1395
1398
|
(`restart_home_older`): its flags may be ones the older chi doesn't know.
|
|
1396
1399
|
- **A home that still fails at boot** (a broken install): the worker writes one `boot_failed` line to the debug log
|
|
1397
|
-
(`log.file`) and exits, rather than leave the spawner waiting.
|
|
1400
|
+
(`log.file`) and exits, rather than leave the spawner waiting; `chi home doctor` reports the newest one.
|
|
1398
1401
|
- Each machine has its own home (a remote's is that machine's).
|
|
1399
1402
|
|
|
1403
|
+
### Checking it: chi home doctor
|
|
1404
|
+
|
|
1405
|
+
`chi home doctor` prints one row per check, `ok`, `warn` or `fail`, each with what to run; it exits 1 when a row
|
|
1406
|
+
fails (a launch can't boot what it should), 0 otherwise. Run it in a project folder to check that project too.
|
|
1407
|
+
|
|
1408
|
+
| Row | When | What to run |
|
|
1409
|
+
|---|---|---|
|
|
1410
|
+
| the home | `fail`: its Ruby is gone (a Ruby upgrade) or has no samagotchi | `gem install samagotchi` with the new Ruby, then `chi home set` from it |
|
|
1411
|
+
| this chi | `warn`: the chi you ran is another Ruby's, or another version, than the home's (`ok` for a source checkout, which starts its own code) | `chi home set` to make this one the home, or run the home's |
|
|
1412
|
+
| the last worker boot | `fail`: the newest worker boot in the debug log since the home was set is a `boot_failed` line (the home's Ruby couldn't load chi), not a worker's `start`; nothing without a log file | `gem install samagotchi` with the home's Ruby (or `chi home set` another), then `chi sessions restart ID` |
|
|
1413
|
+
| the desktop helper (when installed, and its chi reads this chi's config dir: none with `XDG_CONFIG_HOME` elsewhere) | `fail`: its launch file's Ruby or chi is gone; `warn`: it runs `[ruby, chi]` (installed before the home), boots the home as it was before a `chi home set`, or sets `GEM_HOME`/`GEM_PATH` | `chi desktop install --force` / `chi desktop upgrade` |
|
|
1414
|
+
| sessions | `warn`: sessions last saved by a newer chi than the home's (their workers run without what it added) | `chi update` |
|
|
1415
|
+
| the tools' `chi` | `warn`: `$XDG_STATE_HOME/samagotchi/bin/chi` boots another chi than the home | the next worker start rewrites it (`chi sessions restart ID` for a running session) |
|
|
1416
|
+
| this project's Gemfile.lock | `warn`: the nearest Gemfile.lock up to the git root bundles samagotchi (`bundle exec chi` isn't supported) | `bundle remove samagotchi` there; run chi itself |
|
|
1417
|
+
| mise | `ok` with the mise found; without one, `warn` when `session.project_env: mise` is set explicitly (`ok` for the default) | install mise, or `session.project_env: off` |
|
|
1418
|
+
| the last worker here | `warn`: the newest worker record for this folder in the debug log is a `project_env` warning (an untrusted mise.toml, a timeout) | `mise trust` there (or fix what it says), then `chi sessions restart ID` |
|
|
1419
|
+
|
|
1420
|
+
The rows after the home's first one need a home that can boot chi; the last three are checked with or without one.
|
|
1421
|
+
|
|
1422
|
+
**The notice.** An installed chi run by a person at a terminal (stdin and stderr TTYs) on another Ruby, or another
|
|
1423
|
+
version, than the home's prints one dim line on stderr, at most once a day: `chi 0.131.0 on /x/bin/ruby; the chi home
|
|
1424
|
+
is /y/bin/ruby (chi 0.132.0) (chi home doctor)`. Never inside a session's tools, for `--json`, `--format json`,
|
|
1425
|
+
`--non-interactive`, `--version`, `--help`, `chi home`, `chi update` or `chi self`, from a source checkout, or in a
|
|
1426
|
+
test run (`SAMAGOTCHI_ENV`, `RACK_ENV`, `CI`). The day is stamped in `$XDG_STATE_HOME/samagotchi/home_noticed`.
|
|
1427
|
+
|
|
1400
1428
|
## Project specific description
|
|
1401
1429
|
|
|
1402
1430
|
If an AGENTS.md file is present in the project root, samagotchi injects its
|
|
@@ -1504,7 +1532,9 @@ toolkit:
|
|
|
1504
1532
|
suggested`, and the turn's first request declares it; the notice reads
|
|
1505
1533
|
`toolkit: web (suggested, p=0.71) · /toolkit to change`. Anything else (the default, a low-confidence or unknown
|
|
1506
1534
|
pick, `undecided`, no answer, a failing System 1, which warns once as `[samagotchi:toolkit]`) keeps the default
|
|
1507
|
-
kit.
|
|
1535
|
+
kit. No answer in time or none at all says so in a warn notice, `toolkit: full (no suggestion: System 1 took over
|
|
1536
|
+
5000 ms) · /toolkit to change` (or `System 1 gave no answer`), and a timeout logs a `toolkit_suggest_timeout`
|
|
1537
|
+
warning: a slow System 1 host wants a larger `guardrails.system_one.timeout_ms`. A Stop during the wait (or before it, even with the answer in) ends the turn before its first request, and
|
|
1508
1538
|
nothing switches. After an `undecided`, a later prompt within the defer limits may switch the session; that notice
|
|
1509
1539
|
says `from this turn`, and that request re-reads the whole prompt (system prompt, tools and conversation, which
|
|
1510
1540
|
the defer limits keep small) without `/toolkit`'s cache confirmation. `/toolkit` or `ctx.use_toolkit` marks the
|
|
@@ -1548,6 +1578,18 @@ toolkit:
|
|
|
1548
1578
|
`full`. A continue and a fork keep the toolkit name. `/toolkit` shows and changes it. The kit's tools, prompt
|
|
1549
1579
|
text and group selectors are part of its definition, not the session file: edits reach a saved name at its next
|
|
1550
1580
|
worker start.
|
|
1581
|
+
- **A new web chat: 🧰.** The start page's 🧰 (beside ⚙; a bottom sheet on a phone) picks the new session's toolkit.
|
|
1582
|
+
**Auto**, checked by default, sends nothing: the session is on `toolkit.default` with `toolkit_origin: default`, so
|
|
1583
|
+
with `toolkit.suggest.mode: on` its first message may switch it to a suggested kit. Auto is listed only when that
|
|
1584
|
+
can happen as far as chi web can tell (on mode, a `guardrails.system_one.host`, two described config kits); bundle
|
|
1585
|
+
and memory kits can add candidates it doesn't see. A kit picked is sent as `POST /api/sessions`'s `toolkit` and
|
|
1586
|
+
saved as `chosen`: no suggestion, the default kit included (its row says `default`); without Auto, the default's
|
|
1587
|
+
row is the one checked and sends nothing. The list holds the built-ins and config kits a new session can start
|
|
1588
|
+
on: not `review`, `child` or a kit named `undecided`, not a kit whose `groups:` refuses a new session, and not one
|
|
1589
|
+
that doesn't resolve in chi web (a base that is a bundle kit); bundle and memory kits load in the worker, so pick
|
|
1590
|
+
them with `/toolkit`. The configured default is always listed. A pick closes the list and shows a `tools: lean`
|
|
1591
|
+
chip on the row (a dot on 🧰 on a phone); it is for that create only, never remembered. A review session is
|
|
1592
|
+
always `chosen`: with review files set the list says it starts on the default unless you pick a kit.
|
|
1551
1593
|
- **Left out: reachable or removed.** A built-in missing from a kit's base list (`lean` has no `delegate`; a
|
|
1552
1594
|
`kits:` entry on `base: lean` or `child` inherits that) is left out softly: it is **reachable**. `find_tools` searches it as group `chi` (`chi/delegate`, with its inputSchema) beside
|
|
1553
1595
|
the MCP servers' tools, and `call_tool(tool: "chi/delegate", args: {…})` runs it as `delegate` itself, through the
|
|
@@ -1667,14 +1709,18 @@ its own). The first target is a long session such as a day-long coordinator, whe
|
|
|
1667
1709
|
- **Set it** at start with `chi --llm-context stale,forget --llm-context-apply turn_end --llm-context-budget 64k`
|
|
1668
1710
|
(the same flags on `chi send --new`), in the session with `/llm-context strategy … apply … budget …`
|
|
1669
1711
|
(docs/cli.md, "Session commands"), or from the web's `llm ctx` chip in the info bar.
|
|
1670
|
-
- **A new web chat:** the start page's
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
`
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1712
|
+
- **A new web chat:** the start page's ⚙ panel (beside the model picker; a bottom sheet on a phone) holds the new
|
|
1713
|
+
session's rarely used settings, each a row of segments that applies at once: **Thinking** and **Context** (the
|
|
1714
|
+
strategy; **Apply and budget ▸** opens the other two fields, with their own Set), the first segment following the
|
|
1715
|
+
picked model (`default (high)`), and **Review files…** (docs/sessions.md, Review sessions). A row the picked model
|
|
1716
|
+
has no values for is hidden. What is set shows on the row as an accent chip (`think high`, `ctx stale`,
|
|
1717
|
+
`review: README.md`) that opens the panel; on a phone ⚙ carries a dot and its label names them. The create sends
|
|
1718
|
+
the context choice as `POST /api/sessions`'s `llm_context` (`{"strategy": "stale,forget", "apply": "turn_end",
|
|
1719
|
+
"budget": "64k"}`, each a word as `/llm-context` takes it, `default` for unset; anything else answers
|
|
1720
|
+
`400 invalid_llm_context` and starts nothing), so the session file holds it before the first turn runs. Nothing
|
|
1721
|
+
is remembered: the choices reset after a create, on a reload and when you open a session (a failed create keeps
|
|
1722
|
+
them); a choice made before picking a model without that row stays (its chip says it is sent anyway), and nothing
|
|
1723
|
+
set sends nothing (the model's values apply).
|
|
1678
1724
|
- **Values:** `none` and a budget of `off` are values the session sets on purpose (they win over the model's);
|
|
1679
1725
|
`default` (or `/llm-context reset` for all three) unsets one, back to following the model. A budget is 4k to
|
|
1680
1726
|
10M tokens (`4000`–`10000000`, or `4k`–`10000k`). The layers are kept in chi's order (`stale`, then `forget`).
|
|
@@ -2042,6 +2088,7 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
|
|
|
2042
2088
|
| `broadcast.threshold` | `0.5` | | The P(yes) a recipient needs, when the triage host gives logprobs; a plain yes or no is 1 or 0. |
|
|
2043
2089
|
| `session.shared` | `true` | | Plain `chi` runs its session in a worker and attaches; `--no-shared` per run. |
|
|
2044
2090
|
| `session.idle_exit_minutes` | `30` | yes | An unused worker exits after this; `0` = never. |
|
|
2091
|
+
| `session.project_env` | `mise` | | The commands a session's worker runs follow its project: `mise` runs them in the session folder's `mise env`, read once at worker start (`chi sessions restart` picks up a mise.toml edit); `off` doesn't ask mise: the tools keep the env of whoever started the worker (with a chi home, the home's `chi` still goes first on their PATH). See [Project env](sessions.md#project-env). |
|
|
2045
2092
|
| `session.keep_empty` | `false` | | Keep sessions nothing happened in. |
|
|
2046
2093
|
| `session.max_children` | `4` | | Running delegated sessions one session may have. |
|
|
2047
2094
|
| `session.delegate_reports` | `wake` | | When a delegate child ends a turn, its reply reaches the parent: `wake` runs an idle parent's turn for it, `queue` waits for the parent's next turn, `off` leaves it to `delegate_result`. |
|
data/docs/desktop.md
CHANGED
|
@@ -300,7 +300,8 @@ exports. So `install` writes `~/Library/Application Support/Chi Helper/launch.js
|
|
|
300
300
|
whose chi may be found only through them, as under chruby): the home's boot sets its gem path itself, and they
|
|
301
301
|
would reach every command the helper's sessions run (a project's `bundle exec` on the wrong gems). A launch file
|
|
302
302
|
from an older chi loses `RUBYLIB` at the next `chi update`, and `GEM_HOME`/`GEM_PATH` once it boots the chi home
|
|
303
|
-
(`chi desktop upgrade` moves it to the home)
|
|
303
|
+
(`chi desktop upgrade` moves it to the home). `chi home doctor` flags a helper that isn't on the home: a
|
|
304
|
+
`[ruby, chi]` argv, a boot of the home as it was before a `chi home set`, or `GEM_HOME`/`GEM_PATH` in its env;
|
|
304
305
|
- `PATH`: the shell's PATH when the file is written, with only absolute folders, in order, each once. The helper's
|
|
305
306
|
chi, the sessions it starts and their `execute` commands can find the tools your terminal finds (mise, Homebrew,
|
|
306
307
|
`codex`, `node`);
|
data/docs/guardrails.md
CHANGED
|
@@ -429,14 +429,50 @@ message:
|
|
|
429
429
|
get-access-token`, `heroku auth:token`, `kubectl config view --raw`;
|
|
430
430
|
`PairCommand::CREDENTIAL_PRINTERS`). On top of it, a pair turn's `execute` runs
|
|
431
431
|
without every variable whose name matches `KEY|TOKEN|SECRET|PASS|CREDENTIAL|AUTH`
|
|
432
|
-
(`PairBoundary::SECRET_ENV_NAME`, case-insensitive)
|
|
432
|
+
(`PairBoundary::SECRET_ENV_NAME`, case-insensitive) and without the session's
|
|
433
|
+
project env ([Project env](sessions.md#project-env)); a command that runs
|
|
434
|
+
`mise` or `direnv` asks the owner (`ENV_LOADER`: they load the project's env
|
|
435
|
+
files, and `.envrc`, `mise.local.toml` and its kin are secrets like `.env`),
|
|
436
|
+
and so does a write (by a file tool or a command) to mise's config, its
|
|
437
|
+
`.tool-versions` included (`MISE_CONFIG`: the owner's next worker start runs
|
|
438
|
+
its `[env]` and installs what it pins; a command asks when a part of it that
|
|
439
|
+
names one may write: `rm`, `mv`, `find … -delete` and `find … | xargs rm`
|
|
440
|
+
do, a read-only part such as `find . -name mise.toml && make` doesn't, the
|
|
441
|
+
list split at `;`, `&&`, `||` and newlines outside `( … )`; mise's own
|
|
442
|
+
writes, `mise use`/`mise set`, ask as runs of mise; a config that changes
|
|
443
|
+
without being named, by `git checkout`, `git stash pop`, `tar x`, `patch` or
|
|
444
|
+
`cp -r dir/. .`, isn't asked about: the owner's next restart runs it);
|
|
445
|
+
and a command still
|
|
433
446
|
running at the limit is killed, never handed over as a background task;
|
|
434
|
-
- git
|
|
435
|
-
|
|
436
|
-
|
|
447
|
+
- git config writes, whatever the key (any may make git run a command later
|
|
448
|
+
in the owner's name: hooks, `core.fsmonitor`, a filter, diff or merge
|
|
449
|
+
driver, a credential or gpg helper, `hook.<name>.command`, an include): a
|
|
450
|
+
pair turn's `git config` only reads, parsed strictly
|
|
451
|
+
(`PairCommand#git_config_read?`: `--get`, `--get-all`, `--get-regexp`,
|
|
452
|
+
`--get-urlmatch`, `--get-color(bool)`, `-l`/`--list`, `get`, `list` or a lone
|
|
453
|
+
key, with `--show-origin`, `--show-scope`, `--name-only`, `-z`, `--type=…`,
|
|
454
|
+
`--default`, `--file F` and the like, every word accounted for; any other
|
|
455
|
+
option, an abbreviation git would take (`--glob`, `--comm`), a short form
|
|
456
|
+
(`-t`) or a word too many is a write). Denied too: `git -c`, `--config-env`,
|
|
457
|
+
a clone's, init's or submodule's `-c`/`--config`, an assignment of
|
|
458
|
+
`GIT_CONFIG_*` or `GIT_TEMPLATE_DIR`, `git` under an alias, `git config` after
|
|
459
|
+
`xargs` or in `find -exec`, and an interpreter's script naming `git config`
|
|
460
|
+
or `git -c`, behind any of git's own options (`--attr-source X`,
|
|
461
|
+
`--shallow-file X`; one chi doesn't know is read both with and without the
|
|
462
|
+
next word as its value, `PairCommand.git_command_starts`), and by a
|
|
463
|
+
builtin's dashed name (`git-config`, `…/git-core/git-submodule foreach`:
|
|
464
|
+
judged as `git config`, `git submodule`). A command git runs now from its text (`git submodule foreach`,
|
|
465
|
+
`rebase --exec`, `bisect run`, a `filter-branch` filter, `--upload-pack`,
|
|
466
|
+
`--receive-pack`, `--extcmd`, an `ext::` URL) is read as a command of its
|
|
467
|
+
own and asks as an outside write. A `.gitattributes` write isn't asked
|
|
468
|
+
about: it only selects a driver, and defining one is what's denied;
|
|
437
469
|
and writing git's hooks: a `write` or `edit` of a path in a `.git/hooks`
|
|
438
|
-
folder (as given or as it
|
|
439
|
-
|
|
470
|
+
folder, a submodule's `.git/modules/<name>/hooks` too (as given or as it
|
|
471
|
+
resolves), or a command whose part that names one isn't read-only
|
|
472
|
+
(`printf … > .git/hooks/pre-commit`, `cp x .git/hooks/`), and installing
|
|
473
|
+
them: `git init`/`clone --template`, and the hook installers
|
|
474
|
+
(`PairCommand::HOOK_INSTALLERS`: `pre-commit install`, `husky`, `lefthook
|
|
475
|
+
install`, `simple-git-hooks`, `overcommit --install`);
|
|
440
476
|
- running chi, by any wrapper (`bin/chi`, `bundle exec chi`, `ruby … bin/chi`,
|
|
441
477
|
`env … chi`, `watch`, `npx`, `sh -c 'chi …'`, `$(chi …)`), in an assignment's
|
|
442
478
|
value or an alias, a function, after `-exec`/`-c`/`-e`, but `chi --version` and
|
|
@@ -476,7 +512,12 @@ message:
|
|
|
476
512
|
A script a command hands on is read too (`Guardrails::PairCommand`): one a
|
|
477
513
|
shell runs (`sh -c`, `eval`, a heredoc or here-string) as a command of its
|
|
478
514
|
own, one another interpreter runs (`ruby -e`, `python - <<EOF`) by its text
|
|
479
|
-
(chi, secrets, the environment, detaching).
|
|
515
|
+
(chi, secrets, the environment, detaching). So is a command handed through a
|
|
516
|
+
variable a program runs through a shell (`PairCommand::COMMAND_ENV`:
|
|
517
|
+
`GIT_EDITOR`, `GIT_SEQUENCE_EDITOR`, `EDITOR`, `VISUAL`, `GIT_EXTERNAL_DIFF`,
|
|
518
|
+
`GIT_SSH_COMMAND`, `GIT_SSH`, `GIT_PROXY_COMMAND`, `GIT_PAGER`, `PAGER`,
|
|
519
|
+
`MANPAGER`, `GIT_ASKPASS`, `SSH_ASKPASS`), as a prefix, `env`'s or `export`'s:
|
|
520
|
+
read as `sh -c` would run it, for the refusals and for an outside write. A heredoc written as data (`cat`,
|
|
480
521
|
`tee`, `git commit -F -`) is not looked into: where it goes is checked. A
|
|
481
522
|
command the checks can't read is refused, never let through on a text match.
|
|
482
523
|
|
|
@@ -496,7 +537,44 @@ rule lowers it), so it comes even in auto mode. A command fails closed
|
|
|
496
537
|
(`~/oth*`), `git -C`'s folder and the paths in a script it hands on (`sh -c`
|
|
497
538
|
read as a command, `python -c`/`ruby -e`/a heredoc by the paths in its text)
|
|
498
539
|
among them. The program's own word doesn't count (`/usr/bin/sed`), nor does
|
|
499
|
-
`PATH=…`, nor a message's heredoc (`git commit -m "$(cat <<'EOF' … EOF)"`)
|
|
540
|
+
`PATH=…`, nor a message's heredoc (`git commit -m "$(cat <<'EOF' … EOF)"`);
|
|
541
|
+
and it runs no program that writes to its own place outside with no word
|
|
542
|
+
naming it (`Guardrails::ImplicitWriters`, examples rather than a closed
|
|
543
|
+
set: `npm`/`pnpm`/`bun`/`deno` global in any spelling (`-g`, `--global=true`,
|
|
544
|
+
`--location global`, `npm_config_global=true`), `npm`/`yarn`/`bun link`,
|
|
545
|
+
`npm config set`/`login` (`~/.npmrc`), `yarn global`, `pip install`/`pip
|
|
546
|
+
config` by any interpreter (`python3.12 -m pip`, `pypy3 -mpip`; with or
|
|
547
|
+
without `--user`: its site-packages is wherever its Python is; a project
|
|
548
|
+
venv's `.venv/bin/pip` and `-t`/`--target`/`--prefix` don't count), `uv pip
|
|
549
|
+
install --system`, `gem install`, `bundle config set` without `--local`,
|
|
550
|
+
`brew` but its reading verbs, `defaults write`, `cargo install`, `go
|
|
551
|
+
install`/`go env -w`, `pipx`, `uv tool`/`uv python install`, `dotnet tool
|
|
552
|
+
install -g`, `corepack enable`, `rustup`/`pyenv`/`rbenv`/`asdf`/`volta`/`nvm`
|
|
553
|
+
installs and globals, `code --install-extension`, `gh auth`/`config
|
|
554
|
+
set`/`extension install`, `launchctl` but its reading verbs, `git maintenance register`/`start`, `git
|
|
555
|
+
lfs install`, `scalar register`/`clone`; a project
|
|
556
|
+
install such as `npm install` or `bundle install` and build caches don't
|
|
557
|
+
count), and no shell or interpreter that reads its script from stdin: one
|
|
558
|
+
on the receiving end of a pipe whatever its options (`… | sh`, `| bash -x`,
|
|
559
|
+
`| sh -e`, `| python3 -c 'exec(input())'`), `< file`, `bash -s`, `python3 -`,
|
|
560
|
+
or with no script at all: chi can't see that script. A shell's `-e`, `-x`
|
|
561
|
+
and `-o …` are flags, only `-c` (or a cluster holding it, `-ec`, `-cx`)
|
|
562
|
+
hands it a script: the first word after its options (`sh -c -- S`, `bash -c
|
|
563
|
+
-x S`); an interpreter's own code flag does (`python -c`, `ruby -e`,
|
|
564
|
+
`node -e`/`-p`/`--eval=`, `perl -e`/`-E`, `php -r`), alone or in a cluster
|
|
565
|
+
(`ruby -we`, `perl -le`, `node -pe`, `python3 -Ic`, `python3 -c'…'`;
|
|
566
|
+
`PairCommand.cluster_code_at`: a letter that takes the rest of the word,
|
|
567
|
+
`ruby -rset`, holds no code). A script file it names (`sh
|
|
568
|
+
run.sh`, `ruby x.rb`) is not read.
|
|
569
|
+
|
|
570
|
+
Accepted gaps (an accident guard for a trusted team): a script file the turn
|
|
571
|
+
wrote and then runs; inline scripts read only by their text for paths
|
|
572
|
+
(`find … -exec sh -c '…'`, `flock … sh -c`, `awk 'BEGIN{system(…)}'`,
|
|
573
|
+
`deno eval`); a `GIT_CONFIG_*` set by `printf -v` or `read` and exported
|
|
574
|
+
(it affects that command only); a variable outside `COMMAND_ENV` whose
|
|
575
|
+
value a program runs (`LESSOPEN`, a tool's own `*_EDITOR`); `npx -y`,
|
|
576
|
+
`npm exec`, `pnpm dlx`, `uvx`: they fetch into a cache, which doesn't count,
|
|
577
|
+
and run the package as in-project work.
|
|
500
578
|
|
|
501
579
|
Anything else asks: `rm -rf ~/x`, `sed -i … ~/x`, `curl -o ~/x`, `dd of=~/x`,
|
|
502
580
|
`touch`/`mkdir` outside, `perl -pi … ~/x`, `cd .. && make`, `cd && make`, and
|
|
@@ -623,7 +701,8 @@ repo is in count as outside.
|
|
|
623
701
|
`touches: chi_dirs` (shell tools) matches a command that names a path in chi's
|
|
624
702
|
own folders: the config folder (`config.yml`, `memories/`), the hooks folder,
|
|
625
703
|
the approval store, installed bundles (`memories/.bundles`), the session repo's
|
|
626
|
-
git hooks (`core.hooksPath` honoured) or any `.git/hooks`
|
|
704
|
+
git hooks (`core.hooksPath` honoured) or any `.git/hooks` (a submodule's `.git/modules/<name>/hooks` too)
|
|
705
|
+
outside a tmp folder.
|
|
627
706
|
Each word is read as a path against the folder the command is in by then
|
|
628
707
|
(`cd`, `pushd`/`popd` and `( … )` followed), with `~`, `$HOME` and
|
|
629
708
|
`$XDG_CONFIG_HOME`/`$XDG_STATE_HOME` expanded and symlinks resolved. So
|
|
@@ -651,7 +730,9 @@ only `run list|view`, `pr list|view|diff|checks`, `issue list|view` and
|
|
|
651
730
|
at a word's start), an env prefix, `sh -c`, `xargs`, `eval`, `sudo` and the
|
|
652
731
|
like make a command not read-only, as does a `task_create` `env:` that sets `BASH_ENV`, `ENV`, `CDPATH`, `IFS`,
|
|
653
732
|
`SHELLOPTS`, `BASHOPTS`, `BASH_FUNC_…`, `LD_PRELOAD`, `LD_AUDIT`, `DYLD_INSERT_LIBRARIES`, `GIT_EXTERNAL_DIFF`,
|
|
654
|
-
|
|
733
|
+
or a git config variable (`GIT_CONFIG`, `GIT_CONFIG_GLOBAL`, `GIT_CONFIG_SYSTEM`, `GIT_CONFIG_PARAMETERS`,
|
|
734
|
+
`GIT_CONFIG_COUNT`, `GIT_CONFIG_KEY_n`/`VALUE_n`: `ShellGitDirs::CONFIG_ENV`) (they change what its words run; `PATH`
|
|
735
|
+
doesn't count; a pair turn's command that assigns a git config variable is denied like `git -c`). It errs
|
|
655
736
|
towards asking.
|
|
656
737
|
|
|
657
738
|
A tool name may be a glob, so one rule covers a plugin's tools (an MCP server's,
|
|
@@ -1055,8 +1136,22 @@ gate below passes, in this order:
|
|
|
1055
1136
|
it knew the dir: a script or heredoc body (`python3 -c '…'`, `cat >> x.md
|
|
1056
1137
|
<<'EOF'`), a phrase (`echo "- a note"`) or a glob with no `/`. So
|
|
1057
1138
|
`cd memories; cd ..`, `cd "${PWD%/*}"`, `cd $(dirname $PWD)` or `cd -`
|
|
1058
|
-
loses the exemption, and the memories text then refuses it.
|
|
1059
|
-
|
|
1139
|
+
loses the exemption, and the memories text then refuses it. So does a
|
|
1140
|
+
script a shell or interpreter runs that climbs by its own calls
|
|
1141
|
+
(`SelfProtect::SCRIPT_CLIMB`: `os.pardir`, `dirname`, `chdir`, `Dir.pwd`,
|
|
1142
|
+
`Path.parent`, `rindex`, a slice from the end), and one chi can't see: a
|
|
1143
|
+
script file (`cd memories && python3 x.py`) or stdin (`… | python3`,
|
|
1144
|
+
`sh < f`, any interpreter or shell a pipe feeds, `| sh -x` too), by any
|
|
1145
|
+
version's name (`python3.12`, `pypy3`, `Rscript`, `tclsh`), or a program
|
|
1146
|
+
that runs a file (`SelfProtect::FILE_RUNNERS`: `make`, `rake`, `awk -f`,
|
|
1147
|
+
`deno run`, `npm run`). A heredoc only written (`cat >> x.md <<'EOF'`) is
|
|
1148
|
+
data whatever it says. A script body is not looked into beyond its text:
|
|
1149
|
+
an accident guard, not containment (a script can build a path some other
|
|
1150
|
+
way: `p.replace("memories", "config.yml")`, `p[0:len(p)-8]`, `.sub`,
|
|
1151
|
+
`.slice` aren't caught). The price is some harmless commands the user now
|
|
1152
|
+
answers: `python3 scripts/lint.py <memories>/notes.md`, `cat notes.md |
|
|
1153
|
+
python3 -c …` from the memories folder, a climb word written as data by a
|
|
1154
|
+
script (`File.write(".../notes.md", "dirname")`).
|
|
1060
1155
|
A protected rule
|
|
1061
1156
|
(`Verdict::PROTECTED_RULES`) may be listed by name and System 1 may answer
|
|
1062
1157
|
it, but a call that really writes config.yml or the hooks is refused here;
|
|
@@ -45,6 +45,7 @@ Behavior:
|
|
|
45
45
|
- `execute` and `task_create` both spawn with chi's own Ruby/Bundler setup scrubbed (`TaskRuntime.sanitized_spawn_env`): `RUBYOPT` loses its `-r…bundler/setup` flags (others stay) and `RUBYLIB`, `BUNDLE_GEMFILE`, `BUNDLE_BIN_PATH`, `BUNDLER_VERSION` are unset, so a project's `ruby -e` or `bundle exec` uses its own Gemfile when chi runs under `bundle exec`. `task_create` refuses those keys in the model's `env`; `execute`'s `env` comes from chi's callers (`Builtins`, `!cmd`), not the model, and is set over the scrub. An `execute` handed over at its limit keeps the process it spawned, so the task runs with the same env.
|
|
46
46
|
- `execute` and `task_create` export chi's own markers into the command, set over the model's `env` (which can't set them: `Error: env key is reserved`): `SAMAGOTCHI_PARENT_SESSION` (the session's id, `chi` without one; a chi started there answers as a parent agent), `SAMAGOTCHI_SESSION_MODEL` (the resolved ref the session runs on, read at each call so it follows `/model`; unset when unknown) `SAMAGOTCHI_SESSION_THINKING` (the session's own thinking level; unset when it has none) and `SAMAGOTCHI_SESSION_REASONING_HISTORY` (its own reasoning_history level, the same way). `chi self` reads the last three for its model, thinking level and reasoning history. The four are `ChildEnv::SESSION_MARKERS` (`TaskRuntime::MARKER_ENV_KEYS`), which a spawned worker unsets (`ChildEnv.unset`, merged into its spawn env). A session's worker never inherits them from whoever started it (a `chi send` run by another session's `execute`): its other commands (a user's `!cmd`, an MCP server, a context command) have none.
|
|
47
47
|
- They also pass on a set `XDG_STATE_HOME` / `XDG_CONFIG_HOME` made absolute (`Builtins.xdg_absolute`, via `Paths`), so a chi the command runs in another `cwd` (`chi send`) reads this chi's state and config. These aren't reserved: an XDG dir the model sets in `task_create`'s `env` stays its own.
|
|
48
|
+
- Every runner starts from the session's project env, `ProjectEnv.tools_env`: the overlay a spawned worker computed once at boot (`SessionManager.apply_project_env` → `ProjectEnv.compute` → `ProjectEnv.install!`; its folder's `mise env`, and the chi shim's dir first on PATH so `chi` is the chi home's), less `ProjectEnv::TOOLS_DROP` (`RUBYOPT`, `RUBYLIB`, Bundler's: the scrub above sets those). Outside a worker (`ProjectEnv::NONE`) it is `{}`. The sites: `execute` and `task_create` (`Tools::Builtins#project_env`, under the markers and the model's `env`; a participant's turn gets `{}`), the user's `!cmd` (`SessionCommands#shell`) and a context source's command (`ContextFetch#env_for`). MCP servers take `ProjectEnv.current.env` whole under the server's own keys (`bundles/mcp/plugin.rb` `server_env`: no `TOOLS_DROP`, no participant rule). chi's own process env is never changed, and the project env never enters the prompt. User docs: [Project env](../sessions.md#project-env); the launch side: [How chi launches itself](../architecture.md#how-chi-launches-itself).
|
|
48
49
|
|
|
49
50
|
## Finish wake
|
|
50
51
|
|