gitlab-ai-provider 5.1.2 → 5.2.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,15 @@
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>5.2.1 (2026-03-20)</small>
6
+
7
+ - fix: cache workflow model discovery to file and resolve model name on selection ([8ba7056](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/8ba7056))
8
+
9
+ ## 5.2.0 (2026-03-18)
10
+
11
+ - fix: update dists ([57692b5](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/57692b5))
12
+ - feat: add discoverWorkflowModels() for high-level workflow model discovery ([b61ed5a](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/b61ed5a))
13
+
5
14
  ## <small>5.1.2 (2026-03-17)</small>
6
15
 
7
16
  - fix: regenerate dists ([091d3ae](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/091d3ae))
package/dist/index.d.mts CHANGED
@@ -174,6 +174,67 @@ declare class GitLabModelDiscovery {
174
174
  invalidateCache(): void;
175
175
  }
176
176
 
177
+ /**
178
+ * Simple in-memory cache for GitLab project information
179
+ * Used to avoid repeated API calls when detecting projects from git remotes
180
+ */
181
+ interface GitLabProject {
182
+ id: number;
183
+ path: string;
184
+ pathWithNamespace: string;
185
+ name: string;
186
+ namespaceId?: number;
187
+ }
188
+ /**
189
+ * In-memory cache for GitLab project information with TTL support
190
+ */
191
+ declare class GitLabProjectCache {
192
+ private cache;
193
+ private defaultTTL;
194
+ /**
195
+ * Create a new project cache
196
+ * @param defaultTTL - Default time-to-live in milliseconds (default: 5 minutes)
197
+ */
198
+ constructor(defaultTTL?: number);
199
+ /**
200
+ * Get a cached project by key
201
+ * @param key - Cache key (typically the working directory path)
202
+ * @returns The cached project or null if not found or expired
203
+ */
204
+ get(key: string): GitLabProject | null;
205
+ /**
206
+ * Store a project in the cache
207
+ * @param key - Cache key (typically the working directory path)
208
+ * @param project - The project to cache
209
+ * @param ttl - Optional custom TTL in milliseconds
210
+ */
211
+ set(key: string, project: GitLabProject, ttl?: number): void;
212
+ /**
213
+ * Check if a key exists in the cache (and is not expired)
214
+ * @param key - Cache key to check
215
+ * @returns true if the key exists and is not expired
216
+ */
217
+ has(key: string): boolean;
218
+ /**
219
+ * Remove a specific entry from the cache
220
+ * @param key - Cache key to remove
221
+ */
222
+ delete(key: string): void;
223
+ /**
224
+ * Clear all entries from the cache
225
+ */
226
+ clear(): void;
227
+ /**
228
+ * Get the number of entries in the cache (including expired ones)
229
+ */
230
+ get size(): number;
231
+ /**
232
+ * Clean up expired entries from the cache
233
+ * This is useful for long-running processes to prevent memory leaks
234
+ */
235
+ cleanup(): void;
236
+ }
237
+
177
238
  /**
178
239
  * File-based cache for workflow model discovery results and user selection.
179
240
  *
@@ -186,6 +247,7 @@ declare class GitLabModelDiscovery {
186
247
  * that switching instances for the same workspace invalidates the cache.
187
248
  * Each value holds:
188
249
  * - `discovery`: The full DiscoveredModels JSON from the last successful discovery
250
+ * - `project`: The GitLab project detected for this workspace
189
251
  * - `selectedModelRef`: The model ref the user last selected
190
252
  * - `selectedModelName`: Human-readable name of the selected model
191
253
  * - `updatedAt`: ISO timestamp of the last write
@@ -193,6 +255,7 @@ declare class GitLabModelDiscovery {
193
255
 
194
256
  interface ModelCacheEntry {
195
257
  discovery: DiscoveredModels | null;
258
+ project: GitLabProject | null;
196
259
  selectedModelRef: string | null;
197
260
  selectedModelName: string | null;
198
261
  updatedAt: string;
@@ -212,12 +275,19 @@ declare class GitLabModelCache {
212
275
  * Persist the full cache entry to disk.
213
276
  */
214
277
  save(entry: ModelCacheEntry): void;
278
+ /**
279
+ * Returns true if the discovery data is missing or older than DISCOVERY_TTL_MS.
280
+ */
281
+ isDiscoveryExpired(): boolean;
215
282
  /**
216
283
  * Update only the discovery portion of the cache, preserving selection.
284
+ * Optionally persists the associated GitLab project.
217
285
  */
218
- saveDiscovery(discovery: DiscoveredModels): void;
286
+ saveDiscovery(discovery: DiscoveredModels, project?: GitLabProject | null): void;
219
287
  /**
220
288
  * Update only the selected model, preserving the discovery data.
289
+ * If name is null but ref is provided, attempts to resolve the name
290
+ * from the cached discovery data.
221
291
  */
222
292
  saveSelection(ref: string | null, name: string | null): void;
223
293
  /**
@@ -1170,67 +1240,6 @@ declare class GitLabOAuthManager {
1170
1240
  private createExpiresTimestamp;
1171
1241
  }
1172
1242
 
1173
- /**
1174
- * Simple in-memory cache for GitLab project information
1175
- * Used to avoid repeated API calls when detecting projects from git remotes
1176
- */
1177
- interface GitLabProject {
1178
- id: number;
1179
- path: string;
1180
- pathWithNamespace: string;
1181
- name: string;
1182
- namespaceId?: number;
1183
- }
1184
- /**
1185
- * In-memory cache for GitLab project information with TTL support
1186
- */
1187
- declare class GitLabProjectCache {
1188
- private cache;
1189
- private defaultTTL;
1190
- /**
1191
- * Create a new project cache
1192
- * @param defaultTTL - Default time-to-live in milliseconds (default: 5 minutes)
1193
- */
1194
- constructor(defaultTTL?: number);
1195
- /**
1196
- * Get a cached project by key
1197
- * @param key - Cache key (typically the working directory path)
1198
- * @returns The cached project or null if not found or expired
1199
- */
1200
- get(key: string): GitLabProject | null;
1201
- /**
1202
- * Store a project in the cache
1203
- * @param key - Cache key (typically the working directory path)
1204
- * @param project - The project to cache
1205
- * @param ttl - Optional custom TTL in milliseconds
1206
- */
1207
- set(key: string, project: GitLabProject, ttl?: number): void;
1208
- /**
1209
- * Check if a key exists in the cache (and is not expired)
1210
- * @param key - Cache key to check
1211
- * @returns true if the key exists and is not expired
1212
- */
1213
- has(key: string): boolean;
1214
- /**
1215
- * Remove a specific entry from the cache
1216
- * @param key - Cache key to remove
1217
- */
1218
- delete(key: string): void;
1219
- /**
1220
- * Clear all entries from the cache
1221
- */
1222
- clear(): void;
1223
- /**
1224
- * Get the number of entries in the cache (including expired ones)
1225
- */
1226
- get size(): number;
1227
- /**
1228
- * Clean up expired entries from the cache
1229
- * This is useful for long-running processes to prevent memory leaks
1230
- */
1231
- cleanup(): void;
1232
- }
1233
-
1234
1243
  interface GitLabProjectDetectorConfig {
1235
1244
  instanceUrl: string;
1236
1245
  getHeaders: () => Record<string, string>;
@@ -1573,4 +1582,41 @@ declare class GitLabModelConfigRegistry {
1573
1582
  */
1574
1583
  declare function parseModelsYml(text: string): Map<string, ModelConfig>;
1575
1584
 
1576
- export { AGENT_PRIVILEGES, type ActionResponsePayload, type AdditionalContext, type AiChatAvailableModels, type AiModel, BUNDLED_CLIENT_ID, CLIENT_VERSION, type ClientEvent, DEFAULT_AGENT_PRIVILEGES, DEFAULT_AI_GATEWAY_URL, DEFAULT_CLIENT_CAPABILITIES, DEFAULT_WORKFLOW_DEFINITION, type DirectAccessToken, type DiscoveredModels, 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, 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 WorkflowStatus, type WorkflowToolExecutor, WorkflowType, type WorkflowWebSocketOptions, createGitLab, getAnthropicModelForModelId, getModelMapping, getOpenAIApiType, getOpenAIModelForModelId, getProviderForModelId, getValidModelsForProvider, getWorkflowModelRef, gitlab, isResponsesApiModel, isWorkflowModel, parseModelsYml };
1585
+ /**
1586
+ * High-level workflow model discovery.
1587
+ *
1588
+ * Combines project detection, GraphQL model discovery, model config lookup,
1589
+ * and MODEL_MAPPINGS to produce a list of available workflow models with
1590
+ * their token limits. Consumers map these into their own internal model
1591
+ * representations.
1592
+ */
1593
+
1594
+ interface DiscoveredWorkflowModel {
1595
+ /** Model ID suitable for use with `sdk.workflowChat()` (e.g. "duo-workflow-sonnet-4-6") */
1596
+ id: string;
1597
+ /** Upstream model ref (e.g. "claude_sonnet_4_6") */
1598
+ ref: string;
1599
+ /** Human-readable name (e.g. "Claude Sonnet 4.6") */
1600
+ name: string;
1601
+ /** Max input context tokens */
1602
+ context: number;
1603
+ /** Max output tokens */
1604
+ output: number;
1605
+ /** Whether this model was admin-pinned */
1606
+ pinned: boolean;
1607
+ }
1608
+ interface WorkflowDiscoveryConfig {
1609
+ instanceUrl: string;
1610
+ getHeaders: () => Record<string, string>;
1611
+ fetch?: typeof fetch;
1612
+ }
1613
+ interface WorkflowDiscoveryOptions {
1614
+ workingDirectory: string;
1615
+ }
1616
+ interface WorkflowDiscoveryResult {
1617
+ models: DiscoveredWorkflowModel[];
1618
+ project: GitLabProject | null;
1619
+ }
1620
+ declare function discoverWorkflowModels(config: WorkflowDiscoveryConfig, options: WorkflowDiscoveryOptions): Promise<WorkflowDiscoveryResult>;
1621
+
1622
+ export { AGENT_PRIVILEGES, type ActionResponsePayload, type AdditionalContext, type AiChatAvailableModels, type AiModel, 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, 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, isResponsesApiModel, isWorkflowModel, parseModelsYml };
package/dist/index.d.ts CHANGED
@@ -174,6 +174,67 @@ declare class GitLabModelDiscovery {
174
174
  invalidateCache(): void;
175
175
  }
176
176
 
177
+ /**
178
+ * Simple in-memory cache for GitLab project information
179
+ * Used to avoid repeated API calls when detecting projects from git remotes
180
+ */
181
+ interface GitLabProject {
182
+ id: number;
183
+ path: string;
184
+ pathWithNamespace: string;
185
+ name: string;
186
+ namespaceId?: number;
187
+ }
188
+ /**
189
+ * In-memory cache for GitLab project information with TTL support
190
+ */
191
+ declare class GitLabProjectCache {
192
+ private cache;
193
+ private defaultTTL;
194
+ /**
195
+ * Create a new project cache
196
+ * @param defaultTTL - Default time-to-live in milliseconds (default: 5 minutes)
197
+ */
198
+ constructor(defaultTTL?: number);
199
+ /**
200
+ * Get a cached project by key
201
+ * @param key - Cache key (typically the working directory path)
202
+ * @returns The cached project or null if not found or expired
203
+ */
204
+ get(key: string): GitLabProject | null;
205
+ /**
206
+ * Store a project in the cache
207
+ * @param key - Cache key (typically the working directory path)
208
+ * @param project - The project to cache
209
+ * @param ttl - Optional custom TTL in milliseconds
210
+ */
211
+ set(key: string, project: GitLabProject, ttl?: number): void;
212
+ /**
213
+ * Check if a key exists in the cache (and is not expired)
214
+ * @param key - Cache key to check
215
+ * @returns true if the key exists and is not expired
216
+ */
217
+ has(key: string): boolean;
218
+ /**
219
+ * Remove a specific entry from the cache
220
+ * @param key - Cache key to remove
221
+ */
222
+ delete(key: string): void;
223
+ /**
224
+ * Clear all entries from the cache
225
+ */
226
+ clear(): void;
227
+ /**
228
+ * Get the number of entries in the cache (including expired ones)
229
+ */
230
+ get size(): number;
231
+ /**
232
+ * Clean up expired entries from the cache
233
+ * This is useful for long-running processes to prevent memory leaks
234
+ */
235
+ cleanup(): void;
236
+ }
237
+
177
238
  /**
178
239
  * File-based cache for workflow model discovery results and user selection.
179
240
  *
@@ -186,6 +247,7 @@ declare class GitLabModelDiscovery {
186
247
  * that switching instances for the same workspace invalidates the cache.
187
248
  * Each value holds:
188
249
  * - `discovery`: The full DiscoveredModels JSON from the last successful discovery
250
+ * - `project`: The GitLab project detected for this workspace
189
251
  * - `selectedModelRef`: The model ref the user last selected
190
252
  * - `selectedModelName`: Human-readable name of the selected model
191
253
  * - `updatedAt`: ISO timestamp of the last write
@@ -193,6 +255,7 @@ declare class GitLabModelDiscovery {
193
255
 
194
256
  interface ModelCacheEntry {
195
257
  discovery: DiscoveredModels | null;
258
+ project: GitLabProject | null;
196
259
  selectedModelRef: string | null;
197
260
  selectedModelName: string | null;
198
261
  updatedAt: string;
@@ -212,12 +275,19 @@ declare class GitLabModelCache {
212
275
  * Persist the full cache entry to disk.
213
276
  */
214
277
  save(entry: ModelCacheEntry): void;
278
+ /**
279
+ * Returns true if the discovery data is missing or older than DISCOVERY_TTL_MS.
280
+ */
281
+ isDiscoveryExpired(): boolean;
215
282
  /**
216
283
  * Update only the discovery portion of the cache, preserving selection.
284
+ * Optionally persists the associated GitLab project.
217
285
  */
218
- saveDiscovery(discovery: DiscoveredModels): void;
286
+ saveDiscovery(discovery: DiscoveredModels, project?: GitLabProject | null): void;
219
287
  /**
220
288
  * Update only the selected model, preserving the discovery data.
289
+ * If name is null but ref is provided, attempts to resolve the name
290
+ * from the cached discovery data.
221
291
  */
222
292
  saveSelection(ref: string | null, name: string | null): void;
223
293
  /**
@@ -1170,67 +1240,6 @@ declare class GitLabOAuthManager {
1170
1240
  private createExpiresTimestamp;
1171
1241
  }
1172
1242
 
1173
- /**
1174
- * Simple in-memory cache for GitLab project information
1175
- * Used to avoid repeated API calls when detecting projects from git remotes
1176
- */
1177
- interface GitLabProject {
1178
- id: number;
1179
- path: string;
1180
- pathWithNamespace: string;
1181
- name: string;
1182
- namespaceId?: number;
1183
- }
1184
- /**
1185
- * In-memory cache for GitLab project information with TTL support
1186
- */
1187
- declare class GitLabProjectCache {
1188
- private cache;
1189
- private defaultTTL;
1190
- /**
1191
- * Create a new project cache
1192
- * @param defaultTTL - Default time-to-live in milliseconds (default: 5 minutes)
1193
- */
1194
- constructor(defaultTTL?: number);
1195
- /**
1196
- * Get a cached project by key
1197
- * @param key - Cache key (typically the working directory path)
1198
- * @returns The cached project or null if not found or expired
1199
- */
1200
- get(key: string): GitLabProject | null;
1201
- /**
1202
- * Store a project in the cache
1203
- * @param key - Cache key (typically the working directory path)
1204
- * @param project - The project to cache
1205
- * @param ttl - Optional custom TTL in milliseconds
1206
- */
1207
- set(key: string, project: GitLabProject, ttl?: number): void;
1208
- /**
1209
- * Check if a key exists in the cache (and is not expired)
1210
- * @param key - Cache key to check
1211
- * @returns true if the key exists and is not expired
1212
- */
1213
- has(key: string): boolean;
1214
- /**
1215
- * Remove a specific entry from the cache
1216
- * @param key - Cache key to remove
1217
- */
1218
- delete(key: string): void;
1219
- /**
1220
- * Clear all entries from the cache
1221
- */
1222
- clear(): void;
1223
- /**
1224
- * Get the number of entries in the cache (including expired ones)
1225
- */
1226
- get size(): number;
1227
- /**
1228
- * Clean up expired entries from the cache
1229
- * This is useful for long-running processes to prevent memory leaks
1230
- */
1231
- cleanup(): void;
1232
- }
1233
-
1234
1243
  interface GitLabProjectDetectorConfig {
1235
1244
  instanceUrl: string;
1236
1245
  getHeaders: () => Record<string, string>;
@@ -1573,4 +1582,41 @@ declare class GitLabModelConfigRegistry {
1573
1582
  */
1574
1583
  declare function parseModelsYml(text: string): Map<string, ModelConfig>;
1575
1584
 
1576
- export { AGENT_PRIVILEGES, type ActionResponsePayload, type AdditionalContext, type AiChatAvailableModels, type AiModel, BUNDLED_CLIENT_ID, CLIENT_VERSION, type ClientEvent, DEFAULT_AGENT_PRIVILEGES, DEFAULT_AI_GATEWAY_URL, DEFAULT_CLIENT_CAPABILITIES, DEFAULT_WORKFLOW_DEFINITION, type DirectAccessToken, type DiscoveredModels, 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, 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 WorkflowStatus, type WorkflowToolExecutor, WorkflowType, type WorkflowWebSocketOptions, createGitLab, getAnthropicModelForModelId, getModelMapping, getOpenAIApiType, getOpenAIModelForModelId, getProviderForModelId, getValidModelsForProvider, getWorkflowModelRef, gitlab, isResponsesApiModel, isWorkflowModel, parseModelsYml };
1585
+ /**
1586
+ * High-level workflow model discovery.
1587
+ *
1588
+ * Combines project detection, GraphQL model discovery, model config lookup,
1589
+ * and MODEL_MAPPINGS to produce a list of available workflow models with
1590
+ * their token limits. Consumers map these into their own internal model
1591
+ * representations.
1592
+ */
1593
+
1594
+ interface DiscoveredWorkflowModel {
1595
+ /** Model ID suitable for use with `sdk.workflowChat()` (e.g. "duo-workflow-sonnet-4-6") */
1596
+ id: string;
1597
+ /** Upstream model ref (e.g. "claude_sonnet_4_6") */
1598
+ ref: string;
1599
+ /** Human-readable name (e.g. "Claude Sonnet 4.6") */
1600
+ name: string;
1601
+ /** Max input context tokens */
1602
+ context: number;
1603
+ /** Max output tokens */
1604
+ output: number;
1605
+ /** Whether this model was admin-pinned */
1606
+ pinned: boolean;
1607
+ }
1608
+ interface WorkflowDiscoveryConfig {
1609
+ instanceUrl: string;
1610
+ getHeaders: () => Record<string, string>;
1611
+ fetch?: typeof fetch;
1612
+ }
1613
+ interface WorkflowDiscoveryOptions {
1614
+ workingDirectory: string;
1615
+ }
1616
+ interface WorkflowDiscoveryResult {
1617
+ models: DiscoveredWorkflowModel[];
1618
+ project: GitLabProject | null;
1619
+ }
1620
+ declare function discoverWorkflowModels(config: WorkflowDiscoveryConfig, options: WorkflowDiscoveryOptions): Promise<WorkflowDiscoveryResult>;
1621
+
1622
+ export { AGENT_PRIVILEGES, type ActionResponsePayload, type AdditionalContext, type AiChatAvailableModels, type AiModel, 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, 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, isResponsesApiModel, isWorkflowModel, parseModelsYml };
package/dist/index.js CHANGED
@@ -61,6 +61,7 @@ __export(index_exports, {
61
61
  WS_KEEPALIVE_PING_INTERVAL_MS: () => WS_KEEPALIVE_PING_INTERVAL_MS,
62
62
  WorkflowType: () => WorkflowType,
63
63
  createGitLab: () => createGitLab,
64
+ discoverWorkflowModels: () => discoverWorkflowModels,
64
65
  getAnthropicModelForModelId: () => getAnthropicModelForModelId,
65
66
  getModelMapping: () => getModelMapping,
66
67
  getOpenAIApiType: () => getOpenAIApiType,
@@ -1576,7 +1577,7 @@ var GitLabOpenAILanguageModel = class {
1576
1577
  var import_isomorphic_ws = __toESM(require("isomorphic-ws"));
1577
1578
 
1578
1579
  // src/version.ts
1579
- var VERSION = true ? "5.1.1" : "0.0.0-dev";
1580
+ var VERSION = true ? "5.2.0" : "0.0.0-dev";
1580
1581
 
1581
1582
  // src/gitlab-workflow-types.ts
1582
1583
  var WorkflowType = /* @__PURE__ */ ((WorkflowType2) => {
@@ -2745,6 +2746,7 @@ var fs = __toESM(require("fs"));
2745
2746
  var path2 = __toESM(require("path"));
2746
2747
  var os = __toESM(require("os"));
2747
2748
  var crypto = __toESM(require("crypto"));
2749
+ var DISCOVERY_TTL_MS = 10 * 60 * 1e3;
2748
2750
  function getCacheFilePath() {
2749
2751
  const cacheHome = process.env.XDG_CACHE_HOME || path2.join(os.homedir(), ".cache");
2750
2752
  return path2.join(cacheHome, "opencode", "gitlab-workflow-model-cache.json");
@@ -2794,13 +2796,23 @@ var GitLabModelCache = class {
2794
2796
  data[this.key] = entry;
2795
2797
  this.writeAll(data);
2796
2798
  }
2799
+ /**
2800
+ * Returns true if the discovery data is missing or older than DISCOVERY_TTL_MS.
2801
+ */
2802
+ isDiscoveryExpired() {
2803
+ const entry = this.load();
2804
+ if (!entry?.discovery || !entry.updatedAt) return true;
2805
+ return Date.now() - new Date(entry.updatedAt).getTime() > DISCOVERY_TTL_MS;
2806
+ }
2797
2807
  /**
2798
2808
  * Update only the discovery portion of the cache, preserving selection.
2809
+ * Optionally persists the associated GitLab project.
2799
2810
  */
2800
- saveDiscovery(discovery) {
2811
+ saveDiscovery(discovery, project) {
2801
2812
  const existing = this.load();
2802
2813
  this.save({
2803
2814
  discovery,
2815
+ project: project !== void 0 ? project : existing?.project ?? null,
2804
2816
  selectedModelRef: existing?.selectedModelRef ?? null,
2805
2817
  selectedModelName: existing?.selectedModelName ?? null,
2806
2818
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -2808,13 +2820,22 @@ var GitLabModelCache = class {
2808
2820
  }
2809
2821
  /**
2810
2822
  * Update only the selected model, preserving the discovery data.
2823
+ * If name is null but ref is provided, attempts to resolve the name
2824
+ * from the cached discovery data.
2811
2825
  */
2812
2826
  saveSelection(ref, name) {
2813
2827
  const existing = this.load();
2828
+ let resolved = name;
2829
+ if (!resolved && ref && existing?.discovery) {
2830
+ const d = existing.discovery;
2831
+ const match = d.pinnedModel?.ref === ref ? d.pinnedModel : d.selectableModels.find((m) => m.ref === ref) ?? (d.defaultModel?.ref === ref ? d.defaultModel : null);
2832
+ resolved = match?.name ?? null;
2833
+ }
2814
2834
  this.save({
2815
2835
  discovery: existing?.discovery ?? null,
2836
+ project: existing?.project ?? null,
2816
2837
  selectedModelRef: ref,
2817
- selectedModelName: name,
2838
+ selectedModelName: resolved,
2818
2839
  updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2819
2840
  });
2820
2841
  }
@@ -2956,7 +2977,8 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
2956
2977
  */
2957
2978
  set selectedModelRef(ref) {
2958
2979
  this._selectedModelRef = ref ?? void 0;
2959
- this.modelCache.saveSelection(ref, this._selectedModelName ?? null);
2980
+ this._selectedModelName = void 0;
2981
+ this.modelCache.saveSelection(ref, null);
2960
2982
  }
2961
2983
  /**
2962
2984
  * Get the cached selected model display name.
@@ -4493,6 +4515,67 @@ function parseModelsYml(text) {
4493
4515
  }
4494
4516
  return configs;
4495
4517
  }
4518
+
4519
+ // src/gitlab-workflow-discovery.ts
4520
+ var configRegistry = new GitLabModelConfigRegistry();
4521
+ async function buildModels(discovered, project) {
4522
+ const configs = await configRegistry.getConfigs();
4523
+ const refToID = /* @__PURE__ */ new Map();
4524
+ for (const [id, mapping] of Object.entries(MODEL_MAPPINGS)) {
4525
+ if (mapping.provider === "workflow" && id !== "duo-workflow" && id !== "duo-workflow-default") {
4526
+ refToID.set(mapping.model, id);
4527
+ }
4528
+ }
4529
+ const all = discovered.pinnedModel ? [discovered.pinnedModel] : [
4530
+ ...discovered.selectableModels ?? [],
4531
+ ...discovered.defaultModel ? [discovered.defaultModel] : []
4532
+ ];
4533
+ const seen = /* @__PURE__ */ new Set();
4534
+ const models = [];
4535
+ for (const model of all) {
4536
+ if (!model.ref || seen.has(model.ref)) continue;
4537
+ seen.add(model.ref);
4538
+ const id = refToID.get(model.ref) ?? `duo-workflow-${model.ref.replace(/[/_]/g, "-")}`;
4539
+ const limits = configs.get(model.ref);
4540
+ models.push({
4541
+ id,
4542
+ ref: model.ref,
4543
+ name: model.name,
4544
+ context: limits?.context ?? 2e5,
4545
+ output: limits?.output ?? 64e3,
4546
+ pinned: !!discovered.pinnedModel && discovered.pinnedModel.ref === model.ref
4547
+ });
4548
+ }
4549
+ return { models, project };
4550
+ }
4551
+ async function discoverWorkflowModels(config, options) {
4552
+ const cache = new GitLabModelCache(options.workingDirectory, config.instanceUrl);
4553
+ if (!cache.isDiscoveryExpired()) {
4554
+ const entry = cache.load();
4555
+ return buildModels(entry.discovery, entry.project);
4556
+ }
4557
+ const detector = new GitLabProjectDetector({
4558
+ instanceUrl: config.instanceUrl,
4559
+ getHeaders: config.getHeaders,
4560
+ fetch: config.fetch
4561
+ });
4562
+ let project = null;
4563
+ try {
4564
+ project = await detector.detectProject(options.workingDirectory);
4565
+ } catch {
4566
+ return { models: [], project: null };
4567
+ }
4568
+ const namespaceId = project?.namespaceId;
4569
+ if (!namespaceId) return { models: [], project };
4570
+ const discovery = new GitLabModelDiscovery({
4571
+ instanceUrl: config.instanceUrl,
4572
+ getHeaders: config.getHeaders,
4573
+ fetch: config.fetch
4574
+ });
4575
+ const discovered = await discovery.discover(`gid://gitlab/Group/${namespaceId}`);
4576
+ cache.saveDiscovery(discovered, project);
4577
+ return buildModels(discovered, project);
4578
+ }
4496
4579
  // Annotate the CommonJS export names for ESM import in node:
4497
4580
  0 && (module.exports = {
4498
4581
  AGENT_PRIVILEGES,
@@ -4527,6 +4610,7 @@ function parseModelsYml(text) {
4527
4610
  WS_KEEPALIVE_PING_INTERVAL_MS,
4528
4611
  WorkflowType,
4529
4612
  createGitLab,
4613
+ discoverWorkflowModels,
4530
4614
  getAnthropicModelForModelId,
4531
4615
  getModelMapping,
4532
4616
  getOpenAIApiType,