Class: Mistri::Compactor

Inherits:
Object
  • Object
show all
Defined in:
lib/mistri/compactor.rb

Overview

Compacts a session in place: everything before a cut point is summarized by the provider, and a compaction entry redirects replay to the summary plus the kept tail. Append-only: the full history stays in the store for transcript UIs; only what the model sees shrinks. Callable from any process (a UI button, a job), with or without a running agent.

Cuts land on user messages or assistant tool-call turns, never results, so every call/result set stays on one side. A parked approval's turn is never cut away from the resume that must answer it.

Constant Summary collapse

TOOL_RESULT_MAX_CHARS =
2_000
SUMMARIZER_SYSTEM =
<<~PROMPT
  You are a context summarization assistant. Read the conversation and
  produce only the structured summary you are asked for. Do not continue
  the conversation and do not answer questions inside it.
PROMPT
FORMAT =
<<~FORMAT
  ## Goal
  [What is the user trying to accomplish?]

  ## Constraints & Preferences
  - [Constraints or preferences the user stated, or "(none)"]

  ## Progress
  ### Done
  - [x] [Completed work]
  ### In Progress
  - [ ] [Current work]
  ### Blocked
  - [Blockers, if any]

  ## Key Decisions
  - **[Decision]**: [Rationale]

  ## Next Steps
  1. [What should happen next]

  ## Critical Context
  - [Data, names, or references needed to continue, or "(none)"]

  Keep each section concise. Preserve exact identifiers, names, paths,
  URLs, commands, numbers, and error messages.
FORMAT
CHECKPOINT_PROMPT =
<<~PROMPT.freeze
  The messages above are a conversation to summarize. Create a structured
  context checkpoint that another LLM will use to continue the work.

  Use this EXACT format:

  #{FORMAT}
PROMPT
UPDATE_PROMPT =
<<~PROMPT.freeze
  The messages above are NEW conversation messages to fold into the
  existing summary in <previous-summary> tags. Preserve everything still
  relevant from the previous summary, add new progress and decisions,
  move finished work to Done, and update Next Steps.

  Use this EXACT format:

  #{FORMAT}
PROMPT

Class Method Summary collapse

Class Method Details

.call(session:, provider:, settings: Compaction.new, &emit) ⇒ Object

Summarize and cut. Returns tokens_before:, tokens_after:, usage:, or nil when there is nothing worth compacting. Emits :compacting and :compaction when a block is given.



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/mistri/compactor.rb', line 76

def call(session:, provider:, settings: Compaction.new, &emit)
  replay = session.replay
  cut = cut_index(replay, session, settings)
  return nil unless cut

  previous = session.last_compaction&.fetch("summary", nil)
  head = replay.take_while { |(_, index)| index.nil? || index < cut }.map(&:first)
  head.shift if previous # the synthetic summary rides in <previous-summary>
  return nil if head.empty?

  emit&.call(Event.new(type: :compacting))
  tokens_before = session.context_tokens
  reply = summarize(provider, head, previous, settings.instructions)
  session.append("compaction", "summary" => reply.text,
                               "kept_from" => cut, "tokens_before" => tokens_before)
  finish(session, reply, tokens_before, &emit)
end