issue-flow 0.19.1 → 0.20.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/README.md +12 -0
- package/dist/{agent-EWKA7357.js → agent-4DHRKZ2Z.js} +11 -11
- package/dist/{analyze-Z7ODC27F.js → analyze-UEJ2R3H5.js} +19 -19
- package/dist/{apply-VNSWQS6O.js → apply-QDYNQMUX.js} +11 -11
- package/dist/{bench-ZJEIACUK.js → bench-RNRXVEFK.js} +16 -16
- package/dist/{chunk-2EZW4X57.js → chunk-37O2FMAN.js} +8 -8
- package/dist/{chunk-IMB6FA2F.js → chunk-44RF3VFG.js} +48 -4
- package/dist/chunk-44RF3VFG.js.map +1 -0
- package/dist/{chunk-WPHC6JBR.js → chunk-7WBUEBD5.js} +4 -4
- package/dist/{chunk-KDQRNVBZ.js → chunk-ADH6RJNX.js} +3 -3
- package/dist/{chunk-HHXWAWU6.js → chunk-AKL5UPR6.js} +11 -11
- package/dist/{chunk-XKR3VD2Z.js → chunk-BTBJ5UOO.js} +3 -3
- package/dist/{chunk-UDRAD7MF.js → chunk-CLZ6RRZL.js} +7 -7
- package/dist/{chunk-J6VJ5M2U.js → chunk-DBLXLKGU.js} +7 -7
- package/dist/{chunk-3JD25HHJ.js → chunk-DZ3DZ3EN.js} +5 -5
- package/dist/{chunk-P7ZYOC23.js → chunk-EORDSKWZ.js} +7 -7
- package/dist/chunk-EORDSKWZ.js.map +1 -0
- package/dist/{chunk-XQLNZW7I.js → chunk-EYKPWHT7.js} +9 -9
- package/dist/{chunk-2YYP7AE6.js → chunk-FU2FQ4JE.js} +10 -10
- package/dist/{chunk-RGFMQFX7.js → chunk-G442RDFX.js} +5 -5
- package/dist/{chunk-7CMVL4X6.js → chunk-I64N6RL6.js} +2 -2
- package/dist/{chunk-XHCP6ZNO.js → chunk-JBYXJOJJ.js} +11 -11
- package/dist/{chunk-GD7JZMSJ.js → chunk-JCR5C5A7.js} +2 -2
- package/dist/{chunk-UPAAZU4W.js → chunk-K5NM26NV.js} +2 -2
- package/dist/{chunk-TJVX42KY.js → chunk-KUA6KBBD.js} +2 -2
- package/dist/{chunk-WB2FVIUS.js → chunk-KUL64XUF.js} +5 -5
- package/dist/{chunk-DXH4TQRB.js → chunk-MML2G7IC.js} +2 -2
- package/dist/{chunk-IQYXXFRC.js → chunk-PM5YWJYO.js} +5 -5
- package/dist/chunk-PM5YWJYO.js.map +1 -0
- package/dist/{chunk-HWWB22HC.js → chunk-PXPTIXJF.js} +9 -8
- package/dist/chunk-PXPTIXJF.js.map +1 -0
- package/dist/{chunk-MHA7Q3KU.js → chunk-QJHZ3HYJ.js} +24 -24
- package/dist/{chunk-LYC57OXF.js → chunk-RDPK7LYK.js} +4 -4
- package/dist/{chunk-46S7APLQ.js → chunk-RMIBLFG3.js} +9 -9
- package/dist/{chunk-ZAYATXJD.js → chunk-T736DSZI.js} +5 -5
- package/dist/{chunk-MQTVMB7W.js → chunk-UDUUUFQF.js} +35 -12
- package/dist/chunk-UDUUUFQF.js.map +1 -0
- package/dist/{chunk-PHPAK2L6.js → chunk-V7GETODQ.js} +18 -12
- package/dist/{chunk-PHPAK2L6.js.map → chunk-V7GETODQ.js.map} +1 -1
- package/dist/{chunk-PRGPSNGA.js → chunk-VHQMMY2D.js} +2 -2
- package/dist/{chunk-F2NFJ27Z.js → chunk-VJ3PF6B7.js} +50 -16
- package/dist/chunk-VJ3PF6B7.js.map +1 -0
- package/dist/{chunk-HEHHQU4G.js → chunk-WB4FA2EH.js} +6 -6
- package/dist/{chunk-2Q6PG3MT.js → chunk-WQSGL5LX.js} +11 -5
- package/dist/chunk-WQSGL5LX.js.map +1 -0
- package/dist/{chunk-LRWIP7QS.js → chunk-Y2KOWABB.js} +3 -3
- package/dist/{chunk-UHDXXLYH.js → chunk-YC24E66P.js} +538 -40
- package/dist/chunk-YC24E66P.js.map +1 -0
- package/dist/{chunk-NYDAHE3Y.js → chunk-Z6RCKCVB.js} +8 -8
- package/dist/{chunk-ERAG5IQB.js → chunk-ZPGNVBWY.js} +3 -3
- package/dist/cli.js +55 -53
- package/dist/cli.js.map +1 -1
- package/dist/{compat-YPDZB34U.js → compat-OJG76J4I.js} +5 -5
- package/dist/{config-56B4OHZE.js → config-6YTWPDBK.js} +10 -10
- package/dist/{conventions-DW2CM5I3.js → conventions-5UPOTU42.js} +13 -13
- package/dist/{db-5NU77PJA.js → db-QAZG4LQD.js} +9 -9
- package/dist/{diagnostics-UCTS2VUF.js → diagnostics-YEFB42E5.js} +6 -6
- package/dist/execute-QCT64D37.js +38 -0
- package/dist/{generate-O7GUXLN4.js → generate-QATMDKOK.js} +17 -17
- package/dist/{git-LCG45YXD.js → git-XGOECXWG.js} +2 -2
- package/dist/{history-6GSURU3H.js → history-NBPUZVGQ.js} +9 -9
- package/dist/{init-DD6DBRC5.js → init-4LB2YKWP.js} +11 -11
- package/dist/{operations-HU5ZFEBA.js → operations-RUQP2Y2F.js} +11 -11
- package/dist/{permissions-NNNSRC4P.js → permissions-QZPUUBSO.js} +3 -3
- package/dist/plan-2NMSHTMD.js +38 -0
- package/dist/{policy-RFJOJQL6.js → policy-RKKFF5A2.js} +11 -11
- package/dist/pr-QKFO5HI3.js +42 -0
- package/dist/pr-review-H2BFD6F2.js +33 -0
- package/dist/prd-ET6XKYRS.js +37 -0
- package/dist/{ps-AKRHGPSR.js → ps-UIKSOUAQ.js} +10 -10
- package/dist/{recorder-T7426CKV.js → recorder-LOU3PTIE.js} +6 -6
- package/dist/{registry-DWRKD65A.js → registry-HV7M6MAH.js} +4 -4
- package/dist/{repository-TABBRW2C.js → repository-RCRR6RDE.js} +2 -2
- package/dist/{resume-GDXKK5XP.js → resume-BWJZGBLZ.js} +36 -36
- package/dist/review-2MTZLNSS.js +38 -0
- package/dist/{routing-FPNUVE7T.js → routing-RM5ABHHD.js} +12 -12
- package/dist/run-XMACQBRJ.js +61 -0
- package/dist/{state-manager-IGNTD4AB.js → state-manager-SRN4VJSX.js} +4 -4
- package/dist/{usage-XW27AL5K.js → usage-JOODVE5O.js} +9 -9
- package/dist/{web-NCDBVZ5Y.js → web-TLMWJ2BM.js} +15 -15
- package/package.json +3 -2
- package/web/public/app.js +2 -2
- package/dist/chunk-2Q6PG3MT.js.map +0 -1
- package/dist/chunk-F2NFJ27Z.js.map +0 -1
- package/dist/chunk-HWWB22HC.js.map +0 -1
- package/dist/chunk-IMB6FA2F.js.map +0 -1
- package/dist/chunk-IQYXXFRC.js.map +0 -1
- package/dist/chunk-MQTVMB7W.js.map +0 -1
- package/dist/chunk-P7ZYOC23.js.map +0 -1
- package/dist/chunk-UHDXXLYH.js.map +0 -1
- package/dist/execute-R7AVOLNG.js +0 -38
- package/dist/plan-XR6B7EM3.js +0 -38
- package/dist/pr-OSCNJCOO.js +0 -42
- package/dist/pr-review-IKIYHFBO.js +0 -33
- package/dist/prd-HFDZJJQR.js +0 -37
- package/dist/review-JJHMQA2O.js +0 -38
- package/dist/run-RLCURLLL.js +0 -61
- /package/dist/{agent-EWKA7357.js.map → agent-4DHRKZ2Z.js.map} +0 -0
- /package/dist/{analyze-Z7ODC27F.js.map → analyze-UEJ2R3H5.js.map} +0 -0
- /package/dist/{apply-VNSWQS6O.js.map → apply-QDYNQMUX.js.map} +0 -0
- /package/dist/{bench-ZJEIACUK.js.map → bench-RNRXVEFK.js.map} +0 -0
- /package/dist/{chunk-2EZW4X57.js.map → chunk-37O2FMAN.js.map} +0 -0
- /package/dist/{chunk-WPHC6JBR.js.map → chunk-7WBUEBD5.js.map} +0 -0
- /package/dist/{chunk-KDQRNVBZ.js.map → chunk-ADH6RJNX.js.map} +0 -0
- /package/dist/{chunk-HHXWAWU6.js.map → chunk-AKL5UPR6.js.map} +0 -0
- /package/dist/{chunk-XKR3VD2Z.js.map → chunk-BTBJ5UOO.js.map} +0 -0
- /package/dist/{chunk-UDRAD7MF.js.map → chunk-CLZ6RRZL.js.map} +0 -0
- /package/dist/{chunk-J6VJ5M2U.js.map → chunk-DBLXLKGU.js.map} +0 -0
- /package/dist/{chunk-3JD25HHJ.js.map → chunk-DZ3DZ3EN.js.map} +0 -0
- /package/dist/{chunk-XQLNZW7I.js.map → chunk-EYKPWHT7.js.map} +0 -0
- /package/dist/{chunk-2YYP7AE6.js.map → chunk-FU2FQ4JE.js.map} +0 -0
- /package/dist/{chunk-RGFMQFX7.js.map → chunk-G442RDFX.js.map} +0 -0
- /package/dist/{chunk-7CMVL4X6.js.map → chunk-I64N6RL6.js.map} +0 -0
- /package/dist/{chunk-XHCP6ZNO.js.map → chunk-JBYXJOJJ.js.map} +0 -0
- /package/dist/{chunk-GD7JZMSJ.js.map → chunk-JCR5C5A7.js.map} +0 -0
- /package/dist/{chunk-UPAAZU4W.js.map → chunk-K5NM26NV.js.map} +0 -0
- /package/dist/{chunk-TJVX42KY.js.map → chunk-KUA6KBBD.js.map} +0 -0
- /package/dist/{chunk-WB2FVIUS.js.map → chunk-KUL64XUF.js.map} +0 -0
- /package/dist/{chunk-DXH4TQRB.js.map → chunk-MML2G7IC.js.map} +0 -0
- /package/dist/{chunk-MHA7Q3KU.js.map → chunk-QJHZ3HYJ.js.map} +0 -0
- /package/dist/{chunk-LYC57OXF.js.map → chunk-RDPK7LYK.js.map} +0 -0
- /package/dist/{chunk-46S7APLQ.js.map → chunk-RMIBLFG3.js.map} +0 -0
- /package/dist/{chunk-ZAYATXJD.js.map → chunk-T736DSZI.js.map} +0 -0
- /package/dist/{chunk-PRGPSNGA.js.map → chunk-VHQMMY2D.js.map} +0 -0
- /package/dist/{chunk-HEHHQU4G.js.map → chunk-WB4FA2EH.js.map} +0 -0
- /package/dist/{chunk-LRWIP7QS.js.map → chunk-Y2KOWABB.js.map} +0 -0
- /package/dist/{chunk-NYDAHE3Y.js.map → chunk-Z6RCKCVB.js.map} +0 -0
- /package/dist/{chunk-ERAG5IQB.js.map → chunk-ZPGNVBWY.js.map} +0 -0
- /package/dist/{compat-YPDZB34U.js.map → compat-OJG76J4I.js.map} +0 -0
- /package/dist/{config-56B4OHZE.js.map → config-6YTWPDBK.js.map} +0 -0
- /package/dist/{conventions-DW2CM5I3.js.map → conventions-5UPOTU42.js.map} +0 -0
- /package/dist/{db-5NU77PJA.js.map → db-QAZG4LQD.js.map} +0 -0
- /package/dist/{diagnostics-UCTS2VUF.js.map → diagnostics-YEFB42E5.js.map} +0 -0
- /package/dist/{execute-R7AVOLNG.js.map → execute-QCT64D37.js.map} +0 -0
- /package/dist/{generate-O7GUXLN4.js.map → generate-QATMDKOK.js.map} +0 -0
- /package/dist/{git-LCG45YXD.js.map → git-XGOECXWG.js.map} +0 -0
- /package/dist/{history-6GSURU3H.js.map → history-NBPUZVGQ.js.map} +0 -0
- /package/dist/{init-DD6DBRC5.js.map → init-4LB2YKWP.js.map} +0 -0
- /package/dist/{operations-HU5ZFEBA.js.map → operations-RUQP2Y2F.js.map} +0 -0
- /package/dist/{permissions-NNNSRC4P.js.map → permissions-QZPUUBSO.js.map} +0 -0
- /package/dist/{plan-XR6B7EM3.js.map → plan-2NMSHTMD.js.map} +0 -0
- /package/dist/{policy-RFJOJQL6.js.map → policy-RKKFF5A2.js.map} +0 -0
- /package/dist/{pr-OSCNJCOO.js.map → pr-QKFO5HI3.js.map} +0 -0
- /package/dist/{pr-review-IKIYHFBO.js.map → pr-review-H2BFD6F2.js.map} +0 -0
- /package/dist/{prd-HFDZJJQR.js.map → prd-ET6XKYRS.js.map} +0 -0
- /package/dist/{ps-AKRHGPSR.js.map → ps-UIKSOUAQ.js.map} +0 -0
- /package/dist/{recorder-T7426CKV.js.map → recorder-LOU3PTIE.js.map} +0 -0
- /package/dist/{registry-DWRKD65A.js.map → registry-HV7M6MAH.js.map} +0 -0
- /package/dist/{repository-TABBRW2C.js.map → repository-RCRR6RDE.js.map} +0 -0
- /package/dist/{resume-GDXKK5XP.js.map → resume-BWJZGBLZ.js.map} +0 -0
- /package/dist/{review-JJHMQA2O.js.map → review-2MTZLNSS.js.map} +0 -0
- /package/dist/{routing-FPNUVE7T.js.map → routing-RM5ABHHD.js.map} +0 -0
- /package/dist/{run-RLCURLLL.js.map → run-XMACQBRJ.js.map} +0 -0
- /package/dist/{state-manager-IGNTD4AB.js.map → state-manager-SRN4VJSX.js.map} +0 -0
- /package/dist/{usage-XW27AL5K.js.map → usage-JOODVE5O.js.map} +0 -0
- /package/dist/{web-NCDBVZ5Y.js.map → web-TLMWJ2BM.js.map} +0 -0
|
@@ -7,27 +7,29 @@ import {
|
|
|
7
7
|
printWarning,
|
|
8
8
|
readProjectConfigFile,
|
|
9
9
|
resolveIssuePaths
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-DBLXLKGU.js";
|
|
11
11
|
import {
|
|
12
12
|
DEFAULT_TELEMETRY_CONFIG
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-WB4FA2EH.js";
|
|
14
14
|
import {
|
|
15
15
|
parseCodexUsage,
|
|
16
16
|
parseUsage
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-JCR5C5A7.js";
|
|
18
18
|
import {
|
|
19
19
|
AGENT_PHASES,
|
|
20
|
+
AGENT_PROVIDER_IDS,
|
|
20
21
|
ANTIGRAVITY_CAPABILITIES,
|
|
21
22
|
CLAUDE_CAPABILITIES,
|
|
22
23
|
CODEX_CAPABILITIES,
|
|
23
24
|
CURSOR_CAPABILITIES,
|
|
25
|
+
OPENCODE_CAPABILITIES,
|
|
24
26
|
agentConfigInputSchema,
|
|
25
27
|
isAgentPhase,
|
|
26
28
|
isAgentProviderId,
|
|
27
29
|
parsePhasesInput,
|
|
28
30
|
resilienceConfigSchema,
|
|
29
31
|
telemetryConfigInputSchema
|
|
30
|
-
} from "./chunk-
|
|
32
|
+
} from "./chunk-44RF3VFG.js";
|
|
31
33
|
import {
|
|
32
34
|
issuesConfigSchema,
|
|
33
35
|
policyConfigInputSchema,
|
|
@@ -42,19 +44,16 @@ import {
|
|
|
42
44
|
RETRY_CONFIG_KEYS,
|
|
43
45
|
getProjectRoot,
|
|
44
46
|
run
|
|
45
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-KUA6KBBD.js";
|
|
46
48
|
|
|
47
49
|
// src/config/dependencies.ts
|
|
48
50
|
import { platform } from "node:os";
|
|
49
51
|
|
|
50
52
|
// src/agents/availability.ts
|
|
51
|
-
import { execa as
|
|
53
|
+
import { execa as execa7 } from "execa";
|
|
52
54
|
|
|
53
|
-
// src/agents/
|
|
54
|
-
import { execa as
|
|
55
|
-
|
|
56
|
-
// src/agents/antigravity.ts
|
|
57
|
-
import { execa as execa2 } from "execa";
|
|
55
|
+
// src/agents/opencode.ts
|
|
56
|
+
import { execa as execa3 } from "execa";
|
|
58
57
|
|
|
59
58
|
// src/core/shutdown.ts
|
|
60
59
|
var SHUTDOWN_GRACE_MS = 15e3;
|
|
@@ -250,6 +249,9 @@ function describeStall(silentMs, provider = "claude") {
|
|
|
250
249
|
return `${provider} produced no output for ${Math.round(silentMs / 1e3)}s and was stopped (stalled)`;
|
|
251
250
|
}
|
|
252
251
|
|
|
252
|
+
// src/agents/antigravity.ts
|
|
253
|
+
import { execa as execa2 } from "execa";
|
|
254
|
+
|
|
253
255
|
// src/agents/claude.ts
|
|
254
256
|
import { execa } from "execa";
|
|
255
257
|
|
|
@@ -978,11 +980,382 @@ ${stdout}`
|
|
|
978
980
|
}
|
|
979
981
|
};
|
|
980
982
|
|
|
983
|
+
// src/agents/opencode.ts
|
|
984
|
+
var OPENCODE_MIN_VERSION = "1.15.0";
|
|
985
|
+
var DEFAULT_MAX_PROMPT_BYTES2 = 1e5;
|
|
986
|
+
var OPENCODE_INSTALL_URL = "https://opencode.ai/docs";
|
|
987
|
+
var MUTATING_BASH = [
|
|
988
|
+
"rm *",
|
|
989
|
+
"rmdir *",
|
|
990
|
+
"mv *",
|
|
991
|
+
"cp *",
|
|
992
|
+
"chmod *",
|
|
993
|
+
"chown *",
|
|
994
|
+
"mkdir *",
|
|
995
|
+
"touch *",
|
|
996
|
+
"tee *",
|
|
997
|
+
"git add *",
|
|
998
|
+
"git commit *",
|
|
999
|
+
"git push *",
|
|
1000
|
+
"git checkout *",
|
|
1001
|
+
"git reset *",
|
|
1002
|
+
"git rebase *",
|
|
1003
|
+
"npm install *",
|
|
1004
|
+
"npm ci *",
|
|
1005
|
+
"pnpm install *",
|
|
1006
|
+
"yarn add *"
|
|
1007
|
+
];
|
|
1008
|
+
function isRecord2(value) {
|
|
1009
|
+
return typeof value === "object" && value !== null;
|
|
1010
|
+
}
|
|
1011
|
+
function maxPromptBytesOf2(settings) {
|
|
1012
|
+
return settings.maxPromptBytes ?? DEFAULT_MAX_PROMPT_BYTES2;
|
|
1013
|
+
}
|
|
1014
|
+
function interpretOpenCodeAuth(text) {
|
|
1015
|
+
const cleaned = text.replaceAll(`${String.fromCharCode(27)}[`, "[").trim();
|
|
1016
|
+
if (cleaned === "") return false;
|
|
1017
|
+
if (/no credentials|not logged|not authenticated|no providers/i.test(cleaned)) return false;
|
|
1018
|
+
return /[a-z0-9][a-z0-9._-]*/i.test(cleaned);
|
|
1019
|
+
}
|
|
1020
|
+
function parseOpenCodeUsage(tokens) {
|
|
1021
|
+
if (!isRecord2(tokens)) return null;
|
|
1022
|
+
const result = {};
|
|
1023
|
+
if (typeof tokens.input === "number" && Number.isFinite(tokens.input)) {
|
|
1024
|
+
result.inputTokens = tokens.input;
|
|
1025
|
+
}
|
|
1026
|
+
if (typeof tokens.output === "number" && Number.isFinite(tokens.output)) {
|
|
1027
|
+
result.outputTokens = tokens.output;
|
|
1028
|
+
}
|
|
1029
|
+
const cache = isRecord2(tokens.cache) ? tokens.cache : {};
|
|
1030
|
+
if (typeof cache.read === "number" && Number.isFinite(cache.read)) {
|
|
1031
|
+
result.cacheReadTokens = cache.read;
|
|
1032
|
+
}
|
|
1033
|
+
if (typeof cache.write === "number" && Number.isFinite(cache.write)) {
|
|
1034
|
+
result.cacheCreationTokens = cache.write;
|
|
1035
|
+
}
|
|
1036
|
+
return Object.keys(result).length === 0 ? null : result;
|
|
1037
|
+
}
|
|
1038
|
+
function isPermissionDenial2(message) {
|
|
1039
|
+
return /permission|denied|not allowed|external_directory/i.test(message);
|
|
1040
|
+
}
|
|
1041
|
+
function toolDetail(name, input) {
|
|
1042
|
+
const key = name === "bash" || name === "read" || name === "edit" || name === "write" ? ["command", "path", "filePath", "file"] : ["path", "pattern", "query", "url", "command"];
|
|
1043
|
+
for (const field of key) {
|
|
1044
|
+
const value = input[field];
|
|
1045
|
+
if (typeof value === "string" && value !== "") {
|
|
1046
|
+
return value.length > 80 ? `${value.slice(0, 77)}...` : value;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
return "";
|
|
1050
|
+
}
|
|
1051
|
+
function extractError(event) {
|
|
1052
|
+
if (typeof event.error === "string" && event.error !== "") return event.error;
|
|
1053
|
+
const error = isRecord2(event.error) ? event.error : {};
|
|
1054
|
+
const data = isRecord2(error.data) ? error.data : {};
|
|
1055
|
+
if (typeof data.message === "string" && data.message !== "") return data.message;
|
|
1056
|
+
if (typeof error.message === "string" && error.message !== "") return error.message;
|
|
1057
|
+
if (typeof error.name === "string" && error.name !== "") return error.name;
|
|
1058
|
+
return "OpenCode reported an error";
|
|
1059
|
+
}
|
|
1060
|
+
function consumeOpenCodeEvent(event, onEvent, acc) {
|
|
1061
|
+
if (typeof event.sessionID === "string" && event.sessionID !== "") {
|
|
1062
|
+
acc.sessionId = event.sessionID;
|
|
1063
|
+
}
|
|
1064
|
+
if (event.type === "text") {
|
|
1065
|
+
const part = isRecord2(event.part) ? event.part : {};
|
|
1066
|
+
if (typeof part.text === "string" && part.text !== "") {
|
|
1067
|
+
acc.result += part.text;
|
|
1068
|
+
onEvent?.({ kind: "text", text: part.text });
|
|
1069
|
+
}
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
if (event.type === "tool_use") {
|
|
1073
|
+
const part = isRecord2(event.part) ? event.part : {};
|
|
1074
|
+
const name = typeof part.tool === "string" ? part.tool : "tool";
|
|
1075
|
+
const state = isRecord2(part.state) ? part.state : {};
|
|
1076
|
+
const input = isRecord2(state.input) ? state.input : {};
|
|
1077
|
+
const detail = toolDetail(name, input);
|
|
1078
|
+
onEvent?.({ kind: "tool", name, ...detail === "" ? {} : { detail } });
|
|
1079
|
+
const error = isRecord2(state.error) ? state.error : {};
|
|
1080
|
+
const message = typeof error.message === "string" ? error.message : typeof state.status === "string" && state.status === "error" ? `${name} failed` : "";
|
|
1081
|
+
if (message !== "" && isPermissionDenial2(message)) {
|
|
1082
|
+
acc.permissionDenied = { tool: name, message };
|
|
1083
|
+
}
|
|
1084
|
+
return;
|
|
1085
|
+
}
|
|
1086
|
+
if (event.type === "step_finish") {
|
|
1087
|
+
const part = isRecord2(event.part) ? event.part : {};
|
|
1088
|
+
if (part.reason === "stop") acc.seenTerminal = true;
|
|
1089
|
+
const usage = parseOpenCodeUsage(part.tokens);
|
|
1090
|
+
if (usage) acc.usage = usage;
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
if (event.type === "error") {
|
|
1094
|
+
acc.error = extractError(event);
|
|
1095
|
+
acc.seenTerminal = true;
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
function parseOpenCodeStream(raw, onEvent) {
|
|
1099
|
+
const acc = {
|
|
1100
|
+
result: "",
|
|
1101
|
+
sessionId: null,
|
|
1102
|
+
model: null,
|
|
1103
|
+
usage: null,
|
|
1104
|
+
seenTerminal: false,
|
|
1105
|
+
error: null,
|
|
1106
|
+
permissionDenied: null
|
|
1107
|
+
};
|
|
1108
|
+
for (const line of raw.split("\n")) {
|
|
1109
|
+
const trimmed = line.trim();
|
|
1110
|
+
if (trimmed === "") continue;
|
|
1111
|
+
try {
|
|
1112
|
+
const event = JSON.parse(trimmed);
|
|
1113
|
+
if (!isRecord2(event)) continue;
|
|
1114
|
+
consumeOpenCodeEvent(event, onEvent, acc);
|
|
1115
|
+
} catch {
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
return acc;
|
|
1119
|
+
}
|
|
1120
|
+
function buildOpenCodePermission(permission, addDirs = []) {
|
|
1121
|
+
const external = { "*": "deny" };
|
|
1122
|
+
for (const dir of addDirs) {
|
|
1123
|
+
const normalized = dir.replace(/\\/g, "/").replace(/\/$/, "");
|
|
1124
|
+
if (normalized === "") continue;
|
|
1125
|
+
external[normalized] = "allow";
|
|
1126
|
+
external[`${normalized}/**`] = "allow";
|
|
1127
|
+
}
|
|
1128
|
+
const bash = permission === "read-only" ? Object.fromEntries([["*", "allow"], ...MUTATING_BASH.map((rule) => [rule, "deny"])]) : "allow";
|
|
1129
|
+
return {
|
|
1130
|
+
edit: permission === "read-only" ? "deny" : "allow",
|
|
1131
|
+
bash,
|
|
1132
|
+
question: "deny",
|
|
1133
|
+
external_directory: external
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
function buildOpenCodeArgv(invocation, settings) {
|
|
1137
|
+
if (settings.model && !settings.model.includes("/")) {
|
|
1138
|
+
return {
|
|
1139
|
+
error: "configuration: OpenCode model must be provider/model (for example opencode-go/qwen3.8-flash)."
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
const promptBytes = Buffer.byteLength(invocation.prompt, "utf8");
|
|
1143
|
+
const limit = maxPromptBytesOf2(settings.opencode);
|
|
1144
|
+
if (promptBytes > limit) {
|
|
1145
|
+
return {
|
|
1146
|
+
error: `configuration: prompt exceeds OpenCode argv limit (${promptBytes} bytes > ${limit} maxPromptBytes). The promptChannel is argv; shorten the prompt or raise agent.opencode.maxPromptBytes.`
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
const workspace = invocation.workingDirectory ?? process.cwd();
|
|
1150
|
+
const args = ["run", "--format", "json", "--dir", workspace, "--auto"];
|
|
1151
|
+
if (settings.model) args.push("--model", settings.model);
|
|
1152
|
+
if (settings.opencode.variant) args.push("--variant", settings.opencode.variant);
|
|
1153
|
+
args.push(invocation.prompt);
|
|
1154
|
+
return {
|
|
1155
|
+
command: "opencode",
|
|
1156
|
+
args,
|
|
1157
|
+
env: {
|
|
1158
|
+
...process.env,
|
|
1159
|
+
OPENCODE_PERMISSION: JSON.stringify(
|
|
1160
|
+
buildOpenCodePermission(invocation.permission, invocation.addDirs ?? [])
|
|
1161
|
+
)
|
|
1162
|
+
}
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
function opencodeRunVerdict(input) {
|
|
1166
|
+
if (input.parsed.permissionDenied) {
|
|
1167
|
+
return {
|
|
1168
|
+
success: false,
|
|
1169
|
+
error: `configuration: permission denied for ${input.parsed.permissionDenied.tool}. ${input.parsed.permissionDenied.message}`
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
if (input.parsed.error) {
|
|
1173
|
+
return { success: false, error: input.parsed.error };
|
|
1174
|
+
}
|
|
1175
|
+
const hasTerminalEvidence = input.parsed.seenTerminal || input.parsed.result.trim() !== "" || input.parsed.error !== null;
|
|
1176
|
+
if (!hasTerminalEvidence) {
|
|
1177
|
+
return {
|
|
1178
|
+
success: false,
|
|
1179
|
+
error: "configuration: OpenCode finished without a terminal event in the stream."
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
if (input.exitCode !== 0 || input.timedOut) {
|
|
1183
|
+
return {
|
|
1184
|
+
success: false,
|
|
1185
|
+
error: input.parsed.error ?? `opencode exited with code ${input.exitCode}`
|
|
1186
|
+
};
|
|
1187
|
+
}
|
|
1188
|
+
return { success: true, error: null };
|
|
1189
|
+
}
|
|
1190
|
+
function configurationFailure2(agent, message, rawOutput = message) {
|
|
1191
|
+
return {
|
|
1192
|
+
success: false,
|
|
1193
|
+
result: "",
|
|
1194
|
+
rawOutput,
|
|
1195
|
+
exitCode: 1,
|
|
1196
|
+
usage: null,
|
|
1197
|
+
error: message,
|
|
1198
|
+
agent,
|
|
1199
|
+
harnessVersion: peekHarnessVersion("opencode")
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
var OpenCodeRunner = class {
|
|
1203
|
+
id = "opencode";
|
|
1204
|
+
capabilities = OPENCODE_CAPABILITIES;
|
|
1205
|
+
versionCommand() {
|
|
1206
|
+
return { command: "opencode", args: ["--version"] };
|
|
1207
|
+
}
|
|
1208
|
+
authCommand() {
|
|
1209
|
+
return { command: "opencode", args: ["auth", "list"] };
|
|
1210
|
+
}
|
|
1211
|
+
async run(invocation, settings) {
|
|
1212
|
+
const agent = { provider: this.id, model: settings.model };
|
|
1213
|
+
const built = buildOpenCodeArgv(invocation, settings);
|
|
1214
|
+
if ("error" in built) return configurationFailure2(agent, built.error);
|
|
1215
|
+
const minimum = settings.opencode.minVersion ?? OPENCODE_MIN_VERSION;
|
|
1216
|
+
const installed2 = peekHarnessVersion(this.id);
|
|
1217
|
+
if (typeof installed2 === "string" && compareVersions(installed2, minimum) < 0) {
|
|
1218
|
+
return configurationFailure2(
|
|
1219
|
+
agent,
|
|
1220
|
+
`configuration: opencode ${installed2} is below the minimum ${minimum}. Update OpenCode CLI.`
|
|
1221
|
+
);
|
|
1222
|
+
}
|
|
1223
|
+
const { command, args, env } = built;
|
|
1224
|
+
const startTime = Date.now();
|
|
1225
|
+
const timeoutMs = invocation.timeout;
|
|
1226
|
+
let cleanup = null;
|
|
1227
|
+
try {
|
|
1228
|
+
const subprocess = execa3(command, args, {
|
|
1229
|
+
reject: false,
|
|
1230
|
+
timeout: timeoutMs > 0 ? timeoutMs : void 0,
|
|
1231
|
+
stripFinalNewline: false,
|
|
1232
|
+
stdin: "ignore",
|
|
1233
|
+
env,
|
|
1234
|
+
...invocation.workingDirectory ? { cwd: invocation.workingDirectory } : {}
|
|
1235
|
+
});
|
|
1236
|
+
const unregister = registerChild({
|
|
1237
|
+
kill: (signal) => subprocess.kill(signal),
|
|
1238
|
+
done: subprocess.then(
|
|
1239
|
+
() => void 0,
|
|
1240
|
+
() => void 0
|
|
1241
|
+
)
|
|
1242
|
+
});
|
|
1243
|
+
const watchdog = createWatchdog({
|
|
1244
|
+
...invocation.inactivityTimeoutMs === void 0 ? {} : { inactivityTimeoutMs: invocation.inactivityTimeoutMs },
|
|
1245
|
+
child: {
|
|
1246
|
+
kill: (signal) => subprocess.kill(signal),
|
|
1247
|
+
done: subprocess.then(
|
|
1248
|
+
() => void 0,
|
|
1249
|
+
() => void 0
|
|
1250
|
+
)
|
|
1251
|
+
}
|
|
1252
|
+
});
|
|
1253
|
+
cleanup = () => {
|
|
1254
|
+
watchdog.stop();
|
|
1255
|
+
unregister();
|
|
1256
|
+
};
|
|
1257
|
+
const chunks = [];
|
|
1258
|
+
if (subprocess.stdout) {
|
|
1259
|
+
subprocess.stdout.on("data", (chunk) => {
|
|
1260
|
+
watchdog.beat();
|
|
1261
|
+
const text = chunk.toString();
|
|
1262
|
+
chunks.push(text);
|
|
1263
|
+
invocation.onLine?.(text);
|
|
1264
|
+
});
|
|
1265
|
+
}
|
|
1266
|
+
if (subprocess.stderr) {
|
|
1267
|
+
subprocess.stderr.on("data", () => {
|
|
1268
|
+
watchdog.beat();
|
|
1269
|
+
});
|
|
1270
|
+
}
|
|
1271
|
+
const proc = await subprocess;
|
|
1272
|
+
watchdog.stop();
|
|
1273
|
+
unregister();
|
|
1274
|
+
const stdout = chunks.join("") || proc.stdout?.toString() || "";
|
|
1275
|
+
const stderr = proc.stderr?.toString() ?? "";
|
|
1276
|
+
const parsed = parseOpenCodeStream(stdout, invocation.onEvent);
|
|
1277
|
+
const rawOutput = stdout + (stderr ? `
|
|
1278
|
+
${stderr}` : "");
|
|
1279
|
+
const exitCode = proc.exitCode ?? 1;
|
|
1280
|
+
const elapsedMs = Date.now() - startTime;
|
|
1281
|
+
const timedOut = wasTimedOut(proc, timeoutMs, elapsedMs);
|
|
1282
|
+
const stalled = watchdog.stalled;
|
|
1283
|
+
const authFailed = /not authenticated|unauthorized|no api key|auth required|login required/i.test(
|
|
1284
|
+
`${stderr}
|
|
1285
|
+
${stdout}`
|
|
1286
|
+
);
|
|
1287
|
+
if (stalled) {
|
|
1288
|
+
return {
|
|
1289
|
+
success: false,
|
|
1290
|
+
result: "",
|
|
1291
|
+
rawOutput: describeStall(watchdog.silentMs, "opencode"),
|
|
1292
|
+
exitCode: 1,
|
|
1293
|
+
usage: parsed.usage,
|
|
1294
|
+
error: describeStall(watchdog.silentMs, "opencode"),
|
|
1295
|
+
agent: { provider: this.id, model: parsed.model ?? settings.model },
|
|
1296
|
+
sessionId: parsed.sessionId ?? void 0,
|
|
1297
|
+
harnessVersion: peekHarnessVersion(this.id)
|
|
1298
|
+
};
|
|
1299
|
+
}
|
|
1300
|
+
if (authFailed) {
|
|
1301
|
+
return {
|
|
1302
|
+
success: false,
|
|
1303
|
+
result: parsed.result,
|
|
1304
|
+
rawOutput,
|
|
1305
|
+
exitCode: timedOut ? 1 : exitCode,
|
|
1306
|
+
usage: parsed.usage,
|
|
1307
|
+
error: stderr.trim() || "OpenCode is not authenticated.",
|
|
1308
|
+
agent: { provider: this.id, model: parsed.model ?? settings.model },
|
|
1309
|
+
sessionId: parsed.sessionId ?? void 0,
|
|
1310
|
+
harnessVersion: peekHarnessVersion(this.id)
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
const verdict = opencodeRunVerdict({ exitCode, parsed, timedOut });
|
|
1314
|
+
if (!verdict.success) {
|
|
1315
|
+
return {
|
|
1316
|
+
success: false,
|
|
1317
|
+
result: parsed.result,
|
|
1318
|
+
rawOutput,
|
|
1319
|
+
exitCode: timedOut || exitCode === 0 && verdict.error?.startsWith("configuration:") === true ? 1 : exitCode,
|
|
1320
|
+
usage: parsed.usage,
|
|
1321
|
+
error: timedOut ? describeAgentFailure(proc, stderr || rawOutput, timeoutMs, elapsedMs, command) : verdict.error ?? parsed.error ?? (stderr.trim() || rawOutput),
|
|
1322
|
+
agent: { provider: this.id, model: parsed.model ?? settings.model },
|
|
1323
|
+
sessionId: parsed.sessionId ?? void 0,
|
|
1324
|
+
harnessVersion: peekHarnessVersion(this.id)
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
return {
|
|
1328
|
+
success: true,
|
|
1329
|
+
result: parsed.result,
|
|
1330
|
+
rawOutput,
|
|
1331
|
+
exitCode,
|
|
1332
|
+
usage: parsed.usage,
|
|
1333
|
+
error: null,
|
|
1334
|
+
agent: { provider: this.id, model: parsed.model ?? settings.model },
|
|
1335
|
+
sessionId: parsed.sessionId ?? void 0,
|
|
1336
|
+
harnessVersion: peekHarnessVersion(this.id)
|
|
1337
|
+
};
|
|
1338
|
+
} catch (err) {
|
|
1339
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1340
|
+
const missing = /ENOENT|not found|command not found/i.test(message);
|
|
1341
|
+
return configurationFailure2(
|
|
1342
|
+
agent,
|
|
1343
|
+
missing ? `configuration: opencode is not installed. Install OpenCode CLI: ${OPENCODE_INSTALL_URL}` : message
|
|
1344
|
+
);
|
|
1345
|
+
} finally {
|
|
1346
|
+
cleanup?.();
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
};
|
|
1350
|
+
|
|
1351
|
+
// src/agents/registry.ts
|
|
1352
|
+
import { execa as execa6 } from "execa";
|
|
1353
|
+
|
|
981
1354
|
// src/agents/codex.ts
|
|
982
1355
|
import { mkdtemp, readFile, rm } from "node:fs/promises";
|
|
983
1356
|
import { tmpdir } from "node:os";
|
|
984
1357
|
import { join } from "node:path";
|
|
985
|
-
import { execa as
|
|
1358
|
+
import { execa as execa4 } from "execa";
|
|
986
1359
|
var BANNED_CODEX_FLAGS = [
|
|
987
1360
|
"--dangerously-bypass-approvals-and-sandbox",
|
|
988
1361
|
"--dangerously-bypass-hook-trust"
|
|
@@ -1143,7 +1516,7 @@ var CodexRunner = class {
|
|
|
1143
1516
|
const startTime = Date.now();
|
|
1144
1517
|
let cleanup = null;
|
|
1145
1518
|
try {
|
|
1146
|
-
const subprocess =
|
|
1519
|
+
const subprocess = execa4("codex", args, {
|
|
1147
1520
|
input: invocation.prompt,
|
|
1148
1521
|
reject: false,
|
|
1149
1522
|
timeout: invocation.timeout,
|
|
@@ -1266,17 +1639,17 @@ ${stderr}` : "");
|
|
|
1266
1639
|
};
|
|
1267
1640
|
|
|
1268
1641
|
// src/agents/cursor.ts
|
|
1269
|
-
import { execa as
|
|
1270
|
-
function
|
|
1642
|
+
import { execa as execa5 } from "execa";
|
|
1643
|
+
function isRecord3(value) {
|
|
1271
1644
|
return typeof value === "object" && value !== null;
|
|
1272
1645
|
}
|
|
1273
1646
|
function toolName(call) {
|
|
1274
|
-
if (
|
|
1275
|
-
const args =
|
|
1647
|
+
if (isRecord3(call.readToolCall)) {
|
|
1648
|
+
const args = isRecord3(call.readToolCall.args) ? call.readToolCall.args : {};
|
|
1276
1649
|
return { name: "Read", detail: typeof args.path === "string" ? args.path : "" };
|
|
1277
1650
|
}
|
|
1278
|
-
if (
|
|
1279
|
-
const args =
|
|
1651
|
+
if (isRecord3(call.writeToolCall)) {
|
|
1652
|
+
const args = isRecord3(call.writeToolCall.args) ? call.writeToolCall.args : {};
|
|
1280
1653
|
return { name: "Edit", detail: typeof args.path === "string" ? args.path : "" };
|
|
1281
1654
|
}
|
|
1282
1655
|
const key = Object.keys(call).find((name) => name.endsWith("ToolCall"));
|
|
@@ -1290,18 +1663,18 @@ function consumeCursorEvent(event, onEvent) {
|
|
|
1290
1663
|
};
|
|
1291
1664
|
}
|
|
1292
1665
|
if (event.type === "assistant") {
|
|
1293
|
-
const message =
|
|
1666
|
+
const message = isRecord3(event.message) ? event.message : {};
|
|
1294
1667
|
const content = Array.isArray(message.content) ? message.content : [];
|
|
1295
1668
|
const texts = [];
|
|
1296
1669
|
for (const block of content) {
|
|
1297
|
-
if (
|
|
1670
|
+
if (isRecord3(block) && typeof block.text === "string") texts.push(block.text);
|
|
1298
1671
|
}
|
|
1299
1672
|
const text = texts.join("");
|
|
1300
1673
|
if (text && onEvent) onEvent({ kind: "text", text });
|
|
1301
1674
|
return { text };
|
|
1302
1675
|
}
|
|
1303
1676
|
if (event.type === "tool_call" && event.subtype === "started") {
|
|
1304
|
-
const call =
|
|
1677
|
+
const call = isRecord3(event.tool_call) ? event.tool_call : {};
|
|
1305
1678
|
const { name, detail } = toolName(call);
|
|
1306
1679
|
onEvent?.({ kind: "tool", name, ...detail === "" ? {} : { detail } });
|
|
1307
1680
|
return {};
|
|
@@ -1326,7 +1699,7 @@ function parseCursorStream(raw, onEvent) {
|
|
|
1326
1699
|
if (trimmed === "") continue;
|
|
1327
1700
|
try {
|
|
1328
1701
|
const event = JSON.parse(trimmed);
|
|
1329
|
-
if (!
|
|
1702
|
+
if (!isRecord3(event)) continue;
|
|
1330
1703
|
const seen = consumeCursorEvent(event, onEvent);
|
|
1331
1704
|
if (seen.result !== void 0) result = seen.result;
|
|
1332
1705
|
if (seen.isError === true) isError = true;
|
|
@@ -1371,7 +1744,7 @@ var CursorRunner = class {
|
|
|
1371
1744
|
const agent = { provider: this.id, model: settings.model };
|
|
1372
1745
|
let cleanup = null;
|
|
1373
1746
|
try {
|
|
1374
|
-
const subprocess =
|
|
1747
|
+
const subprocess = execa5(command, args, {
|
|
1375
1748
|
reject: false,
|
|
1376
1749
|
timeout: invocation.timeout,
|
|
1377
1750
|
stripFinalNewline: false,
|
|
@@ -1489,6 +1862,7 @@ function ensureRunnersRegistered() {
|
|
|
1489
1862
|
if (!registered.has("codex")) registerRunner(new CodexRunner());
|
|
1490
1863
|
if (!registered.has("cursor")) registerRunner(new CursorRunner());
|
|
1491
1864
|
if (!registered.has("antigravity")) registerRunner(new AntigravityRunner());
|
|
1865
|
+
if (!registered.has("opencode")) registerRunner(new OpenCodeRunner());
|
|
1492
1866
|
}
|
|
1493
1867
|
function runnerFor(providerId) {
|
|
1494
1868
|
ensureRunnersRegistered();
|
|
@@ -1506,7 +1880,7 @@ async function ensureHarnessVersion(providerId) {
|
|
|
1506
1880
|
const runner = runnerFor(providerId);
|
|
1507
1881
|
const { command, args } = runner.versionCommand();
|
|
1508
1882
|
try {
|
|
1509
|
-
const proc = await
|
|
1883
|
+
const proc = await execa6(command, args, { reject: false, timeout: 1e4 });
|
|
1510
1884
|
const version = proc.exitCode === 0 ? proc.stdout?.toString().trim() ?? null : null;
|
|
1511
1885
|
cacheHarnessVersion(providerId, version);
|
|
1512
1886
|
return version;
|
|
@@ -1516,6 +1890,92 @@ async function ensureHarnessVersion(providerId) {
|
|
|
1516
1890
|
}
|
|
1517
1891
|
}
|
|
1518
1892
|
|
|
1893
|
+
// src/routing/opencode-go.ts
|
|
1894
|
+
var OPENCODE_GO_POLICY_VERSION = "1";
|
|
1895
|
+
var OPENCODE_GO_MODELS = {
|
|
1896
|
+
cheap: "opencode-go/mimo-v2.5",
|
|
1897
|
+
default: "opencode-go/qwen3.8-flash",
|
|
1898
|
+
codingCheap: "opencode-go/deepseek-v4-flash",
|
|
1899
|
+
escalate: "opencode-go/gpt-5.6-luna",
|
|
1900
|
+
specialist: "opencode-go/kimi-k2.7-code"
|
|
1901
|
+
};
|
|
1902
|
+
var OPENCODE_GO_LADDER = [
|
|
1903
|
+
OPENCODE_GO_MODELS.cheap,
|
|
1904
|
+
OPENCODE_GO_MODELS.default,
|
|
1905
|
+
OPENCODE_GO_MODELS.escalate,
|
|
1906
|
+
OPENCODE_GO_MODELS.specialist
|
|
1907
|
+
];
|
|
1908
|
+
var ROLE_BY_MODEL = {
|
|
1909
|
+
[OPENCODE_GO_MODELS.cheap]: "cheap",
|
|
1910
|
+
[OPENCODE_GO_MODELS.default]: "default",
|
|
1911
|
+
[OPENCODE_GO_MODELS.codingCheap]: "coding-cheap",
|
|
1912
|
+
[OPENCODE_GO_MODELS.escalate]: "escalate",
|
|
1913
|
+
[OPENCODE_GO_MODELS.specialist]: "specialist"
|
|
1914
|
+
};
|
|
1915
|
+
function choice(role, reasonCodes) {
|
|
1916
|
+
return {
|
|
1917
|
+
model: OPENCODE_GO_MODELS[role === "coding-cheap" ? "codingCheap" : role],
|
|
1918
|
+
role,
|
|
1919
|
+
reasonCodes
|
|
1920
|
+
};
|
|
1921
|
+
}
|
|
1922
|
+
function phaseDefault(phase) {
|
|
1923
|
+
if (phase === "analyze") return "cheap";
|
|
1924
|
+
if (phase === "pr-review") return "escalate";
|
|
1925
|
+
return "default";
|
|
1926
|
+
}
|
|
1927
|
+
function correctionRole(cycle) {
|
|
1928
|
+
if (cycle <= 1) return "coding-cheap";
|
|
1929
|
+
if (cycle === 2) return "default";
|
|
1930
|
+
if (cycle === 3) return "escalate";
|
|
1931
|
+
return "specialist";
|
|
1932
|
+
}
|
|
1933
|
+
function resolveOpenCodeGoModel(input) {
|
|
1934
|
+
const reasons = ["OPENCODE_GO_POLICY"];
|
|
1935
|
+
if ((input.correctionCycle ?? 0) >= 1 && input.phase === "execute") {
|
|
1936
|
+
const role2 = correctionRole(input.correctionCycle ?? 1);
|
|
1937
|
+
if (role2 === "specialist") reasons.push("OPENCODE_GO_SPECIALIST");
|
|
1938
|
+
else if (role2 === "escalate") reasons.push("OPENCODE_GO_ESCALATE");
|
|
1939
|
+
else if (role2 === "coding-cheap") reasons.push("OPENCODE_GO_CODING_CHEAP");
|
|
1940
|
+
return choice(role2, reasons);
|
|
1941
|
+
}
|
|
1942
|
+
const taskClass = input.taskClass ?? "feature";
|
|
1943
|
+
const risk = input.risk ?? "low";
|
|
1944
|
+
let role = phaseDefault(input.phase);
|
|
1945
|
+
if (input.phase === "analyze" && risk === "high") {
|
|
1946
|
+
role = "default";
|
|
1947
|
+
} else if (input.phase === "generate" && taskClass === "docs") {
|
|
1948
|
+
role = "cheap";
|
|
1949
|
+
reasons.push("OPENCODE_GO_CHEAP");
|
|
1950
|
+
} else if (input.phase === "prd" && (risk === "high" || taskClass === "infra")) {
|
|
1951
|
+
role = "escalate";
|
|
1952
|
+
reasons.push("OPENCODE_GO_ESCALATE");
|
|
1953
|
+
} else if (input.phase === "plan" && risk === "high") {
|
|
1954
|
+
role = "escalate";
|
|
1955
|
+
reasons.push("OPENCODE_GO_ESCALATE");
|
|
1956
|
+
} else if (input.phase === "plan" && input.profile === "economy" && risk === "low") {
|
|
1957
|
+
role = "cheap";
|
|
1958
|
+
reasons.push("OPENCODE_GO_CHEAP");
|
|
1959
|
+
} else if (input.phase === "execute") {
|
|
1960
|
+
if (risk === "high" || taskClass === "infra" || taskClass === "refactor" && risk !== "low") {
|
|
1961
|
+
role = "escalate";
|
|
1962
|
+
reasons.push("OPENCODE_GO_ESCALATE");
|
|
1963
|
+
} else if ((taskClass === "bugfix" || taskClass === "test") && risk === "low") {
|
|
1964
|
+
role = "coding-cheap";
|
|
1965
|
+
reasons.push("OPENCODE_GO_CODING_CHEAP");
|
|
1966
|
+
}
|
|
1967
|
+
} else if (input.phase === "review" && risk === "high") {
|
|
1968
|
+
role = "escalate";
|
|
1969
|
+
reasons.push("OPENCODE_GO_ESCALATE");
|
|
1970
|
+
} else if (input.phase === "pr" && taskClass === "bugfix" && risk === "low") {
|
|
1971
|
+
role = "coding-cheap";
|
|
1972
|
+
reasons.push("OPENCODE_GO_CODING_CHEAP");
|
|
1973
|
+
} else if (input.phase === "pr-review" && risk === "low" && taskClass === "docs") {
|
|
1974
|
+
role = "default";
|
|
1975
|
+
}
|
|
1976
|
+
return choice(role, reasons);
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1519
1979
|
// src/agents/origins.ts
|
|
1520
1980
|
var trackedOrigins = null;
|
|
1521
1981
|
function setTrackedOrigins(origins) {
|
|
@@ -1534,7 +1994,8 @@ function mergeAgentBlocks(base, overlay) {
|
|
|
1534
1994
|
claude: { ...base.claude, ...dropUndefined(overlay.claude ?? {}) },
|
|
1535
1995
|
codex: { ...base.codex, ...dropUndefined(overlay.codex ?? {}) },
|
|
1536
1996
|
cursor: { ...base.cursor, ...dropUndefined(overlay.cursor ?? {}) },
|
|
1537
|
-
antigravity: { ...base.antigravity, ...dropUndefined(overlay.antigravity ?? {}) }
|
|
1997
|
+
antigravity: { ...base.antigravity, ...dropUndefined(overlay.antigravity ?? {}) },
|
|
1998
|
+
opencode: { ...base.opencode, ...dropUndefined(overlay.opencode ?? {}) }
|
|
1538
1999
|
};
|
|
1539
2000
|
}
|
|
1540
2001
|
function dropUndefined(value) {
|
|
@@ -1545,7 +2006,7 @@ function dropUndefined(value) {
|
|
|
1545
2006
|
return result;
|
|
1546
2007
|
}
|
|
1547
2008
|
async function resolveAgentFor(phase, options = {}) {
|
|
1548
|
-
const { getAgentCliOverrides: getAgentCliOverrides2, loadAgentConfig: loadAgentConfig2 } = await import("./config-
|
|
2009
|
+
const { getAgentCliOverrides: getAgentCliOverrides2, loadAgentConfig: loadAgentConfig2 } = await import("./config-6YTWPDBK.js");
|
|
1549
2010
|
const config = options.config ?? await loadAgentConfig2();
|
|
1550
2011
|
const cli = options.cli ?? getAgentCliOverrides2();
|
|
1551
2012
|
const defaultBlock = {
|
|
@@ -1554,7 +2015,8 @@ async function resolveAgentFor(phase, options = {}) {
|
|
|
1554
2015
|
claude: config.claude,
|
|
1555
2016
|
codex: config.codex,
|
|
1556
2017
|
cursor: config.cursor,
|
|
1557
|
-
antigravity: config.antigravity
|
|
2018
|
+
antigravity: config.antigravity,
|
|
2019
|
+
opencode: config.opencode
|
|
1558
2020
|
};
|
|
1559
2021
|
const phaseBlock = mergeAgentBlocks(config.phases[phase] ?? {}, cli.phases?.[phase]);
|
|
1560
2022
|
const merged = mergeAgentBlocks(defaultBlock, phaseBlock);
|
|
@@ -1571,6 +2033,9 @@ async function resolveAgentFor(phase, options = {}) {
|
|
|
1571
2033
|
model = cli.forceModel;
|
|
1572
2034
|
modelOrigin = "cli";
|
|
1573
2035
|
}
|
|
2036
|
+
if (provider === "opencode" && model === null) {
|
|
2037
|
+
model = resolveOpenCodeGoModel({ phase }).model;
|
|
2038
|
+
}
|
|
1574
2039
|
return {
|
|
1575
2040
|
provider,
|
|
1576
2041
|
model,
|
|
@@ -1578,6 +2043,7 @@ async function resolveAgentFor(phase, options = {}) {
|
|
|
1578
2043
|
codex: merged.codex ?? {},
|
|
1579
2044
|
cursor: merged.cursor ?? {},
|
|
1580
2045
|
antigravity: merged.antigravity ?? {},
|
|
2046
|
+
opencode: merged.opencode ?? {},
|
|
1581
2047
|
origin: { provider: providerOrigin, model: modelOrigin }
|
|
1582
2048
|
};
|
|
1583
2049
|
}
|
|
@@ -1620,7 +2086,7 @@ function parseAgentPhaseFlag(value) {
|
|
|
1620
2086
|
const [providerRaw, ...modelParts] = rest.split(":");
|
|
1621
2087
|
if (!providerRaw || !isAgentProviderId(providerRaw)) {
|
|
1622
2088
|
throw new Error(
|
|
1623
|
-
`Unknown agent provider "${providerRaw ?? ""}". Valid providers: claude, codex, cursor, antigravity.`
|
|
2089
|
+
`Unknown agent provider "${providerRaw ?? ""}". Valid providers: claude, codex, cursor, antigravity, opencode.`
|
|
1624
2090
|
);
|
|
1625
2091
|
}
|
|
1626
2092
|
const model = modelParts.length > 0 ? modelParts.join(":") : void 0;
|
|
@@ -1632,6 +2098,14 @@ function parseAgentPhaseFlag(value) {
|
|
|
1632
2098
|
}
|
|
1633
2099
|
};
|
|
1634
2100
|
}
|
|
2101
|
+
function applyOpenCodeGoModel(settings, input) {
|
|
2102
|
+
if (settings.provider !== "opencode" || settings.origin.model !== "default") {
|
|
2103
|
+
return settings;
|
|
2104
|
+
}
|
|
2105
|
+
const model = resolveOpenCodeGoModel(input).model;
|
|
2106
|
+
if (model === settings.model) return settings;
|
|
2107
|
+
return { ...settings, model };
|
|
2108
|
+
}
|
|
1635
2109
|
async function describeRunAgents(phases = AGENT_PHASES) {
|
|
1636
2110
|
const target = phases.length > 0 ? phases : AGENT_PHASES;
|
|
1637
2111
|
const byPhase = {};
|
|
@@ -1651,7 +2125,7 @@ async function describeRunAgents(phases = AGENT_PHASES) {
|
|
|
1651
2125
|
defaultModel = settings.model;
|
|
1652
2126
|
}
|
|
1653
2127
|
}
|
|
1654
|
-
const { loadAgentConfig: loadAgentConfig2 } = await import("./config-
|
|
2128
|
+
const { loadAgentConfig: loadAgentConfig2 } = await import("./config-6YTWPDBK.js");
|
|
1655
2129
|
const config = await loadAgentConfig2();
|
|
1656
2130
|
defaultProvider = config.provider;
|
|
1657
2131
|
defaultModel = config.model;
|
|
@@ -1689,9 +2163,10 @@ var PROVIDER_HARNESS = {
|
|
|
1689
2163
|
claude: "claude-code",
|
|
1690
2164
|
codex: "codex-cli",
|
|
1691
2165
|
cursor: "cursor-cli",
|
|
1692
|
-
antigravity: "antigravity-cli"
|
|
2166
|
+
antigravity: "antigravity-cli",
|
|
2167
|
+
opencode: "opencode-cli"
|
|
1693
2168
|
};
|
|
1694
|
-
var ALL_PROVIDERS =
|
|
2169
|
+
var ALL_PROVIDERS = AGENT_PROVIDER_IDS;
|
|
1695
2170
|
var probeCache = /* @__PURE__ */ new Map();
|
|
1696
2171
|
var inventoryCache = null;
|
|
1697
2172
|
function deriveState(installed2, authentication, cooldownUntil, nowMs) {
|
|
@@ -1735,7 +2210,7 @@ async function probeAgentUncached(id, options = {}) {
|
|
|
1735
2210
|
let installed2 = false;
|
|
1736
2211
|
try {
|
|
1737
2212
|
const { command, args } = runner.versionCommand();
|
|
1738
|
-
const proc = await
|
|
2213
|
+
const proc = await execa7(command, args, { reject: false, timeout: 1e4 });
|
|
1739
2214
|
installed2 = proc.exitCode === 0;
|
|
1740
2215
|
version = installed2 ? proc.stdout?.toString().trim() || "unknown" : null;
|
|
1741
2216
|
} catch {
|
|
@@ -1750,10 +2225,10 @@ async function probeAgentUncached(id, options = {}) {
|
|
|
1750
2225
|
} else if (runner.authCommand && runner.capabilities.authProbe !== "none") {
|
|
1751
2226
|
try {
|
|
1752
2227
|
const { command, args } = runner.authCommand();
|
|
1753
|
-
const auth = await
|
|
2228
|
+
const auth = await execa7(command, args, { reject: false, timeout: 1e4 });
|
|
1754
2229
|
const text = `${auth.stdout?.toString() ?? ""}
|
|
1755
2230
|
${auth.stderr?.toString() ?? ""}`;
|
|
1756
|
-
const ok = runner.capabilities.authProbe === "text" ? !/not logged in|not authenticated|no models available/i.test(text) : auth.exitCode === 0;
|
|
2231
|
+
const ok = runner.capabilities.authProbe === "text" ? id === "opencode" ? interpretOpenCodeAuth(text) : !/not logged in|not authenticated|no models available/i.test(text) : auth.exitCode === 0;
|
|
1757
2232
|
authentication = ok ? "confirmed" : "failed";
|
|
1758
2233
|
detail = ok ? `${version} (authenticated)` : `${version} (not authenticated)`;
|
|
1759
2234
|
} catch {
|
|
@@ -1831,6 +2306,9 @@ function installHint(id) {
|
|
|
1831
2306
|
if (id === "antigravity") {
|
|
1832
2307
|
return "Install Antigravity CLI: https://antigravity.google/docs/cli/install/";
|
|
1833
2308
|
}
|
|
2309
|
+
if (id === "opencode") {
|
|
2310
|
+
return "Install OpenCode CLI: https://opencode.ai/docs";
|
|
2311
|
+
}
|
|
1834
2312
|
return "Install Claude Code: https://docs.anthropic.com/en/docs/claude-code";
|
|
1835
2313
|
}
|
|
1836
2314
|
|
|
@@ -1896,7 +2374,7 @@ function readAgentEnv(env, warn) {
|
|
|
1896
2374
|
layer.provider = env.ISSUE_FLOW_AGENT;
|
|
1897
2375
|
} else {
|
|
1898
2376
|
warn(
|
|
1899
|
-
`Ignoring ISSUE_FLOW_AGENT="${env.ISSUE_FLOW_AGENT}": expected claude, codex, cursor or
|
|
2377
|
+
`Ignoring ISSUE_FLOW_AGENT="${env.ISSUE_FLOW_AGENT}": expected claude, codex, cursor, antigravity or opencode.`
|
|
1900
2378
|
);
|
|
1901
2379
|
}
|
|
1902
2380
|
}
|
|
@@ -1957,6 +2435,14 @@ function readAgentEnv(env, warn) {
|
|
|
1957
2435
|
antigravity.executeTimeout = env.ISSUE_FLOW_ANTIGRAVITY_EXECUTE_TIMEOUT;
|
|
1958
2436
|
}
|
|
1959
2437
|
if (Object.keys(antigravity).length > 0) layer.antigravity = antigravity;
|
|
2438
|
+
const opencode = {};
|
|
2439
|
+
if (env.ISSUE_FLOW_OPENCODE_VARIANT !== void 0 && env.ISSUE_FLOW_OPENCODE_VARIANT !== "") {
|
|
2440
|
+
opencode.variant = env.ISSUE_FLOW_OPENCODE_VARIANT;
|
|
2441
|
+
}
|
|
2442
|
+
if (env.ISSUE_FLOW_OPENCODE_MIN_VERSION !== void 0 && env.ISSUE_FLOW_OPENCODE_MIN_VERSION !== "") {
|
|
2443
|
+
opencode.minVersion = env.ISSUE_FLOW_OPENCODE_MIN_VERSION;
|
|
2444
|
+
}
|
|
2445
|
+
if (Object.keys(opencode).length > 0) layer.opencode = opencode;
|
|
1960
2446
|
return layer;
|
|
1961
2447
|
}
|
|
1962
2448
|
function readAgentKey(raw, label, warn) {
|
|
@@ -1979,6 +2465,7 @@ function readAgentKey(raw, label, warn) {
|
|
|
1979
2465
|
...parsed.data.codex !== void 0 ? { codex: parsed.data.codex } : {},
|
|
1980
2466
|
...parsed.data.cursor !== void 0 ? { cursor: parsed.data.cursor } : {},
|
|
1981
2467
|
...parsed.data.antigravity !== void 0 ? { antigravity: parsed.data.antigravity } : {},
|
|
2468
|
+
...parsed.data.opencode !== void 0 ? { opencode: parsed.data.opencode } : {},
|
|
1982
2469
|
...Object.keys(phases).length > 0 ? { phases } : {}
|
|
1983
2470
|
};
|
|
1984
2471
|
}
|
|
@@ -2001,7 +2488,8 @@ function mergeAgentBlockLayers(layers) {
|
|
|
2001
2488
|
claude: { ...merged.claude, ...layer.block.claude },
|
|
2002
2489
|
codex: { ...merged.codex, ...layer.block.codex },
|
|
2003
2490
|
cursor: { ...merged.cursor, ...layer.block.cursor },
|
|
2004
|
-
antigravity: { ...merged.antigravity, ...layer.block.antigravity }
|
|
2491
|
+
antigravity: { ...merged.antigravity, ...layer.block.antigravity },
|
|
2492
|
+
opencode: { ...merged.opencode, ...layer.block.opencode }
|
|
2005
2493
|
};
|
|
2006
2494
|
}
|
|
2007
2495
|
return any ? merged : void 0;
|
|
@@ -2014,6 +2502,7 @@ function dropUndefinedBlock(block) {
|
|
|
2014
2502
|
if (block.codex !== void 0) result.codex = block.codex;
|
|
2015
2503
|
if (block.cursor !== void 0) result.cursor = block.cursor;
|
|
2016
2504
|
if (block.antigravity !== void 0) result.antigravity = block.antigravity;
|
|
2505
|
+
if (block.opencode !== void 0) result.opencode = block.opencode;
|
|
2017
2506
|
return result;
|
|
2018
2507
|
}
|
|
2019
2508
|
async function loadAgentConfig(options = {}) {
|
|
@@ -2057,6 +2546,12 @@ async function loadAgentConfig(options = {}) {
|
|
|
2057
2546
|
env: envLayer.antigravity,
|
|
2058
2547
|
cli: cli.antigravity
|
|
2059
2548
|
});
|
|
2549
|
+
const opencode = mergeConfigLayers({
|
|
2550
|
+
global: globalLayer.opencode,
|
|
2551
|
+
project: projectLayer.opencode,
|
|
2552
|
+
env: envLayer.opencode,
|
|
2553
|
+
cli: cli.opencode
|
|
2554
|
+
});
|
|
2060
2555
|
const phases = {};
|
|
2061
2556
|
const phaseOrigins = {};
|
|
2062
2557
|
for (const phase of AGENT_PHASES) {
|
|
@@ -2101,7 +2596,7 @@ async function loadAgentConfig(options = {}) {
|
|
|
2101
2596
|
]),
|
|
2102
2597
|
phases: phaseOrigins
|
|
2103
2598
|
});
|
|
2104
|
-
const resolved = { provider, model, claude, codex, cursor, antigravity, phases };
|
|
2599
|
+
const resolved = { provider, model, claude, codex, cursor, antigravity, opencode, phases };
|
|
2105
2600
|
if (canCache) cachedAgentConfig = resolved;
|
|
2106
2601
|
return resolved;
|
|
2107
2602
|
}
|
|
@@ -2185,7 +2680,7 @@ async function resolvePaths(config, scriptDir) {
|
|
|
2185
2680
|
lastBranchFile: join2(base, ".last-branch"),
|
|
2186
2681
|
projectRoot
|
|
2187
2682
|
};
|
|
2188
|
-
const { bindTelemetry } = await import("./recorder-
|
|
2683
|
+
const { bindTelemetry } = await import("./recorder-LOU3PTIE.js");
|
|
2189
2684
|
bindTelemetry({ tasksPath: standalone.prdFile });
|
|
2190
2685
|
return standalone;
|
|
2191
2686
|
}
|
|
@@ -2743,6 +3238,8 @@ async function loadWebConfig(options = {}) {
|
|
|
2743
3238
|
var WEB_CONFIG_FILENAME = PROJECT_CONFIG_FILENAME;
|
|
2744
3239
|
|
|
2745
3240
|
export {
|
|
3241
|
+
OPENCODE_GO_POLICY_VERSION,
|
|
3242
|
+
resolveOpenCodeGoModel,
|
|
2746
3243
|
getShutdownSignal,
|
|
2747
3244
|
onShutdown,
|
|
2748
3245
|
installShutdownHandlers,
|
|
@@ -2784,7 +3281,8 @@ export {
|
|
|
2784
3281
|
WEB_CONFIG_FILENAME,
|
|
2785
3282
|
resolveAgentFor,
|
|
2786
3283
|
parseAgentPhaseFlag,
|
|
3284
|
+
applyOpenCodeGoModel,
|
|
2787
3285
|
describeRunAgents,
|
|
2788
3286
|
hasExplicitAgentSelection
|
|
2789
3287
|
};
|
|
2790
|
-
//# sourceMappingURL=chunk-
|
|
3288
|
+
//# sourceMappingURL=chunk-YC24E66P.js.map
|