samagotchi 0.2.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 +7 -0
- data/CHANGELOG.md +43 -0
- data/LICENSE +21 -0
- data/README.md +126 -0
- data/bin/chi +1140 -0
- data/docs/architecture.md +299 -0
- data/docs/cli.md +490 -0
- data/docs/configuration.md +494 -0
- data/docs/desktop.md +97 -0
- data/docs/guardrails.md +218 -0
- data/docs/hooks.md +309 -0
- data/docs/internals/background-tasks.md +26 -0
- data/docs/internals/context-telemetry.md +36 -0
- data/docs/internals/gemma4-contract.md +23 -0
- data/docs/internals/tool-guardrails.md +45 -0
- data/docs/memory.md +85 -0
- data/docs/plugins.md +819 -0
- data/docs/releasing.md +135 -0
- data/docs/sessions.md +155 -0
- data/lib/samagotchi/bridge/bounded_queue.rb +70 -0
- data/lib/samagotchi/bridge/card_store.rb +126 -0
- data/lib/samagotchi/bridge/event_id.rb +25 -0
- data/lib/samagotchi/bridge/ring_buffer.rb +63 -0
- data/lib/samagotchi/bridge/sse_writer.rb +248 -0
- data/lib/samagotchi/bridge/turn_accumulator.rb +189 -0
- data/lib/samagotchi/bridge.rb +993 -0
- data/lib/samagotchi/bridge_client/event_stream.rb +158 -0
- data/lib/samagotchi/bridge_client/sse_parser.rb +51 -0
- data/lib/samagotchi/bridge_client.rb +330 -0
- data/lib/samagotchi/bundle_needs.rb +97 -0
- data/lib/samagotchi/bundles/btw/manifest.yml +10 -0
- data/lib/samagotchi/bundles/btw/plugin.rb +100 -0
- data/lib/samagotchi/bundles/guardrails/guardrails/rules.yml +82 -0
- data/lib/samagotchi/bundles/guardrails/guardrails.md +14 -0
- data/lib/samagotchi/bundles/guardrails/manifest.yml +8 -0
- data/lib/samagotchi/bundles/known-names/hooks/known_names.rb +210 -0
- data/lib/samagotchi/bundles/known-names/known_names.md +3 -0
- data/lib/samagotchi/bundles/known-names/manifest.yml +14 -0
- data/lib/samagotchi/bundles/loop-guard/manifest.yml +10 -0
- data/lib/samagotchi/bundles/loop-guard/plugin.rb +158 -0
- data/lib/samagotchi/bundles/mcp/manifest.yml +11 -0
- data/lib/samagotchi/bundles/mcp/plugin.rb +631 -0
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +149 -0
- data/lib/samagotchi/bundles/system/delegated.md +10 -0
- data/lib/samagotchi/bundles/system/identity.md +7 -0
- data/lib/samagotchi/bundles/system/manifest.yml +11 -0
- data/lib/samagotchi/bundles/system/memory_guide.md +107 -0
- data/lib/samagotchi/bundles/system/self_map.md +55 -0
- data/lib/samagotchi/cancellation_controller.rb +78 -0
- data/lib/samagotchi/client.rb +429 -0
- data/lib/samagotchi/commands/registry.rb +112 -0
- data/lib/samagotchi/config.rb +910 -0
- data/lib/samagotchi/context_note.rb +77 -0
- data/lib/samagotchi/context_quote.rb +21 -0
- data/lib/samagotchi/context_usage.rb +66 -0
- data/lib/samagotchi/context_window.rb +76 -0
- data/lib/samagotchi/debug_log.rb +110 -0
- data/lib/samagotchi/desktop/macos/App.swift +102 -0
- data/lib/samagotchi/desktop/macos/ChiRunner.swift +201 -0
- data/lib/samagotchi/desktop/macos/Hotkey.swift +42 -0
- data/lib/samagotchi/desktop/macos/Info.plist.erb +42 -0
- data/lib/samagotchi/desktop/macos/Panel.swift +383 -0
- data/lib/samagotchi/desktop/macos.rb +255 -0
- data/lib/samagotchi/desktop.rb +21 -0
- data/lib/samagotchi/desktop_command.rb +143 -0
- data/lib/samagotchi/engine.rb +2807 -0
- data/lib/samagotchi/guardrails/approval.rb +125 -0
- data/lib/samagotchi/guardrails/approvals.rb +177 -0
- data/lib/samagotchi/guardrails/context.rb +71 -0
- data/lib/samagotchi/guardrails/gate.rb +125 -0
- data/lib/samagotchi/guardrails/load_failures.rb +46 -0
- data/lib/samagotchi/guardrails/protected_paths.rb +77 -0
- data/lib/samagotchi/guardrails/rules.rb +199 -0
- data/lib/samagotchi/guardrails/targets.rb +119 -0
- data/lib/samagotchi/guardrails/verdict.rb +134 -0
- data/lib/samagotchi/guardrails.rb +18 -0
- data/lib/samagotchi/hooks/bundle_loader.rb +158 -0
- data/lib/samagotchi/hooks/loader.rb +162 -0
- data/lib/samagotchi/hooks/registry.rb +261 -0
- data/lib/samagotchi/hooks.rb +30 -0
- data/lib/samagotchi/host_registry.rb +315 -0
- data/lib/samagotchi/idle_client.rb +147 -0
- data/lib/samagotchi/idle_recap.rb +549 -0
- data/lib/samagotchi/idle_reminders.rb +101 -0
- data/lib/samagotchi/idle_scheduler.rb +76 -0
- data/lib/samagotchi/image_store.rb +393 -0
- data/lib/samagotchi/installed_gem.rb +38 -0
- data/lib/samagotchi/kernel_loop.rb +1017 -0
- data/lib/samagotchi/launch_mode.rb +34 -0
- data/lib/samagotchi/llm/backend.rb +28 -0
- data/lib/samagotchi/llm/chat_loop.rb +450 -0
- data/lib/samagotchi/llm/errors.rb +329 -0
- data/lib/samagotchi/llm/http.rb +412 -0
- data/lib/samagotchi/llm/model_result.rb +72 -0
- data/lib/samagotchi/llm/native_backend.rb +50 -0
- data/lib/samagotchi/llm/native_tool_normalizer.rb +277 -0
- data/lib/samagotchi/llm/openai_chat.rb +403 -0
- data/lib/samagotchi/llm/usage.rb +79 -0
- data/lib/samagotchi/log.rb +200 -0
- data/lib/samagotchi/log_line.rb +127 -0
- data/lib/samagotchi/log_path.rb +31 -0
- data/lib/samagotchi/log_subscriber.rb +163 -0
- data/lib/samagotchi/memory_bundle/builder.rb +364 -0
- data/lib/samagotchi/memory_bundle/index_updater.rb +123 -0
- data/lib/samagotchi/memory_bundle/installer.rb +528 -0
- data/lib/samagotchi/memory_bundle/listing.rb +72 -0
- data/lib/samagotchi/memory_bundle/manifest.rb +225 -0
- data/lib/samagotchi/memory_bundle/merger.rb +52 -0
- data/lib/samagotchi/memory_bundle/placeholder.rb +37 -0
- data/lib/samagotchi/memory_bundle/provenance.rb +257 -0
- data/lib/samagotchi/memory_bundle/source.rb +153 -0
- data/lib/samagotchi/memory_bundle/status.rb +107 -0
- data/lib/samagotchi/memory_bundle/system_bundle.rb +161 -0
- data/lib/samagotchi/memory_bundle/uninstaller.rb +128 -0
- data/lib/samagotchi/memory_bundle.rb +17 -0
- data/lib/samagotchi/memory_paths.rb +101 -0
- data/lib/samagotchi/model_overlay.rb +53 -0
- data/lib/samagotchi/model_profile.rb +309 -0
- data/lib/samagotchi/muted_memories.rb +66 -0
- data/lib/samagotchi/note_command.rb +163 -0
- data/lib/samagotchi/output_formatter.rb +100 -0
- data/lib/samagotchi/owner_lock.rb +110 -0
- data/lib/samagotchi/pending_input_queue.rb +48 -0
- data/lib/samagotchi/plugin/api.rb +362 -0
- data/lib/samagotchi/plugin/context.rb +193 -0
- data/lib/samagotchi/plugin/loader.rb +126 -0
- data/lib/samagotchi/plugin/service.rb +117 -0
- data/lib/samagotchi/plugin/sessions.rb +150 -0
- data/lib/samagotchi/plugin/side_question.rb +60 -0
- data/lib/samagotchi/plugin/tool_result.rb +24 -0
- data/lib/samagotchi/project_scope.rb +25 -0
- data/lib/samagotchi/prompt.rb +119 -0
- data/lib/samagotchi/prompt_literal_guard.rb +70 -0
- data/lib/samagotchi/recap_store.rb +92 -0
- data/lib/samagotchi/reminder_store.rb +165 -0
- data/lib/samagotchi/self_report.rb +195 -0
- data/lib/samagotchi/send_command.rb +170 -0
- data/lib/samagotchi/served_model.rb +32 -0
- data/lib/samagotchi/session.rb +508 -0
- data/lib/samagotchi/session_commands.rb +527 -0
- data/lib/samagotchi/session_delete_command.rb +105 -0
- data/lib/samagotchi/session_manager.rb +1049 -0
- data/lib/samagotchi/session_metrics.rb +466 -0
- data/lib/samagotchi/session_observer.rb +117 -0
- data/lib/samagotchi/terminal_ui/attach_launcher.rb +118 -0
- data/lib/samagotchi/terminal_ui/attached_loop.rb +1037 -0
- data/lib/samagotchi/terminal_ui/attached_view.rb +264 -0
- data/lib/samagotchi/terminal_ui/event_renderer.rb +192 -0
- data/lib/samagotchi/terminal_ui/formatting.rb +291 -0
- data/lib/samagotchi/terminal_ui/image_input.rb +36 -0
- data/lib/samagotchi/terminal_ui/input_support.rb +324 -0
- data/lib/samagotchi/terminal_ui/legacy_surface.rb +111 -0
- data/lib/samagotchi/terminal_ui/line_reader.rb +113 -0
- data/lib/samagotchi/terminal_ui/live_region.rb +36 -0
- data/lib/samagotchi/terminal_ui/plain_surface.rb +51 -0
- data/lib/samagotchi/terminal_ui/question_prompt.rb +153 -0
- data/lib/samagotchi/terminal_ui/question_slot.rb +131 -0
- data/lib/samagotchi/terminal_ui/reline_seam.rb +216 -0
- data/lib/samagotchi/terminal_ui/repl_input.rb +138 -0
- data/lib/samagotchi/terminal_ui/screen.rb +316 -0
- data/lib/samagotchi/terminal_ui/surface.rb +47 -0
- data/lib/samagotchi/terminal_ui/thinking_line.rb +101 -0
- data/lib/samagotchi/terminal_ui.rb +1992 -0
- data/lib/samagotchi/thinking_ticker.rb +110 -0
- data/lib/samagotchi/thought_stream_splitter.rb +149 -0
- data/lib/samagotchi/token_usage.rb +88 -0
- data/lib/samagotchi/tool_activity.rb +216 -0
- data/lib/samagotchi/tool_call_parser.rb +637 -0
- data/lib/samagotchi/tool_declarations.rb +561 -0
- data/lib/samagotchi/tool_runner.rb +211 -0
- data/lib/samagotchi/tools/args.rb +259 -0
- data/lib/samagotchi/tools/ask_user_question.rb +152 -0
- data/lib/samagotchi/tools/builtins.rb +122 -0
- data/lib/samagotchi/tools/cancel_reminder.rb +21 -0
- data/lib/samagotchi/tools/delegate.rb +167 -0
- data/lib/samagotchi/tools/delegate_result.rb +53 -0
- data/lib/samagotchi/tools/delegate_wait.rb +153 -0
- data/lib/samagotchi/tools/edit.rb +155 -0
- data/lib/samagotchi/tools/execute.rb +214 -0
- data/lib/samagotchi/tools/list_reminders.rb +20 -0
- data/lib/samagotchi/tools/list_sessions.rb +74 -0
- data/lib/samagotchi/tools/memory.rb +256 -0
- data/lib/samagotchi/tools/output_guardrails.rb +93 -0
- data/lib/samagotchi/tools/peers.rb +18 -0
- data/lib/samagotchi/tools/read.rb +182 -0
- data/lib/samagotchi/tools/register_reminder.rb +53 -0
- data/lib/samagotchi/tools/registry.rb +60 -0
- data/lib/samagotchi/tools/send_note.rb +49 -0
- data/lib/samagotchi/tools/task_create.rb +29 -0
- data/lib/samagotchi/tools/task_get.rb +39 -0
- data/lib/samagotchi/tools/task_list.rb +43 -0
- data/lib/samagotchi/tools/task_runtime.rb +311 -0
- data/lib/samagotchi/tools/task_stop.rb +29 -0
- data/lib/samagotchi/tools/task_wait.rb +104 -0
- data/lib/samagotchi/tools/tool_path.rb +18 -0
- data/lib/samagotchi/tools/web_fetch.rb +163 -0
- data/lib/samagotchi/tools/write.rb +26 -0
- data/lib/samagotchi/turn_flow.rb +242 -0
- data/lib/samagotchi/turn_note.rb +76 -0
- data/lib/samagotchi/turn_tally.rb +101 -0
- data/lib/samagotchi/version.rb +7 -0
- data/lib/samagotchi/vision_context.rb +132 -0
- data/lib/samagotchi/vision_support.rb +109 -0
- data/lib/samagotchi/web/app.rb +1349 -0
- data/lib/samagotchi/web/markdown_renderer.rb +107 -0
- data/lib/samagotchi/web/message_parts.rb +169 -0
- data/lib/samagotchi/web/public/activity.js +100 -0
- data/lib/samagotchi/web/public/annotations.js +67 -0
- data/lib/samagotchi/web/public/app.js +2382 -0
- data/lib/samagotchi/web/public/card.js +74 -0
- data/lib/samagotchi/web/public/chat_view.js +360 -0
- data/lib/samagotchi/web/public/chunk_router.js +25 -0
- data/lib/samagotchi/web/public/command_complete.js +39 -0
- data/lib/samagotchi/web/public/composer_size.js +19 -0
- data/lib/samagotchi/web/public/copy.js +142 -0
- data/lib/samagotchi/web/public/ctx.js +35 -0
- data/lib/samagotchi/web/public/data.js +256 -0
- data/lib/samagotchi/web/public/format.js +232 -0
- data/lib/samagotchi/web/public/hold.js +78 -0
- data/lib/samagotchi/web/public/images.js +77 -0
- data/lib/samagotchi/web/public/index.html +568 -0
- data/lib/samagotchi/web/public/init_row.js +60 -0
- data/lib/samagotchi/web/public/model_pick.js +23 -0
- data/lib/samagotchi/web/public/question_card.js +100 -0
- data/lib/samagotchi/web/public/route.js +17 -0
- data/lib/samagotchi/web/public/scope.js +36 -0
- data/lib/samagotchi/web/public/scroll.js +24 -0
- data/lib/samagotchi/web/public/sentences.js +88 -0
- data/lib/samagotchi/web/public/sessions_list.js +60 -0
- data/lib/samagotchi/web/public/strip.js +25 -0
- data/lib/samagotchi/web/public/tally.js +37 -0
- data/lib/samagotchi/web/public/thinking_ticker.js +79 -0
- data/lib/samagotchi/web/public/timing.js +185 -0
- data/lib/samagotchi/web/public/turn_events.js +209 -0
- data/lib/samagotchi/web/public/turn_model.js +204 -0
- data/lib/samagotchi/web/public/turn_view.js +587 -0
- data/lib/samagotchi/web/server.rb +183 -0
- data/lib/samagotchi/web/session_hub.rb +329 -0
- data/lib/samagotchi/web/session_summary.rb +85 -0
- data/lib/samagotchi/worker.rb +635 -0
- data/lib/samagotchi/worker_idle_exit.rb +87 -0
- data/lib/samagotchi.rb +12 -0
- metadata +374 -0
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
## Global Config File
|
|
4
|
+
|
|
5
|
+
Chi can preload a global config file and expose those entries as environment
|
|
6
|
+
variables before the app boots.
|
|
7
|
+
|
|
8
|
+
Default path:
|
|
9
|
+
|
|
10
|
+
- `$XDG_CONFIG_HOME/samagotchi/config.yml`
|
|
11
|
+
- Fallback when `XDG_CONFIG_HOME` is unset: `~/.config/samagotchi/config.yml`
|
|
12
|
+
|
|
13
|
+
Example:
|
|
14
|
+
|
|
15
|
+
```yaml
|
|
16
|
+
SAMAGOTCHI_DEFAULT_MODEL: Qwen3-14B-Instruct
|
|
17
|
+
server:
|
|
18
|
+
host: 192.0.2.10
|
|
19
|
+
port: 8081
|
|
20
|
+
SAMAGOTCHI_THINKING_UI: spinner
|
|
21
|
+
|
|
22
|
+
# Multi-host (optional): aggregated /models and per-model routing.
|
|
23
|
+
# Bare SAMAGOTCHI_DEFAULT_MODEL uses the default host; host:model pins to a host.
|
|
24
|
+
# Transport per host overrides SAMAGOTCHI_SERVER_TRANSPORT; api: openai makes a
|
|
25
|
+
# host use the OpenAI chat API instead of chi's raw prompt.
|
|
26
|
+
hosts:
|
|
27
|
+
main:
|
|
28
|
+
host: localhost
|
|
29
|
+
port: 8080
|
|
30
|
+
transport: llama_cpp
|
|
31
|
+
small-box:
|
|
32
|
+
host: 192.0.2.20
|
|
33
|
+
port: 8080
|
|
34
|
+
|
|
35
|
+
# Idle recap: on by default, written with the session's own model and host
|
|
36
|
+
# (on a paid remote host that is one small request per idle window).
|
|
37
|
+
# host_ref + model pin another model: host_ref asks that host's OpenAI API (its
|
|
38
|
+
# url:, else http://host:port/v1) with its api_key_env; base_url is an OpenAI API
|
|
39
|
+
# base as given (e.g. http://h:8081/v1). recap: false turns it off.
|
|
40
|
+
recap:
|
|
41
|
+
# host_ref: small-box
|
|
42
|
+
# model: your-small-model-id
|
|
43
|
+
# inactivity: 180
|
|
44
|
+
# timeout: 30
|
|
45
|
+
# min_user_turns: 2
|
|
46
|
+
# sentences: 2-4 # or 3, 5-7; 1-10 (from the next recap written)
|
|
47
|
+
|
|
48
|
+
model_aliases:
|
|
49
|
+
small: your-small-model-id
|
|
50
|
+
tiny: small-box:your-small-model-id # alias may be bare or host:model (hybrid)
|
|
51
|
+
|
|
52
|
+
# Plain chi runs its session in a background worker and attaches to it, so the
|
|
53
|
+
# web UI can share it (default true; env SAMAGOTCHI_SESSION_SHARED). false keeps
|
|
54
|
+
# the in-process REPL; --no-shared does for one run.
|
|
55
|
+
session:
|
|
56
|
+
shared: true
|
|
57
|
+
# idle_exit_minutes: 30 # an unused worker exits after this (0 = never)
|
|
58
|
+
# keep_empty: false # true keeps sessions nothing happened in (default: deleted when left)
|
|
59
|
+
# max_children: 4 # running sessions one session may have delegated at a time (the delegate tool)
|
|
60
|
+
|
|
61
|
+
# Baseline memories preloaded into the system prompt (same shape as --memory).
|
|
62
|
+
# CLI --memory entries are appended after these, deduped.
|
|
63
|
+
memories:
|
|
64
|
+
- system/user_preferences
|
|
65
|
+
- project/feature-env-template
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Behavior:
|
|
69
|
+
|
|
70
|
+
- The file is optional.
|
|
71
|
+
- Top level is a YAML mapping of scalar env overrides plus nested sections.
|
|
72
|
+
- Real environment variables still win over config-file values.
|
|
73
|
+
- Workers inherit hosts via `SAMAGOTCHI_HOSTS_JSON` propagated through `SessionManager.spawn_options`.
|
|
74
|
+
|
|
75
|
+
This lets you run `chi` without repeating common defaults such as model
|
|
76
|
+
and llama host/port on every invocation.
|
|
77
|
+
|
|
78
|
+
Note: The global config file supports both flat scalar entries (for env vars)
|
|
79
|
+
and nested sections like `hosts:`, `recap:`, `hooks:`, `guardrails:` (see
|
|
80
|
+
[Guardrails](guardrails.md)), `bundles:` (a bundle's settings for its hooks,
|
|
81
|
+
see [Hooks: Settings](hooks.md#settings)), `model_aliases:`,
|
|
82
|
+
`memories:`. Scalar entries are loaded as environment
|
|
83
|
+
variables; non-scalar sections are skipped by the env-loader and parsed by
|
|
84
|
+
their respective subsystems (e.g. the hooks system, `HostRegistry`). The
|
|
85
|
+
`memories:` list is the persistent baseline for preloaded memory entries —
|
|
86
|
+
the same name shape as `--memory` (bare name or `scope/name`), merged under
|
|
87
|
+
any per-run `--memory` values (config baseline first, deduped). A per-run
|
|
88
|
+
`--mute NAME` removes an entry from the merged list for that session (see
|
|
89
|
+
"Muting a memory" in cli.md).
|
|
90
|
+
|
|
91
|
+
## Model Server Transport
|
|
92
|
+
|
|
93
|
+
Chi talks to a model server over HTTP and supports three transports:
|
|
94
|
+
|
|
95
|
+
- `llama_cpp` (default): llama.cpp's native `/completion` and `/models` endpoints.
|
|
96
|
+
- `mlx`: [mlx-lm](https://github.com/ml-explore/mlx-lm)'s OpenAI-compatible
|
|
97
|
+
`/v1/completions` and `/v1/models` endpoints (Apple Silicon-native models).
|
|
98
|
+
- `omlx`: [oMLX](https://github.com/jundot/omlx) (the mlx-lm successor —
|
|
99
|
+
continuous batching + tiered SSD KV cache) using the same `/v1/completions`
|
|
100
|
+
and `/v1/models` endpoints as `mlx`.
|
|
101
|
+
|
|
102
|
+
Select the transport with `SAMAGOTCHI_SERVER_TRANSPORT` (`llama_cpp`, `mlx`, or
|
|
103
|
+
`omlx`). `SAMAGOTCHI_SERVER_HOST`/`SAMAGOTCHI_SERVER_PORT` are reused for all three —
|
|
104
|
+
only the request/response shape differs. oMLX's default server port is `8000` (not
|
|
105
|
+
`8080`), so point `SAMAGOTCHI_SERVER_PORT` at it, e.g. `SAMAGOTCHI_SERVER_PORT=8000`.
|
|
106
|
+
With `hosts:` each entry may set `transport: llama_cpp|mlx|omlx` to override the
|
|
107
|
+
global transport per host (`lib/samagotchi/host_registry.rb`).
|
|
108
|
+
|
|
109
|
+
Each host may also set `api:`, which says how chi talks to it:
|
|
110
|
+
|
|
111
|
+
- `llama_cpp`, `mlx` or `omlx`: chi's own raw-prompt loop (the value is also the
|
|
112
|
+
host's transport, so don't set a different `transport:` next to it);
|
|
113
|
+
- `openai`: the OpenAI chat API at `http://HOST:PORT/v1`, or at `url:` (see below).
|
|
114
|
+
|
|
115
|
+
Without `api:` a host uses the raw-prompt loop, as before. The loop follows the
|
|
116
|
+
model's host, so `/model other-host:model` can move a session between the two.
|
|
117
|
+
Workers started by plain `chi`, `chi web` or `--attach` get the same hosts, `api:` included.
|
|
118
|
+
|
|
119
|
+
Example for mlx-lm:
|
|
120
|
+
|
|
121
|
+
```yaml
|
|
122
|
+
SAMAGOTCHI_SERVER_TRANSPORT: mlx
|
|
123
|
+
server:
|
|
124
|
+
host: 127.0.0.1
|
|
125
|
+
port: 8080
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```shell
|
|
129
|
+
mlx_lm.server --model mlx-community/Qwen3-14B-Instruct-4bit
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Example for oMLX:
|
|
133
|
+
|
|
134
|
+
```yaml
|
|
135
|
+
SAMAGOTCHI_SERVER_TRANSPORT: omlx
|
|
136
|
+
server:
|
|
137
|
+
host: 192.0.2.10
|
|
138
|
+
port: 8000
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Both the `mlx` and `omlx` transports still send chi's own raw formatted prompt
|
|
142
|
+
(via `/v1/completions`) rather than a `messages` array, so the existing
|
|
143
|
+
per-model prompt/tool-call formatting is unaffected — neither server reapplies its
|
|
144
|
+
own chat template on this endpoint. Only the Gemma4 (`<|tool_call>…`) and Qwen3.6
|
|
145
|
+
(`[[…]]`/`<|tool_call>`) tool-call formats are in scope; GLM/Mistral/Kimi/MiniMax
|
|
146
|
+
formats are not parsed.
|
|
147
|
+
|
|
148
|
+
For an OpenAI Chat Completions server such as [Splash](https://github.com/incoai/splash)
|
|
149
|
+
(a fast inference engine for Macs that works well for a local setup alongside
|
|
150
|
+
llama.cpp), give its host `api: openai`:
|
|
151
|
+
|
|
152
|
+
```yaml
|
|
153
|
+
default:
|
|
154
|
+
model: splash:incoai/Qwen3.6-35B-A3B-Splash
|
|
155
|
+
hosts:
|
|
156
|
+
splash:
|
|
157
|
+
host: 192.0.2.10
|
|
158
|
+
port: 8000
|
|
159
|
+
api: openai
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
A host can give `url:` instead of `host:`/`port:` (not both): `http` or `https`,
|
|
163
|
+
with an optional path. For `api: openai` the url is the API base as written (no
|
|
164
|
+
`/v1` is added); raw-prompt hosts use only its scheme, host and port. A remote
|
|
165
|
+
provider's key comes from the environment variable that `api_key_env:` names;
|
|
166
|
+
the key itself never goes into config.yml, `chi self`, logs or events, and
|
|
167
|
+
workers get it by inheriting the environment:
|
|
168
|
+
|
|
169
|
+
```yaml
|
|
170
|
+
hosts:
|
|
171
|
+
fw:
|
|
172
|
+
url: https://api.fireworks.ai/inference/v1
|
|
173
|
+
api: openai
|
|
174
|
+
api_key_env: FIREWORKS_API_KEY
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
`chi self` shows the variable and whether it is set (`api key FIREWORKS_API_KEY (set)`).
|
|
178
|
+
|
|
179
|
+
For models on that host, chi uses the chat loop (its own OpenAI chat adapter): it
|
|
180
|
+
takes the OpenAI base (`url:`, else `http://HOST:PORT/v1`) and streams messages plus
|
|
181
|
+
function schemas from `/v1/chat/completions`; the model's reasoning (`reasoning_content`)
|
|
182
|
+
shows as thinking. This works with Splash and with llama.cpp servers that
|
|
183
|
+
expose the OpenAI-compatible chat endpoint. In verbose mode (`-v`), chi prints the loop the
|
|
184
|
+
starting model uses (`[verbose] backend=chat` or `backend=native`); request
|
|
185
|
+
bodies are not logged. The interactive REPL, `--prompt`,
|
|
186
|
+
workers, and resumed sessions all use the loop of the model's host. Hosts without
|
|
187
|
+
`api: openai` keep using the native `/completion`, `/v1/completions`, or oMLX
|
|
188
|
+
transport path.
|
|
189
|
+
|
|
190
|
+
To manually verify a live chat-loop tool round trip, run the gated integration
|
|
191
|
+
spec. It requires the model to call `execute` and return the current UTC date:
|
|
192
|
+
|
|
193
|
+
```shell
|
|
194
|
+
SAMAGOTCHI_INTEGRATION=1 \
|
|
195
|
+
SAMAGOTCHI_SERVER_HOST=192.0.2.10 SAMAGOTCHI_SERVER_PORT=8000 \
|
|
196
|
+
SAMAGOTCHI_DEFAULT_MODEL=incoai/Qwen3.8-27B-Splash \
|
|
197
|
+
bundle exec rspec spec/integration/chat_loop_spec.rb -fd < /dev/null
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
The same test works against a llama.cpp OpenAI-compatible server by changing
|
|
201
|
+
the host, port, and model values. The test is skipped unless
|
|
202
|
+
`SAMAGOTCHI_INTEGRATION=1` is set.
|
|
203
|
+
|
|
204
|
+
oMLX's known tool-call limitation (a stream filter that strips markup) only
|
|
205
|
+
affects its `/v1/chat/completions` endpoint, not the `/v1/completions` endpoint
|
|
206
|
+
chi uses, so raw `[[…]]`/`<|tool_call>` markers stream through untouched.
|
|
207
|
+
|
|
208
|
+
`SAMAGOTCHI_DEFAULT_MODEL` (config default) and `/model` (runtime effective) pick the model; the status line and `/model`
|
|
209
|
+
output always render the runtime effective model (showing default when diverged). Which prompt format it gets is the
|
|
210
|
+
prompt profile (see "Prompt profile" below). How the selector reaches the request differs by transport:
|
|
211
|
+
|
|
212
|
+
- **mlx** (`mlx_lm.server`): the `model` field is omitted entirely — the server
|
|
213
|
+
uses whatever was loaded via its own `--model` CLI flag.
|
|
214
|
+
- **omlx**: the server *requires* a `model` field and returns `HTTP 400`
|
|
215
|
+
(`model: Field required`) without it, so samagotchi forwards the selector
|
|
216
|
+
resolved to the exact id listed in the server's `/v1/models` — matched by exact
|
|
217
|
+
(case-insensitive) first, then substring, then passed through unchanged. That
|
|
218
|
+
resolved id is usually prefixed (e.g. `mlx-community--gemma-3-4b-it-4bit`), so a
|
|
219
|
+
short selector such as `gemma-3-4b-it-4bit` is what you set in
|
|
220
|
+
`SAMAGOTCHI_DEFAULT_MODEL`. An unknown selector passes through raw and oMLX 404s,
|
|
221
|
+
listing its available models; if `/v1/models` is unreachable, samagotchi falls
|
|
222
|
+
back to the raw selector and lets the server decide (its own 400/404). Either
|
|
223
|
+
error fails the turn with the server's message (see "Server errors" below). Runtime
|
|
224
|
+
model switch re-resolves each completion (the `/v1/models` id list is cached per
|
|
225
|
+
client; the selector itself is re-resolved every time).
|
|
226
|
+
|
|
227
|
+
## Prompt profile
|
|
228
|
+
|
|
229
|
+
A native host (`llama_cpp`, `mlx`, `omlx`) gets a raw prompt in one model family's format: its turn markers, tool-call
|
|
230
|
+
syntax, thought tags and stop sequences. That is the prompt profile, `qwen36` or `gemma4`. A wrong one is not just
|
|
231
|
+
worse output: a ChatML model under `gemma4` never hits a stop sequence, generates until its limit and then runs the
|
|
232
|
+
tool calls it made up on the way. The first of these that says something wins:
|
|
233
|
+
|
|
234
|
+
1. `--profile NAME` (or `--model-profile NAME`), then `SAMAGOTCHI_MODEL_PROFILE`: for every model in the process,
|
|
235
|
+
including one picked later with `/model`.
|
|
236
|
+
2. `models:` in `config.yml`, keyed by model id or alias (case-insensitive; the name as typed, alias-resolved or
|
|
237
|
+
without its host prefix):
|
|
238
|
+
|
|
239
|
+
```yaml
|
|
240
|
+
models:
|
|
241
|
+
ornith-ai/Ornith-1.5-35B-A3B-GGUF:Q4_K_M:
|
|
242
|
+
profile: qwen36
|
|
243
|
+
my-alias:
|
|
244
|
+
profile: qwen36
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
3. `profile:` on a `hosts:` entry, for anything that host serves:
|
|
248
|
+
|
|
249
|
+
```yaml
|
|
250
|
+
hosts:
|
|
251
|
+
mlx:
|
|
252
|
+
host: 192.0.2.10
|
|
253
|
+
port: 8081
|
|
254
|
+
transport: mlx
|
|
255
|
+
profile: qwen36
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
4. The server's chat template, on `llama_cpp` hosts: `/props` (asked with `?model=`, which a router needs) with
|
|
259
|
+
`<|im_start|>` and `<function=` is `qwen36`, any other ChatML template too; `<|turn>` and `<|tool_call>` is `gemma4`.
|
|
260
|
+
mlx_lm.server and oMLX publish no template, so config or the name decides there.
|
|
261
|
+
5. The name: `qwen` → `qwen36`, `gemma` → `gemma4`.
|
|
262
|
+
6. `qwen36`.
|
|
263
|
+
|
|
264
|
+
An unknown value in `models:` or `hosts:` warns and is skipped; an unknown `--profile` or `SAMAGOTCHI_MODEL_PROFILE`
|
|
265
|
+
warns too (the CLI refuses it). The profile is resolved at start and on `/model`, then kept for the session, so the
|
|
266
|
+
system prompt stays the same; a server that swaps models between turns goes unnoticed until `/model` or a new chi. If
|
|
267
|
+
`/props` could not be read at start (server down, or 503 while loading), chi asks again before the next turn.
|
|
268
|
+
|
|
269
|
+
`/stats` and `/model` show the profile and its source (`cli`, `env`, `config (models: …)`, `config (hosts.<name>)`,
|
|
270
|
+
`server (chat_template)`, `name`, `default`); `chi self` shows what config says without asking the server. A chat
|
|
271
|
+
host (`api: openai`) formats nothing itself: its profile comes from the name and only strips thought tags.
|
|
272
|
+
|
|
273
|
+
Workers get `hosts:` (with `profile:`) through `SAMAGOTCHI_HOSTS_JSON` and read `models:` from the same config file.
|
|
274
|
+
`--profile` reaches the worker a chi starts, but a worker that another process wakes later (`chi web`, `--attach`
|
|
275
|
+
after an idle exit) gets that process's environment, so put a lasting choice in config.
|
|
276
|
+
|
|
277
|
+
## Llama HTTP Timeouts
|
|
278
|
+
|
|
279
|
+
Long-running llama.cpp completions can exceed Ruby's default HTTP read timeout.
|
|
280
|
+
Configure these environment variables to avoid premature request failures:
|
|
281
|
+
|
|
282
|
+
- `SAMAGOTCHI_SERVER_OPEN_TIMEOUT` (default: `10`) connection timeout in seconds.
|
|
283
|
+
- `SAMAGOTCHI_SERVER_READ_TIMEOUT` (default: `600`) response read timeout in seconds.
|
|
284
|
+
|
|
285
|
+
A streamed answer also has a **first-token limit**: the seconds it may take to show its first text, reasoning or
|
|
286
|
+
tool call. A remote provider can keep a queued request open for minutes with SSE keep-alive comments
|
|
287
|
+
(OpenRouter's `: OPENROUTER PROCESSING`), which reset the read timeout, so only this limit ends the wait. The
|
|
288
|
+
turn then fails with `no answer from host <name> within 120s (first_token_timeout); …` and is not retried.
|
|
289
|
+
|
|
290
|
+
```yaml
|
|
291
|
+
hosts:
|
|
292
|
+
openrouter:
|
|
293
|
+
url: https://openrouter.ai/api/v1
|
|
294
|
+
api: openai
|
|
295
|
+
api_key_env: OPENROUTER_API_KEY
|
|
296
|
+
first_token_timeout: 180 # seconds; 0 = off
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
`hosts.<name>.first_token_timeout` wins over `server.first_token_timeout` (`SAMAGOTCHI_SERVER_FIRST_TOKEN_TIMEOUT`),
|
|
300
|
+
which applies to every host. With neither set, remote hosts (an API key or an https url) get 120 seconds and local
|
|
301
|
+
servers no limit: a long prompt evaluation is normal there, and the read timeout catches a dead server.
|
|
302
|
+
|
|
303
|
+
Every chat request carries the session's id as a `Session-Id` header (next to `User-Agent: chi/<version>`). A
|
|
304
|
+
gateway that spreads requests over several providers can key on it to keep one conversation on one provider, so
|
|
305
|
+
prompt caches hit and every turn is answered by the same model. Servers that don't know the header ignore it.
|
|
306
|
+
|
|
307
|
+
## Llama Model Routing
|
|
308
|
+
|
|
309
|
+
To explicitly route requests to a named model in llama.cpp, set:
|
|
310
|
+
|
|
311
|
+
- `SAMAGOTCHI_DEFAULT_MODEL` (required): model name/id sent as the `model` field on `/completion` requests.
|
|
312
|
+
|
|
313
|
+
When `SAMAGOTCHI_DEFAULT_MODEL` is unset or blank, Samagotchi fails fast with a clear startup/configuration error.
|
|
314
|
+
|
|
315
|
+
With several `hosts:`, an unqualified model name goes to the host whose `/models`
|
|
316
|
+
list has it (after `/models` ran), by exact id first, then by substring. A
|
|
317
|
+
**remote** host (one with `api_key_env:` or an `https` url) is only chosen by exact
|
|
318
|
+
id, `host:model` or an alias, never by a substring, and its model list is kept for
|
|
319
|
+
10 minutes (60s for local hosts). For a chat host the context window comes from
|
|
320
|
+
the running server (llama.cpp's `/props`), else the window the host's model list
|
|
321
|
+
gives (`context_length`, `context_window`, `max_model_len` or llama.cpp's
|
|
322
|
+
`meta.n_ctx`), else `context.window_tokens`.
|
|
323
|
+
|
|
324
|
+
## Llama Network Retry Behavior
|
|
325
|
+
|
|
326
|
+
Transient network failures are retried automatically with exponential backoff.
|
|
327
|
+
|
|
328
|
+
- Default retries: `5` (up to `6` total attempts including the first call).
|
|
329
|
+
- Default backoff: `0.5s`, `1s`, `2s`, `4s`, `8s`.
|
|
330
|
+
- Retry scope: transient network errors (timeouts, refused/reset connections, EOF/socket reachability failures),
|
|
331
|
+
HTTP 429 and HTTP 500/502/503/504/529. A `Retry-After` header replaces the backoff delay; one longer than
|
|
332
|
+
60s is not waited out and the error is reported instead.
|
|
333
|
+
- A stream that has already produced output is never retried (the retry would repeat it); it fails the turn.
|
|
334
|
+
- Cancellation (`Ctrl-C`) is never retried.
|
|
335
|
+
|
|
336
|
+
Configuration:
|
|
337
|
+
|
|
338
|
+
- `SAMAGOTCHI_RETRY_MAX` (default `5`): number of retries after the first failed attempt.
|
|
339
|
+
- `SAMAGOTCHI_RETRY_BASE_DELAY` (default `0.5`): backoff base delay in seconds.
|
|
340
|
+
- `SAMAGOTCHI_RETRY_MAX_DELAY` (default `8.0`): cap for backoff delay in seconds.
|
|
341
|
+
|
|
342
|
+
Assist-mode UX:
|
|
343
|
+
|
|
344
|
+
- While waiting, retry notices are rendered in the existing thinking spinner area as a red `network error: retrying ...` status.
|
|
345
|
+
- If retry attempts are exhausted, the submitted prompt is restored into the input editor so you can edit and resubmit.
|
|
346
|
+
|
|
347
|
+
## Server errors
|
|
348
|
+
|
|
349
|
+
An error status or a server's error event fails the turn with the server's
|
|
350
|
+
message (before, a failed llama.cpp `/completion` ended the turn as
|
|
351
|
+
`[No response]`). The error names its kind:
|
|
352
|
+
|
|
353
|
+
| Kind | When | Retried |
|
|
354
|
+
|---|---|---|
|
|
355
|
+
| connection | refused, reset, timed out, dropped mid-stream | yes (network retry), not mid-stream |
|
|
356
|
+
| rate limited | HTTP 429 | yes, honouring `Retry-After` |
|
|
357
|
+
| server | HTTP 5xx, llama.cpp's mid-stream `error:` event | 500/502/503/504/529 only |
|
|
358
|
+
| auth | HTTP 401/403 | no |
|
|
359
|
+
| bad request | other 4xx; a prompt larger than the context window, whatever the status | no |
|
|
360
|
+
| protocol | a body the API doesn't promise | no |
|
|
361
|
+
|
|
362
|
+
The turn's prompt and its completed tool calls stay in the session.
|
|
363
|
+
|
|
364
|
+
## Debug Log File
|
|
365
|
+
|
|
366
|
+
Every `chi` process (the REPL, the attached terminal, the background
|
|
367
|
+
workers, `chi web`) appends tagged records to one log file, so you can see
|
|
368
|
+
what happened in a session, what went over the wire and why something
|
|
369
|
+
failed, without `--verbose`.
|
|
370
|
+
|
|
371
|
+
Default path:
|
|
372
|
+
|
|
373
|
+
- `$XDG_STATE_HOME/samagotchi/samagotchi.log`, i.e.
|
|
374
|
+
`~/.local/state/samagotchi/samagotchi.log` when `XDG_STATE_HOME` is unset
|
|
375
|
+
(next to the sessions and prompt history, never inside the gem)
|
|
376
|
+
|
|
377
|
+
Configuration (CLI > env > config file, like every other entry):
|
|
378
|
+
|
|
379
|
+
- `log.file` / `SAMAGOTCHI_LOG_FILE` / `--log-file PATH`: another path. `~`
|
|
380
|
+
and relative paths are expanded against the directory `chi` runs in.
|
|
381
|
+
- `log.disable` / `SAMAGOTCHI_LOG_DISABLE=true` / `--log-disable`: no file logging.
|
|
382
|
+
- `log.level` / `SAMAGOTCHI_LOG_LEVEL` / `--log-level LEVEL`: `debug`, `info`
|
|
383
|
+
(default), `warn` or `error`.
|
|
384
|
+
|
|
385
|
+
A worker takes the log settings (file and level) of the `chi` or `chi web`
|
|
386
|
+
that started it.
|
|
387
|
+
|
|
388
|
+
### Format
|
|
389
|
+
|
|
390
|
+
One record is one line, plus indented payload lines at debug level:
|
|
391
|
+
|
|
392
|
+
```
|
|
393
|
+
2026-09-25T10:11:12.345Z INFO turn pid=4242 sid=6f1c2a9b tool_call_completed iteration=1 tool=read ms=12 output_chars=5120
|
|
394
|
+
2026-09-25T10:11:13.001Z WARN http pid=4242 sid=6f1c2a9b retry host=openrouter method=POST url=https://openrouter.ai/api/v1/chat/completions model=qwen/qwen3.6 purpose=chat attempt=1 max_retries=5 delay_s=2.0 status=429 error=Samagotchi::LLM::RateLimited msg="…"
|
|
395
|
+
2026-09-25T10:11:14.500Z DEBUG model pid=4242 sid=6f1c2a9b response model=qwen3.6 iteration=2
|
|
396
|
+
the model's answer, every line indented by four spaces
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
- time (UTC, milliseconds), level, tag, the process id, the session's first
|
|
400
|
+
8 characters (`sid=`, when the record is about one; `turn_started` has the
|
|
401
|
+
full id as `session=`), the event, then `key=value` fields. A value with a
|
|
402
|
+
space, quote or `=` is a JSON string, so a record never spans lines;
|
|
403
|
+
control characters (terminal colours in tool output) are escaped.
|
|
404
|
+
- Tags: `turn` (a session's event trail), `http` (model requests),
|
|
405
|
+
`worker`, `bridge`, `web`, `attached`, `repl`, `idle`, `recap`, `hooks`,
|
|
406
|
+
`plugins`, `guardrails`, `config`, `memory`, `model` (debug dumps).
|
|
407
|
+
- The format is parsed by `Samagotchi::LogLine` (`parse`, `each_record`);
|
|
408
|
+
keep tools that read it on that parser.
|
|
409
|
+
|
|
410
|
+
What each level adds:
|
|
411
|
+
|
|
412
|
+
- `error`: crashes (a worker, a bridge connection, the idle scheduler, a
|
|
413
|
+
recap) with the first 20 backtrace frames; failed model requests.
|
|
414
|
+
- `warn`: retries (429, 5xx, network), failed turns, hook and guardrail
|
|
415
|
+
problems, config warnings. Warnings `chi` prints on stderr are logged too,
|
|
416
|
+
with the same text as `msg=`; a worker's (its stderr goes nowhere) now
|
|
417
|
+
only reach the file.
|
|
418
|
+
- `info`: turns, generations and tool calls with sizes and times (never the
|
|
419
|
+
text of a prompt, answer or tool output), one line per model request
|
|
420
|
+
(status, time to first token, total), worker start/spawn/stop/idle exit,
|
|
421
|
+
`chi web` start.
|
|
422
|
+
- `debug`: payload dumps (each model answer with its thinking, tool calls
|
|
423
|
+
and results, context status), probes and model lists, every web API and
|
|
424
|
+
bridge request.
|
|
425
|
+
|
|
426
|
+
`-v`/`--verbose` (the plain REPL only) logs at `debug` and prints every
|
|
427
|
+
record to stderr as well.
|
|
428
|
+
|
|
429
|
+
### Rotation and secrets
|
|
430
|
+
|
|
431
|
+
At 5 MB the file moves to `samagotchi.log.1` (one kept) and a new one
|
|
432
|
+
starts; every process follows. Request headers and bodies are never
|
|
433
|
+
logged; fields named like a credential (`api_key`, `token`, `secret`,
|
|
434
|
+
`authorization`, `password`) show `[redacted]`, and URLs lose their user
|
|
435
|
+
info and query. Debug dumps can still hold secrets a tool read or was
|
|
436
|
+
given (a file's contents, a command line): treat a debug log as sensitive.
|
|
437
|
+
`tools/web_fetch` requests are not logged (their own HTTP client).
|
|
438
|
+
|
|
439
|
+
### Recipes
|
|
440
|
+
|
|
441
|
+
```sh
|
|
442
|
+
tail -f ~/.local/state/samagotchi/samagotchi.log
|
|
443
|
+
# one session
|
|
444
|
+
grep 'sid=6f1c2a9b' ~/.local/state/samagotchi/samagotchi.log
|
|
445
|
+
# model requests only, or warnings and errors
|
|
446
|
+
awk '$3 == "http"' ~/.local/state/samagotchi/samagotchi.log
|
|
447
|
+
awk '$2 == "WARN" || $2 == "ERROR"' ~/.local/state/samagotchi/samagotchi.log
|
|
448
|
+
# how long each tool call took
|
|
449
|
+
grep ' tool_call_completed ' ~/.local/state/samagotchi/samagotchi.log | grep -o 'tool=[^ ]* ms=[0-9]*'
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
## Images
|
|
453
|
+
|
|
454
|
+
Images a model gets (see [CLI: Images](cli.md#images)) are converted and
|
|
455
|
+
downscaled first; three settings bound them (env `SAMAGOTCHI_IMAGE_*` or
|
|
456
|
+
`config.yml`):
|
|
457
|
+
|
|
458
|
+
```yaml
|
|
459
|
+
image:
|
|
460
|
+
max_side: 1568 # long side in px (Claude's standard; ~1.3k tokens for 1280×800)
|
|
461
|
+
max_bytes: 3750000 # larger after downscaling → re-encoded as JPEG
|
|
462
|
+
max_per_request: 20 # older images in the conversation become placeholder lines
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
Whether a model can see images is found out before a turn with images is sent:
|
|
466
|
+
|
|
467
|
+
- a native llama.cpp host: `/props` must report `modalities.vision` (the server
|
|
468
|
+
runs with `--mmproj`) and a media marker, and the prompt profile must know the
|
|
469
|
+
chat template's image wrapping (qwen36 does; gemma4 not yet);
|
|
470
|
+
- mlx and oMLX hosts: no;
|
|
471
|
+
- an OpenAI-API host: a local llama.cpp's `/props`, else the host's model list
|
|
472
|
+
(OpenRouter's `architecture.input_modalities`); when it doesn't say, the image
|
|
473
|
+
is sent and a refusal is reported.
|
|
474
|
+
|
|
475
|
+
`vision: true|false` overrides that per model or per host:
|
|
476
|
+
|
|
477
|
+
```yaml
|
|
478
|
+
models:
|
|
479
|
+
ornith: { vision: true }
|
|
480
|
+
hosts:
|
|
481
|
+
gateway: { url: https://…, api: openai, vision: false }
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
`models:` wins over `hosts:`. On a native host, `vision: true` skips only the
|
|
485
|
+
modalities check: without a media marker the prompt can't carry an image.
|
|
486
|
+
|
|
487
|
+
## Project specific description
|
|
488
|
+
|
|
489
|
+
If an AGENT.md file is present in the project root, samagotchi injects its
|
|
490
|
+
contents into the system prompt under a "Project specific description:" section.
|
|
491
|
+
|
|
492
|
+
To skip loading AGENT.md, set:
|
|
493
|
+
|
|
494
|
+
`SAMAGOTCHI_SKIP_AGENT_MD=true`
|
data/docs/desktop.md
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Desktop helper (macOS)
|
|
2
|
+
|
|
3
|
+
`chi desktop` installs **Chi Helper**, a small native app. It sends text you selected in any app to a chi session,
|
|
4
|
+
either with a question as [your message](sessions.md#sending-a-message) (a turn runs, and the answer shows in the
|
|
5
|
+
attached terminal or web page), or as a [context note](sessions.md#context-notes) (the model sees it on its next
|
|
6
|
+
turn, and no turn starts).
|
|
7
|
+
|
|
8
|
+
- **Services menu:** select text → right-click → Services → **Send to chi**.
|
|
9
|
+
- **Hotkey ⌃⌥⌘N:** opens the panel with the **clipboard** (not the selection), for apps whose Services menu lacks
|
|
10
|
+
the item.
|
|
11
|
+
- **A shortcut for the selection:** give "Send to chi" its own shortcut in System Settings → Keyboard → Keyboard
|
|
12
|
+
Shortcuts… → Services → Text (pick one other than ⌃⌥⌘N). It goes through the Services menu route, so the panel
|
|
13
|
+
opens with the selected text. If it doesn't fire at once, see Troubleshooting below.
|
|
14
|
+
|
|
15
|
+
The panel has a one-line message field on top ("Ask chi…", focused), the text below it (editable, shown as the
|
|
16
|
+
quote it becomes), where it came from (the app's name, editable; notes only), the size against the 16 KB cap, and the
|
|
17
|
+
sessions. Keys:
|
|
18
|
+
|
|
19
|
+
- **⏎** sends a message: `chi send -m <the line>` with the text as quoted context above it. With the line empty,
|
|
20
|
+
the text is the message.
|
|
21
|
+
- **⌘⏎** sends a note: `chi note`, the line (if any) then the text.
|
|
22
|
+
- **⇧⏎** is a newline, in either field. Esc closes.
|
|
23
|
+
|
|
24
|
+
The list shows the sessions a worker runs now, then, under a "recent" divider, up to 3 stopped ones (dimmed, with
|
|
25
|
+
their age: "2h ago", "yesterday"). Click a session or press ⌘1…⌘9 to tick it. The last choice is preselected while
|
|
26
|
+
it's still live; if there's only one live session, that one is (a recent one never is). A message to a recent
|
|
27
|
+
session starts its worker; a note to one waits for its next start, and the panel shows chi's line saying so. After a
|
|
28
|
+
send the panel shows chi's line and closes. On an error it stays open and shows the error.
|
|
29
|
+
|
|
30
|
+
A session open in a `chi --no-shared` REPL isn't listed: it takes no notes or messages.
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
chi desktop install # build it into ~/Applications and start it
|
|
36
|
+
chi desktop install --login # … and start it at login (macOS shows a "Login Item Added" notice)
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
It needs the Command Line Tools (`xcode-select --install`): the app is compiled on your Mac with `swiftc` and
|
|
40
|
+
signed ad hoc, with no notarization and no Xcode project. A build takes a few seconds warm, but can take a few
|
|
41
|
+
minutes cold. The app has no Dock icon and keeps running once started. Without `--login` it runs until you log out,
|
|
42
|
+
and opening it from `~/Applications` starts it again.
|
|
43
|
+
|
|
44
|
+
Install from the checkout or gem you keep. From a gem install the helper runs the gem's `chi` wrapper (the one on
|
|
45
|
+
your PATH, e.g. `$GEM_HOME/bin/chi`), which picks the newest installed version, so gem upgrades and `gem cleanup`
|
|
46
|
+
don't break it. From a checkout it runs **that** checkout's `bin/chi`; installing from a linked git worktree prints a
|
|
47
|
+
warning, because the helper stops working once that worktree is removed. After switching between a checkout and a
|
|
48
|
+
gem install, run `chi desktop upgrade` from the one you now use.
|
|
49
|
+
|
|
50
|
+
## Commands
|
|
51
|
+
|
|
52
|
+
| Command | Does |
|
|
53
|
+
|---|---|
|
|
54
|
+
| `chi desktop install [--force] [--login]` | builds, installs and starts it; `--force` replaces an existing copy |
|
|
55
|
+
| `chi desktop upgrade` | rebuilds it for this chi and restarts it, keeping the login setting |
|
|
56
|
+
| `chi desktop uninstall` | quits it and removes the app, its login item, launch file and settings |
|
|
57
|
+
| `chi desktop status` | version against chi's, how it runs chi, state dirs, Service, hotkey, process, login item |
|
|
58
|
+
|
|
59
|
+
`chi self` has a `desktop` line: `0.1.x (matches)`, `0.1.w (chi is 0.1.x: chi desktop upgrade)` or `not installed`.
|
|
60
|
+
|
|
61
|
+
## How it runs chi
|
|
62
|
+
|
|
63
|
+
Apps started by macOS get a bare environment: no shell rc files, so no rbenv/chruby/mise/asdf and none of your
|
|
64
|
+
exports. So `install` writes `~/Library/Application Support/Chi Helper/launch.json` with:
|
|
65
|
+
|
|
66
|
+
- the absolute path of the Ruby running chi and of chi itself (the gem's wrapper, or a checkout's `bin/chi`);
|
|
67
|
+
- `LANG=en_US.UTF-8`;
|
|
68
|
+
- only these variables, and only when they are set: `XDG_CONFIG_HOME`, `XDG_STATE_HOME`, `GEM_HOME`, `GEM_PATH`,
|
|
69
|
+
`RUBYLIB`. No tokens and no `SAMAGOTCHI_*` settings go in.
|
|
70
|
+
|
|
71
|
+
The file freezes the install shell's values. If you install with a temporary `XDG_STATE_HOME`, the helper keeps
|
|
72
|
+
using it. `status` prints the baked dirs.
|
|
73
|
+
|
|
74
|
+
### The contract with chi
|
|
75
|
+
|
|
76
|
+
The helper uses only these commands, so it could ship on its own later:
|
|
77
|
+
|
|
78
|
+
- `chi sessions list --live --scope=all --format json` and `chi sessions list --limit 20 --scope=all --format json` →
|
|
79
|
+
`[{id, short_id, desc, cwd, project, updated_at, live, busy, owner, recap}]` (it uses `id`, `desc`, `cwd`, `busy`, `updated_at`;
|
|
80
|
+
"recent" = rows of the second call that aren't live and have `owner: null`; only UUID-shaped ids go on to chi).
|
|
81
|
+
- `chi send [-m LINE] ID...` with the text on stdin (or none) and `chi note --source NAME ID...` with the text on
|
|
82
|
+
stdin → one line per session on stdout; exit 0 means all sent or queued, 1 means some were refused or failed.
|
|
83
|
+
|
|
84
|
+
Each call is stopped after 10 s. A stopped `chi note` says the note may be partly delivered.
|
|
85
|
+
|
|
86
|
+
## Troubleshooting
|
|
87
|
+
|
|
88
|
+
- **"chi not found at …, run `chi desktop upgrade`"**: the Ruby or checkout in `launch.json` moved (a Ruby upgrade,
|
|
89
|
+
a removed worktree). Run `chi desktop upgrade` from the chi you use now.
|
|
90
|
+
- **No "Send to chi" in the Services menu:** check `chi desktop status` (service). Try
|
|
91
|
+
`/System/Library/CoreServices/pbs -update`, start the app again, or log out and back in. It must be ticked in
|
|
92
|
+
System Settings → Keyboard → Keyboard Shortcuts… → Services → Text.
|
|
93
|
+
- **A keyboard shortcut for the Service** (in the same settings pane) may keep firing the old one until the app
|
|
94
|
+
restarts or you log out: macOS caches Services.
|
|
95
|
+
- **⌃⌥⌘N does nothing:** `status` says whether another app holds it. macOS doesn't report clashes with its own
|
|
96
|
+
shortcuts.
|
|
97
|
+
- **"No live sessions":** start one with `chi` in a terminal; `chi sessions list --live --scope=all` shows the same list.
|