Skip to content

crp.core

Auto-generated reference for the crp.core subpackage.

core

crp.core

Core protocol - orchestrator, task intent, windows, sessions, config, errors.

BatchResult dataclass

Result for one item in a batch operation.

ConfigurationResolver

Resolve configuration through the 5-layer hierarchy.

resolve(**init_kwargs)

Build a CRPConfig from layers 1-4.

Parameters:

Name Type Description Default
**init_kwargs Any

Layer 4 overrides from Client() constructor.

{}

Raises:

Type Description
ValueError

If any resolved value is out of bounds.

CRPConfig dataclass

Resolved CRP configuration.

Constructed via :class:ConfigurationResolver.

enabled property

Return the enabled.

max_continuations property

Return the max continuations.

log_envelopes property

Return the log envelopes.

max_dispatch_rate property

Return the max dispatch rate.

session_timeout property

Return the session timeout.

max_windows_per_session property

Return the max windows per session.

max_total_input_tokens property

Return the max total input tokens.

max_total_output_tokens property

Return the max total output tokens.

get(key, default=None)

Return the resolved config value for key, or default if absent.

lock()

Lock immutable fields - called after session starts.

update(overrides)

Apply mutable-only overrides (Layer 5: runtime configure).

Raises ValueError if attempting to change an immutable field after lock().

BudgetExhaustedError

Bases: CRPError

Raised when a session-level budget cap is exceeded.

Covers windows-per-session, total input tokens, total output tokens, and rate-limit budgets. details typically contains limit, used, and optionally requested.

Parameters:

Name Type Description Default
message str

Human-readable description.

'Budget exhausted'
**details Any

Structured context for logging and headers.

{}

CRPError

Bases: Exception

Base exception for all CRP errors.

Each error carries a numeric code (1001-1031) per §6.8, a human-readable message, and an optional details dict for structured logging and downstream Gateway responses.

Parameters:

Name Type Description Default
code int | ErrorCode

Numeric error code or ErrorCode enum member.

required
message str

Human-readable error description.

required
details dict[str, Any] | None

Optional structured context (limits, ids, reasons, etc.).

None

ProviderError

Bases: CRPError

Raised when the underlying LLM provider returns an error.

SessionClosedError

Bases: CRPError

Raised when an operation is attempted after the session was closed.

SessionExpiredError

Bases: CRPError

Raised when an operation is attempted on an expired session handle.

CachedResult dataclass

Cached dispatch result for idempotency.

is_expired property

Return whether this object is expired.

RequestDeduplicator

Idempotency key manager with TTL-based cache.

Concurrency: session-level write lock for mutations, reads are lock-free.

stats property

Return the stats.

cache_size property

Return the current cache count.

compute_key(system_prompt, task_input, **kwargs)

Compute idempotency key from request parameters.

check(key)

Check if a non-expired result exists for this key.

Returns cached result or None.

store(key, output, report=None)

Store a dispatch result in the cache.

invalidate(key)

Remove a cached entry. Returns True if found and removed.

clear()

Clear all cached entries. Returns count of entries cleared.

SessionLock

Read-write lock with ordering for session operations.

Lock ordering (deadlock prevention): 1. Cold Storage Lock 2. Model Registry Lock 3. Session Write Lock

Write lock for: dispatch, ingest, configure, reset, close Read lock (no-op) for: session_status, estimate, preview

acquire_write()

Acquire write lock. Returns True if acquired within timeout.

release_write()

Release the session write lock.

acquire_read()

Acquire read lock (currently a no-op for reads).

release_read()

Release the read lock (no-op in the current implementation).

CRPOrchestrator

Bases: DispatchMixin, ExtractionMixin

Central dispatcher managing window lifecycle and protocol operations.

Integrates ALL CRP subsystems: - ExtractionPipeline (graduated 6-stage extraction on outputs + ingestion) - WarmStateStore (persistent fact accumulation, ranking, aging) - CKF (ContextualKnowledgeFabric - 4-mode retrieval) - Envelope builder (6-phase budget-aware fact packing) - ContinuationManager (3-way termination, gap analysis, stitching) - SecurityManager (input validation, injection detection, RBAC, etc.)

Parameters:

Name Type Description Default
provider LLMProvider | None

Primary LLM provider. llm is accepted as an alias.

None
config CRPConfig | None

Optional CRPConfig instance.

None
llm LLMProvider | None

Alias for provider (backwards compatibility).

None
model str | None

Model name for auto-detection when no provider is given.

None
**init_kwargs Any

Extra configuration passed to ConfigurationResolver.

{}

emitter property

Protocol event bus for subscribing to events.

feedback property

Access the feedback loop for fact confidence adjustments.

parallel property

Parallel fan-out engine for multi-task dispatch.

session property

Return the session.

dag property

Return the dag.

config property

Return the config.

warm_store property

Access the WarmStateStore for direct fact operations.

ckf property

Access the CKF for 4-mode retrieval queries.

compliance_audit property

Access the compliance audit trail for inspection (§7.14).

pii_scanner property

Access the PII scanner (§7.12).

consent_manager property

Access the consent manager (§7.13).

processing_records property

Access the processing record keeper - GDPR Art. 30 (§7.13).

retention_manager property

Access the retention manager (§7.12).

lineage_tracker property

Access the data lineage tracker (§7.12).

human_oversight property

Access the human oversight controller - EU AI Act Art. 14 (§7.13).

compliance_reporter property

Access the compliance reporter (§7.15).

risk_classifier property

Access the risk classifier - EU AI Act Art. 6 (§7.15).

extraction_pipeline property

Access the extraction pipeline for configuration.

dispatch_with_strategy(strategy, system_prompt, task_input, **kwargs)

Route a dispatch to the named relay strategy.

Unknown strategies fall back to push-based dispatch with a warning.

session_status()

Get live session metrics.

Returns:

Type Description
SessionStatus

A SessionStatus with current counters and remaining budget.

Raises:

Type Description
SessionClosedError

If the session is closed.

SessionExpiredError

If the session has expired.

RateLimitExceededError

If RBAC denies READ_STATE.

estimate_session(system_prompt='', task_input='', *, planned_dispatches=1, avg_output_tokens=None)

Pre-flight cost estimation WITHOUT executing LLM calls.

Parameters:

Name Type Description Default
system_prompt str

Representative system prompt for token estimation.

''
task_input str

Representative task input for token estimation.

''
planned_dispatches int

Number of dispatches planned (default: 1).

1
avg_output_tokens int | None

Expected average output tokens per dispatch. If None, assumes model uses full generation reserve.

None

Returns:

Type Description
CostEstimate

CostEstimate with token estimates and USD cost (if pricing known).

preview_envelope(system_prompt, task_input)

Inspect envelope contents WITHOUT dispatching.

Parameters:

Name Type Description Default
system_prompt str

System prompt for the dispatch.

required
task_input str

User task input.

required

Returns:

Type Description
EnvelopePreview

An EnvelopePreview with token counts and included facts.

Raises:

Type Description
SessionClosedError

If the session is closed.

SessionExpiredError

If the session has expired.

RateLimitExceededError

If RBAC denies READ_ENVELOPE.

configure(**kwargs)

Apply runtime configuration changes (mutable fields only).

Parameters:

Name Type Description Default
**kwargs Any

Key-value pairs to update in the resolved config.

{}

Raises:

Type Description
SessionClosedError

If the session is closed.

SessionExpiredError

If the session has expired.

RateLimitExceededError

If RBAC denies CONFIGURE.

reset_session()

Clear warm state, CKF, event log, DAG. Keeps session open.

Raises:

Type Description
SessionClosedError

If the session is closed.

SessionExpiredError

If the session has expired.

RateLimitExceededError

If RBAC denies MANAGE_SESSIONS.

on(event_type, listener)

Subscribe to a protocol event (convenience wrapper).

Usage::

def on_dispatch(event):
    print(f"Dispatch completed: {event.data}")

orch.on("dispatch.completed", on_dispatch)

boost_fact(fact_id, delta=0.1, reason='')

Boost a fact's confidence (positive feedback).

Parameters:

Name Type Description Default
fact_id str

Fact identifier.

required
delta float

Amount to increase confidence.

0.1
reason str

Optional reason for the feedback.

''

penalize_fact(fact_id, delta=-0.2, reason='')

Penalize a fact's confidence (negative feedback).

Parameters:

Name Type Description Default
fact_id str

Fact identifier.

required
delta float

Amount to decrease confidence.

-0.2
reason str

Optional reason for the feedback.

''

reject_fact(fact_id, reason='')

Reject a fact entirely (user override).

Parameters:

Name Type Description Default
fact_id str

Fact identifier.

required
reason str

Optional reason for rejection.

''

register_provider(provider)

Register an additional LLM provider for fallback routing.

Parameters:

Name Type Description Default
provider LLMProvider

Additional provider to register.

required

close()

Close session - flush warm→cold, persist CKF, zero keys (§2.5).

This method is thread-safe and idempotent.

resume(session_id, provider=None, *, llm=None, data_dir=None, **kwargs) classmethod

Resume a previously closed session by restoring CKF state.

Loads persisted facts from cold storage into a new orchestrator instance, providing cross-session knowledge continuity.

Parameters:

Name Type Description Default
session_id str

The session_id from the previous session.

required
provider LLMProvider | None

LLM provider to use for the resumed session.

None
llm LLMProvider | None

Alias for provider.

None
data_dir str | None

Override for CRP_DATA_DIR (default: env or '.').

None
**kwargs Any

Additional arguments forwarded to the orchestrator init.

{}

Returns:

Type Description
CRPOrchestrator

A new CRPOrchestrator with the previous session's facts loaded.

Raises:

Type Description
ValidationError

If session_id is not a valid UUID.

export_state(fmt=None)

Export session state as encrypted bytes (§2.5).

Includes warm store facts, critical state, structural state, and CKF health. Embeddings are NOT exported (§7.11) - recomputed on import.

Parameters:

Name Type Description Default
fmt str | None

Export format (reserved, currently only 'json' supported).

None

Returns:

Type Description
bytes

Encrypted bytes (AES-256-GCM with session-bound key).

Raises:

Type Description
SessionClosedError

If the session is closed.

SessionExpiredError

If the session has expired.

RateLimitExceededError

If RBAC denies EXPORT_STATE.

async_dispatch(system_prompt, task_input, **kwargs) async

Async version of dispatch() - runs in a configurable thread pool.

Uses the orchestrator's own ThreadPoolExecutor (sized via max_threads config) instead of the default asyncio executor, which prevents thread-pool saturation under concurrent load.

Use from async code (FastAPI, asyncio, etc.)::

output, report = await client.async_dispatch(
    "You are helpful.", "Explain CRP."
)

async_ingest(raw_text, *, source_label='') async

Async version of ingest() - runs in the orchestrator's thread pool.

async_close() async

Async version of close() - runs in the orchestrator's thread pool.

async_dispatch_stream(system_prompt, task_input, **kwargs) async

Async streaming dispatch - yields StreamEvent objects.

Usage::

async for event in client.async_dispatch_stream(
    "You are helpful.", "Explain CRP."
):
    if event.event_type == "token":
        print(event.data, end="")

ExtractionResult dataclass

Result of zero-LLM ingestion.

StreamEvent dataclass

Events emitted during streaming dispatch (§6.10.5).

CostEstimate dataclass

Pre-flight cost estimate returned by estimate_session().

Implements the cost-estimate.json schema.

Attributes:

Name Type Description
estimated_windows int

Number of dispatches estimated.

estimated_input_tokens int

Total input tokens estimated.

estimated_output_tokens int

Total output tokens estimated.

estimated_cost_usd float | None

Estimated USD cost, or None if pricing unknown.

confidence str

"high", "medium", or "low" depending on input completeness.

EnvelopePreview dataclass

Envelope inspection result returned by preview_envelope().

Implements the envelope-preview.json schema.

Attributes:

Name Type Description
total_tokens int

System + task + envelope tokens.

envelope_tokens int

Tokens consumed by the constructed envelope.

generation_reserve int

Tokens reserved for LLM generation.

facts_included int

Facts packed into the envelope.

facts_available int

Total facts available in the warm store.

saturation float

Envelope token utilisation ratio.

QualityReport dataclass

Governance summary returned by every dispatch() call.

Implements the quality-report.json schema. output is the COMPLETE, UNMODIFIED LLM output (Axiom 9).

Attributes:

Name Type Description
session_id str

Session identifier.

window_id str

Window identifier for this dispatch.

output str

Raw LLM output.

facts_extracted int

Number of facts extracted from the output.

security_flags SecurityFlags

Security validation results.

continuation_windows int

Number of continuation windows triggered.

envelope_saturation float

Envelope token utilisation ratio.

quality_tier str | None

"S", "A", "B", "C", or "D".

telemetry dict[str, Any] | None

Optional per-window telemetry dict.

RemainingBudget dataclass

Remaining quota for a session.

Attributes:

Name Type Description
windows_remaining int | None

Windows left before max_windows_per_session.

input_tokens_remaining int | None

Input tokens left before max_total_input_tokens.

output_tokens_remaining int | None

Output tokens left before max_total_output_tokens.

SecurityFlags dataclass

Security validation results - always present in QualityReport (§7.4).

Attributes:

Name Type Description
injection_markers_detected int

Count of injection patterns found.

injection_marker_details list[dict[str, Any]]

Details for each detected marker.

unicode_normalized bool

Whether unicode input was normalised.

control_chars_stripped int

Number of control characters removed.

input_truncated bool

Whether input was truncated to fit budget.

integrity_violations int

Count of provenance-chain violations.

output_injection_facts_penalized int

Output-side injection detections (§7.5.2).

SessionHandle dataclass

Session identity and lifetime metadata.

Implements the session-handle.json schema. The session_key is managed separately by SessionBindingManager and is never serialised to JSON or cold storage.

Attributes:

Name Type Description
session_id str

Unique UUID for this session.

protocol_version str

CRP protocol version running.

capabilities list[str]

Operations the session supports.

created_at float

Unix timestamp of creation.

expires_at float

Unix timestamp after which the session is invalid.

is_expired property

Return True if the current time is past expires_at.

SessionStatus dataclass

Live session metrics returned by session_status().

Implements the session-status.json schema.

Attributes:

Name Type Description
session_id str

Session identifier.

windows_completed int

Number of primary + continuation windows completed.

total_input_tokens int

Cumulative input tokens across all windows.

total_output_tokens int

Cumulative output tokens across all windows.

facts_in_warm_state int

Current number of facts in the warm store.

overhead_ratio float

Ratio of continuation windows to total windows.

remaining_budget RemainingBudget | None

Remaining quota, or None if no caps configured.

total_cost float | None

Estimated USD cost, or None if provider pricing unknown.

TaskIntent dataclass

Structured task description per §2.3.

All fields are optional - CRP infers missing values from the raw task input. Matches the task-intent.json schema exactly.

Attributes:

Name Type Description
description str | None

Optional human-readable summary of the task.

system_prompt str | None

Optional system prompt override for this dispatch.

task_input str | None

The user's actual request / question.

expected_output_type OutputType | str | None

Desired response format.

max_windows int | None

Maximum continuation windows allowed.

max_output_tokens int | None

Maximum tokens to generate in this dispatch.

output_schema dict[str, Any] | None

JSON Schema for structured output validation.

metadata dict[str, Any]

Arbitrary key-value context for routing or observability.

TransferType

Bases: str, Enum

What information flows from a parent window to a child (§8.1).

FULL_CONTEXT = 'FULL_CONTEXT' class-attribute instance-attribute

Complete parent response plus envelope.

SUMMARY = 'SUMMARY' class-attribute instance-attribute

Compressed summary (default).

FACTS_ONLY = 'FACTS_ONLY' class-attribute instance-attribute

Deferred facts only, no response content.

RESULT_ONLY = 'RESULT_ONLY' class-attribute instance-attribute

Final answer only (fan-in synthesis).

WindowDAG

Tracks the provenance graph of all windows in a session (§2.4, Axiom 7).

nodes property

Mapping from window ID to WindowNode.

edges property

List of all context-flow edges.

add_node(node)

Register a created window.

Parameters:

Name Type Description Default
node WindowNode

Window node to add.

required

Raises:

Type Description
ValueError

If the window ID already exists.

set_parent(child_id, parent_id)

Declare a provenance edge: parent contributed facts to child.

Parameters:

Name Type Description Default
child_id str

Child window ID.

required
parent_id str

Parent window ID.

required

add_edge(edge)

Record a context-flow edge with transfer metadata (§3.2).

Establishes the parent/child relationship and rejects edges that would introduce a cycle, preserving the acyclic invariant (§3.3).

descendants(window_id)

Return all transitive descendant window IDs.

Parameters:

Name Type Description Default
window_id str

Window to descend from.

required

Returns:

Type Description
set[str]

Set of all descendant window IDs (transitive children).

get(window_id)

Return the WindowNode for window_id.

roots()

Return windows with no parents (initial windows).

leaves()

Return windows with no children (terminal windows).

ancestors(window_id)

Return all transitive ancestor window IDs.

Parameters:

Name Type Description Default
window_id str

Window to ascend from.

required

Returns:

Type Description
set[str]

Set of all ancestor window IDs (transitive parents).

window_count()

Return the number of windows in the DAG.

lineage(window_id)

Return the DAG path from a root to window_id (CRP-SPEC-004 §14.2).

Follows the first parent at each step; suitable for emitting CRP-Provenance-Window-Lineage (root -> ... -> current).

detect_cycle()

Return True if the graph contains a cycle (violates §3.3).

Uses a three-colour DFS over all nodes.

clear()

Remove all nodes and edges (session reset).

WindowEdge dataclass

A directed context-flow edge from a parent to a child window (§3.2).

Attributes:

Name Type Description
source_id str

Parent window ID.

target_id str

Child window ID.

transfer_type TransferType

Mode of information transfer.

transferred_tokens int

Tokens carried across this edge.

summary_hash str

Hash of the transferred summary for integrity checks.

to_dict()

Serialise the edge to a JSON-compatible dict.

WindowMetrics dataclass

Recorded per window for telemetry.jsonl output.

to_dict()

Serialise to a flat dict for JSONL telemetry output.

WindowNode dataclass

Represents a single task window in the DAG (§2.4).

advance(to_state)

Transition to a new state, enforcing the forward-only invariant.

Parameters:

Name Type Description Default
to_state WindowState

Target state.

required

Raises:

Type Description
ValueError

If the transition is not allowed.

WindowPattern

Bases: str, Enum

How a window relates to its parent(s) in the DAG (§3.1).

LINEAR = 'LINEAR' class-attribute instance-attribute

Single parent, single child chain.

FAN_OUT = 'FAN_OUT' class-attribute instance-attribute

One parent spawns multiple parallel children.

FAN_IN = 'FAN_IN' class-attribute instance-attribute

Multiple parents merge into one child.

BRANCH = 'BRANCH' class-attribute instance-attribute

Exploratory branch that may later merge or terminate.

WindowState

Bases: Enum

Lifecycle states - transitions are strictly forward-only.

dispatch_batch(intents, dispatch_fn=None)

Batch dispatch with sequential execution (parallel fan-out ready).

Each intent should have keys: "system_prompt", "task_input".

ingest_batch(texts, extract_fn=None, task_intent='')

Batch ingestion with per-item extraction.

Returns extraction results per text item.

partition_fan_in_budget(parent_budgets)

Fan-in safety budget is the MINIMUM of all parents (§7.3.4, §6.4).

Parameters:

Name Type Description Default
parent_budgets list[float]

Envelope budgets of each parent window.

required

Returns:

Type Description
float

The conservative minimum budget, or 0.0 if no parents exist.

select_transfer_type(*, available_budget, parent_response_tokens, summary_tokens, deferred_facts_tokens, reserve_fraction=0.6)

Select a transfer type from token-budget pressure (§8.2).

The reserve_fraction (default 0.60) leaves 40% of the budget for new facts specific to the child's query.

Parameters:

Name Type Description Default
available_budget int

Tokens available in the child window.

required
parent_response_tokens int

Tokens of the full parent response.

required
summary_tokens int

Tokens of a compressed parent summary.

required
deferred_facts_tokens int

Tokens of deferred facts.

required
reserve_fraction float

Fraction of budget that may be spent on context relay.

0.6

Returns:

Type Description
TransferType

The most complete transfer type that fits within the reserved budget.

core.app_profile

crp.core.app_profile

Application capability contract and strategy discovery (CRP-SPEC-008 extension).

Most applications already have their own context-management and tool-calling strategy before CRP is introduced: a LangChain chain with a set of tools, a LlamaIndex RAG pipeline, a custom sliding-window summariser, etc. Rather than forcing the integrator to throw that away, :class:ApplicationProfile lets CRP discover or be told what the application already does, so CRP can:

  1. Avoid duplicating existing retrieval (don't build a second vector index if the app already has one).
  2. Delegate tool execution to the application's own tool registry when possible.
  3. Choose a compatible relay strategy (push vs pull vs reflexive) based on the app's actual provider and context budget.
  4. Record provenance accurately for audit (this fact came from the app's existing RAG, not from CRP's warm store).

The profile is intentionally a contract, not a config file. It can be:

  • Declared by the integrator (authoritative).
  • Derived from a message list and an optional tool list (heuristic).
  • Detected from a framework object such as a LangChain BaseCallbackHandler or LlamaIndex QueryEngine (best-effort).

FrameworkKind

Bases: str, Enum

Known application/framework integrations.

ContextStrategy

Bases: str, Enum

How the application manages long conversation context.

ProviderKind

Bases: str, Enum

Broad provider families CRP knows how to govern.

ToolInfo dataclass

A tool the application already exposes.

ApplicationProfile dataclass

Capability contract for an existing application.

Fields are optional; missing fields mean "unknown / not provided" and CRP falls back to its own defaults.

supports_tools()

Return True if the profile indicates tool-calling is available.

to_manifest()

Convert the profile's RAG/sources into a :class:ContextManifest.

to_dict()

Serialize to a JSON-safe dict.

from_dict(data) classmethod

Restore from a JSON-safe dict.

detect_framework(messages)

Heuristically detect the framework from message content/structure.

detect_context_strategy(messages)

Heuristically detect how the app manages context.

This is best-effort: we look at message-count patterns, presence of summarising system prompts, and explicit metadata.

build_profile_from_messages(messages, *, tools=None, provider=None, context_window=None)

Derive an application profile from observed messages and tools.

The derived profile is observed, not authoritative. Use it for negotiation and audit, but prefer an explicitly declared profile when accuracy matters.

core.batch

crp.core.batch

Batch dispatch and ingest operations (§6.6).

dispatch_batch: Fan-out multiple intents in parallel. ingest_batch: Batch ingestion with boundary reconciliation.

BatchResult dataclass

Result for one item in a batch operation.

dispatch_batch(intents, dispatch_fn=None)

Batch dispatch with sequential execution (parallel fan-out ready).

Each intent should have keys: "system_prompt", "task_input".

ingest_batch(texts, extract_fn=None, task_intent='')

Batch ingestion with per-item extraction.

Returns extraction results per text item.

core.circuit_breaker

crp.core.circuit_breaker

Circuit breaker for LLM provider calls (§audit H4).

Implements the CLOSED → OPEN → HALF-OPEN pattern to prevent cascading failures when a provider is unavailable.

CircuitBreakerConfig dataclass

Configuration for the circuit breaker.

CircuitBreaker

Thread-safe circuit breaker for LLM providers.

state property

Current circuit state (may transition OPEN → HALF-OPEN on read).

allow_request()

Check if a request should be allowed through.

record_success()

Record a successful call.

record_failure()

Record a failed call.

reset()

Reset to closed state.

core.config

crp.core.config

ConfigurationResolver - 5-layer hierarchy, feature flags (§06 §6.12).

Resolution order (later overrides prior): Layer 1: Hardcoded defaults (this file) Layer 2: Environment variables (CRP_* prefix) Layer 3: Config file (YAML/.env) - future Layer 4: Client() init kwargs Layer 5: Runtime configure() call

CRPConfig dataclass

Resolved CRP configuration.

Constructed via :class:ConfigurationResolver.

enabled property

Return the enabled.

max_continuations property

Return the max continuations.

log_envelopes property

Return the log envelopes.

max_dispatch_rate property

Return the max dispatch rate.

session_timeout property

Return the session timeout.

max_windows_per_session property

Return the max windows per session.

max_total_input_tokens property

Return the max total input tokens.

max_total_output_tokens property

Return the max total output tokens.

get(key, default=None)

Return the resolved config value for key, or default if absent.

lock()

Lock immutable fields - called after session starts.

update(overrides)

Apply mutable-only overrides (Layer 5: runtime configure).

Raises ValueError if attempting to change an immutable field after lock().

ConfigurationResolver

Resolve configuration through the 5-layer hierarchy.

resolve(**init_kwargs)

Build a CRPConfig from layers 1-4.

Parameters:

Name Type Description Default
**init_kwargs Any

Layer 4 overrides from Client() constructor.

{}

Raises:

Type Description
ValueError

If any resolved value is out of bounds.

core.context_enforcer

crp.core.context_enforcer

Context enforcement pipeline (§7.14.4, introduced in CRP 2.2).

CRP 2.1 defined the vocabulary (ContextSource, ContextManifest). CRP 2.2 defines the enforcement point - the single choke-point that every envelope assembly flows through when a manifest is attached.

Pipeline::

observed sources + manifest
    ├─ manifest-signature verify       (fails → CONTEXT_MANIFEST_INVALID)
    ├─ manifest expiry check           (fails → CONTEXT_MANIFEST_INVALID)
    ├─ attestation mismatch scan       (mismatches → audit events)
    ├─ injection-signal scan (trusted) (hits     → audit events)
    └─ return EnforcementResult (never raises by default; policy decides)

The enforcer is deliberately non-raising for mismatches - in production the right behaviour varies (block, downgrade trust, alert, ignore). Raising is the integrator's decision via policy=.

every observed source is either

(a) declared in a signed, unexpired manifest, or (b) captured in an audit event emitted to the configured sink.

The "global application context" the pessimist asked for lives here: once this enforcer is wired into assemble_messages, no CRP request to an LLM can occur without the orchestrator having seen and classified every non-benign source.

EnforcementPolicy

Bases: str, Enum

What the enforcer does when a violation is detected.

  • OBSERVE - record audit events, never raise. Default for dev.
  • WARN - log a WARNING for every violation, still pass through.
  • REJECT - raise :class:crp.core.errors.CRPError with :attr:CONTEXT_ATTESTATION_MISMATCH or :attr:CONTEXT_MANIFEST_INVALID. Default for prod.

InjectionSignal dataclass

A suspicious pattern found inside a source that was labelled TRUSTED.

Trust labels are declarative - a developer marking a SYSTEM_PROMPT as TRUSTED doesn't make its contents safe (they may have templated untrusted input into it). This record is emitted to the audit sink so the gap between declared and actual trust is observable.

to_audit_event()

Convert this signal into a CONTEXT_TRUST_VIOLATION audit event.

AuditSink

Protocol for audit-event consumers.

Implementations MUST be thread-safe. The enforcer calls emit() once per event with a JSON-ready dict. Events are never re-emitted.

emit(event)

Emit an audit event to the sink.

Parameters:

Name Type Description Default
event dict[str, Any]

JSON-ready audit event dict.

required

Raises:

Type Description
NotImplementedError

Subclasses must implement this method.

LoggingAuditSink

Bases: AuditSink

Emits to the crp.audit logger at WARNING level.

emit(event)

Log the audit event to the crp.audit logger at WARNING level.

InMemoryAuditSink

Bases: AuditSink

Thread-safe ring buffer for tests and cross-turn replay.

The most recent max_events events are retained; older events are dropped silently. Iterating the sink is snapshot-consistent.

events property

Return the events.

emit(event)

Store a copy of the audit event in the ring buffer.

clear()

Remove all stored audit events.

EnforcementResult dataclass

Outcome of a single enforcement pass.

Always non-None even if everything passed - consumers can inspect ok, mismatches, injection_signals to decide next steps.

has_violations property

Return whether this object has violations.

ContextEnforcer

Single enforcement choke-point for CRP 2.2.

Construct once per application / orchestrator and reuse across turns::

enforcer = ContextEnforcer(
    policy=EnforcementPolicy.REJECT,
    sink=LoggingAuditSink(),
    manifest_secret=os.environ["CRP_MANIFEST_SECRET"].encode(),
)
result = enforcer.check(manifest, observed)
if not result.ok:
    ...

The same instance can be installed as the process-wide default via :func:set_default_enforcer so libraries (dispatch router, CKF, warm store) can find it without explicit plumbing.

with_sink(sink)

Return a shallow copy with a different sink (ergonomic swap).

check(manifest, observed, *, emit=True)

Run the full enforcement pipeline.

Accepts either bare :class:ContextSource objects (no content scanning) or :class:_ObservedContent bundles (source + text, which enables injection-signal detection for TRUSTED sources).

If emit is True (default), audit events are pushed to the sink. Pass emit=False for dry-run analysis.

check_messages(messages, manifest=None, *, session_id=None, derive_manifest=False, emit=True)

Re-run the enforcement pipeline against a full chat history (CRP 2.3).

For every message the enforcer derives a :class:ContextSource (system/user/assistant/tool → kind+trust) and an observed-content bundle, so both attestation and injection scans run over the actual bytes that will be sent to the model. Tool-call loops benefit most: tool_result payloads are re-validated every turn instead of only at first assembly.

When derive_manifest=True and manifest is None, a fresh manifest is derived from the messages themselves (unsigned, one source per message). This is the "belt-and-braces" path for integrators who want a per-turn audit record even if they never authored a manifest.

check_tool_result(content, *, source=None, manifest=None, tool_call_id=None, tool_name=None, emit=True)

Validate a single tool-call / function-call result (CRP 2.3).

Runs the same pipeline as :meth:check but bounded to one payload, so agent loops can re-validate each tool response individually. If source is not supplied, a derived FUNCTION_CALL source is constructed with an UNKNOWN trust level (tool outputs are never implicitly trusted).

detect_injection_signals(content, source, *, only_trusted=True)

Scan content for patterns that contradict the source's declared trust.

By default only sources with trust_level == TRUSTED are scanned - untrusted content is expected to contain arbitrary text and scanning it would produce noise. Pass only_trusted=False to scan everything (useful for a red-team sweep).

Returns an empty list if the source is skipped or no signals match.

observed_content(source, content='')

Build an _ObservedContent bundle for :meth:ContextEnforcer.check.

Kept as a function (rather than exporting the class) so the public surface area stays minimal - the bundle is an implementation detail.

default_enforcer()

Return the installed process-wide enforcer, or None.

On first call, honours the CRP_DEFAULT_ENFORCEMENT environment variable (observe|warn|reject|off) as a safety net when :func:set_default_enforcer was never called. Explicit calls to :func:set_default_enforcer always take precedence - env auto-install only fires when no enforcer has been installed yet.

set_default_enforcer(enforcer)

Install enforcer as the process-wide default. Returns the previous one.

Marks env auto-install as "done" so a later default_enforcer() call does not clobber an explicit None.

core.context_source

crp.core.context_source

Context-source provenance primitives (§7.14.3, introduced in CRP 2.1).

Background

The Decision Provenance Engine (crp/provenance/) already classifies every LLM output claim as CONTEXT_GROUNDED | PARAMETRIC | MIXED | UNCERTAIN. That is output-side provenance - where did the answer come from, relative to the envelope?

This module adds the symmetric input-side primitive: where did the envelope content itself come from? Was a fact retrieved from a vector DB, pulled from a relational database, returned by an MCP tool, typed by the end user, or is it model-parametric knowledge?

Without this, an organisation running CRP cannot answer ISO/IEC 42001 §4.1 (Context of the organisation), GDPR Art. 30 (Records of Processing), or EU AI Act Art. 10 (Data governance) truthfully.

Design

Three types:

  • :class:SourceKind - a closed enumeration of upstream source categories. Closed intentionally: if new kinds appear in the wild, they land here via an RFC so auditors have a stable vocabulary.
  • :class:ContextSource - an immutable record attached to facts and messages. Records what kind of source supplied the content, who declared it (a signed manifest, an observed channel, or a heuristic parser), and what we know about its sensitivity, region, and retrieval policy.
  • :class:ContextManifest - a customer-authored declarative catalogue of the sources their application intends to use. Observed sources that do not appear in the manifest are flagged UNATTESTED and emitted to the audit log as :data:CONTEXT_ATTESTATION_MISMATCH events.

This module is pure-data; no I/O, no network, no LLM calls. Consumers (the envelope builder, dispatch router, Decision Provenance Engine, and the crp-comply compliance-reporting layer) integrate it at their own pace - every new field is optional and defaults preserve v2.0 behaviour.

References

  • ISO/IEC 42001:2023 §4.1–4.2 (Context of the organisation)
  • EU AI Act Regulation (EU) 2024/1689, Art. 10 (Data and data governance)
  • GDPR Art. 30 (Records of processing activities)
  • NIST AI RMF 1.0, MAP 4 (Context mapping)

SourceKind

Bases: str, Enum

Closed enumeration of upstream context-source categories.

Stable vocabulary - additions require an RFC so downstream auditors do not receive novel strings without warning.

USER_TURN = 'user_turn' class-attribute instance-attribute

End-user's direct chat input.

SYSTEM_PROMPT = 'system_prompt' class-attribute instance-attribute

Developer-authored system/instruction prompt.

DEVELOPER_PROMPT = 'developer_prompt' class-attribute instance-attribute

Messages from a developer role (OpenAI-style role=developer).

RAG_RETRIEVAL = 'rag_retrieval' class-attribute instance-attribute

Retrieval-augmented generation chunk (generic; use VECTOR_DB or DATABASE when the backend is known).

VECTOR_DB = 'vector_db' class-attribute instance-attribute

Vector-database retrieval (Pinecone, Weaviate, pgvector, etc.).

DATABASE = 'database' class-attribute instance-attribute

Relational/NoSQL database read.

KNOWLEDGE_GRAPH = 'knowledge_graph' class-attribute instance-attribute

Structured knowledge graph (Neo4j, RDF, etc.).

MCP_TOOL = 'mcp_tool' class-attribute instance-attribute

Model Context Protocol server tool invocation.

FUNCTION_CALL = 'function_call' class-attribute instance-attribute

OpenAI/Anthropic function-calling result returned to the model.

Live web-search result.

FILE_UPLOAD = 'file_upload' class-attribute instance-attribute

User-uploaded document.

AGENT_MEMORY = 'agent_memory' class-attribute instance-attribute

Agent-framework conversational memory store.

CKF_RETRIEVAL = 'ckf_retrieval' class-attribute instance-attribute

CRP Contextual Knowledge Fabric retrieval (internal).

WARM_STORE = 'warm_store' class-attribute instance-attribute

CRP warm-store fact (internal, recycled from an earlier window).

PARAMETRIC = 'parametric' class-attribute instance-attribute

Model-internal knowledge - not a retrievable upstream source.

UNATTESTED = 'unattested' class-attribute instance-attribute

Content whose kind could not be determined; flagged for audit.

SourceOrigin

Bases: str, Enum

How this :class:ContextSource record came into existence.

DECLARED = 'declared' class-attribute instance-attribute

Explicitly registered via :class:ContextManifest.

OBSERVED = 'observed' class-attribute instance-attribute

Explicitly attached by the caller (e.g. tool-call plumbing that knows it is returning an MCP tool result).

HEURISTIC = 'heuristic' class-attribute instance-attribute

Inferred by :func:detect_source_kind from marker patterns in the message content. Lowest trust - surfaced in audit reports for review.

TrustLevel

Bases: str, Enum

Caller's trust posture for the source.

Not a security boundary - this is an audit signal. Enforcement (ACLs, redaction, refusal) is the integrator's responsibility.

ContextSource dataclass

Immutable record describing where a piece of envelope content came from.

Attached optionally to :class:crp.Fact via its source field and to role=tool / role=user messages by the dispatch router.

Every field except kind and source_id is optional. Defaults preserve v2.0 behaviour - callers that do not opt in see no change.

Parameters

kind A :class:SourceKind value. Closed enumeration. source_id Stable handle for the source (e.g. "acme-hr-policies-vdb", "mcp://atlassian/jira-search"). Must be non-empty and ≤ 256 chars. origin :class:SourceOrigin - DECLARED (from a signed manifest), OBSERVED (plumbed in by the caller), or HEURISTIC (detected by pattern-match). trust_level Caller's trust posture. Default UNKNOWN. contains_pii Tri-state: True, False, or None (unknown). Used by GDPR deliverables and by the EU AI Act Art. 10 data-governance report. sensitivity Free-form sensitivity label (e.g. "confidential", "public", "restricted"). Not normalised - customer vocabulary passes through. region Geographic region of the source ("eu-west-1", "us-east-2", "on-prem-dc1"). Enables cross-border-transfer audits. retrieval_query For RAG/DB/search sources: the query string used. Stored verbatim for the Source Grounding Engine. Truncated to 512 chars. retrieved_at UTC timestamp of retrieval. upstream_uri Stable URI pointer back to the source record if available. declared_by_manifest_id Links the record back to a :class:ContextManifest when origin == DECLARED. metadata Integrator-defined extras. Size-limited below.

to_dict()

JSON-ready dict for audit logs and envelope serialisation.

from_dict(data) classmethod

Construct from a dict produced by :meth:to_dict.

ManifestValidationError

Bases: ValueError

Raised when a :class:ContextManifest cannot be loaded or verified.

ContextManifest dataclass

Customer-authored declaration of upstream context sources.

Signed with HMAC-SHA256 over a canonical JSON representation. The signature is advisory - it proves the declaration has not been tampered with since signing, but CRP does not manage the signing key.

Minimal usage::

manifest = ContextManifest(system_id="resume-rank-v1", customer_id="acme")
manifest.add(ContextSource(
    kind=SourceKind.VECTOR_DB,
    source_id="acme-hr-policies-vdb",
    origin=SourceOrigin.DECLARED,
    trust_level=TrustLevel.TRUSTED,
    contains_pii=True,
    region="eu-west-1",
))
manifest.sign(secret=os.environ["CRP_MANIFEST_SECRET"].encode())
blob = manifest.to_json()   # persist / ship to proxy

add(source)

Append a :class:ContextSource to the manifest.

The source's origin is forced to DECLARED and the manifest id is stamped into declared_by_manifest_id. Invalidates any existing signature.

sign(secret)

Sign the manifest with HMAC-SHA256 and return the hex digest.

verify(secret)

Verify the HMAC signature in constant time.

Returns True iff signature is present and matches.

is_expired(now=None)

Return True if the manifest has passed its expiry timestamp.

declared_kinds()

Set of :class:SourceKind values that appear in the manifest.

declared_source_ids()

Return the set of non-empty source IDs declared in the manifest.

find(source_id)

Return the declared source with source_id, or None if absent.

to_json()

Serialise to JSON (includes signature if present).

from_json(blob) classmethod

Create a new instance from a JSON string or object.

Parameters:

Name Type Description Default
blob str | bytes

The blob value.

required

Returns:

Type Description
ContextManifest

ContextManifest.

AttestationMismatch dataclass

One audit-log row describing a discrepancy between observed and declared sources.

Emitted whenever :func:check_attestation detects that an observed :class:ContextSource has no counterpart in the current :class:ContextManifest. Consumed by the dispatch router's audit pipeline (§7.14.2) and by the compliance layer's attestation report.

to_audit_event()

Serialise into the §7.14.2 audit-event envelope shape.

detect_source_kind(content, *, role=None, default=SourceKind.UNATTESTED)

Heuristically classify content into a :class:ContextSource.

The resulting :class:ContextSource always has origin=HEURISTIC and trust_level=UNKNOWN. Callers that know the true origin should construct the record directly rather than relying on this function.

Parameters

content Raw message text. role Optional OpenAI-style chat role hint ("user", "system", "developer", "tool", "function"). If provided, strong role-based signals win over content-based ones. default :class:SourceKind to fall back to if nothing matches.

Returns

ContextSource With source_id set to a stable fingerprint of the detected kind plus a short content hash.

check_attestation(observed, manifest, *, now=None)

Return the set of mismatches between observed sources and manifest.

The matching rule is deliberately strict:

  • No manifest → every observed source that is not obviously benign (USER_TURN, SYSTEM_PROMPT, DEVELOPER_PROMPT, PARAMETRIC) yields a no_manifest mismatch.
  • Expired manifest → every observed source yields a manifest_expired mismatch.
  • Otherwise, observed source_id must match a declared source_id or observed kind must be present in declared_kinds(). A missing source_id match falls back to a kind-only match and yields an unattested_source_id row if the source_id is non-empty; a missing kind match yields unattested_kind.

Heuristic and declared sources are both checked - customers who want to suppress heuristic noise should declare their sources.

iso8601(ts)

UTC ISO-8601 string for an epoch timestamp (audit log convenience).

core.context_tools

crp.core.context_tools

Tool-mediated context relay - pull-based architecture (§20).

Instead of pre-loading ALL context into the prompt (push model), CRP exposes its knowledge stores as callable tools. The LLM requests context on demand during generation, consuming only what it needs.

This is fundamentally different from RAG/prompt injection: - Push (old): Pre-compute all context → stuff into prompt → generate - Pull (new): Give LLM task + tools → LLM decides what it needs → CRP serves on demand

The module provides: 1. Tool definitions in OpenAI-compatible format 2. A ContextToolExecutor that routes tool calls to CKF/WarmStore 3. Integration with the iterative dispatch loop in the orchestrator

ToolCall dataclass

A single tool call extracted from an LLM response.

ToolResult dataclass

Result of executing a tool call.

ContextToolExecutor

Routes tool calls to CRP subsystems and returns results.

Wired to: - WarmStateStore: ranked facts, structural state, fact lookup - CKF: semantic/graph retrieval, community queries - Embedding function: for semantic search - Continuation state: gap analysis, directives

calls_executed property

Return the calls executed.

total_tokens_served property

Return the total tokens served.

update_continuation_state(state)

Update continuation state (called between windows).

execute(tool_call)

Execute a single tool call and return the result.

execute_batch(tool_calls)

Execute multiple tool calls and return results.

build_tool_system_prompt(original_system, fact_count)

Augment the system prompt with context-tool usage guidance.

Tells the LLM that it has access to CRP context tools and should use them to retrieve verified information instead of relying on parametric knowledge alone.

tool_results_to_messages(assistant_message, results)

Build the message sequence for a tool call round-trip.

Returns [assistant_msg_with_tool_calls, tool_result_1, tool_result_2, ...]. This is the OpenAI-compatible format for continuing generation after tool calls.

core.dispatch_router

crp.core.dispatch_router

Dispatch router mixin - all CRP dispatch strategies (§2.5, §6.5).

Extracted from orchestrator.py for maintainability. This mixin provides all dispatch method implementations. The CRPOrchestrator inherits from this class.

StreamEvent dataclass

Events emitted during streaming dispatch (§6.10.5).

ExtractionProgress dataclass

Progress update during extraction pipeline.

ContinuationInfo dataclass

Emitted when a streaming continuation window is triggered.

WindowSummary dataclass

Summary of a completed window.

DispatchMixin

Mixin providing all CRP dispatch strategies.

Methods access orchestrator state via self (multiple inheritance).

dispatch(system_prompt, task_input, **kwargs)

Dispatch a task window to the LLM with full envelope + extraction.

Pipeline: 1. Advisory injection scan on task_input 2. Build envelope from WarmStore facts (6-phase) 3. Assemble messages (Axiom 4 - no modification) 4. Dispatch to LLM 5. Extract facts from output (graduated 6-stage pipeline) 6. Store facts in WarmStore + CKF 7. Continuation loop if wall hit + gap remaining + info flowing

Returns (stitched_output, QualityReport). Output is UNMODIFIED (Axiom 9).

dispatch_with_tools(system_prompt, task_input, *, max_tool_rounds=10, **kwargs)

Dispatch with tool-mediated context relay (pull model).

Instead of pre-loading ALL context into the envelope (push model), this method: 1. Sends the task to the LLM with CRP context tools 2. The LLM requests context on demand via tool calls 3. CRP executes tool calls against WarmStore/CKF 4. Results are fed back, and the LLM continues 5. When the LLM finishes (stop/length), extraction proceeds normally

Falls back to push-based dispatch() if the provider doesn't support tool calling.

Parameters:

Name Type Description Default
system_prompt str

System prompt (unmodified per Axiom 4).

required
task_input str

User task/prompt.

required
max_tool_rounds int

Maximum tool call round-trips (safety cap).

10
**kwargs Any

Provider-specific overrides.

{}

Returns:

Type Description
tuple[str, QualityReport]

(output_text, QualityReport) - same interface as dispatch().

dispatch_reflexive(system_prompt, task_input, *, max_refinement_passes=2, **kwargs)

Reflexive dispatch - generate first, verify against KB, refine.

Unlike push (all context upfront) or pull (LLM asks for context), reflexive dispatch lets the model generate freely, then CRP fact-checks the output against the knowledge base and sends back targeted corrections. The model refines based on SPECIFIC evidence rather than wading through pre-loaded context.

Flow

Pass 1: Generate with NO envelope (pure parametric knowledge) CRP: Analyze output - find contradictions, unsupported claims Pass 2: Send model its own output + correction payload Model: Refines with surgical precision (Optional Pass 3+ if coverage remains low)

Parameters:

Name Type Description Default
system_prompt str

System prompt (unmodified per Axiom 4).

required
task_input str

User task/prompt.

required
max_refinement_passes int

Max verify-refine cycles (safety cap).

2
**kwargs Any

Provider-specific overrides.

{}

Returns:

Type Description
tuple[str, QualityReport]

(output_text, QualityReport) - same interface as dispatch().

dispatch_progressive(system_prompt, task_input, **kwargs)

Progressive disclosure - send context INDEX first, details on demand.

Instead of sending ALL facts (push) or none (pull/tools), progressive disclosure sends a compact INDEX of available facts - one-line summaries - so the model sees WHAT knowledge exists at ~10% of the token cost. After initial generation, CRP detects which indexed facts were actually referenced, expands them to full detail, and the model refines with targeted depth.

Flow

Step 1: Build compact context index from WarmStore Step 2: Send task + index to model (model sees all topics cheaply) Step 3: CRP detects which index entries were referenced Step 4: Expand referenced entries to full detail Step 5: Model refines with deep context on referenced items only

Parameters:

Name Type Description Default
system_prompt str

System prompt (unmodified per Axiom 4).

required
task_input str

User task/prompt.

required
**kwargs Any

Provider-specific overrides.

{}

Returns:

Type Description
tuple[str, QualityReport]

(output_text, QualityReport) - same interface as dispatch().

dispatch_stream_augmented(system_prompt, task_input, *, max_injections=5, **kwargs)

Stream-augmented generation - inject context mid-generation.

The most novel strategy: CRP monitors the LLM's output stream in real-time, buffering sentences. After each sentence, CRP checks if relevant WarmStore facts exist for the topic being generated. When relevant facts are found, generation is PAUSED, the partial output + injected facts are sent as a continuation, and the model resumes - now informed.

The model receives context EXACTLY when it's generating about a relevant topic, not before (wasted) and not only when it asks (pull). This is point-of-need context delivery.

Flow
  1. Start streaming generation (no envelope)
  2. Buffer tokens into sentences
  3. After each sentence: CRP fact-matches against WarmStore
  4. If relevant NEW facts found → stop generation
  5. Send partial output + injected facts as continuation prompt
  6. Resume generation from where model left off
  7. Repeat until generation completes or max_injections reached

Parameters:

Name Type Description Default
system_prompt str

System prompt (unmodified per Axiom 4).

required
task_input str

User task/prompt.

required
max_injections int

Maximum mid-stream injections (safety cap).

5
**kwargs Any

Provider-specific overrides.

{}

Returns:

Type Description
tuple[str, QualityReport]

(output_text, QualityReport) - same interface as dispatch().

dispatch_agentic(system_prompt, task_input, *, max_revision_rounds=2, enable_curation=True, enable_planning=True, **kwargs)

Agentic dispatch - CRP uses the LLM as its internal brain.

╔═══════════════════════════════════════════════════════════╗ ║ §22 AGENTIC COGNITIVE LOOP ║ ╠═══════════════════════════════════════════════════════════╣ ║ 1. ANALYZE - LLM analyzes task complexity & needs ║ ║ 2. PLAN - LLM decomposes complex tasks ║ ║ 3. SYNTHESIZE - LLM pre-processes KB facts ║ ║ 4. ROUTE - LLM picks optimal dispatch strategy ║ ║ 5. GENERATE - Execute chosen dispatch strategy ║ ║ 6. EVALUATE - LLM evaluates output quality ║ ║ 7. REVISE - If evaluation says revision needed ║ ║ 8. CURATE - LLM manages CRP's knowledge base ║ ╚═══════════════════════════════════════════════════════════╝

The LLM is INSIDE CRP. CRP orchestrates all reasoning. Every decision point uses the LLM instead of heuristics.

dispatch_intent(intent)

Dispatch using an explicit TaskIntent object.

dispatch_hierarchical(system_prompt, large_input, task_intent='', **kwargs)

Hierarchical map-reduce dispatch for oversized inputs (§14).

Segments the input, dispatches each segment through the LLM, then iteratively reduces the syntheses. All facts extracted from every segment are stored in the warm store + CKF.

Returns (final_syntheses, QualityReport).

dispatch_batch(intents)

Batch dispatch multiple tasks sequentially (§6.6).

Each intent dict should have "system_prompt" and "task_input" keys. Returns list of (output, QualityReport) tuples.

dispatch_stream(system_prompt, task_input, **kwargs)

Streaming dispatch - yields StreamEvent objects.

Stream contract: - Emits one or more "token" events in generation order. - Emits "extraction" events as facts are discovered. - Emits one "window_complete" event per stream segment. - Emits exactly one "done" event as the final event (or "error"). - Token concatenation MUST produce the same string as dispatch(). - No reordering of token events. - Continues automatically when the provider hits the token wall.

assemble_messages(system_prompt, envelope, task_input, *, manifest=None, observed_sources=None, enforcer=None)

Build the chat message array sent to the LLM.

Structural Injection Defense (§7.5.1): Envelope and task_input are placed in SEPARATE user messages with provenance boundary markers.

Invariants (Axiom 4 - Model Ignorance): - system_prompt is NEVER modified - task_input content is NEVER modified - No CRP-internal protocol metadata is injected

Context enforcement (§7.14.4, CRP 2.2): If manifest or observed_sources is supplied, the configured :class:crp.core.context_enforcer.ContextEnforcer runs before the messages are returned. When none is passed explicitly, the process-wide default set via :func:crp.core.context_enforcer.set_default_enforcer is used. Applications with no enforcer installed pay zero cost - this code path short-circuits.

Parameters

manifest Optional :class:~crp.core.context_source.ContextManifest covering this turn. observed_sources Optional iterable of :class:~crp.core.context_source.ContextSource objects (or observed_content() bundles) describing what the envelope exposes. enforcer Optional :class:~crp.core.context_enforcer.ContextEnforcer to use for this call. Overrides the process-wide default.

core.errors

crp.core.errors

CRP error taxonomy - codes 1001-1031 per §6.8.

This module defines the structured exception hierarchy used throughout the SDK. Every error carries a numeric code that can be serialised into the crp-error.json schema and propagated across Gateway / Comply boundaries.

ErrorCode

Bases: IntEnum

All CRP error codes used in CRPError.code.

Codes are grouped by subsystem: - 1001-1005: budget and session lifecycle - 1010-1013: validation and security - 1020-1021: LLM provider failures - 1030-1031: state integrity and provenance chain - 1040-1041: context-source attestation (CRP 2.1+, §7.14.3)

CRPError

Bases: Exception

Base exception for all CRP errors.

Each error carries a numeric code (1001-1031) per §6.8, a human-readable message, and an optional details dict for structured logging and downstream Gateway responses.

Parameters:

Name Type Description Default
code int | ErrorCode

Numeric error code or ErrorCode enum member.

required
message str

Human-readable error description.

required
details dict[str, Any] | None

Optional structured context (limits, ids, reasons, etc.).

None

BudgetExhaustedError

Bases: CRPError

Raised when a session-level budget cap is exceeded.

Covers windows-per-session, total input tokens, total output tokens, and rate-limit budgets. details typically contains limit, used, and optionally requested.

Parameters:

Name Type Description Default
message str

Human-readable description.

'Budget exhausted'
**details Any

Structured context for logging and headers.

{}

RateLimitExceededError

Bases: CRPError

Raised when a per-session rate limit or RBAC quota is hit.

SessionExpiredError

Bases: CRPError

Raised when an operation is attempted on an expired session handle.

SessionClosedError

Bases: CRPError

Raised when an operation is attempted after the session was closed.

ValidationError

Bases: CRPError

Raised for structural validation failures (§7.4).

ProviderError

Bases: CRPError

Raised when the underlying LLM provider returns an error.

ProviderTimeoutError

Bases: CRPError

Raised when the LLM provider fails to respond within the configured timeout.

StateCorruptedError

Bases: CRPError

Raised when cold-state integrity verification fails.

ChainVerificationFailedError

Bases: CRPError

Raised when the window DAG HMAC chain integrity check fails (§7.6).

SecurityInvariantError

Bases: CRPError

Raised when a CRP security invariant is violated (§7.4).

SignatureInvalidError

Bases: CRPError

Raised when cryptographic signature verification fails (§7.3).

core.extraction_facade

crp.core.extraction_facade

Extraction facade mixin - fact extraction and ingestion (§2.5).

Extracted from orchestrator.py for maintainability. This mixin provides extraction and ingestion method implementations. The CRPOrchestrator inherits from this class.

ExtractionResult dataclass

Result of zero-LLM ingestion.

ExtractionMixin

Mixin providing CRP extraction and ingestion methods.

Methods access orchestrator state via self (multiple inheritance).

ingest_batch(texts, task_intent='')

Batch ingest multiple texts, returning facts extracted per text (§6.6).

ingest(raw_text, source_label=None)

Ingest raw text without LLM invocation (§2.5).

Runs graduated extraction pipeline on raw_text, adds facts to WarmStateStore and CKF. No LLM call, no window creation, no envelope.

core.facilitator

crp.core.facilitator

CRP Facilitator - LLM-in-the-Loop cognitive engine (§22).

╔═══════════════════════════════════════════════════════════════════╗ ║ PARADIGM INVERSION ║ ║ ║ ║ Traditional: User → LLM → Text ║ ║ RAG: User → Context + LLM → Text ║ ║ Agentic: LLM (controller) → Tools → LLM → Output ║ ║ ║ ║ CRP §22: CRP (orchestrator) → LLM (cognitive engine) ║ ║ → CRP (acts on reasoning) → LLM (generates) ║ ║ → CRP (evaluates via LLM) → CRP (curates) ║ ║ ║ ║ The LLM is INSIDE CRP. CRP is the system. ║ ║ The LLM provides reasoning. CRP provides structure. ║ ╚═══════════════════════════════════════════════════════════════════╝

The CRPFacilitator uses the LLM for six cognitive functions:

§22.1 TASK ANALYSIS - Parse task complexity, domain, knowledge needs §22.2 STRATEGY ROUTING - Choose optimal dispatch strategy §22.3 FACT SYNTHESIS - Merge/compress/derive new knowledge from facts §22.4 OUTPUT EVALUATION - Assess output quality against task intent §22.5 MEMORY CURATION - Decide what knowledge to keep/merge/discard §22.6 EXECUTION PLANNING - Decompose complex tasks into sub-tasks

The facilitator wraps each cognitive call in a structured prompt with constrained JSON output. CRP interprets the JSON and acts on it - the LLM never touches CRP's internal state directly.

Inspired by
  • Xi et al., "The Rise and Potential of LLM-Based Agents" (2023): Agent = Brain(LLM) + Perception + Action
  • Park et al., "Generative Agents" (2023): Observation → Planning → Reflection cognitive loop
  • Packer et al., "MemGPT" (2023): LLM manages its own memory via structured interrupts

CRP differs from all three: the LLM is NOT the controller. CRP IS the controller. The LLM is the reasoning module.

TaskAnalysis dataclass

LLM's analysis of a task - §22.1.

StrategyDecision dataclass

LLM's routing decision - §22.2.

SynthesizedKnowledge dataclass

LLM-synthesized knowledge from existing facts - §22.3.

OutputEvaluation dataclass

LLM's evaluation of CRP's output - §22.4.

CurationDecision dataclass

LLM's memory curation recommendations - §22.5.

ExecutionPlan dataclass

LLM's execution plan for complex tasks - §22.6.

PlanStep dataclass

One step in an execution plan.

FacilitatorMetrics dataclass

Telemetry for facilitator cognitive calls.

total_cognitive_ms property

Return the total cognitive ms.

CRPFacilitator

LLM-in-the-Loop cognitive engine for CRP.

The facilitator uses the LLM for CRP's internal reasoning: - Task analysis (what kind of task is this?) - Strategy routing (which dispatch method?) - Fact synthesis (merge/compress knowledge) - Output evaluation (did we actually help?) - Memory curation (what to keep/discard?) - Execution planning (how to handle complex tasks?)

The LLM never touches CRP state directly. It returns structured JSON that CRP interprets and acts upon. This maintains CRP's sovereignty over its own subsystems while leveraging the LLM's reasoning capability at every decision point.

metrics property

Return the metrics.

reset_metrics()

Reset all facilitator timing and token counters to zero.

analyze_task(task_input, system_prompt, fact_count=0)

Use the LLM to analyze what kind of task this is.

Instead of CRP guessing task complexity via regex, the LLM actually understands the task semantically. This feeds into every downstream decision: strategy routing, envelope budget, continuation thresholds, extraction depth.

route_strategy(analysis, fact_count, available_strategies=None)

Use the LLM to choose the optimal dispatch strategy.

Given the task analysis and CRP's current state (fact count, available strategies), the LLM reasons about which approach will produce the best output.

This replaces the hardcoded "always use push" default.

synthesize_facts(facts, task_context='')

Use the LLM to synthesize coherent knowledge from raw facts.

Instead of CRP dumping raw facts into an envelope, the LLM: - Merges overlapping facts into coherent statements - Identifies contradictions - Spots knowledge gaps - Ranks by actual relevance to the task

This produces BETTER context than raw fact packing.

evaluate_output(task_input, output, facts_used=0)

Use the LLM to evaluate CRP's own output.

After generation, the LLM assesses whether the output actually addresses the task. This replaces the heuristic quality scoring (fact count × saturation × token count).

The evaluation feeds back into: - Whether to trigger continuation - Whether to try a different strategy - Quality tier classification - Memory curation decisions

curate_memory(facts, recent_task='')

Use the LLM to curate CRP's knowledge base.

Instead of fixed-interval curation with hardcoded rules, the LLM reviews the knowledge base and makes intelligent decisions about what to keep, merge, or discard.

This is the LLM managing CRP's own memory - the deepest integration of LLM-as-cognitive-engine.

plan_execution(analysis, fact_count=0)

Use the LLM to create an execution plan for complex tasks.

For multi-part or complex tasks, the LLM decomposes the task into steps, each with its own dispatch strategy and context needs. CRP then orchestrates the plan, running each step with the optimal configuration.

This replaces the flat "one dispatch" approach with intelligent multi-step orchestration.

core.idempotency

crp.core.idempotency

Request deduplication and idempotency keys (§23).

Same key within TTL returns cached result - no re-dispatch. Concurrency model: session-level write lock for dispatch/ingest/configure, read lock for session_status/estimate/preview. Lock timeout: 30 seconds → CRPError(code=1003).

CachedResult dataclass

Cached dispatch result for idempotency.

is_expired property

Return whether this object is expired.

DeduplicationStats dataclass

Statistics for request deduplication.

RequestDeduplicator

Idempotency key manager with TTL-based cache.

Concurrency: session-level write lock for mutations, reads are lock-free.

stats property

Return the stats.

cache_size property

Return the current cache count.

compute_key(system_prompt, task_input, **kwargs)

Compute idempotency key from request parameters.

check(key)

Check if a non-expired result exists for this key.

Returns cached result or None.

store(key, output, report=None)

Store a dispatch result in the cache.

invalidate(key)

Remove a cached entry. Returns True if found and removed.

clear()

Clear all cached entries. Returns count of entries cleared.

SessionLock

Read-write lock with ordering for session operations.

Lock ordering (deadlock prevention): 1. Cold Storage Lock 2. Model Registry Lock 3. Session Write Lock

Write lock for: dispatch, ingest, configure, reset, close Read lock (no-op) for: session_status, estimate, preview

acquire_write()

Acquire write lock. Returns True if acquired within timeout.

release_write()

Release the session write lock.

acquire_read()

Acquire read lock (currently a no-op for reads).

release_read()

Release the read lock (no-op in the current implementation).

core.ledger_backends

crp.core.ledger_backends

Ledger forwarding sinks (§7.14.5, CRP 2.3).

CRP 2.2's :class:~crp.core.manifest_ledger.ManifestLedger wrote JSONL files to the local filesystem. That is sufficient for a single node but fails in two real operational settings:

  • Distributed workers - multiple replicas need their ledger entries aggregated in a central store (SIEM, SQL, data-lake) so auditors see a single timeline.
  • Tamper-evidence in depth - the CRP 2.3 hash-chain catches edits on the local JSONL file, but a root-level attacker could still delete the file. Forwarding every record to an append-only external system closes that hole.

These sinks implement the existing :class:~crp.core.context_enforcer.AuditSink protocol (emit(event)) so the same wiring serves enforcement events and ledger writes. Pass the sinks in via ManifestLedger(forward_to=[...]).

All sinks here are pure-stdlib. Production deployments should use HTTPS with rotating tokens and rely on the SIEM's native replay/retention.

NullSink

No-op sink. Useful when a configuration requires a sink object but nothing should be forwarded (e.g. in tests).

emit(event)

Drop the event silently.

JSONLinesFileSink

Append every event as one JSON object per line to a local file.

Thread-safe: all writes are serialised on an internal lock. Intended for replicating ledger records to a location monitored by Splunk Universal Forwarder, Vector, Fluentd, etc.

emit(event)

Append event as one JSON line to the configured file.

Parameters:

Name Type Description Default
event dict[str, Any]

Audit or ledger record to persist.

required

HTTPForwardingSink

POST every event as JSON to an HTTPS endpoint.

Intended for Splunk HEC, Elastic Common Schema ingest, Datadog Logs HTTP intake, Azure Monitor, AWS CloudWatch Logs (via signed gateway), or a bespoke SIEM collector.

Failures are logged at WARNING; the sink never raises out to the caller. Callers that need guaranteed delivery should wrap this in :class:AsyncBufferedSink and pair it with a retry layer.

emit(event)

POST event as JSON to the configured HTTP endpoint.

Parameters:

Name Type Description Default
event dict[str, Any]

Audit or ledger record to forward.

required

AsyncBufferedSink

Background-thread buffer wrapping a downstream sink.

Enqueues events in memory (bounded; default 10k) and drains them on a single worker thread. Keeps ledger writes off the hot path when the downstream sink is slow or flaky.

Call :meth:close on shutdown to flush. Events that cannot be enqueued (queue full) are dropped and logged - operators should size the queue for their write rate.

emit(event)

Enqueue event for background forwarding.

Parameters:

Name Type Description Default
event dict[str, Any]

Audit or ledger record to enqueue.

required

close(timeout=5.0)

Signal shutdown and (optionally) wait for the queue to drain.

core.manifest_derive

crp.core.manifest_derive

Derive context sources/manifests from raw message content (CRP 2.3).

Problem

CRP 2.1/2.2 manifests are declarative - the integrator must author them. A malicious or lazy integrator who never calls :meth:ContextManifest.add receives no enforcement benefit. The enforcer cannot distinguish "no manifest because this turn is genuinely ephemeral" from "no manifest because the caller is hostile".

Solution

Given any list of chat messages ([{"role": ..., "content": ...}]), derive a :class:ContextManifest by hashing the content of each message and emitting a :class:ContextSource with role-appropriate defaults:

===================== ========================== =============== role :class:SourceKind :class:TrustLevel ===================== ========================== =============== system SYSTEM_PROMPT TRUSTED developer DEVELOPER_PROMPT TRUSTED user USER_TURN UNKNOWN assistant PARAMETRIC UNKNOWN tool / function FUNCTION_CALL UNKNOWN other UNATTESTED UNKNOWN ===================== ========================== ===============

The resulting manifest is not signed by default - signing it would misrepresent the trust: derivation says "this is what came through the wire", not "a human declared this is safe". When the caller opts in via sign_with=, the signature covers exactly the content the derivation saw.

The content hash lives in each source's metadata["content_sha256"] so downstream audit systems can correlate a manifest entry back to the raw message it was produced from.

Every source derived here is stamped origin=OBSERVED so it is distinguishable from DECLARED sources in a human-authored manifest.

content_hash(text, *, algo='sha256')

Return "{algo}:{hex}" for text. Stable across processes.

source_id_for_role(role, index, *, session_id=None)

Return a stable, human-readable source_id for a derived message source.

derive_source_from_message(message, *, index=0, session_id=None, default_trust=None)

Return (source, content_text) for one chat message.

Parameters

message A chat message dict with at least a role and content key. Follows OpenAI chat-completions convention; Anthropic messages (which omit role on the server's system parameter) should be normalised by the caller before invocation. index Position within the originating message list - used to build a stable source_id. session_id Optional session scope; included in the source_id. default_trust Overrides the role-default trust for all messages (useful when the caller already knows e.g. every user turn is untrusted).

derive_sources_from_messages(messages, *, session_id=None, default_trust=None)

Derive one (source, content) pair per message.

derive_manifest_from_messages(messages, *, session_id=None, system_id='', customer_id='', default_trust=None, sign_with=None)

Build a :class:ContextManifest from raw chat messages.

Each message becomes one declared source; the full content hash is captured in source.metadata["content_sha256"] so the manifest is replayable against the exact bytes that crossed the wire.

Passing sign_with= yields a signed manifest - the signature attests only that these specific bytes passed through derivation, not that a human reviewed them.

core.manifest_ledger

crp.core.manifest_ledger

Manifest ledger - cross-turn continuity and replay (§7.14.5, CRP 2.2).

Motivation

In 2.1, each turn builds a fresh :class:ContextManifest. There is no persistent record answering questions like:

  • "Did this fact in turn 9 originate from the web-search source declared in turn 3?"
  • "Show me the full manifest history for session X from six months ago."
  • "Which turns exposed content from acme-hr-policies-vdb?"

The ledger persists a per-session sequence of signed manifests to the filesystem (crp_sessions/<session_id>.manifest.jsonl) and maintains an in-memory lineage index keyed by source_id.

Storage format

One JSON object per line (JSONL). Each line is a complete signed manifest plus a turn field and a recorded_at epoch stamp. Append-only; tamper-evident via the HMAC signature carried inside each record.

Replay

:meth:ManifestLedger.load rehydrates a session's history into memory. :meth:ManifestLedger.find_by_source_id walks the index and returns every (turn, manifest) pair where the source was declared.

This module deliberately stores on disk only; integrators who need a DB back-end can wrap ManifestLedger - the API is intentionally small.

LedgerChainError

Bases: ManifestValidationError

Raised when the ledger hash-chain fails verification.

ManifestLedgerEntry dataclass

One recorded manifest within a session's history.

CRP 2.3 adds prev_hash + entry_hash fields forming a tamper-evident hash chain. Older entries without these fields (written by CRP 2.2) load with empty strings and are flagged by :meth:ManifestLedger.verify_chain.

to_jsonl()

Serialise the entry to a single JSONL line.

from_jsonl(line) classmethod

Create a new instance from a JSON Lines record.

Parameters:

Name Type Description Default
line str

The line value.

required

Returns:

Type Description
ManifestLedgerEntry

ManifestLedgerEntry.

ManifestLedger

Append-only, per-session manifest store.

The ledger is cheap to construct (no I/O until record or load) and thread-safe for all public methods. One instance per session is the typical pattern; integrators may share a single ledger across sessions if they choose.

session_dir property

Return the session dir.

record(session_id, manifest, *, turn=None)

Append manifest to the ledger for session_id.

Returns the created :class:ManifestLedgerEntry. If turn is not supplied, it defaults to len(existing) + 1 - callers that sequence turns themselves should pass the explicit number.

load(session_id)

Rehydrate a session's ledger from disk into memory.

Idempotent - calling repeatedly returns the same list. Missing files yield [].

history(session_id)

Return all entries for session_id (loads from disk if needed).

latest(session_id)

Return the most recent ledger entry for session_id.

Parameters:

Name Type Description Default
session_id str

Session identifier to query.

required

Returns:

Type Description
ManifestLedgerEntry | None

The last recorded entry, or None if the session has no history.

find_by_source_id(source_id, *, session_id=None)

Every entry whose manifest declares source_id.

If session_id is provided, search is limited to that session - otherwise the currently-cached sessions are scanned. Disk-only sessions are NOT discovered automatically; call :meth:load first to include them.

find_by_kind(kind, *, session_id=None)

Every entry whose manifest declares at least one source of kind.

verify_signatures(session_id, secret)

Return entries whose signature does NOT verify under secret.

Useful for periodic ledger-integrity audits. An empty list means every entry verifies successfully.

clear_cache(session_id=None)

Drop in-memory cache for session_id (or all if None).

verify_chain(session_id, *, strict=False)

Re-compute every entry's hash chain for session_id.

Returns [(turn, reason), ...] for each broken entry. An empty list means the ledger is intact. When strict=True and an entry is missing its entry_hash (written by CRP 2.2 pre-chain) this is treated as a break; otherwise pre-2.3 rows are tolerated.

CRP 2.3 tamper-evidence: any post-write edit of a JSONL line changes that entry's recomputed hash and causes every subsequent entry to report prev_hash_mismatch.

scan_sessions()

Enumerate every session with a ledger file on disk.

Returns a sorted list of session IDs. Useful before calling :meth:find_by_source_id or :meth:find_by_kind without a session_id argument - those methods only search in-memory sessions, so on-disk-only sessions are invisible unless scanned and loaded first.

load_all()

Scan disk and rehydrate every session ledger.

Equivalent to calling :meth:scan_sessions and then :meth:load on each. Returns a dict keyed by session_id.

KeyProvider

Minimal key-provider interface for manifest signing.

Keeps CRP out of the secret-management business while still offering enough structure for rotation, grace periods, and audit-trail binding. Integrators with HSMs, KMS, or Vault plug in by subclassing.

current()

Return the active secret for signing new manifests.

candidates()

Iterate over every secret that should verify against historical manifests - current first, then retired keys within the grace window. Used during rotation.

verify(manifest)

Try each candidate key; return True if any verifies.

EnvVarKeyProvider

Bases: KeyProvider

Read the active secret from an environment variable.

env_var (default CRP_MANIFEST_SECRET) must contain at least 32 bytes of entropy encoded as either raw UTF-8 or hex. Hex is detected automatically when the value is an even-length [0-9a-f]+ string.

This is the minimum viable provider - sufficient for single-node deployments and tests. Production deployments should use a rotating provider backed by KMS/Vault and inject RotatingKeyProvider.

current()

Execute current and return the result.

Returns:

Type Description
bytes

bytes.

candidates()

Execute candidates and return the result.

Returns:

Type Description
Iterable[bytes]

Iterable[bytes].

RotatingKeyProvider

Bases: KeyProvider

Key provider with an in-process rotation ring.

The caller owns actual key storage (KMS / Vault / file). This class is a coordination primitive: during rotation, both the new and the previous key verify, so in-flight manifests signed under the old key remain valid for a configurable grace window.

Usage::

kp = RotatingKeyProvider(initial=load_from_kms())
...
# Rotate when KMS emits a new version
kp.rotate(new_key=load_from_kms(), grace=timedelta(hours=24))

Thread-safe for rotate, current, and candidates.

rotate(new_key)

Promote new_key to current and retire the previous key.

retire_all()

Drop every retired key - active key continues to sign/verify.

current()

Execute current and return the result.

Returns:

Type Description
bytes

bytes.

candidates()

Execute candidates and return the result.

Returns:

Type Description
Iterable[bytes]

Iterable[bytes].

core.orchestrator

crp.core.orchestrator

CRP orchestrator - full-featured dispatch with all subsystems (§2.5, §6.5).

Capabilities
  • Envelope-based dispatch with 6-phase fact packing
  • Graduated 6-stage extraction pipeline on outputs + ingestion
  • WarmStateStore with CriticalState / StructuralState
  • CKF (Contextual Knowledge Fabric) 4-mode retrieval
  • Multi-window continuation with 3-way termination
  • Token measurement & budget validation
  • Message assembly per Axiom 4 (Model Ignorance)
  • QualityReport with real extraction & security stats
  • Session status & cost estimation
  • Budget cap enforcement
  • Streaming dispatch (§6.10.5)
  • Zero-LLM ingestion with graduated extraction (§2.5)
  • State export (§2.5)
  • Injection detection (advisory, §7.5)

CRPOrchestrator

Bases: DispatchMixin, ExtractionMixin

Central dispatcher managing window lifecycle and protocol operations.

Integrates ALL CRP subsystems: - ExtractionPipeline (graduated 6-stage extraction on outputs + ingestion) - WarmStateStore (persistent fact accumulation, ranking, aging) - CKF (ContextualKnowledgeFabric - 4-mode retrieval) - Envelope builder (6-phase budget-aware fact packing) - ContinuationManager (3-way termination, gap analysis, stitching) - SecurityManager (input validation, injection detection, RBAC, etc.)

Parameters:

Name Type Description Default
provider LLMProvider | None

Primary LLM provider. llm is accepted as an alias.

None
config CRPConfig | None

Optional CRPConfig instance.

None
llm LLMProvider | None

Alias for provider (backwards compatibility).

None
model str | None

Model name for auto-detection when no provider is given.

None
**init_kwargs Any

Extra configuration passed to ConfigurationResolver.

{}

emitter property

Protocol event bus for subscribing to events.

feedback property

Access the feedback loop for fact confidence adjustments.

parallel property

Parallel fan-out engine for multi-task dispatch.

session property

Return the session.

dag property

Return the dag.

config property

Return the config.

warm_store property

Access the WarmStateStore for direct fact operations.

ckf property

Access the CKF for 4-mode retrieval queries.

compliance_audit property

Access the compliance audit trail for inspection (§7.14).

pii_scanner property

Access the PII scanner (§7.12).

consent_manager property

Access the consent manager (§7.13).

processing_records property

Access the processing record keeper - GDPR Art. 30 (§7.13).

retention_manager property

Access the retention manager (§7.12).

lineage_tracker property

Access the data lineage tracker (§7.12).

human_oversight property

Access the human oversight controller - EU AI Act Art. 14 (§7.13).

compliance_reporter property

Access the compliance reporter (§7.15).

risk_classifier property

Access the risk classifier - EU AI Act Art. 6 (§7.15).

extraction_pipeline property

Access the extraction pipeline for configuration.

dispatch_with_strategy(strategy, system_prompt, task_input, **kwargs)

Route a dispatch to the named relay strategy.

Unknown strategies fall back to push-based dispatch with a warning.

session_status()

Get live session metrics.

Returns:

Type Description
SessionStatus

A SessionStatus with current counters and remaining budget.

Raises:

Type Description
SessionClosedError

If the session is closed.

SessionExpiredError

If the session has expired.

RateLimitExceededError

If RBAC denies READ_STATE.

estimate_session(system_prompt='', task_input='', *, planned_dispatches=1, avg_output_tokens=None)

Pre-flight cost estimation WITHOUT executing LLM calls.

Parameters:

Name Type Description Default
system_prompt str

Representative system prompt for token estimation.

''
task_input str

Representative task input for token estimation.

''
planned_dispatches int

Number of dispatches planned (default: 1).

1
avg_output_tokens int | None

Expected average output tokens per dispatch. If None, assumes model uses full generation reserve.

None

Returns:

Type Description
CostEstimate

CostEstimate with token estimates and USD cost (if pricing known).

preview_envelope(system_prompt, task_input)

Inspect envelope contents WITHOUT dispatching.

Parameters:

Name Type Description Default
system_prompt str

System prompt for the dispatch.

required
task_input str

User task input.

required

Returns:

Type Description
EnvelopePreview

An EnvelopePreview with token counts and included facts.

Raises:

Type Description
SessionClosedError

If the session is closed.

SessionExpiredError

If the session has expired.

RateLimitExceededError

If RBAC denies READ_ENVELOPE.

configure(**kwargs)

Apply runtime configuration changes (mutable fields only).

Parameters:

Name Type Description Default
**kwargs Any

Key-value pairs to update in the resolved config.

{}

Raises:

Type Description
SessionClosedError

If the session is closed.

SessionExpiredError

If the session has expired.

RateLimitExceededError

If RBAC denies CONFIGURE.

reset_session()

Clear warm state, CKF, event log, DAG. Keeps session open.

Raises:

Type Description
SessionClosedError

If the session is closed.

SessionExpiredError

If the session has expired.

RateLimitExceededError

If RBAC denies MANAGE_SESSIONS.

on(event_type, listener)

Subscribe to a protocol event (convenience wrapper).

Usage::

def on_dispatch(event):
    print(f"Dispatch completed: {event.data}")

orch.on("dispatch.completed", on_dispatch)

boost_fact(fact_id, delta=0.1, reason='')

Boost a fact's confidence (positive feedback).

Parameters:

Name Type Description Default
fact_id str

Fact identifier.

required
delta float

Amount to increase confidence.

0.1
reason str

Optional reason for the feedback.

''

penalize_fact(fact_id, delta=-0.2, reason='')

Penalize a fact's confidence (negative feedback).

Parameters:

Name Type Description Default
fact_id str

Fact identifier.

required
delta float

Amount to decrease confidence.

-0.2
reason str

Optional reason for the feedback.

''

reject_fact(fact_id, reason='')

Reject a fact entirely (user override).

Parameters:

Name Type Description Default
fact_id str

Fact identifier.

required
reason str

Optional reason for rejection.

''

register_provider(provider)

Register an additional LLM provider for fallback routing.

Parameters:

Name Type Description Default
provider LLMProvider

Additional provider to register.

required

close()

Close session - flush warm→cold, persist CKF, zero keys (§2.5).

This method is thread-safe and idempotent.

resume(session_id, provider=None, *, llm=None, data_dir=None, **kwargs) classmethod

Resume a previously closed session by restoring CKF state.

Loads persisted facts from cold storage into a new orchestrator instance, providing cross-session knowledge continuity.

Parameters:

Name Type Description Default
session_id str

The session_id from the previous session.

required
provider LLMProvider | None

LLM provider to use for the resumed session.

None
llm LLMProvider | None

Alias for provider.

None
data_dir str | None

Override for CRP_DATA_DIR (default: env or '.').

None
**kwargs Any

Additional arguments forwarded to the orchestrator init.

{}

Returns:

Type Description
CRPOrchestrator

A new CRPOrchestrator with the previous session's facts loaded.

Raises:

Type Description
ValidationError

If session_id is not a valid UUID.

export_state(fmt=None)

Export session state as encrypted bytes (§2.5).

Includes warm store facts, critical state, structural state, and CKF health. Embeddings are NOT exported (§7.11) - recomputed on import.

Parameters:

Name Type Description Default
fmt str | None

Export format (reserved, currently only 'json' supported).

None

Returns:

Type Description
bytes

Encrypted bytes (AES-256-GCM with session-bound key).

Raises:

Type Description
SessionClosedError

If the session is closed.

SessionExpiredError

If the session has expired.

RateLimitExceededError

If RBAC denies EXPORT_STATE.

async_dispatch(system_prompt, task_input, **kwargs) async

Async version of dispatch() - runs in a configurable thread pool.

Uses the orchestrator's own ThreadPoolExecutor (sized via max_threads config) instead of the default asyncio executor, which prevents thread-pool saturation under concurrent load.

Use from async code (FastAPI, asyncio, etc.)::

output, report = await client.async_dispatch(
    "You are helpful.", "Explain CRP."
)

async_ingest(raw_text, *, source_label='') async

Async version of ingest() - runs in the orchestrator's thread pool.

async_close() async

Async version of close() - runs in the orchestrator's thread pool.

async_dispatch_stream(system_prompt, task_input, **kwargs) async

Async streaming dispatch - yields StreamEvent objects.

Usage::

async for event in client.async_dispatch_stream(
    "You are helpful.", "Explain CRP."
):
    if event.event_type == "token":
        print(event.data, end="")

core.relay_strategies

crp.core.relay_strategies

Novel context relay strategies - beyond push and pull (§21).

Three fundamentally different approaches to providing context to LLMs:

§21.1 REFLEXIVE DISPATCH - Verify-then-Refine Generate first with zero context (pure parametric knowledge). CRP analyzes the output against its knowledge base, identifying factual errors, unsupported claims, and missing information. A targeted correction payload is assembled and sent back with the model's own output - the model refines with surgical precision instead of drowning in pre-loaded context.

   Analogy: A fact-checker reviews your first draft and hands
   you a marked-up copy with corrections and sources.

§21.2 PROGRESSIVE DISCLOSURE - Index → Detail on Demand Instead of full facts, send a compact CONTEXT INDEX - one-line summaries of every available fact, grouped by topic. The model sees WHAT knowledge exists without the full payload. It generates with awareness of available resources, and CRP detects which indexed items were referenced. A second pass provides full detail for only the referenced items.

   Analogy: A library card catalog - you browse titles first,
   then check out only the books you need.

§21.3 STREAM-AUGMENTED GENERATION - Real-time Context Injection Stream tokens from the LLM. Buffer into sentences. After each sentence, CRP runs real-time fact-matching against the knowledge base. When relevant facts are found, generation is paused, the partial output + injected context are sent as a new prompt, and the model continues from where it left off - now informed.

   Analogy: A research assistant who watches you write and slides
   relevant papers across the desk exactly when you need them.
Each strategy has fundamentally different characteristics
  • Reflexive: Best for ACCURACY - catches and corrects hallucinations
  • Progressive: Best for EFFICIENCY - minimal token usage, max coverage
  • Stream-aug: Best for COHERENCE - context arrives at point-of-need

FactCorrection dataclass

A correction identified by comparing output against knowledge base.

ReflexiveAnalysis dataclass

Result of analyzing model output against CRP knowledge base.

needs_refinement property

Whether the output needs a refinement pass.

ContextIndex dataclass

A compact index of available knowledge - titles/summaries only.

to_text()

Render the index as a compact text block for the LLM.

ContextIndexEntry dataclass

One entry in the context index.

AugmentationEvent dataclass

Record of a real-time context injection during streaming.

StreamAugmentationState dataclass

Tracks state during stream-augmented generation.

should_check property

Check after every N sentences to avoid excessive overhead.

analyze_output_against_kb(output, warm_store, count_tokens, embed_fn=None)

Compare LLM output against CRP knowledge base.

Extracts claims/sentences from the output and cross-references each against the WarmStore facts. Identifies: - Supported claims (fact backing exists) - Contradicted claims (conflicting evidence) - Unsupported claims (no evidence either way) - Enrichment opportunities (related facts not mentioned)

build_refinement_prompt(original_output, analysis, count_tokens, max_correction_tokens=3000)

Build the refinement prompt from reflexive analysis.

Returns a structured correction payload that tells the model exactly what to fix, with evidence.

build_context_index(warm_store, count_tokens, max_entries=50, max_index_tokens=1500)

Build a compact context index from WarmStore facts.

Each entry is compressed to a one-line summary (~15 tokens) instead of the full fact text (~50-200 tokens). This gives the LLM awareness of ALL available knowledge at ~10% of the token cost of sending full facts.

detect_index_references(output, index)

Detect which index entries were referenced in the output.

The LLM may reference entries by [F1], [F2] etc., or by mentioning key terms from the summary. Both are detected.

build_detail_injection(referenced_entries, count_tokens, max_tokens=3000)

Build the detail payload for referenced index entries.

Only the entries the model actually referenced get expanded to full text. This is maximally efficient - no wasted context.

find_relevant_facts_for_sentence(sentence, warm_store, already_injected, count_tokens, max_facts=3, max_tokens=500)

Find WarmStore facts relevant to a sentence that haven't been injected yet.

Returns [(fact_id, fact_text), ...] - only NEW facts not already served.

build_augmented_continuation(system_prompt, partial_output, injected_facts, task_input)

Build the message sequence for resuming after a stream augmentation.

The model receives: 1. System prompt (unchanged) 2. Original task 3. Its own partial output so far (as assistant message) 4. Injected context (as system/user interjection) 5. Instruction to continue from where it left off

core.security_manager

crp.core.security_manager

SecurityManager - facade for all CRP security subsystems (§audit4 CQ-C1).

Extracts the 18 security & compliance subsystem initializations from CRPOrchestrator.__init__() into a single cohesive class, reducing orchestrator init from ~360 lines to ~250 lines.

All subsystems remain accessible as attributes on the manager instance.

SecurityManager

Groups all CRP security and compliance subsystems (§7).

Subsystems initialized: - InputValidator (§7.4) - Layer 1, cannot be disabled - InjectionDetector (§7.5) - advisory, never blocks - SessionBindingManager (§7.1) - HMAC-SHA256 key derivation - RBACEnforcer (§7.10) - role-based access + rate limiting - FactIntegrityChain (§7.2, §7.7) - BLAKE3/SHA-256 hash chain - StateEncryptor (§7.3) - AES-256-GCM - IngestQuarantine (§7.8) - 1-window anti-poisoning - EmbeddingDefense (§7.11) - SQ8 + XOR salting - PIIScanner (§7.12) - pattern-based PII detection - RetentionManager (§7.12) - auto-expiry per classification - ErasureManager (§7.12) - GDPR Art. 17 right to erasure - DataLineageTracker (§7.12) - provenance tracking - ConsentManager (§7.13) - purpose-based consent - ProcessingRecordKeeper (§7.13) - GDPR Art. 30 records - HumanOversightController (§7.13) - EU AI Act Art. 14 - ComplianceAuditTrail (§7.14) - tamper-evident HMAC chain - RiskClassifier (§7.15) - EU AI Act risk classification - ComplianceReporter (§7.15) - regulatory reporting

session_key property

Return the session binding key for use by other subsystems.

core.session

crp.core.session

Session data types - SessionHandle, SessionStatus, CostEstimate.

These dataclasses implement the JSON schemas used by the CRP session API: session-handle.json, session-status.json, cost-estimate.json, quality-report.json, envelope-preview.json, stream-event.json, and persisted-state-header.json.

SessionHandle dataclass

Session identity and lifetime metadata.

Implements the session-handle.json schema. The session_key is managed separately by SessionBindingManager and is never serialised to JSON or cold storage.

Attributes:

Name Type Description
session_id str

Unique UUID for this session.

protocol_version str

CRP protocol version running.

capabilities list[str]

Operations the session supports.

created_at float

Unix timestamp of creation.

expires_at float

Unix timestamp after which the session is invalid.

is_expired property

Return True if the current time is past expires_at.

RemainingBudget dataclass

Remaining quota for a session.

Attributes:

Name Type Description
windows_remaining int | None

Windows left before max_windows_per_session.

input_tokens_remaining int | None

Input tokens left before max_total_input_tokens.

output_tokens_remaining int | None

Output tokens left before max_total_output_tokens.

SessionStatus dataclass

Live session metrics returned by session_status().

Implements the session-status.json schema.

Attributes:

Name Type Description
session_id str

Session identifier.

windows_completed int

Number of primary + continuation windows completed.

total_input_tokens int

Cumulative input tokens across all windows.

total_output_tokens int

Cumulative output tokens across all windows.

facts_in_warm_state int

Current number of facts in the warm store.

overhead_ratio float

Ratio of continuation windows to total windows.

remaining_budget RemainingBudget | None

Remaining quota, or None if no caps configured.

total_cost float | None

Estimated USD cost, or None if provider pricing unknown.

CostEstimate dataclass

Pre-flight cost estimate returned by estimate_session().

Implements the cost-estimate.json schema.

Attributes:

Name Type Description
estimated_windows int

Number of dispatches estimated.

estimated_input_tokens int

Total input tokens estimated.

estimated_output_tokens int

Total output tokens estimated.

estimated_cost_usd float | None

Estimated USD cost, or None if pricing unknown.

confidence str

"high", "medium", or "low" depending on input completeness.

SecurityFlags dataclass

Security validation results - always present in QualityReport (§7.4).

Attributes:

Name Type Description
injection_markers_detected int

Count of injection patterns found.

injection_marker_details list[dict[str, Any]]

Details for each detected marker.

unicode_normalized bool

Whether unicode input was normalised.

control_chars_stripped int

Number of control characters removed.

input_truncated bool

Whether input was truncated to fit budget.

integrity_violations int

Count of provenance-chain violations.

output_injection_facts_penalized int

Output-side injection detections (§7.5.2).

QualityReport dataclass

Governance summary returned by every dispatch() call.

Implements the quality-report.json schema. output is the COMPLETE, UNMODIFIED LLM output (Axiom 9).

Attributes:

Name Type Description
session_id str

Session identifier.

window_id str

Window identifier for this dispatch.

output str

Raw LLM output.

facts_extracted int

Number of facts extracted from the output.

security_flags SecurityFlags

Security validation results.

continuation_windows int

Number of continuation windows triggered.

envelope_saturation float

Envelope token utilisation ratio.

quality_tier str | None

"S", "A", "B", "C", or "D".

telemetry dict[str, Any] | None

Optional per-window telemetry dict.

EnvelopePreview dataclass

Envelope inspection result returned by preview_envelope().

Implements the envelope-preview.json schema.

Attributes:

Name Type Description
total_tokens int

System + task + envelope tokens.

envelope_tokens int

Tokens consumed by the constructed envelope.

generation_reserve int

Tokens reserved for LLM generation.

facts_included int

Facts packed into the envelope.

facts_available int

Total facts available in the warm store.

saturation float

Envelope token utilisation ratio.

StreamEvent dataclass

Single event emitted by dispatch_stream() / async_dispatch_stream().

Implements the stream-event.json schema. Concatenating all token events' data values MUST produce the same string as non-streaming output.

Attributes:

Name Type Description
event_type str

One of "token", "extraction", "continuation", "window_complete", "done", or "error".

data Any

Event payload (string for tokens, dict for other events).

PersistedStateHeader dataclass

Header for all persisted state data.

Implements the persisted-state-header.json schema.

Attributes:

Name Type Description
schema_version str

Data schema version.

protocol_version str

CRP protocol version.

created_at float

Unix timestamp of persistence.

checksum str

BLAKE3 hash of the payload.

core.task_intent

crp.core.task_intent

TaskIntent - structured task description for CRP dispatch (§2.3).

This module defines the TaskIntent dataclass and OutputType enum, which together describe what the user wants from a single dispatch. All fields are optional; CRP infers sensible defaults from raw task_input.

OutputType

Bases: str, Enum

Expected output formats per the task-intent.json schema.

TEXT = 'text' class-attribute instance-attribute

Plain text response.

JSON = 'json' class-attribute instance-attribute

Structured JSON response; may be validated against output_schema.

MARKDOWN = 'markdown' class-attribute instance-attribute

Markdown-formatted response.

CODE = 'code' class-attribute instance-attribute

Source code response.

TaskIntent dataclass

Structured task description per §2.3.

All fields are optional - CRP infers missing values from the raw task input. Matches the task-intent.json schema exactly.

Attributes:

Name Type Description
description str | None

Optional human-readable summary of the task.

system_prompt str | None

Optional system prompt override for this dispatch.

task_input str | None

The user's actual request / question.

expected_output_type OutputType | str | None

Desired response format.

max_windows int | None

Maximum continuation windows allowed.

max_output_tokens int | None

Maximum tokens to generate in this dispatch.

output_schema dict[str, Any] | None

JSON Schema for structured output validation.

metadata dict[str, Any]

Arbitrary key-value context for routing or observability.

core.window

crp.core.window

TaskWindow, WindowDAG, and window lifecycle types (§2.4, §5, SPEC-004).

Defines the provenance DAG, window state machine, context-transfer types, and budget helpers used by the orchestrator and continuation manager.

Window lifecycle

CREATED → ASSEMBLED → DISPATCHED → GENERATING → COMPLETED → EXTRACTED

WindowState

Bases: Enum

Lifecycle states - transitions are strictly forward-only.

WindowPattern

Bases: str, Enum

How a window relates to its parent(s) in the DAG (§3.1).

LINEAR = 'LINEAR' class-attribute instance-attribute

Single parent, single child chain.

FAN_OUT = 'FAN_OUT' class-attribute instance-attribute

One parent spawns multiple parallel children.

FAN_IN = 'FAN_IN' class-attribute instance-attribute

Multiple parents merge into one child.

BRANCH = 'BRANCH' class-attribute instance-attribute

Exploratory branch that may later merge or terminate.

TransferType

Bases: str, Enum

What information flows from a parent window to a child (§8.1).

FULL_CONTEXT = 'FULL_CONTEXT' class-attribute instance-attribute

Complete parent response plus envelope.

SUMMARY = 'SUMMARY' class-attribute instance-attribute

Compressed summary (default).

FACTS_ONLY = 'FACTS_ONLY' class-attribute instance-attribute

Deferred facts only, no response content.

RESULT_ONLY = 'RESULT_ONLY' class-attribute instance-attribute

Final answer only (fan-in synthesis).

WindowEdge dataclass

A directed context-flow edge from a parent to a child window (§3.2).

Attributes:

Name Type Description
source_id str

Parent window ID.

target_id str

Child window ID.

transfer_type TransferType

Mode of information transfer.

transferred_tokens int

Tokens carried across this edge.

summary_hash str

Hash of the transferred summary for integrity checks.

to_dict()

Serialise the edge to a JSON-compatible dict.

WindowNode dataclass

Represents a single task window in the DAG (§2.4).

advance(to_state)

Transition to a new state, enforcing the forward-only invariant.

Parameters:

Name Type Description Default
to_state WindowState

Target state.

required

Raises:

Type Description
ValueError

If the transition is not allowed.

WindowMetrics dataclass

Recorded per window for telemetry.jsonl output.

to_dict()

Serialise to a flat dict for JSONL telemetry output.

WindowDAG

Tracks the provenance graph of all windows in a session (§2.4, Axiom 7).

nodes property

Mapping from window ID to WindowNode.

edges property

List of all context-flow edges.

add_node(node)

Register a created window.

Parameters:

Name Type Description Default
node WindowNode

Window node to add.

required

Raises:

Type Description
ValueError

If the window ID already exists.

set_parent(child_id, parent_id)

Declare a provenance edge: parent contributed facts to child.

Parameters:

Name Type Description Default
child_id str

Child window ID.

required
parent_id str

Parent window ID.

required

add_edge(edge)

Record a context-flow edge with transfer metadata (§3.2).

Establishes the parent/child relationship and rejects edges that would introduce a cycle, preserving the acyclic invariant (§3.3).

descendants(window_id)

Return all transitive descendant window IDs.

Parameters:

Name Type Description Default
window_id str

Window to descend from.

required

Returns:

Type Description
set[str]

Set of all descendant window IDs (transitive children).

get(window_id)

Return the WindowNode for window_id.

roots()

Return windows with no parents (initial windows).

leaves()

Return windows with no children (terminal windows).

ancestors(window_id)

Return all transitive ancestor window IDs.

Parameters:

Name Type Description Default
window_id str

Window to ascend from.

required

Returns:

Type Description
set[str]

Set of all ancestor window IDs (transitive parents).

window_count()

Return the number of windows in the DAG.

lineage(window_id)

Return the DAG path from a root to window_id (CRP-SPEC-004 §14.2).

Follows the first parent at each step; suitable for emitting CRP-Provenance-Window-Lineage (root -> ... -> current).

detect_cycle()

Return True if the graph contains a cycle (violates §3.3).

Uses a three-colour DFS over all nodes.

clear()

Remove all nodes and edges (session reset).

select_transfer_type(*, available_budget, parent_response_tokens, summary_tokens, deferred_facts_tokens, reserve_fraction=0.6)

Select a transfer type from token-budget pressure (§8.2).

The reserve_fraction (default 0.60) leaves 40% of the budget for new facts specific to the child's query.

Parameters:

Name Type Description Default
available_budget int

Tokens available in the child window.

required
parent_response_tokens int

Tokens of the full parent response.

required
summary_tokens int

Tokens of a compressed parent summary.

required
deferred_facts_tokens int

Tokens of deferred facts.

required
reserve_fraction float

Fraction of budget that may be spent on context relay.

0.6

Returns:

Type Description
TransferType

The most complete transfer type that fits within the reserved budget.

partition_fan_in_budget(parent_budgets)

Fan-in safety budget is the MINIMUM of all parents (§7.3.4, §6.4).

Parameters:

Name Type Description Default
parent_budgets list[float]

Envelope budgets of each parent window.

required

Returns:

Type Description
float

The conservative minimum budget, or 0.0 if no parents exist.

resolve_generation_reserve(max_output_tokens, provider_max_output, context_window, is_thinking_model=False)

Determine generation reserve G by 3-layer precedence (§2.1).

Parameters:

Name Type Description Default
max_output_tokens int | None

User-specified output limit (highest precedence).

required
provider_max_output int | None

Provider-reported output limit.

required
context_window int

Model context window size.

required
is_thinking_model bool

Whether the model uses internal reasoning tokens.

False

Returns:

Type Description
int

The resolved generation reserve in tokens.

Layer 1: User explicit (TaskIntent.max_output_tokens) Layer 2: Provider reported (LLMProvider.max_output_tokens) Layer 3: Conservative default scaled to the context window.

Small-context models (4K / 8K) cannot afford a full C//4 generation reserve because it leaves almost no room for the envelope. The continuation manager stitches multi-window answers together, so each individual window only needs a modest generation budget:

  • C <= 4096 → 384 tokens
  • C <= 8192 → 768 tokens
  • otherwise → min(C // 4, 16384)

For thinking models (qwen3, deepseek-r1, o1, etc.) the reserve is doubled because the model spends a significant portion of tokens on internal reasoning_content before producing final output.

compute_envelope_budget(context_window, system_tokens, task_tokens, generation_reserve)

Compute E = C - S - T - G (§2.1, Axiom 2).

Parameters:

Name Type Description Default
context_window int

Model context window size C.

required
system_tokens int

Tokens consumed by the system prompt S.

required
task_tokens int

Tokens consumed by the task input T.

required
generation_reserve int

Tokens reserved for generation G.

required

Returns:

Type Description
int

Non-negative envelope budget E.