nolo-cli 0.32.0 → 0.33.0-alpha.10
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 +6 -12
- package/{_executeModel-LTMSIBS4.js → _executeModel-YXWAXPWQ.js} +77 -67
- package/{action-HI3VRQLP.js → action-OQWPNKAH.js} +17 -15
- package/{addContentAction-NCURJ6PK.js → addContentAction-YVINZQTF.js} +14 -13
- package/{addDialogAgentAction-RCY4ECF4.js → addDialogAgentAction-6IOCIEPI.js} +14 -13
- package/{agentDeleteCommand-D3MYZK6E.js → agentDeleteCommand-VGHWJN5T.js} +44 -60
- package/{agentEmailCommands-6BXS574I.js → agentEmailCommands-ZLRF35I3.js} +91 -81
- package/{agentGrantCommands-E7ZZFRHK.js → agentGrantCommands-2LEZDADN.js} +2 -2
- package/{agentListCommands-SF3HEUTT.js → agentListCommands-OHZJ7AFP.js} +75 -79
- package/{agentMachineCommands-YARVXRR6.js → agentMachineCommands-62PHPGTK.js} +43 -59
- package/{agentPullCommand-ARSIDNVC.js → agentPullCommand-TSWAJPS2.js} +5 -5
- package/agentRecordCommands-CW2JWERD.js +113 -0
- package/agentRunCommand-O4HV7YWA.js +131 -0
- package/{agentRuntimeLocal-ATGJY6WC.js → agentRuntimeLocal-4PSBNUOE.js} +37 -51
- package/{agentSlice-4XWW344Y.js → agentSlice-OUTQ5O72.js} +16 -14
- package/{appDeleteCommands-63JO7RXS.js → appDeleteCommands-VTDBVHYR.js} +40 -56
- package/{appDeployCommands-Y7EWYUGM.js → appDeployCommands-WOUP7MDA.js} +40 -56
- package/{appDeployStatusCommands-ZBEY3DWB.js → appDeployStatusCommands-N2NWZKEN.js} +40 -56
- package/{appGetCommands-QSSOSEYL.js → appGetCommands-GYKLWWCA.js} +40 -56
- package/{appListCommands-PH3XWNDX.js → appListCommands-RUWBSEJK.js} +40 -56
- package/{authCommand-BBDHMSO6.js → authCommand-YVXVH25I.js} +21 -20
- package/{authCommands-4UDEKRE6.js → authCommands-WTW6MEYS.js} +10 -10
- package/chatQueueReduxAdapter-2QJAD4KQ.js +181 -0
- package/{chunk-EEF23OOK.js → chunk-266VUBLK.js} +7 -7
- package/{chunk-TEYN42N3.js → chunk-2IVEXF7P.js} +11 -4
- package/{chunk-2HXKAPH7.js → chunk-2QLAVHVK.js} +6 -6
- package/{chunk-WTGCTVJG.js → chunk-2QTEYCR7.js} +3 -6
- package/{chunk-DOISARTV.js → chunk-2UECQ7RE.js} +1 -1
- package/{chunk-E3APEOIM.js → chunk-3GZXL6FM.js} +4 -19
- package/{chunk-KGEVMUMI.js → chunk-3RUW3ONU.js} +11 -11
- package/{chunk-RXVX5LFR.js → chunk-3UG2IIZ3.js} +1 -1
- package/{chunk-O5OMJPPF.js → chunk-3WXRAHCG.js} +6 -5
- package/{chunk-V54H24CQ.js → chunk-4F5M2MW5.js} +3 -3
- package/{chunk-2ZRP5RUG.js → chunk-4OV7DXFJ.js} +12 -11
- package/{chunk-W367V5CH.js → chunk-54BAYVCY.js} +404 -117
- package/{chunk-GAF5X6B7.js → chunk-5QW3O3ET.js} +284 -310
- package/{chunk-DLWCWUNW.js → chunk-64OVNZXE.js} +1197 -757
- package/{chunk-ODS2MVMU.js → chunk-66CWPJK2.js} +19 -303
- package/{chunk-PTHDLIRB.js → chunk-6KOS7ZBZ.js} +1 -1
- package/{chunk-MXFQHRSS.js → chunk-6XIE5ZGI.js} +1 -1
- package/{chunk-ATK5WJOA.js → chunk-6YXDOOSH.js} +4 -4
- package/{chunk-SWYMCHM7.js → chunk-75U775RZ.js} +69 -1
- package/{chunk-UDA3ZYLW.js → chunk-7YYHKBJP.js} +31 -72
- package/chunk-ADLYMQB6.js +642 -0
- package/{chunk-ZWJOGMKH.js → chunk-AETZ7AD6.js} +1 -1
- package/{chunk-43EPBPZ2.js → chunk-ASDXPOJH.js} +2 -2
- package/{chunk-44HACRCK.js → chunk-BANYWEYY.js} +4 -4
- package/chunk-BFPSVRCQ.js +1799 -0
- package/{chunk-TOW4E763.js → chunk-BJIRLHJR.js} +2 -0
- package/{chunk-ZJ2WITOO.js → chunk-BQXOGBFN.js} +2 -2
- package/{chunk-4PNU7W7K.js → chunk-C7H5GMXT.js} +107 -125
- package/chunk-CPYYM4RJ.js +98 -0
- package/{chunk-3GZT3L6F.js → chunk-DDVIBKIB.js} +326 -481
- package/{chunk-2K4NDPT5.js → chunk-DFUPZE6V.js} +20 -12
- package/{chunk-BGTUGS3T.js → chunk-DKMOY3QI.js} +4 -30
- package/chunk-EGRJC4ZN.js +91 -0
- package/{chunk-K6QEHQZ3.js → chunk-EKLRRZIN.js} +1 -1
- package/{chunk-6HYVJV7B.js → chunk-ESHYE7WU.js} +2 -1
- package/{chunk-XFG3BAKN.js → chunk-FMEVI5TG.js} +65 -0
- package/{chunk-U4TRT5V4.js → chunk-GEPHJJW4.js} +1 -1
- package/chunk-GJXRIOLS.js +241 -0
- package/{chunk-VP4WJWKW.js → chunk-GM6PU7B6.js} +431 -345
- package/chunk-HS7MLR74.js +38 -0
- package/{chunk-3AUWC6EM.js → chunk-HUCT7HID.js} +5 -84
- package/chunk-IDAU4UFS.js +437 -0
- package/{chunk-Q76I4SXI.js → chunk-IHEC7VBF.js} +1 -1
- package/{chunk-OWHNFZNW.js → chunk-IKWTTKWG.js} +4 -4
- package/{chunk-3LY4F7SV.js → chunk-INZZJSQF.js} +348 -331
- package/{chunk-ZJH3LKEW.js → chunk-IR6W62Q6.js} +102 -15
- package/{chunk-RH53LMEJ.js → chunk-J44TVX2O.js} +2 -4
- package/{chunk-CF2WZF4N.js → chunk-J6WH4PYZ.js} +0 -8
- package/chunk-KHCLMKZZ.js +3062 -0
- package/{chunk-WHLVPK32.js → chunk-KMSZPTM6.js} +27 -3
- package/{chunk-L3GCUVOH.js → chunk-KSRFVP36.js} +1 -1
- package/{chunk-XDHHF73Q.js → chunk-LUIHOUZC.js} +95 -95
- package/{chunk-UOJRKY5J.js → chunk-M2YMQOLC.js} +44 -53
- package/chunk-M3JEW6N6.js +193 -0
- package/{chunk-MITD7CON.js → chunk-MDIAECHA.js} +1461 -301
- package/{chunk-IXZX5CJK.js → chunk-MQRZ5QUH.js} +1 -1
- package/{chunk-3ASDTTEI.js → chunk-MRWGHF56.js} +2 -0
- package/{chunk-Q3CSL43H.js → chunk-N4XRDM26.js} +1 -1
- package/{chunk-D34RI6NI.js → chunk-NNWHHCVP.js} +75 -1
- package/{chunk-CAENRKNJ.js → chunk-OR2HNLVO.js} +126 -88
- package/chunk-OW765XBU.js +10 -0
- package/{chunk-VMWR7VNI.js → chunk-PRMMCFIN.js} +13 -9
- package/{chunk-JZPBVBIY.js → chunk-QTZD6HBK.js} +18 -8
- package/chunk-RCFIB45Q.js +28 -0
- package/{chunk-7S54ZGF6.js → chunk-RJKIB55Y.js} +256 -12
- package/chunk-RPRRIFXV.js +231 -0
- package/chunk-RQFO3CAF.js +300 -0
- package/chunk-RY57GLGW.js +188 -0
- package/{chunk-YHGHADBQ.js → chunk-SCUETSRP.js} +4 -4
- package/{chunk-TA6EA2A3.js → chunk-TFK3HUNQ.js} +182 -20
- package/{chunk-LAXMH7JY.js → chunk-UM4H3URE.js} +18 -15
- package/{chunk-6YABCOFU.js → chunk-UNHKVD2N.js} +1 -1
- package/{chunk-G5GVZAXF.js → chunk-UNOTBWZZ.js} +15 -19
- package/{chunk-GE6IWA5N.js → chunk-UNZDCI3O.js} +126 -1
- package/chunk-URMS4XY2.js +10 -0
- package/{chunk-SFWZUTCI.js → chunk-VBUQYOX7.js} +2 -2
- package/{chunk-FXFGNOT2.js → chunk-VFVCYIRQ.js} +1 -1
- package/chunk-VIDPVFP3.js +20 -0
- package/{chunk-5RDLGLAP.js → chunk-WJSW43QZ.js} +4 -7
- package/{chunk-CF7S2RYZ.js → chunk-WLH5BAKX.js} +6 -6
- package/{chunk-VVEH6XUE.js → chunk-X2DAEZ3X.js} +1 -1
- package/{chunk-2IJAKBK2.js → chunk-X664KVEV.js} +426 -246
- package/{chunk-3FCNXC2J.js → chunk-XRO5VGQ6.js} +3735 -1394
- package/chunk-Y5GCAMGP.js +9 -0
- package/{chunk-O3JR45Q2.js → chunk-YBMI5JAH.js} +44 -44
- package/{chunk-V42OE5HT.js → chunk-YDTYDA5W.js} +3 -3
- package/chunk-YYLHWIW6.js +48 -0
- package/{chunk-AOS5IN2I.js → chunk-ZHYK3N56.js} +1 -1
- package/{chunk-PJ2DDQQM.js → chunk-ZURFOV5J.js} +2 -2
- package/{chunk-3UGKATZQ.js → chunk-ZWS6DUHX.js} +26 -435
- package/chunk-ZZ2MH7LE.js +84 -0
- package/{cliExecutor-BBUVQU4L.js → cliExecutor-DW3PHGEL.js} +2 -2
- package/{clipboardy-BVE3LVEH.js → clipboardy-MMBYVRLF.js} +166 -372
- package/{codingSkills-V35TRR4Y.js → codingSkills-DY5MINPG.js} +5 -4
- package/{createAgentAutomationAction-WLRNE7C4.js → createAgentAutomationAction-H6ZRPXBR.js} +41 -31
- package/createDialogAction-UJ6LFJYM.js +61 -0
- package/{createPageAction-7YROVZ3W.js → createPageAction-KVPT3IRK.js} +57 -47
- package/{db-6DPSJ6WT.js → db-3H3JECGY.js} +45 -5
- package/{dbSlice-BBL3PEVW.js → dbSlice-UI2VCBLG.js} +13 -12
- package/{deleteFile-RJTEPOT4.js → deleteFile-XQXPBFFB.js} +7 -6
- package/dialogCommands-6ASXJP3H.js +82 -0
- package/{dialogSlice-N4HPG6HM.js → dialogSlice-B7KCGHMY.js} +37 -29
- package/{docCreateCommands-GRA5BHSA.js → docCreateCommands-S4E4MKVC.js} +42 -56
- package/{docDeleteCommands-ONIJM5MD.js → docDeleteCommands-M7XLKWRM.js} +44 -58
- package/{docReadCommands-H55VQ2US.js → docReadCommands-LSCMW2FR.js} +42 -56
- package/{docStore-B5AHDNW5.js → docStore-ZYWZWIRB.js} +40 -30
- package/{docUpdateCommands-ZQIQVGC2.js → docUpdateCommands-OMRBAFOB.js} +45 -59
- package/{fetchMessages-VPPQDDZ4.js → fetchMessages-7QRCCRYK.js} +3 -3
- package/{getPricing-QO5TYNCK.js → getPricing-L2ENXWHV.js} +7 -6
- package/{handleSendMessageAction-IN5XX5U7.js → handleSendMessageAction-Y2XKODRT.js} +39 -31
- package/index.js +74 -74
- package/{lib-FYYBQIXS.js → lib-2ZTNRXR4.js} +432 -183
- package/{localDialogRead-3DBXVYXS.js → localDialogRead-SLMPV5SK.js} +2 -2
- package/{localRuntimeAuthority-NDUA2D7Y.js → localRuntimeAuthority-S77B5QVB.js} +10 -4
- package/{localRuntimeDb-GKYRBWLQ.js → localRuntimeDb-N3H3HQ7G.js} +1 -1
- package/{machineCommands-H3UMKE76.js → machineCommands-62RUZVSW.js} +95 -85
- package/{machineInfo-M6KB6HZN.js → machineInfo-66ZFUMLE.js} +1 -1
- package/machineWsRunDispatch-L3HD3FSF.js +129 -0
- package/{models-WDUTTYFS.js → models-OX2S5C6Q.js} +2 -2
- package/{offlineMarxistsAgentCommand-E6LUG63A.js → offlineMarxistsAgentCommand-GEH6SNNA.js} +1 -1
- package/package.json +1 -4
- package/{read-64OQDO3I.js → read-2VHEHCAN.js} +8 -7
- package/{readDocTool-MXM3T6PE.js → readDocTool-CSWD6NPC.js} +1 -2
- package/{readlineWorkspace-XVJZDCSC.js → readlineWorkspace-3MOJWFM7.js} +3358 -3409
- package/{removeDialogAgentAction-NEKVAMSH.js → removeDialogAgentAction-2T7BPM3T.js} +19 -17
- package/{runAgentClientLoop-H7U53QB6.js → runAgentClientLoop-Y5UW544I.js} +73 -64
- package/{runtimeDoctorCommands-WI2YZUSJ.js → runtimeDoctorCommands-KHGQKT3B.js} +6 -6
- package/{setDialogExtraReferencesAction-WTOHQDIV.js → setDialogExtraReferencesAction-GPT37HTW.js} +15 -14
- package/{setPrimaryDialogAgentAction-LDJOYC4F.js → setPrimaryDialogAgentAction-3746P7IW.js} +19 -17
- package/{settingSlice-4M37TN7V.js → settingSlice-FIFOQ6OR.js} +16 -14
- package/{skillDiscovery-5U6RYAHN.js → skillDiscovery-KLLKXPUO.js} +4 -4
- package/{slateUtils-2M7MUTWX.js → slateUtils-VO2FU36K.js} +1 -1
- package/{spaceCommands-7VVKJBDV.js → spaceCommands-NKSBD5G3.js} +45 -241
- package/{spaceSlice-T6YK6D34.js → spaceSlice-L44CI2QR.js} +39 -61
- package/{streamAgentChatTurn-RQ63QFQ7.js → streamAgentChatTurn-7DU4Z2VJ.js} +1262 -150
- package/{switchDialogAgentAction-XVWGKR2Z.js → switchDialogAgentAction-MQTUTQHL.js} +14 -13
- package/syncMapping-RK2MANUX.js +43 -0
- package/{tableCommands-RJZ3V4TF.js → tableCommands-64FZBAI3.js} +9 -8
- package/{toolApiClient-ARGFDJAG.js → toolApiClient-B2MFXMEG.js} +3 -3
- package/tools-2JI52WJP.js +99 -0
- package/{tuiSummaryLlmCaller-SRZ4QQUP.js → tuiSummaryLlmCaller-EAWQ3IZX.js} +25 -21
- package/{updateCommands-AK2RDOFR.js → updateCommands-MNDUIKN7.js} +2 -2
- package/{updateDialogTitleAction-BBDSWHNG.js → updateDialogTitleAction-HW33OIJI.js} +49 -38
- package/agentRecordCommands-JFZRQJQ4.js +0 -103
- package/agentRunCommand-NOX5FLKO.js +0 -121
- package/chunk-AYMOSAHE.js +0 -3158
- package/chunk-D2BPX6DQ.js +0 -1707
- package/chunk-DCDL3GC4.js +0 -171
- package/chunk-F3MP7CP6.js +0 -73
- package/chunk-WZSRQNQG.js +0 -2587
- package/createDialogAction-Y6YV64QB.js +0 -52
- package/dialogCommands-XLO5CAHZ.js +0 -95
- package/machineWsRunDispatch-HONRUK3R.js +0 -119
- package/tools-KYZX3BGO.js +0 -90
- package/{chunk-Z5MXSEOJ.js → chunk-RQSOAMPJ.js} +2 -2
- package/{chunk-JDSUTNYY.js → chunk-SAQQLJZ5.js} +3 -3
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
applyChatCompletionDelta,
|
|
3
|
+
applyResponsesToolEvent,
|
|
4
|
+
createResponsesToolAccumulator,
|
|
5
|
+
finalizeResponsesToolCalls,
|
|
3
6
|
flushChatCompletionStream,
|
|
4
7
|
providerHttpFailure,
|
|
5
8
|
readSseFrames,
|
|
6
9
|
throwIfChatCompletionStreamFailed
|
|
7
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-RJKIB55Y.js";
|
|
8
11
|
import {
|
|
9
12
|
sanitizeForOutbound
|
|
10
13
|
} from "./chunk-T27IEBNQ.js";
|
|
11
14
|
import {
|
|
12
|
-
parseToolArgumentsJson,
|
|
13
15
|
resolveHistoricalToolContentCap
|
|
14
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-BFPSVRCQ.js";
|
|
15
17
|
import {
|
|
16
18
|
createThinkParserState,
|
|
17
19
|
createToolCallAccumulator,
|
|
@@ -22,24 +24,35 @@ import {
|
|
|
22
24
|
import {
|
|
23
25
|
requiresBareImageUrl,
|
|
24
26
|
toBareImageUrlMessages
|
|
25
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-ZWS6DUHX.js";
|
|
26
28
|
import {
|
|
29
|
+
convertMessagesToResponsesInput,
|
|
30
|
+
extractReasoningFromResponseOutput,
|
|
31
|
+
extractTextFromResponseOutput,
|
|
32
|
+
extractToolCallsFromResponseOutput,
|
|
33
|
+
getUsageRequestOptions,
|
|
27
34
|
shouldStripReasoningContentForOutbound,
|
|
28
|
-
toOpenAiCompatibleMessages
|
|
29
|
-
|
|
35
|
+
toOpenAiCompatibleMessages,
|
|
36
|
+
toResponsesTools
|
|
37
|
+
} from "./chunk-NNWHHCVP.js";
|
|
30
38
|
import {
|
|
31
39
|
buildProviderAuthHeaders,
|
|
32
40
|
buildProviderExecutionPlan
|
|
33
|
-
} from "./chunk-
|
|
34
|
-
import {
|
|
35
|
-
buildDialogFallbackTitleFromUserInput
|
|
36
|
-
} from "./chunk-2MBTWINN.js";
|
|
41
|
+
} from "./chunk-7YYHKBJP.js";
|
|
37
42
|
import {
|
|
38
43
|
NOLO_WORKSPACE_TOOL_NAMES,
|
|
39
44
|
buildNoloWorkspaceCommandArgs,
|
|
45
|
+
noloPositiveIntegerString,
|
|
40
46
|
noloStringArg,
|
|
41
|
-
parseNoloWorkspaceToolArguments
|
|
42
|
-
|
|
47
|
+
parseNoloWorkspaceToolArguments,
|
|
48
|
+
resolveNoloDialogInput
|
|
49
|
+
} from "./chunk-ADLYMQB6.js";
|
|
50
|
+
import {
|
|
51
|
+
buildDialogFallbackTitleFromUserInput
|
|
52
|
+
} from "./chunk-2MBTWINN.js";
|
|
53
|
+
import {
|
|
54
|
+
canonicalizeToolName
|
|
55
|
+
} from "./chunk-ZZ2MH7LE.js";
|
|
43
56
|
import {
|
|
44
57
|
toErrorMessage
|
|
45
58
|
} from "./chunk-HNMXYYJR.js";
|
|
@@ -47,17 +60,18 @@ import {
|
|
|
47
60
|
asRecordOrEmpty
|
|
48
61
|
} from "./chunk-KX6YBIHG.js";
|
|
49
62
|
import {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
normalizeServerOrigin
|
|
54
|
-
} from "./chunk-CF7PE763.js";
|
|
55
|
-
import {
|
|
56
|
-
dialogMessageKey
|
|
57
|
-
} from "./chunk-WHLVPK32.js";
|
|
63
|
+
dialogMessageKey,
|
|
64
|
+
dialogMessageRange
|
|
65
|
+
} from "./chunk-KMSZPTM6.js";
|
|
58
66
|
import {
|
|
59
67
|
asOptionalFiniteNumber
|
|
60
68
|
} from "./chunk-WWC7I6TA.js";
|
|
69
|
+
import {
|
|
70
|
+
asTrimmedNonEmptyStringArray
|
|
71
|
+
} from "./chunk-I42VC5KR.js";
|
|
72
|
+
import {
|
|
73
|
+
asTrimmedLowercaseString
|
|
74
|
+
} from "./chunk-QSJ3HFEF.js";
|
|
61
75
|
import {
|
|
62
76
|
asTrimmedString
|
|
63
77
|
} from "./chunk-3EUW3WEO.js";
|
|
@@ -68,11 +82,8 @@ import {
|
|
|
68
82
|
isRecord
|
|
69
83
|
} from "./chunk-KUG52IAP.js";
|
|
70
84
|
import {
|
|
71
|
-
|
|
72
|
-
} from "./chunk-
|
|
73
|
-
import {
|
|
74
|
-
asTrimmedNonEmptyStringArray
|
|
75
|
-
} from "./chunk-I42VC5KR.js";
|
|
85
|
+
normalizeServerOrigin
|
|
86
|
+
} from "./chunk-CF7PE763.js";
|
|
76
87
|
import {
|
|
77
88
|
asOptionalTrimmedString
|
|
78
89
|
} from "./chunk-G3SUCT4W.js";
|
|
@@ -142,8 +153,173 @@ function parseSseDataLineObject(line) {
|
|
|
142
153
|
return parsed && typeof parsed === "object" ? parsed : null;
|
|
143
154
|
}
|
|
144
155
|
|
|
156
|
+
// packages/integrations/openai/responsesRequestBody.ts
|
|
157
|
+
function buildResponsesRequestBody(body, model) {
|
|
158
|
+
const tools = Array.isArray(body.tools) ? toResponsesTools(body.tools) : void 0;
|
|
159
|
+
const reasoningEffort = body.reasoning && typeof body.reasoning === "object" ? void 0 : typeof body.reasoning_effort === "string" && body.reasoning_effort.trim() ? body.reasoning_effort : void 0;
|
|
160
|
+
return {
|
|
161
|
+
...body,
|
|
162
|
+
model,
|
|
163
|
+
input: Array.isArray(body.input) && body.input.length > 0 ? body.input : convertMessagesToResponsesInput(body.messages ?? []),
|
|
164
|
+
...Array.isArray(body.tools) ? { tools: tools ?? void 0 } : {},
|
|
165
|
+
...typeof body.max_tokens === "number" ? { max_output_tokens: body.max_tokens } : {},
|
|
166
|
+
...reasoningEffort ? { reasoning: { effort: reasoningEffort } } : {},
|
|
167
|
+
messages: void 0,
|
|
168
|
+
max_tokens: void 0,
|
|
169
|
+
reasoning_effort: void 0,
|
|
170
|
+
tool_choice: void 0
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// packages/agent-runtime/responsesSseStream.ts
|
|
175
|
+
function toResponsesStreamFailure(raw, fallbackPrefix = "Responses") {
|
|
176
|
+
if (!raw || typeof raw !== "object") return void 0;
|
|
177
|
+
const err = raw;
|
|
178
|
+
const reason = typeof err.reason === "string" ? err.reason : void 0;
|
|
179
|
+
const message = typeof err.message === "string" && err.message ? err.message : reason ? `${fallbackPrefix} upstream ended incomplete: ${reason}.` : "";
|
|
180
|
+
if (!message) return void 0;
|
|
181
|
+
const code = typeof err.code === "string" ? err.code : reason;
|
|
182
|
+
return {
|
|
183
|
+
...code ? { code } : {},
|
|
184
|
+
...typeof err.type === "string" ? { type: err.type } : {},
|
|
185
|
+
message
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
function responsesStreamFailureStatus(failure) {
|
|
189
|
+
const marker = `${failure.code ?? ""} ${failure.type ?? ""}`.toLowerCase();
|
|
190
|
+
if (marker.includes("overload") || marker.includes("service_unavailable")) return 503;
|
|
191
|
+
if (marker.includes("rate_limit") || marker.includes("usage_limit")) return 429;
|
|
192
|
+
return 502;
|
|
193
|
+
}
|
|
194
|
+
var codexStreamFailureStatus = responsesStreamFailureStatus;
|
|
195
|
+
function createResponsesStreamCollector(callbacks) {
|
|
196
|
+
let content = "";
|
|
197
|
+
let reasoning = "";
|
|
198
|
+
const toolCallsAcc = createResponsesToolAccumulator();
|
|
199
|
+
let usage;
|
|
200
|
+
let failure;
|
|
201
|
+
let completedResponse;
|
|
202
|
+
const processEvent = (ev) => {
|
|
203
|
+
const type = String(ev.type ?? "");
|
|
204
|
+
if (type === "error") {
|
|
205
|
+
failure = failure ?? toResponsesStreamFailure(ev.error, callbacks?.fallbackPrefix);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (type === "response.failed" || type === "response.incomplete") {
|
|
209
|
+
const response = ev.response;
|
|
210
|
+
failure = failure ?? toResponsesStreamFailure(response?.error, callbacks?.fallbackPrefix) ?? toResponsesStreamFailure(response?.incomplete_details, callbacks?.fallbackPrefix) ?? {
|
|
211
|
+
code: type === "response.incomplete" ? "response_incomplete" : "response_failed",
|
|
212
|
+
message: `${callbacks?.fallbackPrefix ?? "Responses"} upstream ended with ${type}.`
|
|
213
|
+
};
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
if (type === "response.output_text.delta" && typeof ev.delta === "string") {
|
|
217
|
+
content += ev.delta;
|
|
218
|
+
callbacks?.onTextDelta?.(ev.delta);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if ((type === "response.reasoning_text.delta" || type === "response.reasoning.delta") && typeof ev.delta === "string") {
|
|
222
|
+
reasoning += ev.delta;
|
|
223
|
+
callbacks?.onReasoningDelta?.(ev.delta);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
if (type === "response.output_item.added" || type === "response.output_item.done" || type === "response.function_call_arguments.delta" || type === "response.function_call_arguments.done") {
|
|
227
|
+
applyResponsesToolEvent(toolCallsAcc, ev);
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
if (type === "response.completed") {
|
|
231
|
+
const response = ev.response;
|
|
232
|
+
if (response) {
|
|
233
|
+
completedResponse = response;
|
|
234
|
+
const u = response.usage;
|
|
235
|
+
if (u) {
|
|
236
|
+
const prompt = typeof u.input_tokens === "number" ? u.input_tokens : typeof u.prompt_tokens === "number" ? u.prompt_tokens : 0;
|
|
237
|
+
const completion = typeof u.output_tokens === "number" ? u.output_tokens : typeof u.completion_tokens === "number" ? u.completion_tokens : 0;
|
|
238
|
+
const total = typeof u.total_tokens === "number" ? u.total_tokens : prompt + completion;
|
|
239
|
+
usage = {
|
|
240
|
+
prompt_tokens: prompt,
|
|
241
|
+
completion_tokens: completion,
|
|
242
|
+
total_tokens: total
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
const finalize = () => {
|
|
249
|
+
let finalToolCalls = finalizeResponsesToolCalls(toolCallsAcc);
|
|
250
|
+
if (finalToolCalls.length === 0 && completedResponse) {
|
|
251
|
+
finalToolCalls = extractToolCallsFromResponseOutput(completedResponse);
|
|
252
|
+
}
|
|
253
|
+
if (!content && completedResponse) {
|
|
254
|
+
content = extractTextFromResponseOutput(completedResponse);
|
|
255
|
+
}
|
|
256
|
+
if (!reasoning && completedResponse) {
|
|
257
|
+
reasoning = extractReasoningFromResponseOutput(completedResponse);
|
|
258
|
+
}
|
|
259
|
+
return {
|
|
260
|
+
content,
|
|
261
|
+
...reasoning ? { reasoning_content: reasoning } : {},
|
|
262
|
+
...finalToolCalls.length > 0 ? { tool_calls: finalToolCalls } : {},
|
|
263
|
+
...usage ? { usage } : {},
|
|
264
|
+
finish_reason: finalToolCalls.length > 0 ? "tool_calls" : "stop",
|
|
265
|
+
stream_complete: true,
|
|
266
|
+
...failure ? { failure } : {}
|
|
267
|
+
};
|
|
268
|
+
};
|
|
269
|
+
return {
|
|
270
|
+
processEvent,
|
|
271
|
+
finalize
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
function aggregateResponsesStream(events, options) {
|
|
275
|
+
const collector = createResponsesStreamCollector({ fallbackPrefix: options?.fallbackPrefix ?? "Codex" });
|
|
276
|
+
for (const ev of events) {
|
|
277
|
+
collector.processEvent(ev);
|
|
278
|
+
}
|
|
279
|
+
const finalized = collector.finalize();
|
|
280
|
+
return {
|
|
281
|
+
text: finalized.content,
|
|
282
|
+
toolCalls: finalized.tool_calls ?? [],
|
|
283
|
+
usage: finalized.usage,
|
|
284
|
+
failure: finalized.failure
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
async function readResponsesSseCompletion(args) {
|
|
288
|
+
const collector = createResponsesStreamCollector({
|
|
289
|
+
onTextDelta: args.onTextDelta,
|
|
290
|
+
onReasoningDelta: args.onReasoningDelta,
|
|
291
|
+
fallbackPrefix: args.fallbackPrefix ?? "Responses"
|
|
292
|
+
});
|
|
293
|
+
for await (const frame of readSseFrames(args.response)) {
|
|
294
|
+
for (const line of frame.split("\n")) {
|
|
295
|
+
const parsed = parseSseDataLineObject(line);
|
|
296
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
297
|
+
collector.processEvent(parsed);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const result = collector.finalize();
|
|
302
|
+
if (result.failure) {
|
|
303
|
+
const status = responsesStreamFailureStatus(result.failure);
|
|
304
|
+
const err = new Error(result.failure.message);
|
|
305
|
+
err.status = status;
|
|
306
|
+
err.code = result.failure.code;
|
|
307
|
+
err.type = result.failure.type;
|
|
308
|
+
throw err;
|
|
309
|
+
}
|
|
310
|
+
return result;
|
|
311
|
+
}
|
|
312
|
+
|
|
145
313
|
// packages/agent-runtime/openAiCompatibleProvider.ts
|
|
314
|
+
function isOpenAiResponsesEndpoint(endpoint) {
|
|
315
|
+
return endpoint.includes("/responses");
|
|
316
|
+
}
|
|
317
|
+
function resolveOpenAiCompatibleWire(config) {
|
|
318
|
+
if (config.wire) return config.wire;
|
|
319
|
+
return isOpenAiResponsesEndpoint(config.endpoint) ? "responses" : "chat.completions";
|
|
320
|
+
}
|
|
146
321
|
function buildOpenAiCompatibleChatCompletionRequest(args) {
|
|
322
|
+
const isResponses = resolveOpenAiCompatibleWire(args.providerConfig) === "responses";
|
|
147
323
|
const shouldStripReasoning = shouldStripReasoningContentForOutbound(
|
|
148
324
|
args.providerConfig.provider,
|
|
149
325
|
args.providerConfig.model
|
|
@@ -152,7 +328,7 @@ function buildOpenAiCompatibleChatCompletionRequest(args) {
|
|
|
152
328
|
const messages = toOpenAiCompatibleMessages(sanitizedMessages, {
|
|
153
329
|
stripReasoningContent: shouldStripReasoning
|
|
154
330
|
});
|
|
155
|
-
const
|
|
331
|
+
const rawBody = {
|
|
156
332
|
model: args.providerConfig.model,
|
|
157
333
|
messages: requiresBareImageUrl({
|
|
158
334
|
endpoint: args.providerConfig.endpoint,
|
|
@@ -162,8 +338,9 @@ function buildOpenAiCompatibleChatCompletionRequest(args) {
|
|
|
162
338
|
stream: args.stream ?? false,
|
|
163
339
|
...args.providerConfig.requestOptions,
|
|
164
340
|
...args.tools && args.tools.length > 0 ? { tools: args.tools } : {},
|
|
165
|
-
|
|
341
|
+
...!isResponses && args.stream ? getUsageRequestOptions(args.providerConfig.provider, { api: "chat-completions" }) : {}
|
|
166
342
|
};
|
|
343
|
+
const body = isResponses ? buildResponsesRequestBody(rawBody, args.providerConfig.model) : rawBody;
|
|
167
344
|
const endpoint = args.providerConfig.endpoint;
|
|
168
345
|
const headers = {
|
|
169
346
|
"Content-Type": "application/json",
|
|
@@ -183,6 +360,28 @@ function buildOpenAiCompatibleChatCompletionRequest(args) {
|
|
|
183
360
|
}
|
|
184
361
|
};
|
|
185
362
|
}
|
|
363
|
+
function parseOpenAiCompatibleResponsesResponse(args) {
|
|
364
|
+
const content = extractTextFromResponseOutput(args.data);
|
|
365
|
+
const reasoning = extractReasoningFromResponseOutput(args.data);
|
|
366
|
+
const tool_calls = extractToolCallsFromResponseOutput(args.data);
|
|
367
|
+
const u = args.data?.usage;
|
|
368
|
+
const usage = u && typeof u === "object" ? {
|
|
369
|
+
prompt_tokens: typeof u.input_tokens === "number" ? u.input_tokens : typeof u.prompt_tokens === "number" ? u.prompt_tokens : 0,
|
|
370
|
+
completion_tokens: typeof u.output_tokens === "number" ? u.output_tokens : typeof u.completion_tokens === "number" ? u.completion_tokens : 0,
|
|
371
|
+
total_tokens: typeof u.total_tokens === "number" ? u.total_tokens : (u.input_tokens ?? 0) + (u.output_tokens ?? 0)
|
|
372
|
+
} : void 0;
|
|
373
|
+
return {
|
|
374
|
+
content,
|
|
375
|
+
model: args.providerConfig.model,
|
|
376
|
+
provider: args.providerConfig.provider,
|
|
377
|
+
...tool_calls.length > 0 ? { tool_calls } : {},
|
|
378
|
+
...reasoning ? { reasoning_content: reasoning } : {},
|
|
379
|
+
finish_reason: tool_calls.length > 0 ? "tool_calls" : "stop",
|
|
380
|
+
stream_complete: true,
|
|
381
|
+
...usage ? { usage } : {},
|
|
382
|
+
trace: args.trace
|
|
383
|
+
};
|
|
384
|
+
}
|
|
186
385
|
function parseOpenAiCompatibleChatCompletionResponse(args) {
|
|
187
386
|
const choiceMessage = args.data?.choices?.[0]?.message ?? {};
|
|
188
387
|
const rawContent = String(choiceMessage?.content ?? "");
|
|
@@ -236,6 +435,7 @@ async function readOpenAiCompatibleSseCompletion(args) {
|
|
|
236
435
|
};
|
|
237
436
|
}
|
|
238
437
|
async function executeOpenAiCompatibleChatCompletion(args) {
|
|
438
|
+
const isResponses = resolveOpenAiCompatibleWire(args.providerConfig) === "responses";
|
|
239
439
|
const send = async (apiKey2) => {
|
|
240
440
|
const request = buildOpenAiCompatibleChatCompletionRequest({
|
|
241
441
|
providerConfig: apiKey2 === args.providerConfig.apiKey ? args.providerConfig : { ...args.providerConfig, apiKey: apiKey2 },
|
|
@@ -272,6 +472,24 @@ async function executeOpenAiCompatibleChatCompletion(args) {
|
|
|
272
472
|
const contentType = res.headers.get("content-type") ?? "";
|
|
273
473
|
const isEventStream = contentType.includes("text/event-stream");
|
|
274
474
|
if (isEventStream) {
|
|
475
|
+
if (isResponses) {
|
|
476
|
+
const streamed2 = await readResponsesSseCompletion({
|
|
477
|
+
response: res,
|
|
478
|
+
...args.onTextDelta ? { onTextDelta: args.onTextDelta } : {},
|
|
479
|
+
...args.onReasoningDelta ? { onReasoningDelta: args.onReasoningDelta } : {}
|
|
480
|
+
});
|
|
481
|
+
return {
|
|
482
|
+
content: streamed2.content,
|
|
483
|
+
model: args.providerConfig.model,
|
|
484
|
+
provider: args.providerConfig.provider,
|
|
485
|
+
...streamed2.tool_calls ? { tool_calls: streamed2.tool_calls } : {},
|
|
486
|
+
...streamed2.reasoning_content ? { reasoning_content: streamed2.reasoning_content } : {},
|
|
487
|
+
...streamed2.usage ? { usage: streamed2.usage } : {},
|
|
488
|
+
...streamed2.stream_complete ? { stream_complete: true } : {},
|
|
489
|
+
...streamed2.finish_reason ? { finish_reason: streamed2.finish_reason } : {},
|
|
490
|
+
trace: args.messages
|
|
491
|
+
};
|
|
492
|
+
}
|
|
275
493
|
const streamed = await readOpenAiCompatibleSseCompletion({
|
|
276
494
|
response: res,
|
|
277
495
|
...args.onTextDelta ? { onTextDelta: args.onTextDelta } : {},
|
|
@@ -296,6 +514,13 @@ async function executeOpenAiCompatibleChatCompletion(args) {
|
|
|
296
514
|
} catch {
|
|
297
515
|
data = {};
|
|
298
516
|
}
|
|
517
|
+
if (isResponses) {
|
|
518
|
+
return parseOpenAiCompatibleResponsesResponse({
|
|
519
|
+
providerConfig: args.providerConfig,
|
|
520
|
+
data,
|
|
521
|
+
trace: args.messages
|
|
522
|
+
});
|
|
523
|
+
}
|
|
299
524
|
return parseOpenAiCompatibleChatCompletionResponse({
|
|
300
525
|
providerConfig: args.providerConfig,
|
|
301
526
|
data,
|
|
@@ -1020,6 +1245,80 @@ function evaluateShellCommandPolicy(args) {
|
|
|
1020
1245
|
};
|
|
1021
1246
|
}
|
|
1022
1247
|
|
|
1248
|
+
// packages/agent-runtime/capabilities/capabilityPolicy.ts
|
|
1249
|
+
function toDisplayCommand(command) {
|
|
1250
|
+
if (typeof command === "string") {
|
|
1251
|
+
const trimmed = command.trim();
|
|
1252
|
+
return trimmed ? trimmed : void 0;
|
|
1253
|
+
}
|
|
1254
|
+
if (Array.isArray(command)) {
|
|
1255
|
+
const joined = command.map((item) => typeof item === "string" ? item : String(item)).filter((item) => item.length > 0).join(" ").trim();
|
|
1256
|
+
return joined ? joined : void 0;
|
|
1257
|
+
}
|
|
1258
|
+
return void 0;
|
|
1259
|
+
}
|
|
1260
|
+
function buildDestructiveShellPermissionRequest(command) {
|
|
1261
|
+
return {
|
|
1262
|
+
id: "permission-shell-destructive-action",
|
|
1263
|
+
tool: "execShell",
|
|
1264
|
+
action: "destructive_shell_command",
|
|
1265
|
+
title: "\u786E\u8BA4\u6267\u884C\u7834\u574F\u6027 shell \u547D\u4EE4",
|
|
1266
|
+
body: "\u8BE5\u547D\u4EE4\u53EF\u80FD\u5220\u9664\u6216\u91CD\u7F6E\u7528\u6237\u5185\u5BB9\uFF0C\u9700\u8981\u7528\u6237\u660E\u786E\u786E\u8BA4\u540E\u624D\u80FD\u6267\u884C\u3002",
|
|
1267
|
+
...command ? { command } : {},
|
|
1268
|
+
suggestedRule: {
|
|
1269
|
+
scope: "once",
|
|
1270
|
+
pattern: { capability: "destructive_action", target: "shell_command" }
|
|
1271
|
+
}
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
function buildDestructiveShellBlockedError(reason, request) {
|
|
1275
|
+
const error = new Error(reason);
|
|
1276
|
+
error.code = "destructive_action_requires_confirmation";
|
|
1277
|
+
error.policy = {
|
|
1278
|
+
capability: "destructive_action",
|
|
1279
|
+
target: "shell_command",
|
|
1280
|
+
detail: "execShell destructive command"
|
|
1281
|
+
};
|
|
1282
|
+
error.permissionRequest = request;
|
|
1283
|
+
return error;
|
|
1284
|
+
}
|
|
1285
|
+
async function evaluateExecShellDestructiveGuard(input, ctx) {
|
|
1286
|
+
const guardEnabled = ctx.enableDestructiveShellGuard !== false;
|
|
1287
|
+
if (!guardEnabled || ctx.confirmed) {
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
const rawCommand = resolveShellCommandArg(
|
|
1291
|
+
typeof input === "object" && input !== null ? input : { command: input }
|
|
1292
|
+
);
|
|
1293
|
+
const extraInput = typeof input === "object" && input !== null ? input.input : void 0;
|
|
1294
|
+
if (!isDestructiveShellCommand({ command: rawCommand, input: extraInput })) {
|
|
1295
|
+
return;
|
|
1296
|
+
}
|
|
1297
|
+
const displayCommand = toDisplayCommand(rawCommand);
|
|
1298
|
+
const request = buildDestructiveShellPermissionRequest(displayCommand);
|
|
1299
|
+
if (!ctx.confirmDestructiveAction) {
|
|
1300
|
+
if (ctx.blockDestructiveWithoutConfirmation) {
|
|
1301
|
+
throw buildDestructiveShellBlockedError(
|
|
1302
|
+
"destructive shell command blocked: no confirmation channel available",
|
|
1303
|
+
request
|
|
1304
|
+
);
|
|
1305
|
+
}
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
const confirmed = await ctx.confirmDestructiveAction(request);
|
|
1309
|
+
if (!confirmed) {
|
|
1310
|
+
throw buildDestructiveShellBlockedError(
|
|
1311
|
+
"destructive shell command blocked: user declined confirmation",
|
|
1312
|
+
request
|
|
1313
|
+
);
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
async function evaluateCapabilityPolicy(capability, normalizedInput, ctx) {
|
|
1317
|
+
if (capability.name === "execShell") {
|
|
1318
|
+
await evaluateExecShellDestructiveGuard(normalizedInput, ctx);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1023
1322
|
// packages/agent-runtime/localToolPolicy.ts
|
|
1024
1323
|
var NEVER_LOCAL_TOOLS = /* @__PURE__ */ new Set([
|
|
1025
1324
|
"deleteSpaces",
|
|
@@ -1064,20 +1363,6 @@ function normalizeLocalToolName(toolName) {
|
|
|
1064
1363
|
String(toolName ?? "").replace(/^functions\./, "").trim()
|
|
1065
1364
|
);
|
|
1066
1365
|
}
|
|
1067
|
-
function parseShellCommandPayload(rawArguments) {
|
|
1068
|
-
return parseToolArgumentsJson(rawArguments);
|
|
1069
|
-
}
|
|
1070
|
-
function toDisplayCommand(command) {
|
|
1071
|
-
if (typeof command === "string") {
|
|
1072
|
-
const trimmed = command.trim();
|
|
1073
|
-
return trimmed ? trimmed : void 0;
|
|
1074
|
-
}
|
|
1075
|
-
if (Array.isArray(command)) {
|
|
1076
|
-
const joined = command.map((item) => typeof item === "string" ? item : String(item)).filter((item) => item.length > 0).join(" ").trim();
|
|
1077
|
-
return joined ? joined : void 0;
|
|
1078
|
-
}
|
|
1079
|
-
return void 0;
|
|
1080
|
-
}
|
|
1081
1366
|
function isRestrictedLocalToolMode(env) {
|
|
1082
1367
|
return env.NOLO_LOCAL_TOOL_MODE === "restricted";
|
|
1083
1368
|
}
|
|
@@ -1127,7 +1412,6 @@ function resolveLocalToolPolicy(args) {
|
|
|
1127
1412
|
reason: `${toolName} is not enabled for local runtime runs. The agent must declare the tool in its manifest; in restricted mode (NOLO_LOCAL_TOOL_MODE=restricted) non-default tools also require adding to NOLO_LOCAL_ALLOWED_TOOLS (e.g. NOLO_LOCAL_ALLOWED_TOOLS=someCustomTool) and restarting the session so the env var loads.`
|
|
1128
1413
|
};
|
|
1129
1414
|
}
|
|
1130
|
-
var ABORT_AWARE_LOCAL_TOOLS = /* @__PURE__ */ new Set(["execShell", "controlAgentRun"]);
|
|
1131
1415
|
async function executeLocalToolWithPolicy(args) {
|
|
1132
1416
|
const decision = resolveLocalToolPolicy({
|
|
1133
1417
|
env: args.env,
|
|
@@ -1139,70 +1423,41 @@ async function executeLocalToolWithPolicy(args) {
|
|
|
1139
1423
|
if (!executor) {
|
|
1140
1424
|
throw new Error(`${decision.toolName} is allowed by policy but no local executor is registered.`);
|
|
1141
1425
|
}
|
|
1142
|
-
const
|
|
1143
|
-
abortSignal: args.abortSignal,
|
|
1144
|
-
detachMs: args.detachMs
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
body: "\u8BE5\u547D\u4EE4\u53EF\u80FD\u5220\u9664\u6216\u91CD\u7F6E\u7528\u6237\u5185\u5BB9\uFF0C\u9700\u8981\u7528\u6237\u660E\u786E\u786E\u8BA4\u540E\u624D\u80FD\u6267\u884C\u3002",
|
|
1158
|
-
...command ? { command } : {},
|
|
1159
|
-
suggestedRule: {
|
|
1160
|
-
scope: "once",
|
|
1161
|
-
pattern: { capability: "destructive_action", target: "shell_command" }
|
|
1162
|
-
}
|
|
1163
|
-
};
|
|
1164
|
-
const buildBlockedError = (reason) => {
|
|
1165
|
-
const error = new Error(reason);
|
|
1166
|
-
error.code = "destructive_action_requires_confirmation";
|
|
1167
|
-
error.policy = {
|
|
1168
|
-
capability: "destructive_action",
|
|
1169
|
-
target: "shell_command",
|
|
1170
|
-
detail: "execShell destructive command"
|
|
1171
|
-
};
|
|
1172
|
-
error.permissionRequest = request;
|
|
1173
|
-
return error;
|
|
1174
|
-
};
|
|
1175
|
-
if (!args.confirmDestructiveAction) {
|
|
1176
|
-
if (args.blockDestructiveWithoutConfirmation) {
|
|
1177
|
-
throw buildBlockedError(
|
|
1178
|
-
"destructive shell command blocked: no confirmation channel available"
|
|
1179
|
-
);
|
|
1180
|
-
}
|
|
1181
|
-
return effectiveExecutor({
|
|
1182
|
-
...args.call,
|
|
1183
|
-
name: decision.toolName
|
|
1184
|
-
});
|
|
1185
|
-
}
|
|
1186
|
-
const confirmed = await args.confirmDestructiveAction(request);
|
|
1187
|
-
if (!confirmed) {
|
|
1188
|
-
throw buildBlockedError(
|
|
1189
|
-
"destructive shell command blocked: user declined confirmation"
|
|
1190
|
-
);
|
|
1426
|
+
const opts = {
|
|
1427
|
+
...args.abortSignal ? { abortSignal: args.abortSignal } : {},
|
|
1428
|
+
...args.detachMs !== void 0 ? { detachMs: args.detachMs } : {},
|
|
1429
|
+
...args.confirmed !== void 0 ? { confirmed: args.confirmed } : {},
|
|
1430
|
+
...args.enableDestructiveShellGuard !== void 0 ? { enableDestructiveShellGuard: args.enableDestructiveShellGuard } : {},
|
|
1431
|
+
...args.confirmDestructiveAction ? { confirmDestructiveAction: args.confirmDestructiveAction } : {},
|
|
1432
|
+
...args.blockDestructiveWithoutConfirmation !== void 0 ? { blockDestructiveWithoutConfirmation: args.blockDestructiveWithoutConfirmation } : {}
|
|
1433
|
+
};
|
|
1434
|
+
if (decision.toolName === "execShell") {
|
|
1435
|
+
let parsedInput = args.call.arguments;
|
|
1436
|
+
if (typeof parsedInput === "string") {
|
|
1437
|
+
try {
|
|
1438
|
+
parsedInput = JSON.parse(parsedInput);
|
|
1439
|
+
} catch {
|
|
1440
|
+
parsedInput = { command: parsedInput };
|
|
1191
1441
|
}
|
|
1192
1442
|
}
|
|
1443
|
+
await evaluateExecShellDestructiveGuard(parsedInput, opts);
|
|
1444
|
+
opts.confirmed = true;
|
|
1193
1445
|
}
|
|
1194
|
-
return
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1446
|
+
return executor(
|
|
1447
|
+
{
|
|
1448
|
+
...args.call,
|
|
1449
|
+
name: decision.toolName
|
|
1450
|
+
},
|
|
1451
|
+
opts
|
|
1452
|
+
);
|
|
1198
1453
|
}
|
|
1199
1454
|
|
|
1200
1455
|
// packages/agent-runtime/localWorkspaceTools.ts
|
|
1201
1456
|
import { existsSync as existsSync2 } from "node:fs";
|
|
1202
1457
|
import { createHash } from "node:crypto";
|
|
1203
1458
|
import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
1204
|
-
import { dirname, relative, resolve, sep } from "node:path";
|
|
1205
|
-
import { spawnSync, spawn as
|
|
1459
|
+
import { dirname, relative, resolve as resolve2, sep } from "node:path";
|
|
1460
|
+
import { spawnSync, spawn as spawnChildProcess2 } from "node:child_process";
|
|
1206
1461
|
|
|
1207
1462
|
// packages/core/readRangeLedger.ts
|
|
1208
1463
|
function filterRetainedLedgerRecords(records, retainedCap) {
|
|
@@ -1389,9 +1644,9 @@ function spawnToWebStreams(options) {
|
|
|
1389
1644
|
env: options.env,
|
|
1390
1645
|
stdio: ["ignore", "pipe", "pipe"]
|
|
1391
1646
|
});
|
|
1392
|
-
const exited = new Promise((
|
|
1393
|
-
child.on("exit", (code) =>
|
|
1394
|
-
child.on("error", () =>
|
|
1647
|
+
const exited = new Promise((resolve3) => {
|
|
1648
|
+
child.on("exit", (code) => resolve3(code ?? 1));
|
|
1649
|
+
child.on("error", () => resolve3(127));
|
|
1395
1650
|
});
|
|
1396
1651
|
const toWeb = (stream) => {
|
|
1397
1652
|
if (!stream) return null;
|
|
@@ -1474,65 +1729,674 @@ function getProcessRegistry() {
|
|
|
1474
1729
|
return registry;
|
|
1475
1730
|
}
|
|
1476
1731
|
|
|
1477
|
-
// packages/agent-runtime/
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
"captureVisualState",
|
|
1486
|
-
"execShell",
|
|
1487
|
-
"launchProcess",
|
|
1488
|
-
"listProcesses"
|
|
1489
|
-
];
|
|
1490
|
-
var SHELL_TOOL_NAMES = ["execShell", "launchProcess", "listProcesses"];
|
|
1491
|
-
var WORKSPACE_TOOL_NAME_SET = new Set(WORKSPACE_TOOL_NAMES);
|
|
1492
|
-
var REMOVED_WORKSPACE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
1493
|
-
"gitStatus",
|
|
1494
|
-
"gitDiff",
|
|
1495
|
-
"gitCreateBranch",
|
|
1496
|
-
"gitAdd",
|
|
1497
|
-
"gitCommit",
|
|
1498
|
-
"commitWorkspace"
|
|
1499
|
-
]);
|
|
1500
|
-
function tokenizeShellPrefix(command) {
|
|
1501
|
-
const tokens = [];
|
|
1502
|
-
const pattern = /"([^"]*)"|'([^']*)'|([^\s"'|;&<>]+)/g;
|
|
1503
|
-
let match;
|
|
1504
|
-
while (match = pattern.exec(command)) {
|
|
1505
|
-
tokens.push(match[1] ?? match[2] ?? match[3] ?? "");
|
|
1506
|
-
}
|
|
1507
|
-
return tokens;
|
|
1732
|
+
// packages/agent-runtime/workspaceShell.ts
|
|
1733
|
+
import { resolve } from "node:path";
|
|
1734
|
+
import { spawn as spawnChildProcess } from "node:child_process";
|
|
1735
|
+
var EXEC_SHELL_TIMEOUT_ENV = "NOLO_EXEC_SHELL_TIMEOUT_MS";
|
|
1736
|
+
var EXEC_SHELL_DETACH_ENV = "NOLO_EXEC_SHELL_DETACH_MS";
|
|
1737
|
+
var DEFAULT_EXEC_SHELL_DETACH_MS = 12e4;
|
|
1738
|
+
function readTrimmedString(value) {
|
|
1739
|
+
return asOptionalTrimmedString(value);
|
|
1508
1740
|
}
|
|
1509
|
-
function
|
|
1741
|
+
function extractActivityRefs(rawRefs) {
|
|
1742
|
+
if (!Array.isArray(rawRefs)) return void 0;
|
|
1743
|
+
const refs = rawRefs.flatMap((entry) => {
|
|
1744
|
+
if (typeof entry === "string" && entry.trim()) {
|
|
1745
|
+
return [{ type: "file", path: entry.trim() }];
|
|
1746
|
+
}
|
|
1747
|
+
if (!isRecord(entry)) return [];
|
|
1748
|
+
const path = readTrimmedString(entry.path);
|
|
1749
|
+
if (path) return [{ type: "file", path }];
|
|
1750
|
+
const terminalId = readTrimmedString(entry.id);
|
|
1751
|
+
const terminalLabel = readTrimmedString(entry.label);
|
|
1752
|
+
if (entry.type === "terminal" || terminalId || terminalLabel) {
|
|
1753
|
+
return [{
|
|
1754
|
+
type: "terminal",
|
|
1755
|
+
...terminalId ? { id: terminalId } : {},
|
|
1756
|
+
...terminalLabel ? { label: terminalLabel } : {}
|
|
1757
|
+
}];
|
|
1758
|
+
}
|
|
1759
|
+
if (entry.type === "url" || typeof entry.url === "string") {
|
|
1760
|
+
const url = readTrimmedString(entry.url);
|
|
1761
|
+
const label = readTrimmedString(entry.label);
|
|
1762
|
+
return url ? [{ type: "url", url, ...label ? { label } : {} }] : [];
|
|
1763
|
+
}
|
|
1764
|
+
return [];
|
|
1765
|
+
});
|
|
1766
|
+
return refs.length ? refs : void 0;
|
|
1767
|
+
}
|
|
1768
|
+
function extractActivityAction(rawAction) {
|
|
1769
|
+
if (!isRecord(rawAction)) return void 0;
|
|
1770
|
+
const title = readTrimmedString(rawAction.title);
|
|
1771
|
+
if (!title) return void 0;
|
|
1772
|
+
const kind = readTrimmedString(rawAction.kind);
|
|
1773
|
+
const detail = readTrimmedString(rawAction.detail);
|
|
1774
|
+
const refs = extractActivityRefs(rawAction.refs);
|
|
1510
1775
|
return {
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1776
|
+
title,
|
|
1777
|
+
...kind ? { kind } : {},
|
|
1778
|
+
...detail ? { detail } : {},
|
|
1779
|
+
...refs ? { refs } : {}
|
|
1514
1780
|
};
|
|
1515
1781
|
}
|
|
1516
|
-
function
|
|
1517
|
-
if (
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
}
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
const maxDepth = {
|
|
1531
|
-
type: "integer",
|
|
1532
|
-
description: "Maximum directory depth to include. Defaults to 1 for the requested directory only."
|
|
1782
|
+
function extractActivityPhase(rawPhase) {
|
|
1783
|
+
if (!isRecord(rawPhase)) return void 0;
|
|
1784
|
+
const title = readTrimmedString(rawPhase.title);
|
|
1785
|
+
if (!title) return void 0;
|
|
1786
|
+
const id = readTrimmedString(rawPhase.id) || title.toLowerCase().replace(/\s+/g, "-");
|
|
1787
|
+
const index = asOptionalFiniteNumber(rawPhase.index);
|
|
1788
|
+
const total = asOptionalFiniteNumber(rawPhase.total);
|
|
1789
|
+
const status = rawPhase.status === "pending" || rawPhase.status === "running" || rawPhase.status === "success" || rawPhase.status === "failed" ? rawPhase.status : void 0;
|
|
1790
|
+
return {
|
|
1791
|
+
id,
|
|
1792
|
+
title,
|
|
1793
|
+
...index !== void 0 ? { index } : {},
|
|
1794
|
+
...total !== void 0 ? { total } : {},
|
|
1795
|
+
...status ? { status } : {}
|
|
1533
1796
|
};
|
|
1534
|
-
|
|
1535
|
-
|
|
1797
|
+
}
|
|
1798
|
+
function extractActivityPlan(rawPlan) {
|
|
1799
|
+
if (!isRecord(rawPlan)) return void 0;
|
|
1800
|
+
if (!Array.isArray(rawPlan.phases)) return void 0;
|
|
1801
|
+
const phases = rawPlan.phases.flatMap((entry, index) => {
|
|
1802
|
+
const phase = extractActivityPhase(entry);
|
|
1803
|
+
if (!phase) return [];
|
|
1804
|
+
return [{
|
|
1805
|
+
id: phase.id,
|
|
1806
|
+
title: phase.title,
|
|
1807
|
+
index: phase.index ?? index + 1,
|
|
1808
|
+
...phase.status ? { status: phase.status } : {}
|
|
1809
|
+
}];
|
|
1810
|
+
});
|
|
1811
|
+
if (phases.length === 0) return void 0;
|
|
1812
|
+
const title = readTrimmedString(rawPlan.title);
|
|
1813
|
+
return {
|
|
1814
|
+
...title ? { title } : {},
|
|
1815
|
+
phases
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1818
|
+
function extractActivity(parsed) {
|
|
1819
|
+
const raw = parsed._activity;
|
|
1820
|
+
if (!isRecord(raw)) return void 0;
|
|
1821
|
+
const nestedAction = extractActivityAction(raw.action);
|
|
1822
|
+
const legacyAction = extractActivityAction(raw);
|
|
1823
|
+
const action = nestedAction || legacyAction;
|
|
1824
|
+
const phase = extractActivityPhase(raw.phase);
|
|
1825
|
+
const plan = extractActivityPlan(raw.plan);
|
|
1826
|
+
if (!action && !plan) return void 0;
|
|
1827
|
+
return {
|
|
1828
|
+
...action ? action : {},
|
|
1829
|
+
...phase ? { phase } : {},
|
|
1830
|
+
...nestedAction ? { action: nestedAction } : {},
|
|
1831
|
+
...plan ? { plan } : {}
|
|
1832
|
+
};
|
|
1833
|
+
}
|
|
1834
|
+
function tokenizeShellPrefix(command) {
|
|
1835
|
+
const tokens = [];
|
|
1836
|
+
const pattern = /"([^"]*)"|'([^']*)'|([^\s"'|;&<>]+)/g;
|
|
1837
|
+
let match;
|
|
1838
|
+
while (match = pattern.exec(command)) {
|
|
1839
|
+
tokens.push(match[1] ?? match[2] ?? match[3] ?? "");
|
|
1840
|
+
}
|
|
1841
|
+
return tokens;
|
|
1842
|
+
}
|
|
1843
|
+
function wrapPowerShellCommand(command) {
|
|
1844
|
+
return [
|
|
1845
|
+
"[Console]::InputEncoding=[System.Text.Encoding]::UTF8",
|
|
1846
|
+
"[Console]::OutputEncoding=[System.Text.Encoding]::UTF8",
|
|
1847
|
+
"$OutputEncoding=[System.Text.Encoding]::UTF8",
|
|
1848
|
+
"$PSStyle.OutputRendering='PlainText'",
|
|
1849
|
+
command
|
|
1850
|
+
].join("; ");
|
|
1851
|
+
}
|
|
1852
|
+
function findPowerShellExecutable() {
|
|
1853
|
+
return resolveExecutableOnPath("pwsh") || resolveExecutableOnPath("powershell.exe") || resolveExecutableOnPath("powershell");
|
|
1854
|
+
}
|
|
1855
|
+
function buildPowerShellCommand(command) {
|
|
1856
|
+
const executable = findPowerShellExecutable();
|
|
1857
|
+
if (!executable) throw new Error("PowerShell is not available on this machine.");
|
|
1858
|
+
return [
|
|
1859
|
+
executable,
|
|
1860
|
+
"-NoLogo",
|
|
1861
|
+
"-NoProfile",
|
|
1862
|
+
"-NonInteractive",
|
|
1863
|
+
"-ExecutionPolicy",
|
|
1864
|
+
"Bypass",
|
|
1865
|
+
"-Command",
|
|
1866
|
+
wrapPowerShellCommand(command)
|
|
1867
|
+
];
|
|
1868
|
+
}
|
|
1869
|
+
function buildBashCommand(command) {
|
|
1870
|
+
const executable = resolveExecutableOnPath("bash") || resolveExecutableOnPath("sh");
|
|
1871
|
+
if (!executable) throw new Error("bash/sh is not available on this machine.");
|
|
1872
|
+
return [executable, "-lc", command];
|
|
1873
|
+
}
|
|
1874
|
+
function buildWorkspaceShellCommand(args) {
|
|
1875
|
+
if (args.shell === "powershell") return buildPowerShellCommand(args.command);
|
|
1876
|
+
if (args.shell === "bash") return buildBashCommand(args.command);
|
|
1877
|
+
return process.platform === "win32" ? buildPowerShellCommand(args.command) : buildBashCommand(args.command);
|
|
1878
|
+
}
|
|
1879
|
+
function findWorkspaceShellEscapeToken(command) {
|
|
1880
|
+
const tokens = tokenizeShellPrefix(command);
|
|
1881
|
+
for (const token of tokens) {
|
|
1882
|
+
if (token === ".." || token.startsWith("../") || token.startsWith("..\\") || token.includes("/../") || token.includes("\\..\\")) {
|
|
1883
|
+
return token;
|
|
1884
|
+
}
|
|
1885
|
+
if (token === "~" || token.startsWith("~/") || token.startsWith("~\\")) {
|
|
1886
|
+
return token;
|
|
1887
|
+
}
|
|
1888
|
+
if (token === "/" || token.startsWith("/") || /^[A-Za-z]:[\\/]/.test(token) || token.startsWith("\\\\")) {
|
|
1889
|
+
return token;
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
return null;
|
|
1893
|
+
}
|
|
1894
|
+
function buildWorkspaceShellEscapeBlockedResult(args) {
|
|
1895
|
+
return {
|
|
1896
|
+
content: [
|
|
1897
|
+
"workspace_shell_escape_blocked",
|
|
1898
|
+
`blockedToken: ${args.token}`,
|
|
1899
|
+
`command: ${args.command}`,
|
|
1900
|
+
"Use paths relative to the authorized folder only.",
|
|
1901
|
+
"exitCode: 126"
|
|
1902
|
+
].join("\n"),
|
|
1903
|
+
metadata: {
|
|
1904
|
+
exitCode: 126,
|
|
1905
|
+
workspaceShellEscapeBlocked: true,
|
|
1906
|
+
blockedToken: args.token
|
|
1907
|
+
}
|
|
1908
|
+
};
|
|
1909
|
+
}
|
|
1910
|
+
function extractInteractiveGhAuthCommand(command) {
|
|
1911
|
+
const tokens = tokenizeShellPrefix(command);
|
|
1912
|
+
if (tokens[0] !== "gh" || tokens[1] !== "auth") return null;
|
|
1913
|
+
const subcommand = tokens[2];
|
|
1914
|
+
if (subcommand !== "login" && subcommand !== "refresh") return null;
|
|
1915
|
+
if (tokens.includes("--help")) return null;
|
|
1916
|
+
const result = ["gh", "auth", subcommand];
|
|
1917
|
+
for (let index = 3; index < tokens.length; index += 1) {
|
|
1918
|
+
const token = tokens[index];
|
|
1919
|
+
if (!token) continue;
|
|
1920
|
+
if (token === "-h" || token === "--hostname" || token === "-s" || token === "--scopes" || token === "--remove-scopes" || token === "-r") {
|
|
1921
|
+
const value = tokens[index + 1];
|
|
1922
|
+
if (value) {
|
|
1923
|
+
result.push(token, value);
|
|
1924
|
+
index += 1;
|
|
1925
|
+
}
|
|
1926
|
+
continue;
|
|
1927
|
+
}
|
|
1928
|
+
if (token === "--clipboard" || token === "-c" || token === "--insecure-storage" || token === "--reset-scopes") {
|
|
1929
|
+
result.push(token);
|
|
1930
|
+
continue;
|
|
1931
|
+
}
|
|
1932
|
+
if (token.startsWith("--hostname=") || token.startsWith("--scopes=") || token.startsWith("--remove-scopes=")) {
|
|
1933
|
+
result.push(token);
|
|
1934
|
+
continue;
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
return result.join(" ");
|
|
1938
|
+
}
|
|
1939
|
+
function buildInteractiveCommandBlockedResult(command) {
|
|
1940
|
+
const argv = tokenizeShellPrefix(command);
|
|
1941
|
+
return {
|
|
1942
|
+
content: [
|
|
1943
|
+
"action_gate: handoff",
|
|
1944
|
+
`command: ${command}`,
|
|
1945
|
+
"Run this in the current TUI terminal, then resume the agent turn.",
|
|
1946
|
+
"exitCode: 130"
|
|
1947
|
+
].join("\n"),
|
|
1948
|
+
metadata: {
|
|
1949
|
+
exitCode: 130,
|
|
1950
|
+
timedOut: false,
|
|
1951
|
+
actionGate: {
|
|
1952
|
+
id: `gate-${Date.now().toString(36)}-terminal-handoff`,
|
|
1953
|
+
kind: "handoff",
|
|
1954
|
+
title: "This command requires an interactive terminal.",
|
|
1955
|
+
body: "Complete it in the terminal, then nolo will continue.",
|
|
1956
|
+
payload: {
|
|
1957
|
+
command: argv,
|
|
1958
|
+
displayCommand: command
|
|
1959
|
+
}
|
|
1960
|
+
},
|
|
1961
|
+
reason: "interactive-command-requires-terminal"
|
|
1962
|
+
}
|
|
1963
|
+
};
|
|
1964
|
+
}
|
|
1965
|
+
function resolveExecShellTimeoutMs(override) {
|
|
1966
|
+
const fromOverride = asOptionalPositiveFiniteNumber(override);
|
|
1967
|
+
if (fromOverride !== void 0) return fromOverride;
|
|
1968
|
+
const raw = process.env[EXEC_SHELL_TIMEOUT_ENV];
|
|
1969
|
+
if (raw === void 0) return void 0;
|
|
1970
|
+
return asOptionalPositiveFiniteNumber(Number(raw));
|
|
1971
|
+
}
|
|
1972
|
+
function resolveDetachMs(override) {
|
|
1973
|
+
if (typeof override === "number" && Number.isFinite(override) && override >= 0) {
|
|
1974
|
+
return override;
|
|
1975
|
+
}
|
|
1976
|
+
const raw = process.env[EXEC_SHELL_DETACH_ENV];
|
|
1977
|
+
if (raw === void 0) return DEFAULT_EXEC_SHELL_DETACH_MS;
|
|
1978
|
+
const parsed = asOptionalPositiveFiniteNumber(Number(raw));
|
|
1979
|
+
return parsed === void 0 ? DEFAULT_EXEC_SHELL_DETACH_MS : parsed;
|
|
1980
|
+
}
|
|
1981
|
+
function truncateToolOutput(value, limit = 2e4) {
|
|
1982
|
+
if (value.length <= limit) return value;
|
|
1983
|
+
const approxMarkerLen = 40;
|
|
1984
|
+
if (limit <= approxMarkerLen) return value.slice(0, limit);
|
|
1985
|
+
const remaining = limit - approxMarkerLen;
|
|
1986
|
+
const headSize = Math.floor(remaining * 0.3);
|
|
1987
|
+
const tailSize = remaining - headSize;
|
|
1988
|
+
const head = value.slice(0, headSize);
|
|
1989
|
+
const tail = value.slice(-tailSize);
|
|
1990
|
+
const actualRemoved = value.length - headSize - tailSize;
|
|
1991
|
+
return `${head}
|
|
1992
|
+
|
|
1993
|
+
[... truncated ${actualRemoved} chars ...]
|
|
1994
|
+
|
|
1995
|
+
${tail}`;
|
|
1996
|
+
}
|
|
1997
|
+
function readNodeStream(stream) {
|
|
1998
|
+
if (!stream) return Promise.resolve("");
|
|
1999
|
+
return new Promise((resolveStream, rejectStream) => {
|
|
2000
|
+
const chunks = [];
|
|
2001
|
+
stream.on("data", (chunk) => {
|
|
2002
|
+
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
2003
|
+
});
|
|
2004
|
+
stream.on("error", rejectStream);
|
|
2005
|
+
stream.on("end", () => {
|
|
2006
|
+
resolveStream(Buffer.concat(chunks).toString("utf8"));
|
|
2007
|
+
});
|
|
2008
|
+
});
|
|
2009
|
+
}
|
|
2010
|
+
function waitForNodeProcessExit(proc) {
|
|
2011
|
+
return new Promise((resolveExit, rejectExit) => {
|
|
2012
|
+
proc.on("error", rejectExit);
|
|
2013
|
+
proc.on("close", (code) => {
|
|
2014
|
+
resolveExit(code ?? 0);
|
|
2015
|
+
});
|
|
2016
|
+
});
|
|
2017
|
+
}
|
|
2018
|
+
function deriveLabel(command) {
|
|
2019
|
+
const trimmed = command.trim();
|
|
2020
|
+
if (!trimmed) return "process";
|
|
2021
|
+
const tokens = trimmed.split(/\s+/);
|
|
2022
|
+
const lastToken = tokens[tokens.length - 1] ?? trimmed;
|
|
2023
|
+
const labelCandidate = lastToken.split(/[/\\]/).pop() || lastToken;
|
|
2024
|
+
return labelCandidate || trimmed;
|
|
2025
|
+
}
|
|
2026
|
+
function isSpawnFailureError(error) {
|
|
2027
|
+
if (!error || typeof error !== "object") return false;
|
|
2028
|
+
const err = error;
|
|
2029
|
+
if (err.code === "ENOENT") return true;
|
|
2030
|
+
const message = toErrorMessage(error).toLowerCase();
|
|
2031
|
+
return message.includes("enoent") || message.includes("executable not found") || message.includes("spawn") && message.includes("not found");
|
|
2032
|
+
}
|
|
2033
|
+
function spawnFailedCommandResult(error, outputLimit) {
|
|
2034
|
+
const stderr = `spawn failed: ${toErrorMessage(error)}
|
|
2035
|
+
`;
|
|
2036
|
+
return {
|
|
2037
|
+
stdout: "",
|
|
2038
|
+
stderr,
|
|
2039
|
+
exitCode: 127,
|
|
2040
|
+
timedOut: false,
|
|
2041
|
+
spawnFailed: true,
|
|
2042
|
+
content: truncateToolOutput(
|
|
2043
|
+
[`stderr:
|
|
2044
|
+
${stderr.trim()}`, "exitCode: 127"].filter(Boolean).join("\n\n"),
|
|
2045
|
+
outputLimit
|
|
2046
|
+
)
|
|
2047
|
+
};
|
|
2048
|
+
}
|
|
2049
|
+
async function runWorkspaceCommand(args) {
|
|
2050
|
+
const timeoutMs = asOptionalPositiveFiniteNumber(args.timeoutMs);
|
|
2051
|
+
const detached = process.platform !== "win32";
|
|
2052
|
+
const command = [
|
|
2053
|
+
...args.commandPrefix ?? [],
|
|
2054
|
+
...args.command
|
|
2055
|
+
];
|
|
2056
|
+
const proc = spawnChildProcess(command[0] ?? "", command.slice(1), {
|
|
2057
|
+
cwd: resolve(args.workspaceRoot),
|
|
2058
|
+
stdio: [
|
|
2059
|
+
args.stdin === void 0 ? "ignore" : "pipe",
|
|
2060
|
+
"pipe",
|
|
2061
|
+
"pipe"
|
|
2062
|
+
],
|
|
2063
|
+
detached
|
|
2064
|
+
});
|
|
2065
|
+
if (args.stdin !== void 0 && proc.stdin) {
|
|
2066
|
+
proc.stdin.write(args.stdin);
|
|
2067
|
+
proc.stdin.end();
|
|
2068
|
+
}
|
|
2069
|
+
const exitPromise = waitForNodeProcessExit(proc);
|
|
2070
|
+
const cleanupChildOnHostSignal = (signal) => {
|
|
2071
|
+
if (typeof proc.pid === "number") {
|
|
2072
|
+
try {
|
|
2073
|
+
process.kill(-proc.pid, signal);
|
|
2074
|
+
} catch {
|
|
2075
|
+
}
|
|
2076
|
+
}
|
|
2077
|
+
};
|
|
2078
|
+
if (detached) {
|
|
2079
|
+
process.once("SIGHUP", cleanupChildOnHostSignal);
|
|
2080
|
+
process.once("SIGTERM", cleanupChildOnHostSignal);
|
|
2081
|
+
process.once("SIGINT", cleanupChildOnHostSignal);
|
|
2082
|
+
}
|
|
2083
|
+
const detachSignalCleanup = () => {
|
|
2084
|
+
if (!detached) return;
|
|
2085
|
+
process.removeListener("SIGHUP", cleanupChildOnHostSignal);
|
|
2086
|
+
process.removeListener("SIGTERM", cleanupChildOnHostSignal);
|
|
2087
|
+
process.removeListener("SIGINT", cleanupChildOnHostSignal);
|
|
2088
|
+
};
|
|
2089
|
+
exitPromise.then(detachSignalCleanup, detachSignalCleanup);
|
|
2090
|
+
const stdoutPromise = readNodeStream(proc.stdout);
|
|
2091
|
+
const stderrPromise = readNodeStream(proc.stderr);
|
|
2092
|
+
const killChild = (signal) => {
|
|
2093
|
+
if (detached && typeof proc.pid === "number") {
|
|
2094
|
+
try {
|
|
2095
|
+
process.kill(-proc.pid, signal);
|
|
2096
|
+
return;
|
|
2097
|
+
} catch {
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
try {
|
|
2101
|
+
proc.kill(signal);
|
|
2102
|
+
} catch {
|
|
2103
|
+
}
|
|
2104
|
+
};
|
|
2105
|
+
let timeout;
|
|
2106
|
+
let detachTimer;
|
|
2107
|
+
const timeoutResult = Symbol("timeout");
|
|
2108
|
+
const abortResult = Symbol("abort");
|
|
2109
|
+
const detachResult = Symbol("detach");
|
|
2110
|
+
let abortListener;
|
|
2111
|
+
const abortPromise = args.abortSignal ? new Promise((resolveAbort) => {
|
|
2112
|
+
if (args.abortSignal.aborted) {
|
|
2113
|
+
resolveAbort(abortResult);
|
|
2114
|
+
return;
|
|
2115
|
+
}
|
|
2116
|
+
abortListener = () => resolveAbort(abortResult);
|
|
2117
|
+
args.abortSignal.addEventListener("abort", abortListener, { once: true });
|
|
2118
|
+
}) : null;
|
|
2119
|
+
const effectiveDetachMs = resolveDetachMs(args.detachMs);
|
|
2120
|
+
const detachPromise = new Promise((resolveDetach) => {
|
|
2121
|
+
detachTimer = setTimeout(() => resolveDetach(detachResult), effectiveDetachMs);
|
|
2122
|
+
});
|
|
2123
|
+
const exitOrTimeoutPromise = timeoutMs ? Promise.race([
|
|
2124
|
+
exitPromise,
|
|
2125
|
+
new Promise((resolveTimeout) => {
|
|
2126
|
+
timeout = setTimeout(() => resolveTimeout(timeoutResult), timeoutMs);
|
|
2127
|
+
})
|
|
2128
|
+
]) : exitPromise;
|
|
2129
|
+
const raceCandidates = [
|
|
2130
|
+
exitOrTimeoutPromise,
|
|
2131
|
+
detachPromise
|
|
2132
|
+
];
|
|
2133
|
+
if (abortPromise) {
|
|
2134
|
+
raceCandidates.push(abortPromise);
|
|
2135
|
+
}
|
|
2136
|
+
let raceWinner;
|
|
2137
|
+
try {
|
|
2138
|
+
raceWinner = await Promise.race(raceCandidates);
|
|
2139
|
+
} catch (error) {
|
|
2140
|
+
if (timeout) clearTimeout(timeout);
|
|
2141
|
+
if (detachTimer) clearTimeout(detachTimer);
|
|
2142
|
+
if (abortListener && args.abortSignal) {
|
|
2143
|
+
args.abortSignal.removeEventListener("abort", abortListener);
|
|
2144
|
+
}
|
|
2145
|
+
if (isSpawnFailureError(error)) {
|
|
2146
|
+
return spawnFailedCommandResult(error, args.outputLimit);
|
|
2147
|
+
}
|
|
2148
|
+
throw error;
|
|
2149
|
+
}
|
|
2150
|
+
if (timeout) clearTimeout(timeout);
|
|
2151
|
+
if (detachTimer) clearTimeout(detachTimer);
|
|
2152
|
+
if (abortListener && args.abortSignal) {
|
|
2153
|
+
args.abortSignal.removeEventListener("abort", abortListener);
|
|
2154
|
+
}
|
|
2155
|
+
const timedOut = raceWinner === timeoutResult;
|
|
2156
|
+
const aborted = raceWinner === abortResult;
|
|
2157
|
+
const detachedProcess = raceWinner === detachResult;
|
|
2158
|
+
if (timedOut || aborted) {
|
|
2159
|
+
killChild("SIGTERM");
|
|
2160
|
+
const forceKillTimer = setTimeout(() => {
|
|
2161
|
+
killChild("SIGKILL");
|
|
2162
|
+
}, 500);
|
|
2163
|
+
try {
|
|
2164
|
+
await exitPromise;
|
|
2165
|
+
} catch {
|
|
2166
|
+
} finally {
|
|
2167
|
+
clearTimeout(forceKillTimer);
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
if (detachedProcess) {
|
|
2171
|
+
exitPromise.catch(() => {
|
|
2172
|
+
});
|
|
2173
|
+
const pid = typeof proc.pid === "number" ? proc.pid : 0;
|
|
2174
|
+
const label = deriveLabel(args.command.join(" "));
|
|
2175
|
+
const pgid = pid;
|
|
2176
|
+
const registry2 = getProcessRegistry();
|
|
2177
|
+
registry2.add({ pid, pgid, command: args.command.join(" "), label });
|
|
2178
|
+
proc.on("close", (code) => {
|
|
2179
|
+
detachSignalCleanup();
|
|
2180
|
+
registry2.markExited(pid, code ?? 1);
|
|
2181
|
+
});
|
|
2182
|
+
const immediate = effectiveDetachMs === 0;
|
|
2183
|
+
const reason = immediate ? `command looks long-running; moved to background immediately (pid=${pid}, label=${label})` : `command detached to background after ${effectiveDetachMs}ms (pid=${pid}, label=${label})`;
|
|
2184
|
+
return {
|
|
2185
|
+
stdout: "",
|
|
2186
|
+
stderr: `${reason}
|
|
2187
|
+
Use listProcesses to inspect or stop it.`,
|
|
2188
|
+
exitCode: 0,
|
|
2189
|
+
timedOut: false,
|
|
2190
|
+
detached: true,
|
|
2191
|
+
pid,
|
|
2192
|
+
label,
|
|
2193
|
+
content: JSON.stringify({
|
|
2194
|
+
detached: true,
|
|
2195
|
+
pid,
|
|
2196
|
+
label,
|
|
2197
|
+
status: "running",
|
|
2198
|
+
...immediate ? { reason: "long-running-command" } : {}
|
|
2199
|
+
})
|
|
2200
|
+
};
|
|
2201
|
+
}
|
|
2202
|
+
const [stdout, rawStderr] = await Promise.all([
|
|
2203
|
+
stdoutPromise,
|
|
2204
|
+
stderrPromise
|
|
2205
|
+
]);
|
|
2206
|
+
const exitCode = aborted ? 130 : timedOut ? 124 : Number(raceWinner);
|
|
2207
|
+
const stderr = aborted ? `${rawStderr.trim() ? `${rawStderr.trim()}
|
|
2208
|
+
` : ""}command aborted by signal
|
|
2209
|
+
` : timedOut ? `${rawStderr.trim() ? `${rawStderr.trim()}
|
|
2210
|
+
` : ""}command timed out after ${timeoutMs ?? "unknown"}ms
|
|
2211
|
+
` : rawStderr;
|
|
2212
|
+
return {
|
|
2213
|
+
stdout,
|
|
2214
|
+
stderr,
|
|
2215
|
+
exitCode,
|
|
2216
|
+
timedOut,
|
|
2217
|
+
aborted,
|
|
2218
|
+
content: truncateToolOutput([
|
|
2219
|
+
stdout.trim() ? `stdout:
|
|
2220
|
+
${stdout.trim()}` : "",
|
|
2221
|
+
stderr.trim() ? `stderr:
|
|
2222
|
+
${stderr.trim()}` : "",
|
|
2223
|
+
`exitCode: ${exitCode}`
|
|
2224
|
+
].filter(Boolean).join("\n\n"), args.outputLimit)
|
|
2225
|
+
};
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
// packages/agent-runtime/capabilities/execShellCapability.ts
|
|
2229
|
+
function buildExecShellToolDefinition(toolName = "execShell") {
|
|
2230
|
+
return {
|
|
2231
|
+
type: "function",
|
|
2232
|
+
function: {
|
|
2233
|
+
name: toolName,
|
|
2234
|
+
description: `Execute a shell command from the workspace root. Prefer one compound command (e.g. 'git status && git diff --stat') to perform complete verification in one step instead of multiple small roundtrips. Do not cd into guessed paths; commands already run from the workspace root. Commands block until exit. Long-running commands (sleep over ${IMMEDIATE_DETACH_SLEEP_THRESHOLD_SECONDS}s, dev servers, watchers) automatically detach to background returning {detached: true, pid, label}; for persistent services, prefer launchProcess.`,
|
|
2235
|
+
parameters: {
|
|
2236
|
+
type: "object",
|
|
2237
|
+
properties: {
|
|
2238
|
+
command: {
|
|
2239
|
+
type: "string",
|
|
2240
|
+
description: "Shell command to run (non-empty)."
|
|
2241
|
+
},
|
|
2242
|
+
cmd: {
|
|
2243
|
+
type: "string",
|
|
2244
|
+
description: "Compatibility alias for command."
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
};
|
|
2250
|
+
}
|
|
2251
|
+
function normalizeExecShellInput(input) {
|
|
2252
|
+
if (input === null || input === void 0) {
|
|
2253
|
+
throw new Error("execShell requires a non-empty command.");
|
|
2254
|
+
}
|
|
2255
|
+
if (typeof input === "string") {
|
|
2256
|
+
const trimmed = input.trim();
|
|
2257
|
+
if (!trimmed) {
|
|
2258
|
+
throw new Error("execShell requires a non-empty command.");
|
|
2259
|
+
}
|
|
2260
|
+
if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
|
|
2261
|
+
try {
|
|
2262
|
+
const parsed = JSON.parse(trimmed);
|
|
2263
|
+
if (parsed && typeof parsed === "object") {
|
|
2264
|
+
return normalizeExecShellInput(parsed);
|
|
2265
|
+
}
|
|
2266
|
+
} catch {
|
|
2267
|
+
}
|
|
2268
|
+
}
|
|
2269
|
+
return { command: trimmed };
|
|
2270
|
+
}
|
|
2271
|
+
if (typeof input === "object") {
|
|
2272
|
+
const record = input;
|
|
2273
|
+
let rawCommand = resolveShellCommandArg(record);
|
|
2274
|
+
if (typeof rawCommand !== "string" || !rawCommand.trim()) {
|
|
2275
|
+
const fallback = record.command ?? record.cmd;
|
|
2276
|
+
if (typeof fallback === "string" && fallback.trim()) {
|
|
2277
|
+
rawCommand = fallback;
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
const command = typeof rawCommand === "string" ? rawCommand.trim() : "";
|
|
2281
|
+
if (!command) {
|
|
2282
|
+
throw new Error("execShell requires a non-empty command.");
|
|
2283
|
+
}
|
|
2284
|
+
const activity = extractActivity(record);
|
|
2285
|
+
return {
|
|
2286
|
+
command,
|
|
2287
|
+
...record.shell !== void 0 ? { shell: record.shell } : {},
|
|
2288
|
+
...activity ? { activity } : {},
|
|
2289
|
+
...record.input !== void 0 ? { input: record.input } : {}
|
|
2290
|
+
};
|
|
2291
|
+
}
|
|
2292
|
+
throw new Error("execShell requires a non-empty command.");
|
|
2293
|
+
}
|
|
2294
|
+
var execShellCapability = {
|
|
2295
|
+
name: "execShell",
|
|
2296
|
+
getToolDefinition(toolName = "execShell") {
|
|
2297
|
+
return buildExecShellToolDefinition(toolName);
|
|
2298
|
+
},
|
|
2299
|
+
normalizeInput(input) {
|
|
2300
|
+
return normalizeExecShellInput(input);
|
|
2301
|
+
},
|
|
2302
|
+
async invoke(ctx, normalized) {
|
|
2303
|
+
const workspaceRoot = ctx.workspaceRoot || process.cwd();
|
|
2304
|
+
const command = normalized.command;
|
|
2305
|
+
if (ctx.restrictToWorkspace) {
|
|
2306
|
+
const escapeToken = findWorkspaceShellEscapeToken(command);
|
|
2307
|
+
if (escapeToken) {
|
|
2308
|
+
return buildWorkspaceShellEscapeBlockedResult({ command, token: escapeToken });
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
const interactiveAuthCommand = extractInteractiveGhAuthCommand(command);
|
|
2312
|
+
if (interactiveAuthCommand) {
|
|
2313
|
+
const blocked = buildInteractiveCommandBlockedResult(interactiveAuthCommand);
|
|
2314
|
+
return {
|
|
2315
|
+
...blocked,
|
|
2316
|
+
metadata: {
|
|
2317
|
+
...blocked.metadata,
|
|
2318
|
+
...normalized.activity ? { activity: normalized.activity } : {}
|
|
2319
|
+
}
|
|
2320
|
+
};
|
|
2321
|
+
}
|
|
2322
|
+
const result = await runWorkspaceCommand({
|
|
2323
|
+
workspaceRoot,
|
|
2324
|
+
command: buildWorkspaceShellCommand({
|
|
2325
|
+
toolName: "execShell",
|
|
2326
|
+
command,
|
|
2327
|
+
shell: normalized.shell
|
|
2328
|
+
}),
|
|
2329
|
+
timeoutMs: resolveExecShellTimeoutMs(ctx.commandTimeoutMs),
|
|
2330
|
+
outputLimit: ctx.commandOutputLimit,
|
|
2331
|
+
commandPrefix: ctx.commandPrefix,
|
|
2332
|
+
abortSignal: ctx.abortSignal,
|
|
2333
|
+
detachMs: isImmediateDetachShellCommand({ command }) ? 0 : ctx.detachMs,
|
|
2334
|
+
stdin: typeof normalized.input === "string" ? normalized.input : void 0
|
|
2335
|
+
});
|
|
2336
|
+
return {
|
|
2337
|
+
content: result.content,
|
|
2338
|
+
metadata: {
|
|
2339
|
+
command,
|
|
2340
|
+
exitCode: result.exitCode,
|
|
2341
|
+
timedOut: result.timedOut,
|
|
2342
|
+
...result.aborted ? { aborted: true } : {},
|
|
2343
|
+
...result.detached ? { detached: true, pid: result.pid, label: result.label, status: "running" } : {},
|
|
2344
|
+
...normalized.activity ? { activity: normalized.activity } : {}
|
|
2345
|
+
}
|
|
2346
|
+
};
|
|
2347
|
+
}
|
|
2348
|
+
};
|
|
2349
|
+
|
|
2350
|
+
// packages/agent-runtime/localWorkspaceToolDefs.ts
|
|
2351
|
+
var WORKSPACE_TOOL_NAMES = [
|
|
2352
|
+
"listFiles",
|
|
2353
|
+
"readFile",
|
|
2354
|
+
"writeFile",
|
|
2355
|
+
"editFile",
|
|
2356
|
+
"globFiles",
|
|
2357
|
+
"searchFiles",
|
|
2358
|
+
"captureVisualState",
|
|
2359
|
+
"execShell",
|
|
2360
|
+
"launchProcess",
|
|
2361
|
+
"listProcesses"
|
|
2362
|
+
];
|
|
2363
|
+
var SHELL_TOOL_NAMES = ["execShell", "launchProcess", "listProcesses"];
|
|
2364
|
+
var WORKSPACE_TOOL_NAME_SET = new Set(WORKSPACE_TOOL_NAMES);
|
|
2365
|
+
var REMOVED_WORKSPACE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
2366
|
+
"gitStatus",
|
|
2367
|
+
"gitDiff",
|
|
2368
|
+
"gitCreateBranch",
|
|
2369
|
+
"gitAdd",
|
|
2370
|
+
"gitCommit",
|
|
2371
|
+
"commitWorkspace"
|
|
2372
|
+
]);
|
|
2373
|
+
function buildWorkspacePathProperty() {
|
|
2374
|
+
return {
|
|
2375
|
+
type: "string",
|
|
2376
|
+
minLength: 1,
|
|
2377
|
+
description: "Path relative to the workspace root. Defaults to workspace root when omitted."
|
|
2378
|
+
};
|
|
2379
|
+
}
|
|
2380
|
+
function buildListWorkspaceDescription(variant) {
|
|
2381
|
+
if (variant === "brief") {
|
|
2382
|
+
return "List files and directories inside a workspace directory.";
|
|
2383
|
+
}
|
|
2384
|
+
if (variant === "workflow") {
|
|
2385
|
+
return "List directory overview inside workspace. Then use globFiles for path patterns, searchFiles for text discovery, and readFile for content.";
|
|
2386
|
+
}
|
|
2387
|
+
if (variant === "antiShell") {
|
|
2388
|
+
return "List directory overview inside workspace. Prefer listFiles over shell ls/find commands.";
|
|
2389
|
+
}
|
|
2390
|
+
return "List files and directories inside a workspace folder. Defaults to shallow overview (depth 1).";
|
|
2391
|
+
}
|
|
2392
|
+
function buildListWorkspaceParameters(variant) {
|
|
2393
|
+
const path = buildWorkspacePathProperty();
|
|
2394
|
+
const maxDepth = {
|
|
2395
|
+
type: "integer",
|
|
2396
|
+
description: "Maximum directory depth to include. Defaults to 1 for the requested directory only."
|
|
2397
|
+
};
|
|
2398
|
+
const maxResults = {
|
|
2399
|
+
type: "integer",
|
|
1536
2400
|
description: "Maximum number of entries to return. Use a small value for large directories."
|
|
1537
2401
|
};
|
|
1538
2402
|
const entryType = {
|
|
@@ -1879,26 +2743,7 @@ function buildCaptureVisualStateTool() {
|
|
|
1879
2743
|
};
|
|
1880
2744
|
}
|
|
1881
2745
|
function buildExecShellTool(toolName) {
|
|
1882
|
-
return
|
|
1883
|
-
type: "function",
|
|
1884
|
-
function: {
|
|
1885
|
-
name: toolName,
|
|
1886
|
-
description: `Execute a shell command from the workspace root. Prefer one compound command (e.g. 'git status && git diff --stat') to perform complete verification in one step instead of multiple small roundtrips. Do not cd into guessed paths; commands already run from the workspace root. Commands block until exit. Long-running commands (sleep over ${IMMEDIATE_DETACH_SLEEP_THRESHOLD_SECONDS}s, dev servers, watchers) automatically detach to background returning {detached: true, pid, label}; for persistent services, prefer launchProcess.`,
|
|
1887
|
-
parameters: {
|
|
1888
|
-
type: "object",
|
|
1889
|
-
properties: {
|
|
1890
|
-
cmd: {
|
|
1891
|
-
type: "string",
|
|
1892
|
-
description: "Shell command to run (non-empty)."
|
|
1893
|
-
},
|
|
1894
|
-
command: {
|
|
1895
|
-
type: "string",
|
|
1896
|
-
description: "Compatibility alias for cmd."
|
|
1897
|
-
}
|
|
1898
|
-
}
|
|
1899
|
-
}
|
|
1900
|
-
}
|
|
1901
|
-
};
|
|
2746
|
+
return buildExecShellToolDefinition(toolName);
|
|
1902
2747
|
}
|
|
1903
2748
|
function buildLaunchProcessTool() {
|
|
1904
2749
|
return {
|
|
@@ -1922,88 +2767,21 @@ function buildLaunchProcessTool() {
|
|
|
1922
2767
|
description: "Keep process alive after session/conversation closes. Defaults to false."
|
|
1923
2768
|
}
|
|
1924
2769
|
},
|
|
1925
|
-
required: ["command"]
|
|
1926
|
-
}
|
|
1927
|
-
}
|
|
1928
|
-
};
|
|
1929
|
-
}
|
|
1930
|
-
function buildListProcessesTool() {
|
|
1931
|
-
return {
|
|
1932
|
-
type: "function",
|
|
1933
|
-
function: {
|
|
1934
|
-
name: "listProcesses",
|
|
1935
|
-
description: "List active background processes launched via launchProcess. Returns {pid, label, command, status, startedAt, persist}[].",
|
|
1936
|
-
parameters: {
|
|
1937
|
-
type: "object",
|
|
1938
|
-
properties: {}
|
|
1939
|
-
}
|
|
1940
|
-
}
|
|
1941
|
-
};
|
|
1942
|
-
}
|
|
1943
|
-
function wrapPowerShellCommand(command) {
|
|
1944
|
-
return [
|
|
1945
|
-
"[Console]::InputEncoding=[System.Text.Encoding]::UTF8",
|
|
1946
|
-
"[Console]::OutputEncoding=[System.Text.Encoding]::UTF8",
|
|
1947
|
-
"$OutputEncoding=[System.Text.Encoding]::UTF8",
|
|
1948
|
-
"$PSStyle.OutputRendering='PlainText'",
|
|
1949
|
-
command
|
|
1950
|
-
].join("; ");
|
|
1951
|
-
}
|
|
1952
|
-
function findPowerShellExecutable() {
|
|
1953
|
-
return resolveExecutableOnPath("pwsh") || resolveExecutableOnPath("powershell.exe") || resolveExecutableOnPath("powershell");
|
|
1954
|
-
}
|
|
1955
|
-
function buildPowerShellCommand(command) {
|
|
1956
|
-
const executable = findPowerShellExecutable();
|
|
1957
|
-
if (!executable) throw new Error("PowerShell is not available on this machine.");
|
|
1958
|
-
return [
|
|
1959
|
-
executable,
|
|
1960
|
-
"-NoLogo",
|
|
1961
|
-
"-NoProfile",
|
|
1962
|
-
"-NonInteractive",
|
|
1963
|
-
"-ExecutionPolicy",
|
|
1964
|
-
"Bypass",
|
|
1965
|
-
"-Command",
|
|
1966
|
-
wrapPowerShellCommand(command)
|
|
1967
|
-
];
|
|
1968
|
-
}
|
|
1969
|
-
function buildBashCommand(command) {
|
|
1970
|
-
const executable = resolveExecutableOnPath("bash") || resolveExecutableOnPath("sh");
|
|
1971
|
-
if (!executable) throw new Error("bash/sh is not available on this machine.");
|
|
1972
|
-
return [executable, "-lc", command];
|
|
1973
|
-
}
|
|
1974
|
-
function buildWorkspaceShellCommand(args) {
|
|
1975
|
-
if (args.shell === "powershell") return buildPowerShellCommand(args.command);
|
|
1976
|
-
if (args.shell === "bash") return buildBashCommand(args.command);
|
|
1977
|
-
return process.platform === "win32" ? buildPowerShellCommand(args.command) : buildBashCommand(args.command);
|
|
1978
|
-
}
|
|
1979
|
-
function findWorkspaceShellEscapeToken(command) {
|
|
1980
|
-
const tokens = tokenizeShellPrefix(command);
|
|
1981
|
-
for (const token of tokens) {
|
|
1982
|
-
if (token === ".." || token.startsWith("../") || token.startsWith("..\\") || token.includes("/../") || token.includes("\\..\\")) {
|
|
1983
|
-
return token;
|
|
1984
|
-
}
|
|
1985
|
-
if (token === "~" || token.startsWith("~/") || token.startsWith("~\\")) {
|
|
1986
|
-
return token;
|
|
1987
|
-
}
|
|
1988
|
-
if (token === "/" || token.startsWith("/") || /^[A-Za-z]:[\\/]/.test(token) || token.startsWith("\\\\")) {
|
|
1989
|
-
return token;
|
|
2770
|
+
required: ["command"]
|
|
2771
|
+
}
|
|
1990
2772
|
}
|
|
1991
|
-
}
|
|
1992
|
-
return null;
|
|
2773
|
+
};
|
|
1993
2774
|
}
|
|
1994
|
-
function
|
|
2775
|
+
function buildListProcessesTool() {
|
|
1995
2776
|
return {
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
exitCode: 126,
|
|
2005
|
-
workspaceShellEscapeBlocked: true,
|
|
2006
|
-
blockedToken: args.token
|
|
2777
|
+
type: "function",
|
|
2778
|
+
function: {
|
|
2779
|
+
name: "listProcesses",
|
|
2780
|
+
description: "List active background processes launched via launchProcess. Returns {pid, label, command, status, startedAt, persist}[].",
|
|
2781
|
+
parameters: {
|
|
2782
|
+
type: "object",
|
|
2783
|
+
properties: {}
|
|
2784
|
+
}
|
|
2007
2785
|
}
|
|
2008
2786
|
};
|
|
2009
2787
|
}
|
|
@@ -2050,10 +2828,24 @@ function filterDeclaredWorkspaceToolNames(args) {
|
|
|
2050
2828
|
);
|
|
2051
2829
|
}
|
|
2052
2830
|
|
|
2831
|
+
// packages/agent-runtime/capabilities/capabilitySdk.ts
|
|
2832
|
+
var BUILTIN_CAPABILITIES = [
|
|
2833
|
+
execShellCapability
|
|
2834
|
+
];
|
|
2835
|
+
async function invokeCapability(capabilityOrName, input, ctx = {}) {
|
|
2836
|
+
const capability = typeof capabilityOrName === "string" ? BUILTIN_CAPABILITIES.find((c) => c.name === capabilityOrName) : capabilityOrName;
|
|
2837
|
+
if (!capability) {
|
|
2838
|
+
throw new Error(`Capability "${String(capabilityOrName)}" not found.`);
|
|
2839
|
+
}
|
|
2840
|
+
const normalizedInput = capability.normalizeInput(input);
|
|
2841
|
+
await evaluateCapabilityPolicy(capability, normalizedInput, ctx);
|
|
2842
|
+
if (ctx.onInvoke) {
|
|
2843
|
+
await ctx.onInvoke(capability.name, normalizedInput);
|
|
2844
|
+
}
|
|
2845
|
+
return capability.invoke(ctx, normalizedInput);
|
|
2846
|
+
}
|
|
2847
|
+
|
|
2053
2848
|
// packages/agent-runtime/localWorkspaceTools.ts
|
|
2054
|
-
var EXEC_SHELL_TIMEOUT_ENV = "NOLO_EXEC_SHELL_TIMEOUT_MS";
|
|
2055
|
-
var EXEC_SHELL_DETACH_ENV = "NOLO_EXEC_SHELL_DETACH_MS";
|
|
2056
|
-
var DEFAULT_EXEC_SHELL_DETACH_MS = 12e4;
|
|
2057
2849
|
var DEFAULT_LOCAL_API_ORIGIN = "http://127.0.0.1:38123";
|
|
2058
2850
|
var ALWAYS_EXCLUDED_GLOBS = [
|
|
2059
2851
|
"*.tsbuildinfo",
|
|
@@ -2073,7 +2865,7 @@ var ALWAYS_EXCLUDED_GLOBS = [
|
|
|
2073
2865
|
];
|
|
2074
2866
|
var DEFAULT_SEARCH_MAX_RESULTS = 200;
|
|
2075
2867
|
var SEARCH_OUTPUT_CHAR_LIMIT = 2e4;
|
|
2076
|
-
function
|
|
2868
|
+
function readTrimmedString2(value) {
|
|
2077
2869
|
return asOptionalTrimmedString(value);
|
|
2078
2870
|
}
|
|
2079
2871
|
function readBoolean(parsed, key) {
|
|
@@ -2086,168 +2878,6 @@ function readBoolean(parsed, key) {
|
|
|
2086
2878
|
}
|
|
2087
2879
|
return void 0;
|
|
2088
2880
|
}
|
|
2089
|
-
function resolveExecShellTimeoutMs(override) {
|
|
2090
|
-
const fromOverride = asOptionalPositiveFiniteNumber(override);
|
|
2091
|
-
if (fromOverride !== void 0) return fromOverride;
|
|
2092
|
-
const raw = process.env[EXEC_SHELL_TIMEOUT_ENV];
|
|
2093
|
-
if (raw === void 0) return void 0;
|
|
2094
|
-
return asOptionalPositiveFiniteNumber(Number(raw));
|
|
2095
|
-
}
|
|
2096
|
-
function resolveDetachMs(override) {
|
|
2097
|
-
if (typeof override === "number" && Number.isFinite(override) && override >= 0) {
|
|
2098
|
-
return override;
|
|
2099
|
-
}
|
|
2100
|
-
const raw = process.env[EXEC_SHELL_DETACH_ENV];
|
|
2101
|
-
if (raw === void 0) return DEFAULT_EXEC_SHELL_DETACH_MS;
|
|
2102
|
-
const parsed = asOptionalPositiveFiniteNumber(Number(raw));
|
|
2103
|
-
return parsed === void 0 ? DEFAULT_EXEC_SHELL_DETACH_MS : parsed;
|
|
2104
|
-
}
|
|
2105
|
-
function extractInteractiveGhAuthCommand(command) {
|
|
2106
|
-
const tokens = tokenizeShellPrefix(command);
|
|
2107
|
-
if (tokens[0] !== "gh" || tokens[1] !== "auth") return null;
|
|
2108
|
-
const subcommand = tokens[2];
|
|
2109
|
-
if (subcommand !== "login" && subcommand !== "refresh") return null;
|
|
2110
|
-
if (tokens.includes("--help")) return null;
|
|
2111
|
-
const result = ["gh", "auth", subcommand];
|
|
2112
|
-
for (let index = 3; index < tokens.length; index += 1) {
|
|
2113
|
-
const token = tokens[index];
|
|
2114
|
-
if (!token) continue;
|
|
2115
|
-
if (token === "-h" || token === "--hostname" || token === "-s" || token === "--scopes" || token === "--remove-scopes" || token === "-r") {
|
|
2116
|
-
const value = tokens[index + 1];
|
|
2117
|
-
if (value) {
|
|
2118
|
-
result.push(token, value);
|
|
2119
|
-
index += 1;
|
|
2120
|
-
}
|
|
2121
|
-
continue;
|
|
2122
|
-
}
|
|
2123
|
-
if (token === "--clipboard" || token === "-c" || token === "--insecure-storage" || token === "--reset-scopes") {
|
|
2124
|
-
result.push(token);
|
|
2125
|
-
continue;
|
|
2126
|
-
}
|
|
2127
|
-
if (token.startsWith("--hostname=") || token.startsWith("--scopes=") || token.startsWith("--remove-scopes=")) {
|
|
2128
|
-
result.push(token);
|
|
2129
|
-
continue;
|
|
2130
|
-
}
|
|
2131
|
-
}
|
|
2132
|
-
return result.join(" ");
|
|
2133
|
-
}
|
|
2134
|
-
function splitShellWords(command) {
|
|
2135
|
-
return tokenizeShellPrefix(command);
|
|
2136
|
-
}
|
|
2137
|
-
function buildInteractiveCommandBlockedResult(command) {
|
|
2138
|
-
const argv = splitShellWords(command);
|
|
2139
|
-
return {
|
|
2140
|
-
content: [
|
|
2141
|
-
"action_gate: handoff",
|
|
2142
|
-
`command: ${command}`,
|
|
2143
|
-
"Run this in the current TUI terminal, then resume the agent turn.",
|
|
2144
|
-
"exitCode: 130"
|
|
2145
|
-
].join("\n"),
|
|
2146
|
-
metadata: {
|
|
2147
|
-
exitCode: 130,
|
|
2148
|
-
timedOut: false,
|
|
2149
|
-
actionGate: {
|
|
2150
|
-
id: `gate-${Date.now().toString(36)}-terminal-handoff`,
|
|
2151
|
-
kind: "handoff",
|
|
2152
|
-
title: "This command requires an interactive terminal.",
|
|
2153
|
-
body: "Complete it in the terminal, then nolo will continue.",
|
|
2154
|
-
payload: {
|
|
2155
|
-
command: argv,
|
|
2156
|
-
displayCommand: command
|
|
2157
|
-
}
|
|
2158
|
-
},
|
|
2159
|
-
reason: "interactive-command-requires-terminal"
|
|
2160
|
-
}
|
|
2161
|
-
};
|
|
2162
|
-
}
|
|
2163
|
-
function extractActivityRefs(rawRefs) {
|
|
2164
|
-
if (!Array.isArray(rawRefs)) return void 0;
|
|
2165
|
-
const refs = rawRefs.flatMap((entry) => {
|
|
2166
|
-
if (!isRecord(entry)) return [];
|
|
2167
|
-
if (entry.type === "file") {
|
|
2168
|
-
const path = readTrimmedString(entry.path);
|
|
2169
|
-
return path ? [{ type: "file", path }] : [];
|
|
2170
|
-
}
|
|
2171
|
-
if (entry.type === "terminal") {
|
|
2172
|
-
const id = readTrimmedString(entry.id);
|
|
2173
|
-
const label = readTrimmedString(entry.label);
|
|
2174
|
-
return id || label ? [{ type: "terminal", ...id ? { id } : {}, ...label ? { label } : {} }] : [];
|
|
2175
|
-
}
|
|
2176
|
-
if (entry.type === "url") {
|
|
2177
|
-
const url = readTrimmedString(entry.url);
|
|
2178
|
-
const label = readTrimmedString(entry.label);
|
|
2179
|
-
return url ? [{ type: "url", url, ...label ? { label } : {} }] : [];
|
|
2180
|
-
}
|
|
2181
|
-
return [];
|
|
2182
|
-
});
|
|
2183
|
-
return refs.length ? refs : void 0;
|
|
2184
|
-
}
|
|
2185
|
-
function extractActivityAction(rawAction) {
|
|
2186
|
-
if (!isRecord(rawAction)) return void 0;
|
|
2187
|
-
const title = readTrimmedString(rawAction.title);
|
|
2188
|
-
if (!title) return void 0;
|
|
2189
|
-
const kind = readTrimmedString(rawAction.kind);
|
|
2190
|
-
const detail = readTrimmedString(rawAction.detail);
|
|
2191
|
-
const refs = extractActivityRefs(rawAction.refs);
|
|
2192
|
-
return {
|
|
2193
|
-
title,
|
|
2194
|
-
...kind ? { kind } : {},
|
|
2195
|
-
...detail ? { detail } : {},
|
|
2196
|
-
...refs ? { refs } : {}
|
|
2197
|
-
};
|
|
2198
|
-
}
|
|
2199
|
-
function extractActivityPhase(rawPhase) {
|
|
2200
|
-
if (!isRecord(rawPhase)) return void 0;
|
|
2201
|
-
const title = readTrimmedString(rawPhase.title);
|
|
2202
|
-
if (!title) return void 0;
|
|
2203
|
-
const id = readTrimmedString(rawPhase.id) || title.toLowerCase().replace(/\s+/g, "-");
|
|
2204
|
-
const index = asOptionalFiniteNumber(rawPhase.index);
|
|
2205
|
-
const total = asOptionalFiniteNumber(rawPhase.total);
|
|
2206
|
-
const status = rawPhase.status === "pending" || rawPhase.status === "running" || rawPhase.status === "success" || rawPhase.status === "failed" ? rawPhase.status : void 0;
|
|
2207
|
-
return {
|
|
2208
|
-
id,
|
|
2209
|
-
title,
|
|
2210
|
-
...index !== void 0 ? { index } : {},
|
|
2211
|
-
...total !== void 0 ? { total } : {},
|
|
2212
|
-
...status ? { status } : {}
|
|
2213
|
-
};
|
|
2214
|
-
}
|
|
2215
|
-
function extractActivityPlan(rawPlan) {
|
|
2216
|
-
if (!isRecord(rawPlan)) return void 0;
|
|
2217
|
-
if (!Array.isArray(rawPlan.phases)) return void 0;
|
|
2218
|
-
const phases = rawPlan.phases.flatMap((entry, index) => {
|
|
2219
|
-
const phase = extractActivityPhase(entry);
|
|
2220
|
-
if (!phase) return [];
|
|
2221
|
-
return [{
|
|
2222
|
-
id: phase.id,
|
|
2223
|
-
title: phase.title,
|
|
2224
|
-
index: phase.index ?? index + 1,
|
|
2225
|
-
...phase.status ? { status: phase.status } : {}
|
|
2226
|
-
}];
|
|
2227
|
-
});
|
|
2228
|
-
if (phases.length === 0) return void 0;
|
|
2229
|
-
const title = readTrimmedString(rawPlan.title);
|
|
2230
|
-
return {
|
|
2231
|
-
...title ? { title } : {},
|
|
2232
|
-
phases
|
|
2233
|
-
};
|
|
2234
|
-
}
|
|
2235
|
-
function extractActivity(parsed) {
|
|
2236
|
-
const raw = parsed._activity;
|
|
2237
|
-
if (!isRecord(raw)) return void 0;
|
|
2238
|
-
const nestedAction = extractActivityAction(raw.action);
|
|
2239
|
-
const legacyAction = extractActivityAction(raw);
|
|
2240
|
-
const action = nestedAction || legacyAction;
|
|
2241
|
-
const phase = extractActivityPhase(raw.phase);
|
|
2242
|
-
const plan = extractActivityPlan(raw.plan);
|
|
2243
|
-
if (!action && !plan) return void 0;
|
|
2244
|
-
return {
|
|
2245
|
-
...action ? action : {},
|
|
2246
|
-
...phase ? { phase } : {},
|
|
2247
|
-
...nestedAction ? { action: nestedAction } : {},
|
|
2248
|
-
...plan ? { plan } : {}
|
|
2249
|
-
};
|
|
2250
|
-
}
|
|
2251
2881
|
function buildLocalWorkspaceToolset(args) {
|
|
2252
2882
|
const exposeShellTools = args.exposeShellTools === true;
|
|
2253
2883
|
if (args.useDeclaredToolNamesOnly) {
|
|
@@ -2650,7 +3280,7 @@ function readVisualStateCaptureArgs(args) {
|
|
|
2650
3280
|
}
|
|
2651
3281
|
async function readWorkspacePackageScripts(workspaceRoot) {
|
|
2652
3282
|
try {
|
|
2653
|
-
const raw = await readFile(
|
|
3283
|
+
const raw = await readFile(resolve2(workspaceRoot, "package.json"), "utf8");
|
|
2654
3284
|
const parsed = JSON.parse(raw);
|
|
2655
3285
|
const scripts = parsed && typeof parsed === "object" && parsed.scripts && typeof parsed.scripts === "object" ? Object.keys(parsed.scripts).sort() : [];
|
|
2656
3286
|
return { scripts };
|
|
@@ -2685,97 +3315,35 @@ async function runWorkspacePackageScript(args) {
|
|
|
2685
3315
|
}
|
|
2686
3316
|
};
|
|
2687
3317
|
}
|
|
2688
|
-
const result = await runWorkspaceCommand({
|
|
2689
|
-
workspaceRoot: args.workspaceRoot,
|
|
2690
|
-
command: ["bun", "run", script, ...extraArgs.length ? ["--", ...extraArgs] : []],
|
|
2691
|
-
timeoutMs: args.commandTimeoutMs
|
|
2692
|
-
});
|
|
2693
|
-
return {
|
|
2694
|
-
content: result.content,
|
|
2695
|
-
metadata: {
|
|
2696
|
-
script,
|
|
2697
|
-
args: extraArgs,
|
|
2698
|
-
exitCode: result.exitCode,
|
|
2699
|
-
reason: result.exitCode === 0 ? "ok" : "script-failed",
|
|
2700
|
-
timedOut: result.timedOut,
|
|
2701
|
-
stdoutTail: result.stdout.trim().slice(-4e3),
|
|
2702
|
-
stderrTail: result.stderr.trim().slice(-4e3)
|
|
2703
|
-
}
|
|
2704
|
-
};
|
|
2705
|
-
}
|
|
2706
|
-
function truncateToolOutput(value, limit = 2e4) {
|
|
2707
|
-
if (value.length <= limit) return value;
|
|
2708
|
-
const approxMarkerLen = 40;
|
|
2709
|
-
if (limit <= approxMarkerLen) return value.slice(0, limit);
|
|
2710
|
-
const remaining = limit - approxMarkerLen;
|
|
2711
|
-
const headSize = Math.floor(remaining * 0.3);
|
|
2712
|
-
const tailSize = remaining - headSize;
|
|
2713
|
-
const head = value.slice(0, headSize);
|
|
2714
|
-
const tail = value.slice(-tailSize);
|
|
2715
|
-
const actualRemoved = value.length - headSize - tailSize;
|
|
2716
|
-
return `${head}
|
|
2717
|
-
|
|
2718
|
-
[... truncated ${actualRemoved} chars ...]
|
|
2719
|
-
|
|
2720
|
-
${tail}`;
|
|
2721
|
-
}
|
|
2722
|
-
function parseLastJsonObject(text) {
|
|
2723
|
-
const trimmed = text.trim();
|
|
2724
|
-
for (let index = trimmed.lastIndexOf("{"); index >= 0; index = trimmed.lastIndexOf("{", index - 1)) {
|
|
2725
|
-
try {
|
|
2726
|
-
const parsed = JSON.parse(trimmed.slice(index));
|
|
2727
|
-
return parsed && typeof parsed === "object" ? parsed : null;
|
|
2728
|
-
} catch {
|
|
2729
|
-
}
|
|
2730
|
-
}
|
|
2731
|
-
return null;
|
|
2732
|
-
}
|
|
2733
|
-
function readNodeStream(stream) {
|
|
2734
|
-
if (!stream) return Promise.resolve("");
|
|
2735
|
-
return new Promise((resolveRead, rejectRead) => {
|
|
2736
|
-
const chunks = [];
|
|
2737
|
-
stream.on("data", (chunk) => {
|
|
2738
|
-
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
|
|
2739
|
-
});
|
|
2740
|
-
stream.on("error", rejectRead);
|
|
2741
|
-
stream.on("end", () => resolveRead(Buffer.concat(chunks).toString("utf8")));
|
|
2742
|
-
});
|
|
2743
|
-
}
|
|
2744
|
-
function waitForNodeProcessExit(proc) {
|
|
2745
|
-
return new Promise((resolveExit, rejectExit) => {
|
|
2746
|
-
proc.once("error", rejectExit);
|
|
2747
|
-
proc.once("close", (code, signal) => {
|
|
2748
|
-
if (typeof code === "number") {
|
|
2749
|
-
resolveExit(code);
|
|
2750
|
-
return;
|
|
2751
|
-
}
|
|
2752
|
-
resolveExit(signal ? 1 : 0);
|
|
2753
|
-
});
|
|
2754
|
-
});
|
|
2755
|
-
}
|
|
2756
|
-
function isSpawnFailureError(error) {
|
|
2757
|
-
if (!error || typeof error !== "object") return false;
|
|
2758
|
-
const err = error;
|
|
2759
|
-
if (err.code === "ENOENT") return true;
|
|
2760
|
-
const message = toErrorMessage(error).toLowerCase();
|
|
2761
|
-
return message.includes("enoent") || message.includes("executable not found") || message.includes("spawn") && message.includes("not found");
|
|
2762
|
-
}
|
|
2763
|
-
function spawnFailedCommandResult(error, outputLimit) {
|
|
2764
|
-
const stderr = `spawn failed: ${toErrorMessage(error)}
|
|
2765
|
-
`;
|
|
3318
|
+
const result = await runWorkspaceCommand({
|
|
3319
|
+
workspaceRoot: args.workspaceRoot,
|
|
3320
|
+
command: ["bun", "run", script, ...extraArgs.length ? ["--", ...extraArgs] : []],
|
|
3321
|
+
timeoutMs: args.commandTimeoutMs
|
|
3322
|
+
});
|
|
2766
3323
|
return {
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
3324
|
+
content: result.content,
|
|
3325
|
+
metadata: {
|
|
3326
|
+
script,
|
|
3327
|
+
args: extraArgs,
|
|
3328
|
+
exitCode: result.exitCode,
|
|
3329
|
+
reason: result.exitCode === 0 ? "ok" : "script-failed",
|
|
3330
|
+
timedOut: result.timedOut,
|
|
3331
|
+
stdoutTail: result.stdout.trim().slice(-4e3),
|
|
3332
|
+
stderrTail: result.stderr.trim().slice(-4e3)
|
|
3333
|
+
}
|
|
2777
3334
|
};
|
|
2778
3335
|
}
|
|
3336
|
+
function parseLastJsonObject(text) {
|
|
3337
|
+
const trimmed = text.trim();
|
|
3338
|
+
for (let index = trimmed.lastIndexOf("{"); index >= 0; index = trimmed.lastIndexOf("{", index - 1)) {
|
|
3339
|
+
try {
|
|
3340
|
+
const parsed = JSON.parse(trimmed.slice(index));
|
|
3341
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
3342
|
+
} catch {
|
|
3343
|
+
}
|
|
3344
|
+
}
|
|
3345
|
+
return null;
|
|
3346
|
+
}
|
|
2779
3347
|
function resolveRipgrepBinary(env = process.env) {
|
|
2780
3348
|
const fromEnv = asOptionalTrimmedString(env.NOLO_BUNDLED_RG);
|
|
2781
3349
|
if (fromEnv) {
|
|
@@ -2789,185 +3357,13 @@ function resolveRipgrepBinary(env = process.env) {
|
|
|
2789
3357
|
function resolveGrepBinary() {
|
|
2790
3358
|
return resolveExecutableOnPath("grep");
|
|
2791
3359
|
}
|
|
2792
|
-
async function runWorkspaceCommand(args) {
|
|
2793
|
-
const timeoutMs = asOptionalPositiveFiniteNumber(args.timeoutMs);
|
|
2794
|
-
const detached = process.platform !== "win32";
|
|
2795
|
-
const command = [
|
|
2796
|
-
...args.commandPrefix ?? [],
|
|
2797
|
-
...args.command
|
|
2798
|
-
];
|
|
2799
|
-
const proc = spawnChildProcess(command[0] ?? "", command.slice(1), {
|
|
2800
|
-
cwd: resolve(args.workspaceRoot),
|
|
2801
|
-
stdio: [
|
|
2802
|
-
args.stdin === void 0 ? "ignore" : "pipe",
|
|
2803
|
-
"pipe",
|
|
2804
|
-
"pipe"
|
|
2805
|
-
],
|
|
2806
|
-
detached
|
|
2807
|
-
});
|
|
2808
|
-
if (args.stdin !== void 0 && proc.stdin) {
|
|
2809
|
-
proc.stdin.write(args.stdin);
|
|
2810
|
-
proc.stdin.end();
|
|
2811
|
-
}
|
|
2812
|
-
const exitPromise = waitForNodeProcessExit(proc);
|
|
2813
|
-
const cleanupChildOnHostSignal = (signal) => {
|
|
2814
|
-
if (typeof proc.pid === "number") {
|
|
2815
|
-
try {
|
|
2816
|
-
process.kill(-proc.pid, signal);
|
|
2817
|
-
} catch {
|
|
2818
|
-
}
|
|
2819
|
-
}
|
|
2820
|
-
};
|
|
2821
|
-
if (detached) {
|
|
2822
|
-
process.once("SIGHUP", cleanupChildOnHostSignal);
|
|
2823
|
-
process.once("SIGTERM", cleanupChildOnHostSignal);
|
|
2824
|
-
process.once("SIGINT", cleanupChildOnHostSignal);
|
|
2825
|
-
}
|
|
2826
|
-
const detachSignalCleanup = () => {
|
|
2827
|
-
if (!detached) return;
|
|
2828
|
-
process.removeListener("SIGHUP", cleanupChildOnHostSignal);
|
|
2829
|
-
process.removeListener("SIGTERM", cleanupChildOnHostSignal);
|
|
2830
|
-
process.removeListener("SIGINT", cleanupChildOnHostSignal);
|
|
2831
|
-
};
|
|
2832
|
-
exitPromise.then(detachSignalCleanup, detachSignalCleanup);
|
|
2833
|
-
const stdoutPromise = readNodeStream(proc.stdout);
|
|
2834
|
-
const stderrPromise = readNodeStream(proc.stderr);
|
|
2835
|
-
const killChild = (signal) => {
|
|
2836
|
-
if (detached && typeof proc.pid === "number") {
|
|
2837
|
-
try {
|
|
2838
|
-
process.kill(-proc.pid, signal);
|
|
2839
|
-
return;
|
|
2840
|
-
} catch {
|
|
2841
|
-
}
|
|
2842
|
-
}
|
|
2843
|
-
try {
|
|
2844
|
-
proc.kill(signal);
|
|
2845
|
-
} catch {
|
|
2846
|
-
}
|
|
2847
|
-
};
|
|
2848
|
-
let timeout;
|
|
2849
|
-
let detachTimer;
|
|
2850
|
-
const timeoutResult = Symbol("timeout");
|
|
2851
|
-
const abortResult = Symbol("abort");
|
|
2852
|
-
const detachResult = Symbol("detach");
|
|
2853
|
-
let abortListener;
|
|
2854
|
-
const abortPromise = args.abortSignal ? new Promise((resolveAbort) => {
|
|
2855
|
-
if (args.abortSignal.aborted) {
|
|
2856
|
-
resolveAbort(abortResult);
|
|
2857
|
-
return;
|
|
2858
|
-
}
|
|
2859
|
-
abortListener = () => resolveAbort(abortResult);
|
|
2860
|
-
args.abortSignal.addEventListener("abort", abortListener, { once: true });
|
|
2861
|
-
}) : null;
|
|
2862
|
-
const effectiveDetachMs = resolveDetachMs(args.detachMs);
|
|
2863
|
-
const detachPromise = new Promise((r) => {
|
|
2864
|
-
detachTimer = setTimeout(() => r(detachResult), effectiveDetachMs);
|
|
2865
|
-
});
|
|
2866
|
-
const racers = [exitPromise];
|
|
2867
|
-
if (timeoutMs) {
|
|
2868
|
-
racers.push(
|
|
2869
|
-
new Promise((resolveTimeout) => {
|
|
2870
|
-
timeout = setTimeout(() => resolveTimeout(timeoutResult), timeoutMs);
|
|
2871
|
-
})
|
|
2872
|
-
);
|
|
2873
|
-
}
|
|
2874
|
-
if (abortPromise) racers.push(abortPromise);
|
|
2875
|
-
racers.push(detachPromise);
|
|
2876
|
-
let raceWinner;
|
|
2877
|
-
try {
|
|
2878
|
-
raceWinner = await Promise.race(racers);
|
|
2879
|
-
} catch (error) {
|
|
2880
|
-
if (timeout) clearTimeout(timeout);
|
|
2881
|
-
if (detachTimer) clearTimeout(detachTimer);
|
|
2882
|
-
if (abortListener && args.abortSignal) {
|
|
2883
|
-
args.abortSignal.removeEventListener("abort", abortListener);
|
|
2884
|
-
}
|
|
2885
|
-
detachSignalCleanup();
|
|
2886
|
-
if (isSpawnFailureError(error)) {
|
|
2887
|
-
return spawnFailedCommandResult(error, args.outputLimit);
|
|
2888
|
-
}
|
|
2889
|
-
throw error;
|
|
2890
|
-
}
|
|
2891
|
-
if (timeout) clearTimeout(timeout);
|
|
2892
|
-
if (detachTimer) clearTimeout(detachTimer);
|
|
2893
|
-
if (abortListener && args.abortSignal) {
|
|
2894
|
-
args.abortSignal.removeEventListener("abort", abortListener);
|
|
2895
|
-
}
|
|
2896
|
-
const timedOut = raceWinner === timeoutResult;
|
|
2897
|
-
const aborted = raceWinner === abortResult;
|
|
2898
|
-
const didDetach = raceWinner === detachResult;
|
|
2899
|
-
if (timedOut || aborted) {
|
|
2900
|
-
killChild("SIGTERM");
|
|
2901
|
-
await Promise.race([
|
|
2902
|
-
exitPromise.catch(() => 124),
|
|
2903
|
-
new Promise((resolveKill) => setTimeout(resolveKill, 500))
|
|
2904
|
-
]);
|
|
2905
|
-
killChild("SIGKILL");
|
|
2906
|
-
}
|
|
2907
|
-
if (didDetach) {
|
|
2908
|
-
exitPromise.catch(() => {
|
|
2909
|
-
});
|
|
2910
|
-
const pid = typeof proc.pid === "number" ? proc.pid : 0;
|
|
2911
|
-
const label = deriveLabel(args.command.join(" "));
|
|
2912
|
-
const pgid = pid;
|
|
2913
|
-
const registry2 = getProcessRegistry();
|
|
2914
|
-
registry2.add({ pid, pgid, command: args.command.join(" "), label });
|
|
2915
|
-
proc.on("close", (code) => {
|
|
2916
|
-
detachSignalCleanup();
|
|
2917
|
-
registry2.markExited(pid, code ?? 1);
|
|
2918
|
-
});
|
|
2919
|
-
const immediate = effectiveDetachMs === 0;
|
|
2920
|
-
const reason = immediate ? `command looks long-running; moved to background immediately (pid=${pid}, label=${label})` : `command detached to background after ${effectiveDetachMs}ms (pid=${pid}, label=${label})`;
|
|
2921
|
-
return {
|
|
2922
|
-
stdout: "",
|
|
2923
|
-
stderr: `${reason}
|
|
2924
|
-
Use listProcesses to inspect or stop it.`,
|
|
2925
|
-
exitCode: 0,
|
|
2926
|
-
timedOut: false,
|
|
2927
|
-
detached: true,
|
|
2928
|
-
pid,
|
|
2929
|
-
label,
|
|
2930
|
-
content: JSON.stringify({
|
|
2931
|
-
detached: true,
|
|
2932
|
-
pid,
|
|
2933
|
-
label,
|
|
2934
|
-
status: "running",
|
|
2935
|
-
...immediate ? { reason: "long-running-command" } : {}
|
|
2936
|
-
})
|
|
2937
|
-
};
|
|
2938
|
-
}
|
|
2939
|
-
const [stdout, rawStderr] = await Promise.all([
|
|
2940
|
-
stdoutPromise,
|
|
2941
|
-
stderrPromise
|
|
2942
|
-
]);
|
|
2943
|
-
const exitCode = aborted ? 130 : timedOut ? 124 : Number(raceWinner);
|
|
2944
|
-
const stderr = aborted ? `${rawStderr.trim() ? `${rawStderr.trim()}
|
|
2945
|
-
` : ""}command aborted by signal
|
|
2946
|
-
` : timedOut ? `${rawStderr.trim() ? `${rawStderr.trim()}
|
|
2947
|
-
` : ""}command timed out after ${timeoutMs ?? "unknown"}ms
|
|
2948
|
-
` : rawStderr;
|
|
2949
|
-
return {
|
|
2950
|
-
stdout,
|
|
2951
|
-
stderr,
|
|
2952
|
-
exitCode,
|
|
2953
|
-
timedOut,
|
|
2954
|
-
aborted,
|
|
2955
|
-
content: truncateToolOutput([
|
|
2956
|
-
stdout.trim() ? `stdout:
|
|
2957
|
-
${stdout.trim()}` : "",
|
|
2958
|
-
stderr.trim() ? `stderr:
|
|
2959
|
-
${stderr.trim()}` : "",
|
|
2960
|
-
`exitCode: ${exitCode}`
|
|
2961
|
-
].filter(Boolean).join("\n\n"), args.outputLimit)
|
|
2962
|
-
};
|
|
2963
|
-
}
|
|
2964
3360
|
async function runWorkspaceCommandLimitedLines(args) {
|
|
2965
3361
|
const command = [
|
|
2966
3362
|
...args.commandPrefix ?? [],
|
|
2967
3363
|
...args.command
|
|
2968
3364
|
];
|
|
2969
|
-
const proc =
|
|
2970
|
-
cwd:
|
|
3365
|
+
const proc = spawnChildProcess2(command[0] ?? "", command.slice(1), {
|
|
3366
|
+
cwd: resolve2(args.workspaceRoot),
|
|
2971
3367
|
stdio: ["ignore", "pipe", "pipe"],
|
|
2972
3368
|
detached: false
|
|
2973
3369
|
});
|
|
@@ -3058,8 +3454,8 @@ async function runWorkspaceCommandLimitedLines(args) {
|
|
|
3058
3454
|
};
|
|
3059
3455
|
}
|
|
3060
3456
|
async function resolveLocalWorkspaceToolPath(args) {
|
|
3061
|
-
const workspaceRoot =
|
|
3062
|
-
const targetPath =
|
|
3457
|
+
const workspaceRoot = resolve2(args.workspaceRoot);
|
|
3458
|
+
const targetPath = resolve2(workspaceRoot, args.requestedPath);
|
|
3063
3459
|
if (!isPathInsideWorkspace({ workspaceRoot, targetPath })) {
|
|
3064
3460
|
if (args.confirmExternalFileAccess) {
|
|
3065
3461
|
const request = {
|
|
@@ -3151,7 +3547,7 @@ async function readFileTool(args) {
|
|
|
3151
3547
|
});
|
|
3152
3548
|
const isTruncated = sliced.truncated || truncatedRead.truncatedByCharLimit;
|
|
3153
3549
|
const relativePath = normalizeWorkspaceRelativePath({
|
|
3154
|
-
workspaceRoot:
|
|
3550
|
+
workspaceRoot: resolve2(args.workspaceRoot),
|
|
3155
3551
|
targetPath: absolutePath
|
|
3156
3552
|
});
|
|
3157
3553
|
const force = parsed.force === true;
|
|
@@ -3218,7 +3614,7 @@ async function writeFileTool(args) {
|
|
|
3218
3614
|
await mkdir(dirname(absolutePath), { recursive: true });
|
|
3219
3615
|
await writeFile(absolutePath, content, "utf8");
|
|
3220
3616
|
const relativePath = normalizeWorkspaceRelativePath({
|
|
3221
|
-
workspaceRoot:
|
|
3617
|
+
workspaceRoot: resolve2(args.workspaceRoot),
|
|
3222
3618
|
targetPath: absolutePath
|
|
3223
3619
|
});
|
|
3224
3620
|
const activity = extractActivity(parsed);
|
|
@@ -3274,7 +3670,7 @@ async function editFileTool(args) {
|
|
|
3274
3670
|
}
|
|
3275
3671
|
await writeFile(absolutePath, nextContent, "utf8");
|
|
3276
3672
|
const relativePath = normalizeWorkspaceRelativePath({
|
|
3277
|
-
workspaceRoot:
|
|
3673
|
+
workspaceRoot: resolve2(args.workspaceRoot),
|
|
3278
3674
|
targetPath: absolutePath
|
|
3279
3675
|
});
|
|
3280
3676
|
const activity = extractActivity(parsed);
|
|
@@ -3300,11 +3696,11 @@ async function listWorkspaceEntries(args) {
|
|
|
3300
3696
|
const names = (await readdir(dirPath)).sort((left, right) => left.localeCompare(right));
|
|
3301
3697
|
for (const name of names) {
|
|
3302
3698
|
if (truncated) break;
|
|
3303
|
-
const absolutePath =
|
|
3699
|
+
const absolutePath = resolve2(dirPath, name);
|
|
3304
3700
|
const info = await stat(absolutePath);
|
|
3305
3701
|
visitedEntries += 1;
|
|
3306
3702
|
const relativePath = normalizeWorkspaceRelativePath({
|
|
3307
|
-
workspaceRoot:
|
|
3703
|
+
workspaceRoot: resolve2(args.workspaceRoot),
|
|
3308
3704
|
targetPath: absolutePath
|
|
3309
3705
|
});
|
|
3310
3706
|
const isDirectory = info.isDirectory();
|
|
@@ -3383,7 +3779,7 @@ async function searchFilesTool(args) {
|
|
|
3383
3779
|
...args.confirmExternalFileAccess ? { confirmExternalFileAccess: args.confirmExternalFileAccess } : {}
|
|
3384
3780
|
});
|
|
3385
3781
|
const relativeSearchPath = normalizeWorkspaceRelativePath({
|
|
3386
|
-
workspaceRoot:
|
|
3782
|
+
workspaceRoot: resolve2(args.workspaceRoot),
|
|
3387
3783
|
targetPath: searchPath
|
|
3388
3784
|
});
|
|
3389
3785
|
const rgBinary = (args.resolveRipgrepBinary ?? resolveRipgrepBinary)();
|
|
@@ -3537,7 +3933,7 @@ async function searchFilesTool(args) {
|
|
|
3537
3933
|
}
|
|
3538
3934
|
async function hasRootGitignore(workspaceRoot) {
|
|
3539
3935
|
try {
|
|
3540
|
-
await stat(
|
|
3936
|
+
await stat(resolve2(workspaceRoot, ".gitignore"));
|
|
3541
3937
|
return true;
|
|
3542
3938
|
} catch {
|
|
3543
3939
|
return false;
|
|
@@ -3545,7 +3941,7 @@ async function hasRootGitignore(workspaceRoot) {
|
|
|
3545
3941
|
}
|
|
3546
3942
|
async function readRootGitignorePatterns(workspaceRoot) {
|
|
3547
3943
|
try {
|
|
3548
|
-
const content = await readFile(
|
|
3944
|
+
const content = await readFile(resolve2(workspaceRoot, ".gitignore"), "utf8");
|
|
3549
3945
|
return content.split(/\r?\n/).map((line) => line.trim()).filter((line) => line && !line.startsWith("#") && !line.startsWith("!")).map((line) => {
|
|
3550
3946
|
const unanchored = line.replace(/^\//, "");
|
|
3551
3947
|
return unanchored.endsWith("/") ? `${unanchored}**` : unanchored;
|
|
@@ -3614,7 +4010,7 @@ async function scanWorkspaceTextMatches(args) {
|
|
|
3614
4010
|
for (const file of files) {
|
|
3615
4011
|
let content = "";
|
|
3616
4012
|
try {
|
|
3617
|
-
content = await readFile(
|
|
4013
|
+
content = await readFile(resolve2(args.workspaceRoot, file), "utf8");
|
|
3618
4014
|
} catch {
|
|
3619
4015
|
continue;
|
|
3620
4016
|
}
|
|
@@ -3659,7 +4055,7 @@ async function globFilesTool(args) {
|
|
|
3659
4055
|
...args.confirmExternalFileAccess ? { confirmExternalFileAccess: args.confirmExternalFileAccess } : {}
|
|
3660
4056
|
});
|
|
3661
4057
|
const relativeSearchPath = normalizeWorkspaceRelativePath({
|
|
3662
|
-
workspaceRoot:
|
|
4058
|
+
workspaceRoot: resolve2(args.workspaceRoot),
|
|
3663
4059
|
targetPath: searchPath
|
|
3664
4060
|
});
|
|
3665
4061
|
const pathPrefix = relativeSearchPath === "." ? "" : `${relativeSearchPath.replace(/\/+$/, "")}/`;
|
|
@@ -3819,63 +4215,24 @@ async function captureVisualStateTool(args) {
|
|
|
3819
4215
|
};
|
|
3820
4216
|
}
|
|
3821
4217
|
async function execShellTool(args) {
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
...activity2 ? { activity: activity2 } : {}
|
|
3839
|
-
}
|
|
3840
|
-
};
|
|
3841
|
-
}
|
|
3842
|
-
const result = await runWorkspaceCommand({
|
|
3843
|
-
workspaceRoot: args.workspaceRoot,
|
|
3844
|
-
command: buildWorkspaceShellCommand({
|
|
3845
|
-
toolName: args.call.name,
|
|
3846
|
-
command,
|
|
3847
|
-
shell: parsed.shell
|
|
3848
|
-
}),
|
|
3849
|
-
timeoutMs: resolveExecShellTimeoutMs(args.commandTimeoutMs),
|
|
3850
|
-
outputLimit: args.commandOutputLimit,
|
|
3851
|
-
commandPrefix: args.commandPrefix,
|
|
3852
|
-
abortSignal: args.abortSignal,
|
|
3853
|
-
// Commands that clearly never exit (long sleeps, tail -f, watch, dev
|
|
3854
|
-
// servers, infinite loops) are promoted to background immediately so the
|
|
3855
|
-
// turn — and the user's conversation — keeps going. Everything else keeps
|
|
3856
|
-
// the caller/env detach grace window.
|
|
3857
|
-
detachMs: isImmediateDetachShellCommand({ command }) ? 0 : args.detachMs
|
|
3858
|
-
});
|
|
3859
|
-
const activity = extractActivity(parsed);
|
|
3860
|
-
return {
|
|
3861
|
-
content: result.content,
|
|
3862
|
-
metadata: {
|
|
3863
|
-
command,
|
|
3864
|
-
exitCode: result.exitCode,
|
|
3865
|
-
timedOut: result.timedOut,
|
|
3866
|
-
...result.aborted ? { aborted: true } : {},
|
|
3867
|
-
...result.detached ? { detached: true, pid: result.pid, label: result.label, status: "running" } : {},
|
|
3868
|
-
...activity ? { activity } : {}
|
|
4218
|
+
return invokeCapability(
|
|
4219
|
+
"execShell",
|
|
4220
|
+
args.call.arguments,
|
|
4221
|
+
{
|
|
4222
|
+
workspaceRoot: args.workspaceRoot,
|
|
4223
|
+
commandTimeoutMs: args.commandTimeoutMs,
|
|
4224
|
+
commandOutputLimit: args.commandOutputLimit,
|
|
4225
|
+
commandPrefix: args.commandPrefix,
|
|
4226
|
+
restrictToWorkspace: args.restrictToWorkspace,
|
|
4227
|
+
abortSignal: args.abortSignal,
|
|
4228
|
+
detachMs: args.detachMs,
|
|
4229
|
+
confirmed: args.confirmed,
|
|
4230
|
+
enableDestructiveShellGuard: args.enableDestructiveShellGuard,
|
|
4231
|
+
confirmDestructiveAction: args.confirmDestructiveAction,
|
|
4232
|
+
blockDestructiveWithoutConfirmation: args.blockDestructiveWithoutConfirmation,
|
|
4233
|
+
onInvoke: args.onInvoke
|
|
3869
4234
|
}
|
|
3870
|
-
|
|
3871
|
-
}
|
|
3872
|
-
function deriveLabel(command) {
|
|
3873
|
-
const trimmed = command.trim();
|
|
3874
|
-
if (!trimmed) return "process";
|
|
3875
|
-
const tokens = trimmed.split(/\s+/);
|
|
3876
|
-
const lastToken = tokens[tokens.length - 1] ?? trimmed;
|
|
3877
|
-
const labelCandidate = lastToken.split(/[/\\]/).pop() || lastToken;
|
|
3878
|
-
return labelCandidate || trimmed;
|
|
4235
|
+
);
|
|
3879
4236
|
}
|
|
3880
4237
|
async function launchProcessTool(args) {
|
|
3881
4238
|
const parsed = parseWorkspaceToolArguments(args.call.arguments);
|
|
@@ -3886,7 +4243,7 @@ async function launchProcessTool(args) {
|
|
|
3886
4243
|
return buildWorkspaceShellEscapeBlockedResult({ command, token: escapeToken });
|
|
3887
4244
|
}
|
|
3888
4245
|
}
|
|
3889
|
-
const labelArg =
|
|
4246
|
+
const labelArg = readTrimmedString2(parsed.label);
|
|
3890
4247
|
const label = labelArg || deriveLabel(command);
|
|
3891
4248
|
const persist = readBoolean(parsed, "persist");
|
|
3892
4249
|
const fullCommand = buildWorkspaceShellCommand({
|
|
@@ -3899,8 +4256,8 @@ async function launchProcessTool(args) {
|
|
|
3899
4256
|
...fullCommand
|
|
3900
4257
|
];
|
|
3901
4258
|
const detached = process.platform !== "win32";
|
|
3902
|
-
const proc =
|
|
3903
|
-
cwd:
|
|
4259
|
+
const proc = spawnChildProcess2(cmdArgs[0] ?? "", cmdArgs.slice(1), {
|
|
4260
|
+
cwd: resolve2(args.workspaceRoot),
|
|
3904
4261
|
stdio: ["ignore", "pipe", "pipe"],
|
|
3905
4262
|
detached
|
|
3906
4263
|
});
|
|
@@ -4015,7 +4372,12 @@ function createLocalWorkspaceToolExecutors(args) {
|
|
|
4015
4372
|
commandPrefix: args.commandPrefix,
|
|
4016
4373
|
restrictToWorkspace: args.restrictShellToWorkspace,
|
|
4017
4374
|
abortSignal: opts?.abortSignal ?? args.abortSignal,
|
|
4018
|
-
detachMs: opts?.detachMs ?? args.detachMs
|
|
4375
|
+
detachMs: opts?.detachMs ?? args.detachMs,
|
|
4376
|
+
confirmed: opts?.confirmed,
|
|
4377
|
+
enableDestructiveShellGuard: opts?.enableDestructiveShellGuard,
|
|
4378
|
+
confirmDestructiveAction: opts?.confirmDestructiveAction,
|
|
4379
|
+
blockDestructiveWithoutConfirmation: opts?.blockDestructiveWithoutConfirmation,
|
|
4380
|
+
onInvoke: opts?.onInvoke
|
|
4019
4381
|
}),
|
|
4020
4382
|
launchProcess: (call) => launchProcessTool({
|
|
4021
4383
|
call,
|
|
@@ -4029,6 +4391,23 @@ function createLocalWorkspaceToolExecutors(args) {
|
|
|
4029
4391
|
};
|
|
4030
4392
|
}
|
|
4031
4393
|
|
|
4394
|
+
// packages/cli/client/localDialogRecords.ts
|
|
4395
|
+
var localDialogMessageRecordToRuntimeMessage = dialogMessageRecordToAgentRuntimeMessage;
|
|
4396
|
+
function buildLocalDialogWritePlan(args) {
|
|
4397
|
+
return buildAgentRuntimeDialogWritePlan({
|
|
4398
|
+
input: args.input,
|
|
4399
|
+
userId: args.userId,
|
|
4400
|
+
now: args.now,
|
|
4401
|
+
createId: args.createId,
|
|
4402
|
+
existingDialog: args.existingDialog,
|
|
4403
|
+
runtimeHost: "cli",
|
|
4404
|
+
runtimeMetadata: {
|
|
4405
|
+
...args.cwd ? { worktreePath: args.cwd } : {}
|
|
4406
|
+
},
|
|
4407
|
+
...args.titleOverride ? { titleOverride: args.titleOverride } : {}
|
|
4408
|
+
});
|
|
4409
|
+
}
|
|
4410
|
+
|
|
4032
4411
|
// packages/agent-runtime/noloWorkspaceTools.node.ts
|
|
4033
4412
|
async function readNoloProcessStream(readable) {
|
|
4034
4413
|
if (!readable) return "";
|
|
@@ -4065,10 +4444,67 @@ async function runNoloWorkspaceCliTool(call, args) {
|
|
|
4065
4444
|
}
|
|
4066
4445
|
};
|
|
4067
4446
|
}
|
|
4447
|
+
async function readDialogInProcess(call, store, env) {
|
|
4448
|
+
const args = parseNoloWorkspaceToolArguments(call.arguments);
|
|
4449
|
+
const dialogInput = noloStringArg(args.dialog ?? args.dialogId ?? args.id);
|
|
4450
|
+
if (!dialogInput) throw new Error("readDialog requires dialog.");
|
|
4451
|
+
const mode = noloStringArg(args.mode);
|
|
4452
|
+
if (mode && mode !== "full" && mode !== "status") {
|
|
4453
|
+
throw new Error(`readDialog mode must be "full" or "status", got "${mode}".`);
|
|
4454
|
+
}
|
|
4455
|
+
const userId = noloStringArg(args.user ?? args.userId ?? args.owner) || env?.NOLO_LOCAL_USER_ID || env?.NOLO_USER_ID || "";
|
|
4456
|
+
const { dbKey: dialogKey, dialogId } = resolveNoloDialogInput(dialogInput, userId);
|
|
4457
|
+
const meta = await store.read(dialogKey, { remote: false });
|
|
4458
|
+
if (mode === "status") {
|
|
4459
|
+
const result2 = {
|
|
4460
|
+
dialogId,
|
|
4461
|
+
dialogKey,
|
|
4462
|
+
meta,
|
|
4463
|
+
status: meta?.status ?? null,
|
|
4464
|
+
runtimeCheckpoint: meta?.runtimeCheckpoint ?? null,
|
|
4465
|
+
source: "in-process"
|
|
4466
|
+
};
|
|
4467
|
+
return {
|
|
4468
|
+
content: JSON.stringify(result2, null, 2),
|
|
4469
|
+
metadata: { noloWorkspaceTool: true, readDialog: true, inProcess: true, mode: "status" }
|
|
4470
|
+
};
|
|
4471
|
+
}
|
|
4472
|
+
const limitNum = noloPositiveIntegerString(args.limit);
|
|
4473
|
+
const limit = limitNum ? Number(limitNum) : void 0;
|
|
4474
|
+
const rawMessages = [];
|
|
4475
|
+
if (store.iterator) {
|
|
4476
|
+
const { start, end } = dialogMessageRange(dialogId);
|
|
4477
|
+
for await (const [, value] of store.iterator({ gte: start, lte: end })) {
|
|
4478
|
+
const message = localDialogMessageRecordToRuntimeMessage(value);
|
|
4479
|
+
if (message) {
|
|
4480
|
+
rawMessages.push(message);
|
|
4481
|
+
} else if (value) {
|
|
4482
|
+
rawMessages.push(value);
|
|
4483
|
+
}
|
|
4484
|
+
}
|
|
4485
|
+
}
|
|
4486
|
+
const messages = limit !== void 0 && rawMessages.length > limit ? rawMessages.slice(rawMessages.length - limit) : rawMessages;
|
|
4487
|
+
const result = {
|
|
4488
|
+
dialogId,
|
|
4489
|
+
dialogKey,
|
|
4490
|
+
meta,
|
|
4491
|
+
messages,
|
|
4492
|
+
messagesCount: messages.length,
|
|
4493
|
+
source: "in-process"
|
|
4494
|
+
};
|
|
4495
|
+
return {
|
|
4496
|
+
content: JSON.stringify(result, null, 2),
|
|
4497
|
+
metadata: { noloWorkspaceTool: true, readDialog: true, inProcess: true }
|
|
4498
|
+
};
|
|
4499
|
+
}
|
|
4068
4500
|
function buildNoloWorkspaceCliToolExecutors(args) {
|
|
4069
4501
|
const executors = {};
|
|
4070
4502
|
for (const toolName of NOLO_WORKSPACE_TOOL_NAMES) {
|
|
4071
4503
|
if (toolName === "loadSkill") continue;
|
|
4504
|
+
if (toolName === "readDialog" && args.store) {
|
|
4505
|
+
executors[toolName] = (call) => readDialogInProcess(call, args.store, args.env);
|
|
4506
|
+
continue;
|
|
4507
|
+
}
|
|
4072
4508
|
executors[toolName] = (call) => runNoloWorkspaceCliTool(call, args);
|
|
4073
4509
|
}
|
|
4074
4510
|
return executors;
|
|
@@ -4086,10 +4522,10 @@ function buildLoadSkillExecutor(args) {
|
|
|
4086
4522
|
metadata: { loadSkill: true, resolved: false }
|
|
4087
4523
|
};
|
|
4088
4524
|
}
|
|
4089
|
-
const { resolveSkillByName } = await import("./skillDiscovery-
|
|
4525
|
+
const { resolveSkillByName } = await import("./skillDiscovery-KLLKXPUO.js");
|
|
4090
4526
|
const resolved = resolveSkillByName(args.cwd, name);
|
|
4091
4527
|
if (!resolved) {
|
|
4092
|
-
const { resolveCodingBuiltinSlug, buildCodingSkillContentBySlug } = await import("./codingSkills-
|
|
4528
|
+
const { resolveCodingBuiltinSlug, buildCodingSkillContentBySlug } = await import("./codingSkills-DY5MINPG.js");
|
|
4093
4529
|
const builtinSlug = resolveCodingBuiltinSlug(name);
|
|
4094
4530
|
if (builtinSlug) {
|
|
4095
4531
|
const body = buildCodingSkillContentBySlug(builtinSlug);
|
|
@@ -4124,7 +4560,7 @@ ${body}`,
|
|
|
4124
4560
|
};
|
|
4125
4561
|
}
|
|
4126
4562
|
async function formatUnknownSkillMessage(cwd, requestedName) {
|
|
4127
|
-
const { discoverSkills } = await import("./skillDiscovery-
|
|
4563
|
+
const { discoverSkills } = await import("./skillDiscovery-KLLKXPUO.js");
|
|
4128
4564
|
let available = [];
|
|
4129
4565
|
try {
|
|
4130
4566
|
available = discoverSkills(cwd).map((s) => s.name);
|
|
@@ -4144,6 +4580,8 @@ export {
|
|
|
4144
4580
|
isAgentCoolingDown,
|
|
4145
4581
|
parseSseDataLineJson,
|
|
4146
4582
|
parseSseDataLineObject,
|
|
4583
|
+
codexStreamFailureStatus,
|
|
4584
|
+
aggregateResponsesStream,
|
|
4147
4585
|
buildOpenAiCompatibleChatCompletionRequest,
|
|
4148
4586
|
parseOpenAiCompatibleChatCompletionResponse,
|
|
4149
4587
|
readOpenAiCompatibleSseCompletion,
|
|
@@ -4165,6 +4603,8 @@ export {
|
|
|
4165
4603
|
buildLocalWorkspacePolicyToolNames,
|
|
4166
4604
|
buildLocalWorkspaceOpenAiTools,
|
|
4167
4605
|
createLocalWorkspaceToolExecutors,
|
|
4606
|
+
localDialogMessageRecordToRuntimeMessage,
|
|
4607
|
+
buildLocalDialogWritePlan,
|
|
4168
4608
|
buildNoloWorkspaceCliToolExecutors,
|
|
4169
4609
|
buildLoadSkillExecutor
|
|
4170
4610
|
};
|