@gitlab/gitlab-ai-provider 1.0.5

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.
@@ -0,0 +1,572 @@
1
+ import {
2
+ LanguageModelV2,
3
+ LanguageModelV2CallOptions,
4
+ LanguageModelV2Content,
5
+ LanguageModelV2FinishReason,
6
+ LanguageModelV2Usage,
7
+ LanguageModelV2CallWarning,
8
+ LanguageModelV2StreamPart,
9
+ } from '@ai-sdk/provider';
10
+ import { z } from 'zod';
11
+ import { Tool } from '@anthropic-ai/sdk/resources/messages';
12
+
13
+ interface GitLabAgenticConfig {
14
+ provider: string;
15
+ instanceUrl: string;
16
+ getHeaders: () => Record<string, string>;
17
+ fetch?: typeof fetch;
18
+ /**
19
+ * Optional callback to refresh the API key when a 401 error occurs.
20
+ * Should clear cached credentials and re-fetch from auth provider.
21
+ */
22
+ refreshApiKey?: () => Promise<void>;
23
+ /**
24
+ * The Anthropic model to use (e.g., 'claude-sonnet-4-5-20250929')
25
+ * @default 'claude-sonnet-4-5-20250929'
26
+ */
27
+ anthropicModel?: string;
28
+ /**
29
+ * Maximum tokens to generate
30
+ * @default 8192
31
+ */
32
+ maxTokens?: number;
33
+ /**
34
+ * Feature flags to pass to the GitLab API
35
+ * @default { DuoAgentPlatformNext: true }
36
+ */
37
+ featureFlags?: {
38
+ DuoAgentPlatformNext: true;
39
+ } & Record<string, boolean>;
40
+ }
41
+ /**
42
+ * GitLab Agentic Language Model
43
+ *
44
+ * This model uses GitLab's Anthropic proxy to provide native tool calling support
45
+ * for the duo-chat model. It connects to Claude through GitLab's cloud proxy
46
+ * at https://cloud.gitlab.com/ai/v1/proxy/anthropic/
47
+ */
48
+ declare class GitLabAgenticLanguageModel implements LanguageModelV2 {
49
+ readonly specificationVersion: 'v2';
50
+ readonly modelId: string;
51
+ readonly supportedUrls: Record<string, RegExp[]>;
52
+ private readonly config;
53
+ private readonly directAccessClient;
54
+ private anthropicClient;
55
+ constructor(modelId: string, config: GitLabAgenticConfig);
56
+ get provider(): string;
57
+ /**
58
+ * Get or create an Anthropic client with valid credentials
59
+ * @param forceRefresh - If true, forces a token refresh before creating the client
60
+ */
61
+ private getAnthropicClient;
62
+ /**
63
+ * Check if an error is a token-related authentication error that can be retried
64
+ */
65
+ private isTokenError;
66
+ /**
67
+ * Convert AI SDK tools to Anthropic tool format
68
+ */
69
+ private convertTools;
70
+ /**
71
+ * Convert AI SDK tool choice to Anthropic format
72
+ */
73
+ private convertToolChoice;
74
+ /**
75
+ * Convert AI SDK prompt to Anthropic messages format
76
+ */
77
+ private convertPrompt;
78
+ /**
79
+ * Convert Anthropic finish reason to AI SDK format
80
+ */
81
+ private convertFinishReason;
82
+ doGenerate(options: LanguageModelV2CallOptions): Promise<{
83
+ content: LanguageModelV2Content[];
84
+ finishReason: LanguageModelV2FinishReason;
85
+ usage: LanguageModelV2Usage;
86
+ warnings: LanguageModelV2CallWarning[];
87
+ }>;
88
+ private doGenerateWithRetry;
89
+ doStream(options: LanguageModelV2CallOptions): Promise<{
90
+ stream: ReadableStream<LanguageModelV2StreamPart>;
91
+ request?: {
92
+ body?: unknown;
93
+ };
94
+ response?: {
95
+ headers?: Record<string, string>;
96
+ };
97
+ }>;
98
+ private doStreamWithRetry;
99
+ }
100
+
101
+ interface GitLabProvider {
102
+ (modelId: string): LanguageModelV2;
103
+ readonly specificationVersion: 'v2';
104
+ languageModel(modelId: string): LanguageModelV2;
105
+ chat(modelId: string): LanguageModelV2;
106
+ agenticChat(modelId: string, options?: GitLabAgenticOptions): GitLabAgenticLanguageModel;
107
+ textEmbeddingModel(modelId: string): never;
108
+ imageModel(modelId: string): never;
109
+ }
110
+ interface GitLabAgenticOptions {
111
+ /**
112
+ * The Anthropic model to use
113
+ * @default 'claude-sonnet-4-20250514'
114
+ */
115
+ anthropicModel?: string;
116
+ /**
117
+ * Maximum tokens to generate
118
+ * @default 8192
119
+ */
120
+ maxTokens?: number;
121
+ /**
122
+ * Feature flags to pass to the GitLab API
123
+ */
124
+ featureFlags?: Record<string, boolean>;
125
+ }
126
+ interface GitLabProviderSettings {
127
+ /**
128
+ * GitLab instance URL (e.g., 'https://gitlab.com')
129
+ * @default 'https://gitlab.com'
130
+ */
131
+ instanceUrl?: string;
132
+ /**
133
+ * API token (Personal Access Token or OAuth access token)
134
+ * Can also be set via GITLAB_TOKEN environment variable
135
+ */
136
+ apiKey?: string;
137
+ /**
138
+ * OAuth refresh token (optional, for OAuth flow)
139
+ */
140
+ refreshToken?: string;
141
+ /**
142
+ * OAuth client ID (required for OAuth flow)
143
+ */
144
+ clientId?: string;
145
+ /**
146
+ * OAuth redirect URI (required for OAuth flow)
147
+ */
148
+ redirectUri?: string;
149
+ /**
150
+ * Custom headers to include in requests
151
+ */
152
+ headers?: Record<string, string>;
153
+ /**
154
+ * Custom fetch implementation
155
+ */
156
+ fetch?: typeof fetch;
157
+ /**
158
+ * Provider name override
159
+ */
160
+ name?: string;
161
+ /**
162
+ * Default feature flags to pass to the GitLab API for all agentic chat models
163
+ */
164
+ featureFlags?: Record<string, boolean>;
165
+ }
166
+ declare function createGitLab(options?: GitLabProviderSettings): GitLabProvider;
167
+ /**
168
+ * Default GitLab Duo provider instance
169
+ *
170
+ * @example
171
+ * ```typescript
172
+ * import { gitlab } from '@ai-sdk/gitlab';
173
+ *
174
+ * const model = gitlab('duo-chat');
175
+ * ```
176
+ */
177
+ declare const gitlab: GitLabProvider;
178
+
179
+ interface GitLabErrorOptions {
180
+ message: string;
181
+ statusCode?: number;
182
+ responseBody?: string;
183
+ cause?: unknown;
184
+ }
185
+ declare class GitLabError extends Error {
186
+ readonly statusCode?: number;
187
+ readonly responseBody?: string;
188
+ readonly cause?: unknown;
189
+ constructor(options: GitLabErrorOptions);
190
+ static fromResponse(response: Response, body: string): GitLabError;
191
+ isAuthError(): boolean;
192
+ isRateLimitError(): boolean;
193
+ isForbiddenError(): boolean;
194
+ isServerError(): boolean;
195
+ }
196
+
197
+ declare const gitlabOAuthTokenResponseSchema: z.ZodObject<
198
+ {
199
+ access_token: z.ZodString;
200
+ refresh_token: z.ZodOptional<z.ZodString>;
201
+ expires_in: z.ZodNumber;
202
+ created_at: z.ZodNumber;
203
+ },
204
+ 'strip',
205
+ z.ZodTypeAny,
206
+ {
207
+ access_token?: string;
208
+ refresh_token?: string;
209
+ expires_in?: number;
210
+ created_at?: number;
211
+ },
212
+ {
213
+ access_token?: string;
214
+ refresh_token?: string;
215
+ expires_in?: number;
216
+ created_at?: number;
217
+ }
218
+ >;
219
+ type GitLabOAuthTokenResponse = z.infer<typeof gitlabOAuthTokenResponseSchema>;
220
+
221
+ /**
222
+ * OAuth types and constants for GitLab authentication
223
+ * Based on gitlab-vscode-extension and gitlab-lsp patterns
224
+ */
225
+ interface GitLabOAuthTokens {
226
+ accessToken: string;
227
+ refreshToken: string;
228
+ expiresAt: number;
229
+ instanceUrl: string;
230
+ }
231
+ interface OpenCodeAuthOAuth {
232
+ type: 'oauth';
233
+ refresh: string;
234
+ access: string;
235
+ expires: number;
236
+ instanceUrl?: string;
237
+ }
238
+ interface OpenCodeAuthApi {
239
+ type: 'api';
240
+ key: string;
241
+ }
242
+ type OpenCodeAuth = OpenCodeAuthOAuth | OpenCodeAuthApi;
243
+ /**
244
+ * Bundled OAuth client ID for GitLab.com
245
+ * Same as used in gitlab-vscode-extension
246
+ */
247
+ declare const BUNDLED_CLIENT_ID =
248
+ '36f2a70cddeb5a0889d4fd8295c241b7e9848e89cf9e599d0eed2d8e5350fbf5';
249
+ /**
250
+ * GitLab.com URL constant
251
+ */
252
+ declare const GITLAB_COM_URL = 'https://gitlab.com';
253
+ /**
254
+ * Token expiry skew in milliseconds (5 minutes)
255
+ * Refresh tokens this many milliseconds before they expire
256
+ */
257
+ declare const TOKEN_EXPIRY_SKEW_MS: number;
258
+ /**
259
+ * OAuth scopes to request
260
+ */
261
+ declare const OAUTH_SCOPES: string[];
262
+
263
+ /**
264
+ * GitLab OAuth Manager
265
+ * Handles OAuth token management, refresh, and exchange
266
+ * Based on gitlab-vscode-extension TokenExchangeService and gitlab-lsp OAuthClientProvider
267
+ */
268
+
269
+ interface TokenExchangeParams {
270
+ instanceUrl: string;
271
+ clientId?: string;
272
+ redirectUri?: string;
273
+ }
274
+ interface AuthorizationCodeParams extends TokenExchangeParams {
275
+ code: string;
276
+ codeVerifier: string;
277
+ }
278
+ interface RefreshTokenParams extends TokenExchangeParams {
279
+ refreshToken: string;
280
+ }
281
+ declare class GitLabOAuthManager {
282
+ private fetch;
283
+ constructor(fetchImpl?: typeof fetch);
284
+ /**
285
+ * Check if a token is expired
286
+ */
287
+ isTokenExpired(expiresAt: number): boolean;
288
+ /**
289
+ * Check if a token needs refresh (within skew window)
290
+ */
291
+ needsRefresh(expiresAt: number): boolean;
292
+ /**
293
+ * Refresh tokens if needed
294
+ * Returns the same tokens if refresh is not needed, or new tokens if refreshed
295
+ */
296
+ refreshIfNeeded(tokens: GitLabOAuthTokens, clientId?: string): Promise<GitLabOAuthTokens>;
297
+ /**
298
+ * Exchange authorization code for tokens
299
+ * Based on gitlab-vscode-extension createOAuthAccountFromCode
300
+ */
301
+ exchangeAuthorizationCode(params: AuthorizationCodeParams): Promise<GitLabOAuthTokens>;
302
+ /**
303
+ * Exchange refresh token for new tokens
304
+ * Based on gitlab-vscode-extension TokenExchangeService
305
+ */
306
+ exchangeRefreshToken(params: RefreshTokenParams): Promise<GitLabOAuthTokens>;
307
+ /**
308
+ * Get the OAuth client ID for an instance
309
+ */
310
+ private getClientId;
311
+ /**
312
+ * Exchange token with GitLab OAuth endpoint
313
+ * Based on gitlab-vscode-extension GitLabService.exchangeToken
314
+ */
315
+ private exchangeToken;
316
+ /**
317
+ * Create GitLabOAuthTokens from token response
318
+ */
319
+ private createTokensFromResponse;
320
+ /**
321
+ * Create expiry timestamp from token response
322
+ * Based on gitlab-vscode-extension createExpiresTimestamp
323
+ */
324
+ private createExpiresTimestamp;
325
+ }
326
+
327
+ /**
328
+ * Tool definitions for Anthropic Claude
329
+ */
330
+ declare const ANTHROPIC_TOOLS: Tool[];
331
+ interface ToolResult {
332
+ result: string;
333
+ error?: string;
334
+ }
335
+ interface ToolInput {
336
+ [key: string]: unknown;
337
+ }
338
+ /**
339
+ * Tool executor for local file and command operations
340
+ */
341
+ declare class AnthropicToolExecutor {
342
+ private readonly workingDirectory;
343
+ constructor(workingDirectory: string);
344
+ /**
345
+ * Execute a tool by name with given input
346
+ */
347
+ execute(toolName: string, input: ToolInput): Promise<ToolResult>;
348
+ private resolvePath;
349
+ private listDir;
350
+ private readFile;
351
+ private writeFile;
352
+ private editFile;
353
+ private findFiles;
354
+ private mkdir;
355
+ private grep;
356
+ private runCommand;
357
+ private runGitCommand;
358
+ private executeCommand;
359
+ }
360
+
361
+ /**
362
+ * GitLab API tools for interacting with GitLab resources
363
+ * These tools allow the AI to access merge requests, issues, pipelines, etc.
364
+ */
365
+ declare const GITLAB_API_TOOLS: Tool[];
366
+ interface GitLabApiToolsConfig {
367
+ instanceUrl: string;
368
+ token: string;
369
+ fetch?: typeof fetch;
370
+ }
371
+ /**
372
+ * Executor for GitLab API tools
373
+ */
374
+ declare class GitLabApiToolExecutor {
375
+ private readonly config;
376
+ constructor(config: GitLabApiToolsConfig);
377
+ private get headers();
378
+ private fetchApi;
379
+ private encodeProjectId;
380
+ /**
381
+ * Execute a GitLab API tool by name
382
+ */
383
+ execute(toolName: string, input: ToolInput): Promise<ToolResult>;
384
+ private getMergeRequest;
385
+ private listMergeRequests;
386
+ private getMrChanges;
387
+ private listMrDiscussions;
388
+ private createMrNote;
389
+ private getIssue;
390
+ private listIssues;
391
+ private createIssueNote;
392
+ private listPipelines;
393
+ private getPipeline;
394
+ private listPipelineJobs;
395
+ private getJobLog;
396
+ private retryJob;
397
+ private getFile;
398
+ private listCommits;
399
+ private getCommitDiff;
400
+ private listBranches;
401
+ private search;
402
+ private getProject;
403
+ private listProjectMembers;
404
+ }
405
+ /**
406
+ * Check if a tool name is a GitLab API tool
407
+ */
408
+ declare function isGitLabApiTool(toolName: string): boolean;
409
+
410
+ /**
411
+ * Simple in-memory cache for GitLab project information
412
+ * Used to avoid repeated API calls when detecting projects from git remotes
413
+ */
414
+ interface GitLabProject {
415
+ id: number;
416
+ path: string;
417
+ pathWithNamespace: string;
418
+ name: string;
419
+ namespaceId?: number;
420
+ }
421
+ /**
422
+ * In-memory cache for GitLab project information with TTL support
423
+ */
424
+ declare class GitLabProjectCache {
425
+ private cache;
426
+ private defaultTTL;
427
+ /**
428
+ * Create a new project cache
429
+ * @param defaultTTL - Default time-to-live in milliseconds (default: 5 minutes)
430
+ */
431
+ constructor(defaultTTL?: number);
432
+ /**
433
+ * Get a cached project by key
434
+ * @param key - Cache key (typically the working directory path)
435
+ * @returns The cached project or null if not found or expired
436
+ */
437
+ get(key: string): GitLabProject | null;
438
+ /**
439
+ * Store a project in the cache
440
+ * @param key - Cache key (typically the working directory path)
441
+ * @param project - The project to cache
442
+ * @param ttl - Optional custom TTL in milliseconds
443
+ */
444
+ set(key: string, project: GitLabProject, ttl?: number): void;
445
+ /**
446
+ * Check if a key exists in the cache (and is not expired)
447
+ * @param key - Cache key to check
448
+ * @returns true if the key exists and is not expired
449
+ */
450
+ has(key: string): boolean;
451
+ /**
452
+ * Remove a specific entry from the cache
453
+ * @param key - Cache key to remove
454
+ */
455
+ delete(key: string): void;
456
+ /**
457
+ * Clear all entries from the cache
458
+ */
459
+ clear(): void;
460
+ /**
461
+ * Get the number of entries in the cache (including expired ones)
462
+ */
463
+ get size(): number;
464
+ /**
465
+ * Clean up expired entries from the cache
466
+ * This is useful for long-running processes to prevent memory leaks
467
+ */
468
+ cleanup(): void;
469
+ }
470
+
471
+ interface GitLabProjectDetectorConfig {
472
+ instanceUrl: string;
473
+ getHeaders: () => Record<string, string>;
474
+ fetch?: typeof fetch;
475
+ cache?: GitLabProjectCache;
476
+ gitTimeout?: number;
477
+ }
478
+ /**
479
+ * Detects GitLab project information from git remote URLs
480
+ *
481
+ * This class provides functionality to:
482
+ * - Parse git remote URLs (SSH, HTTPS, custom domains)
483
+ * - Execute git commands to get remote URLs
484
+ * - Fetch project details from GitLab API
485
+ * - Cache project information to avoid repeated API calls
486
+ */
487
+ declare class GitLabProjectDetector {
488
+ private readonly config;
489
+ private readonly fetchFn;
490
+ private readonly cache;
491
+ constructor(config: GitLabProjectDetectorConfig);
492
+ /**
493
+ * Auto-detect GitLab project from git remote in the working directory
494
+ *
495
+ * @param workingDirectory - The directory to check for git remote
496
+ * @param remoteName - The git remote name to use (default: 'origin')
497
+ * @returns The detected project or null if detection fails
498
+ */
499
+ detectProject(workingDirectory: string, remoteName?: string): Promise<GitLabProject | null>;
500
+ /**
501
+ * Parse a git remote URL to extract the project path
502
+ *
503
+ * Supports:
504
+ * - SSH: git@gitlab.com:namespace/project.git
505
+ * - HTTPS: https://gitlab.com/namespace/project.git
506
+ * - HTTP: http://gitlab.local/namespace/project.git
507
+ * - Custom domains and ports
508
+ *
509
+ * @param remoteUrl - The git remote URL
510
+ * @param instanceUrl - The GitLab instance URL to match against
511
+ * @returns The project path (e.g., "namespace/project") or null if parsing fails
512
+ */
513
+ parseGitRemoteUrl(remoteUrl: string, instanceUrl: string): string | null;
514
+ /**
515
+ * Get the git remote URL from a working directory
516
+ *
517
+ * @param workingDirectory - The directory to check
518
+ * @param remoteName - The git remote name (default: 'origin')
519
+ * @returns The remote URL or null if not found
520
+ */
521
+ getGitRemoteUrl(workingDirectory: string, remoteName?: string): Promise<string | null>;
522
+ /**
523
+ * Fetch project details from GitLab API by project path
524
+ *
525
+ * @param projectPath - The project path (e.g., "namespace/project")
526
+ * @returns The project details
527
+ * @throws GitLabError if the API call fails
528
+ */
529
+ getProjectByPath(projectPath: string): Promise<GitLabProject>;
530
+ /**
531
+ * Clear the project cache
532
+ */
533
+ clearCache(): void;
534
+ /**
535
+ * Get the cache instance (useful for testing)
536
+ */
537
+ getCache(): GitLabProjectCache;
538
+ }
539
+
540
+ export {
541
+ ANTHROPIC_TOOLS,
542
+ AnthropicToolExecutor,
543
+ BUNDLED_CLIENT_ID,
544
+ GITLAB_API_TOOLS,
545
+ GITLAB_COM_URL,
546
+ type GitLabAgenticConfig,
547
+ GitLabAgenticLanguageModel,
548
+ type GitLabAgenticOptions,
549
+ GitLabApiToolExecutor,
550
+ type GitLabApiToolsConfig,
551
+ GitLabError,
552
+ type GitLabErrorOptions,
553
+ GitLabOAuthManager,
554
+ type GitLabOAuthTokenResponse,
555
+ type GitLabOAuthTokens,
556
+ type GitLabProject,
557
+ GitLabProjectCache,
558
+ GitLabProjectDetector,
559
+ type GitLabProjectDetectorConfig,
560
+ type GitLabProvider,
561
+ type GitLabProviderSettings,
562
+ OAUTH_SCOPES,
563
+ type OpenCodeAuth,
564
+ type OpenCodeAuthApi,
565
+ type OpenCodeAuthOAuth,
566
+ TOKEN_EXPIRY_SKEW_MS,
567
+ type ToolInput,
568
+ type ToolResult,
569
+ createGitLab,
570
+ gitlab,
571
+ isGitLabApiTool,
572
+ };