Skip to content

feat: progressive disclosure for tool results — view registry + drilldown loop#100

Merged
dgenio merged 9 commits intomainfrom
feat/progressive-disclosure-views
Mar 5, 2026
Merged

feat: progressive disclosure for tool results — view registry + drilldown loop#100
dgenio merged 9 commits intomainfrom
feat/progressive-disclosure-views

Conversation

@dgenio
Copy link
Owner

@dgenio dgenio commented Mar 4, 2026

What changed

Closes #17 — implements the progressive disclosure loop connecting ViewSpec, ArtifactStore.drilldown(), and ResultEnvelope.views into a usable agent-facing workflow.

New files

  • src/contextweaver/context/views.py — new module (287 lines):
    • ViewRegistry class mapping MIME content types to ViewSpec generators
    • Built-in generators: _json_views, _csv_views, _text_views, _binary_views
    • generate_views() convenience function (module-level default registry)
    • drilldown_tool_spec() — returns a SelectableItem for agent tool catalogs
    • _detect_content_type() — heuristic content-type detection for generic MIME types
  • tests/test_views.py — 32 tests covering all generators, registry, detection, and tool spec

Modified files

  • src/contextweaver/context/firewall.pyapply_firewall() now accepts optional view_registry param, auto-generates ViewSpec entries and populates envelope.views
  • src/contextweaver/context/manager.py — added:
    • ContextManager.drilldown() / drilldown_sync() — wraps artifact_store.drilldown() with optional context injection (inject=True appends result as ContextItem)
    • ContextManager.view_registry property
    • Small outputs in ingest_tool_result() now stored in artifact store with auto-generated views
  • src/contextweaver/context/__init__.py — exports ViewRegistry, generate_views, drilldown_tool_spec
  • src/contextweaver/__init__.py — same new exports
  • tests/test_manager.py — 12 new tests: drilldown basic/json_keys/inject/sync/errors, auto-views on ingest, chained drilldown
  • CHANGELOG.md — updated [Unreleased]

Why

The building blocks for progressive disclosure existed (ViewSpec, drilldown(), ResultEnvelope.views) but were not connected. Agents received ResultEnvelope with empty views and no structured way to request follow-up slices. This change wires everything together:

ingest_tool_result(data)
  → apply_firewall(data) → artifact_store.put() → ArtifactRef
  → view_registry.generate_views(ref, data) → list[ViewSpec]
  → attach views to ResultEnvelope
  → agent sees: summary + available views

agent calls: drilldown(handle, selector)
  → artifact_store.drilldown() → slice text
  → optionally inject into context as ContextItem

How verified

ruff format --check src/ tests/ examples/  →  79 files already formatted
ruff check src/ tests/ examples/           →  All checks passed!
mypy src/                                  →  Success: no issues found in 41 source files
pytest -q                                  →  479 passed in 19.76s
python examples/*.py                       →  all exit 0
python -m contextweaver demo               →  exit 0

Tradeoffs / risks

  • Small outputs are now stored in the artifact store (previously they weren't). This is intentional — enables drilldown on all tool results regardless of size.
  • Content-type detection for application/octet-stream uses heuristics (JSON parse attempt, CSV sniffer). Conservative: falls back to text/plain for decodable text, keeps original MIME for binary.
  • text/* prefix matching falls through to text/plain generator (e.g. text/markdown gets line-based views).

Acceptance criteria checklist

  • Auto-generated ViewSpec entries during ingest_tool_result() / firewall
  • View registry with built-in generators for JSON/CSV/text/binary
  • Extensible: users can register custom view generators
  • ContextManager.drilldown() method
  • drilldown_tool_spec() helper
  • ResultEnvelope.views populated automatically
  • Drilldown results injectable as new ContextItem entries
  • Full type hints and docstrings
  • Tests: 32 view tests + 12 manager drilldown/auto-view tests

…wn loop (#17)

- Add ViewRegistry class mapping content types to ViewSpec generators
- Built-in generators for application/json, text/csv, text/plain, binary
- Auto-generate ViewSpec entries in apply_firewall() and ingest_tool_result()
- Add ContextManager.drilldown() with optional context injection
- Add drilldown_tool_spec() helper for agent tool catalogs
- Small tool outputs now stored in artifact store for drilldown support
- Content-type detection heuristics for generic MIME types
- 32 new tests in test_views.py, 12 new tests in test_manager.py
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements the progressive disclosure loop for tool results, connecting ViewSpec, ViewRegistry, ArtifactStore.drilldown(), and ResultEnvelope.views into a usable agent-facing workflow. It closes issue #17 by wiring together the existing building blocks (which previously existed in isolation) into a cohesive agent-callable flow.

Changes:

  • New src/contextweaver/context/views.py module introducing ViewRegistry, built-in MIME-type generators (_json_views, _csv_views, _text_views, _binary_views), content-type detection, generate_views(), and drilldown_tool_spec()
  • ContextManager gains drilldown() / drilldown_sync() methods and a view_registry property; ingest_tool_result() now stores all outputs (not just large ones) in the artifact store with auto-generated views
  • apply_firewall() / apply_firewall_to_batch() extended with an optional view_registry parameter to populate ResultEnvelope.views on the firewall path

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/contextweaver/context/views.py New module: ViewRegistry, view generators, content-type detection, drilldown_tool_spec()
src/contextweaver/context/manager.py Adds drilldown(), view_registry property; stores small outputs in artifact store with views
src/contextweaver/context/firewall.py Extends apply_firewall[_to_batch]() with optional view_registry to populate envelope.views
src/contextweaver/context/__init__.py Exports ViewRegistry, generate_views, drilldown_tool_spec
src/contextweaver/__init__.py Same top-level exports
tests/test_views.py 32 tests for generators, registry, detection, serialization, and tool spec
tests/test_manager.py 12 new integration tests for drilldown and auto-view generation
CHANGELOG.md Changelog entries for all new public API additions

You can also share your feedback on Copilot code review. Take the survey.

dgenio added 6 commits March 4, 2026 21:05
The previous selector {type: head, chars: 0} returned an empty string
on drilldown, making the Metadata view misleading. Changed to
{type: head, chars: 128} with label 'Header' so agents get useful
file magic bytes. Updated tests to match.
Repeated drilldown(inject=True) with the same handle and selector type
previously crashed with ValueError due to duplicate IDs. Appending
event_log.count() makes IDs unique and deterministic. Added test
covering the repeated-inject scenario.
generate_views() now creates a fresh ViewRegistry() when none is
provided, eliminating shared mutable state that could cause test
pollution or non-deterministic behavior in multi-tenant scenarios.
@dgenio dgenio merged commit 6f902bb into main Mar 5, 2026
3 checks passed
@dgenio dgenio deleted the feat/progressive-disclosure-views branch March 5, 2026 06:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[context] Progressive disclosure for tool results — view registry + drilldown loop

2 participants