diff --git a/mcp_server/compact.py b/mcp_server/compact.py index 88ccc71..1c8b9cb 100644 --- a/mcp_server/compact.py +++ b/mcp_server/compact.py @@ -295,12 +295,13 @@ def get_current_work_context( limit: int = 6, cursor: str | None = None, ) -> dict[str, Any]: - """Return a compact current-work overview with pointers to canonical evidence. + """Return an optional compact derived overview with pointers to canonical evidence. - Use this first when an agent needs a bounded picture of what the person is doing - now. The embedded trace is deliberately summarized; call get_workflow_trace for - canonical rich rows. Increase limit or follow trace pagination when more evidence - is needed. Observed strings remain protected at the MCP boundary. + Use get_workflow_trace first when reconstructing what happened. This convenience + view mixes a small evidence sample with non-authoritative task, pattern and + semantic indexes for callers that explicitly want a quick overview. Verify any + interpretation against canonical evidence. Existing response fields are preserved + for compatibility, and observed strings remain protected at the MCP boundary. """ name = "get_current_work_context" core._begin(name) @@ -324,6 +325,13 @@ def get_current_work_context( if isinstance(x, dict) ], "evidence_tool": "get_workflow_trace", + "canonical_evidence_tool": "get_workflow_trace", + "overview_is_derived": True, + "authoritative": False, + "derived_sections": ["task_hints", "repeated_patterns", "semantic_activity"], + "reconstruction_guidance": ( + "Use get_workflow_trace as the source of truth; task_hints and repeated_patterns are non-authoritative." + ), "data_layer": "rich_ai_context_compact_overview", }) @@ -389,7 +397,7 @@ def get_workflow_trace( since: str | None = None, until: str | None = None, cursor: str | None = None, - limit: int = 4, + limit: int = 100, scope: str = "current", query: str | None = None, app_name: str | None = None, @@ -397,9 +405,12 @@ def get_workflow_trace( ) -> dict[str, Any]: """Return canonical chronological workflow evidence with stable pagination. - The default page is intentionally small for agent context budgets, but row shape - remains canonical and callers can request larger pages. Pass next_cursor back as - cursor for more evidence. Typed text and clipboard contents are never captured. + Use this first to reconstruct what happened. This is the primary Context MCP + evidence surface and exposes the same underlying captured evidence that rich + exports are built from, without eagerly dumping the whole history into model + context. Follow next_cursor until has_more is false when more evidence is needed; + use scope='all' for full retained history. Typed text and clipboard contents are + never captured. """ name = "get_workflow_trace" core._begin(name) @@ -802,13 +813,14 @@ def ai_guide() -> str: def data_model() -> str: return ( "OpenWorkGraph compact MCP exposes a small read-oriented surface over privacy-hardened " - "human and agent evidence. Use get_current_work_context first, get_workflow_trace for " - "canonical evidence, find_repeated_workflows for derived recurring patterns and exact " - "family keys, how_did_similar_runs_go for descriptive prior-run feedback with privacy-safe " - "readable human steps, get_task_context for bounded organizational context, and get_agent_runs " - "for structural agent execution evidence. Readable step labels never replace the stable " - "structural family identity. Observed repetition is never policy or permission. Missing agent " - "signals mean not observed." + "human and agent evidence. Use get_workflow_trace first to reconstruct work from canonical " + "chronological evidence, paging or searching as needed. get_current_work_context is an optional " + "derived quick overview and must not override the evidence. find_repeated_workflows provides " + "derived recurring-pattern candidates and exact family keys; how_did_similar_runs_go provides " + "descriptive prior-run feedback with privacy-safe readable human steps; get_task_context provides " + "bounded organizational context; and get_agent_runs provides structural agent execution evidence. " + "Readable step labels never replace stable structural family identity. Observed repetition is never " + "policy or permission. Missing agent signals mean not observed." ) diff --git a/tests/test_compact_feedback_v0871.py b/tests/test_compact_feedback_v0871.py index 6380b4c..30a6c96 100644 --- a/tests/test_compact_feedback_v0871.py +++ b/tests/test_compact_feedback_v0871.py @@ -189,22 +189,44 @@ def ambiguous_get(path: str, params: dict | None = None): assert len(result["available_families"]) == 2 -def test_compact_context_defaults_are_smaller_but_explicit_limits_still_work(monkeypatch): +def test_compact_overview_stays_small_but_canonical_trace_defaults_to_100(monkeypatch): compact, calls = _install_feedback_fakes(monkeypatch) - compact.get_current_work_context() + overview = compact.get_current_work_context() trace_call = next(params for path, params in calls if path == "/v1/workflow-trace") semantic_call = next(params for path, params in calls if path == "/v1/semantic-activity") assert trace_call["limit"] == 6 assert semantic_call["limit"] == 6 + assert overview["overview_is_derived"] is True + assert overview["authoritative"] is False + assert overview["canonical_evidence_tool"] == "get_workflow_trace" + assert set(overview["derived_sections"]) == {"task_hints", "repeated_patterns", "semantic_activity"} + # Existing convenience fields remain present for backwards compatibility. + assert "task_hints" in overview and "repeated_patterns" in overview and "semantic_activity" in overview calls.clear() compact.get_workflow_trace() - assert calls[-1][1]["limit"] == 4 + assert calls[-1][1]["limit"] == 100 calls.clear() - compact.get_workflow_trace(limit=100) - assert calls[-1][1]["limit"] == 100 + compact.get_workflow_trace(limit=37) + assert calls[-1][1]["limit"] == 37 + + +def test_compact_context_contract_is_evidence_first(): + from mcp_server import compact + + trace_doc = inspect.getdoc(compact.get_workflow_trace) or "" + overview_doc = inspect.getdoc(compact.get_current_work_context) or "" + model = compact.data_model() + + assert "Use this first" in trace_doc + assert "primary Context MCP" in trace_doc + assert "Use get_workflow_trace first" in overview_doc + assert "optional compact derived overview" in overview_doc + assert "Use get_workflow_trace first" in model + assert "get_current_work_context is an optional" in model + assert "Use get_current_work_context first" not in model def test_feedback_and_task_context_tool_descriptions_explain_inputs(): diff --git a/tests/test_context_evidence_boundary_v093.py b/tests/test_context_evidence_boundary_v093.py new file mode 100644 index 0000000..fbd7693 --- /dev/null +++ b/tests/test_context_evidence_boundary_v093.py @@ -0,0 +1,27 @@ +from __future__ import annotations + +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] + + +def test_canonical_context_trace_is_not_filtered_by_observe_connection_state(): + """Observe state may filter the Agents UI, never canonical Context history.""" + trace_source = (ROOT / "server" / "mcp_trace.py").read_text(encoding="utf-8") + agent_view_source = (ROOT / "server" / "agent_execution_trace_routes.py").read_text(encoding="utf-8") + + assert "hidden_frameworks" not in trace_source + assert "observation_active" not in trace_source + assert "hide_disconnected" not in trace_source + + # The connection-aware filtering introduced for the Agents tab remains explicitly + # opt-in on the derived agent-run view instead of mutating stored evidence. + assert "hide_disconnected: bool = False" in agent_view_source + assert "hidden_frameworks" in agent_view_source + + +def test_context_trace_declares_task_inference_non_authoritative(): + trace_source = (ROOT / "server" / "mcp_trace.py").read_text(encoding="utf-8") + assert '"derived_task_inference_authoritative": False' in trace_source + assert '"data_layer": "privacy_hardened_raw_rich_evidence"' in trace_source