v0.3.0
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog,
and this project adheres to Semantic Versioning.
[Unreleased]
[0.3.0] - 2026-05-11
Added
- Minimal core dependencies and extras infrastructure (#49, #50, #54, #55)
pyproject.tomldependencies = ["tiktoken>=0.5", "PyYAML>=6.0", "rank-bm25>=0.2"]
— three small, broadly-used packages that unblock default behaviour the library
would otherwise have to approximate (exact token counts, YAML configs, BM25 retrieval).- New optional extras:
[cli](rich),[retrieval](rapidfuzz),
[ann](hnswlib, reserved),[otel](opentelemetry),[graph](networkx, reserved),
[all](union convenience). - mypy overrides for every new optional package so missing extras don't break type checks.
- YAML catalog and graph support (#54)
contextweaver.routing.catalog.load_catalog_yaml()— load a catalog from a YAML file.contextweaver.routing.catalog.load_catalog()— auto-detect JSON vs. YAML by file
extension (.yaml/.yml→ YAML, anything else → JSON).save_graph()/load_graph()inrouting.graph_ionow auto-detect format from
the file extension and emit deterministic YAML (sort_keys=True).examples/sample_catalog.yaml— runnable YAML version ofsample_catalog.json.
- BM25 and fuzzy retrieval backends (#55)
contextweaver._utils.BM25Scorer— BM25 scorer backed byrank-bm25(core dep);
samefit/score/score_allinterface asTfIdfScorer.contextweaver._utils.FuzzyScorer— fuzzy string-similarity scorer backed by
rapidfuzz; available whencontextweaver[retrieval]is installed,
FuzzyScorer is Noneotherwise.Router(scorer_backend="bm25" | "tfidf" | "fuzzy")— keyword-only parameter to
select a scorer by name; default remains"tfidf"for backward compatibility.
Unknown backend names raiseConfigError. Cooperates with the
engine_registry/retrieverplumbing from issue #47.
- Production observability primitives (#10)
- New
contextweaver.metricsmodule withMetricsCollector(thread-safe
accumulator withsummary()+reset()) andMetricsHook(concrete
EventHookimplementation that feeds a collector). ContextManager(metrics=...)— optionalMetricsCollectorparameter; when
present, fullRouteResultis recorded after every routing call (capturing
candidate count, top score, and confidence gap).ContextManager.metricsproperty exposes the configured collector (orNone).- Counters tracked: total builds, total routes, total prompt tokens, dedup
removals, firewall interceptions, items excluded, budget overruns, and a
mergeddrop_reasonsmap.
- New
- OpenTelemetry integration (#57)
- New
contextweaver.extras.otel.OTelEventHook—EventHookimplementation
that emits OTel spans (contextweaver.context.build,contextweaver.context.firewall,
contextweaver.context.exclude,contextweaver.routing.route) and metrics
(contextweaver.tokens.usedhistogram,contextweaver.firewall.interceptionscounter,
contextweaver.items.excludedcounter,contextweaver.budget.exceededcounter,
contextweaver.routing.candidateshistogram). - Available via
pip install 'contextweaver[otel]'. Importing the module
without the extra raises anImportErrorcarrying the exact install hint.
- New
- Enhanced CLI rendering via
[cli]extra (#52)__main__.pyprint-treesubcommand usesrich.treefor coloured output
when rich is installed (pip install 'contextweaver[cli]');
stdlib argparse + plain ASCII path remains byte-identical when the extra
is absent.
- Public API exports
- Top-level:
MetricsCollector,MetricsHook,BM25Scorer,FuzzyScorer,
load_catalog,load_catalog_yaml.
- Top-level:
- Routing — negative routing (#112).
Router.route()accepts new
keyword-onlyexclude_ids: set[str] | Noneandexclude_tags: set[str] | None
parameters that drop matching items before beam search.
RouteResult.excluded_countreports how many items were filtered. - Routing — context-aware shortlisting (#116).
Router.route()accepts
a new keyword-onlycontext_hints: list[str] | Noneparameter; hints
are appended to the scoring query without altering the catalog or graph. - Routing — toolset gating (#22).
Router.route()accepts new
keyword-onlyallowed_namespaces: set[str] | Noneand
allowed_tags: set[str] | Nonewhitelists.RouteResult.gated_count
reports how many items were filtered. - Routing —
CatalogNormalizer(#44). New
contextweaver.routing.normalizer.CatalogNormalizerand
NormalizationReportapply deterministic metadata hygiene
(case-insensitive tag dedupe, whitespace collapsing, namespace
trimming, description fallback) to raw catalog imports. - Routing —
GraphManifest(#48). New
contextweaver.routing.manifest.GraphManifestrecords build hash,
seed, engine versions, timestamp, item count, strategy, and depth on
every graph built byTreeBuilder.build(). Survives
ChoiceGraph.to_dict()/from_dict()round-trips. Helper
compute_catalog_hash()is exported from the top-level package. - Routing — incremental graph cache (#15).
TreeBuilder.build()
caches built graphs by catalog hash. Subsequent calls with an
unchanged catalog return the cached graph in O(n) rather than
rebuilding. Useuse_cache=Falseto force a rebuild;
clear_cache()drops all cached graphs. - Routing —
RouteTrace(#51). New
contextweaver.routing.trace.RouteTraceandTraceStepdataclasses.
Always populated onRouteResult.trace; per-step beam expansions
remain opt-in viadebug=True. The legacy
RouteResult.debug_traceshape is preserved as a@propertythat
delegates toRouteTrace.to_legacy_dicts()for backward compatibility. - Routing — uncertainty signals (#14).
RouteResultgains
is_ambiguous: boolandclarifying_question: str | None. Set when
the rank-1/rank-2 gap is below the router'sconfidence_gap
threshold; the question is rendered from the most distinguishing
dimension (namespace or name) of the top candidates. - Routing —
EngineRegistry(#47). New
contextweaver.routing.registry.EngineRegistrywithRetriever,
Reranker, andClusteringEngineprotocols onprotocols.py.
Bundled defaults:TfIdfRetriever(wrapsTfIdfScorer),
NoOpReranker, andJaccardClusteringEngine. Module-level
default_registryis pre-populated with the in-tree defaults;
callers may register alternative engines under the"retriever",
"reranker", and"clustering"slots. - Config —
Modeenum andProfileConfig.mode(#45). New
contextweaver.profiles.Modeenum with valuesstrict(default),
seeded, andadaptive(FUTURE placeholder).ProfileConfig
gains amode: Modefield and an optionalseed: int | Nonefield;
both round-trip throughto_dict()/from_dict(). Unknown mode
strings onfrom_dict()raiseConfigError.
ProfileConfig.from_profile()added as a backwards-compatible alias
forfrom_preset(). - Config —
ContextManager.profile(#45).ContextManager.__init__
accepts a keyword-onlyprofile: ProfileConfig | Noneparameter that
fillsbudget,policy, andscoring_configfrom the profile when
per-arg overrides are not supplied. NewContextManager.profileand
ContextManager.modeproperties expose the active profile and mode. - Routing —
TreeBuilder.routing_config(#45).TreeBuilder.__init__
accepts a keyword-onlyrouting_config: RoutingConfig | Noneparameter
that populatesmax_children.Routeralready accepted this
parameter in v0.2.0. ScoringConfig.dedup_thresholdfield — exposes the Jaccard dedup threshold
(default 0.85) via configuration;ContextManagernow passes it through to
deduplicate_candidates()(#182)to_dict()/from_dict()onContextPolicy,ContextBudget, and
ScoringConfig— completes the repo-standard serialisation methods on all
config dataclasses (#184)EpisodicStoreandFactStoreprotocols — formal@runtime_checkable
protocol interfaces matching theInMemory*method signatures;StoreBundle
type hints widened to protocol types (#40)store/protocols.pymodule — store-layer protocols (EventLog,
ArtifactStore,EpisodicStore,FactStore) extracted fromprotocols.py
to stay within the ≤300-line guideline; still importable from
contextweaver.protocolsandcontextweaverfor backward compatibilityprofiles.pymodule —Mode,RoutingConfig, andProfileConfiglive in
contextweaver.profilesto stay within the ≤300-line guideline; importable
fromcontextweaver.profilesandcontextweaver(#179)
Changed
- Documentation: minimal-core-deps reframe (#53)
- README front-matter:
"zero runtime dependencies"→"minimal core dependencies",
"deterministic output"→"deterministic by default". - README installation section gains an extras table covering every optional
capability shipped today. AGENTS.mdstyle rule rewritten: zero core runtime deps + extras model;
new core deps require broad ecosystem use, small wheel, and unblocked
default behaviour.CONTRIBUTING.mdand.github/copilot-instructions.mdupdated to match.
- README front-matter:
TiktokenEstimatorsimplified —tiktokenis now a core dep, so the
try/except-stub fallback is gone. The estimator still degrades gracefully
toCharDivFourEstimatorwhen the tiktoken encoding download fails (offline
/ air-gapped environments) and logs a warning namingTIKTOKEN_CACHE_DIR
as the workaround.Router.__init__now raisesConfigError(aContextWeaverError
subclass) instead of bareValueErrorwhenconfidence_gapis
outside[0.0, 1.0].RouteResult.traceis the new authoritative trace surface;
RouteResult.debug_traceis preserved as a backwards-compatible
property delegating totrace.to_legacy_dicts().TreeBuilder.build()now records the effectivemax_childrenunder
manifest.extra["max_children"], honouring the docstring contract
that the value is persisted on the graph manifest.Router.__init__accepts new keyword-onlyretriever: Retriever | None
andengine_registry: EngineRegistry | Noneparameters so the
pluggableEngineRegistryfrom issue #47 is now wired end-to-end.
The legacyscorer: TfIdfScorer | Noneparameter is still accepted
and is transparently wrapped in an internalRetrieveradapter.TreeBuilder.__init__accepts new keyword-only
clustering: ClusteringEngine | Noneand
engine_registry: EngineRegistry | Noneparameters; the
cluster-grouping strategy now delegates to the configured engine
(default:JaccardClusteringEnginefromdefault_registry) instead
of the previous inline algorithm. Rebalancing of oversized clusters
remains the builder's responsibility.Retrieverprotocol now exposesscore_one(query, index) -> float
for per-document scoring at arbitrary corpus indices (used by the
Router beam search). The bundledTfIdfRetrieverimplements it.RouteResultexposes two new fields —context_hints: list[str]
andcontext_boost_applied: bool— so callers can introspect
whether the issue #116 context-hint augmentation actually altered
the scoring query. The same values round-trip on
RouteTrace.extra["context_hints"]/extra["context_boost_applied"].RouteTrace.retriever_engineis now populated from the resolved
engine name instead of being hard-coded to"tfidf".ProfileConfig.to_dict()/from_dict()now includepolicy(previously
excluded becauseContextPolicylacked serialisation); docstring expanded
to make the round-trip contract explicit (#184)ContextManager.episodic_store/fact_storeproperties now return protocol
types (EpisodicStore/FactStore) instead of concreteInMemory*types (#40)StoreBundle.to_dict()/from_dict()docstrings now spell out the
silent-Noneround-trip behaviour for custom backends that lack a
to_dict()methodMode,RoutingConfig, andProfileConfigare not re-exported from
contextweaver.config; import them fromcontextweaver.profilesor the
top-levelcontextweaverpackage. Dropping the re-export resolves a
circular-import smell betweenconfig.pyandprofiles.py
Fixed
- Routing exclusions (
exclude_ids/exclude_tags) and toolset
gating (allowed_namespaces/allowed_tags) now happen
pre-scoring rather than only at result collection time. Previously
excluded leaf nodes could consume beam slots and prevent eligible
siblings from being explored under tightbeam_width. The router
now skips ineligible children (and internal nodes whose entire
subtree was filtered out) before scoring. RouteResult.is_ambiguousandRouteTrace.runner_up_scoreare now
computed from the untrimmed sorted view of beam-search results, so
callers usingtop_k=1still see the uncertainty signal introduced
by issue #14.routing.normalizermodule docstring now accurately describes
lenient-mode item drops (blank or duplicate IDs are dropped to
report.invalid_ids) instead of claiming the normalizer never
drops items.- Replaced 3 bare
ValueErrorraises incontext/sensitivity.pywith
PolicyViolationError/ConfigError(#183) - Replaced bare
ValueErrorinrouting/router.py(confidence_gapvalidation)
withConfigError(#183) BM25Scorer.fit()now feedsBM25Okapia per-document token list that
preserves term frequency. The previous implementation called
sorted(tokenize(doc))on aset, which collapsed duplicates and degraded
BM25 to a binary-match scorer. A newcontextweaver._utils.tokenize_list()
helper applies the same normalisation pipeline astokenize()but returns
alist[str];tokenize()now delegates to it for the unique-set view.
(review #188)MetricsCollectorno longer accumulates per-route values in unbounded
lists. Route-level statistics are tracked as running sums + maxima so
memory stays O(1) in long-running processes.summary()keys are
preserved and gain three new entries (max_candidates_per_route,
max_top_score,max_confidence_gap). (review #188)OTelEventHooknow recordscontextweaver.tokens.usedas a histogram
instead of a gauge. The synchronouscreate_gaugeinstrument only
landed inopentelemetry-api>=1.27, but the[otel]extra pins
>=1.20. Histograms are portable across the entire supported range and
give callers per-build token distributions instead of a latest-value
cache. (review #188)
Notes
_utils.py(392 lines),routing/catalog.py(~410 lines) androuting/router.py
(~400 lines) are over the 300-line module guideline. These files were already
approaching or over the limit before this PR; decomposition is tracked under
the routing-pipeline epic (#56).EventHook.on_route_completedretains itslist[str](tool ids) signature for
backward compatibility. FullRouteResultmetrics (top score, confidence gap)
flow throughContextManager.metricsinstead of the hook.Mode.adaptiveis currently a forward-compatible placeholder — no
pipeline stage is conditioned on the mode value yet. Selecting
Mode.adaptiveis accepted but has no behavioural effect.TreeBuilder.build()writes a deterministictimestamp=0.0to its
manifest so that two builds of identical inputs produce identical
graphs (per the AGENTS.md "Deterministic by default" invariant).
Callers wanting a wall-clock timestamp can replace the manifest via
graph.manifest = GraphManifest.for_build(items)after build.RouteResult.trace.stepsis empty unlessdebug=True; the rest of
the trace (top scores, ambiguity, exclusion / gating counts) is
always populated.
[0.2.0] - 2026-04-17
Added
.github/prompts/add-feature.prompt.md,.github/prompts/fix-bug.prompt.md, and.github/prompts/refactor-module.prompt.md— reusable step-by-step agent workflows for common tasks (feature addition, bug fixing, module refactoring), each with explicit_Success:criteria andmake cias the final gate (#96)SECURITY.md— vulnerability disclosure policy covering supported versions, GitHub Security Advisories channel, response timeline, and security scope (context firewall, prompt injection, adapter input validation, deserialization)StoreBundle.from_dict()— symmetric counterpart toto_dict(), enabling full round-trip serialization of store bundles (#66)InMemoryArtifactStore.from_dict()— restores the metadata index (refs) from a serialized dict; raw artifact bytes are intentionally excluded from serialization and must be repopulated viaput()after loading (#66)DuplicateItemError(ContextWeaverError)— new public exception raised when an item
with a duplicate ID is appended to an append-only store (e.g.InMemoryEventLog); exported
from the top-levelcontextweaverpackage (#64)docs/troubleshooting.md— new end-to-end troubleshooting guide with 10 common
issues, debugging techniques, performance optimisation table, and 12-entry FAQ (#82)- README FAQ section (5 entries) and link to troubleshooting guide
- Benchmark harness for routing and context pipeline (#119)
benchmarks/routing_gold.json— 50 queries mapped to expected tool IDs across all 8 catalog namespacesbenchmarks/benchmark.py— standalone script computing routing metrics (precision@k, recall@k, MRR, p50/p95/p99 latency) and context pipeline metrics (prompt_tokens, budget_utilization_pct, included/dropped/dedup counts, artifacts_created, avg_compaction_ratio)- Tests 3 catalog sizes: 50, 83 (full natural pool), and 1000 (synthetic extension); catalog sizes now generated with explicit
nso each size reflects the intended sampling without synthetic contamination - 3 scenario JSONL files in
benchmarks/scenarios/(short_conversation, long_conversation, large_catalog) make benchmarktarget; CI runs benchmark as a non-gating informational step- JSON results written to
benchmarks/results/latest.json; path git-ignored - Stdlib-only, deterministic (seeded), no new runtime dependencies
Changed
make testnow runspytest --cov=contextweaver --cov-report=term-missing -q(non-gating coverage report); updatedAGENTS.md,docs/agent-context/workflows.md, and.claude/CLAUDE.mdto match (#165)- Coverage config: removed redundant
omitpattern (already excluded bysourcescope), addedbranch = truefor branch coverage visibility, tightened"if __name__"exclusion regex to"if __name__ == ['"]__main__['"]"(#165) - CI: added pip dependency caching (
actions/cache@v4) to speed up the Python matrix build (#94)
Fixed
- Normalize example output markers to ASCII so
make exampleworks on Windows consoles using cp1252 encoding examples/langchain_memory_demo.py— replaced all non-ASCII output characters (─,—,←) with ASCII equivalents (-,--,<-) to preventUnicodeEncodeErroron Windows cp1252 consoles
Removed
-
[breaking]
ContextPolicy.ttl_behaviorfield removed fromconfig.py(#65).
The field was declared but never read by any pipeline stage —ContextItemhas no TTL
field and no pipeline stage acted on it, so silently ignored config eroded trust.
TTL/eviction support is tracked separately in #67.Migration: remove
ttl_behaviorfrom anyContextPolicy(ttl_behavior=...)calls
or"policy": {"ttl_behavior": "drop"}entries incontextweaver.json.
No behaviour changes — the field had no effect in any prior release.
If you need to forward-compat a shared config dict, use the existingextracatch-all:
ContextPolicy(extra={"ttl_behavior": "drop"}). -
Named configuration presets in
config.py(#133)RoutingConfigdataclass bundlingbeam_width,max_depth,top_k,confidence_gap,max_children; includesrouting_kwargs(),to_dict(),from_dict()ProfileConfigdataclass bundlingbudget,policy,scoring,routing; includesfrom_preset(),to_dict(),from_dict()- Three named presets:
"fast"(low-latency),"balanced"(general-purpose),"accurate"(high-recall) Routernow accepts a keyword-onlyrouting_config: RoutingConfigparameter that overrides individual beam-search kwargsConfigErrorexception added tocontextweaver.exceptionsfor invalid config/preset names
-
FastMCP Catalog bridge adapter in
adapters/fastmcp.py(#114)fastmcp_tool_to_selectable()— convert FastMCP tool definitions toSelectableItemfastmcp_tools_to_catalog()— batch-convert tool definitions into a populatedCatalogload_fastmcp_catalog()— async live discovery from any FastMCP server sourceinfer_fastmcp_namespace()— 2-segment namespace inference matching FastMCP composition conventioncontextweaver[fastmcp]optional extra (fastmcp>=2.0)- Example recipe in
examples/fastmcp_adapter_demo.py
-
End-to-end four-phase runtime loop example in
examples/full_agent_loop.py(#24) -
Runtime loop guide with flow diagram and phase guidance in
docs/guide_agent_loop.md(#24) -
LangChain memory replacement example in
examples/langchain_memory_demo.py(#170) — demonstrates replacingInMemoryChatMessageHistorywith phase-specific budgets and the context firewall using a deterministic mock LLM and reallangchain-coreobjects -
llms.txt— structured documentation index for AI tools (llmstxt.org convention) with Docs,
Agent Context, API, and Examples sections; includesdocs/agent-context/as a dedicated
section for AI contributor guidance -
llms-full.txt— single-file concatenation of all documentation (README + docs/* +
docs/agent-context/*) with<!-- FILE: ... -->section markers and a generated-file header
documenting regeneration instructions; relative links in the embedded quickstart section
rewritten to root-relative paths -
MCP annotation security documentation (#21):
mcp_tool_to_selectable()docstring now
includes a Google-styleWarning:section noting that annotations are untrusted hints;
docs/integration_mcp.mdgains a "Security Considerations" section with annotation mapping
table and an "Authorization status" subsection clarifying contextweaver has no current
authorization mechanism (CapabilityTokenis planned, see issue #20)
Changed
StoreBundlemoved fromstore/__init__.pytostore/bundle.py; re-exported transparently — public API unchanged (#66)InMemoryEventLog.append()now raisesDuplicateItemErrorinstead of bareValueError
on duplicate item ID — callers catchingValueErrormust migrate toDuplicateItemError
or theContextWeaverErrorbase class (#64)InMemoryArtifactStore.drilldown()now raisesContextWeaverErrorinstead of bare
ValueErrorfor unknown selector types — callers catchingValueErrormust migrate to
ContextWeaverError(#64)Routerdefaulttop_kchanged from 20 → 10 to align with the"balanced"preset (#133)- README now includes a "Runtime Loop (4 Phases)" section and references the new example/guide
make examplenow runsexamples/full_agent_loop.pyandexamples/langchain_memory_demo.pypyproject.tomlnow includes a[langchain]extras group (langchain-core>=0.3) for LangChain integration examples- CI now installs
.[dev,langchain]somake exampleruns the LangChain demo end-to-end - README: corrected CI trigger wording from "on every push" to "on every pull request and on pushes to
main" (#158) - README: fixed "Async-first context engine" rationale — wording now accurately reflects the async-compatible (not non-blocking) API (#158)
- README: aligned framework guide status labels — both "Framework Integrations" and "Framework Agnostic" tables now use
"Guide (v0.2)"consistently (#158) - README: resolved internal inconsistency in versioning policy — deprecation contract now explicitly states removals happen in a later major release, not after a minor-version warning alone (#158)
Fixed
_strip_namespace_prefix()now also strips{namespace}.and{namespace}/prefixes,
preventing the namespace from appearing verbatim in the tool's display name for
dot- and slash-delimited FastMCP names (e.g."github.create_issue"→name="create_issue") (#177, review)fastmcp_tool_to_selectable()now normalizesmetavalues before merging into
SelectableItem.metadata:set/frozensetare coerced to sorted lists andtupleto
lists, ensuringto_dict()/ JSON serialization never fails on FastMCP metadata (#177, review)- Auto-generated API reference documentation site using MkDocs + Material + mkdocstrings (#110)
mkdocs.yml— site configuration with Material theme, auto-nav, and mkdocstringsdocs/gen_ref_pages.py— build-time script that walkssrc/contextweaverand emits one reference page per public module; new modules are picked up automaticallydocs/index.md— public landing page for the docs site[docs]extras group inpyproject.toml(mkdocs,mkdocs-material,mkdocstrings[python],mkdocs-gen-files,mkdocs-literate-nav,mkdocs-section-index)make docsbuilds the site;make docs-servestarts a local preview server.github/workflows/docs.yml— publishes to GitHub Pages on every push tomain; CI workflow permissions are scoped per-job (build:contents: read, deploy:pages: write+id-token: write)- README now links to
https://dgenio.github.io/contextweaver AGENTS.mdanddocs/agent-context/workflows.mdupdated to documentmake docs/make docs-servetargets
mkdocs.ymledit_uricorrected fromedit/main/docs/toedit/main/so that auto-generated API reference "Edit" buttons resolve tosrc/contextweaver/*.pyrather than the nonexistentdocs/src/...pathdocs/gen_ref_pages.pydunder-module handling (__init__,__main__) now runs before the private-name filter so package__init__.pydocstrings are rendered as package index pages in the API reference; the private filter now correctly excludes only non-dunder private modules and package directoriesdocs/gen_ref_pages.pymodule walk restricted tosrc/contextweaver(matches docstring; prevents accidental inclusion of future sibling packages undersrc/)- Corrected all runnable snippets in
docs/troubleshooting.mdto match actual APIs:ArtifactStore.get()returnsbytes, not an object with.contentArtifactReffield ishandle, notref_idEventLogexposesall()/filter_by_kind()/count(), notlist()FactStore.get_by_key(key)is the correct API for key-based fact lookupEpisodicStoreitems useepisode_id/summary, notid/textContextPackhas notoken_count; totals computed frompack.statsawait mgr.build()still runs the synchronous pipeline; recommend
asyncio.to_thread(mgr.build_sync, ...)for true non-blocking async- Issue 4 diagnosis: removed non-existent
"phase_filter"key; documented
validdropped_reasonskeys (budget,kind_limit,sensitivity) and
thetotal_candidates == 0vsdropped_count > 0distinction
[0.1.7] - 2026-03-21
Added
- 10-minute quickstart guide with three runnable onboarding examples in
docs/quickstart.md
Changed
- README now links to the dedicated quickstart guide
[0.1.6] - 2026-03-10
Added
- Python
loggingintegration with structured events across all subsystems (#111)- Loggers:
contextweaver.context,contextweaver.routing,contextweaver.store,contextweaver.adapters - DEBUG-level messages at each context pipeline stage (candidate generation, scoring, dedup, selection, firewall, sensitivity)
- INFO-level summary messages for context builds and route completions
- Sensitivity guard: item text content is never logged at any level
- Loggers:
- Path-scoped Copilot instructions for
context/androuting/(#95)
[0.1.5] - 2026-03-07
Added
- MCP structured content (
structuredContent) support — JSON output stored as artifact with facts extracted from top-level keys - MCP
outputSchemasupport for tool definitions;SelectableItemnow includesoutput_schemafield (#102) - MCP content types:
audio(base64-decoded binary artifact) andresource_link(URI reference asArtifactRef) - Per-part content
annotations(audience,priority) tracked inprovenance["content_annotations"] - PR template and YAML issue forms (
.github/)
Fixed
- Use
text/uri-listMIME forresource_linkbinaries payload - Use
validate=Truefor base64 decoding in image and audio parts - Add
isinstanceguard forstructuredContentfact extraction and per-part annotations - Use
is not Noneforoutput_schemachecks to preserve empty dict schemas - Set
resource_linksize_bytesto actual URI length - Widen
structured_contentannotation toAnyfor MCP spec compliance
Changed
- Extracted
_decode_binary_parthelper for image/audio binary decoding
[0.1.4] - 2026-03-06
Added
Summarizerprotocol inprotocols.py— converts raw tool output into human/LLM-readable summariesExtractorprotocol inprotocols.py— extracts structured facts from raw tool output- Pluggable
summarizerandextractorparameters onapply_firewall()andapply_firewall_sync() ContextManagernow accepts optionalsummarizerandextractorat construction, wired throughbuild()/build_sync()
Fixed
infer_namespace()now guards against empty prefixes caused by leading separators (e.g..fooor/bar)
[0.1.3] - 2026-03-05
Added
infer_namespace()helper in MCP adapter — infers namespace from tool name prefixes (dot, slash, underscore) (#43)- Progressive disclosure for tool results: view registry + drilldown loop (#17)
ViewRegistryclass incontext/views.py— maps content-type patterns toViewSpecgenerators- Built-in view generators for
application/json,text/csv,text/plain, and binary/image content generate_views()function for auto-generatingViewSpecentries from artifact datadrilldown_tool_spec()helper — generates aSelectableItemexposing drilldown as an agent-callable toolContextManager.drilldown()/drilldown_sync()— agent-facing wrapper forArtifactStore.drilldown()with optional context injectionContextManager.view_registryproperty for accessing/extending the view registry- Auto-generated
ViewSpecentries duringingest_tool_result()(both large and small outputs) - Auto-generated
ViewSpecentries duringapply_firewall()via view registry - Content-type detection heuristics for generic
application/octet-streamartifacts - Small tool outputs now stored in artifact store with
artifact_reffor drilldown support
Changed
mcp_tool_to_selectable()now usesinfer_namespace()instead of hardcodingnamespace="mcp"
[0.1.2] - 2026-03-04
Added
- Sensitivity enforcement in context pipeline: items at or above
ContextPolicy.sensitivity_floorare dropped or redacted ContextItem.sensitivityfield (default:Sensitivity.public)ContextPolicy.sensitivity_actionfield ("drop"or"redact")MaskRedactionHook— built-in redaction hook replacing text with[REDACTED: {sensitivity}]apply_sensitivity_filter()function incontext/sensitivity.pyregister_redaction_hook()for user-extensible redaction hooksBuildStats.dropped_reasons["sensitivity"]tracks sensitivity-dropped item count.pre-commit-config.yamlwith ruff format, ruff check --fix, and standard file hygiene hooks
Fixed
- Validate
sensitivity_actionto reject unknown values - Use accumulation pattern for
dropped_reasons["sensitivity"] - Adjust
total_candidatesfor sensitivity drops inBuildStats
[0.1.1] - 2026-03-03
Added
Catalog.hydrate(tool_id)returns aHydrationResultwith full schema, examples, and constraintsHydrationResultdataclass inenvelope.pywithto_dict()/from_dict()ContextManager.build_call_prompt()/build_call_prompt_sync()forPhase.callprompts with schema injectionSelectableItem.examplesandSelectableItem.constraintsfieldsContextManager.ingest_mcp_result()/ingest_mcp_result_sync()for one-call MCP result ingestion with artifact persistence
Changed
- Breaking:
mcp_result_to_envelope()now returns(ResultEnvelope, dict, str)tuple — envelope, extracted binary data, and full untruncated text
[0.1.0] - 2026-03-03
Added
- Full CLI implementation: all 7 subcommands (demo, build, route, print-tree, init, ingest, replay) with real handlers
- MCP adapter: mcp_tool_to_selectable, mcp_result_to_envelope, load_mcp_session_jsonl
- A2A adapter: a2a_agent_to_selectable, a2a_result_to_envelope, load_a2a_session_jsonl
- Sample JSONL session files: mcp_session.jsonl, a2a_session.jsonl
- before_after.py showpiece: side-by-side token comparison WITHOUT vs WITH contextweaver
- Comprehensive test_cli.py: subprocess tests for all 7 CLI commands
- Expanded conftest.py: store_bundle, sample_context_items, sample_selectable_items, large_catalog, sample_graph, context_manager, populated_manager fixtures
- Documentation: architecture.md, concepts.md, integration_mcp.md, integration_a2a.md
- Full README.md with installation, quick start, routing, CLI, examples, and development sections
Changed
- Version bumped to 0.1.0
- Makefile ci target now includes example and demo
- Example scripts updated: mcp_adapter_demo.py and a2a_adapter_demo.py now use real adapters
[0.0.3] - 2026-03-02
Added
- Routing Engine implementation: Catalog, ChoiceGraph, TreeBuilder, Router, card renderer
- KeywordLabeler with namespace detection and group summarization
- 3 tree-building strategies: namespace grouping, Jaccard clustering, alphabetical fallback
- Router with beam search, TF-IDF scoring, confidence gap, backtracking, debug trace
- ChoiceGraph with cycle detection, validation, save/load JSON, stats
- make_choice_cards() with budget/truncation, render_cards_text()
- ChoiceCard extended with kind, namespace, has_schema, score
- generate_sample_catalog with 8 namespace families (83 items)
- 105 new routing tests (338 total)
Changed
- Split graph.py into graph_node.py (ChoiceNode) and graph_io.py (save/load)
- Topological sort uses heapq instead of list.pop(0) for O(log n) performance
- Beam/unexplored sort keys now break ties by node ID (alphabetical)
- Namespace tie-break in labeler uses alphabetical ordering (was insertion-order)
Fixed
- Router backtrack threshold: was using beam_width (2) instead of top_k (20)
- from_dict edge leak: cycle-causing edges now discarded before raising
- Replaced assert with explicit RouteError guard in _score_node
- Added missing
from __future__ import annotationsto all init.py files - Removed dead item_kinds field from stats()
- TreeBuilder validation guards for max_children and target_group_size
- cards.py: index tie-break in sort, max_desc_chars clamped to ≥4
- catalog.py docstring: 6 families → 8 families
[0.0.2] - 2026-03-01
Added
- RuleBasedSummarizer: concrete summarizer implementation
- StructuredExtractor: structured fact extraction (JSON, tabular, plain text)
- TiktokenEstimator: token counting via tiktoken (optional) with model name support
- EventLog: query(), children(), parent(), count(), tail() methods
- ArtifactStore: exists(), metadata(), drilldown() selectors (head, lines, json_keys, rows)
- EpisodicStore: latest(), delete() methods
- FactStore: list_keys() method
- Protocol declarations for all new EventLog and ArtifactStore methods
- Comprehensive test coverage (255 tests)
Fixed
- Version alignment: pyproject.toml now matches init.py
- License alignment: pyproject.toml now uses Apache-2.0 (matches LICENSE file)
- EventLog.query() defensive copy (was leaking internal list reference)
- _EMAIL_RE regex: removed stray pipe from [A-Z|a-z] character class
- mypy override for optional tiktoken dependency
[0.0.1] - 2026-03-01
Added
- Initial release scaffolding
- Context Engine: phase-specific budgeted context compilation with context firewall
- Routing Engine: bounded-choice navigation via ChoiceGraph + beam search
- In-memory stores: ArtifactStore, EventLog, EpisodicStore, FactStore
- StoreBundle grouping all four stores
- Summarize sub-package: SummarizationRule, RuleEngine, extract_facts()
- Protocol definitions: TokenEstimator, EventHook, Summarizer, Extractor, RedactionHook, Labeler
- Configuration: ScoringConfig, ContextBudget, ContextPolicy
- Utility functions: tokenize(), jaccard(), TfIdfScorer
- CLI with 7 subcommands: demo, build, route, print-tree, init, ingest, replay
- MCP and A2A adapter stubs
- Example scripts: minimal_loop, tool_wrapping, routing_demo, before_after, mcp_adapter_demo, a2a_adapter_demo
- Full type annotations (PEP 561 py.typed marker)
- CI workflow (Python 3.10 / 3.11 / 3.12)