fix: one session sends byte-identical tools[] across TUI, oneshot and gateway hops
The session tools pin (sessions.tool_names) stored names only, so every fresh
process re-materialized the bytes from its own surface and every surface hop
of one durable session was a full prompt-cache miss:
* tool_search's deferred catalog is built per process ("Search 6 additional
tools" in the TUI gateway vs 5 in -q);
* a pinned tool missing from the fresh build (skill_manage under the -q
footprint) came back from the static registry schema, without its
dynamic_schema_overrides;
* a -q --resume that rebuilt the stored prompt (model switch, cwd drift)
persisted its own pruned array over the pin.
The pin now stores the full definitions and restore replays a pinned tool that
is still available byte-for-byte (deregistered tools drop, new ones append at
the tail, legacy name-only pins still work). A continuing session whose prompt
is rebuilt applies the pin before building it, matching the freeze policy
(tools[] only changes on /new, /reload-mcp, compaction). The array is
content-addressed in the existing system_prompts store like the prompt itself,
so identical arrays across sessions are stored once; get_session resolves it.
This commit is contained in:
@@ -513,16 +513,18 @@ class SessionDB(
|
||||
def _delete_unreferenced_system_prompts(conn) -> None:
|
||||
conn.execute(
|
||||
"DELETE FROM system_prompts WHERE NOT EXISTS ("
|
||||
"SELECT 1 FROM sessions WHERE sessions.system_prompt_hash = system_prompts.hash)"
|
||||
"SELECT 1 FROM sessions WHERE sessions.system_prompt_hash = system_prompts.hash) AND NOT EXISTS ("
|
||||
"SELECT 1 FROM sessions WHERE sessions.tool_names = system_prompts.hash)"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _session_row_dict(row: sqlite3.Row) -> Dict[str, Any]:
|
||||
data = dict(row)
|
||||
if "_system_prompt_resolved" in data:
|
||||
resolved = data.pop("_system_prompt_resolved")
|
||||
if "system_prompt" in data:
|
||||
data["system_prompt"] = resolved
|
||||
for column in ("system_prompt", "tool_names"):
|
||||
if f"_{column}_resolved" in data:
|
||||
resolved = data.pop(f"_{column}_resolved")
|
||||
if column in data:
|
||||
data[column] = resolved
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
|
||||
Reference in New Issue
Block a user