gitlab-ai-provider 6.0.0 → 6.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.mjs CHANGED
@@ -1582,7 +1582,7 @@ var GitLabOpenAILanguageModel = class {
1582
1582
  import WebSocket from "isomorphic-ws";
1583
1583
 
1584
1584
  // src/version.ts
1585
- var VERSION = true ? "5.3.3" : "0.0.0-dev";
1585
+ var VERSION = true ? "6.1.0" : "0.0.0-dev";
1586
1586
 
1587
1587
  // src/gitlab-workflow-types.ts
1588
1588
  var WorkflowType = /* @__PURE__ */ ((WorkflowType2) => {
@@ -1808,7 +1808,9 @@ var GitLabWorkflowClient = class {
1808
1808
  });
1809
1809
  } else if (checkpoint.status === "STOPPED" || checkpoint.status === "CANCELLED") {
1810
1810
  this.emit({ type: "completed" });
1811
- } else if (checkpoint.status === "TOOL_CALL_APPROVAL_REQUIRED" || checkpoint.status === "PLAN_APPROVAL_REQUIRED") {
1811
+ } else if (checkpoint.status === "TOOL_CALL_APPROVAL_REQUIRED") {
1812
+ this.emit({ type: "approval-required", tools: this.extractApprovalTools(checkpoint) });
1813
+ } else if (checkpoint.status === "PLAN_APPROVAL_REQUIRED") {
1812
1814
  this.emit({ type: "completed" });
1813
1815
  }
1814
1816
  return;
@@ -1847,6 +1849,18 @@ var GitLabWorkflowClient = class {
1847
1849
  }
1848
1850
  }
1849
1851
  }
1852
+ extractApprovalTools(checkpoint) {
1853
+ if (!checkpoint.checkpoint) return [];
1854
+ let parsed;
1855
+ try {
1856
+ parsed = JSON.parse(checkpoint.checkpoint);
1857
+ } catch {
1858
+ return [];
1859
+ }
1860
+ return (parsed.channel_values?.ui_chat_log ?? []).filter(
1861
+ (e) => e.message_type === "request" && e.tool_info !== null
1862
+ ).map((e) => ({ name: e.tool_info.name, args: JSON.stringify(e.tool_info.args) }));
1863
+ }
1850
1864
  send(event) {
1851
1865
  if (this.socket?.readyState === WebSocket.OPEN) {
1852
1866
  const json = JSON.stringify(event);
@@ -3018,6 +3032,23 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3018
3032
  * the AI SDK only surfaces usage via finish-step at stream end.
3019
3033
  */
3020
3034
  onUsageUpdate = null;
3035
+ /**
3036
+ * Tool names pre-approved for the current session.
3037
+ * Set by the host (e.g., opencode) and merged into preapproved_tools on each StartRequest.
3038
+ * Updated when the user chooses "always" in the approval prompt.
3039
+ */
3040
+ sessionPreapprovedTools = [];
3041
+ /**
3042
+ * Set the approval handler callback.
3043
+ * Called when DWS requires tool call approval. Host (e.g., opencode) wires this
3044
+ * to its permission system each stream call, similar to toolExecutor.
3045
+ */
3046
+ set approvalHandler(handler) {
3047
+ this.workflowOptions.approvalHandler = handler ?? void 0;
3048
+ }
3049
+ get approvalHandler() {
3050
+ return this.workflowOptions.approvalHandler ?? null;
3051
+ }
3021
3052
  /**
3022
3053
  * Optional callback invoked when multiple workflow models are available
3023
3054
  * and the user should pick one. Set per-stream by the host (e.g., OpenCode)
@@ -3319,7 +3350,10 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3319
3350
  const goal = this.extractGoalFromPrompt(options.prompt);
3320
3351
  const modelRef = await this.resolveModelRef();
3321
3352
  const mcpTools = this.extractMcpTools(options);
3322
- const preapprovedTools = this.workflowOptions.preapprovedTools ?? mcpTools.map((t) => t.name);
3353
+ const preapprovedTools = [
3354
+ ...this.workflowOptions.preapprovedTools ?? mcpTools.map((t) => t.name),
3355
+ ...this.sessionPreapprovedTools
3356
+ ];
3323
3357
  const additionalContext = this.buildAdditionalContext(options.prompt);
3324
3358
  const toolExecutor = this.toolExecutor ?? null;
3325
3359
  const availableToolNames = new Set(options.tools?.map((t) => t.name) ?? []);
@@ -3335,7 +3369,8 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3335
3369
  workflowId = await this.tokenClient.createWorkflow(goal, {
3336
3370
  projectId,
3337
3371
  namespaceId: this.workflowOptions.namespaceId,
3338
- workflowDefinition: this.workflowOptions.workflowDefinition
3372
+ workflowDefinition: this.workflowOptions.workflowDefinition,
3373
+ agentPrivileges: this.workflowOptions.agentPrivileges
3339
3374
  });
3340
3375
  this.currentWorkflowId = workflowId;
3341
3376
  }
@@ -3347,11 +3382,13 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3347
3382
  streamedInputChars: 0,
3348
3383
  streamedOutputChars: 0,
3349
3384
  pendingToolCount: 0,
3385
+ approvalPending: false,
3350
3386
  deferredClose: null,
3351
3387
  activeTextBlockId: null,
3352
3388
  agentMessageEmitted: new Map(this.persistedAgentEmitted),
3353
3389
  currentAgentMessageId: "",
3354
- activeClient: wsClient
3390
+ activeClient: wsClient,
3391
+ processedRequestIDs: /* @__PURE__ */ new Set()
3355
3392
  };
3356
3393
  for (const msg of options.prompt) {
3357
3394
  if (msg.role === "system") {
@@ -3364,6 +3401,7 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3364
3401
  }
3365
3402
  }
3366
3403
  }
3404
+ let startReq;
3367
3405
  const stream = new ReadableStream({
3368
3406
  start: async (controller) => {
3369
3407
  try {
@@ -3384,7 +3422,8 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3384
3422
  wsClient,
3385
3423
  toolExecutor,
3386
3424
  () => `text-${textBlockCounter++}`,
3387
- availableToolNames
3425
+ availableToolNames,
3426
+ startReq
3388
3427
  );
3389
3428
  }
3390
3429
  );
@@ -3406,7 +3445,7 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3406
3445
  const trimmedPreapproved = preapprovedTools.filter(
3407
3446
  (name) => trimmed.mcpTools.some((t) => t.name === name)
3408
3447
  );
3409
- const startReq = {
3448
+ startReq = {
3410
3449
  workflowID: workflowId,
3411
3450
  clientVersion: CLIENT_VERSION,
3412
3451
  workflowDefinition: workflowDef,
@@ -3471,7 +3510,7 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3471
3510
  // ---------------------------------------------------------------------------
3472
3511
  // Event handling
3473
3512
  // ---------------------------------------------------------------------------
3474
- handleWorkflowEvent(ss, event, controller, wsClient, toolExecutor, nextTextId, availableToolNames) {
3513
+ handleWorkflowEvent(ss, event, controller, wsClient, toolExecutor, nextTextId, availableToolNames, startReq) {
3475
3514
  if (ss.streamClosed) {
3476
3515
  return;
3477
3516
  }
@@ -3482,6 +3521,8 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3482
3521
  }
3483
3522
  case "tool-request": {
3484
3523
  const { requestID, data } = event;
3524
+ if (ss.processedRequestIDs.has(requestID)) break;
3525
+ ss.processedRequestIDs.add(requestID);
3485
3526
  let parsedArgs;
3486
3527
  try {
3487
3528
  JSON.parse(data.args);
@@ -3528,6 +3569,8 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3528
3569
  break;
3529
3570
  }
3530
3571
  case "builtin-tool-request": {
3572
+ if (ss.processedRequestIDs.has(event.requestID)) break;
3573
+ ss.processedRequestIDs.add(event.requestID);
3531
3574
  const mapped = mapBuiltinTool(event.toolName, event.data, availableToolNames);
3532
3575
  const mappedArgs = JSON.stringify(mapped.args);
3533
3576
  if (ss.activeTextBlockId) {
@@ -3568,6 +3611,26 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3568
3611
  });
3569
3612
  break;
3570
3613
  }
3614
+ case "approval-required": {
3615
+ ss.approvalPending = true;
3616
+ this.approveAndResume(
3617
+ ss,
3618
+ event.tools,
3619
+ startReq,
3620
+ controller,
3621
+ toolExecutor,
3622
+ nextTextId,
3623
+ availableToolNames
3624
+ ).catch(() => {
3625
+ ss.approvalPending = false;
3626
+ if (ss.deferredClose) {
3627
+ const close = ss.deferredClose;
3628
+ ss.deferredClose = null;
3629
+ close();
3630
+ }
3631
+ });
3632
+ break;
3633
+ }
3571
3634
  case "completed": {
3572
3635
  if (ss.activeTextBlockId) {
3573
3636
  controller.enqueue({ type: "text-end", id: ss.activeTextBlockId });
@@ -3586,7 +3649,7 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3586
3649
  controller.close();
3587
3650
  this.cleanupClient(ss);
3588
3651
  };
3589
- if (ss.pendingToolCount > 0) {
3652
+ if (ss.pendingToolCount > 0 || ss.approvalPending) {
3590
3653
  ss.deferredClose = doCompleteClose;
3591
3654
  } else {
3592
3655
  ss.deferredClose = null;
@@ -3645,7 +3708,7 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3645
3708
  this.cleanupClient(ss);
3646
3709
  }
3647
3710
  };
3648
- if (ss.pendingToolCount > 0) {
3711
+ if (ss.pendingToolCount > 0 || ss.approvalPending) {
3649
3712
  ss.deferredClose = doClose;
3650
3713
  } else {
3651
3714
  ss.deferredClose = null;
@@ -3821,6 +3884,60 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3821
3884
  this.persistedAgentEmitted.clear();
3822
3885
  }
3823
3886
  }
3887
+ async approveAndResume(ss, tools, startReq, controller, toolExecutor, nextTextId, availableToolNames) {
3888
+ const handler = this.workflowOptions.approvalHandler;
3889
+ if (!handler || !startReq) {
3890
+ ss.approvalPending = false;
3891
+ if (ss.deferredClose) {
3892
+ const close = ss.deferredClose;
3893
+ ss.deferredClose = null;
3894
+ close();
3895
+ }
3896
+ return;
3897
+ }
3898
+ let decision;
3899
+ try {
3900
+ decision = await handler(tools);
3901
+ } catch (err) {
3902
+ ss.approvalPending = false;
3903
+ if (!ss.streamClosed) controller.error(err);
3904
+ return;
3905
+ }
3906
+ ss.approvalPending = false;
3907
+ this.cleanupClient(ss, false);
3908
+ const approval = decision.approved ? { approval: { tool_name: tools[0]?.name, tool_args_json: tools[0]?.args } } : { rejection: { message: decision.message ?? "User rejected" } };
3909
+ const newStartReq = { ...startReq, approval };
3910
+ const newClient = new GitLabWorkflowClient();
3911
+ this.activeClients.add(newClient);
3912
+ ss.activeClient = newClient;
3913
+ const modelRef = await this.resolveModelRef();
3914
+ try {
3915
+ await newClient.connect(
3916
+ {
3917
+ instanceUrl: this.config.instanceUrl,
3918
+ modelRef,
3919
+ headers: this.config.getHeaders(),
3920
+ projectId: this.workflowOptions.projectId,
3921
+ namespaceId: this.workflowOptions.namespaceId,
3922
+ rootNamespaceId: this.workflowOptions.rootNamespaceId
3923
+ },
3924
+ (event) => this.handleWorkflowEvent(
3925
+ ss,
3926
+ event,
3927
+ controller,
3928
+ newClient,
3929
+ toolExecutor,
3930
+ nextTextId,
3931
+ availableToolNames,
3932
+ newStartReq
3933
+ )
3934
+ );
3935
+ newClient.sendStartRequest(newStartReq);
3936
+ } catch (err) {
3937
+ this.cleanupClient(ss, true);
3938
+ if (!ss.streamClosed) controller.error(err);
3939
+ }
3940
+ }
3824
3941
  // ---------------------------------------------------------------------------
3825
3942
  // Workflow metadata
3826
3943
  // ---------------------------------------------------------------------------