From d6505ae2009ee8b5aa466c67eb0cd05f771b456f Mon Sep 17 00:00:00 2001 From: Michael Kuehl Date: Wed, 22 Apr 2026 09:34:02 +0200 Subject: [PATCH] fix(knowledge): always write agent trace JSONL for transcript reconstruction (fixes #507) --- agent_fox/engine/run.py | 10 +++++----- agent_fox/engine/session_lifecycle.py | 16 ++++++---------- agent_fox/knowledge/agent_trace.py | 12 +++++------- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/agent_fox/engine/run.py b/agent_fox/engine/run.py index 0fd0f26f..35de388c 100644 --- a/agent_fox/engine/run.py +++ b/agent_fox/engine/run.py @@ -98,11 +98,11 @@ def _setup_infrastructure( knowledge_db = open_knowledge_store(config.knowledge) sink_dispatcher.add(DuckDBSink(knowledge_db.connection, debug=debug)) - # Attach agent trace sink when debug is active (103-REQ-1.1, 103-REQ-7.2) - if debug: - from agent_fox.knowledge.agent_trace import AgentTraceSink + # Attach agent trace sink unconditionally so that trace-based transcript + # reconstruction is available for knowledge extraction (113-REQ-1.1). + from agent_fox.knowledge.agent_trace import AgentTraceSink - sink_dispatcher.add(AgentTraceSink(AUDIT_DIR, "")) + sink_dispatcher.add(AgentTraceSink(AUDIT_DIR, "")) # Ingest at startup try: @@ -152,7 +152,7 @@ def session_runner_factory( timeout_override=timeout_override, max_turns_override=max_turns_override, embedder=embedder, - trace_enabled=debug, + trace_enabled=True, ) # 108-REQ-5.1: Create platform instance (None if not configured) diff --git a/agent_fox/engine/session_lifecycle.py b/agent_fox/engine/session_lifecycle.py index 9331a79a..eef895ca 100644 --- a/agent_fox/engine/session_lifecycle.py +++ b/agent_fox/engine/session_lifecycle.py @@ -205,7 +205,7 @@ def __init__( timeout_override: int | None = None, max_turns_override: int | None = None, embedder: EmbeddingGenerator | None = None, - trace_enabled: bool = False, + trace_enabled: bool = True, ) -> None: self._node_id = node_id self._config = config @@ -598,15 +598,11 @@ async def _extract_knowledge_and_findings( 113-REQ-1.E2 """ # 113-REQ-1.1: Reconstruct full transcript from agent trace JSONL - # Only attempt trace reconstruction when debug tracing is active; - # the trace file is not written in normal (non-debug) runs. - transcript = "" - if self._trace_enabled: - from agent_fox.core.paths import AUDIT_DIR - from agent_fox.knowledge.agent_trace import reconstruct_transcript - - audit_dir = getattr(self, "_audit_dir", None) or AUDIT_DIR - transcript = reconstruct_transcript(audit_dir, self._run_id, node_id) + from agent_fox.core.paths import AUDIT_DIR + from agent_fox.knowledge.agent_trace import reconstruct_transcript + + audit_dir = getattr(self, "_audit_dir", None) or AUDIT_DIR + transcript = reconstruct_transcript(audit_dir, self._run_id, node_id) # 113-REQ-1.E1, 113-REQ-1.E2: Fall back to _build_fallback_input # when trace is unavailable or has no assistant messages diff --git a/agent_fox/knowledge/agent_trace.py b/agent_fox/knowledge/agent_trace.py index 6e00d0a6..0eae14ff 100644 --- a/agent_fox/knowledge/agent_trace.py +++ b/agent_fox/knowledge/agent_trace.py @@ -1,8 +1,9 @@ """Agent conversation trace sink: writes JSONL event log per orchestrator run. -Each debug run produces a file at audit_dir/agent_{run_id}.jsonl containing -one JSON object per line, recording the full agent–model conversation: -session.init, assistant.message, tool.use, tool.error, and session.result. +Each orchestrator run produces a file at audit_dir/agent_{run_id}.jsonl +containing one JSON object per line, recording the full agent–model +conversation: session.init, assistant.message, tool.use, tool.error, and +session.result. Requirements: 103-REQ-1.1 through 103-REQ-8.2 """ @@ -56,10 +57,7 @@ def reconstruct_transcript( event = json.loads(line) except json.JSONDecodeError: continue - if ( - event.get("event_type") == "assistant.message" - and event.get("node_id") == node_id - ): + if event.get("event_type") == "assistant.message" and event.get("node_id") == node_id: content = event.get("content", "") if content: messages.append(content)