Cortask

Architecture

Cortask is a TypeScript monorepo managed with pnpm workspaces. Each package has a focused responsibility, and they compose together at runtime through the gateway.


Packages

Packagenpm nameRole
core@cortask/coreThe engine. Agent runner, LLM providers, tool system, skills, credentials, config, sessions, memory, cron, workspaces, usage tracking.
gateway@cortask/gatewayThe API layer. Express HTTP server + WebSocket server. Wires core components together, serves REST endpoints, streams agent responses, and serves the UI as static files.
ui@cortask/uiThe frontend. React 19 single-page application built with Vite, styled with TailwindCSS and Radix UI, state managed by Zustand.
clicortaskThe terminal interface. A Commander.js CLI that starts the gateway and exposes all platform features as shell commands.
channels@cortask/channelsMessaging integrations. Adapters for Telegram, Discord, and WhatsApp that bridge external messages to the agent runner.
desktop@cortask/desktopThe native app. Electron shell that spawns the gateway as a child process and loads the UI in a frameless BrowserWindow with system tray support.

Build order matters: core and channels must build first, then gateway and UI, then CLI and desktop.


How the packages connect

desktop (Electron)
  |
  +-- starts gateway as child process
  |     |
  |     +-- imports core (agent runner, providers, tools, skills, ...)
  |     +-- imports channels (Telegram, Discord, WhatsApp adapters)
  |     +-- serves ui (static files from build output)
  |     +-- exposes REST API at /api and WebSocket at /ws
  |
  +-- loads ui in BrowserWindow (connects to http://127.0.0.1:<port>)

cli
  +-- imports gateway (starts server programmatically)
  +-- imports core (direct access to config, workspaces, credentials)
  • Core is a pure library with no server or UI code. It exports the AgentRunner class, all LLM provider implementations, built-in tools, the skill loader, credential store, session store, memory manager, cron service, and configuration schema.
  • Gateway imports core and channels, wires everything together, and exposes it over HTTP and WebSocket. It is the single runtime process that all interfaces talk to.
  • UI is a standalone React app that communicates with the gateway exclusively through fetch (REST) and a WebSocket connection. It has no direct dependency on core or gateway code.
  • CLI starts the gateway server in-process, then uses the same REST/WebSocket interface (or direct core imports) to execute commands.
  • Desktop spawns the gateway as a backend process, then opens a BrowserWindow pointed at the gateway's HTTP address.
  • Channels provide adapter classes that the gateway instantiates and manages. Each adapter receives inbound messages and routes them through createAgentRunner, the same factory the UI and CLI use.

Data flow

A typical request follows this path:

User (UI / CLI / Channel / API client)
  |
  v
Gateway (Express + WebSocket)
  |
  v
AgentRunner.runStream()
  |
  +---> builds system prompt (workspace context, memory, skill instructions)
  +---> calls LLMProvider.generateStream() with messages + tool definitions
  |       |
  |       v
  |     LLM Provider (Anthropic / OpenAI / Google / Ollama / ...)
  |       |
  |       v
  |     returns streaming chunks (text, thinking, tool calls)
  |
  +---> executes tool calls (up to 5 concurrently)
  +---> appends tool results to conversation
  +---> loops back to LLM until no more tool calls or maxTurns reached
  |
  v
Response streamed back to user
Session saved to SQLite
Usage recorded for spending limits

The agent loop runs for up to maxTurns iterations (default 25). Each iteration sends the full conversation history to the LLM, processes any tool calls, and feeds the results back. Tool results older than the current turn are truncated to 1500 characters to manage context size.


Tech stack

LayerTechnology
LanguageTypeScript (strict mode), ESM modules
RuntimeNode.js >= 20
Package managerpnpm workspaces
Backend frameworkExpress
Real-timeWebSocket (ws library)
Databasebetter-sqlite3 (per-workspace session DBs + central DB)
Frontend frameworkReact 19 with React Router 7
Build (packages)tsup
Build (UI)Vite
StylingTailwindCSS
UI componentsRadix UI
State managementZustand
DesktopElectron
CLI frameworkCommander.js
ValidationZod
TestingVitest

Networking

  • The gateway listens on port 3777 by default, bound to 127.0.0.1 (localhost only).
  • Both values are configurable via config.yaml (server.port, server.host) or environment variables (CORTASK_PORT, CORTASK_HOST).
  • If the default port is occupied, the gateway automatically tries up to 10 consecutive ports before failing.
  • CORS is restricted to local origins (localhost, 127.0.0.1). Additional origins can be allowed via the CORTASK_CORS_ORIGIN environment variable.
  • REST endpoints live under /api (workspaces, sessions, credentials, providers, skills, cron, usage, models, templates, artifacts, channels, config, health, updates).
  • The WebSocket endpoint at /ws handles real-time chat streaming, tool execution events, permission requests, and session cancellation.
  • The desktop app connects to the gateway over the same local HTTP/WebSocket interface — there is no IPC-based data path for agent communication.