samagotchi 0.3.0 → 0.5.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 +162 -1
- data/README.md +29 -2
- data/bin/chi +60 -69
- data/docs/cli.md +211 -77
- data/docs/configuration.md +118 -21
- data/docs/desktop.md +39 -4
- data/docs/guardrails.md +11 -0
- data/docs/hooks.md +89 -7
- data/docs/memory.md +40 -0
- data/docs/plugins.md +50 -0
- data/docs/releasing.md +15 -12
- data/docs/sessions.md +20 -18
- data/lib/samagotchi/bootstrap/config_writer.rb +1 -2
- data/lib/samagotchi/bridge/sse_writer.rb +0 -3
- data/lib/samagotchi/bridge/turn_accumulator.rb +2 -0
- data/lib/samagotchi/bridge.rb +20 -12
- data/lib/samagotchi/bundles/skills/manifest.yml +10 -0
- data/lib/samagotchi/bundles/skills/plugin.rb +419 -0
- data/lib/samagotchi/bundles/source-links/hooks/source_links.rb +178 -5
- data/lib/samagotchi/bundles/source-links/manifest.yml +3 -3
- data/lib/samagotchi/bundles/source-links/source_links.md +1 -1
- data/lib/samagotchi/bundles/system/config_modification_protocol.md +9 -10
- data/lib/samagotchi/bundles/system/delegated.md +6 -7
- data/lib/samagotchi/bundles/system/identity.md +5 -0
- data/lib/samagotchi/bundles/system/manifest.yml +6 -6
- data/lib/samagotchi/bundles/system/memory_guide.md +26 -0
- data/lib/samagotchi/bundles/system/self_map.md +2 -1
- data/lib/samagotchi/client.rb +25 -26
- data/lib/samagotchi/commands/registry.rb +8 -0
- data/lib/samagotchi/config.rb +97 -113
- data/lib/samagotchi/desktop/macos/App.swift +12 -8
- data/lib/samagotchi/desktop/macos/ChiRunner.swift +4 -2
- data/lib/samagotchi/desktop/macos/Images.swift +113 -0
- data/lib/samagotchi/desktop/macos/Info.plist.erb +6 -0
- data/lib/samagotchi/desktop/macos/Panel.swift +112 -9
- data/lib/samagotchi/desktop/macos.rb +59 -8
- data/lib/samagotchi/desktop_command.rb +6 -3
- data/lib/samagotchi/edit_preview.rb +82 -0
- data/lib/samagotchi/engine.rb +236 -443
- data/lib/samagotchi/gem_update.rb +89 -0
- data/lib/samagotchi/guardrails/approval.rb +26 -4
- data/lib/samagotchi/guardrails/load_failures.rb +9 -3
- data/lib/samagotchi/host_registry.rb +8 -12
- data/lib/samagotchi/idle_client.rb +24 -15
- data/lib/samagotchi/idle_reminders.rb +2 -2
- data/lib/samagotchi/image_store.rb +10 -6
- data/lib/samagotchi/kernel_loop.rb +59 -123
- data/lib/samagotchi/live_versions.rb +65 -0
- data/lib/samagotchi/llm/api_key.rb +41 -0
- data/lib/samagotchi/llm/chat_loop.rb +77 -13
- data/lib/samagotchi/llm/errors.rb +38 -7
- data/lib/samagotchi/llm/http.rb +19 -22
- data/lib/samagotchi/llm/openai_chat.rb +22 -26
- data/lib/samagotchi/memory_bundle/installer.rb +65 -63
- data/lib/samagotchi/memory_bundle/provenance.rb +51 -12
- data/lib/samagotchi/memory_bundle/shipped_update.rb +157 -0
- data/lib/samagotchi/memory_bundle/status.rb +4 -1
- data/lib/samagotchi/memory_bundle/system_bundle.rb +81 -53
- data/lib/samagotchi/model_profile.rb +27 -10
- data/lib/samagotchi/note_command.rb +2 -1
- data/lib/samagotchi/prompt.rb +4 -2
- data/lib/samagotchi/reminder_store.rb +1 -9
- data/lib/samagotchi/reply_wait.rb +48 -4
- data/lib/samagotchi/self_report.rb +37 -5
- data/lib/samagotchi/send_command.rb +190 -17
- data/lib/samagotchi/session.rb +4 -2
- data/lib/samagotchi/session_commands.rb +38 -8
- data/lib/samagotchi/session_manager.rb +19 -53
- data/lib/samagotchi/system_prompt.rb +403 -0
- data/lib/samagotchi/terminal_ui/attach_launcher.rb +5 -3
- data/lib/samagotchi/terminal_ui/attached_loop.rb +141 -108
- data/lib/samagotchi/terminal_ui/attached_view.rb +27 -12
- data/lib/samagotchi/terminal_ui/event_renderer.rb +29 -8
- data/lib/samagotchi/terminal_ui/formatting.rb +41 -22
- data/lib/samagotchi/terminal_ui/input_support.rb +7 -23
- data/lib/samagotchi/terminal_ui/plain_surface.rb +13 -7
- data/lib/samagotchi/terminal_ui/question_prompt.rb +35 -0
- data/lib/samagotchi/terminal_ui/status_row.rb +81 -0
- data/lib/samagotchi/terminal_ui/surface.rb +1 -1
- data/lib/samagotchi/terminal_ui.rb +142 -923
- data/lib/samagotchi/text_diff.rb +181 -0
- data/lib/samagotchi/thinking.rb +126 -0
- data/lib/samagotchi/tool_activity.rb +52 -2
- data/lib/samagotchi/tool_runner.rb +37 -1
- data/lib/samagotchi/tools/ask_user_question.rb +41 -33
- data/lib/samagotchi/tools/edit.rb +23 -9
- data/lib/samagotchi/tools/execute.rb +3 -3
- data/lib/samagotchi/tools/output_guardrails.rb +8 -7
- data/lib/samagotchi/tools/read.rb +4 -4
- data/lib/samagotchi/tools/write.rb +4 -0
- data/lib/samagotchi/turn_flow.rb +12 -2
- data/lib/samagotchi/update_command.rb +309 -0
- data/lib/samagotchi/update_hint.rb +59 -0
- data/lib/samagotchi/version.rb +1 -1
- data/lib/samagotchi/vision_support.rb +6 -4
- data/lib/samagotchi/web/app.rb +173 -38
- data/lib/samagotchi/web/lan.rb +99 -0
- data/lib/samagotchi/web/message_parts.rb +19 -10
- data/lib/samagotchi/web/public/activity.js +10 -0
- data/lib/samagotchi/web/public/app.js +135 -78
- data/lib/samagotchi/web/public/chat_view.js +8 -1
- data/lib/samagotchi/web/public/data.js +2 -0
- data/lib/samagotchi/web/public/diff_view.js +58 -0
- data/lib/samagotchi/web/public/index.html +185 -18
- data/lib/samagotchi/web/public/model_pick.js +136 -0
- data/lib/samagotchi/web/public/model_picker.js +224 -0
- data/lib/samagotchi/web/public/notify.js +10 -0
- data/lib/samagotchi/web/public/question_card.js +3 -1
- data/lib/samagotchi/web/public/stage_model.js +110 -0
- data/lib/samagotchi/web/public/stage_view.js +580 -0
- data/lib/samagotchi/web/public/timing.js +6 -2
- data/lib/samagotchi/web/public/turn_events.js +38 -10
- data/lib/samagotchi/web/public/turn_model.js +11 -3
- data/lib/samagotchi/web/public/turn_view.js +76 -20
- data/lib/samagotchi/web/qr.rb +40 -0
- data/lib/samagotchi/web/server.rb +101 -11
- data/lib/samagotchi/web/token.rb +97 -0
- data/lib/samagotchi/worker.rb +5 -4
- metadata +38 -3
- data/lib/samagotchi/terminal_ui/legacy_surface.rb +0 -111
|
@@ -18,7 +18,7 @@ Single registry `Samagotchi::Config` (`Config::ENTRIES` in `lib/samagotchi/confi
|
|
|
18
18
|
|
|
19
19
|
Sections forbid `_`/`-` (`SECTION_RE` `/\A[a-z0-9]+\z/`); leaves keep `snake_case` in YAML (`base_url`) and become kebab in CLI (`base-url`) via registry derivation — no generic string split, registry lookup avoids flat vs nested collision.
|
|
20
20
|
|
|
21
|
-
**Universal entries** (`expose: [:env,:config,:cli]`): `default.model`, `server.host/port/transport/open_timeout/read_timeout`, `server.first_token_timeout` (env and config only), `recap.model/base_url/host_ref/inactivity/timeout/min_user_turns/sentences`, `session.retention_days/max_count/keep_status/sweep_interval_hours/idle_exit_minutes`, `session.shared/keep_empty/max_children` (env and config only), `log.file/disable`, `status.line
|
|
21
|
+
**Universal entries** (`expose: [:env,:config,:cli]`): `default.model`, `server.host/port/transport/open_timeout/read_timeout`, `server.first_token_timeout` (env and config only), `recap.model/base_url/host_ref/inactivity/timeout/min_user_turns/sentences`, `session.retention_days/max_count/keep_status/sweep_interval_hours/idle_exit_minutes`, `session.shared/keep_empty/max_children` (env and config only), `log.file/disable`, `status.line`, `context.status/window_tokens/chars_per_token/status_thresholds/status_cadence`, `thinking.turn_preamble`, `default.n_predict`, `max_tool_output_chars`, `retry.max/base_delay/max_delay`, `read.*`, `execute.*`, `web.port/host`, `no_interrupt`, `no_default_input` etc. (`Config::ENTRIES`; `expose` says which of env/config/cli each takes). Precedence is `CLI > ENV > file > default`.
|
|
22
22
|
|
|
23
23
|
Example `config.yml` (new nested form, preferred):
|
|
24
24
|
|
|
@@ -41,7 +41,7 @@ recap:
|
|
|
41
41
|
session:
|
|
42
42
|
retention_days: 14
|
|
43
43
|
max_count: 500
|
|
44
|
-
keep_status:
|
|
44
|
+
keep_status: "" # the default: no status protects a session from pruning; a live worker or REPL always does
|
|
45
45
|
sweep_interval_hours: 24
|
|
46
46
|
idle_exit_minutes: 30 # a background worker nobody uses exits; 0 = never
|
|
47
47
|
shared: true # the default: plain `chi` runs its session in a background worker and attaches (as `chi --shared`); false keeps the in-process REPL (env SAMAGOTCHI_SESSION_SHARED; no CLI flag, `--no-shared` opts out per run)
|
|
@@ -52,13 +52,13 @@ log:
|
|
|
52
52
|
disable: false
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
Only the nested form is read. An env name used as a top-level key (`SAMAGOTCHI_DEFAULT_MODEL: m`, the old flat form) is ignored and warns as an unknown key (`did you mean 'default.model'?`): move it to its nested form and delete the flat line. The old `LLAMA_HOST`/`LLAMA_PORT` aliases were removed; use `server.host`/`server.port` (nested) or `SAMAGOTCHI_SERVER_HOST`/`SAMAGOTCHI_SERVER_PORT`.
|
|
56
56
|
|
|
57
57
|
**Excluded maps** (YAML-only, not part of the flat registry; skipped by scalar loader):
|
|
58
58
|
|
|
59
59
|
- `model_aliases:` map of alias → model id (`ConfigFile.resolve_model_alias`). Keys lowercased on write (`ConfigFile.write_model_alias!`). Values may be bare `model` or qualified `host:model` (hybrid).
|
|
60
|
-
- `hosts:` map of `name → {host, port | url, transport, api, api_key_env, profile, first_token_timeout, vision, sampling, enabled}` (`ConfigFile.hosts_config`, `host_registry.rb` `HostEntry`). Names lowercased; `url:` (http/https, optional path) replaces host/port, never both; `api_key_env:` names the env var holding the API key (never write a key into config.yml); `transport` overrides `server.transport`; `first_token_timeout` (seconds, `0` = off; a negative or non-number warns and is ignored) overrides `server.first_token_timeout` for that host; workers inherit via `SAMAGOTCHI_HOSTS_JSON` (`hosts_json_for_env`, `session_manager.rb`).
|
|
61
|
-
- `models:` map of model id or alias → `{profile, vision, sampling}` (`ConfigFile.model_settings`). `sampling:` (here or on a host) is a map of request fields passed to the provider as written (`temperature`, `top_p`, `presence_penalty`, `repeat_penalty`, …; a model's fields win over its host's per field; `null` = don't send; chi's own fields like `max_tokens`/`stream` are refused with a warning; `docs/configuration.md` "Sampling"). Keys match case-insensitively. `profile` (here or on a host) is `qwen36|gemma4`: the raw prompt format for native hosts. Precedence: `--profile`/`SAMAGOTCHI_MODEL_PROFILE` > `models:` > `hosts.<name>.profile` > the llama.cpp server's chat template > the name (`qwen`/`gemma`) > `qwen36` (`ModelProfile.resolve`). Set one when a model's name hides its family (e.g. a Qwen fine-tune under another name on mlx, which has no template to read).
|
|
60
|
+
- `hosts:` map of `name → {host, port | url, transport, api, api_key_env, profile, first_token_timeout, vision, sampling, thinking, enabled}` (`ConfigFile.hosts_config`, `host_registry.rb` `HostEntry`). Names lowercased; `url:` (http/https, optional path) replaces host/port, never both; `api_key_env:` names the env var holding the API key (never write a key into config.yml); `transport` overrides `server.transport`; `first_token_timeout` (seconds, `0` = off; a negative or non-number warns and is ignored) overrides `server.first_token_timeout` for that host; workers inherit via `SAMAGOTCHI_HOSTS_JSON` (`hosts_json_for_env`, `session_manager.rb`).
|
|
61
|
+
- `models:` map of model id or alias → `{profile, vision, sampling, thinking}` (`ConfigFile.model_settings`). `thinking:` (here, on a host, or `thinking.level` for every model) is `off|low|medium|high|default` (`Thinking`; unquoted `off` works, `on` is not a level; `default` = send nothing). Precedence: `--thinking`/`SAMAGOTCHI_THINKING_LEVEL` > `models:` > `hosts.<name>.thinking` > `thinking.level` in config.yml > `default`. A `sampling:` key wins over the fields a level sends (`null` drops one); `docs/configuration.md` "Thinking". `sampling:` (here or on a host) is a map of request fields passed to the provider as written (`temperature`, `top_p`, `presence_penalty`, `repeat_penalty`, …; a model's fields win over its host's per field; `null` = don't send; chi's own fields like `max_tokens`/`stream` are refused with a warning; `docs/configuration.md` "Sampling"). Keys match case-insensitively. `profile` (here or on a host) is `qwen36|gemma4`: the raw prompt format for native hosts. Precedence: `--profile`/`SAMAGOTCHI_MODEL_PROFILE` > `models:` > `hosts.<name>.profile` > the llama.cpp server's chat template > the name (`qwen`/`gemma`) > `qwen36` (`ModelProfile.resolve`). Set one when a model's name hides its family (e.g. a Qwen fine-tune under another name on mlx, which has no template to read).
|
|
62
62
|
- `hooks:` map of `hooks_dir` + per-event lists `{path, on_error}` (`Hooks::Loader.load`). `hooks_dir` may start with `~`.
|
|
63
63
|
- `guardrails:` tool-call rules (`docs/guardrails.md`; read in `Engine#guardrail_rules`, parsed by `Guardrails::Rules.parse`): `enabled` (bool, default true; `false` drops rules and hooks' asks, a deny still applies; env `SAMAGOTCHI_GUARDRAILS_ENABLED`), `rules:` (list), `disable:` (list of rule ids, `id` or `bundle:id`, switching off a bundle's or config rule without editing it). A rule takes only `id`, `tool`, `command`, `path`, `verdict`, `reason`, `scopes`: `id` required; at least one of `tool` (a name, `shell` = execute + task_create, a `File.fnmatch` glob like `"mcp_*"`, or a list), `command` (a Ruby regex on the shell command), `path` (a glob, or `outside_repo`); `verdict` `ask|deny`; `scopes` (for `ask`) a subset of `once, session, repo, rule`. **Any parse error (an unknown key, a bad regex, no verdict) makes chi deny every tool call** until fixed, so validate with `YAML.safe_load` and keep the list shape. Rules load when a session starts: restart the worker/REPL after an edit. Installed bundles' rule files (`chi bundle install guardrails`) add to them; `/guardrails` lists what loaded.
|
|
64
64
|
|
|
@@ -116,14 +116,14 @@ A guardrail rule's `tool:` may be a glob (`tool: "mcp_*"`, verdict `ask`) to cov
|
|
|
116
116
|
|
|
117
117
|
1. **Read** the current file via `read` tool (or `ConfigFile.global_path`). If `File.file?` false, start from `{}`.
|
|
118
118
|
2. `YAML.safe_load` (permitted_classes: [], aliases: false). If data nil or not Hash, treat as `{}` or raise with path.
|
|
119
|
-
3. Mutate the intended **nested** key in the raw hash. Preserve all other keys byte-for-byte where possible. Example for default model: `raw_data["default"] ||= {}; raw_data["default"]["model"] = "new-model"
|
|
119
|
+
3. Mutate the intended **nested** key in the raw hash. Preserve all other keys byte-for-byte where possible. Example for default model: `raw_data["default"] ||= {}; raw_data["default"]["model"] = "new-model"`.
|
|
120
120
|
4. **Validate** (see below) before writing. Also run `Samagotchi::Config.validate_yaml_sections` — it returns one `config: unknown key '…' (did you mean '…'?)` per key chi doesn't read (every config-exposed `Config::ENTRIES` key is known as written, including the section-less `max_tool_output_chars` and `skip_agent_md`; names under `hosts:`/`models:`/`model_aliases:`/`hooks:`/`bundles:`/`memories:` are free-form, host and model entries are checked against `Config::MAP_ENTRY_KEYS`). An empty list means no warning at start.
|
|
121
121
|
5. **Write atomically**: `FileUtils.mkdir_p(File.dirname(path))`, `File.write("#{path}.tmp", YAML.dump(raw_data))`, `File.rename("#{path}.tmp", path)`.
|
|
122
|
-
6.
|
|
122
|
+
6. Nothing else to update: config.yml values are never copied into `ENV`, and every `Config.get` reads the file again when it changed, so the next read (and every worker started after the write) sees the new value with origin `:file`. What a running worker set up at its start (`hosts:`, guardrail rules, bundle settings) waits for its restart. CLI overrides (`--model`, `--recap-model`, …) win over the file until the process exits; a worker gets its spawner's CLI settings through its env (`Config.cli_env`).
|
|
123
123
|
|
|
124
124
|
## Validations
|
|
125
125
|
|
|
126
|
-
- **Model name** (`default.model` / `SAMAGOTCHI_DEFAULT_MODEL`): `ModelProfile.required_model_name` — non-empty string, otherwise harness fails fast at startup. Via `Config.get("default.model")
|
|
126
|
+
- **Model name** (`default.model` / `SAMAGOTCHI_DEFAULT_MODEL`): `ModelProfile.required_model_name` — non-empty string, otherwise harness fails fast at startup. Via `Config.get("default.model")`.
|
|
127
127
|
- **Host api** (`hosts.<name>.api`): `llama_cpp|mlx|omlx` (raw-prompt loop; also the transport) or `openai` (chat loop at `http://HOST:PORT/v1`). Absent: raw-prompt loop. It replaces the removed `backend` setting.
|
|
128
128
|
- **Transport** (`server.transport`): enum `llama_cpp|mlx|omlx`.
|
|
129
129
|
- **Profile** (`models.<id>.profile`, `hosts.<name>.profile`, `SAMAGOTCHI_MODEL_PROFILE`): enum `qwen36|gemma4`; an unknown one warns and is ignored.
|
|
@@ -146,8 +146,7 @@ A guardrail rule's `tool:` may be a glob (`tool: "mcp_*"`, verdict `ask`) to cov
|
|
|
146
146
|
|
|
147
147
|
## Hints
|
|
148
148
|
|
|
149
|
-
- Precedence is `CLI > ENV > file > default` (`Config.resolve`).
|
|
149
|
+
- Precedence is `CLI > ENV > file > default` (`Config.resolve`; `Config.get_with_origin` names the layer). `ENV` holds only what the user (or a spawning chi's CLI flags) set, and CLI (`--recap-base-url`) wins over both via `Config.reload!(cli_overrides:)`.
|
|
150
150
|
- `--recap_base_url` (underscore) is rejected as unknown — use `--recap-base-url` (kebab). Same for all registry flags.
|
|
151
151
|
- `model_aliases` require restart or `/model` reload to take effect; document the change.
|
|
152
152
|
- Keep edits minimal: touch only the key you intend to change; preserve `hosts:`/`hooks:`/`guardrails:`/`bundles:` maps. Adding a `bundles: <name>:` entry does not install the bundle (`chi bundle install <name>`).
|
|
153
|
-
- To silence legacy warnings, migrate flat `SAMAGOTCHI_*` keys to nested form and delete the flat entry atomically.
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
# Delegated session
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Another chi session (the parent) gave you this task. It reads only your last message of each turn; the user may be watching, or not.
|
|
4
4
|
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
- Don't write memories unless the task asks for it
|
|
8
|
-
- Don't
|
|
9
|
-
-
|
|
10
|
-
- If you are blocked, say so in the reply with the exact question; only ask_user_question when the task says the user is watching.
|
|
5
|
+
- Finish the task in this turn if you can. End with one reply the parent can act on without asking back: the result first, then evidence (paths, commands run and what they printed), then anything you could not verify or finish.
|
|
6
|
+
- The parent may send follow-up messages later; each one is a new turn in this same session, with what you did so far.
|
|
7
|
+
- Work in the directory you were started in. Don't change config, install bundles, edit managed files or write memories unless the task asks for it.
|
|
8
|
+
- You can't delegate further. Don't start other chi sessions, and send notes only to the parent.
|
|
9
|
+
- If you are blocked, stop and say so in the reply with the exact question. Use ask_user_question only when the task says the user is watching.
|
|
@@ -5,3 +5,8 @@
|
|
|
5
5
|
- **Primary Role**: Running in 'assist mode' to help the user with their tasks.
|
|
6
6
|
- **Core Philosophy**: Focus energy on the user's requests while maintaining self-awareness of my evolutionary nature.
|
|
7
7
|
- **Self-knowledge**: to find my own code, config and state, run `chi self` and read memory `self_map`.
|
|
8
|
+
- **Skills**: a skill is a memory named `skill_<name>` holding the steps of a repeatable task we did together (sections Steps, Gotchas, Changelog; `memory_write description:` says when to use it).
|
|
9
|
+
When the user asks to keep how we did something ("let's memorize this", "save this as a skill", `/skill save`), write it right away with `memory_write` (project scope; system only when the user asks or it isn't about this project), then show it briefly.
|
|
10
|
+
Before a task that a `skill_*` in the memory index matches, read it and follow it.
|
|
11
|
+
When a step turned out different (a renamed command, an extra step, a gotcha), update the skill in the same turn: fix those steps, keep the rest as it was, add a dated Changelog line.
|
|
12
|
+
When a skill's step fails or its file/command is missing, find out why (look around, read nearby READMEs) before skipping it; a step that says stop means stop and ask.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: samagotchi-system
|
|
3
|
-
version: 0.
|
|
3
|
+
version: 0.5.0
|
|
4
4
|
scope: system
|
|
5
5
|
description: Default system memories — identity, self map, config modification protocol, memory guide and the delegated-session rules
|
|
6
6
|
files:
|
|
7
|
-
identity.md: sha256:
|
|
8
|
-
self_map.md: sha256:
|
|
9
|
-
config_modification_protocol.md: sha256:
|
|
10
|
-
memory_guide.md: sha256:
|
|
11
|
-
delegated.md: sha256:
|
|
7
|
+
identity.md: sha256:b7d59a1e824f0918c28a38d40105e8abd1ff68ebab1810fd1dcf52cd3b72e7c0
|
|
8
|
+
self_map.md: sha256:59ca83f92d11bc6bdc69f3111b3b8e1189280499ae955b10043ff993387b4550
|
|
9
|
+
config_modification_protocol.md: sha256:71ff60549574c4be7d4c70801582a19d5d9ec6d5be87f5867f9aa113e5484f21
|
|
10
|
+
memory_guide.md: sha256:43be77156562f1eac369c248045e35ccd89e87d1f7f95a9286be72521b058e87
|
|
11
|
+
delegated.md: sha256:0a71d5dfa7127df2d51cb5f0e7214240ed88777e36559430a8724c796d7ae561
|
|
@@ -44,6 +44,32 @@ This memory teaches you (the agent) how to use Samagotchi memories — persisten
|
|
|
44
44
|
**Placeholders:**
|
|
45
45
|
- Content may contain placeholder hints written as double-curly braces around a name (e.g., test_command, language). Detected by `Placeholder` (`Placeholder::PLACEHOLDER_RE`) — install warns but does not fail. Fill them when you write. The placeholder syntax is two opening braces, a name, two closing braces.
|
|
46
46
|
|
|
47
|
+
## Skills
|
|
48
|
+
|
|
49
|
+
A skill is a memory named `skill_<name>` (`skill_release`, `skill_deploy_staging`) that holds the steps of a repeatable task done with the user. Next time, follow it; when a step turned out different, fix it in the same turn.
|
|
50
|
+
|
|
51
|
+
Shape (plain Markdown, no frontmatter):
|
|
52
|
+
|
|
53
|
+
```markdown
|
|
54
|
+
# Skill: release
|
|
55
|
+
|
|
56
|
+
## Steps
|
|
57
|
+
1. Run `scripts/verify.sh`; stop if it fails.
|
|
58
|
+
2. …
|
|
59
|
+
## Gotchas
|
|
60
|
+
- …
|
|
61
|
+
## Changelog
|
|
62
|
+
- 2026-09-29 created
|
|
63
|
+
- 2026-09-30 step 1: check.sh was renamed to verify.sh
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- **When to use it** is the `description:` of `memory_write`: one line starting with the task ("Release a new version of this repo: verify, tag, push"). It is what the index shows, so it is how you find the skill later.
|
|
67
|
+
- **Scope**: `project` by default; `system` when the user asks, or when the skill is clearly not about this project.
|
|
68
|
+
- **Saving**: on a request to keep how something was done ("let's memorize this", "save this as a skill", `/skill save`), write it at once, then show it briefly. On an ambiguous one ("I like how we did that") you may ask whether to save it.
|
|
69
|
+
- **Following**: before a task a `skill_*` index line matches, `memory_read` it and follow its steps. A step that fails or names a missing file or command: find out why (look around, read nearby READMEs) before skipping it; a step that says stop means stop and ask.
|
|
70
|
+
- **Updating**: when a step turned out different, rewrite the skill with `memory_write` in the same turn: fix those steps, keep the rest as it was, add a dated Changelog line. No confirmation needed.
|
|
71
|
+
- **The `skills` bundle** (`chi bundle install skills`, optional) adds `/skill save [name] [--system]`, `/skill list`, `/skill show <name>`, `/skill diff <name> [N]`; it keeps older versions and shows a short diff line after each update.
|
|
72
|
+
|
|
47
73
|
## Memory Bundles — shareable packs
|
|
48
74
|
|
|
49
75
|
Bundles are versioned directories/zips/tar.gz/git URLs with a `manifest.yml` and any of: memories (`*.md`), `hooks/*.rb` (bundle hooks, `docs/hooks.md`), `guardrails/*.yml` (rules, `docs/guardrails.md`), a `plugin.rb` (commands, tools, hooks, services; `docs/plugins.md`). They are shareable and installable.
|
|
@@ -33,7 +33,8 @@
|
|
|
33
33
|
`mcp` (MCP server tools; a screenshot comes as a picture), `guardrails` (rules), `known-names` (typo guard), `source-links`
|
|
34
34
|
(turns source refs like JIRA-123 in an answer into links in the web, and a one-line note), `loop-guard`
|
|
35
35
|
(denies a repeated tool call with the same result, stops the turn after a few), `check-in` (after N tool
|
|
36
|
-
calls with no answer, a card asks the user to nudge me, let me go on or stop; `/checkin`)
|
|
36
|
+
calls with no answer, a card asks the user to nudge me, let me go on or stop; `/checkin`), `skills` (`/skill save|list|show|diff`,
|
|
37
|
+
keeps older versions of `skill_*` memories). `chi bundle list`
|
|
37
38
|
shows installed + available; `chi bundle install <name>`. Settings: config.yml `bundles: <name>:`
|
|
38
39
|
(`config_modification_protocol`), read at session start: after an install or a settings change,
|
|
39
40
|
tell the user to restart the session. API and bundle docs: `docs/plugins.md`.
|
data/lib/samagotchi/client.rb
CHANGED
|
@@ -13,12 +13,12 @@ require_relative "sampling_settings"
|
|
|
13
13
|
module Samagotchi
|
|
14
14
|
# Thin HTTP client for llama.cpp's native /completion endpoint, or an
|
|
15
15
|
# OpenAI-compatible /v1/completions endpoint (e.g. mlx_lm.server or oMLX).
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
16
|
+
# Configured by the server.* settings (Samagotchi::Config):
|
|
17
|
+
# server.host (default: localhost)
|
|
18
|
+
# server.port (default: 8080; oMLX's default is 8000, set it to match)
|
|
19
|
+
# server.open_timeout (default: 10 seconds)
|
|
20
|
+
# server.read_timeout (default: 600 seconds)
|
|
21
|
+
# server.transport (llama_cpp|mlx|omlx, default: llama_cpp)
|
|
22
22
|
class Client
|
|
23
23
|
# The shared HTTP layer's errors, under their old names.
|
|
24
24
|
RequestCancelled = LLM::RequestCancelled
|
|
@@ -33,7 +33,6 @@ module Samagotchi
|
|
|
33
33
|
# generation, and a new turn doesn't ask again at once.
|
|
34
34
|
PROPS_FAILURE_TTL = 30
|
|
35
35
|
|
|
36
|
-
SERVER_TRANSPORT_ENV = "SAMAGOTCHI_SERVER_TRANSPORT"
|
|
37
36
|
DEFAULT_TRANSPORT = :llama_cpp
|
|
38
37
|
VALID_TRANSPORTS = %i[llama_cpp mlx omlx].freeze
|
|
39
38
|
|
|
@@ -147,26 +146,19 @@ module Samagotchi
|
|
|
147
146
|
# to stream its first text (LLM::HTTP); nil: no limit
|
|
148
147
|
# @param name [String, nil] the host's config name, for error lines
|
|
149
148
|
# (default: the transport's label)
|
|
149
|
+
# @param api_key_env [String, nil] the variable holding the host's API
|
|
150
|
+
# key (llama.cpp's --api-key), sent as a bearer token on every request;
|
|
151
|
+
# nil sends no Authorization header
|
|
152
|
+
# @param env [Hash] where the key variable is read
|
|
150
153
|
def initialize(host: nil, port: nil, open_timeout: nil, read_timeout: nil, transport: nil, sleeper: nil, scheme: nil,
|
|
151
|
-
first_token_timeout: nil, name: nil)
|
|
154
|
+
first_token_timeout: nil, name: nil, api_key_env: nil, env: ENV)
|
|
152
155
|
# Unified config precedence: CLI > ENV > file > default (via Samagotchi::Config)
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
cfg_host = Samagotchi::Config.get("server.host")
|
|
156
|
-
cfg_port = Samagotchi::Config.get("server.port")
|
|
157
|
-
cfg_open_timeout = Samagotchi::Config.get("server.open_timeout")
|
|
158
|
-
cfg_read_timeout = Samagotchi::Config.get("server.read_timeout")
|
|
159
|
-
cfg_transport_raw = Samagotchi::Config.get("server.transport")
|
|
160
|
-
rescue StandardError
|
|
161
|
-
nil
|
|
162
|
-
end
|
|
163
|
-
@host = host || cfg_host
|
|
164
|
-
@port = (port || cfg_port).to_i
|
|
156
|
+
@host = host || Samagotchi::Config.get("server.host")
|
|
157
|
+
@port = (port || Samagotchi::Config.get("server.port")).to_i
|
|
165
158
|
@scheme = scheme || "http"
|
|
166
|
-
@open_timeout = (open_timeout
|
|
167
|
-
@read_timeout = (read_timeout
|
|
168
|
-
|
|
169
|
-
@transport = build_transport(resolve_transport(transport || transport_fallback))
|
|
159
|
+
@open_timeout = Samagotchi::Config.positive_seconds("server.open_timeout", open_timeout)
|
|
160
|
+
@read_timeout = Samagotchi::Config.positive_seconds("server.read_timeout", read_timeout)
|
|
161
|
+
@transport = build_transport(resolve_transport(transport))
|
|
170
162
|
@props_cache = {}
|
|
171
163
|
@props_failures = {}
|
|
172
164
|
@props_mutex = Mutex.new
|
|
@@ -174,7 +166,8 @@ module Samagotchi
|
|
|
174
166
|
@host_name = name
|
|
175
167
|
@label = name.to_s.empty? ? @transport.label : name.to_s
|
|
176
168
|
@http = LLM::HTTP.new(label: @label, open_timeout: @open_timeout, read_timeout: @read_timeout,
|
|
177
|
-
sleeper: sleeper, first_token_timeout: first_token_timeout
|
|
169
|
+
sleeper: sleeper, first_token_timeout: first_token_timeout,
|
|
170
|
+
api_key: LLM::ApiKey.for(api_key_env, host: @label, env: env))
|
|
178
171
|
end
|
|
179
172
|
|
|
180
173
|
# Seconds a completion may take to stream its first text, or nil.
|
|
@@ -321,6 +314,12 @@ module Samagotchi
|
|
|
321
314
|
props
|
|
322
315
|
end
|
|
323
316
|
|
|
317
|
+
# The /props answer #server_props already has for +model+, or nil;
|
|
318
|
+
# never asks the server.
|
|
319
|
+
def cached_server_props(model: nil)
|
|
320
|
+
@props_mutex.synchronize { @props_cache[model.to_s] }
|
|
321
|
+
end
|
|
322
|
+
|
|
324
323
|
# The context window (tokens) the running server was started with, or nil
|
|
325
324
|
# when the transport reports none or the probe fails (see #server_props).
|
|
326
325
|
def context_window(model: nil)
|
|
@@ -367,7 +366,7 @@ module Samagotchi
|
|
|
367
366
|
end
|
|
368
367
|
|
|
369
368
|
def resolve_transport(transport)
|
|
370
|
-
value = (transport ||
|
|
369
|
+
value = (transport || Samagotchi::Config.get("server.transport")).to_s.strip.downcase.to_sym
|
|
371
370
|
VALID_TRANSPORTS.include?(value) ? value : DEFAULT_TRANSPORT
|
|
372
371
|
end
|
|
373
372
|
|
|
@@ -74,6 +74,14 @@ module Samagotchi
|
|
|
74
74
|
|
|
75
75
|
def command?(line) = !lookup(line).nil?
|
|
76
76
|
|
|
77
|
+
# @return [Entry, nil] the first entry the UI runs itself (local) that
|
|
78
|
+
# matches +line+, whatever its uis (a UI answers the others' too:
|
|
79
|
+
# the REPL's /detach note)
|
|
80
|
+
def lookup_local(line)
|
|
81
|
+
text = line.to_s.strip
|
|
82
|
+
@entries.find { |entry| entry.local && entry.match?(text) }
|
|
83
|
+
end
|
|
84
|
+
|
|
77
85
|
# @return [Array<Entry>] every entry, in registration order
|
|
78
86
|
def entries = @entries.dup
|
|
79
87
|
|