skill and develop
This commit is contained in:
@@ -80,6 +80,24 @@ Nanobot cron job **vždy** běží přes agenta (`agent.process_direct`) — nea
|
||||
|
||||
Plný rozbor: history 2026-05-27 "Spam Output was empty".
|
||||
|
||||
**Pozn. (2026-07-15):** Popis výše (`on_cron_job` hardcoded preamble + `evaluate_response` fail-open) platí pro starší nanobot. V `nanobot-ai` 0.2.2 je mechanismus jiný — viz "Cron `agent_turn` joby v 0.2.2 — `is_bound_cron_job`" níže. Závěr (LLM cron job pro non-reminder úlohy je nespolehlivý → řešit mimo `jobs.json`) platí dál, jen jinou cestou.
|
||||
|
||||
---
|
||||
|
||||
## Cron `agent_turn` joby v 0.2.2 — `is_bound_cron_job`, `session_key` nezávislý na doručení
|
||||
|
||||
V nainstalovaném `nanobot-ai` 0.2.2 nahradil starý "hardcoded reminder preamble + evaluate_response" mechanismus (viz sekce výše, teď zastaralá popiskem) nový: `dream` a `heartbeat` jsou speciální system joby v `on_cron_job` (`nanobot/cli/commands.py`); libovolný jiný `agent_turn` job z `jobs.json` jde přes `is_bound_cron_job()` (`nanobot/cron/session_turns.py`) → `run_bound_cron_job()` (`nanobot/cron/bound_runner.py`).
|
||||
|
||||
**`is_bound_cron_job(job)`** vrací `True`, když `payload.kind == "agent_turn"` a má `session_key`+`origin_channel`+`origin_chat_id`, a **žádné** z `deliver`/`channel`/`to`/`channel_meta` není nastavené. Bound joby se spustí přes `agent.submit_cron_turn(...)` **jako normální session turn** — **bez jakéhokoli delivery gatingu** (na rozdíl od `heartbeat`, který volá `evaluate_response(..., default_notify=False)` s fail-closed chováním). Cokoli agent v bound cron tahu odpoví, jde přímo do kanálu.
|
||||
|
||||
**`session_key` (kam se zapíše historie) je nezávislý na `origin_channel`/`origin_chat_id` (kam se doručí)** — `origin_delivery_context()` (`nanobot/cron/session_delivery.py`) čte výhradně `payload.origin_channel`/`origin_chat_id`, vůbec ne `session_key`. Lze tedy nastavit `session_key` na cokoli (jiná/izolovaná session, nebo — pokud jde mimo `jobs.json` — per-run unikátní hodnota) a doručení do daného kanálu/chatu zůstane funkční.
|
||||
|
||||
**Gotcha:** `jobs.json` neumí dynamický/per-run `session_key` (je to statický string v definici jobu) — takže "vždy čerstvá session" se **nedá** vyřešit uvnitř `jobs.json`, i když by teoreticky šlo nastavit fixní odlišnou session (izolovanou od živého chatu, ale pořád akumulující historii mezi běhy). Pro opravdu čistý kontext při každém běhu je potřeba jít mimo `jobs.json` úplně — Python API `Nanobot.run(session_key=…)` s novou hodnotou (např. timestamp) při každém spuštění, vzor `detach` (`f"detach:{path.stem}"`). `Nanobot.run(ephemeral=True)` (`nanobot.py:126-135`, `process_direct(ephemeral=True)` interně) existuje, ale neprochází channel-delivery pipeline — po ephemeral běhu by bylo nutné doručení řešit ručně (publish `OutboundMessage` nebo přímé Bot API volání).
|
||||
|
||||
**Praktický důsledek:** Cron job navázaný `session_key` na **stejnou session jako živý chat uživatele** (`f"{channel}:{chat_id}"`, typicky vznikne, když se job zakládá přímo z chatu) kontaminuje conversation historii mezi denním chatem a nočními běhy navzájem — omyl agenta v jednom běhu se táhne do dalších. Řešeno pro `compact-memory-auto-daily`: history 2026-07-15.
|
||||
|
||||
Zdroj: `nanobot/cron/session_turns.py:is_bound_cron_job`, `nanobot/cron/bound_runner.py:run_bound_cron_job`, `nanobot/cron/session_delivery.py:origin_delivery_context`, `nanobot/nanobot.py:126-165` (`Nanobot.run`).
|
||||
|
||||
---
|
||||
|
||||
## Workspace vzniká při prvním spuštění agenta
|
||||
@@ -213,11 +231,12 @@ Zdroj: `nanobot/channels/telegram.py:258-326` (BotCommand registrace, regex rout
|
||||
|
||||
## Postup: přidání nového modelu (preset)
|
||||
|
||||
Modely se přidávají jako položky do `modelPresets` v `~/.nanobot/config.json` na serveru `nanobot.hell` (uživatel `nanobot`).
|
||||
Modely se přidávají jako položky do `model_presets` v `~/.nanobot/config.json` na serveru `nanobot.hell` (uživatel `nanobot`). Pozor: top-level klíč je v souboru **snake_case** (`model_presets`), vnitřní klíče presetu camelCase (`maxTokens`, `contextWindowTokens`, `reasoningEffort`).
|
||||
|
||||
**Kroky:**
|
||||
|
||||
1. **Ověř dostupnost u providera.** Pro Ollama: `curl http://nvidia.hell:11434/api/tags` a zkontroluj, že název modelu (přesně, včetně `:cloud` suffixu) je v seznamu. Pro OpenRouter: `curl https://openrouter.ai/api/v1/models`.
|
||||
**Dostupnost v katalogu ≠ použitelnost** — u Ollama Cloud ověř i funkčním voláním: `curl http://nvidia.hell:11434/v1/chat/completions -d '{"model":"<id>","messages":[{"role":"user","content":"say OK"}],"max_tokens":20}'` (viz sekce o extra usage níže).
|
||||
2. **Edituj config in-place** přes Python (zachová ostatní klíče včetně secrets):
|
||||
|
||||
```bash
|
||||
@@ -238,10 +257,29 @@ Modely se přidávají jako položky do `modelPresets` v `~/.nanobot/config.json
|
||||
|
||||
4. **V chatu** (Telegram/WebUI) přepneš příkazem `/model <preset-name>`.
|
||||
|
||||
**Konvence pojmenování presetů:** `<model-zkratka>-<provider>` (např. `kimi-k2.6-openrouter`, `glm-5.1-ollama`). Suffix providera je důležitý — uživatel chce v názvu vidět, odkud model jede.
|
||||
**Konvence pojmenování presetů:** krátký alias podle modelu — `kimi`, `kimi27`, `kimi3`, `glm`, `glm52`, `sonnet`, `haiku`, `gemini-flash`. (Dřív tu stálo `<model>-<provider>` jako `kimi-k2.6-openrouter`; reálný stav na serveru je od nějaké doby krátká forma, ověřeno 2026-07-27.)
|
||||
|
||||
**Parametry presetu:** `maxTokens` 16384 a `temperature` 0.1 napříč všemi presety. `contextWindowTokens` se drží na **~97 % reálného okna modelu** (rezerva na výstup), reálné okno se čte z `curl http://nvidia.hell:11434/api/show -d '{"model":"<id>"}'` → `model_info["<family>.context_length"]`. `reasoningEffort: null` = zachovat default providera (`schema.py:141`), explicitní hodnota jen kde ji chceme vynutit (`glm52: high`).
|
||||
|
||||
**Ollama gotcha:** `providers.ollama.apiBase` musí končit `/v1` (`http://nvidia.hell:11434/v1`) — viz [[Ollama provider potřebuje `/v1` suffix v `apiBase`]].
|
||||
|
||||
## Ollama Cloud: některé modely jsou „extra usage only" (kimi-k3)
|
||||
|
||||
`kimi-k3:cloud` je v `api/tags` vidět a `/model kimi3` v nanobotu se přepne bez chyby, ale **každé volání skončí HTTP 402**:
|
||||
|
||||
```text
|
||||
this model uses extra usage only (not included plan usage) and your extra usage
|
||||
balance is empty, add extra usage or turn on auto reload at ollama.com/settings
|
||||
```
|
||||
|
||||
Nanobot to uživateli přeloží na *„API key is out of quota or the account is in arrears"* — což svádí na chybu v configu; **není to chyba configu**. Ollama má dvě oddělené peněženky: *plan usage* (zahrnuto v Pro/Max) a *extra usage balance* (dokupovaný kredit). K3 je zařazený mimo plán a čerpá **jen** z extra usage — podle [model page](https://ollama.com/library/kimi-k3) vyžaduje Pro/Max **a** nenulový extra balance, kapacita se teprve rozšiřuje. Billing je GPU-time-based, per-token sazba pro extra usage není veřejně zveřejněná.
|
||||
|
||||
**Diagnostika:** ostatní cloud modely (`glm-5.2:cloud`, `kimi-k2.7-code:cloud`) přitom jedou normálně — když 402 sedí jen na jednom modelu, je to tahle politika, ne stav účtu.
|
||||
|
||||
**Alternativa bez dobíjení:** stejný model na OpenRouteru jako `moonshotai/kimi-k3` (ctx 1 048 576, $3/M in, $15/M out) — provider `openrouter` je v configu nakonfigurovaný.
|
||||
|
||||
Zdroj: [Ollama pricing](https://ollama.com/pricing), [ollama.com/library/kimi-k3](https://ollama.com/library/kimi-k3), ověřeno 2026-07-27 (history).
|
||||
|
||||
## Logování: gateway `-v`/`--verbose`, agent `--logs`
|
||||
|
||||
Nanobot defaultně **vypíná vlastní logy** (`logger.disable("nanobot")`), proto v běžném výstupu nic není. Zapínají se podle příkazu:
|
||||
@@ -389,6 +427,70 @@ do meta-statusu.
|
||||
**externím daemonem mimo agent loop** — viz "Detach skill" níže. Stejný pattern
|
||||
už používá `/remind` (viz "Cron job s LLM agentem je nespolehlivý…" výše).
|
||||
|
||||
**AKTUALIZACE (ověřeno 2026-07-15 na `nanobot_ai-0.2.2` živém na serveru): tohle popisuje jen fallback pro "unbound" joby.** Skutečná `agent_turn` architektura je jiná — viz "Cron `agent_turn` job = normal session turn (bound cron, od `nanobot_ai` 0.2.2)" níže. `on_cron_job` v `cli/commands.py` má teď větev `is_bound_cron_job(job)` → `run_bound_cron_job(...)`; jen když job **není** bound (chybí `session_key`/`origin_channel`/`origin_chat_id`, nebo má vyplněné staré `deliver`/`channel`/`to`/`channel_meta`), spadne do staré větve výše s hardcoded preamblem — a ta dnes končí `CronJobSkippedError("unbound agent cron job must be recreated from a chat session")`, ne úspěšným během. Reálný job v `jobs.json` (`nanobot-version-check`) je bound.
|
||||
|
||||
---
|
||||
|
||||
## Cron `agent_turn` job = normal session turn (bound cron, od `nanobot_ai` 0.2.2)
|
||||
|
||||
**Kontext, který LLM dostane při odpálení cron jobu, je (skoro) identický s běžnou zprávou v té session, kde byl job vytvořen** — ne izolovaný/ephemerní jednorázový volání jako dřív (`process_direct`). Ověřeno čtením zdroje na serveru (`nanobot_ai-0.2.2.dist-info`), potvrzeno reálným `jobs.json` (job `nanobot-version-check`: `sessionKey: "websocket:52d0f338-…"`, `originChannel: "websocket"`, `originChatId: "…"`, `deliver: false`, `channel: null`).
|
||||
|
||||
**Tok (`nanobot/cron/bound_runner.py:run_bound_cron_job`):**
|
||||
|
||||
1. Job musí být **bound** (`nanobot/cron/session_turns.py:is_bound_cron_job`): `payload.kind == "agent_turn"` + `session_key`/`origin_channel`/`origin_chat_id` vyplněné + staré delivery pole (`deliver`/`channel`/`to`/`channel_meta`) prázdné. Bound joby vznikají tak, že se vytvoří z chatu (agent zná runtime `Channel`/`Chat ID` z kontextu a předá je do `cron` toolu).
|
||||
2. Zpráva se vyrenderuje z šablony `templates/agent/cron_reminder.md`: *"The scheduled time has arrived. Execute this scheduled cron job now and report the result to the user in the same session."* + pravidla (mluv přímo, nenarrativizuj, žádné user ID, žádné "Done") + `Cron job: {{ message }}` (= `payload.message`).
|
||||
3. Sestaví se `InboundMessage` s `channel`/`chat_id` = **origin** hodnoty z jobu, `session_key_override = payload.session_key`, a metadata `_cron_trigger` (job_id, job_name, run_id, prompt_ref) + `_cron_defer_until_session_idle: true`.
|
||||
4. Ta zpráva jde přes `agent.submit_cron_turn()` → `CronTurnCoordinator.submit()` → **stejnou frontu `bus.consume_inbound()`/`_dispatch()`/`_process_message()` jako kterákoli chatová zpráva** (`agent/loop.py:run`/`_dispatch`). Žádná zkratka, žádný oddělený "cron mód" LLM volání.
|
||||
|
||||
**Co z toho plyne pro obsah promptu, který LLM vidí:**
|
||||
|
||||
- **Celý systémový prompt jako obvykle** — bootstrap soubory (`AGENTS.md`/`SOUL.md`/`USER.md`/`TOOLS.md`), `memory/MEMORY.md`, always-skilly (`ContextBuilder.build_messages`, stejná cesta jako běžný tah).
|
||||
- **Celá historie té session** (`session_key` = session, ve které byl cron job vytvořen — typicky WebUI/Telegram konverzace), ne prázdná/ephemerní session. Cron zpráva se do historie i zapíše (`_persist_user_message_early` s `cron_history_overrides`), ale zobrazená verze v historii je zkrácená: `"Scheduled cron job triggered: <job_name>\n\n<payload.message>"`, ne celá vyrenderovaná šablona — a metadata na ní má `cron_job_id`/`cron_job_name`/`cron_run_id`/`cron_prompt_ref`.
|
||||
- **Runtime context blok** (`Channel`/`Chat ID`/`Current Time`) ukazuje origin channel/chat_id jobu — model tedy vidí, do jakého kanálu odpovídá, stejně jako u normální zprávy.
|
||||
- **Všechny toolly** jsou k dispozici stejně jako v normálním tahu (žádný omezený tool subset jako u Dream `build_dream_tools()`).
|
||||
- **Odpověď se doručí automaticky** — finální text tahu (`_process_message` return) jde přes `bus.publish_outbound()` do origin channel/chat_id, přesně jako běžná odpověď. Žádný druhý fail-open `evaluate_response` gate (ten zůstal jen u `heartbeat` větve).
|
||||
|
||||
**Deferral:** pokud je cílová session zrovna aktivní (probíhá jiný tah), `CronTurnCoordinator.defer_if_active()` job zafrontuje a spustí ho, až se session uvolní (`publish_next_deferred` po dokončení běžícího tahu) — cron turn tedy nikdy neběží souběžně s live chatem ve stejné session, ale ani ho nezahodí.
|
||||
|
||||
**Praktický důsledek:** cron `agent_turn` job dnes **umí** to, co starý hardcoded preamble bránil (viz sekce výše) — protože jede jako normální tah, může provést multi-step úkol, zapsat soubor, a odpověď je jen finální zpráva tahu, ne meta-status obalený cizím promptem. `nanobot-version-check` job v produkčním `jobs.json` toho využívá (kontrola PyPI verze + podmíněné oznámení).
|
||||
|
||||
Zdroj: `nanobot/cron/bound_runner.py`, `nanobot/cron/session_turns.py`, `nanobot/agent/cron_turns.py` (`CronTurnCoordinator`), `nanobot/agent/loop.py:574-578,868-870,1035-1117` (`submit_cron_turn`, `_dispatch`), `nanobot/templates/agent/cron_reminder.md`, `nanobot_ai-0.2.2` na `nanobot.hell` (živý zdroj, ne upstream repo).
|
||||
|
||||
---
|
||||
|
||||
## Bound cron job sdílí session s chatem — důsledky pro růst kontextu, `/new` a (ne)možnost odpoutání
|
||||
|
||||
Navazuje na sekci výše. Bound cron job **nemá vlastní session** — `payload.sessionKey` je přesně ten samý klíč jako chat, odkud vznikl (`CronTool.set_context()`: `session_key = f"{channel}:{chat_id}"`). Každé odpálení jobu appendne celý svůj tah (trigger zpráva + tool volání + finální odpověď) do `session.messages` té konverzace — soutěží o prostor s reálným chatem, ne v izolaci.
|
||||
|
||||
**Co to omezuje a co ne:**
|
||||
|
||||
- **`maxMessages: 120`** (server `config.json`, `agents.defaults.maxMessages` / schema default 120) — do LLM promptu se replayuje jen posledních 120 zpráv (`session/manager.py::get_history`). Token náklad per-tah tedy neroste do nekonečna, stará historie se jen vysouvá z okna.
|
||||
- **`idleCompactAfterMinutes: 0` na `nanobot.hell` — auto-compact (`AutoCompact`) je VYPNUTÝ.** Default v schématu je 15 min; tady je natvrdo 0, takže `check_expired()` (`agent/loop.py`, volané na 1s timeoutu hlavní smyčky) nikdy nic nekomprimuje na 8 zpráv + summary.
|
||||
- I kdyby zapnutý byl: **periodický cron job v té session drží `session.updated_at` čerstvý** → session nikdy nevypadá jako idle → auto-compact by se pro ni stejně nikdy nespustil. Recurring cron bound na chat tedy fakticky blokuje auto-compact té session, i kdyby byl jinde v config zapnutý.
|
||||
- Výsledek: `session.messages` na disku roste bez omezení (jen replay do LLM je capnutý na 120) a nic z toho neprochází konsolidací do `MEMORY.md`/Dream, dokud uživatel neudělá `/new`.
|
||||
|
||||
**`/new` (`cmd_new`, `command/builtin.py:205`) čistí přesně tenhle sdílený session_key** — `session.clear()` + na pozadí archivace starého obsahu přes `consolidator` (feed pro Dream). Protože cron job běží ve stejné session, `/new` v daném chatu **vyprázdní i kontext, který cron job uvidí** při příštím odpálení (stará historie se archivuje, ne ztratí — jen zmizí z "recent" replaye).
|
||||
|
||||
**Odpoutat cron job od session přes `cron` tool nejde — strukturálně.** `CronTool._add_job` (`agent/tools/cron.py`) bere `session_key`/`origin_channel`/`origin_chat_id` vždy z aktuálního request kontextu; bez nich vrátí chybu `"scheduled cron jobs must be created from a chat session"`. Parametr `deliver` v `execute()` existuje, ale nikam se nepředává do `add_job` — mrtvý pozůstatek staré (unbound) architektury. Ruční editace `jobs.json` na "unbound" tvar (bez `sessionKey`/`originChannel`/`originChatId`) taky nepomůže — `is_bound_cron_job()` vrátí `False` a `on_cron_job` rovnou shodí `CronJobSkippedError("unbound agent cron job must be recreated from a chat session")`; **stará větev s hardcoded preamblem (viz sekce "Cron preamble je hardcoded" výše) se pro `agent_turn` joby dnes nikdy neprovede — je to mrtvá cesta.**
|
||||
|
||||
**Pro skutečně nezávislou (session-decoupled) periodickou úlohu použij [[Detach skill]] níže** — vlastní `session_key` namespace (`detach:<slug>`), mimo cron/jobs.json úplně, doručení přímo přes Telegram Bot API bez účasti chatové session.
|
||||
|
||||
Zdroj: `nanobot/agent/tools/cron.py` (`CronTool._add_job`, `set_context`), `nanobot/agent/autocompact.py` (`AutoCompact`), `nanobot/config/schema.py:147-156` (`session_ttl_minutes`/`idleCompactAfterMinutes`, `max_messages`/`maxMessages`), `nanobot/session/manager.py` (`get_history` replay slicing), `nanobot/command/builtin.py:205` (`cmd_new`), živý `~/.nanobot/config.json` na `nanobot.hell` (`idleCompactAfterMinutes: 0`, `maxMessages: 120`) — ověřeno 2026-07-15.
|
||||
|
||||
### Upstream stav: žádný flag na vypnutí sdílení session zatím neexistuje (ověřeno 2026-07-15)
|
||||
|
||||
Sdílení session cronu s originálním chatem (viz výše) je **záměrný design**, ne bug: [PR #4299](https://github.com/HKUDS/nanobot/pull/4299) *"feat(cron): bind scheduled automations to sessions"* (mergnuto 2026-06-12) ho zavedlo cíleně, aby se vyřešilo doručování/injektování zpráv doprostřed živého chatu (`bound_runner.py` vznikl přesně touhle PR).
|
||||
|
||||
Na neomezený růst kontextu si ale stěžují i jinde a řeší se to teď (oba stavy **open, nemergnuto**):
|
||||
|
||||
- [Issue #4082](https://github.com/HKUDS/nanobot/issues/4082) — *"cron jobs reuse fixed cron:{job.id} session context across runs"*, navrhuje buď per-run isolaci, nebo *"an explicit config flag controlling cron context retention"*.
|
||||
- [PR #4550](https://github.com/HKUDS/nanobot/pull/4550) — řeší to per-run izolovanou session (`session_key:run_id`, smazanou hned po tahu přes nový `agent.delete_session()`), **bez** configurovatelného flagu. Autor PR k automatizované review připomínce (ztráta cross-run kontinuity pro monitoring/diff joby) napsal: *"a future `shared_session` option could be added, but that's a design decision for maintainers"* — tedy jen nápad, ne odsouhlasený roadmap item, žádný maintainer zatím nezareagoval. `mergeable_state: dirty` (2026-07-06).
|
||||
- Starší konkurenční [PR #2457](https://github.com/HKUDS/nanobot/pull/2457) (open od 2026-03-25) řeší podobný problém na starším kódu, pravděpodobně obsoletní vůči #4550.
|
||||
|
||||
**Souvisí:** [PR #4370](https://github.com/HKUDS/nanobot/pull/4370) *"Enable idle auto-compact by default"* (mergnuto 2026-06-16) změnilo schema default `idleCompactAfterMinutes` z `0` na `15` — právě kvůli krátkým chatům, co se nikdy nekompaktují. Náš server má v `config.json` **explicitní `0`** (přebíjí schema default bez ohledu na verzi) — nezávisí na tom, jestli byl nastaven před nebo po téhle změně.
|
||||
|
||||
**Akce:** sledovat #4082/#4550 (a případně #2457), jestli se mergne a jestli přibude `shared_session` flag — pak přehodnotit, jestli přenastavit `nanobot-version-check` (a budoucí podobné joby) na isolated mód. Zatím jediná cesta k plnému odpoutání = [[Detach skill]] níže.
|
||||
|
||||
---
|
||||
|
||||
## Detach skill — background úlohy přes externí daemon (mimo agent loop)
|
||||
@@ -567,6 +669,22 @@ Přepsáno z SQLite row-store na **capture → compile pipeline** (vzor llm-wiki
|
||||
- **Search:** `/note search|find <dotaz>` → načti celý `notes.md`, odpověz (read-only). Mazání = editace dokumentu (žádné display ID, žádné tagy).
|
||||
- **Odděleno** od `/keep`, `MEMORY.md`, Dream, llm-wiki (`cml/`). **Kolize triggerů s `keep`:** NL „poznamenej si/ulož si …" může spadnout do `keep` (obě to claimují) — spolehlivý je explicitní `/note` prefix; description note skillu odlišen („NOT durable facts → keep"). Starý `note.py`/`note.sqlite` vyřazeny (`note.sqlite` v `backup/note-retired/`).
|
||||
|
||||
---
|
||||
|
||||
## Skill `/project` — pojmenované dlouhodobé pracovní kontexty (přepsáno 2026-07-22)
|
||||
|
||||
Substituce Claude.ai "Projects". Adresář na projekt, ne jeden soubor: `workspace/projects/<slug>/{prompt.md, memory.md, state.md, artifacts/}`. `prompt.md` = kontext/instrukce čtené při aktivaci; `memory.md` = append-only chronologická historie/rozhodnutí; `state.md` = **živý** dokument (přepisuje se na místě, syntéza "kde to teď je" — ne deník); `artifacts/` = generované soubory bez zvláštní evidence.
|
||||
|
||||
**Aktivní projekt = čistě konverzační paměť**, žádný stavový soubor na disku pro "co je aktivní" (viz [[decisions.md]]). Nový projekt vzniká jen po explicitním potvrzení uživatele — skill bez ptaní pracuje jen s existujícími projekty. Mazání/přejmenování projektu je mimo scope v1 (ruční zásah v adresáři).
|
||||
|
||||
**Nahradil netrackovaný server-side skill** (existoval na serveru mimo tento repo, žádná zmínka v history/knowledge/decisions před tímto datem): plochý soubor `projects/<slug>.md` s frontmatterem `status`/`priority`/`created`, CLI backend (`scripts/project.py`: add/list/show/status), `switch` ukládal aktivní projekt do `my` scratchpad nástroje (viz níže). Reálná data (`projects/radio-1.md`, projekt na stříhání audio streamu Radia 1, 2026-06-09) přemigrována do nového formátu jako `projects/radio1/`. Plná historie: history.md 2026-07-22.
|
||||
|
||||
## `my` nástroj — přece jen nějaký perzistentní scratchpad existuje
|
||||
|
||||
Zjištěno 2026-07-22 při objevu starého `/project` skillu výše: ten používal `my(action="set", key="project_context", value="<slug>")` k uložení aktivního projektu (a zjevně `action="get"` k přečtení). To je v napětí s dřívějším závěrem [[Bound cron job sdílí session s chatem]] výše, že nanobot nemá žádný session-scoped state kromě historie zpráv — `my` je zjevně nějaká forma key-value scratchpadu dostupná agentovi jako tool.
|
||||
|
||||
**Needs-verify:** starý skill sám dokumentoval jen „lives only in `my` scratchpad and is lost on restart" — neříká, jestli je klíč per-`session_key` nebo globální napříč celým agentem. Nebylo ověřeno proti zdrojáku (`nanobot/agent/tools/*.py`), jen pozorováno z použití v `SKILL.md` starého skillu (kopie stažená do `tmp/server-project-check/`, negitované). Pokud je globální, nemusí řešit "aktivní projekt per konverzace" o nic líp než čistě konverzační paměť — proto zůstal nový `/project` skill navržen bez `my`. Stojí za ověření zdrojáku, než se `my` použije jinde jako spolehlivý state store.
|
||||
|
||||
## Nanobot MÁ `web` tool (search + Jina Reader fetch) — `TOOLS.md` ho nezmiňuje
|
||||
|
||||
Config `tools.web` (server `config.json`) má `enable: true` s **search** (`provider: duckduckgo`, maxResults 5) a **fetch** (`useJinaReader: true`). Agent tedy umí stáhnout URL přes `web` tool — Jina Reader vrací čistý markdown a zvládne JS i měkké paywally. `workspace/TOOLS.md` (dokumentace toolů) `web` **neuvádí** (jen `exec`/`grep`/`cron`) — je neúplný; zdroj pravdy je `config.json`. Pro fetch URL ve skillu preferuj `web` tool; `exec`+curl (`-o file` kvůli 10k/60s limitu `exec`) je fallback. Ověřeno při přepisu `/note` (DT Glass URL reálně stažena, history 2026-07-01).
|
||||
|
||||
Reference in New Issue
Block a user