An agentic software engineer that lives in your WhatsApp group.
Coworker follows the team conversation, remembers the work, decides when to contribute, and uses explicit capabilities to move software forward like a regular teammate.
Warning
Coworker is a super-alpha release. It proves the smallest useful version of the product: a continuing WhatsApp participant with durable GitHub Issue Management. Use a WhatsApp account and GitHub repository you can safely test with.
Coworker is an experimental, extensible agentic engineer for WhatsApp. Add it to a group where software work is already being discussed and it becomes part of that conversation: it reads the shared context, retains useful working memory, stays quiet when it has nothing to add, and acts when the team needs help.
Today, that help is deliberately narrow. Coworker can develop a bug report or feature request with the group, search for duplicates, create and organize a GitHub issue, participate in its discussion, and close or reopen it. Issue Management is the first complete Capability, not the final product.
The direction is a complete ambient software-delivery teammate:
| Capability | Status | Intended contribution |
|---|---|---|
| WhatsApp Participation | Available now | Follow a Managed Chat, use its history, remain private, or Say once when useful |
| Issue Management | Available now | Develop, find, create, correct, discuss, close, and reopen authorized GitHub issues |
| Code Review | Next | Read pull requests, checks, and discussion; review changes and report actionable work |
| Implementation | Planned | Take a ready issue, change code, run checks, and open a pull request |
| Planning | Planned | Turn product intent into structured, sequenced, reviewable work |
| Delivery | Planned | Coordinate readiness and releases while preserving explicit human approval |
| Cross-chat memory | Planned | Connect relevant context across conversations without weakening chat isolation |
The goal is not an autonomous black box that silently owns production. It is a visible team member that can carry work from intake to implementation and review, while keeping consequential approval boundaries explicit. In particular, merge authority remains a human decision until a narrower, auditable policy is deliberately designed.
Most chat agents wait for a command, answer once, and forget. Coworker is designed around a different pattern.
Every accepted message in a Managed Chat is processed. A mention or direct question can make a Window flush immediately, but no wake word is required. The Speaker decides whether a contribution would help.
Each Managed Chat is mapped to one continuing Speaker instance using the WhatsApp chatId. Later messages return to
the same canonical context rather than creating isolated one-shot conversations.
The model's ordinary prose is private working context. Nothing is posted back to WhatsApp unless the Speaker explicitly
calls the chat-bound say Tool. Thinking and speaking are different operations.
A per-chat Coalescer turns bursts of messages into stable, lossless Windows. The Speaker reads the conversation as a sequence of meaningful moments instead of being interrupted once per message.
The Speaker does not receive one universal command Tool. Each kind of work arrives as a cohesive Capability: a versioned Skill explains the judgment and policy, typed Tools provide direct abilities, and provider adapters remain private.
This is the reusable design pattern. The current product applies it to software delivery in WhatsApp, but the same Coworker + Capability + Admission pattern can support other ambient agentic systems.
flowchart LR
WA["WhatsApp account"] --> ARCHIVE["Conversation Archive"]
ARCHIVE --> GATE{"Managed Chat?"}
GATE -->|No| RETAIN["Retain for future context"]
GATE -->|Yes| INBOX["Managed Chat Inbox"]
INBOX --> COALESCER["Lossless per-chat Coalescer"]
COALESCER --> WINDOW["Stable Window"]
WINDOW --> RELAY["Admission Relay"]
RELAY --> FLUE["Flue dispatch: id = chatId"]
FLUE --> SPEAKER["Continuing Speaker instance"]
SPEAKER --> WA_CAP["WhatsApp Participation"]
SPEAKER --> ISSUE_CAP["Issue Management"]
WA_CAP -->|say| WA
ISSUE_CAP --> GITHUB["Authorized GitHub repository"]
WEBHOOK["Verified GitHub webhook"] --> RELAY
APP_DB[("application.sqlite")] --- ARCHIVE
APP_DB --- INBOX
APP_DB --- RELAY
FLUE_DB[("flue.sqlite")] --- FLUE
The separation is intentional:
| Boundary | What it owns |
|---|---|
| Speaker | Conversational judgment and continuing private context |
| Skill | Versioned process and policy: how to approach a kind of work |
| Tool | One typed, validated application ability with an observable result |
| Host adapter | WhatsApp, GitHub, or another provider's concrete API mechanics |
application.sqlite |
Conversation Events, inbox state, Windows, admissions, webhook receipts, and operation ledgers |
flue.sqlite |
Canonical Agent streams, accepted submissions, and independent run records |
| Managed credentials | WhatsApp session material, GitHub authorization, and ChatGPT OAuth |
ambient-agent executable |
Setup, validation, dependency composition, diagnostics, and foreground runtime lifecycle |
This structure prevents prompt text from becoming application control flow. Skills can guide behavior without gaining new powers; Tools can perform effects without deciding when they are appropriate; provider code can change without rewriting the Agent.
- macOS or Linux
- Node.js 22.19 or newer
- a ChatGPT Plus or Pro account
- a WhatsApp account that can be linked as a companion device
- a fine-grained GitHub token scoped to the repository Coworker may manage
Install the CLI:
npm install --global ambient-agentRun guided setup:
ambient-agent initSetup will:
- open the ChatGPT device-login flow;
- pair or restore the managed WhatsApp session;
- synchronize chats and let you choose the Managed Chat;
- discover or request a GitHub repository and scoped credential;
- verify the selected services; and
- show a final review before installing anything.
Then verify and start it:
ambient-agent status
ambient-agent doctor --live
ambient-agent startAutomatic pull-request review is deployment-gated: a host must bind an isolated
Reviewer sandbox and explicitly list github.reviewRepositories. The packaged
single-host CLI leaves that list empty and never executes untrusted PR code locally.
start runs in the foreground. Use Ctrl-C for a clean stop; let systemd or another process manager own background
supervision in a deployment.
No model API key or application .env file is required. Coworker owns its managed configuration and credentials.
The default data directory is ~/.ambient-agent on every platform (ADR 0015). Override it with
--data-dir <absolute-path>. An installation created at the former platform-native default
(~/Library/Application Support/ambient-agent on macOS, ${XDG_DATA_HOME:-~/.local/share}/ambient-agent on Linux)
is adopted automatically and atomically on the first run; if both directories exist, the CLI fails closed and
prints both paths.
Useful lifecycle commands:
| Command | Purpose |
|---|---|
ambient-agent |
Start the control plane, and the runtime in the same process |
ambient-agent init |
Create a validated managed installation |
ambient-agent auth |
Replace only the managed ChatGPT authorization |
ambient-agent config |
Review or change the Managed Chat, repository, token, or runtime port |
ambient-agent start |
Start the non-interactive foreground runtime |
ambient-agent status |
Inspect configuration, databases, credentials, runtime health, and counts |
ambient-agent doctor |
Diagnose installation integrity and unresolved work |
ambient-agent doctor --live |
Add bounded real GitHub and model readiness checks |
Use ambient-agent doctor for installation integrity and unresolved (Uncertain) work.
ambient-agent with no subcommand binds a control plane on 127.0.0.1:4747 (--control-port to move it)
and keeps it bound for the life of the process, then brings the Flue runtime up in the same process. A runtime
that cannot start is reported by the control plane rather than taking the process down with it, and an
installation that does not exist yet is reported as not configured rather than as an error.
Every route lives under /api/ and requires Authorization: Bearer <token>; GET /api/status reports the
installation state and how the runtime boot went. The token is generated once and stored at
credentials/control-plane.json (mode 0600); it is handed over by file path and never written to a log or
echoed to stdout. (The exception is a control plane started before ambient-agent init: there is no data
directory to persist into, so that first-run token lives for the process only and is printed — to a
terminal, never to a non-interactive stdout, which under a service manager is the journal.)
curl -sS -H "Authorization: Bearer $(jq -r .token ~/.ambient-agent/credentials/control-plane.json)" \
http://127.0.0.1:4747/api/statusThe runtime owns one logging root (ADR 0016). Command responses stay on stdout — status --json and doctor --json
always emit valid JSON there — while diagnostics go to stderr and to rotating files.
- Default output is concise and human-readable on a terminal: startup phases, connected services, the watched chat and repository, actionable warnings, and terminal failures. No raw JSON, no Effect fiber ids, no message bodies.
ambient-agent start --debugraises the level to debug: full diagnostics, inbound message traces, and raw upstream WhatsApp records.ambient-agent start --log-format jsonforces one JSON record per stderr line for a service manager (--log-format prettyforces human lines). Without the flag, a TTY gets pretty lines and everything else gets JSON.- Files: every record is also written as structured JSON to
<data-dir>/logs/ambient-agent.log, rotated at 10 MiB and capped at 5 files, so "what happened last night" survives without journal discipline. - Redaction: tokens, webhook secrets, OAuth codes, and QR payloads are censored at the root before any sink; message text is logged at debug level only.
Output printed before the application starts is outside its control: npm exec/npx install confirmations and
deprecation notices, and Node.js process warnings, cannot be reformatted by Coworker. Suppress the npm install
confirmation with npm exec --yes ambient-agent (or npx -y ambient-agent); silence Node process warnings, if you
must, with NODE_OPTIONS=--no-warnings.
Capabilities are the canonical extension unit. The two shipped examples live together:
src/capabilities/
├── whatsapp-participation/
│ ├── skill-body.md
│ ├── tools.ts
│ └── whatsapp-port.ts
└── issue-management/
├── SKILL.md # not mounted on any agent, so not in the prompt store
├── tools.ts
├── issue-repository.ts
├── operation-store.ts
└── runtime.ts
To add Code Review, for example:
- Add
src/capabilities/code-review/skill-body.mdwith the review policy and its own version. - Define provider-neutral review interfaces rather than exposing Octokit objects to the Agent.
- Add narrowly scoped, typed Tools for direct reads and effects.
- Put real GitHub mechanics in a private adapter under
src/host/. - Configure the adapter from managed dependencies in
src/app.ts. - Add the skill to the shipped prompt catalog (
packages/agents/src/prompts/catalog.ts) and mount it insrc/agents/speaker.tsalongside its Tool factory. - Add deterministic contract tests, behavioral Evaluation Scenarios, and separately gated live evidence.
Registration is explicit—there is no dynamic plugin scan. Skill bodies and instructions resolve from the prompt store (#375), which is seeded from the catalog on boot, so editing one afterwards is an edit, not a release:
import { PROMPT_IDS, storedInstructions, storedSkill } from "../prompts/catalog.ts";
import { createCodeReviewTools } from "../capabilities/code-review/tools.js";
export default defineAgent(({ id }) => ({
skills: [storedSkill(PROMPT_IDS.whatsappParticipationSkill), storedSkill(PROMPT_IDS.codeReviewSkill)],
tools: [...createWhatsAppParticipationTools(id), ...createIssueManagementTools(), ...createCodeReviewTools()],
instructions: storedInstructions(PROMPT_IDS.speaker),
}));- Use a Tool for a direct typed application function: read a pull request, search issues, add a comment, or Say.
- Use an Action when a reusable operation needs its own narrowly instructed agent harness.
- Use a Bounded Workflow for independent, inspectable work such as implementing an issue or performing a substantial review. A Bounded Workflow finishes or fails and returns control to the Speaker; it does not become a second chat participant.
The stable base currently ships Tools only. Actions and software-delivery Bounded Workflows are extension seams, not hidden features.
- Bind chat-specific Tools to
chatIdwhen constructing them; do not accept an arbitrary chat ID from the model. - Keep Instructions short. Put changing process and policy in versioned Skills.
- Keep provider clients behind Capability-owned interfaces.
- Give every external mutation an application-owned Operation Identity before crossing the provider boundary.
- Never turn a lost response into an automatic retry. Observe first; preserve an honest
Uncertainstate when causation cannot be proven. - Store application facts in
application.sqlite; let Flue exclusively ownflue.sqlite. - Test structural integrity deterministically. Evaluate model judgment separately. Record live provider proof as a third, explicit evidence class.
Today, one Managed Chat maps to one continuing Speaker context. WhatsApp history Tools are permanently scoped to that chat, so one group cannot accidentally reach another group's working context.
The Conversation Archive already retains normalized events observed across the configured WhatsApp account, including events outside the Managed Chat. The Speaker cannot currently search that cross-chat history. Cross-chat and cross-thread memory will require an explicit Capability with its own authorization, relevance, and privacy rules rather than silently removing the existing boundary.
git clone https://github.com/AaronAbuUsama/whatsappd-github-agent.git
cd whatsappd-github-agent
pnpm install --frozen-lockfile
pnpm run typecheck
pnpm test
GITHUB_WEBHOOK_SECRET=ci-build-only-secret pnpm run buildThe package exposes dist/cli/main.js as the ambient-agent executable. To test an exact local artifact rather than the
registry release:
pnpm pack --pack-destination ./artifacts
npm install --global ./artifacts/ambient-agent-*.tgz
ambient-agent --data-dir "$HOME/.ambient-agent-dev" initBehavioral evaluations run through the same public Flue HTTP interface used by production. The default command starts fresh fixture processes and runs the exact faux-model mechanics first, followed by the real-model judged suites:
export SPEAKER_FIXTURE_DATA_DIR=/path/to/initialized/non-production/data
export BRAINTRUST_API_KEY=replace-with-a-non-production-key
# Optional: choose an existing project by ID or name; the default name is "Flue".
export BRAINTRUST_PROJECT_NAME="Ambient Agent Evals"
pnpm evalspnpm evals:deterministic runs only the credential-free mechanics family; pnpm evals:live runs only the real-model and
LLM-judge family. Each command creates a run-scoped Braintrust experiment name unless BRAINTRUST_EXPERIMENT_NAME is
set intentionally to append to an existing experiment. SPEAKER_EVAL_PORT can pin the otherwise dynamically allocated
fixture port. Braintrust traces and experiment records are content-bearing, so use a reviewed non-production project and
credential. Live model checks are not substitutes for deterministic tests, and deterministic green checks are not
presented as proof of real provider delivery.
pnpm evals explicitly enables its own content-bearing Flue tracing when BRAINTRUST_API_KEY is present. A production
app process does not trace from key presence alone: set BRAINTRUST_TRACING=1 alongside the key only after reviewing the
destination project and data policy.
Add a Changeset with pnpm changeset whenever a pull request changes the published package. After changes merge to
main, GitHub Actions maintains a Release packages pull request containing the version and changelog. Merging that
reviewed release pull request asks npm to publish through Trusted Publishing; the repository does not store a long-lived
npm token.
npm publishing additionally requires the ambient-agent package to trust this repository and
.github/workflows/release.yml in npm's package settings. That external binding is not proven by the deterministic test
suite. The first successful live publish is the proof that GitHub OIDC and npm registry acceptance work together. A
Changesets pull request created by GITHUB_TOKEN may also require a maintainer to approve its CI run; approve it and wait
for current-head Node 22 and Node 24 checks before merging.
- System architecture — the conceptual system (Brain, Speakers, Graph, Digest, control loop); §13 maps built-vs-designed
- Architecture map — the code taxonomy: which package owns what
- Domain language — the vocabulary used throughout the product and code
- Status — where the reset stands and what's deferred
This release does not yet provide the internal Planner/Coder/Verifier loop, planning, delivery automation, cross-chat Agent memory, media understanding, active-active ownership, horizontal failover, or cross-host recovery. The supported runtime is one foreground Node process owning one managed local installation.
Coworker uses whatsappd, which ultimately connects through the unofficial WhatsApp Web protocol. Automated use
may carry account risk. Use a dedicated account and a repository with appropriately restricted permissions while the
project remains alpha.
The project grows in vertical Capability slices: one useful behavior, its direct abilities, deterministic integrity, behavioral evaluation, and honest live evidence. Open an issue before broad architectural work so the Capability boundary and human approval surface are explicit.
MIT © Aaron AbuUsama