We need a concrete plan to implement deep research history listing, following the exact patterns already established in the project. The ticket is accurate: reopen and continue works, but session discovery is missing. We will fill that gap with a database function, API endpoint, Devii action, UI, and tests. --- ## Implementation Plan ### 1. Database Layer – add `list_deepresearch_sessions` **File:** `devplacepy/database/deepsearch.py` Add a new function following the existing pattern from `isslop/store.py`: ```python def list_deepsearch_sessions( db: Session, owner_kind: str, owner_id: int, limit: int = 20 ) -> list[DeepsearchSession]: return ( db.query(DeepsearchSession) .filter( DeepsearchSession.owner_kind == owner_kind, DeepsearchSession.owner_id == owner_id ) .order_by(DeepsearchSession.created_at.desc()) .limit(limit) .all() ) ``` This uses the existing index `idx_deepsearch_sessions_owner`. ### 2. Schema – add response model for list **File:** `devplacepy/schemas/jobs.py` Add a new schema class (alongside existing `DeepsearchJobOut` and `DeepsearchSessionOut`): ```python class DeepsearchSessionSummary(BaseModel): uid: str query: str status: str created_at: datetime class DeepsearchListOut(BaseModel): sessions: list[DeepsearchSessionSummary] ``` ### 3. API Endpoint – add `GET /tools/deepsearch/list` **File:** `devplacepy/routers/tools/deepsearch.py` Add after the existing `@router.get("/{uid}/export.md")` block (line ~318), before the WebSocket routes: ```python @router.get("/list", response_model=DeepsearchListOut) def list_deepsearch_sessions( db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): owner_kind = "user" owner_id = current_user.id sessions = database.list_deepsearch_sessions(db, owner_kind, owner_id) return DeepsearchListOut( sessions=[ DeepsearchSessionSummary( uid=s.uid, query=s.query, status=s.status, created_at=s.created_at ) for s in sessions ] ) ``` Import `DeepsearchSessionSummary`, `DeepsearchListOut` and the new database function. ### 4. Devii Agent Action – add `deepsearch_list` **File:** `devplacepy/services/devii/actions/catalog/tools.py` Add after `deepsearch_session` (line ~110): ```python class deepsearch_list(BaseDeviiAction): name = "deepsearch_list" description = "List the user's completed deep research sessions" parameters: list[ActionParameter] = [] # no required params def execute(self, *args, **kwargs) -> str: url = f"{settings.HOST}/tools/deepsearch/list" response = requests.get(url, headers=self._auth_header()) response.raise_for_status() data = response.json() if not data.get("sessions"): return "You have no deep research sessions yet." lines = ["Your past deep research sessions:"] for s in data["sessions"]: lines.append(f"- {s['query']} (status: {s['status']}, created: {s['created_at']})") return "\n".join(lines) ``` ### 5. UI – add history to main deepsearch page **File:** `devplacepy/routers/tools/deepsearch.py`, modify the `GET ""` endpoint (line 80) to also query sessions: ```python @router.get("") def deepsearch_page( request: Request, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): sessions = database.list_deepsearch_sessions(db, "user", current_user.id) return templates.TemplateResponse( "tools/deepsearch.html", { "request": request, "current_user": current_user, "history_sessions": sessions } ) ``` **File:** `devplacepy/templates/tools/deepsearch.html` Add a new section inside the container (after the existing `done-card` block or in the sidebar area): ```html {% if history_sessions %}

My Research History

{% endif %} ``` ### 6. Tests – add `list.py` **File:** `tests/api/tools/deepsearch/list.py` Follow the structure of `tests/api/tools/deepsearch/session.py`. Create a test client, authenticate as a normal user, create a few sessions via the existing `POST /run` endpoint (or directly in DB), then call `GET /tools/deepsearch/list` and verify: - Returns 200 with JSON containing `sessions` list. - The list contains the sessions the user owns. - Only sessions belonging to the authenticated user are returned. - Unauthenticated request returns 401. - Empty history returns empty list. Example skeleton: ```python def test_list_deepsearch_sessions(client, db_session, normal_user_token_headers): # Arrange: create two sessions via API or direct DB # Act: GET /tools/deepsearch/list response = client.get("/tools/deepsearch/list", headers=normal_user_token_headers) # Assert assert response.status_code == 200 data = response.json() assert "sessions" in data # ... exact assertions ``` --- ## Definition of Done - [ ] `list_deepsearch_sessions` database function exists and returns sessions sorted by `created_at DESC` for a given owner. - [ ] `GET /tools/deepsearch/list` endpoint exists, requires authentication, returns a JSON object with a `sessions` list containing `uid`, `query`, `status`, `created_at`. - [ ] Devii agent action `deepsearch_list` is added and can retrieve the user's session list. - [ ] The main deep research page (`/tools/deepsearch`) displays a “My Research History” section with clickable links to each session. - [ ] A test file `tests/api/tools/deepsearch/list.py` exists and contains at least one passing test for the list endpoint. - [ ] The project test command passes when executed inside a Python virtual environment: ``` python3 -m venv .venv && source .venv/bin/activate && pip install -e '.[dev]' -q && make test-unit ``` - [ ] The project lint command passes when executed inside the same virtual environment: ``` pip install -q ruff && ruff check . ``` All changes follow existing code patterns and require no additional external dependencies. The only new files are the test file; all other modifications are additions or minor edits to existing files.