nolo-cli 0.18.0 → 0.24.0-alpha.13

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 (134) hide show
  1. package/README.md +9 -18
  2. package/{_executeModel-X5PCIZN6.js → _executeModel-ZPNYAEFW.js} +46 -44
  3. package/{action-LT7BRFLK.js → action-5MNAXR5B.js} +7 -6
  4. package/{addContentAction-PL3KKBDH.js → addContentAction-UNSYTVTB.js} +5 -5
  5. package/{addDialogAgentAction-FCMSKYAF.js → addDialogAgentAction-LJGNP2AO.js} +5 -5
  6. package/{agentDeleteCommand-2BZDQV36.js → agentDeleteCommand-5ZV4433K.js} +27 -26
  7. package/{agentEmailCommands-2QJDGRMB.js → agentEmailCommands-Z7KOYDVQ.js} +57 -55
  8. package/{agentListCommands-6VEPTKBI.js → agentListCommands-UQUXJYUN.js} +30 -26
  9. package/{agentMachineCommands-NVVHNWYI.js → agentMachineCommands-53O6P6L5.js} +25 -24
  10. package/{agentRecordCommands-4GGRDMD4.js → agentRecordCommands-ZRYNBSSS.js} +52 -50
  11. package/{agentRunCommand-XOLQNKBZ.js → agentRunCommand-B73MKVOG.js} +55 -54
  12. package/{agentRunControl-Y5AFH5PQ.js → agentRunControl-SNUKXN6J.js} +6 -2
  13. package/{agentRuntimeLocal-7WYWCGL4.js → agentRuntimeLocal-GVNERRKQ.js} +21 -18
  14. package/{agentSlice-EAXPGEYV.js → agentSlice-OJBHIL34.js} +5 -5
  15. package/{appDeleteCommands-77KR2RJ4.js → appDeleteCommands-23Z3Z6KE.js} +23 -22
  16. package/{appDeployCommands-K7CAHGBI.js → appDeployCommands-3MFAR4Z7.js} +23 -22
  17. package/{appDeployStatusCommands-4OEKLTBT.js → appDeployStatusCommands-JHU6GSN4.js} +23 -22
  18. package/{appGetCommands-VM2WDLAZ.js → appGetCommands-24J55H2Y.js} +23 -22
  19. package/{appListCommands-GX6BBA6U.js → appListCommands-HE5KP3PH.js} +23 -22
  20. package/{authCommand-DY6UUL6O.js → authCommand-KGRK6MNU.js} +12 -13
  21. package/{authCommands-XTBV7OFZ.js → authCommands-JSOG2H4E.js} +4 -5
  22. package/{chunk-OWOHNGCN.js → chunk-2MBTWINN.js} +42 -3
  23. package/{chunk-MVQ4TBSD.js → chunk-54VHTARN.js} +1 -1
  24. package/{chunk-RQN4SEKL.js → chunk-5DWZ46RJ.js} +1 -1
  25. package/{chunk-VYHDJEQD.js → chunk-5EDKWSQN.js} +136 -78
  26. package/{chunk-Z4NR32XR.js → chunk-5RGU3J7P.js} +13 -0
  27. package/{chunk-7BUYEZ6U.js → chunk-7H3UTGPM.js} +12 -72
  28. package/{chunk-3ZX7PERL.js → chunk-AAWJ5S3G.js} +20 -7
  29. package/{chunk-SCTYAHYK.js → chunk-ADE6577S.js} +237 -142
  30. package/{chunk-PJSV3IZ5.js → chunk-AOPOTHFK.js} +1 -1
  31. package/{chunk-3ND52WIL.js → chunk-ATWUOTJP.js} +180 -24
  32. package/{chunk-SWDPIPOY.js → chunk-AZW5AHMW.js} +1 -1
  33. package/chunk-CF7S2RYZ.js +376 -0
  34. package/{chunk-L4F3I4SR.js → chunk-CV6BM7LU.js} +4 -6
  35. package/{chunk-G5U6LDLG.js → chunk-CWTT2NZ6.js} +6 -6
  36. package/{chunk-MKA6QE5E.js → chunk-CZHE6LLV.js} +135 -55
  37. package/{chunk-KFEVY3ST.js → chunk-D34RI6NI.js} +18 -8
  38. package/{chunk-XRZJGPFI.js → chunk-FON47ACC.js} +4 -4
  39. package/{chunk-K6EUAOW3.js → chunk-FXFGNOT2.js} +67 -1
  40. package/{chunk-LLWKHDG4.js → chunk-G36SUBHA.js} +14 -6
  41. package/{chunk-REA4JLBH.js → chunk-GD23ZX7Z.js} +7 -6
  42. package/{chunk-ROWQEJRP.js → chunk-GE6IWA5N.js} +12 -4
  43. package/{chunk-EDV7EBOI.js → chunk-GGB6MPHD.js} +2155 -1503
  44. package/{chunk-IYEXFJPC.js → chunk-GNGOZMU2.js} +64 -57
  45. package/{chunk-4T5F7YZQ.js → chunk-I6RJGORZ.js} +1 -1
  46. package/{chunk-DFCFMCY6.js → chunk-J2WT5CEO.js} +2 -2
  47. package/{chunk-AHCY2S2I.js → chunk-JJ2WUBFN.js} +1 -1
  48. package/{chunk-UGUW3FHG.js → chunk-JMSCXQUG.js} +2 -2
  49. package/{chunk-VDXCXCXH.js → chunk-KURQBS7S.js} +1 -1
  50. package/{chunk-FQZZLA3C.js → chunk-KXKQO4Q6.js} +17 -0
  51. package/{chunk-3Q7WHGCB.js → chunk-L65QKSVJ.js} +1 -1
  52. package/{chunk-UAIVG4J6.js → chunk-LEHTC2FI.js} +1 -8
  53. package/{chunk-UBHAWFQ6.js → chunk-LLDGWLML.js} +42 -19
  54. package/{chunk-JLA4FXYC.js → chunk-MALVZUYL.js} +1 -1
  55. package/{chunk-N54HIJVJ.js → chunk-MCXQNEJ3.js} +101 -9
  56. package/{chunk-GFUIQ4RY.js → chunk-MXFQHRSS.js} +1 -1
  57. package/{chunk-LSFAWXKC.js → chunk-N7J63Q6C.js} +1 -0
  58. package/{chunk-6Y7A3OQQ.js → chunk-NRY4GCKJ.js} +2 -0
  59. package/{chunk-ELT65PZW.js → chunk-OXLLFZNF.js} +1 -1
  60. package/{chunk-YGW4G5W5.js → chunk-P3RZ6DHN.js} +133 -25
  61. package/{chunk-5MIDPSSK.js → chunk-PB7G5MAM.js} +3 -3
  62. package/{chunk-HEGV7BH3.js → chunk-PMCKFQYC.js} +1 -1
  63. package/{chunk-23FZYPLC.js → chunk-PTKI7TIN.js} +911 -1064
  64. package/{chunk-MHSGJW4Z.js → chunk-PXPISYEH.js} +44 -14
  65. package/{chunk-I2ZJKTG5.js → chunk-PY7VGC5M.js} +29 -10
  66. package/{chunk-PIZYJVHQ.js → chunk-RM464B7I.js} +4 -4
  67. package/{chunk-XHG22OEX.js → chunk-ROJI6DRT.js} +2 -2
  68. package/{chunk-VNAEBGDK.js → chunk-RZNYL4LW.js} +123 -26
  69. package/{chunk-RVOUXDFU.js → chunk-S6KVFOUG.js} +6 -34
  70. package/{chunk-BV4VEE3W.js → chunk-S7AV3NCK.js} +277 -357
  71. package/{chunk-R35XMF6F.js → chunk-SIUEKVK4.js} +484 -67
  72. package/{chunk-B5TOXZAK.js → chunk-SIZQB7BD.js} +1 -1
  73. package/{chunk-DFFIZ25G.js → chunk-SLOGKB7M.js} +4 -1
  74. package/{chunk-MPVYDS7I.js → chunk-TLYHUIWF.js} +3 -3
  75. package/{chunk-HWW2M4SN.js → chunk-TNM27YZ7.js} +5 -4
  76. package/{chunk-OK6YSWLH.js → chunk-TWA4L7J7.js} +1 -1
  77. package/chunk-UZ6XXCVR.js +50 -0
  78. package/{chunk-DKZFOK24.js → chunk-WCP5FDTF.js} +16 -304
  79. package/{chunk-TRT4I4UD.js → chunk-WSQLK2KR.js} +2 -2
  80. package/{chunk-FXUSIQC2.js → chunk-XNZJVYFS.js} +1 -1
  81. package/{chunk-V3ED5AWK.js → chunk-YJY7HODU.js} +27 -16
  82. package/chunk-YYLHWIW6.js +48 -0
  83. package/{chunk-CZ6NYQTV.js → chunk-ZFL3LTU7.js} +2 -2
  84. package/{chunk-34WPV75U.js → chunk-ZUXIXYJI.js} +1 -1
  85. package/{chunk-VC6OZXO5.js → chunk-ZWJOGMKH.js} +2 -26
  86. package/{cliExecutor-DNE7TCC4.js → cliExecutor-K3F7DMRO.js} +2 -2
  87. package/{codingSkills-36KN5NQT.js → codingSkills-EN6Z6G6U.js} +4 -4
  88. package/{createAgentAutomationAction-GNFF524X.js → createAgentAutomationAction-PO62NUR7.js} +19 -18
  89. package/{createDialogAction-RB55KCLO.js → createDialogAction-ORFRYI3C.js} +22 -20
  90. package/{createPageAction-Y7343GLU.js → createPageAction-XWVMNTYH.js} +20 -19
  91. package/{db-W3O47YGF.js → db-W5UHK4P3.js} +150 -0
  92. package/{dbSlice-CGSRFJN3.js → dbSlice-5BKDURDL.js} +4 -4
  93. package/{dialogCommands-IAZK6YHQ.js → dialogCommands-XTJHROXM.js} +24 -23
  94. package/{dialogSlice-UQAXF23P.js → dialogSlice-DJXC53YW.js} +24 -18
  95. package/{docCreateCommands-I2EWZGCI.js → docCreateCommands-T2XRVXYS.js} +24 -27
  96. package/{docDeleteCommands-IZ45UZKG.js → docDeleteCommands-L7RXFXY3.js} +25 -24
  97. package/{docReadCommands-R6Z4QJYU.js → docReadCommands-M5PYNADQ.js} +24 -23
  98. package/{docStore-5HOJ4D7D.js → docStore-6LKLLFDW.js} +20 -19
  99. package/{docUpdateCommands-2LRBKQPY.js → docUpdateCommands-OA5EGPLQ.js} +24 -27
  100. package/{fetchMessages-BMGVZVSE.js → fetchMessages-W4IKHPCS.js} +2 -3
  101. package/{getPricing-G7DLAST6.js → getPricing-6C3C47Y4.js} +3 -3
  102. package/{handleSendMessageAction-Q4HY35WX.js → handleSendMessageAction-ILFGZTN5.js} +23 -20
  103. package/index.js +74 -74
  104. package/{localDialogRead-4H3QJFLW.js → localDialogRead-JXTAHW2E.js} +2 -2
  105. package/{machineCommands-PKBPQKTY.js → machineCommands-AKAQGPAO.js} +59 -57
  106. package/{machineWsRunDispatch-UZAW4OXJ.js → machineWsRunDispatch-RSBERM64.js} +51 -49
  107. package/{models-DJFSJ7SW.js → models-ILWKIX2Y.js} +2 -2
  108. package/package.json +1 -1
  109. package/{read-3FMPZPER.js → read-F3L3T2WJ.js} +2 -1
  110. package/{readDocTool-VSYHKK3V.js → readDocTool-FOHWVA4R.js} +1 -1
  111. package/{readlineWorkspace-Y2RDOFZG.js → readlineWorkspace-TUH3SFHO.js} +937 -430
  112. package/{removeDialogAgentAction-OAGA2G3U.js → removeDialogAgentAction-QCFGIUIH.js} +8 -7
  113. package/{runAgentClientLoop-NBPIKJ6J.js → runAgentClientLoop-C3QB2EXV.js} +45 -38
  114. package/{setDialogExtraReferencesAction-JZEXA5SL.js → setDialogExtraReferencesAction-DK4CKERU.js} +5 -5
  115. package/{setPrimaryDialogAgentAction-4ZUQQKDG.js → setPrimaryDialogAgentAction-SJSO6N27.js} +8 -7
  116. package/{settingSlice-7MNKHZ6R.js → settingSlice-XNM5AFPU.js} +6 -5
  117. package/{spaceCommands-DHCZ7J5I.js → spaceCommands-SXYOFGUJ.js} +25 -24
  118. package/{spaceSlice-ZXLLG3CR.js → spaceSlice-6BHMRP5O.js} +21 -18
  119. package/{streamAgentChatTurn-L3YW6LSB.js → streamAgentChatTurn-GF5W5OIP.js} +140 -98
  120. package/switchDialogAgentAction-4OAC6QRN.js +61 -0
  121. package/{tableCommands-BEQAGE4I.js → tableCommands-QSZILI7I.js} +1 -1
  122. package/{toolApiClient-QI3YBNWB.js → toolApiClient-ISM4ZQX4.js} +2 -2
  123. package/{tools-ECGXR5QF.js → tools-KBCLJ43G.js} +36 -33
  124. package/{tuiSummaryLlmCaller-OMSCNHXA.js → tuiSummaryLlmCaller-ARIMGILF.js} +6 -6
  125. package/{updateCommands-XTW55TOT.js → updateCommands-AK2RDOFR.js} +8 -2
  126. package/{updateDialogTitleAction-HQ2EUDFT.js → updateDialogTitleAction-LMHZUY4Q.js} +28 -27
  127. package/chunk-DL6EPNIK.js +0 -50
  128. package/chunk-V5R4NZP2.js +0 -78
  129. package/chunk-X4Q4UZEY.js +0 -26
  130. package/queryRunOverlay-O4LOMMPO.js +0 -161
  131. package/runOverlayPresentation-ZO4VCWEU.js +0 -11
  132. package/types-ZSTJ7MPR.js +0 -7
  133. package/{chunk-X7W4TNB4.js → chunk-CMKUBIGW.js} +3 -3
  134. package/{runtimeDoctorCommands-E2OUORHK.js → runtimeDoctorCommands-HSU632DY.js} +3 -3
@@ -3,23 +3,9 @@ import {
3
3
  normalizeDialogRecord,
4
4
  readDialogSnapshot,
5
5
  sortDialogs
6
- } from "./chunk-V3ED5AWK.js";
7
- import {
8
- listFavoriteAgentIdsAcrossServers,
9
- listLocalCachedAgents,
10
- listRemoteAgents,
11
- listRemoteAgentsAcrossServers
12
- } from "./chunk-HWW2M4SN.js";
13
- import "./chunk-ID4G2MAR.js";
14
- import {
15
- listUserRecordsFromServers
16
- } from "./chunk-HEGV7BH3.js";
17
- import {
18
- buildOverlayPresentation
19
- } from "./chunk-V5R4NZP2.js";
6
+ } from "./chunk-YJY7HODU.js";
20
7
  import {
21
8
  ANSI_ESCAPE_REGEX,
22
- CLI_AUTO_TIER_AGENT_KEYS,
23
9
  CLI_AUTO_TIER_AGENT_KEY_TABLE,
24
10
  CLI_IMAGE_AGENT_KEY,
25
11
  COLLAPSED_PASTE_PLACEHOLDER_RE,
@@ -45,14 +31,15 @@ import {
45
31
  getActiveDensity,
46
32
  getActiveThemeName,
47
33
  isBalanceExhaustedError,
34
+ isQuotaExhaustedError,
48
35
  normalizeToolDisplayMode,
49
36
  padOrTruncateToWidth,
50
37
  releaseCollapsedPaste,
51
38
  releaseCollapsedPasteReferences,
39
+ renderSurfaceLine,
52
40
  renderTokenStatus,
53
41
  replaceCollapsedPastesWithReferences,
54
42
  resolveAgentContextWindow,
55
- resolveCliAutoFallbackTier,
56
43
  resolveCliColorEnabled,
57
44
  resolveSkillReference,
58
45
  resolveTuiBrightness,
@@ -68,141 +55,143 @@ import {
68
55
  themeColorSequence,
69
56
  themeText,
70
57
  truncateAnsi,
58
+ userSurfaceBackgroundSequence,
71
59
  visibleWidth,
72
60
  wrapTextToLines,
73
61
  wrapTranscriptLine
74
- } from "./chunk-BV4VEE3W.js";
62
+ } from "./chunk-S7AV3NCK.js";
75
63
  import "./chunk-DZ3XJVET.js";
64
+ import "./chunk-6HYVJV7B.js";
76
65
  import {
77
- getReadableCliDb
78
- } from "./chunk-WFHXMLHZ.js";
66
+ listFavoriteAgentIdsAcrossServers,
67
+ listLocalCachedAgents,
68
+ listRemoteAgents,
69
+ listRemoteAgentsAcrossServers,
70
+ normalizeListedAgent
71
+ } from "./chunk-TNM27YZ7.js";
72
+ import "./chunk-ID4G2MAR.js";
79
73
  import {
80
- deleteDbRecord,
81
- queryUserRecords,
82
- readDbRecord
83
- } from "./chunk-DFCFMCY6.js";
74
+ listUserRecordsFromServers,
75
+ readLiveDbRecordAfterTombstoneMerge
76
+ } from "./chunk-PMCKFQYC.js";
84
77
  import {
85
78
  createCliLocalRuntimeAdapter,
86
79
  initCliLocale,
87
- isQuotaExhaustedError,
88
80
  parseCliLocale,
89
81
  readTimestamp,
90
82
  setCliLocale,
91
83
  t
92
- } from "./chunk-23FZYPLC.js";
93
- import "./chunk-OK6YSWLH.js";
94
- import "./chunk-SWDPIPOY.js";
84
+ } from "./chunk-PTKI7TIN.js";
85
+ import "./chunk-TWA4L7J7.js";
86
+ import {
87
+ getReadableCliDb
88
+ } from "./chunk-WFHXMLHZ.js";
89
+ import {
90
+ deleteDbRecord,
91
+ queryUserRecords,
92
+ readDbRecord
93
+ } from "./chunk-J2WT5CEO.js";
94
+ import "./chunk-AZW5AHMW.js";
95
95
  import {
96
+ NOLO_DEFAULT_AGENT_KEY,
96
97
  resolveCliAgentKeyInput
97
98
  } from "./chunk-VHBWHNVT.js";
98
99
  import "./chunk-LTRXGFMS.js";
99
- import "./chunk-MVQ4TBSD.js";
100
- import "./chunk-6HYVJV7B.js";
100
+ import "./chunk-54VHTARN.js";
101
101
  import "./chunk-JEXVIMYZ.js";
102
102
  import {
103
103
  prepareTools
104
- } from "./chunk-CZ6NYQTV.js";
105
- import "./chunk-FXUSIQC2.js";
106
- import {
107
- expandEnabledPacks
108
- } from "./chunk-EDV7EBOI.js";
104
+ } from "./chunk-ZFL3LTU7.js";
105
+ import "./chunk-XNZJVYFS.js";
106
+ import "./chunk-GGB6MPHD.js";
109
107
  import "./chunk-RQSOAMPJ.js";
110
- import "./chunk-3ZX7PERL.js";
108
+ import "./chunk-I6RJGORZ.js";
109
+ import "./chunk-AAWJ5S3G.js";
111
110
  import "./chunk-KQHGZWSV.js";
112
- import "./chunk-4T5F7YZQ.js";
113
111
  import "./chunk-3LY4F7SV.js";
114
112
  import "./chunk-VF4K2QE6.js";
115
- import "./chunk-G5U6LDLG.js";
116
- import {
117
- initialRunOverlayState,
118
- reduceRunOverlay
119
- } from "./chunk-DL6EPNIK.js";
120
- import "./chunk-OWOHNGCN.js";
121
- import "./chunk-JLA4FXYC.js";
113
+ import "./chunk-CWTT2NZ6.js";
114
+ import "./chunk-MALVZUYL.js";
122
115
  import "./chunk-VP4WJWKW.js";
123
116
  import "./chunk-63W5N6P3.js";
124
117
  import "./chunk-WJRJDX2W.js";
125
- import "./chunk-LM6IYQ3F.js";
126
- import "./chunk-IYEXFJPC.js";
118
+ import "./chunk-GNGOZMU2.js";
127
119
  import {
128
120
  extractReferenceKeysFromMessage
129
- } from "./chunk-SCTYAHYK.js";
121
+ } from "./chunk-ADE6577S.js";
130
122
  import {
131
123
  buildBuiltinSummaryContent
132
124
  } from "./chunk-XOIF2TNF.js";
133
- import "./chunk-B5TOXZAK.js";
134
125
  import "./chunk-MRC4Q34M.js";
135
- import "./chunk-REA4JLBH.js";
126
+ import "./chunk-SIZQB7BD.js";
127
+ import "./chunk-GD23ZX7Z.js";
128
+ import "./chunk-YYLHWIW6.js";
136
129
  import "./chunk-CWMSB67P.js";
137
- import "./chunk-XHG22OEX.js";
138
- import "./chunk-Z4NR32XR.js";
130
+ import "./chunk-ROJI6DRT.js";
131
+ import "./chunk-5RGU3J7P.js";
139
132
  import "./chunk-M76ECQRD.js";
140
- import "./chunk-3Q7WHGCB.js";
133
+ import "./chunk-L65QKSVJ.js";
141
134
  import {
142
135
  buildSkillDiscoveryContextBlock
143
136
  } from "./chunk-7OBMZCRD.js";
144
- import "./chunk-7BUYEZ6U.js";
145
- import "./chunk-VC6OZXO5.js";
137
+ import "./chunk-7H3UTGPM.js";
138
+ import {
139
+ expandEnabledPacks
140
+ } from "./chunk-CF7S2RYZ.js";
141
+ import "./chunk-ZWJOGMKH.js";
146
142
  import "./chunk-WZSRQNQG.js";
147
143
  import {
148
144
  buildLocalWorkspaceOpenAiTools,
149
145
  buildLocalWorkspaceToolset,
150
146
  getProcessRegistry
151
- } from "./chunk-R35XMF6F.js";
147
+ } from "./chunk-SIUEKVK4.js";
152
148
  import "./chunk-ZNFJWEN4.js";
153
- import "./chunk-N54HIJVJ.js";
149
+ import "./chunk-MCXQNEJ3.js";
154
150
  import "./chunk-OIQPORYI.js";
155
151
  import {
156
152
  readCommandActionGatePayload,
157
153
  resolveAgentImageInputSupport
158
- } from "./chunk-YGW4G5W5.js";
159
- import "./chunk-LSFAWXKC.js";
154
+ } from "./chunk-P3RZ6DHN.js";
155
+ import "./chunk-N7J63Q6C.js";
160
156
  import {
161
157
  buildCurrentTimeBlock,
162
158
  buildIdentityBlock,
163
159
  buildRuntimeGuidanceBlocks
164
- } from "./chunk-34WPV75U.js";
165
- import "./chunk-KFEVY3ST.js";
160
+ } from "./chunk-ZUXIXYJI.js";
161
+ import "./chunk-D34RI6NI.js";
166
162
  import {
167
163
  resolvePlatformAuthToken
168
- } from "./chunk-MHSGJW4Z.js";
169
- import "./chunk-DKZFOK24.js";
164
+ } from "./chunk-PXPISYEH.js";
165
+ import "./chunk-WCP5FDTF.js";
166
+ import "./chunk-2MBTWINN.js";
170
167
  import {
171
168
  DEFAULT_CONTEXT_WINDOW,
172
169
  estimateTokenCount,
173
170
  getModelContextWindow,
174
171
  planCompression,
175
172
  serializeMessageContent
176
- } from "./chunk-I2ZJKTG5.js";
173
+ } from "./chunk-PY7VGC5M.js";
177
174
  import {
178
175
  clipCompactText
179
176
  } from "./chunk-5MMAK75Q.js";
180
177
  import "./chunk-5CS33IBL.js";
181
178
  import "./chunk-JZPBVBIY.js";
182
- import "./chunk-UAIVG4J6.js";
179
+ import "./chunk-LEHTC2FI.js";
183
180
  import "./chunk-ZHXJ6M7T.js";
184
- import {
185
- toErrorMessage
186
- } from "./chunk-HNMXYYJR.js";
187
- import "./chunk-ELT65PZW.js";
188
181
  import "./chunk-AL2V7U3E.js";
189
182
  import "./chunk-4PWB7JRR.js";
190
183
  import {
191
184
  canonicalizeToolNames
192
185
  } from "./chunk-ZZ2MH7LE.js";
193
- import "./chunk-5MIDPSSK.js";
194
- import {
195
- extractCustomId
196
- } from "./chunk-DFFIZ25G.js";
186
+ import "./chunk-PB7G5MAM.js";
197
187
  import "./chunk-3ASDTTEI.js";
198
188
  import {
199
189
  compactWhitespace
200
190
  } from "./chunk-DWLEOOGR.js";
201
191
  import {
202
192
  checkStaleRun,
203
- queryRunRecords,
204
193
  readRunRecord
205
- } from "./chunk-VNAEBGDK.js";
194
+ } from "./chunk-RZNYL4LW.js";
206
195
  import {
207
196
  clipText,
208
197
  formatDuration,
@@ -213,17 +202,22 @@ import {
213
202
  resolveRunLabel,
214
203
  runShowsLogTail,
215
204
  shortRunId
216
- } from "./chunk-FQZZLA3C.js";
217
- import "./chunk-X7W4TNB4.js";
218
- import "./chunk-ATNTHIZI.js";
205
+ } from "./chunk-KXKQO4Q6.js";
206
+ import "./chunk-CMKUBIGW.js";
219
207
  import "./chunk-JBTJAPUF.js";
208
+ import "./chunk-ATNTHIZI.js";
209
+ import "./chunk-LM6IYQ3F.js";
210
+ import {
211
+ toErrorMessage
212
+ } from "./chunk-HNMXYYJR.js";
220
213
  import {
214
+ checkForCliUpdate,
221
215
  runSelfUpdate
222
- } from "./chunk-K6EUAOW3.js";
216
+ } from "./chunk-FXFGNOT2.js";
223
217
  import {
224
218
  saveProfileAgentSelection,
225
219
  saveProfileLocale
226
- } from "./chunk-ROWQEJRP.js";
220
+ } from "./chunk-GE6IWA5N.js";
227
221
  import {
228
222
  readPipeText,
229
223
  spawnProcess
@@ -237,27 +231,31 @@ import {
237
231
  import {
238
232
  DEFAULT_NOLO_SERVER_URL
239
233
  } from "./chunk-OLZBQWZU.js";
240
- import "./chunk-LLWKHDG4.js";
241
- import "./chunk-MKA6QE5E.js";
242
- import "./chunk-UGUW3FHG.js";
243
- import "./chunk-RVOUXDFU.js";
234
+ import "./chunk-G36SUBHA.js";
235
+ import "./chunk-CZHE6LLV.js";
236
+ import "./chunk-JMSCXQUG.js";
237
+ import "./chunk-S6KVFOUG.js";
244
238
  import "./chunk-Y5GCAMGP.js";
239
+ import "./chunk-UZ6XXCVR.js";
245
240
  import "./chunk-KX6YBIHG.js";
246
- import "./chunk-XRZJGPFI.js";
247
- import "./chunk-6Y7A3OQQ.js";
241
+ import "./chunk-OXLLFZNF.js";
242
+ import "./chunk-FON47ACC.js";
243
+ import "./chunk-NRY4GCKJ.js";
248
244
  import "./chunk-WWC7I6TA.js";
249
245
  import "./chunk-SKDNBYZK.js";
250
246
  import "./chunk-I42VC5KR.js";
251
247
  import {
252
248
  asTrimmedLowercaseString
253
249
  } from "./chunk-QSJ3HFEF.js";
254
- import "./chunk-X4Q4UZEY.js";
255
- import "./chunk-3ND52WIL.js";
250
+ import "./chunk-ATWUOTJP.js";
256
251
  import "./chunk-XMLRJUU5.js";
257
252
  import "./chunk-R5V5G73M.js";
258
253
  import "./chunk-GBRG75BQ.js";
259
254
  import "./chunk-3EUW3WEO.js";
260
255
  import "./chunk-U6VC4MU4.js";
256
+ import {
257
+ extractCustomId
258
+ } from "./chunk-SLOGKB7M.js";
261
259
  import "./chunk-KUG52IAP.js";
262
260
  import "./chunk-LCBAMY2M.js";
263
261
  import "./chunk-4SN6J65J.js";
@@ -526,6 +524,7 @@ async function compactDialog(options) {
526
524
  dialogKey
527
525
  );
528
526
  let summaryGenerated = false;
527
+ let compactedMessageCount = 0;
529
528
  if (options.summaryLlmCaller) {
530
529
  const allMsgs = await readDialogMessages(
531
530
  fetchImpl,
@@ -580,6 +579,7 @@ async function compactDialog(options) {
580
579
  current.compressionCount = compressionCount + 1;
581
580
  current.referenceKeys = Array.from(extractedKeys);
582
581
  summaryGenerated = true;
582
+ compactedMessageCount = plan.compressCount;
583
583
  console.log(
584
584
  `[nolo] compact: compressed ${plan.compressCount} messages into summary (len: ${newSummary.trim().length})`
585
585
  );
@@ -602,7 +602,8 @@ async function compactDialog(options) {
602
602
  dialogId: extractCustomId(next.dbKey),
603
603
  dialogKey: next.dbKey,
604
604
  spaceId: typeof next.spaceId === "string" ? next.spaceId : void 0,
605
- summaryGenerated
605
+ summaryGenerated,
606
+ compactedMessageCount
606
607
  };
607
608
  }
608
609
 
@@ -612,7 +613,7 @@ var DIALOG_CHECKED = "\u25C9";
612
613
  var DIALOG_UNCHECKED = "\u25CB";
613
614
  function renderDialogTitle(title, colorEnabled = resolveCliColorEnabled()) {
614
615
  if (!colorEnabled) return title;
615
- return `\x1B[1m${themeColorSequence("accent")}${title}\x1B[0m`;
616
+ return `${themeColorSequence("accent")}${title}\x1B[0m`;
616
617
  }
617
618
  function renderDialogRow(args, colorEnabled = resolveCliColorEnabled()) {
618
619
  const marker = args.focused ? DIALOG_CURSOR : " ";
@@ -620,7 +621,7 @@ function renderDialogRow(args, colorEnabled = resolveCliColorEnabled()) {
620
621
  const detail = args.detail ? ` ${themeText(args.detail, "muted", colorEnabled)}` : "";
621
622
  const row = `${marker} ${checkbox}${args.label}${detail}`;
622
623
  if (!args.focused || !colorEnabled) return row;
623
- return `${surfaceBackgroundSequence()}${themeColorSequence("accent")}\x1B[1m${row}\x1B[0m`;
624
+ return `${surfaceBackgroundSequence()}${themeColorSequence("accent")}${row}\x1B[0m`;
624
625
  }
625
626
  function renderDialogOverflow(text, colorEnabled = resolveCliColorEnabled()) {
626
627
  return themeText(` ${text}`, "chrome", colorEnabled);
@@ -1328,7 +1329,7 @@ function renderTabBar(questions, activeIndex, colorEnabled) {
1328
1329
  const tabs = questions.map((q, i) => {
1329
1330
  const label = `Q${i + 1}`;
1330
1331
  if (i === activeIndex) {
1331
- return colorEnabled ? `${themeColorSequence("accent")}\x1B[1m ${label} \x1B[0m` : `[${label}]`;
1332
+ return colorEnabled ? `${themeColorSequence("accent")} ${label} \x1B[0m` : `[${label}]`;
1332
1333
  }
1333
1334
  return colorEnabled ? themeText(` ${label} `, "muted", colorEnabled) : ` ${label} `;
1334
1335
  });
@@ -1395,7 +1396,7 @@ function renderAskChoiceFrame(state) {
1395
1396
  const plainRow = `${plainPrefix}${qs.otherText}`;
1396
1397
  if (focused && colorEnabled) {
1397
1398
  lines.push(
1398
- `\x1B[1m${themeColorSequence("accent")}${plainRow}\x1B[0m`
1399
+ `${themeColorSequence("accent")}${plainRow}\x1B[0m`
1399
1400
  );
1400
1401
  } else {
1401
1402
  lines.push(plainRow);
@@ -2159,6 +2160,7 @@ function createRunRegistryPoller(deps) {
2159
2160
  return;
2160
2161
  }
2161
2162
  const alive = /* @__PURE__ */ new Set();
2163
+ const polled = [];
2162
2164
  for (const run of active) {
2163
2165
  alive.add(run.runId);
2164
2166
  let record;
@@ -2172,6 +2174,7 @@ function createRunRegistryPoller(deps) {
2172
2174
  continue;
2173
2175
  }
2174
2176
  if (!record) continue;
2177
+ polled.push(record);
2175
2178
  const snapshot = snapshotFromRunRecord(record, at);
2176
2179
  const mark = fingerprint(snapshot);
2177
2180
  if (lastEmitted.get(run.runId) === mark) continue;
@@ -2184,6 +2187,12 @@ function createRunRegistryPoller(deps) {
2184
2187
  for (const runId of [...lastReconciledAt.keys()]) {
2185
2188
  if (!alive.has(runId)) lastReconciledAt.delete(runId);
2186
2189
  }
2190
+ if (polled.length > 0 && deps.onRecordsPolled) {
2191
+ try {
2192
+ deps.onRecordsPolled(polled);
2193
+ } catch {
2194
+ }
2195
+ }
2187
2196
  };
2188
2197
  return {
2189
2198
  ensureRunning() {
@@ -2199,6 +2208,163 @@ function createRunRegistryPoller(deps) {
2199
2208
  };
2200
2209
  }
2201
2210
 
2211
+ // packages/core/chat/internalTurnEvent.ts
2212
+ function normalizeRunCompletionShape(record) {
2213
+ const runId = String(record.runId ?? record.id ?? "");
2214
+ const agentKey = String(record.agentKey ?? record.agent ?? "");
2215
+ const status = String(record.status ?? "done");
2216
+ const counters = record.activity?.counters;
2217
+ return {
2218
+ runId,
2219
+ agentKey,
2220
+ ...typeof record.agentName === "string" ? { agentName: record.agentName } : {},
2221
+ status,
2222
+ ...typeof record.exitCode === "number" ? { exitCode: record.exitCode } : {},
2223
+ ...typeof record.parentDialogId === "string" ? { parentDialogId: record.parentDialogId } : {},
2224
+ ...typeof record.dialogId === "string" ? { dialogId: record.dialogId } : {},
2225
+ ...typeof record.ephemeral === "boolean" ? { ephemeral: record.ephemeral } : {},
2226
+ ...record.startedAt !== void 0 ? { startedAt: record.startedAt } : {},
2227
+ ...record.endedAt !== void 0 ? { endedAt: record.endedAt } : {},
2228
+ ...typeof record.note === "string" ? { note: record.note } : {},
2229
+ ...typeof record.error === "string" ? { error: record.error } : {},
2230
+ ...typeof record.ack === "boolean" ? { ack: record.ack } : {},
2231
+ ...counters ? {
2232
+ activity: {
2233
+ counters: {
2234
+ toolCalls: typeof counters.toolCalls === "number" ? counters.toolCalls : 0,
2235
+ llmCalls: typeof counters.llmCalls === "number" ? counters.llmCalls : 0,
2236
+ fileEdits: typeof counters.fileEdits === "number" ? counters.fileEdits : 0
2237
+ }
2238
+ }
2239
+ } : {}
2240
+ };
2241
+ }
2242
+ function createTurnRequest(input) {
2243
+ if (typeof input === "string") {
2244
+ return {
2245
+ event: { kind: "user", text: input },
2246
+ text: input
2247
+ };
2248
+ }
2249
+ if ("event" in input && typeof input.event === "object" && typeof input.text === "string") {
2250
+ return input;
2251
+ }
2252
+ const event = input;
2253
+ return {
2254
+ event,
2255
+ text: event.text
2256
+ };
2257
+ }
2258
+
2259
+ // packages/cli/tui/runCompletionWatcher.ts
2260
+ var WAKE_NOTE_MAX_CHARS = 300;
2261
+ function createRunCompletionWatcher(deps) {
2262
+ const now = deps.now ?? (() => Date.now());
2263
+ const createdAt = now();
2264
+ const lastStatusByRunId = /* @__PURE__ */ new Map();
2265
+ const notifiedRunIds = /* @__PURE__ */ new Set();
2266
+ const describeRun = (record) => {
2267
+ const lines = [
2268
+ `runId: ${record.runId}`,
2269
+ `agent: ${resolveRunLabel(record)}`,
2270
+ `status: ${record.status}`
2271
+ ];
2272
+ if (typeof record.exitCode === "number") {
2273
+ lines.push(`exitCode: ${record.exitCode}`);
2274
+ }
2275
+ if (record.dialogId) {
2276
+ lines.push(`childDialogId: ${record.dialogId}`);
2277
+ } else if (record.ephemeral) {
2278
+ lines.push(`ephemeral: true (no persisted child dialog)`);
2279
+ } else {
2280
+ lines.push(`childDialogId: missing (result unpersisted or ephemeral)`);
2281
+ }
2282
+ const duration = formatRunAge(
2283
+ {
2284
+ startedAt: readTimestamp(record.startedAt),
2285
+ finishedAt: readTimestamp(record.endedAt ?? record.finishedAt)
2286
+ },
2287
+ now()
2288
+ );
2289
+ if (duration) lines.push(`duration: ${duration}`);
2290
+ const recordError = "error" in record && typeof record.error === "string" ? record.error : void 0;
2291
+ const note = typeof record.note === "string" ? clipText(record.note, WAKE_NOTE_MAX_CHARS) : typeof recordError === "string" ? clipText(recordError, WAKE_NOTE_MAX_CHARS) : "";
2292
+ if (note) lines.push(`note: ${note}`);
2293
+ const counters = record.activity?.counters;
2294
+ if (counters && typeof counters.toolCalls === "number" && typeof counters.llmCalls === "number" && typeof counters.fileEdits === "number") {
2295
+ lines.push(
2296
+ `activity: ${counters.toolCalls} tool calls \xB7 ${counters.llmCalls} llm calls \xB7 ${counters.fileEdits} edits`
2297
+ );
2298
+ }
2299
+ return lines;
2300
+ };
2301
+ const buildWakeMessage = (finished) => {
2302
+ const lines = [
2303
+ `\u3010\u540E\u53F0 run \u7EC8\u6001\u901A\u77E5\u3011\u4F60\u6D3E\u51FA\u7684 ${finished.length} \u6761\u540E\u53F0 run \u5DF2\u5230\u8FBE\u7EC8\u6001\uFF1A`
2304
+ ];
2305
+ for (const record of finished) {
2306
+ lines.push("", ...describeRun(record));
2307
+ }
2308
+ lines.push(
2309
+ "",
2310
+ '\u4EE5\u4E0A\u662F\u4F60\u901A\u8FC7 startAgentRun \u6D3E\u51FA\u7684\u540E\u53F0 run \u7684\u7EC8\u6001\u901A\u77E5\uFF08\u7CFB\u7EDF\u5185\u90E8\u4E8B\u4EF6\uFF0C\u4E0D\u662F\u7528\u6237\u6D88\u606F\uFF09\u3002\u8BF7\u9605\u8BFB\u4E0A\u9762\u7684\u6458\u8981\u5E76\u81EA\u5DF1\u51B3\u5B9A\u4E0B\u4E00\u6B65\uFF1A\u6C47\u603B\u7ED3\u679C\u3001\u7EE7\u7EED\u540E\u7EED\u5DE5\u4F5C\u3001\u6216\u5411\u7528\u6237\u6C47\u62A5\u7ED3\u8BBA\u3002\u9700\u8981\u5B8C\u6574\u8F93\u51FA\u65F6\u7528 controlAgentRun(action:"status", runId, tailLines:30) \u62C9\u53D6\u5BF9\u5E94 run \u7684\u65E5\u5FD7\u3002'
2311
+ );
2312
+ return lines.join("\n");
2313
+ };
2314
+ return {
2315
+ observe(records) {
2316
+ const finished = [];
2317
+ const currentDialogId = deps.getCurrentDialogId();
2318
+ for (const rawRecord of records) {
2319
+ try {
2320
+ if (!rawRecord || typeof rawRecord.runId !== "string" || !rawRecord.runId) continue;
2321
+ const record = rawRecord;
2322
+ if (notifiedRunIds.has(record.runId)) continue;
2323
+ if (record.ack === true) {
2324
+ notifiedRunIds.add(record.runId);
2325
+ continue;
2326
+ }
2327
+ const status = record.status;
2328
+ if (typeof status !== "string") continue;
2329
+ if (record.parentDialogId !== currentDialogId) continue;
2330
+ const previous = lastStatusByRunId.get(record.runId);
2331
+ lastStatusByRunId.set(record.runId, status);
2332
+ if (!isAgentRunTerminalStatus(status)) continue;
2333
+ if (previous === void 0) {
2334
+ const startedMs = readTimestamp(record.startedAt);
2335
+ if (startedMs === void 0 || startedMs < createdAt) continue;
2336
+ } else if (isAgentRunTerminalStatus(previous)) {
2337
+ continue;
2338
+ }
2339
+ notifiedRunIds.add(record.runId);
2340
+ finished.push(record);
2341
+ } catch {
2342
+ continue;
2343
+ }
2344
+ }
2345
+ if (finished.length === 0) return;
2346
+ const shapes = finished.map((r) => normalizeRunCompletionShape(r));
2347
+ const summaryText = buildWakeMessage(finished);
2348
+ const event = {
2349
+ kind: "child-run-completed",
2350
+ runs: shapes,
2351
+ text: summaryText
2352
+ };
2353
+ deps.onWake(event);
2354
+ },
2355
+ markAcknowledged(runId) {
2356
+ if (runId) notifiedRunIds.add(runId);
2357
+ },
2358
+ isAcknowledged(runId) {
2359
+ return Boolean(runId && notifiedRunIds.has(runId));
2360
+ },
2361
+ dispose() {
2362
+ lastStatusByRunId.clear();
2363
+ notifiedRunIds.clear();
2364
+ }
2365
+ };
2366
+ }
2367
+
2202
2368
  // packages/ai/agent/utils/sortUtils.ts
2203
2369
  function sortAgentsFavoriteOwnedPublic(items) {
2204
2370
  const map = /* @__PURE__ */ new Map();
@@ -2245,7 +2411,7 @@ function sortAgentsFavoriteOwnedPublic(items) {
2245
2411
  }
2246
2412
 
2247
2413
  // packages/cli/tui/agentCatalog.ts
2248
- var DEFAULT_TUI_AGENT_KEY = "agent-pub-01NOLOAPPBLD000000019KCKT0";
2414
+ var DEFAULT_TUI_AGENT_KEY = NOLO_DEFAULT_AGENT_KEY;
2249
2415
  function formatAgentSourceLabel(entry) {
2250
2416
  if (entry.kind === "platform" || entry.apiSource === "platform") return t("agentSourcePlatform");
2251
2417
  if (entry.apiSource === "cli") {
@@ -2261,13 +2427,6 @@ var PLATFORM_AGENTS = [
2261
2427
  model: "-",
2262
2428
  kind: "platform",
2263
2429
  description: "one assistant that routes work across your agents and data"
2264
- },
2265
- {
2266
- name: "app-builder",
2267
- key: "agent-pub-01APPBUILDER00000001YAII3I",
2268
- model: "-",
2269
- kind: "platform",
2270
- description: "builds web apps, tools, charts, and product prototypes"
2271
2430
  }
2272
2431
  ];
2273
2432
  var AUTO_ROUTE_CATALOG_ENTRY = {
@@ -2316,7 +2475,7 @@ function mergeCatalogEntries(currentKey, platformAgents, privateAgents, favorite
2316
2475
  }
2317
2476
  const sortablePrivate = privateAgents.map((entry) => ({
2318
2477
  key: entry.key,
2319
- favoritedAt: favoritedAtByKey[entry.key],
2478
+ ...favoritedAtByKey[entry.key] !== void 0 ? { favoritedAt: favoritedAtByKey[entry.key] } : {},
2320
2479
  isOwned: true,
2321
2480
  updatedAt: entry.updatedAt ?? 0
2322
2481
  }));
@@ -2384,9 +2543,15 @@ function refreshAgentCatalogInBackground(args, env, cacheKey) {
2384
2543
  agentCatalogRefreshInFlight = null;
2385
2544
  });
2386
2545
  }
2546
+ async function prefillCatalogFromLocalDb(_args) {
2547
+ }
2387
2548
  function prefetchAgentCatalog(args) {
2388
- void loadAgentCatalog({ ...args, currentKey: "" }).catch(() => {
2389
- });
2549
+ void (async () => {
2550
+ await prefillCatalogFromLocalDb({ env: args.env, getDb: args.getDb }).catch(() => {
2551
+ });
2552
+ void loadAgentCatalog({ ...args, currentKey: "" }).catch(() => {
2553
+ });
2554
+ })();
2390
2555
  }
2391
2556
  async function fetchRawCatalogData(args, env) {
2392
2557
  const fetchImpl = args.fetchImpl ?? fetch;
@@ -2398,7 +2563,7 @@ async function fetchRawCatalogData(args, env) {
2398
2563
  }
2399
2564
  const serverUrl = resolveServerUrl(env);
2400
2565
  const serverUrls = resolveServerCandidates([], env, serverUrl);
2401
- let privateAgents = [];
2566
+ let listedAgents = [];
2402
2567
  const favoritesPromise = listFavoriteAgentIdsAcrossServers({
2403
2568
  authToken,
2404
2569
  fetchImpl,
@@ -2412,15 +2577,14 @@ async function fetchRawCatalogData(args, env) {
2412
2577
  serverUrls,
2413
2578
  userId
2414
2579
  });
2415
- privateAgents = remoteResult.agents.map(listedAgentToCatalogEntry);
2580
+ listedAgents = remoteResult.agents;
2416
2581
  } catch {
2417
2582
  try {
2418
2583
  const db = await getReadableCliDb({ write: () => {
2419
2584
  } });
2420
- const cached = await listLocalCachedAgents({ db, userId });
2421
- privateAgents = cached.map(listedAgentToCatalogEntry);
2585
+ listedAgents = await listLocalCachedAgents({ db, userId });
2422
2586
  } catch {
2423
- privateAgents = (await listRemoteAgents({
2587
+ listedAgents = await listRemoteAgents({
2424
2588
  authToken,
2425
2589
  fallbackFetchImpl,
2426
2590
  fetchImpl,
@@ -2428,10 +2592,47 @@ async function fetchRawCatalogData(args, env) {
2428
2592
  userId,
2429
2593
  queryUserRecords,
2430
2594
  readDbRecord
2431
- })).map(listedAgentToCatalogEntry);
2595
+ });
2432
2596
  }
2433
2597
  }
2598
+ const privateAgents = listedAgents.map(listedAgentToCatalogEntry);
2434
2599
  const favoritedAtByKey = await favoritesPromise;
2600
+ for (const agent of listedAgents) {
2601
+ const favoritedAt = [agent.privateKey, agent.publicKey, agent.id].map((key) => favoritedAtByKey[key]).find((value) => value !== void 0);
2602
+ if (favoritedAt !== void 0) favoritedAtByKey[agent.privateKey] = favoritedAt;
2603
+ }
2604
+ const existingKeys = /* @__PURE__ */ new Set();
2605
+ for (const agent of listedAgents) {
2606
+ existingKeys.add(agent.privateKey);
2607
+ existingKeys.add(agent.publicKey);
2608
+ existingKeys.add(agent.id);
2609
+ }
2610
+ await Promise.all(
2611
+ Object.keys(favoritedAtByKey).map(async (favKey) => {
2612
+ if (existingKeys.has(favKey)) return;
2613
+ try {
2614
+ const favRead = await readLiveDbRecordAfterTombstoneMerge({
2615
+ authToken,
2616
+ dbKey: favKey,
2617
+ fallbackFetchImpl,
2618
+ fetchImpl,
2619
+ serverUrls
2620
+ });
2621
+ const record = favRead.record;
2622
+ if (!record || record.type && record.type !== "agent") return;
2623
+ const norm = normalizeListedAgent(record);
2624
+ if (!norm) return;
2625
+ if (existingKeys.has(norm.privateKey) || existingKeys.has(norm.publicKey) || existingKeys.has(norm.id)) {
2626
+ return;
2627
+ }
2628
+ existingKeys.add(norm.privateKey);
2629
+ existingKeys.add(norm.publicKey);
2630
+ existingKeys.add(norm.id);
2631
+ privateAgents.push(listedAgentToCatalogEntry(norm));
2632
+ } catch {
2633
+ }
2634
+ })
2635
+ );
2435
2636
  return { privateAgents, favoritedAtByKey };
2436
2637
  }
2437
2638
  function renderAgentCatalogList(entries, currentKey) {
@@ -3096,8 +3297,17 @@ function renderWelcome(state, frame = 0, maxFrames = 0, columns) {
3096
3297
  const widestSceneCol = sceneArt.split("\n").reduce((max, line) => Math.max(max, displayWidth(stripAnsi(line))), 0);
3097
3298
  if (columns < widestSceneCol) sceneArt = "";
3098
3299
  }
3099
- const versionLine = colorEnabled ? `\x1B[1m${themeColorSequence("accent")}nolo\x1B[0m ${state.cliVersion ?? ""} | server ${state.serverUrl}`.replace(" |", " |") : `nolo ${state.cliVersion ?? ""} | server ${state.serverUrl}`.replace(" |", " |");
3100
- const body = sceneArt ? [sceneArt, versionLine, t("welcomeHint"), ""] : [versionLine, t("welcomeHint"), ""];
3300
+ const versionLine = colorEnabled ? `${themeColorSequence("accent")}nolo\x1B[0m ${state.cliVersion ?? ""} | server ${state.serverUrl}`.replace(" |", " |") : `nolo ${state.cliVersion ?? ""} | server ${state.serverUrl}`.replace(" |", " |");
3301
+ const updateLine = state.updateAvailable ? themeText(
3302
+ t(
3303
+ "updateAvailable",
3304
+ state.updateAvailable.latestVersion,
3305
+ state.cliVersion ?? t("versionUnknown")
3306
+ ),
3307
+ "accent",
3308
+ colorEnabled
3309
+ ) : null;
3310
+ const body = sceneArt ? [sceneArt, versionLine, ...updateLine ? [updateLine] : [], t("welcomeHint"), ""] : [versionLine, ...updateLine ? [updateLine] : [], t("welcomeHint"), ""];
3101
3311
  return body.join("\n");
3102
3312
  }
3103
3313
  function renderPrompt(_state) {
@@ -3760,7 +3970,16 @@ function handleTuiInput(input, state) {
3760
3970
  };
3761
3971
  }
3762
3972
  return {
3763
- nextState: state,
3973
+ nextState: {
3974
+ ...state,
3975
+ dialogId: void 0,
3976
+ dialogKey: void 0,
3977
+ dialogLabel: t("newDialog"),
3978
+ attachedDocs: [],
3979
+ attachedSkills: [],
3980
+ attachedImages: [],
3981
+ turnTokens: void 0
3982
+ },
3764
3983
  output: t("clearingDialog"),
3765
3984
  action: { type: "clear", dialogId: state.dialogId }
3766
3985
  };
@@ -4127,52 +4346,65 @@ var initialChatQueueState = {
4127
4346
  function reduceChatQueue(state, event) {
4128
4347
  switch (event.type) {
4129
4348
  case "turn-start":
4130
- if (state.running) return state;
4131
- return { ...state, running: true, lastDrainError: null };
4132
- case "turn-end": {
4133
- if (!state.running) return state;
4134
- if (event.aborted) {
4135
- return {
4136
- ...state,
4137
- running: false,
4138
- queue: [],
4139
- lastDrainError: null
4140
- };
4141
- }
4349
+ if (state.running && state.lastDrainError === null) return state;
4350
+ return {
4351
+ ...state,
4352
+ running: true,
4353
+ lastDrainError: null
4354
+ };
4355
+ case "turn-end":
4142
4356
  return {
4143
4357
  ...state,
4144
4358
  running: false,
4145
- ...event.ok ? {} : { lastDrainError: "previous turn failed" }
4359
+ queue: event.aborted ? [] : state.queue,
4360
+ lastDrainError: !event.ok && !event.aborted ? "previous turn failed" : state.lastDrainError
4146
4361
  };
4147
- }
4148
4362
  case "enqueue": {
4149
- const text = event.text;
4150
- if (!text) return state;
4151
- return { ...state, queue: [...state.queue, text], lastDrainError: null };
4363
+ const req = createTurnRequest(event.text);
4364
+ if (!req.text.trim() && req.event.kind === "user") {
4365
+ return state;
4366
+ }
4367
+ return {
4368
+ ...state,
4369
+ queue: [...state.queue, req]
4370
+ };
4152
4371
  }
4153
- case "dequeue": {
4372
+ case "dequeue":
4154
4373
  if (state.queue.length === 0) return state;
4155
- return { ...state, queue: state.queue.slice(1) };
4156
- }
4374
+ return {
4375
+ ...state,
4376
+ queue: state.queue.slice(1)
4377
+ };
4157
4378
  case "clear":
4158
- if (state.queue.length === 0 && !state.lastDrainError) return state;
4159
- return { ...state, queue: [], lastDrainError: null };
4379
+ if (state.queue.length === 0 && state.lastDrainError === null) return state;
4380
+ return {
4381
+ ...state,
4382
+ queue: [],
4383
+ lastDrainError: null
4384
+ };
4160
4385
  case "pause-drain":
4161
4386
  if (state.drainPaused) return state;
4162
- return { ...state, drainPaused: true };
4387
+ return {
4388
+ ...state,
4389
+ drainPaused: true
4390
+ };
4163
4391
  case "resume-drain":
4164
4392
  if (!state.drainPaused) return state;
4165
- return { ...state, drainPaused: false };
4393
+ return {
4394
+ ...state,
4395
+ drainPaused: false
4396
+ };
4166
4397
  case "drain-error":
4167
- return { ...state, lastDrainError: event.message };
4168
- default: {
4169
- const _exhaustive = event;
4398
+ return {
4399
+ ...state,
4400
+ lastDrainError: event.message || null
4401
+ };
4402
+ default:
4170
4403
  return state;
4171
- }
4172
4404
  }
4173
4405
  }
4174
- function shouldDrainAfterTurnEnd(state, prevEndedOk) {
4175
- return !state.running && !state.drainPaused && state.queue.length > 0 && prevEndedOk;
4406
+ function shouldDrainAfterTurnEnd(state, cleanTurnEnd) {
4407
+ return cleanTurnEnd && !state.running && !state.drainPaused && state.queue.length > 0;
4176
4408
  }
4177
4409
  function applyChatQueueEvent(state, event) {
4178
4410
  const prevQueueLen = state.queue.length;
@@ -4191,7 +4423,8 @@ function applyChatQueueEvent(state, event) {
4191
4423
  }
4192
4424
  if (event.type === "turn-end" && !event.aborted && event.ok) {
4193
4425
  if (shouldDrainAfterTurnEnd(next, true)) {
4194
- outgoing.push({ type: "drain-ready", text: next.queue[0] });
4426
+ const head = next.queue[0];
4427
+ outgoing.push({ type: "drain-ready", text: head.text, request: head });
4195
4428
  }
4196
4429
  }
4197
4430
  return { state: next, outgoing };
@@ -4273,7 +4506,8 @@ function projectChatQueueStatus({
4273
4506
  }) {
4274
4507
  const preview = [];
4275
4508
  for (let i = 0; i < Math.min(maxPreview, state.queue.length); i++) {
4276
- const text = state.queue[i] ?? "";
4509
+ const item = state.queue[i];
4510
+ const text = typeof item === "string" ? item : item?.text ?? "";
4277
4511
  preview.push(text.length > previewCharLimit ? text.slice(0, previewCharLimit) + "\u2026" : text);
4278
4512
  }
4279
4513
  let composerPlaceholderKey = "default";
@@ -4312,10 +4546,19 @@ function createChatQueueTuiBinding(runTurn) {
4312
4546
  isCompactDialogSlashCommand: (s) => s === "/compact"
4313
4547
  });
4314
4548
  };
4315
- const enqueue = (text) => {
4316
- runtime.send({ type: "enqueue", text });
4549
+ const enqueue = (input) => {
4550
+ runtime.send({ type: "enqueue", text: input });
4317
4551
  return projectChatQueueStatus({ state: runtime.getState() });
4318
4552
  };
4553
+ const enqueueAndMaybeRun = async (input) => {
4554
+ enqueue(input);
4555
+ const status = runtime.getState();
4556
+ if (!status.running && !status.drainPaused) {
4557
+ await notifyTurnEnd({ ok: true, aborted: false });
4558
+ return true;
4559
+ }
4560
+ return false;
4561
+ };
4319
4562
  const notifyTurnStart = () => {
4320
4563
  runtime.send({ type: "turn-start" });
4321
4564
  };
@@ -4338,23 +4581,22 @@ function createChatQueueTuiBinding(runTurn) {
4338
4581
  while (true) {
4339
4582
  const status = runtime.getState();
4340
4583
  if (!status.running && !status.drainPaused && status.queue.length > 0 && lastOk) {
4341
- const text = status.queue[0];
4584
+ const req = status.queue[0];
4342
4585
  runtime.send({ type: "turn-start" });
4343
4586
  runtime.send({ type: "dequeue" });
4344
4587
  let turnOutcome;
4345
4588
  try {
4346
- turnOutcome = await runTurn(text);
4589
+ turnOutcome = await runTurn(req);
4347
4590
  } catch {
4348
4591
  turnOutcome = { ok: false, aborted: false };
4349
4592
  }
4350
- const { outcome: cascadeOutcome, mode: cascadeMode } = consumePreempt(turnOutcome);
4351
- runtime.send({ type: "turn-end", ...cascadeOutcome });
4352
- if (cascadeMode === "stop") {
4593
+ const consumed = consumePreempt(turnOutcome);
4594
+ const effectiveTurnOutcome = consumed.outcome;
4595
+ runtime.send({ type: "turn-end", ...effectiveTurnOutcome });
4596
+ if (consumed.mode === "stop") {
4353
4597
  runtime.send({ type: "drain-error", message: "" });
4354
4598
  }
4355
- lastOk = cascadeOutcome.ok && !cascadeOutcome.aborted;
4356
- if (cascadeOutcome.aborted) break;
4357
- if (!lastOk) break;
4599
+ lastOk = effectiveTurnOutcome.ok && !effectiveTurnOutcome.aborted;
4358
4600
  } else {
4359
4601
  break;
4360
4602
  }
@@ -4362,11 +4604,11 @@ function createChatQueueTuiBinding(runTurn) {
4362
4604
  };
4363
4605
  const drainHeadForManualTurn = () => {
4364
4606
  const status = runtime.getState();
4365
- if (status.running || status.drainPaused || status.queue.length === 0) return null;
4366
- const text = status.queue[0];
4607
+ if (status.running || status.queue.length === 0) return null;
4608
+ const head = status.queue[0];
4367
4609
  runtime.send({ type: "turn-start" });
4368
4610
  runtime.send({ type: "dequeue" });
4369
- return text;
4611
+ return head;
4370
4612
  };
4371
4613
  const preemptForDrain = () => {
4372
4614
  const status = runtime.getState();
@@ -4383,7 +4625,7 @@ function createChatQueueTuiBinding(runTurn) {
4383
4625
  const snapshotAndClearQueue = () => {
4384
4626
  const status = runtime.getState();
4385
4627
  if (status.queue.length === 0) return null;
4386
- const merged = status.queue.join("\n");
4628
+ const merged = status.queue.map((req) => req.text).join("\n");
4387
4629
  runtime.send({ type: "clear" });
4388
4630
  return merged;
4389
4631
  };
@@ -4398,6 +4640,7 @@ function createChatQueueTuiBinding(runTurn) {
4398
4640
  return {
4399
4641
  resolveSubmit,
4400
4642
  enqueue,
4643
+ enqueueAndMaybeRun,
4401
4644
  notifyTurnStart,
4402
4645
  notifyTurnEnd,
4403
4646
  drainHeadForManualTurn,
@@ -4411,9 +4654,22 @@ function createChatQueueTuiBinding(runTurn) {
4411
4654
  };
4412
4655
  }
4413
4656
 
4657
+ // packages/cli/tui/terminalNotification.ts
4658
+ function shouldEmitTerminalBell(decision) {
4659
+ return !decision.wasAborted && !decision.streamInterrupted && decision.exitCode === 0 && decision.interactive;
4660
+ }
4661
+ function emitTerminalBell(output) {
4662
+ try {
4663
+ output.write("\x07");
4664
+ } catch {
4665
+ }
4666
+ }
4667
+
4414
4668
  // packages/cli/tui/tuiHistory.ts
4415
4669
  var MAX_TUI_HISTORY_TURNS = 500;
4416
4670
  var turnLineCache = /* @__PURE__ */ new WeakMap();
4671
+ var turnLineCountCache = /* @__PURE__ */ new WeakMap();
4672
+ var renderCacheMissCount = 0;
4417
4673
  function createTurnHistory() {
4418
4674
  return {
4419
4675
  turns: [],
@@ -4432,6 +4688,7 @@ function startTurn(history, role) {
4432
4688
  }
4433
4689
  history.currentRole = role;
4434
4690
  history.currentContent = "";
4691
+ resetStreamingTurnCache();
4435
4692
  }
4436
4693
  function appendToCurrentTurn(history, chunk) {
4437
4694
  history.currentContent += chunk;
@@ -4444,6 +4701,7 @@ function finalizeCurrentTurn(history) {
4444
4701
  });
4445
4702
  history.currentRole = null;
4446
4703
  history.currentContent = "";
4704
+ resetStreamingTurnCache();
4447
4705
  if (history.turns.length > MAX_TUI_HISTORY_TURNS) {
4448
4706
  history.turns = history.turns.slice(history.turns.length - MAX_TUI_HISTORY_TURNS);
4449
4707
  }
@@ -4467,23 +4725,33 @@ function styleAssistantTurn(content, colorEnabled) {
4467
4725
  });
4468
4726
  return styledLines.join("\n");
4469
4727
  }
4728
+ function fillUserBubbleRow(row, surfaceSeq, contentWidth) {
4729
+ const visible = visibleWidth(row);
4730
+ if (visible > contentWidth) {
4731
+ const truncated = truncateAnsi(row, contentWidth);
4732
+ const padAfter = Math.max(0, contentWidth - visibleWidth(truncated));
4733
+ return `${surfaceSeq}${truncated}${" ".repeat(padAfter)}\x1B[0m`;
4734
+ }
4735
+ return renderSurfaceLine({ text: row, surface: surfaceSeq, padTo: contentWidth });
4736
+ }
4470
4737
  function renderTurnBlock(role, content, contentWidth, colorEnabled) {
4738
+ content = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
4471
4739
  const lines = [];
4472
4740
  if (role === "user") {
4473
4741
  const logicalLines = content.split("\n");
4474
4742
  const accentSeq = colorEnabled ? themeColorSequence("accent") : "";
4475
- const chromeSeq = colorEnabled ? themeColorSequence("chrome") : "";
4743
+ const firstPrefix = colorEnabled ? `${accentSeq}\x1B[1m\u2503 ` : "\u2503 ";
4744
+ const multilinePrefix = colorEnabled ? `${accentSeq}\x1B[1m\u2503 ` : "\u2503 ";
4745
+ const hangingIndent = colorEnabled ? `${accentSeq}\x1B[1m\u2503 ` : "\u2503 ";
4746
+ const surfaceSeq = colorEnabled ? userSurfaceBackgroundSequence() : "";
4476
4747
  for (let i = 0; i < logicalLines.length; i++) {
4477
4748
  const line = logicalLines[i];
4478
- let styledLine;
4479
- if (colorEnabled) {
4480
- const prefix = i === 0 ? `${accentSeq}\u276F ` : `${chromeSeq}\u2502 `;
4481
- styledLine = `${prefix}\x1B[1m${accentSeq}${line}`;
4482
- } else {
4483
- const prefix = i === 0 ? "\u276F " : " ";
4484
- styledLine = `${prefix}${line}`;
4485
- }
4486
- lines.push(...wrapTranscriptLine(styledLine, contentWidth, " "));
4749
+ const prefix = i === 0 ? firstPrefix : multilinePrefix;
4750
+ const styledLine = `${prefix}${line}`;
4751
+ const rows = wrapTranscriptLine(styledLine, contentWidth, hangingIndent);
4752
+ lines.push(
4753
+ ...surfaceSeq ? rows.map((row) => fillUserBubbleRow(row, surfaceSeq, contentWidth)) : rows
4754
+ );
4487
4755
  }
4488
4756
  } else {
4489
4757
  const styledEntry = styleAssistantTurn(content, colorEnabled);
@@ -4493,57 +4761,184 @@ function renderTurnBlock(role, content, contentWidth, colorEnabled) {
4493
4761
  }
4494
4762
  return lines;
4495
4763
  }
4496
- function buildCopyViewLines(history) {
4497
- const turns = [...history.turns];
4498
- if (history.currentRole !== null) {
4499
- turns.push({ role: history.currentRole, content: history.currentContent });
4764
+ var streamingTurnCache = null;
4765
+ function resetStreamingTurnCache() {
4766
+ streamingTurnCache = null;
4767
+ }
4768
+ function renderPrefixTurnBlock(role, content, contentWidth, colorEnabled) {
4769
+ content = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
4770
+ if (role === "user") {
4771
+ return renderTurnBlock(role, content, contentWidth, colorEnabled);
4500
4772
  }
4773
+ const highlighted = colorEnabled ? formatAssistantDisplay(content, { trimEdges: false }) : stripAnsi(formatAssistantDisplay(content, { trimEdges: false }));
4774
+ const rawLines = highlighted.split("\n");
4775
+ const styledLines = rawLines.map((line, idx) => {
4776
+ if (idx === 0 && !line.startsWith("[nolo]")) {
4777
+ const anchorPrefix = colorEnabled ? `${themeColorSequence("chrome")}\u25C8\x1B[39m ` : "\u25C8 ";
4778
+ return `${anchorPrefix}${line}`;
4779
+ }
4780
+ return line.startsWith("[nolo]") && colorEnabled ? themeText(line, "chrome", true) : line;
4781
+ });
4501
4782
  const lines = [];
4502
- for (const [index, turn] of turns.entries()) {
4503
- if (index > 0) lines.push("");
4504
- const normalized = stripAnsi(turn.content).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
4505
- lines.push(...normalized.split("\n"));
4783
+ for (const logicalLine of styledLines) {
4784
+ lines.push(...wrapTranscriptLine(logicalLine, contentWidth));
4506
4785
  }
4507
4786
  return lines;
4508
4787
  }
4509
- function buildHistoryLines(history, contentWidth) {
4510
- const colorEnabled = resolveCliColorEnabled();
4788
+ function renderTailTurnBlock(role, content, contentWidth, colorEnabled) {
4789
+ content = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
4790
+ const lines = [];
4791
+ if (role === "user") {
4792
+ const surfaceSeq = colorEnabled ? userSurfaceBackgroundSequence() : "";
4793
+ const accentSeq = colorEnabled ? themeColorSequence("accent") : "";
4794
+ const multilinePrefix = colorEnabled ? `${accentSeq}\x1B[1m\u2503 ` : "\u2503 ";
4795
+ const hangingIndent = colorEnabled ? `${accentSeq}\x1B[1m\u2503 ` : "\u2503 ";
4796
+ for (const rawLine of content.split("\n")) {
4797
+ const styledLine = `${multilinePrefix}${rawLine}`;
4798
+ const rows = wrapTranscriptLine(styledLine, contentWidth, hangingIndent);
4799
+ lines.push(
4800
+ ...surfaceSeq ? rows.map((row) => fillUserBubbleRow(row, surfaceSeq, contentWidth)) : rows
4801
+ );
4802
+ }
4803
+ } else {
4804
+ const highlighted = colorEnabled ? formatAssistantDisplay(content) : stripAnsi(formatAssistantDisplay(content));
4805
+ const rawLines = highlighted.split("\n");
4806
+ const styledLines = rawLines.map((line) => {
4807
+ return line.startsWith("[nolo]") && colorEnabled ? themeText(line, "chrome", true) : line;
4808
+ });
4809
+ for (const logicalLine of styledLines) {
4810
+ lines.push(...wrapTranscriptLine(logicalLine, contentWidth));
4811
+ }
4812
+ }
4813
+ return lines;
4814
+ }
4815
+ function getStreamingTurnLines(role, content, contentWidth, colorEnabled, density, surface) {
4816
+ if (streamingTurnCache && (streamingTurnCache.role !== role || streamingTurnCache.contentWidth !== contentWidth || streamingTurnCache.colorEnabled !== colorEnabled || streamingTurnCache.density !== density || streamingTurnCache.surface !== surface || !content.startsWith(streamingTurnCache.fullContent.slice(0, streamingTurnCache.prefixLength)))) {
4817
+ streamingTurnCache = null;
4818
+ }
4819
+ if (!streamingTurnCache) {
4820
+ streamingTurnCache = {
4821
+ role,
4822
+ contentWidth,
4823
+ colorEnabled,
4824
+ density,
4825
+ surface,
4826
+ fullContent: content,
4827
+ prefixLength: 0,
4828
+ prefixLines: []
4829
+ };
4830
+ }
4831
+ if (content.length > streamingTurnCache.fullContent.length) {
4832
+ let searchPos = streamingTurnCache.prefixLength;
4833
+ let candidateCut = -1;
4834
+ while (true) {
4835
+ const idx = content.indexOf("\n\n", searchPos);
4836
+ if (idx === -1) break;
4837
+ const cut = idx + 1;
4838
+ searchPos = idx + 2;
4839
+ const prefixSub = content.slice(0, cut);
4840
+ const fenceCount = (prefixSub.match(/^```/gm) || []).length;
4841
+ if (fenceCount % 2 === 0) {
4842
+ candidateCut = cut;
4843
+ break;
4844
+ }
4845
+ }
4846
+ if (candidateCut > streamingTurnCache.prefixLength) {
4847
+ const candidatePrefix = content.slice(0, candidateCut);
4848
+ const candidateTail = content.slice(candidateCut);
4849
+ const candPrefixLines = renderPrefixTurnBlock(role, candidatePrefix, contentWidth, colorEnabled);
4850
+ const candTailLines = renderTailTurnBlock(role, candidateTail, contentWidth, colorEnabled);
4851
+ const candCombined = [...candPrefixLines, ...candTailLines];
4852
+ const fullCheckLines = renderTurnBlock(role, content, contentWidth, colorEnabled);
4853
+ if (JSON.stringify(candCombined) === JSON.stringify(fullCheckLines)) {
4854
+ streamingTurnCache.prefixLength = candidateCut;
4855
+ streamingTurnCache.prefixLines = candPrefixLines;
4856
+ streamingTurnCache.fullContent = content;
4857
+ } else {
4858
+ streamingTurnCache.fullContent = content;
4859
+ }
4860
+ } else {
4861
+ streamingTurnCache.fullContent = content;
4862
+ }
4863
+ }
4864
+ if (streamingTurnCache.prefixLength === 0) {
4865
+ return renderTurnBlock(role, content, contentWidth, colorEnabled);
4866
+ }
4867
+ const tailContent = content.slice(streamingTurnCache.prefixLength);
4868
+ const tailLines = renderTailTurnBlock(role, tailContent, contentWidth, colorEnabled);
4869
+ return [...streamingTurnCache.prefixLines, ...tailLines];
4870
+ }
4871
+ var assistantPlainLinesCache = /* @__PURE__ */ new Map();
4872
+ function getAssistantPlainLines(content) {
4873
+ let plainLines = assistantPlainLinesCache.get(content);
4874
+ if (plainLines !== void 0) {
4875
+ return plainLines;
4876
+ }
4877
+ if (!/[\r\x00#|\-*_~`>+\[\]()•☐☑]|\b\d+\./.test(content)) {
4878
+ plainLines = content.trim().split("\n");
4879
+ } else {
4880
+ plainLines = stripAnsi(formatAssistantDisplay(content)).split("\n");
4881
+ }
4882
+ if (assistantPlainLinesCache.size > 2e3) {
4883
+ const firstKey = assistantPlainLinesCache.keys().next().value;
4884
+ if (firstKey !== void 0) assistantPlainLinesCache.delete(firstKey);
4885
+ }
4886
+ assistantPlainLinesCache.set(content, plainLines);
4887
+ return plainLines;
4888
+ }
4889
+ function countTurnLines(role, content, contentWidth) {
4890
+ content = content.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
4891
+ let count = 0;
4892
+ if (role === "user") {
4893
+ for (const line of content.split("\n")) {
4894
+ count += wrapTranscriptLine(`\u2503 ${line}`, contentWidth, "\u2503 ").length;
4895
+ }
4896
+ } else {
4897
+ const plainLines = getAssistantPlainLines(content);
4898
+ for (let i = 0; i < plainLines.length; i++) {
4899
+ const line = plainLines[i];
4900
+ const wrapped = i === 0 && !line.startsWith("[nolo]") ? wrapTranscriptLine(`\u25C8 ${line}`, contentWidth) : wrapTranscriptLine(line, contentWidth);
4901
+ count += wrapped.length;
4902
+ }
4903
+ }
4904
+ return count;
4905
+ }
4906
+ function buildTurnOffsets(history, contentWidth) {
4511
4907
  const density = getActiveDensity();
4512
- const wrapped = [];
4908
+ const entries = [];
4909
+ let offset = 0;
4513
4910
  for (let i = 0; i < history.turns.length; i++) {
4514
4911
  const turn = history.turns[i];
4515
- if (density === "spacious" && (i > 0 || turn.role === "user")) {
4516
- wrapped.push("");
4517
- }
4518
- const cached = turnLineCache.get(turn);
4519
- if (cached && cached.width === contentWidth && cached.color === colorEnabled && cached.density === density) {
4520
- wrapped.push(...cached.lines);
4912
+ const separatorAbove = density === "spacious" && (i > 0 || turn.role === "user") ? 1 : 0;
4913
+ offset += separatorAbove;
4914
+ const cached = turnLineCountCache.get(turn);
4915
+ let lineCount;
4916
+ if (cached && cached.width === contentWidth) {
4917
+ lineCount = cached.count;
4521
4918
  } else {
4522
- const lines = renderTurnBlock(turn.role, turn.content, contentWidth, colorEnabled);
4523
- turnLineCache.set(turn, {
4524
- width: contentWidth,
4525
- color: colorEnabled,
4526
- density,
4527
- lines
4528
- });
4529
- wrapped.push(...lines);
4919
+ lineCount = countTurnLines(turn.role, turn.content, contentWidth);
4920
+ turnLineCountCache.set(turn, { width: contentWidth, count: lineCount });
4530
4921
  }
4922
+ entries.push({ startRow: offset, lineCount, separatorAbove });
4923
+ offset += lineCount;
4531
4924
  }
4532
- if (history.currentRole !== null && history.currentContent) {
4533
- const i = history.turns.length;
4534
- if (density === "spacious" && (i > 0 || history.currentRole === "user")) {
4535
- wrapped.push("");
4536
- }
4537
- wrapped.push(
4538
- ...renderTurnBlock(
4539
- history.currentRole,
4540
- history.currentContent,
4541
- contentWidth,
4542
- colorEnabled
4543
- )
4544
- );
4925
+ return { entries, totalLines: offset };
4926
+ }
4927
+ function currentTurnSeparator(history, density) {
4928
+ return density === "spacious" && (history.turns.length > 0 || history.currentRole === "user") ? 1 : 0;
4929
+ }
4930
+ function buildCopyViewLines(history) {
4931
+ const turns = [...history.turns];
4932
+ if (history.currentRole !== null) {
4933
+ turns.push({ role: history.currentRole, content: history.currentContent });
4545
4934
  }
4546
- return wrapped;
4935
+ const lines = [];
4936
+ for (const [index, turn] of turns.entries()) {
4937
+ if (index > 0) lines.push("");
4938
+ const normalized = stripAnsi(turn.content).replace(/\r\n/g, "\n").replace(/\r/g, "\n");
4939
+ lines.push(...normalized.split("\n"));
4940
+ }
4941
+ return lines;
4547
4942
  }
4548
4943
  function renderHistory(output, history, inputLines) {
4549
4944
  const tty = output;
@@ -4552,8 +4947,28 @@ function renderHistory(output, history, inputLines) {
4552
4947
  const columns = tty.columns ?? 80;
4553
4948
  const visibleHeight = Math.max(1, rows - inputLines);
4554
4949
  const contentWidth = Math.max(1, columns - 1);
4555
- const lines = buildHistoryLines(history, contentWidth);
4556
- const totalLines = lines.length;
4950
+ const colorEnabled = resolveCliColorEnabled();
4951
+ const density = getActiveDensity();
4952
+ const surface = colorEnabled ? userSurfaceBackgroundSequence() : "";
4953
+ const { entries, totalLines: finalizedLines } = buildTurnOffsets(
4954
+ history,
4955
+ contentWidth
4956
+ );
4957
+ let totalLines = finalizedLines;
4958
+ let currentStart = -1;
4959
+ let currentLines = [];
4960
+ if (history.currentRole !== null && history.currentContent) {
4961
+ currentLines = getStreamingTurnLines(
4962
+ history.currentRole,
4963
+ history.currentContent,
4964
+ contentWidth,
4965
+ colorEnabled,
4966
+ density,
4967
+ surface
4968
+ );
4969
+ currentStart = finalizedLines + currentTurnSeparator(history, density);
4970
+ totalLines = currentStart + currentLines.length;
4971
+ }
4557
4972
  if (history.followBottom) {
4558
4973
  history.scrollTop = Math.max(0, totalLines - visibleHeight);
4559
4974
  } else {
@@ -4564,19 +4979,71 @@ function renderHistory(output, history, inputLines) {
4564
4979
  }
4565
4980
  history.hasMoreAbove = history.scrollTop > 0;
4566
4981
  history.hasMoreBelow = history.scrollTop + visibleHeight < totalLines;
4567
- const visibleStart = history.scrollTop;
4568
- const visibleEnd = Math.min(totalLines, visibleStart + visibleHeight);
4569
- const visibleLines = lines.slice(visibleStart, visibleEnd);
4982
+ const winStart = history.scrollTop;
4983
+ const winEnd = Math.min(totalLines, winStart + visibleHeight);
4984
+ const visibleLines = new Array(visibleHeight).fill("");
4985
+ for (let i = 0; i < entries.length; i++) {
4986
+ const entry = entries[i];
4987
+ if (entry.separatorAbove > 0) {
4988
+ const sepRow = entry.startRow - 1;
4989
+ if (sepRow >= winStart && sepRow < winEnd) {
4990
+ visibleLines[sepRow - winStart] = "";
4991
+ }
4992
+ }
4993
+ const turnStart = entry.startRow;
4994
+ const turnEnd = turnStart + entry.lineCount;
4995
+ if (turnEnd <= winStart || turnStart >= winEnd) continue;
4996
+ const turn = history.turns[i];
4997
+ const cached = turnLineCache.get(turn);
4998
+ let lines;
4999
+ if (cached && cached.width === contentWidth && cached.color === colorEnabled && cached.density === density && cached.surface === surface) {
5000
+ lines = cached.lines;
5001
+ } else {
5002
+ renderCacheMissCount += 1;
5003
+ lines = renderTurnBlock(turn.role, turn.content, contentWidth, colorEnabled);
5004
+ turnLineCache.set(turn, {
5005
+ width: contentWidth,
5006
+ color: colorEnabled,
5007
+ density,
5008
+ surface,
5009
+ lines
5010
+ });
5011
+ }
5012
+ const interStart = Math.max(turnStart, winStart);
5013
+ const interEnd = Math.min(turnEnd, winEnd);
5014
+ for (let r = interStart; r < interEnd; r++) {
5015
+ visibleLines[r - winStart] = lines[r - turnStart] ?? "";
5016
+ }
5017
+ }
5018
+ if (currentStart >= 0) {
5019
+ const separatorAbove = currentTurnSeparator(history, density);
5020
+ if (separatorAbove > 0) {
5021
+ const sepRow = currentStart - 1;
5022
+ if (sepRow >= winStart && sepRow < winEnd) {
5023
+ visibleLines[sepRow - winStart] = "";
5024
+ }
5025
+ }
5026
+ const currentEnd = currentStart + currentLines.length;
5027
+ if (currentEnd > winStart && currentStart < winEnd) {
5028
+ const interStart = Math.max(currentStart, winStart);
5029
+ const interEnd = Math.min(currentEnd, winEnd);
5030
+ for (let r = interStart; r < interEnd; r++) {
5031
+ visibleLines[r - winStart] = currentLines[r - currentStart] ?? "";
5032
+ }
5033
+ }
5034
+ }
4570
5035
  let frame = "";
4571
5036
  for (let i = 0; i < visibleHeight; i++) {
4572
5037
  const line = visibleLines[i] ?? "";
4573
5038
  const padded = padOrTruncateToWidth(line, contentWidth);
4574
5039
  const thumb = renderScrollbarRow(i, visibleHeight, totalLines, history.scrollTop);
5040
+ const scrollbarPrefix = colorEnabled ? themeColorSequence("chrome") : "";
5041
+ const scrollbarSuffix = colorEnabled ? "\x1B[39m" : "";
4575
5042
  frame += `\x1B[${i + 1};1H`;
4576
5043
  frame += "\x1B[2K";
4577
5044
  frame += padded;
4578
5045
  frame += `\x1B[${columns}G`;
4579
- frame += thumb;
5046
+ frame += `${scrollbarPrefix}${thumb}${scrollbarSuffix}`;
4580
5047
  }
4581
5048
  const mainBottom = Math.max(1, rows - inputLines);
4582
5049
  frame += `\x1B[${mainBottom};1H`;
@@ -4603,7 +5070,11 @@ function applyScrollAction(history, action, output, inputLines) {
4603
5070
  const columns = tty.columns ?? 80;
4604
5071
  const visibleHeight = Math.max(1, rows - inputLines);
4605
5072
  const contentWidth = Math.max(1, columns - 1);
4606
- const totalLines = buildHistoryLines(history, contentWidth).length;
5073
+ const { totalLines: finalizedLines } = buildTurnOffsets(history, contentWidth);
5074
+ let totalLines = finalizedLines;
5075
+ if (history.currentRole !== null && history.currentContent) {
5076
+ totalLines += currentTurnSeparator(history, getActiveDensity()) + countTurnLines(history.currentRole, history.currentContent, contentWidth);
5077
+ }
4607
5078
  const maxScrollTop = Math.max(0, totalLines - visibleHeight);
4608
5079
  history.followBottom = false;
4609
5080
  switch (action) {
@@ -5211,70 +5682,21 @@ ${content}`;
5211
5682
  return null;
5212
5683
  }
5213
5684
  var autoRouteByDialog = /* @__PURE__ */ new Map();
5214
- function toOverlayStatus(status) {
5215
- switch (status) {
5216
- case "running":
5217
- return "running";
5218
- case "done":
5219
- return "done";
5220
- case "failed":
5221
- case "timeout":
5222
- case "killed":
5223
- return "failed";
5224
- case "orphaned":
5225
- return "orphaned";
5226
- default:
5227
- return "running";
5228
- }
5229
- }
5230
- function buildTuiRunOverlay(parentDialogId, reported) {
5231
- if (!parentDialogId) return null;
5232
- let runs;
5233
- try {
5234
- runs = queryRunRecords({ parentDialogId }, { env: process.env }).runs;
5235
- } catch {
5236
- return null;
5237
- }
5238
- if (runs.length === 0) return null;
5239
- if (reported) {
5240
- runs = runs.filter((run) => reported.get(run.runId) !== run.status);
5241
- for (const run of runs) reported.set(run.runId, run.status);
5242
- if (runs.length === 0) return null;
5243
- }
5244
- let overlay = initialRunOverlayState;
5245
- for (const run of runs) {
5246
- overlay = reduceRunOverlay(overlay, {
5247
- type: "run-state-chg",
5248
- runId: run.runId,
5249
- info: {
5250
- name: run.agentName ?? run.agentKey ?? run.runId,
5251
- status: toOverlayStatus(run.status),
5252
- batchId: run.batchId,
5253
- parentDialogId: run.parentDialogId,
5254
- ...run.status === "failed" && run.note ? { errorMessage: run.note } : {}
5255
- }
5256
- });
5257
- }
5258
- return buildOverlayPresentation(overlay);
5259
- }
5260
5685
  async function runAgentChat(scriptDir, state, message, env, output, agentRunner = runAgentTurn, options = {}) {
5261
5686
  let effectiveAgentKey = state.agentKey;
5262
5687
  let effectiveAgentName = state.agentName;
5263
5688
  const continueId = state.dialogId;
5264
5689
  const cachedRoute = continueId ? autoRouteByDialog.get(continueId) : void 0;
5690
+ const hasImages = options.imageUrls && options.imageUrls.length > 0;
5265
5691
  if (cachedRoute && !options.explicitAgentSwitch) {
5266
5692
  effectiveAgentKey = cachedRoute.agentKey;
5267
5693
  effectiveAgentName = cachedRoute.agentName;
5268
5694
  } else if (!continueId && env.NOLO_AUTO_ROUTE !== "0" && state.agentKey === DEFAULT_TUI_AGENT_KEY) {
5269
- const authToken = resolvePlatformAuthToken(env);
5270
- const route = authToken ? await classifyCliAutoRoute(message, {
5695
+ const route = classifyCliAutoRoute(message, {
5271
5696
  serverUrl: state.serverUrl,
5272
- authToken
5273
- }) : {
5274
- agentKey: CLI_AUTO_TIER_AGENT_KEYS[resolveCliAutoFallbackTier(message)],
5275
- tier: resolveCliAutoFallbackTier(message),
5276
- classified: false
5277
- };
5697
+ authToken: resolvePlatformAuthToken(env),
5698
+ hasImages
5699
+ });
5278
5700
  effectiveAgentKey = route.agentKey;
5279
5701
  effectiveAgentName = `auto\u2192${route.tier}`;
5280
5702
  if (effectiveAgentKey !== state.agentKey) {
@@ -5285,7 +5707,6 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
5285
5707
  );
5286
5708
  }
5287
5709
  }
5288
- const hasImages = options.imageUrls && options.imageUrls.length > 0;
5289
5710
  const userSelectedAgent = state.agentKey !== DEFAULT_TUI_AGENT_KEY;
5290
5711
  const visionProbeKey = hasImages && userSelectedAgent ? state.agentKey : effectiveAgentKey;
5291
5712
  if (hasImages && effectiveAgentKey !== CLI_IMAGE_AGENT_KEY) {
@@ -5398,18 +5819,22 @@ async function runAgentChat(scriptDir, state, message, env, output, agentRunner
5398
5819
  // inside a TUI turn are stamped with the current conversation. Previously
5399
5820
  // this factory only existed when pasted text was present, which meant a
5400
5821
  // plain (no-paste) turn had no injection point for parentDialogId and the
5401
- // run-overlay could never see runs spawned this turn.
5822
+ // run-completion watcher could never attribute runs spawned this turn.
5402
5823
  ...options.pastedTextStore?.items.size ? { pastedTextStore: options.pastedTextStore } : {},
5403
5824
  localRuntimeAdapterFactory: (factoryEnv, factoryOptions) => createCliLocalRuntimeAdapter({
5404
5825
  env: factoryEnv,
5405
5826
  cwd: factoryOptions?.cwd ?? state.cwd,
5406
5827
  ...options.pastedTextStore ? { pastedTextStore: options.pastedTextStore } : {},
5407
5828
  // Stamp spawned background runs with the current TUI dialog so the
5408
- // run-overlay can filter runs belonging to this conversation.
5829
+ // run-completion watcher can attribute terminal-state wakes to this
5830
+ // conversation.
5409
5831
  ...state.dialogId ? { parentDialogId: state.dialogId } : {},
5410
5832
  ...options.activityReporter ? { activityReporter: options.activityReporter } : {}
5411
5833
  }),
5412
5834
  ...options.activityReporter ? { activityReporter: options.activityReporter } : {},
5835
+ // 转发 dock 订阅:runAgentTurn 的输出层靠它判断「有面板」从而抑制
5836
+ // transcript 的进展卡片;dock 本身也靠它接收模型轮询带回来的快照。
5837
+ ...options.onAgentRunStatus ? { onAgentRunStatus: options.onAgentRunStatus } : {},
5413
5838
  ...skillAllowedTools !== void 0 ? { allowedToolNames: skillAllowedTools } : {},
5414
5839
  ...contextBlockScopes.length > 0 ? { contextBlockScopes } : {}
5415
5840
  });
@@ -5514,17 +5939,23 @@ function waitForActionGate(rl, input, output, gate, spawnRunner) {
5514
5939
  });
5515
5940
  });
5516
5941
  }
5517
- function persistAgentSelection(state, env) {
5942
+ function persistAgentSelection(state, env, saveSelection = saveProfileAgentSelection) {
5943
+ const isAuto = state.agentKey === DEFAULT_TUI_AGENT_KEY && state.agentName.trim().toLowerCase() === "auto";
5518
5944
  try {
5519
- saveProfileAgentSelection({
5520
- agentKey: state.agentKey,
5521
- agentName: state.agentName
5945
+ saveSelection({
5946
+ agentKey: isAuto ? "" : state.agentKey,
5947
+ agentName: isAuto ? "" : state.agentName
5522
5948
  });
5523
5949
  } catch {
5524
5950
  }
5525
5951
  if (env) {
5526
- env.NOLO_AGENT = state.agentKey;
5527
- env.NOLO_AGENT_NAME = state.agentName;
5952
+ if (isAuto) {
5953
+ delete env.NOLO_AGENT;
5954
+ delete env.NOLO_AGENT_NAME;
5955
+ } else {
5956
+ env.NOLO_AGENT = state.agentKey;
5957
+ env.NOLO_AGENT_NAME = state.agentName;
5958
+ }
5528
5959
  }
5529
5960
  }
5530
5961
  function isInteractiveInput(input) {
@@ -5658,7 +6089,9 @@ async function startTuiWorkspace(options) {
5658
6089
  setActiveTerminalBaseHex(detected.hex);
5659
6090
  }
5660
6091
  const bannerColumns = output.columns;
5661
- output.write(renderWelcome(state, 0, 0, bannerColumns));
6092
+ const initialWelcome = renderWelcome(state, 0, 0, bannerColumns);
6093
+ output.write(initialWelcome);
6094
+ const initialBannerLineCount = initialWelcome.split("\n").length;
5662
6095
  let lastSentTitle = null;
5663
6096
  const syncWindowTitle = () => {
5664
6097
  if (!output.isTTY) return;
@@ -5696,12 +6129,20 @@ async function startTuiWorkspace(options) {
5696
6129
  }
5697
6130
  });
5698
6131
  const activityReporter = (label) => activityIndicator.report(label);
5699
- const reportedRunStatuses = /* @__PURE__ */ new Map();
6132
+ let runWakeHandler = null;
6133
+ const runCompletionWatcher = createRunCompletionWatcher({
6134
+ getCurrentDialogId: () => state.dialogId ?? null,
6135
+ onWake: (text) => runWakeHandler?.(text)
6136
+ });
5700
6137
  const runRegistryPoller = createRunRegistryPoller({
5701
6138
  getDockedRuns: () => activityIndicator.getAgentRuns(),
5702
6139
  update: (snapshot) => activityIndicator.updateAgentRun(snapshot),
5703
- readRecord: (runId) => readRunRecord(runId),
5704
- reconcile: (runId) => checkStaleRun(runId)
6140
+ // 显式传 env:resolveNoloHome 只认传入 env 的 NOLO_HOME,不传则永远读
6141
+ // ~/.nolo——设了 NOLO_HOME 的环境(dev、测试)会读错目录。run 记录读取
6142
+ // 与 reconcile 的调用点也是同样写法。
6143
+ readRecord: (runId) => readRunRecord(runId, { env: process.env }),
6144
+ reconcile: (runId) => checkStaleRun(runId, { env: process.env }),
6145
+ onRecordsPolled: (records) => runCompletionWatcher.observe(records)
5705
6146
  });
5706
6147
  let explicitAgentSwitch = false;
5707
6148
  let copyViewExitResolver = null;
@@ -5762,6 +6203,41 @@ async function startTuiWorkspace(options) {
5762
6203
  scheduleRender();
5763
6204
  });
5764
6205
  };
6206
+ const repaintBanner = () => {
6207
+ if (!output.isTTY) return;
6208
+ if (fixedInput.isPaused()) return;
6209
+ if (modalOwnsKeyboard) return;
6210
+ const currentColumns = output.columns ?? 80;
6211
+ const welcome = renderWelcome(state, 0, 0, currentColumns);
6212
+ const lines = welcome.split("\n");
6213
+ const safeWidth = Math.max(1, currentColumns);
6214
+ const safeLines = lines.map((line) => padOrTruncateToWidth(line, safeWidth));
6215
+ const clearLines = Math.max(initialBannerLineCount, safeLines.length);
6216
+ output.write("\x1B[?2026h\x1B[?25l");
6217
+ try {
6218
+ let frame = "";
6219
+ for (let i = 0; i < clearLines; i++) frame += `\x1B[${i + 1};1H\x1B[2K`;
6220
+ safeLines.forEach((line, i) => {
6221
+ frame += `\x1B[${i + 1};1H${line}`;
6222
+ });
6223
+ output.write(frame);
6224
+ if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
6225
+ } finally {
6226
+ output.write("\x1B[?25h\x1B[?2026l");
6227
+ }
6228
+ };
6229
+ void checkForCliUpdate(state.cliVersion, state.serverUrl, {
6230
+ fetchImpl,
6231
+ env: options.env ?? process.env
6232
+ }).then((updateAvailable) => {
6233
+ if (sessionEnded || !updateAvailable) return;
6234
+ state = { ...state, updateAvailable };
6235
+ if (history.turns.length === 0 && history.currentRole === null) {
6236
+ repaintBanner();
6237
+ } else {
6238
+ scheduleRender();
6239
+ }
6240
+ });
5765
6241
  let syncingLayout = false;
5766
6242
  const renderHistoryToOutput = () => {
5767
6243
  if (fixedInput.isPaused()) return;
@@ -5864,11 +6340,17 @@ async function startTuiWorkspace(options) {
5864
6340
  void maybeAutoRefreshTheme();
5865
6341
  }, AUTO_THEME_POLL_MS);
5866
6342
  }
5867
- const runOneAgentTurn = async (message, imageUrls, actionGateHandler, confirmDestructiveAction) => {
6343
+ const runOneAgentTurn = async (inputMsg, imageUrls, actionGateHandler, confirmDestructiveAction) => {
6344
+ const req = createTurnRequest(inputMsg);
6345
+ if (req.event.kind === "child-run-completed") {
6346
+ for (const r of req.event.runs) {
6347
+ runCompletionWatcher.markAcknowledged(r.runId);
6348
+ }
6349
+ }
6350
+ const message = req.text;
5868
6351
  forcedStop = false;
5869
6352
  turnEpoch += 1;
5870
6353
  const myEpoch = turnEpoch;
5871
- const turnParentDialogId = state.dialogId;
5872
6354
  history.followBottom = true;
5873
6355
  startTurn(history, "user");
5874
6356
  appendToCurrentTurn(history, message);
@@ -5879,12 +6361,12 @@ async function startTuiWorkspace(options) {
5879
6361
  const agentOutput = isInteractiveInput(input) ? createHistoryOutputStream(history, () => {
5880
6362
  scheduleRender();
5881
6363
  }) : output;
5882
- const requestUserChoice = isInteractiveInput(input) && dialogHost ? async (req) => {
6364
+ const requestUserChoice = isInteractiveInput(input) && dialogHost ? async (req2) => {
5883
6365
  modalOwnsKeyboard = true;
5884
6366
  try {
5885
6367
  return await dialogHost.run(
5886
6368
  (anchor) => runAskChoiceDialog({
5887
- request: req,
6369
+ request: req2,
5888
6370
  input,
5889
6371
  output,
5890
6372
  ...anchor
@@ -5934,7 +6416,8 @@ async function startTuiWorkspace(options) {
5934
6416
  ...runResult.dialogId ? {
5935
6417
  dialogId: runResult.dialogId,
5936
6418
  dialogKey: nextDialogKey,
5937
- dialogLabel: runResult.title || runResult.dialogId
6419
+ dialogLabel: runResult.title || runResult.dialogId,
6420
+ ...runResult.title ? { dialogTitle: runResult.title } : {}
5938
6421
  } : {},
5939
6422
  ...runResult.turnTokens ? { turnTokens: runResult.turnTokens } : {}
5940
6423
  };
@@ -5943,6 +6426,14 @@ async function startTuiWorkspace(options) {
5943
6426
  }
5944
6427
  const wasAborted = activeTurnAbort.signal.aborted;
5945
6428
  activeTurnAbort = null;
6429
+ if (shouldEmitTerminalBell({
6430
+ wasAborted,
6431
+ streamInterrupted: runResult.streamInterrupted,
6432
+ exitCode: runResult.exitCode,
6433
+ interactive: isInteractiveInput(input)
6434
+ })) {
6435
+ emitTerminalBell(output);
6436
+ }
5946
6437
  explicitAgentSwitch = false;
5947
6438
  if (isInteractiveInput(input)) {
5948
6439
  finalizeCurrentTurn(history);
@@ -5964,7 +6455,8 @@ async function startTuiWorkspace(options) {
5964
6455
  ...runResult.dialogId ? {
5965
6456
  dialogId: runResult.dialogId,
5966
6457
  dialogKey: nextDialogKey,
5967
- dialogLabel: runResult.title || runResult.dialogId
6458
+ dialogLabel: runResult.title || runResult.dialogId,
6459
+ ...runResult.title ? { dialogTitle: runResult.title } : {}
5968
6460
  } : {},
5969
6461
  ...runResult.turnTokens ? { turnTokens: runResult.turnTokens } : {},
5970
6462
  ...runResult.contextWindow ? { contextWindow: runResult.contextWindow } : {},
@@ -5985,14 +6477,6 @@ async function startTuiWorkspace(options) {
5985
6477
  emitCommandOutput(t("dialogNotSavedHint"));
5986
6478
  }
5987
6479
  }
5988
- if (!wasAborted) {
5989
- const overlayText = buildTuiRunOverlay(turnParentDialogId, reportedRunStatuses);
5990
- if (overlayText) {
5991
- emitCommandOutput(`
5992
- \u4F60\u7684 run \u8FDB\u5C55\uFF1A
5993
- ${overlayText}`);
5994
- }
5995
- }
5996
6480
  return { ok: !wasAborted, aborted: wasAborted };
5997
6481
  } finally {
5998
6482
  activityIndicator.stop();
@@ -6027,6 +6511,17 @@ ${overlayText}`);
6027
6511
  renderHistoryToOutput();
6028
6512
  if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
6029
6513
  };
6514
+ const persistExplicitAgentSwitch = (previousAgentKey) => {
6515
+ if (state.agentKey === previousAgentKey) return false;
6516
+ if (state.dialogId) autoRouteByDialog.delete(state.dialogId);
6517
+ explicitAgentSwitch = true;
6518
+ persistAgentSelection(
6519
+ state,
6520
+ options.env ?? process.env,
6521
+ options.saveAgentSelection
6522
+ );
6523
+ return true;
6524
+ };
6030
6525
  const runSubmittedLine = async (line, actionGateHandler, confirmDestructiveAction) => {
6031
6526
  if (!line.trim()) return false;
6032
6527
  const result = handleTuiInput(line, state);
@@ -6045,9 +6540,7 @@ ${overlayText}`);
6045
6540
  emitCommandOutput(result.output);
6046
6541
  }
6047
6542
  if (state.agentKey !== previousAgentKey) {
6048
- if (state.dialogId) autoRouteByDialog.delete(state.dialogId);
6049
- explicitAgentSwitch = true;
6050
- persistAgentSelection(state, options.env ?? process.env);
6543
+ persistExplicitAgentSwitch(previousAgentKey);
6051
6544
  }
6052
6545
  if (result.action?.type === "exit") return true;
6053
6546
  if (result.action?.type === "clear") {
@@ -6098,6 +6591,7 @@ ${overlayText}`);
6098
6591
  if (result.action?.type === "compact") {
6099
6592
  const runner = options.compactRunner ?? compactDialog;
6100
6593
  const authToken = resolvePlatformAuthToken(options.env ?? {});
6594
+ const compactStart = Date.now();
6101
6595
  try {
6102
6596
  const compactResult = await runner({
6103
6597
  serverUrl: state.serverUrl,
@@ -6105,20 +6599,45 @@ ${overlayText}`);
6105
6599
  dialogId: result.action.dialogId,
6106
6600
  summaryLlmCaller: options.summaryLlmCaller
6107
6601
  });
6602
+ const elapsedSec = ((Date.now() - compactStart) / 1e3).toFixed(1);
6108
6603
  state = {
6109
6604
  ...state,
6110
6605
  dialogId: compactResult.dialogId,
6111
6606
  dialogKey: compactResult.dialogKey,
6112
- dialogLabel: compactResult.dialogId
6607
+ dialogLabel: compactResult.dialogId,
6608
+ dialogTitle: state.dialogTitle,
6609
+ // Compact forks into a fresh dialog that only inherits the summary,
6610
+ // not the full message history. Drop usage-derived context so the
6611
+ // composer chip falls back to the default CLI surface estimate and
6612
+ // the context percentage visibly drops. Mirrors the /clear reset.
6613
+ turnTokens: void 0,
6614
+ estimatedContextTokens: estimateDefaultCliContextTokens({
6615
+ cwd: state.cwd,
6616
+ agentKey: state.agentKey,
6617
+ agentName: state.agentKey === DEFAULT_TUI_AGENT_KEY && (options.env ?? process.env).NOLO_AUTO_ROUTE !== "0" ? "DeepSeek V4 Flash" : state.agentName,
6618
+ model: state.agentKey === DEFAULT_TUI_AGENT_KEY && (options.env ?? process.env).NOLO_AUTO_ROUTE !== "0" ? "deepseek-v4-flash" : void 0
6619
+ })
6113
6620
  };
6114
- output.write(
6115
- `${t(
6116
- compactResult.summaryGenerated ? "compactDone" : "compactDoneNoSummary",
6117
- result.action.dialogId,
6118
- compactResult.dialogId
6119
- )}
6120
- `
6621
+ const elapsed = `${elapsedSec}s`;
6622
+ const message = compactResult.summaryGenerated ? compactResult.compactedMessageCount > 0 ? t(
6623
+ "compactSuccessWithCount",
6624
+ result.action.dialogId,
6625
+ compactResult.dialogId,
6626
+ elapsed,
6627
+ String(compactResult.compactedMessageCount)
6628
+ ) : t(
6629
+ "compactSuccess",
6630
+ result.action.dialogId,
6631
+ compactResult.dialogId,
6632
+ elapsed
6633
+ ) : t(
6634
+ "compactForked",
6635
+ result.action.dialogId,
6636
+ compactResult.dialogId,
6637
+ elapsed
6121
6638
  );
6639
+ output.write(`${message}
6640
+ `);
6122
6641
  } catch (error) {
6123
6642
  output.write(
6124
6643
  `[nolo] Compact failed: ${toErrorMessage(error)}
@@ -6282,6 +6801,7 @@ ${overlayText}`);
6282
6801
  dialogId: pickResult.dialog.id,
6283
6802
  dialogKey: pickResult.dialog.dbKey,
6284
6803
  dialogLabel: pickResult.dialog.title || pickResult.dialog.id,
6804
+ dialogTitle: pickResult.dialog.title,
6285
6805
  turnTokens: void 0
6286
6806
  };
6287
6807
  clearCollapsedPasteStore(pasteStore);
@@ -6418,12 +6938,6 @@ ${err.message}` : ""}`
6418
6938
  resolveCliColorEnabled()
6419
6939
  );
6420
6940
  }
6421
- if (history.hasMoreAbove || history.hasMoreBelow) {
6422
- const scrollHints = [];
6423
- if (history.hasMoreAbove) scrollHints.push("\u25B2 PgUp");
6424
- if (history.hasMoreBelow) scrollHints.push("\u25BC PgDn");
6425
- base += dimCliText(` \xB7 \u2195 ${scrollHints.join("/")}`, resolveCliColorEnabled());
6426
- }
6427
6941
  return base;
6428
6942
  },
6429
6943
  getActivityLines: () => activityIndicator.getActivityLines(resolveCliColorEnabled()),
@@ -6476,6 +6990,7 @@ ${err.message}` : ""}`
6476
6990
  autoThemeTimer = null;
6477
6991
  }
6478
6992
  runRegistryPoller.dispose();
6993
+ runCompletionWatcher.dispose();
6479
6994
  activityIndicator.dispose();
6480
6995
  resolveDone?.();
6481
6996
  clearCollapsedPasteStore(pasteStore);
@@ -6486,6 +7001,83 @@ ${err.message}` : ""}`
6486
7001
  output.write("\x1B[?25h\x1B[?2026l");
6487
7002
  input.setRawMode?.(false);
6488
7003
  };
7004
+ const buildInteractiveTurnHandlers = () => {
7005
+ const actionGateHandler = async (gate) => {
7006
+ modalOwnsKeyboard = true;
7007
+ try {
7008
+ return await waitForRawActionGate(input, output, gate, spawnRunner, {
7009
+ beforeSubprocess: () => fixedInput.pause(),
7010
+ afterSubprocess: () => fixedInput.resumeFromSubprocess(),
7011
+ registerToken: (handler) => {
7012
+ rawActionGateTokenHandler = handler;
7013
+ }
7014
+ });
7015
+ } finally {
7016
+ modalOwnsKeyboard = false;
7017
+ }
7018
+ };
7019
+ const confirmDestructiveAction = async (request) => {
7020
+ modalOwnsKeyboard = true;
7021
+ try {
7022
+ return await dialogHost.run(
7023
+ (anchor) => runConfirmDialog({
7024
+ request,
7025
+ input,
7026
+ output,
7027
+ ...anchor
7028
+ })
7029
+ );
7030
+ } finally {
7031
+ modalOwnsKeyboard = false;
7032
+ }
7033
+ };
7034
+ return { actionGateHandler, confirmDestructiveAction };
7035
+ };
7036
+ const runIdleTextTurn = async (inputMsg) => {
7037
+ const req = createTurnRequest(inputMsg);
7038
+ const { actionGateHandler, confirmDestructiveAction } = buildInteractiveTurnHandlers();
7039
+ const binding = ensureChatQueueBinding(actionGateHandler, confirmDestructiveAction);
7040
+ binding.notifyTurnStart();
7041
+ busy = true;
7042
+ fixedInput.enterOutputMode(req.text);
7043
+ try {
7044
+ const outcome = await runOneAgentTurn(
7045
+ req,
7046
+ [],
7047
+ actionGateHandler,
7048
+ confirmDestructiveAction
7049
+ );
7050
+ await binding.notifyTurnEnd(outcome);
7051
+ } catch (err) {
7052
+ await binding.notifyTurnEnd({ ok: false, aborted: false });
7053
+ emitCommandOutput(
7054
+ `${t("turnFailed")}${err instanceof Error && err.message ? `
7055
+ ${err.message}` : ""}`
7056
+ );
7057
+ } finally {
7058
+ busy = false;
7059
+ }
7060
+ refreshGitStatus();
7061
+ flushPendingRender();
7062
+ fixedInput.exitOutputMode(buffer, cursorPos);
7063
+ };
7064
+ runWakeHandler = (event) => {
7065
+ if (done) return;
7066
+ if (busy || fixedInput.isPaused() || copyViewExitResolver) {
7067
+ const { actionGateHandler, confirmDestructiveAction } = buildInteractiveTurnHandlers();
7068
+ ensureChatQueueBinding(actionGateHandler, confirmDestructiveAction).enqueue(event);
7069
+ if (fixedInput.active && !fixedInput.isPaused()) {
7070
+ fixedInput.repaint(buffer, cursorPos);
7071
+ }
7072
+ return;
7073
+ }
7074
+ void runIdleTextTurn(event).catch((err) => {
7075
+ emitCommandOutput(
7076
+ `${t("turnFailed")}${err instanceof Error && err.message ? `
7077
+ ${err.message}` : ""}`
7078
+ );
7079
+ });
7080
+ };
6489
7081
  const handleInputToken = async (sequence) => {
6490
7082
  if (done) return;
6491
7083
  if (copyViewExitResolver) {
@@ -6619,18 +7211,41 @@ ${merged}` : merged;
6619
7211
  if (busyLock) {
6620
7212
  const trimmedText = submittedText.trim();
6621
7213
  const busySlashCommand = trimmedText.split(/\s+/)[0]?.toLowerCase();
6622
- const isBusyLocalSlash = busySlashCommand === "/context" || busySlashCommand === "/ctx" || busySlashCommand === "/switch";
7214
+ const isBusyLocalSlash = busySlashCommand === "/context" || busySlashCommand === "/ctx" || busySlashCommand === "/switch" || busySlashCommand === "/theme" || busySlashCommand === "/density" || busySlashCommand === "/runtime" || busySlashCommand === "/tools" || busySlashCommand === "/tasks" || busySlashCommand === "/jobs" || busySlashCommand === "/procs" || busySlashCommand === "/agents" || busySlashCommand === "/doc" || busySlashCommand === "/skill" || busySlashCommand === "/customize" || busySlashCommand === "/login" || busySlashCommand === "/profile" || busySlashCommand === "/version";
6623
7215
  if (isBusyLocalSlash) {
7216
+ releaseCollapsedPasteReferences(submittedText, pasteStore);
7217
+ buffer = "";
7218
+ cursorPos = 0;
7219
+ if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
6624
7220
  const beforeAgentKey = state.agentKey;
6625
7221
  const res = handleTuiInput(submittedText, state);
6626
- if (res.action) {
7222
+ if (res.action?.type === "theme-refresh") {
7223
+ state = res.nextState;
7224
+ const detected2 = await detectTerminalBackground({
7225
+ stdin: input,
7226
+ stdout: output,
7227
+ allowSystemFallback: true
7228
+ });
7229
+ let refreshMsg = "";
7230
+ if (detected2 && applyDetectedBackground(detected2)) {
7231
+ refreshMsg = t("themeRefreshed", detected2.brightness);
7232
+ } else if (detected2) {
7233
+ refreshMsg = t("themeRefreshed", detected2.brightness);
7234
+ } else {
7235
+ refreshMsg = t("themeRefreshFailed");
7236
+ }
7237
+ if (refreshMsg) {
7238
+ output.write(`${refreshMsg}
7239
+ `);
7240
+ }
7241
+ } else if (res.action) {
6627
7242
  output.write(
6628
7243
  "Model picker isn't available while a reply is running. Use `/switch <name>` to switch now (takes effect on the next turn), or wait for the reply to finish.\n"
6629
7244
  );
6630
7245
  } else {
6631
7246
  state = res.nextState;
6632
7247
  let msg = res.output;
6633
- if (busySlashCommand === "/switch" && res.nextState.agentKey !== beforeAgentKey) {
7248
+ if (busySlashCommand === "/switch" && persistExplicitAgentSwitch(beforeAgentKey)) {
6634
7249
  const hint = "Note: the new model takes effect on the next turn. Switching models may consume more tokens because the conversation context is re-sent to the new model.";
6635
7250
  msg = msg ? `${msg}
6636
7251
  ${hint}` : hint;
@@ -6640,43 +7255,10 @@ ${hint}` : hint;
6640
7255
  `);
6641
7256
  }
6642
7257
  }
6643
- releaseCollapsedPasteReferences(submittedText, pasteStore);
6644
- buffer = "";
6645
- cursorPos = 0;
6646
- if (fixedInput.active) fixedInput.repaint(buffer, cursorPos);
6647
7258
  return;
6648
7259
  }
6649
- const binding = ensureChatQueueBinding(
6650
- async (gate) => {
6651
- modalOwnsKeyboard = true;
6652
- try {
6653
- return await waitForRawActionGate(input, output, gate, spawnRunner, {
6654
- beforeSubprocess: () => fixedInput.pause(),
6655
- afterSubprocess: () => fixedInput.resumeFromSubprocess(),
6656
- registerToken: (handler) => {
6657
- rawActionGateTokenHandler = handler;
6658
- }
6659
- });
6660
- } finally {
6661
- modalOwnsKeyboard = false;
6662
- }
6663
- },
6664
- async (request) => {
6665
- modalOwnsKeyboard = true;
6666
- try {
6667
- return await dialogHost.run(
6668
- (anchor) => runConfirmDialog({
6669
- request,
6670
- input,
6671
- output,
6672
- ...anchor
6673
- })
6674
- );
6675
- } finally {
6676
- modalOwnsKeyboard = false;
6677
- }
6678
- }
6679
- );
7260
+ const { actionGateHandler, confirmDestructiveAction } = buildInteractiveTurnHandlers();
7261
+ const binding = ensureChatQueueBinding(actionGateHandler, confirmDestructiveAction);
6680
7262
  const decision = binding.resolveSubmit({
6681
7263
  text: submittedText,
6682
7264
  isRunning: true
@@ -6693,62 +7275,13 @@ ${hint}` : hint;
6693
7275
  return;
6694
7276
  }
6695
7277
  if (!submittedText.trim() && chatQueueBinding && chatQueueBinding.queueLength() > 0) {
6696
- const actionGateHandler = async (gate) => {
6697
- modalOwnsKeyboard = true;
6698
- try {
6699
- return await waitForRawActionGate(input, output, gate, spawnRunner, {
6700
- beforeSubprocess: () => fixedInput.pause(),
6701
- afterSubprocess: () => fixedInput.resumeFromSubprocess(),
6702
- registerToken: (handler) => {
6703
- rawActionGateTokenHandler = handler;
6704
- }
6705
- });
6706
- } finally {
6707
- modalOwnsKeyboard = false;
6708
- }
6709
- };
6710
- const confirmDestructiveAction = async (request) => {
6711
- modalOwnsKeyboard = true;
6712
- try {
6713
- return await dialogHost.run(
6714
- (anchor) => runConfirmDialog({
6715
- request,
6716
- input,
6717
- output,
6718
- ...anchor
6719
- })
6720
- );
6721
- } finally {
6722
- modalOwnsKeyboard = false;
6723
- }
6724
- };
7278
+ const { actionGateHandler, confirmDestructiveAction } = buildInteractiveTurnHandlers();
6725
7279
  const manualBinding = ensureChatQueueBinding(actionGateHandler, confirmDestructiveAction);
6726
7280
  const drainedText = manualBinding.drainHeadForManualTurn();
6727
7281
  if (drainedText !== null) {
6728
- busy = true;
6729
7282
  buffer = "";
6730
7283
  cursorPos = 0;
6731
- fixedInput.enterOutputMode(drainedText);
6732
- try {
6733
- const outcome = await runOneAgentTurn(
6734
- drainedText,
6735
- [],
6736
- actionGateHandler,
6737
- confirmDestructiveAction
6738
- );
6739
- await manualBinding.notifyTurnEnd(outcome);
6740
- } catch (err) {
6741
- await manualBinding.notifyTurnEnd({ ok: false, aborted: false });
6742
- emitCommandOutput(
6743
- `${t("turnFailed")}${err instanceof Error && err.message ? `
6744
- ${err.message}` : ""}`
6745
- );
6746
- } finally {
6747
- busy = false;
6748
- }
6749
- refreshGitStatus();
6750
- flushPendingRender();
6751
- fixedInput.exitOutputMode(buffer, cursorPos);
7284
+ await runIdleTextTurn(drainedText);
6752
7285
  return;
6753
7286
  }
6754
7287
  }
@@ -6758,37 +7291,11 @@ ${err.message}` : ""}`
6758
7291
  fixedInput.enterOutputMode(submittedText);
6759
7292
  let shouldExit = false;
6760
7293
  try {
7294
+ const { actionGateHandler, confirmDestructiveAction } = buildInteractiveTurnHandlers();
6761
7295
  shouldExit = await runSubmittedLine(
6762
7296
  submittedText,
6763
- async (gate) => {
6764
- modalOwnsKeyboard = true;
6765
- try {
6766
- return await waitForRawActionGate(input, output, gate, spawnRunner, {
6767
- beforeSubprocess: () => fixedInput.pause(),
6768
- afterSubprocess: () => fixedInput.resumeFromSubprocess(),
6769
- registerToken: (handler) => {
6770
- rawActionGateTokenHandler = handler;
6771
- }
6772
- });
6773
- } finally {
6774
- modalOwnsKeyboard = false;
6775
- }
6776
- },
6777
- async (request) => {
6778
- modalOwnsKeyboard = true;
6779
- try {
6780
- return await dialogHost.run(
6781
- (anchor) => runConfirmDialog({
6782
- request,
6783
- input,
6784
- output,
6785
- ...anchor
6786
- })
6787
- );
6788
- } finally {
6789
- modalOwnsKeyboard = false;
6790
- }
6791
- }
7297
+ actionGateHandler,
7298
+ confirmDestructiveAction
6792
7299
  );
6793
7300
  } finally {
6794
7301
  busy = false;
@@ -6862,6 +7369,7 @@ ${renderStatusLine(state)}
6862
7369
  output.write(`[nolo] ${t("persistentProcessesLeft", String(persistentCount))}
6863
7370
  `);
6864
7371
  }
7372
+ sessionEnded = true;
6865
7373
  rl.close();
6866
7374
  }
6867
7375
  }
@@ -6871,7 +7379,6 @@ export {
6871
7379
  applyOutputChunkToCurrentTurn,
6872
7380
  applyScrollAction,
6873
7381
  applyTerminalOutputToText,
6874
- buildTuiRunOverlay,
6875
7382
  buildWindowTitle,
6876
7383
  countPhysicalLines,
6877
7384
  createFixedInput,