gitlab-ai-provider 6.2.0 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
4
4
 
5
+ ## 6.3.0 (2026-03-31)
6
+
7
+ - fix(model): clear deferredClose before reconnect to prevent stream dying after batch approval ([017f8fe](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/017f8fe))
8
+ - fix(tests): use class mock for GitLabWorkflowTokenClient to support new in all environments ([e3f0e24](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/e3f0e24))
9
+ - chore: rebuild dist ([71bdc51](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/71bdc51))
10
+ - feat(model): per-session DWS workflow lifecycle and tool call approval ([aa7a09a](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/aa7a09a))
11
+
12
+ ## <small>6.2.1 (2026-03-31)</small>
13
+
14
+ - fix(model): pre-approve all batch tools on approval reconnect to prevent re-triggering ([d7f80e2](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/d7f80e2))
15
+
5
16
  ## 6.2.0 (2026-03-30)
6
17
 
7
18
  - feat: support aiCatalogItemVersionId in createWorkflow for custom agent session linking ([861c98d](https://gitlab.com/vglafirov/gitlab-ai-provider/commit/861c98d))
package/dist/index.d.mts CHANGED
@@ -703,6 +703,7 @@ declare class GitLabWorkflowLanguageModel implements LanguageModelV3 {
703
703
  private readonly modelDiscovery;
704
704
  private readonly modelCache;
705
705
  private detectedProjectPath;
706
+ private readonly sessionWorkflows;
706
707
  private currentWorkflowId;
707
708
  private currentWorkflowDefinition;
708
709
  private persistedAgentEmitted;
@@ -756,6 +757,11 @@ declare class GitLabWorkflowLanguageModel implements LanguageModelV3 {
756
757
  * Updated when the user chooses "always" in the approval prompt.
757
758
  */
758
759
  sessionPreapprovedTools: string[];
760
+ /**
761
+ * The opencode session ID. Set per-stream by the host to key per-session
762
+ * DWS workflows. Different sessions get different DWS workflows.
763
+ */
764
+ sessionID: string;
759
765
  /**
760
766
  * Set the approval handler callback.
761
767
  * Called when DWS requires tool call approval. Host (e.g., opencode) wires this
@@ -843,7 +849,7 @@ declare class GitLabWorkflowLanguageModel implements LanguageModelV3 {
843
849
  * Reset the workflow state, forcing a new workflow to be created on the
844
850
  * next doStream() call. Call this when starting a new conversation.
845
851
  */
846
- resetWorkflow(): void;
852
+ resetWorkflow(sessionKey?: string): void;
847
853
  /**
848
854
  * Get the current workflow ID (if any).
849
855
  * Useful for consumers that need to track workflow state.
package/dist/index.d.ts CHANGED
@@ -703,6 +703,7 @@ declare class GitLabWorkflowLanguageModel implements LanguageModelV3 {
703
703
  private readonly modelDiscovery;
704
704
  private readonly modelCache;
705
705
  private detectedProjectPath;
706
+ private readonly sessionWorkflows;
706
707
  private currentWorkflowId;
707
708
  private currentWorkflowDefinition;
708
709
  private persistedAgentEmitted;
@@ -756,6 +757,11 @@ declare class GitLabWorkflowLanguageModel implements LanguageModelV3 {
756
757
  * Updated when the user chooses "always" in the approval prompt.
757
758
  */
758
759
  sessionPreapprovedTools: string[];
760
+ /**
761
+ * The opencode session ID. Set per-stream by the host to key per-session
762
+ * DWS workflows. Different sessions get different DWS workflows.
763
+ */
764
+ sessionID: string;
759
765
  /**
760
766
  * Set the approval handler callback.
761
767
  * Called when DWS requires tool call approval. Host (e.g., opencode) wires this
@@ -843,7 +849,7 @@ declare class GitLabWorkflowLanguageModel implements LanguageModelV3 {
843
849
  * Reset the workflow state, forcing a new workflow to be created on the
844
850
  * next doStream() call. Call this when starting a new conversation.
845
851
  */
846
- resetWorkflow(): void;
852
+ resetWorkflow(sessionKey?: string): void;
847
853
  /**
848
854
  * Get the current workflow ID (if any).
849
855
  * Useful for consumers that need to track workflow state.
package/dist/index.js CHANGED
@@ -1653,7 +1653,7 @@ var GitLabOpenAILanguageModel = class {
1653
1653
  var import_isomorphic_ws = __toESM(require("isomorphic-ws"));
1654
1654
 
1655
1655
  // src/version.ts
1656
- var VERSION = true ? "6.1.1" : "0.0.0-dev";
1656
+ var VERSION = true ? "6.2.1" : "0.0.0-dev";
1657
1657
 
1658
1658
  // src/gitlab-workflow-types.ts
1659
1659
  var WorkflowType = /* @__PURE__ */ ((WorkflowType2) => {
@@ -3046,14 +3046,12 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3046
3046
  // Cached detected project path
3047
3047
  detectedProjectPath = null;
3048
3048
  // Workflow ID persisted across turns for multi-turn conversations.
3049
- // When DWS sends INPUT_REQUIRED, the workflow stays alive server-side.
3050
- // On the next doStream() call we reuse this ID (skip createWorkflow).
3049
+ // Per-session workflow state. Keyed by opencode sessionID (x-opencode-session header).
3050
+ // Each opencode session gets its own DWS workflow, reused across turns within that session.
3051
+ sessionWorkflows = /* @__PURE__ */ new Map();
3052
+ // Fallback for callers that don't pass x-opencode-session (e.g. direct SDK use).
3051
3053
  currentWorkflowId = null;
3052
- // Track which workflowDefinition/flowConfig the current workflow was created with.
3053
- // When the agent changes, reset the workflow so a new one is created correctly.
3054
3054
  currentWorkflowDefinition = null;
3055
- // Persisted across turns so that cumulative DWS chat logs don't re-emit
3056
- // messages that were already streamed in a previous doStream() call.
3057
3055
  persistedAgentEmitted = /* @__PURE__ */ new Map();
3058
3056
  // Track all active stream clients so stopWorkflow() can stop them all.
3059
3057
  activeClients = /* @__PURE__ */ new Set();
@@ -3115,6 +3113,11 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3115
3113
  * Updated when the user chooses "always" in the approval prompt.
3116
3114
  */
3117
3115
  sessionPreapprovedTools = [];
3116
+ /**
3117
+ * The opencode session ID. Set per-stream by the host to key per-session
3118
+ * DWS workflows. Different sessions get different DWS workflows.
3119
+ */
3120
+ sessionID = "";
3118
3121
  /**
3119
3122
  * Set the approval handler callback.
3120
3123
  * Called when DWS requires tool call approval. Host (e.g., opencode) wires this
@@ -3344,9 +3347,14 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3344
3347
  * Reset the workflow state, forcing a new workflow to be created on the
3345
3348
  * next doStream() call. Call this when starting a new conversation.
3346
3349
  */
3347
- resetWorkflow() {
3348
- this.currentWorkflowId = null;
3349
- this.persistedAgentEmitted.clear();
3350
+ resetWorkflow(sessionKey) {
3351
+ if (sessionKey) {
3352
+ this.sessionWorkflows.delete(sessionKey);
3353
+ } else {
3354
+ this.sessionWorkflows.clear();
3355
+ this.currentWorkflowId = null;
3356
+ this.persistedAgentEmitted.clear();
3357
+ }
3350
3358
  }
3351
3359
  /**
3352
3360
  * Get the current workflow ID (if any).
@@ -3445,12 +3453,21 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3445
3453
  );
3446
3454
  const projectId = await this.resolveProjectId();
3447
3455
  const effectiveDefinition = callFlowConfig ?? callWorkflowDefinition ?? DEFAULT_WORKFLOW_DEFINITION;
3448
- if (this.currentWorkflowId && this.currentWorkflowDefinition !== effectiveDefinition) {
3456
+ const sessionKey = this.sessionID;
3457
+ let sess = this.sessionWorkflows.get(sessionKey);
3458
+ if (sess && sess.workflowDefinition !== effectiveDefinition) {
3459
+ this.sessionWorkflows.delete(sessionKey);
3460
+ sess = void 0;
3461
+ }
3462
+ const useLegacy = sessionKey === "";
3463
+ if (useLegacy && this.currentWorkflowId && this.currentWorkflowDefinition !== effectiveDefinition) {
3449
3464
  this.currentWorkflowId = null;
3450
3465
  this.currentWorkflowDefinition = null;
3451
3466
  }
3452
3467
  let workflowId;
3453
- if (this.currentWorkflowId) {
3468
+ if (sess) {
3469
+ workflowId = sess.workflowId;
3470
+ } else if (useLegacy && this.currentWorkflowId) {
3454
3471
  workflowId = this.currentWorkflowId;
3455
3472
  } else {
3456
3473
  workflowId = await this.tokenClient.createWorkflow(goal, {
@@ -3460,8 +3477,17 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3460
3477
  agentPrivileges: this.workflowOptions.agentPrivileges,
3461
3478
  aiCatalogItemVersionId: callAiCatalogItemVersionId
3462
3479
  });
3463
- this.currentWorkflowId = workflowId;
3464
- this.currentWorkflowDefinition = effectiveDefinition;
3480
+ if (useLegacy) {
3481
+ this.currentWorkflowId = workflowId;
3482
+ this.currentWorkflowDefinition = effectiveDefinition;
3483
+ } else {
3484
+ this.sessionWorkflows.set(sessionKey, {
3485
+ workflowId,
3486
+ workflowDefinition: effectiveDefinition,
3487
+ agentEmitted: /* @__PURE__ */ new Map()
3488
+ });
3489
+ sess = this.sessionWorkflows.get(sessionKey);
3490
+ }
3465
3491
  }
3466
3492
  const wsClient = new GitLabWorkflowClient();
3467
3493
  this.activeClients.add(wsClient);
@@ -3474,10 +3500,11 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3474
3500
  approvalPending: false,
3475
3501
  deferredClose: null,
3476
3502
  activeTextBlockId: null,
3477
- agentMessageEmitted: new Map(this.persistedAgentEmitted),
3503
+ agentMessageEmitted: new Map(sess?.agentEmitted ?? this.persistedAgentEmitted),
3478
3504
  currentAgentMessageId: "",
3479
3505
  activeClient: wsClient,
3480
- processedRequestIDs: /* @__PURE__ */ new Set()
3506
+ processedRequestIDs: /* @__PURE__ */ new Set(),
3507
+ sessionKey
3481
3508
  };
3482
3509
  for (const msg of options.prompt) {
3483
3510
  if (msg.role === "system") {
@@ -3588,7 +3615,13 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3588
3615
  wsClient.close();
3589
3616
  this.activeClients.delete(wsClient);
3590
3617
  ss.activeClient = null;
3591
- this.currentWorkflowId = null;
3618
+ if (!ss.streamClosed) {
3619
+ if (ss.sessionKey) {
3620
+ this.sessionWorkflows.delete(ss.sessionKey);
3621
+ } else {
3622
+ this.currentWorkflowId = null;
3623
+ }
3624
+ }
3592
3625
  }
3593
3626
  });
3594
3627
  return {
@@ -3712,7 +3745,7 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3712
3745
  toolExecutor,
3713
3746
  nextTextId,
3714
3747
  availableToolNames
3715
- ).catch(() => {
3748
+ ).catch((_err) => {
3716
3749
  ss.approvalPending = false;
3717
3750
  if (ss.deferredClose) {
3718
3751
  const close = ss.deferredClose;
@@ -3738,7 +3771,7 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3738
3771
  });
3739
3772
  ss.streamClosed = true;
3740
3773
  controller.close();
3741
- this.cleanupClient(ss);
3774
+ this.cleanupClient(ss, false);
3742
3775
  };
3743
3776
  if (ss.pendingToolCount > 0 || ss.approvalPending) {
3744
3777
  ss.deferredClose = doCompleteClose;
@@ -3796,7 +3829,7 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3796
3829
  });
3797
3830
  ss.streamClosed = true;
3798
3831
  controller.close();
3799
- this.cleanupClient(ss);
3832
+ this.cleanupClient(ss, false);
3800
3833
  }
3801
3834
  };
3802
3835
  if (ss.pendingToolCount > 0 || ss.approvalPending) {
@@ -3865,7 +3898,8 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3865
3898
  });
3866
3899
  ss.streamedOutputChars += delta.length;
3867
3900
  ss.agentMessageEmitted.set(msgId, content.length);
3868
- this.persistedAgentEmitted.set(msgId, content.length);
3901
+ const target = this.sessionWorkflows.get(ss.sessionKey)?.agentEmitted ?? this.persistedAgentEmitted;
3902
+ target.set(msgId, content.length);
3869
3903
  ss.currentAgentMessageId = msgId;
3870
3904
  }
3871
3905
  }
@@ -3971,8 +4005,12 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3971
4005
  ss.activeClient = null;
3972
4006
  }
3973
4007
  if (clearWorkflow) {
3974
- this.currentWorkflowId = null;
3975
- this.persistedAgentEmitted.clear();
4008
+ if (ss.sessionKey) {
4009
+ this.sessionWorkflows.delete(ss.sessionKey);
4010
+ } else {
4011
+ this.currentWorkflowId = null;
4012
+ this.persistedAgentEmitted.clear();
4013
+ }
3976
4014
  }
3977
4015
  }
3978
4016
  async approveAndResume(ss, tools, startReq, controller, toolExecutor, nextTextId, availableToolNames) {
@@ -3995,9 +4033,14 @@ var GitLabWorkflowLanguageModel = class _GitLabWorkflowLanguageModel {
3995
4033
  return;
3996
4034
  }
3997
4035
  ss.approvalPending = false;
4036
+ ss.deferredClose = null;
3998
4037
  this.cleanupClient(ss, false);
3999
4038
  const approval = decision.approved ? { approval: { tool_name: tools[0]?.name, tool_args_json: tools[0]?.args } } : { rejection: { message: decision.message ?? "User rejected" } };
4000
- const newStartReq = { ...startReq, approval };
4039
+ const newStartReq = {
4040
+ ...startReq,
4041
+ approval,
4042
+ preapproved_tools: decision.approved ? [...startReq.preapproved_tools ?? [], ...tools.map((t) => t.name)] : startReq.preapproved_tools ?? []
4043
+ };
4001
4044
  const newClient = new GitLabWorkflowClient();
4002
4045
  this.activeClients.add(newClient);
4003
4046
  ss.activeClient = newClient;