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