Files
PaperClipAI/doc/plans/2026-10-08-cloud-dot-runner-and-external-invitations.md
T
DottaandPaperclip 556daf9a99 feat(dot): run cloud assignments in managed sandboxes (#15691)
## Thinking Path

> - Paperclip manages agents and governs their work.
> - The Runner owns execution and task authority.
> - Dot executes at OpenAI and uses Paperclip through MCP.
> - Dot previously required a self-hosted controller.
> - Cloud must admit Dot work in a managed sandbox and keep protocol
traffic in the tenant.
> - This pull request adds managed Dot execution and dedicated pairing
routes.
> - The same Runner path can later host sandboxed workspace tools.

## Linked Issues or Issue Description

**Agent or provider**

OpenAI Dot, through the existing dedicated MCP adapter. Related changes:
#15402, #15414, and #15581.

**Why this adapter is useful**

Cloud users need to assign work to Dot and let Dot request work while
idle. Neither path should execute agent commands on the control-plane
host or require an idle sandbox.

**How the agent is invoked**

The tenant serves OAuth, signed mailbox events, and scoped tools. Normal
admission creates a managed sandbox for each assignment. A verified Rust
Runner owns that assignment and relays its tools and results.

## What Changed

- Enable the external-agent invitation in Cloud behind the standalone
Dot flag.
- Launch Dot through the existing managed process launcher and
authenticated PRP transport.
- Verify the artifact digest and Dot capability before dispatch. Read
provider checkpoints from the execution target for recovery.
- Reject local execution in Cloud and reject managed workspace access
for this release.
- Add dedicated public Dot pairing aliases. Preserve the personal
assistant consent paths.
- Keep Dot events in the tenant when a personal Cloud broker is
configured.
- Use the app-configured heartbeat for Dot-initiated work. Report
terminal admission failures and fence stranded offers before new
admission.
- Offer normal comments through the active assignment's mailbox. Keep
their wakes deferred until a successful history receipt contains the
exact IDs; preserve unread comments through finish, batching and
recovery without replaying consumed input.
- Require a sandbox process fingerprint before dispatch and allow a
bounded five-minute controller reconnect gap. Preserve remote process
birth without inspecting a colliding controller PID.
- Recognize authenticated remote shutdown receipts even without an OS
exit code.
- Document dedicated pairing aliases and teach route coverage to inspect
literal path arrays.
- Add opt-in private Daytona qualification and focused contract tests.

## Verification

- Workspace typecheck, production build, and UI token gates passed.
- Focused onboarding, OAuth, managed environment, artifact metadata, and
terminal-fence tests passed. One environment-test timeout passed on an
isolated repeat.
- A private Daytona fixture passed with the real Rust Runner, signed
readiness, assigned app tools, document writes, duplicate receipts,
authority fences, completion, and sandbox deletion.
- A real OpenAI Dot completed both an operator-assigned task and a
Dot-initiated task in managed Daytona sandboxes. Both leases were
released.
- A real accepted Dot assignment survived a controller gap longer than
one minute with the same sandbox process, Runner, session, and
assignment. Dot consumed the normal task-comment follow-up and completed
exactly once; the lease was released.
- A real cancellation fenced an accepted assignment, released its lease,
and received Dot's acknowledgement without claiming a global provider
stop.
- Final local checks: 74 Dot execution, OAuth onboarding and API
contract tests after integrating the current agent-lifecycle changes, 9
real Rust driver tests, 21 remote-runtime tests plus the 3
detached-process cleanup tests, and 17 lifecycle tests passed. The final
live Daytona fixture passed and deleted its sandbox.
- A final real Dot follow-up stayed pending until its exact comment
appeared in a successful history receipt. Dot completed the original
managed assignment once; its lease was released.
- The broad local suite crossed a rebase and was stopped; its failures
are not a qualification of this final source. Focused repeats passed.
The final head passed all CI gates: workspace types, build, general and
serialized test suites, Rust and Runner verification, all browser
shards, and the canary dry run. Greptile is 5/5 with both findings
resolved.

## Risks

- Deploy the tenant app with its current Linux Runner artifact and the
corresponding front-door protocol routes together.
- This branch has not been deployed to a Cloud tenant. A deployed tenant
still needs an OAuth and assignment smoke test.
- Dot provider usage and cost are unmetered. Revocation fences Paperclip
tools and does not prove a global OpenAI stop.
- Missing or changed recovery identity fails closed. No replacement Dot
thread is invented.
- No schema migration is added. Managed Dot workspace access remains
unavailable.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, tool use, code execution,
and browser testing. The exact runtime model identifier and
context-window size are not exposed in this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` /
`Closes: #` / `Refs: #` OR (b) described the issue in-PR following the
relevant issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-09 12:58:35 -05:00

14 KiB

Cloud Dot runners and external-agent invitations

Date: 2026-10-08

Status: managed Dot execution and dedicated tenant ingress are implemented. The live invitation flow remains shared across self-hosted and Cloud instances. Private Daytona qualification passed with the actual Rust Runner, signed readiness, task tools, document writes, and normal finalization. A real OpenAI Dot also completed an operator-assigned task through a managed Daytona Runner. Coordinated tenant-app and Cloud front-door deployment is still required; these local qualifications are not evidence of a deployed Cloud rollout.

Decision: remote agents use the new Runner infrastructure

Keep Dot and future remote-agent drivers within the new Rust Runner infrastructure. A remote agent can do its thinking and computer work elsewhere while Paperclip still owns admission, assignment lifecycle, tool authorization, receipts, progress, cancellation, and completion through the same Runner contract used by other agents.

There is also a deliberate future benefit: we may give remote agents tools that access a Paperclip workspace. Those tools require sandbox isolation. Keeping remote agents on the Runner infrastructure gives those future tools an established execution boundary, workspace lifecycle, and authorization path. We should not need to invent a second execution system when a remote agent gains workspace capabilities.

Where the Runner runs and which tools the agent receives are independent decisions. Running a Runner in a sandbox does not itself grant Dot filesystem or shell access. The tool catalog and per-run grants determine that access.

For the first cloud implementation:

  • Dot uses its own OpenAI-hosted computer. Do not expose Paperclip workspace file or command tools to it.
  • Start one ordinary managed Runner execution job for an admitted assignment, using the configured cloud sandbox provider (for example, Kubernetes or Daytona). Reuse the job for that assignment's messages and tool calls.
  • Do not start a new sandbox for each message or tool call. Do not require an idle, always-running sandbox just to receive Dot traffic.
  • Do not run agent-provided commands on the Paperclip control-plane host. Future workspace tools execute inside the assignment's managed sandbox and require explicit capability grants.
  • Keep public MCP, OAuth, subscription handling, and durable mailbox state on the control plane. These remain reachable while no Runner job is active.
flowchart LR
  User[Operator assigns work] --> CP[Paperclip control plane]
  CP -->|admit assignment| Job[Managed sandbox: Rust Runner]
  Job -->|offer, progress, receipts| CP
  CP -->|MCP Events notification| Dot[Dot on OpenAI's computer]
  Dot -->|MCP inbox and tool calls| CP
  CP -->|authorized assignment operations| Job
  Job -.->|future explicitly granted tools| Workspace[Isolated Paperclip workspace]

The control plane authenticates inbound Dot requests even when no assignment exists. Idle reads and connection checks need no sandbox. A request to start work creates a visible task and passes normal admission before launching a job. During an assignment, the control plane routes operations to its owning Runner through the durable bridge. Progress and completion return through normal Runner bookkeeping. After completion or cancellation, tear down the job according to the managed execution policy; keep the binding and mailbox.

This preserves one lifecycle without promising that every provider performs identical work. For Dot, much of the driver coordinates a remote service. For a local coding agent, the driver also supervises a local process. Workspace access can be added later without weakening the boundary between the control plane and execution.

Invitation walkthrough

Entry: New agent → Invite an external agent → Dot / Hermes / Other.

The picker uses square cards matching the harness picker, with the Dot and Hermes brand marks. The shared dialog animates content-height changes without scaling text and respects reduced-motion preferences. The Dot option is governed by the standalone Dot experimental setting and its required Assistant connections (MCP) dependency in the live controller. Experimental infrastructure prerequisites belong in settings, not as a list of implementation details in this invitation.

  1. Choose Dot. Paperclip creates a company/agent-scoped invitation and a short-lived pairing prompt.
  2. Show Copy setup prompt, using the shared AgentSetupPrompt component with the Dot mark. Tell the user to send the whole prompt to their Dot in ChatGPT. The preview remains inspectable and offers selectable text if clipboard access fails.
  3. Dot follows the prompt to install or reuse the private plugin, complete scoped OAuth consent using the one-use pairing code, read its inbox, and subscribe to updates. Retain the exact user-approved plugin-creation consent wording from the existing prompt. The pairing code must remain transient and must not be logged, committed, or stored in browser persistence.
  4. Paperclip watches the connection and updates three checks in place:
    • Connected to Paperclip — the scoped OAuth/binding connection is established.
    • Task updates enabled — the event subscription/callback is verified.
    • Test event confirmed — Paperclip sent a readiness challenge through the event path and the paired Dot explicitly confirmed it through MCP.
  5. Show Your Dot is connected and Done only after the round trip is confirmed. A successful copy, OAuth connection, or subscription alone is insufficient.

The event delivery worker automatically issues the readiness test once the subscription is verified. It must be idempotent across refreshes/reconnects, correlate confirmation to the current binding and challenge, and never mark a replacement binding ready using an old confirmation. Use server events or bounded polling with reconnect/revalidation. UI timers are not evidence of success.

When the event test times out, preserve completed checks and offer Retry test event. When the pairing code expires, automatically prepare a fresh prompt, invalidate the old prompt, and make the new one available to copy. When watching is interrupted, state that updates are paused and offer Reconnect updates; do not imply Dot itself disconnected. Returning to the picker must preserve a pending invitation rather than silently create duplicate agents or codes. Closing the dialog must not revoke an established connection.

The managed invitation authorizes only the selected agent in the selected company; it does not grant operator access. OAuth scope preview and consent remain part of connecting. This flow should not require the operator to manipulate Dot's computer or manually run an event test.

Hermes and Other

Hermes is a named choice with its existing brand mark. No new Hermes protocol, tools, transport, authentication, or setup behavior is part of this work. Hermes and Other use the same existing buildAgentOnboardingPrompt output, including its Hermes Gateway guidance and existing board approval/key-claim sequence. They do not show Dot's MCP event checks or claim readiness from a copied prompt.

Reviewable Storybook surface

Folder: ui/storybook/stories/external-agent-invite/.

  • Onboarding / External agent invitation / Journeys: entry, picker, copy handoff, individual watched states, success, timeout/retry, expiration, interrupted updates, Hermes, Other, mobile, light theme, and long company names.
  • Onboarding / External agent invitation / Components: picker and each controlled connection-check state.
  • Interaction stories exercise copy → watched progression → success, retry without discarding completed checks, and measured intermediate heights while the modal expands and contracts.

The shared presentation lives in ui/src/components/new-agent/ExternalAgentInviteContent.tsx. Storybook alone supplies timers and fixture credentials at .example URLs. It uses the live Dot prompt unchanged, including automatic readiness testing. The prompt optionally asks Dot to upload its own avatar using paperclip_dot_set_avatar; avatar availability does not block pairing. Hermes and Other reuse the production invitation builder without modifications.

Run pnpm --filter @paperclipai/ui storybook, then open the journey group. Copying in the interactive Dot story advances simulated checks; the fixed-state stories remain still for inspection. These stories illustrate the live UX with simulated connection evidence; they do not prove a live cloud pairing.

Managed implementation and qualification

The live invitation controller, owner-scoped pending-invitation lookup, atomic creation, pending-code replacement, automatic event test, feature gating, and watcher are implemented. Active run controllers poll durable operation rows under their current controller generation and lease, so an operation reserved by another HTTP replica reaches its owning Runner. Idle-initiated work already uses normal admission.

Managed dispatch now uses the existing remote launcher, artifact verification, authenticated PRP transport, sandbox lease, and normal finalization. Cloud rejects local/legacy execution before startup. The current artifact must advertise the Dot capability; the older pinned image used during qualification required an explicitly staged current Linux artifact. Dedicated tenant OAuth and pairing routes are public protocol lanes, with no minted Cloud browser identity. Shared personal consent and management routes retain their existing session gate. The optional external launcher uses target-owned provider checkpoints and refuses recovery when identity or state is missing; it does not invent a replacement Dot thread. Preserve company isolation, approval rules, budget stops, assignment authority, and mutation receipts. Qualify real OAuth installation, event round trip, assignment execution, unsolicited Dot work, restart/reconnect, cancellation, and expired/revoked credentials against a real sandbox provider. No cloud-ready claim follows from this Storybook pass.

Implemented invitation semantics

  • POST /api/companies/:companyId/dot-invitations finds or creates one unfinished Dot invitation for the signed-in operator. Creation holds a company row lock and commits the agent, membership, grants, approval (when required), and audit entries atomically. The fixed preset disables workspace/attachment access and periodic wakes; normal on-demand admission remains available.
  • GET on that path returns the operator's unfinished invitation without a pairing secret. The dialog's existing binding query reports pairing/test expiry and agent approval state.
  • Pairing codes stay in component memory and out of React Query's mutation results. After refresh, the UI checks current connection state and automatically creates a fresh prompt for an unfinished pairing. Expiry while the dialog is open also renews automatically; failures offer a retry without an automatic retry loop. Replacing a prompt requires the exact pending binding ID and its issuing operator; a concurrent completed OAuth connection is not revoked.
  • The delivery worker creates one readiness challenge after callback verification under the binding row lock. Refreshing, reconnecting, or repeating a worker tick does not replace that challenge. An expired test needs an explicit retry. A revoked generation cannot confirm it.
  • Hermes and Other share the existing external-agent invitation API and unchanged onboarding prompt. Dot is no longer a separate card in the harness picker.

Verification: a disposable authenticated local instance exercised the natural New Agent entry, Dot prompt copy, Back, Hermes, refresh/resume, and pending-prompt replacement. Signed callback verification, nonce confirmation, scoped OAuth, actual Rust execution, duplicate receipts, and recovery have automated coverage. No real OpenAI Dot or managed cloud sandbox was connected during this implementation pass.

Release qualification (2026-10-09)

  • A fresh private Daytona sandbox ran the current Rust Runner through provider preview ingress. Signed event readiness, app-tool relay, a document write, duplicate mutation receipts, authority fences, completion finalization, and deletion passed.
  • The existing approved OpenAI Dot connection received an operator assignment, accepted it, saved and verified a task document, and completed through the normal Runner result path. A Dot-initiated idle request also completed through normal managed admission after the broker was wired to the app-configured heartbeat service. Both managed execution leases expired after completion. Failed admission receipts now report the terminal outcome instead of telling Dot to wait indefinitely. New admission also retires stranded offers whose runs are terminal, with a durable authority fence and no claim that OpenAI globally stopped.
  • Cloud front-door tests cover exact protocol paths, unauthenticated code-based pairing, stripped spoofed Cloud identity headers, wrong methods, unrelated consent/management routes, and unregistered tenant hosts. Public static access is limited to fingerprinted build assets and the displayed provider brand images.
  • Onboarding tests preserve personal assistant behavior and reject personal OAuth requests through dedicated Dot aliases. The invitation is available under the standalone Dot experimental flag in Cloud.
  • A real accepted Dot assignment survived an intentional controller gap longer than one minute in Daytona. The sandbox PID, process birth/fingerprint, Runner instance, durable session, and assignment stayed unchanged; controller generation advanced to 2. Dot received the ordinary issue-comment follow-up, wrote/read its exact report, and completed the original run. One mailbox reference and one coalesced wake were recorded; no second run appeared, and the lease expired. Launch now requires verifiable process identity before dispatch and allows a five-minute reconnect gap. Missing process evidence fails closed.
  • A real cancellation fenced an accepted assignment, expired the managed lease, and received Dot's control acknowledgement. The receipt correctly kept externalStopConfirmed: false. Clean remote shutdown is recognized by its authenticated receipt even when the process monitor has no OS exit code.
  • Deploy both components together and smoke-test a real Cloud tenant before enabling broadly. No long-running idle sandbox or Paperclip workspace access is added. These managed tests qualify the implementation and real provider, not a deployed Cloud tenant.