opencode-gitlab-duo-agentic 0.2.12 → 0.2.15
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.js +99 -30
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -547,24 +547,26 @@ var WORKFLOW_STATUS = {
|
|
|
547
547
|
function isCheckpointAction(action) {
|
|
548
548
|
return "newCheckpoint" in action && action.newCheckpoint != null;
|
|
549
549
|
}
|
|
550
|
-
var
|
|
551
|
-
WORKFLOW_STATUS.INPUT_REQUIRED,
|
|
550
|
+
var TERMINAL_STATUSES = /* @__PURE__ */ new Set([
|
|
552
551
|
WORKFLOW_STATUS.FINISHED,
|
|
553
552
|
WORKFLOW_STATUS.FAILED,
|
|
554
|
-
WORKFLOW_STATUS.STOPPED
|
|
555
|
-
WORKFLOW_STATUS.PLAN_APPROVAL_REQUIRED,
|
|
556
|
-
WORKFLOW_STATUS.TOOL_CALL_APPROVAL_REQUIRED
|
|
553
|
+
WORKFLOW_STATUS.STOPPED
|
|
557
554
|
]);
|
|
558
|
-
function
|
|
559
|
-
return
|
|
555
|
+
function isTerminal(status) {
|
|
556
|
+
return TERMINAL_STATUSES.has(status);
|
|
560
557
|
}
|
|
561
|
-
var
|
|
562
|
-
WORKFLOW_STATUS.TOOL_CALL_APPROVAL_REQUIRED,
|
|
558
|
+
var TURN_BOUNDARY_STATUSES = /* @__PURE__ */ new Set([
|
|
563
559
|
WORKFLOW_STATUS.INPUT_REQUIRED,
|
|
564
560
|
WORKFLOW_STATUS.PLAN_APPROVAL_REQUIRED
|
|
565
561
|
]);
|
|
566
|
-
function
|
|
567
|
-
return
|
|
562
|
+
function isTurnBoundary(status) {
|
|
563
|
+
return TURN_BOUNDARY_STATUSES.has(status);
|
|
564
|
+
}
|
|
565
|
+
function isToolApproval(status) {
|
|
566
|
+
return status === WORKFLOW_STATUS.TOOL_CALL_APPROVAL_REQUIRED;
|
|
567
|
+
}
|
|
568
|
+
function isTurnComplete(status) {
|
|
569
|
+
return isTerminal(status) || isTurnBoundary(status);
|
|
568
570
|
}
|
|
569
571
|
|
|
570
572
|
// src/workflow/websocket-client.ts
|
|
@@ -786,13 +788,11 @@ function mapActionToToolRequest(action) {
|
|
|
786
788
|
|
|
787
789
|
// src/utils/debug-log.ts
|
|
788
790
|
import { appendFileSync } from "fs";
|
|
789
|
-
var
|
|
791
|
+
var LOG_FILE = "/tmp/duo-workflow-debug.log";
|
|
790
792
|
function dlog(msg) {
|
|
791
|
-
|
|
792
|
-
|
|
793
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
794
|
+
appendFileSync(LOG_FILE, `[${ts}] ${msg}
|
|
793
795
|
`);
|
|
794
|
-
} catch {
|
|
795
|
-
}
|
|
796
796
|
}
|
|
797
797
|
|
|
798
798
|
// src/workflow/session.ts
|
|
@@ -809,6 +809,7 @@ var WorkflowSession = class {
|
|
|
809
809
|
#socket;
|
|
810
810
|
#queue;
|
|
811
811
|
#startRequestSent = false;
|
|
812
|
+
#pendingApproval = false;
|
|
812
813
|
constructor(client, modelId, cwd) {
|
|
813
814
|
this.#client = client;
|
|
814
815
|
this.#tokenService = new WorkflowTokenService(client);
|
|
@@ -832,6 +833,7 @@ var WorkflowSession = class {
|
|
|
832
833
|
this.#checkpoint = createCheckpointState();
|
|
833
834
|
this.#tokenService.clear();
|
|
834
835
|
this.#closeConnection();
|
|
836
|
+
this.#pendingApproval = false;
|
|
835
837
|
this.#startRequestSent = false;
|
|
836
838
|
}
|
|
837
839
|
// ---------------------------------------------------------------------------
|
|
@@ -842,13 +844,30 @@ var WorkflowSession = class {
|
|
|
842
844
|
if (!this.#workflowId) {
|
|
843
845
|
this.#workflowId = await this.#createWorkflow(goal);
|
|
844
846
|
}
|
|
847
|
+
const queue = new AsyncQueue();
|
|
848
|
+
this.#queue = queue;
|
|
849
|
+
await this.#connectSocket(queue);
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Open a WebSocket and wire its callbacks to the given queue.
|
|
853
|
+
* Replaces any existing socket but does NOT create a new queue.
|
|
854
|
+
*/
|
|
855
|
+
async #connectSocket(queue) {
|
|
845
856
|
await this.#tokenService.get(this.#rootNamespaceId);
|
|
846
|
-
this.#queue = new AsyncQueue();
|
|
847
|
-
const queue = this.#queue;
|
|
848
857
|
const socket = new WorkflowWebSocketClient({
|
|
849
858
|
action: (action) => this.#handleAction(action, queue),
|
|
850
859
|
error: (error) => queue.push({ type: "error", message: error.message }),
|
|
851
|
-
close: (
|
|
860
|
+
close: (code, reason) => {
|
|
861
|
+
dlog(`ws-close: code=${code} reason=${reason} pendingApproval=${this.#pendingApproval}`);
|
|
862
|
+
this.#socket = void 0;
|
|
863
|
+
if (this.#pendingApproval) {
|
|
864
|
+
this.#pendingApproval = false;
|
|
865
|
+
this.#reconnectWithApproval(queue);
|
|
866
|
+
} else {
|
|
867
|
+
this.#queue = void 0;
|
|
868
|
+
queue.close();
|
|
869
|
+
}
|
|
870
|
+
}
|
|
852
871
|
});
|
|
853
872
|
const url = buildWebSocketUrl(this.#client.instanceUrl, this.#modelId);
|
|
854
873
|
await socket.connect(url, {
|
|
@@ -891,7 +910,7 @@ var WorkflowSession = class {
|
|
|
891
910
|
* Send a tool result back to DWS on the existing connection.
|
|
892
911
|
*/
|
|
893
912
|
sendToolResult(requestId, output, error) {
|
|
894
|
-
dlog(`sendToolResult: reqId=${requestId} output=${output.length}b socket=${!!this.#socket}`);
|
|
913
|
+
dlog(`sendToolResult: reqId=${requestId} output=${output.length}b error=${error ?? "none"} socket=${!!this.#socket}`);
|
|
895
914
|
if (!this.#socket) throw new Error("Not connected");
|
|
896
915
|
this.#socket.send({
|
|
897
916
|
actionResponse: {
|
|
@@ -924,12 +943,23 @@ var WorkflowSession = class {
|
|
|
924
943
|
#handleAction(action, queue) {
|
|
925
944
|
if (isCheckpointAction(action)) {
|
|
926
945
|
const ckpt = action.newCheckpoint.checkpoint;
|
|
946
|
+
const status = action.newCheckpoint.status;
|
|
947
|
+
dlog(`checkpoint: status=${status} ckptLen=${ckpt.length}`);
|
|
927
948
|
const deltas = extractAgentTextDeltas(ckpt, this.#checkpoint);
|
|
928
949
|
for (const delta of deltas) {
|
|
929
950
|
queue.push({ type: "text-delta", value: delta });
|
|
930
951
|
}
|
|
952
|
+
if (deltas.length > 0) {
|
|
953
|
+
dlog(`checkpoint: ${deltas.length} text deltas`);
|
|
954
|
+
}
|
|
955
|
+
if (isToolApproval(status)) {
|
|
956
|
+
dlog(`checkpoint: TOOL_APPROVAL \u2192 pendingApproval=true (waiting for DWS close)`);
|
|
957
|
+
this.#pendingApproval = true;
|
|
958
|
+
return;
|
|
959
|
+
}
|
|
931
960
|
const toolRequests = extractToolRequests(ckpt, this.#checkpoint);
|
|
932
961
|
for (const req of toolRequests) {
|
|
962
|
+
dlog(`checkpoint: tool-request name=${req.toolName} reqId=${req.requestId}`);
|
|
933
963
|
queue.push({
|
|
934
964
|
type: "tool-request",
|
|
935
965
|
requestId: req.requestId,
|
|
@@ -937,32 +967,74 @@ var WorkflowSession = class {
|
|
|
937
967
|
args: req.args
|
|
938
968
|
});
|
|
939
969
|
}
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
dlog(`action: checkpoint status=${status} CLOSE`);
|
|
970
|
+
if (isTurnComplete(status)) {
|
|
971
|
+
dlog(`checkpoint: turnComplete \u2192 close queue+connection`);
|
|
943
972
|
queue.close();
|
|
944
973
|
this.#closeConnection();
|
|
945
|
-
} else {
|
|
946
|
-
dlog(`action: checkpoint status=${status} KEEP-ALIVE`);
|
|
947
974
|
}
|
|
948
975
|
return;
|
|
949
976
|
}
|
|
950
977
|
const toolAction = action;
|
|
951
978
|
const mapped = mapActionToToolRequest(toolAction);
|
|
952
979
|
if (mapped) {
|
|
953
|
-
dlog(`
|
|
980
|
+
dlog(`standalone: ${mapped.toolName} reqId=${mapped.requestId} args=${JSON.stringify(mapped.args).slice(0, 200)}`);
|
|
954
981
|
queue.push({
|
|
955
982
|
type: "tool-request",
|
|
956
983
|
requestId: mapped.requestId,
|
|
957
984
|
toolName: mapped.toolName,
|
|
958
985
|
args: mapped.args
|
|
959
986
|
});
|
|
987
|
+
} else {
|
|
988
|
+
dlog(`standalone: UNMAPPED action keys=${Object.keys(action).join(",")}`);
|
|
960
989
|
}
|
|
961
990
|
}
|
|
962
991
|
// ---------------------------------------------------------------------------
|
|
963
992
|
// Private: connection management
|
|
964
993
|
// ---------------------------------------------------------------------------
|
|
994
|
+
/**
|
|
995
|
+
* Auto-approve at DWS protocol level and reconnect.
|
|
996
|
+
*
|
|
997
|
+
* DWS closed the stream after TOOL_CALL_APPROVAL_REQUIRED. We open a new
|
|
998
|
+
* WebSocket, send startRequest with approval, and wire it to the SAME queue
|
|
999
|
+
* so Phase 3 in the model continues consuming events seamlessly.
|
|
1000
|
+
*
|
|
1001
|
+
* The actual tool execution still goes through OpenCode's permission system
|
|
1002
|
+
* when the standalone action arrives on the new stream.
|
|
1003
|
+
*/
|
|
1004
|
+
#reconnectWithApproval(queue) {
|
|
1005
|
+
dlog(`reconnectWithApproval: starting (workflowId=${this.#workflowId})`);
|
|
1006
|
+
this.#connectSocket(queue).then(() => {
|
|
1007
|
+
if (!this.#socket || !this.#workflowId) {
|
|
1008
|
+
dlog(`reconnectWithApproval: FAILED no socket/workflowId`);
|
|
1009
|
+
queue.close();
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
const mcpTools = this.#toolsConfig?.mcpTools ?? [];
|
|
1013
|
+
dlog(`reconnectWithApproval: sending startRequest with approval (mcpTools=${mcpTools.length})`);
|
|
1014
|
+
this.#socket.send({
|
|
1015
|
+
startRequest: {
|
|
1016
|
+
workflowID: this.#workflowId,
|
|
1017
|
+
clientVersion: WORKFLOW_CLIENT_VERSION,
|
|
1018
|
+
workflowDefinition: WORKFLOW_DEFINITION,
|
|
1019
|
+
goal: "",
|
|
1020
|
+
workflowMetadata: JSON.stringify({ extended_logging: false }),
|
|
1021
|
+
clientCapabilities: ["shell_command"],
|
|
1022
|
+
mcpTools,
|
|
1023
|
+
additional_context: [],
|
|
1024
|
+
preapproved_tools: mcpTools.map((t) => t.name),
|
|
1025
|
+
approval: { approval: {} }
|
|
1026
|
+
}
|
|
1027
|
+
});
|
|
1028
|
+
this.#startRequestSent = true;
|
|
1029
|
+
dlog(`reconnectWithApproval: approval sent, waiting for standalone actions`);
|
|
1030
|
+
}).catch((err) => {
|
|
1031
|
+
dlog(`reconnectWithApproval: ERROR ${err instanceof Error ? err.message : String(err)}`);
|
|
1032
|
+
this.#queue = void 0;
|
|
1033
|
+
queue.close();
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
965
1036
|
#closeConnection() {
|
|
1037
|
+
this.#pendingApproval = false;
|
|
966
1038
|
this.#socket?.close();
|
|
967
1039
|
this.#socket = void 0;
|
|
968
1040
|
this.#queue = void 0;
|
|
@@ -1451,10 +1523,7 @@ var DuoWorkflowModel = class {
|
|
|
1451
1523
|
const freshResults = toolResults.filter(
|
|
1452
1524
|
(r) => !model.#sentToolCallIds.has(r.toolCallId)
|
|
1453
1525
|
);
|
|
1454
|
-
dlog(`phase1: ${toolResults.length} total, ${freshResults.length} fresh
|
|
1455
|
-
for (const r of toolResults) {
|
|
1456
|
-
dlog(` tr: id=${r.toolCallId} inSent=${model.#sentToolCallIds.has(r.toolCallId)} inPending=${model.#pendingToolRequests.has(r.toolCallId)} out=${r.output.length}b`);
|
|
1457
|
-
}
|
|
1526
|
+
dlog(`phase1: ${toolResults.length} total, ${freshResults.length} fresh`);
|
|
1458
1527
|
let sentToolResults = false;
|
|
1459
1528
|
for (const result of freshResults) {
|
|
1460
1529
|
const hashIdx = result.toolCallId.indexOf("#");
|