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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/{_executeModel-42U3VOVZ.js → _executeModel-2ZAPHFOX.js} +46 -43
  2. package/{action-TLI3V62L.js → action-QM2ONPYZ.js} +9 -9
  3. package/{addContentAction-57RSIIMR.js → addContentAction-UXY3GM53.js} +8 -8
  4. package/{addDialogAgentAction-G3VWEA6E.js → addDialogAgentAction-MUBYOG65.js} +10 -10
  5. package/{agentDeleteCommand-5DD2IM3J.js → agentDeleteCommand-5TFSII4Z.js} +28 -27
  6. package/{agentEmailCommands-6FACJBU4.js → agentEmailCommands-XMXWGUMZ.js} +62 -58
  7. package/{agentListCommands-I6KZYCIG.js → agentListCommands-OWEFQEY4.js} +119 -33
  8. package/{agentMachineCommands-VKLIGMZA.js → agentMachineCommands-DGHYBBFC.js} +27 -26
  9. package/{agentPullCommand-O2MTUAFR.js → agentPullCommand-N6UWRTVL.js} +3 -3
  10. package/{agentRecordCommands-7TZICYDT.js → agentRecordCommands-I433EE5F.js} +53 -49
  11. package/{agentRunCommand-L4OM23KV.js → agentRunCommand-UQSX3IGW.js} +57 -52
  12. package/{agentRunControl-K4VKNEHF.js → agentRunControl-S3G62M6C.js} +3 -3
  13. package/{agentRuntimeLocal-ZYEIPTY4.js → agentRuntimeLocal-73SVOC5U.js} +14 -12
  14. package/{agentSlice-BX7GXSNO.js → agentSlice-JQCS4LR3.js} +9 -9
  15. package/{authCommand-EB5JSOX5.js → authCommand-B6P2XYLN.js} +11 -11
  16. package/{authCommands-XPEHTKSM.js → authCommands-4YKA4GTU.js} +7 -7
  17. package/{chunk-AS4PVWA4.js → chunk-2CVZSC5L.js} +1 -1
  18. package/{chunk-JRKKFRAV.js → chunk-3HPUUWOR.js} +58 -8
  19. package/{chunk-5KLGBAAR.js → chunk-3KGTFX3T.js} +2 -13
  20. package/{chunk-IWX4UW2E.js → chunk-524BDL33.js} +89 -8
  21. package/chunk-6QFNUK2G.js +144 -0
  22. package/{chunk-4QEWT7KD.js → chunk-AISLHYDB.js} +367 -85
  23. package/{chunk-2FTTDCD4.js → chunk-AMJ7LA5D.js} +1 -1
  24. package/{chunk-I6XMPTMR.js → chunk-BFDWXLFF.js} +2 -2
  25. package/{chunk-RL776AGR.js → chunk-BKGNXJAM.js} +1 -1
  26. package/{chunk-RCSJFQPB.js → chunk-BQECZ3Q4.js} +361 -90
  27. package/{chunk-VVX233PH.js → chunk-BT37DVDA.js} +9 -9
  28. package/{chunk-ENOG22KJ.js → chunk-BT57QC5K.js} +18 -20
  29. package/chunk-BYXBJQAS.js +0 -0
  30. package/{chunk-O2ZNTTET.js → chunk-C4DZRHFJ.js} +24 -15
  31. package/{chunk-4XENYTF5.js → chunk-DS3VC4EA.js} +3 -3
  32. package/{chunk-N54XAP77.js → chunk-FVVST32H.js} +1 -1
  33. package/{chunk-P3PKED2I.js → chunk-FZ2G3I4B.js} +11 -13
  34. package/{chunk-AGFH73SK.js → chunk-G7KM6BBZ.js} +1 -1
  35. package/{chunk-2ECQG4YX.js → chunk-GROGNZOA.js} +264 -161
  36. package/chunk-IOQIOZNI.js +42 -0
  37. package/{chunk-AL2KDGR2.js → chunk-IPA2WQGY.js} +201 -98
  38. package/{chunk-7DAJ4AI4.js → chunk-IYMGQ3TN.js} +9 -2
  39. package/{chunk-CKOJQDKR.js → chunk-JEMATI63.js} +1 -1
  40. package/{chunk-7U44LYTR.js → chunk-JTINRTBT.js} +1 -1
  41. package/{chunk-4O3CARJ4.js → chunk-KTHLAJEY.js} +1 -1
  42. package/{chunk-URUUZLPA.js → chunk-L3KWEH3V.js} +8 -8
  43. package/{chunk-XBUBEFNX.js → chunk-L76QNLGU.js} +1 -1
  44. package/{chunk-UB6SN4NJ.js → chunk-LE6JAIXE.js} +6 -6
  45. package/{chunk-2JCXITPK.js → chunk-LRKEOCWY.js} +15 -15
  46. package/{chunk-5EFXE3O7.js → chunk-LTH6AVG7.js} +3 -3
  47. package/{chunk-YPRZSPF3.js → chunk-OCDEWRE2.js} +1 -1
  48. package/{chunk-6KHNO3SK.js → chunk-P2SPC3FT.js} +27 -23
  49. package/{chunk-4IJYA27C.js → chunk-PXZAAB4L.js} +4 -4
  50. package/{chunk-VUW5FBDB.js → chunk-Q7S2ORXX.js} +9 -9
  51. package/{chunk-C3RUNRHT.js → chunk-QWM7YWVT.js} +1 -1
  52. package/{chunk-25LCXTML.js → chunk-RQPZX6KP.js} +2 -2
  53. package/chunk-S3UTETIC.js +4885 -0
  54. package/{chunk-SDJDLO5W.js → chunk-SBVUZC2E.js} +5 -5
  55. package/{chunk-VX3SXJ3N.js → chunk-UBSKX3LP.js} +6 -6
  56. package/{chunk-4ZDM73SD.js → chunk-V2J635L5.js} +1 -1
  57. package/{chunk-DSZUWUYB.js → chunk-VBKJTOAW.js} +1 -1
  58. package/{chunk-FVYY6MCS.js → chunk-VUEXKW6U.js} +17 -17
  59. package/{chunk-7KCUSYT2.js → chunk-WKQGZJVI.js} +7 -7
  60. package/{chunk-GU3PEVGZ.js → chunk-WM7P5OWZ.js} +2639 -1579
  61. package/{chunk-7GNGZRLZ.js → chunk-WTBWWBSF.js} +150 -78
  62. package/{chunk-FSO5B7GH.js → chunk-WVS3GX3B.js} +2 -2
  63. package/chunk-Z3NEXVYE.js +33 -0
  64. package/{createAgentAutomationAction-RJF3N2QY.js → createAgentAutomationAction-GXZNLJZK.js} +26 -24
  65. package/{createDialogAction-DRSPS6CE.js → createDialogAction-KLW42ZGF.js} +26 -24
  66. package/{createPageAction-6B2WMNO3.js → createPageAction-IBJHE6MH.js} +26 -24
  67. package/{dbSlice-Q3TM2FJV.js → dbSlice-R5EJKZBW.js} +7 -7
  68. package/{deleteFile-OW53A6BX.js → deleteFile-WPWVZE4B.js} +5 -5
  69. package/{dialogCommands-4IX4XIZE.js → dialogCommands-7O5PUZRU.js} +23 -22
  70. package/{dialogSlice-3M7KX2TX.js → dialogSlice-GGV7PF53.js} +25 -23
  71. package/{docCreateCommands-SIL3NPGT.js → docCreateCommands-7ZO2NQQF.js} +22 -21
  72. package/{docDeleteCommands-7IOMLUS4.js → docDeleteCommands-YKIXZYBR.js} +23 -22
  73. package/{docReadCommands-FNFXUA5L.js → docReadCommands-5UDONBUL.js} +22 -21
  74. package/{docStore-SV7E34D4.js → docStore-VDWTATFH.js} +25 -23
  75. package/{docUpdateCommands-X2HNDVUC.js → docUpdateCommands-EPZIIQMY.js} +22 -21
  76. package/fetchMessages-KTNM7QAR.js +1 -1
  77. package/{getPricing-3E6UNPKL.js → getPricing-6TOZXPAU.js} +7 -3
  78. package/{handleSendMessageAction-JYETO5RZ.js → handleSendMessageAction-MMATC5V5.js} +30 -28
  79. package/index.js +74 -74
  80. package/{localDialogRead-2VRI24F4.js → localDialogRead-YSKFFRTT.js} +1 -1
  81. package/{machineCommands-RIO3RP5Q.js → machineCommands-PTHPKSIZ.js} +59 -55
  82. package/{machineWsRunDispatch-W7UYLGB5.js → machineWsRunDispatch-M5L2NVWJ.js} +51 -47
  83. package/{memoryCommands-O4MIPIWQ.js → memoryCommands-HG4WKTU7.js} +2 -2
  84. package/{models-4OPOOLFS.js → models-H524GH3U.js} +3 -2
  85. package/package.json +1 -1
  86. package/{read-GW7ZEI23.js → read-5ZMY7FKP.js} +5 -5
  87. package/{readDocTool-HUFU6OCT.js → readDocTool-2NXDK32S.js} +1 -1
  88. package/{readlineWorkspace-27JR7WPW.js → readlineWorkspace-4NXMEOHP.js} +835 -622
  89. package/{removeDialogAgentAction-CXKR4NJI.js → removeDialogAgentAction-KOLQ2R5W.js} +13 -12
  90. package/{runAgentClientLoop-HZFAPJHS.js → runAgentClientLoop-FGUYNGSA.js} +45 -42
  91. package/{runtimeDoctorCommands-LRHLPDRC.js → runtimeDoctorCommands-UWR5UDSM.js} +5 -5
  92. package/{setDialogExtraReferencesAction-6RKUSXHX.js → setDialogExtraReferencesAction-OMTEO7X6.js} +10 -10
  93. package/{setPrimaryDialogAgentAction-T3UTBEFG.js → setPrimaryDialogAgentAction-DOCIS6V4.js} +13 -12
  94. package/{settingSlice-IVKXJXMG.js → settingSlice-UWZJYDOA.js} +8 -8
  95. package/{spaceCommands-E7VOF4LO.js → spaceCommands-S3EB6TOS.js} +29 -28
  96. package/{spaceSlice-TSVROA5U.js → spaceSlice-RG2GSV7K.js} +24 -22
  97. package/{streamAgentChatTurn-3C5RQZJF.js → streamAgentChatTurn-X576C23M.js} +209 -86
  98. package/{tableCommands-O7O2LBEG.js → tableCommands-M3DJUETS.js} +10 -10
  99. package/{toolApiClient-QI3YBNWB.js → toolApiClient-C7P7YSE4.js} +2 -2
  100. package/{tools-EQDEKR6N.js → tools-HURZ2N4O.js} +33 -30
  101. package/{tuiSummaryLlmCaller-RV3THLZF.js → tuiSummaryLlmCaller-DG2LBTBX.js} +8 -6
  102. package/{updateCommands-VOSPKXFF.js → updateCommands-TC5SX42Y.js} +3 -3
  103. package/{updateDialogTitleAction-3XV7VWSZ.js → updateDialogTitleAction-62LVIAUZ.js} +28 -26
  104. package/chunk-BBYVOSU2.js +0 -792
  105. package/chunk-DCOA4QSN.js +0 -105
  106. package/{chunk-DZ3XJVET.js → chunk-3QAF2LTL.js} +3 -3
  107. package/{chunk-EHLHSX7R.js → chunk-5MMZUWSG.js} +3 -3
  108. package/{chunk-ISNCLGUZ.js → chunk-AVMJVOQY.js} +3 -3
  109. package/{chunk-UTLZINAP.js → chunk-DVRHHFEA.js} +10 -10
  110. package/{chunk-4CQLVGRV.js → chunk-ID4G2MAR.js} +3 -3
  111. package/{chunk-5MIDPSSK.js → chunk-JK7VW2FN.js} +3 -3
  112. package/{chunk-TILFE6LV.js → chunk-YOCHZD7D.js} +3 -3
  113. package/{db-HCJ5PRB5.js → db-6QED3ZVO.js} +3 -3
  114. package/{understanding-3CUQQ6SV.js → understanding-ZOESBLF3.js} +3 -3
@@ -1,11 +1,6 @@
1
1
  import {
2
2
  resolveMachineId
3
3
  } from "./chunk-6HYVJV7B.js";
4
- import {
5
- createRunActivityTracker,
6
- finalizeRunRecord,
7
- spawnLocalBackgroundRun
8
- } from "./chunk-2FTTDCD4.js";
9
4
  import {
10
5
  buildLocalRunEnv,
11
6
  isFullstackCodingAgentRef,
@@ -14,22 +9,25 @@ import {
14
9
  resolveRawAgentInput,
15
10
  resolveServerUrl,
16
11
  writeUsage
17
- } from "./chunk-DZ3XJVET.js";
12
+ } from "./chunk-3QAF2LTL.js";
18
13
  import {
19
14
  createCliCallAgentToolExecutor,
20
15
  createCliLocalRuntimeAdapter,
21
16
  isBuiltinNoloAgentRef
22
- } from "./chunk-RCSJFQPB.js";
17
+ } from "./chunk-BQECZ3Q4.js";
18
+ import {
19
+ CliProviderQuotaError
20
+ } from "./chunk-CMKUBIGW.js";
23
21
  import {
24
22
  getReadableCliDb
25
23
  } from "./chunk-W4NVOSFF.js";
26
24
  import {
27
25
  readDbRecord,
28
26
  resolveAgentRecordFromHybridStore
29
- } from "./chunk-SDJDLO5W.js";
27
+ } from "./chunk-SBVUZC2E.js";
30
28
  import {
31
29
  normalizeAgentHandle
32
- } from "./chunk-N54XAP77.js";
30
+ } from "./chunk-FVVST32H.js";
33
31
  import {
34
32
  NOLO_PROJECT_MANAGER_AGENT_KEY,
35
33
  isLocalCliAgentKey
@@ -38,20 +36,26 @@ import {
38
36
  applyModelLayerOverride,
39
37
  buildModelLayerOverride
40
38
  } from "./chunk-LTRXGFMS.js";
41
- import {
42
- CliProviderQuotaError
43
- } from "./chunk-CMKUBIGW.js";
44
39
  import {
45
40
  isGatewayHttpStatus
46
41
  } from "./chunk-GAKEBQD6.js";
47
42
  import {
48
43
  buildMemorySubjectsForAgent,
49
44
  resolveAgentMemoryPolicy
50
- } from "./chunk-ENOG22KJ.js";
45
+ } from "./chunk-BT57QC5K.js";
51
46
  import {
52
47
  parseExternalSkillMarkdown,
53
48
  parseSkillDocProtocol
54
49
  } from "./chunk-VC6OZXO5.js";
50
+ import {
51
+ API_REPORTED_COST_MULTIPLIER,
52
+ DEFAULT_CONTEXT_WINDOW,
53
+ getModelContextWindow,
54
+ parseUiAskChoiceContent
55
+ } from "./chunk-GROGNZOA.js";
56
+ import {
57
+ parsePositiveFiniteNumberOrFallback
58
+ } from "./chunk-Y5GCAMGP.js";
55
59
  import {
56
60
  touchMemoryItemsInDb
57
61
  } from "./chunk-HNW7IJSK.js";
@@ -59,17 +63,10 @@ import {
59
63
  chooseMemoryOwners,
60
64
  loadMemoryCandidatesFromDb
61
65
  } from "./chunk-XENYHRUE.js";
62
- import {
63
- getModelContextWindow,
64
- parseUiAskChoiceContent
65
- } from "./chunk-2ECQG4YX.js";
66
66
  import {
67
67
  readPipeText,
68
68
  spawnProcess
69
69
  } from "./chunk-IDZTNRP2.js";
70
- import {
71
- resolveAuthToken
72
- } from "./chunk-ISNCLGUZ.js";
73
70
  import {
74
71
  buildSkillDiscoveryContextBlock
75
72
  } from "./chunk-AFON4QUU.js";
@@ -80,28 +77,39 @@ import {
80
77
  import {
81
78
  buildPlatformChatCompletionRequest,
82
79
  parsePlatformChatCompletionData
83
- } from "./chunk-5KLGBAAR.js";
80
+ } from "./chunk-3KGTFX3T.js";
84
81
  import {
85
82
  resolveAgentRuntimeConfigFromRecord
86
- } from "./chunk-TILFE6LV.js";
83
+ } from "./chunk-YOCHZD7D.js";
87
84
  import {
88
85
  LOCAL_AGENT_CONFIG_MISSING_CODE,
89
86
  LOCAL_TURN_ABORTED_CODE,
90
87
  readActionGate,
91
88
  readCommandActionGatePayload
92
- } from "./chunk-4QEWT7KD.js";
89
+ } from "./chunk-AISLHYDB.js";
93
90
  import {
94
91
  resolvePlatformChatCompletionsEndpoint
95
- } from "./chunk-JRKKFRAV.js";
92
+ } from "./chunk-3HPUUWOR.js";
96
93
  import {
97
94
  clipCompactText
98
95
  } from "./chunk-5MMAK75Q.js";
99
96
  import {
100
97
  findModelConfig
101
- } from "./chunk-BBYVOSU2.js";
98
+ } from "./chunk-S3UTETIC.js";
99
+ import {
100
+ asTrimmedLowercaseString
101
+ } from "./chunk-QSJ3HFEF.js";
102
102
  import {
103
103
  compactWhitespace
104
104
  } from "./chunk-DWLEOOGR.js";
105
+ import {
106
+ createRunActivityTracker,
107
+ finalizeRunRecord,
108
+ spawnLocalBackgroundRun
109
+ } from "./chunk-AMJ7LA5D.js";
110
+ import {
111
+ resolveAuthToken
112
+ } from "./chunk-AVMJVOQY.js";
105
113
  import {
106
114
  toErrorMessage
107
115
  } from "./chunk-HNMXYYJR.js";
@@ -109,28 +117,251 @@ import {
109
117
  getDefaultCliLocalRuntimeDb
110
118
  } from "./chunk-H33743XH.js";
111
119
  import {
112
- parsePositiveFiniteNumberOrFallback
113
- } from "./chunk-Y5GCAMGP.js";
114
- import {
115
- asTrimmedLowercaseString
116
- } from "./chunk-QSJ3HFEF.js";
117
- import {
120
+ BUILTIN_NOLO_AGENT_KEY,
118
121
  PUBLIC_DEEPSEEK_V4_FLASH_AGENT_KEY,
119
122
  PUBLIC_DEEPSEEK_V4_PRO_AGENT_KEY,
120
123
  PUBLIC_GLM_52_AGENT_KEY,
121
124
  PUBLIC_KIMI_K26_IMAGE_AGENT_KEY
122
- } from "./chunk-DSZUWUYB.js";
123
- import {
124
- isLoopbackUrl
125
- } from "./chunk-4SN6J65J.js";
125
+ } from "./chunk-VBKJTOAW.js";
126
126
  import {
127
127
  asTrimmedString
128
128
  } from "./chunk-3EUW3WEO.js";
129
+ import {
130
+ isLoopbackUrl
131
+ } from "./chunk-4SN6J65J.js";
129
132
  import {
130
133
  asOptionalTrimmedString
131
134
  } from "./chunk-G3SUCT4W.js";
132
135
 
136
+ // packages/agent-runtime/quickChatIntentCore.ts
137
+ var INTENT_MODEL = "deepseek-v4-flash";
138
+ var INTENT_PROVIDER = "deepseek";
139
+ var QUICK_CHAT_INTENT_TIMEOUT_MS = 4e3;
140
+ var SHORT_GREETING_MAX_LENGTH = 20;
141
+ var SHORT_GREETING_PATTERN = /^(?:hi|hello|hey|hiya|yo|sup|howdy|嗨|哈喽|哈囉|你好|您好|在吗|在嘛|喂|早上好|中午好|下午好|晚上好|拜拜|再见|谢谢|thanks|thx|ok|好的|收到|了解)[!.。,、~~]?$/iu;
142
+ function isShortGreeting(text) {
143
+ const trimmed = text.trim();
144
+ if (!trimmed) return false;
145
+ if (trimmed.length > SHORT_GREETING_MAX_LENGTH) return false;
146
+ return SHORT_GREETING_PATTERN.test(trimmed);
147
+ }
148
+ var COMPLEXITY_SIMPLE_MAX_LENGTH = 80;
149
+ var COMPLEXITY_MEDIUM_MAX_LENGTH = 500;
150
+ var COMPLEXITY_KEYWORDS = /为什么|如何|分析|对比|比较|设计|架构|实现|原理|推导|评价|区别|关系|影响|优化|重构/;
151
+ var COMPLEXITY_CODE_BLOCK = /```[\s\S]*```/;
152
+ function estimateComplexity(text) {
153
+ const trimmed = text.trim();
154
+ const len = trimmed.length;
155
+ if (len > COMPLEXITY_MEDIUM_MAX_LENGTH) return "complex";
156
+ if (COMPLEXITY_CODE_BLOCK.test(trimmed)) return "complex";
157
+ if (len > COMPLEXITY_SIMPLE_MAX_LENGTH) return "medium";
158
+ if (COMPLEXITY_KEYWORDS.test(trimmed)) return "medium";
159
+ return "simple";
160
+ }
161
+ var TIER_DESCRIPTIONS = {
162
+ flash: "\u5FEB\u901F\u7B80\u5355\uFF1A\u95EE\u5019\u3001\u95F2\u804A\u3001\u5FEB\u901F\u95EE\u7B54\u3001\u7FFB\u8BD1\u3001\u77ED\u6587",
163
+ balanced: "\u5E73\u8861\u63A8\u7406\uFF1A\u5206\u6790\u3001\u4E2D\u7B49\u957F\u5EA6\u5199\u4F5C\u3001\u4EE3\u7801\u3001\u63A8\u7406",
164
+ quality: "\u9AD8\u8D28\u91CF\u6DF1\u5EA6\uFF1A\u590D\u6742\u63A8\u7406\u3001\u957F\u6587\u3001\u67B6\u6784\u8BBE\u8BA1\u3001\u6DF1\u5EA6\u5206\u6790"
165
+ };
166
+ function buildQuickChatIntentSystemPrompt(tierAgents) {
167
+ const agentList = tierAgents.map((t2) => ` - "${t2.agentKey}"\uFF1A${t2.description}`).join("\n");
168
+ return `\u4F60\u662F\u4E00\u4E2A\u610F\u56FE\u8DEF\u7531\u5668\u3002\u6839\u636E\u7528\u6237\u6D88\u606F\uFF0C\u5224\u65AD\u5E94\u8BE5\u4EA4\u7ED9\u54EA\u4E2A agent \u5904\u7406\u3002
169
+
170
+ \u53EF\u9009 agent \u5217\u8868\uFF08agentKey \u4E0E\u63CF\u8FF0\uFF09\uFF1A
171
+ ${agentList}
172
+
173
+ \u5224\u65AD\u539F\u5219\uFF08\u9ED8\u8BA4\u504F\u5411\u901A\u7528\u6863\u4F4D\uFF1B\u4E13\u804C agent \u5728\u660E\u786E\u5339\u914D\u65F6\u4F7F\u7528\uFF1B**\u7F16\u7801/\u672C\u5730\u4ED3\u5E93\u4EFB\u52A1\u8D70\u901A\u7528\u6863 + needsWorkspace=true**\uFF09\uFF1A
174
+ - \u9ED8\u8BA4\u60C5\u51B5\u4E0B\uFF0C**\u9664\u975E\u7528\u6237\u660E\u786E\u8868\u8FBE\u8981\u5EFA\u5E94\u7528/\u5EFA Agent/\u63D0\u4EA4\u53CD\u9988/\u591A Agent \u7F16\u6392**\uFF0C\u5426\u5219\u9009\u901A\u7528\u6863\u4F4D\uFF08flash/balanced/quality\uFF09\uFF1A
175
+ - \u7B80\u5355\u95EE\u5019\u3001\u95F2\u804A\u3001\u5FEB\u901F\u95EE\u7B54\u3001\u7FFB\u8BD1\u3001\u77ED\u6587 \u2192 \u9009 flash \u6863
176
+ - \u9700\u8981\u63A8\u7406\u3001\u5206\u6790\u3001\u4E2D\u7B49\u957F\u5EA6\u5199\u4F5C\u3001\u666E\u901A\u4EE3\u7801\u89E3\u91CA/\u67B6\u6784\u95EE\u7B54 \u2192 \u9009 balanced \u6863
177
+ - \u590D\u6742\u63A8\u7406\u3001\u957F\u6587\u3001\u67B6\u6784\u8BBE\u8BA1\u3001\u6DF1\u5EA6\u5206\u6790 \u2192 \u9009 quality \u6863
178
+ - **\u660E\u786E\u7F16\u7801\u4EFB\u52A1\uFF08\u5B9E\u73B0\u529F\u80FD\u3001\u4FEE bug\u3001refactor\u3001\u5199/\u8865\u6D4B\u8BD5\u3001\u8DD1 build/tests/ci\uFF09\u6216\u67E5\u770B\u672C\u673A\u4ED3\u5E93/\u4EE3\u7801\u72B6\u6001\uFF08git/\u5206\u652F/commit/\u672C\u5730\u4E0E\u8FDC\u7A0B\u5DEE\u8DDD/\u672A\u63D0\u4EA4\u6539\u52A8\u7B49\uFF09** \u2192 \u9009 balanced \u6216 quality \u6863\uFF08\u6309\u590D\u6742\u5EA6\uFF1B**\u4E0D\u8981\u9009 flash**\uFF09\uFF0C\u4E14 needsWorkspace=true
179
+ - \u4E13\u804C agent \u8DEF\u7531\u89C4\u5219\uFF08\u6709\u660E\u786E\u5339\u914D\u65F6\u4F7F\u7528\uFF09\uFF1A
180
+ - \u8981\u63D0\u4EA4/\u8BB0\u5F55 bug\u3001\u4F53\u9A8C\u95EE\u9898\u3001\u6570\u636E\u5F02\u5E38\u3001\u4EA7\u54C1\u5EFA\u8BAE\uFF0C\u6216\u53E3\u8BED\u5982\u300C\u60F3\u53CD\u9988\u4E00\u4E9B\u95EE\u9898\u300D\u300C\u53CD\u9988\u4E00\u4E0B\u300D\u2192 \u9009\u610F\u89C1\u53CD\u9988\u4E13\u804C agent
181
+ - \u8981\u65B0\u5EFA\u6216\u5B9A\u5236 AI/Agent/\u667A\u80FD\u4F53\u52A9\u624B\uFF08\u542B\u914D\u7F6E\u5DE5\u5177\u3001\u77E5\u8BC6\u3001\u786E\u8BA4\u521B\u5EFA\uFF09\u2192 \u9009\u521B\u5EFA Agent \u4E13\u804C agent
182
+ - \u8981\u505A/\u6539/\u53D1\u5E03\u7F51\u7AD9\u3001\u7F51\u9875\u3001\u5C0F\u5E94\u7528\u3001\u9884\u7EA6\u9875\u3001\u535A\u5BA2\u3001\u770B\u677F \u2192 \u9009\u5E94\u7528\u6784\u5EFA\u4E13\u804C agent
183
+ - \u8981\u6D3E\u591A\u4E2A agent\u3001\u591A agent \u5E76\u884C\u3001\u62C6\u6210\u4EFB\u52A1\u540E\u53F0\u6267\u884C\u3001\u591A\u4E2A agent review\u3001\u8BA9\u4E0D\u540C agent \u8BA8\u8BBA/\u4F1A\u5546 \u2192 \u9009\u591A Agent \u7F16\u6392\u4E13\u804C agent\uFF08\u663E\u5F0F multi-agent \u4F18\u5148\uFF09
184
+ - **\u62FF\u4E0D\u51C6\u65F6**\uFF1A\u7EAF\u95F2\u804A\u9009 flash\uFF1B**\u82E5\u6CBE\u8FB9\u672C\u5730\u4ED3\u5E93/\u5F53\u524D\u4EE3\u7801\u72B6\u6001\uFF0C\u9009 balanced/quality + needsWorkspace=true**\u3002
185
+
186
+ \u540C\u65F6\u5224\u65AD\u672C\u8F6E\u662F\u5426\u9700\u8981\u6CE8\u5165\u300C\u5DE5\u4F5C\u533A\u53EA\u8BFB\u5DE5\u5177\u300D\uFF08listFiles/globFiles/searchFiles/readFile/execShell \u7B49\uFF09\uFF1A
187
+ - needsWorkspace=true \u7684\u573A\u666F\uFF1A\u7528\u6237\u8981\u8BFB/\u6539/\u5206\u6790\u6587\u4EF6\u6216\u4EE3\u7801\u3001\u63D0\u5230\u4E86\u6587\u4EF6\u8DEF\u5F84/\u6269\u5C55\u540D\uFF08.md/.csv/.ts \u7B49\uFF09\u3001\u63D0\u5230\u5DE5\u4F5C\u533A/\u4EE3\u7801\u5E93/\u4ED3\u5E93/\u76EE\u5F55/git/\u5206\u652F\u3001\u8981\u770B\u5F53\u524D\u4EE3\u7801\u6216\u4ED3\u5E93\u72B6\u6001\u3001\u672C\u5730\u641C\u7D22/\u67E5\u770B\u4EE3\u7801\u3002
188
+ - needsWorkspace=false \u7684\u573A\u666F\uFF1A\u901A\u7528\u77E5\u8BC6\u95EE\u7B54\uFF08\u5982\u300C\u5E7F\u4E1C\u7ECF\u6D4E\u6570\u636E\u300D\u300C\u5199\u4E00\u9996\u8BD7\u300D\uFF09\u3001\u7EAF\u95F2\u804A\u95EE\u5019\u3001\u7FFB\u8BD1\u3001\u4E0D\u6D89\u53CA\u672C\u5730\u6587\u4EF6/\u4ED3\u5E93\u3002
189
+ - \u6F0F\u5224\uFF08\u7528\u6237\u8981\u6587\u4EF6\u800C\u6CA1\u5E26\u5DE5\u5177\uFF09\u6BD4\u8BEF\u5224\uFF08\u767D\u641C\u51E0\u8F6E\uFF09\u4EE3\u4EF7\u66F4\u9AD8\uFF0C\u5B81\u53EF\u504F\u5BBD\u5224 true\u3002
190
+
191
+ \u5728\u7ED9\u51FA agentKey \u4E4B\u524D\uFF0C\u5148\u5728\u5FC3\u91CC\u533A\u5206\u5019\u9009\u6863\u4F4D\u7684\u8FB9\u754C\u518D\u7ED9\u7F6E\u4FE1\u5EA6\uFF1Abalanced \u4E0E quality \u7684\u8FB9\u754C\u5728\u4E8E\u590D\u6742\u5EA6/\u7BC7\u5E45/\u662F\u5426\u9700\u8981\u6DF1\u5EA6\u5206\u6790\uFF0C\u62FF\u4E0D\u51C6\u5C31\u7ED9\u4F4E confidence\u3002
192
+
193
+ \u540C\u65F6\u5224\u65AD\u672C\u8F6E\u6D88\u606F\u662F\u5426\u9700\u8981\u6302\u8F7D\u5BF9\u8C61\u64CD\u4F5C\u6280\u80FD\uFF08skills \u6570\u7EC4\uFF0C\u672A\u547D\u4E2D\u7ED9 []\uFF09\uFF1A
194
+ - \u7528\u6237\u8981\u65B0\u5EFA/\u6574\u7406/\u5F55\u5165/\u6279\u91CF\u5904\u7406\u8868\u683C\u6570\u636E\uFF08\u5EFA\u8868\u3001\u52A0\u884C\u3001\u6539\u5217\u3001\u6309\u6761\u4EF6\u6574\u7406\u6570\u636E\uFF09\u2192 skills \u542B "table"
195
+ - \u7528\u6237\u8981\u5199/\u6DA6\u8272/\u6539\u5199/\u7EED\u5199/\u6392\u7248\u4E00\u7BC7\u6587\u6863\u6216\u6587\u7AE0 \u2192 skills \u542B "doc"
196
+ - \u7528\u6237\u8981\u5199/\u6539/\u5B9E\u73B0/\u4FEE bug/refactor/\u8865\u6D4B\u8BD5/\u5BA1\u67E5\u5E76\u4FEE\u6539\u4EE3\u7801 \u2192 skills \u542B "code"\uFF08\u4EC5\u95EE\u901A\u7528\u77E5\u8BC6\u3001\u7EAF\u95F2\u804A\u3001\u53EA\u67E5\u770B git/\u4ED3\u5E93\u72B6\u6001\u800C\u4E0D\u4FEE\u6539\u4EE3\u7801\u65F6\u4E0D\u6302 "code"\uFF09
197
+ - \u666E\u901A\u95EE\u7B54/\u7EAF\u95F2\u804A/\u5EFA\u5E94\u7528 \u2192 skills \u4E3A []
198
+
199
+ \u53EA\u8F93\u51FA JSON\uFF0C\u4E0D\u8981\u8F93\u51FA\u5176\u4ED6\u5185\u5BB9\uFF1A
200
+ {"confidence": <0\u52301\u4E4B\u95F4\u7684\u5C0F\u6570\uFF0C\u8868\u793A\u4F60\u5BF9\u8FD9\u4E2A\u5206\u7C7B\u5224\u65AD\u7684\u628A\u63E1\u7A0B\u5EA6>, "agentKey": "<\u4ECE\u5217\u8868\u4E2D\u9009\u62E9\u7684 agentKey>", "needsWorkspace": <true \u6216 false>, "skills": <\u5B57\u7B26\u4E32\u6570\u7EC4\uFF0C\u5143\u7D20\u53EA\u80FD\u662F "table" | "doc" | "code"\uFF0C\u672A\u547D\u4E2D\u7ED9 []>}`;
201
+ }
202
+ function parseQuickChatIntentResult(content, tierAgents) {
203
+ try {
204
+ const parsed = JSON.parse(content.trim());
205
+ if (typeof parsed !== "object" || parsed === null || !("agentKey" in parsed)) return null;
206
+ const key = parsed.agentKey;
207
+ if (typeof key !== "string") return null;
208
+ if (!tierAgents.some((t2) => t2.agentKey === key)) return null;
209
+ const rawNeedsWorkspace = parsed.needsWorkspace;
210
+ const needsWorkspace = rawNeedsWorkspace === true;
211
+ const rawConfidence = parsed.confidence;
212
+ const confidence = typeof rawConfidence === "number" && Number.isFinite(rawConfidence) && rawConfidence >= 0 && rawConfidence <= 1 ? rawConfidence : void 0;
213
+ const rawSkills = parsed.skills;
214
+ let skills;
215
+ if (Array.isArray(rawSkills)) {
216
+ const filtered = [...new Set(rawSkills)].filter(
217
+ (s) => s === "table" || s === "doc" || s === "code"
218
+ );
219
+ if (filtered.length > 0) skills = filtered;
220
+ }
221
+ return { agentKey: key, needsWorkspace, confidence, skills };
222
+ } catch {
223
+ return null;
224
+ }
225
+ }
226
+
227
+ // packages/cli/client/autoModelRouter.ts
228
+ var CLI_AUTO_TIER_AGENT_KEYS = {
229
+ flash: PUBLIC_DEEPSEEK_V4_FLASH_AGENT_KEY,
230
+ balanced: PUBLIC_DEEPSEEK_V4_PRO_AGENT_KEY,
231
+ quality: PUBLIC_GLM_52_AGENT_KEY
232
+ };
233
+ var CLI_AUTO_TIER_MODELS = {
234
+ flash: "deepseek-v4-flash",
235
+ balanced: "deepseek-v4-pro",
236
+ quality: "glm-5.2"
237
+ };
238
+ var CLI_IMAGE_AGENT_KEY = PUBLIC_KIMI_K26_IMAGE_AGENT_KEY;
239
+ var CLI_IMAGE_AGENT_MODEL = "kimi-k2.6";
240
+ var CLI_AUTO_TIER_AGENT_KEY_TABLE = {
241
+ [CLI_AUTO_TIER_AGENT_KEYS.flash]: true,
242
+ [CLI_AUTO_TIER_AGENT_KEYS.balanced]: true,
243
+ [CLI_AUTO_TIER_AGENT_KEYS.quality]: true
244
+ };
245
+ function resolveCliAutoAgentModel(agentKey) {
246
+ if (agentKey === CLI_AUTO_TIER_AGENT_KEYS.flash) return CLI_AUTO_TIER_MODELS.flash;
247
+ if (agentKey === CLI_AUTO_TIER_AGENT_KEYS.balanced) {
248
+ return CLI_AUTO_TIER_MODELS.balanced;
249
+ }
250
+ if (agentKey === CLI_AUTO_TIER_AGENT_KEYS.quality) {
251
+ return CLI_AUTO_TIER_MODELS.quality;
252
+ }
253
+ if (agentKey === CLI_IMAGE_AGENT_KEY) return CLI_IMAGE_AGENT_MODEL;
254
+ return void 0;
255
+ }
256
+ var buildRouteOptions = () => ["flash", "balanced", "quality"].map((tier) => ({
257
+ tier,
258
+ agentKey: CLI_AUTO_TIER_AGENT_KEYS[tier],
259
+ description: TIER_DESCRIPTIONS[tier]
260
+ }));
261
+ var tierForAgentKey = (agentKey) => {
262
+ for (const tier of ["flash", "balanced", "quality"]) {
263
+ if (CLI_AUTO_TIER_AGENT_KEYS[tier] === agentKey) return tier;
264
+ }
265
+ return null;
266
+ };
267
+ function resolveCliAutoFallbackTier(text) {
268
+ switch (estimateComplexity(text)) {
269
+ case "complex":
270
+ return "quality";
271
+ case "medium":
272
+ return "balanced";
273
+ case "simple":
274
+ default:
275
+ return "flash";
276
+ }
277
+ }
278
+ async function classifyCliAutoRoute(text, options) {
279
+ const timeoutMs = options.timeoutMs ?? QUICK_CHAT_INTENT_TIMEOUT_MS;
280
+ const fallbackTier = resolveCliAutoFallbackTier(text);
281
+ const fallback = () => ({
282
+ agentKey: CLI_AUTO_TIER_AGENT_KEYS[fallbackTier],
283
+ tier: fallbackTier,
284
+ classified: false
285
+ });
286
+ if (!text.trim()) return fallback();
287
+ if (isShortGreeting(text)) {
288
+ return {
289
+ agentKey: CLI_AUTO_TIER_AGENT_KEYS.flash,
290
+ tier: "flash",
291
+ classified: true
292
+ };
293
+ }
294
+ if (!options.authToken) return fallback();
295
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
296
+ const controller = new AbortController();
297
+ const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs);
298
+ try {
299
+ const routeOptions = buildRouteOptions();
300
+ const messages = [
301
+ { role: "system", content: buildQuickChatIntentSystemPrompt(routeOptions) },
302
+ { role: "user", content: text }
303
+ ];
304
+ const { url, init } = buildPlatformChatCompletionRequest({
305
+ providerConfig: {
306
+ serverUrl: options.serverUrl,
307
+ authToken: options.authToken,
308
+ agentKey: CLI_AUTO_TIER_AGENT_KEYS.flash,
309
+ model: INTENT_MODEL,
310
+ provider: INTENT_PROVIDER,
311
+ endpoint: resolvePlatformChatCompletionsEndpoint(INTENT_PROVIDER) ?? "",
312
+ requestOptions: {}
313
+ },
314
+ messages,
315
+ stream: false
316
+ });
317
+ const response = await fetchImpl(url, {
318
+ ...init,
319
+ signal: controller.signal
320
+ });
321
+ if (!response.ok) return fallback();
322
+ const data = parsePlatformChatCompletionData(await response.text());
323
+ const content = data?.choices?.[0]?.message?.content;
324
+ if (typeof content !== "string" || !content.trim()) return fallback();
325
+ const parsed = parseQuickChatIntentResult(content, routeOptions);
326
+ if (!parsed) return fallback();
327
+ const tier = tierForAgentKey(parsed.agentKey) ?? fallbackTier;
328
+ return {
329
+ agentKey: parsed.agentKey,
330
+ tier,
331
+ classified: true,
332
+ confidence: parsed.confidence,
333
+ needsWorkspace: parsed.needsWorkspace,
334
+ skills: parsed.skills
335
+ };
336
+ } catch {
337
+ return fallback();
338
+ } finally {
339
+ clearTimeout(timeoutHandle);
340
+ }
341
+ }
342
+
133
343
  // packages/cli/client/tokenUsage.ts
344
+ var isUsableModelId = (model) => {
345
+ const raw = model?.trim();
346
+ return Boolean(raw) && raw !== "-" && raw !== "auto";
347
+ };
348
+ function resolveAgentContextWindow(opts) {
349
+ if (isUsableModelId(opts.model)) {
350
+ return getModelContextWindow(opts.model);
351
+ }
352
+ const agentKey = opts.agentKey?.trim();
353
+ if (agentKey) {
354
+ const tierModel = resolveCliAutoAgentModel(agentKey);
355
+ if (tierModel) return getModelContextWindow(tierModel);
356
+ if (opts.autoRouteDefault !== false && agentKey === BUILTIN_NOLO_AGENT_KEY) {
357
+ return getModelContextWindow(CLI_AUTO_TIER_MODELS.flash);
358
+ }
359
+ }
360
+ if (opts.agentName?.trim()) {
361
+ return getModelContextWindow(opts.agentName);
362
+ }
363
+ return DEFAULT_CONTEXT_WINDOW;
364
+ }
134
365
  var PROVIDER_LOOKUP_ORDER = [
135
366
  "openrouter",
136
367
  "fireworks",
@@ -166,10 +397,13 @@ function buildTurnTokenUsage(usage, model) {
166
397
  if (!parsed) return void 0;
167
398
  const contextWindow = resolveContextWindow(model);
168
399
  const remaining = contextWindow && parsed.input > 0 ? Math.max(0, contextWindow - parsed.input) : void 0;
400
+ const rawCost = typeof usage?.cost === "number" ? usage.cost : void 0;
401
+ const credits = rawCost !== void 0 && rawCost > 0 ? rawCost * API_REPORTED_COST_MULTIPLIER : void 0;
169
402
  return {
170
403
  ...parsed,
171
404
  ...contextWindow ? { contextWindow } : {},
172
- ...remaining != null ? { remaining } : {}
405
+ ...remaining != null ? { remaining } : {},
406
+ ...credits !== void 0 ? { credits } : {}
173
407
  };
174
408
  }
175
409
  function formatTokenCount(value) {
@@ -242,1151 +476,39 @@ function dimCliText(text, enabled = resolveCliColorEnabled()) {
242
476
  return styleCliText(text, "dim", enabled);
243
477
  }
244
478
 
245
- // packages/cli/tui/theme.ts
246
- var THEME_PALETTES = {
247
- trail: {
248
- light: {
249
- accent: { hex: "2E7DB5", ansiFallback: "\x1B[34m" },
250
- // ocean blue
251
- chrome: { hex: "768594", ansiFallback: "\x1B[90m" },
252
- // slate — two steps below app placeholder A3B0BD (was 8A97A5); rules/tool markers on white need the extra notch
253
- success: { hex: "3F8F5C", ansiFallback: "\x1B[32m" },
254
- // moss green
255
- warning: { hex: "9A6A1F", ansiFallback: "\x1B[33m" },
256
- // deeper amber — B57F2E still read as yellow/washed on white (H2 headers like ## Plan floated); 9A6A1F raises contrast
257
- info: { hex: "4A9FD4", ansiFallback: "\x1B[36m" },
258
- // sky blue
259
- danger: { hex: "C45C4A", ansiFallback: "\x1B[31m" },
260
- // reddish
261
- muted: { hex: "5E6A78", ansiFallback: "\x1B[90m" }
262
- // denser slate — 687584 read too blue in dense prose (inline code/tool labels); 5E6A78 pulls blue down while staying darker than the old value
263
- },
264
- dark: {
265
- accent: { hex: "89B4FA", ansiFallback: "\x1B[34m" },
266
- // blue
267
- chrome: { hex: "6C7086", ansiFallback: "\x1B[90m" },
268
- success: { hex: "A6E3A1", ansiFallback: "\x1B[32m" },
269
- warning: { hex: "F9E2AF", ansiFallback: "\x1B[33m" },
270
- info: { hex: "94E2D5", ansiFallback: "\x1B[36m" },
271
- danger: { hex: "F38BA8", ansiFallback: "\x1B[31m" },
272
- muted: { hex: "9AA3B8", ansiFallback: "\x1B[90m" }
273
- // dimmed from A6ADC8 so secondary text sits a step below body text
274
- }
275
- },
276
- catppuccin: {
277
- light: {
278
- accent: { hex: "0969DA", ansiFallback: "\x1B[34m" },
279
- chrome: { hex: "57606A", ansiFallback: "\x1B[90m" },
280
- success: { hex: "116329", ansiFallback: "\x1B[32m" },
281
- warning: { hex: "9A6700", ansiFallback: "\x1B[33m" },
282
- info: { hex: "0969DA", ansiFallback: "\x1B[36m" },
283
- danger: { hex: "CF222E", ansiFallback: "\x1B[31m" },
284
- muted: { hex: "6E7781", ansiFallback: "\x1B[90m" }
285
- },
286
- dark: {
287
- accent: { hex: "58A6FF", ansiFallback: "\x1B[34m" },
288
- // GitHub sky blue
289
- chrome: { hex: "6E7681", ansiFallback: "\x1B[90m" },
290
- success: { hex: "3FB950", ansiFallback: "\x1B[32m" },
291
- warning: { hex: "D29922", ansiFallback: "\x1B[33m" },
292
- info: { hex: "58A6FF", ansiFallback: "\x1B[36m" },
293
- danger: { hex: "FF7B72", ansiFallback: "\x1B[31m" },
294
- muted: { hex: "B1BAC4", ansiFallback: "\x1B[90m" }
295
- }
296
- },
297
- wave: {
298
- light: {
299
- accent: { hex: "4D699B", ansiFallback: "\x1B[34m" },
300
- // ink blue
301
- chrome: { hex: "9E9B8E", ansiFallback: "\x1B[90m" },
302
- success: { hex: "6F894E", ansiFallback: "\x1B[32m" },
303
- warning: { hex: "836F4A", ansiFallback: "\x1B[33m" },
304
- info: { hex: "4D699B", ansiFallback: "\x1B[36m" },
305
- danger: { hex: "C84053", ansiFallback: "\x1B[31m" },
306
- muted: { hex: "716E61", ansiFallback: "\x1B[90m" }
307
- },
308
- dark: {
309
- accent: { hex: "7E9CD8", ansiFallback: "\x1B[34m" },
310
- // crystalBlue
311
- chrome: { hex: "727169", ansiFallback: "\x1B[90m" },
312
- success: { hex: "98BB6C", ansiFallback: "\x1B[32m" },
313
- warning: { hex: "E6C384", ansiFallback: "\x1B[33m" },
314
- info: { hex: "7FB4CA", ansiFallback: "\x1B[36m" },
315
- danger: { hex: "E82424", ansiFallback: "\x1B[31m" },
316
- muted: { hex: "938AA9", ansiFallback: "\x1B[90m" }
317
- }
318
- },
319
- iris: {
320
- light: {
321
- accent: { hex: "5E6AD2", ansiFallback: "\x1B[34m" },
322
- chrome: { hex: "9E9AB5", ansiFallback: "\x1B[90m" },
323
- success: { hex: "16A34A", ansiFallback: "\x1B[32m" },
324
- warning: { hex: "D97706", ansiFallback: "\x1B[33m" },
325
- info: { hex: "5E6AD2", ansiFallback: "\x1B[36m" },
326
- danger: { hex: "E5484D", ansiFallback: "\x1B[31m" },
327
- muted: { hex: "6E6A8A", ansiFallback: "\x1B[90m" }
328
- },
329
- dark: {
330
- accent: { hex: "8B9CF4", ansiFallback: "\x1B[34m" },
331
- chrome: { hex: "5A5772", ansiFallback: "\x1B[90m" },
332
- success: { hex: "26BD6C", ansiFallback: "\x1B[32m" },
333
- warning: { hex: "F5A623", ansiFallback: "\x1B[33m" },
334
- info: { hex: "8B9CF4", ansiFallback: "\x1B[36m" },
335
- danger: { hex: "EC5B6E", ansiFallback: "\x1B[31m" },
336
- muted: { hex: "938AA9", ansiFallback: "\x1B[90m" }
337
- }
338
- },
339
- rose: {
340
- light: {
341
- accent: { hex: "D14D72", ansiFallback: "\x1B[35m" },
342
- chrome: { hex: "9893A5", ansiFallback: "\x1B[90m" },
343
- success: { hex: "56949F", ansiFallback: "\x1B[32m" },
344
- warning: { hex: "EA9D34", ansiFallback: "\x1B[33m" },
345
- info: { hex: "286983", ansiFallback: "\x1B[36m" },
346
- danger: { hex: "B4637A", ansiFallback: "\x1B[31m" },
347
- muted: { hex: "797593", ansiFallback: "\x1B[90m" }
348
- },
349
- dark: {
350
- accent: { hex: "EB6F92", ansiFallback: "\x1B[35m" },
351
- chrome: { hex: "6E6A86", ansiFallback: "\x1B[90m" },
352
- success: { hex: "9CCFD8", ansiFallback: "\x1B[32m" },
353
- warning: { hex: "F6C177", ansiFallback: "\x1B[33m" },
354
- info: { hex: "C4A7E7", ansiFallback: "\x1B[36m" },
355
- danger: { hex: "EB6F92", ansiFallback: "\x1B[31m" },
356
- muted: { hex: "908CAA", ansiFallback: "\x1B[90m" }
357
- }
358
- },
359
- mono: {
360
- light: {
361
- accent: { hex: "FF9500", ansiFallback: "\x1B[33m" },
362
- chrome: { hex: "9DA6AF", ansiFallback: "\x1B[90m" },
363
- success: { hex: "16A34A", ansiFallback: "\x1B[32m" },
364
- warning: { hex: "D97706", ansiFallback: "\x1B[33m" },
365
- info: { hex: "2563EB", ansiFallback: "\x1B[34m" },
366
- danger: { hex: "DC2626", ansiFallback: "\x1B[31m" },
367
- muted: { hex: "7B8793", ansiFallback: "\x1B[90m" }
368
- },
369
- dark: {
370
- accent: { hex: "FFBF66", ansiFallback: "\x1B[33m" },
371
- chrome: { hex: "7B8793", ansiFallback: "\x1B[90m" },
372
- success: { hex: "4DE800", ansiFallback: "\x1B[32m" },
373
- warning: { hex: "FCD34D", ansiFallback: "\x1B[33m" },
374
- info: { hex: "60A5FA", ansiFallback: "\x1B[34m" },
375
- danger: { hex: "F87171", ansiFallback: "\x1B[31m" },
376
- muted: { hex: "C1C7CD", ansiFallback: "\x1B[90m" }
377
- }
378
- }
379
- };
380
- var activeThemeName = "catppuccin";
381
- function getActiveThemeName() {
382
- return activeThemeName;
383
- }
384
- function setActiveThemeName(name) {
385
- if (THEME_PALETTES[name]) {
386
- activeThemeName = name;
387
- return true;
388
- }
389
- return false;
390
- }
391
- var activeBrightness = null;
392
- function getActiveBrightness() {
393
- return activeBrightness;
394
- }
395
- function setActiveBrightness(brightness) {
396
- activeBrightness = brightness;
397
- }
398
- var activeDensity = "spacious";
399
- function getActiveDensity() {
400
- return activeDensity;
401
- }
402
- function setActiveDensity(density) {
403
- activeDensity = density;
404
- }
405
- function supportsTruecolor(env = process.env) {
406
- return /truecolor|24bit/i.test(env.COLORTERM ?? "");
479
+ // packages/cli/tui/i18n.ts
480
+ var ZH_PATTERNS = /^zh/i;
481
+ function parseCliLocale(raw) {
482
+ const normalized = raw?.trim().toLowerCase();
483
+ if (!normalized) return null;
484
+ if (ZH_PATTERNS.test(normalized)) return "zh";
485
+ if (normalized === "en" || normalized.startsWith("en")) return "en";
486
+ return null;
407
487
  }
408
- function resolveTuiBrightness(env = process.env) {
409
- const explicit = (env.NOLO_TUI_THEME ?? "").trim().toLowerCase();
410
- if (explicit === "light") return "light";
411
- if (explicit === "dark") return "dark";
412
- if (activeBrightness) return activeBrightness;
413
- const colorfgbg = env.COLORFGBG ?? "";
414
- if (colorfgbg) {
415
- const parts = colorfgbg.split(";");
416
- const bg = parts.length > 1 ? Number.parseInt(parts[1] ?? "", 10) : NaN;
417
- if (!Number.isNaN(bg) && bg >= 0 && bg <= 6) return "dark";
418
- if (!Number.isNaN(bg) && bg >= 7 && bg <= 15) return "light";
488
+ function detectLocaleFromEnv(env) {
489
+ const explicit = parseCliLocale(env.NOLO_LANG);
490
+ if (explicit) return explicit;
491
+ const candidates = [env.LC_ALL, env.LC_CTYPE, env.LANG].filter(Boolean);
492
+ for (const candidate of candidates) {
493
+ if (ZH_PATTERNS.test(candidate)) return "zh";
494
+ if (candidate && !candidate.startsWith("C.") && !candidate.startsWith("POSIX")) {
495
+ return "en";
496
+ }
419
497
  }
420
- return "dark";
421
- }
422
- function hexToSgr(hex) {
423
- const r = Number.parseInt(hex.slice(0, 2), 16);
424
- const g = Number.parseInt(hex.slice(2, 4), 16);
425
- const b = Number.parseInt(hex.slice(4, 6), 16);
426
- return `\x1B[38;2;${r};${g};${b}m`;
427
- }
428
- var SURFACE_HEX = {
429
- trail: { light: "E4E8EC", dark: "313244" },
430
- catppuccin: { light: "E8ECF0", dark: "161B22" },
431
- wave: { light: "E5E1D6", dark: "2A2A37" },
432
- iris: { light: "E9E8F2", dark: "2A2740" },
433
- rose: { light: "F2EAE4", dark: "26233A" },
434
- mono: { light: "E8EAED", dark: "2A2A2A" }
435
- };
436
- var TERMINAL_BASE = {
437
- dark: "1E1E2E",
438
- light: "FFFFFF"
439
- };
440
- function readHexChannels(hex) {
441
- if (!/^[0-9A-F]{6}$/.test(hex)) return null;
442
- return [
443
- Number.parseInt(hex.slice(0, 2), 16),
444
- Number.parseInt(hex.slice(2, 4), 16),
445
- Number.parseInt(hex.slice(4, 6), 16)
446
- ];
447
- }
448
- function hexToBgSgr(hex) {
449
- const channels = readHexChannels(hex.replace(/^#/, "").toUpperCase());
450
- if (!channels) return "";
451
- const [r, g, b] = channels;
452
- return `\x1B[48;2;${r};${g};${b}m`;
498
+ return null;
453
499
  }
454
- function blendHex(fg, base, weight) {
455
- const w = Math.min(1, Math.max(0, weight));
456
- const normFg = fg.replace(/^#/, "").toUpperCase();
457
- const normBase = base.replace(/^#/, "").toUpperCase();
458
- if (w === 0) return normBase;
459
- if (w === 1) return normFg;
460
- if (!readHexChannels(normFg) || !readHexChannels(normBase)) return normBase;
461
- const fgR = Number.parseInt(normFg.slice(0, 2), 16);
462
- const fgG = Number.parseInt(normFg.slice(2, 4), 16);
463
- const fgB = Number.parseInt(normFg.slice(4, 6), 16);
464
- const baseR = Number.parseInt(normBase.slice(0, 2), 16);
465
- const baseG = Number.parseInt(normBase.slice(2, 4), 16);
466
- const baseB = Number.parseInt(normBase.slice(4, 6), 16);
467
- const r = Math.round(baseR + (fgR - baseR) * w).toString(16).padStart(2, "0").toUpperCase();
468
- const g = Math.round(baseG + (fgG - baseG) * w).toString(16).padStart(2, "0").toUpperCase();
469
- const b = Math.round(baseB + (fgB - baseB) * w).toString(16).padStart(2, "0").toUpperCase();
470
- return `${r}${g}${b}`;
500
+ function detectLocale(env) {
501
+ return detectLocaleFromEnv(env) ?? "zh";
471
502
  }
472
- function diffLineSequences(env = process.env, brightness = resolveTuiBrightness(env)) {
473
- if (!supportsTruecolor(env)) return null;
474
- const palette = THEME_PALETTES[activeThemeName] ?? THEME_PALETTES.trail;
475
- const successHex = palette[brightness].success.hex;
476
- const dangerHex = palette[brightness].danger.hex;
477
- const weight = brightness === "dark" ? 0.14 : 0.1;
478
- const baseHex = TERMINAL_BASE[brightness];
479
- const addedBgHex = blendHex(successHex, baseHex, weight);
480
- const removedBgHex = blendHex(dangerHex, baseHex, weight);
481
- return {
482
- added: {
483
- fg: hexToSgr(successHex),
484
- bg: hexToBgSgr(addedBgHex)
485
- },
486
- removed: {
487
- fg: hexToSgr(dangerHex),
488
- bg: hexToBgSgr(removedBgHex)
489
- }
490
- };
503
+ var currentLocale = detectLocale(process.env);
504
+ function getCliLocale() {
505
+ return currentLocale;
491
506
  }
492
- function surfaceBackgroundSequence(env = process.env, brightness = resolveTuiBrightness(env)) {
493
- if (!supportsTruecolor(env)) return "";
494
- return hexToBgSgr((SURFACE_HEX[activeThemeName] ?? SURFACE_HEX.trail)[brightness]);
507
+ function setCliLocale(locale) {
508
+ currentLocale = locale;
495
509
  }
496
- function themeColorSequence(token, env = process.env, brightness = resolveTuiBrightness(env)) {
497
- const palette = THEME_PALETTES[activeThemeName] ?? THEME_PALETTES.trail;
498
- const entry = palette[brightness][token];
499
- return supportsTruecolor(env) ? hexToSgr(entry.hex) : entry.ansiFallback;
500
- }
501
- function themeText(text, token, colorEnabled = resolveCliColorEnabled(), env = process.env) {
502
- if (!colorEnabled) return text;
503
- return `${themeColorSequence(token, env)}${text}\x1B[39m`;
504
- }
505
- function renderDiffCodeBlock(code, env = process.env) {
506
- const border = themeText("\u2502", "chrome", true, env);
507
- const addedColor = themeColorSequence("success", env);
508
- const deletedColor = themeColorSequence("danger", env);
509
- const hunkColor = themeColorSequence("info", env);
510
- const contextColor = themeColorSequence("chrome", env);
511
- const reset = "\x1B[0m";
512
- const lines = code.split("\n").map((line) => {
513
- let color = contextColor;
514
- if (line.startsWith("@@")) {
515
- color = hunkColor;
516
- } else if (line.startsWith("+") && !line.startsWith("+++")) {
517
- color = addedColor;
518
- } else if (line.startsWith("-") && !line.startsWith("---")) {
519
- color = deletedColor;
520
- }
521
- return ` ${border} ${color}${line}${reset}`;
522
- });
523
- return ` ${themeText("\u250C\u2500\u2500\u2500", "chrome", true, env)}
524
- ${lines.join("\n")}
525
- ${themeText("\u2514\u2500\u2500\u2500", "chrome", true, env)}`;
526
- }
527
- function highlightMarkdown(text, colorEnabled = resolveCliColorEnabled(), env = process.env) {
528
- if (!colorEnabled) return text;
529
- let result = text;
530
- result = result.replace(/```([a-zA-Z-]*)\n([\s\S]*?)\n```/g, (match, lang, code) => {
531
- if (lang.toLowerCase() === "diff") {
532
- return renderDiffCodeBlock(code, env);
533
- }
534
- const infoColor = themeColorSequence("info", env);
535
- const reset2 = "\x1B[39m\x1B[22m";
536
- const border = themeText("\u2502", "chrome", true, env);
537
- const formattedCode = code.split("\n").map((line) => ` ${border} ${infoColor}${line}${reset2}`).join("\n");
538
- return ` ${themeText("\u250C\u2500\u2500\u2500", "chrome", true, env)}
539
- ${formattedCode}
540
- ${themeText("\u2514\u2500\u2500\u2500", "chrome", true, env)}`;
541
- });
542
- result = result.replace(/^(#{1,3})\s+(.+)$/gm, (match, hashes, title) => {
543
- const level = hashes.length;
544
- const reset2 = "\x1B[0m";
545
- if (level === 1) {
546
- return `\x1B[1m\x1B[4m${themeColorSequence("accent", env)}${title}${reset2}`;
547
- }
548
- if (level === 2) {
549
- return `\x1B[1m${themeColorSequence("warning", env)}${title}${reset2}`;
550
- }
551
- return `\x1B[1m${themeColorSequence("info", env)}${title}${reset2}`;
552
- });
553
- result = result.replace(
554
- /^(进入 nolo-plan[^\n]*)$/gm,
555
- (match, content) => `${themeColorSequence("chrome", env)}\x1B[2m${content}\x1B[0m`
556
- );
557
- result = result.replace(/^>\s?(.*)$/gm, (match, content) => {
558
- const border = themeColorSequence("chrome", env);
559
- return `${border}\u2502\x1B[39m \x1B[2m${content}\x1B[22m`;
560
- });
561
- result = result.replace(/\*\*([\s\S]*?)\*\*/g, "\x1B[1m$1\x1B[22m");
562
- const mutedColor = themeColorSequence("muted", env);
563
- const reset = "\x1B[39m";
564
- result = result.replace(/`([^`\n]+)`/g, `${mutedColor}$1${reset}`);
565
- result = result.replace(/(?<!\*)\*([^*\n]+?)\*(?!\*)/g, `\x1B[2m$1\x1B[22m`);
566
- result = result.replace(/~~([^~\n]+?)~~/g, `\x1B[2m\x1B[9m$1\x1B[29m\x1B[22m`);
567
- return result;
568
- }
569
-
570
- // packages/cli/client/assistantOutput.ts
571
- var STYLE = {
572
- reset: "\x1B[0m",
573
- bold: "\x1B[1m",
574
- dim: "\x1B[2m"
575
- };
576
- function colorSeq(token, brightness) {
577
- return themeColorSequence(token, process.env, brightness);
578
- }
579
- function splitTableCells(line) {
580
- const trimmed = line.trim();
581
- if (!trimmed.includes("|")) return [];
582
- const core = trimmed.replace(/^\|/, "").replace(/\|$/, "");
583
- return core.split("|").map((cell) => cell.trim()).filter(Boolean);
584
- }
585
- function isTableSeparator(line) {
586
- const cells = splitTableCells(line);
587
- return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/.test(cell));
588
- }
589
- function isTableRow(line) {
590
- const cells = splitTableCells(line);
591
- return cells.length >= 2 && cells.some((cell) => cell.length > 0);
592
- }
593
- function isPipeWrappedTableRow(line) {
594
- const trimmed = line.trim();
595
- return trimmed.startsWith("|") && trimmed.endsWith("|") && isTableRow(line);
596
- }
597
- function isCodeFenceLine(line) {
598
- return /^\s*```/.test(line);
599
- }
600
- function readFenceLanguage(line) {
601
- const m = line.match(/^\s*```\s*([a-zA-Z0-9+#_-]*)/);
602
- return m ? (m[1] ?? "").toLowerCase() : "";
603
- }
604
- function normalizeCodeLang(raw) {
605
- switch (raw) {
606
- case "js":
607
- case "jsx":
608
- case "ts":
609
- case "tsx":
610
- case "javascript":
611
- case "typescript":
612
- return "js";
613
- case "py":
614
- case "python":
615
- return "py";
616
- case "sh":
617
- case "bash":
618
- case "zsh":
619
- case "shell":
620
- case "console":
621
- return "sh";
622
- case "json":
623
- return "json";
624
- case "diff":
625
- return "diff";
626
- default:
627
- return "unknown";
628
- }
629
- }
630
- var KEYWORDS = {
631
- js: /* @__PURE__ */ new Set([
632
- "const",
633
- "let",
634
- "var",
635
- "function",
636
- "return",
637
- "if",
638
- "else",
639
- "for",
640
- "while",
641
- "class",
642
- "new",
643
- "await",
644
- "async",
645
- "import",
646
- "export",
647
- "from",
648
- "type",
649
- "interface",
650
- "extends",
651
- "implements",
652
- "null",
653
- "undefined",
654
- "true",
655
- "false"
656
- ]),
657
- py: /* @__PURE__ */ new Set([
658
- "def",
659
- "class",
660
- "return",
661
- "if",
662
- "elif",
663
- "else",
664
- "for",
665
- "while",
666
- "import",
667
- "from",
668
- "as",
669
- "with",
670
- "try",
671
- "except",
672
- "finally",
673
- "lambda",
674
- "None",
675
- "True",
676
- "False",
677
- "self"
678
- ]),
679
- sh: /* @__PURE__ */ new Set([
680
- "if",
681
- "then",
682
- "else",
683
- "fi",
684
- "for",
685
- "do",
686
- "done",
687
- "while",
688
- "case",
689
- "esac",
690
- "function",
691
- "export",
692
- "local",
693
- "return",
694
- "source",
695
- "echo",
696
- "cd"
697
- ]),
698
- json: /* @__PURE__ */ new Set(["true", "false", "null"]),
699
- diff: /* @__PURE__ */ new Set([])
700
- };
701
- function scanStringCommentRegions(line, lang) {
702
- const regions = [];
703
- const n = line.length;
704
- let i = 0;
705
- const commentMarkers = lang === "py" || lang === "sh" ? ["#"] : ["//"];
706
- while (i < n) {
707
- const ch = line[i];
708
- if (ch === "'" || ch === '"' || lang === "js" && ch === "`") {
709
- const quote = ch;
710
- const start = i;
711
- i += 1;
712
- while (i < n) {
713
- if (line[i] === "\\") {
714
- i += 2;
715
- continue;
716
- }
717
- if (line[i] === quote) {
718
- i += 1;
719
- break;
720
- }
721
- i += 1;
722
- }
723
- regions.push({ start, end: i, kind: "string" });
724
- continue;
725
- }
726
- let matched = false;
727
- for (const marker of commentMarkers) {
728
- if (line.startsWith(marker, i)) {
729
- regions.push({ start: i, end: n, kind: "comment" });
730
- i = n;
731
- matched = true;
732
- break;
733
- }
734
- }
735
- if (matched) continue;
736
- i += 1;
737
- }
738
- return regions;
739
- }
740
- function highlightCodeLine(line, lang, brightness) {
741
- const info = colorSeq("info", brightness);
742
- if (lang === "unknown") {
743
- return `${info}${line}${STYLE.reset}`;
744
- }
745
- if (lang === "diff") {
746
- const added = colorSeq("success", brightness);
747
- const deleted = colorSeq("danger", brightness);
748
- const hunk = colorSeq("info", brightness);
749
- const context = colorSeq("chrome", brightness);
750
- const reset2 = STYLE.reset;
751
- let color = context;
752
- if (line.startsWith("@@")) {
753
- color = hunk;
754
- } else if (line.startsWith("+") && !line.startsWith("+++")) {
755
- color = added;
756
- } else if (line.startsWith("-") && !line.startsWith("---")) {
757
- color = deleted;
758
- }
759
- return `${color}${line}${reset2}`;
760
- }
761
- const regions = scanStringCommentRegions(line, lang);
762
- const keywords = KEYWORDS[lang];
763
- const accent = colorSeq("accent", brightness);
764
- const success = colorSeq("success", brightness);
765
- const warning = colorSeq("warning", brightness);
766
- const chrome = colorSeq("chrome", brightness);
767
- const dim = STYLE.dim;
768
- const reset = STYLE.reset;
769
- const out = [];
770
- let cursor = 0;
771
- for (const region of regions) {
772
- if (region.start > cursor) {
773
- out.push(emitPlainGap(line.slice(cursor, region.start), keywords, info, accent, warning, reset));
774
- }
775
- const text = line.slice(region.start, region.end);
776
- if (region.kind === "string") {
777
- out.push(`${success}${text}${reset}`);
778
- } else {
779
- out.push(`${chrome}${dim}${text}${reset}`);
780
- }
781
- cursor = region.end;
782
- }
783
- if (cursor < line.length) {
784
- out.push(emitPlainGap(line.slice(cursor), keywords, info, accent, warning, reset));
785
- }
786
- return out.join("");
787
- }
788
- function emitPlainGap(text, keywords, info, accent, warning, reset) {
789
- const parts = [];
790
- const re = /([A-Za-z_]\w*)|(\d+(?:\.\d+)?)|([\s\S])/g;
791
- let last = 0;
792
- let m;
793
- let started = false;
794
- while ((m = re.exec(text)) !== null) {
795
- if (!started) {
796
- started = true;
797
- }
798
- if (m.index > last) {
799
- parts.push(`${info}${text.slice(last, m.index)}`);
800
- }
801
- if (m[1] !== void 0) {
802
- const word = m[1];
803
- if (keywords.has(word)) {
804
- parts.push(`${accent}${word}${reset}`);
805
- } else {
806
- parts.push(`${info}${word}`);
807
- }
808
- } else if (m[2] !== void 0) {
809
- parts.push(`${warning}${m[2]}${reset}`);
810
- } else {
811
- parts.push(`${info}${m[3]}`);
812
- }
813
- last = re.lastIndex;
814
- }
815
- if (last < text.length) {
816
- parts.push(`${info}${text.slice(last)}`);
817
- }
818
- return parts.join("");
819
- }
820
- function tableRowToBullet(line) {
821
- const row = splitTableCells(line);
822
- const label = row[0] ?? "";
823
- const detail = row.slice(1).join(" \u2014 ").trim();
824
- return detail ? ` \u2022 ${label} \u2014 ${detail}` : ` \u2022 ${label}`;
825
- }
826
- var UNORDERED_LIST_RE = /^(\s*)([-*+])\s+(.+)$/;
827
- var ORDERED_LIST_RE = /^(\s*)(\d+)\.\s+(.+)$/;
828
- var TASK_LIST_RE = /^(\s*)([-*+])\s+\[([ xX])\]\s+(.+)$/;
829
- function normalizeListLine(line) {
830
- const task = line.match(TASK_LIST_RE);
831
- if (task) {
832
- const checked = task[3] === "x" || task[3] === "X";
833
- return `${task[1]}${checked ? "\u2611" : "\u2610"} ${task[4]}`;
834
- }
835
- const unordered = line.match(UNORDERED_LIST_RE);
836
- if (unordered) {
837
- return `${unordered[1]}\u2022 ${unordered[3]}`;
838
- }
839
- const ordered = line.match(ORDERED_LIST_RE);
840
- if (ordered) {
841
- return `${ordered[1]}${ordered[2]}. ${ordered[3]}`;
842
- }
843
- return line;
844
- }
845
- function convertMarkdownTablesForTerminal(text) {
846
- const lines = text.split("\n");
847
- const out = [];
848
- let inFence = false;
849
- for (let index = 0; index < lines.length; index += 1) {
850
- const line = lines[index] ?? "";
851
- if (isCodeFenceLine(line)) {
852
- inFence = !inFence;
853
- out.push(line);
854
- continue;
855
- }
856
- if (inFence) {
857
- out.push(line);
858
- continue;
859
- }
860
- const next = lines[index + 1] ?? "";
861
- if (isTableRow(line) && isTableSeparator(next)) {
862
- index += 1;
863
- while (index + 1 < lines.length && isTableRow(lines[index + 1] ?? "") && !isTableSeparator(lines[index + 1] ?? "")) {
864
- index += 1;
865
- out.push(tableRowToBullet(lines[index] ?? ""));
866
- }
867
- if (out.length > 0 && out[out.length - 1] !== "") out.push("");
868
- continue;
869
- }
870
- if (isPipeWrappedTableRow(line)) {
871
- if (!isTableSeparator(line)) out.push(tableRowToBullet(line));
872
- continue;
873
- }
874
- out.push(normalizeListLine(line));
875
- }
876
- return out.join("\n");
877
- }
878
- function polishAssistantStructure(text, options = {}) {
879
- const converted = convertMarkdownTablesForTerminal(text).replace(/\r\n/g, "\n").replace(/\x00/g, "");
880
- const lines = converted.split("\n");
881
- let inFence = false;
882
- const maskedLines = lines.map((line, index) => {
883
- if (isCodeFenceLine(line)) {
884
- inFence = !inFence;
885
- return line;
886
- }
887
- if (inFence) {
888
- return `\0F${index}\0`;
889
- }
890
- return line;
891
- });
892
- const afterHeading = maskedLines.join("\n").replace(/([^\n])\n(#{1,3} )/g, "$1\n\n$2").replace(/^(#{1,3} .+)\n(?!\n)/gm, "$1\n\n").replace(/\n{4,}/g, "\n\n\n");
893
- const LIST_LIKE = /^\s*(?:•|☐|☑|\d+\.)\s|^\s*[\u2460-\u2473]/;
894
- const headingLines = afterHeading.split("\n");
895
- const breathed = [];
896
- for (let i = 0; i < headingLines.length; i++) {
897
- breathed.push(headingLines[i]);
898
- const cur = headingLines[i];
899
- const next = headingLines[i + 1];
900
- if (cur === "" || next === void 0 || next === "") continue;
901
- if (LIST_LIKE.test(cur) === LIST_LIKE.test(next)) continue;
902
- breathed.push("");
903
- }
904
- const polishedMasked = breathed.join("\n");
905
- const polished = polishedMasked.replace(
906
- /\x00F(\d+)\x00/g,
907
- (_, id) => lines[Number(id)] ?? ""
908
- );
909
- return options.trimEdges === false ? polished : polished.trim();
910
- }
911
- function renderMarkdownLink(match, text, url) {
912
- const visible = `${text} (${url})`;
913
- return `\x1B]8;;${url}\x1B\\${visible}\x1B]8;;\x1B\\`;
914
- }
915
- var MARKDOWN_LINK_RE = /\[([^\]]+)\]\(([^)\s]+)\)/g;
916
- function styleInlineMarkdown(line, brightness) {
917
- const inlineCode = colorSeq("muted", brightness);
918
- const reset = STYLE.reset;
919
- const bold = STYLE.bold;
920
- const dim = STYLE.dim;
921
- return line.replace(MARKDOWN_LINK_RE, (m, t2, u) => renderMarkdownLink(m, t2, u)).replace(/`([^`]+)`/g, `${inlineCode}$1${reset}`).replace(/\*\*(.+?)\*\*/g, `${bold}$1${reset}`).replace(/(?<!\*)\*([^*]+?)\*(?!\*)/g, `${dim}$1${reset}`).replace(/~~([^~]+?)~~/g, `${dim}\x1B[9m$1\x1B[29m${reset}`);
922
- }
923
- function styleRichMarkdownLine(line, brightness) {
924
- const heading = line.match(/^(#{1,3})\s+(.+)$/);
925
- if (heading) {
926
- const level = heading[1].length;
927
- const title = heading[2];
928
- if (level === 1) {
929
- return `${STYLE.bold}\x1B[4m${colorSeq("accent", brightness)}${title}${STYLE.reset}`;
930
- }
931
- if (level === 2) {
932
- return `${STYLE.bold}${colorSeq("warning", brightness)}${title}${STYLE.reset}`;
933
- }
934
- return `${STYLE.bold}${colorSeq("info", brightness)}${title}${STYLE.reset}`;
935
- }
936
- if (line.startsWith("\u8FDB\u5165 nolo-plan")) {
937
- return `${colorSeq("chrome", brightness)}${STYLE.dim}${line}${STYLE.reset}`;
938
- }
939
- const blockquote = line.match(/^>\s?(.*)$/);
940
- if (blockquote) {
941
- const border = colorSeq("chrome", brightness);
942
- const content = blockquote[1];
943
- return `${border}\u2502${STYLE.reset} ${STYLE.dim}${content}${STYLE.reset}`;
944
- }
945
- if (/^---+$/.test(line.trim())) {
946
- return `${colorSeq("chrome", brightness)}${"\u2500".repeat(Math.min(line.trim().length, 40))}${STYLE.reset}`;
947
- }
948
- const bullet = line.match(/^(\s*)(•)\s(.+)$/);
949
- if (bullet) {
950
- const styled = styleInlineMarkdown(bullet[3], brightness);
951
- return `${bullet[1]}${colorSeq("accent", brightness)}\u2022${STYLE.reset} ${styled}`;
952
- }
953
- const ordered = line.match(/^(\s*)(\d+\.)\s(.+)$/);
954
- if (ordered) {
955
- const styled = styleInlineMarkdown(ordered[3], brightness);
956
- return `${ordered[1]}${colorSeq("accent", brightness)}${ordered[2]}${STYLE.reset} ${styled}`;
957
- }
958
- const task = line.match(/^(\s*)(☐|☑)\s(.+)$/);
959
- if (task) {
960
- const styled = styleInlineMarkdown(task[3], brightness);
961
- return `${task[1]}${colorSeq("accent", brightness)}${task[2]}${STYLE.reset} ${styled}`;
962
- }
963
- return styleInlineMarkdown(line, brightness);
964
- }
965
- function formatAssistantDisplay(text, options = {}) {
966
- const brightness = resolveTuiBrightness();
967
- const polished = polishAssistantStructure(text, options);
968
- let inFence = false;
969
- let fenceLang = "unknown";
970
- return polished.split("\n").map((line) => {
971
- if (isCodeFenceLine(line)) {
972
- if (inFence) {
973
- fenceLang = "unknown";
974
- } else {
975
- fenceLang = normalizeCodeLang(readFenceLanguage(line));
976
- }
977
- inFence = !inFence;
978
- return `${STYLE.dim}${line}${STYLE.reset}`;
979
- }
980
- if (inFence) {
981
- return highlightCodeLine(line, fenceLang, brightness);
982
- }
983
- return styleRichMarkdownLine(line, brightness);
984
- }).join("\n");
985
- }
986
- function emitFormattedAssistantBlock(write, text, trailingNewline = false) {
987
- if (!text) return;
988
- write(formatAssistantDisplay(text, { trimEdges: false }));
989
- if (trailingNewline) write("\n");
990
- }
991
- function classifyStreamLine(line) {
992
- if (line === "") return "blank";
993
- if (TASK_LIST_RE.test(line) || UNORDERED_LIST_RE.test(line) || ORDERED_LIST_RE.test(line) || /^\s*(?:•|☐|☑)\s/.test(line) || /^\s*[\u2460-\u2473]/.test(line)) {
994
- return "list";
995
- }
996
- return "prose";
997
- }
998
- function needsListProseBreath(prev, next) {
999
- if (prev === null || prev === "blank" || next === "blank") return false;
1000
- const prevList = prev === "list";
1001
- const nextList = next === "list";
1002
- return prevList !== nextList;
1003
- }
1004
- function createRenderAwareStreamWriter(args) {
1005
- const brightness = resolveTuiBrightness();
1006
- let buffer = "";
1007
- let inFence = false;
1008
- let fenceLang = "unknown";
1009
- let lastKind = null;
1010
- const emitBreathIfNeeded = (nextKind) => {
1011
- if (needsListProseBreath(lastKind, nextKind)) args.write("\n");
1012
- };
1013
- const flushCompleteBlocks = () => {
1014
- while (buffer.includes("\n")) {
1015
- const lines = buffer.split("\n");
1016
- if (lines.length < 2) break;
1017
- const firstLine = lines[0] ?? "";
1018
- if (isCodeFenceLine(firstLine)) {
1019
- if (inFence) {
1020
- fenceLang = "unknown";
1021
- } else {
1022
- fenceLang = normalizeCodeLang(readFenceLanguage(firstLine));
1023
- }
1024
- inFence = !inFence;
1025
- emitBreathIfNeeded("other");
1026
- args.write(`${STYLE.dim}${firstLine}${STYLE.reset}
1027
- `);
1028
- lastKind = "other";
1029
- buffer = lines.slice(1).join("\n");
1030
- continue;
1031
- }
1032
- if (inFence) {
1033
- args.write(`${highlightCodeLine(firstLine, fenceLang, brightness)}
1034
- `);
1035
- lastKind = "other";
1036
- buffer = lines.slice(1).join("\n");
1037
- continue;
1038
- }
1039
- if (isTableRow(firstLine)) {
1040
- const nextLineComplete = lines.length > 2;
1041
- if (!nextLineComplete) break;
1042
- if (isTableSeparator(lines[1] ?? "")) {
1043
- let end = 2;
1044
- while (end < lines.length && isTableRow(lines[end] ?? "") && !isTableSeparator(lines[end] ?? "")) {
1045
- end += 1;
1046
- }
1047
- const tableComplete = end < lines.length - 1;
1048
- if (!tableComplete) break;
1049
- emitBreathIfNeeded("list");
1050
- emitFormattedAssistantBlock(
1051
- args.write,
1052
- lines.slice(0, end).join("\n"),
1053
- true
1054
- );
1055
- lastKind = "list";
1056
- buffer = lines.slice(end).join("\n");
1057
- continue;
1058
- }
1059
- }
1060
- const kind = classifyStreamLine(firstLine);
1061
- emitBreathIfNeeded(kind);
1062
- emitFormattedAssistantBlock(args.write, firstLine, true);
1063
- lastKind = kind;
1064
- buffer = lines.slice(1).join("\n");
1065
- }
1066
- };
1067
- return {
1068
- push(chunk) {
1069
- if (!chunk) return;
1070
- buffer += chunk;
1071
- flushCompleteBlocks();
1072
- },
1073
- flush() {
1074
- if (!buffer) return;
1075
- if (inFence) {
1076
- args.write(buffer);
1077
- } else {
1078
- emitFormattedAssistantBlock(args.write, buffer);
1079
- }
1080
- buffer = "";
1081
- }
1082
- };
1083
- }
1084
-
1085
- // packages/cli/client/thinkingOutput.ts
1086
- var THINK_OPEN = /<think>/i;
1087
- var THINK_CLOSE = /<\/think>/i;
1088
- var COLLAPSED_MARKER = /^\s*▸ 思考已折叠\s*$/;
1089
- function normalizeThinkingDisplayMode(raw, fallback = "hide") {
1090
- const normalized = asTrimmedLowercaseString(raw);
1091
- if (normalized === "hide" || normalized === "off" || normalized === "false" || normalized === "0") {
1092
- return "hide";
1093
- }
1094
- if (normalized === "marker" || normalized === "collapsed" || normalized === "fold") {
1095
- return "marker";
1096
- }
1097
- if (normalized === "show" || normalized === "on" || normalized === "true" || normalized === "1") {
1098
- return "show";
1099
- }
1100
- return fallback;
1101
- }
1102
- function resolveThinkingDisplayMode(env = process.env) {
1103
- return normalizeThinkingDisplayMode(env.NOLO_CLI_THINKING ?? env.NOLO_THINKING, "hide");
1104
- }
1105
- function collapseThinkingBlocks(text, mode = "hide") {
1106
- if (mode === "show") return text;
1107
- const replacement = mode === "marker" ? "\u25B8 \u601D\u8003\u5DF2\u6298\u53E0\n" : "";
1108
- return text.replace(/<think>[\s\S]*?<\/think>\s*/gi, replacement).replace(/\n{3,}/g, "\n\n").trim();
1109
- }
1110
- function stripCollapsedThinkingMarkers(text) {
1111
- return text.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n").replace(/\n{2,}/g, "\n").trim();
1112
- }
1113
- function formatAssistantTextForCli(text, mode = "hide") {
1114
- const collapsed = collapseThinkingBlocks(text, mode);
1115
- return mode === "hide" ? stripCollapsedThinkingMarkers(collapsed) : collapsed;
1116
- }
1117
- function createThinkingEventSink(arg1, arg2) {
1118
- let mode = "hide";
1119
- let write = () => {
1120
- };
1121
- if (typeof arg1 === "function") {
1122
- write = arg1;
1123
- if (typeof arg2 === "string") {
1124
- mode = arg2;
1125
- }
1126
- } else {
1127
- mode = arg1;
1128
- if (typeof arg2 === "function") {
1129
- write = arg2;
1130
- }
1131
- }
1132
- if (mode === "hide") {
1133
- return {
1134
- push(_chunk) {
1135
- }
1136
- };
1137
- }
1138
- if (mode === "show") {
1139
- return {
1140
- push(chunk) {
1141
- if (chunk) write(chunk);
1142
- }
1143
- };
1144
- }
1145
- let markerEmitted = false;
1146
- return {
1147
- push(chunk) {
1148
- if (!chunk || markerEmitted) return;
1149
- write("\n\u25B8 \u601D\u8003\u5DF2\u6298\u53E0\n");
1150
- markerEmitted = true;
1151
- }
1152
- };
1153
- }
1154
- function createThinkingAwareStreamFilter(write, mode = "hide") {
1155
- if (mode === "show") {
1156
- return {
1157
- push(chunk) {
1158
- write(chunk);
1159
- },
1160
- flush() {
1161
- }
1162
- };
1163
- }
1164
- let pending = "";
1165
- let insideThink = false;
1166
- let markerEmitted = false;
1167
- const emit = (chunk) => {
1168
- if (chunk) write(chunk);
1169
- };
1170
- const emitMarker = () => {
1171
- if (mode !== "marker" || markerEmitted) return;
1172
- emit("\n\u25B8 \u601D\u8003\u5DF2\u6298\u53E0\n");
1173
- markerEmitted = true;
1174
- };
1175
- const consume = (input) => {
1176
- pending += input;
1177
- while (pending.length > 0) {
1178
- if (insideThink) {
1179
- const closeMatch = pending.match(THINK_CLOSE);
1180
- if (!closeMatch || closeMatch.index == null) {
1181
- emitMarker();
1182
- pending = "";
1183
- return;
1184
- }
1185
- emitMarker();
1186
- pending = pending.slice(closeMatch.index + closeMatch[0].length);
1187
- insideThink = false;
1188
- markerEmitted = false;
1189
- continue;
1190
- }
1191
- const openMatch = pending.match(THINK_OPEN);
1192
- if (!openMatch || openMatch.index == null) {
1193
- const nextMarker = pending.indexOf("<");
1194
- if (nextMarker === -1) {
1195
- const cleaned = mode === "hide" ? pending.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : pending;
1196
- emit(cleaned);
1197
- pending = "";
1198
- return;
1199
- }
1200
- if (nextMarker > 0) {
1201
- const head = pending.slice(0, nextMarker);
1202
- const cleaned = mode === "hide" ? head.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : head;
1203
- emit(cleaned);
1204
- pending = pending.slice(nextMarker);
1205
- continue;
1206
- }
1207
- if (pending.length < 7) return;
1208
- emit(pending[0]);
1209
- pending = pending.slice(1);
1210
- continue;
1211
- }
1212
- if (openMatch.index > 0) {
1213
- const head = pending.slice(0, openMatch.index);
1214
- const cleaned = mode === "hide" ? head.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : head;
1215
- emit(cleaned);
1216
- pending = pending.slice(openMatch.index);
1217
- continue;
1218
- }
1219
- pending = pending.slice(openMatch[0].length);
1220
- insideThink = true;
1221
- markerEmitted = false;
1222
- }
1223
- };
1224
- return {
1225
- push(chunk) {
1226
- consume(chunk);
1227
- },
1228
- flush() {
1229
- if (insideThink) emitMarker();
1230
- if (pending) {
1231
- const cleaned = mode === "hide" ? pending.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : pending;
1232
- emit(cleaned);
1233
- }
1234
- pending = "";
1235
- insideThink = false;
1236
- markerEmitted = false;
1237
- }
1238
- };
1239
- }
1240
-
1241
- // packages/cli/client/formatReadPathTree.ts
1242
- import { homedir as osHomedir } from "node:os";
1243
- function clipPathAware(value, max = 72) {
1244
- const compact = compactWhitespace(value);
1245
- if (compact.length <= max) return compact;
1246
- if (compact.includes(" ") || !compact.includes("/") && !compact.includes("\\")) {
1247
- return clipCompactText(value, max, "\u2026");
1248
- }
1249
- const segments = compact.split(/[/\\]/);
1250
- const filename = segments[segments.length - 1];
1251
- if (filename.length >= max) {
1252
- return clipCompactText(value, max, "\u2026");
1253
- }
1254
- const ELISION = "/\u2026/";
1255
- let prefix = "";
1256
- for (let i = 0; i < segments.length - 1; i += 1) {
1257
- const candidate = segments.slice(0, i + 1).join("/");
1258
- if (candidate.length + ELISION.length + filename.length > max) break;
1259
- prefix = candidate;
1260
- }
1261
- if (prefix) return `${prefix}${ELISION}${filename}`;
1262
- const filenameOnly = `\u2026/${filename}`;
1263
- if (filenameOnly.length <= max) return filenameOnly;
1264
- return clipCompactText(value, max, "\u2026");
1265
- }
1266
- function formatHomePath(path, customHomedir) {
1267
- if (!path) return "";
1268
- const home = customHomedir || (typeof process !== "undefined" ? process.env?.HOME : void 0) || (typeof osHomedir === "function" ? osHomedir() : "");
1269
- if (home && path.startsWith(home)) {
1270
- return `~${path.slice(home.length)}`;
1271
- }
1272
- return path.replace(/^(\/Users\/[^\/]+\/|\/home\/[^\/]+\/)/, "~/");
1273
- }
1274
- function formatReadItemPath(rawPath, metadata, customHomedir) {
1275
- const inputPath = rawPath || (typeof metadata?.path === "string" ? metadata.path : void 0) || (typeof metadata?.filePath === "string" ? metadata.filePath : void 0) || "file";
1276
- let formattedPath = formatHomePath(inputPath, customHomedir);
1277
- if (!formattedPath) return "file";
1278
- let rangePart = "";
1279
- const match = formattedPath.match(/(:[0-9]+(-[0-9]+)?(,[0-9]+(-[0-9]+)?)*)$/);
1280
- if (match) {
1281
- rangePart = match[1];
1282
- formattedPath = formattedPath.slice(0, -rangePart.length);
1283
- } else if (metadata && typeof metadata.startLine === "number" && typeof metadata.endLine === "number" && typeof metadata.totalLines === "number") {
1284
- const { startLine, endLine, totalLines, truncated } = metadata;
1285
- if (truncated || startLine !== 1 || endLine !== totalLines) {
1286
- rangePart = `:${startLine}-${endLine}`;
1287
- }
1288
- }
1289
- const clippedPath = clipPathAware(formattedPath, 72);
1290
- return `${clippedPath}${rangePart}`;
1291
- }
1292
- function formatReadTreeLines(items, customHomedir) {
1293
- const count = items.length;
1294
- const header = `Read (${count})`;
1295
- const lines = items.map((item, index) => {
1296
- const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
1297
- const pathWithRange = formatReadItemPath(item.path, item.metadata, customHomedir);
1298
- return { connector, pathWithRange };
1299
- });
1300
- return { header, count, lines };
1301
- }
1302
- function formatSearchItemQuery(query, path, customHomedir) {
1303
- let text = (query || "").trim();
1304
- if (path) {
1305
- const formattedPath = formatHomePath(path, customHomedir);
1306
- if (formattedPath) {
1307
- text = text ? `${text} in ${formattedPath}` : formattedPath;
1308
- }
1309
- }
1310
- if (!text) return "search";
1311
- return text.length > 80 ? `${text.slice(0, 79)}\u2026` : text;
1312
- }
1313
- function formatSearchTreeLines(items, customHomedir) {
1314
- const count = items.length;
1315
- const header = `Search (${count})`;
1316
- const lines = items.map((item, index) => {
1317
- const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
1318
- const queryText = formatSearchItemQuery(item.query, item.path, customHomedir);
1319
- return { connector, queryText };
1320
- });
1321
- return { header, count, lines };
1322
- }
1323
- function formatRunItemCommand(command, exitCode, timedOut) {
1324
- let text = compactWhitespace(command || "");
1325
- if (!text) text = "command";
1326
- text = text.length > 80 ? `${text.slice(0, 79)}\u2026` : text;
1327
- if (timedOut) return `${text} (timed out)`;
1328
- if (typeof exitCode === "number" && exitCode !== 0) return `${text} (exit ${exitCode})`;
1329
- return text;
1330
- }
1331
- function formatRunTreeLines(items) {
1332
- const count = items.length;
1333
- const header = `Run (${count})`;
1334
- const lines = items.map((item, index) => {
1335
- const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
1336
- const commandText = formatRunItemCommand(item.command, item.exitCode, item.timedOut);
1337
- return { connector, commandText };
1338
- });
1339
- return { header, count, lines };
1340
- }
1341
- function formatFetchItemUrl(url) {
1342
- const text = (url || "").trim();
1343
- if (!text) return "webpage";
1344
- return text.length > 80 ? `${text.slice(0, 79)}\u2026` : text;
1345
- }
1346
- function formatFetchTreeLines(items) {
1347
- const count = items.length;
1348
- const header = `Fetch (${count})`;
1349
- const lines = items.map((item, index) => {
1350
- const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
1351
- const urlText = formatFetchItemUrl(item.url);
1352
- return { connector, urlText };
1353
- });
1354
- return { header, count, lines };
1355
- }
1356
-
1357
- // packages/cli/tui/i18n.ts
1358
- var ZH_PATTERNS = /^zh/i;
1359
- function parseCliLocale(raw) {
1360
- const normalized = raw?.trim().toLowerCase();
1361
- if (!normalized) return null;
1362
- if (ZH_PATTERNS.test(normalized)) return "zh";
1363
- if (normalized === "en" || normalized.startsWith("en")) return "en";
1364
- return null;
1365
- }
1366
- function detectLocaleFromEnv(env) {
1367
- const explicit = parseCliLocale(env.NOLO_LANG);
1368
- if (explicit) return explicit;
1369
- const candidates = [env.LC_ALL, env.LC_CTYPE, env.LANG].filter(Boolean);
1370
- for (const candidate of candidates) {
1371
- if (ZH_PATTERNS.test(candidate)) return "zh";
1372
- if (candidate && !candidate.startsWith("C.") && !candidate.startsWith("POSIX")) {
1373
- return "en";
1374
- }
1375
- }
1376
- return null;
1377
- }
1378
- function detectLocale(env) {
1379
- return detectLocaleFromEnv(env) ?? "zh";
1380
- }
1381
- var currentLocale = detectLocale(process.env);
1382
- function getCliLocale() {
1383
- return currentLocale;
1384
- }
1385
- function setCliLocale(locale) {
1386
- currentLocale = locale;
1387
- }
1388
- function initCliLocale(env = process.env) {
1389
- currentLocale = detectLocale(env);
510
+ function initCliLocale(env = process.env) {
511
+ currentLocale = detectLocale(env);
1390
512
  }
1391
513
  var STRINGS = {
1392
514
  welcomeHint: {
@@ -1457,6 +579,18 @@ var STRINGS = {
1457
579
  en: "Stopped this reply.",
1458
580
  zh: "\u5DF2\u505C\u6B62\u672C\u6B21\u56DE\u590D\u3002"
1459
581
  },
582
+ turnStoppedToolPending: {
583
+ en: "Stopped waiting. {0} may still be finishing in the background \u2014 its result won't be added to this conversation.",
584
+ zh: "\u5DF2\u505C\u6B62\u7B49\u5F85\u3002{0} \u53EF\u80FD\u4ECD\u5728\u540E\u53F0\u5B8C\u6210\uFF0C\u5176\u7ED3\u679C\u4E0D\u4F1A\u8BA1\u5165\u672C\u6B21\u5BF9\u8BDD\u3002"
585
+ },
586
+ turnStopping: {
587
+ en: "Stopping\u2026 press Esc again to force",
588
+ zh: "\u6B63\u5728\u505C\u6B62\u2026\u518D\u6309\u4E00\u6B21 Esc \u5F3A\u5236\u505C\u6B62"
589
+ },
590
+ forceStopped: {
591
+ en: "Force stopped. Background tasks may still be finishing.",
592
+ zh: "\u5DF2\u5F3A\u5236\u505C\u6B62\uFF0C\u540E\u53F0\u4EFB\u52A1\u53EF\u80FD\u4ECD\u5728\u6536\u5C3E\u3002"
593
+ },
1460
594
  turnFailed: {
1461
595
  en: "This reply failed. Queued messages are kept \u2014 press Enter to resend.",
1462
596
  zh: "\u672C\u6B21\u56DE\u590D\u51FA\u9519\u3002\u5DF2\u6392\u961F\u7684\u6D88\u606F\u4ECD\u4FDD\u7559\uFF0C\u6309 Enter \u53EF\u91CD\u65B0\u53D1\u9001\u3002"
@@ -1465,6 +599,18 @@ var STRINGS = {
1465
599
  en: "[nolo] This agent seems to have hit a quota/rate limit (HTTP 429).\nUse /agent to switch to another agent and keep going \u2014 the dialog context is preserved.",
1466
600
  zh: "[nolo] \u5F53\u524D agent \u4F3C\u4E4E\u989D\u5EA6/\u901F\u7387\u53D7\u9650\uFF08429\uFF09\u3002\n\u53EF\u4EE5\u7528 /agent \u5207\u6362\u5230\u5176\u4ED6 agent \u540E\u7EE7\u7EED\uFF0C\u540C\u4E00\u5BF9\u8BDD\u4F1A\u4FDD\u7559\u4E0A\u4E0B\u6587\u3002"
1467
601
  },
602
+ balanceExhaustedHint: {
603
+ en: '[nolo] Insufficient balance. Your message is saved in this dialog \u2014 top up, then send again or say "continue".',
604
+ zh: "[nolo] \u4F59\u989D\u4E0D\u8DB3\u3002\u4F60\u521A\u53D1\u7684\u8BDD\u5DF2\u4FDD\u5B58\u5728\u5F53\u524D\u5BF9\u8BDD\u91CC\u2014\u2014\u5145\u503C\u540E\u76F4\u63A5\u518D\u8BF4\u4E00\u53E5\u6216\u8BF4\u300C\u7EE7\u7EED\u300D\u5373\u53EF\uFF0C\u4E0D\u4F1A\u4E22\u4E0A\u4E0B\u6587\u3002"
605
+ },
606
+ dialogPreservedHint: {
607
+ en: '[nolo] This turn failed, but the dialog is kept. Send another message (or say "continue") to keep going in the same conversation.',
608
+ zh: "[nolo] \u672C\u8F6E\u5931\u8D25\u4E86\uFF0C\u4F46\u5BF9\u8BDD\u5DF2\u4FDD\u7559\u3002\u76F4\u63A5\u518D\u8BF4\u4E00\u53E5\uFF08\u6216\u8BF4\u300C\u7EE7\u7EED\u300D\uFF09\u5373\u53EF\u5728\u540C\u4E00\u5BF9\u8BDD\u91CC\u63A5\u7740\u804A\u3002"
609
+ },
610
+ dialogNotSavedHint: {
611
+ en: "[nolo] This turn did not create a dialog. Your next message will start a new conversation \u2014 restate what you need.",
612
+ zh: "[nolo] \u672C\u8F6E\u6CA1\u6709\u5EFA\u6210\u5BF9\u8BDD\u3002\u4E0B\u4E00\u53E5\u4F1A\u662F\u65B0\u5BF9\u8BDD\u2014\u2014\u8BF7\u628A\u4F60\u60F3\u505A\u7684\u4E8B\u518D\u8BF4\u4E00\u904D\u3002"
613
+ },
1468
614
  copiedLastReply: {
1469
615
  en: "Copied the last reply to the clipboard.",
1470
616
  zh: "\u5DF2\u590D\u5236\u6700\u540E\u4E00\u6761\u56DE\u590D\u5230\u526A\u8D34\u677F\u3002"
@@ -1513,6 +659,18 @@ var STRINGS = {
1513
659
  en: "Usage: /mouse <on|off>",
1514
660
  zh: "\u7528\u6CD5\uFF1A/mouse <on|off>"
1515
661
  },
662
+ altscreenOn: {
663
+ en: "Alternate screen on: the TUI uses a private buffer so the terminal wheel no longer fights its own scroll state.",
664
+ zh: "\u5907\u7528\u5C4F\u5DF2\u5F00\u542F\uFF1ATUI \u4F7F\u7528\u72EC\u7ACB\u7F13\u51B2\u533A\uFF0C\u7EC8\u7AEF\u6EDA\u8F6E\u4E0D\u518D\u4E0E\u81EA\u8EAB\u6EDA\u52A8\u72B6\u6001\u4E92\u76F8\u6253\u67B6\u3002"
665
+ },
666
+ altscreenOff: {
667
+ en: "Alternate screen off: the TUI shares the shell scrollback (wheel may desync the viewport).",
668
+ zh: "\u5907\u7528\u5C4F\u5DF2\u5173\u95ED\uFF1ATUI \u4E0E shell \u5171\u7528\u56DE\u6EDA\u7F13\u51B2\u533A\uFF08\u6EDA\u8F6E\u53EF\u80FD\u8BA9\u89C6\u53E3\u9519\u4F4D\uFF09\u3002"
669
+ },
670
+ altscreenUsage: {
671
+ en: "Usage: /altscreen <on|off>",
672
+ zh: "\u7528\u6CD5\uFF1A/altscreen <on|off>"
673
+ },
1516
674
  langSwitched: {
1517
675
  en: "Language switched to English.",
1518
676
  zh: "\u5DF2\u5207\u6362\u4E3A\u4E2D\u6587\u3002"
@@ -1598,6 +756,47 @@ var STRINGS = {
1598
756
  en: "abort the operation",
1599
757
  zh: "\u4E2D\u6B62\u64CD\u4F5C"
1600
758
  },
759
+ // --- Ask choice (ui_ask_choice) ------------------------------------------
760
+ askChoiceTitle: {
761
+ en: "question",
762
+ zh: "\u95EE\u9898"
763
+ },
764
+ askChoiceSubmit: {
765
+ en: "Submit",
766
+ zh: "\u63D0\u4EA4"
767
+ },
768
+ askChoiceOtherLabel: {
769
+ en: "Other",
770
+ zh: "\u5176\u4ED6"
771
+ },
772
+ askChoiceHintSingle: {
773
+ en: "Type your answer, then press Enter to save.",
774
+ zh: "\u8F93\u5165\u56DE\u7B54\u540E\u6309 Enter \u4FDD\u5B58\u3002"
775
+ },
776
+ askChoiceHintMulti: {
777
+ en: "Space to toggle, Enter to confirm selection.",
778
+ zh: "\u7A7A\u683C\u5207\u6362\u9009\u4E2D\uFF0CEnter \u786E\u8BA4\u63D0\u4EA4\u3002"
779
+ },
780
+ askChoiceFooterSingle: {
781
+ en: "\u21B5 pick/submit \xB7 tab switch \xB7 esc cancel",
782
+ zh: "\u21B5 \u9009\u62E9/\u63D0\u4EA4 \xB7 tab \u5207\u6362 \xB7 esc \u53D6\u6D88"
783
+ },
784
+ askChoiceFooterMulti: {
785
+ en: "\u21B5 submit \xB7 space toggle \xB7 tab switch \xB7 esc cancel",
786
+ zh: "\u21B5 \u63D0\u4EA4 \xB7 space \u5207\u6362 \xB7 tab \u5207\u6362 \xB7 esc \u53D6\u6D88"
787
+ },
788
+ askChoiceHistoryHint: {
789
+ en: "Type a number to choose, or reply directly:",
790
+ zh: "\u8BF7\u8F93\u5165\u5E8F\u53F7\u9009\u62E9\uFF0C\u6216\u76F4\u63A5\u56DE\u590D\uFF1A"
791
+ },
792
+ askChoiceHistorySelected: {
793
+ en: "selected",
794
+ zh: "\u5DF2\u9009"
795
+ },
796
+ askChoiceHistoryCancelled: {
797
+ en: "cancelled",
798
+ zh: "\u5DF2\u53D6\u6D88"
799
+ },
1601
800
  contextTitle: {
1602
801
  en: "Workspace context",
1603
802
  zh: "\u5DE5\u4F5C\u533A\u4E0A\u4E0B\u6587"
@@ -1654,144 +853,1869 @@ var STRINGS = {
1654
853
  en: "add working context",
1655
854
  zh: "\u6DFB\u52A0\u5DE5\u4F5C\u4E0A\u4E0B\u6587"
1656
855
  },
1657
- contextNextSkill: {
1658
- en: "attach a skill to this workspace",
1659
- zh: "\u4E3A\u8BE5\u5DE5\u4F5C\u533A\u6302\u8F7D\u6280\u80FD"
856
+ contextNextSkill: {
857
+ en: "attach a skill to this workspace",
858
+ zh: "\u4E3A\u8BE5\u5DE5\u4F5C\u533A\u6302\u8F7D\u6280\u80FD"
859
+ },
860
+ contextNextNew: {
861
+ en: "start a clean dialog",
862
+ zh: "\u5F00\u59CB\u4E00\u4E2A\u5E72\u51C0\u7684\u5BF9\u8BDD"
863
+ },
864
+ agentsTitle: {
865
+ en: "Agents:",
866
+ zh: "\u667A\u80FD\u4F53\uFF1A"
867
+ },
868
+ agentsTip: {
869
+ en: "Tip: run /switch for the full picker, or /switch list for your private agents too.",
870
+ zh: "\u63D0\u793A\uFF1A\u7528 /switch \u6253\u5F00\u5B8C\u6574\u9009\u62E9\u5668\uFF0C\u6216 /switch list \u8FDE\u4F60\u7684\u79C1\u6709\u667A\u80FD\u4F53\u4E00\u8D77\u5217\u51FA\u3002"
871
+ },
872
+ helpText: {
873
+ en: [
874
+ "Commands:",
875
+ " /help Show this help",
876
+ " /new Clear screen and start a fresh dialog",
877
+ " /compact Compact current dialog and fork a new one",
878
+ " /context Show workspace context and next actions",
879
+ " /runtime <mode> Use auto, local, or server runtime",
880
+ " /tools <mode> Control tool trace: hide, compact, verbose",
881
+ " /thinking <mode> Control thinking output: hide, marker, show",
882
+ " /switch Pick an agent interactively (\u2191\u2193, Enter)",
883
+ " /switch list List agents as text",
884
+ " /switch <name> Switch directly by name, alias, or key (alias: /agent)",
885
+ " /agents List platform agent shortcuts",
886
+ " /history Pick a recent dialog to resume (\u2191\u2193, Enter)",
887
+ " /resume <dialogId> Resume a dialog directly by id",
888
+ " /lang <zh|en> Switch interface language",
889
+ " /copy Copy the last reply to the clipboard",
890
+ " /copy view Open native scrollback for partial selection (Ctrl+O)",
891
+ " /mouse <on|off> Toggle mouse mode (off = drag to select text)",
892
+ " /altscreen <on|off> Toggle the terminal alternate screen (default on; off shares shell scrollback)",
893
+ " /doc List attached docs",
894
+ " /doc attach <doc> Attach a doc to this workspace",
895
+ " /skill List attached skills",
896
+ " /skill attach <ref> Attach a skill (dbKey, name, or SKILL.md path)",
897
+ " /skill detach <ref> Detach a skill",
898
+ " /skill clear Detach all skills",
899
+ " /customize Describe how you want to tune nolo",
900
+ " /tasks List background process tasks (aliases: /jobs, /procs)",
901
+ " /stop <pid|all> Stop background process tasks",
902
+ " /login Show login/profile hint",
903
+ " /profile Show active profile",
904
+ " /update Update the nolo CLI install",
905
+ " /version Show version/update hint",
906
+ " /exit Leave the workspace",
907
+ "",
908
+ "You can also type normally. nolo routes simple read/status requests to CLI commands and sends the rest to the current agent."
909
+ ].join("\n"),
910
+ zh: [
911
+ "\u547D\u4EE4\uFF1A",
912
+ " /help \u663E\u793A\u672C\u5E2E\u52A9",
913
+ " /new \u6E05\u5C4F\u5E76\u5F00\u59CB\u65B0\u5BF9\u8BDD",
914
+ " /compact \u538B\u7F29\u5F53\u524D\u5BF9\u8BDD\u5E76\u5206\u53C9\u51FA\u65B0\u5BF9\u8BDD",
915
+ " /context \u67E5\u770B\u5DE5\u4F5C\u533A\u4E0A\u4E0B\u6587\u4E0E\u540E\u7EED\u64CD\u4F5C",
916
+ " /runtime <mode> \u5207\u6362 runtime\uFF1Aauto\u3001local\u3001server",
917
+ " /tools <mode> \u5DE5\u5177\u8F68\u8FF9\u663E\u793A\uFF1Ahide\u3001compact\u3001verbose",
918
+ " /thinking <mode> \u601D\u8003\u8FC7\u7A0B\u663E\u793A\uFF1Ahide\u3001marker\u3001show",
919
+ " /switch \u4EA4\u4E92\u5F0F\u9009\u62E9 agent\uFF08\u2191\u2193 \u79FB\u52A8\uFF0CEnter \u786E\u8BA4\uFF09",
920
+ " /switch list \u4EE5\u6587\u672C\u5217\u51FA\u5168\u90E8 agent",
921
+ " /switch <name> \u6309\u540D\u79F0\u3001\u522B\u540D\u6216 key \u76F4\u63A5\u5207\u6362\uFF08\u522B\u540D\uFF1A/agent\uFF09",
922
+ " /agents \u5217\u51FA\u5E73\u53F0 agent \u5FEB\u6377\u65B9\u5F0F",
923
+ " /history \u4ECE\u6700\u8FD1\u5BF9\u8BDD\u4E2D\u9009\u62E9\u5E76\u6062\u590D\uFF08\u2191\u2193\uFF0CEnter\uFF09",
924
+ " /resume <dialogId> \u6309 id \u76F4\u63A5\u6062\u590D\u5BF9\u8BDD",
925
+ " /lang <zh|en> \u5207\u6362\u754C\u9762\u8BED\u8A00",
926
+ " /copy \u590D\u5236\u6700\u540E\u4E00\u6761\u56DE\u590D\u5230\u526A\u8D34\u677F",
927
+ " /copy view \u6253\u5F00\u539F\u751F\u56DE\u6EDA\u8FDB\u884C\u8DE8\u5C4F\u62D6\u9009\uFF08Ctrl+O\uFF09",
928
+ " /mouse <on|off> \u5207\u6362\u9F20\u6807\u6A21\u5F0F\uFF08off \u540E\u53EF\u76F4\u63A5\u62D6\u9009\u6587\u672C\uFF09",
929
+ " /altscreen <on|off> \u5207\u6362\u7EC8\u7AEF\u5907\u7528\u5C4F\uFF08\u9ED8\u8BA4 on\uFF1Boff \u6539\u4E3A\u4E0E shell \u5171\u7528\u56DE\u6EDA\uFF09",
930
+ " /doc \u5217\u51FA\u5DF2\u6302\u8F7D\u7684\u6587\u6863",
931
+ " /doc attach <doc> \u6302\u8F7D\u6587\u6863\u5230\u5F53\u524D\u5DE5\u4F5C\u533A",
932
+ " /skill \u5217\u51FA\u5DF2\u6302\u8F7D\u7684\u6280\u80FD",
933
+ " /skill attach <ref> \u6302\u8F7D\u6280\u80FD\uFF08dbKey\u3001\u540D\u79F0\u6216 SKILL.md \u8DEF\u5F84\uFF09",
934
+ " /skill detach <ref> \u5378\u8F7D\u6307\u5B9A\u6280\u80FD",
935
+ " /skill clear \u5378\u8F7D\u5168\u90E8\u6280\u80FD",
936
+ " /customize \u63CF\u8FF0\u4F60\u60F3\u600E\u4E48\u8C03\u6559 nolo",
937
+ " /tasks \u5217\u51FA\u540E\u53F0\u5B50\u8FDB\u7A0B\u4EFB\u52A1\uFF08\u522B\u540D\uFF1A/jobs, /procs\uFF09",
938
+ " /stop <pid|all> \u505C\u6B62\u6307\u5B9A\u7684\u540E\u53F0\u4EFB\u52A1",
939
+ " /login \u67E5\u770B\u767B\u5F55 / \u914D\u7F6E\u63D0\u793A",
940
+ " /profile \u67E5\u770B\u5F53\u524D\u914D\u7F6E\u73AF\u5883",
941
+ " /update \u66F4\u65B0 nolo CLI",
942
+ " /version \u67E5\u770B\u7248\u672C\u4E0E\u66F4\u65B0\u63D0\u793A",
943
+ " /exit \u9000\u51FA\u5DE5\u4F5C\u533A",
944
+ "",
945
+ "\u4E5F\u53EF\u4EE5\u76F4\u63A5\u8F93\u5165\u81EA\u7136\u8BED\u8A00\u3002\u7B80\u5355\u7684\u8BFB\u53D6/\u72B6\u6001\u8BF7\u6C42\u4F1A\u8D70 CLI \u547D\u4EE4\uFF0C\u5176\u4F59\u4EA4\u7ED9\u5F53\u524D agent\u3002"
946
+ ].join("\n")
947
+ },
948
+ // --- Slash-command output ---------------------------------------------
949
+ // Command results that used to be hardcoded English. Usage lines keep the
950
+ // command literal untouched; only the surrounding words are translated.
951
+ unknownCommand: {
952
+ en: "Unknown command: {0}\n\n",
953
+ zh: "\u672A\u77E5\u547D\u4EE4\uFF1A{0}\n\n"
954
+ },
955
+ runtimeUsage: { en: "Usage: /runtime <auto|local|server>", zh: "\u7528\u6CD5\uFF1A/runtime <auto|local|server>" },
956
+ runtimeSet: { en: "Runtime: {0}", zh: "\u8FD0\u884C\u6A21\u5F0F\uFF1A{0}" },
957
+ toolsCurrent: {
958
+ en: "Tool display: {0} (hide | compact | verbose)",
959
+ zh: "\u5DE5\u5177\u663E\u793A\uFF1A{0}\uFF08hide | compact | verbose\uFF09"
960
+ },
961
+ toolsUsage: { en: "Usage: /tools <hide|compact|verbose>", zh: "\u7528\u6CD5\uFF1A/tools <hide|compact|verbose>" },
962
+ toolsSet: { en: "Tool display: {0}", zh: "\u5DE5\u5177\u663E\u793A\uFF1A{0}" },
963
+ thinkingCurrent: {
964
+ en: "Thinking display: {0} (hide | marker | show)",
965
+ zh: "\u601D\u8003\u663E\u793A\uFF1A{0}\uFF08hide | marker | show\uFF09"
966
+ },
967
+ thinkingUsage: { en: "Usage: /thinking <hide|marker|show>", zh: "\u7528\u6CD5\uFF1A/thinking <hide|marker|show>" },
968
+ thinkingSet: { en: "Thinking display: {0}", zh: "\u601D\u8003\u663E\u793A\uFF1A{0}" },
969
+ tasksRunning: { en: "Running processes ({0}):", zh: "\u8FD0\u884C\u4E2D\u7684\u8FDB\u7A0B\uFF08{0}\uFF09\uFF1A" },
970
+ tasksStopped: { en: "Stopped/exited ({0}):", zh: "\u5DF2\u505C\u6B62/\u5DF2\u9000\u51FA\uFF08{0}\uFF09\uFF1A" },
971
+ tasksNone: { en: "No processes.", zh: "\u6CA1\u6709\u8FD0\u884C\u4E2D\u7684\u8FDB\u7A0B\u3002" },
972
+ stopUsage: { en: "Usage: /stop <pid|label|all>", zh: "\u7528\u6CD5\uFF1A/stop <pid|label|all>" },
973
+ stopAllDone: { en: "Stopped {0} processes", zh: "\u5DF2\u505C\u6B62 {0} \u4E2A\u8FDB\u7A0B" },
974
+ stopNoPid: { en: "No running process with pid {0}", zh: "\u6CA1\u6709 pid \u4E3A {0} \u7684\u8FD0\u884C\u4E2D\u8FDB\u7A0B" },
975
+ stopPidDone: { en: "Stopped pid {0} ({1})", zh: "\u5DF2\u505C\u6B62 pid {0}\uFF08{1}\uFF09" },
976
+ stopNoLabel: { en: "No running process labeled '{0}'", zh: "\u6CA1\u6709\u540D\u4E3A\u201C{0}\u201D\u7684\u8FD0\u884C\u4E2D\u8FDB\u7A0B" },
977
+ stopLabelsDone: { en: "Stopped {0}", zh: "\u5DF2\u505C\u6B62 {0}" },
978
+ compactNothing: {
979
+ en: "Current dialog: new (nothing to compact yet)",
980
+ zh: "\u5F53\u524D\u5BF9\u8BDD\u8FD8\u662F\u65B0\u7684\uFF08\u8FD8\u6CA1\u6709\u53EF\u538B\u7F29\u7684\u5185\u5BB9\uFF09"
981
+ },
982
+ agentCurrent: { en: "Current agent: {0} ({1})", zh: "\u5F53\u524D agent\uFF1A{0}\uFF08{1}\uFF09" },
983
+ agentUnknown: {
984
+ en: `I don't know agent "{0}" yet.
985
+ Use /switch, /switch list, /switch minimax-m3, or a full agent key.`,
986
+ zh: "\u8FD8\u4E0D\u8BA4\u8BC6 agent\u201C{0}\u201D\u3002\n\u53EF\u4EE5\u7528 /switch\u3001/switch list\u3001/switch minimax-m3 \u6216\u5B8C\u6574\u7684 agent key \u6765\u5207\u6362\u3002"
987
+ },
988
+ themeCurrent: { en: "Current theme: {0} \xB7 {1}", zh: "\u5F53\u524D\u4E3B\u9898\uFF1A{0} \xB7 {1}" },
989
+ themeUsage: {
990
+ en: "Usage: /theme <name> | /theme light | /theme dark",
991
+ zh: "\u7528\u6CD5\uFF1A/theme <name> | /theme light | /theme dark"
992
+ },
993
+ themeAvailable: { en: "Available themes: {0}", zh: "\u53EF\u7528\u4E3B\u9898\uFF1A{0}" },
994
+ themeBrightnessSwitched: { en: "Switched to {0} background colors.", zh: "\u5DF2\u5207\u6362\u5230 {0} \u80CC\u666F\u8272\u3002" },
995
+ themeBrightnessAuto: {
996
+ en: "Background colors follow terminal detection (now: {0}).",
997
+ zh: "\u80CC\u666F\u8272\u8DDF\u968F\u7EC8\u7AEF\u81EA\u52A8\u68C0\u6D4B\uFF08\u5F53\u524D\uFF1A{0}\uFF09\u3002"
998
+ },
999
+ themeSwitched: { en: "Switched to theme: {0}", zh: "\u5DF2\u5207\u6362\u5230\u4E3B\u9898\uFF1A{0}" },
1000
+ themeUnknown: { en: "Unknown theme: {0}. Available themes: {1}", zh: "\u672A\u77E5\u4E3B\u9898\uFF1A{0}\u3002\u53EF\u7528\u4E3B\u9898\uFF1A{1}" },
1001
+ densityCurrent: {
1002
+ en: "Current density: {0}\nUsage: /density <cozy|spacious>",
1003
+ zh: "\u5F53\u524D\u5BC6\u5EA6\uFF1A{0}\n\u7528\u6CD5\uFF1A/density <cozy|spacious>"
1004
+ },
1005
+ densitySwitched: { en: "Switched to layout density: {0}", zh: "\u5DF2\u5207\u6362\u5230\u5E03\u5C40\u5BC6\u5EA6\uFF1A{0}" },
1006
+ densityUnknown: {
1007
+ en: "Unknown density: {0}. Use 'cozy' or 'spacious'.",
1008
+ zh: "\u672A\u77E5\u5BC6\u5EA6\uFF1A{0}\u3002\u8BF7\u4F7F\u7528 cozy \u6216 spacious\u3002"
1009
+ },
1010
+ docAttachUsage: { en: "Usage: /doc attach <doc>", zh: "\u7528\u6CD5\uFF1A/doc attach <doc>" },
1011
+ docList: { en: "Attached docs: {0}", zh: "\u5DF2\u6302\u8F7D\u6587\u6863\uFF1A{0}" },
1012
+ docNone: { en: "No docs attached. Use /doc attach <doc>.", zh: "\u8FD8\u6CA1\u6709\u6302\u8F7D\u6587\u6863\u3002\u7528 /doc attach <doc> \u6302\u8F7D\u3002" },
1013
+ skillAttachUsage: { en: "Usage: /skill attach <skill-ref>", zh: "\u7528\u6CD5\uFF1A/skill attach <skill-ref>" },
1014
+ skillAttached: { en: "Attached skill: {0}", zh: "\u5DF2\u6302\u8F7D\u6280\u80FD\uFF1A{0}" },
1015
+ skillDetachUsage: { en: "Usage: /skill detach <skill-ref>", zh: "\u7528\u6CD5\uFF1A/skill detach <skill-ref>" },
1016
+ skillDetached: { en: "Detached skill: {0}", zh: "\u5DF2\u5378\u8F7D\u6280\u80FD\uFF1A{0}" },
1017
+ skillNotAttached: { en: "Skill not attached: {0}", zh: "\u6280\u80FD\u672A\u6302\u8F7D\uFF1A{0}" },
1018
+ skillNone: { en: "No skills attached.", zh: "\u8FD8\u6CA1\u6709\u6302\u8F7D\u6280\u80FD\u3002" },
1019
+ skillCleared: { en: "Cleared {0} skill(s).", zh: "\u5DF2\u6E05\u7A7A {0} \u4E2A\u6280\u80FD\u3002" },
1020
+ skillList: {
1021
+ en: "Attached skills: {0}\nUsage: /skill attach <ref> | /skill detach <ref> | /skill clear",
1022
+ zh: "\u5DF2\u6302\u8F7D\u6280\u80FD\uFF1A{0}\n\u7528\u6CD5\uFF1A/skill attach <ref> | /skill detach <ref> | /skill clear"
1023
+ },
1024
+ skillNoneHint: {
1025
+ en: "No skills attached. Use /skill attach <skill-ref> to attach a skill.\nSkill refs can be a dbKey (page-xxx), a skill name (searched in .agents/skills/ then docs/skills/), or a direct path.",
1026
+ zh: "\u8FD8\u6CA1\u6709\u6302\u8F7D\u6280\u80FD\u3002\u7528 /skill attach <skill-ref> \u6302\u8F7D\u6280\u80FD\u3002\n\u6280\u80FD\u5F15\u7528\u53EF\u4EE5\u662F dbKey\uFF08page-xxx\uFF09\u3001\u6280\u80FD\u540D\uFF08\u4F1A\u5728 .agents/skills/ \u4E0E docs/skills/ \u4E2D\u67E5\u627E\uFF09\u6216\u76F4\u63A5\u8DEF\u5F84\u3002"
1027
+ },
1028
+ customizeHint: {
1029
+ en: "Tell nolo what to change, for example: /customize make my default agent more concise.",
1030
+ zh: "\u544A\u8BC9 nolo \u4F60\u60F3\u6539\u4EC0\u4E48\uFF0C\u4F8B\u5982\uFF1A/customize make my default agent more concise\u3002"
1031
+ },
1032
+ loginHint: {
1033
+ en: "MVP login uses profile/env auth. Set AUTH_TOKEN, NOLO_SERVER, or NOLO_PROFILE before starting nolo.",
1034
+ zh: "MVP \u767B\u5F55\u8D70 profile/\u73AF\u5883\u53D8\u91CF\u8BA4\u8BC1\u3002\u542F\u52A8 nolo \u524D\u8BF7\u8BBE\u7F6E AUTH_TOKEN\u3001NOLO_SERVER \u6216 NOLO_PROFILE\u3002"
1035
+ },
1036
+ versionInfo: {
1037
+ en: "nolo {0}\nUpdate this install with: nolo update\nIf repo-local output differs, publish/install the latest npm package first.",
1038
+ zh: "nolo {0}\n\u7528 nolo update \u66F4\u65B0\u5F53\u524D\u5B89\u88C5\u3002\n\u5982\u679C\u672C\u5730\u4ED3\u5E93\u8F93\u51FA\u7684\u7248\u672C\u4E0D\u540C\uFF0C\u8BF7\u5148\u53D1\u5E03/\u5B89\u88C5\u6700\u65B0\u7684 npm \u5305\u3002"
1039
+ },
1040
+ versionUnknown: { en: "unknown version", zh: "\u672A\u77E5\u7248\u672C" },
1041
+ // --- Dialog list / timestamps -----------------------------------------
1042
+ recentDialogs: { en: "Recent dialogs:", zh: "\u6700\u8FD1\u5BF9\u8BDD\uFF1A" },
1043
+ dialogListTip: {
1044
+ en: "Tip: run /history to pick one interactively, or paste an id after /resume.",
1045
+ zh: "\u63D0\u793A\uFF1A\u7528 /history \u4EA4\u4E92\u5F0F\u9009\u62E9\uFF0C\u6216\u628A id \u7C98\u5230 /resume \u540E\u9762\u3002"
1046
+ },
1047
+ timeJustNow: { en: "just now", zh: "\u521A\u521A" },
1048
+ timeMinutesAgo: { en: "{0}m ago", zh: "{0} \u5206\u949F\u524D" },
1049
+ timeHoursAgo: { en: "{0}h ago", zh: "{0} \u5C0F\u65F6\u524D" },
1050
+ timeDaysAgo: { en: "{0}d ago", zh: "{0} \u5929\u524D" },
1051
+ // Overflow hints shared by select / multi-select / ask-choice. The arrows
1052
+ // and count are part of the copy so each locale can order them naturally.
1053
+ dialogMoreAbove: { en: "\u2191 {0} more", zh: "\u2191 {0} \u66F4\u591A" },
1054
+ dialogMoreBelow: { en: "\u2193 {0} more", zh: "\u2193 {0} \u66F4\u591A" }
1055
+ };
1056
+ function t(key, ...params) {
1057
+ const text = STRINGS[key][currentLocale];
1058
+ if (params.length === 0) return text;
1059
+ return text.replace(/\{(\d+)\}/g, (match, index) => {
1060
+ const replacement = params[Number(index)];
1061
+ return replacement === void 0 ? match : replacement;
1062
+ });
1063
+ }
1064
+ var TOOL_LABELS = {
1065
+ // Local workspace tools (packages/agent-runtime/localWorkspaceTools.ts)
1066
+ readFile: { en: "Read", zh: "\u8BFB\u53D6" },
1067
+ writeFile: { en: "Write", zh: "\u5199\u5165" },
1068
+ editFile: { en: "Edit", zh: "\u7F16\u8F91" },
1069
+ listFiles: { en: "List", zh: "\u5217\u51FA" },
1070
+ searchFiles: { en: "Search", zh: "\u641C\u7D22" },
1071
+ globFiles: { en: "Glob", zh: "\u5339\u914D" },
1072
+ execShell: { en: "Run", zh: "\u6267\u884C" },
1073
+ runCommand: { en: "Run", zh: "\u6267\u884C" },
1074
+ captureVisualState: { en: "Capture", zh: "\u622A\u5C4F" },
1075
+ // Workspace / diagnostics
1076
+ searchWorkspace: { en: "Search workspace", zh: "\u641C\u7D22\u5DE5\u4F5C\u533A" },
1077
+ cliDoctor: { en: "Doctor", zh: "\u81EA\u68C0" },
1078
+ cliWhoami: { en: "Whoami", zh: "\u67E5\u770B\u8EAB\u4EFD" },
1079
+ checkEnv: { en: "Check env", zh: "\u68C0\u67E5\u73AF\u5883" },
1080
+ configure: { en: "Configure", zh: "\u914D\u7F6E" },
1081
+ notifyUser: { en: "Notify", zh: "\u901A\u77E5" },
1082
+ // Docs / dialogs / spaces
1083
+ readDoc: { en: "Read doc", zh: "\u8BFB\u53D6\u6587\u6863" },
1084
+ createDoc: { en: "Create doc", zh: "\u65B0\u5EFA\u6587\u6863" },
1085
+ updateDoc: { en: "Update doc", zh: "\u66F4\u65B0\u6587\u6863" },
1086
+ readDialog: { en: "Read dialog", zh: "\u8BFB\u53D6\u5BF9\u8BDD" },
1087
+ listDialogs: { en: "List dialogs", zh: "\u5217\u51FA\u5BF9\u8BDD" },
1088
+ queryDialogsBySubjectRef: { en: "Query dialogs", zh: "\u67E5\u8BE2\u5BF9\u8BDD" },
1089
+ searchDialogMessages: { en: "Search messages", zh: "\u641C\u7D22\u6D88\u606F" },
1090
+ listSpaces: { en: "List spaces", zh: "\u5217\u51FA\u7A7A\u95F4" },
1091
+ readSpace: { en: "Read space", zh: "\u8BFB\u53D6\u7A7A\u95F4" },
1092
+ // Tables
1093
+ createTable: { en: "Create table", zh: "\u521B\u5EFA\u8868" },
1094
+ queryTableRows: { en: "Query rows", zh: "\u67E5\u8BE2\u8868\u884C" },
1095
+ addTableRow: { en: "Add row", zh: "\u65B0\u589E\u8868\u884C" },
1096
+ addTableRows: { en: "Add rows", zh: "\u65B0\u589E\u8868\u884C" },
1097
+ updateTableRow: { en: "Update row", zh: "\u66F4\u65B0\u8868\u884C" },
1098
+ deleteTableRow: { en: "Delete row", zh: "\u5220\u9664\u8868\u884C" },
1099
+ // Web
1100
+ fetchWebpage: { en: "Fetch page", zh: "\u6293\u53D6\u7F51\u9875" },
1101
+ readPage: { en: "Read page", zh: "\u8BFB\u53D6\u7F51\u9875" },
1102
+ exaSearch: { en: "Web search", zh: "\u8054\u7F51\u641C\u7D22" },
1103
+ // Skill loading
1104
+ loadSkill: { en: "Used Skill", zh: "\u4F7F\u7528\u6280\u80FD" }
1105
+ };
1106
+ function toolLabel(name) {
1107
+ return TOOL_LABELS[name]?.[currentLocale] ?? name;
1108
+ }
1109
+
1110
+ // packages/cli/tui/tuiAnsi.ts
1111
+ var ANSI_ESCAPE_REGEX = /\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/g;
1112
+ var OSC_HYPERLINK_REGEX = /\x1b\]8;;[^\x07\x1b]*(?:\x07|\x1b\\)\x1b\]8;;(?:\x07|\x1b\\)|\x1b\]8;;[^\x07\x1b]*(?:\x07|\x1b\\)/g;
1113
+ function stripAnsi(text) {
1114
+ return text.replace(OSC_HYPERLINK_REGEX, "").replace(ANSI_ESCAPE_REGEX, "");
1115
+ }
1116
+ var SGR_SEQUENCE_REGEX = /^\x1b\[[0-9;]*m/;
1117
+ var TRAILING_SGR_REGEX = /(?:\x1b\[[0-9;]*m)+$/;
1118
+ function applyTerminalOutputToText(existing, chunk) {
1119
+ if (!chunk) return existing;
1120
+ let text = existing;
1121
+ let index = 0;
1122
+ while (index < chunk.length) {
1123
+ if (chunk[index] === "\x1B") {
1124
+ const sgr = SGR_SEQUENCE_REGEX.exec(chunk.slice(index));
1125
+ if (sgr) {
1126
+ text += sgr[0];
1127
+ index += sgr[0].length;
1128
+ continue;
1129
+ }
1130
+ const osc = chunk.slice(index).match(/^\x1b\]8;;[^\x07\x1b]*(?:\x07|\x1b\\)/);
1131
+ if (osc) {
1132
+ index += osc[0].length;
1133
+ continue;
1134
+ }
1135
+ const csi = chunk.slice(index).match(/^\x1b\[[\x30-\x3f]*[\x20-\x2f]*[\x40-\x7e]/);
1136
+ if (csi) {
1137
+ index += csi[0].length;
1138
+ continue;
1139
+ }
1140
+ index += 1;
1141
+ continue;
1142
+ }
1143
+ const ch = chunk[index];
1144
+ if (ch === "\r") {
1145
+ const lastNl = text.lastIndexOf("\n");
1146
+ text = lastNl === -1 ? "" : text.slice(0, lastNl + 1);
1147
+ index += 1;
1148
+ continue;
1149
+ }
1150
+ if (ch === "\n") {
1151
+ text += "\n";
1152
+ index += 1;
1153
+ continue;
1154
+ }
1155
+ if (ch === "\b") {
1156
+ const trailing = TRAILING_SGR_REGEX.exec(text);
1157
+ const sgrTail = trailing ? trailing[0] : "";
1158
+ const head = sgrTail ? text.slice(0, -sgrTail.length) : text;
1159
+ if (head.length > 0 && head[head.length - 1] !== "\n") {
1160
+ text = head.slice(0, -1) + sgrTail;
1161
+ }
1162
+ index += 1;
1163
+ continue;
1164
+ }
1165
+ const code = ch.charCodeAt(0);
1166
+ if (code < 32 && ch !== " " || code === 127) {
1167
+ index += 1;
1168
+ continue;
1169
+ }
1170
+ text += ch;
1171
+ index += 1;
1172
+ }
1173
+ return text;
1174
+ }
1175
+ function displayWidth(str) {
1176
+ let width = 0;
1177
+ for (const char of str) {
1178
+ const code = char.codePointAt(0) ?? 0;
1179
+ if (code < 32 || code === 127) continue;
1180
+ if (code >= 4352 && code <= 4447 || // 0x2768-0x2775 (ornamental brackets, incl. the ❯ prompt at U+276F)
1181
+ // render narrow in terminals; counting them wide drifts the cursor.
1182
+ code >= 9728 && code <= 10175 && !(code >= 10088 && code <= 10101) || // 0x2B00-0x2BFF (Misc Symbols and Arrows, incl. ⬢ U+2B22 used as the
1183
+ // status-line agent icon) render double-wide in common monospace fonts;
1184
+ // counting ⬢ as width 1 drifts the whole status line right of the icon.
1185
+ code >= 11008 && code <= 11263 || code >= 11904 && code <= 12350 || code >= 12352 && code <= 13247 || code >= 13312 && code <= 19903 || code >= 19968 && code <= 42191 || code >= 44032 && code <= 55203 || code >= 63744 && code <= 64255 || code >= 65072 && code <= 65135 || code >= 65281 && code <= 65376 || code >= 65504 && code <= 65510 || code >= 127744 && code <= 129791 || code >= 131072 && code <= 196605 || code >= 196608 && code <= 262141 || // East Asian Ambiguous CJK quotation marks (U+201C/D “ ”, U+2018/9 ‘ ’).
1186
+ // In CJK locale + CJK fonts these render double-wide like 全角 punctuation;
1187
+ // in English fonts they stay width 1. Follow the active locale so Chinese
1188
+ // replies align correctly without breaking English terminal output.
1189
+ getCliLocale() === "zh" && (code === 8220 || code === 8221 || code === 8216 || code === 8217)) {
1190
+ width += 2;
1191
+ } else {
1192
+ width += 1;
1193
+ }
1194
+ }
1195
+ return width;
1196
+ }
1197
+ function visibleWidth(str) {
1198
+ return displayWidth(stripAnsi(str));
1199
+ }
1200
+ function truncateAnsi(text, maxWidth) {
1201
+ if (maxWidth <= 0) return "";
1202
+ if (visibleWidth(text) <= maxWidth) return text;
1203
+ let width = 0;
1204
+ let out = "";
1205
+ let i = 0;
1206
+ let sawAnsi = false;
1207
+ while (i < text.length) {
1208
+ if (text[i] === "\x1B" && text[i + 1] === "[") {
1209
+ sawAnsi = true;
1210
+ let j = i + 2;
1211
+ while (j < text.length) {
1212
+ const code = text.charCodeAt(j);
1213
+ j += 1;
1214
+ if (code >= 64 && code <= 126) break;
1215
+ }
1216
+ out += text.slice(i, j);
1217
+ i = j;
1218
+ continue;
1219
+ }
1220
+ const codePoint = text.codePointAt(i) ?? 0;
1221
+ const char = String.fromCodePoint(codePoint);
1222
+ const charWidth = displayWidth(char);
1223
+ if (width + charWidth > maxWidth) break;
1224
+ out += char;
1225
+ width += charWidth;
1226
+ i += char.length;
1227
+ }
1228
+ return sawAnsi ? `${out}\x1B[0m` : out;
1229
+ }
1230
+ function fitAnsiLine(text, width, ellipsis = "\u2026") {
1231
+ if (width <= 0) return "";
1232
+ if (visibleWidth(text) <= width) return text;
1233
+ const ellipsisWidth = displayWidth(ellipsis);
1234
+ if (width < ellipsisWidth) return truncateAnsi(text, width);
1235
+ if (width === ellipsisWidth) return truncateAnsi(ellipsis, width) || truncateAnsi(text, width);
1236
+ return `${truncateAnsi(text, width - ellipsisWidth)}${ellipsis}`;
1237
+ }
1238
+ function countPhysicalLines(text, columns) {
1239
+ const lines = text.split("\n");
1240
+ let total = 0;
1241
+ for (const line of lines) {
1242
+ const width = displayWidth(line);
1243
+ total += Math.max(1, Math.ceil(width / columns));
1244
+ }
1245
+ return Math.max(total, 1);
1246
+ }
1247
+ function takeDisplayWidth(text, width) {
1248
+ let used = 0;
1249
+ let index = 0;
1250
+ for (const char of text) {
1251
+ const charWidth = displayWidth(char);
1252
+ if (used + charWidth > width && used > 0) break;
1253
+ used += charWidth;
1254
+ index += char.length;
1255
+ }
1256
+ return { prefix: text.slice(0, index), rest: text.slice(index) };
1257
+ }
1258
+ function padOrTruncateToWidth(text, width) {
1259
+ const textWidth = visibleWidth(text);
1260
+ if (textWidth > width) {
1261
+ return truncateAnsi(text, width);
1262
+ }
1263
+ return `${text}${" ".repeat(width - textWidth)}`;
1264
+ }
1265
+ var SGR_RESET_REGEX = /^\x1b\[0?m$/;
1266
+ function tokenizeAnsiLine(line) {
1267
+ const tokens = [];
1268
+ let index = 0;
1269
+ while (index < line.length) {
1270
+ if (line[index] === "\x1B") {
1271
+ const osc = line.slice(index).match(/^\x1b\]8;;[^\x07\x1b]*(?:\x07|\x1b\\)/);
1272
+ if (osc) {
1273
+ tokens.push({ kind: "sgr", value: osc[0], width: 0 });
1274
+ index += osc[0].length;
1275
+ continue;
1276
+ }
1277
+ const sgr = SGR_SEQUENCE_REGEX.exec(line.slice(index));
1278
+ if (sgr) {
1279
+ tokens.push({ kind: "sgr", value: sgr[0], width: 0 });
1280
+ index += sgr[0].length;
1281
+ continue;
1282
+ }
1283
+ }
1284
+ const codePoint = line.codePointAt(index) ?? 0;
1285
+ const value = String.fromCodePoint(codePoint);
1286
+ tokens.push({ kind: "char", value, width: displayWidth(value) });
1287
+ index += value.length;
1288
+ }
1289
+ return tokens;
1290
+ }
1291
+ function wrapTranscriptLine(line, columns) {
1292
+ if (line === "") return [""];
1293
+ const tokens = tokenizeAnsiLine(line);
1294
+ const result = [];
1295
+ let activeStyles = [];
1296
+ const applyStyleToken = (value) => {
1297
+ if (SGR_RESET_REGEX.test(value)) {
1298
+ activeStyles = [];
1299
+ } else {
1300
+ activeStyles.push(value);
1301
+ }
1302
+ };
1303
+ let start = 0;
1304
+ while (start < tokens.length) {
1305
+ if (tokens.slice(start).every((token) => token.kind === "sgr")) {
1306
+ if (result.length > 0) break;
1307
+ }
1308
+ const openingStyles = [...activeStyles];
1309
+ let width = 0;
1310
+ let end = start;
1311
+ let lastBreak = -1;
1312
+ while (end < tokens.length) {
1313
+ const token = tokens[end];
1314
+ if (token.kind === "sgr") {
1315
+ end += 1;
1316
+ continue;
1317
+ }
1318
+ if (width + token.width > columns && width > 0) break;
1319
+ width += token.width;
1320
+ end += 1;
1321
+ if (token.value === " " || token.value === " ") {
1322
+ lastBreak = end;
1323
+ }
1324
+ }
1325
+ let segmentEnd = end;
1326
+ if (end < tokens.length && lastBreak > start) {
1327
+ const overflowToken = tokens[end];
1328
+ if (overflowToken.kind === "char" && overflowToken.value !== " " && overflowToken.width === 1) {
1329
+ segmentEnd = lastBreak;
1330
+ }
1331
+ }
1332
+ if (segmentEnd === start) segmentEnd = start + 1;
1333
+ let segment = "";
1334
+ let sawStyle = openingStyles.length > 0;
1335
+ for (let i = start; i < segmentEnd; i += 1) {
1336
+ const token = tokens[i];
1337
+ segment += token.value;
1338
+ if (token.kind === "sgr") {
1339
+ sawStyle = true;
1340
+ applyStyleToken(token.value);
1341
+ }
1342
+ }
1343
+ const prefix = openingStyles.join("");
1344
+ const needsReset = (sawStyle || activeStyles.length > 0) && !segment.endsWith("\x1B[0m");
1345
+ result.push(`${prefix}${segment}${needsReset ? "\x1B[0m" : ""}`);
1346
+ start = segmentEnd;
1347
+ while (start < tokens.length) {
1348
+ const token = tokens[start];
1349
+ if (token.kind === "char" && token.value === " ") {
1350
+ start += 1;
1351
+ continue;
1352
+ }
1353
+ break;
1354
+ }
1355
+ }
1356
+ return result.length > 0 ? result : [""];
1357
+ }
1358
+ function wrapTextToLines(text, columns) {
1359
+ const result = [];
1360
+ for (const logicalLine of text.split("\n")) {
1361
+ if (logicalLine === "") {
1362
+ result.push("");
1363
+ continue;
1364
+ }
1365
+ let remaining = logicalLine;
1366
+ while (remaining.length > 0) {
1367
+ const { prefix, rest } = takeDisplayWidth(remaining, columns);
1368
+ result.push(prefix);
1369
+ remaining = rest;
1370
+ }
1371
+ }
1372
+ return result;
1373
+ }
1374
+
1375
+ // packages/cli/tui/theme.ts
1376
+ var THEME_PALETTES = {
1377
+ trail: {
1378
+ light: {
1379
+ accent: { hex: "2E7DB5", ansiFallback: "\x1B[34m" },
1380
+ // ocean blue
1381
+ chrome: { hex: "768594", ansiFallback: "\x1B[90m" },
1382
+ // slate — two steps below app placeholder A3B0BD (was 8A97A5); rules/tool markers on white need the extra notch
1383
+ success: { hex: "3F8F5C", ansiFallback: "\x1B[32m" },
1384
+ // moss green
1385
+ warning: { hex: "9A6A1F", ansiFallback: "\x1B[33m" },
1386
+ // deeper amber — B57F2E still read as yellow/washed on white (H2 headers like ## Plan floated); 9A6A1F raises contrast
1387
+ info: { hex: "4A9FD4", ansiFallback: "\x1B[36m" },
1388
+ // sky blue
1389
+ danger: { hex: "C45C4A", ansiFallback: "\x1B[31m" },
1390
+ // reddish
1391
+ muted: { hex: "5E6A78", ansiFallback: "\x1B[90m" }
1392
+ // denser slate — 687584 read too blue in dense prose (inline code/tool labels); 5E6A78 pulls blue down while staying darker than the old value
1393
+ },
1394
+ dark: {
1395
+ accent: { hex: "89B4FA", ansiFallback: "\x1B[34m" },
1396
+ // blue
1397
+ chrome: { hex: "6C7086", ansiFallback: "\x1B[90m" },
1398
+ success: { hex: "A6E3A1", ansiFallback: "\x1B[32m" },
1399
+ warning: { hex: "F9E2AF", ansiFallback: "\x1B[33m" },
1400
+ info: { hex: "94E2D5", ansiFallback: "\x1B[36m" },
1401
+ danger: { hex: "F38BA8", ansiFallback: "\x1B[31m" },
1402
+ muted: { hex: "9AA3B8", ansiFallback: "\x1B[90m" }
1403
+ // dimmed from A6ADC8 so secondary text sits a step below body text
1404
+ }
1405
+ },
1406
+ catppuccin: {
1407
+ light: {
1408
+ accent: { hex: "0969DA", ansiFallback: "\x1B[34m" },
1409
+ chrome: { hex: "57606A", ansiFallback: "\x1B[90m" },
1410
+ success: { hex: "116329", ansiFallback: "\x1B[32m" },
1411
+ warning: { hex: "9A6700", ansiFallback: "\x1B[33m" },
1412
+ info: { hex: "0969DA", ansiFallback: "\x1B[36m" },
1413
+ danger: { hex: "CF222E", ansiFallback: "\x1B[31m" },
1414
+ muted: { hex: "6E7781", ansiFallback: "\x1B[90m" }
1415
+ },
1416
+ dark: {
1417
+ accent: { hex: "58A6FF", ansiFallback: "\x1B[34m" },
1418
+ // GitHub sky blue
1419
+ chrome: { hex: "6E7681", ansiFallback: "\x1B[90m" },
1420
+ success: { hex: "3FB950", ansiFallback: "\x1B[32m" },
1421
+ warning: { hex: "D29922", ansiFallback: "\x1B[33m" },
1422
+ info: { hex: "58A6FF", ansiFallback: "\x1B[36m" },
1423
+ danger: { hex: "FF7B72", ansiFallback: "\x1B[31m" },
1424
+ muted: { hex: "B1BAC4", ansiFallback: "\x1B[90m" }
1425
+ }
1660
1426
  },
1661
- contextNextNew: {
1662
- en: "start a clean dialog",
1663
- zh: "\u5F00\u59CB\u4E00\u4E2A\u5E72\u51C0\u7684\u5BF9\u8BDD"
1427
+ wave: {
1428
+ light: {
1429
+ accent: { hex: "4D699B", ansiFallback: "\x1B[34m" },
1430
+ // ink blue
1431
+ chrome: { hex: "9E9B8E", ansiFallback: "\x1B[90m" },
1432
+ success: { hex: "6F894E", ansiFallback: "\x1B[32m" },
1433
+ warning: { hex: "836F4A", ansiFallback: "\x1B[33m" },
1434
+ info: { hex: "4D699B", ansiFallback: "\x1B[36m" },
1435
+ danger: { hex: "C84053", ansiFallback: "\x1B[31m" },
1436
+ muted: { hex: "716E61", ansiFallback: "\x1B[90m" }
1437
+ },
1438
+ dark: {
1439
+ accent: { hex: "7E9CD8", ansiFallback: "\x1B[34m" },
1440
+ // crystalBlue
1441
+ chrome: { hex: "727169", ansiFallback: "\x1B[90m" },
1442
+ success: { hex: "98BB6C", ansiFallback: "\x1B[32m" },
1443
+ warning: { hex: "E6C384", ansiFallback: "\x1B[33m" },
1444
+ info: { hex: "7FB4CA", ansiFallback: "\x1B[36m" },
1445
+ danger: { hex: "E82424", ansiFallback: "\x1B[31m" },
1446
+ muted: { hex: "938AA9", ansiFallback: "\x1B[90m" }
1447
+ }
1664
1448
  },
1665
- agentsTitle: {
1666
- en: "Agents:",
1667
- zh: "\u667A\u80FD\u4F53\uFF1A"
1449
+ iris: {
1450
+ light: {
1451
+ accent: { hex: "5E6AD2", ansiFallback: "\x1B[34m" },
1452
+ chrome: { hex: "9E9AB5", ansiFallback: "\x1B[90m" },
1453
+ success: { hex: "16A34A", ansiFallback: "\x1B[32m" },
1454
+ warning: { hex: "D97706", ansiFallback: "\x1B[33m" },
1455
+ info: { hex: "5E6AD2", ansiFallback: "\x1B[36m" },
1456
+ danger: { hex: "E5484D", ansiFallback: "\x1B[31m" },
1457
+ muted: { hex: "6E6A8A", ansiFallback: "\x1B[90m" }
1458
+ },
1459
+ dark: {
1460
+ accent: { hex: "8B9CF4", ansiFallback: "\x1B[34m" },
1461
+ chrome: { hex: "5A5772", ansiFallback: "\x1B[90m" },
1462
+ success: { hex: "26BD6C", ansiFallback: "\x1B[32m" },
1463
+ warning: { hex: "F5A623", ansiFallback: "\x1B[33m" },
1464
+ info: { hex: "8B9CF4", ansiFallback: "\x1B[36m" },
1465
+ danger: { hex: "EC5B6E", ansiFallback: "\x1B[31m" },
1466
+ muted: { hex: "938AA9", ansiFallback: "\x1B[90m" }
1467
+ }
1668
1468
  },
1669
- agentsTip: {
1670
- en: "Tip: run /switch for the full picker, or /switch list for your private agents too.",
1671
- zh: "\u63D0\u793A\uFF1A\u7528 /switch \u6253\u5F00\u5B8C\u6574\u9009\u62E9\u5668\uFF0C\u6216 /switch list \u8FDE\u4F60\u7684\u79C1\u6709\u667A\u80FD\u4F53\u4E00\u8D77\u5217\u51FA\u3002"
1469
+ rose: {
1470
+ light: {
1471
+ accent: { hex: "D14D72", ansiFallback: "\x1B[35m" },
1472
+ chrome: { hex: "9893A5", ansiFallback: "\x1B[90m" },
1473
+ success: { hex: "56949F", ansiFallback: "\x1B[32m" },
1474
+ warning: { hex: "EA9D34", ansiFallback: "\x1B[33m" },
1475
+ info: { hex: "286983", ansiFallback: "\x1B[36m" },
1476
+ danger: { hex: "B4637A", ansiFallback: "\x1B[31m" },
1477
+ muted: { hex: "797593", ansiFallback: "\x1B[90m" }
1478
+ },
1479
+ dark: {
1480
+ accent: { hex: "EB6F92", ansiFallback: "\x1B[35m" },
1481
+ chrome: { hex: "6E6A86", ansiFallback: "\x1B[90m" },
1482
+ success: { hex: "9CCFD8", ansiFallback: "\x1B[32m" },
1483
+ warning: { hex: "F6C177", ansiFallback: "\x1B[33m" },
1484
+ info: { hex: "C4A7E7", ansiFallback: "\x1B[36m" },
1485
+ danger: { hex: "EB6F92", ansiFallback: "\x1B[31m" },
1486
+ muted: { hex: "908CAA", ansiFallback: "\x1B[90m" }
1487
+ }
1672
1488
  },
1673
- helpText: {
1674
- en: [
1675
- "Commands:",
1676
- " /help Show this help",
1677
- " /new Clear screen and start a fresh dialog",
1678
- " /compact Compact current dialog and fork a new one",
1679
- " /context Show workspace context and next actions",
1680
- " /runtime <mode> Use auto, local, or server runtime",
1681
- " /tools <mode> Control tool trace: hide, compact, verbose",
1682
- " /thinking <mode> Control thinking output: hide, marker, show",
1683
- " /switch Pick an agent interactively (\u2191\u2193, Enter)",
1684
- " /switch list List agents as text",
1685
- " /switch <name> Switch directly by name, alias, or key (alias: /agent)",
1686
- " /agents List platform agent shortcuts",
1687
- " /history Pick a recent dialog to resume (\u2191\u2193, Enter)",
1688
- " /resume <dialogId> Resume a dialog directly by id",
1689
- " /lang <zh|en> Switch interface language",
1690
- " /copy Copy the last reply to the clipboard",
1691
- " /copy view Open native scrollback for partial selection (Ctrl+O)",
1692
- " /mouse <on|off> Toggle mouse mode (off = drag to select text)",
1693
- " /doc List attached docs",
1694
- " /doc attach <doc> Attach a doc to this workspace",
1695
- " /skill List attached skills",
1696
- " /skill attach <ref> Attach a skill (dbKey, name, or SKILL.md path)",
1697
- " /skill detach <ref> Detach a skill",
1698
- " /skill clear Detach all skills",
1699
- " /customize Describe how you want to tune nolo",
1700
- " /tasks List background process tasks (aliases: /jobs, /procs)",
1701
- " /stop <pid|all> Stop background process tasks",
1702
- " /login Show login/profile hint",
1703
- " /profile Show active profile",
1704
- " /update Update the nolo CLI install",
1705
- " /version Show version/update hint",
1706
- " /exit Leave the workspace",
1707
- "",
1708
- "You can also type normally. nolo routes simple read/status requests to CLI commands and sends the rest to the current agent."
1709
- ].join("\n"),
1710
- zh: [
1711
- "\u547D\u4EE4\uFF1A",
1712
- " /help \u663E\u793A\u672C\u5E2E\u52A9",
1713
- " /new \u6E05\u5C4F\u5E76\u5F00\u59CB\u65B0\u5BF9\u8BDD",
1714
- " /compact \u538B\u7F29\u5F53\u524D\u5BF9\u8BDD\u5E76\u5206\u53C9\u51FA\u65B0\u5BF9\u8BDD",
1715
- " /context \u67E5\u770B\u5DE5\u4F5C\u533A\u4E0A\u4E0B\u6587\u4E0E\u540E\u7EED\u64CD\u4F5C",
1716
- " /runtime <mode> \u5207\u6362 runtime\uFF1Aauto\u3001local\u3001server",
1717
- " /tools <mode> \u5DE5\u5177\u8F68\u8FF9\u663E\u793A\uFF1Ahide\u3001compact\u3001verbose",
1718
- " /thinking <mode> \u601D\u8003\u8FC7\u7A0B\u663E\u793A\uFF1Ahide\u3001marker\u3001show",
1719
- " /switch \u4EA4\u4E92\u5F0F\u9009\u62E9 agent\uFF08\u2191\u2193 \u79FB\u52A8\uFF0CEnter \u786E\u8BA4\uFF09",
1720
- " /switch list \u4EE5\u6587\u672C\u5217\u51FA\u5168\u90E8 agent",
1721
- " /switch <name> \u6309\u540D\u79F0\u3001\u522B\u540D\u6216 key \u76F4\u63A5\u5207\u6362\uFF08\u522B\u540D\uFF1A/agent\uFF09",
1722
- " /agents \u5217\u51FA\u5E73\u53F0 agent \u5FEB\u6377\u65B9\u5F0F",
1723
- " /history \u4ECE\u6700\u8FD1\u5BF9\u8BDD\u4E2D\u9009\u62E9\u5E76\u6062\u590D\uFF08\u2191\u2193\uFF0CEnter\uFF09",
1724
- " /resume <dialogId> \u6309 id \u76F4\u63A5\u6062\u590D\u5BF9\u8BDD",
1725
- " /lang <zh|en> \u5207\u6362\u754C\u9762\u8BED\u8A00",
1726
- " /copy \u590D\u5236\u6700\u540E\u4E00\u6761\u56DE\u590D\u5230\u526A\u8D34\u677F",
1727
- " /copy view \u6253\u5F00\u539F\u751F\u56DE\u6EDA\u8FDB\u884C\u8DE8\u5C4F\u62D6\u9009\uFF08Ctrl+O\uFF09",
1728
- " /mouse <on|off> \u5207\u6362\u9F20\u6807\u6A21\u5F0F\uFF08off \u540E\u53EF\u76F4\u63A5\u62D6\u9009\u6587\u672C\uFF09",
1729
- " /doc \u5217\u51FA\u5DF2\u6302\u8F7D\u7684\u6587\u6863",
1730
- " /doc attach <doc> \u6302\u8F7D\u6587\u6863\u5230\u5F53\u524D\u5DE5\u4F5C\u533A",
1731
- " /skill \u5217\u51FA\u5DF2\u6302\u8F7D\u7684\u6280\u80FD",
1732
- " /skill attach <ref> \u6302\u8F7D\u6280\u80FD\uFF08dbKey\u3001\u540D\u79F0\u6216 SKILL.md \u8DEF\u5F84\uFF09",
1733
- " /skill detach <ref> \u5378\u8F7D\u6307\u5B9A\u6280\u80FD",
1734
- " /skill clear \u5378\u8F7D\u5168\u90E8\u6280\u80FD",
1735
- " /customize \u63CF\u8FF0\u4F60\u60F3\u600E\u4E48\u8C03\u6559 nolo",
1736
- " /tasks \u5217\u51FA\u540E\u53F0\u5B50\u8FDB\u7A0B\u4EFB\u52A1\uFF08\u522B\u540D\uFF1A/jobs, /procs\uFF09",
1737
- " /stop <pid|all> \u505C\u6B62\u6307\u5B9A\u7684\u540E\u53F0\u4EFB\u52A1",
1738
- " /login \u67E5\u770B\u767B\u5F55 / \u914D\u7F6E\u63D0\u793A",
1739
- " /profile \u67E5\u770B\u5F53\u524D\u914D\u7F6E\u73AF\u5883",
1740
- " /update \u66F4\u65B0 nolo CLI",
1741
- " /version \u67E5\u770B\u7248\u672C\u4E0E\u66F4\u65B0\u63D0\u793A",
1742
- " /exit \u9000\u51FA\u5DE5\u4F5C\u533A",
1743
- "",
1744
- "\u4E5F\u53EF\u4EE5\u76F4\u63A5\u8F93\u5165\u81EA\u7136\u8BED\u8A00\u3002\u7B80\u5355\u7684\u8BFB\u53D6/\u72B6\u6001\u8BF7\u6C42\u4F1A\u8D70 CLI \u547D\u4EE4\uFF0C\u5176\u4F59\u4EA4\u7ED9\u5F53\u524D agent\u3002"
1745
- ].join("\n")
1489
+ mono: {
1490
+ light: {
1491
+ accent: { hex: "FF9500", ansiFallback: "\x1B[33m" },
1492
+ chrome: { hex: "9DA6AF", ansiFallback: "\x1B[90m" },
1493
+ success: { hex: "16A34A", ansiFallback: "\x1B[32m" },
1494
+ warning: { hex: "D97706", ansiFallback: "\x1B[33m" },
1495
+ info: { hex: "2563EB", ansiFallback: "\x1B[34m" },
1496
+ danger: { hex: "DC2626", ansiFallback: "\x1B[31m" },
1497
+ muted: { hex: "7B8793", ansiFallback: "\x1B[90m" }
1498
+ },
1499
+ dark: {
1500
+ accent: { hex: "FFBF66", ansiFallback: "\x1B[33m" },
1501
+ chrome: { hex: "7B8793", ansiFallback: "\x1B[90m" },
1502
+ success: { hex: "4DE800", ansiFallback: "\x1B[32m" },
1503
+ warning: { hex: "FCD34D", ansiFallback: "\x1B[33m" },
1504
+ info: { hex: "60A5FA", ansiFallback: "\x1B[34m" },
1505
+ danger: { hex: "F87171", ansiFallback: "\x1B[31m" },
1506
+ muted: { hex: "C1C7CD", ansiFallback: "\x1B[90m" }
1507
+ }
1508
+ }
1509
+ };
1510
+ var activeThemeName = "catppuccin";
1511
+ function getActiveThemeName() {
1512
+ return activeThemeName;
1513
+ }
1514
+ function setActiveThemeName(name) {
1515
+ if (THEME_PALETTES[name]) {
1516
+ activeThemeName = name;
1517
+ return true;
1518
+ }
1519
+ return false;
1520
+ }
1521
+ var activeBrightness = null;
1522
+ function getActiveBrightness() {
1523
+ return activeBrightness;
1524
+ }
1525
+ function setActiveBrightness(brightness) {
1526
+ activeBrightness = brightness;
1527
+ }
1528
+ var activeDensity = "spacious";
1529
+ function getActiveDensity() {
1530
+ return activeDensity;
1531
+ }
1532
+ function setActiveDensity(density) {
1533
+ activeDensity = density;
1534
+ }
1535
+ var TRUECOLOR_TERM_PROGRAMS = /* @__PURE__ */ new Set([
1536
+ "iterm.app",
1537
+ "wezterm",
1538
+ "ghostty",
1539
+ "vscode",
1540
+ "hyper",
1541
+ "tabby",
1542
+ "rio",
1543
+ "warpterminal"
1544
+ ]);
1545
+ function supportsTruecolor(env = process.env) {
1546
+ const explicit = (env.NOLO_TUI_TRUECOLOR ?? "").trim().toLowerCase();
1547
+ if (explicit === "1" || explicit === "true") return true;
1548
+ if (explicit === "0" || explicit === "false") return false;
1549
+ if (/truecolor|24bit/i.test(env.COLORTERM ?? "")) return true;
1550
+ if (/direct|24bit/i.test(env.TERM ?? "")) return true;
1551
+ const program = (env.TERM_PROGRAM ?? "").toLowerCase();
1552
+ return TRUECOLOR_TERM_PROGRAMS.has(program);
1553
+ }
1554
+ function resolveTuiBrightness(env = process.env) {
1555
+ const explicit = (env.NOLO_TUI_THEME ?? "").trim().toLowerCase();
1556
+ if (explicit === "light") return "light";
1557
+ if (explicit === "dark") return "dark";
1558
+ if (activeBrightness) return activeBrightness;
1559
+ const colorfgbg = env.COLORFGBG ?? "";
1560
+ if (colorfgbg) {
1561
+ const parts = colorfgbg.split(";");
1562
+ const bg = parts.length > 1 ? Number.parseInt(parts[1] ?? "", 10) : NaN;
1563
+ if (!Number.isNaN(bg) && bg >= 0 && bg <= 6) return "dark";
1564
+ if (!Number.isNaN(bg) && bg >= 7 && bg <= 15) return "light";
1565
+ }
1566
+ return "dark";
1567
+ }
1568
+ function hexToSgr(hex) {
1569
+ const r = Number.parseInt(hex.slice(0, 2), 16);
1570
+ const g = Number.parseInt(hex.slice(2, 4), 16);
1571
+ const b = Number.parseInt(hex.slice(4, 6), 16);
1572
+ return `\x1B[38;2;${r};${g};${b}m`;
1573
+ }
1574
+ var SURFACE_HEX = {
1575
+ trail: { light: "E4E8EC", dark: "313244" },
1576
+ catppuccin: { light: "E8ECF0", dark: "161B22" },
1577
+ wave: { light: "E5E1D6", dark: "2A2A37" },
1578
+ iris: { light: "E9E8F2", dark: "2A2740" },
1579
+ rose: { light: "F2EAE4", dark: "26233A" },
1580
+ mono: { light: "E8EAED", dark: "2A2A2A" }
1581
+ };
1582
+ var TERMINAL_BASE = {
1583
+ dark: "1E1E2E",
1584
+ light: "FFFFFF"
1585
+ };
1586
+ var activeTerminalBaseHex = null;
1587
+ function setActiveTerminalBaseHex(hex) {
1588
+ if (hex === null) {
1589
+ activeTerminalBaseHex = null;
1590
+ return;
1591
+ }
1592
+ activeTerminalBaseHex = readHexChannels(hex.replace(/^#/, "").toUpperCase()) ? hex.replace(/^#/, "").toUpperCase() : null;
1593
+ }
1594
+ function resolveTerminalBaseHex(brightness) {
1595
+ return activeTerminalBaseHex ?? TERMINAL_BASE[brightness];
1596
+ }
1597
+ function readHexChannels(hex) {
1598
+ if (!/^[0-9A-F]{6}$/.test(hex)) return null;
1599
+ return [
1600
+ Number.parseInt(hex.slice(0, 2), 16),
1601
+ Number.parseInt(hex.slice(2, 4), 16),
1602
+ Number.parseInt(hex.slice(4, 6), 16)
1603
+ ];
1604
+ }
1605
+ function hexToBgSgr(hex) {
1606
+ const channels = readHexChannels(hex.replace(/^#/, "").toUpperCase());
1607
+ if (!channels) return "";
1608
+ const [r, g, b] = channels;
1609
+ return `\x1B[48;2;${r};${g};${b}m`;
1610
+ }
1611
+ function blendHex(fg, base, weight) {
1612
+ const w = Math.min(1, Math.max(0, weight));
1613
+ const normFg = fg.replace(/^#/, "").toUpperCase();
1614
+ const normBase = base.replace(/^#/, "").toUpperCase();
1615
+ if (w === 0) return normBase;
1616
+ if (w === 1) return normFg;
1617
+ if (!readHexChannels(normFg) || !readHexChannels(normBase)) return normBase;
1618
+ const fgR = Number.parseInt(normFg.slice(0, 2), 16);
1619
+ const fgG = Number.parseInt(normFg.slice(2, 4), 16);
1620
+ const fgB = Number.parseInt(normFg.slice(4, 6), 16);
1621
+ const baseR = Number.parseInt(normBase.slice(0, 2), 16);
1622
+ const baseG = Number.parseInt(normBase.slice(2, 4), 16);
1623
+ const baseB = Number.parseInt(normBase.slice(4, 6), 16);
1624
+ const r = Math.round(baseR + (fgR - baseR) * w).toString(16).padStart(2, "0").toUpperCase();
1625
+ const g = Math.round(baseG + (fgG - baseG) * w).toString(16).padStart(2, "0").toUpperCase();
1626
+ const b = Math.round(baseB + (fgB - baseB) * w).toString(16).padStart(2, "0").toUpperCase();
1627
+ return `${r}${g}${b}`;
1628
+ }
1629
+ function diffLineSequences(env = process.env, brightness = resolveTuiBrightness(env)) {
1630
+ if (!supportsTruecolor(env)) return null;
1631
+ const palette = THEME_PALETTES[activeThemeName] ?? THEME_PALETTES.trail;
1632
+ const successHex = palette[brightness].success.hex;
1633
+ const dangerHex = palette[brightness].danger.hex;
1634
+ const infoHex = palette[brightness].info.hex;
1635
+ const weight = brightness === "dark" ? 0.18 : 0.12;
1636
+ const baseHex = resolveTerminalBaseHex(brightness);
1637
+ const addedBgHex = blendHex(successHex, baseHex, weight);
1638
+ const removedBgHex = blendHex(dangerHex, baseHex, weight);
1639
+ const hunkBgHex = blendHex(infoHex, baseHex, weight * 0.6);
1640
+ const successSgr = hexToSgr(successHex);
1641
+ const dangerSgr = hexToSgr(dangerHex);
1642
+ const infoSgr = hexToSgr(infoHex);
1643
+ return {
1644
+ added: {
1645
+ fg: successSgr,
1646
+ bg: hexToBgSgr(addedBgHex),
1647
+ bar: `${successSgr}\u258C\x1B[39m`
1648
+ },
1649
+ removed: {
1650
+ fg: dangerSgr,
1651
+ bg: hexToBgSgr(removedBgHex),
1652
+ bar: `${dangerSgr}\u258C\x1B[39m`
1653
+ },
1654
+ hunk: {
1655
+ fg: infoSgr,
1656
+ bg: hexToBgSgr(hunkBgHex),
1657
+ bar: " "
1658
+ },
1659
+ context: { fg: "", bg: "", bar: " " }
1660
+ };
1661
+ }
1662
+ function renderDiffLine(args) {
1663
+ const { kind, text } = args;
1664
+ const env = args.env ?? process.env;
1665
+ const colorEnabled = args.colorEnabled ?? resolveCliColorEnabled();
1666
+ if (!colorEnabled) return text;
1667
+ const seqs = diffLineSequences(env);
1668
+ if (!seqs) {
1669
+ if (kind === "context") return text;
1670
+ const token = kind === "added" ? "success" : kind === "removed" ? "danger" : "info";
1671
+ return `${themeColorSequence(token, env)}${text}\x1B[39m`;
1672
+ }
1673
+ const seq = seqs[kind];
1674
+ const padding = args.padTo === void 0 ? "" : " ".repeat(Math.max(0, args.padTo - displayWidth(text)));
1675
+ return `${seq.bar}${seq.bg}${seq.fg}${text}${padding}\x1B[0m`;
1676
+ }
1677
+ function surfaceBackgroundSequence(env = process.env, brightness = resolveTuiBrightness(env)) {
1678
+ if (!supportsTruecolor(env)) return "";
1679
+ return hexToBgSgr((SURFACE_HEX[activeThemeName] ?? SURFACE_HEX.trail)[brightness]);
1680
+ }
1681
+ function themeColorSequence(token, env = process.env, brightness = resolveTuiBrightness(env)) {
1682
+ const palette = THEME_PALETTES[activeThemeName] ?? THEME_PALETTES.trail;
1683
+ const entry = palette[brightness][token];
1684
+ return supportsTruecolor(env) ? hexToSgr(entry.hex) : entry.ansiFallback;
1685
+ }
1686
+ function themeText(text, token, colorEnabled = resolveCliColorEnabled(), env = process.env) {
1687
+ if (!colorEnabled) return text;
1688
+ return `${themeColorSequence(token, env)}${text}\x1B[39m`;
1689
+ }
1690
+ function classifyDiffLine(line) {
1691
+ if (line.startsWith("@@")) return "hunk";
1692
+ if (line.startsWith("+") && !line.startsWith("+++")) return "added";
1693
+ if (line.startsWith("-") && !line.startsWith("---")) return "removed";
1694
+ return "context";
1695
+ }
1696
+
1697
+ // packages/cli/client/assistantOutput.ts
1698
+ var STYLE = {
1699
+ reset: "\x1B[0m",
1700
+ bold: "\x1B[1m",
1701
+ dim: "\x1B[2m"
1702
+ };
1703
+ function colorSeq(token, brightness) {
1704
+ return themeColorSequence(token, process.env, brightness);
1705
+ }
1706
+ function splitTableCells(line) {
1707
+ const trimmed = line.trim();
1708
+ if (!trimmed.includes("|")) return [];
1709
+ const core = trimmed.replace(/^\|/, "").replace(/\|$/, "");
1710
+ return core.split("|").map((cell) => cell.trim()).filter(Boolean);
1711
+ }
1712
+ function isTableSeparator(line) {
1713
+ const cells = splitTableCells(line);
1714
+ return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/.test(cell));
1715
+ }
1716
+ function isTableRow(line) {
1717
+ const cells = splitTableCells(line);
1718
+ return cells.length >= 2 && cells.some((cell) => cell.length > 0);
1719
+ }
1720
+ function isPipeWrappedTableRow(line) {
1721
+ const trimmed = line.trim();
1722
+ return trimmed.startsWith("|") && trimmed.endsWith("|") && isTableRow(line);
1723
+ }
1724
+ function isCodeFenceLine(line) {
1725
+ return /^\s*```/.test(line);
1726
+ }
1727
+ function readFenceLanguage(line) {
1728
+ const m = line.match(/^\s*```\s*([a-zA-Z0-9+#_-]*)/);
1729
+ return m ? (m[1] ?? "").toLowerCase() : "";
1730
+ }
1731
+ function normalizeCodeLang(raw) {
1732
+ switch (raw) {
1733
+ case "js":
1734
+ case "jsx":
1735
+ case "ts":
1736
+ case "tsx":
1737
+ case "javascript":
1738
+ case "typescript":
1739
+ return "js";
1740
+ case "py":
1741
+ case "python":
1742
+ return "py";
1743
+ case "sh":
1744
+ case "bash":
1745
+ case "zsh":
1746
+ case "shell":
1747
+ case "console":
1748
+ return "sh";
1749
+ case "json":
1750
+ return "json";
1751
+ case "diff":
1752
+ return "diff";
1753
+ default:
1754
+ return "unknown";
1755
+ }
1756
+ }
1757
+ var KEYWORDS = {
1758
+ js: /* @__PURE__ */ new Set([
1759
+ "const",
1760
+ "let",
1761
+ "var",
1762
+ "function",
1763
+ "return",
1764
+ "if",
1765
+ "else",
1766
+ "for",
1767
+ "while",
1768
+ "class",
1769
+ "new",
1770
+ "await",
1771
+ "async",
1772
+ "import",
1773
+ "export",
1774
+ "from",
1775
+ "type",
1776
+ "interface",
1777
+ "extends",
1778
+ "implements",
1779
+ "null",
1780
+ "undefined",
1781
+ "true",
1782
+ "false"
1783
+ ]),
1784
+ py: /* @__PURE__ */ new Set([
1785
+ "def",
1786
+ "class",
1787
+ "return",
1788
+ "if",
1789
+ "elif",
1790
+ "else",
1791
+ "for",
1792
+ "while",
1793
+ "import",
1794
+ "from",
1795
+ "as",
1796
+ "with",
1797
+ "try",
1798
+ "except",
1799
+ "finally",
1800
+ "lambda",
1801
+ "None",
1802
+ "True",
1803
+ "False",
1804
+ "self"
1805
+ ]),
1806
+ sh: /* @__PURE__ */ new Set([
1807
+ "if",
1808
+ "then",
1809
+ "else",
1810
+ "fi",
1811
+ "for",
1812
+ "do",
1813
+ "done",
1814
+ "while",
1815
+ "case",
1816
+ "esac",
1817
+ "function",
1818
+ "export",
1819
+ "local",
1820
+ "return",
1821
+ "source",
1822
+ "echo",
1823
+ "cd"
1824
+ ]),
1825
+ json: /* @__PURE__ */ new Set(["true", "false", "null"]),
1826
+ diff: /* @__PURE__ */ new Set([])
1827
+ };
1828
+ function scanStringCommentRegions(line, lang) {
1829
+ const regions = [];
1830
+ const n = line.length;
1831
+ let i = 0;
1832
+ const commentMarkers = lang === "py" || lang === "sh" ? ["#"] : ["//"];
1833
+ while (i < n) {
1834
+ const ch = line[i];
1835
+ if (ch === "'" || ch === '"' || lang === "js" && ch === "`") {
1836
+ const quote = ch;
1837
+ const start = i;
1838
+ i += 1;
1839
+ while (i < n) {
1840
+ if (line[i] === "\\") {
1841
+ i += 2;
1842
+ continue;
1843
+ }
1844
+ if (line[i] === quote) {
1845
+ i += 1;
1846
+ break;
1847
+ }
1848
+ i += 1;
1849
+ }
1850
+ regions.push({ start, end: i, kind: "string" });
1851
+ continue;
1852
+ }
1853
+ let matched = false;
1854
+ for (const marker of commentMarkers) {
1855
+ if (line.startsWith(marker, i)) {
1856
+ regions.push({ start: i, end: n, kind: "comment" });
1857
+ i = n;
1858
+ matched = true;
1859
+ break;
1860
+ }
1861
+ }
1862
+ if (matched) continue;
1863
+ i += 1;
1864
+ }
1865
+ return regions;
1866
+ }
1867
+ function highlightCodeLine(line, lang, brightness) {
1868
+ const info = colorSeq("info", brightness);
1869
+ if (lang === "unknown") {
1870
+ return `${info}${line}${STYLE.reset}`;
1871
+ }
1872
+ if (lang === "diff") {
1873
+ return renderDiffLine({ kind: classifyDiffLine(line), text: line, colorEnabled: true });
1874
+ }
1875
+ const regions = scanStringCommentRegions(line, lang);
1876
+ const keywords = KEYWORDS[lang];
1877
+ const accent = colorSeq("accent", brightness);
1878
+ const success = colorSeq("success", brightness);
1879
+ const warning = colorSeq("warning", brightness);
1880
+ const chrome = colorSeq("chrome", brightness);
1881
+ const dim = STYLE.dim;
1882
+ const reset = STYLE.reset;
1883
+ const out = [];
1884
+ let cursor = 0;
1885
+ for (const region of regions) {
1886
+ if (region.start > cursor) {
1887
+ out.push(emitPlainGap(line.slice(cursor, region.start), keywords, info, accent, warning, reset));
1888
+ }
1889
+ const text = line.slice(region.start, region.end);
1890
+ if (region.kind === "string") {
1891
+ out.push(`${success}${text}${reset}`);
1892
+ } else {
1893
+ out.push(`${chrome}${dim}${text}${reset}`);
1894
+ }
1895
+ cursor = region.end;
1896
+ }
1897
+ if (cursor < line.length) {
1898
+ out.push(emitPlainGap(line.slice(cursor), keywords, info, accent, warning, reset));
1899
+ }
1900
+ return out.join("");
1901
+ }
1902
+ function emitPlainGap(text, keywords, info, accent, warning, reset) {
1903
+ const parts = [];
1904
+ const re = /([A-Za-z_]\w*)|(\d+(?:\.\d+)?)|([\s\S])/g;
1905
+ let last = 0;
1906
+ let m;
1907
+ let started = false;
1908
+ while ((m = re.exec(text)) !== null) {
1909
+ if (!started) {
1910
+ started = true;
1911
+ }
1912
+ if (m.index > last) {
1913
+ parts.push(`${info}${text.slice(last, m.index)}`);
1914
+ }
1915
+ if (m[1] !== void 0) {
1916
+ const word = m[1];
1917
+ if (keywords.has(word)) {
1918
+ parts.push(`${accent}${word}${reset}`);
1919
+ } else {
1920
+ parts.push(`${info}${word}`);
1921
+ }
1922
+ } else if (m[2] !== void 0) {
1923
+ parts.push(`${warning}${m[2]}${reset}`);
1924
+ } else {
1925
+ parts.push(`${info}${m[3]}`);
1926
+ }
1927
+ last = re.lastIndex;
1928
+ }
1929
+ if (last < text.length) {
1930
+ parts.push(`${info}${text.slice(last)}`);
1931
+ }
1932
+ return parts.join("");
1933
+ }
1934
+ function tableRowToBullet(line) {
1935
+ const row = splitTableCells(line);
1936
+ const label = row[0] ?? "";
1937
+ const detail = row.slice(1).join(" \u2014 ").trim();
1938
+ return detail ? ` \u2022 ${label} \u2014 ${detail}` : ` \u2022 ${label}`;
1939
+ }
1940
+ var UNORDERED_LIST_RE = /^(\s*)([-*+])\s+(.+)$/;
1941
+ var ORDERED_LIST_RE = /^(\s*)(\d+)\.\s+(.+)$/;
1942
+ var TASK_LIST_RE = /^(\s*)([-*+])\s+\[([ xX])\]\s+(.+)$/;
1943
+ function normalizeListLine(line) {
1944
+ const task = line.match(TASK_LIST_RE);
1945
+ if (task) {
1946
+ const checked = task[3] === "x" || task[3] === "X";
1947
+ return `${task[1]}${checked ? "\u2611" : "\u2610"} ${task[4]}`;
1948
+ }
1949
+ const unordered = line.match(UNORDERED_LIST_RE);
1950
+ if (unordered) {
1951
+ return `${unordered[1]}\u2022 ${unordered[3]}`;
1952
+ }
1953
+ const ordered = line.match(ORDERED_LIST_RE);
1954
+ if (ordered) {
1955
+ return `${ordered[1]}${ordered[2]}. ${ordered[3]}`;
1956
+ }
1957
+ return line;
1958
+ }
1959
+ function convertMarkdownTablesForTerminal(text) {
1960
+ const lines = text.split("\n");
1961
+ const out = [];
1962
+ let inFence = false;
1963
+ for (let index = 0; index < lines.length; index += 1) {
1964
+ const line = lines[index] ?? "";
1965
+ if (isCodeFenceLine(line)) {
1966
+ inFence = !inFence;
1967
+ out.push(line);
1968
+ continue;
1969
+ }
1970
+ if (inFence) {
1971
+ out.push(line);
1972
+ continue;
1973
+ }
1974
+ const next = lines[index + 1] ?? "";
1975
+ if (isTableRow(line) && isTableSeparator(next)) {
1976
+ index += 1;
1977
+ while (index + 1 < lines.length && isTableRow(lines[index + 1] ?? "") && !isTableSeparator(lines[index + 1] ?? "")) {
1978
+ index += 1;
1979
+ out.push(tableRowToBullet(lines[index] ?? ""));
1980
+ }
1981
+ if (out.length > 0 && out[out.length - 1] !== "") out.push("");
1982
+ continue;
1983
+ }
1984
+ if (isPipeWrappedTableRow(line)) {
1985
+ if (!isTableSeparator(line)) out.push(tableRowToBullet(line));
1986
+ continue;
1987
+ }
1988
+ out.push(normalizeListLine(line));
1989
+ }
1990
+ return out.join("\n");
1991
+ }
1992
+ function polishAssistantStructure(text, options = {}) {
1993
+ const converted = convertMarkdownTablesForTerminal(text).replace(/\r\n/g, "\n").replace(/\x00/g, "");
1994
+ const lines = converted.split("\n");
1995
+ let inFence = false;
1996
+ const maskedLines = lines.map((line, index) => {
1997
+ if (isCodeFenceLine(line)) {
1998
+ inFence = !inFence;
1999
+ return line;
2000
+ }
2001
+ if (inFence) {
2002
+ return `\0F${index}\0`;
2003
+ }
2004
+ return line;
2005
+ });
2006
+ const afterHeading = maskedLines.join("\n").replace(/([^\n])\n(#{1,3} )/g, "$1\n\n$2").replace(/^(#{1,3} .+)\n(?!\n)/gm, "$1\n\n").replace(/\n{4,}/g, "\n\n\n");
2007
+ const LIST_LIKE = /^\s*(?:•|☐|☑|\d+\.)\s|^\s*[\u2460-\u2473]/;
2008
+ const headingLines = afterHeading.split("\n");
2009
+ const breathed = [];
2010
+ for (let i = 0; i < headingLines.length; i++) {
2011
+ breathed.push(headingLines[i]);
2012
+ const cur = headingLines[i];
2013
+ const next = headingLines[i + 1];
2014
+ if (cur === "" || next === void 0 || next === "") continue;
2015
+ if (LIST_LIKE.test(cur) === LIST_LIKE.test(next)) continue;
2016
+ breathed.push("");
2017
+ }
2018
+ const polishedMasked = breathed.join("\n");
2019
+ const polished = polishedMasked.replace(
2020
+ /\x00F(\d+)\x00/g,
2021
+ (_, id) => lines[Number(id)] ?? ""
2022
+ );
2023
+ return options.trimEdges === false ? polished : polished.trim();
2024
+ }
2025
+ function renderMarkdownLink(match, text, url) {
2026
+ const visible = `${text} (${url})`;
2027
+ return `\x1B]8;;${url}\x1B\\${visible}\x1B]8;;\x1B\\`;
2028
+ }
2029
+ var MARKDOWN_LINK_RE = /\[([^\]]+)\]\(([^)\s]+)\)/g;
2030
+ function styleInlineMarkdown(line, brightness) {
2031
+ const inlineCode = colorSeq("muted", brightness);
2032
+ const reset = STYLE.reset;
2033
+ const bold = STYLE.bold;
2034
+ const dim = STYLE.dim;
2035
+ return line.replace(MARKDOWN_LINK_RE, (m, t2, u) => renderMarkdownLink(m, t2, u)).replace(/`([^`]+)`/g, `${inlineCode}$1${reset}`).replace(/\*\*(.+?)\*\*/g, `${bold}$1${reset}`).replace(/(?<!\*)\*([^*]+?)\*(?!\*)/g, `${dim}$1${reset}`).replace(/~~([^~]+?)~~/g, `${dim}\x1B[9m$1\x1B[29m${reset}`);
2036
+ }
2037
+ function styleRichMarkdownLine(line, brightness) {
2038
+ const heading = line.match(/^(#{1,3})\s+(.+)$/);
2039
+ if (heading) {
2040
+ const level = heading[1].length;
2041
+ const title = heading[2];
2042
+ if (level === 1) {
2043
+ return `${STYLE.bold}\x1B[4m${colorSeq("accent", brightness)}${title}${STYLE.reset}`;
2044
+ }
2045
+ if (level === 2) {
2046
+ return `${STYLE.bold}${colorSeq("warning", brightness)}${title}${STYLE.reset}`;
2047
+ }
2048
+ return `${STYLE.bold}${colorSeq("info", brightness)}${title}${STYLE.reset}`;
2049
+ }
2050
+ if (line.startsWith("\u8FDB\u5165 nolo-plan")) {
2051
+ return `${colorSeq("chrome", brightness)}${STYLE.dim}${line}${STYLE.reset}`;
2052
+ }
2053
+ const blockquote = line.match(/^>\s?(.*)$/);
2054
+ if (blockquote) {
2055
+ const border = colorSeq("chrome", brightness);
2056
+ const content = blockquote[1];
2057
+ return `${border}\u2502${STYLE.reset} ${STYLE.dim}${content}${STYLE.reset}`;
2058
+ }
2059
+ if (/^---+$/.test(line.trim())) {
2060
+ return `${colorSeq("chrome", brightness)}${"\u2500".repeat(Math.min(line.trim().length, 40))}${STYLE.reset}`;
2061
+ }
2062
+ const bullet = line.match(/^(\s*)(•)\s(.+)$/);
2063
+ if (bullet) {
2064
+ const styled = styleInlineMarkdown(bullet[3], brightness);
2065
+ return `${bullet[1]}${colorSeq("accent", brightness)}\u2022${STYLE.reset} ${styled}`;
2066
+ }
2067
+ const ordered = line.match(/^(\s*)(\d+\.)\s(.+)$/);
2068
+ if (ordered) {
2069
+ const styled = styleInlineMarkdown(ordered[3], brightness);
2070
+ return `${ordered[1]}${colorSeq("accent", brightness)}${ordered[2]}${STYLE.reset} ${styled}`;
2071
+ }
2072
+ const task = line.match(/^(\s*)(☐|☑)\s(.+)$/);
2073
+ if (task) {
2074
+ const styled = styleInlineMarkdown(task[3], brightness);
2075
+ return `${task[1]}${colorSeq("accent", brightness)}${task[2]}${STYLE.reset} ${styled}`;
2076
+ }
2077
+ return styleInlineMarkdown(line, brightness);
2078
+ }
2079
+ function formatAssistantDisplay(text, options = {}) {
2080
+ const brightness = resolveTuiBrightness();
2081
+ const polished = polishAssistantStructure(text, options);
2082
+ let inFence = false;
2083
+ let fenceLang = "unknown";
2084
+ return polished.split("\n").map((line) => {
2085
+ if (isCodeFenceLine(line)) {
2086
+ if (inFence) {
2087
+ fenceLang = "unknown";
2088
+ } else {
2089
+ fenceLang = normalizeCodeLang(readFenceLanguage(line));
2090
+ }
2091
+ inFence = !inFence;
2092
+ return `${STYLE.dim}${line}${STYLE.reset}`;
2093
+ }
2094
+ if (inFence) {
2095
+ return highlightCodeLine(line, fenceLang, brightness);
2096
+ }
2097
+ return styleRichMarkdownLine(line, brightness);
2098
+ }).join("\n");
2099
+ }
2100
+ function emitFormattedAssistantBlock(write, text, trailingNewline = false) {
2101
+ if (!text) return;
2102
+ write(formatAssistantDisplay(text, { trimEdges: false }));
2103
+ if (trailingNewline) write("\n");
2104
+ }
2105
+ function classifyStreamLine(line) {
2106
+ if (line === "") return "blank";
2107
+ if (TASK_LIST_RE.test(line) || UNORDERED_LIST_RE.test(line) || ORDERED_LIST_RE.test(line) || /^\s*(?:•|☐|☑)\s/.test(line) || /^\s*[\u2460-\u2473]/.test(line)) {
2108
+ return "list";
2109
+ }
2110
+ return "prose";
2111
+ }
2112
+ function needsListProseBreath(prev, next) {
2113
+ if (prev === null || prev === "blank" || next === "blank") return false;
2114
+ const prevList = prev === "list";
2115
+ const nextList = next === "list";
2116
+ return prevList !== nextList;
2117
+ }
2118
+ function createRenderAwareStreamWriter(args) {
2119
+ const brightness = resolveTuiBrightness();
2120
+ let buffer = "";
2121
+ let inFence = false;
2122
+ let fenceLang = "unknown";
2123
+ let lastKind = null;
2124
+ const emitBreathIfNeeded = (nextKind) => {
2125
+ if (needsListProseBreath(lastKind, nextKind)) args.write("\n");
2126
+ };
2127
+ const flushCompleteBlocks = () => {
2128
+ while (buffer.includes("\n")) {
2129
+ const lines = buffer.split("\n");
2130
+ if (lines.length < 2) break;
2131
+ const firstLine = lines[0] ?? "";
2132
+ if (isCodeFenceLine(firstLine)) {
2133
+ if (inFence) {
2134
+ fenceLang = "unknown";
2135
+ } else {
2136
+ fenceLang = normalizeCodeLang(readFenceLanguage(firstLine));
2137
+ }
2138
+ inFence = !inFence;
2139
+ emitBreathIfNeeded("other");
2140
+ args.write(`${STYLE.dim}${firstLine}${STYLE.reset}
2141
+ `);
2142
+ lastKind = "other";
2143
+ buffer = lines.slice(1).join("\n");
2144
+ continue;
2145
+ }
2146
+ if (inFence) {
2147
+ args.write(`${highlightCodeLine(firstLine, fenceLang, brightness)}
2148
+ `);
2149
+ lastKind = "other";
2150
+ buffer = lines.slice(1).join("\n");
2151
+ continue;
2152
+ }
2153
+ if (isTableRow(firstLine)) {
2154
+ const nextLineComplete = lines.length > 2;
2155
+ if (!nextLineComplete) break;
2156
+ if (isTableSeparator(lines[1] ?? "")) {
2157
+ let end = 2;
2158
+ while (end < lines.length && isTableRow(lines[end] ?? "") && !isTableSeparator(lines[end] ?? "")) {
2159
+ end += 1;
2160
+ }
2161
+ const tableComplete = end < lines.length - 1;
2162
+ if (!tableComplete) break;
2163
+ emitBreathIfNeeded("list");
2164
+ emitFormattedAssistantBlock(
2165
+ args.write,
2166
+ lines.slice(0, end).join("\n"),
2167
+ true
2168
+ );
2169
+ lastKind = "list";
2170
+ buffer = lines.slice(end).join("\n");
2171
+ continue;
2172
+ }
2173
+ }
2174
+ const kind = classifyStreamLine(firstLine);
2175
+ emitBreathIfNeeded(kind);
2176
+ emitFormattedAssistantBlock(args.write, firstLine, true);
2177
+ lastKind = kind;
2178
+ buffer = lines.slice(1).join("\n");
2179
+ }
2180
+ };
2181
+ return {
2182
+ push(chunk) {
2183
+ if (!chunk) return;
2184
+ buffer += chunk;
2185
+ flushCompleteBlocks();
2186
+ },
2187
+ flush() {
2188
+ if (!buffer) return;
2189
+ if (inFence) {
2190
+ args.write(buffer);
2191
+ } else {
2192
+ emitFormattedAssistantBlock(args.write, buffer);
2193
+ }
2194
+ buffer = "";
2195
+ }
2196
+ };
2197
+ }
2198
+
2199
+ // packages/cli/client/thinkingOutput.ts
2200
+ var THINK_OPEN = /<think>/i;
2201
+ var THINK_CLOSE = /<\/think>/i;
2202
+ var COLLAPSED_MARKER = /^\s*▸ 思考已折叠\s*$/;
2203
+ function normalizeThinkingDisplayMode(raw, fallback = "hide") {
2204
+ const normalized = asTrimmedLowercaseString(raw);
2205
+ if (normalized === "hide" || normalized === "off" || normalized === "false" || normalized === "0") {
2206
+ return "hide";
2207
+ }
2208
+ if (normalized === "marker" || normalized === "collapsed" || normalized === "fold") {
2209
+ return "marker";
2210
+ }
2211
+ if (normalized === "show" || normalized === "on" || normalized === "true" || normalized === "1") {
2212
+ return "show";
2213
+ }
2214
+ return fallback;
2215
+ }
2216
+ function resolveThinkingDisplayMode(env = process.env) {
2217
+ return normalizeThinkingDisplayMode(env.NOLO_CLI_THINKING ?? env.NOLO_THINKING, "hide");
2218
+ }
2219
+ function collapseThinkingBlocks(text, mode = "hide") {
2220
+ if (mode === "show") return text;
2221
+ const replacement = mode === "marker" ? "\u25B8 \u601D\u8003\u5DF2\u6298\u53E0\n" : "";
2222
+ return text.replace(/<think>[\s\S]*?<\/think>\s*/gi, replacement).replace(/\n{3,}/g, "\n\n").trim();
2223
+ }
2224
+ function stripCollapsedThinkingMarkers(text) {
2225
+ return text.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n").replace(/\n{2,}/g, "\n").trim();
2226
+ }
2227
+ function formatAssistantTextForCli(text, mode = "hide") {
2228
+ const collapsed = collapseThinkingBlocks(text, mode);
2229
+ return mode === "hide" ? stripCollapsedThinkingMarkers(collapsed) : collapsed;
2230
+ }
2231
+ function createThinkingEventSink(arg1, arg2) {
2232
+ let mode = "hide";
2233
+ let write = () => {
2234
+ };
2235
+ if (typeof arg1 === "function") {
2236
+ write = arg1;
2237
+ if (typeof arg2 === "string") {
2238
+ mode = arg2;
2239
+ }
2240
+ } else {
2241
+ mode = arg1;
2242
+ if (typeof arg2 === "function") {
2243
+ write = arg2;
2244
+ }
2245
+ }
2246
+ if (mode === "hide") {
2247
+ return {
2248
+ push(_chunk) {
2249
+ }
2250
+ };
2251
+ }
2252
+ if (mode === "show") {
2253
+ return {
2254
+ push(chunk) {
2255
+ if (chunk) write(chunk);
2256
+ }
2257
+ };
2258
+ }
2259
+ let markerEmitted = false;
2260
+ return {
2261
+ push(chunk) {
2262
+ if (!chunk || markerEmitted) return;
2263
+ write("\n\u25B8 \u601D\u8003\u5DF2\u6298\u53E0\n");
2264
+ markerEmitted = true;
2265
+ }
2266
+ };
2267
+ }
2268
+ function createThinkingAwareStreamFilter(write, mode = "hide") {
2269
+ if (mode === "show") {
2270
+ return {
2271
+ push(chunk) {
2272
+ write(chunk);
2273
+ },
2274
+ flush() {
2275
+ }
2276
+ };
2277
+ }
2278
+ let pending = "";
2279
+ let insideThink = false;
2280
+ let markerEmitted = false;
2281
+ const emit = (chunk) => {
2282
+ if (chunk) write(chunk);
2283
+ };
2284
+ const emitMarker = () => {
2285
+ if (mode !== "marker" || markerEmitted) return;
2286
+ emit("\n\u25B8 \u601D\u8003\u5DF2\u6298\u53E0\n");
2287
+ markerEmitted = true;
2288
+ };
2289
+ const consume = (input) => {
2290
+ pending += input;
2291
+ while (pending.length > 0) {
2292
+ if (insideThink) {
2293
+ const closeMatch = pending.match(THINK_CLOSE);
2294
+ if (!closeMatch || closeMatch.index == null) {
2295
+ emitMarker();
2296
+ pending = "";
2297
+ return;
2298
+ }
2299
+ emitMarker();
2300
+ pending = pending.slice(closeMatch.index + closeMatch[0].length);
2301
+ insideThink = false;
2302
+ markerEmitted = false;
2303
+ continue;
2304
+ }
2305
+ const openMatch = pending.match(THINK_OPEN);
2306
+ if (!openMatch || openMatch.index == null) {
2307
+ const nextMarker = pending.indexOf("<");
2308
+ if (nextMarker === -1) {
2309
+ const cleaned = mode === "hide" ? pending.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : pending;
2310
+ emit(cleaned);
2311
+ pending = "";
2312
+ return;
2313
+ }
2314
+ if (nextMarker > 0) {
2315
+ const head = pending.slice(0, nextMarker);
2316
+ const cleaned = mode === "hide" ? head.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : head;
2317
+ emit(cleaned);
2318
+ pending = pending.slice(nextMarker);
2319
+ continue;
2320
+ }
2321
+ if (pending.length < 7) return;
2322
+ emit(pending[0]);
2323
+ pending = pending.slice(1);
2324
+ continue;
2325
+ }
2326
+ if (openMatch.index > 0) {
2327
+ const head = pending.slice(0, openMatch.index);
2328
+ const cleaned = mode === "hide" ? head.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : head;
2329
+ emit(cleaned);
2330
+ pending = pending.slice(openMatch.index);
2331
+ continue;
2332
+ }
2333
+ pending = pending.slice(openMatch[0].length);
2334
+ insideThink = true;
2335
+ markerEmitted = false;
2336
+ }
2337
+ };
2338
+ return {
2339
+ push(chunk) {
2340
+ consume(chunk);
2341
+ },
2342
+ flush() {
2343
+ if (insideThink) emitMarker();
2344
+ if (pending) {
2345
+ const cleaned = mode === "hide" ? pending.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : pending;
2346
+ emit(cleaned);
2347
+ }
2348
+ pending = "";
2349
+ insideThink = false;
2350
+ markerEmitted = false;
2351
+ }
2352
+ };
2353
+ }
2354
+
2355
+ // packages/cli/client/formatReadPathTree.ts
2356
+ import { homedir as osHomedir } from "node:os";
2357
+ function clipPathAware(value, max = 72) {
2358
+ const compact = compactWhitespace(value);
2359
+ if (compact.length <= max) return compact;
2360
+ if (compact.includes(" ") || !compact.includes("/") && !compact.includes("\\")) {
2361
+ return clipCompactText(value, max, "\u2026");
1746
2362
  }
1747
- };
1748
- function t(key) {
1749
- return STRINGS[key][currentLocale];
2363
+ const segments = compact.split(/[/\\]/);
2364
+ const filename = segments[segments.length - 1];
2365
+ if (filename.length >= max) {
2366
+ return clipCompactText(value, max, "\u2026");
2367
+ }
2368
+ const ELISION = "/\u2026/";
2369
+ let prefix = "";
2370
+ for (let i = 0; i < segments.length - 1; i += 1) {
2371
+ const candidate = segments.slice(0, i + 1).join("/");
2372
+ if (candidate.length + ELISION.length + filename.length > max) break;
2373
+ prefix = candidate;
2374
+ }
2375
+ if (prefix) return `${prefix}${ELISION}${filename}`;
2376
+ const filenameOnly = `\u2026/${filename}`;
2377
+ if (filenameOnly.length <= max) return filenameOnly;
2378
+ return clipCompactText(value, max, "\u2026");
1750
2379
  }
1751
- var TOOL_LABELS = {
1752
- // Local workspace tools (packages/agent-runtime/localWorkspaceTools.ts)
1753
- readFile: { en: "Read", zh: "\u8BFB\u53D6" },
1754
- writeFile: { en: "Write", zh: "\u5199\u5165" },
1755
- editFile: { en: "Edit", zh: "\u7F16\u8F91" },
1756
- listFiles: { en: "List", zh: "\u5217\u51FA" },
1757
- searchFiles: { en: "Search", zh: "\u641C\u7D22" },
1758
- globFiles: { en: "Glob", zh: "\u5339\u914D" },
1759
- execShell: { en: "Run", zh: "\u6267\u884C" },
1760
- runCommand: { en: "Run", zh: "\u6267\u884C" },
1761
- captureVisualState: { en: "Capture", zh: "\u622A\u5C4F" },
1762
- // Workspace / diagnostics
1763
- searchWorkspace: { en: "Search workspace", zh: "\u641C\u7D22\u5DE5\u4F5C\u533A" },
1764
- cliDoctor: { en: "Doctor", zh: "\u81EA\u68C0" },
1765
- cliWhoami: { en: "Whoami", zh: "\u67E5\u770B\u8EAB\u4EFD" },
1766
- checkEnv: { en: "Check env", zh: "\u68C0\u67E5\u73AF\u5883" },
1767
- configure: { en: "Configure", zh: "\u914D\u7F6E" },
1768
- notifyUser: { en: "Notify", zh: "\u901A\u77E5" },
1769
- // Docs / dialogs / spaces
1770
- readDoc: { en: "Read doc", zh: "\u8BFB\u53D6\u6587\u6863" },
1771
- createDoc: { en: "Create doc", zh: "\u65B0\u5EFA\u6587\u6863" },
1772
- updateDoc: { en: "Update doc", zh: "\u66F4\u65B0\u6587\u6863" },
1773
- readDialog: { en: "Read dialog", zh: "\u8BFB\u53D6\u5BF9\u8BDD" },
1774
- listDialogs: { en: "List dialogs", zh: "\u5217\u51FA\u5BF9\u8BDD" },
1775
- queryDialogsBySubjectRef: { en: "Query dialogs", zh: "\u67E5\u8BE2\u5BF9\u8BDD" },
1776
- searchDialogMessages: { en: "Search messages", zh: "\u641C\u7D22\u6D88\u606F" },
1777
- listSpaces: { en: "List spaces", zh: "\u5217\u51FA\u7A7A\u95F4" },
1778
- readSpace: { en: "Read space", zh: "\u8BFB\u53D6\u7A7A\u95F4" },
1779
- // Tables
1780
- createTable: { en: "Create table", zh: "\u521B\u5EFA\u8868" },
1781
- queryTableRows: { en: "Query rows", zh: "\u67E5\u8BE2\u8868\u884C" },
1782
- addTableRow: { en: "Add row", zh: "\u65B0\u589E\u8868\u884C" },
1783
- addTableRows: { en: "Add rows", zh: "\u65B0\u589E\u8868\u884C" },
1784
- updateTableRow: { en: "Update row", zh: "\u66F4\u65B0\u8868\u884C" },
1785
- deleteTableRow: { en: "Delete row", zh: "\u5220\u9664\u8868\u884C" },
1786
- // Web
1787
- fetchWebpage: { en: "Fetch page", zh: "\u6293\u53D6\u7F51\u9875" },
1788
- readPage: { en: "Read page", zh: "\u8BFB\u53D6\u7F51\u9875" },
1789
- exaSearch: { en: "Web search", zh: "\u8054\u7F51\u641C\u7D22" },
1790
- // Skill loading
1791
- loadSkill: { en: "Used Skill", zh: "\u4F7F\u7528\u6280\u80FD" }
2380
+ function formatHomePath(path, customHomedir) {
2381
+ if (!path) return "";
2382
+ const home = customHomedir || (typeof process !== "undefined" ? process.env?.HOME : void 0) || (typeof osHomedir === "function" ? osHomedir() : "");
2383
+ if (home && path.startsWith(home)) {
2384
+ return `~${path.slice(home.length)}`;
2385
+ }
2386
+ return path.replace(/^(\/Users\/[^\/]+\/|\/home\/[^\/]+\/)/, "~/");
2387
+ }
2388
+ function formatReadItemPath(rawPath, metadata, customHomedir) {
2389
+ const inputPath = rawPath || (typeof metadata?.path === "string" ? metadata.path : void 0) || (typeof metadata?.filePath === "string" ? metadata.filePath : void 0) || "file";
2390
+ let formattedPath = formatHomePath(inputPath, customHomedir);
2391
+ if (!formattedPath) return "file";
2392
+ let rangePart = "";
2393
+ const match = formattedPath.match(/(:[0-9]+(-[0-9]+)?(,[0-9]+(-[0-9]+)?)*)$/);
2394
+ if (match) {
2395
+ rangePart = match[1];
2396
+ formattedPath = formattedPath.slice(0, -rangePart.length);
2397
+ } else if (metadata && typeof metadata.startLine === "number" && typeof metadata.endLine === "number" && typeof metadata.totalLines === "number") {
2398
+ const { startLine, endLine, totalLines, truncated } = metadata;
2399
+ if (truncated || startLine !== 1 || endLine !== totalLines) {
2400
+ rangePart = `:${startLine}-${endLine}`;
2401
+ }
2402
+ }
2403
+ const clippedPath = clipPathAware(formattedPath, 72);
2404
+ return `${clippedPath}${rangePart}`;
2405
+ }
2406
+ function formatReadTreeLines(items, customHomedir) {
2407
+ const count = items.length;
2408
+ const header = `Read (${count})`;
2409
+ const lines = items.map((item, index) => {
2410
+ const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
2411
+ const pathWithRange = formatReadItemPath(item.path, item.metadata, customHomedir);
2412
+ return { connector, pathWithRange };
2413
+ });
2414
+ return { header, count, lines };
2415
+ }
2416
+ function formatSearchItemQuery(query, path, customHomedir) {
2417
+ let text = (query || "").trim();
2418
+ if (path) {
2419
+ const formattedPath = formatHomePath(path, customHomedir);
2420
+ if (formattedPath) {
2421
+ text = text ? `${text} in ${formattedPath}` : formattedPath;
2422
+ }
2423
+ }
2424
+ if (!text) return "search";
2425
+ return text.length > 80 ? `${text.slice(0, 79)}\u2026` : text;
2426
+ }
2427
+ function formatSearchTreeLines(items, customHomedir) {
2428
+ const count = items.length;
2429
+ const header = `Search (${count})`;
2430
+ const lines = items.map((item, index) => {
2431
+ const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
2432
+ const queryText = formatSearchItemQuery(item.query, item.path, customHomedir);
2433
+ return { connector, queryText };
2434
+ });
2435
+ return { header, count, lines };
2436
+ }
2437
+ function formatRunItemCommand(command, exitCode, timedOut) {
2438
+ let text = compactWhitespace(command || "");
2439
+ if (!text) text = "command";
2440
+ text = text.length > 80 ? `${text.slice(0, 79)}\u2026` : text;
2441
+ if (timedOut) return `${text} (timed out)`;
2442
+ if (typeof exitCode === "number" && exitCode !== 0) return `${text} (exit ${exitCode})`;
2443
+ return text;
2444
+ }
2445
+ function formatRunTreeLines(items) {
2446
+ const count = items.length;
2447
+ const header = `Run (${count})`;
2448
+ const lines = items.map((item, index) => {
2449
+ const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
2450
+ const commandText = formatRunItemCommand(item.command, item.exitCode, item.timedOut);
2451
+ return { connector, commandText };
2452
+ });
2453
+ return { header, count, lines };
2454
+ }
2455
+ function formatFetchItemUrl(url) {
2456
+ const text = (url || "").trim();
2457
+ if (!text) return "webpage";
2458
+ return text.length > 80 ? `${text.slice(0, 79)}\u2026` : text;
2459
+ }
2460
+ function formatFetchTreeLines(items) {
2461
+ const count = items.length;
2462
+ const header = `Fetch (${count})`;
2463
+ const lines = items.map((item, index) => {
2464
+ const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
2465
+ const urlText = formatFetchItemUrl(item.url);
2466
+ return { connector, urlText };
2467
+ });
2468
+ return { header, count, lines };
2469
+ }
2470
+
2471
+ // packages/cli/node_modules/diff/libesm/diff/base.js
2472
+ var Diff = class {
2473
+ diff(oldStr, newStr, options = {}) {
2474
+ let callback;
2475
+ if (typeof options === "function") {
2476
+ callback = options;
2477
+ options = {};
2478
+ } else if ("callback" in options) {
2479
+ callback = options.callback;
2480
+ }
2481
+ const oldString = this.castInput(oldStr, options);
2482
+ const newString = this.castInput(newStr, options);
2483
+ const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
2484
+ const newTokens = this.removeEmpty(this.tokenize(newString, options));
2485
+ return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
2486
+ }
2487
+ diffWithOptionsObj(oldTokens, newTokens, options, callback) {
2488
+ var _a;
2489
+ const done = (value) => {
2490
+ value = this.postProcess(value, options);
2491
+ if (callback) {
2492
+ setTimeout(function() {
2493
+ callback(value);
2494
+ }, 0);
2495
+ return void 0;
2496
+ } else {
2497
+ return value;
2498
+ }
2499
+ };
2500
+ const newLen = newTokens.length, oldLen = oldTokens.length;
2501
+ let editLength = 1;
2502
+ let maxEditLength = newLen + oldLen;
2503
+ if (options.maxEditLength != null) {
2504
+ maxEditLength = Math.min(maxEditLength, options.maxEditLength);
2505
+ }
2506
+ const maxExecutionTime = (_a = options.timeout) !== null && _a !== void 0 ? _a : Infinity;
2507
+ const abortAfterTimestamp = Date.now() + maxExecutionTime;
2508
+ const bestPath = [{ oldPos: -1, lastComponent: void 0 }];
2509
+ let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
2510
+ if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
2511
+ return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
2512
+ }
2513
+ let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
2514
+ const execEditLength = () => {
2515
+ for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
2516
+ let basePath;
2517
+ const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
2518
+ if (removePath) {
2519
+ bestPath[diagonalPath - 1] = void 0;
2520
+ }
2521
+ let canAdd = false;
2522
+ if (addPath) {
2523
+ const addPathNewPos = addPath.oldPos - diagonalPath;
2524
+ canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
2525
+ }
2526
+ const canRemove = removePath && removePath.oldPos + 1 < oldLen;
2527
+ if (!canAdd && !canRemove) {
2528
+ bestPath[diagonalPath] = void 0;
2529
+ continue;
2530
+ }
2531
+ if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) {
2532
+ basePath = this.addToPath(addPath, true, false, 0, options);
2533
+ } else {
2534
+ basePath = this.addToPath(removePath, false, true, 1, options);
2535
+ }
2536
+ newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
2537
+ if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
2538
+ return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
2539
+ } else {
2540
+ bestPath[diagonalPath] = basePath;
2541
+ if (basePath.oldPos + 1 >= oldLen) {
2542
+ maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
2543
+ }
2544
+ if (newPos + 1 >= newLen) {
2545
+ minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
2546
+ }
2547
+ }
2548
+ }
2549
+ editLength++;
2550
+ };
2551
+ if (callback) {
2552
+ (function exec() {
2553
+ setTimeout(function() {
2554
+ if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
2555
+ return callback(void 0);
2556
+ }
2557
+ if (!execEditLength()) {
2558
+ exec();
2559
+ }
2560
+ }, 0);
2561
+ })();
2562
+ } else {
2563
+ while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
2564
+ const ret = execEditLength();
2565
+ if (ret) {
2566
+ return ret;
2567
+ }
2568
+ }
2569
+ }
2570
+ }
2571
+ addToPath(path, added, removed, oldPosInc, options) {
2572
+ const last = path.lastComponent;
2573
+ if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
2574
+ return {
2575
+ oldPos: path.oldPos + oldPosInc,
2576
+ lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
2577
+ };
2578
+ } else {
2579
+ return {
2580
+ oldPos: path.oldPos + oldPosInc,
2581
+ lastComponent: { count: 1, added, removed, previousComponent: last }
2582
+ };
2583
+ }
2584
+ }
2585
+ extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
2586
+ const newLen = newTokens.length, oldLen = oldTokens.length;
2587
+ let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
2588
+ while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
2589
+ newPos++;
2590
+ oldPos++;
2591
+ commonCount++;
2592
+ if (options.oneChangePerToken) {
2593
+ basePath.lastComponent = { count: 1, previousComponent: basePath.lastComponent, added: false, removed: false };
2594
+ }
2595
+ }
2596
+ if (commonCount && !options.oneChangePerToken) {
2597
+ basePath.lastComponent = { count: commonCount, previousComponent: basePath.lastComponent, added: false, removed: false };
2598
+ }
2599
+ basePath.oldPos = oldPos;
2600
+ return newPos;
2601
+ }
2602
+ equals(left, right, options) {
2603
+ if (options.comparator) {
2604
+ return options.comparator(left, right);
2605
+ } else {
2606
+ return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
2607
+ }
2608
+ }
2609
+ removeEmpty(array) {
2610
+ const ret = [];
2611
+ for (let i = 0; i < array.length; i++) {
2612
+ if (array[i]) {
2613
+ ret.push(array[i]);
2614
+ }
2615
+ }
2616
+ return ret;
2617
+ }
2618
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2619
+ castInput(value, options) {
2620
+ return value;
2621
+ }
2622
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
2623
+ tokenize(value, options) {
2624
+ return Array.from(value);
2625
+ }
2626
+ join(chars) {
2627
+ return chars.join("");
2628
+ }
2629
+ postProcess(changeObjects, options) {
2630
+ return changeObjects;
2631
+ }
2632
+ get useLongestToken() {
2633
+ return false;
2634
+ }
2635
+ buildValues(lastComponent, newTokens, oldTokens) {
2636
+ const components = [];
2637
+ let nextComponent;
2638
+ while (lastComponent) {
2639
+ components.push(lastComponent);
2640
+ nextComponent = lastComponent.previousComponent;
2641
+ delete lastComponent.previousComponent;
2642
+ lastComponent = nextComponent;
2643
+ }
2644
+ components.reverse();
2645
+ const componentLen = components.length;
2646
+ let componentPos = 0, newPos = 0, oldPos = 0;
2647
+ for (; componentPos < componentLen; componentPos++) {
2648
+ const component = components[componentPos];
2649
+ if (!component.removed) {
2650
+ if (!component.added && this.useLongestToken) {
2651
+ let value = newTokens.slice(newPos, newPos + component.count);
2652
+ value = value.map(function(value2, i) {
2653
+ const oldValue = oldTokens[oldPos + i];
2654
+ return oldValue.length > value2.length ? oldValue : value2;
2655
+ });
2656
+ component.value = this.join(value);
2657
+ } else {
2658
+ component.value = this.join(newTokens.slice(newPos, newPos + component.count));
2659
+ }
2660
+ newPos += component.count;
2661
+ if (!component.added) {
2662
+ oldPos += component.count;
2663
+ }
2664
+ } else {
2665
+ component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
2666
+ oldPos += component.count;
2667
+ }
2668
+ }
2669
+ return components;
2670
+ }
1792
2671
  };
1793
- function toolLabel(name) {
1794
- return TOOL_LABELS[name]?.[currentLocale] ?? name;
2672
+
2673
+ // packages/cli/node_modules/diff/libesm/diff/line.js
2674
+ var LineDiff = class extends Diff {
2675
+ constructor() {
2676
+ super(...arguments);
2677
+ this.tokenize = tokenize;
2678
+ }
2679
+ equals(left, right, options) {
2680
+ if (options.ignoreWhitespace) {
2681
+ if (!options.newlineIsToken || !left.includes("\n")) {
2682
+ left = left.trim();
2683
+ }
2684
+ if (!options.newlineIsToken || !right.includes("\n")) {
2685
+ right = right.trim();
2686
+ }
2687
+ } else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
2688
+ if (left.endsWith("\n")) {
2689
+ left = left.slice(0, -1);
2690
+ }
2691
+ if (right.endsWith("\n")) {
2692
+ right = right.slice(0, -1);
2693
+ }
2694
+ }
2695
+ return super.equals(left, right, options);
2696
+ }
2697
+ };
2698
+ var lineDiff = new LineDiff();
2699
+ function diffLines(oldStr, newStr, options) {
2700
+ return lineDiff.diff(oldStr, newStr, options);
2701
+ }
2702
+ function tokenize(value, options) {
2703
+ if (options.stripTrailingCr) {
2704
+ value = value.replace(/\r\n/g, "\n");
2705
+ }
2706
+ const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
2707
+ if (!linesAndNewlines[linesAndNewlines.length - 1]) {
2708
+ linesAndNewlines.pop();
2709
+ }
2710
+ for (let i = 0; i < linesAndNewlines.length; i++) {
2711
+ const line = linesAndNewlines[i];
2712
+ if (i % 2 && !options.newlineIsToken) {
2713
+ retLines[retLines.length - 1] += line;
2714
+ } else {
2715
+ retLines.push(line);
2716
+ }
2717
+ }
2718
+ return retLines;
1795
2719
  }
1796
2720
 
1797
2721
  // packages/cli/client/toolOutput.ts
@@ -1864,25 +2788,65 @@ function readReadFileRange(metadata) {
1864
2788
  if (!truncated && startLine === 1 && endLine === totalLines) return void 0;
1865
2789
  return `${startLine}-${endLine}/${totalLines}`;
1866
2790
  }
1867
- var EDIT_SNIPPET_MAX_LINES = 5;
2791
+ var EDIT_SNIPPET_MAX_LINES = 10;
1868
2792
  var EDIT_SNIPPET_MAX_WIDTH = 96;
1869
2793
  function formatEditFileSnippet(metadata) {
1870
2794
  const oldSnippet = typeof metadata.oldSnippet === "string" ? metadata.oldSnippet : void 0;
1871
2795
  const newSnippet = typeof metadata.newSnippet === "string" ? metadata.newSnippet : void 0;
1872
2796
  if (!oldSnippet && !newSnippet) return void 0;
1873
- const lines = [];
1874
- if (oldSnippet) {
1875
- for (const line of snippetLines(oldSnippet)) lines.push(`- ${line}`);
2797
+ if (oldSnippet && !newSnippet) {
2798
+ return snippetLinesWithKind(oldSnippet, "removed");
2799
+ }
2800
+ if (newSnippet && !oldSnippet) {
2801
+ return snippetLinesWithKind(newSnippet, "added");
1876
2802
  }
1877
- if (newSnippet) {
1878
- for (const line of snippetLines(newSnippet)) lines.push(`+ ${line}`);
2803
+ const parts = diffLines(oldSnippet, newSnippet);
2804
+ const all = [];
2805
+ for (const part of parts) {
2806
+ const kind = part.added ? "added" : part.removed ? "removed" : "context";
2807
+ const raw = part.value.split("\n");
2808
+ if (raw.length > 0 && raw[raw.length - 1] === "") raw.pop();
2809
+ for (const line of raw) all.push(buildEditLine(kind, line));
1879
2810
  }
1880
- return lines.length ? lines.join("\n") : void 0;
2811
+ const changeCount = all.filter((l) => l.kind !== "context").length;
2812
+ if (changeCount === 0) return void 0;
2813
+ return windowAndTruncate(all, EDIT_SNIPPET_MAX_LINES);
1881
2814
  }
1882
- function snippetLines(snippet) {
2815
+ function prefixFor(kind) {
2816
+ return kind === "added" ? "+ " : kind === "removed" ? "- " : " ";
2817
+ }
2818
+ function buildEditLine(kind, body) {
2819
+ return { kind, text: prefixFor(kind) + truncateByDisplayWidth(body, EDIT_SNIPPET_MAX_WIDTH) };
2820
+ }
2821
+ function snippetLinesWithKind(snippet, kind) {
1883
2822
  const lines = snippet.split(/\r?\n/).filter((line) => line.length > 0);
1884
- const shown = lines.slice(0, EDIT_SNIPPET_MAX_LINES);
1885
- return shown.map((line) => line.length > EDIT_SNIPPET_MAX_WIDTH ? `${line.slice(0, EDIT_SNIPPET_MAX_WIDTH)}\u2026` : line);
2823
+ return lines.slice(0, EDIT_SNIPPET_MAX_LINES).map((line) => buildEditLine(kind, line));
2824
+ }
2825
+ function truncateByDisplayWidth(line, maxWidth) {
2826
+ if (displayWidth(line) <= maxWidth) return line;
2827
+ let width = 0;
2828
+ let kept = "";
2829
+ for (const char of line) {
2830
+ const w = displayWidth(char);
2831
+ if (width + w + 1 > maxWidth) break;
2832
+ kept += char;
2833
+ width += w;
2834
+ }
2835
+ return `${kept}\u2026`;
2836
+ }
2837
+ function windowAndTruncate(lines, maxLines) {
2838
+ if (lines.length <= maxLines) return lines;
2839
+ const firstChange = lines.findIndex((l) => l.kind !== "context");
2840
+ if (firstChange === -1) return lines.slice(0, maxLines);
2841
+ const half = Math.floor(maxLines / 2);
2842
+ let start = Math.max(0, firstChange - half);
2843
+ if (start + maxLines > lines.length) start = Math.max(0, lines.length - maxLines);
2844
+ const kept = lines.slice(start, start + maxLines);
2845
+ const omitted = lines.length - kept.length;
2846
+ if (omitted > 0) {
2847
+ kept.push({ kind: "context", text: ` \u2026 +${omitted} more lines` });
2848
+ }
2849
+ return kept;
1886
2850
  }
1887
2851
  function isFailedToolResult(event) {
1888
2852
  const exitCode = event.metadata?.exitCode;
@@ -1904,18 +2868,66 @@ function parseUiAskChoiceForCli(event) {
1904
2868
  label: String(c.label ?? "").trim(),
1905
2869
  userMessage: typeof c.userMessage === "string" ? c.userMessage : void 0
1906
2870
  })).filter((c) => c.label.length > 0);
1907
- if (!question || choices.length === 0) return null;
1908
- return { question, choices };
2871
+ const selected = parsed.selected && typeof parsed.selected === "object" ? {
2872
+ label: String(parsed.selected.label ?? "").trim(),
2873
+ userMessage: String(parsed.selected.userMessage ?? "").trim()
2874
+ } : void 0;
2875
+ const answers = Array.isArray(parsed.answers) ? parsed.answers : void 0;
2876
+ const cancelled = Boolean(parsed.cancelled);
2877
+ const resolved = Boolean(
2878
+ event.metadata?.resolved || selected || cancelled || answers && answers.length > 0
2879
+ );
2880
+ if (!resolved && (!question || choices.length === 0)) return null;
2881
+ if (!question && !selected && !cancelled) return null;
2882
+ return {
2883
+ question,
2884
+ choices,
2885
+ ...selected ? { selected } : {},
2886
+ ...answers ? { answers } : {},
2887
+ ...cancelled ? { cancelled: true } : {},
2888
+ resolved
2889
+ };
1909
2890
  }
1910
2891
  function formatUiAskChoiceBlock(event, colorEnabled) {
1911
2892
  const parsed = parseUiAskChoiceForCli(event);
1912
2893
  if (!parsed) return null;
1913
2894
  const lines = [];
1914
2895
  lines.push("");
2896
+ const questionText = parsed.question || "";
1915
2897
  if (colorEnabled) {
1916
- lines.push(`${themeText("\u2753 ", "info", true)}${styleCliText(parsed.question, "cyan", true)}`);
2898
+ lines.push(`${themeText("\u2753 ", "info", true)}${styleCliText(questionText, "cyan", true)}`);
1917
2899
  } else {
1918
- lines.push(`\u2753 ${parsed.question}`);
2900
+ lines.push(`\u2753 ${questionText}`);
2901
+ }
2902
+ if (parsed.resolved) {
2903
+ if (parsed.cancelled) {
2904
+ const cancelled = t("askChoiceHistoryCancelled");
2905
+ lines.push(
2906
+ colorEnabled ? ` ${themeText("\xB7", "chrome", true)} ${themeText(cancelled, "muted", true)}` : ` \xB7 ${cancelled}`
2907
+ );
2908
+ return `${lines.join("\n")}
2909
+ `;
2910
+ }
2911
+ const selectedLabel = parsed.selected?.label || parsed.selected?.userMessage || (parsed.answers ? parsed.answers.map((a) => a.userMessage).filter(Boolean).join(", ") : "");
2912
+ const marker = t("askChoiceHistorySelected");
2913
+ const displayLabel = selectedLabel || "\u2014";
2914
+ if (colorEnabled) {
2915
+ lines.push(
2916
+ ` ${themeText("\u2713", "success", true)} ${themeText(marker, "muted", true)} ${displayLabel}`
2917
+ );
2918
+ } else {
2919
+ lines.push(` \u2713 ${marker} ${displayLabel}`);
2920
+ }
2921
+ if (parsed.answers && parsed.answers.length > 1) {
2922
+ for (const answer of parsed.answers) {
2923
+ if (!answer.userMessage) continue;
2924
+ lines.push(
2925
+ colorEnabled ? ` ${themeText("\xB7", "chrome", true)} ${answer.userMessage}` : ` \xB7 ${answer.userMessage}`
2926
+ );
2927
+ }
2928
+ }
2929
+ return `${lines.join("\n")}
2930
+ `;
1919
2931
  }
1920
2932
  parsed.choices.forEach((choice, i) => {
1921
2933
  const num = String(i + 1);
@@ -1928,7 +2940,7 @@ function formatUiAskChoiceBlock(event, colorEnabled) {
1928
2940
  lines.push(` ${num}. ${label}`);
1929
2941
  }
1930
2942
  });
1931
- const hint = "\u8BF7\u8F93\u5165\u5E8F\u53F7\u9009\u62E9\uFF0C\u6216\u76F4\u63A5\u56DE\u590D\uFF1A";
2943
+ const hint = t("askChoiceHistoryHint");
1932
2944
  if (colorEnabled) {
1933
2945
  lines.push(` ${dimCliText(hint, true)}`);
1934
2946
  } else {
@@ -2158,42 +3170,23 @@ ${detail}
2158
3170
  } else {
2159
3171
  const chromePointer = themeText("\u25B8", "chrome", true);
2160
3172
  const mutedLabel = themeText(rawLabel, "muted", true);
2161
- const argsPart = rawArgs ? ` ${dimCliText(rawArgs, true)}` : "";
2162
- const statusToken = failed ? themeText("\u2717", "danger", true) : isNeedsActionToolResult(event) ? themeText("!", "warning", true) : themeText("\u2713", "success", true);
2163
- const suffixPart = hint.inline ? ` ${dimCliText(hint.inline, true)}` : "";
2164
- mainLine = `${chromePointer} ${mutedLabel}${argsPart} ${statusToken}${suffixPart}
2165
- `;
2166
- }
2167
- if (!hint.detail) return mainLine;
2168
- const detailLines = hint.detail.split("\n").map((line) => formatEditDetailLine(line, colorEnabled)).join("");
2169
- return `${mainLine}${detailLines}`;
2170
- }
2171
- function formatEditDetailLine(line, colorEnabled) {
2172
- const marker = line.slice(0, 2);
2173
- const rest = line.slice(2);
2174
- if (!colorEnabled) return ` ${line}
2175
- `;
2176
- const diff = diffLineSequences();
2177
- if (!diff) {
2178
- const color = marker === "- " ? "red" : marker === "+ " ? "green" : void 0;
2179
- if (color) {
2180
- return ` ${styleCliText(marker, color, true)}${dimCliText(rest, true)}
2181
- `;
2182
- }
2183
- return ` ${dimCliText(line, true)}
2184
- `;
2185
- }
2186
- const RESET2 = "\x1B[0m";
2187
- if (marker === "- ") {
2188
- return ` ${diff.removed.bg}${diff.removed.fg}- ${rest}${RESET2}
3173
+ const argsPart = rawArgs ? ` ${dimCliText(rawArgs, true)}` : "";
3174
+ const statusToken = failed ? themeText("\u2717", "danger", true) : isNeedsActionToolResult(event) ? themeText("!", "warning", true) : themeText("\u2713", "success", true);
3175
+ const suffixPart = hint.inline ? ` ${dimCliText(hint.inline, true)}` : "";
3176
+ mainLine = `${chromePointer} ${mutedLabel}${argsPart} ${statusToken}${suffixPart}
2189
3177
  `;
2190
3178
  }
2191
- if (marker === "+ ") {
2192
- return ` ${diff.added.bg}${diff.added.fg}+ ${rest}${RESET2}
2193
- `;
3179
+ if (!hint.detail?.length) return mainLine;
3180
+ return `${mainLine}${formatEditDetailBlock(hint.detail, colorEnabled)}`;
3181
+ }
3182
+ function formatEditDetailBlock(lines, colorEnabled) {
3183
+ if (!colorEnabled) {
3184
+ return lines.map((line) => ` ${line.text}
3185
+ `).join("");
2194
3186
  }
2195
- return ` ${dimCliText(line, true)}
2196
- `;
3187
+ const padTo = Math.max(...lines.map((line) => displayWidth(line.text))) + 1;
3188
+ return lines.map((line) => ` ${renderDiffLine({ kind: line.kind, text: line.text, padTo, colorEnabled: true })}
3189
+ `).join("");
2197
3190
  }
2198
3191
  function createToolEventFormatter(mode, colorEnabled = resolveCliColorEnabled()) {
2199
3192
  const pending = /* @__PURE__ */ new Map();
@@ -2823,10 +3816,165 @@ function isKnownServerPlatformAgent(options) {
2823
3816
  );
2824
3817
  }
2825
3818
 
3819
+ // packages/core/collapsedPaste.ts
3820
+ var COLLAPSE_PASTE_MIN_LINES = 8;
3821
+ var COLLAPSE_PASTE_MIN_CHARS = 400;
3822
+ var COLLAPSED_PASTE_PLACEHOLDER_RE = /\[paste #(\d+) · (\d+) lines\]/g;
3823
+ var COLLAPSED_PASTE_MODEL_REFERENCE_RE = /\[paste #(\d+)(?: scope:([a-zA-Z0-9_-]+))? · [^\]]+; full content available via readPastedText\(pasteId=(\d+)\)\]/g;
3824
+ function countTextLines(text) {
3825
+ if (text.length === 0) return 0;
3826
+ let lines = 1;
3827
+ for (let i = 0; i < text.length; i += 1) {
3828
+ if (text.charCodeAt(i) === 10) lines += 1;
3829
+ }
3830
+ return lines;
3831
+ }
3832
+ function shouldCollapsePaste(text, threshold = {}) {
3833
+ const minLines = threshold.minLines ?? COLLAPSE_PASTE_MIN_LINES;
3834
+ const minChars = threshold.minChars ?? COLLAPSE_PASTE_MIN_CHARS;
3835
+ if (text.length === 0) return false;
3836
+ if (text.length >= minChars) return true;
3837
+ return countTextLines(text) >= minLines;
3838
+ }
3839
+ function formatPasteByteSize(bytes) {
3840
+ if (!Number.isFinite(bytes) || bytes < 0) return "0 B";
3841
+ if (bytes < 1024) return `${Math.round(bytes)} B`;
3842
+ if (bytes < 1024 * 1024) {
3843
+ const kb = bytes / 1024;
3844
+ return `${kb >= 10 ? kb.toFixed(0) : kb.toFixed(1)} KB`;
3845
+ }
3846
+ const mb = bytes / (1024 * 1024);
3847
+ return `${mb >= 10 ? mb.toFixed(0) : mb.toFixed(1)} MB`;
3848
+ }
3849
+ function estimatePasteBytes(text) {
3850
+ let bytes = 0;
3851
+ for (let i = 0; i < text.length; i += 1) {
3852
+ const code = text.charCodeAt(i);
3853
+ if (code <= 127) bytes += 1;
3854
+ else if (code <= 2047) bytes += 2;
3855
+ else if (code >= 55296 && code <= 56319) {
3856
+ bytes += 4;
3857
+ i += 1;
3858
+ } else bytes += 3;
3859
+ }
3860
+ return bytes;
3861
+ }
3862
+ function formatCollapsedPastePlaceholder(id, text) {
3863
+ const lines = countTextLines(text);
3864
+ return `[paste #${id} \xB7 ${lines} lines]`;
3865
+ }
3866
+ function generatePasteScope() {
3867
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
3868
+ return crypto.randomUUID().slice(0, 8);
3869
+ }
3870
+ return Math.random().toString(36).slice(2, 10);
3871
+ }
3872
+ function formatCollapsedPasteModelReference(id, text, scope) {
3873
+ const lines = countTextLines(text);
3874
+ const size = formatPasteByteSize(estimatePasteBytes(text));
3875
+ const scopeAttr = scope ? ` scope:${scope}` : "";
3876
+ return `[paste #${id}${scopeAttr} \xB7 ${lines} lines \xB7 ${size}; full content available via readPastedText(pasteId=${id})]`;
3877
+ }
3878
+ function createCollapsedPasteStore(scope) {
3879
+ return { scope: scope ?? generatePasteScope(), nextId: 1, items: /* @__PURE__ */ new Map() };
3880
+ }
3881
+ function allocateCollapsedPaste(store, text) {
3882
+ const id = store.nextId;
3883
+ store.nextId += 1;
3884
+ store.items.set(id, text);
3885
+ return { id, placeholder: formatCollapsedPastePlaceholder(id, text) };
3886
+ }
3887
+ function releaseCollapsedPaste(store, id) {
3888
+ store.items.delete(id);
3889
+ }
3890
+ function clearCollapsedPasteStore(store) {
3891
+ store.items.clear();
3892
+ store.nextId = 1;
3893
+ store.scope = generatePasteScope();
3894
+ }
3895
+ function expandCollapsedPastes(buffer, store) {
3896
+ COLLAPSED_PASTE_MODEL_REFERENCE_RE.lastIndex = 0;
3897
+ const expandedReferences = buffer.replace(
3898
+ COLLAPSED_PASTE_MODEL_REFERENCE_RE,
3899
+ (match, _displayIdRaw, scopeRaw, idRaw) => {
3900
+ if (scopeRaw && scopeRaw === store.scope) {
3901
+ const full = store.items.get(Number(idRaw));
3902
+ return full === void 0 ? match : full;
3903
+ }
3904
+ return match;
3905
+ }
3906
+ );
3907
+ if (store.items.size === 0) return expandedReferences;
3908
+ COLLAPSED_PASTE_PLACEHOLDER_RE.lastIndex = 0;
3909
+ return expandedReferences.replace(COLLAPSED_PASTE_PLACEHOLDER_RE, (match, idRaw) => {
3910
+ const id = Number(idRaw);
3911
+ const full = store.items.get(id);
3912
+ return full === void 0 ? match : full;
3913
+ });
3914
+ }
3915
+ function replaceCollapsedPastesWithReferences(buffer, store) {
3916
+ if (store.items.size === 0) return buffer;
3917
+ COLLAPSED_PASTE_PLACEHOLDER_RE.lastIndex = 0;
3918
+ return buffer.replace(COLLAPSED_PASTE_PLACEHOLDER_RE, (match, idRaw) => {
3919
+ const id = Number(idRaw);
3920
+ const full = store.items.get(id);
3921
+ return full === void 0 ? match : formatCollapsedPasteModelReference(id, full, store.scope);
3922
+ });
3923
+ }
3924
+ function releaseCollapsedPasteReferences(buffer, store) {
3925
+ const ids = /* @__PURE__ */ new Set();
3926
+ COLLAPSED_PASTE_MODEL_REFERENCE_RE.lastIndex = 0;
3927
+ let modelMatch;
3928
+ while ((modelMatch = COLLAPSED_PASTE_MODEL_REFERENCE_RE.exec(buffer)) !== null) {
3929
+ const id = Number(modelMatch[3]);
3930
+ const scope = modelMatch[2];
3931
+ if (scope && scope === store.scope) {
3932
+ ids.add(id);
3933
+ }
3934
+ }
3935
+ COLLAPSED_PASTE_PLACEHOLDER_RE.lastIndex = 0;
3936
+ let placeholderMatch;
3937
+ while ((placeholderMatch = COLLAPSED_PASTE_PLACEHOLDER_RE.exec(buffer)) !== null) {
3938
+ ids.add(Number(placeholderMatch[1]));
3939
+ }
3940
+ for (const id of ids) releaseCollapsedPaste(store, id);
3941
+ }
3942
+ function expandRangeToCollapsedPasteChips(buffer, start, end) {
3943
+ let s = Math.max(0, Math.min(start, end));
3944
+ let e = Math.max(s, Math.max(start, end));
3945
+ if (s === e) return { start: s, end: e };
3946
+ COLLAPSED_PASTE_PLACEHOLDER_RE.lastIndex = 0;
3947
+ let match;
3948
+ while ((match = COLLAPSED_PASTE_PLACEHOLDER_RE.exec(buffer)) !== null) {
3949
+ const chipStart = match.index;
3950
+ const chipEnd = chipStart + match[0].length;
3951
+ if (chipStart < e && chipEnd > s) {
3952
+ if (chipStart < s) s = chipStart;
3953
+ if (chipEnd > e) e = chipEnd;
3954
+ }
3955
+ }
3956
+ return { start: s, end: e };
3957
+ }
3958
+ function findCollapsedPasteSpanAt(buffer, pos, opts = {}) {
3959
+ COLLAPSED_PASTE_PLACEHOLDER_RE.lastIndex = 0;
3960
+ let match;
3961
+ while ((match = COLLAPSED_PASTE_PLACEHOLDER_RE.exec(buffer)) !== null) {
3962
+ const start = match.index;
3963
+ const end = start + match[0].length;
3964
+ const inside = pos > start && pos < end;
3965
+ const atEnd = opts.preferLeft && pos === end;
3966
+ const atStart = !opts.preferLeft && pos === start;
3967
+ if (inside || atEnd || atStart) {
3968
+ return { id: Number(match[1]), start, end };
3969
+ }
3970
+ }
3971
+ return null;
3972
+ }
3973
+
2826
3974
  // packages/cli/client/agentRun.ts
2827
3975
  import { ulid } from "ulid";
2828
3976
  async function loadRunLocalAgentTurn() {
2829
- const { runLocalAgentTurn } = await import("./agentRuntimeLocal-ZYEIPTY4.js");
3977
+ const { runLocalAgentTurn } = await import("./agentRuntimeLocal-73SVOC5U.js");
2830
3978
  return runLocalAgentTurn;
2831
3979
  }
2832
3980
  async function resolveCurrentMachineId(options) {
@@ -2839,6 +3987,12 @@ function resolveRequestedRuntimeMode(options) {
2839
3987
  return envMode;
2840
3988
  return "auto";
2841
3989
  }
3990
+ function canResolveCollapsedPasteReference(store, content) {
3991
+ const expand = (text) => expandCollapsedPastes(text, store) !== text;
3992
+ if (typeof content === "string") return expand(content);
3993
+ if (!Array.isArray(content)) return false;
3994
+ return content.some((part) => part.type === "text" && expand(part.text));
3995
+ }
2842
3996
  function buildDefaultLocalRuntimeAdapter(options) {
2843
3997
  return createCliLocalRuntimeAdapter({
2844
3998
  env: options.env,
@@ -2846,7 +4000,8 @@ function buildDefaultLocalRuntimeAdapter(options) {
2846
4000
  cwd: options.localRuntimeCwd,
2847
4001
  output: options.output,
2848
4002
  ...options.confirmDestructiveAction ? { confirmDestructiveAction: options.confirmDestructiveAction } : {},
2849
- ...options.requestUserChoice ? { requestUserChoice: options.requestUserChoice } : {}
4003
+ ...options.requestUserChoice ? { requestUserChoice: options.requestUserChoice } : {},
4004
+ ...options.pastedTextStore?.items.size ? { pastedTextStore: options.pastedTextStore } : {}
2850
4005
  });
2851
4006
  }
2852
4007
  function resolveLocalRuntimeAdapter(options) {
@@ -2985,6 +4140,35 @@ function isMissingLocalAgentConfigError(error, agentRef) {
2985
4140
  error && typeof error === "object" && "code" in error && error.code === LOCAL_AGENT_CONFIG_MISSING_CODE && error.agentRef === agentRef
2986
4141
  );
2987
4142
  }
4143
+ var PLATFORM_PROVIDER_FAILED_RE = /platform provider failed:\s*HTTP\s+(\d+)\b/i;
4144
+ function classifyLocalRunError(message) {
4145
+ const m = message.match(PLATFORM_PROVIDER_FAILED_RE);
4146
+ if (!m) return { kind: "generic" };
4147
+ const status = Number(m[1]);
4148
+ if (status >= 500 && status <= 599) {
4149
+ return { kind: "platform-proxy-transient", status };
4150
+ }
4151
+ return { kind: "platform-proxy-upstream", status };
4152
+ }
4153
+ function describeLocalRunFailure(message) {
4154
+ if (/UPSTREAM_402|Insufficient Balance|余额不足|insufficient\s+balance/i.test(
4155
+ message
4156
+ )) {
4157
+ return `[nolo] auto runtime: local run unavailable (${message}). Not falling back to server. Top up your balance, then send again (or say "\u7EE7\u7EED") \u2014 if a dialog was kept, context is preserved.
4158
+ `;
4159
+ }
4160
+ const cls = classifyLocalRunError(message);
4161
+ if (cls.kind === "platform-proxy-transient") {
4162
+ return `[nolo] auto runtime: local run unavailable (server chat proxy returned HTTP ${cls.status}; this is an upstream/gateway issue, not your local credential or config).${message ? ` Detail: ${message}` : ""} Not falling back to server. Retry shortly, or use --server to run on the server explicitly.
4163
+ `;
4164
+ }
4165
+ if (cls.kind === "platform-proxy-upstream") {
4166
+ return `[nolo] auto runtime: local run unavailable (server chat proxy returned HTTP ${cls.status}).${message ? ` Detail: ${message}` : ""} Not falling back to server. Check the agent's provider/api-key settings on nolo.chat, or use --server to run on the server explicitly.
4167
+ `;
4168
+ }
4169
+ return `[nolo] auto runtime: local run unavailable (${message}). Not falling back to server. Fix the local credential/config and retry, or use --server to run on the server explicitly.
4170
+ `;
4171
+ }
2988
4172
  function shouldAttemptAutoLocal(options) {
2989
4173
  if (options.localRuntimeAdapter || options.localRuntimeAdapterFactory)
2990
4174
  return true;
@@ -3042,12 +4226,14 @@ async function runHttpAgentTurn(options, authToken) {
3042
4226
  (name) => name.trim()
3043
4227
  );
3044
4228
  const shouldStream = !options.noStream && !options.background;
4229
+ const expandedMessage = options.pastedTextStore?.items.size ? expandCollapsedPastes(options.message, options.pastedTextStore) : options.message;
4230
+ const serverContextBlockScopes = options.contextBlockScopes && options.contextBlockScopes.length > 0 ? options.contextBlockScopes : options.extraContextBlocks && options.extraContextBlocks.length > 0 ? options.extraContextBlocks.map((block) => ({
4231
+ content: block,
4232
+ cacheScope: "turn"
4233
+ })) : void 0;
3045
4234
  const buildRequestBody = (stream) => JSON.stringify({
3046
4235
  agentKey: options.agentKey,
3047
- userInput: buildUserInputContent(
3048
- options.extraContextBlocks?.length ? [...options.extraContextBlocks, "", options.message].join("\n") : options.message,
3049
- options.imageUrls
3050
- ),
4236
+ userInput: buildUserInputContent(expandedMessage, options.imageUrls),
3051
4237
  runtimeContext: {
3052
4238
  surface: "cli",
3053
4239
  host: "terminal",
@@ -3067,6 +4253,7 @@ async function runHttpAgentTurn(options, authToken) {
3067
4253
  ...options.background ? { background: true } : {},
3068
4254
  ...typeof options.timeoutMs === "number" ? { timeoutMs: options.timeoutMs } : {},
3069
4255
  ...options.modelOverride ? { runtimeOptions: { quickChatModelOverride: options.modelOverride } } : {},
4256
+ ...serverContextBlockScopes ? { contextBlockScopes: serverContextBlockScopes } : {},
3070
4257
  stream
3071
4258
  });
3072
4259
  const postAgentRun = (stream) => fetchImpl(`${options.serverUrl}/api/agent/run`, {
@@ -3224,7 +4411,7 @@ async function runLocalAgentTurnForCli(options, settings) {
3224
4411
  } : void 0;
3225
4412
  const currentDialogId = options.continueDialogId ?? ulid();
3226
4413
  const runChildTurn = async (input) => {
3227
- const { runLocalAgentTurn } = await import("./agentRuntimeLocal-ZYEIPTY4.js");
4414
+ const { runLocalAgentTurn } = await import("./agentRuntimeLocal-73SVOC5U.js");
3228
4415
  return runLocalAgentTurn(input);
3229
4416
  };
3230
4417
  const createFreshChildBaseAdapter = () => {
@@ -3272,6 +4459,7 @@ async function runLocalAgentTurnForCli(options, settings) {
3272
4459
  spaceId: options.spaceId,
3273
4460
  runtimeContext
3274
4461
  });
4462
+ const expandedMessage = options.pastedTextStore?.items.size ? expandCollapsedPastes(options.message, options.pastedTextStore) : options.message;
3275
4463
  const workingLabel = `${options.agentName} -> working locally`;
3276
4464
  const turnOutput = createCliTurnOutput({
3277
4465
  options,
@@ -3284,6 +4472,19 @@ async function runLocalAgentTurnForCli(options, settings) {
3284
4472
  adapter,
3285
4473
  agentRef: options.agentKey,
3286
4474
  input: buildUserInputContent(options.message, options.imageUrls),
4475
+ ...expandedMessage !== options.message ? {
4476
+ persistedInput: buildUserInputContent(
4477
+ expandedMessage,
4478
+ options.imageUrls
4479
+ ),
4480
+ persistedInputReference: buildUserInputContent(
4481
+ options.message,
4482
+ options.imageUrls
4483
+ )
4484
+ } : {},
4485
+ ...options.pastedTextStore ? {
4486
+ contextReferenceResolver: (reference) => canResolveCollapsedPasteReference(options.pastedTextStore, reference)
4487
+ } : {},
3287
4488
  continueDialogId: currentDialogId,
3288
4489
  spaceId: options.spaceId,
3289
4490
  category: options.category,
@@ -3292,8 +4493,7 @@ async function runLocalAgentTurnForCli(options, settings) {
3292
4493
  background: options.background,
3293
4494
  noStream: options.noStream,
3294
4495
  ...runtimeContext ? { runtimeContext } : {},
3295
- ...options.extraContextBlocks?.length ? { contextBlocks: options.extraContextBlocks } : {},
3296
- ...options.contextBlockScopes?.length ? { contextBlockScopes: options.contextBlockScopes } : {},
4496
+ ...options.contextBlockScopes?.length ? { contextBlockScopes: options.contextBlockScopes } : options.extraContextBlocks?.length ? { contextBlocks: options.extraContextBlocks } : {},
3297
4497
  ...typeof options.timeoutMs === "number" ? { timeoutMs: options.timeoutMs } : {},
3298
4498
  ...options.actionGateHandler ? { onActionGate: options.actionGateHandler } : {},
3299
4499
  onLoopEvent: (event) => {
@@ -3327,12 +4527,14 @@ async function runLocalAgentTurnForCli(options, settings) {
3327
4527
  };
3328
4528
  } catch (error) {
3329
4529
  turnOutput.spinner.stop();
4530
+ const savedDialogId = error?.dialogId;
3330
4531
  if (error?.code === LOCAL_TURN_ABORTED_CODE || options.abortSignal?.aborted) {
3331
- const stoppedDialogId = error?.dialogId;
4532
+ const pendingToolName = error?.pendingToolName;
3332
4533
  return {
3333
4534
  exitCode: 0,
3334
4535
  streamInterrupted: true,
3335
- ...stoppedDialogId ? { dialogId: stoppedDialogId } : {}
4536
+ ...savedDialogId ? { dialogId: savedDialogId } : {},
4537
+ ...pendingToolName ? { pendingToolName } : {}
3336
4538
  };
3337
4539
  }
3338
4540
  if (settings.reportFailure) {
@@ -3341,7 +4543,11 @@ async function runLocalAgentTurnForCli(options, settings) {
3341
4543
  `
3342
4544
  );
3343
4545
  }
3344
- return { exitCode: 1, localError: error };
4546
+ return {
4547
+ exitCode: 1,
4548
+ localError: error,
4549
+ ...savedDialogId ? { dialogId: savedDialogId } : {}
4550
+ };
3345
4551
  }
3346
4552
  }
3347
4553
  async function runAgentTurn(options) {
@@ -3361,7 +4567,8 @@ async function runAgentTurn(options) {
3361
4567
  exitCode: localResult.exitCode,
3362
4568
  ...localResult.dialogId ? { dialogId: localResult.dialogId } : {},
3363
4569
  ...localResult.turnTokens ? { turnTokens: localResult.turnTokens } : {},
3364
- ...localResult.streamInterrupted ? { streamInterrupted: localResult.streamInterrupted } : {}
4570
+ ...localResult.streamInterrupted ? { streamInterrupted: localResult.streamInterrupted } : {},
4571
+ ...localResult.pendingToolName ? { pendingToolName: localResult.pendingToolName } : {}
3365
4572
  };
3366
4573
  }
3367
4574
  if (isMissingLocalAgentConfigError(localResult.localError, options.agentKey)) {
@@ -3387,13 +4594,13 @@ async function runAgentTurn(options) {
3387
4594
  }
3388
4595
  }
3389
4596
  const localErrorMessage = localResult.localError ? toErrorMessage(localResult.localError) : "local runtime failed";
3390
- options.output.write(
3391
- `[nolo] auto runtime: local run unavailable (${localErrorMessage}). Not falling back to server. Fix the local credential/config and retry, or use --server to run on the server explicitly.
3392
- `
3393
- );
4597
+ options.output.write(describeLocalRunFailure(localErrorMessage));
3394
4598
  return {
3395
4599
  exitCode: 1,
3396
- ...localResult.dialogId ? { dialogId: localResult.dialogId } : {}
4600
+ ...localResult.dialogId ? { dialogId: localResult.dialogId } : {},
4601
+ // Keep localError so TUI can show balance/quota/dialog-preserved hints
4602
+ // instead of only the raw auto-runtime line.
4603
+ ...localResult.localError ? { localError: localResult.localError } : {}
3397
4604
  };
3398
4605
  }
3399
4606
  }
@@ -3415,196 +4622,6 @@ async function runAgentTurn(options) {
3415
4622
  import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
3416
4623
  import { join as join2 } from "node:path";
3417
4624
 
3418
- // packages/agent-runtime/quickChatIntentCore.ts
3419
- var INTENT_MODEL = "deepseek-v4-flash";
3420
- var INTENT_PROVIDER = "deepseek";
3421
- var QUICK_CHAT_INTENT_TIMEOUT_MS = 4e3;
3422
- var SHORT_GREETING_MAX_LENGTH = 20;
3423
- var SHORT_GREETING_PATTERN = /^(?:hi|hello|hey|hiya|yo|sup|howdy|嗨|哈喽|哈囉|你好|您好|在吗|在嘛|喂|早上好|中午好|下午好|晚上好|拜拜|再见|谢谢|thanks|thx|ok|好的|收到|了解)[!.。,、~~]?$/iu;
3424
- function isShortGreeting(text) {
3425
- const trimmed = text.trim();
3426
- if (!trimmed) return false;
3427
- if (trimmed.length > SHORT_GREETING_MAX_LENGTH) return false;
3428
- return SHORT_GREETING_PATTERN.test(trimmed);
3429
- }
3430
- var COMPLEXITY_SIMPLE_MAX_LENGTH = 80;
3431
- var COMPLEXITY_MEDIUM_MAX_LENGTH = 500;
3432
- var COMPLEXITY_KEYWORDS = /为什么|如何|分析|对比|比较|设计|架构|实现|原理|推导|评价|区别|关系|影响|优化|重构/;
3433
- var COMPLEXITY_CODE_BLOCK = /```[\s\S]*```/;
3434
- function estimateComplexity(text) {
3435
- const trimmed = text.trim();
3436
- const len = trimmed.length;
3437
- if (len > COMPLEXITY_MEDIUM_MAX_LENGTH) return "complex";
3438
- if (COMPLEXITY_CODE_BLOCK.test(trimmed)) return "complex";
3439
- if (len > COMPLEXITY_SIMPLE_MAX_LENGTH) return "medium";
3440
- if (COMPLEXITY_KEYWORDS.test(trimmed)) return "medium";
3441
- return "simple";
3442
- }
3443
- var TIER_DESCRIPTIONS = {
3444
- flash: "\u5FEB\u901F\u7B80\u5355\uFF1A\u95EE\u5019\u3001\u95F2\u804A\u3001\u5FEB\u901F\u95EE\u7B54\u3001\u7FFB\u8BD1\u3001\u77ED\u6587",
3445
- balanced: "\u5E73\u8861\u63A8\u7406\uFF1A\u5206\u6790\u3001\u4E2D\u7B49\u957F\u5EA6\u5199\u4F5C\u3001\u4EE3\u7801\u3001\u63A8\u7406",
3446
- quality: "\u9AD8\u8D28\u91CF\u6DF1\u5EA6\uFF1A\u590D\u6742\u63A8\u7406\u3001\u957F\u6587\u3001\u67B6\u6784\u8BBE\u8BA1\u3001\u6DF1\u5EA6\u5206\u6790"
3447
- };
3448
- function buildQuickChatIntentSystemPrompt(tierAgents) {
3449
- const agentList = tierAgents.map((t2) => ` - "${t2.agentKey}"\uFF1A${t2.description}`).join("\n");
3450
- return `\u4F60\u662F\u4E00\u4E2A\u610F\u56FE\u8DEF\u7531\u5668\u3002\u6839\u636E\u7528\u6237\u6D88\u606F\uFF0C\u5224\u65AD\u5E94\u8BE5\u4EA4\u7ED9\u54EA\u4E2A agent \u5904\u7406\u3002
3451
-
3452
- \u53EF\u9009 agent \u5217\u8868\uFF08agentKey \u4E0E\u63CF\u8FF0\uFF09\uFF1A
3453
- ${agentList}
3454
-
3455
- \u5224\u65AD\u539F\u5219\uFF08\u9ED8\u8BA4\u504F\u5411\u901A\u7528\u6863\u4F4D\uFF1B\u4E13\u804C agent \u5728\u660E\u786E\u5339\u914D\u65F6\u4F7F\u7528\uFF1B**\u7F16\u7801/\u672C\u5730\u4ED3\u5E93\u4EFB\u52A1\u8D70\u901A\u7528\u6863 + needsWorkspace=true**\uFF09\uFF1A
3456
- - \u9ED8\u8BA4\u60C5\u51B5\u4E0B\uFF0C**\u9664\u975E\u7528\u6237\u660E\u786E\u8868\u8FBE\u8981\u5EFA\u5E94\u7528/\u5EFA Agent/\u63D0\u4EA4\u53CD\u9988/\u591A Agent \u7F16\u6392**\uFF0C\u5426\u5219\u9009\u901A\u7528\u6863\u4F4D\uFF08flash/balanced/quality\uFF09\uFF1A
3457
- - \u7B80\u5355\u95EE\u5019\u3001\u95F2\u804A\u3001\u5FEB\u901F\u95EE\u7B54\u3001\u7FFB\u8BD1\u3001\u77ED\u6587 \u2192 \u9009 flash \u6863
3458
- - \u9700\u8981\u63A8\u7406\u3001\u5206\u6790\u3001\u4E2D\u7B49\u957F\u5EA6\u5199\u4F5C\u3001\u666E\u901A\u4EE3\u7801\u89E3\u91CA/\u67B6\u6784\u95EE\u7B54 \u2192 \u9009 balanced \u6863
3459
- - \u590D\u6742\u63A8\u7406\u3001\u957F\u6587\u3001\u67B6\u6784\u8BBE\u8BA1\u3001\u6DF1\u5EA6\u5206\u6790 \u2192 \u9009 quality \u6863
3460
- - **\u660E\u786E\u7F16\u7801\u4EFB\u52A1\uFF08\u5B9E\u73B0\u529F\u80FD\u3001\u4FEE bug\u3001refactor\u3001\u5199/\u8865\u6D4B\u8BD5\u3001\u8DD1 build/tests/ci\uFF09\u6216\u67E5\u770B\u672C\u673A\u4ED3\u5E93/\u4EE3\u7801\u72B6\u6001\uFF08git/\u5206\u652F/commit/\u672C\u5730\u4E0E\u8FDC\u7A0B\u5DEE\u8DDD/\u672A\u63D0\u4EA4\u6539\u52A8\u7B49\uFF09** \u2192 \u9009 balanced \u6216 quality \u6863\uFF08\u6309\u590D\u6742\u5EA6\uFF1B**\u4E0D\u8981\u9009 flash**\uFF09\uFF0C\u4E14 needsWorkspace=true
3461
- - \u4E13\u804C agent \u8DEF\u7531\u89C4\u5219\uFF08\u6709\u660E\u786E\u5339\u914D\u65F6\u4F7F\u7528\uFF09\uFF1A
3462
- - \u8981\u63D0\u4EA4/\u8BB0\u5F55 bug\u3001\u4F53\u9A8C\u95EE\u9898\u3001\u6570\u636E\u5F02\u5E38\u3001\u4EA7\u54C1\u5EFA\u8BAE\uFF0C\u6216\u53E3\u8BED\u5982\u300C\u60F3\u53CD\u9988\u4E00\u4E9B\u95EE\u9898\u300D\u300C\u53CD\u9988\u4E00\u4E0B\u300D\u2192 \u9009\u610F\u89C1\u53CD\u9988\u4E13\u804C agent
3463
- - \u8981\u65B0\u5EFA\u6216\u5B9A\u5236 AI/Agent/\u667A\u80FD\u4F53\u52A9\u624B\uFF08\u542B\u914D\u7F6E\u5DE5\u5177\u3001\u77E5\u8BC6\u3001\u786E\u8BA4\u521B\u5EFA\uFF09\u2192 \u9009\u521B\u5EFA Agent \u4E13\u804C agent
3464
- - \u8981\u505A/\u6539/\u53D1\u5E03\u7F51\u7AD9\u3001\u7F51\u9875\u3001\u5C0F\u5E94\u7528\u3001\u9884\u7EA6\u9875\u3001\u535A\u5BA2\u3001\u770B\u677F \u2192 \u9009\u5E94\u7528\u6784\u5EFA\u4E13\u804C agent
3465
- - \u8981\u6D3E\u591A\u4E2A agent\u3001\u591A agent \u5E76\u884C\u3001\u62C6\u6210\u4EFB\u52A1\u540E\u53F0\u6267\u884C\u3001\u591A\u4E2A agent review\u3001\u8BA9\u4E0D\u540C agent \u8BA8\u8BBA/\u4F1A\u5546 \u2192 \u9009\u591A Agent \u7F16\u6392\u4E13\u804C agent\uFF08\u663E\u5F0F multi-agent \u4F18\u5148\uFF09
3466
- - **\u62FF\u4E0D\u51C6\u65F6**\uFF1A\u7EAF\u95F2\u804A\u9009 flash\uFF1B**\u82E5\u6CBE\u8FB9\u672C\u5730\u4ED3\u5E93/\u5F53\u524D\u4EE3\u7801\u72B6\u6001\uFF0C\u9009 balanced/quality + needsWorkspace=true**\u3002
3467
-
3468
- \u540C\u65F6\u5224\u65AD\u672C\u8F6E\u662F\u5426\u9700\u8981\u6CE8\u5165\u300C\u5DE5\u4F5C\u533A\u53EA\u8BFB\u5DE5\u5177\u300D\uFF08listFiles/globFiles/searchFiles/readFile/execShell \u7B49\uFF09\uFF1A
3469
- - needsWorkspace=true \u7684\u573A\u666F\uFF1A\u7528\u6237\u8981\u8BFB/\u6539/\u5206\u6790\u6587\u4EF6\u6216\u4EE3\u7801\u3001\u63D0\u5230\u4E86\u6587\u4EF6\u8DEF\u5F84/\u6269\u5C55\u540D\uFF08.md/.csv/.ts \u7B49\uFF09\u3001\u63D0\u5230\u5DE5\u4F5C\u533A/\u4EE3\u7801\u5E93/\u4ED3\u5E93/\u76EE\u5F55/git/\u5206\u652F\u3001\u8981\u770B\u5F53\u524D\u4EE3\u7801\u6216\u4ED3\u5E93\u72B6\u6001\u3001\u672C\u5730\u641C\u7D22/\u67E5\u770B\u4EE3\u7801\u3002
3470
- - needsWorkspace=false \u7684\u573A\u666F\uFF1A\u901A\u7528\u77E5\u8BC6\u95EE\u7B54\uFF08\u5982\u300C\u5E7F\u4E1C\u7ECF\u6D4E\u6570\u636E\u300D\u300C\u5199\u4E00\u9996\u8BD7\u300D\uFF09\u3001\u7EAF\u95F2\u804A\u95EE\u5019\u3001\u7FFB\u8BD1\u3001\u4E0D\u6D89\u53CA\u672C\u5730\u6587\u4EF6/\u4ED3\u5E93\u3002
3471
- - \u6F0F\u5224\uFF08\u7528\u6237\u8981\u6587\u4EF6\u800C\u6CA1\u5E26\u5DE5\u5177\uFF09\u6BD4\u8BEF\u5224\uFF08\u767D\u641C\u51E0\u8F6E\uFF09\u4EE3\u4EF7\u66F4\u9AD8\uFF0C\u5B81\u53EF\u504F\u5BBD\u5224 true\u3002
3472
-
3473
- \u5728\u7ED9\u51FA agentKey \u4E4B\u524D\uFF0C\u5148\u5728\u5FC3\u91CC\u533A\u5206\u5019\u9009\u6863\u4F4D\u7684\u8FB9\u754C\u518D\u7ED9\u7F6E\u4FE1\u5EA6\uFF1Abalanced \u4E0E quality \u7684\u8FB9\u754C\u5728\u4E8E\u590D\u6742\u5EA6/\u7BC7\u5E45/\u662F\u5426\u9700\u8981\u6DF1\u5EA6\u5206\u6790\uFF0C\u62FF\u4E0D\u51C6\u5C31\u7ED9\u4F4E confidence\u3002
3474
-
3475
- \u540C\u65F6\u5224\u65AD\u672C\u8F6E\u6D88\u606F\u662F\u5426\u9700\u8981\u6302\u8F7D\u5BF9\u8C61\u64CD\u4F5C\u6280\u80FD\uFF08skills \u6570\u7EC4\uFF0C\u672A\u547D\u4E2D\u7ED9 []\uFF09\uFF1A
3476
- - \u7528\u6237\u8981\u65B0\u5EFA/\u6574\u7406/\u5F55\u5165/\u6279\u91CF\u5904\u7406\u8868\u683C\u6570\u636E\uFF08\u5EFA\u8868\u3001\u52A0\u884C\u3001\u6539\u5217\u3001\u6309\u6761\u4EF6\u6574\u7406\u6570\u636E\uFF09\u2192 skills \u542B "table"
3477
- - \u7528\u6237\u8981\u5199/\u6DA6\u8272/\u6539\u5199/\u7EED\u5199/\u6392\u7248\u4E00\u7BC7\u6587\u6863\u6216\u6587\u7AE0 \u2192 skills \u542B "doc"
3478
- - \u7528\u6237\u8981\u5199/\u6539/\u5B9E\u73B0/\u4FEE bug/refactor/\u8865\u6D4B\u8BD5/\u5BA1\u67E5\u5E76\u4FEE\u6539\u4EE3\u7801 \u2192 skills \u542B "code"\uFF08\u4EC5\u95EE\u901A\u7528\u77E5\u8BC6\u3001\u7EAF\u95F2\u804A\u3001\u53EA\u67E5\u770B git/\u4ED3\u5E93\u72B6\u6001\u800C\u4E0D\u4FEE\u6539\u4EE3\u7801\u65F6\u4E0D\u6302 "code"\uFF09
3479
- - \u666E\u901A\u95EE\u7B54/\u7EAF\u95F2\u804A/\u5EFA\u5E94\u7528 \u2192 skills \u4E3A []
3480
-
3481
- \u53EA\u8F93\u51FA JSON\uFF0C\u4E0D\u8981\u8F93\u51FA\u5176\u4ED6\u5185\u5BB9\uFF1A
3482
- {"confidence": <0\u52301\u4E4B\u95F4\u7684\u5C0F\u6570\uFF0C\u8868\u793A\u4F60\u5BF9\u8FD9\u4E2A\u5206\u7C7B\u5224\u65AD\u7684\u628A\u63E1\u7A0B\u5EA6>, "agentKey": "<\u4ECE\u5217\u8868\u4E2D\u9009\u62E9\u7684 agentKey>", "needsWorkspace": <true \u6216 false>, "skills": <\u5B57\u7B26\u4E32\u6570\u7EC4\uFF0C\u5143\u7D20\u53EA\u80FD\u662F "table" | "doc" | "code"\uFF0C\u672A\u547D\u4E2D\u7ED9 []>}`;
3483
- }
3484
- function parseQuickChatIntentResult(content, tierAgents) {
3485
- try {
3486
- const parsed = JSON.parse(content.trim());
3487
- if (typeof parsed !== "object" || parsed === null || !("agentKey" in parsed)) return null;
3488
- const key = parsed.agentKey;
3489
- if (typeof key !== "string") return null;
3490
- if (!tierAgents.some((t2) => t2.agentKey === key)) return null;
3491
- const rawNeedsWorkspace = parsed.needsWorkspace;
3492
- const needsWorkspace = rawNeedsWorkspace === true;
3493
- const rawConfidence = parsed.confidence;
3494
- const confidence = typeof rawConfidence === "number" && Number.isFinite(rawConfidence) && rawConfidence >= 0 && rawConfidence <= 1 ? rawConfidence : void 0;
3495
- const rawSkills = parsed.skills;
3496
- let skills;
3497
- if (Array.isArray(rawSkills)) {
3498
- const filtered = [...new Set(rawSkills)].filter(
3499
- (s) => s === "table" || s === "doc" || s === "code"
3500
- );
3501
- if (filtered.length > 0) skills = filtered;
3502
- }
3503
- return { agentKey: key, needsWorkspace, confidence, skills };
3504
- } catch {
3505
- return null;
3506
- }
3507
- }
3508
-
3509
- // packages/cli/client/autoModelRouter.ts
3510
- var CLI_AUTO_TIER_AGENT_KEYS = {
3511
- flash: PUBLIC_DEEPSEEK_V4_FLASH_AGENT_KEY,
3512
- balanced: PUBLIC_DEEPSEEK_V4_PRO_AGENT_KEY,
3513
- quality: PUBLIC_GLM_52_AGENT_KEY
3514
- };
3515
- var CLI_IMAGE_AGENT_KEY = PUBLIC_KIMI_K26_IMAGE_AGENT_KEY;
3516
- var CLI_AUTO_TIER_AGENT_KEY_TABLE = {
3517
- [CLI_AUTO_TIER_AGENT_KEYS.flash]: true,
3518
- [CLI_AUTO_TIER_AGENT_KEYS.balanced]: true,
3519
- [CLI_AUTO_TIER_AGENT_KEYS.quality]: true
3520
- };
3521
- var buildRouteOptions = () => ["flash", "balanced", "quality"].map((tier) => ({
3522
- tier,
3523
- agentKey: CLI_AUTO_TIER_AGENT_KEYS[tier],
3524
- description: TIER_DESCRIPTIONS[tier]
3525
- }));
3526
- var tierForAgentKey = (agentKey) => {
3527
- for (const tier of ["flash", "balanced", "quality"]) {
3528
- if (CLI_AUTO_TIER_AGENT_KEYS[tier] === agentKey) return tier;
3529
- }
3530
- return null;
3531
- };
3532
- function resolveCliAutoFallbackTier(text) {
3533
- switch (estimateComplexity(text)) {
3534
- case "complex":
3535
- return "quality";
3536
- case "medium":
3537
- return "balanced";
3538
- case "simple":
3539
- default:
3540
- return "flash";
3541
- }
3542
- }
3543
- async function classifyCliAutoRoute(text, options) {
3544
- const timeoutMs = options.timeoutMs ?? QUICK_CHAT_INTENT_TIMEOUT_MS;
3545
- const fallbackTier = resolveCliAutoFallbackTier(text);
3546
- const fallback = () => ({
3547
- agentKey: CLI_AUTO_TIER_AGENT_KEYS[fallbackTier],
3548
- tier: fallbackTier,
3549
- classified: false
3550
- });
3551
- if (!text.trim()) return fallback();
3552
- if (isShortGreeting(text)) {
3553
- return {
3554
- agentKey: CLI_AUTO_TIER_AGENT_KEYS.flash,
3555
- tier: "flash",
3556
- classified: true
3557
- };
3558
- }
3559
- if (!options.authToken) return fallback();
3560
- const fetchImpl = options.fetchImpl ?? globalThis.fetch;
3561
- const controller = new AbortController();
3562
- const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs);
3563
- try {
3564
- const routeOptions = buildRouteOptions();
3565
- const messages = [
3566
- { role: "system", content: buildQuickChatIntentSystemPrompt(routeOptions) },
3567
- { role: "user", content: text }
3568
- ];
3569
- const { url, init } = buildPlatformChatCompletionRequest({
3570
- providerConfig: {
3571
- serverUrl: options.serverUrl,
3572
- authToken: options.authToken,
3573
- agentKey: CLI_AUTO_TIER_AGENT_KEYS.flash,
3574
- model: INTENT_MODEL,
3575
- provider: INTENT_PROVIDER,
3576
- endpoint: resolvePlatformChatCompletionsEndpoint(INTENT_PROVIDER) ?? "",
3577
- requestOptions: {}
3578
- },
3579
- messages,
3580
- stream: false
3581
- });
3582
- const response = await fetchImpl(url, {
3583
- ...init,
3584
- signal: controller.signal
3585
- });
3586
- if (!response.ok) return fallback();
3587
- const data = parsePlatformChatCompletionData(await response.text());
3588
- const content = data?.choices?.[0]?.message?.content;
3589
- if (typeof content !== "string" || !content.trim()) return fallback();
3590
- const parsed = parseQuickChatIntentResult(content, routeOptions);
3591
- if (!parsed) return fallback();
3592
- const tier = tierForAgentKey(parsed.agentKey) ?? fallbackTier;
3593
- return {
3594
- agentKey: parsed.agentKey,
3595
- tier,
3596
- classified: true,
3597
- confidence: parsed.confidence,
3598
- needsWorkspace: parsed.needsWorkspace,
3599
- skills: parsed.skills
3600
- };
3601
- } catch {
3602
- return fallback();
3603
- } finally {
3604
- clearTimeout(timeoutHandle);
3605
- }
3606
- }
3607
-
3608
4625
  // packages/cli/agentRunPrompts.ts
3609
4626
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3610
4627
  import { dirname, extname, isAbsolute, join, relative as relativePath, resolve, sep } from "node:path";
@@ -3948,16 +4965,16 @@ var buildCjkTokens = (text) => {
3948
4965
  }
3949
4966
  return tokens;
3950
4967
  };
3951
- var tokenize = (text) => {
4968
+ var tokenize2 = (text) => {
3952
4969
  const normalized = text.toLowerCase();
3953
4970
  const wordTokens = normalized.split(/[\s,.;:!?()[\]{}"'`,。!?:;、]+/).map((token) => token.trim()).filter((token) => token.length >= 2);
3954
4971
  return [...wordTokens, ...buildCjkTokens(normalized)];
3955
4972
  };
3956
4973
  var keywordScore = (query, item) => {
3957
- const queryTokens = new Set(tokenize(query));
4974
+ const queryTokens = new Set(tokenize2(query));
3958
4975
  if (queryTokens.size === 0) return 0;
3959
4976
  const haystack = /* @__PURE__ */ new Set([
3960
- ...tokenize(item.content),
4977
+ ...tokenize2(item.content),
3961
4978
  ...(item.tags ?? []).map((tag) => tag.toLowerCase()),
3962
4979
  item.patternKey?.toLowerCase() ?? ""
3963
4980
  ]);
@@ -4571,8 +5588,7 @@ ${mdContent}`);
4571
5588
  traceTools: parsed.traceTools,
4572
5589
  ...parsed.eventsMode ? { eventsMode: parsed.eventsMode } : {},
4573
5590
  ...parsed.taskEvidence ? { taskEvidence: parsed.taskEvidence } : {},
4574
- ...extraContextBlocks.length > 0 ? { extraContextBlocks } : {},
4575
- ...contextBlockScopes.length > 0 ? { contextBlockScopes } : {}
5591
+ ...contextBlockScopes.length > 0 ? { contextBlockScopes } : extraContextBlocks.length > 0 ? { extraContextBlocks } : {}
4576
5592
  });
4577
5593
  let result = await raceWithWatchdog(
4578
5594
  runner({
@@ -4587,12 +5603,20 @@ ${mdContent}`);
4587
5603
  `[nolo] quota exhausted on ${agentKey}, falling back to ${fallbackAgentKey}
4588
5604
  `
4589
5605
  );
4590
- const fallbackResult = await raceWithWatchdog(runner(buildRunOptions(fallbackAgentKey)));
5606
+ const fallbackResult = await raceWithWatchdog(
5607
+ runner({
5608
+ ...buildRunOptions(fallbackAgentKey),
5609
+ ...result.dialogId ? { continueDialogId: result.dialogId } : {}
5610
+ })
5611
+ );
4591
5612
  clearWatchdogs();
4592
5613
  if (fallbackResult.exitCode === 0) {
4593
5614
  result = fallbackResult;
4594
5615
  } else {
4595
- result = fallbackResult;
5616
+ result = {
5617
+ ...fallbackResult,
5618
+ ...fallbackResult.dialogId || result.dialogId ? { dialogId: fallbackResult.dialogId ?? result.dialogId } : {}
5619
+ };
4596
5620
  output.write(
4597
5621
  `[nolo] fallback to ${fallbackAgentKey} also failed: ${fallbackResult.localError instanceof Error ? fallbackResult.localError.message : String(fallbackResult.localError ?? "unknown error")}
4598
5622
  `
@@ -4682,12 +5706,45 @@ function isQuotaExhaustedError(error) {
4682
5706
  if (typeof maybeStatusCode === "number" && maybeStatusCode === 429) return true;
4683
5707
  return false;
4684
5708
  }
5709
+ var BALANCE_ERROR_PATTERNS = [
5710
+ /UPSTREAM_402/i,
5711
+ /Insufficient Balance/i,
5712
+ /insufficient\s+balance/i,
5713
+ /余额不足/
5714
+ ];
5715
+ function isBalanceExhaustedError(error) {
5716
+ if (!error) return false;
5717
+ const message = error instanceof Error ? error.message : typeof error === "string" ? error : String(error?.message ?? "");
5718
+ return BALANCE_ERROR_PATTERNS.some((pattern) => pattern.test(message));
5719
+ }
4685
5720
 
4686
5721
  export {
5722
+ CLI_AUTO_TIER_AGENT_KEYS,
5723
+ CLI_IMAGE_AGENT_KEY,
5724
+ CLI_AUTO_TIER_AGENT_KEY_TABLE,
5725
+ resolveCliAutoFallbackTier,
5726
+ classifyCliAutoRoute,
5727
+ resolveAgentContextWindow,
4687
5728
  formatTokenCount,
4688
5729
  renderTokenStatus,
4689
5730
  resolveCliColorEnabled,
4690
5731
  dimCliText,
5732
+ parseCliLocale,
5733
+ setCliLocale,
5734
+ initCliLocale,
5735
+ t,
5736
+ ANSI_ESCAPE_REGEX,
5737
+ stripAnsi,
5738
+ applyTerminalOutputToText,
5739
+ displayWidth,
5740
+ visibleWidth,
5741
+ truncateAnsi,
5742
+ fitAnsiLine,
5743
+ countPhysicalLines,
5744
+ takeDisplayWidth,
5745
+ padOrTruncateToWidth,
5746
+ wrapTranscriptLine,
5747
+ wrapTextToLines,
4691
5748
  THEME_PALETTES,
4692
5749
  getActiveThemeName,
4693
5750
  setActiveThemeName,
@@ -4696,22 +5753,24 @@ export {
4696
5753
  getActiveDensity,
4697
5754
  setActiveDensity,
4698
5755
  resolveTuiBrightness,
5756
+ setActiveTerminalBaseHex,
4699
5757
  surfaceBackgroundSequence,
4700
5758
  themeColorSequence,
4701
5759
  themeText,
4702
- highlightMarkdown,
4703
5760
  formatAssistantDisplay,
4704
5761
  normalizeThinkingDisplayMode,
4705
- parseCliLocale,
4706
- getCliLocale,
4707
- setCliLocale,
4708
- initCliLocale,
4709
- t,
4710
5762
  normalizeToolDisplayMode,
5763
+ COLLAPSED_PASTE_PLACEHOLDER_RE,
5764
+ shouldCollapsePaste,
5765
+ createCollapsedPasteStore,
5766
+ allocateCollapsedPaste,
5767
+ releaseCollapsedPaste,
5768
+ clearCollapsedPasteStore,
5769
+ replaceCollapsedPastesWithReferences,
5770
+ releaseCollapsedPasteReferences,
5771
+ expandRangeToCollapsedPasteChips,
5772
+ findCollapsedPasteSpanAt,
4711
5773
  runAgentTurn,
4712
- CLI_IMAGE_AGENT_KEY,
4713
- CLI_AUTO_TIER_AGENT_KEY_TABLE,
4714
- classifyCliAutoRoute,
4715
5774
  resolveWorkflowReference,
4716
5775
  resolveSkillReference,
4717
5776
  buildSkillContextBlocks,
@@ -4719,5 +5778,6 @@ export {
4719
5778
  normalizeCliImageInput,
4720
5779
  inspectLocalRunWorkspace,
4721
5780
  runAgentRunCommand,
4722
- isQuotaExhaustedError
5781
+ isQuotaExhaustedError,
5782
+ isBalanceExhaustedError
4723
5783
  };