gitlab-ai-provider 5.0.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 +438 -0
- package/LICENSE +28 -0
- package/README.md +815 -0
- package/dist/index.d.mts +1521 -0
- package/dist/index.d.ts +1658 -0
- package/dist/index.js +4289 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +4220 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +119 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1658 @@
|
|
|
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
|
+
|
|
12
|
+
interface GitLabAnthropicConfig {
|
|
13
|
+
provider: string;
|
|
14
|
+
instanceUrl: string;
|
|
15
|
+
getHeaders: () => Record<string, string>;
|
|
16
|
+
fetch?: typeof fetch;
|
|
17
|
+
/**
|
|
18
|
+
* Optional callback to refresh the API key when a 401 error occurs.
|
|
19
|
+
* Should clear cached credentials and re-fetch from auth provider.
|
|
20
|
+
*/
|
|
21
|
+
refreshApiKey?: () => Promise<void>;
|
|
22
|
+
/**
|
|
23
|
+
* The Anthropic model to use (e.g., 'claude-sonnet-4-5-20250929')
|
|
24
|
+
* @default 'claude-sonnet-4-5-20250929'
|
|
25
|
+
*/
|
|
26
|
+
anthropicModel?: string;
|
|
27
|
+
/**
|
|
28
|
+
* Maximum tokens to generate
|
|
29
|
+
* @default 8192
|
|
30
|
+
*/
|
|
31
|
+
maxTokens?: number;
|
|
32
|
+
/**
|
|
33
|
+
* Feature flags to pass to the GitLab API
|
|
34
|
+
* @default { DuoAgentPlatformNext: true }
|
|
35
|
+
*/
|
|
36
|
+
featureFlags?: {
|
|
37
|
+
DuoAgentPlatformNext: true;
|
|
38
|
+
} & Record<string, boolean>;
|
|
39
|
+
/**
|
|
40
|
+
* AI Gateway URL for the Anthropic proxy.
|
|
41
|
+
* Can also be set via GITLAB_AI_GATEWAY_URL environment variable.
|
|
42
|
+
* @default 'https://cloud.gitlab.com'
|
|
43
|
+
*/
|
|
44
|
+
aiGatewayUrl?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Custom headers for AI Gateway Anthropic proxy requests.
|
|
47
|
+
* Merged with headers from direct_access token response.
|
|
48
|
+
*/
|
|
49
|
+
aiGatewayHeaders?: Record<string, string>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* GitLab Anthropic Language Model
|
|
53
|
+
*
|
|
54
|
+
* This model uses GitLab's Anthropic proxy to provide native tool calling support
|
|
55
|
+
* for the duo-chat model. It connects to Claude through GitLab's cloud proxy
|
|
56
|
+
* at https://cloud.gitlab.com/ai/v1/proxy/anthropic/
|
|
57
|
+
*/
|
|
58
|
+
declare class GitLabAnthropicLanguageModel implements LanguageModelV2 {
|
|
59
|
+
readonly specificationVersion: 'v2';
|
|
60
|
+
readonly modelId: string;
|
|
61
|
+
readonly supportedUrls: Record<string, RegExp[]>;
|
|
62
|
+
private readonly config;
|
|
63
|
+
private readonly directAccessClient;
|
|
64
|
+
private anthropicClient;
|
|
65
|
+
constructor(modelId: string, config: GitLabAnthropicConfig);
|
|
66
|
+
get provider(): string;
|
|
67
|
+
/**
|
|
68
|
+
* Get or create an Anthropic client with valid credentials
|
|
69
|
+
* @param forceRefresh - If true, forces a token refresh before creating the client
|
|
70
|
+
*/
|
|
71
|
+
private getAnthropicClient;
|
|
72
|
+
/**
|
|
73
|
+
* Check if an error is a token-related authentication error that can be retried
|
|
74
|
+
*/
|
|
75
|
+
private isTokenError;
|
|
76
|
+
/**
|
|
77
|
+
* Check if an error is a context overflow error (prompt too long)
|
|
78
|
+
* These should NOT trigger token refresh and should be reported to the user.
|
|
79
|
+
*/
|
|
80
|
+
private isContextOverflowError;
|
|
81
|
+
/**
|
|
82
|
+
* Convert AI SDK tools to Anthropic tool format
|
|
83
|
+
*/
|
|
84
|
+
private convertTools;
|
|
85
|
+
/**
|
|
86
|
+
* Convert AI SDK tool choice to Anthropic format
|
|
87
|
+
*/
|
|
88
|
+
private convertToolChoice;
|
|
89
|
+
/**
|
|
90
|
+
* Convert AI SDK prompt to Anthropic messages format
|
|
91
|
+
*/
|
|
92
|
+
private convertPrompt;
|
|
93
|
+
/**
|
|
94
|
+
* Convert Anthropic finish reason to AI SDK format
|
|
95
|
+
*/
|
|
96
|
+
private convertFinishReason;
|
|
97
|
+
doGenerate(options: LanguageModelV2CallOptions): Promise<{
|
|
98
|
+
content: LanguageModelV2Content[];
|
|
99
|
+
finishReason: LanguageModelV2FinishReason;
|
|
100
|
+
usage: LanguageModelV2Usage;
|
|
101
|
+
warnings: LanguageModelV2CallWarning[];
|
|
102
|
+
}>;
|
|
103
|
+
private doGenerateWithRetry;
|
|
104
|
+
doStream(options: LanguageModelV2CallOptions): Promise<{
|
|
105
|
+
stream: ReadableStream<LanguageModelV2StreamPart>;
|
|
106
|
+
request?: {
|
|
107
|
+
body?: unknown;
|
|
108
|
+
};
|
|
109
|
+
response?: {
|
|
110
|
+
headers?: Record<string, string>;
|
|
111
|
+
};
|
|
112
|
+
}>;
|
|
113
|
+
private doStreamWithRetry;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Dynamic model discovery via GitLab GraphQL API.
|
|
118
|
+
*
|
|
119
|
+
* Queries `aiChatAvailableModels` to discover which models are available
|
|
120
|
+
* for a given GitLab namespace. Used to dynamically populate `duo-workflow-*`
|
|
121
|
+
* model IDs at runtime.
|
|
122
|
+
*
|
|
123
|
+
* Requires GitLab 18.4+ (18.5+ for pinned model support).
|
|
124
|
+
*/
|
|
125
|
+
interface AiModel {
|
|
126
|
+
/** Display name (e.g., 'Claude Sonnet 4.6') */
|
|
127
|
+
name: string;
|
|
128
|
+
/** Model reference for API use (e.g., 'claude_sonnet_4_6' or 'anthropic/claude-sonnet-4-5-20250929') */
|
|
129
|
+
ref: string;
|
|
130
|
+
}
|
|
131
|
+
interface AiChatAvailableModels {
|
|
132
|
+
defaultModel: AiModel | null;
|
|
133
|
+
selectableModels: AiModel[];
|
|
134
|
+
pinnedModel: AiModel | null;
|
|
135
|
+
}
|
|
136
|
+
interface DiscoveredModels {
|
|
137
|
+
/** The effective model (pinned > user-selected > default) */
|
|
138
|
+
defaultModel: AiModel | null;
|
|
139
|
+
/** All models the user can select from */
|
|
140
|
+
selectableModels: AiModel[];
|
|
141
|
+
/** Admin-pinned model (overrides everything) */
|
|
142
|
+
pinnedModel: AiModel | null;
|
|
143
|
+
/** Whether the ai_user_model_switching feature flag is enabled */
|
|
144
|
+
modelSwitchingEnabled: boolean;
|
|
145
|
+
/** GitLab instance version */
|
|
146
|
+
instanceVersion: string | null;
|
|
147
|
+
}
|
|
148
|
+
interface ModelDiscoveryConfig {
|
|
149
|
+
/** GitLab instance URL */
|
|
150
|
+
instanceUrl: string;
|
|
151
|
+
/** Function returning auth headers */
|
|
152
|
+
getHeaders: () => Record<string, string>;
|
|
153
|
+
/** Custom fetch */
|
|
154
|
+
fetch?: typeof fetch;
|
|
155
|
+
}
|
|
156
|
+
declare class GitLabModelDiscovery {
|
|
157
|
+
private readonly config;
|
|
158
|
+
private readonly fetchFn;
|
|
159
|
+
private cache;
|
|
160
|
+
constructor(config: ModelDiscoveryConfig);
|
|
161
|
+
/**
|
|
162
|
+
* Discover available models for a given root namespace.
|
|
163
|
+
*
|
|
164
|
+
* Results are cached per `rootNamespaceId` with a 10-minute TTL.
|
|
165
|
+
* Use `invalidateCache()` to force an immediate refresh.
|
|
166
|
+
*
|
|
167
|
+
* @param rootNamespaceId - GitLab group ID (e.g., 'gid://gitlab/Group/12345')
|
|
168
|
+
*/
|
|
169
|
+
discover(rootNamespaceId: string): Promise<DiscoveredModels>;
|
|
170
|
+
/**
|
|
171
|
+
* Get the effective model ref to use for a workflow.
|
|
172
|
+
*
|
|
173
|
+
* Priority: pinned > user-selected > default.
|
|
174
|
+
*
|
|
175
|
+
* @param rootNamespaceId - GitLab group ID
|
|
176
|
+
* @param userSelectedRef - Optional user preference
|
|
177
|
+
*/
|
|
178
|
+
getEffectiveModelRef(rootNamespaceId: string, userSelectedRef?: string): Promise<string | null>;
|
|
179
|
+
/**
|
|
180
|
+
* Invalidate the cached discovery results.
|
|
181
|
+
*/
|
|
182
|
+
invalidateCache(): void;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* File-based cache for workflow model discovery results and user selection.
|
|
187
|
+
*
|
|
188
|
+
* A single cache file is stored at
|
|
189
|
+
* `~/.cache/opencode/gitlab-workflow-model-cache.json`
|
|
190
|
+
* (or `$XDG_CACHE_HOME/opencode/...` when set).
|
|
191
|
+
*
|
|
192
|
+
* The file contains a JSON object keyed by a truncated SHA-256 hash of the
|
|
193
|
+
* workspace directory path combined with the GitLab instance URL. This ensures
|
|
194
|
+
* that switching instances for the same workspace invalidates the cache.
|
|
195
|
+
* Each value holds:
|
|
196
|
+
* - `discovery`: The full DiscoveredModels JSON from the last successful discovery
|
|
197
|
+
* - `selectedModelRef`: The model ref the user last selected
|
|
198
|
+
* - `selectedModelName`: Human-readable name of the selected model
|
|
199
|
+
* - `updatedAt`: ISO timestamp of the last write
|
|
200
|
+
*/
|
|
201
|
+
|
|
202
|
+
interface ModelCacheEntry {
|
|
203
|
+
discovery: DiscoveredModels | null;
|
|
204
|
+
selectedModelRef: string | null;
|
|
205
|
+
selectedModelName: string | null;
|
|
206
|
+
updatedAt: string;
|
|
207
|
+
}
|
|
208
|
+
declare class GitLabModelCache {
|
|
209
|
+
private readonly filePath;
|
|
210
|
+
private readonly key;
|
|
211
|
+
constructor(workDir: string, instanceUrl?: string);
|
|
212
|
+
private readAll;
|
|
213
|
+
private writeAll;
|
|
214
|
+
/**
|
|
215
|
+
* Load the cached entry for this workspace.
|
|
216
|
+
* Returns null if no cache exists or is unreadable.
|
|
217
|
+
*/
|
|
218
|
+
load(): ModelCacheEntry | null;
|
|
219
|
+
/**
|
|
220
|
+
* Persist the full cache entry to disk.
|
|
221
|
+
*/
|
|
222
|
+
save(entry: ModelCacheEntry): void;
|
|
223
|
+
/**
|
|
224
|
+
* Update only the discovery portion of the cache, preserving selection.
|
|
225
|
+
*/
|
|
226
|
+
saveDiscovery(discovery: DiscoveredModels): void;
|
|
227
|
+
/**
|
|
228
|
+
* Update only the selected model, preserving the discovery data.
|
|
229
|
+
*/
|
|
230
|
+
saveSelection(ref: string | null, name: string | null): void;
|
|
231
|
+
/**
|
|
232
|
+
* Remove the entry for this workspace from the cache file.
|
|
233
|
+
*/
|
|
234
|
+
clear(): void;
|
|
235
|
+
/**
|
|
236
|
+
* Convenience: get the cached selected model ref (or null).
|
|
237
|
+
*/
|
|
238
|
+
getSelectedModelRef(): string | null;
|
|
239
|
+
/**
|
|
240
|
+
* Convenience: get the cached selected model name (or null).
|
|
241
|
+
*/
|
|
242
|
+
getSelectedModelName(): string | null;
|
|
243
|
+
/**
|
|
244
|
+
* Convenience: get the cached discovery result (or null).
|
|
245
|
+
*/
|
|
246
|
+
getDiscovery(): DiscoveredModels | null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Types for the GitLab Duo Agent Platform (DWS) protocol.
|
|
251
|
+
*
|
|
252
|
+
* DWS uses a WebSocket-based bidirectional protocol where:
|
|
253
|
+
* - Client sends `ClientEvent` messages (startRequest, actionResponse, stopWorkflow, heartbeat)
|
|
254
|
+
* - Server sends `WorkflowAction` messages (newCheckpoint, runMcpTool, built-in tools)
|
|
255
|
+
*
|
|
256
|
+
* Message format is JSON-serialized protobuf with camelCase field names.
|
|
257
|
+
*/
|
|
258
|
+
interface GenerateTokenResponse {
|
|
259
|
+
gitlab_rails: {
|
|
260
|
+
base_url: string;
|
|
261
|
+
token: string;
|
|
262
|
+
token_expires_at: string;
|
|
263
|
+
};
|
|
264
|
+
duo_workflow_service: {
|
|
265
|
+
base_url: string;
|
|
266
|
+
token: string;
|
|
267
|
+
secure: boolean;
|
|
268
|
+
token_expires_at: number;
|
|
269
|
+
headers: Record<string, string>;
|
|
270
|
+
};
|
|
271
|
+
duo_workflow_executor: {
|
|
272
|
+
executor_binary_url: string;
|
|
273
|
+
version: string;
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
interface McpToolDefinition {
|
|
277
|
+
name: string;
|
|
278
|
+
description: string;
|
|
279
|
+
inputSchema: string;
|
|
280
|
+
}
|
|
281
|
+
interface AdditionalContext {
|
|
282
|
+
category: string;
|
|
283
|
+
id?: string;
|
|
284
|
+
content?: string;
|
|
285
|
+
metadata?: string;
|
|
286
|
+
}
|
|
287
|
+
interface StartRequest {
|
|
288
|
+
workflowID: string;
|
|
289
|
+
clientVersion: string;
|
|
290
|
+
workflowDefinition: string;
|
|
291
|
+
goal: string;
|
|
292
|
+
workflowMetadata?: string;
|
|
293
|
+
additional_context?: AdditionalContext[];
|
|
294
|
+
clientCapabilities?: string[];
|
|
295
|
+
mcpTools?: McpToolDefinition[];
|
|
296
|
+
preapproved_tools?: string[];
|
|
297
|
+
flowConfig?: unknown;
|
|
298
|
+
flowConfigSchemaVersion?: string;
|
|
299
|
+
}
|
|
300
|
+
interface ActionResponsePayload {
|
|
301
|
+
requestID: string;
|
|
302
|
+
plainTextResponse: {
|
|
303
|
+
response: string;
|
|
304
|
+
error: string | null;
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
interface StopWorkflow {
|
|
308
|
+
reason: string;
|
|
309
|
+
}
|
|
310
|
+
interface Heartbeat {
|
|
311
|
+
timestamp: number;
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Client → Server event union.
|
|
315
|
+
* Exactly one of the fields should be set per message.
|
|
316
|
+
*/
|
|
317
|
+
type ClientEvent =
|
|
318
|
+
| {
|
|
319
|
+
startRequest: StartRequest;
|
|
320
|
+
}
|
|
321
|
+
| {
|
|
322
|
+
actionResponse: ActionResponsePayload;
|
|
323
|
+
}
|
|
324
|
+
| {
|
|
325
|
+
stopWorkflow: StopWorkflow;
|
|
326
|
+
}
|
|
327
|
+
| {
|
|
328
|
+
heartbeat: Heartbeat;
|
|
329
|
+
};
|
|
330
|
+
type WorkflowStatus =
|
|
331
|
+
| 'CREATED'
|
|
332
|
+
| 'RUNNING'
|
|
333
|
+
| 'FINISHED'
|
|
334
|
+
| 'COMPLETED'
|
|
335
|
+
| 'FAILED'
|
|
336
|
+
| 'STOPPED'
|
|
337
|
+
| 'CANCELLED'
|
|
338
|
+
| 'PAUSED'
|
|
339
|
+
| 'INPUT_REQUIRED'
|
|
340
|
+
| 'PLAN_APPROVAL_REQUIRED'
|
|
341
|
+
| 'TOOL_CALL_APPROVAL_REQUIRED'
|
|
342
|
+
| 'UNKNOWN';
|
|
343
|
+
interface NewCheckpoint {
|
|
344
|
+
status: WorkflowStatus;
|
|
345
|
+
goal?: string;
|
|
346
|
+
/** Raw checkpoint JSON string from DWS (contains channel_values.ui_chat_log) */
|
|
347
|
+
checkpoint?: string;
|
|
348
|
+
/** Legacy content field (may be empty — text is in checkpoint.ui_chat_log) */
|
|
349
|
+
content?: string;
|
|
350
|
+
}
|
|
351
|
+
interface RunMcpTool {
|
|
352
|
+
name: string;
|
|
353
|
+
args: string;
|
|
354
|
+
}
|
|
355
|
+
interface ReadFileAction {
|
|
356
|
+
filepath: string;
|
|
357
|
+
}
|
|
358
|
+
interface ReadFilesAction {
|
|
359
|
+
filepaths: string[];
|
|
360
|
+
}
|
|
361
|
+
interface WriteFileAction {
|
|
362
|
+
filepath: string;
|
|
363
|
+
contents: string;
|
|
364
|
+
}
|
|
365
|
+
interface ShellCommandAction {
|
|
366
|
+
command: string;
|
|
367
|
+
}
|
|
368
|
+
interface EditFileAction {
|
|
369
|
+
filepath: string;
|
|
370
|
+
oldString: string;
|
|
371
|
+
newString: string;
|
|
372
|
+
}
|
|
373
|
+
interface ListDirectoryAction {
|
|
374
|
+
directory: string;
|
|
375
|
+
}
|
|
376
|
+
interface FindFilesAction {
|
|
377
|
+
name_pattern: string;
|
|
378
|
+
}
|
|
379
|
+
interface GrepAction {
|
|
380
|
+
pattern: string;
|
|
381
|
+
search_directory?: string;
|
|
382
|
+
case_insensitive?: boolean;
|
|
383
|
+
}
|
|
384
|
+
interface MkdirAction {
|
|
385
|
+
directory_path: string;
|
|
386
|
+
}
|
|
387
|
+
interface RunCommandAction {
|
|
388
|
+
program: string;
|
|
389
|
+
flags?: string[];
|
|
390
|
+
arguments?: string[];
|
|
391
|
+
}
|
|
392
|
+
interface RunGitCommandAction {
|
|
393
|
+
command: string;
|
|
394
|
+
arguments?: string[];
|
|
395
|
+
repository_url?: string;
|
|
396
|
+
}
|
|
397
|
+
interface GitLabApiRequestAction {
|
|
398
|
+
method: string;
|
|
399
|
+
path: string;
|
|
400
|
+
body?: string;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Server → Client action union.
|
|
404
|
+
* Field names match the DWS protobuf camelCase wire format exactly.
|
|
405
|
+
* Each message has an optional `requestID` (required for tool invocations
|
|
406
|
+
* that need an `actionResponse` back).
|
|
407
|
+
*/
|
|
408
|
+
interface WorkflowAction {
|
|
409
|
+
requestID?: string;
|
|
410
|
+
newCheckpoint?: NewCheckpoint;
|
|
411
|
+
runMCPTool?: RunMcpTool;
|
|
412
|
+
runReadFile?: ReadFileAction;
|
|
413
|
+
runReadFiles?: ReadFilesAction;
|
|
414
|
+
runWriteFile?: WriteFileAction;
|
|
415
|
+
runShellCommand?: ShellCommandAction;
|
|
416
|
+
runEditFile?: EditFileAction;
|
|
417
|
+
listDirectory?: ListDirectoryAction;
|
|
418
|
+
findFiles?: FindFilesAction;
|
|
419
|
+
grep?: GrepAction;
|
|
420
|
+
mkdir?: MkdirAction;
|
|
421
|
+
runCommand?: RunCommandAction;
|
|
422
|
+
runGitCommand?: RunGitCommandAction;
|
|
423
|
+
runHTTPRequest?: GitLabApiRequestAction;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Options for creating a workflow chat model via `provider.workflowChat()`.
|
|
427
|
+
*/
|
|
428
|
+
interface GitLabWorkflowOptions {
|
|
429
|
+
/**
|
|
430
|
+
* Workflow definition type.
|
|
431
|
+
* @default 'chat'
|
|
432
|
+
*/
|
|
433
|
+
workflowDefinition?: string;
|
|
434
|
+
/**
|
|
435
|
+
* Root namespace ID for the GitLab group/project.
|
|
436
|
+
* Used for token scoping and model discovery.
|
|
437
|
+
*/
|
|
438
|
+
rootNamespaceId?: string;
|
|
439
|
+
/**
|
|
440
|
+
* GitLab project ID (numeric or path).
|
|
441
|
+
* Sent as `x-gitlab-project-id` header on WebSocket.
|
|
442
|
+
*/
|
|
443
|
+
projectId?: string;
|
|
444
|
+
/**
|
|
445
|
+
* GitLab namespace ID.
|
|
446
|
+
* Sent as `x-gitlab-namespace-id` header on WebSocket.
|
|
447
|
+
*/
|
|
448
|
+
namespaceId?: string;
|
|
449
|
+
/**
|
|
450
|
+
* MCP tool definitions to expose to the workflow.
|
|
451
|
+
* These are sent in `startRequest.mcpTools`.
|
|
452
|
+
*/
|
|
453
|
+
mcpTools?: McpToolDefinition[];
|
|
454
|
+
/**
|
|
455
|
+
* Client capabilities to advertise.
|
|
456
|
+
* @default ['shell_command']
|
|
457
|
+
*/
|
|
458
|
+
clientCapabilities?: string[];
|
|
459
|
+
/**
|
|
460
|
+
* Tool names that are pre-approved for execution without user confirmation.
|
|
461
|
+
* Sent in `startRequest.preapproved_tools`.
|
|
462
|
+
*/
|
|
463
|
+
preapprovedTools?: string[];
|
|
464
|
+
/**
|
|
465
|
+
* Additional context items to send with the first request.
|
|
466
|
+
* Used for conversation history continuity.
|
|
467
|
+
*/
|
|
468
|
+
additionalContext?: AdditionalContext[];
|
|
469
|
+
/**
|
|
470
|
+
* Feature flags to pass to the GitLab API.
|
|
471
|
+
*/
|
|
472
|
+
featureFlags?: Record<string, boolean>;
|
|
473
|
+
/**
|
|
474
|
+
* Working directory for auto-detecting GitLab project from git remote.
|
|
475
|
+
* Used to resolve `projectId` when not explicitly set.
|
|
476
|
+
* Defaults to `process.cwd()`.
|
|
477
|
+
*/
|
|
478
|
+
workingDirectory?: string;
|
|
479
|
+
/**
|
|
480
|
+
* Flow configuration (parsed YAML object) to send to DWS.
|
|
481
|
+
* Controls agent behavior, intermediate text generation, etc.
|
|
482
|
+
* Sent as `startRequest.flowConfig`.
|
|
483
|
+
*/
|
|
484
|
+
flowConfig?: unknown;
|
|
485
|
+
/**
|
|
486
|
+
* Schema version for the flow configuration.
|
|
487
|
+
* Sent as `startRequest.flowConfigSchemaVersion`.
|
|
488
|
+
*/
|
|
489
|
+
flowConfigSchemaVersion?: string;
|
|
490
|
+
/**
|
|
491
|
+
* Callback invoked when multiple workflow models are available for the
|
|
492
|
+
* workspace and model switching is enabled by the instance admin.
|
|
493
|
+
*
|
|
494
|
+
* The provider calls this before starting the workflow so the host can
|
|
495
|
+
* present a model picker to the user. Return the `ref` of the chosen
|
|
496
|
+
* model, or `null`/`undefined` to fall back to the workspace default.
|
|
497
|
+
*
|
|
498
|
+
* If the returned ref is not in the list of selectable models it is
|
|
499
|
+
* ignored and the workspace default is used instead.
|
|
500
|
+
*
|
|
501
|
+
* Not called when the admin has pinned a model — in that case the
|
|
502
|
+
* pinned model is always used regardless of user preference.
|
|
503
|
+
*
|
|
504
|
+
* @param models - List of models the user can select from
|
|
505
|
+
* @returns The selected model ref, or null/undefined for default
|
|
506
|
+
*/
|
|
507
|
+
onSelectModel?: (models: AiModel[]) => Promise<string | null | undefined>;
|
|
508
|
+
}
|
|
509
|
+
interface GitLabWorkflowClientConfig {
|
|
510
|
+
/** GitLab instance URL (e.g., 'https://gitlab.com') */
|
|
511
|
+
instanceUrl: string;
|
|
512
|
+
/** Function to get current auth headers */
|
|
513
|
+
getHeaders: () => Record<string, string>;
|
|
514
|
+
/**
|
|
515
|
+
* Optional callback to refresh the API key when a 401 error occurs.
|
|
516
|
+
*/
|
|
517
|
+
refreshApiKey?: () => Promise<void>;
|
|
518
|
+
/** Custom fetch implementation */
|
|
519
|
+
fetch?: typeof fetch;
|
|
520
|
+
/** Feature flags for the token request */
|
|
521
|
+
featureFlags?: Record<string, boolean>;
|
|
522
|
+
/**
|
|
523
|
+
* AI Gateway URL.
|
|
524
|
+
* Can also be set via GITLAB_AI_GATEWAY_URL environment variable.
|
|
525
|
+
* @default 'https://cloud.gitlab.com'
|
|
526
|
+
*/
|
|
527
|
+
aiGatewayUrl?: string;
|
|
528
|
+
}
|
|
529
|
+
type WorkflowClientEvent =
|
|
530
|
+
| {
|
|
531
|
+
type: 'checkpoint';
|
|
532
|
+
data: NewCheckpoint;
|
|
533
|
+
}
|
|
534
|
+
| {
|
|
535
|
+
type: 'tool-request';
|
|
536
|
+
requestID: string;
|
|
537
|
+
data: RunMcpTool;
|
|
538
|
+
}
|
|
539
|
+
| {
|
|
540
|
+
type: 'builtin-tool-request';
|
|
541
|
+
requestID: string;
|
|
542
|
+
toolName: string;
|
|
543
|
+
data: Record<string, unknown>;
|
|
544
|
+
}
|
|
545
|
+
| {
|
|
546
|
+
type: 'completed';
|
|
547
|
+
}
|
|
548
|
+
| {
|
|
549
|
+
type: 'failed';
|
|
550
|
+
error: Error;
|
|
551
|
+
}
|
|
552
|
+
| {
|
|
553
|
+
type: 'closed';
|
|
554
|
+
code: number;
|
|
555
|
+
reason: string;
|
|
556
|
+
};
|
|
557
|
+
/**
|
|
558
|
+
* Workflow type enum — matches gitlab-lsp WorkflowType.
|
|
559
|
+
*
|
|
560
|
+
* - CHAT: Shared token across sessions, tokens NOT revoked on completion
|
|
561
|
+
* - SOFTWARE_DEVELOPMENT: Per-workflow token, tokens revoked on completion
|
|
562
|
+
*/
|
|
563
|
+
declare enum WorkflowType {
|
|
564
|
+
CHAT = 'chat',
|
|
565
|
+
SOFTWARE_DEVELOPMENT = 'software_development',
|
|
566
|
+
}
|
|
567
|
+
/** WebSocket ping interval (TCP keepalive) — 45s matching gitlab-lsp */
|
|
568
|
+
declare const WS_KEEPALIVE_PING_INTERVAL_MS = 45000;
|
|
569
|
+
/** Application heartbeat interval — 60s matching gitlab-lsp */
|
|
570
|
+
declare const WS_HEARTBEAT_INTERVAL_MS = 60000;
|
|
571
|
+
/** Default workflow definition — uses CHAT for agentic chat (matches gitlab-lsp) */
|
|
572
|
+
declare const DEFAULT_WORKFLOW_DEFINITION = WorkflowType.CHAT;
|
|
573
|
+
/** Default client capabilities */
|
|
574
|
+
declare const DEFAULT_CLIENT_CAPABILITIES: string[];
|
|
575
|
+
/** Client version sent in startRequest */
|
|
576
|
+
declare const CLIENT_VERSION = '1.0';
|
|
577
|
+
/**
|
|
578
|
+
* Agent privileges for workflow creation.
|
|
579
|
+
* Matches gitlab-lsp AGENT_PRIVILEGES enum.
|
|
580
|
+
*/
|
|
581
|
+
declare const AGENT_PRIVILEGES: {
|
|
582
|
+
readonly READ_WRITE_FILES: 1;
|
|
583
|
+
readonly READ_ONLY_GITLAB: 2;
|
|
584
|
+
readonly READ_WRITE_GITLAB: 3;
|
|
585
|
+
readonly RUN_COMMANDS: 4;
|
|
586
|
+
readonly USE_GIT: 5;
|
|
587
|
+
readonly RUN_MCP_TOOLS: 6;
|
|
588
|
+
};
|
|
589
|
+
/** Default agent privileges — matches gitlab-lsp defaults */
|
|
590
|
+
declare const DEFAULT_AGENT_PRIVILEGES: (1 | 2 | 4 | 3 | 5 | 6)[];
|
|
591
|
+
/** Workflow execution environment */
|
|
592
|
+
declare const WORKFLOW_ENVIRONMENT: 'ide';
|
|
593
|
+
|
|
594
|
+
interface GitLabWorkflowLanguageModelConfig extends GitLabWorkflowClientConfig {
|
|
595
|
+
/** Provider name (e.g., 'gitlab.workflow') */
|
|
596
|
+
provider: string;
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Callback for executing a tool requested by DWS.
|
|
600
|
+
* Provided by the consumer (e.g., OpenCode) to bridge DWS tool requests
|
|
601
|
+
* to the host's tool execution system.
|
|
602
|
+
*
|
|
603
|
+
* @param toolName - Name of the tool DWS wants to execute
|
|
604
|
+
* @param args - JSON-encoded arguments
|
|
605
|
+
* @returns Tool execution result as a string, or throws on error
|
|
606
|
+
*/
|
|
607
|
+
type WorkflowToolExecutor = (
|
|
608
|
+
toolName: string,
|
|
609
|
+
args: string,
|
|
610
|
+
requestID: string
|
|
611
|
+
) => Promise<{
|
|
612
|
+
result: string;
|
|
613
|
+
error?: string | null;
|
|
614
|
+
}>;
|
|
615
|
+
/**
|
|
616
|
+
* GitLab Duo Agent Platform Language Model.
|
|
617
|
+
*
|
|
618
|
+
* Implements LanguageModelV2 by bridging the DWS WebSocket protocol
|
|
619
|
+
* to the Vercel AI SDK stream part format.
|
|
620
|
+
*/
|
|
621
|
+
declare class GitLabWorkflowLanguageModel implements LanguageModelV2 {
|
|
622
|
+
readonly specificationVersion: 'v2';
|
|
623
|
+
readonly modelId: string;
|
|
624
|
+
readonly supportedUrls: Record<string, RegExp[]>;
|
|
625
|
+
private readonly config;
|
|
626
|
+
private readonly workflowOptions;
|
|
627
|
+
private readonly tokenClient;
|
|
628
|
+
private readonly projectDetector;
|
|
629
|
+
private readonly modelDiscovery;
|
|
630
|
+
private readonly modelCache;
|
|
631
|
+
private detectedProjectPath;
|
|
632
|
+
private currentWorkflowId;
|
|
633
|
+
private persistedAgentEmitted;
|
|
634
|
+
private readonly activeClients;
|
|
635
|
+
private _selectedModelRef?;
|
|
636
|
+
private _selectedModelName?;
|
|
637
|
+
private _rootNamespaceId?;
|
|
638
|
+
private _discoveryPromise?;
|
|
639
|
+
/**
|
|
640
|
+
* Get the cached selected model ref.
|
|
641
|
+
*/
|
|
642
|
+
get selectedModelRef(): string | null;
|
|
643
|
+
/**
|
|
644
|
+
* Set the selected model ref (e.g., from an eager discover call).
|
|
645
|
+
* This will be used by resolveModelRef() to skip the picker.
|
|
646
|
+
* Also persists to the file-based workspace cache.
|
|
647
|
+
*/
|
|
648
|
+
set selectedModelRef(ref: string | null);
|
|
649
|
+
/**
|
|
650
|
+
* Get the cached selected model display name.
|
|
651
|
+
*/
|
|
652
|
+
get selectedModelName(): string | null;
|
|
653
|
+
/**
|
|
654
|
+
* Set the selected model display name.
|
|
655
|
+
* Also persists to the file-based workspace cache.
|
|
656
|
+
*/
|
|
657
|
+
set selectedModelName(name: string | null);
|
|
658
|
+
/**
|
|
659
|
+
* Optional external tool executor. When set, this is called for tool
|
|
660
|
+
* requests instead of looking up tools from `options.tools`.
|
|
661
|
+
* This allows the consumer (OpenCode) to wire in its permission system.
|
|
662
|
+
*
|
|
663
|
+
* The executor is automatically bound to the async context at the time
|
|
664
|
+
* it is set, so that AsyncLocalStorage-based contexts (like Instance)
|
|
665
|
+
* remain available when the executor is invoked from WebSocket callbacks.
|
|
666
|
+
*/
|
|
667
|
+
private _toolExecutor;
|
|
668
|
+
/**
|
|
669
|
+
* Optional callback invoked with intermediate token usage estimates
|
|
670
|
+
* after each tool execution completes. This allows the consumer to
|
|
671
|
+
* display live token counts during long-running DWS workflows, since
|
|
672
|
+
* the AI SDK only surfaces usage via finish-step at stream end.
|
|
673
|
+
*/
|
|
674
|
+
onUsageUpdate: ((usage: { inputTokens: number; outputTokens: number }) => void) | null;
|
|
675
|
+
/**
|
|
676
|
+
* Optional callback invoked when multiple workflow models are available
|
|
677
|
+
* and the user should pick one. Set per-stream by the host (e.g., OpenCode)
|
|
678
|
+
* alongside `toolExecutor`. Takes precedence over `workflowOptions.onSelectModel`.
|
|
679
|
+
*/
|
|
680
|
+
onSelectModel: ((models: AiModel[]) => Promise<string | null | undefined>) | null;
|
|
681
|
+
get toolExecutor(): WorkflowToolExecutor | null;
|
|
682
|
+
set toolExecutor(executor: WorkflowToolExecutor | null);
|
|
683
|
+
constructor(
|
|
684
|
+
modelId: string,
|
|
685
|
+
config: GitLabWorkflowLanguageModelConfig,
|
|
686
|
+
workflowOptions?: GitLabWorkflowOptions
|
|
687
|
+
);
|
|
688
|
+
get provider(): string;
|
|
689
|
+
/**
|
|
690
|
+
* Resolve the project ID (path) to use for workflow creation.
|
|
691
|
+
* Priority: explicit option > auto-detected from git remote > undefined.
|
|
692
|
+
*/
|
|
693
|
+
private resolveProjectId;
|
|
694
|
+
/**
|
|
695
|
+
* Resolve the root namespace GID to use for model discovery.
|
|
696
|
+
*
|
|
697
|
+
* Priority:
|
|
698
|
+
* 1. Explicit `rootNamespaceId` in workflowOptions (caller-provided GID)
|
|
699
|
+
* 2. Auto-detected from git remote via project detector (namespace.id → GID)
|
|
700
|
+
* 3. Cached from previous call
|
|
701
|
+
*/
|
|
702
|
+
private resolveRootNamespaceId;
|
|
703
|
+
/**
|
|
704
|
+
* Resolve the effective DWS model ref to use for this stream.
|
|
705
|
+
* Deduplicates concurrent calls via a shared promise.
|
|
706
|
+
*
|
|
707
|
+
* Priority for the canonical `duo-workflow` model ID:
|
|
708
|
+
* 1. Admin-pinned model (from GitLabModelDiscovery) — always wins
|
|
709
|
+
* 2. User selection via onSelectModel callback (if model switching enabled)
|
|
710
|
+
* 3. Workspace default model
|
|
711
|
+
* 4. File-cached discovery/selection — used when live discovery fails
|
|
712
|
+
* 5. Hard-coded 'default' (DWS decides) — fallback when discovery fails
|
|
713
|
+
*
|
|
714
|
+
* For all other `duo-workflow-*` model IDs the static mapping is used as-is.
|
|
715
|
+
*/
|
|
716
|
+
private resolveModelRef;
|
|
717
|
+
private doResolveModelRef;
|
|
718
|
+
/**
|
|
719
|
+
* Pre-fetch available models for the workspace.
|
|
720
|
+
* Call this early (e.g., on IDE startup) to avoid blocking the first stream.
|
|
721
|
+
* Results are persisted to the workspace model cache.
|
|
722
|
+
*
|
|
723
|
+
* @param rootNamespaceId - GitLab group ID (e.g., 'gid://gitlab/Group/12345')
|
|
724
|
+
* @returns Discovered models with default, selectable, and pinned models
|
|
725
|
+
*/
|
|
726
|
+
discoverModels(rootNamespaceId: string): Promise<DiscoveredModels>;
|
|
727
|
+
/**
|
|
728
|
+
* Get the file-based model cache instance for this workspace.
|
|
729
|
+
* Useful for consumers that need direct cache access (e.g., the discover route).
|
|
730
|
+
*/
|
|
731
|
+
getModelCache(): GitLabModelCache;
|
|
732
|
+
/**
|
|
733
|
+
* Stop the active workflow.
|
|
734
|
+
*/
|
|
735
|
+
stopWorkflow(): void;
|
|
736
|
+
/**
|
|
737
|
+
* Reset the workflow state, forcing a new workflow to be created on the
|
|
738
|
+
* next doStream() call. Call this when starting a new conversation.
|
|
739
|
+
*/
|
|
740
|
+
resetWorkflow(): void;
|
|
741
|
+
/**
|
|
742
|
+
* Get the current workflow ID (if any).
|
|
743
|
+
* Useful for consumers that need to track workflow state.
|
|
744
|
+
*/
|
|
745
|
+
get workflowId(): string | null;
|
|
746
|
+
doGenerate(options: LanguageModelV2CallOptions): Promise<{
|
|
747
|
+
content: LanguageModelV2Content[];
|
|
748
|
+
finishReason: LanguageModelV2FinishReason;
|
|
749
|
+
usage: LanguageModelV2Usage;
|
|
750
|
+
warnings: LanguageModelV2CallWarning[];
|
|
751
|
+
}>;
|
|
752
|
+
doStream(options: LanguageModelV2CallOptions): Promise<{
|
|
753
|
+
stream: ReadableStream<LanguageModelV2StreamPart>;
|
|
754
|
+
request?: {
|
|
755
|
+
body?: unknown;
|
|
756
|
+
};
|
|
757
|
+
}>;
|
|
758
|
+
private handleWorkflowEvent;
|
|
759
|
+
private processCheckpoint;
|
|
760
|
+
private executeToolAndRespond;
|
|
761
|
+
private cleanupClient;
|
|
762
|
+
private buildWorkflowMetadata;
|
|
763
|
+
private getGitInfo;
|
|
764
|
+
/**
|
|
765
|
+
* Extract the user's goal (last user message) from the AI SDK prompt.
|
|
766
|
+
*/
|
|
767
|
+
private extractGoalFromPrompt;
|
|
768
|
+
/**
|
|
769
|
+
* Convert AI SDK tools to DWS McpToolDefinition format.
|
|
770
|
+
*/
|
|
771
|
+
private extractMcpTools;
|
|
772
|
+
private static readonly MAX_START_REQUEST_BYTES;
|
|
773
|
+
/**
|
|
774
|
+
* Trim mcpTools and additionalContext to fit within the DWS 4MB gRPC
|
|
775
|
+
* message size limit (`MAX_MESSAGE_SIZE` in duo_workflow_service/server.py).
|
|
776
|
+
*
|
|
777
|
+
* DWS has no per-field limits on tool descriptions, schemas, or context items.
|
|
778
|
+
* The only hard constraint is the total serialized message size.
|
|
779
|
+
*
|
|
780
|
+
* Strategy (progressive, only if over budget):
|
|
781
|
+
* 1. Send everything as-is
|
|
782
|
+
* 2. Simplify tool input schemas (strip descriptions from properties)
|
|
783
|
+
* 3. Strip schemas to minimal form (type + property names only)
|
|
784
|
+
* 4. Drop tools from the end until it fits
|
|
785
|
+
*/
|
|
786
|
+
private trimPayload;
|
|
787
|
+
private buildAdditionalContext;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
interface GitLabProvider {
|
|
791
|
+
(modelId: string): LanguageModelV2;
|
|
792
|
+
readonly specificationVersion: 'v2';
|
|
793
|
+
languageModel(modelId: string): LanguageModelV2;
|
|
794
|
+
chat(modelId: string): LanguageModelV2;
|
|
795
|
+
/**
|
|
796
|
+
* Create an agentic chat model with tool calling support
|
|
797
|
+
*
|
|
798
|
+
* @param modelId - GitLab model identifier. Some IDs automatically map to specific Anthropic models.
|
|
799
|
+
* @param options - Configuration options for the agentic model
|
|
800
|
+
* @returns A language model with native tool calling support via Anthropic
|
|
801
|
+
*
|
|
802
|
+
* @example
|
|
803
|
+
* // Automatic model mapping
|
|
804
|
+
* const model = gitlab.agenticChat('duo-chat-opus-4-5');
|
|
805
|
+
* // Uses claude-opus-4-5-20251101
|
|
806
|
+
*
|
|
807
|
+
* @example
|
|
808
|
+
* // Explicit model override
|
|
809
|
+
* const model = gitlab.agenticChat('duo-chat', {
|
|
810
|
+
* anthropicModel: 'claude-sonnet-4-5-20250929'
|
|
811
|
+
* });
|
|
812
|
+
*/
|
|
813
|
+
agenticChat(modelId: string, options?: GitLabAgenticOptions): GitLabAnthropicLanguageModel;
|
|
814
|
+
/**
|
|
815
|
+
* Create a workflow chat model using GitLab Duo Agent Platform.
|
|
816
|
+
*
|
|
817
|
+
* Workflow models use a server-side agentic loop where GitLab's DWS drives
|
|
818
|
+
* the LLM, requests tool executions from the client via WebSocket, and
|
|
819
|
+
* streams text/status back.
|
|
820
|
+
*
|
|
821
|
+
* Requires GitLab Ultimate with Duo Enterprise add-on.
|
|
822
|
+
*
|
|
823
|
+
* @param modelId - Workflow model identifier (e.g., 'duo-workflow-sonnet-4-6')
|
|
824
|
+
* @param options - Workflow-specific configuration
|
|
825
|
+
* @returns A language model backed by the DWS WebSocket protocol
|
|
826
|
+
*
|
|
827
|
+
* @example
|
|
828
|
+
* const model = gitlab.workflowChat('duo-workflow-sonnet-4-6', {
|
|
829
|
+
* mcpTools: [...],
|
|
830
|
+
* preapprovedTools: ['read_file', 'write_file'],
|
|
831
|
+
* });
|
|
832
|
+
*/
|
|
833
|
+
workflowChat(modelId: string, options?: GitLabWorkflowOptions): GitLabWorkflowLanguageModel;
|
|
834
|
+
textEmbeddingModel(modelId: string): never;
|
|
835
|
+
imageModel(modelId: string): never;
|
|
836
|
+
}
|
|
837
|
+
interface GitLabAgenticOptions {
|
|
838
|
+
/**
|
|
839
|
+
* Override the provider-specific model (optional).
|
|
840
|
+
* Must be a valid model for the detected provider.
|
|
841
|
+
*
|
|
842
|
+
* For Anthropic models:
|
|
843
|
+
* - 'claude-opus-4-6'
|
|
844
|
+
* - 'claude-sonnet-4-6'
|
|
845
|
+
* - 'claude-opus-4-5-20251101'
|
|
846
|
+
* - 'claude-sonnet-4-5-20250929'
|
|
847
|
+
* - 'claude-haiku-4-5-20251001'
|
|
848
|
+
*
|
|
849
|
+
* For OpenAI models:
|
|
850
|
+
* - 'gpt-5.1-2025-11-13'
|
|
851
|
+
* - 'gpt-5-mini-2025-08-07'
|
|
852
|
+
* - 'gpt-5-codex'
|
|
853
|
+
* - 'gpt-5.2-codex'
|
|
854
|
+
*
|
|
855
|
+
* @example
|
|
856
|
+
* // Override with explicit model
|
|
857
|
+
* const model = gitlab.agenticChat('duo-chat-opus-4-5', {
|
|
858
|
+
* providerModel: 'claude-sonnet-4-5-20250929'
|
|
859
|
+
* });
|
|
860
|
+
*/
|
|
861
|
+
providerModel?: string;
|
|
862
|
+
/**
|
|
863
|
+
* Maximum tokens to generate
|
|
864
|
+
* @default 8192
|
|
865
|
+
*/
|
|
866
|
+
maxTokens?: number;
|
|
867
|
+
/**
|
|
868
|
+
* Feature flags to pass to the GitLab API
|
|
869
|
+
*/
|
|
870
|
+
featureFlags?: Record<string, boolean>;
|
|
871
|
+
/**
|
|
872
|
+
* Custom headers for AI Gateway requests (per-model override).
|
|
873
|
+
* These headers are sent to the Anthropic/OpenAI proxy endpoints.
|
|
874
|
+
* Merged with provider-level aiGatewayHeaders (model-level takes precedence).
|
|
875
|
+
*/
|
|
876
|
+
aiGatewayHeaders?: Record<string, string>;
|
|
877
|
+
}
|
|
878
|
+
interface GitLabProviderSettings {
|
|
879
|
+
/**
|
|
880
|
+
* GitLab instance URL (e.g., 'https://gitlab.com')
|
|
881
|
+
* Can also be set via GITLAB_INSTANCE_URL environment variable.
|
|
882
|
+
* @default 'https://gitlab.com'
|
|
883
|
+
*/
|
|
884
|
+
instanceUrl?: string;
|
|
885
|
+
/**
|
|
886
|
+
* API token (Personal Access Token or OAuth access token)
|
|
887
|
+
* Can also be set via GITLAB_TOKEN environment variable
|
|
888
|
+
*/
|
|
889
|
+
apiKey?: string;
|
|
890
|
+
/**
|
|
891
|
+
* OAuth refresh token (optional, for OAuth flow)
|
|
892
|
+
*/
|
|
893
|
+
refreshToken?: string;
|
|
894
|
+
/**
|
|
895
|
+
* OAuth client ID (required for OAuth flow)
|
|
896
|
+
*/
|
|
897
|
+
clientId?: string;
|
|
898
|
+
/**
|
|
899
|
+
* OAuth redirect URI (required for OAuth flow)
|
|
900
|
+
*/
|
|
901
|
+
redirectUri?: string;
|
|
902
|
+
/**
|
|
903
|
+
* Custom headers to include in requests
|
|
904
|
+
*/
|
|
905
|
+
headers?: Record<string, string>;
|
|
906
|
+
/**
|
|
907
|
+
* Custom fetch implementation
|
|
908
|
+
*/
|
|
909
|
+
fetch?: typeof fetch;
|
|
910
|
+
/**
|
|
911
|
+
* Provider name override
|
|
912
|
+
*/
|
|
913
|
+
name?: string;
|
|
914
|
+
/**
|
|
915
|
+
* Default feature flags to pass to the GitLab API for all agentic chat models
|
|
916
|
+
*/
|
|
917
|
+
featureFlags?: Record<string, boolean>;
|
|
918
|
+
/**
|
|
919
|
+
* AI Gateway URL for the Anthropic proxy.
|
|
920
|
+
* Can also be set via GITLAB_AI_GATEWAY_URL environment variable.
|
|
921
|
+
* @default 'https://cloud.gitlab.com'
|
|
922
|
+
*/
|
|
923
|
+
aiGatewayUrl?: string;
|
|
924
|
+
/**
|
|
925
|
+
* Custom headers to include in AI Gateway requests (Anthropic/OpenAI proxy).
|
|
926
|
+
* These headers are merged with the default headers from direct_access response.
|
|
927
|
+
* Default User-Agent: gitlab-ai-provider/{version}
|
|
928
|
+
*/
|
|
929
|
+
aiGatewayHeaders?: Record<string, string>;
|
|
930
|
+
}
|
|
931
|
+
declare function createGitLab(options?: GitLabProviderSettings): GitLabProvider;
|
|
932
|
+
/**
|
|
933
|
+
* Default GitLab Duo provider instance
|
|
934
|
+
*
|
|
935
|
+
* @example
|
|
936
|
+
* ```typescript
|
|
937
|
+
* import { gitlab } from '@ai-sdk/gitlab';
|
|
938
|
+
*
|
|
939
|
+
* const model = gitlab('duo-chat');
|
|
940
|
+
* ```
|
|
941
|
+
*/
|
|
942
|
+
declare const gitlab: GitLabProvider;
|
|
943
|
+
|
|
944
|
+
declare const VERSION: string;
|
|
945
|
+
|
|
946
|
+
interface GitLabOpenAIConfig {
|
|
947
|
+
provider: string;
|
|
948
|
+
instanceUrl: string;
|
|
949
|
+
getHeaders: () => Record<string, string>;
|
|
950
|
+
fetch?: typeof fetch;
|
|
951
|
+
refreshApiKey?: () => Promise<void>;
|
|
952
|
+
openaiModel?: string;
|
|
953
|
+
maxTokens?: number;
|
|
954
|
+
featureFlags?: {
|
|
955
|
+
DuoAgentPlatformNext: true;
|
|
956
|
+
} & Record<string, boolean>;
|
|
957
|
+
aiGatewayUrl?: string;
|
|
958
|
+
/** Whether to use the Responses API instead of Chat Completions API */
|
|
959
|
+
useResponsesApi?: boolean;
|
|
960
|
+
/**
|
|
961
|
+
* Custom headers for AI Gateway OpenAI proxy requests.
|
|
962
|
+
* Merged with headers from direct_access token response.
|
|
963
|
+
*/
|
|
964
|
+
aiGatewayHeaders?: Record<string, string>;
|
|
965
|
+
}
|
|
966
|
+
declare class GitLabOpenAILanguageModel implements LanguageModelV2 {
|
|
967
|
+
readonly specificationVersion: 'v2';
|
|
968
|
+
readonly modelId: string;
|
|
969
|
+
readonly supportedUrls: Record<string, RegExp[]>;
|
|
970
|
+
private readonly config;
|
|
971
|
+
private readonly directAccessClient;
|
|
972
|
+
private readonly useResponsesApi;
|
|
973
|
+
private openaiClient;
|
|
974
|
+
constructor(modelId: string, config: GitLabOpenAIConfig);
|
|
975
|
+
get provider(): string;
|
|
976
|
+
private getOpenAIClient;
|
|
977
|
+
private isTokenError;
|
|
978
|
+
/**
|
|
979
|
+
* Check if an error is a context overflow error (prompt too long)
|
|
980
|
+
* These should NOT trigger token refresh and should be reported to the user.
|
|
981
|
+
*/
|
|
982
|
+
private isContextOverflowError;
|
|
983
|
+
private convertTools;
|
|
984
|
+
private convertToolChoice;
|
|
985
|
+
private convertPrompt;
|
|
986
|
+
private convertFinishReason;
|
|
987
|
+
/**
|
|
988
|
+
* Convert tools to Responses API format
|
|
989
|
+
*/
|
|
990
|
+
private convertToolsForResponses;
|
|
991
|
+
/**
|
|
992
|
+
* Convert prompt to Responses API input format
|
|
993
|
+
*/
|
|
994
|
+
private convertPromptForResponses;
|
|
995
|
+
/**
|
|
996
|
+
* Extract system instructions from prompt
|
|
997
|
+
*/
|
|
998
|
+
private extractSystemInstructions;
|
|
999
|
+
/**
|
|
1000
|
+
* Convert Responses API status to finish reason
|
|
1001
|
+
* Note: Responses API returns 'completed' even when making tool calls,
|
|
1002
|
+
* so we need to check the content for tool calls separately.
|
|
1003
|
+
*/
|
|
1004
|
+
private convertResponsesStatus;
|
|
1005
|
+
doGenerate(options: LanguageModelV2CallOptions): Promise<{
|
|
1006
|
+
content: LanguageModelV2Content[];
|
|
1007
|
+
finishReason: LanguageModelV2FinishReason;
|
|
1008
|
+
usage: LanguageModelV2Usage;
|
|
1009
|
+
warnings: LanguageModelV2CallWarning[];
|
|
1010
|
+
}>;
|
|
1011
|
+
private doGenerateWithChatApi;
|
|
1012
|
+
private doGenerateWithResponsesApi;
|
|
1013
|
+
doStream(options: LanguageModelV2CallOptions): Promise<{
|
|
1014
|
+
stream: ReadableStream<LanguageModelV2StreamPart>;
|
|
1015
|
+
request?: {
|
|
1016
|
+
body?: unknown;
|
|
1017
|
+
};
|
|
1018
|
+
response?: {
|
|
1019
|
+
headers?: Record<string, string>;
|
|
1020
|
+
};
|
|
1021
|
+
}>;
|
|
1022
|
+
private doStreamWithChatApi;
|
|
1023
|
+
private doStreamWithResponsesApi;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
type ModelProvider = 'anthropic' | 'openai' | 'workflow';
|
|
1027
|
+
type OpenAIApiType = 'chat' | 'responses';
|
|
1028
|
+
interface ModelMapping {
|
|
1029
|
+
provider: ModelProvider;
|
|
1030
|
+
model: string;
|
|
1031
|
+
/** For OpenAI models, which API to use: 'chat' for /v1/chat/completions, 'responses' for /v1/responses */
|
|
1032
|
+
openaiApiType?: OpenAIApiType;
|
|
1033
|
+
}
|
|
1034
|
+
declare const MODEL_MAPPINGS: Record<string, ModelMapping>;
|
|
1035
|
+
declare function getModelMapping(modelId: string): ModelMapping | undefined;
|
|
1036
|
+
declare function getProviderForModelId(modelId: string): ModelProvider | undefined;
|
|
1037
|
+
declare function getValidModelsForProvider(provider: ModelProvider): string[];
|
|
1038
|
+
declare function getAnthropicModelForModelId(modelId: string): string | undefined;
|
|
1039
|
+
declare function getOpenAIModelForModelId(modelId: string): string | undefined;
|
|
1040
|
+
declare function getOpenAIApiType(modelId: string): OpenAIApiType;
|
|
1041
|
+
declare function isResponsesApiModel(modelId: string): boolean;
|
|
1042
|
+
declare function isWorkflowModel(modelId: string): boolean;
|
|
1043
|
+
declare function getWorkflowModelRef(modelId: string): string | undefined;
|
|
1044
|
+
declare const MODEL_ID_TO_ANTHROPIC_MODEL: Record<string, string>;
|
|
1045
|
+
|
|
1046
|
+
interface GitLabErrorOptions {
|
|
1047
|
+
message: string;
|
|
1048
|
+
statusCode?: number;
|
|
1049
|
+
responseBody?: string;
|
|
1050
|
+
cause?: unknown;
|
|
1051
|
+
}
|
|
1052
|
+
declare class GitLabError extends Error {
|
|
1053
|
+
readonly statusCode?: number;
|
|
1054
|
+
readonly responseBody?: string;
|
|
1055
|
+
readonly cause?: unknown;
|
|
1056
|
+
constructor(options: GitLabErrorOptions);
|
|
1057
|
+
static fromResponse(response: Response, body: string): GitLabError;
|
|
1058
|
+
isAuthError(): boolean;
|
|
1059
|
+
isRateLimitError(): boolean;
|
|
1060
|
+
isForbiddenError(): boolean;
|
|
1061
|
+
isServerError(): boolean;
|
|
1062
|
+
/**
|
|
1063
|
+
* Check if this error is a context overflow error (prompt too long).
|
|
1064
|
+
* These errors occur when the conversation exceeds the model's token limit.
|
|
1065
|
+
*/
|
|
1066
|
+
isContextOverflowError(): boolean;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
declare const gitlabOAuthTokenResponseSchema: z.ZodObject<
|
|
1070
|
+
{
|
|
1071
|
+
access_token: z.ZodString;
|
|
1072
|
+
refresh_token: z.ZodOptional<z.ZodString>;
|
|
1073
|
+
expires_in: z.ZodNumber;
|
|
1074
|
+
created_at: z.ZodNumber;
|
|
1075
|
+
},
|
|
1076
|
+
'strip',
|
|
1077
|
+
z.ZodTypeAny,
|
|
1078
|
+
{
|
|
1079
|
+
created_at?: number;
|
|
1080
|
+
access_token?: string;
|
|
1081
|
+
refresh_token?: string;
|
|
1082
|
+
expires_in?: number;
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
created_at?: number;
|
|
1086
|
+
access_token?: string;
|
|
1087
|
+
refresh_token?: string;
|
|
1088
|
+
expires_in?: number;
|
|
1089
|
+
}
|
|
1090
|
+
>;
|
|
1091
|
+
type GitLabOAuthTokenResponse = z.infer<typeof gitlabOAuthTokenResponseSchema>;
|
|
1092
|
+
|
|
1093
|
+
/**
|
|
1094
|
+
* OAuth types and constants for GitLab authentication
|
|
1095
|
+
* Based on gitlab-vscode-extension and gitlab-lsp patterns
|
|
1096
|
+
*/
|
|
1097
|
+
interface GitLabOAuthTokens {
|
|
1098
|
+
accessToken: string;
|
|
1099
|
+
refreshToken: string;
|
|
1100
|
+
expiresAt: number;
|
|
1101
|
+
instanceUrl: string;
|
|
1102
|
+
}
|
|
1103
|
+
interface OpenCodeAuthOAuth {
|
|
1104
|
+
type: 'oauth';
|
|
1105
|
+
refresh: string;
|
|
1106
|
+
access: string;
|
|
1107
|
+
expires: number;
|
|
1108
|
+
/** @deprecated Use enterpriseUrl instead. Kept for backwards compatibility with older auth.json files. */
|
|
1109
|
+
instanceUrl?: string;
|
|
1110
|
+
/** Instance URL as written by opencode-gitlab-auth plugin (e.g. 'https://gitlab.com') */
|
|
1111
|
+
enterpriseUrl?: string;
|
|
1112
|
+
}
|
|
1113
|
+
interface OpenCodeAuthApi {
|
|
1114
|
+
type: 'api';
|
|
1115
|
+
key: string;
|
|
1116
|
+
}
|
|
1117
|
+
type OpenCodeAuth = OpenCodeAuthOAuth | OpenCodeAuthApi;
|
|
1118
|
+
/**
|
|
1119
|
+
* Default OAuth client ID for GitLab.com
|
|
1120
|
+
* This is the same client ID used by opencode-gitlab-auth plugin.
|
|
1121
|
+
* The GITLAB_OAUTH_CLIENT_ID env var takes precedence if set.
|
|
1122
|
+
* Note: VS Code extension uses a different client ID ('36f2a70c...') but we use the opencode plugin's ID
|
|
1123
|
+
* to ensure token refresh works correctly with tokens created by the auth plugin.
|
|
1124
|
+
*/
|
|
1125
|
+
declare const OPENCODE_GITLAB_AUTH_CLIENT_ID =
|
|
1126
|
+
'1d89f9fdb23ee96d4e603201f6861dab6e143c5c3c00469a018a2d94bdc03d4e';
|
|
1127
|
+
/**
|
|
1128
|
+
* @deprecated Use OPENCODE_GITLAB_AUTH_CLIENT_ID instead. This is the VS Code extension's client ID
|
|
1129
|
+
* and will cause refresh failures if used with tokens created by opencode-gitlab-auth.
|
|
1130
|
+
*/
|
|
1131
|
+
declare const BUNDLED_CLIENT_ID =
|
|
1132
|
+
'36f2a70cddeb5a0889d4fd8295c241b7e9848e89cf9e599d0eed2d8e5350fbf5';
|
|
1133
|
+
/**
|
|
1134
|
+
* GitLab.com URL constant
|
|
1135
|
+
*/
|
|
1136
|
+
declare const GITLAB_COM_URL = 'https://gitlab.com';
|
|
1137
|
+
/**
|
|
1138
|
+
* Token expiry skew in milliseconds (5 minutes)
|
|
1139
|
+
* Refresh tokens this many milliseconds before they expire
|
|
1140
|
+
*/
|
|
1141
|
+
declare const TOKEN_EXPIRY_SKEW_MS: number;
|
|
1142
|
+
/**
|
|
1143
|
+
* OAuth scopes to request
|
|
1144
|
+
*/
|
|
1145
|
+
declare const OAUTH_SCOPES: string[];
|
|
1146
|
+
|
|
1147
|
+
/**
|
|
1148
|
+
* GitLab OAuth Manager
|
|
1149
|
+
* Handles OAuth token management, refresh, and exchange
|
|
1150
|
+
* Based on gitlab-vscode-extension TokenExchangeService and gitlab-lsp OAuthClientProvider
|
|
1151
|
+
*/
|
|
1152
|
+
|
|
1153
|
+
interface TokenExchangeParams {
|
|
1154
|
+
instanceUrl: string;
|
|
1155
|
+
clientId?: string;
|
|
1156
|
+
redirectUri?: string;
|
|
1157
|
+
}
|
|
1158
|
+
interface AuthorizationCodeParams extends TokenExchangeParams {
|
|
1159
|
+
code: string;
|
|
1160
|
+
codeVerifier: string;
|
|
1161
|
+
}
|
|
1162
|
+
interface RefreshTokenParams extends TokenExchangeParams {
|
|
1163
|
+
refreshToken: string;
|
|
1164
|
+
}
|
|
1165
|
+
declare class GitLabOAuthManager {
|
|
1166
|
+
private fetch;
|
|
1167
|
+
constructor(fetchImpl?: typeof fetch);
|
|
1168
|
+
/**
|
|
1169
|
+
* Check if a token is expired
|
|
1170
|
+
*/
|
|
1171
|
+
isTokenExpired(expiresAt: number): boolean;
|
|
1172
|
+
/**
|
|
1173
|
+
* Check if a token needs refresh (within skew window)
|
|
1174
|
+
*/
|
|
1175
|
+
needsRefresh(expiresAt: number): boolean;
|
|
1176
|
+
/**
|
|
1177
|
+
* Refresh tokens if needed
|
|
1178
|
+
* Returns the same tokens if refresh is not needed, or new tokens if refreshed
|
|
1179
|
+
*/
|
|
1180
|
+
refreshIfNeeded(tokens: GitLabOAuthTokens, clientId?: string): Promise<GitLabOAuthTokens>;
|
|
1181
|
+
/**
|
|
1182
|
+
* Exchange authorization code for tokens
|
|
1183
|
+
* Based on gitlab-vscode-extension createOAuthAccountFromCode
|
|
1184
|
+
*/
|
|
1185
|
+
exchangeAuthorizationCode(params: AuthorizationCodeParams): Promise<GitLabOAuthTokens>;
|
|
1186
|
+
/**
|
|
1187
|
+
* Exchange refresh token for new tokens
|
|
1188
|
+
* Based on gitlab-vscode-extension TokenExchangeService
|
|
1189
|
+
*/
|
|
1190
|
+
exchangeRefreshToken(params: RefreshTokenParams): Promise<GitLabOAuthTokens>;
|
|
1191
|
+
/**
|
|
1192
|
+
* Get the OAuth client ID for an instance.
|
|
1193
|
+
* Priority: env var > opencode-gitlab-auth default (for GitLab.com).
|
|
1194
|
+
* Note: callers (e.g. exchangeRefreshToken) may pass an explicit clientId
|
|
1195
|
+
* that bypasses this method entirely.
|
|
1196
|
+
*/
|
|
1197
|
+
private getClientId;
|
|
1198
|
+
/**
|
|
1199
|
+
* Exchange token with GitLab OAuth endpoint
|
|
1200
|
+
* Based on gitlab-vscode-extension GitLabService.exchangeToken
|
|
1201
|
+
*/
|
|
1202
|
+
private exchangeToken;
|
|
1203
|
+
/**
|
|
1204
|
+
* Create GitLabOAuthTokens from token response
|
|
1205
|
+
*/
|
|
1206
|
+
private createTokensFromResponse;
|
|
1207
|
+
/**
|
|
1208
|
+
* Create expiry timestamp from token response
|
|
1209
|
+
* Based on gitlab-vscode-extension createExpiresTimestamp
|
|
1210
|
+
*/
|
|
1211
|
+
private createExpiresTimestamp;
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
/**
|
|
1215
|
+
* Simple in-memory cache for GitLab project information
|
|
1216
|
+
* Used to avoid repeated API calls when detecting projects from git remotes
|
|
1217
|
+
*/
|
|
1218
|
+
interface GitLabProject {
|
|
1219
|
+
id: number;
|
|
1220
|
+
path: string;
|
|
1221
|
+
pathWithNamespace: string;
|
|
1222
|
+
name: string;
|
|
1223
|
+
namespaceId?: number;
|
|
1224
|
+
}
|
|
1225
|
+
/**
|
|
1226
|
+
* In-memory cache for GitLab project information with TTL support
|
|
1227
|
+
*/
|
|
1228
|
+
declare class GitLabProjectCache {
|
|
1229
|
+
private cache;
|
|
1230
|
+
private defaultTTL;
|
|
1231
|
+
/**
|
|
1232
|
+
* Create a new project cache
|
|
1233
|
+
* @param defaultTTL - Default time-to-live in milliseconds (default: 5 minutes)
|
|
1234
|
+
*/
|
|
1235
|
+
constructor(defaultTTL?: number);
|
|
1236
|
+
/**
|
|
1237
|
+
* Get a cached project by key
|
|
1238
|
+
* @param key - Cache key (typically the working directory path)
|
|
1239
|
+
* @returns The cached project or null if not found or expired
|
|
1240
|
+
*/
|
|
1241
|
+
get(key: string): GitLabProject | null;
|
|
1242
|
+
/**
|
|
1243
|
+
* Store a project in the cache
|
|
1244
|
+
* @param key - Cache key (typically the working directory path)
|
|
1245
|
+
* @param project - The project to cache
|
|
1246
|
+
* @param ttl - Optional custom TTL in milliseconds
|
|
1247
|
+
*/
|
|
1248
|
+
set(key: string, project: GitLabProject, ttl?: number): void;
|
|
1249
|
+
/**
|
|
1250
|
+
* Check if a key exists in the cache (and is not expired)
|
|
1251
|
+
* @param key - Cache key to check
|
|
1252
|
+
* @returns true if the key exists and is not expired
|
|
1253
|
+
*/
|
|
1254
|
+
has(key: string): boolean;
|
|
1255
|
+
/**
|
|
1256
|
+
* Remove a specific entry from the cache
|
|
1257
|
+
* @param key - Cache key to remove
|
|
1258
|
+
*/
|
|
1259
|
+
delete(key: string): void;
|
|
1260
|
+
/**
|
|
1261
|
+
* Clear all entries from the cache
|
|
1262
|
+
*/
|
|
1263
|
+
clear(): void;
|
|
1264
|
+
/**
|
|
1265
|
+
* Get the number of entries in the cache (including expired ones)
|
|
1266
|
+
*/
|
|
1267
|
+
get size(): number;
|
|
1268
|
+
/**
|
|
1269
|
+
* Clean up expired entries from the cache
|
|
1270
|
+
* This is useful for long-running processes to prevent memory leaks
|
|
1271
|
+
*/
|
|
1272
|
+
cleanup(): void;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
interface GitLabProjectDetectorConfig {
|
|
1276
|
+
instanceUrl: string;
|
|
1277
|
+
getHeaders: () => Record<string, string>;
|
|
1278
|
+
fetch?: typeof fetch;
|
|
1279
|
+
cache?: GitLabProjectCache;
|
|
1280
|
+
gitTimeout?: number;
|
|
1281
|
+
}
|
|
1282
|
+
/**
|
|
1283
|
+
* Detects GitLab project information from git remote URLs
|
|
1284
|
+
*
|
|
1285
|
+
* This class provides functionality to:
|
|
1286
|
+
* - Parse git remote URLs (SSH, HTTPS, custom domains)
|
|
1287
|
+
* - Execute git commands to get remote URLs
|
|
1288
|
+
* - Fetch project details from GitLab API
|
|
1289
|
+
* - Cache project information to avoid repeated API calls
|
|
1290
|
+
*/
|
|
1291
|
+
declare class GitLabProjectDetector {
|
|
1292
|
+
private readonly config;
|
|
1293
|
+
private readonly fetchFn;
|
|
1294
|
+
private readonly cache;
|
|
1295
|
+
constructor(config: GitLabProjectDetectorConfig);
|
|
1296
|
+
/**
|
|
1297
|
+
* Auto-detect GitLab project from git remote in the working directory
|
|
1298
|
+
*
|
|
1299
|
+
* @param workingDirectory - The directory to check for git remote
|
|
1300
|
+
* @param remoteName - The git remote name to use (default: 'origin')
|
|
1301
|
+
* @returns The detected project or null if not a git repo / no matching remote
|
|
1302
|
+
* @throws GitLabError if the API call or an unexpected error occurs
|
|
1303
|
+
*/
|
|
1304
|
+
detectProject(workingDirectory: string, remoteName?: string): Promise<GitLabProject | null>;
|
|
1305
|
+
/**
|
|
1306
|
+
* Parse a git remote URL to extract the project path
|
|
1307
|
+
*
|
|
1308
|
+
* Supports:
|
|
1309
|
+
* - SSH: git@gitlab.com:namespace/project.git
|
|
1310
|
+
* - HTTPS: https://gitlab.com/namespace/project.git
|
|
1311
|
+
* - HTTP: http://gitlab.local/namespace/project.git
|
|
1312
|
+
* - Custom domains and ports
|
|
1313
|
+
*
|
|
1314
|
+
* @param remoteUrl - The git remote URL
|
|
1315
|
+
* @param instanceUrl - The GitLab instance URL to match against
|
|
1316
|
+
* @returns The project path (e.g., "namespace/project") or null if parsing fails
|
|
1317
|
+
*/
|
|
1318
|
+
parseGitRemoteUrl(remoteUrl: string, instanceUrl: string): string | null;
|
|
1319
|
+
/**
|
|
1320
|
+
* Get the git remote URL from a working directory
|
|
1321
|
+
*
|
|
1322
|
+
* @param workingDirectory - The directory to check
|
|
1323
|
+
* @param remoteName - The git remote name (default: 'origin')
|
|
1324
|
+
* @returns The remote URL or null if not found
|
|
1325
|
+
*/
|
|
1326
|
+
getGitRemoteUrl(workingDirectory: string, remoteName?: string): Promise<string | null>;
|
|
1327
|
+
/**
|
|
1328
|
+
* Fetch project details from GitLab API by project path
|
|
1329
|
+
*
|
|
1330
|
+
* @param projectPath - The project path (e.g., "namespace/project")
|
|
1331
|
+
* @returns The project details
|
|
1332
|
+
* @throws GitLabError if the API call fails
|
|
1333
|
+
*/
|
|
1334
|
+
getProjectByPath(projectPath: string): Promise<GitLabProject>;
|
|
1335
|
+
/**
|
|
1336
|
+
* Clear the project cache
|
|
1337
|
+
*/
|
|
1338
|
+
clearCache(): void;
|
|
1339
|
+
/**
|
|
1340
|
+
* Get the cache instance (useful for testing)
|
|
1341
|
+
*/
|
|
1342
|
+
getCache(): GitLabProjectCache;
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1345
|
+
/**
|
|
1346
|
+
* Response from /api/v4/ai/third_party_agents/direct_access
|
|
1347
|
+
*/
|
|
1348
|
+
declare const directAccessTokenSchema: z.ZodObject<
|
|
1349
|
+
{
|
|
1350
|
+
headers: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
1351
|
+
token: z.ZodString;
|
|
1352
|
+
},
|
|
1353
|
+
'strip',
|
|
1354
|
+
z.ZodTypeAny,
|
|
1355
|
+
{
|
|
1356
|
+
headers?: Record<string, string>;
|
|
1357
|
+
token?: string;
|
|
1358
|
+
},
|
|
1359
|
+
{
|
|
1360
|
+
headers?: Record<string, string>;
|
|
1361
|
+
token?: string;
|
|
1362
|
+
}
|
|
1363
|
+
>;
|
|
1364
|
+
type DirectAccessToken = z.infer<typeof directAccessTokenSchema>;
|
|
1365
|
+
declare const DEFAULT_AI_GATEWAY_URL = 'https://cloud.gitlab.com';
|
|
1366
|
+
interface GitLabDirectAccessConfig {
|
|
1367
|
+
instanceUrl: string;
|
|
1368
|
+
getHeaders: () => Record<string, string>;
|
|
1369
|
+
fetch?: typeof fetch;
|
|
1370
|
+
/**
|
|
1371
|
+
* Optional callback to refresh the API key when a 401 error occurs.
|
|
1372
|
+
* Should clear cached credentials and re-fetch from auth provider.
|
|
1373
|
+
*/
|
|
1374
|
+
refreshApiKey?: () => Promise<void>;
|
|
1375
|
+
/**
|
|
1376
|
+
* Feature flags to pass to the GitLab API
|
|
1377
|
+
*/
|
|
1378
|
+
featureFlags?: Record<string, boolean>;
|
|
1379
|
+
/**
|
|
1380
|
+
* AI Gateway URL for the Anthropic proxy.
|
|
1381
|
+
* Can also be set via GITLAB_AI_GATEWAY_URL environment variable.
|
|
1382
|
+
* @default 'https://cloud.gitlab.com'
|
|
1383
|
+
*/
|
|
1384
|
+
aiGatewayUrl?: string;
|
|
1385
|
+
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Client for GitLab's third-party agents direct access API.
|
|
1388
|
+
* This allows routing requests through GitLab's proxy to Anthropic.
|
|
1389
|
+
*/
|
|
1390
|
+
declare class GitLabDirectAccessClient {
|
|
1391
|
+
private readonly config;
|
|
1392
|
+
private readonly fetchFn;
|
|
1393
|
+
private readonly aiGatewayUrl;
|
|
1394
|
+
private cachedToken;
|
|
1395
|
+
private tokenExpiresAt;
|
|
1396
|
+
constructor(config: GitLabDirectAccessConfig);
|
|
1397
|
+
/**
|
|
1398
|
+
* Get a direct access token for the Anthropic proxy.
|
|
1399
|
+
* Tokens are cached for 25 minutes (they expire after 30 minutes).
|
|
1400
|
+
* @param forceRefresh - If true, ignores the cache and fetches a new token
|
|
1401
|
+
*/
|
|
1402
|
+
getDirectAccessToken(forceRefresh?: boolean): Promise<DirectAccessToken>;
|
|
1403
|
+
/**
|
|
1404
|
+
* Get the Anthropic proxy base URL
|
|
1405
|
+
*/
|
|
1406
|
+
getAnthropicProxyUrl(): string;
|
|
1407
|
+
/**
|
|
1408
|
+
* Get the OpenAI proxy base URL
|
|
1409
|
+
* Note: The OpenAI SDK expects a base URL like https://api.openai.com/v1
|
|
1410
|
+
* and appends paths like /chat/completions. So we need /v1 at the end.
|
|
1411
|
+
*/
|
|
1412
|
+
getOpenAIProxyUrl(): string;
|
|
1413
|
+
/**
|
|
1414
|
+
* Invalidate the cached token
|
|
1415
|
+
*/
|
|
1416
|
+
invalidateToken(): void;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
/**
|
|
1420
|
+
* WebSocket client for the GitLab Duo Agent Platform (DWS).
|
|
1421
|
+
*
|
|
1422
|
+
* Handles:
|
|
1423
|
+
* - WebSocket connection to `wss://{instance}/api/v4/ai/duo_workflows/ws`
|
|
1424
|
+
* - Sending ClientEvent messages (startRequest, actionResponse, stopWorkflow)
|
|
1425
|
+
* - Receiving WorkflowAction messages (newCheckpoint, runMcpTool, built-in tools)
|
|
1426
|
+
* - Dual heartbeat: ws.ping(45s) + JSON heartbeat(60s) — matching gitlab-lsp
|
|
1427
|
+
* - No reconnection on drop (matches gitlab-lsp behavior)
|
|
1428
|
+
*/
|
|
1429
|
+
|
|
1430
|
+
interface WorkflowWebSocketOptions {
|
|
1431
|
+
/** GitLab instance URL */
|
|
1432
|
+
instanceUrl: string;
|
|
1433
|
+
/** Model reference for DWS (e.g. 'anthropic/claude-sonnet-4-5-20250929' or 'default') */
|
|
1434
|
+
modelRef: string;
|
|
1435
|
+
/** Auth headers — must include Authorization */
|
|
1436
|
+
headers: Record<string, string>;
|
|
1437
|
+
/** Optional correlation ID */
|
|
1438
|
+
requestId?: string;
|
|
1439
|
+
/** Optional project context */
|
|
1440
|
+
projectId?: string;
|
|
1441
|
+
namespaceId?: string;
|
|
1442
|
+
rootNamespaceId?: string;
|
|
1443
|
+
}
|
|
1444
|
+
type EventCallback = (event: WorkflowClientEvent) => void;
|
|
1445
|
+
declare class GitLabWorkflowClient {
|
|
1446
|
+
private socket;
|
|
1447
|
+
private keepaliveInterval;
|
|
1448
|
+
private heartbeatInterval;
|
|
1449
|
+
private eventCallback;
|
|
1450
|
+
private closed;
|
|
1451
|
+
private lastSendTime;
|
|
1452
|
+
/**
|
|
1453
|
+
* Connect to the DWS WebSocket and start listening for events.
|
|
1454
|
+
*
|
|
1455
|
+
* @param options - Connection parameters
|
|
1456
|
+
* @param onEvent - Callback invoked for each WorkflowClientEvent
|
|
1457
|
+
* @returns Promise that resolves when the connection is open
|
|
1458
|
+
*/
|
|
1459
|
+
connect(options: WorkflowWebSocketOptions, onEvent: EventCallback): Promise<void>;
|
|
1460
|
+
/**
|
|
1461
|
+
* Send a startRequest to begin the workflow.
|
|
1462
|
+
*/
|
|
1463
|
+
sendStartRequest(request: StartRequest): void;
|
|
1464
|
+
/**
|
|
1465
|
+
* Send an actionResponse (tool result) back to DWS.
|
|
1466
|
+
*/
|
|
1467
|
+
sendActionResponse(requestID: string, response: string, error?: string | null): void;
|
|
1468
|
+
/**
|
|
1469
|
+
* Stop the workflow gracefully.
|
|
1470
|
+
*/
|
|
1471
|
+
stop(): void;
|
|
1472
|
+
/**
|
|
1473
|
+
* Close the WebSocket connection.
|
|
1474
|
+
*/
|
|
1475
|
+
close(): void;
|
|
1476
|
+
/**
|
|
1477
|
+
* Check if the WebSocket is currently connected.
|
|
1478
|
+
*/
|
|
1479
|
+
get isConnected(): boolean;
|
|
1480
|
+
private validateOptions;
|
|
1481
|
+
private buildWebSocketUrl;
|
|
1482
|
+
private buildWebSocketHeaders;
|
|
1483
|
+
private handleAction;
|
|
1484
|
+
private send;
|
|
1485
|
+
private sendHeartbeatIfNeeded;
|
|
1486
|
+
private emit;
|
|
1487
|
+
/**
|
|
1488
|
+
* Start ws.ping() keepalive (45s interval).
|
|
1489
|
+
* Keeps TCP connection alive through proxies/load balancers.
|
|
1490
|
+
*/
|
|
1491
|
+
private startKeepalive;
|
|
1492
|
+
/**
|
|
1493
|
+
* Start application-level heartbeat (60s interval).
|
|
1494
|
+
* Prevents DWS from timing out the workflow.
|
|
1495
|
+
*/
|
|
1496
|
+
private startHeartbeat;
|
|
1497
|
+
private cleanedUp;
|
|
1498
|
+
/**
|
|
1499
|
+
* Clean up intervals. Idempotent — safe to call multiple times.
|
|
1500
|
+
*/
|
|
1501
|
+
private cleanup;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
/**
|
|
1505
|
+
* Token management for the GitLab Duo Agent Platform (DWS).
|
|
1506
|
+
*
|
|
1507
|
+
* Handles two API calls:
|
|
1508
|
+
* 1. POST /api/v4/ai/duo_workflows/direct_access → GenerateTokenResponse (workflow token)
|
|
1509
|
+
* 2. POST /api/v4/ai/duo_workflows/workflows → CreateWorkflowResponse (workflow ID)
|
|
1510
|
+
*
|
|
1511
|
+
* Tokens are cached for 25 minutes (they expire after ~30 minutes).
|
|
1512
|
+
* On 401, optionally triggers an API key refresh callback and retries once.
|
|
1513
|
+
* On 403, throws a clear error explaining GitLab Duo Enterprise requirements.
|
|
1514
|
+
*/
|
|
1515
|
+
|
|
1516
|
+
declare class GitLabWorkflowTokenClient {
|
|
1517
|
+
private readonly config;
|
|
1518
|
+
private readonly fetchFn;
|
|
1519
|
+
/**
|
|
1520
|
+
* Token cache keyed by workflow definition type.
|
|
1521
|
+
*
|
|
1522
|
+
* - CHAT workflows use a shared key (CHAT_SHARED_TOKEN_KEY) so tokens
|
|
1523
|
+
* are reused across ALL chat sessions (matching gitlab-lsp behavior).
|
|
1524
|
+
* - SOFTWARE_DEVELOPMENT workflows would use per-workflow-id keys,
|
|
1525
|
+
* but since we fetch tokens before creating workflows, we key by type.
|
|
1526
|
+
*/
|
|
1527
|
+
private tokenCache;
|
|
1528
|
+
constructor(config: GitLabWorkflowClientConfig);
|
|
1529
|
+
/**
|
|
1530
|
+
* Resolve the cache key for a given workflow definition.
|
|
1531
|
+
* CHAT workflows share a single token per namespace; other types get per-type keys.
|
|
1532
|
+
*/
|
|
1533
|
+
private getCacheKey;
|
|
1534
|
+
/**
|
|
1535
|
+
* Get a DWS token, using cached value if still valid.
|
|
1536
|
+
*
|
|
1537
|
+
* Token caching strategy (matches gitlab-lsp):
|
|
1538
|
+
* - CHAT workflows: shared token across all sessions
|
|
1539
|
+
* - Other workflows: per-type token
|
|
1540
|
+
*
|
|
1541
|
+
* @param workflowDefinition - Workflow type (default: 'chat')
|
|
1542
|
+
* @param rootNamespaceId - Optional root namespace for scoping
|
|
1543
|
+
* @param forceRefresh - Bypass cache
|
|
1544
|
+
*/
|
|
1545
|
+
getToken(
|
|
1546
|
+
workflowDefinition?: string,
|
|
1547
|
+
rootNamespaceId?: string,
|
|
1548
|
+
forceRefresh?: boolean
|
|
1549
|
+
): Promise<GenerateTokenResponse>;
|
|
1550
|
+
/**
|
|
1551
|
+
* Create a new workflow on the GitLab instance.
|
|
1552
|
+
*
|
|
1553
|
+
* @param goal - The user's message / goal for this workflow
|
|
1554
|
+
* @param options - Additional workflow creation options
|
|
1555
|
+
* @returns The created workflow's ID
|
|
1556
|
+
*/
|
|
1557
|
+
createWorkflow(
|
|
1558
|
+
goal: string,
|
|
1559
|
+
options?: {
|
|
1560
|
+
projectId?: string;
|
|
1561
|
+
namespaceId?: string;
|
|
1562
|
+
workflowDefinition?: string;
|
|
1563
|
+
agentPrivileges?: number[];
|
|
1564
|
+
environment?: string;
|
|
1565
|
+
allowAgentToRequestUser?: boolean;
|
|
1566
|
+
}
|
|
1567
|
+
): Promise<string>;
|
|
1568
|
+
/**
|
|
1569
|
+
* Invalidate cached tokens.
|
|
1570
|
+
*
|
|
1571
|
+
* @param workflowDefinition - If provided, only invalidate for this type.
|
|
1572
|
+
* If omitted, clears ALL cached tokens.
|
|
1573
|
+
*/
|
|
1574
|
+
invalidateToken(workflowDefinition?: string, rootNamespaceId?: string): void;
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
export {
|
|
1578
|
+
AGENT_PRIVILEGES,
|
|
1579
|
+
type ActionResponsePayload,
|
|
1580
|
+
type AdditionalContext,
|
|
1581
|
+
type AiChatAvailableModels,
|
|
1582
|
+
type AiModel,
|
|
1583
|
+
BUNDLED_CLIENT_ID,
|
|
1584
|
+
CLIENT_VERSION,
|
|
1585
|
+
type ClientEvent,
|
|
1586
|
+
DEFAULT_AGENT_PRIVILEGES,
|
|
1587
|
+
DEFAULT_AI_GATEWAY_URL,
|
|
1588
|
+
DEFAULT_CLIENT_CAPABILITIES,
|
|
1589
|
+
DEFAULT_WORKFLOW_DEFINITION,
|
|
1590
|
+
type DirectAccessToken,
|
|
1591
|
+
type DiscoveredModels,
|
|
1592
|
+
GITLAB_COM_URL,
|
|
1593
|
+
type GenerateTokenResponse,
|
|
1594
|
+
type GitLabAgenticOptions,
|
|
1595
|
+
type GitLabAnthropicConfig,
|
|
1596
|
+
GitLabAnthropicLanguageModel,
|
|
1597
|
+
GitLabDirectAccessClient,
|
|
1598
|
+
type GitLabDirectAccessConfig,
|
|
1599
|
+
GitLabError,
|
|
1600
|
+
type GitLabErrorOptions,
|
|
1601
|
+
GitLabModelCache,
|
|
1602
|
+
GitLabModelDiscovery,
|
|
1603
|
+
GitLabOAuthManager,
|
|
1604
|
+
type GitLabOAuthTokenResponse,
|
|
1605
|
+
type GitLabOAuthTokens,
|
|
1606
|
+
type GitLabOpenAIConfig,
|
|
1607
|
+
GitLabOpenAILanguageModel,
|
|
1608
|
+
type GitLabProject,
|
|
1609
|
+
GitLabProjectCache,
|
|
1610
|
+
GitLabProjectDetector,
|
|
1611
|
+
type GitLabProjectDetectorConfig,
|
|
1612
|
+
type GitLabProvider,
|
|
1613
|
+
type GitLabProviderSettings,
|
|
1614
|
+
GitLabWorkflowClient,
|
|
1615
|
+
type GitLabWorkflowClientConfig,
|
|
1616
|
+
GitLabWorkflowLanguageModel,
|
|
1617
|
+
type GitLabWorkflowLanguageModelConfig,
|
|
1618
|
+
type GitLabWorkflowOptions,
|
|
1619
|
+
GitLabWorkflowTokenClient,
|
|
1620
|
+
MODEL_ID_TO_ANTHROPIC_MODEL,
|
|
1621
|
+
MODEL_MAPPINGS,
|
|
1622
|
+
type McpToolDefinition,
|
|
1623
|
+
type ModelCacheEntry,
|
|
1624
|
+
type ModelDiscoveryConfig,
|
|
1625
|
+
type ModelMapping,
|
|
1626
|
+
type ModelProvider,
|
|
1627
|
+
type NewCheckpoint,
|
|
1628
|
+
OAUTH_SCOPES,
|
|
1629
|
+
OPENCODE_GITLAB_AUTH_CLIENT_ID,
|
|
1630
|
+
type OpenAIApiType,
|
|
1631
|
+
type OpenCodeAuth,
|
|
1632
|
+
type OpenCodeAuthApi,
|
|
1633
|
+
type OpenCodeAuthOAuth,
|
|
1634
|
+
type RunMcpTool,
|
|
1635
|
+
type StartRequest,
|
|
1636
|
+
TOKEN_EXPIRY_SKEW_MS,
|
|
1637
|
+
VERSION,
|
|
1638
|
+
WORKFLOW_ENVIRONMENT,
|
|
1639
|
+
WS_HEARTBEAT_INTERVAL_MS,
|
|
1640
|
+
WS_KEEPALIVE_PING_INTERVAL_MS,
|
|
1641
|
+
type WorkflowAction,
|
|
1642
|
+
type WorkflowClientEvent,
|
|
1643
|
+
type WorkflowStatus,
|
|
1644
|
+
type WorkflowToolExecutor,
|
|
1645
|
+
WorkflowType,
|
|
1646
|
+
type WorkflowWebSocketOptions,
|
|
1647
|
+
createGitLab,
|
|
1648
|
+
getAnthropicModelForModelId,
|
|
1649
|
+
getModelMapping,
|
|
1650
|
+
getOpenAIApiType,
|
|
1651
|
+
getOpenAIModelForModelId,
|
|
1652
|
+
getProviderForModelId,
|
|
1653
|
+
getValidModelsForProvider,
|
|
1654
|
+
getWorkflowModelRef,
|
|
1655
|
+
gitlab,
|
|
1656
|
+
isResponsesApiModel,
|
|
1657
|
+
isWorkflowModel,
|
|
1658
|
+
};
|