-
Notifications
You must be signed in to change notification settings - Fork 46.3k
fix: scope session search handoff recall #35762
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
a249169329-cpu
wants to merge
2
commits into
NousResearch:main
Choose a base branch
from
a249169329-cpu:fix/session-search-scope-handoff-clean
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
138 changes: 138 additions & 0 deletions
138
docs/plans/2026-05-31-session-search-scope-handoff-contract.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,138 @@ | ||
| # Session Search Scope + Previous/Handoff Contract Implementation Plan | ||
|
|
||
| > **For Hermes:** This plan is the implementation contract for fixing gateway session-search cross-contamination after `/new`. | ||
|
|
||
| **Goal:** Prevent `/new` handoff/previous-session requests from retrieving unrelated sessions, especially across QQ users or adjacent projects. | ||
|
|
||
| **Architecture:** Store stable session scope metadata in `sessions`, propagate current gateway scope into `session_search`, and add an explicit `previous`/`handoff` retrieval path that does not rely on keyword search. Default gateway recall is scoped; explicit global search remains available for debug/admin use. | ||
|
|
||
| **Tech Stack:** Python, SQLite SessionDB, Hermes gateway `SessionSource`, tool executor, `session_search` tool. | ||
|
|
||
| --- | ||
|
|
||
| ## Merged Review Contract | ||
|
|
||
| This merges Codex design review and the user's follow-up review. | ||
|
|
||
| ### 1. Dedicated previous/handoff mode | ||
|
|
||
| `session_search(mode="previous" | "handoff", scope="current")` | ||
|
|
||
| Behavior: | ||
|
|
||
| - Only uses the current platform/chat scope. | ||
| - Excludes the current session lineage. | ||
| - Picks the most recent ended session first (`ended_at DESC`). | ||
| - Falls back to last message activity only after `ended_at` ordering. | ||
| - Never performs keyword/global discovery. | ||
| - If scoped lookup finds nothing, returns an empty result; it must not silently fall back to global search. | ||
|
|
||
| This is the core fix for “刚才那个会话 / 交接信息”. | ||
|
|
||
| ### 2. Stable scope fields | ||
|
|
||
| `session_key` may be persisted and used as auxiliary evidence, but default isolation is based on stable business fields: | ||
|
|
||
| - `source` — canonical platform/source (`qqbot`, `telegram`, `cli`, `webui`, `cron`, etc.) | ||
| - `chat_type` | ||
| - `chat_id` | ||
| - `thread_id` | ||
| - `user_id` | ||
| - `session_key` | ||
|
|
||
| QQ DM default scope is `source + chat_type + chat_id`; `user_id` is stored but not the primary QQ DM isolation key. | ||
|
|
||
| ### 3. Scope propagation chain | ||
|
|
||
| The current scope must be available from gateway to tool execution: | ||
|
|
||
| - gateway adapter creates `SessionSource` | ||
| - gateway `SessionStore` persists scope on new/reset session creation | ||
| - `run_agent.AIAgent` receives `platform/user_id/chat_id/chat_type/thread_id/gateway_session_key` | ||
| - `agent/tool_executor.py` passes those as hidden current-scope kwargs to `session_search` | ||
| - `tools/session_search_tool.py` applies scope defaults and filters | ||
|
|
||
| ### 4. Legacy compatibility with strict fallback | ||
|
|
||
| New sessions write full scope fields. Old sessions may have null scope fields. | ||
|
|
||
| Rules: | ||
|
|
||
| - `previous`/`handoff`: primary path is scoped new fields. Legacy fallback is allowed only within the same `source`, excluding current lineage, bounded to recent/ended ordering, and marked in the response. No cross-source/global fallback. | ||
| - Ordinary search/browse: gateway sessions default to current scope. CLI remains broad/legacy-friendly unless explicit scope is provided. | ||
| - Global search must be explicit: `scope="global"`. | ||
|
|
||
| ### 5. Reliable ended_at | ||
|
|
||
| `/new`, auto reset, session switch, and compression split should mark old sessions ended. This change depends on existing `SessionStore.reset_session()` and `SessionDB.end_session()` behavior; tests must cover `/new`-style ended-session selection. | ||
|
|
||
| ### 6. Behavior-level regression tests | ||
|
|
||
| Required tests: | ||
|
|
||
| - QQ user A/B both mention “新增功能”; A scoped search does not see B. | ||
| - A `/new` then `mode="handoff"` returns A's just-ended admissions session. | ||
| - Adjacent admissions/tutoring sessions: `mode="handoff"` returns admissions and not tutoring/OCR/PDF/学生档案. | ||
| - Current lineage is excluded. | ||
| - Scoped no-result does not global fallback unless `scope="global"`. | ||
| - Legacy null-scope sessions are not lost, but fallback is source-bounded and flagged. | ||
| - CLI search is not accidentally constrained by QQ scope rules. | ||
|
|
||
| ## Implementation Tasks | ||
|
|
||
| ### Task 1: Add scope columns and write paths | ||
|
|
||
| Modify `hermes_state.py`: | ||
|
|
||
| - Add nullable columns to `sessions`: `chat_type`, `chat_id`, `thread_id`, `session_key`, `user_id_alt`. | ||
| - Keep indexes referencing new columns after `_reconcile_columns()`. | ||
| - Extend `_insert_session_row()` / `create_session()` to accept those kwargs. | ||
|
|
||
| Modify `gateway/session.py` and `run_agent.py`: | ||
|
|
||
| - Pass scope metadata when creating DB sessions. | ||
|
|
||
| ### Task 2: Add scope helper + scoped filtering | ||
|
|
||
| Modify `tools/session_search_tool.py`: | ||
|
|
||
| - Add helper to resolve current scope from hidden kwargs. | ||
| - Add helper to decide default `scope`: | ||
| - gateway source (`qqbot`, `telegram`, `discord`, `slack`, etc.) + chat scope => current | ||
| - CLI/local with no chat scope => legacy/global-ish | ||
| - explicit `scope="global"` bypasses scope filters | ||
| - Add shared session scope matcher. | ||
|
|
||
| ### Task 3: Add previous/handoff mode | ||
|
|
||
| Modify `tools/session_search_tool.py`: | ||
|
|
||
| - Add `mode` schema enum: `previous`, `handoff`. | ||
| - Implement previous/handoff selection by current scope, excluding current lineage. | ||
| - Return recent session metadata + bookend start/end + messages; no FTS keyword search. | ||
| - Return empty when scoped none. | ||
|
|
||
| ### Task 4: Preserve search behavior with scoped default | ||
|
|
||
| Modify discovery/browse paths: | ||
|
|
||
| - Apply scope filters when default/current scoped. | ||
| - Keep explicit global mode. | ||
| - Preserve CLI broad search by default. | ||
|
|
||
| ### Task 5: Verify | ||
|
|
||
| Run focused tests: | ||
|
|
||
| ```bash | ||
| python -m pytest tests/tools/test_session_search.py -o addopts='' -q | ||
| python -m pytest tests/gateway/test_session*.py tests/test_hermes_state*.py -o addopts='' -q | ||
| ``` | ||
|
|
||
| Then inspect: | ||
|
|
||
| ```bash | ||
| git status --short --branch --untracked-files=all | ||
| git diff --stat | ||
| git diff --check | ||
| ``` |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This only covers the sequential executor. Current main’s concurrent path calls
agent_runtime_helpers.invoke_tool()and itssession_searchbranch still forwards onlydbandcurrent_session_id; please thread this same scope payload through that path and add a concurrent-dispatch regression.