gitlab-ai-provider 6.15.0 → 6.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,16 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
4
4
 
5
+ ## 6.16.0 (2026-09-22)
6
+
7
+ - Merge branch 'feature-add-opus-5-5' into 'main' ([c1a5f12](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/c1a5f12))
8
+ - feat(models): add Claude Opus 5.5 model mappings ([6b6efe4](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/6b6efe4))
9
+
10
+ ## <small>6.15.1 (2026-09-09)</small>
11
+
12
+ - Merge branch 'fix/agent-platform-model-discovery' into 'main' ([98c1fa7](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/98c1fa7))
13
+ - fix(discovery): scope Agent Platform model discovery by project and correct stale workflow refs ([9dd9681](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/9dd9681))
14
+
5
15
  ## 6.15.0 (2026-09-07)
6
16
 
7
17
  - Merge branch 'feature-add-gpt-6-astra' into 'main' ([19d1e4f](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/19d1e4f))
package/README.md CHANGED
@@ -123,6 +123,7 @@ const customModel = gitlab.agenticChat('duo-chat-opus-4-5', {
123
123
  | ------------------------ | --------- | ---------------------------- |
124
124
  | `duo-chat-fable-5-1` | Anthropic | `claude-fable-5-1` |
125
125
  | `duo-chat-fable-5` | Anthropic | `claude-fable-5` |
126
+ | `duo-chat-opus-5-5` | Anthropic | `claude-opus-5-5` |
126
127
  | `duo-chat-opus-5` | Anthropic | `claude-opus-5` |
127
128
  | `duo-chat-opus-4-8` | Anthropic | `claude-opus-4-8` |
128
129
  | `duo-chat-opus-4-7` | Anthropic | `claude-opus-4-7` |
@@ -629,7 +630,14 @@ const directToken = await client.getDirectAccessToken();
629
630
 
630
631
  #### GitLabModelDiscovery
631
632
 
632
- Discovers available workflow models via GraphQL.
633
+ Discovers available workflow models via GraphQL. Scope by `projectId`
634
+ (preferred — works for both group- and user-namespace projects) or
635
+ `rootNamespaceId` (group namespaces only, supported on older GitLab
636
+ instances). A bare string is treated as `rootNamespaceId` for backwards
637
+ compatibility. When scoped by `projectId` with a `rootNamespaceId` also
638
+ supplied, discovery falls back to the namespace-scoped query if the
639
+ `projectId` query fails (e.g. on GitLab versions predating `projectId`
640
+ support).
633
641
 
634
642
  ```typescript
635
643
  const discovery = new GitLabModelDiscovery({
@@ -637,11 +645,14 @@ const discovery = new GitLabModelDiscovery({
637
645
  getHeaders: () => ({ Authorization: `Bearer ${token}` }),
638
646
  });
639
647
 
640
- const models = await discovery.discover('gid://gitlab/Group/12345');
648
+ const models = await discovery.discover({
649
+ projectId: 'gid://gitlab/Project/12345',
650
+ rootNamespaceId: 'gid://gitlab/Group/6789', // optional fallback
651
+ });
641
652
  // Returns: { defaultModel, selectableModels, pinnedModel, modelSwitchingEnabled }
642
653
 
643
654
  const effectiveRef = await discovery.getEffectiveModelRef(
644
- 'gid://gitlab/Group/12345',
655
+ { projectId: 'gid://gitlab/Project/12345' },
645
656
  'claude_sonnet_4_6'
646
657
  );
647
658
  ```
package/dist/index.d.mts CHANGED
@@ -161,15 +161,18 @@ declare class GitLabAnthropicLanguageModel implements LanguageModelV3 {
161
161
  * Dynamic model discovery via GitLab GraphQL API.
162
162
  *
163
163
  * Queries `aiChatAvailableModels` to discover which models are available
164
- * for a given GitLab namespace. Used to dynamically populate `duo-workflow-*`
165
- * model IDs at runtime.
164
+ * for a given GitLab project or namespace. Used to dynamically populate
165
+ * `duo-workflow-*` model IDs at runtime.
166
166
  *
167
- * Requires GitLab 18.4+ (18.5+ for pinned model support).
167
+ * Requires GitLab 18.4+ (18.5+ for pinned model support). `projectId` scoping
168
+ * requires a newer GitLab release than `rootNamespaceId` scoping; when a
169
+ * `projectId` query fails and a `rootNamespaceId` is also known, discovery
170
+ * falls back to the namespace-scoped query.
168
171
  */
169
172
  interface AiModel {
170
173
  /** Display name (e.g., 'Claude Sonnet 4.6') */
171
174
  name: string;
172
- /** Model reference for API use (e.g., 'claude_sonnet_4_6' or 'anthropic/claude-sonnet-4-5-20250929') */
175
+ /** Model reference for API use (e.g., 'claude_sonnet_4_6') */
173
176
  ref: string;
174
177
  }
175
178
  interface AiChatAvailableModels {
@@ -197,29 +200,43 @@ interface ModelDiscoveryConfig {
197
200
  /** Custom fetch */
198
201
  fetch?: typeof fetch;
199
202
  }
203
+ /**
204
+ * Scope to discover models for. `projectId` is preferred (works for both
205
+ * group- and user-namespace projects). An optional `rootNamespaceId` may be
206
+ * supplied alongside it as a fallback for GitLab instances too old to
207
+ * support `projectId`-scoped discovery.
208
+ */
209
+ type DiscoveryScope = {
210
+ projectId: string;
211
+ rootNamespaceId?: string;
212
+ } | {
213
+ rootNamespaceId: string;
214
+ };
200
215
  declare class GitLabModelDiscovery {
201
216
  private readonly config;
202
217
  private readonly fetchFn;
203
218
  private cache;
204
219
  constructor(config: ModelDiscoveryConfig);
205
220
  /**
206
- * Discover available models for a given root namespace.
221
+ * Discover available models for a given project or root namespace.
207
222
  *
208
- * Results are cached per `rootNamespaceId` with a 10-minute TTL.
223
+ * Results are cached per scope with a 10-minute TTL.
209
224
  * Use `invalidateCache()` to force an immediate refresh.
210
225
  *
211
- * @param rootNamespaceId - GitLab group ID (e.g., 'gid://gitlab/Group/12345')
226
+ * @param scope - `{ projectId }` (preferred), `{ rootNamespaceId }`, or a
227
+ * bare string (treated as `rootNamespaceId` for backwards compatibility)
212
228
  */
213
- discover(rootNamespaceId: string): Promise<DiscoveredModels>;
229
+ discover(scope: DiscoveryScope | string): Promise<DiscoveredModels>;
230
+ private query;
214
231
  /**
215
232
  * Get the effective model ref to use for a workflow.
216
233
  *
217
234
  * Priority: pinned > user-selected > default.
218
235
  *
219
- * @param rootNamespaceId - GitLab group ID
236
+ * @param scope - Project or root namespace to discover models for
220
237
  * @param userSelectedRef - Optional user preference
221
238
  */
222
- getEffectiveModelRef(rootNamespaceId: string, userSelectedRef?: string): Promise<string | null>;
239
+ getEffectiveModelRef(scope: DiscoveryScope | string, userSelectedRef?: string): Promise<string | null>;
223
240
  /**
224
241
  * Invalidate the cached discovery results.
225
242
  */
@@ -885,6 +902,14 @@ declare class GitLabWorkflowLanguageModel implements LanguageModelV3 {
885
902
  * 3. Cached from previous call
886
903
  */
887
904
  private resolveRootNamespaceId;
905
+ /**
906
+ * Resolve the model discovery scope to use for `aiChatAvailableModels`.
907
+ *
908
+ * `projectId` is preferred — it works for both group- and user-namespace
909
+ * projects — with `rootNamespaceId` attached as a fallback for GitLab
910
+ * instances too old to support `projectId`-scoped discovery.
911
+ */
912
+ private resolveDiscoveryScope;
888
913
  /**
889
914
  * Resolve the effective DWS model ref to use for this stream.
890
915
  * Deduplicates concurrent calls via a shared promise.
@@ -905,10 +930,12 @@ declare class GitLabWorkflowLanguageModel implements LanguageModelV3 {
905
930
  * Call this early (e.g., on IDE startup) to avoid blocking the first stream.
906
931
  * Results are persisted to the workspace model cache.
907
932
  *
908
- * @param rootNamespaceId - GitLab group ID (e.g., 'gid://gitlab/Group/12345')
933
+ * @param scope - Project or root namespace GID to discover models for.
934
+ * A bare string is treated as a `rootNamespaceId` (e.g.
935
+ * 'gid://gitlab/Group/12345') for backwards compatibility.
909
936
  * @returns Discovered models with default, selectable, and pinned models
910
937
  */
911
- discoverModels(rootNamespaceId: string): Promise<DiscoveredModels>;
938
+ discoverModels(scope: DiscoveryScope | string): Promise<DiscoveredModels>;
912
939
  /**
913
940
  * Get the file-based model cache instance for this workspace.
914
941
  * Useful for consumers that need direct cache access (e.g., the discover route).
@@ -1851,4 +1878,4 @@ interface WorkflowDiscoveryResult {
1851
1878
  }
1852
1879
  declare function discoverWorkflowModels(config: WorkflowDiscoveryConfig, options: WorkflowDiscoveryOptions): Promise<WorkflowDiscoveryResult>;
1853
1880
 
1854
- export { AGENT_PRIVILEGES, type ActionResponsePayload, type AdditionalContext, type AiChatAvailableModels, type AiModel, type ApprovalDecision, BUNDLED_CLIENT_ID, CLIENT_VERSION, type ClientEvent, DEFAULT_AGENT_PRIVILEGES, DEFAULT_AI_GATEWAY_URL, DEFAULT_CLIENT_CAPABILITIES, DEFAULT_WORKFLOW_DEFINITION, type DirectAccessToken, type DiscoveredModels, type DiscoveredWorkflowModel, GITLAB_COM_URL, type GenerateTokenResponse, type GitLabAgenticOptions, type GitLabAnthropicConfig, GitLabAnthropicLanguageModel, GitLabDirectAccessClient, type GitLabDirectAccessConfig, GitLabError, type GitLabErrorOptions, GitLabModelCache, GitLabModelConfigRegistry, GitLabModelDiscovery, GitLabOAuthManager, type GitLabOAuthTokenResponse, type GitLabOAuthTokens, type GitLabOpenAIConfig, GitLabOpenAILanguageModel, type GitLabProject, GitLabProjectCache, GitLabProjectDetector, type GitLabProjectDetectorConfig, type GitLabProvider, type GitLabProviderSettings, type GitLabThinkingConfig, GitLabWorkflowClient, type GitLabWorkflowClientConfig, GitLabWorkflowLanguageModel, type GitLabWorkflowLanguageModelConfig, type GitLabWorkflowOptions, GitLabWorkflowTokenClient, MODEL_ID_TO_ANTHROPIC_MODEL, MODEL_MAPPINGS, type McpToolDefinition, type ModelCacheEntry, type ModelConfig, type ModelConfigRegistryOptions, type ModelDiscoveryConfig, type ModelMapping, type ModelProvider, type NewCheckpoint, OAUTH_SCOPES, OPENCODE_GITLAB_AUTH_CLIENT_ID, type OpenAIApiType, type OpenCodeAuth, type OpenCodeAuthApi, type OpenCodeAuthOAuth, type RunMcpTool, type StartRequest, TOKEN_EXPIRY_SKEW_MS, VERSION, WORKFLOW_ENVIRONMENT, WS_HEARTBEAT_INTERVAL_MS, WS_KEEPALIVE_PING_INTERVAL_MS, type WorkflowAction, type WorkflowClientEvent, type WorkflowDiscoveryConfig, type WorkflowDiscoveryOptions, type WorkflowDiscoveryResult, type WorkflowStatus, type WorkflowToolExecutor, WorkflowType, type WorkflowWebSocketOptions, createGitLab, discoverWorkflowModels, getAnthropicModelForModelId, getModelMapping, getOpenAIApiType, getOpenAIModelForModelId, getProviderForModelId, getValidModelsForProvider, getWorkflowModelRef, gitlab, gitlabOAuthTokenResponseSchema, isResponsesApiModel, isWorkflowModel, parseModelsYml };
1881
+ export { AGENT_PRIVILEGES, type ActionResponsePayload, type AdditionalContext, type AiChatAvailableModels, type AiModel, type ApprovalDecision, BUNDLED_CLIENT_ID, CLIENT_VERSION, type ClientEvent, DEFAULT_AGENT_PRIVILEGES, DEFAULT_AI_GATEWAY_URL, DEFAULT_CLIENT_CAPABILITIES, DEFAULT_WORKFLOW_DEFINITION, type DirectAccessToken, type DiscoveredModels, type DiscoveredWorkflowModel, type DiscoveryScope, GITLAB_COM_URL, type GenerateTokenResponse, type GitLabAgenticOptions, type GitLabAnthropicConfig, GitLabAnthropicLanguageModel, GitLabDirectAccessClient, type GitLabDirectAccessConfig, GitLabError, type GitLabErrorOptions, GitLabModelCache, GitLabModelConfigRegistry, GitLabModelDiscovery, GitLabOAuthManager, type GitLabOAuthTokenResponse, type GitLabOAuthTokens, type GitLabOpenAIConfig, GitLabOpenAILanguageModel, type GitLabProject, GitLabProjectCache, GitLabProjectDetector, type GitLabProjectDetectorConfig, type GitLabProvider, type GitLabProviderSettings, type GitLabThinkingConfig, GitLabWorkflowClient, type GitLabWorkflowClientConfig, GitLabWorkflowLanguageModel, type GitLabWorkflowLanguageModelConfig, type GitLabWorkflowOptions, GitLabWorkflowTokenClient, MODEL_ID_TO_ANTHROPIC_MODEL, MODEL_MAPPINGS, type McpToolDefinition, type ModelCacheEntry, type ModelConfig, type ModelConfigRegistryOptions, type ModelDiscoveryConfig, type ModelMapping, type ModelProvider, type NewCheckpoint, OAUTH_SCOPES, OPENCODE_GITLAB_AUTH_CLIENT_ID, type OpenAIApiType, type OpenCodeAuth, type OpenCodeAuthApi, type OpenCodeAuthOAuth, type RunMcpTool, type StartRequest, TOKEN_EXPIRY_SKEW_MS, VERSION, WORKFLOW_ENVIRONMENT, WS_HEARTBEAT_INTERVAL_MS, WS_KEEPALIVE_PING_INTERVAL_MS, type WorkflowAction, type WorkflowClientEvent, type WorkflowDiscoveryConfig, type WorkflowDiscoveryOptions, type WorkflowDiscoveryResult, type WorkflowStatus, type WorkflowToolExecutor, WorkflowType, type WorkflowWebSocketOptions, createGitLab, discoverWorkflowModels, getAnthropicModelForModelId, getModelMapping, getOpenAIApiType, getOpenAIModelForModelId, getProviderForModelId, getValidModelsForProvider, getWorkflowModelRef, gitlab, gitlabOAuthTokenResponseSchema, isResponsesApiModel, isWorkflowModel, parseModelsYml };
package/dist/index.d.ts CHANGED
@@ -161,15 +161,18 @@ declare class GitLabAnthropicLanguageModel implements LanguageModelV3 {
161
161
  * Dynamic model discovery via GitLab GraphQL API.
162
162
  *
163
163
  * Queries `aiChatAvailableModels` to discover which models are available
164
- * for a given GitLab namespace. Used to dynamically populate `duo-workflow-*`
165
- * model IDs at runtime.
164
+ * for a given GitLab project or namespace. Used to dynamically populate
165
+ * `duo-workflow-*` model IDs at runtime.
166
166
  *
167
- * Requires GitLab 18.4+ (18.5+ for pinned model support).
167
+ * Requires GitLab 18.4+ (18.5+ for pinned model support). `projectId` scoping
168
+ * requires a newer GitLab release than `rootNamespaceId` scoping; when a
169
+ * `projectId` query fails and a `rootNamespaceId` is also known, discovery
170
+ * falls back to the namespace-scoped query.
168
171
  */
169
172
  interface AiModel {
170
173
  /** Display name (e.g., 'Claude Sonnet 4.6') */
171
174
  name: string;
172
- /** Model reference for API use (e.g., 'claude_sonnet_4_6' or 'anthropic/claude-sonnet-4-5-20250929') */
175
+ /** Model reference for API use (e.g., 'claude_sonnet_4_6') */
173
176
  ref: string;
174
177
  }
175
178
  interface AiChatAvailableModels {
@@ -197,29 +200,43 @@ interface ModelDiscoveryConfig {
197
200
  /** Custom fetch */
198
201
  fetch?: typeof fetch;
199
202
  }
203
+ /**
204
+ * Scope to discover models for. `projectId` is preferred (works for both
205
+ * group- and user-namespace projects). An optional `rootNamespaceId` may be
206
+ * supplied alongside it as a fallback for GitLab instances too old to
207
+ * support `projectId`-scoped discovery.
208
+ */
209
+ type DiscoveryScope = {
210
+ projectId: string;
211
+ rootNamespaceId?: string;
212
+ } | {
213
+ rootNamespaceId: string;
214
+ };
200
215
  declare class GitLabModelDiscovery {
201
216
  private readonly config;
202
217
  private readonly fetchFn;
203
218
  private cache;
204
219
  constructor(config: ModelDiscoveryConfig);
205
220
  /**
206
- * Discover available models for a given root namespace.
221
+ * Discover available models for a given project or root namespace.
207
222
  *
208
- * Results are cached per `rootNamespaceId` with a 10-minute TTL.
223
+ * Results are cached per scope with a 10-minute TTL.
209
224
  * Use `invalidateCache()` to force an immediate refresh.
210
225
  *
211
- * @param rootNamespaceId - GitLab group ID (e.g., 'gid://gitlab/Group/12345')
226
+ * @param scope - `{ projectId }` (preferred), `{ rootNamespaceId }`, or a
227
+ * bare string (treated as `rootNamespaceId` for backwards compatibility)
212
228
  */
213
- discover(rootNamespaceId: string): Promise<DiscoveredModels>;
229
+ discover(scope: DiscoveryScope | string): Promise<DiscoveredModels>;
230
+ private query;
214
231
  /**
215
232
  * Get the effective model ref to use for a workflow.
216
233
  *
217
234
  * Priority: pinned > user-selected > default.
218
235
  *
219
- * @param rootNamespaceId - GitLab group ID
236
+ * @param scope - Project or root namespace to discover models for
220
237
  * @param userSelectedRef - Optional user preference
221
238
  */
222
- getEffectiveModelRef(rootNamespaceId: string, userSelectedRef?: string): Promise<string | null>;
239
+ getEffectiveModelRef(scope: DiscoveryScope | string, userSelectedRef?: string): Promise<string | null>;
223
240
  /**
224
241
  * Invalidate the cached discovery results.
225
242
  */
@@ -885,6 +902,14 @@ declare class GitLabWorkflowLanguageModel implements LanguageModelV3 {
885
902
  * 3. Cached from previous call
886
903
  */
887
904
  private resolveRootNamespaceId;
905
+ /**
906
+ * Resolve the model discovery scope to use for `aiChatAvailableModels`.
907
+ *
908
+ * `projectId` is preferred — it works for both group- and user-namespace
909
+ * projects — with `rootNamespaceId` attached as a fallback for GitLab
910
+ * instances too old to support `projectId`-scoped discovery.
911
+ */
912
+ private resolveDiscoveryScope;
888
913
  /**
889
914
  * Resolve the effective DWS model ref to use for this stream.
890
915
  * Deduplicates concurrent calls via a shared promise.
@@ -905,10 +930,12 @@ declare class GitLabWorkflowLanguageModel implements LanguageModelV3 {
905
930
  * Call this early (e.g., on IDE startup) to avoid blocking the first stream.
906
931
  * Results are persisted to the workspace model cache.
907
932
  *
908
- * @param rootNamespaceId - GitLab group ID (e.g., 'gid://gitlab/Group/12345')
933
+ * @param scope - Project or root namespace GID to discover models for.
934
+ * A bare string is treated as a `rootNamespaceId` (e.g.
935
+ * 'gid://gitlab/Group/12345') for backwards compatibility.
909
936
  * @returns Discovered models with default, selectable, and pinned models
910
937
  */
911
- discoverModels(rootNamespaceId: string): Promise<DiscoveredModels>;
938
+ discoverModels(scope: DiscoveryScope | string): Promise<DiscoveredModels>;
912
939
  /**
913
940
  * Get the file-based model cache instance for this workspace.
914
941
  * Useful for consumers that need direct cache access (e.g., the discover route).
@@ -1851,4 +1878,4 @@ interface WorkflowDiscoveryResult {
1851
1878
  }
1852
1879
  declare function discoverWorkflowModels(config: WorkflowDiscoveryConfig, options: WorkflowDiscoveryOptions): Promise<WorkflowDiscoveryResult>;
1853
1880
 
1854
- export { AGENT_PRIVILEGES, type ActionResponsePayload, type AdditionalContext, type AiChatAvailableModels, type AiModel, type ApprovalDecision, BUNDLED_CLIENT_ID, CLIENT_VERSION, type ClientEvent, DEFAULT_AGENT_PRIVILEGES, DEFAULT_AI_GATEWAY_URL, DEFAULT_CLIENT_CAPABILITIES, DEFAULT_WORKFLOW_DEFINITION, type DirectAccessToken, type DiscoveredModels, type DiscoveredWorkflowModel, GITLAB_COM_URL, type GenerateTokenResponse, type GitLabAgenticOptions, type GitLabAnthropicConfig, GitLabAnthropicLanguageModel, GitLabDirectAccessClient, type GitLabDirectAccessConfig, GitLabError, type GitLabErrorOptions, GitLabModelCache, GitLabModelConfigRegistry, GitLabModelDiscovery, GitLabOAuthManager, type GitLabOAuthTokenResponse, type GitLabOAuthTokens, type GitLabOpenAIConfig, GitLabOpenAILanguageModel, type GitLabProject, GitLabProjectCache, GitLabProjectDetector, type GitLabProjectDetectorConfig, type GitLabProvider, type GitLabProviderSettings, type GitLabThinkingConfig, GitLabWorkflowClient, type GitLabWorkflowClientConfig, GitLabWorkflowLanguageModel, type GitLabWorkflowLanguageModelConfig, type GitLabWorkflowOptions, GitLabWorkflowTokenClient, MODEL_ID_TO_ANTHROPIC_MODEL, MODEL_MAPPINGS, type McpToolDefinition, type ModelCacheEntry, type ModelConfig, type ModelConfigRegistryOptions, type ModelDiscoveryConfig, type ModelMapping, type ModelProvider, type NewCheckpoint, OAUTH_SCOPES, OPENCODE_GITLAB_AUTH_CLIENT_ID, type OpenAIApiType, type OpenCodeAuth, type OpenCodeAuthApi, type OpenCodeAuthOAuth, type RunMcpTool, type StartRequest, TOKEN_EXPIRY_SKEW_MS, VERSION, WORKFLOW_ENVIRONMENT, WS_HEARTBEAT_INTERVAL_MS, WS_KEEPALIVE_PING_INTERVAL_MS, type WorkflowAction, type WorkflowClientEvent, type WorkflowDiscoveryConfig, type WorkflowDiscoveryOptions, type WorkflowDiscoveryResult, type WorkflowStatus, type WorkflowToolExecutor, WorkflowType, type WorkflowWebSocketOptions, createGitLab, discoverWorkflowModels, getAnthropicModelForModelId, getModelMapping, getOpenAIApiType, getOpenAIModelForModelId, getProviderForModelId, getValidModelsForProvider, getWorkflowModelRef, gitlab, gitlabOAuthTokenResponseSchema, isResponsesApiModel, isWorkflowModel, parseModelsYml };
1881
+ export { AGENT_PRIVILEGES, type ActionResponsePayload, type AdditionalContext, type AiChatAvailableModels, type AiModel, type ApprovalDecision, BUNDLED_CLIENT_ID, CLIENT_VERSION, type ClientEvent, DEFAULT_AGENT_PRIVILEGES, DEFAULT_AI_GATEWAY_URL, DEFAULT_CLIENT_CAPABILITIES, DEFAULT_WORKFLOW_DEFINITION, type DirectAccessToken, type DiscoveredModels, type DiscoveredWorkflowModel, type DiscoveryScope, GITLAB_COM_URL, type GenerateTokenResponse, type GitLabAgenticOptions, type GitLabAnthropicConfig, GitLabAnthropicLanguageModel, GitLabDirectAccessClient, type GitLabDirectAccessConfig, GitLabError, type GitLabErrorOptions, GitLabModelCache, GitLabModelConfigRegistry, GitLabModelDiscovery, GitLabOAuthManager, type GitLabOAuthTokenResponse, type GitLabOAuthTokens, type GitLabOpenAIConfig, GitLabOpenAILanguageModel, type GitLabProject, GitLabProjectCache, GitLabProjectDetector, type GitLabProjectDetectorConfig, type GitLabProvider, type GitLabProviderSettings, type GitLabThinkingConfig, GitLabWorkflowClient, type GitLabWorkflowClientConfig, GitLabWorkflowLanguageModel, type GitLabWorkflowLanguageModelConfig, type GitLabWorkflowOptions, GitLabWorkflowTokenClient, MODEL_ID_TO_ANTHROPIC_MODEL, MODEL_MAPPINGS, type McpToolDefinition, type ModelCacheEntry, type ModelConfig, type ModelConfigRegistryOptions, type ModelDiscoveryConfig, type ModelMapping, type ModelProvider, type NewCheckpoint, OAUTH_SCOPES, OPENCODE_GITLAB_AUTH_CLIENT_ID, type OpenAIApiType, type OpenCodeAuth, type OpenCodeAuthApi, type OpenCodeAuthOAuth, type RunMcpTool, type StartRequest, TOKEN_EXPIRY_SKEW_MS, VERSION, WORKFLOW_ENVIRONMENT, WS_HEARTBEAT_INTERVAL_MS, WS_KEEPALIVE_PING_INTERVAL_MS, type WorkflowAction, type WorkflowClientEvent, type WorkflowDiscoveryConfig, type WorkflowDiscoveryOptions, type WorkflowDiscoveryResult, type WorkflowStatus, type WorkflowToolExecutor, WorkflowType, type WorkflowWebSocketOptions, createGitLab, discoverWorkflowModels, getAnthropicModelForModelId, getModelMapping, getOpenAIApiType, getOpenAIModelForModelId, getProviderForModelId, getValidModelsForProvider, getWorkflowModelRef, gitlab, gitlabOAuthTokenResponseSchema, isResponsesApiModel, isWorkflowModel, parseModelsYml };
package/dist/index.js CHANGED
@@ -1602,6 +1602,7 @@ var MODEL_MAPPINGS = {
1602
1602
  // Anthropic models
1603
1603
  "duo-chat-fable-5-1": { provider: "anthropic", model: "claude-fable-5-1" },
1604
1604
  "duo-chat-fable-5": { provider: "anthropic", model: "claude-fable-5" },
1605
+ "duo-chat-opus-5-5": { provider: "anthropic", model: "claude-opus-5-5" },
1605
1606
  "duo-chat-opus-5": { provider: "anthropic", model: "claude-opus-5" },
1606
1607
  "duo-chat-opus-4-8": { provider: "anthropic", model: "claude-opus-4-8" },
1607
1608
  "duo-chat-opus-4-7": { provider: "anthropic", model: "claude-opus-4-7" },
@@ -1662,14 +1663,15 @@ var MODEL_MAPPINGS = {
1662
1663
  "duo-workflow-default": { provider: "workflow", model: "default" },
1663
1664
  "duo-workflow-sonnet-4-5": {
1664
1665
  provider: "workflow",
1665
- model: "anthropic/claude-sonnet-4-5-20250929"
1666
+ model: "claude_sonnet_4_5_20250929"
1666
1667
  },
1667
1668
  "duo-workflow-sonnet-5": { provider: "workflow", model: "claude_sonnet_5" },
1669
+ "duo-workflow-opus-5-5": { provider: "workflow", model: "claude_opus_5_5" },
1668
1670
  "duo-workflow-opus-5": { provider: "workflow", model: "claude_opus_5" },
1669
1671
  "duo-workflow-sonnet-4-6": { provider: "workflow", model: "claude_sonnet_4_6" },
1670
1672
  "duo-workflow-opus-4-5": {
1671
1673
  provider: "workflow",
1672
- model: "anthropic/claude-opus-4-5-20251101"
1674
+ model: "claude_opus_4_5_20251101"
1673
1675
  },
1674
1676
  "duo-workflow-haiku-4-5": { provider: "workflow", model: "claude_haiku_4_5_20251001" },
1675
1677
  "duo-workflow-opus-4-6": { provider: "workflow", model: "claude_opus_4_6_20260205" }
@@ -2771,7 +2773,7 @@ var import_node_async_hooks = require("async_hooks");
2771
2773
  var import_isomorphic_ws = __toESM(require("isomorphic-ws"));
2772
2774
 
2773
2775
  // src/version.ts
2774
- var VERSION = true ? "6.14.0" : "0.0.0-dev";
2776
+ var VERSION = true ? "6.15.1" : "0.0.0-dev";
2775
2777
 
2776
2778
  // src/gitlab-workflow-client.ts
2777
2779
  var WS_CONNECT_TIMEOUT_MS = 3e4;
@@ -3491,8 +3493,14 @@ var GitLabProjectDetector = class {
3491
3493
  };
3492
3494
 
3493
3495
  // src/gitlab-model-discovery.ts
3496
+ function scopeCacheKey(scope) {
3497
+ return "projectId" in scope ? `p:${scope.projectId}` : `n:${scope.rootNamespaceId}`;
3498
+ }
3499
+ function normalizeScope(scope) {
3500
+ return typeof scope === "string" ? { rootNamespaceId: scope } : scope;
3501
+ }
3494
3502
  var AI_CHAT_AVAILABLE_MODELS_QUERY = `
3495
- query aiChatAvailableModels($rootNamespaceId: GroupID!) {
3503
+ query aiChatAvailableModels($projectId: ProjectID, $rootNamespaceId: GroupID) {
3496
3504
  metadata {
3497
3505
  featureFlags(names: ["ai_user_model_switching"]) {
3498
3506
  enabled
@@ -3501,7 +3509,7 @@ var AI_CHAT_AVAILABLE_MODELS_QUERY = `
3501
3509
  version
3502
3510
  }
3503
3511
 
3504
- aiChatAvailableModels(rootNamespaceId: $rootNamespaceId) {
3512
+ aiChatAvailableModels(projectId: $projectId, rootNamespaceId: $rootNamespaceId) {
3505
3513
  defaultModel {
3506
3514
  name
3507
3515
  ref
@@ -3527,18 +3535,42 @@ var GitLabModelDiscovery = class {
3527
3535
  this.fetchFn = config.fetch ?? fetch;
3528
3536
  }
3529
3537
  /**
3530
- * Discover available models for a given root namespace.
3538
+ * Discover available models for a given project or root namespace.
3531
3539
  *
3532
- * Results are cached per `rootNamespaceId` with a 10-minute TTL.
3540
+ * Results are cached per scope with a 10-minute TTL.
3533
3541
  * Use `invalidateCache()` to force an immediate refresh.
3534
3542
  *
3535
- * @param rootNamespaceId - GitLab group ID (e.g., 'gid://gitlab/Group/12345')
3543
+ * @param scope - `{ projectId }` (preferred), `{ rootNamespaceId }`, or a
3544
+ * bare string (treated as `rootNamespaceId` for backwards compatibility)
3536
3545
  */
3537
- async discover(rootNamespaceId) {
3538
- const cached = this.cache.get(rootNamespaceId);
3546
+ async discover(scope) {
3547
+ const normalized = normalizeScope(scope);
3548
+ const cacheKey = scopeCacheKey(normalized);
3549
+ const cached = this.cache.get(cacheKey);
3539
3550
  if (cached && cached.expiresAt > Date.now()) {
3540
3551
  return cached.data;
3541
3552
  }
3553
+ try {
3554
+ const result = await this.query(normalized);
3555
+ this.cache.set(cacheKey, {
3556
+ data: result,
3557
+ expiresAt: Date.now() + DISCOVERY_CACHE_TTL_MS
3558
+ });
3559
+ return result;
3560
+ } catch (error) {
3561
+ if ("projectId" in normalized && normalized.rootNamespaceId) {
3562
+ const fallbackScope = { rootNamespaceId: normalized.rootNamespaceId };
3563
+ const result = await this.query(fallbackScope);
3564
+ this.cache.set(scopeCacheKey(fallbackScope), {
3565
+ data: result,
3566
+ expiresAt: Date.now() + DISCOVERY_CACHE_TTL_MS
3567
+ });
3568
+ return result;
3569
+ }
3570
+ throw error;
3571
+ }
3572
+ }
3573
+ async query(scope) {
3542
3574
  const url = `${this.config.instanceUrl}/api/graphql`;
3543
3575
  try {
3544
3576
  const response = await this.fetchFn(url, {
@@ -3549,7 +3581,7 @@ var GitLabModelDiscovery = class {
3549
3581
  },
3550
3582
  body: JSON.stringify({
3551
3583
  query: AI_CHAT_AVAILABLE_MODELS_QUERY,
3552
- variables: { rootNamespaceId }
3584
+ variables: "projectId" in scope ? { projectId: scope.projectId, rootNamespaceId: void 0 } : { projectId: void 0, rootNamespaceId: scope.rootNamespaceId }
3553
3585
  })
3554
3586
  });
3555
3587
  if (!response.ok) {
@@ -3568,19 +3600,15 @@ var GitLabModelDiscovery = class {
3568
3600
  }
3569
3601
  const models = json.data?.aiChatAvailableModels;
3570
3602
  const metadata = json.data?.metadata;
3571
- const modelSwitchingEnabled = metadata?.featureFlags?.find((f) => f.name === "ai_user_model_switching")?.enabled ?? false;
3572
- const result = {
3603
+ const flag = metadata?.featureFlags?.find((f) => f.name === "ai_user_model_switching");
3604
+ const modelSwitchingEnabled = flag ? flag.enabled : true;
3605
+ return {
3573
3606
  defaultModel: models?.defaultModel ?? null,
3574
3607
  selectableModels: models?.selectableModels ?? [],
3575
3608
  pinnedModel: models?.pinnedModel ?? null,
3576
3609
  modelSwitchingEnabled,
3577
3610
  instanceVersion: metadata?.version ?? null
3578
3611
  };
3579
- this.cache.set(rootNamespaceId, {
3580
- data: result,
3581
- expiresAt: Date.now() + DISCOVERY_CACHE_TTL_MS
3582
- });
3583
- return result;
3584
3612
  } catch (error) {
3585
3613
  if (error instanceof GitLabError) throw error;
3586
3614
  throw new GitLabError({
@@ -3594,11 +3622,11 @@ var GitLabModelDiscovery = class {
3594
3622
  *
3595
3623
  * Priority: pinned > user-selected > default.
3596
3624
  *
3597
- * @param rootNamespaceId - GitLab group ID
3625
+ * @param scope - Project or root namespace to discover models for
3598
3626
  * @param userSelectedRef - Optional user preference
3599
3627
  */
3600
- async getEffectiveModelRef(rootNamespaceId, userSelectedRef) {
3601
- const discovered = await this.discover(rootNamespaceId);
3628
+ async getEffectiveModelRef(scope, userSelectedRef) {
3629
+ const discovered = await this.discover(scope);
3602
3630
  if (discovered.pinnedModel) {
3603
3631
  return discovered.pinnedModel.ref;
3604
3632
  }
@@ -4045,6 +4073,25 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
4045
4073
  this._rootNamespaceId = null;
4046
4074
  return null;
4047
4075
  }
4076
+ /**
4077
+ * Resolve the model discovery scope to use for `aiChatAvailableModels`.
4078
+ *
4079
+ * `projectId` is preferred — it works for both group- and user-namespace
4080
+ * projects — with `rootNamespaceId` attached as a fallback for GitLab
4081
+ * instances too old to support `projectId`-scoped discovery.
4082
+ */
4083
+ async resolveDiscoveryScope() {
4084
+ const rootNamespaceId = await this.resolveRootNamespaceId();
4085
+ const workDir = this.workflowOptions.workingDirectory ?? process.cwd();
4086
+ const project = await this.projectDetector.detectProject(workDir);
4087
+ if (project?.id) {
4088
+ return {
4089
+ projectId: `gid://gitlab/Project/${project.id}`,
4090
+ ...rootNamespaceId ? { rootNamespaceId } : {}
4091
+ };
4092
+ }
4093
+ return rootNamespaceId ? { rootNamespaceId } : null;
4094
+ }
4048
4095
  /**
4049
4096
  * Resolve the effective DWS model ref to use for this stream.
4050
4097
  * Deduplicates concurrent calls via a shared promise.
@@ -4075,13 +4122,13 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
4075
4122
  return this._discoveryPromise;
4076
4123
  }
4077
4124
  async doResolveModelRef() {
4078
- const rootNamespaceId = await this.resolveRootNamespaceId();
4079
- if (!rootNamespaceId) {
4125
+ const scope = await this.resolveDiscoveryScope();
4126
+ if (!scope) {
4080
4127
  this._selectedModelRef = "default";
4081
4128
  return "default";
4082
4129
  }
4083
4130
  try {
4084
- const discovered = await this.modelDiscovery.discover(rootNamespaceId);
4131
+ const discovered = await this.modelDiscovery.discover(scope);
4085
4132
  this.modelCache.saveDiscovery(discovered);
4086
4133
  if (discovered.pinnedModel) {
4087
4134
  this._selectedModelRef = discovered.pinnedModel.ref;
@@ -4124,11 +4171,13 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
4124
4171
  * Call this early (e.g., on IDE startup) to avoid blocking the first stream.
4125
4172
  * Results are persisted to the workspace model cache.
4126
4173
  *
4127
- * @param rootNamespaceId - GitLab group ID (e.g., 'gid://gitlab/Group/12345')
4174
+ * @param scope - Project or root namespace GID to discover models for.
4175
+ * A bare string is treated as a `rootNamespaceId` (e.g.
4176
+ * 'gid://gitlab/Group/12345') for backwards compatibility.
4128
4177
  * @returns Discovered models with default, selectable, and pinned models
4129
4178
  */
4130
- async discoverModels(rootNamespaceId) {
4131
- const result = await this.modelDiscovery.discover(rootNamespaceId);
4179
+ async discoverModels(scope) {
4180
+ const result = await this.modelDiscovery.discover(scope);
4132
4181
  this.modelCache.saveDiscovery(result);
4133
4182
  return result;
4134
4183
  }
@@ -5870,7 +5919,7 @@ async function buildModels(discovered, project) {
5870
5919
  for (const model of all) {
5871
5920
  if (!model.ref || seen.has(model.ref)) continue;
5872
5921
  seen.add(model.ref);
5873
- const id = refToID.get(model.ref) ?? `duo-workflow-${model.ref.replace(/[/_]/g, "-")}`;
5922
+ const id = refToID.get(model.ref) ?? (model.ref.startsWith("__default__") ? "duo-workflow-default" : `duo-workflow-${model.ref.replace(/[/_]/g, "-")}`);
5874
5923
  const limits = configs.get(model.ref);
5875
5924
  models.push({
5876
5925
  id,
@@ -5902,14 +5951,22 @@ async function discoverWorkflowModels(config, options) {
5902
5951
  } catch {
5903
5952
  return { models: [], project: null };
5904
5953
  }
5905
- const namespaceId = project?.namespaceId;
5906
- if (!namespaceId) return { models: [], project };
5954
+ if (!project?.id) return { models: [], project };
5955
+ const scope = {
5956
+ projectId: `gid://gitlab/Project/${project.id}`,
5957
+ ...project.namespaceId ? { rootNamespaceId: `gid://gitlab/Group/${project.namespaceId}` } : {}
5958
+ };
5907
5959
  const discovery = new GitLabModelDiscovery({
5908
5960
  instanceUrl: config.instanceUrl,
5909
5961
  getHeaders: config.getHeaders,
5910
5962
  fetch: config.fetch
5911
5963
  });
5912
- const discovered = await discovery.discover(`gid://gitlab/Group/${namespaceId}`);
5964
+ let discovered;
5965
+ try {
5966
+ discovered = await discovery.discover(scope);
5967
+ } catch {
5968
+ return { models: [], project };
5969
+ }
5913
5970
  cache.saveDiscovery(discovered, project);
5914
5971
  if (!cache.getSelectedModelRef()) {
5915
5972
  const pick = discovered.pinnedModel ?? discovered.defaultModel;