nolo-cli 0.54.0-alpha.1 → 0.54.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 (146) hide show
  1. package/{_executeModel-IVEOJ7L6.js → _executeModel-OPTKYHJA.js} +53 -48
  2. package/{action-IU6ON7ZB.js → action-ORXWW2ZG.js} +9 -9
  3. package/{addContentAction-5V5CCI3I.js → addContentAction-53BZ34AE.js} +9 -8
  4. package/{addDialogAgentAction-KF3KZZNZ.js → addDialogAgentAction-JRYUS54Y.js} +11 -10
  5. package/{agentDeleteCommand-D7AFLKQN.js → agentDeleteCommand-FJIWU2BS.js} +31 -29
  6. package/{agentEmailCommands-MXFZGF54.js → agentEmailCommands-CE32L43B.js} +62 -58
  7. package/{agentListCommands-MP732Q23.js → agentListCommands-VJNKULIK.js} +43 -34
  8. package/{agentMachineCommands-352JFJX4.js → agentMachineCommands-HCJ3WQXL.js} +31 -29
  9. package/agentRecordCommands-QFWERED4.js +119 -0
  10. package/{agentRunCommand-UUWJMH7J.js → agentRunCommand-G3GQMUNN.js} +70 -59
  11. package/{agentRunControl-4GHHSXFZ.js → agentRunControl-INKFDXDA.js} +3 -1
  12. package/{agentRuntimeLocal-KWBPJ53H.js → agentRuntimeLocal-K327ELSL.js} +31 -27
  13. package/{agentSlice-V742SNKC.js → agentSlice-ODKF4J6M.js} +9 -9
  14. package/{agentSuperviseCommands-VB6BUS5H.js → agentSuperviseCommands-5AR3DKNN.js} +2 -2
  15. package/{appDeleteCommands-GV4TF52A.js → appDeleteCommands-XUXMFDI4.js} +32 -30
  16. package/{appDeployCommands-EZMUPNSW.js → appDeployCommands-37F4GPCU.js} +32 -30
  17. package/{appDeployStatusCommands-AAGELKAN.js → appDeployStatusCommands-WSPU7TUK.js} +32 -30
  18. package/{appGetCommands-W37KDAON.js → appGetCommands-YLOBA3EM.js} +32 -30
  19. package/{appListCommands-IFDWBBNV.js → appListCommands-UD62R5ME.js} +32 -30
  20. package/{authCommand-5HM57KPL.js → authCommand-OK6HLHGS.js} +13 -13
  21. package/{authCommands-SBQVTD6V.js → authCommands-2DUIUEMT.js} +2 -2
  22. package/{chatQueueReduxAdapter-ZXXP6E4O.js → chatQueueReduxAdapter-TV2TUOQS.js} +28 -25
  23. package/{chunk-7CH6U3FL.js → chunk-2GRGFCF6.js} +2 -4
  24. package/{chunk-T27IEBNQ.js → chunk-2QDDKFG5.js} +74 -1
  25. package/{chunk-2JYSSXLY.js → chunk-2WBFJHQA.js} +5 -27
  26. package/{chunk-FZMM2KZC.js → chunk-3LKZRP7E.js} +2 -2
  27. package/{chunk-WUMYQ2FE.js → chunk-3Y2K5O2R.js} +64 -6
  28. package/{chunk-5XCW6RQG.js → chunk-3ZTR7ZO4.js} +1 -1
  29. package/{chunk-W6N3GLUU.js → chunk-5FCIPEKT.js} +327 -149
  30. package/{chunk-B54WOZ77.js → chunk-5UAYZEXO.js} +2 -2
  31. package/{chunk-5SXS7T5Q.js → chunk-6COJEAYH.js} +36 -105
  32. package/{chunk-PEMGYJNS.js → chunk-6HDYSFLO.js} +4 -6
  33. package/{chunk-6XIE5ZGI.js → chunk-6HIGYUBQ.js} +1 -1
  34. package/chunk-6IGD47IC.js +19 -0
  35. package/chunk-6YTOJY35.js +352 -0
  36. package/{chunk-6FGXGFAH.js → chunk-75MSWWUE.js} +1 -1
  37. package/{chunk-ALMVQQPG.js → chunk-7FAS3NEF.js} +17 -17
  38. package/{chunk-76UA7376.js → chunk-7NYDJAQ7.js} +20 -15
  39. package/{chunk-2T6XO4PK.js → chunk-A65UV3M5.js} +4 -4
  40. package/{chunk-V2IPBG5U.js → chunk-AA564GBL.js} +2 -2
  41. package/{chunk-SQTVMYMU.js → chunk-AUVFJGPC.js} +4 -4
  42. package/{chunk-HR5XDXYM.js → chunk-BBTN4NQD.js} +4 -4
  43. package/{chunk-CULR4XB4.js → chunk-BG7J6Y5X.js} +1 -1
  44. package/chunk-BY2FF2C4.js +526 -0
  45. package/{chunk-FA4SY4F5.js → chunk-BYEJ7K2Z.js} +1 -1
  46. package/chunk-BYXBJQAS.js +0 -0
  47. package/{chunk-3AOLVADY.js → chunk-DVINR3PL.js} +1 -1
  48. package/{chunk-DIQCV4FR.js → chunk-EA5OYZVD.js} +1 -1
  49. package/{chunk-RO2IUWZE.js → chunk-EICIFHVS.js} +1 -1
  50. package/chunk-EJE33XNR.js +19059 -0
  51. package/{chunk-M2CGG6VJ.js → chunk-EVBFYPDC.js} +1 -1
  52. package/{chunk-KHCLMKZZ.js → chunk-EY7V7REW.js} +280 -6
  53. package/{chunk-MC66ASS6.js → chunk-FJHMLRDQ.js} +12 -12
  54. package/{chunk-YFHUQLJF.js → chunk-GRFK2XP4.js} +2 -2
  55. package/{chunk-J3UALFHL.js → chunk-HJTAGT3W.js} +1 -1
  56. package/{chunk-J7KVJAUQ.js → chunk-HPW5MNDQ.js} +38 -99
  57. package/{chunk-H2G6JXTR.js → chunk-HQUORJTM.js} +1 -1
  58. package/{chunk-OYNDYVSE.js → chunk-JON2PHYT.js} +1 -1
  59. package/{chunk-RBJD33HM.js → chunk-JXIP6ZWH.js} +7 -7
  60. package/{chunk-PTHLNXGY.js → chunk-K4NISKOR.js} +46 -8
  61. package/{chunk-5Y5G76BJ.js → chunk-KLIBF3BY.js} +2 -2
  62. package/chunk-LRGKOI5Y.js +297 -0
  63. package/{chunk-OU7NGN5Y.js → chunk-MLEZPE3U.js} +2 -2
  64. package/{chunk-4RXXP6WD.js → chunk-MYMBBB6Q.js} +5 -64
  65. package/{chunk-ET7BXWAW.js → chunk-N4JKMCY3.js} +2 -2
  66. package/{chunk-45E65JER.js → chunk-NFLA6GSU.js} +4 -4
  67. package/{chunk-SVEUG45B.js → chunk-NHRQCXIQ.js} +0 -24
  68. package/{chunk-WJV3G7BM.js → chunk-NJUQBYFJ.js} +3 -1
  69. package/{chunk-LGUQHYAL.js → chunk-O5FLGN2L.js} +3 -2
  70. package/{chunk-Q24S4NDF.js → chunk-Q4XZVZPN.js} +1 -1
  71. package/{chunk-L3JWI7WX.js → chunk-QOKAKDPE.js} +15 -11
  72. package/{chunk-VM7K5EJL.js → chunk-RHMDMTDK.js} +2 -2
  73. package/{chunk-YZ2WE6CW.js → chunk-SAJSBPI2.js} +2 -2
  74. package/{chunk-WHVQA7PJ.js → chunk-SSXTSPSG.js} +27 -6
  75. package/chunk-SUEIAK5N.js +2574 -0
  76. package/{chunk-X7S7OFUX.js → chunk-SVC3GGUA.js} +6 -6
  77. package/{chunk-7UFH6THF.js → chunk-SVDMVX4G.js} +118 -2
  78. package/{chunk-ZMV5PCRS.js → chunk-SWJGJMTW.js} +1 -1
  79. package/{chunk-DQLYEGP7.js → chunk-SYYUU7EV.js} +2 -2
  80. package/{chunk-K7CNV7GI.js → chunk-TC6P4NDR.js} +7 -7
  81. package/{chunk-SUFDWBLV.js → chunk-TR4DXUYX.js} +1988 -452
  82. package/{chunk-BANYWEYY.js → chunk-TY3ADTX5.js} +1 -1
  83. package/chunk-UAD2WT54.js +64 -0
  84. package/{chunk-HCBQVVFZ.js → chunk-UGBHDMHI.js} +4 -4
  85. package/{chunk-R63FNYSI.js → chunk-UOWXD4VN.js} +851 -970
  86. package/{chunk-GBXR3CSM.js → chunk-UR66XPVF.js} +3 -3
  87. package/{chunk-6BYAXNH5.js → chunk-VZMUOPYG.js} +12 -2
  88. package/{chunk-RVHPY5PC.js → chunk-WQHGJHAL.js} +441 -2982
  89. package/chunk-XIJZOCSP.js +1766 -0
  90. package/{chunk-HHBXCRJS.js → chunk-XOJRZUPF.js} +4 -4
  91. package/{chunk-BKPUPZ6W.js → chunk-YBFJQC2M.js} +95 -778
  92. package/{chunk-MRWGHF56.js → chunk-YDH3FDFQ.js} +12 -356
  93. package/{chunk-SG65C33A.js → chunk-YLCUU3WM.js} +4 -4
  94. package/{chunk-XXZULHJ7.js → chunk-YQBXOHE6.js} +1 -1
  95. package/chunk-ZJ7UHBJS.js +89 -0
  96. package/{chunk-TNPCQNGQ.js → chunk-ZVOHVJI3.js} +4 -9
  97. package/{codingSkills-BYQI6EPT.js → codingSkills-BCQXRZOI.js} +3 -4
  98. package/contentThunks-KNI6VXLT.js +71 -0
  99. package/{createAgentAutomationAction-LE3ZYDT6.js → createAgentAutomationAction-OK4L33KF.js} +29 -25
  100. package/createDialogAction-JDSMY4AY.js +62 -0
  101. package/{createPageAction-X4C7K3OD.js → createPageAction-T7RA3HCG.js} +32 -28
  102. package/{dbSlice-FWF5F4L7.js → dbSlice-TVX6SN2T.js} +7 -7
  103. package/{deleteFile-NPZ64DFN.js → deleteFile-JYKRT7C3.js} +4 -4
  104. package/{dialogCommands-CP2U44A7.js → dialogCommands-UQZYWOOV.js} +33 -31
  105. package/{dialogSlice-2SH5636X.js → dialogSlice-TYW2G7IL.js} +28 -25
  106. package/{docCreateCommands-A2KIJSF7.js → docCreateCommands-OA6HZBWO.js} +37 -36
  107. package/{docDeleteCommands-NZ7XO4Z4.js → docDeleteCommands-KBB3YQI2.js} +34 -33
  108. package/{docReadCommands-VWVK4ZAR.js → docReadCommands-ZLMFVC67.js} +33 -32
  109. package/{docStore-J6GQYKHS.js → docStore-2URPBVCS.js} +9 -34
  110. package/{docUpdateCommands-YTUEC6PN.js → docUpdateCommands-HMH5KUON.js} +37 -36
  111. package/{fetchMessages-KZQVBXKY.js → fetchMessages-DRABNDPZ.js} +3 -3
  112. package/{getPricing-I53UFOPH.js → getPricing-RFOPUWHY.js} +4 -4
  113. package/{handleSendMessageAction-MD325XZS.js → handleSendMessageAction-GO53TY7U.js} +30 -27
  114. package/index.js +67 -67
  115. package/{localDialogRead-NQ33HBSI.js → localDialogRead-PUQKFI73.js} +1 -1
  116. package/{machineCommands-TCNZV2DC.js → machineCommands-RK6L7ZWL.js} +64 -60
  117. package/{machineWsRunDispatch-QSX5IWPH.js → machineWsRunDispatch-3X64DW2Z.js} +59 -55
  118. package/memberThunks-THZYZFSN.js +638 -0
  119. package/{models-E2CYP4IB.js → models-JVKUEER5.js} +2 -2
  120. package/package.json +2 -3
  121. package/{read-J7RVVCHH.js → read-K4JU4MQC.js} +4 -4
  122. package/{readDocTool-G2WBM4IH.js → readDocTool-LQGR6FUN.js} +1 -1
  123. package/{readlineWorkspace-N3RGX7EK.js → readlineWorkspace-XUFJV3RS.js} +1005 -437
  124. package/{removeDialogAgentAction-RMPISD42.js → removeDialogAgentAction-ECHCFB7N.js} +13 -12
  125. package/{runAgentClientLoop-R3NTOERA.js → runAgentClientLoop-24GQ2AW6.js} +51 -46
  126. package/{setDialogExtraReferencesAction-7BUTHDAL.js → setDialogExtraReferencesAction-KCVJNGMB.js} +10 -9
  127. package/{setPrimaryDialogAgentAction-WCYIBGZ4.js → setPrimaryDialogAgentAction-VDOBOMKC.js} +13 -12
  128. package/{settingSlice-7NN2MI6Y.js → settingSlice-VR2IJSXC.js} +8 -8
  129. package/{skillDiscovery-COX56OBO.js → skillDiscovery-2QTZINON.js} +3 -4
  130. package/{spaceCommands-RC7XMYQ7.js → spaceCommands-S5YJJXL7.js} +31 -29
  131. package/{streamAgentChatTurn-WLWEBWSF.js → streamAgentChatTurn-QHKP3ABO.js} +82 -70
  132. package/{switchDialogAgentAction-WGMMQHDI.js → switchDialogAgentAction-5L4UP7VI.js} +11 -10
  133. package/{tableCommands-P2TXJ3CF.js → tableCommands-R6WFRDYF.js} +1 -1
  134. package/{toolApiClient-B2MFXMEG.js → toolApiClient-53U4UDFF.js} +3 -2
  135. package/{tools-LSLDSUWU.js → tools-SSXSTC3O.js} +42 -38
  136. package/{tuiSummaryLlmCaller-TPBL4BV2.js → tuiSummaryLlmCaller-URFJHFC5.js} +19 -17
  137. package/{updateDialogTitleAction-4HJEARM7.js → updateDialogTitleAction-YFYAEW3T.js} +30 -26
  138. package/agentRecordCommands-G6EFHU3G.js +0 -115
  139. package/chunk-AETZ7AD6.js +0 -274
  140. package/chunk-DMCFF4KE.js +0 -435
  141. package/chunk-EUG45QJR.js +0 -6104
  142. package/chunk-J4Z6UI4C.js +0 -7076
  143. package/chunk-OFZPJ3NT.js +0 -114
  144. package/createDialogAction-2C6SR62D.js +0 -59
  145. package/spaceSlice-KBVKKO3M.js +0 -127
  146. package/{chunk-NQ4N4IGO.js → chunk-CS6TCJ7E.js} +3 -3
@@ -1,6 +1,6 @@
1
1
  import {
2
- generateRunReport
3
- } from "./chunk-J3UALFHL.js";
2
+ findPotentialSecrets
3
+ } from "./chunk-S7Z2HMCN.js";
4
4
  import {
5
5
  buildLocalRunEnv,
6
6
  isFullstackCodingAgentRef,
@@ -10,6 +10,9 @@ import {
10
10
  resolveServerUrl,
11
11
  writeUsage
12
12
  } from "./chunk-LYGNQHUW.js";
13
+ import {
14
+ resolveMachineId
15
+ } from "./chunk-ESHYE7WU.js";
13
16
  import {
14
17
  agentRunCardLabels,
15
18
  createCliLocalRuntimeAdapter,
@@ -19,7 +22,12 @@ import {
19
22
  parseAgentRunEvent,
20
23
  t,
21
24
  toolLabel
22
- } from "./chunk-W6N3GLUU.js";
25
+ } from "./chunk-5FCIPEKT.js";
26
+ import {
27
+ readCredentialEntry,
28
+ recordCredentialProbe,
29
+ resolveCredentialKeyWithFallback
30
+ } from "./chunk-NI2BS6UD.js";
23
31
  import {
24
32
  getReadableCliDb
25
33
  } from "./chunk-43CXXVJD.js";
@@ -27,11 +35,14 @@ import {
27
35
  normalizeAgentHandle,
28
36
  readDbRecord,
29
37
  resolveAgentRecordFromHybridStore
30
- } from "./chunk-DQLYEGP7.js";
38
+ } from "./chunk-SYYUU7EV.js";
31
39
  import {
32
40
  NOLO_PROJECT_MANAGER_AGENT_KEY,
33
41
  isLocalCliAgentKey
34
42
  } from "./chunk-EKLRRZIN.js";
43
+ import {
44
+ generateRunReport
45
+ } from "./chunk-HJTAGT3W.js";
35
46
  import {
36
47
  CliProviderQuotaError
37
48
  } from "./chunk-JDSUTNYY.js";
@@ -42,17 +53,14 @@ import {
42
53
  readRunRecord,
43
54
  resolveRunQueuePath,
44
55
  spawnLocalBackgroundRun
45
- } from "./chunk-WHVQA7PJ.js";
46
- import {
47
- resolveMachineId
48
- } from "./chunk-ESHYE7WU.js";
56
+ } from "./chunk-SSXTSPSG.js";
49
57
  import {
50
58
  applyModelLayerOverride,
51
59
  buildModelLayerOverride
52
60
  } from "./chunk-LTRXGFMS.js";
53
61
  import {
54
62
  formatAgentListCard
55
- } from "./chunk-SUFDWBLV.js";
63
+ } from "./chunk-TR4DXUYX.js";
56
64
  import {
57
65
  isTransientFetchError
58
66
  } from "./chunk-5XTOKQLW.js";
@@ -66,39 +74,37 @@ import {
66
74
  formatStartRunCard,
67
75
  formatStatusRunCard,
68
76
  formatStopRunCard,
69
- isAgentRunTerminalStatus,
70
- runShowsLogTail
77
+ isAgentRunTerminalStatus
71
78
  } from "./chunk-KXKQO4Q6.js";
72
79
  import {
73
80
  buildMemorySubjectsForAgent,
74
81
  chooseMemoryOwners,
75
82
  loadMemoryCandidatesFromDb,
76
83
  resolveAgentMemoryPolicy
77
- } from "./chunk-MC66ASS6.js";
84
+ } from "./chunk-FJHMLRDQ.js";
78
85
  import {
79
86
  API_REPORTED_COST_MULTIPLIER,
80
87
  normalizeUsage,
81
88
  parseUiAskChoiceContent
82
- } from "./chunk-BKPUPZ6W.js";
89
+ } from "./chunk-YBFJQC2M.js";
83
90
  import {
84
91
  getDefaultCliLocalRuntimeDb
85
92
  } from "./chunk-IA3A5373.js";
86
93
  import {
87
94
  buildSkillDiscoveryContextLayer
88
- } from "./chunk-OFZPJ3NT.js";
95
+ } from "./chunk-SUEIAK5N.js";
89
96
  import {
90
97
  parseExternalSkillMarkdown,
91
98
  parseSkillDocProtocol
92
- } from "./chunk-AETZ7AD6.js";
99
+ } from "./chunk-EY7V7REW.js";
93
100
  import {
94
101
  LOCAL_AGENT_CONFIG_MISSING_CODE,
95
102
  LOCAL_TURN_ABORTED_CODE,
96
- readActionGate,
97
- readCommandActionGatePayload
98
- } from "./chunk-J4Z6UI4C.js";
103
+ readActionGate
104
+ } from "./chunk-EJE33XNR.js";
99
105
  import {
100
106
  resolveAgentRuntimeConfigFromRecord
101
- } from "./chunk-2T6XO4PK.js";
107
+ } from "./chunk-A65UV3M5.js";
102
108
  import {
103
109
  createThinkParserState,
104
110
  flushThinkParser,
@@ -107,20 +113,22 @@ import {
107
113
  import {
108
114
  resolveClientVersion,
109
115
  resolvePlatformAuthToken
110
- } from "./chunk-5Y5G76BJ.js";
116
+ } from "./chunk-KLIBF3BY.js";
111
117
  import {
112
- isQuotaExhaustedError
118
+ isQuotaExhaustedError,
119
+ mergeAvailabilityDeadline,
120
+ resolveCooldownGate
113
121
  } from "./chunk-IWUMSAQF.js";
114
122
  import {
115
123
  DEFAULT_CONTEXT_WINDOW,
116
124
  getModelContextWindow
117
- } from "./chunk-PTHLNXGY.js";
125
+ } from "./chunk-K4NISKOR.js";
118
126
  import {
119
127
  clipCompactText
120
- } from "./chunk-HR5XDXYM.js";
128
+ } from "./chunk-BBTN4NQD.js";
121
129
  import {
122
130
  findModelConfig
123
- } from "./chunk-7UFH6THF.js";
131
+ } from "./chunk-SVDMVX4G.js";
124
132
  import {
125
133
  compactWhitespace
126
134
  } from "./chunk-DWLEOOGR.js";
@@ -133,7 +141,7 @@ import {
133
141
  buildMemoryUseGuidanceLayer,
134
142
  partitionScopedBlocks,
135
143
  renderTurnContextBlocksWithScope
136
- } from "./chunk-3AOLVADY.js";
144
+ } from "./chunk-DVINR3PL.js";
137
145
  import {
138
146
  readPipeText,
139
147
  spawnProcess
@@ -145,13 +153,13 @@ import {
145
153
  CLIENT_VERSION_TOO_OLD_CODE,
146
154
  NOLO_CLIENT_VERSION_HEADER,
147
155
  buildClientVersionUpgradeMessage
148
- } from "./chunk-J7KVJAUQ.js";
156
+ } from "./chunk-HPW5MNDQ.js";
149
157
  import {
150
158
  parsePositiveFiniteNumberOrFallback
151
159
  } from "./chunk-Y5GCAMGP.js";
152
160
  import {
153
161
  createMemoryKey
154
- } from "./chunk-SVEUG45B.js";
162
+ } from "./chunk-NHRQCXIQ.js";
155
163
  import {
156
164
  asTrimmedLowercaseString
157
165
  } from "./chunk-QSJ3HFEF.js";
@@ -159,7 +167,7 @@ import {
159
167
  BUILTIN_NOLO_AGENT_KEY,
160
168
  BUILTIN_NOLO_AGENT_NAME,
161
169
  builtinAgentCatalogEntryById
162
- } from "./chunk-2JYSSXLY.js";
170
+ } from "./chunk-2WBFJHQA.js";
163
171
  import {
164
172
  parsePublicAgentId
165
173
  } from "./chunk-BJIRLHJR.js";
@@ -174,6 +182,7 @@ import {
174
182
  } from "./chunk-G3SUCT4W.js";
175
183
 
176
184
  // packages/cli/client/tokenUsage.ts
185
+ var PLATFORM_CREDIT_UNIT = "credits";
177
186
  var isUsableModelId = (model) => {
178
187
  const raw = model?.trim();
179
188
  return Boolean(raw) && raw !== "-" && raw !== "auto";
@@ -245,7 +254,7 @@ function buildTurnTokenUsage(usage, model) {
245
254
  const contextWindow = resolveContextWindow(model);
246
255
  const remaining = contextWindow && parsed.input > 0 ? Math.max(0, contextWindow - parsed.input) : void 0;
247
256
  const rawCost = typeof usage?.cost === "number" ? usage.cost : void 0;
248
- const isPlatformBilled = usage?.billing_unit === "credits";
257
+ const isPlatformBilled = usage?.billing_unit === PLATFORM_CREDIT_UNIT;
249
258
  const credits = rawCost !== void 0 && rawCost > 0 ? isPlatformBilled ? rawCost : rawCost * API_REPORTED_COST_MULTIPLIER : void 0;
250
259
  return {
251
260
  ...parsed,
@@ -254,6 +263,31 @@ function buildTurnTokenUsage(usage, model) {
254
263
  ...credits !== void 0 ? { credits } : {}
255
264
  };
256
265
  }
266
+ function sumPlatformCredits(records) {
267
+ if (!records?.length) return void 0;
268
+ let total = 0;
269
+ let sawPlatformCall = false;
270
+ for (const record of records) {
271
+ const credits = platformCreditsFromUsage(record?.usage);
272
+ if (credits === void 0) continue;
273
+ sawPlatformCall = true;
274
+ total += credits;
275
+ }
276
+ return sawPlatformCall ? total : void 0;
277
+ }
278
+ function platformCreditsFromUsage(usage) {
279
+ if (!usage || usage.billing_unit !== PLATFORM_CREDIT_UNIT) return void 0;
280
+ const cost = typeof usage.cost === "number" ? usage.cost : 0;
281
+ return Number.isFinite(cost) && cost > 0 ? cost : 0;
282
+ }
283
+ function withTurnCredits(tokens, credits) {
284
+ if (!tokens) return tokens;
285
+ if (credits === void 0) return tokens;
286
+ return { ...tokens, credits };
287
+ }
288
+ function formatCreditsChip(credits, options) {
289
+ return options?.compact ? `\u26A1${credits.toFixed(2)}` : `\u26A1 ${credits.toFixed(2)} \u79EF\u5206`;
290
+ }
257
291
  function formatTokenCount(value) {
258
292
  if (!Number.isFinite(value) || value < 0) return "\u2014";
259
293
  if (value < 1e3) return String(Math.round(value));
@@ -1240,13 +1274,6 @@ function setActiveThemeMode(mode) {
1240
1274
  function setActiveBrightness(brightness) {
1241
1275
  setActiveThemeMode(brightness ?? "terminal");
1242
1276
  }
1243
- var activeDensity = "spacious";
1244
- function getActiveDensity() {
1245
- return activeDensity;
1246
- }
1247
- function setActiveDensity(density) {
1248
- activeDensity = density;
1249
- }
1250
1277
  var TRUECOLOR_TERM_PROGRAMS = /* @__PURE__ */ new Set([
1251
1278
  "iterm.app",
1252
1279
  "wezterm",
@@ -2915,6 +2942,14 @@ function normalizeListLine(line) {
2915
2942
  }
2916
2943
  return line;
2917
2944
  }
2945
+ function isPolishListLikeLine(line) {
2946
+ return LIST_LIKE_RE_FOR_GUARD.test(normalizeListLine(line));
2947
+ }
2948
+ var LIST_LIKE_RE_FOR_GUARD = /^\s*(?:•|☐|☑|\d+\.)\s|^\s*[\u2460-\u2473]|^\s*[├└]──\s/;
2949
+ function polishBreathInsertsBlankBetween(cur, next) {
2950
+ if (cur === "" || next === "") return false;
2951
+ return isPolishListLikeLine(cur) !== isPolishListLikeLine(next);
2952
+ }
2918
2953
  function convertMarkdownTablesForTerminal(text) {
2919
2954
  const lines = text.split("\n");
2920
2955
  const out = [];
@@ -2982,7 +3017,7 @@ function polishAssistantStructure(text, options = {}) {
2982
3017
  return line;
2983
3018
  });
2984
3019
  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");
2985
- const LIST_LIKE = /^\s*(?:•|☐|☑|\d+\.)\s|^\s*[\u2460-\u2473]/;
3020
+ const LIST_LIKE = /^\s*(?:•|☐|☑|\d+\.)\s|^\s*[\u2460-\u2473]|^\s*[├└]──\s/;
2986
3021
  const headingLines = afterHeading.split("\n");
2987
3022
  const breathed = [];
2988
3023
  for (let i = 0; i < headingLines.length; i++) {
@@ -3340,130 +3375,16 @@ function formatAssistantTextForCli(text) {
3340
3375
  return text.replace(/\u003cthink\u003e[\s\S]*?\u003c\/think\u003e\s*/gi, "").replace(/\n{3,}/g, "\n\n").trim();
3341
3376
  }
3342
3377
 
3343
- // packages/cli/client/formatReadPathTree.ts
3344
- import { homedir as osHomedir } from "node:os";
3345
-
3346
- // packages/core/foldHomePath.ts
3347
- function foldHomePath(path, home) {
3348
- if (!path) return path;
3349
- if (home) {
3350
- const cleanHome = home.endsWith("/") ? home.slice(0, -1) : home;
3351
- if (path === cleanHome) return "~";
3352
- if (path.startsWith(cleanHome + "/")) return `~${path.slice(cleanHome.length)}`;
3353
- }
3354
- return path.replace(/^(\/Users\/[^/]+|\/home\/[^/]+)(\/|$)/, "~$2");
3355
- }
3356
-
3357
- // packages/cli/client/formatReadPathTree.ts
3358
- function clipPathAware(value, max = 72) {
3359
- const compact = compactWhitespace(value);
3360
- if (compact.length <= max) return compact;
3361
- if (compact.includes(" ") || !compact.includes("/") && !compact.includes("\\")) {
3362
- return clipCompactText(value, max, "\u2026");
3363
- }
3364
- const segments = compact.split(/[/\\]/);
3365
- const filename = segments[segments.length - 1];
3366
- if (filename.length >= max) {
3367
- return clipCompactText(value, max, "\u2026");
3368
- }
3369
- const ELISION = "/\u2026/";
3370
- let prefix = "";
3371
- for (let i = 0; i < segments.length - 1; i += 1) {
3372
- const candidate = segments.slice(0, i + 1).join("/");
3373
- if (candidate.length + ELISION.length + filename.length > max) break;
3374
- prefix = candidate;
3375
- }
3376
- if (prefix) return `${prefix}${ELISION}${filename}`;
3377
- const filenameOnly = `\u2026/${filename}`;
3378
- if (filenameOnly.length <= max) return filenameOnly;
3379
- return clipCompactText(value, max, "\u2026");
3380
- }
3381
- function formatHomePath(path, customHomedir) {
3382
- if (!path) return "";
3383
- const home = customHomedir || (typeof process !== "undefined" ? process.env?.HOME : void 0) || (typeof osHomedir === "function" ? osHomedir() : "");
3384
- return foldHomePath(path, home);
3385
- }
3386
- function formatReadItemPath(rawPath, metadata, customHomedir) {
3387
- const inputPath = rawPath || (typeof metadata?.path === "string" ? metadata.path : void 0) || (typeof metadata?.filePath === "string" ? metadata.filePath : void 0) || "file";
3388
- let formattedPath = formatHomePath(inputPath, customHomedir);
3389
- if (!formattedPath) return "file";
3390
- let rangePart = "";
3391
- const match = formattedPath.match(/(:[0-9]+(-[0-9]+)?(,[0-9]+(-[0-9]+)?)*)$/);
3392
- if (match) {
3393
- rangePart = match[1];
3394
- formattedPath = formattedPath.slice(0, -rangePart.length);
3395
- } else if (metadata && typeof metadata.startLine === "number" && typeof metadata.endLine === "number" && typeof metadata.totalLines === "number") {
3396
- const { startLine, endLine, totalLines, truncated } = metadata;
3397
- if (truncated || startLine !== 1 || endLine !== totalLines) {
3398
- rangePart = `:${startLine}-${endLine}`;
3399
- }
3400
- }
3401
- const clippedPath = clipPathAware(formattedPath, 72);
3402
- return `${clippedPath}${rangePart}`;
3403
- }
3404
- function formatReadTreeLines(items, customHomedir) {
3405
- const count = items.length;
3406
- const header = `Read (${count})`;
3407
- const lines = items.map((item, index) => {
3408
- const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
3409
- const pathWithRange = formatReadItemPath(item.path, item.metadata, customHomedir);
3410
- return { connector, pathWithRange };
3411
- });
3412
- return { header, count, lines };
3413
- }
3414
- function formatSearchItemQuery(query, path, customHomedir) {
3415
- let text = (query || "").trim();
3416
- if (path) {
3417
- const formattedPath = formatHomePath(path, customHomedir);
3418
- if (formattedPath) {
3419
- text = text ? `${text} in ${formattedPath}` : formattedPath;
3420
- }
3421
- }
3422
- if (!text) return "search";
3423
- return text.length > 80 ? `${text.slice(0, 79)}\u2026` : text;
3424
- }
3425
- function formatSearchTreeLines(items, customHomedir) {
3426
- const count = items.length;
3427
- const header = `Search (${count})`;
3428
- const lines = items.map((item, index) => {
3429
- const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
3430
- const queryText = formatSearchItemQuery(item.query, item.path, customHomedir);
3431
- return { connector, queryText };
3432
- });
3433
- return { header, count, lines };
3434
- }
3435
- function formatRunItemCommand(command, exitCode, timedOut) {
3436
- let text = compactWhitespace(command || "");
3437
- if (!text) text = "command";
3438
- text = text.length > 80 ? `${text.slice(0, 79)}\u2026` : text;
3439
- if (timedOut) return `${text} (timed out)`;
3440
- if (typeof exitCode === "number" && exitCode !== 0) return `${text} (exit ${exitCode})`;
3441
- return text;
3442
- }
3443
- function formatRunTreeLines(items) {
3444
- const count = items.length;
3445
- const header = `Run (${count})`;
3446
- const lines = items.map((item, index) => {
3447
- const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
3448
- const commandText = formatRunItemCommand(item.command, item.exitCode, item.timedOut);
3449
- return { connector, commandText };
3450
- });
3451
- return { header, count, lines };
3452
- }
3453
- function formatFetchItemUrl(url) {
3454
- const text = (url || "").trim();
3455
- if (!text) return "webpage";
3456
- return text.length > 80 ? `${text.slice(0, 79)}\u2026` : text;
3457
- }
3458
- function formatFetchTreeLines(items) {
3459
- const count = items.length;
3460
- const header = `Fetch (${count})`;
3461
- const lines = items.map((item, index) => {
3462
- const connector = index === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 ";
3463
- const urlText = formatFetchItemUrl(item.url);
3464
- return { connector, urlText };
3465
- });
3466
- return { header, count, lines };
3378
+ // packages/cli/tui/redactSecrets.ts
3379
+ var REDACTED = "\u27E8redacted\u27E9";
3380
+ function redactSecrets(text) {
3381
+ return text.replace(/\b(?:sk|pk)-[A-Za-z0-9_-]{10,}/g, REDACTED).replace(/\b(Bearer\s+)[A-Za-z0-9._~+/=-]{12,}/gi, `$1${REDACTED}`).replace(
3382
+ /\b((?:api[_-]?key|apikey|access[_-]?token|auth[_-]?token|token|secret|password|passwd|credential)s?\s*[=:]\s*["']?)[^\s"',;]{8,}/gi,
3383
+ `$1${REDACTED}`
3384
+ ).replace(
3385
+ /\b((?:--[a-z-]*(?:token|key|secret|password|credential)[a-z-]*)\s+)[^\s"']{8,}/gi,
3386
+ `$1${REDACTED}`
3387
+ );
3467
3388
  }
3468
3389
 
3469
3390
  // node_modules/diff/libesm/diff/base.js
@@ -3716,186 +3637,110 @@ function tokenize(value, options) {
3716
3637
  return retLines;
3717
3638
  }
3718
3639
 
3719
- // packages/cli/client/toolOutput.ts
3720
- function normalizeToolDisplayMode(raw, fallback = "normal") {
3721
- const normalized = asTrimmedLowercaseString(raw);
3722
- if (normalized === "hide" || normalized === "off" || normalized === "false" || normalized === "0") {
3723
- return "hide";
3640
+ // packages/cli/client/formatReadPathTree.ts
3641
+ import { homedir as osHomedir } from "node:os";
3642
+
3643
+ // packages/core/foldHomePath.ts
3644
+ function foldHomePath(path, home) {
3645
+ if (!path) return path;
3646
+ if (home) {
3647
+ const cleanHome = home.endsWith("/") ? home.slice(0, -1) : home;
3648
+ if (path === cleanHome) return "~";
3649
+ if (path.startsWith(cleanHome + "/")) return `~${path.slice(cleanHome.length)}`;
3724
3650
  }
3725
- if (normalized === "verbose" || normalized === "debug" || normalized === "trace" || normalized === "full") {
3726
- return "verbose";
3651
+ return path.replace(/^(\/Users\/[^/]+|\/home\/[^/]+)(\/|$)/, "~$2");
3652
+ }
3653
+
3654
+ // packages/cli/client/formatReadPathTree.ts
3655
+ function clipPathAware(value, max = 72) {
3656
+ const compact = compactWhitespace(value);
3657
+ if (compact.length <= max) return compact;
3658
+ if (compact.includes(" ") || !compact.includes("/") && !compact.includes("\\")) {
3659
+ return clipCompactText(value, max, "\u2026");
3727
3660
  }
3728
- if (normalized === "normal" || normalized === "summary" || normalized === "on") {
3729
- return "normal";
3661
+ const segments = compact.split(/[/\\]/);
3662
+ const filename = segments[segments.length - 1];
3663
+ if (filename.length >= max) {
3664
+ return clipCompactText(value, max, "\u2026");
3730
3665
  }
3731
- if (normalized === "pro" || normalized === "compact" || normalized === "minimal" || normalized === "short") {
3732
- return "pro";
3666
+ const ELISION = "/\u2026/";
3667
+ let prefix = "";
3668
+ for (let i = 0; i < segments.length - 1; i += 1) {
3669
+ const candidate = segments.slice(0, i + 1).join("/");
3670
+ if (candidate.length + ELISION.length + filename.length > max) break;
3671
+ prefix = candidate;
3733
3672
  }
3734
- return fallback;
3673
+ if (prefix) return `${prefix}${ELISION}${filename}`;
3674
+ const filenameOnly = `\u2026/${filename}`;
3675
+ if (filenameOnly.length <= max) return filenameOnly;
3676
+ return clipCompactText(value, max, "\u2026");
3677
+ }
3678
+ function formatHomePath(path, customHomedir) {
3679
+ if (!path) return "";
3680
+ const home = customHomedir || (typeof process !== "undefined" ? process.env?.HOME : void 0) || (typeof osHomedir === "function" ? osHomedir() : "");
3681
+ return foldHomePath(path, home);
3735
3682
  }
3736
- function resolveToolDisplayMode(env = process.env) {
3737
- const legacyTrace = asTrimmedLowercaseString(env.NOLO_TRACE_TOOLS);
3738
- if (legacyTrace === "0" || legacyTrace === "false" || legacyTrace === "off") {
3739
- return "hide";
3683
+ function formatReadItemPath(rawPath, metadata, customHomedir) {
3684
+ const inputPath = rawPath || (typeof metadata?.path === "string" ? metadata.path : void 0) || (typeof metadata?.filePath === "string" ? metadata.filePath : void 0) || "file";
3685
+ let formattedPath = formatHomePath(inputPath, customHomedir);
3686
+ if (!formattedPath) return "file";
3687
+ let rangePart = "";
3688
+ const match = formattedPath.match(/(:[0-9]+(-[0-9]+)?(,[0-9]+(-[0-9]+)?)*)$/);
3689
+ if (match) {
3690
+ rangePart = match[1];
3691
+ formattedPath = formattedPath.slice(0, -rangePart.length);
3692
+ } else if (metadata && typeof metadata.startLine === "number" && typeof metadata.endLine === "number" && typeof metadata.totalLines === "number") {
3693
+ const { startLine, endLine, totalLines, truncated } = metadata;
3694
+ if (truncated || startLine !== 1 || endLine !== totalLines) {
3695
+ rangePart = `:${startLine}-${endLine}`;
3696
+ }
3740
3697
  }
3741
- if (legacyTrace === "verbose" || legacyTrace === "full") {
3742
- return "verbose";
3698
+ const clippedPath = clipPathAware(formattedPath, 72);
3699
+ return `${clippedPath}${rangePart}`;
3700
+ }
3701
+ function formatSearchItemQuery(query, path, customHomedir) {
3702
+ let text = (query || "").trim();
3703
+ if (path) {
3704
+ const formattedPath = formatHomePath(path, customHomedir);
3705
+ if (formattedPath) {
3706
+ text = text ? `${text} in ${formattedPath}` : formattedPath;
3707
+ }
3743
3708
  }
3744
- return normalizeToolDisplayMode(env.NOLO_CLI_TOOLS ?? env.NOLO_TOOLS, "normal");
3709
+ if (!text) return "search";
3710
+ return text.length > 80 ? `${text.slice(0, 79)}\u2026` : text;
3711
+ }
3712
+ function formatRunItemCommand(command, exitCode, timedOut) {
3713
+ let text = compactWhitespace(command || "");
3714
+ if (!text) text = "command";
3715
+ text = text.length > 80 ? `${text.slice(0, 79)}\u2026` : text;
3716
+ if (timedOut) return `${text} (timed out)`;
3717
+ if (typeof exitCode === "number" && exitCode !== 0) return `${text} (exit ${exitCode})`;
3718
+ return text;
3745
3719
  }
3746
- function shouldEmitToolEvents(mode) {
3747
- return mode !== "hide";
3720
+ function formatFetchItemUrl(url) {
3721
+ const text = (url || "").trim();
3722
+ if (!text) return "webpage";
3723
+ return text.length > 80 ? `${text.slice(0, 79)}\u2026` : text;
3748
3724
  }
3725
+
3726
+ // packages/cli/client/toolOutput.ts
3749
3727
  function clip(value, max = 72) {
3750
3728
  return clipCompactText(value, max, "\u2026");
3751
3729
  }
3752
- function formatActiveToolLabel(event) {
3753
- const toolName = event.toolName || "tool";
3754
- const args = clipPathAware(event.argumentsPreview ?? "");
3755
- const label = toolLabel(toolName);
3756
- return args ? `${label} ${args}` : label;
3757
- }
3758
3730
  function formatConservativeActiveToolLabel(event) {
3759
3731
  return toolLabel(event.toolName || "tool");
3760
3732
  }
3761
- function compactResultHint(event) {
3762
- const gate = readActionGate(event.metadata?.actionGate);
3763
- if (gate) {
3764
- const commandPayload = gate.kind === "handoff" ? readCommandActionGatePayload(gate.payload) : null;
3765
- const command = commandPayload?.displayCommand ?? commandPayload?.command.join(" ") ?? "";
3766
- const detail = command.trim() ? command : gate.title;
3767
- return { inline: `${t("toolNeedsAction")}: ${clip(detail, 120)}` };
3768
- }
3769
- if (event.metadata?.timedOut) return { inline: t("toolTimedOut") };
3770
- if (event.toolName === "readFile" && event.metadata) {
3771
- const range = readReadFileRange(event.metadata);
3772
- if (range) return { inline: range };
3773
- }
3774
- if (event.toolName === "editFile" && event.metadata) {
3775
- const res = formatEditFileSnippet(event.metadata);
3776
- if (res) {
3777
- const stats = [];
3778
- if (res.addedCount > 0) stats.push(`+${res.addedCount}`);
3779
- if (res.removedCount > 0) stats.push(`-${res.removedCount}`);
3780
- const inline = stats.length > 0 ? `(${stats.join(", ")})` : "";
3781
- return { inline, detail: res.lines };
3782
- }
3783
- }
3784
- if (event.toolName === "writeFile" && event.metadata) {
3785
- const totalLines = event.metadata.totalLines ?? event.metadata.lines;
3786
- if (typeof totalLines === "number" && totalLines > 0) {
3787
- return { inline: `(+${totalLines})` };
3788
- }
3789
- }
3790
- const summary = event.summary;
3791
- if (!summary) return { inline: "" };
3792
- const exitMatch = summary.match(/exit=(\d+)/);
3793
- if (exitMatch && exitMatch[1] !== "0") return { inline: `${t("toolExitCode")} ${exitMatch[1]}` };
3794
- return { inline: "" };
3795
- }
3796
- function readReadFileRange(metadata) {
3797
- const startLine = metadata.startLine;
3798
- const endLine = metadata.endLine;
3799
- const totalLines = metadata.totalLines;
3800
- const truncated = metadata.truncated;
3801
- if (typeof startLine !== "number" || typeof endLine !== "number" || typeof totalLines !== "number") {
3802
- return void 0;
3803
- }
3804
- if (!truncated && startLine === 1 && endLine === totalLines) return void 0;
3805
- return `${startLine}-${endLine}/${totalLines}`;
3806
- }
3807
- var EDIT_SNIPPET_MAX_LINES = 24;
3808
- var DEFAULT_EDIT_SNIPPET_MAX_WIDTH = 96;
3809
- function resolveEditSnippetMaxWidth(env = process.env, columns) {
3810
- if (env.NOLO_TEST_DIFF_WIDTH) {
3811
- const parsed = parseInt(env.NOLO_TEST_DIFF_WIDTH, 10);
3812
- if (!Number.isNaN(parsed) && parsed > 0) return parsed;
3813
- }
3814
- const cols = typeof columns === "number" ? columns : typeof process !== "undefined" && typeof process.stdout?.columns === "number" ? process.stdout.columns : 0;
3815
- if (cols > 0) {
3816
- return Math.max(80, Math.min(160, cols - 8));
3817
- }
3818
- return DEFAULT_EDIT_SNIPPET_MAX_WIDTH;
3819
- }
3820
- function formatEditFileSnippet(metadata) {
3821
- const oldSnippet = typeof metadata.oldSnippet === "string" ? metadata.oldSnippet : void 0;
3822
- const newSnippet = typeof metadata.newSnippet === "string" ? metadata.newSnippet : void 0;
3823
- if (!oldSnippet && !newSnippet) return void 0;
3824
- const maxWidth = resolveEditSnippetMaxWidth();
3825
- if (oldSnippet && !newSnippet) {
3826
- const lines = snippetLinesWithKind(oldSnippet, "removed", maxWidth);
3827
- return { lines, addedCount: 0, removedCount: lines.length };
3828
- }
3829
- if (newSnippet && !oldSnippet) {
3830
- const lines = snippetLinesWithKind(newSnippet, "added", maxWidth);
3831
- return { lines, addedCount: lines.length, removedCount: 0 };
3832
- }
3833
- const parts = diffLines(oldSnippet, newSnippet);
3834
- const all = [];
3835
- let addedCount = 0;
3836
- let removedCount = 0;
3837
- for (const part of parts) {
3838
- const kind = part.added ? "added" : part.removed ? "removed" : "context";
3839
- const raw = part.value.split("\n");
3840
- if (raw.length > 0 && raw[raw.length - 1] === "") raw.pop();
3841
- for (const line of raw) {
3842
- if (kind === "added") addedCount++;
3843
- if (kind === "removed") removedCount++;
3844
- all.push(buildEditLine(kind, line, maxWidth));
3845
- }
3846
- }
3847
- const changeCount = all.filter((l) => l.kind !== "context").length;
3848
- if (changeCount === 0) return void 0;
3849
- return {
3850
- lines: windowAndTruncate(all, EDIT_SNIPPET_MAX_LINES),
3851
- addedCount,
3852
- removedCount
3853
- };
3854
- }
3855
- function prefixFor(kind) {
3856
- return kind === "added" ? "+ " : kind === "removed" ? "- " : " ";
3733
+ function isRunToolName(name) {
3734
+ return name === "execShell" || name === "runCommand" || name === "launchProcess";
3857
3735
  }
3858
- function buildEditLine(kind, body, maxWidth = resolveEditSnippetMaxWidth()) {
3859
- return { kind, text: prefixFor(kind) + truncateByDisplayWidth(body, maxWidth) };
3736
+ function isReadToolName(name) {
3737
+ return name === "readFile";
3860
3738
  }
3861
- function snippetLinesWithKind(snippet, kind, maxWidth = resolveEditSnippetMaxWidth()) {
3862
- const lines = snippet.split(/\r?\n/).filter((line) => line.length > 0);
3863
- return lines.slice(0, EDIT_SNIPPET_MAX_LINES).map((line) => buildEditLine(kind, line, maxWidth));
3864
- }
3865
- function truncateByDisplayWidth(line, maxWidth) {
3866
- if (displayWidth(line) <= maxWidth) return line;
3867
- let width = 0;
3868
- let kept = "";
3869
- for (const char of line) {
3870
- const w = displayWidth(char);
3871
- if (width + w + 1 > maxWidth) break;
3872
- kept += char;
3873
- width += w;
3874
- }
3875
- return `${kept}\u2026`;
3876
- }
3877
- function windowAndTruncate(lines, maxLines) {
3878
- if (lines.length <= maxLines) return lines;
3879
- const firstChange = lines.findIndex((l) => l.kind !== "context");
3880
- if (firstChange === -1) return lines.slice(0, maxLines);
3881
- const half = Math.floor(maxLines / 2);
3882
- let start = Math.max(0, firstChange - half);
3883
- if (start + maxLines > lines.length) start = Math.max(0, lines.length - maxLines);
3884
- const kept = lines.slice(start, start + maxLines);
3885
- const omitted = lines.length - kept.length;
3886
- if (omitted > 0) {
3887
- kept.push({ kind: "context", text: ` \u2026 +${omitted} more lines` });
3888
- }
3889
- return kept;
3890
- }
3891
- function isFailedToolResult(event) {
3892
- const exitCode = event.metadata?.exitCode;
3893
- if (event.metadata?.actionGate) return false;
3894
- if (typeof exitCode === "number" && exitCode !== 0) return true;
3895
- return Boolean(event.metadata?.timedOut);
3896
- }
3897
- function isNeedsActionToolResult(event) {
3898
- return Boolean(event.metadata?.actionGate);
3739
+ function isFailedToolResult(event) {
3740
+ const exitCode = event.metadata?.exitCode;
3741
+ if (event.metadata?.actionGate) return false;
3742
+ if (typeof exitCode === "number" && exitCode !== 0) return true;
3743
+ return Boolean(event.metadata?.timedOut);
3899
3744
  }
3900
3745
  function parseUiAskChoiceForCli(event) {
3901
3746
  if (event.toolName !== "ask_user" && !event.metadata?.uiAskChoice) {
@@ -4013,7 +3858,10 @@ function renderRunCard(snapshot, opts) {
4013
3858
  lastToolNames: snapshot.lastToolNames?.map(cleanUserText)
4014
3859
  } : snapshot;
4015
3860
  const hadError = safe && Boolean(source.errorMessage?.trim());
4016
- const errorFields = safe ? { errorMessage: void 0, logLines: void 0 } : { errorMessage: source.errorMessage, logLines: source.logLines };
3861
+ const errorFields = safe ? {
3862
+ errorMessage: hadError ? clip(redactSecrets(cleanUserText(source.errorMessage)), 160) : void 0,
3863
+ logLines: void 0
3864
+ } : { errorMessage: source.errorMessage, logLines: source.logLines };
4017
3865
  const body = isAgentRunTerminalStatus(snapshot.status) ? formatFinishedRunCard(name, snapshot.status, {
4018
3866
  runId: snapshot.runId,
4019
3867
  toolCallCount: source.toolCallCount,
@@ -4033,8 +3881,7 @@ function renderRunCard(snapshot, opts) {
4033
3881
  ...opts?.includeLogTail !== void 0 ? { includeLogTail: opts.includeLogTail } : {},
4034
3882
  labels
4035
3883
  });
4036
- return hadError ? `${body}
4037
- error ${t("runDiagnosticsHidden")}` : body;
3884
+ return body;
4038
3885
  }
4039
3886
  function recoverOrchestrationCard(event, toolName, opts) {
4040
3887
  const safe = opts?.safe === true;
@@ -4080,10 +3927,6 @@ function recoverOrchestrationCard(event, toolName, opts) {
4080
3927
  if (kind === "stop") return formatStopRunCard(snapshot.status, labels);
4081
3928
  return renderRunCard(snapshot, opts);
4082
3929
  }
4083
- function parseControlAgentRunStatusEvent(event) {
4084
- const parsed = parseAgentRunEvent(event);
4085
- return parsed?.kind === "status" ? parsed.snapshot : null;
4086
- }
4087
3930
  function formatOrchestrationCardBlock(event, toolName, colorEnabled, opts) {
4088
3931
  const safe = opts?.safe === true;
4089
3932
  const rawLabel = toolLabel(toolName);
@@ -4091,8 +3934,8 @@ function formatOrchestrationCardBlock(event, toolName, colorEnabled, opts) {
4091
3934
  const rawDataStr = recoverOrchestrationCard(event, toolName, { safe }) || bakedCard || (typeof event.content === "string" ? event.content : "");
4092
3935
  const failed = event.type === "tool-error" || isFailedToolResult(event) || Boolean(event.metadata?.failed) || /^Error:/i.test(rawDataStr.trim());
4093
3936
  if (failed) {
4094
- const firstLine = safe ? t("toolFailed") : rawDataStr.trim().split("\n")[0] || event.summary || event.message || t("toolFailed");
4095
- const message = clip(firstLine, 96);
3937
+ const firstLine = stripAnsi2(rawDataStr.trim().split("\n")[0] || event.summary || event.message || t("toolFailed"));
3938
+ const message = clip(redactSecrets(firstLine), 96);
4096
3939
  if (!colorEnabled) {
4097
3940
  return `\u2717 ${rawLabel} ${message}
4098
3941
  `;
@@ -4134,7 +3977,7 @@ function sanitizeRunCardForNormal(lines) {
4134
3977
  break;
4135
3978
  }
4136
3979
  if (/^\s*error\b/i.test(line)) {
4137
- out.push(` error ${t("runDiagnosticsHidden")}`);
3980
+ out.push(clip(redactSecrets(stripAnsi2(line)), 160));
4138
3981
  errorIndent = line.length - line.trimStart().length;
4139
3982
  continue;
4140
3983
  }
@@ -4153,59 +3996,207 @@ function sanitizeRunCardForNormal(lines) {
4153
3996
  }
4154
3997
  return out;
4155
3998
  }
4156
- function mergeRunSnapshot(previous, next) {
4157
- return {
4158
- ...previous,
4159
- ...next,
4160
- errorMessage: next.errorMessage ?? previous.errorMessage,
4161
- logLines: next.logLines ?? previous.logLines,
4162
- logKey: next.logKey || previous.logKey,
4163
- toolCallCount: next.toolCallCount ?? previous.toolCallCount,
4164
- lastToolNames: next.lastToolNames ?? previous.lastToolNames,
4165
- lastAssistantText: next.lastAssistantText ?? previous.lastAssistantText,
4166
- startedAt: next.startedAt ?? previous.startedAt,
4167
- finishedAt: next.finishedAt ?? previous.finishedAt
4168
- };
3999
+ var NORMAL_TOOL_GIST_MAX = 28;
4000
+ function gistClip(value, max = NORMAL_TOOL_GIST_MAX) {
4001
+ const oneLine = value.replace(/\s+/g, " ").trim();
4002
+ if (!oneLine) return "";
4003
+ return oneLine.length <= max ? oneLine : `${oneLine.slice(0, max - 1)}\u2026`;
4169
4004
  }
4170
- function formatFoldedControlStatusCard(snapshot, colorEnabled) {
4171
- const body = renderRunCard(snapshot, { includeLogTail: snapshot.includeLogTail });
4172
- return formatOrchestrationCardBlock(
4173
- {
4174
- type: "tool-result",
4175
- round: 0,
4176
- toolCallId: `fold-${snapshot.runId}`,
4177
- toolName: "controlAgentRun",
4178
- content: "",
4179
- metadata: { displayData: body }
4180
- },
4181
- "controlAgentRun",
4182
- colorEnabled
4183
- );
4005
+ var DEFAULT_RUN_GIST_MAX = 96;
4006
+ function normalRunGistMaxWidth(env = process.env, columns) {
4007
+ const pinned = parseInt(env.NOLO_TEST_RUN_GIST_WIDTH ?? "", 10);
4008
+ if (!Number.isNaN(pinned) && pinned > 0) return pinned;
4009
+ const cols = typeof columns === "number" ? columns : typeof process !== "undefined" && typeof process.stdout?.columns === "number" ? process.stdout.columns : 0;
4010
+ if (cols > 0) {
4011
+ return Math.max(48, Math.min(160, cols - 32));
4012
+ }
4013
+ return DEFAULT_RUN_GIST_MAX;
4184
4014
  }
4185
- function formatVerboseToolEvent(event, colorEnabled) {
4186
- const round = event.round + 1;
4187
- const detail = event.argumentsPreview ? ` ${event.argumentsPreview}` : "";
4188
- if (event.type === "tool-call") {
4189
- return formatToolTraceLine(`[nolo:tool] #${round} -> ${event.toolName}${detail}`, colorEnabled);
4015
+ function commandFullGist(command, max) {
4016
+ const oneLine = command.replace(/\s+/g, " ").trim();
4017
+ if (!oneLine) return "";
4018
+ return truncateByDisplayWidth(redactSecrets(oneLine), max);
4019
+ }
4020
+ function pathBasenameGist(path) {
4021
+ const segments = path.split(/[\\/]/).filter((segment) => segment.length > 0);
4022
+ const base = segments.pop();
4023
+ return base ? gistClip(base) : "";
4024
+ }
4025
+ var EDIT_SNIPPET_MAX_LINES = 24;
4026
+ var DEFAULT_EDIT_SNIPPET_MAX_WIDTH = 96;
4027
+ function resolveEditSnippetMaxWidth(env = process.env, columns) {
4028
+ if (env.NOLO_TEST_DIFF_WIDTH) {
4029
+ const parsed = parseInt(env.NOLO_TEST_DIFF_WIDTH, 10);
4030
+ if (!Number.isNaN(parsed) && parsed > 0) return parsed;
4190
4031
  }
4191
- if (event.type === "tool-error") {
4192
- const elapsed2 = typeof event.elapsedMs === "number" ? ` ${event.elapsedMs}ms` : "";
4193
- return formatToolTraceLine(
4194
- `[nolo:tool] #${round} !! ${event.toolName}${elapsed2}: ${event.message ?? "failed"}`,
4195
- colorEnabled,
4196
- "error"
4197
- );
4032
+ const cols = typeof columns === "number" ? columns : typeof process !== "undefined" && typeof process.stdout?.columns === "number" ? process.stdout.columns : 0;
4033
+ if (cols > 0) {
4034
+ return Math.max(80, Math.min(160, cols - 8));
4198
4035
  }
4199
- const elapsed = typeof event.elapsedMs === "number" ? ` ${event.elapsedMs}ms` : "";
4200
- if (event.type === "tool-result" && (event.toolName === "ask_user" || event.metadata?.uiAskChoice)) {
4201
- const block = formatUiAskChoiceBlock(event, colorEnabled);
4202
- if (block) return `${formatToolTraceLine(`[nolo:tool] #${round} <- ${event.toolName}${elapsed}`, colorEnabled)}${block}`;
4036
+ return DEFAULT_EDIT_SNIPPET_MAX_WIDTH;
4037
+ }
4038
+ function prefixFor(kind) {
4039
+ return kind === "added" ? "+ " : kind === "removed" ? "- " : " ";
4040
+ }
4041
+ function buildEditLine(kind, body, maxWidth = resolveEditSnippetMaxWidth()) {
4042
+ return { kind, text: prefixFor(kind) + truncateByDisplayWidth(body, maxWidth) };
4043
+ }
4044
+ function snippetLinesWithKind(snippet, kind, maxWidth = resolveEditSnippetMaxWidth()) {
4045
+ const lines = snippet.split(/\r?\n/);
4046
+ if (lines.at(-1) === "") lines.pop();
4047
+ return lines.slice(0, EDIT_SNIPPET_MAX_LINES).map((line) => buildEditLine(kind, line, maxWidth));
4048
+ }
4049
+ function truncateByDisplayWidth(line, maxWidth) {
4050
+ if (displayWidth(line) <= maxWidth) return line;
4051
+ let width = 0;
4052
+ let kept = "";
4053
+ for (const char of line) {
4054
+ const w = displayWidth(char);
4055
+ if (width + w + 1 > maxWidth) break;
4056
+ kept += char;
4057
+ width += w;
4203
4058
  }
4204
- const summary = event.summary ? ` ${event.summary}` : "";
4205
- return formatToolTraceLine(
4206
- `[nolo:tool] #${round} <- ${event.toolName}${elapsed}${summary}`,
4207
- colorEnabled
4208
- );
4059
+ return `${kept}\u2026`;
4060
+ }
4061
+ function windowAndTruncate(lines, maxLines) {
4062
+ if (lines.length <= maxLines) return lines;
4063
+ const firstChange = lines.findIndex((l) => l.kind !== "context");
4064
+ if (firstChange === -1) return lines.slice(0, maxLines);
4065
+ const half = Math.floor(maxLines / 2);
4066
+ let start = Math.max(0, firstChange - half);
4067
+ if (start + maxLines > lines.length) start = Math.max(0, lines.length - maxLines);
4068
+ const kept = lines.slice(start, start + maxLines);
4069
+ const omitted = lines.length - kept.length;
4070
+ if (omitted > 0) {
4071
+ kept.push({ kind: "context", text: ` \u2026 +${omitted} more lines` });
4072
+ }
4073
+ return kept;
4074
+ }
4075
+ function formatEditFileSnippet(metadata) {
4076
+ const oldSnippet = typeof metadata.oldSnippet === "string" ? metadata.oldSnippet : void 0;
4077
+ const newSnippet = typeof metadata.newSnippet === "string" ? metadata.newSnippet : void 0;
4078
+ if (!oldSnippet && !newSnippet) return void 0;
4079
+ const maxWidth = resolveEditSnippetMaxWidth();
4080
+ if (oldSnippet && !newSnippet) {
4081
+ const lines = snippetLinesWithKind(oldSnippet, "removed", maxWidth);
4082
+ return { lines, addedCount: 0, removedCount: lines.length };
4083
+ }
4084
+ if (newSnippet && !oldSnippet) {
4085
+ const lines = snippetLinesWithKind(newSnippet, "added", maxWidth);
4086
+ return { lines, addedCount: lines.length, removedCount: 0 };
4087
+ }
4088
+ const parts = diffLines(oldSnippet, newSnippet);
4089
+ const all = [];
4090
+ let addedCount = 0;
4091
+ let removedCount = 0;
4092
+ for (const part of parts) {
4093
+ const kind = part.added ? "added" : part.removed ? "removed" : "context";
4094
+ const bodyLines = part.value.replace(/\n$/, "").split("\n");
4095
+ for (const line of bodyLines) {
4096
+ if (kind === "added") addedCount++;
4097
+ if (kind === "removed") removedCount++;
4098
+ all.push(buildEditLine(kind, line, maxWidth));
4099
+ }
4100
+ }
4101
+ return {
4102
+ lines: windowAndTruncate(all, EDIT_SNIPPET_MAX_LINES),
4103
+ addedCount,
4104
+ removedCount
4105
+ };
4106
+ }
4107
+ function editFileResultHint(event) {
4108
+ if (event.toolName !== "editFile" || !event.metadata) return null;
4109
+ const res = formatEditFileSnippet(event.metadata);
4110
+ if (!res) return null;
4111
+ const stats = [];
4112
+ if (res.addedCount > 0) stats.push(`+${res.addedCount}`);
4113
+ if (res.removedCount > 0) stats.push(`-${res.removedCount}`);
4114
+ const inline = stats.length > 0 ? `(${stats.join(", ")})` : "";
4115
+ return { inline, detail: res.lines };
4116
+ }
4117
+ function formatEditDetailBlock(lines, colorEnabled, filePath, env = process.env) {
4118
+ if (!colorEnabled) {
4119
+ return lines.map((line) => ` ${line.text}
4120
+ `).join("");
4121
+ }
4122
+ const isTruecolor = supportsTruecolor(env);
4123
+ const lang = isTruecolor ? detectCodeLangFromPath(filePath) : "unknown";
4124
+ const brightness = resolveTuiBrightness(env);
4125
+ const padTo = Math.max(...lines.map((line) => displayWidth(line.text))) + 1;
4126
+ return lines.map((line) => ` ${renderDiffLine({
4127
+ kind: line.kind,
4128
+ text: line.text,
4129
+ highlightedText: shouldHighlightEditLine(line, lang, isTruecolor) ? buildHighlightedEditLine(line, lang, brightness, env) : void 0,
4130
+ padTo,
4131
+ colorEnabled: true,
4132
+ env
4133
+ })}
4134
+ `).join("");
4135
+ }
4136
+ function shouldHighlightEditLine(line, lang, isTruecolor) {
4137
+ if (!isTruecolor || lang === "unknown") return false;
4138
+ if (line.text.startsWith(" \u2026") || line.text.startsWith("\u2026")) return false;
4139
+ return line.text.length >= 2;
4140
+ }
4141
+ function buildHighlightedEditLine(line, lang, brightness, env) {
4142
+ const prefix = line.text.slice(0, 2);
4143
+ const body = line.text.slice(2);
4144
+ const prefixToken = line.kind === "added" ? "success" : line.kind === "removed" ? "danger" : "chrome";
4145
+ const coloredPrefix = themeText(prefix, prefixToken, true, env);
4146
+ const highlightedBody = highlightCodeLine(body, lang, brightness);
4147
+ return `${coloredPrefix}${highlightedBody}`;
4148
+ }
4149
+ function normalToolGistRaw(event) {
4150
+ const metadata = event.metadata ?? {};
4151
+ const toolName = event.toolName || "";
4152
+ if (toolName === "readFile") {
4153
+ const path2 = typeof metadata.path === "string" ? metadata.path : typeof metadata.filePath === "string" ? metadata.filePath : "";
4154
+ if (path2) return gistClip(formatReadItemPath(path2, metadata), 52);
4155
+ }
4156
+ if (toolName === "fetchWebpage") {
4157
+ const url = typeof metadata.url === "string" ? metadata.url : "";
4158
+ if (url) return formatFetchItemUrl(url);
4159
+ }
4160
+ if (toolName === "exa_search") {
4161
+ const query2 = typeof metadata.query === "string" ? metadata.query : typeof metadata.pattern === "string" ? metadata.pattern : "";
4162
+ const path2 = typeof metadata.path === "string" ? metadata.path : void 0;
4163
+ if (query2 || path2) return formatSearchItemQuery(query2, path2);
4164
+ }
4165
+ if (isRunToolName(toolName)) {
4166
+ const command2 = typeof metadata.command === "string" ? metadata.command : "";
4167
+ if (command2) {
4168
+ return commandFullGist(command2, normalRunGistMaxWidth());
4169
+ }
4170
+ }
4171
+ const path = typeof metadata.path === "string" ? metadata.path : "";
4172
+ if (path) return pathBasenameGist(path);
4173
+ const command = typeof metadata.command === "string" ? metadata.command : "";
4174
+ if (command) return commandFullGist(command, normalRunGistMaxWidth());
4175
+ const query = typeof metadata.query === "string" ? metadata.query : "";
4176
+ if (query) return clipCompactText(query, 64, "\u2026");
4177
+ const remembered = event.toolName === "rememberMemory" && typeof metadata.content === "string" ? metadata.content : "";
4178
+ if (remembered) return clipCompactText(redactSecrets(remembered), 64, "\u2026");
4179
+ return "";
4180
+ }
4181
+ var NORMAL_GIST_SECRET_PATTERNS = [
4182
+ /\bbearer\s+[A-Za-z0-9._~+/=-]{8,}/i,
4183
+ /\bsk-(?:ant-)?(?:api)?[0-9a-zA-Z-]{10,}/,
4184
+ /\b(?:ghp|gho|ghu|ghs|ghr)_[A-Za-z0-9]{10,}/,
4185
+ /\bxox[baprs]-[A-Za-z0-9-]{10,}/
4186
+ ];
4187
+ function withholdIfSecretLike(gist) {
4188
+ if (!gist) return "";
4189
+ if (findPotentialSecrets(gist).length > 0) return "";
4190
+ if (NORMAL_GIST_SECRET_PATTERNS.some((pattern) => pattern.test(gist))) return "";
4191
+ return gist;
4192
+ }
4193
+ function normalToolGist(event) {
4194
+ return withholdIfSecretLike(normalToolGistRaw(event));
4195
+ }
4196
+ function formatNormalToolDuration(elapsedMs) {
4197
+ if (typeof elapsedMs !== "number" || !Number.isFinite(elapsedMs) || elapsedMs <= 500) return "";
4198
+ if (elapsedMs < 1e3) return `${Math.round(elapsedMs)}ms`;
4199
+ return `${(elapsedMs / 1e3).toFixed(1)}s`;
4209
4200
  }
4210
4201
  function formatNormalToolLine(event, pending, colorEnabled) {
4211
4202
  if (event.type === "tool-call") return "";
@@ -4227,158 +4218,42 @@ function formatNormalToolLine(event, pending, colorEnabled) {
4227
4218
  return formatToolTraceLine(`\u25B8 ${label} ! ${t("toolNeedsAction")}`, colorEnabled, "error");
4228
4219
  }
4229
4220
  if (event.type === "tool-error") {
4230
- return formatToolTraceLine(`\u25B8 ${label} \u2717 ${t("toolFailed")}`, colorEnabled, "error");
4221
+ return formatToolTraceLine(`\u25B8 ${label} \u2717 ${toolFailureReason(event) || t("toolFailed")}`, colorEnabled, "error");
4231
4222
  }
4232
4223
  if (event.metadata?.timedOut) {
4233
4224
  return formatToolTraceLine(`\u25B8 ${label} \u2717 ${t("toolTimedOut")}`, colorEnabled, "error");
4234
4225
  }
4235
4226
  if (isFailedToolResult(event)) {
4236
- return formatToolTraceLine(`\u25B8 ${label} \u2717 ${t("toolFailed")}`, colorEnabled, "error");
4237
- }
4238
- return formatToolTraceLine(`\u25B8 ${label} \u2713`, colorEnabled);
4239
- }
4240
- function isReadToolName(name) {
4241
- if (!name) return false;
4242
- return name === "read" || name === "readFile" || name === "read_file" || name === "readWorkspaceFile";
4243
- }
4244
- function isSearchToolName(name) {
4245
- if (!name) return false;
4246
- return name === "grep" || name === "codeSearch" || name === "code_search" || name === "globFiles" || name === "glob_files" || name === "glob" || name === "searchWorkspace" || name === "search_workspace" || name === "search_all_spaces" || name === "search";
4247
- }
4248
- function isRunToolName(name) {
4249
- if (!name) return false;
4250
- return name === "execShell" || name === "runCommand" || name === "run_command" || name === "launchProcess";
4251
- }
4252
- function isFetchToolName(name) {
4253
- if (!name) return false;
4254
- return name === "fetchWebpage" || name === "fetch_webpage";
4255
- }
4256
- function isWebSearchToolName(name) {
4257
- if (!name) return false;
4258
- return name === "exa_search" || name === "exaSearch";
4259
- }
4260
- function isRunResultFoldable(event) {
4261
- return Boolean(event.metadata?.actionGate) === false;
4262
- }
4263
- function formatSearchTreeBlockForCli(items, colorEnabled) {
4264
- if (items.length === 0) return "";
4265
- const { count, lines } = formatSearchTreeLines(items);
4266
- const titleText = toolLabel("codeSearch");
4267
- if (!colorEnabled) {
4268
- const headerLine2 = `\u2022 ${titleText} (${count})
4269
- `;
4270
- const treeLines2 = lines.map((l) => ` ${l.connector}${l.queryText}`).join("\n");
4271
- return `${headerLine2}${treeLines2}
4272
- `;
4273
- }
4274
- const bullet = themeText("\u2022", "chrome", true);
4275
- const title = styleCliText(titleText, "bold", true);
4276
- const countText = themeText(`(${count})`, "muted", true);
4277
- const headerLine = `${bullet} ${title} ${countText}
4278
- `;
4279
- const treeLines = lines.map((l) => {
4280
- const connector = themeText(` ${l.connector}`, "chrome", true);
4281
- const queryText = themeText(l.queryText, "muted", true);
4282
- return `${connector}${queryText}`;
4283
- }).join("\n");
4284
- return `${headerLine}${treeLines}
4285
- `;
4286
- }
4287
- function formatReadTreeBlockForCli(items, colorEnabled) {
4288
- if (items.length === 0) return "";
4289
- const { count, lines } = formatReadTreeLines(items);
4290
- const titleText = toolLabel("readFile");
4291
- if (!colorEnabled) {
4292
- const headerLine2 = `\u2022 ${titleText} (${count})
4293
- `;
4294
- const treeLines2 = lines.map((l) => ` ${l.connector}${l.pathWithRange}`).join("\n");
4295
- return `${headerLine2}${treeLines2}
4296
- `;
4297
- }
4298
- const bullet = themeText("\u2022", "chrome", true);
4299
- const title = styleCliText(titleText, "bold", true);
4300
- const countText = themeText(`(${count})`, "muted", true);
4301
- const headerLine = `${bullet} ${title} ${countText}
4302
- `;
4303
- const treeLines = lines.map((l) => {
4304
- const connector = themeText(` ${l.connector}`, "chrome", true);
4305
- const pathText = themeText(l.pathWithRange, "muted", true);
4306
- return `${connector}${pathText}`;
4307
- }).join("\n");
4308
- return `${headerLine}${treeLines}
4309
- `;
4310
- }
4311
- function formatRunTreeBlockForCli(items, colorEnabled) {
4312
- if (items.length === 0) return "";
4313
- const { count, lines } = formatRunTreeLines(items);
4314
- const titleText = toolLabel("execShell");
4315
- if (!colorEnabled) {
4316
- const headerLine2 = `\u2022 ${titleText} (${count})
4317
- `;
4318
- const treeLines2 = lines.map((l) => ` ${l.connector}${l.commandText}`).join("\n");
4319
- return `${headerLine2}${treeLines2}
4320
- `;
4321
- }
4322
- const bullet = themeText("\u2022", "chrome", true);
4323
- const title = styleCliText(titleText, "bold", true);
4324
- const countText = themeText(`(${count})`, "muted", true);
4325
- const headerLine = `${bullet} ${title} ${countText}
4326
- `;
4327
- const treeLines = lines.map((l) => {
4328
- const connector = themeText(` ${l.connector}`, "chrome", true);
4329
- const commandText = themeText(l.commandText, "muted", true);
4330
- return `${connector}${commandText}`;
4331
- }).join("\n");
4332
- return `${headerLine}${treeLines}
4333
- `;
4334
- }
4335
- function formatWebSearchTreeBlockForCli(items, colorEnabled) {
4336
- if (items.length === 0) return "";
4337
- const { count, lines } = formatSearchTreeLines(items);
4338
- const titleText = toolLabel("exa_search");
4339
- if (!colorEnabled) {
4340
- const headerLine2 = `\u2022 ${titleText} (${count})
4341
- `;
4342
- const treeLines2 = lines.map((l) => ` ${l.connector}${l.queryText}`).join("\n");
4343
- return `${headerLine2}${treeLines2}
4344
- `;
4345
- }
4346
- const bullet = themeText("\u2022", "chrome", true);
4347
- const title = styleCliText(titleText, "bold", true);
4348
- const countText = themeText(`(${count})`, "muted", true);
4349
- const headerLine = `${bullet} ${title} ${countText}
4350
- `;
4351
- const treeLines = lines.map((l) => {
4352
- const connector = themeText(` ${l.connector}`, "chrome", true);
4353
- const queryText = themeText(l.queryText, "muted", true);
4354
- return `${connector}${queryText}`;
4355
- }).join("\n");
4356
- return `${headerLine}${treeLines}
4357
- `;
4358
- }
4359
- function formatFetchTreeBlockForCli(items, colorEnabled) {
4360
- if (items.length === 0) return "";
4361
- const { count, lines } = formatFetchTreeLines(items);
4362
- const titleText = toolLabel("fetchWebpage");
4363
- if (!colorEnabled) {
4364
- const headerLine2 = `\u2022 ${titleText} (${count})
4365
- `;
4366
- const treeLines2 = lines.map((l) => ` ${l.connector}${l.urlText}`).join("\n");
4367
- return `${headerLine2}${treeLines2}
4368
- `;
4369
- }
4370
- const bullet = themeText("\u2022", "chrome", true);
4371
- const title = styleCliText(titleText, "bold", true);
4372
- const countText = themeText(`(${count})`, "muted", true);
4373
- const headerLine = `${bullet} ${title} ${countText}
4374
- `;
4375
- const treeLines = lines.map((l) => {
4376
- const connector = themeText(` ${l.connector}`, "chrome", true);
4377
- const urlText = themeText(l.urlText, "muted", true);
4378
- return `${connector}${urlText}`;
4379
- }).join("\n");
4380
- return `${headerLine}${treeLines}
4381
- `;
4227
+ return formatToolTraceLine(`\u25B8 ${label} \u2717 ${toolFailureReason(event) || t("toolFailed")}`, colorEnabled, "error");
4228
+ }
4229
+ const gist = normalToolGist(event);
4230
+ const hint = editFileResultHint(event);
4231
+ const statsInline = hint?.inline ? ` ${hint.inline}` : "";
4232
+ const durationInline = formatNormalToolDuration(event.elapsedMs);
4233
+ const tail = durationInline ? ` \u2713 ${durationInline}` : " \u2713";
4234
+ const mainLine = formatToolTraceLine(
4235
+ gist ? `\u25B8 ${label} \xB7 ${gist}${statsInline}${tail}` : `\u25B8 ${label}${statsInline}${tail}`,
4236
+ colorEnabled
4237
+ );
4238
+ if (hint?.detail?.length) {
4239
+ const filePath = typeof event.metadata?.path === "string" ? event.metadata.path : void 0;
4240
+ return `${mainLine}${formatEditDetailBlock(hint.detail, colorEnabled, filePath)}`;
4241
+ }
4242
+ return mainLine;
4243
+ }
4244
+ function toolFailureReason(event) {
4245
+ const metadata = event.metadata ?? {};
4246
+ const rawError = metadata.error ?? metadata.errorMessage;
4247
+ const raw = typeof rawError === "string" && rawError.trim() || event.message?.trim() || typeof event.content === "string" && event.content.trim() || event.summary?.trim() || "";
4248
+ const lines = raw.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
4249
+ const sectionLines = (section) => {
4250
+ const start = lines.findIndex((line) => new RegExp(`^${section}\\s*:$`, "i").test(line));
4251
+ if (start < 0) return [];
4252
+ const end = lines.findIndex((line, index) => index > start && /^(stdout|stderr|exitCode)\s*:/i.test(line));
4253
+ return lines.slice(start + 1, end < 0 ? lines.length : end);
4254
+ };
4255
+ const reasonLine = sectionLines("stderr")[0] ?? sectionLines("stdout")[0] ?? lines.find((line) => !/^(stdout|stderr|exitCode)\s*:/i.test(line)) ?? lines[0] ?? "";
4256
+ return reasonLine ? clip(redactSecrets(stripAnsi2(reasonLine)), 96) : "";
4382
4257
  }
4383
4258
  function resolveLoadSkillName(event) {
4384
4259
  const metaName = typeof event.metadata?.name === "string" ? event.metadata.name : void 0;
@@ -4443,326 +4318,63 @@ function formatLoadSkillBlock(event, colorEnabled, opts) {
4443
4318
  return `${star} ${labelPart}: ${namePart}
4444
4319
  `;
4445
4320
  }
4446
- function formatCompactToolLine(event, pending, colorEnabled) {
4447
- const toolName = event.toolName || pending?.toolName || "tool";
4448
- const todoBlock = formatTodoListForCli(event, colorEnabled);
4449
- if (todoBlock) return todoBlock;
4450
- if (isReadToolName(toolName) && event.type === "tool-result") {
4451
- 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 || "";
4452
- return formatReadTreeBlockForCli([{ path: rawPath, metadata: event.metadata }], colorEnabled);
4453
- }
4454
- if (isSearchToolName(toolName) && event.type === "tool-result") {
4455
- 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 || "";
4456
- const rawPath = typeof event.metadata?.path === "string" ? event.metadata.path : void 0;
4457
- return formatSearchTreeBlockForCli([{ query: rawQuery, path: rawPath }], colorEnabled);
4458
- }
4459
- if (isFetchToolName(toolName) && event.type === "tool-result") {
4460
- const rawUrl = (typeof event.metadata?.url === "string" ? event.metadata.url : void 0) || event.argumentsPreview || pending?.argumentsPreview || "";
4461
- return formatFetchTreeBlockForCli([{ url: rawUrl }], colorEnabled);
4462
- }
4463
- if (isRunToolName(toolName) && event.type === "tool-result" && isRunResultFoldable(event)) {
4464
- const rawCommand = (typeof event.metadata?.command === "string" ? event.metadata.command : void 0) || event.argumentsPreview || pending?.argumentsPreview || "";
4465
- const exitCode = typeof event.metadata?.exitCode === "number" ? event.metadata.exitCode : void 0;
4466
- const timedOut = Boolean(event.metadata?.timedOut);
4467
- return formatRunTreeBlockForCli([{ command: rawCommand, exitCode, timedOut }], colorEnabled);
4468
- }
4469
- const rawArgs = clipPathAware(event.argumentsPreview || pending?.argumentsPreview || "", 72);
4470
- const rawLabel = toolLabel(toolName);
4471
- const label = rawArgs ? `${rawLabel} ${rawArgs}` : rawLabel;
4472
- if (event.type === "tool-error") {
4473
- const message = clip(event.message ?? t("toolFailed"), 96);
4474
- return formatToolTraceLine(`\u25B8 ${label} \u2717 ${message}`, colorEnabled, "error");
4475
- }
4476
- const loadSkillBlock = formatLoadSkillBlock(event, colorEnabled);
4477
- if (loadSkillBlock) return loadSkillBlock;
4478
- if (event.type === "tool-result" && (toolName === "listAgents" || toolName === "startAgentRun" || toolName === "controlAgentRun")) {
4479
- return formatOrchestrationCardBlock(event, toolName, colorEnabled);
4480
- }
4481
- if (event.type === "tool-result" && (event.toolName === "ask_user" || event.metadata?.uiAskChoice)) {
4482
- const block = formatUiAskChoiceBlock(event, colorEnabled);
4483
- if (block) return block;
4484
- }
4485
- const hint = compactResultHint(event);
4486
- const suffix = hint.inline ? ` ${hint.inline}` : "";
4487
- const failed = isFailedToolResult(event);
4488
- const marker = failed ? "\u2717" : isNeedsActionToolResult(event) ? "!" : "\u2713";
4489
- let mainLine;
4490
- if (!colorEnabled) {
4491
- mainLine = `\u25B8 ${label} ${marker}${suffix}
4492
- `;
4493
- } else {
4494
- const chromePointer = themeText("\u25B8", "chrome", true);
4495
- const mutedLabel = themeText(rawLabel, "muted", true);
4496
- const argsPart = rawArgs ? ` ${dimCliText(rawArgs, true)}` : "";
4497
- const statusToken = failed ? themeText("\u2717", "danger", true) : isNeedsActionToolResult(event) ? themeText("!", "warning", true) : themeText("\u2713", "success", true);
4498
- const suffixPart = hint.inline ? ` ${dimCliText(hint.inline, true)}` : "";
4499
- mainLine = `${chromePointer} ${mutedLabel}${argsPart} ${statusToken}${suffixPart}
4500
- `;
4501
- }
4502
- if (!hint.detail?.length) return mainLine;
4503
- const filePath = typeof event.metadata?.path === "string" ? event.metadata.path : event.argumentsPreview;
4504
- return `${mainLine}${formatEditDetailBlock(hint.detail, colorEnabled, filePath)}`;
4505
- }
4506
- function formatEditDetailBlock(lines, colorEnabled, filePath, env = process.env) {
4507
- if (!colorEnabled) {
4508
- return lines.map((line) => ` ${line.text}
4509
- `).join("");
4510
- }
4511
- const isTruecolor = supportsTruecolor(env);
4512
- const lang = isTruecolor ? detectCodeLangFromPath(filePath) : "unknown";
4513
- const brightness = resolveTuiBrightness(env);
4514
- const padTo = Math.max(...lines.map((line) => displayWidth(line.text))) + 1;
4515
- return lines.map((line) => ` ${renderDiffLine({
4516
- kind: line.kind,
4517
- text: line.text,
4518
- highlightedText: shouldHighlightEditLine(line, lang, isTruecolor) ? buildHighlightedEditLine(line, lang, brightness, env) : void 0,
4519
- padTo,
4520
- colorEnabled: true,
4521
- env
4522
- })}
4523
- `).join("");
4524
- }
4525
- function shouldHighlightEditLine(line, lang, isTruecolor) {
4526
- if (!isTruecolor || lang === "unknown") return false;
4527
- if (line.text.startsWith(" \u2026") || line.text.startsWith("\u2026")) return false;
4528
- return line.text.length >= 2;
4529
- }
4530
- function buildHighlightedEditLine(line, lang, brightness, env) {
4531
- const prefix = line.text.slice(0, 2);
4532
- const body = line.text.slice(2);
4533
- const prefixToken = line.kind === "added" ? "success" : line.kind === "removed" ? "danger" : "chrome";
4534
- const coloredPrefix = themeText(prefix, prefixToken, true, env);
4535
- const highlightedBody = highlightCodeLine(body, lang, brightness);
4536
- return `${coloredPrefix}${highlightedBody}`;
4537
- }
4538
- function createToolEventFormatter(mode, colorEnabled = resolveCliColorEnabled(), opts = {}) {
4539
- const suppressRunProgressCards = opts.suppressRunProgressCards === true;
4321
+ var TUI_TREE_MARKER = "TUI_TREE\n";
4322
+ var TUI_TREE_END = "";
4323
+ function createToolEventFormatter(colorEnabled = resolveCliColorEnabled(), opts = {}) {
4540
4324
  const pending = /* @__PURE__ */ new Map();
4541
- let readBuffer = [];
4542
- let searchBuffer = [];
4543
- let runBuffer = [];
4544
- let fetchBuffer = [];
4545
- let webSearchBuffer = [];
4546
- let statusFold = null;
4547
- const lastEmittedLogByRunId = /* @__PURE__ */ new Map();
4548
- const lastPrintedCardByRunId = /* @__PURE__ */ new Map();
4549
- const cardSubstance = (snapshot) => JSON.stringify([
4550
- snapshot.status,
4551
- snapshot.toolCallCount ?? -1,
4552
- snapshot.lastToolNames ?? [],
4553
- snapshot.lastAssistantText ?? "",
4554
- snapshot.errorMessage ?? "",
4555
- snapshot.logKey
4556
- ]);
4557
- const flushStatusFold = () => {
4558
- if (!statusFold) return "";
4559
- const fold = statusFold;
4560
- statusFold = null;
4561
- const substance = cardSubstance(fold);
4562
- if (lastPrintedCardByRunId.get(fold.runId) === substance) return "";
4563
- const prevLog = lastEmittedLogByRunId.get(fold.runId);
4564
- const changed = Boolean(fold.logKey) && fold.logKey !== prevLog;
4565
- if (suppressRunProgressCards) {
4566
- const terminal = isAgentRunTerminalStatus(fold.status);
4567
- if (!terminal && !fold.errorMessage && !changed) {
4568
- lastPrintedCardByRunId.set(fold.runId, substance);
4569
- return "";
4570
- }
4571
- }
4572
- lastPrintedCardByRunId.set(fold.runId, substance);
4573
- if (changed && runShowsLogTail(fold.status)) {
4574
- lastEmittedLogByRunId.set(fold.runId, fold.logKey);
4575
- }
4576
- return formatFoldedControlStatusCard({ ...fold, includeLogTail: changed }, colorEnabled);
4577
- };
4578
- const flushToolBuffers = () => {
4579
- let out = "";
4580
- if (readBuffer.length > 0) {
4581
- const items = readBuffer.map((evt) => {
4582
- const call = pending.get(evt.toolCallId);
4583
- 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 || "";
4584
- return { path: rawPath, metadata: evt.metadata };
4585
- });
4586
- for (const evt of readBuffer) pending.delete(evt.toolCallId);
4587
- readBuffer = [];
4588
- out += formatReadTreeBlockForCli(items, colorEnabled);
4589
- }
4590
- if (searchBuffer.length > 0) {
4591
- const items = searchBuffer.map((evt) => {
4592
- const call = pending.get(evt.toolCallId);
4593
- 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 || "";
4594
- const rawPath = typeof evt.metadata?.path === "string" ? evt.metadata.path : void 0;
4595
- return { query: rawQuery, path: rawPath };
4596
- });
4597
- for (const evt of searchBuffer) pending.delete(evt.toolCallId);
4598
- searchBuffer = [];
4599
- out += formatSearchTreeBlockForCli(items, colorEnabled);
4600
- }
4601
- if (runBuffer.length > 0) {
4602
- const items = runBuffer.map((evt) => {
4603
- const call = pending.get(evt.toolCallId);
4604
- const rawCommand = (typeof evt.metadata?.command === "string" ? evt.metadata.command : void 0) || evt.argumentsPreview || call?.argumentsPreview || "";
4605
- const exitCode = typeof evt.metadata?.exitCode === "number" ? evt.metadata.exitCode : void 0;
4606
- const timedOut = Boolean(evt.metadata?.timedOut);
4607
- return { command: rawCommand, exitCode, timedOut };
4608
- });
4609
- for (const evt of runBuffer) pending.delete(evt.toolCallId);
4610
- runBuffer = [];
4611
- out += formatRunTreeBlockForCli(items, colorEnabled);
4612
- }
4613
- if (fetchBuffer.length > 0) {
4614
- const items = fetchBuffer.map((evt) => {
4615
- const call = pending.get(evt.toolCallId);
4616
- const rawUrl = (typeof evt.metadata?.url === "string" ? evt.metadata.url : void 0) || evt.argumentsPreview || call?.argumentsPreview || "";
4617
- return { url: rawUrl };
4618
- });
4619
- for (const evt of fetchBuffer) pending.delete(evt.toolCallId);
4620
- fetchBuffer = [];
4621
- out += formatFetchTreeBlockForCli(items, colorEnabled);
4622
- }
4623
- if (webSearchBuffer.length > 0) {
4624
- const items = webSearchBuffer.map((evt) => {
4625
- const call = pending.get(evt.toolCallId);
4626
- 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 || "";
4627
- return { query: rawQuery };
4628
- });
4629
- for (const evt of webSearchBuffer) pending.delete(evt.toolCallId);
4630
- webSearchBuffer = [];
4631
- out += formatWebSearchTreeBlockForCli(items, colorEnabled);
4632
- }
4633
- return out;
4634
- };
4635
- const flushBuffers = () => `${flushStatusFold()}${flushToolBuffers()}`;
4636
- const bufferToolResult = (buffer, event) => {
4637
- const flushed = flushStatusFold();
4638
- buffer.push(event);
4639
- return flushed;
4325
+ let tree = null;
4326
+ const treeKind = (name) => isRunToolName(name) ? "Run" : isReadToolName(name) ? "Read" : name === "exa_search" ? "Search" : name === "fetchWebpage" ? "Fetch" : null;
4327
+ const treeLine = (kind, items) => {
4328
+ const label = kind === "Run" ? toolLabel("execShell") : kind === "Read" ? toolLabel("readFile") : kind === "Fetch" ? toolLabel("fetchWebpage") : toolLabel("exa_search");
4329
+ return `${TUI_TREE_MARKER}${label} (${items.length})
4330
+ ${items.map((item, i) => `${i === items.length - 1 ? "\u2514\u2500\u2500 " : "\u251C\u2500\u2500 "}${item} \u2713`).join("\n")}${TUI_TREE_END}
4331
+ `;
4640
4332
  };
4641
- const formatter = (event) => {
4642
- if (mode === "hide") return "";
4643
- if (mode === "verbose") return formatVerboseToolEvent(event, colorEnabled);
4644
- if (mode === "normal") {
4645
- if (event.type === "tool-call") {
4646
- pending.set(event.toolCallId, {
4647
- toolName: event.toolName,
4648
- argumentsPreview: event.argumentsPreview
4649
- });
4650
- return "";
4651
- }
4652
- const call2 = pending.get(event.toolCallId);
4653
- pending.delete(event.toolCallId);
4654
- return formatNormalToolLine(event, call2, colorEnabled);
4655
- }
4656
- if (event.toolName === "controlAgentRun") {
4657
- if (event.type === "tool-call") {
4658
- pending.set(event.toolCallId, {
4659
- toolName: event.toolName,
4660
- argumentsPreview: event.argumentsPreview
4661
- });
4662
- return "";
4663
- }
4664
- if (event.type === "tool-result") {
4665
- const snapshot = parseControlAgentRunStatusEvent(event);
4666
- if (snapshot) {
4667
- let out = flushToolBuffers();
4668
- const open = statusFold;
4669
- const sameRun = open?.runId === snapshot.runId;
4670
- if (open && (!sameRun || open.status !== snapshot.status)) {
4671
- out += flushStatusFold();
4672
- }
4673
- statusFold = open && sameRun ? mergeRunSnapshot(open, snapshot) : snapshot;
4674
- pending.delete(event.toolCallId);
4675
- if (isAgentRunTerminalStatus(snapshot.status)) {
4676
- out += flushBuffers();
4677
- }
4678
- return out;
4679
- }
4680
- const flushed2 = flushBuffers();
4681
- const call2 = pending.get(event.toolCallId);
4682
- pending.delete(event.toolCallId);
4683
- return `${flushed2}${formatCompactToolLine(event, call2, colorEnabled)}`;
4684
- }
4685
- }
4686
- if (isReadToolName(event.toolName)) {
4687
- if (event.type === "tool-call") {
4688
- pending.set(event.toolCallId, {
4689
- toolName: event.toolName,
4690
- argumentsPreview: event.argumentsPreview
4691
- });
4692
- return "";
4693
- }
4694
- if (event.type === "tool-result") {
4695
- return bufferToolResult(readBuffer, event);
4696
- }
4697
- }
4698
- if (isSearchToolName(event.toolName)) {
4699
- if (event.type === "tool-call") {
4700
- pending.set(event.toolCallId, {
4701
- toolName: event.toolName,
4702
- argumentsPreview: event.argumentsPreview
4703
- });
4704
- return "";
4705
- }
4706
- if (event.type === "tool-result") {
4707
- return bufferToolResult(searchBuffer, event);
4708
- }
4709
- }
4710
- if (isRunToolName(event.toolName)) {
4711
- if (event.type === "tool-result" && !isRunResultFoldable(event)) {
4712
- const flushed2 = flushBuffers();
4713
- const call2 = pending.get(event.toolCallId);
4714
- pending.delete(event.toolCallId);
4715
- return `${flushed2}${formatCompactToolLine(event, call2, colorEnabled)}`;
4716
- }
4717
- if (event.type === "tool-call") {
4718
- pending.set(event.toolCallId, {
4719
- toolName: event.toolName,
4720
- argumentsPreview: event.argumentsPreview
4721
- });
4722
- return "";
4723
- }
4724
- if (event.type === "tool-result") {
4725
- return bufferToolResult(runBuffer, event);
4726
- }
4727
- }
4728
- if (isFetchToolName(event.toolName)) {
4729
- if (event.type === "tool-call") {
4730
- pending.set(event.toolCallId, {
4731
- toolName: event.toolName,
4732
- argumentsPreview: event.argumentsPreview
4733
- });
4734
- return "";
4735
- }
4736
- if (event.type === "tool-result") {
4737
- return bufferToolResult(fetchBuffer, event);
4738
- }
4333
+ const formatTreeEvent = (event, call) => {
4334
+ if (!opts.tuiTrees || event.type !== "tool-result") return null;
4335
+ if (isFailedToolResult(event) || event.metadata?.failed || event.metadata?.timedOut || readActionGate(event.metadata?.actionGate)) {
4336
+ tree = null;
4337
+ return null;
4739
4338
  }
4740
- if (isWebSearchToolName(event.toolName)) {
4741
- if (event.type === "tool-call") {
4742
- pending.set(event.toolCallId, {
4743
- toolName: event.toolName,
4744
- argumentsPreview: event.argumentsPreview
4745
- });
4746
- return "";
4747
- }
4748
- if (event.type === "tool-result") {
4749
- return bufferToolResult(webSearchBuffer, event);
4750
- }
4339
+ const name = event.toolName || call?.toolName || "tool";
4340
+ const kind = treeKind(name);
4341
+ if (!kind) {
4342
+ tree = null;
4343
+ return null;
4751
4344
  }
4752
- const flushed = flushBuffers();
4345
+ let item = "";
4346
+ const m = event.metadata ?? {};
4347
+ if (kind === "Run") {
4348
+ const command = typeof m.command === "string" ? m.command : "";
4349
+ item = command ? formatRunItemCommand(command, typeof m.exitCode === "number" ? m.exitCode : void 0, Boolean(m.timedOut)) : "command";
4350
+ }
4351
+ if (kind === "Read") item = formatReadItemPath(typeof m.path === "string" ? m.path : typeof m.filePath === "string" ? m.filePath : "file", m);
4352
+ if (kind === "Search") item = formatSearchItemQuery(typeof m.query === "string" ? m.query : typeof m.pattern === "string" ? m.pattern : "search", typeof m.path === "string" ? m.path : void 0);
4353
+ if (kind === "Fetch") item = formatFetchItemUrl(typeof m.url === "string" ? m.url : "webpage");
4354
+ item = withholdIfSecretLike(item) || toolLabel(name);
4355
+ if (tree?.kind !== kind) tree = { kind, items: [] };
4356
+ tree.items.push(item);
4357
+ if (tree.items.length < 2) return formatNormalToolLine(event, call, colorEnabled);
4358
+ return treeLine(kind, tree.items);
4359
+ };
4360
+ const format = (event) => {
4753
4361
  if (event.type === "tool-call") {
4754
4362
  pending.set(event.toolCallId, {
4755
4363
  toolName: event.toolName,
4756
4364
  argumentsPreview: event.argumentsPreview
4757
4365
  });
4758
- return flushed;
4366
+ return "";
4759
4367
  }
4760
4368
  const call = pending.get(event.toolCallId);
4761
4369
  pending.delete(event.toolCallId);
4762
- return `${flushed}${formatCompactToolLine(event, call, colorEnabled)}`;
4370
+ const treeOutput = formatTreeEvent(event, call);
4371
+ if (treeOutput !== null) return treeOutput;
4372
+ return formatNormalToolLine(event, call, colorEnabled);
4373
+ };
4374
+ format.reset = () => {
4375
+ tree = null;
4763
4376
  };
4764
- formatter.flush = flushBuffers;
4765
- return formatter;
4377
+ return format;
4766
4378
  }
4767
4379
  function createSseToolEventAdapter(onEvent) {
4768
4380
  let round = 0;
@@ -4923,7 +4535,7 @@ ${this.text}
4923
4535
  0,
4924
4536
  Math.floor((Date.now() - this.startTime) / 1e3)
4925
4537
  );
4926
- const thinkingPart = this.thinkingHint ? `: ${truncateThinkingHint(this.thinkingHint, 60)}` : "";
4538
+ const thinkingPart = this.thinkingHint ? dimCliText(`: ${truncateThinkingHint(this.thinkingHint, 60)}`) : "";
4927
4539
  return `${themeColorSequence("accent")}${frame}\x1B[39m ${this.text}${thinkingPart} (${formatElapsed(elapsedSeconds)})`;
4928
4540
  }
4929
4541
  };
@@ -4980,10 +4592,8 @@ function createCliTurnOutput(params) {
4980
4592
  const { options } = params;
4981
4593
  const workingLabel = params.workingLabel ?? `${options.agentName} -> working`;
4982
4594
  const spinner = params.spinner ?? new Spinner(options.output, workingLabel, Boolean(options.activityReporter));
4983
- const toolDisplayMode = resolveToolDisplayMode(options.env);
4984
- const traceLocalTools = shouldEmitToolEvents(toolDisplayMode);
4985
- const formatToolEvent = createToolEventFormatter(toolDisplayMode, void 0, {
4986
- suppressRunProgressCards: typeof options.onAgentRunStatus === "function"
4595
+ const formatToolEvent = createToolEventFormatter(void 0, {
4596
+ tuiTrees: options.output.tuiTrees === true
4987
4597
  });
4988
4598
  const eventMode = resolveAgentEventMode(options);
4989
4599
  const showThinking = options.showThinking !== false;
@@ -4991,12 +4601,27 @@ function createCliTurnOutput(params) {
4991
4601
  let everStreamedAnyText = false;
4992
4602
  let printedAssistantLabel = false;
4993
4603
  let thinkState = createThinkParserState();
4604
+ let thinkingFirstAt = null;
4605
+ let thinkingLastAt = 0;
4606
+ const markThinkingActivity = () => {
4607
+ const now = Date.now();
4608
+ if (thinkingFirstAt === null) thinkingFirstAt = now;
4609
+ thinkingLastAt = now;
4610
+ };
4611
+ const writeThinkingTrace = () => {
4612
+ if (thinkingFirstAt === null) return;
4613
+ const seconds = Math.max(0, Math.round((thinkingLastAt - thinkingFirstAt) / 1e3));
4614
+ thinkingFirstAt = null;
4615
+ if (seconds < 1) return;
4616
+ options.output.write(`${dimCliText(t("thinkingTraceLine", formatElapsed(seconds)))}
4617
+ `);
4618
+ };
4994
4619
  let compactionEvent = null;
4995
4620
  const assistantLabelManaged = options.output.assistantLabelManaged === true;
4996
4621
  const writeToolOutput = (chunk) => {
4997
4622
  if (!chunk) return;
4998
4623
  if (typeof options.output.writeToolBlock === "function") {
4999
- options.output.writeToolBlock(chunk);
4624
+ if (!options.output.writeToolBlock(chunk)) formatToolEvent.reset?.();
5000
4625
  } else {
5001
4626
  options.output.write(chunk);
5002
4627
  }
@@ -5006,13 +4631,16 @@ function createCliTurnOutput(params) {
5006
4631
  });
5007
4632
  const writeVisibleAssistantChunk = (chunk) => {
5008
4633
  if (!chunk) return;
4634
+ formatToolEvent.reset?.();
5009
4635
  const parsed = processThinkChunk(chunk, thinkState);
5010
4636
  thinkState = parsed.state;
5011
4637
  if (!parsed.content && !parsed.reasoning) return;
5012
4638
  if (showThinking && parsed.reasoning) {
4639
+ markThinkingActivity();
5013
4640
  spinner.setThinkingHint(parsed.reasoning);
5014
4641
  }
5015
4642
  if (!parsed.content) return;
4643
+ writeThinkingTrace();
5016
4644
  spinner.stop();
5017
4645
  options.activityReporter?.(null);
5018
4646
  if (!printedAssistantLabel) {
@@ -5027,7 +4655,6 @@ ${options.agentName}${IDENTITY_LABEL_SEPARATOR}`);
5027
4655
  renderWriter.push(parsed.content);
5028
4656
  };
5029
4657
  const handleToolEvent = (event) => {
5030
- if (!traceLocalTools) return;
5031
4658
  const chunk = eventMode === "jsonl" ? formatToolJsonEvent(event) : formatToolEvent(event);
5032
4659
  if (eventMode === "jsonl") {
5033
4660
  options.output.write(chunk);
@@ -5045,8 +4672,8 @@ ${options.agentName}${IDENTITY_LABEL_SEPARATOR}`);
5045
4672
  if (chunk) {
5046
4673
  writeToolOutput(chunk);
5047
4674
  }
5048
- if ((toolDisplayMode === "compact" || toolDisplayMode === "pro" || toolDisplayMode === "normal") && event.type === "tool-call") {
5049
- const activeLabel = toolDisplayMode === "normal" ? formatConservativeActiveToolLabel(event) : formatActiveToolLabel(event);
4675
+ if (event.type === "tool-call") {
4676
+ const activeLabel = formatConservativeActiveToolLabel(event);
5050
4677
  spinner.show(activeLabel);
5051
4678
  options.activityReporter?.(activeLabel);
5052
4679
  }
@@ -5059,20 +4686,15 @@ ${options.agentName}${IDENTITY_LABEL_SEPARATOR}`);
5059
4686
  };
5060
4687
  return {
5061
4688
  spinner,
5062
- toolDisplayMode,
5063
- traceLocalTools,
5064
4689
  eventMode,
5065
4690
  pushText(chunk) {
5066
- if (eventMode !== "jsonl" && formatToolEvent.flush) {
5067
- const pendingToolOutput = formatToolEvent.flush();
5068
- if (pendingToolOutput) {
5069
- writeToolOutput(pendingToolOutput);
5070
- }
5071
- }
5072
4691
  writeVisibleAssistantChunk(chunk);
5073
4692
  },
5074
4693
  pushThinking(chunk) {
5075
- if (showThinking) spinner.setThinkingHint(chunk);
4694
+ if (showThinking) {
4695
+ markThinkingActivity();
4696
+ spinner.setThinkingHint(chunk);
4697
+ }
5076
4698
  },
5077
4699
  handleToolEvent,
5078
4700
  /** 记录一条 compaction 观测事件(TUI 在 turn 结束时渲染一行 dim 摘要)。 */
@@ -5087,15 +4709,12 @@ ${options.agentName}${IDENTITY_LABEL_SEPARATOR}`);
5087
4709
  finish(fallbackContent) {
5088
4710
  spinner.stop();
5089
4711
  options.activityReporter?.(null);
4712
+ writeThinkingTrace();
5090
4713
  const flushedThink = flushThinkParser(thinkState);
5091
4714
  thinkState = flushedThink.state;
5092
4715
  if (flushedThink.content) {
5093
4716
  writeVisibleAssistantChunk(flushedThink.content);
5094
4717
  }
5095
- const pendingToolOutput = formatToolEvent.flush ? formatToolEvent.flush() : "";
5096
- if (pendingToolOutput) {
5097
- writeToolOutput(pendingToolOutput);
5098
- }
5099
4718
  if (streamedAssistantText) {
5100
4719
  renderWriter.flush();
5101
4720
  options.output.write("\n");
@@ -5296,10 +4915,12 @@ ${describeClientVersionTooOldFailure(message, error)}`);
5296
4915
  if (usageText && shouldShowUsage(options.env))
5297
4916
  options.output.write(`${usageText}
5298
4917
  `);
4918
+ const turnCredits = platformCreditsFromUsage(usage);
5299
4919
  return {
5300
4920
  exitCode: 0,
5301
4921
  ...dialogId ? { dialogId } : {},
5302
- turnTokens: buildTurnTokenUsage(usage, options.agentKey)
4922
+ turnTokens: buildTurnTokenUsage(usage, options.agentKey),
4923
+ ...turnCredits !== void 0 ? { turnCredits } : {}
5303
4924
  };
5304
4925
  }
5305
4926
 
@@ -5307,6 +4928,10 @@ ${describeClientVersionTooOldFailure(message, error)}`);
5307
4928
  function resolveAuthToken2(env) {
5308
4929
  return resolvePlatformAuthToken(env);
5309
4930
  }
4931
+ function resolveFileWriteGateEnabled(env) {
4932
+ const raw = (env.NOLO_CLI_WRITE_GATE ?? "").trim().toLowerCase();
4933
+ return raw !== "off" && raw !== "0" && raw !== "false";
4934
+ }
5310
4935
  function isMachineBoundLocalhostCustomProvider(agentConfig) {
5311
4936
  const machineId = agentConfig?.runtimeBinding && typeof agentConfig.runtimeBinding === "object" ? String(agentConfig.runtimeBinding.machineId ?? "").trim() : "";
5312
4937
  const providerUrl = asTrimmedString(agentConfig?.customProviderUrl);
@@ -5398,7 +5023,16 @@ function isKnownServerPlatformAgent(options) {
5398
5023
  // packages/core/collapsedPaste.ts
5399
5024
  var COLLAPSE_PASTE_MIN_LINES = 8;
5400
5025
  var COLLAPSE_PASTE_MIN_CHARS = 400;
5401
- var COLLAPSED_PASTE_PLACEHOLDER_RE = /\[paste #(\d+) · (\d+) lines\]/g;
5026
+ var COLLAPSED_PASTE_PLACEHOLDER_RE = (
5027
+ // 兼容两种格式:旧 `[paste #N · L lines]` 与新 `[paste #N · L lines · preview]`。
5028
+ // preview 段用负向先行断言排除模型引用(`…; full content available via
5029
+ // readPastedText(pasteId=N)`):legacy 无 scope 的模型引用与新 chip 形状重叠,
5030
+ // 若被当作 chip 匹配,跨会话残留的引用会被错误展开/替换(破坏 durable
5031
+ // fallback 语义)。preview 与模型引用尾段在生成时都不会包含该标记原文之外
5032
+ // 的歧义,此处按"完整模型引用尾段"精确排除。
5033
+ /\[paste #(\d+) · (\d+) lines(?: · (?![^\]]*; full content available via readPastedText\(pasteId=\d+\))([^\]]*))?\]/g
5034
+ );
5035
+ var COLLAPSED_PASTE_PREVIEW_MAX_CHARS = 24;
5402
5036
  var COLLAPSED_PASTE_MODEL_REFERENCE_RE = /\[paste #(\d+)(?: scope:([a-zA-Z0-9_-]+))? · [^\]]+; full content available via readPastedText\(pasteId=(\d+)\)\]/g;
5403
5037
  function countTextLines(text) {
5404
5038
  if (text.length === 0) return 0;
@@ -5438,9 +5072,22 @@ function estimatePasteBytes(text) {
5438
5072
  }
5439
5073
  return bytes;
5440
5074
  }
5075
+ function buildCollapsedPastePreview(text) {
5076
+ const firstNonEmptyLine = text.split(/\r\n|\r|\n/).find((line) => line.trim().length > 0) ?? "";
5077
+ let preview = compactWhitespace(firstNonEmptyLine);
5078
+ preview = preview.replace(new RegExp("\\]|\\p{C}", "gu"), "").trim();
5079
+ if (preview.length === 0) return "";
5080
+ const chars = Array.from(preview);
5081
+ if (chars.length > COLLAPSED_PASTE_PREVIEW_MAX_CHARS) {
5082
+ return chars.slice(0, COLLAPSED_PASTE_PREVIEW_MAX_CHARS).join("") + "\u2026";
5083
+ }
5084
+ return preview;
5085
+ }
5441
5086
  function formatCollapsedPastePlaceholder(id, text) {
5442
5087
  const lines = countTextLines(text);
5443
- return `[paste #${id} \xB7 ${lines} lines]`;
5088
+ const preview = buildCollapsedPastePreview(text);
5089
+ if (!preview) return `[paste #${id} \xB7 ${lines} lines]`;
5090
+ return `[paste #${id} \xB7 ${lines} lines \xB7 ${preview}]`;
5444
5091
  }
5445
5092
  function generatePasteScope() {
5446
5093
  if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
@@ -5553,10 +5200,14 @@ function findCollapsedPasteSpanAt(buffer, pos, opts = {}) {
5553
5200
  // packages/cli/client/agentRun.ts
5554
5201
  import { ulid } from "ulid";
5555
5202
  async function loadRunLocalAgentTurn() {
5556
- const { runLocalAgentTurn } = await import("./agentRuntimeLocal-KWBPJ53H.js");
5203
+ const { runLocalAgentTurn } = await import("./agentRuntimeLocal-K327ELSL.js");
5557
5204
  return runLocalAgentTurn;
5558
5205
  }
5559
5206
  var stripDebugNoise = (s) => s.replace(/\s*raw="[\s\S]*"/g, "").replace(/\s*raw=\S+/g, "").replace(/\s*headers=\[[^\]]*\]/g, "").replace(/\s*headers=\S+/g, "").trim();
5207
+ var pickEmptyAssistantFlags = (result) => ({
5208
+ ...result.emptyAssistantFallbackReason ? { emptyAssistantFallbackReason: result.emptyAssistantFallbackReason } : {},
5209
+ ...result.emptyAssistantOutputUsable ? { emptyAssistantOutputUsable: true } : {}
5210
+ });
5560
5211
  function extractEmbeddedErrorMessage(message) {
5561
5212
  const match = /"message"\s*:\s*"((?:[^"\\]|\\.)*)"/.exec(message);
5562
5213
  if (!match) return void 0;
@@ -6094,13 +5745,17 @@ ${options.agentName} > (no text response)
6094
5745
  const usage = formatUsage(data?.usage, data?.dialogId);
6095
5746
  if (usage && shouldShowUsage(options.env)) options.output.write(`${usage}
6096
5747
  `);
5748
+ const turnCredits = platformCreditsFromUsage(
5749
+ data?.usage
5750
+ );
6097
5751
  return {
6098
5752
  exitCode: 0,
6099
5753
  ...typeof data?.dialogId === "string" && data.dialogId ? { dialogId: data.dialogId } : {},
6100
5754
  turnTokens: buildTurnTokenUsage(
6101
5755
  data?.usage,
6102
5756
  typeof data?.model === "string" ? data.model : options.agentKey
6103
- )
5757
+ ),
5758
+ ...turnCredits !== void 0 ? { turnCredits } : {}
6104
5759
  };
6105
5760
  }
6106
5761
  async function runInjectedLocalAgentTurn(options) {
@@ -6179,6 +5834,7 @@ async function runLocalAgentTurnForCli(options, settings) {
6179
5834
  } : {},
6180
5835
  continueDialogId: currentDialogId,
6181
5836
  fileWriteSessionId: currentDialogId,
5837
+ fileWriteGateEnabled: resolveFileWriteGateEnabled(options.env),
6182
5838
  spaceId: options.spaceId,
6183
5839
  category: options.category,
6184
5840
  inheritedFromDialogKey: options.inheritedFromDialogKey,
@@ -6204,11 +5860,13 @@ async function runLocalAgentTurnForCli(options, settings) {
6204
5860
  options.onLoopEvent?.(event);
6205
5861
  },
6206
5862
  ...options.abortSignal ? { abortSignal: options.abortSignal } : {},
6207
- ...turnOutput.traceLocalTools ? {
6208
- onToolEvent: (event) => {
6209
- turnOutput.handleToolEvent(event);
6210
- }
6211
- } : {},
5863
+ // 回合内注入(TUI 后台 run 唤醒直投):透传给 local loop。
5864
+ ...options.drainInjections ? { drainInjections: options.drainInjections } : {},
5865
+ // tool-gist-fold 后 onToolEvent 无条件挂载(不再受 traceLocalTools 开关
5866
+ // 控制,显示模式合并为单一 gist/折叠通道)——保留 alpha 的新形态。
5867
+ onToolEvent: (event) => {
5868
+ turnOutput.handleToolEvent(event);
5869
+ },
6212
5870
  ...!options.noStream ? {
6213
5871
  onTextDelta: (chunk) => {
6214
5872
  turnOutput.pushText(chunk);
@@ -6219,24 +5877,35 @@ async function runLocalAgentTurnForCli(options, settings) {
6219
5877
  } : {}
6220
5878
  });
6221
5879
  turnOutput.finish(result.content);
5880
+ const turnCredits = sumPlatformCredits(result.usageRecords);
6222
5881
  return {
6223
5882
  exitCode: 0,
6224
5883
  dialogId: result.dialogId,
6225
5884
  title: result.title,
6226
5885
  ...result.titlePatchPromise ? { titlePatchPromise: result.titlePatchPromise } : {},
6227
- ...result.emptyAssistantFallbackReason ? { emptyAssistantFallbackReason: result.emptyAssistantFallbackReason } : {},
6228
- turnTokens: buildTurnTokenUsage(result.usage, result.model)
5886
+ ...pickEmptyAssistantFlags(result),
5887
+ // 积分口径按「全轮」而非「最后一次调用」:result.usage 只是收尾那次
5888
+ // provider 调用的 usage,本轮前面 N-1 次工具循环调用(以及自动压缩摘要)
5889
+ // 的 cost 都只存在于 usageRecords 里。token 字段仍取 result.usage——
5890
+ // 上下文占用本来就该看最后一次调用的累计输入,不是各次相加。
5891
+ turnTokens: withTurnCredits(buildTurnTokenUsage(result.usage, result.model), turnCredits),
5892
+ ...turnCredits !== void 0 ? { turnCredits } : {},
5893
+ ...result.usageRecords?.length ? { usageRecords: result.usageRecords } : {}
6229
5894
  };
6230
5895
  } catch (error) {
6231
5896
  turnOutput.spinner.stop();
6232
5897
  const savedDialogId = error?.dialogId;
5898
+ const abortedUsageRecords = error?.usageRecords;
5899
+ const abortedTurnCredits = sumPlatformCredits(abortedUsageRecords);
6233
5900
  if (error?.code === LOCAL_TURN_ABORTED_CODE || options.abortSignal?.aborted) {
6234
5901
  const pendingToolName = error?.pendingToolName;
6235
5902
  return {
6236
5903
  exitCode: 0,
6237
5904
  streamInterrupted: true,
6238
5905
  ...savedDialogId ? { dialogId: savedDialogId } : {},
6239
- ...pendingToolName ? { pendingToolName } : {}
5906
+ ...pendingToolName ? { pendingToolName } : {},
5907
+ ...abortedTurnCredits !== void 0 ? { turnCredits: abortedTurnCredits } : {},
5908
+ ...abortedUsageRecords?.length ? { usageRecords: abortedUsageRecords } : {}
6240
5909
  };
6241
5910
  }
6242
5911
  const cooldownUntil = await markStartupRateLimitCooldown(
@@ -6262,10 +5931,70 @@ async function runLocalAgentTurnForCli(options, settings) {
6262
5931
  return {
6263
5932
  exitCode: 1,
6264
5933
  localError: error,
6265
- ...savedDialogId ? { dialogId: savedDialogId } : {}
5934
+ ...savedDialogId ? { dialogId: savedDialogId } : {},
5935
+ ...abortedTurnCredits !== void 0 ? { turnCredits: abortedTurnCredits } : {},
5936
+ ...abortedUsageRecords?.length ? { usageRecords: abortedUsageRecords } : {}
6266
5937
  };
6267
5938
  }
6268
5939
  }
5940
+ async function checkLocalAvailabilityBeforeHttpDispatch(options) {
5941
+ const adapter = resolveLocalRuntimeAdapter(options);
5942
+ if (!adapter || typeof adapter.loadAgentConfig !== "function") return null;
5943
+ let config;
5944
+ try {
5945
+ config = await adapter.loadAgentConfig(options.agentKey);
5946
+ } catch {
5947
+ return null;
5948
+ }
5949
+ if (!config || typeof config !== "object") return null;
5950
+ const baseRecord = config.rawRecord ?? config;
5951
+ const credentialKey = resolveCredentialKeyWithFallback(baseRecord);
5952
+ const agentLevelAt = baseRecord.nextAvailableAt;
5953
+ const agentLevelNextAvailableAt = typeof agentLevelAt === "number" && Number.isFinite(agentLevelAt) ? agentLevelAt : void 0;
5954
+ if (!credentialKey && agentLevelNextAvailableAt === void 0) return null;
5955
+ const env = options.env;
5956
+ const now = Date.now();
5957
+ const entry = credentialKey ? await readCredentialEntry(credentialKey, env, now).catch(() => void 0) : void 0;
5958
+ const entryAt = entry?.nextAvailableAt;
5959
+ const effectiveNextAvailableAt = typeof entryAt === "number" ? mergeAvailabilityDeadline(agentLevelNextAvailableAt, entryAt) : agentLevelNextAvailableAt;
5960
+ if (typeof effectiveNextAvailableAt !== "number") return null;
5961
+ const gateDecision = resolveCooldownGate(
5962
+ { nextAvailableAt: effectiveNextAvailableAt, lastProbeAt: entry?.lastProbeAt },
5963
+ now
5964
+ );
5965
+ if (gateDecision === "probe" && credentialKey) {
5966
+ await recordCredentialProbe(credentialKey, env, now).catch(() => void 0);
5967
+ return null;
5968
+ }
5969
+ if (gateDecision !== "blocked") return null;
5970
+ options.output.write(
5971
+ `[nolo] Agent ${options.agentName || options.agentKey} is temporarily unavailable (429 cooldown) until ${new Date(Number(effectiveNextAvailableAt)).toISOString()}.
5972
+ Dispatch aborted before reaching the server. Pick another agent via listAgents, or retry after the cooldown.
5973
+ `
5974
+ );
5975
+ return { exitCode: 1 };
5976
+ }
5977
+ function foldLocalResultForTui(localResult) {
5978
+ return {
5979
+ exitCode: localResult.exitCode,
5980
+ ...localResult.dialogId ? { dialogId: localResult.dialogId } : {},
5981
+ title: localResult.title,
5982
+ ...localResult.titlePatchPromise ? { titlePatchPromise: localResult.titlePatchPromise } : {},
5983
+ // 空 assistant 兜底成因必须随行:后台 run 的结算(resolveRunOutcome 的
5984
+ // isStalledOrTruncated)靠它把截断轮判成 stalled 而不是 clean 失败。
5985
+ ...pickEmptyAssistantFlags(localResult),
5986
+ ...localResult.turnTokens ? { turnTokens: localResult.turnTokens } : {},
5987
+ // 平台积分必须随行:runLocalAgentTurnForCli 里 sumPlatformCredits 已按
5988
+ // 「全轮逐次求和」算好(只认 billing_unit === "credits" 的平台计费帧)。
5989
+ ...localResult.turnCredits !== void 0 ? { turnCredits: localResult.turnCredits } : {},
5990
+ // 逐帧用量明细必须随行:cli-local 计费走 fail-open 客户端记账,服务端没有
5991
+ // 逐帧记录,TUI 计费审计(appendTurnBillingAudit)要靠它对账。白名单历史上
5992
+ // 漏过 turnCredits(fix/tui-session-credits),usageRecords 是同坑位新字段。
5993
+ ...localResult.usageRecords?.length ? { usageRecords: localResult.usageRecords } : {},
5994
+ ...localResult.streamInterrupted ? { streamInterrupted: localResult.streamInterrupted } : {},
5995
+ ...localResult.pendingToolName ? { pendingToolName: localResult.pendingToolName } : {}
5996
+ };
5997
+ }
6269
5998
  async function runAgentTurn(options) {
6270
5999
  const authToken = resolveAuthToken2(options.env);
6271
6000
  const runtimeMode = resolveRequestedRuntimeMode(options);
@@ -6279,15 +6008,7 @@ async function runAgentTurn(options) {
6279
6008
  reportFailure: false
6280
6009
  });
6281
6010
  if (localResult.exitCode === 0) {
6282
- return {
6283
- exitCode: localResult.exitCode,
6284
- ...localResult.dialogId ? { dialogId: localResult.dialogId } : {},
6285
- title: localResult.title,
6286
- ...localResult.titlePatchPromise ? { titlePatchPromise: localResult.titlePatchPromise } : {},
6287
- ...localResult.turnTokens ? { turnTokens: localResult.turnTokens } : {},
6288
- ...localResult.streamInterrupted ? { streamInterrupted: localResult.streamInterrupted } : {},
6289
- ...localResult.pendingToolName ? { pendingToolName: localResult.pendingToolName } : {}
6290
- };
6011
+ return foldLocalResultForTui(localResult);
6291
6012
  }
6292
6013
  if (isMissingLocalAgentConfigError(localResult.localError, options.agentKey)) {
6293
6014
  options.output.write(
@@ -6301,13 +6022,7 @@ async function runAgentTurn(options) {
6301
6022
  reportFailure: false
6302
6023
  });
6303
6024
  if (retriedLocalResult.exitCode === 0) {
6304
- return {
6305
- exitCode: retriedLocalResult.exitCode,
6306
- ...retriedLocalResult.dialogId ? { dialogId: retriedLocalResult.dialogId } : {},
6307
- title: retriedLocalResult.title,
6308
- ...retriedLocalResult.titlePatchPromise ? { titlePatchPromise: retriedLocalResult.titlePatchPromise } : {},
6309
- ...retriedLocalResult.turnTokens ? { turnTokens: retriedLocalResult.turnTokens } : {}
6310
- };
6025
+ return foldLocalResultForTui(retriedLocalResult);
6311
6026
  }
6312
6027
  }
6313
6028
  } catch {
@@ -6320,7 +6035,11 @@ async function runAgentTurn(options) {
6320
6035
  ...localResult.dialogId ? { dialogId: localResult.dialogId } : {},
6321
6036
  // Keep localError so TUI can show balance/quota/dialog-preserved hints
6322
6037
  // instead of only the raw auto-runtime line.
6323
- ...localResult.localError ? { localError: localResult.localError } : {}
6038
+ ...localResult.localError ? { localError: localResult.localError } : {},
6039
+ // 失败轮同样可能已发生平台计费(abort/失败照样扣费):turnCredits 与
6040
+ // 逐帧明细一起随行,否则审计日志在 auto 失败路径上缺账。
6041
+ ...localResult.turnCredits !== void 0 ? { turnCredits: localResult.turnCredits } : {},
6042
+ ...localResult.usageRecords?.length ? { usageRecords: localResult.usageRecords } : {}
6324
6043
  };
6325
6044
  }
6326
6045
  }
@@ -6330,26 +6049,50 @@ async function runAgentTurn(options) {
6330
6049
  );
6331
6050
  return { exitCode: 1 };
6332
6051
  }
6052
+ const localBlock = await checkLocalAvailabilityBeforeHttpDispatch(options);
6053
+ if (localBlock) {
6054
+ return { exitCode: localBlock.exitCode };
6055
+ }
6333
6056
  return runHttpAgentTurn(options, authToken);
6334
6057
  }
6335
6058
 
6336
6059
  // packages/cli/agentRunCommand.ts
6337
6060
  import * as nodeFs from "node:fs";
6338
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
6339
- import { join as join2 } from "node:path";
6340
6061
 
6341
6062
  // packages/cli/client/autoModelRouter.ts
6342
6063
  var CLI_AUTO_ROUTE_AGENT_KEY = BUILTIN_NOLO_AGENT_KEY;
6343
6064
 
6065
+ // packages/agent-runtime/agentsMd.ts
6066
+ import { existsSync, readFileSync } from "node:fs";
6067
+ import { join } from "node:path";
6068
+ var AGENTS_MD_MAX_BYTES = 8192;
6069
+ var AGENTS_MD_CANDIDATES = ["AGENTS.md", "CLAUDE.md"];
6070
+ function readAgentsMdLayerFromDisk(cwd) {
6071
+ for (const name of AGENTS_MD_CANDIDATES) {
6072
+ const filePath = join(cwd, name);
6073
+ if (!existsSync(filePath)) continue;
6074
+ try {
6075
+ let content = readFileSync(filePath, "utf8").trim();
6076
+ if (!content) continue;
6077
+ if (Buffer.byteLength(content, "utf8") > AGENTS_MD_MAX_BYTES) {
6078
+ content = Buffer.from(content, "utf8").subarray(0, AGENTS_MD_MAX_BYTES).toString("utf8") + "\n\n<!-- AGENTS.md truncated -->";
6079
+ }
6080
+ return buildAgentsMdLayer(content, name);
6081
+ } catch {
6082
+ }
6083
+ }
6084
+ return null;
6085
+ }
6086
+
6344
6087
  // packages/cli/agentRunPrompts.ts
6345
- import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
6346
- import { dirname, extname, isAbsolute, join, relative as relativePath, resolve, sep } from "node:path";
6088
+ import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync, statSync } from "node:fs";
6089
+ import { dirname, extname, isAbsolute, join as join2, relative as relativePath, resolve, sep } from "node:path";
6347
6090
  function workflowRefToCandidatePath(cwd, ref) {
6348
6091
  const normalized = ref.trim();
6349
6092
  if (!normalized) return "";
6350
6093
  if (normalized.endsWith(".md") || normalized.includes("/") || normalized.includes("\\")) {
6351
6094
  const directPath = resolve(cwd, normalized);
6352
- if (existsSync(directPath)) return directPath;
6095
+ if (existsSync2(directPath)) return directPath;
6353
6096
  }
6354
6097
  const fileName = normalized.replace(/[^a-zA-Z0-9一-鿿]+/g, "-").replace(/^-+|-+$/g, "");
6355
6098
  return resolve(cwd, "docs", "workflows", `${fileName}.md`);
@@ -6362,21 +6105,21 @@ function skillRefToCandidatePath(cwd, ref) {
6362
6105
  if (!normalized) return "";
6363
6106
  if (normalized.endsWith(".md") || normalized.includes("/") || normalized.includes("\\")) {
6364
6107
  const directPath = resolve(cwd, normalized);
6365
- if (existsSync(directPath)) return directPath;
6108
+ if (existsSync2(directPath)) return directPath;
6366
6109
  }
6367
6110
  const fileName = normalized.replace(/[^a-zA-Z0-9一-鿿]+/g, "-").replace(/^-+|-+$/g, "");
6368
6111
  for (const dir of SKILL_SEARCH_DIRS) {
6369
6112
  const skillMdPath = resolve(cwd, dir, fileName, "SKILL.md");
6370
- if (existsSync(skillMdPath)) return skillMdPath;
6113
+ if (existsSync2(skillMdPath)) return skillMdPath;
6371
6114
  }
6372
6115
  return resolve(cwd, ".agents", "skills", fileName, "SKILL.md");
6373
6116
  }
6374
6117
  async function resolveWorkflowReference(ref, cwd = process.cwd()) {
6375
6118
  const path = workflowRefToCandidatePath(cwd, ref);
6376
- if (!path || !existsSync(path)) {
6119
+ if (!path || !existsSync2(path)) {
6377
6120
  throw new Error(`Workflow reference not found: ${ref}`);
6378
6121
  }
6379
- const markdown = readFileSync(path, "utf8");
6122
+ const markdown = readFileSync2(path, "utf8");
6380
6123
  const parsed = parseSkillDocProtocol(markdown);
6381
6124
  return {
6382
6125
  ref,
@@ -6393,8 +6136,8 @@ function inlineRelativeRefs(body, skillDir) {
6393
6136
  const absPath = resolve(skillDir, cleaned);
6394
6137
  const relative = relativePath(normalizedSkillDir, absPath);
6395
6138
  if (relative.startsWith("..") || isAbsolute(relative)) return match;
6396
- if (!existsSync(absPath) || !absPath.endsWith(".md")) return match;
6397
- const refContent = readFileSync(absPath, "utf8");
6139
+ if (!existsSync2(absPath) || !absPath.endsWith(".md")) return match;
6140
+ const refContent = readFileSync2(absPath, "utf8");
6398
6141
  return `
6399
6142
 
6400
6143
  ### ${label}
@@ -6405,8 +6148,8 @@ ${refContent}
6405
6148
  });
6406
6149
  }
6407
6150
  function listSkillScripts(skillDir) {
6408
- const scriptsDir = join(skillDir, "scripts");
6409
- if (!existsSync(scriptsDir) || !statSync(scriptsDir).isDirectory()) return null;
6151
+ const scriptsDir = join2(skillDir, "scripts");
6152
+ if (!existsSync2(scriptsDir) || !statSync(scriptsDir).isDirectory()) return null;
6410
6153
  const entries = readdirSync(scriptsDir).filter((e) => !e.startsWith("."));
6411
6154
  if (entries.length === 0) return null;
6412
6155
  return [
@@ -6433,10 +6176,10 @@ async function resolveSkillReference(ref, options = {}) {
6433
6176
  } else {
6434
6177
  const cwd = options.cwd ?? process.cwd();
6435
6178
  const path = skillRefToCandidatePath(cwd, ref);
6436
- if (!path || !existsSync(path)) {
6179
+ if (!path || !existsSync2(path)) {
6437
6180
  throw new Error(`Skill reference not found: ${ref}`);
6438
6181
  }
6439
- markdown = readFileSync(path, "utf8");
6182
+ markdown = readFileSync2(path, "utf8");
6440
6183
  filePath = path;
6441
6184
  skillDir = dirname(path);
6442
6185
  }
@@ -6541,8 +6284,8 @@ function prependSubjectDialogMarker(message, subjectDialogKey) {
6541
6284
  function normalizeCliImageInput(input) {
6542
6285
  if (/^(https?:|data:|file:)/i.test(input)) return input;
6543
6286
  const absolutePath = resolve(input);
6544
- if (!existsSync(absolutePath)) return input;
6545
- const base64 = readFileSync(absolutePath).toString("base64");
6287
+ if (!existsSync2(absolutePath)) return input;
6288
+ const base64 = readFileSync2(absolutePath).toString("base64");
6546
6289
  return `data:${imageMimeTypeForPath(absolutePath)};base64,${base64}`;
6547
6290
  }
6548
6291
  function imageMimeTypeForPath(path) {
@@ -6625,6 +6368,19 @@ var touchMemoryItemsInDb = async (db, items, now = (/* @__PURE__ */ new Date()).
6625
6368
  // packages/ai/memory/constants.ts
6626
6369
  var EXPLICIT_REMEMBER_PREFIX_REGEX = /^(你要记住|请记住|以后记住|记住)[,,。.\s::]*/u;
6627
6370
 
6371
+ // packages/ai/memory/source.ts
6372
+ var getMemorySourceKind = (item) => {
6373
+ if (item.sourceKind) return item.sourceKind;
6374
+ if (item.patternKey === "explicit-remember") return "explicit-user-directive";
6375
+ if (item.patternKey === "agent-remember" || item.patternKey === "procedural-runbook")
6376
+ return "agent-tool";
6377
+ if (item.patternKey?.startsWith("dialog-learning:"))
6378
+ return "dialog-learning";
6379
+ if (item.tags?.includes("understanding-memory"))
6380
+ return "inferred-understanding";
6381
+ return "agent-tool";
6382
+ };
6383
+
6628
6384
  // packages/ai/memory/overlay.ts
6629
6385
  var KIND_TITLES = {
6630
6386
  episodic: "Episodic",
@@ -6658,6 +6414,8 @@ var FACET_DISPLAY = {
6658
6414
  style: { label: "\u66F4\u504F\u597D", strip: "\u66F4\u559C\u6B22" },
6659
6415
  goal: { label: "\u60F3\u63A8\u8FDB", strip: "\u60F3\u63A8\u8FDB" }
6660
6416
  };
6417
+ var INFERRED_SOURCE_KINDS = /* @__PURE__ */ new Set(["inferred-understanding", "dialog-learning"]);
6418
+ var isInferredMemory = (item) => INFERRED_SOURCE_KINDS.has(getMemorySourceKind(item));
6661
6419
  var formatFacetContent = (facet, normalized, prefix) => {
6662
6420
  const { label, strip } = FACET_DISPLAY[facet];
6663
6421
  const stripped = normalized.startsWith(strip) ? normalized.slice(strip.length).trim() : normalized;
@@ -6679,9 +6437,79 @@ var normalizeDisplayContent = (item) => {
6679
6437
  }
6680
6438
  return normalized;
6681
6439
  };
6440
+ var pickWithinKind = (list, perKindLimit) => {
6441
+ if (perKindLimit <= 0) return [];
6442
+ const head = list.slice(0, perKindLimit);
6443
+ if (head.length < perKindLimit) return head;
6444
+ if (head.some((item) => item.subjectType === "user")) return head;
6445
+ const firstUserSubject = list.find((item) => item.subjectType === "user");
6446
+ if (!firstUserSubject) return head;
6447
+ return [...head.slice(0, perKindLimit - 1), firstUserSubject];
6448
+ };
6449
+ var DEFAULT_MEMORY_OVERLAY_TOKEN_BUDGET = 3200;
6450
+ var MIN_TRUNCATED_TOKENS = 20;
6451
+ var TRUNCATED_SUFFIX = "\u2026\uFF08\u672C\u6761\u5DF2\u622A\u65AD\uFF09";
6452
+ var truncateLineToTokens = (lineText, budgetTokens) => {
6453
+ if (budgetTokens < MIN_TRUNCATED_TOKENS) return null;
6454
+ const suffixTokens = estimateTokens(TRUNCATED_SUFFIX);
6455
+ const bodyBudget = budgetTokens - suffixTokens;
6456
+ if (bodyBudget < MIN_TRUNCATED_TOKENS / 2) return null;
6457
+ let used = 0;
6458
+ let cut = 0;
6459
+ for (const ch of lineText) {
6460
+ const chTokens = estimateTokens(ch);
6461
+ if (used + chTokens > bodyBudget) break;
6462
+ used += chTokens;
6463
+ cut += ch.length;
6464
+ }
6465
+ if (cut <= 2) return null;
6466
+ return `${lineText.slice(0, cut).trimEnd()}${TRUNCATED_SUFFIX}`;
6467
+ };
6468
+ var fitLinesToBudget = (allLines, remainingBudget) => {
6469
+ let usedTokens = 0;
6470
+ const keptByKind = {};
6471
+ let truncatedCount = 0;
6472
+ let droppedCount = 0;
6473
+ const pushLine = (kind, lineText) => {
6474
+ if (!keptByKind[kind]) keptByKind[kind] = [];
6475
+ keptByKind[kind].push(lineText);
6476
+ };
6477
+ for (const candidate of allLines) {
6478
+ const remaining = remainingBudget - usedTokens;
6479
+ if (remaining <= 0) {
6480
+ droppedCount += 1;
6481
+ continue;
6482
+ }
6483
+ if (candidate.lineTokens <= remaining) {
6484
+ usedTokens += candidate.lineTokens;
6485
+ pushLine(candidate.kind, candidate.lineText);
6486
+ continue;
6487
+ }
6488
+ const truncated = truncateLineToTokens(candidate.lineText, remaining);
6489
+ if (!truncated) {
6490
+ droppedCount += 1;
6491
+ continue;
6492
+ }
6493
+ usedTokens += estimateTokens(truncated);
6494
+ pushLine(candidate.kind, truncated);
6495
+ truncatedCount += 1;
6496
+ }
6497
+ return { keptByKind, truncatedCount, droppedCount };
6498
+ };
6499
+ var buildBudgetFooter = (fit) => {
6500
+ const notices = [];
6501
+ if (fit.truncatedCount > 0) {
6502
+ notices.push(`${fit.truncatedCount} \u6761\u56E0\u9884\u7B97\u88AB\u622A\u65AD`);
6503
+ }
6504
+ if (fit.droppedCount > 0) {
6505
+ notices.push(`${fit.droppedCount} \u6761\u672A\u663E\u793A`);
6506
+ }
6507
+ if (notices.length === 0) return [];
6508
+ return [`\uFF08${notices.join("\u3001")}\uFF1B\u9700\u8981\u5B8C\u6574\u5185\u5BB9\u6216\u66F4\u591A\u76F8\u5173\u8BB0\u5FC6\u65F6\u7528 queryMemory \u68C0\u7D22\uFF09`];
6509
+ };
6682
6510
  var buildMemoryOverlay = (items, options) => {
6683
6511
  if (items.length === 0) return null;
6684
- const maxTokens = options?.maxTokens ?? 2e3;
6512
+ const maxTokens = options?.maxTokens ?? DEFAULT_MEMORY_OVERLAY_TOKEN_BUDGET;
6685
6513
  const perKindLimit = options?.perKindLimit ?? 3;
6686
6514
  const byKind = {
6687
6515
  episodic: [],
@@ -6696,19 +6524,15 @@ var buildMemoryOverlay = (items, options) => {
6696
6524
  return OVERLAY_HEADER_LINES.join("\n");
6697
6525
  }
6698
6526
  const allLines = Object.entries(byKind).filter(([, list]) => list.length > 0).flatMap(
6699
- ([kind, list]) => list.slice(0, perKindLimit).map((item) => {
6700
- const lineText = `- ${normalizeDisplayContent(item)}`;
6527
+ ([kind, list]) => pickWithinKind(list, perKindLimit).map((item) => {
6528
+ const lineText = `- ${isInferredMemory(item) ? "\uFF08\u63A8\u65AD\uFF09" : ""}${normalizeDisplayContent(item)}`;
6701
6529
  return { kind, lineText, lineTokens: estimateTokens(lineText) };
6702
6530
  })
6703
6531
  ).sort((a, b) => (KIND_PRIORITY[a.kind] ?? 99) - (KIND_PRIORITY[b.kind] ?? 99));
6704
- let usedTokens = 0;
6705
- const keptByKind = {};
6706
- for (const candidate of allLines) {
6707
- if (usedTokens + candidate.lineTokens > remainingBudget) continue;
6708
- usedTokens += candidate.lineTokens;
6709
- if (!keptByKind[candidate.kind]) keptByKind[candidate.kind] = [];
6710
- keptByKind[candidate.kind].push(candidate.lineText);
6711
- }
6532
+ const { keptByKind, truncatedCount, droppedCount } = fitLinesToBudget(
6533
+ allLines,
6534
+ remainingBudget
6535
+ );
6712
6536
  const kindOrder = ["semantic", "procedural", "episodic"];
6713
6537
  const sections = kindOrder.filter((kind) => keptByKind[kind] && keptByKind[kind].length > 0).map((kind) => {
6714
6538
  return [`[${KIND_TITLES[kind]}]`, ...keptByKind[kind]].join("\n");
@@ -6716,7 +6540,7 @@ var buildMemoryOverlay = (items, options) => {
6716
6540
  if (sections.length === 0) {
6717
6541
  return OVERLAY_HEADER_LINES.join("\n");
6718
6542
  }
6719
- return [...OVERLAY_HEADER_LINES, ...sections].join("\n");
6543
+ return [...OVERLAY_HEADER_LINES, ...sections, ...buildBudgetFooter({ keptByKind, truncatedCount, droppedCount })].join("\n");
6720
6544
  };
6721
6545
 
6722
6546
  // packages/ai/memory/rank.ts
@@ -6763,13 +6587,28 @@ var identifierScore = (query, item) => {
6763
6587
  ).length;
6764
6588
  return matches / identifiers.length;
6765
6589
  };
6766
- var activationScore = (item, nowMs) => {
6590
+ var DAY_MS = 864e5;
6591
+ var UNPROVEN_MEMORY_GRACE_DAYS = 3;
6592
+ var MIN_UNPROVEN_SCORE_FACTOR = 0.3;
6593
+ var UNPROVEN_DECAY_HALF_LIFE_DAYS = 7;
6594
+ var provenActivationScore = (item, nowMs) => {
6767
6595
  const lastActivatedMs = Date.parse(item.lastActivatedAt || item.createdAt);
6768
- const ageDays = Math.max(0, (nowMs - lastActivatedMs) / 864e5);
6596
+ const ageDays = Math.max(0, (nowMs - lastActivatedMs) / DAY_MS);
6769
6597
  const recency = 1 / (1 + ageDays / 7);
6770
6598
  const reinforcement = Math.min(1, Math.log1p(item.activationCount ?? 0) / 3);
6771
6599
  return 0.7 * recency + 0.3 * reinforcement;
6772
6600
  };
6601
+ var unprovenDecayFactor = (item, nowMs) => {
6602
+ if ((item.activationCount ?? 0) !== 0) return 1;
6603
+ const createdAgeDays = Math.max(0, (nowMs - Date.parse(item.createdAt)) / DAY_MS);
6604
+ if (createdAgeDays <= UNPROVEN_MEMORY_GRACE_DAYS) return 1;
6605
+ const overdue = createdAgeDays - UNPROVEN_MEMORY_GRACE_DAYS;
6606
+ return Math.max(
6607
+ MIN_UNPROVEN_SCORE_FACTOR,
6608
+ 1 / (1 + overdue / UNPROVEN_DECAY_HALF_LIFE_DAYS)
6609
+ );
6610
+ };
6611
+ var activationScore = (item, nowMs) => provenActivationScore(item, nowMs) * unprovenDecayFactor(item, nowMs);
6773
6612
  var creationRecencyScore = (item, nowMs) => {
6774
6613
  const createdMs = Date.parse(item.createdAt);
6775
6614
  const ageDays = Math.max(0, (nowMs - createdMs) / 864e5);
@@ -6825,7 +6664,7 @@ var rankMemoryCandidates = (items, query, context) => {
6825
6664
 
6826
6665
  // packages/ai/memory/runtime.ts
6827
6666
  var COLD_STORAGE_CONFIDENCE = 0.3;
6828
- var MEMORY_OVERLAY_TOKEN_BUDGET = 2e3;
6667
+ var MEMORY_OVERLAY_TOKEN_BUDGET = DEFAULT_MEMORY_OVERLAY_TOKEN_BUDGET;
6829
6668
  var normalizeSelectedContent = (text) => text.trim().replace(EXPLICIT_REMEMBER_PREFIX_REGEX, "").replace(/[。!?!?]+$/u, "").trim();
6830
6669
  var STACK_TERMS = [
6831
6670
  "typescript",
@@ -6927,6 +6766,7 @@ var selectRuntimeMemoryItems = (items, context, userInput = "") => {
6927
6766
  );
6928
6767
  add(unique.find((item) => item.ownerType === "user"));
6929
6768
  add(unique.find((item) => item.ownerType === "space"));
6769
+ add(unique.find((item) => item.subjectType === "user"));
6930
6770
  add(unique.find((item) => item.kind === "procedural"));
6931
6771
  for (const item of unique) {
6932
6772
  add(item);
@@ -7046,6 +6886,36 @@ var resolveCliMemory = async (input) => {
7046
6886
  function resolvePositiveMs(value, fallback) {
7047
6887
  return parsePositiveFiniteNumberOrFallback(value, fallback);
7048
6888
  }
6889
+ function resolveRunOutcome(input) {
6890
+ switch (input.kind) {
6891
+ case "stall":
6892
+ return { status: "failed", exitCode: 1 };
6893
+ case "timeout":
6894
+ return { status: "timeout", exitCode: 124 };
6895
+ case "result": {
6896
+ const failed = input.isStalledOrTruncated || input.exitCode !== 0;
6897
+ if (!failed) {
6898
+ return { status: "done", exitCode: 0 };
6899
+ }
6900
+ return { status: "failed", exitCode: input.exitCode !== 0 ? input.exitCode : 1 };
6901
+ }
6902
+ }
6903
+ }
6904
+ var FAILED_EMPTY_ASSISTANT_REASONS = /* @__PURE__ */ new Set([
6905
+ "length_truncated",
6906
+ "stream_truncated",
6907
+ "repetition_loop",
6908
+ "stagnant_tool_calls"
6909
+ ]);
6910
+ var isRunResultStalledOrTruncated = (result) => !result.emptyAssistantOutputUsable && FAILED_EMPTY_ASSISTANT_REASONS.has(result.emptyAssistantFallbackReason ?? "");
6911
+ var resolveRunSettlementNote = (result) => {
6912
+ const reason = result.emptyAssistantFallbackReason;
6913
+ if (!reason || !FAILED_EMPTY_ASSISTANT_REASONS.has(reason)) return {};
6914
+ if (result.emptyAssistantOutputUsable) {
6915
+ return { note: `output complete but stream lacked finish frame: ${reason}` };
6916
+ }
6917
+ return { note: `empty assistant output: ${reason}` };
6918
+ };
7049
6919
  async function resolveAgentRunAgentKey(args) {
7050
6920
  if (!args.agentInput.trim() || args.agentInput.startsWith("agent-")) {
7051
6921
  return void 0;
@@ -7081,11 +6951,14 @@ async function runAgentRunCommand(args, deps) {
7081
6951
  deps.stallTimeoutMs ?? env.NOLO_LOCAL_RUN_STALL_TIMEOUT_MS,
7082
6952
  5 * 6e4
7083
6953
  );
6954
+ const stallEnvExplicitlySet = typeof env.NOLO_LOCAL_RUN_STALL_TIMEOUT_MS === "string" && env.NOLO_LOCAL_RUN_STALL_TIMEOUT_MS.trim().length > 0;
6955
+ const armStallWatchdog = hasRegistry || stallEnvExplicitlySet;
6956
+ const armTimeoutWatchdog = typeof parsed.timeoutMs === "number" && parsed.timeoutMs > 0;
7084
6957
  let timedOut = false;
7085
6958
  let stalled = false;
7086
6959
  let timeoutTimer;
7087
6960
  let stallTimer;
7088
- const activityTracker = hasRegistry ? createRunActivityTracker(childRunId, {
6961
+ const activityTracker = armStallWatchdog ? createRunActivityTracker(childRunId ?? "__foreground__", {
7089
6962
  env,
7090
6963
  homedir: deps.homedir,
7091
6964
  fs: deps.fs,
@@ -7098,7 +6971,7 @@ async function runAgentRunCommand(args, deps) {
7098
6971
  activityTracker?.dispose();
7099
6972
  };
7100
6973
  let rejectOnWatchdogFailure;
7101
- const watchdogFailure = hasRegistry ? new Promise((_, reject) => {
6974
+ const watchdogFailure = armStallWatchdog || armTimeoutWatchdog ? new Promise((_, reject) => {
7102
6975
  rejectOnWatchdogFailure = reject;
7103
6976
  }) : void 0;
7104
6977
  const raceWithWatchdog = (work) => watchdogFailure ? Promise.race([work, watchdogFailure]) : work;
@@ -7108,7 +6981,15 @@ async function runAgentRunCommand(args, deps) {
7108
6981
  } catch {
7109
6982
  }
7110
6983
  };
7111
- if (hasRegistry) {
6984
+ const finalizeWatchdogOutcome = (outcome, note) => {
6985
+ if (!hasRegistry) return;
6986
+ (deps.finalizeRunRecord ?? finalizeRunRecord)(
6987
+ childRunId,
6988
+ { status: outcome.status, exitCode: outcome.exitCode, note },
6989
+ { env, homedir: deps.homedir, fs: deps.fs, now: deps.now }
6990
+ );
6991
+ };
6992
+ if (armStallWatchdog) {
7112
6993
  stallTimer = setInterval(() => {
7113
6994
  if (timedOut || stalled) return;
7114
6995
  const activity = activityTracker?.getActivity();
@@ -7126,31 +7007,25 @@ async function runAgentRunCommand(args, deps) {
7126
7007
  const note = `stalled: no progress for ${stallTimeoutMs}ms`;
7127
7008
  output.write(`[nolo] local run ${note}
7128
7009
  `);
7129
- (deps.finalizeRunRecord ?? finalizeRunRecord)(
7130
- childRunId,
7131
- { status: "failed", note },
7132
- { env, homedir: deps.homedir, fs: deps.fs, now: deps.now }
7133
- );
7010
+ const outcome = resolveRunOutcome({ kind: "stall" });
7011
+ finalizeWatchdogOutcome(outcome, note);
7134
7012
  rejectOnWatchdogFailure?.(new Error(`[nolo] local run ${note}`));
7135
- exitFromWatchdog(1);
7013
+ exitFromWatchdog(outcome.exitCode);
7136
7014
  }
7137
7015
  }, Math.min(stallTimeoutMs, 1e4));
7138
- if (typeof parsed.timeoutMs === "number" && parsed.timeoutMs > 0) {
7139
- timeoutTimer = setTimeout(() => {
7140
- timedOut = true;
7141
- clearWatchdogs();
7142
- const note = `timed out after ${parsed.timeoutMs}ms`;
7143
- output.write(`[nolo] local run ${note}
7016
+ }
7017
+ if (armTimeoutWatchdog) {
7018
+ timeoutTimer = setTimeout(() => {
7019
+ timedOut = true;
7020
+ clearWatchdogs();
7021
+ const note = `timed out after ${parsed.timeoutMs}ms`;
7022
+ output.write(`[nolo] local run ${note}
7144
7023
  `);
7145
- (deps.finalizeRunRecord ?? finalizeRunRecord)(
7146
- childRunId,
7147
- { status: "timeout", note },
7148
- { env, homedir: deps.homedir, fs: deps.fs, now: deps.now }
7149
- );
7150
- rejectOnWatchdogFailure?.(new Error(`[nolo] local run ${note}`));
7151
- exitFromWatchdog(124);
7152
- }, parsed.timeoutMs);
7153
- }
7024
+ const outcome = resolveRunOutcome({ kind: "timeout" });
7025
+ finalizeWatchdogOutcome(outcome, note);
7026
+ rejectOnWatchdogFailure?.(new Error(`[nolo] local run ${note}`));
7027
+ exitFromWatchdog(outcome.exitCode);
7028
+ }, parsed.timeoutMs);
7154
7029
  }
7155
7030
  const wantsFallbackSuggestions = parsed.fallbackAgentKeys && parsed.fallbackAgentKeys.length > 0;
7156
7031
  const rawAgentInput = resolveRawAgentInput(args, deps.commandPath) ?? parsed.agentKey;
@@ -7305,22 +7180,8 @@ async function runAgentRunCommand(args, deps) {
7305
7180
  let memoryOverlayLayer = null;
7306
7181
  let memoryUseGuidanceLayer = null;
7307
7182
  if (!isSubtask) {
7308
- const AGENTS_MD_MAX = 8192;
7309
- for (const name of ["AGENTS.md", "CLAUDE.md"]) {
7310
- const agentsMdPath = join2(cliCwd, name);
7311
- if (existsSync2(agentsMdPath)) {
7312
- try {
7313
- let mdContent = readFileSync2(agentsMdPath, "utf8").trim();
7314
- if (!mdContent) continue;
7315
- if (Buffer.byteLength(mdContent, "utf8") > AGENTS_MD_MAX) {
7316
- mdContent = Buffer.from(mdContent, "utf8").subarray(0, AGENTS_MD_MAX).toString("utf8") + "\n\n<!-- AGENTS.md truncated -->";
7317
- }
7318
- scopedLayers.push(buildAgentsMdLayer(mdContent, name));
7319
- break;
7320
- } catch {
7321
- }
7322
- }
7323
- }
7183
+ const agentsMdLayer = readAgentsMdLayerFromDisk(cliCwd);
7184
+ if (agentsMdLayer) scopedLayers.push(agentsMdLayer);
7324
7185
  skillContentBlocks.push(...buildSkillContextBlocks(skillReferences));
7325
7186
  scopedLayers.push(buildSkillDiscoveryContextLayer(cliCwd));
7326
7187
  if (!deps.memoryRecallDisabled) {
@@ -7401,6 +7262,7 @@ async function runAgentRunCommand(args, deps) {
7401
7262
  })
7402
7263
  );
7403
7264
  clearWatchdogs();
7265
+ let runCreditsTotal = result.turnCredits;
7404
7266
  if (result.exitCode !== 0 && isQuotaExhaustedError(result.localError) && isLocalRun && parsed.fallbackAgentKeys && parsed.fallbackAgentKeys.length > 0) {
7405
7267
  const fallbackAgentKey = parsed.fallbackAgentKeys[0];
7406
7268
  output.write(
@@ -7416,7 +7278,11 @@ async function runAgentRunCommand(args, deps) {
7416
7278
  clearWatchdogs();
7417
7279
  if (fallbackResult.exitCode === 0) {
7418
7280
  result = fallbackResult;
7419
- } else {
7281
+ }
7282
+ if (fallbackResult.turnCredits !== void 0) {
7283
+ runCreditsTotal = (runCreditsTotal ?? 0) + fallbackResult.turnCredits;
7284
+ }
7285
+ if (fallbackResult.exitCode !== 0) {
7420
7286
  result = {
7421
7287
  ...fallbackResult,
7422
7288
  ...fallbackResult.dialogId || result.dialogId ? { dialogId: fallbackResult.dialogId ?? result.dialogId } : {}
@@ -7459,6 +7325,9 @@ async function runAgentRunCommand(args, deps) {
7459
7325
  );
7460
7326
  clearWatchdogs();
7461
7327
  result = turnResult;
7328
+ if (turnResult.turnCredits !== void 0) {
7329
+ runCreditsTotal = (runCreditsTotal ?? 0) + turnResult.turnCredits;
7330
+ }
7462
7331
  if (result.exitCode !== 0) {
7463
7332
  break;
7464
7333
  }
@@ -7509,15 +7378,22 @@ async function runAgentRunCommand(args, deps) {
7509
7378
  }
7510
7379
  }
7511
7380
  if (typeof childRunId === "string" && childRunId.length > 0) {
7512
- const isStalledOrTruncated = result.emptyAssistantFallbackReason === "length_truncated" || result.emptyAssistantFallbackReason === "stream_truncated" || result.emptyAssistantFallbackReason === "repetition_loop" || result.emptyAssistantFallbackReason === "stagnant_tool_calls";
7513
- const truncationNote = isStalledOrTruncated ? { note: `empty assistant output: ${result.emptyAssistantFallbackReason}` } : {};
7514
- const finalStatus = isStalledOrTruncated || result.exitCode !== 0 ? "failed" : "done";
7381
+ const isStalledOrTruncated = isRunResultStalledOrTruncated(result);
7382
+ const truncationNote = resolveRunSettlementNote(result);
7383
+ const outcome = resolveRunOutcome({
7384
+ kind: "result",
7385
+ exitCode: result.exitCode,
7386
+ isStalledOrTruncated
7387
+ });
7515
7388
  await (deps.finalizeRunRecord ?? finalizeRunRecord)(
7516
7389
  childRunId,
7517
7390
  {
7518
- status: finalStatus,
7519
- exitCode: result.exitCode,
7391
+ status: outcome.status,
7392
+ exitCode: outcome.exitCode,
7520
7393
  dialogId: result.dialogId,
7394
+ // run 级累计的平台积分(主轮 + fallback + drain 轮):dock 行据此显示
7395
+ // 「⚡ x.xx」,让派发任务的消耗可见。undefined = 全程无平台计费。
7396
+ ...runCreditsTotal !== void 0 ? { credits: runCreditsTotal } : {},
7521
7397
  ...truncationNote
7522
7398
  },
7523
7399
  {
@@ -7562,6 +7438,7 @@ function isBalanceExhaustedError(error) {
7562
7438
  export {
7563
7439
  resolveAgentModelIdentity,
7564
7440
  resolveAgentContextWindow,
7441
+ formatCreditsChip,
7565
7442
  formatTokenCount,
7566
7443
  renderTokenStatus,
7567
7444
  resolveCliColorEnabled,
@@ -7588,8 +7465,6 @@ export {
7588
7465
  getActiveThemeMode,
7589
7466
  setActiveThemeMode,
7590
7467
  setActiveBrightness,
7591
- getActiveDensity,
7592
- setActiveDensity,
7593
7468
  detectSystemBrightnessFromEnv,
7594
7469
  resolveTuiBrightness,
7595
7470
  resolveTuiThemeMode,
@@ -7602,8 +7477,10 @@ export {
7602
7477
  themeText,
7603
7478
  tuiRenderThemeFingerprint,
7604
7479
  setMathRenderingEnabled,
7480
+ polishBreathInsertsBlankBetween,
7605
7481
  formatAssistantDisplay,
7606
- normalizeToolDisplayMode,
7482
+ redactSecrets,
7483
+ TUI_TREE_MARKER,
7607
7484
  COLLAPSED_PASTE_PLACEHOLDER_RE,
7608
7485
  shouldCollapsePaste,
7609
7486
  createCollapsedPasteStore,
@@ -7615,6 +7492,7 @@ export {
7615
7492
  expandRangeToCollapsedPasteChips,
7616
7493
  findCollapsedPasteSpanAt,
7617
7494
  runAgentTurn,
7495
+ readAgentsMdLayerFromDisk,
7618
7496
  resolveWorkflowReference,
7619
7497
  resolveSkillReference,
7620
7498
  buildSkillContextBlocks,
@@ -7622,6 +7500,9 @@ export {
7622
7500
  normalizeCliImageInput,
7623
7501
  inspectLocalRunWorkspace,
7624
7502
  resolveCliMemory,
7503
+ resolveRunOutcome,
7504
+ isRunResultStalledOrTruncated,
7505
+ resolveRunSettlementNote,
7625
7506
  runAgentRunCommand,
7626
7507
  isBalanceExhaustedError
7627
7508
  };