{"slug": "cowork-self-hosted-task-queue-and-dashboard-for-claude-codex-and-ai-agents", "title": "Cowork – Self-hosted task queue and dashboard for Claude, Codex, and AI agents", "summary": "An open-source project called Cowork, published on GitHub by developer slashman413, offers a self-hosted MCP server and web dashboard that routes coding tasks across Claude Code, Hermes, Gemini and Ollama through automatic fallback chains when an agent hits a rate limit. Cowork stores all state as plain JSON and Markdown files with no Postgres, Redis or SQLite database, ships 285+ built-in specialist personas across 19 departments, and lets additional machines join as remote workers with a one-line Node command. The project positions itself against CrewAI, AutoGPT, OpenHands and LangGraph by natively supporting Claude Code and Hermes command-line agents and adding quota-aware failover the comparison table marks as manual or absent in the others.", "body_md": "**Self-hosted MCP server & Web Dashboard with auto-fallback chains, remote fleet execution, and zero-database storage.**\n\n**[⚡ Quick Start](#quick-start)** •\n**[💡 Why Cowork?](#-why-cowork)** •\n**[⚔️ Competitive Comparison](#%EF%B8%8F-how-cowork-compares)** •\n**[📖 Documentation](https://github.com/slashman413/cowork/blob/main/docs)** •\n**[🌐 Live Architecture](#task-execution--the-dispatcher)** •\n**[🤝 Join as a Remote Agent](https://github.com/slashman413/cowork/blob/main/JOIN-AS-AN-AGENT.md)** •\n[⭐ Star on GitHub](https://github.com/slashman413/cowork/stargazers)\n\nYou are using **Claude Code** for development. Two hours into a sprint, you hit the dreaded rate limit:\n\n`Error: You have exceeded your current quota. Please wait 4 hours and 12 minutes.`\n\nYour workflow stalls. Your terminal hangs. You switch to Gemini CLI or a local Hermes instance, but you lose your entire task state, intermediate patches, and execution context.\n\n**Cowork solves this in one command.** It is an open-source control plane that connects all your coding agents into one unified task inbox:\n\n- 🔀 **Smart Fallback Chains:** Route tasks from Claude Code ➔ Hermes ➔ Gemini ➔ Ollama. When an API limits out, Cowork transparently hands the work to the next agent in line.\n- 🖥️ **Single-Pane-of-Glass Dark Dashboard:** Live CPU/GPU/memory thermals, running agent status, interactive calendar routines, and direct artifact downloads.\n- 📁 **Zero-Database Simplicity:** Pure JSON and Markdown on disk. No Postgres, Redis, or SQLite migrations to maintain.\n- ⚡ **One-Command Remote Workers:** Harness GPUs or models across laptops, Mac Studios, and cloud boxes:`COWORK_URL=https://hub:6868 HOST=mac-studio node cowork/deploy/remote-agent-client.mjs`\n- 🎭 **285+ Built-in Specialist Personas:** Out-of-the-box routing across 19 departments (Frontend, SRE, Security, Penetration Testing, Data Engineering).\n\n| Feature | **Cowork** | CrewAI / AutoGPT | OpenHands | LangGraph | \n|---|---|---|---|---|\n| **Primary Focus** | **Multi-CLI / Multi-Agent Control Plane** | LLM Python Scripts | Headless Dev Container | Graph Logic Framework | \n| **Supports Claude Code & Hermes Native CLIs** | ✅ **Native** | ❌ No | ❌ No | ❌ No | \n| **Auto Quota / Rate-Limit Fallback Chains** | ✅ **Built-in** | ❌ Manual | ❌ No |  | \n| **Self-Hosted Dark Web UI & Hardware Telemetry** | ✅ **Instant** |  | ✅ Web UI |  | \n| **Model Context Protocol (MCP) Streamable Server** | ✅ **First-class** | ❌ No |  | ❌ Custom | \n| **Storage Engine** | **Plain Files / JSON** | SQLite/Postgres | Docker Volume | Postgres/Memory | \n| **Multi-Machine Remote Cluster Joining** | ✅ **1-Line Node CLI** | ❌ Complex | ❌ Docker swarm | ❌ Complex | \n| **Pre-loaded Agent Personas** | **285+ Personas** | 0 (DIY) | 0 (Generalist) | 0 (DIY) | \n\nJump to: [Quick Start](#quick-start) · [Connecting agents](#connecting-ai-agents) ·\n[Dispatcher](#task-execution--the-dispatcher) · [Agents](#agents--named-execution-identities-model--platform--location) ·\n[Vocabulary](#vocabulary) · [Thinking level](#thinking-level) ·\n[MCP tools](#mcp-tools-reference)\n\nOne dark-themed **single pane of glass** for the whole operation. The **Overview** shows live\nhost metrics (CPU / GPU / memory / temp), active workers, open tasks and personas, a\n7-day quality panel (pass rate, retries, wait-input, timeouts), the latest agent\n**rulings**, the dispatcher's role → model chains, and a real-time activity feed.\n\nThe sidebar groups every view: **Operations** (Overview, Portal, Tasks, Recipes, Calendar,\nGoals), **AI Fleet** (Routing, Agents) and **System** (Workers, Configuration). Integrations\n(skills, MCP servers and persona packs) have their own page, `/integrations.html`.\nThe [Tasks](#task-execution--the-dispatcher), [Routing](#agent-fallback-chains-get-apichains),\n[Agents](#agents--named-execution-identities-model--platform--location) and\n[Workers](#wiring-a-remote-agent-machine) views are shown in context throughout this README.\n\n| **Calendar** — scheduled tasks and recurring routines by week | **Goals** — long-running goals with phases, decisions and budget | \n|---|---|\n| **Recipes** — saved tasks you re-run with one click | **Integrations** — audited git repos installed as skills / MCP / persona packs / portal cards | \n| **Portal** — one-click launcher for local web services | **Obsidian Vault** — the shared, readable knowledge base | \n| **Configuration** — every setting, editable live |  | \n\n| Platform | Agents | Format | \n|---|---|---|\n| Claude Code | ~285 | `.md` with YAML frontmatter | \n| Antigravity (AGY) | Built-in + skills | `SKILL.md` | \n| Hermes Agent | 39 skills | `SKILL.md` + triggers | \n| Gemini CLI | Converted from source | `.md` subagents | \n| Copilot | Same as Claude | `.md` agents | \n| Codex | Converted | `.toml` agents | \n| Cursor | Converted | `.mdc` rules | \n| + 7 more | See the agency-agents persona pack | Various formats | \n\n- **Node.js** ≥ 20 (tested with v22)\n- **npm** ≥ 10\n- Nothing else to clone. Personas and skills are **integrations** : on first run, once\nthe server is up, it provisions the default integrations in the background —`msitarzewski/agency-agents` (persona pack the router picks from per task),`garrytan/gbrain` ,`addyosmani/agent-skills` ,`anthropics/skills` ,`vercel-labs/agent-browser` ,`backnotprop/pstack` ,`pbakaus/impeccable` ,`Nutlope/hallmark` ,`multica-ai/andrej-karpathy-skills` . Check them with`GET /api/integrations/defaults` (or the Integrations page); override the list with`integrations.defaults` in config.\n\nFrom the repo root of your clone:\n\n```\n# 1. Build + launch the server (installs deps, builds, starts a systemd user service)\ndeploy/install-server.sh\n\n# 2. Drop the coordination skill + MCP connection into your agent clients on this box\ndeploy/install-skill.sh            # auto-detects Claude Code / Hermes / Antigravity\n```\n\n`install-server.sh` generates a systemd unit with **this machine's** real node path and\nworking dir (nothing to hand-edit), or pass `--foreground` to launch in the terminal and\n`--build-only` to stop after building. `install-skill.sh` copies the right per-client\nskill into place and, for Claude Code, merges `mcpServers.cowork` into `~/.claude.json`\nnon-destructively (add `--url http://<host>:6868` for a remote server; `--client claude|hermes|agy|all`\nto target one). The numbered steps below are the same thing, broken out for when you want\nto do it by hand.\n\n```\ndocker run -d -p 6868:6868 -v cowork-data:/data ghcr.io/cowork/cowork:1.0.1\ndocker logs <container>   # first start prints the generated API key once\n```\n\nThe image (amd64 + arm64) runs the MCP server, dashboard and task inbox with the local\ndispatcher off: agents connect to `http://<host>:6868/mcp`, and machines offer their agents\nwith `deploy/remote-agent-client.mjs` (see [JOIN-AS-AN-AGENT.md](https://github.com/slashman413/cowork/blob/main/JOIN-AS-AN-AGENT.md)). Config\nand all state live in the `/data` volume. `/api` and `/mcp` always require a key: pass your own\nwith `-e COWORK_API_KEY=…`, or the server generates one on first start, saves it to\n`/data/api-key` and prints it once. Clients send `Authorization: Bearer <key>` (the agent client\nreads `COWORK_API_KEY`); open `http://<host>:6868/?apiKey=<key>` once to log the dashboard in. Images are\nbuilt from `v*` tags by the release workflow on a digest-pinned base.\n\nOnce the hub is up, open **Integrations** (or `GET /api/integrations/defaults`) to check that the\ndefault persona and skill packs finished provisioning. Until the persona pack is installed, the\nrouter has no personas to pick from.\n\n```\ncd cowork/server\nnpm install\n```\n\n**Config lives outside the repo.** The tracked `cowork/config.json` is a sanitized\n**template only**. On first run the server copies it to **`~/.cowork/config.json`** —\nyour real per-server config (host binding, registered agents, chains). Edit *that*\ncopy, not the template; the server also persists all live dashboard/API edits there,\nso your personal host/agent settings never touch the repo. Override the location with\nthe `COWORK_CONFIG` env var.\n\nEdit `~/.cowork/config.json` to match your environment:\n\n```\n{\n  \"server\": {\n    \"port\": 6868,          // ← Change the port here\n    \"host\": \"0.0.0.0\",     // ← Bind address (0.0.0.0 = all interfaces)\n    \"name\": \"cowork-mcp\",\n    \"version\": \"1.0.0\",\n    \"apiKey\": null          // ← Set a string to require API key auth (see below for null)\n  },\n  \"paths\": {\n    \"inbox\": \"./inbox\",\n    \"status\": \"./.status\",\n    \"decisions\": \"./decisions\",\n    \"artifacts\": \"./artifacts\",\n    \"inputs\": \"./inputs\"\n  }\n}\n```\n\n**All paths are relative to `cowork/`** (the parent of `server/`).\nUse `~` for home directory paths (e.g., `~/.claude/agents`).\n\n| Setting | Default | Description | \n|---|---|---|\n| `server.port` | `6868` | HTTP port for MCP endpoint + Web UI | \n| `server.host` | `0.0.0.0` | Bind address ( `127.0.0.1` for local-only) | \n| `server.apiKey` | `null` | API key for /api and /mcp. When null and `host` is not loopback, a key is generated into`api-key` beside the config and printed once | \n| `server.allowUnauthenticated` | `false` | Serve a non-loopback host with no key (also `COWORK_ALLOW_NO_AUTH=1` ). Only for a trusted, firewalled network | \n| `server.blockedClients` | — | Client denylist enforced regardless of the API key: `{ips: [...], names: [...]}` . A matching source IP gets 403 on every path; a`names` substring (case-insensitive) in the User-Agent /`X-Cowork-Client` header, MCP`clientInfo.name` , or tool-call identity args (`agent_name` ,`agent_id` ,`capabilities` ,`agents[].id` ) gets 403 on`/api` /`/mcp` . Matching agents are disabled so tasks stop routing to them. See`server/src/core/client-guard.ts` | \n| `paths.agencyAgents` | unset | Optional. Pins the roster to one local persona checkout; when unset, personas come from every installed persona-pack integration | \n| `integrations.defaults` | built-in list | Repos provisioned once on first start (persona pack + skill packs) | \n\n| `platforms.*.enabled` | `true` | Enable/disable individual platforms |\n| `platforms.*.agentsDir` | varies | Where platform-specific agents live on disk |\n| `services.*.enabled` | `false` | Enable/disable service health monitoring |\n\n```\n# Development mode (with hot-reload)\ncd cowork/server\nnpm run dev\n\n# Production mode\nnpm run build\nnpm start\n```\n\nYou should see:\n\n```\n=========================================\n Multi-Agent Cowork MCP Server Started\n HTTP API: http://0.0.0.0:6868/api\n MCP Endpoint: http://0.0.0.0:6868/mcp\n SSE Stream: http://0.0.0.0:6868/api/events\n=========================================\n```\n\nOpen **[http://localhost:6868](http://localhost:6868)** in your browser.\n\nFor programmatic access, wire the MCP connection into your agent clients:\n\n**Scripted (recommended):** `deploy/install-skill.sh` installs the `cowork` coordination\nskill and wires the MCP connection for every agent client detected on the box — one command\ninstead of hand-editing each config:\n\n```\ndeploy/install-skill.sh                          # auto-detect all clients, localhost server\ndeploy/install-skill.sh --client claude          # just Claude Code\ndeploy/install-skill.sh --client all --url http://cowork-host:6868   # remote server, every client\n```\n\nIt drops the per-client skill (canonical copies in `deploy/skills/`) into the client's live\nskill dir and, for Claude Code, merges the `mcpServers.cowork` entry into `~/.claude.json`\nwithout disturbing your other servers. The manual per-client config is below for reference.\n\nAdd to your MCP configuration (`~/.claude.json` or project-level):\n\n```\n{\n  \"mcpServers\": {\n    \"cowork\": {\n      \"url\": \"http://localhost:6868/mcp\",\n      \"transport\": \"streamable-http\"\n    }\n  }\n}\n```\n\nThen in Claude Code, the agent can call:\n\n```\nUse the cowork MCP tools: register_agent, create_task, get_roster, etc.\n```\n\nAdd to your AGY MCP settings:\n\n```\n{\n  \"mcpServers\": {\n    \"cowork\": {\n      \"url\": \"http://localhost:6868/mcp\"\n    }\n  }\n}\n```\n\n**agy quota meter / token refresh.** To mint Antigravity access tokens, the usage\nprobe and `deploy/remote-agent-client.mjs` need Google OAuth client values. These are\nthe public values that ship with the agy and Gemini CLIs. They are not committed. Copy\n[`.env.example`](https://github.com/slashman413/cowork/blob/main/.env.example) to `~/.cowork/oauth-clients.env`, fill it in, and run\n`chmod 600`. Alternatively, export the same variables in the service environment;\nprocess env takes precedence. If no values are set, the agy meter only works while a\ncached CLI access token is still valid.\n\nConfigure the MCP endpoint in Hermes:\n\n```\n{\n  \"mcp_endpoints\": {\n    \"cowork\": \"http://localhost:6868/mcp\"\n  }\n}\n```\n\nThe server speaks standard MCP over Streamable HTTP. Any client that supports\nMCP can connect to `http://localhost:6868/mcp`.\n\nThe server includes a **dispatcher** that turns queued tasks into real agent runs.\nOutput becomes the task `result`; the full transcript is saved as `result.md` in the\ntask's artifacts dir.\n\nEvery task lands in the **Task Inbox**, tagged with the division/agent that ran it and the\nagent it ran on, with its output artifacts attached and filterable by status\n(done · in-progress · pending · wait-input · failed).\n\nAn unassigned task is routed in **two stages** by an orchestrator/classifier agent\n(default `qwen3.8-27b` via Hermes, `orchestration.classifier`):\n\n1. **Division** — pick 1 of 19 divisions (testing, engineering, security, …). With`orchestration.workRoles.enabled` the same reply also names a**work role** (`mechanical` ,`feature` ,`bug-fix` ,`perf-issue` ,`research` ,`prose` ,`hardest` ),\nwhich sets the task's`context.tier` and`context.thinking` unless the task set them\n(thinking is capped by`workRoles.budget` : small = medium, medium = high,\nlarge = xhigh, unlimited = max).\n2. **Persona** — pick 1 of ~285 personas in that division. The chosen persona's`.md` file becomes the system prompt. If the router names no persona (empty reply,\nclassifier off), a keyword matcher (`core/persona-match.ts` ) picks one; if the reply\ncan't be parsed at all, the task goes to`classifier.fallbackRole` (`generalist` ).\n\nA persona shapes the **prompt** only; the agent that runs it comes from the fallback\nchain (below). Skip the router by targeting directly: `context.persona: \"<persona-slug>\"`\nor a special-executor name. Tag a task `manual` to never auto-execute.\n\nA task **pinned** to an agent (`context.agent`) skips agent selection. If that agent runs a\nsmall model (≤ 70B parameters, parsed from the model id, e.g. `qwen3.8-27b`) and the task\nhas no persona, the router still runs first so the pinned agent gets a persona. Override\nper agent with `orchestration.agents.<id>.personaRouting: \"always\" | \"never\"`.\n\n- **Special executors** (`config.json → orchestration.roleChains` ) — only`orchestrator` ,`generalist` ,`video` ; each is`{description, agents: [...]}` with its own chain.`video` runs the ComfyUI**LTX** pipeline\n(`deploy/video-pipeline.sh` ; LTX only — never Wan/Hunyuan/SVD).\n- **Personas** — the ~285 personas from the installed persona-pack integrations\n(default:`msitarzewski/agency-agents` , cloned under`~/.cowork/integrations/` ),\ngrouped into 19 divisions. They have no chain of their own: per-division and\nper-persona chains were removed on 2026-11-13, and`divisionChains` /`personaChains` in an old config are ignored.\n\n- **Global default** —`orchestration.defaultChain` ; drag-reorder in the**Routing** view (`PUT /api/chains/default` ).\n- **Tier lists** —`orchestration.tiers.<mechanical|standard|judgment>` (`GET /api/chains/tiers` ,`PUT /api/chains/tier/:tier` ). A task with a`context.tier` (set by hand or by its work role) tries its tier's agents first, then the default chain.\n- **Special executors** —`orchestration.roleChains.<role>.agents` (`/api/roles-config/:name` ).\n\nA chain runs top → bottom: the task runs on `chain[0]`; on failure the dispatcher\n**hands over** to `chain[1]`, then `[2]`… recording the failed agent on the task\neach attempt, until success or the chain is exhausted. Pin a single task to one\nagent with `context.agent: \"<id>\"`.\n\nThe **Routing** view edits the special-executor chains, the tier lists and the default\nchain: drag to reorder or `＋ add agent…`. Changes apply live, with no restart.\n\n| Term | Meaning | Where you see it | \n|---|---|---|\n| **Agent** | One model on one CLI on one machine: the thing that runs a task. Example: `local-cc-opus-5-5` . | `orchestration.agents` ,`context.agent` ,`/api/agents` , Agents page | \n| **Thinking** | How hard an agent reasons on a run. A parameter of the run, never part of an agent id. | `context.thinking` ,`agents.<id>.thinking` ,`orchestration.defaultThinking` | \n| **Persona** | A roster specialist the run takes on. It shapes the prompt, not the agent choice. Example: `engineering-sre` . | `context.persona` ,`get_roster` , persona-pack integrations | \n| **Work role** | The kind of work the router judged a task to be; sets its tier + thinking. Example: `bug-fix` . | `orchestration.workRoles` ,`context.tier` | \n| **Role** | A named chain of agents for a built-in job ( `orchestrator` ,`generalist` ,`video` ). | `orchestration.roleChains` , Routing page | \n| **Worker** | A connected client that registers agents and claims their tasks. | `/api/workers` , Workers page,`register_agent` | \n\n`register_agent`, `deregister_agent` and their `agent_id` / `agent_name` arguments keep their names so\nconnected clients don't break: `agent_id` is the **worker's** registration id, not an id from `agents`.\n\nAgent ids no longer carry a level: `local-agy-gemini-3.8-flash` replaces the `-high`, `-medium` and `-low`\nvariants. A run's level comes from the first of these that is set:\n\n1. the task's `context.thinking`\n2. the agent's `thinking`\n3. `orchestration.defaultThinking`\n4. `medium`\n\nLevels are `low`, `medium`, `high`, `xhigh` and `max`. Set one on the New task, Edit, Re-run, Reassign and\nContinue dialogs, per agent on the Agents page, or through the API:\n\n```\ncurl -X POST \"$COWORK/api/inbox\" -H 'Content-Type: application/json' -d '{\n  \"title\": \"...\", \"description\": \"...\", \"from\": {\"platform\": \"cli\", \"agent\": \"me\"}, \"to\": {},\n  \"context\": {\"agent\": \"local-cc-opus-5-5\", \"thinking\": \"high\"}\n}'\ncurl -X PATCH \"$COWORK/api/agents/local-cc-opus-5-5/thinking\" -H 'Content-Type: application/json' -d '{\"thinking\": \"high\"}'\n```\n\n| exec | How the level is applied | \n|---|---|\n| claude | `--effort <level>` | \n| codex | `-c model_reasoning_effort=\"<level>\"` (`max` runs as`xhigh` ) | \n| agy | model slug `<model>-<level>` (`xhigh` and`max` run as`high` ;`gpt-oss-120b` only has`medium` ) | \n| hermes | `--reasoning <level>` , only when a level was set explicitly (`xhigh` and`max` run as`high` ) | \n| dsh, ollama, script | no reasoning control, so the level is ignored | \n\nA hub upgraded from the `brain` naming migrates itself: on start, the server detects the old config shape, backs up\nthe config and state to `backups/agent-naming-boot-*.tar.gz` next to the config, and runs\n`scripts/migrate-agent-naming.mjs` before loading anything. So `git pull && deploy/redeploy.sh` is enough. Local\nclients still on `cowork-local-brain@` keep working under that unit; `deploy/cutover-agent-naming.sh` moves them to\n`cowork-local-agent@`.\n\nClients on the old contract keep working: the hub translates their old field names at the door\n(`server/legacy-aliases.json`, read only by `server/src/legacy-wire.ts`) and answers with `agent_aliases` so they\ncan update. `GET /api/legacy-wire` counts how often that still happens. Once it stays at zero, delete those two files.\n\n`config.json → orchestration.agents` (`GET /api/agents`) names each agent a chain\ncan reference. The **Agents** view lists every registered agent — its platform\n(`claude` / `agy` / `hermes`), underlying model, location, and whether it was\n`auto`-registered by a connecting client — with one-click deregister:\n\n| Alias | Location | Runs | \n|---|---|---|\n| `local-hermes-qwen3.8-27b` | local | Hermes on that model | \n| `local-dsh-qwen3-8-27b` | local | DeepSeek Harness ( `dsh` ) on that model | \n| `local-cc-opus-5-5` /`-sonnet-5-5` /`-fable-5-1` | local | Claude Code on that model | \n| `local-agy-*` /`local-comfy-ltx` | local | Antigravity/Gemini / ComfyUI-LTX video | \n| `local-codex-gpt-6-luna` /`-gpt-5-6-terra` /`-gpt-5-6-luna` /`-default` | remote | Codex on this host, claimed by the codex client | \n| `local-script-eval-harness` | local | The `eval-prompt-change` harness (`exec: script` ) | \n| `remote-ai-code-gen-cc-*` | remote | Claude Code on the ai-code-gen machine | \n| `remote-<host>-cc-sonnet-5-5` | remote | Claude Code on another machine | \n\nThis table is an example. Read the live set with `GET /api/agents`.\n\nAgent ids carry the **model version** (`local-cc-opus-5-5`, not `local-cc-opus`) so the\nmodel an agent runs is readable from its id; only `-default` agents, which follow the\naccount default, are unversioned.\n\n**Naming convention — the prefix names the machine, `location` names the runner.**\nThe `local-`/` remote-` id prefix says **which machine the agent executes on**:\n`local-` = this host (it can see `/home/wayne`, `~/.priv`, the GPU), `remote-` =\nanother machine (`remote-<host>-…`). The separate `location` field says **who\nstarts it**: `local` = the dispatcher spawns it, `remote` = a client claims it from\nthe inbox. The two usually agree, but not always — `local-codex-*` runs on this\nhost yet has `location: remote` because the codex client claims its tasks. Code must\nread `location`, never infer it from the prefix. DeepSeek\nagents are retired — any id matching `/deepseek/i` is scrubbed from the registry on\nload (see `DENYLISTED_AGENT_RE`), so never put `deepseek` in a new id.\n\n**Renamed ids keep working.** `AGENT_ID_ALIASES` in `server/src/config.ts` (plus an\noptional `orchestration.agentAliases` in config) maps retired ids to their canonical\nid. Old ids in an on-disk config, a task's `context.agent` pin, or a client's\nregistration all resolve to the canonical agent. A client that still declares an\nold id keeps receiving its tasks under that id (`clientId` on the agent), until it\nre-declares the canonical one. Renames (2026-10-07, and codex again on 2026-11-03):\n\n| Retired id | Canonical id | \n|---|---|\n| `local-ha-qwen3-8-27b` ,`local-ha-qwen38-27b` ,`local-ha-qwen3.8-27b` | `local-hermes-qwen3.8-27b` | \n| `remote-codex-default` /`-gpt-5-6-terra` /`-gpt-5-6-luna` /`-gpt-6-luna` | `local-codex-default` /`-gpt-5-6-terra` /`-gpt-5-6-luna` /`-gpt-6-luna` (2026-11-03: the prefix names the machine, so the 10-07`local-codex-*` →`remote-codex-*` rename was reversed) | \n| `remote-ai-code-gen-cc-opus` /`-sonnet` /`-fable` | `remote-ai-code-gen-cc-opus-4-8` /`-sonnet-5` /`-fable-5` | \n\n**Local** agents the dispatcher spawns here. **Remote** agents it leaves `pending`\nand **publishes the agent id onto the task's `context.agent`** so that machine's\nclient can discover and claim it (the client filters `list_inbox` for tasks whose\n`context.agent` is one of its own ids). If no client claims a remote rung within\n`orchestration.remoteGraceMs` (default 60 s), the dispatcher advances to the next\nagent in the chain, so a clientless remote rung never stalls a task.\n\n**Artifacts** work for both: local agents save files to `$COWORK_ARTIFACTS_DIR`\n(the dispatcher collects them from disk); remote agents save to the same env dir\nand the client **uploads** each file via `POST /api/artifacts/:taskId/:file`.\nEither way they land in `artifacts/<task-id>/` and become downloadable from the\nInbox. **Outputs** (the task's final deliverables) go in the `outputs/` subfolder\n(`$COWORK_OUTPUTS_DIR`; remote clients upload via `POST /api/outputs/:taskId/:file`)\nand get their own OUTPUTS section on the card, after RESULT and before ARTIFACTS.\n\n**Input files** flow the other way — a person attaches files for the agent to\n**read**. Attach them on any Inbox card (\"＋ Attach\nfiles\"); they are stored under `inputs/<task-id>/` and mirrored onto\n`context.inputFiles`. The dispatcher points a **local** agent at their absolute\npaths in the prompt; a **remote** agent client downloads them (`GET /api/inputs/:taskId`)\nbefore running and lists their local paths in the prompt. Inputs survive a re-run.\n\n**Failed tasks & re-run.** When a task exhausts its whole fallback chain (every\nagent failed verification) it finishes flagged `failed: true`. The Inbox groups\nthese under a red **\"failed\"** filter (kept out of the green \"done\" count) and\noffers a confirm-gated **↻ Re-run** that resets the task to `pending` from the top\nof its chain (`POST /api/inbox/:id/rerun`) — preserving its brief and attached inputs.\n\nA connecting MCP client DECLARES the agents it can run via the `register_agent`\ntool's `agents` field; the server auto-adds them to the registry (marked `dynamic`,\nowned by that client). One machine can offer several models at once. Auto-registered\nagents persist and are removed **only** by `deregister_agent` or the Agents view\n(never on heartbeat timeout); removal cascades out of the default chain, every\ntier list, and every special role. Manage all of this from the dashboard's\n**Agents** and **Routing** views.\n\n`deploy/remote-agent-client.mjs` is a zero-dependency (Node 18+) Cowork **MCP**\nclient that does exactly that loop. Its header comment documents every env var.\n\n**Quickest — run it directly** (foreground; good for a first test). One agent:\n\n```\ngit clone <cowork-repo-url> cowork   # the git remote your hub was installed from\nCOWORK_URL=http://<cowork-host>:6868 EXEC=claude MODEL=claude-sonnet-5-5 \\\n  AGENT_ID=remote-<host>-cc-sonnet-5-5 \\\n  node cowork/deploy/remote-agent-client.mjs\n```\n\nSeveral agents from one machine (declares them all; each becomes a targetable agent):\n\n```\nCOWORK_URL=http://<cowork-host>:6868 EXEC=claude HOST=<host> \\\n  AGENTS='[{\"id\":\"remote-<host>-cc-opus-5-5\",\"model\":\"claude-opus-5-5\"},\n           {\"id\":\"remote-<host>-cc-sonnet-5-5\",\"model\":\"claude-sonnet-5-5\"}]' \\\n  node cowork/deploy/remote-agent-client.mjs\n```\n\n**As a boot service** (recommended for permanent machines):\n\n```\nmkdir -p ~/.config/cowork-remote-agent\ncp cowork/deploy/remote-agent-client.env.example ~/.config/cowork-remote-agent/aicodegen.env\n# edit: COWORK_URL, AGENTS (or AGENT_ID), EXEC, HOST, COWORK_CLIENT_JS\ncp cowork/deploy/cowork-remote-agent@.service ~/.config/systemd/user/\nsystemctl --user daemon-reload && systemctl --user enable --now cowork-remote-agent@aicodegen\n```\n\nIt connects to `COWORK_URL/mcp`, `register_agent` s (declaring its `AGENTS`, which\nauto-register into the registry), then polls and claims only tasks whose\n`context.agent` is one of its agent ids, runs the matching `EXEC`/` MODEL`, and\n`complete_task` s with its result and uploaded artifacts — appearing in **Workers** like any other\nclient. **Flexible**: the same script serves any agent by changing env only.\n**Scalable**: add machines/agents by adding `<name>.env` files; claims are atomic\n(single-process compare-and-set) so even multiple clients on the *same* agent id\nnever double-run a task — first claim wins.\n\nEvery connected client shows up live in the **Workers** view — grouped by platform,\nlisting the agents it offers, its idle/busy state, and a per-session tally of how many\ntasks each agent has run vs. submitted:\n\nThe always-on coordinator agent shown in **Workers** as `cowork/orchestrator`\npolls the inbox, two-stage-routes unassigned tasks, reclaims orphans, and\ndispatches; transient per-task workers appear as e.g.\n`testing / Workflow Optimizer · local-hermes-qwen3.8-27b` or `video · local-comfy-ltx`\nwhile running.\n\nCEO flow: tell Hermes (e.g. via Discord) an idea → Hermes creates ONE task with\n`context.persona: \"orchestrator\"` → the orchestrator decomposes it into subtasks via\n`POST /api/inbox` → each subtask is two-stage-routed (persona + work role) and runs on\nits tier/default chain → results and artifacts appear live on the dashboard.\n\nAn unassigned task (e.g. a free-text idea filed straight from Discord) no longer\nstalls: the dispatcher runs the **two-stage LLM router** (default qwen3.8-27b\nvia Hermes, `orchestration.classifier` in config.json) that reads the task, picks\na division (plus a work role), then a persona, and the task dispatches normally on\nits tier/default chain. Tag a task `manual` to skip both routing and dispatch.\n\n`orchestration.staleClaimMs` (code default `0` = off; the template `config.json` sets 10 min) reclaims any in-progress task\nwhose claiming agent has disappeared — a crashed/exited live agent or a\ndispatcher killed mid-run — back to `pending` so the work is retried. A task\nstill owned by a heartbeating agent is never touched.\n\nDispatcher status: `GET /api/dispatcher`. Tune `orchestration.maxConcurrent` /\n`taskTimeoutMs` / `pollIntervalMs` / `classifier` / `staleClaimMs` in config.\n\nEasiest — `deploy/install-server.sh` generates and enables the user service with this\nmachine's resolved node path and working dir:\n\n```\ndeploy/install-server.sh          # build + install & start the cowork-mcp user service\n```\n\nManual equivalent (the tracked `deploy/cowork-mcp.service` has `/home/USER` placeholders\nyou must edit first — the script fills them in for you):\n\n```\ncp deploy/cowork-mcp.service ~/.config/systemd/user/   # then edit the USER/paths inside\nsystemctl --user daemon-reload\nsystemctl --user enable --now cowork-mcp\n```\n\nWith `server.host: \"0.0.0.0\"` the dashboard is reachable from other machines at\n`http://<host-ip>:6868/` (LAN or Tailscale IP — `0.0.0.0` itself is a bind\naddress, not a URL). A non-loopback host always requires a key: set `server.apiKey`\n(or `COWORK_API_KEY`), or use the one generated into `api-key` beside the config on first\nstart. Log the dashboard in once with `http://<host-ip>:6868/?apiKey=<key>` (it sets a\nsame-site cookie). `server.allowUnauthenticated: true` opts out; anyone who can reach the port\ncan then create tasks for your agents.\n\n**Moving a running open hub to a key without locking anyone out:** set the key *and* keep\n`allowUnauthenticated: true`. That is soft (migration) mode: a valid key is accepted, a wrong key\ngets 401, and keyless requests still pass but are listed at `GET /api/auth/keyless` (key required).\nGive the key to every client (agent clients: `COWORK_API_KEY`; MCP configs: an\n`Authorization: Bearer <key>` header, e.g. `deploy/install-skill.sh --key <key>`; dashboards:\n`/?apiKey=<key>` once). Dispatched tasks get `COWORK_API_KEY` in their env automatically. When\n`/api/auth/keyless` stays empty, drop `allowUnauthenticated` and restart to enforce.\n\nBy default the server listens over plain `http://`. That is fine over `localhost`,\nbut a browser only exposes the **microphone** (the New-task *Dictate* speech-to-text\nbutton) in a **secure context** — `https://` or `http://localhost`. Reach the\ndashboard over `http://<lan-or-tailscale-ip>:6868` and `navigator.mediaDevices` is\nundefined, so Dictate can't start. Serving over HTTPS fixes it.\n\nWhen `server.tls` points at valid cert/key files, Cowork runs a **multiplexed dual-protocol listener**\non port 6868 that accepts both HTTP and HTTPS simultaneously:\n\n- **Localhost in Chrome (`http://localhost:6868/`)** : Chrome and modern browsers natively treat` http://localhost` as a W3C-standard**Secure Context** (`window.isSecureContext === true` ).\nThe New-task*Dictate* mic button and clipboard work out of the box with**zero certificate warnings** .\n- **Remote / Mobile on Tailscale (`https://<machine>.tailXXXX.ts.net:6868/`)** : Remote connections\nuse the valid Let's Encrypt TLS certificate provisioned via Tailscale. Chrome shows a verified,\ntrusted green lock.\n- **Automatic IP/Domain HTTP-to-HTTPS Redirection** : Requests arriving over plain HTTP for external\nhostnames or IPs are automatically redirected (302) to the trusted HTTPS domain.\n- **SNI Fallback** : Multi-domain TLS SNI callback routes domain traffic to the trusted certificate\nand loopback TLS traffic to the local fallback certificate.\n\nMigrate in this order:\n\n1. \n**Obtain or Generate a TLS Certificate:**\n  - **Tailscale Let's Encrypt (Recommended, Zero Warnings)** :\nThis fetches a genuine Let's Encrypt certificate via\n\n```\ndeploy/update-tailscale-cert.sh\n```\n\n `tailscale cert` and installs it to`~/.cowork/tls/cert.pem` and`~/.cowork/tls/key.pem` .\n  - **Self-Signed (Alternative)** :\n\n```\nserver/gen-tls-cert.sh ~/.cowork/tls cowork-host 100.64.0.1 cowork.tailXXXX.ts.net\n```\n\n2. \n**Point the config at the files.** Add a`tls` block under`server` in`~/.cowork/config.json` (the live config — the repo`config.json` is only the\ndefault template and ships`\"tls\": null` ):\n\n```\n\"server\": {\n  \"host\": \"0.0.0.0\",\n  \"port\": 6868,\n  \"tls\": { \"certFile\": \"~/.cowork/tls/cert.pem\", \"keyFile\": \"~/.cowork/tls/key.pem\" }\n}\n```\n\n `~` and repo-relative paths are resolved for you.**This is the step most often\nmissed:** generating the cert alone does nothing — without this block the server\nstays on`http://` .\n3. \n**Restart the server so the new scheme takes effect.** A plain`systemctl --user restart cowork-mcp` run*from inside a task* SIGKILLs that task\n(it is a child of the unit it's restarting), so use the idle-safe redeploy, which\nwaits until no task is in flight:\n\n```\nsystemd-run --user --collect --unit=cowork-server-redeploy \\\n  deploy/redeploy-server-when-idle.sh\n```\n\n Confirm it came up: \n\n```\njournalctl --user -u cowork-mcp -n 10 | grep -E 'Trusted HTTPS|Local HTTP|API:'\ncurl -s http://localhost:6868/api/status | jq .uptime\n```\n\n4. \n**Accessing the Dashboard in Chrome:**\n  - **On the host machine** : Open`http://localhost:6868/` (Secure Context active, 0 SSL warnings).\n  - **From remote devices or mobile** : Open`https://<tailscale-domain>:6868/` (Trusted Let's Encrypt SSL lock).\n5. \n**Updating Agent Clients and Skills:** Run`deploy/install-skill.sh` , which automatically detects your Tailscale domain or localhost\nconfiguration:\n\n```\ndeploy/install-skill.sh --client all\n```\n\n**Server-side self-dispatch is loopback-optimized** — local subtask dispatch runs over\n`http://localhost:6868` through the multiplexed listener, avoiding TLS handshake overhead\nand certificate hostname mismatch errors entirely.\n\nTo roll back, set `server.tls` to `null` (or remove the block) and redeploy — the\nserver returns to `http://`.\n\nOnce connected, agents have access to these tools:\n\n| Tool | Description | \n|---|---|\n| `register_agent` | Register this client; optionally DECLARE the `agents` it can run | \n| `deregister_agent` | Remove this client and cascade-remove every agent it registered | \n| `heartbeat` | Update status and current task | \n| `get_roster` | Search ~285 personas across 19 divisions | \n| `create_task` | Create a task for another agent/platform | \n| `claim_task` | Claim a pending inbox task | \n| `complete_task` | Mark a task as done with results | \n| `list_inbox` | List inbox tasks with status/platform filters | \n| `get_dashboard` | Get full dashboard data (active workers, inbox stats, etc.) | \n| `create_goal` /`list_goals` /`get_goal` /`update_goal_progress` | Long-lived, phase-tracked Goals driven toward a yes/no success criterion | \n| `obsidian_info` /`obsidian_search` /`obsidian_read` /`obsidian_list` | Shared Obsidian knowledge base (when a vault is configured) | \n| `integrations` | Integrate a git / open-source repository (list, add, install, …) | \n\n```\nYou: \"Review the auth code in saas-starter\"\n\nAGY Agent calls:\n  create_task(\n    title: \"Review auth middleware\",\n    from_platform: \"antigravity\",\n    from_agent: \"self\",\n    to_platform: \"claude\",\n    to_agent: \"engineering-code-reviewer\",\n    priority: \"normal\"\n  )\n\n→ Task appears in dashboard inbox\n→ Claude Code picks it up, runs review\n→ Claude calls complete_task(...) with its result + artifacts\n→ Result + artifacts appear on the task card\n```\n\nThe declarative **Workflows** engine (`workflows/*.json`, `/api/workflows*`, the\nWorkflows tab) was removed on 2026-11-02. Its jobs are covered by:\n\n- **Recipes** — a task saved from a good run, or written from scratch with**New recipe** ,\nwith`{{fields}}` you fill in per run. A recipe carries its agent, thinking level and\ntimebox; the Run sheet can override agent and thinking for one run\n(`POST /api/recipes/:id/run` with`{values, agent, thinking, scheduledAt, recurrence}` ).\nRecipes don't pin a persona: the router picks one per run.\n- **Goals** — a long-lived objective with a yes/no success criterion and phases, driven by\nthe Achiever loop until it is met (`create_goal` ,`/api/goals` ). See[docs/goals-redesign.md](https://github.com/slashman413/cowork/blob/main/docs/goals-redesign.md) and[docs/goal-loop-idiom.md](https://github.com/slashman413/cowork/blob/main/docs/goal-loop-idiom.md) .\n- **Orchestrator decomposition** — a task with`context.persona: \"orchestrator\"` splits\nitself into subtasks through`POST /api/inbox` .\n\nThe Web UI uses these endpoints (also available for scripts/integrations):\n\n| Method | Path | Description | \n|---|---|---|\n| `GET` | `/api/status` | Dashboard overview data | \n| `GET` | `/api/workers` | All active workers (id → name) | \n| `GET` | `/api/connections` | Live MCP clients (heartbeat) + per-agent ran/submitted counters | \n| `GET` | `/api/inbox?status=pending` | Inbox tasks (filterable) | \n| `GET` | `/api/tasks` | Same list and filters as `/api/inbox` , wrapped as`{tasks}` | \n| `POST` | `/api/inbox` | Create a new task; optional `inputs: [{token,name}]` attaches uploaded files | \n| `PATCH` | `/api/inbox/:id` | Claim or complete a task | \n| `POST` | `/api/inbox/:id/rerun` | Re-queue a FAILED (chain-exhausted) task — reset to pending from the top of its chain, keep its inputs | \n| `GET` /`POST` | `/api/inbox/:id/inputs` | List / attach input files to an existing task ( `{inputs:[{token,name}]}` ) | \n| `POST` | `/api/uploads?name=<file>` | Stage an uploaded file (raw body) → `{token}` to pass as a task input | \n| `GET` | `/api/inputs/:taskId` ,`/:taskId/:file` | List / download a task's attached input files | \n| `POST` | `/api/inbox/:id/pause` ,`/resume` (or`/api/tasks/pause\\|resume` with`{taskId}` ) | Pause a task (stops a running agent) / resume it with its context intact | \n| `GET` | `/api/tasks/paused` | Paused tasks | \n| `GET` | `/api/roster?division=engineering` | Personas from the installed persona packs (filterable) | \n| `GET` | `/api/roster-divisions` | Personas grouped by division | \n| `GET` | `/api/dispatcher` | Special roles + agents + defaultChain + running | \n| `GET` /`PUT` | `/api/chains` ,`/chains/default` ,`/chains/tiers` ,`/chains/tier/:tier` | Read/edit the default chain and tier lists | \n| `GET` /`PUT` /`DELETE` | `/api/agents` ,`/api/agents/:id` | Agent registry (cascades on delete) | \n| `PATCH` | `/api/agents/:id/enable` ,`/disable` ,`/thinking` | Toggle an agent, set its default thinking level | \n| `GET` /`PUT` /`DELETE` | `/api/roles-config` /`/api/roles-config/:name` | Special-executor chains | \n| `GET` /`POST` | `/api/recipes` ,`/api/recipes/:id` (`PUT` /`DELETE` ),`/api/recipes/:id/run` ,`/api/recipes/draft-from-task/:taskId` | Saved recipes | \n| `GET` /`POST` | `/api/goals` ,`/api/goals/:id` (`PATCH` /`DELETE` ),`/goals/:id/activate\\|pause\\|block` ,`/goals/templates` | Goals | \n| `GET` | `/api/calendar` | Scheduled + recurring tasks for the Calendar view | \n| `GET` /`POST` | `/api/integrations` ,`/api/integrations/:id/:action` ,`/api/integrations/defaults` ,`/api/integrations/defaults/provision` | Integrations (skills, MCP, persona packs); verify / re-run first-run provisioning | \n| `GET` /`POST` | `/api/artifacts/:taskId` ,`/:taskId/:file` | List/download; POST (raw body) uploads a file from a remote agent | \n| `GET` /`POST` | `/api/outputs/:taskId` ,`/:taskId/:file` | Same, for the task's `outputs/` deliverables | \n| `GET` | `/api/config` | Current configuration | \n| `GET` | `/api/events` | SSE event stream | \n\n```\ncowork/\n├── config.json              # TEMPLATE only — real config at ~/.cowork/config.json\n├── README.md                # This file\n├── PROTOCOL.md              # Protocol specification\n├── JOIN-AS-AN-AGENT.md       # Onboarding for a remote agent client\n├── CONVENTIONS.md           # Operating rules injected into every dispatched task\n├── layout.json / portal.json # Dashboard nav + widgets / Portal cards (live copies in ~/.cowork/)\n├── methods/                 # Playbook cards the sharpener attaches to a task\n├── vendor/pstack/           # Vendored pstack skills\n├── server/                  # MCP Server + Web UI\n│   ├── src/                 # TypeScript source\n│   └── public/              # Web UI (HTML/CSS/JS)\n├── deploy/                  # systemd units, remote-agent client, presets, skills\n├── inbox/                   # Task queue (JSON files, auto-managed)\n├── artifacts/               # Per-task OUTPUT files (audio/video/md), downloadable\n├── inputs/                  # Per-task INPUT files a person attached for the agent (gitignored)\n├── goals/                   # Goal aggregates (runtime, gitignored)\n├── decisions/               # Decision log\n└── .status/                 # Runtime state (auto-managed)\n\n~/.cowork/\n├── config.json              # Real per-server config\n├── integrations.json        # Integration registry\n└── integrations/            # Cloned integrations, incl. the persona pack(s)\n```\n\nTest the MCP endpoint with the official inspector:\n\n```\nnpx @modelcontextprotocol/inspector http://localhost:6868/mcp\n```\n\nThis opens a web UI at [http://localhost:6274](http://localhost:6274) where you can browse and test all\nMCP tools interactively.\n\n```\n# Check server status\ncurl http://localhost:6868/api/status | jq\n\n# List active workers\ncurl http://localhost:6868/api/workers | jq\n\n# List pending inbox tasks\ncurl \"http://localhost:6868/api/inbox?status=pending\" | jq\n\n# Browse engineering personas\ncurl \"http://localhost:6868/api/roster?division=engineering\" | jq\n\n# Watch real-time events\ncurl -N http://localhost:6868/api/events\n# Register a test agent\ncurl -X POST http://localhost:6868/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"jsonrpc\": \"2.0\",\n    \"method\": \"tools/call\",\n    \"params\": {\n      \"name\": \"register_agent\",\n      \"arguments\": {\n        \"platform\": \"antigravity\",\n        \"agent_name\": \"test-agent\",\n        \"current_task\": \"Testing MCP connection\"\n      }\n    },\n    \"id\": 1\n  }'\n\n# Get dashboard data\ncurl -X POST http://localhost:6868/mcp \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"jsonrpc\": \"2.0\",\n    \"method\": \"tools/call\",\n    \"params\": {\n      \"name\": \"get_dashboard\",\n      \"arguments\": {}\n    },\n    \"id\": 2\n  }'\n{\n  \"server\": {\n    \"port\": 6868,\n    \"host\": \"0.0.0.0\",\n    \"name\": \"cowork-mcp\",\n    \"version\": \"1.0.0\",\n    \"apiKey\": null\n  },\n  \"paths\": {\n    \"inbox\": \"./inbox\",\n    \"status\": \"./.status\",\n    \"decisions\": \"./decisions\",\n    \"artifacts\": \"./artifacts\",\n    \"inputs\": \"./inputs\"\n  },\n  \"platforms\": {\n    \"claude\": {\n      \"enabled\": true,\n      \"agentsDir\": \"~/.claude/agents\",\n      \"color\": \"#D97757\"\n    },\n    \"hermes\": {\n      \"enabled\": true,\n      \"skillsDir\": \"../hermes-agent/skills\",\n      \"color\": \"#7C3AED\"\n    },\n    \"antigravity\": {\n      \"enabled\": true,\n      \"skillsDir\": \"~/.gemini/config/skills\",\n      \"color\": \"#0EA5E9\"\n    }\n  },\n  \"inbox\": {\n    \"autoArchiveDays\": 30,\n    \"maxRetries\": 3\n  }\n}\n```\n\nPortal launcher cards live in their own file, `portal.json` (live copy\n`~/.cowork/portal.json`, hot-reloaded) — see [docs/dashboard.md#portal](https://github.com/slashman413/cowork/blob/main/docs/dashboard.md#portal).\n\nThe real config is read from `~/.cowork/config.json` by default. Point at a\ndifferent file with the `COWORK_CONFIG` env var (and override the port / API key\nwithout editing any file):\n\n```\nCOWORK_CONFIG=~/.cowork/config.staging.json COWORK_PORT=6900 npm start\n```\n\nThe persona catalog is cached in memory and rescanned at most once per `COWORK_ROSTER_TTL_MS`\n(default 30000). A newly installed or updated persona pack propagates within that window with no\nrestart;\nset `0` to rescan on every query, or a larger value to reduce disk work.\n\nGitHub stars tell us which agent integrations (Cursor MCP, Windsurf, Roo-Code, Goose) to build next.\n\n- ⭐ **Star now** to bookmark this project and receive release alerts when new agent adapters drop.\n- 🍴 **Fork** to run an isolated agent fleet for your team or homelab.\n\n[Cowork Pro ($59)](https://slashmaster6.gumroad.com/l/cowork-pro?utm_source=cowork&utm_medium=readme) is the production\npackage: advanced workflows, priority support and lifetime updates. It is also part of the\n[AI Developer Stack Bundle ($79)](https://slashmaster6.gumroad.com/l/ai-dev-stack?utm_source=cowork&utm_medium=readme).\n\n[MIT](https://github.com/slashman413/cowork/blob/main/LICENSE) © 2026 Cowork. The core server, dashboard and remote-agent client are\nfree and open source; Cowork Pro (above) is the separate paid add-on.", "url": "https://wpnews.pro/news/cowork-self-hosted-task-queue-and-dashboard-for-claude-codex-and-ai-agents", "canonical_source": "https://github.com/slashman413/cowork", "published_at": "2026-11-14 16:27:00+00:00", "updated_at": "2026-11-14 16:52:40.483227+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "agent-protocols", "ai-products"], "entities": ["Cowork", "slashman413", "Claude Code", "Hermes", "Gemini", "Ollama", "CrewAI", "AutoGPT"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/cowork-self-hosted-task-queue-and-dashboard-for-claude-codex-and-ai-agents", "markdown": "https://wpnews.pro/news/cowork-self-hosted-task-queue-and-dashboard-for-claude-codex-and-ai-agents.md", "text": "https://wpnews.pro/news/cowork-self-hosted-task-queue-and-dashboard-for-claude-codex-and-ai-agents.txt", "jsonld": "https://wpnews.pro/news/cowork-self-hosted-task-queue-and-dashboard-for-claude-codex-and-ai-agents.jsonld"}}