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

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