mirror of
https://github.com/pewdiepie-archdaemon/odysseus.git
synced 2026-10-16 11:52:25 +02:00
feat(harness): authority, turn contract, tools, clean_agent_preview
Chat/agent harness work: request-authority model, turn contract, tool
policy/parsing/execution, clean_agent_preview streaming/thinking, ask_user
persistence, session probes, and scheduler.
Source: vendor/photocraft (ad1cff44) harness files, 3-way merged onto
upstream/dev (a8c147b2). Harness core files were independently developed
in both lineages; resolution policy documented in pr_work/C_conflicts.md
(vendor kept where ahead, upstream kept where vendor is behind; full delta
preserved in pr_work/C_upstream_delta.patch).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
a8c147b238
commit
364229f2dc
@@ -0,0 +1,108 @@
|
||||
"""Session-load 404 regression (passover item #9).
|
||||
|
||||
Loading a chat session used to fire two benign 404s in the browser console:
|
||||
GET /api/research/status/<id> (no research for this session)
|
||||
GET /api/chat/stream_status/<id> (no active stream for this session)
|
||||
|
||||
Both are now a normal "empty" state: HTTP 200 with an explicit status
|
||||
("none" / "idle"). The clients already branch on the body status, so this is
|
||||
backward compatible (an older server's 404 is still tolerated).
|
||||
"""
|
||||
import asyncio
|
||||
import inspect
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from routes import chat_routes
|
||||
from routes import research_routes
|
||||
|
||||
|
||||
def _route_endpoint(router, path, method="GET"):
|
||||
for route in router.routes:
|
||||
if getattr(route, "path", "") != path:
|
||||
continue
|
||||
if method in getattr(route, "methods", set()):
|
||||
return route.endpoint
|
||||
raise AssertionError(f"{method} {path} not registered")
|
||||
|
||||
|
||||
def _req(user="alice"):
|
||||
return SimpleNamespace(state=SimpleNamespace(current_user=user))
|
||||
|
||||
|
||||
# --- /api/research/status/<id> -------------------------------------------
|
||||
|
||||
def test_research_status_returns_200_none_when_absent(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(research_routes, "DEEP_RESEARCH_DIR", str(tmp_path / "deep_research"))
|
||||
handler = MagicMock()
|
||||
handler._active_tasks = {}
|
||||
router = research_routes.setup_research_routes(handler)
|
||||
endpoint = _route_endpoint(router, "/api/research/status/{session_id}")
|
||||
|
||||
out = asyncio.run(endpoint(session_id="sess-abc", request=_req()))
|
||||
|
||||
assert out == {"status": "none"}
|
||||
|
||||
|
||||
def test_research_status_returns_status_when_present(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(research_routes, "DEEP_RESEARCH_DIR", str(tmp_path / "deep_research"))
|
||||
handler = MagicMock()
|
||||
handler._active_tasks = {"sess-abc": {"owner": "alice"}}
|
||||
handler.get_status.return_value = {"status": "running", "progress": {"phase": "searching"}}
|
||||
handler.get_source_state.return_value = None
|
||||
handler.get_source_coverage.return_value = None
|
||||
handler.get_navigation_trace.return_value = None
|
||||
handler.get_action_trace.return_value = None
|
||||
router = research_routes.setup_research_routes(handler)
|
||||
endpoint = _route_endpoint(router, "/api/research/status/{session_id}")
|
||||
|
||||
out = asyncio.run(endpoint(session_id="sess-abc", request=_req()))
|
||||
|
||||
assert out["status"] == "running"
|
||||
|
||||
|
||||
# --- /api/chat/stream_status/<id> ----------------------------------------
|
||||
|
||||
def _chat_stream_status_endpoint(monkeypatch):
|
||||
monkeypatch.setattr(chat_routes, "_verify_session_owner", lambda request, session_id: None)
|
||||
router = chat_routes.setup_chat_routes(
|
||||
MagicMock(), MagicMock(), MagicMock(), MagicMock(), MagicMock(), MagicMock()
|
||||
)
|
||||
return _route_endpoint(router, "/api/chat/stream_status/{session_id}")
|
||||
|
||||
|
||||
def test_stream_status_returns_200_idle_when_no_stream(monkeypatch):
|
||||
endpoint = _chat_stream_status_endpoint(monkeypatch)
|
||||
chat_routes._active_streams.pop("sess-idle", None)
|
||||
|
||||
out = asyncio.run(endpoint(SimpleNamespace(), "sess-idle"))
|
||||
|
||||
assert out == {"status": "idle"}
|
||||
|
||||
|
||||
def test_stream_status_returns_record_when_streaming(monkeypatch):
|
||||
endpoint = _chat_stream_status_endpoint(monkeypatch)
|
||||
chat_routes._active_streams["sess-live"] = {"status": "streaming", "mode": "agent"}
|
||||
try:
|
||||
out = asyncio.run(endpoint(SimpleNamespace(), "sess-live"))
|
||||
finally:
|
||||
chat_routes._active_streams.pop("sess-live", None)
|
||||
|
||||
assert out["status"] == "streaming"
|
||||
|
||||
|
||||
# --- client compatibility (source-level) ---------------------------------
|
||||
|
||||
def test_chat_watchdog_accepts_200_idle_body():
|
||||
src = inspect.getsource(chat_routes) # ensure chat_routes imported
|
||||
chatjs = open("static/js/chat.js", encoding="utf-8").read()
|
||||
# The stale-stream watchdog must key off the body status, not only a 404.
|
||||
assert "_noActiveStream" in chatjs
|
||||
assert "_probe.status !== 'streaming'" in chatjs
|
||||
assert src # keep flake happy
|
||||
|
||||
|
||||
def test_sessions_check_uses_body_status_not_ok_only():
|
||||
sj = open("static/js/sessions.js", encoding="utf-8").read()
|
||||
assert "info.status !== 'streaming'" in sj
|
||||
assert "status !== 'streaming'" in sj
|
||||
Reference in New Issue
Block a user