feat(providers): add Devin as a first-class provider - #1251
devin-ai-integration[bot] wants to merge 56 commits into
Conversation
Devin joins ADE as one provider id covering local and cloud. Local: devin acp runs as an ACP dialect in the shared host for native Work chats, and the tracked devin CLI row gives PTY sessions with resume. Cloud: the v3 Sessions API powers an org-wide fleet view (list with repo/tag filters, provenance chips Mine/From ADE/All), mirrored transcript chats over GET/POST messages, lane-bound session creation with ade/ade:lane:<id> tags and a devin_mode picker, terminate + archive/unarchive, pull-into-lane for pushed branches/PRs, and a built-in-browser live view via session.url. Cloud sessions join the attention system (waiting_for_user -> Needs you) and sync Devin attachments into the proof drawer. Hand off to Devin Cloud packages lane context into a cloud session; Continue in lane seeds a local CLI from a pulled session. Auth is a pasted v3 PAT (cog_) with a v1 personal-key fallback; CLI chats use devin auth login. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configurationConfiguration used: Repository: arul28/ADE/.coderabbit.yaml Review profile: CHILL Plan: Advanced 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 |
…fleet row, fix review findings Problem - Devin surfaces used a generic diamond icon instead of the Cognition mark, and the Devin fleet duplicated a sidebar row that lives only in the top header for Linear/Cursor. - Devin Review flagged correctness/security issues: seconds-vs-ms timestamps re-sorted remote events, unparseable API bodies were treated as empty success, attachments downloaded without a size cap, cloud sends ran a runtime-backed readiness gate and stayed 'active', pull-into-lane imported a ref that was never fetched, and credential mutation was missing from the CTO-only action policy. Change and boundary - Swap every Devin glyph (top header button, fleet modal, cloud panel, chat header, composer menus, provider logos) to the Cognition mark via devin.svg; remove the sidebar 'Devin Cloud' nav row (header button only); tighten fleet modal to match the Cursor modal. - Cloud client: parse ISO and seconds/ms epochs, throw DevinCloudResponseError on unparseable non-empty 2xx bodies, verify org id + record/items shape, cap attachment downloads at 50MB. - Cloud sends: cloud-specific readiness (disposed/pending-input/ in-flight), in-flight dedup set, idle transition on success/failure. - Pull-into-lane: fetch refs/pull/<n>/head into refs/heads/<branch> before importBranch for new lanes; fetch into the target worktree so FETCH_HEAD resolves for existing lanes. - Persisted-link lookup in openDevinCloudChat so reopened links reuse the original chat; attachment sync marks 'seen' only after ingest. - Add 'devin-chat' toolType mappings; gate setDevinCloudCredentials as CTO-only; restore cursor-fleet default includeArchived behavior. Verification - npm --prefix apps/desktop run typecheck: clean. - vitest ModelPicker.test.tsx: 73 passed. - eslint on changed files: 0 errors. Built with Devin (Cognition AI). Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Problem - typecheck-desktop OOM'd on this branch (tsc exited 134 after ~110s of GC thrashing at the default ~4GB heap). Change and boundary - NODE_OPTIONS=--max-old-space-size=8192 on the desktop typecheck step only; matches the repo's own precedent (the lint script already runs with an 8GB heap). Other typecheck jobs are unchanged. Built with Devin (Cognition AI). Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…streaming cap, attention ownership Problem - The cloud mirror only read the first page of messages (transcripts stop at 200), emitted 'done' while status was TTL-unknown, cleared attention markers owned by newer sources, and wrote remote attachment names to disk unsanitized. - pullIntoLane merged the project's same-numbered PR when a session's PR belonged to another repo; attachment downloads still buffered the full body when Content-Length was absent; a cleared devinCloudOrgId was resurrected from shared config. - The launch shelf's Devin Cloud machine row showed the generic violet cloud icon instead of the Cognition mark. Change and boundary - Follow listMessages endCursor (repeated-cursor guard) so mirrored transcripts pass 200 messages. - Gate the completion 'done' on a known-terminal status; refresh the remote record once when fresh output arrives with status unknown. - clearAttentionRequest gains an optional expectedSource; the mirror clears only provider_structured markers. - Attachment filenames are reduced to their basename before writing. - pullIntoLane compares the PR URL's repo to the project origin before fetching. - downloadAttachment streams the body with the 50MB cap enforced mid-read; Content-Length is only an early-out. - coerceAiConfig preserves explicit null devinCloudOrgId. - DraftMachineOption gains cloudProvider so the Devin Cloud row renders DevinLogo; cursor row tagged too. Verification - npm --prefix apps/desktop run typecheck: clean. - vitest DraftMachinePicker.test.tsx: 4 passed. - eslint on changed files: 0 errors. Built with Devin (Cognition AI). Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…l; poll only the transcript tail Problem - A Devin cloud chat whose output hydrated while the session-status read failed never emitted 'done': later polls deduplicated the transcript, so completion handling never ran again. - Every mirror poll re-downloaded the whole transcript (all pages), so API load grew with history instead of with new output. Change and boundary - The mirror now holds the hydrate turn id in a pending-done map when output arrives without a provable terminal status; each later poll re-checks the remote record and emits 'done' with that turn id the first time the session reads terminal. - A per-session tail cursor checkpoints the last messages page, so steady-state polls request only rows after it. A failed fetch on a checkpoint retries once from scratch (covers cursor invalidation); in-memory only, so a host restart re-pulls once and dedupe hides it. - Adds the devin spec to providerKeySpecs/AddApiKeySheet (merge fallout) and pins @cursor/sdk 1.0.31 node_modules per upstream. Verification - npm --prefix apps/desktop run typecheck: clean. - npm --prefix apps/ade-cli run typecheck: clean. Built with Devin (Cognition AI). Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…w findings - register Devin fleet/auth/chat remote commands so mobile, web, and relay clients get the same surface as desktop, with Cursor-parity viewer vs controller gating - reset cloud-turn completion state on each new send so follow-up turns still emit done - require an explicit org id when a PAT exposes multiple orgs instead of silently defaulting to the first - normalize ports and the ssh.github.com alias in repoMatchKey so valid SSH origins pass the pull-into-lane repo guard (covers Cursor too) - file the Windsurf CLI key under devin-cli so it cannot overwrite the cloud PAT stored under devin - scope the provider-key-spec credential test to harness providers Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
… history - verify() now refuses when a PAT sees >1 org without a configured id, and validates a configured org id against the visible set (orgName comes from the matching row) instead of caching the first row - v1 listMessages returns the whole inline transcript (mirror dedupes on event_id) instead of silently dropping everything before the tail - update cli/model-picker/sync-capability expectations for the new provider Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Both v3 discovery paths now follow end_cursor across GET /v3/enterprise/organizations, so a configured org past page one is found instead of rejected. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…unch on lane branch - Closing an armed row menu now clears confirmDeleteId instead of re-arming - Saving a v1 key clears the persisted org so a later v3 key isn't verified against the previous org - Lane-bound cloud sessions name the pushed lane branch in the prompt (v3 create has no branch field) Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The create path now checks the remote for the lane branch and pushes it when absent, so callers that never push (drawer, remote command) cannot hand Devin a checkout instruction for a branch that does not exist. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…n-provider Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> # Conflicts: # apps/desktop/src/main/utils/terminalTuiMarkers.ts
…ed path, remote input caps Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Missing remote launches repo-less sessions (collapsed finding on |
The payload-based non-empty check also rejected the desktop's own Settings saves, because preload routes setDevinCloudCredentials through the bound project runtime for local projects too. Register the command with localOnly instead so the peer-policy gate rejects paired/relay callers while the trusted runtime socket path (desktop local + remote-bound) still reaches the handler. Remote clearing stays possible via the new remote-allowed ai.deleteDevinCloudCredentials command, which still routes through the service so the stored org id and client cache are dropped. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Devin cloud sends dropped attachments silently: the transcript showed the files but only text was posted. devinCloudSendTurn now uploads each resolved file to Devin's attachment store (v3 POST .../attachments, v1 /v1/attachments) before the user_message emits and references the URLs in the send — attachment_urls on v3, ATTACHMENT:"url" lines on v1 — and fails the turn honestly when a file is unreadable or over the 50MB cap. Mirror durability: persistChatState now seeds devinCloudAttentionRaised from the persisted flag before writing, so a persist that runs before the mirror's first pass no longer erases a raised Needs-you and leaves the marker uncleanable. Attention flips and newly synced proof attachments also trigger a persist even when no visible output arrived that pass. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Addressed the three Devin Review findings that had no inline threads (fixed in
|
There was a problem hiding this comment.
Note
Newer findings are available below. Devin Review posted a newer report on this PR, in addition to the findings presented here.
Devin Review found 2 new potential issues.
⚠️ 2 issues in files not directly in the diff
⚠️ Attachment uploads bypass send serialization
When two attachment sends overlap, both pass devinCloudSendInFlight before either upload completes. Both turns then dispatch concurrently, reordering remote messages and local state events.
⚠️ Unsaved attachment edits are omitted
fs.readFileSync uploads the on-disk file even when the editor holds newer unsaved content. Devin receives stale attachment bytes instead of the attached version.
10 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)
The upload loop introduced an await between the devinCloudSendInFlight check and add, letting two overlapping attachment sends both pass the guard and dispatch concurrently. The slot is now taken before the first await and released on upload failure; the existing finally still covers the send path. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Follow-up on the latest Devin Review round (
|
There was a problem hiding this comment.
Note
Newer findings are available below. Devin Review posted a newer report on this PR, in addition to the findings presented here.
Devin Review found 1 new potential issue.
⚠️ 1 issue in files not directly in the diff
⚠️ Pre-dispatch failure blocks future sends
If event emission throws after devinCloudSendInFlight is acquired, neither cleanup block releases it. Every later send fails as already active.
10 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)
Emitting prepared events or resetting mirror state happens after the in-flight slot is taken but outside the send's try/finally, so a throw there permanently blocked later sends. One outer try/finally now covers the whole send body after acquisition. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Fixed in |
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
…n-provider Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> # Conflicts: # apps/desktop/src/main/services/chat/acpHost/acpHost.test.ts # docs/features/chat/agent-routing.md
Main's usage-measurement work replaced the usageSource field with standardAcpUsage and requires extensionNotifications, localUsage, readAccount, inferCompaction, and usageUpdateAfterTurn on every dialect. Devin now reads usage via standardAcpUsage, declares no local ledger or extension notifications, infers compaction from usage_update drops, and reports its account as subscription when ~/.local/share/devin/ credentials.toml exists (api_key on WINDSURF_API_KEY, else unknown). contextCompaction gains the devin tint required by the provider-keyed map. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Every Devin fleet-mutating ADE action now invalidates the fleet cache (previously only the direct IPC and sync-command routes did), so a remote-bound Remove/replace can't serve the old account's cached fleet. - Forced status refresh probes devin like the other ACP providers and synthesizes cli-subscription availability from runtime health. - inspectAcpCliCredentials checks the file devin auth login actually writes ($XDG_DATA_HOME/devin/credentials.toml, plus the Windows data dirs) via a shared devinCredentialFiles helper also used by the ACP account reader — the config-dir JSON guesses never matched a real login. - consumeDevinEchoFingerprint no longer suffix-matches arbitrary text: a remote user row dedupes only on exact text or after stripping the delivery lines ADE itself appends (Image URL: / ATTACHMENT:), so a distinct cloud message that extends a local prompt is not eaten. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- devinCredentialFiles returns only the active data dir: when XDG_DATA_HOME is set the default location is not searched, so a stale logged-out credential can no longer mark Devin authenticated. - Cloud sends record the fingerprint of the exact text delivered to Devin (which composeLaunchDirectives can prefix with lane/execution/ guidance blocks) paired with the visible fingerprint, persisted so the echo dedupes across restarts; a failed send withdraws its pair. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Note
Newer findings are available below. Devin Review posted a newer report on this PR, in addition to the findings presented here.
Devin Review found 1 new potential issue.
⚠️ 1 issue in files not directly in the diff
⚠️ Cleared pending echoes return
When devinCloudPendingEchoPairs becomes empty, persistence restores its previous nonempty value. That stale pair can hide a later identical remote message.
11 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)
A loaded devinCloudPendingEchoPairs list is authoritative even when empty — carrying prevPersisted forward resurrected pairs whose echoes had already arrived, which could suppress a later identical remote message. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Fixed in |
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Devin Review found 9 new potential issues.
🐛 5 issues in files not directly in the diff
🐛 Existing chats silently relink to Devin
When sessionId identifies an existing chat, openDevinCloudChat replaces its cloud link without checking its current owner. Future replies target the new Devin session while the old transcript remains.
⚠️ Cloud chat creation depends on local CLI
When no ADE chat exists, openDevinCloudChat creates a local-provider session before linking the cloud session. Users with only a cloud token cannot reliably open their cloud chats if local-provider checks reject creation.
⚠️ Older cloud chats duplicate after restart
After a restart, openDevinCloudChat searches only 500 persisted chats for an existing link. Opening an older Devin session creates a second ADE chat instead of restoring its original transcript.
⚠️ Cloud turns remain unfinished after restart
If Devin produces output before ADE restarts, devinCloudPendingDoneTurn loses the pending turn. Replayed output deduplicates, so a later terminal status never emits done for that turn.
⚠️ Cloud transcript polling can miss new messages
When a paginated read finishes, devinCloudMessagesTailCursor retains the last continuation cursor rather than a cursor after its final item. Later polling replays the final page and can miss newly appended messages if cursors expire.
12 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)
| const created = await window.ade.ai.devinCloudCreateSession({ | ||
| laneId: targetLaneId, | ||
| prompt, | ||
| sessionId, | ||
| title: buildDraftLaunchJobTitle("chat", snapshot), | ||
| devinMode: devinCloudModeSel, | ||
| bypassApproval: devinBypassApproval, | ||
| platform: devinCloudPlatformSel.trim() || null, | ||
| }); |
There was a problem hiding this comment.
| const snapshot = buildDraftLaunchSnapshotForCurrentState() | ||
| ?? (promptText.trim().length | ||
| ? ({ | ||
| text: promptText, | ||
| draft: promptText, | ||
| modelId, |
There was a problem hiding this comment.
| register("ai.deleteDevinCloudCredentials", { viewerAllowed: true, queueable: true }, async () => { | ||
| const status = await requireService(args.aiIntegrationService, "AI integration service not available.").setDevinCloudCredentials({ apiKey: "" }); |
There was a problem hiding this comment.
…tale org label Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
|
Addressed the latest Devin Review round (fixed in
Assessments on the remaining items:
|
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Summary
Devin (Cognition AI) becomes a first-class ADE provider under a single
devinprovider id — covering local (devin acpchats + trackeddevinCLI) and cloud (the org session fleet), with capability gates deciding which parts light up per surface. The shape deliberately mirrors the Cursor integration: ACP dialect for chats, tracked-CLI row for terminals, and adevinCloud*service set behindade.ai.devinCloud.*IPC for the fleet, so Devin inherits ADE's existing chat, drawer, attention, proof, and lane machinery rather than a bespoke UI.Local surface
acpHost/acpDialects/devin.ts— new dialect spawningdevin acp(JSON-RPC stdio), registered inACP_DIALECTS+acpProviderMetadata; works on every surface the ACP host serves (desktop chats, TUI, remote).devinCLI row (launch profiledevin -- <prompt>, tool detection, PTY sessions) — same tier as cursor-agent/claude/codex.devin auth loginbrowser OAuth orWINDSURF_API_KEY; no key needed for local chats. Devin sits next to Cursor inMODEL_PICKER_PROVIDER_ORDERwith adaptive/swe/opus/gpt/fable catalog entries (devin-*aliases — bareopus/fablecollided with the anthropic family).Cloud surface (
services/ai/devinCloudClient.ts+devinCloudFleetService.ts+devinCloudConversation.ts)cog_, self-serve on every Devin account — no org-only gating) with v1apk_user_personal-key fallback; org id collected once and auto-discovered (paginatedlistAllOrganizations).DevinCloudQuickViewButton(top bar next to Linear/Cursor, Cognition mark, renders only when configured) →DevinCloudFleetModal: org sessions with status/PR/ACU, Mine / From ADE / All provenance chips, status + lane filters; status derived once inshared/devinCloudFleetStatus.ts(waiting_for_user/waiting_for_approval→ needs_you).GET/POST …/messages), livesession.urlin the built-in browser, stop, archive/unarchive, PR, delete, and pull-into-lane for pushed branches (same refusal rules as Cursor: dirty worktrees refused, conflicts abort).devin ssh/devin forward/devin --cloudsurface): fleet rows gain SSH into VM, Forward port…, and Steer in terminal — each spawned as a tracked PTY in the entry's lane, gated ondevinCLI detection and using CLI credentials, not the API token.devin_modepicker, VM platform field mapped to the v3platformlabel, approval-gate skip); sends create sessions taggedade+ade:lane:<id>bound to the lane's repos.waiting_for_user/waiting_for_approvalraise "Needs you" viarequestAttention(provider_structured); Devin attachments download into the computer-use artifact store and ingest into the proof drawer (deduped by attachment id); Hand off to Devin Cloud attach-menu item packages lane context into a cloud launch; Continue in lane on the pull toast launches the localdevinCLI seeded with the session's context.Cause
ADE had no Devin surface at all; users with Devin accounts had no way to chat with the CLI, watch their cloud fleet, or move work between lanes and Devin's cloud VMs.
Change and boundary
Provider plumbing only — no changes to shared chat semantics, other providers, or launch machinery. Cloud sessions are VM-side: no local file access and no REST API for screen/exec — the deep-link live view covers watching, and the CLI's
devin ssh/devin forwardcover shell + port access. Cloud auth is a token paste (no third-party OAuth exists for the REST API — same as Cursor); the v1 API is used only as a fallback credential path.Verification
npm --prefix apps/desktop run typecheck— clean;npm --prefix apps/ade-cli run typecheck— clean.acpHost.test.ts(141) +modelCatalog.test.ts(4) +orchestrationRuntimePolicy.test.ts(8) — 153 pass; ade-cli expectations updated for the new provider (enum, mobile-sync capability list, model-picker rail).npm run dev:vite, seeded mock): quick-view button, fleet modal rows/badges/provenance chips, row menus, pull-into-lane → Continue-in-lane launch chain, settings provider card + Devin Cloud token section, chat-drawer cloud panel withdevin_modepicker, model picker.cog_/apk_user_token), a realdevin acpspawn, and thedevin ssh/forward/--cloudPTY actions against a live cloud session.Screenshots (new surfaces — nothing existed before):
Link to Devin session: https://app.devin.ai/sessions/6ecdc532fd424266bc43edb520e69168
Open in Devin Desktop: https://app.devin.ai/desktop/session/6ecdc532fd424266bc43edb520e69168?variant=devin
Requested by: @arul28