Commit Graph

2 Commits

Author SHA1 Message Date
Eugene Eisenstein 514233aaef fix(dialectic): record prefetch evidence only once the context is built
The prefetch recorded the rows its searches returned immediately, then went
on to check for an empty result, stamp telemetry and format two markdown
sections. Any failure in that tail is swallowed by the surrounding handler,
which returns None and leaves `_prepare_query` building a prompt with no
prefetch block at all -- so evidence could name conclusions the agent was
never shown.

Move the recording below the formatting, so what is recorded is what the
returned context actually contains.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 14:07:37 -04:00
Eugene Eisenstein 7b3d331846 feat(dialectic): return the evidence a chat answer was built from
Add an opt-in `include_evidence` to both chat endpoints. The response then
carries the conclusions and messages the agent read while answering, plus the
tools it called.

Evidence is collated from what the agent accessed rather than reported by the
model. That over-reports -- a conclusion appears because the agent saw it, not
as proof the answer used it -- but it is deterministic, costs no model tokens,
and behaves the same at every reasoning level. Asking the model to cite its
sources fails quietly instead: weaker models produce incomplete or invented
citations, and a sparse citation list is indistinguishable from a sparse
answer. The prefetch already makes the point, feeding explicit conclusions
into the prompt without their IDs, so the model could not cite them if asked.

An accumulator is threaded from the router through the agent into ToolContext,
and read handlers hand it the rows they already loaded. Nothing is re-queried
when the response is built, so evidence inherits the scoping of the reads that
produced it and cannot become a way around a session allowlist.

Three details worth knowing:

- Prefetched conclusions never pass through the tool executor, so they are
  captured via a new `documents_out` sink on `search_memory`. On a query that
  answers without a tool call they are the whole of what was read.
- Conclusions dedupe by ID. `Representation`'s own deduplication keys on
  content and timestamp and ignores IDs, which would collapse distinct
  conclusions that happen to read alike.
- Conclusion timestamps are re-stamped UTC. `Representation` strips tzinfo so
  observations render compactly into prompts, which would otherwise put naive
  timestamps in the API beside timezone-aware message ones.

The two chat routes had byte-identical nested SSE formatters; they now share
one helper, so the terminal event carries evidence on both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-03 13:19:10 -04:00