feat(devin): add and harden CLI provider support - #9335
Conversation
- AcpSessionRuntime now supports on-demand authentication and retains state-bearing session/update notifications (available_commands_update, config_option_update, current_mode_update) during session startup, replaying them once the session is established. - AcpRuntimeModel parses the new session update types into typed events. - contracts/settings adds DevinSettings and DevinSettingsPatch for the new driver.
Add coverage for the Devin driver registration, adapter construction, and ACP on-demand authentication behavior used by the Devin runtime.
Add Devin icon and badge to the web and mobile provider pickers, settings driver meta, and chat provider icon mapping.
…nitialize omits them Devin ACP `initialize` does not advertise `modelState`, so the provider probe fell back to a single "Devin Default" model and a warning. Add a secondary probe that runs `devin models list --format json`, parses the family/variant JSON into `ServerProviderModel` entries, and uses them when ACP does not provide models. Keep the ACP path as the preferred source so future Devin versions that advertise models in `initialize` continue to work. - Add `parseDevinModelsListJson` and `discoverDevinModelsViaModelsList` - Mark provider `ready` and `auth: authenticated` when CLI list succeeds - Include the built-in "default" model alongside discovered variants - Add focused tests covering parser behavior and CLI fallback
| break; | ||
| } | ||
| case "config_option_update": { | ||
| if (Array.isArray(upd.configOptions) && upd.configOptions.length > 0) { |
There was a problem hiding this comment.
🟠 High acp/AcpRuntimeModel.ts:862
An empty config_option_update produces no ConfigOptionsChanged event, so removing the last option leaves stale options in the runtime. Nonempty updates are also later treated by mergeSessionConfigOptions as patches, so omitted option IDs remain stale; emit empty snapshots and replace the stored set rather than merging IDs.
Also found in 1 other location(s)
apps/server/src/provider/acp/AcpSessionRuntime.ts:976
mergeSessionConfigOptionstreatsconfig_option_updateas a patch, but ACP definesconfigOptionsas the full current set. If an agent removes optionBand sends the remaining nonempty list containing onlyA, this function retains staleB; later reads and validation can present or accept a configuration option that no longer exists.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/provider/acp/AcpRuntimeModel.ts around line 862:
An empty `config_option_update` produces no `ConfigOptionsChanged` event, so removing the last option leaves stale options in the runtime. Nonempty updates are also later treated by `mergeSessionConfigOptions` as patches, so omitted option IDs remain stale; emit empty snapshots and replace the stored set rather than merging IDs.
Also found in 1 other location(s):
- apps/server/src/provider/acp/AcpSessionRuntime.ts:976 -- `mergeSessionConfigOptions` treats `config_option_update` as a patch, but ACP defines `configOptions` as the full current set. If an agent removes option `B` and sends the remaining nonempty list containing only `A`, this function retains stale `B`; later reads and validation can present or accept a configuration option that no longer exists.
There was a problem hiding this comment.
Fixed in c247b15d: empty config_option_update now emits ConfigOptionsChanged, and applySessionUpdate replaces the stored config option set instead of merging it.
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.
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This PR adds a substantial Devin provider integration spanning ACP sessions, subprocess execution, permissions, model discovery, text generation, settings, and multiple clients. It also changes product defaults and adds static-analysis suppression directives, while the supplied ACP correctness finding remains a material review concern. 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. |
- Pass resumeCursor as resumeSessionId and propagate started session id as resumeCursor. - Read image and file attachments and include them in session/prompt. - Reject overlapping sendTurn calls and only update activeTurnId after setup succeeds. - Guard stopSession/stopAll against deleting replaced sessions and leaving ACP processes. - Implement permission and user-input response handlers; only auto-approve in full-access mode. - Make rollback a provider-side no-op with a clear error. - Always emit ConfigOptionsChanged for config_option_update, including empty updates. - Harden ACP startup: serialize pending startup update replay, filter by session id, and set startStateRef only after replay. - Fix Devin profile identity to include resolved home path and env values. - Fix configPath whitespace expansion and settings/server-settings devin defaults.
|
Pushed
Typecheck and the Devin/Acp/ProviderRegistry tests pass. |
… event stamps - Remove withThreadLock from respondToRequest/respondToUserInput so startup permission and elicitation requests can be answered while startSession holds the per-thread semaphore. - Track pending approvals and user-inputs by request id at the adapter scope so responses can resolve the corresponding Deferreds before the session context is published. - Generate a fresh event stamp for user-input.resolved and session.state.changed so lifecycle and resolution events have distinct event ids.
soramikan
left a comment
There was a problem hiding this comment.
Pushed 54a7b3a16 to address the latest round of review findings.
Changes in this update:
respondToRequestandrespondToUserInputno longer acquire the per-threadwithThreadLock, so permission and elicitation requests emitted duringacp.start()can be answered whilestartSessionstill holds that semaphore.- Pending approvals and user-input responses are tracked by request id at the adapter scope, allowing them to be resolved before the
DevinSessionContextis published tosessions. session.startedandsession.state.changednow mint separate event stamps.user-input.resolvednow mints its own event stamp instead of reusinguser-input.requested's stamp.
All prior macroscope and Cursor findings (resume cursor, attachments, rollback, config option replace/emit, overlapping sendTurn, stopAll concurrency, identity, settings, provider history) remain addressed in c247b15d9608c91712fe044e58551e972a5ee7f4.
Typecheck and the Devin/Acp/ProviderService test suites pass.
…in session stopSessionInternal now interrupts all pending permission and elicitation Deferreds for the stopped thread and removes their request IDs from both the per-context and adapter-wide pending maps, preventing stale entries and late responses from settling requests belonging to a closed session.
|
Pushed
The earlier startup-permission deadlock and shared event-id findings are still fixed in Typecheck and the Devin/Acp/ProviderService test suites pass. |
…ession stop - `respondToRequest` and `respondToUserInput` now reject responses for stopped sessions by deleting stale entries from the adapter-wide maps. - Pending approval and user-input contexts store the metadata needed to emit resolved events, so `stopSessionInternal` can emit `request.resolved` and `user-input.resolved` with a cancel/empty payload when it interrupts a pending request, instead of leaving the UI waiting.
… interrupting `stopSessionInternal` now resolves pending permission and user-input Deferreds with cancel/empty payloads, letting the ACP callbacks emit `request.resolved` and `user-input.resolved`, return a cancelled outcome, and clean up the adapter-wide maps. `respondToRequest` / `respondToUserInput` still guard against resolving stopped sessions.
… turn snapshots - `respondToRequest` and `respondToUserInput` now verify the request belongs to the supplied `threadId` before resolving the deferred. - `sendTurn` stores the user prompt and final prompt result on the active turn, and `handleParsedEvent` appends ACP content/tool/plan events as they arrive, so `readThread` no longer returns empty turn items.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 3 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit afdcc23. Configure here.
| authenticatePayload, | ||
| acp.agent.authenticate(authenticatePayload), | ||
| ); | ||
| return yield* promptOnce; |
There was a problem hiding this comment.
Auth retry not gated on mode
High Severity
The new prompt-path authenticate-and-retry runs for every ACP provider, not only authenticationMode: "on-demand". Any AcpRequestError with code -32000 (a generic JSON-RPC server error) now triggers a second authenticate plus a second session/prompt on Cursor and Grok. That can re-run a failed turn and produce duplicate agent work.
Reviewed by Cursor Bugbot for commit afdcc23. Configure here.
| if (modelChanged && targetProtocolValue !== undefined) { | ||
| yield* input.runtime | ||
| .setModel(targetProtocolValue) | ||
| .pipe(Effect.mapError((cause) => input.mapError({ cause, step: "set-model" }))); |
There was a problem hiding this comment.
Model switch compares mismatched IDs
Medium Severity
applyDevinAcpModelSelection treats a model change as requestedModelId !== currentModelId, but the requested value is a T3 slug and currentModelId is the ACP protocol id. After the first apply the stored id is the protocol value, so every later sendTurn with a non-default model looks like a switch and calls setModel again.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit afdcc23. Configure here.
|
|
||
| const promptResult = yield* Effect.gen(function* () { | ||
| yield* runtime.start(); | ||
| yield* Effect.ignore(runtime.setMode("ask")); |
There was a problem hiding this comment.
Text generation starts an unconstrained agent
High Severity
Commit-message, PR, branch, and title generation spawn devin acp with no runtimeMode and then ignore setMode("ask"). Devin's known modes are normal, accept-edits, smart, plan, and bypass — not ask — so the process stays in the CLI default, which can edit the workspace while producing structured text.
Reviewed by Cursor Bugbot for commit afdcc23. Configure here.
|
Note 🤖 GPT-6 Astra (preview) responding on behalf of Theo This note is part of an automated cleanup pass. Carryover from #9483 at eb63014: its Devin CLI 3000.6.7 probe found the model catalog in |


Summary
This PR adds Devin as a first-class T3 Code provider using the existing ACP runtime and provider-instance infrastructure.
DevinAdapter,DevinProvider,DevinTextGeneration, andDevinProfileto bridgedevin acp(JSON-RPC over stdio).AcpSessionRuntimewith on-demand authentication and retention of startup state-bearing updates.devin models list --format jsonwhen ACPinitializedoes not advertise them, so the provider reachesreadyinstead of warning.This is a fresh implementation against current
main, informed by the closed #7567 but not cherry-picked from it.Test plan
npx vp run --filter t3 typechecknpx vp run --filter @t3tools/web typechecknpx vp run --filter @t3tools/mobile typechecknpx vp run --filter @t3tools/contracts typechecknpx vp test run apps/server/src/provider/Drivers/DevinDriver.test.tsnpx vp test run apps/server/src/provider/Layers/DevinAdapter.test.tsnpx vp test run apps/server/src/provider/acp/AcpSessionRuntime.test.tsnpx vp test run apps/server/src/provider/Layers/DevinProvider.test.tsnpx vp test run apps/server/src/provider/Layers/ProviderRegistry.test.tsnpx vp check --fixModel/harness: SWE-1.7 Max via the T3 Code dev server with Devin CLI 3000.6.12.
Note
Medium Risk
Large new provider path that spawns external CLI processes, handles permission/elicitation flows, and changes shared ACP runtime auth behavior used by Devin (and potentially other ACP providers).
Overview
Adds Devin as an opt-in built-in provider: contracts gain
DevinSettingsand defaults,DevinDriverregisters inBUILT_IN_DRIVERS, and server settings/history treat Devin like other optional drivers (Cursor/Grok/OpenCode).The server stack spawns
devin acpthrough a newDevinAdapter(sessions, turns, permissions, elicitation, attachments, runtime events) withDevinProfileforDEVIN_HOME/DEVIN_CONFIGand continuation identity.DevinProviderhealth-checks the CLI, discovers models from ACP initialize ordevin models list --format json, and exposes an Early Access snapshot.DevinTextGenerationreuses the same ACP path for commit/PR/branch/title helpers.AcpSessionRuntimeis extended for Devin: optional on-demand authentication (authenticate after a failed prompt), buffering of state-bearing startup updates, tracking of config options and available commands, and safer session-id filtering on updates.DevinAcpSupportcentralizes spawn args, permission modes, and in-session model/reasoning selection.Web and mobile pick up Devin icons, settings metadata, and a “new” provider picker entry. Tests cover the driver, adapter construction, provider status/model parsing, registry listing, and on-demand auth behavior.
Note:
rollbackThreadis explicitly unsupported for Devin ACP sessions.Reviewed by Cursor Bugbot for commit afdcc23. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add
DevinCLI provider with adapter, settings, and ACP runtime supportDevinDriver,DevinAdapter,DevinProviderstatus checks,DevinProfileruntime resolution,DevinTextGeneration, andDevinSettings/DevinSettingsPatchschemas in settings.ts and model constants in model.tsDevinAdapterimplements ACP-backed session lifecycle, per-thread serialization, permission/elicitation callbacks, attachment handling, runtime event streaming, and turn interrupt/resumeDevinProviderprobes CLI version, discovers models via ACP initialization then CLI models-list fallback, and merges built-in/custom/discovered models into the provider snapshotAcpSessionRuntimegains on-demand authentication (retry prompt after auth failure),getAvailableCommands, and state-aware update routing that ignores notifications for other session IDsDevinIcon, provider picker entry with a "new" badge, Early Access provider definition, and settings form metadataDevinAdapter.rollbackThreadalways returns a validation error because provider-side Devin ACP rollback is unsupported; clients expecting rollback support will fail.discoverDevinModelsViaAcpInitializehas an 8-second timeout anddiscoverDevinModelsViaModelsListhas a 10-second timeout — slow CLI responses yield empty model lists rather than errors.Macroscope summarized afdcc23.