@explyt/ai-agent 0.3.2 → 0.4.0-dogfood.2

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.
Files changed (67) hide show
  1. package/README.md +3 -3
  2. package/THIRD-PARTY-NOTICES.md +33 -30
  3. package/agent-docs/README.md +4 -1
  4. package/agent-docs/cache-warming.md +62 -0
  5. package/agent-docs/local-models.md +34 -0
  6. package/agent-docs/mcp.md +11 -0
  7. package/agent-docs/plugins.md +62 -0
  8. package/agent-docs/telegram.md +171 -0
  9. package/agent-docs/updates.md +28 -2
  10. package/agent-docs/web-search.md +4 -0
  11. package/dist/cli/brand-profile.js +2 -1
  12. package/dist/cli/engine.js +171 -16
  13. package/dist/cli/index.js +19 -10
  14. package/dist/cli/install-auto.js +1 -5
  15. package/dist/cli/install-config.js +8 -6
  16. package/dist/cli/install-run.js +2 -2
  17. package/dist/cli/install-settings.js +62 -63
  18. package/dist/cli/plugin.js +1 -0
  19. package/dist/cli/settings-lock.js +121 -0
  20. package/dist/cli/update-env.js +38 -0
  21. package/dist/cli/update.js +2 -9
  22. package/dist/extensions/ask-user-question.js +1 -1
  23. package/dist/extensions/bash.js +1 -1
  24. package/dist/extensions/custom-provider.js +1 -1
  25. package/dist/extensions/explyt-account.js +1 -1
  26. package/dist/extensions/hello.js +1 -1
  27. package/dist/extensions/identity.js +1 -1
  28. package/dist/extensions/login-menu.js +1 -0
  29. package/dist/extensions/mcp.js +1 -1
  30. package/dist/extensions/misc-hacks.js +1 -1
  31. package/dist/extensions/plugins.js +1 -0
  32. package/dist/extensions/policy.js +1 -1
  33. package/dist/extensions/rules.js +1 -1
  34. package/dist/extensions/session-title.js +1 -1
  35. package/dist/extensions/skills.js +1 -1
  36. package/dist/extensions/subagents.js +1 -1
  37. package/dist/extensions/telegram.js +1 -0
  38. package/dist/extensions/telemetry.js +1 -1
  39. package/dist/extensions/todo.js +1 -1
  40. package/dist/extensions/tool-rows.js +1 -1
  41. package/dist/extensions/tui.js +1 -1
  42. package/dist/extensions/update-notice.js +1 -1
  43. package/dist/extensions/web-access.js +1 -1
  44. package/dist/extensions/write.js +1 -1
  45. package/dist/install/installer.js +147 -83
  46. package/dist/mcp-adapter.js +415 -197
  47. package/dist/mcp-script-worker.mjs +240 -110
  48. package/dist/rpiv-ask-user-question/{chunk-JGZK32CE.js → chunk-CDNB7COL.js} +1 -1
  49. package/dist/rpiv-ask-user-question/{chunk-VN36RZ2Z.js → chunk-NXE76BE4.js} +1 -1
  50. package/dist/rpiv-ask-user-question/{chunk-NG5WTH2G.js → chunk-V6PM4YRC.js} +2 -1
  51. package/dist/rpiv-ask-user-question/index.js +3 -3
  52. package/dist/rpiv-todo/{chunk-CQEKZQYZ.js → chunk-7UKL4B2W.js} +1 -1
  53. package/dist/rpiv-todo/{chunk-KQHBIJFK.js → chunk-RYUIY66D.js} +1 -1
  54. package/dist/rpiv-todo/{chunk-NG5WTH2G.js → chunk-V6PM4YRC.js} +2 -1
  55. package/dist/rpiv-todo/index.js +4 -4
  56. package/dist/vendor/pi-coding-agent.js +7 -8
  57. package/dist/vendor/pi-mcp-adapter-types.js +44 -1
  58. package/dist/vendor/pi-tui.js +4848 -3128
  59. package/dist/vendor/pi-web-access.js +10294 -8633
  60. package/dist/vendor/typebox/{chunk-6XHASRAZ.js → chunk-TYYI2UPT.js} +1 -1
  61. package/dist/vendor/typebox/index.js +2 -2
  62. package/dist/vendor/typebox/value.js +278 -59
  63. package/dist/vendor/zod.js +51 -2
  64. package/package.json +11 -10
  65. package/dist/vendor/pi-ai-compat.js +0 -2
  66. package/dist/vendor/pi-web-access-deps/modelcontextprotocol__sdk__client__index.js.js +0 -9
  67. package/dist/vendor/pi-web-access-deps/modelcontextprotocol__sdk__client__streamableHttp.js.js +0 -9
package/README.md CHANGED
@@ -17,8 +17,8 @@ explyt update # install the newer latest, then restart open sessi
17
17
  For a one-off pinned run:
18
18
 
19
19
  ```bash
20
- npx -y "@explyt/ai-agent@0.3.2" # start the terminal UI
21
- npx -y "@explyt/ai-agent@0.3.2" login # store a provider credential, once
20
+ npx -y "@explyt/ai-agent@0.4.0" # start the terminal UI
21
+ npx -y "@explyt/ai-agent@0.4.0" login # store a provider credential, once
22
22
  ```
23
23
 
24
24
  The agent runs with exactly the extensions this build ships with and ignores any
@@ -28,4 +28,4 @@ you get.
28
28
  IDE (ACP) integration is not part of this package: it is provided by the Explyt
29
29
  managed backend, which the Neo plugin downloads and manages.
30
30
 
31
- Run `npx -y @explyt/ai-agent@0.3.2 --help` for the available options.
31
+ Run `npx -y @explyt/ai-agent@0.4.0 --help` for the available options.
@@ -89,7 +89,7 @@ License or other written license agreements with the Company.
89
89
  the unmodified `pi-mcp-adapter` entrypoint (`index.ts`) together with the package's
90
90
  small runtime helper files, so that Node can load it outside the engine's TypeScript
91
91
  loader. The compilation also inlines the package's own runtime dependencies except
92
- `recheck`, `@napi-rs/keyring`, `open` and `@modelcontextprotocol/ext-apps`, which npm
92
+ `recheck`, `@napi-rs/keyring`, `open` and `quickjs-wasi`, which npm
93
93
  installs beside it; each inlined package is listed with its copyright notice in the
94
94
  generated "Packages compiled into this package's own files" section at the end of this file.
95
95
  - The public npm package contains `dist/vendor/pi-tui.js` and `dist/vendor/pi-mcp-adapter-types.js`:
@@ -102,23 +102,19 @@ License or other written license agreements with the Company.
102
102
  - The public npm package contains `dist/vendor/pi-web-access.js`, an unobfuscated
103
103
  compilation of the unmodified `pi-web-access` entrypoint (`dist/index.js`) carrying the
104
104
  package's name, version and licence in a banner, so that its imports of the engine and
105
- `typebox` resolve to the product's files under `dist/vendor/`; and small generated files
106
- under `dist/vendor/pi-web-access-deps/` that import the package's own installed copy of
107
- `@modelcontextprotocol/sdk`. The package's own files are not altered.
108
- - In the managed backend archive only, `pi-mcp-adapter` is redistributed with one
109
- textual modification made at build time: in its `index.ts`, the slash-command alias
110
- registered as `pi-mcp` (a second name for the package's `mcp` status command) is
111
- renamed to `explyt-mcp`. The command's behavior, the primary `mcp` name, the package's
112
- event names and diagnostics are unchanged. The modification is applied by
113
- `scripts/build-release.mjs` (`scripts/bundled-command-rename.mjs`) and is documented
114
- here as required by the MIT License; the original copyright and license notice above
115
- apply to the modified package unchanged. The public npm package does not carry the
116
- adapter and therefore carries no modification of it.
105
+ `typebox` resolve to the product's files under `dist/vendor/`. The package's own files
106
+ are not altered.
107
+ - In the managed backend archive only, `pi-mcp-adapter` is redistributed as installed
108
+ from npm; the public npm package does not carry the adapter's files. Any textual
109
+ modification made at build time is listed below, applied by `scripts/build-release.mjs`
110
+ (`scripts/mcp-keyring-service.mjs`) and documented here as required by the MIT
111
+ License; the original copyright and license notice above apply to a modified package
112
+ unchanged.
117
113
  - The public npm package contains `dist/vendor/pi-coding-agent.js`, a generated file that
118
114
  re-exports the engine's own bundled entry point by name; it contains no engine code.
119
115
  - The formerly bundled `pi-subagents` package was removed by spec 008; references to it in
120
116
  older release history are historical and do not describe the current artifact.
121
- - Seven units of `@earendil-works/pi-coding-agent` 0.84.4 are **copied** (from the TypeScript in
117
+ - Seven units of `@earendil-works/pi-coding-agent` 1.1.0 are **copied** (from the TypeScript in
122
118
  the shipped sourcemaps' `sourcesContent`) into `extensions/bash/engine/` under spec 006
123
119
  (RQ-37) — `killProcessTree` into `packages/shared/src/process/`, where spec 008's children
124
120
  share it; the whitespace-normalized originals sit next to each copy as `*.upstream.txt`, and every
@@ -133,7 +129,7 @@ License or other written license agreements with the Company.
133
129
  replaced by `join(getAgentDir(), "bin")`.
134
130
  - `sanitizeBinaryOutput` — `src/utils/shell.ts` → `sanitize-binary-output.ts`; unmodified.
135
131
  - `OutputAccumulator` — `src/core/tools/output-accumulator.ts` → `output-accumulator.ts`;
136
- modified: the `os.tmpdir()` spill file is removed.
132
+ modified: the spill file (and `readFullOutput`, which only reads it back) is removed.
137
133
  - `resolveSpawnContext` — `src/core/tools/bash.ts` → `spawn-context.ts`; modified: produces only the
138
134
  `PI_*` session variables (`sessionEnvFrom`); the env merge, `spawnHook` and `exposeSessionEnvironment` are gone.
139
135
  - `formatOutput` — `src/core/tools/bash.ts` (closure inside `execute`) → `format-preview.ts`; modified:
@@ -650,19 +646,20 @@ Generated at build time. It records the license metadata declared by the npm pac
650
646
  this package depends on at runtime; none is bundled into the tarball, npm installs them beside it.
651
647
  Consult each package for its complete license text.
652
648
 
653
- - @modelcontextprotocol/ext-apps@1.7.5: license metadata = MIT (installed by npm beside this package)
649
+ - @modelcontextprotocol/sdk@1.32.1: license metadata = MIT (installed by npm beside this package)
654
650
  - @mozilla/readability@0.6.0: license metadata = Apache-2.0 (installed by npm beside this package)
655
- - @napi-rs/keyring@1.3.0: license metadata = MIT (installed by npm beside this package)
651
+ - @napi-rs/keyring@2.1.1: license metadata = MIT (installed by npm beside this package)
656
652
  - defuddle@0.19.3: license metadata = MIT (installed by npm beside this package)
657
653
  - linkedom@0.16.11: license metadata = ISC (installed by npm beside this package)
658
654
  - minimatch@10.2.6: license metadata = BlueOak-1.0.0 (installed by npm beside this package)
659
655
  - model-probe@0.6.0: license metadata = MIT (installed by npm beside this package)
660
656
  - open@10.2.0: license metadata = MIT (installed by npm beside this package)
661
657
  - p-limit@6.2.0: license metadata = MIT (installed by npm beside this package)
662
- - pi-mcp-adapter@2.31.0: license metadata = MIT (installed by npm beside this package)
663
- - pi-web-access@0.31.0: license metadata = MIT (installed by npm beside this package)
658
+ - pi-mcp-adapter@5.2.0: license metadata = MIT (installed by npm beside this package)
659
+ - pi-web-access@0.39.0: license metadata = MIT (installed by npm beside this package)
664
660
  - promise.try@2.0.2: license metadata = MIT (installed by npm beside this package)
665
- - recheck@4.5.0: license metadata = MIT (installed by npm beside this package)
661
+ - quickjs-wasi@3.6.2: license metadata = MIT (installed by npm beside this package)
662
+ - recheck@4.6.0-beta.3: license metadata = MIT (installed by npm beside this package)
666
663
  - semver@7.8.5: license metadata = ISC (installed by npm beside this package)
667
664
  - turndown@7.2.4: license metadata = MIT (installed by npm beside this package)
668
665
  - unbash@5.0.0: license metadata = ISC (installed by npm beside this package)
@@ -670,9 +667,9 @@ Consult each package for its complete license text.
670
667
  - unpdf@1.8.1: license metadata = MIT (installed by npm beside this package)
671
668
  - yaml@2.9.0: license metadata = ISC (installed by npm beside this package)
672
669
  - yauzl@3.4.0: license metadata = MIT (installed by npm beside this package)
673
- - typebox@1.3.24: license metadata = MIT (compiled into dist/vendor/typebox)
670
+ - typebox@1.3.27: license metadata = MIT (compiled into dist/vendor/typebox)
674
671
  - model-probe@0.6.0: license metadata = MIT (compiled into dist/vendor/model-probe)
675
- - pi-web-access@0.31.0: license metadata = MIT (compiled into dist/vendor/pi-web-access.js)
672
+ - pi-web-access@0.39.0: license metadata = MIT (compiled into dist/vendor/pi-web-access.js)
676
673
 
677
674
  ## Packages compiled into this package's own files
678
675
 
@@ -680,13 +677,17 @@ Generated at build time from the packages each compiled, unobfuscated file inlin
680
677
  directories do not ship, so their copyright notices are reproduced here; the full text of
681
678
  each licence named is under "Full License Texts" above.
682
679
 
683
- - @earendil-works/pi-tui@0.84.4, MIT, compiled into dist/vendor/pi-tui.js
680
+ - @earendil-works/pi-tui@1.1.0, MIT, compiled into dist/vendor/pi-tui.js
684
681
  - Copyright (c) 2025 Mario Zechner
685
- - notice recorded from https://github.com/earendil-works/pi/blob/v0.84.4/LICENSE (the package ships none)
686
- - @modelcontextprotocol/client@2.0.0, MIT, compiled into dist/mcp-adapter.js
682
+ - notice recorded from https://github.com/earendil-works/pi/blob/v1.1.0/LICENSE (the package ships none)
683
+ - @modelcontextprotocol/client@2.3.1, Apache-2.0, compiled into dist/mcp-adapter.js
687
684
  - Copyright (c) 2024-2025 Model Context Protocol a Series of LF Projects, LLC.
688
- - @modelcontextprotocol/core@2.0.0, MIT, compiled into dist/mcp-adapter.js
685
+ - @modelcontextprotocol/core@2.3.1, Apache-2.0, compiled into dist/mcp-adapter.js
689
686
  - Copyright (c) 2024-2025 Model Context Protocol a Series of LF Projects, LLC.
687
+ - @modelcontextprotocol/ext-tasks@0.1.0, Apache-2.0, compiled into dist/mcp-adapter.js
688
+ - Copyright 2025 The Model Context Protocol Authors
689
+ - @typesafe-ai/sdk@0.6.0, MIT, compiled into dist/mcp-adapter.js
690
+ - Copyright (c) 2026 TypeSafe
690
691
  - ajv-formats@3.0.1, MIT, compiled into dist/mcp-adapter.js
691
692
  - Copyright (c) 2020 Evgeny Poberezkin
692
693
  - ajv@8.20.0, MIT, compiled into dist/mcp-adapter.js
@@ -716,7 +717,7 @@ each licence named is under "Full License Texts" above.
716
717
  - Copyright (c) 2018 Filip Skokan
717
718
  - json-schema-traverse@1.0.0, MIT, compiled into dist/mcp-adapter.js
718
719
  - Copyright (c) 2017 Evgeny Poberezkin
719
- - marked@18.0.5, MIT, compiled into dist/vendor/pi-tui.js
720
+ - marked@18.0.11, MIT, compiled into dist/vendor/pi-tui.js
720
721
  - Copyright (c) 2018+, MarkedJS (https://github.com/markedjs/)
721
722
  - Copyright (c) 2011-2018, Christopher Jeffrey (https://github.com/chjj/)
722
723
  - Copyright © 2004, John Gruber
@@ -727,7 +728,7 @@ each licence named is under "Full License Texts" above.
727
728
  - Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
728
729
  - pend@1.2.0, MIT, compiled into dist/vendor/yauzl.js
729
730
  - Copyright (c) 2014 Andrew Kelley
730
- - pi-mcp-adapter@2.31.0, MIT, compiled into dist/mcp-adapter.js, dist/vendor/pi-mcp-adapter-types.js
731
+ - pi-mcp-adapter@5.2.0, MIT, compiled into dist/mcp-adapter.js, dist/vendor/pi-mcp-adapter-types.js
731
732
  - Copyright (c) 2026 Nico Bailon
732
733
  - pkce-challenge@5.0.1, MIT, compiled into dist/mcp-adapter.js
733
734
  - Copyright (c) 2019
@@ -737,15 +738,17 @@ each licence named is under "Full License Texts" above.
737
738
  - Copyright (c) Kevin Mårtensson <kevinmartensson@gmail.com> (github.com/kevva)
738
739
  - shebang-regex@3.0.0, MIT, compiled into dist/mcp-adapter.js
739
740
  - Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
740
- - smol-toml@1.9.0, BSD-3-Clause, compiled into dist/mcp-adapter.js
741
+ - smol-toml@1.9.1, BSD-3-Clause, compiled into dist/mcp-adapter.js
741
742
  - Copyright (c) Squirrel Chat et al., All rights reserved.
742
743
  - strip-json-comments@5.0.3, MIT, compiled into dist/mcp-adapter.js
743
744
  - Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (https://sindresorhus.com)
745
+ - undici@6.29.0, MIT, compiled into dist/mcp-adapter.js
746
+ - Copyright (c) Matteo Collina and Undici contributors
744
747
  - which@2.0.2, ISC, compiled into dist/mcp-adapter.js
745
748
  - Copyright (c) Isaac Z. Schlueter and Contributors
746
749
  - yaml@2.9.0, ISC, compiled into dist/vendor/yaml.js
747
750
  - Copyright Eemeli Aro <eemeli@gmail.com>
748
751
  - yauzl@3.4.0, MIT, compiled into dist/vendor/yauzl.js
749
752
  - Copyright (c) 2014 Josh Wolfe
750
- - zod@4.6.5, MIT, compiled into dist/vendor/zod.js
753
+ - zod@4.6.6, MIT, compiled into dist/vendor/zod.js
751
754
  - Copyright (c) 2025 Colin McDonnell
@@ -15,6 +15,8 @@ it. Read the whole file before editing anything it describes.
15
15
  | `agents.md` | helper agents and model profiles: where the files live, every field, which model a helper runs on, disabling one |
16
16
  | `mcp.md` | MCP servers: the two `mcp.json` files, the entry shape, disabling a server or a tool |
17
17
  | `web-search.md` | web search and page fetch: `web-search.json`, where requests go, keys, switching search off, failures |
18
+ | `local-models.md` | models on the user's own machine: the llama.cpp router and `/llama`, other OpenAI-compatible servers |
19
+ | `cache-warming.md` | prompt cache warming: what it costs and saves, which models it works with, `cacheWarming` and how to turn it on |
18
20
  | `plugins.md` | Marketplace plugins (dogfood builds only; a stable build has neither this file nor `explyt plugin`): the project manifest, the `explyt plugin` command, plugin values |
19
21
  | `telegram.md` | the Telegram remote for terminal sessions (dogfood builds only; a stable build has neither this file nor `/telegram`): setup and pairing, `/telegram` and its subcommands, notifications, its files, what leaves the machine |
20
22
 
@@ -58,7 +60,8 @@ dogfood builds only: if `telegram.md` is absent here, this build does not have t
58
60
 
59
61
  The agent updates itself: the terminal UI checks its channel at most once a day and installs a newer
60
62
  version in the background, used from the next start. `explyt update` updates at once, and
61
- `PI_OFFLINE=1` turns the check off. The update channel and the automatic update switch
63
+ `EXPLYT_OFFLINE=1` turns the check off, together with the engine's own background requests;
64
+ `updates.md` lists what it stops and what still goes out. The update channel and the automatic update switch
62
65
  (`updateChannel`, `autoUpdate`) are not `.explyt/` settings: they live in the engine's own
63
66
  `settings.json` and are changed with `explyt config`. `updates.md` has the details.
64
67
 
@@ -0,0 +1,62 @@
1
+ # Prompt cache warming
2
+
3
+ Some model providers cache the beginning of a request — the system prompt, the tools, the
4
+ conversation so far — for a few minutes. A later request that starts the same way reads that part
5
+ from the cache at a fraction of the price. When the cache expires, the next request pays the full
6
+ price for all of it again.
7
+
8
+ **Cache warming** keeps that cache alive: shortly before it expires, the agent re-sends the last
9
+ request with a one-token answer limit, so the next real request still finds the cache. It is
10
+ **off by default**. Turn it on only when the user asks for it or agrees to it after you explain the
11
+ trade-off below.
12
+
13
+ ## What it costs and what it saves
14
+
15
+ Every refresh is a **real, billed request** that the user did not make: it is paid at the cache-read
16
+ price for the whole prompt plus one output token. It saves money only when a real request follows
17
+ before the cache would have expired anyway; a refresh that nobody needed is pure cost.
18
+
19
+ The agent decides each refresh on its own and refreshes only when the expected saving is at least
20
+ $0.05. Refreshes stop one hour after the last real request of a run (30 minutes after it between
21
+ runs), when the conversation changes, or when the mode is switched off. Each real request starts that
22
+ clock again, so a run that keeps sending requests keeps being warmed. Their cost counts toward the
23
+ session's usage; they do not enter the conversation.
24
+
25
+ It pays off with a large context on an expensive model and long pauses inside a run — a long build,
26
+ long tests, a slow tool — that would otherwise outlive the cache.
27
+
28
+ ## Which models it works with
29
+
30
+ Only models whose cache lifetime the agent knows. In the built-in model catalogue these are the
31
+ **Anthropic** models (Claude), with a 5-minute cache, or 1 hour when the environment the agent starts
32
+ with sets `EXPLYT_CACHE_RETENTION=long` (`long` is the only value; any other is ignored, and so is the
33
+ engine's own `PI_CACHE_RETENTION` set by hand). For every other provider in the catalogue — OpenAI, Google, Bedrock,
34
+ OpenRouter and the rest — no lifetime is known, so warming does nothing there even when it is on.
35
+ The Explyt AI Agent account models and custom providers are not warmed either: they declare no
36
+ cache lifetime and no price, so a refresh could never be shown to save anything. A Claude model that thinks with a fixed thinking budget (not adaptive
37
+ thinking) is never warmed either, because its cache cannot be refreshed safely.
38
+
39
+ ## Modes
40
+
41
+ | Value | Meaning |
42
+ |---|---|
43
+ | `"off"` | never refresh (the product default) |
44
+ | `"streaming"` | refresh while the agent is working on a request; stop when it finishes |
45
+ | `"idle"` | also keep refreshing for up to 30 minutes after the last real request, in case the user writes again soon |
46
+
47
+ `"idle"` costs the most: most refreshes between requests are not followed by a request in time.
48
+
49
+ ## Turning it on or off
50
+
51
+ The setting is `cacheWarming` in the engine's own settings file, `<root>/agent/settings.json`
52
+ (`updates.md` says where `<root>` is) — **not** `.explyt/settings.json`, and there is no project
53
+ value. Change it with the command, which keeps every other key of that file:
54
+
55
+ ```
56
+ explyt config set cacheWarming streaming
57
+ explyt config set cacheWarming off
58
+ explyt config get cacheWarming
59
+ ```
60
+
61
+ A change applies to sessions started afterwards. `/session` in the terminal shows the current warming
62
+ decision of the session.
@@ -0,0 +1,34 @@
1
+ # Local models
2
+
3
+ There are two ways to run the agent on a model served from the user's own machine or network.
4
+
5
+ ## A llama.cpp router: `/llama`
6
+
7
+ The agent supports the [llama.cpp](https://github.com/ggml-org/llama.cpp) router server, which finds
8
+ the GGUF models in a directory and loads or unloads them on demand.
9
+
10
+ 1. Start the router without `--model` (a model on the command line starts single-model mode instead):
11
+
12
+ ```bash
13
+ llama-server --models-dir ~/models --no-models-autoload --jinja --host 127.0.0.1 --port 8080 -ngl 999 -c 32768
14
+ ```
15
+
16
+ 2. In the terminal, run `/login llama.cpp` and enter the router URL (default `http://127.0.0.1:8080`)
17
+ and an API key if the router was started with `--api-key`. The environment variables
18
+ `LLAMA_BASE_URL` and `LLAMA_API_KEY` configure the same without `/login`.
19
+ 3. Run `/llama` to load a model (or download one from Hugging Face), then pick it with `/model`.
20
+
21
+ `/llama` lists the router's models: select an unloaded one to load it, a loaded one to unload it, or
22
+ **Download model…** to search Hugging Face. Loaded and sleeping models appear in `/model`; with
23
+ `--no-models-autoload` a model has to be loaded through `/llama` first. Nothing here deletes model
24
+ files. `/llama` is a terminal command; in an IDE, load the model in the terminal (or start the router
25
+ with autoload) and choose it in the IDE's model list.
26
+
27
+ When something does not work, check the router first: `curl http://127.0.0.1:8080/health` and
28
+ `curl http://127.0.0.1:8080/models`. No models in `/llama` means the `--models-dir` or its layout is
29
+ wrong; a load that fails or runs out of memory needs a smaller `-c` or another model unloaded.
30
+
31
+ ## Any OpenAI-compatible server: `/custom-provider`
32
+
33
+ Ollama, LM Studio, vLLM or any other server with an OpenAI-compatible API is added with
34
+ `/custom-provider`, which asks for the address and finds the server's models.
package/agent-docs/mcp.md CHANGED
@@ -53,6 +53,17 @@ HTTP server uses `url` with optional `headers`.
53
53
  | `headers` | object of strings | headers sent with each request |
54
54
  | `disabled` | boolean | `true` keeps the definition but does not start the server |
55
55
  | `excludeTools` | array of strings | tools of this server the model must not see |
56
+ | `auth` | `{ "provider": "<id>" }` | send the credential stored with `/login` for that model provider as a Bearer token; **not in a project file** |
57
+
58
+ `auth` with a `provider` is honoured in `~/.explyt/mcp.json` and in the IDE's own session
59
+ request. A project file or a plugin chooses its own server URL, so the product refuses such a
60
+ server there: it is not started, and `/mcp` shows why on its row.
61
+
62
+ That refusal covers only the credentials of the model providers. It is **not** a boundary for other
63
+ secrets: a project file can still put an environment variable into `headers`, `url` or `env`
64
+ (`${VAR}`), and that value goes to whatever server the file names. Treat a project's `mcp.json` like
65
+ the project's code: do not add a server that sends a secret to a URL you have not checked, and do not
66
+ put a secret the project should not see into the environment the agent starts with.
56
67
 
57
68
  An entry whose field has the wrong type is skipped with a warning, and the rest of the file still
58
69
  loads. A file that is not valid JSON, or whose top level is not an object, contributes no servers
@@ -0,0 +1,62 @@
1
+ # Marketplace plugins
2
+
3
+ ## Project activation
4
+
5
+ Plugins are enabled per project in `.explyt/plugins.json`. Commit the exact plugin version and each MCP server's zero-based `runModes` index; do not put credentials or filesystem paths in this file. An entry without a `version` is reported and ignored, and `enable`, `disable` and `config set-run-mode` drop it when they rewrite the file.
6
+
7
+ Example:
8
+
9
+ ```json
10
+ {
11
+ "schemaVersion": 1,
12
+ "plugins": {
13
+ "security-pack": {
14
+ "version": "1.3.1",
15
+ "runModes": { "Github MCP": 1 }
16
+ }
17
+ }
18
+ }
19
+ ```
20
+
21
+ ## Plugin commands
22
+
23
+ Plugins are managed from a shell in the project directory with `explyt plugin <operation>`. Without a global install, run `npx -y @explyt/ai-agent@<version> plugin <operation>` with the same version as the running agent (`explyt --version`, or the version it was started with). The current directory is the project. The command prints results on stdout and errors on stderr, and exits `0` on success, `1` when an operation fails, and `2` on an argument or usage error: an unknown operation or flag, a missing or extra argument, or an invalid plugin ID, version or run-mode index. Each error names its typed kind, such as `PluginVersionNotInstalled`.
24
+
25
+ The operations are `install`, `uninstall`, `update`, `enable`, `disable`, `list`, `details`, `verify`, `config set`, and `config set-run-mode`; `explyt plugin help` lists their arguments, shows both values-file shapes and an `enable` example. Plugin activation requires an installed exact version. When enabling a plugin is not a no-op, provide an explicit run-mode choice for every declared MCP server, one `--run-mode <server-name> <index>` each, for example `explyt plugin enable security-pack@1.3.1 --run-mode "Github MCP" 1 --run-mode Local 0`; an existing selection for the same exact version in the current project may be reused. `verify` takes no argument and reports every issue of the plugins the current project enables: a missing or damaged version, an incompatible `minPluginVersion`, and a required value that is not set. The issues are its result: it prints them on stdout and exits `0`, and fails only when the project manifest cannot be read or is invalid.
26
+
27
+ Before writing a values file or choosing run modes, run `explyt plugin details <plugin-id>@<version> --project` for the installed version. It lists each declared variable with its type, whether it is required, its default and choices, and whether a global or project value is stored (never the value itself), and each MCP server with its run-mode variants by index and the index this project selects. Without a version, or for a version that is not installed, `details` lists no declarations.
28
+
29
+ No command changes a running session. Changes take effect in a new session, or in a running terminal session after `/reload`. A command run through a tool cannot reload the session it was run from.
30
+
31
+ ## Values and credentials
32
+
33
+ Credentials and other plugin values stay in the local plugin store, not in the project manifest. Values are first supplied at install time with a JSON object mapping each declared variable ID to a string; a required value that is neither supplied, already stored, nor defaulted fails the install:
34
+
35
+ ```text
36
+ explyt plugin install <plugin-id>[@<version>] --values-file <values-file>
37
+ ```
38
+
39
+ ```json
40
+ { "github_pat": "ghp_...", "mode": "http" }
41
+ ```
42
+
43
+ Later changes go through `config set`:
44
+
45
+ ```text
46
+ explyt plugin config set <plugin-id>@<version> <values-file>
47
+ explyt plugin config set <plugin-id>@<version> --project <values-file>
48
+ ```
49
+
50
+ The first command patches global values; the second patches values for the current project. A project value overrides the global value for that project. The values file for `config set` contains a `values` object and may contain an `unset` array; omitted keys are preserved, and an ID may not be in both:
51
+
52
+ ```json
53
+ { "values": { "mode": "mcp" }, "unset": ["api_key"] }
54
+ ```
55
+
56
+ A `secret`, `path` or free-form `string` value may be an `${env:NAME}` or `${env:NAME:-fallback}` reference instead of the value itself, which keeps a secret out of the store; it is read from the agent's environment when a session starts.
57
+
58
+ To remove an installed version, use `explyt plugin uninstall <plugin-id>[@<version>]`. Removing the last installed version also removes that plugin's stored values and per-version `${PLUGIN_DATA}` directories. To change one MCP server's selection for an enabled version, use:
59
+
60
+ ```text
61
+ explyt plugin config set-run-mode <plugin-id>@<version> <server-name> <index>
62
+ ```
@@ -0,0 +1,171 @@
1
+ # Telegram remote for terminal sessions
2
+
3
+ The Telegram remote lets the user follow and continue a **terminal** (`explyt`) session from the
4
+ phone: write to the agent, stop it, answer its permission dialogs and questions, and get notified
5
+ when it finishes or fails. It works only in the terminal UI; IDE sessions and subagents do not
6
+ load it. Nothing happens until the user runs `/telegram setup`.
7
+
8
+ The remote is **not configured in `.explyt/`**. There is no `settings.json` key for it: the user
9
+ sets it up with terminal commands, and its files live in the agent's own directory (below). Do not
10
+ write a token or a Telegram setting into any `settings.json`; nothing reads it there.
11
+
12
+ ## Setting it up
13
+
14
+ 1. The user creates a bot of their own with `@BotFather` in Telegram (`/newbot`) and copies its
15
+ token. The product has no shared bot and no server of its own: the terminal talks to Telegram
16
+ directly.
17
+ 2. The user turns on **Threaded Mode** for the bot: open @BotFather's mini app (the Open button in
18
+ the BotFather chat) → the bot → Bot Settings → Threaded Mode. The classic `/mybots` menu does not
19
+ have this setting. Setup refuses a bot with Threaded Mode off, because a session the user opens
20
+ gets a topic of its own (topics exist only on demand; see below).
21
+ 3. In the terminal: `/telegram setup`. It asks for the token in a masked prompt.
22
+ 4. Setup shows a single-use link `https://t.me/<bot>?start=<code>` and a QR code. The user opens it
23
+ on the phone within 5 minutes and presses Start. The Telegram account that does this becomes the
24
+ only one that can control the agent. A wrong, expired or used code gets no reply in Telegram;
25
+ the terminal says to open the newest link from `/telegram setup`. A link from an earlier setup
26
+ or an earlier terminal run is no longer valid. The link and QR code leave the terminal once the
27
+ code is used, after 5 minutes, or when the setup is cancelled.
28
+
29
+ Setup stops and stores nothing when the token is rejected, when Threaded Mode is off, or when the bot
30
+ already has a **webhook** set — that means another program uses the bot, and setup shows the
31
+ webhook URL instead of deleting it. The user should then make a separate bot for the terminal.
32
+
33
+ Running `/telegram setup` again pairs anew; the old pairing stays until the new one succeeds.
34
+
35
+ ## Terminal commands
36
+
37
+ The remote has one terminal command, `/telegram`, with subcommands (the terminal completes them):
38
+
39
+ | Command | Effect |
40
+ |---|---|
41
+ | `/telegram` or `/telegram status` | show the pairing, notifications, presence and connection |
42
+ | `/telegram setup` | set up or re-pair the remote (above) |
43
+ | `/telegram away` | mark the user away, for every terminal session of this user |
44
+ | `/telegram back` | clear the away mark and show what happened remotely while away |
45
+ | `/telegram notify always\|auto\|off` | set the notifications (below) |
46
+ | `/telegram unpair` | disconnect: delete the token and all remote state |
47
+
48
+ There is no `/away`, `/back` or `/mode` command of the remote; `/mode` in the terminal belongs to
49
+ the permission modes.
50
+
51
+ The footer shows `telegram: online`, `away` (you are marked away), `off` (notifications off), `offline`, `token revoked`, `conflict` (another
52
+ program polls the same bot) or `not paired`. `online` and `offline` are the view of the terminal that
53
+ polls Telegram, the same in every terminal; `offline` also means no terminal polls. `not paired` shows in every terminal session until
54
+ the first setup, so the remote is discoverable; while not paired the extension makes no network
55
+ call. The away mark lasts until `/telegram back`; typing in the terminal does not clear it.
56
+
57
+ ## Notifications
58
+
59
+ | Notifications | Permission cards and questions | "Done" and final-error notices | Reply to a run started from Telegram | Progress and `typing…` |
60
+ |---|---|---|---|---|
61
+ | `auto` (default), not away | sent silently, only if the session has a topic | sent silently, only if the session has a topic | sent silently | for every run of a session that has a topic |
62
+ | `auto`, after `/telegram away` | sent with sound (the topic is created if needed) | sent with sound | sent with sound | for every run, silently |
63
+ | `always` | sent with sound (the topic is created if needed) | sent with sound | sent with sound | for every run, silently |
64
+ | `off` | not sent | not sent | not sent | never |
65
+
66
+ `auto` rings the phone only while you are marked away. While you are at the computer (`auto`, no `/telegram away`), a session you did not open from
67
+ `/sessions` stays out of Telegram entirely. A session that has a topic is watched: every run of
68
+ it, also one you start in the terminal, shows its progress (with `⏹ Stop`) and its answer in the
69
+ topic, silently; the phone rings only after `/telegram away` or with `always`.
70
+
71
+ A run counts as started from Telegram when a message from the phone reached it (also a steer into
72
+ a running turn); its final answer, or its final error, comes back to the topic.
73
+
74
+ When a card is sent (see the table), a permission dialog or a question is open in the terminal
75
+ **and** in Telegram at once; the first answer, from either side, wins and closes the other. The Telegram card
76
+ shows what the terminal question shows, in the same order: the effect sentences (or the
77
+ `IRREVERSIBLE:` heading of a once-only action), the command, the shell prefix line when one is
78
+ set, and the folder. It offers exactly the terminal's options: `Allow once`, `Allow for this
79
+ session`, `Reject` — or `Reject`, `Allow once` for a once-only action. `Allow for this session`
80
+ pressed in Telegram records the same session grant as in the terminal. A subagent's permission
81
+ question arrives the same way, as a card in its parent session's topic that names the subagent.
82
+ With `off` the bot still accepts commands and messages.
83
+
84
+ ## In Telegram
85
+
86
+ The bot works only in its private chat with the paired user; messages from anyone else, forwarded
87
+ or edited messages, and groups are ignored (the bot leaves a group it is added to).
88
+
89
+ A terminal session has a topic only on demand: you open it from `/sessions`, or it is created
90
+ when the session needs you while you are away (or with `always`) — a permission card, a
91
+ question, or the answer of a run. Opening a terminal creates nothing. When the session ends (the
92
+ terminal closes, `/new`, `/resume` or `/fork`; not `/reload`), its topic is deleted with its
93
+ messages, so the chat lists only sessions that run. After `/new`, `/resume` or `/fork` the delete
94
+ waits a few seconds, so `/resume` back to the same session keeps its topic. If a terminal is killed,
95
+ or Telegram is unreachable when it closes, the delete is not lost: the next running terminal does
96
+ it (the pending deletes live in `state.json`). A card that waited while the session had no topic
97
+ appears once you open the session from `/sessions` or run `/telegram away`. A topic is named `<project> · <session title>`
98
+ (`new session` until the session has a title) and is renamed only when the title changes, because
99
+ Telegram posts a visible "renamed the thread" line for each rename. Agent text is shown with its
100
+ Markdown formatting (code, bold, links). Nothing is pinned.
101
+
102
+ `/sessions` is a picker of the running sessions, with their state (🟢 running, ⏳ waiting for you,
103
+ 💤 idle):
104
+
105
+ - `☐ <project · title · state>` — a session without a topic; a tap selects it (`☑`).
106
+ - `📂 Open selected (N)` — creates the topics of the selected sessions; each gets a
107
+ `📍 This session: …` note, so it shows as unread in the chat's topic list. The button answers
108
+ `Opening N topic(s)…` at once; a session that could not be opened is reported in a message
109
+ under the list.
110
+ - `💬 <project · title> · open` — a session that has a topic; a tap posts the note there again.
111
+
112
+ Only one list is active: a new `/sessions` deletes the previous list and the previous `/sessions`
113
+ message (one older than 48 hours is marked `⌛ This list is old` instead).
114
+
115
+ While a run goes (when the table above shows progress), the bot posts one progress message when
116
+ the run starts and shows `typing…`; cards and replies sent during the run come after it. The
117
+ message shows the elapsed time (it moves every few seconds) and the step number, which is the
118
+ number of tool calls so far. It lists the run's last five tool calls: `▶` while a call runs, `✓`
119
+ once that call ended; calls the agent runs in parallel show `▶` together, and a subagent shows its
120
+ task. It also shows `retry n/m` while the provider is retried, `⏳ Waiting for you` while a card
121
+ waits, and a `⏹ Stop` button that stops that run. When the run ends, the message folds into one
122
+ line (`✓ Done in … · N steps`, `✖ Stopped after …` or `❌ Failed after …`), also after a rate limit
123
+ or a short outage, and the answer arrives as a new message with `Diff` and `History` buttons.
124
+ Turning notifications `off` during a run stops its progress message where it is.
125
+
126
+ - Plain text in a session topic goes to the agent. While the agent works, it steers the current
127
+ run, as typing in the terminal does; `/later <text>` queues it for after the run instead.
128
+ - A message older than 10 minutes (for example, sent while the laptop slept) is not run on its
129
+ own: the bot asks `Run now` or `Discard`.
130
+ - The bot's command menu holds the chat-wide commands: `/sessions`, `/notify always|auto|off`,
131
+ `/help`, `/unpair`. `/notify off` and `/unpair` from the phone ask for confirmation. Telegram
132
+ cannot give a topic its own menu, so the session commands are not in the menu; type them in a
133
+ session topic (`/help` lists them): `/stop`, `/later <text>`, `/history` (recent turns, `Full`
134
+ sends the whole history as a file), `/diff` (files changed in the session by `edit` and `write`,
135
+ with a `.diff` file; changes made through `bash` are not counted). The `⏹ Stop` button on the
136
+ progress message and the `Diff`/`History` buttons on an answer do the same. `/start` is sent by
137
+ the pairing link's Start button; you never type it.
138
+ - Messages from Telegram appear in the terminal transcript as user messages. The bot posts no
139
+ "received" reply: `typing…` and the progress message show that the session took the message;
140
+ only a message the session could not take gets a reply.
141
+
142
+ A topic you created yourself, or one whose session ended but could not be deleted, answers that
143
+ no session uses it or that the session is closed. The remote works only while a terminal session is running: when the machine sleeps or the
144
+ terminal is closed, nothing answers. With several terminals open, one of them polls Telegram and
145
+ routes messages to the others; if a session is open in two terminals, only the first has the
146
+ remote.
147
+
148
+ ## Where its files are
149
+
150
+ All of them are in the agent's own directory, under `telegram/`, with mode `0600` (directory
151
+ `0700`):
152
+
153
+ ```text
154
+ <agent dir>/telegram/credentials.json bot token, bot id and username, paired user id
155
+ <agent dir>/telegram/state.json mode, away mark, topics of running sessions, topics owed a delete, update offset, the active /sessions list
156
+ <agent dir>/telegram/leader.json which terminal process polls Telegram
157
+ <agent dir>/telegram/sessions/ running sessions and how to reach them
158
+ <agent dir>/telegram/handled/ update ids already handled, to drop repeats
159
+ ```
160
+
161
+ `<agent dir>` is `$XDG_DATA_HOME/explyt-ai-agent/agent` (default `~/.local/share/explyt-ai-agent/agent`),
162
+ or `PI_CODING_AGENT_DIR` when it is set. Do not edit these files; `/telegram unpair` removes them.
163
+
164
+ ## What leaves the machine
165
+
166
+ Telegram receives the agent's final answers (cut to 1000 characters), the progress of a run (tool
167
+ names and a short argument of each), permission cards with the command, questions, and — on request —
168
+ history and diffs. Bot chats are **not end-to-end encrypted** and stay on Telegram's servers. A
169
+ user who must not send project code there should keep notifications `off` or not set the remote up. The
170
+ token is never written to logs; anyone who has it can read what the bot receives, so it belongs
171
+ only in the credentials file.
@@ -33,12 +33,38 @@ conflict, and the installer offers to remove it.
33
33
  Updates are automatic: the terminal UI checks its channel at most once a day and installs a newer
34
34
  version in the background. The new version is used from the next start; the terminal UI then says so,
35
35
  and `explyt --continue` resumes the session on it. A running session keeps its version until it exits.
36
- `explyt update` updates at once. `PI_OFFLINE=1` turns every check off.
36
+ `explyt update` updates at once.
37
+
38
+ ## Offline mode
39
+
40
+ `EXPLYT_OFFLINE=1` (also `true` or `yes`) in the environment the agent starts with turns off these
41
+ requests the agent makes on its own:
42
+
43
+ - the update check and the background install described above;
44
+ - the engine's own checks at startup and the refresh of the model catalogue — including the model
45
+ list of the Explyt AI Agent account: after a login, or when the account adds a model, the list stays
46
+ as it was stored last (on a fresh login, only the free model) until the agent runs without the
47
+ variable.
48
+
49
+ Only this variable does it. The engine's own `PI_OFFLINE`, set by hand, keeps the engine offline but
50
+ no longer stops the agent's update check and background install; use `EXPLYT_OFFLINE` instead.
51
+
52
+ It is not a switch for all network access. These still go out with it set:
53
+
54
+ - requests the user's work needs: the model provider, web search and page fetch, MCP servers and
55
+ their sign-in;
56
+ - the product's usage telemetry, which this variable does not control;
57
+ - the Telegram remote, when it is set up: it keeps polling Telegram while it runs;
58
+ - the MCP servers' own background refresh of their tool lists, while those servers are configured.
59
+
60
+ Set the variable where the agent is started — the shell profile for the terminal, the IDE's
61
+ environment for the IDE — and restart it.
37
62
 
38
63
  ## The two update settings
39
64
 
40
65
  They live in the engine's settings file, `<root>/agent/settings.json`, **not** in `.explyt/`. Change them
41
- with `explyt config`, which keeps every other key of that file:
66
+ with `explyt config`, which keeps every other key of that file (it also sets `cacheWarming`,
67
+ described in `cache-warming.md`):
42
68
 
43
69
  | Key | Values | Default | Meaning |
44
70
  |---|---|---|---|
@@ -36,6 +36,7 @@ Every time the product starts, it adds these keys to the file when they are abse
36
36
  {
37
37
  "provider": ["exa", "duckduckgo"],
38
38
  "workflow": "none",
39
+ "toolActivation": "eager",
39
40
  "autoOpenBrowser": false,
40
41
  "fetch": { "allowedModes": ["readable", "raw"] },
41
42
  "githubClone": { "clonePath": "<os temp dir>/explyt-github-repos" },
@@ -55,6 +56,9 @@ Every time the product starts, it adds these keys to the file when they are abse
55
56
  - `provider` is not added when the file already has `provider`, `searchProvider` or `searchRouting`.
56
57
  - `workflow` is ignored: every search runs without the browser-based curator and without a
57
58
  generated summary, whatever the file says.
59
+ - `toolActivation: "eager"` gives you the four web tools from the first message. Do not change it:
60
+ with `"auto"` or `"dynamic"` the tools are hidden behind an extra activation step
61
+ (`web_enable`) that the product does not support.
58
62
  - The page-answer mode of `fetch_content` (`answer`) is not available; the tool offers `readable`
59
63
  and `raw` only.
60
64
 
@@ -38,11 +38,12 @@ const PROFILE = freeze({
38
38
  "repository": "explyt/explyt-ai-agent-distribution"
39
39
  },
40
40
  "telemetryEndpoint": "https://plugin.explyt.ai/telemetry",
41
- "mcpAlias": "explyt-mcp",
42
41
  "loginCommand": "explyt login",
43
42
  "githubCloneDir": "explyt-github-repos",
44
43
  "agentDirEnv": "EXPLYT_CODING_AGENT_DIR",
45
44
  "updateCheckEnv": "EXPLYT_UPDATE_CHECK",
45
+ "offlineEnv": "EXPLYT_OFFLINE",
46
+ "cacheRetentionEnv": "EXPLYT_CACHE_RETENTION",
46
47
  "oauth": {
47
48
  "keyringService": "pi-mcp-adapter.oauth",
48
49
  "redirectUri": "http://127.0.0.1:34517/callback",