gitlab-ai-provider 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1521 @@
1
+ import { LanguageModelV2, LanguageModelV2CallOptions, LanguageModelV2Content, LanguageModelV2FinishReason, LanguageModelV2Usage, LanguageModelV2CallWarning, LanguageModelV2StreamPart } from '@ai-sdk/provider';
2
+ import { z } from 'zod';
3
+
4
+ interface GitLabAnthropicConfig {
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>;
42
+ }
43
+ /**
44
+ * GitLab Anthropic Language Model
45
+ *
46
+ * This model uses GitLab's Anthropic proxy to provide native tool calling support
47
+ * for the duo-chat model. It connects to Claude through GitLab's cloud proxy
48
+ * at https://cloud.gitlab.com/ai/v1/proxy/anthropic/
49
+ */
50
+ declare class GitLabAnthropicLanguageModel implements LanguageModelV2 {
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;
106
+ }
107
+
108
+ /**
109
+ * Dynamic model discovery via GitLab GraphQL API.
110
+ *
111
+ * Queries `aiChatAvailableModels` to discover which models are available
112
+ * for a given GitLab namespace. Used to dynamically populate `duo-workflow-*`
113
+ * model IDs at runtime.
114
+ *
115
+ * Requires GitLab 18.4+ (18.5+ for pinned model support).
116
+ */
117
+ interface AiModel {
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;
122
+ }
123
+ interface AiChatAvailableModels {
124
+ defaultModel: AiModel | null;
125
+ selectableModels: AiModel[];
126
+ pinnedModel: AiModel | null;
127
+ }
128
+ interface DiscoveredModels {
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;
139
+ }
140
+ interface ModelDiscoveryConfig {
141
+ /** GitLab instance URL */
142
+ instanceUrl: string;
143
+ /** Function returning auth headers */
144
+ getHeaders: () => Record<string, string>;
145
+ /** Custom fetch */
146
+ fetch?: typeof fetch;
147
+ }
148
+ declare class GitLabModelDiscovery {
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;
175
+ }
176
+
177
+ /**
178
+ * File-based cache for workflow model discovery results and user selection.
179
+ *
180
+ * A single cache file is stored at
181
+ * `~/.cache/opencode/gitlab-workflow-model-cache.json`
182
+ * (or `$XDG_CACHE_HOME/opencode/...` when set).
183
+ *
184
+ * The file contains a JSON object keyed by a truncated SHA-256 hash of the
185
+ * workspace directory path combined with the GitLab instance URL. This ensures
186
+ * that switching instances for the same workspace invalidates the cache.
187
+ * Each value holds:
188
+ * - `discovery`: The full DiscoveredModels JSON from the last successful discovery
189
+ * - `selectedModelRef`: The model ref the user last selected
190
+ * - `selectedModelName`: Human-readable name of the selected model
191
+ * - `updatedAt`: ISO timestamp of the last write
192
+ */
193
+
194
+ interface ModelCacheEntry {
195
+ discovery: DiscoveredModels | null;
196
+ selectedModelRef: string | null;
197
+ selectedModelName: string | null;
198
+ updatedAt: string;
199
+ }
200
+ declare class GitLabModelCache {
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;
239
+ }
240
+
241
+ /**
242
+ * Types for the GitLab Duo Agent Platform (DWS) protocol.
243
+ *
244
+ * DWS uses a WebSocket-based bidirectional protocol where:
245
+ * - Client sends `ClientEvent` messages (startRequest, actionResponse, stopWorkflow, heartbeat)
246
+ * - Server sends `WorkflowAction` messages (newCheckpoint, runMcpTool, built-in tools)
247
+ *
248
+ * Message format is JSON-serialized protobuf with camelCase field names.
249
+ */
250
+ interface GenerateTokenResponse {
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
+ };
267
+ }
268
+ interface McpToolDefinition {
269
+ name: string;
270
+ description: string;
271
+ inputSchema: string;
272
+ }
273
+ interface AdditionalContext {
274
+ category: string;
275
+ id?: string;
276
+ content?: string;
277
+ metadata?: string;
278
+ }
279
+ interface StartRequest {
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;
291
+ }
292
+ interface ActionResponsePayload {
293
+ requestID: string;
294
+ plainTextResponse: {
295
+ response: string;
296
+ error: string | null;
297
+ };
298
+ }
299
+ interface StopWorkflow {
300
+ reason: string;
301
+ }
302
+ interface Heartbeat {
303
+ timestamp: number;
304
+ }
305
+ /**
306
+ * Client → Server event union.
307
+ * Exactly one of the fields should be set per message.
308
+ */
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';
319
+ interface NewCheckpoint {
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;
326
+ }
327
+ interface RunMcpTool {
328
+ name: string;
329
+ args: string;
330
+ }
331
+ interface ReadFileAction {
332
+ filepath: string;
333
+ }
334
+ interface ReadFilesAction {
335
+ filepaths: string[];
336
+ }
337
+ interface WriteFileAction {
338
+ filepath: string;
339
+ contents: string;
340
+ }
341
+ interface ShellCommandAction {
342
+ command: string;
343
+ }
344
+ interface EditFileAction {
345
+ filepath: string;
346
+ oldString: string;
347
+ newString: string;
348
+ }
349
+ interface ListDirectoryAction {
350
+ directory: string;
351
+ }
352
+ interface FindFilesAction {
353
+ name_pattern: string;
354
+ }
355
+ interface GrepAction {
356
+ pattern: string;
357
+ search_directory?: string;
358
+ case_insensitive?: boolean;
359
+ }
360
+ interface MkdirAction {
361
+ directory_path: string;
362
+ }
363
+ interface RunCommandAction {
364
+ program: string;
365
+ flags?: string[];
366
+ arguments?: string[];
367
+ }
368
+ interface RunGitCommandAction {
369
+ command: string;
370
+ arguments?: string[];
371
+ repository_url?: string;
372
+ }
373
+ interface GitLabApiRequestAction {
374
+ method: string;
375
+ path: string;
376
+ body?: string;
377
+ }
378
+ /**
379
+ * Server → Client action union.
380
+ * Field names match the DWS protobuf camelCase wire format exactly.
381
+ * Each message has an optional `requestID` (required for tool invocations
382
+ * that need an `actionResponse` back).
383
+ */
384
+ interface WorkflowAction {
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;
400
+ }
401
+ /**
402
+ * Options for creating a workflow chat model via `provider.workflowChat()`.
403
+ */
404
+ interface GitLabWorkflowOptions {
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>;
484
+ }
485
+ interface GitLabWorkflowClientConfig {
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;
504
+ }
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
+ };
527
+ /**
528
+ * Workflow type enum — matches gitlab-lsp WorkflowType.
529
+ *
530
+ * - CHAT: Shared token across sessions, tokens NOT revoked on completion
531
+ * - SOFTWARE_DEVELOPMENT: Per-workflow token, tokens revoked on completion
532
+ */
533
+ declare enum WorkflowType {
534
+ CHAT = "chat",
535
+ SOFTWARE_DEVELOPMENT = "software_development"
536
+ }
537
+ /** WebSocket ping interval (TCP keepalive) — 45s matching gitlab-lsp */
538
+ declare const WS_KEEPALIVE_PING_INTERVAL_MS = 45000;
539
+ /** Application heartbeat interval — 60s matching gitlab-lsp */
540
+ declare const WS_HEARTBEAT_INTERVAL_MS = 60000;
541
+ /** Default workflow definition — uses CHAT for agentic chat (matches gitlab-lsp) */
542
+ declare const DEFAULT_WORKFLOW_DEFINITION = WorkflowType.CHAT;
543
+ /** Default client capabilities */
544
+ declare const DEFAULT_CLIENT_CAPABILITIES: string[];
545
+ /** Client version sent in startRequest */
546
+ declare const CLIENT_VERSION = "1.0";
547
+ /**
548
+ * Agent privileges for workflow creation.
549
+ * Matches gitlab-lsp AGENT_PRIVILEGES enum.
550
+ */
551
+ declare const AGENT_PRIVILEGES: {
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;
558
+ };
559
+ /** Default agent privileges — matches gitlab-lsp defaults */
560
+ declare const DEFAULT_AGENT_PRIVILEGES: (1 | 2 | 4 | 3 | 5 | 6)[];
561
+ /** Workflow execution environment */
562
+ declare const WORKFLOW_ENVIRONMENT: "ide";
563
+
564
+ interface GitLabWorkflowLanguageModelConfig extends GitLabWorkflowClientConfig {
565
+ /** Provider name (e.g., 'gitlab.workflow') */
566
+ provider: string;
567
+ }
568
+ /**
569
+ * Callback for executing a tool requested by DWS.
570
+ * Provided by the consumer (e.g., OpenCode) to bridge DWS tool requests
571
+ * to the host's tool execution system.
572
+ *
573
+ * @param toolName - Name of the tool DWS wants to execute
574
+ * @param args - JSON-encoded arguments
575
+ * @returns Tool execution result as a string, or throws on error
576
+ */
577
+ type WorkflowToolExecutor = (toolName: string, args: string, requestID: string) => Promise<{
578
+ result: string;
579
+ error?: string | null;
580
+ }>;
581
+ /**
582
+ * GitLab Duo Agent Platform Language Model.
583
+ *
584
+ * Implements LanguageModelV2 by bridging the DWS WebSocket protocol
585
+ * to the Vercel AI SDK stream part format.
586
+ */
587
+ declare class GitLabWorkflowLanguageModel implements LanguageModelV2 {
588
+ readonly specificationVersion: "v2";
589
+ readonly modelId: string;
590
+ readonly supportedUrls: Record<string, RegExp[]>;
591
+ private readonly config;
592
+ private readonly workflowOptions;
593
+ private readonly tokenClient;
594
+ private readonly projectDetector;
595
+ private readonly modelDiscovery;
596
+ private readonly modelCache;
597
+ private detectedProjectPath;
598
+ private currentWorkflowId;
599
+ private persistedAgentEmitted;
600
+ private readonly activeClients;
601
+ private _selectedModelRef?;
602
+ private _selectedModelName?;
603
+ private _rootNamespaceId?;
604
+ private _discoveryPromise?;
605
+ /**
606
+ * Get the cached selected model ref.
607
+ */
608
+ get selectedModelRef(): string | null;
609
+ /**
610
+ * Set the selected model ref (e.g., from an eager discover call).
611
+ * This will be used by resolveModelRef() to skip the picker.
612
+ * Also persists to the file-based workspace cache.
613
+ */
614
+ set selectedModelRef(ref: string | null);
615
+ /**
616
+ * Get the cached selected model display name.
617
+ */
618
+ get selectedModelName(): string | null;
619
+ /**
620
+ * Set the selected model display name.
621
+ * Also persists to the file-based workspace cache.
622
+ */
623
+ set selectedModelName(name: string | null);
624
+ /**
625
+ * Optional external tool executor. When set, this is called for tool
626
+ * requests instead of looking up tools from `options.tools`.
627
+ * This allows the consumer (OpenCode) to wire in its permission system.
628
+ *
629
+ * The executor is automatically bound to the async context at the time
630
+ * it is set, so that AsyncLocalStorage-based contexts (like Instance)
631
+ * remain available when the executor is invoked from WebSocket callbacks.
632
+ */
633
+ private _toolExecutor;
634
+ /**
635
+ * Optional callback invoked with intermediate token usage estimates
636
+ * after each tool execution completes. This allows the consumer to
637
+ * display live token counts during long-running DWS workflows, since
638
+ * the AI SDK only surfaces usage via finish-step at stream end.
639
+ */
640
+ onUsageUpdate: ((usage: {
641
+ inputTokens: number;
642
+ outputTokens: number;
643
+ }) => void) | null;
644
+ /**
645
+ * Optional callback invoked when multiple workflow models are available
646
+ * and the user should pick one. Set per-stream by the host (e.g., OpenCode)
647
+ * alongside `toolExecutor`. Takes precedence over `workflowOptions.onSelectModel`.
648
+ */
649
+ onSelectModel: ((models: AiModel[]) => Promise<string | null | undefined>) | null;
650
+ get toolExecutor(): WorkflowToolExecutor | null;
651
+ set toolExecutor(executor: WorkflowToolExecutor | null);
652
+ constructor(modelId: string, config: GitLabWorkflowLanguageModelConfig, workflowOptions?: GitLabWorkflowOptions);
653
+ get provider(): string;
654
+ /**
655
+ * Resolve the project ID (path) to use for workflow creation.
656
+ * Priority: explicit option > auto-detected from git remote > undefined.
657
+ */
658
+ private resolveProjectId;
659
+ /**
660
+ * Resolve the root namespace GID to use for model discovery.
661
+ *
662
+ * Priority:
663
+ * 1. Explicit `rootNamespaceId` in workflowOptions (caller-provided GID)
664
+ * 2. Auto-detected from git remote via project detector (namespace.id → GID)
665
+ * 3. Cached from previous call
666
+ */
667
+ private resolveRootNamespaceId;
668
+ /**
669
+ * Resolve the effective DWS model ref to use for this stream.
670
+ * Deduplicates concurrent calls via a shared promise.
671
+ *
672
+ * Priority for the canonical `duo-workflow` model ID:
673
+ * 1. Admin-pinned model (from GitLabModelDiscovery) — always wins
674
+ * 2. User selection via onSelectModel callback (if model switching enabled)
675
+ * 3. Workspace default model
676
+ * 4. File-cached discovery/selection — used when live discovery fails
677
+ * 5. Hard-coded 'default' (DWS decides) — fallback when discovery fails
678
+ *
679
+ * For all other `duo-workflow-*` model IDs the static mapping is used as-is.
680
+ */
681
+ private resolveModelRef;
682
+ private doResolveModelRef;
683
+ /**
684
+ * Pre-fetch available models for the workspace.
685
+ * Call this early (e.g., on IDE startup) to avoid blocking the first stream.
686
+ * Results are persisted to the workspace model cache.
687
+ *
688
+ * @param rootNamespaceId - GitLab group ID (e.g., 'gid://gitlab/Group/12345')
689
+ * @returns Discovered models with default, selectable, and pinned models
690
+ */
691
+ discoverModels(rootNamespaceId: string): Promise<DiscoveredModels>;
692
+ /**
693
+ * Get the file-based model cache instance for this workspace.
694
+ * Useful for consumers that need direct cache access (e.g., the discover route).
695
+ */
696
+ getModelCache(): GitLabModelCache;
697
+ /**
698
+ * Stop the active workflow.
699
+ */
700
+ stopWorkflow(): void;
701
+ /**
702
+ * Reset the workflow state, forcing a new workflow to be created on the
703
+ * next doStream() call. Call this when starting a new conversation.
704
+ */
705
+ resetWorkflow(): void;
706
+ /**
707
+ * Get the current workflow ID (if any).
708
+ * Useful for consumers that need to track workflow state.
709
+ */
710
+ get workflowId(): string | null;
711
+ doGenerate(options: LanguageModelV2CallOptions): Promise<{
712
+ content: LanguageModelV2Content[];
713
+ finishReason: LanguageModelV2FinishReason;
714
+ usage: LanguageModelV2Usage;
715
+ warnings: LanguageModelV2CallWarning[];
716
+ }>;
717
+ doStream(options: LanguageModelV2CallOptions): Promise<{
718
+ stream: ReadableStream<LanguageModelV2StreamPart>;
719
+ request?: {
720
+ body?: unknown;
721
+ };
722
+ }>;
723
+ private handleWorkflowEvent;
724
+ private processCheckpoint;
725
+ private executeToolAndRespond;
726
+ private cleanupClient;
727
+ private buildWorkflowMetadata;
728
+ private getGitInfo;
729
+ /**
730
+ * Extract the user's goal (last user message) from the AI SDK prompt.
731
+ */
732
+ private extractGoalFromPrompt;
733
+ /**
734
+ * Convert AI SDK tools to DWS McpToolDefinition format.
735
+ */
736
+ private extractMcpTools;
737
+ private static readonly MAX_START_REQUEST_BYTES;
738
+ /**
739
+ * Trim mcpTools and additionalContext to fit within the DWS 4MB gRPC
740
+ * message size limit (`MAX_MESSAGE_SIZE` in duo_workflow_service/server.py).
741
+ *
742
+ * DWS has no per-field limits on tool descriptions, schemas, or context items.
743
+ * The only hard constraint is the total serialized message size.
744
+ *
745
+ * Strategy (progressive, only if over budget):
746
+ * 1. Send everything as-is
747
+ * 2. Simplify tool input schemas (strip descriptions from properties)
748
+ * 3. Strip schemas to minimal form (type + property names only)
749
+ * 4. Drop tools from the end until it fits
750
+ */
751
+ private trimPayload;
752
+ private buildAdditionalContext;
753
+ }
754
+
755
+ interface GitLabProvider {
756
+ (modelId: string): LanguageModelV2;
757
+ readonly specificationVersion: 'v2';
758
+ languageModel(modelId: string): LanguageModelV2;
759
+ chat(modelId: string): LanguageModelV2;
760
+ /**
761
+ * Create an agentic chat model with tool calling support
762
+ *
763
+ * @param modelId - GitLab model identifier. Some IDs automatically map to specific Anthropic models.
764
+ * @param options - Configuration options for the agentic model
765
+ * @returns A language model with native tool calling support via Anthropic
766
+ *
767
+ * @example
768
+ * // Automatic model mapping
769
+ * const model = gitlab.agenticChat('duo-chat-opus-4-5');
770
+ * // Uses claude-opus-4-5-20251101
771
+ *
772
+ * @example
773
+ * // Explicit model override
774
+ * const model = gitlab.agenticChat('duo-chat', {
775
+ * anthropicModel: 'claude-sonnet-4-5-20250929'
776
+ * });
777
+ */
778
+ agenticChat(modelId: string, options?: GitLabAgenticOptions): GitLabAnthropicLanguageModel;
779
+ /**
780
+ * Create a workflow chat model using GitLab Duo Agent Platform.
781
+ *
782
+ * Workflow models use a server-side agentic loop where GitLab's DWS drives
783
+ * the LLM, requests tool executions from the client via WebSocket, and
784
+ * streams text/status back.
785
+ *
786
+ * Requires GitLab Ultimate with Duo Enterprise add-on.
787
+ *
788
+ * @param modelId - Workflow model identifier (e.g., 'duo-workflow-sonnet-4-6')
789
+ * @param options - Workflow-specific configuration
790
+ * @returns A language model backed by the DWS WebSocket protocol
791
+ *
792
+ * @example
793
+ * const model = gitlab.workflowChat('duo-workflow-sonnet-4-6', {
794
+ * mcpTools: [...],
795
+ * preapprovedTools: ['read_file', 'write_file'],
796
+ * });
797
+ */
798
+ workflowChat(modelId: string, options?: GitLabWorkflowOptions): GitLabWorkflowLanguageModel;
799
+ textEmbeddingModel(modelId: string): never;
800
+ imageModel(modelId: string): never;
801
+ }
802
+ interface GitLabAgenticOptions {
803
+ /**
804
+ * Override the provider-specific model (optional).
805
+ * Must be a valid model for the detected provider.
806
+ *
807
+ * For Anthropic models:
808
+ * - 'claude-opus-4-6'
809
+ * - 'claude-sonnet-4-6'
810
+ * - 'claude-opus-4-5-20251101'
811
+ * - 'claude-sonnet-4-5-20250929'
812
+ * - 'claude-haiku-4-5-20251001'
813
+ *
814
+ * For OpenAI models:
815
+ * - 'gpt-5.1-2025-11-13'
816
+ * - 'gpt-5-mini-2025-08-07'
817
+ * - 'gpt-5-codex'
818
+ * - 'gpt-5.2-codex'
819
+ *
820
+ * @example
821
+ * // Override with explicit model
822
+ * const model = gitlab.agenticChat('duo-chat-opus-4-5', {
823
+ * providerModel: 'claude-sonnet-4-5-20250929'
824
+ * });
825
+ */
826
+ providerModel?: string;
827
+ /**
828
+ * Maximum tokens to generate
829
+ * @default 8192
830
+ */
831
+ maxTokens?: number;
832
+ /**
833
+ * Feature flags to pass to the GitLab API
834
+ */
835
+ featureFlags?: Record<string, boolean>;
836
+ /**
837
+ * Custom headers for AI Gateway requests (per-model override).
838
+ * These headers are sent to the Anthropic/OpenAI proxy endpoints.
839
+ * Merged with provider-level aiGatewayHeaders (model-level takes precedence).
840
+ */
841
+ aiGatewayHeaders?: Record<string, string>;
842
+ }
843
+ interface GitLabProviderSettings {
844
+ /**
845
+ * GitLab instance URL (e.g., 'https://gitlab.com')
846
+ * Can also be set via GITLAB_INSTANCE_URL environment variable.
847
+ * @default 'https://gitlab.com'
848
+ */
849
+ instanceUrl?: string;
850
+ /**
851
+ * API token (Personal Access Token or OAuth access token)
852
+ * Can also be set via GITLAB_TOKEN environment variable
853
+ */
854
+ apiKey?: string;
855
+ /**
856
+ * OAuth refresh token (optional, for OAuth flow)
857
+ */
858
+ refreshToken?: string;
859
+ /**
860
+ * OAuth client ID (required for OAuth flow)
861
+ */
862
+ clientId?: string;
863
+ /**
864
+ * OAuth redirect URI (required for OAuth flow)
865
+ */
866
+ redirectUri?: string;
867
+ /**
868
+ * Custom headers to include in requests
869
+ */
870
+ headers?: Record<string, string>;
871
+ /**
872
+ * Custom fetch implementation
873
+ */
874
+ fetch?: typeof fetch;
875
+ /**
876
+ * Provider name override
877
+ */
878
+ name?: string;
879
+ /**
880
+ * Default feature flags to pass to the GitLab API for all agentic chat models
881
+ */
882
+ featureFlags?: Record<string, boolean>;
883
+ /**
884
+ * AI Gateway URL for the Anthropic proxy.
885
+ * Can also be set via GITLAB_AI_GATEWAY_URL environment variable.
886
+ * @default 'https://cloud.gitlab.com'
887
+ */
888
+ aiGatewayUrl?: string;
889
+ /**
890
+ * Custom headers to include in AI Gateway requests (Anthropic/OpenAI proxy).
891
+ * These headers are merged with the default headers from direct_access response.
892
+ * Default User-Agent: gitlab-ai-provider/{version}
893
+ */
894
+ aiGatewayHeaders?: Record<string, string>;
895
+ }
896
+ declare function createGitLab(options?: GitLabProviderSettings): GitLabProvider;
897
+ /**
898
+ * Default GitLab Duo provider instance
899
+ *
900
+ * @example
901
+ * ```typescript
902
+ * import { gitlab } from '@ai-sdk/gitlab';
903
+ *
904
+ * const model = gitlab('duo-chat');
905
+ * ```
906
+ */
907
+ declare const gitlab: GitLabProvider;
908
+
909
+ declare const VERSION: string;
910
+
911
+ interface GitLabOpenAIConfig {
912
+ provider: string;
913
+ instanceUrl: string;
914
+ getHeaders: () => Record<string, string>;
915
+ fetch?: typeof fetch;
916
+ refreshApiKey?: () => Promise<void>;
917
+ openaiModel?: string;
918
+ maxTokens?: number;
919
+ featureFlags?: {
920
+ DuoAgentPlatformNext: true;
921
+ } & Record<string, boolean>;
922
+ aiGatewayUrl?: string;
923
+ /** Whether to use the Responses API instead of Chat Completions API */
924
+ useResponsesApi?: boolean;
925
+ /**
926
+ * Custom headers for AI Gateway OpenAI proxy requests.
927
+ * Merged with headers from direct_access token response.
928
+ */
929
+ aiGatewayHeaders?: Record<string, string>;
930
+ }
931
+ declare class GitLabOpenAILanguageModel implements LanguageModelV2 {
932
+ readonly specificationVersion: "v2";
933
+ readonly modelId: string;
934
+ readonly supportedUrls: Record<string, RegExp[]>;
935
+ private readonly config;
936
+ private readonly directAccessClient;
937
+ private readonly useResponsesApi;
938
+ private openaiClient;
939
+ constructor(modelId: string, config: GitLabOpenAIConfig);
940
+ get provider(): string;
941
+ private getOpenAIClient;
942
+ private isTokenError;
943
+ /**
944
+ * Check if an error is a context overflow error (prompt too long)
945
+ * These should NOT trigger token refresh and should be reported to the user.
946
+ */
947
+ private isContextOverflowError;
948
+ private convertTools;
949
+ private convertToolChoice;
950
+ private convertPrompt;
951
+ private convertFinishReason;
952
+ /**
953
+ * Convert tools to Responses API format
954
+ */
955
+ private convertToolsForResponses;
956
+ /**
957
+ * Convert prompt to Responses API input format
958
+ */
959
+ private convertPromptForResponses;
960
+ /**
961
+ * Extract system instructions from prompt
962
+ */
963
+ private extractSystemInstructions;
964
+ /**
965
+ * Convert Responses API status to finish reason
966
+ * Note: Responses API returns 'completed' even when making tool calls,
967
+ * so we need to check the content for tool calls separately.
968
+ */
969
+ private convertResponsesStatus;
970
+ doGenerate(options: LanguageModelV2CallOptions): Promise<{
971
+ content: LanguageModelV2Content[];
972
+ finishReason: LanguageModelV2FinishReason;
973
+ usage: LanguageModelV2Usage;
974
+ warnings: LanguageModelV2CallWarning[];
975
+ }>;
976
+ private doGenerateWithChatApi;
977
+ private doGenerateWithResponsesApi;
978
+ doStream(options: LanguageModelV2CallOptions): Promise<{
979
+ stream: ReadableStream<LanguageModelV2StreamPart>;
980
+ request?: {
981
+ body?: unknown;
982
+ };
983
+ response?: {
984
+ headers?: Record<string, string>;
985
+ };
986
+ }>;
987
+ private doStreamWithChatApi;
988
+ private doStreamWithResponsesApi;
989
+ }
990
+
991
+ type ModelProvider = 'anthropic' | 'openai' | 'workflow';
992
+ type OpenAIApiType = 'chat' | 'responses';
993
+ interface ModelMapping {
994
+ provider: ModelProvider;
995
+ model: string;
996
+ /** For OpenAI models, which API to use: 'chat' for /v1/chat/completions, 'responses' for /v1/responses */
997
+ openaiApiType?: OpenAIApiType;
998
+ }
999
+ declare const MODEL_MAPPINGS: Record<string, ModelMapping>;
1000
+ declare function getModelMapping(modelId: string): ModelMapping | undefined;
1001
+ declare function getProviderForModelId(modelId: string): ModelProvider | undefined;
1002
+ declare function getValidModelsForProvider(provider: ModelProvider): string[];
1003
+ declare function getAnthropicModelForModelId(modelId: string): string | undefined;
1004
+ declare function getOpenAIModelForModelId(modelId: string): string | undefined;
1005
+ declare function getOpenAIApiType(modelId: string): OpenAIApiType;
1006
+ declare function isResponsesApiModel(modelId: string): boolean;
1007
+ declare function isWorkflowModel(modelId: string): boolean;
1008
+ declare function getWorkflowModelRef(modelId: string): string | undefined;
1009
+ declare const MODEL_ID_TO_ANTHROPIC_MODEL: Record<string, string>;
1010
+
1011
+ interface GitLabErrorOptions {
1012
+ message: string;
1013
+ statusCode?: number;
1014
+ responseBody?: string;
1015
+ cause?: unknown;
1016
+ }
1017
+ declare class GitLabError extends Error {
1018
+ readonly statusCode?: number;
1019
+ readonly responseBody?: string;
1020
+ readonly cause?: unknown;
1021
+ constructor(options: GitLabErrorOptions);
1022
+ static fromResponse(response: Response, body: string): GitLabError;
1023
+ isAuthError(): boolean;
1024
+ isRateLimitError(): boolean;
1025
+ isForbiddenError(): boolean;
1026
+ isServerError(): boolean;
1027
+ /**
1028
+ * Check if this error is a context overflow error (prompt too long).
1029
+ * These errors occur when the conversation exceeds the model's token limit.
1030
+ */
1031
+ isContextOverflowError(): boolean;
1032
+ }
1033
+
1034
+ declare const gitlabOAuthTokenResponseSchema: z.ZodObject<{
1035
+ access_token: z.ZodString;
1036
+ refresh_token: z.ZodOptional<z.ZodString>;
1037
+ expires_in: z.ZodNumber;
1038
+ created_at: z.ZodNumber;
1039
+ }, "strip", z.ZodTypeAny, {
1040
+ created_at?: number;
1041
+ access_token?: string;
1042
+ refresh_token?: string;
1043
+ expires_in?: number;
1044
+ }, {
1045
+ created_at?: number;
1046
+ access_token?: string;
1047
+ refresh_token?: string;
1048
+ expires_in?: number;
1049
+ }>;
1050
+ type GitLabOAuthTokenResponse = z.infer<typeof gitlabOAuthTokenResponseSchema>;
1051
+
1052
+ /**
1053
+ * OAuth types and constants for GitLab authentication
1054
+ * Based on gitlab-vscode-extension and gitlab-lsp patterns
1055
+ */
1056
+ interface GitLabOAuthTokens {
1057
+ accessToken: string;
1058
+ refreshToken: string;
1059
+ expiresAt: number;
1060
+ instanceUrl: string;
1061
+ }
1062
+ interface OpenCodeAuthOAuth {
1063
+ type: 'oauth';
1064
+ refresh: string;
1065
+ access: string;
1066
+ expires: number;
1067
+ /** @deprecated Use enterpriseUrl instead. Kept for backwards compatibility with older auth.json files. */
1068
+ instanceUrl?: string;
1069
+ /** Instance URL as written by opencode-gitlab-auth plugin (e.g. 'https://gitlab.com') */
1070
+ enterpriseUrl?: string;
1071
+ }
1072
+ interface OpenCodeAuthApi {
1073
+ type: 'api';
1074
+ key: string;
1075
+ }
1076
+ type OpenCodeAuth = OpenCodeAuthOAuth | OpenCodeAuthApi;
1077
+ /**
1078
+ * Default OAuth client ID for GitLab.com
1079
+ * This is the same client ID used by opencode-gitlab-auth plugin.
1080
+ * The GITLAB_OAUTH_CLIENT_ID env var takes precedence if set.
1081
+ * Note: VS Code extension uses a different client ID ('36f2a70c...') but we use the opencode plugin's ID
1082
+ * to ensure token refresh works correctly with tokens created by the auth plugin.
1083
+ */
1084
+ declare const OPENCODE_GITLAB_AUTH_CLIENT_ID = "1d89f9fdb23ee96d4e603201f6861dab6e143c5c3c00469a018a2d94bdc03d4e";
1085
+ /**
1086
+ * @deprecated Use OPENCODE_GITLAB_AUTH_CLIENT_ID instead. This is the VS Code extension's client ID
1087
+ * and will cause refresh failures if used with tokens created by opencode-gitlab-auth.
1088
+ */
1089
+ declare const BUNDLED_CLIENT_ID = "36f2a70cddeb5a0889d4fd8295c241b7e9848e89cf9e599d0eed2d8e5350fbf5";
1090
+ /**
1091
+ * GitLab.com URL constant
1092
+ */
1093
+ declare const GITLAB_COM_URL = "https://gitlab.com";
1094
+ /**
1095
+ * Token expiry skew in milliseconds (5 minutes)
1096
+ * Refresh tokens this many milliseconds before they expire
1097
+ */
1098
+ declare const TOKEN_EXPIRY_SKEW_MS: number;
1099
+ /**
1100
+ * OAuth scopes to request
1101
+ */
1102
+ declare const OAUTH_SCOPES: string[];
1103
+
1104
+ /**
1105
+ * GitLab OAuth Manager
1106
+ * Handles OAuth token management, refresh, and exchange
1107
+ * Based on gitlab-vscode-extension TokenExchangeService and gitlab-lsp OAuthClientProvider
1108
+ */
1109
+
1110
+ interface TokenExchangeParams {
1111
+ instanceUrl: string;
1112
+ clientId?: string;
1113
+ redirectUri?: string;
1114
+ }
1115
+ interface AuthorizationCodeParams extends TokenExchangeParams {
1116
+ code: string;
1117
+ codeVerifier: string;
1118
+ }
1119
+ interface RefreshTokenParams extends TokenExchangeParams {
1120
+ refreshToken: string;
1121
+ }
1122
+ declare class GitLabOAuthManager {
1123
+ private fetch;
1124
+ constructor(fetchImpl?: typeof fetch);
1125
+ /**
1126
+ * Check if a token is expired
1127
+ */
1128
+ isTokenExpired(expiresAt: number): boolean;
1129
+ /**
1130
+ * Check if a token needs refresh (within skew window)
1131
+ */
1132
+ needsRefresh(expiresAt: number): boolean;
1133
+ /**
1134
+ * Refresh tokens if needed
1135
+ * Returns the same tokens if refresh is not needed, or new tokens if refreshed
1136
+ */
1137
+ refreshIfNeeded(tokens: GitLabOAuthTokens, clientId?: string): Promise<GitLabOAuthTokens>;
1138
+ /**
1139
+ * Exchange authorization code for tokens
1140
+ * Based on gitlab-vscode-extension createOAuthAccountFromCode
1141
+ */
1142
+ exchangeAuthorizationCode(params: AuthorizationCodeParams): Promise<GitLabOAuthTokens>;
1143
+ /**
1144
+ * Exchange refresh token for new tokens
1145
+ * Based on gitlab-vscode-extension TokenExchangeService
1146
+ */
1147
+ exchangeRefreshToken(params: RefreshTokenParams): Promise<GitLabOAuthTokens>;
1148
+ /**
1149
+ * Get the OAuth client ID for an instance.
1150
+ * Priority: env var > opencode-gitlab-auth default (for GitLab.com).
1151
+ * Note: callers (e.g. exchangeRefreshToken) may pass an explicit clientId
1152
+ * that bypasses this method entirely.
1153
+ */
1154
+ private getClientId;
1155
+ /**
1156
+ * Exchange token with GitLab OAuth endpoint
1157
+ * Based on gitlab-vscode-extension GitLabService.exchangeToken
1158
+ */
1159
+ private exchangeToken;
1160
+ /**
1161
+ * Create GitLabOAuthTokens from token response
1162
+ */
1163
+ private createTokensFromResponse;
1164
+ /**
1165
+ * Create expiry timestamp from token response
1166
+ * Based on gitlab-vscode-extension createExpiresTimestamp
1167
+ */
1168
+ private createExpiresTimestamp;
1169
+ }
1170
+
1171
+ /**
1172
+ * Simple in-memory cache for GitLab project information
1173
+ * Used to avoid repeated API calls when detecting projects from git remotes
1174
+ */
1175
+ interface GitLabProject {
1176
+ id: number;
1177
+ path: string;
1178
+ pathWithNamespace: string;
1179
+ name: string;
1180
+ namespaceId?: number;
1181
+ }
1182
+ /**
1183
+ * In-memory cache for GitLab project information with TTL support
1184
+ */
1185
+ declare class GitLabProjectCache {
1186
+ private cache;
1187
+ private defaultTTL;
1188
+ /**
1189
+ * Create a new project cache
1190
+ * @param defaultTTL - Default time-to-live in milliseconds (default: 5 minutes)
1191
+ */
1192
+ constructor(defaultTTL?: number);
1193
+ /**
1194
+ * Get a cached project by key
1195
+ * @param key - Cache key (typically the working directory path)
1196
+ * @returns The cached project or null if not found or expired
1197
+ */
1198
+ get(key: string): GitLabProject | null;
1199
+ /**
1200
+ * Store a project in the cache
1201
+ * @param key - Cache key (typically the working directory path)
1202
+ * @param project - The project to cache
1203
+ * @param ttl - Optional custom TTL in milliseconds
1204
+ */
1205
+ set(key: string, project: GitLabProject, ttl?: number): void;
1206
+ /**
1207
+ * Check if a key exists in the cache (and is not expired)
1208
+ * @param key - Cache key to check
1209
+ * @returns true if the key exists and is not expired
1210
+ */
1211
+ has(key: string): boolean;
1212
+ /**
1213
+ * Remove a specific entry from the cache
1214
+ * @param key - Cache key to remove
1215
+ */
1216
+ delete(key: string): void;
1217
+ /**
1218
+ * Clear all entries from the cache
1219
+ */
1220
+ clear(): void;
1221
+ /**
1222
+ * Get the number of entries in the cache (including expired ones)
1223
+ */
1224
+ get size(): number;
1225
+ /**
1226
+ * Clean up expired entries from the cache
1227
+ * This is useful for long-running processes to prevent memory leaks
1228
+ */
1229
+ cleanup(): void;
1230
+ }
1231
+
1232
+ interface GitLabProjectDetectorConfig {
1233
+ instanceUrl: string;
1234
+ getHeaders: () => Record<string, string>;
1235
+ fetch?: typeof fetch;
1236
+ cache?: GitLabProjectCache;
1237
+ gitTimeout?: number;
1238
+ }
1239
+ /**
1240
+ * Detects GitLab project information from git remote URLs
1241
+ *
1242
+ * This class provides functionality to:
1243
+ * - Parse git remote URLs (SSH, HTTPS, custom domains)
1244
+ * - Execute git commands to get remote URLs
1245
+ * - Fetch project details from GitLab API
1246
+ * - Cache project information to avoid repeated API calls
1247
+ */
1248
+ declare class GitLabProjectDetector {
1249
+ private readonly config;
1250
+ private readonly fetchFn;
1251
+ private readonly cache;
1252
+ constructor(config: GitLabProjectDetectorConfig);
1253
+ /**
1254
+ * Auto-detect GitLab project from git remote in the working directory
1255
+ *
1256
+ * @param workingDirectory - The directory to check for git remote
1257
+ * @param remoteName - The git remote name to use (default: 'origin')
1258
+ * @returns The detected project or null if not a git repo / no matching remote
1259
+ * @throws GitLabError if the API call or an unexpected error occurs
1260
+ */
1261
+ detectProject(workingDirectory: string, remoteName?: string): Promise<GitLabProject | null>;
1262
+ /**
1263
+ * Parse a git remote URL to extract the project path
1264
+ *
1265
+ * Supports:
1266
+ * - SSH: git@gitlab.com:namespace/project.git
1267
+ * - HTTPS: https://gitlab.com/namespace/project.git
1268
+ * - HTTP: http://gitlab.local/namespace/project.git
1269
+ * - Custom domains and ports
1270
+ *
1271
+ * @param remoteUrl - The git remote URL
1272
+ * @param instanceUrl - The GitLab instance URL to match against
1273
+ * @returns The project path (e.g., "namespace/project") or null if parsing fails
1274
+ */
1275
+ parseGitRemoteUrl(remoteUrl: string, instanceUrl: string): string | null;
1276
+ /**
1277
+ * Get the git remote URL from a working directory
1278
+ *
1279
+ * @param workingDirectory - The directory to check
1280
+ * @param remoteName - The git remote name (default: 'origin')
1281
+ * @returns The remote URL or null if not found
1282
+ */
1283
+ getGitRemoteUrl(workingDirectory: string, remoteName?: string): Promise<string | null>;
1284
+ /**
1285
+ * Fetch project details from GitLab API by project path
1286
+ *
1287
+ * @param projectPath - The project path (e.g., "namespace/project")
1288
+ * @returns The project details
1289
+ * @throws GitLabError if the API call fails
1290
+ */
1291
+ getProjectByPath(projectPath: string): Promise<GitLabProject>;
1292
+ /**
1293
+ * Clear the project cache
1294
+ */
1295
+ clearCache(): void;
1296
+ /**
1297
+ * Get the cache instance (useful for testing)
1298
+ */
1299
+ getCache(): GitLabProjectCache;
1300
+ }
1301
+
1302
+ /**
1303
+ * Response from /api/v4/ai/third_party_agents/direct_access
1304
+ */
1305
+ declare const directAccessTokenSchema: z.ZodObject<{
1306
+ headers: z.ZodRecord<z.ZodString, z.ZodString>;
1307
+ token: z.ZodString;
1308
+ }, "strip", z.ZodTypeAny, {
1309
+ headers?: Record<string, string>;
1310
+ token?: string;
1311
+ }, {
1312
+ headers?: Record<string, string>;
1313
+ token?: string;
1314
+ }>;
1315
+ type DirectAccessToken = z.infer<typeof directAccessTokenSchema>;
1316
+ declare const DEFAULT_AI_GATEWAY_URL = "https://cloud.gitlab.com";
1317
+ interface GitLabDirectAccessConfig {
1318
+ instanceUrl: string;
1319
+ getHeaders: () => Record<string, string>;
1320
+ fetch?: typeof fetch;
1321
+ /**
1322
+ * Optional callback to refresh the API key when a 401 error occurs.
1323
+ * Should clear cached credentials and re-fetch from auth provider.
1324
+ */
1325
+ refreshApiKey?: () => Promise<void>;
1326
+ /**
1327
+ * Feature flags to pass to the GitLab API
1328
+ */
1329
+ featureFlags?: Record<string, boolean>;
1330
+ /**
1331
+ * AI Gateway URL for the Anthropic proxy.
1332
+ * Can also be set via GITLAB_AI_GATEWAY_URL environment variable.
1333
+ * @default 'https://cloud.gitlab.com'
1334
+ */
1335
+ aiGatewayUrl?: string;
1336
+ }
1337
+ /**
1338
+ * Client for GitLab's third-party agents direct access API.
1339
+ * This allows routing requests through GitLab's proxy to Anthropic.
1340
+ */
1341
+ declare class GitLabDirectAccessClient {
1342
+ private readonly config;
1343
+ private readonly fetchFn;
1344
+ private readonly aiGatewayUrl;
1345
+ private cachedToken;
1346
+ private tokenExpiresAt;
1347
+ constructor(config: GitLabDirectAccessConfig);
1348
+ /**
1349
+ * Get a direct access token for the Anthropic proxy.
1350
+ * Tokens are cached for 25 minutes (they expire after 30 minutes).
1351
+ * @param forceRefresh - If true, ignores the cache and fetches a new token
1352
+ */
1353
+ getDirectAccessToken(forceRefresh?: boolean): Promise<DirectAccessToken>;
1354
+ /**
1355
+ * Get the Anthropic proxy base URL
1356
+ */
1357
+ getAnthropicProxyUrl(): string;
1358
+ /**
1359
+ * Get the OpenAI proxy base URL
1360
+ * Note: The OpenAI SDK expects a base URL like https://api.openai.com/v1
1361
+ * and appends paths like /chat/completions. So we need /v1 at the end.
1362
+ */
1363
+ getOpenAIProxyUrl(): string;
1364
+ /**
1365
+ * Invalidate the cached token
1366
+ */
1367
+ invalidateToken(): void;
1368
+ }
1369
+
1370
+ /**
1371
+ * WebSocket client for the GitLab Duo Agent Platform (DWS).
1372
+ *
1373
+ * Handles:
1374
+ * - WebSocket connection to `wss://{instance}/api/v4/ai/duo_workflows/ws`
1375
+ * - Sending ClientEvent messages (startRequest, actionResponse, stopWorkflow)
1376
+ * - Receiving WorkflowAction messages (newCheckpoint, runMcpTool, built-in tools)
1377
+ * - Dual heartbeat: ws.ping(45s) + JSON heartbeat(60s) — matching gitlab-lsp
1378
+ * - No reconnection on drop (matches gitlab-lsp behavior)
1379
+ */
1380
+
1381
+ interface WorkflowWebSocketOptions {
1382
+ /** GitLab instance URL */
1383
+ instanceUrl: string;
1384
+ /** Model reference for DWS (e.g. 'anthropic/claude-sonnet-4-5-20250929' or 'default') */
1385
+ modelRef: string;
1386
+ /** Auth headers — must include Authorization */
1387
+ headers: Record<string, string>;
1388
+ /** Optional correlation ID */
1389
+ requestId?: string;
1390
+ /** Optional project context */
1391
+ projectId?: string;
1392
+ namespaceId?: string;
1393
+ rootNamespaceId?: string;
1394
+ }
1395
+ type EventCallback = (event: WorkflowClientEvent) => void;
1396
+ declare class GitLabWorkflowClient {
1397
+ private socket;
1398
+ private keepaliveInterval;
1399
+ private heartbeatInterval;
1400
+ private eventCallback;
1401
+ private closed;
1402
+ private lastSendTime;
1403
+ /**
1404
+ * Connect to the DWS WebSocket and start listening for events.
1405
+ *
1406
+ * @param options - Connection parameters
1407
+ * @param onEvent - Callback invoked for each WorkflowClientEvent
1408
+ * @returns Promise that resolves when the connection is open
1409
+ */
1410
+ connect(options: WorkflowWebSocketOptions, onEvent: EventCallback): Promise<void>;
1411
+ /**
1412
+ * Send a startRequest to begin the workflow.
1413
+ */
1414
+ sendStartRequest(request: StartRequest): void;
1415
+ /**
1416
+ * Send an actionResponse (tool result) back to DWS.
1417
+ */
1418
+ sendActionResponse(requestID: string, response: string, error?: string | null): void;
1419
+ /**
1420
+ * Stop the workflow gracefully.
1421
+ */
1422
+ stop(): void;
1423
+ /**
1424
+ * Close the WebSocket connection.
1425
+ */
1426
+ close(): void;
1427
+ /**
1428
+ * Check if the WebSocket is currently connected.
1429
+ */
1430
+ get isConnected(): boolean;
1431
+ private validateOptions;
1432
+ private buildWebSocketUrl;
1433
+ private buildWebSocketHeaders;
1434
+ private handleAction;
1435
+ private send;
1436
+ private sendHeartbeatIfNeeded;
1437
+ private emit;
1438
+ /**
1439
+ * Start ws.ping() keepalive (45s interval).
1440
+ * Keeps TCP connection alive through proxies/load balancers.
1441
+ */
1442
+ private startKeepalive;
1443
+ /**
1444
+ * Start application-level heartbeat (60s interval).
1445
+ * Prevents DWS from timing out the workflow.
1446
+ */
1447
+ private startHeartbeat;
1448
+ private cleanedUp;
1449
+ /**
1450
+ * Clean up intervals. Idempotent — safe to call multiple times.
1451
+ */
1452
+ private cleanup;
1453
+ }
1454
+
1455
+ /**
1456
+ * Token management for the GitLab Duo Agent Platform (DWS).
1457
+ *
1458
+ * Handles two API calls:
1459
+ * 1. POST /api/v4/ai/duo_workflows/direct_access → GenerateTokenResponse (workflow token)
1460
+ * 2. POST /api/v4/ai/duo_workflows/workflows → CreateWorkflowResponse (workflow ID)
1461
+ *
1462
+ * Tokens are cached for 25 minutes (they expire after ~30 minutes).
1463
+ * On 401, optionally triggers an API key refresh callback and retries once.
1464
+ * On 403, throws a clear error explaining GitLab Duo Enterprise requirements.
1465
+ */
1466
+
1467
+ declare class GitLabWorkflowTokenClient {
1468
+ private readonly config;
1469
+ private readonly fetchFn;
1470
+ /**
1471
+ * Token cache keyed by workflow definition type.
1472
+ *
1473
+ * - CHAT workflows use a shared key (CHAT_SHARED_TOKEN_KEY) so tokens
1474
+ * are reused across ALL chat sessions (matching gitlab-lsp behavior).
1475
+ * - SOFTWARE_DEVELOPMENT workflows would use per-workflow-id keys,
1476
+ * but since we fetch tokens before creating workflows, we key by type.
1477
+ */
1478
+ private tokenCache;
1479
+ constructor(config: GitLabWorkflowClientConfig);
1480
+ /**
1481
+ * Resolve the cache key for a given workflow definition.
1482
+ * CHAT workflows share a single token per namespace; other types get per-type keys.
1483
+ */
1484
+ private getCacheKey;
1485
+ /**
1486
+ * Get a DWS token, using cached value if still valid.
1487
+ *
1488
+ * Token caching strategy (matches gitlab-lsp):
1489
+ * - CHAT workflows: shared token across all sessions
1490
+ * - Other workflows: per-type token
1491
+ *
1492
+ * @param workflowDefinition - Workflow type (default: 'chat')
1493
+ * @param rootNamespaceId - Optional root namespace for scoping
1494
+ * @param forceRefresh - Bypass cache
1495
+ */
1496
+ getToken(workflowDefinition?: string, rootNamespaceId?: string, forceRefresh?: boolean): Promise<GenerateTokenResponse>;
1497
+ /**
1498
+ * Create a new workflow on the GitLab instance.
1499
+ *
1500
+ * @param goal - The user's message / goal for this workflow
1501
+ * @param options - Additional workflow creation options
1502
+ * @returns The created workflow's ID
1503
+ */
1504
+ createWorkflow(goal: string, options?: {
1505
+ projectId?: string;
1506
+ namespaceId?: string;
1507
+ workflowDefinition?: string;
1508
+ agentPrivileges?: number[];
1509
+ environment?: string;
1510
+ allowAgentToRequestUser?: boolean;
1511
+ }): Promise<string>;
1512
+ /**
1513
+ * Invalidate cached tokens.
1514
+ *
1515
+ * @param workflowDefinition - If provided, only invalidate for this type.
1516
+ * If omitted, clears ALL cached tokens.
1517
+ */
1518
+ invalidateToken(workflowDefinition?: string, rootNamespaceId?: string): void;
1519
+ }
1520
+
1521
+ 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, 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 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 };