Metadata-Version: 2.4
Name: symfonic-core
Version: 8.13.0
Summary: Agent Graph Framework — core graph primitives for symfonic
Project-URL: Homepage, https://core.symfonic.dev
Project-URL: Documentation, https://core.symfonic.dev
Project-URL: Changelog, https://core.symfonic.dev/CHANGELOG/
Author: AmSiDev
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: langchain-core<1.0,>=0.3
Requires-Dist: langgraph<1.0,>=0.2
Requires-Dist: pydantic<3.0,>=2.0
Provides-Extra: agent-api
Requires-Dist: fastapi>=0.115; extra == 'agent-api'
Requires-Dist: jinja2>=3.1; extra == 'agent-api'
Requires-Dist: python-jose[cryptography]>=3.3; extra == 'agent-api'
Requires-Dist: sse-starlette>=2.0; extra == 'agent-api'
Provides-Extra: agent-dev
Requires-Dist: argon2-cffi>=23.1; extra == 'agent-dev'
Requires-Dist: email-validator>=2.1; extra == 'agent-dev'
Requires-Dist: fastapi>=0.115; extra == 'agent-dev'
Requires-Dist: fpdf2>=2.7; extra == 'agent-dev'
Requires-Dist: jinja2>=3.1; extra == 'agent-dev'
Requires-Dist: mypy>=1.13; extra == 'agent-dev'
Requires-Dist: openpyxl>=3.1; extra == 'agent-dev'
Requires-Dist: pdfplumber>=0.11; extra == 'agent-dev'
Requires-Dist: pillow>=10.0; extra == 'agent-dev'
Requires-Dist: pydantic-settings>=2.3; extra == 'agent-dev'
Requires-Dist: pypdf>=4.0; extra == 'agent-dev'
Requires-Dist: pytesseract>=0.3; extra == 'agent-dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'agent-dev'
Requires-Dist: pytest-cov>=5.0; extra == 'agent-dev'
Requires-Dist: pytest>=8.0; extra == 'agent-dev'
Requires-Dist: python-docx>=1.1; extra == 'agent-dev'
Requires-Dist: python-jose[cryptography]>=3.3; extra == 'agent-dev'
Requires-Dist: python-multipart>=0.0.9; extra == 'agent-dev'
Requires-Dist: ruff>=0.8; extra == 'agent-dev'
Requires-Dist: slowapi>=0.1.9; extra == 'agent-dev'
Requires-Dist: sse-starlette>=2.0; extra == 'agent-dev'
Requires-Dist: vulture>=2.11; extra == 'agent-dev'
Provides-Extra: anthropic
Requires-Dist: langchain-anthropic<1.0,>=0.3.20; extra == 'anthropic'
Requires-Dist: python-dotenv>=1.0; extra == 'anthropic'
Provides-Extra: ask-user
Requires-Dist: aiosqlite>=0.19; extra == 'ask-user'
Requires-Dist: langgraph-checkpoint-postgres>=0.1; extra == 'ask-user'
Requires-Dist: langgraph-checkpoint-sqlite>=0.1; extra == 'ask-user'
Requires-Dist: psycopg-pool>=3.2; extra == 'ask-user'
Requires-Dist: psycopg[binary,pool]>=3.2; extra == 'ask-user'
Requires-Dist: python-jose[cryptography]>=3.3; extra == 'ask-user'
Provides-Extra: attachments
Requires-Dist: openpyxl>=3.1; extra == 'attachments'
Requires-Dist: pdfplumber>=0.11; extra == 'attachments'
Requires-Dist: pillow>=10.0; extra == 'attachments'
Requires-Dist: pypdf>=4.0; extra == 'attachments'
Requires-Dist: pytesseract>=0.3; extra == 'attachments'
Requires-Dist: python-docx>=1.1; extra == 'attachments'
Provides-Extra: attachments-docx
Requires-Dist: python-docx>=1.1; extra == 'attachments-docx'
Provides-Extra: attachments-ocr
Requires-Dist: pillow>=10.0; extra == 'attachments-ocr'
Requires-Dist: pytesseract>=0.3; extra == 'attachments-ocr'
Provides-Extra: attachments-pdf
Requires-Dist: pdfplumber>=0.11; extra == 'attachments-pdf'
Requires-Dist: pypdf>=4.0; extra == 'attachments-pdf'
Provides-Extra: attachments-xlsx
Requires-Dist: openpyxl>=3.1; extra == 'attachments-xlsx'
Provides-Extra: aws
Requires-Dist: langchain-aws>=0.2.1; extra == 'aws'
Provides-Extra: chroma
Requires-Dist: langchain-chroma>=0.2.0; extra == 'chroma'
Provides-Extra: cli
Requires-Dist: click<8.3,>=8.0; extra == 'cli'
Requires-Dist: typer>=0.12; extra == 'cli'
Provides-Extra: deepseek
Requires-Dist: langchain-openai<1.0,>=0.3; extra == 'deepseek'
Provides-Extra: demo
Requires-Dist: aiosqlite>=0.19; extra == 'demo'
Requires-Dist: asyncpg>=0.29; extra == 'demo'
Requires-Dist: celery[redis]>=5.3; extra == 'demo'
Requires-Dist: chromadb>=0.4; extra == 'demo'
Requires-Dist: fastapi>=0.115; extra == 'demo'
Requires-Dist: fpdf2>=2.7; extra == 'demo'
Requires-Dist: httpx>=0.27; extra == 'demo'
Requires-Dist: jinja2>=3.1; extra == 'demo'
Requires-Dist: langgraph-checkpoint-postgres>=0.1; extra == 'demo'
Requires-Dist: langgraph-checkpoint-sqlite>=0.1; extra == 'demo'
Requires-Dist: matplotlib>=3.8; extra == 'demo'
Requires-Dist: motor>=3.3; extra == 'demo'
Requires-Dist: pgvector>=0.3; extra == 'demo'
Requires-Dist: psycopg-pool>=3.2; extra == 'demo'
Requires-Dist: psycopg[binary,pool]>=3.2; extra == 'demo'
Requires-Dist: python-jose[cryptography]>=3.3; extra == 'demo'
Requires-Dist: sqlalchemy[asyncio]>=2.0; extra == 'demo'
Requires-Dist: sse-starlette>=2.0; extra == 'demo'
Provides-Extra: dev
Requires-Dist: aiosqlite>=0.19; extra == 'dev'
Requires-Dist: argon2-cffi>=23.1; extra == 'dev'
Requires-Dist: asyncpg>=0.29; extra == 'dev'
Requires-Dist: build>=1.0; extra == 'dev'
Requires-Dist: celery[redis]>=5.3; extra == 'dev'
Requires-Dist: chromadb>=0.4; extra == 'dev'
Requires-Dist: click<8.3,>=8.0; extra == 'dev'
Requires-Dist: email-validator>=2.1; extra == 'dev'
Requires-Dist: fastapi>=0.115; extra == 'dev'
Requires-Dist: fpdf2>=2.7; extra == 'dev'
Requires-Dist: httpx>=0.27; extra == 'dev'
Requires-Dist: jinja2>=3.1; extra == 'dev'
Requires-Dist: langchain-anthropic<1.0,>=0.3.20; extra == 'dev'
Requires-Dist: langgraph-checkpoint-postgres>=0.1; extra == 'dev'
Requires-Dist: langgraph-checkpoint-sqlite>=0.1; extra == 'dev'
Requires-Dist: matplotlib>=3.8; extra == 'dev'
Requires-Dist: motor>=3.3; extra == 'dev'
Requires-Dist: mypy>=1.13; extra == 'dev'
Requires-Dist: openpyxl>=3.1; extra == 'dev'
Requires-Dist: pdfplumber>=0.11; extra == 'dev'
Requires-Dist: pgvector>=0.3; extra == 'dev'
Requires-Dist: pillow>=10.0; extra == 'dev'
Requires-Dist: psycopg-pool>=3.2; extra == 'dev'
Requires-Dist: psycopg[binary,pool]>=3.2; extra == 'dev'
Requires-Dist: pydantic-settings>=2.3; extra == 'dev'
Requires-Dist: pypdf>=4.0; extra == 'dev'
Requires-Dist: pytesseract>=0.3; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: python-docx>=1.1; extra == 'dev'
Requires-Dist: python-dotenv>=1.0; extra == 'dev'
Requires-Dist: python-jose[cryptography]>=3.3; extra == 'dev'
Requires-Dist: python-multipart>=0.0.9; extra == 'dev'
Requires-Dist: ruff>=0.8; extra == 'dev'
Requires-Dist: slowapi>=0.1.9; extra == 'dev'
Requires-Dist: sqlalchemy[asyncio]>=2.0; extra == 'dev'
Requires-Dist: sse-starlette>=2.0; extra == 'dev'
Requires-Dist: typer>=0.12; extra == 'dev'
Requires-Dist: vulture>=2.11; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-gen-files>=0.5; extra == 'docs'
Requires-Dist: mkdocs-literate-nav>=0.6; extra == 'docs'
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
Requires-Dist: mkdocs>=1.6; extra == 'docs'
Requires-Dist: mkdocstrings[python]>=0.26; extra == 'docs'
Requires-Dist: pymdown-extensions>=10.0; extra == 'docs'
Provides-Extra: embedding-openai
Requires-Dist: langchain-openai<1.0,>=0.3; extra == 'embedding-openai'
Provides-Extra: embedding-sentence-transformers
Requires-Dist: sentence-transformers<4.0,>=2.0; extra == 'embedding-sentence-transformers'
Provides-Extra: entity-linker
Requires-Dist: spacy<4.0,>=3.7; extra == 'entity-linker'
Provides-Extra: entity-linker-llm
Provides-Extra: entity-linker-spacy
Requires-Dist: spacy<4.0,>=3.7; extra == 'entity-linker-spacy'
Provides-Extra: google
Requires-Dist: langchain-google-genai>=2.0; extra == 'google'
Provides-Extra: init
Requires-Dist: jinja2>=3.1; extra == 'init'
Provides-Extra: kimi
Requires-Dist: langchain-openai<1.0,>=0.3; extra == 'kimi'
Provides-Extra: knowledge
Requires-Dist: chromadb>=0.4; extra == 'knowledge'
Provides-Extra: mcp
Requires-Dist: httpx>=0.27; extra == 'mcp'
Provides-Extra: mongodb
Requires-Dist: motor>=3.3; extra == 'mongodb'
Provides-Extra: ollama
Requires-Dist: langchain-ollama>=0.3; extra == 'ollama'
Provides-Extra: openai
Requires-Dist: langchain-openai<1.0,>=0.3; extra == 'openai'
Provides-Extra: openrouter
Requires-Dist: langchain-openai<1.0,>=0.3; extra == 'openrouter'
Provides-Extra: otel
Requires-Dist: opentelemetry-api<2.0,>=1.25; extra == 'otel'
Requires-Dist: opentelemetry-exporter-otlp-proto-grpc<2.0,>=1.25; extra == 'otel'
Requires-Dist: opentelemetry-sdk<2.0,>=1.25; extra == 'otel'
Provides-Extra: postgres
Requires-Dist: asyncpg>=0.29; extra == 'postgres'
Requires-Dist: langgraph-checkpoint-postgres>=0.1; extra == 'postgres'
Requires-Dist: pgvector>=0.3; extra == 'postgres'
Requires-Dist: psycopg-pool>=3.2; extra == 'postgres'
Requires-Dist: psycopg[binary,pool]>=3.2; extra == 'postgres'
Provides-Extra: prompts
Requires-Dist: jinja2>=3.1; extra == 'prompts'
Provides-Extra: testing
Requires-Dist: argon2-cffi>=23.1; extra == 'testing'
Requires-Dist: email-validator>=2.1; extra == 'testing'
Requires-Dist: fpdf2>=2.7; extra == 'testing'
Requires-Dist: openpyxl>=3.1; extra == 'testing'
Requires-Dist: pdfplumber>=0.11; extra == 'testing'
Requires-Dist: pillow>=10.0; extra == 'testing'
Requires-Dist: pydantic-settings>=2.3; extra == 'testing'
Requires-Dist: pypdf>=4.0; extra == 'testing'
Requires-Dist: pytesseract>=0.3; extra == 'testing'
Requires-Dist: pytest-asyncio>=0.24; extra == 'testing'
Requires-Dist: pytest-cov>=5.0; extra == 'testing'
Requires-Dist: pytest>=8.0; extra == 'testing'
Requires-Dist: python-docx>=1.1; extra == 'testing'
Requires-Dist: python-jose[cryptography]>=3.3; extra == 'testing'
Requires-Dist: python-multipart>=0.0.9; extra == 'testing'
Requires-Dist: slowapi>=0.1.9; extra == 'testing'
Description-Content-Type: text/markdown

# symfonic-core

Hierarchical Memory System for AI Agents

## Overview

`symfonic-core` is a Python framework for building AI agents with persistent,
structured memory. It implements a five-layer Hierarchical Memory System (HMS)
inspired by cognitive science: Semantic, Episodic, Working, Prospective, and
Procedural memory -- all backed by Postgres for durable persistence.

The framework goes beyond simple retrieval-augmented generation. On every
query, it hydrates context from multiple memory layers, runs spreading
activation across the knowledge graph to surface related facts, executes the
LLM, extracts structured memory operations from the response, and consolidates
them back into the appropriate layers. A Deep Sleep consolidation engine
strengthens recurring knowledge, prunes stale nodes, and generates meta-nodes
from clusters -- running as a nightly task or on manual trigger.

`symfonic-core` ships a set of cooperating subpackages in a single distribution:

| Subpackage | Role |
|-----------|------|
| `symfonic.core` | Graph execution engine: nodes, edges, DI container, tools, streaming |
| `symfonic.memory` | 5-layer HMS: episodic, semantic, procedural, prospective, working |
| `symfonic.agent` | Orchestration: wires core + memory, exposes `SymfonicAgent`, FastAPI bridge |
| `symfonic.knowledge` | Semantic knowledge retrieval (RAG) layer |
| `symfonic.observability` | Cross-cutting metrics, cost tracking, and OpenTelemetry adapters |
| `symfonic.tools` | Built-in tool integration adapters |
| `symfonic.infra` | Infrastructure abstractions for external services |
| `symfonic.diagnostics` | `symfonic doctor` — adopter-config audit |
| `symfonic.cli` | Scaffolding CLI (`symfonic init`) and agent command line |

## Quick Start

### Installation

```bash
pip install symfonic-core
```

With Anthropic provider and FastAPI support:

```bash
pip install "symfonic-core[anthropic,agent-api]"
```

symfonic-core is published on [PyPI](https://pypi.org/project/symfonic-core/),
so no extra index configuration is required.

### Environment Variables

| Variable | Required | Description |
|----------|----------|-------------|
| `ANTHROPIC_API_KEY` | For real LLM | Anthropic API key |
| `POSTGRES_DSN` | For persistence | PostgreSQL connection string |
| `MONGODB_URI` | Optional | MongoDB URI for graph visualization secondary |

### Minimal Code Example

```python
import asyncio
from symfonic.agent import SymfonicAgent, FrameworkTenantScope
from symfonic.core.testing import MockModelProvider

async def main() -> None:
    agent = SymfonicAgent(model_provider=MockModelProvider(response="Hello!"))
    scope = FrameworkTenantScope(tenant_id="acme")
    response = await agent.run("What do you know about us?", scope=scope)
    print(response.final_response)
    print(f"Memory entries used: {response.memory_entries_used}")
    print(f"Duration: {response.duration_ms:.0f}ms")

asyncio.run(main())
```

## Supported Providers

symfonic-core ships with several model providers. Each is optional -- only install
the package for the provider you need.

| Provider | Package | Install Extra | Thinking | Streaming | Limitations |
|----------|---------|--------------|----------|-----------|-------------|
| Anthropic | langchain-anthropic | `[anthropic]` | Yes (extended thinking) | Yes | Requires API key |
| OpenAI | langchain-openai | `[openai]` | Partial (o1/o3 only) | Yes | No thinking blocks for GPT-4o |
| DeepSeek | langchain-openai | `[deepseek]` | Yes (R1 reasoning) | Yes | Reasoning format differs from Anthropic thinking blocks -- ThinkingDeltaEvent may not capture all reasoning |
| Google Gemini | langchain-google-genai | `[google]` | Yes (2.5 thinking) | Yes | Thinking format not yet standardized in LangChain |
| AWS Bedrock | langchain-aws | `[aws]` | Yes (Claude 3.7+/4) | Yes | **Any** Bedrock model (Claude, Llama, Nova, Mistral, …) via the Converse API. Wire family auto-detected per model id; non-Claude models are wire-neutral. `LLM_MODEL` must be a Bedrock model/inference-profile id; region + creds via the boto3 chain. Claude cost telemetry uses Anthropic list rates; other vendors need a pricing override. Native `cache_control` is not wire-compatible with Bedrock `cachePoint` |
| OpenRouter | langchain-openai | `[openrouter]` | Per model | Yes | Gateway to many vendors via one OpenAI-compatible endpoint. `LLM_MODEL` is a fully-qualified id (`anthropic/claude-sonnet-4.5`); auth via `OPENROUTER_API_KEY`. Wire-neutral (`unknown`) family |
| Ollama (local) | langchain-ollama | `[ollama]` | No | Yes | No cloud API -- runs locally. No thinking support. Memory/speed depends on hardware |
| AnthropicOAuth | (built-in) | `[anthropic]` | Yes | Yes | **DEV/TEST ONLY** — uses Claude Code OAuth token against a Claude Pro/Max subscription. Not suitable for multi-tenant production (no per-tenant billing, interactive token refresh). |

`AnthropicOAuthProvider` lives in `src/symfonic/core/oauth_provider.py` and
requires no `ANTHROPIC_API_KEY`. Use it for local development and running the
full pipeline (autonomous learning, metacognition) against a real LLM without
provisioning API credits. Never deploy it to production.

```python
from symfonic.core.oauth_provider import AnthropicOAuthProvider
agent = SymfonicAgent(model_provider=AnthropicOAuthProvider())
```

### Provider Usage

```python
# Anthropic (default)
from symfonic.core.providers import AnthropicProvider
agent = SymfonicAgent(model_provider=AnthropicProvider())

# OpenAI
from symfonic.core.providers import OpenAIProvider
agent = SymfonicAgent(model_provider=OpenAIProvider())

# DeepSeek
from symfonic.core.providers import DeepSeekProvider
agent = SymfonicAgent(model_provider=DeepSeekProvider(api_key="sk-..."))

# Google Gemini
from symfonic.core.providers import GoogleProvider
agent = SymfonicAgent(model_provider=GoogleProvider())

# Local Ollama
from symfonic.core.providers import OllamaProvider
agent = SymfonicAgent(model_provider=OllamaProvider())
```

## The Pentad -- 5 Memory Layers

| Layer | Purpose | Backend | Persistence |
|-------|---------|---------|-------------|
| Semantic | Stable knowledge graph -- facts, entities, relationships | Postgres Graph (+ optional MongoDB secondary) | Persistent |
| Episodic | Timestamped interaction history and events | Postgres Vector | Persistent |
| Working | Active reasoning context with 24h TTL thinking nodes | Postgres Graph (short-TTL) | Persistent |
| Prospective | Future plans, reminders, scheduled triggers | Postgres Graph | Persistent |
| Procedural | Learned skills, workflows, tool routing | Postgres Graph + SkillRegistry | Persistent |

All five layers share the `BaseMemoryStore` protocol with a uniform
`write / retrieve / delete` interface and strict per-tenant isolation via
`TenantScope`.

> **v8.0 BREAKING — hierarchical `TenantScope`.** `TenantScope` is now an
> arbitrary-depth, root-first **path** of `ScopeLevel(kind, id)` values (e.g.
> `org → brand → conversation`), not a fixed `(tenant_id, sub_tenant_id)` shape.
> A query at a path sees memories at its own path and every ancestor prefix and
> NEVER a sibling — prefix isolation is an always-on security boundary that
> closes cross-session bleed. The flat fields are **retained and
> backward-compatible** (computed off the path), so most adopters need no source
> change; bare `TenantScope(tenant_id=...)` still works but is **deprecated** —
> switch to the factories (`root(...).child(...)`, `from_path([...])`). Opt-in
> scope-distance retrieval weighting blends ancestor memories down-weighted by
> hierarchy distance. See
> [`docs/concepts/hierarchical-tenant-scope.md`](https://core.symfonic.dev/concepts/hierarchical-tenant-scope/),
> the `examples/jarvio_reference/` demonstration,
> and the `## [8.0.0]` migration guide in [`CHANGELOG.md`](https://core.symfonic.dev/CHANGELOG/).

## Key Features

Every capability below is default-preserving — `FrameworkConfig()` reproduces
pre-upgrade behaviour byte-for-byte. Adopt features one knob at a time. For a
grouped walk-through with opt-in examples, see
[`docs/FEATURES.md`](https://core.symfonic.dev/FEATURES/).

1. **Agent and Prompting** — `SymfonicAgent` + `FrameworkConfig` + `DomainTemplate`.
   Full HMS system prompt (9 sections, ~1800 tokens) or JIT mode
   (≤230 tokens). `DomainTemplate.description` reaches both prompts since
   v7.0.2 (capped by `domain_description_max_chars`). TOOL DISCIPLINE
   directive (v6.1.10) in both templates.
   **v7.2 / Item 8:** `FrameworkConfig.context_strategy="jit"|"stratified"`
   selects between the `JITContextManager` (~210-token prompt + reactive
   hydration) and `StratifiedHMSContextManager` (L0/L1/L2 layered prompt
   with inline hydration) via the new `ContextManager` protocol. Default
   `None` infers from the legacy `jit_context` flag (byte-identical
   pre-v7.2 behaviour). JIT mode auto-registers the `hydrate_context`
   tool on the agent so the LLM has a real on-demand memory escape
   hatch — no manual wiring needed.
   **v7.2 / Item 9 (experimental):** `agent.register_interrupt()` +
   `agent.interrupt()` generalise the `ask_user` pause/resume envelope
   to any named human-in-the-loop step (approvals, sign-offs,
   review gates) with a caller-supplied Pydantic payload. Same
   HMAC + scope-bound + single-use security envelope as `ask_user`.
   Gated by `experimental_interrupt=True`; ask_user keeps its
   dedicated codepath so the event stream stays byte-identical when
   only the built-in case is exercised.
   **Structured output:** pass a Pydantic `response_model` to `run()` and the
   validated instance lands on `AgentResponse.structured` (the prose answer
   stays on `final_response`). Provider-neutral; fail-loud via
   `StructuredOutputError`.
   [Guide](https://core.symfonic.dev/guides/01-quickstart/) /
   [Structured Output](https://core.symfonic.dev/concepts/structured-output/).

2. **5-Pentad Memory** — Semantic (knowledge graph), Episodic (narrative log),
   Working (short-term with 24h TTL), Procedural (learned skills), Prospective
   (reminders). Five `POST /api/v1/memories/{layer}` endpoints (v6.1.4) plus
   GET / PATCH / DELETE. All tenant-scoped. [Guide](https://core.symfonic.dev/guides/02-memory-persistence/).

3. **Retrieval and Hydration** — Spreading activation with N-hop BFS
   (`hydration_max_hops`, v7.0), importance threshold (`hydration_threshold`,
   v6.1.8), raw-turns cap (`working_memory_raw_turns`), STM summary
   (`stm_summary_mode` off/extractive/llm, v7.0), credential scrub
   (`credential_patterns`, v6.2). `hydrate_context` JIT escape hatch tool.

4. **Tooling and Routing** — `tool_trigger_keywords` for in-prompt manifest
   filtering (v6.1.8), `tool_routing_mode` for wire-level `tools=` narrowing
   (v7.0.1, observe/enforce), `always_include_tools` (v7.0.3),
   `jit_manifest_token_budget` for JIT manifest compression (v6.2 T04),
   `lazy_tooling` for procedural-memory-driven tool selection.
   **`@symfonic_tool` (GA):** `from symfonic.core import symfonic_tool` —
   `@symfonic_tool(scope=..., routing_mode=..., visibility=...)` derives the
   tool's JSON schema from type hints + docstrings and threads the symfonic
   metadata into the existing `ToolRegistry` — zero manifest plumbing.
   Async-generator tools yield `ToolProgressEvent`s for streaming progress.
   No flag required (the former `experimental_tool_decorator` is a deprecated
   no-op). **Tool-call steering:** an `on_before_tool_call` callback can
   refuse a call and hand the model corrective feedback it self-corrects
   against, or cancel it outright
   ([Guide 18](https://core.symfonic.dev/guides/18-tool-call-steering/)).
   **v7.1.x / Item 10:** `otel_enabled=True` ships an opt-in
   OpenTelemetry exporter (bridge-adapter pattern, lazy import) that
   emits `symfonic.run` root spans + node/llm/tool/interrupt child
   spans. Zero `opentelemetry` modules loaded when disabled.
   [Guide 6](https://core.symfonic.dev/guides/06-lazy-tooling/) /
   [Guide 7](https://core.symfonic.dev/guides/07-intent-and-tool-routing/).

5. **Intent Classification** — `IntentClassifier` labels each turn
   `knowledge`/`action`/`ambiguous` before hydration via deterministic
   verb+keyword matching (zero LLM cost, v7.0). Unicode-aware + NFC-normalised
   tokenizer (v7.0.3) — Spanish / German / French / Portuguese / Chinese /
   Japanese classify correctly. `intent_filter_mode` off/observe/enforce.

6. **Fabrication Detection** — Cross-reference detector flags UUIDs, kv pairs
   (`task_id=abc`), verb claims (`"I spawned X"`), and context-anchored
   short-hex identifiers (v7.0.3) that are not grounded by a fired tool.
   Per-verb tool grounding (`_VERB_TO_TOOL_HINTS`). Language-agnostic
   `intent_action_no_tool` finding (v7.0.6) fires when the `IntentVerdict`
   says "action" and zero tools ran — catches Spanish / French / German /
   Portuguese fabrications the English-only verb regex misses. Modes:
   off / observe / revise / refuse, gated by
   `fabrication_refuse_min_confidence` (default 0.8). Typed
   `FabricationDetectedEvent` (v7.0.7) carries `run_id`, `action_taken`,
   `refuse_threshold`, `max_finding_confidence`, and 240-char draft /
   replacement previews so SRE dashboards can tune the threshold from
   telemetry without recomputing the verdict.
   [Guide](https://core.symfonic.dev/guides/08-fabrication-detection/).

7. **Consolidation (Deep Sleep)** — `SleepConsolidator` runs 11 distinct
   phases: strengthen, cooccurrence, tag_risk, pending_edges, prune_orphans,
   meta_nodes, soul_corrections, working_ttl, decay_importance,
   episodic_summary, synthetic_links, procedural_promotion. Three entrypoints:
   `quick_nap` (every-N-turns), `run` (full roster), `nightly_nap` (v7.0 T12,
   operator-scheduled). `ConsolidationReport.phases_run` for observability.
   **v7.2 / Item 12:** opt-in `entity_links` phase (`enable_entity_linker`)
   autonomously mints `Entity:<kind>:<surface>` semantic nodes and `MENTIONS`
   edges from natural-language episodic content, so spreading activation
   crosses months/years without manual `semantic.link()` plumbing. Pluggable
   extractor (`regex` default, `spacy` / `llm` opt-in via extras).
   [Guide](https://core.symfonic.dev/guides/09-consolidation-and-deep-sleep/).

8. **Observability** — `LLMStartEvent` (engine view), `LLMPreCallEvent`
   (wire view, v6.1.7), `LLMEndEvent` (response), `ToolRoutingDecisionEvent`
   (v7.0.1), `ExtensionEvent` for intent / fabrication. Optional hooks are
   `hasattr`-gated so legacy handlers cost nothing. `TokenBudgetTracker` +
   `PostgresBudgetStore` for multi-replica correctness. Episodic telemetry
   sink (`episodic_telemetry_sink`, v6.1).
   [Guide](https://core.symfonic.dev/guides/03-streaming-and-callbacks/).

9. **Security and Multi-tenancy** — `FrameworkTenantScope` enforced at the
   backend level. `create_agent_router()` raises `RuntimeError` in production
   without `set_tenant_auth_verifier()`. Three-tier credential scrubbing
   (v6.1.9): extraction directive + write-time + read-time. Tunable patterns
   via `credential_patterns` (v6.2 T01). GDPR Article 17 + 20 endpoints.
   [Guide](https://core.symfonic.dev/guides/05-production/).

10. **Streaming** — `stream_text()` (clean strings), `stream_typed()` (typed
    events), `stream()` (raw `StreamChunk`). `ExtractionFilter` +
    `CitationFilter` strip `<MEMORY_EXTRACT>` / `[semantic]` tags safely across
    chunk boundaries. Full event taxonomy in
    [concepts/streaming-events.md](https://core.symfonic.dev/concepts/streaming-events/).

11. **Domain Plugins** — `BaseDomainPlugin` + `DomainTemplate` with
    `core_preferences`, `soul_schema`, `tool_manifest`, `tool_trigger_keywords`,
    `identifier_keys` (v7.0 fabrication lexicon), `monitoring`. Store Copilot starter
    (`symfonic init --components store-starter`) bundles 8 sales/inventory tools +
    guardrails as a reference implementation. [Guide](https://core.symfonic.dev/guides/04-plugins-and-domains/)
    / [Store Copilot Starter](https://core.symfonic.dev/guides/store-starter/).

## Configuration Matrix

Every `FrameworkConfig` field grouped by cluster with default value. This
table is the source of truth for the v7.0.3 surface; verified against
`src/symfonic/agent/config.py`. See [`docs/FEATURES.md`](https://core.symfonic.dev/FEATURES/) for
opt-in examples and [`docs/guides/migrate-6x-to-7x.md`](https://core.symfonic.dev/guides/migrate-6x-to-7x/)
for the v7.0 upgrade path.

| Cluster | Field | Default |
|---------|-------|---------|
| Agent | `enable_hms_prompt` | `False` |
| Agent | `jit_context` | `False` |
| Agent | `memory_extract_cadence` | `1` |
| Agent | `activation_log_mode` | `"always"` |
| Agent | `domain_description_max_chars` | `(2000, 400)` |
| Agent | `reflection_prompt` | `None` |
| Agent | `self_reflection` | `False` |
| Memory | `enabled_layers` | `{working,episodic,semantic,procedural,prospective}` |
| Memory | `auto_hydrate` | `True` |
| Memory | `auto_consolidate` | `True` |
| Retrieval | `spreading_activation` | `True` |
| Retrieval | `hydration_max_hops` | `0` |
| Retrieval | `hydration_threshold` | `0.0` |
| Retrieval | `working_memory_raw_turns` | `None` |
| Retrieval | `stm_summary_mode` | `"off"` |
| Retrieval | `credential_patterns` | `None` |
| Tooling | `lazy_tooling` | `True` |
| Tooling | `tool_routing_mode` | `"off"` |
| Tooling | `always_include_tools` | `("hydrate_context",)` |
| Tooling | `jit_manifest_token_budget` | `0` |
| Intent | `intent_filter_mode` | `"off"` |
| Fabrication | `fabrication_check_mode` | `"off"` |
| Fabrication | `fabrication_refuse_min_confidence` | `0.8` |
| Consolidation | `quick_nap_interval` | `0` |
| Consolidation | `nightly_nap_enabled` | `False` |
| Consolidation | `nightly_nap_cron` | `"0 3 * * *"` |
| Consolidation | `phase1_spreading_weight` | `0.5` |
| Consolidation | `synthetic_link_min_co_count` | `2` |
| Consolidation | `promotion_min_pattern_count` | `3` |
| Consolidation | `promotion_recency_days` | `30` |

Additional fields (episodic summarization, metacognition, scheduler,
skill auto-approve) are documented in
[`docs/FEATURES.md`](https://core.symfonic.dev/FEATURES/) to keep this matrix at the 30-row cap.

## Scaffolding (`symfonic init`)

`symfonic init <name> --components <list>` generates a production-ready project.
Available components (9 total): `fastapi`, `webapp`, `docker-compose`, `auth`, `chats`, `admin`, `orgs`, `onboarding`, `store-starter`. A bare `symfonic init` uses the default set — the eight infrastructure components (everything except `store-starter`) — and scaffolds the generic `app/domains/starter/` domain. Example domains such as `store-starter` are opt-in and mutually exclusive with the generic starter.

The `store-starter` component adds an opt-in Store Copilot domain plugin (convenience-store sales & inventory assistant):
- 8 tools: `top_sellers`, `sales_trend`, `sales_chart`, `revenue_summary`, `product_lookup`, `list_low_stock`, `register_product`, `update_stock`
- `CorePreferences` guardrails: read-only access to any connected store DB, confirm-before-writing for `register_product` / `update_stock`, and cite-the-numbers on every sales claim
- An Insights charts dashboard (`/api/v1/store/insights`), a seeded demo dataset, dev memory seed script, and smoke tests

```bash
symfonic init my-store --components fastapi,webapp,docker-compose,auth,store-starter
```

`store-starter` and the default `starter` domain plugin are mutually exclusive — selecting it omits `app/domains/starter/` and ships `app/domains/store/` instead.

## API Endpoints

The FastAPI router (`create_agent_router`) mounts these endpoints:

| Method | Path | Description |
|--------|------|-------------|
| POST | `/chat` | Synchronous agent execution, returns `AgentResponse` |
| POST | `/stream` | SSE streaming, yields `StreamChunk` events |
| POST | `/stream/typed` | SSE stream of typed events (`TextDeltaEvent`, `ToolCallStartEvent`, `MemoryExtractedEvent`, `ConsolidationScheduledEvent`, etc.) — see [Streaming Events](https://core.symfonic.dev/concepts/streaming-events/) |
| GET | `/chats` | List chat sessions for tenant |
| GET | `/memories/status` | Memory block status per label |
| GET | `/memories` | List all semantic memory nodes |
| POST | `/memories/semantic` | Create a permanent fact node (v6.1.4) |
| POST | `/memories/episodic` | Create a timestamped narrative event (v6.1.4) |
| POST | `/memories/procedural` | Create a skill / workflow node (v6.1.4) |
| POST | `/memories/working` | Push an entry to the working-memory buffer (v6.1.4) |
| POST | `/memories/prospective` | Create a trigger / reminder (v6.1.4) |
| PATCH | `/memories/{node_id}` | Update a memory node |
| DELETE | `/memories/{node_id}` | Delete a memory node |
| GET | `/procedures` | List procedural skills (approved by default; `?status=draft\|rejected\|all`) |
| GET | `/procedures/drafts` | List skills pending human review |
| POST | `/procedures/{id}/approve` | Approve a draft skill (makes it active) |
| POST | `/procedures/{id}/reject` | Reject a draft skill |
| PATCH | `/procedures/{node_id}` | Update a procedure |
| POST | `/procedures/{node_id}/toggle` | Toggle procedure active flag |
| POST | `/procedures/deduplicate` | SemanticMerge deduplication |
| POST | `/memory/consolidate` | Trigger Deep Sleep consolidation |
| GET | `/graph/nodes/{id}/neighborhood` | BFS neighborhood traversal |
| GET | `/graph/paths` | Shortest path between nodes |
| GET | `/edges` | List all edges for tenant |
| POST | `/edges` | Create edge between nodes |
| DELETE | `/edges/{edge_id}` | Delete an edge |
| GET | `/graph/clusters` | Cluster projection by label prefix |
| GET | `/memories/{node_id}` | Retrieve a single memory node |
| GET | `/procedures/{node_id}` | Retrieve a single procedure node |
| DELETE | `/procedures/{node_id}` | Delete a procedure node (cascades edges) |
| GET | `/tenants/me/export` | GDPR Art. 20 — export all tenant data as JSON (rate-limited: 5/day) |
| DELETE | `/tenants/me/data` | GDPR Art. 17 — erase all tenant data (requires `?confirmation=DELETE-<tenant_id>`, rate-limited: 1/hour) |
| GET | `/billing/usage` | Token usage for today + current month |
| GET | `/billing/limits` | Budget ceilings + headroom for tenant |
| GET | `/admin/audit` | Paginated audit log (admin/org-owner scoped) † |
| POST | `/admin/tenants/{tenant_id}/budget` | Set / update tenant daily + monthly budget limits † |
| GET | `/admin/tenants/{tenant_id}/usage` | Read tenant's current usage window † |
| POST | `/admin/tenants/{tenant_id}/budget/reset` | Zero the in-memory budget tracker for a tenant † |

† Admin endpoints require JWT `role="admin"` OR org-owner membership
(`Membership.role == "owner"`). Non-admin callers receive `403 Forbidden`.

The full demo (`examples/full_demo/`) adds additional endpoints for metrics,
traces, analytics, graph visualization, billing, sub-agent findings, and
scheduled tasks.

## Architecture

The Thalamic pipeline runs four stages per turn:

```
Query --> SymfonicAgent.run() / stream_typed() / stream_text()
           |
           +--> 1. Triage
           |      |-- IntentClassifier (deterministic, zero-LLM)
           |      |     verdict: knowledge / action / ambiguous
           |      |-- apply_tool_routing
           |      |     narrow tools= payload per intent verdict
           |      '-- emit ExtensionEvent(intent_classified) + ToolRoutingDecisionEvent
           |
           +--> 2. Hydrate
           |      |-- Retrieve from enabled layers (semantic/episodic/procedural/prospective)
           |      |-- SpreadingActivation.expand() N-hop BFS with decay
           |      |-- Scrub credential-shaped keys (_scrub_credential_keys)
           |      '-- Format context string with activation_log metadata
           |
           +--> 3. LLM Execution (AgentRuntime / LangGraph)
           |      |-- HMS system prompt (9 sections, with TOOL DISCIPLINE)
           |      |-- Or JIT prompt (<=230 tokens + hydrate_context escape hatch)
           |      |-- LLMStartEvent (engine view) / LLMPreCallEvent (wire view)
           |      |-- EventTranspiler: ThinkingDelta / TextDelta / ToolCall events
           |      |-- ExtractionFilter + CitationFilter strip blocks safely
           |      '-- LLM generates response + MEMORY_EXTRACT block
           |
           +--> 4. Fabrication check + Consolidation
                  |-- Fabrication detector cross-references identifiers/verbs
                  |     against tool results + hydrated context
                  |-- refuse/revise modes can replace draft
                  |-- _consolidate (background): parse ops, scrub creds, persist
                  |-- Write thinking nodes to Working memory (24h TTL)
                  |-- Write episodic turn summary (with telemetry sink)
                  '-- quick_nap every N turns / nightly_nap per scheduler
```

See [docs/concepts/architecture.md](https://core.symfonic.dev/concepts/architecture/) for the
full component diagram.

## Examples

| Example | Description |
|---------|-------------|
| `examples/tui_chat/` | Zero-infra interactive TUI playground. Runs with `MockModelProvider` (no API key, no DB). Ideal for exploring the agent loop and streaming locally. See `examples/tui_chat/README.md`. |
| `examples/full_demo/` | Full-stack demo: Postgres, MongoDB, web UI (chat, Memory Vault, graph viz, billing, analytics). |

## Demo

Run the full interactive demo with Postgres, MongoDB, and web UI:

```bash
cd examples/full_demo
# Start infrastructure
docker compose up -d  # Postgres + MongoDB
# Install dependencies
pip install -e ".[agent-api,anthropic,mongodb]"
# Run server (mock mode or with ANTHROPIC_API_KEY)
python -m examples.full_demo.server
```

Open http://localhost:8000 for the web UI with chat, Memory Vault, graph
visualization, workflows, analytics, and observability panels.

See [the Examples Catalog](https://core.symfonic.dev/concepts/examples/) for details.

## Documentation

**Start here**

| Document | Purpose |
|----------|---------|
| [Feature Catalog](https://core.symfonic.dev/FEATURES/) | One-page reference for all 11 capability clusters with opt-in examples |
| [Changelog](https://core.symfonic.dev/CHANGELOG/) | User-facing changes by release |
| [Migration 6.x → 7.x](https://core.symfonic.dev/guides/migrate-6x-to-7x/) | Upgrade path including the `enable_hms_prompt` kwarg removal |

**Guides (level-up path)**

| Guide | Description |
|-------|-------------|
| [01 Quickstart](https://core.symfonic.dev/guides/01-quickstart/) | 10-line agent with `MockModelProvider` |
| [02 Memory Persistence](https://core.symfonic.dev/guides/02-memory-persistence/) | Postgres + pgvector, 5 memory layers, tenant isolation |
| [03 Streaming & Callbacks](https://core.symfonic.dev/guides/03-streaming-and-callbacks/) | Three stream APIs, `LLMPreCallEvent`, `ToolRoutingDecisionEvent` |
| [04 Plugins & Domains](https://core.symfonic.dev/guides/04-plugins-and-domains/) | `DomainTemplate`, `BaseDomainPlugin`, guardrails, SOUL schema |
| [05 Production](https://core.symfonic.dev/guides/05-production/) | Docker stack, Celery beat, auth gate, credential scrub |
| [06 Lazy Tooling](https://core.symfonic.dev/guides/06-lazy-tooling/) | Procedural-memory-driven tool selection |
| [07 Intent & Tool Routing](https://core.symfonic.dev/guides/07-intent-and-tool-routing/) | Intent classifier, `tool_routing_mode`, i18n tokenizer |
| [08 Fabrication Detection](https://core.symfonic.dev/guides/08-fabrication-detection/) | Detector patterns, confidence thresholds, deployment stance |
| [09 Consolidation & Deep Sleep](https://core.symfonic.dev/guides/09-consolidation-and-deep-sleep/) | 11 phases, `quick_nap` vs `nightly_nap`, decay exemptions |
| [Store Copilot Starter](https://core.symfonic.dev/guides/store-starter/) | Store Copilot domain plugin with 8 sales/inventory tools + guardrails |

**Concepts**

| Document | Description |
|----------|-------------|
| [Architecture](https://core.symfonic.dev/concepts/architecture/) | Thalamic pipeline, component diagram, multi-tenant isolation |
| [Memory (HMS)](https://core.symfonic.dev/concepts/memory/) | 5 layers, hydration, spreading activation |
| [Agent Layer](https://core.symfonic.dev/concepts/agent/) | `SymfonicAgent`, `AgentResponse`, streaming events |
| [Capabilities & DI](https://core.symfonic.dev/concepts/capabilities/) | Protocol-based dependency injection |
| [Streaming Events](https://core.symfonic.dev/concepts/streaming-events/) | Event taxonomy, ordering guarantees |
| [Deep Sleep Phases](https://core.symfonic.dev/concepts/deep-sleep-phases/) | Phase reference |
| [Autonomous Learning](https://core.symfonic.dev/concepts/autonomous-learning/) | Episodic → Phase 12 → human review pipeline |
| [Examples](https://core.symfonic.dev/concepts/examples/) | Walk-through of curated examples |
| [Research Foundations](https://core.symfonic.dev/concepts/research-foundations/) | Academic basis |

## Research Foundations

symfonic-core's architecture is grounded in peer-reviewed research on agent
memory systems. See [Research Foundations](https://core.symfonic.dev/concepts/research-foundations/)
for the academic basis and where symfonic innovates beyond the literature.

## Testing

```bash
pytest tests/ -q
# With coverage
pytest --cov=symfonic.core --cov=symfonic.memory --cov=symfonic.agent --cov-report=term-missing
```

6200+ tests covering unit, integration, contract, and release-gate layers.

## Development

```bash
git clone <repo-url> && cd symfonic-core
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"

# Type checking
mypy src/

# Linting
ruff check src/ tests/
```

## License

MIT
