Files
yvgude--lean-ctx/docs/rfcs/sdk-embedding-v1.md
T
wehub-resource-sync 26382a7ac6
CI / Clippy (push) Failing after 15m13s
CI / Test (ubuntu-latest) (push) Failing after 16m1s
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Build (no embeddings / no ORT) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Cookbook (Node) (push) Has been cancelled
CI / Pi Extension (Node) (push) Has been cancelled
CI / Rust SDK (lean-ctx-client) (push) Has been cancelled
CI / Embed SDK (lean-ctx-sdk) (push) Has been cancelled
CI / Python SDK (leanctx) (push) Has been cancelled
CI / Hermes Plugin (Python) (push) Has been cancelled
CI / SDK Conformance Matrix (push) Has been cancelled
CI / Coverage (push) Has been cancelled
CI / cargo-deny (push) Has been cancelled
CI / Adversarial Safety (push) Has been cancelled
CI / Benchmarks (push) Has been cancelled
CI / Output-Quality Gate (eval A/B) (push) Has been cancelled
CI / Documentation (push) Has been cancelled
CI / CI Green (push) Has been cancelled
JetBrains Plugin / Actionlint (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (rust) (push) Has been cancelled
JetBrains Plugin / Validation (push) Has been cancelled
JetBrains Plugin / Build (push) Has been cancelled
JetBrains Plugin / Test (push) Has been cancelled
Security Check / Security Scan (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:35:30 +08:00

127 lines
6.1 KiB
Markdown

# RFC: lean-ctx SDK / Embedding (v1)
Status: **accepted, increment 1 implemented**
Crate: `rust/crates/lean-ctx-sdk` (`lean_ctx_sdk`)
Related plans: *lean-ctx SDK Embedding*, *lean-ctx Developer Platform* (Track A)
## Problem
The Addon system lets the engine call *your* tool (out-of-process, no access to
internals). The opposite need — **consume lean-ctx as an embedded engine** — has
no supported surface. Lean-md is the driving case: it calls engine cores
in-process with a **shared `SessionCache`** so a read → re-read produces a token
delta. Going through `lean_ctx::core::…` directly couples every consumer to
internal churn.
## Goals
1. A small, **stable Rust façade** with its own types (`Engine`, `ReadMode`,
`Output`, `Error`) — engine internals can change without breaking embedders.
2. **Shared session cache** so the in-process read→re-read delta works (the
acceptance property).
3. **Safe by default**: PathJail on, scoped state dir, auto-update off,
write/exec behind explicit opt-in, no forced global allocator.
4. **No new mechanism**: dispatch the *real* registered tools, exactly as the
MCP server does — zero behavioural drift.
## Non-goals (v1)
- Async API. v1 is synchronous (owns a multi-thread runtime, dispatches via the
blocking pool like the server). Async wrappers can come later.
- Re-exporting global mutations (`Config::update_global`, install/uninstall).
- A feature-minimal engine build. The engine references `proxy`/`http_server`/
`ort` unconditionally today, so the SDK pins **default-minus-jemalloc** rather
than a hand-cut feature set. `tree-sitter` stays on (AST read modes).
## Design
### The `Engine`
`Engine` owns: the resolved project root (the PathJail root), a shared
`Arc<RwLock<SessionCache>>`, a shared `Arc<RwLock<SessionState>>`, the full
`ToolRegistry` (`build_registry()`), and a multi-threaded Tokio runtime.
Each call builds a `ToolContext` wired to the shared cache/session and dispatches
the tool via `spawn_blocking(move || tool.handle(&args, &ctx))` — the exact path
`LeanCtxServer` uses, so `ctx_read`'s `Handle::block_on` and `ctx_search`'s
`block_in_place` are both legal.
### Own types
| Façade type | Wraps |
|-------------|-------|
| `Engine` / `EngineBuilder` | registry + shared cache/session + runtime |
| `ReadMode` | the engine `mode` string (`auto`/`full`/`signatures`/`lines:N-M`/…) |
| `Output` | `ToolOutput` (text + token accounting), derives `Debug`/`Clone` |
| `Error` | `rmcp::ErrorData` + jail/permission/init errors |
### Safe-by-default
`EngineBuilder::build()` resolves + validates the project root, sets the engine's
data/config/state/cache dirs to a scoped temp dir (unless `.data_dir(…)`),
disables the update check, and constructs the runtime. Write tools (`ctx_edit`,
`ctx_fill`) and exec tools (`ctx_shell`, `ctx_execute`, `shell`) return
`Error::NotPermitted` unless `.allow_write(true)` / `.allow_exec(true)`.
## Surface map (~26 capabilities)
dasTholo's Lean-md uses ~26 engine capabilities. v1 ships ergonomic typed
methods for the read-mostly core and an escape hatch (`Engine::call`) that
reaches **every** registered tool (write/exec gated). The table tracks how each
capability is served today.
| Capability | Engine tool | v1 surface |
|------------|-------------|-----------|
| read | `ctx_read` | **typed** `read()` |
| search | `ctx_search` | **typed** `search()` |
| symbol | `ctx_symbol` | **typed** `symbol()` |
| outline | `ctx_outline` | **typed** `outline()` |
| tree / repomap | `ctx_tree` / `ctx_repomap` | **typed** `tree()` · `call()` |
| find | `ctx_glob` | `call("ctx_glob", …)` |
| count | `ctx_cost` / `tokens` | `tokens::count` · `call()` |
| graph | `ctx_graph` | `call("ctx_graph", …)` |
| callgraph | `ctx_callgraph` | `call("ctx_callgraph", …)` |
| impact | `ctx_impact` | `call("ctx_impact", …)` |
| architecture | `ctx_architecture` | `call("ctx_architecture", …)` |
| smells | `ctx_smells` | `call("ctx_smells", …)` |
| refactor | `ctx_refactor` | `call("ctx_refactor", …)` |
| review | `ctx_review` | `call("ctx_review", …)` |
| recall / remember | `ctx_knowledge` | `call("ctx_knowledge", …)` |
| query (semantic) | `ctx_semantic_search` | `call("ctx_semantic_search", …)` |
| render / compose | `ctx_compose` / `ctx_overview` | `call(…)` |
| inspect / list (tools) | `ctx_tools` | `call("ctx_tools", …)` |
| include / addressing | `ctx_read` (`lines:`/paths) | `read()` |
| reformat / compress | shell pattern engine | `compress::shell_output` |
| date / env / routes | `ctx_routes` etc. | `call(…)` |
| edit | `ctx_edit` | `call()` **(needs `allow_write`)** |
| shell / exec | `ctx_shell` / `ctx_execute` | `call()` **(needs `allow_exec`)** |
| hash | engine hash | `hash::blake3_*` |
| addon authoring/audit | scaffold + audit gate | `addon::scaffold/audit` |
Promoting a `call()`-served capability to a typed method is additive and
semver-safe; the plan is to graduate them as the Lean-md port exercises them.
## Acceptance
- In-repo: `tests/engine_read.rs` proves read→re-read saves ≥ the first read,
PathJail rejects escapes, search finds symbols, and the write/exec gate +
unknown-tool paths error correctly. `examples/embed.rs` shows a live ~99%
re-read delta on `Cargo.toml`.
- External: dasTholo ports Lean-md onto the `Engine` — the real acceptance test
that the surface is sufficient (tracked on the GitLab epic).
## Distribution & trust
The SDK is a **build** substrate; **distribution stays the Addon system**. A
binary built with the SDK and shipped as an addon still runs under the gateway's
OS sandbox + output redaction + trust/signing — embedding does not weaken
distribution security. Two trust contexts: (1) distributed as an addon =
sandboxed; (2) run standalone = the embedder owns the boundary, like any binary.
## Build reality (honest)
Full parity keeps `tree-sitter` on (AST modes are intrinsically costly).
Embedders needing only read/search/knowledge can later get a lighter path once
the engine compiles under a minimal feature set (today it does not). A
`lean-ctx-crypto`/ML split is deferred until measured build numbers justify it.