Integrations
The technical side of the outside world: how sync is mapped, where each piece lives, and the protocols the app speaks. Setting up your own accounts is in the User guide.
How sync works
Sync is bolt-on, not load-bearing. The local database is authoritative for the app; a sync engine maps it onto an external system, records the mapping in the storage layer, and reconciles changes in both directions. Nothing in the task loop requires a network call, so a sync that fails leaves the app fully usable.
Each outside system gets its own storage module, its own authentication path in the app, and a spec indocs/spec/ that states what is implemented today — those specs are the accurate record, including the parts that are still open.
Todoist
Todoist is the primary external task list, and the sync currently runs one way: import only. The spec (draft v1) covers a push of title, description, and done deltas, but that is not implemented — libs/storage/src/todoist.rs says so at the top, and it matters because remote state wins every mapped field whenever the remoteupdated_at is newer than the link's timestamp. Local edits and completions on a synced task can therefore be overwritten until push lands, and label and section removal is additive-only for the same reason.
For each linked project the import is a three-step pass:
- Fetch the project's sections and turn them into child tags under the project tag, recorded as tag sections on it — which is what lets a synced project show its section headings.
- Fetch its tasks and create or per-field merge each one, tagged with the project tag and its own section's child tag.
- Tombstone any locally linked task that is no longer present remotely.
The mapping lives in four tables from 0010_todoist_sync.sql: integrations (provider, account label), external_task_links and external_tag_links (integration plus external id to a local row), and sync_state, which holds the watermark a project was last synced from. Priority crosses the boundary by mapping Todoist's four-step priority onto the local urgency factor and back, and due dates onto deadlines; a sync reports what it did as counts of projects, sections, upserted tasks, and tombstones.
GitHub
GitHub support is in preview and has two halves: issues and projects sync, and a git-backed coding workflow. preview Expect the shape of the mapping to change, since the sync engine and the workflow engine are both still moving.
The coding workflow is the interesting half: a run can use a real checkout, drive the git operations itself, and reach tool servers over MCP (coding_git.rs and coding_mcp.rs).The workflow engine covers that in full.
GitHub's sync keeps its own watermark table, separate from Todoist's, storing an etag and asince cursor alongside the last sync time — so a poll can be conditional and incremental rather than re-reading everything.
When a pass runs is demand-driven rather than purely periodic. The poller idles at the card's sync frequency (five minutes by default, never on Manual) and tightens to fifteen seconds while a pull request or a run is live. Two moments skip the cadence entirely, because someone is waiting on them: the first load of the connection starts a pass, so the projects on screen at startup are current, and opening a project that a repo backs starts one, so its issues are fetched as the view renders instead of at the next tick. Passes never stack, and one that started seconds ago is treated as fresh enough to leave alone.
The provider layer
Providers are OpenAI-compatible endpoints. libs/ai_providers implements them on top of the cersei agent library, including an OAuth-backed provider for services that do not hand out static keys. Request tuning is per model rather than per provider, so one model's sampling parameters can never leak into another model on the same endpoint.
Fallback lives above the provider: a combo is a named sequence of provider/model pairs, tried in order when a run fails before producing any output. Because the switch only happens before the first token, a retry cannot duplicate partial work. Entries that fail cool down for a few minutes, and that cooldown state is persisted in a small JSON file rather than a database, so a rate-limited provider is not hammered again immediately.
Tool servers, hooks, and proxies
The agent's config also wires up its tools and its lifecycle. MCP servers are launched as child processes and their tools are merged into the agent's tool set; hooks run shell commands on agent events; a proxy can route provider traffic through a gateway.
1 {
2 "mcp_servers": [
3 { "name": "my-tools", "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "."] }
4 ],
5 "hooks": [
6 { "event": "on_turn_end", "command": "echo turn done" }
7 ],
8 "proxy": { "enabled": false, "url": "http://localhost:8317/v1" }
9 }The agent protocol
agent-cli doubles as an Agent Client Protocol server, which is how the desktop app's agent pane drives it. The protocol runs over stdio as newline-delimited JSON-RPC 2.0 and supports initialize, session/new, session/load, session/prompt, session/set_config_option, and session/cancel, with streaming updates delivered as notifications.
cargo run -p agent-cli # interactive TUI
cargo run -p agent-cli -- --acp # ACP server over stdio (JSON-RPC 2.0 NDJSON)
cargo run -p agent-cli -- -p "fix the tests" # single-shot prompt, runs with toolssession/new advertises the available providers and models so a client can switch models mid-session, and a fallback switch is reported to the client rather than happening silently. The client side is libs/acp-client: process launch, sessions, the transcript model, and the file-system and terminal capabilities a session needs.
The server side — the full method surface, the connection, the agent's tools and sub-agents, and the patches carried on the vendored agent library — isAgent runtime.
Where each piece lives
| Surface | Code | Spec |
|---|---|---|
| Todoist | apps/taskstream-desktop/src/todoist_auth.rs, libs/storage/src/todoist.rs, apps/taskstream-desktop/src/ui_parts/todoist_sync.rs | docs/spec/todoist-sync-spec.md |
| GitHub | apps/taskstream-desktop/src/github_auth.rs, libs/storage/src/github.rs | docs/spec/github-integration-spec.md |
| Coding workflow git side | apps/taskstream-desktop/src/coding_git.rs, apps/taskstream-desktop/src/coding_mcp.rs | docs/spec/coding-workflow-runs-spec.md |
| Agent panel ← ACP client | apps/taskstream-desktop/src/ui_parts/agent_pane.rs, libs/acp-client | docs/spec/acp-client-and-agent-panel-spec.md |
| Provider layer | libs/ai_providers, apps/agent-cli/src/providers.rs | apps/agent-cli/README.md |
Architecture covers the crates themselves, and thespec table lists every spec with its status.