Add managed conversation runtime and token-budget solver #102

Merged
lost-rob0t merged 19 commits from agent/conversation-runtime into main 2026-08-19 04:07:06 +00:00
lost-rob0t commented 2026-08-18 19:38:26 +00:00 (Migrated from github.com)

What changed

Starts #101 with the first real managed-conversation slice.

  • adds rlm_conversation with memory and persistent append-only transcript stores;
  • adds conversation create/open/list, exact message lookup, ordered/ranged history, recent/before/after/around/role selectors, search, stats, and term export;
  • makes invalid history ranges fail with structured outcomes instead of plain Prolog failure;
  • adds conversation_context_pack/3 and conversation_token_ledger/3;
  • adds conversation_turn/4 as a managed layer above the existing stateless rlm_completion/4 primitive;
  • adds rlm_context_budget, a provider-visible context ledger and CLP(FD) packing solver;
  • separates operator max_context_tokens from provider physical context size and uses the smaller hard ceiling;
  • charges output reserve and safety margin before selectable context;
  • tracks host-only metadata separately from model-visible context;
  • records a stage-aware token ledger with observed, charged, and cumulative tokens for every fixed section, selected context unit, output reserve, and safety margin;
  • supports explicit exact tokenizer callbacks and clearly marked conservative estimates when no tokenizer is registered;
  • models context units with multiple representations and uses CLP(FD) labeling/backtracking to maximize utility under the hard cap;
  • exposes the new runtime through prolog/rlm.pl;
  • adds deterministic tests and docs/conversation-runtime.md.

Why

The existing completion runtime deliberately creates bounded fresh roots. That is a good primitive but the wrong default contract for an OpenCode/Claude Code-style interactive agent. AgentProlog needs a durable complete transcript plus a bounded rolling provider-visible projection, with old turns still addressable through RLM rather than silently forgotten.

This slice establishes that boundary without changing rlm_completion/4 semantics.

Important invariants

  • context eviction does not delete transcript history;
  • conversation references remain enumerable and reopenable from the owning store;
  • a user-selected 300k working cap remains 300k even when the provider advertises a 1M-token window;
  • mandatory context cannot be silently dropped to make arithmetic fit;
  • unsatisfiable packs return structured errors rather than plain Prolog failure;
  • host-only MCP/config metadata is measured but not charged against model context;
  • the stage ledger cumulative total must equal the final charged total;
  • estimated token counts are never labeled exact.

Current limitations / next slices

This PR intentionally does not pretend #101 is complete. Follow-up work remains for:

  • provider/model tokenizer registry and counting of the final rendered provider request;
  • automatic prompt-compiler integration for local tools, MCP tools/prompts/resources, skills, and project instructions;
  • rich warm-context variants and extraction;
  • RLM cold-history retrieval without materializing the full transcript into each managed call;
  • relevance/task/entity/dependency/provenance utility scoring and candidate narrowing before exact packing;
  • async/streaming managed turns for AgentProlog.

Validation

The canonical test runner includes rlm_context_budget_test.pl and rlm_conversation_test.pl, and test/load_all.pl checks both new runtime modules.

An earlier exact head passed the full deterministic CI job, including static loads, PlUnit, benchmark/conformance, deep-recursion, CLI smoke, persistent graph resume, durable artifact restart, and whitespace checks. Its REAL OpenRouter job failed only because openrouter/free routed the streaming sentinel test to nvidia/nemotron-3.5-content-safety:free, which returned a safety classification instead of STREAM_OK; this is the existing #46 router-safety defect. GitHub Actions on the current exact head remains authoritative.

## What changed Starts #101 with the first real managed-conversation slice. - adds `rlm_conversation` with memory and persistent append-only transcript stores; - adds conversation create/open/list, exact message lookup, ordered/ranged history, recent/before/after/around/role selectors, search, stats, and term export; - makes invalid history ranges fail with structured outcomes instead of plain Prolog failure; - adds `conversation_context_pack/3` and `conversation_token_ledger/3`; - adds `conversation_turn/4` as a managed layer above the existing stateless `rlm_completion/4` primitive; - adds `rlm_context_budget`, a provider-visible context ledger and CLP(FD) packing solver; - separates operator `max_context_tokens` from provider physical context size and uses the smaller hard ceiling; - charges output reserve and safety margin before selectable context; - tracks host-only metadata separately from model-visible context; - records a stage-aware token ledger with observed, charged, and cumulative tokens for every fixed section, selected context unit, output reserve, and safety margin; - supports explicit exact tokenizer callbacks and clearly marked conservative estimates when no tokenizer is registered; - models context units with multiple representations and uses CLP(FD) labeling/backtracking to maximize utility under the hard cap; - exposes the new runtime through `prolog/rlm.pl`; - adds deterministic tests and `docs/conversation-runtime.md`. ## Why The existing completion runtime deliberately creates bounded fresh roots. That is a good primitive but the wrong default contract for an OpenCode/Claude Code-style interactive agent. AgentProlog needs a durable complete transcript plus a bounded rolling provider-visible projection, with old turns still addressable through RLM rather than silently forgotten. This slice establishes that boundary without changing `rlm_completion/4` semantics. ## Important invariants - context eviction does not delete transcript history; - conversation references remain enumerable and reopenable from the owning store; - a user-selected 300k working cap remains 300k even when the provider advertises a 1M-token window; - mandatory context cannot be silently dropped to make arithmetic fit; - unsatisfiable packs return structured errors rather than plain Prolog failure; - host-only MCP/config metadata is measured but not charged against model context; - the stage ledger cumulative total must equal the final charged total; - estimated token counts are never labeled exact. ## Current limitations / next slices This PR intentionally does not pretend #101 is complete. Follow-up work remains for: - provider/model tokenizer registry and counting of the final rendered provider request; - automatic prompt-compiler integration for local tools, MCP tools/prompts/resources, skills, and project instructions; - rich warm-context variants and extraction; - RLM cold-history retrieval without materializing the full transcript into each managed call; - relevance/task/entity/dependency/provenance utility scoring and candidate narrowing before exact packing; - async/streaming managed turns for AgentProlog. ## Validation The canonical test runner includes `rlm_context_budget_test.pl` and `rlm_conversation_test.pl`, and `test/load_all.pl` checks both new runtime modules. An earlier exact head passed the full deterministic CI job, including static loads, PlUnit, benchmark/conformance, deep-recursion, CLI smoke, persistent graph resume, durable artifact restart, and whitespace checks. Its REAL OpenRouter job failed only because `openrouter/free` routed the streaming sentinel test to `nvidia/nemotron-3.5-content-safety:free`, which returned a safety classification instead of `STREAM_OK`; this is the existing #46 router-safety defect. GitHub Actions on the current exact head remains authoritative.
Sign in to join this conversation.
No description provided.