Skip to main content
What this is for: the comis command is how you set up your daemon, manage agents and channels, browse memory, audit security, and operate the running system from the terminal. Who it’s for: operators and developers running Comis on their own machine or a server. Comis ships 24 command groups (verified against packages/cli/src/cli.ts). The CLI talks to the running daemon over JSON-RPC; offline-only commands such as secrets operate directly on local files. Run any command with --help to see its full flag list — every flag below is also documented inline in the binary.
For installation and your first run-through, see Quick Start and Operations. This page is the exhaustive reference; the Operations section is the friendlier task-oriented guide.

Your first hour with the CLI

A complete first-time setup, end to end. Each step is one command.
1

Initialize the workspace

comis init walks you through provider keys, the first agent, and at least one channel. Use --non-interactive with flags for CI/Docker.
2

Confirm the daemon is healthy

comis health shows only failures and warnings. Exits non-zero in CI when something is wrong.
3

Check the unified status

comis status aggregates daemon, gateway, channels, and agents into one view.
4

Tail the daemon logs

comis daemon logs -f streams Pino JSON logs with color coding. Useful while debugging your first agent reply.
5

Add a new agent

comis agent create <name> creates and persists an agent through the daemon — no manual YAML edits needed.
6

Wire a channel

Channels are added with comis configure --section channels (interactive) or by editing ~/.comis/config.yaml directly. Token values are pulled from ~/.comis/.env via ${VAR_NAME} substitution.
7

Store secrets safely

comis secrets set encrypts API keys with AES-256-GCM into secrets.db. comis secrets audit flags any plaintext secrets still sitting in your config.
After this you have a running agent reachable from at least one channel, with secrets encrypted and an auditable config history (comis config history).

Global Options

These options apply across multiple commands:

Command Groups

Not to be confused with comis-agent. Every command on this page is the operator comis CLI: it talks to the daemon over the gateway with an operator token, runs on the host, and includes admin commands (secrets, config, tokens, …). There is a separate, very different CLI named comis-agent that an agent drives from inside its own jail — it authenticates with a capability lease (not an operator token), rides the loopback capability Unix socket (not the gateway), has no admin verbs, and is not on the host PATH. If you are looking for what an autonomous agent can do from inside its sandbox, see the comis-agent CLI reference — none of the operator commands below are reachable from there.

comis agent

Agent management commands for listing, creating, updating, and deleting agent configurations.

Subcommands

agent list

agent create <name>

agent configure <name>

agent delete <name>

agent models <agentId>

Show how each agent operation resolves to a concrete model: primary model, tiered operation overrides, source (config or default), per-operation timeouts, cross-provider flag, and API-key availability.

comis auth

OAuth authentication management for subscription-based providers (currently OpenAI Codex). Profiles are written to the OAuthCredentialStorePort — separate from SecretStorePort, with refresh + per-profile-ID locking semantics.

Subcommands

auth login

Exit codes (auth login):

auth list

Exit codes: 0 success (including empty store with informative message); 1 config load / store open failure.

auth logout

Exit codes: 0 profile removed; 1 profile not found in store.

auth status

Reports per-provider state: profile id, identity (email or accountId), expiry timestamp, status (active / expired), and last refresh time.
Errors from any auth subcommand are classified into 6 categories — see OAuth concepts → Error classification. The CLI prints both userMessage and an actionable hint (e.g., Re-authenticate with: comis auth login --provider openai-codex).

comis cache

Inspect aggregated token-usage cache statistics stored in the durable obs_token_usage SQLite database. Dispatches the obs.cacheStats.window RPC.

Subcommands

comis cache stats flags


comis channel

Channel management commands for viewing the connection status of configured chat platform adapters.

Subcommands

channel status


comis config

Configuration management commands for validating, viewing, modifying, and version-controlling the config file.

Subcommands

config validate

config show [section]

config set <path> <value>

No additional flags. The path uses dot-notation (e.g. gateway.port 5000). Values are parsed as JSON first, falling back to string.

config history

config diff [sha]

No additional flags. Displays a colorized diff of the current config against HEAD or a specific commit SHA.

config rollback <sha>

config sync-tooling

Discovers connected MCP servers (from integrations.mcp.servers[].name) and installed skills (walks agents.<id>.skills.discoveryPaths plus the daemon defaults ~/.comis/skills and ~/.comis/workspace/skills) and materializes the tooling: block in config.yaml. Modes:
  • Inspect (default) — prints the discovered/existing/diff/wouldWrite shape to stdout; never touches config.yaml. Daemon may be running.
  • --write — atomic write with config.pre-sync-tooling-<ISO>-<hex>.yaml backup. Append-only on existing keys (operator hand-edits to description / replacesPackages are preserved); stale hints (MCPs/skills no longer in discovery) are pruned.
  • --write --overwrite — full regenerate of the managed sub-tree, plus the destructive-overwrite warning.
Stub-emission contract: newly-generated MCP and skill hints land with description: TODO, replacesPackages: [], and a # TODO: list npm/pip packages this MCP replaces comment. Operators fill these by hand or via config tooling-fill (below). Until they’re filled, the install-detour subsystem cannot fire on alias overlaps for that hint. Exit codes:

config tooling-fill [hint-name]

Fills the description and replacesPackages fields of a tooling capability hint via the live Comis agent. Automates the LLM call, the daemon-stop window, the AST-aware YAML edit, and the daemon-restart cycle. Use after config sync-tooling --write has materialized stubs. Operating principles:
  • Daemon must be up for the LLM call (the CLI POSTs to /api/chat on the local gateway). The CLI then stops the daemon, mutates the file atomically, writes a config.pre-tooling-fill-<ISO>-<hex>.yaml backup, and restarts.
  • After systemctl start (or the equivalent) succeeds, the orchestrator polls /api/system.ping for up to 15 s to confirm the daemon actually came up — not just that the unit was queued. If the daemon crashed during boot (e.g. invalid YAML), the orchestrator restores the backup and exits 2 with a manual-recovery hint.
  • File ownership of config.yaml is preserved across the atomic write, even when run as root on behalf of an unprivileged service user (the typical VPS setup with comis user under systemd).
  • Backups older than the 5 most recent are pruned automatically after a successful run. The tooling-fill and sync-tooling prefixes prune independently.
  • The agent’s response is restricted to a strict 2-line format (DESCRIPTION: and REPLACES_PACKAGES:); any other field is stripped from the parsed output. Package names that fail the npm/pip name-shape regex are silently dropped (or surface as a hard failure if all are dropped).
Idempotency: A hint is “stub-valued” iff description ∈ {missing, "", "TODO"} AND replacesPackages ∈ {missing, []}. Stub-valued hints fill freely; hints with any operator-authored value refuse without --force. Exit codes:
comis config tooling-fill requires the daemon to be running at invocation time (the LLM call goes through the local gateway). It then stops the daemon, writes the file, and restarts. This inverts the daemon-state requirement of config sync-tooling --write (which requires the daemon to be stopped at invocation time).

comis configure

Interactive configuration editor using terminal prompts. Walks through editable config fields section by section, validates changes against the Zod schema, and writes back to the YAML file preserving formatting and comments.
Requires an interactive terminal (TTY). Use comis config set for non-interactive config changes.

comis daemon

Control the Comis daemon process. Uses systemd if available, otherwise falls back to direct process management with PID file tracking.

Subcommands

daemon start

No additional flags. Starts via systemd if installed, otherwise spawns a detached Node.js process and waits for the gateway health endpoint to respond.

daemon stop

No additional flags. Sends SIGTERM (graceful), escalates to SIGKILL after 10 seconds if the process does not exit.

daemon status

No additional flags. Checks status via RPC first, then systemd, then PID file.

daemon logs


comis doctor

Run diagnostic health checks across eleven categories: configuration, daemon, gateway, version, channels, Teams, Matrix, workspace, oauth, secrets-audit, and LCD store. The Teams and Matrix categories add channel-specific probes the shared channel check cannot: Teams probes its inbound webhook route, while Matrix — a polling channel — probes reachability, the e2ee crypto backend, and the device-verification posture over RPC. Supports auto-repair mode for fixable issues. The oauth category covers JWT expiry, schema mismatch, ca-certificates, HTTPS_PROXY drift, and TLS preflight — see OAuth concepts → Error classification. The version category compares the running comis CLI version against the daemon’s reported version (read from the gateway.status RPC) and warns on a mismatch — with a stronger message on a major.minor skew. This catches a stale global comis (e.g. an old npm i -g comisai) earlier on PATH than a freshly-built daemon: an out-of-date CLI validates config against an OLD schema and reports phantom failures (a valid key flagged “Unrecognized”). It passes when the versions match and skips cleanly (never fails) when the daemon is unreachable or its status response carries no version field. All checks share one config resolution that mirrors daemon boot: ${VAR} references are substituted from the process environment, then ~/.comis/.env, then the encrypted secret store (offline read). A reference none of them resolves is reported with its config path and var name, and the channel check fails an enabled channel whose credential reference did not resolve — encrypted-store deployments validate cleanly without exporting secrets into the environment. LCD Store checks — when memory.db is present, six read-only scan classes run automatically against the lossless context store: orphaned summaries, dangling context_items references, fallback-marker summaries (quality debt), FTS row-count drift, R4 scope anomalies (NULL tenant/agent keys), and lcd_ingest_cursor over-count (a cursor claiming more ingested messages than the store holds). Output is content-free: findings carry only counts and row ids, never message text. Silently skipped on new installs where memory.db does not exist yet.
--repair flag — LCD store repair actions When --repair is passed, actionable findings are automatically repaired offline (no daemon required). Two LCD finding categories support offline repair: Fallback-marker summaries (fallback=1) are not repairable offline. They are quality debt produced when the LLM summarizer was unavailable — re-summarization requires the running daemon. The daemon re-summarizes fallback-marker summaries automatically during normal compaction as conversations are processed. Raw message history (lcd_messages) is never modified by any repair action — the lossless verbatim raw store is read-only from the repair path.
Doctor repair requires the daemon to be stopped. The repair opens memory.db in read-write mode. If the daemon is running, the repair will return SQLITE_BUSY.Always run comis sessions backup before comis doctor --repair to ensure a recovery point exists before any writes to memory.db.

comis health

Quick view of system health issues. Unlike doctor which shows all checks, health shows only failures and warnings by default. Exits with code 1 when failures exist, making it suitable for CI pipelines.

comis init

Interactive setup wizard for first-time configuration. Supports three modes: interactive wizard (default), non-interactive with flags for CI/Docker, and JSON output for automation.

Key Flags

Provider and Credentials

Gateway

Channels

Media Generation and Processing

The interactive advanced flow includes dedicated steps that offer every supported provider for each media feature: Image Generation (integrations.media.imageGeneration.provider), Video Generation (...videoGeneration.provider), Voice Transcription (...transcription.provider), and Text-to-Speech (...tts.provider). The same selections are available non-interactively via the flags below. (TTS provider setup moved here from the tool-providers step, which is now web-search only, so a key is never requested twice.) Credentials follow the provider-following model: auto (image/video) reuses the agent’s main provider key; openai/google/openrouter/xai/groq reuse OPENAI_API_KEY/GOOGLE_API_KEY/OPENROUTER_API_KEY/XAI_API_KEY/GROQ_API_KEY when the main provider already supplies them; openai-codex (image) uses your Codex OAuth login; and edge (TTS) is free. So the --*-api-key flags are only needed for a dedicated backend (fal, deepgram, elevenlabs) or a cross-provider static-key choice. See Environment Variables for the key reuse rules.

Paths and Behavior

Run comis init --help for the complete list of all flags.

Non-interactive rejection: --provider openai-codex

comis init --non-interactive --provider openai-codex is deliberately rejected because the Codex OAuth flow requires interactive UI (browser callback, device-code prompt, or manual paste). The CLI exits non-zero with the literal error:
openai-codex requires interactive login; run comis init interactively or run comis auth login --provider openai-codex --method device-code separately.
For headless / CI environments, you have three options:
  1. Run comis init interactively (in a TTY).
  2. Run comis init --non-interactive with a different provider, then run comis auth login --method device-code afterwards to add the Codex profile.
  3. Pre-seed the OAuth credential via the OAUTH_OPENAI_CODEX environment variable on the daemon’s first start. The daemon’s env-var bootstrap path is read once when the credential store is empty for that provider; subsequent drift triggers a one-shot WARN.

comis memory

Memory management and recall-diagnostics commands for searching, inspecting, and clearing conversation memory entries, and for inspecting how hybrid recall ranked them.

Subcommands

memory recall-trace, memory observations, memory entities, memory pin, and memory unpin are admin-gated and (tenant, agent)-scoped — they require an admin-scoped gateway token and operate within the caller’s tenant/agent scope.

memory search <query>

memory inspect <id>

Accepts a full id or an id prefix. Resolution scans the 1000 most recent entries (via memory.browse); for older entries use comis memory export.

memory stats

memory stats overlays best-effort recall counters on top of the base memory statistics: lane usage, rerank-fallback rate, consolidation throughput, and recall hit-rate. The overlay is best-effort — a daemon that has not wired the counters (or a non-admin caller) still renders base stats.

memory learning

Outcome-learning telemetry for the verified-learning signal: the coverage (the share of finished trajectories that produced a resolvable outcome), the volume by source, and the success/failure ratio, broken down per agent. Read offline from the local outcome_events ledger in ~/.comis/memory.db (no gateway required). Counts only — no message bodies, no confidence values, no recalled/skill ids.
Outcome capture is per-agent default-off (agents.<id>.learningOutcome.enabled, gated by the master switch memory.enabled). With no recorded events the command prints an honest “no outcome events recorded yet” message naming the config keys to enable, rather than a misleading empty table. The same learning signal is reconstructable per session via comis explain.The wrongness-based forgetting sweep emits its own counts-only trajectory signals — learning:memory_demoted / learning:memory_evicted (soft-eviction counts), a once-per-run learning:lifecycle_swept summary (scanned/promoted/demoted/evicted), and learning:memory_failure_attributed (the corroborated-failure accrual that precedes eviction) — surfaced through comis explain (per session), comis fleet (the daemon-wide memory_lifecycle finding, fleet-wide), and cron.runs jobName "Memory lifecycle" (the per-sweep scanned/evicted/demoted counts). It is part of the one learning layer (agents.<id>.learning.enabled, also under memory.enabled), reads learning.forget.*, and carries no memory body — see Learning. (Recall ranking is the fixed rag.scoring fusion — there is no learned recall weight.)

memory skills

Reflection-engine telemetry for the Verified Learning loop: the learned-doc admission funnel — the total count and the per-state breakdown (candidate / active / stale / archived), broken down per agent, plus the per-doc name·state·proof-count (with confidence and a mutating flag). It also reports the promotion / demotion roll-upPromoted (active) (the count that reached active) and Demoted (stale+archived) (the count that left active), store-wide and per agent — derived from the per-state tally. Read offline from the kind='skill' rows of the local mental_models table in ~/.comis/memory.db (no gateway required). Counts, ids, and closed enums only — never a procedure body, script, or description (the same closed-graph firewall as the trajectory signals). The active count is what the agent can actually use: an active, read-only procedure is surfaced into <available_skills> (default-off), while candidate / stale / archived rows are admitted-but-not-surfaced. See Learning in config-yaml.
The reflection engine is per-agent default-off (agents.<id>.learning.enabled, gated by the master switch memory.enabled). With no learned docs the command prints an honest “no learned skills yet” message naming the config keys to enable, rather than a misleading empty table. Each reflection run emits counts-only trajectory signals — reflect:admitted (the admitted-doc count) and reflect:funnel (the whole funnel: synthesized / validated / admitted / maxClusterCardinality plus a content-free admissionOutcome closed enum — reflectOutcome — so comis explain answers “why was 0 admitted” from one field) — surfaced through comis explain (per session) and comis fleet (fleet-wide); a learned procedure used in a failed/corrected trajectory or a low-capability abstain is named by the learned_skill_failing / synthesis_abstained_low_capability root-cause verdicts (the abstain is benign — Defer ≠ Retry).

memory recall-trace <session>

Inspect the hybrid-recall trace for a session — the per-recall ranking previews recorded by the opt-in diagnostics.recallTrace artifact. Admin-gated and (tenant, agent)-scoped.
The table view shows the correlation keys and the final-count summary (trace, session, finalCount, ts) — enough to locate the right record. The full per-record ranking breakdown (matched lanes, fused order, pre/post-rerank scores, score components, include/exclude reasons) is available via --format json. See the recall runbook.

memory observations

List observation provenance — the consolidated observations and the sources + history behind them. Admin-gated and (tenant, agent)-scoped.

memory entities

List an agent’s entity graph, most-mentioned first. Admin-gated and (tenant, agent)-scoped.

memory clear

At least one filter (--filter or --tenant) is required. This safety check prevents accidental blanket clears.

memory export

Export an agent’s memory to a versioned, secret-scrubbed JSON file. Admin-gated.
The output file uses the comis-memory-export-v1 envelope format. Every content field is run through the secret scrubber before being written — secrets matching known patterns (API keys, tokens) are replaced with [REDACTED]. The file is written with permissions 0600 (owner read/write only). The daemon returns the scrubbed payload over RPC; the CLI writes the file locally. This design means memory export works correctly even when the daemon runs under node --permission (which disables fd-based filesystem APIs on the daemon side).

memory import

Import memory from a comis-memory-export-v1 JSON envelope into a target agent scope. Admin-gated.
The CLI validates the envelope’s schemaVersion before sending anything to the daemon. Files with a mismatched or missing schemaVersion are rejected with a non-zero exit code (fail-closed: no data is sent to the daemon). Every entry is routed through the memory-poisoning firewall on the daemon:
  • CRITICAL entries (secret-bearing content) are blocked — not persisted, counted in blocked.
  • WARN entries (jailbreak-pattern content) are stored at downgraded external trust with a security-tainted tag, counted in downgraded.
  • Clean entries are stored at learned trust (capped — system trust cannot be imported).
Import re-stamps tenantId and agentId to the target scope. The envelope’s scope field is metadata only and is never used for writes.

memory pin <id>

Mark a memory entry as always-injected in recall — the entry is prepended to every recall result regardless of fused score. Admin-gated.
Idempotent: pinning an already-pinned entry is a no-op. The entry must exist in the target (tenant, agent) scope.

memory unpin <id>

Remove the always-inject pin from a memory entry. Admin-gated.
Idempotent: unpinning an already-unpinned entry is a no-op.
Recall-trace records only exist when diagnostics.recallTrace.enabled is on (opt-in, default off). See the recall runbook for the enable-and-read workflow.

comis models

Model management commands for browsing the model catalog and updating agent model assignments. The catalog is sourced live from the @earendil-works/pi-ai SDK — adding a new provider/model to pi-ai automatically surfaces it here with no Comis update required. When the daemon is running, the commands fetch via the models.list RPC. When the daemon is stopped (e.g., during comis init), they fall back to the local pi-ai static catalog so the wizard works pre-init.

Subcommands

models list

models set <agent> <model>


comis mcp

Manage Model Context Protocol (MCP) server connections. Connect, disconnect, probe, and authenticate MCP servers; inspect their exposed tools and capabilities. All subcommands accept --token <token> to override COMIS_GATEWAY_TOKEN.

Subcommands


comis providers

Provider management commands. Like comis models, the provider list is sourced live from the pi-ai SDK catalog (getProviders() via the models.list_providers RPC), with local fallback when the daemon isn’t running.

Subcommands

providers list

The output includes three columns:
  • Provider — pi-ai provider id (e.g., anthropic, openrouter, groq)
  • Models — count of models the provider exposes in the catalog
  • Statusconfigured (canonical env key resolved), missing key (provider listed but key not in env), or keyless (local providers like ollama / lm-studio that don’t need a key)
To switch an agent to a different provider, use comis agent configure with --provider <id> --model <id>.

comis pm2

Manage the daemon via pm2 as a cross-platform process supervisor (alternative to systemd on macOS and WSL).

Subcommands

pm2 setup

pm2 logs

The other pm2 subcommands (start, stop, restart, status) have no additional flags.

comis reset

Reset sessions, configuration, or the entire workspace. Requires explicit confirmation for all destructive operations.
  • sessions — Deletes all conversation sessions (tries RPC first, falls back to direct database removal)
  • config — Removes config.yaml and .env from the config directory
  • workspace — Removes the entire data directory and config files
The workspace target deletes all data including sessions, memory, logs, and configuration. This cannot be undone.

comis secrets

Encrypted secret management using AES-256-GCM. All operations work offline without a running daemon.

Subcommands

secrets init

secrets set <name>

secrets get <name>

secrets list

secrets delete <name>

secrets import

secrets audit


comis security

Security audit and auto-remediation tools. Runs all check categories covering configuration validation, file permissions, secret exposure, gateway hardening, and more.

Subcommands

security audit

security fix

security audit-log

Query the durable security-decision audit log — the obs_audit_events SQLite table populated by the daemon (secret access, injection detection, command blocks, canary leaks, sandbox-downgrade refusals, and classified audit:event actions). Admin-scoped (the operator gateway token carries admin scope); rows are content-free (ids, closed-enum fields, a scrubbed refs blob — never a secret value). Reads the same events surfaced by the obs.audit.query RPC and the obs_query {action:"audit"} agent tool.

comis sessions

Session management commands for listing, inspecting, and deleting conversation sessions.

Subcommands

sessions list

sessions inspect <key>

sessions delete <key>

sessions reset <sessionKey>

Complete conversation reset — clears both the LCD durable history and the daemon sessionStore working transcript. This operation is irreversible. Admin-gated — requires an admin token. After a reset, the model starts with a clean slate for this session in both dag and pipeline modes: the LCD store is empty (dag mode) and the sessionStore messages are empty (both modes — the JSONL-backed transcript that feeds state.messages on the next turn is wiped). This is the correct command when you want the model to explicitly forget a conversation. Deleting the session JSONL file alone (sessions delete) does not clear the LCD store, so the model would continue with its LCD history intact.
Use sessions reset (not sessions delete) when you want the model to explicitly forget a conversation. Deletion only removes the session record; the LCD store keeps its full history and the model would resume from it. See Data Directory for the full durability model.
--memory is the explicit delete side of the LCD→LTM loop. There is also an automatic, non-deleting complement on the recall side: when a conversation’s condensed LCD summary has been distilled into a learned memory, recall down-weights (it does not remove) the overlapping paired-conversation memories from that same session, so the distilled summary and its own source rows do not double-count. That down-weighting needs no flag and never deletes anything — --memory / --purge-derived remain the only commands that actually remove memories.

sessions backup

Creates a timestamped copy of memory.db using the SQLite Online Backup API. Safe to run while the daemon is running — the backup API handles concurrent writes atomically. The backup file is placed in the same directory as memory.db with restricted permissions (0600).
The backup filename includes an ISO timestamp: memory.db.backup.20260610T120000000Z.
Run comis sessions backup before schema migrations or destructive operations. To restore, copy the backup file back to memory.db while the daemon is stopped.

comis signal-setup

Interactive setup wizard for installing and configuring Signal CLI. Guides through Java prerequisite checks, signal-cli installation from GitHub releases, phone number registration with SMS or voice verification, and a test message. No flags. Requires an interactive terminal (TTY).

comis trace

Search, tail, and export agent execution traces. Traces capture the inbound message pipeline and are stored in the durable trace store. Backed by ObsTraceSearchContract, ObsTraceTailContract, and ObsTraceExportContract. Primarily flag-driven; use --chat with --tail to stream live events.

Flags

Subcommands


comis cost export

Export the corrected-cost time buckets as CSV (default) or JSON, honoring agent / provider / model / time-window filters. Each row carries the cost rollup — totalCost, totalTokens, callCount, totalCacheSaved, totalCostCorrection (the SDK-vs-corrected adjustment) — plus a pricing-coverage column so a finance review sees how trustworthy the dollars are: pricingState (the bucket’s dominant three-state signal — priced / free / unknown) and missingPricingCount (how many calls in the bucket had an unknown or unset pricing state, i.e. dollars not catalog-backed). The export is content-free — ids, counts, dollars, and the pricing enum only, never a message body, secret, or query. Buckets are hourly by default; --quarter-hour switches to 15-minute buckets (the four quarter-hour buckets inside an hour always sum back to that hour’s total). The command reads the local ~/.comis observability store directly (the telemetry lives on disk) — it contacts no daemon and needs no gateway token. A missing or unreadable store yields an empty export (an honest empty, never a fabricated zero-cost row).

comis explain

Assemble an IncidentReport for a single agent session — a bounded, causal post-mortem (outcome, cost, per-tool stats, normalized failures, circuit-breaker timeline, large-result offloads, recall health — including degraded/failed recall lanes — session-wide activity-finalize tallies, and a deterministic likely root cause). The report is derived from log evidence only; no LLM is invoked, so the same session always yields the same verdict. Backed by ObsExplainContract (obs.explain). The positional argument is routed automatically by shape: a root- run id (an autonomy run’s rootRunId — the synthetic in-process root-session-<sessionKey> or a real spawned-run lease id; checked first because a synthetic root contains : but must route as a run id, not a session key) is passed as rootRunId; otherwise a session key (tenant:user:channel:ts, contains :) is passed as sessionKey; otherwise a trace ID (a UUID, no :) is passed as traceId. The daemon canonicalizes a trace ID or a rootRunId to its session key first, so all three forms produce the identical report. A rootRunId is the comis fleetcomis explain drill-down target: paste the worstRootRunId that comis fleet’s autonomy block names straight in, and the report renders that run’s spawnTree (an unresolvable id surfaces an honest session_not_found, never a clean-looking empty report). When the session included a transcription or synthesis, the report carries a voice block reconstructing that turn: the provider, whether it ran keyless, the model, durationMs, the outcome, the resolved selection source rung (with the onSkip reasons when auto skipped a higher rung), the costUsd (0 for a keyless turn; omitted for a keyed turn — the audio ports return no per-call cost), and the failure errorKind. Content-free — ids, labels, and numbers only, never the audio or transcript text. See Voice → Observability and cost. When the session made capability-gated calls (an autonomous agent, or one that spawned children via orchestrate), the report carries a spawnTree block reconstructing the run’s authorization topology — the one call to root-cause an unattended run. Each node is a leaseId (the in-process root groups under its synthetic rootRunId) and surfaces its parentLeaseId (the child→root edge; absent on the root), the agentId, the attenuated caps it held, the tool NAMES it invoked (toolsInvoked), and any capability it was DENIED (denials — each a CapabilityDeniedError cap). In the table view a node renders as <leaseId> ←<parent> caps=[…] tools=[…] DENIED=[…]; --format json emits the full spawnTree array. The tree is reconstructed offline from the session’s capability.audited trajectory records, so --offline shows it too. Multi-agent graph (DAG) nodes appear as leaves too: a graph node spawns in-process and never crosses the socket chokepoint that emits capability.audited, so each node also emits a graph.node_spawned record — reconstructed as a leaf keyed by <graphId>:<nodeId> (its stable node identity, not a socket lease), nested under the graph root via parentLeaseId, carrying its child agentId and caps:["orch:graph"] (without this the spawn-tree showed only the root while N graph children ran). Content-free — ids, caps, tool NAMES, and decision-derived denials only, never a tool argument, a message body, or a secret. Per-node remaining budget is not on the offline tree (it is live daemon state) — use comis whoami for an in-flight run’s remaining budget; the spawn tree is the post-mortem topology. When a scheduled job’s wake-gate woke the model, the report folds a content-free cronWakeGate fact for that fire — the job id, the wake verdict, the gate’s durationMs, its tool-call count, and the model turns it saved (ids and counts only, never the gate’s gathered finding). A skipped fire is different: it runs no model and opens no session, so it never reaches comis explain. The argument is a session key, trace ID, or rootRunIdnever a cron job id. To inspect a job’s skipped fires read its run history (cron.runs), and for the cross-job skip-rate and turns-saved use comis fleet.
Requires an admin-scoped gateway token (same as comis trace) — unless --offline. When the gateway is unreachable (daemon down / wrong port) the command automatically falls back to the offline assembly with a notice. When the gateway rejects the token it does NOT silently fall back — the daemon is demonstrably running, so the error names COMIS_GATEWAY_TOKEN (env var or ~/.comis/.env) and suggests --offline as the explicit out.

comis orchestrate

Operator-only deterministic replay of an orchestrate run. Available only when autonomy.durability.orchestrateResume was on for the run (so the pinned script + its recorded results survived).
replay re-runs the run’s pinned <runId>.<language> script bytes against a separate, operator-invoked replay socket that serves the run’s recorded cap-call results (from results/replay.jsonl) back in order — so the same pinned script + recorded responses produce byte-identical stdout. The caller supplies only the runId; no script bytes are ever accepted (the pinned bytes are the sole source). The replay socket is a physically separate Unix socket that speaks the capability wire but can only serve recorded results — it is never a mode of the production capability endpoint, so a replay cannot perform a live side effect. Backed by OrchestrateReplayContract (orchestrate.replay).
orchestrate.replay is admin-scoped and deny-by-origin: an agent-origin call is rejected before the handler runs (an agent can never replay a run), and it requires an admin-scoped gateway token. An unknown runId, or a run with no pinned script (durability was off, or its checkpoint was reclaimed), returns a content-free error naming the failure class — never the runId.

comis fleet

Assemble a FleetHealthReport — a bounded, cross-session triage over a recent window: the session count and degradation rate, the degraded-by-cause breakdown (degraded sessions bucketed by named endReason cause, e.g. context_exhausted / output_starved), the recurring WARN findings (recurring lcd_divergence / model / config-posture signals, counts + hints only), the total circuit-breaker trips, the window cost, and a deterministic likely root cause. Like comis explain, the report is derived from log + diagnostics evidence only; no LLM is invoked, so the same window yields the same verdict. Backed by ObsFleetHealthContract (obs.fleet.health). cost.costUsd is the per-session spend (the session-summary rollup). cost.offSessionUsd is the background-job spend in the window — reflection/learning cron runs et al. that key their token usage to a synthetic __PREFIX__ session with no session_summary, so their cost is absent from costUsd. The two are distinct and never double-count: the operator’s full provider bill is costUsd + offSessionUsd. The table view appends + $Z off-session (reflection/background) only when it is non-zero. This closes a reconciliation gap where reflection spend hit the provider console but was invisible to the fleet lens. The table view prints the degraded-by-cause spread as a Degraded by cause: context_exhausted=13, output_starved=9 line (sorted highest-count first; counts + closed-set labels only, no raw bodies) — omitted when no degraded session carried a named cause. The full per-cause map is also on the --format json output (degradedByCause). A health_signal:cache_prefix_churn finding fires when a session’s Anthropic prompt-cache prefix collapsed on a recurring basis — a cached-region message mutated across turns on 3+ calls within a recent window, wasting the cache write (up to hundreds of thousands of cache-creation tokens over a session). The finding names the recurring mutation class (structural-shift = a message index shifted below the cache fence, e.g. from LCD condense/re-admit; datetime-preamble / thinking-cleared / content-cleared = an in-place edit). This closes a fleet-blindness gap where the churn was visible only as a daemon.log Unstable prefix detected WARN. Counts + the closed class label only — never message text. A dedicated config_posture:served_below_configured finding fires when an Ollama provider serves a smaller num_ctx than the configured contextWindow — the agents on that provider silently run with the smaller window. The count is the number of providers affected at the latest boot (posture is standing state, not cumulative — a healthy restart clears it). Fix by raising the served window (OLLAMA_CONTEXT_LENGTH=<configured> ollama serve, or Modelfile PARAMETER num_ctx <configured> — see the served-window section for the VRAM caveat), then run comis explain on a served-bound session for the numbers-backed budget verdict — and see the Local models playbook for the full local-deployment triage map. A dedicated config_posture:media_credential_gap finding fires when a configured media pipeline (imageGeneration / transcription / tts / videoGeneration) has a pinned provider whose credential is absent — the pipeline authenticates fine for the main completion path but fails at first media use, so it was previously invisible to the fleet lens (you had to hit it or grep the log). The count is the number of affected pipelines at the latest boot (standing state; a login/key + restart clears it). Fix by setting the provider’s credential (OPENAI_API_KEY / GOOGLE_API_KEY / FAL_KEY / …), logging in for openai-codex (comis auth login --provider openai-codex), or switching the pipeline’s provider to one whose credential is present (or auto to follow the main provider). Counts only — never a provider name or credential value. A dedicated config_posture:unresolved_model finding fires when one or more agents are configured with a model id that does not resolve in the model catalog for its provider (and is not an operator-declared custom model under providers.entries.<provider>.models) — e.g. openai-codex: gpt-5.6 when the real ids are gpt-5.6-terra / -luna / -sol. An unresolved model collapses the whole model profile to the fail-closed nano profile (an ~8K-token window), so every non-trivial turn context-exhausts — and neither the chimeric_model nor the pricing_gap finding catches it (a non-native provider resolves "free" for an unknown model, and the model family still parses). The count is the number of affected agents at the latest boot (standing state; a valid model id + restart clears it). Fix by setting agents.<id>.model to a valid catalog id for its provider — the daemon.log unresolved-model WARN names the provider’s available ids — or declare the model under providers.entries.<provider>.models. Counts only — never a model id in the finding body. A voice_health finding fires when transcriptions/syntheses degraded across the window: the count of degraded STT/TTS turns and the dominant voice errorKind (e.g. model_load_failed → check the local whisper model cache, auth_required → set the provider’s audio key). Counts + a closed errorKind label + a static hint only — no raw provider body or secret, safe to paste. Drill into the worst voice session it points at with comis explain. See Voice → Observability and cost. The acute-degradation root cause (fleet_acute_degradation) names the worst degraded session’s key inline (worst: <sessionKey>) and in its suggested next step (comis explain <sessionKey>), so you paste it straight in rather than hunting for “the worst session”. When the daemon runs unattended/durable runs, the report carries an autonomy block: the cross-run run count + degraded rate (sourced from the crash-surviving durable_runs table, so it survives a hard crash), the orphaned / resumed / revoked / killed counts (killed is separable from revoked because a hard run.kill and a cooperative lease.revoke emit distinct events), the autonomy breaker-trip + budget-breach counts, the window cost, and the worst degraded run’s worstRootRunId (orphaned > killed > revoked) so you can paste it straight into comis explain for its spawn-tree. The orphaned/revoked/killed counts also surface as dedicated durable_orphaned / autonomy_revoked / autonomy_killed findings, and a degraded autonomy run drives the top-ranked fleet_autonomy_degradation root cause. Counts + the worst run’s id only — never a lease bearer, an orphan-reason body, or a secret; safe to paste. The block is absent under --offline (the daemon-less CLI has no durable-store edge) or on a non-durability boot — an honest coverage degradation, not a silent zero. When cron jobs ran a wake-gate over the window, the report carries a cronWakeGate efficiency block and a cron_wake_gate_efficiency finding: the gated-fire total, the skip count and skip-rate, the fail-open count and rate, the model turns saved, and the gate’s own tool-call cost, rolled up per agent (counts and agent ids only — never the gate’s gathered findings or script, safe to paste). A high skip-rate is usually the gate working (most polls found nothing), not a fault; the three signals worth inspecting are a 100% skip-rate on a monitor you expect to fire, a high fail-open rate (the gate crashed/timed-out/over-capped every fire — it saves nothing and costs its own run, yet reads skipRate 0 like a busy monitor), and tool calls exceeding turns saved (a gate that costs more than it saves). Because a skip opens no session, this is the cross-job lens for it — pair it with cron.runs jobName "<job>" for a single job’s per-fire decisions. This is the cross-session sibling of comis explain (single-session). It is a remote admin RPC over the operator’s gateway token — DISTINCT from comis health, the local daemon doctor that runs offline checks with no RPC. (comis health is unaffected; this is a separate command.)
Requires an admin-scoped gateway token (same as comis explain / comis trace) — unless --offline. Unreachable-gateway calls fall back to the offline assembly automatically; a token rejection surfaces the auth error (with the --offline tip) instead of masking the misconfiguration.

comis messages

Extract the inbound messages users typed, per channel, from the local session logs — the conversation-export lens. It reads the raw session .jsonl message logs under every agent workspace tree (<dataDir>/workspace[-<agentId>]/sessions/...) and parses each user turn’s inbound envelope ([<channelType>] <senderId> (<time>):), so one command answers “what did users send us on Telegram yesterday” without hand-joining session files. The command runs fully offline and has no RPC sibling — on purpose. Unlike comis explain / comis fleet (bounded digests, counts + hints only), its output is content-bearing: message bodies are the payload. The obs network surfaces stay content-free, so this read never crosses the gateway — it only reads local files the operator already owns. Internal dispatch turns are excluded by default and counted, not hidden: cron, sub-agent, and heartbeat sessions plus the reserved system sender are agent-/scheduler-authored prompts, not humans — a summary note reports how many were excluded, and --include-internal shows them tagged origin: "internal". Two more honest-coverage notes: user turns with no parsable envelope (e.g. envelope.showProvider: false produces headerless turns) are counted instead of silently dropped, and when --limit cuts the result the latest N are kept and the truncation is announced. <when> values for --since / --until accept epoch ms (1783900800000), relative-ago (30m, 24h, 7d), or an ISO date/datetime (2026-07-12, 2026-07-12T10:00:00Z). An unparsable bound is an error naming the accepted forms — never a silent widen-to-everything. --date is one-UTC-day sugar and cannot be combined with --since/--until.
Reads COMIS_DATA_DIR (falling back to ~/.comis) like the other offline commands — run it as the daemon’s user, or point COMIS_DATA_DIR at the daemon’s data dir.

comis support-bundle

Assemble a paste-ready support bundle — a single directory to hand to a maintainer or attach to a bug report. Each run writes up to nine content-free files (plus a trace-exports/ directory with --deep) under <dataDir>/support-bundles/comis-support-<ts>/: a machine-readable triage verdict, the full health-check findings, a human-readable issue summary, an AI-fillable issue draft, a cross-session fleet health digest, a config-posture digest (config section membership only), a window-scoped audit-summary digest, and a manifest carrying the redaction fingerprint. Focusing on one session with --session adds a per-session explain.json incident digest; --deep additionally embeds that session’s redacted trace bundle under trace-exports/. The fleet digest, config-posture digest, and audit-summary digest are each omitted when their source cannot be read — a thrown fleet assembly, an unparseable config, or an absent observability store — and the manifest records the gap. The verdict is derived from the local health checks only — no LLM is invoked, so the same install state always yields the same verdict. The command runs fully offline: it composes the same checks as comis doctor against a stopped daemon, folds them into the triage verdict, and writes the bundle without contacting the gateway. A degraded or partial triage is still a written bundle — the degraded verdict is the content, not a failure — so the command exits 0 on any bundle it manages to write. It is safe to run the moment something looks wrong. The bundle is content-free by construction: it excludes secrets, message bodies, and raw config values, and the directory name carries a timestamp only (no hostname). Every file is written under an owner-only (0o700) directory with 0o600 file modes through a symlink-refusing safe writer. Output files — written under comis-support-<ts>/:
In table format the command also prints the copy-paste commands to compress the bundle and open an issue from the summary — you run them; the command never does:
When you did not pass --session and a best-effort local scan finds a recently-degraded session, the table view also prints a tip naming that session, so you can re-run with --session <key> --deep for a focused per-session bundle. Exit codes:
Privacy noticeThe bundle excludes secrets, message bodies, and raw config values by construction, and a redaction backstop masks value-shaped strings and substitutes absolute paths before anything reaches disk. Redaction is heuristic, though — always treat the bundle as sensitive: share it only with authorized engineers over a secure channel, and delete it after triage.With --deep, the bundle additionally embeds the redacted raw session trajectory (session content, PII-adjacent) under trace-exports/ — strictly more sensitive than the default digest, and the command’s printed notice escalates to match. Handle a --deep bundle with the same care as a comis trace export.
No daemon required. The bundle is assembled entirely from the local ~/.comis files and the offline health checks, so it works when the daemon is stopped or crash-looping — exactly when you most need it. When the daemon happens to be running, the bundle also records its build version; otherwise that field is simply absent.

comis whoami

Show the run’s resolved orchestration capabilities plus the remaining per-root budget and outward quota — “what can I do, and how much budget is left”. Backed by CapabilitiesIntrospectContract (capabilities.introspect, rpc scope — read-only, no capability required). The read is self-scoped: the daemon resolves the caller from the connection, so an operator token sees the default agent’s posture and an in-process agent sees its OWN (never another agent’s — the request carries no agentId). The Caps: line is the resolved autonomy-profile capabilities (orch:* strings). When the run has an in-flight root (an active agent turn / spawn tree) the Budget: line shows the remaining tokensRemaining · wallClockMsRemaining$usdRemaining when the model is priced, $ uncountable for a zero-price subscription/Codex model — the token and wall-clock limbs are authoritative regardless), and the Outward: line the remaining per-hour outward-send allowance. With no live root (an idle agent, pre-spawn) the budget/quota lines are omitted rather than shown as zero. comis whoami is LIVE-only. Unlike comis explain / comis fleet there is no --offline mode: the remaining budget/quota lives only in the running daemon’s in-memory autonomy state, never on disk, so there is nothing to reconstruct offline. When the daemon is unreachable (or rejects the token) the command fails clearly with a non-zero exit — it never fabricates an empty/zero snapshot (a false posture). Use comis explain’s spawnTree for the post-mortem authorization topology of a finished run; whoami is for an in-flight run’s remaining budget.
Requires a gateway token (the rpc scope — any valid token works; no admin scope needed, unlike comis explain / comis fleet). There is no offline fallback — the daemon must be running.

comis status

Display a unified system status overview showing daemon, gateway, channel, and agent information. Connects to the daemon via RPC for live data and handles daemon offline gracefully.

comis uninstall

Remove Comis from the host. Re-invokes install.sh --uninstall (downloaded from comis.ai) so the installer remains the single source of truth for everything that may have been created during install.
The uninstaller cleans up everything the installer wrote: the main comis.service systemd unit, the optional comis-xvfb.service companion (if --with-xvfb was used), the CLI binary, and — when --purge is set — the data dir, the CloakBrowser binary cache (which can be 200–500 MB depending on cached versions), and the COMIS_EGRESS egress-logging iptables chain (the uid-scoped OUTPUT jump is unhooked before the chain is flushed and deleted). On --remove-user the comis system user is dropped too. Sudoers rules and the AppArmor profile are left in place; remove those manually if you’re decommissioning the host.

Exit Codes

Commands that detect problems (doctor, health, security audit) exit with code 1 when failures or critical findings are present, making them suitable for CI pipeline gating.

Config YAML

Configuration file reference

Environment Variables

Environment variable reference

Operations Overview

Deployment and management guides

Secrets Management

Managing API keys and passwords