nolo-cli 0.54.0-alpha.1 → 0.54.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{_executeModel-IVEOJ7L6.js → _executeModel-OPTKYHJA.js} +53 -48
- package/{action-IU6ON7ZB.js → action-ORXWW2ZG.js} +9 -9
- package/{addContentAction-5V5CCI3I.js → addContentAction-53BZ34AE.js} +9 -8
- package/{addDialogAgentAction-KF3KZZNZ.js → addDialogAgentAction-JRYUS54Y.js} +11 -10
- package/{agentDeleteCommand-D7AFLKQN.js → agentDeleteCommand-FJIWU2BS.js} +31 -29
- package/{agentEmailCommands-MXFZGF54.js → agentEmailCommands-CE32L43B.js} +62 -58
- package/{agentListCommands-MP732Q23.js → agentListCommands-VJNKULIK.js} +43 -34
- package/{agentMachineCommands-352JFJX4.js → agentMachineCommands-HCJ3WQXL.js} +31 -29
- package/agentRecordCommands-QFWERED4.js +119 -0
- package/{agentRunCommand-UUWJMH7J.js → agentRunCommand-G3GQMUNN.js} +70 -59
- package/{agentRunControl-4GHHSXFZ.js → agentRunControl-INKFDXDA.js} +3 -1
- package/{agentRuntimeLocal-KWBPJ53H.js → agentRuntimeLocal-K327ELSL.js} +31 -27
- package/{agentSlice-V742SNKC.js → agentSlice-ODKF4J6M.js} +9 -9
- package/{agentSuperviseCommands-VB6BUS5H.js → agentSuperviseCommands-5AR3DKNN.js} +2 -2
- package/{appDeleteCommands-GV4TF52A.js → appDeleteCommands-XUXMFDI4.js} +32 -30
- package/{appDeployCommands-EZMUPNSW.js → appDeployCommands-37F4GPCU.js} +32 -30
- package/{appDeployStatusCommands-AAGELKAN.js → appDeployStatusCommands-WSPU7TUK.js} +32 -30
- package/{appGetCommands-W37KDAON.js → appGetCommands-YLOBA3EM.js} +32 -30
- package/{appListCommands-IFDWBBNV.js → appListCommands-UD62R5ME.js} +32 -30
- package/{authCommand-5HM57KPL.js → authCommand-OK6HLHGS.js} +13 -13
- package/{authCommands-SBQVTD6V.js → authCommands-2DUIUEMT.js} +2 -2
- package/{chatQueueReduxAdapter-ZXXP6E4O.js → chatQueueReduxAdapter-TV2TUOQS.js} +28 -25
- package/{chunk-7CH6U3FL.js → chunk-2GRGFCF6.js} +2 -4
- package/{chunk-T27IEBNQ.js → chunk-2QDDKFG5.js} +74 -1
- package/{chunk-2JYSSXLY.js → chunk-2WBFJHQA.js} +5 -27
- package/{chunk-FZMM2KZC.js → chunk-3LKZRP7E.js} +2 -2
- package/{chunk-WUMYQ2FE.js → chunk-3Y2K5O2R.js} +64 -6
- package/{chunk-5XCW6RQG.js → chunk-3ZTR7ZO4.js} +1 -1
- package/{chunk-W6N3GLUU.js → chunk-5FCIPEKT.js} +327 -149
- package/{chunk-B54WOZ77.js → chunk-5UAYZEXO.js} +2 -2
- package/{chunk-5SXS7T5Q.js → chunk-6COJEAYH.js} +36 -105
- package/{chunk-PEMGYJNS.js → chunk-6HDYSFLO.js} +4 -6
- package/{chunk-6XIE5ZGI.js → chunk-6HIGYUBQ.js} +1 -1
- package/chunk-6IGD47IC.js +19 -0
- package/chunk-6YTOJY35.js +352 -0
- package/{chunk-6FGXGFAH.js → chunk-75MSWWUE.js} +1 -1
- package/{chunk-ALMVQQPG.js → chunk-7FAS3NEF.js} +17 -17
- package/{chunk-76UA7376.js → chunk-7NYDJAQ7.js} +20 -15
- package/{chunk-2T6XO4PK.js → chunk-A65UV3M5.js} +4 -4
- package/{chunk-V2IPBG5U.js → chunk-AA564GBL.js} +2 -2
- package/{chunk-SQTVMYMU.js → chunk-AUVFJGPC.js} +4 -4
- package/{chunk-HR5XDXYM.js → chunk-BBTN4NQD.js} +4 -4
- package/{chunk-CULR4XB4.js → chunk-BG7J6Y5X.js} +1 -1
- package/chunk-BY2FF2C4.js +526 -0
- package/{chunk-FA4SY4F5.js → chunk-BYEJ7K2Z.js} +1 -1
- package/chunk-BYXBJQAS.js +0 -0
- package/{chunk-3AOLVADY.js → chunk-DVINR3PL.js} +1 -1
- package/{chunk-DIQCV4FR.js → chunk-EA5OYZVD.js} +1 -1
- package/{chunk-RO2IUWZE.js → chunk-EICIFHVS.js} +1 -1
- package/chunk-EJE33XNR.js +19059 -0
- package/{chunk-M2CGG6VJ.js → chunk-EVBFYPDC.js} +1 -1
- package/{chunk-KHCLMKZZ.js → chunk-EY7V7REW.js} +280 -6
- package/{chunk-MC66ASS6.js → chunk-FJHMLRDQ.js} +12 -12
- package/{chunk-YFHUQLJF.js → chunk-GRFK2XP4.js} +2 -2
- package/{chunk-J3UALFHL.js → chunk-HJTAGT3W.js} +1 -1
- package/{chunk-J7KVJAUQ.js → chunk-HPW5MNDQ.js} +38 -99
- package/{chunk-H2G6JXTR.js → chunk-HQUORJTM.js} +1 -1
- package/{chunk-OYNDYVSE.js → chunk-JON2PHYT.js} +1 -1
- package/{chunk-RBJD33HM.js → chunk-JXIP6ZWH.js} +7 -7
- package/{chunk-PTHLNXGY.js → chunk-K4NISKOR.js} +46 -8
- package/{chunk-5Y5G76BJ.js → chunk-KLIBF3BY.js} +2 -2
- package/chunk-LRGKOI5Y.js +297 -0
- package/{chunk-OU7NGN5Y.js → chunk-MLEZPE3U.js} +2 -2
- package/{chunk-4RXXP6WD.js → chunk-MYMBBB6Q.js} +5 -64
- package/{chunk-ET7BXWAW.js → chunk-N4JKMCY3.js} +2 -2
- package/{chunk-45E65JER.js → chunk-NFLA6GSU.js} +4 -4
- package/{chunk-SVEUG45B.js → chunk-NHRQCXIQ.js} +0 -24
- package/{chunk-WJV3G7BM.js → chunk-NJUQBYFJ.js} +3 -1
- package/{chunk-LGUQHYAL.js → chunk-O5FLGN2L.js} +3 -2
- package/{chunk-Q24S4NDF.js → chunk-Q4XZVZPN.js} +1 -1
- package/{chunk-L3JWI7WX.js → chunk-QOKAKDPE.js} +15 -11
- package/{chunk-VM7K5EJL.js → chunk-RHMDMTDK.js} +2 -2
- package/{chunk-YZ2WE6CW.js → chunk-SAJSBPI2.js} +2 -2
- package/{chunk-WHVQA7PJ.js → chunk-SSXTSPSG.js} +27 -6
- package/chunk-SUEIAK5N.js +2574 -0
- package/{chunk-X7S7OFUX.js → chunk-SVC3GGUA.js} +6 -6
- package/{chunk-7UFH6THF.js → chunk-SVDMVX4G.js} +118 -2
- package/{chunk-ZMV5PCRS.js → chunk-SWJGJMTW.js} +1 -1
- package/{chunk-DQLYEGP7.js → chunk-SYYUU7EV.js} +2 -2
- package/{chunk-K7CNV7GI.js → chunk-TC6P4NDR.js} +7 -7
- package/{chunk-SUFDWBLV.js → chunk-TR4DXUYX.js} +1988 -452
- package/{chunk-BANYWEYY.js → chunk-TY3ADTX5.js} +1 -1
- package/chunk-UAD2WT54.js +64 -0
- package/{chunk-HCBQVVFZ.js → chunk-UGBHDMHI.js} +4 -4
- package/{chunk-R63FNYSI.js → chunk-UOWXD4VN.js} +851 -970
- package/{chunk-GBXR3CSM.js → chunk-UR66XPVF.js} +3 -3
- package/{chunk-6BYAXNH5.js → chunk-VZMUOPYG.js} +12 -2
- package/{chunk-RVHPY5PC.js → chunk-WQHGJHAL.js} +441 -2982
- package/chunk-XIJZOCSP.js +1766 -0
- package/{chunk-HHBXCRJS.js → chunk-XOJRZUPF.js} +4 -4
- package/{chunk-BKPUPZ6W.js → chunk-YBFJQC2M.js} +95 -778
- package/{chunk-MRWGHF56.js → chunk-YDH3FDFQ.js} +12 -356
- package/{chunk-SG65C33A.js → chunk-YLCUU3WM.js} +4 -4
- package/{chunk-XXZULHJ7.js → chunk-YQBXOHE6.js} +1 -1
- package/chunk-ZJ7UHBJS.js +89 -0
- package/{chunk-TNPCQNGQ.js → chunk-ZVOHVJI3.js} +4 -9
- package/{codingSkills-BYQI6EPT.js → codingSkills-BCQXRZOI.js} +3 -4
- package/contentThunks-KNI6VXLT.js +71 -0
- package/{createAgentAutomationAction-LE3ZYDT6.js → createAgentAutomationAction-OK4L33KF.js} +29 -25
- package/createDialogAction-JDSMY4AY.js +62 -0
- package/{createPageAction-X4C7K3OD.js → createPageAction-T7RA3HCG.js} +32 -28
- package/{dbSlice-FWF5F4L7.js → dbSlice-TVX6SN2T.js} +7 -7
- package/{deleteFile-NPZ64DFN.js → deleteFile-JYKRT7C3.js} +4 -4
- package/{dialogCommands-CP2U44A7.js → dialogCommands-UQZYWOOV.js} +33 -31
- package/{dialogSlice-2SH5636X.js → dialogSlice-TYW2G7IL.js} +28 -25
- package/{docCreateCommands-A2KIJSF7.js → docCreateCommands-OA6HZBWO.js} +37 -36
- package/{docDeleteCommands-NZ7XO4Z4.js → docDeleteCommands-KBB3YQI2.js} +34 -33
- package/{docReadCommands-VWVK4ZAR.js → docReadCommands-ZLMFVC67.js} +33 -32
- package/{docStore-J6GQYKHS.js → docStore-2URPBVCS.js} +9 -34
- package/{docUpdateCommands-YTUEC6PN.js → docUpdateCommands-HMH5KUON.js} +37 -36
- package/{fetchMessages-KZQVBXKY.js → fetchMessages-DRABNDPZ.js} +3 -3
- package/{getPricing-I53UFOPH.js → getPricing-RFOPUWHY.js} +4 -4
- package/{handleSendMessageAction-MD325XZS.js → handleSendMessageAction-GO53TY7U.js} +30 -27
- package/index.js +67 -67
- package/{localDialogRead-NQ33HBSI.js → localDialogRead-PUQKFI73.js} +1 -1
- package/{machineCommands-TCNZV2DC.js → machineCommands-RK6L7ZWL.js} +64 -60
- package/{machineWsRunDispatch-QSX5IWPH.js → machineWsRunDispatch-3X64DW2Z.js} +59 -55
- package/memberThunks-THZYZFSN.js +638 -0
- package/{models-E2CYP4IB.js → models-JVKUEER5.js} +2 -2
- package/package.json +2 -3
- package/{read-J7RVVCHH.js → read-K4JU4MQC.js} +4 -4
- package/{readDocTool-G2WBM4IH.js → readDocTool-LQGR6FUN.js} +1 -1
- package/{readlineWorkspace-N3RGX7EK.js → readlineWorkspace-XUFJV3RS.js} +1005 -437
- package/{removeDialogAgentAction-RMPISD42.js → removeDialogAgentAction-ECHCFB7N.js} +13 -12
- package/{runAgentClientLoop-R3NTOERA.js → runAgentClientLoop-24GQ2AW6.js} +51 -46
- package/{setDialogExtraReferencesAction-7BUTHDAL.js → setDialogExtraReferencesAction-KCVJNGMB.js} +10 -9
- package/{setPrimaryDialogAgentAction-WCYIBGZ4.js → setPrimaryDialogAgentAction-VDOBOMKC.js} +13 -12
- package/{settingSlice-7NN2MI6Y.js → settingSlice-VR2IJSXC.js} +8 -8
- package/{skillDiscovery-COX56OBO.js → skillDiscovery-2QTZINON.js} +3 -4
- package/{spaceCommands-RC7XMYQ7.js → spaceCommands-S5YJJXL7.js} +31 -29
- package/{streamAgentChatTurn-WLWEBWSF.js → streamAgentChatTurn-QHKP3ABO.js} +82 -70
- package/{switchDialogAgentAction-WGMMQHDI.js → switchDialogAgentAction-5L4UP7VI.js} +11 -10
- package/{tableCommands-P2TXJ3CF.js → tableCommands-R6WFRDYF.js} +1 -1
- package/{toolApiClient-B2MFXMEG.js → toolApiClient-53U4UDFF.js} +3 -2
- package/{tools-LSLDSUWU.js → tools-SSXSTC3O.js} +42 -38
- package/{tuiSummaryLlmCaller-TPBL4BV2.js → tuiSummaryLlmCaller-URFJHFC5.js} +19 -17
- package/{updateDialogTitleAction-4HJEARM7.js → updateDialogTitleAction-YFYAEW3T.js} +30 -26
- package/agentRecordCommands-G6EFHU3G.js +0 -115
- package/chunk-AETZ7AD6.js +0 -274
- package/chunk-DMCFF4KE.js +0 -435
- package/chunk-EUG45QJR.js +0 -6104
- package/chunk-J4Z6UI4C.js +0 -7076
- package/chunk-OFZPJ3NT.js +0 -114
- package/createDialogAction-2C6SR62D.js +0 -59
- package/spaceSlice-KBVKKO3M.js +0 -127
- package/{chunk-NQ4N4IGO.js → chunk-CS6TCJ7E.js} +3 -3
|
@@ -3,11 +3,12 @@ import {
|
|
|
3
3
|
normalizeDialogRecord,
|
|
4
4
|
readDialogSnapshot,
|
|
5
5
|
sortDialogs
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-XIJZOCSP.js";
|
|
7
7
|
import {
|
|
8
8
|
ANSI_ESCAPE_REGEX,
|
|
9
9
|
COLLAPSED_PASTE_PLACEHOLDER_RE,
|
|
10
10
|
THEME_PALETTES,
|
|
11
|
+
TUI_TREE_MARKER,
|
|
11
12
|
allocateCollapsedPaste,
|
|
12
13
|
applyDetectedBackground,
|
|
13
14
|
applyTerminalOutputToText,
|
|
@@ -24,13 +25,15 @@ import {
|
|
|
24
25
|
findCollapsedPasteSpanAt,
|
|
25
26
|
fitAnsiLine,
|
|
26
27
|
formatAssistantDisplay,
|
|
28
|
+
formatCreditsChip,
|
|
27
29
|
formatTokenCount,
|
|
28
|
-
getActiveDensity,
|
|
29
30
|
getActiveThemeMode,
|
|
30
31
|
getActiveThemeName,
|
|
31
32
|
isBalanceExhaustedError,
|
|
32
|
-
normalizeToolDisplayMode,
|
|
33
33
|
padOrTruncateToWidth,
|
|
34
|
+
polishBreathInsertsBlankBetween,
|
|
35
|
+
readAgentsMdLayerFromDisk,
|
|
36
|
+
redactSecrets,
|
|
34
37
|
releaseCollapsedPaste,
|
|
35
38
|
releaseCollapsedPasteReferences,
|
|
36
39
|
renderSurfaceLine,
|
|
@@ -46,7 +49,6 @@ import {
|
|
|
46
49
|
resolveTuiThemeMode,
|
|
47
50
|
runAgentTurn,
|
|
48
51
|
setActiveBrightness,
|
|
49
|
-
setActiveDensity,
|
|
50
52
|
setActiveThemeMode,
|
|
51
53
|
setActiveThemeName,
|
|
52
54
|
setMathRenderingEnabled,
|
|
@@ -64,30 +66,32 @@ import {
|
|
|
64
66
|
wrapTextToLines,
|
|
65
67
|
wrapTranscriptLine,
|
|
66
68
|
wrapTranscriptLineWithLayout
|
|
67
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-UOWXD4VN.js";
|
|
70
|
+
import "./chunk-S7Z2HMCN.js";
|
|
71
|
+
import "./chunk-LYGNQHUW.js";
|
|
72
|
+
import "./chunk-ESHYE7WU.js";
|
|
68
73
|
import {
|
|
69
74
|
listFavoriteAgentIdsAcrossServers,
|
|
70
75
|
listLocalCachedAgents,
|
|
71
76
|
listRemoteAgents,
|
|
72
77
|
listRemoteAgentsAcrossServers,
|
|
73
78
|
normalizeListedAgent
|
|
74
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-UR66XPVF.js";
|
|
75
80
|
import "./chunk-ID4G2MAR.js";
|
|
76
81
|
import {
|
|
77
82
|
listUserRecordsFromServers,
|
|
78
83
|
readLiveDbRecordAfterTombstoneMerge
|
|
79
|
-
} from "./chunk-
|
|
80
|
-
import "./chunk-J3UALFHL.js";
|
|
81
|
-
import "./chunk-LYGNQHUW.js";
|
|
84
|
+
} from "./chunk-Q4XZVZPN.js";
|
|
82
85
|
import {
|
|
86
|
+
RUN_WAKE_CHANNEL_ENV,
|
|
83
87
|
createCliLocalRuntimeAdapter,
|
|
84
88
|
initCliLocale,
|
|
85
89
|
parseCliLocale,
|
|
86
90
|
readTimestamp,
|
|
87
91
|
setCliLocale,
|
|
88
92
|
t
|
|
89
|
-
} from "./chunk-
|
|
90
|
-
import "./chunk-
|
|
93
|
+
} from "./chunk-5FCIPEKT.js";
|
|
94
|
+
import "./chunk-NFLA6GSU.js";
|
|
91
95
|
import "./chunk-EADNWBAZ.js";
|
|
92
96
|
import "./chunk-NI2BS6UD.js";
|
|
93
97
|
import {
|
|
@@ -97,37 +101,46 @@ import {
|
|
|
97
101
|
deleteDbRecord,
|
|
98
102
|
queryUserRecords,
|
|
99
103
|
readDbRecord
|
|
100
|
-
} from "./chunk-
|
|
101
|
-
import "./chunk-
|
|
104
|
+
} from "./chunk-SYYUU7EV.js";
|
|
105
|
+
import "./chunk-JON2PHYT.js";
|
|
102
106
|
import {
|
|
103
107
|
NOLO_DEFAULT_AGENT_KEY,
|
|
104
108
|
resolveCliAgentKeyInput
|
|
105
109
|
} from "./chunk-EKLRRZIN.js";
|
|
110
|
+
import "./chunk-HJTAGT3W.js";
|
|
111
|
+
import {
|
|
112
|
+
createChatQueueRuntime,
|
|
113
|
+
createTurnRequest,
|
|
114
|
+
normalizeRunCompletionShape
|
|
115
|
+
} from "./chunk-YPREESF4.js";
|
|
106
116
|
import "./chunk-JDSUTNYY.js";
|
|
117
|
+
import "./chunk-JBTJAPUF.js";
|
|
107
118
|
import {
|
|
119
|
+
backfillRunRecordParentDialog,
|
|
108
120
|
checkStaleRun,
|
|
109
121
|
isRunRecordClaimed,
|
|
110
122
|
listRunRecords,
|
|
111
123
|
readRunRecord
|
|
112
|
-
} from "./chunk-
|
|
113
|
-
import "./chunk-ESHYE7WU.js";
|
|
114
|
-
import "./chunk-JBTJAPUF.js";
|
|
124
|
+
} from "./chunk-SSXTSPSG.js";
|
|
115
125
|
import "./chunk-LTRXGFMS.js";
|
|
116
|
-
import "./chunk-
|
|
126
|
+
import "./chunk-EICIFHVS.js";
|
|
117
127
|
import "./chunk-ATNTHIZI.js";
|
|
118
128
|
import "./chunk-JEXVIMYZ.js";
|
|
119
129
|
import {
|
|
120
130
|
prepareTools
|
|
121
|
-
} from "./chunk-
|
|
131
|
+
} from "./chunk-RHMDMTDK.js";
|
|
132
|
+
import {
|
|
133
|
+
buildResponseGuidelines
|
|
134
|
+
} from "./chunk-6IGD47IC.js";
|
|
122
135
|
import "./chunk-HS7MLR74.js";
|
|
123
|
-
import "./chunk-
|
|
124
|
-
import "./chunk-
|
|
136
|
+
import "./chunk-BG7J6Y5X.js";
|
|
137
|
+
import "./chunk-TR4DXUYX.js";
|
|
125
138
|
import "./chunk-5XTOKQLW.js";
|
|
126
139
|
import "./chunk-NF64D32W.js";
|
|
127
140
|
import "./chunk-3L2WKC3W.js";
|
|
128
|
-
import "./chunk-
|
|
141
|
+
import "./chunk-GRFK2XP4.js";
|
|
129
142
|
import "./chunk-KQHGZWSV.js";
|
|
130
|
-
import "./chunk-
|
|
143
|
+
import "./chunk-3ZTR7ZO4.js";
|
|
131
144
|
import {
|
|
132
145
|
clipText,
|
|
133
146
|
formatDuration,
|
|
@@ -139,67 +152,65 @@ import {
|
|
|
139
152
|
runShowsLogTail,
|
|
140
153
|
shortRunId
|
|
141
154
|
} from "./chunk-KXKQO4Q6.js";
|
|
155
|
+
import "./chunk-LRGKOI5Y.js";
|
|
156
|
+
import "./chunk-TY3ADTX5.js";
|
|
157
|
+
import "./chunk-UGBHDMHI.js";
|
|
142
158
|
import "./chunk-INZZJSQF.js";
|
|
143
|
-
import "./chunk-PEMGYJNS.js";
|
|
144
|
-
import "./chunk-BANYWEYY.js";
|
|
145
|
-
import "./chunk-HCBQVVFZ.js";
|
|
146
159
|
import "./chunk-MEYIRKDN.js";
|
|
147
160
|
import "./chunk-63W5N6P3.js";
|
|
148
161
|
import "./chunk-VF4K2QE6.js";
|
|
162
|
+
import "./chunk-6HDYSFLO.js";
|
|
163
|
+
import "./chunk-FJHMLRDQ.js";
|
|
164
|
+
import "./chunk-WQHGJHAL.js";
|
|
165
|
+
import "./chunk-ZJ7UHBJS.js";
|
|
149
166
|
import {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
import "./chunk-
|
|
155
|
-
import "./chunk-RVHPY5PC.js";
|
|
156
|
-
import {
|
|
157
|
-
extractReferenceKeysFromMessage
|
|
158
|
-
} from "./chunk-BKPUPZ6W.js";
|
|
159
|
-
import "./chunk-M2CGG6VJ.js";
|
|
160
|
-
import "./chunk-H2G6JXTR.js";
|
|
167
|
+
extractReferenceKeysFromMessage,
|
|
168
|
+
normalizeUsage
|
|
169
|
+
} from "./chunk-YBFJQC2M.js";
|
|
170
|
+
import "./chunk-EVBFYPDC.js";
|
|
171
|
+
import "./chunk-HQUORJTM.js";
|
|
161
172
|
import "./chunk-YYLHWIW6.js";
|
|
162
|
-
import "./chunk-K7CNV7GI.js";
|
|
163
173
|
import "./chunk-AGGIDPCZ.js";
|
|
164
|
-
import "./chunk-
|
|
174
|
+
import "./chunk-TC6P4NDR.js";
|
|
175
|
+
import "./chunk-YLCUU3WM.js";
|
|
165
176
|
import "./chunk-5RGU3J7P.js";
|
|
177
|
+
import "./chunk-6YTOJY35.js";
|
|
166
178
|
import "./chunk-M76ECQRD.js";
|
|
167
|
-
import "./chunk-
|
|
179
|
+
import "./chunk-YDH3FDFQ.js";
|
|
168
180
|
import "./chunk-URMS4XY2.js";
|
|
169
181
|
import "./chunk-IA3A5373.js";
|
|
170
182
|
import "./chunk-GLKOYSRU.js";
|
|
171
183
|
import {
|
|
172
184
|
buildSkillDiscoveryContextBlock,
|
|
173
185
|
buildSkillDiscoveryContextLayer
|
|
174
|
-
} from "./chunk-
|
|
175
|
-
import "./chunk-
|
|
186
|
+
} from "./chunk-SUEIAK5N.js";
|
|
187
|
+
import "./chunk-AA564GBL.js";
|
|
176
188
|
import {
|
|
177
189
|
expandEnabledPacks
|
|
178
|
-
} from "./chunk-
|
|
179
|
-
import "./chunk-
|
|
180
|
-
import "./chunk-KHCLMKZZ.js";
|
|
190
|
+
} from "./chunk-O5FLGN2L.js";
|
|
191
|
+
import "./chunk-EY7V7REW.js";
|
|
181
192
|
import {
|
|
182
193
|
buildLocalWorkspaceOpenAiTools,
|
|
183
194
|
buildLocalWorkspaceToolset,
|
|
184
195
|
getProcessRegistry,
|
|
185
196
|
readCommandActionGatePayload
|
|
186
|
-
} from "./chunk-
|
|
187
|
-
import "./chunk-WUMYQ2FE.js";
|
|
197
|
+
} from "./chunk-EJE33XNR.js";
|
|
188
198
|
import "./chunk-ZNFJWEN4.js";
|
|
189
|
-
import "./chunk-
|
|
190
|
-
import "./chunk-
|
|
199
|
+
import "./chunk-3Y2K5O2R.js";
|
|
200
|
+
import "./chunk-A65UV3M5.js";
|
|
201
|
+
import "./chunk-2QDDKFG5.js";
|
|
191
202
|
import "./chunk-N7J63Q6C.js";
|
|
192
203
|
import {
|
|
193
204
|
buildCurrentTimeBlock,
|
|
194
205
|
buildIdentityBlock,
|
|
195
206
|
buildRuntimeGuidanceBlocks,
|
|
196
207
|
resolveToolGuidedSections
|
|
197
|
-
} from "./chunk-
|
|
198
|
-
import "./chunk-
|
|
208
|
+
} from "./chunk-BY2FF2C4.js";
|
|
209
|
+
import "./chunk-VZMUOPYG.js";
|
|
199
210
|
import {
|
|
200
211
|
resolvePlatformAuthToken
|
|
201
|
-
} from "./chunk-
|
|
202
|
-
import "./chunk-
|
|
212
|
+
} from "./chunk-KLIBF3BY.js";
|
|
213
|
+
import "./chunk-ZVOHVJI3.js";
|
|
203
214
|
import {
|
|
204
215
|
isQuotaExhaustedError
|
|
205
216
|
} from "./chunk-IWUMSAQF.js";
|
|
@@ -207,7 +218,7 @@ import "./chunk-2MBTWINN.js";
|
|
|
207
218
|
import {
|
|
208
219
|
DEFAULT_CONTEXT_WINDOW,
|
|
209
220
|
getModelContextWindow
|
|
210
|
-
} from "./chunk-
|
|
221
|
+
} from "./chunk-K4NISKOR.js";
|
|
211
222
|
import {
|
|
212
223
|
buildCompactionMetricsFromPlan,
|
|
213
224
|
buildCompactionUserContent,
|
|
@@ -218,32 +229,32 @@ import {
|
|
|
218
229
|
formatMessagesForSummaryWithTruncation,
|
|
219
230
|
planCompression,
|
|
220
231
|
serializeMessageContent
|
|
221
|
-
} from "./chunk-
|
|
222
|
-
import "./chunk-
|
|
223
|
-
import "./chunk-
|
|
224
|
-
import "./chunk-
|
|
225
|
-
import "./chunk-7UFH6THF.js";
|
|
232
|
+
} from "./chunk-BBTN4NQD.js";
|
|
233
|
+
import "./chunk-N4JKMCY3.js";
|
|
234
|
+
import "./chunk-BYXBJQAS.js";
|
|
235
|
+
import "./chunk-SVDMVX4G.js";
|
|
226
236
|
import {
|
|
227
237
|
compactWhitespace
|
|
228
238
|
} from "./chunk-DWLEOOGR.js";
|
|
239
|
+
import "./chunk-CS6TCJ7E.js";
|
|
240
|
+
import "./chunk-FD36SCCD.js";
|
|
229
241
|
import "./chunk-ZHXJ6M7T.js";
|
|
230
242
|
import {
|
|
231
243
|
toErrorMessage
|
|
232
244
|
} from "./chunk-HNMXYYJR.js";
|
|
233
245
|
import {
|
|
234
|
-
buildAgentsMdLayer,
|
|
235
246
|
buildMemoryOverlayLayer,
|
|
236
247
|
buildMemoryUseGuidanceLayer,
|
|
237
248
|
partitionScopedBlocks,
|
|
238
249
|
renderTurnContextBlocksWithScope
|
|
239
|
-
} from "./chunk-
|
|
250
|
+
} from "./chunk-DVINR3PL.js";
|
|
240
251
|
import {
|
|
241
252
|
buildUserResponseLanguageContext
|
|
242
|
-
} from "./chunk-
|
|
253
|
+
} from "./chunk-2GRGFCF6.js";
|
|
243
254
|
import {
|
|
244
255
|
canonicalizeToolNames
|
|
245
256
|
} from "./chunk-O6OSWFZQ.js";
|
|
246
|
-
import "./chunk-
|
|
257
|
+
import "./chunk-BYEJ7K2Z.js";
|
|
247
258
|
import {
|
|
248
259
|
checkForCliUpdate,
|
|
249
260
|
runSelfUpdateDetailed
|
|
@@ -268,19 +279,20 @@ import {
|
|
|
268
279
|
import {
|
|
269
280
|
DEFAULT_NOLO_SERVER_URL
|
|
270
281
|
} from "./chunk-OLZBQWZU.js";
|
|
271
|
-
import "./chunk-
|
|
272
|
-
import "./chunk-
|
|
273
|
-
import "./chunk-
|
|
274
|
-
import "./chunk-
|
|
282
|
+
import "./chunk-NJUQBYFJ.js";
|
|
283
|
+
import "./chunk-HPW5MNDQ.js";
|
|
284
|
+
import "./chunk-AUVFJGPC.js";
|
|
285
|
+
import "./chunk-SVC3GGUA.js";
|
|
275
286
|
import "./chunk-Y5GCAMGP.js";
|
|
276
287
|
import "./chunk-UZ6XXCVR.js";
|
|
277
288
|
import "./chunk-KX6YBIHG.js";
|
|
278
|
-
import "./chunk-
|
|
279
|
-
import "./chunk-
|
|
280
|
-
import "./chunk-
|
|
289
|
+
import "./chunk-MYMBBB6Q.js";
|
|
290
|
+
import "./chunk-UAD2WT54.js";
|
|
291
|
+
import "./chunk-7NYDJAQ7.js";
|
|
292
|
+
import "./chunk-NHRQCXIQ.js";
|
|
281
293
|
import "./chunk-WWC7I6TA.js";
|
|
282
294
|
import "./chunk-I42VC5KR.js";
|
|
283
|
-
import "./chunk-
|
|
295
|
+
import "./chunk-SAJSBPI2.js";
|
|
284
296
|
import {
|
|
285
297
|
asTrimmedLowercaseString
|
|
286
298
|
} from "./chunk-QSJ3HFEF.js";
|
|
@@ -289,18 +301,18 @@ import {
|
|
|
289
301
|
BUILTIN_NOLO_AGENT_MODEL,
|
|
290
302
|
BUILTIN_NOLO_AGENT_NAME,
|
|
291
303
|
builtinAgentCatalogEntryById
|
|
292
|
-
} from "./chunk-
|
|
304
|
+
} from "./chunk-2WBFJHQA.js";
|
|
293
305
|
import {
|
|
294
306
|
extractCustomId,
|
|
295
307
|
parsePublicAgentId
|
|
296
308
|
} from "./chunk-BJIRLHJR.js";
|
|
297
|
-
import "./chunk-
|
|
298
|
-
import "./chunk-
|
|
309
|
+
import "./chunk-XOJRZUPF.js";
|
|
310
|
+
import "./chunk-QOKAKDPE.js";
|
|
299
311
|
import "./chunk-OW765XBU.js";
|
|
300
312
|
import "./chunk-GBRG75BQ.js";
|
|
301
|
-
import "./chunk-KUG52IAP.js";
|
|
302
313
|
import "./chunk-3EUW3WEO.js";
|
|
303
314
|
import "./chunk-U6VC4MU4.js";
|
|
315
|
+
import "./chunk-KUG52IAP.js";
|
|
304
316
|
import "./chunk-LCBAMY2M.js";
|
|
305
317
|
import "./chunk-4SN6J65J.js";
|
|
306
318
|
import "./chunk-CF7PE763.js";
|
|
@@ -624,9 +636,9 @@ function createRawKeyReader(input) {
|
|
|
624
636
|
};
|
|
625
637
|
const resolveWaiter = (value) => {
|
|
626
638
|
if (!waiter) return;
|
|
627
|
-
const
|
|
639
|
+
const resolve4 = waiter;
|
|
628
640
|
waiter = null;
|
|
629
|
-
|
|
641
|
+
resolve4(value);
|
|
630
642
|
};
|
|
631
643
|
const tryDeliver = () => {
|
|
632
644
|
if (!waiter) return;
|
|
@@ -683,12 +695,12 @@ function createRawKeyReader(input) {
|
|
|
683
695
|
input.on("close", onClose);
|
|
684
696
|
input.on("end", onClose);
|
|
685
697
|
input.resume?.();
|
|
686
|
-
const reader = () => new Promise((
|
|
698
|
+
const reader = () => new Promise((resolve4) => {
|
|
687
699
|
if (closed) {
|
|
688
|
-
|
|
700
|
+
resolve4(drainClosed());
|
|
689
701
|
return;
|
|
690
702
|
}
|
|
691
|
-
waiter =
|
|
703
|
+
waiter = resolve4;
|
|
692
704
|
tryDeliver();
|
|
693
705
|
});
|
|
694
706
|
reader.dispose = () => {
|
|
@@ -1225,7 +1237,81 @@ function appendLocalTurn(history, command, output) {
|
|
|
1225
1237
|
trimHistoryToBudget(history);
|
|
1226
1238
|
resetStreamingTurnCache();
|
|
1227
1239
|
}
|
|
1240
|
+
var TOOL_SUCCESS_LINE_RE = /^(▸ .*?)(?: ×(\d+))?( ✓)$/;
|
|
1241
|
+
function applyTreeRewrite(text, chunk) {
|
|
1242
|
+
if (!chunk.startsWith(TUI_TREE_MARKER)) return null;
|
|
1243
|
+
const payload = chunk.slice(TUI_TREE_MARKER.length).replace(/\u0002\n?$/, "");
|
|
1244
|
+
const tree = `\u2022 ${payload}`;
|
|
1245
|
+
const hadTrailingNewline = text.endsWith("\n");
|
|
1246
|
+
const body = hadTrailingNewline ? text.slice(0, -1) : text;
|
|
1247
|
+
const bodyLines = body.split("\n");
|
|
1248
|
+
const plainLines = bodyLines.map((line) => stripAnsi(line));
|
|
1249
|
+
const tail = plainLines.at(-1) ?? "";
|
|
1250
|
+
let prefix;
|
|
1251
|
+
if (tail.trimStart().startsWith("\u25B8 ")) {
|
|
1252
|
+
prefix = bodyLines.length < 2 ? "" : `${bodyLines.slice(0, -1).join("\n")}
|
|
1253
|
+
`;
|
|
1254
|
+
} else if (tail.trimStart().startsWith("\u2514\u2500\u2500 ")) {
|
|
1255
|
+
const headerIndex = plainLines.findLastIndex((line) => line.trimStart().startsWith("\u2022 "));
|
|
1256
|
+
if (headerIndex < 0) return null;
|
|
1257
|
+
prefix = `${bodyLines.slice(0, headerIndex).join("\n")}${headerIndex > 0 ? "\n" : ""}`;
|
|
1258
|
+
} else return null;
|
|
1259
|
+
return `${prefix}${tree}
|
|
1260
|
+
`;
|
|
1261
|
+
}
|
|
1262
|
+
function foldToolSuccessLine(text, chunk) {
|
|
1263
|
+
const plain = stripAnsi(chunk).replace(/\n+$/, "");
|
|
1264
|
+
if (!plain || plain.includes("\n") || !TOOL_SUCCESS_LINE_RE.test(plain)) return null;
|
|
1265
|
+
const hadTrailingNewline = text.endsWith("\n");
|
|
1266
|
+
const body = hadTrailingNewline ? text.slice(0, -1) : text;
|
|
1267
|
+
const newlineAt = body.lastIndexOf("\n");
|
|
1268
|
+
const tail = newlineAt === -1 ? body : body.slice(newlineAt + 1);
|
|
1269
|
+
const match = TOOL_SUCCESS_LINE_RE.exec(stripAnsi(tail));
|
|
1270
|
+
if (!match || `${match[1]}${match[3]}` !== plain) return null;
|
|
1271
|
+
const count = (match[2] ? Number(match[2]) : 1) + 1;
|
|
1272
|
+
const anchor = tail.lastIndexOf(" ");
|
|
1273
|
+
if (anchor < 0) return null;
|
|
1274
|
+
const head = tail.slice(0, anchor).replace(/\s×\d+$/, "");
|
|
1275
|
+
const merged = `${head} \xD7${count}${tail.slice(anchor)}`;
|
|
1276
|
+
const prefix = newlineAt === -1 ? "" : body.slice(0, newlineAt + 1);
|
|
1277
|
+
return `${prefix}${merged}${hadTrailingNewline ? "\n" : ""}`;
|
|
1278
|
+
}
|
|
1228
1279
|
function applyOutputChunkToCurrentTurn(history, chunk, kind = "assistant") {
|
|
1280
|
+
if (kind === "tool") {
|
|
1281
|
+
if (chunk.startsWith(TUI_TREE_MARKER)) {
|
|
1282
|
+
const rewrittenTree = applyTreeRewrite(history.currentContent, chunk);
|
|
1283
|
+
if (rewrittenTree === null) {
|
|
1284
|
+
const safe = chunk.slice(TUI_TREE_MARKER.length).replace(/\u0002/g, "");
|
|
1285
|
+
const leaves = safe.split("\n").filter((line) => /^(?:├── |└── )/.test(line));
|
|
1286
|
+
const newest = leaves.at(-1)?.replace(/^(?:├── |└── )/, "\u25B8 ") ?? "";
|
|
1287
|
+
if (!newest) return false;
|
|
1288
|
+
const flat = `${newest}
|
|
1289
|
+
`;
|
|
1290
|
+
history.currentContent = applyTerminalOutputToText(history.currentContent, flat);
|
|
1291
|
+
appendCurrentBlock(history, kind, flat);
|
|
1292
|
+
return false;
|
|
1293
|
+
}
|
|
1294
|
+
const treeContent = rewrittenTree;
|
|
1295
|
+
history.currentContent = treeContent;
|
|
1296
|
+
const lastBlock = history.currentBlocks.at(-1);
|
|
1297
|
+
const treeLines = treeContent.split("\n");
|
|
1298
|
+
const treeHeaderIndex = treeLines.findIndex((line) => line.trimStart().startsWith("\u2022 "));
|
|
1299
|
+
const treeBlock = treeHeaderIndex >= 0 ? treeLines.slice(treeHeaderIndex).join("\n") : treeContent;
|
|
1300
|
+
if (lastBlock?.kind === "tool") lastBlock.content = lastBlock.content.replace(/[^\n]*$/, "") + treeBlock;
|
|
1301
|
+
else history.currentBlocks.push({ kind: "tool", content: treeBlock });
|
|
1302
|
+
return true;
|
|
1303
|
+
}
|
|
1304
|
+
const foldedContent = foldToolSuccessLine(history.currentContent, chunk);
|
|
1305
|
+
if (foldedContent !== null) {
|
|
1306
|
+
history.currentContent = foldedContent;
|
|
1307
|
+
const lastBlock = history.currentBlocks.at(-1);
|
|
1308
|
+
if (lastBlock?.kind === "tool") {
|
|
1309
|
+
const foldedBlock = foldToolSuccessLine(lastBlock.content, chunk);
|
|
1310
|
+
if (foldedBlock !== null) lastBlock.content = foldedBlock;
|
|
1311
|
+
}
|
|
1312
|
+
return true;
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1229
1315
|
const next = applyTerminalOutputToText(history.currentContent, chunk);
|
|
1230
1316
|
if (next === history.currentContent) return false;
|
|
1231
1317
|
history.currentContent = next;
|
|
@@ -1385,9 +1471,9 @@ function layoutTurnRows(role, content, contentWidth, colorEnabled, command) {
|
|
|
1385
1471
|
}
|
|
1386
1472
|
return rows.length > 0 ? rows : [{ rendered: "", sourceStart: 0, sourceEnd: 0, prefixWidth: 0 }];
|
|
1387
1473
|
}
|
|
1388
|
-
function getTurnLayoutRows(turn, contentWidth, colorEnabled,
|
|
1474
|
+
function getTurnLayoutRows(turn, contentWidth, colorEnabled, themeFingerprint) {
|
|
1389
1475
|
const cached = getCachedTurnLine(turn);
|
|
1390
|
-
if (cached && cached.width === contentWidth && cached.color === colorEnabled && cached.
|
|
1476
|
+
if (cached && cached.width === contentWidth && cached.color === colorEnabled && cached.themeFingerprint === themeFingerprint) {
|
|
1391
1477
|
return cached.layoutRows;
|
|
1392
1478
|
}
|
|
1393
1479
|
const layoutRows = layoutTurnRows(turn.role, turn.content, contentWidth, colorEnabled, turn.command);
|
|
@@ -1395,7 +1481,6 @@ function getTurnLayoutRows(turn, contentWidth, colorEnabled, density, themeFinge
|
|
|
1395
1481
|
setCachedTurnLine(turn, {
|
|
1396
1482
|
width: contentWidth,
|
|
1397
1483
|
color: colorEnabled,
|
|
1398
|
-
density,
|
|
1399
1484
|
themeFingerprint,
|
|
1400
1485
|
lines,
|
|
1401
1486
|
layoutRows
|
|
@@ -1409,6 +1494,40 @@ var streamingTurnCache = null;
|
|
|
1409
1494
|
function resetStreamingTurnCache() {
|
|
1410
1495
|
streamingTurnCache = null;
|
|
1411
1496
|
}
|
|
1497
|
+
function countFenceFlips(text) {
|
|
1498
|
+
const matches = text.match(/^[ \t]*```/gm);
|
|
1499
|
+
return matches ? matches.length : 0;
|
|
1500
|
+
}
|
|
1501
|
+
var HEADING_LIKE_RE = /^#{1,3} /;
|
|
1502
|
+
function isPipeRowLike(line) {
|
|
1503
|
+
const trimmed = line.trim();
|
|
1504
|
+
return trimmed.length > 0 && trimmed.startsWith("|");
|
|
1505
|
+
}
|
|
1506
|
+
function isSuspiciousAppend(newTail, fenceParityEven) {
|
|
1507
|
+
if (!fenceParityEven) return true;
|
|
1508
|
+
if (newTail.includes("\r")) return true;
|
|
1509
|
+
if (newTail.includes("```")) return true;
|
|
1510
|
+
if (newTail.includes("$$") || newTail.includes("\\[") || newTail.includes("\\]")) {
|
|
1511
|
+
return true;
|
|
1512
|
+
}
|
|
1513
|
+
if (/\n{4,}/.test(newTail)) return true;
|
|
1514
|
+
if (newTail.includes("\0")) return true;
|
|
1515
|
+
return hasPipeRowLine(newTail);
|
|
1516
|
+
}
|
|
1517
|
+
function hasPipeRowLine(text) {
|
|
1518
|
+
const lines = text.split("\n");
|
|
1519
|
+
return lines.some((line) => isPipeRowLike(line));
|
|
1520
|
+
}
|
|
1521
|
+
function isSuspiciousBoundary(committedLastLine, newTail) {
|
|
1522
|
+
const newlineIdx = newTail.indexOf("\n");
|
|
1523
|
+
const firstLine = newlineIdx === -1 ? newTail : newTail.slice(0, newlineIdx);
|
|
1524
|
+
if (HEADING_LIKE_RE.test(firstLine) && committedLastLine !== "") return true;
|
|
1525
|
+
if (HEADING_LIKE_RE.test(committedLastLine) && firstLine !== "") return true;
|
|
1526
|
+
if (isPipeRowLike(firstLine) || isPipeRowLike(committedLastLine)) {
|
|
1527
|
+
return true;
|
|
1528
|
+
}
|
|
1529
|
+
return polishBreathInsertsBlankBetween(committedLastLine, firstLine);
|
|
1530
|
+
}
|
|
1412
1531
|
function renderPrefixTurnBlock(role, content, contentWidth, colorEnabled) {
|
|
1413
1532
|
content = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
1414
1533
|
if (role === "user") {
|
|
@@ -1450,75 +1569,105 @@ function renderTailTurnBlock(role, content, contentWidth, colorEnabled) {
|
|
|
1450
1569
|
return lines;
|
|
1451
1570
|
}
|
|
1452
1571
|
}
|
|
1453
|
-
function getStreamingTurnLines(role, content, contentWidth, colorEnabled,
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
if (!
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1572
|
+
function getStreamingTurnLines(role, content, contentWidth, colorEnabled, themeFingerprint) {
|
|
1573
|
+
const forceValidate = process.env.NOLO_TUI_RENDER_VALIDATE === "1";
|
|
1574
|
+
let cache = streamingTurnCache;
|
|
1575
|
+
const geometryChanged = !cache || cache.role !== role || cache.contentWidth !== contentWidth || cache.colorEnabled !== colorEnabled || cache.themeFingerprint !== themeFingerprint;
|
|
1576
|
+
if (geometryChanged || !content.startsWith(cache.fullContent)) {
|
|
1577
|
+
return rebuildStreamingCache(content, role, contentWidth, colorEnabled, themeFingerprint);
|
|
1578
|
+
}
|
|
1579
|
+
if (content === cache.fullContent && cache.lastResult) {
|
|
1580
|
+
return cache.lastResult;
|
|
1581
|
+
}
|
|
1582
|
+
const suffix = content.slice(cache.fullContent.length);
|
|
1583
|
+
if (suffix === "") {
|
|
1584
|
+
return cache.lastResult ?? cache.committedLines;
|
|
1585
|
+
}
|
|
1586
|
+
const renderFull = () => {
|
|
1587
|
+
const lines = renderTurnBlock(role, content, contentWidth, colorEnabled);
|
|
1588
|
+
cache.fullContent = content;
|
|
1589
|
+
cache.fenceFlips = countFenceFlips(content);
|
|
1590
|
+
const lastNewline = content.lastIndexOf("\n");
|
|
1591
|
+
cache.completeContentLength = lastNewline + 1;
|
|
1592
|
+
cache.committedLines = lastNewline === -1 ? [] : renderTurnBlock(role, content.slice(0, lastNewline), contentWidth, colorEnabled);
|
|
1593
|
+
cache.pendingTail = content.slice(lastNewline + 1);
|
|
1594
|
+
cache.committedLastLine = lastNewline === -1 ? "" : lastLogicalLineOf(content.slice(0, lastNewline + 1));
|
|
1595
|
+
cache.lastResult = lines;
|
|
1596
|
+
return lines;
|
|
1597
|
+
};
|
|
1598
|
+
const newTail = cache.pendingTail + suffix;
|
|
1599
|
+
if (!cache.fastPathEnabled || isSuspiciousAppend(newTail, cache.fenceFlips % 2 === 0) || isSuspiciousBoundary(cache.committedLastLine, newTail)) {
|
|
1600
|
+
return renderFull();
|
|
1601
|
+
}
|
|
1602
|
+
const tailRenderer = cache.completeContentLength === 0 ? renderPrefixTurnBlock : renderTailTurnBlock;
|
|
1603
|
+
const tailLines = tailRenderer(role, newTail, contentWidth, colorEnabled);
|
|
1604
|
+
const composed = cache.completeContentLength === 0 ? tailLines : [...cache.committedLines, ...tailLines];
|
|
1605
|
+
if (!cache.spliceValidated || forceValidate) {
|
|
1606
|
+
const full = renderTurnBlock(role, content, contentWidth, colorEnabled);
|
|
1607
|
+
cache.spliceValidated = true;
|
|
1608
|
+
if (composed.length === full.length && composed.every((line, index) => line === full[index])) {
|
|
1609
|
+
commitCompletedTail(cache, content, newTail, role, contentWidth, colorEnabled);
|
|
1610
|
+
cache.lastResult = composed;
|
|
1611
|
+
return composed;
|
|
1612
|
+
}
|
|
1613
|
+
cache.fastPathEnabled = false;
|
|
1614
|
+
return renderFull();
|
|
1615
|
+
}
|
|
1616
|
+
commitCompletedTail(cache, content, newTail, role, contentWidth, colorEnabled);
|
|
1617
|
+
cache.lastResult = composed;
|
|
1618
|
+
return composed;
|
|
1619
|
+
}
|
|
1620
|
+
function lastLogicalLineOf(content) {
|
|
1621
|
+
const trimmed = content.replace(/\n$/, "");
|
|
1622
|
+
const idx = trimmed.lastIndexOf("\n");
|
|
1623
|
+
return idx === -1 ? trimmed : trimmed.slice(idx + 1);
|
|
1624
|
+
}
|
|
1625
|
+
function rebuildStreamingCache(content, role, contentWidth, colorEnabled, themeFingerprint) {
|
|
1626
|
+
const lines = renderTurnBlock(role, content, contentWidth, colorEnabled);
|
|
1627
|
+
const lastNewline = content.lastIndexOf("\n");
|
|
1628
|
+
streamingTurnCache = {
|
|
1629
|
+
role,
|
|
1630
|
+
contentWidth,
|
|
1631
|
+
colorEnabled,
|
|
1632
|
+
themeFingerprint,
|
|
1633
|
+
fullContent: content,
|
|
1634
|
+
// committed 渲染源剥掉段尾 \n(幽灵空行修正,见 renderFull 注释)。
|
|
1635
|
+
committedLines: lastNewline === -1 ? [] : renderTurnBlock(role, content.slice(0, lastNewline), contentWidth, colorEnabled),
|
|
1636
|
+
completeContentLength: lastNewline + 1,
|
|
1637
|
+
pendingTail: content.slice(lastNewline + 1),
|
|
1638
|
+
fenceFlips: countFenceFlips(content),
|
|
1639
|
+
committedLastLine: lastNewline === -1 ? "" : lastLogicalLineOf(content.slice(0, lastNewline + 1)),
|
|
1640
|
+
fastPathEnabled: true,
|
|
1641
|
+
spliceValidated: false,
|
|
1642
|
+
lastResult: lines
|
|
1643
|
+
};
|
|
1644
|
+
return lines;
|
|
1645
|
+
}
|
|
1646
|
+
function commitCompletedTail(cache, content, newTail, role, contentWidth, colorEnabled) {
|
|
1647
|
+
const lastNewline = newTail.lastIndexOf("\n");
|
|
1648
|
+
if (lastNewline !== -1) {
|
|
1649
|
+
const completePart = newTail.slice(0, lastNewline + 1);
|
|
1650
|
+
const renderSource = completePart.slice(0, -1);
|
|
1651
|
+
const renderer = cache.completeContentLength === 0 ? renderPrefixTurnBlock : renderTailTurnBlock;
|
|
1652
|
+
cache.committedLines.push(
|
|
1653
|
+
...renderer(role, renderSource, contentWidth, colorEnabled)
|
|
1654
|
+
);
|
|
1655
|
+
cache.completeContentLength += completePart.length;
|
|
1656
|
+
cache.fenceFlips += countFenceFlips(completePart);
|
|
1657
|
+
cache.committedLastLine = lastLogicalLineOf(completePart);
|
|
1658
|
+
cache.pendingTail = newTail.slice(lastNewline + 1);
|
|
1507
1659
|
}
|
|
1508
|
-
|
|
1509
|
-
const tailLines = renderTailTurnBlock(role, tailContent, contentWidth, colorEnabled);
|
|
1510
|
-
return [...streamingTurnCache.prefixLines, ...tailLines];
|
|
1660
|
+
cache.fullContent = content;
|
|
1511
1661
|
}
|
|
1512
1662
|
function countTurnLines(role, content, contentWidth, command) {
|
|
1513
1663
|
return layoutTurnRows(role, content, contentWidth, false, command).length;
|
|
1514
1664
|
}
|
|
1515
1665
|
function buildTurnOffsets(history, contentWidth) {
|
|
1516
|
-
const density = getActiveDensity();
|
|
1517
1666
|
const entries = [];
|
|
1518
1667
|
let offset = 0;
|
|
1519
1668
|
for (let i = 0; i < history.turns.length; i++) {
|
|
1520
1669
|
const turn = history.turns[i];
|
|
1521
|
-
const separatorAbove =
|
|
1670
|
+
const separatorAbove = i > 0 && turn.role === "user" ? 1 : 0;
|
|
1522
1671
|
offset += separatorAbove;
|
|
1523
1672
|
const cacheMap = turnLineCountCache.get(turn);
|
|
1524
1673
|
let lineCount;
|
|
@@ -1547,7 +1696,6 @@ function getAllTurnEntries(history, contentWidth) {
|
|
|
1547
1696
|
const { entries, totalLines: finalizedLines } = buildTurnOffsets(history, contentWidth);
|
|
1548
1697
|
const turns = [...history.turns];
|
|
1549
1698
|
if (history.currentRole !== null && history.currentContent) {
|
|
1550
|
-
const density = getActiveDensity();
|
|
1551
1699
|
const colorEnabled = resolveCliColorEnabled();
|
|
1552
1700
|
const themeFingerprint = tuiRenderThemeFingerprint(colorEnabled);
|
|
1553
1701
|
const streamingLines = getStreamingTurnLines(
|
|
@@ -1555,10 +1703,9 @@ function getAllTurnEntries(history, contentWidth) {
|
|
|
1555
1703
|
history.currentContent,
|
|
1556
1704
|
contentWidth,
|
|
1557
1705
|
colorEnabled,
|
|
1558
|
-
density,
|
|
1559
1706
|
themeFingerprint
|
|
1560
1707
|
);
|
|
1561
|
-
const sep = currentTurnSeparator(history
|
|
1708
|
+
const sep = currentTurnSeparator(history);
|
|
1562
1709
|
const startRow = finalizedLines + sep;
|
|
1563
1710
|
entries.push({
|
|
1564
1711
|
startRow,
|
|
@@ -1573,12 +1720,11 @@ function getAllTurnEntries(history, contentWidth) {
|
|
|
1573
1720
|
}
|
|
1574
1721
|
return { entries, totalLines: finalizedLines, turns };
|
|
1575
1722
|
}
|
|
1576
|
-
function currentTurnSeparator(history
|
|
1577
|
-
return
|
|
1723
|
+
function currentTurnSeparator(history) {
|
|
1724
|
+
return history.turns.length > 0 && history.currentRole === "user" ? 1 : 0;
|
|
1578
1725
|
}
|
|
1579
1726
|
function buildHistoryLayoutRows(history, contentWidth) {
|
|
1580
1727
|
const colorEnabled = resolveCliColorEnabled();
|
|
1581
|
-
const density = getActiveDensity();
|
|
1582
1728
|
const themeFingerprint = tuiRenderThemeFingerprint(colorEnabled);
|
|
1583
1729
|
const rows = [];
|
|
1584
1730
|
const separatorRow = () => ({
|
|
@@ -1589,20 +1735,19 @@ function buildHistoryLayoutRows(history, contentWidth) {
|
|
|
1589
1735
|
});
|
|
1590
1736
|
for (let i = 0; i < history.turns.length; i++) {
|
|
1591
1737
|
const turn = history.turns[i];
|
|
1592
|
-
if (
|
|
1738
|
+
if (i > 0 && turn.role === "user") {
|
|
1593
1739
|
rows.push(separatorRow());
|
|
1594
1740
|
}
|
|
1595
1741
|
rows.push(...getTurnLayoutRows(
|
|
1596
1742
|
turn,
|
|
1597
1743
|
contentWidth,
|
|
1598
1744
|
colorEnabled,
|
|
1599
|
-
density,
|
|
1600
1745
|
themeFingerprint
|
|
1601
1746
|
));
|
|
1602
1747
|
}
|
|
1603
1748
|
if (history.currentRole !== null && history.currentContent) {
|
|
1604
1749
|
const i = history.turns.length;
|
|
1605
|
-
if (
|
|
1750
|
+
if (i > 0 && history.currentRole === "user") {
|
|
1606
1751
|
rows.push(separatorRow());
|
|
1607
1752
|
}
|
|
1608
1753
|
rows.push(
|
|
@@ -1625,7 +1770,6 @@ function renderHistory(output, history, inputLines, selection) {
|
|
|
1625
1770
|
const visibleHeight = Math.max(1, rows - inputLines);
|
|
1626
1771
|
const contentWidth = Math.max(1, columns - 1);
|
|
1627
1772
|
const colorEnabled = resolveCliColorEnabled();
|
|
1628
|
-
const density = getActiveDensity();
|
|
1629
1773
|
const themeFingerprint = tuiRenderThemeFingerprint(colorEnabled);
|
|
1630
1774
|
const { entries, totalLines: finalizedLines } = buildTurnOffsets(history, contentWidth);
|
|
1631
1775
|
let currentLines = [];
|
|
@@ -1636,10 +1780,9 @@ function renderHistory(output, history, inputLines, selection) {
|
|
|
1636
1780
|
history.currentContent,
|
|
1637
1781
|
contentWidth,
|
|
1638
1782
|
colorEnabled,
|
|
1639
|
-
density,
|
|
1640
1783
|
themeFingerprint
|
|
1641
1784
|
);
|
|
1642
|
-
const sep = currentTurnSeparator(history
|
|
1785
|
+
const sep = currentTurnSeparator(history);
|
|
1643
1786
|
currentStart = finalizedLines + sep;
|
|
1644
1787
|
}
|
|
1645
1788
|
const totalLines = currentStart >= 0 ? currentStart + currentLines.length : finalizedLines;
|
|
@@ -1670,7 +1813,7 @@ function renderHistory(output, history, inputLines, selection) {
|
|
|
1670
1813
|
const turn = history.turns[i];
|
|
1671
1814
|
const cached = getCachedTurnLine(turn);
|
|
1672
1815
|
let layoutRows;
|
|
1673
|
-
if (cached && cached.width === contentWidth && cached.color === colorEnabled && cached.
|
|
1816
|
+
if (cached && cached.width === contentWidth && cached.color === colorEnabled && cached.themeFingerprint === themeFingerprint) {
|
|
1674
1817
|
layoutRows = cached.layoutRows;
|
|
1675
1818
|
} else {
|
|
1676
1819
|
renderCacheMissCount += 1;
|
|
@@ -1678,7 +1821,6 @@ function renderHistory(output, history, inputLines, selection) {
|
|
|
1678
1821
|
turn,
|
|
1679
1822
|
contentWidth,
|
|
1680
1823
|
colorEnabled,
|
|
1681
|
-
density,
|
|
1682
1824
|
themeFingerprint
|
|
1683
1825
|
);
|
|
1684
1826
|
}
|
|
@@ -1690,7 +1832,7 @@ function renderHistory(output, history, inputLines, selection) {
|
|
|
1690
1832
|
}
|
|
1691
1833
|
}
|
|
1692
1834
|
if (currentStart >= 0) {
|
|
1693
|
-
const separatorAbove = currentTurnSeparator(history
|
|
1835
|
+
const separatorAbove = currentTurnSeparator(history);
|
|
1694
1836
|
if (separatorAbove > 0) {
|
|
1695
1837
|
const sepRow = currentStart - 1;
|
|
1696
1838
|
if (sepRow >= winStart && sepRow < winEnd) {
|
|
@@ -1750,6 +1892,7 @@ function createHistoryOutputStream(history, onUpdate) {
|
|
|
1750
1892
|
const stream = {
|
|
1751
1893
|
isTTY: true,
|
|
1752
1894
|
assistantLabelManaged: true,
|
|
1895
|
+
tuiTrees: true,
|
|
1753
1896
|
write(chunk) {
|
|
1754
1897
|
const text = typeof chunk === "string" ? chunk : chunk.toString();
|
|
1755
1898
|
if (applyOutputChunkToCurrentTurn(history, text)) {
|
|
@@ -1758,9 +1901,9 @@ function createHistoryOutputStream(history, onUpdate) {
|
|
|
1758
1901
|
return true;
|
|
1759
1902
|
},
|
|
1760
1903
|
writeToolBlock(chunk) {
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1904
|
+
const applied = applyOutputChunkToCurrentTurn(history, chunk, "tool");
|
|
1905
|
+
if (applied) onUpdate();
|
|
1906
|
+
return applied;
|
|
1764
1907
|
}
|
|
1765
1908
|
};
|
|
1766
1909
|
return stream;
|
|
@@ -1774,7 +1917,7 @@ function applyScrollAction(history, action, output, inputLines) {
|
|
|
1774
1917
|
const { totalLines: finalizedLines } = buildTurnOffsets(history, contentWidth);
|
|
1775
1918
|
let totalLines = finalizedLines;
|
|
1776
1919
|
if (history.currentRole !== null && history.currentContent) {
|
|
1777
|
-
totalLines += currentTurnSeparator(history
|
|
1920
|
+
totalLines += currentTurnSeparator(history) + countTurnLines(history.currentRole, history.currentContent, contentWidth);
|
|
1778
1921
|
}
|
|
1779
1922
|
const maxScrollTop = Math.max(0, totalLines - visibleHeight);
|
|
1780
1923
|
history.followBottom = false;
|
|
@@ -1823,6 +1966,14 @@ function resolveDialogBottomRow(args) {
|
|
|
1823
1966
|
return Math.max(1, resolveTtyRows(args.output) - args.inputLines);
|
|
1824
1967
|
}
|
|
1825
1968
|
function createDialogHost(args) {
|
|
1969
|
+
let keyboardClaimed = false;
|
|
1970
|
+
const claimKeyboard = () => {
|
|
1971
|
+
keyboardClaimed = true;
|
|
1972
|
+
};
|
|
1973
|
+
const releaseKeyboard = () => {
|
|
1974
|
+
args.drainDecoder?.();
|
|
1975
|
+
keyboardClaimed = false;
|
|
1976
|
+
};
|
|
1826
1977
|
return {
|
|
1827
1978
|
async run(body) {
|
|
1828
1979
|
const anchor = {
|
|
@@ -1832,14 +1983,25 @@ function createDialogHost(args) {
|
|
|
1832
1983
|
inputLines: args.composer.getInputLines()
|
|
1833
1984
|
})
|
|
1834
1985
|
};
|
|
1986
|
+
claimKeyboard();
|
|
1835
1987
|
args.composer.pause();
|
|
1836
1988
|
try {
|
|
1837
1989
|
return await body(anchor);
|
|
1838
1990
|
} finally {
|
|
1839
1991
|
resetHistoryFrameDiffCache(args.output);
|
|
1840
1992
|
args.composer.resumeFromDialog();
|
|
1993
|
+
releaseKeyboard();
|
|
1841
1994
|
}
|
|
1842
|
-
}
|
|
1995
|
+
},
|
|
1996
|
+
async withKeyboard(body) {
|
|
1997
|
+
claimKeyboard();
|
|
1998
|
+
try {
|
|
1999
|
+
return await body();
|
|
2000
|
+
} finally {
|
|
2001
|
+
releaseKeyboard();
|
|
2002
|
+
}
|
|
2003
|
+
},
|
|
2004
|
+
isKeyboardClaimed: () => keyboardClaimed
|
|
1843
2005
|
};
|
|
1844
2006
|
}
|
|
1845
2007
|
|
|
@@ -1865,13 +2027,14 @@ function formatUnassignedFact(snapshot) {
|
|
|
1865
2027
|
}
|
|
1866
2028
|
function sanitizeRunSnapshotForNormal(snapshot) {
|
|
1867
2029
|
const clean = (value) => stripAnsi(value).replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
|
|
2030
|
+
const errorMessage = snapshot.errorMessage?.trim() ? redactSecrets(clean(snapshot.errorMessage)) : void 0;
|
|
1868
2031
|
return {
|
|
1869
2032
|
...snapshot,
|
|
1870
2033
|
agentName: snapshot.agentName ? clean(snapshot.agentName) : snapshot.agentName,
|
|
1871
2034
|
taskPreview: snapshot.taskPreview ? clean(snapshot.taskPreview) : snapshot.taskPreview,
|
|
1872
2035
|
lastAssistantText: snapshot.lastAssistantText ? clean(snapshot.lastAssistantText) : snapshot.lastAssistantText,
|
|
1873
2036
|
lastToolNames: snapshot.lastToolNames?.map(clean).filter(Boolean),
|
|
1874
|
-
errorMessage
|
|
2037
|
+
errorMessage,
|
|
1875
2038
|
logLines: void 0
|
|
1876
2039
|
};
|
|
1877
2040
|
}
|
|
@@ -1912,9 +2075,10 @@ function formatAgentRunPanelLines(snapshot, colorEnabled, now = Date.now()) {
|
|
|
1912
2075
|
const progressPart = formatPanelProgress(snapshot, now);
|
|
1913
2076
|
const age = formatTerminalRunAge(snapshot, now);
|
|
1914
2077
|
const agePart = age ? ` \xB7 ${age}` : "";
|
|
2078
|
+
const creditsPart = typeof snapshot.credits === "number" && Number.isFinite(snapshot.credits) ? ` \xB7 ${formatCreditsChip(snapshot.credits)}` : "";
|
|
1915
2079
|
if (!colorEnabled) {
|
|
1916
2080
|
lines.push(
|
|
1917
|
-
`\u{1F916} Sub-Agent: ${name}${runId} \xB7 ${statusSymbol} ${status}${unassignedPart}${agePart}${progressPart}${errPart}`
|
|
2081
|
+
`\u{1F916} Sub-Agent: ${name}${runId} \xB7 ${statusSymbol} ${status}${unassignedPart}${agePart}${progressPart}${creditsPart}${errPart}`
|
|
1918
2082
|
);
|
|
1919
2083
|
} else {
|
|
1920
2084
|
const botIcon = themeText("\u{1F916}", "accent", true);
|
|
@@ -1924,8 +2088,9 @@ function formatAgentRunPanelLines(snapshot, colorEnabled, now = Date.now()) {
|
|
|
1924
2088
|
const unassignedText = unassignedPart ? themeText(unassignedPart, "muted") : "";
|
|
1925
2089
|
const ageText = agePart ? themeText(agePart, "chrome") : "";
|
|
1926
2090
|
const progressText = progressPart ? themeText(progressPart, "chrome") : "";
|
|
2091
|
+
const creditsText = creditsPart ? themeText(creditsPart, "muted") : "";
|
|
1927
2092
|
const errorText = errPart ? themeText(errPart, "danger") : "";
|
|
1928
|
-
lines.push(`${botIcon}${labelText}${nameText}${statusText}${unassignedText}${ageText}${progressText}${errorText}`);
|
|
2093
|
+
lines.push(`${botIcon}${labelText}${nameText}${statusText}${unassignedText}${ageText}${progressText}${creditsText}${errorText}`);
|
|
1929
2094
|
}
|
|
1930
2095
|
const detail = formatPanelDetail(snapshot);
|
|
1931
2096
|
if (detail) {
|
|
@@ -2049,8 +2214,7 @@ function createRunDock(deps) {
|
|
|
2049
2214
|
const ordered = orderedEntries();
|
|
2050
2215
|
if (ordered.length === 0) return [];
|
|
2051
2216
|
const at = now();
|
|
2052
|
-
const
|
|
2053
|
-
const displaySnapshots = mode === "normal" || mode === "hide" ? ordered.map((entry) => sanitizeRunSnapshotForNormal(entry.snapshot)) : ordered.map((entry) => entry.snapshot);
|
|
2217
|
+
const displaySnapshots = ordered.map((entry) => sanitizeRunSnapshotForNormal(entry.snapshot));
|
|
2054
2218
|
if (displaySnapshots.length === 1) {
|
|
2055
2219
|
return formatAgentRunPanelLines(displaySnapshots[0], colorEnabled, at);
|
|
2056
2220
|
}
|
|
@@ -2122,6 +2286,9 @@ function formatRunDockRow(snapshot, colorEnabled, now) {
|
|
|
2122
2286
|
if (typeof snapshot.toolCallCount === "number" && Number.isFinite(snapshot.toolCallCount)) {
|
|
2123
2287
|
facts.push(`${snapshot.toolCallCount} tools`);
|
|
2124
2288
|
}
|
|
2289
|
+
if (typeof snapshot.credits === "number" && Number.isFinite(snapshot.credits)) {
|
|
2290
|
+
facts.push(formatCreditsChip(snapshot.credits, { compact: true }));
|
|
2291
|
+
}
|
|
2125
2292
|
const inFlight = activeInFlight(snapshot);
|
|
2126
2293
|
if (inFlight) {
|
|
2127
2294
|
facts.push(formatInFlightFact(inFlight, now));
|
|
@@ -2224,8 +2391,7 @@ function createActivityIndicator(deps) {
|
|
|
2224
2391
|
onRepaint: deps.onRepaint,
|
|
2225
2392
|
now,
|
|
2226
2393
|
setIntervalFn,
|
|
2227
|
-
clearIntervalFn
|
|
2228
|
-
displayMode: deps.displayMode
|
|
2394
|
+
clearIntervalFn
|
|
2229
2395
|
});
|
|
2230
2396
|
const updateAgentRun = (snapshot) => {
|
|
2231
2397
|
runDock.update(snapshot);
|
|
@@ -2309,6 +2475,8 @@ function snapshotFromRunRecord(record, now) {
|
|
|
2309
2475
|
...startedAt !== void 0 ? { startedAt } : {},
|
|
2310
2476
|
...endedAt !== void 0 ? { finishedAt: endedAt } : {},
|
|
2311
2477
|
...typeof counters?.toolCalls === "number" ? { toolCallCount: counters.toolCalls } : {},
|
|
2478
|
+
// 收尾自报的平台积分 → dock 行「⚡ x.xx 积分」。
|
|
2479
|
+
...typeof record.credits === "number" && Number.isFinite(record.credits) ? { credits: record.credits } : {},
|
|
2312
2480
|
// note 在终态时是死因("orphaned: process gone…"),非终态时只是备注。
|
|
2313
2481
|
...record.note && isAgentRunTerminalStatus(record.status) ? { errorMessage: record.note } : {},
|
|
2314
2482
|
inFlight,
|
|
@@ -2323,6 +2491,7 @@ function fingerprint(snapshot) {
|
|
|
2323
2491
|
snapshot.toolCallCount ?? -1,
|
|
2324
2492
|
snapshot.errorMessage ?? "",
|
|
2325
2493
|
snapshot.finishedAt ?? 0,
|
|
2494
|
+
snapshot.credits ?? -1,
|
|
2326
2495
|
snapshot.inFlight ? [snapshot.inFlight.kind, snapshot.inFlight.name, snapshot.inFlight.startedAt] : null
|
|
2327
2496
|
]);
|
|
2328
2497
|
}
|
|
@@ -3327,6 +3496,20 @@ function summarizeAttachment(img) {
|
|
|
3327
3496
|
].join("\n");
|
|
3328
3497
|
return themeText(text, "chrome", colorEnabled);
|
|
3329
3498
|
}
|
|
3499
|
+
function formatComposerAttachmentLine(images) {
|
|
3500
|
+
if (images.length === 0) return null;
|
|
3501
|
+
const head = images.slice(0, 2).map((img) => `\u{1F4CE} ${img.filename} (${formatBytes(img.sizeBytes)})`);
|
|
3502
|
+
const extra = images.length - head.length;
|
|
3503
|
+
return extra > 0 ? `${head.join(", ")} +${extra}` : head.join(", ");
|
|
3504
|
+
}
|
|
3505
|
+
function popLastAttachedImage(images) {
|
|
3506
|
+
if (images.length === 0) return { handled: false, images };
|
|
3507
|
+
return {
|
|
3508
|
+
handled: true,
|
|
3509
|
+
images: images.slice(0, -1),
|
|
3510
|
+
removed: images[images.length - 1]
|
|
3511
|
+
};
|
|
3512
|
+
}
|
|
3330
3513
|
function mergeAttachedImages(existing, incoming) {
|
|
3331
3514
|
if (incoming.length === 0) return existing;
|
|
3332
3515
|
if (existing.length === 0) return [...incoming];
|
|
@@ -3348,12 +3531,12 @@ function mergeAttachedImages(existing, incoming) {
|
|
|
3348
3531
|
return out;
|
|
3349
3532
|
}
|
|
3350
3533
|
async function readImagePaths(paths, options = {}) {
|
|
3351
|
-
const
|
|
3534
|
+
const resolve4 = options.resolve ?? ((p) => p);
|
|
3352
3535
|
const reportedFailures = /* @__PURE__ */ new Set();
|
|
3353
3536
|
const images = [];
|
|
3354
3537
|
const failures = [];
|
|
3355
3538
|
for (const raw of paths) {
|
|
3356
|
-
const absolute =
|
|
3539
|
+
const absolute = resolve4(raw);
|
|
3357
3540
|
try {
|
|
3358
3541
|
const img = await readImageAsDataUrl(absolute, {
|
|
3359
3542
|
...options.maxBytes !== void 0 ? { maxBytes: options.maxBytes } : {}
|
|
@@ -3393,7 +3576,7 @@ async function resolveAttachmentImageUrls({
|
|
|
3393
3576
|
// packages/cli/tui/clipboardImage.ts
|
|
3394
3577
|
import { createHash } from "node:crypto";
|
|
3395
3578
|
import { mkdirSync } from "node:fs";
|
|
3396
|
-
import { writeFile } from "node:fs/promises";
|
|
3579
|
+
import { readdir, stat as stat2, unlink, writeFile } from "node:fs/promises";
|
|
3397
3580
|
import { tmpdir } from "node:os";
|
|
3398
3581
|
import { join } from "node:path";
|
|
3399
3582
|
var ClipboardImageError = class extends Error {
|
|
@@ -3405,6 +3588,34 @@ var ClipboardImageError = class extends Error {
|
|
|
3405
3588
|
}
|
|
3406
3589
|
};
|
|
3407
3590
|
var DEFAULT_CLIPBOARD_TIMEOUT_MS = 3e4;
|
|
3591
|
+
var CLIPBOARD_TEMP_DIR_NAME = "nolo-clipboard";
|
|
3592
|
+
function getDefaultClipboardTempDir() {
|
|
3593
|
+
return join(tmpdir(), CLIPBOARD_TEMP_DIR_NAME);
|
|
3594
|
+
}
|
|
3595
|
+
var CLIPBOARD_SWEEP_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
3596
|
+
var CLIPBOARD_SWEEP_FILE_RE = /^clip-[0-9a-f]{8,}\.(png|jpg|jpeg|gif|webp)$/;
|
|
3597
|
+
async function sweepStaleClipboardFiles(tempDir, opts = {}) {
|
|
3598
|
+
try {
|
|
3599
|
+
const maxAgeMs = opts.maxAgeMs ?? CLIPBOARD_SWEEP_MAX_AGE_MS;
|
|
3600
|
+
const now = opts.now ?? Date.now();
|
|
3601
|
+
const entries = await readdir(tempDir);
|
|
3602
|
+
let removed = 0;
|
|
3603
|
+
for (const name of entries) {
|
|
3604
|
+
if (!CLIPBOARD_SWEEP_FILE_RE.test(name)) continue;
|
|
3605
|
+
try {
|
|
3606
|
+
const fullPath = join(tempDir, name);
|
|
3607
|
+
const stats = await stat2(fullPath);
|
|
3608
|
+
if (stats.mtimeMs >= now - maxAgeMs) continue;
|
|
3609
|
+
await unlink(fullPath);
|
|
3610
|
+
removed += 1;
|
|
3611
|
+
} catch {
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
return removed;
|
|
3615
|
+
} catch {
|
|
3616
|
+
return 0;
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3408
3619
|
function isRemoteSession(env = process.env) {
|
|
3409
3620
|
return Boolean(env.SSH_CONNECTION || env.SSH_TTY || env.SSH_CLIENT);
|
|
3410
3621
|
}
|
|
@@ -3420,13 +3631,20 @@ function getClipboardImageCommand(deps = {}) {
|
|
|
3420
3631
|
}
|
|
3421
3632
|
if (platform === "darwin") {
|
|
3422
3633
|
const pngpaste = which("pngpaste");
|
|
3423
|
-
if (
|
|
3634
|
+
if (pngpaste) {
|
|
3635
|
+
return { command: [pngpaste, "-"], outputFormat: "raw" };
|
|
3636
|
+
}
|
|
3637
|
+
const osascript = which("osascript");
|
|
3638
|
+
if (!osascript) {
|
|
3424
3639
|
throw new ClipboardImageError(
|
|
3425
3640
|
"binary-missing",
|
|
3426
|
-
"\u672A\u627E\u5230 pngpaste\uFF0C\u8BF7\u8FD0\u884C brew install pngpaste \u5B89\u88C5\uFF0C\u6216\u76F4\u63A5\u62D6\u5165\u56FE\u7247\u6587\u4EF6/\u8F93\u5165\u56FE\u7247\u8DEF\u5F84"
|
|
3641
|
+
"\u672A\u627E\u5230 pngpaste\uFF0C\u4E5F\u672A\u627E\u5230\u7CFB\u7EDF\u81EA\u5E26\u7684 osascript\uFF0C\u65E0\u6CD5\u8BFB\u53D6\u526A\u8D34\u677F\u56FE\u50CF\u3002\u8BF7\u8FD0\u884C brew install pngpaste \u5B89\u88C5\uFF0C\u6216\u76F4\u63A5\u62D6\u5165\u56FE\u7247\u6587\u4EF6/\u8F93\u5165\u56FE\u7247\u8DEF\u5F84"
|
|
3427
3642
|
);
|
|
3428
3643
|
}
|
|
3429
|
-
return {
|
|
3644
|
+
return {
|
|
3645
|
+
command: [osascript, "-e", "the clipboard as \xABclass PNGf\xBB"],
|
|
3646
|
+
outputFormat: "osascript-hex"
|
|
3647
|
+
};
|
|
3430
3648
|
}
|
|
3431
3649
|
if (platform === "win32") {
|
|
3432
3650
|
const ps = which("powershell.exe") || which("powershell") || which("pwsh.exe") || which("pwsh");
|
|
@@ -3443,7 +3661,8 @@ function getClipboardImageCommand(deps = {}) {
|
|
|
3443
3661
|
"-NonInteractive",
|
|
3444
3662
|
"-Command",
|
|
3445
3663
|
"$img = Get-Clipboard -Format Image; if ($img) { $ms = New-Object System.IO.MemoryStream; $img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png); [Console]::OpenStandardOutput().Write($ms.ToArray(), 0, $ms.Length) }"
|
|
3446
|
-
]
|
|
3664
|
+
],
|
|
3665
|
+
outputFormat: "raw"
|
|
3447
3666
|
};
|
|
3448
3667
|
}
|
|
3449
3668
|
if (platform === "linux") {
|
|
@@ -3456,17 +3675,18 @@ function getClipboardImageCommand(deps = {}) {
|
|
|
3456
3675
|
"\u672A\u627E\u5230 wl-paste\uFF0C\u8BF7\u5B89\u88C5 wl-clipboard (\u4F8B\u5982 sudo apt install wl-clipboard)\uFF0C\u6216\u76F4\u63A5\u62D6\u5165\u56FE\u7247\u6587\u4EF6/\u8F93\u5165\u56FE\u7247\u8DEF\u5F84"
|
|
3457
3676
|
);
|
|
3458
3677
|
}
|
|
3459
|
-
return { command: [wlPaste2, "--type", "image/png"] };
|
|
3678
|
+
return { command: [wlPaste2, "--type", "image/png"], outputFormat: "raw" };
|
|
3460
3679
|
}
|
|
3461
3680
|
const xclip = which("xclip");
|
|
3462
3681
|
if (xclip) {
|
|
3463
3682
|
return {
|
|
3464
|
-
command: [xclip, "-selection", "clipboard", "-t", "image/png", "-o"]
|
|
3683
|
+
command: [xclip, "-selection", "clipboard", "-t", "image/png", "-o"],
|
|
3684
|
+
outputFormat: "raw"
|
|
3465
3685
|
};
|
|
3466
3686
|
}
|
|
3467
3687
|
const wlPaste = which("wl-paste");
|
|
3468
3688
|
if (wlPaste) {
|
|
3469
|
-
return { command: [wlPaste, "--type", "image/png"] };
|
|
3689
|
+
return { command: [wlPaste, "--type", "image/png"], outputFormat: "raw" };
|
|
3470
3690
|
}
|
|
3471
3691
|
throw new ClipboardImageError(
|
|
3472
3692
|
"binary-missing",
|
|
@@ -3478,6 +3698,21 @@ function getClipboardImageCommand(deps = {}) {
|
|
|
3478
3698
|
`\u5F53\u524D\u64CD\u4F5C\u7CFB\u7EDF\u6682\u4E0D\u652F\u6301\u526A\u8D34\u677F\u56FE\u50CF\u8BFB\u53D6: ${platform}`
|
|
3479
3699
|
);
|
|
3480
3700
|
}
|
|
3701
|
+
var OSASCRIPT_EMPTY_CLIPBOARD_MESSAGE = "\u526A\u8D34\u677F\u4E2D\u6CA1\u6709\u56FE\u50CF\u6570\u636E\u3002\u53EF\u76F4\u63A5\u62D6\u5165\u56FE\u7247\u6587\u4EF6\u3001\u8F93\u5165\u56FE\u7247\u8DEF\u5F84\uFF0C\u6216\u8FD0\u884C brew install pngpaste \u4EE5\u652F\u6301\u66F4\u591A\u56FE\u7247\u683C\u5F0F";
|
|
3702
|
+
function decodeOsascriptPngHex(text) {
|
|
3703
|
+
const match = /PNGf\s*([0-9A-Fa-f]*)/.exec(text);
|
|
3704
|
+
if (!match) return { kind: "empty" };
|
|
3705
|
+
const hex = match[1] ?? "";
|
|
3706
|
+
const tail = text.slice(match.index + match[0].length).replace(/[\s»]*$/, "");
|
|
3707
|
+
if (tail.length > 0) return { kind: "invalid" };
|
|
3708
|
+
if (hex.length === 0) return { kind: "empty" };
|
|
3709
|
+
if (hex.length % 2 !== 0) return { kind: "invalid" };
|
|
3710
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
3711
|
+
for (let i = 0; i < bytes.length; i += 1) {
|
|
3712
|
+
bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
3713
|
+
}
|
|
3714
|
+
return { kind: "bytes", bytes };
|
|
3715
|
+
}
|
|
3481
3716
|
async function readClipboardImage(deps = {}) {
|
|
3482
3717
|
const env = deps.env ?? process.env;
|
|
3483
3718
|
const platform = deps.platform ?? process.platform;
|
|
@@ -3485,8 +3720,8 @@ async function readClipboardImage(deps = {}) {
|
|
|
3485
3720
|
const spawn = deps.spawn ?? Bun.spawn;
|
|
3486
3721
|
const maxBytes = deps.maxBytes ?? DEFAULT_MAX_IMAGE_BYTES;
|
|
3487
3722
|
const timeoutMs = deps.timeoutMs ?? DEFAULT_CLIPBOARD_TIMEOUT_MS;
|
|
3488
|
-
const tempDir = deps.tempDir ??
|
|
3489
|
-
const { command } = getClipboardImageCommand({
|
|
3723
|
+
const tempDir = deps.tempDir ?? getDefaultClipboardTempDir();
|
|
3724
|
+
const { command, outputFormat } = getClipboardImageCommand({
|
|
3490
3725
|
env,
|
|
3491
3726
|
platform,
|
|
3492
3727
|
which
|
|
@@ -3520,6 +3755,12 @@ async function readClipboardImage(deps = {}) {
|
|
|
3520
3755
|
timeoutPromise
|
|
3521
3756
|
]);
|
|
3522
3757
|
if (exitCode !== 0 || stdoutArrayBuffer.byteLength === 0) {
|
|
3758
|
+
if (outputFormat === "osascript-hex") {
|
|
3759
|
+
throw new ClipboardImageError(
|
|
3760
|
+
"empty-clipboard",
|
|
3761
|
+
OSASCRIPT_EMPTY_CLIPBOARD_MESSAGE + (stderrText.trim() ? `: ${stderrText.trim()}` : "")
|
|
3762
|
+
);
|
|
3763
|
+
}
|
|
3523
3764
|
throw new ClipboardImageError(
|
|
3524
3765
|
"empty-clipboard",
|
|
3525
3766
|
"\u526A\u8D34\u677F\u4E2D\u6CA1\u6709\u56FE\u50CF\u6570\u636E\uFF0C\u8BF7\u76F4\u63A5\u62D6\u5165\u56FE\u7247\u6587\u4EF6\u6216\u8F93\u5165\u56FE\u7247\u8DEF\u5F84" + (stderrText.trim() ? `: ${stderrText.trim()}` : "")
|
|
@@ -3538,6 +3779,23 @@ async function readClipboardImage(deps = {}) {
|
|
|
3538
3779
|
`\u8BFB\u53D6\u526A\u8D34\u677F\u56FE\u50CF\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
|
|
3539
3780
|
);
|
|
3540
3781
|
}
|
|
3782
|
+
if (outputFormat === "osascript-hex") {
|
|
3783
|
+
const text = Buffer.from(stdoutBuf).toString("utf8");
|
|
3784
|
+
const decoded = decodeOsascriptPngHex(text);
|
|
3785
|
+
if (decoded.kind === "empty") {
|
|
3786
|
+
throw new ClipboardImageError(
|
|
3787
|
+
"empty-clipboard",
|
|
3788
|
+
OSASCRIPT_EMPTY_CLIPBOARD_MESSAGE
|
|
3789
|
+
);
|
|
3790
|
+
}
|
|
3791
|
+
if (decoded.kind === "invalid") {
|
|
3792
|
+
throw new ClipboardImageError(
|
|
3793
|
+
"read-failed",
|
|
3794
|
+
"\u526A\u8D34\u677F\u56FE\u50CF\u6570\u636E\u65E0\u6CD5\u89E3\u6790\uFF08hex \u7F16\u7801\u635F\u574F\uFF09\uFF0C\u8BF7\u91CD\u65B0\u590D\u5236\u56FE\u7247\u540E\u91CD\u8BD5\uFF0C\u6216\u76F4\u63A5\u62D6\u5165\u56FE\u7247\u6587\u4EF6/\u8F93\u5165\u56FE\u7247\u8DEF\u5F84"
|
|
3795
|
+
);
|
|
3796
|
+
}
|
|
3797
|
+
stdoutBuf = decoded.bytes;
|
|
3798
|
+
}
|
|
3541
3799
|
const compressResult = await compressImage(stdoutBuf, "image/png");
|
|
3542
3800
|
const finalBuf = compressResult.buffer;
|
|
3543
3801
|
const finalMime = compressResult.mime;
|
|
@@ -3598,7 +3856,7 @@ function parseStatusOutput(branch, status) {
|
|
|
3598
3856
|
return { branch, modified, untracked };
|
|
3599
3857
|
}
|
|
3600
3858
|
function execFileUtf8(file, args, cwd) {
|
|
3601
|
-
return new Promise((
|
|
3859
|
+
return new Promise((resolve4, reject) => {
|
|
3602
3860
|
execFile(
|
|
3603
3861
|
file,
|
|
3604
3862
|
args,
|
|
@@ -3608,7 +3866,7 @@ function execFileUtf8(file, args, cwd) {
|
|
|
3608
3866
|
reject(err);
|
|
3609
3867
|
return;
|
|
3610
3868
|
}
|
|
3611
|
-
|
|
3869
|
+
resolve4(typeof stdout === "string" ? stdout : String(stdout ?? ""));
|
|
3612
3870
|
}
|
|
3613
3871
|
);
|
|
3614
3872
|
});
|
|
@@ -3650,6 +3908,12 @@ function renderComposerTokenChip(tokens, fallbackContextWindow, estimatedContext
|
|
|
3650
3908
|
const pctText = pct > 0 && pct < 10 ? pct.toFixed(1) : Math.round(pct).toString();
|
|
3651
3909
|
return `context: ${pctText}% (${formatTokenCount(used)}/${formatTokenCount(cw)})`;
|
|
3652
3910
|
}
|
|
3911
|
+
function resolveStatusLineCredits(state) {
|
|
3912
|
+
const base = state.dialogCreditsBase;
|
|
3913
|
+
const session = state.sessionCredits;
|
|
3914
|
+
if (base === void 0 && session === void 0) return void 0;
|
|
3915
|
+
return (base ?? 0) + (session ?? 0);
|
|
3916
|
+
}
|
|
3653
3917
|
function renderStatusLine(state, maxWidth) {
|
|
3654
3918
|
const colorEnabled = resolveCliColorEnabled();
|
|
3655
3919
|
const sep = themeText(" \xB7 ", "chrome", colorEnabled);
|
|
@@ -3666,18 +3930,19 @@ function renderStatusLine(state, maxWidth) {
|
|
|
3666
3930
|
const untrackedText = untracked > 0 ? ` ${themeText(`?${untracked}`, "muted", colorEnabled)}` : "";
|
|
3667
3931
|
parts.push(`${branchText}${modifiedText}${untrackedText}`);
|
|
3668
3932
|
}
|
|
3669
|
-
const credits = state
|
|
3670
|
-
const creditsSuffix = credits !== void 0 && credits > 0 ? ` \xB7 \u26A1 ${credits.toFixed(2)} \u79EF\u5206` : "";
|
|
3933
|
+
const credits = resolveStatusLineCredits(state);
|
|
3671
3934
|
const tokenSegment = themeText(
|
|
3672
3935
|
renderComposerTokenChip(
|
|
3673
3936
|
state.turnTokens,
|
|
3674
3937
|
state.contextWindow,
|
|
3675
3938
|
state.estimatedContextTokens
|
|
3676
|
-
)
|
|
3939
|
+
),
|
|
3677
3940
|
"muted",
|
|
3678
3941
|
colorEnabled
|
|
3679
3942
|
);
|
|
3943
|
+
const creditsSegment = credits !== void 0 && credits > 0 ? themeText(formatCreditsChip(credits), "muted", colorEnabled) : "";
|
|
3680
3944
|
parts.push(tokenSegment);
|
|
3945
|
+
if (creditsSegment) parts.push(creditsSegment);
|
|
3681
3946
|
const runningTaskCount = getProcessRegistry().listBackground().filter((p) => p.status === "running").length;
|
|
3682
3947
|
if (runningTaskCount > 0) {
|
|
3683
3948
|
parts.push(themeText(`\u2699 ${runningTaskCount} running`, "info", colorEnabled));
|
|
@@ -3688,7 +3953,8 @@ function renderStatusLine(state, maxWidth) {
|
|
|
3688
3953
|
let visibleParts = parts;
|
|
3689
3954
|
if (maxWidth && maxWidth > 0) {
|
|
3690
3955
|
const widthOf = (segments) => displayWidth(stripAnsi(segments.join(" \xB7 "))) + 2;
|
|
3691
|
-
for (const optional of [
|
|
3956
|
+
for (const optional of [cwdSegment, agentSegment, tokenSegment, creditsSegment]) {
|
|
3957
|
+
if (!optional) continue;
|
|
3692
3958
|
if (widthOf(visibleParts) <= maxWidth) break;
|
|
3693
3959
|
visibleParts = visibleParts.filter((part) => part !== optional);
|
|
3694
3960
|
}
|
|
@@ -3721,6 +3987,9 @@ function renderStatusLine(state, maxWidth) {
|
|
|
3721
3987
|
colorEnabled
|
|
3722
3988
|
));
|
|
3723
3989
|
}
|
|
3990
|
+
if (creditsSegment) {
|
|
3991
|
+
emergency.push(themeText(formatCreditsChip(credits ?? 0, { compact: true }), "muted", colorEnabled));
|
|
3992
|
+
}
|
|
3724
3993
|
visibleParts = [emergency.join(" ")];
|
|
3725
3994
|
}
|
|
3726
3995
|
}
|
|
@@ -3851,6 +4120,20 @@ function renderTuiHelp(colorEnabled = resolveCliColorEnabled()) {
|
|
|
3851
4120
|
return themeText(line, "muted", colorEnabled);
|
|
3852
4121
|
}).join("\n");
|
|
3853
4122
|
}
|
|
4123
|
+
function renderCreditsDebug(state, colorEnabled = resolveCliColorEnabled()) {
|
|
4124
|
+
const credits = resolveStatusLineCredits(state);
|
|
4125
|
+
const lines = [
|
|
4126
|
+
"[credits debug] \u79EF\u5206\u94FE\u8DEF\u771F\u503C",
|
|
4127
|
+
`\u2022 dialogCreditsBase : ${state.dialogCreditsBase?.toFixed(4) ?? "undefined\uFF08\u670D\u52A1\u7AEF\u57FA\u6570\u672A seed\u2014\u2014/pick \u540E\u9996\u6B21\u8BFB\u53D6\u5931\u8D25\u6216\u670D\u52A1\u7AEF\u672A\u5199 totalCost\uFF09"}`,
|
|
4128
|
+
`\u2022 sessionCredits : ${state.sessionCredits?.toFixed(4) ?? "undefined\uFF08\u672C\u4F1A\u8BDD\u8FD8\u6CA1\u6709\u5E73\u53F0\u8BA1\u8D39\u8F6E\uFF09"}`,
|
|
4129
|
+
`\u2022 \u72B6\u6001\u884C\u663E\u793A : ${credits !== void 0 && credits > 0 ? formatCreditsChip(credits) : "\uFF08\u65E0 \u26A1\u2014\u2014\u4E24\u6BB5\u5408\u8BA1\u4E3A 0/undefined\uFF09"}`,
|
|
4130
|
+
`\u2022 turnTokens.credits: ${state.turnTokens?.credits?.toFixed(4) ?? "undefined"}\uFF08\u672C\u8F6E\u6700\u540E\u4E00\u6B21\u8C03\u7528\u7684\u6298\u7B97\u503C\uFF0C\u4EC5\u4F9B\u53C2\u8003\uFF0C\u4E0D\u8FDB\u72B6\u6001\u884C\uFF09`,
|
|
4131
|
+
`\u2022 contextWindow : ${state.contextWindow ?? "unknown"}`,
|
|
4132
|
+
'\u2022 \u5224\u5B9A\u53E3\u5F84\uFF1A\u53EA\u6709 billing_unit === "credits" \u7684\u5E73\u53F0\u8BA1\u8D39\u5E27\u624D\u7D2F\u8BA1\uFF1B\u81EA\u6709 API / \u8BA2\u9605\u5236\u6052\u4E3A undefined\u3002',
|
|
4133
|
+
"\u2022 \u82E5\u521A\u8DD1\u5B8C\u5E73\u53F0\u8BA1\u8D39\u8F6E\u4ECD\u65E0 \u26A1\uFF1A\u68C0\u67E5 runAgentTurn \u8FD4\u56DE\u662F\u5426\u5E26 turnCredits\uFF08foldLocalResultForTui \u767D\u540D\u5355\uFF09\u3002"
|
|
4134
|
+
];
|
|
4135
|
+
return lines.map((line) => colorEnabled ? themeText(line, "chrome") : line).join("\n");
|
|
4136
|
+
}
|
|
3854
4137
|
function renderContextPanel(state, colorEnabled = resolveCliColorEnabled()) {
|
|
3855
4138
|
const docs = state.attachedDocs.length ? state.attachedDocs.join(", ") : "none";
|
|
3856
4139
|
const skills = state.attachedSkills.length ? state.attachedSkills.join(", ") : "none";
|
|
@@ -3862,7 +4145,6 @@ function renderContextPanel(state, colorEnabled = resolveCliColorEnabled()) {
|
|
|
3862
4145
|
t("contextFieldSkills"),
|
|
3863
4146
|
t("contextFieldProfile"),
|
|
3864
4147
|
t("contextFieldRuntime"),
|
|
3865
|
-
t("contextFieldTools"),
|
|
3866
4148
|
t("contextFieldServer")
|
|
3867
4149
|
];
|
|
3868
4150
|
let maxW = 0;
|
|
@@ -3892,8 +4174,7 @@ function renderContextPanel(state, colorEnabled = resolveCliColorEnabled()) {
|
|
|
3892
4174
|
field(labels[4], skills),
|
|
3893
4175
|
field(labels[5], state.profileName),
|
|
3894
4176
|
field(labels[6], state.runtimeMode),
|
|
3895
|
-
field(labels[7], state.
|
|
3896
|
-
field(labels[8], state.serverUrl),
|
|
4177
|
+
field(labels[7], state.serverUrl),
|
|
3897
4178
|
"",
|
|
3898
4179
|
themeText(t("contextNext"), "chrome", colorEnabled),
|
|
3899
4180
|
next("/agents ", ` ${t("contextNextAgents")}`),
|
|
@@ -3923,6 +4204,9 @@ function renderKnownAgents(colorEnabled = resolveCliColorEnabled()) {
|
|
|
3923
4204
|
}
|
|
3924
4205
|
|
|
3925
4206
|
// packages/cli/tui/sessionInput.ts
|
|
4207
|
+
import { readdirSync, statSync } from "node:fs";
|
|
4208
|
+
import { homedir as homedir2 } from "node:os";
|
|
4209
|
+
import { resolve as resolve2 } from "node:path";
|
|
3926
4210
|
var PASTE_TOKEN_PREFIX = "\0PASTE\0";
|
|
3927
4211
|
function applyTuiInputKey(buffer, sequence, key = {}, cursorPos, options) {
|
|
3928
4212
|
const seq = sequence ?? "";
|
|
@@ -4027,6 +4311,16 @@ function applyTuiInputKey(buffer, sequence, key = {}, cursorPos, options) {
|
|
|
4027
4311
|
return { buffer, cursorPos: curPos };
|
|
4028
4312
|
}
|
|
4029
4313
|
if (seq === " " || key.name === "tab") {
|
|
4314
|
+
if (options?.cwd) {
|
|
4315
|
+
const pathCandidates = completeSlashCommand(buffer, options.cwd);
|
|
4316
|
+
if (pathCandidates.length === 1) {
|
|
4317
|
+
const [only] = pathCandidates;
|
|
4318
|
+
return { buffer: only, cursorPos: only.length };
|
|
4319
|
+
}
|
|
4320
|
+
if (pathCandidates.length > 1) {
|
|
4321
|
+
return { buffer, cursorPos: curPos };
|
|
4322
|
+
}
|
|
4323
|
+
}
|
|
4030
4324
|
const completed = completeSlashPrefix(buffer) ?? buffer;
|
|
4031
4325
|
return { buffer: completed, cursorPos: completed.length };
|
|
4032
4326
|
}
|
|
@@ -4079,9 +4373,9 @@ function deleteBufferRange(buffer, start, end, pasteStore) {
|
|
|
4079
4373
|
function releasePastesInRange(buffer, start, end, pasteStore) {
|
|
4080
4374
|
if (!pasteStore || start >= end) return;
|
|
4081
4375
|
const slice = buffer.slice(start, end);
|
|
4082
|
-
|
|
4376
|
+
COLLAPSED_PASTE_PLACEHOLDER_RE.lastIndex = 0;
|
|
4083
4377
|
let match;
|
|
4084
|
-
while ((match =
|
|
4378
|
+
while ((match = COLLAPSED_PASTE_PLACEHOLDER_RE.exec(slice)) !== null) {
|
|
4085
4379
|
releaseCollapsedPaste(pasteStore, Number(match[1]));
|
|
4086
4380
|
}
|
|
4087
4381
|
}
|
|
@@ -4135,12 +4429,11 @@ var SLASH_COMMANDS = [
|
|
|
4135
4429
|
"/clear",
|
|
4136
4430
|
"/compact",
|
|
4137
4431
|
"/theme",
|
|
4138
|
-
"/density",
|
|
4139
4432
|
"/context",
|
|
4140
4433
|
"/ctx",
|
|
4434
|
+
"/credits",
|
|
4435
|
+
"/cd",
|
|
4141
4436
|
"/runtime",
|
|
4142
|
-
"/tools",
|
|
4143
|
-
"/thinking",
|
|
4144
4437
|
"/auto",
|
|
4145
4438
|
"/switch",
|
|
4146
4439
|
"/agent",
|
|
@@ -4178,12 +4471,49 @@ function completeSlashPrefix(buffer) {
|
|
|
4178
4471
|
}
|
|
4179
4472
|
return prefix.length > buffer.length ? prefix : null;
|
|
4180
4473
|
}
|
|
4181
|
-
function completeSlashCommand(buffer) {
|
|
4474
|
+
function completeSlashCommand(buffer, cwd) {
|
|
4182
4475
|
if (!buffer.startsWith("/")) return [];
|
|
4183
4476
|
const trimmed = buffer.trim();
|
|
4477
|
+
if (cwd && buffer.startsWith("/cd ")) {
|
|
4478
|
+
const prefix = buffer.slice("/cd ".length);
|
|
4479
|
+
return completeCdPathCandidates(prefix, cwd);
|
|
4480
|
+
}
|
|
4184
4481
|
if (trimmed.includes(" ")) return [];
|
|
4185
4482
|
return SLASH_COMMANDS.filter((cmd) => cmd.startsWith(trimmed) && cmd !== trimmed);
|
|
4186
4483
|
}
|
|
4484
|
+
function completeCdPathCandidates(prefix, cwd) {
|
|
4485
|
+
const lastSlash = prefix.lastIndexOf("/");
|
|
4486
|
+
const head = lastSlash >= 0 ? prefix.slice(0, lastSlash + 1) : "";
|
|
4487
|
+
const baseName = lastSlash >= 0 ? prefix.slice(lastSlash + 1) : prefix;
|
|
4488
|
+
let dirPath;
|
|
4489
|
+
if (head === "~" || head === "~/" || head === "~\\") {
|
|
4490
|
+
dirPath = homedir2();
|
|
4491
|
+
} else if (head.startsWith("~/") || head.startsWith("~\\")) {
|
|
4492
|
+
dirPath = resolve2(homedir2(), head.slice(2));
|
|
4493
|
+
} else {
|
|
4494
|
+
dirPath = head.startsWith("/") ? head : resolve2(cwd, head);
|
|
4495
|
+
}
|
|
4496
|
+
let entries;
|
|
4497
|
+
try {
|
|
4498
|
+
entries = readdirSync(dirPath, { withFileTypes: true });
|
|
4499
|
+
} catch {
|
|
4500
|
+
return [];
|
|
4501
|
+
}
|
|
4502
|
+
const hiddenRequested = baseName.startsWith(".");
|
|
4503
|
+
const names = entries.filter((e) => {
|
|
4504
|
+
if (e.isDirectory()) return true;
|
|
4505
|
+
if (!e.isSymbolicLink()) return false;
|
|
4506
|
+
try {
|
|
4507
|
+
return statSync(resolve2(dirPath, e.name)).isDirectory();
|
|
4508
|
+
} catch {
|
|
4509
|
+
return false;
|
|
4510
|
+
}
|
|
4511
|
+
}).map((e) => e.name).filter((name) => {
|
|
4512
|
+
if (!name.startsWith(baseName)) return false;
|
|
4513
|
+
return hiddenRequested || !name.startsWith(".");
|
|
4514
|
+
}).sort();
|
|
4515
|
+
return names.map((name) => `/cd ${head}${name}/`);
|
|
4516
|
+
}
|
|
4187
4517
|
function isLikelySlashCommand(input) {
|
|
4188
4518
|
const trimmed = input.trim();
|
|
4189
4519
|
if (!trimmed.startsWith("/")) return false;
|
|
@@ -4202,6 +4532,11 @@ function stripImageTokens(input, hints) {
|
|
|
4202
4532
|
return compactWhitespace(out);
|
|
4203
4533
|
}
|
|
4204
4534
|
|
|
4535
|
+
// packages/cli/tui/sessionDispatch.ts
|
|
4536
|
+
import { statSync as statSync2 } from "node:fs";
|
|
4537
|
+
import { homedir as homedir3 } from "node:os";
|
|
4538
|
+
import { resolve as resolve3 } from "node:path";
|
|
4539
|
+
|
|
4205
4540
|
// packages/cli/tui/agentPicker.ts
|
|
4206
4541
|
function toAgentPickerItems(entries) {
|
|
4207
4542
|
return entries.map((entry) => ({
|
|
@@ -4266,7 +4601,6 @@ async function runAgentPicker(args) {
|
|
|
4266
4601
|
name: selected.name,
|
|
4267
4602
|
key: selected.key,
|
|
4268
4603
|
model: selected.model,
|
|
4269
|
-
apiSource: selected.apiSource ?? (selected.kind === "platform" ? "platform" : void 0),
|
|
4270
4604
|
entries
|
|
4271
4605
|
};
|
|
4272
4606
|
}
|
|
@@ -4278,10 +4612,7 @@ function resolveAgentSwitchTarget(rawTarget, catalogEntries = []) {
|
|
|
4278
4612
|
return {
|
|
4279
4613
|
name: aliasEntry?.name ?? asTrimmedLowercaseString(target),
|
|
4280
4614
|
key: resolvedKey,
|
|
4281
|
-
...aliasEntry?.model ? { model: aliasEntry.model } : {}
|
|
4282
|
-
...aliasEntry ? {
|
|
4283
|
-
...aliasEntry.apiSource ? { apiSource: aliasEntry.apiSource } : aliasEntry.kind === "platform" ? { apiSource: "platform" } : {}
|
|
4284
|
-
} : {}
|
|
4615
|
+
...aliasEntry?.model ? { model: aliasEntry.model } : {}
|
|
4285
4616
|
};
|
|
4286
4617
|
}
|
|
4287
4618
|
return findAgentCatalogEntry(catalogEntries, rawTarget);
|
|
@@ -4363,6 +4694,10 @@ function estimateDefaultCliContextTokens(opts = {}) {
|
|
|
4363
4694
|
|
|
4364
4695
|
// packages/cli/tui/sessionDispatch.ts
|
|
4365
4696
|
var DEFAULT_TUI_SERVER_URL = DEFAULT_NOLO_SERVER_URL;
|
|
4697
|
+
function resolveThinkingDisplayMode(env = process.env) {
|
|
4698
|
+
const raw = (env.NOLO_CLI_THINKING ?? "").trim().toLowerCase();
|
|
4699
|
+
return raw === "hide" || raw === "false" || raw === "0" ? "hide" : "show";
|
|
4700
|
+
}
|
|
4366
4701
|
function createInitialTuiState(env = process.env) {
|
|
4367
4702
|
const agentKey = asOptionalTrimmedString(env.NOLO_AGENT) ?? DEFAULT_TUI_AGENT_KEY;
|
|
4368
4703
|
const agentName = asOptionalTrimmedString(env.NOLO_AGENT_NAME) ?? "nolo";
|
|
@@ -4395,16 +4730,14 @@ function createInitialTuiState(env = process.env) {
|
|
|
4395
4730
|
modeLabel: asOptionalTrimmedString(env.NOLO_CLI_STATUS_MODE) ?? runtimeMode,
|
|
4396
4731
|
userLanguage: asOptionalTrimmedString(env.NOLO_LANG),
|
|
4397
4732
|
gitStatus: void 0,
|
|
4398
|
-
|
|
4399
|
-
thinkingDisplay: "show",
|
|
4733
|
+
thinkingDisplay: resolveThinkingDisplayMode(env),
|
|
4400
4734
|
contextWindow: resolveAgentContextWindow({ agentKey, agentName }),
|
|
4401
4735
|
estimatedContextTokens: estimateDefaultCliContextTokens({
|
|
4402
4736
|
cwd,
|
|
4403
4737
|
agentKey,
|
|
4404
4738
|
userLanguage: asOptionalTrimmedString(env.NOLO_LANG),
|
|
4405
4739
|
...resolveAgentModelIdentity({ agentKey, agentName })
|
|
4406
|
-
})
|
|
4407
|
-
apiSource: PLATFORM_AGENTS.some((entry) => entry.key === agentKey) ? "platform" : void 0
|
|
4740
|
+
})
|
|
4408
4741
|
};
|
|
4409
4742
|
}
|
|
4410
4743
|
function applyAgentSwitch(state, target) {
|
|
@@ -4425,8 +4758,7 @@ function applyAgentSwitch(state, target) {
|
|
|
4425
4758
|
agentName: target.name,
|
|
4426
4759
|
model: target.model,
|
|
4427
4760
|
userLanguage: state.userLanguage
|
|
4428
|
-
})
|
|
4429
|
-
apiSource: target.apiSource
|
|
4761
|
+
})
|
|
4430
4762
|
},
|
|
4431
4763
|
output: `Switched to ${target.name}. ${state.dialogId ? `Dialog kept: ${state.dialogId}` : "Dialog kept: new"}`
|
|
4432
4764
|
};
|
|
@@ -4520,31 +4852,58 @@ function handleTuiInput(input, state) {
|
|
|
4520
4852
|
};
|
|
4521
4853
|
}
|
|
4522
4854
|
}
|
|
4523
|
-
case "/
|
|
4524
|
-
|
|
4525
|
-
const sub = parts[0]?.trim();
|
|
4526
|
-
if (!sub) {
|
|
4855
|
+
case "/cd": {
|
|
4856
|
+
if (!argText) {
|
|
4527
4857
|
return {
|
|
4528
4858
|
nextState: state,
|
|
4529
|
-
output: t("
|
|
4859
|
+
output: `${t("cdCurrent", state.cwd)}
|
|
4860
|
+
${t("cdUsage")}`
|
|
4530
4861
|
};
|
|
4531
4862
|
}
|
|
4532
|
-
if (
|
|
4533
|
-
|
|
4534
|
-
|
|
4535
|
-
|
|
4536
|
-
|
|
4537
|
-
|
|
4538
|
-
} else {
|
|
4863
|
+
if (argText === "-") {
|
|
4864
|
+
if (!state.prevCwd) {
|
|
4865
|
+
return { nextState: state, output: t("cdNoPrevious") };
|
|
4866
|
+
}
|
|
4867
|
+
const previous = state.prevCwd;
|
|
4868
|
+
const switchMessage2 = t("cdSwitchedMessage", state.cwd, previous);
|
|
4539
4869
|
return {
|
|
4540
|
-
nextState: state,
|
|
4541
|
-
output: t("
|
|
4870
|
+
nextState: { ...state, cwd: previous, prevCwd: void 0, pendingCwdNotice: switchMessage2 },
|
|
4871
|
+
output: t("cdSwitched", previous),
|
|
4872
|
+
action: { type: "cwd-refresh", switchMessage: switchMessage2 }
|
|
4542
4873
|
};
|
|
4543
4874
|
}
|
|
4875
|
+
const expanded = argText === "~" ? homedir3() : argText.startsWith("~/") || argText.startsWith("~\\") ? resolve3(homedir3(), argText.slice(2)) : argText;
|
|
4876
|
+
const target = resolve3(state.cwd, expanded);
|
|
4877
|
+
try {
|
|
4878
|
+
if (!statSync2(target).isDirectory()) {
|
|
4879
|
+
return { nextState: state, output: t("cdNotDir", target) };
|
|
4880
|
+
}
|
|
4881
|
+
} catch {
|
|
4882
|
+
return { nextState: state, output: t("cdNotFound", target) };
|
|
4883
|
+
}
|
|
4884
|
+
const normalized = target === "/" ? "/" : target.replace(/\/+$/, "");
|
|
4885
|
+
if (normalized === state.cwd) {
|
|
4886
|
+
return { nextState: state, output: t("cdCurrent", state.cwd) };
|
|
4887
|
+
}
|
|
4888
|
+
const switchMessage = t("cdSwitchedMessage", state.cwd, normalized);
|
|
4889
|
+
return {
|
|
4890
|
+
nextState: { ...state, cwd: normalized, prevCwd: state.cwd, pendingCwdNotice: switchMessage },
|
|
4891
|
+
output: t("cdSwitched", normalized),
|
|
4892
|
+
action: { type: "cwd-refresh", switchMessage }
|
|
4893
|
+
};
|
|
4544
4894
|
}
|
|
4895
|
+
case "/density":
|
|
4896
|
+
case "/tools":
|
|
4897
|
+
case "/thinking":
|
|
4898
|
+
return {
|
|
4899
|
+
nextState: state,
|
|
4900
|
+
output: t("displayFixedHint")
|
|
4901
|
+
};
|
|
4545
4902
|
case "/context":
|
|
4546
4903
|
case "/ctx":
|
|
4547
4904
|
return { nextState: state, output: renderContextPanel(state) };
|
|
4905
|
+
case "/credits":
|
|
4906
|
+
return { nextState: state, output: renderCreditsDebug(state) };
|
|
4548
4907
|
case "/runtime": {
|
|
4549
4908
|
if (argText !== "auto" && argText !== "local" && argText !== "server") {
|
|
4550
4909
|
return {
|
|
@@ -4557,45 +4916,6 @@ function handleTuiInput(input, state) {
|
|
|
4557
4916
|
output: t("runtimeSet", argText)
|
|
4558
4917
|
};
|
|
4559
4918
|
}
|
|
4560
|
-
case "/tools": {
|
|
4561
|
-
if (!argText) {
|
|
4562
|
-
return {
|
|
4563
|
-
nextState: state,
|
|
4564
|
-
output: t("toolsCurrent", state.toolDisplay)
|
|
4565
|
-
};
|
|
4566
|
-
}
|
|
4567
|
-
const normalizedArg = asTrimmedLowercaseString(argText);
|
|
4568
|
-
if (!["normal", "pro", "hide", "compact", "verbose", "on", "off"].includes(normalizedArg)) {
|
|
4569
|
-
return {
|
|
4570
|
-
nextState: state,
|
|
4571
|
-
output: t("toolsUsage")
|
|
4572
|
-
};
|
|
4573
|
-
}
|
|
4574
|
-
const nextMode = normalizeToolDisplayMode(normalizedArg, state.toolDisplay);
|
|
4575
|
-
return {
|
|
4576
|
-
nextState: { ...state, toolDisplay: nextMode },
|
|
4577
|
-
output: t("toolsSet", nextMode)
|
|
4578
|
-
};
|
|
4579
|
-
}
|
|
4580
|
-
case "/thinking": {
|
|
4581
|
-
const normalizedArg = asTrimmedLowercaseString(argText);
|
|
4582
|
-
if (!normalizedArg) {
|
|
4583
|
-
return {
|
|
4584
|
-
nextState: state,
|
|
4585
|
-
output: t("thinkingCurrent", state.thinkingDisplay)
|
|
4586
|
-
};
|
|
4587
|
-
}
|
|
4588
|
-
if (normalizedArg !== "show" && normalizedArg !== "hide") {
|
|
4589
|
-
return {
|
|
4590
|
-
nextState: state,
|
|
4591
|
-
output: t("thinkingUsage")
|
|
4592
|
-
};
|
|
4593
|
-
}
|
|
4594
|
-
return {
|
|
4595
|
-
nextState: { ...state, thinkingDisplay: normalizedArg },
|
|
4596
|
-
output: t("thinkingSet", normalizedArg)
|
|
4597
|
-
};
|
|
4598
|
-
}
|
|
4599
4919
|
case "/auto": {
|
|
4600
4920
|
const normalizedArg = asTrimmedLowercaseString(argText);
|
|
4601
4921
|
if (!normalizedArg) {
|
|
@@ -5004,7 +5324,7 @@ async function probeTerminalBackground(args = {}) {
|
|
|
5004
5324
|
if (typeof stdin.setRawMode !== "function") return null;
|
|
5005
5325
|
if (!isTerminal(stdin)) return null;
|
|
5006
5326
|
const wasRaw = Boolean(stdin.isRaw);
|
|
5007
|
-
return await new Promise((
|
|
5327
|
+
return await new Promise((resolve4) => {
|
|
5008
5328
|
let buffer = "";
|
|
5009
5329
|
let settled = false;
|
|
5010
5330
|
let timer;
|
|
@@ -5017,7 +5337,7 @@ async function probeTerminalBackground(args = {}) {
|
|
|
5017
5337
|
if (!wasRaw) stdin.setRawMode?.(false);
|
|
5018
5338
|
} catch {
|
|
5019
5339
|
}
|
|
5020
|
-
|
|
5340
|
+
resolve4(result);
|
|
5021
5341
|
};
|
|
5022
5342
|
const onData = (chunk) => {
|
|
5023
5343
|
buffer += chunk.toString("latin1");
|
|
@@ -5080,9 +5400,106 @@ function detectSystemBrightness(env = process.env, options = {}) {
|
|
|
5080
5400
|
return null;
|
|
5081
5401
|
}
|
|
5082
5402
|
|
|
5083
|
-
// packages/cli/tui/
|
|
5084
|
-
|
|
5403
|
+
// packages/cli/tui/turnInjectionInbox.ts
|
|
5404
|
+
function appendStreamSafeNotice(history, text, deps) {
|
|
5405
|
+
const wasStreamingAssistant = history.currentRole === "assistant";
|
|
5406
|
+
if (wasStreamingAssistant && !history.currentContent) {
|
|
5407
|
+
history.currentRole = null;
|
|
5408
|
+
history.currentBlocks = [];
|
|
5409
|
+
}
|
|
5410
|
+
deps.appendLocalTurn(history, "", text);
|
|
5411
|
+
if (wasStreamingAssistant) deps.startTurn(history, "assistant");
|
|
5412
|
+
}
|
|
5413
|
+
function createTurnInjectionInbox() {
|
|
5414
|
+
let pending = [];
|
|
5415
|
+
let closed = false;
|
|
5416
|
+
return {
|
|
5417
|
+
push(entry) {
|
|
5418
|
+
if (closed) return false;
|
|
5419
|
+
if (!entry || typeof entry.text !== "string" || !entry.text.trim()) return false;
|
|
5420
|
+
pending.push(entry);
|
|
5421
|
+
return true;
|
|
5422
|
+
},
|
|
5423
|
+
drain() {
|
|
5424
|
+
if (pending.length === 0) return [];
|
|
5425
|
+
const taken = pending;
|
|
5426
|
+
pending = [];
|
|
5427
|
+
return taken.map((entry) => entry.text);
|
|
5428
|
+
},
|
|
5429
|
+
close() {
|
|
5430
|
+
closed = true;
|
|
5431
|
+
const remaining = pending;
|
|
5432
|
+
pending = [];
|
|
5433
|
+
return remaining;
|
|
5434
|
+
},
|
|
5435
|
+
get size() {
|
|
5436
|
+
return pending.length;
|
|
5437
|
+
},
|
|
5438
|
+
get closed() {
|
|
5439
|
+
return closed;
|
|
5440
|
+
}
|
|
5441
|
+
};
|
|
5442
|
+
}
|
|
5443
|
+
|
|
5444
|
+
// packages/cli/client/turnBillingAudit.ts
|
|
5445
|
+
import { appendFileSync, mkdirSync as mkdirSync2 } from "node:fs";
|
|
5446
|
+
import { homedir as homedir4 } from "node:os";
|
|
5085
5447
|
import { join as join3 } from "node:path";
|
|
5448
|
+
function projectUsageForAudit(usage) {
|
|
5449
|
+
if (!usage || typeof usage !== "object") return null;
|
|
5450
|
+
const normalized = normalizeUsage(usage);
|
|
5451
|
+
const projected = {
|
|
5452
|
+
input_tokens: normalized.input_tokens,
|
|
5453
|
+
output_tokens: normalized.output_tokens,
|
|
5454
|
+
cache_read_input_tokens: normalized.cache_read_input_tokens,
|
|
5455
|
+
cache_creation_input_tokens: normalized.cache_creation_input_tokens
|
|
5456
|
+
};
|
|
5457
|
+
if (typeof usage.cost === "number" && Number.isFinite(usage.cost)) {
|
|
5458
|
+
projected.cost = usage.cost;
|
|
5459
|
+
}
|
|
5460
|
+
if (typeof usage.billing_unit === "string" && usage.billing_unit.trim()) {
|
|
5461
|
+
projected.billing_unit = usage.billing_unit;
|
|
5462
|
+
}
|
|
5463
|
+
if (typeof usage.provider_call_id === "string" && usage.provider_call_id.trim()) {
|
|
5464
|
+
projected.provider_call_id = usage.provider_call_id.trim();
|
|
5465
|
+
}
|
|
5466
|
+
return projected;
|
|
5467
|
+
}
|
|
5468
|
+
function resolveTurnBillingAuditLogPath(env = process.env) {
|
|
5469
|
+
const noloHome = env.NOLO_HOME?.trim() || join3(homedir4(), ".nolo");
|
|
5470
|
+
return join3(noloHome, "logs", "turn-billing.jsonl");
|
|
5471
|
+
}
|
|
5472
|
+
function appendTurnBillingAudit(input) {
|
|
5473
|
+
try {
|
|
5474
|
+
if (process.env.NOLO_TURN_BILLING_AUDIT === "0") return;
|
|
5475
|
+
const calls = (input.usageRecords ?? []).map((record) => {
|
|
5476
|
+
const usage = projectUsageForAudit(record?.usage);
|
|
5477
|
+
if (!usage) return null;
|
|
5478
|
+
return {
|
|
5479
|
+
...record?.callId ? { callId: record.callId } : {},
|
|
5480
|
+
...record?.model ? { model: record.model } : {},
|
|
5481
|
+
...record?.provider ? { provider: record.provider } : {},
|
|
5482
|
+
...usage
|
|
5483
|
+
};
|
|
5484
|
+
}).filter((call) => call !== null);
|
|
5485
|
+
if (calls.length === 0 && (input.turnCredits === void 0 || input.turnCredits === null)) {
|
|
5486
|
+
return;
|
|
5487
|
+
}
|
|
5488
|
+
const row = {
|
|
5489
|
+
ts: Date.now(),
|
|
5490
|
+
...input.dialogId ? { dialogId: input.dialogId } : {},
|
|
5491
|
+
...input.agentKey ? { agentKey: input.agentKey } : {},
|
|
5492
|
+
...input.turnCredits !== void 0 && input.turnCredits !== null ? { turnCredits: input.turnCredits } : {},
|
|
5493
|
+
...input.aborted ? { aborted: true } : {},
|
|
5494
|
+
calls
|
|
5495
|
+
};
|
|
5496
|
+
const logPath = resolveTurnBillingAuditLogPath();
|
|
5497
|
+
mkdirSync2(join3(logPath, ".."), { recursive: true });
|
|
5498
|
+
appendFileSync(logPath, `${JSON.stringify(row)}
|
|
5499
|
+
`);
|
|
5500
|
+
} catch {
|
|
5501
|
+
}
|
|
5502
|
+
}
|
|
5086
5503
|
|
|
5087
5504
|
// packages/ai/tools/askChoiceState.ts
|
|
5088
5505
|
function normalizeAskChoiceArgs(args) {
|
|
@@ -5861,24 +6278,6 @@ function emitTerminalBell(output) {
|
|
|
5861
6278
|
}
|
|
5862
6279
|
|
|
5863
6280
|
// packages/cli/tui/tuiTurnRunner.ts
|
|
5864
|
-
var AGENTS_MD_MAX_BYTES = 8192;
|
|
5865
|
-
function readAgentsMdLayer(cwd) {
|
|
5866
|
-
for (const name of ["AGENTS.md", "CLAUDE.md"]) {
|
|
5867
|
-
const filePath = join3(cwd, name);
|
|
5868
|
-
if (existsSync3(filePath)) {
|
|
5869
|
-
try {
|
|
5870
|
-
let content = readFileSync3(filePath, "utf8").trim();
|
|
5871
|
-
if (!content) continue;
|
|
5872
|
-
if (Buffer.byteLength(content, "utf8") > AGENTS_MD_MAX_BYTES) {
|
|
5873
|
-
content = Buffer.from(content, "utf8").subarray(0, AGENTS_MD_MAX_BYTES).toString("utf8") + "\n\n<!-- AGENTS.md truncated -->";
|
|
5874
|
-
}
|
|
5875
|
-
return buildAgentsMdLayer(content, name);
|
|
5876
|
-
} catch {
|
|
5877
|
-
}
|
|
5878
|
-
}
|
|
5879
|
-
}
|
|
5880
|
-
return null;
|
|
5881
|
-
}
|
|
5882
6281
|
async function runAgentChat(scriptDir, state, message, env, output, agentRunner = runAgentTurn, options = {}) {
|
|
5883
6282
|
const effectiveAgentKey = state.agentKey;
|
|
5884
6283
|
const effectiveAgentName = state.agentName;
|
|
@@ -5923,12 +6322,16 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
|
|
|
5923
6322
|
}
|
|
5924
6323
|
}
|
|
5925
6324
|
const layers = [
|
|
5926
|
-
|
|
6325
|
+
readAgentsMdLayerFromDisk(state.cwd),
|
|
5927
6326
|
// Skill discovery: scan conventional skill dirs for SKILL.md and inject an
|
|
5928
6327
|
// index layer so the model knows what skills exist and can readFile them
|
|
5929
6328
|
// on-demand. Mirrors agentRunCommand.ts and desktopAgentRuntimeTurnService.
|
|
5930
6329
|
buildSkillDiscoveryContextLayer(state.cwd)
|
|
5931
6330
|
];
|
|
6331
|
+
const responseGuidelinesBlock = {
|
|
6332
|
+
content: buildResponseGuidelines(true),
|
|
6333
|
+
cacheScope: "session"
|
|
6334
|
+
};
|
|
5932
6335
|
let memoryPromptBlock = state.cachedMemoryOverlay;
|
|
5933
6336
|
if (memoryPromptBlock === void 0) {
|
|
5934
6337
|
memoryPromptBlock = await resolveCliMemory({
|
|
@@ -5941,15 +6344,19 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
|
|
|
5941
6344
|
}
|
|
5942
6345
|
const memoryOverlayLayer = buildMemoryOverlayLayer({ promptBlock: memoryPromptBlock });
|
|
5943
6346
|
const memoryUseGuidanceLayer = buildMemoryUseGuidanceLayer({ promptBlock: memoryPromptBlock });
|
|
6347
|
+
const cwdNoticeBlocks = state.pendingCwdNotice ? [{ content: state.pendingCwdNotice, cacheScope: "turn" }] : [];
|
|
5944
6348
|
const contextBlockScopes = partitionScopedBlocks([
|
|
5945
6349
|
...renderTurnContextBlocksWithScope(layers),
|
|
6350
|
+
responseGuidelinesBlock,
|
|
5946
6351
|
...renderTurnContextBlocksWithScope([memoryUseGuidanceLayer]),
|
|
5947
6352
|
// Attached skill bodies are already self-contained sections built by
|
|
5948
6353
|
// buildSkillContextBlocks; they stay turn-scope because the user can
|
|
5949
6354
|
// attach/detach skills between turns.
|
|
5950
6355
|
...(skillContextBlocks ?? []).map((content) => content.trim()).filter((content) => content.length > 0).map((content) => ({ content, cacheScope: "turn" })),
|
|
5951
|
-
...renderTurnContextBlocksWithScope([memoryOverlayLayer])
|
|
6356
|
+
...renderTurnContextBlocksWithScope([memoryOverlayLayer]),
|
|
6357
|
+
...cwdNoticeBlocks
|
|
5952
6358
|
]);
|
|
6359
|
+
const spawnedRunsMissingParent = [];
|
|
5953
6360
|
const result = await agentRunner({
|
|
5954
6361
|
agentName: effectiveAgentName,
|
|
5955
6362
|
agentKey: effectiveAgentKey,
|
|
@@ -5967,10 +6374,12 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
|
|
|
5967
6374
|
scriptDir,
|
|
5968
6375
|
env: {
|
|
5969
6376
|
...env,
|
|
5970
|
-
NOLO_LANG: state.userLanguage
|
|
5971
|
-
|
|
6377
|
+
NOLO_LANG: state.userLanguage
|
|
6378
|
+
},
|
|
6379
|
+
output: {
|
|
6380
|
+
...output,
|
|
6381
|
+
tuiTrees: output.tuiTrees === true && typeof output.writeToolBlock === "function"
|
|
5972
6382
|
},
|
|
5973
|
-
output,
|
|
5974
6383
|
showThinking: state.thinkingDisplay === "show",
|
|
5975
6384
|
...options.imageUrls && options.imageUrls.length > 0 ? { imageUrls: options.imageUrls } : {},
|
|
5976
6385
|
...options.actionGateHandler ? { actionGateHandler: options.actionGateHandler } : {},
|
|
@@ -5994,15 +6403,28 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
|
|
|
5994
6403
|
// run-completion watcher can attribute terminal-state wakes to this
|
|
5995
6404
|
// conversation.
|
|
5996
6405
|
...state.dialogId ? { parentDialogId: state.dialogId } : {},
|
|
6406
|
+
// 首轮无 dialogId 时的兜底:收集失归属的 spawn,turn 结束回填。
|
|
6407
|
+
onBackgroundRunSpawned: (runId) => {
|
|
6408
|
+
spawnedRunsMissingParent.push(runId);
|
|
6409
|
+
},
|
|
5997
6410
|
...options.activityReporter ? { activityReporter: options.activityReporter } : {}
|
|
5998
6411
|
}),
|
|
5999
6412
|
...options.activityReporter ? { activityReporter: options.activityReporter } : {},
|
|
6000
6413
|
// 转发 dock 订阅:runAgentTurn 的输出层靠它判断「有面板」从而抑制
|
|
6001
6414
|
// transcript 的进展卡片;dock 本身也靠它接收模型轮询带回来的快照。
|
|
6002
6415
|
...options.onAgentRunStatus ? { onAgentRunStatus: options.onAgentRunStatus } : {},
|
|
6416
|
+
...options.drainInjections ? { drainInjections: options.drainInjections } : {},
|
|
6003
6417
|
...skillAllowedTools !== void 0 ? { allowedToolNames: skillAllowedTools } : {},
|
|
6004
6418
|
...contextBlockScopes.length > 0 ? { contextBlockScopes } : {}
|
|
6005
6419
|
});
|
|
6420
|
+
if (result.dialogId && spawnedRunsMissingParent.length > 0) {
|
|
6421
|
+
for (const runId of spawnedRunsMissingParent) {
|
|
6422
|
+
try {
|
|
6423
|
+
backfillRunRecordParentDialog(runId, result.dialogId, { env });
|
|
6424
|
+
} catch {
|
|
6425
|
+
}
|
|
6426
|
+
}
|
|
6427
|
+
}
|
|
6006
6428
|
return {
|
|
6007
6429
|
...result,
|
|
6008
6430
|
contextWindow: resolveAgentContextWindow({
|
|
@@ -6012,13 +6434,47 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
|
|
|
6012
6434
|
cachedMemoryOverlay: memoryPromptBlock
|
|
6013
6435
|
};
|
|
6014
6436
|
}
|
|
6015
|
-
|
|
6437
|
+
var ESC = "\x1B";
|
|
6438
|
+
var CTRL_C = "";
|
|
6439
|
+
function isApprovedConfirmationInput(answer) {
|
|
6440
|
+
const normalized = answer.trim().toLowerCase();
|
|
6441
|
+
return normalized === "" || normalized === "y" || normalized === "yes" || normalized === "\u786E\u8BA4";
|
|
6442
|
+
}
|
|
6443
|
+
function buildGateConfirmedResult(gate) {
|
|
6444
|
+
return {
|
|
6445
|
+
content: `action gate completed: ${gate.title}`,
|
|
6446
|
+
metadata: { actionGateResult: { gateId: gate.id, status: "completed", output: "confirmed" } }
|
|
6447
|
+
};
|
|
6448
|
+
}
|
|
6449
|
+
function buildGateCancelledResult(gate, reason) {
|
|
6450
|
+
return {
|
|
6451
|
+
content: `action gate cancelled: ${gate.title}`,
|
|
6452
|
+
metadata: {
|
|
6453
|
+
exitCode: 130,
|
|
6454
|
+
actionGateResult: { gateId: gate.id, status: "cancelled", output: reason }
|
|
6455
|
+
}
|
|
6456
|
+
};
|
|
6457
|
+
}
|
|
6458
|
+
function buildGateFailedResult(gate, message) {
|
|
6459
|
+
return {
|
|
6460
|
+
content: `action gate failed: ${gate.title}`,
|
|
6461
|
+
metadata: {
|
|
6462
|
+
exitCode: 1,
|
|
6463
|
+
actionGateResult: { gateId: gate.id, status: "failed", output: message }
|
|
6464
|
+
}
|
|
6465
|
+
};
|
|
6466
|
+
}
|
|
6467
|
+
function deriveGateMeta(gate) {
|
|
6016
6468
|
const commandPayload = gate.kind === "handoff" ? readCommandActionGatePayload(gate.payload) : null;
|
|
6017
6469
|
const isConfirmation = gate.kind === "confirm";
|
|
6018
6470
|
const displayCommand = commandPayload?.displayCommand ?? commandPayload?.command.join(" ") ?? gate.title;
|
|
6019
6471
|
const isInteractiveHandoff = gate.kind === "handoff" && gate.title === "This command requires an interactive terminal.";
|
|
6020
6472
|
const title = isInteractiveHandoff ? t("actionGateInteractiveTitle") : gate.title;
|
|
6021
6473
|
const body = isInteractiveHandoff ? t("actionGateInteractiveBody") : gate.body;
|
|
6474
|
+
return { commandPayload, isConfirmation, displayCommand, isInteractiveHandoff, title, body };
|
|
6475
|
+
}
|
|
6476
|
+
function writeGatePrompt(output, meta) {
|
|
6477
|
+
const { title, body, displayCommand, isConfirmation } = meta;
|
|
6022
6478
|
output.write(`
|
|
6023
6479
|
[nolo] ${t("actionGateNeeded")}
|
|
6024
6480
|
`);
|
|
@@ -6028,44 +6484,32 @@ function waitForActionGate(rl, input, output, gate, spawnRunner) {
|
|
|
6028
6484
|
`);
|
|
6029
6485
|
output.write(` ${displayCommand}
|
|
6030
6486
|
`);
|
|
6031
|
-
output.write(`[nolo] ${t("actionGateEnterHint")}
|
|
6487
|
+
output.write(`[nolo] ${isConfirmation ? t("actionGateConfirmHint") : t("actionGateEnterHint")}
|
|
6032
6488
|
`);
|
|
6033
|
-
|
|
6489
|
+
}
|
|
6490
|
+
function waitForActionGate(rl, input, output, gate, spawnRunner) {
|
|
6491
|
+
const meta = deriveGateMeta(gate);
|
|
6492
|
+
const { commandPayload, isConfirmation, displayCommand } = meta;
|
|
6493
|
+
writeGatePrompt(output, meta);
|
|
6494
|
+
return new Promise((resolve4) => {
|
|
6034
6495
|
let settled = false;
|
|
6035
6496
|
const finish = (result) => {
|
|
6036
6497
|
if (settled) return;
|
|
6037
6498
|
settled = true;
|
|
6038
6499
|
rl.off("close", onClose);
|
|
6039
6500
|
rl.off("SIGINT", onSigint);
|
|
6040
|
-
|
|
6501
|
+
resolve4(result);
|
|
6041
6502
|
};
|
|
6042
|
-
const cancelResult = (reason) => (
|
|
6043
|
-
|
|
6044
|
-
metadata: {
|
|
6045
|
-
exitCode: 130,
|
|
6046
|
-
actionGateResult: { gateId: gate.id, status: "cancelled", output: reason }
|
|
6047
|
-
}
|
|
6048
|
-
});
|
|
6049
|
-
const failResult = (message) => ({
|
|
6050
|
-
content: `action gate failed: ${gate.title}`,
|
|
6051
|
-
metadata: {
|
|
6052
|
-
exitCode: 1,
|
|
6053
|
-
actionGateResult: { gateId: gate.id, status: "failed", output: message }
|
|
6054
|
-
}
|
|
6055
|
-
});
|
|
6503
|
+
const cancelResult = (reason) => buildGateCancelledResult(gate, reason);
|
|
6504
|
+
const failResult = (message) => buildGateFailedResult(gate, message);
|
|
6056
6505
|
const onClose = () => finish(cancelResult("readline closed"));
|
|
6057
6506
|
const onSigint = () => finish(cancelResult("interrupted"));
|
|
6058
6507
|
rl.once("close", onClose);
|
|
6059
6508
|
rl.once("SIGINT", onSigint);
|
|
6060
|
-
rl.question("", async () => {
|
|
6509
|
+
rl.question("", async (answer) => {
|
|
6061
6510
|
if (settled) return;
|
|
6062
6511
|
if (isConfirmation) {
|
|
6063
|
-
finish(
|
|
6064
|
-
content: `action gate completed: ${gate.title}`,
|
|
6065
|
-
metadata: {
|
|
6066
|
-
actionGateResult: { gateId: gate.id, status: "completed", output: "confirmed" }
|
|
6067
|
-
}
|
|
6068
|
-
});
|
|
6512
|
+
finish(isApprovedConfirmationInput(answer) ? buildGateConfirmedResult(gate) : cancelResult("confirmation declined"));
|
|
6069
6513
|
return;
|
|
6070
6514
|
}
|
|
6071
6515
|
if (!commandPayload) {
|
|
@@ -6113,48 +6557,23 @@ function isInteractiveInput(input) {
|
|
|
6113
6557
|
return Boolean(candidate.isTTY) && typeof candidate.setRawMode === "function";
|
|
6114
6558
|
}
|
|
6115
6559
|
function waitForRawActionGate(input, output, gate, spawnRunner, hooks) {
|
|
6116
|
-
const
|
|
6117
|
-
const isConfirmation
|
|
6118
|
-
|
|
6119
|
-
|
|
6120
|
-
const title = isInteractiveHandoff ? t("actionGateInteractiveTitle") : gate.title;
|
|
6121
|
-
const body = isInteractiveHandoff ? t("actionGateInteractiveBody") : gate.body;
|
|
6122
|
-
output.write(`
|
|
6123
|
-
[nolo] ${t("actionGateNeeded")}
|
|
6124
|
-
`);
|
|
6125
|
-
output.write(`[nolo] ${title}
|
|
6126
|
-
`);
|
|
6127
|
-
if (body) output.write(`[nolo] ${body}
|
|
6128
|
-
`);
|
|
6129
|
-
output.write(` ${displayCommand}
|
|
6130
|
-
`);
|
|
6131
|
-
output.write(`[nolo] ${t("actionGateEnterHint")}
|
|
6132
|
-
`);
|
|
6133
|
-
return new Promise((resolve2) => {
|
|
6560
|
+
const meta = deriveGateMeta(gate);
|
|
6561
|
+
const { commandPayload, isConfirmation, displayCommand } = meta;
|
|
6562
|
+
writeGatePrompt(output, meta);
|
|
6563
|
+
return new Promise((resolve4) => {
|
|
6134
6564
|
const rawInput = input;
|
|
6135
6565
|
let settled = false;
|
|
6136
6566
|
let commandRunning = false;
|
|
6567
|
+
let confirmationLine = "";
|
|
6137
6568
|
const finish = (result) => {
|
|
6138
6569
|
if (settled) return;
|
|
6139
6570
|
settled = true;
|
|
6140
6571
|
hooks?.registerToken?.(null);
|
|
6141
6572
|
input.off("data", onData);
|
|
6142
|
-
|
|
6573
|
+
resolve4(result);
|
|
6143
6574
|
};
|
|
6144
|
-
const cancel = (reason) => finish(
|
|
6145
|
-
|
|
6146
|
-
metadata: {
|
|
6147
|
-
exitCode: 130,
|
|
6148
|
-
actionGateResult: { gateId: gate.id, status: "cancelled", output: reason }
|
|
6149
|
-
}
|
|
6150
|
-
});
|
|
6151
|
-
const fail = (message) => finish({
|
|
6152
|
-
content: `action gate failed: ${gate.title}`,
|
|
6153
|
-
metadata: {
|
|
6154
|
-
exitCode: 1,
|
|
6155
|
-
actionGateResult: { gateId: gate.id, status: "failed", output: message }
|
|
6156
|
-
}
|
|
6157
|
-
});
|
|
6575
|
+
const cancel = (reason) => finish(buildGateCancelledResult(gate, reason));
|
|
6576
|
+
const fail = (message) => finish(buildGateFailedResult(gate, message));
|
|
6158
6577
|
const runCommand = async () => {
|
|
6159
6578
|
if (settled || commandRunning) return;
|
|
6160
6579
|
commandRunning = true;
|
|
@@ -6197,21 +6616,26 @@ function waitForRawActionGate(input, output, gate, spawnRunner, hooks) {
|
|
|
6197
6616
|
};
|
|
6198
6617
|
const handleToken = (text) => {
|
|
6199
6618
|
if (settled || commandRunning) return;
|
|
6200
|
-
if (text.includes(
|
|
6619
|
+
if (text.includes(CTRL_C) || isConfirmation && text === ESC) {
|
|
6201
6620
|
cancel("interrupted");
|
|
6202
6621
|
return;
|
|
6203
6622
|
}
|
|
6204
|
-
if (
|
|
6205
|
-
|
|
6206
|
-
|
|
6207
|
-
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6623
|
+
if (isConfirmation) {
|
|
6624
|
+
const newline = text.search(/[\r\n]/);
|
|
6625
|
+
confirmationLine += newline >= 0 ? text.slice(0, newline) : text;
|
|
6626
|
+
if (newline >= 0) {
|
|
6627
|
+
const normalized = confirmationLine.trim().toLowerCase();
|
|
6628
|
+
confirmationLine = "";
|
|
6629
|
+
if (!isApprovedConfirmationInput(normalized)) {
|
|
6630
|
+
cancel("confirmation declined");
|
|
6631
|
+
return;
|
|
6632
|
+
}
|
|
6633
|
+
finish(buildGateConfirmedResult(gate));
|
|
6214
6634
|
}
|
|
6635
|
+
return;
|
|
6636
|
+
}
|
|
6637
|
+
if (text.includes("\r") || text.includes("\n")) {
|
|
6638
|
+
void runCommand();
|
|
6215
6639
|
}
|
|
6216
6640
|
};
|
|
6217
6641
|
const onData = (chunk) => {
|
|
@@ -6225,6 +6649,40 @@ function waitForRawActionGate(input, output, gate, spawnRunner, hooks) {
|
|
|
6225
6649
|
}
|
|
6226
6650
|
});
|
|
6227
6651
|
}
|
|
6652
|
+
function asConfirmPermissionRequest(gate) {
|
|
6653
|
+
return gate;
|
|
6654
|
+
}
|
|
6655
|
+
function isAutoConfirmableFileWriteGate(gate) {
|
|
6656
|
+
return gate.kind === "confirm" && asConfirmPermissionRequest(gate).action === "file_write";
|
|
6657
|
+
}
|
|
6658
|
+
async function resolveActionGate(gate, deps) {
|
|
6659
|
+
if (gate.kind === "confirm") {
|
|
6660
|
+
const approved = await deps.dialogHost.run(
|
|
6661
|
+
(anchor) => runConfirmDialog({
|
|
6662
|
+
request: asConfirmPermissionRequest(gate),
|
|
6663
|
+
input: deps.input,
|
|
6664
|
+
output: deps.output,
|
|
6665
|
+
...anchor
|
|
6666
|
+
})
|
|
6667
|
+
);
|
|
6668
|
+
return approved ? buildGateConfirmedResult(gate) : buildGateCancelledResult(gate, "confirmation declined");
|
|
6669
|
+
}
|
|
6670
|
+
return deps.dialogHost.withKeyboard(async () => {
|
|
6671
|
+
let subprocessRan = false;
|
|
6672
|
+
deps.pauseComposer();
|
|
6673
|
+
try {
|
|
6674
|
+
return await waitForRawActionGate(deps.input, deps.output, gate, deps.spawnRunner, {
|
|
6675
|
+
beforeSubprocess: () => {
|
|
6676
|
+
subprocessRan = true;
|
|
6677
|
+
},
|
|
6678
|
+
afterSubprocess: deps.resumeComposerFromSubprocess,
|
|
6679
|
+
registerToken: deps.registerToken
|
|
6680
|
+
});
|
|
6681
|
+
} finally {
|
|
6682
|
+
if (!subprocessRan) deps.resumeComposerFromDialog();
|
|
6683
|
+
}
|
|
6684
|
+
});
|
|
6685
|
+
}
|
|
6228
6686
|
async function runOneAgentTurn(ctx, inputMsg, imageUrls, actionGateHandler, confirmDestructiveAction) {
|
|
6229
6687
|
const scheduleTitlePatchSync = (runResult) => {
|
|
6230
6688
|
if (!runResult.titlePatchPromise || !runResult.dialogId) return;
|
|
@@ -6296,7 +6754,6 @@ async function runOneAgentTurn(ctx, inputMsg, imageUrls, actionGateHandler, conf
|
|
|
6296
6754
|
ctx.scheduleRender();
|
|
6297
6755
|
}) : ctx.output;
|
|
6298
6756
|
const requestUserChoice = isInteractiveInput(ctx.input) && ctx.dialogHost ? async (choiceReq) => {
|
|
6299
|
-
ctx.modalOwnsKeyboard = true;
|
|
6300
6757
|
try {
|
|
6301
6758
|
return await ctx.dialogHost.run(
|
|
6302
6759
|
(anchor) => runAskChoiceDialog({
|
|
@@ -6308,12 +6765,11 @@ async function runOneAgentTurn(ctx, inputMsg, imageUrls, actionGateHandler, conf
|
|
|
6308
6765
|
);
|
|
6309
6766
|
} catch {
|
|
6310
6767
|
return { kind: "cancelled" };
|
|
6311
|
-
} finally {
|
|
6312
|
-
ctx.composerDecoderDrain?.();
|
|
6313
|
-
ctx.modalOwnsKeyboard = false;
|
|
6314
6768
|
}
|
|
6315
6769
|
} : void 0;
|
|
6316
6770
|
ctx.runRegistryPoller.beginHold();
|
|
6771
|
+
const injectionInbox = createTurnInjectionInbox();
|
|
6772
|
+
ctx.turnInjectionInbox = injectionInbox;
|
|
6317
6773
|
try {
|
|
6318
6774
|
ctx.activeTurnAbort = new AbortController();
|
|
6319
6775
|
ctx.activeTurnEpoch = myEpoch;
|
|
@@ -6330,6 +6786,7 @@ async function runOneAgentTurn(ctx, inputMsg, imageUrls, actionGateHandler, conf
|
|
|
6330
6786
|
...confirmDestructiveAction ? { confirmDestructiveAction } : {},
|
|
6331
6787
|
...requestUserChoice ? { requestUserChoice } : {},
|
|
6332
6788
|
abortSignal: ctx.activeTurnAbort.signal,
|
|
6789
|
+
drainInjections: () => injectionInbox.drain(),
|
|
6333
6790
|
pastedTextStore: ctx.pasteStore,
|
|
6334
6791
|
activityReporter: ctx.activityReporter,
|
|
6335
6792
|
onAgentRunStatus: (snapshot) => {
|
|
@@ -6355,12 +6812,18 @@ async function runOneAgentTurn(ctx, inputMsg, imageUrls, actionGateHandler, conf
|
|
|
6355
6812
|
...runResult.title ? { dialogTitle: runResult.title } : {}
|
|
6356
6813
|
} : {},
|
|
6357
6814
|
...runResult.turnTokens ? { turnTokens: runResult.turnTokens } : {},
|
|
6358
|
-
...runResult.cachedMemoryOverlay !== void 0 ? { cachedMemoryOverlay: runResult.cachedMemoryOverlay } : {}
|
|
6815
|
+
...runResult.cachedMemoryOverlay !== void 0 ? { cachedMemoryOverlay: runResult.cachedMemoryOverlay } : {},
|
|
6816
|
+
// 本轮已发起 agent 调用(消息已注入),消费切换通知。
|
|
6817
|
+
pendingCwdNotice: void 0
|
|
6359
6818
|
};
|
|
6360
|
-
if (runResult.dialogId && nextDialogKey) {
|
|
6361
|
-
ctx.refreshDialogTotalCredits(runResult.dialogId, nextDialogKey);
|
|
6362
|
-
}
|
|
6363
6819
|
}
|
|
6820
|
+
ctx.accumulateSessionCredits(runResult.turnCredits);
|
|
6821
|
+
appendTurnBillingAudit({
|
|
6822
|
+
dialogId: runResult.dialogId ?? null,
|
|
6823
|
+
turnCredits: runResult.turnCredits ?? null,
|
|
6824
|
+
usageRecords: runResult.usageRecords,
|
|
6825
|
+
aborted: true
|
|
6826
|
+
});
|
|
6364
6827
|
scheduleTitlePatchSync(runResult);
|
|
6365
6828
|
return { ok: false, aborted: true };
|
|
6366
6829
|
}
|
|
@@ -6403,12 +6866,22 @@ async function runOneAgentTurn(ctx, inputMsg, imageUrls, actionGateHandler, conf
|
|
|
6403
6866
|
// estimatedContextTokens:下一轮若 provider 不返回 usage,context
|
|
6404
6867
|
// chip 仍显示真实累计占用而不是回退到启动时的静态估算。
|
|
6405
6868
|
...runResult.turnTokens && runResult.turnTokens.input > 0 ? { estimatedContextTokens: runResult.turnTokens.input } : {},
|
|
6406
|
-
...runResult.cachedMemoryOverlay !== void 0 ? { cachedMemoryOverlay: runResult.cachedMemoryOverlay } : {}
|
|
6869
|
+
...runResult.cachedMemoryOverlay !== void 0 ? { cachedMemoryOverlay: runResult.cachedMemoryOverlay } : {},
|
|
6870
|
+
// 切换消息本轮已注入 agent 上下文(runAgentChat 已读),消费掉,
|
|
6871
|
+
// 避免下轮重复注入。仅当确实发起了 agent 调用(本轮是真实 turn、
|
|
6872
|
+
// 而非纯 child-run-completed 事件短路)时才清除。
|
|
6873
|
+
pendingCwdNotice: void 0
|
|
6407
6874
|
};
|
|
6408
|
-
if (runResult.dialogId && nextDialogKey) {
|
|
6409
|
-
ctx.refreshDialogTotalCredits(runResult.dialogId, nextDialogKey);
|
|
6410
|
-
}
|
|
6411
6875
|
}
|
|
6876
|
+
ctx.accumulateSessionCredits(runResult.turnCredits);
|
|
6877
|
+
appendTurnBillingAudit({
|
|
6878
|
+
dialogId: runResult.dialogId ?? null,
|
|
6879
|
+
turnCredits: runResult.turnCredits ?? null,
|
|
6880
|
+
usageRecords: runResult.usageRecords,
|
|
6881
|
+
// 普通中断(Esc)同样属于「账照记、轮未正常收尾」,审计行标注出来,
|
|
6882
|
+
// 与 1028 行 forceStop 路径及正常完成轮区分开。
|
|
6883
|
+
...wasAborted || runResult.streamInterrupted ? { aborted: true } : {}
|
|
6884
|
+
});
|
|
6412
6885
|
scheduleTitlePatchSync(runResult);
|
|
6413
6886
|
if (!wasAborted && runResult.exitCode !== 0) {
|
|
6414
6887
|
if (isBalanceExhaustedError(runResult.localError) && runResult.dialogId) {
|
|
@@ -6426,6 +6899,21 @@ async function runOneAgentTurn(ctx, inputMsg, imageUrls, actionGateHandler, conf
|
|
|
6426
6899
|
ctx.runRegistryPoller.endHold();
|
|
6427
6900
|
ctx.activityIndicator.stop();
|
|
6428
6901
|
ctx.activeTurnAbort = null;
|
|
6902
|
+
if (ctx.turnInjectionInbox === injectionInbox) ctx.turnInjectionInbox = null;
|
|
6903
|
+
const leftover = injectionInbox.close();
|
|
6904
|
+
if (leftover.length > 0) {
|
|
6905
|
+
const binding = ensureChatQueueBinding(
|
|
6906
|
+
ctx,
|
|
6907
|
+
actionGateHandler,
|
|
6908
|
+
confirmDestructiveAction
|
|
6909
|
+
);
|
|
6910
|
+
for (const entry of leftover) {
|
|
6911
|
+
try {
|
|
6912
|
+
binding.enqueue(entry.fallback);
|
|
6913
|
+
} catch {
|
|
6914
|
+
}
|
|
6915
|
+
}
|
|
6916
|
+
}
|
|
6429
6917
|
}
|
|
6430
6918
|
}
|
|
6431
6919
|
function ensureChatQueueBinding(ctx, actionGateHandler, confirmDestructiveAction) {
|
|
@@ -7013,7 +7501,7 @@ function createFixedInput(output, config) {
|
|
|
7013
7501
|
const renderInputArea = (buffer, cursorPos) => {
|
|
7014
7502
|
const colorEnabled = resolveCliColorEnabled();
|
|
7015
7503
|
const cols = Math.max(1, getColumns());
|
|
7016
|
-
const completions = completeSlashCommand(buffer);
|
|
7504
|
+
const completions = completeSlashCommand(buffer, config.getCwd?.());
|
|
7017
7505
|
const sections = [];
|
|
7018
7506
|
if (completions.length > 0) {
|
|
7019
7507
|
sections.push(fitAnsiLine(dimCliText(completions.join(" "), colorEnabled), cols));
|
|
@@ -7034,6 +7522,10 @@ function createFixedInput(output, config) {
|
|
|
7034
7522
|
for (const line of queueLines) {
|
|
7035
7523
|
sections.push(fitAnsiLine(line.replace(/\r?\n/g, " "), cols));
|
|
7036
7524
|
}
|
|
7525
|
+
const attachmentLine = config.getAttachmentLine?.() ?? null;
|
|
7526
|
+
if (attachmentLine) {
|
|
7527
|
+
sections.push(fitAnsiLine(attachmentLine.replace(/\r?\n/g, " "), cols));
|
|
7528
|
+
}
|
|
7037
7529
|
const promptRaw = t("promptLabel");
|
|
7038
7530
|
const promptWidth = displayWidth(promptRaw);
|
|
7039
7531
|
const prompt = colorEnabled ? `${themeColorSequence("accent")}${promptRaw}\x1B[39m` : promptRaw;
|
|
@@ -7083,7 +7575,7 @@ function createFixedInput(output, config) {
|
|
|
7083
7575
|
}
|
|
7084
7576
|
const text = sections.join("\n");
|
|
7085
7577
|
const lines = sections.length;
|
|
7086
|
-
const headerRows = (completions.length > 0 ? 1 : 0) + 2 + (titleLine ? 1 : 0) + activityLines.length + queueLines.length;
|
|
7578
|
+
const headerRows = (completions.length > 0 ? 1 : 0) + 2 + (titleLine ? 1 : 0) + activityLines.length + queueLines.length + (attachmentLine ? 1 : 0);
|
|
7087
7579
|
return { text, lines, cursorCol, cursorRow: headerRows + cursorRow };
|
|
7088
7580
|
};
|
|
7089
7581
|
const repaintAt = (buffer, cursorPos, force = false) => {
|
|
@@ -7493,7 +7985,8 @@ async function runSubmittedSlashLine(line, host, actionGateHandler, confirmDestr
|
|
|
7493
7985
|
turnCtx,
|
|
7494
7986
|
emitCommandOutput,
|
|
7495
7987
|
renderHistoryToOutput,
|
|
7496
|
-
|
|
7988
|
+
scheduleRender,
|
|
7989
|
+
seedDialogCreditsBase,
|
|
7497
7990
|
persistExplicitAgentSwitch,
|
|
7498
7991
|
persistAgentSelection: persistAgentSelection2,
|
|
7499
7992
|
writeClipboard: writeClipboard2,
|
|
@@ -7541,8 +8034,10 @@ async function runSubmittedSlashLine(line, host, actionGateHandler, confirmDestr
|
|
|
7541
8034
|
host.state = {
|
|
7542
8035
|
...host.state,
|
|
7543
8036
|
turnTokens: void 0,
|
|
7544
|
-
//
|
|
7545
|
-
|
|
8037
|
+
// 新对话的积分累计从 0 重新开始:既清掉旧对话的本会话累加,也清掉旧对话
|
|
8038
|
+
// 的历史基数(新对话没有历史)。
|
|
8039
|
+
sessionCredits: void 0,
|
|
8040
|
+
dialogCreditsBase: void 0,
|
|
7546
8041
|
cachedMemoryOverlay: void 0,
|
|
7547
8042
|
// 新对话重新加载记忆
|
|
7548
8043
|
estimatedContextTokens: estimateDefaultCliContextTokens({
|
|
@@ -7592,9 +8087,10 @@ async function runSubmittedSlashLine(line, host, actionGateHandler, confirmDestr
|
|
|
7592
8087
|
// composer chip falls back to the default CLI surface estimate and
|
|
7593
8088
|
// the context percentage visibly drops. Mirrors the /clear reset.
|
|
7594
8089
|
turnTokens: void 0,
|
|
7595
|
-
// compact fork 出新的官方 dialog
|
|
7596
|
-
//
|
|
7597
|
-
|
|
8090
|
+
// compact fork 出新的官方 dialog,计费从新记录开始累计;新 dialog 没有
|
|
8091
|
+
// 历史基数,本会话累加也随之归零。
|
|
8092
|
+
sessionCredits: void 0,
|
|
8093
|
+
dialogCreditsBase: void 0,
|
|
7598
8094
|
cachedMemoryOverlay: void 0,
|
|
7599
8095
|
// compact 创建新 dialog,重新加载记忆
|
|
7600
8096
|
estimatedContextTokens: estimateDefaultCliContextTokens({
|
|
@@ -7666,6 +8162,18 @@ async function runSubmittedSlashLine(line, host, actionGateHandler, confirmDestr
|
|
|
7666
8162
|
emitCommandOutput(t("themeRefreshFailed"));
|
|
7667
8163
|
}
|
|
7668
8164
|
}
|
|
8165
|
+
if (result.action?.type === "cwd-refresh") {
|
|
8166
|
+
if ((options.env?.NOLO_CLI_GIT_STATUS ?? process.env.NOLO_CLI_GIT_STATUS) !== "0") {
|
|
8167
|
+
const gitStatus = await detectGitStatusAsync(host.state.cwd);
|
|
8168
|
+
if (gitStatus !== void 0) {
|
|
8169
|
+
host.state = { ...host.state, gitStatus };
|
|
8170
|
+
}
|
|
8171
|
+
}
|
|
8172
|
+
if (result.action.switchMessage) {
|
|
8173
|
+
emitCommandOutput(result.action.switchMessage);
|
|
8174
|
+
}
|
|
8175
|
+
scheduleRender();
|
|
8176
|
+
}
|
|
7669
8177
|
if (result.action?.type === "pick-agent") {
|
|
7670
8178
|
try {
|
|
7671
8179
|
const pickResult = await dialogHost.run(
|
|
@@ -7697,7 +8205,6 @@ async function runSubmittedSlashLine(line, host, actionGateHandler, confirmDestr
|
|
|
7697
8205
|
model: pickResult.model,
|
|
7698
8206
|
userLanguage: host.state.userLanguage
|
|
7699
8207
|
}),
|
|
7700
|
-
...pickResult.apiSource ? { apiSource: pickResult.apiSource } : {},
|
|
7701
8208
|
cachedMemoryOverlay: void 0
|
|
7702
8209
|
// 切换 agent 后重新加载记忆
|
|
7703
8210
|
};
|
|
@@ -7814,13 +8321,14 @@ async function runSubmittedSlashLine(line, host, actionGateHandler, confirmDestr
|
|
|
7814
8321
|
dialogLabel: pickResult.dialog.title || pickResult.dialog.id,
|
|
7815
8322
|
dialogTitle: pickResult.dialog.title,
|
|
7816
8323
|
turnTokens: void 0,
|
|
7817
|
-
//
|
|
8324
|
+
// 切换对话先清空上一个对话的积分,等下面异步 seed 成功后回填历史基数;
|
|
7818
8325
|
// 若读取失败也不至于把旧对话的累计值残留显示在状态行。
|
|
7819
|
-
|
|
8326
|
+
sessionCredits: void 0,
|
|
8327
|
+
dialogCreditsBase: void 0,
|
|
7820
8328
|
cachedMemoryOverlay: void 0
|
|
7821
8329
|
// 切换对话后重新加载记忆
|
|
7822
8330
|
};
|
|
7823
|
-
|
|
8331
|
+
seedDialogCreditsBase(pickResult.dialog.id, pickResult.dialog.dbKey);
|
|
7824
8332
|
clearCollapsedPasteStore(pasteStore);
|
|
7825
8333
|
emitCommandOutput(
|
|
7826
8334
|
`${t("resumedDialogPrefix")}: ${pickResult.dialog.title} (${pickResult.dialog.id})`
|
|
@@ -8097,7 +8605,6 @@ async function runTuiWorkspace(options) {
|
|
|
8097
8605
|
isTurnActive: () => activeTurnAbort !== null,
|
|
8098
8606
|
fallbackLabel: () => `${state.agentName} -> working`,
|
|
8099
8607
|
stoppingLabel: () => t("turnStopping"),
|
|
8100
|
-
displayMode: () => state.toolDisplay,
|
|
8101
8608
|
onRepaint: () => {
|
|
8102
8609
|
if (fixedInput.active && !fixedInput.isPaused()) {
|
|
8103
8610
|
output.write("\x1B[?2026h\x1B[?25l");
|
|
@@ -8141,7 +8648,14 @@ async function runTuiWorkspace(options) {
|
|
|
8141
8648
|
getInputLines: () => fixedInput.getInputLines(),
|
|
8142
8649
|
isPaused: () => fixedInput.isPaused()
|
|
8143
8650
|
},
|
|
8144
|
-
output
|
|
8651
|
+
output,
|
|
8652
|
+
// The decoder-drain hook is only bound once the interactive raw-mode
|
|
8653
|
+
// composer installs its decoder (inside the `isInteractiveInput` block
|
|
8654
|
+
// below); the non-raw readline path never binds it, so this stays a
|
|
8655
|
+
// no-op there — which is correct, that path has no raw decoder to drain.
|
|
8656
|
+
drainDecoder: () => {
|
|
8657
|
+
if (composerDecoderDrain) composerDecoderDrain();
|
|
8658
|
+
}
|
|
8145
8659
|
});
|
|
8146
8660
|
let sessionEnded = false;
|
|
8147
8661
|
const selectionState = createSelectionState();
|
|
@@ -8170,7 +8684,7 @@ async function runTuiWorkspace(options) {
|
|
|
8170
8684
|
const repaintBanner = () => {
|
|
8171
8685
|
if (!output.isTTY) return;
|
|
8172
8686
|
if (fixedInput.isPaused()) return;
|
|
8173
|
-
if (
|
|
8687
|
+
if (dialogHost.isKeyboardClaimed()) return;
|
|
8174
8688
|
const currentColumns = output.columns ?? 80;
|
|
8175
8689
|
const welcome = renderWelcome(state, 0, 0, currentColumns);
|
|
8176
8690
|
const lines = welcome.split("\n");
|
|
@@ -8219,9 +8733,8 @@ async function runTuiWorkspace(options) {
|
|
|
8219
8733
|
selectionState.anchor = null;
|
|
8220
8734
|
selectionState.head = null;
|
|
8221
8735
|
};
|
|
8222
|
-
let modalOwnsKeyboard = false;
|
|
8223
8736
|
let rawActionGateTokenHandler = null;
|
|
8224
|
-
const
|
|
8737
|
+
const seedDialogCreditsBase = (dialogId, dialogKey) => {
|
|
8225
8738
|
const env = options.env ?? process.env;
|
|
8226
8739
|
const authToken = resolvePlatformAuthToken(env);
|
|
8227
8740
|
if (!authToken) return;
|
|
@@ -8233,14 +8746,19 @@ async function runTuiWorkspace(options) {
|
|
|
8233
8746
|
}).then((record) => {
|
|
8234
8747
|
if (sessionEnded) return;
|
|
8235
8748
|
if (state.dialogId !== dialogId) return;
|
|
8749
|
+
if (state.sessionCredits !== void 0) return;
|
|
8236
8750
|
const recordObj = record && typeof record === "object" ? record : null;
|
|
8237
8751
|
const totalCost = recordObj ? Number(recordObj?.totalCost) : NaN;
|
|
8238
8752
|
if (Number.isFinite(totalCost) && totalCost >= 0) {
|
|
8239
|
-
state = { ...state,
|
|
8753
|
+
state = { ...state, dialogCreditsBase: totalCost };
|
|
8240
8754
|
}
|
|
8241
8755
|
}).catch(() => {
|
|
8242
8756
|
});
|
|
8243
8757
|
};
|
|
8758
|
+
const accumulateSessionCredits = (credits) => {
|
|
8759
|
+
if (credits === void 0 || !Number.isFinite(credits)) return;
|
|
8760
|
+
state = { ...state, sessionCredits: (state.sessionCredits ?? 0) + credits };
|
|
8761
|
+
};
|
|
8244
8762
|
let composerDecoderDrain = null;
|
|
8245
8763
|
const emitCommandOutput = (text, command = "") => {
|
|
8246
8764
|
if (!text) return;
|
|
@@ -8255,6 +8773,7 @@ async function runTuiWorkspace(options) {
|
|
|
8255
8773
|
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
8256
8774
|
};
|
|
8257
8775
|
let chatQueueBinding = null;
|
|
8776
|
+
let turnInjectionInbox = null;
|
|
8258
8777
|
const turnCtx = {
|
|
8259
8778
|
get state() {
|
|
8260
8779
|
return state;
|
|
@@ -8292,24 +8811,18 @@ async function runTuiWorkspace(options) {
|
|
|
8292
8811
|
set activeTurnEpoch(v) {
|
|
8293
8812
|
activeTurnEpoch = v;
|
|
8294
8813
|
},
|
|
8295
|
-
get modalOwnsKeyboard() {
|
|
8296
|
-
return modalOwnsKeyboard;
|
|
8297
|
-
},
|
|
8298
|
-
set modalOwnsKeyboard(v) {
|
|
8299
|
-
modalOwnsKeyboard = v;
|
|
8300
|
-
},
|
|
8301
|
-
get composerDecoderDrain() {
|
|
8302
|
-
return composerDecoderDrain;
|
|
8303
|
-
},
|
|
8304
|
-
set composerDecoderDrain(v) {
|
|
8305
|
-
composerDecoderDrain = v;
|
|
8306
|
-
},
|
|
8307
8814
|
get chatQueueBinding() {
|
|
8308
8815
|
return chatQueueBinding;
|
|
8309
8816
|
},
|
|
8310
8817
|
set chatQueueBinding(v) {
|
|
8311
8818
|
chatQueueBinding = v;
|
|
8312
8819
|
},
|
|
8820
|
+
get turnInjectionInbox() {
|
|
8821
|
+
return turnInjectionInbox;
|
|
8822
|
+
},
|
|
8823
|
+
set turnInjectionInbox(v) {
|
|
8824
|
+
turnInjectionInbox = v;
|
|
8825
|
+
},
|
|
8313
8826
|
get sessionEnded() {
|
|
8314
8827
|
return sessionEnded;
|
|
8315
8828
|
},
|
|
@@ -8337,7 +8850,8 @@ async function runTuiWorkspace(options) {
|
|
|
8337
8850
|
renderHistoryToOutput,
|
|
8338
8851
|
scheduleRender,
|
|
8339
8852
|
flushPendingRender,
|
|
8340
|
-
|
|
8853
|
+
seedDialogCreditsBase,
|
|
8854
|
+
accumulateSessionCredits,
|
|
8341
8855
|
emitCommandOutput
|
|
8342
8856
|
};
|
|
8343
8857
|
const persistExplicitAgentSwitch = (previousAgentKey) => {
|
|
@@ -8375,7 +8889,8 @@ async function runTuiWorkspace(options) {
|
|
|
8375
8889
|
turnCtx,
|
|
8376
8890
|
emitCommandOutput,
|
|
8377
8891
|
renderHistoryToOutput,
|
|
8378
|
-
|
|
8892
|
+
scheduleRender,
|
|
8893
|
+
seedDialogCreditsBase,
|
|
8379
8894
|
persistExplicitAgentSwitch,
|
|
8380
8895
|
persistAgentSelection,
|
|
8381
8896
|
writeClipboard: writeClipboard2,
|
|
@@ -8407,9 +8922,16 @@ async function runTuiWorkspace(options) {
|
|
|
8407
8922
|
return dimCliText(` \u2937 ${i + 1}. ${oneLine}`, colorEnabled);
|
|
8408
8923
|
});
|
|
8409
8924
|
},
|
|
8925
|
+
getAttachmentLine: () => {
|
|
8926
|
+
const line = formatComposerAttachmentLine(state.attachedImages);
|
|
8927
|
+
return line ? dimCliText(line, resolveCliColorEnabled()) : null;
|
|
8928
|
+
},
|
|
8410
8929
|
onInputLinesChange: () => {
|
|
8411
8930
|
renderHistoryToOutput();
|
|
8412
|
-
}
|
|
8931
|
+
},
|
|
8932
|
+
// /cd 路径补全候选行渲染需要当前 cwd(读 state 而非入参,保证
|
|
8933
|
+
// /cd 切换后立即以新 cwd 渲染,composer 不必重建)。
|
|
8934
|
+
getCwd: () => state.cwd
|
|
8413
8935
|
});
|
|
8414
8936
|
fixedInput = {
|
|
8415
8937
|
...baseFixedInput,
|
|
@@ -8483,39 +9005,33 @@ async function runTuiWorkspace(options) {
|
|
|
8483
9005
|
input.setRawMode?.(false);
|
|
8484
9006
|
};
|
|
8485
9007
|
const buildInteractiveTurnHandlers = () => {
|
|
8486
|
-
const releaseKeyboardToComposer = () => {
|
|
8487
|
-
onData.destroy();
|
|
8488
|
-
modalOwnsKeyboard = false;
|
|
8489
|
-
};
|
|
8490
9008
|
const actionGateHandler = async (gate) => {
|
|
8491
|
-
|
|
8492
|
-
|
|
8493
|
-
return await waitForRawActionGate(input, output, gate, spawnRunner, {
|
|
8494
|
-
beforeSubprocess: () => fixedInput.pause(),
|
|
8495
|
-
afterSubprocess: () => fixedInput.resumeFromSubprocess(),
|
|
8496
|
-
registerToken: (handler) => {
|
|
8497
|
-
rawActionGateTokenHandler = handler;
|
|
8498
|
-
}
|
|
8499
|
-
});
|
|
8500
|
-
} finally {
|
|
8501
|
-
releaseKeyboardToComposer();
|
|
9009
|
+
if (state.autoConfirm && isAutoConfirmableFileWriteGate(gate)) {
|
|
9010
|
+
return buildGateConfirmedResult(gate);
|
|
8502
9011
|
}
|
|
9012
|
+
return await resolveActionGate(gate, {
|
|
9013
|
+
dialogHost,
|
|
9014
|
+
input,
|
|
9015
|
+
output,
|
|
9016
|
+
spawnRunner,
|
|
9017
|
+
registerToken: (handler) => {
|
|
9018
|
+
rawActionGateTokenHandler = handler;
|
|
9019
|
+
},
|
|
9020
|
+
pauseComposer: () => fixedInput.pause(),
|
|
9021
|
+
resumeComposerFromSubprocess: () => fixedInput.resumeFromSubprocess(),
|
|
9022
|
+
resumeComposerFromDialog: () => fixedInput.resumeFromDialog()
|
|
9023
|
+
});
|
|
8503
9024
|
};
|
|
8504
9025
|
const confirmDestructiveAction = async (request) => {
|
|
8505
9026
|
if (state.autoConfirm) return true;
|
|
8506
|
-
|
|
8507
|
-
|
|
8508
|
-
|
|
8509
|
-
|
|
8510
|
-
|
|
8511
|
-
|
|
8512
|
-
|
|
8513
|
-
|
|
8514
|
-
})
|
|
8515
|
-
);
|
|
8516
|
-
} finally {
|
|
8517
|
-
releaseKeyboardToComposer();
|
|
8518
|
-
}
|
|
9027
|
+
return await dialogHost.run(
|
|
9028
|
+
(anchor) => runConfirmDialog({
|
|
9029
|
+
request,
|
|
9030
|
+
input,
|
|
9031
|
+
output,
|
|
9032
|
+
...anchor
|
|
9033
|
+
})
|
|
9034
|
+
);
|
|
8519
9035
|
};
|
|
8520
9036
|
return { actionGateHandler, confirmDestructiveAction };
|
|
8521
9037
|
};
|
|
@@ -8548,11 +9064,30 @@ ${err.message}` : ""}`
|
|
|
8548
9064
|
flushPendingRender();
|
|
8549
9065
|
fixedInput.exitOutputMode(buffer, cursorPos);
|
|
8550
9066
|
};
|
|
9067
|
+
(options.env ?? process.env)[RUN_WAKE_CHANNEL_ENV] = "1";
|
|
8551
9068
|
runWakeHandler = (event) => {
|
|
8552
9069
|
if (done) return;
|
|
8553
9070
|
if (busy || fixedInput.isPaused()) {
|
|
8554
|
-
const
|
|
8555
|
-
|
|
9071
|
+
const text = typeof event === "string" ? event : event.text;
|
|
9072
|
+
const injected = !!turnInjectionInbox && turnInjectionInbox.push({ text, fallback: event });
|
|
9073
|
+
if (injected) {
|
|
9074
|
+
const displayText = typeof event !== "string" && event.kind === "child-run-completed" ? event.displayText ?? event.text : text;
|
|
9075
|
+
const noticeLine = dimCliText(displayText, resolveCliColorEnabled());
|
|
9076
|
+
if (!isInteractiveInput(input)) {
|
|
9077
|
+
output.write(`${noticeLine}
|
|
9078
|
+
`);
|
|
9079
|
+
} else {
|
|
9080
|
+
history.followBottom = true;
|
|
9081
|
+
appendStreamSafeNotice(history, noticeLine, {
|
|
9082
|
+
appendLocalTurn,
|
|
9083
|
+
startTurn
|
|
9084
|
+
});
|
|
9085
|
+
renderHistoryToOutput();
|
|
9086
|
+
}
|
|
9087
|
+
} else {
|
|
9088
|
+
const { actionGateHandler, confirmDestructiveAction } = buildInteractiveTurnHandlers();
|
|
9089
|
+
ensureChatQueueBinding(turnCtx, actionGateHandler, confirmDestructiveAction).enqueue(event);
|
|
9090
|
+
}
|
|
8556
9091
|
if (fixedInput.active && !fixedInput.isPaused()) {
|
|
8557
9092
|
fixedInput.repaint(buffer, cursorPos);
|
|
8558
9093
|
}
|
|
@@ -8693,6 +9228,19 @@ ${err.message}` : ""}`
|
|
|
8693
9228
|
output.write(`${refreshMsg}
|
|
8694
9229
|
`);
|
|
8695
9230
|
}
|
|
9231
|
+
} else if (res.action?.type === "cwd-refresh") {
|
|
9232
|
+
state = res.nextState;
|
|
9233
|
+
if (res.output) {
|
|
9234
|
+
output.write(`${res.output}
|
|
9235
|
+
`);
|
|
9236
|
+
}
|
|
9237
|
+
if ((options.env?.NOLO_CLI_GIT_STATUS ?? process.env.NOLO_CLI_GIT_STATUS) !== "0") {
|
|
9238
|
+
const gitStatus = await detectGitStatusAsync(state.cwd);
|
|
9239
|
+
if (gitStatus !== void 0) {
|
|
9240
|
+
state = { ...state, gitStatus };
|
|
9241
|
+
}
|
|
9242
|
+
}
|
|
9243
|
+
scheduleRender();
|
|
8696
9244
|
} else if (res.action) {
|
|
8697
9245
|
output.write(
|
|
8698
9246
|
"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"
|
|
@@ -8717,7 +9265,7 @@ ${hint}` : hint;
|
|
|
8717
9265
|
};
|
|
8718
9266
|
const handleInputToken = async (sequence) => {
|
|
8719
9267
|
if (done) return;
|
|
8720
|
-
if (
|
|
9268
|
+
if (dialogHost.isKeyboardClaimed()) {
|
|
8721
9269
|
rawActionGateTokenHandler?.(sequence);
|
|
8722
9270
|
return;
|
|
8723
9271
|
}
|
|
@@ -8901,8 +9449,20 @@ ${merged}` : merged;
|
|
|
8901
9449
|
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
8902
9450
|
return;
|
|
8903
9451
|
}
|
|
9452
|
+
if (isBackspaceSequence(sequence, {}) && buffer === "" && state.attachedImages.length > 0) {
|
|
9453
|
+
const undo = popLastAttachedImage(state.attachedImages);
|
|
9454
|
+
if (undo.handled && undo.removed) {
|
|
9455
|
+
state = { ...state, attachedImages: undo.images };
|
|
9456
|
+
emitCommandOutput(
|
|
9457
|
+
`[nolo] ${t("attachmentRemovedHint", undo.removed.filename, String(undo.images.length))}`
|
|
9458
|
+
);
|
|
9459
|
+
if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
|
|
9460
|
+
return;
|
|
9461
|
+
}
|
|
9462
|
+
}
|
|
8904
9463
|
const result = applyTuiInputKey(buffer, sequence, {}, cursorPos, {
|
|
8905
|
-
pasteStore
|
|
9464
|
+
pasteStore,
|
|
9465
|
+
cwd: state.cwd
|
|
8906
9466
|
});
|
|
8907
9467
|
if (result.redraw) {
|
|
8908
9468
|
resetHistoryFrameDiffCache(output);
|
|
@@ -8925,7 +9485,7 @@ ${merged}` : merged;
|
|
|
8925
9485
|
if (busyLock) {
|
|
8926
9486
|
const trimmedText = submittedText.trim();
|
|
8927
9487
|
const busySlashCommand = trimmedText.split(/\s+/)[0]?.toLowerCase();
|
|
8928
|
-
const isBusyLocalSlash = busySlashCommand === "/context" || busySlashCommand === "/ctx" || busySlashCommand === "/
|
|
9488
|
+
const isBusyLocalSlash = busySlashCommand === "/context" || busySlashCommand === "/ctx" || busySlashCommand === "/cd" || busySlashCommand === "/switch" || busySlashCommand === "/theme" || busySlashCommand === "/runtime" || busySlashCommand === "/tools" || busySlashCommand === "/thinking" || busySlashCommand === "/auto" || busySlashCommand === "/tasks" || busySlashCommand === "/jobs" || busySlashCommand === "/procs" || busySlashCommand === "/agents" || busySlashCommand === "/doc" || busySlashCommand === "/skill" || busySlashCommand === "/customize" || busySlashCommand === "/login" || busySlashCommand === "/profile" || busySlashCommand === "/version";
|
|
8929
9489
|
if (isBusyLocalSlash) {
|
|
8930
9490
|
await handleBusyLocalSlash(submittedText, busySlashCommand);
|
|
8931
9491
|
return;
|
|
@@ -8995,9 +9555,9 @@ ${merged}` : merged;
|
|
|
8995
9555
|
input.on("data", onData);
|
|
8996
9556
|
fixedInput.repaint(buffer, cursorPos);
|
|
8997
9557
|
refreshGitStatus();
|
|
8998
|
-
await new Promise((
|
|
8999
|
-
resolveDone =
|
|
9000
|
-
if (done)
|
|
9558
|
+
await new Promise((resolve4) => {
|
|
9559
|
+
resolveDone = resolve4;
|
|
9560
|
+
if (done) resolve4();
|
|
9001
9561
|
});
|
|
9002
9562
|
} finally {
|
|
9003
9563
|
finish();
|
|
@@ -9011,7 +9571,11 @@ ${merged}` : merged;
|
|
|
9011
9571
|
for await (const line of rl) {
|
|
9012
9572
|
const shouldExit = await runSubmittedLine(
|
|
9013
9573
|
line,
|
|
9014
|
-
(gate) =>
|
|
9574
|
+
(gate) => (
|
|
9575
|
+
// /auto on 同款短路,见上方交互路径的 actionGateHandler 注释:
|
|
9576
|
+
// 只放行 file_write 的 confirm gate,handoff/input 类仍要求真人操作。
|
|
9577
|
+
state.autoConfirm && isAutoConfirmableFileWriteGate(gate) ? Promise.resolve(buildGateConfirmedResult(gate)) : waitForActionGate(rl, input, output, gate, spawnRunner)
|
|
9578
|
+
),
|
|
9015
9579
|
async (request) => {
|
|
9016
9580
|
if (state.autoConfirm) return true;
|
|
9017
9581
|
rl.pause();
|
|
@@ -9050,12 +9614,16 @@ ${renderStatusLine(state)}
|
|
|
9050
9614
|
}
|
|
9051
9615
|
async function startTuiWorkspace(options) {
|
|
9052
9616
|
const themeOwner = ++latestWorkspaceThemeOwner;
|
|
9617
|
+
void sweepStaleClipboardFiles(getDefaultClipboardTempDir()).catch(() => {
|
|
9618
|
+
});
|
|
9053
9619
|
try {
|
|
9054
9620
|
return await runTuiWorkspace(options);
|
|
9055
9621
|
} finally {
|
|
9056
9622
|
if (themeOwner === latestWorkspaceThemeOwner) {
|
|
9057
9623
|
resetWorkspaceThemeState();
|
|
9058
9624
|
}
|
|
9625
|
+
void sweepStaleClipboardFiles(getDefaultClipboardTempDir()).catch(() => {
|
|
9626
|
+
});
|
|
9059
9627
|
}
|
|
9060
9628
|
}
|
|
9061
9629
|
export {
|