nolo-cli 0.104.0-alpha.1 → 0.104.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/README.md +1 -1
  2. package/{_executeModel-4W7C3ACS.js → _executeModel-JXG5TLPE.js} +75 -68
  3. package/action-GVA5ARSJ.js +20 -0
  4. package/{addContentAction-O7R66HEX.js → addContentAction-J3DLDV64.js} +12 -11
  5. package/{addDialogAgentAction-74OA7KYP.js → addDialogAgentAction-PHIXJCF2.js} +18 -19
  6. package/{agentDeleteCommand-2YAHAZFJ.js → agentDeleteCommand-2AWJGHXG.js} +117 -42
  7. package/{agentEmailCommands-O2KH4G7U.js → agentEmailCommands-VV5OSILE.js} +98 -88
  8. package/agentListCommands-OQMRBDWP.js +742 -0
  9. package/{agentMachineCommands-5I5CCUBM.js → agentMachineCommands-3VLLR6RL.js} +48 -43
  10. package/{agentProviderCommands-GNPTL5XR.js → agentProviderCommands-XPJYZSCE.js} +97 -2
  11. package/{agentPullCommand-O5MPVJ5E.js → agentPullCommand-B4T2DJ5H.js} +1 -1
  12. package/agentRecordCommands-GPIARVOW.js +138 -0
  13. package/agentRunCommand-LRJTZ7IM.js +167 -0
  14. package/{agentRunControl-AVJR4JW4.js → agentRunControl-3HWA2LLQ.js} +2 -2
  15. package/{agentRuntimeLocal-VXZSDIGT.js → agentRuntimeLocal-PV6AZOEU.js} +48 -39
  16. package/{agentSlice-6LBOYNKN.js → agentSlice-6R3JKS6I.js} +14 -13
  17. package/{appDeleteCommands-32MHLR4L.js → appDeleteCommands-WEX73P5R.js} +46 -41
  18. package/{appDeployCommands-WCQOUSOB.js → appDeployCommands-7BAXWA4M.js} +46 -41
  19. package/{appDeployStatusCommands-6QKB7DYN.js → appDeployStatusCommands-EJ6A2U4X.js} +46 -41
  20. package/{appGetCommands-SFAPP5PB.js → appGetCommands-SB355OEX.js} +46 -41
  21. package/{appListCommands-EKUBDRUO.js → appListCommands-TSZDPIKJ.js} +46 -41
  22. package/{authCommand-F4EFRRX6.js → authCommand-5PB3YRVV.js} +118 -24
  23. package/{authCommands-RGOGCPP6.js → authCommands-5RTUDYZW.js} +7 -5
  24. package/{builtinSkillRegistry-FUAVTRVZ.js → builtinSkillRegistry-SBBWTANC.js} +10 -8
  25. package/{chromeCommands-FZYAU75D.js → chromeCommands-AU5ZJXBG.js} +289 -37
  26. package/{chromeOpsCommands-62P2Q2YB.js → chromeOpsCommands-ZYTCMNHG.js} +156 -22
  27. package/{chunk-6OZ36FXI.js → chunk-27ONFIEY.js} +22 -4
  28. package/{chunk-JEDZAO7L.js → chunk-2SYTUTLI.js} +257 -37
  29. package/{chunk-HVURBGF3.js → chunk-2U3NHADM.js} +6 -6
  30. package/{chunk-53HEEOTT.js → chunk-2WJ57RJA.js} +83 -16
  31. package/{chunk-OIVSQH2O.js → chunk-3BXNTXYI.js} +299 -5
  32. package/{chunk-V4MJDUSR.js → chunk-3F424ASC.js} +2 -2
  33. package/{chunk-HJUYAD4A.js → chunk-3Q7NEHAR.js} +5 -5
  34. package/chunk-44CU7CKA.js +13545 -0
  35. package/{chunk-EYAQT56M.js → chunk-4DPP773Q.js} +2 -2
  36. package/chunk-4LCOCOR5.js +50 -0
  37. package/{chunk-JPGCXHNM.js → chunk-4LKMZ3RR.js} +1 -1
  38. package/{chunk-2GRGFCF6.js → chunk-4VJAC32I.js} +2 -1
  39. package/{chunk-6D6VBVEW.js → chunk-4W24JM32.js} +80 -14
  40. package/{chunk-H62HCIUP.js → chunk-4WMMIL66.js} +35 -22
  41. package/{chunk-MC4XLIJ7.js → chunk-523DITKR.js} +1 -1
  42. package/{chunk-ZCYXJSTU.js → chunk-54VPQTOA.js} +66 -94
  43. package/{chunk-E4MARS3Q.js → chunk-5GVBDNTO.js} +1 -1
  44. package/chunk-5MMAK75Q.js +15 -0
  45. package/{chunk-7S4YWN7P.js → chunk-5UUIGN6I.js} +4 -4
  46. package/{chunk-42V6BK2E.js → chunk-62IL3KQV.js} +4 -34
  47. package/{chunk-GQBOR3G6.js → chunk-7KVZ5LDQ.js} +2 -2
  48. package/chunk-A4SQRTZ6.js +15 -0
  49. package/{chunk-L5UXH3B2.js → chunk-AFH5QC6R.js} +58 -9
  50. package/{chunk-AEOE4Z6Q.js → chunk-AFSY3ZMB.js} +39 -15
  51. package/chunk-AGIM7IMJ.js +147 -0
  52. package/{chunk-5UTFL4QJ.js → chunk-AOTBKDRK.js} +7 -0
  53. package/{chunk-NA35QHCF.js → chunk-AWVP7PPG.js} +1 -1
  54. package/{chunk-EEGDMATE.js → chunk-B7APRSPS.js} +86 -81
  55. package/{chunk-W324THOB.js → chunk-BB7LVBTS.js} +20 -11
  56. package/{chunk-6YTOJY35.js → chunk-BZ7NPA6G.js} +35 -2
  57. package/{chunk-5H7KJX7J.js → chunk-CFHWVEUJ.js} +5 -5
  58. package/chunk-CHOZJMJS.js +78 -0
  59. package/{chunk-ETI2NYVF.js → chunk-CNA27EMD.js} +4 -4
  60. package/{chunk-ACTMNVHM.js → chunk-CYSYR67N.js} +1 -1
  61. package/{chunk-WV42OMV7.js → chunk-D2GQE6AF.js} +8 -8
  62. package/{chunk-GBRG75BQ.js → chunk-D2MDMY2M.js} +1 -0
  63. package/{chunk-Q554LLSI.js → chunk-DMXLXZKL.js} +1 -1
  64. package/chunk-DZ7R5V3Y.js +164 -0
  65. package/{chunk-3VNWWVKB.js → chunk-E3YWHWCR.js} +50 -18
  66. package/{chunk-KQ4ONNQD.js → chunk-EKPKW6RM.js} +155 -8
  67. package/{chunk-2J5Y72GY.js → chunk-EP7BWZ4B.js} +1 -1
  68. package/{chunk-ABQYVJBJ.js → chunk-EV235ILT.js} +5 -5
  69. package/{chunk-TRV2MIDW.js → chunk-EXAJ6DCR.js} +1 -0
  70. package/{chunk-3ZW5ZJMM.js → chunk-FGSJ3YMD.js} +23 -28
  71. package/{chunk-2F7RJC6L.js → chunk-FOXQ7BAB.js} +1 -1
  72. package/{chunk-SH43IESU.js → chunk-GGC55Q32.js} +199 -29
  73. package/chunk-GMZLP5DV.js +82 -0
  74. package/{chunk-DHDMC6FO.js → chunk-GSKPJIHY.js} +14 -9
  75. package/{chunk-FDTR4QJ6.js → chunk-H6A6X4EK.js} +32 -1
  76. package/{chunk-DVINR3PL.js → chunk-HDLLCBRF.js} +1 -1
  77. package/{chunk-QOKAKDPE.js → chunk-HIM3FCLF.js} +0 -1
  78. package/{chunk-ONXOABJR.js → chunk-HNGRBWYF.js} +3 -0
  79. package/{chunk-VLTIYV3X.js → chunk-HP57OUJO.js} +4 -4
  80. package/{chunk-NXUYQBDZ.js → chunk-HYXABXCZ.js} +29 -78
  81. package/{chunk-DNCEALKX.js → chunk-IEJ5YDA4.js} +22 -29
  82. package/{chunk-O6OSWFZQ.js → chunk-IHPDK6UD.js} +6 -0
  83. package/{chunk-BP4XNZRM.js → chunk-JIXGJTBU.js} +4 -4
  84. package/{chunk-GPCZ7UXG.js → chunk-K6U4NKFJ.js} +1 -1
  85. package/{chunk-Z77CRGTW.js → chunk-KA5O5KQL.js} +1 -1
  86. package/{chunk-4OEFYTWL.js → chunk-L5WOICCV.js} +67 -48
  87. package/{chunk-LS2ZANRJ.js → chunk-LOCNXNOU.js} +1 -1
  88. package/chunk-LWLNLZC5.js +265 -0
  89. package/{chunk-H66KLASO.js → chunk-LWXE6IFU.js} +567 -519
  90. package/{chunk-PUOC4YRO.js → chunk-M7VLQGQP.js} +11 -0
  91. package/chunk-NKWIS6G2.js +442 -0
  92. package/{chunk-BFU6C242.js → chunk-NMFV26UG.js} +1 -1
  93. package/{chunk-DJWMIXDZ.js → chunk-NSAFG343.js} +10 -10
  94. package/{chunk-UKZDAFYT.js → chunk-NSBID7PR.js} +6 -10
  95. package/{chunk-4J4QNKTW.js → chunk-NTR4SD3P.js} +845 -240
  96. package/{chunk-FZGV4FGK.js → chunk-NUHHBEJC.js} +378 -187
  97. package/chunk-NWENLDMM.js +42 -0
  98. package/chunk-O7SVKKLY.js +875 -0
  99. package/chunk-OCZKATRC.js +129 -0
  100. package/{chunk-VMFTFOWF.js → chunk-OKGVNZTL.js} +35 -8
  101. package/{chunk-KQHGZWSV.js → chunk-OXME3PEE.js} +1 -1
  102. package/{chunk-JRQFUXY7.js → chunk-P5GCTVGV.js} +99 -22
  103. package/{chunk-PQFHGD77.js → chunk-QKTESDWK.js} +161 -14
  104. package/{chunk-U3JZVWIB.js → chunk-QMXJ5KDJ.js} +68 -46
  105. package/{chunk-GEPUP7OO.js → chunk-QYUBY3OZ.js} +3 -3
  106. package/{chunk-3BMYXEUW.js → chunk-R5AJODNI.js} +1 -1
  107. package/{chunk-J4MOO56S.js → chunk-RILRSUGC.js} +10 -2
  108. package/{chunk-OEVAY4J6.js → chunk-RWX7AY7J.js} +18 -20
  109. package/{chunk-YY7I6GQ3.js → chunk-S2NSK7AZ.js} +11 -9
  110. package/{chunk-IIVGDG4I.js → chunk-SJKULPGX.js} +13 -7
  111. package/{chunk-BYMV6QZG.js → chunk-TNIX77YM.js} +4 -0
  112. package/chunk-TY3TIUJK.js +89 -0
  113. package/{chunk-I3TQJXQ7.js → chunk-U3C3IA32.js} +9 -2
  114. package/{chunk-UZGQNTVW.js → chunk-UA324D4O.js} +8 -6
  115. package/{chunk-YYLHWIW6.js → chunk-UKZX3NHN.js} +1 -1
  116. package/chunk-UWXDA7L5.js +8005 -0
  117. package/{chunk-3DII57IT.js → chunk-VCAQFMVN.js} +1 -1
  118. package/chunk-VIWDGTDA.js +8 -0
  119. package/chunk-VJVNU77E.js +83 -0
  120. package/{chunk-A6FMKU7M.js → chunk-VVJ2ENLY.js} +47 -67
  121. package/{chunk-UKWGR5SS.js → chunk-VY6VFGKR.js} +194 -1144
  122. package/{chunk-QDSINZM3.js → chunk-WJ3B6FX6.js} +1 -14
  123. package/{chunk-Q67DOFOO.js → chunk-Y3PMXGLH.js} +2 -0
  124. package/chunk-YK5MHOWM.js +915 -0
  125. package/chunk-YV25TM3R.js +780 -0
  126. package/{chunk-TOP2VXOR.js → chunk-YVU4XVJD.js} +5 -5
  127. package/{chunk-7SOWRCQQ.js → chunk-Z5NGE5DH.js} +29 -9
  128. package/{chunk-6VFARHZW.js → chunk-ZBHR642M.js} +994 -1079
  129. package/chunk-ZRAU6T47.js +56 -0
  130. package/{chunk-B6P4QKEN.js → chunk-ZSUDOTPL.js} +19 -17
  131. package/commandRegistry-QU62WJQX.js +833 -0
  132. package/{contentThunks-LXOZUD2Y.js → contentThunks-VGV3JJVB.js} +37 -35
  133. package/{cooldownCommands-REAWYVTY.js → cooldownCommands-ZLEPIB4A.js} +2 -2
  134. package/{createAgentAutomationAction-M5NDEIC6.js → createAgentAutomationAction-ZNVLI4WV.js} +39 -37
  135. package/createDialogAction-TTUA46DT.js +69 -0
  136. package/{createPageAction-WY5ZKP2M.js → createPageAction-KUFSC5XW.js} +46 -43
  137. package/{chunk-ITBLTYQB.js → cursorProvider-YGGNMVWX.js} +2496 -9897
  138. package/{dbSlice-XVWBAGYN.js → dbSlice-MYEETO73.js} +11 -10
  139. package/{deleteFile-YG6QMJLU.js → deleteFile-KESP65GC.js} +7 -6
  140. package/{dialogCommands-5J56DFWN.js → dialogCommands-DRDPEOTL.js} +49 -45
  141. package/{dialogSlice-HSGREZGA.js → dialogSlice-V2SVSTIV.js} +41 -34
  142. package/{dist-VODFSGNG.js → dist-NJC2W7I4.js} +61 -54
  143. package/{docCreateCommands-KYI2DC2U.js → docCreateCommands-M26CFDDQ.js} +48 -43
  144. package/{docDeleteCommands-67REUSRY.js → docDeleteCommands-LRZHCKVX.js} +49 -44
  145. package/{docReadCommands-HOSVSBDJ.js → docReadCommands-6QJSISHK.js} +47 -42
  146. package/{docStore-IIS36NGG.js → docStore-4RWAFNDM.js} +16 -12
  147. package/{docUpdateCommands-EMGCSMXY.js → docUpdateCommands-W4KU4U5T.js} +48 -43
  148. package/{fetchMessages-EIU7UFFK.js → fetchMessages-3PXAHKCR.js} +5 -5
  149. package/{getPricing-LUSEABA6.js → getPricing-NMMFT3YI.js} +4 -4
  150. package/{handleSendMessageAction-FEHCWMN2.js → handleSendMessageAction-VEWLGB5Z.js} +107 -41
  151. package/index.js +87 -884
  152. package/{lib-FRRS27KN.js → lib-PU2G2MSO.js} +523 -191
  153. package/{localDialogRead-BTFJ4V2I.js → localDialogRead-UYCKO7YZ.js} +2 -2
  154. package/{localRuntimeAuthority-6IR6BBXE.js → localRuntimeAuthority-TU6OBC76.js} +9 -6
  155. package/{localRuntimeDb-SYQGUF4U.js → localRuntimeDb-VKWVOPPI.js} +1 -1
  156. package/{machineCommands-G5J754EH.js → machineCommands-AEHHCJEW.js} +92 -82
  157. package/machineWsRunDispatch-MY2KHS36.js +154 -0
  158. package/{memberThunks-QGFLFVCI.js → memberThunks-EE6TI3TV.js} +20 -18
  159. package/{models-T3SG6MJ5.js → models-ZPU5EDF4.js} +2 -2
  160. package/oauthTokenStore-ITEB663N.js +23 -0
  161. package/package.json +1 -1
  162. package/pdf-U6KSUEZZ.js +26435 -0
  163. package/processSpawn-SOAV3FEV.js +11 -0
  164. package/profileConfig-ZGOFORWV.js +43 -0
  165. package/{providerPresetApply-FG6NZ57W.js → providerPresetApply-TEBH7FDX.js} +7 -7
  166. package/{providerRegistry-757OWH3O.js → providerRegistry-CBJLNCET.js} +11 -7
  167. package/{read-COE3ZYR6.js → read-PJIT2YEJ.js} +7 -6
  168. package/{readDocTool-5USN4A6V.js → readDocTool-DRSXHGNL.js} +5 -3
  169. package/{readlineWorkspace-TGCYJFEB.js → readlineWorkspace-7Q6MITLV.js} +2685 -2050
  170. package/{removeDialogAgentAction-IX3MN2ZO.js → removeDialogAgentAction-IN4WUKJJ.js} +21 -25
  171. package/{runAgentClientLoop-UY76PPZM.js → runAgentClientLoop-H2JALUVD.js} +76 -70
  172. package/{runtimeDoctorCommands-X6WDBEQX.js → runtimeDoctorCommands-PIVSBZRZ.js} +4 -4
  173. package/runtimeModeArgs-2Y7BALTO.js +31 -0
  174. package/{setDialogExtraReferencesAction-X4HCPNZL.js → setDialogExtraReferencesAction-VDRF7LPK.js} +15 -13
  175. package/{setPrimaryDialogAgentAction-4GMIQLOD.js → setPrimaryDialogAgentAction-RO6LIWJ4.js} +22 -26
  176. package/{chunk-AFUORTY4.js → settingThunks-3OPOLOIA.js} +226 -940
  177. package/{skillDiscovery-CYUCJMAG.js → skillDiscovery-ZBU76COE.js} +3 -3
  178. package/{slateUtils-RIZES3PV.js → slateUtils-NGV2NMKM.js} +1 -1
  179. package/{spaceCommands-NKJVJCQQ.js → spaceCommands-UBLL5EF2.js} +51 -46
  180. package/{streamAgentChatTurn-3YVIO4KE.js → streamAgentChatTurn-FFJWW3FD.js} +214 -108
  181. package/{switchDialogAgentAction-N52LRPA3.js → switchDialogAgentAction-MVF2JNE7.js} +18 -19
  182. package/{tableCommands-O6NWHFKV.js → tableCommands-6U4BMLRR.js} +10 -10
  183. package/{toolApiClient-QVJ6HDNN.js → toolApiClient-EJT6TMFG.js} +3 -3
  184. package/tools-AZRYLYNK.js +116 -0
  185. package/{tuiSummaryLlmCaller-WFRWERGK.js → tuiSummaryLlmCaller-7S5HJ3MT.js} +25 -22
  186. package/{updateCommands-2M74UL7P.js → updateCommands-4EHDLF5J.js} +3 -3
  187. package/{updateDialogTitleAction-GF5T3VMK.js → updateDialogTitleAction-5WXTRUJM.js} +82 -44
  188. package/action-YVYQEAAV.js +0 -31
  189. package/agentListCommands-GUDMBDHZ.js +0 -410
  190. package/agentRecordCommands-RSS44XHC.js +0 -128
  191. package/agentRunCommand-P7VHL4ZR.js +0 -156
  192. package/browser-image-compression-MFDTD7K6.js +0 -1763
  193. package/chatQueueReduxAdapter-WFROMKD3.js +0 -186
  194. package/chunk-CQ3WA2NE.js +0 -299
  195. package/chunk-FQUHIG5X.js +0 -20782
  196. package/chunk-M76ECQRD.js +0 -13658
  197. package/chunk-MEYIRKDN.js +0 -8967
  198. package/chunk-Q5FJJ6XI.js +0 -103
  199. package/chunk-S7Z2HMCN.js +0 -25
  200. package/chunk-Y5GCAMGP.js +0 -9
  201. package/chunk-YPREESF4.js +0 -225
  202. package/createDialogAction-TDFOINOY.js +0 -65
  203. package/machineWsRunDispatch-VXYBYYKM.js +0 -144
  204. package/settingSlice-ZQZVUPKK.js +0 -204
  205. package/syncMapping-N6RGXGBW.js +0 -437
  206. package/tools-Q6IBBG33.js +0 -110
  207. package/{chunk-X7HHHIOZ.js → chunk-Y6ROFCZO.js} +0 -0
  208. package/{db-WK5MDZVN.js → db-VQSAOWAO.js} +1 -1
@@ -1,3 +1,8 @@
1
+ import {
2
+ DEVIN_CONNECT_URL,
3
+ parseSseDataLineJson,
4
+ parseSseDataLineObject
5
+ } from "./chunk-YK5MHOWM.js";
1
6
  import {
2
7
  applyChatCompletionDelta,
3
8
  applyResponsesToolEvent,
@@ -5,13 +10,15 @@ import {
5
10
  finalizeResponsesToolCalls,
6
11
  flushChatCompletionStream,
7
12
  providerHttpFailure,
8
- readSseFrames,
9
- streamSseDataValues,
10
13
  throwIfChatCompletionStreamFailed
11
- } from "./chunk-ZCYXJSTU.js";
14
+ } from "./chunk-54VPQTOA.js";
12
15
  import {
13
- DEVIN_CONNECT_URL
14
- } from "./chunk-Q5FJJ6XI.js";
16
+ readSseFrames
17
+ } from "./chunk-GMZLP5DV.js";
18
+ import {
19
+ evaluateFileWritePolicy,
20
+ summarizeToolArguments
21
+ } from "./chunk-CHOZJMJS.js";
15
22
  import {
16
23
  clipToolText,
17
24
  hasImageInRuntimeMessages,
@@ -20,13 +27,13 @@ import {
20
27
  resolveHistoricalToolContentCap,
21
28
  resolveToolOutputProfile,
22
29
  stripImagePartsFromMessages
23
- } from "./chunk-5H7KJX7J.js";
30
+ } from "./chunk-CFHWVEUJ.js";
24
31
  import {
25
32
  downgradeUnparsableToolCalls,
26
33
  hasParsableObjectArguments,
27
34
  repairTruncatedToolArguments,
28
35
  sanitizeForOutbound
29
- } from "./chunk-CQ3WA2NE.js";
36
+ } from "./chunk-NKWIS6G2.js";
30
37
  import {
31
38
  createThinkParserState,
32
39
  createToolCallAccumulator,
@@ -35,6 +42,7 @@ import {
35
42
  finalizeAccumulatedToolCalls
36
43
  } from "./chunk-N7J63Q6C.js";
37
44
  import {
45
+ DELETE_SAFETY_RED_LINE,
38
46
  TOOL_GUIDED_SECTION_ORDER,
39
47
  buildIdentityBlock,
40
48
  buildRuntimeGuidanceBlocks,
@@ -46,17 +54,18 @@ import {
46
54
  requiresBareImageUrl,
47
55
  resolveToolGuidedSections,
48
56
  toBareImageUrlMessages
49
- } from "./chunk-L5UXH3B2.js";
57
+ } from "./chunk-AFH5QC6R.js";
50
58
  import {
51
59
  convertMessagesToResponsesInput,
52
60
  extractReasoningFromResponseOutput,
53
61
  extractTextFromResponseOutput,
54
62
  extractToolCallsFromResponseOutput,
55
63
  getUsageRequestOptions,
64
+ shouldReplayReasoningContentForOutbound,
56
65
  shouldStripReasoningContentForOutbound,
57
66
  toOpenAiCompatibleMessages,
58
67
  toResponsesTools
59
- } from "./chunk-PUOC4YRO.js";
68
+ } from "./chunk-M7VLQGQP.js";
60
69
  import {
61
70
  BRIDGE_STREAM_MAX_CHARS,
62
71
  DEFAULT_READ_DIALOG_MAX_RESPONSE_CHARS,
@@ -64,22 +73,14 @@ import {
64
73
  applyToolSurfaceConstraints,
65
74
  buildDialogReadEnvelope,
66
75
  buildNoloWorkspaceCommandArgs,
76
+ buildNoloWorkspaceOpenAiTools,
67
77
  createStreamingDialogReadProjector,
68
78
  noloPositiveIntegerString,
69
79
  noloStringArg,
70
80
  parseNoloWorkspaceToolArguments,
71
81
  readTextStreamUpToChars,
72
82
  resolveNoloDialogInput
73
- } from "./chunk-IIVGDG4I.js";
74
- import {
75
- getAntigravityUserAgent,
76
- readAntigravityProjectId,
77
- resolveAntigravityCloudCodeBaseUrl
78
- } from "./chunk-UZGQNTVW.js";
79
- import {
80
- buildProviderAuthHeaders,
81
- buildProviderExecutionPlan
82
- } from "./chunk-6OZ36FXI.js";
83
+ } from "./chunk-SJKULPGX.js";
83
84
  import {
84
85
  buildDialogFallbackTitleFromUserInput
85
86
  } from "./chunk-2MBTWINN.js";
@@ -93,13 +94,12 @@ import {
93
94
  resolveEmptyAssistantFallbackMessage,
94
95
  resolveEmptyAssistantOutcome,
95
96
  resolveTruncatedReasoningTailLog
96
- } from "./chunk-4OEFYTWL.js";
97
+ } from "./chunk-L5WOICCV.js";
97
98
  import {
98
99
  COMPACTION_SUMMARY_SCHEMA_VERSION,
99
100
  COMPACTION_SUMMARY_SYSTEM_PROMPT,
100
101
  buildCompactionMetricsFromPlan,
101
102
  buildCompactionUserContent,
102
- clipCompactText,
103
103
  createProviderCallTimingTracker,
104
104
  finalizeProviderCallTiming,
105
105
  formatCompactionMetricsLog,
@@ -112,37 +112,36 @@ import {
112
112
  resolveCompressionTriggerRatio,
113
113
  truncateToolOutputForContext,
114
114
  withProviderCallTimingFields
115
- } from "./chunk-DNCEALKX.js";
115
+ } from "./chunk-IEJ5YDA4.js";
116
+ import {
117
+ clipCompactText
118
+ } from "./chunk-5MMAK75Q.js";
119
+ import {
120
+ asRecordOrEmpty
121
+ } from "./chunk-KX6YBIHG.js";
116
122
  import {
117
123
  estimateTokenCount
118
124
  } from "./chunk-PSCNXBYA.js";
119
125
  import {
120
126
  resolveNoloStateDir
121
- } from "./chunk-ONXOABJR.js";
122
- import {
123
- toErrorMessage
124
- } from "./chunk-HNMXYYJR.js";
127
+ } from "./chunk-HNGRBWYF.js";
125
128
  import {
126
129
  buildDialogSummaryLayer
127
- } from "./chunk-DVINR3PL.js";
130
+ } from "./chunk-HDLLCBRF.js";
128
131
  import {
129
132
  buildUserResponseLanguageContext
130
- } from "./chunk-2GRGFCF6.js";
133
+ } from "./chunk-4VJAC32I.js";
131
134
  import {
132
135
  canonicalizeToolName,
133
136
  canonicalizeToolNames
134
- } from "./chunk-O6OSWFZQ.js";
135
- import {
136
- asRecordOrEmpty
137
- } from "./chunk-KX6YBIHG.js";
137
+ } from "./chunk-IHPDK6UD.js";
138
138
  import {
139
- asTrimmedNonEmptyStringArray,
140
- dialogMessageKey,
141
- dialogMessageRange
142
- } from "./chunk-VMFTFOWF.js";
139
+ buildProviderAuthHeaders,
140
+ buildProviderExecutionPlan
141
+ } from "./chunk-27ONFIEY.js";
143
142
  import {
144
- asOptionalFiniteNumber
145
- } from "./chunk-WWC7I6TA.js";
143
+ toErrorMessage
144
+ } from "./chunk-HNMXYYJR.js";
146
145
  import {
147
146
  asTrimmedLowercaseString
148
147
  } from "./chunk-QSJ3HFEF.js";
@@ -150,11 +149,19 @@ import {
150
149
  isRecord
151
150
  } from "./chunk-KUG52IAP.js";
152
151
  import {
153
- asTrimmedString
154
- } from "./chunk-3EUW3WEO.js";
152
+ asTrimmedNonEmptyStringArray,
153
+ dialogMessageKey,
154
+ dialogMessageRange
155
+ } from "./chunk-OKGVNZTL.js";
156
+ import {
157
+ asOptionalFiniteNumber
158
+ } from "./chunk-WWC7I6TA.js";
155
159
  import {
156
160
  asOptionalPositiveFiniteNumber
157
161
  } from "./chunk-U6VC4MU4.js";
162
+ import {
163
+ asTrimmedString
164
+ } from "./chunk-3EUW3WEO.js";
158
165
  import {
159
166
  normalizeServerOrigin
160
167
  } from "./chunk-CF7PE763.js";
@@ -11900,6 +11907,7 @@ var toQueue = /* @__PURE__ */ dual(2, (self, options) => toQueueArray(self.chann
11900
11907
  var runIntoQueue = /* @__PURE__ */ dual(2, (self, queue) => runIntoQueueArray(self.channel, queue));
11901
11908
 
11902
11909
  // packages/agent-runtime/abortableKernel.ts
11910
+ var IDLE_WATCHDOG_SLICES = 20;
11903
11911
  function runAbortableWithTimeout(args2) {
11904
11912
  const taskEffect = Effect_exports.callback(
11905
11913
  (resume) => {
@@ -11935,12 +11943,26 @@ function runAbortableWithTimeout(args2) {
11935
11943
  raced = Effect_exports.raceFirst(taskEffect, abortEffect);
11936
11944
  }
11937
11945
  if (typeof args2.timeoutMs === "number") {
11938
- const inner = raced;
11946
+ const outer = raced;
11939
11947
  const timeoutMs = args2.timeoutMs;
11940
- raced = Effect_exports.gen(function* () {
11941
- const outcome = yield* Effect_exports.timeoutOption(inner, Duration_exports.millis(timeoutMs));
11942
- return Option_exports.isNone(outcome) ? { kind: "timeout" } : outcome.value;
11948
+ const activityVersion = args2.activityVersion;
11949
+ const idleWatchdog = Effect_exports.gen(function* () {
11950
+ const sliceCount = activityVersion ? IDLE_WATCHDOG_SLICES : 1;
11951
+ const baseSliceMs = Math.max(1, Math.floor(timeoutMs / sliceCount));
11952
+ let idleMs = 0;
11953
+ for (; ; ) {
11954
+ const versionAtSliceStart = activityVersion?.();
11955
+ const sliceMs = Math.max(1, Math.min(timeoutMs - idleMs, baseSliceMs));
11956
+ yield* Effect_exports.sleep(Duration_exports.millis(sliceMs));
11957
+ idleMs += sliceMs;
11958
+ if (activityVersion && activityVersion() !== versionAtSliceStart) {
11959
+ idleMs = 0;
11960
+ continue;
11961
+ }
11962
+ if (idleMs >= timeoutMs) return { kind: "timeout" };
11963
+ }
11943
11964
  });
11965
+ raced = Effect_exports.raceFirst(outer, idleWatchdog);
11944
11966
  }
11945
11967
  return args2.runtime ? args2.runtime.runPromise(raced) : Effect_exports.runPromise(raced);
11946
11968
  }
@@ -12039,26 +12061,6 @@ function createLocalLoopObservationBoundary(initialCallbacks) {
12039
12061
  };
12040
12062
  }
12041
12063
 
12042
- // packages/agent-runtime/fileWritePolicy.ts
12043
- function evaluateFileWritePolicy(args2) {
12044
- if (args2.sessionApproved) return { permissionDecision: "allow" };
12045
- return {
12046
- permissionDecision: "ask",
12047
- permissionRequest: {
12048
- id: "permission-file-write-session",
12049
- tool: args2.tool,
12050
- action: "file_write",
12051
- title: "\u786E\u8BA4\u5199\u5165\u6587\u4EF6",
12052
- body: `\u672C\u4F1A\u8BDD\u9996\u6B21\u5199\u5165\u6587\u4EF6\uFF1A${args2.path}\u3002\u6279\u51C6\u540E\u672C\u4F1A\u8BDD\u540E\u7EED\u5199\u5165\u4E0D\u518D\u9010\u6B21\u786E\u8BA4\u3002`,
12053
- command: args2.path,
12054
- suggestedRule: {
12055
- scope: "session",
12056
- pattern: { capability: "file_write", target: "session" }
12057
- }
12058
- }
12059
- };
12060
- }
12061
-
12062
12064
  // packages/agent-runtime/toolCallPairing.ts
12063
12065
  function readToolCallId(message) {
12064
12066
  if (typeof message.tool_call_id === "string" && message.tool_call_id) {
@@ -12160,50 +12162,6 @@ function sanitizeToolCallPairing(messages) {
12160
12162
  return output;
12161
12163
  }
12162
12164
 
12163
- // packages/agent-runtime/parseToolArguments.ts
12164
- function parseToolArgumentsJson(raw) {
12165
- if (!raw?.trim()) return {};
12166
- try {
12167
- return asRecordOrEmpty(JSON.parse(raw));
12168
- } catch {
12169
- return {};
12170
- }
12171
- }
12172
-
12173
- // packages/agent-runtime/summarizeToolArguments.ts
12174
- function clip(value, max4 = 240) {
12175
- return clipCompactText(value, max4);
12176
- }
12177
- function summarizeToolArguments(toolName, rawArgs) {
12178
- const args2 = parseToolArgumentsJson(rawArgs);
12179
- const pick = (...keys3) => {
12180
- for (const key of keys3) {
12181
- const value = asOptionalTrimmedString(args2[key]);
12182
- if (value) return value;
12183
- }
12184
- return "";
12185
- };
12186
- const command = pick("command", "cmd", "runCommand", "executeCommand", "bash");
12187
- if (command) return clip(command);
12188
- const query = pick("query", "pattern", "search", "q");
12189
- if (query) return clip(query);
12190
- const filePath = pick("filePath", "file_path", "path", "filename", "file");
12191
- if (filePath) return clip(filePath);
12192
- const keys2 = Object.keys(args2);
12193
- if (keys2.length === 0) return "";
12194
- return clip(
12195
- keys2.slice(0, 6).map((key) => {
12196
- const value = args2[key];
12197
- if (typeof value === "string") return `${key}=${clip(value, 80)}`;
12198
- if (typeof value === "number" || typeof value === "boolean") {
12199
- return `${key}=${String(value)}`;
12200
- }
12201
- if (Array.isArray(value)) return `${key}[${value.length}]`;
12202
- return `${key}=${value === null ? "null" : typeof value}`;
12203
- }).join(" ")
12204
- );
12205
- }
12206
-
12207
12165
  // packages/agent-runtime/progressGuard.ts
12208
12166
  var DEFAULT_MAX_CONSECUTIVE_IDENTICAL_ROUNDS = 5;
12209
12167
  var DEFAULT_MAX_CONSECUTIVE_STAGNANT_TOOL_ROUNDS = 8;
@@ -12559,7 +12517,7 @@ function formatToolOverflowMarker(args2) {
12559
12517
  }
12560
12518
 
12561
12519
  // packages/agent-runtime/localAutoCompaction.ts
12562
- import { createHash as createHash2 } from "node:crypto";
12520
+ import { createHash as createHash2, randomUUID } from "node:crypto";
12563
12521
  function toPlanCompressionMessages(history) {
12564
12522
  return history.map((message, index) => ({
12565
12523
  id: `local-${index}`,
@@ -12627,6 +12585,13 @@ function isColdResume(history, nowMs) {
12627
12585
  }
12628
12586
  async function maybeAutoCompactLocalHistory(args2) {
12629
12587
  const { adapter, dialogId, history } = args2;
12588
+ const emitPhase = (phase) => {
12589
+ try {
12590
+ args2.onPhase?.(phase);
12591
+ } catch {
12592
+ }
12593
+ };
12594
+ emitPhase({ kind: "validating-context" });
12630
12595
  const unchanged = (decision) => ({
12631
12596
  history,
12632
12597
  compressed: false,
@@ -12722,8 +12687,46 @@ async function maybeAutoCompactLocalHistory(args2) {
12722
12687
  if (!plan.shouldCompress) {
12723
12688
  return projectExisting();
12724
12689
  }
12690
+ emitPhase({ kind: "compaction-start" });
12691
+ const compactionAttemptId = randomUUID();
12692
+ let compactionT0 = Date.now();
12693
+ let compactionInputTokens;
12694
+ let compactionPrevSummaryTokens;
12695
+ let compactionInputSource;
12696
+ let compactionPrevSummarySource;
12697
+ let primaryElapsedMs;
12698
+ let saveElapsedMs;
12699
+ const logCompactionTiming = (outcome, fields) => {
12700
+ if (compactionInputTokens === void 0 && compactionInputSource !== void 0) {
12701
+ compactionInputTokens = estimateTokenCount(compactionInputSource);
12702
+ }
12703
+ if (compactionPrevSummaryTokens === void 0 && compactionPrevSummarySource !== void 0) {
12704
+ compactionPrevSummaryTokens = estimateTokenCount(compactionPrevSummarySource);
12705
+ }
12706
+ console.log(
12707
+ `[compaction-timing] ${JSON.stringify({
12708
+ attemptId: compactionAttemptId,
12709
+ model: fields.model ?? args2.model ?? null,
12710
+ inputTokens: compactionInputTokens ?? null,
12711
+ prevSummaryTokens: compactionPrevSummaryTokens ?? null,
12712
+ outputChars: fields.outputChars ?? null,
12713
+ elapsedMs: Date.now() - compactionT0,
12714
+ primaryElapsedMs: primaryElapsedMs ?? null,
12715
+ // main 无 fallback 通道,保留该字段以兼容日志 schema。
12716
+ fallbackElapsedMs: null,
12717
+ saveElapsedMs: saveElapsedMs ?? null,
12718
+ outcome
12719
+ })}`
12720
+ );
12721
+ };
12725
12722
  try {
12723
+ if (args2.abortSignal?.aborted) {
12724
+ throw Object.assign(new Error("auto-compaction aborted"), { compactionReason: "aborted" });
12725
+ }
12726
12726
  const provider = await args2.resolveProvider();
12727
+ if (args2.abortSignal?.aborted) {
12728
+ throw Object.assign(new Error("auto-compaction aborted"), { compactionReason: "aborted" });
12729
+ }
12727
12730
  const msgsToCompress = plan.msgsToCompress;
12728
12731
  const messagesText = formatMessagesForSummaryWithTruncation(msgsToCompress);
12729
12732
  const fileOpsText = formatFileOperationsFromMessages(
@@ -12735,24 +12738,71 @@ async function maybeAutoCompactLocalHistory(args2) {
12735
12738
  messagesText,
12736
12739
  fileOpsText
12737
12740
  });
12738
- const result4 = await provider.complete([
12739
- { role: "system", content: COMPACTION_SUMMARY_SYSTEM_PROMPT },
12740
- { role: "user", content: promptContent }
12741
- ]);
12741
+ emitPhase({ kind: "waiting-provider" });
12742
+ compactionInputSource = messagesText;
12743
+ compactionPrevSummarySource = existingSummary;
12744
+ compactionT0 = Date.now();
12745
+ const timeoutMs = typeof args2.timeoutMs === "number" && Number.isFinite(args2.timeoutMs) && args2.timeoutMs > 0 ? args2.timeoutMs : void 0;
12746
+ const requestController = new AbortController();
12747
+ let timeoutHandle;
12748
+ let abortListener;
12749
+ const request3 = provider.complete(
12750
+ [{ role: "system", content: COMPACTION_SUMMARY_SYSTEM_PROMPT }, { role: "user", content: promptContent }],
12751
+ {
12752
+ signal: requestController.signal,
12753
+ ...timeoutMs ? { timeoutMs } : {},
12754
+ // 仅日志用的 attempt id:让 compaction 埋点与 transport 诊断日志 /
12755
+ // 服务端 x-request-id 可对上,用于关联请求级证据。不进 prompt/摘要/持久化。
12756
+ requestId: compactionAttemptId
12757
+ }
12758
+ ).then(
12759
+ (value) => ({ kind: "done", value }),
12760
+ (error) => ({ kind: "provider-error", error })
12761
+ );
12762
+ const guards = [];
12763
+ if (timeoutMs) guards.push(new Promise((resolve5) => {
12764
+ timeoutHandle = setTimeout(() => {
12765
+ requestController.abort();
12766
+ resolve5({ kind: "timeout" });
12767
+ }, timeoutMs);
12768
+ }));
12769
+ if (args2.abortSignal) guards.push(new Promise((resolve5) => {
12770
+ abortListener = () => {
12771
+ requestController.abort(args2.abortSignal?.reason);
12772
+ resolve5({ kind: "aborted" });
12773
+ };
12774
+ args2.abortSignal?.addEventListener("abort", abortListener, { once: true });
12775
+ }));
12776
+ const outcome = guards.length ? await Promise.race([request3, ...guards]) : await request3;
12777
+ primaryElapsedMs = Date.now() - compactionT0;
12778
+ if (timeoutHandle) clearTimeout(timeoutHandle);
12779
+ if (abortListener) args2.abortSignal?.removeEventListener("abort", abortListener);
12780
+ if (outcome.kind !== "done") {
12781
+ const reason = outcome.kind === "timeout" ? "timeout" : outcome.kind === "aborted" ? "aborted" : "provider-error";
12782
+ const detail = outcome.kind === "provider-error" ? outcome.error instanceof Error ? outcome.error.message : String(outcome.error) : reason === "timeout" ? `auto-compaction timed out after ${timeoutMs}ms` : "auto-compaction aborted";
12783
+ throw Object.assign(new Error(detail), { compactionReason: reason });
12784
+ }
12785
+ const result4 = outcome.value;
12742
12786
  const newSummary = typeof result4.content === "string" ? result4.content.trim() : "";
12743
12787
  if (!newSummary) {
12744
12788
  console.warn(
12745
12789
  "[localLoop] auto-compaction produced empty summary; keeping prior projection"
12746
12790
  );
12791
+ logCompactionTiming("empty", {});
12792
+ const failureReason = "provider-error";
12793
+ emitPhase({ kind: "compaction-failed", reason: failureReason });
12747
12794
  return {
12748
12795
  ...projectExisting(),
12749
12796
  failureMessage: "summary model returned empty content",
12797
+ failureReason,
12750
12798
  decision: {
12751
12799
  outcome: "failed",
12800
+ reason: failureReason,
12752
12801
  detail: "summary model returned empty content"
12753
12802
  }
12754
12803
  };
12755
12804
  }
12805
+ const saveT0 = Date.now();
12756
12806
  await adapter.saveDialogSummary({
12757
12807
  dialogId,
12758
12808
  summary: newSummary,
@@ -12766,6 +12816,7 @@ async function maybeAutoCompactLocalHistory(args2) {
12766
12816
  })(),
12767
12817
  schemaVersion: COMPACTION_SUMMARY_SCHEMA_VERSION
12768
12818
  });
12819
+ saveElapsedMs = Date.now() - saveT0;
12769
12820
  const metricsReason = invalidSummary ? "invalid_summary" : coldResume ? "cold_resume" : "context_budget";
12770
12821
  const metrics = buildCompactionMetricsFromPlan({
12771
12822
  reason: metricsReason,
@@ -12775,6 +12826,11 @@ async function maybeAutoCompactLocalHistory(args2) {
12775
12826
  summaryUsage: result4.usage
12776
12827
  });
12777
12828
  console.log(formatCompactionMetricsLog(metrics));
12829
+ logCompactionTiming("success", {
12830
+ model: result4?.model ?? args2.model ?? null,
12831
+ outputChars: newSummary.length
12832
+ });
12833
+ emitPhase({ kind: "compaction-end" });
12778
12834
  return {
12779
12835
  history: projectHistoryWithSummary({
12780
12836
  history,
@@ -12802,10 +12858,14 @@ async function maybeAutoCompactLocalHistory(args2) {
12802
12858
  } catch (error) {
12803
12859
  console.warn("[localLoop] auto-compaction failed:", error);
12804
12860
  const failureMessage = error instanceof Error && error.message ? error.message.slice(0, 200) : String(error).slice(0, 200);
12861
+ const failureReason = error instanceof Error && "compactionReason" in error ? error.compactionReason : "provider-error";
12862
+ logCompactionTiming(failureReason, {});
12863
+ emitPhase({ kind: "compaction-failed", reason: failureReason });
12805
12864
  return {
12806
12865
  ...projectExisting(),
12807
12866
  failureMessage,
12808
- decision: { outcome: "failed", detail: failureMessage }
12867
+ failureReason,
12868
+ decision: { outcome: "failed", reason: failureReason, detail: failureMessage }
12809
12869
  };
12810
12870
  }
12811
12871
  }
@@ -12862,6 +12922,8 @@ function emitLoopEvent(boundary, event, bridge) {
12862
12922
  boundary.emit(bridge ? { event, bridge } : { event });
12863
12923
  }
12864
12924
  var LLM_REQUEST_TIMEOUT = "LLM_REQUEST_TIMEOUT";
12925
+ var DEFAULT_LLM_REQUEST_TIMEOUT_MS = 10 * 60 * 1e3;
12926
+ var DEFAULT_COMPACTION_TIMEOUT_MS = 60 * 1e3;
12865
12927
  var LOCAL_TURN_ABORTED_CODE = "LOCAL_TURN_ABORTED";
12866
12928
  function buildAbortedError() {
12867
12929
  const error = new Error("local agent turn aborted by user");
@@ -12889,8 +12951,11 @@ async function runAbortableToolTask(input, task, pendingToolName) {
12889
12951
  }
12890
12952
  function resolveLlmRequestTimeoutMs(input) {
12891
12953
  const raw = input.llmRequestTimeoutMs ?? input.timeoutMs;
12892
- if (typeof raw !== "number" || !Number.isFinite(raw) || raw <= 0) return void 0;
12893
- return raw;
12954
+ return typeof raw === "number" && Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_LLM_REQUEST_TIMEOUT_MS;
12955
+ }
12956
+ function resolveCompactionTimeoutMs(input) {
12957
+ const raw = input.compactionTimeoutMs;
12958
+ return typeof raw === "number" && Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_COMPACTION_TIMEOUT_MS;
12894
12959
  }
12895
12960
  async function runCompleteWithTimeout(args2) {
12896
12961
  const { provider, messages, options, timeoutMs, round, input, boundary, context: context3, providerName, model } = args2;
@@ -12913,6 +12978,7 @@ async function runCompleteWithTimeout(args2) {
12913
12978
  const outcome = await runAbortableWithTimeout({
12914
12979
  task: complete,
12915
12980
  timeoutMs: typeof timeoutMs === "number" ? timeoutMs : void 0,
12981
+ activityVersion: args2.activityVersion,
12916
12982
  abortSignal: input.abortSignal,
12917
12983
  runtime: input.effectRuntime
12918
12984
  });
@@ -12923,7 +12989,7 @@ async function runCompleteWithTimeout(args2) {
12923
12989
  }
12924
12990
  if (outcome.kind === "timeout") {
12925
12991
  const timeoutError = new Error(
12926
- `LLM request timed out after ${timeoutMs}ms (round ${round})`
12992
+ `LLM request timed out after ${timeoutMs}ms without new output (idle window, round ${round})`
12927
12993
  );
12928
12994
  timeoutError.code = LLM_REQUEST_TIMEOUT;
12929
12995
  throw timeoutError;
@@ -12967,16 +13033,16 @@ async function runCompleteWithTimeout(args2) {
12967
13033
  });
12968
13034
  }
12969
13035
  }
12970
- function clip2(value, max4 = 240) {
13036
+ function clip(value, max4 = 240) {
12971
13037
  return clipCompactText(value, max4);
12972
13038
  }
12973
13039
  function projectSafeToolObservationMetadata(metadata) {
12974
13040
  if (!metadata || typeof metadata !== "object") return void 0;
12975
13041
  const safe = {};
12976
13042
  if (typeof metadata.exitCode === "number") safe.exitCode = metadata.exitCode;
12977
- if (typeof metadata.actionGate === "string") safe.actionGate = clip2(metadata.actionGate, 240);
12978
- if (typeof metadata.command === "string") safe.command = clip2(metadata.command, 240);
12979
- if (typeof metadata.path === "string") safe.path = clip2(metadata.path, 240);
13043
+ if (typeof metadata.actionGate === "string") safe.actionGate = clip(metadata.actionGate, 240);
13044
+ if (typeof metadata.command === "string") safe.command = clip(metadata.command, 240);
13045
+ if (typeof metadata.path === "string") safe.path = clip(metadata.path, 240);
12980
13046
  if (typeof metadata.truncated === "boolean") safe.truncated = metadata.truncated;
12981
13047
  if (typeof metadata.byteCount === "number") safe.byteCount = metadata.byteCount;
12982
13048
  if (typeof metadata.lineCount === "number") safe.lineCount = metadata.lineCount;
@@ -12992,7 +13058,7 @@ function summarizeToolResult(content, metadata) {
12992
13058
  const lines = trimmed.split(/\r?\n/).length;
12993
13059
  parts2.push(`${lines} line${lines === 1 ? "" : "s"}`);
12994
13060
  parts2.push(`${trimmed.length} chars`);
12995
- const tail = clip2(trimmed.slice(-160), 160);
13061
+ const tail = clip(trimmed.slice(-160), 160);
12996
13062
  if (tail) parts2.push(`tail="${tail}"`);
12997
13063
  } else {
12998
13064
  parts2.push("empty");
@@ -13604,6 +13670,9 @@ async function runLocalAgentTurn(input) {
13604
13670
  input.contextBlockScopes
13605
13671
  );
13606
13672
  const mergedContextBlockScopes = [
13673
+ // 删除安全红线:系统层最高优先级约束,位于所有块之前(含身份信息块),
13674
+ // owner 要求常驻「根本提示词」的最顶部(2026-09-26 批量误删事故)。
13675
+ { content: DELETE_SAFETY_RED_LINE, cacheScope: "session" },
13607
13676
  { content: identityBlock, cacheScope: "session" },
13608
13677
  ...guidanceScopes,
13609
13678
  ...callerScopes
@@ -13636,6 +13705,16 @@ async function runLocalAgentTurn(input) {
13636
13705
  }
13637
13706
  }
13638
13707
  let compactionUsage;
13708
+ const emitCompactionPhase = (phase, scope3) => {
13709
+ const atMs = Date.now();
13710
+ if (phase.kind === "validating-context") emitLoopEvent(observationBoundary, { kind: "turn-phase", phase: "validating-context", atMs, compactionScope: scope3 });
13711
+ else if (phase.kind === "compaction-start") {
13712
+ emitLoopEvent(observationBoundary, { kind: "turn-phase", phase: "compacting", atMs, compactionScope: scope3 });
13713
+ emitLoopEvent(observationBoundary, { kind: "compaction-start", atMs, scope: scope3 });
13714
+ } else if (phase.kind === "waiting-provider") emitLoopEvent(observationBoundary, { kind: "turn-phase", phase: "waiting-provider", atMs, compactionScope: scope3 });
13715
+ else if (phase.kind === "compaction-end") emitLoopEvent(observationBoundary, { kind: "compaction-end", atMs, scope: scope3 });
13716
+ else emitLoopEvent(observationBoundary, { kind: "compaction-failed", atMs, scope: scope3, reason: phase.reason });
13717
+ };
13639
13718
  const emitCompactionObservation = (compacted) => {
13640
13719
  const decision = compacted.decision;
13641
13720
  if (decision?.outcome === "skipped") {
@@ -13669,7 +13748,10 @@ async function runLocalAgentTurn(input) {
13669
13748
  model: agentConfig.model,
13670
13749
  resolveProvider: resolveProviderOnce,
13671
13750
  contextWindow,
13672
- realContextUsagePercent
13751
+ realContextUsagePercent,
13752
+ abortSignal: input.abortSignal,
13753
+ timeoutMs: resolveCompactionTimeoutMs(input),
13754
+ onPhase: (phase) => emitCompactionPhase(phase, "initial")
13673
13755
  });
13674
13756
  history = compacted.history;
13675
13757
  compactionUsage = compacted.usage;
@@ -13775,7 +13857,10 @@ async function runLocalAgentTurn(input) {
13775
13857
  model: agentConfig.model,
13776
13858
  resolveProvider: resolveProviderOnce,
13777
13859
  contextWindow,
13778
- ...ratio !== void 0 ? { realContextUsagePercent: ratio } : {}
13860
+ ...ratio !== void 0 ? { realContextUsagePercent: ratio } : {},
13861
+ abortSignal: input.abortSignal,
13862
+ timeoutMs: resolveCompactionTimeoutMs(input),
13863
+ onPhase: (phase) => emitCompactionPhase(phase, "in-loop")
13779
13864
  });
13780
13865
  if (compacted.usage) {
13781
13866
  compactionUsage = addOutOfBandUsage(compactionUsage, compacted.usage);
@@ -13845,11 +13930,12 @@ async function runLocalAgentTurn(input) {
13845
13930
  const shouldPassToolEvents = Boolean(
13846
13931
  input.onToolEvent || input.onObservationEvent || input.observationBoundary
13847
13932
  );
13933
+ let llmStreamActivity = 0;
13848
13934
  result4 = await runCompleteWithTimeout({
13849
13935
  provider,
13850
13936
  messages: requestMessages,
13851
13937
  options: {
13852
- ...typeof input.timeoutMs === "number" ? { timeoutMs: input.timeoutMs } : {},
13938
+ timeoutMs: resolveLlmRequestTimeoutMs(input),
13853
13939
  // 用户取消信号穿进 provider options:providerStreamRetry 据此在
13854
13940
  // 取消后跳过重试,provider 分支(如 antigravity/openai-compatible)
13855
13941
  // 也可把 fetch 绑定到同一信号,实现真正的传输层取消。
@@ -13858,6 +13944,7 @@ async function runLocalAgentTurn(input) {
13858
13944
  // 归到具体对话而不是 chat-proxy 兜底桶。新对话首轮 id 尚未分配。
13859
13945
  ...input.continueDialogId ? { dialogId: input.continueDialogId } : {},
13860
13946
  ...shouldStreamDeltas ? { onTextDelta: (chunk) => {
13947
+ llmStreamActivity += 1;
13861
13948
  partialContent += chunk;
13862
13949
  observationBoundary.emit({
13863
13950
  kind: "text-delta",
@@ -13867,6 +13954,7 @@ async function runLocalAgentTurn(input) {
13867
13954
  });
13868
13955
  } } : {},
13869
13956
  ...shouldStreamReasoning ? { onReasoningDelta: (chunk) => {
13957
+ llmStreamActivity += 1;
13870
13958
  observationBoundary.emit({
13871
13959
  kind: "reasoning-delta",
13872
13960
  chunk,
@@ -13875,6 +13963,7 @@ async function runLocalAgentTurn(input) {
13875
13963
  });
13876
13964
  } } : {},
13877
13965
  ...shouldPassToolEvents ? { onToolEvent: (event) => {
13966
+ llmStreamActivity += 1;
13878
13967
  observationBoundary.emit({
13879
13968
  kind: "tool-event",
13880
13969
  event,
@@ -13885,6 +13974,7 @@ async function runLocalAgentTurn(input) {
13885
13974
  ...shouldPassToolEvents ? { toolEventRound: round } : {}
13886
13975
  },
13887
13976
  timeoutMs: resolveLlmRequestTimeoutMs(input),
13977
+ activityVersion: () => llmStreamActivity,
13888
13978
  round,
13889
13979
  input,
13890
13980
  boundary: observationBoundary,
@@ -14356,33 +14446,6 @@ ${reasoningTailLog}
14356
14446
  }
14357
14447
  }
14358
14448
 
14359
- // packages/agent-runtime/kimiUserAgent.ts
14360
- var KIMI_CODE_CLI_VERSION = "0.29.2";
14361
- var KIMI_CODE_USER_AGENT = `kimi-code-cli/${KIMI_CODE_CLI_VERSION} (nolo)`;
14362
- function isKimiEndpoint(url) {
14363
- return url.includes("api.kimi.com");
14364
- }
14365
- function kimiIdentityHeaders(url) {
14366
- return isKimiEndpoint(url) ? { "User-Agent": KIMI_CODE_USER_AGENT } : {};
14367
- }
14368
-
14369
- // packages/agent-runtime/sseDataLine.ts
14370
- function parseSseDataLineJson(line) {
14371
- const trimmed = line.trim();
14372
- if (!trimmed.startsWith("data:")) return null;
14373
- const payload = trimmed.slice(5).trim();
14374
- if (!payload || payload === "[DONE]") return null;
14375
- try {
14376
- return JSON.parse(payload);
14377
- } catch {
14378
- return null;
14379
- }
14380
- }
14381
- function parseSseDataLineObject(line) {
14382
- const parsed = parseSseDataLineJson(line);
14383
- return parsed && typeof parsed === "object" ? parsed : null;
14384
- }
14385
-
14386
14449
  // packages/integrations/openai/responsesRequestBody.ts
14387
14450
  function buildResponsesRequestBody(body, model) {
14388
14451
  const tools = Array.isArray(body.tools) ? toResponsesTools(body.tools) : void 0;
@@ -14493,8 +14556,31 @@ function createResponsesStreamCollector(callbacks) {
14493
14556
  };
14494
14557
  const finalize2 = () => {
14495
14558
  let finalToolCalls = finalizeResponsesToolCalls(toolCallsAcc);
14496
- if (finalToolCalls.length === 0 && completedResponse) {
14497
- finalToolCalls = extractToolCallsFromResponseOutput(completedResponse);
14559
+ if (completedResponse) {
14560
+ const completedToolCalls = extractToolCallsFromResponseOutput(completedResponse);
14561
+ const completedById = new Map(completedToolCalls.map((call) => [call.id, call]));
14562
+ finalToolCalls = finalToolCalls.map((call) => {
14563
+ const completed = completedById.get(call.id);
14564
+ if (!completed) return call;
14565
+ let accumulatedParses = false;
14566
+ let completedParses = false;
14567
+ try {
14568
+ JSON.parse(call.function.arguments);
14569
+ accumulatedParses = true;
14570
+ } catch {
14571
+ }
14572
+ try {
14573
+ JSON.parse(completed.function.arguments);
14574
+ completedParses = true;
14575
+ } catch {
14576
+ return call;
14577
+ }
14578
+ if (completedParses && (!accumulatedParses || completed.function.arguments.startsWith(call.function.arguments))) {
14579
+ return { ...call, function: { ...call.function, arguments: completed.function.arguments } };
14580
+ }
14581
+ return call;
14582
+ });
14583
+ if (finalToolCalls.length === 0) finalToolCalls = completedToolCalls;
14498
14584
  }
14499
14585
  if (!content && completedResponse) {
14500
14586
  content = extractTextFromResponseOutput(completedResponse);
@@ -14561,6 +14647,7 @@ async function readResponsesSseCompletion(args2) {
14561
14647
  }
14562
14648
 
14563
14649
  // packages/agent-runtime/openAiCompatibleProvider.ts
14650
+ var REASONING_REPLAY_PLACEHOLDER = "(reasoning not captured for this turn)";
14564
14651
  function isOpenAiResponsesEndpoint(endpoint) {
14565
14652
  return endpoint.includes("/responses");
14566
14653
  }
@@ -14574,9 +14661,14 @@ function buildOpenAiCompatibleChatCompletionRequest(args2) {
14574
14661
  args2.providerConfig.provider,
14575
14662
  args2.providerConfig.model
14576
14663
  );
14664
+ const shouldReplayReasoning = shouldReplayReasoningContentForOutbound(
14665
+ args2.providerConfig.provider,
14666
+ args2.providerConfig.model
14667
+ );
14577
14668
  const sanitizedMessages = sanitizeForOutbound(args2.messages, args2.tools);
14578
14669
  const messages = toOpenAiCompatibleMessages(sanitizedMessages, {
14579
- stripReasoningContent: shouldStripReasoning
14670
+ stripReasoningContent: shouldStripReasoning,
14671
+ ...shouldReplayReasoning && !shouldStripReasoning ? { replayReasoningPlaceholder: REASONING_REPLAY_PLACEHOLDER } : {}
14580
14672
  });
14581
14673
  const rawBody = {
14582
14674
  model: args2.providerConfig.model,
@@ -14605,9 +14697,9 @@ function buildOpenAiCompatibleChatCompletionRequest(args2) {
14605
14697
  ...buildProviderAuthHeaders({
14606
14698
  endpoint,
14607
14699
  apiKey: args2.providerConfig.apiKey,
14608
- apiKeyHeader: args2.providerConfig.apiKeyHeader
14609
- }),
14610
- ...kimiIdentityHeaders(endpoint)
14700
+ apiKeyHeader: args2.providerConfig.apiKeyHeader,
14701
+ sessionId: args2.sessionId
14702
+ })
14611
14703
  };
14612
14704
  return {
14613
14705
  url: endpoint,
@@ -14710,7 +14802,8 @@ async function executeOpenAiCompatibleChatCompletion(args2) {
14710
14802
  providerConfig: apiKey2 === args2.providerConfig.apiKey ? args2.providerConfig : { ...args2.providerConfig, apiKey: apiKey2 },
14711
14803
  messages: args2.messages,
14712
14804
  tools: args2.tools,
14713
- stream: args2.stream
14805
+ stream: args2.stream,
14806
+ sessionId: args2.sessionId
14714
14807
  });
14715
14808
  return args2.fetchImpl(request3.url, {
14716
14809
  ...request3.init,
@@ -15652,132 +15745,6 @@ async function evaluateCapabilityPolicy(capability, normalizedInput, ctx) {
15652
15745
  }
15653
15746
  }
15654
15747
 
15655
- // packages/agent-runtime/localToolPolicy.ts
15656
- var NEVER_LOCAL_TOOLS = /* @__PURE__ */ new Set([
15657
- "deleteSpaces",
15658
- "updateAgent",
15659
- "updateSelf",
15660
- "createAgent"
15661
- ]);
15662
- var DEFAULT_LOCAL_TOOLS = /* @__PURE__ */ new Set([
15663
- "readFile",
15664
- "writeFile",
15665
- "editFile",
15666
- "globFiles",
15667
- "execShell",
15668
- "launchProcess",
15669
- "listProcesses",
15670
- // ProcessTask follow-up tools. Same tier as listProcesses: they observe/stop
15671
- // tasks the agent itself already spawned, so their blast radius is strictly
15672
- // narrower than launchProcess (which is default-enabled). Model-facing
15673
- // follow-ups are taskWait/taskStop only — lifecycle history (taskLogs) stays
15674
- // runtime-internal.
15675
- "taskWait",
15676
- "taskStop",
15677
- // Scheduling tools: forking/observing sub-agents is a declared capability
15678
- // of capable agents, and the blast radius is strictly narrower than
15679
- // execShell (already default-enabled). Requiring an env allowlist for these
15680
- // while execShell is open by default inverted the risk model and forced every
15681
- // new local user to hand-edit .env files before sub-agent tasks worked.
15682
- "startAgentRun",
15683
- "controlAgentRun"
15684
- ]);
15685
- function parseToolAllowlist(value) {
15686
- return (value ?? "").split(",").flatMap((item) => {
15687
- const trimmed = item.trim();
15688
- return trimmed ? [trimmed] : [];
15689
- });
15690
- }
15691
- function normalizeLocalToolName(toolName) {
15692
- return canonicalizeToolName(
15693
- String(toolName ?? "").replace(/^functions\./, "").trim()
15694
- );
15695
- }
15696
- function isRestrictedLocalToolMode(env) {
15697
- return env.NOLO_LOCAL_TOOL_MODE === "restricted";
15698
- }
15699
- function resolveLocalToolPolicy(args2) {
15700
- const toolName = normalizeLocalToolName(args2.toolName);
15701
- const restrictedMode = isRestrictedLocalToolMode(args2.env);
15702
- if (!toolName) {
15703
- return {
15704
- allowed: false,
15705
- toolName,
15706
- reason: "Tool name is required."
15707
- };
15708
- }
15709
- if (NEVER_LOCAL_TOOLS.has(toolName)) {
15710
- return {
15711
- allowed: false,
15712
- toolName,
15713
- reason: `${toolName} is blocked by the local runtime safety policy.`
15714
- };
15715
- }
15716
- const allowedByEnv = new Set(parseToolAllowlist(args2.env.NOLO_LOCAL_ALLOWED_TOOLS));
15717
- const agentTools = new Set(
15718
- (args2.agentToolNames ?? []).flatMap((item) => {
15719
- const normalized = normalizeLocalToolName(item);
15720
- return normalized ? [normalized] : [];
15721
- })
15722
- );
15723
- if (!restrictedMode && agentTools.has(toolName)) {
15724
- return { allowed: true, toolName };
15725
- }
15726
- if (DEFAULT_LOCAL_TOOLS.has(toolName) && agentTools.has(toolName)) {
15727
- return { allowed: true, toolName };
15728
- }
15729
- if (allowedByEnv.has(toolName) && agentTools.has(toolName)) {
15730
- return { allowed: true, toolName };
15731
- }
15732
- return {
15733
- allowed: false,
15734
- toolName,
15735
- reason: `${toolName} is not enabled for local runtime runs. The agent must declare the tool in its manifest; in restricted mode (NOLO_LOCAL_TOOL_MODE=restricted) non-default tools also require adding to NOLO_LOCAL_ALLOWED_TOOLS (e.g. NOLO_LOCAL_ALLOWED_TOOLS=someCustomTool) and restarting the session so the env var loads.`
15736
- };
15737
- }
15738
- async function executeLocalToolWithPolicy(args2) {
15739
- if (args2.runToolNames !== void 0 && !args2.runToolNames.includes(normalizeLocalToolName(args2.call.name))) {
15740
- throw new Error(`${args2.call.name} is outside the final run tool surface.`);
15741
- }
15742
- const decision = resolveLocalToolPolicy({
15743
- env: args2.env,
15744
- agentToolNames: args2.agentToolNames,
15745
- toolName: args2.call.name
15746
- });
15747
- if (!decision.allowed) throw new Error(decision.reason);
15748
- const executor = args2.executors?.[decision.toolName];
15749
- if (!executor) {
15750
- throw new Error(`${decision.toolName} is allowed by policy but no local executor is registered.`);
15751
- }
15752
- const opts = {
15753
- ...args2.abortSignal ? { abortSignal: args2.abortSignal } : {},
15754
- ...args2.detachMs !== void 0 ? { detachMs: args2.detachMs } : {},
15755
- ...args2.confirmed !== void 0 ? { confirmed: args2.confirmed } : {},
15756
- ...args2.enableDestructiveShellGuard !== void 0 ? { enableDestructiveShellGuard: args2.enableDestructiveShellGuard } : {},
15757
- ...args2.confirmDestructiveAction ? { confirmDestructiveAction: args2.confirmDestructiveAction } : {},
15758
- ...args2.blockDestructiveWithoutConfirmation !== void 0 ? { blockDestructiveWithoutConfirmation: args2.blockDestructiveWithoutConfirmation } : {}
15759
- };
15760
- if (decision.toolName === "execShell") {
15761
- let parsedInput = args2.call.arguments;
15762
- if (typeof parsedInput === "string") {
15763
- try {
15764
- parsedInput = JSON.parse(parsedInput);
15765
- } catch {
15766
- parsedInput = { command: parsedInput };
15767
- }
15768
- }
15769
- await evaluateExecShellDestructiveGuard(parsedInput, opts);
15770
- opts.confirmed = true;
15771
- }
15772
- return executor(
15773
- {
15774
- ...args2.call,
15775
- name: decision.toolName
15776
- },
15777
- opts
15778
- );
15779
- }
15780
-
15781
15748
  // packages/agent-runtime/localWorkspaceTools.ts
15782
15749
  import { existsSync as existsSync3 } from "node:fs";
15783
15750
  import { createHash as createHash3 } from "node:crypto";
@@ -16576,29 +16543,6 @@ function getProcessRegistry() {
16576
16543
  return registry;
16577
16544
  }
16578
16545
 
16579
- // packages/agent-runtime/processOwnership.ts
16580
- var asNonEmptyString = (value) => {
16581
- if (typeof value !== "string") return null;
16582
- const trimmed = value.trim();
16583
- return trimmed ? trimmed : null;
16584
- };
16585
- function readProcessOwner(runtimeContext) {
16586
- if (!runtimeContext || typeof runtimeContext !== "object") return null;
16587
- const dialogId = asNonEmptyString(runtimeContext.dialogId) ?? asNonEmptyString(runtimeContext.parentThreadId);
16588
- if (!dialogId) return null;
16589
- const turnId = asNonEmptyString(runtimeContext.turnId);
16590
- const ownerId = asNonEmptyString(runtimeContext.ownerId);
16591
- return {
16592
- dialogId,
16593
- ...turnId ? { turnId } : {},
16594
- ...ownerId ? { ownerId } : {}
16595
- };
16596
- }
16597
-
16598
- // packages/agent-runtime/workspaceShell.ts
16599
- import { resolve as resolve3 } from "node:path";
16600
- import { spawn as spawnChildProcess2 } from "node:child_process";
16601
-
16602
16546
  // packages/agent-runtime/processTaskResult.ts
16603
16547
  var PROCESS_RESULT_CAPSULE_TAIL_CHARS = 4e3;
16604
16548
  var PROCESS_RESULT_CAPSULE_SPILL_THRESHOLD_CHARS = 2e4;
@@ -16618,11 +16562,17 @@ function buildProcessTaskResultCapsule(args2) {
16618
16562
  const exitCode = args2.exitCode;
16619
16563
  const captureError = args2.captureError?.trim() || void 0;
16620
16564
  const durationMs = typeof args2.startedAt === "number" && Number.isFinite(args2.startedAt) ? Math.max(0, (args2.endedAt ?? Date.now()) - args2.startedAt) : void 0;
16621
- const stdoutTail = tailText(stdout, tailChars);
16622
- const stderrTail = tailText(stderr, tailChars);
16565
+ const stdoutProvenance = args2.streamProvenance?.stdout;
16566
+ const stderrProvenance = args2.streamProvenance?.stderr;
16567
+ const ringEvicted = Boolean(stdoutProvenance?.evicted || stderrProvenance?.evicted);
16568
+ const stdoutTailSource = typeof args2.stdoutTail === "string" ? args2.stdoutTail : stdout;
16569
+ const stderrTailSource = typeof args2.stderrTail === "string" ? args2.stderrTail : stderr;
16570
+ const stdoutTail = tailText(stdoutTailSource, tailChars);
16571
+ const stderrTail = tailText(stderrTailSource, tailChars);
16623
16572
  const inlineSize = stdout.length + stderr.length;
16624
16573
  const inlineTruncated = stdoutTail.truncated || stderrTail.truncated;
16625
- const shouldSpill = inlineTruncated || inlineSize > spillThresholdChars;
16574
+ const truncated = inlineTruncated || ringEvicted || Boolean(captureError);
16575
+ const shouldSpill = truncated || inlineSize > spillThresholdChars;
16626
16576
  let spill;
16627
16577
  if (shouldSpill) {
16628
16578
  try {
@@ -16636,8 +16586,11 @@ function buildProcessTaskResultCapsule(args2) {
16636
16586
  });
16637
16587
  spill = {
16638
16588
  displayPath: result4.displayPath,
16639
- totalChars: result4.totalChars,
16640
- totalLines: result4.totalLines
16589
+ // Prefer the caller's live provenance counters: they count every
16590
+ // byte of the original stream, while the buffered content above may
16591
+ // be a capped prefix of it.
16592
+ totalChars: (stdoutProvenance?.totalChars ?? stdout.length) + (stderrProvenance?.totalChars ?? stderr.length),
16593
+ totalLines: (stdoutProvenance?.totalLines ?? countLines(stdout)) + (stderrProvenance?.totalLines ?? countLines(stderr))
16641
16594
  };
16642
16595
  } catch {
16643
16596
  }
@@ -16648,14 +16601,35 @@ function buildProcessTaskResultCapsule(args2) {
16648
16601
  exitCode,
16649
16602
  ...durationMs !== void 0 ? { durationMs } : {},
16650
16603
  ...captureError ? { captureError } : {},
16651
- truncated: inlineTruncated || Boolean(captureError),
16604
+ truncated,
16652
16605
  ...spill ? { spill } : {}
16653
16606
  };
16654
16607
  }
16655
16608
 
16656
- // packages/agent-runtime/workspaceShell.ts
16657
- var EXEC_SHELL_TIMEOUT_ENV = "NOLO_EXEC_SHELL_TIMEOUT_MS";
16658
- var EXEC_SHELL_DETACH_ENV = "NOLO_EXEC_SHELL_DETACH_MS";
16609
+ // packages/agent-runtime/processOwnership.ts
16610
+ var asNonEmptyString = (value) => {
16611
+ if (typeof value !== "string") return null;
16612
+ const trimmed = value.trim();
16613
+ return trimmed ? trimmed : null;
16614
+ };
16615
+ function readProcessOwner(runtimeContext) {
16616
+ if (!runtimeContext || typeof runtimeContext !== "object") return null;
16617
+ const dialogId = asNonEmptyString(runtimeContext.dialogId) ?? asNonEmptyString(runtimeContext.parentThreadId);
16618
+ if (!dialogId) return null;
16619
+ const turnId = asNonEmptyString(runtimeContext.turnId);
16620
+ const ownerId = asNonEmptyString(runtimeContext.ownerId);
16621
+ return {
16622
+ dialogId,
16623
+ ...turnId ? { turnId } : {},
16624
+ ...ownerId ? { ownerId } : {}
16625
+ };
16626
+ }
16627
+
16628
+ // packages/agent-runtime/workspaceShell.ts
16629
+ import { resolve as resolve3 } from "node:path";
16630
+ import { spawn as spawnChildProcess2 } from "node:child_process";
16631
+ var EXEC_SHELL_TIMEOUT_ENV = "NOLO_EXEC_SHELL_TIMEOUT_MS";
16632
+ var EXEC_SHELL_DETACH_ENV = "NOLO_EXEC_SHELL_DETACH_MS";
16659
16633
  var DEFAULT_EXEC_SHELL_DETACH_MS = 12e4;
16660
16634
  function readTrimmedString(value) {
16661
16635
  return asOptionalTrimmedString(value);
@@ -17522,13 +17496,169 @@ function runTasks(args2) {
17522
17496
  return { tasks, count: tasks.length };
17523
17497
  }
17524
17498
 
17499
+ // packages/agent-runtime/execShellCompletionWarnings.ts
17500
+ var BG_ESCAPE_WORD_PATTERN = /(?:^|[\s;|(&/\\])(nohup|disown|setsid|start-process)(?=$|[\s;|)&])/i;
17501
+ var SHELL_SPLIT_CHARS = /* @__PURE__ */ new Set([";", "|", "&", "(", ")", "<", ">", "\n", " ", " "]);
17502
+ function maskQuotedAndEscaped(command) {
17503
+ const out = Array.from(command);
17504
+ let quote = null;
17505
+ for (let i = 0; i < out.length; i += 1) {
17506
+ const ch = out[i];
17507
+ if (quote) {
17508
+ out[i] = " ";
17509
+ if (ch === "\\" && quote !== "'") {
17510
+ if (i + 1 < out.length) out[i + 1] = " ";
17511
+ i += 1;
17512
+ } else if (ch === quote) {
17513
+ quote = null;
17514
+ }
17515
+ continue;
17516
+ }
17517
+ if (ch === "\\") {
17518
+ out[i] = " ";
17519
+ if (i + 1 < out.length) out[i + 1] = " ";
17520
+ i += 1;
17521
+ continue;
17522
+ }
17523
+ if (ch === "'" || ch === '"' || ch === "`") {
17524
+ quote = ch;
17525
+ out[i] = " ";
17526
+ continue;
17527
+ }
17528
+ if (ch === "#" && (i === 0 || /[\s;]/.test(out[i - 1] ?? " "))) {
17529
+ while (i < out.length && out[i] !== "\n") {
17530
+ out[i] = " ";
17531
+ i += 1;
17532
+ }
17533
+ }
17534
+ }
17535
+ return out.join("");
17536
+ }
17537
+ function readRedirectTarget(original, start) {
17538
+ let i = start;
17539
+ while (i < original.length && /\s/.test(original[i] ?? "")) {
17540
+ i += 1;
17541
+ }
17542
+ if (i >= original.length) return void 0;
17543
+ let text = "";
17544
+ let j = i;
17545
+ while (j < original.length) {
17546
+ const o = original[j];
17547
+ if (o === "'" || o === '"' || o === "`") {
17548
+ const quote = o;
17549
+ j += 1;
17550
+ while (j < original.length && original[j] !== quote) {
17551
+ if (original[j] === "\\" && quote !== "'" && j + 1 < original.length) {
17552
+ text += original[j + 1];
17553
+ j += 2;
17554
+ continue;
17555
+ }
17556
+ text += original[j];
17557
+ j += 1;
17558
+ }
17559
+ j += 1;
17560
+ continue;
17561
+ }
17562
+ if (/\s/.test(o) || SHELL_SPLIT_CHARS.has(o)) break;
17563
+ text += o;
17564
+ j += 1;
17565
+ }
17566
+ if (!text || text === "$") return void 0;
17567
+ return { text, end: j };
17568
+ }
17569
+ function isIgnoredRedirectTarget(target) {
17570
+ return target === "/dev/null";
17571
+ }
17572
+ function detectExecShellSynchronousWarnings(command, shell) {
17573
+ const masked = maskQuotedAndEscaped(command);
17574
+ const isPowerShell = shell?.resolvedShell === "powershell";
17575
+ const redirectTargets = [];
17576
+ let backgrounded = false;
17577
+ let i = 0;
17578
+ while (i < masked.length) {
17579
+ const ch = masked[i];
17580
+ if (ch === "&") {
17581
+ const next = masked[i + 1];
17582
+ if (next === "&") {
17583
+ i += 2;
17584
+ continue;
17585
+ }
17586
+ if (next === ">") {
17587
+ let j = i + 2;
17588
+ if (masked[j] === ">") j += 1;
17589
+ const target = readRedirectTarget(command, j);
17590
+ if (target && !isIgnoredRedirectTarget(target.text)) redirectTargets.push(target.text);
17591
+ i = target ? target.end : j;
17592
+ continue;
17593
+ }
17594
+ const prev = masked[i - 1];
17595
+ if (prev === ">" || prev === "<") {
17596
+ i += 1;
17597
+ continue;
17598
+ }
17599
+ if (isPowerShell) {
17600
+ i += 1;
17601
+ continue;
17602
+ }
17603
+ backgrounded = true;
17604
+ i += 1;
17605
+ continue;
17606
+ }
17607
+ if (ch === ">") {
17608
+ const prev = masked[i - 1];
17609
+ if (prev === ">" || prev === "&" || prev === "<") {
17610
+ i += 1;
17611
+ continue;
17612
+ }
17613
+ let j = i + 1;
17614
+ if (masked[j] === ">") j += 1;
17615
+ if (masked[j] === "&") {
17616
+ const ampTarget = readRedirectTarget(command, j + 1);
17617
+ const isFdDuplication = ampTarget !== void 0 && (/^\d+$/.test(ampTarget.text) || ampTarget.text === "-");
17618
+ if (ampTarget && !isFdDuplication && !isIgnoredRedirectTarget(ampTarget.text)) {
17619
+ redirectTargets.push(ampTarget.text);
17620
+ }
17621
+ i = ampTarget ? ampTarget.end : j + 1;
17622
+ continue;
17623
+ }
17624
+ const target = readRedirectTarget(command, j);
17625
+ if (target && !isIgnoredRedirectTarget(target.text)) redirectTargets.push(target.text);
17626
+ i = target ? target.end : j;
17627
+ continue;
17628
+ }
17629
+ if (ch === "<") {
17630
+ i += masked[i + 1] === "<" || masked[i + 1] === "&" ? 2 : 1;
17631
+ continue;
17632
+ }
17633
+ i += 1;
17634
+ }
17635
+ const wordMatch = BG_ESCAPE_WORD_PATTERN.exec(masked);
17636
+ if (wordMatch) backgrounded = true;
17637
+ const warnings = [];
17638
+ if (redirectTargets.length > 0) {
17639
+ const target = redirectTargets[0];
17640
+ warnings.push({
17641
+ kind: "output-redirect",
17642
+ redirectTarget: target,
17643
+ message: `[wake-safety] This execShell call completed synchronously; its output was redirected to ${target}. That file is NOT delivered by any completion notification \u2014 no tracked background task was created. If you need the result, read that file in this turn.`
17644
+ });
17645
+ }
17646
+ if (backgrounded) {
17647
+ warnings.push({
17648
+ kind: "shell-backgrounding",
17649
+ message: `[wake-safety] Detected shell-internal backgrounding (lone \`&\`, nohup/disown/setsid/Start-Process or similar). Descendant processes are NOT tracked as platform tasks and will not produce completion notifications; text such as \`LAUNCHED\` printed by the command is plain stdout, not a platform receipt.`
17650
+ });
17651
+ }
17652
+ return warnings;
17653
+ }
17654
+
17525
17655
  // packages/agent-runtime/capabilities/execShellCapability.ts
17526
17656
  function buildExecShellToolDefinition(toolName = "execShell") {
17527
17657
  return {
17528
17658
  type: "function",
17529
17659
  function: {
17530
17660
  name: toolName,
17531
- description: `Execute a shell command from the workspace root. Prefer one compound command (e.g. 'git status && git diff --stat') to perform complete verification in one step instead of multiple small roundtrips. Do not cd into guessed paths; commands already run from the workspace root. Commands block until exit. Long-running commands (sleep over ${IMMEDIATE_DETACH_SLEEP_THRESHOLD_SECONDS}s, dev servers, watchers) automatically detach to background returning {detached: true, pid, label}; for persistent services, prefer launchProcess. On Windows the resolved shell may be Windows PowerShell 5.1 (metadata.shellKind="powershell5"): it does not support '&&'/'||' or $PSStyle \u2014 join commands with ';' or split into multiple calls. metadata.shellKind="pwsh" means PowerShell 7+ with full syntax. Read resolvedShell/shellKind from any execShell result before writing Windows-specific syntax.`,
17661
+ description: `Execute a shell command from the workspace root. Prefer one compound command (e.g. 'git status && git diff --stat') to perform complete verification in one step instead of multiple small roundtrips. Do not cd into guessed paths; commands already run from the workspace root. Commands block until exit. Long-running commands (sleep over ${IMMEDIATE_DETACH_SLEEP_THRESHOLD_SECONDS}s, dev servers, watchers) automatically detach to background returning {detached: true, pid, label}; for persistent services, prefer launchProcess. On Windows the resolved shell may be Windows PowerShell 5.1 (metadata.shellKind="powershell5"): it does not support '&&'/'||' or $PSStyle \u2014 join commands with ';' or split into multiple calls. metadata.shellKind="pwsh" means PowerShell 7+ with full syntax. Read resolvedShell/shellKind from any execShell result before writing Windows-specific syntax. LIFECYCLE CONTRACT (misreading this contract silenced a real dialog for 10 hours): (1) A normal command waits synchronously for at most 120 seconds; if it ends inside that window the call IS finished \u2014 a result containing exitCode means the command has already completed, never "still running". (2) Only a result with metadata.detached === true AND a taskId represents a tracked background task that may trigger completion wake-up (terminal completion turn carrying a bounded result capsule); that handle may be waited on with taskWait and will produce a completion notification. (3) Text the command itself prints (LAUNCHED, STARTED, BACKGROUND, ...) is plain stdout with zero lifecycle meaning \u2014 never read it as a platform receipt. (4) Shell-level backgrounding (\`&\`, nohup, disown, setsid, self-daemonizing scripts, PowerShell Start-Process) is NOT tracked: those descendants produce no completion notification, and the outer shell finishing tells you nothing about them. Use background: true to turn a finite job into a tracked background task immediately. When a synchronous result's core output was redirected to a file, read that file in the SAME turn \u2014 never end the turn promising a later automatic report without a taskId.`,
17532
17662
  parameters: {
17533
17663
  type: "object",
17534
17664
  properties: {
@@ -17539,6 +17669,10 @@ function buildExecShellToolDefinition(toolName = "execShell") {
17539
17669
  cmd: {
17540
17670
  type: "string",
17541
17671
  description: "Compatibility alias for command."
17672
+ },
17673
+ background: {
17674
+ type: "boolean",
17675
+ description: "Immediately detach this command into a tracked background task (no 120s synchronous wait): returns {detached: true, taskId}, and on completion the platform resumes this conversation with a bounded result capsule (exitCode, duration, stdout/stderr tail). Default false. Use for finite jobs (tests, builds, batch) whose result you need later; NOT for dev servers/watchers \u2014 use launchProcess for those."
17542
17676
  }
17543
17677
  }
17544
17678
  }
@@ -17579,11 +17713,13 @@ function normalizeExecShellInput(input) {
17579
17713
  throw new Error("execShell requires a non-empty command.");
17580
17714
  }
17581
17715
  const activity = extractActivity(record);
17716
+ const background = record.background === true || typeof record.background === "string" && record.background.trim().toLowerCase() === "true";
17582
17717
  return {
17583
17718
  command,
17584
17719
  ...record.shell !== void 0 ? { shell: record.shell } : {},
17585
17720
  ...activity ? { activity } : {},
17586
- ...record.input !== void 0 ? { input: record.input } : {}
17721
+ ...record.input !== void 0 ? { input: record.input } : {},
17722
+ ...background ? { background: true } : {}
17587
17723
  };
17588
17724
  }
17589
17725
  throw new Error("execShell requires a non-empty command.");
@@ -17621,6 +17757,7 @@ var execShellCapability = {
17621
17757
  command,
17622
17758
  shell: normalized.shell
17623
17759
  });
17760
+ const background = normalized.background === true;
17624
17761
  const result4 = await runWorkspaceCommand({
17625
17762
  workspaceRoot,
17626
17763
  command: shellPlan.argv,
@@ -17628,15 +17765,18 @@ var execShellCapability = {
17628
17765
  outputLimit: ctx.commandOutputLimit,
17629
17766
  commandPrefix: ctx.commandPrefix,
17630
17767
  abortSignal: ctx.abortSignal,
17631
- detachMs: isImmediateDetachShellCommand({ command }) ? 0 : ctx.detachMs,
17768
+ detachMs: background ? 0 : isImmediateDetachShellCommand({ command }) ? 0 : ctx.detachMs,
17632
17769
  stdin: typeof normalized.input === "string" ? normalized.input : void 0,
17633
17770
  // Ownership of the turn that invoked this command: an auto-detached
17634
17771
  // command keeps its parent dialog so its terminal can resume that
17635
17772
  // conversation (see processOwnership.ts). Absent ctx.owner = unowned.
17636
17773
  owner: ctx.owner ?? null
17637
17774
  });
17775
+ const warnings = result4.detached || result4.spawnFailed ? [] : detectExecShellSynchronousWarnings(command, {
17776
+ resolvedShell: shellPlan.resolvedShell
17777
+ });
17638
17778
  return {
17639
- content: result4.content,
17779
+ content: warnings.length > 0 ? [result4.content, ...warnings.map((warning) => warning.message)].join("\n\n") : result4.content,
17640
17780
  metadata: {
17641
17781
  command,
17642
17782
  exitCode: result4.exitCode,
@@ -17651,8 +17791,21 @@ var execShellCapability = {
17651
17791
  // Phase 0: stable taskId from the pre-registered Execution
17652
17792
  // Envelope (additive; existing detached fields untouched).
17653
17793
  taskId: result4.taskId,
17654
- status: "running"
17655
- } : {},
17794
+ status: "running",
17795
+ // P0 machine-readable execution state: an explicit positive
17796
+ // signal instead of requiring the model to infer "not detached"
17797
+ // from absence. Mirrors the real lifecycle contract.
17798
+ executionState: "detached",
17799
+ trackedBackgroundTask: true,
17800
+ completionNotification: "terminal-resume"
17801
+ } : {
17802
+ // P0 machine-readable execution state: the command already
17803
+ // finished inside the synchronous window; no background task
17804
+ // exists, so no completion notification will ever arrive.
17805
+ executionState: "synchronous-complete",
17806
+ trackedBackgroundTask: false,
17807
+ completionNotification: "none"
17808
+ },
17656
17809
  ...normalized.activity ? { activity: normalized.activity } : {}
17657
17810
  }
17658
17811
  };
@@ -17868,7 +18021,7 @@ function buildLaunchProcessTool() {
17868
18021
  type: "function",
17869
18022
  function: {
17870
18023
  name: "launchProcess",
17871
- description: "Start a long-running background process (dev server, watcher, REPL) and return immediately with {pid, label, status}. Use listProcesses to inspect or stop it.",
18024
+ description: "Start a long-running ambient background process (dev server, watcher, REPL) and return immediately with {pid, label, taskId, status}. Real boundaries: this registers a tracked background task, but completion does NOT automatically start a new turn \u2014 the process is intentionally notice-only (an exited dev server must not silently burn a model turn). When it reaches a terminal state the bounded result (exitCode, duration, stdout/stderr tail) is recorded and surfaced as a notice injected only on your next real turn in the same conversation. Use listProcesses to inspect, taskWait to block for its terminal state, or taskStop to stop it. For a finite job (tests, builds, batch) whose result must resume the conversation automatically, use execShell with background: true instead.",
17872
18025
  parameters: {
17873
18026
  type: "object",
17874
18027
  properties: {
@@ -18654,6 +18807,87 @@ async function writeFileTool(args2) {
18654
18807
  }
18655
18808
  };
18656
18809
  }
18810
+ function extractSnippetWords(text) {
18811
+ const tokens = [];
18812
+ const latinWords = text.match(/[A-Za-z0-9_]{2,}/g);
18813
+ if (latinWords) {
18814
+ for (const w of latinWords) tokens.push(w.toLowerCase());
18815
+ }
18816
+ const cjkChars = text.match(new RegExp("\\p{Script=Han}", "gu"));
18817
+ if (cjkChars) {
18818
+ for (let i = 0; i < cjkChars.length - 1; i++) {
18819
+ tokens.push(cjkChars[i] + cjkChars[i + 1]);
18820
+ }
18821
+ if (cjkChars.length === 1) tokens.push(cjkChars[0]);
18822
+ }
18823
+ return tokens;
18824
+ }
18825
+ function calculateEditLineSimilarity(a, b) {
18826
+ if (a === b) return 1;
18827
+ if (!a || !b) return 0;
18828
+ if (a.includes(b) || b.includes(a)) return 0.8;
18829
+ const wordsA = extractSnippetWords(a);
18830
+ const wordsB = extractSnippetWords(b);
18831
+ if (wordsA.length === 0 || wordsB.length === 0) return 0;
18832
+ const setB = new Set(wordsB);
18833
+ let matches = 0;
18834
+ for (const w of wordsA) {
18835
+ if (setB.has(w)) matches++;
18836
+ }
18837
+ return 2 * matches / (wordsA.length + wordsB.length);
18838
+ }
18839
+ function findClosestEditSnippet(content, oldText) {
18840
+ const contentLines = content.split(/\r?\n/);
18841
+ const oldLinesRaw = oldText.split(/\r?\n/);
18842
+ const oldTrimmedLines = oldLinesRaw.map((l) => l.trim()).filter(Boolean);
18843
+ if (contentLines.length === 0 || oldTrimmedLines.length === 0) return null;
18844
+ if (oldTrimmedLines.length > 0 && contentLines.length >= oldTrimmedLines.length) {
18845
+ for (let i = 0; i <= contentLines.length - oldTrimmedLines.length; i++) {
18846
+ let allMatch = true;
18847
+ for (let j = 0; j < oldTrimmedLines.length; j++) {
18848
+ if (contentLines[i + j].trim() !== oldTrimmedLines[j]) {
18849
+ allMatch = false;
18850
+ break;
18851
+ }
18852
+ }
18853
+ if (allMatch) {
18854
+ const startLine2 = i + 1;
18855
+ const endLine2 = i + oldTrimmedLines.length;
18856
+ const snippetLines2 = [];
18857
+ for (let k = i; k < i + oldTrimmedLines.length; k++) {
18858
+ snippetLines2.push(`> ${k + 1}: ${contentLines[k]}`);
18859
+ }
18860
+ return `Exact match found with different indentation/whitespace (lines ${startLine2}-${endLine2}):
18861
+ ${snippetLines2.join("\n")}`;
18862
+ }
18863
+ }
18864
+ }
18865
+ const candidateTargetLines = oldTrimmedLines.filter((l) => l.length >= 4);
18866
+ const targetLine = candidateTargetLines[0] || oldTrimmedLines[0];
18867
+ if (!targetLine || targetLine.length < 3) return null;
18868
+ let bestIndex = -1;
18869
+ let bestScore = 0;
18870
+ for (let i = 0; i < contentLines.length; i++) {
18871
+ const line = contentLines[i].trim();
18872
+ if (!line || line.length < 3) continue;
18873
+ const score = calculateEditLineSimilarity(line, targetLine);
18874
+ if (score > bestScore && score >= 0.45) {
18875
+ bestScore = score;
18876
+ bestIndex = i;
18877
+ if (score === 1) break;
18878
+ }
18879
+ }
18880
+ if (bestIndex === -1) return null;
18881
+ const startLine = Math.max(0, bestIndex - 2);
18882
+ const endLine = Math.min(contentLines.length - 1, bestIndex + 2);
18883
+ const snippetLines = [];
18884
+ for (let i = startLine; i <= endLine; i++) {
18885
+ const prefix = i === bestIndex ? "> " : " ";
18886
+ snippetLines.push(`${prefix}${i + 1}: ${contentLines[i]}`);
18887
+ }
18888
+ return `Closest match near line ${bestIndex + 1}:
18889
+ ${snippetLines.join("\n")}`;
18890
+ }
18657
18891
  async function editFileTool(args2) {
18658
18892
  const parsed = parseWorkspaceToolArguments(args2.call.arguments);
18659
18893
  const requestedPath = requireWorkspaceToolPath(parsed);
@@ -18672,8 +18906,11 @@ async function editFileTool(args2) {
18672
18906
  newText
18673
18907
  });
18674
18908
  if (replacementCount !== expectedReplacements) {
18909
+ const hint = replacementCount === 0 ? findClosestEditSnippet(content, oldText) : null;
18675
18910
  throw new Error(
18676
- `editFile expected ${expectedReplacements} ${pluralizeReplacement(expectedReplacements)} but found ${replacementCount} in ${requestedPath} (matched after EOL normalization; verify the exact text).`
18911
+ `editFile expected ${expectedReplacements} ${pluralizeReplacement(expectedReplacements)} but found ${replacementCount} in ${requestedPath} (matched after EOL normalization; verify the exact text).` + (hint ? `
18912
+
18913
+ ${hint}` : "")
18677
18914
  );
18678
18915
  }
18679
18916
  await writeFile(absolutePath, nextContent, "utf8");
@@ -19009,6 +19246,55 @@ async function execShellTool(args2) {
19009
19246
  }
19010
19247
  );
19011
19248
  }
19249
+ var LAUNCH_PROCESS_STREAM_RING_CHARS = 65536;
19250
+ var LAUNCH_PROCESS_STREAM_CAPTURE_MAX_CHARS = 4e6;
19251
+ function createLaunchProcessStreamCapture() {
19252
+ let ring = "";
19253
+ let full = null;
19254
+ let capturedChars = 0;
19255
+ let totalChars = 0;
19256
+ let totalNewlines = 0;
19257
+ return {
19258
+ push(chunk) {
19259
+ const str = typeof chunk === "string" ? chunk : chunk.toString("utf8");
19260
+ if (!str) return;
19261
+ totalChars += str.length;
19262
+ for (let i = 0; i < str.length; i += 1) {
19263
+ if (str.charCodeAt(i) === 10) totalNewlines += 1;
19264
+ }
19265
+ const combined = ring + str;
19266
+ if (combined.length <= LAUNCH_PROCESS_STREAM_RING_CHARS) {
19267
+ ring = combined;
19268
+ return;
19269
+ }
19270
+ if (full === null) {
19271
+ full = [combined];
19272
+ capturedChars = combined.length;
19273
+ } else if (capturedChars < LAUNCH_PROCESS_STREAM_CAPTURE_MAX_CHARS) {
19274
+ const room = LAUNCH_PROCESS_STREAM_CAPTURE_MAX_CHARS - capturedChars;
19275
+ const slice = str.length <= room ? str : str.slice(0, room);
19276
+ full.push(slice);
19277
+ capturedChars += slice.length;
19278
+ }
19279
+ ring = combined.slice(-LAUNCH_PROCESS_STREAM_RING_CHARS);
19280
+ },
19281
+ text() {
19282
+ return full === null ? ring : full.join("");
19283
+ },
19284
+ tailText() {
19285
+ return ring;
19286
+ },
19287
+ evicted() {
19288
+ return full !== null;
19289
+ },
19290
+ totalChars() {
19291
+ return totalChars;
19292
+ },
19293
+ totalLines() {
19294
+ return totalChars > 0 ? totalNewlines + 1 : 0;
19295
+ }
19296
+ };
19297
+ }
19012
19298
  async function launchProcessTool(args2) {
19013
19299
  const parsed = parseWorkspaceToolArguments(args2.call.arguments);
19014
19300
  const command = requireShellCommand(parsed, args2.call.name);
@@ -19036,6 +19322,14 @@ async function launchProcessTool(args2) {
19036
19322
  stdio: ["ignore", "pipe", "pipe"],
19037
19323
  detached
19038
19324
  });
19325
+ const stdoutCapture = createLaunchProcessStreamCapture();
19326
+ const stderrCapture = createLaunchProcessStreamCapture();
19327
+ proc.stdout?.on("data", (chunk) => stdoutCapture.push(chunk));
19328
+ proc.stderr?.on("data", (chunk) => stderrCapture.push(chunk));
19329
+ proc.stdout?.on("error", () => {
19330
+ });
19331
+ proc.stderr?.on("error", () => {
19332
+ });
19039
19333
  const pid = proc.pid;
19040
19334
  if (typeof pid !== "number") {
19041
19335
  return {
@@ -19076,7 +19370,38 @@ async function launchProcessTool(args2) {
19076
19370
  process.removeListener("SIGTERM", cleanupChildOnHostSignal);
19077
19371
  process.removeListener("SIGINT", cleanupChildOnHostSignal);
19078
19372
  }
19079
- registry2.markExited(pid, code ?? 1);
19373
+ const exitCode = code ?? 1;
19374
+ let capsule;
19375
+ try {
19376
+ capsule = buildProcessTaskResultCapsule({
19377
+ // The full captured output (bounded prefix when the process was
19378
+ // extremely chatty): the module tails it for the inline capsule and
19379
+ // spills it for recovery. Provenance carries the live original-stream
19380
+ // counters so truncated/spill metadata never derives from the ring.
19381
+ stdout: stdoutCapture.text(),
19382
+ stderr: stderrCapture.text(),
19383
+ stdoutTail: stdoutCapture.tailText(),
19384
+ stderrTail: stderrCapture.tailText(),
19385
+ exitCode,
19386
+ // Duration measured from the registry envelope's startedAt (spawn).
19387
+ startedAt: envelope.startedAt,
19388
+ workspaceRoot: args2.workspaceRoot,
19389
+ streamProvenance: {
19390
+ stdout: {
19391
+ evicted: stdoutCapture.evicted(),
19392
+ totalChars: stdoutCapture.totalChars(),
19393
+ totalLines: stdoutCapture.totalLines()
19394
+ },
19395
+ stderr: {
19396
+ evicted: stderrCapture.evicted(),
19397
+ totalChars: stderrCapture.totalChars(),
19398
+ totalLines: stderrCapture.totalLines()
19399
+ }
19400
+ }
19401
+ });
19402
+ } catch {
19403
+ }
19404
+ registry2.markExited(pid, exitCode, capsule);
19080
19405
  });
19081
19406
  const activity = extractActivity(parsed);
19082
19407
  const resultData = { pid, label, taskId: envelope.taskId, status: "running" };
@@ -19088,6 +19413,13 @@ async function launchProcessTool(args2) {
19088
19413
  label,
19089
19414
  status: "running",
19090
19415
  taskId: envelope.taskId,
19416
+ // P0 machine-readable execution state (consistent with the real wake
19417
+ // contract): a real background task from birth, but ambient — its
19418
+ // terminal result rides the next real turn's notice, not an automatic
19419
+ // completion turn.
19420
+ executionState: "background",
19421
+ trackedBackgroundTask: true,
19422
+ completionNotification: "next-turn-notice",
19091
19423
  processLaunch: resultData,
19092
19424
  ...activity ? { activity } : {}
19093
19425
  }
@@ -19190,7 +19522,7 @@ async function tasksTool(_args) {
19190
19522
  }
19191
19523
  async function openDesktopPreviewTool(args2) {
19192
19524
  const parsed = parseWorkspaceToolArguments(args2.call.arguments);
19193
- const url = typeof parsed.url === "string" ? parsed.url.trim() : "";
19525
+ const url = asTrimmedString(parsed.url);
19194
19526
  if (!url) {
19195
19527
  return {
19196
19528
  content: JSON.stringify({ error: "openDesktopPreview requires a non-empty url." }),
@@ -19312,6 +19644,292 @@ function createLocalWorkspaceToolExecutors(args2) {
19312
19644
  };
19313
19645
  }
19314
19646
 
19647
+ // packages/agent-runtime/toolArgumentGate.ts
19648
+ var FIELD_ALIAS_GROUPS = [
19649
+ // localWorkspaceTools.ts:336 导出常量(readFile/writeFile/editFile/globFiles)
19650
+ PATH_FIELD_ALIASES,
19651
+ // localWorkspaceTools.ts:339-340 导出常量(editFile)
19652
+ OLD_TEXT_FIELD_ALIASES,
19653
+ NEW_TEXT_FIELD_ALIASES,
19654
+ // localWorkspaceTools.ts:733 `args.cmd || args.command`(launchProcess);
19655
+ // launchProcess 的 required ["command"] 见 localWorkspaceToolDefs.ts:283。
19656
+ // 注:execShell 的 schema 无 required(command 在 executor 层强制,
19657
+ // capabilities/execShellCapability.ts:42 声明 cmd 兼容字段),本组对它不产生拦截。
19658
+ ["command", "cmd"],
19659
+ // localWorkspaceTools.ts:698-699 `args.pattern ?? args.glob`(globFiles)
19660
+ ["pattern", "glob"],
19661
+ // noloWorkspaceTools.ts:492 `args.dialog ?? args.dialogId ?? args.id`(readDialog)
19662
+ ["dialog", "dialogId", "id"],
19663
+ // noloWorkspaceTools.ts:545 `args.agent ?? args.agentKey ?? args.id`(readAgent)
19664
+ ["agent", "agentKey", "id"],
19665
+ // noloWorkspaceTools.ts:552 `args.space ?? args.spaceId ?? args.id`(readSpace)
19666
+ ["space", "spaceId", "id"],
19667
+ // noloWorkspaceTools.ts:561/567 `args.doc ?? args.docKey ?? args.pageKey ?? args.key`
19668
+ // (readDoc / readSkillDoc)
19669
+ ["doc", "docKey", "pageKey", "key"],
19670
+ // noloWorkspaceTools.node.ts:238 `parsed.name ?? parsed.skillName ?? parsed.skill`(loadSkill)
19671
+ ["name", "skillName", "skill"],
19672
+ // noloWorkspaceTools.ts:593 `args.table ?? args.tableId ?? args.metaKey`(queryTableRows)
19673
+ ["table", "tableId", "metaKey"]
19674
+ ];
19675
+ var TYPE_MATCHERS = {
19676
+ string: (value) => typeof value === "string",
19677
+ // 数字字符串视为可接受:executor 普遍 Number()/clamp 宽松处理,拦截会误报。
19678
+ number: (value) => typeof value === "number" || typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value)),
19679
+ integer: (value) => typeof value === "number" || typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value)),
19680
+ boolean: (value) => typeof value === "boolean" || value === "true" || value === "false",
19681
+ object: (value) => typeof value === "object" && value !== null && !Array.isArray(value),
19682
+ array: (value) => Array.isArray(value)
19683
+ };
19684
+ function describeValueType(value) {
19685
+ if (value === null) return "null";
19686
+ if (Array.isArray(value)) return "array";
19687
+ return typeof value;
19688
+ }
19689
+ function isAliasSatisfied(args2, field) {
19690
+ for (const group of FIELD_ALIAS_GROUPS) {
19691
+ if (!group.includes(field)) continue;
19692
+ if (group.some((alias) => args2[alias] !== void 0)) return true;
19693
+ }
19694
+ return false;
19695
+ }
19696
+ function readRequiredFields(parameters) {
19697
+ const required = parameters.required;
19698
+ if (!Array.isArray(required)) return [];
19699
+ return required.filter((field) => typeof field === "string");
19700
+ }
19701
+ function readProperties(parameters) {
19702
+ const properties = parameters.properties;
19703
+ if (!properties || typeof properties !== "object" || Array.isArray(properties)) {
19704
+ return {};
19705
+ }
19706
+ return properties;
19707
+ }
19708
+ function validateToolArguments(args2) {
19709
+ const { toolName, rawArguments, parameters } = args2;
19710
+ if (!parameters || typeof parameters !== "object" || Array.isArray(parameters)) {
19711
+ return { ok: true, args: {} };
19712
+ }
19713
+ const schema = parameters;
19714
+ const requiredFields = readRequiredFields(schema);
19715
+ const properties = readProperties(schema);
19716
+ if (requiredFields.length === 0 && Object.keys(properties).length === 0) {
19717
+ return { ok: true, args: {} };
19718
+ }
19719
+ const raw = typeof rawArguments === "string" ? rawArguments.trim() : "";
19720
+ let record = {};
19721
+ if (raw !== "") {
19722
+ let parsed;
19723
+ try {
19724
+ parsed = JSON.parse(raw);
19725
+ } catch {
19726
+ return { ok: true, args: {} };
19727
+ }
19728
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
19729
+ return { ok: true, args: {} };
19730
+ }
19731
+ record = parsed;
19732
+ }
19733
+ for (const field of requiredFields) {
19734
+ if (record[field] !== void 0) continue;
19735
+ if (isAliasSatisfied(record, field)) continue;
19736
+ return {
19737
+ ok: false,
19738
+ message: `${toolName} \u53C2\u6570\u6821\u9A8C\u5931\u8D25\uFF1A\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570 "${field}"\u3002\u8BF7\u8865\u4E0A\u540E\u91CD\u65B0\u8C03\u7528 ${toolName}\u3002`
19739
+ };
19740
+ }
19741
+ for (const [field, spec] of Object.entries(properties)) {
19742
+ const value = record[field];
19743
+ if (value === void 0 || value === null) continue;
19744
+ if (!spec || typeof spec !== "object" || Array.isArray(spec)) continue;
19745
+ const declared = spec.type;
19746
+ if (typeof declared !== "string") continue;
19747
+ const matcher = TYPE_MATCHERS[declared];
19748
+ if (!matcher) continue;
19749
+ if (!matcher(value)) {
19750
+ return {
19751
+ ok: false,
19752
+ message: `${toolName} \u53C2\u6570\u6821\u9A8C\u5931\u8D25\uFF1A\u53C2\u6570 "${field}" \u5E94\u4E3A ${declared}\uFF0C\u5B9E\u9645\u6536\u5230 ${describeValueType(value)}\u3002\u8BF7\u4FEE\u6B63\u540E\u91CD\u65B0\u8C03\u7528 ${toolName}\u3002`
19753
+ };
19754
+ }
19755
+ }
19756
+ return { ok: true, args: record };
19757
+ }
19758
+ function buildToolParametersIndex(tools) {
19759
+ const index = /* @__PURE__ */ new Map();
19760
+ for (const tool of tools) {
19761
+ if (!tool || typeof tool !== "object") continue;
19762
+ const fn3 = tool.function;
19763
+ if (!fn3 || typeof fn3 !== "object") continue;
19764
+ const name = fn3.name;
19765
+ const parameters = fn3.parameters;
19766
+ if (typeof name === "string" && name && parameters !== void 0) {
19767
+ index.set(name, parameters);
19768
+ }
19769
+ }
19770
+ return index;
19771
+ }
19772
+ var cachedAgentRuntimeIndex = null;
19773
+ function getAgentRuntimeToolParametersIndex() {
19774
+ if (cachedAgentRuntimeIndex) return cachedAgentRuntimeIndex;
19775
+ const index = /* @__PURE__ */ new Map();
19776
+ try {
19777
+ const workspaceTools = buildLocalWorkspaceOpenAiTools({
19778
+ toolNames: [...WORKSPACE_TOOL_NAMES],
19779
+ exposeShellTools: true
19780
+ });
19781
+ for (const [name, parameters] of buildToolParametersIndex(workspaceTools)) {
19782
+ index.set(name, parameters);
19783
+ }
19784
+ const noloTools = buildNoloWorkspaceOpenAiTools({
19785
+ toolNames: [...NOLO_WORKSPACE_TOOL_NAMES]
19786
+ });
19787
+ for (const [name, parameters] of buildToolParametersIndex(noloTools)) {
19788
+ index.set(name, parameters);
19789
+ }
19790
+ } catch {
19791
+ }
19792
+ cachedAgentRuntimeIndex = index;
19793
+ return index;
19794
+ }
19795
+
19796
+ // packages/agent-runtime/localToolPolicy.ts
19797
+ var NEVER_LOCAL_TOOLS = /* @__PURE__ */ new Set([
19798
+ "deleteSpaces",
19799
+ "updateAgent",
19800
+ "updateSelf",
19801
+ "createAgent"
19802
+ ]);
19803
+ var DEFAULT_LOCAL_TOOLS = /* @__PURE__ */ new Set([
19804
+ "readFile",
19805
+ "writeFile",
19806
+ "editFile",
19807
+ "globFiles",
19808
+ "execShell",
19809
+ "launchProcess",
19810
+ "listProcesses",
19811
+ // ProcessTask follow-up tools. Same tier as listProcesses: they observe/stop
19812
+ // tasks the agent itself already spawned, so their blast radius is strictly
19813
+ // narrower than launchProcess (which is default-enabled). Model-facing
19814
+ // follow-ups are taskWait/taskStop only — lifecycle history (taskLogs) stays
19815
+ // runtime-internal.
19816
+ "taskWait",
19817
+ "taskStop",
19818
+ // Scheduling tools: forking/observing sub-agents is a declared capability
19819
+ // of capable agents, and the blast radius is strictly narrower than
19820
+ // execShell (already default-enabled). Requiring an env allowlist for these
19821
+ // while execShell is open by default inverted the risk model and forced every
19822
+ // new local user to hand-edit .env files before sub-agent tasks worked.
19823
+ "startAgentRun",
19824
+ "controlAgentRun"
19825
+ ]);
19826
+ function parseToolAllowlist(value) {
19827
+ return (value ?? "").split(",").flatMap((item) => {
19828
+ const trimmed = item.trim();
19829
+ return trimmed ? [trimmed] : [];
19830
+ });
19831
+ }
19832
+ function normalizeLocalToolName(toolName) {
19833
+ return canonicalizeToolName(
19834
+ String(toolName ?? "").replace(/^functions\./, "").trim()
19835
+ );
19836
+ }
19837
+ function isRestrictedLocalToolMode(env) {
19838
+ return env.NOLO_LOCAL_TOOL_MODE === "restricted";
19839
+ }
19840
+ function resolveLocalToolPolicy(args2) {
19841
+ const toolName = normalizeLocalToolName(args2.toolName);
19842
+ const restrictedMode = isRestrictedLocalToolMode(args2.env);
19843
+ if (!toolName) {
19844
+ return {
19845
+ allowed: false,
19846
+ toolName,
19847
+ reason: "Tool name is required."
19848
+ };
19849
+ }
19850
+ if (NEVER_LOCAL_TOOLS.has(toolName)) {
19851
+ return {
19852
+ allowed: false,
19853
+ toolName,
19854
+ reason: `${toolName} is blocked by the local runtime safety policy.`
19855
+ };
19856
+ }
19857
+ const allowedByEnv = new Set(
19858
+ parseToolAllowlist(args2.env.NOLO_LOCAL_ALLOWED_TOOLS).map(normalizeLocalToolName)
19859
+ );
19860
+ const agentTools = new Set(
19861
+ (args2.agentToolNames ?? []).flatMap((item) => {
19862
+ const normalized = normalizeLocalToolName(item);
19863
+ return normalized ? [normalized] : [];
19864
+ })
19865
+ );
19866
+ if (!restrictedMode && agentTools.has(toolName)) {
19867
+ return { allowed: true, toolName };
19868
+ }
19869
+ if (DEFAULT_LOCAL_TOOLS.has(toolName) && agentTools.has(toolName)) {
19870
+ return { allowed: true, toolName };
19871
+ }
19872
+ if (allowedByEnv.has(toolName) && agentTools.has(toolName)) {
19873
+ return { allowed: true, toolName };
19874
+ }
19875
+ return {
19876
+ allowed: false,
19877
+ toolName,
19878
+ reason: `${toolName} is not enabled for local runtime runs. The agent must declare the tool in its manifest; in restricted mode (NOLO_LOCAL_TOOL_MODE=restricted) non-default tools also require adding to NOLO_LOCAL_ALLOWED_TOOLS (e.g. NOLO_LOCAL_ALLOWED_TOOLS=someCustomTool) and restarting the session so the env var loads.`
19879
+ };
19880
+ }
19881
+ async function executeLocalToolWithPolicy(args2) {
19882
+ if (args2.runToolNames !== void 0 && !args2.runToolNames.map(normalizeLocalToolName).includes(normalizeLocalToolName(args2.call.name))) {
19883
+ throw new Error(`${args2.call.name} is outside the final run tool surface.`);
19884
+ }
19885
+ const decision = resolveLocalToolPolicy({
19886
+ env: args2.env,
19887
+ agentToolNames: args2.agentToolNames,
19888
+ toolName: args2.call.name
19889
+ });
19890
+ if (!decision.allowed) throw new Error(decision.reason);
19891
+ const executor = args2.executors?.[decision.toolName];
19892
+ if (!executor) {
19893
+ throw new Error(`${decision.toolName} is allowed by policy but no local executor is registered.`);
19894
+ }
19895
+ const parametersSchema = args2.toolParametersIndex?.get(decision.toolName);
19896
+ if (parametersSchema !== void 0) {
19897
+ const gate = validateToolArguments({
19898
+ toolName: decision.toolName,
19899
+ rawArguments: args2.call.arguments,
19900
+ parameters: parametersSchema
19901
+ });
19902
+ if (!gate.ok) throw new Error(gate.message);
19903
+ }
19904
+ const opts = {
19905
+ ...args2.abortSignal ? { abortSignal: args2.abortSignal } : {},
19906
+ ...args2.detachMs !== void 0 ? { detachMs: args2.detachMs } : {},
19907
+ ...args2.confirmed !== void 0 ? { confirmed: args2.confirmed } : {},
19908
+ ...args2.enableDestructiveShellGuard !== void 0 ? { enableDestructiveShellGuard: args2.enableDestructiveShellGuard } : {},
19909
+ ...args2.confirmDestructiveAction ? { confirmDestructiveAction: args2.confirmDestructiveAction } : {},
19910
+ ...args2.blockDestructiveWithoutConfirmation !== void 0 ? { blockDestructiveWithoutConfirmation: args2.blockDestructiveWithoutConfirmation } : {}
19911
+ };
19912
+ if (decision.toolName === "execShell") {
19913
+ let parsedInput = args2.call.arguments;
19914
+ if (typeof parsedInput === "string") {
19915
+ try {
19916
+ parsedInput = JSON.parse(parsedInput);
19917
+ } catch {
19918
+ parsedInput = { command: parsedInput };
19919
+ }
19920
+ }
19921
+ await evaluateExecShellDestructiveGuard(parsedInput, opts);
19922
+ opts.confirmed = true;
19923
+ }
19924
+ return executor(
19925
+ {
19926
+ ...args2.call,
19927
+ name: decision.toolName
19928
+ },
19929
+ opts
19930
+ );
19931
+ }
19932
+
19315
19933
  // packages/cli/client/localDialogRecords.ts
19316
19934
  var localDialogMessageRecordToRuntimeMessage = dialogMessageRecordToAgentRuntimeMessage;
19317
19935
  function buildLocalDialogWritePlan(args2) {
@@ -19448,10 +20066,10 @@ function buildLoadSkillExecutor(args2) {
19448
20066
  metadata: { loadSkill: true, resolved: false }
19449
20067
  };
19450
20068
  }
19451
- const { resolveSkillByName } = await import("./skillDiscovery-CYUCJMAG.js");
20069
+ const { resolveSkillByName } = await import("./skillDiscovery-ZBU76COE.js");
19452
20070
  const resolved = resolveSkillByName(args2.cwd, name);
19453
20071
  if (!resolved) {
19454
- const { resolveBuiltinSkillByName } = await import("./builtinSkillRegistry-FUAVTRVZ.js");
20072
+ const { resolveBuiltinSkillByName } = await import("./builtinSkillRegistry-SBBWTANC.js");
19455
20073
  const builtin = resolveBuiltinSkillByName(name);
19456
20074
  if (builtin) {
19457
20075
  return {
@@ -19494,7 +20112,7 @@ ${body}`,
19494
20112
  };
19495
20113
  }
19496
20114
  async function formatUnknownSkillMessage(cwd, requestedName) {
19497
- const { discoverSkills } = await import("./skillDiscovery-CYUCJMAG.js");
20115
+ const { discoverSkills } = await import("./skillDiscovery-ZBU76COE.js");
19498
20116
  let available = [];
19499
20117
  try {
19500
20118
  available = discoverSkills(cwd).map((s) => s.name);
@@ -19509,739 +20127,29 @@ No skills were discovered in this workspace.`;
19509
20127
  Available skills: ${available.join(", ")}`;
19510
20128
  }
19511
20129
 
19512
- // packages/core/chat/upstreamErrorBody.ts
19513
- function parseUpstreamErrorBody(errorText, statusText) {
19514
- if (errorText) {
19515
- try {
19516
- const parsed = JSON.parse(errorText);
19517
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
19518
- const error = parsed.error;
19519
- if (error && typeof error === "object" && !Array.isArray(error)) {
19520
- return parsed;
19521
- }
19522
- }
19523
- } catch {
19524
- }
19525
- }
19526
- return { error: { message: errorText || statusText } };
19527
- }
19528
-
19529
- // packages/agent-runtime/antigravityCloudCodeProvider.ts
19530
- import { randomUUID } from "node:crypto";
19531
-
19532
- // packages/agent-runtime/antigravityWireModel.ts
19533
- var WIRE_PROFILES = {
19534
- // Antigravity discovery exposes 3.7 Flash as one tiered wire model.
19535
- // The UI labels its effort variants (Low/Medium/High), but the literal
19536
- // -medium/-high ids return 404 from streamGenerateContent.
19537
- "gemini-3.7-flash-tiered": { modelEnum: "MODEL_PLACEHOLDER_M301", maxOutputTokens: 65536 },
19538
- // Gemini 3.8 Flash 沿用 3.7 的 tiered 兼容策略:仓库内暂无 Antigravity discovery
19539
- // 证据确认 3.8 的 wire modelEnum,禁止虚构,先只声明同档输出上限;enum 待
19540
- // discovery 证据到位后补齐。
19541
- "gemini-3.8-flash-tiered": { maxOutputTokens: 65536 },
19542
- "gemini-3.6-flash-low": { maxOutputTokens: 65536 },
19543
- "gemini-3.6-flash-medium": { maxOutputTokens: 65536 },
19544
- "gemini-3.6-flash-high": { maxOutputTokens: 65536 },
19545
- // 旧模型 wire 映射保留:存量自定义 agent 仍可能引用(注册表已下线不影响此处)。
19546
- "gemini-3.5-flash-extra-low": { modelEnum: "MODEL_PLACEHOLDER_M187", maxOutputTokens: 65536 },
19547
- "gemini-3.5-flash-low": { modelEnum: "MODEL_PLACEHOLDER_M20", maxOutputTokens: 65536 },
19548
- "gemini-3-flash-agent": { modelEnum: "MODEL_PLACEHOLDER_M132", maxOutputTokens: 65536 },
19549
- "gemini-3.1-pro-low": { modelEnum: "MODEL_PLACEHOLDER_M36", maxOutputTokens: 65535 },
19550
- "gemini-pro-agent": { modelEnum: "MODEL_PLACEHOLDER_M16", maxOutputTokens: 65535 },
19551
- "claude-sonnet-5": { maxOutputTokens: 64e3 },
19552
- "claude-sonnet-4-6": { maxOutputTokens: 64e3 },
19553
- "claude-opus-4-6-thinking": { maxOutputTokens: 64e3 },
19554
- "gemini-2.5-flash": { maxOutputTokens: 65536 },
19555
- "gemini-2.5-pro": { maxOutputTokens: 65536 }
19556
- };
19557
- function resolveAntigravityWireModel(logicalModelId) {
19558
- const id = logicalModelId.trim();
19559
- const lower = id.toLowerCase();
19560
- if (lower === "gemini-3.5-flash") {
19561
- const wireModelId = "gemini-3.5-flash-low";
19562
- return { wireModelId, profile: WIRE_PROFILES[wireModelId] };
19563
- }
19564
- if (lower === "gemini-3.7-flash" || ["gemini-3.7-flash-low", "gemini-3.7-flash-medium", "gemini-3.7-flash-high"].includes(lower)) {
19565
- const wireModelId = "gemini-3.7-flash-tiered";
19566
- return { wireModelId, profile: WIRE_PROFILES[wireModelId] };
19567
- }
19568
- if (lower === "gemini-3.8-flash" || ["gemini-3.8-flash-low", "gemini-3.8-flash-medium", "gemini-3.8-flash-high"].includes(lower)) {
19569
- const wireModelId = "gemini-3.8-flash-tiered";
19570
- return { wireModelId, profile: WIRE_PROFILES[wireModelId] };
19571
- }
19572
- if (lower === "gemini-3.6-flash") {
19573
- const wireModelId = "gemini-3.6-flash-low";
19574
- return { wireModelId, profile: WIRE_PROFILES[wireModelId] };
19575
- }
19576
- if (lower === "gemini-3.1-pro" || lower === "gemini-3.1-pro-preview") {
19577
- const wireModelId = "gemini-3.1-pro-low";
19578
- return { wireModelId, profile: WIRE_PROFILES[wireModelId] };
19579
- }
19580
- if (WIRE_PROFILES[id]) {
19581
- return { wireModelId: id, profile: WIRE_PROFILES[id] };
19582
- }
19583
- return { wireModelId: id };
19584
- }
19585
-
19586
- // packages/agent-runtime/geminiNativeShared.ts
19587
- var SKIP_THOUGHT_SIGNATURE = "skip_thought_signature_validator";
19588
- function resolveGeminiModelQuirks(modelId) {
19589
- const lower = modelId.toLowerCase();
19590
- const isGemini3 = lower.includes("gemini-3");
19591
- const isStrictSignatureModel = lower.includes("gemini-3.5") || lower.includes("flash-preview");
19592
- const isClaude = lower.includes("claude");
19593
- return {
19594
- requiresThoughtSignature: isGemini3,
19595
- allowsThoughtSignatureSentinel: isGemini3 && !isStrictSignatureModel,
19596
- isClaudeCrossModel: isClaude
19597
- };
19598
- }
19599
- function isGemini3Model(modelId) {
19600
- return resolveGeminiModelQuirks(modelId).requiresThoughtSignature;
19601
- }
19602
- function messageText(content) {
19603
- if (typeof content === "string") return content;
19604
- if (!Array.isArray(content)) return "";
19605
- return content.filter(
19606
- (part) => part?.type === "text"
19607
- ).map((part) => part.text).join("\n").trim();
19608
- }
19609
- function parseDataUrlToInlineData(url) {
19610
- const match7 = /^data:([A-Za-z0-9.+-]+\/[A-Za-z0-9.+-]+);base64,([A-Za-z0-9+/=]*)$/.exec(
19611
- url
19612
- );
19613
- if (!match7) return null;
19614
- return { mimeType: match7[1], data: match7[2] };
19615
- }
19616
- function extractInlineDataParts(content) {
19617
- if (!Array.isArray(content)) return [];
19618
- const parts2 = [];
19619
- for (const part of content) {
19620
- if (!part || typeof part !== "object") continue;
19621
- if (part.type !== "image_url") continue;
19622
- const url = part.image_url?.url;
19623
- if (typeof url !== "string" || !url.trim()) continue;
19624
- const inline = parseDataUrlToInlineData(url);
19625
- if (inline) parts2.push({ inlineData: inline });
19626
- }
19627
- return parts2;
19628
- }
19629
- function convertOpenAiMessagesToGemini(messages, options) {
19630
- const contents = [];
19631
- const systemTexts = [];
19632
- let toolCallIndex = 0;
19633
- let pendingFunctionCalls = [];
19634
- const pushOrMergeContent = (role, parts2) => {
19635
- if (parts2.length === 0) return;
19636
- const last = contents[contents.length - 1];
19637
- if (last && last.role === role) {
19638
- last.parts.push(...parts2);
19639
- } else {
19640
- contents.push({ role, parts: parts2 });
19641
- }
19642
- };
19643
- const flushPendingFunctionCalls = () => {
19644
- if (pendingFunctionCalls.length === 0) return;
19645
- const dummyResponses = pendingFunctionCalls.map(
19646
- ({ name, id }) => ({
19647
- functionResponse: {
19648
- name,
19649
- response: { output: "{}" },
19650
- ...id ? { id } : {}
19651
- }
19652
- })
19653
- );
19654
- pendingFunctionCalls = [];
19655
- pushOrMergeContent("user", dummyResponses);
19656
- };
19657
- for (const raw of messages) {
19658
- if (!raw || typeof raw !== "object" || !("role" in raw)) continue;
19659
- const role = String(raw.role);
19660
- const content = "content" in raw ? raw.content : null;
19661
- if (role === "system") {
19662
- const text = messageText(
19663
- content
19664
- );
19665
- if (text) systemTexts.push(text);
19666
- continue;
19667
- }
19668
- if (role === "user") {
19669
- flushPendingFunctionCalls();
19670
- const text = messageText(
19671
- content
19672
- );
19673
- const imageParts = extractInlineDataParts(
19674
- content
19675
- );
19676
- if (!text && imageParts.length === 0) continue;
19677
- const parts2 = [];
19678
- if (text) parts2.push({ text });
19679
- for (const img of imageParts) parts2.push(img);
19680
- pushOrMergeContent("user", parts2);
19681
- continue;
19682
- }
19683
- if (role === "assistant") {
19684
- flushPendingFunctionCalls();
19685
- const text = messageText(
19686
- content
19687
- );
19688
- const toolCalls = "tool_calls" in raw && Array.isArray(raw.tool_calls) ? raw.tool_calls ?? [] : [];
19689
- const parts2 = [];
19690
- if (text) {
19691
- parts2.push({ text });
19692
- }
19693
- for (const call of toolCalls) {
19694
- const name = call?.function?.name?.trim();
19695
- if (!name) continue;
19696
- const id = call.id?.trim() || `${name}_${toolCallIndex++}`;
19697
- pendingFunctionCalls.push({ name, id });
19698
- const realSignature = typeof call.thought_signature === "string" && call.thought_signature ? call.thought_signature : void 0;
19699
- const isFirstFunctionCallPart = !parts2.some(
19700
- (p) => "functionCall" in p
19701
- );
19702
- const signature = realSignature ?? (options.attachSkipThoughtSignature && isFirstFunctionCallPart ? SKIP_THOUGHT_SIGNATURE : void 0);
19703
- const part = {
19704
- functionCall: {
19705
- name,
19706
- args: parseToolArgs(call.function?.arguments),
19707
- ...id ? { id } : {}
19708
- },
19709
- ...signature ? { thoughtSignature: signature } : {}
19710
- };
19711
- parts2.push(part);
19712
- }
19713
- pushOrMergeContent("model", parts2);
19714
- continue;
19715
- }
19716
- if (role === "tool") {
19717
- const toolCallId = "tool_call_id" in raw && typeof raw.tool_call_id === "string" ? raw.tool_call_id : "";
19718
- const rawName = "name" in raw && typeof raw.name === "string" ? raw.name.trim() : "";
19719
- const pendingIndex = pendingFunctionCalls.findIndex(
19720
- (p) => toolCallId ? p.id === toolCallId : rawName ? p.name === rawName : true
19721
- );
19722
- if (pendingIndex === -1) continue;
19723
- const [{ name, id }] = pendingFunctionCalls.splice(pendingIndex, 1);
19724
- const output = messageText(content) || "{}";
19725
- pushOrMergeContent("user", [
19726
- {
19727
- functionResponse: {
19728
- name,
19729
- response: { output },
19730
- // 保留 OpenAI tool_call_id:antigravity Cloud Code Assist 网关把
19731
- // Gemini contents 转成 Claude messages 时需要 functionResponse →
19732
- // tool_result 的 tool_use_id;缺 id 时网关报
19733
- // "tool_result.tool_use_id: Field required"(HTTP 400)。
19734
- ...id ? { id } : {}
19735
- }
19736
- }
19737
- ]);
19738
- }
19739
- }
19740
- flushPendingFunctionCalls();
19741
- if (contents.length > 0 && contents[0]?.role === "model") {
19742
- contents.unshift({
19743
- role: "user",
19744
- parts: [{ text: "Continue the conversation." }]
19745
- });
19746
- }
19747
- return { contents, systemTexts };
19748
- }
19749
- function parseToolArgs(raw) {
19750
- if (typeof raw === "string") {
19751
- try {
19752
- const parsed = JSON.parse(raw);
19753
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
19754
- return parsed;
19755
- }
19756
- } catch {
19757
- }
19758
- }
19759
- if (raw && typeof raw === "object" && !Array.isArray(raw)) {
19760
- return raw;
19761
- }
19762
- return {};
19763
- }
19764
- function sanitizeGeminiSchemaNode(node, active) {
19765
- const out = {};
19766
- if (!node || typeof node !== "object" || Array.isArray(node)) {
19767
- return { type: "string" };
19768
- }
19769
- if (active?.has(node)) {
19770
- return { type: "object", description: "(circular schema reference)" };
19771
- }
19772
- const seen = active ?? /* @__PURE__ */ new WeakSet();
19773
- seen.add(node);
19774
- try {
19775
- return sanitizeGeminiSchemaNodeInner(node, out, seen);
19776
- } finally {
19777
- seen.delete(node);
19778
- }
19779
- }
19780
- function sanitizeGeminiSchemaNodeInner(src, out, seen) {
19781
- if (Array.isArray(src.type)) {
19782
- const firstNonNull = src.type.find((t) => t !== "null");
19783
- if (typeof firstNonNull === "string") out.type = firstNonNull;
19784
- out.nullable = true;
19785
- } else if (typeof src.type === "string") {
19786
- out.type = src.type;
19787
- }
19788
- if (typeof src.description === "string") out.description = src.description;
19789
- if (typeof src.minimum === "number") out.minimum = src.minimum;
19790
- if (typeof src.maximum === "number") out.maximum = src.maximum;
19791
- if (Array.isArray(src.enum) && src.enum.length > 0) out.enum = src.enum;
19792
- if (src.nullable === true) out.nullable = true;
19793
- if ("const" in src && src.const !== void 0 && !out.enum) {
19794
- out.enum = [src.const];
19795
- }
19796
- const variantBranches = [
19797
- ...Array.isArray(src.oneOf) ? src.oneOf : [],
19798
- ...Array.isArray(src.anyOf) ? src.anyOf : []
19799
- ];
19800
- if (variantBranches.length > 0) {
19801
- const mergedProperties = {};
19802
- let requiredIntersection;
19803
- for (const branch of variantBranches) {
19804
- const sanitized = sanitizeGeminiSchemaNode(branch, seen);
19805
- const branchProperties = sanitized.properties ?? {};
19806
- for (const [key, value] of Object.entries(branchProperties)) {
19807
- const previous = mergedProperties[key];
19808
- const prevEnum = previous?.enum;
19809
- const nextEnum = value?.enum;
19810
- if (prevEnum && nextEnum) {
19811
- mergedProperties[key] = {
19812
- ...value,
19813
- enum: [.../* @__PURE__ */ new Set([...prevEnum, ...nextEnum])]
19814
- };
19815
- } else {
19816
- mergedProperties[key] = value;
19817
- }
19818
- }
19819
- const branchRequired = Array.isArray(sanitized.required) ? sanitized.required : [];
19820
- requiredIntersection = requiredIntersection === void 0 ? [...branchRequired] : requiredIntersection.filter((r) => branchRequired.includes(r));
19821
- }
19822
- out.properties = {
19823
- ...out.properties ?? {},
19824
- ...mergedProperties
19825
- };
19826
- if (requiredIntersection !== void 0 && requiredIntersection.length > 0) {
19827
- out.required = requiredIntersection;
19828
- }
19829
- if (typeof out.type !== "string") out.type = "object";
19830
- }
19831
- if (Array.isArray(src.allOf)) {
19832
- for (const branch of src.allOf) {
19833
- const sanitized = sanitizeGeminiSchemaNode(branch, seen);
19834
- out.properties = {
19835
- ...out.properties ?? {},
19836
- ...sanitized.properties ?? {}
19837
- };
19838
- if (Array.isArray(sanitized.required)) {
19839
- out.required = [
19840
- .../* @__PURE__ */ new Set([
19841
- ...out.required ?? [],
19842
- ...sanitized.required ?? []
19843
- ])
19844
- ];
19845
- }
19846
- }
19847
- if (typeof out.type !== "string") out.type = "object";
19848
- }
19849
- if (src.properties && typeof src.properties === "object") {
19850
- const properties = {};
19851
- for (const [key, value] of Object.entries(
19852
- src.properties
19853
- )) {
19854
- properties[key] = sanitizeGeminiSchemaNode(value, seen);
19855
- }
19856
- out.properties = properties;
19857
- if (typeof out.type !== "string") out.type = "object";
19858
- }
19859
- if (Array.isArray(src.required)) {
19860
- const properties = out.properties ?? {};
19861
- const required = [...new Set(src.required)].filter(
19862
- (r) => typeof r === "string" && r in properties
19863
- );
19864
- if (required.length > 0) out.required = required;
19865
- }
19866
- if (src.items !== void 0) {
19867
- out.items = sanitizeGeminiSchemaNode(src.items, seen);
19868
- if (typeof out.type !== "string") out.type = "array";
19869
- }
19870
- if (typeof out.type !== "string") out.type = "object";
19871
- return out;
19872
- }
19873
- function convertOpenAiToolsToGemini(tools) {
19874
- if (!Array.isArray(tools) || tools.length === 0) return void 0;
19875
- const declarations = [];
19876
- for (const tool of tools) {
19877
- if (!tool || typeof tool !== "object" || !("function" in tool)) continue;
19878
- const fn3 = tool.function;
19879
- const name = typeof fn3.name === "string" ? fn3.name : "";
19880
- if (!name) continue;
19881
- declarations.push({
19882
- name,
19883
- description: typeof fn3.description === "string" ? fn3.description : "",
19884
- parameters: sanitizeGeminiSchemaNode(
19885
- fn3.parameters ?? { type: "object", properties: {} }
19886
- )
19887
- });
19888
- }
19889
- if (declarations.length === 0) return void 0;
19890
- return [{ functionDeclarations: declarations }];
19891
- }
19892
- function createGeminiAccumulatorState() {
19893
- return {
19894
- text: "",
19895
- toolCalls: [],
19896
- reasoning: "",
19897
- events: []
19898
- };
19899
- }
19900
- function applyGeminiChunk(chunk, state, options) {
19901
- if (!chunk || typeof chunk !== "object") return;
19902
- const response = "response" in chunk && chunk.response && typeof chunk.response === "object" ? chunk.response : chunk;
19903
- if ("usageMetadata" in response && response.usageMetadata && typeof response.usageMetadata === "object") {
19904
- const meta = response.usageMetadata;
19905
- const prompt = typeof meta.promptTokenCount === "number" ? meta.promptTokenCount : 0;
19906
- const candidates2 = typeof meta.candidatesTokenCount === "number" ? meta.candidatesTokenCount : 0;
19907
- const total = typeof meta.totalTokenCount === "number" ? meta.totalTokenCount : prompt + candidates2;
19908
- const cached3 = typeof meta.cachedContentTokenCount === "number" && Number.isFinite(meta.cachedContentTokenCount) ? (
19909
- // cached ⊆ prompt;对畸形上游防御性钳位,避免 cache_read > input 破坏归一化与计费
19910
- Math.min(Math.max(0, Math.floor(meta.cachedContentTokenCount)), prompt)
19911
- ) : 0;
19912
- state.usage = {
19913
- prompt_tokens: prompt,
19914
- completion_tokens: candidates2,
19915
- total_tokens: total,
19916
- ...cached3 > 0 ? { cache_read_input_tokens: cached3 } : {}
19917
- };
19918
- state.events.push({ kind: "usage", usage: state.usage });
19919
- }
19920
- const candidates = Array.isArray(response.candidates) ? response.candidates : [];
19921
- for (const candidate of candidates) {
19922
- if (!candidate || typeof candidate !== "object") continue;
19923
- const candidateFinishReason = candidate.finishReason;
19924
- if (typeof candidateFinishReason === "string" && candidateFinishReason.trim()) {
19925
- state.finishReason = candidateFinishReason.trim();
19926
- state.events.push({ kind: "finish", reason: state.finishReason });
19927
- }
19928
- const parts2 = Array.isArray(
19929
- candidate.content?.parts
19930
- ) ? candidate.content.parts : [];
19931
- for (const part of parts2) {
19932
- if (!part || typeof part !== "object") continue;
19933
- const partSignature = part.thoughtSignature;
19934
- const isThought = Boolean(part.thought);
19935
- if ("text" in part && typeof part.text === "string") {
19936
- const piece = part.text;
19937
- if (!isThought) {
19938
- state.text += piece;
19939
- if (piece) {
19940
- state.events.push({ kind: "text", text: piece });
19941
- if (options?.onTextDelta) options.onTextDelta(piece);
19942
- }
19943
- } else {
19944
- state.reasoning += piece;
19945
- if (piece) {
19946
- state.events.push({ kind: "reasoning", text: piece });
19947
- if (options?.onReasoningDelta) options.onReasoningDelta(piece);
19948
- }
19949
- }
19950
- }
19951
- if (isThought && typeof partSignature === "string" && partSignature) {
19952
- state.pendingThoughtSignature = partSignature;
19953
- }
19954
- if ("functionCall" in part && part.functionCall) {
19955
- const call = part.functionCall;
19956
- const name = typeof call.name === "string" ? call.name : "tool";
19957
- const id = typeof call.id === "string" ? call.id : `${name}_${state.toolCalls.length}`;
19958
- const argsObj = call.args && typeof call.args === "object" ? call.args : {};
19959
- const resolvedSignature = typeof partSignature === "string" && partSignature ? partSignature : state.pendingThoughtSignature;
19960
- state.pendingThoughtSignature = void 0;
19961
- const toolCall = {
19962
- id,
19963
- type: "function",
19964
- function: { name, arguments: JSON.stringify(argsObj) },
19965
- ...typeof resolvedSignature === "string" && resolvedSignature ? { thought_signature: resolvedSignature } : {}
19966
- };
19967
- state.toolCalls.push(toolCall);
19968
- state.events.push({ kind: "tool_call", toolCall });
19969
- }
19970
- }
19971
- }
19972
- }
19973
- function accumulateGeminiChunks(chunks2, options) {
19974
- const state = createGeminiAccumulatorState();
19975
- for (const chunk of chunks2) {
19976
- applyGeminiChunk(chunk, state, options);
19977
- }
19978
- return {
19979
- text: state.text,
19980
- toolCalls: state.toolCalls,
19981
- usage: state.usage,
19982
- finishReason: state.finishReason,
19983
- reasoningContent: state.reasoning,
19984
- events: state.events
19985
- };
19986
- }
19987
- async function accumulateGeminiStream(stream, options) {
19988
- const state = createGeminiAccumulatorState();
19989
- for await (const chunk of stream) {
19990
- applyGeminiChunk(chunk, state, options);
19991
- }
19992
- return {
19993
- text: state.text,
19994
- toolCalls: state.toolCalls,
19995
- usage: state.usage,
19996
- finishReason: state.finishReason,
19997
- reasoningContent: state.reasoning,
19998
- events: state.events
19999
- };
20000
- }
20001
- function shouldUseGeminiNativeToolRoute(provider, model, tools, isImageModel) {
20002
- return provider === "google" && !isImageModel(model) && isGemini3Model(model) && Array.isArray(tools) && tools.length > 0;
20003
- }
20004
- function buildGeminiGenerateContentRequest(args2) {
20005
- const { contents, systemTexts } = convertOpenAiMessagesToGemini(
20006
- args2.messages,
20007
- { attachSkipThoughtSignature: args2.attachSkipThoughtSignature }
20008
- );
20009
- const request3 = { contents };
20010
- if (systemTexts.length > 0) {
20011
- request3.systemInstruction = {
20012
- role: "user",
20013
- parts: systemTexts.map((text) => ({ text }))
20014
- };
20015
- }
20016
- const toolsResult = convertOpenAiToolsToGemini(args2.tools);
20017
- if (toolsResult) {
20018
- request3.tools = toolsResult;
20019
- request3.toolConfig = { functionCallingConfig: { mode: "VALIDATED" } };
20020
- }
20021
- const generationConfig = {};
20022
- if (typeof args2.maxTokens === "number" && args2.maxTokens > 0) {
20023
- generationConfig.maxOutputTokens = args2.maxTokens;
20024
- }
20025
- if (typeof args2.temperature === "number") {
20026
- generationConfig.temperature = args2.temperature;
20027
- }
20028
- if (Object.keys(generationConfig).length > 0) {
20029
- request3.generationConfig = generationConfig;
20030
- }
20031
- return request3;
20032
- }
20033
-
20034
- // packages/agent-runtime/antigravityCloudCodeProvider.ts
20035
- var STREAM_PATH = "/v1internal:streamGenerateContent?alt=sse";
20036
- function buildCloudCodeAssistPayload(args2) {
20037
- const projectId = readAntigravityProjectId(args2.metadata);
20038
- if (!projectId) {
20039
- throw new Error(
20040
- "Antigravity OAuth credential is missing metadata.projectId. Re-run `nolo auth antigravity`."
20041
- );
20042
- }
20043
- const logicalModel = asOptionalTrimmedString(args2.openAiBody.model) ?? asOptionalTrimmedString(args2.agentConfig.model) ?? "gemini-3.1-pro";
20044
- const { wireModelId: model, profile } = resolveAntigravityWireModel(logicalModel);
20045
- const rawMessages = Array.isArray(args2.openAiBody.messages) ? args2.openAiBody.messages : [];
20046
- const quirks = resolveGeminiModelQuirks(model);
20047
- const isClaude = quirks.isClaudeCrossModel;
20048
- const { contents, systemTexts } = convertOpenAiMessagesToGemini(rawMessages, {
20049
- attachSkipThoughtSignature: quirks.allowsThoughtSignatureSentinel
20050
- });
20051
- if (contents.length === 0) {
20052
- throw new Error("Antigravity Cloud Code Assist request has no user/model contents.");
20053
- }
20054
- const prompt = args2.agentConfig.prompt?.trim();
20055
- if (prompt) systemTexts.unshift(prompt);
20056
- const request3 = { contents };
20057
- if (systemTexts.length > 0) {
20058
- request3.systemInstruction = {
20059
- role: "user",
20060
- parts: systemTexts.map((text) => ({ text }))
20061
- };
20062
- }
20063
- const tools = convertOpenAiToolsToGemini(
20064
- Array.isArray(args2.openAiBody.tools) ? args2.openAiBody.tools : void 0
20065
- );
20066
- if (tools) {
20067
- request3.tools = tools;
20068
- request3.toolConfig = { functionCallingConfig: { mode: "VALIDATED" } };
20069
- }
20070
- const generationConfig = {};
20071
- if (profile?.maxOutputTokens) {
20072
- generationConfig.maxOutputTokens = profile.maxOutputTokens;
20073
- } else if (typeof args2.agentConfig.max_tokens === "number" && args2.agentConfig.max_tokens > 0) {
20074
- generationConfig.maxOutputTokens = args2.agentConfig.max_tokens;
20075
- }
20076
- if (typeof args2.agentConfig.temperature === "number") {
20077
- generationConfig.temperature = args2.agentConfig.temperature;
20078
- }
20079
- if (Object.keys(generationConfig).length > 0) {
20080
- request3.generationConfig = generationConfig;
20081
- }
20082
- const agentId = randomUUID();
20083
- const trajectoryId = randomUUID();
20084
- const step = 2;
20085
- const requestId = `agent/${agentId}/${Date.now()}/${trajectoryId}/${step}`;
20086
- const labels = {
20087
- trajectory_id: trajectoryId,
20088
- last_step_index: String(step - 1),
20089
- used_claude: String(isClaude),
20090
- used_claude_conservative: String(isClaude)
20091
- };
20092
- if (profile?.modelEnum) {
20093
- labels.model_enum = profile.modelEnum;
20094
- }
20095
- request3.labels = labels;
20096
- request3.sessionId = `-${Math.floor(Math.random() * 9e15)}`;
20097
- return {
20098
- url: `${resolveAntigravityCloudCodeBaseUrl(args2.agentConfig.customProviderUrl)}${STREAM_PATH}`,
20099
- envelope: {
20100
- project: projectId,
20101
- model,
20102
- request: request3,
20103
- requestId,
20104
- requestType: "agent",
20105
- userAgent: "antigravity"
20106
- }
20107
- };
20108
- }
20109
- function resolveAntigravityFinishReason(upstream) {
20110
- const normalized = (upstream ?? "").trim().toUpperCase();
20111
- if (normalized === "MAX_TOKENS") return "length";
20112
- if (normalized === "SAFETY" || normalized === "RECITATION" || normalized === "PROHIBITED_CONTENT" || normalized === "BLOCKLIST") {
20113
- return "content_filter";
20114
- }
20115
- return "stop";
20116
- }
20117
- function normalizeAntigravityCompletion(decoded) {
20118
- const { text, toolCalls, usage, finishReason, reasoningContent, events } = decoded;
20119
- const normalizedFinishReason = (finishReason ?? "").trim().toUpperCase();
20120
- if (!text && !reasoningContent && toolCalls.length === 0 && !finishReason && !usage) {
20121
- const body = {
20122
- error: {
20123
- message: "antigravity upstream returned an empty stream (no content, finishReason or usage); channel degradation, not model emptiness"
20124
- },
20125
- provider_events: events
20126
- };
20127
- return {
20128
- status: 502,
20129
- text: "",
20130
- toolCalls: [],
20131
- providerEvents: events,
20132
- body
20133
- };
20134
- }
20135
- if (normalizedFinishReason === "MALFORMED_FUNCTION_CALL") {
20136
- const message2 = "antigravity upstream returned MALFORMED_FUNCTION_CALL (model generated an invalid function call or tool arguments were rejected by upstream)";
20137
- const providerFailure = {
20138
- kind: "provider_failure",
20139
- provider: "antigravity",
20140
- code: "MALFORMED_FUNCTION_CALL",
20141
- message: message2,
20142
- providerReason: "MALFORMED_FUNCTION_CALL",
20143
- retryable: false,
20144
- status: 502
20145
- };
20146
- return {
20147
- status: 502,
20148
- text: "",
20149
- toolCalls: [],
20150
- finishReason,
20151
- providerEvents: events,
20152
- providerFailure,
20153
- runtimeProviderFailure: {
20154
- message: providerFailure.message,
20155
- retryable: providerFailure.retryable
20156
- },
20157
- body: {
20158
- error: {
20159
- message: message2,
20160
- code: "MALFORMED_FUNCTION_CALL",
20161
- category: "malformed_function_call",
20162
- providerReason: "MALFORMED_FUNCTION_CALL",
20163
- retryable: false
20164
- },
20165
- provider_failure: providerFailure,
20166
- provider_events: events
20167
- }
20168
- };
20169
- }
20170
- const message = {
20171
- role: "assistant",
20172
- content: text || null
20173
- };
20174
- if (toolCalls.length > 0) message.tool_calls = toolCalls;
20175
- if (reasoningContent) message.reasoning_content = reasoningContent;
20176
- return {
20177
- status: 200,
20178
- text,
20179
- ...reasoningContent ? { reasoningContent } : {},
20180
- toolCalls,
20181
- ...usage ? { usage } : {},
20182
- ...finishReason ? { finishReason } : {},
20183
- providerEvents: events,
20184
- body: {
20185
- choices: [{
20186
- index: 0,
20187
- message,
20188
- finish_reason: toolCalls.length > 0 ? "tool_calls" : resolveAntigravityFinishReason(finishReason)
20189
- }],
20190
- ...usage ? { usage } : {},
20191
- provider_events: events
20192
- }
20193
- };
20194
- }
20195
- async function fetchAntigravityCloudCodeCompletion(args2) {
20196
- const fetchImpl = args2.fetchImpl ?? fetch;
20197
- const { url, envelope } = buildCloudCodeAssistPayload(args2);
20198
- const response = await fetchImpl(url, {
20199
- method: "POST",
20200
- headers: {
20201
- Authorization: `Bearer ${args2.accessToken}`,
20202
- "Content-Type": "application/json",
20203
- "User-Agent": getAntigravityUserAgent()
20204
- },
20205
- body: JSON.stringify(envelope),
20206
- signal: args2.signal
20207
- });
20208
- if (!response.ok) {
20209
- const errorText = await response.text();
20210
- return {
20211
- status: response.status,
20212
- text: "",
20213
- toolCalls: [],
20214
- providerEvents: [],
20215
- // 保留上游结构:Antigravity 的限流 body 同样携带结构化的重置信息,
20216
- // 压成字符串会让冷却退化成 5 分钟默认值。
20217
- body: parseUpstreamErrorBody(errorText, response.statusText)
20218
- };
20219
- }
20220
- const chunkStream = streamSseDataValues(response, parseSseDataLineJson);
20221
- const { text, toolCalls, usage, finishReason, reasoningContent, events } = await accumulateGeminiStream(chunkStream, {
20222
- onTextDelta: args2.onTextDelta,
20223
- onReasoningDelta: args2.onReasoningDelta
20224
- });
20225
- const normalized = normalizeAntigravityCompletion({
20226
- text,
20227
- toolCalls,
20228
- usage,
20229
- finishReason,
20230
- reasoningContent,
20231
- events
20232
- });
20233
- if (normalized.status !== 200 || !text && toolCalls.length === 0) {
20234
- console.warn(
20235
- `[antigravity] empty completion: finishReason=${JSON.stringify(finishReason)} reasoningLen=${reasoningContent?.length ?? 0} usage=${JSON.stringify(usage) ?? "none"} model=${String(envelope.model)} base=${new URL(url).host}`
20236
- );
20237
- }
20238
- return normalized;
20130
+ // packages/agent-runtime/sessionIdentity.ts
20131
+ import { createHash as createHash4 } from "node:crypto";
20132
+ var SALT = "nolo-session-identity-v1";
20133
+ function isStableSessionIdentityEnabled(env = process.env) {
20134
+ const v = env.NOLO_STABLE_SESSION_IDENTITY?.trim().toLowerCase();
20135
+ return !(v === "0" || v === "false" || v === "off");
20136
+ }
20137
+ function deriveStableUuid(sessionKey, purpose) {
20138
+ const h = createHash4("sha256").update(`${SALT}\0${purpose}\0${sessionKey}`).digest();
20139
+ h[6] = h[6] & 15 | 80;
20140
+ h[8] = h[8] & 63 | 128;
20141
+ const x = h.subarray(0, 16).toString("hex");
20142
+ return `${x.slice(0, 8)}-${x.slice(8, 12)}-${x.slice(12, 16)}-${x.slice(16, 20)}-${x.slice(20, 32)}`;
20143
+ }
20144
+ function resolveSessionKey(args2) {
20145
+ const d = args2.dialogId?.trim();
20146
+ return d ? `dialog:${d}` : void 0;
20239
20147
  }
20240
20148
 
20241
20149
  // packages/agent-runtime/devin/devinProvider.ts
20242
20150
  import { randomBytes, randomUUID as randomUUID2 } from "node:crypto";
20243
20151
  var CLIENT_NAME = "chisel";
20244
- var CLIENT_VERSION = "2026.8.18";
20152
+ var CLIENT_VERSION = "3000.11.3";
20245
20153
  function writeVarint(fieldNum, value) {
20246
20154
  const tag = fieldNum << 3 | 0;
20247
20155
  const bytes = [];
@@ -20298,10 +20206,13 @@ function encodeDevinClientMetadata(token) {
20298
20206
  writeStringField(31, generateFingerprint())
20299
20207
  ]);
20300
20208
  }
20301
- function encodeDevinChatMessage(msg) {
20209
+ function devinId(id, purpose) {
20210
+ return id?.sessionKey && isStableSessionIdentityEnabled() ? deriveStableUuid(id.sessionKey, purpose) : randomUUID2();
20211
+ }
20212
+ function encodeDevinChatMessage(msg, identity2, index = 0) {
20302
20213
  const source = msg.role === "assistant" ? 2 : msg.role === "tool" ? 4 : 1;
20303
20214
  return Buffer.concat([
20304
- writeStringField(1, randomUUID2()),
20215
+ writeStringField(1, devinId(identity2, `msg:${index}:${msg.role}`)),
20305
20216
  writeVarint(2, source),
20306
20217
  writeStringField(3, msg.text),
20307
20218
  ...(msg.toolCalls ?? []).map(
@@ -20357,6 +20268,7 @@ function buildCompletionConfig(params) {
20357
20268
  }
20358
20269
  function buildGetChatMessageRequest(params) {
20359
20270
  const { token, messages, model, tools = [], temperature } = params;
20271
+ const identity2 = { sessionKey: params.sessionKey };
20360
20272
  const metadata = encodeDevinClientMetadata(token);
20361
20273
  let systemPrompt = "";
20362
20274
  const filteredMessages = [];
@@ -20386,7 +20298,7 @@ ${item.text}`;
20386
20298
  coalesced.push({ ...item });
20387
20299
  }
20388
20300
  }
20389
- const chatMessageBuffers = coalesced.map((m) => encodeDevinChatMessage(m));
20301
+ const chatMessageBuffers = coalesced.map((m, i) => encodeDevinChatMessage(m, identity2, i));
20390
20302
  const completionConfig = buildCompletionConfig({ temperature });
20391
20303
  const modelConfig = Buffer.concat([
20392
20304
  writeStringField(1, randomUUID2()),
@@ -20616,6 +20528,11 @@ function decodeDevinResponsePayload(payload) {
20616
20528
  }
20617
20529
  return { content, reasoning, finishReason, events, usage };
20618
20530
  }
20531
+ function isMeaningfulDevinEvent(event) {
20532
+ if (event.type === "reasoning" || event.type === "content") return event.text.length > 0;
20533
+ if (event.type === "tool-call-start") return !!event.name;
20534
+ return event.argsDelta.length > 0;
20535
+ }
20619
20536
  function createDevinProvider(options) {
20620
20537
  const { token, model = "swe-2-max", fetchImpl = fetch, tools = [], temperature } = options;
20621
20538
  return {
@@ -20627,9 +20544,11 @@ function createDevinProvider(options) {
20627
20544
  messages,
20628
20545
  model: selectedModel,
20629
20546
  tools,
20630
- temperature
20547
+ temperature,
20548
+ sessionKey: resolveSessionKey({ dialogId: opts?.dialogId })
20631
20549
  });
20632
20550
  const frame = buildDevinConnectFrame(requestPayload);
20551
+ const timingTracker = createProviderCallTimingTracker();
20633
20552
  const response = await fetchImpl(DEVIN_CONNECT_URL, {
20634
20553
  method: "POST",
20635
20554
  headers: {
@@ -20687,6 +20606,7 @@ function createDevinProvider(options) {
20687
20606
  reasoning += delta.reasoning;
20688
20607
  if (delta.finishReason) finishReason = delta.finishReason;
20689
20608
  for (const event of delta.events) {
20609
+ if (isMeaningfulDevinEvent(event)) observeMeaningfulProviderResponse(timingTracker);
20690
20610
  if (event.type === "reasoning") {
20691
20611
  opts?.onReasoningDelta?.(event.text);
20692
20612
  } else if (event.type === "content") {
@@ -20735,13 +20655,14 @@ function createDevinProvider(options) {
20735
20655
  if (!text && !reasoning && toolCalls.length === 0) {
20736
20656
  throw new Error("Devin Connect upstream completed without content or tool calls");
20737
20657
  }
20738
- const usage = frameUsage ? {
20658
+ const timing = finalizeProviderCallTiming(timingTracker);
20659
+ const usage = frameUsage ? withProviderCallTimingFields({
20739
20660
  prompt_tokens: frameUsage.prompt + frameUsage.cacheRead,
20740
20661
  completion_tokens: frameUsage.completion ?? 0,
20741
20662
  total_tokens: frameUsage.prompt + frameUsage.cacheRead + (frameUsage.completion ?? 0) + frameUsage.cacheWrite,
20742
20663
  ...frameUsage.cacheRead > 0 ? { prompt_tokens_details: { cached_tokens: frameUsage.cacheRead } } : {},
20743
20664
  ...frameUsage.cacheWrite > 0 ? { cache_creation_input_tokens: frameUsage.cacheWrite } : {}
20744
- } : void 0;
20665
+ }, timing) : void 0;
20745
20666
  return {
20746
20667
  content: text,
20747
20668
  model: selectedModel,
@@ -20763,20 +20684,16 @@ function createDevinProvider(options) {
20763
20684
  }
20764
20685
 
20765
20686
  export {
20687
+ readCommandActionGatePayload,
20688
+ readActionGate,
20766
20689
  Option_exports,
20767
20690
  Duration_exports,
20768
20691
  Exit_exports,
20769
20692
  Effect_exports,
20770
- readCommandActionGatePayload,
20771
- readActionGate,
20772
- evaluateFileWritePolicy,
20773
- summarizeToolArguments,
20774
20693
  formatToolOverflowMarker,
20775
20694
  LOCAL_AGENT_CONFIG_MISSING_CODE,
20776
20695
  LOCAL_TURN_ABORTED_CODE,
20777
20696
  runLocalAgentTurn,
20778
- parseSseDataLineJson,
20779
- parseSseDataLineObject,
20780
20697
  codexStreamFailureStatus,
20781
20698
  aggregateResponsesStream,
20782
20699
  buildOpenAiCompatibleChatCompletionRequest,
@@ -20792,8 +20709,6 @@ export {
20792
20709
  createHybridRecordStore,
20793
20710
  isDestructiveShellCommand,
20794
20711
  evaluateShellCommandPolicy,
20795
- resolveLocalToolPolicy,
20796
- executeLocalToolWithPolicy,
20797
20712
  filterRetainedLedgerRecords,
20798
20713
  isLineRangeCovered,
20799
20714
  getProcessRegistry,
@@ -20804,17 +20719,17 @@ export {
20804
20719
  internalSearchWorkspace,
20805
20720
  internalListWorkspaceEntries,
20806
20721
  createLocalWorkspaceToolExecutors,
20722
+ buildToolParametersIndex,
20723
+ getAgentRuntimeToolParametersIndex,
20724
+ resolveLocalToolPolicy,
20725
+ executeLocalToolWithPolicy,
20807
20726
  localDialogMessageRecordToRuntimeMessage,
20808
20727
  buildLocalDialogWritePlan,
20809
20728
  buildNoloWorkspaceCliToolExecutors,
20810
20729
  buildLoadSkillExecutor,
20811
- parseUpstreamErrorBody,
20812
- isGemini3Model,
20813
- accumulateGeminiChunks,
20814
- shouldUseGeminiNativeToolRoute,
20815
- buildGeminiGenerateContentRequest,
20816
- resolveAntigravityFinishReason,
20817
- fetchAntigravityCloudCodeCompletion,
20730
+ isStableSessionIdentityEnabled,
20731
+ deriveStableUuid,
20732
+ resolveSessionKey,
20818
20733
  readDevinUpstreamFailure,
20819
20734
  createDevinProvider
20820
20735
  };