samagotchi 0.65.0 → 0.67.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 +107 -1
- data/README.md +2 -1
- data/bin/chi +51 -2
- data/docs/architecture.md +50 -28
- data/docs/cli.md +14 -2
- data/docs/configuration.md +101 -16
- data/docs/context.md +39 -2
- data/docs/desktop.md +90 -22
- data/docs/guardrails.md +8 -5
- data/docs/hooks.md +2 -2
- data/docs/internals/anthropic.md +156 -0
- data/docs/internals/background-tasks.md +1 -1
- data/docs/internals/bundles.md +2 -0
- data/docs/internals/engine.md +75 -30
- data/docs/internals/gemma4-contract.md +1 -1
- data/docs/internals/llm-context-bench.md +5 -4
- data/docs/internals/prompt-caching.md +36 -13
- data/docs/memory.md +1 -1
- data/docs/plugins.md +289 -25
- data/docs/releasing.md +1 -1
- data/docs/sessions.md +16 -6
- data/docs/sub-agent.md +6 -1
- data/docs/testing.md +17 -3
- data/lib/samagotchi/bootstrap/probe.rb +4 -3
- data/lib/samagotchi/bootstrap_command.rb +13 -3
- data/lib/samagotchi/bridge/ring_buffer.rb +0 -5
- data/lib/samagotchi/bridge.rb +6 -3
- data/lib/samagotchi/bundles/claude-peer/manifest.yml +7 -6
- data/lib/samagotchi/bundles/claude-peer/plugin.rb +40 -302
- data/lib/samagotchi/bundles/claude-peer/scripts/chi-peer-SKILL.md +8 -3
- data/lib/samagotchi/bundles/claude-peer/skill_claude_peer.md +14 -6
- data/lib/samagotchi/bundles/codex-peer/manifest.yml +22 -0
- data/lib/samagotchi/bundles/codex-peer/plugin.rb +189 -0
- data/lib/samagotchi/bundles/codex-peer/scripts/chi-peer-SKILL.md +136 -0
- data/lib/samagotchi/bundles/codex-peer/scripts/pair +92 -0
- data/lib/samagotchi/bundles/codex-peer/skill_codex_peer.md +76 -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/mcp/manifest.yml +5 -5
- data/lib/samagotchi/bundles/mcp/plugin.rb +82 -294
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +7 -3
- data/lib/samagotchi/bundles/system/delegated.md +2 -1
- data/lib/samagotchi/bundles/system/manifest.yml +5 -5
- data/lib/samagotchi/bundles/system/memory_guide.md +1 -1
- data/lib/samagotchi/bundles/system/self_map.md +1 -1
- data/lib/samagotchi/cache_confirm.rb +9 -6
- data/lib/samagotchi/commands/registry.rb +25 -8
- data/lib/samagotchi/config.rb +23 -8
- data/lib/samagotchi/context_absorber.rb +66 -13
- data/lib/samagotchi/desktop/macos/AgentTerminal.swift +260 -0
- data/lib/samagotchi/desktop/macos/App.swift +3 -3
- data/lib/samagotchi/desktop/macos/ChiRunner.swift +5 -2
- data/lib/samagotchi/desktop/macos/Images.swift +2 -2
- data/lib/samagotchi/desktop/macos/Kitty.swift +33 -197
- data/lib/samagotchi/desktop/macos/Panel.swift +118 -82
- data/lib/samagotchi/desktop/macos/ProcessRunner.swift +1 -1
- data/lib/samagotchi/desktop/macos/Tmux.swift +239 -0
- data/lib/samagotchi/desktop/macos.rb +84 -13
- data/lib/samagotchi/desktop_command.rb +14 -3
- data/lib/samagotchi/engine.rb +120 -107
- data/lib/samagotchi/engine_peers.rb +3 -0
- data/lib/samagotchi/guardrail_wiring.rb +1 -1
- data/lib/samagotchi/guardrails/approval.rb +1 -1
- data/lib/samagotchi/guardrails/approvals.rb +1 -1
- data/lib/samagotchi/guardrails/rules.rb +1 -1
- data/lib/samagotchi/guardrails/targets.rb +1 -1
- data/lib/samagotchi/hook_routes.rb +151 -0
- data/lib/samagotchi/hooks/registry.rb +2 -2
- data/lib/samagotchi/host_keys.rb +147 -0
- data/lib/samagotchi/kernel_loop.rb +9 -1
- data/lib/samagotchi/llm/adapter_common.rb +138 -0
- data/lib/samagotchi/llm/anthropic_assembly.rb +4 -2
- data/lib/samagotchi/llm/anthropic_messages.rb +28 -105
- data/lib/samagotchi/llm/api_key.rb +23 -7
- data/lib/samagotchi/llm/chat_loop.rb +10 -6
- data/lib/samagotchi/llm/openai_chat.rb +9 -92
- data/lib/samagotchi/llm/refusal_memo.rb +29 -0
- data/lib/samagotchi/llm_context_override.rb +1 -0
- data/lib/samagotchi/peer_sources.rb +58 -0
- data/lib/samagotchi/plugin/api.rb +138 -156
- data/lib/samagotchi/plugin/context.rb +22 -1
- data/lib/samagotchi/plugin/loader.rb +5 -2
- data/lib/samagotchi/plugin/peer.rb +394 -0
- data/lib/samagotchi/plugin/tool_spec.rb +166 -0
- data/lib/samagotchi/plugin_tasks.rb +2 -2
- data/lib/samagotchi/row_refs.rb +1 -1
- data/lib/samagotchi/self_report.rb +5 -3
- data/lib/samagotchi/send_command.rb +41 -15
- data/lib/samagotchi/session.rb +18 -1
- data/lib/samagotchi/session_commands.rb +16 -1
- data/lib/samagotchi/session_continue.rb +58 -15
- data/lib/samagotchi/session_inbox.rb +0 -13
- 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_call_parser.rb +1 -1
- data/lib/samagotchi/tool_runner.rb +13 -1
- data/lib/samagotchi/toolkit.rb +246 -0
- data/lib/samagotchi/toolkit_binding.rb +138 -0
- data/lib/samagotchi/toolkit_command.rb +117 -0
- data/lib/samagotchi/tools/context_read.rb +14 -0
- data/lib/samagotchi/tools/execute.rb +12 -12
- data/lib/samagotchi/tools/registry.rb +43 -6
- data/lib/samagotchi/tools/task_create.rb +6 -6
- data/lib/samagotchi/tools/task_get.rb +13 -13
- data/lib/samagotchi/tools/task_list.rb +6 -6
- data/lib/samagotchi/tools/task_record.rb +52 -0
- data/lib/samagotchi/tools/task_runtime.rb +55 -91
- data/lib/samagotchi/tools/task_stop.rb +6 -6
- data/lib/samagotchi/tools/task_wait.rb +12 -13
- data/lib/samagotchi/tools/tool_index.rb +454 -0
- data/lib/samagotchi/update_command.rb +9 -6
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/web/app.rb +28 -9
- data/lib/samagotchi/web/capabilities.rb +1 -1
- data/lib/samagotchi/web/public/activity.js +2 -2
- 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 +52 -43
- data/lib/samagotchi/web/public/chains.js +1 -1
- data/lib/samagotchi/web/public/commands.js +6 -4
- data/lib/samagotchi/web/public/composer_size.js +2 -2
- data/lib/samagotchi/web/public/copy.js +1 -1
- data/lib/samagotchi/web/public/ctx.js +1 -1
- data/lib/samagotchi/web/public/delivery_line.js +4 -4
- data/lib/samagotchi/web/public/families_view.js +1 -1
- data/lib/samagotchi/web/public/format.js +11 -7
- data/lib/samagotchi/web/public/images.js +25 -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/llm_context_chip.js +2 -2
- data/lib/samagotchi/web/public/notify.js +6 -4
- data/lib/samagotchi/web/public/question_card.js +1 -1
- data/lib/samagotchi/web/public/ref_scan.js +1 -1
- data/lib/samagotchi/web/public/session_state.js +17 -0
- data/lib/samagotchi/web/public/session_title.js +2 -3
- data/lib/samagotchi/web/public/sessions_list.js +0 -8
- data/lib/samagotchi/web/public/stage_model.js +2 -2
- data/lib/samagotchi/web/public/stream_handlers.js +13 -0
- data/lib/samagotchi/web/public/strip.js +3 -3
- data/lib/samagotchi/web/public/task_stop.js +3 -2
- data/lib/samagotchi/web/public/toolkit_chip.js +29 -0
- data/lib/samagotchi/web/public/turn_events.js +1 -1
- data/lib/samagotchi/web/public/turn_html.js +2 -2
- data/lib/samagotchi/web/public/turn_model.js +2 -2
- data/lib/samagotchi/web/public/turn_view.js +1 -1
- data/lib/samagotchi/web/server.rb +0 -4
- data/lib/samagotchi/worker.rb +13 -8
- data/lib/samagotchi/worker_inbound.rb +36 -4
- data/lib/samagotchi/worker_wakes.rb +67 -7
- metadata +23 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 58f42be965113cdd1f5736f77b9a5be8e9fbd77088b4a2013f9b7e86ae7ceb12
|
|
4
|
+
data.tar.gz: a093f0921d62e84c9e6c34ac10482e57e9e041a5a9d21eda41a88052e819cda1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d02e8644b724765182cabb9a74eafcfaec94eb13781680e29cc33bfb1ee2bc850f55a7df4117b61d8a2889c7d48bf4792b88ba316676a1fb6be5632cc98d1a3f
|
|
7
|
+
data.tar.gz: a4397b9a00729a4eb230415e265c834f85f42b8b6ded242dc3c981ad6f3e543f367d7a9df00ed6d516aef12c2e2266a765b0f66c7025f575975c83fd4b556b86
|
data/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,110 @@ and commands may change between minor versions. How releases are made:
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
## [0.67.0] - 2026-10-14
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **Desktop helper: agents in tmux panes**: with `tmux.enabled: true` (then `chi update`) the panel lists agent CLIs
|
|
16
|
+
running in tmux panes, in any terminal, beside kitty's, and pastes into them with or without Enter (a pane in copy
|
|
17
|
+
mode is taken out of it first). `tmux.binary` defaults to the tmux on chi's PATH when the launch file is written;
|
|
18
|
+
`chi update` warns when there is none. `ChiHelper --kitty` is now `--agents` (the helper's internal seam). See
|
|
19
|
+
docs/desktop.md, "Agents in kitty and tmux".
|
|
20
|
+
- **Continue on another model**: `chi send --new --continues ID --model NAME` and the web API's `POST /api/sessions`
|
|
21
|
+
with `continues` and `model` start the next link of a chain on that model (its host checked first, nothing
|
|
22
|
+
archived on a refusal). See docs/sessions.md, "Session chains".
|
|
23
|
+
- **Host API keys from the macOS Keychain**: with the variable a host's `api_key_env:` names unset, chi reads the key
|
|
24
|
+
from the login Keychain (service `chi`, account the variable's name), so the desktop helper's sessions and model
|
|
25
|
+
list, launchd and cron reach keyed hosts. Store a key with `security add-generic-password -s chi -a VAR -w`.
|
|
26
|
+
`chi self` says `VAR (keychain)`, `chi desktop status` has a `host keys` row, and install/upgrade warn about keys
|
|
27
|
+
the helper won't get. `SAMAGOTCHI_KEYCHAIN=off` turns it off. See docs/configuration.md, "API keys from the Keychain".
|
|
28
|
+
- **The peer loop**: chi and a paired Claude Code or Codex session can go back and forth on a part. A peer's answer
|
|
29
|
+
wakes chi up to `context.peer_turns` (default 3) turns in a row, within `session.max_wakes`; chi fixes what it
|
|
30
|
+
flagged or replies with `claude_send`/`codex_send`; an answer whose verdict is `VERDICT OK` arrives as a note and
|
|
31
|
+
ends the round; only a message typed in the web or a TUI starts the count again (not `chi send`). The wake note
|
|
32
|
+
frames the answer as the peer's advice, within your task; every other source keeps its guard line. Plugins
|
|
33
|
+
declare a peer's source with `chi.peer_source`. See docs/context.md, "Peer sources: the peer loop".
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- **claude-peer 0.3.0 and codex-peer 0.2.0** join the peer loop and need chi 0.67.0 (`chi update`, then `chi bundle
|
|
38
|
+
upgrade claude-peer` / `codex-peer`, and `/claude install-skill` / `/codex install-skill` again for the peer's
|
|
39
|
+
side). Both are now built on `Samagotchi::Plugin::Peer`, one shared base; their texts are unchanged but for the
|
|
40
|
+
peer loop's.
|
|
41
|
+
- `session.max_wakes` caps every wake turn in a row (delegate reports, attached context, peers); the docs said
|
|
42
|
+
delegate reports only.
|
|
43
|
+
- **MCP: `find_mcp_tools` and `mcp_call` are now `find_tools` and `call_tool`**, chi's own tool index's (the mcp
|
|
44
|
+
bundle publishes its servers' tools into it: `chi.tool_source`, docs/plugins.md). Run `chi bundle upgrade mcp`
|
|
45
|
+
(mcp 0.7.0) to get it; an older mcp bundle keeps its own two tools on this chi. Search, calls, lazy starts and
|
|
46
|
+
`/mcp` work as before. Rename `tool: mcp_call` in guardrail rules and hooks, and `mcp_call` in loop-guard's or
|
|
47
|
+
check-in's `ignore_tools` in config.yml, to `call_tool`; rules on `mcp_*` keep working (they match the MCP
|
|
48
|
+
tool). Approvals saved for MCP tools (keyed `mcp_call>…`) no longer match: each MCP tool is asked about once
|
|
49
|
+
more. The two tools' new names and place in the tool list re-prefill an MCP user's prompt once.
|
|
50
|
+
- A continue whose previous link's model names a host config.yml no longer has (or a disabled one) starts the new
|
|
51
|
+
link on the default model with a warning (`chi send: warning: …`, the API's `model_warning`) instead of being
|
|
52
|
+
refused.
|
|
53
|
+
|
|
54
|
+
### Fixed
|
|
55
|
+
|
|
56
|
+
- Web: a session opened (or switched to) shows its live worker's features at once: the queue link on the delivery
|
|
57
|
+
line, the task-stop button, Alt+Enter's queue and the stale-worker badge with Restart no longer wait for the
|
|
58
|
+
session's next change.
|
|
59
|
+
- Web: a switch starts the open session's page state over: a "copied ✓" on the info bar no longer carries into the
|
|
60
|
+
next session, a Restart under way in one session no longer blocks Restart in another, and a worker woken after a
|
|
61
|
+
failed load streams from its start, not from the previous session's cursor.
|
|
62
|
+
- Web: a send stays with the session it was sent from: switching sessions while its images upload no longer sends the
|
|
63
|
+
prompt into the other session, and an image whose send failed in one session is uploaded again when it is sent to
|
|
64
|
+
another (it was refused there as an unknown image).
|
|
65
|
+
|
|
66
|
+
Update with `chi update` (it upgrades the bundles too): mcp 0.7.0, claude-peer 0.3.0, codex-peer 0.2.0 and the
|
|
67
|
+
system bundle 0.67.0. Then restart `chi web` and run `chi desktop upgrade` for the tmux support.
|
|
68
|
+
|
|
69
|
+
## [0.66.0] - 2026-10-14
|
|
70
|
+
|
|
71
|
+
### Added
|
|
72
|
+
|
|
73
|
+
- **Plugin annotation actions** (web): `chi.annotation(label, command:, template:, send:)` adds a pill to the
|
|
74
|
+
selection bar that prepares a command line of the plugin from the selected text (`{quote}`, `{text}`, `{source}`):
|
|
75
|
+
it fills the composer (an earlier draft stays under the quote), or with `send: true` sends at once. Up to 3 inline,
|
|
76
|
+
the rest behind `⋯`. See docs/plugins.md, `chi.annotation`.
|
|
77
|
+
- claude-peer 0.2.0: **Confirm with Claude** in the web's selection bar fills `/claude validate` with the quote;
|
|
78
|
+
`/claude validate|context` titles a quote by its first quoted line, not its `From …:` label.
|
|
79
|
+
- The codex-peer bundle (in the dev profile): claude-peer for an OpenAI Codex CLI session. Codex pairs (its
|
|
80
|
+
`chi-peer` skill, installed by `/codex install-skill`), the `codex_send` tool and `/codex validate|context|last`
|
|
81
|
+
send it a part of the work with `codex queue` (killed after 10 s, never a hang), and Codex's answer comes back as
|
|
82
|
+
the `codex-review` context; **Confirm with Codex** in the web's selection bar. Codex's sandbox needs chi's
|
|
83
|
+
context folder in `writable_roots` (the install card shows the line). See docs/plugins.md, "The codex-peer bundle".
|
|
84
|
+
- **Toolkits**: a session's toolkit names the tools its prompts declare. chi ships `full` (every built-in, the
|
|
85
|
+
default), `lean` (the file, shell, memory, task, question, `web_fetch` and `context_read` tools: ~1.4k tokens
|
|
86
|
+
fewer) and `child` (`lean` with `send_note`, without `memory_write`); config's `toolkit.kits` and bundles
|
|
87
|
+
(`chi.toolkit`) add their own. Pick one with `toolkit.default` / `toolkit.child`, `chi --toolkit NAME` (with
|
|
88
|
+
`--tool` / `--no-tool`, also on `chi send --new`) or `/toolkit NAME | add TOOL | remove TOOL`. It is saved in the
|
|
89
|
+
session when it is created and kept by continues and forks; a change applies at the next turn's start. A left-out
|
|
90
|
+
tool is named in one prompt line, and a call to it is refused before the guardrails. The web shows a session not
|
|
91
|
+
on `full` as a `tools` chip. See docs/configuration.md, "Toolkits".
|
|
92
|
+
- Plugins: `ctx.toolkit` and `ctx.use_toolkit(name)` (before the session's first reply). The coordinator bundle
|
|
93
|
+
(0.4.0) declares the `coordinator` toolkit (`lean` + `delegate`, `delegate_result`, `list_sessions`), and
|
|
94
|
+
`/coordinate` puts a fresh session on it.
|
|
95
|
+
|
|
96
|
+
### Changed
|
|
97
|
+
|
|
98
|
+
- A command's name may be followed by any whitespace, a newline too: `/foo` on its own line with text under it
|
|
99
|
+
runs `/foo` with that text, in the terminals and the web (before, only a space did).
|
|
100
|
+
- Web: a command refused as busy mid-turn comes back into the composer (it was cleared when sent).
|
|
101
|
+
- A delegate child is told to report what should change (a wrong memory or skill, instructions that led it astray)
|
|
102
|
+
in its reply instead of writing memories (`delegated.md`).
|
|
103
|
+
|
|
104
|
+
### Fixed
|
|
105
|
+
|
|
106
|
+
- `dialect: fireworks` hosts: the web's ctx meter no longer reads 0% while an answer streams (Fireworks' first stream
|
|
107
|
+
event carries all-zero usage; its counts come at the end).
|
|
108
|
+
|
|
109
|
+
**Still open:** the Claude 5.x "bound to a different conversation" 400 on Anthropic Messages hosts (see 0.65.0); its
|
|
110
|
+
recovery comes in a later release.
|
|
111
|
+
|
|
112
|
+
Update with `chi update` (the system bundle moves with the gem; claude-peer 0.2.0, coordinator 0.4.0, and the dev
|
|
113
|
+
profile 0.5.0 with the new codex-peer 0.1.0).
|
|
114
|
+
|
|
11
115
|
## [0.65.0] - 2026-10-13
|
|
12
116
|
|
|
13
117
|
### Added
|
|
@@ -2833,7 +2937,9 @@ and long-lived sessions.
|
|
|
2833
2937
|
- A macOS desktop helper (`chi desktop install`): a "Send to chi" Service and a
|
|
2834
2938
|
hotkey panel that send selected text or the clipboard to your sessions.
|
|
2835
2939
|
|
|
2836
|
-
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.
|
|
2940
|
+
[Unreleased]: https://github.com/dm1try/samagotchi/compare/v0.67.0...HEAD
|
|
2941
|
+
[0.67.0]: https://github.com/dm1try/samagotchi/compare/v0.66.0...v0.67.0
|
|
2942
|
+
[0.66.0]: https://github.com/dm1try/samagotchi/compare/v0.65.0...v0.66.0
|
|
2837
2943
|
[0.65.0]: https://github.com/dm1try/samagotchi/compare/v0.64.0...v0.65.0
|
|
2838
2944
|
[0.64.0]: https://github.com/dm1try/samagotchi/compare/v0.63.0...v0.64.0
|
|
2839
2945
|
[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/architecture.md
CHANGED
|
@@ -45,7 +45,7 @@ A compact visual overview of the current architecture, then the core API in pros
|
|
|
45
45
|
│ task_create/get/list/stop/wait · delegate · delegate_result · ask_user_question │
|
|
46
46
|
│ register_reminder/cancel_reminder/list_reminders · send_note · list_sessions · │
|
|
47
47
|
│ context_read · forget_outputs (+ bundle plugin tools) │
|
|
48
|
-
│
|
|
48
|
+
│ offered by Tools::Registry (Builtins + bundles), filtered by the toolkit │
|
|
49
49
|
└──────────────────────────────────────────────────────────────────────────────────┘
|
|
50
50
|
│
|
|
51
51
|
▼
|
|
@@ -83,10 +83,10 @@ TerminalUI ── delegates ──▶ Engine
|
|
|
83
83
|
## Request / turn flow
|
|
84
84
|
|
|
85
85
|
```
|
|
86
|
-
bin/chi ─▶ TerminalUI ─▶ Engine#run_turn ─▶ KernelLoop ──┬─▶ Model API (LLM)
|
|
87
|
-
(builds) │
|
|
88
|
-
│
|
|
89
|
-
└── on_event: ─▶ render update
|
|
86
|
+
bin/chi ─▶ TerminalUI ─▶ Engine#run_turn ─▶ KernelLoop / ChatLoop ──┬─▶ Model API (LLM)
|
|
87
|
+
(builds) │ └─▶ Tool call(s) ─▶ Tool
|
|
88
|
+
│ │
|
|
89
|
+
└── on_event: ─▶ render update ────────────────┘
|
|
90
90
|
(turn started / completed / canceled)
|
|
91
91
|
```
|
|
92
92
|
|
|
@@ -99,12 +99,12 @@ terminal renders the events it gets over the Bridge.
|
|
|
99
99
|
|-------|-----------|----------------|
|
|
100
100
|
| Core | `Samagotchi::Engine` | System prompt, memory injection, tool declarations, session lifecycle, model↔tool loop (`run_turn`). No terminal coupling. |
|
|
101
101
|
| UI | `Samagotchi::TerminalUI` | REPL (Reline), rendering, REPL commands. Delegates all core work to an `Engine`. |
|
|
102
|
-
| Model loops | `KernelLoop` (via `LLM::NativeBackend`), `LLM::ChatLoop` | The model↔tool loop: raw prompt or
|
|
102
|
+
| Model loops | `KernelLoop` (via `LLM::NativeBackend`), `LLM::ChatLoop` | The model↔tool loop: raw prompt, or a chat API (OpenAI chat, Anthropic Messages), chosen per host (see below). |
|
|
103
103
|
| Adapters | `Samagotchi::Client`, `LLM::OpenAIChat`, `LLM::AnthropicMessages`, `LLM::HTTP` | Raw-prompt servers, the OpenAI chat API, Anthropic's Messages API, and the HTTP they share. |
|
|
104
104
|
| Tools | `lib/samagotchi/tools/*` | Execute, read, edit, write, memory, task_*, web_fetch, delegate/delegate_result, ask_user_question, reminders, send_note, list_sessions, context_read, forget_outputs, plus runtime/output-guardrails. |
|
|
105
105
|
| Bundles | `lib/samagotchi/memory_bundle/*`, `BundleCommand` | `chi bundle`: install/upgrade (3-way merge), build, uninstall; the install record under `.bundles/<name>/` that the hook, guardrail and plugin loaders read ([internals/bundles.md](internals/bundles.md)). |
|
|
106
106
|
| Background | `Samagotchi::Worker`, `Samagotchi::SessionManager` | `SessionManager` forks/spawns one worker process per session; its `Worker` builds the `Engine` and `Bridge` (no terminal rendering) and runs queued turns. The spawned process (own process group) forks the worker, which `setsid`s and is reparented to init once its starter exits (it waits until the worker holds `owner.lock`, so the spawner's `Process.detach` waiter tells a starting worker): nothing that walks the spawner's process tree reaches it (`SAMAGOTCHI_WORKER_DETACH=1`, `detach_from_spawner`). The lock's pid is the worker's; `spawn`'s `child_pid` is its starter's. |
|
|
107
|
-
| Web | `Samagotchi::Web::App`, `Samagotchi::Web::Server`, `Samagotchi::Web::SessionHub` | Rack+WEBrick
|
|
107
|
+
| Web | `Samagotchi::Web::App`, `Samagotchi::Web::Server`, `Samagotchi::Web::SessionHub` | Rack+WEBrick on one port, `127.0.0.1:4567` (index.html + `/api/*` + SSE); with `web.host: lan` also on this machine's LAN address, behind an access token (`Web::Lan`, `Web::Token`). The hub is chi web's projection of the session list, pushed to every tab over `GET /api/events`. |
|
|
108
108
|
| Sessions | `Samagotchi::Session`, `SessionManager` | File `sessions/<uuid>.json` + sidecar `input/`/`output/`; retention 14d/500, `updated_at desc`, lazy sweep. |
|
|
109
109
|
|
|
110
110
|
## Entry points
|
|
@@ -122,7 +122,8 @@ terminal renders the events it gets over the Bridge.
|
|
|
122
122
|
as a client of its Bridge (`BridgeClient#follow`, `post_turn`, `post_command`, `cancel`, `answer`,
|
|
123
123
|
`dismiss_question`). The worker runs in the session's `working_directory`, so `!cmd` and
|
|
124
124
|
the tools don't depend on where the terminal attached from.
|
|
125
|
-
- `bin/chi web` → builds `Web::Server` (Rack+WEBrick on `127.0.0.1:4567`, `--port`/`SAMAGOTCHI_WEB_PORT`, `--open
|
|
125
|
+
- `bin/chi web` → builds `Web::Server` (Rack+WEBrick on `127.0.0.1:4567`, `--port`/`SAMAGOTCHI_WEB_PORT`, `--open`;
|
|
126
|
+
`web.host: lan` adds a LAN address, `--new-token` replaces its access token).
|
|
126
127
|
- `bin/chi sessions {list,stop,restart,archive,unarchive,delete,prune,clean,stats}` (`SessionsCommand`; not the REPL's `SessionCommands`) → retention & ordering (`SessionRetention`, `updated_at desc`, dry-run, test-only); `stop` is `SessionManager.stop_session(wait:)`, which waits for the worker to release `owner.lock`; `delete` (`SessionDeleteCommand`) is `SessionManager.delete_session(stop:)`, which the TUI's `/exit --delete` and the web's `DELETE /api/sessions/:id` use too.
|
|
127
128
|
- In REPL mode `TerminalUI` builds the `Engine` in process (`-p` and `--non-interactive` too);
|
|
128
129
|
otherwise the session's `Worker` (`lib/samagotchi/worker.rb`, spawned by `SessionManager`) builds it.
|
|
@@ -132,7 +133,7 @@ terminal renders the events it gets over the Bridge.
|
|
|
132
133
|
- **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.
|
|
133
134
|
- **Single owner:** the process running a session's Engine (worker or in-process TUI) holds a flock on `owner.lock` (`OwnerLock`); a second owner backs off, and the web answers 409 for a TUI-owned session.
|
|
134
135
|
- **Status:** `status` is turn state (`idle`/`running`); liveness is the lock. A stop is the `<uuid>/stopped` marker (`Session.mark_stopped`; a resume removes it), which `Session.load` and `.summary_from_file` read as status `stopped`, so no other process rewrites a worker's session file.
|
|
135
|
-
- **Retention:** 14 days / 500 cap (env `SAMAGOTCHI_SESSION_RETENTION_DAYS`/`MAX_COUNT`, optional `KEEP_STATUS`), live-owner guard, only when `*.json` present; lazy sweep
|
|
136
|
+
- **Retention:** 14 days / 500 cap (env `SAMAGOTCHI_SESSION_RETENTION_DAYS`/`MAX_COUNT`, optional `KEEP_STATUS`), live-owner guard, only when `*.json` present; lazy sweep at most once per `session.sweep_interval_hours` (default 24) from the session hub's full-probe tick (and on `GET /api/sessions`, which the page no longer calls), manual via `bin/chi sessions prune --dry-run`.
|
|
136
137
|
- **Ordering:** `Session.list(sort:,order:,limit:,offset:)` and `GET /api/sessions?sort=&order=&limit=&offset=` default `updated_at desc`; Web UI sort/filter/pagination.
|
|
137
138
|
- **Test hygiene:** `test_run` flag when `SAMAGOTCHI_ENV=test`/`RACK_ENV=test`/`CI`, targetable via `prune --test-only` / `clean`.
|
|
138
139
|
|
|
@@ -145,9 +146,9 @@ agent logic and can be used without any terminal rendering; the UI is a thin lay
|
|
|
145
146
|
|-------|-------|----------------|
|
|
146
147
|
| Core | `Samagotchi::Engine` | System prompt, memory injection, tool declarations, session lifecycle, the model↔tool loop (`run_turn`). No terminal coupling. |
|
|
147
148
|
| UI | `Samagotchi::TerminalUI` | Interactive REPL (Reline), rendering (ANSI, spinner, status line), REPL commands. Delegates all core work to an `Engine`. |
|
|
148
|
-
| Model loops and adapters | `KernelLoop`, `LLM::ChatLoop`, `Samagotchi::Client`, `LLM::OpenAIChat`, `LLM::HTTP` | The model↔tool loops and the HTTP adapters they talk through (see "Model loops and adapters"). |
|
|
149
|
+
| Model loops and adapters | `KernelLoop`, `LLM::ChatLoop`, `Samagotchi::Client`, `LLM::OpenAIChat`, `LLM::AnthropicMessages`, `LLM::HTTP` | The model↔tool loops and the HTTP adapters they talk through (see "Model loops and adapters"). |
|
|
149
150
|
| Bridge (SSE/HTTP) | `Samagotchi::Bridge`, `SessionManager` | The **single live client transport**: an SSE read stream + HTTP POST turn/cancel/answer surface that attaches to a worker's existing `Engine` via `Engine#subscribe`. Every session worker starts it (bound `127.0.0.1`, no auth, localhost-only). |
|
|
150
|
-
| Web (Rack) | `Samagotchi::Web::App`, `SessionManager` | Single-port `127.0.0.1:4567` control plane via `rack`+`webrick` (serve `index.html` + `/api/*`). It talks to each worker's Bridge (`App#relay`, `bridge_get`; `/stream` proxies it) and reads the session files; no `Engine` of its own. `bin/chi web` entrypoint. |
|
|
151
|
+
| Web (Rack) | `Samagotchi::Web::App`, `SessionManager` | Single-port `127.0.0.1:4567` control plane via `rack`+`webrick` (serve `index.html` + `/api/*`). With `web.host: lan` (or one of this machine's IPv4 addresses) the server also listens on that address, where every request but this machine's needs the access token (`App#token_gate`: the `chi_token` cookie or `Authorization: Bearer`; [CLI](cli.md#chi-web-on-your-phone)). It talks to each worker's Bridge (`App#relay`, `bridge_get`; `/stream` proxies it) and reads the session files; no `Engine` of its own. `bin/chi web` entrypoint. |
|
|
151
152
|
| Sessions | `Samagotchi::Session`, `SessionManager` | File-based `~/.local/state/samagotchi/sessions/<uuid>.json` + sidecar `input/`/`output/`; retention (14d/500) + ordering (`updated_at desc`). |
|
|
152
153
|
|
|
153
154
|
- `bin/chi` in REPL mode (see `LaunchMode` above) builds `TerminalUI`. `TerminalUI#run` is the single
|
|
@@ -199,21 +200,24 @@ renders whatever it needs from the stream + final `Result`. The public Engine AP
|
|
|
199
200
|
```ruby
|
|
200
201
|
engine.run_turn(session, prompt, on_event: nil, max_iterations: nil, cancel_controller: nil, # nil: turn.max_iterations
|
|
201
202
|
max_tool_output_chars: nil, pending_input: nil, continue: false, origin: nil,
|
|
202
|
-
images: [], id: nil)
|
|
203
|
+
images: [], id: nil, resumed: false, resume_from: nil) # resumed/resume_from: a step-limit Continue
|
|
203
204
|
engine.system_prompt # fully built system prompt string
|
|
204
205
|
engine.session # current session (Engine owns create/resume)
|
|
205
206
|
```
|
|
206
207
|
|
|
207
|
-
The system prompt (`SystemPrompt#build`) is the
|
|
208
|
-
|
|
209
|
-
|
|
208
|
+
The system prompt (`SystemPrompt#build`, assembled in `#system_prompt_with_index`) is the
|
|
209
|
+
base prompt, then the toolkit's "Not loaded in this session" line (only when the toolkit
|
|
210
|
+
leaves tools out), rg guidance, the identity, the model notes, the preloaded memories,
|
|
211
|
+
AGENTS.md (root → cwd), the appended files (`--append-system-prompt`), the memory
|
|
212
|
+
indexes, and last **the model** (`Model: this session runs on <ref> (host …; model key …)` and a line telling
|
|
210
213
|
the model to answer "which model are you" from it, not from training), the working
|
|
211
214
|
directory and the session id and log (Gemma's native tool declarations still follow).
|
|
212
215
|
The order is for the servers' prompt caches, which reuse only an exact token prefix:
|
|
213
216
|
what every session shares first, what changes least before what changes more, the
|
|
214
217
|
per-session lines last, so a new session reuses everything above them. It is built
|
|
215
|
-
once per loop and rebuilt only by a model switch
|
|
216
|
-
line costs no
|
|
218
|
+
once per loop and thinking level and rebuilt only by a model switch, changed tools or a
|
|
219
|
+
toolkit change (`Engine#apply_toolkit!`, at a turn's start), so the model line costs no
|
|
220
|
+
KV churn.
|
|
217
221
|
The full request layout, what breaks the cache and the rules for changes:
|
|
218
222
|
[internals/prompt-caching.md](internals/prompt-caching.md).
|
|
219
223
|
|
|
@@ -272,7 +276,8 @@ process that already owns the `Engine`); every worker starts it, and it exposes:
|
|
|
272
276
|
- `POST /session/:id/tasks/stop` — `{task_id}`: stop a background task as the user
|
|
273
277
|
(`TaskRuntime.stop_task by: "user"`, the web's stop-task button on a running `task_wait`;
|
|
274
278
|
web route `POST /api/sessions/:id/tasks/:task_id/stop`). Only a task this conversation's
|
|
275
|
-
`task_create` started
|
|
279
|
+
`task_create` started, or an `execute` handed over as a task at its limit (`TaskRuntime.created_ids_in` on the
|
|
280
|
+
saved messages, or the running turn's accumulator); answers once the
|
|
276
281
|
task is gone: `200 {status, stop_reason, task_id}`, `404 task_not_found`, `409 not_running`.
|
|
277
282
|
The worker names it in its sidecar features (`task_stop`).
|
|
278
283
|
- `POST /session/:id/relay` — the approval relay, on a delegated child's worker
|
|
@@ -307,7 +312,10 @@ process that already owns the `Engine`); every worker starts it, and it exposes:
|
|
|
307
312
|
actions: its turn no longer runs), `event_seq`, `event_id`, all taken in one event-log hold; no message
|
|
308
313
|
list, so it costs the same however long the session is. `?turn_id=` answers that turn's answer
|
|
309
314
|
(an unknown or missing id: the newest); the only route that reads its query.
|
|
310
|
-
-
|
|
315
|
+
- No browser: only chi's own clients talk to a Bridge. A request with an `Origin` header, a `Sec-Fetch-Site`
|
|
316
|
+
other than `none`, or a `Host` that isn't loopback (a DNS-rebound page) is `403 cross_origin`
|
|
317
|
+
(`Bridge#browser_request?`, before any route); there is no CORS and no `OPTIONS` route. The browser reaches a
|
|
318
|
+
worker only through `chi web`.
|
|
311
319
|
|
|
312
320
|
The metrics in `/state`, `/snapshot`, `/stats` and the SSE `snapshot`/`reset` frames
|
|
313
321
|
(`SessionMetrics#snapshot`) carry the session's totals and only the recent timing records: the
|
|
@@ -438,7 +446,7 @@ attached TUI get a fresh number each time), and on a rise past the second
|
|
|
438
446
|
threshold puts the model's own `[CONTEXT: …]` line on the tail as a system
|
|
439
447
|
message (docs/internals/context-telemetry.md).
|
|
440
448
|
|
|
441
|
-
**Adapters.** `Client` (raw-prompt servers) and `LLM::
|
|
449
|
+
**Adapters.** `Client` (raw-prompt servers), `LLM::OpenAIChat` and `LLM::AnthropicMessages` (one per host,
|
|
442
450
|
`HostRegistry#adapter_for`, built by `LLM::Adapters` from the host's api) share
|
|
443
451
|
`LLM::HTTP`: timeouts, TLS for https, a line reader for streamed bodies, the retry loop (`retry.*`; network errors, 429 and
|
|
444
452
|
500/502/503/504/529, honouring `Retry-After`; never after a stream has produced
|
|
@@ -459,7 +467,10 @@ of chi reads (the prompt is input + cache reads + cache writes), stop reasons to
|
|
|
459
467
|
(`max_tokens` → `length`, `tool_use` → `tool_calls`, `end_turn` → `stop`). A stream is read by content-block index
|
|
460
468
|
from the `data:` lines alone (`event:` lines are not needed); `message_start` / `message_delta` events reach
|
|
461
469
|
`on_delta` with that normalized `usage`, pings not at all, and a block's start or delta counts as shown for the
|
|
462
|
-
first-token limit. Both adapters share their model-list cache (`LLM::ModelListing`)
|
|
470
|
+
first-token limit. Both adapters share their model-list cache (`LLM::ModelListing`) and their HTTP plumbing
|
|
471
|
+
(`LLM::AdapterCommon`: timeouts, the paged model list, the SSE line loop, JSON errors). A host's `dialect:`
|
|
472
|
+
(`LLM::Dialect`, `fireworks`) changes the auth header, the thinking `display`, the cache breakpoints and a session
|
|
473
|
+
header; [internals/anthropic.md](internals/anthropic.md) covers the adapter.
|
|
463
474
|
|
|
464
475
|
**Errors.** A failed request raises an `LLM::ProviderError` of one kind:
|
|
465
476
|
`ConnectionError` (`RetryExhausted`), `RateLimited` (`CreditsHeld`: a 402 about
|
|
@@ -474,9 +485,10 @@ stream events (server counts, else a chars/4 estimate). Model lists are `LLM::Mo
|
|
|
474
485
|
(`HostRegistry#list_all_models`); the context window comes from the running
|
|
475
486
|
server (`/props`), then the host's model list, then config.
|
|
476
487
|
|
|
477
|
-
**Keys.** A host's API key comes
|
|
478
|
-
`api_key_env:` names
|
|
479
|
-
|
|
488
|
+
**Keys.** A host's API key comes from the environment variable its
|
|
489
|
+
`api_key_env:` names, else, on macOS, the login Keychain's item for that name (`HostKeys`, read through
|
|
490
|
+
`/usr/bin/security` with a deadline; hits cached per process; `SAMAGOTCHI_KEYCHAIN=off` skips it); it never
|
|
491
|
+
reaches config.yml, `SAMAGOTCHI_HOSTS_JSON`, `chi self`, logs or events.
|
|
480
492
|
|
|
481
493
|
## Other components
|
|
482
494
|
|
|
@@ -487,16 +499,26 @@ A map of the parts the overview above doesn't name (paths under `lib/samagotchi/
|
|
|
487
499
|
| Worker | `worker.rb` (`worker_idle_exit.rb`, `worker_inbound.rb`, `worker_signals.rb`) | A session's background process: builds the `Engine` and `Bridge`, runs queued turns, exits when idle. A spawned one traps TERM, HUP and INT (`WorkerSignals`, installed by `run_session_loop` when `SAMAGOTCHI_WORKER_PROCESS=1`): the running turn stops as a Stop with reason `signal`, is saved, and the worker leaves (`:signal`, exit 0; a new worker only for input already queued). A running turn is checkpointed at its iteration boundaries (`TurnCheckpoint`, the `interrupted_turn` mark), and the next worker settles a turn its predecessor died in (`TurnCheckpoint.settle`). |
|
|
488
500
|
| Config | `config.rb` | Every setting as an `Entry`: env, config.yml and CLI layers ([configuration.md](configuration.md)). |
|
|
489
501
|
| MemoryBundle | `memory_bundle.rb`, `memory_bundle/{installer,builder,installed_bundle,plugin_ref,bundle_hook}.rb` (`Installer`, `Builder`, `InstalledBundle`, `PluginRef`, `BundleHook`) | Installing, building and reading bundles of memories, hooks and plugins. |
|
|
490
|
-
| Plugin API | `plugin/api.rb` (`Plugin::Api`), `plugin/loader.rb` | What a bundle's plugin registers: tools, commands, hooks, services ([plugins.md](plugins.md)). |
|
|
502
|
+
| Plugin API | `plugin/api.rb` (`Plugin::Api`), `plugin/tool_spec.rb` (`Plugin::ToolSpec`: a checked tool declaration, how it is registered), `plugin/loader.rb` | What a bundle's plugin registers: tools, commands, annotation actions, hooks, services, toolkits, peer sources ([plugins.md](plugins.md)). |
|
|
503
|
+
| Peer bundles' base | `plugin/peer.rb` (`Plugin::Peer`) | What claude-peer and codex-peer share (the send tool, the command, the cards, payloads); each bundle's subclass sets its strings and transport ([plugins.md](plugins.md#the-claude-peer-bundle)). |
|
|
491
504
|
| Shipped bundles | `bundles/` (`mcp`, `github-pr`, `loop-guard`, `skills`, `source-links`, …) | Bundles that ship with chi. |
|
|
492
|
-
| Hooks | `hooks/registry.rb` (`Hooks::Registry`), `hooks/loader.rb` | Hook registration and dispatch ([hooks.md](hooks.md)). |
|
|
505
|
+
| Hooks | `hooks/registry.rb` (`Hooks::Registry`), `hooks/loader.rb`, `hook_routes.rb` (`HookRoutes`) | Hook registration and dispatch; what a hook or plugin does beyond reading its event (notice, question, stop, steer) routed to the UIs ([hooks.md](hooks.md)). |
|
|
493
506
|
| Guardrails | `guardrails.rb`, `guardrails/shell_lex.rb`, `guardrails/rules.rb`, `guardrail_wiring.rb` | The gate before every tool call: shell lexing, YAML rules, approvals ([guardrails.md](guardrails.md)). |
|
|
494
507
|
| Delegation | `tools/delegate.rb`, `tools/delegate_result.rb`, `tools/delegate_wait.rb`, `child_reports.rb`, `relay_desk.rb` | Child sessions a session starts and waits on, and their approval relay ([sessions.md](sessions.md#delegating)). |
|
|
495
508
|
| send / broadcast | `send_command.rb` (`SendCommand`), `broadcast_command.rb` (`BroadcastCommand`), `broadcast/` | `chi send` and `chi broadcast` ([broadcast.md](broadcast.md), [sub-agent.md](sub-agent.md)). |
|
|
496
509
|
| Idle work | `idle_scheduler.rb` (`IdleScheduler`), `idle_recap.rb` (`IdleRecap`), `idle_reminders.rb` | One poller for an idle session's recap and reminders. |
|
|
497
|
-
| Attached context | `context_sources.rb` (`ContextSources`), `context_poller.rb`, `context_absorber.rb` | External sources a session keeps fresh ([context.md](context.md)). |
|
|
510
|
+
| Attached context | `context_sources.rb` (`ContextSources`), `context_poller.rb`, `context_absorber.rb`, `peer_sources.rb` (`PeerSources`: the sources peer bundles declare) | External sources a session keeps fresh, and the peer loop's wakes ([context.md](context.md)). |
|
|
498
511
|
| LLM context strategies | `llm_context_strategy.rb`, `llm_context_view.rb`, `llm_context_stale.rb`, `llm_context_forget.rb` | What a request sends of the history (none, stale, forget) ([internals/llm-context-forget.md](internals/llm-context-forget.md)). |
|
|
499
512
|
| Session chains | `session_continue.rb` (`SessionContinue`), `session_chain.rb` (`SessionChain`) | A session that continues an earlier one. |
|
|
500
513
|
| Analytics | `analytics_file.rb` (`AnalyticsFile`) | A session's `analytics.json` (`SessionMetrics#persist`). |
|
|
501
514
|
| Images / vision | `image_store.rb` (`ImageStore`), `vision_support.rb`, `vision_context.rb` | Stored prompt images and whether a model takes them. |
|
|
502
|
-
|
|
|
515
|
+
| Toolkits | `toolkit.rb` (`Toolkit`), `toolkit_binding.rb` (`ToolkitBinding`, the Engine's seam), `toolkit_command.rb` (`/toolkit`), `tools/registry.rb` (`Registry#toolkit`, `#left_out_names`), `web/public/toolkit_chip.js` | Which registered tools a session's prompts declare: saved in the session, resolved at a turn's start (`Engine#apply_toolkit!`), named in the prompt's not-loaded line; `chi.toolkit` and `ctx.use_toolkit` for plugins ([configuration.md](configuration.md#toolkits)). |
|
|
516
|
+
| Steer delivery | `steer.rb` (`Steer`), `delivery.rb` (`Delivery`), `steer_cut.rb` (`SteerCut`), `generation_phase.rb`, `waiting_steer.rb`, `steer_cut_after.rb` (`SteerCutAfter`), `cut_policy.rb` (`CutPolicy`) | A message for a running turn: at the next boundary, cutting a thinking-only generation, or queued for the turn's end ([internals/engine.md](internals/engine.md)). |
|
|
517
|
+
| Prompt files | `prompt_files.rb` (`PromptFiles`) | The AGENTS.md chain (root → cwd) and the appended files the system prompt carries. |
|
|
518
|
+
| Model settings | `model_profile.rb` (`ModelProfile`), `model_settings.rb`, `sampling_settings.rb`, `model_catalog.rb` (`ModelCatalog`), `model_notes.rb` (`ModelNotes`) | A model's prompt format, its `models:` entry, its request parameters, the pickable model rows (`chi models`, `/api/models`), the notes its prompt loads. |
|
|
519
|
+
| Dialects | `llm/dialect.rb` (`LLM::Dialect`) | What an `api: anthropic` provider's `/v1/messages` does differently (`dialect: fireworks`) ([internals/anthropic.md](internals/anthropic.md)). |
|
|
520
|
+
| Logging | `log.rb` (`Log`), `log_subscriber.rb` (`LogSubscriber`), `log_path.rb` (`LogPath`), `log_line.rb` | The debug log: tagged records, a session's event trail, where the file goes ([configuration.md](configuration.md#debug-log-file)). |
|
|
521
|
+
| Stores | `reminder_store.rb` (`ReminderStore`), `recap_store.rb` (`RecapStore`) | A session's reminders, and its idle recap (`recap.json`). |
|
|
522
|
+
| Setup and update | `bootstrap_command.rb`, `bootstrap/`, `update_command.rb`, `gem_update.rb` | `chi bootstrap` (first config.yml, system bundle, core profile) and `chi update` (the gem, shipped bundles, the macOS helper). |
|
|
523
|
+
| Process groups | `process_group.rb` (`ProcessGroup`) | A command in a process group of its own, stopped as a group (TERM, grace, KILL): execute, tasks, context fetches, the mcp bundle's servers. |
|
|
524
|
+
| Desktop | `desktop.rb`, `desktop/macos.rb`, `desktop_command.rb`, `desktop/macos/*.swift` | `chi desktop`, the macOS "Send to chi" helper ([desktop.md](desktop.md)). The helper's Swift side: the panel (`Panel.swift`), the chi CLI runner (`ChiRunner.swift`), agent terminals behind `AgentTerminal` (`AgentTerminal.swift`, `Kitty.swift`, `Tmux.swift`; `ChiHelper --agents` is their CLI seam). |
|
data/docs/cli.md
CHANGED
|
@@ -44,7 +44,9 @@ chi bootstrap # try localhost 8080, 11434, 1234, 8000
|
|
|
44
44
|
at once.
|
|
45
45
|
- **The key.** A server that answers 401/403 wants an API key: `--key-env VAR`
|
|
46
46
|
names the environment variable holding it (on a terminal chi asks for the
|
|
47
|
-
name)
|
|
47
|
+
name); on macOS a key only the login Keychain holds under that name counts
|
|
48
|
+
(docs/configuration.md, "API keys from the Keychain"). Only the variable's
|
|
49
|
+
name is written, never the key.
|
|
48
50
|
- **The model.** One model is taken; with several, `--model ID` picks one
|
|
49
51
|
(a terminal gets a numbered list, a script the ids and exit 2). A llama.cpp
|
|
50
52
|
server also shows its context size and the prompt profile its chat template
|
|
@@ -187,6 +189,7 @@ controls exit behavior (`--non-interactive`); `--resume` composes with both.
|
|
|
187
189
|
| `--attach SESSION_ID` | Attach to a session's worker, waking one if it has exited. |
|
|
188
190
|
| `--model NAME` | Use this model for the run (overrides the configured default and a resumed session's model). |
|
|
189
191
|
| `--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. |
|
|
192
|
+
| `--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
193
|
| `--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
194
|
| `--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
195
|
| `--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 +516,16 @@ reasoning of the turn it continues. `/stats` shows it on its `reasoning history:
|
|
|
513
516
|
line, the web as the `reasoning` chip. See "Reasoning history" in
|
|
514
517
|
[configuration.md](configuration.md).
|
|
515
518
|
|
|
516
|
-
|
|
519
|
+
`/toolkit` lists the tools the next turn declares (each with its estimated
|
|
520
|
+
tokens), the session's toolkit and the tools it leaves out. `/toolkit NAME` puts
|
|
521
|
+
the session on another toolkit (dropping its own adds and removes); `/toolkit
|
|
522
|
+
add TOOL` and `/toolkit remove TOOL` change one tool (a tool's name keeps its
|
|
523
|
+
case). The change is saved at once and applies from the next turn's start; a
|
|
524
|
+
running turn keeps its tools. `/toolkit` alone answers mid-turn too; a change
|
|
525
|
+
sent while a turn runs waits for the turn's end. The web shows a session that
|
|
526
|
+
isn't on `full` as the `tools` chip. See [Toolkits](configuration.md#toolkits).
|
|
527
|
+
|
|
528
|
+
A `/thinking`, `/reasoning-history` or `/toolkit` change that restarts the prompt cache
|
|
517
529
|
asks first: the reply says what it costs, changes nothing, and names the line
|
|
518
530
|
that applies it (`/thinking low --yes`). `--yes` (or `-y`) applies it at once.
|
|
519
531
|
The terminal puts that line in the next input (Enter applies it); the web's
|