nolo-cli 0.8.0 → 0.9.0

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