- Delete CodeSession::chat() — the legacy inline tool loop that
duplicated the orchestrator's conversation + tool dispatch logic
- Delete wait_for_tool_result() — only used by the legacy path
- Make orchestrator mandatory in run_session (no more if/else fallback)
- Unify conversation creation into create_fresh_conversation()
- Add corrupted conversation recovery to create_or_append_conversation:
detects "function calls and responses" errors from Mistral (caused by
disconnecting mid-tool-call) and auto-creates a fresh conversation
- Add tracing-appender for optional rotating log file (SOL_LOG_FILE env)
- Add Procfile.dev for overmind process management
- extract_project_symbols against tempdir with Rust/TypeScript/Python
sources — walk_directory, detect_language, hidden/vendor dir skipping,
ProjectSymbol field population, docstring extraction
- script: sol.rooms() against live Tuwunel, sol.fetch() with allowlist
enforcement (allowed domain succeeds, blocked domain caught),
sol.memory.set/get round-trip against OpenSearch
- research: single-task execute against Mistral API with real Matrix
room, verifies session persistence lifecycle
- needs_compaction: verify threshold triggers after token accumulation
- set_agent_id / get_agent_id: round-trip agent ID storage
- reset_all: verify all rooms cleared, SQLite and memory
- context_hint: verify conversation receives recent history context
when creating new conversation with hint parameter
- bootstrap: create integration test room in Tuwunel, send bootstrap
message, print room ID in summary
- room_info: list_rooms and get_room_members against live Tuwunel
- research: execute with empty tasks against real Matrix room + Mistral
- identity: fix flaky list_users_tool test (use search instead of
unbounded list to avoid pagination)
Cover the transport-agnostic orchestrator, gRPC CodeAgent service,
tree-sitter code indexing, client/server tool dispatch, research tool,
SearXNG web search, Kratos identity tools, breadcrumbs, and all new
config sections.
Tools module:
- Added search_code to tool_definitions() (was in execute but not defined)
- 8 new unit tests: tool definitions (base/gitea/kratos/all), agent tools,
minimal registry, unknown tool dispatch, search_code without OpenSearch
Gitea indexer fix:
- Direct API calls for directory listings (SDK parses as single object)
- PAT-based auth for file fetching
- GiteaClient.base_url made public for direct API access
Integration tests:
- Gitea SDK: list_repos, get_repo, get_file, directory, full code indexing
- gRPC bridge: thinking events, tool call mapping, request ID filtering
- Evaluator: rule matching, conversation registry lifecycle
- Web search: SearXNG round-trip
When client sends IndexSymbols after session start, Sol indexes
the symbols to the sol_code OpenSearch index. Each symbol becomes
a SymbolDocument with file_path, name, kind, signature, docstring,
branch, and source="local".
Code index (sol_code):
- SymbolDocument: file_path, repo_name, language, symbol_name, symbol_kind,
signature, docstring, branch, source, embedding (768-dim knn_vector)
- CodeIndexer: batch symbol indexer with idempotent upserts
- Branch-aware: symbols scoped to branch with mainline fallback
Breadcrumbs:
- build_breadcrumbs(): adaptive context injection for coding prompts
- Default: project outline via aggregation (modules, types, fns)
- Adaptive: hybrid search (_analyze → symbol matching → BM25 + neural)
- Token budget enforcement with priority (outline first, then relevance)
- format_symbol(): signature + first-line docstring + file:line
Query optimization: uses _analyze API to extract key terms from
free-form user text, matches against actual symbol names in the index
before running the hybrid search.
session_chat_via_orchestrator now:
- Spawns generation on a background task
- Reads in_stream for client tool results in foreground
- Forwards results to orchestrator.submit_tool_result()
- Uses tokio::select! to handle both concurrently
- Uses GenerateRequest + Metadata (no transport types in orchestrator)
- Calls grpc::bridge (not orchestrator::grpc_bridge)
ToolRegistry gains execute_with_context(&ToolContext) which bridges
to the existing execute(&ResponseContext) via a shim. The orchestrator
calls only the new method — no ResponseContext in its dependency tree.
grpc_bridge.rs → grpc/bridge.rs. The bridge maps OrchestratorEvents
to protobuf ServerMessages — it imports from orchestrator (downstream)
and grpc protos (sibling). The orchestrator never imports from grpc.
- Single run_tool_loop() replaces chat/code-specific variants
- generate() for ConversationRegistry path
- generate_from_response() for caller-managed conversations
- Engine uses ToolContext via execute_with_context(), no ResponseContext
- No imports from grpc, sync, matrix, context, or agent_ux modules
Replace transport-coupled types with clean abstractions:
- GenerateRequest: single entry point, no room_id/session_id
- Metadata: opaque key-value bag for transport routing data
- ToolContext: replaces ResponseContext in tool execution
- TokenUsage: clean token count struct
- Simplified OrchestratorEvent: remove AgentProgress*, MemoryExtraction*
Removed: ResponseMode, ChatRequest, CodeRequest.
Orchestrator engine:
- engine.rs: unified Mistral Conversations API tool loop that emits
OrchestratorEvent instead of calling Matrix/gRPC directly
- tool_dispatch.rs: ToolSide routing (client vs server tools)
- Memory loading stubbed (migrates in Phase 4)
Server-side tokenizer:
- tokenizer.rs: HuggingFace tokenizers-rs with Mistral's BPE tokenizer
- count_tokens() for accurate usage metrics
- Loads from local tokenizer.json or falls back to bundled vocab
- Config: mistral.tokenizer_path (optional)
No behavior change — engine is wired but not yet called from
sync.rs or session.rs (Phase 2 continuation).
Introduces the orchestrator module with:
- OrchestratorEvent enum: 11 event variants covering lifecycle, tools,
progress, and side effects
- RequestId (UUID per generation), ResponseMode (Chat/Code), ToolSide
- ChatRequest/CodeRequest structs for transport-agnostic request input
- Orchestrator struct with tokio::broadcast channel (capacity 256)
- subscribe() for transport bridges, emit() for the engine
- Client-side tool dispatch: pending_client_tools map with oneshot channels
- submit_tool_result() to unblock engine from gRPC client responses
Additive only — no behavior change. Existing responder + gRPC session
paths are untouched. Phase 2 will migrate the Conversations API path.
- gRPC dev_mode config: disables JWT auth, uses fixed dev identity
- Agent prefix (agents.agent_prefix): dev agents use "dev-sol-orchestrator"
to avoid colliding with production on shared Mistral accounts
- Coding sessions use instructions (system prompt + coding addendum)
with mistral-medium-latest for personality adherence
- Conversations API: don't send both model + agent_id (422 fix)
- GrpcState carries system_prompt + orchestrator_agent_id
- Session.end() keeps session active for reuse (not "ended")
- User messages posted as m.notice, assistant as m.text (role detection)
- History loaded from Matrix room on session resume
- Docker Compose local dev stack: OpenSearch 3 + Tuwunel + SearXNG
- Dev config: localhost URLs, dev_mode, opensearch-init.sh for ML setup
spawns gRPC server alongside Matrix sync loop when [grpc] config
is present. shares ToolRegistry, Store, MistralClient, and Matrix
client with the gRPC CodeSession handler.
when append_conversation fails (422, 404, etc.), the stale mapping
is deleted and a fresh conversation is created automatically.
prevents Sol from being permanently stuck after a hung research
session or Mistral API error.
new search_web tool calls SearXNG (cluster-internal, free, no tracking)
instead of Mistral's built-in web_search ($0.03/query + rate limits).
returns structured results from DuckDuckGo, Wikipedia, StackOverflow,
GitHub, arXiv, and Brave. no API keys, no cost, no rate limits.
removed Mistral AgentTool::web_search() from orchestrator — replaced
by the custom tool which goes through Sol's normal tool dispatch.
research agents now have a 2-minute timeout via tokio::time::timeout.
a hung Mistral API call can no longer block Sol's entire sync loop.
timed-out agents return partial results instead of hanging forever.
on startup, Sol detects research sessions with status='running' from
previous crashes and marks them as failed. 6 new tests covering the
full research session lifecycle: create, append findings, complete,
fail, hung cleanup, and partial findings survival.
main.rs: create KratosClient, pass mistral+store to ToolRegistry,
build active_agents list for dynamic delegation.
conversations.rs: context_hint for new conversations, reset_all.
sdk/mod.rs: added kratos module.
new research tool spawns 3-25 micro-agents (ministral-3b) in
parallel via futures::join_all. each agent gets its own Mistral
conversation with full tool access.
recursive spawning up to depth 4 — agents can spawn sub-agents.
research sessions persisted in SQLite (survive reboots).
thread UX: 🔍 reaction, per-agent progress posts, ✅ when done.
cost: ~$0.03 per research task (20 micro-agents on ministral-3b).
search_archive, get_room_context, and sol.search() (in run_script)
enforce a configurable member overlap threshold. results from a
room are only visible if >=25% of that room's members are also in
the requesting room.
system-level filter applied at the opensearch query layer — sol
never sees results from excluded rooms.
new engagement types: Respond (inline), ThreadReply (threaded),
React, Ignore. LLM returns response_type to decide HOW to engage.
silence mechanic: "shut up"/"be quiet" sets a 30min per-room timer.
only direct @mention breaks through.
structural suppression (A+B):
- reply to non-Sol human → capped at React
- 3+ human messages since Sol → forced passive mode
threads have a lower relevance threshold (70% of spontaneous).
time context injected into evaluator prompt.
midnight-based day boundaries (today, yesterday, 2 days ago),
week/month boundaries, rolling offsets (1h to 30d). injected
into system prompt via {time_block} and per-message via compact
time line. models no longer need to compute epoch timestamps.
list_users, get_user, create_user, recover_user, disable_user,
enable_user, list_sessions. all via kratos admin API (cluster-
internal, no auth needed). email-to-UUID resolution with fallback
search. delete_user and set_password excluded (CLI-only).
repos: create, edit, fork, list org repos
issues: edit, list comments, create comment
PRs: get, create, merge
branches: list, create, delete
orgs: list user orgs, get org
notifications: list
added write:repository and read:notification PAT scopes.
new response types: Comment, Branch, Organization, Notification.
authed_patch and authed_delete helpers with 401 retry.
URL-encoded query params throughout.
relicensed from MIT to AGPL-3.0-or-later. commercial license
available for organizations that need private modifications
(does not permit redistribution).
sol 1.0.0 ships with:
- multi-agent architecture with mistral conversations API
- user impersonation via vault-backed PAT provisioning
- gitea integration (first domain agent: devtools)
- per-user memory system with automatic extraction
- full-context evaluator with system prompt awareness
- agent recreation on prompt changes with conversation reset
- web search, sandboxed deno runtime, archive search