Skip to content

Agentic provenance: framework plugins, AI-coding-harness capture, and provenance analysis - #351

Open
GueroudjiAmal wants to merge 31 commits into
ORNL:devfrom
GueroudjiAmal:main
Open

Agentic provenance: framework plugins, AI-coding-harness capture, and provenance analysis#351
GueroudjiAmal wants to merge 31 commits into
ORNL:devfrom
GueroudjiAmal:main

Conversation

@GueroudjiAmal

Copy link
Copy Markdown
Collaborator

This PR extends Flowcept's provenance capture from scientific workflows to agentic systems: agent frameworks (Academy, LangGraph, CrewAI, AutoGen, LangChain, OpenAI Agents SDK, Claude Agent SDK), AI coding harnesses (Claude Code, Codex CLI, Gemini CLI, Cursor, OpenCode, or anything emitting OpenTelemetry GenAI spans), and a shared provenance-analysis layer that all of these — plus the existing MCP agent, the webservice chat, and the CLI — query through one set of functions.

The framing follows PROV-AGENT: an agentic session is a workflow — a prompt causes a turn, a turn causes tool calls, a tool call edits a file — which is the structure Flowcept already stores and queries. It also adds a Diaspora MQ adapter, which came along on the same branch (see Should this be split? below).

What's included

1. Agent-framework plugins (src/flowcept/agents/{academy,langgraph,crewai,autogen,langchain,openai_agents,claude_agent_sdk}/)

Each plugin captures its framework's execution as Flowcept tasks/workflows. They are declarative — no code changes in user workloads:

plugins:
  langgraph:
    enabled: true
    kind: langgraph
    workflow_name: "langgraph-workflow"
    performance_tracking: true

Flowcept.start() builds, starts, and stops enabled plugins alongside Flowcept, propagating campaign_id so all plugins in a run share a campaign. Running instances are exposed via the new Flowcept.plugins property. Each plugin's interceptor reuses the InstrumentationInterceptor singleton (same MQ connection and buffer) — the pattern the Dask client interceptor already uses. A plugin that fails to start is logged and skipped rather than taking Flowcept down.

2. AI coding harness capture (src/flowcept/agents/harness/ + per-harness plugin modules)

Shared capture core (recorder, tracer, emit, events, prov, state, sanitize, ids, vocab, config, cli, mcp_server) with these sources:

Source Mechanism
Claude Code Claude Code plugin (hooks) or hooks in settings.json
Codex CLI, Gemini CLI, Cursor, OpenCode generic hook adapter + a JSON profile (cli_harness/profiles/*.json)
OpenTelemetry GenAI spans span exporter, or ingest exported spans
Claude Agent SDK / OpenAI Agents SDK / LangChain / LangGraph wrapper, tracing processor, callback handler
Custom agents SessionTracer, or the MCP server's record_event tool

The capture path is deliberately stdlib-only — it imports nothing outside flowcept.agents.harness and the standard library. A harness hook is a fresh process on the interactive critical path, so importing Flowcept's heavy dependencies would cost far more than the capture itself; those are loaded only to read what was captured. Adding a new CLI harness means adding a JSON profile, not writing code.

New entry points: flowcept-harness (sessions/show/report/analyze/install/hook/compare), flowcept-harness-mcp, flowcept-claude-code.

3. Provenance analysis (src/flowcept/agents/prov_analysis/)

Framework-free pure functions over records: list[dict], following the existing data_query_tools/ layering rule (cores are framework-free; MCP/LangChain wrappers are thin): summarize_execution, analyze_errors, analyze_agent_behavior, find_slowest_tasks, cross_framework_links, compare_executions. They handle both the harness-buffer record shape and the framework-plugin shape, degrading gracefully on absent fields.

Four surfaces share this one implementation: the harness MCP server, the Flowcept agent MCP server (new mcp/mcp_tools/analysis_mcp_tools.py, registered in mcp/mcp_server.py), the webservice chat (chat_orchestration/tool_registry.py), and flowcept-harness analyze.

cross_framework_links builds edges from source_agent_id pointers, so a task recorded by one plugin can be linked to the agent that caused it in another.

4. Diaspora MQ adapter

MQDaoDiaspora (commons/daos/mq_dao/mq_dao_diaspora.py), wired into the MQDao.build() dispatch as mq.type: diaspora, plus resources/diaspora/ setup scripts and deployment/compose-diaspora.yml.

5. Smaller core changes

  • configs.py: new PLUGINS settings section (defaults to {}; absent config is a no-op).
  • autoflush_buffer.py: the timer and flush threads are now daemon=True, so a process can exit even if stop() was never reached.
  • .gitignore: core.*core.[0-9]* so files like core.py are not ignored.

6. Docs, examples, tests, CI

  • Sphinx: docs/agent_plugins.rst (+318), docs/harness_plugins.rst (+322), docs/agent.rst and docs/index.rst updates; regenerated OpenAPI spec; +275 lines of README.
  • 13 runnable examples under examples/agents/, one per plugin plus a combined multi-framework one.
  • 36 new test files: tests/harness/ (12), tests/agents/plugins/ and tests/agents/prov_analysis/ (10), tests/adapters/test_diaspora.py. Coverage includes each capture source, cross-capture and cross-plugin linking, CLI, MCP server, plugin assets, and Flowcept interop.
  • CI: pin ruff==0.15.22 in checks.yml and run-tests.yml (unpinned ruff was changing formatting expectations between runs), and a new check that the committed OpenAPI spec matches what the code generates.
  • pyproject.toml: new harness_otel, harness_claude_sdk, and diaspora extras.

Backwards compatibility

No existing public API changed. Everything new is opt-in: plugins are off unless plugins: is set, harness capture only runs when hooks/exporters are installed, and the new extras are not part of the default install. The only behavioral change to existing code paths is the daemon=True on the autoflush threads.

Testing

make reformat && make checks      # ruff + Sphinx build
pytest tests/harness tests/agents # new suites

The plugins were exercised end-to-end against the real frameworks and a live Anthropic API key (commit 9d6d654a), and the CLI-harness profiles were validated by replaying recorded Gemini-profile sessions (commit 2d827328). Tests that need network or vendor SDKs skip when the dependency or key is absent.

Notes for reviewers

  • Two accidental reverts from the merge base, please confirm before merge. Relative to main, this branch reintroduces psutil_p.connections() in telemetry_capture.py (upstream 06da889c had changed it to net_connections() for Fix deprecated warning in telemetry_capture #155) and drops the # group_id: auto Kafka comment from sample_settings.yaml (upstream fc138b5f). Both were carried in by the older Diaspora branch; happy to fix in this PR.
  • The diaspora extra uses a direct git reference (diaspora-stream-api @ git+https://…) and required [tool.hatch.metadata] allow-direct-references = true. PyPI rejects direct-URL dependencies in uploaded metadata, so this may break the release workflow that publishes on pushes to main. Options: drop the extra from this PR, or gate it behind a non-published extra. Guidance welcome.
  • Should this be split? The Diaspora MQ adapter is independent of the agentic work; it can be pulled into its own PR if that makes review easier.
  • The harness capture path's stdlib-only constraint is load-bearing for interactive latency — worth keeping in mind if a review suggests reusing Flowcept helpers there.

GueroudjiAmal and others added 30 commits March 17, 2026 15:26
# Conflicts:
#	README.md
#	resources/sample_settings.yaml
#	src/flowcept/agents/__init__.py
#	src/flowcept/commons/daos/mq_dao/mq_dao_base.py
#	src/flowcept/flowcept_api/flowcept_controller.py
#	src/flowcept/version.py
Upstream now resolves settings to plain containers at load time, so the
OmegaConf.to_container call on the plugins block failed with a plain dict.
Also run ruff format over src so 'make checks' formatting passes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Capture what an AI coding harness actually did - prompts, turns, tool
calls, subagents - as PROV-AGENT provenance, following the same
per-framework plugin layout as the academy/langgraph/crewai/autogen
plugins:

  src/flowcept/agents/harness/          shared capture core (stdlib-only)
  src/flowcept/agents/claude_code/      Claude Code hook adapter
  src/flowcept/agents/cli_harness/      profile-driven adapter (codex,
                                        gemini, cursor, opencode)
  src/flowcept/agents/otel/             OTel GenAI span exporter/ingest
  src/flowcept/agents/claude_agent_sdk/ trace_query wrapper
  src/flowcept/agents/openai_agents/    tracing processor
  src/flowcept/agents/langchain/        callback handler
  plugins/flowcept/                     the Claude Code plugin
  tests/harness/                        test suite (87 tests)
  examples/agents/harness/              SessionTracer example

flowcept.agents.__init__ becomes lazy (PEP 562): the MCP tool modules
pull optional heavy deps, and this package is now on the import path of
capture hooks that run on the interactive critical path.

New console scripts: flowcept-harness, flowcept-harness-mcp,
flowcept-claude-code. New extras: harness_otel, harness_claude_sdk.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Preserve the full harness documentation (configuration table, privacy
posture, record model, design constraints) as the harness package's own
README, and link it from the main README.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… fix issues found

Ran every plugin against the real thing (academy 0.5, langgraph, crewai,
autogen-agentchat, claude-agent-sdk, openai-agents via Anthropic's
OpenAI-compatible endpoint, codex hook profile, online Redis MQ -> LMDB
-> query, harness flush -> live backend). Fixes that fell out:

- examples: pick the LLM provider from the environment (ANTHROPIC_API_KEY
  -> anthropic_chat, else openai_chat), so they run with either key
- examples/academy: academy 0.5 removed academy.logging.init_logging;
  use logging.basicConfig
- examples/autogen: the result parser dropped ints followed by
  punctuation ('15.'), so the assertion read 5 instead of 15
- plugins: default Anthropic model claude-3-5-haiku-latest no longer
  exists; use claude-haiku-4-5-20251001
- harness flush: stop the MQ with check_safe_stops=False; the control
  messages it sent carried a None interceptor id that crashed the
  document inserter

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds the missing pydocstyle docstrings (D102/D103/D105/D205/D400/D401)
in the new agent plugin and harness modules, moves a misplaced import
(E402), and fixes an undefined ProcessPoolExecutor name in the academy
plugin's type annotation (F821) via a TYPE_CHECKING import. No logic
changes; ruff check --select D,E,F,W and ruff format are clean on all
touched files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Unit tests (68 new, tests/agents/plugins/) for the academy, autogen,
  crewai, and langgraph provenance plugins plus cross-framework linking
  (_source_agent_id), driving the real plugin code against an in-memory
  interceptor fake; each module importorskips its framework so CI
  without the frameworks skips cleanly. No network, MQ, or LLM keys.
- Runnable examples for the six plugins that only had README snippets:
  langchain, openai_agents, otel, cli_harness, claude_code, and
  claude_agent_sdk; all verified end-to-end (five run fully offline).
- New Sphinx pages docs/agent_plugins.rst and docs/harness_plugins.rst
  wired into the toctree; build is clean with no new warnings.
- src/flowcept/agents/README.md now indexes all agent subpackages and
  links the harness README, docs pages, and examples; top-level README
  examples list extended.
- Regenerated docs/openapi specs (stale: missing the root health route).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- tests/harness/test_generic.py: four gemini-CLI event-replay tests
  (session fields, nested toolCall mapping, prompt+response turn, tool
  error), matching the coverage codex/cursor/opencode already had, plus
  docstrings on the pre-existing tests.
- tests/harness/test_cross_capture.py (new, 6 tests): source_agent_id
  round-trip through the harness provenance builder and buffer; shared
  campaign_id joining harness and LangGraph plugin records (config and
  env paths); simultaneous harness SessionTracer + LangGraph capture in
  one process with disjoint ids; and a real harness tool task_id linked
  into a LangGraph run via _source_agent_id.

Note: harness-side emission of source_agent_id has no caller yet
(recorder never passes it); tested at the builder/emitter level only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… hooks

New src/flowcept/agents/prov_analysis/ package: seven dependency-light
analysis functions over PROV-AGENT records (summarize_execution,
analyze_errors, analyze_agent_behavior, find_slowest_tasks,
cross_framework_links, compare_executions, load_records), handling both
harness-buffer and framework-plugin record shapes and reusing
report.aggregations primitives.

Surfaces:
- Harness MCP server: analyze_session, analyze_errors, find_slowest,
  cross_links (stdio, JSONL-only, no DB required).
- Flowcept agent MCP: df_*/db_* analysis tools plus compare_executions,
  also exposed to the web chat via the LangChain tool registry.
- CLI: flowcept-harness analyze <session> [--errors|--slowest N|--links].
- Claude Code plugin v0.2.0: .mcp.json wiring the flowcept-provenance
  stdio server; new skills prov-analysis (how to analyze captured
  provenance) and write-flowcept-plugin (how to author a new provenance
  plugin); opt-in SessionEnd auto-report hook gated by
  FLOWCEPT_HARNESS_AUTOREPORT=1.

57 new offline tests (core, MCP registration, harness tools, CLI,
plugin assets) — suite now 235 passing. Offline example added and
executed; Sphinx docs (agent.rst, harness_plugins.rst) and READMEs
updated with zero new build warnings.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ning

- Harness records (turns, tool calls, LLM calls) can now link back to a
  framework-emitted task: source_agent_id flows from the hook payload
  key flowcept_source_agent_id (wins) or env
  FLOWCEPT_HARNESS_SOURCE_AGENT_ID, through HarnessEvent and the
  recorder; SessionTracer accepts source_agent_id= as sticky context.
  cross_framework_links picks the new edges up unchanged.
- OTel plugin: session identity no longer derives from gen_ai.system,
  which split one conversation into multiple workflows when the
  attribute was inconsistent; identity now comes from the conversation
  id only, and the provider name is recorded first-wins as a one-time
  lifecycle notice. Reproduced failing-first in tests.
- flowcept-harness analyze --compare A B: per-activity count, duration,
  and error-rate deltas between two session buffers.
- CI: pin ruff==0.15.22 (unpinned latest fails even upstream main;
  make checks passes clean under the pin) and fail if the committed
  OpenAPI spec drifts from regeneration.
- .gitignore: narrow core.* (meant for core dumps) to core.[0-9]* so it
  cannot swallow Python modules named core.py.

10 new tests; suite now 245 passing, all offline.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

3 participants