nolo-cli 0.54.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 (111) hide show
  1. package/{_executeModel-IVEOJ7L6.js → _executeModel-ANY4SIAS.js} +35 -36
  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-D7AFLKQN.js → agentDeleteCommand-JWIKNWWI.js} +17 -17
  6. package/{agentEmailCommands-MXFZGF54.js → agentEmailCommands-PL22BEU5.js} +40 -41
  7. package/{agentListCommands-MP732Q23.js → agentListCommands-6NR7AQEK.js} +19 -19
  8. package/{agentMachineCommands-352JFJX4.js → agentMachineCommands-TASPU6OA.js} +18 -18
  9. package/{agentRecordCommands-G6EFHU3G.js → agentRecordCommands-Z64TA63M.js} +39 -40
  10. package/{agentRunCommand-UUWJMH7J.js → agentRunCommand-4QJQLPRW.js} +40 -41
  11. package/{agentRuntimeLocal-KWBPJ53H.js → agentRuntimeLocal-PCJJ56J2.js} +17 -17
  12. package/{agentSlice-V742SNKC.js → agentSlice-KYLDUSNN.js} +6 -6
  13. package/{appDeleteCommands-GV4TF52A.js → appDeleteCommands-WLHMJAVT.js} +19 -19
  14. package/{appDeployCommands-EZMUPNSW.js → appDeployCommands-CKON7N2E.js} +19 -19
  15. package/{appDeployStatusCommands-AAGELKAN.js → appDeployStatusCommands-PXRLDASH.js} +19 -19
  16. package/{appGetCommands-W37KDAON.js → appGetCommands-RAB5RM4M.js} +19 -19
  17. package/{appListCommands-IFDWBBNV.js → appListCommands-GU57FB62.js} +19 -19
  18. package/{authCommand-5HM57KPL.js → authCommand-QOPOEYEK.js} +7 -7
  19. package/{chatQueueReduxAdapter-ZXXP6E4O.js → chatQueueReduxAdapter-5O2WLITN.js} +17 -17
  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-ALMVQQPG.js → chunk-3Z3DXZBD.js} +12 -12
  24. package/{chunk-RO2IUWZE.js → chunk-4NN3EK7H.js} +1 -1
  25. package/{chunk-WJV3G7BM.js → chunk-56W233KG.js} +1 -1
  26. package/{chunk-GBXR3CSM.js → chunk-5WADL4O7.js} +1 -1
  27. package/{chunk-J4Z6UI4C.js → chunk-6BGAQ3KR.js} +20 -15
  28. package/{chunk-R63FNYSI.js → chunk-6FQ2552G.js} +66 -28
  29. package/{chunk-6XIE5ZGI.js → chunk-6HIGYUBQ.js} +1 -1
  30. package/{chunk-MC66ASS6.js → chunk-6SZ52ORL.js} +8 -8
  31. package/{chunk-CULR4XB4.js → chunk-757BGY6Y.js} +1 -1
  32. package/{chunk-SQTVMYMU.js → chunk-7APQB6R6.js} +3 -3
  33. package/{chunk-RBJD33HM.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-BKPUPZ6W.js → chunk-BJLYCOZK.js} +25 -25
  37. package/{chunk-2T6XO4PK.js → chunk-CVT7L2WZ.js} +1 -1
  38. package/{chunk-XXZULHJ7.js → chunk-D3JQPONH.js} +1 -1
  39. package/{chunk-M2CGG6VJ.js → chunk-EVBFYPDC.js} +1 -1
  40. package/{chunk-KHCLMKZZ.js → chunk-EY7V7REW.js} +280 -6
  41. package/{chunk-4RXXP6WD.js → chunk-FGEYVPPR.js} +2 -2
  42. package/{chunk-ZMV5PCRS.js → chunk-GBADO3SM.js} +1 -1
  43. package/chunk-GLGD2JGY.js +1766 -0
  44. package/{chunk-W6N3GLUU.js → chunk-GV35EHNA.js} +81 -69
  45. package/{chunk-HR5XDXYM.js → chunk-H4RYESI6.js} +3 -3
  46. package/{chunk-FZMM2KZC.js → chunk-IKTHA3JH.js} +2 -2
  47. package/{chunk-WUMYQ2FE.js → chunk-INXBNYCG.js} +3 -3
  48. package/{chunk-PTHLNXGY.js → chunk-KUUTWU4I.js} +3 -3
  49. package/{chunk-RVHPY5PC.js → chunk-LA4IDAK7.js} +12 -12
  50. package/{chunk-OU7NGN5Y.js → chunk-LN3OQJGG.js} +1 -1
  51. package/{chunk-YZ2WE6CW.js → chunk-MFI4FGHX.js} +2 -2
  52. package/{chunk-LGUQHYAL.js → chunk-O5FLGN2L.js} +3 -2
  53. package/{chunk-HCBQVVFZ.js → chunk-PAHILFNT.js} +1 -1
  54. package/{chunk-HHBXCRJS.js → chunk-PYKV6FSC.js} +1 -1
  55. package/{chunk-7UFH6THF.js → chunk-QDHZCAH3.js} +1 -1
  56. package/{chunk-L3JWI7WX.js → chunk-QOKAKDPE.js} +15 -11
  57. package/{chunk-B54WOZ77.js → chunk-RTLJUGQH.js} +1 -1
  58. package/{chunk-YFHUQLJF.js → chunk-RU5D3B22.js} +1 -1
  59. package/{chunk-5Y5G76BJ.js → chunk-SP3DPVBH.js} +2 -2
  60. package/{chunk-5SXS7T5Q.js → chunk-TI5BCZCH.js} +18 -18
  61. package/{chunk-K7CNV7GI.js → chunk-U2A47SVI.js} +5 -5
  62. package/{chunk-45E65JER.js → chunk-U5Z7LFZF.js} +1 -1
  63. package/{chunk-DIQCV4FR.js → chunk-UITMHBVK.js} +1 -1
  64. package/{chunk-VM7K5EJL.js → chunk-UW4INMD2.js} +2 -2
  65. package/{chunk-SUFDWBLV.js → chunk-V4KZGLOI.js} +30 -30
  66. package/{chunk-ET7BXWAW.js → chunk-VRJUIUIY.js} +2 -2
  67. package/{chunk-5XCW6RQG.js → chunk-VRZIH5JO.js} +1 -1
  68. package/{chunk-2JYSSXLY.js → chunk-VULELK4T.js} +2 -2
  69. package/{chunk-J7KVJAUQ.js → chunk-WL3UOGYO.js} +0 -1
  70. package/{chunk-PEMGYJNS.js → chunk-Y7LNKP32.js} +3 -3
  71. package/{chunk-DMCFF4KE.js → chunk-YBV6QJKM.js} +14 -8
  72. package/{codingSkills-BYQI6EPT.js → codingSkills-BCQXRZOI.js} +3 -4
  73. package/{createAgentAutomationAction-LE3ZYDT6.js → createAgentAutomationAction-UJLNFFFC.js} +18 -18
  74. package/{createDialogAction-2C6SR62D.js → createDialogAction-TJCX6GUE.js} +21 -22
  75. package/{createPageAction-X4C7K3OD.js → createPageAction-N3IYABHV.js} +19 -20
  76. package/{dbSlice-FWF5F4L7.js → dbSlice-D5CNGWPD.js} +5 -5
  77. package/{deleteFile-NPZ64DFN.js → deleteFile-44TUM34Y.js} +3 -3
  78. package/{dialogCommands-CP2U44A7.js → dialogCommands-35M6RBAC.js} +18 -18
  79. package/{dialogSlice-2SH5636X.js → dialogSlice-KO7ADD7H.js} +17 -17
  80. package/{docCreateCommands-A2KIJSF7.js → docCreateCommands-VXHZWQ3P.js} +20 -21
  81. package/{docDeleteCommands-NZ7XO4Z4.js → docDeleteCommands-4WFAJR6A.js} +20 -21
  82. package/{docReadCommands-VWVK4ZAR.js → docReadCommands-OYREEFAR.js} +20 -21
  83. package/{docStore-J6GQYKHS.js → docStore-5BU76DFK.js} +19 -19
  84. package/{docUpdateCommands-YTUEC6PN.js → docUpdateCommands-A7QERSME.js} +20 -21
  85. package/{getPricing-I53UFOPH.js → getPricing-HAU62AFM.js} +3 -3
  86. package/{handleSendMessageAction-MD325XZS.js → handleSendMessageAction-OSGQJQ45.js} +17 -17
  87. package/index.js +58 -58
  88. package/{machineCommands-TCNZV2DC.js → machineCommands-J43U3TX5.js} +39 -40
  89. package/{machineWsRunDispatch-QSX5IWPH.js → machineWsRunDispatch-Z2Z6STXZ.js} +39 -40
  90. package/{models-E2CYP4IB.js → models-4QF4BJVY.js} +2 -2
  91. package/package.json +2 -3
  92. package/{read-J7RVVCHH.js → read-RGNPI5H2.js} +3 -3
  93. package/{readDocTool-G2WBM4IH.js → readDocTool-6ESWKTXD.js} +1 -1
  94. package/{readlineWorkspace-N3RGX7EK.js → readlineWorkspace-YRPV6JPN.js} +462 -235
  95. package/{removeDialogAgentAction-RMPISD42.js → removeDialogAgentAction-JYT73FU2.js} +7 -7
  96. package/{runAgentClientLoop-R3NTOERA.js → runAgentClientLoop-5YM5WMAY.js} +33 -34
  97. package/{setDialogExtraReferencesAction-7BUTHDAL.js → setDialogExtraReferencesAction-D564LMYQ.js} +6 -6
  98. package/{setPrimaryDialogAgentAction-WCYIBGZ4.js → setPrimaryDialogAgentAction-ACY3BLYA.js} +7 -7
  99. package/{settingSlice-7NN2MI6Y.js → settingSlice-MKI3MNZJ.js} +6 -6
  100. package/{skillDiscovery-COX56OBO.js → skillDiscovery-GPUDHDTZ.js} +1 -2
  101. package/{spaceCommands-RC7XMYQ7.js → spaceCommands-MY26V52L.js} +17 -17
  102. package/{spaceSlice-KBVKKO3M.js → spaceSlice-JUIXC365.js} +18 -18
  103. package/{streamAgentChatTurn-WLWEBWSF.js → streamAgentChatTurn-XV5X5TNQ.js} +37 -38
  104. package/{switchDialogAgentAction-WGMMQHDI.js → switchDialogAgentAction-CJ5H6SAR.js} +5 -5
  105. package/{tableCommands-P2TXJ3CF.js → tableCommands-R6WFRDYF.js} +1 -1
  106. package/{tools-LSLDSUWU.js → tools-MIMELA2Y.js} +28 -29
  107. package/{tuiSummaryLlmCaller-TPBL4BV2.js → tuiSummaryLlmCaller-XJ5BKNNS.js} +13 -13
  108. package/{updateDialogTitleAction-4HJEARM7.js → updateDialogTitleAction-EIZUSWHR.js} +18 -18
  109. package/chunk-AETZ7AD6.js +0 -274
  110. package/chunk-EUG45QJR.js +0 -6104
  111. 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-R63FNYSI.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-GBXR3CSM.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-W6N3GLUU.js";
90
- import "./chunk-45E65JER.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-VM7K5EJL.js";
119
+ } from "./chunk-UW4INMD2.js";
122
120
  import "./chunk-HS7MLR74.js";
123
- import "./chunk-CULR4XB4.js";
124
- import "./chunk-SUFDWBLV.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-YFHUQLJF.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-PEMGYJNS.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-MC66ASS6.js";
155
- import "./chunk-RVHPY5PC.js";
152
+ import "./chunk-6SZ52ORL.js";
153
+ import "./chunk-LA4IDAK7.js";
156
154
  import {
157
155
  extractReferenceKeysFromMessage
158
- } from "./chunk-BKPUPZ6W.js";
159
- import "./chunk-M2CGG6VJ.js";
156
+ } from "./chunk-BJLYCOZK.js";
157
+ import "./chunk-EVBFYPDC.js";
160
158
  import "./chunk-H2G6JXTR.js";
161
159
  import "./chunk-YYLHWIW6.js";
162
- import "./chunk-K7CNV7GI.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-J4Z6UI4C.js";
187
- import "./chunk-WUMYQ2FE.js";
183
+ } from "./chunk-6BGAQ3KR.js";
184
+ import "./chunk-INXBNYCG.js";
188
185
  import "./chunk-ZNFJWEN4.js";
189
- import "./chunk-2T6XO4PK.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-DMCFF4KE.js";
194
+ } from "./chunk-YBV6QJKM.js";
198
195
  import "./chunk-6BYAXNH5.js";
199
196
  import {
200
197
  resolvePlatformAuthToken
201
- } from "./chunk-5Y5G76BJ.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-PTHLNXGY.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-ET7BXWAW.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-7UFH6THF.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-WJV3G7BM.js";
272
- import "./chunk-J7KVJAUQ.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;
@@ -3327,6 +3314,20 @@ function summarizeAttachment(img) {
3327
3314
  ].join("\n");
3328
3315
  return themeText(text, "chrome", colorEnabled);
3329
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
+ }
3330
3331
  function mergeAttachedImages(existing, incoming) {
3331
3332
  if (incoming.length === 0) return existing;
3332
3333
  if (existing.length === 0) return [...incoming];
@@ -3348,12 +3349,12 @@ function mergeAttachedImages(existing, incoming) {
3348
3349
  return out;
3349
3350
  }
3350
3351
  async function readImagePaths(paths, options = {}) {
3351
- const resolve2 = options.resolve ?? ((p) => p);
3352
+ const resolve4 = options.resolve ?? ((p) => p);
3352
3353
  const reportedFailures = /* @__PURE__ */ new Set();
3353
3354
  const images = [];
3354
3355
  const failures = [];
3355
3356
  for (const raw of paths) {
3356
- const absolute = resolve2(raw);
3357
+ const absolute = resolve4(raw);
3357
3358
  try {
3358
3359
  const img = await readImageAsDataUrl(absolute, {
3359
3360
  ...options.maxBytes !== void 0 ? { maxBytes: options.maxBytes } : {}
@@ -3393,7 +3394,7 @@ async function resolveAttachmentImageUrls({
3393
3394
  // packages/cli/tui/clipboardImage.ts
3394
3395
  import { createHash } from "node:crypto";
3395
3396
  import { mkdirSync } from "node:fs";
3396
- import { writeFile } from "node:fs/promises";
3397
+ import { readdir, stat as stat2, unlink, writeFile } from "node:fs/promises";
3397
3398
  import { tmpdir } from "node:os";
3398
3399
  import { join } from "node:path";
3399
3400
  var ClipboardImageError = class extends Error {
@@ -3405,6 +3406,34 @@ var ClipboardImageError = class extends Error {
3405
3406
  }
3406
3407
  };
3407
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
+ }
3408
3437
  function isRemoteSession(env = process.env) {
3409
3438
  return Boolean(env.SSH_CONNECTION || env.SSH_TTY || env.SSH_CLIENT);
3410
3439
  }
@@ -3420,13 +3449,20 @@ function getClipboardImageCommand(deps = {}) {
3420
3449
  }
3421
3450
  if (platform === "darwin") {
3422
3451
  const pngpaste = which("pngpaste");
3423
- if (!pngpaste) {
3452
+ if (pngpaste) {
3453
+ return { command: [pngpaste, "-"], outputFormat: "raw" };
3454
+ }
3455
+ const osascript = which("osascript");
3456
+ if (!osascript) {
3424
3457
  throw new ClipboardImageError(
3425
3458
  "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"
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"
3427
3460
  );
3428
3461
  }
3429
- return { command: [pngpaste, "-"] };
3462
+ return {
3463
+ command: [osascript, "-e", "the clipboard as \xABclass PNGf\xBB"],
3464
+ outputFormat: "osascript-hex"
3465
+ };
3430
3466
  }
3431
3467
  if (platform === "win32") {
3432
3468
  const ps = which("powershell.exe") || which("powershell") || which("pwsh.exe") || which("pwsh");
@@ -3443,7 +3479,8 @@ function getClipboardImageCommand(deps = {}) {
3443
3479
  "-NonInteractive",
3444
3480
  "-Command",
3445
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) }"
3446
- ]
3482
+ ],
3483
+ outputFormat: "raw"
3447
3484
  };
3448
3485
  }
3449
3486
  if (platform === "linux") {
@@ -3456,17 +3493,18 @@ function getClipboardImageCommand(deps = {}) {
3456
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"
3457
3494
  );
3458
3495
  }
3459
- return { command: [wlPaste2, "--type", "image/png"] };
3496
+ return { command: [wlPaste2, "--type", "image/png"], outputFormat: "raw" };
3460
3497
  }
3461
3498
  const xclip = which("xclip");
3462
3499
  if (xclip) {
3463
3500
  return {
3464
- command: [xclip, "-selection", "clipboard", "-t", "image/png", "-o"]
3501
+ command: [xclip, "-selection", "clipboard", "-t", "image/png", "-o"],
3502
+ outputFormat: "raw"
3465
3503
  };
3466
3504
  }
3467
3505
  const wlPaste = which("wl-paste");
3468
3506
  if (wlPaste) {
3469
- return { command: [wlPaste, "--type", "image/png"] };
3507
+ return { command: [wlPaste, "--type", "image/png"], outputFormat: "raw" };
3470
3508
  }
3471
3509
  throw new ClipboardImageError(
3472
3510
  "binary-missing",
@@ -3478,6 +3516,21 @@ function getClipboardImageCommand(deps = {}) {
3478
3516
  `\u5F53\u524D\u64CD\u4F5C\u7CFB\u7EDF\u6682\u4E0D\u652F\u6301\u526A\u8D34\u677F\u56FE\u50CF\u8BFB\u53D6: ${platform}`
3479
3517
  );
3480
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
+ }
3481
3534
  async function readClipboardImage(deps = {}) {
3482
3535
  const env = deps.env ?? process.env;
3483
3536
  const platform = deps.platform ?? process.platform;
@@ -3485,8 +3538,8 @@ async function readClipboardImage(deps = {}) {
3485
3538
  const spawn = deps.spawn ?? Bun.spawn;
3486
3539
  const maxBytes = deps.maxBytes ?? DEFAULT_MAX_IMAGE_BYTES;
3487
3540
  const timeoutMs = deps.timeoutMs ?? DEFAULT_CLIPBOARD_TIMEOUT_MS;
3488
- const tempDir = deps.tempDir ?? join(tmpdir(), "nolo-clipboard");
3489
- const { command } = getClipboardImageCommand({
3541
+ const tempDir = deps.tempDir ?? getDefaultClipboardTempDir();
3542
+ const { command, outputFormat } = getClipboardImageCommand({
3490
3543
  env,
3491
3544
  platform,
3492
3545
  which
@@ -3520,6 +3573,12 @@ async function readClipboardImage(deps = {}) {
3520
3573
  timeoutPromise
3521
3574
  ]);
3522
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
+ }
3523
3582
  throw new ClipboardImageError(
3524
3583
  "empty-clipboard",
3525
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()}` : "")
@@ -3538,6 +3597,23 @@ async function readClipboardImage(deps = {}) {
3538
3597
  `\u8BFB\u53D6\u526A\u8D34\u677F\u56FE\u50CF\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
3539
3598
  );
3540
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
+ }
3541
3617
  const compressResult = await compressImage(stdoutBuf, "image/png");
3542
3618
  const finalBuf = compressResult.buffer;
3543
3619
  const finalMime = compressResult.mime;
@@ -3598,7 +3674,7 @@ function parseStatusOutput(branch, status) {
3598
3674
  return { branch, modified, untracked };
3599
3675
  }
3600
3676
  function execFileUtf8(file, args, cwd) {
3601
- return new Promise((resolve2, reject) => {
3677
+ return new Promise((resolve4, reject) => {
3602
3678
  execFile(
3603
3679
  file,
3604
3680
  args,
@@ -3608,7 +3684,7 @@ function execFileUtf8(file, args, cwd) {
3608
3684
  reject(err);
3609
3685
  return;
3610
3686
  }
3611
- resolve2(typeof stdout === "string" ? stdout : String(stdout ?? ""));
3687
+ resolve4(typeof stdout === "string" ? stdout : String(stdout ?? ""));
3612
3688
  }
3613
3689
  );
3614
3690
  });
@@ -3923,6 +3999,9 @@ function renderKnownAgents(colorEnabled = resolveCliColorEnabled()) {
3923
3999
  }
3924
4000
 
3925
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";
3926
4005
  var PASTE_TOKEN_PREFIX = "\0PASTE\0";
3927
4006
  function applyTuiInputKey(buffer, sequence, key = {}, cursorPos, options) {
3928
4007
  const seq = sequence ?? "";
@@ -4027,6 +4106,16 @@ function applyTuiInputKey(buffer, sequence, key = {}, cursorPos, options) {
4027
4106
  return { buffer, cursorPos: curPos };
4028
4107
  }
4029
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
+ }
4030
4119
  const completed = completeSlashPrefix(buffer) ?? buffer;
4031
4120
  return { buffer: completed, cursorPos: completed.length };
4032
4121
  }
@@ -4079,9 +4168,9 @@ function deleteBufferRange(buffer, start, end, pasteStore) {
4079
4168
  function releasePastesInRange(buffer, start, end, pasteStore) {
4080
4169
  if (!pasteStore || start >= end) return;
4081
4170
  const slice = buffer.slice(start, end);
4082
- const re = /\[paste #(\d+) · (\d+) lines\]/g;
4171
+ COLLAPSED_PASTE_PLACEHOLDER_RE.lastIndex = 0;
4083
4172
  let match;
4084
- while ((match = re.exec(slice)) !== null) {
4173
+ while ((match = COLLAPSED_PASTE_PLACEHOLDER_RE.exec(slice)) !== null) {
4085
4174
  releaseCollapsedPaste(pasteStore, Number(match[1]));
4086
4175
  }
4087
4176
  }
@@ -4135,12 +4224,10 @@ var SLASH_COMMANDS = [
4135
4224
  "/clear",
4136
4225
  "/compact",
4137
4226
  "/theme",
4138
- "/density",
4139
4227
  "/context",
4140
4228
  "/ctx",
4229
+ "/cd",
4141
4230
  "/runtime",
4142
- "/tools",
4143
- "/thinking",
4144
4231
  "/auto",
4145
4232
  "/switch",
4146
4233
  "/agent",
@@ -4178,12 +4265,49 @@ function completeSlashPrefix(buffer) {
4178
4265
  }
4179
4266
  return prefix.length > buffer.length ? prefix : null;
4180
4267
  }
4181
- function completeSlashCommand(buffer) {
4268
+ function completeSlashCommand(buffer, cwd) {
4182
4269
  if (!buffer.startsWith("/")) return [];
4183
4270
  const trimmed = buffer.trim();
4271
+ if (cwd && buffer.startsWith("/cd ")) {
4272
+ const prefix = buffer.slice("/cd ".length);
4273
+ return completeCdPathCandidates(prefix, cwd);
4274
+ }
4184
4275
  if (trimmed.includes(" ")) return [];
4185
4276
  return SLASH_COMMANDS.filter((cmd) => cmd.startsWith(trimmed) && cmd !== trimmed);
4186
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
+ }
4187
4311
  function isLikelySlashCommand(input) {
4188
4312
  const trimmed = input.trim();
4189
4313
  if (!trimmed.startsWith("/")) return false;
@@ -4202,6 +4326,11 @@ function stripImageTokens(input, hints) {
4202
4326
  return compactWhitespace(out);
4203
4327
  }
4204
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
+
4205
4334
  // packages/cli/tui/agentPicker.ts
4206
4335
  function toAgentPickerItems(entries) {
4207
4336
  return entries.map((entry) => ({
@@ -4363,6 +4492,10 @@ function estimateDefaultCliContextTokens(opts = {}) {
4363
4492
 
4364
4493
  // packages/cli/tui/sessionDispatch.ts
4365
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
+ }
4366
4499
  function createInitialTuiState(env = process.env) {
4367
4500
  const agentKey = asOptionalTrimmedString(env.NOLO_AGENT) ?? DEFAULT_TUI_AGENT_KEY;
4368
4501
  const agentName = asOptionalTrimmedString(env.NOLO_AGENT_NAME) ?? "nolo";
@@ -4396,7 +4529,7 @@ function createInitialTuiState(env = process.env) {
4396
4529
  userLanguage: asOptionalTrimmedString(env.NOLO_LANG),
4397
4530
  gitStatus: void 0,
4398
4531
  toolDisplay: normalizeToolDisplayMode(env.NOLO_CLI_TOOLS ?? env.NOLO_TOOLS, "normal"),
4399
- thinkingDisplay: "show",
4532
+ thinkingDisplay: resolveThinkingDisplayMode(env),
4400
4533
  contextWindow: resolveAgentContextWindow({ agentKey, agentName }),
4401
4534
  estimatedContextTokens: estimateDefaultCliContextTokens({
4402
4535
  cwd,
@@ -4520,28 +4653,53 @@ function handleTuiInput(input, state) {
4520
4653
  };
4521
4654
  }
4522
4655
  }
4523
- case "/density": {
4524
- const parts = argText.split(/\s+/);
4525
- const sub = parts[0]?.trim();
4526
- if (!sub) {
4656
+ case "/cd": {
4657
+ if (!argText) {
4527
4658
  return {
4528
4659
  nextState: state,
4529
- output: t("densityCurrent", getActiveDensity())
4660
+ output: `${t("cdCurrent", state.cwd)}
4661
+ ${t("cdUsage")}`
4530
4662
  };
4531
4663
  }
4532
- if (sub === "cozy" || sub === "spacious") {
4533
- setActiveDensity(sub);
4534
- return {
4535
- nextState: state,
4536
- output: t("densitySwitched", sub)
4537
- };
4538
- } 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);
4539
4670
  return {
4540
- nextState: state,
4541
- 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 }
4542
4674
  };
4543
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
+ };
4544
4695
  }
4696
+ case "/density":
4697
+ case "/tools":
4698
+ case "/thinking":
4699
+ return {
4700
+ nextState: state,
4701
+ output: t("displayFixedHint")
4702
+ };
4545
4703
  case "/context":
4546
4704
  case "/ctx":
4547
4705
  return { nextState: state, output: renderContextPanel(state) };
@@ -4557,45 +4715,6 @@ function handleTuiInput(input, state) {
4557
4715
  output: t("runtimeSet", argText)
4558
4716
  };
4559
4717
  }
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
4718
  case "/auto": {
4600
4719
  const normalizedArg = asTrimmedLowercaseString(argText);
4601
4720
  if (!normalizedArg) {
@@ -5004,7 +5123,7 @@ async function probeTerminalBackground(args = {}) {
5004
5123
  if (typeof stdin.setRawMode !== "function") return null;
5005
5124
  if (!isTerminal(stdin)) return null;
5006
5125
  const wasRaw = Boolean(stdin.isRaw);
5007
- return await new Promise((resolve2) => {
5126
+ return await new Promise((resolve4) => {
5008
5127
  let buffer = "";
5009
5128
  let settled = false;
5010
5129
  let timer;
@@ -5017,7 +5136,7 @@ async function probeTerminalBackground(args = {}) {
5017
5136
  if (!wasRaw) stdin.setRawMode?.(false);
5018
5137
  } catch {
5019
5138
  }
5020
- resolve2(result);
5139
+ resolve4(result);
5021
5140
  };
5022
5141
  const onData = (chunk) => {
5023
5142
  buffer += chunk.toString("latin1");
@@ -5941,6 +6060,7 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
5941
6060
  }
5942
6061
  const memoryOverlayLayer = buildMemoryOverlayLayer({ promptBlock: memoryPromptBlock });
5943
6062
  const memoryUseGuidanceLayer = buildMemoryUseGuidanceLayer({ promptBlock: memoryPromptBlock });
6063
+ const cwdNoticeBlocks = state.pendingCwdNotice ? [{ content: state.pendingCwdNotice, cacheScope: "turn" }] : [];
5944
6064
  const contextBlockScopes = partitionScopedBlocks([
5945
6065
  ...renderTurnContextBlocksWithScope(layers),
5946
6066
  ...renderTurnContextBlocksWithScope([memoryUseGuidanceLayer]),
@@ -5948,7 +6068,8 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
5948
6068
  // buildSkillContextBlocks; they stay turn-scope because the user can
5949
6069
  // attach/detach skills between turns.
5950
6070
  ...(skillContextBlocks ?? []).map((content) => content.trim()).filter((content) => content.length > 0).map((content) => ({ content, cacheScope: "turn" })),
5951
- ...renderTurnContextBlocksWithScope([memoryOverlayLayer])
6071
+ ...renderTurnContextBlocksWithScope([memoryOverlayLayer]),
6072
+ ...cwdNoticeBlocks
5952
6073
  ]);
5953
6074
  const result = await agentRunner({
5954
6075
  agentName: effectiveAgentName,
@@ -6012,13 +6133,47 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
6012
6133
  cachedMemoryOverlay: memoryPromptBlock
6013
6134
  };
6014
6135
  }
6015
- 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) {
6016
6167
  const commandPayload = gate.kind === "handoff" ? readCommandActionGatePayload(gate.payload) : null;
6017
6168
  const isConfirmation = gate.kind === "confirm";
6018
6169
  const displayCommand = commandPayload?.displayCommand ?? commandPayload?.command.join(" ") ?? gate.title;
6019
6170
  const isInteractiveHandoff = gate.kind === "handoff" && gate.title === "This command requires an interactive terminal.";
6020
6171
  const title = isInteractiveHandoff ? t("actionGateInteractiveTitle") : gate.title;
6021
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;
6022
6177
  output.write(`
6023
6178
  [nolo] ${t("actionGateNeeded")}
6024
6179
  `);
@@ -6028,44 +6183,32 @@ function waitForActionGate(rl, input, output, gate, spawnRunner) {
6028
6183
  `);
6029
6184
  output.write(` ${displayCommand}
6030
6185
  `);
6031
- output.write(`[nolo] ${t("actionGateEnterHint")}
6186
+ output.write(`[nolo] ${isConfirmation ? t("actionGateConfirmHint") : t("actionGateEnterHint")}
6032
6187
  `);
6033
- 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) => {
6034
6194
  let settled = false;
6035
6195
  const finish = (result) => {
6036
6196
  if (settled) return;
6037
6197
  settled = true;
6038
6198
  rl.off("close", onClose);
6039
6199
  rl.off("SIGINT", onSigint);
6040
- resolve2(result);
6200
+ resolve4(result);
6041
6201
  };
6042
- const cancelResult = (reason) => ({
6043
- content: `action gate cancelled: ${gate.title}`,
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
- });
6202
+ const cancelResult = (reason) => buildGateCancelledResult(gate, reason);
6203
+ const failResult = (message) => buildGateFailedResult(gate, message);
6056
6204
  const onClose = () => finish(cancelResult("readline closed"));
6057
6205
  const onSigint = () => finish(cancelResult("interrupted"));
6058
6206
  rl.once("close", onClose);
6059
6207
  rl.once("SIGINT", onSigint);
6060
- rl.question("", async () => {
6208
+ rl.question("", async (answer) => {
6061
6209
  if (settled) return;
6062
6210
  if (isConfirmation) {
6063
- finish({
6064
- content: `action gate completed: ${gate.title}`,
6065
- metadata: {
6066
- actionGateResult: { gateId: gate.id, status: "completed", output: "confirmed" }
6067
- }
6068
- });
6211
+ finish(isApprovedConfirmationInput(answer) ? buildGateConfirmedResult(gate) : cancelResult("confirmation declined"));
6069
6212
  return;
6070
6213
  }
6071
6214
  if (!commandPayload) {
@@ -6113,48 +6256,23 @@ function isInteractiveInput(input) {
6113
6256
  return Boolean(candidate.isTTY) && typeof candidate.setRawMode === "function";
6114
6257
  }
6115
6258
  function waitForRawActionGate(input, output, gate, spawnRunner, hooks) {
6116
- const commandPayload = gate.kind === "handoff" ? readCommandActionGatePayload(gate.payload) : null;
6117
- const isConfirmation = gate.kind === "confirm";
6118
- const displayCommand = commandPayload?.displayCommand ?? commandPayload?.command.join(" ") ?? gate.title;
6119
- const isInteractiveHandoff = gate.kind === "handoff" && gate.title === "This command requires an interactive terminal.";
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) => {
6259
+ const meta = deriveGateMeta(gate);
6260
+ const { commandPayload, isConfirmation, displayCommand } = meta;
6261
+ writeGatePrompt(output, meta);
6262
+ return new Promise((resolve4) => {
6134
6263
  const rawInput = input;
6135
6264
  let settled = false;
6136
6265
  let commandRunning = false;
6266
+ let confirmationLine = "";
6137
6267
  const finish = (result) => {
6138
6268
  if (settled) return;
6139
6269
  settled = true;
6140
6270
  hooks?.registerToken?.(null);
6141
6271
  input.off("data", onData);
6142
- resolve2(result);
6272
+ resolve4(result);
6143
6273
  };
6144
- const cancel = (reason) => finish({
6145
- content: `action gate cancelled: ${gate.title}`,
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
- });
6274
+ const cancel = (reason) => finish(buildGateCancelledResult(gate, reason));
6275
+ const fail = (message) => finish(buildGateFailedResult(gate, message));
6158
6276
  const runCommand = async () => {
6159
6277
  if (settled || commandRunning) return;
6160
6278
  commandRunning = true;
@@ -6197,21 +6315,26 @@ function waitForRawActionGate(input, output, gate, spawnRunner, hooks) {
6197
6315
  };
6198
6316
  const handleToken = (text) => {
6199
6317
  if (settled || commandRunning) return;
6200
- if (text.includes("") || isConfirmation && text.includes("\x1B")) {
6318
+ if (text.includes(CTRL_C) || isConfirmation && text === ESC) {
6201
6319
  cancel("interrupted");
6202
6320
  return;
6203
6321
  }
6204
- if (text.includes("\r") || text.includes("\n")) {
6205
- if (isConfirmation) {
6206
- finish({
6207
- content: `action gate completed: ${gate.title}`,
6208
- metadata: {
6209
- actionGateResult: { gateId: gate.id, status: "completed", output: "confirmed" }
6210
- }
6211
- });
6212
- } else {
6213
- void runCommand();
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));
6214
6333
  }
6334
+ return;
6335
+ }
6336
+ if (text.includes("\r") || text.includes("\n")) {
6337
+ void runCommand();
6215
6338
  }
6216
6339
  };
6217
6340
  const onData = (chunk) => {
@@ -6225,6 +6348,38 @@ function waitForRawActionGate(input, output, gate, spawnRunner, hooks) {
6225
6348
  }
6226
6349
  });
6227
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
+ }
6228
6383
  async function runOneAgentTurn(ctx, inputMsg, imageUrls, actionGateHandler, confirmDestructiveAction) {
6229
6384
  const scheduleTitlePatchSync = (runResult) => {
6230
6385
  if (!runResult.titlePatchPromise || !runResult.dialogId) return;
@@ -6355,7 +6510,9 @@ async function runOneAgentTurn(ctx, inputMsg, imageUrls, actionGateHandler, conf
6355
6510
  ...runResult.title ? { dialogTitle: runResult.title } : {}
6356
6511
  } : {},
6357
6512
  ...runResult.turnTokens ? { turnTokens: runResult.turnTokens } : {},
6358
- ...runResult.cachedMemoryOverlay !== void 0 ? { cachedMemoryOverlay: runResult.cachedMemoryOverlay } : {}
6513
+ ...runResult.cachedMemoryOverlay !== void 0 ? { cachedMemoryOverlay: runResult.cachedMemoryOverlay } : {},
6514
+ // 本轮已发起 agent 调用(消息已注入),消费切换通知。
6515
+ pendingCwdNotice: void 0
6359
6516
  };
6360
6517
  if (runResult.dialogId && nextDialogKey) {
6361
6518
  ctx.refreshDialogTotalCredits(runResult.dialogId, nextDialogKey);
@@ -6403,7 +6560,11 @@ async function runOneAgentTurn(ctx, inputMsg, imageUrls, actionGateHandler, conf
6403
6560
  // estimatedContextTokens:下一轮若 provider 不返回 usage,context
6404
6561
  // chip 仍显示真实累计占用而不是回退到启动时的静态估算。
6405
6562
  ...runResult.turnTokens && runResult.turnTokens.input > 0 ? { estimatedContextTokens: runResult.turnTokens.input } : {},
6406
- ...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
6407
6568
  };
6408
6569
  if (runResult.dialogId && nextDialogKey) {
6409
6570
  ctx.refreshDialogTotalCredits(runResult.dialogId, nextDialogKey);
@@ -7013,7 +7174,7 @@ function createFixedInput(output, config) {
7013
7174
  const renderInputArea = (buffer, cursorPos) => {
7014
7175
  const colorEnabled = resolveCliColorEnabled();
7015
7176
  const cols = Math.max(1, getColumns());
7016
- const completions = completeSlashCommand(buffer);
7177
+ const completions = completeSlashCommand(buffer, config.getCwd?.());
7017
7178
  const sections = [];
7018
7179
  if (completions.length > 0) {
7019
7180
  sections.push(fitAnsiLine(dimCliText(completions.join(" "), colorEnabled), cols));
@@ -7034,6 +7195,10 @@ function createFixedInput(output, config) {
7034
7195
  for (const line of queueLines) {
7035
7196
  sections.push(fitAnsiLine(line.replace(/\r?\n/g, " "), cols));
7036
7197
  }
7198
+ const attachmentLine = config.getAttachmentLine?.() ?? null;
7199
+ if (attachmentLine) {
7200
+ sections.push(fitAnsiLine(attachmentLine.replace(/\r?\n/g, " "), cols));
7201
+ }
7037
7202
  const promptRaw = t("promptLabel");
7038
7203
  const promptWidth = displayWidth(promptRaw);
7039
7204
  const prompt = colorEnabled ? `${themeColorSequence("accent")}${promptRaw}\x1B[39m` : promptRaw;
@@ -7083,7 +7248,7 @@ function createFixedInput(output, config) {
7083
7248
  }
7084
7249
  const text = sections.join("\n");
7085
7250
  const lines = sections.length;
7086
- 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);
7087
7252
  return { text, lines, cursorCol, cursorRow: headerRows + cursorRow };
7088
7253
  };
7089
7254
  const repaintAt = (buffer, cursorPos, force = false) => {
@@ -7493,6 +7658,7 @@ async function runSubmittedSlashLine(line, host, actionGateHandler, confirmDestr
7493
7658
  turnCtx,
7494
7659
  emitCommandOutput,
7495
7660
  renderHistoryToOutput,
7661
+ scheduleRender,
7496
7662
  refreshDialogTotalCredits,
7497
7663
  persistExplicitAgentSwitch,
7498
7664
  persistAgentSelection: persistAgentSelection2,
@@ -7666,6 +7832,18 @@ async function runSubmittedSlashLine(line, host, actionGateHandler, confirmDestr
7666
7832
  emitCommandOutput(t("themeRefreshFailed"));
7667
7833
  }
7668
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
+ }
7669
7847
  if (result.action?.type === "pick-agent") {
7670
7848
  try {
7671
7849
  const pickResult = await dialogHost.run(
@@ -8375,6 +8553,7 @@ async function runTuiWorkspace(options) {
8375
8553
  turnCtx,
8376
8554
  emitCommandOutput,
8377
8555
  renderHistoryToOutput,
8556
+ scheduleRender,
8378
8557
  refreshDialogTotalCredits,
8379
8558
  persistExplicitAgentSwitch,
8380
8559
  persistAgentSelection,
@@ -8407,9 +8586,16 @@ async function runTuiWorkspace(options) {
8407
8586
  return dimCliText(` \u2937 ${i + 1}. ${oneLine}`, colorEnabled);
8408
8587
  });
8409
8588
  },
8589
+ getAttachmentLine: () => {
8590
+ const line = formatComposerAttachmentLine(state.attachedImages);
8591
+ return line ? dimCliText(line, resolveCliColorEnabled()) : null;
8592
+ },
8410
8593
  onInputLinesChange: () => {
8411
8594
  renderHistoryToOutput();
8412
- }
8595
+ },
8596
+ // /cd 路径补全候选行渲染需要当前 cwd(读 state 而非入参,保证
8597
+ // /cd 切换后立即以新 cwd 渲染,composer 不必重建)。
8598
+ getCwd: () => state.cwd
8413
8599
  });
8414
8600
  fixedInput = {
8415
8601
  ...baseFixedInput,
@@ -8488,14 +8674,22 @@ async function runTuiWorkspace(options) {
8488
8674
  modalOwnsKeyboard = false;
8489
8675
  };
8490
8676
  const actionGateHandler = async (gate) => {
8677
+ if (state.autoConfirm && isAutoConfirmableFileWriteGate(gate)) {
8678
+ return buildGateConfirmedResult(gate);
8679
+ }
8491
8680
  modalOwnsKeyboard = true;
8492
8681
  try {
8493
- return await waitForRawActionGate(input, output, gate, spawnRunner, {
8494
- beforeSubprocess: () => fixedInput.pause(),
8495
- afterSubprocess: () => fixedInput.resumeFromSubprocess(),
8682
+ return await resolveActionGate(gate, {
8683
+ dialogHost,
8684
+ input,
8685
+ output,
8686
+ spawnRunner,
8496
8687
  registerToken: (handler) => {
8497
8688
  rawActionGateTokenHandler = handler;
8498
- }
8689
+ },
8690
+ pauseComposer: () => fixedInput.pause(),
8691
+ resumeComposerFromSubprocess: () => fixedInput.resumeFromSubprocess(),
8692
+ resumeComposerFromDialog: () => fixedInput.resumeFromDialog()
8499
8693
  });
8500
8694
  } finally {
8501
8695
  releaseKeyboardToComposer();
@@ -8693,6 +8887,19 @@ ${err.message}` : ""}`
8693
8887
  output.write(`${refreshMsg}
8694
8888
  `);
8695
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();
8696
8903
  } else if (res.action) {
8697
8904
  output.write(
8698
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"
@@ -8901,8 +9108,20 @@ ${merged}` : merged;
8901
9108
  if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
8902
9109
  return;
8903
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
+ }
8904
9122
  const result = applyTuiInputKey(buffer, sequence, {}, cursorPos, {
8905
- pasteStore
9123
+ pasteStore,
9124
+ cwd: state.cwd
8906
9125
  });
8907
9126
  if (result.redraw) {
8908
9127
  resetHistoryFrameDiffCache(output);
@@ -8925,7 +9144,7 @@ ${merged}` : merged;
8925
9144
  if (busyLock) {
8926
9145
  const trimmedText = submittedText.trim();
8927
9146
  const busySlashCommand = trimmedText.split(/\s+/)[0]?.toLowerCase();
8928
- 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";
8929
9148
  if (isBusyLocalSlash) {
8930
9149
  await handleBusyLocalSlash(submittedText, busySlashCommand);
8931
9150
  return;
@@ -8995,9 +9214,9 @@ ${merged}` : merged;
8995
9214
  input.on("data", onData);
8996
9215
  fixedInput.repaint(buffer, cursorPos);
8997
9216
  refreshGitStatus();
8998
- await new Promise((resolve2) => {
8999
- resolveDone = resolve2;
9000
- if (done) resolve2();
9217
+ await new Promise((resolve4) => {
9218
+ resolveDone = resolve4;
9219
+ if (done) resolve4();
9001
9220
  });
9002
9221
  } finally {
9003
9222
  finish();
@@ -9011,7 +9230,11 @@ ${merged}` : merged;
9011
9230
  for await (const line of rl) {
9012
9231
  const shouldExit = await runSubmittedLine(
9013
9232
  line,
9014
- (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
+ ),
9015
9238
  async (request) => {
9016
9239
  if (state.autoConfirm) return true;
9017
9240
  rl.pause();
@@ -9050,12 +9273,16 @@ ${renderStatusLine(state)}
9050
9273
  }
9051
9274
  async function startTuiWorkspace(options) {
9052
9275
  const themeOwner = ++latestWorkspaceThemeOwner;
9276
+ void sweepStaleClipboardFiles(getDefaultClipboardTempDir()).catch(() => {
9277
+ });
9053
9278
  try {
9054
9279
  return await runTuiWorkspace(options);
9055
9280
  } finally {
9056
9281
  if (themeOwner === latestWorkspaceThemeOwner) {
9057
9282
  resetWorkspaceThemeState();
9058
9283
  }
9284
+ void sweepStaleClipboardFiles(getDefaultClipboardTempDir()).catch(() => {
9285
+ });
9059
9286
  }
9060
9287
  }
9061
9288
  export {