power-loop 3.0.1__tar.gz → 3.0.2__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.
Files changed (104) hide show
  1. {power_loop-3.0.1 → power_loop-3.0.2}/PKG-INFO +32 -9
  2. {power_loop-3.0.1 → power_loop-3.0.2}/README.md +31 -8
  3. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/__init__.py +1 -1
  4. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop.egg-info/PKG-INFO +32 -9
  5. {power_loop-3.0.1 → power_loop-3.0.2}/LICENSE +0 -0
  6. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/_vendor/__init__.py +0 -0
  7. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/_vendor/llm_client/__init__.py +0 -0
  8. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/_vendor/llm_client/anthropic_factory.py +0 -0
  9. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/_vendor/llm_client/capabilities.py +0 -0
  10. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/_vendor/llm_client/interface.py +0 -0
  11. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/_vendor/llm_client/llm_factory.py +0 -0
  12. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/_vendor/llm_client/llm_tooling.py +0 -0
  13. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/_vendor/llm_client/llm_utils.py +0 -0
  14. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/_vendor/llm_client/multimodal.py +0 -0
  15. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/agent/__init__.py +0 -0
  16. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/agent/follow_up.py +0 -0
  17. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/agent/sink.py +0 -0
  18. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/agent/stateful_loop.py +0 -0
  19. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/agent/system_prompt.py +0 -0
  20. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/agent/types.py +0 -0
  21. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/contracts/__init__.py +0 -0
  22. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/contracts/errors.py +0 -0
  23. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/contracts/event_payloads.py +0 -0
  24. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/contracts/events.py +0 -0
  25. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/contracts/handlers.py +0 -0
  26. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/contracts/hook_contexts.py +0 -0
  27. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/contracts/hooks.py +0 -0
  28. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/contracts/messages.py +0 -0
  29. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/contracts/protocols.py +0 -0
  30. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/contracts/tools.py +0 -0
  31. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/contrib/__init__.py +0 -0
  32. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/contrib/_redact.py +0 -0
  33. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/contrib/jsonl_sink.py +0 -0
  34. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/contrib/logging_sink.py +0 -0
  35. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/contrib/mcp.py +0 -0
  36. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/contrib/metrics_sink.py +0 -0
  37. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/contrib/otel_sink.py +0 -0
  38. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/core/agent_context.py +0 -0
  39. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/core/events.py +0 -0
  40. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/core/hooks.py +0 -0
  41. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/core/phase.py +0 -0
  42. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/core/pipeline.py +0 -0
  43. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/core/runner.py +0 -0
  44. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/core/state.py +0 -0
  45. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/py.typed +0 -0
  46. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/blackboard.py +0 -0
  47. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/budget.py +0 -0
  48. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/cancellation.py +0 -0
  49. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/compact.py +0 -0
  50. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/env.py +0 -0
  51. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/exec_backend.py +0 -0
  52. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/fold.py +0 -0
  53. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/fold_adapter.py +0 -0
  54. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/history_projector.py +0 -0
  55. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/history_sanitize.py +0 -0
  56. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/human_input.py +0 -0
  57. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/memory.py +0 -0
  58. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/notes.py +0 -0
  59. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/provider.py +0 -0
  60. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/representation.py +0 -0
  61. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/retry.py +0 -0
  62. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/runtime_state.py +0 -0
  63. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/session_store.py +0 -0
  64. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/skills.py +0 -0
  65. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/spec.py +0 -0
  66. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/store/__init__.py +0 -0
  67. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/store/backends/__init__.py +0 -0
  68. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/store/backends/mysql.py +0 -0
  69. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/store/backends/postgres.py +0 -0
  70. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/store/backends/sqlite.py +0 -0
  71. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/store/capabilities.py +0 -0
  72. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/store/db.py +0 -0
  73. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/store/dialect.py +0 -0
  74. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/store/factory.py +0 -0
  75. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/store/schema.py +0 -0
  76. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/store/store.py +0 -0
  77. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/store/types.py +0 -0
  78. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/structured.py +0 -0
  79. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/stub_provider.py +0 -0
  80. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/runtime/timers.py +0 -0
  81. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/tools/__init__.py +0 -0
  82. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/tools/blackboard.py +0 -0
  83. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/tools/default_manifest.py +0 -0
  84. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/tools/default_tools.py +0 -0
  85. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/tools/registry.py +0 -0
  86. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/tools/spawn_agent.py +0 -0
  87. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/workflow/__init__.py +0 -0
  88. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/workflow/api.py +0 -0
  89. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/workflow/engine.py +0 -0
  90. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/workflow/introspect.py +0 -0
  91. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/workflow/journal.py +0 -0
  92. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/workflow/result.py +0 -0
  93. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/workflow/resume.py +0 -0
  94. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/workflow/runner.py +0 -0
  95. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/workflow/spec.py +0 -0
  96. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/workflow/subprocess_executor.py +0 -0
  97. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/workflow/tool.py +0 -0
  98. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop/workflow/worker.py +0 -0
  99. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop.egg-info/SOURCES.txt +0 -0
  100. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop.egg-info/dependency_links.txt +0 -0
  101. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop.egg-info/requires.txt +0 -0
  102. {power_loop-3.0.1 → power_loop-3.0.2}/power_loop.egg-info/top_level.txt +0 -0
  103. {power_loop-3.0.1 → power_loop-3.0.2}/pyproject.toml +0 -0
  104. {power_loop-3.0.1 → power_loop-3.0.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: power-loop
3
- Version: 3.0.1
3
+ Version: 3.0.2
4
4
  Summary: Embeddable agent execution kernel — LLM loop, hooks, events, tools, dynamic sub-agents.
5
5
  Author-email: zhangran <zhangran24@126.com>
6
6
  License: MIT
@@ -106,7 +106,7 @@ print((await loop.send("And my second-favorite?", session_id=sid)).final_text)
106
106
  pip install 'power-loop[openai]' # or [anthropic] · add [postgres] / [mysql] for those backends
107
107
  ```
108
108
 
109
- > **1.0 stable.** The public API is frozen under SemVer (a breaking change requires a major bump), machine-enforced by a baseline guard in CI. The **core has zero runtime dependencies** (pure stdlib; verified by a CI job that imports it with nothing else installed) — LLM transports *and database drivers* are optional extras. See [Stability](#stability--semver) and the [honest caveats](#honest-scope) — a young, single-maintainer project says so plainly.
109
+ > **Stable since 1.0; now 3.x.** The public API is frozen under SemVer and machine-enforced by a baseline guard in CI — and the two major bumps since prove the discipline rather than undercut it: **2.0** moved storage to a pluggable async backend, **3.0** made context handling two orthogonal axes. Both were real breaking changes, so both got a major bump. The **core has zero runtime dependencies** (pure stdlib; verified by a CI job that imports it with nothing else installed) — LLM transports *and database drivers* are optional extras. Backed by **900+ unit tests**, a **live-LLM** suite, and a **3-backend conformance suite** (SQLite/PostgreSQL/MySQL). See [Stability](#stability--semver) and the [honest caveats](#honest-scope) — a young, single-maintainer project says so plainly.
110
110
 
111
111
  ---
112
112
 
@@ -116,7 +116,7 @@ pip install 'power-loop[openai]' # or [anthropic] · add [postgres] / [mysq
116
116
  |---|---|
117
117
  | 🚀 **New** — show me the 5-minute version | [Getting Started](docs/en/getting-started.md) |
118
118
  | 🛠️ **Learning by building** | [Tutorials](docs/en/tutorials/index.md) — chatbot · tools · human-in-the-loop · multi-agent |
119
- | 🧩 **Browsing runnable code** | [40 examples](examples/README.md) — `00_hello_world.py` → full chatbot |
119
+ | 🧩 **Browsing runnable code** | [43 examples](examples/README.md) — `00_hello_world.py` → full chatbot |
120
120
  | 📚 **Looking something up** | [User Guide](docs/en/user-guide/index.md) · [API reference](docs/en/api/index.md) |
121
121
  | 🤔 **Deciding if it fits** | [How it compares](#how-it-compares) · [Honest scope](#honest-scope) |
122
122
 
@@ -128,10 +128,11 @@ pip install 'power-loop[openai]' # or [anthropic] · add [postgres] / [mysq
128
128
 
129
129
  Most "agent frameworks" ask you to build your app *inside* them. power-loop is the opposite: a **library you embed**. You keep your HTTP layer, your auth, your queues, your RAG, your UI, your deploy. It runs the agent loop — and lets you *engineer* it.
130
130
 
131
- - 🪶 **Featherweight & zero-dependency.** No `pydantic`, no LangChain, no graph DSL. A compact, pure-stdlib core (~20k lines) whose public surface is essentially one class — and **zero runtime dependencies**. LLM transports *and* the Postgres/MySQL drivers are pulled in only by the extra you install.
131
+ - 🪶 **Featherweight & zero-dependency.** No `pydantic`, no LangChain, no graph DSL. A compact, pure-stdlib core (~24k lines) whose public surface is essentially one class — and **zero runtime dependencies**. LLM transports *and* the Postgres/MySQL drivers are pulled in only by the extra you install.
132
132
  - 🗄️ **Pluggable storage, zero-infra default.** Sessions, timers, sub-agent trees, workflow journals, the shared blackboard — one backend-neutral store written once against a tiny `Database`/`Dialect` port. The default is **one SQLite file** (copy the file, you've copied the state); point a DSN at **PostgreSQL or MySQL** when you want a real multi-writer server — same code, same conformance suite. Tables are auto-created, or **provisioned out-of-band** with a printed DDL script (see [Storage backends](docs/en/user-guide/storage-backends.md)).
133
133
  - ♻️ **Stateless, resumable loops.** A `StatefulAgentLoop` carries no authoritative state — all of it lives in the store. So a loop is cheap to create and trivially **restored from a DSN + a session id** (ideal for web handlers, workers, cold starts). It self-caches each session's active window (a rebuildable accelerator that never changes what the model sees) to skip re-reads on hot paths.
134
134
  - ⏱️ **Durable by default.** Crash mid-run and `resume()`. Agents schedule their own **durable timers** that survive restarts. Workflows **replay finished steps and re-run only the unfinished tail** after a process death. The store survives version upgrades (a portable, backend-neutral migration-version table) and can be **pruned, VACUUMed, and exported**.
135
+ - 🧠 **Context engineering, not one fixed strategy.** How each finished send is *recorded/rendered* (**representation**: full **verbatim** or a terse per-send **projection**) and how older history is *compacted* once over budget (**fold strategy**: a single **LLM summary**, or an **agentic** pass that also writes durable notes) are two **orthogonal, config-driven axes** — any representation composes with any fold strategy, and both take your own `Representation` / `FoldStrategy` implementation. Folds always keep whole sends (never split a tool-call/result pair); `recall_send` / `recall_compacted` pull the original detail back from the immutable audit log.
135
136
  - 🧩 **Composable from one loop to a fleet.** Start with `send()`. Add tools. Spawn sub-agents. Fan out a deterministic **workflow** (`sequence`/`parallel`/`foreach`/`branch`). Run each leaf in its **own process and DB** behind a sandbox. Same primitives all the way up.
136
137
  - 🛡️ **Isolation seams where it counts.** Tool-level sandboxing via a `ShellBackend` (drop in gVisor/Docker for `bash`); process-level via a `WorkerLauncher` (wrap a whole sub-agent worker per leaf). power-loop stays sandbox-agnostic; you choose the policy.
137
138
  - 🔬 **Built to be observed.** Typed events for every stream chunk, tool call, round, and **individual LLM call** — each `seq`-ordered + monotonic-clock stamped. Pluggable sinks behind extras: durable **JSONL** (with `replay`), **Prometheus/StatsD** metrics, an **OpenTelemetry** span tree. Per-run + per-session token accounting and hard per-run budgets.
@@ -153,8 +154,8 @@ Most "agent frameworks" ask you to build your app *inside* them. power-loop is t
153
154
  | **Workflow resume** | Journals each step; after a crash, replays completed steps and re-runs only the tail | [Workflows](docs/en/user-guide/workflows.md) |
154
155
  | **Process sandboxing** | Each workflow leaf in its own OS process + own DB; wrap each in gVisor/Docker per leaf | [Sandboxing](docs/en/user-guide/sandboxing.md) |
155
156
  | **Durable timers** | Agents schedule their own wake-ups; survive restarts; one-shot or recurring | [Timers](docs/en/user-guide/timers.md) |
156
- | **Context compaction** | Auto-summarize old turns (never splits a tool-call pair); `recall_compacted` to pull originals back | [Compaction](docs/en/user-guide/compaction.md) |
157
- | **Send-context projection** | Opt-in: feed a per-send plain-text projection of finished sends (derived `pl_project_messages`) instead of verbatim history; `pl_messages` stays immutable; `recall_send` to re-expand | [Projection](docs/en/user-guide/send-context-projection.md) |
157
+ | **Context — representation** | Record/render each finished send **verbatim** or as a terse per-send **projection** (derived `pl_project_messages`); `pl_messages` stays immutable; `recall_send` re-expands | [Projection](docs/en/user-guide/send-context-projection.md) |
158
+ | **Context — fold strategy** | Compact older history once over budget: **LLM summary** or **agentic** (also writes notes); pluggable `FoldStrategy`; never splits a tool pair; `recall_compacted` re-expands | [Compaction](docs/en/user-guide/compaction.md) |
158
159
  | **Durability ops** | Portable migration-version table, retention/prune, VACUUM, `export_session`/`import_session`, graceful `aclose()` | [Sessions](docs/en/user-guide/sessions.md) |
159
160
  | **Observability** | Typed `seq`-ordered events → durable JSONL + `replay`, Prometheus/StatsD metrics, OpenTelemetry spans | [Observability](docs/en/user-guide/observability.md) |
160
161
  | **MCP tools** | Surface a Model Context Protocol server's tools as power-loop tools | [Extending](docs/en/user-guide/extending-tools.md) |
@@ -200,6 +201,28 @@ result = await loop.send(user_text, session_id=session_id)
200
201
 
201
202
  Under the hood the loop keeps a per-session **active-window cache** — but it caches only the *durable* projection, validated by a monotonic `next_seq` token, so it's a pure accelerator: a cold loop with an empty cache produces byte-for-byte the same prompts (proven by a warm-vs-cold conformance test, including the recall/compaction/prompt-edit edge cases).
202
203
 
204
+ ### Context engineering — two orthogonal axes you choose (and can implement yourself)
205
+
206
+ Long conversations outgrow the window. Most libraries give you *one* fixed compaction behavior; power-loop (3.0) splits it into two independent, config-driven axes:
207
+
208
+ - **Representation** — how each *finished send* is recorded & rendered: `VerbatimRepresentation` (full, byte-identical history) or `ProjectedRepresentation` (a terse per-send plain-text projection). The original detail always stays in the immutable `pl_messages` audit log.
209
+ - **Fold strategy** — how *older* history is compacted once the rendered prefix crosses the budget: `LLMSummaryFold` (one summary call) or `AgenticFold` (a bounded tool loop that also persists durable facts as notes).
210
+
211
+ ```python
212
+ from power_loop import (
213
+ StatefulAgentLoop, AgentLoopConfig,
214
+ ProjectedRepresentation, AgenticFold, # mix & match either axis — or pass your own impl
215
+ )
216
+
217
+ cfg = AgentLoopConfig(
218
+ representation=ProjectedRepresentation(max_chars=300), # terse projection (or VerbatimRepresentation)
219
+ fold_strategy=AgenticFold(keep_last_sends=4), # summarize older sends + write notes
220
+ )
221
+ loop = StatefulAgentLoop(llm=llm, dsn="app.db", config=cfg)
222
+ ```
223
+
224
+ Any representation composes with any fold strategy, and each axis is a small `Protocol` you can implement yourself. A fold always keeps **whole sends** (it never splits an atomic tool-call/result pair), and the model can call `recall_send(send_index=N)` / `recall_compacted()` to pull the full original detail back from the audit log. (The two classes above are public but **provisional** — added in 3.0, not yet frozen into `STABLE_API`; `AgentLoopConfig` itself is Stable.)
225
+
203
226
  ### Deterministic multi-agent workflows — that the model can author, and that survive a crash
204
227
 
205
228
  Sub-agent delegation is *model-driven* ("go do this"). When you want **code-driven, deterministic** orchestration — fan out over a list, branch on a result, run a pipeline — describe it as a `WorkflowSpec` and let the engine interpret it. The only LLM calls are the leaves; `sequence`/`parallel`/`foreach`/`branch` are plain code.
@@ -281,7 +304,7 @@ await register_mcp_tools(registry, client, prefix="fs.") # MCP tools → power
281
304
 
282
305
  The seam is a tiny `MCPToolSource` Protocol, so the `mcp` SDK is optional and any client works.
283
306
 
284
- > More: hard token budgets, structured output, crash recovery, memory, the blackboard — see [`examples/`](examples/README.md) (40 runnable programs) and the [docs](docs/en/index.md).
307
+ > More: hard token budgets, structured output, crash recovery, memory, the blackboard — see [`examples/`](examples/README.md) (43 runnable programs) and the [docs](docs/en/index.md).
285
308
 
286
309
  ---
287
310
 
@@ -289,7 +312,7 @@ The seam is a tiny `MCPToolSource` Protocol, so the `mcp` SDK is optional and an
289
312
 
290
313
  power-loop is a **kernel**, not a platform — that's the whole trade-off.
291
314
 
292
- - **vs. LangChain / LangGraph / LlamaIndex / CrewAI / AutoGen** — those are batteries-included frameworks with large ecosystems (connectors, vector stores, integrations) and heavy dependency trees. power-loop deliberately ships **none of that**: a compact (~20k-line) pure-stdlib core with zero runtime dependencies, and you bring your own tools (or an MCP server). You get durable sessions across SQLite/PG/MySQL, crash-resumable workflows, and real sandbox seams out of the box; you do **not** get a bundled RAG stack or 100 connectors.
315
+ - **vs. LangChain / LangGraph / LlamaIndex / CrewAI / AutoGen** — those are batteries-included frameworks with large ecosystems (connectors, vector stores, integrations) and heavy dependency trees. power-loop deliberately ships **none of that**: a compact (~24k-line) pure-stdlib core with zero runtime dependencies, and you bring your own tools (or an MCP server). You get durable sessions across SQLite/PG/MySQL, crash-resumable workflows, and real sandbox seams out of the box; you do **not** get a bundled RAG stack or 100 connectors.
293
316
  - **Choose power-loop** when you want to *embed* an agent in an existing app, keep your dependency surface tiny, pick your own database, and care about durability + isolation + a stable contract.
294
317
  - **Choose a framework** when you want batteries included, a big integration catalog, and don't mind the weight.
295
318
 
@@ -321,7 +344,7 @@ Python 3.10+. See [Getting Started](docs/en/getting-started.md). Optional extras
321
344
 
322
345
  ## Stability & SemVer
323
346
 
324
- As of **1.0**, the **STABLE** API (listed in `power_loop.STABLE_API`) is under SemVer: a breaking change requires a major bump (`2.0.0`), enforced by a frozen-baseline test in CI — including the flagship `StatefulAgentLoop` *and the LLM contract needed to construct it*. Error `.code` strings are frozen too.
347
+ Since **1.0**, the **STABLE** API (listed in `power_loop.STABLE_API`) is under SemVer: a breaking change requires a major bump, enforced by a frozen-baseline test in CI — including the flagship `StatefulAgentLoop` *and the LLM contract needed to construct it*. Error `.code` strings are frozen too. The two majors since (2.0 pluggable async storage, 3.0 orthogonal context axes) were exactly that policy in action — breaking changes earned a major bump, each documented in the [Changelog](CHANGELOG.md).
325
348
 
326
349
  | Tier | Meaning |
327
350
  |---|---|
@@ -33,7 +33,7 @@ print((await loop.send("And my second-favorite?", session_id=sid)).final_text)
33
33
  pip install 'power-loop[openai]' # or [anthropic] · add [postgres] / [mysql] for those backends
34
34
  ```
35
35
 
36
- > **1.0 stable.** The public API is frozen under SemVer (a breaking change requires a major bump), machine-enforced by a baseline guard in CI. The **core has zero runtime dependencies** (pure stdlib; verified by a CI job that imports it with nothing else installed) — LLM transports *and database drivers* are optional extras. See [Stability](#stability--semver) and the [honest caveats](#honest-scope) — a young, single-maintainer project says so plainly.
36
+ > **Stable since 1.0; now 3.x.** The public API is frozen under SemVer and machine-enforced by a baseline guard in CI — and the two major bumps since prove the discipline rather than undercut it: **2.0** moved storage to a pluggable async backend, **3.0** made context handling two orthogonal axes. Both were real breaking changes, so both got a major bump. The **core has zero runtime dependencies** (pure stdlib; verified by a CI job that imports it with nothing else installed) — LLM transports *and database drivers* are optional extras. Backed by **900+ unit tests**, a **live-LLM** suite, and a **3-backend conformance suite** (SQLite/PostgreSQL/MySQL). See [Stability](#stability--semver) and the [honest caveats](#honest-scope) — a young, single-maintainer project says so plainly.
37
37
 
38
38
  ---
39
39
 
@@ -43,7 +43,7 @@ pip install 'power-loop[openai]' # or [anthropic] · add [postgres] / [mysq
43
43
  |---|---|
44
44
  | 🚀 **New** — show me the 5-minute version | [Getting Started](docs/en/getting-started.md) |
45
45
  | 🛠️ **Learning by building** | [Tutorials](docs/en/tutorials/index.md) — chatbot · tools · human-in-the-loop · multi-agent |
46
- | 🧩 **Browsing runnable code** | [40 examples](examples/README.md) — `00_hello_world.py` → full chatbot |
46
+ | 🧩 **Browsing runnable code** | [43 examples](examples/README.md) — `00_hello_world.py` → full chatbot |
47
47
  | 📚 **Looking something up** | [User Guide](docs/en/user-guide/index.md) · [API reference](docs/en/api/index.md) |
48
48
  | 🤔 **Deciding if it fits** | [How it compares](#how-it-compares) · [Honest scope](#honest-scope) |
49
49
 
@@ -55,10 +55,11 @@ pip install 'power-loop[openai]' # or [anthropic] · add [postgres] / [mysq
55
55
 
56
56
  Most "agent frameworks" ask you to build your app *inside* them. power-loop is the opposite: a **library you embed**. You keep your HTTP layer, your auth, your queues, your RAG, your UI, your deploy. It runs the agent loop — and lets you *engineer* it.
57
57
 
58
- - 🪶 **Featherweight & zero-dependency.** No `pydantic`, no LangChain, no graph DSL. A compact, pure-stdlib core (~20k lines) whose public surface is essentially one class — and **zero runtime dependencies**. LLM transports *and* the Postgres/MySQL drivers are pulled in only by the extra you install.
58
+ - 🪶 **Featherweight & zero-dependency.** No `pydantic`, no LangChain, no graph DSL. A compact, pure-stdlib core (~24k lines) whose public surface is essentially one class — and **zero runtime dependencies**. LLM transports *and* the Postgres/MySQL drivers are pulled in only by the extra you install.
59
59
  - 🗄️ **Pluggable storage, zero-infra default.** Sessions, timers, sub-agent trees, workflow journals, the shared blackboard — one backend-neutral store written once against a tiny `Database`/`Dialect` port. The default is **one SQLite file** (copy the file, you've copied the state); point a DSN at **PostgreSQL or MySQL** when you want a real multi-writer server — same code, same conformance suite. Tables are auto-created, or **provisioned out-of-band** with a printed DDL script (see [Storage backends](docs/en/user-guide/storage-backends.md)).
60
60
  - ♻️ **Stateless, resumable loops.** A `StatefulAgentLoop` carries no authoritative state — all of it lives in the store. So a loop is cheap to create and trivially **restored from a DSN + a session id** (ideal for web handlers, workers, cold starts). It self-caches each session's active window (a rebuildable accelerator that never changes what the model sees) to skip re-reads on hot paths.
61
61
  - ⏱️ **Durable by default.** Crash mid-run and `resume()`. Agents schedule their own **durable timers** that survive restarts. Workflows **replay finished steps and re-run only the unfinished tail** after a process death. The store survives version upgrades (a portable, backend-neutral migration-version table) and can be **pruned, VACUUMed, and exported**.
62
+ - 🧠 **Context engineering, not one fixed strategy.** How each finished send is *recorded/rendered* (**representation**: full **verbatim** or a terse per-send **projection**) and how older history is *compacted* once over budget (**fold strategy**: a single **LLM summary**, or an **agentic** pass that also writes durable notes) are two **orthogonal, config-driven axes** — any representation composes with any fold strategy, and both take your own `Representation` / `FoldStrategy` implementation. Folds always keep whole sends (never split a tool-call/result pair); `recall_send` / `recall_compacted` pull the original detail back from the immutable audit log.
62
63
  - 🧩 **Composable from one loop to a fleet.** Start with `send()`. Add tools. Spawn sub-agents. Fan out a deterministic **workflow** (`sequence`/`parallel`/`foreach`/`branch`). Run each leaf in its **own process and DB** behind a sandbox. Same primitives all the way up.
63
64
  - 🛡️ **Isolation seams where it counts.** Tool-level sandboxing via a `ShellBackend` (drop in gVisor/Docker for `bash`); process-level via a `WorkerLauncher` (wrap a whole sub-agent worker per leaf). power-loop stays sandbox-agnostic; you choose the policy.
64
65
  - 🔬 **Built to be observed.** Typed events for every stream chunk, tool call, round, and **individual LLM call** — each `seq`-ordered + monotonic-clock stamped. Pluggable sinks behind extras: durable **JSONL** (with `replay`), **Prometheus/StatsD** metrics, an **OpenTelemetry** span tree. Per-run + per-session token accounting and hard per-run budgets.
@@ -80,8 +81,8 @@ Most "agent frameworks" ask you to build your app *inside* them. power-loop is t
80
81
  | **Workflow resume** | Journals each step; after a crash, replays completed steps and re-runs only the tail | [Workflows](docs/en/user-guide/workflows.md) |
81
82
  | **Process sandboxing** | Each workflow leaf in its own OS process + own DB; wrap each in gVisor/Docker per leaf | [Sandboxing](docs/en/user-guide/sandboxing.md) |
82
83
  | **Durable timers** | Agents schedule their own wake-ups; survive restarts; one-shot or recurring | [Timers](docs/en/user-guide/timers.md) |
83
- | **Context compaction** | Auto-summarize old turns (never splits a tool-call pair); `recall_compacted` to pull originals back | [Compaction](docs/en/user-guide/compaction.md) |
84
- | **Send-context projection** | Opt-in: feed a per-send plain-text projection of finished sends (derived `pl_project_messages`) instead of verbatim history; `pl_messages` stays immutable; `recall_send` to re-expand | [Projection](docs/en/user-guide/send-context-projection.md) |
84
+ | **Context — representation** | Record/render each finished send **verbatim** or as a terse per-send **projection** (derived `pl_project_messages`); `pl_messages` stays immutable; `recall_send` re-expands | [Projection](docs/en/user-guide/send-context-projection.md) |
85
+ | **Context — fold strategy** | Compact older history once over budget: **LLM summary** or **agentic** (also writes notes); pluggable `FoldStrategy`; never splits a tool pair; `recall_compacted` re-expands | [Compaction](docs/en/user-guide/compaction.md) |
85
86
  | **Durability ops** | Portable migration-version table, retention/prune, VACUUM, `export_session`/`import_session`, graceful `aclose()` | [Sessions](docs/en/user-guide/sessions.md) |
86
87
  | **Observability** | Typed `seq`-ordered events → durable JSONL + `replay`, Prometheus/StatsD metrics, OpenTelemetry spans | [Observability](docs/en/user-guide/observability.md) |
87
88
  | **MCP tools** | Surface a Model Context Protocol server's tools as power-loop tools | [Extending](docs/en/user-guide/extending-tools.md) |
@@ -127,6 +128,28 @@ result = await loop.send(user_text, session_id=session_id)
127
128
 
128
129
  Under the hood the loop keeps a per-session **active-window cache** — but it caches only the *durable* projection, validated by a monotonic `next_seq` token, so it's a pure accelerator: a cold loop with an empty cache produces byte-for-byte the same prompts (proven by a warm-vs-cold conformance test, including the recall/compaction/prompt-edit edge cases).
129
130
 
131
+ ### Context engineering — two orthogonal axes you choose (and can implement yourself)
132
+
133
+ Long conversations outgrow the window. Most libraries give you *one* fixed compaction behavior; power-loop (3.0) splits it into two independent, config-driven axes:
134
+
135
+ - **Representation** — how each *finished send* is recorded & rendered: `VerbatimRepresentation` (full, byte-identical history) or `ProjectedRepresentation` (a terse per-send plain-text projection). The original detail always stays in the immutable `pl_messages` audit log.
136
+ - **Fold strategy** — how *older* history is compacted once the rendered prefix crosses the budget: `LLMSummaryFold` (one summary call) or `AgenticFold` (a bounded tool loop that also persists durable facts as notes).
137
+
138
+ ```python
139
+ from power_loop import (
140
+ StatefulAgentLoop, AgentLoopConfig,
141
+ ProjectedRepresentation, AgenticFold, # mix & match either axis — or pass your own impl
142
+ )
143
+
144
+ cfg = AgentLoopConfig(
145
+ representation=ProjectedRepresentation(max_chars=300), # terse projection (or VerbatimRepresentation)
146
+ fold_strategy=AgenticFold(keep_last_sends=4), # summarize older sends + write notes
147
+ )
148
+ loop = StatefulAgentLoop(llm=llm, dsn="app.db", config=cfg)
149
+ ```
150
+
151
+ Any representation composes with any fold strategy, and each axis is a small `Protocol` you can implement yourself. A fold always keeps **whole sends** (it never splits an atomic tool-call/result pair), and the model can call `recall_send(send_index=N)` / `recall_compacted()` to pull the full original detail back from the audit log. (The two classes above are public but **provisional** — added in 3.0, not yet frozen into `STABLE_API`; `AgentLoopConfig` itself is Stable.)
152
+
130
153
  ### Deterministic multi-agent workflows — that the model can author, and that survive a crash
131
154
 
132
155
  Sub-agent delegation is *model-driven* ("go do this"). When you want **code-driven, deterministic** orchestration — fan out over a list, branch on a result, run a pipeline — describe it as a `WorkflowSpec` and let the engine interpret it. The only LLM calls are the leaves; `sequence`/`parallel`/`foreach`/`branch` are plain code.
@@ -208,7 +231,7 @@ await register_mcp_tools(registry, client, prefix="fs.") # MCP tools → power
208
231
 
209
232
  The seam is a tiny `MCPToolSource` Protocol, so the `mcp` SDK is optional and any client works.
210
233
 
211
- > More: hard token budgets, structured output, crash recovery, memory, the blackboard — see [`examples/`](examples/README.md) (40 runnable programs) and the [docs](docs/en/index.md).
234
+ > More: hard token budgets, structured output, crash recovery, memory, the blackboard — see [`examples/`](examples/README.md) (43 runnable programs) and the [docs](docs/en/index.md).
212
235
 
213
236
  ---
214
237
 
@@ -216,7 +239,7 @@ The seam is a tiny `MCPToolSource` Protocol, so the `mcp` SDK is optional and an
216
239
 
217
240
  power-loop is a **kernel**, not a platform — that's the whole trade-off.
218
241
 
219
- - **vs. LangChain / LangGraph / LlamaIndex / CrewAI / AutoGen** — those are batteries-included frameworks with large ecosystems (connectors, vector stores, integrations) and heavy dependency trees. power-loop deliberately ships **none of that**: a compact (~20k-line) pure-stdlib core with zero runtime dependencies, and you bring your own tools (or an MCP server). You get durable sessions across SQLite/PG/MySQL, crash-resumable workflows, and real sandbox seams out of the box; you do **not** get a bundled RAG stack or 100 connectors.
242
+ - **vs. LangChain / LangGraph / LlamaIndex / CrewAI / AutoGen** — those are batteries-included frameworks with large ecosystems (connectors, vector stores, integrations) and heavy dependency trees. power-loop deliberately ships **none of that**: a compact (~24k-line) pure-stdlib core with zero runtime dependencies, and you bring your own tools (or an MCP server). You get durable sessions across SQLite/PG/MySQL, crash-resumable workflows, and real sandbox seams out of the box; you do **not** get a bundled RAG stack or 100 connectors.
220
243
  - **Choose power-loop** when you want to *embed* an agent in an existing app, keep your dependency surface tiny, pick your own database, and care about durability + isolation + a stable contract.
221
244
  - **Choose a framework** when you want batteries included, a big integration catalog, and don't mind the weight.
222
245
 
@@ -248,7 +271,7 @@ Python 3.10+. See [Getting Started](docs/en/getting-started.md). Optional extras
248
271
 
249
272
  ## Stability & SemVer
250
273
 
251
- As of **1.0**, the **STABLE** API (listed in `power_loop.STABLE_API`) is under SemVer: a breaking change requires a major bump (`2.0.0`), enforced by a frozen-baseline test in CI — including the flagship `StatefulAgentLoop` *and the LLM contract needed to construct it*. Error `.code` strings are frozen too.
274
+ Since **1.0**, the **STABLE** API (listed in `power_loop.STABLE_API`) is under SemVer: a breaking change requires a major bump, enforced by a frozen-baseline test in CI — including the flagship `StatefulAgentLoop` *and the LLM contract needed to construct it*. Error `.code` strings are frozen too. The two majors since (2.0 pluggable async storage, 3.0 orthogonal context axes) were exactly that policy in action — breaking changes earned a major bump, each documented in the [Changelog](CHANGELOG.md).
252
275
 
253
276
  | Tier | Meaning |
254
277
  |---|---|
@@ -15,7 +15,7 @@ Stability tiers
15
15
  无版本承诺,可随时变更或删除。
16
16
  """
17
17
 
18
- __version__ = "3.0.1"
18
+ __version__ = "3.0.2"
19
19
 
20
20
  # Public LLM contract (SDK-free) re-exported so callers (e.g. writing llm.* hooks or
21
21
  # a custom LLMService) don't reach into the internal vendored transport package (H3.4).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: power-loop
3
- Version: 3.0.1
3
+ Version: 3.0.2
4
4
  Summary: Embeddable agent execution kernel — LLM loop, hooks, events, tools, dynamic sub-agents.
5
5
  Author-email: zhangran <zhangran24@126.com>
6
6
  License: MIT
@@ -106,7 +106,7 @@ print((await loop.send("And my second-favorite?", session_id=sid)).final_text)
106
106
  pip install 'power-loop[openai]' # or [anthropic] · add [postgres] / [mysql] for those backends
107
107
  ```
108
108
 
109
- > **1.0 stable.** The public API is frozen under SemVer (a breaking change requires a major bump), machine-enforced by a baseline guard in CI. The **core has zero runtime dependencies** (pure stdlib; verified by a CI job that imports it with nothing else installed) — LLM transports *and database drivers* are optional extras. See [Stability](#stability--semver) and the [honest caveats](#honest-scope) — a young, single-maintainer project says so plainly.
109
+ > **Stable since 1.0; now 3.x.** The public API is frozen under SemVer and machine-enforced by a baseline guard in CI — and the two major bumps since prove the discipline rather than undercut it: **2.0** moved storage to a pluggable async backend, **3.0** made context handling two orthogonal axes. Both were real breaking changes, so both got a major bump. The **core has zero runtime dependencies** (pure stdlib; verified by a CI job that imports it with nothing else installed) — LLM transports *and database drivers* are optional extras. Backed by **900+ unit tests**, a **live-LLM** suite, and a **3-backend conformance suite** (SQLite/PostgreSQL/MySQL). See [Stability](#stability--semver) and the [honest caveats](#honest-scope) — a young, single-maintainer project says so plainly.
110
110
 
111
111
  ---
112
112
 
@@ -116,7 +116,7 @@ pip install 'power-loop[openai]' # or [anthropic] · add [postgres] / [mysq
116
116
  |---|---|
117
117
  | 🚀 **New** — show me the 5-minute version | [Getting Started](docs/en/getting-started.md) |
118
118
  | 🛠️ **Learning by building** | [Tutorials](docs/en/tutorials/index.md) — chatbot · tools · human-in-the-loop · multi-agent |
119
- | 🧩 **Browsing runnable code** | [40 examples](examples/README.md) — `00_hello_world.py` → full chatbot |
119
+ | 🧩 **Browsing runnable code** | [43 examples](examples/README.md) — `00_hello_world.py` → full chatbot |
120
120
  | 📚 **Looking something up** | [User Guide](docs/en/user-guide/index.md) · [API reference](docs/en/api/index.md) |
121
121
  | 🤔 **Deciding if it fits** | [How it compares](#how-it-compares) · [Honest scope](#honest-scope) |
122
122
 
@@ -128,10 +128,11 @@ pip install 'power-loop[openai]' # or [anthropic] · add [postgres] / [mysq
128
128
 
129
129
  Most "agent frameworks" ask you to build your app *inside* them. power-loop is the opposite: a **library you embed**. You keep your HTTP layer, your auth, your queues, your RAG, your UI, your deploy. It runs the agent loop — and lets you *engineer* it.
130
130
 
131
- - 🪶 **Featherweight & zero-dependency.** No `pydantic`, no LangChain, no graph DSL. A compact, pure-stdlib core (~20k lines) whose public surface is essentially one class — and **zero runtime dependencies**. LLM transports *and* the Postgres/MySQL drivers are pulled in only by the extra you install.
131
+ - 🪶 **Featherweight & zero-dependency.** No `pydantic`, no LangChain, no graph DSL. A compact, pure-stdlib core (~24k lines) whose public surface is essentially one class — and **zero runtime dependencies**. LLM transports *and* the Postgres/MySQL drivers are pulled in only by the extra you install.
132
132
  - 🗄️ **Pluggable storage, zero-infra default.** Sessions, timers, sub-agent trees, workflow journals, the shared blackboard — one backend-neutral store written once against a tiny `Database`/`Dialect` port. The default is **one SQLite file** (copy the file, you've copied the state); point a DSN at **PostgreSQL or MySQL** when you want a real multi-writer server — same code, same conformance suite. Tables are auto-created, or **provisioned out-of-band** with a printed DDL script (see [Storage backends](docs/en/user-guide/storage-backends.md)).
133
133
  - ♻️ **Stateless, resumable loops.** A `StatefulAgentLoop` carries no authoritative state — all of it lives in the store. So a loop is cheap to create and trivially **restored from a DSN + a session id** (ideal for web handlers, workers, cold starts). It self-caches each session's active window (a rebuildable accelerator that never changes what the model sees) to skip re-reads on hot paths.
134
134
  - ⏱️ **Durable by default.** Crash mid-run and `resume()`. Agents schedule their own **durable timers** that survive restarts. Workflows **replay finished steps and re-run only the unfinished tail** after a process death. The store survives version upgrades (a portable, backend-neutral migration-version table) and can be **pruned, VACUUMed, and exported**.
135
+ - 🧠 **Context engineering, not one fixed strategy.** How each finished send is *recorded/rendered* (**representation**: full **verbatim** or a terse per-send **projection**) and how older history is *compacted* once over budget (**fold strategy**: a single **LLM summary**, or an **agentic** pass that also writes durable notes) are two **orthogonal, config-driven axes** — any representation composes with any fold strategy, and both take your own `Representation` / `FoldStrategy` implementation. Folds always keep whole sends (never split a tool-call/result pair); `recall_send` / `recall_compacted` pull the original detail back from the immutable audit log.
135
136
  - 🧩 **Composable from one loop to a fleet.** Start with `send()`. Add tools. Spawn sub-agents. Fan out a deterministic **workflow** (`sequence`/`parallel`/`foreach`/`branch`). Run each leaf in its **own process and DB** behind a sandbox. Same primitives all the way up.
136
137
  - 🛡️ **Isolation seams where it counts.** Tool-level sandboxing via a `ShellBackend` (drop in gVisor/Docker for `bash`); process-level via a `WorkerLauncher` (wrap a whole sub-agent worker per leaf). power-loop stays sandbox-agnostic; you choose the policy.
137
138
  - 🔬 **Built to be observed.** Typed events for every stream chunk, tool call, round, and **individual LLM call** — each `seq`-ordered + monotonic-clock stamped. Pluggable sinks behind extras: durable **JSONL** (with `replay`), **Prometheus/StatsD** metrics, an **OpenTelemetry** span tree. Per-run + per-session token accounting and hard per-run budgets.
@@ -153,8 +154,8 @@ Most "agent frameworks" ask you to build your app *inside* them. power-loop is t
153
154
  | **Workflow resume** | Journals each step; after a crash, replays completed steps and re-runs only the tail | [Workflows](docs/en/user-guide/workflows.md) |
154
155
  | **Process sandboxing** | Each workflow leaf in its own OS process + own DB; wrap each in gVisor/Docker per leaf | [Sandboxing](docs/en/user-guide/sandboxing.md) |
155
156
  | **Durable timers** | Agents schedule their own wake-ups; survive restarts; one-shot or recurring | [Timers](docs/en/user-guide/timers.md) |
156
- | **Context compaction** | Auto-summarize old turns (never splits a tool-call pair); `recall_compacted` to pull originals back | [Compaction](docs/en/user-guide/compaction.md) |
157
- | **Send-context projection** | Opt-in: feed a per-send plain-text projection of finished sends (derived `pl_project_messages`) instead of verbatim history; `pl_messages` stays immutable; `recall_send` to re-expand | [Projection](docs/en/user-guide/send-context-projection.md) |
157
+ | **Context — representation** | Record/render each finished send **verbatim** or as a terse per-send **projection** (derived `pl_project_messages`); `pl_messages` stays immutable; `recall_send` re-expands | [Projection](docs/en/user-guide/send-context-projection.md) |
158
+ | **Context — fold strategy** | Compact older history once over budget: **LLM summary** or **agentic** (also writes notes); pluggable `FoldStrategy`; never splits a tool pair; `recall_compacted` re-expands | [Compaction](docs/en/user-guide/compaction.md) |
158
159
  | **Durability ops** | Portable migration-version table, retention/prune, VACUUM, `export_session`/`import_session`, graceful `aclose()` | [Sessions](docs/en/user-guide/sessions.md) |
159
160
  | **Observability** | Typed `seq`-ordered events → durable JSONL + `replay`, Prometheus/StatsD metrics, OpenTelemetry spans | [Observability](docs/en/user-guide/observability.md) |
160
161
  | **MCP tools** | Surface a Model Context Protocol server's tools as power-loop tools | [Extending](docs/en/user-guide/extending-tools.md) |
@@ -200,6 +201,28 @@ result = await loop.send(user_text, session_id=session_id)
200
201
 
201
202
  Under the hood the loop keeps a per-session **active-window cache** — but it caches only the *durable* projection, validated by a monotonic `next_seq` token, so it's a pure accelerator: a cold loop with an empty cache produces byte-for-byte the same prompts (proven by a warm-vs-cold conformance test, including the recall/compaction/prompt-edit edge cases).
202
203
 
204
+ ### Context engineering — two orthogonal axes you choose (and can implement yourself)
205
+
206
+ Long conversations outgrow the window. Most libraries give you *one* fixed compaction behavior; power-loop (3.0) splits it into two independent, config-driven axes:
207
+
208
+ - **Representation** — how each *finished send* is recorded & rendered: `VerbatimRepresentation` (full, byte-identical history) or `ProjectedRepresentation` (a terse per-send plain-text projection). The original detail always stays in the immutable `pl_messages` audit log.
209
+ - **Fold strategy** — how *older* history is compacted once the rendered prefix crosses the budget: `LLMSummaryFold` (one summary call) or `AgenticFold` (a bounded tool loop that also persists durable facts as notes).
210
+
211
+ ```python
212
+ from power_loop import (
213
+ StatefulAgentLoop, AgentLoopConfig,
214
+ ProjectedRepresentation, AgenticFold, # mix & match either axis — or pass your own impl
215
+ )
216
+
217
+ cfg = AgentLoopConfig(
218
+ representation=ProjectedRepresentation(max_chars=300), # terse projection (or VerbatimRepresentation)
219
+ fold_strategy=AgenticFold(keep_last_sends=4), # summarize older sends + write notes
220
+ )
221
+ loop = StatefulAgentLoop(llm=llm, dsn="app.db", config=cfg)
222
+ ```
223
+
224
+ Any representation composes with any fold strategy, and each axis is a small `Protocol` you can implement yourself. A fold always keeps **whole sends** (it never splits an atomic tool-call/result pair), and the model can call `recall_send(send_index=N)` / `recall_compacted()` to pull the full original detail back from the audit log. (The two classes above are public but **provisional** — added in 3.0, not yet frozen into `STABLE_API`; `AgentLoopConfig` itself is Stable.)
225
+
203
226
  ### Deterministic multi-agent workflows — that the model can author, and that survive a crash
204
227
 
205
228
  Sub-agent delegation is *model-driven* ("go do this"). When you want **code-driven, deterministic** orchestration — fan out over a list, branch on a result, run a pipeline — describe it as a `WorkflowSpec` and let the engine interpret it. The only LLM calls are the leaves; `sequence`/`parallel`/`foreach`/`branch` are plain code.
@@ -281,7 +304,7 @@ await register_mcp_tools(registry, client, prefix="fs.") # MCP tools → power
281
304
 
282
305
  The seam is a tiny `MCPToolSource` Protocol, so the `mcp` SDK is optional and any client works.
283
306
 
284
- > More: hard token budgets, structured output, crash recovery, memory, the blackboard — see [`examples/`](examples/README.md) (40 runnable programs) and the [docs](docs/en/index.md).
307
+ > More: hard token budgets, structured output, crash recovery, memory, the blackboard — see [`examples/`](examples/README.md) (43 runnable programs) and the [docs](docs/en/index.md).
285
308
 
286
309
  ---
287
310
 
@@ -289,7 +312,7 @@ The seam is a tiny `MCPToolSource` Protocol, so the `mcp` SDK is optional and an
289
312
 
290
313
  power-loop is a **kernel**, not a platform — that's the whole trade-off.
291
314
 
292
- - **vs. LangChain / LangGraph / LlamaIndex / CrewAI / AutoGen** — those are batteries-included frameworks with large ecosystems (connectors, vector stores, integrations) and heavy dependency trees. power-loop deliberately ships **none of that**: a compact (~20k-line) pure-stdlib core with zero runtime dependencies, and you bring your own tools (or an MCP server). You get durable sessions across SQLite/PG/MySQL, crash-resumable workflows, and real sandbox seams out of the box; you do **not** get a bundled RAG stack or 100 connectors.
315
+ - **vs. LangChain / LangGraph / LlamaIndex / CrewAI / AutoGen** — those are batteries-included frameworks with large ecosystems (connectors, vector stores, integrations) and heavy dependency trees. power-loop deliberately ships **none of that**: a compact (~24k-line) pure-stdlib core with zero runtime dependencies, and you bring your own tools (or an MCP server). You get durable sessions across SQLite/PG/MySQL, crash-resumable workflows, and real sandbox seams out of the box; you do **not** get a bundled RAG stack or 100 connectors.
293
316
  - **Choose power-loop** when you want to *embed* an agent in an existing app, keep your dependency surface tiny, pick your own database, and care about durability + isolation + a stable contract.
294
317
  - **Choose a framework** when you want batteries included, a big integration catalog, and don't mind the weight.
295
318
 
@@ -321,7 +344,7 @@ Python 3.10+. See [Getting Started](docs/en/getting-started.md). Optional extras
321
344
 
322
345
  ## Stability & SemVer
323
346
 
324
- As of **1.0**, the **STABLE** API (listed in `power_loop.STABLE_API`) is under SemVer: a breaking change requires a major bump (`2.0.0`), enforced by a frozen-baseline test in CI — including the flagship `StatefulAgentLoop` *and the LLM contract needed to construct it*. Error `.code` strings are frozen too.
347
+ Since **1.0**, the **STABLE** API (listed in `power_loop.STABLE_API`) is under SemVer: a breaking change requires a major bump, enforced by a frozen-baseline test in CI — including the flagship `StatefulAgentLoop` *and the LLM contract needed to construct it*. Error `.code` strings are frozen too. The two majors since (2.0 pluggable async storage, 3.0 orthogonal context axes) were exactly that policy in action — breaking changes earned a major bump, each documented in the [Changelog](CHANGELOG.md).
325
348
 
326
349
  | Tier | Meaning |
327
350
  |---|---|
File without changes
File without changes
File without changes