agentkernel-cli 0.1.0__tar.gz → 0.2.0__tar.gz
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.
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/PKG-INFO +20 -6
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/README.md +19 -5
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agent-kernel-design.md +5 -5
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/agent.py +8 -2
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/cli.py +236 -17
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/config.py +6 -1
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/context/manager.py +13 -2
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/curation.py +41 -8
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/memory.py +198 -27
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/providers/__init__.py +25 -1
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/providers/anthropic.py +19 -1
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/providers/base.py +1 -0
- agentkernel_cli-0.2.0/agentkernel/providers/compat.py +87 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/providers/local.py +4 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/providers/openai.py +22 -3
- agentkernel_cli-0.2.0/agentkernel/roles.py +55 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/semantic_memory.py +20 -5
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/skills.py +74 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/types.py +52 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel.toml.example +14 -1
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/pyproject.toml +1 -1
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/.gitignore +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/AGENT.md +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/LICENSE +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/__init__.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/__main__.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/approval/__init__.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/approval/base.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/approval/cli.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/approval/policy.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/approval/risk.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/approval/sandbox.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/budget.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/checkpoint.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/context/__init__.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/context/truncate.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/cron.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/doctor.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/embeddings.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/evaluation.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/improvement.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/insights.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/kanban.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/knowledge.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/loops.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/mcp/__init__.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/mcp/client.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/mcp/config.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/mcp/tools.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/paths.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/plugins.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/profiles.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/progress.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/providers/_http.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/providers/credentials.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/redaction.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/semantic_index.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/subagent.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/telemetry.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/tools/__init__.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/tools/base.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/tools/builtin/__init__.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/tools/builtin/checkpoint_tool.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/tools/builtin/clarify.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/tools/builtin/files.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/tools/builtin/kanban_tool.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/tools/builtin/search.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/tools/builtin/shell.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/tools/builtin/todo.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/tui/__init__.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/tui/app.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/agentkernel/worktree.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/examples/README.md +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/examples/evals/builtin-tools.toml +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/examples/loops/until-tests-pass.toml +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/examples/playground/NOTES.md +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/examples/playground/calc.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/examples/playground/inventory.py +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/examples/skills/code-review/SKILL.md +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/templates/README.md +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/templates/SKILL.md +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/templates/eval-suite.toml +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/templates/loop.toml +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/templates/mcp-servers.toml +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/templates/profile.toml +0 -0
- {agentkernel_cli-0.1.0 → agentkernel_cli-0.2.0}/templates/tool_module.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentkernel-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A minimal, dependency-light kernel for a general-purpose AI agent — runs anywhere, brings its own brain.
|
|
5
5
|
Project-URL: Homepage, https://github.com/sebbyrule/agentkernal
|
|
6
6
|
Project-URL: Repository, https://github.com/sebbyrule/agentkernal
|
|
@@ -76,6 +76,9 @@ API keys are read **only** from the environment — never from config files or t
|
|
|
76
76
|
```bash
|
|
77
77
|
export ANTHROPIC_API_KEY=*** # for provider = "anthropic"
|
|
78
78
|
export OPENAI_API_KEY=*** # for provider = "openai" / embeddings
|
|
79
|
+
export OPENROUTER_API_KEY=*** # for provider = "openrouter"
|
|
80
|
+
export DEEPSEEK_API_KEY=*** # for provider = "deepseek"
|
|
81
|
+
export GEMINI_API_KEY=*** # for provider = "gemini"
|
|
79
82
|
# local/OpenAI-compatible endpoints (Ollama, vLLM) usually need no key
|
|
80
83
|
# Credential pool: give several keys and the provider rotates on rate limits —
|
|
81
84
|
# comma-separate (ANTHROPIC_API_KEY="k1,k2") or number them (ANTHROPIC_API_KEY_1, _2).
|
|
@@ -115,6 +118,11 @@ uv run agentkernel tui # full-screen curses terminal UI
|
|
|
115
118
|
uv run agentkernel run "your prompt" # single non-interactive run, prints the answer
|
|
116
119
|
uv run agentkernel run --file task.md # single run from a prompt file
|
|
117
120
|
uv run agentkernel run --background "..." # detached run; output goes to a file
|
|
121
|
+
uv run agentkernel run --image diagram.png "explain this" # attach an image (repeatable; path or URL)
|
|
122
|
+
uv run agentkernel skill list # list discovered skills
|
|
123
|
+
uv run agentkernel skill pack code-review # package a skill into code-review.skill.zip
|
|
124
|
+
uv run agentkernel skill install cr.skill.zip # install a shared skill bundle into skills/
|
|
125
|
+
source <(agentkernel completion bash) # shell completion (bash | zsh | fish)
|
|
118
126
|
uv run agentkernel improve # reflect on the latest trace, write a rule note
|
|
119
127
|
uv run agentkernel eval --suite s.toml # run an eval suite, score answers with a judge
|
|
120
128
|
uv run agentkernel eval --suite s.toml -o report.json # ...and write a JSON report
|
|
@@ -142,13 +150,14 @@ status per turn, and writes a per-session JSONL trace. It supports slash command
|
|
|
142
150
|
$ uv run agentkernel
|
|
143
151
|
[session trace: .agentkernel/traces/<session-id>.jsonl]
|
|
144
152
|
agentkernel REPL - type your message and press enter. Commands: /exit, /clear,
|
|
145
|
-
/system, /profile, /skills, /skill, /tools, /trace, /cost, /memory, /improve.
|
|
153
|
+
/image, /system, /profile, /skills, /skill, /tools, /trace, /cost, /memory, /improve.
|
|
146
154
|
> summarize the files in this directory
|
|
147
155
|
```
|
|
148
156
|
|
|
149
157
|
| Command | Effect |
|
|
150
158
|
|---|---|
|
|
151
159
|
| `/clear` | reset the conversation context |
|
|
160
|
+
| `/image <path-or-url>` | attach an image to the next message (`/image clear` to discard) |
|
|
152
161
|
| `/system [text]` | set (or clear) the system prompt for following turns |
|
|
153
162
|
| `/profile [name]` | show, or load, a profile from `profile_dir` |
|
|
154
163
|
| `/skills` | list discovered skills (`*` = active) |
|
|
@@ -192,9 +201,10 @@ Configuration loads from `agentkernel.toml` (see [`agentkernel.toml.example`](ag
|
|
|
192
201
|
|
|
193
202
|
| Key | Default | Meaning |
|
|
194
203
|
|---|---|---|
|
|
195
|
-
| `provider` | `anthropic` | `anthropic` \| `openai` \| `local` |
|
|
204
|
+
| `provider` | `anthropic` | `anthropic` \| `openai` \| `local` \| `openrouter` \| `deepseek` \| `gemini` |
|
|
196
205
|
| `model` | `claude-sonnet-4-6` | model id for the selected provider |
|
|
197
206
|
| `base_url` | `None` | endpoint for `provider = "local"` |
|
|
207
|
+
| `local_supports_images` | `False` | opt in to sending images to a vision-capable `local` endpoint (Anthropic/OpenAI accept images automatically) |
|
|
198
208
|
| `max_output_tokens` | `4096` | reply token cap |
|
|
199
209
|
| `output_reserve` | `8192` | budget headroom reserved for the reply |
|
|
200
210
|
| `max_iterations` | `25` | loop guard against runaway sessions |
|
|
@@ -218,6 +228,9 @@ Configuration loads from `agentkernel.toml` (see [`agentkernel.toml.example`](ag
|
|
|
218
228
|
| `enable_memory_tools` | `False` | register `remember`/`recall`/`forget` tools |
|
|
219
229
|
| `memory_auto_context` / `memory_auto_context_limit` | `False` / `3` | auto-inject recalled notes before each user message |
|
|
220
230
|
| `memory_store_budget` | `None` | summarize older turns before persisting memory |
|
|
231
|
+
| `memory_scope` | `None` | recall namespace: `auto` (project dir name), a literal name, or off; recall returns global + active-namespace notes |
|
|
232
|
+
| `memory_recency_weight` / `memory_importance_weight` | `0.0` / `0.0` | opt-in recall re-ranking: boost notes by recency of creation and by recall frequency (`0` = relevance only) |
|
|
233
|
+
| `memory_half_life_days` | `30.0` | days for a note's recency score to halve |
|
|
221
234
|
| `memory_curator_model` | `None` | cheap model for `memory extract`/`consolidate` (falls back to `summarizer_model`/`model`) |
|
|
222
235
|
| `semantic_search` | `False` | rank note recall with dense embeddings (SQLite only) |
|
|
223
236
|
| `semantic_search_lsh_bits` | `None` | approximate vector index bits; omit for brute force |
|
|
@@ -271,12 +284,13 @@ MCP servers are declared separately as `[[mcp_servers]]` tables (see [MCP](#mcp-
|
|
|
271
284
|
|
|
272
285
|
### Providers ([`providers/`](agentkernel/providers))
|
|
273
286
|
|
|
274
|
-
Hand-written `httpx` adapters for **Anthropic** (Messages API), **OpenAI** (Chat Completions), and **local** (OpenAI-compatible: Ollama, vLLM, LM Studio). Each adapter:
|
|
287
|
+
Hand-written `httpx` adapters for **Anthropic** (Messages API), **OpenAI** (Chat Completions), and **local** (OpenAI-compatible: Ollama, vLLM, LM Studio). **OpenRouter**, **DeepSeek**, and **Gemini** ship as thin named adapters over the OpenAI shape — each just pins a default `base_url` and `*_API_KEY` env var, so `provider = "gemini"` works without hand-wiring an endpoint. Each adapter:
|
|
275
288
|
|
|
276
289
|
- translates canonical messages/tools to the provider's exact wire shape and back,
|
|
277
290
|
- handles the **tool-result pairing** fan-out (Anthropic: all results in one `user` message of `tool_result` blocks; OpenAI: one `role:"tool"` message per result),
|
|
278
291
|
- reports cache read/write token counts where available,
|
|
279
|
-
- applies cache markers on the stable prefix (Anthropic `cache_control: ephemeral`)
|
|
292
|
+
- applies cache markers on the stable prefix (Anthropic `cache_control: ephemeral`),
|
|
293
|
+
- translates **image input** (`Message.images`) to each wire shape (Anthropic `image` blocks, OpenAI/local `image_url` parts), gated by `Provider.supports_images` so a text-only provider drops images instead of erroring.
|
|
280
294
|
|
|
281
295
|
Translation is implemented as **pure functions** separate from the HTTP call, which is what makes adapter behavior testable offline. The adapters share one `httpx` transport ([`providers/_http.py`](agentkernel/providers/_http.py)) that retries transient failures (timeouts and `429`/`5xx`), honoring a server `Retry-After` header (bounded) when present, and raises `ProviderError` only once retries are exhausted.
|
|
282
296
|
|
|
@@ -335,7 +349,7 @@ These are implemented on top of the kernel using the three primitives — a tool
|
|
|
335
349
|
|
|
336
350
|
- **Profiles** ([`profiles.py`](agentkernel/profiles.py)) — a run parameter `(system_prompt, tool_filter, model_override, rubric)` loaded from `profiles/<name>.toml`. The loop honors `system_prompt` and `tool_filter`; CLI `--profile` sets the active profile, and a profile's `model_override` or `rubric` override the defaults.
|
|
337
351
|
- **Skills** ([`skills.py`](agentkernel/skills.py)) — [Anthropic-style](https://github.com/anthropics/skills) `SKILL.md` folders (YAML frontmatter `name`/`description` + body + bundled files) discovered from `skills_dir`, with **progressive disclosure**: only a name+description catalog sits in the (stable, assembled-once) prefix; the model loads a skill's full body + file listing on demand via the `use_skill` tool. A skill can also be *pinned* (`skills = [...]`, `--skill <name>`, or `/skill <name>`) to force its body into the prefix. Loose `.md`/`.toml` skills still work.
|
|
338
|
-
- **Memory** ([`memory.py`](agentkernel/memory.py), [`semantic_memory.py`](agentkernel/semantic_memory.py)) — a `MemoryStore` loaded before a run and saved after; ships with in-memory, JSONL, and SQLite/FTS5 stores. Enable with `memory_store`. The SQLite notebook supports optional `semantic_search` via an OpenAI-compatible embedding endpoint, cosine-ranked recall, and a standard-library-only approximate LSH index (`semantic_search_lsh_bits`) for large notebooks. The `reindex_memory` tool backfills embeddings when a notebook is promoted to semantic recall.
|
|
352
|
+
- **Memory** ([`memory.py`](agentkernel/memory.py), [`semantic_memory.py`](agentkernel/semantic_memory.py)) — a `MemoryStore` loaded before a run and saved after; ships with in-memory, JSONL, and SQLite/FTS5 stores. Enable with `memory_store`. The SQLite notebook supports optional `semantic_search` via an OpenAI-compatible embedding endpoint, cosine-ranked recall, and a standard-library-only approximate LSH index (`semantic_search_lsh_bits`) for large notebooks. The `reindex_memory` tool backfills embeddings when a notebook is promoted to semantic recall. Recall can optionally be re-ranked by note age and recall frequency (`memory_recency_weight`/`memory_importance_weight`/`memory_half_life_days`) using the `access_count` metadata the notebook already tracks; with the default zero weights, ordering is pure relevance. A single notebook can also be partitioned into namespaces with `memory_scope` (e.g. `auto` for a per-project namespace): recall returns global notes plus the active namespace's, and new notes are stamped with it — keeping one "global brain" file while letting project-specific facts stay project-local. Within a scoped run the model can still pin a universal fact with `remember(global=true)`, which writes it to the empty scope so it's recalled everywhere.
|
|
339
353
|
- **Knowledge graph** ([`knowledge.py`](agentkernel/knowledge.py)) — a file-backed triple store exposed purely as `graph_add`, `graph_query`, `graph_neighbors`, `graph_path`, and `graph_stats` tools (`enable_graph = true`). The kernel keeps no graph state.
|
|
340
354
|
- **Loops** ([`loops.py`](agentkernel/loops.py)) — [loop-engineering](https://signals.forwardfuture.ai/loop-library/) workflows: `agentkernel loop` re-runs the agent on a loop's prompt until a stopping condition (a success shell-check and/or an N-in-a-row streak), following **action → check → iterate → stop**. Loops load from TOML or from a skill body (`--skill`), and the success check runs in the sandbox so a loop can verify its own work (e.g. "fix until `pytest` is green").
|
|
341
355
|
- **Self-improvement** ([`improvement.py`](agentkernel/improvement.py)) — `agentkernel improve` or the REPL's `/improve` reads a session trace and asks the model for one concrete rule, written to `improvements_dir`. This is why telemetry exists from turn one.
|
|
@@ -45,6 +45,9 @@ API keys are read **only** from the environment — never from config files or t
|
|
|
45
45
|
```bash
|
|
46
46
|
export ANTHROPIC_API_KEY=*** # for provider = "anthropic"
|
|
47
47
|
export OPENAI_API_KEY=*** # for provider = "openai" / embeddings
|
|
48
|
+
export OPENROUTER_API_KEY=*** # for provider = "openrouter"
|
|
49
|
+
export DEEPSEEK_API_KEY=*** # for provider = "deepseek"
|
|
50
|
+
export GEMINI_API_KEY=*** # for provider = "gemini"
|
|
48
51
|
# local/OpenAI-compatible endpoints (Ollama, vLLM) usually need no key
|
|
49
52
|
# Credential pool: give several keys and the provider rotates on rate limits —
|
|
50
53
|
# comma-separate (ANTHROPIC_API_KEY="k1,k2") or number them (ANTHROPIC_API_KEY_1, _2).
|
|
@@ -84,6 +87,11 @@ uv run agentkernel tui # full-screen curses terminal UI
|
|
|
84
87
|
uv run agentkernel run "your prompt" # single non-interactive run, prints the answer
|
|
85
88
|
uv run agentkernel run --file task.md # single run from a prompt file
|
|
86
89
|
uv run agentkernel run --background "..." # detached run; output goes to a file
|
|
90
|
+
uv run agentkernel run --image diagram.png "explain this" # attach an image (repeatable; path or URL)
|
|
91
|
+
uv run agentkernel skill list # list discovered skills
|
|
92
|
+
uv run agentkernel skill pack code-review # package a skill into code-review.skill.zip
|
|
93
|
+
uv run agentkernel skill install cr.skill.zip # install a shared skill bundle into skills/
|
|
94
|
+
source <(agentkernel completion bash) # shell completion (bash | zsh | fish)
|
|
87
95
|
uv run agentkernel improve # reflect on the latest trace, write a rule note
|
|
88
96
|
uv run agentkernel eval --suite s.toml # run an eval suite, score answers with a judge
|
|
89
97
|
uv run agentkernel eval --suite s.toml -o report.json # ...and write a JSON report
|
|
@@ -111,13 +119,14 @@ status per turn, and writes a per-session JSONL trace. It supports slash command
|
|
|
111
119
|
$ uv run agentkernel
|
|
112
120
|
[session trace: .agentkernel/traces/<session-id>.jsonl]
|
|
113
121
|
agentkernel REPL - type your message and press enter. Commands: /exit, /clear,
|
|
114
|
-
/system, /profile, /skills, /skill, /tools, /trace, /cost, /memory, /improve.
|
|
122
|
+
/image, /system, /profile, /skills, /skill, /tools, /trace, /cost, /memory, /improve.
|
|
115
123
|
> summarize the files in this directory
|
|
116
124
|
```
|
|
117
125
|
|
|
118
126
|
| Command | Effect |
|
|
119
127
|
|---|---|
|
|
120
128
|
| `/clear` | reset the conversation context |
|
|
129
|
+
| `/image <path-or-url>` | attach an image to the next message (`/image clear` to discard) |
|
|
121
130
|
| `/system [text]` | set (or clear) the system prompt for following turns |
|
|
122
131
|
| `/profile [name]` | show, or load, a profile from `profile_dir` |
|
|
123
132
|
| `/skills` | list discovered skills (`*` = active) |
|
|
@@ -161,9 +170,10 @@ Configuration loads from `agentkernel.toml` (see [`agentkernel.toml.example`](ag
|
|
|
161
170
|
|
|
162
171
|
| Key | Default | Meaning |
|
|
163
172
|
|---|---|---|
|
|
164
|
-
| `provider` | `anthropic` | `anthropic` \| `openai` \| `local` |
|
|
173
|
+
| `provider` | `anthropic` | `anthropic` \| `openai` \| `local` \| `openrouter` \| `deepseek` \| `gemini` |
|
|
165
174
|
| `model` | `claude-sonnet-4-6` | model id for the selected provider |
|
|
166
175
|
| `base_url` | `None` | endpoint for `provider = "local"` |
|
|
176
|
+
| `local_supports_images` | `False` | opt in to sending images to a vision-capable `local` endpoint (Anthropic/OpenAI accept images automatically) |
|
|
167
177
|
| `max_output_tokens` | `4096` | reply token cap |
|
|
168
178
|
| `output_reserve` | `8192` | budget headroom reserved for the reply |
|
|
169
179
|
| `max_iterations` | `25` | loop guard against runaway sessions |
|
|
@@ -187,6 +197,9 @@ Configuration loads from `agentkernel.toml` (see [`agentkernel.toml.example`](ag
|
|
|
187
197
|
| `enable_memory_tools` | `False` | register `remember`/`recall`/`forget` tools |
|
|
188
198
|
| `memory_auto_context` / `memory_auto_context_limit` | `False` / `3` | auto-inject recalled notes before each user message |
|
|
189
199
|
| `memory_store_budget` | `None` | summarize older turns before persisting memory |
|
|
200
|
+
| `memory_scope` | `None` | recall namespace: `auto` (project dir name), a literal name, or off; recall returns global + active-namespace notes |
|
|
201
|
+
| `memory_recency_weight` / `memory_importance_weight` | `0.0` / `0.0` | opt-in recall re-ranking: boost notes by recency of creation and by recall frequency (`0` = relevance only) |
|
|
202
|
+
| `memory_half_life_days` | `30.0` | days for a note's recency score to halve |
|
|
190
203
|
| `memory_curator_model` | `None` | cheap model for `memory extract`/`consolidate` (falls back to `summarizer_model`/`model`) |
|
|
191
204
|
| `semantic_search` | `False` | rank note recall with dense embeddings (SQLite only) |
|
|
192
205
|
| `semantic_search_lsh_bits` | `None` | approximate vector index bits; omit for brute force |
|
|
@@ -240,12 +253,13 @@ MCP servers are declared separately as `[[mcp_servers]]` tables (see [MCP](#mcp-
|
|
|
240
253
|
|
|
241
254
|
### Providers ([`providers/`](agentkernel/providers))
|
|
242
255
|
|
|
243
|
-
Hand-written `httpx` adapters for **Anthropic** (Messages API), **OpenAI** (Chat Completions), and **local** (OpenAI-compatible: Ollama, vLLM, LM Studio). Each adapter:
|
|
256
|
+
Hand-written `httpx` adapters for **Anthropic** (Messages API), **OpenAI** (Chat Completions), and **local** (OpenAI-compatible: Ollama, vLLM, LM Studio). **OpenRouter**, **DeepSeek**, and **Gemini** ship as thin named adapters over the OpenAI shape — each just pins a default `base_url` and `*_API_KEY` env var, so `provider = "gemini"` works without hand-wiring an endpoint. Each adapter:
|
|
244
257
|
|
|
245
258
|
- translates canonical messages/tools to the provider's exact wire shape and back,
|
|
246
259
|
- handles the **tool-result pairing** fan-out (Anthropic: all results in one `user` message of `tool_result` blocks; OpenAI: one `role:"tool"` message per result),
|
|
247
260
|
- reports cache read/write token counts where available,
|
|
248
|
-
- applies cache markers on the stable prefix (Anthropic `cache_control: ephemeral`)
|
|
261
|
+
- applies cache markers on the stable prefix (Anthropic `cache_control: ephemeral`),
|
|
262
|
+
- translates **image input** (`Message.images`) to each wire shape (Anthropic `image` blocks, OpenAI/local `image_url` parts), gated by `Provider.supports_images` so a text-only provider drops images instead of erroring.
|
|
249
263
|
|
|
250
264
|
Translation is implemented as **pure functions** separate from the HTTP call, which is what makes adapter behavior testable offline. The adapters share one `httpx` transport ([`providers/_http.py`](agentkernel/providers/_http.py)) that retries transient failures (timeouts and `429`/`5xx`), honoring a server `Retry-After` header (bounded) when present, and raises `ProviderError` only once retries are exhausted.
|
|
251
265
|
|
|
@@ -304,7 +318,7 @@ These are implemented on top of the kernel using the three primitives — a tool
|
|
|
304
318
|
|
|
305
319
|
- **Profiles** ([`profiles.py`](agentkernel/profiles.py)) — a run parameter `(system_prompt, tool_filter, model_override, rubric)` loaded from `profiles/<name>.toml`. The loop honors `system_prompt` and `tool_filter`; CLI `--profile` sets the active profile, and a profile's `model_override` or `rubric` override the defaults.
|
|
306
320
|
- **Skills** ([`skills.py`](agentkernel/skills.py)) — [Anthropic-style](https://github.com/anthropics/skills) `SKILL.md` folders (YAML frontmatter `name`/`description` + body + bundled files) discovered from `skills_dir`, with **progressive disclosure**: only a name+description catalog sits in the (stable, assembled-once) prefix; the model loads a skill's full body + file listing on demand via the `use_skill` tool. A skill can also be *pinned* (`skills = [...]`, `--skill <name>`, or `/skill <name>`) to force its body into the prefix. Loose `.md`/`.toml` skills still work.
|
|
307
|
-
- **Memory** ([`memory.py`](agentkernel/memory.py), [`semantic_memory.py`](agentkernel/semantic_memory.py)) — a `MemoryStore` loaded before a run and saved after; ships with in-memory, JSONL, and SQLite/FTS5 stores. Enable with `memory_store`. The SQLite notebook supports optional `semantic_search` via an OpenAI-compatible embedding endpoint, cosine-ranked recall, and a standard-library-only approximate LSH index (`semantic_search_lsh_bits`) for large notebooks. The `reindex_memory` tool backfills embeddings when a notebook is promoted to semantic recall.
|
|
321
|
+
- **Memory** ([`memory.py`](agentkernel/memory.py), [`semantic_memory.py`](agentkernel/semantic_memory.py)) — a `MemoryStore` loaded before a run and saved after; ships with in-memory, JSONL, and SQLite/FTS5 stores. Enable with `memory_store`. The SQLite notebook supports optional `semantic_search` via an OpenAI-compatible embedding endpoint, cosine-ranked recall, and a standard-library-only approximate LSH index (`semantic_search_lsh_bits`) for large notebooks. The `reindex_memory` tool backfills embeddings when a notebook is promoted to semantic recall. Recall can optionally be re-ranked by note age and recall frequency (`memory_recency_weight`/`memory_importance_weight`/`memory_half_life_days`) using the `access_count` metadata the notebook already tracks; with the default zero weights, ordering is pure relevance. A single notebook can also be partitioned into namespaces with `memory_scope` (e.g. `auto` for a per-project namespace): recall returns global notes plus the active namespace's, and new notes are stamped with it — keeping one "global brain" file while letting project-specific facts stay project-local. Within a scoped run the model can still pin a universal fact with `remember(global=true)`, which writes it to the empty scope so it's recalled everywhere.
|
|
308
322
|
- **Knowledge graph** ([`knowledge.py`](agentkernel/knowledge.py)) — a file-backed triple store exposed purely as `graph_add`, `graph_query`, `graph_neighbors`, `graph_path`, and `graph_stats` tools (`enable_graph = true`). The kernel keeps no graph state.
|
|
309
323
|
- **Loops** ([`loops.py`](agentkernel/loops.py)) — [loop-engineering](https://signals.forwardfuture.ai/loop-library/) workflows: `agentkernel loop` re-runs the agent on a loop's prompt until a stopping condition (a success shell-check and/or an N-in-a-row streak), following **action → check → iterate → stop**. Loops load from TOML or from a skill body (`--skill`), and the success check runs in the sandbox so a loop can verify its own work (e.g. "fix until `pytest` is green").
|
|
310
324
|
- **Self-improvement** ([`improvement.py`](agentkernel/improvement.py)) — `agentkernel improve` or the REPL's `/improve` reads a session trace and asks the model for one concrete rule, written to `improvements_dir`. This is why telemetry exists from turn one.
|
|
@@ -612,13 +612,13 @@ within each group is rough priority. Nothing here is committed.
|
|
|
612
612
|
| **Credential pools / key rotation** ✅ | provider | **Done** (`providers/credentials.py`). Keys are read from one env var (comma-separated) plus numbered siblings `<VAR>_1..N`; `post_json_pooled` rotates to the next key on a `RateLimitError` (429 after retries), marking the spent one exhausted. A single key is a pool of one, so existing setups are unchanged. Invisible to the loop. |
|
|
613
613
|
| **Reasoning-effort run parameter** ✅ | run param | **Done** (`Profile.reasoning`, plumbed through `run` → `complete`). OpenAI gets `reasoning_effort`; Anthropic maps low/medium/high to an extended-thinking budget (capped under `max_tokens`); local omits it (arbitrary endpoints may reject it); others ignore it. |
|
|
614
614
|
| **Model router for auxiliary work** | config | Generalize today's single `summarizer_model` into named roles (summarize / judge / classify-risk) so compaction, evals, and `smart` approval can each pick a cheap model. *(Deferred — separate `summarizer_model` / `judge_model` / `approval_judge_model` already exist; unifying them is a refactor, not new capability.)* |
|
|
615
|
-
| **More first-class adapters** | provider |
|
|
615
|
+
| **More first-class adapters** ✅ | provider | **Done** (`providers/compat.py`: `OpenRouterProvider`, `DeepSeekProvider`, `GeminiProvider`). All three speak the OpenAI shape, so each is a thin `OpenAIProvider` subclass pinning a default `base_url`, `*_API_KEY` env var, and capability (Gemini/OpenRouter multimodal; DeepSeek text-only) — no new wire translation. Wired into `make_provider` (`provider = "openrouter"|"deepseek"|"gemini"`), `base_url` still overridable. |
|
|
616
616
|
|
|
617
|
-
### 18.6 Multimodality
|
|
617
|
+
### 18.6 Multimodality — ✅ done
|
|
618
618
|
|
|
619
619
|
| Idea | Seam | Notes |
|
|
620
620
|
|---|---|---|
|
|
621
|
-
| **Image input in canonical messages** | canonical types |
|
|
621
|
+
| **Image input in canonical messages** ✅ | canonical types | **Done** (`types.ImageContent`, `Message.images`). Images ride alongside `Message.content` as typed parts (rather than turning `content` into a union, so every existing `m.content` reader is untouched). Each adapter translates them: Anthropic `image` blocks (base64/url `source`), OpenAI/local `image_url` content parts (data-URI or url). Gated by `Provider.supports_images` — Anthropic/OpenAI `True`, `local` opt-in via `local_supports_images` for vision endpoints — so a text-only provider silently drops images instead of breaking. `ImageContent.from_path`/`from_url`, a flat per-image token charge in the estimator (§9.1), and `agentkernel run --image PATH_OR_URL` (repeatable; warns if the provider can't accept images). Loop untouched. |
|
|
622
622
|
|
|
623
623
|
### 18.7 Observability & DX — ✅ done (completions deferred)
|
|
624
624
|
|
|
@@ -627,7 +627,7 @@ within each group is rough priority. Nothing here is committed.
|
|
|
627
627
|
| **`insights` command** ✅ | reads telemetry | **Done** (`insights.py`, `agentkernel insights [--days N]`). Aggregates the JSONL traces into sessions/turns/tokens/cost, a per-model breakdown, and tool-frequency with error counts — pure reading of the stable §12 schema, offline. |
|
|
628
628
|
| **`doctor` command** ✅ | standalone | **Done** (`doctor.py`, `agentkernel doctor`). Network-free checks: Python version, required deps, provider credentials, sandbox (docker CLI), embedding key for semantic search, curses on Windows, writable log dir. Exits non-zero on any failure. |
|
|
629
629
|
| **Plugin discovery seam** ✅ | tool registration | **Done** (`plugins.py`, `enable_plugins`/`plugins_dir`). Auto-imports `plugins/*.py` exposing `tools()` or `TOOLS`; each spec registers like a builtin. A bad plugin is reported, not fatal. Opt-in because importing executes code. |
|
|
630
|
-
| **Shell completions** | CLI |
|
|
630
|
+
| **Shell completions** ✅ | CLI | **Done** (`run_completion`, `agentkernel completion bash|zsh|fish`). Emits a static completion script that completes the subcommand at the first position and defers to file completion after; the command list is read from the live argparse subparsers so it never drifts from the real commands. |
|
|
631
631
|
|
|
632
632
|
### 18.8 Bundled content & templates (assets, not engine work) — ✅ done
|
|
633
633
|
|
|
@@ -654,7 +654,7 @@ future work.
|
|
|
654
654
|
| **A starter loops library** | external driver | Like skills and profiles, the loop runner (`loops.py`, `agentkernel loop`) is built and tested but ships only one example (`examples/loops/until-tests-pass.toml`). Add a curated `loops/` set following action → check → iterate → stop, each with a sandboxed `success_check` and/or `success_streak`: `until-tests-pass` (promote the example), `until-lint-clean`, `until-typecheck-clean`, `until-build-succeeds`, `review-and-fix` (run a review, apply fixes, repeat until clean). Pure TOML over the existing runner — no engine change. |
|
|
655
655
|
| **Reusable templates** | DX / scaffolding | A `templates/` directory of annotated skeletons for the things users author repeatedly: `SKILL.md`, a profile TOML, an eval suite, a loop TOML, an `[[mcp_servers]]` block, and a builtin-style tool module. Each is a copy-paste starting point with inline comments explaining every field. |
|
|
656
656
|
| **`new` scaffolding command** | CLI / DX | `agentkernel new skill <name>` / `new profile <name>` / `new eval <name>` / `new loop <name>` copies the matching template into place with the name filled in. Turns the templates above into a one-liner; pure file generation, no loop involvement. |
|
|
657
|
-
| **A shareable bundle format** | packaging |
|
|
657
|
+
| **A shareable bundle format** ✅ | packaging | **Done** (`skills.pack_skill`/`install_skill`, `agentkernel skill pack/install/list`). A bundle is just a zip with `SKILL.md` (+ any resources) at the root — the same folder shape `SkillLibrary` discovers, so a packed skill installs straight back with no manifest format to learn. Install names the directory from the skill's own frontmatter `name` (robust to a renamed archive) and rejects unsafe members (absolute/`..` paths) and any zip lacking a root `SKILL.md`. |
|
|
658
658
|
|
|
659
659
|
### 18.9 Explicitly out of the kernel (separate packages)
|
|
660
660
|
|
|
@@ -67,13 +67,15 @@ class Agent:
|
|
|
67
67
|
*,
|
|
68
68
|
profile: Any | None = None,
|
|
69
69
|
on_text: Any | None = None,
|
|
70
|
+
images: Any | None = None,
|
|
70
71
|
) -> str:
|
|
71
72
|
"""Drive the loop until a final answer or the max-iteration guard.
|
|
72
73
|
|
|
73
74
|
``profile`` (design §13, Phase 5) is accepted but, in the kernel, only
|
|
74
75
|
``tool_filter`` / ``system_prompt`` are honored if trivially present.
|
|
75
76
|
``on_text`` (when set) receives streamed text deltas; the loop contract is
|
|
76
|
-
otherwise unchanged.
|
|
77
|
+
otherwise unchanged. ``images`` (a list of ``ImageContent``) attaches to
|
|
78
|
+
the user turn; adapters that can't accept images ignore them (§18.6).
|
|
77
79
|
"""
|
|
78
80
|
session_id = getattr(self.telemetry, "session_id", str(uuid.uuid4()))
|
|
79
81
|
|
|
@@ -84,7 +86,11 @@ class Agent:
|
|
|
84
86
|
self.context.add(message)
|
|
85
87
|
|
|
86
88
|
self.context.add(
|
|
87
|
-
Message(
|
|
89
|
+
Message(
|
|
90
|
+
role="user",
|
|
91
|
+
content=self._prepare_user_message(user_input),
|
|
92
|
+
images=list(images) if images else [],
|
|
93
|
+
)
|
|
88
94
|
)
|
|
89
95
|
|
|
90
96
|
# Assemble the cacheable prefix ONCE per run and reuse the same objects
|