5 Commits
Author SHA1 Message Date
Teknium 792330dc91 fix(hooks): a blocked prompt is stored only as the withheld placeholder
A pre_llm_call veto kept the typed text as the user row's content and put
the placeholder only in api_content, which only the main-loop wire replay
reads. Compaction (summary request and handoff), session_search, external
memory providers and the routed background-review digest all read content,
so the vetoed prompt reached a model at the next compaction or search.

- The placeholder is now the row's content and the persist override; the
  typed text lives only in display_metadata.withheld_prompt, which the CLI
  resume recap, TUI/Desktop session history and the dashboard render.
- A row the TUI/Desktop wrote at submit time is rewritten in place
  (set_user_message_content gains an optional display_metadata, also on the
  managed-worker store op).
- The hook now runs before turn-start compaction and a blocked turn skips
  it: that summary request quotes recent user turns, this one included.
- Memory-provider transcript hand-offs drop the display-only key.
2026-10-31 22:59:20 -07:00
Teknium 5b4256eb2f fix(worker): implement the prologue's row-addressed user-row backfills
The turn prologue (agent/turn_context.py) updates a user row another writer
already materialized (in-place preflight compaction, an early/close flush, a
submit-time row) by its _row_id:
  _stamp_api_content_sidecar -> set_message_api_content(sid, row_id, ...)
  _append_multimodal_context -> set_user_message_content(sid, row_id, ...)
A managed worker runs the same prologue with RuntimeSessionStore as
_session_db, which had neither method. Both calls raised AttributeError,
were swallowed by the prologue's warning-only except, and the durable row
kept the pre-context bytes: a resumed session replays a view the model never
saw (prompt-cache divergence for the sidecar; the multimodal context part
missing from the stored row).

Two closed, fenced receipt ops mirror the SessionDB guards exactly:
session.message_api_content (row_id + content match, active user row of THIS
session) and session.user_content (row_id, active user row of THIS session).
Neither accepts arbitrary columns; a foreign or inactive row id updates 0 rows.

Probe (real SessionDB + worker receipt txn + real prologue functions,
r6-base): DIRECT sidecar ('question', 'question\n\nPLUGIN CONTEXT'),
multimodal 'look | [screenshot] | PLUGIN CONTEXT'; WORKER ('question', None)
and the raw parts JSON, with AttributeError logged for both. After: equal.
Test test_turn_prologue_row_backfills_land_through_the_worker_like_direct:
red on r6-base `('question', None) != ('question', 'question\n\nPLUGIN
CONTEXT')`, green after (also pins the foreign-row fence).

Review: https://github.com/NousResearch/hermes-agent/pull/106742#issuecomment-6067585232
2026-10-08 19:08:58 -07:00
Teknium b7d14c758e fix: retain recoverable worker title conflicts as durable outcomes 2026-09-08 17:49:54 -07:00
Teknium 235e4f12d7 feat: persist worker titles activity and route sidecars with receipts 2026-09-08 17:49:54 -07:00
Teknium b7de5a8eba feat: add assignment-fenced worker constructor and finalization handlers 2026-09-08 17:49:54 -07:00