factor

module
v0.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 15, 2026 License: MIT

README ΒΆ

Factor logo

Factor

CI Coverage Go reference Latest release MIT license

A fast, reliable, lightweight desktop AI agent and companion β€” with a real memory.

Factor is a single static Go binary that lives on your machine, talks to you over the CLI or Telegram, does real work with real tools, and remembers what matters across conversations. Its long-term memory is smrti β€” Bayesian truth values, attention economics, emotional valence β€” so Factor doesn't just log what you said: it consolidates, prioritizes, and never repeats a critical mistake.

Highlights

🧠 Memory as the soul Salience-ranked recall every turn; past failures become hard constraints; consolidation decays, promotes, and prunes β€” plus deliberate remember / recall / forget / reflect tools
⚑ Never keeps you waiting Long work runs as background jobs β€” Factor acks instantly and proactively messages you when the result lands, even mid-conversation
🎯 Mid-turn steering A second message during a live turn is injected between tool iterations instead of queuing
πŸ” Provider failover that works OpenAI-compatible (OpenRouter, Ollama, LM Studio, Groq, llama.cpp, …) and native Anthropic, with error classification, per-candidate cooldowns, and overflow-triggered compaction
🧭 Reasoning, dialect-translated One provider.reasoning setting becomes reasoning (OpenRouter), reasoning_effort (OpenAI/Groq), or a thinking budget (Anthropic)
πŸ–οΈ Hands on your desktop Windows, screenshots, mouse, keyboard, clipboard, notifications β€” X11, Wayland, macOS, Windows; auto-registered when a display exists
🌐 A real browser, not just fetch CDP tools attach to your running Chrome/Chromium/Brave or launch a managed instance β€” visible by default so you can watch it work
🧩 Extensible everything Channel connectors, Go tools, runtime-mounted MCP servers, markdown skills, drop-in instructions β€” see Extending
πŸ”§ Self-managing Edits its own config, installs packages (apt/dnf/pip/npm/…), runs cron schedules and HEARTBEAT.md checks that cost zero LLM calls when idle
πŸ›‘οΈ Safety rails Workspace-restricted files, exec deny-patterns, sender allowlists, secrets scrubbed from every tool result β€” rails, not a sandbox (Security)

How it works

flowchart LR
    TG([Telegram]) <--> BUS
    CLI([CLI]) <--> BUS
    BUS[message bus] --> LOOP["agent loop Β· one live turn per session"]
    LOOP <-->|recall Β· store| MEM[("smrti REST sidecar")]
    LOOP --> PROV["provider chain Β· failover Β· cooldowns Β· compaction"]
    LOOP --> REG[tool registry]
    REG --- SUITES["fs Β· exec Β· web Β· browser Β· desktop Β· memory Β· jobs Β· cron Β· config Β· pkg Β· skills Β· MCP"]
    BG[jobs Β· cron Β· heartbeat] -.->|proactive results| BUS

Bus + bounded workers, mid-turn steering, narrow pluggable seams, CGO-free portability β€” the architecture of PicoClaw distilled into a codebase that runs happily on an old Puppy Linux box.

Get started

go install github.com/cyqlelabs/factor/cmd/factor@latest
# or grab a release binary; linux-amd64 targets GOAMD64=v1 (no SSE4.2 needed)

factor init      # interactive setup wizard

The wizard verifies every step live β€” the provider with a real completion, the model picked from the endpoint's live list, the Telegram token with getMe β€” and installs smrti if it's missing (uv β†’ pipx β†’ pip --user β†’ private venv; no root needed). factor init -y takes the defaults for scripting; --no-install keeps it from installing anything.

export FACTOR_PROVIDER_API_KEY=sk-or-...   # OpenRouter by default
factor                                     # interactive chat
factor -m "what's on my disk?"             # one-shot
factor gateway                             # daemon: Telegram, cron, heartbeat, jobs
factor status                              # daemon / provider / memory health

Factor spawns and supervises the smrti sidecar automatically, restarts it with backoff, and degrades gracefully (empty recalls, dropped writes) when it's down. Point memory.mode: "external" + memory.url at a shared smrti if you run one.

Configuration

~/.factor/config.json β€” every key optional, defaults work. FACTOR_* env overrides: FACTOR_PROVIDER_API_KEY, FACTOR_PROVIDER_MODEL, FACTOR_MEMORY_MODE, …

Annotated example
{
  "provider": {
    "type": "openrouter",                    // openrouter|openai|groq|ollama|lmstudio|llamacpp|anthropic|custom
    "api_key": "sk-or-...",
    "model": "google/gemini-3.1-pro-preview",
    "reasoning": { "effort": "xhigh" },      // or {"max_tokens": 12000}; "none" turns it off
    "fallbacks": [{ "type": "ollama", "model": "qwen3:8b" }]
  },
  "memory": {
    "mode": "sidecar",                       // sidecar | external | off
    "auto_install": true,                    // install smrti when it is missing
    "personality": "balanced",               // analytical | curious | empathetic | maverick | deterministic
    "space": "main"
  },
  "channels": {
    "telegram": { "token": "123:ABC", "allow_from": ["your-telegram-id"] }
  },
  "mcp": {
    "servers": { "github": { "command": "github-mcp-server", "args": ["stdio"] } }
  },
  "tools": { "disabled": [], "restrict_to_workspace": true },
  "desktop": { "enabled": null },            // null = on when a display exists
  "browser": { "enabled": true, "headless": false },
  "heartbeat": { "enabled": true, "interval_minutes": 30 }
}

The workspace (~/.factor/workspace) is the agent's home: AGENT.md, SOUL.md, USER.md shape its identity; HEARTBEAT.md lists proactive tasks; instructions/, skills/, sessions/, cron/ do what they say.

Extending Factor

Seam What it takes
Connector One package: channel.Register(name, factory) in init(), with its own config section
Tool Four methods β€” Name, Description, Parameters, Execute β€” and one registry.Register(t) line
MCP server mcp_add (or the mcp.servers config section) mounts its tools at runtime β€” no Go required
Skill Drop workspace/skills/<name>/SKILL.md β€” catalog in prompt, full text on demand, skill_install from git
Wiring in a connector
func init() {
    channel.Register("mychat", func(raw json.RawMessage, b *bus.MessageBus) (channel.Channel, error) {
        var cfg MyConfig
        _ = json.Unmarshal(raw, &cfg)          // your own config section
        return New(cfg, b), nil                // implement Name/Start/Stop/Send/MaxMessageLength
    })
}

Security model

Factor is a personal agent, not a multi-tenant service. The guardrails (workspace restriction, exec deny-patterns, allowlists, secret redaction) protect against accidents and casual prompt-injection β€” they are not a security boundary. Run it under your own account for yourself; set channels.telegram.allow_from; keep restrict_to_workspace on unless you know why you're turning it off.

Development

make check        # gofmt + vet + race tests + coverage gate (β‰₯90%, what CI runs)
make build        # local binary
make build-all    # release cross-compile (incl. GOAMD64=v1 for old x86-64)
make build-tiny   # -tags nobrowser: smallest binary

The suite runs against fakes β€” scripted providers, a fake smrti sidecar (spawned by re-execing the test binary), a fake Telegram API, a fake MCP server over real stdio JSON-RPC, a scripted desktop β€” plus live headless-Chrome and desktop round-trip tests that auto-skip where the machine can't host them.

License

MIT Β© CyqleLabs

Directories ΒΆ

Path Synopsis
cmd
factor command
Command factor is a fast, reliable desktop AI agent and companion with smrti long-term memory.
Command factor is a fast, reliable desktop AI agent and companion with smrti long-term memory.
internal
agent
Package agent implements the turn loop: one live turn per session, mid-turn steering for overflow messages, bounded worker concurrency, and a system prompt assembled from identity, workspace bootstrap files, drop-in instructions, the skills catalog, and smrti memory recall.
Package agent implements the turn loop: one live turn per session, mid-turn steering for overflow messages, bounded worker concurrency, and a system prompt assembled from identity, workspace bootstrap files, drop-in instructions, the skills catalog, and smrti memory recall.
app
Package app is the composition root: it wires config, provider chain, smrti memory, tools, skills, sessions, and the agent loop into one unit shared by the CLI and the gateway daemon.
Package app is the composition root: it wires config, provider chain, smrti memory, tools, skills, sessions, and the agent loop into one unit shared by the CLI and the gateway daemon.
browser
Package browser gives the agent a real browser via the Chrome DevTools Protocol (chromedp): it attaches to the user's running Chrome/Chromium/ Brave when a DevTools port is open, otherwise launches a managed instance β€” visible by default, so the user can watch the agent work.
Package browser gives the agent a real browser via the Chrome DevTools Protocol (chromedp): it attaches to the user's running Chrome/Chromium/ Brave when a DevTools port is open, otherwise launches a managed instance β€” visible by default, so the user can watch the agent work.
bus
Package bus decouples channels from the agent loop with bounded queues.
Package bus decouples channels from the agent loop with bounded queues.
channel
Package channel defines the connector seam.
Package channel defines the connector seam.
channel/telegram
Package telegram is the reference connector: raw Bot API over HTTP long-polling, no SDK.
Package telegram is the reference connector: raw Bot API over HTTP long-polling, no SDK.
config
Package config loads, defaults, persists, and redacts Factor's configuration.
Package config loads, defaults, persists, and redacts Factor's configuration.
cron
Package cron schedules recurring agent tasks.
Package cron schedules recurring agent tasks.
desktop
Package desktop gives the agent hands on the graphical session: listing and controlling windows, taking screenshots, moving the mouse, typing, the clipboard, notifications, and opening files or URLs.
Package desktop gives the agent hands on the graphical session: listing and controlling windows, taking screenshots, moving the mouse, typing, the clipboard, notifications, and opening files or URLs.
gateway
Package gateway runs Factor as a daemon: channels, cron, heartbeat, background jobs, and a local health endpoint.
Package gateway runs Factor as a daemon: channels, cron, heartbeat, background jobs, and a local health endpoint.
heartbeat
Package heartbeat periodically checks HEARTBEAT.md for user-defined tasks.
Package heartbeat periodically checks HEARTBEAT.md for user-defined tasks.
jobs
Package jobs runs long work in the background so the agent can reply immediately and report back when the work finishes.
Package jobs runs long work in the background so the agent can reply immediately and report back when the work finishes.
mcp
Package mcp implements a minimal MCP client (JSON-RPC 2.0 over stdio, newline-delimited) so external MCP servers' tools mount directly into Factor's tool registry as <server>__<tool>.
Package mcp implements a minimal MCP client (JSON-RPC 2.0 over stdio, newline-delimited) so external MCP servers' tools mount directly into Factor's tool registry as <server>__<tool>.
memory
Package memory is the soul of Factor: long-term memory backed by smrti (github.com/cyqlelabs/smrti), an AtomSpace-inspired engine with Bayesian truth values, attention economics, and emotional valence, reached over a localhost REST sidecar.
Package memory is the soul of Factor: long-term memory backed by smrti (github.com/cyqlelabs/smrti), an AtomSpace-inspired engine with Bayesian truth values, attention economics, and emotional valence, reached over a localhost REST sidecar.
provider
Package provider defines the LLM provider seam: a minimal Chat interface, wire-format adapters (OpenAI-compatible, Anthropic), error classification, and a failover chain with per-candidate cooldowns.
Package provider defines the LLM provider seam: a minimal Chat interface, wire-format adapters (OpenAI-compatible, Anthropic), error classification, and a failover chain with per-candidate cooldowns.
session
Package session persists conversation history as append-only JSONL files with a meta sidecar carrying the summary and logical truncation offset.
Package session persists conversation history as append-only JSONL files with a meta sidecar carrying the summary and logical truncation offset.
skills
Package skills implements progressive-disclosure markdown skills: only name + description enter the system prompt; the model reads the full SKILL.md with read_file when it decides a skill applies.
Package skills implements progressive-disclosure markdown skills: only name + description enter the system prompt; the model reads the full SKILL.md with read_file when it decides a skill applies.
tools
Package tools defines the tool seam and the built-in arsenal.
Package tools defines the tool seam and the built-in arsenal.
tui
Package tui draws Factor's interactive chat.
Package tui draws Factor's interactive chat.
version
Package version holds build metadata injected at link time.
Package version holds build metadata injected at link time.
wizard
Package wizard is Factor's interactive setup: a terminal front-end for `factor init` that picks a provider and model (probing them live), installs the smrti memory engine, wires up channels, and checks the desktop tools.
Package wizard is Factor's interactive setup: a terminal front-end for `factor init` that picks a provider and model (probing them live), installs the smrti memory engine, wires up channels, and checks the desktop tools.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL