from __future__ import annotations import json import tempfile import unittest from datetime import datetime, timezone from pathlib import Path from agent_integration.client import OpenAgentEvent from arr_processing.agent_trace import ( JsonlAgentTraceStore, capture_failure_record, project_trace_event, sanitize_trace_summary, trace_run_id, ) class AgentTraceProjectionTests(unittest.TestCase): def test_trace_projection_keeps_summaries_but_drops_content_and_sensitive_values(self) -> None: raw_summary = ( "调用完成 submission_grant=grant-secret " "Authorization:Bearer token-secret " "https://example.test/private?q=1 /Users/private/work/result.json " "guest@example.test 8613800138000 Guest" ) event = OpenAgentEvent( event="trace", data={ "event": "step.updated", "run_id": "run-trace-001", "ts": "2026-07-29T13:00:00Z", "data": {"summary": raw_summary}, "org_id": "must-not-escape", }, ) projected = project_trace_event(event, attempt_no=1, sequence=2) self.assertIsNotNone(projected) assert projected is not None self.assertEqual(projected["code"], "AGENT_STEP_UPDATED") rendered = json.dumps(projected, ensure_ascii=False) for private in ( "grant-secret", "token-secret", "example.test", "/Users/private", "guest@example.test", "8613800138000", "FULL_NAME", "must-not-escape", ): self.assertNotIn(private, rendered) for marker in ("[REDACTED]", "[URL]", "[PATH]", "[EMAIL]", "[NUMBER]", "[XML]"): self.assertIn(marker, rendered) hidden = project_trace_event( OpenAgentEvent( event="trace", data={ "event": "message.final", "run_id": "run-trace-001", "text": "signed payload must not escape", }, ), attempt_no=1, sequence=3, ) self.assertIsNone(hidden) def test_run_id_is_accepted_only_from_run_started(self) -> None: started = OpenAgentEvent( event="trace", data={"event": "run.started", "run_id": "run-trace-001"}, ) completed = OpenAgentEvent( event="trace", data={"event": "run.completed", "run_id": "run-trace-001"}, ) self.assertEqual(trace_run_id(started), "run-trace-001") self.assertIsNone(trace_run_id(completed)) def test_capture_failure_exposes_only_exception_class(self) -> None: secret = "network secret payload" record = capture_failure_record( attempt_no=2, sequence=9, error=RuntimeError(secret), now=datetime(2026, 7, 29, 13, 0, tzinfo=timezone.utc), ) rendered = json.dumps(record, ensure_ascii=False) self.assertIn("RuntimeError", rendered) self.assertNotIn(secret, rendered) def test_empty_summary_is_not_logged(self) -> None: self.assertIsNone(sanitize_trace_summary(" \n\t ")) class JsonlAgentTraceStoreTests(unittest.TestCase): def test_round_trip_is_append_only_and_invalid_lines_are_reported(self) -> None: with tempfile.TemporaryDirectory() as temporary: store = JsonlAgentTraceStore(Path(temporary) / "trace") record = project_trace_event( OpenAgentEvent( event="trace", data={ "event": "run.started", "run_id": "run-trace-001", "ts": "2026-07-29T13:00:00Z", }, ), attempt_no=1, sequence=1, ) assert record is not None store.append("arrjob-trace-001", record) path = store._path("arrjob-trace-001") # pylint: disable=protected-access with path.open("a", encoding="utf-8") as target: target.write('{"raw":"must not be accepted"}\n') records = store.read("arrjob-trace-001") self.assertEqual(records[0]["code"], "AGENT_RUN_STARTED") self.assertEqual(records[-1]["code"], "AGENT_TRACE_RECORD_INVALID") self.assertEqual(records[-1]["details"]["invalid_records"], 1) self.assertNotIn("must not be accepted", json.dumps(records)) if __name__ == "__main__": unittest.main(verbosity=2)