nolo-cli 0.19.0 → 0.24.0-alpha.14

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 (134) hide show
  1. package/README.md +9 -18
  2. package/{_executeModel-X5PCIZN6.js → _executeModel-ZPNYAEFW.js} +46 -44
  3. package/{action-LT7BRFLK.js → action-5MNAXR5B.js} +7 -6
  4. package/{addContentAction-PL3KKBDH.js → addContentAction-UNSYTVTB.js} +5 -5
  5. package/{addDialogAgentAction-FCMSKYAF.js → addDialogAgentAction-LJGNP2AO.js} +5 -5
  6. package/{agentDeleteCommand-2BZDQV36.js → agentDeleteCommand-5ZV4433K.js} +27 -26
  7. package/{agentEmailCommands-2QJDGRMB.js → agentEmailCommands-Z7KOYDVQ.js} +57 -55
  8. package/{agentListCommands-6VEPTKBI.js → agentListCommands-UQUXJYUN.js} +30 -26
  9. package/{agentMachineCommands-NVVHNWYI.js → agentMachineCommands-53O6P6L5.js} +25 -24
  10. package/{agentRecordCommands-4GGRDMD4.js → agentRecordCommands-ZRYNBSSS.js} +52 -50
  11. package/{agentRunCommand-XOLQNKBZ.js → agentRunCommand-B73MKVOG.js} +55 -54
  12. package/{agentRunControl-Y5AFH5PQ.js → agentRunControl-SNUKXN6J.js} +6 -2
  13. package/{agentRuntimeLocal-7WYWCGL4.js → agentRuntimeLocal-GVNERRKQ.js} +21 -18
  14. package/{agentSlice-EAXPGEYV.js → agentSlice-OJBHIL34.js} +5 -5
  15. package/{appDeleteCommands-77KR2RJ4.js → appDeleteCommands-23Z3Z6KE.js} +23 -22
  16. package/{appDeployCommands-K7CAHGBI.js → appDeployCommands-3MFAR4Z7.js} +23 -22
  17. package/{appDeployStatusCommands-4OEKLTBT.js → appDeployStatusCommands-JHU6GSN4.js} +23 -22
  18. package/{appGetCommands-VM2WDLAZ.js → appGetCommands-24J55H2Y.js} +23 -22
  19. package/{appListCommands-GX6BBA6U.js → appListCommands-HE5KP3PH.js} +23 -22
  20. package/{authCommand-DY6UUL6O.js → authCommand-KGRK6MNU.js} +12 -13
  21. package/{authCommands-XTBV7OFZ.js → authCommands-JSOG2H4E.js} +4 -5
  22. package/{chunk-OWOHNGCN.js → chunk-2MBTWINN.js} +42 -3
  23. package/{chunk-MVQ4TBSD.js → chunk-54VHTARN.js} +1 -1
  24. package/{chunk-RQN4SEKL.js → chunk-5DWZ46RJ.js} +1 -1
  25. package/{chunk-VYHDJEQD.js → chunk-5EDKWSQN.js} +136 -78
  26. package/{chunk-Z4NR32XR.js → chunk-5RGU3J7P.js} +13 -0
  27. package/{chunk-7BUYEZ6U.js → chunk-7H3UTGPM.js} +12 -72
  28. package/{chunk-3ZX7PERL.js → chunk-AAWJ5S3G.js} +20 -7
  29. package/{chunk-SCTYAHYK.js → chunk-ADE6577S.js} +237 -142
  30. package/{chunk-PJSV3IZ5.js → chunk-AOPOTHFK.js} +1 -1
  31. package/{chunk-3ND52WIL.js → chunk-ATWUOTJP.js} +180 -24
  32. package/{chunk-SWDPIPOY.js → chunk-AZW5AHMW.js} +1 -1
  33. package/chunk-CF7S2RYZ.js +376 -0
  34. package/{chunk-L4F3I4SR.js → chunk-CV6BM7LU.js} +4 -6
  35. package/{chunk-G5U6LDLG.js → chunk-CWTT2NZ6.js} +6 -6
  36. package/{chunk-MKA6QE5E.js → chunk-CZHE6LLV.js} +135 -55
  37. package/{chunk-KFEVY3ST.js → chunk-D34RI6NI.js} +18 -8
  38. package/{chunk-XRZJGPFI.js → chunk-FON47ACC.js} +4 -4
  39. package/{chunk-K6EUAOW3.js → chunk-FXFGNOT2.js} +67 -1
  40. package/{chunk-LLWKHDG4.js → chunk-G36SUBHA.js} +14 -6
  41. package/{chunk-REA4JLBH.js → chunk-GD23ZX7Z.js} +7 -6
  42. package/{chunk-ROWQEJRP.js → chunk-GE6IWA5N.js} +12 -4
  43. package/{chunk-EDV7EBOI.js → chunk-GGB6MPHD.js} +2155 -1503
  44. package/{chunk-IYEXFJPC.js → chunk-GNGOZMU2.js} +64 -57
  45. package/{chunk-4T5F7YZQ.js → chunk-I6RJGORZ.js} +1 -1
  46. package/{chunk-DFCFMCY6.js → chunk-J2WT5CEO.js} +2 -2
  47. package/{chunk-AHCY2S2I.js → chunk-JJ2WUBFN.js} +1 -1
  48. package/{chunk-UGUW3FHG.js → chunk-JMSCXQUG.js} +2 -2
  49. package/{chunk-VDXCXCXH.js → chunk-KURQBS7S.js} +1 -1
  50. package/{chunk-FQZZLA3C.js → chunk-KXKQO4Q6.js} +17 -0
  51. package/{chunk-3Q7WHGCB.js → chunk-L65QKSVJ.js} +1 -1
  52. package/{chunk-UAIVG4J6.js → chunk-LEHTC2FI.js} +1 -8
  53. package/{chunk-UBHAWFQ6.js → chunk-LLDGWLML.js} +42 -19
  54. package/{chunk-JLA4FXYC.js → chunk-MALVZUYL.js} +1 -1
  55. package/{chunk-N54HIJVJ.js → chunk-MCXQNEJ3.js} +101 -9
  56. package/{chunk-GFUIQ4RY.js → chunk-MXFQHRSS.js} +1 -1
  57. package/{chunk-LSFAWXKC.js → chunk-N7J63Q6C.js} +1 -0
  58. package/{chunk-6Y7A3OQQ.js → chunk-NRY4GCKJ.js} +2 -0
  59. package/{chunk-ELT65PZW.js → chunk-OXLLFZNF.js} +1 -1
  60. package/{chunk-YGW4G5W5.js → chunk-P3RZ6DHN.js} +133 -25
  61. package/{chunk-5MIDPSSK.js → chunk-PB7G5MAM.js} +3 -3
  62. package/{chunk-HEGV7BH3.js → chunk-PMCKFQYC.js} +1 -1
  63. package/{chunk-23FZYPLC.js → chunk-PTKI7TIN.js} +911 -1064
  64. package/{chunk-MHSGJW4Z.js → chunk-PXPISYEH.js} +44 -14
  65. package/{chunk-I2ZJKTG5.js → chunk-PY7VGC5M.js} +29 -10
  66. package/{chunk-PIZYJVHQ.js → chunk-RM464B7I.js} +4 -4
  67. package/{chunk-XHG22OEX.js → chunk-ROJI6DRT.js} +2 -2
  68. package/{chunk-VNAEBGDK.js → chunk-RZNYL4LW.js} +123 -26
  69. package/{chunk-RVOUXDFU.js → chunk-S6KVFOUG.js} +6 -34
  70. package/{chunk-BV4VEE3W.js → chunk-S7AV3NCK.js} +277 -357
  71. package/{chunk-R35XMF6F.js → chunk-SIUEKVK4.js} +484 -67
  72. package/{chunk-B5TOXZAK.js → chunk-SIZQB7BD.js} +1 -1
  73. package/{chunk-DFFIZ25G.js → chunk-SLOGKB7M.js} +4 -1
  74. package/{chunk-MPVYDS7I.js → chunk-TLYHUIWF.js} +3 -3
  75. package/{chunk-HWW2M4SN.js → chunk-TNM27YZ7.js} +5 -4
  76. package/{chunk-OK6YSWLH.js → chunk-TWA4L7J7.js} +1 -1
  77. package/chunk-UZ6XXCVR.js +50 -0
  78. package/{chunk-DKZFOK24.js → chunk-WCP5FDTF.js} +16 -304
  79. package/{chunk-TRT4I4UD.js → chunk-WSQLK2KR.js} +2 -2
  80. package/{chunk-FXUSIQC2.js → chunk-XNZJVYFS.js} +1 -1
  81. package/{chunk-V3ED5AWK.js → chunk-YJY7HODU.js} +27 -16
  82. package/chunk-YYLHWIW6.js +48 -0
  83. package/{chunk-CZ6NYQTV.js → chunk-ZFL3LTU7.js} +2 -2
  84. package/{chunk-34WPV75U.js → chunk-ZUXIXYJI.js} +1 -1
  85. package/{chunk-VC6OZXO5.js → chunk-ZWJOGMKH.js} +2 -26
  86. package/{cliExecutor-DNE7TCC4.js → cliExecutor-K3F7DMRO.js} +2 -2
  87. package/{codingSkills-36KN5NQT.js → codingSkills-EN6Z6G6U.js} +4 -4
  88. package/{createAgentAutomationAction-GNFF524X.js → createAgentAutomationAction-PO62NUR7.js} +19 -18
  89. package/{createDialogAction-RB55KCLO.js → createDialogAction-ORFRYI3C.js} +22 -20
  90. package/{createPageAction-Y7343GLU.js → createPageAction-XWVMNTYH.js} +20 -19
  91. package/{db-W3O47YGF.js → db-W5UHK4P3.js} +150 -0
  92. package/{dbSlice-CGSRFJN3.js → dbSlice-5BKDURDL.js} +4 -4
  93. package/{dialogCommands-IAZK6YHQ.js → dialogCommands-XTJHROXM.js} +24 -23
  94. package/{dialogSlice-UQAXF23P.js → dialogSlice-DJXC53YW.js} +24 -18
  95. package/{docCreateCommands-I2EWZGCI.js → docCreateCommands-T2XRVXYS.js} +24 -27
  96. package/{docDeleteCommands-IZ45UZKG.js → docDeleteCommands-L7RXFXY3.js} +25 -24
  97. package/{docReadCommands-R6Z4QJYU.js → docReadCommands-M5PYNADQ.js} +24 -23
  98. package/{docStore-5HOJ4D7D.js → docStore-6LKLLFDW.js} +20 -19
  99. package/{docUpdateCommands-2LRBKQPY.js → docUpdateCommands-OA5EGPLQ.js} +24 -27
  100. package/{fetchMessages-BMGVZVSE.js → fetchMessages-W4IKHPCS.js} +2 -3
  101. package/{getPricing-G7DLAST6.js → getPricing-6C3C47Y4.js} +3 -3
  102. package/{handleSendMessageAction-Q4HY35WX.js → handleSendMessageAction-ILFGZTN5.js} +23 -20
  103. package/index.js +74 -74
  104. package/{localDialogRead-4H3QJFLW.js → localDialogRead-JXTAHW2E.js} +2 -2
  105. package/{machineCommands-PKBPQKTY.js → machineCommands-AKAQGPAO.js} +59 -57
  106. package/{machineWsRunDispatch-UZAW4OXJ.js → machineWsRunDispatch-RSBERM64.js} +51 -49
  107. package/{models-DJFSJ7SW.js → models-ILWKIX2Y.js} +2 -2
  108. package/package.json +1 -1
  109. package/{read-3FMPZPER.js → read-F3L3T2WJ.js} +2 -1
  110. package/{readDocTool-VSYHKK3V.js → readDocTool-FOHWVA4R.js} +1 -1
  111. package/{readlineWorkspace-Y2RDOFZG.js → readlineWorkspace-TUH3SFHO.js} +937 -430
  112. package/{removeDialogAgentAction-OAGA2G3U.js → removeDialogAgentAction-QCFGIUIH.js} +8 -7
  113. package/{runAgentClientLoop-NBPIKJ6J.js → runAgentClientLoop-C3QB2EXV.js} +45 -38
  114. package/{setDialogExtraReferencesAction-JZEXA5SL.js → setDialogExtraReferencesAction-DK4CKERU.js} +5 -5
  115. package/{setPrimaryDialogAgentAction-4ZUQQKDG.js → setPrimaryDialogAgentAction-SJSO6N27.js} +8 -7
  116. package/{settingSlice-7MNKHZ6R.js → settingSlice-XNM5AFPU.js} +6 -5
  117. package/{spaceCommands-DHCZ7J5I.js → spaceCommands-SXYOFGUJ.js} +25 -24
  118. package/{spaceSlice-ZXLLG3CR.js → spaceSlice-6BHMRP5O.js} +21 -18
  119. package/{streamAgentChatTurn-L3YW6LSB.js → streamAgentChatTurn-GF5W5OIP.js} +140 -98
  120. package/switchDialogAgentAction-4OAC6QRN.js +61 -0
  121. package/{tableCommands-BEQAGE4I.js → tableCommands-QSZILI7I.js} +1 -1
  122. package/{toolApiClient-QI3YBNWB.js → toolApiClient-ISM4ZQX4.js} +2 -2
  123. package/{tools-ECGXR5QF.js → tools-KBCLJ43G.js} +36 -33
  124. package/{tuiSummaryLlmCaller-OMSCNHXA.js → tuiSummaryLlmCaller-ARIMGILF.js} +6 -6
  125. package/{updateCommands-XTW55TOT.js → updateCommands-AK2RDOFR.js} +8 -2
  126. package/{updateDialogTitleAction-HQ2EUDFT.js → updateDialogTitleAction-LMHZUY4Q.js} +28 -27
  127. package/chunk-DL6EPNIK.js +0 -50
  128. package/chunk-V5R4NZP2.js +0 -78
  129. package/chunk-X4Q4UZEY.js +0 -26
  130. package/queryRunOverlay-O4LOMMPO.js +0 -161
  131. package/runOverlayPresentation-ZO4VCWEU.js +0 -11
  132. package/types-ZSTJ7MPR.js +0 -7
  133. package/{chunk-X7W4TNB4.js → chunk-CMKUBIGW.js} +3 -3
  134. package/{runtimeDoctorCommands-E2OUORHK.js → runtimeDoctorCommands-HSU632DY.js} +3 -3
@@ -3,11 +3,10 @@ import {
3
3
  refreshAntigravityOAuthToken,
4
4
  refreshOpenAiCodexToken,
5
5
  refreshXaiOAuthToken
6
- } from "./chunk-OK6YSWLH.js";
6
+ } from "./chunk-TWA4L7J7.js";
7
7
  import {
8
- buildLocalAgentLookupKeys,
9
- shouldReadAgentKeyRemotely
10
- } from "./chunk-SWDPIPOY.js";
8
+ buildLocalAgentLookupKeys
9
+ } from "./chunk-AZW5AHMW.js";
11
10
  import {
12
11
  LOCAL_CODEX_AGENT_ID,
13
12
  LOCAL_CODEX_AGENT_KEY,
@@ -16,96 +15,111 @@ import {
16
15
  } from "./chunk-VHBWHNVT.js";
17
16
  import {
18
17
  inferCaptureIntent
19
- } from "./chunk-MVQ4TBSD.js";
18
+ } from "./chunk-54VHTARN.js";
20
19
  import {
21
20
  inlineImageUrlsForCustomProvider,
22
21
  prepareTools
23
- } from "./chunk-CZ6NYQTV.js";
22
+ } from "./chunk-ZFL3LTU7.js";
24
23
  import {
25
24
  getAntigravityUserAgent,
26
25
  isAntigravityOAuthAgent,
27
26
  readAntigravityProjectId,
28
27
  resolveAntigravityCloudCodeBaseUrl
29
- } from "./chunk-FXUSIQC2.js";
28
+ } from "./chunk-XNZJVYFS.js";
30
29
  import {
31
- FORCED_TOOLS,
32
- addDefaultLightWebToolsForConfiguredAgents,
33
- addDefaultSystemCapabilityTools,
34
- appendEnabledPackPromptPatches,
35
- applyDisabledTools,
36
- applySystemBuiltinSkillFilter,
37
- expandEnabledPacks,
38
30
  readXPostFunc,
39
31
  readXPostFunctionSchema,
40
32
  readXhsProfileFunc,
41
33
  readXhsProfileFunctionSchema,
42
34
  rememberMemoryFunctionSchema,
43
- resolveEffectiveEnabledPacks
44
- } from "./chunk-EDV7EBOI.js";
35
+ resolveAgentRequiredPackIds,
36
+ setTodoListFunc
37
+ } from "./chunk-GGB6MPHD.js";
45
38
  import {
46
39
  createDrainExhaustedResponse,
47
40
  isCoreDrainingBody,
48
41
  normalizeNonNegativeMs,
49
42
  parseRetryAfterHeaderMs
50
43
  } from "./chunk-RQSOAMPJ.js";
51
- import {
52
- normalizeDialogTitle
53
- } from "./chunk-OWOHNGCN.js";
54
44
  import {
55
45
  getDefaultCliLocalRuntimeDb
56
46
  } from "./chunk-WJRJDX2W.js";
57
47
  import {
58
- prepareTokenUsageData
59
- } from "./chunk-SCTYAHYK.js";
48
+ applyTokenUsageToDayStats,
49
+ prepareTokenUsageData,
50
+ resolveBuiltinPlatformAgentConfig,
51
+ runKeyed
52
+ } from "./chunk-ADE6577S.js";
60
53
  import {
61
54
  BUILTIN_TITLE_LLM_CONFIG
62
55
  } from "./chunk-XOIF2TNF.js";
63
56
  import {
64
57
  fetchServerSyncedCredential
65
- } from "./chunk-REA4JLBH.js";
58
+ } from "./chunk-GD23ZX7Z.js";
59
+ import {
60
+ require_date_fns
61
+ } from "./chunk-M76ECQRD.js";
62
+ import {
63
+ FORCED_TOOLS,
64
+ addDefaultLightWebToolsForConfiguredAgents,
65
+ addDefaultSystemCapabilityTools,
66
+ appendEnabledPackPromptPatches,
67
+ applyDisabledTools,
68
+ applySystemBuiltinSkillFilter,
69
+ expandEnabledPacks,
70
+ resolveEffectiveEnabledPacks
71
+ } from "./chunk-CF7S2RYZ.js";
66
72
  import {
67
73
  buildAgentRuntimeDialogWritePlan,
74
+ buildLoadSkillExecutor,
68
75
  buildLocalWorkspaceOpenAiTools,
69
76
  buildLocalWorkspaceToolset,
77
+ buildNoloWorkspaceCliToolExecutors,
70
78
  createHybridRecordStore,
71
79
  createLocalWorkspaceToolExecutors,
72
80
  dialogMessageRecordToAgentRuntimeMessage,
73
81
  executeLocalToolWithPolicy,
74
82
  executeOpenAiCompatibleChatCompletion,
83
+ isAgentCoolingDown,
75
84
  parseSseDataLineJson,
76
85
  parseSseDataLineObject,
86
+ parseSyncServersEnv,
77
87
  readOpenAiCompatibleSseCompletion,
88
+ resolveAgentNextAvailableAt,
78
89
  resolveOpenAiCompatibleProviderConfig
79
- } from "./chunk-R35XMF6F.js";
90
+ } from "./chunk-SIUEKVK4.js";
80
91
  import {
81
92
  buildPlatformChatCompletionRequest,
82
93
  canUsePlatformChatProvider,
94
+ hasDirectOpenAiCompatibleProvider,
83
95
  parsePlatformChatCompletionData,
84
96
  parsePlatformChatCompletionResponse,
85
97
  readSseDataValues,
86
98
  resolvePlatformChatProviderConfig,
87
99
  shouldUsePlatformChatProvider
88
- } from "./chunk-N54HIJVJ.js";
100
+ } from "./chunk-MCXQNEJ3.js";
89
101
  import {
90
102
  resolveAgentRuntimeConfigFromRecord
91
103
  } from "./chunk-OIQPORYI.js";
92
104
  import {
93
105
  summarizeToolArguments
94
- } from "./chunk-YGW4G5W5.js";
106
+ } from "./chunk-P3RZ6DHN.js";
95
107
  import {
96
108
  convertMessagesToResponsesInput,
97
109
  toResponsesTools
98
- } from "./chunk-KFEVY3ST.js";
110
+ } from "./chunk-D34RI6NI.js";
99
111
  import {
100
112
  resolvePlatformAuthToken
101
- } from "./chunk-MHSGJW4Z.js";
113
+ } from "./chunk-PXPISYEH.js";
102
114
  import {
103
- buildLoadSkillExecutor,
104
- buildNoloWorkspaceCliToolExecutors,
105
115
  buildNoloWorkspaceOpenAiTools,
106
116
  parseNoloWorkspaceToolArguments,
107
117
  resolveRuntimeToolSurfaceForAgent
108
- } from "./chunk-DKZFOK24.js";
118
+ } from "./chunk-WCP5FDTF.js";
119
+ import {
120
+ buildDialogFallbackTitleFromUserInput,
121
+ normalizeDialogTitle
122
+ } from "./chunk-2MBTWINN.js";
109
123
  import {
110
124
  clipCompactText
111
125
  } from "./chunk-5MMAK75Q.js";
@@ -123,10 +137,7 @@ import {
123
137
  } from "./chunk-JZPBVBIY.js";
124
138
  import {
125
139
  isAdaptiveThinkingModelId
126
- } from "./chunk-UAIVG4J6.js";
127
- import {
128
- toErrorMessage
129
- } from "./chunk-HNMXYYJR.js";
140
+ } from "./chunk-LEHTC2FI.js";
130
141
  import {
131
142
  canonicalizeToolNames
132
143
  } from "./chunk-ZZ2MH7LE.js";
@@ -134,13 +145,14 @@ import {
134
145
  compactWhitespace
135
146
  } from "./chunk-DWLEOOGR.js";
136
147
  import {
148
+ ackRunRecord,
137
149
  checkStaleRun,
138
150
  finalizeRunRecord,
139
151
  findRunRecord,
140
152
  gcRunRecords,
141
153
  queryRunRecords,
142
154
  spawnLocalBackgroundRun
143
- } from "./chunk-VNAEBGDK.js";
155
+ } from "./chunk-RZNYL4LW.js";
144
156
  import {
145
157
  TASK_PREVIEW_MAX,
146
158
  formatListRunsCard,
@@ -152,14 +164,17 @@ import {
152
164
  isAgentRunTerminalStatus,
153
165
  resolveRunLabel,
154
166
  shortRunId
155
- } from "./chunk-FQZZLA3C.js";
167
+ } from "./chunk-KXKQO4Q6.js";
156
168
  import {
157
169
  CliProviderQuotaError,
158
170
  executeCli
159
- } from "./chunk-X7W4TNB4.js";
171
+ } from "./chunk-CMKUBIGW.js";
160
172
  import {
161
173
  buildCliPrompt
162
174
  } from "./chunk-ATNTHIZI.js";
175
+ import {
176
+ toErrorMessage
177
+ } from "./chunk-HNMXYYJR.js";
163
178
  import {
164
179
  isCompiledBinary,
165
180
  parseUserIdFromAuthToken
@@ -169,9 +184,10 @@ import {
169
184
  } from "./chunk-OLZBQWZU.js";
170
185
  import {
171
186
  createTokenKey,
187
+ createTokenStatsKey,
172
188
  createUserKey,
173
189
  dialogMessageRange
174
- } from "./chunk-6Y7A3OQQ.js";
190
+ } from "./chunk-NRY4GCKJ.js";
175
191
  import {
176
192
  asOptionalFiniteNumber
177
193
  } from "./chunk-WWC7I6TA.js";
@@ -182,15 +198,8 @@ import {
182
198
  asTrimmedLowercaseString
183
199
  } from "./chunk-QSJ3HFEF.js";
184
200
  import {
185
- BUILTIN_NOLO_AGENT_KEY,
186
- PUBLIC_DEEPSEEK_V4_FLASH_AGENT_KEY,
187
- PUBLIC_DEEPSEEK_V4_PRO_AGENT_KEY,
188
- PUBLIC_GLM_52_AGENT_KEY,
189
- PUBLIC_KIMI_K26_IMAGE_AGENT_KEY
190
- } from "./chunk-3ND52WIL.js";
191
- import {
192
- asTrimmedString
193
- } from "./chunk-3EUW3WEO.js";
201
+ isSystemBuiltinTrustedAgentKey
202
+ } from "./chunk-ATWUOTJP.js";
194
203
  import {
195
204
  isRecord
196
205
  } from "./chunk-KUG52IAP.js";
@@ -206,6 +215,9 @@ import {
206
215
  import {
207
216
  asOptionalTrimmedString
208
217
  } from "./chunk-G3SUCT4W.js";
218
+ import {
219
+ __toESM
220
+ } from "./chunk-4VNS5WPM.js";
209
221
 
210
222
  // packages/cli/tui/i18n.ts
211
223
  var ZH_PATTERNS = /^zh/i;
@@ -782,6 +794,18 @@ var STRINGS = {
782
794
  en: "Forked dialog {0} \u2192 {1} (no summary needed).",
783
795
  zh: "\u5DF2\u5206\u53C9\u5BF9\u8BDD {0} \u2192 {1}\uFF08\u65E0\u9700\u538B\u7F29\uFF09\u3002"
784
796
  },
797
+ compactSuccess: {
798
+ en: "\u2713 Compacted dialog {0} \u2192 {1} in {2}.",
799
+ zh: "\u2713 \u5DF2\u538B\u7F29\u5BF9\u8BDD {0} \u2192 {1}\uFF0C\u8017\u65F6 {2}\u3002"
800
+ },
801
+ compactSuccessWithCount: {
802
+ en: "\u2713 Compacted dialog {0} \u2192 {1} in {2} ({3} messages compressed).",
803
+ zh: "\u2713 \u5DF2\u538B\u7F29\u5BF9\u8BDD {0} \u2192 {1}\uFF0C\u8017\u65F6 {2}\uFF08\u538B\u7F29 {3} \u6761\u6D88\u606F\uFF09\u3002"
804
+ },
805
+ compactForked: {
806
+ en: "\u2713 Forked dialog {0} \u2192 {1} in {2} (no summary needed).",
807
+ zh: "\u2713 \u5DF2\u5206\u53C9\u5BF9\u8BDD {0} \u2192 {1}\uFF0C\u8017\u65F6 {2}\uFF08\u65E0\u9700\u538B\u7F29\uFF09\u3002"
808
+ },
785
809
  agentCurrent: { en: "Current agent: {0} ({1})", zh: "\u5F53\u524D agent\uFF1A{0}\uFF08{1}\uFF09" },
786
810
  agentUnknown: {
787
811
  en: `I don't know agent "{0}" yet.
@@ -849,6 +873,10 @@ Use /switch, /switch list, /switch minimax-m3, or a full agent key.`,
849
873
  zh: "nolo {0}\n\u7528 nolo update \u66F4\u65B0\u5F53\u524D\u5B89\u88C5\u3002\n\u5982\u679C\u672C\u5730\u4ED3\u5E93\u8F93\u51FA\u7684\u7248\u672C\u4E0D\u540C\uFF0C\u8BF7\u5148\u53D1\u5E03/\u5B89\u88C5\u6700\u65B0\u7684 npm \u5305\u3002"
850
874
  },
851
875
  versionUnknown: { en: "unknown version", zh: "\u672A\u77E5\u7248\u672C" },
876
+ updateAvailable: {
877
+ en: "New nolo {0} available (you have {1}) \u2014 run /update to upgrade",
878
+ zh: "\u65B0\u7248\u672C nolo {0} \u53EF\u7528\uFF08\u5F53\u524D {1}\uFF09\u2014 \u8FD0\u884C /update \u5347\u7EA7"
879
+ },
852
880
  // --- Dialog list / timestamps -----------------------------------------
853
881
  recentDialogs: { en: "Recent dialogs:", zh: "\u6700\u8FD1\u5BF9\u8BDD\uFF1A" },
854
882
  dialogListTip: {
@@ -915,6 +943,11 @@ var TOOL_LABELS = {
915
943
  captureVisualState: { en: "Capture", zh: "\u622A\u5C4F" },
916
944
  // Workspace / diagnostics
917
945
  searchWorkspace: { en: "Search workspace", zh: "\u641C\u7D22\u5DE5\u4F5C\u533A" },
946
+ // 同一工具在 web/server 工具面用 snake_case 命名(packages/ai/tools/index.ts)。
947
+ search_workspace: { en: "Search workspace", zh: "\u641C\u7D22\u5DE5\u4F5C\u533A" },
948
+ // 全空间搜索:内置 skill search-all-spaces 声明的工作区工具,loadSkill 后进入
949
+ // 工具面;同样需要中英文标签,避免回退成裸工具名。
950
+ search_all_spaces: { en: "Search all spaces", zh: "\u641C\u7D22\u5168\u90E8\u7A7A\u95F4" },
918
951
  cliDoctor: { en: "Doctor", zh: "\u81EA\u68C0" },
919
952
  cliWhoami: { en: "Whoami", zh: "\u67E5\u770B\u8EAB\u4EFD" },
920
953
  checkEnv: { en: "Check env", zh: "\u68C0\u67E5\u73AF\u5883" },
@@ -960,185 +993,6 @@ function agentRunCardLabels() {
960
993
  };
961
994
  }
962
995
 
963
- // packages/ai/tools/agent/quotaCircuitBreaker.ts
964
- var QUOTA_ERROR_PATTERNS = [
965
- /429/,
966
- /quota/i,
967
- /rate\s*limit/i,
968
- /too\s*many\s*requests/i,
969
- /额度/,
970
- /上限/,
971
- /用尽/,
972
- /CliProviderQuotaError/i
973
- ];
974
- var AGENT_NOT_FOUND_PATTERNS = [
975
- /agent[^\n]{0,80}not\s+found/i,
976
- /agent[^\n]{0,80}not\s+exist/i,
977
- /agent[^\n]{0,80}不存在/i,
978
- /agent[^\n]{0,80}未找到/i,
979
- /AGENT_NOT_FOUND/
980
- ];
981
- var AUTH_ERROR_PATTERNS = [
982
- /401/,
983
- /403/,
984
- /unauthori[sz]ed/i,
985
- /authentication\s+failed/i,
986
- /invalid\s+(api[-\s]?key|token|credential)/i,
987
- /api[-\s]?key\s+(invalid|missing|not\s+found)/i,
988
- /token\s*(invalid|expired|失效|过期)/i,
989
- /未授权/,
990
- /认证失败/,
991
- /凭证/
992
- ];
993
- var NETWORK_ERROR_PATTERNS = [
994
- /ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENOTFOUND|EHOSTUNREACH|EPIPE/i,
995
- /getaddrinfo|DNS\s+(error|fail)/i,
996
- /fetch\s+failed/i,
997
- /network\s+(error|unreachable|down|disconnected)/i,
998
- /socket\s+(hang|closed|error)/i,
999
- /连接失败/,
1000
- /网络错误|网络异常|无法连接/
1001
- ];
1002
- function findActiveBreaker(now, table, target) {
1003
- if (!Number.isFinite(now)) return void 0;
1004
- for (const entry of table) {
1005
- if (entry.target !== target) continue;
1006
- if (typeof entry.until === "number" && entry.until > now) return entry;
1007
- }
1008
- return void 0;
1009
- }
1010
- function isCliProviderQuotaErrorShape(error) {
1011
- if (!error || typeof error !== "object") return false;
1012
- const e = error;
1013
- return e.name === "CliProviderQuotaError" || typeof e.message === "string" && e.message.startsWith("[QUOTA_LIMITED:");
1014
- }
1015
- function isQuotaExhaustedError(error) {
1016
- if (!error) return false;
1017
- if (isCliProviderQuotaErrorShape(error)) return true;
1018
- if (error instanceof Error) {
1019
- return QUOTA_ERROR_PATTERNS.some((pattern) => pattern.test(error.message));
1020
- }
1021
- if (typeof error === "string") {
1022
- return QUOTA_ERROR_PATTERNS.some((pattern) => pattern.test(error));
1023
- }
1024
- const maybeStatus = error.status;
1025
- if (typeof maybeStatus === "number" && maybeStatus === 429) return true;
1026
- const maybeStatusCode = error.statusCode;
1027
- if (typeof maybeStatusCode === "number" && maybeStatusCode === 429) return true;
1028
- return false;
1029
- }
1030
- function isAgentNotFoundError(error) {
1031
- if (!error) return false;
1032
- const e = error;
1033
- if (e.code === "AGENT_NOT_FOUND") return true;
1034
- const message = typeof e.message === "string" ? e.message : typeof error === "string" ? error : "";
1035
- if (AGENT_NOT_FOUND_PATTERNS.some((pattern) => pattern.test(message))) return true;
1036
- if ((e.status === 404 || e.statusCode === 404) && /agent|不存在|未找到/i.test(message)) {
1037
- return true;
1038
- }
1039
- return false;
1040
- }
1041
- function extractMessage(error) {
1042
- if (typeof error === "string") return error;
1043
- if (error instanceof Error) return error.message;
1044
- if (error && typeof error === "object") {
1045
- const m = error.message;
1046
- return typeof m === "string" ? m : "";
1047
- }
1048
- return "";
1049
- }
1050
- function extractStatus(error) {
1051
- if (!error || typeof error !== "object") return void 0;
1052
- const e = error;
1053
- const s = typeof e.status === "number" ? e.status : void 0;
1054
- const sc = typeof e.statusCode === "number" ? e.statusCode : void 0;
1055
- return s ?? sc;
1056
- }
1057
- function parseResetsInMs(text) {
1058
- if (!text) return void 0;
1059
- const unitMs = (n, unit) => {
1060
- if (/^d/.test(unit)) return n * 24 * 3600 * 1e3;
1061
- if (/^h/.test(unit)) return n * 3600 * 1e3;
1062
- if (/^m/.test(unit)) return n * 60 * 1e3;
1063
- if (/^s/.test(unit)) return n * 1e3;
1064
- return NaN;
1065
- };
1066
- const re = /(\d+)\s*(d(?:ay|ays)?|h(?:r|rs|our|ours)?|m(?:in|ins|inute|inutes)?|s(?:ec|ecs|econd|econds)?)/gi;
1067
- let totalMs = 0;
1068
- let matched = false;
1069
- let m;
1070
- while ((m = re.exec(text)) !== null) {
1071
- const ms = unitMs(Number(m[1]), m[2]);
1072
- if (!Number.isFinite(ms)) continue;
1073
- totalMs += ms;
1074
- matched = true;
1075
- }
1076
- return matched ? totalMs : void 0;
1077
- }
1078
- function extractProvider(error) {
1079
- if (!error || typeof error !== "object") return void 0;
1080
- const p = error.provider;
1081
- if (typeof p === "string" && p.length > 0) return p;
1082
- const message = extractMessage(error);
1083
- const tag = /\[QUOTA_LIMITED:([^\]]+)\]/i.exec(message);
1084
- if (tag?.[1]) return tag[1];
1085
- return void 0;
1086
- }
1087
- function extractRetryAfterHeader(error) {
1088
- if (!error || typeof error !== "object") return void 0;
1089
- const headers = error.headers;
1090
- if (!headers || typeof headers !== "object") return void 0;
1091
- const raw = headers["retry-after"];
1092
- if (typeof raw !== "string" && typeof raw !== "number") return void 0;
1093
- const secs = Number(raw);
1094
- return Number.isFinite(secs) && secs > 0 ? secs * 1e3 : void 0;
1095
- }
1096
- function classifyRunFailure(error) {
1097
- const message = extractMessage(error);
1098
- const status = extractStatus(error);
1099
- const retryAfterMs = extractRetryAfterHeader(error) ?? parseResetsInMs(message);
1100
- if (isQuotaExhaustedError(error)) {
1101
- return {
1102
- reason: "quota",
1103
- provider: extractProvider(error),
1104
- // 纯函数不取时钟:resetsAt 由调用方用 now + retryAfterMs 计算后补齐
1105
- retryAfterMs,
1106
- message
1107
- };
1108
- }
1109
- if (isAgentNotFoundError(error)) {
1110
- return { reason: "agent-not-found", message };
1111
- }
1112
- if (isAuthError(error)) {
1113
- return { reason: "auth", message };
1114
- }
1115
- if (isNetworkError(error)) {
1116
- return { reason: "network", message };
1117
- }
1118
- return { reason: "other", message };
1119
- }
1120
- function isAuthError(error) {
1121
- const message = extractMessage(error);
1122
- const status = extractStatus(error);
1123
- if (status === 401 || status === 403) return true;
1124
- return AUTH_ERROR_PATTERNS.some((pattern) => pattern.test(message));
1125
- }
1126
- function isNetworkError(error) {
1127
- const message = extractMessage(error);
1128
- return NETWORK_ERROR_PATTERNS.some((pattern) => pattern.test(message));
1129
- }
1130
- var DEFAULT_QUOTA_BREAKER_MS = 6 * 3600 * 1e3;
1131
- var DEFAULT_AGENT_NOT_FOUND_BREAKER_MS = 15 * 60 * 1e3;
1132
- function buildBreakerEntry(now, target, kind, retryAfterMs) {
1133
- const ttl = kind === "quota" ? typeof retryAfterMs === "number" && retryAfterMs > 0 ? retryAfterMs : DEFAULT_QUOTA_BREAKER_MS : DEFAULT_AGENT_NOT_FOUND_BREAKER_MS;
1134
- return {
1135
- target,
1136
- kind,
1137
- until: now + ttl,
1138
- ...kind === "quota" && typeof retryAfterMs === "number" ? { resetsAt: now + retryAfterMs } : {}
1139
- };
1140
- }
1141
-
1142
996
  // packages/cli/client/localRuntimeDiagnostics.ts
1143
997
  import { ulid } from "ulid";
1144
998
  var preparedAgentRuntimeCache = /* @__PURE__ */ new Map();
@@ -1177,6 +1031,36 @@ function summarizeOpenAiToolNames(tools) {
1177
1031
  }, []);
1178
1032
  }
1179
1033
 
1034
+ // packages/agent-runtime/agentRunIsolation.ts
1035
+ var ORCHESTRATION_TOOL_NAMES = /* @__PURE__ */ new Set([
1036
+ "startAgentRun",
1037
+ "controlAgentRun",
1038
+ "listAgents",
1039
+ "readAgent",
1040
+ "startAgentDialog",
1041
+ "runStreamingAgent",
1042
+ "streamParallelAgents"
1043
+ ]);
1044
+ var SUBTASK_REMOVED_GIT_TOOL_NAMES = /* @__PURE__ */ new Set([
1045
+ "gitAdd",
1046
+ "gitCommit",
1047
+ "gitCreateBranch",
1048
+ "commitWorkspace"
1049
+ ]);
1050
+ var SUBTASK_REMOVED_TOOL_NAMES = /* @__PURE__ */ new Set([
1051
+ ...ORCHESTRATION_TOOL_NAMES,
1052
+ ...SUBTASK_REMOVED_GIT_TOOL_NAMES
1053
+ ]);
1054
+ function isSubtaskRun(env) {
1055
+ if (!env) return false;
1056
+ const value = env.NOLO_AGENT_RUN_CHILD;
1057
+ return typeof value === "string" && value.length > 0 && value !== "0";
1058
+ }
1059
+ function filterToolNamesForRunKind(toolNames, isSubtask) {
1060
+ if (!isSubtask) return [...toolNames];
1061
+ return toolNames.filter((name) => !SUBTASK_REMOVED_TOOL_NAMES.has(name));
1062
+ }
1063
+
1180
1064
  // packages/cli/client/localRuntimeAdapter.ts
1181
1065
  import { dirname as dirname2, extname, join as join2 } from "node:path";
1182
1066
  import { fileURLToPath } from "node:url";
@@ -1662,93 +1546,6 @@ function assertWithinLocalToolBudget(args) {
1662
1546
  );
1663
1547
  }
1664
1548
 
1665
- // packages/agent-runtime/builtinPlatformAgentConfigs.ts
1666
- var BUILTIN_PLATFORM_AGENT_CONFIGS = {
1667
- [PUBLIC_DEEPSEEK_V4_FLASH_AGENT_KEY]: {
1668
- key: PUBLIC_DEEPSEEK_V4_FLASH_AGENT_KEY,
1669
- name: "DeepSeek V4 Flash",
1670
- provider: "nolo",
1671
- model: "deepseek-v4-flash",
1672
- apiSource: "platform",
1673
- useServerProxy: true,
1674
- rawRecord: {
1675
- dbKey: PUBLIC_DEEPSEEK_V4_FLASH_AGENT_KEY,
1676
- isPublic: true,
1677
- provider: "nolo",
1678
- model: "deepseek-v4-flash",
1679
- apiSource: "platform",
1680
- useServerProxy: true
1681
- }
1682
- },
1683
- [PUBLIC_DEEPSEEK_V4_PRO_AGENT_KEY]: {
1684
- key: PUBLIC_DEEPSEEK_V4_PRO_AGENT_KEY,
1685
- name: "DeepSeek V4 Pro",
1686
- provider: "nolo",
1687
- model: "deepseek-v4-pro",
1688
- apiSource: "platform",
1689
- useServerProxy: true,
1690
- rawRecord: {
1691
- dbKey: PUBLIC_DEEPSEEK_V4_PRO_AGENT_KEY,
1692
- isPublic: true,
1693
- provider: "nolo",
1694
- model: "deepseek-v4-pro",
1695
- apiSource: "platform",
1696
- useServerProxy: true
1697
- }
1698
- },
1699
- [PUBLIC_GLM_52_AGENT_KEY]: {
1700
- key: PUBLIC_GLM_52_AGENT_KEY,
1701
- name: "GLM 5.2",
1702
- provider: "nolo",
1703
- model: "glm-5.2",
1704
- apiSource: "platform",
1705
- useServerProxy: true,
1706
- rawRecord: {
1707
- dbKey: PUBLIC_GLM_52_AGENT_KEY,
1708
- isPublic: true,
1709
- provider: "nolo",
1710
- model: "glm-5.2",
1711
- apiSource: "platform",
1712
- useServerProxy: true
1713
- }
1714
- },
1715
- [PUBLIC_KIMI_K26_IMAGE_AGENT_KEY]: {
1716
- key: PUBLIC_KIMI_K26_IMAGE_AGENT_KEY,
1717
- name: "Kimi K2.6",
1718
- provider: "nolo",
1719
- model: "kimi-k2.6",
1720
- apiSource: "platform",
1721
- useServerProxy: true,
1722
- rawRecord: {
1723
- dbKey: PUBLIC_KIMI_K26_IMAGE_AGENT_KEY,
1724
- isPublic: true,
1725
- provider: "nolo",
1726
- model: "kimi-k2.6",
1727
- apiSource: "platform",
1728
- useServerProxy: true
1729
- }
1730
- },
1731
- [BUILTIN_NOLO_AGENT_KEY]: {
1732
- key: BUILTIN_NOLO_AGENT_KEY,
1733
- name: "Nolo",
1734
- provider: "nolo",
1735
- model: "deepseek-v4-flash",
1736
- apiSource: "platform",
1737
- useServerProxy: true,
1738
- rawRecord: {
1739
- dbKey: BUILTIN_NOLO_AGENT_KEY,
1740
- isPublic: true,
1741
- provider: "nolo",
1742
- model: "deepseek-v4-flash",
1743
- apiSource: "platform",
1744
- useServerProxy: true
1745
- }
1746
- }
1747
- };
1748
- function resolveBuiltinPlatformAgentConfig(agentRef) {
1749
- return BUILTIN_PLATFORM_AGENT_CONFIGS[agentRef] ?? null;
1750
- }
1751
-
1752
1549
  // packages/cli/client/localDialogRecords.ts
1753
1550
  var localDialogMessageRecordToRuntimeMessage = dialogMessageRecordToAgentRuntimeMessage;
1754
1551
  function buildLocalDialogWritePlan(args) {
@@ -1794,9 +1591,7 @@ function resolveBuiltinLocalCliAgentConfig(agentRef, userId) {
1794
1591
  }
1795
1592
  async function readAgentFromStore(args) {
1796
1593
  for (const key of buildLocalAgentLookupKeys(args)) {
1797
- const record = await args.store.read(key, {
1798
- remote: shouldReadAgentKeyRemotely(key)
1799
- });
1594
+ const record = await args.store.read(key);
1800
1595
  if (!record || typeof record !== "object") continue;
1801
1596
  return resolveAgentRuntimeConfigFromRecord(key, record);
1802
1597
  }
@@ -1814,22 +1609,6 @@ async function readDialogMessages(args) {
1814
1609
  }
1815
1610
 
1816
1611
  // packages/cli/client/cliProviderHelpers.ts
1817
- function buildDelegatedTaskContent(task, input) {
1818
- if (input === void 0 || input === null) {
1819
- return task;
1820
- }
1821
- if (typeof input === "string") {
1822
- return `${task}
1823
-
1824
- --- INPUT (text) ---
1825
- ${input}`;
1826
- }
1827
- const jsonStr = JSON.stringify(input, null, 2);
1828
- return `${task}
1829
-
1830
- --- INPUT (json) ---
1831
- ${jsonStr}`;
1832
- }
1833
1612
  function isCliProviderAgent(agentConfig) {
1834
1613
  return Boolean(
1835
1614
  agentConfig.apiSource === "cli" || agentConfig.provider === "cli" || agentConfig.cliProvider
@@ -1890,7 +1669,7 @@ function collectCliProviderImageInputs(messages) {
1890
1669
  }
1891
1670
 
1892
1671
  // packages/cli/client/cliAgentConfigHelpers.ts
1893
- var BUILTIN_NOLO_AGENT_KEY2 = NOLO_DEFAULT_AGENT_KEY;
1672
+ var BUILTIN_NOLO_AGENT_KEY = NOLO_DEFAULT_AGENT_KEY;
1894
1673
  var BUILTIN_NOLO_AGENT_ID = NOLO_DEFAULT_AGENT_ID;
1895
1674
  function resolveLocalUserId(env) {
1896
1675
  const explicitUserId = env.NOLO_LOCAL_USER_ID || env.NOLO_USER_ID;
@@ -1901,7 +1680,7 @@ function resolveLocalUserId(env) {
1901
1680
  function isBuiltinNoloAgentRef(ref) {
1902
1681
  if (typeof ref !== "string") return false;
1903
1682
  const normalized = ref.trim();
1904
- return normalized === BUILTIN_NOLO_AGENT_KEY2 || normalized === BUILTIN_NOLO_AGENT_ID || normalized.endsWith(`-${BUILTIN_NOLO_AGENT_ID}`);
1683
+ return normalized === BUILTIN_NOLO_AGENT_KEY || normalized === BUILTIN_NOLO_AGENT_ID || normalized.endsWith(`-${BUILTIN_NOLO_AGENT_ID}`);
1905
1684
  }
1906
1685
  function isBuiltinNoloAgentConfig(agentConfig) {
1907
1686
  const key = agentConfig?.key || agentConfig?.rawRecord?.dbKey || agentConfig?.rawRecord?.agentKey;
@@ -1913,14 +1692,15 @@ function withResolvedRuntimeToolSurface(agentConfig, env) {
1913
1692
  const currentUserId = resolveLocalUserId(env);
1914
1693
  const rawRecord = agentConfig.rawRecord ?? {};
1915
1694
  const ownerId = asOptionalTrimmedString(rawRecord.userId) ?? null;
1695
+ const agentKey = rawRecord.dbKey ?? agentConfig.key;
1916
1696
  const toolSurface = resolveRuntimeToolSurfaceForAgent({
1917
1697
  explicitToolNames: agentConfig.toolNames,
1918
1698
  currentUserId,
1919
1699
  agentOwnerId: ownerId,
1920
- agentKey: rawRecord.dbKey ?? agentConfig.key,
1700
+ agentKey,
1921
1701
  isPublic: !isBuiltinNoloAgentConfig(agentConfig) && rawRecord.isPublic === true,
1922
1702
  sharingLevel: typeof rawRecord.sharingLevel === "string" ? rawRecord.sharingLevel : null,
1923
- trustedPrivateInvocation: isBuiltinNoloAgentConfig(agentConfig),
1703
+ trustedPrivateInvocation: isBuiltinNoloAgentConfig(agentConfig) || isSystemBuiltinTrustedAgentKey(agentKey),
1924
1704
  runtimeHost: "cli"
1925
1705
  });
1926
1706
  return {
@@ -2439,54 +2219,29 @@ function aggregateBatch(input) {
2439
2219
  };
2440
2220
  }
2441
2221
 
2442
- // packages/ai/tools/agent/runtimeTodo.ts
2443
- var TODO_REVIEW_BLOCK = "BLOCK";
2444
- function deriveTodoStatus(input) {
2222
+ // packages/ai/tools/agent/agentRunTodo.ts
2223
+ var SUCCESS_STATUSES = /* @__PURE__ */ new Set(["completed", "done", "success", "passed", "finished"]);
2224
+ var FAILED_STATUSES = /* @__PURE__ */ new Set(["failed", "error", "stopped", "cancelled", "orphaned", "blocked"]);
2225
+ function deriveAgentRunTodoStatus(input) {
2445
2226
  const { todo, runs } = input;
2446
- if (todo.status === "abandoned") {
2447
- return { status: "abandoned" };
2448
- }
2449
- if (runs.length === 0) {
2450
- return { status: todo.status };
2451
- }
2452
- if (runs.some((r) => !isAgentRunTerminalStatus(r.status))) {
2453
- return { status: "running", latestRun: latestRun(runs) };
2227
+ if (!runs || runs.length === 0) {
2228
+ return { status: todo.status ?? "pending" };
2454
2229
  }
2455
- const latest = latestRun(runs);
2456
- if (latest?.reviewVerdict === TODO_REVIEW_BLOCK) {
2457
- return {
2458
- status: "blocked",
2459
- latestRun: latest,
2460
- blockedReason: clipSummary(latest.reviewReason, MAX_ERROR_SUMMARY_CHARS)
2461
- };
2230
+ const latestRun = runs[runs.length - 1];
2231
+ const hasRunning = runs.some(
2232
+ (r) => r.status === "running" || r.status === "in_progress" || r.status === "executing"
2233
+ );
2234
+ if (hasRunning) {
2235
+ return { status: "running", latestRun };
2462
2236
  }
2463
- if (latest?.status === "done") {
2464
- return { status: "done", latestRun: latest };
2237
+ const runStatus = (latestRun.status || "").toLowerCase();
2238
+ if (SUCCESS_STATUSES.has(runStatus)) {
2239
+ return { status: "done", latestRun };
2465
2240
  }
2466
- return { status: "pending", latestRun: latest };
2467
- }
2468
- function toEpochMs2(iso) {
2469
- if (!iso) return void 0;
2470
- const ms = Date.parse(iso);
2471
- return Number.isFinite(ms) ? ms : void 0;
2472
- }
2473
- function latestRun(runs) {
2474
- let latest;
2475
- let latestScore = -1;
2476
- for (const run of runs) {
2477
- const score = toEpochMs2(run.finishedAt) ?? toEpochMs2(run.startedAt) ?? -1;
2478
- if (latest === void 0 || score >= latestScore) {
2479
- latest = run;
2480
- latestScore = score;
2481
- }
2241
+ if (FAILED_STATUSES.has(runStatus)) {
2242
+ return { status: "failed", latestRun };
2482
2243
  }
2483
- return latest;
2484
- }
2485
- function clipSummary(text, max) {
2486
- if (!text) return void 0;
2487
- const folded = text.replace(/\s+/g, " ").trim();
2488
- if (folded.length === 0) return void 0;
2489
- return folded.length > max ? `${folded.slice(0, max - 1)}\u2026` : folded;
2244
+ return { status: todo.status ?? "pending", latestRun };
2490
2245
  }
2491
2246
 
2492
2247
  // packages/cli/client/fileSystemStores.ts
@@ -2519,50 +2274,41 @@ function readJson(path, fs) {
2519
2274
  function writeJson(path, data, fs) {
2520
2275
  fs.writeFileSync(path, JSON.stringify(data, null, 2));
2521
2276
  }
2522
- function createFileSystemCircuitBreakerStore(opts = {}) {
2277
+ function createFileSystemTodoStore(opts = {}) {
2523
2278
  const env = opts.env ?? process.env;
2524
2279
  const homedir = opts.homedir ?? nodeHomedir;
2525
2280
  const fs = opts.fs ?? nodeFs;
2526
- const filePath = opts.filePath ?? join(resolveNoloHome(env, homedir), "breakers.json");
2281
+ const noloHome = resolveNoloHome(env, homedir);
2282
+ const filePath = opts.filePath ?? join(noloHome, "agent_run_todos.json");
2283
+ const oldFilePath = join(noloHome, "todos.json");
2527
2284
  const loadMap = () => {
2528
- const data = readJson(filePath, fs);
2529
- const map = /* @__PURE__ */ new Map();
2530
- if (data?.entries) {
2531
- for (const entry of data.entries) {
2532
- if (entry && typeof entry.target === "string") map.set(entry.target, entry);
2285
+ if (!opts.filePath && !fs.existsSync(filePath) && fs.existsSync(oldFilePath)) {
2286
+ const oldData = readJson(oldFilePath, fs);
2287
+ const map2 = /* @__PURE__ */ new Map();
2288
+ if (oldData?.todos) {
2289
+ for (const item of oldData.todos) {
2290
+ if (item && typeof item.id === "string") {
2291
+ let status = "pending";
2292
+ if (item.status === "running") status = "running";
2293
+ else if (item.status === "done") status = "done";
2294
+ else if (item.status === "blocked" || item.status === "abandoned" || item.status === "failed") status = "failed";
2295
+ const rec = {
2296
+ ...item,
2297
+ id: item.id,
2298
+ title: item.title || "Untitled Task",
2299
+ status,
2300
+ runIds: Array.isArray(item.runIds) ? item.runIds : [],
2301
+ createdAt: item.createdAt || (/* @__PURE__ */ new Date()).toISOString(),
2302
+ updatedAt: item.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
2303
+ };
2304
+ map2.set(rec.id, rec);
2305
+ }
2306
+ }
2533
2307
  }
2308
+ ensureDir(dirname(filePath), fs);
2309
+ writeJson(filePath, { todos: [...map2.values()] }, fs);
2310
+ return map2;
2534
2311
  }
2535
- return map;
2536
- };
2537
- const persist = (map) => {
2538
- ensureDir(dirname(filePath), fs);
2539
- writeJson(filePath, { entries: [...map.values()] }, fs);
2540
- };
2541
- return {
2542
- get(target) {
2543
- return loadMap().get(target);
2544
- },
2545
- set(entry) {
2546
- const map = loadMap();
2547
- map.set(entry.target, entry);
2548
- persist(map);
2549
- },
2550
- clear(target) {
2551
- const map = loadMap();
2552
- map.delete(target);
2553
- persist(map);
2554
- },
2555
- clearAll() {
2556
- persist(/* @__PURE__ */ new Map());
2557
- }
2558
- };
2559
- }
2560
- function createFileSystemTodoStore(opts = {}) {
2561
- const env = opts.env ?? process.env;
2562
- const homedir = opts.homedir ?? nodeHomedir;
2563
- const fs = opts.fs ?? nodeFs;
2564
- const filePath = opts.filePath ?? join(resolveNoloHome(env, homedir), "todos.json");
2565
- const loadMap = () => {
2566
2312
  const data = readJson(filePath, fs);
2567
2313
  const map = /* @__PURE__ */ new Map();
2568
2314
  if (data?.todos) {
@@ -2578,13 +2324,15 @@ function createFileSystemTodoStore(opts = {}) {
2578
2324
  };
2579
2325
  return {
2580
2326
  async getTodo(id) {
2581
- return loadMap().get(id);
2327
+ return loadMap().get(id) ?? null;
2582
2328
  },
2583
- async listTodos(filter) {
2584
- const all = [...loadMap().values()];
2585
- if (!filter?.statuses || filter.statuses.length === 0) return all;
2586
- const wanted = new Set(filter.statuses);
2587
- return all.filter((t2) => wanted.has(t2.status));
2329
+ async listTodos() {
2330
+ return [...loadMap().values()];
2331
+ },
2332
+ async saveTodo(todo) {
2333
+ const map = loadMap();
2334
+ map.set(todo.id, todo);
2335
+ persist(map);
2588
2336
  },
2589
2337
  async putTodo(todo) {
2590
2338
  const map = loadMap();
@@ -2593,8 +2341,10 @@ function createFileSystemTodoStore(opts = {}) {
2593
2341
  },
2594
2342
  async deleteTodo(id) {
2595
2343
  const map = loadMap();
2344
+ const existed = map.has(id);
2596
2345
  map.delete(id);
2597
2346
  persist(map);
2347
+ return existed;
2598
2348
  }
2599
2349
  };
2600
2350
  }
@@ -2621,10 +2371,8 @@ var tailFile = (logPath, tailLines, fs) => {
2621
2371
  return void 0;
2622
2372
  }
2623
2373
  };
2624
- var resolveBreakerStore = (deps) => deps.breakerStore ?? createFileSystemCircuitBreakerStore({ env: deps.env, homedir: deps.homedir, fs: deps.fs });
2625
2374
  var resolveTodoStore = (deps) => deps.todoStore ?? createFileSystemTodoStore({ env: deps.env, homedir: deps.homedir, fs: deps.fs });
2626
2375
  var resolveNowMs = (deps) => typeof deps.nowMs === "function" ? deps.nowMs() : Date.now();
2627
- var resolveTarget = (deps, agentKey) => typeof deps.resolveProviderTarget === "function" ? deps.resolveProviderTarget(agentKey) : agentKey;
2628
2376
  function buildProgressField(record, nowMs) {
2629
2377
  const activity = record.activity;
2630
2378
  if (!activity || isAgentRunTerminalStatus(record.status)) return {};
@@ -2642,6 +2390,55 @@ function buildProgressField(record, nowMs) {
2642
2390
  }
2643
2391
  return { progress };
2644
2392
  }
2393
+ var WAIT_POLL_INTERVAL_MS = 500;
2394
+ var DEFAULT_WAIT_TIMEOUT_MS = 1e5;
2395
+ function buildRunStatusPayload(reconciled, deps, opts = {}) {
2396
+ const logLines = opts.logTail ? opts.logTail.split("\n") : void 0;
2397
+ const name = resolveRunLabel(reconciled);
2398
+ const labels = agentRunCardLabels();
2399
+ return {
2400
+ content: JSON.stringify({
2401
+ runId: reconciled.runId,
2402
+ found: true,
2403
+ status: reconciled.status,
2404
+ pid: reconciled.pid ?? null,
2405
+ agentKey: reconciled.agentKey,
2406
+ ...reconciled.agentName ? { agentName: reconciled.agentName } : {},
2407
+ startedAt: reconciled.startedAt,
2408
+ endedAt: reconciled.endedAt ?? null,
2409
+ exitCode: reconciled.exitCode ?? null,
2410
+ // Expose dialogId so the caller can read the agent's actual output
2411
+ // via `nolo dialog read <dialogId>`. The dialog is the authoritative
2412
+ // result store; the run log is the child process stdout/stderr,
2413
+ // whose contents vary by provider (some stream tokens to stdout,
2414
+ // some only emit startup + stderr). Because status does not carry
2415
+ // logTail by default, a "done" run with an empty logTail is
2416
+ // indistinguishable from a hung run without this field. Surfacing
2417
+ // dialogId gives the caller a reliable way to fetch the result.
2418
+ ...reconciled.dialogId ? { dialogId: reconciled.dialogId } : {},
2419
+ // Progress is what makes tailLines:0 an actual answer to "is it
2420
+ // stuck?". Without it a poll returns `running` and a pid, so the only
2421
+ // way to tell a working run from a wedged one was to pull 30 lines of
2422
+ // log — the exact thing the orchestration prompt tells the model not
2423
+ // to do. The registry has been recording this all along and `nolo
2424
+ // agent status` has been printing it; only the tool was blind.
2425
+ ...buildProgressField(reconciled, resolveNowMs(deps)),
2426
+ ...opts.logTail !== void 0 ? { logTail: opts.logTail } : {},
2427
+ ...logLines ? { logLines } : {}
2428
+ }),
2429
+ metadata: {
2430
+ displayData: formatStatusRunCard(name, reconciled.status, {
2431
+ runId: reconciled.runId,
2432
+ timing: {
2433
+ startedAt: readTimestamp(reconciled.startedAt),
2434
+ finishedAt: readTimestamp(reconciled.endedAt)
2435
+ },
2436
+ logLines,
2437
+ labels
2438
+ })
2439
+ }
2440
+ };
2441
+ }
2645
2442
  function createCliStartAgentRunExecutor(deps = {}) {
2646
2443
  return async (call) => {
2647
2444
  const args = parseCallArgs(call);
@@ -2649,29 +2446,7 @@ function createCliStartAgentRunExecutor(deps = {}) {
2649
2446
  const task = typeof args.task === "string" ? args.task.trim() : "";
2650
2447
  if (!agentKey) throw new Error("startAgentRun: \u7F3A\u5C11 agentKey \u53C2\u6570\u3002");
2651
2448
  if (!task) throw new Error("startAgentRun: \u7F3A\u5C11\u6709\u6548\u7684 task \u6587\u672C\u63CF\u8FF0\u3002");
2652
- const breakerStore = resolveBreakerStore(deps);
2653
- const target = resolveTarget(deps, agentKey);
2654
2449
  const nowMs = resolveNowMs(deps);
2655
- const activeBreaker = findActiveBreaker(
2656
- nowMs,
2657
- [breakerStore.get(target)].filter(Boolean),
2658
- target
2659
- );
2660
- if (activeBreaker) {
2661
- const resetsAt = activeBreaker.resetsAt;
2662
- return {
2663
- content: JSON.stringify({
2664
- rejected: true,
2665
- reason: "quota",
2666
- provider: target,
2667
- ...resetsAt !== void 0 ? { resetsAt } : {},
2668
- message: `provider ${target} \u5904\u4E8E\u7194\u65AD\u671F\uFF08\u914D\u989D\u8017\u5C3D\uFF09\uFF0C\u5EFA\u8BAE\u6539\u6D3E\u5176\u5B83 provider\u3002`
2669
- }),
2670
- metadata: {
2671
- displayData: `Run rejected \xB7 quota breaker active on ${target}`
2672
- }
2673
- };
2674
- }
2675
2450
  const message = typeof args.input === "undefined" ? task : `${task}
2676
2451
 
2677
2452
  --- \u9644\u52A0\u8F93\u5165 ---
@@ -2719,16 +2494,15 @@ ${JSON.stringify(args.input)}`;
2719
2494
  id: todoId,
2720
2495
  title: taskPreview || task.slice(0, 100),
2721
2496
  status: existing?.status ?? "pending",
2722
- ...existing?.specPath ? { specPath: existing.specPath } : {},
2723
- ...existing?.worktree ? { worktree: existing.worktree } : {},
2724
- ...existing?.branch ? { branch: existing.branch } : {},
2725
- ...deps.cwd ? { worktree: deps.cwd } : {},
2726
2497
  runIds,
2727
- deps: existing?.deps ?? [],
2728
2498
  createdAt: existing?.createdAt ?? nowIso,
2729
2499
  updatedAt: nowIso
2730
2500
  };
2731
- await todoStore.putTodo(todo);
2501
+ if (typeof todoStore.saveTodo === "function") {
2502
+ await todoStore.saveTodo(todo);
2503
+ } else {
2504
+ await todoStore.putTodo(todo);
2505
+ }
2732
2506
  } catch {
2733
2507
  }
2734
2508
  }
@@ -2808,12 +2582,11 @@ function createCliControlAgentRunExecutor(deps = {}) {
2808
2582
  }
2809
2583
  };
2810
2584
  }
2811
- if (action === "todo") {
2585
+ if (action === "todo" || action === "agentRunTodo" || action === "agent_run_todo") {
2812
2586
  const todoStore = resolveTodoStore(deps);
2813
- const statusesRaw = typeof args.status === "string" ? args.status.trim() : void 0;
2814
- const filter = statusesRaw && statusesRaw !== "all" ? { statuses: statusesRaw.split(",").map((s) => s.trim()).filter(Boolean) } : void 0;
2815
- const todos = await todoStore.listTodos(filter);
2816
- const enriched = await Promise.all(
2587
+ const todos = await todoStore.listTodos();
2588
+ const statusFilter = typeof args.status === "string" ? args.status.trim().toLowerCase() : void 0;
2589
+ let enriched = await Promise.all(
2817
2590
  todos.map(async (todo) => {
2818
2591
  const runSummaries = [];
2819
2592
  for (const rid of todo.runIds) {
@@ -2822,26 +2595,29 @@ function createCliControlAgentRunExecutor(deps = {}) {
2822
2595
  runSummaries.push({
2823
2596
  runId: rec.runId,
2824
2597
  status: rec.status,
2825
- startedAt: rec.startedAt,
2598
+ startedAt: readTimestamp(rec.startedAt)?.toString(),
2599
+ finishedAt: readTimestamp(rec.endedAt)?.toString(),
2826
2600
  ...rec.agentName ? { agentName: rec.agentName } : {}
2827
2601
  });
2828
2602
  }
2829
- const derived = deriveTodoStatus({
2603
+ const derived = deriveAgentRunTodoStatus({
2830
2604
  todo: { id: todo.id, status: todo.status },
2831
- runs: runSummaries,
2832
- now: new Date(resolveNowMs(deps)).toISOString()
2605
+ runs: runSummaries
2833
2606
  });
2834
2607
  return {
2835
2608
  id: todo.id,
2836
2609
  title: todo.title,
2837
2610
  status: derived.status,
2838
2611
  runIds: todo.runIds,
2839
- ...derived.blockedReason ? { blockedReason: derived.blockedReason } : {},
2840
2612
  ...derived.latestRun ? { latestRunId: derived.latestRun.runId, latestRunStatus: derived.latestRun.status } : {},
2841
2613
  updatedAt: todo.updatedAt
2842
2614
  };
2843
2615
  })
2844
2616
  );
2617
+ if (statusFilter && statusFilter !== "all") {
2618
+ const wanted = new Set(statusFilter.split(",").map((s) => s.trim()).filter(Boolean));
2619
+ enriched = enriched.filter((t2) => wanted.has(t2.status.toLowerCase()));
2620
+ }
2845
2621
  return {
2846
2622
  content: JSON.stringify({ todos: enriched, count: enriched.length }),
2847
2623
  metadata: {
@@ -2855,7 +2631,7 @@ function createCliControlAgentRunExecutor(deps = {}) {
2855
2631
  }
2856
2632
  };
2857
2633
  }
2858
- if (action !== "status" && action !== "stop") {
2634
+ if (action !== "status" && action !== "stop" && action !== "wait") {
2859
2635
  throw new Error(`controlAgentRun: \u672A\u77E5 action "${action}"\u3002`);
2860
2636
  }
2861
2637
  if (!args.runId) throw new Error(`controlAgentRun(action:"${action}"): \u7F3A\u5C11 runId\u3002`);
@@ -2870,70 +2646,43 @@ function createCliControlAgentRunExecutor(deps = {}) {
2870
2646
  if (action === "status") {
2871
2647
  const reconciled = checkStaleRun(record.runId, deps) ?? record;
2872
2648
  const logTail = tailLines > 0 ? tailFile(record.logPath, tailLines, resolveFs(deps)) : void 0;
2873
- const name = resolveRunLabel(reconciled);
2874
- const logLines = logTail ? logTail.split("\n") : void 0;
2875
- const labels2 = agentRunCardLabels();
2876
- if (reconciled.status === "failed" || reconciled.status === "orphaned") {
2877
- try {
2878
- const failureSource = logTail ?? reconciled.note ?? "";
2879
- const failureInfo = classifyRunFailure(
2880
- failureSource || reconciled.status
2881
- );
2882
- if (failureInfo.reason === "quota") {
2883
- const t2 = resolveTarget(deps, reconciled.agentKey);
2884
- const entry = buildBreakerEntry(
2885
- resolveNowMs(deps),
2886
- t2,
2887
- "quota",
2888
- failureInfo.retryAfterMs
2889
- );
2890
- resolveBreakerStore(deps).set(entry);
2891
- }
2892
- } catch {
2649
+ return buildRunStatusPayload(reconciled, deps, { logTail });
2650
+ }
2651
+ if (action === "wait") {
2652
+ const timeoutMs = typeof args.timeoutMs === "number" && args.timeoutMs > 0 ? args.timeoutMs : DEFAULT_WAIT_TIMEOUT_MS;
2653
+ const startMs = resolveNowMs(deps);
2654
+ const deadline = startMs + timeoutMs;
2655
+ const sleep = deps.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
2656
+ let reconciled = record;
2657
+ for (; ; ) {
2658
+ reconciled = checkStaleRun(record.runId, deps) ?? record;
2659
+ if (isAgentRunTerminalStatus(reconciled.status)) break;
2660
+ const nowMs = resolveNowMs(deps);
2661
+ const waitedMs = nowMs - startMs;
2662
+ if (nowMs >= deadline) {
2663
+ return {
2664
+ content: JSON.stringify({
2665
+ runId: reconciled.runId,
2666
+ found: true,
2667
+ status: "timeout",
2668
+ runStatus: reconciled.status,
2669
+ pid: reconciled.pid ?? null,
2670
+ agentKey: reconciled.agentKey,
2671
+ ...reconciled.agentName ? { agentName: reconciled.agentName } : {},
2672
+ startedAt: reconciled.startedAt,
2673
+ waitedMs,
2674
+ timeoutMs,
2675
+ ...buildProgressField(reconciled, nowMs)
2676
+ }),
2677
+ metadata: {
2678
+ displayData: `\u23F3 wait \u8D85\u65F6\uFF08${Math.round(waitedMs / 1e3)}s\uFF09\uFF0Crun \u4ECD\u5728\u8FD0\u884C\uFF1A\u53EF\u7A0D\u540E\u518D wait\uFF0C\u6216\u6539\u7528 status/stop`
2679
+ }
2680
+ };
2893
2681
  }
2682
+ await sleep(WAIT_POLL_INTERVAL_MS);
2894
2683
  }
2895
- return {
2896
- content: JSON.stringify({
2897
- runId: reconciled.runId,
2898
- found: true,
2899
- status: reconciled.status,
2900
- pid: reconciled.pid ?? null,
2901
- agentKey: reconciled.agentKey,
2902
- ...reconciled.agentName ? { agentName: reconciled.agentName } : {},
2903
- startedAt: reconciled.startedAt,
2904
- endedAt: reconciled.endedAt ?? null,
2905
- exitCode: reconciled.exitCode ?? null,
2906
- // Expose dialogId so the caller can read the agent's actual output
2907
- // via `nolo dialog read <dialogId>`. The dialog is the authoritative
2908
- // result store; the run log is the child process stdout/stderr,
2909
- // whose contents vary by provider (some stream tokens to stdout,
2910
- // some only emit startup + stderr). Because status does not carry
2911
- // logTail by default, a "done" run with an empty logTail is
2912
- // indistinguishable from a hung run without this field. Surfacing
2913
- // dialogId gives the caller a reliable way to fetch the result.
2914
- ...reconciled.dialogId ? { dialogId: reconciled.dialogId } : {},
2915
- // Progress is what makes tailLines:0 an actual answer to "is it
2916
- // stuck?". Without it a poll returns `running` and a pid, so the only
2917
- // way to tell a working run from a wedged one was to pull 30 lines of
2918
- // log — the exact thing the orchestration prompt tells the model not
2919
- // to do. The registry has been recording this all along and `nolo
2920
- // agent status` has been printing it; only the tool was blind.
2921
- ...buildProgressField(reconciled, resolveNowMs(deps)),
2922
- ...logTail !== void 0 ? { logTail } : {},
2923
- ...logLines ? { logLines } : {}
2924
- }),
2925
- metadata: {
2926
- displayData: formatStatusRunCard(name, reconciled.status, {
2927
- runId: reconciled.runId,
2928
- timing: {
2929
- startedAt: readTimestamp(reconciled.startedAt),
2930
- finishedAt: readTimestamp(reconciled.endedAt)
2931
- },
2932
- logLines,
2933
- labels: labels2
2934
- })
2935
- }
2936
- };
2684
+ ackRunRecord(reconciled.runId, deps);
2685
+ return buildRunStatusPayload(reconciled, deps);
2937
2686
  }
2938
2687
  if (typeof record.pid === "number") {
2939
2688
  try {
@@ -3168,6 +2917,20 @@ function buildLocalToolExecutors(args) {
3168
2917
  controlAgentRun: createCliControlAgentRunExecutor({
3169
2918
  env: args.env
3170
2919
  }),
2920
+ setTodoList: async (call) => {
2921
+ const parsedArgs = typeof call?.arguments === "string" ? (() => {
2922
+ try {
2923
+ return JSON.parse(call.arguments);
2924
+ } catch {
2925
+ return {};
2926
+ }
2927
+ })() : call?.arguments || {};
2928
+ const res = await setTodoListFunc(parsedArgs);
2929
+ return {
2930
+ content: JSON.stringify(res.rawData),
2931
+ metadata: { displayData: res.displayData }
2932
+ };
2933
+ },
3171
2934
  ...args.pastedTextStore ? { readPastedText: createReadPastedTextExecutor(args.pastedTextStore) } : {},
3172
2935
  ...args.localToolExecutors ?? {}
3173
2936
  };
@@ -3178,6 +2941,9 @@ import { randomUUID } from "node:crypto";
3178
2941
 
3179
2942
  // packages/agent-runtime/antigravityWireModel.ts
3180
2943
  var WIRE_PROFILES = {
2944
+ "gemini-3.7-flash-low": { maxOutputTokens: 65536 },
2945
+ "gemini-3.7-flash-medium": { maxOutputTokens: 65536 },
2946
+ "gemini-3.7-flash-high": { maxOutputTokens: 65536 },
3181
2947
  "gemini-3.6-flash-low": { maxOutputTokens: 65536 },
3182
2948
  "gemini-3.6-flash-medium": { maxOutputTokens: 65536 },
3183
2949
  "gemini-3.6-flash-high": { maxOutputTokens: 65536 },
@@ -3199,6 +2965,10 @@ function resolveAntigravityWireModel(logicalModelId) {
3199
2965
  const wireModelId = "gemini-3.5-flash-low";
3200
2966
  return { wireModelId, profile: WIRE_PROFILES[wireModelId] };
3201
2967
  }
2968
+ if (lower === "gemini-3.7-flash") {
2969
+ const wireModelId = "gemini-3.7-flash-low";
2970
+ return { wireModelId, profile: WIRE_PROFILES[wireModelId] };
2971
+ }
3202
2972
  if (lower === "gemini-3.6-flash") {
3203
2973
  const wireModelId = "gemini-3.6-flash-low";
3204
2974
  return { wireModelId, profile: WIRE_PROFILES[wireModelId] };
@@ -3297,12 +3067,15 @@ function convertOpenAiMessagesToGemini(messages, options) {
3297
3067
  }
3298
3068
  if (role === "assistant") {
3299
3069
  flushPendingFunctionCalls();
3300
- const parts = [];
3301
3070
  const text = messageText(
3302
3071
  content
3303
3072
  );
3304
- if (text) parts.push({ text });
3305
3073
  const toolCalls = "tool_calls" in raw && Array.isArray(raw.tool_calls) ? raw.tool_calls ?? [] : [];
3074
+ if (text) pushOrMergeContent("model", [{ text }]);
3075
+ if (toolCalls.length > 0 && contents.length > 0 && contents[contents.length - 1]?.role !== "user") {
3076
+ contents.push({ role: "user", parts: [{ text: "" }] });
3077
+ }
3078
+ const parts = [];
3306
3079
  for (const call of toolCalls) {
3307
3080
  const name = call?.function?.name?.trim();
3308
3081
  if (!name) continue;
@@ -3330,15 +3103,11 @@ function convertOpenAiMessagesToGemini(messages, options) {
3330
3103
  if (role === "tool") {
3331
3104
  const toolCallId = "tool_call_id" in raw && typeof raw.tool_call_id === "string" ? raw.tool_call_id : "";
3332
3105
  const rawName = "name" in raw && typeof raw.name === "string" ? raw.name.trim() : "";
3333
- const name = toolNamesById.get(toolCallId) || rawName || pendingFunctionCalls[0]?.name || "tool";
3334
3106
  const pendingIndex = pendingFunctionCalls.findIndex(
3335
- (p) => toolCallId ? p.id === toolCallId : p.name === name
3107
+ (p) => toolCallId ? p.id === toolCallId : rawName ? p.name === rawName : true
3336
3108
  );
3337
- if (pendingIndex !== -1) {
3338
- pendingFunctionCalls.splice(pendingIndex, 1);
3339
- } else if (pendingFunctionCalls.length > 0) {
3340
- pendingFunctionCalls.shift();
3341
- }
3109
+ if (pendingIndex === -1) continue;
3110
+ const [{ name, id }] = pendingFunctionCalls.splice(pendingIndex, 1);
3342
3111
  const output = messageText(content) || "{}";
3343
3112
  pushOrMergeContent("user", [
3344
3113
  {
@@ -3349,7 +3118,7 @@ function convertOpenAiMessagesToGemini(messages, options) {
3349
3118
  // Gemini contents 转成 Claude messages 时需要 functionResponse →
3350
3119
  // tool_result 的 tool_use_id;缺 id 时网关报
3351
3120
  // "tool_result.tool_use_id: Field required"(HTTP 400)。
3352
- ...toolCallId ? { id: toolCallId } : {}
3121
+ ...id ? { id } : {}
3353
3122
  }
3354
3123
  }
3355
3124
  ]);
@@ -3798,6 +3567,9 @@ function buildAnthropicMessagesBody(args) {
3798
3567
  input_schema: fn?.parameters && typeof fn.parameters === "object" ? fn.parameters : { type: "object", properties: {} }
3799
3568
  }];
3800
3569
  }) : [];
3570
+ if (tools.length > 0 && !tools.some((tool) => isCacheControl(tool.cache_control))) {
3571
+ tools[tools.length - 1].cache_control = { type: "ephemeral" };
3572
+ }
3801
3573
  const model = stringValue(args.openAiBody.model) ?? args.agentConfig.model ?? "claude-sonnet-5";
3802
3574
  const maxTokensRaw = args.openAiBody.max_completion_tokens ?? args.openAiBody.max_tokens ?? args.agentConfig.max_tokens;
3803
3575
  const maxTokens = typeof maxTokensRaw === "number" && Number.isFinite(maxTokensRaw) ? Math.max(1, Math.floor(maxTokensRaw)) : 8192;
@@ -3905,7 +3677,7 @@ async function fetchAnthropicMessagesCompletion(args) {
3905
3677
  const payload = await response.json().catch(async () => ({
3906
3678
  error: { message: await response.text().catch(() => response.statusText) }
3907
3679
  }));
3908
- if (!response.ok) return { status: response.status, body: payload };
3680
+ if (!response.ok) return { status: response.status, body: payload, headers: response.headers };
3909
3681
  return { status: 200, body: mapAnthropicMessageToOpenAi(payload) };
3910
3682
  }
3911
3683
  function isAnthropicOAuthAgent(agent) {
@@ -10076,13 +9848,25 @@ function stablePromptCacheKey(parts) {
10076
9848
  }
10077
9849
  return `nolo-codex-${hash.toString(16).padStart(8, "0")}`;
10078
9850
  }
9851
+ function convertMessagesToCodexInput(messages) {
9852
+ return convertMessagesToResponsesInput(messages).map((item) => {
9853
+ if (item.type !== "reasoning") return item;
9854
+ return {
9855
+ type: "reasoning",
9856
+ summary: item.content.map((part) => ({
9857
+ type: "summary_text",
9858
+ text: part.text
9859
+ }))
9860
+ };
9861
+ });
9862
+ }
10079
9863
  function buildCodexRequestBody(args, identity = createCodexRequestIdentity()) {
10080
9864
  const rawMessages = Array.isArray(args.openAiBody.messages) ? args.openAiBody.messages : [];
10081
9865
  const nonSystem = rawMessages.filter((m) => {
10082
9866
  const role = m && typeof m === "object" ? String(m.role) : "";
10083
9867
  return role !== "system" && role !== "developer";
10084
9868
  });
10085
- const input = convertMessagesToResponsesInput(nonSystem);
9869
+ const input = convertMessagesToCodexInput(nonSystem);
10086
9870
  const instructions = collectInstructions(rawMessages, args.agentConfig.prompt);
10087
9871
  const stableInstructions = collectStableInstructions(rawMessages, args.agentConfig.prompt);
10088
9872
  const model = asOptionalTrimmedString(args.openAiBody.model) ?? asOptionalTrimmedString(args.agentConfig.model) ?? "gpt-5.5";
@@ -10107,12 +9891,47 @@ function buildCodexRequestBody(args, identity = createCodexRequestIdentity()) {
10107
9891
  }
10108
9892
  return body;
10109
9893
  }
9894
+ function codexStreamFailureStatus(failure) {
9895
+ const marker = `${failure.code ?? ""} ${failure.type ?? ""}`.toLowerCase();
9896
+ if (marker.includes("overload") || marker.includes("service_unavailable")) return 503;
9897
+ if (marker.includes("rate_limit") || marker.includes("usage_limit")) return 429;
9898
+ return 502;
9899
+ }
9900
+ var RETRYABLE_CODEX_STREAM_STATUSES = /* @__PURE__ */ new Set([503]);
9901
+ var CODEX_STREAM_MAX_ATTEMPTS = 3;
9902
+ var CODEX_STREAM_RETRY_BASE_DELAY_MS = 500;
9903
+ function toStreamFailure(raw) {
9904
+ if (!raw || typeof raw !== "object") return void 0;
9905
+ const err = raw;
9906
+ const reason = typeof err.reason === "string" ? err.reason : void 0;
9907
+ const message = typeof err.message === "string" && err.message ? err.message : reason ? `Codex upstream ended incomplete: ${reason}.` : "";
9908
+ if (!message) return void 0;
9909
+ const code = typeof err.code === "string" ? err.code : reason;
9910
+ return {
9911
+ ...code ? { code } : {},
9912
+ ...typeof err.type === "string" ? { type: err.type } : {},
9913
+ message
9914
+ };
9915
+ }
10110
9916
  function aggregateResponsesStream(events) {
10111
9917
  let text = "";
10112
9918
  const toolCalls = [];
10113
9919
  let usage;
9920
+ let failure;
10114
9921
  for (const ev of events) {
10115
9922
  const type = String(ev.type ?? "");
9923
+ if (type === "error") {
9924
+ failure = failure ?? toStreamFailure(ev.error);
9925
+ continue;
9926
+ }
9927
+ if (type === "response.failed" || type === "response.incomplete") {
9928
+ const response = ev.response;
9929
+ failure = failure ?? toStreamFailure(response?.error) ?? toStreamFailure(response?.incomplete_details) ?? {
9930
+ code: type === "response.incomplete" ? "response_incomplete" : "response_failed",
9931
+ message: `Codex upstream ended with ${type}.`
9932
+ };
9933
+ continue;
9934
+ }
10116
9935
  if (type === "response.output_text.delta" && typeof ev.delta === "string") {
10117
9936
  text += ev.delta;
10118
9937
  continue;
@@ -10149,7 +9968,7 @@ function aggregateResponsesStream(events) {
10149
9968
  }
10150
9969
  }
10151
9970
  }
10152
- return { text, toolCalls, usage };
9971
+ return { text, toolCalls, usage, failure };
10153
9972
  }
10154
9973
  async function readSseEvents(response) {
10155
9974
  return readSseDataValues(response, (line) => {
@@ -10157,19 +9976,9 @@ async function readSseEvents(response) {
10157
9976
  return ev && !Array.isArray(ev) ? ev : null;
10158
9977
  });
10159
9978
  }
10160
- async function fetchCodexResponsesCompletion(args) {
9979
+ var defaultSleep2 = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
9980
+ async function callCodexResponsesOnce(args, accountId) {
10161
9981
  const fetchImpl = args.fetchImpl ?? fetch;
10162
- const accountId = args.accountId?.trim() || decodeCodexAccountId(args.accessToken);
10163
- if (!accountId) {
10164
- return {
10165
- status: 401,
10166
- body: {
10167
- error: {
10168
- message: "ChatGPT Codex credential is missing chatgpt_account_id. Re-run `nolo auth chatgpt`."
10169
- }
10170
- }
10171
- };
10172
- }
10173
9982
  const identity = createCodexRequestIdentity();
10174
9983
  const body = buildCodexRequestBody(args, identity);
10175
9984
  const headers = buildCodexCompatibilityHeaders(identity, accountId);
@@ -10188,7 +9997,19 @@ async function fetchCodexResponsesCompletion(args) {
10188
9997
  };
10189
9998
  }
10190
9999
  const events = await readSseEvents(response);
10191
- const { text, toolCalls, usage } = aggregateResponsesStream(events);
10000
+ const { text, toolCalls, usage, failure } = aggregateResponsesStream(events);
10001
+ if (failure) {
10002
+ return {
10003
+ status: codexStreamFailureStatus(failure),
10004
+ body: {
10005
+ error: {
10006
+ message: failure.message,
10007
+ ...failure.code ? { code: failure.code } : {},
10008
+ ...failure.type ? { type: failure.type } : {}
10009
+ }
10010
+ }
10011
+ };
10012
+ }
10192
10013
  const message = {
10193
10014
  role: "assistant",
10194
10015
  content: text || null
@@ -10208,6 +10029,30 @@ async function fetchCodexResponsesCompletion(args) {
10208
10029
  }
10209
10030
  };
10210
10031
  }
10032
+ async function fetchCodexResponsesCompletion(args) {
10033
+ const accountId = args.accountId?.trim() || decodeCodexAccountId(args.accessToken);
10034
+ if (!accountId) {
10035
+ return {
10036
+ status: 401,
10037
+ body: {
10038
+ error: {
10039
+ message: "ChatGPT Codex credential is missing chatgpt_account_id. Re-run `nolo auth chatgpt`."
10040
+ }
10041
+ }
10042
+ };
10043
+ }
10044
+ const sleep = args.sleep ?? defaultSleep2;
10045
+ const requestedMaxAttempts = Number(args.maxAttempts);
10046
+ const maxAttempts = Number.isFinite(requestedMaxAttempts) ? Math.min(10, Math.max(1, Math.floor(requestedMaxAttempts))) : CODEX_STREAM_MAX_ATTEMPTS;
10047
+ let result = await callCodexResponsesOnce(args, accountId);
10048
+ for (let attempt = 1; attempt < maxAttempts; attempt += 1) {
10049
+ if (!RETRYABLE_CODEX_STREAM_STATUSES.has(result.status)) break;
10050
+ if (args.signal?.aborted) break;
10051
+ await sleep(CODEX_STREAM_RETRY_BASE_DELAY_MS * 2 ** (attempt - 1));
10052
+ result = await callCodexResponsesOnce(args, accountId);
10053
+ }
10054
+ return result;
10055
+ }
10211
10056
  function isCodexOAuthAgent(agentConfig) {
10212
10057
  if (!agentConfig) return false;
10213
10058
  const apiKeyRef = asTrimmedLowercaseString(agentConfig.apiKeyRef);
@@ -10302,313 +10147,15 @@ function createOAuthApiKeyRefResolver(options = {}) {
10302
10147
  };
10303
10148
  }
10304
10149
 
10305
- // packages/cli/client/hybridRecordStore.ts
10306
- function resolveFallbackServers(env) {
10307
- const values = [
10308
- env.NOLO_SERVER_URL,
10309
- env.BASE_URL,
10310
- ...NOLO_CLUSTER_SERVERS
10311
- ].filter((value) => typeof value === "string" && value.trim().length > 0);
10312
- return [...new Set(values.map(normalizeServerOrigin))];
10150
+ // packages/cli/client/cliWorkspaceToolVariants.ts
10151
+ function shouldUseDeclaredOnlyLocalWorkspaceTools(env) {
10152
+ const value = env.NOLO_LOCAL_WORKSPACE_TOOLSET || env.NOLO_LOCAL_TOOLSET_MODE || "";
10153
+ return value === "declared-only" || value === "declared";
10313
10154
  }
10314
- function resolveAuthToken(env) {
10315
- return resolvePlatformAuthToken(env);
10316
- }
10317
- function createCliHybridRecordStore(deps) {
10318
- return createHybridRecordStore({
10319
- db: deps.db,
10320
- defaultServer: normalizeServerOrigin(
10321
- deps.env.NOLO_SERVER || deps.env.BASE_URL || DEFAULT_NOLO_SERVER_URL
10322
- ),
10323
- fallbackServers: resolveFallbackServers(deps.env),
10324
- authToken: resolveAuthToken(deps.env),
10325
- fetchImpl: deps.fetchImpl
10326
- });
10327
- }
10328
-
10329
- // packages/agent-runtime/dialogTitleLlm.ts
10330
- async function generateLocalDialogTitle(input) {
10331
- const { env, messages, fallbackTitle } = input;
10332
- const timeoutMs = input.timeoutMs ?? 15e3;
10333
- const visibleMessages = messages.filter((m) => m.role === "user" || m.role === "assistant").slice(-12).map((m) => ({
10334
- role: m.role,
10335
- content: typeof m.content === "string" ? m.content : extractText(m.content)
10336
- }));
10337
- if (visibleMessages.length === 0) {
10338
- return { title: fallbackTitle, source: "fallback" };
10339
- }
10340
- try {
10341
- const providerConfig = await input.resolveProviderConfig({
10342
- agentConfig: { ...BUILTIN_TITLE_LLM_CONFIG, key: BUILTIN_TITLE_LLM_CONFIG.id },
10343
- env
10344
- });
10345
- if (!providerConfig?.authToken) {
10346
- return { title: fallbackTitle, source: "fallback" };
10347
- }
10348
- const request = input.buildRequest({
10349
- providerConfig,
10350
- messages: [
10351
- {
10352
- role: "user",
10353
- content: JSON.stringify(
10354
- visibleMessages.map((m) => ({ role: m.role, content: m.content }))
10355
- )
10356
- }
10357
- ],
10358
- stream: false
10359
- });
10360
- const res = await input.fetchImpl(request.url, {
10361
- ...request.init,
10362
- signal: AbortSignal.timeout(timeoutMs)
10363
- });
10364
- if (!res.ok) {
10365
- return { title: fallbackTitle, source: "fallback" };
10366
- }
10367
- const raw = await res.text().catch(() => "");
10368
- const data = safeParseJson(raw);
10369
- if (!data) {
10370
- return { title: fallbackTitle, source: "fallback" };
10371
- }
10372
- const parsed = input.parseResponse({ providerConfig, data });
10373
- const generated = normalizeDialogTitle(parsed.content);
10374
- if (!generated) {
10375
- return { title: fallbackTitle, source: "fallback" };
10376
- }
10377
- return { title: generated, source: "llm" };
10378
- } catch {
10379
- return { title: fallbackTitle, source: "fallback" };
10380
- }
10381
- }
10382
- function extractText(content) {
10383
- if (typeof content === "string") return content;
10384
- if (Array.isArray(content)) {
10385
- return content.filter((part) => part?.type === "text").map((part) => part.text ?? "").join(" ");
10386
- }
10387
- return "";
10388
- }
10389
- function safeParseJson(raw) {
10390
- try {
10391
- return JSON.parse(raw);
10392
- } catch {
10393
- return null;
10394
- }
10395
- }
10396
-
10397
- // packages/cli/client/localRuntimeDialog.ts
10398
- async function resolveStore(deps) {
10399
- if (deps.store) return deps.store;
10400
- return createCliHybridRecordStore({
10401
- db: deps.db ?? await defaultLocalRuntimeDb(),
10402
- env: deps.env,
10403
- fetchImpl: deps.fetchImpl
10404
- });
10405
- }
10406
- async function getOrCreateSharedStore(deps) {
10407
- if (deps.store) return deps.store;
10408
- const cacheKey = normalizeRuntimeCacheCwd(deps.cwd);
10409
- let storePromise = hybridStoreCache.get(cacheKey);
10410
- if (!storePromise) {
10411
- storePromise = resolveStore(deps);
10412
- hybridStoreCache.set(cacheKey, storePromise);
10413
- }
10414
- return storePromise;
10415
- }
10416
- function createLocalDialogTitleGenerator(deps, ctx) {
10417
- return async (input) => {
10418
- if (!canUsePlatformChatProvider(deps.env)) {
10419
- return null;
10420
- }
10421
- const result = await generateLocalDialogTitle({
10422
- messages: input.messages,
10423
- env: deps.env,
10424
- fetchImpl: async (url, init) => fetchWithTransientRetry(
10425
- deps.fetchImpl ?? fetch,
10426
- url,
10427
- init,
10428
- {
10429
- sleep: deps.sleep,
10430
- loopbackRequest: ctx.loopbackRequest
10431
- }
10432
- ),
10433
- resolveProviderConfig: async (args) => resolvePlatformChatProviderConfig({
10434
- agentConfig: args.agentConfig,
10435
- env: args.env,
10436
- apiKeyRefResolver: ctx.apiKeyRefResolver,
10437
- credentialBroker: ctx.credentialBroker
10438
- }),
10439
- buildRequest: (args) => buildPlatformChatCompletionRequest({
10440
- providerConfig: args.providerConfig,
10441
- messages: args.messages,
10442
- stream: false
10443
- }),
10444
- parseResponse: (args) => parsePlatformChatCompletionResponse({
10445
- providerConfig: args.providerConfig,
10446
- data: args.data,
10447
- trace: []
10448
- }),
10449
- fallbackTitle: input.fallbackTitle,
10450
- timeoutMs: 15e3
10451
- });
10452
- return result.source === "llm" ? result.title : null;
10453
- };
10454
- }
10455
- async function writeLocalTokenRecord(args) {
10456
- const rawUsage = args.input.result.usage;
10457
- if (!rawUsage || typeof rawUsage !== "object" || Object.keys(rawUsage).length === 0) {
10458
- return;
10459
- }
10460
- const timestamp = args.now();
10461
- const prepared = prepareTokenUsageData({
10462
- rawUsage,
10463
- agentConfig: {
10464
- model: args.input.result.model || "unknown",
10465
- provider: args.input.result.provider,
10466
- apiSource: "cli"
10467
- },
10468
- userId: args.userId,
10469
- agentId: args.input.agentKey,
10470
- dialogId: args.dialogId,
10471
- timestamp,
10472
- entry_path: "cli-local"
10473
- });
10474
- const recordKey = createTokenKey.record(args.userId, timestamp);
10475
- const tokenRecord = {
10476
- id: args.createId(),
10477
- type: "token",
10478
- ...prepared.tokenData
10479
- };
10480
- await args.store.write(recordKey, tokenRecord);
10481
- }
10482
- async function writeDialog(args) {
10483
- let existingDialog = null;
10484
- if (args.input.continueDialogId) {
10485
- const dialogKey = `dialog-${args.userId}-${args.input.continueDialogId}`;
10486
- existingDialog = await args.store.read(dialogKey);
10487
- }
10488
- const authToken = resolveRuntimeAuthToken(args.env);
10489
- const isLoggedIn = Boolean(
10490
- authToken && (parseUserIdFromAuthToken(authToken) || args.env.NOLO_MACHINE_API_KEY?.trim())
10491
- );
10492
- const hasExistingTitle = typeof existingDialog?.title === "string" && existingDialog.title.trim();
10493
- let titleOverride;
10494
- if (isLoggedIn && !hasExistingTitle && args.titleGenerator) {
10495
- const fallbackTitle = extractLastUserText(args.input.messages).trim().slice(0, 80) || "Local agent run";
10496
- try {
10497
- const generated = await args.titleGenerator({
10498
- messages: args.input.messages,
10499
- fallbackTitle
10500
- });
10501
- if (generated) {
10502
- titleOverride = generated;
10503
- }
10504
- } catch {
10505
- }
10506
- }
10507
- const plan = buildLocalDialogWritePlan({
10508
- input: args.input,
10509
- userId: args.userId,
10510
- now: args.now(),
10511
- createId: args.createId,
10512
- existingDialog,
10513
- cwd: args.cwd,
10514
- ...titleOverride ? { titleOverride } : {}
10515
- });
10516
- await args.store.batch(plan.ops);
10517
- try {
10518
- await writeLocalTokenRecord({
10519
- store: args.store,
10520
- input: args.input,
10521
- userId: args.userId,
10522
- dialogId: plan.dialogId,
10523
- now: args.now,
10524
- createId: args.createId,
10525
- output: args.output
10526
- });
10527
- } catch (error) {
10528
- args.output?.write(
10529
- `[nolo] Token usage record write failed (non-fatal): ${toErrorMessage(error)}
10530
- `
10531
- );
10532
- }
10533
- const hasSubjectRefs = localTurnHasSubjectRefs(args.input);
10534
- const isLocalUser = args.userId === "local";
10535
- if (!isLocalUser) {
10536
- try {
10537
- const syncResult = await syncLocalDialogEvidenceToRemote({
10538
- env: args.env,
10539
- fetchImpl: args.fetchImpl,
10540
- input: args.input,
10541
- ops: plan.ops,
10542
- output: args.output,
10543
- userId: args.userId
10544
- });
10545
- if (!syncResult.attempted) {
10546
- if (hasSubjectRefs) {
10547
- args.output?.write(
10548
- "[nolo] Local dialog evidence is local-only; set NOLO_SERVER and AUTH_TOKEN to make subjectRefs remotely queryable.\n"
10549
- );
10550
- }
10551
- }
10552
- } catch (error) {
10553
- if (hasSubjectRefs) {
10554
- throw error;
10555
- }
10556
- args.output?.write(
10557
- `[nolo] Remote dialog evidence sync failed; local dialog only: ${toErrorMessage(
10558
- error
10559
- )}
10560
- `
10561
- );
10562
- }
10563
- }
10564
- return { dialogId: plan.dialogId, title: plan.title };
10565
- }
10566
- function resolveCliDialogRecordKey(userId, dialogId) {
10567
- if (dialogId.startsWith("dialog-") && !dialogId.includes("-msg-")) {
10568
- return dialogId;
10569
- }
10570
- return `dialog-${userId}-${dialogId}`;
10571
- }
10572
- async function loadCliDialogSummary(args) {
10573
- const dialogKey = resolveCliDialogRecordKey(args.userId, args.dialogId);
10574
- const record = await args.store.read(dialogKey);
10575
- if (!record || typeof record !== "object") return null;
10576
- const summary = typeof record.summary === "string" ? record.summary.trim() : "";
10577
- if (!summary) return null;
10578
- const summarizedBeforeId = record.summarizedBeforeId;
10579
- return {
10580
- summary,
10581
- ...typeof summarizedBeforeId === "string" && summarizedBeforeId ? { summarizedBeforeId } : {}
10582
- };
10583
- }
10584
- async function saveCliDialogSummary(args) {
10585
- const dialogKey = resolveCliDialogRecordKey(args.userId, args.dialogId);
10586
- const existing = await args.store.read(dialogKey) ?? {};
10587
- const bareId = typeof existing.id === "string" && existing.id ? existing.id : args.dialogId.startsWith("dialog-") ? args.dialogId.slice(args.dialogId.lastIndexOf("-") + 1) : args.dialogId;
10588
- const compressionCount = typeof existing.compressionCount === "number" ? existing.compressionCount + 1 : 1;
10589
- await args.store.write(dialogKey, {
10590
- ...existing,
10591
- id: bareId,
10592
- dbKey: dialogKey,
10593
- type: "dialog",
10594
- userId: args.userId,
10595
- summary: args.summary,
10596
- ...args.summarizedBeforeId !== void 0 ? { summarizedBeforeId: args.summarizedBeforeId } : {},
10597
- compressionCount,
10598
- summaryPending: false,
10599
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
10600
- });
10601
- }
10602
-
10603
- // packages/cli/client/cliWorkspaceToolVariants.ts
10604
- function shouldUseDeclaredOnlyLocalWorkspaceTools(env) {
10605
- const value = env.NOLO_LOCAL_WORKSPACE_TOOLSET || env.NOLO_LOCAL_TOOLSET_MODE || "";
10606
- return value === "declared-only" || value === "declared";
10607
- }
10608
- function resolveGlobFilesDescriptionVariant(env) {
10609
- return resolveLocalWorkspaceDescriptionVariant(
10610
- env.NOLO_GLOBFILES_DESCRIPTION_VARIANT
10611
- );
10155
+ function resolveGlobFilesDescriptionVariant(env) {
10156
+ return resolveLocalWorkspaceDescriptionVariant(
10157
+ env.NOLO_GLOBFILES_DESCRIPTION_VARIANT
10158
+ );
10612
10159
  }
10613
10160
  function resolveListFilesDescriptionVariant(env) {
10614
10161
  return resolveLocalWorkspaceDescriptionVariant(
@@ -10652,61 +10199,10 @@ function resolveLocalWorkspaceParameterVariant(value) {
10652
10199
  return value === "minimal" || value === "scoped" || value === "rich" ? value : "rich";
10653
10200
  }
10654
10201
 
10655
- // packages/cli/client/cliChildDialogPersist.ts
10656
- async function persistCliPendingChildDialog(args) {
10657
- const nowIso = new Date(args.now).toISOString();
10658
- const dialogKey = `dialog-${args.userId}-${args.dialogId}`;
10659
- const record = {
10660
- id: args.dialogId,
10661
- dbKey: dialogKey,
10662
- type: "dialog",
10663
- userId: args.userId,
10664
- cybots: [args.agentKey],
10665
- primaryAgentKey: args.agentKey,
10666
- title: args.title.slice(0, 80),
10667
- status: "pending",
10668
- triggerType: "cli-local",
10669
- executionMode: args.background ? "background" : "foreground",
10670
- createdAt: nowIso,
10671
- updatedAt: nowIso,
10672
- ...args.spaceId ? { spaceId: args.spaceId } : {},
10673
- ...args.parentDialogId ? { parentDialogId: args.parentDialogId } : {},
10674
- ...args.rootDialogId ? { rootDialogId: args.rootDialogId } : {},
10675
- localRuntime: {
10676
- host: "cli",
10677
- workspaceRoot: args.workspaceRoot,
10678
- workspaceKind: "current",
10679
- workspaceAccess: "inherited"
10680
- }
10681
- };
10682
- await args.store.batch([{ type: "put", key: dialogKey, value: record }]);
10683
- }
10684
- async function persistCliFailedChildDialog(args) {
10685
- const dialogKey = `dialog-${args.userId}-${args.dialogId}`;
10686
- const existing = await args.store.read(dialogKey);
10687
- const existingRecord = existing && typeof existing === "object" ? existing : {};
10688
- await args.store.batch([
10689
- {
10690
- type: "put",
10691
- key: dialogKey,
10692
- value: {
10693
- ...existingRecord,
10694
- id: args.dialogId,
10695
- dbKey: dialogKey,
10696
- status: "failed",
10697
- errorMessage: args.errorMessage,
10698
- updatedAt: new Date(args.now).toISOString(),
10699
- finishedAt: args.now
10700
- }
10701
- }
10702
- ]);
10703
- }
10704
-
10705
10202
  // packages/cli/client/localRuntimeTools.ts
10706
10203
  function buildOpenAiTools(args) {
10707
10204
  const toolset = buildLocalWorkspaceToolsetForEnv(args);
10708
10205
  const toolNameSet = new Set(args.toolNames ?? []);
10709
- const callAgentTools = toolNameSet.has("callAgent") ? prepareTools(["callAgent"]) : [];
10710
10206
  const uiAskChoiceTools = toolNameSet.has("ask_user") ? prepareTools(["ask_user"]) : [];
10711
10207
  const readPastedTextTools = toolNameSet.has("readPastedText") ? [
10712
10208
  {
@@ -10740,7 +10236,6 @@ function buildOpenAiTools(args) {
10740
10236
  }
10741
10237
  ] : [];
10742
10238
  return [
10743
- ...callAgentTools,
10744
10239
  ...uiAskChoiceTools,
10745
10240
  ...readPastedTextTools,
10746
10241
  ...buildLocalWorkspaceOpenAiTools({
@@ -10775,7 +10270,7 @@ function addDefaultCliCoreTools(toolNames, env, options) {
10775
10270
  }
10776
10271
  function resolveCliEffectiveEnabledPacks(args) {
10777
10272
  return resolveEffectiveEnabledPacks({
10778
- enabledPacks: args.enabledPacks,
10273
+ enabledPacks: resolveAgentRequiredPackIds(args),
10779
10274
  declaredOnly: args.declaredOnly,
10780
10275
  emptyFallbackPacks: ["code"]
10781
10276
  });
@@ -10804,6 +10299,7 @@ function resolveCliRequestedToolNames(agentConfig, env, systemBuiltinSkills, opt
10804
10299
  expandEnabledPacks(
10805
10300
  resolveCliEffectiveEnabledPacks({
10806
10301
  enabledPacks: agentConfig?.enabledPacks,
10302
+ skills: agentConfig?.skills,
10807
10303
  declaredOnly
10808
10304
  }),
10809
10305
  resolveRequestedRuntimeToolNames({ agentConfig })
@@ -10822,10 +10318,11 @@ function resolveCliRequestedToolNames(agentConfig, env, systemBuiltinSkills, opt
10822
10318
  declaredOnly ? expanded : addDefaultSystemCapabilityTools(expanded),
10823
10319
  systemBuiltinSkills
10824
10320
  );
10825
- return applyDisabledTools(
10321
+ const afterDisabled = applyDisabledTools(
10826
10322
  filtered,
10827
10323
  agentConfig?.disabledTools
10828
10324
  );
10325
+ return filterToolNamesForRunKind(afterDisabled, isSubtaskRun(env));
10829
10326
  }
10830
10327
  function resolveProviderOpenAiToolBundle(agentConfig, env, buildTools = buildOpenAiTools, additionalToolNames = [], options) {
10831
10328
  const requestedToolNames = [
@@ -10898,167 +10395,480 @@ function buildServerPlatformOpenAiTools(args) {
10898
10395
  ...webTools
10899
10396
  ];
10900
10397
  }
10901
- function createCliCallAgentToolExecutor(deps, ctx) {
10902
- const userId = resolveLocalUserId(deps.env);
10903
- const workspaceRoot = deps.cwd ?? process.cwd();
10904
- const now = deps.now ?? Date.now;
10905
- const createId = deps.createId ?? createFallbackId;
10906
- return async (call) => {
10907
- const parsed = parseNoloWorkspaceToolArguments(call.arguments);
10908
- const agentKey = asTrimmedString(parsed.agentKey);
10909
- const task = asTrimmedString(parsed.task);
10910
- if (!agentKey) {
10911
- return {
10912
- content: JSON.stringify({ error: "callAgent: agentKey is required" }),
10913
- metadata: { callAgent: true }
10914
- };
10915
- }
10916
- if (!task) {
10917
- return {
10918
- content: JSON.stringify({ error: "callAgent: task is required" }),
10919
- metadata: { callAgent: true }
10920
- };
10921
- }
10922
- const allowedChildAgentKeys = asTrimmedNonEmptyStringArray(
10923
- ctx.runtimeContext?.allowedChildAgentKeys
10924
- );
10925
- if (allowedChildAgentKeys.length > 0 && !allowedChildAgentKeys.includes(agentKey)) {
10926
- return {
10927
- content: JSON.stringify({
10928
- error: "callAgent: agentKey is not allowed by parent runtimeContext.allowedChildAgentKeys",
10929
- agentKey,
10930
- allowedChildAgentKeys
10931
- }),
10932
- metadata: { callAgent: true }
10398
+
10399
+ // packages/cli/client/hybridRecordStore.ts
10400
+ function resolveFallbackServers(env) {
10401
+ const values = [
10402
+ env.NOLO_SERVER_URL,
10403
+ env.BASE_URL,
10404
+ ...parseSyncServersEnv(env),
10405
+ ...NOLO_CLUSTER_SERVERS
10406
+ ].filter((value) => typeof value === "string" && value.trim().length > 0);
10407
+ return [...new Set(values.map(normalizeServerOrigin))];
10408
+ }
10409
+ function resolveAuthToken(env) {
10410
+ return resolvePlatformAuthToken(env);
10411
+ }
10412
+ function createCliHybridRecordStore(deps) {
10413
+ return createHybridRecordStore({
10414
+ db: deps.db,
10415
+ defaultServer: normalizeServerOrigin(
10416
+ deps.env.NOLO_SERVER || deps.env.BASE_URL || DEFAULT_NOLO_SERVER_URL
10417
+ ),
10418
+ fallbackServers: resolveFallbackServers(deps.env),
10419
+ authToken: resolveAuthToken(deps.env),
10420
+ fetchImpl: deps.fetchImpl
10421
+ });
10422
+ }
10423
+
10424
+ // packages/agent-runtime/dialogTitleLlm.ts
10425
+ async function generateLocalDialogTitle(input) {
10426
+ const { env, messages, fallbackTitle } = input;
10427
+ const timeoutMs = input.timeoutMs ?? 15e3;
10428
+ const normalized = messages.filter((m) => m.role === "user" || m.role === "assistant").map((m, index) => ({
10429
+ id: m.id || `msg-${index}`,
10430
+ role: m.role,
10431
+ content: typeof m.content === "string" ? m.content : extractText(m.content)
10432
+ })).filter((m) => Boolean(m.content.trim()));
10433
+ if (normalized.length === 0) {
10434
+ return { title: fallbackTitle, source: "fallback" };
10435
+ }
10436
+ const earlyUserTurns = normalized.filter((m) => m.role === "user").slice(0, 2);
10437
+ const recentTurns = normalized.slice(-10);
10438
+ const seen = /* @__PURE__ */ new Set();
10439
+ const visibleMessages = [...earlyUserTurns, ...recentTurns].filter((m) => {
10440
+ if (seen.has(m.id)) return false;
10441
+ seen.add(m.id);
10442
+ return true;
10443
+ }).map((m) => {
10444
+ const content = m.content.length > 500 ? `${m.content.slice(0, 500)}...` : m.content;
10445
+ return { role: m.role, content };
10446
+ });
10447
+ while (visibleMessages.length > 1 && JSON.stringify(visibleMessages).length > 4e3) {
10448
+ visibleMessages.shift();
10449
+ }
10450
+ if (visibleMessages.length === 0) {
10451
+ return { title: fallbackTitle, source: "fallback" };
10452
+ }
10453
+ if (input.resolveProviderConfig && input.buildRequest && input.parseResponse) {
10454
+ try {
10455
+ const providerConfig = await input.resolveProviderConfig({
10456
+ agentConfig: { ...BUILTIN_TITLE_LLM_CONFIG, key: BUILTIN_TITLE_LLM_CONFIG.id },
10457
+ env
10458
+ });
10459
+ if (providerConfig?.authToken) {
10460
+ const request = input.buildRequest({
10461
+ providerConfig,
10462
+ messages: [
10463
+ {
10464
+ role: "user",
10465
+ content: JSON.stringify(visibleMessages)
10466
+ }
10467
+ ],
10468
+ stream: false
10469
+ });
10470
+ const res = await input.fetchImpl(request.url, {
10471
+ ...request.init,
10472
+ signal: AbortSignal.timeout(timeoutMs)
10473
+ });
10474
+ if (res.ok) {
10475
+ const raw = await res.text().catch(() => "");
10476
+ const data = safeParseJson(raw);
10477
+ if (data) {
10478
+ const parsed = input.parseResponse({ providerConfig, data });
10479
+ const generated = normalizeDialogTitle(parsed.content);
10480
+ if (generated) {
10481
+ return { title: generated, source: "llm" };
10482
+ }
10483
+ }
10484
+ }
10485
+ }
10486
+ } catch {
10487
+ }
10488
+ }
10489
+ if (input.resolveDirectProviderConfig) {
10490
+ try {
10491
+ const envApiKey = env.OPENAI_API_KEY || env.NOLO_LOCAL_OPENAI_API_KEY || "";
10492
+ const directAgentConfig = input.agentConfig && input.agentConfig.apiSource !== "platform" ? {
10493
+ ...input.agentConfig,
10494
+ // Fill env key only when the caller's agentConfig has no key of
10495
+ // its own; never overwrite an explicit agentConfig.apiKey.
10496
+ ...envApiKey && !input.agentConfig.apiKey ? { apiKey: envApiKey } : {}
10497
+ } : {
10498
+ ...BUILTIN_TITLE_LLM_CONFIG,
10499
+ apiSource: "custom",
10500
+ useServerProxy: false,
10501
+ key: BUILTIN_TITLE_LLM_CONFIG.id,
10502
+ ...envApiKey ? { apiKey: envApiKey } : {}
10933
10503
  };
10504
+ const directConfig = await input.resolveDirectProviderConfig({
10505
+ agentConfig: directAgentConfig,
10506
+ env
10507
+ });
10508
+ if (directConfig?.endpoint) {
10509
+ const apiKeyHeader = directConfig.apiKeyHeader || "Authorization";
10510
+ const apiKey = directConfig.apiKey || "";
10511
+ if (!apiKey && isRemoteAuthEndpoint(directConfig.endpoint)) {
10512
+ return { title: fallbackTitle, source: "fallback" };
10513
+ }
10514
+ const authValue = apiKeyHeader.toLowerCase() === "authorization" && apiKey && !apiKey.startsWith("Bearer ") ? `Bearer ${apiKey}` : apiKey;
10515
+ const headers = {
10516
+ "content-type": "application/json",
10517
+ ...apiKey ? { [apiKeyHeader]: authValue } : {}
10518
+ };
10519
+ const res = await input.fetchImpl(directConfig.endpoint, {
10520
+ method: "POST",
10521
+ headers,
10522
+ body: JSON.stringify({
10523
+ model: directConfig.model || input.agentConfig?.model || BUILTIN_TITLE_LLM_CONFIG.model,
10524
+ messages: [
10525
+ { role: "system", content: BUILTIN_TITLE_LLM_CONFIG.prompt },
10526
+ { role: "user", content: JSON.stringify(visibleMessages) }
10527
+ ],
10528
+ stream: false
10529
+ }),
10530
+ signal: AbortSignal.timeout(timeoutMs)
10531
+ });
10532
+ if (res.ok) {
10533
+ const raw = await res.text().catch(() => "");
10534
+ const data = safeParseJson(raw);
10535
+ const rawContent = data?.choices?.[0]?.message?.content ?? "";
10536
+ const generated = normalizeDialogTitle(rawContent);
10537
+ if (generated) {
10538
+ return { title: generated, source: "llm" };
10539
+ }
10540
+ }
10541
+ }
10542
+ } catch {
10934
10543
  }
10935
- const background = parsed.background === true;
10936
- const parentDialogId = asOptionalTrimmedString(ctx.dialogId);
10937
- const parentThreadId = parentDialogId ?? asOptionalTrimmedString(ctx.runtimeContext?.parentThreadId);
10938
- const rootThreadId = asOptionalTrimmedString(ctx.runtimeContext?.rootThreadId) ?? asOptionalTrimmedString(ctx.runtimeContext?.parentThreadId) ?? parentThreadId;
10939
- const presentationIntent = background ? "background_handoff" : "inline_result";
10940
- const threadKind = background ? "background" : "inline";
10941
- const childRuntimeContext = {
10942
- ...ctx.runtimeContext ?? {},
10943
- surface: "cli",
10944
- entrypoint: "agent-tool:callAgent",
10945
- threadKind,
10946
- presentationIntent,
10947
- ...parentThreadId ? { parentThreadId } : {},
10948
- ...rootThreadId ? { rootThreadId } : {},
10949
- workspaceRoot,
10950
- workspaceKind: "current",
10951
- workspaceAccess: "inherited"
10952
- };
10953
- const childDialogId = createId();
10954
- const store = await getOrCreateSharedStore(deps);
10955
- await persistCliPendingChildDialog({
10956
- store,
10957
- userId,
10958
- dialogId: childDialogId,
10959
- agentKey,
10960
- title: task,
10961
- spaceId: ctx.spaceId,
10962
- parentDialogId,
10963
- rootDialogId: rootThreadId,
10964
- workspaceRoot,
10965
- background,
10966
- now: now()
10544
+ }
10545
+ return { title: fallbackTitle, source: "fallback" };
10546
+ }
10547
+ function extractText(content) {
10548
+ if (typeof content === "string") return content;
10549
+ if (Array.isArray(content)) {
10550
+ const parts = content.map((part) => {
10551
+ if (!part) return "";
10552
+ if (typeof part === "string") return part;
10553
+ if (part.type === "text") return part.text ?? "";
10554
+ return "[\u56FE\u7247]";
10555
+ }).filter(Boolean);
10556
+ return parts.join(" ");
10557
+ }
10558
+ if (content && typeof content === "object") {
10559
+ const part = content;
10560
+ if (part.type === "text") return part.text ?? "";
10561
+ return "[\u56FE\u7247]";
10562
+ }
10563
+ return "";
10564
+ }
10565
+ function safeParseJson(raw) {
10566
+ try {
10567
+ return JSON.parse(raw);
10568
+ } catch {
10569
+ return null;
10570
+ }
10571
+ }
10572
+ function isRemoteAuthEndpoint(endpoint) {
10573
+ let host;
10574
+ try {
10575
+ host = new URL(endpoint).hostname.toLowerCase();
10576
+ } catch {
10577
+ return false;
10578
+ }
10579
+ if (!host) return false;
10580
+ if (host === "localhost" || host === "127.0.0.1" || host === "::1" || host.endsWith(".local") || host.startsWith("192.168.") || host.startsWith("10.") || /^172\.(1[6-9]|2\d|3[01])\./.test(host)) {
10581
+ return false;
10582
+ }
10583
+ if (host === "api.openai.com" || host.endsWith(".openai.com") || host.endsWith(".openai.azure.com") || host === "api.deepseek.com" || host === "api.anthropic.com" || host === "generativelanguage.googleapis.com" || host === "api.together.xyz" || host === "api.groq.com" || host === "openrouter.ai" || host === "api.mistral.ai") {
10584
+ return true;
10585
+ }
10586
+ return false;
10587
+ }
10588
+
10589
+ // packages/cli/client/localRuntimeDialog.ts
10590
+ var import_date_fns = __toESM(require_date_fns(), 1);
10591
+ async function resolveStore(deps) {
10592
+ if (deps.store) return deps.store;
10593
+ return createCliHybridRecordStore({
10594
+ db: deps.db ?? await defaultLocalRuntimeDb(),
10595
+ env: deps.env,
10596
+ fetchImpl: deps.fetchImpl
10597
+ });
10598
+ }
10599
+ async function getOrCreateSharedStore(deps) {
10600
+ if (deps.store) return deps.store;
10601
+ const cacheKey = normalizeRuntimeCacheCwd(deps.cwd);
10602
+ let storePromise = hybridStoreCache.get(cacheKey);
10603
+ if (!storePromise) {
10604
+ storePromise = resolveStore(deps);
10605
+ hybridStoreCache.set(cacheKey, storePromise);
10606
+ }
10607
+ return storePromise;
10608
+ }
10609
+ function createLocalDialogTitleGenerator(deps, ctx) {
10610
+ if (!canUsePlatformChatProvider(deps.env) && !hasDirectOpenAiCompatibleProvider(deps.env)) {
10611
+ return null;
10612
+ }
10613
+ return async (input) => {
10614
+ const result = await generateLocalDialogTitle({
10615
+ messages: input.messages,
10616
+ env: deps.env,
10617
+ fetchImpl: async (url, init) => fetchWithTransientRetry(
10618
+ deps.fetchImpl ?? fetch,
10619
+ url,
10620
+ init,
10621
+ {
10622
+ sleep: deps.sleep,
10623
+ loopbackRequest: ctx.loopbackRequest,
10624
+ maxAttempts: 1
10625
+ }
10626
+ ),
10627
+ resolveProviderConfig: async (args) => resolvePlatformChatProviderConfig({
10628
+ agentConfig: args.agentConfig,
10629
+ env: args.env,
10630
+ apiKeyRefResolver: ctx.apiKeyRefResolver,
10631
+ credentialBroker: ctx.credentialBroker
10632
+ }),
10633
+ resolveDirectProviderConfig: async (args) => resolveOpenAiCompatibleProviderConfig({
10634
+ agentConfig: args.agentConfig,
10635
+ env: args.env,
10636
+ apiKeyRefResolver: ctx.apiKeyRefResolver,
10637
+ credentialBroker: ctx.credentialBroker
10638
+ }),
10639
+ buildRequest: (args) => buildPlatformChatCompletionRequest({
10640
+ providerConfig: args.providerConfig,
10641
+ messages: args.messages,
10642
+ stream: false
10643
+ }),
10644
+ parseResponse: (args) => parsePlatformChatCompletionResponse({
10645
+ providerConfig: args.providerConfig,
10646
+ data: args.data,
10647
+ trace: []
10648
+ }),
10649
+ fallbackTitle: input.fallbackTitle,
10650
+ timeoutMs: 4e3
10967
10651
  });
10968
- const childAdapter = ctx.createChildAdapter({
10969
- dialogId: childDialogId,
10970
- spaceId: ctx.spaceId,
10971
- runtimeContext: childRuntimeContext
10652
+ return result.source === "llm" ? result.title : null;
10653
+ };
10654
+ }
10655
+ async function writeLocalTokenRecord(args) {
10656
+ const usageRecords = args.input.usageRecords?.length ? args.input.usageRecords : args.input.result.usage ? [{
10657
+ callId: typeof args.input.result.usage?.provider_call_id === "string" && args.input.result.usage.provider_call_id.trim() ? args.input.result.usage.provider_call_id.trim() : args.createId(),
10658
+ usage: args.input.result.usage,
10659
+ model: args.input.result.model,
10660
+ ...args.input.result.provider ? { provider: args.input.result.provider } : {}
10661
+ }] : [];
10662
+ if (usageRecords.length === 0) {
10663
+ return [];
10664
+ }
10665
+ const ops = [];
10666
+ for (const item of usageRecords) {
10667
+ if (!item.usage || Object.keys(item.usage).length === 0) continue;
10668
+ const timestamp = args.now();
10669
+ const callId = item.callId || typeof item.usage.provider_call_id === "string" && item.usage.provider_call_id.trim() || args.createId();
10670
+ const prepared = prepareTokenUsageData({
10671
+ rawUsage: item.usage,
10672
+ agentConfig: {
10673
+ model: item.model || args.input.billingConfig?.model || "unknown",
10674
+ provider: item.provider || args.input.billingConfig?.provider,
10675
+ apiSource: args.input.billingConfig?.apiSource,
10676
+ apiKeyRef: args.input.billingConfig?.apiKeyRef,
10677
+ inputPrice: args.input.billingConfig?.inputPrice,
10678
+ outputPrice: args.input.billingConfig?.outputPrice,
10679
+ sharingLevel: args.input.billingConfig?.sharingLevel,
10680
+ id: args.input.billingConfig?.id,
10681
+ userId: args.input.billingConfig?.userId
10682
+ },
10683
+ userId: args.userId,
10684
+ agentId: args.input.agentKey,
10685
+ dialogId: args.dialogId,
10686
+ timestamp,
10687
+ entry_path: "cli-local"
10972
10688
  });
10973
- const childInputBase = {
10974
- adapter: childAdapter,
10975
- agentRef: agentKey,
10976
- input: buildDelegatedTaskContent(task, parsed.input),
10977
- runtimeContext: childRuntimeContext,
10978
- spaceId: ctx.spaceId,
10979
- continueDialogId: childDialogId,
10980
- parentDialogId
10689
+ const recordKey = createTokenKey.recordForStableCall(args.userId, callId);
10690
+ const existingToken = await args.store.read(recordKey, { remote: false }).catch(() => null);
10691
+ if (existingToken) continue;
10692
+ const tokenRecord = {
10693
+ id: args.createId(),
10694
+ type: "token",
10695
+ ...prepared.tokenData
10981
10696
  };
10982
- if (background) {
10983
- void ctx.runChildTurn(childInputBase).catch(async (error) => {
10984
- const errorMessage = toErrorMessage(error);
10985
- try {
10986
- await persistCliFailedChildDialog({
10987
- store,
10988
- userId,
10989
- dialogId: childDialogId,
10990
- errorMessage,
10991
- now: now()
10992
- });
10993
- } catch (persistError) {
10994
- deps.output?.write(
10995
- `[nolo] failed to persist background child failure: ${toErrorMessage(
10996
- persistError
10997
- )}
10998
- `
10999
- );
11000
- }
10697
+ await args.store.write(recordKey, tokenRecord);
10698
+ ops.push({ type: "put", key: recordKey, value: tokenRecord });
10699
+ const dateKey = (0, import_date_fns.format)(timestamp, "yyyy-MM-dd");
10700
+ const statsKey = createTokenStatsKey(args.userId, dateKey);
10701
+ await runKeyed(statsKey, async () => {
10702
+ let existingStats = null;
10703
+ try {
10704
+ existingStats = await args.store.read(statsKey, { remote: false });
10705
+ } catch {
10706
+ }
10707
+ const newStats = applyTokenUsageToDayStats(existingStats, {
10708
+ userId: args.userId,
10709
+ timeKey: dateKey,
10710
+ model: prepared.billedModel,
10711
+ provider: prepared.recordProvider,
10712
+ input_tokens: prepared.usage.input_tokens,
10713
+ output_tokens: prepared.usage.output_tokens,
10714
+ cost: prepared.tokenData.cost
11001
10715
  });
11002
- return {
11003
- content: JSON.stringify({
11004
- success: true,
11005
- status: "pending",
11006
- agentKey,
11007
- childDialogId,
11008
- ...parentDialogId ? { parentDialogId } : {}
11009
- }),
11010
- metadata: { callAgent: true, background: true, localRuntime: true }
11011
- };
10716
+ const statsRecord = { ...newStats, id: statsKey, type: "token" };
10717
+ await args.store.write(statsKey, statsRecord);
10718
+ });
10719
+ }
10720
+ return ops;
10721
+ }
10722
+ async function writeDialog(args) {
10723
+ let existingDialog = null;
10724
+ if (args.input.continueDialogId) {
10725
+ const dialogKey = `dialog-${args.userId}-${args.input.continueDialogId}`;
10726
+ existingDialog = await args.store.read(dialogKey);
10727
+ }
10728
+ const nowMs = args.now();
10729
+ const existingTitle = typeof existingDialog?.title === "string" ? existingDialog.title.trim() : "";
10730
+ const existingTitleSource = existingDialog?.titleSource;
10731
+ const titleTimeoutMs = args.titleTimeoutMs ?? 2500;
10732
+ let needsTitleUpdate = false;
10733
+ if (existingTitleSource === "manual") {
10734
+ needsTitleUpdate = false;
10735
+ } else if (!existingTitle) {
10736
+ needsTitleUpdate = true;
10737
+ } else {
10738
+ const titleUpdatedAtMs = parseTitleUpdatedAtMs(existingDialog);
10739
+ if (titleUpdatedAtMs > 0) {
10740
+ if (nowMs - titleUpdatedAtMs >= 30 * 60 * 1e3) {
10741
+ needsTitleUpdate = true;
10742
+ }
10743
+ } else {
10744
+ needsTitleUpdate = false;
11012
10745
  }
10746
+ }
10747
+ let titlePromise;
10748
+ if (needsTitleUpdate && args.titleGenerator) {
10749
+ const rawLastUserText = extractLastUserText(args.input.messages);
10750
+ const fallbackTitle = buildDialogFallbackTitleFromUserInput(rawLastUserText) || "Local agent run";
10751
+ titlePromise = args.titleGenerator({ messages: args.input.messages, fallbackTitle }).catch(() => null);
10752
+ }
10753
+ let resolvedTitle = null;
10754
+ if (titlePromise) {
10755
+ let timeoutId;
10756
+ const timeoutPromise = new Promise((resolve) => {
10757
+ timeoutId = setTimeout(() => resolve(null), titleTimeoutMs);
10758
+ });
11013
10759
  try {
11014
- const childResult = await ctx.runChildTurn(childInputBase);
11015
- return {
11016
- content: JSON.stringify({
11017
- success: true,
11018
- agentKey,
11019
- dialogId: childDialogId,
11020
- model: childResult.model ?? null,
11021
- provider: childResult.provider ?? null,
11022
- content: childResult.content ?? "",
11023
- usage: childResult.usage ?? null
11024
- }),
11025
- metadata: { callAgent: true, background: false, localRuntime: true }
11026
- };
11027
- } catch (error) {
11028
- const errorMessage = toErrorMessage(error);
11029
- try {
11030
- await persistCliFailedChildDialog({
11031
- store,
11032
- userId,
11033
- dialogId: childDialogId,
11034
- errorMessage,
11035
- now: now()
11036
- });
11037
- } catch (persistError) {
11038
- deps.output?.write(
11039
- `[nolo] failed to persist foreground child failure: ${toErrorMessage(
11040
- persistError
11041
- )}
10760
+ resolvedTitle = await Promise.race([titlePromise, timeoutPromise]);
10761
+ } catch {
10762
+ resolvedTitle = null;
10763
+ } finally {
10764
+ if (timeoutId) clearTimeout(timeoutId);
10765
+ }
10766
+ }
10767
+ const plan = buildLocalDialogWritePlan({
10768
+ input: args.input,
10769
+ userId: args.userId,
10770
+ now: nowMs,
10771
+ createId: args.createId,
10772
+ existingDialog,
10773
+ cwd: args.cwd,
10774
+ ...resolvedTitle ? { titleOverride: resolvedTitle } : {}
10775
+ });
10776
+ await args.store.batch(plan.ops);
10777
+ let tokenOps = [];
10778
+ try {
10779
+ tokenOps = await writeLocalTokenRecord({
10780
+ store: args.store,
10781
+ input: args.input,
10782
+ userId: args.userId,
10783
+ dialogId: plan.dialogId,
10784
+ now: args.now,
10785
+ createId: args.createId,
10786
+ output: args.output
10787
+ });
10788
+ } catch (error) {
10789
+ args.output?.write(
10790
+ `[nolo] Token usage record write failed (non-fatal): ${toErrorMessage(error)}
11042
10791
  `
11043
- );
11044
- }
11045
- return {
11046
- content: JSON.stringify({
11047
- success: false,
11048
- agentKey,
11049
- dialogId: childDialogId,
11050
- error: errorMessage
11051
- }),
11052
- metadata: {
11053
- callAgent: true,
11054
- background: false,
11055
- localRuntime: true,
11056
- error: true
10792
+ );
10793
+ }
10794
+ const hasSubjectRefs = localTurnHasSubjectRefs(args.input);
10795
+ const isLocalUser = args.userId === "local";
10796
+ if (!isLocalUser) {
10797
+ try {
10798
+ const syncResult = await syncLocalDialogEvidenceToRemote({
10799
+ env: args.env,
10800
+ fetchImpl: args.fetchImpl,
10801
+ input: args.input,
10802
+ ops: [...plan.ops, ...tokenOps],
10803
+ output: args.output,
10804
+ userId: args.userId
10805
+ });
10806
+ if (!syncResult.attempted) {
10807
+ if (hasSubjectRefs) {
10808
+ args.output?.write(
10809
+ "[nolo] Local dialog evidence is local-only; set NOLO_SERVER and AUTH_TOKEN to make subjectRefs remotely queryable.\n"
10810
+ );
11057
10811
  }
11058
- };
10812
+ }
10813
+ } catch (error) {
10814
+ if (hasSubjectRefs) {
10815
+ throw error;
10816
+ }
10817
+ args.output?.write(
10818
+ `[nolo] Remote dialog evidence sync failed; local dialog only: ${toErrorMessage(
10819
+ error
10820
+ )}
10821
+ `
10822
+ );
11059
10823
  }
10824
+ }
10825
+ return { dialogId: plan.dialogId, title: plan.title };
10826
+ }
10827
+ function resolveCliDialogRecordKey(userId, dialogId) {
10828
+ if (dialogId.startsWith("dialog-") && !dialogId.includes("-msg-")) {
10829
+ return dialogId;
10830
+ }
10831
+ return `dialog-${userId}-${dialogId}`;
10832
+ }
10833
+ async function loadCliDialogSummary(args) {
10834
+ const dialogKey = resolveCliDialogRecordKey(args.userId, args.dialogId);
10835
+ const record = await args.store.read(dialogKey);
10836
+ if (!record || typeof record !== "object") return null;
10837
+ const summary = typeof record.summary === "string" ? record.summary.trim() : "";
10838
+ if (!summary) return null;
10839
+ const summarizedBeforeId = record.summarizedBeforeId;
10840
+ return {
10841
+ summary,
10842
+ ...typeof summarizedBeforeId === "string" && summarizedBeforeId ? { summarizedBeforeId } : {}
11060
10843
  };
11061
10844
  }
10845
+ async function saveCliDialogSummary(args) {
10846
+ const dialogKey = resolveCliDialogRecordKey(args.userId, args.dialogId);
10847
+ const existing = await args.store.read(dialogKey) ?? {};
10848
+ const bareId = typeof existing.id === "string" && existing.id ? existing.id : args.dialogId.startsWith("dialog-") ? args.dialogId.slice(args.dialogId.lastIndexOf("-") + 1) : args.dialogId;
10849
+ const compressionCount = typeof existing.compressionCount === "number" ? existing.compressionCount + 1 : 1;
10850
+ await args.store.write(dialogKey, {
10851
+ ...existing,
10852
+ id: bareId,
10853
+ dbKey: dialogKey,
10854
+ type: "dialog",
10855
+ userId: args.userId,
10856
+ summary: args.summary,
10857
+ ...args.summarizedBeforeId !== void 0 ? { summarizedBeforeId: args.summarizedBeforeId } : {},
10858
+ compressionCount,
10859
+ summaryPending: false,
10860
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
10861
+ });
10862
+ }
10863
+ function parseTitleUpdatedAtMs(existingDialog) {
10864
+ const raw = existingDialog?.titleUpdatedAt;
10865
+ if (typeof raw === "number" && !Number.isNaN(raw)) return raw;
10866
+ if (typeof raw === "string" && raw.trim()) {
10867
+ const parsed = Date.parse(raw);
10868
+ if (!Number.isNaN(parsed)) return parsed;
10869
+ }
10870
+ return 0;
10871
+ }
11062
10872
 
11063
10873
  // packages/cli/client/localRuntimeAdapter.ts
11064
10874
  var currentMetaFile = fileURLToPath(import.meta.url);
@@ -11210,6 +11020,21 @@ function createCliLocalRuntimeAdapter(deps) {
11210
11020
  })
11211
11021
  }),
11212
11022
  resolveProvider: async (agentConfig) => {
11023
+ if (isAgentCoolingDown(agentConfig, now())) {
11024
+ throw new Error(
11025
+ `agent temporarily unavailable until ${new Date(Number(agentConfig.nextAvailableAt)).toISOString()}`
11026
+ );
11027
+ }
11028
+ const recordLocalAvailability = async (status, body) => {
11029
+ const key = typeof agentConfig.key === "string" ? agentConfig.key : "";
11030
+ if (!key || status !== 200 && status !== 429) return;
11031
+ const current = await getOrCreateSharedStore(deps).then((store) => store.read(key, { remote: false })).catch(() => null);
11032
+ if (!current || typeof current !== "object") return;
11033
+ const next = { ...current };
11034
+ if (status === 429) next.nextAvailableAt = resolveAgentNextAvailableAt(body, now());
11035
+ else delete next.nextAvailableAt;
11036
+ await getOrCreateSharedStore(deps).then((store) => store.write(key, next)).catch(() => void 0);
11037
+ };
11213
11038
  if (isCliProviderAgent(agentConfig)) {
11214
11039
  const provider = resolveCliProviderName(agentConfig);
11215
11040
  logLocalRuntimeDiagnostic("provider.selected", {
@@ -11260,7 +11085,20 @@ function createCliLocalRuntimeAdapter(deps) {
11260
11085
  const credentialBroker = createFileCredentialBroker();
11261
11086
  const serverUrl = asOptionalTrimmedString(deps.env.NOLO_SERVER) ?? "https://us.nolo.chat";
11262
11087
  const authToken = asOptionalTrimmedString(deps.env.AUTH_TOKEN);
11263
- const syncFetcher = authToken ? (ref) => fetchServerSyncedCredential({ currentServer: serverUrl, authToken }, ref) : void 0;
11088
+ const syncFetcher = authToken ? async (ref) => {
11089
+ try {
11090
+ return await fetchServerSyncedCredential(
11091
+ { currentServer: serverUrl, authToken },
11092
+ ref
11093
+ );
11094
+ } catch (error) {
11095
+ logLocalRuntimeDiagnostic("credential.sync.unavailable", {
11096
+ ref,
11097
+ error: toErrorMessage(error)
11098
+ });
11099
+ return null;
11100
+ }
11101
+ } : void 0;
11264
11102
  if (isAntigravityOAuthAgent(agentConfig)) {
11265
11103
  const accessToken = await apiKeyRefResolver("antigravity");
11266
11104
  if (!accessToken) {
@@ -11314,6 +11152,7 @@ function createCliLocalRuntimeAdapter(deps) {
11314
11152
  loopbackRequest
11315
11153
  })
11316
11154
  });
11155
+ await recordLocalAvailability(result.status, result.body);
11317
11156
  if (result.status < 200 || result.status >= 300) {
11318
11157
  const errMsg = result.body && typeof result.body === "object" && result.body.error && typeof result.body.error.message === "string" ? result.body.error.message : JSON.stringify(result.body);
11319
11158
  throw new Error(
@@ -11382,6 +11221,7 @@ function createCliLocalRuntimeAdapter(deps) {
11382
11221
  loopbackRequest
11383
11222
  })
11384
11223
  });
11224
+ await recordLocalAvailability(result.status, result.body);
11385
11225
  if (result.status < 200 || result.status >= 300) {
11386
11226
  const errMsg = result.body?.error && typeof result.body.error === "object" && typeof result.body.error.message === "string" ? result.body.error.message : JSON.stringify(result.body);
11387
11227
  throw new Error(
@@ -11452,6 +11292,7 @@ function createCliLocalRuntimeAdapter(deps) {
11452
11292
  loopbackRequest
11453
11293
  })
11454
11294
  });
11295
+ await recordLocalAvailability(result.status, result.body);
11455
11296
  if (result.status < 200 || result.status >= 300) {
11456
11297
  const errMsg = result.body?.error && typeof result.body.error === "object" && typeof result.body.error.message === "string" ? result.body.error.message : JSON.stringify(result.body);
11457
11298
  throw new Error(
@@ -11474,6 +11315,14 @@ function createCliLocalRuntimeAdapter(deps) {
11474
11315
  return {
11475
11316
  content,
11476
11317
  ...tool_calls ? { tool_calls } : {},
11318
+ // fetchCodexResponsesCompletion 已把 codex responses 流聚合完毕并归一化成
11319
+ // OpenAI chat.completions 形状:choices[0].finish_reason("stop"/"tool_calls")
11320
+ // + usage(prompt/completion/total_tokens)。必须透传,否则 localLoop 把正常走完
11321
+ // 的空轮误判成 stream_truncated、TUI context chip 也拿不到 token 数更新。
11322
+ finish_reason: typeof choice?.finish_reason === "string" ? choice.finish_reason : void 0,
11323
+ stream_complete: true,
11324
+ // body 类型是 Record<string, unknown>,透传 usage 需 cast 到 AgentRuntimeResult.usage。
11325
+ usage: result.body?.usage,
11477
11326
  trace: messages
11478
11327
  };
11479
11328
  }
@@ -11923,9 +11772,7 @@ export {
11923
11772
  t,
11924
11773
  toolLabel,
11925
11774
  agentRunCardLabels,
11926
- QUOTA_ERROR_PATTERNS,
11927
- isQuotaExhaustedError,
11928
11775
  clearCliLocalRuntimePreparedAgentCache,
11929
- createCliCallAgentToolExecutor,
11776
+ isSubtaskRun,
11930
11777
  createCliLocalRuntimeAdapter
11931
11778
  };