opencode-gitlab-duo-agentic 0.2.21 → 0.2.24

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.
Files changed (2) hide show
  1. package/dist/index.js +80 -96
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -382,6 +382,7 @@ function isUtilityAgent(agent) {
382
382
  return UTILITY_AGENTS.has(name);
383
383
  }
384
384
  function isGitLabProvider(model) {
385
+ if (model.api?.npm === "opencode-gitlab-duo-agentic") return true;
385
386
  if (model.api?.npm === "opencode-gitlab-duo-agentic") return true;
386
387
  if (model.providerID === "gitlab" && model.api?.npm !== "@gitlab/gitlab-ai-provider") return true;
387
388
  return model.providerID.toLowerCase().includes("gitlab-duo");
@@ -779,6 +780,8 @@ function dlog(msg) {
779
780
  }
780
781
 
781
782
  // src/workflow/session.ts
783
+ var BLOCKED_HTTP_REQUEST_ERROR = "gitlab_api_request is disabled by client policy";
784
+ var BLOCKED_GIT_COMMAND_ERROR = "run_git_command is disabled by client policy";
782
785
  var WorkflowSession = class {
783
786
  #client;
784
787
  #tokenService;
@@ -793,6 +796,7 @@ var WorkflowSession = class {
793
796
  #queue;
794
797
  #startRequestSent = false;
795
798
  #pendingApproval = false;
799
+ #resumed = false;
796
800
  #onWorkflowCreated;
797
801
  constructor(client, modelId, cwd, options) {
798
802
  this.#client = client;
@@ -801,6 +805,7 @@ var WorkflowSession = class {
801
805
  this.#cwd = cwd;
802
806
  if (options?.existingWorkflowId) {
803
807
  this.#workflowId = options.existingWorkflowId;
808
+ this.#resumed = true;
804
809
  }
805
810
  this.#onWorkflowCreated = options?.onWorkflowCreated;
806
811
  }
@@ -822,6 +827,7 @@ var WorkflowSession = class {
822
827
  this.#tokenService.clear();
823
828
  this.#closeConnection();
824
829
  this.#pendingApproval = false;
830
+ this.#resumed = false;
825
831
  this.#startRequestSent = false;
826
832
  }
827
833
  // ---------------------------------------------------------------------------
@@ -953,11 +959,16 @@ var WorkflowSession = class {
953
959
  const status = action.newCheckpoint.status;
954
960
  dlog(`checkpoint: status=${status} ckptLen=${ckpt.length}`);
955
961
  const deltas = extractAgentTextDeltas(ckpt, this.#checkpoint);
956
- for (const delta of deltas) {
957
- queue.push({ type: "text-delta", value: delta });
958
- }
959
- if (deltas.length > 0) {
960
- dlog(`checkpoint: ${deltas.length} text deltas`);
962
+ if (this.#resumed) {
963
+ dlog(`checkpoint: RESUMED \u2014 discarding ${deltas.length} old deltas, fast-forwarding state`);
964
+ this.#resumed = false;
965
+ } else {
966
+ for (const delta of deltas) {
967
+ queue.push({ type: "text-delta", value: delta });
968
+ }
969
+ if (deltas.length > 0) {
970
+ dlog(`checkpoint: ${deltas.length} text deltas`);
971
+ }
961
972
  }
962
973
  if (isToolApproval(status)) {
963
974
  dlog(`checkpoint: TOOL_APPROVAL \u2192 pendingApproval=true (waiting for DWS close)`);
@@ -973,9 +984,13 @@ var WorkflowSession = class {
973
984
  }
974
985
  const toolAction = action;
975
986
  if (toolAction.runHTTPRequest && toolAction.requestID) {
976
- dlog(`standalone: httpRequest ${toolAction.runHTTPRequest.method} ${toolAction.runHTTPRequest.path} reqId=${toolAction.requestID}`);
977
- this.#executeHttpRequest(toolAction.requestID, toolAction.runHTTPRequest).catch(() => {
978
- });
987
+ dlog(`standalone: BLOCKED httpRequest ${toolAction.runHTTPRequest.method} ${toolAction.runHTTPRequest.path} reqId=${toolAction.requestID}`);
988
+ this.sendHttpResult(toolAction.requestID, 403, {}, "", BLOCKED_HTTP_REQUEST_ERROR);
989
+ return;
990
+ }
991
+ if (toolAction.runGitCommand && toolAction.requestID) {
992
+ dlog(`standalone: BLOCKED runGitCommand ${toolAction.runGitCommand.command} reqId=${toolAction.requestID}`);
993
+ this.sendToolResult(toolAction.requestID, "", BLOCKED_GIT_COMMAND_ERROR);
979
994
  return;
980
995
  }
981
996
  const mapped = mapActionToToolRequest(toolAction);
@@ -992,42 +1007,6 @@ var WorkflowSession = class {
992
1007
  }
993
1008
  }
994
1009
  // ---------------------------------------------------------------------------
995
- // Private: HTTP request handling (gitlab_api_request)
996
- // ---------------------------------------------------------------------------
997
- /**
998
- * Execute a GitLab API request directly and send the response as httpResponse.
999
- * DWS is the only action that expects httpResponse instead of plainTextResponse.
1000
- * Runs async in the background (fire-and-forget from #handleAction).
1001
- */
1002
- async #executeHttpRequest(requestId, request2) {
1003
- try {
1004
- const url = `${this.#client.instanceUrl}/api/v4/${request2.path}`;
1005
- dlog(`httpRequest: ${request2.method} ${request2.path} reqId=${requestId}`);
1006
- const init = {
1007
- method: request2.method,
1008
- headers: {
1009
- "authorization": `Bearer ${this.#client.token}`,
1010
- "content-type": "application/json"
1011
- }
1012
- };
1013
- if (request2.body) {
1014
- init.body = request2.body;
1015
- }
1016
- const response = await fetch(url, init);
1017
- const body = await response.text();
1018
- const headers = {};
1019
- response.headers.forEach((value, key) => {
1020
- headers[key] = value;
1021
- });
1022
- dlog(`httpRequest: ${request2.method} ${request2.path} \u2192 ${response.status} body=${body.length}b`);
1023
- this.sendHttpResult(requestId, response.status, headers, body);
1024
- } catch (error) {
1025
- const message = error instanceof Error ? error.message : String(error);
1026
- dlog(`httpRequest: ERROR ${request2.method} ${request2.path} \u2192 ${message}`);
1027
- this.sendHttpResult(requestId, 0, {}, "", message);
1028
- }
1029
- }
1030
- // ---------------------------------------------------------------------------
1031
1010
  // Private: connection management
1032
1011
  // ---------------------------------------------------------------------------
1033
1012
  /**
@@ -1061,7 +1040,11 @@ var WorkflowSession = class {
1061
1040
  mcpTools,
1062
1041
  additional_context: [],
1063
1042
  preapproved_tools: mcpTools.map((t) => t.name),
1064
- approval: { approval: {} }
1043
+ approval: { approval: {} },
1044
+ ...this.#toolsConfig?.flowConfig ? {
1045
+ flowConfig: this.#toolsConfig.flowConfig,
1046
+ flowConfigSchemaVersion: this.#toolsConfig.flowConfigSchemaVersion ?? "v1"
1047
+ } : {}
1065
1048
  }
1066
1049
  });
1067
1050
  this.#startRequestSent = true;
@@ -1506,6 +1489,44 @@ function loadWorkflowId(key) {
1506
1489
  return store[key];
1507
1490
  }
1508
1491
 
1492
+ // src/workflow/flow-config.ts
1493
+ var TOOL_ALLOWLIST = [
1494
+ "read_file",
1495
+ "read_files",
1496
+ "create_file_with_contents",
1497
+ "edit_file",
1498
+ "list_dir",
1499
+ "find_files",
1500
+ "grep",
1501
+ "mkdir",
1502
+ "run_command"
1503
+ ];
1504
+ function buildFlowConfig(systemPrompt) {
1505
+ return {
1506
+ version: "v1",
1507
+ environment: "chat-partial",
1508
+ components: [
1509
+ {
1510
+ // Keep "chat" for behavior parity with GitLab Duo CLI flow metadata.
1511
+ name: "chat",
1512
+ type: "AgentComponent",
1513
+ prompt_id: "chat/agent",
1514
+ toolset: [...TOOL_ALLOWLIST]
1515
+ }
1516
+ ],
1517
+ prompts: [
1518
+ {
1519
+ name: "chat",
1520
+ prompt_id: "chat/agent",
1521
+ unit_primitives: ["duo_chat"],
1522
+ prompt_template: {
1523
+ system: systemPrompt
1524
+ }
1525
+ }
1526
+ ]
1527
+ };
1528
+ }
1529
+
1509
1530
  // src/provider/duo-workflow-model.ts
1510
1531
  var sessions = /* @__PURE__ */ new Map();
1511
1532
  var UNKNOWN_USAGE = {
@@ -1527,8 +1548,6 @@ var DuoWorkflowModel = class {
1527
1548
  #sentToolCallIds = /* @__PURE__ */ new Set();
1528
1549
  #lastSentGoal = null;
1529
1550
  #stateSessionId;
1530
- #agentMode;
1531
- #agentModeReminder;
1532
1551
  constructor(modelId, client, cwd) {
1533
1552
  this.modelId = modelId;
1534
1553
  this.#client = client;
@@ -1569,8 +1588,6 @@ var DuoWorkflowModel = class {
1569
1588
  this.#multiCallGroups.clear();
1570
1589
  this.#sentToolCallIds.clear();
1571
1590
  this.#lastSentGoal = null;
1572
- this.#agentMode = void 0;
1573
- this.#agentModeReminder = void 0;
1574
1591
  this.#stateSessionId = sessionID;
1575
1592
  }
1576
1593
  const model = this;
@@ -1639,31 +1656,24 @@ var DuoWorkflowModel = class {
1639
1656
  await session.ensureConnected(goal);
1640
1657
  if (!session.hasStarted) {
1641
1658
  const extraContext = [];
1659
+ const extractedSystemPrompt = extractSystemPrompt(options.prompt);
1660
+ const sanitizedSystemPrompt = sanitizeSystemPrompt(
1661
+ extractedSystemPrompt ?? "You are GitLab Duo, an AI coding assistant."
1662
+ );
1663
+ session.setToolsConfig({
1664
+ mcpTools: [],
1665
+ flowConfig: buildFlowConfig(sanitizedSystemPrompt),
1666
+ flowConfigSchemaVersion: "v1"
1667
+ });
1642
1668
  extraContext.push(...buildSystemContext());
1643
- const systemPrompt = extractSystemPrompt(options.prompt);
1644
- if (systemPrompt) {
1645
- extraContext.push({
1646
- category: "agent_context",
1647
- content: sanitizeSystemPrompt(systemPrompt),
1648
- id: "agent_system_prompt",
1649
- metadata: JSON.stringify({
1650
- title: "Agent System Prompt",
1651
- enabled: true,
1652
- subType: "system_prompt"
1653
- })
1654
- });
1655
- }
1656
1669
  const agentReminders = extractAgentReminders(options.prompt);
1657
- const modeReminder = detectLatestModeReminder(agentReminders);
1658
- if (modeReminder) {
1659
- model.#agentMode = modeReminder.mode;
1660
- model.#agentModeReminder = modeReminder.reminder;
1661
- }
1662
- const remindersForContext = buildReminderContext(agentReminders, model.#agentModeReminder);
1663
- if (remindersForContext.length > 0) {
1670
+ if (agentReminders.length > 0) {
1664
1671
  extraContext.push({
1665
1672
  category: "agent_context",
1666
- content: sanitizeSystemPrompt(remindersForContext.join("\n\n")),
1673
+ content: sanitizeSystemPrompt(
1674
+ `[context-id:${Date.now()}]
1675
+ ${agentReminders.join("\n\n")}`
1676
+ ),
1667
1677
  id: "agent_reminders",
1668
1678
  metadata: JSON.stringify({
1669
1679
  title: "Agent Reminders",
@@ -1802,32 +1812,6 @@ var DuoWorkflowModel = class {
1802
1812
  function sessionKey(instanceUrl, modelId, sessionID) {
1803
1813
  return `${instanceUrl}::${modelId}::${sessionID}`;
1804
1814
  }
1805
- function detectLatestModeReminder(reminders) {
1806
- let latest;
1807
- for (const reminder of reminders) {
1808
- const classification = classifyModeReminder(reminder);
1809
- if (classification === "other") continue;
1810
- latest = { mode: classification, reminder };
1811
- }
1812
- return latest;
1813
- }
1814
- function buildReminderContext(reminders, modeReminder) {
1815
- const nonModeReminders = reminders.filter(
1816
- (r) => classifyModeReminder(r) === "other"
1817
- );
1818
- if (!modeReminder) return nonModeReminders;
1819
- return [...nonModeReminders, modeReminder];
1820
- }
1821
- function classifyModeReminder(reminder) {
1822
- const text2 = reminder.toLowerCase();
1823
- if (text2.includes("operational mode has changed from build to plan")) return "plan";
1824
- if (text2.includes("operational mode has changed from plan to build")) return "build";
1825
- if (text2.includes("you are no longer in read-only mode")) return "build";
1826
- if (text2.includes("you are now in read-only mode")) return "plan";
1827
- if (text2.includes("you are in read-only mode")) return "plan";
1828
- if (text2.includes("you are permitted to make file changes")) return "build";
1829
- return "other";
1830
- }
1831
1815
 
1832
1816
  // src/provider/index.ts
1833
1817
  function createFallbackProvider(input = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-gitlab-duo-agentic",
3
- "version": "0.2.21",
3
+ "version": "0.2.24",
4
4
  "description": "OpenCode plugin and provider for GitLab Duo Agentic workflows",
5
5
  "license": "MIT",
6
6
  "type": "module",