Runner.Compact folds the older portion of history into a single
system-role summary when the previous turn's input tokens cross
threshold_ratio × MaxContextWindow. When summarization fails, the
runner falls back to truncateToBudget so a flaky summarize call never
breaks the user's request.
EstimatePromptTokens / totalPromptTokens give a conservative count
(roughly 3 chars per token) used by both the compaction trigger and
BuildMessages' new limitRAGContext / fitHistory helpers to cap the
prompt inside the provider's reported window before the request goes
out. Covers the first-turn case where no usage has been reported yet.
Adds runner_compaction_test.go with table-driven coverage for the
disabled, below-threshold, short-history, unknown-window, fallback
and first-stream cases, plus a regression for the RAG-context trimmer.
Adds a configurable compaction section to portfolio-bot.yaml with
threshold_ratio, keep_recent_turns and an optional summary prompt.
Wires the new fields through config.Validate() and cmd/chat-bot/main.go
into agent.Runner.WithCompaction() so the runner can opt in to
auto-compaction at startup.
Conversations survive page reloads and work for any frontend, not just
the widget. Server-side SQLite, conversation ID as bearer token, browser
identity via localStorage.
Backend
-------
- internal/portfolio/conversations.go: schema + CRUD. Conversations and
messages tables in the same SQLite DB as the RAG index, with
foreign-key cascade delete. Conv IDs are 16-byte random hex
(128 bits of entropy).
- internal/portfolio/indexer.go: applies conversation schema + enables
foreign_keys pragma in OpenStore.
- internal/server/handlers.go: POST /api/chat accepts an optional
conversation_id, mints one if absent, persists user message before
the LLM runs and assistant message (with sources) after the stream
completes. New handlers: GetConversation, ListConversations,
DeleteConversation.
- internal/server/server.go: routes for GET /api/conversations,
GET/DELETE /api/conversations/{id}.
- internal/server/conversations_test.go: 6 tests (round-trip, continue,
list, 404, delete, streaming).
Widget
------
- web/chat-widget.js: stores conv_id in localStorage["rony-chat-conv"],
includes it in the chat request body, captures new IDs from the
server's 'start' SSE event, and calls GET /api/conversations/{id} on
load to restore history. On 404 it clears the stored ID and starts
fresh.
Docs
----
- docs/architecture.md: §3.1 documents the conversation_id field and
new REST endpoints; new §3.4 covers persistence lifecycle, schema,
client responsibilities, and auth model. §5.6 updated; filetree
reflects the new files.
- web/README.md: new 'Conversation persistence' section explains the
browser-scoped behavior and how to opt out or persist across devices.