Releases: mcheemaa/mistri
Release list
v0.4.0
Agents as markdown files: Mistri::Definition loads frontmatter config and a prompt body with fail-loud placeholder rendering. Agent context: pass any object as Mistri.agent(context:) and every tool handler and hook receives it as context.app. Images parse straight from data: URIs. OpenAI reasoning summaries keep their paragraph structure, live and finished.
Full changes: CHANGELOG.md
v0.3.0
Sessions now heal: a run killed mid-tool (deploy, crash) resumes cleanly instead of bricking the session, and context assembly reads the store exactly once. Empty completions retry under the standard policy instead of ending a run in silence. Gemini no longer merges consecutive user turns, which made it answer empty candidates. The spawn tool takes an optional name, so fan-out streams read as pricing-scout#a41f.
Full changes: CHANGELOG.md
v0.2.1
The project site is live: mistri.sh, with full docs at mistri.sh/docs. The gem's homepage and documentation links now point there.
Full changes: CHANGELOG.md
mistri 0.2.0
-
Repository hygiene: coverage floor enforced in CI (simplecov, 90% line),
contributing/security/conduct docs, issue and PR templates, Dependabot,
and rubygems documentation and bug tracker links. -
Per-tool timeouts: Tool.define(..., timeout: 30) answers in band when a
handler stalls, so one hung tool cannot stall the run. -
:tool_result events carry duration (seconds) for executed tools, feeding
latency metrics straight from any sink. -
Mistri::Reminder.every(3, text): a periodic tail reminder for long runs,
riding transform_context; due by completed assistant turns, fresh on the
wire each time, never persisted. -
Tool hooks: before_tool(call, context) blocks a call by returning the
reason as a String, answered to the model in band; it outranks the
approval gate and screens approved calls again at settle time, so an
aged approval never beats current policy. after_tool(call, result,
context) may replace a result (both channels), nil keeps it. Hooks that
raise fail safe: before blocks, after answers in band. -
transform_context accepts an array of transforms, applied in order.
-
Result#usage: every run reports its own token and cost accounting,
summing persisted turns and compaction calls; task sums across its fix
passes. Hosts meter a run without walking the session. -
MCP stdio wire: Client.new(command: [...], env: {...}) spawns a local
server as a child process speaking line-delimited JSON-RPC, credentials
in its environment per spec. Dying servers and non-protocol stdout fail
loudly; close terminates the child. -
MCP connections out of the box: Mistri::MCP::OAuth.start/.complete/
.refresh are storage-agnostic services implementing the spec's OAuth 2.1
subset (challenge and well-known discovery, RFC 8414 metadata with an
OpenID fallback, dynamic client registration as the host application,
PKCE with resource indicators, rotating refresh).rails generate mistri:mcp YourModelcreates a host-named connection model whose rows
carry their own flow state and encrypted tokens, with connection.tools
bridging straight into an agent and refreshing ahead of expiry. -
MCP bridge: Mistri::MCP::Client speaks Streamable HTTP (initialize
handshake, tools/list with pagination, tools/call, sessions with
transparent expiry recovery, JSON or SSE responses) with zero new
dependencies. Auth is a headers hash or a token string-or-lambda; a
lambda re-resolves once on 401, so host refresh logic lives in one place.
Mistri::MCP.tools bridges any server (or any duck-typed client, the
official mcp gem included) into Mistri tools with allow/deny lists, name
prefixing, and per-tool approval gates, so a third-party write tool can
ride the human-approval arc.
mistri 0.1.0
-
Live integration harness:
rake integrationruns every feature end to
end against real provider APIs, once per model in the matrix
(MISTRI_INTEGRATION_MODELS overrides the default trio). Scenarios assert
that generated codenames flowed through the machinery, so answers prove
information flow rather than model knowledge. The defaultrake test
stays hermetic. -
The spawn tool's child models now come only from a host allowlist
(models:on SubAgent.spawner); without one no model choice is offered,
so a hallucinated model id can never construct a provider. Found by the
integration harness on its first run. -
The skills system-prompt section instructs selection more firmly.
-
Sub-agents: delegation with a clean context (#2). Mistri::SubAgent names
a curated specialist (own provider/system/tools, optional schema: for
validated JSON answers); SubAgent.spawner is the open spawn_agent tool
where the model writes the child's instructions, grants a tool subset,
and may pick a model. Children run fresh sessions on the caller's store,
linked in its transcript and on the tool result's ui channel; their
events stream into the parent tagged with origin. Parallel spawn calls
fan out on the executor pool. Approval-gated tools are refused inside
children; gate the delegation itself instead. -
Tool handlers now receive an optional second argument, ToolContext
(session, signal, emit); procs ignore it invisibly and strict lambdas
keep their old arity. Events gained an origin field. -
Task mode: Agent#task(input, schema:) runs an exchange that must end in
JSON matching the schema — tools run as usual, providers constrain the
final answer natively where supported (Anthropic output_config, OpenAI
text.format strict, Gemini responseJsonSchema when no tools), and the
answer validates client-side everywhere. A violation goes back to the
model once (fixes:), then raises SchemaError; Result#output carries the
validated value. run(output_schema:) exposes raw constrained runs. -
Schema.violations (a zero-dependency validator for the supported subset,
with model-feedable error messages) and Schema.strict (wire preparation:
additionalProperties false everywhere, all-required for OpenAI strict). -
Skills: Mistri::Skill and Skills.load (a directory of SKILL.md folders or
flat .md files, flat-frontmatter name/description). Pass skills: to the
Agent (array or path): descriptions join the system prompt and a
read_skill tool serves full bodies on demand, so a skill library costs
one line each until used. Hosts with skills in a database construct
Skill objects directly. -
Rails integration:
rails generate mistri:install YourModelcreates a
host-named entry model and its migration for Stores::ActiveRecord
(MEDIUMTEXT payload on MySQL-family adapters). Streaming sinks under
Mistri::Sinks — ActionCable (lazy server, injectable), SSE (outbound
frames to any IO), and Coalesced (merges delta bursts to UI speed) — all
pure Ruby, usable asagent.run(input, &sink). No Railtie: generators
auto-discover and everything else duck-types. -
Retry policy: transient turn failures (429/5xx/529, timeouts, dropped or
truncated streams) retry with jittered backoff, honoring retry-after. On
by default (retries: on the Agent; false disables, or pass a tuned
RetryPolicy). Failed attempts record as retry entries and emit :retry
events but never become messages, so retries stay invisible to the model. -
Errored messages now carry a machine-readable error field ({type, status,
retry_after}) alongside error_message; in-stream provider errors keep
their wire classification (Anthropic overloaded, OpenAI rate limits,
Gemini status codes) instead of folding into prose. -
Two-channel tool results: a handler may return Mistri::ToolResult with
content for the model and ui for the host. The ui payload rides the tool
message and its :tool_result event, persists with the session for
transcript re-renders, and never reaches a provider. -
Context compaction: sessions compact automatically when the context grows
into the reserve headroom (compaction: on the Agent, on by default when the
model's window is known; pass false to disable). The provider writes a
visible structured summary; a compaction entry redirects replay to summary
plus kept tail while the full history stays in the store. Cuts land only on
user messages so tool pairs never split, parked approvals stay resumable,
and second compactions update the first summary. Agent#compact is the
manual button, Agent#context_usage the {tokens, window, fraction} gauge,
and :compacting/:compaction the events. -
transform_context: an Agent option that reshapes what the model sees each
turn (reminders, redaction, windowing) while the stored transcript stays
untouched. The lambda receives the replay messages and returns the messages
to send; it must keep tool calls paired with their results. -
Steering: Session#steer queues a user message from any process while a run
is live. The loop folds pending steers into the transcript at the next turn
boundary, and one that arrives as the model finishes cleanly extends the
run so it gets answered instead of dangling. Steers compose with approval
suspensions: queue a thought, approve, resume. -
Human-in-the-loop approval: a tool marked needs_approval (true or a
predicate on its arguments) suspends the run instead of executing. Runs
return a Result immediately; decisions are recorded on the session from any
process (approve/deny), and resume settles them and continues. No thread
ever waits on a human. -
Every run now returns a Mistri::Result (completed, awaiting_approval,
aborted, budget, or error) that delegates text and stop_reason to its final
message. -
Session entries are normalized to one canonical JSON shape across all
stores. -
Budget stops report stop_reason :budget, distinct from a user abort.
-
Tool results that are arrays of data serialize as JSON; empty input and
duplicate tool names fail loudly at the boundary. -
Mistri::MemoryandMistri::Tools.memory: durable knowledge across
sessions, read and rewritten whole, living wherever the host points it. -
Mistri::Workspace: the document store agents work in, with memory,
directory, ActiveRecord, and single-document backends, so editing a
database column works exactly like editing a file. -
Mistri::Tools.files: the built-in document tools (read_file, write_file,
edit_file, find_in_file, list_files). The edit tool speaks the flat
old_string/new_string shape models are trained on, tolerates alias keys,
and reports misses with the closest region and its exact difference. -
Mistri::Edit.replace: single-edit replacement with replace_all, newline
and BOM preservation, and near-miss diagnostics. -
Truncated streams now fail as retryable errors on every provider instead of
reading as user cancellations, and their tool calls pair without executing. -
Provider error turns carry the HTTP status and response body.
-
Budgets measure every ceiling per run, including wall clock.
-
Mistri::Edit: pure fuzzy text replacement with a uniqueness guarantee,
so an edit never silently changes the wrong region; the string core for a
workspace-backed edit tool that works against a database as well as a file. -
Mistri.agentandMistri.provider: build an agent or provider from a model
id, inferring the provider and reading its key from the environment. -
Aborted and truncated turns now replay without provider errors: unusable
thinking degrades to text, empty blocks are dropped, every tool call is
paired, and budget-only models skip adaptive thinking. -
The error hierarchy: every Mistri failure rescues as
Mistri::Error. -
The message protocol: immutable content blocks (text, thinking, image, tool call),
messages with provider identity, usage accounting with cost math, stop reasons. -
The streaming event union: twelve event types, each carrying an immutable
partial-message snapshot. -
Mistri::Providers::Fake: a scriptable provider for hermetic host tests. -
Mistri::AbortSignal: a thread-safe cancel latch with abort callbacks. -
Mistri::SSE: an incremental server-sent-events decoder. -
Mistri::Transport: a persistent per-provider streaming connection with
status-mapped errors and hard abort of hung streams. -
Mistri::PartialJson: best-effort parsing of in-flight tool arguments. -
Mistri::Models: a capability catalog with graceful passthrough, so unknown
models work the day they ship. -
Mistri::Providers::Anthropic: the Messages API streamed, with adaptive
thinking, prompt caching, signature round-trips, and eager tool-input
streaming. -
Mistri::Providers::OpenAI: the Responses API streamed and stateless, with
encrypted reasoning replay and thinking summaries. -
Mistri::Providers::Gemini: generateContent streamed, with unconstrained
thinking by default and verbatim thought-signature replay. -
Mistri::Agent: the streaming tool-calling loop, persisting each turn as it
completes so aborts and crashes resume without repair. -
Mistri::ToolandMistri::Schema: define tools with a raw JSON Schema or a
Ruby schema block; results may be text, JSON, or content blocks. -
Mistri::Sessionwith pluggable stores (Memory,JSONL, and an optional
ActiveRecordadapter for the host's own database). -
Mistri::Budget: opt-in ceilings on turns, tokens, cost, and wall-clock;
nothing is enforced unless the host sets it.