samagotchi 0.65.0 → 0.66.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 +48 -1
- data/README.md +2 -1
- data/bin/chi +51 -2
- data/docs/cli.md +11 -1
- data/docs/configuration.md +51 -7
- data/docs/context.md +4 -1
- data/docs/internals/engine.md +4 -1
- data/docs/internals/prompt-caching.md +10 -2
- data/docs/memory.md +1 -1
- data/docs/plugins.md +163 -2
- data/docs/releasing.md +1 -1
- data/docs/sessions.md +10 -0
- data/docs/sub-agent.md +3 -1
- data/lib/samagotchi/bundles/claude-peer/manifest.yml +2 -2
- data/lib/samagotchi/bundles/claude-peer/plugin.rb +18 -1
- data/lib/samagotchi/bundles/codex-peer/manifest.yml +21 -0
- data/lib/samagotchi/bundles/codex-peer/plugin.rb +464 -0
- data/lib/samagotchi/bundles/codex-peer/scripts/chi-peer-SKILL.md +131 -0
- data/lib/samagotchi/bundles/codex-peer/scripts/pair +92 -0
- data/lib/samagotchi/bundles/codex-peer/skill_codex_peer.md +68 -0
- data/lib/samagotchi/bundles/coordinator/manifest.yml +5 -5
- data/lib/samagotchi/bundles/coordinator/plugin.rb +24 -2
- data/lib/samagotchi/bundles/dev/manifest.yml +2 -1
- data/lib/samagotchi/bundles/system/delegated.md +2 -1
- data/lib/samagotchi/bundles/system/manifest.yml +3 -3
- data/lib/samagotchi/bundles/system/memory_guide.md +1 -1
- data/lib/samagotchi/cache_confirm.rb +9 -6
- data/lib/samagotchi/commands/registry.rb +25 -8
- data/lib/samagotchi/config.rb +8 -1
- data/lib/samagotchi/engine.rb +112 -4
- data/lib/samagotchi/kernel_loop.rb +9 -1
- data/lib/samagotchi/llm/anthropic_assembly.rb +4 -2
- data/lib/samagotchi/plugin/api.rb +80 -7
- data/lib/samagotchi/plugin/context.rb +22 -1
- data/lib/samagotchi/plugin/loader.rb +3 -2
- data/lib/samagotchi/send_command.rb +23 -7
- data/lib/samagotchi/session.rb +18 -1
- data/lib/samagotchi/session_commands.rb +16 -1
- data/lib/samagotchi/session_manager.rb +4 -1
- data/lib/samagotchi/session_setup.rb +14 -7
- data/lib/samagotchi/system_prompt.rb +16 -1
- data/lib/samagotchi/terminal_ui/attach_launcher.rb +19 -9
- data/lib/samagotchi/terminal_ui.rb +18 -2
- data/lib/samagotchi/tool_runner.rb +13 -1
- data/lib/samagotchi/toolkit.rb +246 -0
- data/lib/samagotchi/toolkit_command.rb +117 -0
- data/lib/samagotchi/tools/registry.rb +43 -6
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +12 -0
- data/lib/samagotchi/web/public/annotate_actions.js +109 -0
- data/lib/samagotchi/web/public/annotate_bar.js +84 -2
- data/lib/samagotchi/web/public/app.js +21 -7
- data/lib/samagotchi/web/public/commands.js +4 -2
- data/lib/samagotchi/web/public/index.html +9 -0
- data/lib/samagotchi/web/public/info_bar.js +5 -3
- data/lib/samagotchi/web/public/session_state.js +3 -0
- data/lib/samagotchi/web/public/stream_handlers.js +13 -0
- data/lib/samagotchi/web/public/toolkit_chip.js +29 -0
- data/lib/samagotchi/worker.rb +2 -0
- metadata +10 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0f78cadf95c501357ce71b15b672dee0a4558016b995a1948532924168580f86
|
|
4
|
+
data.tar.gz: 583f9594eed0ed2febe41390e9c2e0dfbfbde2df72c92f6eab5d8d6a3cf8d776
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 312f72d24bbfc6543d0899f164e69798d068916cef8728242d54893a783c3da7efceb2cbbfeac0337790596984af221e78f5c98541a910945266a2a1122e6ef7
|
|
7
|
+
data.tar.gz: 347d52314752545c6fbc37e7b1a7186faadb6fa2170f91e00b35b8101c76ed9b0ee80b8b954ea927382583c97511dae2fd0a6eca28b694778657f1b654c81226
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,52 @@ and commands may change between minor versions. How releases are made:
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.66.0] - 2026-10-14
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **Plugin annotation actions** (web): `chi.annotation(label, command:, template:, send:)` adds a pill to the
|
|
16
|
+
selection bar that prepares a command line of the plugin from the selected text (`{quote}`, `{text}`, `{source}`):
|
|
17
|
+
it fills the composer (an earlier draft stays under the quote), or with `send: true` sends at once. Up to 3 inline,
|
|
18
|
+
the rest behind `⋯`. See docs/plugins.md, `chi.annotation`.
|
|
19
|
+
- claude-peer 0.2.0: **Confirm with Claude** in the web's selection bar fills `/claude validate` with the quote;
|
|
20
|
+
`/claude validate|context` titles a quote by its first quoted line, not its `From …:` label.
|
|
21
|
+
- The codex-peer bundle (in the dev profile): claude-peer for an OpenAI Codex CLI session. Codex pairs (its
|
|
22
|
+
`chi-peer` skill, installed by `/codex install-skill`), the `codex_send` tool and `/codex validate|context|last`
|
|
23
|
+
send it a part of the work with `codex queue` (killed after 10 s, never a hang), and Codex's answer comes back as
|
|
24
|
+
the `codex-review` context; **Confirm with Codex** in the web's selection bar. Codex's sandbox needs chi's
|
|
25
|
+
context folder in `writable_roots` (the install card shows the line). See docs/plugins.md, "The codex-peer bundle".
|
|
26
|
+
- **Toolkits**: a session's toolkit names the tools its prompts declare. chi ships `full` (every built-in, the
|
|
27
|
+
default), `lean` (the file, shell, memory, task, question, `web_fetch` and `context_read` tools: ~1.4k tokens
|
|
28
|
+
fewer) and `child` (`lean` with `send_note`, without `memory_write`); config's `toolkit.kits` and bundles
|
|
29
|
+
(`chi.toolkit`) add their own. Pick one with `toolkit.default` / `toolkit.child`, `chi --toolkit NAME` (with
|
|
30
|
+
`--tool` / `--no-tool`, also on `chi send --new`) or `/toolkit NAME | add TOOL | remove TOOL`. It is saved in the
|
|
31
|
+
session when it is created and kept by continues and forks; a change applies at the next turn's start. A left-out
|
|
32
|
+
tool is named in one prompt line, and a call to it is refused before the guardrails. The web shows a session not
|
|
33
|
+
on `full` as a `tools` chip. See docs/configuration.md, "Toolkits".
|
|
34
|
+
- Plugins: `ctx.toolkit` and `ctx.use_toolkit(name)` (before the session's first reply). The coordinator bundle
|
|
35
|
+
(0.4.0) declares the `coordinator` toolkit (`lean` + `delegate`, `delegate_result`, `list_sessions`), and
|
|
36
|
+
`/coordinate` puts a fresh session on it.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- A command's name may be followed by any whitespace, a newline too: `/foo` on its own line with text under it
|
|
41
|
+
runs `/foo` with that text, in the terminals and the web (before, only a space did).
|
|
42
|
+
- Web: a command refused as busy mid-turn comes back into the composer (it was cleared when sent).
|
|
43
|
+
- A delegate child is told to report what should change (a wrong memory or skill, instructions that led it astray)
|
|
44
|
+
in its reply instead of writing memories (`delegated.md`).
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- `dialect: fireworks` hosts: the web's ctx meter no longer reads 0% while an answer streams (Fireworks' first stream
|
|
49
|
+
event carries all-zero usage; its counts come at the end).
|
|
50
|
+
|
|
51
|
+
**Still open:** the Claude 5.x "bound to a different conversation" 400 on Anthropic Messages hosts (see 0.65.0); its
|
|
52
|
+
recovery comes in a later release.
|
|
53
|
+
|
|
54
|
+
Update with `chi update` (the system bundle moves with the gem; claude-peer 0.2.0, coordinator 0.4.0, and the dev
|
|
55
|
+
profile 0.5.0 with the new codex-peer 0.1.0).
|
|
56
|
+
|
|
11
57
|
## [0.65.0] - 2026-10-13
|
|
12
58
|
|
|
13
59
|
### Added
|
|
@@ -2833,7 +2879,8 @@ and long-lived sessions.
|
|
|
2833
2879
|
- A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
|
|
2834
2880
|
hotkey panel that send selected text or the clipboard to your sessions.
|
|
2835
2881
|
|
|
2836
|
-
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.
|
|
2882
|
+
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.66.0...HEAD
|
|
2883
|
+
[0.66.0]: https://github.com/dm1try/samagotchi/compare/v0.65.0...v0.66.0
|
|
2837
2884
|
[0.65.0]: https://github.com/dm1try/samagotchi/compare/v0.64.0...v0.65.0
|
|
2838
2885
|
[0.64.0]: https://github.com/dm1try/samagotchi/compare/v0.63.0...v0.64.0
|
|
2839
2886
|
[0.63.0]: https://github.com/dm1try/samagotchi/compare/v0.62.0...v0.63.0
|
data/README.md
CHANGED
|
@@ -153,7 +153,8 @@ in a child session in its own git worktree, check each report and ask you
|
|
|
153
153
|
before each merge; `/children` shows the children and their state. With the
|
|
154
154
|
`claude-peer` bundle (in `dev`), chi sends a part of its work to a Claude Code
|
|
155
155
|
session you pair it with, to validate or as context, and Claude answers back
|
|
156
|
-
into chi (`/claude`)
|
|
156
|
+
into chi (`/claude`); the `codex-peer` bundle (in `dev`) does the same with an
|
|
157
|
+
OpenAI Codex CLI session (`/codex`).
|
|
157
158
|
|
|
158
159
|
`chi send` is the other half: the text goes in as your message, the same as
|
|
159
160
|
typing it in the attached terminal or the web composer, and a turn runs. Piped
|
data/bin/chi
CHANGED
|
@@ -82,7 +82,7 @@ ARGV.each do |arg|
|
|
|
82
82
|
base = arg.split("=", 2).first
|
|
83
83
|
# If dashed version is a known flag (registry or hardcoded), reject underscore variant
|
|
84
84
|
dashed = base.tr("_", "-")
|
|
85
|
-
known = %w[--prompt --resume --attach --shared --no-shared --model --thinking --memory --mute --append-system-prompt --llm-context --llm-context-apply --llm-context-budget --no-interrupt --non-interactive --verbose --no-default-input --port --open --new-token] + Samagotchi::Config.cli_entries.map(&:cli_flag)
|
|
85
|
+
known = %w[--prompt --resume --attach --shared --no-shared --model --thinking --toolkit --tool --no-tool --memory --mute --append-system-prompt --llm-context --llm-context-apply --llm-context-budget --no-interrupt --non-interactive --verbose --no-default-input --port --open --new-token] + Samagotchi::Config.cli_entries.map(&:cli_flag)
|
|
86
86
|
if known.include?(dashed) || known.include?(base)
|
|
87
87
|
warn "Unknown option: #{arg} (did you mean #{dashed}?)"
|
|
88
88
|
exit 1
|
|
@@ -119,6 +119,12 @@ parser = OptionParser.new do |opts|
|
|
|
119
119
|
opts.on("--thinking LEVEL", Samagotchi::Thinking::LEVELS.map(&:to_s), "This session's own thinking level: off|low|medium|high|default (saved in the session, " \
|
|
120
120
|
"before the model's; default unsets it; /thinking changes it). With chi web: the default for the sessions " \
|
|
121
121
|
"its workers run, below a session's own [env SAMAGOTCHI_THINKING_LEVEL]") { |l| options[:thinking] = l }
|
|
122
|
+
opts.on("--toolkit NAME", "This session's toolkit, the tools its prompts declare: full|lean|child|a config or bundle one " \
|
|
123
|
+
"(saved in the session; config toolkit.default; /toolkit changes it)") { |n| options[:toolkit] = n }
|
|
124
|
+
opts.on("--tool NAME", "Also declare this tool on top of the toolkit (repeatable; a comma list too)") { |t| (options[:tools] ||= []) << t }
|
|
125
|
+
# --no-tool is taken out of ARGV before parse! (OptionParser would read it
|
|
126
|
+
# as --tool's negation); this is its help line.
|
|
127
|
+
opts.separator " --no-tool NAME Leave this tool out of the toolkit (repeatable; a comma list too)"
|
|
122
128
|
opts.on("--memory NAME", "Preload a memory entry into the system prompt (repeatable; a comma list too)") { |m| (options[:memories] ||= []) << m }
|
|
123
129
|
opts.on("--append-system-prompt FILE", "Put FILE's text into the system prompt, after AGENTS.md (repeatable; saved in the session; " \
|
|
124
130
|
"config append_system_prompt: is the baseline)") { |f| (options[:append_system_prompt] ||= []) << f }
|
|
@@ -171,6 +177,34 @@ parser = OptionParser.new do |opts|
|
|
|
171
177
|
opts.on("--version", "Print chi's version") { puts "chi #{Samagotchi::VERSION}"; exit }
|
|
172
178
|
opts.on("-h", "--help", "Show help") { puts opts; exit }
|
|
173
179
|
end
|
|
180
|
+
# --no-tool NAME (or --no-tool=NAME), up to a "--". A flag that takes a
|
|
181
|
+
# value (-p, --model …: the parser's own list) keeps its next word, so
|
|
182
|
+
# `chi -p "--no-tool=x is broken"` is a prompt. A tool name never starts
|
|
183
|
+
# with "-": `--no-tool --` or `--no-tool --model` is a missing argument.
|
|
184
|
+
takes_value = parser.instance_variable_get(:@stack).flat_map do |list|
|
|
185
|
+
list.list.select { |switch| switch.is_a?(OptionParser::Switch::RequiredArgument) }.flat_map { |switch| switch.long + switch.short }
|
|
186
|
+
end
|
|
187
|
+
missing_no_tool = lambda do
|
|
188
|
+
warn "Error: missing argument: --no-tool (see chi --help)"
|
|
189
|
+
exit 1
|
|
190
|
+
end
|
|
191
|
+
index = 0
|
|
192
|
+
while index < ARGV.size && ARGV[index] != "--"
|
|
193
|
+
arg = ARGV[index]
|
|
194
|
+
if arg == "--no-tool"
|
|
195
|
+
value = ARGV[index + 1]
|
|
196
|
+
missing_no_tool.call if value.nil? || value.start_with?("-")
|
|
197
|
+
(options[:no_tools] ||= []) << ARGV.delete_at(index + 1)
|
|
198
|
+
ARGV.delete_at(index)
|
|
199
|
+
elsif arg.start_with?("--no-tool=")
|
|
200
|
+
value = arg.delete_prefix("--no-tool=")
|
|
201
|
+
missing_no_tool.call if value.empty? || value.start_with?("-")
|
|
202
|
+
options[:no_tools] = (options[:no_tools] || []) << value
|
|
203
|
+
ARGV.delete_at(index)
|
|
204
|
+
else
|
|
205
|
+
index += takes_value.include?(arg) ? 2 : 1
|
|
206
|
+
end
|
|
207
|
+
end
|
|
174
208
|
begin
|
|
175
209
|
parser.parse!
|
|
176
210
|
rescue OptionParser::ParseError => e
|
|
@@ -327,6 +361,17 @@ if options[:append_system_prompt]
|
|
|
327
361
|
end.uniq
|
|
328
362
|
end
|
|
329
363
|
|
|
364
|
+
# --toolkit/--tool/--no-tool: a new session's toolkit, as named (the
|
|
365
|
+
# worker resolves it; a bundle's toolkit isn't known here).
|
|
366
|
+
if options[:toolkit] || options[:tools] || options[:no_tools]
|
|
367
|
+
require "samagotchi/toolkit"
|
|
368
|
+
# The flags as given, for the line an existing session's run prints.
|
|
369
|
+
options[:toolkit_flags] = [("--toolkit" if options[:toolkit]), ("--tool" if options[:tools]),
|
|
370
|
+
("--no-tool" if options[:no_tools])].compact
|
|
371
|
+
options[:toolkit_value] = Samagotchi::Toolkit.from_flags(name: options[:toolkit], add: options[:tools],
|
|
372
|
+
remove: options[:no_tools])
|
|
373
|
+
end
|
|
374
|
+
|
|
330
375
|
chi_update_hint.call
|
|
331
376
|
require "samagotchi/launch_mode"
|
|
332
377
|
launch, launch_note = if scratch
|
|
@@ -361,7 +406,9 @@ if launch == :attached
|
|
|
361
406
|
muted_memories: options[:muted] || [],
|
|
362
407
|
llm_context: options[:llm_context] || {},
|
|
363
408
|
thinking: options[:thinking],
|
|
364
|
-
appended_prompt_files: options[:appended_prompt_files] || []
|
|
409
|
+
appended_prompt_files: options[:appended_prompt_files] || [],
|
|
410
|
+
toolkit: options[:toolkit_value],
|
|
411
|
+
toolkit_flags: options[:toolkit_flags] || [])
|
|
365
412
|
rescue Samagotchi::TerminalUI::AttachLauncher::Error => e
|
|
366
413
|
warn "Error: #{e.message}"
|
|
367
414
|
exit 1
|
|
@@ -381,6 +428,8 @@ begin
|
|
|
381
428
|
llm_context: options[:llm_context] || {},
|
|
382
429
|
thinking: options[:thinking],
|
|
383
430
|
appended_prompt_files: options[:appended_prompt_files] || [],
|
|
431
|
+
toolkit: options[:toolkit_value],
|
|
432
|
+
toolkit_flags: options[:toolkit_flags] || [],
|
|
384
433
|
non_interactive: options[:non_interactive],
|
|
385
434
|
model_name: options[:model],
|
|
386
435
|
scratch: scratch
|
data/docs/cli.md
CHANGED
|
@@ -187,6 +187,7 @@ controls exit behavior (`--non-interactive`); `--resume` composes with both.
|
|
|
187
187
|
| `--attach SESSION_ID` | Attach to a session's worker, waking one if it has exited. |
|
|
188
188
|
| `--model NAME` | Use this model for the run (overrides the configured default and a resumed session's model). |
|
|
189
189
|
| `--thinking LEVEL` | This session's own thinking level: `off`, `low`, `medium` or `high`, saved in the session and first in the order (`default` unsets it). On `--resume` or `--attach` it is set at once, through a `/thinking` on an attached worker. With `chi web` it is instead the default for the sessions its workers run, below a session's own (as env `SAMAGOTCHI_THINKING_LEVEL` is). See "Thinking" in configuration.md. |
|
|
190
|
+
| `--toolkit NAME`, `--tool NAME`, `--no-tool NAME` | A new session's toolkit, the tools its prompts declare: `full` (every built-in), `lean`, `child`, one of config's `toolkit.kits` or a bundle's (without `--toolkit`: config's `toolkit.default`), with tools declared on top of it (`--tool`) or left out (`--no-tool`); both repeatable, a comma list too. Saved in the session as given; a respawned worker, a fork and a continue keep it. Ignored with `--attach`/`--resume` (one line, as `--memory`); `/toolkit` changes it. Not on `chi web` (config's `toolkit.default` covers its sessions). See [Toolkits](configuration.md#toolkits). |
|
|
190
191
|
| `--profile NAME` | Prompt profile (`qwen36` or `gemma4`) for every model in this run, over config and the server's template (same as `--model-profile`, env `SAMAGOTCHI_MODEL_PROFILE`). See "Prompt profile" in configuration.md. |
|
|
191
192
|
| `--memory NAME` | Preload a memory entry into the system prompt (repeatable; a comma list too). Merged under the config.yml `memories:` baseline. Works attached: the list is stored on the session, so its worker builds the same prompt on every respawn. |
|
|
192
193
|
| `--append-system-prompt FILE` | Put `FILE`'s text into the system prompt, after AGENTS.md and before the memory indexes (repeatable; relative to the shell's directory). Added after the config.yml `append_system_prompt:` list. A missing or unreadable file is an error, exit 1. Saved in the session, so a respawned worker, a fork and a continue keep it; ignored with `--attach`/`--resume` (one line, as `--memory`). See [Appended prompt files](configuration.md#appended-prompt-files). |
|
|
@@ -513,7 +514,16 @@ reasoning of the turn it continues. `/stats` shows it on its `reasoning history:
|
|
|
513
514
|
line, the web as the `reasoning` chip. See "Reasoning history" in
|
|
514
515
|
[configuration.md](configuration.md).
|
|
515
516
|
|
|
516
|
-
|
|
517
|
+
`/toolkit` lists the tools the next turn declares (each with its estimated
|
|
518
|
+
tokens), the session's toolkit and the tools it leaves out. `/toolkit NAME` puts
|
|
519
|
+
the session on another toolkit (dropping its own adds and removes); `/toolkit
|
|
520
|
+
add TOOL` and `/toolkit remove TOOL` change one tool (a tool's name keeps its
|
|
521
|
+
case). The change is saved at once and applies from the next turn's start; a
|
|
522
|
+
running turn keeps its tools. `/toolkit` alone answers mid-turn too; a change
|
|
523
|
+
sent while a turn runs waits for the turn's end. The web shows a session that
|
|
524
|
+
isn't on `full` as the `tools` chip. See [Toolkits](configuration.md#toolkits).
|
|
525
|
+
|
|
526
|
+
A `/thinking`, `/reasoning-history` or `/toolkit` change that restarts the prompt cache
|
|
517
527
|
asks first: the reply says what it costs, changes nothing, and names the line
|
|
518
528
|
that applies it (`/thinking low --yes`). `--yes` (or `-y`) applies it at once.
|
|
519
529
|
The terminal puts that line in the next input (Enter applies it); the web's
|
data/docs/configuration.md
CHANGED
|
@@ -319,11 +319,11 @@ hosts:
|
|
|
319
319
|
|
|
320
320
|
`auth:` works on any host (`bearer` is the default everywhere but `api: anthropic`).
|
|
321
321
|
|
|
322
|
-
**Known limit (until
|
|
322
|
+
**Known limit (until a later release):** Claude 5.x models (Opus, Sonnet and Haiku 5.5, Fable 5.1) bind each
|
|
323
323
|
thinking block to the request it came from. When the history before a block changes (the system prompt rebuilt after a
|
|
324
324
|
worker restart or a `/model` switch, a changed tool set, the forget layer's edits), such a model may answer HTTP 400
|
|
325
325
|
"… bound to a different conversation" and the turn fails with that error; `/reasoning-history turn` makes it rarer.
|
|
326
|
-
|
|
326
|
+
A later release sends Anthropic's binding controls and recovers from that 400. Fireworks, Splash and other
|
|
327
327
|
non-Claude models are not affected.
|
|
328
328
|
|
|
329
329
|
The loop, tools, events, hooks and UIs are the chat loop's, as for `api: openai`; only the wire differs: the
|
|
@@ -739,10 +739,10 @@ models:
|
|
|
739
739
|
|
|
740
740
|
## Changes that restart the prompt cache
|
|
741
741
|
|
|
742
|
-
`/thinking
|
|
743
|
-
thinking switch, a remote chat host's thinking fields ("Thinking"),
|
|
744
|
-
has thinking ("Reasoning history")
|
|
745
|
-
session has a model reply.
|
|
742
|
+
`/thinking`, `/reasoning-history` and `/toolkit` ask before a change that makes the server read the conversation again:
|
|
743
|
+
Gemma's thinking switch, a remote chat host's thinking fields ("Thinking"), a change to or from `all` once an earlier
|
|
744
|
+
reply has thinking ("Reasoning history"), and any change to the declared tools, which start every request
|
|
745
|
+
("Toolkits"). They ask only when what the next turn runs under really changes and the session has a model reply.
|
|
746
746
|
|
|
747
747
|
- The reply says what it costs and changes nothing yet: `Nothing changed yet: send /thinking low --yes to apply.`
|
|
748
748
|
- `--yes` (or `-y`, anywhere after the command) applies it at once.
|
|
@@ -1276,6 +1276,48 @@ work tree top when inside it, else with `~`.
|
|
|
1276
1276
|
picked up by a new process or a rebuild (`/model`). `/stats` lists the files
|
|
1277
1277
|
in the prompt on its `prompt files:` line.
|
|
1278
1278
|
|
|
1279
|
+
## Toolkits
|
|
1280
|
+
|
|
1281
|
+
A session's **toolkit** names the tools its prompts declare. The declared tools are the front of every request (the
|
|
1282
|
+
`tools` field, or the native template's declarations), so a session that declares fewer sends a shorter prompt, and
|
|
1283
|
+
sessions on the same toolkit share that prefix in a prompt cache.
|
|
1284
|
+
|
|
1285
|
+
```yaml
|
|
1286
|
+
toolkit:
|
|
1287
|
+
default: lean # the sessions you start (unset: full)
|
|
1288
|
+
child: child # delegate children (unset: full)
|
|
1289
|
+
kits: # your own, on a base
|
|
1290
|
+
review: { base: lean, add: [web_fetch, delegate], remove: [memory_write] }
|
|
1291
|
+
```
|
|
1292
|
+
|
|
1293
|
+
| toolkit | declares |
|
|
1294
|
+
|---|---|
|
|
1295
|
+
| `full` | every built-in; the default |
|
|
1296
|
+
| `lean` | `execute`, `read`, `write`, `edit`, `memory_read`, `memory_write`, `task_create`, `task_wait`, `task_get`, `task_list`, `task_stop`, `ask_user_question`, `web_fetch`, `context_read` |
|
|
1297
|
+
| `child` | `lean` plus `send_note`, without `memory_write` |
|
|
1298
|
+
| `coordinator` | `lean` plus `delegate`, `delegate_result`, `list_sessions` (declared by the coordinator bundle) |
|
|
1299
|
+
|
|
1300
|
+
- **Plugin tools** are declared in every toolkit unless its `remove:` names them, and so is `forget_outputs` under
|
|
1301
|
+
the `forget` layer. A `kits:` entry takes `base:` (default `full`), `add:` and `remove:`; it can't reuse a
|
|
1302
|
+
built-in's name. A bundle declares its own with `chi.toolkit` ([plugins](plugins.md#chitoolkitname-base-add--remove-)).
|
|
1303
|
+
- **Saved at creation, never re-resolved from config.** A session stores its toolkit as named
|
|
1304
|
+
(`"toolkit": {"name": "lean", "add": [], "remove": []}`) when it is created: `toolkit.default`, `toolkit.child`
|
|
1305
|
+
for a delegate child, or `chi --toolkit NAME` / `--tool` / `--no-tool` ([cli](cli.md)). Changing `toolkit.default`
|
|
1306
|
+
doesn't move existing sessions, not even when their worker is woken again; a session from before toolkits is on
|
|
1307
|
+
`full`. A continue and a fork keep the toolkit. `/toolkit` shows and changes it.
|
|
1308
|
+
- **Not loaded.** When a toolkit leaves tools out, one line after the base prompt names them (`Not loaded in this
|
|
1309
|
+
session (toolkit lean): …. Only the user can load one (/toolkit add <name>).`). A call to one is refused before
|
|
1310
|
+
the guardrails (no approval card): `Error: delegate isn't loaded in this session (toolkit lean) …`.
|
|
1311
|
+
- **Applied at a turn's start.** A change (`/toolkit`, a plugin's `ctx.use_toolkit`) is saved at once and the next
|
|
1312
|
+
turn declares it; once the session has a model reply, `/toolkit` asks first ("Changes that restart the prompt
|
|
1313
|
+
cache").
|
|
1314
|
+
- **A name that doesn't resolve** (a typo, a bundle that was uninstalled) declares every tool for that process,
|
|
1315
|
+
with one `toolkit>` warning at the first turn; the saved name stays, so the bundle's return brings it back.
|
|
1316
|
+
- **Versions.** A toolkit's definition can change with chi or a bundle, which changes what saved sessions on it
|
|
1317
|
+
declare after an upgrade: the next request reads the prompt again once, as a tool's schema change does. So can
|
|
1318
|
+
an edit to `toolkit.kits`: a session's worker reads the kits once when it starts, so a running session keeps its
|
|
1319
|
+
tools and the edit reaches it at its next worker start (after an idle exit, `chi sessions restart`).
|
|
1320
|
+
|
|
1279
1321
|
## LLM context: the forget layer
|
|
1280
1322
|
|
|
1281
1323
|
Experimental, off by default. With `forget` in a model's `llm_context.strategy` (next to `stale`), chi offers that
|
|
@@ -1415,7 +1457,7 @@ Every setting below takes the three forms described in
|
|
|
1415
1457
|
marked config.yml only) and, where the CLI
|
|
1416
1458
|
column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
|
|
1417
1459
|
`model_aliases:`, `hooks:`, `guardrails:` rules, `bundles:`, `memories:`,
|
|
1418
|
-
`append_system_prompt
|
|
1460
|
+
`append_system_prompt:`, `toolkit.kits`) are described in their own sections.
|
|
1419
1461
|
|
|
1420
1462
|
| Setting | Default | CLI | What it does |
|
|
1421
1463
|
|---|---|---|---|
|
|
@@ -1483,6 +1525,8 @@ column says so, a `--kebab-name` flag. The maps (`hosts:`, `models:`,
|
|
|
1483
1525
|
| `context.status_thresholds` | `20,40,60,80` | yes | Percentages that put the estimate into a bucket; the rise into a guidance bucket (from the second up) leaves the model its own `[CONTEXT: …]` line, and the bucket decides the guidance. |
|
|
1484
1526
|
| `context.every_seconds` | `300` | | How often an attached context source's command runs when it has no `--every` (seconds, at least 30). See [Attached context](context.md). |
|
|
1485
1527
|
| `context.wake` | `true` | yes | A source whose update says `wake: true` may start a turn in a live, idle session (one per source per 10 minutes, within `session.max_wakes`); so may a session's pushed source's first push. `false`: updates wait as notes for the next turn. See [Waking](context.md#waking). |
|
|
1528
|
+
| `toolkit.default` | `full` | | The toolkit a new session is stored on: the tools its prompts declare (`full`, `lean`, `child`, one of `toolkit.kits` or a bundle's). `chi --toolkit` picks one per session. See "Toolkits". |
|
|
1529
|
+
| `toolkit.child` | `full` | | A delegate child's toolkit. See "Toolkits". |
|
|
1486
1530
|
| `thinking.turn_preamble` | `true` | yes | Ask a `qwen36` model to open its thinking with a short `TURN:` line (the step label). |
|
|
1487
1531
|
| `reasoning_history` | `turn` | | Which of its reasoning a chat host's model gets back: `drop`, `turn`, `all`; environment only (`SAMAGOTCHI_REASONING_HISTORY`), below a session's own level (`/reasoning-history`), above the `models:`/`hosts:` entries. See "Reasoning history". |
|
|
1488
1532
|
| `thinking.level` | `default` | `--thinking` | `off`, `low`, `medium`, `high` or `default` for every model; the flag and env outrank the `models:`/`hosts:` entries, the file's value doesn't; a session's own level outranks them all, and `--thinking` sets it on a session run (on `chi web` it sets this). See "Thinking". |
|
data/docs/context.md
CHANGED
|
@@ -126,7 +126,10 @@ pbpaste | chi context push clip --project
|
|
|
126
126
|
A Claude Code session can be a pushed source too: the
|
|
127
127
|
[claude-peer bundle](plugins.md#the-claude-peer-bundle)'s `pair` script attaches
|
|
128
128
|
`claude-inbox` (Claude's inbox socket as the hint) and `claude-review`, where
|
|
129
|
-
Claude pushes its answers as JSON with `wake: true`.
|
|
129
|
+
Claude pushes its answers as JSON with `wake: true`. The
|
|
130
|
+
[codex-peer bundle](plugins.md#the-codex-peer-bundle)'s does the same for a
|
|
131
|
+
Codex session: `codex-inbox` (Codex's thread id as the hint) and
|
|
132
|
+
`codex-review`.
|
|
130
133
|
|
|
131
134
|
### A stream: chat, a log, a feed
|
|
132
135
|
|
data/docs/internals/engine.md
CHANGED
|
@@ -35,7 +35,8 @@ Engine#run_turn(session, prompt, on_event:, max_iterations:, cancel_controller:,
|
|
|
35
35
|
│ # images (only when there are any), cut_after (the model's cut wait, s)}
|
|
36
36
|
│ # refresh_profile! (may ask the server's /props once)
|
|
37
37
|
│ # turn.settings = LLM::TurnSettings (vision, sampling, thinking; model_name is set in generate)
|
|
38
|
-
│ # announces guardrail load failures, starts and awaits plugin init tasks, applies staged tools
|
|
38
|
+
│ # announces guardrail load failures, starts and awaits plugin init tasks, applies staged tools,
|
|
39
|
+
│ # then the session's toolkit (apply_toolkit!: Registry#toolkit, a prompt rebuild when it changed)
|
|
39
40
|
│ # fires the :session_start hook on the Engine's first turn, then the :before_turn hook
|
|
40
41
|
│ # (a frozen copy of the history, the prompt, and resumed)
|
|
41
42
|
│ # turn_messages: system head + history + due reminders (:reminder_injected) + the user message
|
|
@@ -227,6 +228,8 @@ One iteration with tool calls:
|
|
|
227
228
|
→ :tool_dispatch_started {iteration, call_count}
|
|
228
229
|
→ for each call: ToolRunner#run(call, iteration:, call_index:, call_count:,
|
|
229
230
|
on_stream_event:, max_tool_output_chars:)
|
|
231
|
+
→ a tool the session's toolkit leaves out (Registry#left_out) skips the gate: no hooks, no card;
|
|
232
|
+
KernelLoop#dispatch answers it with the not-loaded error
|
|
230
233
|
→ Guardrails::Gate#evaluate (the before_tool_call hooks, then the core checks)
|
|
231
234
|
→ :tool_call_started
|
|
232
235
|
→ an ask verdict: Gate#settle_ask (asks the user, records waited_ms)
|
|
@@ -22,11 +22,13 @@ right-hand column happens.
|
|
|
22
22
|
```
|
|
23
23
|
shared by every session ────────────────────────────────────────────────── breaks it
|
|
24
24
|
┌──────────────────────────────────────────────────────────────────────────┐
|
|
25
|
-
│ tools built-ins, then plugin tools sorted by (bundle, name)
|
|
26
|
-
│
|
|
25
|
+
│ tools built-ins, then plugin tools sorted by (bundle, name), │ a tool added/changed,
|
|
26
|
+
│ those the session's toolkit declares (per toolkit) │ /model, a profile change,
|
|
27
|
+
│ (chat: the `tools` field; native: the model's template) │ a toolkit change
|
|
27
28
|
├──────────────────────────────────────────────────────────────────────────┤
|
|
28
29
|
│ SYSTEM, stable part SystemPrompt#system_prompt_with_index
|
|
29
30
|
│ [native thinking token] base prompt │
|
|
31
|
+
│ not-loaded line (only when the toolkit leaves tools out) │
|
|
30
32
|
│ rg guidance │ /thinking level,
|
|
31
33
|
│ system identity (+ model overlay) │ /model, editing a
|
|
32
34
|
│ model notes (model_notes_* matching the model) │ memory / AGENTS.md
|
|
@@ -57,6 +59,12 @@ right-hand column happens.
|
|
|
57
59
|
- The system prompt is built **once per loop** and rebuilt only on `@prompt_builder.reset!`: a model switch
|
|
58
60
|
(`Engine#switch_model!`), a plugin's `tools_changed!`, or a profile change. A per-turn rebuild would
|
|
59
61
|
break everything below it.
|
|
62
|
+
- The tools layer is shared **per toolkit** (configuration.md, "Toolkits"): sessions on the same toolkit share it,
|
|
63
|
+
and a chain's links keep theirs. A session's toolkit is saved when it is created and never resolved again from
|
|
64
|
+
config. A change (`/toolkit`, `ctx.use_toolkit`) is applied only at a turn's start (`Engine#apply_toolkit!`,
|
|
65
|
+
beside `apply_staged_tools!`, then `reset!`): the chat loop builds `tools:` per request, so a mid-turn change
|
|
66
|
+
would differ from the turn's prompt. Under `full` nothing is filtered and no line is added: the bytes are what
|
|
67
|
+
they were before toolkits.
|
|
60
68
|
- On remote Claude, `LLM::PromptCache.mark` sends the system message as two text parts. The stable part carries
|
|
61
69
|
breakpoint #1, so a **new session** reads it from the cache. The last non-system message carries breakpoint
|
|
62
70
|
#2, so the **next request in the session** reads everything up to it. Mid and tail system messages never take
|
data/docs/memory.md
CHANGED
|
@@ -270,7 +270,7 @@ profiles, meta bundles that install a set of them:
|
|
|
270
270
|
| profile | bundles |
|
|
271
271
|
|---|---|
|
|
272
272
|
| `core` | loop-guard, check-in, guardrails, slow-commands, agents-md: the recommended safety set |
|
|
273
|
-
| `dev` | known-names, mcp, btw, skills, source-links, github-pr, coordinator, claude-peer |
|
|
273
|
+
| `dev` | known-names, mcp, btw, skills, source-links, github-pr, coordinator, claude-peer, codex-peer |
|
|
274
274
|
|
|
275
275
|
`chi bootstrap` installs `core` and, on a terminal, offers `dev`. An existing
|
|
276
276
|
install gets neither by itself: `chi bundle install core` (or `dev`).
|
data/docs/plugins.md
CHANGED
|
@@ -92,10 +92,14 @@ A line that is not a known command keeps its old meaning: in the terminal
|
|
|
92
92
|
UIs `/foo` goes to the model as a prompt; the web refuses it and names the
|
|
93
93
|
commands it knows.
|
|
94
94
|
|
|
95
|
+
Any whitespace may follow the name, a newline too: `/hello` on its own line
|
|
96
|
+
with text under it runs `/hello` with that text as `args` (its inner lines,
|
|
97
|
+
blank lines and indentation as typed; only the ends are stripped).
|
|
98
|
+
|
|
95
99
|
#### `anytime: true`
|
|
96
100
|
|
|
97
101
|
A plugin's normal command waits for its turn: typed while a turn runs, it is
|
|
98
|
-
refused as busy (the REPL puts it back into the prompt; a plugin command can't
|
|
102
|
+
refused as busy (the REPL puts it back into the prompt, the web into an empty composer; a plugin command can't
|
|
99
103
|
ask to run after the turn, as the built-in `/model X` does). An `anytime: true` command
|
|
100
104
|
runs **at once, on its own thread, beside the running turn**, and the turn
|
|
101
105
|
goes on:
|
|
@@ -144,6 +148,54 @@ chi's own, each bundle's with the bundle's name, and the terminal UIs' own
|
|
|
144
148
|
`attached only`, `repl only`, or `terminal and web only` (`/cut`).
|
|
145
149
|
It works in all three UIs.
|
|
146
150
|
|
|
151
|
+
### `chi.annotation(label, command:, template: "{quote}", send: false)`
|
|
152
|
+
|
|
153
|
+
This adds a pill to the web's selection bar. Selecting text in an answer, a
|
|
154
|
+
thinking block, a step, a tool row or your own message shows
|
|
155
|
+
`[ Annotate | <presets> ┊ <plugin pills> ]`; a plugin pill prepares a command
|
|
156
|
+
line of the plugin from the selection:
|
|
157
|
+
|
|
158
|
+
```ruby
|
|
159
|
+
chi.command "/claude", "…", anytime: true do |args, ctx| … end
|
|
160
|
+
chi.annotation "Confirm with Claude", command: "/claude validate"
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Clicking it on a thinking line fills the composer with
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
/claude validate
|
|
167
|
+
From your thinking:
|
|
168
|
+
> the quoted line
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
and the caret goes under the quote, for your note; Send runs `/claude` with
|
|
173
|
+
everything after the name as `args`.
|
|
174
|
+
|
|
175
|
+
- `label`: the pill's text, 1–40 characters. Its tooltip adds the bundle.
|
|
176
|
+
- `command:`: `/name` or `/name words`, and `/name` must be a command **this
|
|
177
|
+
plugin** registers (checked once `register` returns, so the order of the
|
|
178
|
+
calls doesn't matter; another one's is a load error). At most 5 per plugin.
|
|
179
|
+
- `template:` goes on the lines under the command line, filled in the
|
|
180
|
+
browser: `{quote}` the labelled quote block Annotate makes, `{text}` the
|
|
181
|
+
selection alone, `{source}` where it came from (`answer`, `thinking`,
|
|
182
|
+
`step`, `tool`, `user`). Another `{x}` stays as written. Each line's
|
|
183
|
+
trailing blanks and the blank lines at either end are dropped, and
|
|
184
|
+
indentation is kept (code stays indented).
|
|
185
|
+
- **Fill** (the default) never sends: an empty composer gets the line; a
|
|
186
|
+
composer that already starts with the same command line gets the quote
|
|
187
|
+
appended (several selections collect under one command); any other draft
|
|
188
|
+
stays, under the command line and the quote.
|
|
189
|
+
- `send: true` sends the line at once (the pill is marked ↗). While a turn
|
|
190
|
+
runs, such a pill is disabled unless its command is `anytime`: it would
|
|
191
|
+
come back busy. A fill pill always works; a typed command refused as busy
|
|
192
|
+
comes back into the composer.
|
|
193
|
+
|
|
194
|
+
Pills ride on their command's listing (the worker's snapshot): a muted
|
|
195
|
+
bundle's go with its command, and a session's pills need a worker started
|
|
196
|
+
after the bundle was installed. Up to 3 show inline, the rest behind `⋯`.
|
|
197
|
+
Web only: the terminals type the command (`/claude validate <text>`).
|
|
198
|
+
|
|
147
199
|
### `chi.tool(name, description, params:, schema:, label:, preview:, targets:, refs:) { |args, ctx| … }`
|
|
148
200
|
|
|
149
201
|
This adds a tool the model can call. It is declared in the system prompt and
|
|
@@ -512,6 +564,21 @@ end
|
|
|
512
564
|
- Each task runs once per session start. A `-p … --non-interactive` run
|
|
513
565
|
starts them with its turn and shows nothing but the answer.
|
|
514
566
|
|
|
567
|
+
### `chi.toolkit(name, base:, add: [], remove: [])`
|
|
568
|
+
|
|
569
|
+
A toolkit sessions can be put on: `base`'s tools (`full`, `lean`, `child`, a
|
|
570
|
+
config or bundle toolkit) with `add` declared and `remove` left out. Users pick
|
|
571
|
+
it like chi's own (`chi --toolkit NAME`, `toolkit.default`, `/toolkit NAME`),
|
|
572
|
+
and the plugin with `ctx.use_toolkit` (below). Names are `a-z`, `0-9`, `_` and
|
|
573
|
+
`-`; one chi has (`full`, `lean`, `child`) or another bundle declared is a
|
|
574
|
+
load error. A session saved on a toolkit whose bundle is gone declares every
|
|
575
|
+
tool (full) until it comes back, with a warning at its first turn. See
|
|
576
|
+
[Toolkits](configuration.md#toolkits).
|
|
577
|
+
|
|
578
|
+
```ruby
|
|
579
|
+
chi.toolkit "review", base: "lean", add: %w[delegate delegate_result]
|
|
580
|
+
```
|
|
581
|
+
|
|
515
582
|
### `chi.ctx`
|
|
516
583
|
|
|
517
584
|
The plugin's context (the `ctx` its handlers get), for `register` itself:
|
|
@@ -564,6 +631,9 @@ full label, `plugin.rb (bundle my-bundle)`.
|
|
|
564
631
|
| `ctx.stop_generation(reason)` | cut the generation that is streaming: the turn goes on and the model is asked again ([hooks.md](hooks.md#watching-the-stream)). Shows nothing: post your own notice. True when it cut one now |
|
|
565
632
|
| `ctx.ask_model(messages:, prompt:, …)` | a side answer from the session's model: see [Side answers](#side-answers-ctxask_model) |
|
|
566
633
|
| `ctx.sessions` | fork, send to, read, list and stop other sessions: see [Other sessions](#other-sessions-ctxsessions) |
|
|
634
|
+
| `ctx.toolkit` | the name of the session's toolkit (`full` for a session from before toolkits); nil with no session |
|
|
635
|
+
| `ctx.declared_tools` | the tool names the session's next turn declares (its toolkit resolved, with its own adds and removes): ask this, not the name, whether a tool is there |
|
|
636
|
+
| `ctx.use_toolkit(name)` | put the session on toolkit `name` (yours from `chi.toolkit`, or chi's): saved, applied at the next turn's start and announced (`toolkit> toolkit: NAME from the next turn's start`, the web's tools chip follows). Only before the session's first model reply and outside a turn (the declared tools start every request, and a plugin doesn't ask first); otherwise it returns false and changes nothing: tell the user to run `/toolkit NAME`. An unknown name raises ArgumentError. Needs chi 0.66.0 |
|
|
567
637
|
| `ctx.context` | attach outside text to this session and list it: see [Attached context](#attached-context-ctxcontext) |
|
|
568
638
|
|
|
569
639
|
The Engine itself is never handed to a plugin.
|
|
@@ -1486,6 +1556,13 @@ by themselves there.
|
|
|
1486
1556
|
report ending with the `/coordinate resume <slug>` to pick it up next
|
|
1487
1557
|
time. It starts, merges and stops nothing: running children stay running
|
|
1488
1558
|
and are named in the report.
|
|
1559
|
+
- **The coordinator toolkit.** The bundle declares `coordinator`: `lean`
|
|
1560
|
+
plus `delegate`, `delegate_result` and `list_sessions`
|
|
1561
|
+
([Toolkits](configuration.md#toolkits)). `/coordinate` puts a fresh session
|
|
1562
|
+
on it before it asks the model (`ctx.use_toolkit`). When chi refuses (a reply
|
|
1563
|
+
or a turn already there) and the session's toolkit has no `delegate`, the
|
|
1564
|
+
reply says to run `/toolkit coordinator`. A continue of a coordinator link
|
|
1565
|
+
keeps the toolkit.
|
|
1489
1566
|
- **`/children [all]`** shows the session's children as a card, newest first,
|
|
1490
1567
|
one line each: `` `ab12cd34` · running · fix/flaky · "fix the flaky spec" ``,
|
|
1491
1568
|
`` `9a8b7c6d` · done · feat/x · reported · "All 12 specs pass" `` (`not
|
|
@@ -1566,7 +1643,13 @@ Claude pairs; without one the bundle costs one tool declaration.
|
|
|
1566
1643
|
file is there (it goes when that Claude session ends), the last send, and
|
|
1567
1644
|
Claude's last answer (the `claude-review` summary).
|
|
1568
1645
|
- `/claude validate <text>` and `/claude context <text>` send the text as it
|
|
1569
|
-
is, titled by its first line
|
|
1646
|
+
is, titled by its first line (past a web quote's `From …:` label and a
|
|
1647
|
+
quoted line's `> `): paste a selection after them.
|
|
1648
|
+
- In the web, selecting text offers **Confirm with Claude**
|
|
1649
|
+
([`chi.annotation`](#chiannotationlabel-command-template-quote-send-false)):
|
|
1650
|
+
it fills the composer with `/claude validate` and the quote, for you to
|
|
1651
|
+
add what needs confirming. On a chi without `chi.annotation` (0.62–0.65)
|
|
1652
|
+
the bundle loads without the pill.
|
|
1570
1653
|
- `/claude last` sends the model's last completed answer as context. Mid-turn
|
|
1571
1654
|
it is the answer before the running turn, never the turn's partial text;
|
|
1572
1655
|
a cancelled or failed turn has no answer, so the one before it goes.
|
|
@@ -1598,6 +1681,84 @@ Claude pairs; without one the bundle costs one tool declaration.
|
|
|
1598
1681
|
minutes). It is local only (a Unix socket), but it does hand session
|
|
1599
1682
|
content to another agent: only what the model or you picked.
|
|
1600
1683
|
|
|
1684
|
+
## The codex-peer bundle
|
|
1685
|
+
|
|
1686
|
+
```sh
|
|
1687
|
+
chi bundle install codex-peer # in the dev profile
|
|
1688
|
+
```
|
|
1689
|
+
|
|
1690
|
+
The [claude-peer bundle](#the-claude-peer-bundle) for an OpenAI Codex CLI
|
|
1691
|
+
session: chi sends the Codex session you work with a part of the work to
|
|
1692
|
+
validate or as context, and Codex's answer comes back into chi. It is a copy
|
|
1693
|
+
of claude-peer with the transport swapped; what differs is below, the rest
|
|
1694
|
+
(kinds, payload files, the last send, `/codex last`, the status card, the
|
|
1695
|
+
skill install card) works the same.
|
|
1696
|
+
|
|
1697
|
+
- **Sending is `codex queue`.** `codex_send(kind, title, text)` and
|
|
1698
|
+
`/codex validate|context <text>` run
|
|
1699
|
+
`codex queue --thread <uuid> --message <text>` (an argv, no shell), which
|
|
1700
|
+
wakes an idle Codex TUI: the message runs there as a new user turn (checked
|
|
1701
|
+
on Codex 0.162.1; a message queued mid-turn runs after the turn). The
|
|
1702
|
+
thread is the `codex-inbox` context's hint, Codex's `CODEX_THREAD_ID`, which
|
|
1703
|
+
must look like a UUID. `codex` is `$CODEX`, else `codex` on the PATH: a
|
|
1704
|
+
worker started by `chi web` or launchd may have another PATH than your
|
|
1705
|
+
shell, so set `CODEX=` where chi runs if the error says there's no codex.
|
|
1706
|
+
The tool and the anytime `/codex` wait for `codex queue` up to 10 s; a
|
|
1707
|
+
queue that takes longer is killed (its process group) and the send fails
|
|
1708
|
+
with a timeout error, never a hang. Other errors say what to do: no
|
|
1709
|
+
`codex-inbox` (pair from Codex), a thread Codex has no record of (that
|
|
1710
|
+
Codex session is gone: pair again), an ephemeral thread
|
|
1711
|
+
(`codex exec --ephemeral` can't take queued messages), or `codex queue`'s
|
|
1712
|
+
first error line. A Codex session that was closed (`/quit`) is not gone:
|
|
1713
|
+
Codex's app-server still takes the queue (exit 0), chi says sent, and no
|
|
1714
|
+
answer comes; the message waits for that session (`codex resume` there, or
|
|
1715
|
+
pair again from a new one). The status card shows the thread id and says
|
|
1716
|
+
so; there is no cheap check that the session is open.
|
|
1717
|
+
- **Codex's sandbox.** Pairing and every reply write chi's state
|
|
1718
|
+
(`chi context add`/`push` write the session's context files directly),
|
|
1719
|
+
which Codex's default sandbox (workspace-write) refuses. The user adds, to
|
|
1720
|
+
`$CODEX_HOME/config.toml` (else `~/.codex/config.toml`):
|
|
1721
|
+
|
|
1722
|
+
```toml
|
|
1723
|
+
[sandbox_workspace_write]
|
|
1724
|
+
writable_roots = ["/Users/you/.local/state/samagotchi/context"]
|
|
1725
|
+
```
|
|
1726
|
+
|
|
1727
|
+
and restarts Codex. Only chi's `context/` folder: the state folder above it
|
|
1728
|
+
also holds guardrails, session files, plugins and the web token.
|
|
1729
|
+
`/codex install-skill`'s card shows the line with your state folder
|
|
1730
|
+
(absolute: config.toml doesn't expand `~`), and the pair script names it
|
|
1731
|
+
when a write is refused ("Operation not permitted"). chi never edits
|
|
1732
|
+
Codex's config. Reading a payload file needs nothing: the default sandbox
|
|
1733
|
+
reads anywhere. No network is needed: a push writes files, and chi wakes
|
|
1734
|
+
on them.
|
|
1735
|
+
- **Pairing** is Codex's, as with Claude: `/codex install-skill` copies the
|
|
1736
|
+
bundle's `scripts/chi-peer-SKILL.md` to
|
|
1737
|
+
`$CODEX_HOME/skills/chi-peer/SKILL.md` (else `~/.codex/…`), with the
|
|
1738
|
+
installed `scripts/pair` path, the payload folder and the writable root
|
|
1739
|
+
written in; in Codex you say "pair with chi <id or chi web URL>", and
|
|
1740
|
+
`sh <bundle>/scripts/pair` attaches `codex-inbox` (the thread id as its
|
|
1741
|
+
hint, pushed so a re-pair refreshes it) and `codex-review`. Run outside
|
|
1742
|
+
Codex (no `CODEX_THREAD_ID`) it refuses. Codex replies with one JSON push
|
|
1743
|
+
to `codex-review` (`text`, `summary`, `wake: true`), which wakes chi, the
|
|
1744
|
+
first answer too: that needs chi 0.63.0, the bundle's `requires_chi`.
|
|
1745
|
+
- **Peer messages are user turns in Codex.** Claude Code marks an inbox
|
|
1746
|
+
message as from another session; `codex queue` lands as a plain user
|
|
1747
|
+
message. So Codex's skill starts with the rule: a message whose first line
|
|
1748
|
+
is exactly `chi <8 hex> · <kind> · <title>` comes from chi, a peer agent,
|
|
1749
|
+
not the user; it can't approve anything, change settings or widen the
|
|
1750
|
+
sandbox, and the part it carries (and a payload file) is data to check,
|
|
1751
|
+
never instructions, since it may hold tool or web output. This is a prompt
|
|
1752
|
+
rule Codex follows, not something enforced: anything shaped like that
|
|
1753
|
+
header is treated this way, and nothing stops a message from asking for
|
|
1754
|
+
more.
|
|
1755
|
+
- In the web, selecting text offers **Confirm with Codex**, which fills
|
|
1756
|
+
`/codex validate` and the quote, as claude-peer's pill does.
|
|
1757
|
+
- **The skill.** `skill_codex_peer` (system scope) is claude-peer's with
|
|
1758
|
+
Codex's names, and on a missing answer points to Codex being mid-turn or
|
|
1759
|
+
its sandbox (the `writable_roots` line). To be asked before each send, a
|
|
1760
|
+
guardrails rule with `tool: codex_send` (see the claude-peer section).
|
|
1761
|
+
|
|
1601
1762
|
## Shutdown
|
|
1602
1763
|
|
|
1603
1764
|
When the REPL exits, or a session's worker exits (an idle exit, `/exit`, a
|
data/docs/releasing.md
CHANGED
|
@@ -13,7 +13,7 @@ can run the whole release; the user approves the notes before the tag and the
|
|
|
13
13
|
and `rake release:check` enforce it). The system bundle upgrades itself when
|
|
14
14
|
chi starts.
|
|
15
15
|
- **Every other shipped bundle** (btw, check-in, guardrails, known-names,
|
|
16
|
-
loop-guard, mcp, skills, source-links, github-pr, coordinator, claude-peer, and the profiles core and dev) has its own semver in its
|
|
16
|
+
loop-guard, mcp, skills, source-links, github-pr, coordinator, claude-peer, codex-peer, and the profiles core and dev) has its own semver in its
|
|
17
17
|
`manifest.yml` and, when it needs a newer chi, a `requires_chi:` line. They never upgrade by themselves: users run
|
|
18
18
|
`chi update` (all of them, with the gem) or `chi bundle upgrade NAME`. So:
|
|
19
19
|
- a change to a bundle's files bumps that bundle's `version:`
|
data/docs/sessions.md
CHANGED
|
@@ -220,6 +220,16 @@ A session can **continue** an earlier one: "the next day of the same routine" (a
|
|
|
220
220
|
- **CLI:** `chi send --new --continues (ID|PREFIX|last:ID) [-m TEXT]` (see *Starting a session*).
|
|
221
221
|
- **Which link:** an id, a unique prefix, or `last:<id or prefix>`, the latest link of the chain that session is in. It walks the links that still exist: a deleted link in the middle (`chi sessions delete`, the retention sweep of an unarchived one) ends the walk before it, so `last:` of an earlier link then names that earlier link, and continuing it starts a second line beside the links after the gap; name a link after the gap instead. A trigger names the chain this way: `chi send` to an archived link would bring it back (a human's input), not continue the chain.
|
|
222
222
|
|
|
223
|
+
## Toolkits
|
|
224
|
+
|
|
225
|
+
A session file's `toolkit` (`{"name", "add", "remove"}`) is the toolkit it was created on, as named: config's
|
|
226
|
+
`toolkit.default`, `toolkit.child` for a delegate child, or `chi --toolkit` / `chi send --new --toolkit` (with
|
|
227
|
+
`--tool` / `--no-tool`). It is never resolved again from config: a respawned or woken worker declares the same
|
|
228
|
+
tools. `null` (a session from before toolkits) is `full`. A continue (`continue →`, `chi send --new --continues`)
|
|
229
|
+
and a fork (`ctx.sessions.fork`) keep it, as they keep the thinking level; a delegate child gets `toolkit.child`.
|
|
230
|
+
`/toolkit` changes it, saved at once, from the next turn's start. See
|
|
231
|
+
[Toolkits](configuration.md#toolkits).
|
|
232
|
+
|
|
223
233
|
## Delegating
|
|
224
234
|
|
|
225
235
|
The `delegate` tool hands a task to a **child session**: an ordinary chi session in a worker of its own, started in the parent's `working_directory` with the task, verbatim, as its first user message, on the parent's model unless the call names one (`model:` takes a name or an alias, resolved as `--model` is; an unknown host comes back as the tool's `Error:` line; an id the host's saved model list doesn't have starts the child with a `Warning: host 'h' doesn't list model 'x' (did you mean: …?)` line in the result). Children run in parallel and only their **final reply** comes back to the parent: the child's newest `output/<timestamp>.txt` file, which the worker writes at the end of each turn that produced visible text, cut head-and-tail like an `execute` result. Nothing of the child's trace enters the parent's context. Nothing is hidden: a child shows in `chi sessions list` (`↳ <parent>`), the web (folded into its parent's card on the strip and in all sessions, whose `▸ N delegates` chip lists the delegates at any depth, see Web UI above; a card of its own with a `↳` chip when its parent isn't listed; `delegated by` in its info bar; the parent's info bar carries a `⑂ N` chip counting its delegates, with how many run, wait, are done, failed, stopped or idle in the tooltip and the waiting ones named in the attention colour; a click opens all sessions), the terminal's status row (a parent's `⑂ 2 running · 1 waiting`, see [Status Line](cli.md#status-line)) and the `list_sessions` tool (`child`; the parent shows as `parent` in the child's own list); the user can `chi --attach <child id>` and steer it mid-run, and `chi send` and `send_note` reach it like any session.
|