Ver Fonte

feat: enable GitLab Agent Platform with workflow model discovery (#18014)

Vladimir Glafirov há 4 semanas atrás
pai
commit
05d3e65f76

+ 15 - 11
bun.lock

@@ -325,8 +325,6 @@
         "@aws-sdk/credential-providers": "3.993.0",
         "@clack/prompts": "1.0.0-alpha.1",
         "@effect/platform-node": "catalog:",
-        "@gitlab/gitlab-ai-provider": "3.6.0",
-        "@gitlab/opencode-gitlab-auth": "1.3.3",
         "@hono/standard-validator": "0.1.5",
         "@hono/zod-validator": "catalog:",
         "@modelcontextprotocol/sdk": "1.25.2",
@@ -358,6 +356,7 @@
         "drizzle-orm": "catalog:",
         "effect": "catalog:",
         "fuzzysort": "3.1.0",
+        "gitlab-ai-provider": "5.2.2",
         "glob": "13.0.5",
         "google-auth-library": "10.5.0",
         "gray-matter": "4.0.3",
@@ -368,6 +367,7 @@
         "mime-types": "3.0.2",
         "minimatch": "10.0.3",
         "open": "10.1.2",
+        "opencode-gitlab-auth": "2.0.0",
         "opentui-spinner": "0.0.6",
         "partial-json": "0.1.7",
         "remeda": "catalog:",
@@ -1110,10 +1110,6 @@
 
     "@fontsource/inter": ["@fontsource/[email protected]", "", {}, "sha512-P6r5WnJoKiNVV+zvW2xM13gNdFhAEpQ9dQJHt3naLvfg+LkF2ldgSLiF4T41lf1SQCM9QmkqPTn4TH568IRagg=="],
 
-    "@gitlab/gitlab-ai-provider": ["@gitlab/[email protected]", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "openai": "^6.16.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=2.0.0", "@ai-sdk/provider-utils": ">=3.0.0" } }, "sha512-8LmcIQ86xkMtC7L4P1/QYVEC+yKMTRerfPeniaaQGalnzXKtX6iMHLjLPOL9Rxp55lOXi6ed0WrFuJzZx+fNRg=="],
-
-    "@gitlab/opencode-gitlab-auth": ["@gitlab/[email protected]", "", { "dependencies": { "@fastify/rate-limit": "^10.2.0", "@opencode-ai/plugin": "*", "fastify": "^5.2.0", "open": "^10.0.0" } }, "sha512-FT+KsCmAJjtqWr1YAq0MywGgL9kaLQ4apmsoowAXrPqHtoYf2i/nY10/A+L06kNj22EATeEDRpbB1NWXMto/SA=="],
-
     "@graphql-typed-document-node/core": ["@graphql-typed-document-node/[email protected]", "", { "peerDependencies": { "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ=="],
 
     "@happy-dom/global-registrator": ["@happy-dom/[email protected]", "", { "dependencies": { "@types/node": "^20.0.0", "happy-dom": "^20.0.11" } }, "sha512-GqNqiShBT/lzkHTMC/slKBrvN0DsD4Di8ssBk4aDaVgEn+2WMzE6DXxq701ndSXj7/0cJ8mNT71pM7Bnrr6JRw=="],
@@ -3032,6 +3028,8 @@
 
     "github-slugger": ["[email protected]", "", {}, "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw=="],
 
+    "gitlab-ai-provider": ["[email protected]", "", { "dependencies": { "@anthropic-ai/sdk": "^0.71.0", "@anycable/core": "^0.9.2", "graphql-request": "^6.1.0", "isomorphic-ws": "^5.0.0", "openai": "^6.16.0", "socket.io-client": "^4.8.1", "vscode-jsonrpc": "^8.2.1", "zod": "^3.25.76" }, "peerDependencies": { "@ai-sdk/provider": ">=2.0.0", "@ai-sdk/provider-utils": ">=3.0.0" } }, "sha512-ejwnie62rimfVHbjYZ2tsnqwLjF9YLgXD3OQA458gHz8hUvw7vEnhuyuMv5PmWQtyS3ISAghiX7r5SBhUWeCTA=="],
+
     "glob": ["[email protected]", "", { "dependencies": { "minimatch": "^10.2.1", "minipass": "^7.1.2", "path-scurry": "^2.0.0" } }, "sha512-BzXxZg24Ibra1pbQ/zE7Kys4Ua1ks7Bn6pKLkVPZ9FZe4JQS6/Q7ef3LG1H+k7lUf5l4T3PLSyYyYJVYUvfgTw=="],
 
     "glob-parent": ["[email protected]", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
@@ -3784,6 +3782,8 @@
 
     "opencode": ["opencode@workspace:packages/opencode"],
 
+    "opencode-gitlab-auth": ["[email protected]", "", { "dependencies": { "@fastify/rate-limit": "^10.2.0", "@opencode-ai/plugin": "*", "fastify": "^5.2.0", "open": "^10.0.0" } }, "sha512-jmZOOvYIurRScQCtdBqIW5HbP1JbmIiq7UtI7NGgn2vjke46g9d4NVPBg5/ZmFFVIBwZcgyFgJ7b8kGEOR9ujA=="],
+
     "opencontrol": ["[email protected]", "", { "dependencies": { "@modelcontextprotocol/sdk": "1.6.1", "@tsconfig/bun": "1.0.7", "hono": "4.7.4", "zod": "3.24.2", "zod-to-json-schema": "3.24.3" }, "bin": { "opencontrol": "bin/index.mjs" } }, "sha512-QeCrpOK5D15QV8kjnGVeD/BHFLwcVr+sn4T6KKmP0WAMs2pww56e4h+eOGHb5iPOufUQXbdbBKi6WV2kk7tefQ=="],
 
     "openid-client": ["[email protected]", "", { "dependencies": { "jose": "^4.15.4", "lru-cache": "^6.0.0", "object-hash": "^2.2.0", "oidc-token-hash": "^5.0.3" } }, "sha512-T1h3B10BRPKfcObdBklX639tVz+xh34O7GjofqrqiAQdm7eHsQ00ih18x6wuJ/E6FxdtS2u3FmUGPDeEcMwzNA=="],
@@ -4246,7 +4246,7 @@
 
     "socket.io-client": ["[email protected]", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1", "engine.io-client": "~6.6.1", "socket.io-parser": "~4.2.4" } }, "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g=="],
 
-    "socket.io-parser": ["[email protected].5", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1" } }, "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ=="],
+    "socket.io-parser": ["[email protected].6", "", { "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.4.1" } }, "sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg=="],
 
     "socks": ["[email protected]", "", { "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" } }, "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A=="],
 
@@ -5060,10 +5060,6 @@
 
     "@fastify/proxy-addr/ipaddr.js": ["[email protected]", "", {}, "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg=="],
 
-    "@gitlab/gitlab-ai-provider/openai": ["[email protected]", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-osTKySlrdYrLYTt0zjhY8yp0JUBmWDCN+Q+QxsV4xMQnnoVFpylgKGgxwN8sSdTNw0G4y+WUXs4eCMWpyDNWZQ=="],
-
-    "@gitlab/gitlab-ai-provider/zod": ["[email protected]", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
-
     "@hey-api/openapi-ts/open": ["[email protected]", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="],
 
     "@hey-api/openapi-ts/semver": ["[email protected]", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
@@ -5460,6 +5456,10 @@
 
     "gaxios/node-fetch": ["[email protected]", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
 
+    "gitlab-ai-provider/openai": ["[email protected]", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-j3k+BjydAf8yQlcOI7WUQMQTbbF5GEIMAE2iZYCOzwwB3S2pCheaWYp+XZRNAch4jWVc52PMDGRRjutao3lLCg=="],
+
+    "gitlab-ai-provider/zod": ["[email protected]", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
+
     "glob/minimatch": ["[email protected]", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="],
 
     "globby/ignore": ["[email protected]", "", {}, "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="],
@@ -5536,6 +5536,8 @@
 
     "opencode/@ai-sdk/openai-compatible": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@ai-sdk/provider-utils": "3.0.20" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-YspqqyJPzHjqWrjt4y/Wgc2aJgCcQj5uIJgZpq2Ar/lH30cEVhgE+keePDbjKpetD9UwNggCj7u6kO3unS23OQ=="],
 
+    "opencode-gitlab-auth/open": ["[email protected]", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
+
     "opencontrol/@modelcontextprotocol/sdk": ["@modelcontextprotocol/[email protected]", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^4.1.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-oxzMzYCkZHMntzuyerehK3fV6A2Kwh5BD6CGEJSVDU2QNEhfLOptf2X7esQgaHZXHZY0oHmMsOtIDLP71UJXgA=="],
 
     "opencontrol/@tsconfig/bun": ["@tsconfig/[email protected]", "", {}, "sha512-udGrGJBNQdXGVulehc1aWT73wkR9wdaGBtB6yL70RJsqwW/yJhIg6ZbRlPOfIUiFNrnBuYLBi9CSmMKfDC7dvA=="],
@@ -6286,6 +6288,8 @@
 
     "node-gyp/which/isexe": ["[email protected]", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="],
 
+    "opencode-gitlab-auth/open/wsl-utils": ["[email protected]", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
+
     "opencode/@ai-sdk/openai/@ai-sdk/provider-utils": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ=="],
 
     "opencode/@ai-sdk/openai-compatible/@ai-sdk/provider-utils": ["@ai-sdk/[email protected]", "", { "dependencies": { "@ai-sdk/provider": "2.0.1", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ=="],

+ 2 - 2
packages/opencode/package.json

@@ -89,9 +89,9 @@
     "@ai-sdk/xai": "2.0.51",
     "@aws-sdk/credential-providers": "3.993.0",
     "@clack/prompts": "1.0.0-alpha.1",
+    "gitlab-ai-provider": "5.2.2",
+    "opencode-gitlab-auth": "2.0.0",
     "@effect/platform-node": "catalog:",
-    "@gitlab/gitlab-ai-provider": "3.6.0",
-    "@gitlab/opencode-gitlab-auth": "1.3.3",
     "@hono/standard-validator": "0.1.5",
     "@hono/zod-validator": "catalog:",
     "@modelcontextprotocol/sdk": "1.25.2",

+ 1 - 1
packages/opencode/src/plugin/index.ts

@@ -11,7 +11,7 @@ import { CodexAuthPlugin } from "./codex"
 import { Session } from "../session"
 import { NamedError } from "@opencode-ai/util/error"
 import { CopilotAuthPlugin } from "./copilot"
-import { gitlabAuthPlugin as GitlabAuthPlugin } from "@gitlab/opencode-gitlab-auth"
+import { gitlabAuthPlugin as GitlabAuthPlugin } from "opencode-gitlab-auth"
 
 export namespace Plugin {
   const log = Log.create({ service: "plugin" })

+ 114 - 14
packages/opencode/src/provider/provider.ts

@@ -40,7 +40,12 @@ import { createGateway } from "@ai-sdk/gateway"
 import { createTogetherAI } from "@ai-sdk/togetherai"
 import { createPerplexity } from "@ai-sdk/perplexity"
 import { createVercel } from "@ai-sdk/vercel"
-import { createGitLab, VERSION as GITLAB_PROVIDER_VERSION } from "@gitlab/gitlab-ai-provider"
+import {
+  createGitLab,
+  VERSION as GITLAB_PROVIDER_VERSION,
+  isWorkflowModel,
+  discoverWorkflowModels,
+} from "gitlab-ai-provider"
 import { fromNodeProviderChain } from "@aws-sdk/credential-providers"
 import { GoogleAuth } from "google-auth-library"
 import { ProviderTransform } from "./transform"
@@ -124,18 +129,20 @@ export namespace Provider {
     "@ai-sdk/togetherai": createTogetherAI,
     "@ai-sdk/perplexity": createPerplexity,
     "@ai-sdk/vercel": createVercel,
-    "@gitlab/gitlab-ai-provider": createGitLab,
+    "gitlab-ai-provider": createGitLab,
     // @ts-ignore (TODO: kill this code so we dont have to maintain it)
     "@ai-sdk/github-copilot": createGitHubCopilotOpenAICompatible,
   }
 
   type CustomModelLoader = (sdk: any, modelID: string, options?: Record<string, any>) => Promise<any>
   type CustomVarsLoader = (options: Record<string, any>) => Record<string, string>
+  type CustomDiscoverModels = () => Promise<Record<string, Model>>
   type CustomLoader = (provider: Info) => Promise<{
     autoload: boolean
     getModel?: CustomModelLoader
     vars?: CustomVarsLoader
     options?: Record<string, any>
+    discoverModels?: CustomDiscoverModels
   }>
 
   function useLanguageModel(sdk: any) {
@@ -533,28 +540,105 @@ export namespace Provider {
         ...(providerConfig?.options?.aiGatewayHeaders || {}),
       }
 
+      const featureFlags = {
+        duo_agent_platform_agentic_chat: true,
+        duo_agent_platform: true,
+        ...(providerConfig?.options?.featureFlags || {}),
+      }
+
       return {
         autoload: !!apiKey,
         options: {
           instanceUrl,
           apiKey,
           aiGatewayHeaders,
-          featureFlags: {
-            duo_agent_platform_agentic_chat: true,
-            duo_agent_platform: true,
-            ...(providerConfig?.options?.featureFlags || {}),
-          },
+          featureFlags,
         },
-        async getModel(sdk: ReturnType<typeof createGitLab>, modelID: string) {
+        async getModel(sdk: ReturnType<typeof createGitLab>, modelID: string, options?: Record<string, any>) {
+          if (modelID.startsWith("duo-workflow-")) {
+            const workflowRef = options?.workflowRef as string | undefined
+            // Use the static mapping if it exists, otherwise use duo-workflow with selectedModelRef
+            const sdkModelID = isWorkflowModel(modelID) ? modelID : "duo-workflow"
+            const model = sdk.workflowChat(sdkModelID, {
+              featureFlags,
+            })
+            if (workflowRef) {
+              model.selectedModelRef = workflowRef
+            }
+            return model
+          }
           return sdk.agenticChat(modelID, {
             aiGatewayHeaders,
-            featureFlags: {
-              duo_agent_platform_agentic_chat: true,
-              duo_agent_platform: true,
-              ...(providerConfig?.options?.featureFlags || {}),
-            },
+            featureFlags,
           })
         },
+        async discoverModels(): Promise<Record<string, Model>> {
+          if (!apiKey) {
+            log.info("gitlab model discovery skipped: no apiKey")
+            return {}
+          }
+
+          try {
+            const token = apiKey
+            const getHeaders = (): Record<string, string> =>
+              auth?.type === "api" ? { "PRIVATE-TOKEN": token } : { Authorization: `Bearer ${token}` }
+
+            log.info("gitlab model discovery starting", { instanceUrl })
+            const result = await discoverWorkflowModels(
+              { instanceUrl, getHeaders },
+              { workingDirectory: Instance.directory },
+            )
+
+            if (!result.models.length) {
+              log.info("gitlab model discovery skipped: no models found", {
+                project: result.project ? { id: result.project.id, path: result.project.pathWithNamespace } : null,
+              })
+              return {}
+            }
+
+            const models: Record<string, Model> = {}
+            for (const m of result.models) {
+              if (!input.models[m.id]) {
+                models[m.id] = {
+                  id: ModelID.make(m.id),
+                  providerID: ProviderID.make("gitlab"),
+                  name: `Agent Platform (${m.name})`,
+                  family: "",
+                  api: {
+                    id: m.id,
+                    url: instanceUrl,
+                    npm: "gitlab-ai-provider",
+                  },
+                  status: "active",
+                  headers: {},
+                  options: { workflowRef: m.ref },
+                  cost: { input: 0, output: 0, cache: { read: 0, write: 0 } },
+                  limit: { context: m.context, output: m.output },
+                  capabilities: {
+                    temperature: false,
+                    reasoning: true,
+                    attachment: true,
+                    toolcall: true,
+                    input: { text: true, audio: false, image: true, video: false, pdf: true },
+                    output: { text: true, audio: false, image: false, video: false, pdf: false },
+                    interleaved: false,
+                  },
+                  release_date: "",
+                  variants: {},
+                }
+              }
+            }
+
+            log.info("gitlab model discovery complete", {
+              count: Object.keys(models).length,
+              models: Object.keys(models),
+            })
+            return models
+          } catch (e) {
+            log.warn("gitlab model discovery failed", { error: e })
+            return {}
+          }
+        },
       }
     },
     "cloudflare-workers-ai": async (input) => {
@@ -853,6 +937,9 @@ export namespace Provider {
     const varsLoaders: {
       [providerID: string]: CustomVarsLoader
     } = {}
+    const discoveryLoaders: {
+      [providerID: string]: CustomDiscoverModels
+    } = {}
     const sdk = new Map<string, SDK>()
 
     log.info("init")
@@ -1009,6 +1096,7 @@ export namespace Provider {
       if (result && (result.autoload || providers[providerID])) {
         if (result.getModel) modelLoaders[providerID] = result.getModel
         if (result.vars) varsLoaders[providerID] = result.vars
+        if (result.discoverModels) discoveryLoaders[providerID] = result.discoverModels
         const opts = result.options ?? {}
         const patch: Partial<Info> = providers[providerID] ? { options: opts } : { source: "custom", options: opts }
         mergeProvider(providerID, patch)
@@ -1070,6 +1158,18 @@ export namespace Provider {
       log.info("found", { providerID })
     }
 
+    const gitlab = ProviderID.make("gitlab")
+    if (discoveryLoaders[gitlab] && providers[gitlab]) {
+      await (async () => {
+        const discovered = await discoveryLoaders[gitlab]()
+        for (const [modelID, model] of Object.entries(discovered)) {
+          if (!providers[gitlab].models[modelID]) {
+            providers[gitlab].models[modelID] = model
+          }
+        }
+      })().catch((e) => log.warn("state discovery error", { id: "gitlab", error: e }))
+    }
+
     return {
       models: languages,
       providers,
@@ -1250,7 +1350,7 @@ export namespace Provider {
 
     try {
       const language = s.modelLoaders[model.providerID]
-        ? await s.modelLoaders[model.providerID](sdk, model.api.id, provider.options)
+        ? await s.modelLoaders[model.providerID](sdk, model.api.id, { ...provider.options, ...model.options })
         : sdk.languageModel(model.api.id)
       s.models.set(key, language)
       return language

+ 3 - 0
packages/opencode/src/server/routes/provider.ts

@@ -9,6 +9,9 @@ import { ProviderID } from "../../provider/schema"
 import { mapValues } from "remeda"
 import { errors } from "../error"
 import { lazy } from "../../util/lazy"
+import { Log } from "../../util/log"
+
+const log = Log.create({ service: "server" })
 
 export const ProviderRoutes = lazy(() =>
   new Hono()

+ 29 - 0
packages/opencode/src/session/llm.ts

@@ -12,6 +12,7 @@ import {
   jsonSchema,
 } from "ai"
 import { mergeDeep, pipe } from "remeda"
+import { GitLabWorkflowLanguageModel } from "gitlab-ai-provider"
 import { ProviderTransform } from "@/provider/transform"
 import { Config } from "@/config/config"
 import { Instance } from "@/project/instance"
@@ -184,6 +185,34 @@ export namespace LLM {
       })
     }
 
+    // Wire up toolExecutor for DWS workflow models so that tool calls
+    // from the workflow service are executed via opencode's tool system
+    // and results sent back over the WebSocket.
+    if (language instanceof GitLabWorkflowLanguageModel) {
+      const workflowModel = language
+      workflowModel.toolExecutor = async (toolName, argsJson, _requestID) => {
+        const t = tools[toolName]
+        if (!t || !t.execute) {
+          return { result: "", error: `Unknown tool: ${toolName}` }
+        }
+        try {
+          const result = await t.execute!(JSON.parse(argsJson), {
+            toolCallId: _requestID,
+            messages: input.messages,
+            abortSignal: input.abort,
+          })
+          const output = typeof result === "string" ? result : (result?.output ?? JSON.stringify(result))
+          return {
+            result: output,
+            metadata: typeof result === "object" ? result?.metadata : undefined,
+            title: typeof result === "object" ? result?.title : undefined,
+          }
+        } catch (e: any) {
+          return { result: "", error: e.message ?? String(e) }
+        }
+      }
+    }
+
     return streamText({
       onError(error) {
         l.error("stream error", {

+ 121 - 2
packages/opencode/test/provider/gitlab-duo.test.ts

@@ -1,12 +1,13 @@
-import { test, expect } from "bun:test"
+import { test, expect, describe } from "bun:test"
 import path from "path"
 
-import { ProviderID } from "../../src/provider/schema"
+import { ProviderID, ModelID } from "../../src/provider/schema"
 import { tmpdir } from "../fixture/fixture"
 import { Instance } from "../../src/project/instance"
 import { Provider } from "../../src/provider/provider"
 import { Env } from "../../src/env"
 import { Global } from "../../src/global"
+import { GitLabWorkflowLanguageModel } from "gitlab-ai-provider"
 
 test("GitLab Duo: loads provider with API key from environment", async () => {
   await using tmp = await tmpdir({
@@ -287,3 +288,121 @@ test("GitLab Duo: has multiple agentic chat models available", async () => {
     },
   })
 })
+
+describe("GitLab Duo: workflow model routing", () => {
+  test("duo-workflow-* model routes through workflowChat", async () => {
+    await using tmp = await tmpdir({
+      init: async (dir) => {
+        await Bun.write(path.join(dir, "opencode.json"), JSON.stringify({ $schema: "https://opencode.ai/config.json" }))
+      },
+    })
+    await Instance.provide({
+      directory: tmp.path,
+      init: async () => {
+        Env.set("GITLAB_TOKEN", "test-token")
+      },
+      fn: async () => {
+        const providers = await Provider.list()
+        const gitlab = providers[ProviderID.gitlab]
+        expect(gitlab).toBeDefined()
+        gitlab.models["duo-workflow-sonnet-4-6"] = {
+          id: ModelID.make("duo-workflow-sonnet-4-6"),
+          providerID: ProviderID.make("gitlab"),
+          name: "Agent Platform (Claude Sonnet 4.6)",
+          family: "",
+          api: { id: "duo-workflow-sonnet-4-6", url: "https://gitlab.com", npm: "gitlab-ai-provider" },
+          status: "active",
+          headers: {},
+          options: { workflowRef: "claude_sonnet_4_6" },
+          cost: { input: 0, output: 0, cache: { read: 0, write: 0 } },
+          limit: { context: 200000, output: 64000 },
+          capabilities: {
+            temperature: false,
+            reasoning: true,
+            attachment: true,
+            toolcall: true,
+            input: { text: true, audio: false, image: true, video: false, pdf: true },
+            output: { text: true, audio: false, image: false, video: false, pdf: false },
+            interleaved: false,
+          },
+          release_date: "",
+          variants: {},
+        }
+        const model = await Provider.getModel(ProviderID.gitlab, ModelID.make("duo-workflow-sonnet-4-6"))
+        expect(model).toBeDefined()
+        expect(model.options?.workflowRef).toBe("claude_sonnet_4_6")
+        const language = await Provider.getLanguage(model)
+        expect(language).toBeDefined()
+        expect(language).toBeInstanceOf(GitLabWorkflowLanguageModel)
+      },
+    })
+  })
+
+  test("duo-chat-* model routes through agenticChat (not workflow)", async () => {
+    await using tmp = await tmpdir({
+      init: async (dir) => {
+        await Bun.write(path.join(dir, "opencode.json"), JSON.stringify({ $schema: "https://opencode.ai/config.json" }))
+      },
+    })
+    await Instance.provide({
+      directory: tmp.path,
+      init: async () => {
+        Env.set("GITLAB_TOKEN", "test-token")
+      },
+      fn: async () => {
+        const providers = await Provider.list()
+        expect(providers[ProviderID.gitlab]).toBeDefined()
+        const model = await Provider.getModel(ProviderID.gitlab, ModelID.make("duo-chat-sonnet-4-5"))
+        expect(model).toBeDefined()
+        const language = await Provider.getLanguage(model)
+        expect(language).toBeDefined()
+        expect(language).not.toBeInstanceOf(GitLabWorkflowLanguageModel)
+      },
+    })
+  })
+
+  test("model.options merged with provider.options in getLanguage", async () => {
+    await using tmp = await tmpdir({
+      init: async (dir) => {
+        await Bun.write(path.join(dir, "opencode.json"), JSON.stringify({ $schema: "https://opencode.ai/config.json" }))
+      },
+    })
+    await Instance.provide({
+      directory: tmp.path,
+      init: async () => {
+        Env.set("GITLAB_TOKEN", "test-token")
+      },
+      fn: async () => {
+        const providers = await Provider.list()
+        const gitlab = providers[ProviderID.gitlab]
+        expect(gitlab.options?.featureFlags).toBeDefined()
+        const model = await Provider.getModel(ProviderID.gitlab, ModelID.make("duo-chat-sonnet-4-5"))
+        expect(model).toBeDefined()
+        expect(model.options).toBeDefined()
+      },
+    })
+  })
+})
+
+describe("GitLab Duo: static models", () => {
+  test("static duo-chat models always present regardless of discovery", async () => {
+    await using tmp = await tmpdir({
+      init: async (dir) => {
+        await Bun.write(path.join(dir, "opencode.json"), JSON.stringify({ $schema: "https://opencode.ai/config.json" }))
+      },
+    })
+    await Instance.provide({
+      directory: tmp.path,
+      init: async () => {
+        Env.set("GITLAB_TOKEN", "test-token")
+      },
+      fn: async () => {
+        const providers = await Provider.list()
+        const models = Object.keys(providers[ProviderID.gitlab].models)
+        expect(models).toContain("duo-chat-haiku-4-5")
+        expect(models).toContain("duo-chat-sonnet-4-5")
+        expect(models).toContain("duo-chat-opus-4-5")
+      },
+    })
+  })
+})

+ 1 - 1
packages/opencode/test/tool/fixtures/models-api.json

@@ -32933,7 +32933,7 @@
   "gitlab": {
     "id": "gitlab",
     "env": ["GITLAB_TOKEN"],
-    "npm": "@gitlab/gitlab-ai-provider",
+    "npm": "gitlab-ai-provider",
     "name": "GitLab Duo",
     "doc": "https://docs.gitlab.com/user/duo_agent_platform/",
     "models": {

+ 4 - 8
packages/web/src/content/docs/ar/providers.mdx

@@ -752,7 +752,7 @@ export GITLAB_TOKEN=glpat-...
 :::note
 يجب على مدير GitLab لديك تفعيل ما يلي:
 
-1. [Duo Agent Platform](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) للمستخدم أو المجموعة أو المثيل
+1. [Duo Agent Platform](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/) للمستخدم أو المجموعة أو المثيل
 2. Feature flags (عبر Rails console):
    - `agent_platform_claude_code`
    - `third_party_agents_enabled`
@@ -774,7 +774,7 @@ callback URL ‏`http://127.0.0.1:8080/callback` ونطاقات الصلاحيا
 export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```
 
-مزيد من التوثيق على صفحة [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth).
+مزيد من التوثيق على صفحة [opencode-gitlab-auth](https://www.npmjs.com/package/opencode-gitlab-auth).
 
 ##### التهيئة
 
@@ -786,11 +786,7 @@ export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
   "provider": {
     "gitlab": {
       "options": {
-        "instanceUrl": "https://gitlab.com",
-        "featureFlags": {
-          "duo_agent_platform_agentic_chat": true,
-          "duo_agent_platform": true
-        }
+        "instanceUrl": "https://gitlab.com"
       }
     }
   }
@@ -804,7 +800,7 @@ export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```json title="opencode.json"
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["@gitlab/opencode-gitlab-plugin"]
+  "plugin": ["opencode-gitlab-plugin"]
 }
 ```
 

+ 4 - 8
packages/web/src/content/docs/bs/providers.mdx

@@ -760,7 +760,7 @@ export GITLAB_TOKEN=glpat-...
 :::note
 Vaš GitLab administrator mora omogućiti sljedeće:
 
-1. [Duo Agent Platforma](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) za korisnika, grupu ili instancu
+1. [Duo Agent Platforma](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/) za korisnika, grupu ili instancu
 2. Zastavice funkcija (preko Rails konzole):
    - `agent_platform_claude_code`
    - `third_party_agents_enabled`
@@ -782,7 +782,7 @@ Zatim izložite ID aplikacije kao varijablu okruženja:
 export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```
 
-Više dokumentacije na početnoj stranici [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth).
+Više dokumentacije na početnoj stranici [opencode-gitlab-auth](https://www.npmjs.com/package/opencode-gitlab-auth).
 
 ##### Konfiguracija
 
@@ -794,11 +794,7 @@ Prilagodite putem `opencode.json`:
   "provider": {
     "gitlab": {
       "options": {
-        "instanceUrl": "https://gitlab.com",
-        "featureFlags": {
-          "duo_agent_platform_agentic_chat": true,
-          "duo_agent_platform": true
-        }
+        "instanceUrl": "https://gitlab.com"
       }
     }
   }
@@ -812,7 +808,7 @@ Za pristup GitLab alatima (zahtjevi za spajanje, problemi, cjevovodi, CI/CD, itd
 ```json title="opencode.json"
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["@gitlab/opencode-gitlab-plugin"]
+  "plugin": ["opencode-gitlab-plugin"]
 }
 ```
 

+ 4 - 8
packages/web/src/content/docs/da/providers.mdx

@@ -749,7 +749,7 @@ export GITLAB_TOKEN=glpat-...
 :::note
 Din GitLab-administrator skal aktivere følgende:
 
-1. [Duo Agent Platform](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) for brugeren, gruppen eller instansen
+1. [Duo Agent Platform](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/) for brugeren, gruppen eller instansen
 2. Funktionsflag (via Rails-konsollen):
    - `agent_platform_claude_code`
    - `third_party_agents_enabled`
@@ -771,7 +771,7 @@ Udsæt derefter applikations-ID som miljøvariabel:
 export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```
 
-Mere dokumentation på [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth) hjemmesiden.
+Mere dokumentation på [opencode-gitlab-auth](https://www.npmjs.com/package/opencode-gitlab-auth) hjemmesiden.
 
 ##### Konfiguration
 
@@ -783,11 +783,7 @@ Tilpas gennem `opencode.json`:
   "provider": {
     "gitlab": {
       "options": {
-        "instanceUrl": "https://gitlab.com",
-        "featureFlags": {
-          "duo_agent_platform_agentic_chat": true,
-          "duo_agent_platform": true
-        }
+        "instanceUrl": "https://gitlab.com"
       }
     }
   }
@@ -801,7 +797,7 @@ For at få adgang til GitLab-værktøjer (merge requests, problemer, pipelines,
 ```json title="opencode.json"
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["@gitlab/opencode-gitlab-plugin"]
+  "plugin": ["opencode-gitlab-plugin"]
 }
 ```
 

+ 4 - 8
packages/web/src/content/docs/de/providers.mdx

@@ -755,7 +755,7 @@ export GITLAB_TOKEN=glpat-...
 :::note
 Ihr GitLab-Administrator muss Folgendes aktivieren:
 
-1. [Duo Agent Platform](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) für den Benutzer, die Gruppe oder die Instanz
+1. [Duo Agent Platform](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/) für den Benutzer, die Gruppe oder die Instanz
 2. Feature-Flags (über die Rails-Konsole):
    - `agent_platform_claude_code`
    - `third_party_agents_enabled`
@@ -777,7 +777,7 @@ Stellen Sie dann die Anwendung ID als Umgebungsvariable bereit:
 export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```
 
-Weitere Dokumentation auf der [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth)-Homepage.
+Weitere Dokumentation auf der [opencode-gitlab-auth](https://www.npmjs.com/package/opencode-gitlab-auth)-Homepage.
 
 ##### Konfiguration
 
@@ -789,11 +789,7 @@ Anpassen über `opencode.json`:
   "provider": {
     "gitlab": {
       "options": {
-        "instanceUrl": "https://gitlab.com",
-        "featureFlags": {
-          "duo_agent_platform_agentic_chat": true,
-          "duo_agent_platform": true
-        }
+        "instanceUrl": "https://gitlab.com"
       }
     }
   }
@@ -807,7 +803,7 @@ So greifen Sie auf GitLab-Tools zu (Zusammenführungsanfragen, Probleme, Pipelin
 ```json title="opencode.json"
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["@gitlab/opencode-gitlab-plugin"]
+  "plugin": ["opencode-gitlab-plugin"]
 }
 ```
 

+ 4 - 8
packages/web/src/content/docs/es/providers.mdx

@@ -757,7 +757,7 @@ export GITLAB_TOKEN=glpat-...
 :::note
 Su administrador GitLab debe habilitar lo siguiente:
 
-1. [Plataforma de agente Duo](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) para el usuario, grupo o instancia
+1. [Plataforma de agente Duo](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/) para el usuario, grupo o instancia
 2. Indicadores de funciones (a través de la consola Rails):
    - `agent_platform_claude_code`
    - `third_party_agents_enabled`
@@ -779,7 +779,7 @@ Luego exponga el ID de la aplicación como variable de entorno:
 export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```
 
-Más documentación en la página de inicio de [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth).
+Más documentación en la página de inicio de [opencode-gitlab-auth](https://www.npmjs.com/package/opencode-gitlab-auth).
 
 ##### Configuración
 
@@ -791,11 +791,7 @@ Personalizar a través de `opencode.json`:
   "provider": {
     "gitlab": {
       "options": {
-        "instanceUrl": "https://gitlab.com",
-        "featureFlags": {
-          "duo_agent_platform_agentic_chat": true,
-          "duo_agent_platform": true
-        }
+        "instanceUrl": "https://gitlab.com"
       }
     }
   }
@@ -809,7 +805,7 @@ Para acceder a herramientas GitLab (solicitudes de fusión, problemas, canalizac
 ```json title="opencode.json"
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["@gitlab/opencode-gitlab-plugin"]
+  "plugin": ["opencode-gitlab-plugin"]
 }
 ```
 

+ 4 - 8
packages/web/src/content/docs/fr/providers.mdx

@@ -763,7 +763,7 @@ export GITLAB_TOKEN=glpat-...
 :::note
 Votre administrateur GitLab doit activer les éléments suivants :
 
-1. [Duo Agent Platform](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) pour l'utilisateur, le groupe ou l'instance
+1. [Duo Agent Platform](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/) pour l'utilisateur, le groupe ou l'instance
 2. Indicateurs de fonctionnalités (via la console Rails) :
    - `agent_platform_claude_code`
    - `third_party_agents_enabled`
@@ -785,7 +785,7 @@ Exposez ensuite l'ID de l'application en tant que variable d'environnement :
 export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```
 
-Plus de documentation sur la page d'accueil [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth).
+Plus de documentation sur la page d'accueil [opencode-gitlab-auth](https://www.npmjs.com/package/opencode-gitlab-auth).
 
 ##### Configuration
 
@@ -797,11 +797,7 @@ Personnalisez via `opencode.json` :
   "provider": {
     "gitlab": {
       "options": {
-        "instanceUrl": "https://gitlab.com",
-        "featureFlags": {
-          "duo_agent_platform_agentic_chat": true,
-          "duo_agent_platform": true
-        }
+        "instanceUrl": "https://gitlab.com"
       }
     }
   }
@@ -815,7 +811,7 @@ Pour accéder aux outils GitLab (demandes de fusion, tickets, pipelines, CI/CD,
 ```json title="opencode.json"
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["@gitlab/opencode-gitlab-plugin"]
+  "plugin": ["opencode-gitlab-plugin"]
 }
 ```
 

+ 4 - 8
packages/web/src/content/docs/it/providers.mdx

@@ -733,7 +733,7 @@ export GITLAB_TOKEN=glpat-...
 :::note
 Il tuo amministratore GitLab deve abilitare quanto segue:
 
-1. [Duo Agent Platform](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) per l'utente, gruppo o istanza
+1. [Duo Agent Platform](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/) per l'utente, gruppo o istanza
 2. Feature flags (via Rails console):
    - `agent_platform_claude_code`
    - `third_party_agents_enabled`
@@ -755,7 +755,7 @@ Poi esponi l'ID applicazione come variabile d'ambiente:
 export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```
 
-Maggior documentazione sulla homepage di [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth).
+Maggior documentazione sulla homepage di [opencode-gitlab-auth](https://www.npmjs.com/package/opencode-gitlab-auth).
 
 ##### Configurazione
 
@@ -767,11 +767,7 @@ Personalizza tramite `opencode.json`:
   "provider": {
     "gitlab": {
       "options": {
-        "instanceUrl": "https://gitlab.com",
-        "featureFlags": {
-          "duo_agent_platform_agentic_chat": true,
-          "duo_agent_platform": true
-        }
+        "instanceUrl": "https://gitlab.com"
       }
     }
   }
@@ -785,7 +781,7 @@ Per accedere agli strumenti GitLab (merge requests, issues, pipelines, CI/CD, ec
 ```json title="opencode.json"
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["@gitlab/opencode-gitlab-plugin"]
+  "plugin": ["opencode-gitlab-plugin"]
 }
 ```
 

+ 4 - 8
packages/web/src/content/docs/ja/providers.mdx

@@ -797,7 +797,7 @@ export GITLAB_TOKEN=glpat-...
 :::note
 GitLab 管理者は以下を有効にする必要があります。
 
-1. [Duo Agent Platform](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) (ユーザー、グループ、またはインスタンス用)
+1. [Duo Agent Platform](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/) (ユーザー、グループ、またはインスタンス用)
 2. 機能フラグ (Rails コンソール経由):
    - `agent_platform_claude_code`
    - `third_party_agents_enabled`
@@ -819,7 +819,7 @@ GitLab 管理者は以下を有効にする必要があります。
 export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```
 
-詳細については、[opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth) ホームページ。
+詳細については、[opencode-gitlab-auth](https://www.npmjs.com/package/opencode-gitlab-auth) ホームページ。
 
 ##### 設定
 
@@ -831,11 +831,7 @@ export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
   "provider": {
     "gitlab": {
       "options": {
-        "instanceUrl": "https://gitlab.com",
-        "featureFlags": {
-          "duo_agent_platform_agentic_chat": true,
-          "duo_agent_platform": true
-        }
+        "instanceUrl": "https://gitlab.com"
       }
     }
   }
@@ -849,7 +845,7 @@ GitLab ツール (マージリクエスト、問題、パイプライン、CI/CD
 ```json title="opencode.json"
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["@gitlab/opencode-gitlab-plugin"]
+  "plugin": ["opencode-gitlab-plugin"]
 }
 ```
 

+ 4 - 8
packages/web/src/content/docs/ko/providers.mdx

@@ -758,7 +758,7 @@ export GITLAB_TOKEN=glpat-...
 :::note
 GitLab 관리자는 다음을 활성화해야 합니다:
 
-1. [Duo Agent Platform](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) (사용자, 그룹 또는 인스턴스)
+1. [Duo Agent Platform](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/) (사용자, 그룹 또는 인스턴스)
 2. 기능 플래그 (Rails 콘솔을 통해):
    - `agent_platform_claude_code`
    - `third_party_agents_enabled`
@@ -779,7 +779,7 @@ GitLab 관리자는 다음을 활성화해야 합니다:
 export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```
 
-[opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth) 홈페이지에 추가 문서가 있습니다.
+[opencode-gitlab-auth](https://www.npmjs.com/package/opencode-gitlab-auth) 홈페이지에 추가 문서가 있습니다.
 
 #### 구성
 
@@ -791,11 +791,7 @@ export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
   "provider": {
     "gitlab": {
       "options": {
-        "instanceUrl": "https://gitlab.com",
-        "featureFlags": {
-          "duo_agent_platform_agentic_chat": true,
-          "duo_agent_platform": true
-        }
+        "instanceUrl": "https://gitlab.com"
       }
     }
   }
@@ -809,7 +805,7 @@ GitLab 도구(병합 요청, 이슈, 파이프라인, CI/CD 등)에 액세스하
 ```json title="opencode.json"
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["@gitlab/opencode-gitlab-plugin"]
+  "plugin": ["opencode-gitlab-plugin"]
 }
 ```
 

+ 4 - 8
packages/web/src/content/docs/nb/providers.mdx

@@ -757,7 +757,7 @@ export GITLAB_TOKEN=glpat-...
 :::note
 GitLab-administratoren din må aktivere følgende:
 
-1. [Duo Agent Platform](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) for brukeren, gruppen eller forekomsten
+1. [Duo Agent Platform](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/) for brukeren, gruppen eller forekomsten
 2. Funksjonsflagg (via Rails-konsollen):
    - `agent_platform_claude_code`
    - `third_party_agents_enabled`
@@ -779,7 +779,7 @@ Utsett deretter applikasjonen ID som miljøvariabel:
 export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```
 
-Mer dokumentasjon på [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth) hjemmeside.
+Mer dokumentasjon på [opencode-gitlab-auth](https://www.npmjs.com/package/opencode-gitlab-auth) hjemmeside.
 
 ##### Konfigurasjon
 
@@ -791,11 +791,7 @@ Tilpass gjennom `opencode.json`:
   "provider": {
     "gitlab": {
       "options": {
-        "instanceUrl": "https://gitlab.com",
-        "featureFlags": {
-          "duo_agent_platform_agentic_chat": true,
-          "duo_agent_platform": true
-        }
+        "instanceUrl": "https://gitlab.com"
       }
     }
   }
@@ -809,7 +805,7 @@ For å få tilgang til GitLab-verktøy (sammenslåingsforespørsler, problemer,
 ```json title="opencode.json"
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["@gitlab/opencode-gitlab-plugin"]
+  "plugin": ["opencode-gitlab-plugin"]
 }
 ```
 

+ 4 - 8
packages/web/src/content/docs/pl/providers.mdx

@@ -755,7 +755,7 @@ export GITLAB_TOKEN=glpat-...
 :::note
 Twój administrator GitLab musi włączyć następujące opcje:
 
-1. [Platforma Duo Agent](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) dla użytkownika, grupy lub instancji
+1. [Platforma Duo Agent](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/) dla użytkownika, grupy lub instancji
 2. Feature flags (via Rails console):
    - `agent_platform_claude_code`
    - `third_party_agents_enabled`
@@ -777,7 +777,7 @@ Następnie ustaw ID aplikacji jako zmienną środowiskową:
 export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```
 
-Więcej informacji znajdziesz na stronie [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth).
+Więcej informacji znajdziesz na stronie [opencode-gitlab-auth](https://www.npmjs.com/package/opencode-gitlab-auth).
 
 ##### Konfiguracja
 
@@ -789,11 +789,7 @@ Customize through `opencode.json`:
   "provider": {
     "gitlab": {
       "options": {
-        "instanceUrl": "https://gitlab.com",
-        "featureFlags": {
-          "duo_agent_platform_agentic_chat": true,
-          "duo_agent_platform": true
-        }
+        "instanceUrl": "https://gitlab.com"
       }
     }
   }
@@ -807,7 +803,7 @@ To access GitLab tools (merge requests, issues, pipelines, CI/CD, etc.):
 ```json title="opencode.json"
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["@gitlab/opencode-gitlab-plugin"]
+  "plugin": ["opencode-gitlab-plugin"]
 }
 ```
 

+ 40 - 15
packages/web/src/content/docs/providers.mdx

@@ -722,7 +722,20 @@ Cloudflare Workers AI lets you run AI models on Cloudflare's global network dire
 
 ### GitLab Duo
 
-GitLab Duo provides AI-powered agentic chat with native tool calling capabilities through GitLab's Anthropic proxy.
+:::caution[Experimental]
+GitLab Duo support in OpenCode is experimental. Features, configuration, and
+behavior may change in future releases.
+:::
+
+OpenCode integrates with the [GitLab Duo Agent Platform](https://docs.gitlab.com/user/duo_agent_platform/),
+providing AI-powered agentic chat with native tool calling capabilities.
+
+:::note[License requirements]
+GitLab Duo Agent Platform requires a **Premium** or **Ultimate** GitLab
+subscription. It is available on GitLab.com and GitLab Self-Managed.
+See [GitLab Duo Agent Platform prerequisites](https://docs.gitlab.com/user/duo_agent_platform/#prerequisites)
+for full requirements.
+:::
 
 1. Run the `/connect` command and select GitLab.
 
@@ -807,13 +820,15 @@ export GITLAB_TOKEN=glpat-...
 ```
 
 :::note
-Your GitLab administrator must enable the following:
-
-1. [Duo Agent Platform](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) for the user, group, or instance
-2. Feature flags (via Rails console):
-   - `agent_platform_claude_code`
-   - `third_party_agents_enabled`
-     :::
+Your GitLab administrator must:
+
+1. [Turn on GitLab Duo](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/#turn-gitlab-duo-on-or-off)
+   for the user, group, or instance
+2. [Turn on the Agent Platform](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/#turn-gitlab-duo-agent-platform-on-or-off)
+   (GitLab 18.8+) or [enable beta and experimental features](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/#turn-on-beta-and-experimental-features)
+   (GitLab 18.7 and earlier)
+3. For Self-Managed, [configure your instance](https://docs.gitlab.com/administration/gitlab_duo/configure/gitlab_self_managed/)
+   :::
 
 ##### OAuth for Self-Hosted instances
 
@@ -831,7 +846,7 @@ Then expose application ID as environment variable:
 export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```
 
-More documentation on [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth) homepage.
+More documentation on [opencode-gitlab-auth](https://www.npmjs.com/package/opencode-gitlab-auth) homepage.
 
 ##### Configuration
 
@@ -843,17 +858,27 @@ Customize through `opencode.json`:
   "provider": {
     "gitlab": {
       "options": {
-        "instanceUrl": "https://gitlab.com",
-        "featureFlags": {
-          "duo_agent_platform_agentic_chat": true,
-          "duo_agent_platform": true
-        }
+        "instanceUrl": "https://gitlab.com"
       }
     }
   }
 }
 ```
 
+##### GitLab Duo Agent Platform (DAP) Workflow Models
+
+DAP workflow models provide an alternative execution path that routes tool calls
+through GitLab's Duo Workflow Service (DWS) instead of the standard agentic chat.
+When a `duo-workflow-*` model is selected, OpenCode will:
+
+1. Discover available models from your GitLab namespace
+2. Present a selection picker if multiple models are available
+3. Cache the selected model to disk for fast subsequent startups
+4. Route tool execution requests through OpenCode's permission-gated tool system
+
+Available DAP workflow models follow the `duo-workflow-*` naming convention and
+are dynamically discovered from your GitLab instance.
+
 ##### GitLab API Tools (Optional, but highly recommended)
 
 To access GitLab tools (merge requests, issues, pipelines, CI/CD, etc.):
@@ -861,7 +886,7 @@ To access GitLab tools (merge requests, issues, pipelines, CI/CD, etc.):
 ```json title="opencode.json"
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["@gitlab/opencode-gitlab-plugin"]
+  "plugin": ["opencode-gitlab-plugin"]
 }
 ```
 

+ 4 - 8
packages/web/src/content/docs/pt-br/providers.mdx

@@ -759,7 +759,7 @@ export GITLAB_TOKEN=glpat-...
 :::note
 Seu administrador do GitLab deve habilitar o seguinte:
 
-1. [Duo Agent Platform](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) para o usuário, grupo ou instância
+1. [Duo Agent Platform](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/) para o usuário, grupo ou instância
 2. Flags de recurso (via console Rails):
    - `agent_platform_claude_code`
    - `third_party_agents_enabled`
@@ -781,7 +781,7 @@ Em seguida, exponha o ID do aplicativo como variável de ambiente:
 export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```
 
-Mais documentação na página [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth).
+Mais documentação na página [opencode-gitlab-auth](https://www.npmjs.com/package/opencode-gitlab-auth).
 
 ##### Configuração
 
@@ -793,11 +793,7 @@ Personalize através do `opencode.json`:
   "provider": {
     "gitlab": {
       "options": {
-        "instanceUrl": "https://gitlab.com",
-        "featureFlags": {
-          "duo_agent_platform_agentic_chat": true,
-          "duo_agent_platform": true
-        }
+        "instanceUrl": "https://gitlab.com"
       }
     }
   }
@@ -811,7 +807,7 @@ Para acessar ferramentas do GitLab (merge requests, issues, pipelines, CI/CD, et
 ```json title="opencode.json"
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["@gitlab/opencode-gitlab-plugin"]
+  "plugin": ["opencode-gitlab-plugin"]
 }
 ```
 

+ 4 - 8
packages/web/src/content/docs/ru/providers.mdx

@@ -755,7 +755,7 @@ export GITLAB_TOKEN=glpat-...
 :::note
 Ваш администратор GitLab должен включить следующее:
 
-1. [Платформа Duo Agent](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) для пользователя, группы или экземпляра
+1. [Платформа Duo Agent](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/) для пользователя, группы или экземпляра
 2. Флаги функций (через консоль Rails):
    - `agent_platform_claude_code`
    - `third_party_agents_enabled`
@@ -777,7 +777,7 @@ URL обратного вызова `http://127.0.0.1:8080/callback` и след
 export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```
 
-Дополнительная документация на домашней странице [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth).
+Дополнительная документация на домашней странице [opencode-gitlab-auth](https://www.npmjs.com/package/opencode-gitlab-auth).
 
 ##### Конфигурация
 
@@ -789,11 +789,7 @@ export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
   "provider": {
     "gitlab": {
       "options": {
-        "instanceUrl": "https://gitlab.com",
-        "featureFlags": {
-          "duo_agent_platform_agentic_chat": true,
-          "duo_agent_platform": true
-        }
+        "instanceUrl": "https://gitlab.com"
       }
     }
   }
@@ -807,7 +803,7 @@ export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```json title="opencode.json"
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["@gitlab/opencode-gitlab-plugin"]
+  "plugin": ["opencode-gitlab-plugin"]
 }
 ```
 

+ 4 - 8
packages/web/src/content/docs/th/providers.mdx

@@ -756,7 +756,7 @@ export GITLAB_TOKEN=glpat-...
 :::note
 ผู้ดูแลระบบ GitLab ของคุณต้องเปิดใช้งานสิ่งต่อไปนี้:
 
-1. [Duo Agent Platform](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/) สำหรับผู้ใช้ กลุ่ม หรืออินสแตนซ์
+1. [Duo Agent Platform](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/) สำหรับผู้ใช้ กลุ่ม หรืออินสแตนซ์
 2. แฟล็กคุณลักษณะ (ผ่านคอนโซล Rails):
    - `agent_platform_claude_code`
    - `third_party_agents_enabled`
@@ -778,7 +778,7 @@ export GITLAB_TOKEN=glpat-...
 export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```
 
-เอกสารประกอบเพิ่มเติมเกี่ยวกับหน้าแรกของ [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth)
+เอกสารประกอบเพิ่มเติมเกี่ยวกับหน้าแรกของ [opencode-gitlab-auth](https://www.npmjs.com/package/opencode-gitlab-auth)
 
 ##### การกำหนดค่า
 
@@ -790,11 +790,7 @@ export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
   "provider": {
     "gitlab": {
       "options": {
-        "instanceUrl": "https://gitlab.com",
-        "featureFlags": {
-          "duo_agent_platform_agentic_chat": true,
-          "duo_agent_platform": true
-        }
+        "instanceUrl": "https://gitlab.com"
       }
     }
   }
@@ -808,7 +804,7 @@ export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```json title="opencode.json"
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["@gitlab/opencode-gitlab-plugin"]
+  "plugin": ["opencode-gitlab-plugin"]
 }
 ```
 

+ 4 - 8
packages/web/src/content/docs/tr/providers.mdx

@@ -757,7 +757,7 @@ export GITLAB_TOKEN=glpat-...
 :::note
 GitLab yöneticiniz aşağıdakileri etkinleştirmelidir:
 
-1. Kullanıcı, grup veya örnek için [Duo Agent Platform](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/)
+1. Kullanıcı, grup veya örnek için [Duo Agent Platform](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/)
 2. Feature flags (via Rails console):
    - `agent_platform_claude_code`
    - `third_party_agents_enabled`
@@ -779,7 +779,7 @@ Then expose application ID as environment variable:
 export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```
 
-Daha fazla belge [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth) ana sayfasında.
+Daha fazla belge [opencode-gitlab-auth](https://www.npmjs.com/package/opencode-gitlab-auth) ana sayfasında.
 
 ##### Yapılandırma
 
@@ -791,11 +791,7 @@ Daha fazla belge [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/op
   "provider": {
     "gitlab": {
       "options": {
-        "instanceUrl": "https://gitlab.com",
-        "featureFlags": {
-          "duo_agent_platform_agentic_chat": true,
-          "duo_agent_platform": true
-        }
+        "instanceUrl": "https://gitlab.com"
       }
     }
   }
@@ -809,7 +805,7 @@ GitLab araçlarına (birleştirme istekleri, sorunlar, işlem hatları, CI/CD vb
 ```json title="opencode.json"
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["@gitlab/opencode-gitlab-plugin"]
+  "plugin": ["opencode-gitlab-plugin"]
 }
 ```
 

+ 4 - 8
packages/web/src/content/docs/zh-cn/providers.mdx

@@ -725,7 +725,7 @@ export GITLAB_TOKEN=glpat-...
 :::note
 你的 GitLab 管理员必须启用以下功能:
 
-1. 为用户、群组或实例启用 [Duo Agent Platform](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/)
+1. 为用户、群组或实例启用 [Duo Agent Platform](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/)
 2. 功能标志(通过 Rails 控制台):
    - `agent_platform_claude_code`
    - `third_party_agents_enabled`
@@ -745,7 +745,7 @@ export GITLAB_TOKEN=glpat-...
 export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```
 
-更多文档请参阅 [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth) 主页。
+更多文档请参阅 [opencode-gitlab-auth](https://www.npmjs.com/package/opencode-gitlab-auth) 主页。
 
 ##### 配置
 
@@ -757,11 +757,7 @@ export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
   "provider": {
     "gitlab": {
       "options": {
-        "instanceUrl": "https://gitlab.com",
-        "featureFlags": {
-          "duo_agent_platform_agentic_chat": true,
-          "duo_agent_platform": true
-        }
+        "instanceUrl": "https://gitlab.com"
       }
     }
   }
@@ -775,7 +771,7 @@ export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```json title="opencode.json"
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["@gitlab/opencode-gitlab-plugin"]
+  "plugin": ["opencode-gitlab-plugin"]
 }
 ```
 

+ 4 - 8
packages/web/src/content/docs/zh-tw/providers.mdx

@@ -746,7 +746,7 @@ export GITLAB_TOKEN=glpat-...
 :::note
 您的 GitLab 管理員必須啟用以下功能:
 
-1. 為使用者、群組或實例啟用 [Duo Agent Platform](https://docs.gitlab.com/user/gitlab_duo/turn_on_off/)
+1. 為使用者、群組或實例啟用 [Duo Agent Platform](https://docs.gitlab.com/user/duo_agent_platform/turn_on_off/)
 2. 功能旗標(透過 Rails 控制台):
    - `agent_platform_claude_code`
    - `third_party_agents_enabled`
@@ -766,7 +766,7 @@ export GITLAB_TOKEN=glpat-...
 export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```
 
-更多文件請參閱 [opencode-gitlab-auth](https://www.npmjs.com/package/@gitlab/opencode-gitlab-auth) 首頁。
+更多文件請參閱 [opencode-gitlab-auth](https://www.npmjs.com/package/opencode-gitlab-auth) 首頁。
 
 ##### 設定
 
@@ -778,11 +778,7 @@ export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
   "provider": {
     "gitlab": {
       "options": {
-        "instanceUrl": "https://gitlab.com",
-        "featureFlags": {
-          "duo_agent_platform_agentic_chat": true,
-          "duo_agent_platform": true
-        }
+        "instanceUrl": "https://gitlab.com"
       }
     }
   }
@@ -796,7 +792,7 @@ export GITLAB_OAUTH_CLIENT_ID=your_application_id_here
 ```json title="opencode.json"
 {
   "$schema": "https://opencode.ai/config.json",
-  "plugin": ["@gitlab/opencode-gitlab-plugin"]
+  "plugin": ["opencode-gitlab-plugin"]
 }
 ```