opencode-gitlab-duo-agentic 0.2.18 → 0.2.21

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 +115 -7
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -793,11 +793,16 @@ var WorkflowSession = class {
793
793
  #queue;
794
794
  #startRequestSent = false;
795
795
  #pendingApproval = false;
796
- constructor(client, modelId, cwd) {
796
+ #onWorkflowCreated;
797
+ constructor(client, modelId, cwd, options) {
797
798
  this.#client = client;
798
799
  this.#tokenService = new WorkflowTokenService(client);
799
800
  this.#modelId = modelId;
800
801
  this.#cwd = cwd;
802
+ if (options?.existingWorkflowId) {
803
+ this.#workflowId = options.existingWorkflowId;
804
+ }
805
+ this.#onWorkflowCreated = options?.onWorkflowCreated;
801
806
  }
802
807
  /**
803
808
  * Opt-in: override the server-side system prompt and/or register MCP tools.
@@ -905,6 +910,25 @@ var WorkflowSession = class {
905
910
  }
906
911
  });
907
912
  }
913
+ /**
914
+ * Send an HTTP response back to DWS on the existing connection.
915
+ * Used for gitlab_api_request which requires httpResponse (not plainTextResponse).
916
+ */
917
+ sendHttpResult(requestId, statusCode, headers, body, error) {
918
+ dlog(`sendHttpResult: reqId=${requestId} status=${statusCode} body=${body.length}b error=${error ?? "none"} socket=${!!this.#socket}`);
919
+ if (!this.#socket) throw new Error("Not connected");
920
+ this.#socket.send({
921
+ actionResponse: {
922
+ requestID: requestId,
923
+ httpResponse: {
924
+ statusCode,
925
+ headers,
926
+ body,
927
+ error: error ?? ""
928
+ }
929
+ }
930
+ });
931
+ }
908
932
  /**
909
933
  * Wait for the next event from the session.
910
934
  * Returns null when the stream is closed (turn complete or connection lost).
@@ -948,6 +972,12 @@ var WorkflowSession = class {
948
972
  return;
949
973
  }
950
974
  const toolAction = action;
975
+ 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
+ });
979
+ return;
980
+ }
951
981
  const mapped = mapActionToToolRequest(toolAction);
952
982
  if (mapped) {
953
983
  dlog(`standalone: ${mapped.toolName} reqId=${mapped.requestId} args=${JSON.stringify(mapped.args).slice(0, 200)}`);
@@ -962,6 +992,42 @@ var WorkflowSession = class {
962
992
  }
963
993
  }
964
994
  // ---------------------------------------------------------------------------
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
+ // ---------------------------------------------------------------------------
965
1031
  // Private: connection management
966
1032
  // ---------------------------------------------------------------------------
967
1033
  /**
@@ -1027,7 +1093,9 @@ var WorkflowSession = class {
1027
1093
  const details = [created.message, created.error].filter(Boolean).join("; ");
1028
1094
  throw new Error(`failed to create workflow${details ? `: ${details}` : ""}`);
1029
1095
  }
1030
- return String(created.id);
1096
+ const workflowId = String(created.id);
1097
+ this.#onWorkflowCreated?.(workflowId);
1098
+ return workflowId;
1031
1099
  }
1032
1100
  async #loadProjectContext() {
1033
1101
  if (this.#projectPath !== void 0) return;
@@ -1193,10 +1261,7 @@ function mapDuoToolRequest(toolName, args) {
1193
1261
  switch (toolName) {
1194
1262
  case "list_dir": {
1195
1263
  const directory = asString2(args.directory) ?? ".";
1196
- return {
1197
- toolName: "bash",
1198
- args: { command: `ls -la ${shellQuote(directory)}`, description: "List directory contents", workdir: "." }
1199
- };
1264
+ return { toolName: "read", args: { filePath: directory } };
1200
1265
  }
1201
1266
  case "read_file": {
1202
1267
  const filePath = asString2(args.file_path) ?? asString2(args.filepath) ?? asString2(args.filePath) ?? asString2(args.path);
@@ -1408,6 +1473,39 @@ CODE QUALITY:
1408
1473
  </communication>
1409
1474
  </system-reminder>`;
1410
1475
 
1476
+ // src/workflow/session-store.ts
1477
+ import { readFileSync, writeFileSync, mkdirSync } from "fs";
1478
+ import { join } from "path";
1479
+ import { homedir } from "os";
1480
+ function getStorePath() {
1481
+ const dir = process.env.XDG_CACHE_HOME?.trim() ? join(process.env.XDG_CACHE_HOME, "opencode") : join(homedir(), ".cache", "opencode");
1482
+ return join(dir, "duo-workflow-sessions.json");
1483
+ }
1484
+ function readStore() {
1485
+ try {
1486
+ return JSON.parse(readFileSync(getStorePath(), "utf8"));
1487
+ } catch {
1488
+ return {};
1489
+ }
1490
+ }
1491
+ function writeStore(store) {
1492
+ try {
1493
+ const storePath = getStorePath();
1494
+ mkdirSync(join(storePath, ".."), { recursive: true });
1495
+ writeFileSync(storePath, JSON.stringify(store, null, 2), "utf8");
1496
+ } catch {
1497
+ }
1498
+ }
1499
+ function saveWorkflowId(key, workflowId) {
1500
+ const store = readStore();
1501
+ store[key] = workflowId;
1502
+ writeStore(store);
1503
+ }
1504
+ function loadWorkflowId(key) {
1505
+ const store = readStore();
1506
+ return store[key];
1507
+ }
1508
+
1411
1509
  // src/provider/duo-workflow-model.ts
1412
1510
  var sessions = /* @__PURE__ */ new Map();
1413
1511
  var UNKNOWN_USAGE = {
@@ -1685,7 +1783,17 @@ var DuoWorkflowModel = class {
1685
1783
  const key = sessionKey(this.#client.instanceUrl, this.modelId, sessionID);
1686
1784
  const existing = sessions.get(key);
1687
1785
  if (existing) return existing;
1688
- const created = new WorkflowSession(this.#client, this.modelId, this.#cwd);
1786
+ const existingWorkflowId = loadWorkflowId(key);
1787
+ if (existingWorkflowId) {
1788
+ dlog(`resolveSession: restored workflowId=${existingWorkflowId} from disk for ${sessionID}`);
1789
+ }
1790
+ const created = new WorkflowSession(this.#client, this.modelId, this.#cwd, {
1791
+ existingWorkflowId,
1792
+ onWorkflowCreated: (workflowId) => {
1793
+ dlog(`resolveSession: saving workflowId=${workflowId} for ${sessionID}`);
1794
+ saveWorkflowId(key, workflowId);
1795
+ }
1796
+ });
1689
1797
  if (this.#toolsConfig) created.setToolsConfig(this.#toolsConfig);
1690
1798
  sessions.set(key, created);
1691
1799
  return created;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-gitlab-duo-agentic",
3
- "version": "0.2.18",
3
+ "version": "0.2.21",
4
4
  "description": "OpenCode plugin and provider for GitLab Duo Agentic workflows",
5
5
  "license": "MIT",
6
6
  "type": "module",