linch
An embeddable agent harness for context-heavy Python applications.
linch is an async-first, event-driven Python SDK for building agent loops inside your own application.
Linch 2.0 is intentionally an SDK rather than a coding-agent product: a bare
Agent is domain-neutral and has no implicit workspace or shell authority.
Choose a tool registry or preset explicitly. Upgrading from 1.x? Read the
2.0 migration guide.
It gives you the runtime pieces that production agents usually need but simple tool-calling wrappers leave to you: streamed events, tool scheduling, context/RAG, tiered memory, virtual filesystem offloading, structured outputs, permissions, retries, provider adapters, cost tracking, evals, MCP, skills, and subagents.
Use linch when you want to build your own domain workflow — not force it into a hosted assistant, a rigid graph DSL, or a black-box multi-agent abstraction.
Why linch?
Most agent frameworks start from one of three ideas:
- agents as chat loops — simple, but hard to control once tools, context, memory, and side effects grow;
- agents as graphs — explicit and durable, but sometimes too heavy for application-level workflows;
- agents as teams — useful for collaboration patterns, but not always the right abstraction for context-heavy product features.
linch starts from a different idea:
An agent is a runtime you embed, observe, constrain, and feed with the right context.
That means linch focuses on the boring-but-critical layer between your app and the model:
- Context control — build RAG/context per turn before the model sees it.
- Tool control — schedule tools with resource awareness, timeout, retry, and permission checks.
- State control — keep sessions, memory, files, citations, and large outputs manageable.
- Provider control — run against different model providers without rewriting your workflow.
What you can build
linch is designed for application developers building agentic features such as:
- document and report assistants;
- RAG systems over large internal knowledge bases;
- coding and software-engineering agents;
- extraction and synthesis workflows;
- long-running research assistants;
- local/offline agent demos;
- tools that need citations, permissions, filesystem state, or large-result offloading.
Core features
| Area | What linch provides |
|---|---|
| Agent loop | Agent, Session, async event streaming, loop guards |
| Tools | Duck-typed tools, runtime registries, parallel scheduling, resource locks, local or Docker-backed Bash execution |
| Reliability | Per-tool timeout, opt-in retry, structured tool errors, recovery hints |
| Context | ContextBuilder, RAG context injection, context budgets, compaction ladder (micro-compact, reactive recovery, circuit breaker) |
| Memory | Search/upsert tools, flat and tiered memory stores, persistent memory examples |
| Filesystem | Virtual filesystem, automatic large-result offloading, disk/state/sqlite backends |
| Safety | Permission engine, tool/path/bash rules, dangerous-action skipping |
| Budgets | RunBudget token/USD caps shared across the subagent tree, warning + exceeded events, graceful stop |
| Workflows | Deterministic fleet loops: wf.agent/parallel/pipeline/phase, journaled resume, shared budgets |
| Providers | OpenAI Responses, OpenAI Chat Completions, Anthropic, Gemini, llama.cpp, OpenAI-compatible endpoints, pluggable providers |
| Extensibility | Python hooks, MCP, skills, subagents (fork/continue, background workers, coordinator mode), observer compatibility |
| Outputs | Structured output schemas, citations, usage/cost events, metadata-rich tool results, run reports |
Architecture
flowchart LR
App[Your application] --> Session[Session]
Session --> Loop[Agent loop]
Loop --> Provider[Model provider]
Loop --> Scheduler[Tool scheduler]
Loop --> Context[Context builder]
Loop --> Memory[Memory]
Loop --> FS[Virtual filesystem]
Loop --> Permissions[Permission engine]
Context --> RAG[RAG / retrieval]
Scheduler --> Tools[Tools]
Tools --> FS
Tools --> Citations[Citations & metadata]
Loop --> Events[Streaming events]
Events --> UI[CLI / Web UI / API]
linch keeps the loop small, but makes the surrounding runtime explicit: context, tools, permissions, state, files, memory, and events are all first-class parts of the system.
Install
From PyPI:
pip install linch
With optional extras (Anthropic, MCP, Gemini, OpenTelemetry, Postgres):
pip install 'linch[anthropic,mcp,gemini]'
For local development:
pip install -e '.[dev,mcp,anthropic,gemini]'
Quickstart
New to Linch? The step-by-step guide is in
docs/usage/quickstart.md, including an offline
smoke test that runs without an API key.
import asyncio
import os
from linch import Agent, InMemorySessionStore
agent = Agent(
model="gpt-5",
openai_api_key=os.environ.get("OPENAI_API_KEY"),
session_store=InMemorySessionStore(),
permissions={"mode": "skip-dangerous"},
)
async def main():
session = await agent.session()
async for event in session.run("Explain what an embeddable agent SDK is in one paragraph."):
if event.type == "result":
print(event.final_text)
asyncio.run(main())
For a coding/workspace agent, make that authority visible at construction:
from linch import Agent, workspace_tools
agent = Agent(model="gpt-5", tools=workspace_tools())
Put OPENAI_API_KEY=... in .env or export it in your shell. Never commit .env.
Safe local demos
These examples exercise useful local code paths without requiring a live provider call.
python3 examples/tools/parallel_search_agent.py
python3 examples/tools/runtime_tools.py
python3 examples/context/rag_context_builder.py
python3 examples/memory/memory_agent.py
python3 examples/tools/tool_reliability_agent.py
python3 examples/memory/sqlite_memory_agent.py
# Filesystem offload — fully offline, no API key needed
python3 examples/tools/filesystem_offload.py
Core concepts
Agent and Session
An Agent owns shared configuration: model, provider, tools, permissions, hooks, memory, and session store.
A Session represents one conversation or workflow run. One agent can serve many sessions.
Deep agent preset
create_deep_agent() returns an Agent with long-horizon orchestration defaults: durable run/session SQLite storage, task planning tools, specialist subagents (researcher, planner, implementer), a persistent /memories virtual filesystem partition, and deepened system prompt with planning/verification doctrine.
coordinator=True turns the parent into a pure orchestrator — it loses heavy tools (Edit/Write/Bash) and gains SubagentContinueTool and TaskStopTool. Workers receive full tool access. Background workers (run_in_background=True on Subagent) deliver results via <task-notification> at the top of the next turn. Fork/continue lets you re-engage any retained worker with its full prior context via SubagentContinue.
Background delivery is in-process: a RunStore keeps status audit telemetry,
but active detached tasks and result notifications are not reconstructed after
a process restart. Use an application-owned durable queue/workflow when that
delivery guarantee is required.
Events
linch streams the loop as events instead of hiding execution behind a blocking function call. This makes it easier to build CLIs, web UIs, background workers, and observability integrations.
Hooks
Hooks are the canonical extension mechanism for the agent loop. Register Python hook objects with Agent(hooks=[...]) to inspect or control the run at typed chokepoints: agent start/stop, prompt submit, turn start/stop, provider calls, pre/post tool use, final answer, stop, subagent start/stop, and event emission.
Built-in adapters are available when you want a standard extension as a hook:
ContextInjectionHook, ToolMiddlewareHook, RunTelemetryHook,
FinalAnswerVerifierHook, and StopPredicateHook.
from linch import Agent, HookResult, workspace_tools
class GuardTools:
def on_pre_tool_use(self, ctx):
if ctx.tool_name == "Bash" and "rm -rf" in str(ctx.input.get("command", "")):
return HookResult.block("blocked dangerous command")
return None
agent = Agent(
model="gpt-5",
tools=workspace_tools(),
hooks=[GuardTools()],
permissions={"mode": "skip-dangerous"},
)
Tools and scheduler
Tools are duck-typed Python objects. They can declare schemas, scopes, resources, timeouts, summaries, and execution behavior. The scheduler runs compatible tool calls in parallel while respecting resource constraints and permission rules.
For simple tools, use @tool; it creates the same Tool-compatible object that
the registry and scheduler already understand:
from linch import Agent, InMemorySessionStore, ToolContext, empty_tools, tool
@tool(description="Look up an internal document.")
async def search_docs(query: str, ctx: ToolContext) -> str:
return await ctx.deps.search(query)
agent = Agent(
model="gpt-5",
tools=empty_tools(search_docs),
deps=my_vector_store,
session_store=InMemorySessionStore(),
)
ContextBuilder
ContextBuilder lets you construct turn-specific context before the model runs. It is implemented as a built-in hook adapter; for new cross-cutting extension work, prefer Agent(hooks=[...]).
Memory
linch includes memory primitives and tools for searching and upserting reusable facts. Memory can be in-memory, sqlite-backed, tiered by working/episodic/semantic buckets, or replaced with your own store.
Virtual filesystem
Large tool results should not always stay in the model context. linch can offload large outputs to a virtual filesystem and pass references back into the loop.
Permissions
The permission engine controls dangerous actions at the runtime layer. You can define tool, path, and bash rules instead of relying only on prompts.
Providers
linch separates the agent runtime from the model provider. Built-in providers include OpenAI Responses, OpenAI Chat Completions, DeepSeek, Anthropic, Gemini, and llama.cpp. Use DeepSeekProvider for the native DeepSeek API, or OpenAI Chat Completions with base_url for generic compatible endpoints, and inspect known direct-provider models with list_provider_models().
When to use linch
linch is a good fit when you need:
- an agent loop embedded inside your own Python application;
- streaming events for UI, API, or worker integration;
- explicit control over tools, permissions, retries, and timeouts;
- RAG/context construction as a first-class runtime step;
- memory and filesystem state across longer workflows;
- provider flexibility without rewriting application logic;
- a small runtime layer you can build domain-specific workflows on top of.
When not to use linch
linch may be more than you need if:
- your feature is a single stateless LLM call;
- you want a no-code agent builder;
- you want a fully hosted assistant platform;
- you want every workflow expressed as a static graph from day one;
- you only need a thin wrapper around one provider's API.
Examples
Examples are organized by subsystem under examples/.
examples/core/ — agent fundamentals and real-world patterns
| File | What it shows |
|---|---|
core/tiny_agent.py |
Smallest possible agent |
core/coding_agent.py |
SWE agent — full tool set, BashRule/PathRule safety fence, LoopGuard, multi-turn |
core/policy_aware_execution.py |
Docker-backed Bash execution — permission rules plus opt-in runtime restrictions |
core/reading_agent.py |
Read-only codebase Q&A — no write/edit/bash, custom reviewer persona |
core/chat_agent.py |
Pure conversation agent — no tools, custom domain, structured output, ContextBuilder injection |
core/custom_permissions.py |
All permission modes and rule types |
core/system_prompts.py |
append, replace, per-session override, persona patterns |
core/structured_output.py |
OutputSchema, final_tool_name, JSON extraction |
core/event_streaming.py |
Consuming events for SSE, WebSocket, CLI progress |
core/multi_session.py |
One Agent, many users, shared deps |
core/deep_agent_resume.py |
create_deep_agent() — 4 demos: planning + /memories + run resume; background worker + notification; fork/continue retained worker; coordinator orchestration |
core/loop_guard_agent.py |
LoopGuard — identical-call and failure-streak detection |
core/budget_capped_agent.py |
RunBudget — token cap, 90% warning event, graceful exceeded stop |
core/compaction_ladder_agent.py |
CompactionLadder — micro-compact rung, reactive recovery, circuit breaker |
core/workflow_fleet.py |
agent.run_workflow() — parallel fan-out, pipeline, shared budget, crash + journal resume |
core/interactive_cli.py |
Interactive REPL |
examples/tools/ — tool patterns and scheduler
| File | What it shows |
|---|---|
tools/custom_tools.py |
5 tool patterns: read, write, exec, parallel, with deps |
tools/parallel_search_agent.py |
Scheduler V2: parallel search, resources, concurrency cap |
tools/runtime_tools.py |
Runtime registry add/remove/replace/select and schema export |
tools/tool_reliability_agent.py |
Timeout, per-tool opt-out, RetryOptions |
tools/filesystem_offload.py |
Virtual filesystem backends, auto-offload of large results (offline) |
examples/context/ — RAG and context hooks
| File | What it shows |
|---|---|
context/context_injection.py |
ContextInjectionHook patterns: RAG per-turn, budget, selected tools |
context/rag_context_builder.py |
ContextInjectionHook RAG with metadata and budget reporting |
examples/memory/ — memory primitives
| File | What it shows |
|---|---|
memory/memory_agent.py |
Core memory primitives with search/upsert tools and citations |
memory/sqlite_memory_agent.py |
SqliteMemoryStore — persistent memory, round-trip, upsert update |
memory/pgvector_memory.py |
Postgres memory example with optional vector-style retrieval |
examples/observability/ — telemetry hooks and tracing
| File | What it shows |
|---|---|
observability/observability_agent.py |
RunTelemetryHook with LoggingObserver + optional OpenTelemetryObserver |
observability/custom_observer.py |
Custom observer: latency tracking, error counts per tool |
examples/providers/ — provider-specific features
| File | What it shows |
|---|---|
providers/openai_agent.py |
OpenAI Chat Completions — basic, thinking, tool use, structured output, multi-turn |
providers/anthropic_agent.py |
Anthropic — extended thinking, prompt caching, tool-backed structured output |
providers/deepseek_agent.py |
DeepSeek via OpenAI-compatible and Anthropic-compatible endpoints |
examples/integrations/ — subagents, skills, MCP
| File | What it shows |
|---|---|
integrations/subagent_coordinator.py |
Agent definition files, tool-filtered subagents, SubagentEvent |
integrations/multi_agent_isolation.py |
Context isolation, sequential pipeline, parallel analysts, subagent + filesystem offload (offline) |
Public API
The supported, semver-governed API is exactly linch.__all__. Import supported names
from the top-level package:
from linch import (
Agent,
BashRule,
EvalCase,
PathRule,
ScriptedProvider,
ToolRule,
run_eval,
)
This surface includes the core agent/session runtime, provider adapters, typed events,
tools and extension contracts, workflow/deep-agent/loop primitives, permission rules,
and the complete deterministic eval harness and built-in scorers. Submodule paths are
implementation details rather than compatibility promises. See
docs/versioning.md for the contract and pinning policy.
Documentation
| File | What it covers |
|---|---|
docs/usage/ |
Getting started, install, config, providers, tools, hooks, structured output, RAG, workflows, deep agent |
docs/architecture.md |
Internal data flow, module contracts, design invariants |
docs/contributing.md |
Dev setup, code rules, test conventions, PR checklist |
examples/ |
Runnable examples by subsystem |
Development
pytest
ruff check . && ruff format --check .
pyright
See docs/contributing.md for the full contributor guide.
Name
linch blends two ideas:
靈 linh |
In classical Sino-Vietnamese, 靈 carries the meaning of spirit, effortless intelligence, and vital agility — an unseen force that makes a mechanism feel alive. |
| cinch | A saddle harness piece; English slang for easy, simple, sure. |
A linch is small — a single pin — but it holds the wheel on the axle. That is the SDK's ambition: the smallest harness that makes complex agent orchestration feel like a cinch.
靈活簡便,以一馭萬。
Linh hoạt giản tiện, dĩ nhất ngự vạn.
"Agile and simple — govern ten thousand things with one."
Metadata
Release files for linch 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| linch-2.0.0.tar.gz | 1.0 MB | Without provenance | Details for linch-2.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| linch-2.0.0-py3-none-any.whl | Python 3 | none | any | Details for linch-2.0.0-py3-none-any.whl |
Total release size: 1.5 MB
Release files / linch-2.0.0.tar.gz
| Download URL | linch-2.0.0.tar.gz |
|---|---|
| Size | 1.0 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
14f402b56413bdbc6910a1b760ae9bcb16a25072e8503c19b75b5840f685408a
|
|
BLAKE2b-256 checksum How to use checksums |
cde62712537731402275d4eea1fb466691ef4cbcfe6ca06b0394098f2c95ebb0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.14
|
Release files / linch-2.0.0-py3-none-any.whl
| Download URL | linch-2.0.0-py3-none-any.whl |
|---|---|
| Size | 458.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
543af3ef40dcb385b3f0c970bd675049c41d313e90b79192370f2793b103c1ff
|
|
BLAKE2b-256 checksum How to use checksums |
c5a8613ae62505c03326bf6e9d57086af9a95f7b1fc9fb07add2304da971506b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.11.14
|
Security for linch 2.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.