Skip to content

feat(providers): add Devin CLI and Devin Cloud as first-party providers - #7531

Open
OxFrancesco wants to merge 12 commits into
pingdotgg:mainfrom
OxFrancesco:feat/devin-providers
Open

feat(providers): add Devin CLI and Devin Cloud as first-party providers#7531
OxFrancesco wants to merge 12 commits into
pingdotgg:mainfrom
OxFrancesco:feat/devin-providers

Conversation

@OxFrancesco

@OxFrancesco OxFrancesco commented Aug 19, 2026

Copy link
Copy Markdown

Summary

T3 Code has no adapter for Devin, so Devin subscribers can't drive it from T3. This PR adds two providers.

Devin CLI (devin). Wraps devin acp. Streams assistant and reasoning text, maps tool calls and permission approvals, turns ACP form elicitation into structured questions, reads context usage from usage_update, shows plan-mode proposals, discovers models and skills from the CLI, and backs auxiliary text generation. Sign-in is lazy. When a prompt fails with auth-required, the adapter runs the CLI's advertised devin-browser flow, restarts the ACP session silently, and retries the prompt. The restart is not optional: sessions created before sign-in never pick up new credentials. I verified that against the live CLI.

Devin Cloud (devinCloud). Creates and resumes hosted Devin sessions through the organization REST API and polls session messages into the thread. The model picker maps to the API's devin_mode: Devin, Devin Fast, and the preview modes Lite, Ultra, and Fusion. The mode locks in at session creation, so changing it takes a new thread. The session id and message cursor persist in the thread's resume cursor, so detach, reconnect, and app restarts work. When the Devin CLI is signed in on the server machine, the provider reuses that credential and resolves the organization via /self, so no service-user key is needed.

Walking the AGENTS checklist: server drivers, adapters, and typed contracts; web icons, settings, model picker, composer, and context window; mobile icons and model options; user docs (providers-devin.md, providers-devin-cloud.md) plus internals docs and the glossary; the marketing harness grid. The all-drivers registry test now boots all seven drivers. Both providers ship off by default following #7459, so users opt in from Settings.

Tests pin the protocol behavior: a fake ACP peer suite plus elicitation, provider, adapter, and credential unit tests.

Three bot review rounds are folded in; every finding checked out against the source and got fixed. The notification consumer forks into the session scope, so streaming survives startSession returning. A regression test runs startSession in a fiber that completes and failed on the old code. Cloud polls read session status before messages, so a settled turn keeps its last output. Messages that arrived while detached replay on the next turn, and each poll advances the persisted cursor so a restart does not replay them twice. The lazy sign-in restart no longer leaks a mid-turn session.exited. Elicitation answers convert back to their declared number and boolean types. The cloud modules follow the Effect service conventions: errors keep their cause, operation is a literal union, and HttpClient comes from the requirements channel. Session creation also stopped sending platform: "t3-code", which the API defines as the VM/outpost selector and rejects with a 400.

Test plan

  • vp test run on all Devin test files + ProviderInstanceRegistryLive.test.ts + contracts/server settings tests
  • Targeted typecheck: server, contracts, web, mobile
  • Live verification against the real Devin CLI (browser sign-in, streaming, approvals) and the real cloud API (session create, resume, CLI-credential fallback)

Built with Claude Fable 5 High via Devin CLI.

Generated with Devin


Note

Medium Risk
Large new provider surface (ACP process management, auth restart, and external API/credential handling) with broad client/docs touchpoints; behavior is heavily tested but mistakes could affect threads, streaming, or credential handling.

Overview
Adds Devin CLI and Devin Cloud as first-class providers end-to-end: contracts/settings (off by default), server drivers/adapters, web/mobile/marketing surfaces, docs, and expanded ACP mock behaviors for tests.

Devin CLI wraps devin acp with ACP session lifecycle, resume cursors, runtime/interaction mode mapping, streaming (assistant, reasoning, usage), tool approvals, form elicitation → user-input events, plan-mode exit_plan_mode → proposed plans, lazy browser sign-in with session restart/retry, CLI model/skill discovery, and auxiliary text generation.

Devin Cloud talks to api.devin.ai to create/resume org sessions, poll messages into runtime events, and persist resume cursors (including message cursors). Credentials can come from settings or the local Devin CLI credentials.toml plus /self org resolution; cloud auxiliary text generation is explicitly unsupported.

Reviewed by Cursor Bugbot for commit e5027b1. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add Devin CLI and Devin Cloud as first-party provider drivers

  • Registers two new ProviderDriver implementations: DevinDriver (ACP-backed CLI) and DevinCloudDriver (REST polling against the Devin Cloud API), both disabled by default in settings.
  • DevinDriver spawns and manages the Devin CLI process over ACP, streams runtime events, handles plan/implement/approval interaction modes, supports resume cursors, and exposes a devin update --force maintenance action.
  • DevinCloudDriver lazily resolves credentials from explicit settings or the local Devin CLI sign-in, creates or resumes remote sessions, polls for messages, and streams ProviderRuntimeEvents; auxiliary text-generation methods (commit messages, PR content, etc.) deterministically fail with TextGenerationError.
  • Adds DevinSettings and DevinCloudSettings schemas to ServerSettings with full patch support, and registers devin/devinCloud driver kinds in the contracts package with default models and display names.
  • Surfaces Devin and Devin Cloud in the web settings UI, chat icon map, provider picker, mobile icon renderer, and marketing homepage.

Macroscope summarized e5027b1.

OxFrancesco and others added 6 commits August 19, 2026 12:23
Integrates `devin acp` as a first-party driver across server, web, and
mobile: streaming (assistant + reasoning), tool calls, permissions,
structured questions via ACP form elicitation (`_session/elicitation`),
context usage from `usage_update`, plan-mode proposals via
`exit_plan_mode`, model/skill discovery, text generation, and settings.

Sign-in is lazy: prompts failing with auth-required trigger the CLI's
advertised `devin-browser` flow, then the ACP session is restarted
(pre-auth sessions never pick up new credentials) and the prompt
retried. Startup never auto-authenticates, since Devin's only method
opens a browser.

Protocol behavior verified against the live Devin CLI and locked in
with a fake-ACP-peer suite plus elicitation/provider unit tests.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Devin variant labels are self-describing ("Claude Fable 5 High"), but
setting `subProvider` to the family made the picker strip it from the
row title, leaving only the effort qualifier ("High"). Drop the
subProvider so Devin rows read like other providers: full model name
with the provider as the subtitle.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Follows the provider-enabled policy from pingdotgg#7459: only stable bindings
probe on every install, so Devin and Devin Cloud start disabled and
users opt in from Settings.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: dfbac3e7-aefe-4cfe-a967-ec4c15cef4ab

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 19, 2026
Comment thread apps/server/src/provider/Layers/DevinCloudAdapter.ts
Comment thread apps/server/src/provider/acp/DevinAcpElicitation.ts
Comment thread apps/server/src/textGeneration/DevinTextGeneration.ts
Comment thread apps/server/src/provider/Layers/DevinAdapter.ts
Comment thread apps/server/src/provider/Layers/DevinAdapter.ts Outdated
Comment thread apps/server/src/provider/Layers/DevinCloudAdapter.ts
Comment thread apps/server/src/provider/Layers/DevinProvider.ts Outdated
Comment thread apps/server/src/provider/Layers/DevinCloudAdapter.ts
Comment thread apps/server/src/provider/Layers/DevinProvider.ts
Comment thread apps/server/src/provider/Layers/DevinCloudAdapter.ts Outdated

@macroscopeapp macroscopeapp Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new Devin/Devin Cloud Effect modules against the service conventions. Most of the new code follows the established provider patterns (namespace imports, Effect.fn constructors, catchTags/structural catchIf, driver-owned dependency acquisition). Three findings, all in the new Devin Cloud modules, are noted inline: a service error that discards its underlying cause, an HttpClient service instance passed as a constructor parameter, and a fabricated cause on a pure domain error.

Posted via Macroscope — Effect Service Conventions

Comment thread apps/server/src/provider/DevinCloudApi.ts Outdated
Comment thread apps/server/src/provider/DevinCloudApi.ts Outdated
Comment thread apps/server/src/provider/Layers/DevinCloudAdapter.ts Outdated
Comment thread apps/server/src/provider/Layers/DevinAdapter.ts
Comment thread apps/server/src/provider/Layers/DevinCloudAdapter.ts
Comment thread apps/server/src/provider/Layers/DevinCloudAdapter.ts
Comment thread apps/server/src/provider/DevinCloudCredentials.ts Outdated
@macroscopeapp

macroscopeapp Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Skipped

Macroscope did not run approvability analysis for this PR. Diff is too large for automated approval analysis, so this PR cannot be approved automatically.

Not approved because:

  • 2 blocking correctness issues found at or above your repo's Minimum Blocking Severity

Adjust the Minimum Blocking Severity for this repo — including turning it Off — in Settings. You can add or adjust custom eligibility rules. Learn more.

Devin CLI: the ACP notification consumer is forked into the session scope
instead of the startSession fiber, so streaming survives the request that
started it (with a regression test mirroring GrokAdapter's); the active
turn id is bound before cooperative yields so a concurrent sendTurn steers
instead of opening a second turn; numeric and boolean elicitation answers
are converted back to their declared types; title regeneration passes the
previous title; skills with errors no longer surface slash commands; a
failed or timed-out auth probe reports auth as unknown instead of telling
a signed-in user to log in.

Devin Cloud: polls observe the session status before reading messages so
the last messages of a settled turn are not dropped; messages that arrived
while detached are delivered as a backlog on the next turn instead of
being swallowed; readThread records messages on their turns; settled and
interrupted sessions clear activeTurnId and successful turns clear
lastError; resume cursors are validated before the previous session is
stopped; CLI credentials are also probed under %APPDATA% for Windows.

Effect conventions: DevinCloudApiError carries the underlying cause and a
literal operation union, makeDevinCloudApi and resolveDevinCloudCredentials
take HttpClient from the requirements channel, and the unsupported() helper
no longer fabricates a cause.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@OxFrancesco

Copy link
Copy Markdown
Author

Reviewed every bot finding against the source. All of them were real, and 1ea463e fixes the lot:

Devin CLI

  • Effect.forkChild on the notification consumer (Bugbot + Macroscope, High): forked into the session scope instead, same fix and comment as GrokAdapter, plus a regression test that runs startSession in a fiber that completes.
  • Concurrent sendTurn race on activeTurnId: the turn id now binds before the first cooperative yield, matching GrokAdapter.
  • coerceAnswer sent strings for number/integer/boolean elicitation properties: answers now convert back to the declared type, with tests.
  • generateThreadTitle dropped input.previousTitle: now passed, like every sibling provider.
  • Skills with errors still surfaced slash commands: gated on enabled.
  • Failed or timed-out auth probes reported unauthenticated: they now report unknown with a warning message instead of telling a signed-in user to log in.

Devin Cloud

  • Poll read messages before checking status, dropping a settled turn's last messages: the status check now comes first.
  • Follow-up turns swallowed messages that arrived while detached: with a persisted cursor they now replay as a backlog under the new turn. Without a cursor the delivered/undelivered split is unknowable, so the history is only marked seen to avoid replaying the whole remote session.
  • readThread() always returned empty items: emitMessage records each message on its turn.
  • Stale activeTurnId / lastError on settled sessions: cleared in finishTurn, the poll error handler, and interruptTurn.
  • startSession stopped the previous session before validating the resume cursor: validation now runs first.
  • Windows CLI credentials: %APPDATA%\devin\credentials.toml is probed alongside the XDG path.

Effect conventions (Macroscope check)

  • DevinCloudApiError now carries cause: Schema.optional(Schema.Defect()) (matching provider/Errors.ts) and a literal operation union.
  • makeDevinCloudApi and resolveDevinCloudCredentials take HttpClient from the requirements channel; tests provide a stub via Effect.provideService.
  • unsupported() no longer fabricates a cause.

Not fixable from here: the Vercel t3code-marketing check fails with "Authorization required to deploy", which needs a maintainer to authorize the fork deploy.

Verified with vp test run on all Devin suites + ProviderInstanceRegistryLive.test.ts (61 tests) and a server typecheck. The new forkChild regression test was checked red on the old code, green on the fix.

Comment thread apps/server/src/provider/Layers/DevinCloudAdapter.ts Outdated
Comment thread apps/server/src/provider/Layers/DevinCloudAdapter.ts Outdated
const exitPlanMarkdown =
exitPlanMarkdownFromToolCall(parsePermissionRequest(params).toolCall) ??
exitPlanTexts.get(params.toolCall.toolCallId);
if (exitPlanMarkdown !== undefined) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium Layers/DevinAdapter.ts:729

Every switch_mode permission request with a non-empty rawInput.plan is emitted as turn.proposed.completed and rejected, even when it comes from implementation mode or is not the exit-plan operation. This prevents normal switch-mode requests from being approved; restrict this branch to the actual exit-plan operation in a turn whose interactionMode is plan.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Layers/DevinAdapter.ts around line 729:

Every `switch_mode` permission request with a non-empty `rawInput.plan` is emitted as `turn.proposed.completed` and rejected, even when it comes from implementation mode or is not the exit-plan operation. This prevents normal switch-mode requests from being approved; restrict this branch to the actual exit-plan operation in a turn whose `interactionMode` is `plan`.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dismissing this one. The rawInput.plan gate is the exit-plan signature, verified against the live CLI: Devin only attaches plan markdown to the exit_plan_mode tool call (kind switch_mode), and ordinary mode-switch permission requests carry no plan payload, so they fall through to the normal approval path untouched. Gating on the turn's interactionMode would also break the case where the user asks for a plan in a default-mode turn and Devin switches itself into plan mode before proposing. See the doc comment on exitPlanMarkdownFromToolCall.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I'm unable to act on this request because you do not have permissions within this repository.

OxFrancesco and others added 3 commits August 19, 2026 20:01
Two follow-up findings from the second review round: the persisted resume
cursor now advances with each poll so a restart does not replay messages
the poll already delivered, and a publish failure between the remote send
and the poll fork resets the local turn instead of blocking every later
sendTurn on a turn that has no poller.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
(previousMessage) => !context.seenMessageIds.has(previousMessage.event_id),
);
}
for (const previousMessage of baseline.items) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Medium Layers/DevinCloudAdapter.ts:464

sendTurn permanently drops detached backlog messages when sendMessage or turn initialization fails: it marks them seen and advances context.messageCursor before they are emitted or attached to a turn, so retries filter them out for the lifetime of the adapter. Delay committing the seen set and cursor until backlog delivery succeeds, or roll both changes back on failure.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Layers/DevinCloudAdapter.ts around line 464:

`sendTurn` permanently drops detached backlog messages when `sendMessage` or turn initialization fails: it marks them seen and advances `context.messageCursor` before they are emitted or attached to a turn, so retries filter them out for the lifetime of the adapter. Delay committing the seen set and cursor until backlog delivery succeeds, or roll both changes back on failure.

issue: "Local attachments are not supported by Devin Cloud yet.",
});
}
if (context.activeTurnId) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟠 High Layers/DevinCloudAdapter.ts:443

Concurrent sendTurn calls for the same ready thread can both start remote prompts, but only the later activeTurnId remains tracked; the earlier poll then exits without emitting turn.completed, leaving the first caller with an uncompleted turn. Because the check at context.activeTurnId occurs before the yielding createSession/sendMessage operations, reserve the turn atomically before the first yield or serialize sends per session.

🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/Layers/DevinCloudAdapter.ts around line 443:

Concurrent `sendTurn` calls for the same ready thread can both start remote prompts, but only the later `activeTurnId` remains tracked; the earlier poll then exits without emitting `turn.completed`, leaving the first caller with an uncompleted turn. Because the check at `context.activeTurnId` occurs before the yielding `createSession`/`sendMessage` operations, reserve the turn atomically before the first yield or serialize sends per session.

Comment thread apps/server/src/provider/Layers/DevinAdapter.ts
The lazy sign-in retry replaced the ACP session through startSession,
which emitted session.exited mid-turn, and orchestration read that as a
stop. The pre-auth session now tears down silently before the restart,
and turn settlement counts in-flight prompts on the live context so a
steer that arrives during the retry keeps the merged turn running. The
lazy-auth test now asserts no session.exited is emitted.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 636479a. Configure here.

// Tear down the pre-auth session silently: this restart is
// internal to the running turn, and a session.exited here
// would make orchestration treat the turn as stopped.
yield* stopSessionInternal(ctx, { emitSessionExited: false });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auth restart emits idle session state

High Severity

Suppressing session.exited during lazy sign-in still calls startSession, which publishes session.state.changed with ready while the original turn is in flight. Orchestration treats ready as idle and can clear the active turn, so the retry looks stopped even though the prompt is still running.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 636479a. Configure here.

Devin Cloud showed a single placeholder model, so the picker had nothing
to choose. The API's devin_mode on session creation is the real model
choice: the picker now offers Devin, Devin Fast, and the preview modes
Lite, Ultra, and Fusion, and the adapter passes the selection through
when it creates the remote session. The mode is fixed at creation, which
the presentation already declares via requiresNewThreadForModelChange.
Legacy "devin-cloud" threads fall back to the organization default mode.

Also drops the platform field from session creation: the API defines
platform as the VM platform / outpost pool selector and rejects
unrecognized values with a 400.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@dabit3

dabit3 commented Aug 20, 2026

Copy link
Copy Markdown

subscribed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants