fix: key the tools[] pin by code version; never re-add config-excluded tools
Review follow-up on the byte-identical tools[] pin. - The pin records the code identity that built it (checkout/build sha, else the release version). Written by the same code, every pinned tool that is still available keeps its pinned bytes, including tools whose parameters are derived per surface (delegate_task, text_to_speech, memory, patch). The per-tool "parameters differ -> take current" rule replaced those bytes on every surface hop and rewrote the ~44KB pin each time. A pin from other code (`hermes update`, legacy name lists) takes the current definitions once and is re-pinned. - A pinned tool this process did not build is carried forward only while this agent's toolset selection allows it (enabled minus disabled toolsets and role reservations, before check_fn). It must also pass the session schema gates on the merged array, so browser_exec never comes back once terminal is gone. Client-surface toolsets (desktop_ui, project) still carry across hops: no config choice removed them there. - The rotation compaction child inherits the parent's pin in the publish transaction. - `hermes sessions recover` keeps pin rows in its system_prompts sweep and clears dangling pin hashes, as lost-and-found now does too. Profile moves carry the pin like the prompt. A continuing session whose pin is missing or unreadable (a row swept by an older build) pins the tools it sends on that turn, so later hops stay stable.
This commit is contained in:
@@ -661,12 +661,12 @@ class SessionSessionsMixin:
|
||||
self._delete_unreferenced_system_prompts(conn)
|
||||
self._execute_write(_do)
|
||||
|
||||
def update_session_tool_names(self, session_id: str, tools: Optional[List[Any]]) -> None:
|
||||
"""Persist the session's ``tools[]`` pin so a rebuilt AIAgent sends the same bytes; ``None``
|
||||
clears. The array repeats across sessions like a system prompt does, so it is stored in the
|
||||
same content-addressed ``system_prompts`` table and the column holds its hash (legacy rows:
|
||||
an inline JSON name list); ``get_session`` resolves either."""
|
||||
payload = json.dumps(list(tools)) if tools is not None else None
|
||||
def update_session_tool_names(self, session_id: str, pin: Any) -> None:
|
||||
"""Persist the session's ``tools[]`` pin (JSON-serializable) so a rebuilt AIAgent sends the
|
||||
same bytes; ``None`` clears. The array repeats across sessions like a system prompt does, so it
|
||||
is stored in the same content-addressed ``system_prompts`` table and the column holds its hash
|
||||
(legacy rows: an inline JSON name list); ``get_session`` resolves either."""
|
||||
payload = json.dumps(pin) if pin is not None else None
|
||||
def _do(conn):
|
||||
conn.execute("UPDATE sessions SET tool_names = ? WHERE id = ?",
|
||||
(self._store_system_prompt(conn, payload), session_id))
|
||||
|
||||
Reference in New Issue
Block a user