nolo-cli 0.11.0 → 0.12.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/{_executeModel-ZEDPX6OF.js → _executeModel-4F7VZR5U.js} +52 -50
- package/{action-AA4IJ5K2.js → action-OTOWUMPW.js} +8 -9
- package/{addContentAction-QYVO4SYP.js → addContentAction-JGBUVR7K.js} +7 -9
- package/{addDialogAgentAction-WVVKT5WQ.js → addDialogAgentAction-V6CL2UCF.js} +9 -10
- package/{agentDeleteCommand-RDPGYIWF.js → agentDeleteCommand-MMRSG6AB.js} +39 -28
- package/{agentEmailCommands-2HJJKS5F.js → agentEmailCommands-YFVKOGM7.js} +65 -62
- package/{agentListCommands-HFEVV3ZW.js → agentListCommands-2PZHIQ3I.js} +128 -31
- package/{agentMachineCommands-25ZOENJU.js → agentMachineCommands-55UUZNHF.js} +38 -26
- package/{agentPullCommand-GOL2S4W2.js → agentPullCommand-KRBRIFIY.js} +7 -7
- package/{agentRecordCommands-GTCOAM3L.js → agentRecordCommands-BTP3OAPL.js} +57 -54
- package/{agentRunCommand-YBS3JDLD.js → agentRunCommand-AA4QLRQZ.js} +60 -59
- package/{agentRunControl-K4VKNEHF.js → agentRunControl-IX4F5W2C.js} +3 -3
- package/{agentRuntimeLocal-UQBMQI2M.js → agentRuntimeLocal-QDJCBXM2.js} +31 -15
- package/{agentSlice-6EKOBCCS.js → agentSlice-6JAJOU3X.js} +8 -9
- package/{authCommand-O2OWACN4.js → authCommand-DMW336GT.js} +15 -15
- package/{authCommands-H7365TZP.js → authCommands-GH5T6RYV.js} +6 -6
- package/{chunk-E4HNJSHC.js → chunk-2BVGERJ2.js} +81 -19
- package/{chunk-LP65VRCI.js → chunk-3MI5LDDU.js} +6 -6
- package/{chunk-OPZYGHUC.js → chunk-3WGLLTK4.js} +59 -1
- package/{chunk-KJR5KAUQ.js → chunk-3XF6LCCN.js} +20 -7
- package/{chunk-PXZAAB4L.js → chunk-4LG74DRG.js} +4 -4
- package/{chunk-2FTTDCD4.js → chunk-4ON5AV72.js} +2 -1
- package/{chunk-IWX4UW2E.js → chunk-524BDL33.js} +89 -8
- package/{chunk-ISNCLGUZ.js → chunk-5NBHQD4D.js} +4 -3
- package/{chunk-ZPRK73XY.js → chunk-5S4SWV5G.js} +51 -38
- package/{chunk-EVZKZF32.js → chunk-5V524MHX.js} +195 -78
- package/{chunk-CKOJQDKR.js → chunk-7JJCGV7N.js} +146 -3
- package/{chunk-NEM67FX7.js → chunk-ARSC56DC.js} +1 -1
- package/{chunk-AFON4QUU.js → chunk-AS3ZQ3O2.js} +19 -28
- package/{chunk-4SOKXIW4.js → chunk-B4KT7RN2.js} +10 -13
- package/chunk-BYXBJQAS.js +0 -0
- package/{chunk-PXQN7IBQ.js → chunk-C3BFP57W.js} +95 -2
- package/{chunk-AEZAORLG.js → chunk-CGDQDXKZ.js} +5 -5
- package/{chunk-MQI77IRB.js → chunk-DPFWKRHG.js} +2 -2
- package/{chunk-TACHTH5D.js → chunk-EIMX5ER5.js} +18 -18
- package/{chunk-5MOUAAMK.js → chunk-FSUZIGIG.js} +197 -2578
- package/{chunk-FXXSGWAJ.js → chunk-H5Y2H2N6.js} +1 -1
- package/chunk-IKXL2HZ3.js +70 -0
- package/{chunk-QNMFBBZK.js → chunk-IVAOVLM7.js} +1 -1
- package/chunk-JFANOO7I.js +2274 -0
- package/{chunk-YPRZSPF3.js → chunk-JKWWVF2I.js} +1 -1
- package/{chunk-3NSIQZZ6.js → chunk-JU7J7EAV.js} +45 -45
- package/{chunk-X2S7JTIG.js → chunk-KWBH4RAL.js} +1280 -137
- package/{chunk-OUI3ZHYD.js → chunk-LUKQQJFL.js} +39 -2
- package/{chunk-BX6U3ETE.js → chunk-MG6BVV3T.js} +320 -102
- package/{chunk-L53FGHA4.js → chunk-MQRZ5QUH.js} +1 -1
- package/{chunk-2W2VYVI5.js → chunk-MZHSAPZP.js} +675 -177
- package/{chunk-YOCHZD7D.js → chunk-NCGFB7NN.js} +5 -3
- package/{chunk-DWGAEFJL.js → chunk-O4FOPRCP.js} +1 -1
- package/chunk-OGGHETUE.js +4900 -0
- package/{chunk-PNEW2Z3T.js → chunk-OHEKYYJF.js} +7 -7
- package/chunk-OOIN7HSO.js +146 -0
- package/{chunk-SV5MWASH.js → chunk-OPM2DE2J.js} +12 -12
- package/{chunk-5OIFWVX7.js → chunk-QJSTNKJY.js} +1 -1
- package/{chunk-C4FTUXVF.js → chunk-QLUFR47U.js} +11 -1
- package/{chunk-LAX2JSTX.js → chunk-QUHUVG6Y.js} +20 -2
- package/{chunk-D6OQHXVG.js → chunk-RQSOAMPJ.js} +22 -6
- package/{chunk-ZEYDOXHF.js → chunk-RUGEW4P7.js} +1515 -884
- package/{chunk-GBDSZATH.js → chunk-RWO5DGXI.js} +3 -3
- package/{chunk-S3GS2PRL.js → chunk-TA7VKOHU.js} +613 -70
- package/{chunk-XYZA7S7V.js → chunk-TQYYGE6G.js} +6 -6
- package/{chunk-YTBMA7NO.js → chunk-TYPHUVK5.js} +70 -8
- package/{chunk-4ZDM73SD.js → chunk-V2J635L5.js} +1 -1
- package/{chunk-C2MDQQ4O.js → chunk-VBSWHE5W.js} +1 -1
- package/{chunk-O26GI2JP.js → chunk-VKG4HXYV.js} +39 -8
- package/{chunk-IPYUUY4E.js → chunk-WPHL6ALY.js} +1 -1
- package/{chunk-7D3JY6C2.js → chunk-WVTBAALN.js} +64 -5
- package/{chunk-XFOLQLDN.js → chunk-XHQWJUW4.js} +2 -2
- package/{chunk-Q3UKJVU4.js → chunk-Y5P3ZRGC.js} +10 -10
- package/{chunk-KQQVGRA3.js → chunk-YUMYYXPI.js} +53 -183
- package/cliEnvHelpers-VYC2OPTB.js +30 -0
- package/{createAgentAutomationAction-LTI5QU35.js → createAgentAutomationAction-4NY4JLOK.js} +26 -26
- package/{createDialogAction-OJJ7L3L3.js → createDialogAction-I5WUWFJ6.js} +26 -26
- package/{createPageAction-5MXWVRGY.js → createPageAction-HFTIQ7TW.js} +26 -26
- package/{dbSlice-RQFQQVHT.js → dbSlice-J4PUZ6Y5.js} +6 -7
- package/{deleteFile-7XP5BGC7.js → deleteFile-M7UNIUOO.js} +4 -4
- package/{dialogCommands-JTJ3RPPW.js → dialogCommands-SVXRJFZS.js} +34 -23
- package/{dialogSlice-OWROKURJ.js → dialogSlice-35LA3ZRG.js} +26 -26
- package/{docCreateCommands-WJBEW2V7.js → docCreateCommands-Z7RIAYKQ.js} +34 -23
- package/{docDeleteCommands-BVEHJOY5.js → docDeleteCommands-NEYHUVZF.js} +35 -25
- package/{docReadCommands-NEWOZIJV.js → docReadCommands-I4US2SDE.js} +34 -23
- package/{docStore-3DXRYBMD.js → docStore-3CP2IO6M.js} +25 -25
- package/{docUpdateCommands-C7WHT7UU.js → docUpdateCommands-UU5HQBRP.js} +34 -23
- package/fetchMessages-KTNM7QAR.js +1 -1
- package/{getPricing-3E6UNPKL.js → getPricing-UGDJHI7M.js} +7 -3
- package/{handleSendMessageAction-B5PRR7BE.js → handleSendMessageAction-FWTLQGOZ.js} +30 -30
- package/index.js +77 -76
- package/{localDialogRead-YSKFFRTT.js → localDialogRead-2VRI24F4.js} +1 -1
- package/{machineCommands-IIIREAEK.js → machineCommands-STHZKBDJ.js} +64 -61
- package/{machineWsRunDispatch-3SIU6ILW.js → machineWsRunDispatch-7YHT4TPT.js} +56 -53
- package/{memoryCommands-O4MIPIWQ.js → memoryCommands-AFMES3LN.js} +5 -5
- package/{models-4OPOOLFS.js → models-LSCRJJ6P.js} +2 -2
- package/package.json +1 -1
- package/{read-QJNKRROM.js → read-2MIMRBZ2.js} +4 -4
- package/{readDocTool-H6X6KO6X.js → readDocTool-QHVGQHUJ.js} +1 -1
- package/{readlineWorkspace-HGP3I3OC.js → readlineWorkspace-OMRWYMB6.js} +1279 -716
- package/{removeDialogAgentAction-TCT35SOF.js → removeDialogAgentAction-XUZYQBV4.js} +13 -13
- package/{runAgentClientLoop-EFM4PTVI.js → runAgentClientLoop-FP2K33ZO.js} +47 -46
- package/{runtimeDoctorCommands-ID43Z7VX.js → runtimeDoctorCommands-QNF3J3UV.js} +6 -6
- package/{setDialogExtraReferencesAction-CGYLZMFH.js → setDialogExtraReferencesAction-PTAIFKGM.js} +9 -10
- package/{setPrimaryDialogAgentAction-F6BFQDJU.js → setPrimaryDialogAgentAction-HRFTDB44.js} +13 -13
- package/{settingSlice-2I5HMG47.js → settingSlice-VGRJUSE5.js} +7 -8
- package/{skillDiscovery-UGSQGX2U.js → skillDiscovery-6M2BZUFN.js} +3 -3
- package/{spaceCommands-MT7BWCIL.js → spaceCommands-GN73BCOB.js} +38 -29
- package/{spaceSlice-BS5TSW3W.js → spaceSlice-WTFNKOVA.js} +30 -26
- package/{streamAgentChatTurn-I3VBFNR3.js → streamAgentChatTurn-HBAQQVNK.js} +442 -130
- package/{toolApiClient-C7P7YSE4.js → toolApiClient-QI3YBNWB.js} +2 -2
- package/{tools-XSD2BOEN.js → tools-TPQYTLQZ.js} +34 -33
- package/{tuiSummaryLlmCaller-32NRHTT2.js → tuiSummaryLlmCaller-XDC7WG4K.js} +11 -7
- package/{updateCommands-VOSPKXFF.js → updateCommands-XZDOOYFH.js} +13 -5
- package/{updateDialogTitleAction-7FYNJOGP.js → updateDialogTitleAction-AWXNPUQJ.js} +30 -29
- package/chunk-AZPCASRE.js +0 -36
- package/chunk-BBYVOSU2.js +0 -792
- package/chunk-DCOA4QSN.js +0 -105
- package/chunk-UAD2WT54.js +0 -64
- package/{chunk-JK7VW2FN.js → chunk-5MIDPSSK.js} +3 -3
- package/{chunk-5CS33IBL.js → chunk-PDL2AV3E.js} +3 -3
- package/{chunk-5MMZUWSG.js → chunk-TOILAPLH.js} +5 -5
- package/{chunk-OOM6SJNR.js → chunk-TSUJOM6N.js} +6 -6
- package/{db-6QED3ZVO.js → db-HCJ5PRB5.js} +3 -3
- package/{offlineMarxistsAgentCommand-BCPJITJL.js → offlineMarxistsAgentCommand-5OOWZLJI.js} +3 -3
- package/{tableCommands-M3DJUETS.js → tableCommands-E4SQWDOX.js} +6 -6
- package/{understanding-ZOESBLF3.js → understanding-3CUQQ6SV.js} +3 -3
|
@@ -3,55 +3,81 @@ import {
|
|
|
3
3
|
normalizeDialogRecord,
|
|
4
4
|
readDialogSnapshot,
|
|
5
5
|
sortDialogs
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-OPM2DE2J.js";
|
|
7
7
|
import {
|
|
8
|
+
ANSI_ESCAPE_REGEX,
|
|
9
|
+
CLI_AUTO_TIER_AGENT_KEYS,
|
|
8
10
|
CLI_AUTO_TIER_AGENT_KEY_TABLE,
|
|
9
11
|
CLI_IMAGE_AGENT_KEY,
|
|
12
|
+
COLLAPSED_PASTE_PLACEHOLDER_RE,
|
|
10
13
|
THEME_PALETTES,
|
|
14
|
+
allocateCollapsedPaste,
|
|
15
|
+
applyTerminalOutputToText,
|
|
11
16
|
buildSkillContextBlocks,
|
|
12
17
|
classifyCliAutoRoute,
|
|
18
|
+
clearCollapsedPasteStore,
|
|
19
|
+
countPhysicalLines,
|
|
20
|
+
createCollapsedPasteStore,
|
|
13
21
|
dimCliText,
|
|
22
|
+
displayWidth,
|
|
23
|
+
expandRangeToCollapsedPasteChips,
|
|
24
|
+
findCollapsedPasteSpanAt,
|
|
25
|
+
fitAnsiLine,
|
|
14
26
|
formatAssistantDisplay,
|
|
15
27
|
formatTokenCount,
|
|
16
28
|
getActiveBrightness,
|
|
17
29
|
getActiveDensity,
|
|
18
30
|
getActiveThemeName,
|
|
19
|
-
getCliLocale,
|
|
20
|
-
highlightMarkdown,
|
|
21
|
-
initCliLocale,
|
|
22
31
|
isBalanceExhaustedError,
|
|
23
32
|
isQuotaExhaustedError,
|
|
24
33
|
normalizeThinkingDisplayMode,
|
|
25
34
|
normalizeToolDisplayMode,
|
|
26
|
-
|
|
35
|
+
padOrTruncateToWidth,
|
|
36
|
+
releaseCollapsedPaste,
|
|
37
|
+
releaseCollapsedPasteReferences,
|
|
27
38
|
renderTokenStatus,
|
|
39
|
+
replaceCollapsedPastesWithReferences,
|
|
40
|
+
resolveAgentContextWindow,
|
|
41
|
+
resolveCliAutoFallbackTier,
|
|
28
42
|
resolveCliColorEnabled,
|
|
29
43
|
resolveSkillReference,
|
|
30
44
|
resolveTuiBrightness,
|
|
31
45
|
runAgentTurn,
|
|
32
46
|
setActiveBrightness,
|
|
33
47
|
setActiveDensity,
|
|
48
|
+
setActiveTerminalBaseHex,
|
|
34
49
|
setActiveThemeName,
|
|
35
|
-
|
|
50
|
+
shouldCollapsePaste,
|
|
51
|
+
stripAnsi,
|
|
36
52
|
surfaceBackgroundSequence,
|
|
37
|
-
|
|
53
|
+
takeDisplayWidth,
|
|
38
54
|
themeColorSequence,
|
|
39
|
-
themeText
|
|
40
|
-
|
|
55
|
+
themeText,
|
|
56
|
+
truncateAnsi,
|
|
57
|
+
visibleWidth,
|
|
58
|
+
wrapTextToLines,
|
|
59
|
+
wrapTranscriptLine
|
|
60
|
+
} from "./chunk-RUGEW4P7.js";
|
|
61
|
+
import "./chunk-3QAF2LTL.js";
|
|
41
62
|
import "./chunk-6HYVJV7B.js";
|
|
42
63
|
import {
|
|
43
64
|
listFavoriteAgentIdsAcrossServers,
|
|
44
65
|
listLocalCachedAgents,
|
|
45
66
|
listRemoteAgents,
|
|
46
67
|
listRemoteAgentsAcrossServers
|
|
47
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-QUHUVG6Y.js";
|
|
48
69
|
import "./chunk-ID4G2MAR.js";
|
|
49
70
|
import {
|
|
50
71
|
listUserRecordsFromServers
|
|
51
|
-
} from "./chunk-
|
|
52
|
-
import
|
|
53
|
-
|
|
54
|
-
|
|
72
|
+
} from "./chunk-O4FOPRCP.js";
|
|
73
|
+
import {
|
|
74
|
+
createCliLocalRuntimeAdapter,
|
|
75
|
+
initCliLocale,
|
|
76
|
+
parseCliLocale,
|
|
77
|
+
setCliLocale,
|
|
78
|
+
t
|
|
79
|
+
} from "./chunk-KWBH4RAL.js";
|
|
80
|
+
import "./chunk-TOILAPLH.js";
|
|
55
81
|
import "./chunk-CMKUBIGW.js";
|
|
56
82
|
import "./chunk-JBTJAPUF.js";
|
|
57
83
|
import {
|
|
@@ -60,135 +86,150 @@ import {
|
|
|
60
86
|
import {
|
|
61
87
|
queryUserRecords,
|
|
62
88
|
readDbRecord
|
|
63
|
-
} from "./chunk-
|
|
64
|
-
import "./chunk-
|
|
89
|
+
} from "./chunk-CGDQDXKZ.js";
|
|
90
|
+
import "./chunk-VBSWHE5W.js";
|
|
65
91
|
import {
|
|
66
92
|
resolveCliAgentKeyInput
|
|
67
93
|
} from "./chunk-VHBWHNVT.js";
|
|
68
94
|
import "./chunk-LTRXGFMS.js";
|
|
69
|
-
import "./chunk-
|
|
95
|
+
import "./chunk-QJSTNKJY.js";
|
|
70
96
|
import "./chunk-ATNTHIZI.js";
|
|
71
97
|
import "./chunk-JEXVIMYZ.js";
|
|
72
|
-
import
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
import "./chunk-
|
|
98
|
+
import {
|
|
99
|
+
prepareTools
|
|
100
|
+
} from "./chunk-C3BFP57W.js";
|
|
101
|
+
import "./chunk-H5Y2H2N6.js";
|
|
102
|
+
import {
|
|
103
|
+
expandEnabledPacks
|
|
104
|
+
} from "./chunk-MZHSAPZP.js";
|
|
105
|
+
import "./chunk-RQSOAMPJ.js";
|
|
106
|
+
import "./chunk-OOIN7HSO.js";
|
|
76
107
|
import "./chunk-KQHGZWSV.js";
|
|
77
|
-
import "./chunk-
|
|
108
|
+
import "./chunk-IVAOVLM7.js";
|
|
78
109
|
import "./chunk-3LY4F7SV.js";
|
|
79
110
|
import "./chunk-VP4WJWKW.js";
|
|
80
111
|
import "./chunk-63W5N6P3.js";
|
|
81
112
|
import "./chunk-VF4K2QE6.js";
|
|
82
|
-
import "./chunk-
|
|
83
|
-
import "./chunk-
|
|
113
|
+
import "./chunk-TQYYGE6G.js";
|
|
114
|
+
import "./chunk-2BVGERJ2.js";
|
|
84
115
|
import "./chunk-VC6OZXO5.js";
|
|
85
116
|
import "./chunk-OWOHNGCN.js";
|
|
86
117
|
import {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
getModelContextWindow,
|
|
90
|
-
planCompression,
|
|
91
|
-
serializeMessageContent
|
|
92
|
-
} from "./chunk-5MOUAAMK.js";
|
|
118
|
+
extractReferenceKeysFromMessage
|
|
119
|
+
} from "./chunk-FSUZIGIG.js";
|
|
93
120
|
import {
|
|
94
121
|
buildBuiltinSummaryContent
|
|
95
|
-
} from "./chunk-
|
|
122
|
+
} from "./chunk-V2J635L5.js";
|
|
96
123
|
import "./chunk-MRC4Q34M.js";
|
|
97
124
|
import "./chunk-MTU75CZ2.js";
|
|
98
125
|
import "./chunk-CWMSB67P.js";
|
|
99
|
-
import "./chunk-
|
|
100
|
-
import "./chunk-
|
|
126
|
+
import "./chunk-Y5P3ZRGC.js";
|
|
127
|
+
import "./chunk-RWO5DGXI.js";
|
|
101
128
|
import "./chunk-Z4NR32XR.js";
|
|
102
129
|
import "./chunk-M76ECQRD.js";
|
|
103
|
-
import "./chunk-MQI77IRB.js";
|
|
104
|
-
import "./chunk-3NSIQZZ6.js";
|
|
105
|
-
import "./chunk-Y5GCAMGP.js";
|
|
106
|
-
import "./chunk-SOTZ6RDN.js";
|
|
107
|
-
import "./chunk-7D3JY6C2.js";
|
|
108
|
-
import "./chunk-UAD2WT54.js";
|
|
109
|
-
import "./chunk-O26GI2JP.js";
|
|
110
|
-
import "./chunk-AZPCASRE.js";
|
|
111
|
-
import "./chunk-DCOA4QSN.js";
|
|
112
|
-
import "./chunk-HNW7IJSK.js";
|
|
113
|
-
import "./chunk-XENYHRUE.js";
|
|
114
|
-
import "./chunk-IPYUUY4E.js";
|
|
115
|
-
import {
|
|
116
|
-
runSelfUpdate
|
|
117
|
-
} from "./chunk-CKOJQDKR.js";
|
|
118
|
-
import {
|
|
119
|
-
saveProfileAgentSelection,
|
|
120
|
-
saveProfileLocale
|
|
121
|
-
} from "./chunk-ROWQEJRP.js";
|
|
122
|
-
import {
|
|
123
|
-
readPipeText,
|
|
124
|
-
spawnProcess
|
|
125
|
-
} from "./chunk-IDZTNRP2.js";
|
|
126
130
|
import {
|
|
127
131
|
buildSkillDiscoveryContextBlock
|
|
128
|
-
} from "./chunk-
|
|
129
|
-
import "./chunk-C4FTUXVF.js";
|
|
130
|
-
import "./chunk-KKY4B2Q2.js";
|
|
132
|
+
} from "./chunk-AS3ZQ3O2.js";
|
|
131
133
|
import "./chunk-WZSRQNQG.js";
|
|
132
134
|
import {
|
|
135
|
+
buildLocalWorkspaceOpenAiTools,
|
|
136
|
+
buildLocalWorkspaceToolset,
|
|
133
137
|
getProcessRegistry
|
|
134
|
-
} from "./chunk-
|
|
135
|
-
import "./chunk-ZPRK73XY.js";
|
|
138
|
+
} from "./chunk-MG6BVV3T.js";
|
|
136
139
|
import "./chunk-ZNFJWEN4.js";
|
|
137
|
-
import "./chunk-
|
|
140
|
+
import "./chunk-5S4SWV5G.js";
|
|
141
|
+
import "./chunk-NCGFB7NN.js";
|
|
138
142
|
import {
|
|
139
143
|
readCommandActionGatePayload,
|
|
140
144
|
resolveAgentImageInputSupport
|
|
141
|
-
} from "./chunk-
|
|
142
|
-
import "./chunk-
|
|
143
|
-
import
|
|
144
|
-
|
|
145
|
-
|
|
145
|
+
} from "./chunk-TA7VKOHU.js";
|
|
146
|
+
import "./chunk-3WGLLTK4.js";
|
|
147
|
+
import {
|
|
148
|
+
buildCurrentTimeBlock,
|
|
149
|
+
buildIdentityBlock,
|
|
150
|
+
buildRuntimeGuidanceBlocks
|
|
151
|
+
} from "./chunk-LUKQQJFL.js";
|
|
152
|
+
import "./chunk-TYPHUVK5.js";
|
|
153
|
+
import {
|
|
154
|
+
canonicalizeToolNames
|
|
155
|
+
} from "./chunk-B4KT7RN2.js";
|
|
156
|
+
import {
|
|
157
|
+
DEFAULT_CONTEXT_WINDOW,
|
|
158
|
+
estimateTokenCount,
|
|
159
|
+
getModelContextWindow,
|
|
160
|
+
planCompression,
|
|
161
|
+
serializeMessageContent
|
|
162
|
+
} from "./chunk-JFANOO7I.js";
|
|
146
163
|
import {
|
|
147
164
|
clipCompactText
|
|
148
165
|
} from "./chunk-5MMAK75Q.js";
|
|
149
|
-
import "./chunk-
|
|
150
|
-
import "./chunk-
|
|
166
|
+
import "./chunk-OGGHETUE.js";
|
|
167
|
+
import "./chunk-PDL2AV3E.js";
|
|
151
168
|
import "./chunk-JZPBVBIY.js";
|
|
152
|
-
import "./chunk-
|
|
153
|
-
import "./chunk-
|
|
154
|
-
import "./chunk-
|
|
155
|
-
import "./chunk-
|
|
156
|
-
import "./chunk-WWC7I6TA.js";
|
|
157
|
-
import "./chunk-JK7VW2FN.js";
|
|
169
|
+
import "./chunk-BYXBJQAS.js";
|
|
170
|
+
import "./chunk-QLUFR47U.js";
|
|
171
|
+
import "./chunk-KKY4B2Q2.js";
|
|
172
|
+
import "./chunk-5MIDPSSK.js";
|
|
158
173
|
import {
|
|
159
174
|
extractCustomId
|
|
160
175
|
} from "./chunk-DFFIZ25G.js";
|
|
161
176
|
import "./chunk-3ASDTTEI.js";
|
|
177
|
+
import "./chunk-4ON5AV72.js";
|
|
162
178
|
import {
|
|
163
|
-
|
|
164
|
-
} from "./chunk-
|
|
179
|
+
toErrorMessage
|
|
180
|
+
} from "./chunk-HNMXYYJR.js";
|
|
181
|
+
import "./chunk-H33743XH.js";
|
|
182
|
+
import "./chunk-LM6IYQ3F.js";
|
|
183
|
+
import "./chunk-SOTZ6RDN.js";
|
|
184
|
+
import "./chunk-HNW7IJSK.js";
|
|
185
|
+
import "./chunk-XENYHRUE.js";
|
|
165
186
|
import {
|
|
166
187
|
compactWhitespace
|
|
167
188
|
} from "./chunk-DWLEOOGR.js";
|
|
168
|
-
import
|
|
189
|
+
import {
|
|
190
|
+
runSelfUpdate
|
|
191
|
+
} from "./chunk-7JJCGV7N.js";
|
|
192
|
+
import {
|
|
193
|
+
saveProfileAgentSelection,
|
|
194
|
+
saveProfileLocale
|
|
195
|
+
} from "./chunk-ROWQEJRP.js";
|
|
196
|
+
import {
|
|
197
|
+
readPipeText,
|
|
198
|
+
spawnProcess
|
|
199
|
+
} from "./chunk-IDZTNRP2.js";
|
|
200
|
+
import "./chunk-IKXL2HZ3.js";
|
|
201
|
+
import "./chunk-524BDL33.js";
|
|
202
|
+
import "./chunk-DPFWKRHG.js";
|
|
203
|
+
import "./chunk-JU7J7EAV.js";
|
|
204
|
+
import "./chunk-Y5GCAMGP.js";
|
|
205
|
+
import "./chunk-KX6YBIHG.js";
|
|
206
|
+
import "./chunk-WVTBAALN.js";
|
|
207
|
+
import "./chunk-VKG4HXYV.js";
|
|
208
|
+
import "./chunk-WPHL6ALY.js";
|
|
209
|
+
import "./chunk-O4BU24WZ.js";
|
|
210
|
+
import "./chunk-I42VC5KR.js";
|
|
211
|
+
import "./chunk-WWC7I6TA.js";
|
|
212
|
+
import {
|
|
213
|
+
asTrimmedLowercaseString
|
|
214
|
+
} from "./chunk-QSJ3HFEF.js";
|
|
215
|
+
import "./chunk-XHQWJUW4.js";
|
|
216
|
+
import "./chunk-TSUJOM6N.js";
|
|
217
|
+
import "./chunk-OOSFSFMA.js";
|
|
218
|
+
import "./chunk-GBRG75BQ.js";
|
|
219
|
+
import "./chunk-KUG52IAP.js";
|
|
220
|
+
import "./chunk-3EUW3WEO.js";
|
|
221
|
+
import "./chunk-U6VC4MU4.js";
|
|
169
222
|
import {
|
|
170
223
|
parseUserIdFromAuthToken,
|
|
171
224
|
resolveAuthToken,
|
|
172
225
|
resolveServerCandidates,
|
|
173
226
|
resolveServerUrl
|
|
174
|
-
} from "./chunk-
|
|
227
|
+
} from "./chunk-5NBHQD4D.js";
|
|
175
228
|
import {
|
|
176
229
|
DEFAULT_NOLO_SERVER_URL
|
|
177
230
|
} from "./chunk-OLZBQWZU.js";
|
|
178
|
-
import {
|
|
179
|
-
toErrorMessage
|
|
180
|
-
} from "./chunk-HNMXYYJR.js";
|
|
181
|
-
import "./chunk-H33743XH.js";
|
|
182
|
-
import "./chunk-LM6IYQ3F.js";
|
|
183
|
-
import "./chunk-XFOLQLDN.js";
|
|
184
|
-
import "./chunk-OOM6SJNR.js";
|
|
185
|
-
import "./chunk-OOSFSFMA.js";
|
|
186
|
-
import "./chunk-GBRG75BQ.js";
|
|
187
|
-
import "./chunk-3EUW3WEO.js";
|
|
188
|
-
import "./chunk-U6VC4MU4.js";
|
|
189
|
-
import "./chunk-KUG52IAP.js";
|
|
190
|
-
import "./chunk-4SN6J65J.js";
|
|
191
231
|
import "./chunk-LCBAMY2M.js";
|
|
232
|
+
import "./chunk-4SN6J65J.js";
|
|
192
233
|
import "./chunk-CF7PE763.js";
|
|
193
234
|
import {
|
|
194
235
|
asOptionalTrimmedString
|
|
@@ -198,8 +239,81 @@ import "./chunk-4VNS5WPM.js";
|
|
|
198
239
|
// packages/cli/tui/readlineWorkspace.ts
|
|
199
240
|
import { createInterface } from "node:readline";
|
|
200
241
|
import { stdin as defaultInput, stdout as defaultOutput } from "node:process";
|
|
201
|
-
import { existsSync as
|
|
242
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
|
|
243
|
+
import { join as join2 } from "node:path";
|
|
244
|
+
|
|
245
|
+
// packages/cli/client/estimateCliContext.ts
|
|
246
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
202
247
|
import { join } from "node:path";
|
|
248
|
+
var DEFAULT_CLI_PACKS = ["code", "agent-orchestration", "skills"];
|
|
249
|
+
var DEFAULT_CLI_PLATFORM_TOOLS = [
|
|
250
|
+
"ui_ask_choice",
|
|
251
|
+
"exa_search",
|
|
252
|
+
"fetchWebpage"
|
|
253
|
+
];
|
|
254
|
+
var DEFAULT_FLASH_PROMPT = "\u4F60\u662F\u5171\u4EAB\u7A7A\u95F4\u91CC\u7684\u9AD8\u6027\u4EF7\u6BD4\u901A\u7528 AI \u52A9\u624B\u3002\u4F18\u5148\u5FEB\u901F\u3001\u76F4\u63A5\u3001\u7A33\u5B9A\u5730\u5B8C\u6210\u4EFB\u52A1\uFF1B\u9700\u8981\u63A8\u7406\u65F6\u4FDD\u6301\u6B65\u9AA4\u6E05\u6670\u3002";
|
|
255
|
+
function readAgentsMd(cwd) {
|
|
256
|
+
for (const name of ["AGENTS.md", "agents.md"]) {
|
|
257
|
+
const path = join(cwd, name);
|
|
258
|
+
if (!existsSync(path)) continue;
|
|
259
|
+
try {
|
|
260
|
+
return readFileSync(path, "utf8");
|
|
261
|
+
} catch {
|
|
262
|
+
return "";
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return "";
|
|
266
|
+
}
|
|
267
|
+
function estimateToolSchemas(toolNames) {
|
|
268
|
+
const toolset = buildLocalWorkspaceToolset({
|
|
269
|
+
declaredToolNames: toolNames,
|
|
270
|
+
exposeShellTools: true
|
|
271
|
+
});
|
|
272
|
+
const workspaceTools = buildLocalWorkspaceOpenAiTools({
|
|
273
|
+
toolNames: toolset.toolNames,
|
|
274
|
+
exposeShellTools: true
|
|
275
|
+
});
|
|
276
|
+
const platformNames = DEFAULT_CLI_PLATFORM_TOOLS.filter(
|
|
277
|
+
(name) => toolNames.includes(name)
|
|
278
|
+
);
|
|
279
|
+
const platformTools = platformNames.length > 0 ? prepareTools([...platformNames]) : [];
|
|
280
|
+
return estimateTokenCount(JSON.stringify([...workspaceTools, ...platformTools]));
|
|
281
|
+
}
|
|
282
|
+
function estimateDefaultCliContextTokens(opts = {}) {
|
|
283
|
+
const cwd = opts.cwd?.trim() || process.cwd();
|
|
284
|
+
const model = opts.model?.trim() || "deepseek-v4-flash";
|
|
285
|
+
const agentName = opts.agentName?.trim() || "DeepSeek V4 Flash";
|
|
286
|
+
const agentKey = opts.agentKey?.trim() || "agent-pub-deepseek-v4-flash";
|
|
287
|
+
const prompt = opts.agentPrompt?.trim() || DEFAULT_FLASH_PROMPT;
|
|
288
|
+
const packTools = canonicalizeToolNames(
|
|
289
|
+
expandEnabledPacks([...DEFAULT_CLI_PACKS], [])
|
|
290
|
+
);
|
|
291
|
+
const toolNames = canonicalizeToolNames([
|
|
292
|
+
...packTools,
|
|
293
|
+
...DEFAULT_CLI_PLATFORM_TOOLS
|
|
294
|
+
]);
|
|
295
|
+
const guidance = buildRuntimeGuidanceBlocks(toolNames);
|
|
296
|
+
const systemParts = [
|
|
297
|
+
prompt,
|
|
298
|
+
buildIdentityBlock({
|
|
299
|
+
agentName,
|
|
300
|
+
agentId: agentKey,
|
|
301
|
+
model
|
|
302
|
+
}),
|
|
303
|
+
guidance.startupProtocol,
|
|
304
|
+
guidance.contextLayerContract,
|
|
305
|
+
guidance.emailRegistrationWorkflow,
|
|
306
|
+
guidance.webResearchToolPolicy,
|
|
307
|
+
buildCurrentTimeBlock(/* @__PURE__ */ new Date()),
|
|
308
|
+
readAgentsMd(cwd),
|
|
309
|
+
buildSkillDiscoveryContextBlock(cwd) ?? ""
|
|
310
|
+
];
|
|
311
|
+
const systemTokens = estimateTokenCount(
|
|
312
|
+
systemParts.filter((part) => part?.trim()).join("\n\n")
|
|
313
|
+
);
|
|
314
|
+
const toolTokens = estimateToolSchemas(toolNames);
|
|
315
|
+
return Math.max(0, systemTokens + toolTokens);
|
|
316
|
+
}
|
|
203
317
|
|
|
204
318
|
// packages/cli/client/compactDialog.ts
|
|
205
319
|
import { ulid } from "ulid";
|
|
@@ -480,6 +594,12 @@ function renderDialogRow(args, colorEnabled = resolveCliColorEnabled()) {
|
|
|
480
594
|
function renderDialogOverflow(text, colorEnabled = resolveCliColorEnabled()) {
|
|
481
595
|
return themeText(` ${text}`, "chrome", colorEnabled);
|
|
482
596
|
}
|
|
597
|
+
function renderOverflowAbove(count, colorEnabled = resolveCliColorEnabled()) {
|
|
598
|
+
return renderDialogOverflow(t("dialogMoreAbove", String(count)), colorEnabled);
|
|
599
|
+
}
|
|
600
|
+
function renderOverflowBelow(count, colorEnabled = resolveCliColorEnabled()) {
|
|
601
|
+
return renderDialogOverflow(t("dialogMoreBelow", String(count)), colorEnabled);
|
|
602
|
+
}
|
|
483
603
|
function renderDialogCommand(text, colorEnabled = resolveCliColorEnabled()) {
|
|
484
604
|
return themeText(` ${text}`, "danger", colorEnabled);
|
|
485
605
|
}
|
|
@@ -578,7 +698,7 @@ function renderSelectDialog(args) {
|
|
|
578
698
|
];
|
|
579
699
|
const lines = [...titleLines, ""];
|
|
580
700
|
if (window.start > 0) {
|
|
581
|
-
lines.push(
|
|
701
|
+
lines.push(renderOverflowAbove(window.start));
|
|
582
702
|
}
|
|
583
703
|
for (let index = window.start; index < window.end; index += 1) {
|
|
584
704
|
const item = args.items[index];
|
|
@@ -591,7 +711,7 @@ function renderSelectDialog(args) {
|
|
|
591
711
|
);
|
|
592
712
|
}
|
|
593
713
|
if (window.end < total) {
|
|
594
|
-
lines.push("",
|
|
714
|
+
lines.push("", renderOverflowBelow(total - window.end));
|
|
595
715
|
}
|
|
596
716
|
return lines.join("\n");
|
|
597
717
|
}
|
|
@@ -820,7 +940,7 @@ async function runSelectDialog(args) {
|
|
|
820
940
|
// packages/cli/tui/confirmDialog.ts
|
|
821
941
|
var FALLBACK_TERMINAL_WIDTH = 80;
|
|
822
942
|
var WIDE_CHAR = /[ᄀ-ᅟ⺀-〾ぁ-㏿㐀-䶿一-鿿ꀀ-가-힣豈-︰--⦆¢-₩]/;
|
|
823
|
-
function
|
|
943
|
+
function displayWidth2(text) {
|
|
824
944
|
let width = 0;
|
|
825
945
|
for (const char of text) width += WIDE_CHAR.test(char) ? 2 : 1;
|
|
826
946
|
return width;
|
|
@@ -828,9 +948,9 @@ function displayWidth(text) {
|
|
|
828
948
|
function truncateCommand(command, maxWidth) {
|
|
829
949
|
const indent = 2;
|
|
830
950
|
const budget = Math.max(10, maxWidth - indent);
|
|
831
|
-
if (
|
|
951
|
+
if (displayWidth2(command) <= budget) return command;
|
|
832
952
|
const marker = ` ${t("dialogConfirmCommandTruncated")}`;
|
|
833
|
-
const keep = Math.max(1, budget -
|
|
953
|
+
const keep = Math.max(1, budget - displayWidth2(marker));
|
|
834
954
|
let kept = "";
|
|
835
955
|
let width = 0;
|
|
836
956
|
for (const char of command) {
|
|
@@ -860,9 +980,15 @@ async function runConfirmDialog(args) {
|
|
|
860
980
|
value: false
|
|
861
981
|
}
|
|
862
982
|
];
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
983
|
+
let titleText = args.request.title;
|
|
984
|
+
let bodyText = args.request.body;
|
|
985
|
+
if (args.request.action === "destructive_shell_command") {
|
|
986
|
+
titleText = t("dialogConfirmTitle");
|
|
987
|
+
bodyText = t("dialogConfirmBody");
|
|
988
|
+
} else if (args.request.action === "external_file_access") {
|
|
989
|
+
titleText = t("dialogConfirmExternalFileTitle");
|
|
990
|
+
bodyText = t("dialogConfirmExternalFileBody");
|
|
991
|
+
}
|
|
866
992
|
const terminalWidth = typeof output.columns === "number" && output.columns > 0 ? output.columns : FALLBACK_TERMINAL_WIDTH;
|
|
867
993
|
const titleLines = [
|
|
868
994
|
renderDialogTitle(`${titleText} ${t("dialogConfirmHint")}`)
|
|
@@ -946,6 +1072,27 @@ function createInitialAskChoiceState(questions) {
|
|
|
946
1072
|
};
|
|
947
1073
|
}
|
|
948
1074
|
function askChoiceReducer(state, action) {
|
|
1075
|
+
if (action.type === "HYDRATE_QUESTIONS") {
|
|
1076
|
+
if (state.phase !== "active") return state;
|
|
1077
|
+
const newQuestions = action.questions;
|
|
1078
|
+
if (newQuestions.length <= state.questionStates.length) {
|
|
1079
|
+
if (newQuestions.length === state.questions.length) return state;
|
|
1080
|
+
return { ...state, questions: newQuestions };
|
|
1081
|
+
}
|
|
1082
|
+
const appended = newQuestions.slice(state.questionStates.length).map(() => ({
|
|
1083
|
+
cursorIndex: 0,
|
|
1084
|
+
selectedIds: [],
|
|
1085
|
+
pickedId: null,
|
|
1086
|
+
otherText: "",
|
|
1087
|
+
otherFocused: false
|
|
1088
|
+
}));
|
|
1089
|
+
return {
|
|
1090
|
+
...state,
|
|
1091
|
+
questions: newQuestions,
|
|
1092
|
+
questionStates: [...state.questionStates, ...appended],
|
|
1093
|
+
activeIndex: Math.min(state.activeIndex, newQuestions.length - 1)
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
949
1096
|
if (state.phase !== "active") return state;
|
|
950
1097
|
switch (action.type) {
|
|
951
1098
|
case "CANCEL":
|
|
@@ -1112,19 +1259,23 @@ function renderTabBar(questions, activeIndex, colorEnabled) {
|
|
|
1112
1259
|
}
|
|
1113
1260
|
return colorEnabled ? themeText(` ${label} `, "muted", colorEnabled) : ` ${label} `;
|
|
1114
1261
|
});
|
|
1115
|
-
const submitLabel = "
|
|
1262
|
+
const submitLabel = t("askChoiceSubmit");
|
|
1116
1263
|
return tabs.join(" ") + " " + (colorEnabled ? themeText(submitLabel, "chrome", colorEnabled) : submitLabel);
|
|
1117
1264
|
}
|
|
1118
|
-
function renderFooter(colorEnabled) {
|
|
1119
|
-
const hints =
|
|
1265
|
+
function renderFooter(multiSelect, colorEnabled) {
|
|
1266
|
+
const hints = multiSelect ? t("askChoiceFooterMulti") : t("askChoiceFooterSingle");
|
|
1120
1267
|
return colorEnabled ? themeText(` ${hints}`, "chrome", colorEnabled) : ` ${hints}`;
|
|
1121
1268
|
}
|
|
1269
|
+
function otherRowPrefix(index) {
|
|
1270
|
+
return ` [${index + 1}] ${t("askChoiceOtherLabel")}: `;
|
|
1271
|
+
}
|
|
1122
1272
|
function renderAskChoiceFrame(state) {
|
|
1123
1273
|
const colorEnabled = resolveCliColorEnabled();
|
|
1124
1274
|
const q = state.questions[state.activeIndex];
|
|
1125
1275
|
const qs = state.questionStates[state.activeIndex];
|
|
1126
1276
|
const lines = [];
|
|
1127
|
-
|
|
1277
|
+
let otherCursor = null;
|
|
1278
|
+
lines.push(renderDialogTitle(t("askChoiceTitle")));
|
|
1128
1279
|
lines.push("");
|
|
1129
1280
|
if (state.questions.length > 1) {
|
|
1130
1281
|
lines.push(renderTabBar(state.questions, state.activeIndex, colorEnabled));
|
|
@@ -1135,11 +1286,11 @@ function renderAskChoiceFrame(state) {
|
|
|
1135
1286
|
);
|
|
1136
1287
|
if (q.multiSelect) {
|
|
1137
1288
|
lines.push(
|
|
1138
|
-
colorEnabled ? themeText(
|
|
1289
|
+
colorEnabled ? themeText(` ${t("askChoiceHintMulti")}`, "muted", colorEnabled) : ` ${t("askChoiceHintMulti")}`
|
|
1139
1290
|
);
|
|
1140
1291
|
} else {
|
|
1141
1292
|
lines.push(
|
|
1142
|
-
colorEnabled ? themeText(
|
|
1293
|
+
colorEnabled ? themeText(` ${t("askChoiceHintSingle")}`, "muted", colorEnabled) : ` ${t("askChoiceHintSingle")}`
|
|
1143
1294
|
);
|
|
1144
1295
|
}
|
|
1145
1296
|
lines.push("");
|
|
@@ -1149,9 +1300,7 @@ function renderAskChoiceFrame(state) {
|
|
|
1149
1300
|
total: totalRows
|
|
1150
1301
|
});
|
|
1151
1302
|
if (window.start > 0) {
|
|
1152
|
-
lines.push(
|
|
1153
|
-
colorEnabled ? themeText(` \u2191 ${window.start} more`, "chrome", colorEnabled) : ` \u2191 ${window.start} more`
|
|
1154
|
-
);
|
|
1303
|
+
lines.push(renderOverflowAbove(window.start));
|
|
1155
1304
|
}
|
|
1156
1305
|
for (let i = window.start; i < window.end; i++) {
|
|
1157
1306
|
if (i < q.choices.length) {
|
|
@@ -1169,25 +1318,29 @@ function renderAskChoiceFrame(state) {
|
|
|
1169
1318
|
} else {
|
|
1170
1319
|
const focused = qs.cursorIndex === i;
|
|
1171
1320
|
const marker = focused ? DIALOG_CURSOR : " ";
|
|
1172
|
-
const
|
|
1173
|
-
const
|
|
1321
|
+
const plainPrefix = `${marker}${otherRowPrefix(i)}`;
|
|
1322
|
+
const plainRow = `${plainPrefix}${qs.otherText}`;
|
|
1174
1323
|
if (focused && colorEnabled) {
|
|
1175
1324
|
lines.push(
|
|
1176
|
-
`\x1B[1m${themeColorSequence("accent")}${
|
|
1325
|
+
`\x1B[1m${themeColorSequence("accent")}${plainRow}\x1B[0m`
|
|
1177
1326
|
);
|
|
1178
1327
|
} else {
|
|
1179
|
-
lines.push(
|
|
1328
|
+
lines.push(plainRow);
|
|
1329
|
+
}
|
|
1330
|
+
if (qs.otherFocused) {
|
|
1331
|
+
otherCursor = {
|
|
1332
|
+
lineIndex: lines.length - 1,
|
|
1333
|
+
col: displayWidth(plainPrefix + qs.otherText)
|
|
1334
|
+
};
|
|
1180
1335
|
}
|
|
1181
1336
|
}
|
|
1182
1337
|
}
|
|
1183
1338
|
if (window.end < totalRows) {
|
|
1184
|
-
lines.push(
|
|
1185
|
-
colorEnabled ? themeText(` \u2193 ${totalRows - window.end} more`, "chrome", colorEnabled) : ` \u2193 ${totalRows - window.end} more`
|
|
1186
|
-
);
|
|
1339
|
+
lines.push(renderOverflowBelow(totalRows - window.end));
|
|
1187
1340
|
}
|
|
1188
1341
|
lines.push("");
|
|
1189
|
-
lines.push(renderFooter(colorEnabled));
|
|
1190
|
-
return lines.join("\n");
|
|
1342
|
+
lines.push(renderFooter(q.multiSelect, colorEnabled));
|
|
1343
|
+
return { text: lines.join("\n"), otherCursor };
|
|
1191
1344
|
}
|
|
1192
1345
|
async function runAskChoiceDialog(args) {
|
|
1193
1346
|
const { request } = args;
|
|
@@ -1200,9 +1353,6 @@ async function runAskChoiceDialog(args) {
|
|
|
1200
1353
|
if (normalized.questions.length === 0) {
|
|
1201
1354
|
return { kind: "cancelled" };
|
|
1202
1355
|
}
|
|
1203
|
-
const q0 = normalized.questions[0];
|
|
1204
|
-
if (normalized.questions.length === 1 && !q0.multiSelect && !q0.allowOther) {
|
|
1205
|
-
}
|
|
1206
1356
|
let state = createInitialAskChoiceState(normalized.questions);
|
|
1207
1357
|
const output = args.output ?? process.stdout;
|
|
1208
1358
|
const input = args.input ?? process.stdin;
|
|
@@ -1216,13 +1366,28 @@ async function runAskChoiceDialog(args) {
|
|
|
1216
1366
|
typeof args.bottomRow === "function" ? args.bottomRow() : args.bottomRow ?? 0
|
|
1217
1367
|
);
|
|
1218
1368
|
let lastBottomRow = 0;
|
|
1369
|
+
let scrolledHeadroom = false;
|
|
1370
|
+
let unanchoredCursorLift = 0;
|
|
1371
|
+
const restoreUnanchoredCursor = () => {
|
|
1372
|
+
if (unanchoredCursorLift <= 0) return;
|
|
1373
|
+
output.write(`\x1B[${unanchoredCursorLift}B`);
|
|
1374
|
+
unanchoredCursorLift = 0;
|
|
1375
|
+
};
|
|
1219
1376
|
const paint = () => {
|
|
1220
1377
|
const frame = renderAskChoiceFrame(state);
|
|
1221
|
-
const lines = frame.split("\n");
|
|
1378
|
+
const lines = frame.text.split("\n");
|
|
1222
1379
|
const lineCount = lines.length;
|
|
1223
1380
|
const canPosition = outputIsTty(output) && typeof output.write === "function";
|
|
1224
1381
|
if (bottomAnchored && canPosition) {
|
|
1225
1382
|
const anchorRow = resolveBottomRow();
|
|
1383
|
+
if (!scrolledHeadroom) {
|
|
1384
|
+
const ttyRows = typeof output === "object" && output !== null && "rows" in output && typeof output.rows === "number" ? output.rows : 24;
|
|
1385
|
+
const scrollN = Math.min(lineCount, Math.max(0, ttyRows - 1));
|
|
1386
|
+
if (scrollN > 0) {
|
|
1387
|
+
output.write(`\x1B[${scrollN}S`);
|
|
1388
|
+
}
|
|
1389
|
+
scrolledHeadroom = true;
|
|
1390
|
+
}
|
|
1226
1391
|
clearAnchoredLines(
|
|
1227
1392
|
output,
|
|
1228
1393
|
lastBottomRow > 0 ? lastBottomRow : anchorRow,
|
|
@@ -1235,22 +1400,37 @@ async function runAskChoiceDialog(args) {
|
|
|
1235
1400
|
}
|
|
1236
1401
|
lastBottomRow = anchorRow;
|
|
1237
1402
|
renderedLineCount = lineCount;
|
|
1403
|
+
unanchoredCursorLift = 0;
|
|
1404
|
+
if (frame.otherCursor) {
|
|
1405
|
+
const cursorRow = anchorRow - (lineCount - 1 - frame.otherCursor.lineIndex);
|
|
1406
|
+
if (cursorRow >= 1) {
|
|
1407
|
+
output.write(`\x1B[${cursorRow};${frame.otherCursor.col + 1}H`);
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1238
1410
|
return;
|
|
1239
1411
|
}
|
|
1240
1412
|
if (canPosition) {
|
|
1413
|
+
restoreUnanchoredCursor();
|
|
1241
1414
|
for (let i = 0; i < renderedLineCount; i++) {
|
|
1242
1415
|
output.write("\x1B[1A\x1B[2K");
|
|
1243
1416
|
}
|
|
1244
|
-
output.write(`${frame}
|
|
1417
|
+
output.write(`${frame.text}
|
|
1245
1418
|
`);
|
|
1246
1419
|
renderedLineCount = lineCount;
|
|
1420
|
+
if (frame.otherCursor) {
|
|
1421
|
+
const linesFromBottom = lineCount - frame.otherCursor.lineIndex;
|
|
1422
|
+
output.write(`\x1B[${linesFromBottom}A`);
|
|
1423
|
+
output.write(`\x1B[${frame.otherCursor.col + 1}G`);
|
|
1424
|
+
unanchoredCursorLift = linesFromBottom;
|
|
1425
|
+
}
|
|
1247
1426
|
return;
|
|
1248
1427
|
}
|
|
1249
1428
|
if (typeof output.write === "function") {
|
|
1250
|
-
output.write(`${frame}
|
|
1429
|
+
output.write(`${frame.text}
|
|
1251
1430
|
`);
|
|
1252
1431
|
}
|
|
1253
1432
|
renderedLineCount = lineCount;
|
|
1433
|
+
unanchoredCursorLift = 0;
|
|
1254
1434
|
};
|
|
1255
1435
|
const resizeTarget = output;
|
|
1256
1436
|
const onOutputResize = () => paint();
|
|
@@ -1278,6 +1458,9 @@ async function runAskChoiceDialog(args) {
|
|
|
1278
1458
|
continue;
|
|
1279
1459
|
}
|
|
1280
1460
|
let action = null;
|
|
1461
|
+
const qs = state.questionStates[state.activeIndex];
|
|
1462
|
+
const q = state.questions[state.activeIndex];
|
|
1463
|
+
const isOtherRow = qs.cursorIndex >= q.choices.length;
|
|
1281
1464
|
if (isCancel(sequence)) {
|
|
1282
1465
|
action = { type: "CANCEL" };
|
|
1283
1466
|
} else if (sequence === CSI_TAB) {
|
|
@@ -1289,11 +1472,17 @@ async function runAskChoiceDialog(args) {
|
|
|
1289
1472
|
} else if (isArrowDown(sequence)) {
|
|
1290
1473
|
action = { type: "MOVE_CURSOR", delta: 1 };
|
|
1291
1474
|
} else if (sequence === CSI_SPACE) {
|
|
1292
|
-
|
|
1475
|
+
if (qs.otherFocused) {
|
|
1476
|
+
action = {
|
|
1477
|
+
type: "SET_OTHER_TEXT",
|
|
1478
|
+
text: qs.otherText + " "
|
|
1479
|
+
};
|
|
1480
|
+
} else if (q.multiSelect) {
|
|
1481
|
+
action = { type: "TOGGLE_AT_CURSOR" };
|
|
1482
|
+
} else if (isOtherRow) {
|
|
1483
|
+
action = { type: "FOCUS_OTHER" };
|
|
1484
|
+
}
|
|
1293
1485
|
} else if (isSubmit(sequence)) {
|
|
1294
|
-
const qs = state.questionStates[state.activeIndex];
|
|
1295
|
-
const q = state.questions[state.activeIndex];
|
|
1296
|
-
const isOtherRow = qs.cursorIndex >= q.choices.length;
|
|
1297
1486
|
if (qs.otherFocused) {
|
|
1298
1487
|
action = { type: "BLUR_OTHER" };
|
|
1299
1488
|
} else if (isOtherRow && !qs.otherFocused) {
|
|
@@ -1304,24 +1493,29 @@ async function runAskChoiceDialog(args) {
|
|
|
1304
1493
|
action = { type: "SELECT_AT_CURSOR" };
|
|
1305
1494
|
}
|
|
1306
1495
|
} else if (sequence === CSI_BACKSPACE || sequence === CSI_BACKSPACE_ALT) {
|
|
1307
|
-
const qs = state.questionStates[state.activeIndex];
|
|
1308
1496
|
if (qs.otherFocused && qs.otherText.length > 0) {
|
|
1497
|
+
const chars = Array.from(qs.otherText);
|
|
1309
1498
|
action = {
|
|
1310
1499
|
type: "SET_OTHER_TEXT",
|
|
1311
|
-
text:
|
|
1500
|
+
text: chars.slice(0, -1).join("")
|
|
1312
1501
|
};
|
|
1313
1502
|
}
|
|
1314
|
-
} else if (sequence.length
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1503
|
+
} else if (sequence.length >= 1 && sequence.charCodeAt(0) >= 32 && !sequence.startsWith("\x1B")) {
|
|
1504
|
+
if (qs.otherFocused || isOtherRow && q.allowOther) {
|
|
1505
|
+
const cleaned = sequence.replace(/[\x00-\x1f\x7f]/g, "");
|
|
1506
|
+
if (cleaned) {
|
|
1507
|
+
if (!qs.otherFocused) {
|
|
1508
|
+
state = askChoiceReducer(state, { type: "FOCUS_OTHER" });
|
|
1509
|
+
}
|
|
1510
|
+
const currentText = state.questionStates[state.activeIndex].otherText;
|
|
1511
|
+
action = {
|
|
1512
|
+
type: "SET_OTHER_TEXT",
|
|
1513
|
+
text: currentText + cleaned
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1321
1516
|
}
|
|
1322
1517
|
}
|
|
1323
1518
|
if (action) {
|
|
1324
|
-
const prev = state;
|
|
1325
1519
|
state = askChoiceReducer(state, action);
|
|
1326
1520
|
if (state.phase !== "active") break;
|
|
1327
1521
|
if (action.type === "BLUR_OTHER" && state.activeIndex >= state.questions.length - 1 && canSubmit(state)) {
|
|
@@ -1345,6 +1539,7 @@ async function runAskChoiceDialog(args) {
|
|
|
1345
1539
|
renderedLineCount
|
|
1346
1540
|
);
|
|
1347
1541
|
} else {
|
|
1542
|
+
restoreUnanchoredCursor();
|
|
1348
1543
|
for (let i = 0; i < renderedLineCount; i++) {
|
|
1349
1544
|
output.write("\x1B[1A\x1B[2K");
|
|
1350
1545
|
}
|
|
@@ -1358,13 +1553,13 @@ async function runAskChoiceDialog(args) {
|
|
|
1358
1553
|
}
|
|
1359
1554
|
if (result.answers.length === 1) {
|
|
1360
1555
|
const a = result.answers[0];
|
|
1556
|
+
const q = normalized.questions[0];
|
|
1557
|
+
const labels = a.selectedIds.map((id) => q.choices.find((c) => c.id === id)?.label ?? "").filter(Boolean);
|
|
1558
|
+
if (a.otherText) labels.push(a.otherText);
|
|
1361
1559
|
return {
|
|
1362
1560
|
kind: "selected",
|
|
1363
1561
|
userMessage: a.userMessage,
|
|
1364
|
-
label:
|
|
1365
|
-
const q = normalized.questions[0];
|
|
1366
|
-
return q.choices.find((c) => c.id === id)?.label ?? "";
|
|
1367
|
-
}).join(", ") || a.otherText || ""
|
|
1562
|
+
label: labels.join(", ") || a.otherText || ""
|
|
1368
1563
|
};
|
|
1369
1564
|
}
|
|
1370
1565
|
return {
|
|
@@ -1422,8 +1617,13 @@ function createActivityIndicator(deps) {
|
|
|
1422
1617
|
let lastActivityAt = 0;
|
|
1423
1618
|
let frameIndex = 0;
|
|
1424
1619
|
let timer = null;
|
|
1620
|
+
let stopping = false;
|
|
1425
1621
|
const elapsedSecFrom = (startedAt) => startedAt > 0 ? Math.max(0, Math.floor((now() - startedAt) / 1e3)) : 0;
|
|
1426
1622
|
const tick = () => {
|
|
1623
|
+
if (stopping) {
|
|
1624
|
+
deps.onRepaint();
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1427
1627
|
frameIndex += 1;
|
|
1428
1628
|
if (explicitLabel === null && !fallbackActive && deps.isTurnActive() && lastActivityAt > 0 && now() - lastActivityAt >= fallbackDelayMs) {
|
|
1429
1629
|
fallbackActive = true;
|
|
@@ -1452,9 +1652,21 @@ function createActivityIndicator(deps) {
|
|
|
1452
1652
|
ensureTimer();
|
|
1453
1653
|
deps.onRepaint();
|
|
1454
1654
|
};
|
|
1655
|
+
const markStopping = () => {
|
|
1656
|
+
stopping = true;
|
|
1657
|
+
deps.onRepaint();
|
|
1658
|
+
};
|
|
1659
|
+
const isStopping = () => stopping;
|
|
1455
1660
|
const getView = () => {
|
|
1456
1661
|
if (!deps.isTurnActive()) return null;
|
|
1457
1662
|
const frame = ACTIVITY_FRAMES[frameIndex % ACTIVITY_FRAMES.length];
|
|
1663
|
+
if (stopping) {
|
|
1664
|
+
return {
|
|
1665
|
+
frame,
|
|
1666
|
+
label: deps.stoppingLabel(),
|
|
1667
|
+
elapsedSec: 0
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1458
1670
|
if (explicitLabel !== null) {
|
|
1459
1671
|
return {
|
|
1460
1672
|
frame,
|
|
@@ -1471,6 +1683,36 @@ function createActivityIndicator(deps) {
|
|
|
1471
1683
|
}
|
|
1472
1684
|
return null;
|
|
1473
1685
|
};
|
|
1686
|
+
let agentRunSnapshot = null;
|
|
1687
|
+
const updateAgentRun = (snapshot) => {
|
|
1688
|
+
agentRunSnapshot = snapshot;
|
|
1689
|
+
deps.onRepaint();
|
|
1690
|
+
};
|
|
1691
|
+
const clearAgentRun = () => {
|
|
1692
|
+
agentRunSnapshot = null;
|
|
1693
|
+
deps.onRepaint();
|
|
1694
|
+
};
|
|
1695
|
+
const getAgentRun = () => agentRunSnapshot;
|
|
1696
|
+
const getActivityLines = (colorEnabled = resolveCliColorEnabled()) => {
|
|
1697
|
+
const lines = [];
|
|
1698
|
+
const baseView = getView();
|
|
1699
|
+
if (baseView) {
|
|
1700
|
+
const elapsed = Math.max(0, baseView.elapsedSec);
|
|
1701
|
+
const elapsedStr = `${elapsed}s`;
|
|
1702
|
+
const stopHint = t("stopHint");
|
|
1703
|
+
if (!colorEnabled) {
|
|
1704
|
+
lines.push(`${baseView.frame} ${baseView.label} (${elapsedStr}) \xB7 ${stopHint}`);
|
|
1705
|
+
} else {
|
|
1706
|
+
lines.push(
|
|
1707
|
+
themeText(baseView.frame, "accent") + " " + themeText(baseView.label, "muted") + themeText(` (${elapsedStr})`, "chrome") + themeText(` \xB7 ${stopHint}`, "chrome")
|
|
1708
|
+
);
|
|
1709
|
+
}
|
|
1710
|
+
}
|
|
1711
|
+
if (agentRunSnapshot) {
|
|
1712
|
+
lines.push(...formatAgentRunPanelLines(agentRunSnapshot, colorEnabled));
|
|
1713
|
+
}
|
|
1714
|
+
return lines.length > 0 ? lines : null;
|
|
1715
|
+
};
|
|
1474
1716
|
const stop = () => {
|
|
1475
1717
|
if (timer !== null) {
|
|
1476
1718
|
clearIntervalFn(timer);
|
|
@@ -1481,8 +1723,70 @@ function createActivityIndicator(deps) {
|
|
|
1481
1723
|
fallbackActive = false;
|
|
1482
1724
|
fallbackStartedAt = 0;
|
|
1483
1725
|
lastActivityAt = 0;
|
|
1726
|
+
stopping = false;
|
|
1727
|
+
agentRunSnapshot = null;
|
|
1484
1728
|
};
|
|
1485
|
-
return {
|
|
1729
|
+
return {
|
|
1730
|
+
report,
|
|
1731
|
+
updateAgentRun,
|
|
1732
|
+
clearAgentRun,
|
|
1733
|
+
getAgentRun,
|
|
1734
|
+
getView,
|
|
1735
|
+
getActivityLines,
|
|
1736
|
+
markStopping,
|
|
1737
|
+
isStopping,
|
|
1738
|
+
stop
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
function formatAgentRunPanelLines(snapshot, colorEnabled) {
|
|
1742
|
+
const lines = [];
|
|
1743
|
+
const name = snapshot.agentName || "sub-agent";
|
|
1744
|
+
const runId = snapshot.runId ? ` (${snapshot.runId.slice(0, 8)})` : "";
|
|
1745
|
+
const status = snapshot.status || "running";
|
|
1746
|
+
let statusSymbol = "\u231B";
|
|
1747
|
+
let statusColor = "warning";
|
|
1748
|
+
if (status === "completed" || status === "finished" || status === "success") {
|
|
1749
|
+
statusSymbol = "\u2713";
|
|
1750
|
+
statusColor = "success";
|
|
1751
|
+
} else if (status === "killed" || status === "cancelled" || status === "failed" || status === "stopped") {
|
|
1752
|
+
statusSymbol = "\u{1F534}";
|
|
1753
|
+
statusColor = "danger";
|
|
1754
|
+
}
|
|
1755
|
+
const pollPart = snapshot.polls !== void 0 ? ` (polls: ${snapshot.polls})` : "";
|
|
1756
|
+
const errPart = snapshot.errorMessage ? ` \xB7 ${snapshot.errorMessage}` : "";
|
|
1757
|
+
if (!colorEnabled) {
|
|
1758
|
+
lines.push(`\u{1F916} Sub-Agent: ${name}${runId} \xB7 ${statusSymbol} ${status}${pollPart}${errPart}`);
|
|
1759
|
+
} else {
|
|
1760
|
+
const botIcon = themeText("\u{1F916}", "accent", true);
|
|
1761
|
+
const labelText = themeText(" Sub-Agent: ", "muted", true);
|
|
1762
|
+
const nameText = themeText(`${name}${runId}`, "chrome", true);
|
|
1763
|
+
const statusText = themeText(` \xB7 ${statusSymbol} ${status}`, statusColor, true);
|
|
1764
|
+
const pollsText = pollPart ? themeText(pollPart, "chrome") : "";
|
|
1765
|
+
const errorText = errPart ? themeText(errPart, "danger") : "";
|
|
1766
|
+
lines.push(`${botIcon}${labelText}${nameText}${statusText}${pollsText}${errorText}`);
|
|
1767
|
+
}
|
|
1768
|
+
let logLine;
|
|
1769
|
+
if (snapshot.logTail) {
|
|
1770
|
+
const trimmed = snapshot.logTail.trim();
|
|
1771
|
+
if (trimmed) {
|
|
1772
|
+
const parts = trimmed.split(/\r?\n/);
|
|
1773
|
+
logLine = parts[parts.length - 1];
|
|
1774
|
+
}
|
|
1775
|
+
} else if (snapshot.logLines && snapshot.logLines.length > 0) {
|
|
1776
|
+
logLine = snapshot.logLines[snapshot.logLines.length - 1];
|
|
1777
|
+
}
|
|
1778
|
+
if (logLine && logLine.trim()) {
|
|
1779
|
+
const cleanLog = logLine.trim();
|
|
1780
|
+
if (!colorEnabled) {
|
|
1781
|
+
lines.push(` \u2514 Log: ${cleanLog}`);
|
|
1782
|
+
} else {
|
|
1783
|
+
const branchChar = themeText(" \u2514 ", "muted");
|
|
1784
|
+
const logTag = themeText("Log: ", "muted");
|
|
1785
|
+
const logContent = themeText(cleanLog, "chrome");
|
|
1786
|
+
lines.push(`${branchChar}${logTag}${logContent}`);
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
return lines;
|
|
1486
1790
|
}
|
|
1487
1791
|
|
|
1488
1792
|
// packages/ai/agent/utils/sortUtils.ts
|
|
@@ -1533,12 +1837,12 @@ function sortAgentsFavoriteOwnedPublic(items) {
|
|
|
1533
1837
|
// packages/cli/tui/agentCatalog.ts
|
|
1534
1838
|
var DEFAULT_TUI_AGENT_KEY = "agent-pub-01NOLOAPPBLD000000019KCKT0";
|
|
1535
1839
|
function formatAgentSourceLabel(entry) {
|
|
1536
|
-
if (entry.kind === "platform" || entry.apiSource === "platform") return "
|
|
1840
|
+
if (entry.kind === "platform" || entry.apiSource === "platform") return t("agentSourcePlatform");
|
|
1537
1841
|
if (entry.apiSource === "cli") {
|
|
1538
|
-
return entry.cliProvider ?
|
|
1842
|
+
return entry.cliProvider ? `${t("agentSourceSubscription")}(${entry.cliProvider})` : t("agentSourceSubscription");
|
|
1539
1843
|
}
|
|
1540
|
-
if (entry.apiSource === "custom") return "
|
|
1541
|
-
return "
|
|
1844
|
+
if (entry.apiSource === "custom") return t("agentSourceApi");
|
|
1845
|
+
return t("agentSourcePlatform");
|
|
1542
1846
|
}
|
|
1543
1847
|
var PLATFORM_AGENTS = [
|
|
1544
1848
|
{
|
|
@@ -1722,13 +2026,25 @@ function findAgentCatalogEntry(entries, rawTarget) {
|
|
|
1722
2026
|
if (!target) return null;
|
|
1723
2027
|
if (/^\d+$/.test(target)) {
|
|
1724
2028
|
const entry = entries[Number(target) - 1];
|
|
1725
|
-
return entry ? {
|
|
2029
|
+
return entry ? {
|
|
2030
|
+
name: entry.name,
|
|
2031
|
+
key: entry.key,
|
|
2032
|
+
model: entry.model,
|
|
2033
|
+
...entry.apiSource ? { apiSource: entry.apiSource } : entry.kind === "platform" ? { apiSource: "platform" } : {}
|
|
2034
|
+
} : null;
|
|
1726
2035
|
}
|
|
1727
2036
|
const lower = target.toLowerCase();
|
|
1728
2037
|
const byName = entries.find(
|
|
1729
2038
|
(entry) => entry.name.toLowerCase() === lower || entry.key.toLowerCase() === lower || entry.key.toLowerCase().endsWith(`-${lower}`)
|
|
1730
2039
|
);
|
|
1731
|
-
if (byName)
|
|
2040
|
+
if (byName) {
|
|
2041
|
+
return {
|
|
2042
|
+
name: byName.name,
|
|
2043
|
+
key: byName.key,
|
|
2044
|
+
model: byName.model,
|
|
2045
|
+
...byName.apiSource ? { apiSource: byName.apiSource } : byName.kind === "platform" ? { apiSource: "platform" } : {}
|
|
2046
|
+
};
|
|
2047
|
+
}
|
|
1732
2048
|
if (target.startsWith("agent-") || target.startsWith("agent-pub-")) {
|
|
1733
2049
|
return { name: target, key: target };
|
|
1734
2050
|
}
|
|
@@ -1787,6 +2103,8 @@ async function runAgentPicker(args) {
|
|
|
1787
2103
|
kind: "selected",
|
|
1788
2104
|
name: selected.name,
|
|
1789
2105
|
key: selected.key,
|
|
2106
|
+
model: selected.model,
|
|
2107
|
+
apiSource: selected.apiSource ?? (selected.kind === "platform" ? "platform" : void 0),
|
|
1790
2108
|
entries
|
|
1791
2109
|
};
|
|
1792
2110
|
}
|
|
@@ -1796,7 +2114,11 @@ function resolveAgentSwitchTarget(rawTarget, catalogEntries = []) {
|
|
|
1796
2114
|
const aliasEntry = catalogEntries.find((entry) => entry.key === resolvedKey);
|
|
1797
2115
|
return {
|
|
1798
2116
|
name: aliasEntry?.name ?? asTrimmedLowercaseString(rawTarget),
|
|
1799
|
-
key: resolvedKey
|
|
2117
|
+
key: resolvedKey,
|
|
2118
|
+
...aliasEntry?.model ? { model: aliasEntry.model } : {},
|
|
2119
|
+
...aliasEntry ? {
|
|
2120
|
+
...aliasEntry.apiSource ? { apiSource: aliasEntry.apiSource } : aliasEntry.kind === "platform" ? { apiSource: "platform" } : {}
|
|
2121
|
+
} : {}
|
|
1800
2122
|
};
|
|
1801
2123
|
}
|
|
1802
2124
|
return findAgentCatalogEntry(catalogEntries, rawTarget);
|
|
@@ -1809,10 +2131,10 @@ function formatDialogTimestamp(value, now = Date.now()) {
|
|
|
1809
2131
|
const time = typeof value === "number" ? value : Date.parse(value);
|
|
1810
2132
|
if (!Number.isFinite(time) || time <= 0) return "";
|
|
1811
2133
|
const deltaSeconds = Math.max(0, Math.floor((now - time) / 1e3));
|
|
1812
|
-
if (deltaSeconds < 60) return "
|
|
1813
|
-
if (deltaSeconds < 3600) return
|
|
1814
|
-
if (deltaSeconds < 86400) return
|
|
1815
|
-
if (deltaSeconds < 30 * 86400) return
|
|
2134
|
+
if (deltaSeconds < 60) return t("timeJustNow");
|
|
2135
|
+
if (deltaSeconds < 3600) return t("timeMinutesAgo", String(Math.floor(deltaSeconds / 60)));
|
|
2136
|
+
if (deltaSeconds < 86400) return t("timeHoursAgo", String(Math.floor(deltaSeconds / 3600)));
|
|
2137
|
+
if (deltaSeconds < 30 * 86400) return t("timeDaysAgo", String(Math.floor(deltaSeconds / 86400)));
|
|
1816
2138
|
const date = new Date(time);
|
|
1817
2139
|
const pad = (part) => String(part).padStart(2, "0");
|
|
1818
2140
|
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
@@ -1829,7 +2151,7 @@ function toDialogPickerItems(dialogs) {
|
|
|
1829
2151
|
}
|
|
1830
2152
|
function renderDialogList(dialogs) {
|
|
1831
2153
|
if (dialogs.length === 0) return t("noDialogsYet");
|
|
1832
|
-
const lines = ["
|
|
2154
|
+
const lines = [t("recentDialogs")];
|
|
1833
2155
|
for (const [index, dialog] of dialogs.entries()) {
|
|
1834
2156
|
const stamp = formatDialogTimestamp(dialog.updatedAt ?? dialog.createdAt);
|
|
1835
2157
|
lines.push(
|
|
@@ -1837,7 +2159,7 @@ function renderDialogList(dialogs) {
|
|
|
1837
2159
|
` id=${dialog.id}`
|
|
1838
2160
|
);
|
|
1839
2161
|
}
|
|
1840
|
-
lines.push("", "
|
|
2162
|
+
lines.push("", t("dialogListTip"));
|
|
1841
2163
|
return lines.join("\n");
|
|
1842
2164
|
}
|
|
1843
2165
|
async function fetchRecentDialogs(args) {
|
|
@@ -1918,7 +2240,7 @@ async function runDialogPicker(args) {
|
|
|
1918
2240
|
}
|
|
1919
2241
|
|
|
1920
2242
|
// packages/cli/tui/pasteImage.ts
|
|
1921
|
-
import { existsSync } from "node:fs";
|
|
2243
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
1922
2244
|
import { readFile, stat } from "node:fs/promises";
|
|
1923
2245
|
import { homedir } from "node:os";
|
|
1924
2246
|
import { isAbsolute, resolve } from "node:path";
|
|
@@ -1973,7 +2295,7 @@ function detectImagePaths(line, cwd) {
|
|
|
1973
2295
|
if (!token.raw) continue;
|
|
1974
2296
|
const resolved = resolveImageSource(token.decoded, cwd);
|
|
1975
2297
|
if (!isImagePath(resolved)) continue;
|
|
1976
|
-
if (!
|
|
2298
|
+
if (!existsSync2(resolved)) continue;
|
|
1977
2299
|
out.push({ raw: token.raw, resolvedPath: resolved });
|
|
1978
2300
|
}
|
|
1979
2301
|
return out;
|
|
@@ -2156,302 +2478,48 @@ async function readImagePaths(paths, options = {}) {
|
|
|
2156
2478
|
}
|
|
2157
2479
|
|
|
2158
2480
|
// packages/cli/tui/gitStatus.ts
|
|
2159
|
-
import { execSync } from "node:child_process";
|
|
2160
|
-
function
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
return { branch, modified, untracked };
|
|
2187
|
-
} catch {
|
|
2188
|
-
return void 0;
|
|
2189
|
-
}
|
|
2190
|
-
}
|
|
2191
|
-
|
|
2192
|
-
// packages/cli/tui/tuiAnsi.ts
|
|
2193
|
-
var ANSI_ESCAPE_REGEX = /\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/g;
|
|
2194
|
-
var OSC_HYPERLINK_REGEX = /\x1b\]8;;[^\x07\x1b]*(?:\x07|\x1b\\)\x1b\]8;;(?:\x07|\x1b\\)|\x1b\]8;;[^\x07\x1b]*(?:\x07|\x1b\\)/g;
|
|
2195
|
-
function stripAnsi(text) {
|
|
2196
|
-
return text.replace(OSC_HYPERLINK_REGEX, "").replace(ANSI_ESCAPE_REGEX, "");
|
|
2197
|
-
}
|
|
2198
|
-
var SGR_SEQUENCE_REGEX = /^\x1b\[[0-9;]*m/;
|
|
2199
|
-
var TRAILING_SGR_REGEX = /(?:\x1b\[[0-9;]*m)+$/;
|
|
2200
|
-
function applyTerminalOutputToText(existing, chunk) {
|
|
2201
|
-
if (!chunk) return existing;
|
|
2202
|
-
let text = existing;
|
|
2203
|
-
let index = 0;
|
|
2204
|
-
while (index < chunk.length) {
|
|
2205
|
-
if (chunk[index] === "\x1B") {
|
|
2206
|
-
const sgr = SGR_SEQUENCE_REGEX.exec(chunk.slice(index));
|
|
2207
|
-
if (sgr) {
|
|
2208
|
-
text += sgr[0];
|
|
2209
|
-
index += sgr[0].length;
|
|
2210
|
-
continue;
|
|
2211
|
-
}
|
|
2212
|
-
const osc = chunk.slice(index).match(/^\x1b\]8;;[^\x07\x1b]*(?:\x07|\x1b\\)/);
|
|
2213
|
-
if (osc) {
|
|
2214
|
-
index += osc[0].length;
|
|
2215
|
-
continue;
|
|
2216
|
-
}
|
|
2217
|
-
const csi = chunk.slice(index).match(/^\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/);
|
|
2218
|
-
if (csi) {
|
|
2219
|
-
index += csi[0].length;
|
|
2220
|
-
continue;
|
|
2221
|
-
}
|
|
2222
|
-
index += 1;
|
|
2223
|
-
continue;
|
|
2224
|
-
}
|
|
2225
|
-
const ch = chunk[index];
|
|
2226
|
-
if (ch === "\r") {
|
|
2227
|
-
const lastNl = text.lastIndexOf("\n");
|
|
2228
|
-
text = lastNl === -1 ? "" : text.slice(0, lastNl + 1);
|
|
2229
|
-
index += 1;
|
|
2230
|
-
continue;
|
|
2231
|
-
}
|
|
2232
|
-
if (ch === "\n") {
|
|
2233
|
-
text += "\n";
|
|
2234
|
-
index += 1;
|
|
2235
|
-
continue;
|
|
2236
|
-
}
|
|
2237
|
-
if (ch === "\b") {
|
|
2238
|
-
const trailing = TRAILING_SGR_REGEX.exec(text);
|
|
2239
|
-
const sgrTail = trailing ? trailing[0] : "";
|
|
2240
|
-
const head = sgrTail ? text.slice(0, -sgrTail.length) : text;
|
|
2241
|
-
if (head.length > 0 && head[head.length - 1] !== "\n") {
|
|
2242
|
-
text = head.slice(0, -1) + sgrTail;
|
|
2243
|
-
}
|
|
2244
|
-
index += 1;
|
|
2245
|
-
continue;
|
|
2246
|
-
}
|
|
2247
|
-
const code = ch.charCodeAt(0);
|
|
2248
|
-
if (code < 32 && ch !== " " || code === 127) {
|
|
2249
|
-
index += 1;
|
|
2250
|
-
continue;
|
|
2251
|
-
}
|
|
2252
|
-
text += ch;
|
|
2253
|
-
index += 1;
|
|
2254
|
-
}
|
|
2255
|
-
return text;
|
|
2256
|
-
}
|
|
2257
|
-
function displayWidth2(str) {
|
|
2258
|
-
let width = 0;
|
|
2259
|
-
for (const char of str) {
|
|
2260
|
-
const code = char.codePointAt(0) ?? 0;
|
|
2261
|
-
if (code < 32 || code === 127) continue;
|
|
2262
|
-
if (code >= 4352 && code <= 4447 || // 0x2768-0x2775 (ornamental brackets, incl. the ❯ prompt at U+276F)
|
|
2263
|
-
// render narrow in terminals; counting them wide drifts the cursor.
|
|
2264
|
-
code >= 9728 && code <= 10175 && !(code >= 10088 && code <= 10101) || // 0x2B00-0x2BFF (Misc Symbols and Arrows, incl. ⬢ U+2B22 used as the
|
|
2265
|
-
// status-line agent icon) render double-wide in common monospace fonts;
|
|
2266
|
-
// counting ⬢ as width 1 drifts the whole status line right of the icon.
|
|
2267
|
-
code >= 11008 && code <= 11263 || code >= 11904 && code <= 12350 || code >= 12352 && code <= 13247 || code >= 13312 && code <= 19903 || code >= 19968 && code <= 42191 || code >= 44032 && code <= 55203 || code >= 63744 && code <= 64255 || code >= 65072 && code <= 65135 || code >= 65281 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 127744 && code <= 129791 || code >= 131072 && code <= 196605 || code >= 196608 && code <= 262141 || // East Asian Ambiguous CJK quotation marks (U+201C/D “ ”, U+2018/9 ‘ ’).
|
|
2268
|
-
// In CJK locale + CJK fonts these render double-wide like 全角 punctuation;
|
|
2269
|
-
// in English fonts they stay width 1. Follow the active locale so Chinese
|
|
2270
|
-
// replies align correctly without breaking English terminal output.
|
|
2271
|
-
getCliLocale() === "zh" && (code === 8220 || code === 8221 || code === 8216 || code === 8217)) {
|
|
2272
|
-
width += 2;
|
|
2273
|
-
} else {
|
|
2274
|
-
width += 1;
|
|
2275
|
-
}
|
|
2276
|
-
}
|
|
2277
|
-
return width;
|
|
2278
|
-
}
|
|
2279
|
-
function visibleWidth(str) {
|
|
2280
|
-
return displayWidth2(stripAnsi(str));
|
|
2281
|
-
}
|
|
2282
|
-
function truncateAnsi(text, maxWidth) {
|
|
2283
|
-
if (maxWidth <= 0) return "";
|
|
2284
|
-
if (visibleWidth(text) <= maxWidth) return text;
|
|
2285
|
-
let width = 0;
|
|
2286
|
-
let out = "";
|
|
2287
|
-
let i = 0;
|
|
2288
|
-
let sawAnsi = false;
|
|
2289
|
-
while (i < text.length) {
|
|
2290
|
-
if (text[i] === "\x1B" && text[i + 1] === "[") {
|
|
2291
|
-
sawAnsi = true;
|
|
2292
|
-
let j = i + 2;
|
|
2293
|
-
while (j < text.length) {
|
|
2294
|
-
const code = text.charCodeAt(j);
|
|
2295
|
-
j += 1;
|
|
2296
|
-
if (code >= 64 && code <= 126) break;
|
|
2297
|
-
}
|
|
2298
|
-
out += text.slice(i, j);
|
|
2299
|
-
i = j;
|
|
2300
|
-
continue;
|
|
2301
|
-
}
|
|
2302
|
-
const codePoint = text.codePointAt(i) ?? 0;
|
|
2303
|
-
const char = String.fromCodePoint(codePoint);
|
|
2304
|
-
const charWidth = displayWidth2(char);
|
|
2305
|
-
if (width + charWidth > maxWidth) break;
|
|
2306
|
-
out += char;
|
|
2307
|
-
width += charWidth;
|
|
2308
|
-
i += char.length;
|
|
2309
|
-
}
|
|
2310
|
-
return sawAnsi ? `${out}\x1B[0m` : out;
|
|
2311
|
-
}
|
|
2312
|
-
function fitAnsiLine(text, width, ellipsis = "\u2026") {
|
|
2313
|
-
if (width <= 0) return "";
|
|
2314
|
-
if (visibleWidth(text) <= width) return text;
|
|
2315
|
-
const ellipsisWidth = displayWidth2(ellipsis);
|
|
2316
|
-
if (width < ellipsisWidth) return truncateAnsi(text, width);
|
|
2317
|
-
if (width === ellipsisWidth) return truncateAnsi(ellipsis, width) || truncateAnsi(text, width);
|
|
2318
|
-
return `${truncateAnsi(text, width - ellipsisWidth)}${ellipsis}`;
|
|
2319
|
-
}
|
|
2320
|
-
function countPhysicalLines(text, columns) {
|
|
2321
|
-
const lines = text.split("\n");
|
|
2322
|
-
let total = 0;
|
|
2323
|
-
for (const line of lines) {
|
|
2324
|
-
const width = displayWidth2(line);
|
|
2325
|
-
total += Math.max(1, Math.ceil(width / columns));
|
|
2326
|
-
}
|
|
2327
|
-
return Math.max(total, 1);
|
|
2328
|
-
}
|
|
2329
|
-
function takeDisplayWidth(text, width) {
|
|
2330
|
-
let used = 0;
|
|
2331
|
-
let index = 0;
|
|
2332
|
-
for (const char of text) {
|
|
2333
|
-
const charWidth = displayWidth2(char);
|
|
2334
|
-
if (used + charWidth > width && used > 0) break;
|
|
2335
|
-
used += charWidth;
|
|
2336
|
-
index += char.length;
|
|
2337
|
-
}
|
|
2338
|
-
return { prefix: text.slice(0, index), rest: text.slice(index) };
|
|
2339
|
-
}
|
|
2340
|
-
function padOrTruncateToWidth(text, width) {
|
|
2341
|
-
const textWidth = visibleWidth(text);
|
|
2342
|
-
if (textWidth > width) {
|
|
2343
|
-
return truncateAnsi(text, width);
|
|
2344
|
-
}
|
|
2345
|
-
return `${text}${" ".repeat(width - textWidth)}`;
|
|
2346
|
-
}
|
|
2347
|
-
var SGR_RESET_REGEX = /^\x1b\[0?m$/;
|
|
2348
|
-
function tokenizeAnsiLine(line) {
|
|
2349
|
-
const tokens = [];
|
|
2350
|
-
let index = 0;
|
|
2351
|
-
while (index < line.length) {
|
|
2352
|
-
if (line[index] === "\x1B") {
|
|
2353
|
-
const osc = line.slice(index).match(/^\x1b\]8;;[^\x07\x1b]*(?:\x07|\x1b\\)/);
|
|
2354
|
-
if (osc) {
|
|
2355
|
-
tokens.push({ kind: "sgr", value: osc[0], width: 0 });
|
|
2356
|
-
index += osc[0].length;
|
|
2357
|
-
continue;
|
|
2358
|
-
}
|
|
2359
|
-
const sgr = SGR_SEQUENCE_REGEX.exec(line.slice(index));
|
|
2360
|
-
if (sgr) {
|
|
2361
|
-
tokens.push({ kind: "sgr", value: sgr[0], width: 0 });
|
|
2362
|
-
index += sgr[0].length;
|
|
2363
|
-
continue;
|
|
2364
|
-
}
|
|
2365
|
-
}
|
|
2366
|
-
const codePoint = line.codePointAt(index) ?? 0;
|
|
2367
|
-
const value = String.fromCodePoint(codePoint);
|
|
2368
|
-
tokens.push({ kind: "char", value, width: displayWidth2(value) });
|
|
2369
|
-
index += value.length;
|
|
2370
|
-
}
|
|
2371
|
-
return tokens;
|
|
2372
|
-
}
|
|
2373
|
-
function wrapTranscriptLine(line, columns) {
|
|
2374
|
-
if (line === "") return [""];
|
|
2375
|
-
const tokens = tokenizeAnsiLine(line);
|
|
2376
|
-
const result = [];
|
|
2377
|
-
let activeStyles = [];
|
|
2378
|
-
const applyStyleToken = (value) => {
|
|
2379
|
-
if (SGR_RESET_REGEX.test(value)) {
|
|
2380
|
-
activeStyles = [];
|
|
2381
|
-
} else {
|
|
2382
|
-
activeStyles.push(value);
|
|
2383
|
-
}
|
|
2384
|
-
};
|
|
2385
|
-
let start = 0;
|
|
2386
|
-
while (start < tokens.length) {
|
|
2387
|
-
if (tokens.slice(start).every((token) => token.kind === "sgr")) {
|
|
2388
|
-
if (result.length > 0) break;
|
|
2389
|
-
}
|
|
2390
|
-
const openingStyles = [...activeStyles];
|
|
2391
|
-
let width = 0;
|
|
2392
|
-
let end = start;
|
|
2393
|
-
let lastBreak = -1;
|
|
2394
|
-
while (end < tokens.length) {
|
|
2395
|
-
const token = tokens[end];
|
|
2396
|
-
if (token.kind === "sgr") {
|
|
2397
|
-
end += 1;
|
|
2398
|
-
continue;
|
|
2399
|
-
}
|
|
2400
|
-
if (width + token.width > columns && width > 0) break;
|
|
2401
|
-
width += token.width;
|
|
2402
|
-
end += 1;
|
|
2403
|
-
if (token.value === " " || token.value === " ") {
|
|
2404
|
-
lastBreak = end;
|
|
2405
|
-
}
|
|
2406
|
-
}
|
|
2407
|
-
let segmentEnd = end;
|
|
2408
|
-
if (end < tokens.length && lastBreak > start) {
|
|
2409
|
-
const overflowToken = tokens[end];
|
|
2410
|
-
if (overflowToken.kind === "char" && overflowToken.value !== " " && overflowToken.width === 1) {
|
|
2411
|
-
segmentEnd = lastBreak;
|
|
2412
|
-
}
|
|
2413
|
-
}
|
|
2414
|
-
if (segmentEnd === start) segmentEnd = start + 1;
|
|
2415
|
-
let segment = "";
|
|
2416
|
-
let sawStyle = openingStyles.length > 0;
|
|
2417
|
-
for (let i = start; i < segmentEnd; i += 1) {
|
|
2418
|
-
const token = tokens[i];
|
|
2419
|
-
segment += token.value;
|
|
2420
|
-
if (token.kind === "sgr") {
|
|
2421
|
-
sawStyle = true;
|
|
2422
|
-
applyStyleToken(token.value);
|
|
2423
|
-
}
|
|
2424
|
-
}
|
|
2425
|
-
const prefix = openingStyles.join("");
|
|
2426
|
-
const needsReset = (sawStyle || activeStyles.length > 0) && !segment.endsWith("\x1B[0m");
|
|
2427
|
-
result.push(`${prefix}${segment}${needsReset ? "\x1B[0m" : ""}`);
|
|
2428
|
-
start = segmentEnd;
|
|
2429
|
-
while (start < tokens.length) {
|
|
2430
|
-
const token = tokens[start];
|
|
2431
|
-
if (token.kind === "char" && token.value === " ") {
|
|
2432
|
-
start += 1;
|
|
2433
|
-
continue;
|
|
2481
|
+
import { execFile, execSync } from "node:child_process";
|
|
2482
|
+
function parseStatusOutput(branch, status) {
|
|
2483
|
+
if (!status) {
|
|
2484
|
+
return { branch, modified: 0, untracked: 0 };
|
|
2485
|
+
}
|
|
2486
|
+
let modified = 0;
|
|
2487
|
+
let untracked = 0;
|
|
2488
|
+
for (const line of status.split("\n")) {
|
|
2489
|
+
if (!line) continue;
|
|
2490
|
+
const flag = line[0];
|
|
2491
|
+
if (flag === "?") untracked += 1;
|
|
2492
|
+
else modified += 1;
|
|
2493
|
+
}
|
|
2494
|
+
return { branch, modified, untracked };
|
|
2495
|
+
}
|
|
2496
|
+
function execFileUtf8(file, args, cwd) {
|
|
2497
|
+
return new Promise((resolve2, reject) => {
|
|
2498
|
+
execFile(
|
|
2499
|
+
file,
|
|
2500
|
+
args,
|
|
2501
|
+
{ cwd, encoding: "utf8", timeout: 2e3 },
|
|
2502
|
+
(err, stdout) => {
|
|
2503
|
+
if (err) {
|
|
2504
|
+
reject(err);
|
|
2505
|
+
return;
|
|
2506
|
+
}
|
|
2507
|
+
resolve2(typeof stdout === "string" ? stdout : String(stdout ?? ""));
|
|
2434
2508
|
}
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
}
|
|
2438
|
-
return result.length > 0 ? result : [""];
|
|
2509
|
+
);
|
|
2510
|
+
});
|
|
2439
2511
|
}
|
|
2440
|
-
function
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
const { prefix, rest } = takeDisplayWidth(remaining, columns);
|
|
2450
|
-
result.push(prefix);
|
|
2451
|
-
remaining = rest;
|
|
2512
|
+
function detectGitStatusAsync(cwd) {
|
|
2513
|
+
return (async () => {
|
|
2514
|
+
try {
|
|
2515
|
+
const branch = (await execFileUtf8("git", ["rev-parse", "--abbrev-ref", "HEAD"], cwd)).trim();
|
|
2516
|
+
if (!branch) return void 0;
|
|
2517
|
+
const status = (await execFileUtf8("git", ["status", "--porcelain"], cwd)).trim();
|
|
2518
|
+
return parseStatusOutput(branch, status);
|
|
2519
|
+
} catch {
|
|
2520
|
+
return void 0;
|
|
2452
2521
|
}
|
|
2453
|
-
}
|
|
2454
|
-
return result;
|
|
2522
|
+
})();
|
|
2455
2523
|
}
|
|
2456
2524
|
|
|
2457
2525
|
// packages/cli/tui/sessionRender.ts
|
|
@@ -2468,12 +2536,12 @@ function formatElapsedSeconds(totalSeconds) {
|
|
|
2468
2536
|
const mins = minutes % 60;
|
|
2469
2537
|
return `${hours}h ${mins}m`;
|
|
2470
2538
|
}
|
|
2471
|
-
function renderComposerTokenChip(tokens, fallbackContextWindow) {
|
|
2539
|
+
function renderComposerTokenChip(tokens, fallbackContextWindow, estimatedContextTokens) {
|
|
2472
2540
|
const cw = tokens?.contextWindow ?? fallbackContextWindow;
|
|
2473
2541
|
if (!cw) {
|
|
2474
2542
|
return "context: \u2014";
|
|
2475
2543
|
}
|
|
2476
|
-
const used = tokens ? tokens.input + tokens.output : 0;
|
|
2544
|
+
const used = tokens ? tokens.input + tokens.output : Math.max(0, estimatedContextTokens ?? 0);
|
|
2477
2545
|
const pct = Math.min(100, used / cw * 100);
|
|
2478
2546
|
const pctText = pct > 0 && pct < 10 ? pct.toFixed(1) : Math.round(pct).toString();
|
|
2479
2547
|
return `context: ${pctText}% (${formatTokenCount(used)}/${formatTokenCount(cw)})`;
|
|
@@ -2495,7 +2563,17 @@ function renderStatusLine(state) {
|
|
|
2495
2563
|
const untrackedText = untracked > 0 ? ` ${themeText(`?${untracked}`, "muted", colorEnabled)}` : "";
|
|
2496
2564
|
parts.push(`${branchText}${modifiedText}${untrackedText}`);
|
|
2497
2565
|
}
|
|
2498
|
-
const
|
|
2566
|
+
const credits = state.apiSource === "platform" ? state.turnTokens?.credits : void 0;
|
|
2567
|
+
const creditsSuffix = credits !== void 0 && credits > 0 ? ` \xB7 \u26A1 ${credits.toFixed(2)} \u79EF\u5206` : "";
|
|
2568
|
+
const tokenSegment = themeText(
|
|
2569
|
+
renderComposerTokenChip(
|
|
2570
|
+
state.turnTokens,
|
|
2571
|
+
state.contextWindow,
|
|
2572
|
+
state.estimatedContextTokens
|
|
2573
|
+
) + creditsSuffix,
|
|
2574
|
+
"muted",
|
|
2575
|
+
colorEnabled
|
|
2576
|
+
);
|
|
2499
2577
|
parts.push(tokenSegment);
|
|
2500
2578
|
const runningCount = getProcessRegistry().list().filter((p) => p.status === "running").length;
|
|
2501
2579
|
if (runningCount > 0) {
|
|
@@ -2577,7 +2655,7 @@ function renderWelcome(state, frame = 0, maxFrames = 0, columns) {
|
|
|
2577
2655
|
const isDark = brightness === "dark";
|
|
2578
2656
|
let sceneArt = colorEnabled ? buildColoredScene(isDark, frame, maxFrames) : buildPlainScene(isDark, frame, maxFrames);
|
|
2579
2657
|
if (typeof columns === "number" && columns > 0) {
|
|
2580
|
-
const widestSceneCol = sceneArt.split("\n").reduce((max, line) => Math.max(max,
|
|
2658
|
+
const widestSceneCol = sceneArt.split("\n").reduce((max, line) => Math.max(max, displayWidth(stripAnsi(line))), 0);
|
|
2581
2659
|
if (columns < widestSceneCol) sceneArt = "";
|
|
2582
2660
|
}
|
|
2583
2661
|
const versionLine = colorEnabled ? `\x1B[1m${themeColorSequence("accent")}nolo\x1B[0m ${state.cliVersion ?? ""} | server ${state.serverUrl}`.replace(" |", " |") : `nolo ${state.cliVersion ?? ""} | server ${state.serverUrl}`.replace(" |", " |");
|
|
@@ -2627,19 +2705,19 @@ function renderContextPanel(state, colorEnabled = resolveCliColorEnabled()) {
|
|
|
2627
2705
|
];
|
|
2628
2706
|
let maxW = 0;
|
|
2629
2707
|
for (const l of labels) {
|
|
2630
|
-
const w =
|
|
2708
|
+
const w = displayWidth(l);
|
|
2631
2709
|
if (w > maxW) maxW = w;
|
|
2632
2710
|
}
|
|
2633
2711
|
const targetWidth = maxW >= 9 ? maxW + 1 : 9;
|
|
2634
2712
|
const padLabel = (raw) => {
|
|
2635
|
-
const w =
|
|
2713
|
+
const w = displayWidth(raw);
|
|
2636
2714
|
const padding = " ".repeat(Math.max(0, targetWidth - w));
|
|
2637
2715
|
return raw + padding;
|
|
2638
2716
|
};
|
|
2639
2717
|
const field = (rawLabel, value) => `${themeText(padLabel(rawLabel), "muted", colorEnabled)}${value}`;
|
|
2640
2718
|
const next = (command, description) => ` ${themeText(command, "accent", colorEnabled)}${themeText(description, "muted", colorEnabled)}`;
|
|
2641
2719
|
const titleText = t("contextTitle");
|
|
2642
|
-
const heading = colorEnabled ? [renderDialogTitle(titleText, true)] : [titleText, "\u2500".repeat(
|
|
2720
|
+
const heading = colorEnabled ? [renderDialogTitle(titleText, true)] : [titleText, "\u2500".repeat(displayWidth(titleText))];
|
|
2643
2721
|
return [
|
|
2644
2722
|
...heading,
|
|
2645
2723
|
field(labels[0], `${state.agentName} (${state.agentKey})`),
|
|
@@ -2684,14 +2762,21 @@ function renderKnownAgents(colorEnabled = resolveCliColorEnabled()) {
|
|
|
2684
2762
|
|
|
2685
2763
|
// packages/cli/tui/sessionInput.ts
|
|
2686
2764
|
var PASTE_TOKEN_PREFIX = "\0PASTE\0";
|
|
2687
|
-
function applyTuiInputKey(buffer, sequence, key = {}, cursorPos) {
|
|
2765
|
+
function applyTuiInputKey(buffer, sequence, key = {}, cursorPos, options) {
|
|
2688
2766
|
const seq = sequence ?? "";
|
|
2689
2767
|
const curPos = Math.max(0, Math.min(buffer.length, cursorPos ?? buffer.length));
|
|
2768
|
+
const pasteStore = options?.pasteStore;
|
|
2690
2769
|
if (seq.startsWith(PASTE_TOKEN_PREFIX)) {
|
|
2691
2770
|
const rawPayload = seq.slice(PASTE_TOKEN_PREFIX.length);
|
|
2692
2771
|
const normalized = rawPayload.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
2693
|
-
const
|
|
2694
|
-
|
|
2772
|
+
const insertAt2 = snapCursorOutsidePasteChip(buffer, curPos);
|
|
2773
|
+
if (pasteStore && shouldCollapsePaste(normalized)) {
|
|
2774
|
+
const { placeholder } = allocateCollapsedPaste(pasteStore, normalized);
|
|
2775
|
+
const nextBuf3 = buffer.slice(0, insertAt2) + placeholder + buffer.slice(insertAt2);
|
|
2776
|
+
return { buffer: nextBuf3, cursorPos: insertAt2 + placeholder.length };
|
|
2777
|
+
}
|
|
2778
|
+
const nextBuf2 = buffer.slice(0, insertAt2) + normalized + buffer.slice(insertAt2);
|
|
2779
|
+
return { buffer: nextBuf2, cursorPos: insertAt2 + normalized.length };
|
|
2695
2780
|
}
|
|
2696
2781
|
if (seq === "" || key.ctrl && key.name === "c") {
|
|
2697
2782
|
return { buffer, cursorPos: curPos, abort: true };
|
|
@@ -2700,16 +2785,25 @@ function applyTuiInputKey(buffer, sequence, key = {}, cursorPos) {
|
|
|
2700
2785
|
return { buffer, cursorPos: curPos, copyView: true };
|
|
2701
2786
|
}
|
|
2702
2787
|
if (seq === "\x1B[13;2~" || seq === "\x1B[27;2;13~" || seq === "\x1B\r" || key.shift && (key.name === "enter" || key.name === "return") || seq === "\n" || key.ctrl && key.name === "j") {
|
|
2703
|
-
const
|
|
2704
|
-
|
|
2788
|
+
const insertAt2 = snapCursorOutsidePasteChip(buffer, curPos);
|
|
2789
|
+
const nextBuf2 = buffer.slice(0, insertAt2) + "\n" + buffer.slice(insertAt2);
|
|
2790
|
+
return { buffer: nextBuf2, cursorPos: insertAt2 + 1 };
|
|
2705
2791
|
}
|
|
2706
2792
|
if (key.name === "enter" || key.name === "return" || seq === "\r") {
|
|
2707
2793
|
return { buffer: "", cursorPos: 0, submit: buffer };
|
|
2708
2794
|
}
|
|
2709
2795
|
if (isLeftArrowSequence(seq, key)) {
|
|
2796
|
+
const span = findCollapsedPasteSpanAt(buffer, curPos, { preferLeft: true });
|
|
2797
|
+
if (span && curPos > span.start && curPos <= span.end) {
|
|
2798
|
+
return { buffer, cursorPos: span.start };
|
|
2799
|
+
}
|
|
2710
2800
|
return { buffer, cursorPos: Math.max(0, curPos - 1) };
|
|
2711
2801
|
}
|
|
2712
2802
|
if (isRightArrowSequence(seq, key)) {
|
|
2803
|
+
const span = findCollapsedPasteSpanAt(buffer, curPos);
|
|
2804
|
+
if (span && curPos >= span.start && curPos < span.end) {
|
|
2805
|
+
return { buffer, cursorPos: span.end };
|
|
2806
|
+
}
|
|
2713
2807
|
return { buffer, cursorPos: Math.min(buffer.length, curPos + 1) };
|
|
2714
2808
|
}
|
|
2715
2809
|
if (isHomeSequence(seq, key)) {
|
|
@@ -2720,24 +2814,33 @@ function applyTuiInputKey(buffer, sequence, key = {}, cursorPos) {
|
|
|
2720
2814
|
}
|
|
2721
2815
|
if (isDeleteWordSequence(seq, key)) {
|
|
2722
2816
|
if (curPos === 0) return { buffer, cursorPos: curPos };
|
|
2817
|
+
const pasteSpan = findCollapsedPasteSpanAt(buffer, curPos, {
|
|
2818
|
+
preferLeft: true
|
|
2819
|
+
});
|
|
2820
|
+
if (pasteSpan && (curPos === pasteSpan.end || curPos > pasteSpan.start && curPos < pasteSpan.end)) {
|
|
2821
|
+
return removePasteSpan(buffer, pasteSpan, pasteStore);
|
|
2822
|
+
}
|
|
2723
2823
|
const cutIdx = findWordStartLeft(buffer, curPos);
|
|
2724
|
-
|
|
2725
|
-
return { buffer: nextBuf2, cursorPos: cutIdx };
|
|
2824
|
+
return deleteBufferRange(buffer, cutIdx, curPos, pasteStore);
|
|
2726
2825
|
}
|
|
2727
2826
|
if (isKillToLineStartSequence(seq, key)) {
|
|
2728
2827
|
if (curPos === 0) return { buffer, cursorPos: curPos };
|
|
2729
2828
|
const lineStart = buffer.lastIndexOf("\n", curPos - 1) + 1;
|
|
2730
|
-
|
|
2731
|
-
return { buffer: nextBuf2, cursorPos: lineStart };
|
|
2829
|
+
return deleteBufferRange(buffer, lineStart, curPos, pasteStore);
|
|
2732
2830
|
}
|
|
2733
2831
|
if (isKillToLineEndSequence(seq, key)) {
|
|
2734
2832
|
const nlIdx = buffer.indexOf("\n", curPos);
|
|
2735
2833
|
const lineEnd = nlIdx === -1 ? buffer.length : nlIdx;
|
|
2736
|
-
|
|
2737
|
-
return { buffer: nextBuf2, cursorPos: curPos };
|
|
2834
|
+
return deleteBufferRange(buffer, curPos, lineEnd, pasteStore);
|
|
2738
2835
|
}
|
|
2739
2836
|
if (isBackspaceSequence(seq, key)) {
|
|
2740
2837
|
if (curPos > 0) {
|
|
2838
|
+
const pasteSpan = findCollapsedPasteSpanAt(buffer, curPos, {
|
|
2839
|
+
preferLeft: true
|
|
2840
|
+
});
|
|
2841
|
+
if (pasteSpan && (curPos === pasteSpan.end || curPos > pasteSpan.start && curPos < pasteSpan.end)) {
|
|
2842
|
+
return removePasteSpan(buffer, pasteSpan, pasteStore);
|
|
2843
|
+
}
|
|
2741
2844
|
const nextBuf2 = buffer.slice(0, curPos - 1) + buffer.slice(curPos);
|
|
2742
2845
|
return { buffer: nextBuf2, cursorPos: curPos - 1 };
|
|
2743
2846
|
}
|
|
@@ -2745,6 +2848,10 @@ function applyTuiInputKey(buffer, sequence, key = {}, cursorPos) {
|
|
|
2745
2848
|
}
|
|
2746
2849
|
if (isForwardDeleteSequence(seq, key)) {
|
|
2747
2850
|
if (curPos < buffer.length) {
|
|
2851
|
+
const pasteSpan = findCollapsedPasteSpanAt(buffer, curPos);
|
|
2852
|
+
if (pasteSpan && (curPos === pasteSpan.start || curPos > pasteSpan.start && curPos < pasteSpan.end)) {
|
|
2853
|
+
return removePasteSpan(buffer, pasteSpan, pasteStore);
|
|
2854
|
+
}
|
|
2748
2855
|
const nextBuf2 = buffer.slice(0, curPos) + buffer.slice(curPos + 1);
|
|
2749
2856
|
return { buffer: nextBuf2, cursorPos: curPos };
|
|
2750
2857
|
}
|
|
@@ -2761,8 +2868,14 @@ function applyTuiInputKey(buffer, sequence, key = {}, cursorPos) {
|
|
|
2761
2868
|
if (!seq || key.ctrl || key.meta || seq.startsWith("\x1B")) {
|
|
2762
2869
|
return { buffer, cursorPos: curPos };
|
|
2763
2870
|
}
|
|
2764
|
-
const
|
|
2765
|
-
|
|
2871
|
+
const insertAt = snapCursorOutsidePasteChip(buffer, curPos);
|
|
2872
|
+
const nextBuf = buffer.slice(0, insertAt) + seq + buffer.slice(insertAt);
|
|
2873
|
+
return { buffer: nextBuf, cursorPos: insertAt + seq.length };
|
|
2874
|
+
}
|
|
2875
|
+
function snapCursorOutsidePasteChip(buffer, pos) {
|
|
2876
|
+
const span = findCollapsedPasteSpanAt(buffer, pos);
|
|
2877
|
+
if (span && pos > span.start && pos < span.end) return span.end;
|
|
2878
|
+
return pos;
|
|
2766
2879
|
}
|
|
2767
2880
|
function isLeftArrowSequence(seq, key) {
|
|
2768
2881
|
if (key.name === "left") return true;
|
|
@@ -2786,13 +2899,44 @@ function isEndSequence(seq, key) {
|
|
|
2786
2899
|
if (seq === "" || seq === "\x1B[F" || seq === "\x1B[4~" || seq === "\x1B[8~" || seq === "\x1BOF") return true;
|
|
2787
2900
|
return false;
|
|
2788
2901
|
}
|
|
2902
|
+
function removePasteSpan(buffer, span, pasteStore) {
|
|
2903
|
+
if (pasteStore) releaseCollapsedPaste(pasteStore, span.id);
|
|
2904
|
+
const nextBuf = buffer.slice(0, span.start) + buffer.slice(span.end);
|
|
2905
|
+
return { buffer: nextBuf, cursorPos: span.start };
|
|
2906
|
+
}
|
|
2907
|
+
function deleteBufferRange(buffer, start, end, pasteStore) {
|
|
2908
|
+
if (start >= end) return { buffer, cursorPos: start };
|
|
2909
|
+
const range = expandRangeToCollapsedPasteChips(buffer, start, end);
|
|
2910
|
+
releasePastesInRange(buffer, range.start, range.end, pasteStore);
|
|
2911
|
+
const nextBuf = buffer.slice(0, range.start) + buffer.slice(range.end);
|
|
2912
|
+
return { buffer: nextBuf, cursorPos: range.start };
|
|
2913
|
+
}
|
|
2914
|
+
function releasePastesInRange(buffer, start, end, pasteStore) {
|
|
2915
|
+
if (!pasteStore || start >= end) return;
|
|
2916
|
+
const slice = buffer.slice(start, end);
|
|
2917
|
+
const re = /\[paste #(\d+) · (\d+) lines\]/g;
|
|
2918
|
+
let match;
|
|
2919
|
+
while ((match = re.exec(slice)) !== null) {
|
|
2920
|
+
releaseCollapsedPaste(pasteStore, Number(match[1]));
|
|
2921
|
+
}
|
|
2922
|
+
}
|
|
2789
2923
|
var CJK_CHAR = /[\u4e00-\u9fff\u3000-\u303f\uff00-\uffef]/;
|
|
2790
2924
|
function findWordStartLeft(buffer, curPos) {
|
|
2791
2925
|
let i = curPos;
|
|
2792
2926
|
while (i > 0 && /\s/.test(buffer[i - 1])) i--;
|
|
2793
2927
|
if (i === 0) return 0;
|
|
2928
|
+
const spanAtCursor = findCollapsedPasteSpanAt(buffer, i, {
|
|
2929
|
+
preferLeft: true
|
|
2930
|
+
});
|
|
2931
|
+
if (spanAtCursor && (i === spanAtCursor.end || i > spanAtCursor.start && i < spanAtCursor.end)) {
|
|
2932
|
+
return spanAtCursor.start;
|
|
2933
|
+
}
|
|
2794
2934
|
if (CJK_CHAR.test(buffer[i - 1])) return i - 1;
|
|
2795
|
-
while (i > 0 && !/\s/.test(buffer[i - 1]) && !CJK_CHAR.test(buffer[i - 1]))
|
|
2935
|
+
while (i > 0 && !/\s/.test(buffer[i - 1]) && !CJK_CHAR.test(buffer[i - 1])) {
|
|
2936
|
+
const span = findCollapsedPasteSpanAt(buffer, i, { preferLeft: true });
|
|
2937
|
+
if (span && i === span.end) break;
|
|
2938
|
+
i--;
|
|
2939
|
+
}
|
|
2796
2940
|
return i;
|
|
2797
2941
|
}
|
|
2798
2942
|
function isDeleteWordSequence(seq, key) {
|
|
@@ -2903,6 +3047,7 @@ function createInitialTuiState(env = process.env) {
|
|
|
2903
3047
|
) || void 0;
|
|
2904
3048
|
const dialogEnvValue = asOptionalTrimmedString(env.NOLO_DIALOG);
|
|
2905
3049
|
const explicitDialogKey = asOptionalTrimmedString(env.NOLO_DIALOG_KEY) ?? (dialogEnvValue?.startsWith("dialog-") ? dialogEnvValue : void 0);
|
|
3050
|
+
const autoRouteDefault = env.NOLO_AUTO_ROUTE !== "0";
|
|
2906
3051
|
return {
|
|
2907
3052
|
agentKey,
|
|
2908
3053
|
agentName,
|
|
@@ -2922,22 +3067,47 @@ function createInitialTuiState(env = process.env) {
|
|
|
2922
3067
|
attachedImages: [],
|
|
2923
3068
|
runtimeMode,
|
|
2924
3069
|
modeLabel: asOptionalTrimmedString(env.NOLO_CLI_STATUS_MODE) ?? runtimeMode,
|
|
2925
|
-
gitStatus:
|
|
3070
|
+
gitStatus: void 0,
|
|
2926
3071
|
thinkingDisplay: normalizeThinkingDisplayMode(
|
|
2927
3072
|
env.NOLO_CLI_THINKING ?? env.NOLO_THINKING,
|
|
2928
3073
|
"hide"
|
|
2929
3074
|
),
|
|
2930
3075
|
toolDisplay: normalizeToolDisplayMode(env.NOLO_CLI_TOOLS ?? env.NOLO_TOOLS, "compact"),
|
|
2931
|
-
contextWindow:
|
|
3076
|
+
contextWindow: resolveAgentContextWindow({
|
|
3077
|
+
agentKey,
|
|
3078
|
+
agentName,
|
|
3079
|
+
autoRouteDefault
|
|
3080
|
+
}),
|
|
3081
|
+
estimatedContextTokens: estimateDefaultCliContextTokens({
|
|
3082
|
+
cwd,
|
|
3083
|
+
agentKey,
|
|
3084
|
+
agentName: autoRouteDefault ? "DeepSeek V4 Flash" : agentName,
|
|
3085
|
+
model: autoRouteDefault ? "deepseek-v4-flash" : void 0
|
|
3086
|
+
}),
|
|
3087
|
+
apiSource: PLATFORM_AGENTS.some((entry) => entry.key === agentKey) ? "platform" : void 0
|
|
2932
3088
|
};
|
|
2933
3089
|
}
|
|
2934
|
-
function applyAgentSwitch(state, target) {
|
|
3090
|
+
function applyAgentSwitch(state, target, opts) {
|
|
3091
|
+
const autoRouteDefault = opts?.autoRouteDefault ?? process.env.NOLO_AUTO_ROUTE !== "0";
|
|
3092
|
+
const contextWindow = resolveAgentContextWindow({
|
|
3093
|
+
agentKey: target.key,
|
|
3094
|
+
agentName: target.name,
|
|
3095
|
+
model: target.model,
|
|
3096
|
+
autoRouteDefault
|
|
3097
|
+
});
|
|
2935
3098
|
return {
|
|
2936
3099
|
nextState: {
|
|
2937
3100
|
...state,
|
|
2938
3101
|
agentName: target.name,
|
|
2939
3102
|
agentKey: target.key,
|
|
2940
|
-
contextWindow
|
|
3103
|
+
contextWindow,
|
|
3104
|
+
estimatedContextTokens: estimateDefaultCliContextTokens({
|
|
3105
|
+
cwd: state.cwd,
|
|
3106
|
+
agentKey: target.key,
|
|
3107
|
+
agentName: target.name,
|
|
3108
|
+
model: target.model
|
|
3109
|
+
}),
|
|
3110
|
+
apiSource: target.apiSource
|
|
2941
3111
|
},
|
|
2942
3112
|
output: `Switched to ${target.name}. ${state.dialogId ? `Dialog kept: ${state.dialogId}` : "Dialog kept: new"}`
|
|
2943
3113
|
};
|
|
@@ -2999,32 +3169,32 @@ function handleTuiInput(input, state) {
|
|
|
2999
3169
|
return {
|
|
3000
3170
|
nextState: state,
|
|
3001
3171
|
output: [
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3172
|
+
t("themeCurrent", getActiveThemeName(), brightness),
|
|
3173
|
+
t("themeUsage"),
|
|
3174
|
+
t("themeAvailable", available)
|
|
3005
3175
|
].join("\n")
|
|
3006
3176
|
};
|
|
3007
3177
|
}
|
|
3008
3178
|
if (sub === "light" || sub === "dark") {
|
|
3009
3179
|
setActiveBrightness(sub);
|
|
3010
|
-
return { nextState: state, output:
|
|
3180
|
+
return { nextState: state, output: t("themeBrightnessSwitched", sub) };
|
|
3011
3181
|
}
|
|
3012
3182
|
if (sub === "auto") {
|
|
3013
3183
|
setActiveBrightness(null);
|
|
3014
3184
|
return {
|
|
3015
3185
|
nextState: state,
|
|
3016
|
-
output:
|
|
3186
|
+
output: t("themeBrightnessAuto", resolveTuiBrightness())
|
|
3017
3187
|
};
|
|
3018
3188
|
}
|
|
3019
3189
|
if (setActiveThemeName(sub)) {
|
|
3020
3190
|
return {
|
|
3021
3191
|
nextState: state,
|
|
3022
|
-
output:
|
|
3192
|
+
output: t("themeSwitched", sub)
|
|
3023
3193
|
};
|
|
3024
3194
|
} else {
|
|
3025
3195
|
return {
|
|
3026
3196
|
nextState: state,
|
|
3027
|
-
output:
|
|
3197
|
+
output: t("themeUnknown", sub, available)
|
|
3028
3198
|
};
|
|
3029
3199
|
}
|
|
3030
3200
|
}
|
|
@@ -3034,20 +3204,19 @@ function handleTuiInput(input, state) {
|
|
|
3034
3204
|
if (!sub) {
|
|
3035
3205
|
return {
|
|
3036
3206
|
nextState: state,
|
|
3037
|
-
output:
|
|
3038
|
-
Usage: /density <cozy|spacious>`
|
|
3207
|
+
output: t("densityCurrent", getActiveDensity())
|
|
3039
3208
|
};
|
|
3040
3209
|
}
|
|
3041
3210
|
if (sub === "cozy" || sub === "spacious") {
|
|
3042
3211
|
setActiveDensity(sub);
|
|
3043
3212
|
return {
|
|
3044
3213
|
nextState: state,
|
|
3045
|
-
output:
|
|
3214
|
+
output: t("densitySwitched", sub)
|
|
3046
3215
|
};
|
|
3047
3216
|
} else {
|
|
3048
3217
|
return {
|
|
3049
3218
|
nextState: state,
|
|
3050
|
-
output:
|
|
3219
|
+
output: t("densityUnknown", sub)
|
|
3051
3220
|
};
|
|
3052
3221
|
}
|
|
3053
3222
|
}
|
|
@@ -3058,52 +3227,52 @@ Usage: /density <cozy|spacious>`
|
|
|
3058
3227
|
if (argText !== "auto" && argText !== "local" && argText !== "server") {
|
|
3059
3228
|
return {
|
|
3060
3229
|
nextState: state,
|
|
3061
|
-
output: "
|
|
3230
|
+
output: t("runtimeUsage")
|
|
3062
3231
|
};
|
|
3063
3232
|
}
|
|
3064
3233
|
return {
|
|
3065
3234
|
nextState: { ...state, runtimeMode: argText },
|
|
3066
|
-
output:
|
|
3235
|
+
output: t("runtimeSet", argText)
|
|
3067
3236
|
};
|
|
3068
3237
|
}
|
|
3069
3238
|
case "/tools": {
|
|
3070
3239
|
if (!argText) {
|
|
3071
3240
|
return {
|
|
3072
3241
|
nextState: state,
|
|
3073
|
-
output:
|
|
3242
|
+
output: t("toolsCurrent", state.toolDisplay)
|
|
3074
3243
|
};
|
|
3075
3244
|
}
|
|
3076
3245
|
const normalizedArg = asTrimmedLowercaseString(argText);
|
|
3077
3246
|
if (!["hide", "compact", "verbose", "on", "off"].includes(normalizedArg)) {
|
|
3078
3247
|
return {
|
|
3079
3248
|
nextState: state,
|
|
3080
|
-
output: "
|
|
3249
|
+
output: t("toolsUsage")
|
|
3081
3250
|
};
|
|
3082
3251
|
}
|
|
3083
3252
|
const nextMode = normalizeToolDisplayMode(normalizedArg, state.toolDisplay);
|
|
3084
3253
|
return {
|
|
3085
3254
|
nextState: { ...state, toolDisplay: nextMode },
|
|
3086
|
-
output:
|
|
3255
|
+
output: t("toolsSet", nextMode)
|
|
3087
3256
|
};
|
|
3088
3257
|
}
|
|
3089
3258
|
case "/thinking": {
|
|
3090
3259
|
if (!argText) {
|
|
3091
3260
|
return {
|
|
3092
3261
|
nextState: state,
|
|
3093
|
-
output:
|
|
3262
|
+
output: t("thinkingCurrent", state.thinkingDisplay)
|
|
3094
3263
|
};
|
|
3095
3264
|
}
|
|
3096
3265
|
const normalizedArg = asTrimmedLowercaseString(argText);
|
|
3097
3266
|
if (!["hide", "marker", "show", "on", "off"].includes(normalizedArg)) {
|
|
3098
3267
|
return {
|
|
3099
3268
|
nextState: state,
|
|
3100
|
-
output: "
|
|
3269
|
+
output: t("thinkingUsage")
|
|
3101
3270
|
};
|
|
3102
3271
|
}
|
|
3103
3272
|
const nextMode = normalizeThinkingDisplayMode(normalizedArg, state.thinkingDisplay);
|
|
3104
3273
|
return {
|
|
3105
3274
|
nextState: { ...state, thinkingDisplay: nextMode },
|
|
3106
|
-
output:
|
|
3275
|
+
output: t("thinkingSet", nextMode)
|
|
3107
3276
|
};
|
|
3108
3277
|
}
|
|
3109
3278
|
case "/tasks":
|
|
@@ -3115,14 +3284,14 @@ Usage: /density <cozy|spacious>`
|
|
|
3115
3284
|
const stopped = all.filter((p) => p.status !== "running");
|
|
3116
3285
|
const lines = [];
|
|
3117
3286
|
if (running.length > 0) {
|
|
3118
|
-
lines.push(
|
|
3287
|
+
lines.push(t("tasksRunning", String(running.length)));
|
|
3119
3288
|
for (const p of running) {
|
|
3120
3289
|
const elapsed = formatElapsedSeconds(Math.floor((Date.now() - p.startedAt) / 1e3));
|
|
3121
3290
|
lines.push(` pid ${p.pid} ${p.label} running ${elapsed}`);
|
|
3122
3291
|
}
|
|
3123
3292
|
}
|
|
3124
3293
|
if (stopped.length > 0) {
|
|
3125
|
-
lines.push(
|
|
3294
|
+
lines.push(t("tasksStopped", String(stopped.length)));
|
|
3126
3295
|
for (const p of stopped) {
|
|
3127
3296
|
const elapsed = formatElapsedSeconds(Math.floor((Date.now() - p.startedAt) / 1e3));
|
|
3128
3297
|
const exitInfo = p.exitCode !== void 0 ? ` exit ${p.exitCode}` : "";
|
|
@@ -3130,38 +3299,38 @@ Usage: /density <cozy|spacious>`
|
|
|
3130
3299
|
}
|
|
3131
3300
|
}
|
|
3132
3301
|
if (all.length === 0) {
|
|
3133
|
-
lines.push("
|
|
3302
|
+
lines.push(t("tasksNone"));
|
|
3134
3303
|
}
|
|
3135
3304
|
return { nextState: state, output: lines.join("\n") };
|
|
3136
3305
|
}
|
|
3137
3306
|
case "/stop": {
|
|
3138
3307
|
const registry = getProcessRegistry();
|
|
3139
3308
|
if (!argText) {
|
|
3140
|
-
return { nextState: state, output: "
|
|
3309
|
+
return { nextState: state, output: t("stopUsage") };
|
|
3141
3310
|
}
|
|
3142
3311
|
if (argText === "all") {
|
|
3143
3312
|
const before = registry.list().filter((p) => p.status === "running").length;
|
|
3144
3313
|
registry.stopAll();
|
|
3145
|
-
return { nextState: state, output:
|
|
3314
|
+
return { nextState: state, output: t("stopAllDone", String(before)) };
|
|
3146
3315
|
}
|
|
3147
3316
|
if (/^\d+$/.test(argText)) {
|
|
3148
3317
|
const pid = parseInt(argText, 10);
|
|
3149
3318
|
const proc = registry.get(pid);
|
|
3150
3319
|
if (!proc || proc.status !== "running") {
|
|
3151
|
-
return { nextState: state, output:
|
|
3320
|
+
return { nextState: state, output: t("stopNoPid", String(pid)) };
|
|
3152
3321
|
}
|
|
3153
3322
|
registry.kill(pid);
|
|
3154
|
-
return { nextState: state, output:
|
|
3323
|
+
return { nextState: state, output: t("stopPidDone", String(pid), proc.label) };
|
|
3155
3324
|
}
|
|
3156
3325
|
const matches = registry.list().filter((p) => p.status === "running" && p.label === argText);
|
|
3157
3326
|
if (matches.length === 0) {
|
|
3158
|
-
return { nextState: state, output:
|
|
3327
|
+
return { nextState: state, output: t("stopNoLabel", argText) };
|
|
3159
3328
|
}
|
|
3160
3329
|
for (const p of matches) {
|
|
3161
3330
|
registry.kill(p.pid);
|
|
3162
3331
|
}
|
|
3163
3332
|
const stoppedNames = matches.map((p) => `pid ${p.pid} (${p.label})`).join(", ");
|
|
3164
|
-
return { nextState: state, output:
|
|
3333
|
+
return { nextState: state, output: t("stopLabelsDone", stoppedNames) };
|
|
3165
3334
|
}
|
|
3166
3335
|
case "/exit":
|
|
3167
3336
|
case "/quit":
|
|
@@ -3185,15 +3354,13 @@ Usage: /density <cozy|spacious>`
|
|
|
3185
3354
|
if (argText) {
|
|
3186
3355
|
return {
|
|
3187
3356
|
nextState: state,
|
|
3188
|
-
output:
|
|
3189
|
-
|
|
3190
|
-
${renderTuiHelp()}`
|
|
3357
|
+
output: t("unknownCommand", trimmed) + renderTuiHelp()
|
|
3191
3358
|
};
|
|
3192
3359
|
}
|
|
3193
3360
|
if (!state.dialogId) {
|
|
3194
3361
|
return {
|
|
3195
3362
|
nextState: state,
|
|
3196
|
-
output: "
|
|
3363
|
+
output: t("compactNothing")
|
|
3197
3364
|
};
|
|
3198
3365
|
}
|
|
3199
3366
|
return {
|
|
@@ -3220,15 +3387,14 @@ ${renderTuiHelp()}`
|
|
|
3220
3387
|
if (argText === "current" || argText === "show") {
|
|
3221
3388
|
return {
|
|
3222
3389
|
nextState: state,
|
|
3223
|
-
output:
|
|
3390
|
+
output: t("agentCurrent", state.agentName, state.agentKey)
|
|
3224
3391
|
};
|
|
3225
3392
|
}
|
|
3226
3393
|
const resolvedTarget = resolveAgentSwitchTarget(argText, resolveCatalogPlatformAgents());
|
|
3227
3394
|
if (!resolvedTarget) {
|
|
3228
3395
|
return {
|
|
3229
3396
|
nextState: state,
|
|
3230
|
-
output:
|
|
3231
|
-
Use /switch, /switch list, /switch minimax-m3, or a full agent key.`
|
|
3397
|
+
output: t("agentUnknown", argText)
|
|
3232
3398
|
};
|
|
3233
3399
|
}
|
|
3234
3400
|
return applyAgentSwitch(state, resolvedTarget);
|
|
@@ -3282,6 +3448,16 @@ Use /switch, /switch list, /switch minimax-m3, or a full agent key.`
|
|
|
3282
3448
|
action: { type: "set-mouse", enabled: argText === "on" }
|
|
3283
3449
|
};
|
|
3284
3450
|
}
|
|
3451
|
+
case "/altscreen": {
|
|
3452
|
+
if (argText !== "on" && argText !== "off") {
|
|
3453
|
+
return { nextState: state, output: t("altscreenUsage") };
|
|
3454
|
+
}
|
|
3455
|
+
return {
|
|
3456
|
+
nextState: state,
|
|
3457
|
+
output: argText === "on" ? t("altscreenOn") : t("altscreenOff"),
|
|
3458
|
+
action: { type: "set-altscreen", enabled: argText === "on" }
|
|
3459
|
+
};
|
|
3460
|
+
}
|
|
3285
3461
|
case "/resume": {
|
|
3286
3462
|
if (!argText) {
|
|
3287
3463
|
return {
|
|
@@ -3311,7 +3487,7 @@ Use /switch, /switch list, /switch minimax-m3, or a full agent key.`
|
|
|
3311
3487
|
if (rest[0] === "attach") {
|
|
3312
3488
|
const docName = rest.slice(1).join(" ").trim();
|
|
3313
3489
|
if (!docName) {
|
|
3314
|
-
return { nextState: state, output: "
|
|
3490
|
+
return { nextState: state, output: t("docAttachUsage") };
|
|
3315
3491
|
}
|
|
3316
3492
|
const attachedDocs = state.attachedDocs.includes(docName) ? state.attachedDocs : [...state.attachedDocs, docName];
|
|
3317
3493
|
return {
|
|
@@ -3321,7 +3497,7 @@ Use /switch, /switch list, /switch minimax-m3, or a full agent key.`
|
|
|
3321
3497
|
}
|
|
3322
3498
|
return {
|
|
3323
3499
|
nextState: state,
|
|
3324
|
-
output: state.attachedDocs.length > 0 ?
|
|
3500
|
+
output: state.attachedDocs.length > 0 ? t("docList", state.attachedDocs.join(", ")) : t("docNone")
|
|
3325
3501
|
};
|
|
3326
3502
|
}
|
|
3327
3503
|
case "/skill": {
|
|
@@ -3329,7 +3505,7 @@ Use /switch, /switch list, /switch minimax-m3, or a full agent key.`
|
|
|
3329
3505
|
if (sub === "attach") {
|
|
3330
3506
|
const skillRef = rest.slice(1).join(" ").trim();
|
|
3331
3507
|
if (!skillRef) {
|
|
3332
|
-
return { nextState: state, output: "
|
|
3508
|
+
return { nextState: state, output: t("skillAttachUsage") };
|
|
3333
3509
|
}
|
|
3334
3510
|
const attachedSkills = state.attachedSkills.includes(skillRef) ? state.attachedSkills : [...state.attachedSkills, skillRef];
|
|
3335
3511
|
return {
|
|
@@ -3340,38 +3516,37 @@ Use /switch, /switch list, /switch minimax-m3, or a full agent key.`
|
|
|
3340
3516
|
if (sub === "detach") {
|
|
3341
3517
|
const skillRef = rest.slice(1).join(" ").trim();
|
|
3342
3518
|
if (!skillRef) {
|
|
3343
|
-
return { nextState: state, output: "
|
|
3519
|
+
return { nextState: state, output: t("skillDetachUsage") };
|
|
3344
3520
|
}
|
|
3345
3521
|
const attachedSkills = state.attachedSkills.filter((s) => s !== skillRef);
|
|
3346
3522
|
return {
|
|
3347
3523
|
nextState: { ...state, attachedSkills },
|
|
3348
|
-
output: state.attachedSkills.includes(skillRef) ?
|
|
3524
|
+
output: state.attachedSkills.includes(skillRef) ? t("skillDetached", skillRef) : t("skillNotAttached", skillRef)
|
|
3349
3525
|
};
|
|
3350
3526
|
}
|
|
3351
3527
|
if (sub === "clear") {
|
|
3352
3528
|
if (state.attachedSkills.length === 0) {
|
|
3353
|
-
return { nextState: state, output: "
|
|
3529
|
+
return { nextState: state, output: t("skillNone") };
|
|
3354
3530
|
}
|
|
3355
3531
|
return {
|
|
3356
3532
|
nextState: { ...state, attachedSkills: [] },
|
|
3357
|
-
output:
|
|
3533
|
+
output: t("skillCleared", String(state.attachedSkills.length))
|
|
3358
3534
|
};
|
|
3359
3535
|
}
|
|
3360
3536
|
return {
|
|
3361
3537
|
nextState: state,
|
|
3362
|
-
output: state.attachedSkills.length > 0 ?
|
|
3363
|
-
Usage: /skill attach <ref> | /skill detach <ref> | /skill clear` : "No skills attached. Use /skill attach <skill-ref> to attach a skill.\nSkill refs can be a dbKey (page-xxx), a skill name (searched in .agents/skills/ then docs/skills/), or a direct path."
|
|
3538
|
+
output: state.attachedSkills.length > 0 ? t("skillList", state.attachedSkills.join(", ")) : t("skillNoneHint")
|
|
3364
3539
|
};
|
|
3365
3540
|
}
|
|
3366
3541
|
case "/customize":
|
|
3367
3542
|
return {
|
|
3368
3543
|
nextState: state,
|
|
3369
|
-
output: "
|
|
3544
|
+
output: t("customizeHint")
|
|
3370
3545
|
};
|
|
3371
3546
|
case "/login":
|
|
3372
3547
|
return {
|
|
3373
3548
|
nextState: state,
|
|
3374
|
-
output: "
|
|
3549
|
+
output: t("loginHint")
|
|
3375
3550
|
};
|
|
3376
3551
|
case "/profile":
|
|
3377
3552
|
return {
|
|
@@ -3387,16 +3562,12 @@ Usage: /skill attach <ref> | /skill detach <ref> | /skill clear` : "No skills at
|
|
|
3387
3562
|
case "/version":
|
|
3388
3563
|
return {
|
|
3389
3564
|
nextState: state,
|
|
3390
|
-
output:
|
|
3391
|
-
Update this install with: nolo update
|
|
3392
|
-
If repo-local output differs, publish/install the latest npm package first.`
|
|
3565
|
+
output: t("versionInfo", state.cliVersion || t("versionUnknown"))
|
|
3393
3566
|
};
|
|
3394
3567
|
default:
|
|
3395
3568
|
return {
|
|
3396
3569
|
nextState: state,
|
|
3397
|
-
output:
|
|
3398
|
-
|
|
3399
|
-
${renderTuiHelp()}`
|
|
3570
|
+
output: t("unknownCommand", command) + renderTuiHelp()
|
|
3400
3571
|
};
|
|
3401
3572
|
}
|
|
3402
3573
|
}
|
|
@@ -3414,19 +3585,23 @@ function brightnessFromRgb(r, g, b) {
|
|
|
3414
3585
|
const luma = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
|
|
3415
3586
|
return luma >= 0.5 ? "light" : "dark";
|
|
3416
3587
|
}
|
|
3417
|
-
function
|
|
3588
|
+
function parseOsc11Background(reply) {
|
|
3418
3589
|
const match = reply.match(OSC11_REPLY_RE);
|
|
3419
3590
|
if (!match) return null;
|
|
3420
|
-
|
|
3591
|
+
const channels = [
|
|
3421
3592
|
componentToByte(match[1]),
|
|
3422
3593
|
componentToByte(match[2]),
|
|
3423
3594
|
componentToByte(match[3])
|
|
3424
|
-
|
|
3595
|
+
];
|
|
3596
|
+
return {
|
|
3597
|
+
brightness: brightnessFromRgb(channels[0], channels[1], channels[2]),
|
|
3598
|
+
hex: channels.map((v) => v.toString(16).padStart(2, "0").toUpperCase()).join("")
|
|
3599
|
+
};
|
|
3425
3600
|
}
|
|
3426
3601
|
function isTerminalDevice(stream) {
|
|
3427
3602
|
return stream instanceof ReadStream;
|
|
3428
3603
|
}
|
|
3429
|
-
async function
|
|
3604
|
+
async function detectTerminalBackground(args = {}) {
|
|
3430
3605
|
const stdin = args.stdin ?? process.stdin;
|
|
3431
3606
|
const stdout = args.stdout ?? process.stdout;
|
|
3432
3607
|
const timeoutMs = args.timeoutMs ?? 100;
|
|
@@ -3453,7 +3628,7 @@ async function detectTerminalBrightness(args = {}) {
|
|
|
3453
3628
|
};
|
|
3454
3629
|
const onData = (chunk) => {
|
|
3455
3630
|
buffer += chunk.toString("latin1");
|
|
3456
|
-
const parsed =
|
|
3631
|
+
const parsed = parseOsc11Background(buffer);
|
|
3457
3632
|
if (parsed) finish(parsed);
|
|
3458
3633
|
else if (buffer.length > 256) finish(null);
|
|
3459
3634
|
};
|
|
@@ -3747,6 +3922,7 @@ function createChatQueueTuiBinding(runTurn) {
|
|
|
3747
3922
|
|
|
3748
3923
|
// packages/cli/tui/tuiHistory.ts
|
|
3749
3924
|
var MAX_TUI_HISTORY_TURNS = 500;
|
|
3925
|
+
var turnLineCache = /* @__PURE__ */ new WeakMap();
|
|
3750
3926
|
function createTurnHistory() {
|
|
3751
3927
|
return {
|
|
3752
3928
|
turns: [],
|
|
@@ -3788,45 +3964,81 @@ function applyOutputChunkToCurrentTurn(history, chunk) {
|
|
|
3788
3964
|
history.currentContent = next;
|
|
3789
3965
|
return true;
|
|
3790
3966
|
}
|
|
3791
|
-
function
|
|
3792
|
-
const
|
|
3793
|
-
const
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
const
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
const anchorPrefix = colorEnabled ? `${themeColorSequence("chrome")}\u25C8\x1B[39m ` : "\u25C8 ";
|
|
3805
|
-
return `${anchorPrefix}${line}`;
|
|
3806
|
-
}
|
|
3807
|
-
return line.startsWith("[nolo]") && colorEnabled ? themeText(line, "chrome", true) : line;
|
|
3808
|
-
});
|
|
3809
|
-
return styledLines.join("\n");
|
|
3810
|
-
};
|
|
3967
|
+
function styleAssistantTurn(content, colorEnabled) {
|
|
3968
|
+
const highlighted = colorEnabled ? formatAssistantDisplay(content) : stripAnsi(formatAssistantDisplay(content));
|
|
3969
|
+
const rawLines = highlighted.split("\n");
|
|
3970
|
+
const styledLines = rawLines.map((line, idx) => {
|
|
3971
|
+
if (idx === 0 && !line.startsWith("[nolo]")) {
|
|
3972
|
+
const anchorPrefix = colorEnabled ? `${themeColorSequence("chrome")}\u25C8\x1B[39m ` : "\u25C8 ";
|
|
3973
|
+
return `${anchorPrefix}${line}`;
|
|
3974
|
+
}
|
|
3975
|
+
return line.startsWith("[nolo]") && colorEnabled ? themeText(line, "chrome", true) : line;
|
|
3976
|
+
});
|
|
3977
|
+
return styledLines.join("\n");
|
|
3978
|
+
}
|
|
3979
|
+
function renderTurnBlock(role, content, contentWidth, colorEnabled) {
|
|
3811
3980
|
const lines = [];
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3981
|
+
if (role === "user") {
|
|
3982
|
+
const logicalLines = content.split("\n");
|
|
3983
|
+
const accentSeq = colorEnabled ? themeColorSequence("accent") : "";
|
|
3984
|
+
const chromeSeq = colorEnabled ? themeColorSequence("chrome") : "";
|
|
3985
|
+
for (let i = 0; i < logicalLines.length; i++) {
|
|
3986
|
+
const line = logicalLines[i];
|
|
3987
|
+
let styledLine;
|
|
3988
|
+
if (colorEnabled) {
|
|
3989
|
+
const prefix = i === 0 ? `${accentSeq}\u276F\x1B[39m ` : `${chromeSeq}\u2502\x1B[39m `;
|
|
3990
|
+
styledLine = `${prefix}${line}`;
|
|
3991
|
+
} else {
|
|
3992
|
+
const prefix = i === 0 ? "\u276F " : " ";
|
|
3993
|
+
styledLine = `${prefix}${line}`;
|
|
3994
|
+
}
|
|
3995
|
+
lines.push(...wrapTranscriptLine(styledLine, contentWidth, " "));
|
|
3996
|
+
}
|
|
3997
|
+
} else {
|
|
3998
|
+
const styledEntry = styleAssistantTurn(content, colorEnabled);
|
|
3999
|
+
for (const logicalLine of styledEntry.split("\n")) {
|
|
4000
|
+
lines.push(...wrapTranscriptLine(logicalLine, contentWidth));
|
|
3815
4001
|
}
|
|
3816
|
-
lines.push(styleTurn(role, content));
|
|
3817
|
-
};
|
|
3818
|
-
for (const turn of history.turns) {
|
|
3819
|
-
pushTurn(turn.role, turn.content);
|
|
3820
|
-
}
|
|
3821
|
-
if (history.currentRole !== null && history.currentContent) {
|
|
3822
|
-
pushTurn(history.currentRole, history.currentContent);
|
|
3823
4002
|
}
|
|
4003
|
+
return lines;
|
|
4004
|
+
}
|
|
4005
|
+
function buildHistoryLines(history, contentWidth) {
|
|
4006
|
+
const colorEnabled = resolveCliColorEnabled();
|
|
4007
|
+
const density = getActiveDensity();
|
|
3824
4008
|
const wrapped = [];
|
|
3825
|
-
for (
|
|
3826
|
-
|
|
3827
|
-
|
|
4009
|
+
for (let i = 0; i < history.turns.length; i++) {
|
|
4010
|
+
const turn = history.turns[i];
|
|
4011
|
+
if (density === "spacious" && (i > 0 || turn.role === "user")) {
|
|
4012
|
+
wrapped.push("");
|
|
4013
|
+
}
|
|
4014
|
+
const cached = turnLineCache.get(turn);
|
|
4015
|
+
if (cached && cached.width === contentWidth && cached.color === colorEnabled && cached.density === density) {
|
|
4016
|
+
wrapped.push(...cached.lines);
|
|
4017
|
+
} else {
|
|
4018
|
+
const lines = renderTurnBlock(turn.role, turn.content, contentWidth, colorEnabled);
|
|
4019
|
+
turnLineCache.set(turn, {
|
|
4020
|
+
width: contentWidth,
|
|
4021
|
+
color: colorEnabled,
|
|
4022
|
+
density,
|
|
4023
|
+
lines
|
|
4024
|
+
});
|
|
4025
|
+
wrapped.push(...lines);
|
|
3828
4026
|
}
|
|
3829
4027
|
}
|
|
4028
|
+
if (history.currentRole !== null && history.currentContent) {
|
|
4029
|
+
const i = history.turns.length;
|
|
4030
|
+
if (density === "spacious" && (i > 0 || history.currentRole === "user")) {
|
|
4031
|
+
wrapped.push("");
|
|
4032
|
+
}
|
|
4033
|
+
wrapped.push(
|
|
4034
|
+
...renderTurnBlock(
|
|
4035
|
+
history.currentRole,
|
|
4036
|
+
history.currentContent,
|
|
4037
|
+
contentWidth,
|
|
4038
|
+
colorEnabled
|
|
4039
|
+
)
|
|
4040
|
+
);
|
|
4041
|
+
}
|
|
3830
4042
|
return wrapped;
|
|
3831
4043
|
}
|
|
3832
4044
|
function renderHistory(output, history, inputLines) {
|
|
@@ -3924,6 +4136,38 @@ function applyScrollAction(history, action, output, inputLines) {
|
|
|
3924
4136
|
}
|
|
3925
4137
|
|
|
3926
4138
|
// packages/cli/tui/tuiRawInput.ts
|
|
4139
|
+
function normalizePasteNewlines(text) {
|
|
4140
|
+
return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
4141
|
+
}
|
|
4142
|
+
var altScreenOn = /* @__PURE__ */ new WeakMap();
|
|
4143
|
+
function outputIsTty2(output) {
|
|
4144
|
+
return Boolean(output.isTTY);
|
|
4145
|
+
}
|
|
4146
|
+
function enterAltScreen(output) {
|
|
4147
|
+
if (!outputIsTty2(output)) return false;
|
|
4148
|
+
if (altScreenOn.get(output)) return false;
|
|
4149
|
+
altScreenOn.set(output, true);
|
|
4150
|
+
output.write("\x1B[?1049h");
|
|
4151
|
+
return true;
|
|
4152
|
+
}
|
|
4153
|
+
function leaveAltScreen(output) {
|
|
4154
|
+
if (!outputIsTty2(output)) return false;
|
|
4155
|
+
if (!altScreenOn.get(output)) return false;
|
|
4156
|
+
altScreenOn.set(output, false);
|
|
4157
|
+
output.write("\x1B[?1049l");
|
|
4158
|
+
return true;
|
|
4159
|
+
}
|
|
4160
|
+
function isAltScreenOn(output) {
|
|
4161
|
+
return Boolean(altScreenOn.get(output));
|
|
4162
|
+
}
|
|
4163
|
+
function styleCollapsedPastePlaceholders(text, colorEnabled) {
|
|
4164
|
+
if (!colorEnabled || !text.includes("[paste #")) return text;
|
|
4165
|
+
COLLAPSED_PASTE_PLACEHOLDER_RE.lastIndex = 0;
|
|
4166
|
+
return text.replace(
|
|
4167
|
+
COLLAPSED_PASTE_PLACEHOLDER_RE,
|
|
4168
|
+
(match) => themeText(match, "accent", true)
|
|
4169
|
+
);
|
|
4170
|
+
}
|
|
3927
4171
|
function createNoopFixedInput() {
|
|
3928
4172
|
return {
|
|
3929
4173
|
active: false,
|
|
@@ -3946,6 +4190,8 @@ function createNoopFixedInput() {
|
|
|
3946
4190
|
getInputLines: () => 1,
|
|
3947
4191
|
isPaused: () => false,
|
|
3948
4192
|
setMouseEnabled() {
|
|
4193
|
+
},
|
|
4194
|
+
setAltScreenEnabled() {
|
|
3949
4195
|
}
|
|
3950
4196
|
};
|
|
3951
4197
|
}
|
|
@@ -3977,9 +4223,10 @@ function createFixedInput(output, config) {
|
|
|
3977
4223
|
if (completions.length > 0) {
|
|
3978
4224
|
sections.push(fitAnsiLine(dimCliText(completions.join(" "), colorEnabled), cols));
|
|
3979
4225
|
}
|
|
3980
|
-
const
|
|
3981
|
-
|
|
3982
|
-
|
|
4226
|
+
const rawActivity = config.getActivityLines ? config.getActivityLines() : config.getActivityLine ? config.getActivityLine() : null;
|
|
4227
|
+
const activityLines = Array.isArray(rawActivity) ? rawActivity.filter((line) => line !== null && line !== void 0) : typeof rawActivity === "string" && rawActivity.length > 0 ? [rawActivity] : [];
|
|
4228
|
+
for (const actLine of activityLines) {
|
|
4229
|
+
sections.push(fitAnsiLine(actLine.replace(/\r?\n/g, " "), cols));
|
|
3983
4230
|
}
|
|
3984
4231
|
const rule = colorEnabled ? `\x1B[2m${themeColorSequence("chrome")}${"\u2500".repeat(cols)}\x1B[0m` : "\u2500".repeat(cols);
|
|
3985
4232
|
sections.push(rule);
|
|
@@ -3989,7 +4236,7 @@ function createFixedInput(output, config) {
|
|
|
3989
4236
|
sections.push(fitAnsiLine(line.replace(/\r?\n/g, " "), cols));
|
|
3990
4237
|
}
|
|
3991
4238
|
const promptRaw = t("promptLabel");
|
|
3992
|
-
const promptWidth =
|
|
4239
|
+
const promptWidth = displayWidth(promptRaw);
|
|
3993
4240
|
const prompt = colorEnabled ? `${themeColorSequence("accent")}${promptRaw}\x1B[39m` : promptRaw;
|
|
3994
4241
|
const contentWidth = Math.max(1, cols - promptWidth);
|
|
3995
4242
|
const logicalLines = buffer.length === 0 ? [""] : buffer.split("\n");
|
|
@@ -4017,13 +4264,15 @@ function createFixedInput(output, config) {
|
|
|
4017
4264
|
for (let j = 0; j < rows.length; j += 1) {
|
|
4018
4265
|
const rowPrefix = j === 0 ? prefix : " ".repeat(promptWidth);
|
|
4019
4266
|
const rowText = rows[j] ?? "";
|
|
4020
|
-
sections.push(
|
|
4267
|
+
sections.push(
|
|
4268
|
+
`${rowPrefix}${styleCollapsedPastePlaceholders(rowText, colorEnabled)}`
|
|
4269
|
+
);
|
|
4021
4270
|
const rowLen = rowText.length;
|
|
4022
4271
|
if (!cursorFound) {
|
|
4023
4272
|
if (targetPos <= charOffset + rowLen || j === rows.length - 1 && i === logicalLines.length - 1) {
|
|
4024
4273
|
const subOffset = Math.max(0, Math.min(rowLen, targetPos - charOffset));
|
|
4025
4274
|
const subStr = rowText.slice(0, subOffset);
|
|
4026
|
-
cursorCol = promptWidth +
|
|
4275
|
+
cursorCol = promptWidth + displayWidth(subStr);
|
|
4027
4276
|
cursorRow = inputRows;
|
|
4028
4277
|
cursorFound = true;
|
|
4029
4278
|
}
|
|
@@ -4035,15 +4284,20 @@ function createFixedInput(output, config) {
|
|
|
4035
4284
|
}
|
|
4036
4285
|
const text = sections.join("\n");
|
|
4037
4286
|
const lines = sections.length;
|
|
4038
|
-
const headerRows = (completions.length > 0 ? 1 : 0) + 2 +
|
|
4287
|
+
const headerRows = (completions.length > 0 ? 1 : 0) + 2 + activityLines.length + queueLines.length;
|
|
4039
4288
|
return { text, lines, cursorCol, cursorRow: headerRows + cursorRow };
|
|
4040
4289
|
};
|
|
4041
4290
|
const repaintAt = (buffer, cursorPos) => {
|
|
4042
4291
|
const { text, lines, cursorCol, cursorRow } = renderInputArea(buffer, cursorPos);
|
|
4292
|
+
let linesChanged = false;
|
|
4043
4293
|
if (lines !== inputLines) {
|
|
4044
4294
|
inputLines = lines;
|
|
4295
|
+
linesChanged = true;
|
|
4045
4296
|
}
|
|
4046
4297
|
setScrollRegion(inputLines);
|
|
4298
|
+
if (linesChanged) {
|
|
4299
|
+
config.onInputLinesChange?.(inputLines);
|
|
4300
|
+
}
|
|
4047
4301
|
const startRow = getRows() - inputLines + 1;
|
|
4048
4302
|
write(`\x1B[${startRow};1H`);
|
|
4049
4303
|
write("\x1B[J");
|
|
@@ -4091,8 +4345,7 @@ function createFixedInput(output, config) {
|
|
|
4091
4345
|
disable() {
|
|
4092
4346
|
disableMouse();
|
|
4093
4347
|
resetScrollRegion();
|
|
4094
|
-
|
|
4095
|
-
write(`\x1B[${rows};1H\x1B[2K\x1B[${Math.max(1, rows - 1)};1H`);
|
|
4348
|
+
leaveAltScreen(output);
|
|
4096
4349
|
},
|
|
4097
4350
|
getInputLines: () => inputLines,
|
|
4098
4351
|
isPaused: () => paused,
|
|
@@ -4103,6 +4356,13 @@ function createFixedInput(output, config) {
|
|
|
4103
4356
|
} else {
|
|
4104
4357
|
write("\x1B[?1000l\x1B[?1006l");
|
|
4105
4358
|
}
|
|
4359
|
+
},
|
|
4360
|
+
setAltScreenEnabled(enabled) {
|
|
4361
|
+
if (enabled) {
|
|
4362
|
+
enterAltScreen(output);
|
|
4363
|
+
} else {
|
|
4364
|
+
leaveAltScreen(output);
|
|
4365
|
+
}
|
|
4106
4366
|
}
|
|
4107
4367
|
};
|
|
4108
4368
|
}
|
|
@@ -4152,9 +4412,27 @@ function isIncompleteTail(input, start) {
|
|
|
4152
4412
|
}
|
|
4153
4413
|
return false;
|
|
4154
4414
|
}
|
|
4155
|
-
var PASTE_START = "\x1B[
|
|
4156
|
-
var PASTE_END = "\x1B[
|
|
4157
|
-
function
|
|
4415
|
+
var PASTE_START = "\x1B[200~";
|
|
4416
|
+
var PASTE_END = "\x1B[201~";
|
|
4417
|
+
function findBracketedPasteEnd(input, contentStart) {
|
|
4418
|
+
return input.indexOf(PASTE_END, contentStart);
|
|
4419
|
+
}
|
|
4420
|
+
function emitPlainRunAsTokens(run) {
|
|
4421
|
+
if (run.length === 0) return [];
|
|
4422
|
+
if (shouldCollapsePaste(normalizePasteNewlines(run))) {
|
|
4423
|
+
return [`${PASTE_TOKEN_PREFIX}${run}`];
|
|
4424
|
+
}
|
|
4425
|
+
const tokens = [];
|
|
4426
|
+
for (let index = 0; index < run.length; ) {
|
|
4427
|
+
const codePoint = run.codePointAt(index);
|
|
4428
|
+
if (codePoint === void 0) break;
|
|
4429
|
+
const value = String.fromCodePoint(codePoint);
|
|
4430
|
+
tokens.push(value);
|
|
4431
|
+
index += value.length;
|
|
4432
|
+
}
|
|
4433
|
+
return tokens;
|
|
4434
|
+
}
|
|
4435
|
+
function splitRawInputWithTail(input, options) {
|
|
4158
4436
|
const tokens = [];
|
|
4159
4437
|
for (let index = 0; index < input.length; ) {
|
|
4160
4438
|
if (isIncompleteTail(input, index)) {
|
|
@@ -4162,15 +4440,17 @@ function splitRawInputWithTail(input) {
|
|
|
4162
4440
|
}
|
|
4163
4441
|
if (input.startsWith(PASTE_START, index)) {
|
|
4164
4442
|
const contentStart = index + PASTE_START.length;
|
|
4165
|
-
const endPos = input
|
|
4443
|
+
const endPos = findBracketedPasteEnd(input, contentStart);
|
|
4166
4444
|
if (endPos !== -1) {
|
|
4167
4445
|
const payload = input.slice(contentStart, endPos);
|
|
4168
4446
|
tokens.push(`${PASTE_TOKEN_PREFIX}${payload}`);
|
|
4169
4447
|
index = endPos + PASTE_END.length;
|
|
4170
|
-
} else {
|
|
4448
|
+
} else if (options?.forceCompleteOpenPaste) {
|
|
4171
4449
|
const payload = input.slice(contentStart);
|
|
4172
4450
|
tokens.push(`${PASTE_TOKEN_PREFIX}${payload}`);
|
|
4173
4451
|
index = input.length;
|
|
4452
|
+
} else {
|
|
4453
|
+
return { tokens, tail: input.slice(index) };
|
|
4174
4454
|
}
|
|
4175
4455
|
continue;
|
|
4176
4456
|
}
|
|
@@ -4195,63 +4475,121 @@ function splitRawInputWithTail(input) {
|
|
|
4195
4475
|
index += csi.length;
|
|
4196
4476
|
continue;
|
|
4197
4477
|
}
|
|
4198
|
-
const
|
|
4199
|
-
|
|
4200
|
-
const
|
|
4201
|
-
|
|
4202
|
-
|
|
4478
|
+
const nextEsc = input.indexOf("\x1B", index);
|
|
4479
|
+
const runEnd = nextEsc === -1 ? input.length : nextEsc;
|
|
4480
|
+
const run = input.slice(index, runEnd);
|
|
4481
|
+
if (run.length === 0) {
|
|
4482
|
+
const codePoint = input.codePointAt(index);
|
|
4483
|
+
if (codePoint === void 0) break;
|
|
4484
|
+
const value = String.fromCodePoint(codePoint);
|
|
4485
|
+
tokens.push(value);
|
|
4486
|
+
index += value.length;
|
|
4487
|
+
continue;
|
|
4488
|
+
}
|
|
4489
|
+
tokens.push(...emitPlainRunAsTokens(run));
|
|
4490
|
+
index = runEnd;
|
|
4203
4491
|
}
|
|
4204
4492
|
return { tokens, tail: "" };
|
|
4205
4493
|
}
|
|
4206
4494
|
function splitRawInput(input) {
|
|
4207
|
-
const { tokens, tail } = splitRawInputWithTail(input
|
|
4495
|
+
const { tokens, tail } = splitRawInputWithTail(input, {
|
|
4496
|
+
forceCompleteOpenPaste: true
|
|
4497
|
+
});
|
|
4208
4498
|
if (!tail) return tokens;
|
|
4209
|
-
|
|
4210
|
-
for (let index = 0; index < tail.length; ) {
|
|
4211
|
-
const codePoint = tail.codePointAt(index);
|
|
4212
|
-
if (codePoint === void 0) break;
|
|
4213
|
-
const value = String.fromCodePoint(codePoint);
|
|
4214
|
-
tailTokens.push(value);
|
|
4215
|
-
index += value.length;
|
|
4216
|
-
}
|
|
4217
|
-
return [...tokens, ...tailTokens];
|
|
4499
|
+
return [...tokens, ...emitPlainRunAsTokens(tail)];
|
|
4218
4500
|
}
|
|
4219
4501
|
function createRawInputDecoder(onToken, options) {
|
|
4220
4502
|
const decoder = new TextDecoder("utf-8", { fatal: false, ignoreBOM: true });
|
|
4221
4503
|
let pendingBuffer = "";
|
|
4222
4504
|
let timer = null;
|
|
4223
4505
|
const timeoutMs = options?.escTimeoutMs ?? 15;
|
|
4506
|
+
const unmarkedPasteDebounceMs = options?.unmarkedPasteDebounceMs ?? 40;
|
|
4224
4507
|
const clearTimer = () => {
|
|
4225
4508
|
if (timer !== null) {
|
|
4226
4509
|
clearTimeout(timer);
|
|
4227
4510
|
timer = null;
|
|
4228
4511
|
}
|
|
4229
4512
|
};
|
|
4230
|
-
const
|
|
4513
|
+
const emitCodePoints = (text) => {
|
|
4514
|
+
for (let index = 0; index < text.length; ) {
|
|
4515
|
+
const codePoint = text.codePointAt(index);
|
|
4516
|
+
if (codePoint === void 0) break;
|
|
4517
|
+
const value = String.fromCodePoint(codePoint);
|
|
4518
|
+
onToken(value);
|
|
4519
|
+
index += value.length;
|
|
4520
|
+
}
|
|
4521
|
+
};
|
|
4522
|
+
const flushPlainPending = () => {
|
|
4231
4523
|
clearTimer();
|
|
4232
|
-
if (pendingBuffer.length
|
|
4233
|
-
|
|
4234
|
-
|
|
4235
|
-
|
|
4236
|
-
|
|
4237
|
-
|
|
4524
|
+
if (pendingBuffer.length === 0) return;
|
|
4525
|
+
const text = pendingBuffer;
|
|
4526
|
+
pendingBuffer = "";
|
|
4527
|
+
for (const token of emitPlainRunAsTokens(text)) {
|
|
4528
|
+
onToken(token);
|
|
4529
|
+
}
|
|
4530
|
+
};
|
|
4531
|
+
const flushEscPending = (forceCompleteOpenPaste) => {
|
|
4532
|
+
clearTimer();
|
|
4533
|
+
if (pendingBuffer.length === 0) return;
|
|
4534
|
+
const textToFlush = pendingBuffer;
|
|
4535
|
+
pendingBuffer = "";
|
|
4536
|
+
const { tokens, tail } = splitRawInputWithTail(textToFlush, {
|
|
4537
|
+
forceCompleteOpenPaste
|
|
4538
|
+
});
|
|
4539
|
+
for (const token of tokens) {
|
|
4540
|
+
onToken(token);
|
|
4541
|
+
}
|
|
4542
|
+
if (!tail) return;
|
|
4543
|
+
if (forceCompleteOpenPaste) {
|
|
4544
|
+
emitCodePoints(tail);
|
|
4545
|
+
return;
|
|
4238
4546
|
}
|
|
4547
|
+
pendingBuffer = tail;
|
|
4239
4548
|
};
|
|
4240
4549
|
const decodeFn = (chunk) => {
|
|
4241
4550
|
clearTimer();
|
|
4242
4551
|
const bytes = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
|
|
4243
4552
|
const text = decoder.decode(bytes, { stream: true });
|
|
4244
4553
|
pendingBuffer += text;
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4554
|
+
if (pendingBuffer.startsWith(PASTE_START)) {
|
|
4555
|
+
const endPos = findBracketedPasteEnd(
|
|
4556
|
+
pendingBuffer,
|
|
4557
|
+
PASTE_START.length
|
|
4558
|
+
);
|
|
4559
|
+
if (endPos === -1) return;
|
|
4560
|
+
const { tokens, tail } = splitRawInputWithTail(pendingBuffer);
|
|
4561
|
+
pendingBuffer = tail;
|
|
4562
|
+
for (const token of tokens) onToken(token);
|
|
4563
|
+
if (pendingBuffer.startsWith(PASTE_START)) return;
|
|
4564
|
+
}
|
|
4565
|
+
if (pendingBuffer.includes("\x1B")) {
|
|
4566
|
+
const { tokens, tail } = splitRawInputWithTail(pendingBuffer);
|
|
4567
|
+
pendingBuffer = tail;
|
|
4568
|
+
for (const token of tokens) onToken(token);
|
|
4569
|
+
if (pendingBuffer.startsWith(PASTE_START)) return;
|
|
4570
|
+
if (pendingBuffer.length > 0) {
|
|
4571
|
+
timer = setTimeout(() => flushEscPending(true), timeoutMs);
|
|
4572
|
+
}
|
|
4573
|
+
return;
|
|
4574
|
+
}
|
|
4575
|
+
if (shouldCollapsePaste(normalizePasteNewlines(pendingBuffer))) {
|
|
4576
|
+
timer = setTimeout(flushPlainPending, unmarkedPasteDebounceMs);
|
|
4577
|
+
return;
|
|
4249
4578
|
}
|
|
4250
|
-
if (pendingBuffer.
|
|
4251
|
-
|
|
4579
|
+
if (!pendingBuffer.includes("\n") && !pendingBuffer.includes("\r")) {
|
|
4580
|
+
emitCodePoints(pendingBuffer);
|
|
4581
|
+
pendingBuffer = "";
|
|
4582
|
+
return;
|
|
4252
4583
|
}
|
|
4584
|
+
timer = setTimeout(flushPlainPending, unmarkedPasteDebounceMs);
|
|
4585
|
+
};
|
|
4586
|
+
decodeFn.flush = () => {
|
|
4587
|
+
if (pendingBuffer.startsWith(PASTE_START) || pendingBuffer.includes("\x1B")) {
|
|
4588
|
+
flushEscPending(true);
|
|
4589
|
+
return;
|
|
4590
|
+
}
|
|
4591
|
+
flushPlainPending();
|
|
4253
4592
|
};
|
|
4254
|
-
decodeFn.flush = flush;
|
|
4255
4593
|
decodeFn.destroy = () => {
|
|
4256
4594
|
clearTimer();
|
|
4257
4595
|
pendingBuffer = "";
|
|
@@ -4261,12 +4599,66 @@ function createRawInputDecoder(onToken, options) {
|
|
|
4261
4599
|
|
|
4262
4600
|
// packages/cli/tui/readlineWorkspace.ts
|
|
4263
4601
|
var AGENTS_MD_MAX_BYTES = 8192;
|
|
4602
|
+
var altScreenRestoreOutput = null;
|
|
4603
|
+
var altScreenHandlersInstalled = false;
|
|
4604
|
+
var preExistingSignalListeners = {};
|
|
4605
|
+
var SIGNAL_EXIT_CODE = {
|
|
4606
|
+
SIGINT: 130,
|
|
4607
|
+
SIGTERM: 143,
|
|
4608
|
+
SIGHUP: 129
|
|
4609
|
+
};
|
|
4610
|
+
var restoreAltScreen = () => {
|
|
4611
|
+
try {
|
|
4612
|
+
if (altScreenRestoreOutput) leaveAltScreen(altScreenRestoreOutput);
|
|
4613
|
+
} catch {
|
|
4614
|
+
}
|
|
4615
|
+
};
|
|
4616
|
+
function installAltScreenRestoreHandlers(output) {
|
|
4617
|
+
altScreenRestoreOutput = output;
|
|
4618
|
+
if (altScreenHandlersInstalled) return;
|
|
4619
|
+
altScreenHandlersInstalled = true;
|
|
4620
|
+
process.on("exit", restoreAltScreen);
|
|
4621
|
+
for (const sig of ["SIGINT", "SIGTERM", "SIGHUP"]) {
|
|
4622
|
+
const existing = process.listeners(sig);
|
|
4623
|
+
preExistingSignalListeners[sig] = [...existing];
|
|
4624
|
+
const handler = () => {
|
|
4625
|
+
restoreAltScreen();
|
|
4626
|
+
const others = process.listeners(sig).filter((fn) => fn !== handler);
|
|
4627
|
+
if (others.length === 0) {
|
|
4628
|
+
process.exit(SIGNAL_EXIT_CODE[sig] ?? 128 + 1);
|
|
4629
|
+
return;
|
|
4630
|
+
}
|
|
4631
|
+
};
|
|
4632
|
+
process.prependListener(sig, handler);
|
|
4633
|
+
}
|
|
4634
|
+
process.on("uncaughtException", (err) => {
|
|
4635
|
+
restoreAltScreen();
|
|
4636
|
+
try {
|
|
4637
|
+
process.stderr.write(
|
|
4638
|
+
`uncaughtException: ${err?.stack ?? String(err)}
|
|
4639
|
+
`
|
|
4640
|
+
);
|
|
4641
|
+
} catch {
|
|
4642
|
+
}
|
|
4643
|
+
process.exit(1);
|
|
4644
|
+
});
|
|
4645
|
+
process.on("unhandledRejection", (reason) => {
|
|
4646
|
+
restoreAltScreen();
|
|
4647
|
+
try {
|
|
4648
|
+
const text = reason instanceof Error ? reason.stack ?? String(reason) : String(reason);
|
|
4649
|
+
process.stderr.write(`unhandledRejection: ${text}
|
|
4650
|
+
`);
|
|
4651
|
+
} catch {
|
|
4652
|
+
}
|
|
4653
|
+
process.exit(1);
|
|
4654
|
+
});
|
|
4655
|
+
}
|
|
4264
4656
|
function readAgentsMdBlock(cwd) {
|
|
4265
4657
|
for (const name of ["AGENTS.md", "CLAUDE.md"]) {
|
|
4266
|
-
const filePath =
|
|
4267
|
-
if (
|
|
4658
|
+
const filePath = join2(cwd, name);
|
|
4659
|
+
if (existsSync3(filePath)) {
|
|
4268
4660
|
try {
|
|
4269
|
-
let content =
|
|
4661
|
+
let content = readFileSync2(filePath, "utf8").trim();
|
|
4270
4662
|
if (!content) continue;
|
|
4271
4663
|
if (Buffer.byteLength(content, "utf8") > AGENTS_MD_MAX_BYTES) {
|
|
4272
4664
|
content = Buffer.from(content, "utf8").subarray(0, AGENTS_MD_MAX_BYTES).toString("utf8") + "\n\n<!-- AGENTS.md truncated -->";
|
|
@@ -4290,10 +4682,14 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
|
|
|
4290
4682
|
effectiveAgentName = cachedRoute.agentName;
|
|
4291
4683
|
} else if (!continueId && env.NOLO_AUTO_ROUTE !== "0" && state.agentKey === DEFAULT_TUI_AGENT_KEY) {
|
|
4292
4684
|
const authToken = env.AUTH_TOKEN ?? env.AUTH ?? env.BENCHMARK_AUTH_TOKEN ?? "";
|
|
4293
|
-
const route = await classifyCliAutoRoute(message, {
|
|
4685
|
+
const route = authToken ? await classifyCliAutoRoute(message, {
|
|
4294
4686
|
serverUrl: state.serverUrl,
|
|
4295
4687
|
authToken
|
|
4296
|
-
})
|
|
4688
|
+
}) : {
|
|
4689
|
+
agentKey: CLI_AUTO_TIER_AGENT_KEYS[resolveCliAutoFallbackTier(message)],
|
|
4690
|
+
tier: resolveCliAutoFallbackTier(message),
|
|
4691
|
+
classified: false
|
|
4692
|
+
};
|
|
4297
4693
|
effectiveAgentKey = route.agentKey;
|
|
4298
4694
|
effectiveAgentName = `auto\u2192${route.tier}`;
|
|
4299
4695
|
if (effectiveAgentKey !== state.agentKey) {
|
|
@@ -4381,6 +4777,18 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
|
|
|
4381
4777
|
if (skillDiscoveryBlock) {
|
|
4382
4778
|
extraContextBlocks.push(skillDiscoveryBlock);
|
|
4383
4779
|
}
|
|
4780
|
+
const agentsMdMarker = "--- \u9879\u76EE\u6307\u4EE4\uFF08";
|
|
4781
|
+
const contextBlockScopes = [];
|
|
4782
|
+
for (const block of extraContextBlocks) {
|
|
4783
|
+
if (block.startsWith(agentsMdMarker)) {
|
|
4784
|
+
contextBlockScopes.push({ content: block, cacheScope: "session" });
|
|
4785
|
+
}
|
|
4786
|
+
}
|
|
4787
|
+
for (const block of extraContextBlocks) {
|
|
4788
|
+
if (!block.startsWith(agentsMdMarker)) {
|
|
4789
|
+
contextBlockScopes.push({ content: block, cacheScope: "turn" });
|
|
4790
|
+
}
|
|
4791
|
+
}
|
|
4384
4792
|
const result = await agentRunner({
|
|
4385
4793
|
agentName: effectiveAgentName,
|
|
4386
4794
|
agentKey: effectiveAgentKey,
|
|
@@ -4401,9 +4809,17 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
|
|
|
4401
4809
|
...options.confirmDestructiveAction ? { confirmDestructiveAction: options.confirmDestructiveAction } : {},
|
|
4402
4810
|
...options.requestUserChoice ? { requestUserChoice: options.requestUserChoice } : {},
|
|
4403
4811
|
...options.abortSignal ? { abortSignal: options.abortSignal } : {},
|
|
4812
|
+
...options.pastedTextStore?.items.size ? {
|
|
4813
|
+
pastedTextStore: options.pastedTextStore,
|
|
4814
|
+
localRuntimeAdapterFactory: (factoryEnv, factoryOptions) => createCliLocalRuntimeAdapter({
|
|
4815
|
+
env: factoryEnv,
|
|
4816
|
+
cwd: factoryOptions?.cwd ?? state.cwd,
|
|
4817
|
+
pastedTextStore: options.pastedTextStore
|
|
4818
|
+
})
|
|
4819
|
+
} : {},
|
|
4404
4820
|
...options.activityReporter ? { activityReporter: options.activityReporter } : {},
|
|
4405
4821
|
...skillAllowedTools !== void 0 ? { allowedToolNames: skillAllowedTools } : {},
|
|
4406
|
-
...
|
|
4822
|
+
...contextBlockScopes.length > 0 ? { contextBlockScopes } : {}
|
|
4407
4823
|
});
|
|
4408
4824
|
if (!continueId && result.dialogId && effectiveAgentKey !== state.agentKey) {
|
|
4409
4825
|
autoRouteByDialog.set(result.dialogId, {
|
|
@@ -4411,19 +4827,32 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
|
|
|
4411
4827
|
agentName: effectiveAgentName
|
|
4412
4828
|
});
|
|
4413
4829
|
}
|
|
4414
|
-
return
|
|
4830
|
+
return {
|
|
4831
|
+
...result,
|
|
4832
|
+
contextWindow: resolveAgentContextWindow({
|
|
4833
|
+
agentKey: effectiveAgentKey,
|
|
4834
|
+
agentName: effectiveAgentName,
|
|
4835
|
+
autoRouteDefault: env.NOLO_AUTO_ROUTE !== "0"
|
|
4836
|
+
})
|
|
4837
|
+
};
|
|
4415
4838
|
}
|
|
4416
4839
|
function waitForActionGate(rl, input, output, gate, spawnRunner) {
|
|
4417
4840
|
const commandPayload = gate.kind === "handoff" ? readCommandActionGatePayload(gate.payload) : null;
|
|
4418
4841
|
const displayCommand = commandPayload?.displayCommand ?? commandPayload?.command.join(" ") ?? gate.title;
|
|
4419
|
-
|
|
4420
|
-
|
|
4842
|
+
const isInteractiveHandoff = gate.kind === "handoff" && gate.title === "This command requires an interactive terminal.";
|
|
4843
|
+
const title = isInteractiveHandoff ? t("actionGateInteractiveTitle") : gate.title;
|
|
4844
|
+
const body = isInteractiveHandoff ? t("actionGateInteractiveBody") : gate.body;
|
|
4845
|
+
output.write(`
|
|
4846
|
+
[nolo] ${t("actionGateNeeded")}
|
|
4421
4847
|
`);
|
|
4422
|
-
|
|
4848
|
+
output.write(`[nolo] ${title}
|
|
4849
|
+
`);
|
|
4850
|
+
if (body) output.write(`[nolo] ${body}
|
|
4423
4851
|
`);
|
|
4424
4852
|
output.write(` ${displayCommand}
|
|
4425
4853
|
`);
|
|
4426
|
-
output.write(
|
|
4854
|
+
output.write(`[nolo] ${t("actionGateEnterHint")}
|
|
4855
|
+
`);
|
|
4427
4856
|
return new Promise((resolve2) => {
|
|
4428
4857
|
let settled = false;
|
|
4429
4858
|
const finish = (result) => {
|
|
@@ -4513,17 +4942,28 @@ function isInteractiveInput(input) {
|
|
|
4513
4942
|
function waitForRawActionGate(input, output, gate, spawnRunner, hooks) {
|
|
4514
4943
|
const commandPayload = gate.kind === "handoff" ? readCommandActionGatePayload(gate.payload) : null;
|
|
4515
4944
|
const displayCommand = commandPayload?.displayCommand ?? commandPayload?.command.join(" ") ?? gate.title;
|
|
4516
|
-
|
|
4517
|
-
|
|
4945
|
+
const isInteractiveHandoff = gate.kind === "handoff" && gate.title === "This command requires an interactive terminal.";
|
|
4946
|
+
const title = isInteractiveHandoff ? t("actionGateInteractiveTitle") : gate.title;
|
|
4947
|
+
const body = isInteractiveHandoff ? t("actionGateInteractiveBody") : gate.body;
|
|
4948
|
+
output.write(`
|
|
4949
|
+
[nolo] ${t("actionGateNeeded")}
|
|
4518
4950
|
`);
|
|
4519
|
-
|
|
4951
|
+
output.write(`[nolo] ${title}
|
|
4952
|
+
`);
|
|
4953
|
+
if (body) output.write(`[nolo] ${body}
|
|
4520
4954
|
`);
|
|
4521
4955
|
output.write(` ${displayCommand}
|
|
4522
4956
|
`);
|
|
4523
|
-
output.write(
|
|
4957
|
+
output.write(`[nolo] ${t("actionGateEnterHint")}
|
|
4958
|
+
`);
|
|
4524
4959
|
return new Promise((resolve2) => {
|
|
4525
4960
|
const rawInput = input;
|
|
4961
|
+
let settled = false;
|
|
4962
|
+
let commandRunning = false;
|
|
4526
4963
|
const finish = (result) => {
|
|
4964
|
+
if (settled) return;
|
|
4965
|
+
settled = true;
|
|
4966
|
+
hooks?.registerToken?.(null);
|
|
4527
4967
|
input.off("data", onData);
|
|
4528
4968
|
resolve2(result);
|
|
4529
4969
|
};
|
|
@@ -4541,13 +4981,9 @@ function waitForRawActionGate(input, output, gate, spawnRunner, hooks) {
|
|
|
4541
4981
|
actionGateResult: { gateId: gate.id, status: "failed", output: message }
|
|
4542
4982
|
}
|
|
4543
4983
|
});
|
|
4544
|
-
const
|
|
4545
|
-
|
|
4546
|
-
|
|
4547
|
-
cancel("interrupted");
|
|
4548
|
-
return;
|
|
4549
|
-
}
|
|
4550
|
-
if (!text.includes("\r") && !text.includes("\n")) return;
|
|
4984
|
+
const runCommand = async () => {
|
|
4985
|
+
if (settled || commandRunning) return;
|
|
4986
|
+
commandRunning = true;
|
|
4551
4987
|
if (!commandPayload) {
|
|
4552
4988
|
fail("unsupported gate payload");
|
|
4553
4989
|
return;
|
|
@@ -4585,7 +5021,25 @@ function waitForRawActionGate(input, output, gate, spawnRunner, hooks) {
|
|
|
4585
5021
|
}
|
|
4586
5022
|
});
|
|
4587
5023
|
};
|
|
4588
|
-
|
|
5024
|
+
const handleToken = (text) => {
|
|
5025
|
+
if (settled || commandRunning) return;
|
|
5026
|
+
if (text.includes("")) {
|
|
5027
|
+
cancel("interrupted");
|
|
5028
|
+
return;
|
|
5029
|
+
}
|
|
5030
|
+
if (text.includes("\r") || text.includes("\n")) {
|
|
5031
|
+
void runCommand();
|
|
5032
|
+
}
|
|
5033
|
+
};
|
|
5034
|
+
const onData = (chunk) => {
|
|
5035
|
+
const text = String(chunk);
|
|
5036
|
+
handleToken(text);
|
|
5037
|
+
};
|
|
5038
|
+
if (hooks?.registerToken) {
|
|
5039
|
+
hooks.registerToken(handleToken);
|
|
5040
|
+
} else {
|
|
5041
|
+
input.on("data", onData);
|
|
5042
|
+
}
|
|
4589
5043
|
});
|
|
4590
5044
|
}
|
|
4591
5045
|
async function startTuiWorkspace(options) {
|
|
@@ -4597,27 +5051,38 @@ async function startTuiWorkspace(options) {
|
|
|
4597
5051
|
const spawnRunner = options.spawnRunner ?? spawnProcess;
|
|
4598
5052
|
const selfUpdater = options.selfUpdater ?? ((target) => runSelfUpdate({ output: target }));
|
|
4599
5053
|
if (output.isTTY) {
|
|
4600
|
-
output
|
|
5054
|
+
enterAltScreen(output);
|
|
5055
|
+
installAltScreenRestoreHandlers(output);
|
|
5056
|
+
output.write("\x1B[2J\x1B[H");
|
|
4601
5057
|
}
|
|
4602
|
-
const detected = await
|
|
5058
|
+
const detected = await detectTerminalBackground({
|
|
4603
5059
|
stdin: input,
|
|
4604
5060
|
stdout: output
|
|
4605
5061
|
});
|
|
4606
|
-
if (detected)
|
|
5062
|
+
if (detected) {
|
|
5063
|
+
setActiveBrightness(detected.brightness);
|
|
5064
|
+
setActiveTerminalBaseHex(detected.hex);
|
|
5065
|
+
}
|
|
4607
5066
|
const bannerColumns = output.columns;
|
|
4608
5067
|
output.write(renderWelcome(state, 0, 0, bannerColumns));
|
|
4609
5068
|
let fixedInput = createNoopFixedInput();
|
|
4610
5069
|
let buffer = "";
|
|
4611
5070
|
let cursorPos = 0;
|
|
5071
|
+
const pasteStore = createCollapsedPasteStore();
|
|
4612
5072
|
let activeTurnAbort = null;
|
|
5073
|
+
let forcedStop = false;
|
|
5074
|
+
let forcedStopEpoch = 0;
|
|
5075
|
+
let turnEpoch = 0;
|
|
5076
|
+
let activeTurnEpoch = 0;
|
|
4613
5077
|
const activityIndicator = createActivityIndicator({
|
|
4614
5078
|
isTurnActive: () => activeTurnAbort !== null,
|
|
4615
5079
|
fallbackLabel: () => `${state.agentName} -> working`,
|
|
5080
|
+
stoppingLabel: () => t("turnStopping"),
|
|
4616
5081
|
onRepaint: () => {
|
|
4617
5082
|
if (fixedInput.active && !fixedInput.isPaused()) {
|
|
4618
5083
|
output.write("\x1B[?2026h\x1B[?25l");
|
|
4619
5084
|
try {
|
|
4620
|
-
fixedInput.repaint(buffer);
|
|
5085
|
+
fixedInput.repaint(buffer, cursorPos);
|
|
4621
5086
|
} finally {
|
|
4622
5087
|
output.write("\x1B[?25h\x1B[?2026l");
|
|
4623
5088
|
}
|
|
@@ -4635,15 +5100,17 @@ async function startTuiWorkspace(options) {
|
|
|
4635
5100
|
fixedInput.resumeFromDialog();
|
|
4636
5101
|
flushPendingRender();
|
|
4637
5102
|
renderHistoryToOutput();
|
|
4638
|
-
fixedInput.repaint(buffer);
|
|
5103
|
+
fixedInput.repaint(buffer, cursorPos);
|
|
4639
5104
|
},
|
|
4640
5105
|
getInputLines: () => fixedInput.getInputLines(),
|
|
4641
5106
|
isPaused: () => fixedInput.isPaused()
|
|
4642
5107
|
},
|
|
4643
5108
|
output
|
|
4644
5109
|
});
|
|
5110
|
+
const RENDER_THROTTLE_MS = 33;
|
|
4645
5111
|
let renderScheduled = false;
|
|
4646
5112
|
let renderTimer = null;
|
|
5113
|
+
let sessionEnded = false;
|
|
4647
5114
|
const flushPendingRender = () => {
|
|
4648
5115
|
if (renderTimer !== null) {
|
|
4649
5116
|
clearTimeout(renderTimer);
|
|
@@ -4658,7 +5125,7 @@ async function startTuiWorkspace(options) {
|
|
|
4658
5125
|
output.write("\x1B[?2026h\x1B[?25l");
|
|
4659
5126
|
try {
|
|
4660
5127
|
renderHistory(output, history, fixedInput.getInputLines());
|
|
4661
|
-
if (fixedInput.active) fixedInput.repaint(buffer);
|
|
5128
|
+
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
4662
5129
|
} finally {
|
|
4663
5130
|
output.write("\x1B[?25h\x1B[?2026l");
|
|
4664
5131
|
}
|
|
@@ -4670,11 +5137,26 @@ async function startTuiWorkspace(options) {
|
|
|
4670
5137
|
renderScheduled = false;
|
|
4671
5138
|
renderTimer = null;
|
|
4672
5139
|
paintSyncedFrame();
|
|
4673
|
-
},
|
|
5140
|
+
}, RENDER_THROTTLE_MS);
|
|
5141
|
+
};
|
|
5142
|
+
const refreshGitStatus = () => {
|
|
5143
|
+
if ((options.env?.NOLO_CLI_GIT_STATUS ?? process.env.NOLO_CLI_GIT_STATUS) === "0") return;
|
|
5144
|
+
void detectGitStatusAsync(state.cwd).then((gitStatus) => {
|
|
5145
|
+
if (sessionEnded) return;
|
|
5146
|
+
state = { ...state, gitStatus };
|
|
5147
|
+
scheduleRender();
|
|
5148
|
+
});
|
|
4674
5149
|
};
|
|
5150
|
+
let syncingLayout = false;
|
|
4675
5151
|
const renderHistoryToOutput = () => {
|
|
4676
5152
|
if (fixedInput.isPaused()) return;
|
|
4677
|
-
|
|
5153
|
+
if (syncingLayout) return;
|
|
5154
|
+
syncingLayout = true;
|
|
5155
|
+
try {
|
|
5156
|
+
renderHistory(output, history, fixedInput.getInputLines());
|
|
5157
|
+
} finally {
|
|
5158
|
+
syncingLayout = false;
|
|
5159
|
+
}
|
|
4678
5160
|
};
|
|
4679
5161
|
const readLatestAssistantReply = () => {
|
|
4680
5162
|
const lastReply = [...history.turns].reverse().find((turn) => turn.role === "assistant")?.content;
|
|
@@ -4707,18 +5189,22 @@ ${t("copyViewHint")}
|
|
|
4707
5189
|
fixedInput.resumeFromDialog();
|
|
4708
5190
|
flushPendingRender();
|
|
4709
5191
|
renderHistoryToOutput();
|
|
4710
|
-
fixedInput.repaint(buffer);
|
|
5192
|
+
fixedInput.repaint(buffer, cursorPos);
|
|
4711
5193
|
}
|
|
4712
5194
|
return true;
|
|
4713
5195
|
};
|
|
4714
5196
|
let modalOwnsKeyboard = false;
|
|
5197
|
+
let rawActionGateTokenHandler = null;
|
|
4715
5198
|
const runOneAgentTurn = async (message, imageUrls, actionGateHandler, confirmDestructiveAction) => {
|
|
5199
|
+
forcedStop = false;
|
|
5200
|
+
turnEpoch += 1;
|
|
5201
|
+
const myEpoch = turnEpoch;
|
|
4716
5202
|
history.followBottom = true;
|
|
4717
5203
|
startTurn(history, "user");
|
|
4718
5204
|
appendToCurrentTurn(history, message);
|
|
4719
5205
|
finalizeCurrentTurn(history);
|
|
4720
5206
|
renderHistoryToOutput();
|
|
4721
|
-
if (fixedInput.active) fixedInput.repaint(buffer);
|
|
5207
|
+
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
4722
5208
|
startTurn(history, "assistant");
|
|
4723
5209
|
const agentOutput = isInteractiveInput(input) ? createHistoryOutputStream(history, () => {
|
|
4724
5210
|
scheduleRender();
|
|
@@ -4742,6 +5228,7 @@ ${t("copyViewHint")}
|
|
|
4742
5228
|
} : void 0;
|
|
4743
5229
|
try {
|
|
4744
5230
|
activeTurnAbort = new AbortController();
|
|
5231
|
+
activeTurnEpoch = myEpoch;
|
|
4745
5232
|
const runResult = await runAgentChat(
|
|
4746
5233
|
options.scriptDir,
|
|
4747
5234
|
state,
|
|
@@ -4755,10 +5242,34 @@ ${t("copyViewHint")}
|
|
|
4755
5242
|
...confirmDestructiveAction ? { confirmDestructiveAction } : {},
|
|
4756
5243
|
...requestUserChoice ? { requestUserChoice } : {},
|
|
4757
5244
|
abortSignal: activeTurnAbort.signal,
|
|
5245
|
+
pastedTextStore: pasteStore,
|
|
4758
5246
|
explicitAgentSwitch,
|
|
4759
|
-
activityReporter
|
|
5247
|
+
activityReporter,
|
|
5248
|
+
onAgentRunStatus: (snapshot) => {
|
|
5249
|
+
if (snapshot) {
|
|
5250
|
+
activityIndicator.updateAgentRun(snapshot);
|
|
5251
|
+
} else {
|
|
5252
|
+
activityIndicator.clearAgentRun();
|
|
5253
|
+
}
|
|
5254
|
+
}
|
|
4760
5255
|
}
|
|
4761
5256
|
);
|
|
5257
|
+
const wasForceStopped = forcedStopEpoch === myEpoch;
|
|
5258
|
+
if (wasForceStopped) {
|
|
5259
|
+
if (runResult.dialogId || runResult.turnTokens) {
|
|
5260
|
+
const nextDialogKey = runResult.dialogId ? runResult.dialogId === state.dialogId && state.dialogKey ? state.dialogKey : state.dialogOwnerId ? `dialog-${state.dialogOwnerId}-${runResult.dialogId}` : void 0 : state.dialogKey;
|
|
5261
|
+
state = {
|
|
5262
|
+
...state,
|
|
5263
|
+
...runResult.dialogId ? {
|
|
5264
|
+
dialogId: runResult.dialogId,
|
|
5265
|
+
dialogKey: nextDialogKey,
|
|
5266
|
+
dialogLabel: runResult.dialogId
|
|
5267
|
+
} : {},
|
|
5268
|
+
...runResult.turnTokens ? { turnTokens: runResult.turnTokens } : {}
|
|
5269
|
+
};
|
|
5270
|
+
}
|
|
5271
|
+
return { ok: false, aborted: true };
|
|
5272
|
+
}
|
|
4762
5273
|
const wasAborted = activeTurnAbort.signal.aborted;
|
|
4763
5274
|
activeTurnAbort = null;
|
|
4764
5275
|
explicitAgentSwitch = false;
|
|
@@ -4766,12 +5277,16 @@ ${t("copyViewHint")}
|
|
|
4766
5277
|
finalizeCurrentTurn(history);
|
|
4767
5278
|
flushPendingRender();
|
|
4768
5279
|
renderHistoryToOutput();
|
|
4769
|
-
if (fixedInput.active) fixedInput.repaint(buffer);
|
|
5280
|
+
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
4770
5281
|
}
|
|
4771
5282
|
if (wasAborted) {
|
|
4772
|
-
|
|
5283
|
+
if (runResult.pendingToolName) {
|
|
5284
|
+
emitCommandOutput(t("turnStoppedToolPending", runResult.pendingToolName));
|
|
5285
|
+
} else {
|
|
5286
|
+
emitCommandOutput(t("turnStopped"));
|
|
5287
|
+
}
|
|
4773
5288
|
}
|
|
4774
|
-
if (runResult.dialogId || runResult.turnTokens) {
|
|
5289
|
+
if (runResult.dialogId || runResult.turnTokens || runResult.contextWindow) {
|
|
4775
5290
|
const nextDialogKey = runResult.dialogId ? runResult.dialogId === state.dialogId && state.dialogKey ? state.dialogKey : state.dialogOwnerId ? `dialog-${state.dialogOwnerId}-${runResult.dialogId}` : void 0 : state.dialogKey;
|
|
4776
5291
|
state = {
|
|
4777
5292
|
...state,
|
|
@@ -4780,7 +5295,8 @@ ${t("copyViewHint")}
|
|
|
4780
5295
|
dialogKey: nextDialogKey,
|
|
4781
5296
|
dialogLabel: runResult.dialogId
|
|
4782
5297
|
} : {},
|
|
4783
|
-
...runResult.turnTokens ? { turnTokens: runResult.turnTokens } : {}
|
|
5298
|
+
...runResult.turnTokens ? { turnTokens: runResult.turnTokens } : {},
|
|
5299
|
+
...runResult.contextWindow ? { contextWindow: runResult.contextWindow } : {}
|
|
4784
5300
|
};
|
|
4785
5301
|
}
|
|
4786
5302
|
if (!wasAborted && runResult.exitCode !== 0) {
|
|
@@ -4821,7 +5337,7 @@ ${t("copyViewHint")}
|
|
|
4821
5337
|
appendToCurrentTurn(history, text);
|
|
4822
5338
|
finalizeCurrentTurn(history);
|
|
4823
5339
|
renderHistoryToOutput();
|
|
4824
|
-
if (fixedInput.active) fixedInput.repaint(buffer);
|
|
5340
|
+
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
4825
5341
|
};
|
|
4826
5342
|
const runSubmittedLine = async (line, actionGateHandler, confirmDestructiveAction) => {
|
|
4827
5343
|
if (!line.trim()) return false;
|
|
@@ -4835,7 +5351,7 @@ ${t("copyViewHint")}
|
|
|
4835
5351
|
appendToCurrentTurn(history, line.trim());
|
|
4836
5352
|
finalizeCurrentTurn(history);
|
|
4837
5353
|
renderHistoryToOutput();
|
|
4838
|
-
if (fixedInput.active) fixedInput.repaint(buffer);
|
|
5354
|
+
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
4839
5355
|
}
|
|
4840
5356
|
if (result.output) {
|
|
4841
5357
|
emitCommandOutput(result.output);
|
|
@@ -4847,6 +5363,7 @@ ${t("copyViewHint")}
|
|
|
4847
5363
|
}
|
|
4848
5364
|
if (result.action?.type === "exit") return true;
|
|
4849
5365
|
if (result.action?.type === "clear") {
|
|
5366
|
+
clearCollapsedPasteStore(pasteStore);
|
|
4850
5367
|
history.turns.length = 0;
|
|
4851
5368
|
history.currentRole = null;
|
|
4852
5369
|
history.currentContent = "";
|
|
@@ -4877,6 +5394,14 @@ ${t("copyViewHint")}
|
|
|
4877
5394
|
dialogKey: compactResult.dialogKey,
|
|
4878
5395
|
dialogLabel: compactResult.dialogId
|
|
4879
5396
|
};
|
|
5397
|
+
output.write(
|
|
5398
|
+
`${t(
|
|
5399
|
+
compactResult.summaryGenerated ? "compactDone" : "compactDoneNoSummary",
|
|
5400
|
+
result.action.dialogId,
|
|
5401
|
+
compactResult.dialogId
|
|
5402
|
+
)}
|
|
5403
|
+
`
|
|
5404
|
+
);
|
|
4880
5405
|
} catch (error) {
|
|
4881
5406
|
output.write(
|
|
4882
5407
|
`[nolo] Compact failed: ${toErrorMessage(error)}
|
|
@@ -4917,7 +5442,19 @@ ${t("copyViewHint")}
|
|
|
4917
5442
|
...state,
|
|
4918
5443
|
agentName: pickResult.name,
|
|
4919
5444
|
agentKey: pickResult.key,
|
|
4920
|
-
contextWindow:
|
|
5445
|
+
contextWindow: resolveAgentContextWindow({
|
|
5446
|
+
agentKey: pickResult.key,
|
|
5447
|
+
agentName: pickResult.name,
|
|
5448
|
+
model: pickResult.model,
|
|
5449
|
+
autoRouteDefault: (options.env ?? process.env).NOLO_AUTO_ROUTE !== "0"
|
|
5450
|
+
}),
|
|
5451
|
+
estimatedContextTokens: estimateDefaultCliContextTokens({
|
|
5452
|
+
cwd: state.cwd,
|
|
5453
|
+
agentKey: pickResult.key,
|
|
5454
|
+
agentName: pickResult.name,
|
|
5455
|
+
model: pickResult.model
|
|
5456
|
+
}),
|
|
5457
|
+
...pickResult.apiSource ? { apiSource: pickResult.apiSource } : {}
|
|
4921
5458
|
};
|
|
4922
5459
|
if (state.dialogId) autoRouteByDialog.delete(state.dialogId);
|
|
4923
5460
|
explicitAgentSwitch = true;
|
|
@@ -4949,6 +5486,16 @@ ${t("copyViewHint")}
|
|
|
4949
5486
|
if (result.action?.type === "set-mouse") {
|
|
4950
5487
|
fixedInput.setMouseEnabled(result.action.enabled);
|
|
4951
5488
|
}
|
|
5489
|
+
if (result.action?.type === "set-altscreen") {
|
|
5490
|
+
if (result.action.enabled) {
|
|
5491
|
+
enterAltScreen(output);
|
|
5492
|
+
installAltScreenRestoreHandlers(output);
|
|
5493
|
+
} else {
|
|
5494
|
+
leaveAltScreen(output);
|
|
5495
|
+
}
|
|
5496
|
+
renderHistoryToOutput();
|
|
5497
|
+
fixedInput.repaint(buffer, cursorPos);
|
|
5498
|
+
}
|
|
4952
5499
|
if (result.action?.type === "copy-view") {
|
|
4953
5500
|
const opened = await openCopyView();
|
|
4954
5501
|
if (!opened) emitCommandOutput(t("copyNothing"));
|
|
@@ -4992,14 +5539,7 @@ ${t("copyViewHint")}
|
|
|
4992
5539
|
dialog: pickResult.dialog,
|
|
4993
5540
|
env: options.env ?? process.env
|
|
4994
5541
|
});
|
|
4995
|
-
const restored = loadedTurns.slice(-MAX_TUI_HISTORY_TURNS)
|
|
4996
|
-
(turn) => turn.role === "assistant" ? {
|
|
4997
|
-
...turn,
|
|
4998
|
-
content: formatAssistantDisplay(
|
|
4999
|
-
turn.content
|
|
5000
|
-
)
|
|
5001
|
-
} : turn
|
|
5002
|
-
);
|
|
5542
|
+
const restored = loadedTurns.slice(-MAX_TUI_HISTORY_TURNS);
|
|
5003
5543
|
history.turns.length = 0;
|
|
5004
5544
|
history.turns.push(...restored);
|
|
5005
5545
|
history.currentRole = null;
|
|
@@ -5013,6 +5553,7 @@ ${t("copyViewHint")}
|
|
|
5013
5553
|
dialogLabel: pickResult.dialog.id,
|
|
5014
5554
|
turnTokens: void 0
|
|
5015
5555
|
};
|
|
5556
|
+
clearCollapsedPasteStore(pasteStore);
|
|
5016
5557
|
emitCommandOutput(
|
|
5017
5558
|
`${t("resumedDialogPrefix")}: ${pickResult.dialog.title} (${pickResult.dialog.id})`
|
|
5018
5559
|
);
|
|
@@ -5127,7 +5668,7 @@ ${stderrText.trim()}
|
|
|
5127
5668
|
${err.message}` : ""}`
|
|
5128
5669
|
);
|
|
5129
5670
|
}
|
|
5130
|
-
if (fixedInput.active) fixedInput.repaint(buffer);
|
|
5671
|
+
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
5131
5672
|
}
|
|
5132
5673
|
return false;
|
|
5133
5674
|
};
|
|
@@ -5136,7 +5677,7 @@ ${err.message}` : ""}`
|
|
|
5136
5677
|
output.write("\x1B[?2004h");
|
|
5137
5678
|
let busy = false;
|
|
5138
5679
|
let done = false;
|
|
5139
|
-
let
|
|
5680
|
+
let resolveDone = null;
|
|
5140
5681
|
fixedInput = createFixedInput(output, {
|
|
5141
5682
|
getStatusLine: () => {
|
|
5142
5683
|
let base = renderStatusLine(state);
|
|
@@ -5154,17 +5695,7 @@ ${err.message}` : ""}`
|
|
|
5154
5695
|
}
|
|
5155
5696
|
return base;
|
|
5156
5697
|
},
|
|
5157
|
-
|
|
5158
|
-
const view = activityIndicator.getView();
|
|
5159
|
-
if (view === null) return null;
|
|
5160
|
-
const colorEnabled = resolveCliColorEnabled();
|
|
5161
|
-
const elapsed = formatElapsedSeconds(view.elapsedSec);
|
|
5162
|
-
const stopHint = t("stopHint");
|
|
5163
|
-
if (!colorEnabled) {
|
|
5164
|
-
return `${view.frame} ${view.label} (${elapsed}) \xB7 ${stopHint}`;
|
|
5165
|
-
}
|
|
5166
|
-
return themeText(view.frame, "accent") + " " + themeText(view.label, "muted") + themeText(` (${elapsed})`, "chrome") + themeText(` \xB7 ${stopHint}`, "chrome");
|
|
5167
|
-
},
|
|
5698
|
+
getActivityLines: () => activityIndicator.getActivityLines(resolveCliColorEnabled()),
|
|
5168
5699
|
getQueueLines: () => {
|
|
5169
5700
|
if (!chatQueueBinding || chatQueueBinding.queueLength() === 0) return [];
|
|
5170
5701
|
const colorEnabled = resolveCliColorEnabled();
|
|
@@ -5172,18 +5703,21 @@ ${err.message}` : ""}`
|
|
|
5172
5703
|
const oneLine = text.replace(/\r?\n/g, " \u23CE ");
|
|
5173
5704
|
return dimCliText(` \u2937 ${i + 1}. ${oneLine}`, colorEnabled);
|
|
5174
5705
|
});
|
|
5706
|
+
},
|
|
5707
|
+
onInputLinesChange: () => {
|
|
5708
|
+
renderHistoryToOutput();
|
|
5175
5709
|
}
|
|
5176
5710
|
});
|
|
5177
5711
|
fixedInput.init();
|
|
5178
5712
|
const paintFrame = (draft) => {
|
|
5179
5713
|
renderHistoryToOutput();
|
|
5180
|
-
fixedInput.repaint(draft,
|
|
5714
|
+
fixedInput.repaint(draft, cursorPos);
|
|
5181
5715
|
};
|
|
5182
5716
|
const onResize = () => {
|
|
5183
5717
|
if (done || copyViewExitResolver) return;
|
|
5184
5718
|
if (fixedInput.isPaused()) {
|
|
5185
5719
|
renderHistory(output, history, fixedInput.getInputLines());
|
|
5186
|
-
fixedInput.repaint(buffer,
|
|
5720
|
+
fixedInput.repaint(buffer, cursorPos);
|
|
5187
5721
|
return;
|
|
5188
5722
|
}
|
|
5189
5723
|
flushPendingRender();
|
|
@@ -5194,6 +5728,9 @@ ${err.message}` : ""}`
|
|
|
5194
5728
|
const finish = () => {
|
|
5195
5729
|
if (done) return;
|
|
5196
5730
|
done = true;
|
|
5731
|
+
sessionEnded = true;
|
|
5732
|
+
resolveDone?.();
|
|
5733
|
+
clearCollapsedPasteStore(pasteStore);
|
|
5197
5734
|
resizeTarget.off?.("resize", onResize);
|
|
5198
5735
|
input.off("data", onData);
|
|
5199
5736
|
onData.destroy();
|
|
@@ -5211,7 +5748,10 @@ ${err.message}` : ""}`
|
|
|
5211
5748
|
}
|
|
5212
5749
|
return;
|
|
5213
5750
|
}
|
|
5214
|
-
if (modalOwnsKeyboard)
|
|
5751
|
+
if (modalOwnsKeyboard) {
|
|
5752
|
+
rawActionGateTokenHandler?.(sequence);
|
|
5753
|
+
return;
|
|
5754
|
+
}
|
|
5215
5755
|
const busyLock = busy;
|
|
5216
5756
|
const scrollAction = parseScrollAction(sequence);
|
|
5217
5757
|
if (scrollAction) {
|
|
@@ -5221,19 +5761,33 @@ ${err.message}` : ""}`
|
|
|
5221
5761
|
return;
|
|
5222
5762
|
}
|
|
5223
5763
|
if (busyLock && sequence === "\x1B" && activeTurnAbort) {
|
|
5764
|
+
if (activityIndicator.isStopping()) {
|
|
5765
|
+
forcedStop = true;
|
|
5766
|
+
forcedStopEpoch = activeTurnEpoch;
|
|
5767
|
+
activityIndicator.stop();
|
|
5768
|
+
activeTurnAbort.abort();
|
|
5769
|
+
activeTurnAbort = null;
|
|
5770
|
+
activeTurnEpoch = 0;
|
|
5771
|
+
busy = false;
|
|
5772
|
+
emitCommandOutput(t("forceStopped"));
|
|
5773
|
+
flushPendingRender();
|
|
5774
|
+
renderHistoryToOutput();
|
|
5775
|
+
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
5776
|
+
return;
|
|
5777
|
+
}
|
|
5224
5778
|
const stopBinding = chatQueueBinding;
|
|
5225
5779
|
if (stopBinding && stopBinding.queueLength() > 0) {
|
|
5226
5780
|
stopBinding.preemptForStop();
|
|
5227
5781
|
}
|
|
5782
|
+
activityIndicator.markStopping();
|
|
5228
5783
|
activeTurnAbort.abort();
|
|
5229
5784
|
return;
|
|
5230
5785
|
}
|
|
5231
|
-
const result = applyTuiInputKey(buffer, sequence, {},
|
|
5786
|
+
const result = applyTuiInputKey(buffer, sequence, {}, cursorPos, {
|
|
5787
|
+
pasteStore
|
|
5788
|
+
});
|
|
5232
5789
|
if (result.copyView) {
|
|
5233
|
-
if (busyLock) return;
|
|
5234
|
-
busy = true;
|
|
5235
5790
|
const opened = await openCopyView();
|
|
5236
|
-
busy = false;
|
|
5237
5791
|
if (!opened) {
|
|
5238
5792
|
history.followBottom = true;
|
|
5239
5793
|
startTurn(history, "assistant");
|
|
@@ -5249,14 +5803,19 @@ ${err.message}` : ""}`
|
|
|
5249
5803
|
return;
|
|
5250
5804
|
}
|
|
5251
5805
|
if (result.submit !== void 0) {
|
|
5806
|
+
const submittedText = replaceCollapsedPastesWithReferences(
|
|
5807
|
+
result.submit,
|
|
5808
|
+
pasteStore
|
|
5809
|
+
);
|
|
5810
|
+
const submittedTrimmed = submittedText.trim();
|
|
5811
|
+
const startsChatTurn = submittedTrimmed.length > 0 && !submittedTrimmed.startsWith("/") && !submittedTrimmed.startsWith("!");
|
|
5252
5812
|
if (busyLock) {
|
|
5253
|
-
const
|
|
5254
|
-
const trimmedText = submittedText2.trim();
|
|
5813
|
+
const trimmedText = submittedText.trim();
|
|
5255
5814
|
const busySlashCommand = trimmedText.split(/\s+/)[0]?.toLowerCase();
|
|
5256
5815
|
const isBusyLocalSlash = busySlashCommand === "/context" || busySlashCommand === "/ctx" || busySlashCommand === "/switch";
|
|
5257
5816
|
if (isBusyLocalSlash) {
|
|
5258
5817
|
const beforeAgentKey = state.agentKey;
|
|
5259
|
-
const res = handleTuiInput(
|
|
5818
|
+
const res = handleTuiInput(submittedText, state);
|
|
5260
5819
|
if (res.action) {
|
|
5261
5820
|
output.write(
|
|
5262
5821
|
"Model picker isn't available while a reply is running. Use `/switch <name>` to switch now (takes effect on the next turn), or wait for the reply to finish.\n"
|
|
@@ -5274,9 +5833,10 @@ ${hint}` : hint;
|
|
|
5274
5833
|
`);
|
|
5275
5834
|
}
|
|
5276
5835
|
}
|
|
5836
|
+
releaseCollapsedPasteReferences(submittedText, pasteStore);
|
|
5277
5837
|
buffer = "";
|
|
5278
|
-
|
|
5279
|
-
if (fixedInput.active) fixedInput.repaint(buffer,
|
|
5838
|
+
cursorPos = 0;
|
|
5839
|
+
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
5280
5840
|
return;
|
|
5281
5841
|
}
|
|
5282
5842
|
const binding = ensureChatQueueBinding(
|
|
@@ -5285,7 +5845,10 @@ ${hint}` : hint;
|
|
|
5285
5845
|
try {
|
|
5286
5846
|
return await waitForRawActionGate(input, output, gate, spawnRunner, {
|
|
5287
5847
|
beforeSubprocess: () => fixedInput.pause(),
|
|
5288
|
-
afterSubprocess: () => fixedInput.resumeFromSubprocess()
|
|
5848
|
+
afterSubprocess: () => fixedInput.resumeFromSubprocess(),
|
|
5849
|
+
registerToken: (handler) => {
|
|
5850
|
+
rawActionGateTokenHandler = handler;
|
|
5851
|
+
}
|
|
5289
5852
|
});
|
|
5290
5853
|
} finally {
|
|
5291
5854
|
modalOwnsKeyboard = false;
|
|
@@ -5308,16 +5871,16 @@ ${hint}` : hint;
|
|
|
5308
5871
|
}
|
|
5309
5872
|
);
|
|
5310
5873
|
const decision = binding.resolveSubmit({
|
|
5311
|
-
text:
|
|
5874
|
+
text: submittedText,
|
|
5312
5875
|
isRunning: true
|
|
5313
5876
|
});
|
|
5314
5877
|
if (decision.kind === "queue-text") {
|
|
5315
5878
|
binding.enqueue(decision.text);
|
|
5316
5879
|
buffer = "";
|
|
5317
|
-
|
|
5318
|
-
fixedInput.repaint(buffer,
|
|
5880
|
+
cursorPos = 0;
|
|
5881
|
+
fixedInput.repaint(buffer, cursorPos);
|
|
5319
5882
|
} else if (decision.kind === "queue-blocked") {
|
|
5320
|
-
} else if (decision.kind === "noop" && !
|
|
5883
|
+
} else if (decision.kind === "noop" && !submittedText.trim() && binding.queueLength() > 0) {
|
|
5321
5884
|
if (binding.preemptForDrain()) {
|
|
5322
5885
|
if (activeTurnAbort) {
|
|
5323
5886
|
activeTurnAbort.abort();
|
|
@@ -5326,13 +5889,16 @@ ${hint}` : hint;
|
|
|
5326
5889
|
}
|
|
5327
5890
|
return;
|
|
5328
5891
|
}
|
|
5329
|
-
if (!
|
|
5892
|
+
if (!submittedText.trim() && chatQueueBinding && chatQueueBinding.queueLength() > 0) {
|
|
5330
5893
|
const actionGateHandler = async (gate) => {
|
|
5331
5894
|
modalOwnsKeyboard = true;
|
|
5332
5895
|
try {
|
|
5333
5896
|
return await waitForRawActionGate(input, output, gate, spawnRunner, {
|
|
5334
5897
|
beforeSubprocess: () => fixedInput.pause(),
|
|
5335
|
-
afterSubprocess: () => fixedInput.resumeFromSubprocess()
|
|
5898
|
+
afterSubprocess: () => fixedInput.resumeFromSubprocess(),
|
|
5899
|
+
registerToken: (handler) => {
|
|
5900
|
+
rawActionGateTokenHandler = handler;
|
|
5901
|
+
}
|
|
5336
5902
|
});
|
|
5337
5903
|
} finally {
|
|
5338
5904
|
modalOwnsKeyboard = false;
|
|
@@ -5358,7 +5924,7 @@ ${hint}` : hint;
|
|
|
5358
5924
|
if (drainedText !== null) {
|
|
5359
5925
|
busy = true;
|
|
5360
5926
|
buffer = "";
|
|
5361
|
-
|
|
5927
|
+
cursorPos = 0;
|
|
5362
5928
|
fixedInput.enterOutputMode(drainedText);
|
|
5363
5929
|
try {
|
|
5364
5930
|
const outcome = await runOneAgentTurn(
|
|
@@ -5377,19 +5943,15 @@ ${err.message}` : ""}`
|
|
|
5377
5943
|
} finally {
|
|
5378
5944
|
busy = false;
|
|
5379
5945
|
}
|
|
5380
|
-
|
|
5381
|
-
if (drainEnv.NOLO_CLI_GIT_STATUS !== "0") {
|
|
5382
|
-
state = { ...state, gitStatus: detectGitStatus(state.cwd) };
|
|
5383
|
-
}
|
|
5946
|
+
refreshGitStatus();
|
|
5384
5947
|
flushPendingRender();
|
|
5385
|
-
fixedInput.exitOutputMode(buffer,
|
|
5948
|
+
fixedInput.exitOutputMode(buffer, cursorPos);
|
|
5386
5949
|
return;
|
|
5387
5950
|
}
|
|
5388
5951
|
}
|
|
5389
|
-
busy =
|
|
5390
|
-
const submittedText = result.submit;
|
|
5952
|
+
busy = startsChatTurn;
|
|
5391
5953
|
buffer = "";
|
|
5392
|
-
|
|
5954
|
+
cursorPos = 0;
|
|
5393
5955
|
fixedInput.enterOutputMode(submittedText);
|
|
5394
5956
|
let shouldExit = false;
|
|
5395
5957
|
try {
|
|
@@ -5400,7 +5962,10 @@ ${err.message}` : ""}`
|
|
|
5400
5962
|
try {
|
|
5401
5963
|
return await waitForRawActionGate(input, output, gate, spawnRunner, {
|
|
5402
5964
|
beforeSubprocess: () => fixedInput.pause(),
|
|
5403
|
-
afterSubprocess: () => fixedInput.resumeFromSubprocess()
|
|
5965
|
+
afterSubprocess: () => fixedInput.resumeFromSubprocess(),
|
|
5966
|
+
registerToken: (handler) => {
|
|
5967
|
+
rawActionGateTokenHandler = handler;
|
|
5968
|
+
}
|
|
5404
5969
|
});
|
|
5405
5970
|
} finally {
|
|
5406
5971
|
modalOwnsKeyboard = false;
|
|
@@ -5430,31 +5995,25 @@ ${err.message}` : ""}`
|
|
|
5430
5995
|
finish();
|
|
5431
5996
|
return;
|
|
5432
5997
|
}
|
|
5433
|
-
|
|
5434
|
-
if (refreshEnv.NOLO_CLI_GIT_STATUS !== "0") {
|
|
5435
|
-
state = { ...state, gitStatus: detectGitStatus(state.cwd) };
|
|
5436
|
-
}
|
|
5998
|
+
refreshGitStatus();
|
|
5437
5999
|
flushPendingRender();
|
|
5438
|
-
fixedInput.exitOutputMode(buffer,
|
|
6000
|
+
fixedInput.exitOutputMode(buffer, cursorPos);
|
|
5439
6001
|
return;
|
|
5440
6002
|
}
|
|
5441
6003
|
buffer = result.buffer;
|
|
5442
|
-
|
|
5443
|
-
fixedInput.repaint(buffer,
|
|
6004
|
+
cursorPos = result.cursorPos ?? buffer.length;
|
|
6005
|
+
fixedInput.repaint(buffer, cursorPos);
|
|
5444
6006
|
};
|
|
5445
6007
|
const onData = createRawInputDecoder((token) => {
|
|
5446
6008
|
void handleInputToken(token);
|
|
5447
6009
|
});
|
|
5448
6010
|
try {
|
|
5449
6011
|
input.on("data", onData);
|
|
5450
|
-
fixedInput.repaint(buffer,
|
|
6012
|
+
fixedInput.repaint(buffer, cursorPos);
|
|
6013
|
+
refreshGitStatus();
|
|
5451
6014
|
await new Promise((resolve2) => {
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
clearInterval(check);
|
|
5455
|
-
resolve2();
|
|
5456
|
-
}
|
|
5457
|
-
}, 10);
|
|
6015
|
+
resolveDone = resolve2;
|
|
6016
|
+
if (done) resolve2();
|
|
5458
6017
|
});
|
|
5459
6018
|
} finally {
|
|
5460
6019
|
finish();
|
|
@@ -5509,9 +6068,13 @@ export {
|
|
|
5509
6068
|
createNoopFixedInput,
|
|
5510
6069
|
createRawInputDecoder,
|
|
5511
6070
|
createTurnHistory,
|
|
5512
|
-
|
|
6071
|
+
displayWidth,
|
|
6072
|
+
enterAltScreen,
|
|
5513
6073
|
finalizeCurrentTurn,
|
|
5514
6074
|
fitAnsiLine,
|
|
6075
|
+
installAltScreenRestoreHandlers,
|
|
6076
|
+
isAltScreenOn,
|
|
6077
|
+
leaveAltScreen,
|
|
5515
6078
|
padOrTruncateToWidth,
|
|
5516
6079
|
parseScrollAction,
|
|
5517
6080
|
renderHistory,
|