Prerequisite: 04-comet-sdk.md
Next: 06-cometmind-features.md
cometmind is the local agent runtime and source of truth. It owns reasoning orchestration, session/job persistence, workspace scoping, tool execution, memory, MCP, and the localhost API the desktop app consumes.
| Surface | Command / file | Role |
|---|---|---|
| HTTP API | cometmind serve → server/server.go |
Primary Cometline integration |
| CLI chat | cometmind chat "message" |
Terminal testing |
| CLI init | cometmind init |
Create config + DB + register workspace |
| Discord | cometmind gateway run --platform discord |
Messaging gateway |
| Settings/process | cometmind settings reload, cometmind process ... |
Long-running process control |
| Models | cometmind model list/set |
Inspect or update enabled/default models |
| Library | internal/runtime, internal/agent |
Shared by all surfaces |
All surfaces use the same agent.Runner and session.Service — no duplicate agent implementations.
runtime.New() in internal/runtime/runtime.go is the composition root:
runtime.New()
→ config.Load() (JSON settings or legacy TOML)
→ store.OpenSQLite() (SQLite + pragmas + migration)
→ session.New(db)
→ jobs.NewService(db)
→ scheduler.NewService(db)
→ memory.NewService(...) if enabled
→ mcp.NewManager(...) and background connect
→ retention, jobs maintenance, scheduler, autonomy workers where enabled
RunnerFor(session) wires a session-specific provider, session service, and workspace-scoped tool registry into an agent.Runner.
agent.Runner in internal/agent/runner.go is the load-bearing brain.
type TurnStore interface {
BuildSDKMessages(ctx) ([]cometsdk.Message, error)
AppendAssistantStep(...)
AppendToolResults(...)
// ...
}The runner depends on TurnStore, not concrete SQLite — making the loop testable.
Run(ctx, turn, emit):
defer emit(done)
for step < MaxSteps:
messages ← store.BuildSDKMessages()
messages ← NormalizeHistoryForProvider(messages)
emit(turn_status)
memories ← memory.RetrieveForTurn(...)
messages/context ← compact if needed
req ← BuildRequest(system+memories+skills, tools, messages)
stream ← llm.StreamMessage(provider, req)
for event := range stream.Events():
emit(translate(event))
result ← stream.Result()
store.AppendAssistantStep(result)
store.SaveTokenUsage(result.Usage)
if no tool calls or stop or max_tokens: break
for each tool call:
output ← registry.Execute(tool, input)
store.AppendToolResult(...)
emit(tool_result)
GitNexus context Run -f runner.go confirms outgoing calls to StreamMessage, all event.* emitters, BuildRequest, and NormalizeHistoryForProvider.
A turn is more than a done event. The runtime persists assistant/tool output and token usage, keeps opaque provider continuation state only for the matching provider/model scope, and can persist assistant media separately from text. Post-turn memory extraction is asynchronous and globally bounded, so completion is not held hostage by a slow extraction. Provider/model capability failures also feed a compatibility policy, preventing the runtime from repeatedly requesting known-unsupported features.
| File | Role |
|---|---|
request.go |
BuildRequest — assembles comet-sdk Request |
normalize.go |
Provider-specific history cleanup |
job_progress_hook.go |
Background job progress during runs |
contextwindow.go, compaction.go, budget.go |
Context budget and compaction |
| Table | Purpose |
|---|---|
workspaces |
Registered absolute workspace paths |
sessions |
Conversations: model, provider, token usage JSON |
messages |
User, assistant, tool_result, system rows |
tool_calls |
Tool-call shells + execution output |
memories |
Semantic memory entries with embeddings |
memory_events |
Memory audit events |
memory_reembed_jobs |
Durable embedding migration/rebuild work |
assistant_provider_states |
Opaque continuation state scoped to the provider/model that produced it |
model_capability_negatives |
Learned compatibility exclusions for unsupported model capabilities |
inbox_messages |
Durable user-facing notifications and reply state |
gateway_sessions |
External chat thread/channel to session mappings |
jobs |
Durable job queue with status, leases, retry/archive/delete metadata |
scheduled_jobs |
One-shot and recurring schedule definitions |
job_events |
Audit log for job lifecycle changes |
Database path: ~/.cometmind/cometmind.db
- Tracked via
PRAGMA user_version/schemaVersionininternal/db/migrate.go - Read
schemaVersioninmigrate.gofor the current version; schema changes for existing users need an incrementalalterStatementsentry, not only aschema.sqledit - Never edit generated sqlc files — run
sqlc generateafter schema/query changes
| Operation | Method area |
|---|---|
| Register workspace | EnsureWorkspace |
| Create/list/delete sessions | CRUD methods |
| Append user message | AppendUserMessageContent |
| Persist assistant step | AppendAssistantStep |
| Persist tool results | AppendToolResult |
| Rebuild SDK history | buildSDKMessagesFromRows |
| UI transcript | LoadTranscript in transcript.go |
| Token usage snapshot | JSON in sessions.token_usage |
| Field | Format |
|---|---|
messages.reasoning_content |
JSON array of reasoning blocks |
messages.content (tool_result) |
JSON {tool_call_id, content, is_error} |
sessions.token_usage |
JSON cometsdk.TokenUsage |
sessions.context_summary |
Compacted conversation summary |
Gin app in server/server.go, built via server.New(deps).
Implemented in server/messages.go as handlePostMessage (registered from server/server.go):
handlePostMessage:
1. Parse + validate JSON body
2. Load session + workspace
3. runtime.RunnerFor(session)
4. runManager.Acquire(sessionID) — one active run
5. Persist user message (+ auto-title if first)
6. Set SSE headers (text/event-stream)
7. goroutine: runner.Run(ctx, turn, writeSSE)
8. Flush after each event
9. runManager.Release on completion
server/run_manager.go enforces one in-flight run per session. Prevents interleaved tool results and corrupted transcripts when the user rapid-fires messages.
Cancel via DELETE /api/v1/sessions/{id}/runs/current → RunManager.Cancel.
Allows Vite dev origins, localhost, app://, file://, and empty origin for packaged app.
Capability policy, not separate registries of hand-picked names:
| Surface | Used by | Capabilities |
|---|---|---|
ParentSurface |
Main agent | Full: read/edit/run, skills+drafts, spawn, jobs, memory, MCP, settings; delegate_coding_task only if ACP enabled |
ResearchSurface |
In-process general subagent | Read + skills |
CodingSurface |
In-process coding subagent | Read + edit + run + skills (no MCP / spawn / settings) |
Built per workspace root via newRegistryWithSurface:
| Family | Tools |
|---|---|
| FS / shell / web | read_file, edit_file, write_file, list_dir, glob, grep, run_command, web_fetch, web_search |
| Skills | load_skill, read_skill_file, write_skill, draft tools (write_skill_draft, …) |
| Subagents / harness | spawn_general_agent, wait_subagents; delegate_coding_task when harness enabled |
| MCP | list_mcp_servers, reconnect_mcp_server, plus mcp_{serverId}_{toolName} |
| Jobs | list_jobs, propose_job, create_job, claim_job, update_job, complete_job, release_job, scheduled-job tools |
| Memory | recall_task_outcome, list_memories, search_memories, create_memory, update_memory, delete_memory |
| Settings | list_settings, get_settings, patch_settings (parent only; reject desktop keys) |
internal/tools/sandbox/pathcheck.go prevents path escape outside workspace root. Every file tool goes through this check.
type Tool interface {
Spec() ToolSpec // name, description, JSON schema
Execute(ctx, input) (output, error)
}Register new tools in registry.go init() or NewRegistry.
- Read
~/.cometmind/cometline-settings.json(preferred) - Fall back to
~/.cometmind/config.tomlif JSON missing - Overlay
COMETMIND_*environment variables
NewForModel(providerID, modelID):
- Resolve provider entry from settings
- Resolve API key (settings → env → provider-specific vars) for key-based methods
- Wire
codexandxaisubscription/session providers - Construct concrete
cometsdk.Provider - For
opencode-go, dispatch by the model's resolved protocol from models.dev metadata:@ai-sdk/openai→ OpenAI Responses (openairesponsesprovider),@ai-sdk/anthropic→ Anthropic Messages, default (including offline catalog) → Chat Completions
NewFor (entry's primary model) and NewMemoryLLM (extraction model) delegate to NewForModel. The shared Responses wire protocol lives in comet-sdk/internal/responsesproto and is reused by both the Codex and OpenCode Go providers.
internal/event/event.go defines the CometMind-native event union and JSON wire format. The runner translates comet-sdk events into these before the server writes SSE frames.
Runtime-only events (no direct SDK equivalent) include:
turn_status,turn_recovermemory_injected,memory_updated,memory_compaction_completedsubagent_started,subagent_progress,subagent_finished
This is a second translation layer — intentional separation so the OpenAPI contract can diverge slightly from SDK internals.
| Command | Use |
|---|---|
go run . init --workspace /path |
Bootstrap config + DB |
go run . serve --port 7700 |
Start API (what Electron spawns) |
go run . chat "hello" |
Quick terminal test |
go run . session list |
List sessions |
go run . gateway run --platform discord |
Start Discord bot |
go run . settings reload |
Ask running processes to reload safe settings in place |
go run . process status|stop|restart |
Inspect/control long-running CometMind processes |
go run . model list|set |
Inspect or change model defaults in settings |
cd cometmind
go test ./... # All tests
go test -run TestPostMessage ./server # Specific handler testServer tests use httptest + temporary SQLite databases.
Before changing CometMind, verify:
- Sessions remain workspace-scoped
- One run per session enforced
-
donealways emitted - Tool calls persisted before results
-
turn_status/doneevents keep UI progress and termination coherent - Workspace sandbox intact
- Schema changes have migrations
- OpenAPI updated if API changes
- Jobs changes update job events, leases, settings, and retention behavior together
06-cometmind-features.md covers memory, MCP, coding-harness delegation, Discord, skills, and background jobs built on this runtime.