feat(providers): add Devin CLI and Devin Cloud as first-party providers - #7531
feat(providers): add Devin CLI and Devin Cloud as first-party providers#7531OxFrancesco wants to merge 12 commits into
Conversation
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>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
There was a problem hiding this comment.
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
ApprovabilityVerdict: 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:
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>
|
Reviewed every bot finding against the source. All of them were real, and 1ea463e fixes the lot: Devin CLI
Devin Cloud
Effect conventions (Macroscope check)
Not fixable from here: the Vercel Verified with |
| const exitPlanMarkdown = | ||
| exitPlanMarkdownFromToolCall(parsePermissionRequest(params).toolCall) ?? | ||
| exitPlanTexts.get(params.toolCall.toolCallId); | ||
| if (exitPlanMarkdown !== undefined) { |
There was a problem hiding this comment.
🟡 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`.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Sorry, I'm unable to act on this request because you do not have permissions within this repository.
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) { |
There was a problem hiding this comment.
🟡 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) { |
There was a problem hiding this comment.
🟠 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.
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>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ 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 }); |
There was a problem hiding this comment.
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)
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>
|
subscribed |


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). Wrapsdevin acp. Streams assistant and reasoning text, maps tool calls and permission approvals, turns ACP form elicitation into structured questions, reads context usage fromusage_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 advertiseddevin-browserflow, 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'sdevin_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
startSessionreturning. A regression test runsstartSessionin 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-turnsession.exited. Elicitation answers convert back to their declared number and boolean types. The cloud modules follow the Effect service conventions: errors keep their cause,operationis a literal union, andHttpClientcomes from the requirements channel. Session creation also stopped sendingplatform: "t3-code", which the API defines as the VM/outpost selector and rejects with a 400.Test plan
vp test runon all Devin test files +ProviderInstanceRegistryLive.test.ts+ contracts/server settings testsBuilt 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 acpwith ACP session lifecycle, resume cursors, runtime/interaction mode mapping, streaming (assistant, reasoning, usage), tool approvals, form elicitation → user-input events, plan-modeexit_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.aito 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 CLIcredentials.tomlplus/selforg 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
ProviderDriverimplementations:DevinDriver(ACP-backed CLI) andDevinCloudDriver(REST polling against the Devin Cloud API), both disabled by default in settings.DevinDriverspawns and manages the Devin CLI process over ACP, streams runtime events, handles plan/implement/approval interaction modes, supports resume cursors, and exposes adevin update --forcemaintenance action.DevinCloudDriverlazily resolves credentials from explicit settings or the local Devin CLI sign-in, creates or resumes remote sessions, polls for messages, and streamsProviderRuntimeEvents; auxiliary text-generation methods (commit messages, PR content, etc.) deterministically fail withTextGenerationError.DevinSettingsandDevinCloudSettingsschemas toServerSettingswith full patch support, and registersdevin/devinClouddriver kinds in the contracts package with default models and display names.Macroscope summarized e5027b1.