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
| Package | npm name | Role |
|---|---|---|
| core | @cortask/core | The engine. Agent runner, LLM providers, tool system, skills, credentials, config, sessions, memory, cron, workspaces, usage tracking. |
| gateway | @cortask/gateway | The 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/ui | The frontend. React 19 single-page application built with Vite, styled with TailwindCSS and Radix UI, state managed by Zustand. |
| cli | cortask | The terminal interface. A Commander.js CLI that starts the gateway and exposes all platform features as shell commands. |
| channels | @cortask/channels | Messaging integrations. Adapters for Telegram, Discord, and WhatsApp that bridge external messages to the agent runner. |
| desktop | @cortask/desktop | The 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
AgentRunnerclass, 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
| Layer | Technology |
|---|---|
| Language | TypeScript (strict mode), ESM modules |
| Runtime | Node.js >= 20 |
| Package manager | pnpm workspaces |
| Backend framework | Express |
| Real-time | WebSocket (ws library) |
| Database | better-sqlite3 (per-workspace session DBs + central DB) |
| Frontend framework | React 19 with React Router 7 |
| Build (packages) | tsup |
| Build (UI) | Vite |
| Styling | TailwindCSS |
| UI components | Radix UI |
| State management | Zustand |
| Desktop | Electron |
| CLI framework | Commander.js |
| Validation | Zod |
| Testing | Vitest |
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 theCORTASK_CORS_ORIGINenvironment variable. - REST endpoints live under
/api(workspaces, sessions, credentials, providers, skills, cron, usage, models, templates, artifacts, channels, config, health, updates). - The WebSocket endpoint at
/wshandles 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.