nolo-cli 0.6.1 → 0.8.0-alpha.10

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 (175) hide show
  1. package/_executeModel-CPDNUUQV.js +198 -0
  2. package/action-PAXRPJJF.js +27 -0
  3. package/addContentAction-CZVWF3QZ.js +25 -0
  4. package/addDialogAgentAction-CCIDTPHC.js +67 -0
  5. package/agentDeleteCommand-OSUMJMZM.js +323 -0
  6. package/agentEmailCommands-OQTJIPQU.js +621 -0
  7. package/agentListCommands-FATQEUYT.js +219 -0
  8. package/agentMachineCommands-6LZWF4GL.js +421 -0
  9. package/agentPullCommand-O2MTUAFR.js +122 -0
  10. package/agentRecordCommands-TIHFU65Z.js +89 -0
  11. package/agentRunCommand-R64Y3HCH.js +102 -0
  12. package/agentRunControl-K4VKNEHF.js +57 -0
  13. package/agentRuntimeLocal-6PIWAHLQ.js +99 -0
  14. package/agentSlice-TP4VARJF.js +41 -0
  15. package/authCommand-WXMD3IPR.js +574 -0
  16. package/authCommands-X74AFDTC.js +26 -0
  17. package/chunk-2EISRLRC.js +238 -0
  18. package/chunk-2FTTDCD4.js +675 -0
  19. package/chunk-2MALU52L.js +51 -0
  20. package/chunk-2TPUKEU7.js +1078 -0
  21. package/chunk-3ASDTTEI.js +1774 -0
  22. package/chunk-3EUW3WEO.js +8 -0
  23. package/chunk-3LY4F7SV.js +9293 -0
  24. package/chunk-4CQLVGRV.js +50 -0
  25. package/chunk-4IJYA27C.js +342 -0
  26. package/chunk-4SN6J65J.js +38 -0
  27. package/chunk-4VNS5WPM.js +42 -0
  28. package/chunk-572EKYLF.js +792 -0
  29. package/chunk-5MIDPSSK.js +206 -0
  30. package/chunk-5MMAK75Q.js +15 -0
  31. package/chunk-5VV37AT4.js +222 -0
  32. package/chunk-63W5N6P3.js +150 -0
  33. package/chunk-6A5EF6IR.js +929 -0
  34. package/chunk-6HYVJV7B.js +102 -0
  35. package/chunk-6P2WX5IA.js +3631 -0
  36. package/chunk-6WD3OOJS.js +20 -0
  37. package/chunk-ATNTHIZI.js +13 -0
  38. package/chunk-AUGHVUFE.js +69 -0
  39. package/chunk-AVMKZHBC.js +85 -0
  40. package/chunk-AZPCASRE.js +36 -0
  41. package/chunk-BNMT7YY5.js +34 -0
  42. package/chunk-BR2WP3DF.js +548 -0
  43. package/chunk-BSTNFPHD.js +231 -0
  44. package/chunk-BU4OPIXC.js +171 -0
  45. package/chunk-CF2WZF4N.js +134 -0
  46. package/chunk-CF7PE763.js +10 -0
  47. package/chunk-CKOJQDKR.js +198 -0
  48. package/chunk-CM54RSSA.js +206 -0
  49. package/chunk-CWMSB67P.js +34 -0
  50. package/chunk-DA2OEKS6.js +27 -0
  51. package/chunk-DFFIZ25G.js +30 -0
  52. package/chunk-DWLEOOGR.js +8 -0
  53. package/chunk-DZ3XJVET.js +231 -0
  54. package/chunk-EHLHSX7R.js +1433 -0
  55. package/chunk-ELRRLDWI.js +478 -0
  56. package/chunk-EYEEHCU4.js +3066 -0
  57. package/chunk-FQZWM5YX.js +628 -0
  58. package/chunk-G3SUCT4W.js +8 -0
  59. package/chunk-G5H7RNFL.js +807 -0
  60. package/chunk-GAKEBQD6.js +30 -0
  61. package/chunk-GBRG75BQ.js +86 -0
  62. package/chunk-H2RMRZJE.js +113 -0
  63. package/chunk-H33743XH.js +25 -0
  64. package/chunk-HD63YREL.js +19 -0
  65. package/chunk-HEIIIZBX.js +104 -0
  66. package/chunk-HNMXYYJR.js +12 -0
  67. package/chunk-HOMYP7GZ.js +2152 -0
  68. package/chunk-HZFOMJRA.js +4300 -0
  69. package/chunk-IAXBCTTE.js +175 -0
  70. package/chunk-IDZTNRP2.js +85 -0
  71. package/chunk-IHHA5Y2S.js +9013 -0
  72. package/chunk-ISNCLGUZ.js +146 -0
  73. package/chunk-IYQV26RA.js +138 -0
  74. package/chunk-J4576WMJ.js +91 -0
  75. package/chunk-JBTJAPUF.js +35 -0
  76. package/chunk-JZPBVBIY.js +94 -0
  77. package/chunk-KQHGZWSV.js +63 -0
  78. package/chunk-KUG52IAP.js +8 -0
  79. package/chunk-KX6YBIHG.js +8 -0
  80. package/chunk-LCBAMY2M.js +60 -0
  81. package/chunk-LM25IZBJ.js +902 -0
  82. package/chunk-LM6IYQ3F.js +16 -0
  83. package/chunk-M76ECQRD.js +13658 -0
  84. package/chunk-MRC4Q34M.js +188 -0
  85. package/chunk-NEMRK4EK.js +944 -0
  86. package/chunk-NXZGF7F7.js +2878 -0
  87. package/chunk-OF5H3QWS.js +300 -0
  88. package/chunk-OLZBQWZU.js +6 -0
  89. package/chunk-OOYVBFM7.js +191 -0
  90. package/chunk-OWOHNGCN.js +50 -0
  91. package/chunk-PN7AFLOK.js +3401 -0
  92. package/chunk-POCFIDGQ.js +562 -0
  93. package/chunk-Q6WACBKB.js +1949 -0
  94. package/chunk-QFV2OAIM.js +421 -0
  95. package/chunk-QJ6V2NIN.js +1447 -0
  96. package/chunk-QSJ3HFEF.js +8 -0
  97. package/chunk-QUWDTOUP.js +190 -0
  98. package/chunk-QYXLLLGL.js +4519 -0
  99. package/chunk-ROWQEJRP.js +139 -0
  100. package/chunk-S7Z2HMCN.js +25 -0
  101. package/chunk-TDIQFT2F.js +301 -0
  102. package/chunk-TILFE6LV.js +381 -0
  103. package/chunk-TKOUBWBO.js +24674 -0
  104. package/chunk-TYH5JE6N.js +27158 -0
  105. package/chunk-U4TRT5V4.js +246 -0
  106. package/chunk-U6VC4MU4.js +8 -0
  107. package/chunk-UAD2WT54.js +64 -0
  108. package/chunk-USZMK5UT.js +105 -0
  109. package/chunk-VEJLAVTC.js +2052 -0
  110. package/chunk-VF4K2QE6.js +21 -0
  111. package/chunk-VHBWHNVT.js +59 -0
  112. package/chunk-VIDPVFP3.js +20 -0
  113. package/chunk-VP4WJWKW.js +8882 -0
  114. package/chunk-VQ2AJRGD.js +148 -0
  115. package/chunk-W4NVOSFF.js +37 -0
  116. package/chunk-WWC7I6TA.js +8 -0
  117. package/chunk-X7W4TNB4.js +1550 -0
  118. package/chunk-XDVAYP26.js +537 -0
  119. package/chunk-XKIMRZO2.js +221 -0
  120. package/chunk-Y5GCAMGP.js +9 -0
  121. package/chunk-YWPXYBY7.js +360 -0
  122. package/chunk-Z4NR32XR.js +50 -0
  123. package/chunk-ZNFJWEN4.js +56 -0
  124. package/cliExecutor-DNE7TCC4.js +36 -0
  125. package/clipboardy-BVE3LVEH.js +12001 -0
  126. package/createAgentAutomationAction-KLKS2HGU.js +864 -0
  127. package/createDialogAction-Z33JMBF5.js +52 -0
  128. package/createPageAction-AQVNOAAI.js +169 -0
  129. package/db-HCJ5PRB5.js +271 -0
  130. package/dbSlice-C4UJLRZP.js +59 -0
  131. package/deleteFile-4SKGPHOZ.js +41 -0
  132. package/dialogCommands-W5PD6MZU.js +59 -0
  133. package/dialogSlice-RBJYVSAR.js +146 -0
  134. package/docCreateCommands-MM34S4Z3.js +215 -0
  135. package/docDeleteCommands-JJTU722V.js +176 -0
  136. package/docReadCommands-GLYDMCTU.js +129 -0
  137. package/docStore-5TYME4DO.js +96 -0
  138. package/docUpdateCommands-ZCGQ7URR.js +313 -0
  139. package/fetchMessages-JOCYIFKL.js +16 -0
  140. package/getPricing-PWPJ6MNV.js +30 -0
  141. package/handleSendMessageAction-KA2MRW6E.js +183 -0
  142. package/index.js +184 -112254
  143. package/lib-FYYBQIXS.js +19839 -0
  144. package/localDialogRead-D3BELSTF.js +26 -0
  145. package/localFirstLog-DHNOY2NA.js +19 -0
  146. package/localRuntimeAuthority-TEGCOWX4.js +471 -0
  147. package/localRuntimeDb-LGDF6FQP.js +12 -0
  148. package/machineCommands-ZB5WUSPF.js +774 -0
  149. package/machineInfo-M6KB6HZN.js +10 -0
  150. package/machineWsRunDispatch-GWCAPPCM.js +105 -0
  151. package/memoryCommands-O4MIPIWQ.js +476 -0
  152. package/models-XLEOET3E.js +9 -0
  153. package/offlineMarxistsAgentCommand-BCPJITJL.js +242 -0
  154. package/package.json +3 -25
  155. package/read-RIJCIVKX.js +17 -0
  156. package/readDocTool-GZD2N3LO.js +16 -0
  157. package/readlineWorkspace-FEDY44AA.js +5357 -0
  158. package/removeDialogAgentAction-CXLST3N6.js +72 -0
  159. package/runAgentClientLoop-W4NKE64V.js +288 -0
  160. package/runtimeDoctorCommands-LRHLPDRC.js +289 -0
  161. package/setDialogExtraReferencesAction-LP6VYGU7.js +59 -0
  162. package/setPrimaryDialogAgentAction-D3GGH23T.js +79 -0
  163. package/settingSlice-J4I3Z5H7.js +198 -0
  164. package/skillSummaryMarker-42HKSXYY.js +9 -0
  165. package/slateToRenderMarkdown-Y5IYN73U.js +7 -0
  166. package/slateUtils-2M7MUTWX.js +19 -0
  167. package/spaceCommands-LC4FDAKE.js +450 -0
  168. package/spaceSlice-32XOGBEF.js +145 -0
  169. package/streamAgentChatTurn-YQW32AQH.js +4503 -0
  170. package/tableCommands-GOFP7QKA.js +1286 -0
  171. package/toolApiClient-QI3YBNWB.js +26 -0
  172. package/tools-XGTFRYD7.js +83 -0
  173. package/understanding-HCSFPE7R.js +318 -0
  174. package/updateCommands-VOSPKXFF.js +31 -0
  175. package/updateDialogTitleAction-4LHQDNHQ.js +185 -0
@@ -0,0 +1,4300 @@
1
+ import {
2
+ createRunActivityTracker,
3
+ finalizeRunRecord,
4
+ spawnLocalBackgroundRun
5
+ } from "./chunk-2FTTDCD4.js";
6
+ import {
7
+ buildLocalRunEnv,
8
+ isFullstackCodingAgentRef,
9
+ parseAgentRunArgs,
10
+ readFlagValue,
11
+ resolveRawAgentInput,
12
+ resolveServerUrl,
13
+ writeUsage
14
+ } from "./chunk-DZ3XJVET.js";
15
+ import {
16
+ createCliCallAgentToolExecutor,
17
+ createCliLocalRuntimeAdapter,
18
+ isBuiltinNoloAgentRef
19
+ } from "./chunk-IHHA5Y2S.js";
20
+ import {
21
+ getReadableCliDb
22
+ } from "./chunk-W4NVOSFF.js";
23
+ import {
24
+ readDbRecord,
25
+ resolveAgentRecordFromHybridStore
26
+ } from "./chunk-FQZWM5YX.js";
27
+ import {
28
+ normalizeAgentHandle
29
+ } from "./chunk-HD63YREL.js";
30
+ import {
31
+ NOLO_PROJECT_MANAGER_AGENT_KEY,
32
+ isLocalCliAgentKey
33
+ } from "./chunk-VHBWHNVT.js";
34
+ import {
35
+ applyModelLayerOverride,
36
+ buildModelLayerOverride,
37
+ buildSkillDiscoveryLayer
38
+ } from "./chunk-CM54RSSA.js";
39
+ import {
40
+ CliProviderQuotaError
41
+ } from "./chunk-X7W4TNB4.js";
42
+ import {
43
+ isGatewayHttpStatus
44
+ } from "./chunk-GAKEBQD6.js";
45
+ import {
46
+ load,
47
+ parseExternalSkillMarkdown,
48
+ parseSkillDocProtocol
49
+ } from "./chunk-NXZGF7F7.js";
50
+ import {
51
+ getModelContextWindow,
52
+ parseUiAskChoiceContent
53
+ } from "./chunk-TKOUBWBO.js";
54
+ import {
55
+ readPipeText,
56
+ spawnProcess
57
+ } from "./chunk-IDZTNRP2.js";
58
+ import {
59
+ resolveAuthToken
60
+ } from "./chunk-ISNCLGUZ.js";
61
+ import {
62
+ buildPlatformChatCompletionRequest,
63
+ parsePlatformChatCompletionData
64
+ } from "./chunk-6P2WX5IA.js";
65
+ import {
66
+ resolveAgentRuntimeConfigFromRecord
67
+ } from "./chunk-TILFE6LV.js";
68
+ import {
69
+ LOCAL_AGENT_CONFIG_MISSING_CODE,
70
+ LOCAL_TURN_ABORTED_CODE,
71
+ readActionGate,
72
+ readCommandActionGatePayload
73
+ } from "./chunk-NEMRK4EK.js";
74
+ import {
75
+ resolvePlatformChatCompletionsEndpoint
76
+ } from "./chunk-XDVAYP26.js";
77
+ import {
78
+ clipCompactText
79
+ } from "./chunk-5MMAK75Q.js";
80
+ import {
81
+ findModelConfig
82
+ } from "./chunk-572EKYLF.js";
83
+ import {
84
+ compactWhitespace
85
+ } from "./chunk-DWLEOOGR.js";
86
+ import {
87
+ toErrorMessage
88
+ } from "./chunk-HNMXYYJR.js";
89
+ import {
90
+ parsePositiveFiniteNumberOrFallback
91
+ } from "./chunk-Y5GCAMGP.js";
92
+ import {
93
+ require_index_umd
94
+ } from "./chunk-PN7AFLOK.js";
95
+ import {
96
+ asTrimmedLowercaseString
97
+ } from "./chunk-QSJ3HFEF.js";
98
+ import {
99
+ PUBLIC_DEEPSEEK_V4_FLASH_AGENT_KEY,
100
+ PUBLIC_DEEPSEEK_V4_PRO_AGENT_KEY,
101
+ PUBLIC_GLM_52_AGENT_KEY,
102
+ PUBLIC_KIMI_K26_IMAGE_AGENT_KEY
103
+ } from "./chunk-ELRRLDWI.js";
104
+ import {
105
+ isLoopbackUrl
106
+ } from "./chunk-4SN6J65J.js";
107
+ import {
108
+ asTrimmedString
109
+ } from "./chunk-3EUW3WEO.js";
110
+ import {
111
+ asOptionalTrimmedString
112
+ } from "./chunk-G3SUCT4W.js";
113
+ import {
114
+ __toESM
115
+ } from "./chunk-4VNS5WPM.js";
116
+
117
+ // packages/cli/client/tokenUsage.ts
118
+ var PROVIDER_LOOKUP_ORDER = [
119
+ "openrouter",
120
+ "fireworks",
121
+ "openai",
122
+ "mimo",
123
+ "gmi",
124
+ "google",
125
+ "deepseek",
126
+ "mistral",
127
+ "vultr",
128
+ "deepinfra",
129
+ "cloudflare"
130
+ ];
131
+ function parseUsageRecord(usage) {
132
+ if (!usage || typeof usage !== "object") return void 0;
133
+ const input = Number(usage.input_tokens ?? usage.prompt_tokens ?? 0);
134
+ const output = Number(usage.output_tokens ?? usage.completion_tokens ?? 0);
135
+ if (!Number.isFinite(input) || !Number.isFinite(output)) return void 0;
136
+ if (!input && !output) return void 0;
137
+ return { input, output };
138
+ }
139
+ function resolveContextWindow(model) {
140
+ const raw = model?.trim();
141
+ if (!raw) return void 0;
142
+ for (const provider of PROVIDER_LOOKUP_ORDER) {
143
+ const config = findModelConfig(provider, raw);
144
+ if (config?.contextWindow) return config.contextWindow;
145
+ }
146
+ return getModelContextWindow(raw);
147
+ }
148
+ function buildTurnTokenUsage(usage, model) {
149
+ const parsed = parseUsageRecord(usage);
150
+ if (!parsed) return void 0;
151
+ const contextWindow = resolveContextWindow(model);
152
+ const remaining = contextWindow && parsed.input > 0 ? Math.max(0, contextWindow - parsed.input) : void 0;
153
+ return {
154
+ ...parsed,
155
+ ...contextWindow ? { contextWindow } : {},
156
+ ...remaining != null ? { remaining } : {}
157
+ };
158
+ }
159
+ function formatTokenCount(value) {
160
+ if (!Number.isFinite(value) || value < 0) return "\u2014";
161
+ if (value < 1e3) return String(Math.round(value));
162
+ if (value < 1e6) {
163
+ const compact2 = value / 1e3;
164
+ if (Number.isInteger(compact2)) return `${compact2}k`;
165
+ return `${compact2.toFixed(1).replace(/\.0$/, "")}k`;
166
+ }
167
+ const compact = value / 1e6;
168
+ return compact >= 100 ? `${Math.round(compact)}M` : `${compact.toFixed(1).replace(/\.0$/, "")}M`;
169
+ }
170
+ function renderTokenStatus(tokens) {
171
+ if (!tokens) return "in \u2014 out \u2014 left \u2014";
172
+ const left = tokens.remaining != null ? formatTokenCount(tokens.remaining) : tokens.contextWindow ? "\u2014" : "\u2014";
173
+ return `in ${formatTokenCount(tokens.input)} out ${formatTokenCount(tokens.output)} left ${left}`;
174
+ }
175
+ function shouldShowUsage(env) {
176
+ return env.NOLO_DEBUG === "1" || env.NOLO_SHOW_USAGE === "1";
177
+ }
178
+ function formatUsage(usage, dialogId) {
179
+ const parts = [];
180
+ if (typeof dialogId === "string" && dialogId)
181
+ parts.push(`dialog=${dialogId}`);
182
+ const input = usage?.input_tokens ?? usage?.prompt_tokens ?? 0;
183
+ const output = usage?.output_tokens ?? usage?.completion_tokens ?? 0;
184
+ if (input || output) parts.push(`tokens=${input}+${output}`);
185
+ return parts.length ? ` (${parts.join(" ")})` : "";
186
+ }
187
+
188
+ // packages/cli/client/terminalStyles.ts
189
+ var ANSI_FG = {
190
+ dim: "\x1B[2m",
191
+ bold: "\x1B[1m",
192
+ cyan: "\x1B[36m",
193
+ blue: "\x1B[34m",
194
+ green: "\x1B[32m",
195
+ red: "\x1B[31m",
196
+ yellow: "\x1B[33m",
197
+ magenta: "\x1B[35m",
198
+ white: "\x1B[37m",
199
+ black: "\x1B[30m"
200
+ };
201
+ var ANSI_BG = {
202
+ bgCyan: "\x1B[46m",
203
+ bgGray: "\x1B[100m",
204
+ bgMagenta: "\x1B[45m",
205
+ bgYellow: "\x1B[43m",
206
+ bgBlue: "\x1B[44m",
207
+ bgGreen: "\x1B[42m",
208
+ bgRed: "\x1B[41m",
209
+ bgWhite: "\x1B[47m"
210
+ };
211
+ var RESET = "\x1B[0m";
212
+ function resolveCliColorEnabled(env = process.env, isTTY = Boolean(process.stdout.isTTY)) {
213
+ const setting = asTrimmedLowercaseString(env.NOLO_CLI_COLOR);
214
+ if (setting === "0" || setting === "false" || setting === "off") return false;
215
+ if (env.NO_COLOR) return false;
216
+ if (setting === "1" || setting === "true" || setting === "on") return true;
217
+ return isTTY;
218
+ }
219
+ function styleCliText(text, style, enabled = resolveCliColorEnabled()) {
220
+ if (!enabled || !text) return text;
221
+ const code = ANSI_FG[style] ?? ANSI_BG[style];
222
+ if (!code) return text;
223
+ return `${code}${text}${RESET}`;
224
+ }
225
+ function dimCliText(text, enabled = resolveCliColorEnabled()) {
226
+ return styleCliText(text, "dim", enabled);
227
+ }
228
+
229
+ // packages/cli/tui/theme.ts
230
+ var THEME_PALETTES = {
231
+ trail: {
232
+ light: {
233
+ accent: { hex: "2E7DB5", ansiFallback: "\x1B[34m" },
234
+ // ocean blue
235
+ chrome: { hex: "768594", ansiFallback: "\x1B[90m" },
236
+ // slate — two steps below app placeholder A3B0BD (was 8A97A5); rules/tool markers on white need the extra notch
237
+ success: { hex: "3F8F5C", ansiFallback: "\x1B[32m" },
238
+ // moss green
239
+ warning: { hex: "9A6A1F", ansiFallback: "\x1B[33m" },
240
+ // deeper amber — B57F2E still read as yellow/washed on white (H2 headers like ## Plan floated); 9A6A1F raises contrast
241
+ info: { hex: "4A9FD4", ansiFallback: "\x1B[36m" },
242
+ // sky blue
243
+ danger: { hex: "C45C4A", ansiFallback: "\x1B[31m" },
244
+ // reddish
245
+ muted: { hex: "5E6A78", ansiFallback: "\x1B[90m" }
246
+ // denser slate — 687584 read too blue in dense prose (inline code/tool labels); 5E6A78 pulls blue down while staying darker than the old value
247
+ },
248
+ dark: {
249
+ accent: { hex: "89B4FA", ansiFallback: "\x1B[34m" },
250
+ // blue
251
+ chrome: { hex: "6C7086", ansiFallback: "\x1B[90m" },
252
+ success: { hex: "A6E3A1", ansiFallback: "\x1B[32m" },
253
+ warning: { hex: "F9E2AF", ansiFallback: "\x1B[33m" },
254
+ info: { hex: "94E2D5", ansiFallback: "\x1B[36m" },
255
+ danger: { hex: "F38BA8", ansiFallback: "\x1B[31m" },
256
+ muted: { hex: "9AA3B8", ansiFallback: "\x1B[90m" }
257
+ // dimmed from A6ADC8 so secondary text sits a step below body text
258
+ }
259
+ },
260
+ catppuccin: {
261
+ light: {
262
+ accent: { hex: "0969DA", ansiFallback: "\x1B[34m" },
263
+ chrome: { hex: "57606A", ansiFallback: "\x1B[90m" },
264
+ success: { hex: "116329", ansiFallback: "\x1B[32m" },
265
+ warning: { hex: "9A6700", ansiFallback: "\x1B[33m" },
266
+ info: { hex: "0969DA", ansiFallback: "\x1B[36m" },
267
+ danger: { hex: "CF222E", ansiFallback: "\x1B[31m" },
268
+ muted: { hex: "6E7781", ansiFallback: "\x1B[90m" }
269
+ },
270
+ dark: {
271
+ accent: { hex: "58A6FF", ansiFallback: "\x1B[34m" },
272
+ // GitHub sky blue
273
+ chrome: { hex: "6E7681", ansiFallback: "\x1B[90m" },
274
+ success: { hex: "3FB950", ansiFallback: "\x1B[32m" },
275
+ warning: { hex: "D29922", ansiFallback: "\x1B[33m" },
276
+ info: { hex: "58A6FF", ansiFallback: "\x1B[36m" },
277
+ danger: { hex: "FF7B72", ansiFallback: "\x1B[31m" },
278
+ muted: { hex: "B1BAC4", ansiFallback: "\x1B[90m" }
279
+ }
280
+ },
281
+ wave: {
282
+ light: {
283
+ accent: { hex: "4D699B", ansiFallback: "\x1B[34m" },
284
+ // ink blue
285
+ chrome: { hex: "9E9B8E", ansiFallback: "\x1B[90m" },
286
+ success: { hex: "6F894E", ansiFallback: "\x1B[32m" },
287
+ warning: { hex: "836F4A", ansiFallback: "\x1B[33m" },
288
+ info: { hex: "4D699B", ansiFallback: "\x1B[36m" },
289
+ danger: { hex: "C84053", ansiFallback: "\x1B[31m" },
290
+ muted: { hex: "716E61", ansiFallback: "\x1B[90m" }
291
+ },
292
+ dark: {
293
+ accent: { hex: "7E9CD8", ansiFallback: "\x1B[34m" },
294
+ // crystalBlue
295
+ chrome: { hex: "727169", ansiFallback: "\x1B[90m" },
296
+ success: { hex: "98BB6C", ansiFallback: "\x1B[32m" },
297
+ warning: { hex: "E6C384", ansiFallback: "\x1B[33m" },
298
+ info: { hex: "7FB4CA", ansiFallback: "\x1B[36m" },
299
+ danger: { hex: "E82424", ansiFallback: "\x1B[31m" },
300
+ muted: { hex: "938AA9", ansiFallback: "\x1B[90m" }
301
+ }
302
+ },
303
+ iris: {
304
+ light: {
305
+ accent: { hex: "5E6AD2", ansiFallback: "\x1B[34m" },
306
+ chrome: { hex: "9E9AB5", ansiFallback: "\x1B[90m" },
307
+ success: { hex: "16A34A", ansiFallback: "\x1B[32m" },
308
+ warning: { hex: "D97706", ansiFallback: "\x1B[33m" },
309
+ info: { hex: "5E6AD2", ansiFallback: "\x1B[36m" },
310
+ danger: { hex: "E5484D", ansiFallback: "\x1B[31m" },
311
+ muted: { hex: "6E6A8A", ansiFallback: "\x1B[90m" }
312
+ },
313
+ dark: {
314
+ accent: { hex: "8B9CF4", ansiFallback: "\x1B[34m" },
315
+ chrome: { hex: "5A5772", ansiFallback: "\x1B[90m" },
316
+ success: { hex: "26BD6C", ansiFallback: "\x1B[32m" },
317
+ warning: { hex: "F5A623", ansiFallback: "\x1B[33m" },
318
+ info: { hex: "8B9CF4", ansiFallback: "\x1B[36m" },
319
+ danger: { hex: "EC5B6E", ansiFallback: "\x1B[31m" },
320
+ muted: { hex: "938AA9", ansiFallback: "\x1B[90m" }
321
+ }
322
+ },
323
+ rose: {
324
+ light: {
325
+ accent: { hex: "D14D72", ansiFallback: "\x1B[35m" },
326
+ chrome: { hex: "9893A5", ansiFallback: "\x1B[90m" },
327
+ success: { hex: "56949F", ansiFallback: "\x1B[32m" },
328
+ warning: { hex: "EA9D34", ansiFallback: "\x1B[33m" },
329
+ info: { hex: "286983", ansiFallback: "\x1B[36m" },
330
+ danger: { hex: "B4637A", ansiFallback: "\x1B[31m" },
331
+ muted: { hex: "797593", ansiFallback: "\x1B[90m" }
332
+ },
333
+ dark: {
334
+ accent: { hex: "EB6F92", ansiFallback: "\x1B[35m" },
335
+ chrome: { hex: "6E6A86", ansiFallback: "\x1B[90m" },
336
+ success: { hex: "9CCFD8", ansiFallback: "\x1B[32m" },
337
+ warning: { hex: "F6C177", ansiFallback: "\x1B[33m" },
338
+ info: { hex: "C4A7E7", ansiFallback: "\x1B[36m" },
339
+ danger: { hex: "EB6F92", ansiFallback: "\x1B[31m" },
340
+ muted: { hex: "908CAA", ansiFallback: "\x1B[90m" }
341
+ }
342
+ },
343
+ mono: {
344
+ light: {
345
+ accent: { hex: "FF9500", ansiFallback: "\x1B[33m" },
346
+ chrome: { hex: "9DA6AF", ansiFallback: "\x1B[90m" },
347
+ success: { hex: "16A34A", ansiFallback: "\x1B[32m" },
348
+ warning: { hex: "D97706", ansiFallback: "\x1B[33m" },
349
+ info: { hex: "2563EB", ansiFallback: "\x1B[34m" },
350
+ danger: { hex: "DC2626", ansiFallback: "\x1B[31m" },
351
+ muted: { hex: "7B8793", ansiFallback: "\x1B[90m" }
352
+ },
353
+ dark: {
354
+ accent: { hex: "FFBF66", ansiFallback: "\x1B[33m" },
355
+ chrome: { hex: "7B8793", ansiFallback: "\x1B[90m" },
356
+ success: { hex: "4DE800", ansiFallback: "\x1B[32m" },
357
+ warning: { hex: "FCD34D", ansiFallback: "\x1B[33m" },
358
+ info: { hex: "60A5FA", ansiFallback: "\x1B[34m" },
359
+ danger: { hex: "F87171", ansiFallback: "\x1B[31m" },
360
+ muted: { hex: "C1C7CD", ansiFallback: "\x1B[90m" }
361
+ }
362
+ }
363
+ };
364
+ var activeThemeName = "catppuccin";
365
+ function getActiveThemeName() {
366
+ return activeThemeName;
367
+ }
368
+ function setActiveThemeName(name) {
369
+ if (THEME_PALETTES[name]) {
370
+ activeThemeName = name;
371
+ return true;
372
+ }
373
+ return false;
374
+ }
375
+ var activeBrightness = null;
376
+ function getActiveBrightness() {
377
+ return activeBrightness;
378
+ }
379
+ function setActiveBrightness(brightness) {
380
+ activeBrightness = brightness;
381
+ }
382
+ var activeDensity = "spacious";
383
+ function getActiveDensity() {
384
+ return activeDensity;
385
+ }
386
+ function setActiveDensity(density) {
387
+ activeDensity = density;
388
+ }
389
+ function supportsTruecolor(env = process.env) {
390
+ return /truecolor|24bit/i.test(env.COLORTERM ?? "");
391
+ }
392
+ function resolveTuiBrightness(env = process.env) {
393
+ const explicit = (env.NOLO_TUI_THEME ?? "").trim().toLowerCase();
394
+ if (explicit === "light") return "light";
395
+ if (explicit === "dark") return "dark";
396
+ if (activeBrightness) return activeBrightness;
397
+ const colorfgbg = env.COLORFGBG ?? "";
398
+ if (colorfgbg) {
399
+ const parts = colorfgbg.split(";");
400
+ const bg = parts.length > 1 ? Number.parseInt(parts[1] ?? "", 10) : NaN;
401
+ if (!Number.isNaN(bg) && bg >= 0 && bg <= 6) return "dark";
402
+ if (!Number.isNaN(bg) && bg >= 7 && bg <= 15) return "light";
403
+ }
404
+ return "dark";
405
+ }
406
+ function hexToSgr(hex) {
407
+ const r = Number.parseInt(hex.slice(0, 2), 16);
408
+ const g = Number.parseInt(hex.slice(2, 4), 16);
409
+ const b = Number.parseInt(hex.slice(4, 6), 16);
410
+ return `\x1B[38;2;${r};${g};${b}m`;
411
+ }
412
+ var SURFACE_HEX = {
413
+ trail: { light: "E4E8EC", dark: "313244" },
414
+ catppuccin: { light: "E8ECF0", dark: "161B22" },
415
+ wave: { light: "E5E1D6", dark: "2A2A37" },
416
+ iris: { light: "E9E8F2", dark: "2A2740" },
417
+ rose: { light: "F2EAE4", dark: "26233A" },
418
+ mono: { light: "E8EAED", dark: "2A2A2A" }
419
+ };
420
+ var TERMINAL_BASE = {
421
+ dark: "1E1E2E",
422
+ light: "FFFFFF"
423
+ };
424
+ function readHexChannels(hex) {
425
+ if (!/^[0-9A-F]{6}$/.test(hex)) return null;
426
+ return [
427
+ Number.parseInt(hex.slice(0, 2), 16),
428
+ Number.parseInt(hex.slice(2, 4), 16),
429
+ Number.parseInt(hex.slice(4, 6), 16)
430
+ ];
431
+ }
432
+ function hexToBgSgr(hex) {
433
+ const channels = readHexChannels(hex.replace(/^#/, "").toUpperCase());
434
+ if (!channels) return "";
435
+ const [r, g, b] = channels;
436
+ return `\x1B[48;2;${r};${g};${b}m`;
437
+ }
438
+ function blendHex(fg, base, weight) {
439
+ const w = Math.min(1, Math.max(0, weight));
440
+ const normFg = fg.replace(/^#/, "").toUpperCase();
441
+ const normBase = base.replace(/^#/, "").toUpperCase();
442
+ if (w === 0) return normBase;
443
+ if (w === 1) return normFg;
444
+ if (!readHexChannels(normFg) || !readHexChannels(normBase)) return normBase;
445
+ const fgR = Number.parseInt(normFg.slice(0, 2), 16);
446
+ const fgG = Number.parseInt(normFg.slice(2, 4), 16);
447
+ const fgB = Number.parseInt(normFg.slice(4, 6), 16);
448
+ const baseR = Number.parseInt(normBase.slice(0, 2), 16);
449
+ const baseG = Number.parseInt(normBase.slice(2, 4), 16);
450
+ const baseB = Number.parseInt(normBase.slice(4, 6), 16);
451
+ const r = Math.round(baseR + (fgR - baseR) * w).toString(16).padStart(2, "0").toUpperCase();
452
+ const g = Math.round(baseG + (fgG - baseG) * w).toString(16).padStart(2, "0").toUpperCase();
453
+ const b = Math.round(baseB + (fgB - baseB) * w).toString(16).padStart(2, "0").toUpperCase();
454
+ return `${r}${g}${b}`;
455
+ }
456
+ function diffLineSequences(env = process.env, brightness = resolveTuiBrightness(env)) {
457
+ if (!supportsTruecolor(env)) return null;
458
+ const palette = THEME_PALETTES[activeThemeName] ?? THEME_PALETTES.trail;
459
+ const successHex = palette[brightness].success.hex;
460
+ const dangerHex = palette[brightness].danger.hex;
461
+ const weight = brightness === "dark" ? 0.14 : 0.1;
462
+ const baseHex = TERMINAL_BASE[brightness];
463
+ const addedBgHex = blendHex(successHex, baseHex, weight);
464
+ const removedBgHex = blendHex(dangerHex, baseHex, weight);
465
+ return {
466
+ added: {
467
+ fg: hexToSgr(successHex),
468
+ bg: hexToBgSgr(addedBgHex)
469
+ },
470
+ removed: {
471
+ fg: hexToSgr(dangerHex),
472
+ bg: hexToBgSgr(removedBgHex)
473
+ }
474
+ };
475
+ }
476
+ function surfaceBackgroundSequence(env = process.env, brightness = resolveTuiBrightness(env)) {
477
+ if (!supportsTruecolor(env)) return "";
478
+ return hexToBgSgr((SURFACE_HEX[activeThemeName] ?? SURFACE_HEX.trail)[brightness]);
479
+ }
480
+ function themeColorSequence(token, env = process.env, brightness = resolveTuiBrightness(env)) {
481
+ const palette = THEME_PALETTES[activeThemeName] ?? THEME_PALETTES.trail;
482
+ const entry = palette[brightness][token];
483
+ return supportsTruecolor(env) ? hexToSgr(entry.hex) : entry.ansiFallback;
484
+ }
485
+ function themeText(text, token, colorEnabled = resolveCliColorEnabled(), env = process.env) {
486
+ if (!colorEnabled) return text;
487
+ return `${themeColorSequence(token, env)}${text}\x1B[39m`;
488
+ }
489
+ function highlightMarkdown(text, colorEnabled = resolveCliColorEnabled(), env = process.env) {
490
+ if (!colorEnabled) return text;
491
+ let result = text;
492
+ result = result.replace(/```[a-zA-Z-]*\n([\s\S]*?)\n```/g, (match, code) => {
493
+ const infoColor = themeColorSequence("info", env);
494
+ const reset2 = "\x1B[39m\x1B[22m";
495
+ const border = themeText("\u2502", "chrome", true, env);
496
+ const formattedCode = code.split("\n").map((line) => ` ${border} ${infoColor}${line}${reset2}`).join("\n");
497
+ return ` ${themeText("\u250C\u2500\u2500\u2500", "chrome", true, env)}
498
+ ${formattedCode}
499
+ ${themeText("\u2514\u2500\u2500\u2500", "chrome", true, env)}`;
500
+ });
501
+ result = result.replace(/^(#{1,3})\s+(.+)$/gm, (match, hashes, title) => {
502
+ const level = hashes.length;
503
+ const reset2 = "\x1B[0m";
504
+ if (level === 1) {
505
+ return `\x1B[1m\x1B[4m${themeColorSequence("accent", env)}${title}${reset2}`;
506
+ }
507
+ if (level === 2) {
508
+ return `\x1B[1m${themeColorSequence("warning", env)}${title}${reset2}`;
509
+ }
510
+ return `\x1B[1m${themeColorSequence("info", env)}${title}${reset2}`;
511
+ });
512
+ result = result.replace(
513
+ /^(进入 nolo-plan[^\n]*)$/gm,
514
+ (match, content) => `${themeColorSequence("chrome", env)}\x1B[2m${content}\x1B[0m`
515
+ );
516
+ result = result.replace(/^>\s?(.*)$/gm, (match, content) => {
517
+ const border = themeColorSequence("chrome", env);
518
+ return `${border}\u2502\x1B[39m \x1B[2m${content}\x1B[22m`;
519
+ });
520
+ result = result.replace(/\*\*([\s\S]*?)\*\*/g, "\x1B[1m$1\x1B[22m");
521
+ const mutedColor = themeColorSequence("muted", env);
522
+ const reset = "\x1B[39m";
523
+ result = result.replace(/`([^`\n]+)`/g, `${mutedColor}$1${reset}`);
524
+ result = result.replace(/(?<!\*)\*([^*\n]+?)\*(?!\*)/g, `\x1B[2m$1\x1B[22m`);
525
+ result = result.replace(/~~([^~\n]+?)~~/g, `\x1B[2m\x1B[9m$1\x1B[29m\x1B[22m`);
526
+ return result;
527
+ }
528
+
529
+ // packages/cli/client/assistantOutput.ts
530
+ var STYLE = {
531
+ reset: "\x1B[0m",
532
+ bold: "\x1B[1m",
533
+ dim: "\x1B[2m"
534
+ };
535
+ function colorSeq(token, brightness) {
536
+ return themeColorSequence(token, process.env, brightness);
537
+ }
538
+ function splitTableCells(line) {
539
+ const trimmed = line.trim();
540
+ if (!trimmed.includes("|")) return [];
541
+ const core = trimmed.replace(/^\|/, "").replace(/\|$/, "");
542
+ return core.split("|").map((cell) => cell.trim()).filter(Boolean);
543
+ }
544
+ function isTableSeparator(line) {
545
+ const cells = splitTableCells(line);
546
+ return cells.length > 0 && cells.every((cell) => /^:?-{3,}:?$/.test(cell));
547
+ }
548
+ function isTableRow(line) {
549
+ const cells = splitTableCells(line);
550
+ return cells.length >= 2 && cells.some((cell) => cell.length > 0);
551
+ }
552
+ function isPipeWrappedTableRow(line) {
553
+ const trimmed = line.trim();
554
+ return trimmed.startsWith("|") && trimmed.endsWith("|") && isTableRow(line);
555
+ }
556
+ function isCodeFenceLine(line) {
557
+ return /^\s*```/.test(line);
558
+ }
559
+ function readFenceLanguage(line) {
560
+ const m = line.match(/^\s*```\s*([a-zA-Z0-9+#_-]*)/);
561
+ return m ? (m[1] ?? "").toLowerCase() : "";
562
+ }
563
+ function normalizeCodeLang(raw) {
564
+ switch (raw) {
565
+ case "js":
566
+ case "jsx":
567
+ case "ts":
568
+ case "tsx":
569
+ case "javascript":
570
+ case "typescript":
571
+ return "js";
572
+ case "py":
573
+ case "python":
574
+ return "py";
575
+ case "sh":
576
+ case "bash":
577
+ case "zsh":
578
+ case "shell":
579
+ case "console":
580
+ return "sh";
581
+ case "json":
582
+ return "json";
583
+ default:
584
+ return "unknown";
585
+ }
586
+ }
587
+ var KEYWORDS = {
588
+ js: /* @__PURE__ */ new Set([
589
+ "const",
590
+ "let",
591
+ "var",
592
+ "function",
593
+ "return",
594
+ "if",
595
+ "else",
596
+ "for",
597
+ "while",
598
+ "class",
599
+ "new",
600
+ "await",
601
+ "async",
602
+ "import",
603
+ "export",
604
+ "from",
605
+ "type",
606
+ "interface",
607
+ "extends",
608
+ "implements",
609
+ "null",
610
+ "undefined",
611
+ "true",
612
+ "false"
613
+ ]),
614
+ py: /* @__PURE__ */ new Set([
615
+ "def",
616
+ "class",
617
+ "return",
618
+ "if",
619
+ "elif",
620
+ "else",
621
+ "for",
622
+ "while",
623
+ "import",
624
+ "from",
625
+ "as",
626
+ "with",
627
+ "try",
628
+ "except",
629
+ "finally",
630
+ "lambda",
631
+ "None",
632
+ "True",
633
+ "False",
634
+ "self"
635
+ ]),
636
+ sh: /* @__PURE__ */ new Set([
637
+ "if",
638
+ "then",
639
+ "else",
640
+ "fi",
641
+ "for",
642
+ "do",
643
+ "done",
644
+ "while",
645
+ "case",
646
+ "esac",
647
+ "function",
648
+ "export",
649
+ "local",
650
+ "return",
651
+ "source",
652
+ "echo",
653
+ "cd"
654
+ ]),
655
+ json: /* @__PURE__ */ new Set(["true", "false", "null"])
656
+ };
657
+ function scanStringCommentRegions(line, lang) {
658
+ const regions = [];
659
+ const n = line.length;
660
+ let i = 0;
661
+ const commentMarkers = lang === "py" || lang === "sh" ? ["#"] : ["//"];
662
+ while (i < n) {
663
+ const ch = line[i];
664
+ if (ch === "'" || ch === '"' || lang === "js" && ch === "`") {
665
+ const quote = ch;
666
+ const start = i;
667
+ i += 1;
668
+ while (i < n) {
669
+ if (line[i] === "\\") {
670
+ i += 2;
671
+ continue;
672
+ }
673
+ if (line[i] === quote) {
674
+ i += 1;
675
+ break;
676
+ }
677
+ i += 1;
678
+ }
679
+ regions.push({ start, end: i, kind: "string" });
680
+ continue;
681
+ }
682
+ let matched = false;
683
+ for (const marker of commentMarkers) {
684
+ if (line.startsWith(marker, i)) {
685
+ regions.push({ start: i, end: n, kind: "comment" });
686
+ i = n;
687
+ matched = true;
688
+ break;
689
+ }
690
+ }
691
+ if (matched) continue;
692
+ i += 1;
693
+ }
694
+ return regions;
695
+ }
696
+ function highlightCodeLine(line, lang, brightness) {
697
+ const info = colorSeq("info", brightness);
698
+ if (lang === "unknown") {
699
+ return `${info}${line}${STYLE.reset}`;
700
+ }
701
+ const regions = scanStringCommentRegions(line, lang);
702
+ const keywords = KEYWORDS[lang];
703
+ const accent = colorSeq("accent", brightness);
704
+ const success = colorSeq("success", brightness);
705
+ const warning = colorSeq("warning", brightness);
706
+ const chrome = colorSeq("chrome", brightness);
707
+ const dim = STYLE.dim;
708
+ const reset = STYLE.reset;
709
+ const out = [];
710
+ let cursor = 0;
711
+ for (const region of regions) {
712
+ if (region.start > cursor) {
713
+ out.push(emitPlainGap(line.slice(cursor, region.start), keywords, info, accent, warning, reset));
714
+ }
715
+ const text = line.slice(region.start, region.end);
716
+ if (region.kind === "string") {
717
+ out.push(`${success}${text}${reset}`);
718
+ } else {
719
+ out.push(`${chrome}${dim}${text}${reset}`);
720
+ }
721
+ cursor = region.end;
722
+ }
723
+ if (cursor < line.length) {
724
+ out.push(emitPlainGap(line.slice(cursor), keywords, info, accent, warning, reset));
725
+ }
726
+ return out.join("");
727
+ }
728
+ function emitPlainGap(text, keywords, info, accent, warning, reset) {
729
+ const parts = [];
730
+ const re = /([A-Za-z_]\w*)|(\d+(?:\.\d+)?)|([\s\S])/g;
731
+ let last = 0;
732
+ let m;
733
+ let started = false;
734
+ while ((m = re.exec(text)) !== null) {
735
+ if (!started) {
736
+ started = true;
737
+ }
738
+ if (m.index > last) {
739
+ parts.push(`${info}${text.slice(last, m.index)}`);
740
+ }
741
+ if (m[1] !== void 0) {
742
+ const word = m[1];
743
+ if (keywords.has(word)) {
744
+ parts.push(`${accent}${word}${reset}`);
745
+ } else {
746
+ parts.push(`${info}${word}`);
747
+ }
748
+ } else if (m[2] !== void 0) {
749
+ parts.push(`${warning}${m[2]}${reset}`);
750
+ } else {
751
+ parts.push(`${info}${m[3]}`);
752
+ }
753
+ last = re.lastIndex;
754
+ }
755
+ if (last < text.length) {
756
+ parts.push(`${info}${text.slice(last)}`);
757
+ }
758
+ return parts.join("");
759
+ }
760
+ function tableRowToBullet(line) {
761
+ const row = splitTableCells(line);
762
+ const label = row[0] ?? "";
763
+ const detail = row.slice(1).join(" \u2014 ").trim();
764
+ return detail ? ` \u2022 ${label} \u2014 ${detail}` : ` \u2022 ${label}`;
765
+ }
766
+ var UNORDERED_LIST_RE = /^(\s*)([-*+])\s+(.+)$/;
767
+ var ORDERED_LIST_RE = /^(\s*)(\d+)\.\s+(.+)$/;
768
+ var TASK_LIST_RE = /^(\s*)([-*+])\s+\[([ xX])\]\s+(.+)$/;
769
+ function normalizeListLine(line) {
770
+ const task = line.match(TASK_LIST_RE);
771
+ if (task) {
772
+ const checked = task[3] === "x" || task[3] === "X";
773
+ return `${task[1]}${checked ? "\u2611" : "\u2610"} ${task[4]}`;
774
+ }
775
+ const unordered = line.match(UNORDERED_LIST_RE);
776
+ if (unordered) {
777
+ return `${unordered[1]}\u2022 ${unordered[3]}`;
778
+ }
779
+ const ordered = line.match(ORDERED_LIST_RE);
780
+ if (ordered) {
781
+ return `${ordered[1]}${ordered[2]}. ${ordered[3]}`;
782
+ }
783
+ return line;
784
+ }
785
+ function convertMarkdownTablesForTerminal(text) {
786
+ const lines = text.split("\n");
787
+ const out = [];
788
+ let inFence = false;
789
+ for (let index = 0; index < lines.length; index += 1) {
790
+ const line = lines[index] ?? "";
791
+ if (isCodeFenceLine(line)) {
792
+ inFence = !inFence;
793
+ out.push(line);
794
+ continue;
795
+ }
796
+ if (inFence) {
797
+ out.push(line);
798
+ continue;
799
+ }
800
+ const next = lines[index + 1] ?? "";
801
+ if (isTableRow(line) && isTableSeparator(next)) {
802
+ index += 1;
803
+ while (index + 1 < lines.length && isTableRow(lines[index + 1] ?? "") && !isTableSeparator(lines[index + 1] ?? "")) {
804
+ index += 1;
805
+ out.push(tableRowToBullet(lines[index] ?? ""));
806
+ }
807
+ if (out.length > 0 && out[out.length - 1] !== "") out.push("");
808
+ continue;
809
+ }
810
+ if (isPipeWrappedTableRow(line)) {
811
+ if (!isTableSeparator(line)) out.push(tableRowToBullet(line));
812
+ continue;
813
+ }
814
+ out.push(normalizeListLine(line));
815
+ }
816
+ return out.join("\n");
817
+ }
818
+ function polishAssistantStructure(text, options = {}) {
819
+ const converted = convertMarkdownTablesForTerminal(text).replace(/\r\n/g, "\n").replace(/\x00/g, "");
820
+ const lines = converted.split("\n");
821
+ let inFence = false;
822
+ const maskedLines = lines.map((line, index) => {
823
+ if (isCodeFenceLine(line)) {
824
+ inFence = !inFence;
825
+ return line;
826
+ }
827
+ if (inFence) {
828
+ return `\0F${index}\0`;
829
+ }
830
+ return line;
831
+ });
832
+ 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");
833
+ const LIST_LIKE = /^\s*(?:•|☐|☑|\d+\.)\s|^\s*[\u2460-\u2473]/;
834
+ const headingLines = afterHeading.split("\n");
835
+ const breathed = [];
836
+ for (let i = 0; i < headingLines.length; i++) {
837
+ breathed.push(headingLines[i]);
838
+ const cur = headingLines[i];
839
+ const next = headingLines[i + 1];
840
+ if (cur === "" || next === void 0 || next === "") continue;
841
+ if (LIST_LIKE.test(cur) === LIST_LIKE.test(next)) continue;
842
+ breathed.push("");
843
+ }
844
+ const polishedMasked = breathed.join("\n");
845
+ const polished = polishedMasked.replace(
846
+ /\x00F(\d+)\x00/g,
847
+ (_, id) => lines[Number(id)] ?? ""
848
+ );
849
+ return options.trimEdges === false ? polished : polished.trim();
850
+ }
851
+ function renderMarkdownLink(match, text, url) {
852
+ const visible = `${text} (${url})`;
853
+ return `\x1B]8;;${url}\x1B\\${visible}\x1B]8;;\x1B\\`;
854
+ }
855
+ var MARKDOWN_LINK_RE = /\[([^\]]+)\]\(([^)\s]+)\)/g;
856
+ function styleInlineMarkdown(line, brightness) {
857
+ const inlineCode = colorSeq("muted", brightness);
858
+ const reset = STYLE.reset;
859
+ const bold = STYLE.bold;
860
+ const dim = STYLE.dim;
861
+ 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}`);
862
+ }
863
+ function styleRichMarkdownLine(line, brightness) {
864
+ const heading = line.match(/^(#{1,3})\s+(.+)$/);
865
+ if (heading) {
866
+ const level = heading[1].length;
867
+ const title = heading[2];
868
+ if (level === 1) {
869
+ return `${STYLE.bold}\x1B[4m${colorSeq("accent", brightness)}${title}${STYLE.reset}`;
870
+ }
871
+ if (level === 2) {
872
+ return `${STYLE.bold}${colorSeq("warning", brightness)}${title}${STYLE.reset}`;
873
+ }
874
+ return `${STYLE.bold}${colorSeq("info", brightness)}${title}${STYLE.reset}`;
875
+ }
876
+ if (line.startsWith("\u8FDB\u5165 nolo-plan")) {
877
+ return `${colorSeq("chrome", brightness)}${STYLE.dim}${line}${STYLE.reset}`;
878
+ }
879
+ const blockquote = line.match(/^>\s?(.*)$/);
880
+ if (blockquote) {
881
+ const border = colorSeq("chrome", brightness);
882
+ const content = blockquote[1];
883
+ return `${border}\u2502${STYLE.reset} ${STYLE.dim}${content}${STYLE.reset}`;
884
+ }
885
+ if (/^---+$/.test(line.trim())) {
886
+ return `${colorSeq("chrome", brightness)}${"\u2500".repeat(Math.min(line.trim().length, 40))}${STYLE.reset}`;
887
+ }
888
+ const bullet = line.match(/^(\s*)(•)\s(.+)$/);
889
+ if (bullet) {
890
+ const styled = styleInlineMarkdown(bullet[3], brightness);
891
+ return `${bullet[1]}${colorSeq("accent", brightness)}\u2022${STYLE.reset} ${styled}`;
892
+ }
893
+ const ordered = line.match(/^(\s*)(\d+\.)\s(.+)$/);
894
+ if (ordered) {
895
+ const styled = styleInlineMarkdown(ordered[3], brightness);
896
+ return `${ordered[1]}${colorSeq("accent", brightness)}${ordered[2]}${STYLE.reset} ${styled}`;
897
+ }
898
+ const task = line.match(/^(\s*)(☐|☑)\s(.+)$/);
899
+ if (task) {
900
+ const styled = styleInlineMarkdown(task[3], brightness);
901
+ return `${task[1]}${colorSeq("accent", brightness)}${task[2]}${STYLE.reset} ${styled}`;
902
+ }
903
+ return styleInlineMarkdown(line, brightness);
904
+ }
905
+ function formatAssistantDisplay(text, options = {}) {
906
+ const brightness = resolveTuiBrightness();
907
+ const polished = polishAssistantStructure(text, options);
908
+ let inFence = false;
909
+ let fenceLang = "unknown";
910
+ return polished.split("\n").map((line) => {
911
+ if (isCodeFenceLine(line)) {
912
+ if (inFence) {
913
+ fenceLang = "unknown";
914
+ } else {
915
+ fenceLang = normalizeCodeLang(readFenceLanguage(line));
916
+ }
917
+ inFence = !inFence;
918
+ return `${STYLE.dim}${line}${STYLE.reset}`;
919
+ }
920
+ if (inFence) {
921
+ return highlightCodeLine(line, fenceLang, brightness);
922
+ }
923
+ return styleRichMarkdownLine(line, brightness);
924
+ }).join("\n");
925
+ }
926
+ function emitFormattedAssistantBlock(write, text, trailingNewline = false) {
927
+ if (!text) return;
928
+ write(formatAssistantDisplay(text, { trimEdges: false }));
929
+ if (trailingNewline) write("\n");
930
+ }
931
+ function classifyStreamLine(line) {
932
+ if (line === "") return "blank";
933
+ 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)) {
934
+ return "list";
935
+ }
936
+ return "prose";
937
+ }
938
+ function needsListProseBreath(prev, next) {
939
+ if (prev === null || prev === "blank" || next === "blank") return false;
940
+ const prevList = prev === "list";
941
+ const nextList = next === "list";
942
+ return prevList !== nextList;
943
+ }
944
+ function createRenderAwareStreamWriter(args) {
945
+ const brightness = resolveTuiBrightness();
946
+ let buffer = "";
947
+ let inFence = false;
948
+ let fenceLang = "unknown";
949
+ let lastKind = null;
950
+ const emitBreathIfNeeded = (nextKind) => {
951
+ if (needsListProseBreath(lastKind, nextKind)) args.write("\n");
952
+ };
953
+ const flushCompleteBlocks = () => {
954
+ while (buffer.includes("\n")) {
955
+ const lines = buffer.split("\n");
956
+ if (lines.length < 2) break;
957
+ const firstLine = lines[0] ?? "";
958
+ if (isCodeFenceLine(firstLine)) {
959
+ if (inFence) {
960
+ fenceLang = "unknown";
961
+ } else {
962
+ fenceLang = normalizeCodeLang(readFenceLanguage(firstLine));
963
+ }
964
+ inFence = !inFence;
965
+ emitBreathIfNeeded("other");
966
+ args.write(`${STYLE.dim}${firstLine}${STYLE.reset}
967
+ `);
968
+ lastKind = "other";
969
+ buffer = lines.slice(1).join("\n");
970
+ continue;
971
+ }
972
+ if (inFence) {
973
+ args.write(`${highlightCodeLine(firstLine, fenceLang, brightness)}
974
+ `);
975
+ lastKind = "other";
976
+ buffer = lines.slice(1).join("\n");
977
+ continue;
978
+ }
979
+ if (isTableRow(firstLine)) {
980
+ const nextLineComplete = lines.length > 2;
981
+ if (!nextLineComplete) break;
982
+ if (isTableSeparator(lines[1] ?? "")) {
983
+ let end = 2;
984
+ while (end < lines.length && isTableRow(lines[end] ?? "") && !isTableSeparator(lines[end] ?? "")) {
985
+ end += 1;
986
+ }
987
+ const tableComplete = end < lines.length - 1;
988
+ if (!tableComplete) break;
989
+ emitBreathIfNeeded("list");
990
+ emitFormattedAssistantBlock(
991
+ args.write,
992
+ lines.slice(0, end).join("\n"),
993
+ true
994
+ );
995
+ lastKind = "list";
996
+ buffer = lines.slice(end).join("\n");
997
+ continue;
998
+ }
999
+ }
1000
+ const kind = classifyStreamLine(firstLine);
1001
+ emitBreathIfNeeded(kind);
1002
+ emitFormattedAssistantBlock(args.write, firstLine, true);
1003
+ lastKind = kind;
1004
+ buffer = lines.slice(1).join("\n");
1005
+ }
1006
+ };
1007
+ return {
1008
+ push(chunk) {
1009
+ if (!chunk) return;
1010
+ buffer += chunk;
1011
+ flushCompleteBlocks();
1012
+ },
1013
+ flush() {
1014
+ if (!buffer) return;
1015
+ if (inFence) {
1016
+ args.write(buffer);
1017
+ } else {
1018
+ emitFormattedAssistantBlock(args.write, buffer);
1019
+ }
1020
+ buffer = "";
1021
+ }
1022
+ };
1023
+ }
1024
+
1025
+ // packages/cli/client/thinkingOutput.ts
1026
+ var THINK_OPEN = /<think>/i;
1027
+ var THINK_CLOSE = /<\/think>/i;
1028
+ var COLLAPSED_MARKER = /^\s*▸ 思考已折叠\s*$/;
1029
+ function normalizeThinkingDisplayMode(raw, fallback = "hide") {
1030
+ const normalized = asTrimmedLowercaseString(raw);
1031
+ if (normalized === "hide" || normalized === "off" || normalized === "false" || normalized === "0") {
1032
+ return "hide";
1033
+ }
1034
+ if (normalized === "marker" || normalized === "collapsed" || normalized === "fold") {
1035
+ return "marker";
1036
+ }
1037
+ if (normalized === "show" || normalized === "on" || normalized === "true" || normalized === "1") {
1038
+ return "show";
1039
+ }
1040
+ return fallback;
1041
+ }
1042
+ function resolveThinkingDisplayMode(env = process.env) {
1043
+ return normalizeThinkingDisplayMode(env.NOLO_CLI_THINKING ?? env.NOLO_THINKING, "hide");
1044
+ }
1045
+ function collapseThinkingBlocks(text, mode = "hide") {
1046
+ if (mode === "show") return text;
1047
+ const replacement = mode === "marker" ? "\u25B8 \u601D\u8003\u5DF2\u6298\u53E0\n" : "";
1048
+ return text.replace(/<think>[\s\S]*?<\/think>\s*/gi, replacement).replace(/\n{3,}/g, "\n\n").trim();
1049
+ }
1050
+ function stripCollapsedThinkingMarkers(text) {
1051
+ return text.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n").replace(/\n{2,}/g, "\n").trim();
1052
+ }
1053
+ function formatAssistantTextForCli(text, mode = "hide") {
1054
+ const collapsed = collapseThinkingBlocks(text, mode);
1055
+ return mode === "hide" ? stripCollapsedThinkingMarkers(collapsed) : collapsed;
1056
+ }
1057
+ function createThinkingEventSink(arg1, arg2) {
1058
+ let mode = "hide";
1059
+ let write = () => {
1060
+ };
1061
+ if (typeof arg1 === "function") {
1062
+ write = arg1;
1063
+ if (typeof arg2 === "string") {
1064
+ mode = arg2;
1065
+ }
1066
+ } else {
1067
+ mode = arg1;
1068
+ if (typeof arg2 === "function") {
1069
+ write = arg2;
1070
+ }
1071
+ }
1072
+ if (mode === "hide") {
1073
+ return {
1074
+ push(_chunk) {
1075
+ }
1076
+ };
1077
+ }
1078
+ if (mode === "show") {
1079
+ return {
1080
+ push(chunk) {
1081
+ if (chunk) write(chunk);
1082
+ }
1083
+ };
1084
+ }
1085
+ let markerEmitted = false;
1086
+ return {
1087
+ push(chunk) {
1088
+ if (!chunk || markerEmitted) return;
1089
+ write("\n\u25B8 \u601D\u8003\u5DF2\u6298\u53E0\n");
1090
+ markerEmitted = true;
1091
+ }
1092
+ };
1093
+ }
1094
+ function createThinkingAwareStreamFilter(write, mode = "hide") {
1095
+ if (mode === "show") {
1096
+ return {
1097
+ push(chunk) {
1098
+ write(chunk);
1099
+ },
1100
+ flush() {
1101
+ }
1102
+ };
1103
+ }
1104
+ let pending = "";
1105
+ let insideThink = false;
1106
+ let markerEmitted = false;
1107
+ const emit = (chunk) => {
1108
+ if (chunk) write(chunk);
1109
+ };
1110
+ const emitMarker = () => {
1111
+ if (mode !== "marker" || markerEmitted) return;
1112
+ emit("\n\u25B8 \u601D\u8003\u5DF2\u6298\u53E0\n");
1113
+ markerEmitted = true;
1114
+ };
1115
+ const consume = (input) => {
1116
+ pending += input;
1117
+ while (pending.length > 0) {
1118
+ if (insideThink) {
1119
+ const closeMatch = pending.match(THINK_CLOSE);
1120
+ if (!closeMatch || closeMatch.index == null) {
1121
+ emitMarker();
1122
+ pending = "";
1123
+ return;
1124
+ }
1125
+ emitMarker();
1126
+ pending = pending.slice(closeMatch.index + closeMatch[0].length);
1127
+ insideThink = false;
1128
+ markerEmitted = false;
1129
+ continue;
1130
+ }
1131
+ const openMatch = pending.match(THINK_OPEN);
1132
+ if (!openMatch || openMatch.index == null) {
1133
+ const nextMarker = pending.indexOf("<");
1134
+ if (nextMarker === -1) {
1135
+ const cleaned = mode === "hide" ? pending.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : pending;
1136
+ emit(cleaned);
1137
+ pending = "";
1138
+ return;
1139
+ }
1140
+ if (nextMarker > 0) {
1141
+ const head = pending.slice(0, nextMarker);
1142
+ const cleaned = mode === "hide" ? head.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : head;
1143
+ emit(cleaned);
1144
+ pending = pending.slice(nextMarker);
1145
+ continue;
1146
+ }
1147
+ if (pending.length < 7) return;
1148
+ emit(pending[0]);
1149
+ pending = pending.slice(1);
1150
+ continue;
1151
+ }
1152
+ if (openMatch.index > 0) {
1153
+ const head = pending.slice(0, openMatch.index);
1154
+ const cleaned = mode === "hide" ? head.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : head;
1155
+ emit(cleaned);
1156
+ pending = pending.slice(openMatch.index);
1157
+ continue;
1158
+ }
1159
+ pending = pending.slice(openMatch[0].length);
1160
+ insideThink = true;
1161
+ markerEmitted = false;
1162
+ }
1163
+ };
1164
+ return {
1165
+ push(chunk) {
1166
+ consume(chunk);
1167
+ },
1168
+ flush() {
1169
+ if (insideThink) emitMarker();
1170
+ if (pending) {
1171
+ const cleaned = mode === "hide" ? pending.split("\n").filter((line) => !COLLAPSED_MARKER.test(line)).join("\n") : pending;
1172
+ emit(cleaned);
1173
+ }
1174
+ pending = "";
1175
+ insideThink = false;
1176
+ markerEmitted = false;
1177
+ }
1178
+ };
1179
+ }
1180
+
1181
+ // packages/cli/client/formatReadPathTree.ts
1182
+ import { homedir as osHomedir } from "node:os";
1183
+ function clipPathAware(value, max = 72) {
1184
+ const compact = compactWhitespace(value);
1185
+ if (compact.length <= max) return compact;
1186
+ if (compact.includes(" ") || !compact.includes("/") && !compact.includes("\\")) {
1187
+ return clipCompactText(value, max, "\u2026");
1188
+ }
1189
+ const segments = compact.split(/[/\\]/);
1190
+ const filename = segments[segments.length - 1];
1191
+ if (filename.length >= max) {
1192
+ return clipCompactText(value, max, "\u2026");
1193
+ }
1194
+ const ELISION = "/\u2026/";
1195
+ let prefix = "";
1196
+ for (let i = 0; i < segments.length - 1; i += 1) {
1197
+ const candidate = segments.slice(0, i + 1).join("/");
1198
+ if (candidate.length + ELISION.length + filename.length > max) break;
1199
+ prefix = candidate;
1200
+ }
1201
+ if (prefix) return `${prefix}${ELISION}${filename}`;
1202
+ const filenameOnly = `\u2026/${filename}`;
1203
+ if (filenameOnly.length <= max) return filenameOnly;
1204
+ return clipCompactText(value, max, "\u2026");
1205
+ }
1206
+ function formatHomePath(path, customHomedir) {
1207
+ if (!path) return "";
1208
+ const home = customHomedir || (typeof process !== "undefined" ? process.env?.HOME : void 0) || (typeof osHomedir === "function" ? osHomedir() : "");
1209
+ if (home && path.startsWith(home)) {
1210
+ return `~${path.slice(home.length)}`;
1211
+ }
1212
+ return path.replace(/^(\/Users\/[^\/]+\/|\/home\/[^\/]+\/)/, "~/");
1213
+ }
1214
+ function formatReadItemPath(rawPath, metadata, customHomedir) {
1215
+ const inputPath = rawPath || (typeof metadata?.path === "string" ? metadata.path : void 0) || (typeof metadata?.filePath === "string" ? metadata.filePath : void 0) || "file";
1216
+ let formattedPath = formatHomePath(inputPath, customHomedir);
1217
+ if (!formattedPath) return "file";
1218
+ let rangePart = "";
1219
+ const match = formattedPath.match(/(:[0-9]+(-[0-9]+)?(,[0-9]+(-[0-9]+)?)*)$/);
1220
+ if (match) {
1221
+ rangePart = match[1];
1222
+ formattedPath = formattedPath.slice(0, -rangePart.length);
1223
+ } else if (metadata && typeof metadata.startLine === "number" && typeof metadata.endLine === "number" && typeof metadata.totalLines === "number") {
1224
+ const { startLine, endLine, totalLines, truncated } = metadata;
1225
+ if (truncated || startLine !== 1 || endLine !== totalLines) {
1226
+ rangePart = `:${startLine}-${endLine}`;
1227
+ }
1228
+ }
1229
+ const clippedPath = clipPathAware(formattedPath, 72);
1230
+ return `${clippedPath}${rangePart}`;
1231
+ }
1232
+ function formatReadTreeLines(items, customHomedir) {
1233
+ const count = items.length;
1234
+ const header = `Read (${count})`;
1235
+ const lines = items.map((item, index) => {
1236
+ const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
1237
+ const pathWithRange = formatReadItemPath(item.path, item.metadata, customHomedir);
1238
+ return { connector, pathWithRange };
1239
+ });
1240
+ return { header, count, lines };
1241
+ }
1242
+ function formatSearchItemQuery(query, path, customHomedir) {
1243
+ let text = (query || "").trim();
1244
+ if (path) {
1245
+ const formattedPath = formatHomePath(path, customHomedir);
1246
+ if (formattedPath) {
1247
+ text = text ? `${text} in ${formattedPath}` : formattedPath;
1248
+ }
1249
+ }
1250
+ if (!text) return "search";
1251
+ return text.length > 80 ? `${text.slice(0, 79)}\u2026` : text;
1252
+ }
1253
+ function formatSearchTreeLines(items, customHomedir) {
1254
+ const count = items.length;
1255
+ const header = `Search (${count})`;
1256
+ const lines = items.map((item, index) => {
1257
+ const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
1258
+ const queryText = formatSearchItemQuery(item.query, item.path, customHomedir);
1259
+ return { connector, queryText };
1260
+ });
1261
+ return { header, count, lines };
1262
+ }
1263
+ function formatRunItemCommand(command, exitCode, timedOut) {
1264
+ let text = compactWhitespace(command || "");
1265
+ if (!text) text = "command";
1266
+ text = text.length > 80 ? `${text.slice(0, 79)}\u2026` : text;
1267
+ if (timedOut) return `${text} (timed out)`;
1268
+ if (typeof exitCode === "number" && exitCode !== 0) return `${text} (exit ${exitCode})`;
1269
+ return text;
1270
+ }
1271
+ function formatRunTreeLines(items) {
1272
+ const count = items.length;
1273
+ const header = `Run (${count})`;
1274
+ const lines = items.map((item, index) => {
1275
+ const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
1276
+ const commandText = formatRunItemCommand(item.command, item.exitCode, item.timedOut);
1277
+ return { connector, commandText };
1278
+ });
1279
+ return { header, count, lines };
1280
+ }
1281
+ function formatFetchItemUrl(url) {
1282
+ const text = (url || "").trim();
1283
+ if (!text) return "webpage";
1284
+ return text.length > 80 ? `${text.slice(0, 79)}\u2026` : text;
1285
+ }
1286
+ function formatFetchTreeLines(items) {
1287
+ const count = items.length;
1288
+ const header = `Fetch (${count})`;
1289
+ const lines = items.map((item, index) => {
1290
+ const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
1291
+ const urlText = formatFetchItemUrl(item.url);
1292
+ return { connector, urlText };
1293
+ });
1294
+ return { header, count, lines };
1295
+ }
1296
+
1297
+ // packages/cli/tui/i18n.ts
1298
+ var ZH_PATTERNS = /^zh/i;
1299
+ function parseCliLocale(raw) {
1300
+ const normalized = raw?.trim().toLowerCase();
1301
+ if (!normalized) return null;
1302
+ if (ZH_PATTERNS.test(normalized)) return "zh";
1303
+ if (normalized === "en" || normalized.startsWith("en")) return "en";
1304
+ return null;
1305
+ }
1306
+ function detectLocaleFromEnv(env) {
1307
+ const explicit = parseCliLocale(env.NOLO_LANG);
1308
+ if (explicit) return explicit;
1309
+ const candidates = [env.LC_ALL, env.LC_CTYPE, env.LANG].filter(Boolean);
1310
+ for (const candidate of candidates) {
1311
+ if (ZH_PATTERNS.test(candidate)) return "zh";
1312
+ if (candidate && !candidate.startsWith("C.") && !candidate.startsWith("POSIX")) {
1313
+ return "en";
1314
+ }
1315
+ }
1316
+ return null;
1317
+ }
1318
+ function detectLocale(env) {
1319
+ return detectLocaleFromEnv(env) ?? "zh";
1320
+ }
1321
+ var currentLocale = detectLocale(process.env);
1322
+ function getCliLocale() {
1323
+ return currentLocale;
1324
+ }
1325
+ function setCliLocale(locale) {
1326
+ currentLocale = locale;
1327
+ }
1328
+ function initCliLocale(env = process.env) {
1329
+ currentLocale = detectLocale(env);
1330
+ }
1331
+ var STRINGS = {
1332
+ welcomeHint: {
1333
+ en: "Tell nolo what you want. Use /help for commands. Shift+Enter for newline.",
1334
+ zh: "\u544A\u8BC9 nolo \u4F60\u60F3\u8981\u4EC0\u4E48\u3002\u8F93\u5165 /help \u67E5\u770B\u547D\u4EE4\u3002Shift+Enter \u6362\u884C\u3002"
1335
+ },
1336
+ promptLabel: {
1337
+ en: "\u276F ",
1338
+ zh: "\u276F "
1339
+ },
1340
+ continueLabel: {
1341
+ en: "\u2502 ",
1342
+ zh: "\u2502 "
1343
+ },
1344
+ placeholder: {
1345
+ en: "Type a message or / for commands...",
1346
+ zh: "\u8F93\u5165\u6D88\u606F\uFF0C\u6216\u7528 / \u67E5\u770B\u547D\u4EE4\u2026"
1347
+ },
1348
+ newDialog: {
1349
+ en: "new dialog",
1350
+ zh: "\u65B0\u5BF9\u8BDD"
1351
+ },
1352
+ startedFreshDialog: {
1353
+ en: "Started a fresh dialog.",
1354
+ zh: "\u5DF2\u5F00\u59CB\u65B0\u5BF9\u8BDD\u3002"
1355
+ },
1356
+ bye: {
1357
+ en: "Bye.",
1358
+ zh: "\u518D\u89C1\u3002"
1359
+ },
1360
+ resumedDialogPrefix: {
1361
+ en: "Resumed dialog",
1362
+ zh: "\u5DF2\u6062\u590D\u5BF9\u8BDD"
1363
+ },
1364
+ resumeInvalidId: {
1365
+ en: "does not look like a dialog id. Use /history to pick one.",
1366
+ zh: "\u770B\u8D77\u6765\u4E0D\u662F dialog id\u3002\u7528 /history \u4ECE\u5217\u8868\u91CC\u9009\u4E00\u4E2A\u3002"
1367
+ },
1368
+ dialogResumeCancelled: {
1369
+ en: "Dialog resume cancelled.",
1370
+ zh: "\u5DF2\u53D6\u6D88\u6062\u590D\u5BF9\u8BDD\u3002"
1371
+ },
1372
+ agentSwitchCancelled: {
1373
+ en: "Agent switch cancelled.",
1374
+ zh: "\u5DF2\u53D6\u6D88\u5207\u6362 agent\u3002"
1375
+ },
1376
+ historyPickerTitle: {
1377
+ en: "Resume dialog (\u2191\u2193 Enter Esc)",
1378
+ zh: "\u6062\u590D\u5BF9\u8BDD\uFF08\u2191\u2193 \u79FB\u52A8 \xB7 Enter \u9009\u62E9 \xB7 Esc \u53D6\u6D88\uFF09"
1379
+ },
1380
+ noDialogsYet: {
1381
+ en: "No dialogs yet.",
1382
+ zh: "\u8FD8\u6CA1\u6709\u5386\u53F2\u5BF9\u8BDD\u3002"
1383
+ },
1384
+ langUsage: {
1385
+ en: "Usage: /lang <zh|en>",
1386
+ zh: "\u7528\u6CD5\uFF1A/lang <zh|en>"
1387
+ },
1388
+ stopHint: {
1389
+ en: "Esc to stop",
1390
+ zh: "Esc \u505C\u6B62\u56DE\u590D"
1391
+ },
1392
+ queuedHint: {
1393
+ en: "queued",
1394
+ zh: "\u6392\u961F"
1395
+ },
1396
+ turnStopped: {
1397
+ en: "Stopped this reply.",
1398
+ zh: "\u5DF2\u505C\u6B62\u672C\u6B21\u56DE\u590D\u3002"
1399
+ },
1400
+ turnFailed: {
1401
+ en: "This reply failed. Queued messages are kept \u2014 press Enter to resend.",
1402
+ 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"
1403
+ },
1404
+ quotaExhaustedHint: {
1405
+ 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.",
1406
+ 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"
1407
+ },
1408
+ copiedLastReply: {
1409
+ en: "Copied the last reply to the clipboard.",
1410
+ zh: "\u5DF2\u590D\u5236\u6700\u540E\u4E00\u6761\u56DE\u590D\u5230\u526A\u8D34\u677F\u3002"
1411
+ },
1412
+ copyNothing: {
1413
+ en: "Nothing to copy yet.",
1414
+ zh: "\u8FD8\u6CA1\u6709\u53EF\u590D\u5236\u7684\u5185\u5BB9\u3002"
1415
+ },
1416
+ copyFailed: {
1417
+ en: "Copy failed",
1418
+ zh: "\u590D\u5236\u5931\u8D25"
1419
+ },
1420
+ copyUnavailable: {
1421
+ en: "Clipboard is unavailable in this environment. The last reply was printed above for manual copy.",
1422
+ zh: "\u5F53\u524D\u73AF\u5883\u6CA1\u6709\u53EF\u7528\u7684\u526A\u8D34\u677F\u3002\u6700\u540E\u4E00\u6761\u56DE\u590D\u5DF2\u6253\u5370\u5728\u4E0A\u65B9\uFF0C\u53EF\u624B\u52A8\u590D\u5236\u3002"
1423
+ },
1424
+ copyUsage: {
1425
+ en: "Usage: /copy or /copy view",
1426
+ zh: "\u7528\u6CD5\uFF1A/copy \u6216 /copy view"
1427
+ },
1428
+ copyViewTitle: {
1429
+ en: "Copy view \u2014 latest AI reply",
1430
+ zh: "\u590D\u5236\u89C6\u56FE \u2014 \u6700\u65B0 AI \u56DE\u590D"
1431
+ },
1432
+ copyViewHint: {
1433
+ en: "Drag to select; edge scrolling uses native terminal scrollback. Press Esc or Enter to return.",
1434
+ zh: "\u76F4\u63A5\u62D6\u9009\uFF0C\u62D6\u5230\u8FB9\u7F18\u53EF\u7528\u7EC8\u7AEF\u539F\u751F\u56DE\u6EDA\u8DE8\u5C4F\u9009\u62E9\u3002\u6309 Esc \u6216 Enter \u8FD4\u56DE\u3002"
1435
+ },
1436
+ historyNoToken: {
1437
+ en: "History requires an auth token. Run `nolo login` or set AUTH_TOKEN.",
1438
+ zh: "\u67E5\u770B\u5386\u53F2\u5BF9\u8BDD\u9700\u8981\u767B\u5F55\u51ED\u8BC1\u3002\u8BF7\u8FD0\u884C `nolo login` \u6216\u8BBE\u7F6E AUTH_TOKEN\u3002"
1439
+ },
1440
+ historyBadToken: {
1441
+ en: "Could not read a user id from AUTH_TOKEN. Run `nolo login` again.",
1442
+ zh: "\u65E0\u6CD5\u4ECE AUTH_TOKEN \u89E3\u6790\u51FA\u7528\u6237 id\uFF0C\u8BF7\u91CD\u65B0\u8FD0\u884C `nolo login`\u3002"
1443
+ },
1444
+ mouseOn: {
1445
+ en: "Mouse mode on: wheel scrolls the transcript; hold Shift (or Option/Fn) to select text.",
1446
+ zh: "\u9F20\u6807\u6A21\u5F0F\u5DF2\u5F00\u542F\uFF1A\u6EDA\u8F6E\u6EDA\u52A8\u5BF9\u8BDD\u8BB0\u5F55\uFF1B\u6309\u4F4F Shift\uFF08\u6216 Option/Fn\uFF09\u53EF\u62D6\u9009\u590D\u5236\u3002"
1447
+ },
1448
+ mouseOff: {
1449
+ en: "Mouse mode off: drag to select/copy freely; scroll with PageUp/PageDown.",
1450
+ zh: "\u9F20\u6807\u6A21\u5F0F\u5DF2\u5173\u95ED\uFF1A\u53EF\u76F4\u63A5\u62D6\u9009\u590D\u5236\uFF1B\u7528 PageUp/PageDown \u6EDA\u52A8\u5BF9\u8BDD\u8BB0\u5F55\u3002"
1451
+ },
1452
+ mouseUsage: {
1453
+ en: "Usage: /mouse <on|off>",
1454
+ zh: "\u7528\u6CD5\uFF1A/mouse <on|off>"
1455
+ },
1456
+ langSwitched: {
1457
+ en: "Language switched to English.",
1458
+ zh: "\u5DF2\u5207\u6362\u4E3A\u4E2D\u6587\u3002"
1459
+ },
1460
+ // --- Tool trace copy ------------------------------------------------------
1461
+ // The compact trace shows only status, never timing or output size: a line
1462
+ // count told the user nothing actionable and the ms figure read as noise.
1463
+ toolNeedsAction: {
1464
+ en: "needs action",
1465
+ zh: "\u5F85\u786E\u8BA4"
1466
+ },
1467
+ toolTimedOut: {
1468
+ en: "timed out",
1469
+ zh: "\u5DF2\u8D85\u65F6"
1470
+ },
1471
+ toolExitCode: {
1472
+ en: "exit",
1473
+ zh: "\u9000\u51FA\u7801"
1474
+ },
1475
+ toolFailed: {
1476
+ en: "failed",
1477
+ zh: "\u5931\u8D25"
1478
+ },
1479
+ // --- Dialog (picker / confirm) copy --------------------------------------
1480
+ // Key-hint wording is unified across select / multi-select / confirm so the
1481
+ // three dialogs read as one family: "<Label> <↑↓ move · Enter choose ·
1482
+ // Esc cancel> <count>". Connectors are "·" between keys, two spaces between
1483
+ // the label, hint, and count. zh uses full-width parentheses to match the
1484
+ // existing historyPickerTitle; en uses ASCII parentheses.
1485
+ dialogSelectLabel: {
1486
+ en: "Select",
1487
+ zh: "\u9009\u62E9"
1488
+ },
1489
+ dialogSelectHint: {
1490
+ en: "(\u2191\u2193 move \xB7 Enter choose \xB7 Esc cancel)",
1491
+ zh: "\uFF08\u2191\u2193 \u79FB\u52A8 \xB7 Enter \u9009\u62E9 \xB7 Esc \u53D6\u6D88\uFF09"
1492
+ },
1493
+ dialogMultiSelectLabel: {
1494
+ en: "Select",
1495
+ zh: "\u9009\u62E9"
1496
+ },
1497
+ dialogMultiSelectHint: {
1498
+ en: "(\u2191\u2193 move \xB7 Space toggle \xB7 Enter submit \xB7 Esc cancel)",
1499
+ zh: "\uFF08\u2191\u2193 \u79FB\u52A8 \xB7 Space \u5207\u6362 \xB7 Enter \u63D0\u4EA4 \xB7 Esc \u53D6\u6D88\uFF09"
1500
+ },
1501
+ dialogMultiSelectSelected: {
1502
+ en: "selected",
1503
+ zh: "\u5DF2\u9009"
1504
+ },
1505
+ dialogConfirmHint: {
1506
+ en: "(\u2191\u2193 move \xB7 Enter choose \xB7 Esc cancel)",
1507
+ zh: "\uFF08\u2191\u2193 \u79FB\u52A8 \xB7 Enter \u9009\u62E9 \xB7 Esc \u53D6\u6D88\uFF09"
1508
+ },
1509
+ dialogConfirmTitle: {
1510
+ en: "Confirm destructive shell command",
1511
+ zh: "\u786E\u8BA4\u6267\u884C\u7834\u574F\u6027 shell \u547D\u4EE4"
1512
+ },
1513
+ dialogConfirmBody: {
1514
+ en: "This command may delete or reset user content and needs explicit confirmation before it runs.",
1515
+ zh: "\u8BE5\u547D\u4EE4\u53EF\u80FD\u5220\u9664\u6216\u91CD\u7F6E\u7528\u6237\u5185\u5BB9\uFF0C\u9700\u8981\u7528\u6237\u660E\u786E\u786E\u8BA4\u540E\u624D\u80FD\u6267\u884C\u3002"
1516
+ },
1517
+ dialogConfirmCommandLabel: {
1518
+ en: "Command",
1519
+ zh: "\u547D\u4EE4"
1520
+ },
1521
+ dialogConfirmCommandTruncated: {
1522
+ en: "(truncated)",
1523
+ zh: "\uFF08\u5DF2\u622A\u65AD\uFF09"
1524
+ },
1525
+ dialogConfirmAllowLabel: {
1526
+ en: "Allow",
1527
+ zh: "\u5141\u8BB8"
1528
+ },
1529
+ dialogConfirmAllowDetail: {
1530
+ en: "execute this time",
1531
+ zh: "\u672C\u6B21\u6267\u884C"
1532
+ },
1533
+ dialogConfirmCancelLabel: {
1534
+ en: "Cancel",
1535
+ zh: "\u53D6\u6D88"
1536
+ },
1537
+ dialogConfirmCancelDetail: {
1538
+ en: "abort the operation",
1539
+ zh: "\u4E2D\u6B62\u64CD\u4F5C"
1540
+ },
1541
+ contextTitle: {
1542
+ en: "Workspace context",
1543
+ zh: "\u5DE5\u4F5C\u533A\u4E0A\u4E0B\u6587"
1544
+ },
1545
+ contextNext: {
1546
+ en: "Next:",
1547
+ zh: "\u4E0B\u4E00\u6B65\uFF1A"
1548
+ },
1549
+ contextFieldAgent: {
1550
+ en: "agent",
1551
+ zh: "\u667A\u80FD\u4F53"
1552
+ },
1553
+ contextFieldTokens: {
1554
+ en: "tokens",
1555
+ zh: "\u4EE4\u724C"
1556
+ },
1557
+ contextFieldDialog: {
1558
+ en: "dialog",
1559
+ zh: "\u5BF9\u8BDD"
1560
+ },
1561
+ contextFieldDocs: {
1562
+ en: "docs",
1563
+ zh: "\u6587\u6863"
1564
+ },
1565
+ contextFieldSkills: {
1566
+ en: "skills",
1567
+ zh: "\u6280\u80FD"
1568
+ },
1569
+ contextFieldProfile: {
1570
+ en: "profile",
1571
+ zh: "\u914D\u7F6E"
1572
+ },
1573
+ contextFieldRuntime: {
1574
+ en: "runtime",
1575
+ zh: "\u8FD0\u884C\u65F6"
1576
+ },
1577
+ contextFieldTools: {
1578
+ en: "tools",
1579
+ zh: "\u5DE5\u5177"
1580
+ },
1581
+ contextFieldThinking: {
1582
+ en: "thinking",
1583
+ zh: "\u601D\u8003"
1584
+ },
1585
+ contextFieldServer: {
1586
+ en: "server",
1587
+ zh: "\u670D\u52A1\u7AEF"
1588
+ },
1589
+ contextNextAgents: {
1590
+ en: "see specialist shortcuts",
1591
+ zh: "\u67E5\u770B\u4E13\u7528\u667A\u80FD\u4F53\u5FEB\u6377\u65B9\u5F0F"
1592
+ },
1593
+ contextNextDoc: {
1594
+ en: "add working context",
1595
+ zh: "\u6DFB\u52A0\u5DE5\u4F5C\u4E0A\u4E0B\u6587"
1596
+ },
1597
+ contextNextSkill: {
1598
+ en: "attach a skill to this workspace",
1599
+ zh: "\u4E3A\u8BE5\u5DE5\u4F5C\u533A\u6302\u8F7D\u6280\u80FD"
1600
+ },
1601
+ contextNextNew: {
1602
+ en: "start a clean dialog",
1603
+ zh: "\u5F00\u59CB\u4E00\u4E2A\u5E72\u51C0\u7684\u5BF9\u8BDD"
1604
+ },
1605
+ agentsTitle: {
1606
+ en: "Agents:",
1607
+ zh: "\u667A\u80FD\u4F53\uFF1A"
1608
+ },
1609
+ agentsTip: {
1610
+ en: "Tip: run /switch for the full picker, or /switch list for your private agents too.",
1611
+ 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"
1612
+ },
1613
+ helpText: {
1614
+ en: [
1615
+ "Commands:",
1616
+ " /help Show this help",
1617
+ " /new Clear screen and start a fresh dialog",
1618
+ " /compact Compact current dialog and fork a new one",
1619
+ " /context Show workspace context and next actions",
1620
+ " /runtime <mode> Use auto, local, or server runtime",
1621
+ " /tools <mode> Control tool trace: hide, compact, verbose",
1622
+ " /thinking <mode> Control thinking output: hide, marker, show",
1623
+ " /switch Pick an agent interactively (\u2191\u2193, Enter)",
1624
+ " /switch list List agents as text",
1625
+ " /switch <name> Switch directly by name, alias, or key (alias: /agent)",
1626
+ " /agents List platform agent shortcuts",
1627
+ " /history Pick a recent dialog to resume (\u2191\u2193, Enter)",
1628
+ " /resume <dialogId> Resume a dialog directly by id",
1629
+ " /lang <zh|en> Switch interface language",
1630
+ " /copy Copy the last reply to the clipboard",
1631
+ " /copy view Open native scrollback for partial selection (Ctrl+O)",
1632
+ " /mouse <on|off> Toggle mouse mode (off = drag to select text)",
1633
+ " /doc List attached docs",
1634
+ " /doc attach <doc> Attach a doc to this workspace",
1635
+ " /skill List attached skills",
1636
+ " /skill attach <ref> Attach a skill (dbKey, name, or SKILL.md path)",
1637
+ " /skill detach <ref> Detach a skill",
1638
+ " /skill clear Detach all skills",
1639
+ " /customize Describe how you want to tune nolo",
1640
+ " /tasks List background process tasks (aliases: /jobs, /procs)",
1641
+ " /stop <pid|all> Stop background process tasks",
1642
+ " /login Show login/profile hint",
1643
+ " /profile Show active profile",
1644
+ " /update Update the nolo CLI install",
1645
+ " /version Show version/update hint",
1646
+ " /exit Leave the workspace",
1647
+ "",
1648
+ "You can also type normally. nolo routes simple read/status requests to CLI commands and sends the rest to the current agent."
1649
+ ].join("\n"),
1650
+ zh: [
1651
+ "\u547D\u4EE4\uFF1A",
1652
+ " /help \u663E\u793A\u672C\u5E2E\u52A9",
1653
+ " /new \u6E05\u5C4F\u5E76\u5F00\u59CB\u65B0\u5BF9\u8BDD",
1654
+ " /compact \u538B\u7F29\u5F53\u524D\u5BF9\u8BDD\u5E76\u5206\u53C9\u51FA\u65B0\u5BF9\u8BDD",
1655
+ " /context \u67E5\u770B\u5DE5\u4F5C\u533A\u4E0A\u4E0B\u6587\u4E0E\u540E\u7EED\u64CD\u4F5C",
1656
+ " /runtime <mode> \u5207\u6362 runtime\uFF1Aauto\u3001local\u3001server",
1657
+ " /tools <mode> \u5DE5\u5177\u8F68\u8FF9\u663E\u793A\uFF1Ahide\u3001compact\u3001verbose",
1658
+ " /thinking <mode> \u601D\u8003\u8FC7\u7A0B\u663E\u793A\uFF1Ahide\u3001marker\u3001show",
1659
+ " /switch \u4EA4\u4E92\u5F0F\u9009\u62E9 agent\uFF08\u2191\u2193 \u79FB\u52A8\uFF0CEnter \u786E\u8BA4\uFF09",
1660
+ " /switch list \u4EE5\u6587\u672C\u5217\u51FA\u5168\u90E8 agent",
1661
+ " /switch <name> \u6309\u540D\u79F0\u3001\u522B\u540D\u6216 key \u76F4\u63A5\u5207\u6362\uFF08\u522B\u540D\uFF1A/agent\uFF09",
1662
+ " /agents \u5217\u51FA\u5E73\u53F0 agent \u5FEB\u6377\u65B9\u5F0F",
1663
+ " /history \u4ECE\u6700\u8FD1\u5BF9\u8BDD\u4E2D\u9009\u62E9\u5E76\u6062\u590D\uFF08\u2191\u2193\uFF0CEnter\uFF09",
1664
+ " /resume <dialogId> \u6309 id \u76F4\u63A5\u6062\u590D\u5BF9\u8BDD",
1665
+ " /lang <zh|en> \u5207\u6362\u754C\u9762\u8BED\u8A00",
1666
+ " /copy \u590D\u5236\u6700\u540E\u4E00\u6761\u56DE\u590D\u5230\u526A\u8D34\u677F",
1667
+ " /copy view \u6253\u5F00\u539F\u751F\u56DE\u6EDA\u8FDB\u884C\u8DE8\u5C4F\u62D6\u9009\uFF08Ctrl+O\uFF09",
1668
+ " /mouse <on|off> \u5207\u6362\u9F20\u6807\u6A21\u5F0F\uFF08off \u540E\u53EF\u76F4\u63A5\u62D6\u9009\u6587\u672C\uFF09",
1669
+ " /doc \u5217\u51FA\u5DF2\u6302\u8F7D\u7684\u6587\u6863",
1670
+ " /doc attach <doc> \u6302\u8F7D\u6587\u6863\u5230\u5F53\u524D\u5DE5\u4F5C\u533A",
1671
+ " /skill \u5217\u51FA\u5DF2\u6302\u8F7D\u7684\u6280\u80FD",
1672
+ " /skill attach <ref> \u6302\u8F7D\u6280\u80FD\uFF08dbKey\u3001\u540D\u79F0\u6216 SKILL.md \u8DEF\u5F84\uFF09",
1673
+ " /skill detach <ref> \u5378\u8F7D\u6307\u5B9A\u6280\u80FD",
1674
+ " /skill clear \u5378\u8F7D\u5168\u90E8\u6280\u80FD",
1675
+ " /customize \u63CF\u8FF0\u4F60\u60F3\u600E\u4E48\u8C03\u6559 nolo",
1676
+ " /tasks \u5217\u51FA\u540E\u53F0\u5B50\u8FDB\u7A0B\u4EFB\u52A1\uFF08\u522B\u540D\uFF1A/jobs, /procs\uFF09",
1677
+ " /stop <pid|all> \u505C\u6B62\u6307\u5B9A\u7684\u540E\u53F0\u4EFB\u52A1",
1678
+ " /login \u67E5\u770B\u767B\u5F55 / \u914D\u7F6E\u63D0\u793A",
1679
+ " /profile \u67E5\u770B\u5F53\u524D\u914D\u7F6E\u73AF\u5883",
1680
+ " /update \u66F4\u65B0 nolo CLI",
1681
+ " /version \u67E5\u770B\u7248\u672C\u4E0E\u66F4\u65B0\u63D0\u793A",
1682
+ " /exit \u9000\u51FA\u5DE5\u4F5C\u533A",
1683
+ "",
1684
+ "\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"
1685
+ ].join("\n")
1686
+ }
1687
+ };
1688
+ function t(key) {
1689
+ return STRINGS[key][currentLocale];
1690
+ }
1691
+ var TOOL_LABELS = {
1692
+ // Local workspace tools (packages/agent-runtime/localWorkspaceTools.ts)
1693
+ readFile: { en: "Read", zh: "\u8BFB\u53D6" },
1694
+ writeFile: { en: "Write", zh: "\u5199\u5165" },
1695
+ editFile: { en: "Edit", zh: "\u7F16\u8F91" },
1696
+ listFiles: { en: "List", zh: "\u5217\u51FA" },
1697
+ searchFiles: { en: "Search", zh: "\u641C\u7D22" },
1698
+ globFiles: { en: "Glob", zh: "\u5339\u914D" },
1699
+ execShell: { en: "Run", zh: "\u6267\u884C" },
1700
+ runCommand: { en: "Run", zh: "\u6267\u884C" },
1701
+ captureVisualState: { en: "Capture", zh: "\u622A\u5C4F" },
1702
+ // Workspace / diagnostics
1703
+ searchWorkspace: { en: "Search workspace", zh: "\u641C\u7D22\u5DE5\u4F5C\u533A" },
1704
+ cliDoctor: { en: "Doctor", zh: "\u81EA\u68C0" },
1705
+ cliWhoami: { en: "Whoami", zh: "\u67E5\u770B\u8EAB\u4EFD" },
1706
+ checkEnv: { en: "Check env", zh: "\u68C0\u67E5\u73AF\u5883" },
1707
+ configure: { en: "Configure", zh: "\u914D\u7F6E" },
1708
+ notifyUser: { en: "Notify", zh: "\u901A\u77E5" },
1709
+ // Docs / dialogs / spaces
1710
+ readDoc: { en: "Read doc", zh: "\u8BFB\u53D6\u6587\u6863" },
1711
+ createDoc: { en: "Create doc", zh: "\u65B0\u5EFA\u6587\u6863" },
1712
+ updateDoc: { en: "Update doc", zh: "\u66F4\u65B0\u6587\u6863" },
1713
+ readDialog: { en: "Read dialog", zh: "\u8BFB\u53D6\u5BF9\u8BDD" },
1714
+ listDialogs: { en: "List dialogs", zh: "\u5217\u51FA\u5BF9\u8BDD" },
1715
+ queryDialogsBySubjectRef: { en: "Query dialogs", zh: "\u67E5\u8BE2\u5BF9\u8BDD" },
1716
+ searchDialogMessages: { en: "Search messages", zh: "\u641C\u7D22\u6D88\u606F" },
1717
+ listSpaces: { en: "List spaces", zh: "\u5217\u51FA\u7A7A\u95F4" },
1718
+ readSpace: { en: "Read space", zh: "\u8BFB\u53D6\u7A7A\u95F4" },
1719
+ // Tables
1720
+ createTable: { en: "Create table", zh: "\u521B\u5EFA\u8868" },
1721
+ queryTableRows: { en: "Query rows", zh: "\u67E5\u8BE2\u8868\u884C" },
1722
+ addTableRow: { en: "Add row", zh: "\u65B0\u589E\u8868\u884C" },
1723
+ addTableRows: { en: "Add rows", zh: "\u65B0\u589E\u8868\u884C" },
1724
+ updateTableRow: { en: "Update row", zh: "\u66F4\u65B0\u8868\u884C" },
1725
+ deleteTableRow: { en: "Delete row", zh: "\u5220\u9664\u8868\u884C" },
1726
+ // Web
1727
+ fetchWebpage: { en: "Fetch page", zh: "\u6293\u53D6\u7F51\u9875" },
1728
+ readPage: { en: "Read page", zh: "\u8BFB\u53D6\u7F51\u9875" },
1729
+ exaSearch: { en: "Web search", zh: "\u8054\u7F51\u641C\u7D22" }
1730
+ };
1731
+ function toolLabel(name) {
1732
+ return TOOL_LABELS[name]?.[currentLocale] ?? name;
1733
+ }
1734
+
1735
+ // packages/cli/client/toolOutput.ts
1736
+ function normalizeToolDisplayMode(raw, fallback = "compact") {
1737
+ const normalized = asTrimmedLowercaseString(raw);
1738
+ if (normalized === "hide" || normalized === "off" || normalized === "false" || normalized === "0") {
1739
+ return "hide";
1740
+ }
1741
+ if (normalized === "verbose" || normalized === "debug" || normalized === "trace" || normalized === "full") {
1742
+ return "verbose";
1743
+ }
1744
+ if (normalized === "compact" || normalized === "minimal" || normalized === "short") {
1745
+ return "compact";
1746
+ }
1747
+ return fallback;
1748
+ }
1749
+ function resolveToolDisplayMode(env = process.env) {
1750
+ const legacyTrace = asTrimmedLowercaseString(env.NOLO_TRACE_TOOLS);
1751
+ if (legacyTrace === "0" || legacyTrace === "false" || legacyTrace === "off") {
1752
+ return "hide";
1753
+ }
1754
+ if (legacyTrace === "verbose" || legacyTrace === "full") {
1755
+ return "verbose";
1756
+ }
1757
+ return normalizeToolDisplayMode(env.NOLO_CLI_TOOLS ?? env.NOLO_TOOLS, "compact");
1758
+ }
1759
+ function shouldEmitToolEvents(mode) {
1760
+ return mode !== "hide";
1761
+ }
1762
+ function clip(value, max = 72) {
1763
+ return clipCompactText(value, max, "\u2026");
1764
+ }
1765
+ function formatActiveToolLabel(event) {
1766
+ const toolName = event.toolName || "tool";
1767
+ const args = clipPathAware(event.argumentsPreview ?? "");
1768
+ const label = toolLabel(toolName);
1769
+ return args ? `${label} ${args}` : label;
1770
+ }
1771
+ function compactResultHint(event) {
1772
+ const gate = readActionGate(event.metadata?.actionGate);
1773
+ if (gate) {
1774
+ const commandPayload = gate.kind === "handoff" ? readCommandActionGatePayload(gate.payload) : null;
1775
+ const command = commandPayload?.displayCommand ?? commandPayload?.command.join(" ") ?? "";
1776
+ const detail = command.trim() ? command : gate.title;
1777
+ return { inline: `${t("toolNeedsAction")}: ${clip(detail, 120)}` };
1778
+ }
1779
+ if (event.metadata?.timedOut) return { inline: t("toolTimedOut") };
1780
+ if (event.toolName === "readFile" && event.metadata) {
1781
+ const range = readReadFileRange(event.metadata);
1782
+ if (range) return { inline: range };
1783
+ }
1784
+ if (event.toolName === "editFile" && event.metadata) {
1785
+ const detail = formatEditFileSnippet(event.metadata);
1786
+ if (detail) return { inline: "", detail };
1787
+ }
1788
+ const summary = event.summary;
1789
+ if (!summary) return { inline: "" };
1790
+ const exitMatch = summary.match(/exit=(\d+)/);
1791
+ if (exitMatch && exitMatch[1] !== "0") return { inline: `${t("toolExitCode")} ${exitMatch[1]}` };
1792
+ return { inline: "" };
1793
+ }
1794
+ function readReadFileRange(metadata) {
1795
+ const startLine = metadata.startLine;
1796
+ const endLine = metadata.endLine;
1797
+ const totalLines = metadata.totalLines;
1798
+ const truncated = metadata.truncated;
1799
+ if (typeof startLine !== "number" || typeof endLine !== "number" || typeof totalLines !== "number") {
1800
+ return void 0;
1801
+ }
1802
+ if (!truncated && startLine === 1 && endLine === totalLines) return void 0;
1803
+ return `${startLine}-${endLine}/${totalLines}`;
1804
+ }
1805
+ var EDIT_SNIPPET_MAX_LINES = 5;
1806
+ var EDIT_SNIPPET_MAX_WIDTH = 96;
1807
+ function formatEditFileSnippet(metadata) {
1808
+ const oldSnippet = typeof metadata.oldSnippet === "string" ? metadata.oldSnippet : void 0;
1809
+ const newSnippet = typeof metadata.newSnippet === "string" ? metadata.newSnippet : void 0;
1810
+ if (!oldSnippet && !newSnippet) return void 0;
1811
+ const lines = [];
1812
+ if (oldSnippet) {
1813
+ for (const line of snippetLines(oldSnippet)) lines.push(`- ${line}`);
1814
+ }
1815
+ if (newSnippet) {
1816
+ for (const line of snippetLines(newSnippet)) lines.push(`+ ${line}`);
1817
+ }
1818
+ return lines.length ? lines.join("\n") : void 0;
1819
+ }
1820
+ function snippetLines(snippet) {
1821
+ const lines = snippet.split(/\r?\n/).filter((line) => line.length > 0);
1822
+ const shown = lines.slice(0, EDIT_SNIPPET_MAX_LINES);
1823
+ return shown.map((line) => line.length > EDIT_SNIPPET_MAX_WIDTH ? `${line.slice(0, EDIT_SNIPPET_MAX_WIDTH)}\u2026` : line);
1824
+ }
1825
+ function isFailedToolResult(event) {
1826
+ const exitCode = event.metadata?.exitCode;
1827
+ if (event.metadata?.actionGate) return false;
1828
+ if (typeof exitCode === "number" && exitCode !== 0) return true;
1829
+ return Boolean(event.metadata?.timedOut);
1830
+ }
1831
+ function isNeedsActionToolResult(event) {
1832
+ return Boolean(event.metadata?.actionGate);
1833
+ }
1834
+ function parseUiAskChoiceForCli(event) {
1835
+ if (event.toolName !== "ui_ask_choice" && !event.metadata?.uiAskChoice) {
1836
+ return null;
1837
+ }
1838
+ const parsed = parseUiAskChoiceContent(event.content);
1839
+ if (!parsed) return null;
1840
+ const question = typeof parsed.question === "string" ? parsed.question.trim() : "";
1841
+ const choices = (Array.isArray(parsed.choices) ? parsed.choices : []).filter((c) => Boolean(c && typeof c === "object")).map((c) => ({
1842
+ label: String(c.label ?? "").trim(),
1843
+ userMessage: typeof c.userMessage === "string" ? c.userMessage : void 0
1844
+ })).filter((c) => c.label.length > 0);
1845
+ if (!question || choices.length === 0) return null;
1846
+ return { question, choices };
1847
+ }
1848
+ function formatUiAskChoiceBlock(event, colorEnabled) {
1849
+ const parsed = parseUiAskChoiceForCli(event);
1850
+ if (!parsed) return null;
1851
+ const lines = [];
1852
+ lines.push("");
1853
+ if (colorEnabled) {
1854
+ lines.push(`${themeText("\u2753 ", "info", true)}${styleCliText(parsed.question, "cyan", true)}`);
1855
+ } else {
1856
+ lines.push(`\u2753 ${parsed.question}`);
1857
+ }
1858
+ parsed.choices.forEach((choice, i) => {
1859
+ const num = String(i + 1);
1860
+ const label = choice.label;
1861
+ if (colorEnabled) {
1862
+ lines.push(
1863
+ ` ${themeText(num + ".", "chrome", true)} ${label}`
1864
+ );
1865
+ } else {
1866
+ lines.push(` ${num}. ${label}`);
1867
+ }
1868
+ });
1869
+ const hint = "\u8BF7\u8F93\u5165\u5E8F\u53F7\u9009\u62E9\uFF0C\u6216\u76F4\u63A5\u56DE\u590D\uFF1A";
1870
+ if (colorEnabled) {
1871
+ lines.push(` ${dimCliText(hint, true)}`);
1872
+ } else {
1873
+ lines.push(` ${hint}`);
1874
+ }
1875
+ return `${lines.join("\n")}
1876
+ `;
1877
+ }
1878
+ function formatToolTraceLine(text, colorEnabled, accent = "none") {
1879
+ if (!colorEnabled) return `${text}
1880
+ `;
1881
+ if (accent === "error") {
1882
+ return `${themeText(text, "danger", true)}
1883
+ `;
1884
+ }
1885
+ return `${dimCliText(text, true)}
1886
+ `;
1887
+ }
1888
+ function formatVerboseToolEvent(event, colorEnabled) {
1889
+ const round = event.round + 1;
1890
+ const detail = event.argumentsPreview ? ` ${event.argumentsPreview}` : "";
1891
+ if (event.type === "tool-call") {
1892
+ return formatToolTraceLine(`[nolo:tool] #${round} -> ${event.toolName}${detail}`, colorEnabled);
1893
+ }
1894
+ if (event.type === "tool-error") {
1895
+ const elapsed2 = typeof event.elapsedMs === "number" ? ` ${event.elapsedMs}ms` : "";
1896
+ return formatToolTraceLine(
1897
+ `[nolo:tool] #${round} !! ${event.toolName}${elapsed2}: ${event.message ?? "failed"}`,
1898
+ colorEnabled,
1899
+ "error"
1900
+ );
1901
+ }
1902
+ const elapsed = typeof event.elapsedMs === "number" ? ` ${event.elapsedMs}ms` : "";
1903
+ if (event.type === "tool-result" && (event.toolName === "ui_ask_choice" || event.metadata?.uiAskChoice)) {
1904
+ const block = formatUiAskChoiceBlock(event, colorEnabled);
1905
+ if (block) return `${formatToolTraceLine(`[nolo:tool] #${round} <- ${event.toolName}${elapsed}`, colorEnabled)}${block}`;
1906
+ }
1907
+ const summary = event.summary ? ` ${event.summary}` : "";
1908
+ return formatToolTraceLine(
1909
+ `[nolo:tool] #${round} <- ${event.toolName}${elapsed}${summary}`,
1910
+ colorEnabled
1911
+ );
1912
+ }
1913
+ function isReadToolName(name) {
1914
+ if (!name) return false;
1915
+ return name === "read" || name === "readFile" || name === "read_file" || name === "readWorkspaceFile";
1916
+ }
1917
+ function isSearchToolName(name) {
1918
+ if (!name) return false;
1919
+ return name === "grep" || name === "searchFiles" || name === "search_files" || name === "globFiles" || name === "glob_files" || name === "glob" || name === "searchWorkspace" || name === "search";
1920
+ }
1921
+ function isRunToolName(name) {
1922
+ if (!name) return false;
1923
+ return name === "execShell" || name === "runCommand" || name === "run_command" || name === "launchProcess";
1924
+ }
1925
+ function isFetchToolName(name) {
1926
+ if (!name) return false;
1927
+ return name === "fetchWebpage" || name === "fetch_webpage";
1928
+ }
1929
+ function isRunResultFoldable(event) {
1930
+ return Boolean(event.metadata?.actionGate) === false;
1931
+ }
1932
+ function formatSearchTreeBlockForCli(items, colorEnabled) {
1933
+ if (items.length === 0) return "";
1934
+ const { count, lines } = formatSearchTreeLines(items);
1935
+ if (!colorEnabled) {
1936
+ const headerLine2 = `\u2022 Search (${count})
1937
+ `;
1938
+ const treeLines2 = lines.map((l) => ` ${l.connector}${l.queryText}`).join("\n");
1939
+ return `${headerLine2}${treeLines2}
1940
+ `;
1941
+ }
1942
+ const bullet = themeText("\u2022", "chrome", true);
1943
+ const title = styleCliText("Search", "bold", true);
1944
+ const countText = themeText(`(${count})`, "muted", true);
1945
+ const headerLine = `${bullet} ${title} ${countText}
1946
+ `;
1947
+ const treeLines = lines.map((l) => {
1948
+ const connector = themeText(` ${l.connector}`, "chrome", true);
1949
+ const queryText = themeText(l.queryText, "muted", true);
1950
+ return `${connector}${queryText}`;
1951
+ }).join("\n");
1952
+ return `${headerLine}${treeLines}
1953
+ `;
1954
+ }
1955
+ function formatReadTreeBlockForCli(items, colorEnabled) {
1956
+ if (items.length === 0) return "";
1957
+ const { count, lines } = formatReadTreeLines(items);
1958
+ if (!colorEnabled) {
1959
+ const headerLine2 = `\u2022 Read (${count})
1960
+ `;
1961
+ const treeLines2 = lines.map((l) => ` ${l.connector}${l.pathWithRange}`).join("\n");
1962
+ return `${headerLine2}${treeLines2}
1963
+ `;
1964
+ }
1965
+ const bullet = themeText("\u2022", "chrome", true);
1966
+ const title = styleCliText("Read", "bold", true);
1967
+ const countText = themeText(`(${count})`, "muted", true);
1968
+ const headerLine = `${bullet} ${title} ${countText}
1969
+ `;
1970
+ const treeLines = lines.map((l) => {
1971
+ const connector = themeText(` ${l.connector}`, "chrome", true);
1972
+ const pathText = themeText(l.pathWithRange, "muted", true);
1973
+ return `${connector}${pathText}`;
1974
+ }).join("\n");
1975
+ return `${headerLine}${treeLines}
1976
+ `;
1977
+ }
1978
+ function formatRunTreeBlockForCli(items, colorEnabled) {
1979
+ if (items.length === 0) return "";
1980
+ const { count, lines } = formatRunTreeLines(items);
1981
+ if (!colorEnabled) {
1982
+ const headerLine2 = `\u2022 Run (${count})
1983
+ `;
1984
+ const treeLines2 = lines.map((l) => ` ${l.connector}${l.commandText}`).join("\n");
1985
+ return `${headerLine2}${treeLines2}
1986
+ `;
1987
+ }
1988
+ const bullet = themeText("\u2022", "chrome", true);
1989
+ const title = styleCliText("Run", "bold", true);
1990
+ const countText = themeText(`(${count})`, "muted", true);
1991
+ const headerLine = `${bullet} ${title} ${countText}
1992
+ `;
1993
+ const treeLines = lines.map((l) => {
1994
+ const connector = themeText(` ${l.connector}`, "chrome", true);
1995
+ const commandText = themeText(l.commandText, "muted", true);
1996
+ return `${connector}${commandText}`;
1997
+ }).join("\n");
1998
+ return `${headerLine}${treeLines}
1999
+ `;
2000
+ }
2001
+ function formatFetchTreeBlockForCli(items, colorEnabled) {
2002
+ if (items.length === 0) return "";
2003
+ const { count, lines } = formatFetchTreeLines(items);
2004
+ if (!colorEnabled) {
2005
+ const headerLine2 = `\u2022 Fetch (${count})
2006
+ `;
2007
+ const treeLines2 = lines.map((l) => ` ${l.connector}${l.urlText}`).join("\n");
2008
+ return `${headerLine2}${treeLines2}
2009
+ `;
2010
+ }
2011
+ const bullet = themeText("\u2022", "chrome", true);
2012
+ const title = styleCliText("Fetch", "bold", true);
2013
+ const countText = themeText(`(${count})`, "muted", true);
2014
+ const headerLine = `${bullet} ${title} ${countText}
2015
+ `;
2016
+ const treeLines = lines.map((l) => {
2017
+ const connector = themeText(` ${l.connector}`, "chrome", true);
2018
+ const urlText = themeText(l.urlText, "muted", true);
2019
+ return `${connector}${urlText}`;
2020
+ }).join("\n");
2021
+ return `${headerLine}${treeLines}
2022
+ `;
2023
+ }
2024
+ function formatCompactToolLine(event, pending, colorEnabled) {
2025
+ const toolName = event.toolName || pending?.toolName || "tool";
2026
+ if (isReadToolName(toolName) && event.type === "tool-result") {
2027
+ const rawPath = (typeof event.metadata?.path === "string" ? event.metadata.path : void 0) || (typeof event.metadata?.filePath === "string" ? event.metadata.filePath : void 0) || event.argumentsPreview || pending?.argumentsPreview || "";
2028
+ return formatReadTreeBlockForCli([{ path: rawPath, metadata: event.metadata }], colorEnabled);
2029
+ }
2030
+ if (isSearchToolName(toolName) && event.type === "tool-result") {
2031
+ const rawQuery = (typeof event.metadata?.query === "string" ? event.metadata.query : void 0) || (typeof event.metadata?.pattern === "string" ? event.metadata.pattern : void 0) || event.argumentsPreview || pending?.argumentsPreview || "";
2032
+ const rawPath = typeof event.metadata?.path === "string" ? event.metadata.path : void 0;
2033
+ return formatSearchTreeBlockForCli([{ query: rawQuery, path: rawPath }], colorEnabled);
2034
+ }
2035
+ if (isFetchToolName(toolName) && event.type === "tool-result") {
2036
+ const rawUrl = (typeof event.metadata?.url === "string" ? event.metadata.url : void 0) || event.argumentsPreview || pending?.argumentsPreview || "";
2037
+ return formatFetchTreeBlockForCli([{ url: rawUrl }], colorEnabled);
2038
+ }
2039
+ if (isRunToolName(toolName) && event.type === "tool-result" && isRunResultFoldable(event)) {
2040
+ const rawCommand = (typeof event.metadata?.command === "string" ? event.metadata.command : void 0) || event.argumentsPreview || pending?.argumentsPreview || "";
2041
+ const exitCode = typeof event.metadata?.exitCode === "number" ? event.metadata.exitCode : void 0;
2042
+ const timedOut = Boolean(event.metadata?.timedOut);
2043
+ return formatRunTreeBlockForCli([{ command: rawCommand, exitCode, timedOut }], colorEnabled);
2044
+ }
2045
+ const rawArgs = clipPathAware(event.argumentsPreview || pending?.argumentsPreview || "", 72);
2046
+ const rawLabel = toolLabel(toolName);
2047
+ const label = rawArgs ? `${rawLabel} ${rawArgs}` : rawLabel;
2048
+ if (event.type === "tool-error") {
2049
+ const message = clip(event.message ?? t("toolFailed"), 96);
2050
+ return formatToolTraceLine(`\u25B8 ${label} \u2717 ${message}`, colorEnabled, "error");
2051
+ }
2052
+ if (event.type === "tool-result" && (event.toolName === "ui_ask_choice" || event.metadata?.uiAskChoice)) {
2053
+ const block = formatUiAskChoiceBlock(event, colorEnabled);
2054
+ if (block) return block;
2055
+ }
2056
+ const hint = compactResultHint(event);
2057
+ const suffix = hint.inline ? ` ${hint.inline}` : "";
2058
+ const failed = isFailedToolResult(event);
2059
+ const marker = failed ? "\u2717" : isNeedsActionToolResult(event) ? "!" : "\u2713";
2060
+ let mainLine;
2061
+ if (!colorEnabled) {
2062
+ mainLine = `\u25B8 ${label} ${marker}${suffix}
2063
+ `;
2064
+ } else {
2065
+ const chromePointer = themeText("\u25B8", "chrome", true);
2066
+ const mutedLabel = themeText(rawLabel, "muted", true);
2067
+ const argsPart = rawArgs ? ` ${dimCliText(rawArgs, true)}` : "";
2068
+ const statusToken = failed ? themeText("\u2717", "danger", true) : isNeedsActionToolResult(event) ? themeText("!", "warning", true) : themeText("\u2713", "success", true);
2069
+ const suffixPart = hint.inline ? ` ${dimCliText(hint.inline, true)}` : "";
2070
+ mainLine = `${chromePointer} ${mutedLabel}${argsPart} ${statusToken}${suffixPart}
2071
+ `;
2072
+ }
2073
+ if (!hint.detail) return mainLine;
2074
+ const detailLines = hint.detail.split("\n").map((line) => formatEditDetailLine(line, colorEnabled)).join("");
2075
+ return `${mainLine}${detailLines}`;
2076
+ }
2077
+ function formatEditDetailLine(line, colorEnabled) {
2078
+ const marker = line.slice(0, 2);
2079
+ const rest = line.slice(2);
2080
+ if (!colorEnabled) return ` ${line}
2081
+ `;
2082
+ const diff = diffLineSequences();
2083
+ if (!diff) {
2084
+ const color = marker === "- " ? "red" : marker === "+ " ? "green" : void 0;
2085
+ if (color) {
2086
+ return ` ${styleCliText(marker, color, true)}${dimCliText(rest, true)}
2087
+ `;
2088
+ }
2089
+ return ` ${dimCliText(line, true)}
2090
+ `;
2091
+ }
2092
+ const RESET2 = "\x1B[0m";
2093
+ if (marker === "- ") {
2094
+ return ` ${diff.removed.bg}${diff.removed.fg}- ${rest}${RESET2}
2095
+ `;
2096
+ }
2097
+ if (marker === "+ ") {
2098
+ return ` ${diff.added.bg}${diff.added.fg}+ ${rest}${RESET2}
2099
+ `;
2100
+ }
2101
+ return ` ${dimCliText(line, true)}
2102
+ `;
2103
+ }
2104
+ function createToolEventFormatter(mode, colorEnabled = resolveCliColorEnabled()) {
2105
+ const pending = /* @__PURE__ */ new Map();
2106
+ let readBuffer = [];
2107
+ let searchBuffer = [];
2108
+ let runBuffer = [];
2109
+ let fetchBuffer = [];
2110
+ const flushBuffers = () => {
2111
+ let out = "";
2112
+ if (readBuffer.length > 0) {
2113
+ const items = readBuffer.map((evt) => {
2114
+ const call = pending.get(evt.toolCallId);
2115
+ const rawPath = (typeof evt.metadata?.path === "string" ? evt.metadata.path : void 0) || (typeof evt.metadata?.filePath === "string" ? evt.metadata.filePath : void 0) || evt.argumentsPreview || call?.argumentsPreview || "";
2116
+ return { path: rawPath, metadata: evt.metadata };
2117
+ });
2118
+ for (const evt of readBuffer) pending.delete(evt.toolCallId);
2119
+ readBuffer = [];
2120
+ out += formatReadTreeBlockForCli(items, colorEnabled);
2121
+ }
2122
+ if (searchBuffer.length > 0) {
2123
+ const items = searchBuffer.map((evt) => {
2124
+ const call = pending.get(evt.toolCallId);
2125
+ const rawQuery = (typeof evt.metadata?.query === "string" ? evt.metadata.query : void 0) || (typeof evt.metadata?.pattern === "string" ? evt.metadata.pattern : void 0) || evt.argumentsPreview || call?.argumentsPreview || "";
2126
+ const rawPath = typeof evt.metadata?.path === "string" ? evt.metadata.path : void 0;
2127
+ return { query: rawQuery, path: rawPath };
2128
+ });
2129
+ for (const evt of searchBuffer) pending.delete(evt.toolCallId);
2130
+ searchBuffer = [];
2131
+ out += formatSearchTreeBlockForCli(items, colorEnabled);
2132
+ }
2133
+ if (runBuffer.length > 0) {
2134
+ const items = runBuffer.map((evt) => {
2135
+ const call = pending.get(evt.toolCallId);
2136
+ const rawCommand = (typeof evt.metadata?.command === "string" ? evt.metadata.command : void 0) || evt.argumentsPreview || call?.argumentsPreview || "";
2137
+ const exitCode = typeof evt.metadata?.exitCode === "number" ? evt.metadata.exitCode : void 0;
2138
+ const timedOut = Boolean(evt.metadata?.timedOut);
2139
+ return { command: rawCommand, exitCode, timedOut };
2140
+ });
2141
+ for (const evt of runBuffer) pending.delete(evt.toolCallId);
2142
+ runBuffer = [];
2143
+ out += formatRunTreeBlockForCli(items, colorEnabled);
2144
+ }
2145
+ if (fetchBuffer.length > 0) {
2146
+ const items = fetchBuffer.map((evt) => {
2147
+ const call = pending.get(evt.toolCallId);
2148
+ const rawUrl = (typeof evt.metadata?.url === "string" ? evt.metadata.url : void 0) || evt.argumentsPreview || call?.argumentsPreview || "";
2149
+ return { url: rawUrl };
2150
+ });
2151
+ for (const evt of fetchBuffer) pending.delete(evt.toolCallId);
2152
+ fetchBuffer = [];
2153
+ out += formatFetchTreeBlockForCli(items, colorEnabled);
2154
+ }
2155
+ return out;
2156
+ };
2157
+ const formatter = (event) => {
2158
+ if (mode === "hide") return "";
2159
+ if (mode === "verbose") return formatVerboseToolEvent(event, colorEnabled);
2160
+ if (isReadToolName(event.toolName)) {
2161
+ if (event.type === "tool-call") {
2162
+ pending.set(event.toolCallId, {
2163
+ toolName: event.toolName,
2164
+ argumentsPreview: event.argumentsPreview
2165
+ });
2166
+ return "";
2167
+ }
2168
+ if (event.type === "tool-result") {
2169
+ readBuffer.push(event);
2170
+ return "";
2171
+ }
2172
+ }
2173
+ if (isSearchToolName(event.toolName)) {
2174
+ if (event.type === "tool-call") {
2175
+ pending.set(event.toolCallId, {
2176
+ toolName: event.toolName,
2177
+ argumentsPreview: event.argumentsPreview
2178
+ });
2179
+ return "";
2180
+ }
2181
+ if (event.type === "tool-result") {
2182
+ searchBuffer.push(event);
2183
+ return "";
2184
+ }
2185
+ }
2186
+ if (isRunToolName(event.toolName)) {
2187
+ if (event.type === "tool-result" && !isRunResultFoldable(event)) {
2188
+ const flushed2 = flushBuffers();
2189
+ const call2 = pending.get(event.toolCallId);
2190
+ pending.delete(event.toolCallId);
2191
+ return `${flushed2}${formatCompactToolLine(event, call2, colorEnabled)}`;
2192
+ }
2193
+ if (event.type === "tool-call") {
2194
+ pending.set(event.toolCallId, {
2195
+ toolName: event.toolName,
2196
+ argumentsPreview: event.argumentsPreview
2197
+ });
2198
+ return "";
2199
+ }
2200
+ if (event.type === "tool-result") {
2201
+ runBuffer.push(event);
2202
+ return "";
2203
+ }
2204
+ }
2205
+ if (isFetchToolName(event.toolName)) {
2206
+ if (event.type === "tool-call") {
2207
+ pending.set(event.toolCallId, {
2208
+ toolName: event.toolName,
2209
+ argumentsPreview: event.argumentsPreview
2210
+ });
2211
+ return "";
2212
+ }
2213
+ if (event.type === "tool-result") {
2214
+ fetchBuffer.push(event);
2215
+ return "";
2216
+ }
2217
+ }
2218
+ const flushed = flushBuffers();
2219
+ if (event.type === "tool-call") {
2220
+ pending.set(event.toolCallId, {
2221
+ toolName: event.toolName,
2222
+ argumentsPreview: event.argumentsPreview
2223
+ });
2224
+ return flushed;
2225
+ }
2226
+ const call = pending.get(event.toolCallId);
2227
+ pending.delete(event.toolCallId);
2228
+ return `${flushed}${formatCompactToolLine(event, call, colorEnabled)}`;
2229
+ };
2230
+ formatter.flush = flushBuffers;
2231
+ return formatter;
2232
+ }
2233
+ function createSseToolEventAdapter(onEvent) {
2234
+ let round = 0;
2235
+ let callIndex = 0;
2236
+ let pendingCalls = [];
2237
+ const emit = (event) => {
2238
+ onEvent?.(event);
2239
+ return event;
2240
+ };
2241
+ return {
2242
+ onToolStart(payload) {
2243
+ const calls = Array.isArray(payload) ? payload : payload?.calls ?? [];
2244
+ pendingCalls = [];
2245
+ const events = [];
2246
+ for (const name of calls) {
2247
+ callIndex++;
2248
+ const toolCallId = `sse-call-${callIndex}`;
2249
+ const toolName = name || "tool";
2250
+ pendingCalls.push({ toolCallId, toolName });
2251
+ const event = {
2252
+ type: "tool-call",
2253
+ toolCallId,
2254
+ toolName,
2255
+ round
2256
+ };
2257
+ events.push(emit(event));
2258
+ }
2259
+ return events;
2260
+ },
2261
+ onToolResult(payload) {
2262
+ const pending = payload.toolCallId ? pendingCalls.find((p) => p.toolCallId === payload.toolCallId) : pendingCalls.shift();
2263
+ if (pending && payload.toolCallId) {
2264
+ pendingCalls = pendingCalls.filter((p) => p.toolCallId !== payload.toolCallId);
2265
+ }
2266
+ const toolCallId = payload.toolCallId || pending?.toolCallId || `sse-call-${callIndex}`;
2267
+ const toolName = payload.toolName || pending?.toolName || "tool";
2268
+ const rawContent = typeof payload.content === "string" ? payload.content : "";
2269
+ const summary = rawContent ? clipCompactText(rawContent, 120, "\u2026") : void 0;
2270
+ const event = {
2271
+ type: "tool-result",
2272
+ toolCallId,
2273
+ toolName,
2274
+ summary,
2275
+ metadata: payload.metadata,
2276
+ round
2277
+ };
2278
+ return emit(event);
2279
+ },
2280
+ onToolEnd() {
2281
+ round++;
2282
+ pendingCalls = [];
2283
+ }
2284
+ };
2285
+ }
2286
+
2287
+ // packages/cli/client/agentRunSpinner.ts
2288
+ function formatElapsed(totalSeconds) {
2289
+ if (totalSeconds < 60) return `${totalSeconds}s`;
2290
+ const minutes = Math.floor(totalSeconds / 60);
2291
+ const seconds = totalSeconds % 60;
2292
+ if (minutes < 60) return `${minutes}m ${seconds}s`;
2293
+ const hours = Math.floor(minutes / 60);
2294
+ const mins = minutes % 60;
2295
+ return `${hours}h ${mins}m`;
2296
+ }
2297
+ var Spinner = class {
2298
+ constructor(output, text, silent = false) {
2299
+ this.output = output;
2300
+ this.text = text;
2301
+ this.silent = silent;
2302
+ const explicit = output.isTTY;
2303
+ this.isTTY = typeof explicit === "boolean" ? explicit : Boolean(process.stdout.isTTY);
2304
+ }
2305
+ timer = null;
2306
+ startTime = 0;
2307
+ frameIndex = 0;
2308
+ frames = ["\xB7", "~", "\u2248", "\u223F", "\u2248", "~"];
2309
+ isTTY;
2310
+ start() {
2311
+ if (this.silent) {
2312
+ this.startTime = Date.now();
2313
+ return;
2314
+ }
2315
+ if (!this.isTTY) {
2316
+ this.output.write(`
2317
+ ${this.text}
2318
+ `);
2319
+ return;
2320
+ }
2321
+ if (this.timer) {
2322
+ clearInterval(this.timer);
2323
+ this.timer = null;
2324
+ }
2325
+ this.frameIndex = 0;
2326
+ this.startTime = Date.now();
2327
+ this.output.write("\x1B[?25l");
2328
+ this.output.write(this.renderLine(this.frames[0]));
2329
+ this.timer = setInterval(() => {
2330
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
2331
+ this.output.write(`\r${this.renderLine(this.frames[this.frameIndex])}`);
2332
+ }, 80);
2333
+ }
2334
+ stop() {
2335
+ const wasActive = this.timer !== null;
2336
+ if (this.timer) {
2337
+ clearInterval(this.timer);
2338
+ this.timer = null;
2339
+ }
2340
+ if (this.silent) return;
2341
+ if (this.isTTY && wasActive) {
2342
+ this.output.write("\r\x1B[K");
2343
+ this.output.write("\x1B[?25h");
2344
+ }
2345
+ }
2346
+ show(text) {
2347
+ this.text = text;
2348
+ if (this.silent) {
2349
+ if (!this.startTime) {
2350
+ this.startTime = Date.now();
2351
+ }
2352
+ return;
2353
+ }
2354
+ if (!this.isTTY) return;
2355
+ if (!this.timer) {
2356
+ this.start();
2357
+ return;
2358
+ }
2359
+ this.frameIndex = 0;
2360
+ this.startTime = Date.now();
2361
+ this.output.write(`\r${this.renderLine(this.frames[0])}`);
2362
+ }
2363
+ renderLine(frame) {
2364
+ const elapsedSeconds = Math.max(
2365
+ 0,
2366
+ Math.floor((Date.now() - this.startTime) / 1e3)
2367
+ );
2368
+ return `${themeColorSequence("accent")}${frame}\x1B[39m ${this.text} (${formatElapsed(elapsedSeconds)})`;
2369
+ }
2370
+ };
2371
+
2372
+ // packages/cli/client/agentRunOutput.ts
2373
+ function formatAssistantResponseForCli(text, options) {
2374
+ const thinkingMode = resolveThinkingDisplayMode(options.env);
2375
+ return formatAssistantDisplay(
2376
+ formatAssistantTextForCli(text, thinkingMode)
2377
+ );
2378
+ }
2379
+ function resolveAgentEventMode(options) {
2380
+ if (options.eventsMode === "jsonl") return "jsonl";
2381
+ return options.env.NOLO_AGENT_EVENTS === "jsonl" ? "jsonl" : "text";
2382
+ }
2383
+ function formatToolJsonEvent(event) {
2384
+ return `${JSON.stringify({
2385
+ schemaVersion: 1,
2386
+ type: event.type,
2387
+ round: event.round + 1,
2388
+ tool: event.toolName,
2389
+ toolCallId: event.toolCallId,
2390
+ ...event.argumentsPreview ? { argsPreview: event.argumentsPreview } : {},
2391
+ ...typeof event.elapsedMs === "number" ? { elapsedMs: event.elapsedMs } : {},
2392
+ ...event.summary ? { summary: event.summary } : {},
2393
+ ...event.message ? { message: event.message } : {},
2394
+ ...event.metadata ? { metadata: event.metadata } : {}
2395
+ })}
2396
+ `;
2397
+ }
2398
+ function createCliTurnOutput(params) {
2399
+ const { options } = params;
2400
+ const workingLabel = params.workingLabel ?? `${options.agentName} -> working`;
2401
+ const spinner = params.spinner ?? new Spinner(options.output, workingLabel, Boolean(options.activityReporter));
2402
+ const toolDisplayMode = resolveToolDisplayMode(options.env);
2403
+ const traceLocalTools = shouldEmitToolEvents(toolDisplayMode);
2404
+ const formatToolEvent = createToolEventFormatter(toolDisplayMode);
2405
+ const eventMode = resolveAgentEventMode(options);
2406
+ let streamedAssistantText = false;
2407
+ let everStreamedAnyText = false;
2408
+ let printedAssistantLabel = false;
2409
+ const thinkingMode = resolveThinkingDisplayMode(options.env);
2410
+ const renderWriter = createRenderAwareStreamWriter({
2411
+ write: (chunk) => options.output.write(chunk)
2412
+ });
2413
+ const writeVisibleAssistantChunk = (chunk) => {
2414
+ if (!chunk) return;
2415
+ spinner.stop();
2416
+ options.activityReporter?.(null);
2417
+ if (!printedAssistantLabel) {
2418
+ options.output.write(`
2419
+ ${options.agentName} > `);
2420
+ printedAssistantLabel = true;
2421
+ }
2422
+ streamedAssistantText = true;
2423
+ everStreamedAnyText = true;
2424
+ renderWriter.push(chunk);
2425
+ };
2426
+ const thinkingFilter = createThinkingAwareStreamFilter(
2427
+ writeVisibleAssistantChunk,
2428
+ thinkingMode
2429
+ );
2430
+ const thinkingSink = createThinkingEventSink((chunk) => {
2431
+ spinner.stop();
2432
+ options.activityReporter?.(null);
2433
+ options.output.write(chunk);
2434
+ }, thinkingMode);
2435
+ const handleToolEvent = (event) => {
2436
+ if (!traceLocalTools) return;
2437
+ const chunk = eventMode === "jsonl" ? formatToolJsonEvent(event) : formatToolEvent(event);
2438
+ if (eventMode === "jsonl") {
2439
+ options.output.write(chunk);
2440
+ return;
2441
+ }
2442
+ if (event.type === "tool-call") {
2443
+ thinkingFilter.flush();
2444
+ renderWriter.flush();
2445
+ }
2446
+ spinner.stop();
2447
+ options.activityReporter?.(null);
2448
+ if (event.type === "tool-call" && streamedAssistantText) {
2449
+ options.output.write("\n");
2450
+ streamedAssistantText = false;
2451
+ }
2452
+ if (chunk) {
2453
+ options.output.write(chunk);
2454
+ }
2455
+ if (toolDisplayMode === "compact" && event.type === "tool-call") {
2456
+ const activeLabel = formatActiveToolLabel(event);
2457
+ spinner.show(activeLabel);
2458
+ options.activityReporter?.(activeLabel);
2459
+ }
2460
+ };
2461
+ return {
2462
+ spinner,
2463
+ thinkingMode,
2464
+ toolDisplayMode,
2465
+ traceLocalTools,
2466
+ eventMode,
2467
+ pushText(chunk) {
2468
+ thinkingFilter.push(chunk);
2469
+ },
2470
+ pushThinking(chunk) {
2471
+ thinkingSink.push(chunk);
2472
+ },
2473
+ handleToolEvent,
2474
+ showWorking(label) {
2475
+ const activeLabel = label ?? workingLabel;
2476
+ spinner.show(activeLabel);
2477
+ options.activityReporter?.(activeLabel);
2478
+ },
2479
+ finish(fallbackContent) {
2480
+ spinner.stop();
2481
+ options.activityReporter?.(null);
2482
+ const pendingToolOutput = formatToolEvent.flush ? formatToolEvent.flush() : "";
2483
+ if (pendingToolOutput) {
2484
+ options.output.write(pendingToolOutput);
2485
+ }
2486
+ if (streamedAssistantText) {
2487
+ thinkingFilter.flush();
2488
+ renderWriter.flush();
2489
+ options.output.write("\n");
2490
+ } else if (everStreamedAnyText) {
2491
+ thinkingFilter.flush();
2492
+ options.output.write("\n");
2493
+ } else {
2494
+ const content = fallbackContent ? formatAssistantResponseForCli(fallbackContent.trim(), options) : "";
2495
+ if (content) {
2496
+ options.output.write(`
2497
+ ${options.agentName} > ${content}
2498
+ `);
2499
+ } else {
2500
+ options.output.write(`
2501
+ ${options.agentName} > (no text response)
2502
+ `);
2503
+ }
2504
+ }
2505
+ }
2506
+ };
2507
+ }
2508
+
2509
+ // packages/cli/client/agentRunStream.ts
2510
+ async function readStreamingAgentRun(options, res, existingSpinner) {
2511
+ const reader = res.body?.getReader();
2512
+ if (!reader) {
2513
+ existingSpinner?.stop();
2514
+ options.output.write(
2515
+ "[nolo] Agent stream response did not include a readable body.\n"
2516
+ );
2517
+ return { exitCode: 1 };
2518
+ }
2519
+ options.abortSignal?.addEventListener(
2520
+ "abort",
2521
+ () => {
2522
+ reader.cancel("user-stop").catch(() => {
2523
+ });
2524
+ },
2525
+ { once: true }
2526
+ );
2527
+ const decoder = new TextDecoder();
2528
+ const turnOutput = createCliTurnOutput({
2529
+ options,
2530
+ workingLabel: `${options.agentName} -> working`,
2531
+ spinner: existingSpinner
2532
+ });
2533
+ const sseAdapter = createSseToolEventAdapter((evt) => {
2534
+ turnOutput.handleToolEvent(evt);
2535
+ });
2536
+ let buffer = "";
2537
+ let content = "";
2538
+ let dialogId;
2539
+ let usage;
2540
+ const handlePayload = (payload) => {
2541
+ if (typeof payload?.dialogId === "string" && payload.dialogId.trim()) {
2542
+ dialogId = payload.dialogId;
2543
+ }
2544
+ if (payload?.error || payload?.type === "error") {
2545
+ throw new Error(
2546
+ String(payload.error || payload.message || "Agent stream failed")
2547
+ );
2548
+ }
2549
+ if (payload?.type === "done") {
2550
+ usage = payload.usage;
2551
+ return;
2552
+ }
2553
+ if (payload?.type === "dialog" || payload?.type === "status") {
2554
+ return;
2555
+ }
2556
+ if (payload?.type === "turn_warning") {
2557
+ return;
2558
+ }
2559
+ if (payload?.type === "thinking") {
2560
+ const thinkChunk = typeof payload.content === "string" ? payload.content : typeof payload.chunk === "string" ? payload.chunk : "";
2561
+ if (thinkChunk) {
2562
+ turnOutput.pushThinking(thinkChunk);
2563
+ }
2564
+ return;
2565
+ }
2566
+ if (payload?.type === "tool_start") {
2567
+ sseAdapter.onToolStart(payload.calls ?? payload);
2568
+ return;
2569
+ }
2570
+ if (payload?.type === "tool_result") {
2571
+ sseAdapter.onToolResult(payload);
2572
+ return;
2573
+ }
2574
+ if (payload?.type === "tool_end") {
2575
+ sseAdapter.onToolEnd();
2576
+ turnOutput.showWorking();
2577
+ return;
2578
+ }
2579
+ const chunk = payload?.type === "text" ? payload.content : typeof payload?.chunk === "string" ? payload.chunk : "";
2580
+ if (!chunk) return;
2581
+ content += chunk;
2582
+ turnOutput.pushText(chunk);
2583
+ };
2584
+ try {
2585
+ while (true) {
2586
+ const { done, value } = await reader.read();
2587
+ if (done) break;
2588
+ buffer += decoder.decode(value, { stream: true });
2589
+ const events = buffer.split("\n\n");
2590
+ buffer = events.pop() ?? "";
2591
+ for (const event of events) {
2592
+ const dataLines = event.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim()).filter(Boolean);
2593
+ for (const raw of dataLines) {
2594
+ handlePayload(JSON.parse(raw));
2595
+ }
2596
+ }
2597
+ }
2598
+ if (buffer.trim()) {
2599
+ const raw = buffer.split("\n").find((line) => line.startsWith("data:"))?.slice(5).trim();
2600
+ if (raw) handlePayload(JSON.parse(raw));
2601
+ }
2602
+ } catch (error) {
2603
+ turnOutput.spinner.stop();
2604
+ if (options.abortSignal?.aborted) {
2605
+ return {
2606
+ exitCode: 0,
2607
+ ...dialogId ? { dialogId } : {},
2608
+ streamInterrupted: true
2609
+ };
2610
+ }
2611
+ const message = toErrorMessage(error);
2612
+ if (dialogId) {
2613
+ options.output.write(
2614
+ `
2615
+ [nolo] Agent stream transport interrupted after dialog ${dialogId} was created: ${message}
2616
+ `
2617
+ );
2618
+ options.output.write(
2619
+ "[nolo] The agent run may still finish on the server; read the dialog before retrying.\n"
2620
+ );
2621
+ return { exitCode: 0, dialogId, streamInterrupted: true };
2622
+ }
2623
+ options.output.write(`
2624
+ [nolo] Agent stream failed: ${message}
2625
+ `);
2626
+ return { exitCode: 1 };
2627
+ }
2628
+ turnOutput.finish(content);
2629
+ const usageText = formatUsage(usage, dialogId);
2630
+ if (usageText && shouldShowUsage(options.env))
2631
+ options.output.write(`${usageText}
2632
+ `);
2633
+ return {
2634
+ exitCode: 0,
2635
+ ...dialogId ? { dialogId } : {},
2636
+ turnTokens: buildTurnTokenUsage(usage, options.agentKey)
2637
+ };
2638
+ }
2639
+
2640
+ // packages/cli/client/agentRunTypes.ts
2641
+ function resolveAuthToken2(env) {
2642
+ return env.AUTH_TOKEN || env.AUTH || env.BENCHMARK_AUTH_TOKEN || "";
2643
+ }
2644
+ function isMachineBoundLocalhostCustomProvider(agentConfig) {
2645
+ const machineId = agentConfig?.runtimeBinding && typeof agentConfig.runtimeBinding === "object" ? String(agentConfig.runtimeBinding.machineId ?? "").trim() : "";
2646
+ const providerUrl = asTrimmedString(agentConfig?.customProviderUrl);
2647
+ if (!machineId || !providerUrl) return false;
2648
+ return isLoopbackUrl(providerUrl);
2649
+ }
2650
+ function resolveBoundMachineId(agentConfig) {
2651
+ return agentConfig?.runtimeBinding && typeof agentConfig.runtimeBinding === "object" ? String(agentConfig.runtimeBinding.machineId ?? "").trim() : "";
2652
+ }
2653
+ var CLI_PROVIDER_NAMES = /* @__PURE__ */ new Set([
2654
+ "agy",
2655
+ "claude",
2656
+ "codex",
2657
+ "copilot",
2658
+ "gemini",
2659
+ "grok",
2660
+ "kimi",
2661
+ "opencode",
2662
+ "qoder"
2663
+ ]);
2664
+ function isCliProviderAgentConfig(agentConfig) {
2665
+ const cliProvider = asTrimmedLowercaseString(agentConfig?.cliProvider);
2666
+ const provider = asTrimmedLowercaseString(agentConfig?.provider);
2667
+ return Boolean(cliProvider) || CLI_PROVIDER_NAMES.has(provider);
2668
+ }
2669
+ async function detectCurrentMachineId(env) {
2670
+ const fromEnv = (env.NOLO_CURRENT_MACHINE_ID || env.NOLO_MACHINE_ID || "").trim();
2671
+ if (fromEnv) return fromEnv;
2672
+ try {
2673
+ const { detectMachineInfo } = await import("./machineInfo-M6KB6HZN.js");
2674
+ const machine = await detectMachineInfo({ probeLaunchable: true });
2675
+ return asOptionalTrimmedString(machine?.machineId);
2676
+ } catch {
2677
+ return void 0;
2678
+ }
2679
+ }
2680
+
2681
+ // packages/cli/client/agentRunPlatformTools.ts
2682
+ var SERVER_PLATFORM_TOOL_NAMES = /* @__PURE__ */ new Set([
2683
+ "addTableRow",
2684
+ "addTableRows",
2685
+ "deleteTableRow",
2686
+ "deleteTableRows",
2687
+ "updateTableRow",
2688
+ "updateTableRows"
2689
+ ]);
2690
+ var KNOWN_SERVER_PLATFORM_AGENT_KEYS = /* @__PURE__ */ new Set([NOLO_PROJECT_MANAGER_AGENT_KEY]);
2691
+ var KNOWN_SERVER_PLATFORM_AGENT_ALIASES = /* @__PURE__ */ new Set([
2692
+ "code-review",
2693
+ "frontend",
2694
+ "frontend-agent",
2695
+ "frontend-implementer",
2696
+ "full-stack",
2697
+ "fullstack",
2698
+ "nolo code review",
2699
+ "nolo fullstack",
2700
+ "nolo project manager",
2701
+ "nolo reviewer",
2702
+ "nolo-code-review",
2703
+ "nolo-fullstack",
2704
+ "nolo-pm",
2705
+ "nolo-project-manager",
2706
+ "nolo-reviewer",
2707
+ "pm",
2708
+ "project-manager",
2709
+ "review",
2710
+ "reviewer"
2711
+ ]);
2712
+ function findServerPlatformTools(toolNames) {
2713
+ if (!Array.isArray(toolNames)) return [];
2714
+ return toolNames.filter(
2715
+ (toolName) => SERVER_PLATFORM_TOOL_NAMES.has(toolName)
2716
+ );
2717
+ }
2718
+ function resolveServerPlatformToolNames(agentConfig) {
2719
+ return findServerPlatformTools([
2720
+ ...Array.isArray(agentConfig?.toolNames) ? agentConfig.toolNames : [],
2721
+ ...Array.isArray(agentConfig?.runtimeToolPolicy?.agentTools) ? agentConfig.runtimeToolPolicy.agentTools : []
2722
+ ]);
2723
+ }
2724
+ function isKnownServerPlatformAgent(options) {
2725
+ if (KNOWN_SERVER_PLATFORM_AGENT_KEYS.has(options.agentKey)) return true;
2726
+ const normalizedKey = normalizeAgentHandle(options.agentKey);
2727
+ return Boolean(
2728
+ normalizedKey && KNOWN_SERVER_PLATFORM_AGENT_ALIASES.has(normalizedKey)
2729
+ );
2730
+ }
2731
+
2732
+ // packages/cli/client/agentRun.ts
2733
+ var import_ulid = __toESM(require_index_umd(), 1);
2734
+ async function loadRunLocalAgentTurn() {
2735
+ const { runLocalAgentTurn } = await import("./agentRuntimeLocal-6PIWAHLQ.js");
2736
+ return runLocalAgentTurn;
2737
+ }
2738
+ async function resolveCurrentMachineId(options) {
2739
+ return options.currentMachineIdResolver ? options.currentMachineIdResolver(options.env) : detectCurrentMachineId(options.env);
2740
+ }
2741
+ function resolveRequestedRuntimeMode(options) {
2742
+ const envMode = options.env.NOLO_RUNTIME_MODE;
2743
+ if (options.runtimeMode) return options.runtimeMode;
2744
+ if (envMode === "local" || envMode === "server" || envMode === "auto")
2745
+ return envMode;
2746
+ return "auto";
2747
+ }
2748
+ function buildDefaultLocalRuntimeAdapter(options) {
2749
+ return createCliLocalRuntimeAdapter({
2750
+ env: options.env,
2751
+ fetchImpl: options.fetchImpl,
2752
+ cwd: options.localRuntimeCwd,
2753
+ output: options.output,
2754
+ ...options.confirmDestructiveAction ? { confirmDestructiveAction: options.confirmDestructiveAction } : {},
2755
+ ...options.requestUserChoice ? { requestUserChoice: options.requestUserChoice } : {}
2756
+ });
2757
+ }
2758
+ function resolveLocalRuntimeAdapter(options) {
2759
+ return options.localRuntimeAdapter || options.localRuntimeAdapterFactory?.(options.env, {
2760
+ cwd: options.localRuntimeCwd
2761
+ }) || buildDefaultLocalRuntimeAdapter(options);
2762
+ }
2763
+ function wrapAdapterEphemeral(adapter) {
2764
+ return {
2765
+ ...adapter,
2766
+ loadDialogHistory: async () => [],
2767
+ saveTurn: async (input) => ({
2768
+ dialogId: input.continueDialogId ?? "ephemeral"
2769
+ })
2770
+ };
2771
+ }
2772
+ function applyEphemeralIfRequested(options, adapter) {
2773
+ return options.ephemeral ? wrapAdapterEphemeral(adapter) : adapter;
2774
+ }
2775
+ function wrapLoadAgentConfigWithModelOverride(adapter, targetAgentKey, override) {
2776
+ return {
2777
+ ...adapter,
2778
+ loadAgentConfig: async (agentRef) => {
2779
+ const config = await adapter.loadAgentConfig(agentRef);
2780
+ if (!config || agentRef !== targetAgentKey) return config;
2781
+ const baseRecord = config.rawRecord ?? config;
2782
+ return resolveAgentRuntimeConfigFromRecord(
2783
+ agentRef,
2784
+ applyModelLayerOverride(baseRecord, override)
2785
+ );
2786
+ }
2787
+ };
2788
+ }
2789
+ async function shouldSkipAutoLocalForServerPlatformTools(options) {
2790
+ if (isBuiltinNoloAgentRef(options.agentKey)) return false;
2791
+ if (options.localRuntimeCwd) {
2792
+ return false;
2793
+ }
2794
+ const knownServerPlatformAgent = isKnownServerPlatformAgent(options);
2795
+ const adapter = resolveLocalRuntimeAdapter(options);
2796
+ if (!adapter) return knownServerPlatformAgent;
2797
+ let agentConfig;
2798
+ try {
2799
+ agentConfig = await adapter.loadAgentConfig(options.agentKey);
2800
+ } catch {
2801
+ if (knownServerPlatformAgent) {
2802
+ options.output.write(
2803
+ `[nolo] auto runtime: skipping local runtime because ${options.agentKey} is a known platform agent. Use --local explicitly to force local workspace tools.
2804
+ `
2805
+ );
2806
+ return true;
2807
+ }
2808
+ return false;
2809
+ }
2810
+ if (isCliProviderAgentConfig(agentConfig)) {
2811
+ const boundMachineId = resolveBoundMachineId(agentConfig);
2812
+ if (!boundMachineId) return false;
2813
+ const currentMachineId = (await resolveCurrentMachineId(options))?.trim() || "";
2814
+ if (currentMachineId && currentMachineId === boundMachineId) return false;
2815
+ options.output.write(
2816
+ `[nolo] auto runtime: skipping local runtime because ${options.agentKey} is bound to ${boundMachineId}` + (currentMachineId ? ` and this machine is ${currentMachineId}.` : ".") + " Use --local explicitly to force the current machine.\n"
2817
+ );
2818
+ return true;
2819
+ }
2820
+ if (knownServerPlatformAgent) {
2821
+ options.output.write(
2822
+ `[nolo] auto runtime: skipping local runtime because ${options.agentKey} is a known platform agent. Use --local explicitly to force local workspace tools.
2823
+ `
2824
+ );
2825
+ return true;
2826
+ }
2827
+ if (isMachineBoundLocalhostCustomProvider(agentConfig)) {
2828
+ options.output.write(
2829
+ `[nolo] auto runtime: skipping local runtime because ${options.agentKey} is a machine-bound localhost custom provider. Use --local explicitly to force the current machine.
2830
+ `
2831
+ );
2832
+ return true;
2833
+ }
2834
+ const serverTools = resolveServerPlatformToolNames(agentConfig);
2835
+ if (serverTools.length === 0) return false;
2836
+ options.output.write(
2837
+ `[nolo] auto runtime: skipping local runtime because ${options.agentKey} declares server platform tools (${serverTools.join(", ")}). Use --local explicitly to force local workspace tools.
2838
+ `
2839
+ );
2840
+ return true;
2841
+ }
2842
+ function buildUserInputContent(message, imageUrls = []) {
2843
+ if (imageUrls.length === 0) return message;
2844
+ return [
2845
+ ...message.trim() ? [{ type: "text", text: message }] : [],
2846
+ ...imageUrls.map((url) => ({
2847
+ type: "image_url",
2848
+ image_url: { url }
2849
+ }))
2850
+ ];
2851
+ }
2852
+ function buildSubjectRefs(options) {
2853
+ const refs = [];
2854
+ const seen = /* @__PURE__ */ new Set();
2855
+ const pushRef = (ref) => {
2856
+ const kind = ref.kind.trim();
2857
+ const id = ref.id.trim();
2858
+ const role = ref.role?.trim();
2859
+ if (!kind || !id) return;
2860
+ const key = `${kind}\0${id}\0${role ?? ""}`;
2861
+ if (seen.has(key)) return;
2862
+ seen.add(key);
2863
+ refs.push({ kind, id, ...role ? { role } : {} });
2864
+ };
2865
+ for (const ref of options.subjectRefs ?? []) pushRef(ref);
2866
+ if (options.subjectDialogKey) {
2867
+ pushRef({
2868
+ kind: "dialog",
2869
+ id: options.subjectDialogKey,
2870
+ role: "subject"
2871
+ });
2872
+ }
2873
+ if (options.taskEvidence?.rowDbKey) {
2874
+ pushRef({
2875
+ kind: "table-row",
2876
+ id: options.taskEvidence.rowDbKey,
2877
+ role: "task"
2878
+ });
2879
+ }
2880
+ for (const artifactId of options.taskEvidence?.artifactIds ?? []) {
2881
+ pushRef({
2882
+ kind: "artifact",
2883
+ id: artifactId,
2884
+ role: "evidence"
2885
+ });
2886
+ }
2887
+ return refs.length ? refs : void 0;
2888
+ }
2889
+ function isMissingLocalAgentConfigError(error, agentRef) {
2890
+ return Boolean(
2891
+ error && typeof error === "object" && "code" in error && error.code === LOCAL_AGENT_CONFIG_MISSING_CODE && error.agentRef === agentRef
2892
+ );
2893
+ }
2894
+ function shouldAttemptAutoLocal(options) {
2895
+ if (options.localRuntimeAdapter || options.localRuntimeAdapterFactory)
2896
+ return true;
2897
+ if (options.env.NOLO_DISABLE_CLI_WORKSPACE_TOOLS !== "1" && isBuiltinNoloAgentRef(options.agentKey) && resolveAuthToken2(options.env)) {
2898
+ return true;
2899
+ }
2900
+ if (options.env.NOLO_DISABLE_CLI_WORKSPACE_TOOLS !== "1" && resolveAuthToken2(options.env) && !isKnownServerPlatformAgent(options)) {
2901
+ return true;
2902
+ }
2903
+ return Boolean(
2904
+ options.env.NOLO_LOCAL_OPENAI_API_KEY || options.env.OPENAI_API_KEY || options.env.NOLO_LOCAL_OPENAI_BASE_URL || options.env.OPENAI_BASE_URL || options.env.NOLO_LOCAL_AGENT_KEY
2905
+ );
2906
+ }
2907
+ function buildTransportErrorHint(serverUrl, error) {
2908
+ const endpoint = `${serverUrl}/api/agent/run`;
2909
+ const reason = toErrorMessage(error);
2910
+ let detail = `[nolo] Could not reach ${endpoint}.
2911
+ Reason: ${reason}
2912
+ `;
2913
+ try {
2914
+ const parsed = new URL(serverUrl);
2915
+ if (parsed.hostname === "127.0.0.1" || parsed.hostname === "localhost") {
2916
+ detail += "If you meant local dev, start the local API first.\nOtherwise set NOLO_SERVER to a reachable server, or re-run `nolo login --server https://nolo.chat`.\n";
2917
+ return detail;
2918
+ }
2919
+ } catch {
2920
+ }
2921
+ detail += "Check NOLO_SERVER / BASE_URL and make sure the configured server is reachable.\n";
2922
+ return detail;
2923
+ }
2924
+ async function readAgentRunFailureMetadata(res) {
2925
+ const data = await res.clone().json().catch(() => ({}));
2926
+ return {
2927
+ ...asOptionalTrimmedString(data?.dialogId) ? { dialogId: asOptionalTrimmedString(data?.dialogId) } : {}
2928
+ };
2929
+ }
2930
+ async function runHttpAgentTurn(options, authToken) {
2931
+ const spinner = new Spinner(
2932
+ options.output,
2933
+ `${options.agentName} -> working`,
2934
+ // 与 createCliTurnOutput 的兜底构造一致:传入 activityReporter 时
2935
+ // Spinner 静默,避免 TUI docked 活动行与 spinner 帧重复 live 指示。
2936
+ Boolean(options.activityReporter)
2937
+ );
2938
+ spinner.start();
2939
+ const fetchImpl = options.fetchImpl ?? fetch;
2940
+ const subjectRefs = buildSubjectRefs(options);
2941
+ const allowedChildAgentKeys = options.allowedChildAgentKeys?.filter(
2942
+ (key) => key.trim()
2943
+ );
2944
+ const allowedToolNames = options.allowedToolNames?.filter(
2945
+ (name) => name.trim()
2946
+ );
2947
+ const blockedToolNames = options.blockedToolNames?.filter(
2948
+ (name) => name.trim()
2949
+ );
2950
+ const shouldStream = !options.noStream && !options.background;
2951
+ const buildRequestBody = (stream) => JSON.stringify({
2952
+ agentKey: options.agentKey,
2953
+ userInput: buildUserInputContent(
2954
+ options.extraContextBlocks?.length ? [...options.extraContextBlocks, "", options.message].join("\n") : options.message,
2955
+ options.imageUrls
2956
+ ),
2957
+ runtimeContext: {
2958
+ surface: "cli",
2959
+ host: "terminal",
2960
+ runtime: "bun",
2961
+ entrypoint: "nolo-cli",
2962
+ capabilities: ["text-io", "streaming", "slash-commands"],
2963
+ ...subjectRefs ? { subjectRefs } : {},
2964
+ ...allowedChildAgentKeys?.length ? { allowedChildAgentKeys } : {},
2965
+ ...blockedToolNames?.length ? { blockedToolNames } : {},
2966
+ ...allowedToolNames?.length ? { allowedToolNames } : {}
2967
+ },
2968
+ ...options.continueDialogId ? { continueDialogId: options.continueDialogId } : {},
2969
+ ...options.spaceId ? { spaceId: options.spaceId } : {},
2970
+ ...options.category ? { category: options.category } : {},
2971
+ ...options.inheritedFromDialogKey ? { inheritedFromDialogKey: options.inheritedFromDialogKey } : {},
2972
+ ...options.parentDialogId ? { parentDialogId: options.parentDialogId } : {},
2973
+ ...options.background ? { background: true } : {},
2974
+ ...typeof options.timeoutMs === "number" ? { timeoutMs: options.timeoutMs } : {},
2975
+ ...options.modelOverride ? { runtimeOptions: { quickChatModelOverride: options.modelOverride } } : {},
2976
+ stream
2977
+ });
2978
+ const postAgentRun = (stream) => fetchImpl(`${options.serverUrl}/api/agent/run`, {
2979
+ method: "POST",
2980
+ headers: {
2981
+ Authorization: `Bearer ${authToken}`,
2982
+ "Content-Type": "application/json"
2983
+ },
2984
+ body: buildRequestBody(stream),
2985
+ ...options.abortSignal ? { signal: options.abortSignal } : {}
2986
+ });
2987
+ let res;
2988
+ try {
2989
+ res = await postAgentRun(shouldStream);
2990
+ } catch (error) {
2991
+ spinner.stop();
2992
+ if (options.abortSignal?.aborted) {
2993
+ return { exitCode: 0, streamInterrupted: true };
2994
+ }
2995
+ options.output.write(buildTransportErrorHint(options.serverUrl, error));
2996
+ return { exitCode: 1 };
2997
+ }
2998
+ if (shouldStream && isGatewayHttpStatus(res.status)) {
2999
+ const failureMeta = await readAgentRunFailureMetadata(res);
3000
+ if (!failureMeta.dialogId) {
3001
+ spinner.stop();
3002
+ options.output.write(
3003
+ `[nolo] streaming request returned HTTP ${res.status}; retrying once without streaming.
3004
+ `
3005
+ );
3006
+ spinner.start();
3007
+ try {
3008
+ res = await postAgentRun(false);
3009
+ } catch (error) {
3010
+ spinner.stop();
3011
+ options.output.write(buildTransportErrorHint(options.serverUrl, error));
3012
+ return { exitCode: 1 };
3013
+ }
3014
+ }
3015
+ }
3016
+ const contentType = res.headers.get("content-type") || "";
3017
+ if (contentType.includes("text/event-stream") && res.body) {
3018
+ const result = await readStreamingAgentRun(options, res, spinner);
3019
+ return result;
3020
+ }
3021
+ spinner.stop();
3022
+ let data = {};
3023
+ try {
3024
+ data = await res.json();
3025
+ } catch {
3026
+ data = {};
3027
+ }
3028
+ if (!res.ok) {
3029
+ options.output.write(`[nolo] Agent request failed: HTTP ${res.status}
3030
+ `);
3031
+ const errorText = asTrimmedString(data?.error);
3032
+ const messageText = asTrimmedString(data?.message);
3033
+ const reasonText = asTrimmedString(data?.reason);
3034
+ const codeText = asTrimmedString(data?.code);
3035
+ const dialogIdText = asTrimmedString(data?.dialogId);
3036
+ if (errorText || messageText) {
3037
+ options.output.write(`${errorText || messageText}
3038
+ `);
3039
+ if (messageText && messageText !== errorText) {
3040
+ options.output.write(`${messageText}
3041
+ `);
3042
+ }
3043
+ if (codeText && codeText !== errorText && codeText !== messageText) {
3044
+ options.output.write(`code=${codeText}
3045
+ `);
3046
+ }
3047
+ if (reasonText && reasonText !== errorText && reasonText !== messageText) {
3048
+ options.output.write(`reason=${reasonText}
3049
+ `);
3050
+ }
3051
+ }
3052
+ if (dialogIdText) {
3053
+ options.output.write(`[nolo] failed dialog: ${dialogIdText}
3054
+ `);
3055
+ options.output.write(
3056
+ `[nolo] continue with: nolo agent run ${options.agentKey} --continue ${dialogIdText} --msg "retry"
3057
+ `
3058
+ );
3059
+ }
3060
+ return dialogIdText ? { exitCode: 1, dialogId: dialogIdText } : { exitCode: 1 };
3061
+ }
3062
+ const content = formatAssistantResponseForCli(
3063
+ String(data?.content ?? data?.message ?? ""),
3064
+ options
3065
+ );
3066
+ if (content) {
3067
+ options.output.write(`
3068
+ ${options.agentName} > ${content}
3069
+ `);
3070
+ } else {
3071
+ options.output.write(`
3072
+ ${options.agentName} > (no text response)
3073
+ `);
3074
+ }
3075
+ const usage = formatUsage(data?.usage, data?.dialogId);
3076
+ if (usage && shouldShowUsage(options.env)) options.output.write(`${usage}
3077
+ `);
3078
+ return {
3079
+ exitCode: 0,
3080
+ ...typeof data?.dialogId === "string" && data.dialogId ? { dialogId: data.dialogId } : {},
3081
+ turnTokens: buildTurnTokenUsage(
3082
+ data?.usage,
3083
+ typeof data?.model === "string" ? data.model : options.agentKey
3084
+ )
3085
+ };
3086
+ }
3087
+ async function runInjectedLocalAgentTurn(options) {
3088
+ return runLocalAgentTurnForCli(options, { reportFailure: true });
3089
+ }
3090
+ async function refreshMissingLocalAgentConfig(options) {
3091
+ const adapter = resolveLocalRuntimeAdapter(options);
3092
+ if (!adapter) return false;
3093
+ const agentConfig = await adapter.loadAgentConfig(options.agentKey);
3094
+ return Boolean(agentConfig);
3095
+ }
3096
+ async function runLocalAgentTurnForCli(options, settings) {
3097
+ const resolvedBaseAdapter = resolveLocalRuntimeAdapter(options);
3098
+ const baseAdapter = (() => {
3099
+ let adapter2 = resolvedBaseAdapter && options.modelOverride ? wrapLoadAgentConfigWithModelOverride(
3100
+ resolvedBaseAdapter,
3101
+ options.agentKey,
3102
+ options.modelOverride
3103
+ ) : resolvedBaseAdapter;
3104
+ if (adapter2) adapter2 = applyEphemeralIfRequested(options, adapter2);
3105
+ return adapter2;
3106
+ })();
3107
+ if (!baseAdapter) {
3108
+ options.output.write(
3109
+ "[nolo] Local runtime was requested but no local runtime adapter is available.\n"
3110
+ );
3111
+ return { exitCode: 1 };
3112
+ }
3113
+ const subjectRefs = buildSubjectRefs(options);
3114
+ const allowedChildAgentKeys = options.allowedChildAgentKeys?.filter(
3115
+ (key) => key.trim()
3116
+ );
3117
+ const allowedToolNames = options.allowedToolNames?.filter(
3118
+ (name) => name.trim()
3119
+ );
3120
+ const blockedToolNames = options.blockedToolNames?.filter(
3121
+ (name) => name.trim()
3122
+ );
3123
+ const runtimeContext = subjectRefs || allowedChildAgentKeys?.length || allowedToolNames?.length || blockedToolNames?.length || options.parentWakeOnTerminal ? {
3124
+ ...subjectRefs ? { subjectRefs } : {},
3125
+ ...allowedChildAgentKeys?.length ? { allowedChildAgentKeys } : {},
3126
+ ...allowedToolNames?.length ? { allowedToolNames } : {},
3127
+ ...blockedToolNames?.length ? { blockedToolNames } : {},
3128
+ ...options.parentWakeOnTerminal ? { parentWakeOnTerminal: true } : {},
3129
+ ...options.parentDialogId ? { parentThreadId: options.parentDialogId } : {}
3130
+ } : void 0;
3131
+ const currentDialogId = options.continueDialogId ?? (0, import_ulid.ulid)();
3132
+ const runChildTurn = async (input) => {
3133
+ const { runLocalAgentTurn } = await import("./agentRuntimeLocal-6PIWAHLQ.js");
3134
+ return runLocalAgentTurn(input);
3135
+ };
3136
+ const createFreshChildBaseAdapter = () => {
3137
+ const fresh = options.localRuntimeAdapterFactory?.(options.env, {
3138
+ cwd: options.localRuntimeCwd
3139
+ }) ?? buildDefaultLocalRuntimeAdapter(options);
3140
+ return applyEphemeralIfRequested(options, fresh);
3141
+ };
3142
+ const withLocalDelegation = (args) => {
3143
+ let adapter2;
3144
+ const callAgentExecutor = createCliCallAgentToolExecutor(
3145
+ {
3146
+ env: options.env,
3147
+ fetchImpl: options.fetchImpl,
3148
+ cwd: options.localRuntimeCwd,
3149
+ output: options.output
3150
+ },
3151
+ {
3152
+ createChildAdapter: (child) => withLocalDelegation({
3153
+ base: createFreshChildBaseAdapter(),
3154
+ dialogId: child.dialogId,
3155
+ spaceId: child.spaceId,
3156
+ runtimeContext: child.runtimeContext
3157
+ }),
3158
+ runChildTurn,
3159
+ dialogId: args.dialogId,
3160
+ spaceId: args.spaceId,
3161
+ runtimeContext: args.runtimeContext
3162
+ }
3163
+ );
3164
+ adapter2 = {
3165
+ ...args.base,
3166
+ executeTool: async (call) => {
3167
+ if (call.name === "callAgent") {
3168
+ return callAgentExecutor(call);
3169
+ }
3170
+ return args.base.executeTool(call);
3171
+ }
3172
+ };
3173
+ return adapter2;
3174
+ };
3175
+ const adapter = withLocalDelegation({
3176
+ base: baseAdapter,
3177
+ dialogId: currentDialogId,
3178
+ spaceId: options.spaceId,
3179
+ runtimeContext
3180
+ });
3181
+ const workingLabel = `${options.agentName} -> working locally`;
3182
+ const turnOutput = createCliTurnOutput({
3183
+ options,
3184
+ workingLabel
3185
+ });
3186
+ turnOutput.spinner.start();
3187
+ try {
3188
+ const runLocalAgentTurn = await loadRunLocalAgentTurn();
3189
+ const result = await runLocalAgentTurn({
3190
+ adapter,
3191
+ agentRef: options.agentKey,
3192
+ input: buildUserInputContent(options.message, options.imageUrls),
3193
+ continueDialogId: currentDialogId,
3194
+ spaceId: options.spaceId,
3195
+ category: options.category,
3196
+ inheritedFromDialogKey: options.inheritedFromDialogKey,
3197
+ parentDialogId: options.parentDialogId,
3198
+ background: options.background,
3199
+ noStream: options.noStream,
3200
+ ...runtimeContext ? { runtimeContext } : {},
3201
+ ...options.extraContextBlocks?.length ? { contextBlocks: options.extraContextBlocks } : {},
3202
+ ...typeof options.timeoutMs === "number" ? { timeoutMs: options.timeoutMs } : {},
3203
+ ...options.actionGateHandler ? { onActionGate: options.actionGateHandler } : {},
3204
+ onLoopEvent: (event) => {
3205
+ if (event.kind === "llm-start") {
3206
+ turnOutput.showWorking();
3207
+ }
3208
+ if (event.kind === "image-downgraded") {
3209
+ options.output.write(
3210
+ "[nolo] \u5F53\u524D agent \u4E0D\u652F\u6301\u56FE\u7247\u8F93\u5165\uFF0C\u5DF2\u7528\u5360\u4F4D\u6587\u672C\u66FF\u4EE3\u3002\u8981\u5B8C\u6574\u56FE\u7247\u7406\u89E3\u53EF /switch \u5230 Kimi K2.6\u3002\n"
3211
+ );
3212
+ }
3213
+ options.onLoopEvent?.(event);
3214
+ },
3215
+ ...options.abortSignal ? { abortSignal: options.abortSignal } : {},
3216
+ ...turnOutput.traceLocalTools ? {
3217
+ onToolEvent: (event) => {
3218
+ turnOutput.handleToolEvent(event);
3219
+ }
3220
+ } : {},
3221
+ ...!options.noStream ? {
3222
+ onTextDelta: (chunk) => {
3223
+ turnOutput.pushText(chunk);
3224
+ }
3225
+ } : {}
3226
+ });
3227
+ turnOutput.finish(result.content);
3228
+ return {
3229
+ exitCode: 0,
3230
+ dialogId: result.dialogId,
3231
+ turnTokens: buildTurnTokenUsage(result.usage, result.model)
3232
+ };
3233
+ } catch (error) {
3234
+ turnOutput.spinner.stop();
3235
+ if (error?.code === LOCAL_TURN_ABORTED_CODE || options.abortSignal?.aborted) {
3236
+ const stoppedDialogId = error?.dialogId;
3237
+ return {
3238
+ exitCode: 0,
3239
+ streamInterrupted: true,
3240
+ ...stoppedDialogId ? { dialogId: stoppedDialogId } : {}
3241
+ };
3242
+ }
3243
+ if (settings.reportFailure) {
3244
+ options.output.write(
3245
+ `[nolo] Local agent run failed: ${toErrorMessage(error)}
3246
+ `
3247
+ );
3248
+ }
3249
+ return { exitCode: 1, localError: error };
3250
+ }
3251
+ }
3252
+ async function runAgentTurn(options) {
3253
+ const authToken = resolveAuthToken2(options.env);
3254
+ const runtimeMode = resolveRequestedRuntimeMode(options);
3255
+ if (runtimeMode === "local") {
3256
+ return runInjectedLocalAgentTurn(options);
3257
+ }
3258
+ if (runtimeMode === "auto" && shouldAttemptAutoLocal(options)) {
3259
+ const skipLocal = await shouldSkipAutoLocalForServerPlatformTools(options);
3260
+ if (!skipLocal) {
3261
+ const localResult = await runLocalAgentTurnForCli(options, {
3262
+ reportFailure: false
3263
+ });
3264
+ if (localResult.exitCode === 0) {
3265
+ return {
3266
+ exitCode: localResult.exitCode,
3267
+ ...localResult.dialogId ? { dialogId: localResult.dialogId } : {},
3268
+ ...localResult.turnTokens ? { turnTokens: localResult.turnTokens } : {},
3269
+ ...localResult.streamInterrupted ? { streamInterrupted: localResult.streamInterrupted } : {}
3270
+ };
3271
+ }
3272
+ if (isMissingLocalAgentConfigError(localResult.localError, options.agentKey)) {
3273
+ options.output.write(
3274
+ `[nolo] Local agent config was missing; refreshing local config and retrying local once.
3275
+ `
3276
+ );
3277
+ try {
3278
+ const refreshed = await refreshMissingLocalAgentConfig(options);
3279
+ if (refreshed) {
3280
+ const retriedLocalResult = await runLocalAgentTurnForCli(options, {
3281
+ reportFailure: false
3282
+ });
3283
+ if (retriedLocalResult.exitCode === 0) {
3284
+ return {
3285
+ exitCode: retriedLocalResult.exitCode,
3286
+ ...retriedLocalResult.dialogId ? { dialogId: retriedLocalResult.dialogId } : {},
3287
+ ...retriedLocalResult.turnTokens ? { turnTokens: retriedLocalResult.turnTokens } : {}
3288
+ };
3289
+ }
3290
+ }
3291
+ } catch {
3292
+ }
3293
+ }
3294
+ const localErrorMessage = localResult.localError ? toErrorMessage(localResult.localError) : "local runtime failed";
3295
+ options.output.write(
3296
+ `[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.
3297
+ `
3298
+ );
3299
+ return {
3300
+ exitCode: 1,
3301
+ ...localResult.dialogId ? { dialogId: localResult.dialogId } : {}
3302
+ };
3303
+ }
3304
+ }
3305
+ if (!authToken) {
3306
+ options.output.write(
3307
+ "[nolo] This install needs an auth token before it can talk to agents.\nRun `nolo login`, or set AUTH_TOKEN / NOLO_SERVER for non-interactive runs.\n"
3308
+ );
3309
+ return { exitCode: 1 };
3310
+ }
3311
+ if (options.ephemeral) {
3312
+ options.output.write(
3313
+ "[nolo] --ephemeral/--memory-only is only effective with the local runtime (--local); the server stores dialogs on its own and cannot run memory-only.\n"
3314
+ );
3315
+ }
3316
+ return runHttpAgentTurn(options, authToken);
3317
+ }
3318
+
3319
+ // packages/cli/agentRunCommand.ts
3320
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
3321
+ import { join as join3 } from "node:path";
3322
+
3323
+ // packages/agent-runtime/quickChatIntentCore.ts
3324
+ var INTENT_MODEL = "deepseek-v4-flash";
3325
+ var INTENT_PROVIDER = "deepseek";
3326
+ var QUICK_CHAT_INTENT_TIMEOUT_MS = 4e3;
3327
+ var SHORT_GREETING_MAX_LENGTH = 20;
3328
+ var SHORT_GREETING_PATTERN = /^(?:hi|hello|hey|hiya|yo|sup|howdy|嗨|哈喽|哈囉|你好|您好|在吗|在嘛|喂|早上好|中午好|下午好|晚上好|拜拜|再见|谢谢|thanks|thx|ok|好的|收到|了解)[!.。,、~~]?$/iu;
3329
+ function isShortGreeting(text) {
3330
+ const trimmed = text.trim();
3331
+ if (!trimmed) return false;
3332
+ if (trimmed.length > SHORT_GREETING_MAX_LENGTH) return false;
3333
+ return SHORT_GREETING_PATTERN.test(trimmed);
3334
+ }
3335
+ var COMPLEXITY_SIMPLE_MAX_LENGTH = 80;
3336
+ var COMPLEXITY_MEDIUM_MAX_LENGTH = 500;
3337
+ var COMPLEXITY_KEYWORDS = /为什么|如何|分析|对比|比较|设计|架构|实现|原理|推导|评价|区别|关系|影响|优化|重构/;
3338
+ var COMPLEXITY_CODE_BLOCK = /```[\s\S]*```/;
3339
+ function estimateComplexity(text) {
3340
+ const trimmed = text.trim();
3341
+ const len = trimmed.length;
3342
+ if (len > COMPLEXITY_MEDIUM_MAX_LENGTH) return "complex";
3343
+ if (COMPLEXITY_CODE_BLOCK.test(trimmed)) return "complex";
3344
+ if (len > COMPLEXITY_SIMPLE_MAX_LENGTH) return "medium";
3345
+ if (COMPLEXITY_KEYWORDS.test(trimmed)) return "medium";
3346
+ return "simple";
3347
+ }
3348
+ var TIER_DESCRIPTIONS = {
3349
+ flash: "\u5FEB\u901F\u7B80\u5355\uFF1A\u95EE\u5019\u3001\u95F2\u804A\u3001\u5FEB\u901F\u95EE\u7B54\u3001\u7FFB\u8BD1\u3001\u77ED\u6587",
3350
+ balanced: "\u5E73\u8861\u63A8\u7406\uFF1A\u5206\u6790\u3001\u4E2D\u7B49\u957F\u5EA6\u5199\u4F5C\u3001\u4EE3\u7801\u3001\u63A8\u7406",
3351
+ quality: "\u9AD8\u8D28\u91CF\u6DF1\u5EA6\uFF1A\u590D\u6742\u63A8\u7406\u3001\u957F\u6587\u3001\u67B6\u6784\u8BBE\u8BA1\u3001\u6DF1\u5EA6\u5206\u6790"
3352
+ };
3353
+ function buildQuickChatIntentSystemPrompt(tierAgents) {
3354
+ const agentList = tierAgents.map((t2) => ` - "${t2.agentKey}"\uFF1A${t2.description}`).join("\n");
3355
+ 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
3356
+
3357
+ \u53EF\u9009 agent \u5217\u8868\uFF08agentKey \u4E0E\u63CF\u8FF0\uFF09\uFF1A
3358
+ ${agentList}
3359
+
3360
+ \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
3361
+ - \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
3362
+ - \u7B80\u5355\u95EE\u5019\u3001\u95F2\u804A\u3001\u5FEB\u901F\u95EE\u7B54\u3001\u7FFB\u8BD1\u3001\u77ED\u6587 \u2192 \u9009 flash \u6863
3363
+ - \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
3364
+ - \u590D\u6742\u63A8\u7406\u3001\u957F\u6587\u3001\u67B6\u6784\u8BBE\u8BA1\u3001\u6DF1\u5EA6\u5206\u6790 \u2192 \u9009 quality \u6863
3365
+ - **\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
3366
+ - \u4E13\u804C agent \u8DEF\u7531\u89C4\u5219\uFF08\u6709\u660E\u786E\u5339\u914D\u65F6\u4F7F\u7528\uFF09\uFF1A
3367
+ - \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
3368
+ - \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
3369
+ - \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
3370
+ - \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
3371
+ - **\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
3372
+
3373
+ \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
3374
+ - 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
3375
+ - 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
3376
+ - \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
3377
+
3378
+ \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
3379
+
3380
+ \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
3381
+ - \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"
3382
+ - \u7528\u6237\u8981\u5199/\u6DA6\u8272/\u6539\u5199/\u7EED\u5199/\u6392\u7248\u4E00\u7BC7\u6587\u6863\u6216\u6587\u7AE0 \u2192 skills \u542B "doc"
3383
+ - \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
3384
+ - \u666E\u901A\u95EE\u7B54/\u7EAF\u95F2\u804A/\u5EFA\u5E94\u7528 \u2192 skills \u4E3A []
3385
+
3386
+ \u53EA\u8F93\u51FA JSON\uFF0C\u4E0D\u8981\u8F93\u51FA\u5176\u4ED6\u5185\u5BB9\uFF1A
3387
+ {"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 []>}`;
3388
+ }
3389
+ function parseQuickChatIntentResult(content, tierAgents) {
3390
+ try {
3391
+ const parsed = JSON.parse(content.trim());
3392
+ if (typeof parsed !== "object" || parsed === null || !("agentKey" in parsed)) return null;
3393
+ const key = parsed.agentKey;
3394
+ if (typeof key !== "string") return null;
3395
+ if (!tierAgents.some((t2) => t2.agentKey === key)) return null;
3396
+ const rawNeedsWorkspace = parsed.needsWorkspace;
3397
+ const needsWorkspace = rawNeedsWorkspace === true;
3398
+ const rawConfidence = parsed.confidence;
3399
+ const confidence = typeof rawConfidence === "number" && Number.isFinite(rawConfidence) && rawConfidence >= 0 && rawConfidence <= 1 ? rawConfidence : void 0;
3400
+ const rawSkills = parsed.skills;
3401
+ let skills;
3402
+ if (Array.isArray(rawSkills)) {
3403
+ const filtered = [...new Set(rawSkills)].filter(
3404
+ (s) => s === "table" || s === "doc" || s === "code"
3405
+ );
3406
+ if (filtered.length > 0) skills = filtered;
3407
+ }
3408
+ return { agentKey: key, needsWorkspace, confidence, skills };
3409
+ } catch {
3410
+ return null;
3411
+ }
3412
+ }
3413
+
3414
+ // packages/cli/client/autoModelRouter.ts
3415
+ var CLI_AUTO_TIER_AGENT_KEYS = {
3416
+ flash: PUBLIC_DEEPSEEK_V4_FLASH_AGENT_KEY,
3417
+ balanced: PUBLIC_DEEPSEEK_V4_PRO_AGENT_KEY,
3418
+ quality: PUBLIC_GLM_52_AGENT_KEY
3419
+ };
3420
+ var CLI_IMAGE_AGENT_KEY = PUBLIC_KIMI_K26_IMAGE_AGENT_KEY;
3421
+ var CLI_AUTO_TIER_AGENT_KEY_TABLE = {
3422
+ [CLI_AUTO_TIER_AGENT_KEYS.flash]: true,
3423
+ [CLI_AUTO_TIER_AGENT_KEYS.balanced]: true,
3424
+ [CLI_AUTO_TIER_AGENT_KEYS.quality]: true
3425
+ };
3426
+ var buildRouteOptions = () => ["flash", "balanced", "quality"].map((tier) => ({
3427
+ tier,
3428
+ agentKey: CLI_AUTO_TIER_AGENT_KEYS[tier],
3429
+ description: TIER_DESCRIPTIONS[tier]
3430
+ }));
3431
+ var tierForAgentKey = (agentKey) => {
3432
+ for (const tier of ["flash", "balanced", "quality"]) {
3433
+ if (CLI_AUTO_TIER_AGENT_KEYS[tier] === agentKey) return tier;
3434
+ }
3435
+ return null;
3436
+ };
3437
+ function resolveCliAutoFallbackTier(text) {
3438
+ switch (estimateComplexity(text)) {
3439
+ case "complex":
3440
+ return "quality";
3441
+ case "medium":
3442
+ return "balanced";
3443
+ case "simple":
3444
+ default:
3445
+ return "flash";
3446
+ }
3447
+ }
3448
+ async function classifyCliAutoRoute(text, options) {
3449
+ const timeoutMs = options.timeoutMs ?? QUICK_CHAT_INTENT_TIMEOUT_MS;
3450
+ const fallbackTier = resolveCliAutoFallbackTier(text);
3451
+ const fallback = () => ({
3452
+ agentKey: CLI_AUTO_TIER_AGENT_KEYS[fallbackTier],
3453
+ tier: fallbackTier,
3454
+ classified: false
3455
+ });
3456
+ if (!text.trim()) return fallback();
3457
+ if (isShortGreeting(text)) {
3458
+ return {
3459
+ agentKey: CLI_AUTO_TIER_AGENT_KEYS.flash,
3460
+ tier: "flash",
3461
+ classified: true
3462
+ };
3463
+ }
3464
+ if (!options.authToken) return fallback();
3465
+ const fetchImpl = options.fetchImpl ?? globalThis.fetch;
3466
+ const controller = new AbortController();
3467
+ const timeoutHandle = setTimeout(() => controller.abort(), timeoutMs);
3468
+ try {
3469
+ const routeOptions = buildRouteOptions();
3470
+ const messages = [
3471
+ { role: "system", content: buildQuickChatIntentSystemPrompt(routeOptions) },
3472
+ { role: "user", content: text }
3473
+ ];
3474
+ const { url, init } = buildPlatformChatCompletionRequest({
3475
+ providerConfig: {
3476
+ serverUrl: options.serverUrl,
3477
+ authToken: options.authToken,
3478
+ agentKey: CLI_AUTO_TIER_AGENT_KEYS.flash,
3479
+ model: INTENT_MODEL,
3480
+ provider: INTENT_PROVIDER,
3481
+ endpoint: resolvePlatformChatCompletionsEndpoint(INTENT_PROVIDER) ?? "",
3482
+ requestOptions: {}
3483
+ },
3484
+ messages,
3485
+ stream: false
3486
+ });
3487
+ const response = await fetchImpl(url, {
3488
+ ...init,
3489
+ signal: controller.signal
3490
+ });
3491
+ if (!response.ok) return fallback();
3492
+ const data = parsePlatformChatCompletionData(await response.text());
3493
+ const content = data?.choices?.[0]?.message?.content;
3494
+ if (typeof content !== "string" || !content.trim()) return fallback();
3495
+ const parsed = parseQuickChatIntentResult(content, routeOptions);
3496
+ if (!parsed) return fallback();
3497
+ const tier = tierForAgentKey(parsed.agentKey) ?? fallbackTier;
3498
+ return {
3499
+ agentKey: parsed.agentKey,
3500
+ tier,
3501
+ classified: true,
3502
+ confidence: parsed.confidence,
3503
+ needsWorkspace: parsed.needsWorkspace,
3504
+ skills: parsed.skills
3505
+ };
3506
+ } catch {
3507
+ return fallback();
3508
+ } finally {
3509
+ clearTimeout(timeoutHandle);
3510
+ }
3511
+ }
3512
+
3513
+ // packages/agent-runtime/skillDiscovery.ts
3514
+ import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3515
+ import { join } from "node:path";
3516
+ var SKILL_SCAN_DIRS = [".agents/skills", "docs/skills"];
3517
+ var MAX_DISCOVERED_SKILLS = 50;
3518
+ function parseSkillFrontmatter(filePath) {
3519
+ try {
3520
+ const content = readFileSync(filePath, "utf8").slice(0, 2048);
3521
+ const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
3522
+ if (!fmMatch) return {};
3523
+ const fm = load(fmMatch[1]);
3524
+ if (!fm || typeof fm !== "object") return {};
3525
+ const name = typeof fm.name === "string" ? fm.name : void 0;
3526
+ const rawDesc = fm.description;
3527
+ const description = typeof rawDesc === "string" ? rawDesc : rawDesc == null ? void 0 : String(rawDesc);
3528
+ const sanitize = (s) => s?.trim().replace(/^["']|["']$/g, "").replace(/[\n\r]+/g, " ").slice(0, 200);
3529
+ return { name: sanitize(name), description: sanitize(description) };
3530
+ } catch {
3531
+ return {};
3532
+ }
3533
+ }
3534
+ function discoverSkills(cwd) {
3535
+ const skills = [];
3536
+ for (const dir of SKILL_SCAN_DIRS) {
3537
+ if (skills.length >= MAX_DISCOVERED_SKILLS) break;
3538
+ const absDir = join(cwd, dir);
3539
+ if (!existsSync(absDir) || !statSync(absDir).isDirectory()) continue;
3540
+ try {
3541
+ for (const entry of readdirSync(absDir)) {
3542
+ if (skills.length >= MAX_DISCOVERED_SKILLS) break;
3543
+ const skillMdPath = join(absDir, entry, "SKILL.md");
3544
+ if (existsSync(skillMdPath)) {
3545
+ const { name, description } = parseSkillFrontmatter(skillMdPath);
3546
+ skills.push({ name: name ?? entry, description: description ?? "", relativePath: join(dir, entry, "SKILL.md") });
3547
+ continue;
3548
+ }
3549
+ if (dir === "docs/skills" && entry.endsWith(".md")) {
3550
+ const flatPath = join(absDir, entry);
3551
+ const { name, description } = parseSkillFrontmatter(flatPath);
3552
+ skills.push({ name: name ?? entry.replace(/\.md$/, ""), description: description ?? "", relativePath: join(dir, entry) });
3553
+ }
3554
+ }
3555
+ } catch {
3556
+ }
3557
+ }
3558
+ return skills;
3559
+ }
3560
+ function buildSkillDiscoveryContextBlock(cwd) {
3561
+ try {
3562
+ const discovered = discoverSkills(cwd);
3563
+ const layer = buildSkillDiscoveryLayer(discovered, cwd);
3564
+ return layer?.content ?? null;
3565
+ } catch {
3566
+ return null;
3567
+ }
3568
+ }
3569
+
3570
+ // packages/cli/agentRunPrompts.ts
3571
+ import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
3572
+ import { dirname, extname, isAbsolute, join as join2, relative as relativePath, resolve, sep } from "node:path";
3573
+ function workflowRefToCandidatePath(cwd, ref) {
3574
+ const normalized = ref.trim();
3575
+ if (!normalized) return "";
3576
+ if (normalized.endsWith(".md") || normalized.includes("/") || normalized.includes("\\")) {
3577
+ const directPath = resolve(cwd, normalized);
3578
+ if (existsSync2(directPath)) return directPath;
3579
+ }
3580
+ const fileName = normalized.replace(/[^a-zA-Z0-9一-鿿]+/g, "-").replace(/^-+|-+$/g, "");
3581
+ return resolve(cwd, "docs", "workflows", `${fileName}.md`);
3582
+ }
3583
+ var SKILL_SEARCH_DIRS = [
3584
+ ".agents/skills",
3585
+ "docs/skills"
3586
+ ];
3587
+ function skillRefToCandidatePath(cwd, ref) {
3588
+ const normalized = ref.trim();
3589
+ if (!normalized) return "";
3590
+ if (normalized.endsWith(".md") || normalized.includes("/") || normalized.includes("\\")) {
3591
+ const directPath = resolve(cwd, normalized);
3592
+ if (existsSync2(directPath)) return directPath;
3593
+ }
3594
+ const fileName = normalized.replace(/[^a-zA-Z0-9一-鿿]+/g, "-").replace(/^-+|-+$/g, "");
3595
+ for (const dir of SKILL_SEARCH_DIRS) {
3596
+ if (dir === "docs/skills") {
3597
+ const flatPath = resolve(cwd, dir, `${fileName}.md`);
3598
+ if (existsSync2(flatPath)) return flatPath;
3599
+ } else {
3600
+ const skillMdPath = resolve(cwd, dir, fileName, "SKILL.md");
3601
+ if (existsSync2(skillMdPath)) return skillMdPath;
3602
+ }
3603
+ }
3604
+ return resolve(cwd, "docs", "skills", `${fileName}.md`);
3605
+ }
3606
+ async function resolveWorkflowReference(ref, cwd = process.cwd()) {
3607
+ const path = workflowRefToCandidatePath(cwd, ref);
3608
+ if (!path || !existsSync2(path)) {
3609
+ throw new Error(`Workflow reference not found: ${ref}`);
3610
+ }
3611
+ const markdown = readFileSync2(path, "utf8");
3612
+ const parsed = parseSkillDocProtocol(markdown);
3613
+ return {
3614
+ ref,
3615
+ content: parsed.content,
3616
+ ...parsed.meta?.workflowConfig ? { config: parsed.meta.workflowConfig } : {}
3617
+ };
3618
+ }
3619
+ function inlineRelativeRefs(body, skillDir) {
3620
+ const refPattern = /\[([^\]]+)\]\((\.\/[^)]+|[^)]+\/[^)]+)\)/g;
3621
+ const normalizedSkillDir = resolve(skillDir) + sep;
3622
+ return body.replace(refPattern, (match, label, relPath) => {
3623
+ const cleaned = relPath.trim();
3624
+ if (!cleaned.startsWith("./") && !cleaned.startsWith("../")) return match;
3625
+ const absPath = resolve(skillDir, cleaned);
3626
+ const relative = relativePath(normalizedSkillDir, absPath);
3627
+ if (relative.startsWith("..") || isAbsolute(relative)) return match;
3628
+ if (!existsSync2(absPath) || !absPath.endsWith(".md")) return match;
3629
+ const refContent = readFileSync2(absPath, "utf8");
3630
+ return `
3631
+
3632
+ ### ${label}
3633
+
3634
+ ${refContent}
3635
+
3636
+ `;
3637
+ });
3638
+ }
3639
+ function listSkillScripts(skillDir) {
3640
+ const scriptsDir = join2(skillDir, "scripts");
3641
+ if (!existsSync2(scriptsDir) || !statSync2(scriptsDir).isDirectory()) return null;
3642
+ const entries = readdirSync2(scriptsDir).filter((e) => !e.startsWith("."));
3643
+ if (entries.length === 0) return null;
3644
+ return [
3645
+ "",
3646
+ "--- Available scripts (use execShell/readFile to access) ---",
3647
+ ...entries.map((e) => `- scripts/${e}`)
3648
+ ].join("\n");
3649
+ }
3650
+ async function resolveSkillReference(ref, options = {}) {
3651
+ const isDbKey = /^(page|doc)-[0-9a-z]+-/i.test(ref);
3652
+ let markdown = "";
3653
+ let skillDir;
3654
+ let filePath;
3655
+ if (isDbKey) {
3656
+ if (!options.readDbRecord) {
3657
+ throw new Error("skill dbKey requires server access");
3658
+ }
3659
+ const record = await options.readDbRecord(ref);
3660
+ const contentText = record?.content ?? record?.prompt ?? record?.text;
3661
+ if (contentText === void 0 || contentText === null || contentText === "") {
3662
+ throw new Error(`Skill reference has no content: ${ref}`);
3663
+ }
3664
+ markdown = String(contentText);
3665
+ } else {
3666
+ const cwd = options.cwd ?? process.cwd();
3667
+ const path = skillRefToCandidatePath(cwd, ref);
3668
+ if (!path || !existsSync2(path)) {
3669
+ throw new Error(`Skill reference not found: ${ref}`);
3670
+ }
3671
+ markdown = readFileSync2(path, "utf8");
3672
+ filePath = path;
3673
+ skillDir = dirname(path);
3674
+ }
3675
+ const parsed = parseSkillDocProtocol(markdown);
3676
+ const name = parsed.meta?.skillConfig?.name;
3677
+ const promptPatch = parsed.meta?.skillConfig?.promptPatch;
3678
+ const externalParsed = parseExternalSkillMarkdown(markdown);
3679
+ const allowedTools = externalParsed.allowedTools;
3680
+ const effectiveName = name ?? externalParsed.name;
3681
+ let effectiveContent = parsed.content;
3682
+ if (skillDir) {
3683
+ effectiveContent = inlineRelativeRefs(effectiveContent, skillDir);
3684
+ const scriptsHint = listSkillScripts(skillDir);
3685
+ if (scriptsHint) effectiveContent += scriptsHint;
3686
+ }
3687
+ return {
3688
+ ref,
3689
+ content: effectiveContent,
3690
+ ...effectiveName ? { name: effectiveName } : {},
3691
+ ...promptPatch ? { promptPatch } : {},
3692
+ ...allowedTools && allowedTools.length > 0 ? { allowedTools } : {},
3693
+ ...skillDir ? { skillDir } : {}
3694
+ };
3695
+ }
3696
+ function buildSkillContextBlocks(skills) {
3697
+ if (!skills || !skills.length) return [];
3698
+ const seen = /* @__PURE__ */ new Set();
3699
+ const blocks = [];
3700
+ for (const skill of skills) {
3701
+ const dedupKey = skill.name ?? skill.ref;
3702
+ if (seen.has(dedupKey)) continue;
3703
+ seen.add(dedupKey);
3704
+ const header = `## ${dedupKey}`;
3705
+ const patch = skill.promptPatch ? `${skill.promptPatch}
3706
+ ` : "";
3707
+ blocks.push(`${header}
3708
+ ${patch}${skill.content}`);
3709
+ }
3710
+ return blocks;
3711
+ }
3712
+ function prependWorkflowReferencePrompt(message, workflow) {
3713
+ if (!workflow) return message;
3714
+ const config = workflow.config;
3715
+ return [
3716
+ "AI-native workflow reference:",
3717
+ "- This reference is guidance for the agent, not a central workflow engine.",
3718
+ `- ref: ${workflow.ref}`,
3719
+ ...config?.id ? [`- id: ${config.id}`] : [],
3720
+ ...config?.name ? [`- name: ${config.name}`] : [],
3721
+ ...config?.defaultAgent ? [`- suggested defaultAgent: ${config.defaultAgent}`] : [],
3722
+ ...config?.inputs?.length ? [`- inputs: ${config.inputs.join(", ")}`] : [],
3723
+ ...config?.recommendedTools?.length ? [`- recommendedTools: ${config.recommendedTools.join(", ")}`] : [],
3724
+ ...config?.requiredTools?.length ? [`- requiredTools: ${config.requiredTools.join(", ")}`] : [],
3725
+ ...config?.requiredOutputs?.length ? [`- requiredOutputs: ${config.requiredOutputs.join(", ")}`] : [],
3726
+ ...config?.gates?.length ? [`- gates: ${config.gates.join(", ")}`] : [],
3727
+ ...config?.contextStrategy ? [`- contextStrategy: ${config.contextStrategy}`] : [],
3728
+ ...config?.failureProtocol ? [`- failureProtocol: ${config.failureProtocol}`] : [],
3729
+ "",
3730
+ "Reference body:",
3731
+ workflow.content,
3732
+ "",
3733
+ "User task:",
3734
+ message
3735
+ ].join("\n");
3736
+ }
3737
+ function prependFeatureWorktreeInstruction(message, enabled) {
3738
+ if (!enabled) return message;
3739
+ return [
3740
+ "Local execution rule:",
3741
+ "- You are running in the current git checkout with shell access.",
3742
+ "- For read-only checks, smoke tests, or answering questions, stay in the current directory.",
3743
+ "- Before developing a new feature or making non-trivial code changes, create a separate git worktree yourself with git worktree and do the edits there.",
3744
+ "- Commit and push only when the user explicitly asks or the task requires it.",
3745
+ "",
3746
+ "User task:",
3747
+ message
3748
+ ].join("\n");
3749
+ }
3750
+ function prependSubjectDialogMarker(message, subjectDialogKey) {
3751
+ if (!subjectDialogKey) return message;
3752
+ return [
3753
+ `Subject dialog for this run: ${subjectDialogKey}`,
3754
+ "If the user asks to evaluate the referenced dialog, call readDialog with this id/key first.",
3755
+ "",
3756
+ message
3757
+ ].join("\n");
3758
+ }
3759
+ function normalizeCliImageInput(input) {
3760
+ if (/^(https?:|data:|file:)/i.test(input)) return input;
3761
+ const absolutePath = resolve(input);
3762
+ if (!existsSync2(absolutePath)) return input;
3763
+ const base64 = readFileSync2(absolutePath).toString("base64");
3764
+ return `data:${imageMimeTypeForPath(absolutePath)};base64,${base64}`;
3765
+ }
3766
+ function imageMimeTypeForPath(path) {
3767
+ switch (extname(path).toLowerCase()) {
3768
+ case ".jpg":
3769
+ case ".jpeg":
3770
+ return "image/jpeg";
3771
+ case ".webp":
3772
+ return "image/webp";
3773
+ case ".gif":
3774
+ return "image/gif";
3775
+ default:
3776
+ return "image/png";
3777
+ }
3778
+ }
3779
+
3780
+ // packages/cli/agentRunLocalWorkspace.ts
3781
+ async function runGitForLocalSummary(args, deps = {}) {
3782
+ const spawn = deps.spawnProcess ?? spawnProcess;
3783
+ const read = deps.readPipeText ?? readPipeText;
3784
+ const proc = spawn({
3785
+ cmd: ["git", ...args.gitArgs],
3786
+ cwd: args.cwd,
3787
+ stdout: "pipe",
3788
+ stderr: "pipe"
3789
+ });
3790
+ const [stdout, stderr, exitCode] = await Promise.all([
3791
+ read(proc.stdout),
3792
+ read(proc.stderr),
3793
+ proc.exited
3794
+ ]);
3795
+ if (exitCode !== 0) {
3796
+ throw new Error((stderr || stdout || `git ${args.gitArgs.join(" ")} failed`).trim());
3797
+ }
3798
+ return stdout.trim();
3799
+ }
3800
+ async function inspectLocalRunWorkspace(cwd, deps = {}) {
3801
+ const run = (gitArgs) => runGitForLocalSummary({ cwd, gitArgs }, deps);
3802
+ const status = await run(["status", "--short"]);
3803
+ const rawCommit = await run(["log", "-1", "--format=%h%x00%s"]).catch(() => "");
3804
+ const [hash, subject] = rawCommit.split("\0");
3805
+ return {
3806
+ cwd,
3807
+ clean: status.trim() === "",
3808
+ status,
3809
+ ...hash ? { commit: { hash, subject: subject ?? "" } } : {}
3810
+ };
3811
+ }
3812
+ function shouldPrintLocalRunSummary(args) {
3813
+ return Boolean(args.localRuntimeCwd && args.parsed.runtimeMode === "local");
3814
+ }
3815
+ function formatLocalRunSummary(args) {
3816
+ return [
3817
+ "",
3818
+ "[nolo] local run summary",
3819
+ `workspace: ${args.inspection.cwd}`,
3820
+ ...args.dialogId ? [`dialog: ${args.dialogId}`] : [],
3821
+ ...args.inspection.commit ? [`commit: ${args.inspection.commit.hash} ${args.inspection.commit.subject}`] : [],
3822
+ `dirty: ${args.inspection.clean ? "clean" : "dirty"}`,
3823
+ ...!args.inspection.clean && args.inspection.status.trim() ? [`status:
3824
+ ${args.inspection.status.trim()}`] : [],
3825
+ ""
3826
+ ].join("\n");
3827
+ }
3828
+
3829
+ // packages/cli/agentRunCommand.ts
3830
+ function resolvePositiveMs(value, fallback) {
3831
+ return parsePositiveFiniteNumberOrFallback(value, fallback);
3832
+ }
3833
+ async function resolveAgentRunAgentKey(args) {
3834
+ if (!args.agentInput.trim() || args.agentInput.startsWith("agent-")) {
3835
+ return void 0;
3836
+ }
3837
+ const db = await getReadableCliDb(args.output);
3838
+ const resolved = await resolveAgentRecordFromHybridStore({
3839
+ agentInput: args.agentInput,
3840
+ cliArgs: args.cliArgs,
3841
+ env: args.env,
3842
+ db,
3843
+ fetchImpl: fetch
3844
+ });
3845
+ return resolved?.agentKey;
3846
+ }
3847
+ async function runAgentRunCommand(args, deps) {
3848
+ const env = deps.env ?? process.env;
3849
+ const output = deps.output ?? process.stdout;
3850
+ if (args.includes("--help") || args.includes("-h")) {
3851
+ writeUsage(output, deps.commandPath);
3852
+ return 0;
3853
+ }
3854
+ const parsed = parseAgentRunArgs(args, { commandPath: deps.commandPath });
3855
+ if (!parsed) {
3856
+ writeUsage(output, deps.commandPath);
3857
+ return 1;
3858
+ }
3859
+ const runner = deps.runner ?? runAgentTurn;
3860
+ const nowMs = () => deps.now ? deps.now().getTime() : Date.now();
3861
+ const childRunId = env.NOLO_AGENT_RUN_ID;
3862
+ const hasRegistry = typeof childRunId === "string" && childRunId.length > 0;
3863
+ const processExit = deps.processExit ?? ((code) => process.exit(code));
3864
+ const stallTimeoutMs = resolvePositiveMs(
3865
+ deps.stallTimeoutMs ?? env.NOLO_LOCAL_RUN_STALL_TIMEOUT_MS,
3866
+ 5 * 6e4
3867
+ );
3868
+ let timedOut = false;
3869
+ let stalled = false;
3870
+ let timeoutTimer;
3871
+ let stallTimer;
3872
+ const activityTracker = hasRegistry ? createRunActivityTracker(childRunId, {
3873
+ env,
3874
+ homedir: deps.homedir,
3875
+ fs: deps.fs,
3876
+ now: deps.now
3877
+ }) : void 0;
3878
+ const clearWatchdogs = () => {
3879
+ if (timeoutTimer !== void 0) clearTimeout(timeoutTimer);
3880
+ if (stallTimer !== void 0) clearInterval(stallTimer);
3881
+ activityTracker?.flush();
3882
+ activityTracker?.dispose();
3883
+ };
3884
+ let rejectOnWatchdogFailure;
3885
+ const watchdogFailure = hasRegistry ? new Promise((_, reject) => {
3886
+ rejectOnWatchdogFailure = reject;
3887
+ }) : void 0;
3888
+ const raceWithWatchdog = (work) => watchdogFailure ? Promise.race([work, watchdogFailure]) : work;
3889
+ const exitFromWatchdog = (code) => {
3890
+ try {
3891
+ processExit(code);
3892
+ } catch {
3893
+ }
3894
+ };
3895
+ if (hasRegistry) {
3896
+ stallTimer = setInterval(() => {
3897
+ if (timedOut || stalled) return;
3898
+ const activity = activityTracker?.getActivity();
3899
+ const lastEventAtMs = activity ? new Date(activity.lastEventAt).getTime() : nowMs();
3900
+ const elapsedSinceEvent = nowMs() - lastEventAtMs;
3901
+ if (activity?.inFlight?.kind === "tool") {
3902
+ return;
3903
+ }
3904
+ if (activity?.inFlight?.kind === "llm" && elapsedSinceEvent < stallTimeoutMs) {
3905
+ return;
3906
+ }
3907
+ if (elapsedSinceEvent >= stallTimeoutMs) {
3908
+ stalled = true;
3909
+ clearWatchdogs();
3910
+ const note = `stalled: no progress for ${stallTimeoutMs}ms`;
3911
+ output.write(`[nolo] local run ${note}
3912
+ `);
3913
+ (deps.finalizeRunRecord ?? finalizeRunRecord)(
3914
+ childRunId,
3915
+ { status: "failed", note },
3916
+ { env, homedir: deps.homedir, fs: deps.fs, now: deps.now }
3917
+ );
3918
+ rejectOnWatchdogFailure?.(new Error(`[nolo] local run ${note}`));
3919
+ exitFromWatchdog(1);
3920
+ }
3921
+ }, Math.min(stallTimeoutMs, 1e4));
3922
+ if (typeof parsed.timeoutMs === "number" && parsed.timeoutMs > 0) {
3923
+ timeoutTimer = setTimeout(() => {
3924
+ timedOut = true;
3925
+ clearWatchdogs();
3926
+ const note = `timed out after ${parsed.timeoutMs}ms`;
3927
+ output.write(`[nolo] local run ${note}
3928
+ `);
3929
+ (deps.finalizeRunRecord ?? finalizeRunRecord)(
3930
+ childRunId,
3931
+ { status: "timeout", note },
3932
+ { env, homedir: deps.homedir, fs: deps.fs, now: deps.now }
3933
+ );
3934
+ rejectOnWatchdogFailure?.(new Error(`[nolo] local run ${note}`));
3935
+ exitFromWatchdog(124);
3936
+ }, parsed.timeoutMs);
3937
+ }
3938
+ }
3939
+ const wantsFallbackSuggestions = parsed.fallbackAgentKeys && parsed.fallbackAgentKeys.length > 0;
3940
+ const rawAgentInput = resolveRawAgentInput(args, deps.commandPath) ?? parsed.agentKey;
3941
+ const fullstackLocal = isFullstackCodingAgentRef(rawAgentInput, parsed.agentKey);
3942
+ const explicitLocalCli = isLocalCliAgentKey(parsed.agentKey);
3943
+ const isLocalRun = parsed.runtimeMode === "local" || !parsed.runtimeMode && (fullstackLocal || explicitLocalCli || args.includes("--dangerously-allow-shell"));
3944
+ if (wantsFallbackSuggestions && !isLocalRun) {
3945
+ output.write("[nolo] --fallback-agent (suggestions) is only supported for local CLI runs (use --local)\n");
3946
+ return 1;
3947
+ }
3948
+ let effectiveMessage = parsed.message;
3949
+ if (wantsFallbackSuggestions) {
3950
+ effectiveMessage = `${parsed.message}
3951
+
3952
+ [Quota fallback context for agent decision: If this execution for ${parsed.agentKey} hits subscription quota, YOU (the agent) must decide the next agent to use. Do not rely on automatic wrapper. Reason based on the specific task (complexity, length, domain), agent strengths, cost, previous attempt results. Suggested alternatives: ${parsed.fallbackAgentKeys.join(", ")}. Update the task row with attempt history and dispatch the chosen next (via new agent run or callAgent({ background: true })).]`;
3953
+ }
3954
+ const resolvedAgentKey = parsed.runtimeMode !== "local" ? await (deps.resolveAgentRunAgentKey ?? resolveAgentRunAgentKey)({
3955
+ agentInput: resolveRawAgentInput(args, deps.commandPath) ?? parsed.agentKey,
3956
+ cliArgs: args,
3957
+ env,
3958
+ output
3959
+ }).catch(() => void 0) : void 0;
3960
+ const agentKey = resolvedAgentKey ?? parsed.agentKey;
3961
+ let effectiveAgentKey = agentKey;
3962
+ let modelOverride = null;
3963
+ const autoRouteRequested = deps.commandPath?.join(" ") === "chat" && !parsed.continueDialogId && (args.includes("--auto-route") || env.NOLO_AUTO_ROUTE === "1") && env.NOLO_AUTO_ROUTE !== "0";
3964
+ if (autoRouteRequested) {
3965
+ const authToken = resolveAuthToken(args, env);
3966
+ const serverUrl = resolveServerUrl(env);
3967
+ const hasExplicitAgent = Boolean(readFlagValue(args, "--agent"));
3968
+ const route = await classifyCliAutoRoute(effectiveMessage, {
3969
+ serverUrl,
3970
+ authToken
3971
+ });
3972
+ if (hasExplicitAgent) {
3973
+ modelOverride = buildModelLayerOverride(
3974
+ await readDbRecord({
3975
+ dbKey: agentKey,
3976
+ authToken,
3977
+ serverUrl,
3978
+ fetchImpl: fetch
3979
+ }).catch(() => null)
3980
+ );
3981
+ }
3982
+ if (hasExplicitAgent && !modelOverride) {
3983
+ output.write(
3984
+ "[nolo] auto-route: \u8986\u76D6\u6E90 agent \u8BFB\u53D6\u5931\u8D25\uFF0C\u6309\u539F\u6837\u76F4\u8DD1\u6240\u9009 agent\u3002\n"
3985
+ );
3986
+ } else {
3987
+ effectiveAgentKey = route.agentKey;
3988
+ output.write(
3989
+ `[nolo] auto \u2192 ${route.tier}${modelOverride ? ` (model: ${agentKey})` : ""}
3990
+ `
3991
+ );
3992
+ }
3993
+ }
3994
+ let workflowReference;
3995
+ if (parsed.workflowRef) {
3996
+ try {
3997
+ workflowReference = await (deps.resolveWorkflowReference ?? resolveWorkflowReference)(
3998
+ parsed.workflowRef
3999
+ );
4000
+ } catch (error) {
4001
+ output.write(`[nolo] ${toErrorMessage(error)}
4002
+ `);
4003
+ return 1;
4004
+ }
4005
+ }
4006
+ let skillReferences;
4007
+ let skillAllowedToolOverride;
4008
+ if (parsed.skillRefs?.length) {
4009
+ const skills = [];
4010
+ const authToken = resolveAuthToken(args, env);
4011
+ const serverUrl = resolveServerUrl(env);
4012
+ for (const ref of parsed.skillRefs) {
4013
+ try {
4014
+ const resolved = await resolveSkillReference(ref, {
4015
+ cwd: parsed.cwd,
4016
+ readDbRecord: async (dbKey) => {
4017
+ return readDbRecord({
4018
+ dbKey,
4019
+ authToken,
4020
+ serverUrl,
4021
+ fetchImpl: fetch
4022
+ });
4023
+ }
4024
+ });
4025
+ skills.push(resolved);
4026
+ } catch (error) {
4027
+ output.write(`[nolo] ${toErrorMessage(error)}
4028
+ `);
4029
+ return 1;
4030
+ }
4031
+ }
4032
+ skillReferences = skills;
4033
+ const skillToolLists = skills.map((s) => s.allowedTools).filter((t2) => !!t2 && t2.length > 0);
4034
+ if (skillToolLists.length > 0) {
4035
+ const skillAllowed = skillToolLists.reduce(
4036
+ (acc, tools) => acc.filter((t2) => tools.includes(t2))
4037
+ );
4038
+ if (skillAllowed.length === 0) {
4039
+ output.write(`[nolo] warning: attached skills declare incompatible allowed-tools; no tool restriction enforced
4040
+ `);
4041
+ }
4042
+ skillAllowedToolOverride = parsed.allowedToolNames?.length ? parsed.allowedToolNames.filter((t2) => skillAllowed.includes(t2)) : skillAllowed;
4043
+ }
4044
+ }
4045
+ let localRuntimeCwd = parsed.cwd;
4046
+ if (!localRuntimeCwd && parsed.runtimeMode === "local") {
4047
+ localRuntimeCwd = process.cwd();
4048
+ }
4049
+ const runEnv = buildLocalRunEnv({
4050
+ env,
4051
+ allowShell: parsed.allowShell
4052
+ });
4053
+ if (parsed.background && isLocalRun) {
4054
+ const { runId, pid, logPath } = await (deps.spawnLocalBackgroundRun ?? spawnLocalBackgroundRun)(
4055
+ {
4056
+ rawArgs: args,
4057
+ commandPath: deps.commandPath,
4058
+ cliEntrypointPath: deps.cliEntrypointPath,
4059
+ agentKey,
4060
+ cwd: localRuntimeCwd,
4061
+ msgFile: readFlagValue(args, "--msg-file"),
4062
+ message: parsed.message,
4063
+ timeoutMs: parsed.timeoutMs,
4064
+ output
4065
+ },
4066
+ {
4067
+ env,
4068
+ homedir: deps.homedir,
4069
+ spawn: deps.spawn,
4070
+ fs: deps.fs,
4071
+ now: deps.now,
4072
+ generateRunId: deps.generateRunId
4073
+ }
4074
+ );
4075
+ output.write(`[nolo] runId=${runId}
4076
+ `);
4077
+ output.write(`[nolo] pid=${pid ?? "-"}
4078
+ `);
4079
+ output.write(`[nolo] log=${logPath}
4080
+ `);
4081
+ output.write(`[nolo] status: nolo agent status ${runId}
4082
+ `);
4083
+ output.write(`[nolo] stop: nolo agent stop ${runId}
4084
+ `);
4085
+ return 0;
4086
+ }
4087
+ const cliCwd = parsed.cwd ?? process.cwd();
4088
+ const extraContextBlocks = [];
4089
+ const AGENTS_MD_MAX = 8192;
4090
+ for (const name of ["AGENTS.md", "CLAUDE.md"]) {
4091
+ const agentsMdPath = join3(cliCwd, name);
4092
+ if (existsSync3(agentsMdPath)) {
4093
+ try {
4094
+ let mdContent = readFileSync3(agentsMdPath, "utf8").trim();
4095
+ if (!mdContent) continue;
4096
+ if (Buffer.byteLength(mdContent, "utf8") > AGENTS_MD_MAX) {
4097
+ mdContent = Buffer.from(mdContent, "utf8").subarray(0, AGENTS_MD_MAX).toString("utf8") + "\n\n<!-- AGENTS.md truncated -->";
4098
+ }
4099
+ extraContextBlocks.push(`--- \u9879\u76EE\u6307\u4EE4\uFF08${name}\uFF09---
4100
+ ${mdContent}`);
4101
+ break;
4102
+ } catch {
4103
+ }
4104
+ }
4105
+ }
4106
+ extraContextBlocks.push(...buildSkillContextBlocks(skillReferences));
4107
+ const skillDiscoveryBlock = buildSkillDiscoveryContextBlock(cliCwd);
4108
+ if (skillDiscoveryBlock) {
4109
+ extraContextBlocks.push(skillDiscoveryBlock);
4110
+ }
4111
+ const buildRunOptions = (targetAgentKey) => ({
4112
+ agentName: targetAgentKey,
4113
+ agentKey: targetAgentKey,
4114
+ serverUrl: resolveServerUrl(env),
4115
+ message: prependWorkflowReferencePrompt(
4116
+ prependSubjectDialogMarker(
4117
+ prependFeatureWorktreeInstruction(
4118
+ effectiveMessage,
4119
+ parsed.injectFeatureWorktreeInstruction
4120
+ ),
4121
+ parsed.subjectDialogKey
4122
+ ),
4123
+ workflowReference
4124
+ ),
4125
+ imageUrls: parsed.imageUrls.map(normalizeCliImageInput),
4126
+ scriptDir: deps.scriptDir,
4127
+ env: runEnv,
4128
+ output,
4129
+ ...deps.localRuntimeAdapterFactory ? { localRuntimeAdapterFactory: deps.localRuntimeAdapterFactory } : {},
4130
+ ...localRuntimeCwd ? { localRuntimeCwd } : {},
4131
+ ...parsed.runtimeMode ? { runtimeMode: parsed.runtimeMode } : {},
4132
+ ...parsed.continueDialogId ? { continueDialogId: parsed.continueDialogId } : {},
4133
+ ...parsed.spaceId ? { spaceId: parsed.spaceId } : {},
4134
+ ...parsed.category ? { category: parsed.category } : {},
4135
+ ...parsed.inheritedFromDialogKey ? { inheritedFromDialogKey: parsed.inheritedFromDialogKey } : {},
4136
+ ...parsed.parentDialogId ? { parentDialogId: parsed.parentDialogId } : {},
4137
+ ...parsed.parentWakeOnTerminal ? { parentWakeOnTerminal: true } : {},
4138
+ ...parsed.subjectDialogKey ? { subjectDialogKey: parsed.subjectDialogKey } : {},
4139
+ ...parsed.subjectRefs?.length ? { subjectRefs: parsed.subjectRefs } : {},
4140
+ ...parsed.allowedChildAgentKeys?.length ? { allowedChildAgentKeys: parsed.allowedChildAgentKeys } : {},
4141
+ ...skillAllowedToolOverride !== void 0 ? { allowedToolNames: skillAllowedToolOverride } : parsed.allowedToolNames?.length ? { allowedToolNames: parsed.allowedToolNames } : {},
4142
+ ...parsed.blockedToolNames?.length ? { blockedToolNames: parsed.blockedToolNames } : {},
4143
+ ...activityTracker ? { onLoopEvent: activityTracker.onLoopEvent } : {},
4144
+ background: parsed.background,
4145
+ noStream: parsed.noStream,
4146
+ ephemeral: parsed.ephemeral,
4147
+ ...typeof parsed.timeoutMs === "number" ? { timeoutMs: parsed.timeoutMs } : {},
4148
+ traceTools: parsed.traceTools,
4149
+ ...parsed.eventsMode ? { eventsMode: parsed.eventsMode } : {},
4150
+ ...parsed.taskEvidence ? { taskEvidence: parsed.taskEvidence } : {},
4151
+ ...extraContextBlocks.length > 0 ? { extraContextBlocks } : {}
4152
+ });
4153
+ let result = await raceWithWatchdog(
4154
+ runner({
4155
+ ...buildRunOptions(effectiveAgentKey),
4156
+ ...modelOverride ? { modelOverride } : {}
4157
+ })
4158
+ );
4159
+ clearWatchdogs();
4160
+ if (result.exitCode !== 0 && isQuotaExhaustedError(result.localError) && isLocalRun && parsed.fallbackAgentKeys && parsed.fallbackAgentKeys.length > 0) {
4161
+ const fallbackAgentKey = parsed.fallbackAgentKeys[0];
4162
+ output.write(
4163
+ `[nolo] quota exhausted on ${agentKey}, falling back to ${fallbackAgentKey}
4164
+ `
4165
+ );
4166
+ const fallbackResult = await raceWithWatchdog(runner(buildRunOptions(fallbackAgentKey)));
4167
+ clearWatchdogs();
4168
+ if (fallbackResult.exitCode === 0) {
4169
+ result = fallbackResult;
4170
+ } else {
4171
+ result = fallbackResult;
4172
+ output.write(
4173
+ `[nolo] fallback to ${fallbackAgentKey} also failed: ${fallbackResult.localError instanceof Error ? fallbackResult.localError.message : String(fallbackResult.localError ?? "unknown error")}
4174
+ `
4175
+ );
4176
+ }
4177
+ } else if (result.localError instanceof CliProviderQuotaError) {
4178
+ output.write(`
4179
+ [nolo] Quota limit hit for ${parsed.agentKey} (CliProviderQuotaError).
4180
+ `);
4181
+ if (parsed.fallbackAgentKeys && parsed.fallbackAgentKeys.length > 0) {
4182
+ output.write(`Suggested alternatives for your decision: ${parsed.fallbackAgentKeys.join(", ")}
4183
+ `);
4184
+ }
4185
+ output.write(`As the orchestrating agent, decide next based on task and re-dispatch (update task state with this attempt).
4186
+ `);
4187
+ }
4188
+ if (result.dialogId) {
4189
+ if (parsed.background) {
4190
+ output.write(`
4191
+ [nolo] background dialog ${result.dialogId}
4192
+ `);
4193
+ output.write(`[nolo] read: nolo dialog read ${result.dialogId}
4194
+ `);
4195
+ } else {
4196
+ output.write(`
4197
+ [nolo] dialog ${result.dialogId}
4198
+ `);
4199
+ }
4200
+ }
4201
+ if (shouldPrintLocalRunSummary({ parsed, localRuntimeCwd })) {
4202
+ try {
4203
+ const inspect = deps.inspectLocalRunWorkspace ?? inspectLocalRunWorkspace;
4204
+ const inspection = await inspect(localRuntimeCwd);
4205
+ output.write(formatLocalRunSummary({
4206
+ dialogId: result.dialogId,
4207
+ inspection
4208
+ }));
4209
+ } catch (error) {
4210
+ output.write(
4211
+ `
4212
+ [nolo] local run summary unavailable: ${toErrorMessage(error)}
4213
+ `
4214
+ );
4215
+ }
4216
+ }
4217
+ if (typeof childRunId === "string" && childRunId.length > 0) {
4218
+ const finalStatus = result.exitCode === 0 ? "done" : "failed";
4219
+ await (deps.finalizeRunRecord ?? finalizeRunRecord)(
4220
+ childRunId,
4221
+ {
4222
+ status: finalStatus,
4223
+ exitCode: result.exitCode,
4224
+ dialogId: result.dialogId
4225
+ },
4226
+ {
4227
+ env,
4228
+ homedir: deps.homedir,
4229
+ fs: deps.fs,
4230
+ now: deps.now
4231
+ }
4232
+ );
4233
+ }
4234
+ return result.exitCode;
4235
+ }
4236
+ var QUOTA_ERROR_PATTERNS = [
4237
+ /429/,
4238
+ /quota/i,
4239
+ /rate\s*limit/i,
4240
+ /too\s*many\s*requests/i,
4241
+ /额度/,
4242
+ /上限/,
4243
+ /用尽/,
4244
+ /CliProviderQuotaError/i
4245
+ ];
4246
+ function isQuotaExhaustedError(error) {
4247
+ if (!error) return false;
4248
+ if (error instanceof CliProviderQuotaError) return true;
4249
+ if (error instanceof Error) {
4250
+ return QUOTA_ERROR_PATTERNS.some((pattern) => pattern.test(error.message));
4251
+ }
4252
+ if (typeof error === "string") {
4253
+ return QUOTA_ERROR_PATTERNS.some((pattern) => pattern.test(error));
4254
+ }
4255
+ const maybeStatus = error.status;
4256
+ if (typeof maybeStatus === "number" && maybeStatus === 429) return true;
4257
+ const maybeStatusCode = error.statusCode;
4258
+ if (typeof maybeStatusCode === "number" && maybeStatusCode === 429) return true;
4259
+ return false;
4260
+ }
4261
+
4262
+ export {
4263
+ formatTokenCount,
4264
+ renderTokenStatus,
4265
+ resolveCliColorEnabled,
4266
+ dimCliText,
4267
+ THEME_PALETTES,
4268
+ getActiveThemeName,
4269
+ setActiveThemeName,
4270
+ getActiveBrightness,
4271
+ setActiveBrightness,
4272
+ getActiveDensity,
4273
+ setActiveDensity,
4274
+ resolveTuiBrightness,
4275
+ surfaceBackgroundSequence,
4276
+ themeColorSequence,
4277
+ themeText,
4278
+ highlightMarkdown,
4279
+ formatAssistantDisplay,
4280
+ normalizeThinkingDisplayMode,
4281
+ parseCliLocale,
4282
+ getCliLocale,
4283
+ setCliLocale,
4284
+ initCliLocale,
4285
+ t,
4286
+ normalizeToolDisplayMode,
4287
+ runAgentTurn,
4288
+ CLI_IMAGE_AGENT_KEY,
4289
+ CLI_AUTO_TIER_AGENT_KEY_TABLE,
4290
+ classifyCliAutoRoute,
4291
+ buildSkillDiscoveryContextBlock,
4292
+ resolveWorkflowReference,
4293
+ resolveSkillReference,
4294
+ buildSkillContextBlocks,
4295
+ prependSubjectDialogMarker,
4296
+ normalizeCliImageInput,
4297
+ inspectLocalRunWorkspace,
4298
+ runAgentRunCommand,
4299
+ isQuotaExhaustedError
4300
+ };