gitlab-ai-provider 6.14.0 → 6.15.1

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
+ ## <small>6.15.1 (2026-09-09)</small>
6
+
7
+ - Merge branch 'fix/agent-platform-model-discovery' into 'main' ([98c1fa7](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/98c1fa7))
8
+ - fix(discovery): scope Agent Platform model discovery by project and correct stale workflow refs ([9dd9681](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/9dd9681))
9
+
10
+ ## 6.15.0 (2026-09-07)
11
+
12
+ - Merge branch 'feature-add-gpt-6-astra' into 'main' ([19d1e4f](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/19d1e4f))
13
+ - feat(models): add GPT-6 Astra model mappings ([a636486](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/a636486))
14
+
5
15
  ## 6.14.0 (2026-09-04)
6
16
 
7
17
  - Merge branch 'reasoning-controls' into 'main' ([7e13eb7](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/7e13eb7))
package/README.md CHANGED
@@ -132,6 +132,7 @@ const customModel = gitlab.agenticChat('duo-chat-opus-4-5', {
132
132
  | `duo-chat-opus-4-5` | Anthropic | `claude-opus-4-5-20251101` |
133
133
  | `duo-chat-sonnet-4-5` | Anthropic | `claude-sonnet-4-5-20250929` |
134
134
  | `duo-chat-haiku-4-5` | Anthropic | `claude-haiku-4-5-20251001` |
135
+ | `duo-chat-gpt-6-astra` | OpenAI | `gpt-6-astra` |
135
136
  | `duo-chat-gpt-5-6-sol` | OpenAI | `gpt-5.6-sol` |
136
137
  | `duo-chat-gpt-5-6-terra` | OpenAI | `gpt-5.6-terra` |
137
138
  | `duo-chat-gpt-5-6-luna` | OpenAI | `gpt-5.6-luna` |
@@ -628,7 +629,14 @@ const directToken = await client.getDirectAccessToken();
628
629
 
629
630
  #### GitLabModelDiscovery
630
631
 
631
- Discovers available workflow models via GraphQL.
632
+ Discovers available workflow models via GraphQL. Scope by `projectId`
633
+ (preferred — works for both group- and user-namespace projects) or
634
+ `rootNamespaceId` (group namespaces only, supported on older GitLab
635
+ instances). A bare string is treated as `rootNamespaceId` for backwards
636
+ compatibility. When scoped by `projectId` with a `rootNamespaceId` also
637
+ supplied, discovery falls back to the namespace-scoped query if the
638
+ `projectId` query fails (e.g. on GitLab versions predating `projectId`
639
+ support).
632
640
 
633
641
  ```typescript
634
642
  const discovery = new GitLabModelDiscovery({
@@ -636,11 +644,14 @@ const discovery = new GitLabModelDiscovery({
636
644
  getHeaders: () => ({ Authorization: `Bearer ${token}` }),
637
645
  });
638
646
 
639
- const models = await discovery.discover('gid://gitlab/Group/12345');
647
+ const models = await discovery.discover({
648
+ projectId: 'gid://gitlab/Project/12345',
649
+ rootNamespaceId: 'gid://gitlab/Group/6789', // optional fallback
650
+ });
640
651
  // Returns: { defaultModel, selectableModels, pinnedModel, modelSwitchingEnabled }
641
652
 
642
653
  const effectiveRef = await discovery.getEffectiveModelRef(
643
- 'gid://gitlab/Group/12345',
654
+ { projectId: 'gid://gitlab/Project/12345' },
644
655
  'claude_sonnet_4_6'
645
656
  );
646
657
  ```
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
@@ -1611,6 +1611,10 @@ var MODEL_MAPPINGS = {
1611
1611
  "duo-chat-opus-4-5": { provider: "anthropic", model: "claude-opus-4-5-20251101" },
1612
1612
  "duo-chat-sonnet-4-5": { provider: "anthropic", model: "claude-sonnet-4-5-20250929" },
1613
1613
  "duo-chat-haiku-4-5": { provider: "anthropic", model: "claude-haiku-4-5-20251001" },
1614
+ // OpenAI models - Responses API
1615
+ // GPT-6 Astra rejects `reasoning_effort` with function tools on Chat Completions;
1616
+ // it must use the Responses API (matches OpenAI's own routing for GPT-5.x/GPT-6).
1617
+ "duo-chat-gpt-6-astra": { provider: "openai", model: "gpt-6-astra", openaiApiType: "responses" },
1614
1618
  // OpenAI models - Chat Completions API
1615
1619
  "duo-chat-gpt-5-1": { provider: "openai", model: "gpt-5.1-2025-11-13", openaiApiType: "chat" },
1616
1620
  "duo-chat-gpt-5-2": { provider: "openai", model: "gpt-5.2-2025-12-11", openaiApiType: "chat" },
@@ -1658,14 +1662,14 @@ var MODEL_MAPPINGS = {
1658
1662
  "duo-workflow-default": { provider: "workflow", model: "default" },
1659
1663
  "duo-workflow-sonnet-4-5": {
1660
1664
  provider: "workflow",
1661
- model: "anthropic/claude-sonnet-4-5-20250929"
1665
+ model: "claude_sonnet_4_5_20250929"
1662
1666
  },
1663
1667
  "duo-workflow-sonnet-5": { provider: "workflow", model: "claude_sonnet_5" },
1664
1668
  "duo-workflow-opus-5": { provider: "workflow", model: "claude_opus_5" },
1665
1669
  "duo-workflow-sonnet-4-6": { provider: "workflow", model: "claude_sonnet_4_6" },
1666
1670
  "duo-workflow-opus-4-5": {
1667
1671
  provider: "workflow",
1668
- model: "anthropic/claude-opus-4-5-20251101"
1672
+ model: "claude_opus_4_5_20251101"
1669
1673
  },
1670
1674
  "duo-workflow-haiku-4-5": { provider: "workflow", model: "claude_haiku_4_5_20251001" },
1671
1675
  "duo-workflow-opus-4-6": { provider: "workflow", model: "claude_opus_4_6_20260205" }
@@ -2767,7 +2771,7 @@ var import_node_async_hooks = require("async_hooks");
2767
2771
  var import_isomorphic_ws = __toESM(require("isomorphic-ws"));
2768
2772
 
2769
2773
  // src/version.ts
2770
- var VERSION = true ? "6.13.0" : "0.0.0-dev";
2774
+ var VERSION = true ? "6.15.0" : "0.0.0-dev";
2771
2775
 
2772
2776
  // src/gitlab-workflow-client.ts
2773
2777
  var WS_CONNECT_TIMEOUT_MS = 3e4;
@@ -3487,8 +3491,14 @@ var GitLabProjectDetector = class {
3487
3491
  };
3488
3492
 
3489
3493
  // src/gitlab-model-discovery.ts
3494
+ function scopeCacheKey(scope) {
3495
+ return "projectId" in scope ? `p:${scope.projectId}` : `n:${scope.rootNamespaceId}`;
3496
+ }
3497
+ function normalizeScope(scope) {
3498
+ return typeof scope === "string" ? { rootNamespaceId: scope } : scope;
3499
+ }
3490
3500
  var AI_CHAT_AVAILABLE_MODELS_QUERY = `
3491
- query aiChatAvailableModels($rootNamespaceId: GroupID!) {
3501
+ query aiChatAvailableModels($projectId: ProjectID, $rootNamespaceId: GroupID) {
3492
3502
  metadata {
3493
3503
  featureFlags(names: ["ai_user_model_switching"]) {
3494
3504
  enabled
@@ -3497,7 +3507,7 @@ var AI_CHAT_AVAILABLE_MODELS_QUERY = `
3497
3507
  version
3498
3508
  }
3499
3509
 
3500
- aiChatAvailableModels(rootNamespaceId: $rootNamespaceId) {
3510
+ aiChatAvailableModels(projectId: $projectId, rootNamespaceId: $rootNamespaceId) {
3501
3511
  defaultModel {
3502
3512
  name
3503
3513
  ref
@@ -3523,18 +3533,42 @@ var GitLabModelDiscovery = class {
3523
3533
  this.fetchFn = config.fetch ?? fetch;
3524
3534
  }
3525
3535
  /**
3526
- * Discover available models for a given root namespace.
3536
+ * Discover available models for a given project or root namespace.
3527
3537
  *
3528
- * Results are cached per `rootNamespaceId` with a 10-minute TTL.
3538
+ * Results are cached per scope with a 10-minute TTL.
3529
3539
  * Use `invalidateCache()` to force an immediate refresh.
3530
3540
  *
3531
- * @param rootNamespaceId - GitLab group ID (e.g., 'gid://gitlab/Group/12345')
3541
+ * @param scope - `{ projectId }` (preferred), `{ rootNamespaceId }`, or a
3542
+ * bare string (treated as `rootNamespaceId` for backwards compatibility)
3532
3543
  */
3533
- async discover(rootNamespaceId) {
3534
- const cached = this.cache.get(rootNamespaceId);
3544
+ async discover(scope) {
3545
+ const normalized = normalizeScope(scope);
3546
+ const cacheKey = scopeCacheKey(normalized);
3547
+ const cached = this.cache.get(cacheKey);
3535
3548
  if (cached && cached.expiresAt > Date.now()) {
3536
3549
  return cached.data;
3537
3550
  }
3551
+ try {
3552
+ const result = await this.query(normalized);
3553
+ this.cache.set(cacheKey, {
3554
+ data: result,
3555
+ expiresAt: Date.now() + DISCOVERY_CACHE_TTL_MS
3556
+ });
3557
+ return result;
3558
+ } catch (error) {
3559
+ if ("projectId" in normalized && normalized.rootNamespaceId) {
3560
+ const fallbackScope = { rootNamespaceId: normalized.rootNamespaceId };
3561
+ const result = await this.query(fallbackScope);
3562
+ this.cache.set(scopeCacheKey(fallbackScope), {
3563
+ data: result,
3564
+ expiresAt: Date.now() + DISCOVERY_CACHE_TTL_MS
3565
+ });
3566
+ return result;
3567
+ }
3568
+ throw error;
3569
+ }
3570
+ }
3571
+ async query(scope) {
3538
3572
  const url = `${this.config.instanceUrl}/api/graphql`;
3539
3573
  try {
3540
3574
  const response = await this.fetchFn(url, {
@@ -3545,7 +3579,7 @@ var GitLabModelDiscovery = class {
3545
3579
  },
3546
3580
  body: JSON.stringify({
3547
3581
  query: AI_CHAT_AVAILABLE_MODELS_QUERY,
3548
- variables: { rootNamespaceId }
3582
+ variables: "projectId" in scope ? { projectId: scope.projectId, rootNamespaceId: void 0 } : { projectId: void 0, rootNamespaceId: scope.rootNamespaceId }
3549
3583
  })
3550
3584
  });
3551
3585
  if (!response.ok) {
@@ -3564,19 +3598,15 @@ var GitLabModelDiscovery = class {
3564
3598
  }
3565
3599
  const models = json.data?.aiChatAvailableModels;
3566
3600
  const metadata = json.data?.metadata;
3567
- const modelSwitchingEnabled = metadata?.featureFlags?.find((f) => f.name === "ai_user_model_switching")?.enabled ?? false;
3568
- const result = {
3601
+ const flag = metadata?.featureFlags?.find((f) => f.name === "ai_user_model_switching");
3602
+ const modelSwitchingEnabled = flag ? flag.enabled : true;
3603
+ return {
3569
3604
  defaultModel: models?.defaultModel ?? null,
3570
3605
  selectableModels: models?.selectableModels ?? [],
3571
3606
  pinnedModel: models?.pinnedModel ?? null,
3572
3607
  modelSwitchingEnabled,
3573
3608
  instanceVersion: metadata?.version ?? null
3574
3609
  };
3575
- this.cache.set(rootNamespaceId, {
3576
- data: result,
3577
- expiresAt: Date.now() + DISCOVERY_CACHE_TTL_MS
3578
- });
3579
- return result;
3580
3610
  } catch (error) {
3581
3611
  if (error instanceof GitLabError) throw error;
3582
3612
  throw new GitLabError({
@@ -3590,11 +3620,11 @@ var GitLabModelDiscovery = class {
3590
3620
  *
3591
3621
  * Priority: pinned > user-selected > default.
3592
3622
  *
3593
- * @param rootNamespaceId - GitLab group ID
3623
+ * @param scope - Project or root namespace to discover models for
3594
3624
  * @param userSelectedRef - Optional user preference
3595
3625
  */
3596
- async getEffectiveModelRef(rootNamespaceId, userSelectedRef) {
3597
- const discovered = await this.discover(rootNamespaceId);
3626
+ async getEffectiveModelRef(scope, userSelectedRef) {
3627
+ const discovered = await this.discover(scope);
3598
3628
  if (discovered.pinnedModel) {
3599
3629
  return discovered.pinnedModel.ref;
3600
3630
  }
@@ -4041,6 +4071,25 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
4041
4071
  this._rootNamespaceId = null;
4042
4072
  return null;
4043
4073
  }
4074
+ /**
4075
+ * Resolve the model discovery scope to use for `aiChatAvailableModels`.
4076
+ *
4077
+ * `projectId` is preferred — it works for both group- and user-namespace
4078
+ * projects — with `rootNamespaceId` attached as a fallback for GitLab
4079
+ * instances too old to support `projectId`-scoped discovery.
4080
+ */
4081
+ async resolveDiscoveryScope() {
4082
+ const rootNamespaceId = await this.resolveRootNamespaceId();
4083
+ const workDir = this.workflowOptions.workingDirectory ?? process.cwd();
4084
+ const project = await this.projectDetector.detectProject(workDir);
4085
+ if (project?.id) {
4086
+ return {
4087
+ projectId: `gid://gitlab/Project/${project.id}`,
4088
+ ...rootNamespaceId ? { rootNamespaceId } : {}
4089
+ };
4090
+ }
4091
+ return rootNamespaceId ? { rootNamespaceId } : null;
4092
+ }
4044
4093
  /**
4045
4094
  * Resolve the effective DWS model ref to use for this stream.
4046
4095
  * Deduplicates concurrent calls via a shared promise.
@@ -4071,13 +4120,13 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
4071
4120
  return this._discoveryPromise;
4072
4121
  }
4073
4122
  async doResolveModelRef() {
4074
- const rootNamespaceId = await this.resolveRootNamespaceId();
4075
- if (!rootNamespaceId) {
4123
+ const scope = await this.resolveDiscoveryScope();
4124
+ if (!scope) {
4076
4125
  this._selectedModelRef = "default";
4077
4126
  return "default";
4078
4127
  }
4079
4128
  try {
4080
- const discovered = await this.modelDiscovery.discover(rootNamespaceId);
4129
+ const discovered = await this.modelDiscovery.discover(scope);
4081
4130
  this.modelCache.saveDiscovery(discovered);
4082
4131
  if (discovered.pinnedModel) {
4083
4132
  this._selectedModelRef = discovered.pinnedModel.ref;
@@ -4120,11 +4169,13 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
4120
4169
  * Call this early (e.g., on IDE startup) to avoid blocking the first stream.
4121
4170
  * Results are persisted to the workspace model cache.
4122
4171
  *
4123
- * @param rootNamespaceId - GitLab group ID (e.g., 'gid://gitlab/Group/12345')
4172
+ * @param scope - Project or root namespace GID to discover models for.
4173
+ * A bare string is treated as a `rootNamespaceId` (e.g.
4174
+ * 'gid://gitlab/Group/12345') for backwards compatibility.
4124
4175
  * @returns Discovered models with default, selectable, and pinned models
4125
4176
  */
4126
- async discoverModels(rootNamespaceId) {
4127
- const result = await this.modelDiscovery.discover(rootNamespaceId);
4177
+ async discoverModels(scope) {
4178
+ const result = await this.modelDiscovery.discover(scope);
4128
4179
  this.modelCache.saveDiscovery(result);
4129
4180
  return result;
4130
4181
  }
@@ -5866,7 +5917,7 @@ async function buildModels(discovered, project) {
5866
5917
  for (const model of all) {
5867
5918
  if (!model.ref || seen.has(model.ref)) continue;
5868
5919
  seen.add(model.ref);
5869
- const id = refToID.get(model.ref) ?? `duo-workflow-${model.ref.replace(/[/_]/g, "-")}`;
5920
+ const id = refToID.get(model.ref) ?? (model.ref.startsWith("__default__") ? "duo-workflow-default" : `duo-workflow-${model.ref.replace(/[/_]/g, "-")}`);
5870
5921
  const limits = configs.get(model.ref);
5871
5922
  models.push({
5872
5923
  id,
@@ -5898,14 +5949,22 @@ async function discoverWorkflowModels(config, options) {
5898
5949
  } catch {
5899
5950
  return { models: [], project: null };
5900
5951
  }
5901
- const namespaceId = project?.namespaceId;
5902
- if (!namespaceId) return { models: [], project };
5952
+ if (!project?.id) return { models: [], project };
5953
+ const scope = {
5954
+ projectId: `gid://gitlab/Project/${project.id}`,
5955
+ ...project.namespaceId ? { rootNamespaceId: `gid://gitlab/Group/${project.namespaceId}` } : {}
5956
+ };
5903
5957
  const discovery = new GitLabModelDiscovery({
5904
5958
  instanceUrl: config.instanceUrl,
5905
5959
  getHeaders: config.getHeaders,
5906
5960
  fetch: config.fetch
5907
5961
  });
5908
- const discovered = await discovery.discover(`gid://gitlab/Group/${namespaceId}`);
5962
+ let discovered;
5963
+ try {
5964
+ discovered = await discovery.discover(scope);
5965
+ } catch {
5966
+ return { models: [], project };
5967
+ }
5909
5968
  cache.saveDiscovery(discovered, project);
5910
5969
  if (!cache.getSelectedModelRef()) {
5911
5970
  const pick = discovered.pinnedModel ?? discovered.defaultModel;