nolo-cli 0.8.0-alpha.21 → 0.8.0-alpha.24

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 (114) hide show
  1. package/{_executeModel-42U3VOVZ.js → _executeModel-2ZAPHFOX.js} +46 -43
  2. package/{action-TLI3V62L.js → action-QM2ONPYZ.js} +9 -9
  3. package/{addContentAction-57RSIIMR.js → addContentAction-UXY3GM53.js} +8 -8
  4. package/{addDialogAgentAction-G3VWEA6E.js → addDialogAgentAction-MUBYOG65.js} +10 -10
  5. package/{agentDeleteCommand-5DD2IM3J.js → agentDeleteCommand-5TFSII4Z.js} +28 -27
  6. package/{agentEmailCommands-6FACJBU4.js → agentEmailCommands-XMXWGUMZ.js} +62 -58
  7. package/{agentListCommands-I6KZYCIG.js → agentListCommands-OWEFQEY4.js} +119 -33
  8. package/{agentMachineCommands-VKLIGMZA.js → agentMachineCommands-DGHYBBFC.js} +27 -26
  9. package/{agentPullCommand-O2MTUAFR.js → agentPullCommand-N6UWRTVL.js} +3 -3
  10. package/{agentRecordCommands-7TZICYDT.js → agentRecordCommands-I433EE5F.js} +53 -49
  11. package/{agentRunCommand-L4OM23KV.js → agentRunCommand-UQSX3IGW.js} +57 -52
  12. package/{agentRunControl-K4VKNEHF.js → agentRunControl-S3G62M6C.js} +3 -3
  13. package/{agentRuntimeLocal-ZYEIPTY4.js → agentRuntimeLocal-73SVOC5U.js} +14 -12
  14. package/{agentSlice-BX7GXSNO.js → agentSlice-JQCS4LR3.js} +9 -9
  15. package/{authCommand-EB5JSOX5.js → authCommand-B6P2XYLN.js} +11 -11
  16. package/{authCommands-XPEHTKSM.js → authCommands-4YKA4GTU.js} +7 -7
  17. package/{chunk-AS4PVWA4.js → chunk-2CVZSC5L.js} +1 -1
  18. package/{chunk-JRKKFRAV.js → chunk-3HPUUWOR.js} +58 -8
  19. package/{chunk-5KLGBAAR.js → chunk-3KGTFX3T.js} +2 -13
  20. package/{chunk-IWX4UW2E.js → chunk-524BDL33.js} +89 -8
  21. package/chunk-6QFNUK2G.js +144 -0
  22. package/{chunk-4QEWT7KD.js → chunk-AISLHYDB.js} +367 -85
  23. package/{chunk-2FTTDCD4.js → chunk-AMJ7LA5D.js} +1 -1
  24. package/{chunk-I6XMPTMR.js → chunk-BFDWXLFF.js} +2 -2
  25. package/{chunk-RL776AGR.js → chunk-BKGNXJAM.js} +1 -1
  26. package/{chunk-RCSJFQPB.js → chunk-BQECZ3Q4.js} +361 -90
  27. package/{chunk-VVX233PH.js → chunk-BT37DVDA.js} +9 -9
  28. package/{chunk-ENOG22KJ.js → chunk-BT57QC5K.js} +18 -20
  29. package/chunk-BYXBJQAS.js +0 -0
  30. package/{chunk-O2ZNTTET.js → chunk-C4DZRHFJ.js} +24 -15
  31. package/{chunk-4XENYTF5.js → chunk-DS3VC4EA.js} +3 -3
  32. package/{chunk-N54XAP77.js → chunk-FVVST32H.js} +1 -1
  33. package/{chunk-P3PKED2I.js → chunk-FZ2G3I4B.js} +11 -13
  34. package/{chunk-AGFH73SK.js → chunk-G7KM6BBZ.js} +1 -1
  35. package/{chunk-2ECQG4YX.js → chunk-GROGNZOA.js} +264 -161
  36. package/chunk-IOQIOZNI.js +42 -0
  37. package/{chunk-AL2KDGR2.js → chunk-IPA2WQGY.js} +201 -98
  38. package/{chunk-7DAJ4AI4.js → chunk-IYMGQ3TN.js} +9 -2
  39. package/{chunk-CKOJQDKR.js → chunk-JEMATI63.js} +1 -1
  40. package/{chunk-7U44LYTR.js → chunk-JTINRTBT.js} +1 -1
  41. package/{chunk-4O3CARJ4.js → chunk-KTHLAJEY.js} +1 -1
  42. package/{chunk-URUUZLPA.js → chunk-L3KWEH3V.js} +8 -8
  43. package/{chunk-XBUBEFNX.js → chunk-L76QNLGU.js} +1 -1
  44. package/{chunk-UB6SN4NJ.js → chunk-LE6JAIXE.js} +6 -6
  45. package/{chunk-2JCXITPK.js → chunk-LRKEOCWY.js} +15 -15
  46. package/{chunk-5EFXE3O7.js → chunk-LTH6AVG7.js} +3 -3
  47. package/{chunk-YPRZSPF3.js → chunk-OCDEWRE2.js} +1 -1
  48. package/{chunk-6KHNO3SK.js → chunk-P2SPC3FT.js} +27 -23
  49. package/{chunk-4IJYA27C.js → chunk-PXZAAB4L.js} +4 -4
  50. package/{chunk-VUW5FBDB.js → chunk-Q7S2ORXX.js} +9 -9
  51. package/{chunk-C3RUNRHT.js → chunk-QWM7YWVT.js} +1 -1
  52. package/{chunk-25LCXTML.js → chunk-RQPZX6KP.js} +2 -2
  53. package/chunk-S3UTETIC.js +4885 -0
  54. package/{chunk-SDJDLO5W.js → chunk-SBVUZC2E.js} +5 -5
  55. package/{chunk-VX3SXJ3N.js → chunk-UBSKX3LP.js} +6 -6
  56. package/{chunk-4ZDM73SD.js → chunk-V2J635L5.js} +1 -1
  57. package/{chunk-DSZUWUYB.js → chunk-VBKJTOAW.js} +1 -1
  58. package/{chunk-FVYY6MCS.js → chunk-VUEXKW6U.js} +17 -17
  59. package/{chunk-7KCUSYT2.js → chunk-WKQGZJVI.js} +7 -7
  60. package/{chunk-GU3PEVGZ.js → chunk-WM7P5OWZ.js} +2639 -1579
  61. package/{chunk-7GNGZRLZ.js → chunk-WTBWWBSF.js} +150 -78
  62. package/{chunk-FSO5B7GH.js → chunk-WVS3GX3B.js} +2 -2
  63. package/chunk-Z3NEXVYE.js +33 -0
  64. package/{createAgentAutomationAction-RJF3N2QY.js → createAgentAutomationAction-GXZNLJZK.js} +26 -24
  65. package/{createDialogAction-DRSPS6CE.js → createDialogAction-KLW42ZGF.js} +26 -24
  66. package/{createPageAction-6B2WMNO3.js → createPageAction-IBJHE6MH.js} +26 -24
  67. package/{dbSlice-Q3TM2FJV.js → dbSlice-R5EJKZBW.js} +7 -7
  68. package/{deleteFile-OW53A6BX.js → deleteFile-WPWVZE4B.js} +5 -5
  69. package/{dialogCommands-4IX4XIZE.js → dialogCommands-7O5PUZRU.js} +23 -22
  70. package/{dialogSlice-3M7KX2TX.js → dialogSlice-GGV7PF53.js} +25 -23
  71. package/{docCreateCommands-SIL3NPGT.js → docCreateCommands-7ZO2NQQF.js} +22 -21
  72. package/{docDeleteCommands-7IOMLUS4.js → docDeleteCommands-YKIXZYBR.js} +23 -22
  73. package/{docReadCommands-FNFXUA5L.js → docReadCommands-5UDONBUL.js} +22 -21
  74. package/{docStore-SV7E34D4.js → docStore-VDWTATFH.js} +25 -23
  75. package/{docUpdateCommands-X2HNDVUC.js → docUpdateCommands-EPZIIQMY.js} +22 -21
  76. package/fetchMessages-KTNM7QAR.js +1 -1
  77. package/{getPricing-3E6UNPKL.js → getPricing-6TOZXPAU.js} +7 -3
  78. package/{handleSendMessageAction-JYETO5RZ.js → handleSendMessageAction-MMATC5V5.js} +30 -28
  79. package/index.js +74 -74
  80. package/{localDialogRead-2VRI24F4.js → localDialogRead-YSKFFRTT.js} +1 -1
  81. package/{machineCommands-RIO3RP5Q.js → machineCommands-PTHPKSIZ.js} +59 -55
  82. package/{machineWsRunDispatch-W7UYLGB5.js → machineWsRunDispatch-M5L2NVWJ.js} +51 -47
  83. package/{memoryCommands-O4MIPIWQ.js → memoryCommands-HG4WKTU7.js} +2 -2
  84. package/{models-4OPOOLFS.js → models-H524GH3U.js} +3 -2
  85. package/package.json +1 -1
  86. package/{read-GW7ZEI23.js → read-5ZMY7FKP.js} +5 -5
  87. package/{readDocTool-HUFU6OCT.js → readDocTool-2NXDK32S.js} +1 -1
  88. package/{readlineWorkspace-27JR7WPW.js → readlineWorkspace-4NXMEOHP.js} +835 -622
  89. package/{removeDialogAgentAction-CXKR4NJI.js → removeDialogAgentAction-KOLQ2R5W.js} +13 -12
  90. package/{runAgentClientLoop-HZFAPJHS.js → runAgentClientLoop-FGUYNGSA.js} +45 -42
  91. package/{runtimeDoctorCommands-LRHLPDRC.js → runtimeDoctorCommands-UWR5UDSM.js} +5 -5
  92. package/{setDialogExtraReferencesAction-6RKUSXHX.js → setDialogExtraReferencesAction-OMTEO7X6.js} +10 -10
  93. package/{setPrimaryDialogAgentAction-T3UTBEFG.js → setPrimaryDialogAgentAction-DOCIS6V4.js} +13 -12
  94. package/{settingSlice-IVKXJXMG.js → settingSlice-UWZJYDOA.js} +8 -8
  95. package/{spaceCommands-E7VOF4LO.js → spaceCommands-S3EB6TOS.js} +29 -28
  96. package/{spaceSlice-TSVROA5U.js → spaceSlice-RG2GSV7K.js} +24 -22
  97. package/{streamAgentChatTurn-3C5RQZJF.js → streamAgentChatTurn-X576C23M.js} +209 -86
  98. package/{tableCommands-O7O2LBEG.js → tableCommands-M3DJUETS.js} +10 -10
  99. package/{toolApiClient-QI3YBNWB.js → toolApiClient-C7P7YSE4.js} +2 -2
  100. package/{tools-EQDEKR6N.js → tools-HURZ2N4O.js} +33 -30
  101. package/{tuiSummaryLlmCaller-RV3THLZF.js → tuiSummaryLlmCaller-DG2LBTBX.js} +8 -6
  102. package/{updateCommands-VOSPKXFF.js → updateCommands-TC5SX42Y.js} +3 -3
  103. package/{updateDialogTitleAction-3XV7VWSZ.js → updateDialogTitleAction-62LVIAUZ.js} +28 -26
  104. package/chunk-BBYVOSU2.js +0 -792
  105. package/chunk-DCOA4QSN.js +0 -105
  106. package/{chunk-DZ3XJVET.js → chunk-3QAF2LTL.js} +3 -3
  107. package/{chunk-EHLHSX7R.js → chunk-5MMZUWSG.js} +3 -3
  108. package/{chunk-ISNCLGUZ.js → chunk-AVMJVOQY.js} +3 -3
  109. package/{chunk-UTLZINAP.js → chunk-DVRHHFEA.js} +10 -10
  110. package/{chunk-4CQLVGRV.js → chunk-ID4G2MAR.js} +3 -3
  111. package/{chunk-5MIDPSSK.js → chunk-JK7VW2FN.js} +3 -3
  112. package/{chunk-TILFE6LV.js → chunk-YOCHZD7D.js} +3 -3
  113. package/{db-HCJ5PRB5.js → db-6QED3ZVO.js} +3 -3
  114. package/{understanding-3CUQQ6SV.js → understanding-ZOESBLF3.js} +3 -3
@@ -3,113 +3,151 @@ import {
3
3
  normalizeDialogRecord,
4
4
  readDialogSnapshot,
5
5
  sortDialogs
6
- } from "./chunk-FVYY6MCS.js";
6
+ } from "./chunk-VUEXKW6U.js";
7
7
  import {
8
+ ANSI_ESCAPE_REGEX,
9
+ CLI_AUTO_TIER_AGENT_KEYS,
8
10
  CLI_AUTO_TIER_AGENT_KEY_TABLE,
9
11
  CLI_IMAGE_AGENT_KEY,
12
+ COLLAPSED_PASTE_PLACEHOLDER_RE,
10
13
  THEME_PALETTES,
14
+ allocateCollapsedPaste,
15
+ applyTerminalOutputToText,
11
16
  buildSkillContextBlocks,
12
17
  classifyCliAutoRoute,
18
+ clearCollapsedPasteStore,
19
+ countPhysicalLines,
20
+ createCollapsedPasteStore,
13
21
  dimCliText,
22
+ displayWidth,
23
+ expandRangeToCollapsedPasteChips,
24
+ findCollapsedPasteSpanAt,
25
+ fitAnsiLine,
14
26
  formatAssistantDisplay,
15
27
  formatTokenCount,
16
28
  getActiveBrightness,
17
29
  getActiveDensity,
18
30
  getActiveThemeName,
19
- getCliLocale,
20
- highlightMarkdown,
21
31
  initCliLocale,
32
+ isBalanceExhaustedError,
22
33
  isQuotaExhaustedError,
23
34
  normalizeThinkingDisplayMode,
24
35
  normalizeToolDisplayMode,
36
+ padOrTruncateToWidth,
25
37
  parseCliLocale,
38
+ releaseCollapsedPaste,
39
+ releaseCollapsedPasteReferences,
26
40
  renderTokenStatus,
41
+ replaceCollapsedPastesWithReferences,
42
+ resolveAgentContextWindow,
43
+ resolveCliAutoFallbackTier,
27
44
  resolveCliColorEnabled,
28
45
  resolveSkillReference,
29
46
  resolveTuiBrightness,
30
47
  runAgentTurn,
31
48
  setActiveBrightness,
32
49
  setActiveDensity,
50
+ setActiveTerminalBaseHex,
33
51
  setActiveThemeName,
34
52
  setCliLocale,
53
+ shouldCollapsePaste,
54
+ stripAnsi,
35
55
  surfaceBackgroundSequence,
36
56
  t,
57
+ takeDisplayWidth,
37
58
  themeColorSequence,
38
- themeText
39
- } from "./chunk-GU3PEVGZ.js";
59
+ themeText,
60
+ truncateAnsi,
61
+ visibleWidth,
62
+ wrapTextToLines,
63
+ wrapTranscriptLine
64
+ } from "./chunk-WM7P5OWZ.js";
40
65
  import "./chunk-6HYVJV7B.js";
41
- import "./chunk-2FTTDCD4.js";
42
66
  import {
43
67
  listFavoriteAgentIdsAcrossServers,
44
68
  listLocalCachedAgents,
45
69
  listRemoteAgents,
46
70
  listRemoteAgentsAcrossServers
47
- } from "./chunk-7DAJ4AI4.js";
48
- import "./chunk-4CQLVGRV.js";
71
+ } from "./chunk-IYMGQ3TN.js";
72
+ import "./chunk-ID4G2MAR.js";
49
73
  import {
50
74
  listUserRecordsFromServers
51
- } from "./chunk-RL776AGR.js";
52
- import "./chunk-DZ3XJVET.js";
53
- import "./chunk-RCSJFQPB.js";
54
- import "./chunk-EHLHSX7R.js";
75
+ } from "./chunk-BKGNXJAM.js";
76
+ import "./chunk-3QAF2LTL.js";
77
+ import {
78
+ createCliLocalRuntimeAdapter
79
+ } from "./chunk-BQECZ3Q4.js";
80
+ import "./chunk-5MMZUWSG.js";
81
+ import "./chunk-CMKUBIGW.js";
82
+ import "./chunk-JBTJAPUF.js";
55
83
  import {
56
84
  getReadableCliDb
57
85
  } from "./chunk-W4NVOSFF.js";
58
86
  import {
59
87
  queryUserRecords,
60
88
  readDbRecord
61
- } from "./chunk-SDJDLO5W.js";
62
- import "./chunk-N54XAP77.js";
89
+ } from "./chunk-SBVUZC2E.js";
90
+ import "./chunk-FVVST32H.js";
63
91
  import {
64
92
  resolveCliAgentKeyInput
65
93
  } from "./chunk-VHBWHNVT.js";
66
94
  import "./chunk-LTRXGFMS.js";
67
- import "./chunk-4O3CARJ4.js";
68
- import "./chunk-CMKUBIGW.js";
69
- import "./chunk-JBTJAPUF.js";
95
+ import "./chunk-KTHLAJEY.js";
70
96
  import "./chunk-ATNTHIZI.js";
71
97
  import "./chunk-JEXVIMYZ.js";
72
- import "./chunk-FSO5B7GH.js";
73
- import "./chunk-C3RUNRHT.js";
74
- import "./chunk-AL2KDGR2.js";
98
+ import {
99
+ prepareTools
100
+ } from "./chunk-WVS3GX3B.js";
101
+ import "./chunk-QWM7YWVT.js";
102
+ import {
103
+ expandEnabledPacks
104
+ } from "./chunk-IPA2WQGY.js";
75
105
  import "./chunk-GAKEBQD6.js";
106
+ import "./chunk-6QFNUK2G.js";
76
107
  import "./chunk-KQHGZWSV.js";
77
- import "./chunk-AS4PVWA4.js";
108
+ import "./chunk-2CVZSC5L.js";
78
109
  import "./chunk-3LY4F7SV.js";
79
110
  import "./chunk-VP4WJWKW.js";
80
111
  import "./chunk-63W5N6P3.js";
81
112
  import "./chunk-VF4K2QE6.js";
82
- import "./chunk-UB6SN4NJ.js";
83
- import "./chunk-ENOG22KJ.js";
113
+ import "./chunk-LE6JAIXE.js";
114
+ import "./chunk-BT57QC5K.js";
84
115
  import "./chunk-VC6OZXO5.js";
85
116
  import "./chunk-OWOHNGCN.js";
86
- import "./chunk-25LCXTML.js";
87
- import "./chunk-HNW7IJSK.js";
88
- import "./chunk-XENYHRUE.js";
89
117
  import {
90
118
  DEFAULT_CONTEXT_WINDOW,
119
+ estimateTokenCount,
91
120
  extractReferenceKeysFromMessage,
92
121
  getModelContextWindow,
93
122
  planCompression,
94
123
  serializeMessageContent
95
- } from "./chunk-2ECQG4YX.js";
124
+ } from "./chunk-GROGNZOA.js";
96
125
  import {
97
126
  buildBuiltinSummaryContent
98
- } from "./chunk-4ZDM73SD.js";
127
+ } from "./chunk-V2J635L5.js";
99
128
  import "./chunk-MRC4Q34M.js";
100
129
  import "./chunk-MTU75CZ2.js";
101
130
  import "./chunk-CWMSB67P.js";
102
- import "./chunk-VUW5FBDB.js";
103
- import "./chunk-5EFXE3O7.js";
131
+ import "./chunk-Q7S2ORXX.js";
132
+ import "./chunk-LTH6AVG7.js";
104
133
  import "./chunk-Z4NR32XR.js";
105
134
  import "./chunk-M76ECQRD.js";
135
+ import "./chunk-RQPZX6KP.js";
136
+ import "./chunk-UBSKX3LP.js";
137
+ import "./chunk-Y5GCAMGP.js";
106
138
  import "./chunk-SOTZ6RDN.js";
107
- import "./chunk-I6XMPTMR.js";
139
+ import "./chunk-BFDWXLFF.js";
108
140
  import "./chunk-UAD2WT54.js";
109
- import "./chunk-DCOA4QSN.js";
141
+ import "./chunk-L3KWEH3V.js";
142
+ import "./chunk-AZPCASRE.js";
143
+ import "./chunk-IOQIOZNI.js";
144
+ import "./chunk-Z3NEXVYE.js";
145
+ import "./chunk-HNW7IJSK.js";
146
+ import "./chunk-XENYHRUE.js";
147
+ import "./chunk-JTINRTBT.js";
110
148
  import {
111
149
  runSelfUpdate
112
- } from "./chunk-CKOJQDKR.js";
150
+ } from "./chunk-JEMATI63.js";
113
151
  import {
114
152
  saveProfileAgentSelection,
115
153
  saveProfileLocale
@@ -118,15 +156,6 @@ import {
118
156
  readPipeText,
119
157
  spawnProcess
120
158
  } from "./chunk-IDZTNRP2.js";
121
- import {
122
- parseUserIdFromAuthToken,
123
- resolveAuthToken,
124
- resolveServerCandidates,
125
- resolveServerUrl
126
- } from "./chunk-ISNCLGUZ.js";
127
- import {
128
- DEFAULT_NOLO_SERVER_URL
129
- } from "./chunk-OLZBQWZU.js";
130
159
  import {
131
160
  buildSkillDiscoveryContextBlock
132
161
  } from "./chunk-AFON4QUU.js";
@@ -134,71 +163,158 @@ import "./chunk-C4FTUXVF.js";
134
163
  import "./chunk-KKY4B2Q2.js";
135
164
  import "./chunk-WZSRQNQG.js";
136
165
  import {
166
+ buildLocalWorkspaceOpenAiTools,
167
+ buildLocalWorkspaceToolset,
137
168
  getProcessRegistry
138
- } from "./chunk-O2ZNTTET.js";
139
- import "./chunk-5KLGBAAR.js";
169
+ } from "./chunk-C4DZRHFJ.js";
170
+ import "./chunk-3KGTFX3T.js";
140
171
  import "./chunk-ZNFJWEN4.js";
141
- import "./chunk-TILFE6LV.js";
172
+ import "./chunk-YOCHZD7D.js";
142
173
  import {
143
174
  readCommandActionGatePayload,
144
175
  resolveAgentImageInputSupport
145
- } from "./chunk-4QEWT7KD.js";
176
+ } from "./chunk-AISLHYDB.js";
146
177
  import "./chunk-OPZYGHUC.js";
147
- import "./chunk-AGFH73SK.js";
148
- import "./chunk-JRKKFRAV.js";
149
- import "./chunk-P3PKED2I.js";
178
+ import {
179
+ buildCurrentTimeBlock,
180
+ buildIdentityBlock,
181
+ buildRuntimeGuidanceBlocks
182
+ } from "./chunk-G7KM6BBZ.js";
183
+ import "./chunk-3HPUUWOR.js";
184
+ import {
185
+ canonicalizeToolNames
186
+ } from "./chunk-FZ2G3I4B.js";
150
187
  import {
151
188
  clipCompactText
152
189
  } from "./chunk-5MMAK75Q.js";
153
- import "./chunk-BBYVOSU2.js";
190
+ import "./chunk-S3UTETIC.js";
154
191
  import "./chunk-5CS33IBL.js";
155
192
  import "./chunk-JZPBVBIY.js";
156
- import "./chunk-IWX4UW2E.js";
157
- import "./chunk-5MIDPSSK.js";
193
+ import "./chunk-BYXBJQAS.js";
194
+ import "./chunk-KX6YBIHG.js";
195
+ import "./chunk-524BDL33.js";
196
+ import "./chunk-O4BU24WZ.js";
197
+ import "./chunk-I42VC5KR.js";
198
+ import "./chunk-WWC7I6TA.js";
199
+ import "./chunk-JK7VW2FN.js";
158
200
  import {
159
201
  extractCustomId
160
202
  } from "./chunk-DFFIZ25G.js";
161
203
  import "./chunk-3ASDTTEI.js";
204
+ import {
205
+ asTrimmedLowercaseString
206
+ } from "./chunk-QSJ3HFEF.js";
162
207
  import {
163
208
  compactWhitespace
164
209
  } from "./chunk-DWLEOOGR.js";
210
+ import "./chunk-AMJ7LA5D.js";
211
+ import {
212
+ parseUserIdFromAuthToken,
213
+ resolveAuthToken,
214
+ resolveServerCandidates,
215
+ resolveServerUrl
216
+ } from "./chunk-AVMJVOQY.js";
217
+ import {
218
+ DEFAULT_NOLO_SERVER_URL
219
+ } from "./chunk-OLZBQWZU.js";
165
220
  import {
166
221
  toErrorMessage
167
222
  } from "./chunk-HNMXYYJR.js";
168
223
  import "./chunk-H33743XH.js";
169
224
  import "./chunk-LM6IYQ3F.js";
170
- import "./chunk-VX3SXJ3N.js";
171
- import "./chunk-Y5GCAMGP.js";
172
- import "./chunk-URUUZLPA.js";
173
- import "./chunk-AZPCASRE.js";
174
- import "./chunk-7U44LYTR.js";
175
- import "./chunk-KX6YBIHG.js";
176
- import "./chunk-O4BU24WZ.js";
177
- import "./chunk-I42VC5KR.js";
178
- import "./chunk-WWC7I6TA.js";
179
- import {
180
- asTrimmedLowercaseString
181
- } from "./chunk-QSJ3HFEF.js";
182
- import "./chunk-DSZUWUYB.js";
183
- import "./chunk-UTLZINAP.js";
225
+ import "./chunk-VBKJTOAW.js";
226
+ import "./chunk-DVRHHFEA.js";
184
227
  import "./chunk-OOSFSFMA.js";
185
228
  import "./chunk-GBRG75BQ.js";
186
- import "./chunk-4SN6J65J.js";
187
- import "./chunk-LCBAMY2M.js";
188
229
  import "./chunk-3EUW3WEO.js";
189
230
  import "./chunk-U6VC4MU4.js";
231
+ import "./chunk-KUG52IAP.js";
232
+ import "./chunk-LCBAMY2M.js";
233
+ import "./chunk-4SN6J65J.js";
190
234
  import "./chunk-CF7PE763.js";
191
235
  import {
192
236
  asOptionalTrimmedString
193
237
  } from "./chunk-G3SUCT4W.js";
194
- import "./chunk-KUG52IAP.js";
195
238
  import "./chunk-4VNS5WPM.js";
196
239
 
197
240
  // packages/cli/tui/readlineWorkspace.ts
198
241
  import { createInterface } from "node:readline";
199
242
  import { stdin as defaultInput, stdout as defaultOutput } from "node:process";
200
- import { existsSync as existsSync2, readFileSync } from "node:fs";
243
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
244
+ import { join as join2 } from "node:path";
245
+
246
+ // packages/cli/client/estimateCliContext.ts
247
+ import { existsSync, readFileSync } from "node:fs";
201
248
  import { join } from "node:path";
249
+ var DEFAULT_CLI_PACKS = ["code", "agent-orchestration"];
250
+ var DEFAULT_CLI_PLATFORM_TOOLS = [
251
+ "ui_ask_choice",
252
+ "exa_search",
253
+ "fetchWebpage"
254
+ ];
255
+ var DEFAULT_FLASH_PROMPT = "\u4F60\u662F\u5171\u4EAB\u7A7A\u95F4\u91CC\u7684\u9AD8\u6027\u4EF7\u6BD4\u901A\u7528 AI \u52A9\u624B\u3002\u4F18\u5148\u5FEB\u901F\u3001\u76F4\u63A5\u3001\u7A33\u5B9A\u5730\u5B8C\u6210\u4EFB\u52A1\uFF1B\u9700\u8981\u63A8\u7406\u65F6\u4FDD\u6301\u6B65\u9AA4\u6E05\u6670\u3002";
256
+ function readAgentsMd(cwd) {
257
+ for (const name of ["AGENTS.md", "agents.md"]) {
258
+ const path = join(cwd, name);
259
+ if (!existsSync(path)) continue;
260
+ try {
261
+ return readFileSync(path, "utf8");
262
+ } catch {
263
+ return "";
264
+ }
265
+ }
266
+ return "";
267
+ }
268
+ function estimateToolSchemas(toolNames) {
269
+ const toolset = buildLocalWorkspaceToolset({
270
+ declaredToolNames: toolNames,
271
+ exposeShellTools: true
272
+ });
273
+ const workspaceTools = buildLocalWorkspaceOpenAiTools({
274
+ toolNames: toolset.toolNames,
275
+ exposeShellTools: true
276
+ });
277
+ const platformNames = DEFAULT_CLI_PLATFORM_TOOLS.filter(
278
+ (name) => toolNames.includes(name)
279
+ );
280
+ const platformTools = platformNames.length > 0 ? prepareTools([...platformNames]) : [];
281
+ return estimateTokenCount(JSON.stringify([...workspaceTools, ...platformTools]));
282
+ }
283
+ function estimateDefaultCliContextTokens(opts = {}) {
284
+ const cwd = opts.cwd?.trim() || process.cwd();
285
+ const model = opts.model?.trim() || "deepseek-v4-flash";
286
+ const agentName = opts.agentName?.trim() || "DeepSeek V4 Flash";
287
+ const agentKey = opts.agentKey?.trim() || "agent-pub-deepseek-v4-flash";
288
+ const prompt = opts.agentPrompt?.trim() || DEFAULT_FLASH_PROMPT;
289
+ const packTools = canonicalizeToolNames(
290
+ expandEnabledPacks([...DEFAULT_CLI_PACKS], [])
291
+ );
292
+ const toolNames = canonicalizeToolNames([
293
+ ...packTools,
294
+ ...DEFAULT_CLI_PLATFORM_TOOLS
295
+ ]);
296
+ const guidance = buildRuntimeGuidanceBlocks(toolNames);
297
+ const systemParts = [
298
+ prompt,
299
+ buildIdentityBlock({
300
+ agentName,
301
+ agentId: agentKey,
302
+ model
303
+ }),
304
+ guidance.startupProtocol,
305
+ guidance.contextLayerContract,
306
+ guidance.emailRegistrationWorkflow,
307
+ guidance.webResearchToolPolicy,
308
+ buildCurrentTimeBlock(/* @__PURE__ */ new Date()),
309
+ readAgentsMd(cwd),
310
+ buildSkillDiscoveryContextBlock(cwd) ?? ""
311
+ ];
312
+ const systemTokens = estimateTokenCount(
313
+ systemParts.filter((part) => part?.trim()).join("\n\n")
314
+ );
315
+ const toolTokens = estimateToolSchemas(toolNames);
316
+ return Math.max(0, systemTokens + toolTokens);
317
+ }
202
318
 
203
319
  // packages/cli/client/compactDialog.ts
204
320
  import { ulid } from "ulid";
@@ -479,6 +595,12 @@ function renderDialogRow(args, colorEnabled = resolveCliColorEnabled()) {
479
595
  function renderDialogOverflow(text, colorEnabled = resolveCliColorEnabled()) {
480
596
  return themeText(` ${text}`, "chrome", colorEnabled);
481
597
  }
598
+ function renderOverflowAbove(count, colorEnabled = resolveCliColorEnabled()) {
599
+ return renderDialogOverflow(t("dialogMoreAbove", String(count)), colorEnabled);
600
+ }
601
+ function renderOverflowBelow(count, colorEnabled = resolveCliColorEnabled()) {
602
+ return renderDialogOverflow(t("dialogMoreBelow", String(count)), colorEnabled);
603
+ }
482
604
  function renderDialogCommand(text, colorEnabled = resolveCliColorEnabled()) {
483
605
  return themeText(` ${text}`, "danger", colorEnabled);
484
606
  }
@@ -577,7 +699,7 @@ function renderSelectDialog(args) {
577
699
  ];
578
700
  const lines = [...titleLines, ""];
579
701
  if (window.start > 0) {
580
- lines.push(renderDialogOverflow(`\u2191 ${window.start} more`));
702
+ lines.push(renderOverflowAbove(window.start));
581
703
  }
582
704
  for (let index = window.start; index < window.end; index += 1) {
583
705
  const item = args.items[index];
@@ -590,7 +712,7 @@ function renderSelectDialog(args) {
590
712
  );
591
713
  }
592
714
  if (window.end < total) {
593
- lines.push("", renderDialogOverflow(`\u2193 ${total - window.end} more`));
715
+ lines.push("", renderOverflowBelow(total - window.end));
594
716
  }
595
717
  return lines.join("\n");
596
718
  }
@@ -819,7 +941,7 @@ async function runSelectDialog(args) {
819
941
  // packages/cli/tui/confirmDialog.ts
820
942
  var FALLBACK_TERMINAL_WIDTH = 80;
821
943
  var WIDE_CHAR = /[ᄀ-ᅟ⺀-〾ぁ-㏿㐀-䶿一-鿿ꀀ-꓏가-힣豈-﫿︰-﹯＀-⦆¢-₩]/;
822
- function displayWidth(text) {
944
+ function displayWidth2(text) {
823
945
  let width = 0;
824
946
  for (const char of text) width += WIDE_CHAR.test(char) ? 2 : 1;
825
947
  return width;
@@ -827,9 +949,9 @@ function displayWidth(text) {
827
949
  function truncateCommand(command, maxWidth) {
828
950
  const indent = 2;
829
951
  const budget = Math.max(10, maxWidth - indent);
830
- if (displayWidth(command) <= budget) return command;
952
+ if (displayWidth2(command) <= budget) return command;
831
953
  const marker = ` ${t("dialogConfirmCommandTruncated")}`;
832
- const keep = Math.max(1, budget - displayWidth(marker));
954
+ const keep = Math.max(1, budget - displayWidth2(marker));
833
955
  let kept = "";
834
956
  let width = 0;
835
957
  for (const char of command) {
@@ -1111,19 +1233,23 @@ function renderTabBar(questions, activeIndex, colorEnabled) {
1111
1233
  }
1112
1234
  return colorEnabled ? themeText(` ${label} `, "muted", colorEnabled) : ` ${label} `;
1113
1235
  });
1114
- const submitLabel = "Submit";
1236
+ const submitLabel = t("askChoiceSubmit");
1115
1237
  return tabs.join(" ") + " " + (colorEnabled ? themeText(submitLabel, "chrome", colorEnabled) : submitLabel);
1116
1238
  }
1117
- function renderFooter(colorEnabled) {
1118
- const hints = "\u21B5 pick/submit \xB7 space toggle \xB7 tab switch \xB7 esc cancel";
1239
+ function renderFooter(multiSelect, colorEnabled) {
1240
+ const hints = multiSelect ? t("askChoiceFooterMulti") : t("askChoiceFooterSingle");
1119
1241
  return colorEnabled ? themeText(` ${hints}`, "chrome", colorEnabled) : ` ${hints}`;
1120
1242
  }
1243
+ function otherRowPrefix(index) {
1244
+ return ` [${index + 1}] ${t("askChoiceOtherLabel")}: `;
1245
+ }
1121
1246
  function renderAskChoiceFrame(state) {
1122
1247
  const colorEnabled = resolveCliColorEnabled();
1123
1248
  const q = state.questions[state.activeIndex];
1124
1249
  const qs = state.questionStates[state.activeIndex];
1125
1250
  const lines = [];
1126
- lines.push(renderDialogTitle("question"));
1251
+ let otherCursor = null;
1252
+ lines.push(renderDialogTitle(t("askChoiceTitle")));
1127
1253
  lines.push("");
1128
1254
  if (state.questions.length > 1) {
1129
1255
  lines.push(renderTabBar(state.questions, state.activeIndex, colorEnabled));
@@ -1134,11 +1260,11 @@ function renderAskChoiceFrame(state) {
1134
1260
  );
1135
1261
  if (q.multiSelect) {
1136
1262
  lines.push(
1137
- colorEnabled ? themeText(" Space to toggle, Enter to confirm selection.", "muted", colorEnabled) : " Space to toggle, Enter to confirm selection."
1263
+ colorEnabled ? themeText(` ${t("askChoiceHintMulti")}`, "muted", colorEnabled) : ` ${t("askChoiceHintMulti")}`
1138
1264
  );
1139
1265
  } else {
1140
1266
  lines.push(
1141
- colorEnabled ? themeText(" Type your answer, then press Enter to save.", "muted", colorEnabled) : " Type your answer, then press Enter to save."
1267
+ colorEnabled ? themeText(` ${t("askChoiceHintSingle")}`, "muted", colorEnabled) : ` ${t("askChoiceHintSingle")}`
1142
1268
  );
1143
1269
  }
1144
1270
  lines.push("");
@@ -1148,9 +1274,7 @@ function renderAskChoiceFrame(state) {
1148
1274
  total: totalRows
1149
1275
  });
1150
1276
  if (window.start > 0) {
1151
- lines.push(
1152
- colorEnabled ? themeText(` \u2191 ${window.start} more`, "chrome", colorEnabled) : ` \u2191 ${window.start} more`
1153
- );
1277
+ lines.push(renderOverflowAbove(window.start));
1154
1278
  }
1155
1279
  for (let i = window.start; i < window.end; i++) {
1156
1280
  if (i < q.choices.length) {
@@ -1168,25 +1292,29 @@ function renderAskChoiceFrame(state) {
1168
1292
  } else {
1169
1293
  const focused = qs.cursorIndex === i;
1170
1294
  const marker = focused ? DIALOG_CURSOR : " ";
1171
- const otherContent = qs.otherFocused ? `${qs.otherText}\u2588` : qs.otherText || "";
1172
- const row = `${marker} [${i + 1}] Other: ${otherContent}`;
1295
+ const plainPrefix = `${marker}${otherRowPrefix(i)}`;
1296
+ const plainRow = `${plainPrefix}${qs.otherText}`;
1173
1297
  if (focused && colorEnabled) {
1174
1298
  lines.push(
1175
- `\x1B[1m${themeColorSequence("accent")}${row}\x1B[0m`
1299
+ `\x1B[1m${themeColorSequence("accent")}${plainRow}\x1B[0m`
1176
1300
  );
1177
1301
  } else {
1178
- lines.push(row);
1302
+ lines.push(plainRow);
1303
+ }
1304
+ if (qs.otherFocused) {
1305
+ otherCursor = {
1306
+ lineIndex: lines.length - 1,
1307
+ col: displayWidth(plainPrefix + qs.otherText)
1308
+ };
1179
1309
  }
1180
1310
  }
1181
1311
  }
1182
1312
  if (window.end < totalRows) {
1183
- lines.push(
1184
- colorEnabled ? themeText(` \u2193 ${totalRows - window.end} more`, "chrome", colorEnabled) : ` \u2193 ${totalRows - window.end} more`
1185
- );
1313
+ lines.push(renderOverflowBelow(totalRows - window.end));
1186
1314
  }
1187
1315
  lines.push("");
1188
- lines.push(renderFooter(colorEnabled));
1189
- return lines.join("\n");
1316
+ lines.push(renderFooter(q.multiSelect, colorEnabled));
1317
+ return { text: lines.join("\n"), otherCursor };
1190
1318
  }
1191
1319
  async function runAskChoiceDialog(args) {
1192
1320
  const { request } = args;
@@ -1199,9 +1327,6 @@ async function runAskChoiceDialog(args) {
1199
1327
  if (normalized.questions.length === 0) {
1200
1328
  return { kind: "cancelled" };
1201
1329
  }
1202
- const q0 = normalized.questions[0];
1203
- if (normalized.questions.length === 1 && !q0.multiSelect && !q0.allowOther) {
1204
- }
1205
1330
  let state = createInitialAskChoiceState(normalized.questions);
1206
1331
  const output = args.output ?? process.stdout;
1207
1332
  const input = args.input ?? process.stdin;
@@ -1215,9 +1340,15 @@ async function runAskChoiceDialog(args) {
1215
1340
  typeof args.bottomRow === "function" ? args.bottomRow() : args.bottomRow ?? 0
1216
1341
  );
1217
1342
  let lastBottomRow = 0;
1343
+ let unanchoredCursorLift = 0;
1344
+ const restoreUnanchoredCursor = () => {
1345
+ if (unanchoredCursorLift <= 0) return;
1346
+ output.write(`\x1B[${unanchoredCursorLift}B`);
1347
+ unanchoredCursorLift = 0;
1348
+ };
1218
1349
  const paint = () => {
1219
1350
  const frame = renderAskChoiceFrame(state);
1220
- const lines = frame.split("\n");
1351
+ const lines = frame.text.split("\n");
1221
1352
  const lineCount = lines.length;
1222
1353
  const canPosition = outputIsTty(output) && typeof output.write === "function";
1223
1354
  if (bottomAnchored && canPosition) {
@@ -1234,22 +1365,37 @@ async function runAskChoiceDialog(args) {
1234
1365
  }
1235
1366
  lastBottomRow = anchorRow;
1236
1367
  renderedLineCount = lineCount;
1368
+ unanchoredCursorLift = 0;
1369
+ if (frame.otherCursor) {
1370
+ const cursorRow = anchorRow - (lineCount - 1 - frame.otherCursor.lineIndex);
1371
+ if (cursorRow >= 1) {
1372
+ output.write(`\x1B[${cursorRow};${frame.otherCursor.col + 1}H`);
1373
+ }
1374
+ }
1237
1375
  return;
1238
1376
  }
1239
1377
  if (canPosition) {
1378
+ restoreUnanchoredCursor();
1240
1379
  for (let i = 0; i < renderedLineCount; i++) {
1241
1380
  output.write("\x1B[1A\x1B[2K");
1242
1381
  }
1243
- output.write(`${frame}
1382
+ output.write(`${frame.text}
1244
1383
  `);
1245
1384
  renderedLineCount = lineCount;
1385
+ if (frame.otherCursor) {
1386
+ const linesFromBottom = lineCount - frame.otherCursor.lineIndex;
1387
+ output.write(`\x1B[${linesFromBottom}A`);
1388
+ output.write(`\x1B[${frame.otherCursor.col + 1}G`);
1389
+ unanchoredCursorLift = linesFromBottom;
1390
+ }
1246
1391
  return;
1247
1392
  }
1248
1393
  if (typeof output.write === "function") {
1249
- output.write(`${frame}
1394
+ output.write(`${frame.text}
1250
1395
  `);
1251
1396
  }
1252
1397
  renderedLineCount = lineCount;
1398
+ unanchoredCursorLift = 0;
1253
1399
  };
1254
1400
  const resizeTarget = output;
1255
1401
  const onOutputResize = () => paint();
@@ -1277,6 +1423,9 @@ async function runAskChoiceDialog(args) {
1277
1423
  continue;
1278
1424
  }
1279
1425
  let action = null;
1426
+ const qs = state.questionStates[state.activeIndex];
1427
+ const q = state.questions[state.activeIndex];
1428
+ const isOtherRow = qs.cursorIndex >= q.choices.length;
1280
1429
  if (isCancel(sequence)) {
1281
1430
  action = { type: "CANCEL" };
1282
1431
  } else if (sequence === CSI_TAB) {
@@ -1288,11 +1437,17 @@ async function runAskChoiceDialog(args) {
1288
1437
  } else if (isArrowDown(sequence)) {
1289
1438
  action = { type: "MOVE_CURSOR", delta: 1 };
1290
1439
  } else if (sequence === CSI_SPACE) {
1291
- action = { type: "TOGGLE_AT_CURSOR" };
1440
+ if (qs.otherFocused) {
1441
+ action = {
1442
+ type: "SET_OTHER_TEXT",
1443
+ text: qs.otherText + " "
1444
+ };
1445
+ } else if (q.multiSelect) {
1446
+ action = { type: "TOGGLE_AT_CURSOR" };
1447
+ } else if (isOtherRow) {
1448
+ action = { type: "FOCUS_OTHER" };
1449
+ }
1292
1450
  } else if (isSubmit(sequence)) {
1293
- const qs = state.questionStates[state.activeIndex];
1294
- const q = state.questions[state.activeIndex];
1295
- const isOtherRow = qs.cursorIndex >= q.choices.length;
1296
1451
  if (qs.otherFocused) {
1297
1452
  action = { type: "BLUR_OTHER" };
1298
1453
  } else if (isOtherRow && !qs.otherFocused) {
@@ -1303,27 +1458,29 @@ async function runAskChoiceDialog(args) {
1303
1458
  action = { type: "SELECT_AT_CURSOR" };
1304
1459
  }
1305
1460
  } else if (sequence === CSI_BACKSPACE || sequence === CSI_BACKSPACE_ALT) {
1306
- const qs = state.questionStates[state.activeIndex];
1307
1461
  if (qs.otherFocused && qs.otherText.length > 0) {
1462
+ const chars = Array.from(qs.otherText);
1308
1463
  action = {
1309
1464
  type: "SET_OTHER_TEXT",
1310
- text: qs.otherText.slice(0, -1)
1465
+ text: chars.slice(0, -1).join("")
1311
1466
  };
1312
1467
  }
1313
1468
  } else if (sequence.length >= 1 && sequence.charCodeAt(0) >= 32 && !sequence.startsWith("\x1B")) {
1314
- const qs = state.questionStates[state.activeIndex];
1315
- if (qs.otherFocused) {
1469
+ if (qs.otherFocused || isOtherRow && q.allowOther) {
1316
1470
  const cleaned = sequence.replace(/[\x00-\x1f\x7f]/g, "");
1317
1471
  if (cleaned) {
1472
+ if (!qs.otherFocused) {
1473
+ state = askChoiceReducer(state, { type: "FOCUS_OTHER" });
1474
+ }
1475
+ const currentText = state.questionStates[state.activeIndex].otherText;
1318
1476
  action = {
1319
1477
  type: "SET_OTHER_TEXT",
1320
- text: qs.otherText + cleaned
1478
+ text: currentText + cleaned
1321
1479
  };
1322
1480
  }
1323
1481
  }
1324
1482
  }
1325
1483
  if (action) {
1326
- const prev = state;
1327
1484
  state = askChoiceReducer(state, action);
1328
1485
  if (state.phase !== "active") break;
1329
1486
  if (action.type === "BLUR_OTHER" && state.activeIndex >= state.questions.length - 1 && canSubmit(state)) {
@@ -1347,6 +1504,7 @@ async function runAskChoiceDialog(args) {
1347
1504
  renderedLineCount
1348
1505
  );
1349
1506
  } else {
1507
+ restoreUnanchoredCursor();
1350
1508
  for (let i = 0; i < renderedLineCount; i++) {
1351
1509
  output.write("\x1B[1A\x1B[2K");
1352
1510
  }
@@ -1360,13 +1518,13 @@ async function runAskChoiceDialog(args) {
1360
1518
  }
1361
1519
  if (result.answers.length === 1) {
1362
1520
  const a = result.answers[0];
1521
+ const q = normalized.questions[0];
1522
+ const labels = a.selectedIds.map((id) => q.choices.find((c) => c.id === id)?.label ?? "").filter(Boolean);
1523
+ if (a.otherText) labels.push(a.otherText);
1363
1524
  return {
1364
1525
  kind: "selected",
1365
1526
  userMessage: a.userMessage,
1366
- label: a.selectedIds.map((id) => {
1367
- const q = normalized.questions[0];
1368
- return q.choices.find((c) => c.id === id)?.label ?? "";
1369
- }).join(", ") || a.otherText || ""
1527
+ label: labels.join(", ") || a.otherText || ""
1370
1528
  };
1371
1529
  }
1372
1530
  return {
@@ -1424,8 +1582,13 @@ function createActivityIndicator(deps) {
1424
1582
  let lastActivityAt = 0;
1425
1583
  let frameIndex = 0;
1426
1584
  let timer = null;
1585
+ let stopping = false;
1427
1586
  const elapsedSecFrom = (startedAt) => startedAt > 0 ? Math.max(0, Math.floor((now() - startedAt) / 1e3)) : 0;
1428
1587
  const tick = () => {
1588
+ if (stopping) {
1589
+ deps.onRepaint();
1590
+ return;
1591
+ }
1429
1592
  frameIndex += 1;
1430
1593
  if (explicitLabel === null && !fallbackActive && deps.isTurnActive() && lastActivityAt > 0 && now() - lastActivityAt >= fallbackDelayMs) {
1431
1594
  fallbackActive = true;
@@ -1454,9 +1617,21 @@ function createActivityIndicator(deps) {
1454
1617
  ensureTimer();
1455
1618
  deps.onRepaint();
1456
1619
  };
1620
+ const markStopping = () => {
1621
+ stopping = true;
1622
+ deps.onRepaint();
1623
+ };
1624
+ const isStopping = () => stopping;
1457
1625
  const getView = () => {
1458
1626
  if (!deps.isTurnActive()) return null;
1459
1627
  const frame = ACTIVITY_FRAMES[frameIndex % ACTIVITY_FRAMES.length];
1628
+ if (stopping) {
1629
+ return {
1630
+ frame,
1631
+ label: deps.stoppingLabel(),
1632
+ elapsedSec: 0
1633
+ };
1634
+ }
1460
1635
  if (explicitLabel !== null) {
1461
1636
  return {
1462
1637
  frame,
@@ -1483,8 +1658,9 @@ function createActivityIndicator(deps) {
1483
1658
  fallbackActive = false;
1484
1659
  fallbackStartedAt = 0;
1485
1660
  lastActivityAt = 0;
1661
+ stopping = false;
1486
1662
  };
1487
- return { report, getView, stop };
1663
+ return { report, getView, markStopping, isStopping, stop };
1488
1664
  }
1489
1665
 
1490
1666
  // packages/ai/agent/utils/sortUtils.ts
@@ -1724,13 +1900,25 @@ function findAgentCatalogEntry(entries, rawTarget) {
1724
1900
  if (!target) return null;
1725
1901
  if (/^\d+$/.test(target)) {
1726
1902
  const entry = entries[Number(target) - 1];
1727
- return entry ? { name: entry.name, key: entry.key } : null;
1903
+ return entry ? {
1904
+ name: entry.name,
1905
+ key: entry.key,
1906
+ model: entry.model,
1907
+ ...entry.apiSource ? { apiSource: entry.apiSource } : entry.kind === "platform" ? { apiSource: "platform" } : {}
1908
+ } : null;
1728
1909
  }
1729
1910
  const lower = target.toLowerCase();
1730
1911
  const byName = entries.find(
1731
1912
  (entry) => entry.name.toLowerCase() === lower || entry.key.toLowerCase() === lower || entry.key.toLowerCase().endsWith(`-${lower}`)
1732
1913
  );
1733
- if (byName) return { name: byName.name, key: byName.key };
1914
+ if (byName) {
1915
+ return {
1916
+ name: byName.name,
1917
+ key: byName.key,
1918
+ model: byName.model,
1919
+ ...byName.apiSource ? { apiSource: byName.apiSource } : byName.kind === "platform" ? { apiSource: "platform" } : {}
1920
+ };
1921
+ }
1734
1922
  if (target.startsWith("agent-") || target.startsWith("agent-pub-")) {
1735
1923
  return { name: target, key: target };
1736
1924
  }
@@ -1789,6 +1977,8 @@ async function runAgentPicker(args) {
1789
1977
  kind: "selected",
1790
1978
  name: selected.name,
1791
1979
  key: selected.key,
1980
+ model: selected.model,
1981
+ apiSource: selected.apiSource ?? (selected.kind === "platform" ? "platform" : void 0),
1792
1982
  entries
1793
1983
  };
1794
1984
  }
@@ -1798,7 +1988,11 @@ function resolveAgentSwitchTarget(rawTarget, catalogEntries = []) {
1798
1988
  const aliasEntry = catalogEntries.find((entry) => entry.key === resolvedKey);
1799
1989
  return {
1800
1990
  name: aliasEntry?.name ?? asTrimmedLowercaseString(rawTarget),
1801
- key: resolvedKey
1991
+ key: resolvedKey,
1992
+ ...aliasEntry?.model ? { model: aliasEntry.model } : {},
1993
+ ...aliasEntry ? {
1994
+ ...aliasEntry.apiSource ? { apiSource: aliasEntry.apiSource } : aliasEntry.kind === "platform" ? { apiSource: "platform" } : {}
1995
+ } : {}
1802
1996
  };
1803
1997
  }
1804
1998
  return findAgentCatalogEntry(catalogEntries, rawTarget);
@@ -1811,10 +2005,10 @@ function formatDialogTimestamp(value, now = Date.now()) {
1811
2005
  const time = typeof value === "number" ? value : Date.parse(value);
1812
2006
  if (!Number.isFinite(time) || time <= 0) return "";
1813
2007
  const deltaSeconds = Math.max(0, Math.floor((now - time) / 1e3));
1814
- if (deltaSeconds < 60) return "just now";
1815
- if (deltaSeconds < 3600) return `${Math.floor(deltaSeconds / 60)}m ago`;
1816
- if (deltaSeconds < 86400) return `${Math.floor(deltaSeconds / 3600)}h ago`;
1817
- if (deltaSeconds < 30 * 86400) return `${Math.floor(deltaSeconds / 86400)}d ago`;
2008
+ if (deltaSeconds < 60) return t("timeJustNow");
2009
+ if (deltaSeconds < 3600) return t("timeMinutesAgo", String(Math.floor(deltaSeconds / 60)));
2010
+ if (deltaSeconds < 86400) return t("timeHoursAgo", String(Math.floor(deltaSeconds / 3600)));
2011
+ if (deltaSeconds < 30 * 86400) return t("timeDaysAgo", String(Math.floor(deltaSeconds / 86400)));
1818
2012
  const date = new Date(time);
1819
2013
  const pad = (part) => String(part).padStart(2, "0");
1820
2014
  return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
@@ -1831,7 +2025,7 @@ function toDialogPickerItems(dialogs) {
1831
2025
  }
1832
2026
  function renderDialogList(dialogs) {
1833
2027
  if (dialogs.length === 0) return t("noDialogsYet");
1834
- const lines = ["Recent dialogs:"];
2028
+ const lines = [t("recentDialogs")];
1835
2029
  for (const [index, dialog] of dialogs.entries()) {
1836
2030
  const stamp = formatDialogTimestamp(dialog.updatedAt ?? dialog.createdAt);
1837
2031
  lines.push(
@@ -1839,7 +2033,7 @@ function renderDialogList(dialogs) {
1839
2033
  ` id=${dialog.id}`
1840
2034
  );
1841
2035
  }
1842
- lines.push("", "Tip: run /history to pick one interactively, or paste an id after /resume.");
2036
+ lines.push("", t("dialogListTip"));
1843
2037
  return lines.join("\n");
1844
2038
  }
1845
2039
  async function fetchRecentDialogs(args) {
@@ -1920,7 +2114,7 @@ async function runDialogPicker(args) {
1920
2114
  }
1921
2115
 
1922
2116
  // packages/cli/tui/pasteImage.ts
1923
- import { existsSync } from "node:fs";
2117
+ import { existsSync as existsSync2 } from "node:fs";
1924
2118
  import { readFile, stat } from "node:fs/promises";
1925
2119
  import { homedir } from "node:os";
1926
2120
  import { isAbsolute, resolve } from "node:path";
@@ -1975,7 +2169,7 @@ function detectImagePaths(line, cwd) {
1975
2169
  if (!token.raw) continue;
1976
2170
  const resolved = resolveImageSource(token.decoded, cwd);
1977
2171
  if (!isImagePath(resolved)) continue;
1978
- if (!existsSync(resolved)) continue;
2172
+ if (!existsSync2(resolved)) continue;
1979
2173
  out.push({ raw: token.raw, resolvedPath: resolved });
1980
2174
  }
1981
2175
  return out;
@@ -2191,271 +2385,6 @@ function detectGitStatus(cwd) {
2191
2385
  }
2192
2386
  }
2193
2387
 
2194
- // packages/cli/tui/tuiAnsi.ts
2195
- var ANSI_ESCAPE_REGEX = /\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/g;
2196
- var OSC_HYPERLINK_REGEX = /\x1b\]8;;[^\x07\x1b]*(?:\x07|\x1b\\)\x1b\]8;;(?:\x07|\x1b\\)|\x1b\]8;;[^\x07\x1b]*(?:\x07|\x1b\\)/g;
2197
- function stripAnsi(text) {
2198
- return text.replace(OSC_HYPERLINK_REGEX, "").replace(ANSI_ESCAPE_REGEX, "");
2199
- }
2200
- var SGR_SEQUENCE_REGEX = /^\x1b\[[0-9;]*m/;
2201
- var TRAILING_SGR_REGEX = /(?:\x1b\[[0-9;]*m)+$/;
2202
- function applyTerminalOutputToText(existing, chunk) {
2203
- if (!chunk) return existing;
2204
- let text = existing;
2205
- let index = 0;
2206
- while (index < chunk.length) {
2207
- if (chunk[index] === "\x1B") {
2208
- const sgr = SGR_SEQUENCE_REGEX.exec(chunk.slice(index));
2209
- if (sgr) {
2210
- text += sgr[0];
2211
- index += sgr[0].length;
2212
- continue;
2213
- }
2214
- const osc = chunk.slice(index).match(/^\x1b\]8;;[^\x07\x1b]*(?:\x07|\x1b\\)/);
2215
- if (osc) {
2216
- index += osc[0].length;
2217
- continue;
2218
- }
2219
- const csi = chunk.slice(index).match(/^\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/);
2220
- if (csi) {
2221
- index += csi[0].length;
2222
- continue;
2223
- }
2224
- index += 1;
2225
- continue;
2226
- }
2227
- const ch = chunk[index];
2228
- if (ch === "\r") {
2229
- const lastNl = text.lastIndexOf("\n");
2230
- text = lastNl === -1 ? "" : text.slice(0, lastNl + 1);
2231
- index += 1;
2232
- continue;
2233
- }
2234
- if (ch === "\n") {
2235
- text += "\n";
2236
- index += 1;
2237
- continue;
2238
- }
2239
- if (ch === "\b") {
2240
- const trailing = TRAILING_SGR_REGEX.exec(text);
2241
- const sgrTail = trailing ? trailing[0] : "";
2242
- const head = sgrTail ? text.slice(0, -sgrTail.length) : text;
2243
- if (head.length > 0 && head[head.length - 1] !== "\n") {
2244
- text = head.slice(0, -1) + sgrTail;
2245
- }
2246
- index += 1;
2247
- continue;
2248
- }
2249
- const code = ch.charCodeAt(0);
2250
- if (code < 32 && ch !== " " || code === 127) {
2251
- index += 1;
2252
- continue;
2253
- }
2254
- text += ch;
2255
- index += 1;
2256
- }
2257
- return text;
2258
- }
2259
- function displayWidth2(str) {
2260
- let width = 0;
2261
- for (const char of str) {
2262
- const code = char.codePointAt(0) ?? 0;
2263
- if (code < 32 || code === 127) continue;
2264
- if (code >= 4352 && code <= 4447 || // 0x2768-0x2775 (ornamental brackets, incl. the ❯ prompt at U+276F)
2265
- // render narrow in terminals; counting them wide drifts the cursor.
2266
- code >= 9728 && code <= 10175 && !(code >= 10088 && code <= 10101) || // 0x2B00-0x2BFF (Misc Symbols and Arrows, incl. ⬢ U+2B22 used as the
2267
- // status-line agent icon) render double-wide in common monospace fonts;
2268
- // counting ⬢ as width 1 drifts the whole status line right of the icon.
2269
- code >= 11008 && code <= 11263 || code >= 11904 && code <= 12350 || code >= 12352 && code <= 13247 || code >= 13312 && code <= 19903 || code >= 19968 && code <= 42191 || code >= 44032 && code <= 55203 || code >= 63744 && code <= 64255 || code >= 65072 && code <= 65135 || code >= 65281 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 127744 && code <= 129791 || code >= 131072 && code <= 196605 || code >= 196608 && code <= 262141 || // East Asian Ambiguous CJK quotation marks (U+201C/D “ ”, U+2018/9 ‘ ’).
2270
- // In CJK locale + CJK fonts these render double-wide like 全角 punctuation;
2271
- // in English fonts they stay width 1. Follow the active locale so Chinese
2272
- // replies align correctly without breaking English terminal output.
2273
- getCliLocale() === "zh" && (code === 8220 || code === 8221 || code === 8216 || code === 8217)) {
2274
- width += 2;
2275
- } else {
2276
- width += 1;
2277
- }
2278
- }
2279
- return width;
2280
- }
2281
- function visibleWidth(str) {
2282
- return displayWidth2(stripAnsi(str));
2283
- }
2284
- function truncateAnsi(text, maxWidth) {
2285
- if (maxWidth <= 0) return "";
2286
- if (visibleWidth(text) <= maxWidth) return text;
2287
- let width = 0;
2288
- let out = "";
2289
- let i = 0;
2290
- let sawAnsi = false;
2291
- while (i < text.length) {
2292
- if (text[i] === "\x1B" && text[i + 1] === "[") {
2293
- sawAnsi = true;
2294
- let j = i + 2;
2295
- while (j < text.length) {
2296
- const code = text.charCodeAt(j);
2297
- j += 1;
2298
- if (code >= 64 && code <= 126) break;
2299
- }
2300
- out += text.slice(i, j);
2301
- i = j;
2302
- continue;
2303
- }
2304
- const codePoint = text.codePointAt(i) ?? 0;
2305
- const char = String.fromCodePoint(codePoint);
2306
- const charWidth = displayWidth2(char);
2307
- if (width + charWidth > maxWidth) break;
2308
- out += char;
2309
- width += charWidth;
2310
- i += char.length;
2311
- }
2312
- return sawAnsi ? `${out}\x1B[0m` : out;
2313
- }
2314
- function fitAnsiLine(text, width, ellipsis = "\u2026") {
2315
- if (width <= 0) return "";
2316
- if (visibleWidth(text) <= width) return text;
2317
- const ellipsisWidth = displayWidth2(ellipsis);
2318
- if (width < ellipsisWidth) return truncateAnsi(text, width);
2319
- if (width === ellipsisWidth) return truncateAnsi(ellipsis, width) || truncateAnsi(text, width);
2320
- return `${truncateAnsi(text, width - ellipsisWidth)}${ellipsis}`;
2321
- }
2322
- function countPhysicalLines(text, columns) {
2323
- const lines = text.split("\n");
2324
- let total = 0;
2325
- for (const line of lines) {
2326
- const width = displayWidth2(line);
2327
- total += Math.max(1, Math.ceil(width / columns));
2328
- }
2329
- return Math.max(total, 1);
2330
- }
2331
- function takeDisplayWidth(text, width) {
2332
- let used = 0;
2333
- let index = 0;
2334
- for (const char of text) {
2335
- const charWidth = displayWidth2(char);
2336
- if (used + charWidth > width && used > 0) break;
2337
- used += charWidth;
2338
- index += char.length;
2339
- }
2340
- return { prefix: text.slice(0, index), rest: text.slice(index) };
2341
- }
2342
- function padOrTruncateToWidth(text, width) {
2343
- const textWidth = visibleWidth(text);
2344
- if (textWidth > width) {
2345
- return truncateAnsi(text, width);
2346
- }
2347
- return `${text}${" ".repeat(width - textWidth)}`;
2348
- }
2349
- var SGR_RESET_REGEX = /^\x1b\[0?m$/;
2350
- function tokenizeAnsiLine(line) {
2351
- const tokens = [];
2352
- let index = 0;
2353
- while (index < line.length) {
2354
- if (line[index] === "\x1B") {
2355
- const osc = line.slice(index).match(/^\x1b\]8;;[^\x07\x1b]*(?:\x07|\x1b\\)/);
2356
- if (osc) {
2357
- tokens.push({ kind: "sgr", value: osc[0], width: 0 });
2358
- index += osc[0].length;
2359
- continue;
2360
- }
2361
- const sgr = SGR_SEQUENCE_REGEX.exec(line.slice(index));
2362
- if (sgr) {
2363
- tokens.push({ kind: "sgr", value: sgr[0], width: 0 });
2364
- index += sgr[0].length;
2365
- continue;
2366
- }
2367
- }
2368
- const codePoint = line.codePointAt(index) ?? 0;
2369
- const value = String.fromCodePoint(codePoint);
2370
- tokens.push({ kind: "char", value, width: displayWidth2(value) });
2371
- index += value.length;
2372
- }
2373
- return tokens;
2374
- }
2375
- function wrapTranscriptLine(line, columns) {
2376
- if (line === "") return [""];
2377
- const tokens = tokenizeAnsiLine(line);
2378
- const result = [];
2379
- let activeStyles = [];
2380
- const applyStyleToken = (value) => {
2381
- if (SGR_RESET_REGEX.test(value)) {
2382
- activeStyles = [];
2383
- } else {
2384
- activeStyles.push(value);
2385
- }
2386
- };
2387
- let start = 0;
2388
- while (start < tokens.length) {
2389
- if (tokens.slice(start).every((token) => token.kind === "sgr")) {
2390
- if (result.length > 0) break;
2391
- }
2392
- const openingStyles = [...activeStyles];
2393
- let width = 0;
2394
- let end = start;
2395
- let lastBreak = -1;
2396
- while (end < tokens.length) {
2397
- const token = tokens[end];
2398
- if (token.kind === "sgr") {
2399
- end += 1;
2400
- continue;
2401
- }
2402
- if (width + token.width > columns && width > 0) break;
2403
- width += token.width;
2404
- end += 1;
2405
- if (token.value === " " || token.value === " ") {
2406
- lastBreak = end;
2407
- }
2408
- }
2409
- let segmentEnd = end;
2410
- if (end < tokens.length && lastBreak > start) {
2411
- const overflowToken = tokens[end];
2412
- if (overflowToken.kind === "char" && overflowToken.value !== " " && overflowToken.width === 1) {
2413
- segmentEnd = lastBreak;
2414
- }
2415
- }
2416
- if (segmentEnd === start) segmentEnd = start + 1;
2417
- let segment = "";
2418
- let sawStyle = openingStyles.length > 0;
2419
- for (let i = start; i < segmentEnd; i += 1) {
2420
- const token = tokens[i];
2421
- segment += token.value;
2422
- if (token.kind === "sgr") {
2423
- sawStyle = true;
2424
- applyStyleToken(token.value);
2425
- }
2426
- }
2427
- const prefix = openingStyles.join("");
2428
- const needsReset = (sawStyle || activeStyles.length > 0) && !segment.endsWith("\x1B[0m");
2429
- result.push(`${prefix}${segment}${needsReset ? "\x1B[0m" : ""}`);
2430
- start = segmentEnd;
2431
- while (start < tokens.length) {
2432
- const token = tokens[start];
2433
- if (token.kind === "char" && token.value === " ") {
2434
- start += 1;
2435
- continue;
2436
- }
2437
- break;
2438
- }
2439
- }
2440
- return result.length > 0 ? result : [""];
2441
- }
2442
- function wrapTextToLines(text, columns) {
2443
- const result = [];
2444
- for (const logicalLine of text.split("\n")) {
2445
- if (logicalLine === "") {
2446
- result.push("");
2447
- continue;
2448
- }
2449
- let remaining = logicalLine;
2450
- while (remaining.length > 0) {
2451
- const { prefix, rest } = takeDisplayWidth(remaining, columns);
2452
- result.push(prefix);
2453
- remaining = rest;
2454
- }
2455
- }
2456
- return result;
2457
- }
2458
-
2459
2388
  // packages/cli/tui/sessionRender.ts
2460
2389
  function formatCwd(cwd) {
2461
2390
  const parts = cwd.split(/[/\\]/);
@@ -2470,12 +2399,12 @@ function formatElapsedSeconds(totalSeconds) {
2470
2399
  const mins = minutes % 60;
2471
2400
  return `${hours}h ${mins}m`;
2472
2401
  }
2473
- function renderComposerTokenChip(tokens, fallbackContextWindow) {
2402
+ function renderComposerTokenChip(tokens, fallbackContextWindow, estimatedContextTokens) {
2474
2403
  const cw = tokens?.contextWindow ?? fallbackContextWindow;
2475
2404
  if (!cw) {
2476
2405
  return "context: \u2014";
2477
2406
  }
2478
- const used = tokens ? tokens.input + tokens.output : 0;
2407
+ const used = tokens ? tokens.input + tokens.output : Math.max(0, estimatedContextTokens ?? 0);
2479
2408
  const pct = Math.min(100, used / cw * 100);
2480
2409
  const pctText = pct > 0 && pct < 10 ? pct.toFixed(1) : Math.round(pct).toString();
2481
2410
  return `context: ${pctText}% (${formatTokenCount(used)}/${formatTokenCount(cw)})`;
@@ -2497,7 +2426,17 @@ function renderStatusLine(state) {
2497
2426
  const untrackedText = untracked > 0 ? ` ${themeText(`?${untracked}`, "muted", colorEnabled)}` : "";
2498
2427
  parts.push(`${branchText}${modifiedText}${untrackedText}`);
2499
2428
  }
2500
- const tokenSegment = themeText(renderComposerTokenChip(state.turnTokens, state.contextWindow), "muted", colorEnabled);
2429
+ const credits = state.apiSource === "platform" ? state.turnTokens?.credits : void 0;
2430
+ const creditsSuffix = credits !== void 0 && credits > 0 ? ` \xB7 \u26A1 ${credits.toFixed(2)} \u79EF\u5206` : "";
2431
+ const tokenSegment = themeText(
2432
+ renderComposerTokenChip(
2433
+ state.turnTokens,
2434
+ state.contextWindow,
2435
+ state.estimatedContextTokens
2436
+ ) + creditsSuffix,
2437
+ "muted",
2438
+ colorEnabled
2439
+ );
2501
2440
  parts.push(tokenSegment);
2502
2441
  const runningCount = getProcessRegistry().list().filter((p) => p.status === "running").length;
2503
2442
  if (runningCount > 0) {
@@ -2579,7 +2518,7 @@ function renderWelcome(state, frame = 0, maxFrames = 0, columns) {
2579
2518
  const isDark = brightness === "dark";
2580
2519
  let sceneArt = colorEnabled ? buildColoredScene(isDark, frame, maxFrames) : buildPlainScene(isDark, frame, maxFrames);
2581
2520
  if (typeof columns === "number" && columns > 0) {
2582
- const widestSceneCol = sceneArt.split("\n").reduce((max, line) => Math.max(max, displayWidth2(stripAnsi(line))), 0);
2521
+ const widestSceneCol = sceneArt.split("\n").reduce((max, line) => Math.max(max, displayWidth(stripAnsi(line))), 0);
2583
2522
  if (columns < widestSceneCol) sceneArt = "";
2584
2523
  }
2585
2524
  const versionLine = colorEnabled ? `\x1B[1m${themeColorSequence("accent")}nolo\x1B[0m ${state.cliVersion ?? ""} | server ${state.serverUrl}`.replace(" |", " |") : `nolo ${state.cliVersion ?? ""} | server ${state.serverUrl}`.replace(" |", " |");
@@ -2629,19 +2568,19 @@ function renderContextPanel(state, colorEnabled = resolveCliColorEnabled()) {
2629
2568
  ];
2630
2569
  let maxW = 0;
2631
2570
  for (const l of labels) {
2632
- const w = displayWidth2(l);
2571
+ const w = displayWidth(l);
2633
2572
  if (w > maxW) maxW = w;
2634
2573
  }
2635
2574
  const targetWidth = maxW >= 9 ? maxW + 1 : 9;
2636
2575
  const padLabel = (raw) => {
2637
- const w = displayWidth2(raw);
2576
+ const w = displayWidth(raw);
2638
2577
  const padding = " ".repeat(Math.max(0, targetWidth - w));
2639
2578
  return raw + padding;
2640
2579
  };
2641
2580
  const field = (rawLabel, value) => `${themeText(padLabel(rawLabel), "muted", colorEnabled)}${value}`;
2642
2581
  const next = (command, description) => ` ${themeText(command, "accent", colorEnabled)}${themeText(description, "muted", colorEnabled)}`;
2643
2582
  const titleText = t("contextTitle");
2644
- const heading = colorEnabled ? [renderDialogTitle(titleText, true)] : [titleText, "\u2500".repeat(displayWidth2(titleText))];
2583
+ const heading = colorEnabled ? [renderDialogTitle(titleText, true)] : [titleText, "\u2500".repeat(displayWidth(titleText))];
2645
2584
  return [
2646
2585
  ...heading,
2647
2586
  field(labels[0], `${state.agentName} (${state.agentKey})`),
@@ -2684,86 +2623,6 @@ function renderKnownAgents(colorEnabled = resolveCliColorEnabled()) {
2684
2623
  ].join("\n");
2685
2624
  }
2686
2625
 
2687
- // packages/core/collapsedPaste.ts
2688
- var COLLAPSE_PASTE_MIN_LINES = 8;
2689
- var COLLAPSE_PASTE_MIN_CHARS = 400;
2690
- var COLLAPSED_PASTE_PLACEHOLDER_RE = /\[paste #(\d+) · (\d+) lines\]/g;
2691
- function countTextLines(text) {
2692
- if (text.length === 0) return 0;
2693
- let lines = 1;
2694
- for (let i = 0; i < text.length; i += 1) {
2695
- if (text.charCodeAt(i) === 10) lines += 1;
2696
- }
2697
- return lines;
2698
- }
2699
- function shouldCollapsePaste(text, threshold = {}) {
2700
- const minLines = threshold.minLines ?? COLLAPSE_PASTE_MIN_LINES;
2701
- const minChars = threshold.minChars ?? COLLAPSE_PASTE_MIN_CHARS;
2702
- if (text.length === 0) return false;
2703
- if (text.length >= minChars) return true;
2704
- return countTextLines(text) >= minLines;
2705
- }
2706
- function formatCollapsedPastePlaceholder(id, text) {
2707
- const lines = countTextLines(text);
2708
- return `[paste #${id} \xB7 ${lines} lines]`;
2709
- }
2710
- function createCollapsedPasteStore() {
2711
- return { nextId: 1, items: /* @__PURE__ */ new Map() };
2712
- }
2713
- function allocateCollapsedPaste(store, text) {
2714
- const id = store.nextId;
2715
- store.nextId += 1;
2716
- store.items.set(id, text);
2717
- return { id, placeholder: formatCollapsedPastePlaceholder(id, text) };
2718
- }
2719
- function releaseCollapsedPaste(store, id) {
2720
- store.items.delete(id);
2721
- }
2722
- function clearCollapsedPasteStore(store) {
2723
- store.items.clear();
2724
- store.nextId = 1;
2725
- }
2726
- function expandCollapsedPastes(buffer, store) {
2727
- if (store.items.size === 0) return buffer;
2728
- COLLAPSED_PASTE_PLACEHOLDER_RE.lastIndex = 0;
2729
- return buffer.replace(COLLAPSED_PASTE_PLACEHOLDER_RE, (match, idRaw) => {
2730
- const id = Number(idRaw);
2731
- const full = store.items.get(id);
2732
- return full === void 0 ? match : full;
2733
- });
2734
- }
2735
- function expandRangeToCollapsedPasteChips(buffer, start, end) {
2736
- let s = Math.max(0, Math.min(start, end));
2737
- let e = Math.max(s, Math.max(start, end));
2738
- if (s === e) return { start: s, end: e };
2739
- COLLAPSED_PASTE_PLACEHOLDER_RE.lastIndex = 0;
2740
- let match;
2741
- while ((match = COLLAPSED_PASTE_PLACEHOLDER_RE.exec(buffer)) !== null) {
2742
- const chipStart = match.index;
2743
- const chipEnd = chipStart + match[0].length;
2744
- if (chipStart < e && chipEnd > s) {
2745
- if (chipStart < s) s = chipStart;
2746
- if (chipEnd > e) e = chipEnd;
2747
- }
2748
- }
2749
- return { start: s, end: e };
2750
- }
2751
- function findCollapsedPasteSpanAt(buffer, pos, opts = {}) {
2752
- COLLAPSED_PASTE_PLACEHOLDER_RE.lastIndex = 0;
2753
- let match;
2754
- while ((match = COLLAPSED_PASTE_PLACEHOLDER_RE.exec(buffer)) !== null) {
2755
- const start = match.index;
2756
- const end = start + match[0].length;
2757
- const inside = pos > start && pos < end;
2758
- const atEnd = opts.preferLeft && pos === end;
2759
- const atStart = !opts.preferLeft && pos === start;
2760
- if (inside || atEnd || atStart) {
2761
- return { id: Number(match[1]), start, end };
2762
- }
2763
- }
2764
- return null;
2765
- }
2766
-
2767
2626
  // packages/cli/tui/sessionInput.ts
2768
2627
  var PASTE_TOKEN_PREFIX = "\0PASTE\0";
2769
2628
  function applyTuiInputKey(buffer, sequence, key = {}, cursorPos, options) {
@@ -3051,6 +2910,7 @@ function createInitialTuiState(env = process.env) {
3051
2910
  ) || void 0;
3052
2911
  const dialogEnvValue = asOptionalTrimmedString(env.NOLO_DIALOG);
3053
2912
  const explicitDialogKey = asOptionalTrimmedString(env.NOLO_DIALOG_KEY) ?? (dialogEnvValue?.startsWith("dialog-") ? dialogEnvValue : void 0);
2913
+ const autoRouteDefault = env.NOLO_AUTO_ROUTE !== "0";
3054
2914
  return {
3055
2915
  agentKey,
3056
2916
  agentName,
@@ -3076,16 +2936,41 @@ function createInitialTuiState(env = process.env) {
3076
2936
  "hide"
3077
2937
  ),
3078
2938
  toolDisplay: normalizeToolDisplayMode(env.NOLO_CLI_TOOLS ?? env.NOLO_TOOLS, "compact"),
3079
- contextWindow: getModelContextWindow(agentName)
2939
+ contextWindow: resolveAgentContextWindow({
2940
+ agentKey,
2941
+ agentName,
2942
+ autoRouteDefault
2943
+ }),
2944
+ estimatedContextTokens: estimateDefaultCliContextTokens({
2945
+ cwd,
2946
+ agentKey,
2947
+ agentName: autoRouteDefault ? "DeepSeek V4 Flash" : agentName,
2948
+ model: autoRouteDefault ? "deepseek-v4-flash" : void 0
2949
+ }),
2950
+ apiSource: PLATFORM_AGENTS.some((entry) => entry.key === agentKey) ? "platform" : void 0
3080
2951
  };
3081
2952
  }
3082
- function applyAgentSwitch(state, target) {
2953
+ function applyAgentSwitch(state, target, opts) {
2954
+ const autoRouteDefault = opts?.autoRouteDefault ?? process.env.NOLO_AUTO_ROUTE !== "0";
2955
+ const contextWindow = resolveAgentContextWindow({
2956
+ agentKey: target.key,
2957
+ agentName: target.name,
2958
+ model: target.model,
2959
+ autoRouteDefault
2960
+ });
3083
2961
  return {
3084
2962
  nextState: {
3085
2963
  ...state,
3086
2964
  agentName: target.name,
3087
2965
  agentKey: target.key,
3088
- contextWindow: getModelContextWindow(target.name)
2966
+ contextWindow,
2967
+ estimatedContextTokens: estimateDefaultCliContextTokens({
2968
+ cwd: state.cwd,
2969
+ agentKey: target.key,
2970
+ agentName: target.name,
2971
+ model: target.model
2972
+ }),
2973
+ apiSource: target.apiSource
3089
2974
  },
3090
2975
  output: `Switched to ${target.name}. ${state.dialogId ? `Dialog kept: ${state.dialogId}` : "Dialog kept: new"}`
3091
2976
  };
@@ -3147,32 +3032,32 @@ function handleTuiInput(input, state) {
3147
3032
  return {
3148
3033
  nextState: state,
3149
3034
  output: [
3150
- `Current theme: ${getActiveThemeName()} \xB7 ${brightness}`,
3151
- `Usage: /theme <name> | /theme light | /theme dark`,
3152
- `Available themes: ${available}`
3035
+ t("themeCurrent", getActiveThemeName(), brightness),
3036
+ t("themeUsage"),
3037
+ t("themeAvailable", available)
3153
3038
  ].join("\n")
3154
3039
  };
3155
3040
  }
3156
3041
  if (sub === "light" || sub === "dark") {
3157
3042
  setActiveBrightness(sub);
3158
- return { nextState: state, output: `Switched to ${sub} background colors.` };
3043
+ return { nextState: state, output: t("themeBrightnessSwitched", sub) };
3159
3044
  }
3160
3045
  if (sub === "auto") {
3161
3046
  setActiveBrightness(null);
3162
3047
  return {
3163
3048
  nextState: state,
3164
- output: `Background colors follow terminal detection (now: ${resolveTuiBrightness()}).`
3049
+ output: t("themeBrightnessAuto", resolveTuiBrightness())
3165
3050
  };
3166
3051
  }
3167
3052
  if (setActiveThemeName(sub)) {
3168
3053
  return {
3169
3054
  nextState: state,
3170
- output: `Switched to theme: ${sub}`
3055
+ output: t("themeSwitched", sub)
3171
3056
  };
3172
3057
  } else {
3173
3058
  return {
3174
3059
  nextState: state,
3175
- output: `Unknown theme: ${sub}. Available themes: ${available}`
3060
+ output: t("themeUnknown", sub, available)
3176
3061
  };
3177
3062
  }
3178
3063
  }
@@ -3182,20 +3067,19 @@ function handleTuiInput(input, state) {
3182
3067
  if (!sub) {
3183
3068
  return {
3184
3069
  nextState: state,
3185
- output: `Current density: ${getActiveDensity()}
3186
- Usage: /density <cozy|spacious>`
3070
+ output: t("densityCurrent", getActiveDensity())
3187
3071
  };
3188
3072
  }
3189
3073
  if (sub === "cozy" || sub === "spacious") {
3190
3074
  setActiveDensity(sub);
3191
3075
  return {
3192
3076
  nextState: state,
3193
- output: `Switched to layout density: ${sub}`
3077
+ output: t("densitySwitched", sub)
3194
3078
  };
3195
3079
  } else {
3196
3080
  return {
3197
3081
  nextState: state,
3198
- output: `Unknown density: ${sub}. Use 'cozy' or 'spacious'.`
3082
+ output: t("densityUnknown", sub)
3199
3083
  };
3200
3084
  }
3201
3085
  }
@@ -3206,52 +3090,52 @@ Usage: /density <cozy|spacious>`
3206
3090
  if (argText !== "auto" && argText !== "local" && argText !== "server") {
3207
3091
  return {
3208
3092
  nextState: state,
3209
- output: "Usage: /runtime <auto|local|server>"
3093
+ output: t("runtimeUsage")
3210
3094
  };
3211
3095
  }
3212
3096
  return {
3213
3097
  nextState: { ...state, runtimeMode: argText },
3214
- output: `Runtime: ${argText}`
3098
+ output: t("runtimeSet", argText)
3215
3099
  };
3216
3100
  }
3217
3101
  case "/tools": {
3218
3102
  if (!argText) {
3219
3103
  return {
3220
3104
  nextState: state,
3221
- output: `Tool display: ${state.toolDisplay} (hide | compact | verbose)`
3105
+ output: t("toolsCurrent", state.toolDisplay)
3222
3106
  };
3223
3107
  }
3224
3108
  const normalizedArg = asTrimmedLowercaseString(argText);
3225
3109
  if (!["hide", "compact", "verbose", "on", "off"].includes(normalizedArg)) {
3226
3110
  return {
3227
3111
  nextState: state,
3228
- output: "Usage: /tools <hide|compact|verbose>"
3112
+ output: t("toolsUsage")
3229
3113
  };
3230
3114
  }
3231
3115
  const nextMode = normalizeToolDisplayMode(normalizedArg, state.toolDisplay);
3232
3116
  return {
3233
3117
  nextState: { ...state, toolDisplay: nextMode },
3234
- output: `Tool display: ${nextMode}`
3118
+ output: t("toolsSet", nextMode)
3235
3119
  };
3236
3120
  }
3237
3121
  case "/thinking": {
3238
3122
  if (!argText) {
3239
3123
  return {
3240
3124
  nextState: state,
3241
- output: `Thinking display: ${state.thinkingDisplay} (hide | marker | show)`
3125
+ output: t("thinkingCurrent", state.thinkingDisplay)
3242
3126
  };
3243
3127
  }
3244
3128
  const normalizedArg = asTrimmedLowercaseString(argText);
3245
3129
  if (!["hide", "marker", "show", "on", "off"].includes(normalizedArg)) {
3246
3130
  return {
3247
3131
  nextState: state,
3248
- output: "Usage: /thinking <hide|marker|show>"
3132
+ output: t("thinkingUsage")
3249
3133
  };
3250
3134
  }
3251
3135
  const nextMode = normalizeThinkingDisplayMode(normalizedArg, state.thinkingDisplay);
3252
3136
  return {
3253
3137
  nextState: { ...state, thinkingDisplay: nextMode },
3254
- output: `Thinking display: ${nextMode}`
3138
+ output: t("thinkingSet", nextMode)
3255
3139
  };
3256
3140
  }
3257
3141
  case "/tasks":
@@ -3263,14 +3147,14 @@ Usage: /density <cozy|spacious>`
3263
3147
  const stopped = all.filter((p) => p.status !== "running");
3264
3148
  const lines = [];
3265
3149
  if (running.length > 0) {
3266
- lines.push(`Running processes (${running.length}):`);
3150
+ lines.push(t("tasksRunning", String(running.length)));
3267
3151
  for (const p of running) {
3268
3152
  const elapsed = formatElapsedSeconds(Math.floor((Date.now() - p.startedAt) / 1e3));
3269
3153
  lines.push(` pid ${p.pid} ${p.label} running ${elapsed}`);
3270
3154
  }
3271
3155
  }
3272
3156
  if (stopped.length > 0) {
3273
- lines.push(`Stopped/exited (${stopped.length}):`);
3157
+ lines.push(t("tasksStopped", String(stopped.length)));
3274
3158
  for (const p of stopped) {
3275
3159
  const elapsed = formatElapsedSeconds(Math.floor((Date.now() - p.startedAt) / 1e3));
3276
3160
  const exitInfo = p.exitCode !== void 0 ? ` exit ${p.exitCode}` : "";
@@ -3278,38 +3162,38 @@ Usage: /density <cozy|spacious>`
3278
3162
  }
3279
3163
  }
3280
3164
  if (all.length === 0) {
3281
- lines.push("No processes.");
3165
+ lines.push(t("tasksNone"));
3282
3166
  }
3283
3167
  return { nextState: state, output: lines.join("\n") };
3284
3168
  }
3285
3169
  case "/stop": {
3286
3170
  const registry = getProcessRegistry();
3287
3171
  if (!argText) {
3288
- return { nextState: state, output: "Usage: /stop <pid|label|all>" };
3172
+ return { nextState: state, output: t("stopUsage") };
3289
3173
  }
3290
3174
  if (argText === "all") {
3291
3175
  const before = registry.list().filter((p) => p.status === "running").length;
3292
3176
  registry.stopAll();
3293
- return { nextState: state, output: `Stopped ${before} processes` };
3177
+ return { nextState: state, output: t("stopAllDone", String(before)) };
3294
3178
  }
3295
3179
  if (/^\d+$/.test(argText)) {
3296
3180
  const pid = parseInt(argText, 10);
3297
3181
  const proc = registry.get(pid);
3298
3182
  if (!proc || proc.status !== "running") {
3299
- return { nextState: state, output: `No running process with pid ${pid}` };
3183
+ return { nextState: state, output: t("stopNoPid", String(pid)) };
3300
3184
  }
3301
3185
  registry.kill(pid);
3302
- return { nextState: state, output: `Stopped pid ${pid} (${proc.label})` };
3186
+ return { nextState: state, output: t("stopPidDone", String(pid), proc.label) };
3303
3187
  }
3304
3188
  const matches = registry.list().filter((p) => p.status === "running" && p.label === argText);
3305
3189
  if (matches.length === 0) {
3306
- return { nextState: state, output: `No running process labeled '${argText}'` };
3190
+ return { nextState: state, output: t("stopNoLabel", argText) };
3307
3191
  }
3308
3192
  for (const p of matches) {
3309
3193
  registry.kill(p.pid);
3310
3194
  }
3311
3195
  const stoppedNames = matches.map((p) => `pid ${p.pid} (${p.label})`).join(", ");
3312
- return { nextState: state, output: `Stopped ${stoppedNames}` };
3196
+ return { nextState: state, output: t("stopLabelsDone", stoppedNames) };
3313
3197
  }
3314
3198
  case "/exit":
3315
3199
  case "/quit":
@@ -3333,15 +3217,13 @@ Usage: /density <cozy|spacious>`
3333
3217
  if (argText) {
3334
3218
  return {
3335
3219
  nextState: state,
3336
- output: `Unknown command: ${trimmed}
3337
-
3338
- ${renderTuiHelp()}`
3220
+ output: t("unknownCommand", trimmed) + renderTuiHelp()
3339
3221
  };
3340
3222
  }
3341
3223
  if (!state.dialogId) {
3342
3224
  return {
3343
3225
  nextState: state,
3344
- output: "Current dialog: new (nothing to compact yet)"
3226
+ output: t("compactNothing")
3345
3227
  };
3346
3228
  }
3347
3229
  return {
@@ -3368,15 +3250,14 @@ ${renderTuiHelp()}`
3368
3250
  if (argText === "current" || argText === "show") {
3369
3251
  return {
3370
3252
  nextState: state,
3371
- output: `Current agent: ${state.agentName} (${state.agentKey})`
3253
+ output: t("agentCurrent", state.agentName, state.agentKey)
3372
3254
  };
3373
3255
  }
3374
3256
  const resolvedTarget = resolveAgentSwitchTarget(argText, resolveCatalogPlatformAgents());
3375
3257
  if (!resolvedTarget) {
3376
3258
  return {
3377
3259
  nextState: state,
3378
- output: `I don't know agent "${argText}" yet.
3379
- Use /switch, /switch list, /switch minimax-m3, or a full agent key.`
3260
+ output: t("agentUnknown", argText)
3380
3261
  };
3381
3262
  }
3382
3263
  return applyAgentSwitch(state, resolvedTarget);
@@ -3430,6 +3311,16 @@ Use /switch, /switch list, /switch minimax-m3, or a full agent key.`
3430
3311
  action: { type: "set-mouse", enabled: argText === "on" }
3431
3312
  };
3432
3313
  }
3314
+ case "/altscreen": {
3315
+ if (argText !== "on" && argText !== "off") {
3316
+ return { nextState: state, output: t("altscreenUsage") };
3317
+ }
3318
+ return {
3319
+ nextState: state,
3320
+ output: argText === "on" ? t("altscreenOn") : t("altscreenOff"),
3321
+ action: { type: "set-altscreen", enabled: argText === "on" }
3322
+ };
3323
+ }
3433
3324
  case "/resume": {
3434
3325
  if (!argText) {
3435
3326
  return {
@@ -3459,7 +3350,7 @@ Use /switch, /switch list, /switch minimax-m3, or a full agent key.`
3459
3350
  if (rest[0] === "attach") {
3460
3351
  const docName = rest.slice(1).join(" ").trim();
3461
3352
  if (!docName) {
3462
- return { nextState: state, output: "Usage: /doc attach <doc>" };
3353
+ return { nextState: state, output: t("docAttachUsage") };
3463
3354
  }
3464
3355
  const attachedDocs = state.attachedDocs.includes(docName) ? state.attachedDocs : [...state.attachedDocs, docName];
3465
3356
  return {
@@ -3469,7 +3360,7 @@ Use /switch, /switch list, /switch minimax-m3, or a full agent key.`
3469
3360
  }
3470
3361
  return {
3471
3362
  nextState: state,
3472
- output: state.attachedDocs.length > 0 ? `Attached docs: ${state.attachedDocs.join(", ")}` : "No docs attached. Use /doc attach <doc>."
3363
+ output: state.attachedDocs.length > 0 ? t("docList", state.attachedDocs.join(", ")) : t("docNone")
3473
3364
  };
3474
3365
  }
3475
3366
  case "/skill": {
@@ -3477,7 +3368,7 @@ Use /switch, /switch list, /switch minimax-m3, or a full agent key.`
3477
3368
  if (sub === "attach") {
3478
3369
  const skillRef = rest.slice(1).join(" ").trim();
3479
3370
  if (!skillRef) {
3480
- return { nextState: state, output: "Usage: /skill attach <skill-ref>" };
3371
+ return { nextState: state, output: t("skillAttachUsage") };
3481
3372
  }
3482
3373
  const attachedSkills = state.attachedSkills.includes(skillRef) ? state.attachedSkills : [...state.attachedSkills, skillRef];
3483
3374
  return {
@@ -3488,38 +3379,37 @@ Use /switch, /switch list, /switch minimax-m3, or a full agent key.`
3488
3379
  if (sub === "detach") {
3489
3380
  const skillRef = rest.slice(1).join(" ").trim();
3490
3381
  if (!skillRef) {
3491
- return { nextState: state, output: "Usage: /skill detach <skill-ref>" };
3382
+ return { nextState: state, output: t("skillDetachUsage") };
3492
3383
  }
3493
3384
  const attachedSkills = state.attachedSkills.filter((s) => s !== skillRef);
3494
3385
  return {
3495
3386
  nextState: { ...state, attachedSkills },
3496
- output: state.attachedSkills.includes(skillRef) ? `Detached skill: ${skillRef}` : `Skill not attached: ${skillRef}`
3387
+ output: state.attachedSkills.includes(skillRef) ? t("skillDetached", skillRef) : t("skillNotAttached", skillRef)
3497
3388
  };
3498
3389
  }
3499
3390
  if (sub === "clear") {
3500
3391
  if (state.attachedSkills.length === 0) {
3501
- return { nextState: state, output: "No skills attached." };
3392
+ return { nextState: state, output: t("skillNone") };
3502
3393
  }
3503
3394
  return {
3504
3395
  nextState: { ...state, attachedSkills: [] },
3505
- output: `Cleared ${state.attachedSkills.length} skill(s).`
3396
+ output: t("skillCleared", String(state.attachedSkills.length))
3506
3397
  };
3507
3398
  }
3508
3399
  return {
3509
3400
  nextState: state,
3510
- output: state.attachedSkills.length > 0 ? `Attached skills: ${state.attachedSkills.join(", ")}
3511
- Usage: /skill attach <ref> | /skill detach <ref> | /skill clear` : "No skills attached. Use /skill attach <skill-ref> to attach a skill.\nSkill refs can be a dbKey (page-xxx), a skill name (searched in .agents/skills/ then docs/skills/), or a direct path."
3401
+ output: state.attachedSkills.length > 0 ? t("skillList", state.attachedSkills.join(", ")) : t("skillNoneHint")
3512
3402
  };
3513
3403
  }
3514
3404
  case "/customize":
3515
3405
  return {
3516
3406
  nextState: state,
3517
- output: "Tell nolo what to change, for example: /customize make my default agent more concise."
3407
+ output: t("customizeHint")
3518
3408
  };
3519
3409
  case "/login":
3520
3410
  return {
3521
3411
  nextState: state,
3522
- output: "MVP login uses profile/env auth. Set AUTH_TOKEN, NOLO_SERVER, or NOLO_PROFILE before starting nolo."
3412
+ output: t("loginHint")
3523
3413
  };
3524
3414
  case "/profile":
3525
3415
  return {
@@ -3535,16 +3425,12 @@ Usage: /skill attach <ref> | /skill detach <ref> | /skill clear` : "No skills at
3535
3425
  case "/version":
3536
3426
  return {
3537
3427
  nextState: state,
3538
- output: `nolo ${state.cliVersion || "unknown version"}
3539
- Update this install with: nolo update
3540
- If repo-local output differs, publish/install the latest npm package first.`
3428
+ output: t("versionInfo", state.cliVersion || t("versionUnknown"))
3541
3429
  };
3542
3430
  default:
3543
3431
  return {
3544
3432
  nextState: state,
3545
- output: `Unknown command: ${command}
3546
-
3547
- ${renderTuiHelp()}`
3433
+ output: t("unknownCommand", command) + renderTuiHelp()
3548
3434
  };
3549
3435
  }
3550
3436
  }
@@ -3562,19 +3448,23 @@ function brightnessFromRgb(r, g, b) {
3562
3448
  const luma = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
3563
3449
  return luma >= 0.5 ? "light" : "dark";
3564
3450
  }
3565
- function parseOsc11Reply(reply) {
3451
+ function parseOsc11Background(reply) {
3566
3452
  const match = reply.match(OSC11_REPLY_RE);
3567
3453
  if (!match) return null;
3568
- return brightnessFromRgb(
3454
+ const channels = [
3569
3455
  componentToByte(match[1]),
3570
3456
  componentToByte(match[2]),
3571
3457
  componentToByte(match[3])
3572
- );
3458
+ ];
3459
+ return {
3460
+ brightness: brightnessFromRgb(channels[0], channels[1], channels[2]),
3461
+ hex: channels.map((v) => v.toString(16).padStart(2, "0").toUpperCase()).join("")
3462
+ };
3573
3463
  }
3574
3464
  function isTerminalDevice(stream) {
3575
3465
  return stream instanceof ReadStream;
3576
3466
  }
3577
- async function detectTerminalBrightness(args = {}) {
3467
+ async function detectTerminalBackground(args = {}) {
3578
3468
  const stdin = args.stdin ?? process.stdin;
3579
3469
  const stdout = args.stdout ?? process.stdout;
3580
3470
  const timeoutMs = args.timeoutMs ?? 100;
@@ -3601,7 +3491,7 @@ async function detectTerminalBrightness(args = {}) {
3601
3491
  };
3602
3492
  const onData = (chunk) => {
3603
3493
  buffer += chunk.toString("latin1");
3604
- const parsed = parseOsc11Reply(buffer);
3494
+ const parsed = parseOsc11Background(buffer);
3605
3495
  if (parsed) finish(parsed);
3606
3496
  else if (buffer.length > 256) finish(null);
3607
3497
  };
@@ -3945,7 +3835,7 @@ function buildHistoryLines(history, contentWidth) {
3945
3835
  const reset = "\x1B[39m";
3946
3836
  return `${accent}\u276F${reset} ${content}`;
3947
3837
  }
3948
- const highlighted = highlightMarkdown(content, colorEnabled);
3838
+ const highlighted = colorEnabled ? formatAssistantDisplay(content) : stripAnsi(formatAssistantDisplay(content));
3949
3839
  const rawLines = highlighted.split("\n");
3950
3840
  const styledLines = rawLines.map((line, idx) => {
3951
3841
  if (idx === 0 && !line.startsWith("[nolo]")) {
@@ -4072,6 +3962,30 @@ function applyScrollAction(history, action, output, inputLines) {
4072
3962
  }
4073
3963
 
4074
3964
  // packages/cli/tui/tuiRawInput.ts
3965
+ function normalizePasteNewlines(text) {
3966
+ return text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
3967
+ }
3968
+ var altScreenOn = /* @__PURE__ */ new WeakMap();
3969
+ function outputIsTty2(output) {
3970
+ return Boolean(output.isTTY);
3971
+ }
3972
+ function enterAltScreen(output) {
3973
+ if (!outputIsTty2(output)) return false;
3974
+ if (altScreenOn.get(output)) return false;
3975
+ altScreenOn.set(output, true);
3976
+ output.write("\x1B[?1049h");
3977
+ return true;
3978
+ }
3979
+ function leaveAltScreen(output) {
3980
+ if (!outputIsTty2(output)) return false;
3981
+ if (!altScreenOn.get(output)) return false;
3982
+ altScreenOn.set(output, false);
3983
+ output.write("\x1B[?1049l");
3984
+ return true;
3985
+ }
3986
+ function isAltScreenOn(output) {
3987
+ return Boolean(altScreenOn.get(output));
3988
+ }
4075
3989
  function styleCollapsedPastePlaceholders(text, colorEnabled) {
4076
3990
  if (!colorEnabled || !text.includes("[paste #")) return text;
4077
3991
  COLLAPSED_PASTE_PLACEHOLDER_RE.lastIndex = 0;
@@ -4102,6 +4016,8 @@ function createNoopFixedInput() {
4102
4016
  getInputLines: () => 1,
4103
4017
  isPaused: () => false,
4104
4018
  setMouseEnabled() {
4019
+ },
4020
+ setAltScreenEnabled() {
4105
4021
  }
4106
4022
  };
4107
4023
  }
@@ -4145,7 +4061,7 @@ function createFixedInput(output, config) {
4145
4061
  sections.push(fitAnsiLine(line.replace(/\r?\n/g, " "), cols));
4146
4062
  }
4147
4063
  const promptRaw = t("promptLabel");
4148
- const promptWidth = displayWidth2(promptRaw);
4064
+ const promptWidth = displayWidth(promptRaw);
4149
4065
  const prompt = colorEnabled ? `${themeColorSequence("accent")}${promptRaw}\x1B[39m` : promptRaw;
4150
4066
  const contentWidth = Math.max(1, cols - promptWidth);
4151
4067
  const logicalLines = buffer.length === 0 ? [""] : buffer.split("\n");
@@ -4181,7 +4097,7 @@ function createFixedInput(output, config) {
4181
4097
  if (targetPos <= charOffset + rowLen || j === rows.length - 1 && i === logicalLines.length - 1) {
4182
4098
  const subOffset = Math.max(0, Math.min(rowLen, targetPos - charOffset));
4183
4099
  const subStr = rowText.slice(0, subOffset);
4184
- cursorCol = promptWidth + displayWidth2(subStr);
4100
+ cursorCol = promptWidth + displayWidth(subStr);
4185
4101
  cursorRow = inputRows;
4186
4102
  cursorFound = true;
4187
4103
  }
@@ -4198,10 +4114,15 @@ function createFixedInput(output, config) {
4198
4114
  };
4199
4115
  const repaintAt = (buffer, cursorPos) => {
4200
4116
  const { text, lines, cursorCol, cursorRow } = renderInputArea(buffer, cursorPos);
4117
+ let linesChanged = false;
4201
4118
  if (lines !== inputLines) {
4202
4119
  inputLines = lines;
4120
+ linesChanged = true;
4203
4121
  }
4204
4122
  setScrollRegion(inputLines);
4123
+ if (linesChanged) {
4124
+ config.onInputLinesChange?.(inputLines);
4125
+ }
4205
4126
  const startRow = getRows() - inputLines + 1;
4206
4127
  write(`\x1B[${startRow};1H`);
4207
4128
  write("\x1B[J");
@@ -4249,8 +4170,7 @@ function createFixedInput(output, config) {
4249
4170
  disable() {
4250
4171
  disableMouse();
4251
4172
  resetScrollRegion();
4252
- const rows = getRows();
4253
- write(`\x1B[${rows};1H\x1B[2K\x1B[${Math.max(1, rows - 1)};1H`);
4173
+ leaveAltScreen(output);
4254
4174
  },
4255
4175
  getInputLines: () => inputLines,
4256
4176
  isPaused: () => paused,
@@ -4261,6 +4181,13 @@ function createFixedInput(output, config) {
4261
4181
  } else {
4262
4182
  write("\x1B[?1000l\x1B[?1006l");
4263
4183
  }
4184
+ },
4185
+ setAltScreenEnabled(enabled) {
4186
+ if (enabled) {
4187
+ enterAltScreen(output);
4188
+ } else {
4189
+ leaveAltScreen(output);
4190
+ }
4264
4191
  }
4265
4192
  };
4266
4193
  }
@@ -4310,9 +4237,27 @@ function isIncompleteTail(input, start) {
4310
4237
  }
4311
4238
  return false;
4312
4239
  }
4313
- var PASTE_START = "\x1B[?2004h";
4314
- var PASTE_END = "\x1B[?2004l";
4315
- function splitRawInputWithTail(input) {
4240
+ var PASTE_START = "\x1B[200~";
4241
+ var PASTE_END = "\x1B[201~";
4242
+ function findBracketedPasteEnd(input, contentStart) {
4243
+ return input.indexOf(PASTE_END, contentStart);
4244
+ }
4245
+ function emitPlainRunAsTokens(run) {
4246
+ if (run.length === 0) return [];
4247
+ if (shouldCollapsePaste(normalizePasteNewlines(run))) {
4248
+ return [`${PASTE_TOKEN_PREFIX}${run}`];
4249
+ }
4250
+ const tokens = [];
4251
+ for (let index = 0; index < run.length; ) {
4252
+ const codePoint = run.codePointAt(index);
4253
+ if (codePoint === void 0) break;
4254
+ const value = String.fromCodePoint(codePoint);
4255
+ tokens.push(value);
4256
+ index += value.length;
4257
+ }
4258
+ return tokens;
4259
+ }
4260
+ function splitRawInputWithTail(input, options) {
4316
4261
  const tokens = [];
4317
4262
  for (let index = 0; index < input.length; ) {
4318
4263
  if (isIncompleteTail(input, index)) {
@@ -4320,15 +4265,17 @@ function splitRawInputWithTail(input) {
4320
4265
  }
4321
4266
  if (input.startsWith(PASTE_START, index)) {
4322
4267
  const contentStart = index + PASTE_START.length;
4323
- const endPos = input.indexOf(PASTE_END, contentStart);
4268
+ const endPos = findBracketedPasteEnd(input, contentStart);
4324
4269
  if (endPos !== -1) {
4325
4270
  const payload = input.slice(contentStart, endPos);
4326
4271
  tokens.push(`${PASTE_TOKEN_PREFIX}${payload}`);
4327
4272
  index = endPos + PASTE_END.length;
4328
- } else {
4273
+ } else if (options?.forceCompleteOpenPaste) {
4329
4274
  const payload = input.slice(contentStart);
4330
4275
  tokens.push(`${PASTE_TOKEN_PREFIX}${payload}`);
4331
4276
  index = input.length;
4277
+ } else {
4278
+ return { tokens, tail: input.slice(index) };
4332
4279
  }
4333
4280
  continue;
4334
4281
  }
@@ -4353,63 +4300,121 @@ function splitRawInputWithTail(input) {
4353
4300
  index += csi.length;
4354
4301
  continue;
4355
4302
  }
4356
- const codePoint = input.codePointAt(index);
4357
- if (codePoint === void 0) break;
4358
- const value = String.fromCodePoint(codePoint);
4359
- tokens.push(value);
4360
- index += value.length;
4303
+ const nextEsc = input.indexOf("\x1B", index);
4304
+ const runEnd = nextEsc === -1 ? input.length : nextEsc;
4305
+ const run = input.slice(index, runEnd);
4306
+ if (run.length === 0) {
4307
+ const codePoint = input.codePointAt(index);
4308
+ if (codePoint === void 0) break;
4309
+ const value = String.fromCodePoint(codePoint);
4310
+ tokens.push(value);
4311
+ index += value.length;
4312
+ continue;
4313
+ }
4314
+ tokens.push(...emitPlainRunAsTokens(run));
4315
+ index = runEnd;
4361
4316
  }
4362
4317
  return { tokens, tail: "" };
4363
4318
  }
4364
4319
  function splitRawInput(input) {
4365
- const { tokens, tail } = splitRawInputWithTail(input);
4320
+ const { tokens, tail } = splitRawInputWithTail(input, {
4321
+ forceCompleteOpenPaste: true
4322
+ });
4366
4323
  if (!tail) return tokens;
4367
- const tailTokens = [];
4368
- for (let index = 0; index < tail.length; ) {
4369
- const codePoint = tail.codePointAt(index);
4370
- if (codePoint === void 0) break;
4371
- const value = String.fromCodePoint(codePoint);
4372
- tailTokens.push(value);
4373
- index += value.length;
4374
- }
4375
- return [...tokens, ...tailTokens];
4324
+ return [...tokens, ...emitPlainRunAsTokens(tail)];
4376
4325
  }
4377
4326
  function createRawInputDecoder(onToken, options) {
4378
4327
  const decoder = new TextDecoder("utf-8", { fatal: false, ignoreBOM: true });
4379
4328
  let pendingBuffer = "";
4380
4329
  let timer = null;
4381
4330
  const timeoutMs = options?.escTimeoutMs ?? 15;
4331
+ const unmarkedPasteDebounceMs = options?.unmarkedPasteDebounceMs ?? 40;
4382
4332
  const clearTimer = () => {
4383
4333
  if (timer !== null) {
4384
4334
  clearTimeout(timer);
4385
4335
  timer = null;
4386
4336
  }
4387
4337
  };
4388
- const flush = () => {
4338
+ const emitCodePoints = (text) => {
4339
+ for (let index = 0; index < text.length; ) {
4340
+ const codePoint = text.codePointAt(index);
4341
+ if (codePoint === void 0) break;
4342
+ const value = String.fromCodePoint(codePoint);
4343
+ onToken(value);
4344
+ index += value.length;
4345
+ }
4346
+ };
4347
+ const flushPlainPending = () => {
4389
4348
  clearTimer();
4390
- if (pendingBuffer.length > 0) {
4391
- const textToFlush = pendingBuffer;
4392
- pendingBuffer = "";
4393
- for (const token of splitRawInput(textToFlush)) {
4394
- onToken(token);
4395
- }
4349
+ if (pendingBuffer.length === 0) return;
4350
+ const text = pendingBuffer;
4351
+ pendingBuffer = "";
4352
+ for (const token of emitPlainRunAsTokens(text)) {
4353
+ onToken(token);
4396
4354
  }
4397
4355
  };
4356
+ const flushEscPending = (forceCompleteOpenPaste) => {
4357
+ clearTimer();
4358
+ if (pendingBuffer.length === 0) return;
4359
+ const textToFlush = pendingBuffer;
4360
+ pendingBuffer = "";
4361
+ const { tokens, tail } = splitRawInputWithTail(textToFlush, {
4362
+ forceCompleteOpenPaste
4363
+ });
4364
+ for (const token of tokens) {
4365
+ onToken(token);
4366
+ }
4367
+ if (!tail) return;
4368
+ if (forceCompleteOpenPaste) {
4369
+ emitCodePoints(tail);
4370
+ return;
4371
+ }
4372
+ pendingBuffer = tail;
4373
+ };
4398
4374
  const decodeFn = (chunk) => {
4399
4375
  clearTimer();
4400
4376
  const bytes = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
4401
4377
  const text = decoder.decode(bytes, { stream: true });
4402
4378
  pendingBuffer += text;
4403
- const { tokens, tail } = splitRawInputWithTail(pendingBuffer);
4404
- pendingBuffer = tail;
4405
- for (const token of tokens) {
4406
- onToken(token);
4379
+ if (pendingBuffer.startsWith(PASTE_START)) {
4380
+ const endPos = findBracketedPasteEnd(
4381
+ pendingBuffer,
4382
+ PASTE_START.length
4383
+ );
4384
+ if (endPos === -1) return;
4385
+ const { tokens, tail } = splitRawInputWithTail(pendingBuffer);
4386
+ pendingBuffer = tail;
4387
+ for (const token of tokens) onToken(token);
4388
+ if (pendingBuffer.startsWith(PASTE_START)) return;
4389
+ }
4390
+ if (pendingBuffer.includes("\x1B")) {
4391
+ const { tokens, tail } = splitRawInputWithTail(pendingBuffer);
4392
+ pendingBuffer = tail;
4393
+ for (const token of tokens) onToken(token);
4394
+ if (pendingBuffer.startsWith(PASTE_START)) return;
4395
+ if (pendingBuffer.length > 0) {
4396
+ timer = setTimeout(() => flushEscPending(true), timeoutMs);
4397
+ }
4398
+ return;
4399
+ }
4400
+ if (shouldCollapsePaste(normalizePasteNewlines(pendingBuffer))) {
4401
+ timer = setTimeout(flushPlainPending, unmarkedPasteDebounceMs);
4402
+ return;
4403
+ }
4404
+ if (!pendingBuffer.includes("\n") && !pendingBuffer.includes("\r")) {
4405
+ emitCodePoints(pendingBuffer);
4406
+ pendingBuffer = "";
4407
+ return;
4407
4408
  }
4408
- if (pendingBuffer.length > 0) {
4409
- timer = setTimeout(flush, timeoutMs);
4409
+ timer = setTimeout(flushPlainPending, unmarkedPasteDebounceMs);
4410
+ };
4411
+ decodeFn.flush = () => {
4412
+ if (pendingBuffer.startsWith(PASTE_START) || pendingBuffer.includes("\x1B")) {
4413
+ flushEscPending(true);
4414
+ return;
4410
4415
  }
4416
+ flushPlainPending();
4411
4417
  };
4412
- decodeFn.flush = flush;
4413
4418
  decodeFn.destroy = () => {
4414
4419
  clearTimer();
4415
4420
  pendingBuffer = "";
@@ -4419,12 +4424,66 @@ function createRawInputDecoder(onToken, options) {
4419
4424
 
4420
4425
  // packages/cli/tui/readlineWorkspace.ts
4421
4426
  var AGENTS_MD_MAX_BYTES = 8192;
4427
+ var altScreenRestoreOutput = null;
4428
+ var altScreenHandlersInstalled = false;
4429
+ var preExistingSignalListeners = {};
4430
+ var SIGNAL_EXIT_CODE = {
4431
+ SIGINT: 130,
4432
+ SIGTERM: 143,
4433
+ SIGHUP: 129
4434
+ };
4435
+ var restoreAltScreen = () => {
4436
+ try {
4437
+ if (altScreenRestoreOutput) leaveAltScreen(altScreenRestoreOutput);
4438
+ } catch {
4439
+ }
4440
+ };
4441
+ function installAltScreenRestoreHandlers(output) {
4442
+ altScreenRestoreOutput = output;
4443
+ if (altScreenHandlersInstalled) return;
4444
+ altScreenHandlersInstalled = true;
4445
+ process.on("exit", restoreAltScreen);
4446
+ for (const sig of ["SIGINT", "SIGTERM", "SIGHUP"]) {
4447
+ const existing = process.listeners(sig);
4448
+ preExistingSignalListeners[sig] = [...existing];
4449
+ const handler = () => {
4450
+ restoreAltScreen();
4451
+ const others = process.listeners(sig).filter((fn) => fn !== handler);
4452
+ if (others.length === 0) {
4453
+ process.exit(SIGNAL_EXIT_CODE[sig] ?? 128 + 1);
4454
+ return;
4455
+ }
4456
+ };
4457
+ process.prependListener(sig, handler);
4458
+ }
4459
+ process.on("uncaughtException", (err) => {
4460
+ restoreAltScreen();
4461
+ try {
4462
+ process.stderr.write(
4463
+ `uncaughtException: ${err?.stack ?? String(err)}
4464
+ `
4465
+ );
4466
+ } catch {
4467
+ }
4468
+ process.exit(1);
4469
+ });
4470
+ process.on("unhandledRejection", (reason) => {
4471
+ restoreAltScreen();
4472
+ try {
4473
+ const text = reason instanceof Error ? reason.stack ?? String(reason) : String(reason);
4474
+ process.stderr.write(`unhandledRejection: ${text}
4475
+ `);
4476
+ } catch {
4477
+ }
4478
+ process.exit(1);
4479
+ });
4480
+ }
4422
4481
  function readAgentsMdBlock(cwd) {
4423
4482
  for (const name of ["AGENTS.md", "CLAUDE.md"]) {
4424
- const filePath = join(cwd, name);
4425
- if (existsSync2(filePath)) {
4483
+ const filePath = join2(cwd, name);
4484
+ if (existsSync3(filePath)) {
4426
4485
  try {
4427
- let content = readFileSync(filePath, "utf8").trim();
4486
+ let content = readFileSync2(filePath, "utf8").trim();
4428
4487
  if (!content) continue;
4429
4488
  if (Buffer.byteLength(content, "utf8") > AGENTS_MD_MAX_BYTES) {
4430
4489
  content = Buffer.from(content, "utf8").subarray(0, AGENTS_MD_MAX_BYTES).toString("utf8") + "\n\n<!-- AGENTS.md truncated -->";
@@ -4448,10 +4507,14 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
4448
4507
  effectiveAgentName = cachedRoute.agentName;
4449
4508
  } else if (!continueId && env.NOLO_AUTO_ROUTE !== "0" && state.agentKey === DEFAULT_TUI_AGENT_KEY) {
4450
4509
  const authToken = env.AUTH_TOKEN ?? env.AUTH ?? env.BENCHMARK_AUTH_TOKEN ?? "";
4451
- const route = await classifyCliAutoRoute(message, {
4510
+ const route = authToken ? await classifyCliAutoRoute(message, {
4452
4511
  serverUrl: state.serverUrl,
4453
4512
  authToken
4454
- });
4513
+ }) : {
4514
+ agentKey: CLI_AUTO_TIER_AGENT_KEYS[resolveCliAutoFallbackTier(message)],
4515
+ tier: resolveCliAutoFallbackTier(message),
4516
+ classified: false
4517
+ };
4455
4518
  effectiveAgentKey = route.agentKey;
4456
4519
  effectiveAgentName = `auto\u2192${route.tier}`;
4457
4520
  if (effectiveAgentKey !== state.agentKey) {
@@ -4539,6 +4602,18 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
4539
4602
  if (skillDiscoveryBlock) {
4540
4603
  extraContextBlocks.push(skillDiscoveryBlock);
4541
4604
  }
4605
+ const agentsMdMarker = "--- \u9879\u76EE\u6307\u4EE4\uFF08";
4606
+ const contextBlockScopes = [];
4607
+ for (const block of extraContextBlocks) {
4608
+ if (block.startsWith(agentsMdMarker)) {
4609
+ contextBlockScopes.push({ content: block, cacheScope: "session" });
4610
+ }
4611
+ }
4612
+ for (const block of extraContextBlocks) {
4613
+ if (!block.startsWith(agentsMdMarker)) {
4614
+ contextBlockScopes.push({ content: block, cacheScope: "turn" });
4615
+ }
4616
+ }
4542
4617
  const result = await agentRunner({
4543
4618
  agentName: effectiveAgentName,
4544
4619
  agentKey: effectiveAgentKey,
@@ -4559,9 +4634,17 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
4559
4634
  ...options.confirmDestructiveAction ? { confirmDestructiveAction: options.confirmDestructiveAction } : {},
4560
4635
  ...options.requestUserChoice ? { requestUserChoice: options.requestUserChoice } : {},
4561
4636
  ...options.abortSignal ? { abortSignal: options.abortSignal } : {},
4637
+ ...options.pastedTextStore?.items.size ? {
4638
+ pastedTextStore: options.pastedTextStore,
4639
+ localRuntimeAdapterFactory: (factoryEnv, factoryOptions) => createCliLocalRuntimeAdapter({
4640
+ env: factoryEnv,
4641
+ cwd: factoryOptions?.cwd ?? state.cwd,
4642
+ pastedTextStore: options.pastedTextStore
4643
+ })
4644
+ } : {},
4562
4645
  ...options.activityReporter ? { activityReporter: options.activityReporter } : {},
4563
4646
  ...skillAllowedTools !== void 0 ? { allowedToolNames: skillAllowedTools } : {},
4564
- ...extraContextBlocks.length > 0 ? { extraContextBlocks } : {}
4647
+ ...contextBlockScopes.length > 0 ? { contextBlockScopes } : {}
4565
4648
  });
4566
4649
  if (!continueId && result.dialogId && effectiveAgentKey !== state.agentKey) {
4567
4650
  autoRouteByDialog.set(result.dialogId, {
@@ -4569,7 +4652,14 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
4569
4652
  agentName: effectiveAgentName
4570
4653
  });
4571
4654
  }
4572
- return result;
4655
+ return {
4656
+ ...result,
4657
+ contextWindow: resolveAgentContextWindow({
4658
+ agentKey: effectiveAgentKey,
4659
+ agentName: effectiveAgentName,
4660
+ autoRouteDefault: env.NOLO_AUTO_ROUTE !== "0"
4661
+ })
4662
+ };
4573
4663
  }
4574
4664
  function waitForActionGate(rl, input, output, gate, spawnRunner) {
4575
4665
  const commandPayload = gate.kind === "handoff" ? readCommandActionGatePayload(gate.payload) : null;
@@ -4681,7 +4771,12 @@ function waitForRawActionGate(input, output, gate, spawnRunner, hooks) {
4681
4771
  output.write("[nolo] Press Enter to run it now. Follow any prompts below, or Ctrl+C to cancel.\n");
4682
4772
  return new Promise((resolve2) => {
4683
4773
  const rawInput = input;
4774
+ let settled = false;
4775
+ let commandRunning = false;
4684
4776
  const finish = (result) => {
4777
+ if (settled) return;
4778
+ settled = true;
4779
+ hooks?.registerToken?.(null);
4685
4780
  input.off("data", onData);
4686
4781
  resolve2(result);
4687
4782
  };
@@ -4699,13 +4794,9 @@ function waitForRawActionGate(input, output, gate, spawnRunner, hooks) {
4699
4794
  actionGateResult: { gateId: gate.id, status: "failed", output: message }
4700
4795
  }
4701
4796
  });
4702
- const onData = async (chunk) => {
4703
- const text = String(chunk);
4704
- if (text.includes("")) {
4705
- cancel("interrupted");
4706
- return;
4707
- }
4708
- if (!text.includes("\r") && !text.includes("\n")) return;
4797
+ const runCommand = async () => {
4798
+ if (settled || commandRunning) return;
4799
+ commandRunning = true;
4709
4800
  if (!commandPayload) {
4710
4801
  fail("unsupported gate payload");
4711
4802
  return;
@@ -4743,7 +4834,25 @@ function waitForRawActionGate(input, output, gate, spawnRunner, hooks) {
4743
4834
  }
4744
4835
  });
4745
4836
  };
4746
- input.on("data", onData);
4837
+ const handleToken = (text) => {
4838
+ if (settled || commandRunning) return;
4839
+ if (text.includes("")) {
4840
+ cancel("interrupted");
4841
+ return;
4842
+ }
4843
+ if (text.includes("\r") || text.includes("\n")) {
4844
+ void runCommand();
4845
+ }
4846
+ };
4847
+ const onData = (chunk) => {
4848
+ const text = String(chunk);
4849
+ handleToken(text);
4850
+ };
4851
+ if (hooks?.registerToken) {
4852
+ hooks.registerToken(handleToken);
4853
+ } else {
4854
+ input.on("data", onData);
4855
+ }
4747
4856
  });
4748
4857
  }
4749
4858
  async function startTuiWorkspace(options) {
@@ -4755,13 +4864,18 @@ async function startTuiWorkspace(options) {
4755
4864
  const spawnRunner = options.spawnRunner ?? spawnProcess;
4756
4865
  const selfUpdater = options.selfUpdater ?? ((target) => runSelfUpdate({ output: target }));
4757
4866
  if (output.isTTY) {
4758
- output.write("\x1B[2J\x1B[3J\x1B[H");
4867
+ enterAltScreen(output);
4868
+ installAltScreenRestoreHandlers(output);
4869
+ output.write("\x1B[2J\x1B[H");
4759
4870
  }
4760
- const detected = await detectTerminalBrightness({
4871
+ const detected = await detectTerminalBackground({
4761
4872
  stdin: input,
4762
4873
  stdout: output
4763
4874
  });
4764
- if (detected) setActiveBrightness(detected);
4875
+ if (detected) {
4876
+ setActiveBrightness(detected.brightness);
4877
+ setActiveTerminalBaseHex(detected.hex);
4878
+ }
4765
4879
  const bannerColumns = output.columns;
4766
4880
  output.write(renderWelcome(state, 0, 0, bannerColumns));
4767
4881
  let fixedInput = createNoopFixedInput();
@@ -4769,9 +4883,14 @@ async function startTuiWorkspace(options) {
4769
4883
  let cursorPos = 0;
4770
4884
  const pasteStore = createCollapsedPasteStore();
4771
4885
  let activeTurnAbort = null;
4886
+ let forcedStop = false;
4887
+ let forcedStopEpoch = 0;
4888
+ let turnEpoch = 0;
4889
+ let activeTurnEpoch = 0;
4772
4890
  const activityIndicator = createActivityIndicator({
4773
4891
  isTurnActive: () => activeTurnAbort !== null,
4774
4892
  fallbackLabel: () => `${state.agentName} -> working`,
4893
+ stoppingLabel: () => t("turnStopping"),
4775
4894
  onRepaint: () => {
4776
4895
  if (fixedInput.active && !fixedInput.isPaused()) {
4777
4896
  output.write("\x1B[?2026h\x1B[?25l");
@@ -4831,9 +4950,16 @@ async function startTuiWorkspace(options) {
4831
4950
  paintSyncedFrame();
4832
4951
  }, 0);
4833
4952
  };
4953
+ let syncingLayout = false;
4834
4954
  const renderHistoryToOutput = () => {
4835
4955
  if (fixedInput.isPaused()) return;
4836
- renderHistory(output, history, fixedInput.getInputLines());
4956
+ if (syncingLayout) return;
4957
+ syncingLayout = true;
4958
+ try {
4959
+ renderHistory(output, history, fixedInput.getInputLines());
4960
+ } finally {
4961
+ syncingLayout = false;
4962
+ }
4837
4963
  };
4838
4964
  const readLatestAssistantReply = () => {
4839
4965
  const lastReply = [...history.turns].reverse().find((turn) => turn.role === "assistant")?.content;
@@ -4871,7 +4997,11 @@ ${t("copyViewHint")}
4871
4997
  return true;
4872
4998
  };
4873
4999
  let modalOwnsKeyboard = false;
5000
+ let rawActionGateTokenHandler = null;
4874
5001
  const runOneAgentTurn = async (message, imageUrls, actionGateHandler, confirmDestructiveAction) => {
5002
+ forcedStop = false;
5003
+ turnEpoch += 1;
5004
+ const myEpoch = turnEpoch;
4875
5005
  history.followBottom = true;
4876
5006
  startTurn(history, "user");
4877
5007
  appendToCurrentTurn(history, message);
@@ -4901,6 +5031,7 @@ ${t("copyViewHint")}
4901
5031
  } : void 0;
4902
5032
  try {
4903
5033
  activeTurnAbort = new AbortController();
5034
+ activeTurnEpoch = myEpoch;
4904
5035
  const runResult = await runAgentChat(
4905
5036
  options.scriptDir,
4906
5037
  state,
@@ -4914,10 +5045,27 @@ ${t("copyViewHint")}
4914
5045
  ...confirmDestructiveAction ? { confirmDestructiveAction } : {},
4915
5046
  ...requestUserChoice ? { requestUserChoice } : {},
4916
5047
  abortSignal: activeTurnAbort.signal,
5048
+ pastedTextStore: pasteStore,
4917
5049
  explicitAgentSwitch,
4918
5050
  activityReporter
4919
5051
  }
4920
5052
  );
5053
+ const wasForceStopped = forcedStopEpoch === myEpoch;
5054
+ if (wasForceStopped) {
5055
+ if (runResult.dialogId || runResult.turnTokens) {
5056
+ const nextDialogKey = runResult.dialogId ? runResult.dialogId === state.dialogId && state.dialogKey ? state.dialogKey : state.dialogOwnerId ? `dialog-${state.dialogOwnerId}-${runResult.dialogId}` : void 0 : state.dialogKey;
5057
+ state = {
5058
+ ...state,
5059
+ ...runResult.dialogId ? {
5060
+ dialogId: runResult.dialogId,
5061
+ dialogKey: nextDialogKey,
5062
+ dialogLabel: runResult.dialogId
5063
+ } : {},
5064
+ ...runResult.turnTokens ? { turnTokens: runResult.turnTokens } : {}
5065
+ };
5066
+ }
5067
+ return { ok: false, aborted: true };
5068
+ }
4921
5069
  const wasAborted = activeTurnAbort.signal.aborted;
4922
5070
  activeTurnAbort = null;
4923
5071
  explicitAgentSwitch = false;
@@ -4928,9 +5076,13 @@ ${t("copyViewHint")}
4928
5076
  if (fixedInput.active) fixedInput.repaint(buffer);
4929
5077
  }
4930
5078
  if (wasAborted) {
4931
- emitCommandOutput(t("turnStopped"));
5079
+ if (runResult.pendingToolName) {
5080
+ emitCommandOutput(t("turnStoppedToolPending", runResult.pendingToolName));
5081
+ } else {
5082
+ emitCommandOutput(t("turnStopped"));
5083
+ }
4932
5084
  }
4933
- if (runResult.dialogId || runResult.turnTokens) {
5085
+ if (runResult.dialogId || runResult.turnTokens || runResult.contextWindow) {
4934
5086
  const nextDialogKey = runResult.dialogId ? runResult.dialogId === state.dialogId && state.dialogKey ? state.dialogKey : state.dialogOwnerId ? `dialog-${state.dialogOwnerId}-${runResult.dialogId}` : void 0 : state.dialogKey;
4935
5087
  state = {
4936
5088
  ...state,
@@ -4939,11 +5091,20 @@ ${t("copyViewHint")}
4939
5091
  dialogKey: nextDialogKey,
4940
5092
  dialogLabel: runResult.dialogId
4941
5093
  } : {},
4942
- ...runResult.turnTokens ? { turnTokens: runResult.turnTokens } : {}
5094
+ ...runResult.turnTokens ? { turnTokens: runResult.turnTokens } : {},
5095
+ ...runResult.contextWindow ? { contextWindow: runResult.contextWindow } : {}
4943
5096
  };
4944
5097
  }
4945
- if (!wasAborted && runResult.exitCode !== 0 && isQuotaExhaustedError(runResult.localError)) {
4946
- emitCommandOutput(t("quotaExhaustedHint"));
5098
+ if (!wasAborted && runResult.exitCode !== 0) {
5099
+ if (isBalanceExhaustedError(runResult.localError) && runResult.dialogId) {
5100
+ emitCommandOutput(t("balanceExhaustedHint"));
5101
+ } else if (isQuotaExhaustedError(runResult.localError)) {
5102
+ emitCommandOutput(t("quotaExhaustedHint"));
5103
+ } else if (runResult.dialogId) {
5104
+ emitCommandOutput(t("dialogPreservedHint"));
5105
+ } else {
5106
+ emitCommandOutput(t("dialogNotSavedHint"));
5107
+ }
4947
5108
  }
4948
5109
  return { ok: !wasAborted, aborted: wasAborted };
4949
5110
  } finally {
@@ -4998,6 +5159,7 @@ ${t("copyViewHint")}
4998
5159
  }
4999
5160
  if (result.action?.type === "exit") return true;
5000
5161
  if (result.action?.type === "clear") {
5162
+ clearCollapsedPasteStore(pasteStore);
5001
5163
  history.turns.length = 0;
5002
5164
  history.currentRole = null;
5003
5165
  history.currentContent = "";
@@ -5068,7 +5230,19 @@ ${t("copyViewHint")}
5068
5230
  ...state,
5069
5231
  agentName: pickResult.name,
5070
5232
  agentKey: pickResult.key,
5071
- contextWindow: getModelContextWindow(pickResult.name)
5233
+ contextWindow: resolveAgentContextWindow({
5234
+ agentKey: pickResult.key,
5235
+ agentName: pickResult.name,
5236
+ model: pickResult.model,
5237
+ autoRouteDefault: (options.env ?? process.env).NOLO_AUTO_ROUTE !== "0"
5238
+ }),
5239
+ estimatedContextTokens: estimateDefaultCliContextTokens({
5240
+ cwd: state.cwd,
5241
+ agentKey: pickResult.key,
5242
+ agentName: pickResult.name,
5243
+ model: pickResult.model
5244
+ }),
5245
+ ...pickResult.apiSource ? { apiSource: pickResult.apiSource } : {}
5072
5246
  };
5073
5247
  if (state.dialogId) autoRouteByDialog.delete(state.dialogId);
5074
5248
  explicitAgentSwitch = true;
@@ -5100,6 +5274,16 @@ ${t("copyViewHint")}
5100
5274
  if (result.action?.type === "set-mouse") {
5101
5275
  fixedInput.setMouseEnabled(result.action.enabled);
5102
5276
  }
5277
+ if (result.action?.type === "set-altscreen") {
5278
+ if (result.action.enabled) {
5279
+ enterAltScreen(output);
5280
+ installAltScreenRestoreHandlers(output);
5281
+ } else {
5282
+ leaveAltScreen(output);
5283
+ }
5284
+ renderHistoryToOutput();
5285
+ fixedInput.repaint(buffer);
5286
+ }
5103
5287
  if (result.action?.type === "copy-view") {
5104
5288
  const opened = await openCopyView();
5105
5289
  if (!opened) emitCommandOutput(t("copyNothing"));
@@ -5143,14 +5327,7 @@ ${t("copyViewHint")}
5143
5327
  dialog: pickResult.dialog,
5144
5328
  env: options.env ?? process.env
5145
5329
  });
5146
- const restored = loadedTurns.slice(-MAX_TUI_HISTORY_TURNS).map(
5147
- (turn) => turn.role === "assistant" ? {
5148
- ...turn,
5149
- content: formatAssistantDisplay(
5150
- turn.content
5151
- )
5152
- } : turn
5153
- );
5330
+ const restored = loadedTurns.slice(-MAX_TUI_HISTORY_TURNS);
5154
5331
  history.turns.length = 0;
5155
5332
  history.turns.push(...restored);
5156
5333
  history.currentRole = null;
@@ -5164,6 +5341,7 @@ ${t("copyViewHint")}
5164
5341
  dialogLabel: pickResult.dialog.id,
5165
5342
  turnTokens: void 0
5166
5343
  };
5344
+ clearCollapsedPasteStore(pasteStore);
5167
5345
  emitCommandOutput(
5168
5346
  `${t("resumedDialogPrefix")}: ${pickResult.dialog.title} (${pickResult.dialog.id})`
5169
5347
  );
@@ -5323,6 +5501,9 @@ ${err.message}` : ""}`
5323
5501
  const oneLine = text.replace(/\r?\n/g, " \u23CE ");
5324
5502
  return dimCliText(` \u2937 ${i + 1}. ${oneLine}`, colorEnabled);
5325
5503
  });
5504
+ },
5505
+ onInputLinesChange: () => {
5506
+ renderHistoryToOutput();
5326
5507
  }
5327
5508
  });
5328
5509
  fixedInput.init();
@@ -5345,6 +5526,7 @@ ${err.message}` : ""}`
5345
5526
  const finish = () => {
5346
5527
  if (done) return;
5347
5528
  done = true;
5529
+ clearCollapsedPasteStore(pasteStore);
5348
5530
  resizeTarget.off?.("resize", onResize);
5349
5531
  input.off("data", onData);
5350
5532
  onData.destroy();
@@ -5362,7 +5544,10 @@ ${err.message}` : ""}`
5362
5544
  }
5363
5545
  return;
5364
5546
  }
5365
- if (modalOwnsKeyboard) return;
5547
+ if (modalOwnsKeyboard) {
5548
+ rawActionGateTokenHandler?.(sequence);
5549
+ return;
5550
+ }
5366
5551
  const busyLock = busy;
5367
5552
  const scrollAction = parseScrollAction(sequence);
5368
5553
  if (scrollAction) {
@@ -5372,10 +5557,25 @@ ${err.message}` : ""}`
5372
5557
  return;
5373
5558
  }
5374
5559
  if (busyLock && sequence === "\x1B" && activeTurnAbort) {
5560
+ if (activityIndicator.isStopping()) {
5561
+ forcedStop = true;
5562
+ forcedStopEpoch = activeTurnEpoch;
5563
+ activityIndicator.stop();
5564
+ activeTurnAbort.abort();
5565
+ activeTurnAbort = null;
5566
+ activeTurnEpoch = 0;
5567
+ busy = false;
5568
+ emitCommandOutput(t("forceStopped"));
5569
+ flushPendingRender();
5570
+ renderHistoryToOutput();
5571
+ if (fixedInput.active) fixedInput.repaint(buffer);
5572
+ return;
5573
+ }
5375
5574
  const stopBinding = chatQueueBinding;
5376
5575
  if (stopBinding && stopBinding.queueLength() > 0) {
5377
5576
  stopBinding.preemptForStop();
5378
5577
  }
5578
+ activityIndicator.markStopping();
5379
5579
  activeTurnAbort.abort();
5380
5580
  return;
5381
5581
  }
@@ -5383,10 +5583,7 @@ ${err.message}` : ""}`
5383
5583
  pasteStore
5384
5584
  });
5385
5585
  if (result.copyView) {
5386
- if (busyLock) return;
5387
- busy = true;
5388
5586
  const opened = await openCopyView();
5389
- busy = false;
5390
5587
  if (!opened) {
5391
5588
  history.followBottom = true;
5392
5589
  startTurn(history, "assistant");
@@ -5402,7 +5599,12 @@ ${err.message}` : ""}`
5402
5599
  return;
5403
5600
  }
5404
5601
  if (result.submit !== void 0) {
5405
- const submittedText = expandCollapsedPastes(result.submit, pasteStore);
5602
+ const submittedText = replaceCollapsedPastesWithReferences(
5603
+ result.submit,
5604
+ pasteStore
5605
+ );
5606
+ const submittedTrimmed = submittedText.trim();
5607
+ const startsChatTurn = submittedTrimmed.length > 0 && !submittedTrimmed.startsWith("/") && !submittedTrimmed.startsWith("!");
5406
5608
  if (busyLock) {
5407
5609
  const trimmedText = submittedText.trim();
5408
5610
  const busySlashCommand = trimmedText.split(/\s+/)[0]?.toLowerCase();
@@ -5427,7 +5629,7 @@ ${hint}` : hint;
5427
5629
  `);
5428
5630
  }
5429
5631
  }
5430
- clearCollapsedPasteStore(pasteStore);
5632
+ releaseCollapsedPasteReferences(submittedText, pasteStore);
5431
5633
  buffer = "";
5432
5634
  cursorPos2 = 0;
5433
5635
  if (fixedInput.active) fixedInput.repaint(buffer, cursorPos2);
@@ -5439,7 +5641,10 @@ ${hint}` : hint;
5439
5641
  try {
5440
5642
  return await waitForRawActionGate(input, output, gate, spawnRunner, {
5441
5643
  beforeSubprocess: () => fixedInput.pause(),
5442
- afterSubprocess: () => fixedInput.resumeFromSubprocess()
5644
+ afterSubprocess: () => fixedInput.resumeFromSubprocess(),
5645
+ registerToken: (handler) => {
5646
+ rawActionGateTokenHandler = handler;
5647
+ }
5443
5648
  });
5444
5649
  } finally {
5445
5650
  modalOwnsKeyboard = false;
@@ -5466,7 +5671,6 @@ ${hint}` : hint;
5466
5671
  isRunning: true
5467
5672
  });
5468
5673
  if (decision.kind === "queue-text") {
5469
- clearCollapsedPasteStore(pasteStore);
5470
5674
  binding.enqueue(decision.text);
5471
5675
  buffer = "";
5472
5676
  cursorPos2 = 0;
@@ -5487,7 +5691,10 @@ ${hint}` : hint;
5487
5691
  try {
5488
5692
  return await waitForRawActionGate(input, output, gate, spawnRunner, {
5489
5693
  beforeSubprocess: () => fixedInput.pause(),
5490
- afterSubprocess: () => fixedInput.resumeFromSubprocess()
5694
+ afterSubprocess: () => fixedInput.resumeFromSubprocess(),
5695
+ registerToken: (handler) => {
5696
+ rawActionGateTokenHandler = handler;
5697
+ }
5491
5698
  });
5492
5699
  } finally {
5493
5700
  modalOwnsKeyboard = false;
@@ -5541,8 +5748,7 @@ ${err.message}` : ""}`
5541
5748
  return;
5542
5749
  }
5543
5750
  }
5544
- clearCollapsedPasteStore(pasteStore);
5545
- busy = true;
5751
+ busy = startsChatTurn;
5546
5752
  buffer = "";
5547
5753
  cursorPos2 = 0;
5548
5754
  fixedInput.enterOutputMode(submittedText);
@@ -5555,7 +5761,10 @@ ${err.message}` : ""}`
5555
5761
  try {
5556
5762
  return await waitForRawActionGate(input, output, gate, spawnRunner, {
5557
5763
  beforeSubprocess: () => fixedInput.pause(),
5558
- afterSubprocess: () => fixedInput.resumeFromSubprocess()
5764
+ afterSubprocess: () => fixedInput.resumeFromSubprocess(),
5765
+ registerToken: (handler) => {
5766
+ rawActionGateTokenHandler = handler;
5767
+ }
5559
5768
  });
5560
5769
  } finally {
5561
5770
  modalOwnsKeyboard = false;
@@ -5664,9 +5873,13 @@ export {
5664
5873
  createNoopFixedInput,
5665
5874
  createRawInputDecoder,
5666
5875
  createTurnHistory,
5667
- displayWidth2 as displayWidth,
5876
+ displayWidth,
5877
+ enterAltScreen,
5668
5878
  finalizeCurrentTurn,
5669
5879
  fitAnsiLine,
5880
+ installAltScreenRestoreHandlers,
5881
+ isAltScreenOn,
5882
+ leaveAltScreen,
5670
5883
  padOrTruncateToWidth,
5671
5884
  parseScrollAction,
5672
5885
  renderHistory,