nolo-cli 0.53.0-alpha.1 → 0.54.0-alpha.2

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.
Files changed (110) hide show
  1. package/{_executeModel-36GKPAEZ.js → _executeModel-ANY4SIAS.js} +34 -35
  2. package/{action-IU6ON7ZB.js → action-Q4W5JPI5.js} +7 -7
  3. package/{addContentAction-5V5CCI3I.js → addContentAction-O25HFR37.js} +6 -6
  4. package/{addDialogAgentAction-KF3KZZNZ.js → addDialogAgentAction-GH7AUPPB.js} +5 -5
  5. package/{agentDeleteCommand-SBODEUPE.js → agentDeleteCommand-JWIKNWWI.js} +17 -17
  6. package/{agentEmailCommands-ME7BCY5S.js → agentEmailCommands-PL22BEU5.js} +39 -40
  7. package/{agentListCommands-QBWPKN5D.js → agentListCommands-6NR7AQEK.js} +19 -19
  8. package/{agentMachineCommands-KFFUPELZ.js → agentMachineCommands-TASPU6OA.js} +18 -18
  9. package/{agentRecordCommands-4BF6JIHS.js → agentRecordCommands-Z64TA63M.js} +38 -39
  10. package/{agentRunCommand-4MA7D6SV.js → agentRunCommand-4QJQLPRW.js} +39 -40
  11. package/{agentRuntimeLocal-RD52NPCG.js → agentRuntimeLocal-PCJJ56J2.js} +17 -17
  12. package/{agentSlice-AJABGQHU.js → agentSlice-KYLDUSNN.js} +6 -6
  13. package/{appDeleteCommands-I42NL3LP.js → appDeleteCommands-WLHMJAVT.js} +19 -19
  14. package/{appDeployCommands-CZWHXVPU.js → appDeployCommands-CKON7N2E.js} +19 -19
  15. package/{appDeployStatusCommands-PIQYXACM.js → appDeployStatusCommands-PXRLDASH.js} +19 -19
  16. package/{appGetCommands-CI2OWLT7.js → appGetCommands-RAB5RM4M.js} +19 -19
  17. package/{appListCommands-LWLSHPCP.js → appListCommands-GU57FB62.js} +19 -19
  18. package/{authCommand-WLWOEVYI.js → authCommand-QOPOEYEK.js} +7 -7
  19. package/{chatQueueReduxAdapter-V37RK3A6.js → chatQueueReduxAdapter-5O2WLITN.js} +16 -16
  20. package/chunk-2BWU4ORW.js +2574 -0
  21. package/{chunk-SG65C33A.js → chunk-32I5RVMY.js} +4 -4
  22. package/{chunk-X7S7OFUX.js → chunk-3QJTD6DO.js} +4 -4
  23. package/{chunk-CY5KIIPU.js → chunk-3Z3DXZBD.js} +12 -12
  24. package/{chunk-RO2IUWZE.js → chunk-4NN3EK7H.js} +1 -1
  25. package/{chunk-Y7TGWSUN.js → chunk-56W233KG.js} +1 -1
  26. package/{chunk-C2WF2OY7.js → chunk-5WADL4O7.js} +1 -1
  27. package/{chunk-QTH72J3N.js → chunk-6BGAQ3KR.js} +116 -23
  28. package/{chunk-67Q6IHM5.js → chunk-6FQ2552G.js} +142 -62
  29. package/{chunk-6XIE5ZGI.js → chunk-6HIGYUBQ.js} +1 -1
  30. package/{chunk-XPW6JLOB.js → chunk-6SZ52ORL.js} +8 -8
  31. package/{chunk-4ZL2KBI7.js → chunk-757BGY6Y.js} +1 -1
  32. package/{chunk-SQTVMYMU.js → chunk-7APQB6R6.js} +3 -3
  33. package/{chunk-QF27IZB3.js → chunk-7K5VMB4Q.js} +3 -3
  34. package/{chunk-V2IPBG5U.js → chunk-AA564GBL.js} +2 -2
  35. package/{chunk-76UA7376.js → chunk-AV6KQCM5.js} +6 -6
  36. package/{chunk-PGKZ2KGE.js → chunk-BJLYCOZK.js} +24 -24
  37. package/{chunk-VKSBVRGP.js → chunk-CVT7L2WZ.js} +1 -1
  38. package/{chunk-JVKFKKYN.js → chunk-D3JQPONH.js} +1 -1
  39. package/{chunk-KHCLMKZZ.js → chunk-EY7V7REW.js} +280 -6
  40. package/{chunk-4RXXP6WD.js → chunk-FGEYVPPR.js} +2 -2
  41. package/{chunk-AZ6DT2UD.js → chunk-GBADO3SM.js} +1 -1
  42. package/chunk-GLGD2JGY.js +1766 -0
  43. package/{chunk-POLE2QLO.js → chunk-GV35EHNA.js} +126 -59
  44. package/{chunk-HR5XDXYM.js → chunk-H4RYESI6.js} +3 -3
  45. package/{chunk-W3RZLXMA.js → chunk-IKTHA3JH.js} +2 -2
  46. package/{chunk-XB7ACJFH.js → chunk-INXBNYCG.js} +3 -3
  47. package/{chunk-F22JQTSX.js → chunk-KUUTWU4I.js} +3 -3
  48. package/{chunk-DFY6O64O.js → chunk-LA4IDAK7.js} +12 -12
  49. package/{chunk-IVKEJD7I.js → chunk-LN3OQJGG.js} +1 -1
  50. package/{chunk-YZ2WE6CW.js → chunk-MFI4FGHX.js} +2 -2
  51. package/{chunk-LGUQHYAL.js → chunk-O5FLGN2L.js} +3 -2
  52. package/{chunk-HCBQVVFZ.js → chunk-PAHILFNT.js} +1 -1
  53. package/{chunk-HHBXCRJS.js → chunk-PYKV6FSC.js} +1 -1
  54. package/{chunk-BO3YFOZR.js → chunk-QDHZCAH3.js} +1 -1
  55. package/{chunk-L3JWI7WX.js → chunk-QOKAKDPE.js} +15 -11
  56. package/{chunk-B54WOZ77.js → chunk-RTLJUGQH.js} +1 -1
  57. package/{chunk-Z7A54NFZ.js → chunk-RU5D3B22.js} +1 -1
  58. package/{chunk-JBL24PK6.js → chunk-SP3DPVBH.js} +2 -2
  59. package/{chunk-TZRB2I2M.js → chunk-TI5BCZCH.js} +18 -18
  60. package/{chunk-KOZ2G3ZG.js → chunk-U2A47SVI.js} +5 -5
  61. package/{chunk-3YVCM5PE.js → chunk-U5Z7LFZF.js} +1 -1
  62. package/{chunk-DIQCV4FR.js → chunk-UITMHBVK.js} +1 -1
  63. package/{chunk-H5QLIP6S.js → chunk-UW4INMD2.js} +2 -2
  64. package/{chunk-5SLTA66C.js → chunk-V4KZGLOI.js} +30 -30
  65. package/{chunk-AV4Z57LV.js → chunk-VRJUIUIY.js} +2 -2
  66. package/{chunk-5XCW6RQG.js → chunk-VRZIH5JO.js} +1 -1
  67. package/{chunk-2JYSSXLY.js → chunk-VULELK4T.js} +2 -2
  68. package/{chunk-VFN256RI.js → chunk-WL3UOGYO.js} +25 -0
  69. package/{chunk-PJASTA2U.js → chunk-Y7LNKP32.js} +3 -3
  70. package/{chunk-OZKJBDG4.js → chunk-YBV6QJKM.js} +14 -8
  71. package/{codingSkills-BYQI6EPT.js → codingSkills-BCQXRZOI.js} +3 -4
  72. package/{createAgentAutomationAction-5OZIWOK4.js → createAgentAutomationAction-UJLNFFFC.js} +17 -17
  73. package/{createDialogAction-334CHPJF.js → createDialogAction-TJCX6GUE.js} +20 -21
  74. package/{createPageAction-LXY22WTA.js → createPageAction-N3IYABHV.js} +18 -19
  75. package/{dbSlice-FWF5F4L7.js → dbSlice-D5CNGWPD.js} +5 -5
  76. package/{deleteFile-NPZ64DFN.js → deleteFile-44TUM34Y.js} +3 -3
  77. package/{dialogCommands-4MGUHAJI.js → dialogCommands-35M6RBAC.js} +18 -18
  78. package/{dialogSlice-76W7S3C4.js → dialogSlice-KO7ADD7H.js} +16 -16
  79. package/{docCreateCommands-346W5443.js → docCreateCommands-VXHZWQ3P.js} +20 -21
  80. package/{docDeleteCommands-FKEEPUUS.js → docDeleteCommands-4WFAJR6A.js} +20 -21
  81. package/{docReadCommands-AACI4DVO.js → docReadCommands-OYREEFAR.js} +20 -21
  82. package/{docStore-N7QTPHAX.js → docStore-5BU76DFK.js} +18 -18
  83. package/{docUpdateCommands-WHWVZ2XE.js → docUpdateCommands-A7QERSME.js} +20 -21
  84. package/{getPricing-JNOCWYDH.js → getPricing-HAU62AFM.js} +3 -3
  85. package/{handleSendMessageAction-EAXEX5AX.js → handleSendMessageAction-OSGQJQ45.js} +16 -16
  86. package/index.js +58 -58
  87. package/{machineCommands-XNWLQVTN.js → machineCommands-J43U3TX5.js} +38 -39
  88. package/{machineWsRunDispatch-56HWQ67L.js → machineWsRunDispatch-Z2Z6STXZ.js} +38 -39
  89. package/{models-N4NXQ44D.js → models-4QF4BJVY.js} +2 -2
  90. package/package.json +2 -3
  91. package/{read-J7RVVCHH.js → read-RGNPI5H2.js} +3 -3
  92. package/{readDocTool-G2WBM4IH.js → readDocTool-6ESWKTXD.js} +1 -1
  93. package/{readlineWorkspace-AXTB7DJC.js → readlineWorkspace-YRPV6JPN.js} +485 -222
  94. package/{removeDialogAgentAction-RMPISD42.js → removeDialogAgentAction-JYT73FU2.js} +7 -7
  95. package/{runAgentClientLoop-PU3R3UXA.js → runAgentClientLoop-5YM5WMAY.js} +32 -33
  96. package/{setDialogExtraReferencesAction-7BUTHDAL.js → setDialogExtraReferencesAction-D564LMYQ.js} +6 -6
  97. package/{setPrimaryDialogAgentAction-WCYIBGZ4.js → setPrimaryDialogAgentAction-ACY3BLYA.js} +7 -7
  98. package/{settingSlice-7NN2MI6Y.js → settingSlice-MKI3MNZJ.js} +6 -6
  99. package/{skillDiscovery-COX56OBO.js → skillDiscovery-GPUDHDTZ.js} +1 -2
  100. package/{spaceCommands-JYI3X4N2.js → spaceCommands-MY26V52L.js} +17 -17
  101. package/{spaceSlice-ERZ3RGWC.js → spaceSlice-JUIXC365.js} +17 -17
  102. package/{streamAgentChatTurn-2IH4MTQC.js → streamAgentChatTurn-XV5X5TNQ.js} +36 -37
  103. package/{switchDialogAgentAction-WGMMQHDI.js → switchDialogAgentAction-CJ5H6SAR.js} +5 -5
  104. package/{tableCommands-P2TXJ3CF.js → tableCommands-R6WFRDYF.js} +1 -1
  105. package/{tools-DJUTQEMP.js → tools-MIMELA2Y.js} +27 -28
  106. package/{tuiSummaryLlmCaller-U4EGD2SX.js → tuiSummaryLlmCaller-XJ5BKNNS.js} +12 -12
  107. package/{updateDialogTitleAction-OVOPGTC3.js → updateDialogTitleAction-EIZUSWHR.js} +17 -17
  108. package/chunk-AETZ7AD6.js +0 -274
  109. package/chunk-EUG45QJR.js +0 -6104
  110. package/chunk-OFZPJ3NT.js +0 -114
@@ -3,7 +3,7 @@ import {
3
3
  normalizeDialogRecord,
4
4
  readDialogSnapshot,
5
5
  sortDialogs
6
- } from "./chunk-EUG45QJR.js";
6
+ } from "./chunk-GLGD2JGY.js";
7
7
  import {
8
8
  ANSI_ESCAPE_REGEX,
9
9
  COLLAPSED_PASTE_PLACEHOLDER_RE,
@@ -25,7 +25,6 @@ import {
25
25
  fitAnsiLine,
26
26
  formatAssistantDisplay,
27
27
  formatTokenCount,
28
- getActiveDensity,
29
28
  getActiveThemeMode,
30
29
  getActiveThemeName,
31
30
  isBalanceExhaustedError,
@@ -46,7 +45,6 @@ import {
46
45
  resolveTuiThemeMode,
47
46
  runAgentTurn,
48
47
  setActiveBrightness,
49
- setActiveDensity,
50
48
  setActiveThemeMode,
51
49
  setActiveThemeName,
52
50
  setMathRenderingEnabled,
@@ -64,14 +62,14 @@ import {
64
62
  wrapTextToLines,
65
63
  wrapTranscriptLine,
66
64
  wrapTranscriptLineWithLayout
67
- } from "./chunk-67Q6IHM5.js";
65
+ } from "./chunk-6FQ2552G.js";
68
66
  import {
69
67
  listFavoriteAgentIdsAcrossServers,
70
68
  listLocalCachedAgents,
71
69
  listRemoteAgents,
72
70
  listRemoteAgentsAcrossServers,
73
71
  normalizeListedAgent
74
- } from "./chunk-C2WF2OY7.js";
72
+ } from "./chunk-5WADL4O7.js";
75
73
  import "./chunk-ID4G2MAR.js";
76
74
  import {
77
75
  listUserRecordsFromServers,
@@ -86,8 +84,8 @@ import {
86
84
  readTimestamp,
87
85
  setCliLocale,
88
86
  t
89
- } from "./chunk-POLE2QLO.js";
90
- import "./chunk-3YVCM5PE.js";
87
+ } from "./chunk-GV35EHNA.js";
88
+ import "./chunk-U5Z7LFZF.js";
91
89
  import "./chunk-EADNWBAZ.js";
92
90
  import "./chunk-NI2BS6UD.js";
93
91
  import {
@@ -113,21 +111,21 @@ import {
113
111
  import "./chunk-ESHYE7WU.js";
114
112
  import "./chunk-JBTJAPUF.js";
115
113
  import "./chunk-LTRXGFMS.js";
116
- import "./chunk-RO2IUWZE.js";
114
+ import "./chunk-4NN3EK7H.js";
117
115
  import "./chunk-ATNTHIZI.js";
118
116
  import "./chunk-JEXVIMYZ.js";
119
117
  import {
120
118
  prepareTools
121
- } from "./chunk-H5QLIP6S.js";
119
+ } from "./chunk-UW4INMD2.js";
122
120
  import "./chunk-HS7MLR74.js";
123
- import "./chunk-4ZL2KBI7.js";
124
- import "./chunk-5SLTA66C.js";
121
+ import "./chunk-757BGY6Y.js";
122
+ import "./chunk-V4KZGLOI.js";
125
123
  import "./chunk-5XTOKQLW.js";
126
124
  import "./chunk-NF64D32W.js";
127
125
  import "./chunk-3L2WKC3W.js";
128
- import "./chunk-Z7A54NFZ.js";
126
+ import "./chunk-RU5D3B22.js";
129
127
  import "./chunk-KQHGZWSV.js";
130
- import "./chunk-5XCW6RQG.js";
128
+ import "./chunk-VRZIH5JO.js";
131
129
  import {
132
130
  clipText,
133
131
  formatDuration,
@@ -140,9 +138,9 @@ import {
140
138
  shortRunId
141
139
  } from "./chunk-KXKQO4Q6.js";
142
140
  import "./chunk-INZZJSQF.js";
143
- import "./chunk-PJASTA2U.js";
141
+ import "./chunk-Y7LNKP32.js";
144
142
  import "./chunk-BANYWEYY.js";
145
- import "./chunk-HCBQVVFZ.js";
143
+ import "./chunk-PAHILFNT.js";
146
144
  import "./chunk-MEYIRKDN.js";
147
145
  import "./chunk-63W5N6P3.js";
148
146
  import "./chunk-VF4K2QE6.js";
@@ -151,17 +149,17 @@ import {
151
149
  createTurnRequest,
152
150
  normalizeRunCompletionShape
153
151
  } from "./chunk-YPREESF4.js";
154
- import "./chunk-XPW6JLOB.js";
155
- import "./chunk-DFY6O64O.js";
152
+ import "./chunk-6SZ52ORL.js";
153
+ import "./chunk-LA4IDAK7.js";
156
154
  import {
157
155
  extractReferenceKeysFromMessage
158
- } from "./chunk-PGKZ2KGE.js";
156
+ } from "./chunk-BJLYCOZK.js";
159
157
  import "./chunk-EVBFYPDC.js";
160
158
  import "./chunk-H2G6JXTR.js";
161
159
  import "./chunk-YYLHWIW6.js";
162
- import "./chunk-KOZ2G3ZG.js";
160
+ import "./chunk-U2A47SVI.js";
163
161
  import "./chunk-AGGIDPCZ.js";
164
- import "./chunk-SG65C33A.js";
162
+ import "./chunk-32I5RVMY.js";
165
163
  import "./chunk-5RGU3J7P.js";
166
164
  import "./chunk-M76ECQRD.js";
167
165
  import "./chunk-MRWGHF56.js";
@@ -171,22 +169,21 @@ import "./chunk-GLKOYSRU.js";
171
169
  import {
172
170
  buildSkillDiscoveryContextBlock,
173
171
  buildSkillDiscoveryContextLayer
174
- } from "./chunk-OFZPJ3NT.js";
175
- import "./chunk-V2IPBG5U.js";
172
+ } from "./chunk-2BWU4ORW.js";
173
+ import "./chunk-AA564GBL.js";
176
174
  import {
177
175
  expandEnabledPacks
178
- } from "./chunk-LGUQHYAL.js";
179
- import "./chunk-AETZ7AD6.js";
180
- import "./chunk-KHCLMKZZ.js";
176
+ } from "./chunk-O5FLGN2L.js";
177
+ import "./chunk-EY7V7REW.js";
181
178
  import {
182
179
  buildLocalWorkspaceOpenAiTools,
183
180
  buildLocalWorkspaceToolset,
184
181
  getProcessRegistry,
185
182
  readCommandActionGatePayload
186
- } from "./chunk-QTH72J3N.js";
187
- import "./chunk-XB7ACJFH.js";
183
+ } from "./chunk-6BGAQ3KR.js";
184
+ import "./chunk-INXBNYCG.js";
188
185
  import "./chunk-ZNFJWEN4.js";
189
- import "./chunk-VKSBVRGP.js";
186
+ import "./chunk-CVT7L2WZ.js";
190
187
  import "./chunk-T27IEBNQ.js";
191
188
  import "./chunk-N7J63Q6C.js";
192
189
  import {
@@ -194,11 +191,11 @@ import {
194
191
  buildIdentityBlock,
195
192
  buildRuntimeGuidanceBlocks,
196
193
  resolveToolGuidedSections
197
- } from "./chunk-OZKJBDG4.js";
194
+ } from "./chunk-YBV6QJKM.js";
198
195
  import "./chunk-6BYAXNH5.js";
199
196
  import {
200
197
  resolvePlatformAuthToken
201
- } from "./chunk-JBL24PK6.js";
198
+ } from "./chunk-SP3DPVBH.js";
202
199
  import "./chunk-TNPCQNGQ.js";
203
200
  import {
204
201
  isQuotaExhaustedError
@@ -207,7 +204,7 @@ import "./chunk-2MBTWINN.js";
207
204
  import {
208
205
  DEFAULT_CONTEXT_WINDOW,
209
206
  getModelContextWindow
210
- } from "./chunk-F22JQTSX.js";
207
+ } from "./chunk-KUUTWU4I.js";
211
208
  import {
212
209
  buildCompactionMetricsFromPlan,
213
210
  buildCompactionUserContent,
@@ -218,11 +215,11 @@ import {
218
215
  formatMessagesForSummaryWithTruncation,
219
216
  planCompression,
220
217
  serializeMessageContent
221
- } from "./chunk-HR5XDXYM.js";
222
- import "./chunk-AV4Z57LV.js";
218
+ } from "./chunk-H4RYESI6.js";
219
+ import "./chunk-VRJUIUIY.js";
223
220
  import "./chunk-NQ4N4IGO.js";
224
221
  import "./chunk-FD36SCCD.js";
225
- import "./chunk-BO3YFOZR.js";
222
+ import "./chunk-QDHZCAH3.js";
226
223
  import {
227
224
  compactWhitespace
228
225
  } from "./chunk-DWLEOOGR.js";
@@ -268,19 +265,19 @@ import {
268
265
  import {
269
266
  DEFAULT_NOLO_SERVER_URL
270
267
  } from "./chunk-OLZBQWZU.js";
271
- import "./chunk-Y7TGWSUN.js";
272
- import "./chunk-VFN256RI.js";
273
- import "./chunk-SQTVMYMU.js";
274
- import "./chunk-X7S7OFUX.js";
268
+ import "./chunk-56W233KG.js";
269
+ import "./chunk-WL3UOGYO.js";
270
+ import "./chunk-7APQB6R6.js";
271
+ import "./chunk-3QJTD6DO.js";
275
272
  import "./chunk-Y5GCAMGP.js";
276
273
  import "./chunk-UZ6XXCVR.js";
277
274
  import "./chunk-KX6YBIHG.js";
278
- import "./chunk-4RXXP6WD.js";
279
- import "./chunk-76UA7376.js";
275
+ import "./chunk-FGEYVPPR.js";
276
+ import "./chunk-AV6KQCM5.js";
280
277
  import "./chunk-SVEUG45B.js";
281
278
  import "./chunk-WWC7I6TA.js";
282
279
  import "./chunk-I42VC5KR.js";
283
- import "./chunk-YZ2WE6CW.js";
280
+ import "./chunk-MFI4FGHX.js";
284
281
  import {
285
282
  asTrimmedLowercaseString
286
283
  } from "./chunk-QSJ3HFEF.js";
@@ -289,13 +286,13 @@ import {
289
286
  BUILTIN_NOLO_AGENT_MODEL,
290
287
  BUILTIN_NOLO_AGENT_NAME,
291
288
  builtinAgentCatalogEntryById
292
- } from "./chunk-2JYSSXLY.js";
289
+ } from "./chunk-VULELK4T.js";
293
290
  import {
294
291
  extractCustomId,
295
292
  parsePublicAgentId
296
293
  } from "./chunk-BJIRLHJR.js";
297
- import "./chunk-HHBXCRJS.js";
298
- import "./chunk-L3JWI7WX.js";
294
+ import "./chunk-PYKV6FSC.js";
295
+ import "./chunk-QOKAKDPE.js";
299
296
  import "./chunk-OW765XBU.js";
300
297
  import "./chunk-GBRG75BQ.js";
301
298
  import "./chunk-KUG52IAP.js";
@@ -624,9 +621,9 @@ function createRawKeyReader(input) {
624
621
  };
625
622
  const resolveWaiter = (value) => {
626
623
  if (!waiter) return;
627
- const resolve2 = waiter;
624
+ const resolve4 = waiter;
628
625
  waiter = null;
629
- resolve2(value);
626
+ resolve4(value);
630
627
  };
631
628
  const tryDeliver = () => {
632
629
  if (!waiter) return;
@@ -683,12 +680,12 @@ function createRawKeyReader(input) {
683
680
  input.on("close", onClose);
684
681
  input.on("end", onClose);
685
682
  input.resume?.();
686
- const reader = () => new Promise((resolve2) => {
683
+ const reader = () => new Promise((resolve4) => {
687
684
  if (closed) {
688
- resolve2(drainClosed());
685
+ resolve4(drainClosed());
689
686
  return;
690
687
  }
691
- waiter = resolve2;
688
+ waiter = resolve4;
692
689
  tryDeliver();
693
690
  });
694
691
  reader.dispose = () => {
@@ -1385,9 +1382,9 @@ function layoutTurnRows(role, content, contentWidth, colorEnabled, command) {
1385
1382
  }
1386
1383
  return rows.length > 0 ? rows : [{ rendered: "", sourceStart: 0, sourceEnd: 0, prefixWidth: 0 }];
1387
1384
  }
1388
- function getTurnLayoutRows(turn, contentWidth, colorEnabled, density, themeFingerprint) {
1385
+ function getTurnLayoutRows(turn, contentWidth, colorEnabled, themeFingerprint) {
1389
1386
  const cached = getCachedTurnLine(turn);
1390
- if (cached && cached.width === contentWidth && cached.color === colorEnabled && cached.density === density && cached.themeFingerprint === themeFingerprint) {
1387
+ if (cached && cached.width === contentWidth && cached.color === colorEnabled && cached.themeFingerprint === themeFingerprint) {
1391
1388
  return cached.layoutRows;
1392
1389
  }
1393
1390
  const layoutRows = layoutTurnRows(turn.role, turn.content, contentWidth, colorEnabled, turn.command);
@@ -1395,7 +1392,6 @@ function getTurnLayoutRows(turn, contentWidth, colorEnabled, density, themeFinge
1395
1392
  setCachedTurnLine(turn, {
1396
1393
  width: contentWidth,
1397
1394
  color: colorEnabled,
1398
- density,
1399
1395
  themeFingerprint,
1400
1396
  lines,
1401
1397
  layoutRows
@@ -1450,8 +1446,8 @@ function renderTailTurnBlock(role, content, contentWidth, colorEnabled) {
1450
1446
  return lines;
1451
1447
  }
1452
1448
  }
1453
- function getStreamingTurnLines(role, content, contentWidth, colorEnabled, density, themeFingerprint) {
1454
- if (streamingTurnCache && (streamingTurnCache.role !== role || streamingTurnCache.contentWidth !== contentWidth || streamingTurnCache.colorEnabled !== colorEnabled || streamingTurnCache.density !== density || streamingTurnCache.themeFingerprint !== themeFingerprint || !content.startsWith(streamingTurnCache.fullContent.slice(0, streamingTurnCache.prefixLength)))) {
1449
+ function getStreamingTurnLines(role, content, contentWidth, colorEnabled, themeFingerprint) {
1450
+ if (streamingTurnCache && (streamingTurnCache.role !== role || streamingTurnCache.contentWidth !== contentWidth || streamingTurnCache.colorEnabled !== colorEnabled || streamingTurnCache.themeFingerprint !== themeFingerprint || !content.startsWith(streamingTurnCache.fullContent.slice(0, streamingTurnCache.prefixLength)))) {
1455
1451
  streamingTurnCache = null;
1456
1452
  }
1457
1453
  if (!streamingTurnCache) {
@@ -1459,7 +1455,6 @@ function getStreamingTurnLines(role, content, contentWidth, colorEnabled, densit
1459
1455
  role,
1460
1456
  contentWidth,
1461
1457
  colorEnabled,
1462
- density,
1463
1458
  themeFingerprint,
1464
1459
  fullContent: content,
1465
1460
  prefixLength: 0,
@@ -1513,12 +1508,11 @@ function countTurnLines(role, content, contentWidth, command) {
1513
1508
  return layoutTurnRows(role, content, contentWidth, false, command).length;
1514
1509
  }
1515
1510
  function buildTurnOffsets(history, contentWidth) {
1516
- const density = getActiveDensity();
1517
1511
  const entries = [];
1518
1512
  let offset = 0;
1519
1513
  for (let i = 0; i < history.turns.length; i++) {
1520
1514
  const turn = history.turns[i];
1521
- const separatorAbove = density === "spacious" && (i > 0 || turn.role === "user") ? 1 : 0;
1515
+ const separatorAbove = i > 0 && turn.role === "user" ? 1 : 0;
1522
1516
  offset += separatorAbove;
1523
1517
  const cacheMap = turnLineCountCache.get(turn);
1524
1518
  let lineCount;
@@ -1547,7 +1541,6 @@ function getAllTurnEntries(history, contentWidth) {
1547
1541
  const { entries, totalLines: finalizedLines } = buildTurnOffsets(history, contentWidth);
1548
1542
  const turns = [...history.turns];
1549
1543
  if (history.currentRole !== null && history.currentContent) {
1550
- const density = getActiveDensity();
1551
1544
  const colorEnabled = resolveCliColorEnabled();
1552
1545
  const themeFingerprint = tuiRenderThemeFingerprint(colorEnabled);
1553
1546
  const streamingLines = getStreamingTurnLines(
@@ -1555,10 +1548,9 @@ function getAllTurnEntries(history, contentWidth) {
1555
1548
  history.currentContent,
1556
1549
  contentWidth,
1557
1550
  colorEnabled,
1558
- density,
1559
1551
  themeFingerprint
1560
1552
  );
1561
- const sep = currentTurnSeparator(history, density);
1553
+ const sep = currentTurnSeparator(history);
1562
1554
  const startRow = finalizedLines + sep;
1563
1555
  entries.push({
1564
1556
  startRow,
@@ -1573,12 +1565,11 @@ function getAllTurnEntries(history, contentWidth) {
1573
1565
  }
1574
1566
  return { entries, totalLines: finalizedLines, turns };
1575
1567
  }
1576
- function currentTurnSeparator(history, density) {
1577
- return density === "spacious" && (history.turns.length > 0 || history.currentRole === "user") ? 1 : 0;
1568
+ function currentTurnSeparator(history) {
1569
+ return history.turns.length > 0 && history.currentRole === "user" ? 1 : 0;
1578
1570
  }
1579
1571
  function buildHistoryLayoutRows(history, contentWidth) {
1580
1572
  const colorEnabled = resolveCliColorEnabled();
1581
- const density = getActiveDensity();
1582
1573
  const themeFingerprint = tuiRenderThemeFingerprint(colorEnabled);
1583
1574
  const rows = [];
1584
1575
  const separatorRow = () => ({
@@ -1589,20 +1580,19 @@ function buildHistoryLayoutRows(history, contentWidth) {
1589
1580
  });
1590
1581
  for (let i = 0; i < history.turns.length; i++) {
1591
1582
  const turn = history.turns[i];
1592
- if (density === "spacious" && (i > 0 || turn.role === "user")) {
1583
+ if (i > 0 && turn.role === "user") {
1593
1584
  rows.push(separatorRow());
1594
1585
  }
1595
1586
  rows.push(...getTurnLayoutRows(
1596
1587
  turn,
1597
1588
  contentWidth,
1598
1589
  colorEnabled,
1599
- density,
1600
1590
  themeFingerprint
1601
1591
  ));
1602
1592
  }
1603
1593
  if (history.currentRole !== null && history.currentContent) {
1604
1594
  const i = history.turns.length;
1605
- if (density === "spacious" && (i > 0 || history.currentRole === "user")) {
1595
+ if (i > 0 && history.currentRole === "user") {
1606
1596
  rows.push(separatorRow());
1607
1597
  }
1608
1598
  rows.push(
@@ -1625,7 +1615,6 @@ function renderHistory(output, history, inputLines, selection) {
1625
1615
  const visibleHeight = Math.max(1, rows - inputLines);
1626
1616
  const contentWidth = Math.max(1, columns - 1);
1627
1617
  const colorEnabled = resolveCliColorEnabled();
1628
- const density = getActiveDensity();
1629
1618
  const themeFingerprint = tuiRenderThemeFingerprint(colorEnabled);
1630
1619
  const { entries, totalLines: finalizedLines } = buildTurnOffsets(history, contentWidth);
1631
1620
  let currentLines = [];
@@ -1636,10 +1625,9 @@ function renderHistory(output, history, inputLines, selection) {
1636
1625
  history.currentContent,
1637
1626
  contentWidth,
1638
1627
  colorEnabled,
1639
- density,
1640
1628
  themeFingerprint
1641
1629
  );
1642
- const sep = currentTurnSeparator(history, density);
1630
+ const sep = currentTurnSeparator(history);
1643
1631
  currentStart = finalizedLines + sep;
1644
1632
  }
1645
1633
  const totalLines = currentStart >= 0 ? currentStart + currentLines.length : finalizedLines;
@@ -1670,7 +1658,7 @@ function renderHistory(output, history, inputLines, selection) {
1670
1658
  const turn = history.turns[i];
1671
1659
  const cached = getCachedTurnLine(turn);
1672
1660
  let layoutRows;
1673
- if (cached && cached.width === contentWidth && cached.color === colorEnabled && cached.density === density && cached.themeFingerprint === themeFingerprint) {
1661
+ if (cached && cached.width === contentWidth && cached.color === colorEnabled && cached.themeFingerprint === themeFingerprint) {
1674
1662
  layoutRows = cached.layoutRows;
1675
1663
  } else {
1676
1664
  renderCacheMissCount += 1;
@@ -1678,7 +1666,6 @@ function renderHistory(output, history, inputLines, selection) {
1678
1666
  turn,
1679
1667
  contentWidth,
1680
1668
  colorEnabled,
1681
- density,
1682
1669
  themeFingerprint
1683
1670
  );
1684
1671
  }
@@ -1690,7 +1677,7 @@ function renderHistory(output, history, inputLines, selection) {
1690
1677
  }
1691
1678
  }
1692
1679
  if (currentStart >= 0) {
1693
- const separatorAbove = currentTurnSeparator(history, density);
1680
+ const separatorAbove = currentTurnSeparator(history);
1694
1681
  if (separatorAbove > 0) {
1695
1682
  const sepRow = currentStart - 1;
1696
1683
  if (sepRow >= winStart && sepRow < winEnd) {
@@ -1774,7 +1761,7 @@ function applyScrollAction(history, action, output, inputLines) {
1774
1761
  const { totalLines: finalizedLines } = buildTurnOffsets(history, contentWidth);
1775
1762
  let totalLines = finalizedLines;
1776
1763
  if (history.currentRole !== null && history.currentContent) {
1777
- totalLines += currentTurnSeparator(history, getActiveDensity()) + countTurnLines(history.currentRole, history.currentContent, contentWidth);
1764
+ totalLines += currentTurnSeparator(history) + countTurnLines(history.currentRole, history.currentContent, contentWidth);
1778
1765
  }
1779
1766
  const maxScrollTop = Math.max(0, totalLines - visibleHeight);
1780
1767
  history.followBottom = false;
@@ -1863,6 +1850,18 @@ function formatTerminalRunAge(snapshot, now) {
1863
1850
  function formatUnassignedFact(snapshot) {
1864
1851
  return snapshot.unassigned ? "parent: ?" : null;
1865
1852
  }
1853
+ function sanitizeRunSnapshotForNormal(snapshot) {
1854
+ const clean = (value) => stripAnsi(value).replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
1855
+ return {
1856
+ ...snapshot,
1857
+ agentName: snapshot.agentName ? clean(snapshot.agentName) : snapshot.agentName,
1858
+ taskPreview: snapshot.taskPreview ? clean(snapshot.taskPreview) : snapshot.taskPreview,
1859
+ lastAssistantText: snapshot.lastAssistantText ? clean(snapshot.lastAssistantText) : snapshot.lastAssistantText,
1860
+ lastToolNames: snapshot.lastToolNames?.map(clean).filter(Boolean),
1861
+ errorMessage: void 0,
1862
+ logLines: void 0
1863
+ };
1864
+ }
1866
1865
 
1867
1866
  // packages/cli/tui/agentRunPanelLines.ts
1868
1867
  function formatPanelProgress(snapshot, now) {
@@ -2037,11 +2036,13 @@ function createRunDock(deps) {
2037
2036
  const ordered = orderedEntries();
2038
2037
  if (ordered.length === 0) return [];
2039
2038
  const at = now();
2040
- if (ordered.length === 1) {
2041
- return formatAgentRunPanelLines(ordered[0].snapshot, colorEnabled, at);
2039
+ const mode = typeof deps.displayMode === "function" ? deps.displayMode() : deps.displayMode;
2040
+ const displaySnapshots = mode === "normal" || mode === "hide" ? ordered.map((entry) => sanitizeRunSnapshotForNormal(entry.snapshot)) : ordered.map((entry) => entry.snapshot);
2041
+ if (displaySnapshots.length === 1) {
2042
+ return formatAgentRunPanelLines(displaySnapshots[0], colorEnabled, at);
2042
2043
  }
2043
2044
  return formatRunDockLines(
2044
- ordered.map((entry) => entry.snapshot),
2045
+ displaySnapshots,
2045
2046
  colorEnabled,
2046
2047
  at,
2047
2048
  maxRows
@@ -2210,7 +2211,8 @@ function createActivityIndicator(deps) {
2210
2211
  onRepaint: deps.onRepaint,
2211
2212
  now,
2212
2213
  setIntervalFn,
2213
- clearIntervalFn
2214
+ clearIntervalFn,
2215
+ displayMode: deps.displayMode
2214
2216
  });
2215
2217
  const updateAgentRun = (snapshot) => {
2216
2218
  runDock.update(snapshot);
@@ -3312,6 +3314,20 @@ function summarizeAttachment(img) {
3312
3314
  ].join("\n");
3313
3315
  return themeText(text, "chrome", colorEnabled);
3314
3316
  }
3317
+ function formatComposerAttachmentLine(images) {
3318
+ if (images.length === 0) return null;
3319
+ const head = images.slice(0, 2).map((img) => `\u{1F4CE} ${img.filename} (${formatBytes(img.sizeBytes)})`);
3320
+ const extra = images.length - head.length;
3321
+ return extra > 0 ? `${head.join(", ")} +${extra}` : head.join(", ");
3322
+ }
3323
+ function popLastAttachedImage(images) {
3324
+ if (images.length === 0) return { handled: false, images };
3325
+ return {
3326
+ handled: true,
3327
+ images: images.slice(0, -1),
3328
+ removed: images[images.length - 1]
3329
+ };
3330
+ }
3315
3331
  function mergeAttachedImages(existing, incoming) {
3316
3332
  if (incoming.length === 0) return existing;
3317
3333
  if (existing.length === 0) return [...incoming];
@@ -3333,12 +3349,12 @@ function mergeAttachedImages(existing, incoming) {
3333
3349
  return out;
3334
3350
  }
3335
3351
  async function readImagePaths(paths, options = {}) {
3336
- const resolve2 = options.resolve ?? ((p) => p);
3352
+ const resolve4 = options.resolve ?? ((p) => p);
3337
3353
  const reportedFailures = /* @__PURE__ */ new Set();
3338
3354
  const images = [];
3339
3355
  const failures = [];
3340
3356
  for (const raw of paths) {
3341
- const absolute = resolve2(raw);
3357
+ const absolute = resolve4(raw);
3342
3358
  try {
3343
3359
  const img = await readImageAsDataUrl(absolute, {
3344
3360
  ...options.maxBytes !== void 0 ? { maxBytes: options.maxBytes } : {}
@@ -3378,7 +3394,7 @@ async function resolveAttachmentImageUrls({
3378
3394
  // packages/cli/tui/clipboardImage.ts
3379
3395
  import { createHash } from "node:crypto";
3380
3396
  import { mkdirSync } from "node:fs";
3381
- import { writeFile } from "node:fs/promises";
3397
+ import { readdir, stat as stat2, unlink, writeFile } from "node:fs/promises";
3382
3398
  import { tmpdir } from "node:os";
3383
3399
  import { join } from "node:path";
3384
3400
  var ClipboardImageError = class extends Error {
@@ -3390,6 +3406,34 @@ var ClipboardImageError = class extends Error {
3390
3406
  }
3391
3407
  };
3392
3408
  var DEFAULT_CLIPBOARD_TIMEOUT_MS = 3e4;
3409
+ var CLIPBOARD_TEMP_DIR_NAME = "nolo-clipboard";
3410
+ function getDefaultClipboardTempDir() {
3411
+ return join(tmpdir(), CLIPBOARD_TEMP_DIR_NAME);
3412
+ }
3413
+ var CLIPBOARD_SWEEP_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
3414
+ var CLIPBOARD_SWEEP_FILE_RE = /^clip-[0-9a-f]{8,}\.(png|jpg|jpeg|gif|webp)$/;
3415
+ async function sweepStaleClipboardFiles(tempDir, opts = {}) {
3416
+ try {
3417
+ const maxAgeMs = opts.maxAgeMs ?? CLIPBOARD_SWEEP_MAX_AGE_MS;
3418
+ const now = opts.now ?? Date.now();
3419
+ const entries = await readdir(tempDir);
3420
+ let removed = 0;
3421
+ for (const name of entries) {
3422
+ if (!CLIPBOARD_SWEEP_FILE_RE.test(name)) continue;
3423
+ try {
3424
+ const fullPath = join(tempDir, name);
3425
+ const stats = await stat2(fullPath);
3426
+ if (stats.mtimeMs >= now - maxAgeMs) continue;
3427
+ await unlink(fullPath);
3428
+ removed += 1;
3429
+ } catch {
3430
+ }
3431
+ }
3432
+ return removed;
3433
+ } catch {
3434
+ return 0;
3435
+ }
3436
+ }
3393
3437
  function isRemoteSession(env = process.env) {
3394
3438
  return Boolean(env.SSH_CONNECTION || env.SSH_TTY || env.SSH_CLIENT);
3395
3439
  }
@@ -3405,13 +3449,20 @@ function getClipboardImageCommand(deps = {}) {
3405
3449
  }
3406
3450
  if (platform === "darwin") {
3407
3451
  const pngpaste = which("pngpaste");
3408
- if (!pngpaste) {
3452
+ if (pngpaste) {
3453
+ return { command: [pngpaste, "-"], outputFormat: "raw" };
3454
+ }
3455
+ const osascript = which("osascript");
3456
+ if (!osascript) {
3409
3457
  throw new ClipboardImageError(
3410
3458
  "binary-missing",
3411
- "\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"
3459
+ "\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"
3412
3460
  );
3413
3461
  }
3414
- return { command: [pngpaste, "-"] };
3462
+ return {
3463
+ command: [osascript, "-e", "the clipboard as \xABclass PNGf\xBB"],
3464
+ outputFormat: "osascript-hex"
3465
+ };
3415
3466
  }
3416
3467
  if (platform === "win32") {
3417
3468
  const ps = which("powershell.exe") || which("powershell") || which("pwsh.exe") || which("pwsh");
@@ -3428,7 +3479,8 @@ function getClipboardImageCommand(deps = {}) {
3428
3479
  "-NonInteractive",
3429
3480
  "-Command",
3430
3481
  "$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) }"
3431
- ]
3482
+ ],
3483
+ outputFormat: "raw"
3432
3484
  };
3433
3485
  }
3434
3486
  if (platform === "linux") {
@@ -3441,17 +3493,18 @@ function getClipboardImageCommand(deps = {}) {
3441
3493
  "\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"
3442
3494
  );
3443
3495
  }
3444
- return { command: [wlPaste2, "--type", "image/png"] };
3496
+ return { command: [wlPaste2, "--type", "image/png"], outputFormat: "raw" };
3445
3497
  }
3446
3498
  const xclip = which("xclip");
3447
3499
  if (xclip) {
3448
3500
  return {
3449
- command: [xclip, "-selection", "clipboard", "-t", "image/png", "-o"]
3501
+ command: [xclip, "-selection", "clipboard", "-t", "image/png", "-o"],
3502
+ outputFormat: "raw"
3450
3503
  };
3451
3504
  }
3452
3505
  const wlPaste = which("wl-paste");
3453
3506
  if (wlPaste) {
3454
- return { command: [wlPaste, "--type", "image/png"] };
3507
+ return { command: [wlPaste, "--type", "image/png"], outputFormat: "raw" };
3455
3508
  }
3456
3509
  throw new ClipboardImageError(
3457
3510
  "binary-missing",
@@ -3463,6 +3516,21 @@ function getClipboardImageCommand(deps = {}) {
3463
3516
  `\u5F53\u524D\u64CD\u4F5C\u7CFB\u7EDF\u6682\u4E0D\u652F\u6301\u526A\u8D34\u677F\u56FE\u50CF\u8BFB\u53D6: ${platform}`
3464
3517
  );
3465
3518
  }
3519
+ 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";
3520
+ function decodeOsascriptPngHex(text) {
3521
+ const match = /PNGf\s*([0-9A-Fa-f]*)/.exec(text);
3522
+ if (!match) return { kind: "empty" };
3523
+ const hex = match[1] ?? "";
3524
+ const tail = text.slice(match.index + match[0].length).replace(/[\s»]*$/, "");
3525
+ if (tail.length > 0) return { kind: "invalid" };
3526
+ if (hex.length === 0) return { kind: "empty" };
3527
+ if (hex.length % 2 !== 0) return { kind: "invalid" };
3528
+ const bytes = new Uint8Array(hex.length / 2);
3529
+ for (let i = 0; i < bytes.length; i += 1) {
3530
+ bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
3531
+ }
3532
+ return { kind: "bytes", bytes };
3533
+ }
3466
3534
  async function readClipboardImage(deps = {}) {
3467
3535
  const env = deps.env ?? process.env;
3468
3536
  const platform = deps.platform ?? process.platform;
@@ -3470,8 +3538,8 @@ async function readClipboardImage(deps = {}) {
3470
3538
  const spawn = deps.spawn ?? Bun.spawn;
3471
3539
  const maxBytes = deps.maxBytes ?? DEFAULT_MAX_IMAGE_BYTES;
3472
3540
  const timeoutMs = deps.timeoutMs ?? DEFAULT_CLIPBOARD_TIMEOUT_MS;
3473
- const tempDir = deps.tempDir ?? join(tmpdir(), "nolo-clipboard");
3474
- const { command } = getClipboardImageCommand({
3541
+ const tempDir = deps.tempDir ?? getDefaultClipboardTempDir();
3542
+ const { command, outputFormat } = getClipboardImageCommand({
3475
3543
  env,
3476
3544
  platform,
3477
3545
  which
@@ -3505,6 +3573,12 @@ async function readClipboardImage(deps = {}) {
3505
3573
  timeoutPromise
3506
3574
  ]);
3507
3575
  if (exitCode !== 0 || stdoutArrayBuffer.byteLength === 0) {
3576
+ if (outputFormat === "osascript-hex") {
3577
+ throw new ClipboardImageError(
3578
+ "empty-clipboard",
3579
+ OSASCRIPT_EMPTY_CLIPBOARD_MESSAGE + (stderrText.trim() ? `: ${stderrText.trim()}` : "")
3580
+ );
3581
+ }
3508
3582
  throw new ClipboardImageError(
3509
3583
  "empty-clipboard",
3510
3584
  "\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()}` : "")
@@ -3523,6 +3597,23 @@ async function readClipboardImage(deps = {}) {
3523
3597
  `\u8BFB\u53D6\u526A\u8D34\u677F\u56FE\u50CF\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
3524
3598
  );
3525
3599
  }
3600
+ if (outputFormat === "osascript-hex") {
3601
+ const text = Buffer.from(stdoutBuf).toString("utf8");
3602
+ const decoded = decodeOsascriptPngHex(text);
3603
+ if (decoded.kind === "empty") {
3604
+ throw new ClipboardImageError(
3605
+ "empty-clipboard",
3606
+ OSASCRIPT_EMPTY_CLIPBOARD_MESSAGE
3607
+ );
3608
+ }
3609
+ if (decoded.kind === "invalid") {
3610
+ throw new ClipboardImageError(
3611
+ "read-failed",
3612
+ "\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"
3613
+ );
3614
+ }
3615
+ stdoutBuf = decoded.bytes;
3616
+ }
3526
3617
  const compressResult = await compressImage(stdoutBuf, "image/png");
3527
3618
  const finalBuf = compressResult.buffer;
3528
3619
  const finalMime = compressResult.mime;
@@ -3583,7 +3674,7 @@ function parseStatusOutput(branch, status) {
3583
3674
  return { branch, modified, untracked };
3584
3675
  }
3585
3676
  function execFileUtf8(file, args, cwd) {
3586
- return new Promise((resolve2, reject) => {
3677
+ return new Promise((resolve4, reject) => {
3587
3678
  execFile(
3588
3679
  file,
3589
3680
  args,
@@ -3593,7 +3684,7 @@ function execFileUtf8(file, args, cwd) {
3593
3684
  reject(err);
3594
3685
  return;
3595
3686
  }
3596
- resolve2(typeof stdout === "string" ? stdout : String(stdout ?? ""));
3687
+ resolve4(typeof stdout === "string" ? stdout : String(stdout ?? ""));
3597
3688
  }
3598
3689
  );
3599
3690
  });
@@ -3694,7 +3785,7 @@ function renderStatusLine(state, maxWidth) {
3694
3785
  if (state.autoConfirm === true) {
3695
3786
  emergency.push(themeText("\u23F5", "warning", colorEnabled));
3696
3787
  }
3697
- const runningTotal = runningTaskCount + runningAgentCount;
3788
+ const runningTotal = runningTaskCount;
3698
3789
  if (runningTotal > 0) {
3699
3790
  emergency.push(themeText(`\u2699${runningTotal}`, "info", colorEnabled));
3700
3791
  }
@@ -3908,6 +3999,9 @@ function renderKnownAgents(colorEnabled = resolveCliColorEnabled()) {
3908
3999
  }
3909
4000
 
3910
4001
  // packages/cli/tui/sessionInput.ts
4002
+ import { readdirSync, statSync } from "node:fs";
4003
+ import { homedir as homedir2 } from "node:os";
4004
+ import { resolve as resolve2 } from "node:path";
3911
4005
  var PASTE_TOKEN_PREFIX = "\0PASTE\0";
3912
4006
  function applyTuiInputKey(buffer, sequence, key = {}, cursorPos, options) {
3913
4007
  const seq = sequence ?? "";
@@ -4012,6 +4106,16 @@ function applyTuiInputKey(buffer, sequence, key = {}, cursorPos, options) {
4012
4106
  return { buffer, cursorPos: curPos };
4013
4107
  }
4014
4108
  if (seq === " " || key.name === "tab") {
4109
+ if (options?.cwd) {
4110
+ const pathCandidates = completeSlashCommand(buffer, options.cwd);
4111
+ if (pathCandidates.length === 1) {
4112
+ const [only] = pathCandidates;
4113
+ return { buffer: only, cursorPos: only.length };
4114
+ }
4115
+ if (pathCandidates.length > 1) {
4116
+ return { buffer, cursorPos: curPos };
4117
+ }
4118
+ }
4015
4119
  const completed = completeSlashPrefix(buffer) ?? buffer;
4016
4120
  return { buffer: completed, cursorPos: completed.length };
4017
4121
  }
@@ -4064,9 +4168,9 @@ function deleteBufferRange(buffer, start, end, pasteStore) {
4064
4168
  function releasePastesInRange(buffer, start, end, pasteStore) {
4065
4169
  if (!pasteStore || start >= end) return;
4066
4170
  const slice = buffer.slice(start, end);
4067
- const re = /\[paste #(\d+) · (\d+) lines\]/g;
4171
+ COLLAPSED_PASTE_PLACEHOLDER_RE.lastIndex = 0;
4068
4172
  let match;
4069
- while ((match = re.exec(slice)) !== null) {
4173
+ while ((match = COLLAPSED_PASTE_PLACEHOLDER_RE.exec(slice)) !== null) {
4070
4174
  releaseCollapsedPaste(pasteStore, Number(match[1]));
4071
4175
  }
4072
4176
  }
@@ -4120,12 +4224,10 @@ var SLASH_COMMANDS = [
4120
4224
  "/clear",
4121
4225
  "/compact",
4122
4226
  "/theme",
4123
- "/density",
4124
4227
  "/context",
4125
4228
  "/ctx",
4229
+ "/cd",
4126
4230
  "/runtime",
4127
- "/tools",
4128
- "/thinking",
4129
4231
  "/auto",
4130
4232
  "/switch",
4131
4233
  "/agent",
@@ -4163,12 +4265,49 @@ function completeSlashPrefix(buffer) {
4163
4265
  }
4164
4266
  return prefix.length > buffer.length ? prefix : null;
4165
4267
  }
4166
- function completeSlashCommand(buffer) {
4268
+ function completeSlashCommand(buffer, cwd) {
4167
4269
  if (!buffer.startsWith("/")) return [];
4168
4270
  const trimmed = buffer.trim();
4271
+ if (cwd && buffer.startsWith("/cd ")) {
4272
+ const prefix = buffer.slice("/cd ".length);
4273
+ return completeCdPathCandidates(prefix, cwd);
4274
+ }
4169
4275
  if (trimmed.includes(" ")) return [];
4170
4276
  return SLASH_COMMANDS.filter((cmd) => cmd.startsWith(trimmed) && cmd !== trimmed);
4171
4277
  }
4278
+ function completeCdPathCandidates(prefix, cwd) {
4279
+ const lastSlash = prefix.lastIndexOf("/");
4280
+ const head = lastSlash >= 0 ? prefix.slice(0, lastSlash + 1) : "";
4281
+ const baseName = lastSlash >= 0 ? prefix.slice(lastSlash + 1) : prefix;
4282
+ let dirPath;
4283
+ if (head === "~" || head === "~/" || head === "~\\") {
4284
+ dirPath = homedir2();
4285
+ } else if (head.startsWith("~/") || head.startsWith("~\\")) {
4286
+ dirPath = resolve2(homedir2(), head.slice(2));
4287
+ } else {
4288
+ dirPath = head.startsWith("/") ? head : resolve2(cwd, head);
4289
+ }
4290
+ let entries;
4291
+ try {
4292
+ entries = readdirSync(dirPath, { withFileTypes: true });
4293
+ } catch {
4294
+ return [];
4295
+ }
4296
+ const hiddenRequested = baseName.startsWith(".");
4297
+ const names = entries.filter((e) => {
4298
+ if (e.isDirectory()) return true;
4299
+ if (!e.isSymbolicLink()) return false;
4300
+ try {
4301
+ return statSync(resolve2(dirPath, e.name)).isDirectory();
4302
+ } catch {
4303
+ return false;
4304
+ }
4305
+ }).map((e) => e.name).filter((name) => {
4306
+ if (!name.startsWith(baseName)) return false;
4307
+ return hiddenRequested || !name.startsWith(".");
4308
+ }).sort();
4309
+ return names.map((name) => `/cd ${head}${name}/`);
4310
+ }
4172
4311
  function isLikelySlashCommand(input) {
4173
4312
  const trimmed = input.trim();
4174
4313
  if (!trimmed.startsWith("/")) return false;
@@ -4187,6 +4326,11 @@ function stripImageTokens(input, hints) {
4187
4326
  return compactWhitespace(out);
4188
4327
  }
4189
4328
 
4329
+ // packages/cli/tui/sessionDispatch.ts
4330
+ import { statSync as statSync2 } from "node:fs";
4331
+ import { homedir as homedir3 } from "node:os";
4332
+ import { resolve as resolve3 } from "node:path";
4333
+
4190
4334
  // packages/cli/tui/agentPicker.ts
4191
4335
  function toAgentPickerItems(entries) {
4192
4336
  return entries.map((entry) => ({
@@ -4348,6 +4492,10 @@ function estimateDefaultCliContextTokens(opts = {}) {
4348
4492
 
4349
4493
  // packages/cli/tui/sessionDispatch.ts
4350
4494
  var DEFAULT_TUI_SERVER_URL = DEFAULT_NOLO_SERVER_URL;
4495
+ function resolveThinkingDisplayMode(env = process.env) {
4496
+ const raw = (env.NOLO_CLI_THINKING ?? "").trim().toLowerCase();
4497
+ return raw === "hide" || raw === "false" || raw === "0" ? "hide" : "show";
4498
+ }
4351
4499
  function createInitialTuiState(env = process.env) {
4352
4500
  const agentKey = asOptionalTrimmedString(env.NOLO_AGENT) ?? DEFAULT_TUI_AGENT_KEY;
4353
4501
  const agentName = asOptionalTrimmedString(env.NOLO_AGENT_NAME) ?? "nolo";
@@ -4381,7 +4529,7 @@ function createInitialTuiState(env = process.env) {
4381
4529
  userLanguage: asOptionalTrimmedString(env.NOLO_LANG),
4382
4530
  gitStatus: void 0,
4383
4531
  toolDisplay: normalizeToolDisplayMode(env.NOLO_CLI_TOOLS ?? env.NOLO_TOOLS, "normal"),
4384
- thinkingDisplay: "show",
4532
+ thinkingDisplay: resolveThinkingDisplayMode(env),
4385
4533
  contextWindow: resolveAgentContextWindow({ agentKey, agentName }),
4386
4534
  estimatedContextTokens: estimateDefaultCliContextTokens({
4387
4535
  cwd,
@@ -4505,28 +4653,53 @@ function handleTuiInput(input, state) {
4505
4653
  };
4506
4654
  }
4507
4655
  }
4508
- case "/density": {
4509
- const parts = argText.split(/\s+/);
4510
- const sub = parts[0]?.trim();
4511
- if (!sub) {
4656
+ case "/cd": {
4657
+ if (!argText) {
4512
4658
  return {
4513
4659
  nextState: state,
4514
- output: t("densityCurrent", getActiveDensity())
4660
+ output: `${t("cdCurrent", state.cwd)}
4661
+ ${t("cdUsage")}`
4515
4662
  };
4516
4663
  }
4517
- if (sub === "cozy" || sub === "spacious") {
4518
- setActiveDensity(sub);
4519
- return {
4520
- nextState: state,
4521
- output: t("densitySwitched", sub)
4522
- };
4523
- } else {
4664
+ if (argText === "-") {
4665
+ if (!state.prevCwd) {
4666
+ return { nextState: state, output: t("cdNoPrevious") };
4667
+ }
4668
+ const previous = state.prevCwd;
4669
+ const switchMessage2 = t("cdSwitchedMessage", state.cwd, previous);
4524
4670
  return {
4525
- nextState: state,
4526
- output: t("densityUnknown", sub)
4671
+ nextState: { ...state, cwd: previous, prevCwd: void 0, pendingCwdNotice: switchMessage2 },
4672
+ output: t("cdSwitched", previous),
4673
+ action: { type: "cwd-refresh", switchMessage: switchMessage2 }
4527
4674
  };
4528
4675
  }
4676
+ const expanded = argText === "~" ? homedir3() : argText.startsWith("~/") || argText.startsWith("~\\") ? resolve3(homedir3(), argText.slice(2)) : argText;
4677
+ const target = resolve3(state.cwd, expanded);
4678
+ try {
4679
+ if (!statSync2(target).isDirectory()) {
4680
+ return { nextState: state, output: t("cdNotDir", target) };
4681
+ }
4682
+ } catch {
4683
+ return { nextState: state, output: t("cdNotFound", target) };
4684
+ }
4685
+ const normalized = target === "/" ? "/" : target.replace(/\/+$/, "");
4686
+ if (normalized === state.cwd) {
4687
+ return { nextState: state, output: t("cdCurrent", state.cwd) };
4688
+ }
4689
+ const switchMessage = t("cdSwitchedMessage", state.cwd, normalized);
4690
+ return {
4691
+ nextState: { ...state, cwd: normalized, prevCwd: state.cwd, pendingCwdNotice: switchMessage },
4692
+ output: t("cdSwitched", normalized),
4693
+ action: { type: "cwd-refresh", switchMessage }
4694
+ };
4529
4695
  }
4696
+ case "/density":
4697
+ case "/tools":
4698
+ case "/thinking":
4699
+ return {
4700
+ nextState: state,
4701
+ output: t("displayFixedHint")
4702
+ };
4530
4703
  case "/context":
4531
4704
  case "/ctx":
4532
4705
  return { nextState: state, output: renderContextPanel(state) };
@@ -4542,45 +4715,6 @@ function handleTuiInput(input, state) {
4542
4715
  output: t("runtimeSet", argText)
4543
4716
  };
4544
4717
  }
4545
- case "/tools": {
4546
- if (!argText) {
4547
- return {
4548
- nextState: state,
4549
- output: t("toolsCurrent", state.toolDisplay)
4550
- };
4551
- }
4552
- const normalizedArg = asTrimmedLowercaseString(argText);
4553
- if (!["normal", "pro", "hide", "compact", "verbose", "on", "off"].includes(normalizedArg)) {
4554
- return {
4555
- nextState: state,
4556
- output: t("toolsUsage")
4557
- };
4558
- }
4559
- const nextMode = normalizeToolDisplayMode(normalizedArg, state.toolDisplay);
4560
- return {
4561
- nextState: { ...state, toolDisplay: nextMode },
4562
- output: t("toolsSet", nextMode)
4563
- };
4564
- }
4565
- case "/thinking": {
4566
- const normalizedArg = asTrimmedLowercaseString(argText);
4567
- if (!normalizedArg) {
4568
- return {
4569
- nextState: state,
4570
- output: t("thinkingCurrent", state.thinkingDisplay)
4571
- };
4572
- }
4573
- if (normalizedArg !== "show" && normalizedArg !== "hide") {
4574
- return {
4575
- nextState: state,
4576
- output: t("thinkingUsage")
4577
- };
4578
- }
4579
- return {
4580
- nextState: { ...state, thinkingDisplay: normalizedArg },
4581
- output: t("thinkingSet", normalizedArg)
4582
- };
4583
- }
4584
4718
  case "/auto": {
4585
4719
  const normalizedArg = asTrimmedLowercaseString(argText);
4586
4720
  if (!normalizedArg) {
@@ -4989,7 +5123,7 @@ async function probeTerminalBackground(args = {}) {
4989
5123
  if (typeof stdin.setRawMode !== "function") return null;
4990
5124
  if (!isTerminal(stdin)) return null;
4991
5125
  const wasRaw = Boolean(stdin.isRaw);
4992
- return await new Promise((resolve2) => {
5126
+ return await new Promise((resolve4) => {
4993
5127
  let buffer = "";
4994
5128
  let settled = false;
4995
5129
  let timer;
@@ -5002,7 +5136,7 @@ async function probeTerminalBackground(args = {}) {
5002
5136
  if (!wasRaw) stdin.setRawMode?.(false);
5003
5137
  } catch {
5004
5138
  }
5005
- resolve2(result);
5139
+ resolve4(result);
5006
5140
  };
5007
5141
  const onData = (chunk) => {
5008
5142
  buffer += chunk.toString("latin1");
@@ -5926,6 +6060,7 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
5926
6060
  }
5927
6061
  const memoryOverlayLayer = buildMemoryOverlayLayer({ promptBlock: memoryPromptBlock });
5928
6062
  const memoryUseGuidanceLayer = buildMemoryUseGuidanceLayer({ promptBlock: memoryPromptBlock });
6063
+ const cwdNoticeBlocks = state.pendingCwdNotice ? [{ content: state.pendingCwdNotice, cacheScope: "turn" }] : [];
5929
6064
  const contextBlockScopes = partitionScopedBlocks([
5930
6065
  ...renderTurnContextBlocksWithScope(layers),
5931
6066
  ...renderTurnContextBlocksWithScope([memoryUseGuidanceLayer]),
@@ -5933,7 +6068,8 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
5933
6068
  // buildSkillContextBlocks; they stay turn-scope because the user can
5934
6069
  // attach/detach skills between turns.
5935
6070
  ...(skillContextBlocks ?? []).map((content) => content.trim()).filter((content) => content.length > 0).map((content) => ({ content, cacheScope: "turn" })),
5936
- ...renderTurnContextBlocksWithScope([memoryOverlayLayer])
6071
+ ...renderTurnContextBlocksWithScope([memoryOverlayLayer]),
6072
+ ...cwdNoticeBlocks
5937
6073
  ]);
5938
6074
  const result = await agentRunner({
5939
6075
  agentName: effectiveAgentName,
@@ -5997,12 +6133,47 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
5997
6133
  cachedMemoryOverlay: memoryPromptBlock
5998
6134
  };
5999
6135
  }
6000
- function waitForActionGate(rl, input, output, gate, spawnRunner) {
6136
+ var ESC = "\x1B";
6137
+ var CTRL_C = "";
6138
+ function isApprovedConfirmationInput(answer) {
6139
+ const normalized = answer.trim().toLowerCase();
6140
+ return normalized === "" || normalized === "y" || normalized === "yes" || normalized === "\u786E\u8BA4";
6141
+ }
6142
+ function buildGateConfirmedResult(gate) {
6143
+ return {
6144
+ content: `action gate completed: ${gate.title}`,
6145
+ metadata: { actionGateResult: { gateId: gate.id, status: "completed", output: "confirmed" } }
6146
+ };
6147
+ }
6148
+ function buildGateCancelledResult(gate, reason) {
6149
+ return {
6150
+ content: `action gate cancelled: ${gate.title}`,
6151
+ metadata: {
6152
+ exitCode: 130,
6153
+ actionGateResult: { gateId: gate.id, status: "cancelled", output: reason }
6154
+ }
6155
+ };
6156
+ }
6157
+ function buildGateFailedResult(gate, message) {
6158
+ return {
6159
+ content: `action gate failed: ${gate.title}`,
6160
+ metadata: {
6161
+ exitCode: 1,
6162
+ actionGateResult: { gateId: gate.id, status: "failed", output: message }
6163
+ }
6164
+ };
6165
+ }
6166
+ function deriveGateMeta(gate) {
6001
6167
  const commandPayload = gate.kind === "handoff" ? readCommandActionGatePayload(gate.payload) : null;
6168
+ const isConfirmation = gate.kind === "confirm";
6002
6169
  const displayCommand = commandPayload?.displayCommand ?? commandPayload?.command.join(" ") ?? gate.title;
6003
6170
  const isInteractiveHandoff = gate.kind === "handoff" && gate.title === "This command requires an interactive terminal.";
6004
6171
  const title = isInteractiveHandoff ? t("actionGateInteractiveTitle") : gate.title;
6005
6172
  const body = isInteractiveHandoff ? t("actionGateInteractiveBody") : gate.body;
6173
+ return { commandPayload, isConfirmation, displayCommand, isInteractiveHandoff, title, body };
6174
+ }
6175
+ function writeGatePrompt(output, meta) {
6176
+ const { title, body, displayCommand, isConfirmation } = meta;
6006
6177
  output.write(`
6007
6178
  [nolo] ${t("actionGateNeeded")}
6008
6179
  `);
@@ -6012,37 +6183,34 @@ function waitForActionGate(rl, input, output, gate, spawnRunner) {
6012
6183
  `);
6013
6184
  output.write(` ${displayCommand}
6014
6185
  `);
6015
- output.write(`[nolo] ${t("actionGateEnterHint")}
6186
+ output.write(`[nolo] ${isConfirmation ? t("actionGateConfirmHint") : t("actionGateEnterHint")}
6016
6187
  `);
6017
- return new Promise((resolve2) => {
6188
+ }
6189
+ function waitForActionGate(rl, input, output, gate, spawnRunner) {
6190
+ const meta = deriveGateMeta(gate);
6191
+ const { commandPayload, isConfirmation, displayCommand } = meta;
6192
+ writeGatePrompt(output, meta);
6193
+ return new Promise((resolve4) => {
6018
6194
  let settled = false;
6019
6195
  const finish = (result) => {
6020
6196
  if (settled) return;
6021
6197
  settled = true;
6022
6198
  rl.off("close", onClose);
6023
6199
  rl.off("SIGINT", onSigint);
6024
- resolve2(result);
6200
+ resolve4(result);
6025
6201
  };
6026
- const cancelResult = (reason) => ({
6027
- content: `action gate cancelled: ${gate.title}`,
6028
- metadata: {
6029
- exitCode: 130,
6030
- actionGateResult: { gateId: gate.id, status: "cancelled", output: reason }
6031
- }
6032
- });
6033
- const failResult = (message) => ({
6034
- content: `action gate failed: ${gate.title}`,
6035
- metadata: {
6036
- exitCode: 1,
6037
- actionGateResult: { gateId: gate.id, status: "failed", output: message }
6038
- }
6039
- });
6202
+ const cancelResult = (reason) => buildGateCancelledResult(gate, reason);
6203
+ const failResult = (message) => buildGateFailedResult(gate, message);
6040
6204
  const onClose = () => finish(cancelResult("readline closed"));
6041
6205
  const onSigint = () => finish(cancelResult("interrupted"));
6042
6206
  rl.once("close", onClose);
6043
6207
  rl.once("SIGINT", onSigint);
6044
- rl.question("", async () => {
6208
+ rl.question("", async (answer) => {
6045
6209
  if (settled) return;
6210
+ if (isConfirmation) {
6211
+ finish(isApprovedConfirmationInput(answer) ? buildGateConfirmedResult(gate) : cancelResult("confirmation declined"));
6212
+ return;
6213
+ }
6046
6214
  if (!commandPayload) {
6047
6215
  finish(failResult("unsupported gate payload"));
6048
6216
  return;
@@ -6088,47 +6256,23 @@ function isInteractiveInput(input) {
6088
6256
  return Boolean(candidate.isTTY) && typeof candidate.setRawMode === "function";
6089
6257
  }
6090
6258
  function waitForRawActionGate(input, output, gate, spawnRunner, hooks) {
6091
- const commandPayload = gate.kind === "handoff" ? readCommandActionGatePayload(gate.payload) : null;
6092
- const displayCommand = commandPayload?.displayCommand ?? commandPayload?.command.join(" ") ?? gate.title;
6093
- const isInteractiveHandoff = gate.kind === "handoff" && gate.title === "This command requires an interactive terminal.";
6094
- const title = isInteractiveHandoff ? t("actionGateInteractiveTitle") : gate.title;
6095
- const body = isInteractiveHandoff ? t("actionGateInteractiveBody") : gate.body;
6096
- output.write(`
6097
- [nolo] ${t("actionGateNeeded")}
6098
- `);
6099
- output.write(`[nolo] ${title}
6100
- `);
6101
- if (body) output.write(`[nolo] ${body}
6102
- `);
6103
- output.write(` ${displayCommand}
6104
- `);
6105
- output.write(`[nolo] ${t("actionGateEnterHint")}
6106
- `);
6107
- return new Promise((resolve2) => {
6259
+ const meta = deriveGateMeta(gate);
6260
+ const { commandPayload, isConfirmation, displayCommand } = meta;
6261
+ writeGatePrompt(output, meta);
6262
+ return new Promise((resolve4) => {
6108
6263
  const rawInput = input;
6109
6264
  let settled = false;
6110
6265
  let commandRunning = false;
6266
+ let confirmationLine = "";
6111
6267
  const finish = (result) => {
6112
6268
  if (settled) return;
6113
6269
  settled = true;
6114
6270
  hooks?.registerToken?.(null);
6115
6271
  input.off("data", onData);
6116
- resolve2(result);
6272
+ resolve4(result);
6117
6273
  };
6118
- const cancel = (reason) => finish({
6119
- content: `action gate cancelled: ${gate.title}`,
6120
- metadata: {
6121
- exitCode: 130,
6122
- actionGateResult: { gateId: gate.id, status: "cancelled", output: reason }
6123
- }
6124
- });
6125
- const fail = (message) => finish({
6126
- content: `action gate failed: ${gate.title}`,
6127
- metadata: {
6128
- exitCode: 1,
6129
- actionGateResult: { gateId: gate.id, status: "failed", output: message }
6130
- }
6131
- });
6274
+ const cancel = (reason) => finish(buildGateCancelledResult(gate, reason));
6275
+ const fail = (message) => finish(buildGateFailedResult(gate, message));
6132
6276
  const runCommand = async () => {
6133
6277
  if (settled || commandRunning) return;
6134
6278
  commandRunning = true;
@@ -6171,10 +6315,24 @@ function waitForRawActionGate(input, output, gate, spawnRunner, hooks) {
6171
6315
  };
6172
6316
  const handleToken = (text) => {
6173
6317
  if (settled || commandRunning) return;
6174
- if (text.includes("")) {
6318
+ if (text.includes(CTRL_C) || isConfirmation && text === ESC) {
6175
6319
  cancel("interrupted");
6176
6320
  return;
6177
6321
  }
6322
+ if (isConfirmation) {
6323
+ const newline = text.search(/[\r\n]/);
6324
+ confirmationLine += newline >= 0 ? text.slice(0, newline) : text;
6325
+ if (newline >= 0) {
6326
+ const normalized = confirmationLine.trim().toLowerCase();
6327
+ confirmationLine = "";
6328
+ if (!isApprovedConfirmationInput(normalized)) {
6329
+ cancel("confirmation declined");
6330
+ return;
6331
+ }
6332
+ finish(buildGateConfirmedResult(gate));
6333
+ }
6334
+ return;
6335
+ }
6178
6336
  if (text.includes("\r") || text.includes("\n")) {
6179
6337
  void runCommand();
6180
6338
  }
@@ -6190,6 +6348,38 @@ function waitForRawActionGate(input, output, gate, spawnRunner, hooks) {
6190
6348
  }
6191
6349
  });
6192
6350
  }
6351
+ function asConfirmPermissionRequest(gate) {
6352
+ return gate;
6353
+ }
6354
+ function isAutoConfirmableFileWriteGate(gate) {
6355
+ return gate.kind === "confirm" && asConfirmPermissionRequest(gate).action === "file_write";
6356
+ }
6357
+ async function resolveActionGate(gate, deps) {
6358
+ if (gate.kind === "confirm") {
6359
+ const approved = await deps.dialogHost.run(
6360
+ (anchor) => runConfirmDialog({
6361
+ request: asConfirmPermissionRequest(gate),
6362
+ input: deps.input,
6363
+ output: deps.output,
6364
+ ...anchor
6365
+ })
6366
+ );
6367
+ return approved ? buildGateConfirmedResult(gate) : buildGateCancelledResult(gate, "confirmation declined");
6368
+ }
6369
+ let subprocessRan = false;
6370
+ deps.pauseComposer();
6371
+ try {
6372
+ return await waitForRawActionGate(deps.input, deps.output, gate, deps.spawnRunner, {
6373
+ beforeSubprocess: () => {
6374
+ subprocessRan = true;
6375
+ },
6376
+ afterSubprocess: deps.resumeComposerFromSubprocess,
6377
+ registerToken: deps.registerToken
6378
+ });
6379
+ } finally {
6380
+ if (!subprocessRan) deps.resumeComposerFromDialog();
6381
+ }
6382
+ }
6193
6383
  async function runOneAgentTurn(ctx, inputMsg, imageUrls, actionGateHandler, confirmDestructiveAction) {
6194
6384
  const scheduleTitlePatchSync = (runResult) => {
6195
6385
  if (!runResult.titlePatchPromise || !runResult.dialogId) return;
@@ -6320,7 +6510,9 @@ async function runOneAgentTurn(ctx, inputMsg, imageUrls, actionGateHandler, conf
6320
6510
  ...runResult.title ? { dialogTitle: runResult.title } : {}
6321
6511
  } : {},
6322
6512
  ...runResult.turnTokens ? { turnTokens: runResult.turnTokens } : {},
6323
- ...runResult.cachedMemoryOverlay !== void 0 ? { cachedMemoryOverlay: runResult.cachedMemoryOverlay } : {}
6513
+ ...runResult.cachedMemoryOverlay !== void 0 ? { cachedMemoryOverlay: runResult.cachedMemoryOverlay } : {},
6514
+ // 本轮已发起 agent 调用(消息已注入),消费切换通知。
6515
+ pendingCwdNotice: void 0
6324
6516
  };
6325
6517
  if (runResult.dialogId && nextDialogKey) {
6326
6518
  ctx.refreshDialogTotalCredits(runResult.dialogId, nextDialogKey);
@@ -6368,7 +6560,11 @@ async function runOneAgentTurn(ctx, inputMsg, imageUrls, actionGateHandler, conf
6368
6560
  // estimatedContextTokens:下一轮若 provider 不返回 usage,context
6369
6561
  // chip 仍显示真实累计占用而不是回退到启动时的静态估算。
6370
6562
  ...runResult.turnTokens && runResult.turnTokens.input > 0 ? { estimatedContextTokens: runResult.turnTokens.input } : {},
6371
- ...runResult.cachedMemoryOverlay !== void 0 ? { cachedMemoryOverlay: runResult.cachedMemoryOverlay } : {}
6563
+ ...runResult.cachedMemoryOverlay !== void 0 ? { cachedMemoryOverlay: runResult.cachedMemoryOverlay } : {},
6564
+ // 切换消息本轮已注入 agent 上下文(runAgentChat 已读),消费掉,
6565
+ // 避免下轮重复注入。仅当确实发起了 agent 调用(本轮是真实 turn、
6566
+ // 而非纯 child-run-completed 事件短路)时才清除。
6567
+ pendingCwdNotice: void 0
6372
6568
  };
6373
6569
  if (runResult.dialogId && nextDialogKey) {
6374
6570
  ctx.refreshDialogTotalCredits(runResult.dialogId, nextDialogKey);
@@ -6978,7 +7174,7 @@ function createFixedInput(output, config) {
6978
7174
  const renderInputArea = (buffer, cursorPos) => {
6979
7175
  const colorEnabled = resolveCliColorEnabled();
6980
7176
  const cols = Math.max(1, getColumns());
6981
- const completions = completeSlashCommand(buffer);
7177
+ const completions = completeSlashCommand(buffer, config.getCwd?.());
6982
7178
  const sections = [];
6983
7179
  if (completions.length > 0) {
6984
7180
  sections.push(fitAnsiLine(dimCliText(completions.join(" "), colorEnabled), cols));
@@ -6999,6 +7195,10 @@ function createFixedInput(output, config) {
6999
7195
  for (const line of queueLines) {
7000
7196
  sections.push(fitAnsiLine(line.replace(/\r?\n/g, " "), cols));
7001
7197
  }
7198
+ const attachmentLine = config.getAttachmentLine?.() ?? null;
7199
+ if (attachmentLine) {
7200
+ sections.push(fitAnsiLine(attachmentLine.replace(/\r?\n/g, " "), cols));
7201
+ }
7002
7202
  const promptRaw = t("promptLabel");
7003
7203
  const promptWidth = displayWidth(promptRaw);
7004
7204
  const prompt = colorEnabled ? `${themeColorSequence("accent")}${promptRaw}\x1B[39m` : promptRaw;
@@ -7048,7 +7248,7 @@ function createFixedInput(output, config) {
7048
7248
  }
7049
7249
  const text = sections.join("\n");
7050
7250
  const lines = sections.length;
7051
- const headerRows = (completions.length > 0 ? 1 : 0) + 2 + (titleLine ? 1 : 0) + activityLines.length + queueLines.length;
7251
+ const headerRows = (completions.length > 0 ? 1 : 0) + 2 + (titleLine ? 1 : 0) + activityLines.length + queueLines.length + (attachmentLine ? 1 : 0);
7052
7252
  return { text, lines, cursorCol, cursorRow: headerRows + cursorRow };
7053
7253
  };
7054
7254
  const repaintAt = (buffer, cursorPos, force = false) => {
@@ -7458,6 +7658,7 @@ async function runSubmittedSlashLine(line, host, actionGateHandler, confirmDestr
7458
7658
  turnCtx,
7459
7659
  emitCommandOutput,
7460
7660
  renderHistoryToOutput,
7661
+ scheduleRender,
7461
7662
  refreshDialogTotalCredits,
7462
7663
  persistExplicitAgentSwitch,
7463
7664
  persistAgentSelection: persistAgentSelection2,
@@ -7631,6 +7832,18 @@ async function runSubmittedSlashLine(line, host, actionGateHandler, confirmDestr
7631
7832
  emitCommandOutput(t("themeRefreshFailed"));
7632
7833
  }
7633
7834
  }
7835
+ if (result.action?.type === "cwd-refresh") {
7836
+ if ((options.env?.NOLO_CLI_GIT_STATUS ?? process.env.NOLO_CLI_GIT_STATUS) !== "0") {
7837
+ const gitStatus = await detectGitStatusAsync(host.state.cwd);
7838
+ if (gitStatus !== void 0) {
7839
+ host.state = { ...host.state, gitStatus };
7840
+ }
7841
+ }
7842
+ if (result.action.switchMessage) {
7843
+ emitCommandOutput(result.action.switchMessage);
7844
+ }
7845
+ scheduleRender();
7846
+ }
7634
7847
  if (result.action?.type === "pick-agent") {
7635
7848
  try {
7636
7849
  const pickResult = await dialogHost.run(
@@ -8062,6 +8275,7 @@ async function runTuiWorkspace(options) {
8062
8275
  isTurnActive: () => activeTurnAbort !== null,
8063
8276
  fallbackLabel: () => `${state.agentName} -> working`,
8064
8277
  stoppingLabel: () => t("turnStopping"),
8278
+ displayMode: () => state.toolDisplay,
8065
8279
  onRepaint: () => {
8066
8280
  if (fixedInput.active && !fixedInput.isPaused()) {
8067
8281
  output.write("\x1B[?2026h\x1B[?25l");
@@ -8339,6 +8553,7 @@ async function runTuiWorkspace(options) {
8339
8553
  turnCtx,
8340
8554
  emitCommandOutput,
8341
8555
  renderHistoryToOutput,
8556
+ scheduleRender,
8342
8557
  refreshDialogTotalCredits,
8343
8558
  persistExplicitAgentSwitch,
8344
8559
  persistAgentSelection,
@@ -8371,9 +8586,16 @@ async function runTuiWorkspace(options) {
8371
8586
  return dimCliText(` \u2937 ${i + 1}. ${oneLine}`, colorEnabled);
8372
8587
  });
8373
8588
  },
8589
+ getAttachmentLine: () => {
8590
+ const line = formatComposerAttachmentLine(state.attachedImages);
8591
+ return line ? dimCliText(line, resolveCliColorEnabled()) : null;
8592
+ },
8374
8593
  onInputLinesChange: () => {
8375
8594
  renderHistoryToOutput();
8376
- }
8595
+ },
8596
+ // /cd 路径补全候选行渲染需要当前 cwd(读 state 而非入参,保证
8597
+ // /cd 切换后立即以新 cwd 渲染,composer 不必重建)。
8598
+ getCwd: () => state.cwd
8377
8599
  });
8378
8600
  fixedInput = {
8379
8601
  ...baseFixedInput,
@@ -8452,14 +8674,22 @@ async function runTuiWorkspace(options) {
8452
8674
  modalOwnsKeyboard = false;
8453
8675
  };
8454
8676
  const actionGateHandler = async (gate) => {
8677
+ if (state.autoConfirm && isAutoConfirmableFileWriteGate(gate)) {
8678
+ return buildGateConfirmedResult(gate);
8679
+ }
8455
8680
  modalOwnsKeyboard = true;
8456
8681
  try {
8457
- return await waitForRawActionGate(input, output, gate, spawnRunner, {
8458
- beforeSubprocess: () => fixedInput.pause(),
8459
- afterSubprocess: () => fixedInput.resumeFromSubprocess(),
8682
+ return await resolveActionGate(gate, {
8683
+ dialogHost,
8684
+ input,
8685
+ output,
8686
+ spawnRunner,
8460
8687
  registerToken: (handler) => {
8461
8688
  rawActionGateTokenHandler = handler;
8462
- }
8689
+ },
8690
+ pauseComposer: () => fixedInput.pause(),
8691
+ resumeComposerFromSubprocess: () => fixedInput.resumeFromSubprocess(),
8692
+ resumeComposerFromDialog: () => fixedInput.resumeFromDialog()
8463
8693
  });
8464
8694
  } finally {
8465
8695
  releaseKeyboardToComposer();
@@ -8657,6 +8887,19 @@ ${err.message}` : ""}`
8657
8887
  output.write(`${refreshMsg}
8658
8888
  `);
8659
8889
  }
8890
+ } else if (res.action?.type === "cwd-refresh") {
8891
+ state = res.nextState;
8892
+ if (res.output) {
8893
+ output.write(`${res.output}
8894
+ `);
8895
+ }
8896
+ if ((options.env?.NOLO_CLI_GIT_STATUS ?? process.env.NOLO_CLI_GIT_STATUS) !== "0") {
8897
+ const gitStatus = await detectGitStatusAsync(state.cwd);
8898
+ if (gitStatus !== void 0) {
8899
+ state = { ...state, gitStatus };
8900
+ }
8901
+ }
8902
+ scheduleRender();
8660
8903
  } else if (res.action) {
8661
8904
  output.write(
8662
8905
  "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"
@@ -8865,8 +9108,20 @@ ${merged}` : merged;
8865
9108
  if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
8866
9109
  return;
8867
9110
  }
9111
+ if (isBackspaceSequence(sequence, {}) && buffer === "" && state.attachedImages.length > 0) {
9112
+ const undo = popLastAttachedImage(state.attachedImages);
9113
+ if (undo.handled && undo.removed) {
9114
+ state = { ...state, attachedImages: undo.images };
9115
+ emitCommandOutput(
9116
+ `[nolo] ${t("attachmentRemovedHint", undo.removed.filename, String(undo.images.length))}`
9117
+ );
9118
+ if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
9119
+ return;
9120
+ }
9121
+ }
8868
9122
  const result = applyTuiInputKey(buffer, sequence, {}, cursorPos, {
8869
- pasteStore
9123
+ pasteStore,
9124
+ cwd: state.cwd
8870
9125
  });
8871
9126
  if (result.redraw) {
8872
9127
  resetHistoryFrameDiffCache(output);
@@ -8889,7 +9144,7 @@ ${merged}` : merged;
8889
9144
  if (busyLock) {
8890
9145
  const trimmedText = submittedText.trim();
8891
9146
  const busySlashCommand = trimmedText.split(/\s+/)[0]?.toLowerCase();
8892
- const isBusyLocalSlash = busySlashCommand === "/context" || busySlashCommand === "/ctx" || busySlashCommand === "/switch" || busySlashCommand === "/theme" || busySlashCommand === "/density" || 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";
9147
+ 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";
8893
9148
  if (isBusyLocalSlash) {
8894
9149
  await handleBusyLocalSlash(submittedText, busySlashCommand);
8895
9150
  return;
@@ -8959,9 +9214,9 @@ ${merged}` : merged;
8959
9214
  input.on("data", onData);
8960
9215
  fixedInput.repaint(buffer, cursorPos);
8961
9216
  refreshGitStatus();
8962
- await new Promise((resolve2) => {
8963
- resolveDone = resolve2;
8964
- if (done) resolve2();
9217
+ await new Promise((resolve4) => {
9218
+ resolveDone = resolve4;
9219
+ if (done) resolve4();
8965
9220
  });
8966
9221
  } finally {
8967
9222
  finish();
@@ -8975,7 +9230,11 @@ ${merged}` : merged;
8975
9230
  for await (const line of rl) {
8976
9231
  const shouldExit = await runSubmittedLine(
8977
9232
  line,
8978
- (gate) => waitForActionGate(rl, input, output, gate, spawnRunner),
9233
+ (gate) => (
9234
+ // /auto on 同款短路,见上方交互路径的 actionGateHandler 注释:
9235
+ // 只放行 file_write 的 confirm gate,handoff/input 类仍要求真人操作。
9236
+ state.autoConfirm && isAutoConfirmableFileWriteGate(gate) ? Promise.resolve(buildGateConfirmedResult(gate)) : waitForActionGate(rl, input, output, gate, spawnRunner)
9237
+ ),
8979
9238
  async (request) => {
8980
9239
  if (state.autoConfirm) return true;
8981
9240
  rl.pause();
@@ -9014,12 +9273,16 @@ ${renderStatusLine(state)}
9014
9273
  }
9015
9274
  async function startTuiWorkspace(options) {
9016
9275
  const themeOwner = ++latestWorkspaceThemeOwner;
9276
+ void sweepStaleClipboardFiles(getDefaultClipboardTempDir()).catch(() => {
9277
+ });
9017
9278
  try {
9018
9279
  return await runTuiWorkspace(options);
9019
9280
  } finally {
9020
9281
  if (themeOwner === latestWorkspaceThemeOwner) {
9021
9282
  resetWorkspaceThemeState();
9022
9283
  }
9284
+ void sweepStaleClipboardFiles(getDefaultClipboardTempDir()).catch(() => {
9285
+ });
9023
9286
  }
9024
9287
  }
9025
9288
  export {