OPEN SOURCE DEEP DIVE
OpenDuet: A Self-Hosted Proactive Personal Agent
A self-hosted proactive personal agent built on OpenBot and CopilotKit, with an isolated computer, governed tool calls, approvals, and auditable execution.
OpenDuet is CopilotKit’s open-source attempt at a proactive personal agent. Instead of waiting for every prompt, it starts the day with a brief, proposes work, and keeps operating on a computer of its own while the user steps away. The project is early and actively changing. Most of its runtime comes from CopilotKit’s OpenBot platform, while the OpenDuet-specific brand, agents, channels, model, and skills live in the openbot/examples/openduet tenant package. The repository is MIT licensed.
The interesting part is not the word “proactive.” It is the governance chain required to make proactivity tolerable. An agent with persistent access to mail, a browser, files, and a shell can cause more damage as it becomes more capable. OpenDuet routes every acting tool call back through a server gateway that resolves the target, evaluates policy, writes an audit row, and only then permits the computer to act. That is the point where an autonomy demo starts becoming an operable system.
A proactive agent needs a real computer
Chat assistants are good at explaining work and poor at continuing a multi-step task after the user closes the tab. OpenDuet gives each agent its own container with Chromium, a persistent browser profile, a /workspace directory, and shell access. The agent can open sites, organize files, run commands, save intermediate results, and continue from the same workspace in a later turn.
That persistence removes a common failure of chat interfaces. A finished turn should not erase the page state, downloaded files, and work completed so far. Login state survives in the browser profile, while artifacts survive in the workspace. A request to research, fill out, and organize something can therefore remain one continuing job. Users can reach the same agent through the web app, mobile, Slack, or Microsoft Teams; OpenBot supplies those surfaces along with identity, team features, and the live interface.
Persistent computers also amplify mistakes. A model that clicks the wrong control, sends the wrong message, or deletes the wrong file now produces a real-world effect instead of a bad paragraph. OpenDuet therefore does not let the agent treat the entire computer as an unmediated tool. Browser, file, and MCP actions return to a server gateway. The computer supplies execution capacity; the gateway retains authority.
The path of one request
After a user submits a request, the OpenDuet server resolves the signed-in person, the selected agent, and the conversation. It sends the turn to the agent over AG-UI. The endpoint can use LangGraph, Mastra, CrewAI, Pydantic AI, Google ADK, or a hand-written implementation. OpenDuet is not locked to one agent framework.
When the agent needs a browser or connector tool, the call comes back to the server. The gateway first resolves an opaque element reference against a server-held page snapshot, so it can determine which real target the model meant by “that button.” It then evaluates the current action policy and decides whether to allow, ask, or hand off the request. It writes an audit row for that decision before acting. Only a forwarded action reaches the computer. If execution fails, the system records a second audit row for the failure.
Each step addresses a different trust problem. Server-side resolution stops the model from inventing or retargeting an element reference. Policy lives outside the prompt, so model output cannot grant itself permission. Audit precedes action, because a successful operation that was never recorded cannot be reconstructed. The ordering is deliberate: approval and evidence are not added after an incident; they are prerequisites for execution.
Policy fails closed, with one important qualification
OpenBot policies use CEL expressions and can inspect the tool name, intent, agent identity, current person, page URL, element role and name, key presses, shell command, file properties, MCP server and tool, and run initiator. Deny rules are evaluated before allow rules. A broken deny rule denies the action, while a broken allow rule does not permit it. Missing and empty policies permit nothing.
The distinction between the policy engine and the shipped startup configuration is easy to blur. The engine fails closed, but OpenBot’s shipped startup default is an explicit deny: [] and allow: ["true"], unless an environment variable or a saved administrator policy replaces it. It is therefore inaccurate to claim that a normal OpenDuet install always starts in universal deny mode. The defensible statement is narrower and more useful: malformed policy cannot silently broaden access, and every deployment must verify the active policy rather than assuming the engine’s failure mode is the installed policy.
| Governance layer | Responsibility | Failure it is designed to prevent |
|---|---|---|
| Server snapshot | Resolve an opaque element reference into the real page target | A model acting on a stale or fabricated target |
| Action policy | Evaluate tools, pages, files, commands, actors, and initiators | An agent granting itself authority through prompt text |
| Audit record | Record the actor, initiator, target, and decision before action | An effect that cannot be attributed or investigated later |
| Human approval | Pause high-impact actions and show the exact proposed operation | Irreversible consequences chosen without a person seeing them |
The audit trail also separates whose authority a run carries from what started it. A scheduled routine acts as its owner, but its initiator is the routine, not the person who happened to be at the screen. Administrators can therefore filter for activity that occurred with “nobody watching.” For a proactive agent, that distinction is more important than the simpler audit question of which account authorized a tool.
Approval is a graded decision, not a master switch
OpenDuet resolves actions through four levels: allow, pre_approved, ask, and hand_off. Ordinary low-risk reads can proceed directly. Actions that require consent pause and show the exact target, content, and rule that triggered the request, typically with Refuse, Allow once, and Always allow. Credentials, security settings, and payments always hand off to the person.
Pre-approval still uses a reviewer. The system checks that the current request actually asks for the action and target covered by the existing grant. Reviewer failure, invalid output, or timeout becomes a visible request for approval. Messages to other people always ask once and show the recipients and text. The design recognizes that consent belongs to a bounded situation; a model must not convert one approval into a permanent universal grant.
Secrets use a separate path. When a site asks for a password, the person types it into a masked secret field. The value reaches the site without entering the model-readable transcript. Stored credentials are encrypted, write-only, never returned by the API, and redacted from audit events. The audit can state that a secret was requested or supplied and how many characters it contained, but not the secret itself.
Human takeover handles login walls and two-factor prompts. While a person controls the browser, agent actions are refused rather than queued. Handing control back requires a fresh snapshot of the page. This prevents the agent from clicking alongside the person or acting on a stale view created before the human intervention.
An isolated computer still needs a network boundary
OpenDuet supports four network modes: allow all, defaults plus an allowlist, allowlist only, and deny all. Cloud metadata and link-local addresses are denied in every mode. A newly started computer cannot make outbound connections until the server pushes its policy. Those controls reduce the chance that a hostile page can steer the agent toward cloud credentials or internal services.
The filtering layer is a proxy inherited by the browser and shell commands, not a kernel-level network jail. A process that ignores the proxy variables and opens a raw socket is outside its reach. The project documentation says Kubernetes NetworkPolicy is needed for that layer. A serious deployment should treat application proxy policy and container network policy as two separate boundaries.
The computer container is also separated from PostgreSQL. Because the agent has a shell, it can reach whatever its container can reach. The database therefore sits on a separate data network with only itself and the migration service. If the API server runs inside Compose, it joins both networks and becomes the place where they meet, which deserves explicit review. On compatible hosts, the computer runtime can use gVisor through runsc for a stronger isolation boundary.
How the morning brief becomes a task inbox
The first concrete workflow is Gmail and Google Calendar. Each person connects an account. OpenDuet reads mail and the current day’s calendar, separates messages that need the person from messages that can wait, and gives a one-line reason for each. The morning brief is created the first time Chat is opened each day. It summarizes what needs attention, what can wait, the calendar, and anything the system could not check. It is published once even when several tabs and devices are opened.
Triage is not entirely delegated to the language model. Rules can mark a message as likely to wait, but uncertain or potentially important mail goes to the model. Failed triage appears under “needs you” rather than disappearing. The brief reads at most fifty messages and reports when more remain. These choices keep the system from presenting a partial scan as a complete understanding of the inbox.
Only Start turns a brief row into work. The Tasks surface separates items that need an answer, background suggestions, active work, results ready for review, and runs waiting for input. Research can create a suggestion, but a suggestion does not start itself. Even a tool call that creates several task cards accepts only one to eight cards and starts none of them; an explicit user action is still required.
Routines have similarly concrete limits. A routine runs as the person who created it, has a minimum interval of fifteen minutes, and is capped at twenty enabled routines per person. Ten consecutive failures disable it. A missed execution window is skipped rather than replayed when the worker recovers. For work that can affect the outside world, replaying delayed events could be more dangerous than doing nothing.
Proactivity rests on ordinary systems engineering
OpenDuet also includes Tasks, routines, governed MCP connectors, Google Drive and Notion integrations, artifacts made through conversation, Slack and Microsoft Teams channels, and reusable skills invoked with a slash command. Underneath, proactive behavior is produced by ordinary persistence, scheduling, permissions, and state-management components.
Threads and memory live in CopilotKit Intelligence. Policies, approvals, audit rows, and encrypted credentials live in PostgreSQL with pgvector. The default model is gpt-5.6-terra; Anthropic models and external AG-UI agents are also supported. Self-hosting requires Docker, Bun 1.3 or newer, Node, a model key, and a CopilotKit Intelligence project. Intelligence is required because it stores threads and memory, and the server will not start without its key. The free Developer plan is sufficient.
The repository reflects that split. The server, app, routines worker, computer containers, Docker files, and Helm files are under openbot/. OpenDuet’s own brand, agent instructions, channels, model, and skills are much smaller and live in one tenant directory. A team can copy that directory, change TENANT_PACKAGE_DIR, and build a different assistant while continuing to receive fixes from the OpenBot foundation.
What the triage evaluation establishes
The repository includes a regression set of invented email messages for checking whether triage routes work correctly. On one set of sixty-two messages, Claude Sonnet 5.5 reached 100% precision, recall, and accuracy. On an earlier set of sixty, GPT-5.6 Terra reached 96.2% precision and 100% recall. Rules alone decided twelve of the sixty-two messages, with no observed errors.
Those figures test a bounded regression suite; they do not establish performance on a real inbox. The messages were invented for evaluation and cannot represent every company’s email culture, long threads, attachments, hostile content, or distribution shift. A team preparing to deploy OpenDuet should build a separate, redacted set from its own messages. It should also price false positives and false negatives differently: missing an important message damages trust, while repeated false alarms make people ignore the brief altogether.
Who should use it, and what it cannot promise yet
OpenDuet is aimed at developers and small teams willing to run the service, configure a model and Google OAuth client, and take approvals and audit seriously. It is useful for studying how a proactive agent gets a persistent computer and how unattended execution connects to human authorization. It is also a viable base for a customized personal work agent. It is not yet a turnkey hosted consumer product.
The project’s early stage should be stated plainly. OpenDuet does not publish its own image or deployment guide yet. The Docker and Helm guides deploy plain OpenBot, not a version with the OpenDuet tenant layer already applied. Changes to the computer, gateway, approvals, credentials, or connectors usually belong upstream in OpenBot, and contributors are asked to open an issue before substantial work. Anyone using it around real payments, legal documents, or sensitive company data still needs key management, backups, network policy, compliance review, and incident response of their own.
The broader thesis is concise. A proactive agent becomes useful only when unattended execution is governed as carefully as the capability that enables it. A dedicated computer lets it continue working. The gateway prevents it from expanding its own authority. Approval returns irreversible consequences to a person. Audit turns “what did it do?” into an answerable question. For an early open-source project, that architecture matters more than the number of automated entry points.