nolo-cli 0.83.0-alpha.2 → 0.83.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 (161) hide show
  1. package/README.md +2 -2
  2. package/{_executeModel-TRYFRXDJ.js → _executeModel-LUNEV235.js} +48 -48
  3. package/{action-D22U6CTH.js → action-YVYQEAAV.js} +11 -11
  4. package/{addContentAction-XAPUTGL4.js → addContentAction-O7R66HEX.js} +9 -9
  5. package/{addDialogAgentAction-M7VLLBZM.js → addDialogAgentAction-74OA7KYP.js} +8 -8
  6. package/{agentDeleteCommand-YZ74ZWP5.js → agentDeleteCommand-2YAHAZFJ.js} +31 -30
  7. package/{agentEmailCommands-RMTYKFBY.js → agentEmailCommands-G6A7IRPQ.js} +67 -64
  8. package/{agentGrantCommands-R3VQEZJR.js → agentGrantCommands-M3XFO6EN.js} +2 -2
  9. package/{agentListCommands-5WZ7MGH4.js → agentListCommands-KMS675GZ.js} +40 -33
  10. package/{agentMachineCommands-U7QFPT5D.js → agentMachineCommands-W2SMNSQ3.js} +31 -30
  11. package/{agentProviderCommands-65ZC2WB4.js → agentProviderCommands-GNPTL5XR.js} +87 -0
  12. package/{agentPullCommand-N7L2CT7L.js → agentPullCommand-O5MPVJ5E.js} +3 -3
  13. package/agentRecordCommands-D7BRMFNI.js +128 -0
  14. package/{agentRunCommand-ZXEAWYLV.js → agentRunCommand-VL6MMEUB.js} +65 -62
  15. package/{agentRunControl-T44MESII.js → agentRunControl-AVJR4JW4.js} +2 -2
  16. package/{agentRuntimeLocal-PQEIUOJU.js → agentRuntimeLocal-VXZSDIGT.js} +27 -26
  17. package/{agentSlice-Q5XOM2PN.js → agentSlice-5YJHUO47.js} +10 -10
  18. package/{appDeleteCommands-UVLHOLVH.js → appDeleteCommands-32MHLR4L.js} +31 -30
  19. package/{appDeployCommands-NTKUPWIJ.js → appDeployCommands-WCQOUSOB.js} +31 -30
  20. package/{appDeployStatusCommands-SXJHAM6D.js → appDeployStatusCommands-6QKB7DYN.js} +31 -30
  21. package/{appGetCommands-MAV3RF4X.js → appGetCommands-SFAPP5PB.js} +31 -30
  22. package/{appListCommands-7HWANPQU.js → appListCommands-EKUBDRUO.js} +31 -30
  23. package/{authCommand-7N2G6MXP.js → authCommand-F4EFRRX6.js} +89 -24
  24. package/{authCommands-ILTBI6JV.js → authCommands-RGOGCPP6.js} +4 -5
  25. package/builtinSkillRegistry-FUAVTRVZ.js +33 -0
  26. package/{chatQueueReduxAdapter-WICMMQZT.js → chatQueueReduxAdapter-PQMRR2PK.js} +24 -24
  27. package/{chromeCommands-VOIRNJBL.js → chromeCommands-FZYAU75D.js} +1 -1
  28. package/{chromeOpsCommands-M7DPCX2T.js → chromeOpsCommands-62P2Q2YB.js} +31 -1
  29. package/{chunk-C3FZR5EC.js → chunk-2F7RJC6L.js} +1 -1
  30. package/{chunk-HQUORJTM.js → chunk-3BMYXEUW.js} +1 -1
  31. package/{chunk-3QPYFHAH.js → chunk-3DII57IT.js} +1 -1
  32. package/{chunk-NTW5GWDM.js → chunk-3VNWWVKB.js} +2 -0
  33. package/{chunk-DWUCB2A3.js → chunk-42V6BK2E.js} +1 -1
  34. package/{chunk-5G3IP3R6.js → chunk-45C5ER5U.js} +10 -10
  35. package/{chunk-DSRXPQWA.js → chunk-4OEFYTWL.js} +43 -43
  36. package/{chunk-6RUOYAPM.js → chunk-4S5QUMJP.js} +7 -7
  37. package/{chunk-GBPZKFYK.js → chunk-53HEEOTT.js} +4 -4
  38. package/{chunk-5Q5LYRJH.js → chunk-5FBTLSFG.js} +155 -795
  39. package/{chunk-KIS76A2P.js → chunk-5H7KJX7J.js} +38 -4
  40. package/{chunk-5XTOKQLW.js → chunk-5UTFL4QJ.js} +6 -2
  41. package/chunk-5ZUL4CUO.js +10 -0
  42. package/{chunk-E6RGHPDR.js → chunk-6D6VBVEW.js} +4 -6
  43. package/{chunk-KVDUCO4Q.js → chunk-6IG4XSS6.js} +16 -12
  44. package/{chunk-CMH2SYHX.js → chunk-6OZ36FXI.js} +2 -2
  45. package/{chunk-5BVAAVIF.js → chunk-6Q33GUCC.js} +1 -1
  46. package/{chunk-2OVE3GVB.js → chunk-6VFARHZW.js} +581 -298
  47. package/{chunk-BYEJ7K2Z.js → chunk-7S4YWN7P.js} +1 -1
  48. package/{chunk-SREASRV5.js → chunk-ABQYVJBJ.js} +1 -1
  49. package/chunk-ACTMNVHM.js +93 -0
  50. package/{chunk-NRALD3CC.js → chunk-AEOE4Z6Q.js} +54 -10
  51. package/{chunk-XWNRAIUL.js → chunk-AFUORTY4.js} +6 -16
  52. package/chunk-BNGW5ASC.js +344 -0
  53. package/{chunk-SZLVDREM.js → chunk-BP4XNZRM.js} +3 -3
  54. package/{chunk-JZYXSXMZ.js → chunk-BTI7PJDX.js} +15 -15
  55. package/{chunk-2KDR4CWE.js → chunk-BYMV6QZG.js} +24 -1
  56. package/{chunk-GCJ3IS5M.js → chunk-DHDMC6FO.js} +175 -6
  57. package/{chunk-E36HZARC.js → chunk-DNCEALKX.js} +93 -17
  58. package/{chunk-N5KGQ5AD.js → chunk-DRGSDQBL.js} +3 -3
  59. package/{chunk-D6XYLSOS.js → chunk-ETI2NYVF.js} +1 -1
  60. package/{chunk-EXMHJN7C.js → chunk-EYAQT56M.js} +4 -3
  61. package/{chunk-CJCJF5XB.js → chunk-FDTR4QJ6.js} +4 -2
  62. package/{chunk-JPVNC5B2.js → chunk-G2O6V3HJ.js} +3 -3
  63. package/{chunk-WL2RKIBF.js → chunk-GEPUP7OO.js} +1 -1
  64. package/{chunk-PYXL2CJL.js → chunk-GPCZ7UXG.js} +1 -1
  65. package/{chunk-K7AG465Q.js → chunk-GQBOR3G6.js} +4 -2
  66. package/{chunk-YFADVSZE.js → chunk-H62HCIUP.js} +6 -6
  67. package/{chunk-PZM3EENV.js → chunk-HJUYAD4A.js} +1 -1
  68. package/{chunk-3JQ2NRYU.js → chunk-HVURBGF3.js} +4 -4
  69. package/{chunk-HH65VA5U.js → chunk-I3TQJXQ7.js} +109 -6
  70. package/{chunk-ZVN6I33J.js → chunk-IIVGDG4I.js} +6 -5
  71. package/{chunk-UDQWGU7F.js → chunk-J4MOO56S.js} +1 -1
  72. package/{chunk-KC5FAHVS.js → chunk-JEDZAO7L.js} +1 -1
  73. package/{chunk-6XIE5ZGI.js → chunk-JPGCXHNM.js} +1 -1
  74. package/chunk-JRQFUXY7.js +1625 -0
  75. package/chunk-KQ4ONNQD.js +677 -0
  76. package/{chunk-CPSZJBLK.js → chunk-L5UXH3B2.js} +105 -26
  77. package/{chunk-RXX4GDIH.js → chunk-LS2ZANRJ.js} +1 -1
  78. package/{chunk-BJIRLHJR.js → chunk-M2EJHDJP.js} +4 -0
  79. package/{chunk-EVBFYPDC.js → chunk-MC4XLIJ7.js} +6 -2
  80. package/{chunk-DUJWWUGV.js → chunk-NA35QHCF.js} +1 -1
  81. package/{chunk-FNGAF7C4.js → chunk-OEVAY4J6.js} +137 -12
  82. package/{chunk-UJ63UXGE.js → chunk-OIVSQH2O.js} +1006 -801
  83. package/{chunk-LZIMZZYT.js → chunk-ORYWEJO4.js} +13 -15
  84. package/{chunk-LKSDMMY2.js → chunk-Q2XN3EOZ.js} +40 -30
  85. package/{chunk-AETZ7AD6.js → chunk-Q554LLSI.js} +1 -1
  86. package/{chunk-FMODTASJ.js → chunk-Q5FJJ6XI.js} +1 -7
  87. package/{chunk-AJHEFRGJ.js → chunk-Q67DOFOO.js} +214 -5
  88. package/{chunk-UZ6XXCVR.js → chunk-QDSINZM3.js} +15 -2
  89. package/{chunk-BHXK3KPV.js → chunk-QVKRR2SS.js} +2314 -66
  90. package/{chunk-BNQK6UPC.js → chunk-RFCKMYDK.js} +11 -173
  91. package/{chunk-YWG6EMH3.js → chunk-RG6JBBX5.js} +10 -6
  92. package/{chunk-QCZX4FTW.js → chunk-SH43IESU.js} +173 -15
  93. package/{chunk-LLJM5TW2.js → chunk-SSDAAEC2.js} +1235 -59
  94. package/{chunk-SPAQ4PVV.js → chunk-SV5IJK6K.js} +1 -1
  95. package/{chunk-BJCWNHVU.js → chunk-TOP2VXOR.js} +2 -2
  96. package/{chunk-4EJ5QYB5.js → chunk-UKZDAFYT.js} +1 -1
  97. package/{chunk-PGSKWZCB.js → chunk-UZGQNTVW.js} +1 -1
  98. package/{chunk-ZZCJUXUI.js → chunk-V4MJDUSR.js} +2 -2
  99. package/{chunk-JYYZCK2Z.js → chunk-V7E4PXQD.js} +1 -1
  100. package/{chunk-OZX2IPKM.js → chunk-VLTIYV3X.js} +1 -1
  101. package/{chunk-NHRQCXIQ.js → chunk-VMFTFOWF.js} +13 -3
  102. package/{chunk-5J7W2VWW.js → chunk-WV42OMV7.js} +6 -265
  103. package/{chunk-EH7BNTCR.js → chunk-X6DN5HBD.js} +6 -6
  104. package/{chunk-3447FUKM.js → chunk-YFE4A4O7.js} +731 -90
  105. package/{chunk-BOYUESO7.js → chunk-Z77CRGTW.js} +1 -1
  106. package/{chunk-QC5LGHYP.js → chunk-ZCYXJSTU.js} +10 -5
  107. package/{contentThunks-A2ZG3SW6.js → contentThunks-GJV22WVZ.js} +25 -25
  108. package/{cooldownCommands-BSCH2W7P.js → cooldownCommands-REAWYVTY.js} +2 -2
  109. package/{createAgentAutomationAction-QFQQPG35.js → createAgentAutomationAction-6V25TZEO.js} +25 -25
  110. package/{createDialogAction-WI73R5EQ.js → createDialogAction-SX36BGSX.js} +28 -28
  111. package/{createPageAction-XN237WPJ.js → createPageAction-IR4BGJSI.js} +26 -26
  112. package/{dbSlice-GKGGFNOB.js → dbSlice-XVWBAGYN.js} +8 -8
  113. package/{deleteFile-LWSR674W.js → deleteFile-YG6QMJLU.js} +3 -3
  114. package/{dialogCommands-LWKIIWKC.js → dialogCommands-5J56DFWN.js} +35 -32
  115. package/{dialogSlice-56G4Q4RU.js → dialogSlice-UNX2FAPZ.js} +24 -24
  116. package/{docCreateCommands-FC5IRW2I.js → docCreateCommands-KYI2DC2U.js} +32 -31
  117. package/{docDeleteCommands-GUGLMPIP.js → docDeleteCommands-67REUSRY.js} +33 -32
  118. package/{docReadCommands-JVMVMNQZ.js → docReadCommands-HOSVSBDJ.js} +32 -31
  119. package/{docStore-PZSZWXX3.js → docStore-JWVG5NDO.js} +9 -9
  120. package/{docUpdateCommands-XZRIQPK4.js → docUpdateCommands-EMGCSMXY.js} +32 -31
  121. package/{fetchMessages-DRABNDPZ.js → fetchMessages-EIU7UFFK.js} +3 -4
  122. package/{getPricing-HRMSOF3E.js → getPricing-LUSEABA6.js} +4 -4
  123. package/{handleSendMessageAction-MHKC4R3Q.js → handleSendMessageAction-B75UOJKF.js} +24 -24
  124. package/index.js +99 -86
  125. package/{localDialogRead-PUQKFI73.js → localDialogRead-BTFJ4V2I.js} +2 -2
  126. package/{localRuntimeDb-P37ZTX55.js → localRuntimeDb-SYQGUF4U.js} +1 -1
  127. package/{machineCommands-KSTY7PYK.js → machineCommands-GFPTOY6L.js} +64 -61
  128. package/{machineWsRunDispatch-MG6KR26V.js → machineWsRunDispatch-46PSZ3DJ.js} +62 -59
  129. package/{memberThunks-KATHH3F4.js → memberThunks-QGFLFVCI.js} +9 -9
  130. package/{models-GS72DVJS.js → models-T3SG6MJ5.js} +2 -2
  131. package/{offlineMarxistsAgentCommand-GEH6SNNA.js → offlineMarxistsAgentCommand-HGXZV4XJ.js} +1 -1
  132. package/package.json +1 -1
  133. package/providerPresetApply-FG6NZ57W.js +214 -0
  134. package/providerRegistry-757OWH3O.js +31 -0
  135. package/{read-LC6HVYBN.js → read-COE3ZYR6.js} +5 -4
  136. package/{readDocTool-N3ERJHHF.js → readDocTool-5USN4A6V.js} +1 -1
  137. package/{readlineWorkspace-FG4NFANI.js → readlineWorkspace-UFSWHFKD.js} +144 -109
  138. package/{removeDialogAgentAction-F5GPTALR.js → removeDialogAgentAction-IX3MN2ZO.js} +11 -11
  139. package/{runAgentClientLoop-U3GEPCQI.js → runAgentClientLoop-25LJ4XGV.js} +47 -47
  140. package/{runtimeDoctorCommands-CROTMULY.js → runtimeDoctorCommands-X6WDBEQX.js} +2 -2
  141. package/{setDialogExtraReferencesAction-AY4ZCF3A.js → setDialogExtraReferencesAction-X4HCPNZL.js} +9 -9
  142. package/{setPrimaryDialogAgentAction-HPMG7V3R.js → setPrimaryDialogAgentAction-4GMIQLOD.js} +11 -11
  143. package/{settingSlice-OEJA4NQO.js → settingSlice-ZQZVUPKK.js} +10 -10
  144. package/{spaceCommands-CB6SU7KK.js → spaceCommands-NKJVJCQQ.js} +30 -29
  145. package/{streamAgentChatTurn-DAGDJH3L.js → streamAgentChatTurn-A46QFV4O.js} +59 -58
  146. package/{switchDialogAgentAction-HMDZTZ4I.js → switchDialogAgentAction-N52LRPA3.js} +8 -8
  147. package/{tableCommands-SUNTHAYX.js → tableCommands-O6NWHFKV.js} +1 -1
  148. package/{toolApiClient-VGYUB4P5.js → toolApiClient-QVJ6HDNN.js} +2 -2
  149. package/{tools-73VPBNX3.js → tools-TTDGRNY6.js} +43 -43
  150. package/tuiSummaryLlmCaller-WFRWERGK.js +127 -0
  151. package/{updateCommands-Y363MC3L.js → updateCommands-2M74UL7P.js} +2 -2
  152. package/{updateDialogTitleAction-RFQONESQ.js → updateDialogTitleAction-QJPZGF52.js} +45 -28
  153. package/agentRecordCommands-RJRU2YVT.js +0 -125
  154. package/chunk-I42VC5KR.js +0 -15
  155. package/chunk-NB5SIWC4.js +0 -611
  156. package/chunk-NG4UT4K4.js +0 -79
  157. package/codingSkills-7S6523WH.js +0 -48
  158. package/tuiSummaryLlmCaller-3Z24VUWM.js +0 -116
  159. package/{chunk-3RAG2NAD.js → chunk-UKWGR5SS.js} +3 -3
  160. package/{db-VQSAOWAO.js → db-WK5MDZVN.js} +1 -1
  161. package/{localRuntimeAuthority-R5UBODT3.js → localRuntimeAuthority-6IR6BBXE.js} +3 -3
@@ -8,18 +8,19 @@ import {
8
8
  readSseFrames,
9
9
  streamSseDataValues,
10
10
  throwIfChatCompletionStreamFailed
11
- } from "./chunk-QC5LGHYP.js";
11
+ } from "./chunk-ZCYXJSTU.js";
12
12
  import {
13
13
  DEVIN_CONNECT_URL
14
- } from "./chunk-FMODTASJ.js";
14
+ } from "./chunk-Q5FJJ6XI.js";
15
15
  import {
16
16
  clipToolText,
17
17
  hasImageInRuntimeMessages,
18
+ resolveAgentContextWindow,
18
19
  resolveAgentImageInputSupport,
19
20
  resolveHistoricalToolContentCap,
20
21
  resolveToolOutputProfile,
21
22
  stripImagePartsFromMessages
22
- } from "./chunk-KIS76A2P.js";
23
+ } from "./chunk-5H7KJX7J.js";
23
24
  import {
24
25
  downgradeUnparsableToolCalls,
25
26
  hasParsableObjectArguments,
@@ -37,13 +38,15 @@ import {
37
38
  TOOL_GUIDED_SECTION_ORDER,
38
39
  buildIdentityBlock,
39
40
  buildRuntimeGuidanceBlocks,
41
+ detectDispatchIntent,
42
+ detectDispatchIntentFromMessages,
40
43
  estimateContextTokens,
41
44
  hashStablePrefixContent,
42
45
  normalizeChatCompletionsBodyForProvider,
43
46
  requiresBareImageUrl,
44
47
  resolveToolGuidedSections,
45
48
  toBareImageUrlMessages
46
- } from "./chunk-CPSZJBLK.js";
49
+ } from "./chunk-L5UXH3B2.js";
47
50
  import {
48
51
  convertMessagesToResponsesInput,
49
52
  extractReasoningFromResponseOutput,
@@ -67,16 +70,16 @@ import {
67
70
  parseNoloWorkspaceToolArguments,
68
71
  readTextStreamUpToChars,
69
72
  resolveNoloDialogInput
70
- } from "./chunk-ZVN6I33J.js";
73
+ } from "./chunk-IIVGDG4I.js";
71
74
  import {
72
75
  getAntigravityUserAgent,
73
76
  readAntigravityProjectId,
74
77
  resolveAntigravityCloudCodeBaseUrl
75
- } from "./chunk-PGSKWZCB.js";
78
+ } from "./chunk-UZGQNTVW.js";
76
79
  import {
77
80
  buildProviderAuthHeaders,
78
81
  buildProviderExecutionPlan
79
- } from "./chunk-CMH2SYHX.js";
82
+ } from "./chunk-6OZ36FXI.js";
80
83
  import {
81
84
  buildDialogFallbackTitleFromUserInput
82
85
  } from "./chunk-2MBTWINN.js";
@@ -90,7 +93,7 @@ import {
90
93
  resolveEmptyAssistantFallbackMessage,
91
94
  resolveEmptyAssistantOutcome,
92
95
  resolveTruncatedReasoningTailLog
93
- } from "./chunk-DSRXPQWA.js";
96
+ } from "./chunk-4OEFYTWL.js";
94
97
  import {
95
98
  COMPACTION_SUMMARY_SCHEMA_VERSION,
96
99
  COMPACTION_SUMMARY_SYSTEM_PROMPT,
@@ -109,7 +112,7 @@ import {
109
112
  resolveCompressionTriggerRatio,
110
113
  truncateToolOutputForContext,
111
114
  withProviderCallTimingFields
112
- } from "./chunk-E36HZARC.js";
115
+ } from "./chunk-DNCEALKX.js";
113
116
  import {
114
117
  estimateTokenCount
115
118
  } from "./chunk-PSCNXBYA.js";
@@ -133,27 +136,25 @@ import {
133
136
  asRecordOrEmpty
134
137
  } from "./chunk-KX6YBIHG.js";
135
138
  import {
139
+ asTrimmedNonEmptyStringArray,
136
140
  dialogMessageKey,
137
141
  dialogMessageRange
138
- } from "./chunk-NHRQCXIQ.js";
142
+ } from "./chunk-VMFTFOWF.js";
139
143
  import {
140
144
  asOptionalFiniteNumber
141
145
  } from "./chunk-WWC7I6TA.js";
142
- import {
143
- asTrimmedNonEmptyStringArray
144
- } from "./chunk-I42VC5KR.js";
145
146
  import {
146
147
  asTrimmedLowercaseString
147
148
  } from "./chunk-QSJ3HFEF.js";
149
+ import {
150
+ isRecord
151
+ } from "./chunk-KUG52IAP.js";
148
152
  import {
149
153
  asTrimmedString
150
154
  } from "./chunk-3EUW3WEO.js";
151
155
  import {
152
156
  asOptionalPositiveFiniteNumber
153
157
  } from "./chunk-U6VC4MU4.js";
154
- import {
155
- isRecord
156
- } from "./chunk-KUG52IAP.js";
157
158
  import {
158
159
  normalizeServerOrigin
159
160
  } from "./chunk-CF7PE763.js";
@@ -12203,8 +12204,208 @@ function summarizeToolArguments(toolName, rawArgs) {
12203
12204
  );
12204
12205
  }
12205
12206
 
12207
+ // packages/agent-runtime/progressGuard.ts
12208
+ var DEFAULT_MAX_CONSECUTIVE_IDENTICAL_ROUNDS = 5;
12209
+ var DEFAULT_MAX_CONSECUTIVE_STAGNANT_TOOL_ROUNDS = 8;
12210
+ function sortJsonKeys(obj) {
12211
+ if (obj === null || typeof obj !== "object") return obj;
12212
+ if (Array.isArray(obj)) return obj.map(sortJsonKeys);
12213
+ const sorted = {};
12214
+ for (const key of Object.keys(obj).sort()) {
12215
+ sorted[key] = sortJsonKeys(obj[key]);
12216
+ }
12217
+ return sorted;
12218
+ }
12219
+ function normalizeToolArguments(rawArgs) {
12220
+ if (typeof rawArgs !== "string") {
12221
+ try {
12222
+ return JSON.stringify(sortJsonKeys(rawArgs ?? ""));
12223
+ } catch {
12224
+ return String(rawArgs ?? "");
12225
+ }
12226
+ }
12227
+ const trimmed = rawArgs.trim();
12228
+ if (!trimmed) return "";
12229
+ try {
12230
+ const parsed = JSON.parse(trimmed);
12231
+ return JSON.stringify(sortJsonKeys(parsed));
12232
+ } catch {
12233
+ return trimmed;
12234
+ }
12235
+ }
12236
+ function extractNormalizedContentText(content) {
12237
+ if (typeof content === "string") return content.trim();
12238
+ if (!Array.isArray(content)) return "";
12239
+ return content.map((part) => {
12240
+ if (part?.type === "text") return String(part.text ?? "").trim();
12241
+ if (part?.type === "image_url") return `[img:${part.image_url?.url ?? ""}]`;
12242
+ return "";
12243
+ }).filter(Boolean).join("\n");
12244
+ }
12245
+ function buildToolCallsSignature(toolCalls) {
12246
+ if (!toolCalls || toolCalls.length === 0) return "";
12247
+ return toolCalls.map(
12248
+ (tc) => `${tc.function?.name ?? ""}:${normalizeToolArguments(tc.function?.arguments)}`
12249
+ ).join(";;");
12250
+ }
12251
+ function buildAssistantActionFingerprint(result4) {
12252
+ const content = extractNormalizedContentText(result4.content);
12253
+ const reasoning = typeof result4.reasoning_content === "string" ? result4.reasoning_content.trim() : "";
12254
+ let toolCallsSig = buildToolCallsSignature(result4.tool_calls);
12255
+ let hasToolCalls = Array.isArray(result4.tool_calls) && result4.tool_calls.length > 0;
12256
+ if (!toolCallsSig && Array.isArray(result4.output) && result4.output.length > 0) {
12257
+ const blocksToolCalls = result4.output.filter(
12258
+ (b) => b.type === "toolCall"
12259
+ ).map((b) => b.toolCall);
12260
+ if (blocksToolCalls.length > 0) {
12261
+ toolCallsSig = buildToolCallsSignature(blocksToolCalls);
12262
+ hasToolCalls = true;
12263
+ }
12264
+ }
12265
+ const hasNonEmptyText = content.length > 0 || reasoning.length > 0;
12266
+ const fingerprint = `${content}##${reasoning}##${toolCallsSig}`;
12267
+ return {
12268
+ fingerprint,
12269
+ hasNonEmptyText,
12270
+ hasToolCalls,
12271
+ toolCallsSignature: toolCallsSig
12272
+ };
12273
+ }
12274
+ function buildToolResultsSignature(toolExecutions) {
12275
+ if (toolExecutions.length === 0) return "";
12276
+ return toolExecutions.map((exec) => {
12277
+ const contentStr = typeof exec.content === "string" ? exec.content.trim() : "";
12278
+ const metaStr = exec.metadata ? JSON.stringify(sortJsonKeys(exec.metadata)) : "";
12279
+ return `${exec.toolName}:${contentStr}:${metaStr}`;
12280
+ }).join(";;");
12281
+ }
12282
+ function resolveProgressGuardConfig(options, env = typeof process !== "undefined" ? process.env : {}) {
12283
+ const envDisabled = env.NOLO_LOOP_PROGRESS_GUARD_DISABLED === "true" || env.NOLO_LOOP_PROGRESS_GUARD_DISABLED === "1";
12284
+ const disabled = options?.disabled ?? envDisabled ?? false;
12285
+ const parsePositiveInt = (val, fallback) => {
12286
+ if (typeof val === "number" && Number.isFinite(val) && val >= 1) {
12287
+ return Math.floor(val);
12288
+ }
12289
+ if (typeof val === "string") {
12290
+ const parsed = parseInt(val, 10);
12291
+ if (Number.isFinite(parsed) && parsed >= 1) {
12292
+ return parsed;
12293
+ }
12294
+ }
12295
+ return fallback;
12296
+ };
12297
+ const maxConsecutiveIdenticalRounds = parsePositiveInt(
12298
+ options?.maxConsecutiveIdenticalRounds ?? env.NOLO_LOOP_MAX_IDENTICAL_ROUNDS,
12299
+ DEFAULT_MAX_CONSECUTIVE_IDENTICAL_ROUNDS
12300
+ );
12301
+ const maxConsecutiveStagnantToolRounds = parsePositiveInt(
12302
+ options?.maxConsecutiveStagnantToolRounds ?? env.NOLO_LOOP_MAX_STAGNANT_TOOL_ROUNDS,
12303
+ DEFAULT_MAX_CONSECUTIVE_STAGNANT_TOOL_ROUNDS
12304
+ );
12305
+ return {
12306
+ disabled,
12307
+ maxConsecutiveIdenticalRounds,
12308
+ maxConsecutiveStagnantToolRounds
12309
+ };
12310
+ }
12311
+ var LocalLoopProgressGuard = class {
12312
+ config;
12313
+ lastAssistantFingerprint = null;
12314
+ lastAssistantHasNonEmptyText = false;
12315
+ consecutiveIdenticalCount = 0;
12316
+ lastToolCallsSignature = null;
12317
+ lastToolResultsSignature = null;
12318
+ consecutiveStagnantToolCount = 0;
12319
+ constructor(config, env) {
12320
+ this.config = resolveProgressGuardConfig(config, env);
12321
+ }
12322
+ /**
12323
+ * 在 LLM 返回 assistant 响应后调用。
12324
+ * 仅在纯文本/无工具调用且实质文本相同达到阈值时直接熔断;
12325
+ * 若存在工具调用,则不在此处熔断,留待工具执行后结合工具结果综合判定。
12326
+ */
12327
+ observeAssistantResponse(result4) {
12328
+ if (this.config.disabled) return { action: "continue" };
12329
+ const { fingerprint, hasNonEmptyText, hasToolCalls } = buildAssistantActionFingerprint(result4);
12330
+ if (this.lastAssistantFingerprint !== null && this.lastAssistantFingerprint === fingerprint) {
12331
+ this.consecutiveIdenticalCount += 1;
12332
+ if (!hasToolCalls && hasNonEmptyText && this.consecutiveIdenticalCount >= this.config.maxConsecutiveIdenticalRounds) {
12333
+ return {
12334
+ action: "stall",
12335
+ reason: "repetition_loop",
12336
+ detail: `Consecutive identical text-only assistant response for ${this.consecutiveIdenticalCount} rounds.`,
12337
+ consecutiveRounds: this.consecutiveIdenticalCount
12338
+ };
12339
+ }
12340
+ } else {
12341
+ this.lastAssistantFingerprint = fingerprint;
12342
+ this.lastAssistantHasNonEmptyText = hasNonEmptyText;
12343
+ this.consecutiveIdenticalCount = 1;
12344
+ }
12345
+ return { action: "continue" };
12346
+ }
12347
+ /**
12348
+ * 在本轮所有工具执行完毕后调用,检查是否陷入停滞死循环。
12349
+ * 只有当工具返回内容与元数据完全无变化时才计入无进展 streak;
12350
+ * 一旦工具结果发生变化(如合法轮询状态推进),立即重置 identical streak,确保绝不误杀正常长任务。
12351
+ */
12352
+ observeToolExecution(toolCalls, toolResults) {
12353
+ if (this.config.disabled) return { action: "continue" };
12354
+ if (!toolCalls || toolCalls.length === 0) return { action: "continue" };
12355
+ const toolCallsSig = buildToolCallsSignature(toolCalls);
12356
+ const toolResultsSig = buildToolResultsSignature(toolResults);
12357
+ const toolCallsUnchanged = this.lastToolCallsSignature !== null && this.lastToolCallsSignature === toolCallsSig;
12358
+ const toolResultsUnchanged = this.lastToolResultsSignature !== null && this.lastToolResultsSignature === toolResultsSig;
12359
+ if (toolCallsUnchanged && toolResultsUnchanged) {
12360
+ this.consecutiveStagnantToolCount += 1;
12361
+ if (this.consecutiveIdenticalCount >= this.config.maxConsecutiveIdenticalRounds) {
12362
+ return {
12363
+ action: "stall",
12364
+ reason: "repetition_loop",
12365
+ detail: `Consecutive identical assistant response with unchanged tool results for ${this.consecutiveIdenticalCount} rounds.`,
12366
+ consecutiveRounds: this.consecutiveIdenticalCount
12367
+ };
12368
+ }
12369
+ if (this.consecutiveStagnantToolCount >= this.config.maxConsecutiveStagnantToolRounds) {
12370
+ return {
12371
+ action: "stall",
12372
+ reason: "stagnant_tool_calls",
12373
+ detail: `Consecutive stagnant tool calls with unchanged results for ${this.consecutiveStagnantToolCount} rounds.`,
12374
+ consecutiveRounds: this.consecutiveStagnantToolCount
12375
+ };
12376
+ }
12377
+ } else {
12378
+ this.lastToolCallsSignature = toolCallsSig;
12379
+ this.lastToolResultsSignature = toolResultsSig;
12380
+ this.consecutiveStagnantToolCount = 1;
12381
+ if (!toolResultsUnchanged) {
12382
+ this.consecutiveIdenticalCount = 1;
12383
+ }
12384
+ }
12385
+ return { action: "continue" };
12386
+ }
12387
+ };
12388
+ function createLocalLoopProgressGuard(config, env) {
12389
+ return new LocalLoopProgressGuard(config, env);
12390
+ }
12391
+
12392
+ // packages/agent-runtime/toolArgumentsFingerprint.ts
12393
+ var hashFingerprintInput = (value) => {
12394
+ let hash2 = 2166136261;
12395
+ for (let index = 0; index < value.length; index += 1) {
12396
+ hash2 ^= value.charCodeAt(index);
12397
+ hash2 = Math.imul(hash2, 16777619);
12398
+ }
12399
+ return (hash2 >>> 0).toString(16).padStart(8, "0");
12400
+ };
12401
+ function buildToolArgumentsFingerprint(rawArguments) {
12402
+ if (rawArguments === void 0 || rawArguments === null) return void 0;
12403
+ const normalized = normalizeToolArguments(rawArguments);
12404
+ return hashFingerprintInput(normalized);
12405
+ }
12406
+
12206
12407
  // packages/agent-runtime/leafFinalHandoff.ts
12207
- var LEAF_FINAL_HANDOFF_INSTRUCTIONS = `Leaf subtask final: return only the result, blockers/findings, and minimal evidence the parent needs. Do not narrate your process or restate the task. Keep success minimal (a one-line result is enough); on failure include the blocker, severity when relevant, and an evidence reference (file:line, test command, commit, artifact, or run/dialog). If the task explicitly requires a long-form deliverable (document, translation, dataset, code, or other artifact), provide that deliverable in full and keep only the handoff around it concise.`;
12408
+ var LEAF_FINAL_HANDOFF_INSTRUCTIONS = `Leaf subtask final: return only the result, blockers/findings, and minimal evidence the parent needs. Do not narrate your process or restate the task. Keep success minimal (a one-line result is enough); on failure include the blocker, severity when relevant, and an evidence reference (file:line, test command, commit, artifact, or run/dialog). If the task explicitly requires a long-form deliverable (document, translation, dataset, code, or other artifact), provide that deliverable in full and keep only the handoff around it concise. During execution, follow the brief as given: only when new evidence would materially change the goal, risk, irreversibility, or DoD should you surface it as a blocker/finding in the handoff \u2014 do not silently expand scope or re-litigate the settled direction.`;
12208
12409
 
12209
12410
  // packages/agent-runtime/contextBlockScope.ts
12210
12411
  var isContextBlockScope = (value) => {
@@ -12426,20 +12627,33 @@ function isColdResume(history, nowMs) {
12426
12627
  }
12427
12628
  async function maybeAutoCompactLocalHistory(args2) {
12428
12629
  const { adapter, dialogId, history } = args2;
12429
- const unchanged = () => ({
12630
+ const unchanged = (decision) => ({
12430
12631
  history,
12431
12632
  compressed: false,
12432
- summaryGenerated: false
12633
+ summaryGenerated: false,
12634
+ decision
12433
12635
  });
12434
- if (!dialogId || history.length === 0 || typeof adapter.loadDialogSummary !== "function" || typeof adapter.saveDialogSummary !== "function") {
12435
- return unchanged();
12636
+ if (!dialogId) {
12637
+ return unchanged({ outcome: "skipped", skipReason: "no-dialog-id" });
12638
+ }
12639
+ if (history.length === 0) {
12640
+ return unchanged({ outcome: "skipped", skipReason: "empty-history" });
12641
+ }
12642
+ if (typeof adapter.loadDialogSummary !== "function" || typeof adapter.saveDialogSummary !== "function") {
12643
+ return unchanged({
12644
+ outcome: "skipped",
12645
+ skipReason: "adapter-missing-summary-methods"
12646
+ });
12436
12647
  }
12437
12648
  let stored = null;
12438
12649
  try {
12439
12650
  stored = await adapter.loadDialogSummary(dialogId);
12440
12651
  } catch (error) {
12441
12652
  console.warn("[localLoop] loadDialogSummary failed:", error);
12442
- return unchanged();
12653
+ return unchanged({
12654
+ outcome: "skipped",
12655
+ skipReason: "load-summary-failed"
12656
+ });
12443
12657
  }
12444
12658
  const bridged = toPlanCompressionMessages(history);
12445
12659
  const validation = validateStoredSummary({ history, stored: stored ?? {} });
@@ -12475,7 +12689,21 @@ async function maybeAutoCompactLocalHistory(args2) {
12475
12689
  ...args2.realContextUsagePercent !== void 0 ? { realContextUsagePercent: args2.realContextUsagePercent } : {}
12476
12690
  });
12477
12691
  const projectExisting = () => {
12478
- if (!existingSummary.trim()) return unchanged();
12692
+ const diag = plan.diagnostics;
12693
+ const diagSnapshot = diag ? {
12694
+ estimatedTokens: diag.totalUsed,
12695
+ historyBudget: diag.historyBudget,
12696
+ triggerRatio: diag.triggerRatio,
12697
+ ...diag.realUsageRatio !== void 0 ? { realUsageRatio: diag.realUsageRatio } : {}
12698
+ } : {};
12699
+ const skippedDecision = {
12700
+ outcome: "skipped",
12701
+ skipReason: diag?.emptyReason === "no-pending" ? "no-pending" : diag?.emptyReason === "below-min-compress-count" ? "below-min-compress-count" : "below-trigger",
12702
+ ...diagSnapshot,
12703
+ ...diag ? { pendingMsgCount: diag.pendingMsgCount } : {},
12704
+ ...diag?.compressCount !== void 0 ? { compressCount: diag.compressCount } : {}
12705
+ };
12706
+ if (!existingSummary.trim()) return unchanged(skippedDecision);
12479
12707
  return {
12480
12708
  history: projectHistoryWithSummary({
12481
12709
  history,
@@ -12483,7 +12711,12 @@ async function maybeAutoCompactLocalHistory(args2) {
12483
12711
  summarizedBeforeId
12484
12712
  }),
12485
12713
  compressed: true,
12486
- summaryGenerated: false
12714
+ summaryGenerated: false,
12715
+ decision: {
12716
+ outcome: "compressed",
12717
+ trigger: "existing-summary",
12718
+ ...diagSnapshot
12719
+ }
12487
12720
  };
12488
12721
  };
12489
12722
  if (!plan.shouldCompress) {
@@ -12513,7 +12746,11 @@ async function maybeAutoCompactLocalHistory(args2) {
12513
12746
  );
12514
12747
  return {
12515
12748
  ...projectExisting(),
12516
- failureMessage: "summary model returned empty content"
12749
+ failureMessage: "summary model returned empty content",
12750
+ decision: {
12751
+ outcome: "failed",
12752
+ detail: "summary model returned empty content"
12753
+ }
12517
12754
  };
12518
12755
  }
12519
12756
  await adapter.saveDialogSummary({
@@ -12550,198 +12787,27 @@ async function maybeAutoCompactLocalHistory(args2) {
12550
12787
  beforeTokens: metrics.previousSummaryTokens + metrics.compressedTokens,
12551
12788
  afterTokens: metrics.newSummaryTokens + metrics.retainedTokens,
12552
12789
  ...result4.usage ? { usage: result4.usage } : {},
12553
- metrics
12790
+ metrics,
12791
+ decision: {
12792
+ outcome: "compressed",
12793
+ trigger: invalidSummary ? "invalid-summary" : coldResume ? "cold-resume" : plan.diagnostics?.triggeredByRealUsage ? "real-usage" : "estimate",
12794
+ ...plan.diagnostics ? {
12795
+ estimatedTokens: plan.diagnostics.totalUsed,
12796
+ historyBudget: plan.diagnostics.historyBudget,
12797
+ triggerRatio: plan.diagnostics.triggerRatio,
12798
+ ...plan.diagnostics.realUsageRatio !== void 0 ? { realUsageRatio: plan.diagnostics.realUsageRatio } : {}
12799
+ } : {}
12800
+ }
12554
12801
  };
12555
12802
  } catch (error) {
12556
12803
  console.warn("[localLoop] auto-compaction failed:", error);
12557
12804
  const failureMessage = error instanceof Error && error.message ? error.message.slice(0, 200) : String(error).slice(0, 200);
12558
- return { ...projectExisting(), failureMessage };
12559
- }
12560
- }
12561
-
12562
- // packages/agent-runtime/progressGuard.ts
12563
- var DEFAULT_MAX_CONSECUTIVE_IDENTICAL_ROUNDS = 5;
12564
- var DEFAULT_MAX_CONSECUTIVE_STAGNANT_TOOL_ROUNDS = 8;
12565
- function sortJsonKeys(obj) {
12566
- if (obj === null || typeof obj !== "object") return obj;
12567
- if (Array.isArray(obj)) return obj.map(sortJsonKeys);
12568
- const sorted = {};
12569
- for (const key of Object.keys(obj).sort()) {
12570
- sorted[key] = sortJsonKeys(obj[key]);
12571
- }
12572
- return sorted;
12573
- }
12574
- function normalizeToolArguments(rawArgs) {
12575
- if (typeof rawArgs !== "string") {
12576
- try {
12577
- return JSON.stringify(sortJsonKeys(rawArgs ?? ""));
12578
- } catch {
12579
- return String(rawArgs ?? "");
12580
- }
12581
- }
12582
- const trimmed = rawArgs.trim();
12583
- if (!trimmed) return "";
12584
- try {
12585
- const parsed = JSON.parse(trimmed);
12586
- return JSON.stringify(sortJsonKeys(parsed));
12587
- } catch {
12588
- return trimmed;
12589
- }
12590
- }
12591
- function extractNormalizedContentText(content) {
12592
- if (typeof content === "string") return content.trim();
12593
- if (!Array.isArray(content)) return "";
12594
- return content.map((part) => {
12595
- if (part?.type === "text") return String(part.text ?? "").trim();
12596
- if (part?.type === "image_url") return `[img:${part.image_url?.url ?? ""}]`;
12597
- return "";
12598
- }).filter(Boolean).join("\n");
12599
- }
12600
- function buildToolCallsSignature(toolCalls) {
12601
- if (!toolCalls || toolCalls.length === 0) return "";
12602
- return toolCalls.map(
12603
- (tc) => `${tc.function?.name ?? ""}:${normalizeToolArguments(tc.function?.arguments)}`
12604
- ).join(";;");
12605
- }
12606
- function buildAssistantActionFingerprint(result4) {
12607
- const content = extractNormalizedContentText(result4.content);
12608
- const reasoning = typeof result4.reasoning_content === "string" ? result4.reasoning_content.trim() : "";
12609
- let toolCallsSig = buildToolCallsSignature(result4.tool_calls);
12610
- let hasToolCalls = Array.isArray(result4.tool_calls) && result4.tool_calls.length > 0;
12611
- if (!toolCallsSig && Array.isArray(result4.output) && result4.output.length > 0) {
12612
- const blocksToolCalls = result4.output.filter(
12613
- (b) => b.type === "toolCall"
12614
- ).map((b) => b.toolCall);
12615
- if (blocksToolCalls.length > 0) {
12616
- toolCallsSig = buildToolCallsSignature(blocksToolCalls);
12617
- hasToolCalls = true;
12618
- }
12619
- }
12620
- const hasNonEmptyText = content.length > 0 || reasoning.length > 0;
12621
- const fingerprint = `${content}##${reasoning}##${toolCallsSig}`;
12622
- return {
12623
- fingerprint,
12624
- hasNonEmptyText,
12625
- hasToolCalls,
12626
- toolCallsSignature: toolCallsSig
12627
- };
12628
- }
12629
- function buildToolResultsSignature(toolExecutions) {
12630
- if (toolExecutions.length === 0) return "";
12631
- return toolExecutions.map((exec) => {
12632
- const contentStr = typeof exec.content === "string" ? exec.content.trim() : "";
12633
- const metaStr = exec.metadata ? JSON.stringify(sortJsonKeys(exec.metadata)) : "";
12634
- return `${exec.toolName}:${contentStr}:${metaStr}`;
12635
- }).join(";;");
12636
- }
12637
- function resolveProgressGuardConfig(options, env = typeof process !== "undefined" ? process.env : {}) {
12638
- const envDisabled = env.NOLO_LOOP_PROGRESS_GUARD_DISABLED === "true" || env.NOLO_LOOP_PROGRESS_GUARD_DISABLED === "1";
12639
- const disabled = options?.disabled ?? envDisabled ?? false;
12640
- const parsePositiveInt = (val, fallback) => {
12641
- if (typeof val === "number" && Number.isFinite(val) && val >= 1) {
12642
- return Math.floor(val);
12643
- }
12644
- if (typeof val === "string") {
12645
- const parsed = parseInt(val, 10);
12646
- if (Number.isFinite(parsed) && parsed >= 1) {
12647
- return parsed;
12648
- }
12649
- }
12650
- return fallback;
12651
- };
12652
- const maxConsecutiveIdenticalRounds = parsePositiveInt(
12653
- options?.maxConsecutiveIdenticalRounds ?? env.NOLO_LOOP_MAX_IDENTICAL_ROUNDS,
12654
- DEFAULT_MAX_CONSECUTIVE_IDENTICAL_ROUNDS
12655
- );
12656
- const maxConsecutiveStagnantToolRounds = parsePositiveInt(
12657
- options?.maxConsecutiveStagnantToolRounds ?? env.NOLO_LOOP_MAX_STAGNANT_TOOL_ROUNDS,
12658
- DEFAULT_MAX_CONSECUTIVE_STAGNANT_TOOL_ROUNDS
12659
- );
12660
- return {
12661
- disabled,
12662
- maxConsecutiveIdenticalRounds,
12663
- maxConsecutiveStagnantToolRounds
12664
- };
12665
- }
12666
- var LocalLoopProgressGuard = class {
12667
- config;
12668
- lastAssistantFingerprint = null;
12669
- lastAssistantHasNonEmptyText = false;
12670
- consecutiveIdenticalCount = 0;
12671
- lastToolCallsSignature = null;
12672
- lastToolResultsSignature = null;
12673
- consecutiveStagnantToolCount = 0;
12674
- constructor(config, env) {
12675
- this.config = resolveProgressGuardConfig(config, env);
12676
- }
12677
- /**
12678
- * 在 LLM 返回 assistant 响应后调用。
12679
- * 仅在纯文本/无工具调用且实质文本相同达到阈值时直接熔断;
12680
- * 若存在工具调用,则不在此处熔断,留待工具执行后结合工具结果综合判定。
12681
- */
12682
- observeAssistantResponse(result4) {
12683
- if (this.config.disabled) return { action: "continue" };
12684
- const { fingerprint, hasNonEmptyText, hasToolCalls } = buildAssistantActionFingerprint(result4);
12685
- if (this.lastAssistantFingerprint !== null && this.lastAssistantFingerprint === fingerprint) {
12686
- this.consecutiveIdenticalCount += 1;
12687
- if (!hasToolCalls && hasNonEmptyText && this.consecutiveIdenticalCount >= this.config.maxConsecutiveIdenticalRounds) {
12688
- return {
12689
- action: "stall",
12690
- reason: "repetition_loop",
12691
- detail: `Consecutive identical text-only assistant response for ${this.consecutiveIdenticalCount} rounds.`,
12692
- consecutiveRounds: this.consecutiveIdenticalCount
12693
- };
12694
- }
12695
- } else {
12696
- this.lastAssistantFingerprint = fingerprint;
12697
- this.lastAssistantHasNonEmptyText = hasNonEmptyText;
12698
- this.consecutiveIdenticalCount = 1;
12699
- }
12700
- return { action: "continue" };
12701
- }
12702
- /**
12703
- * 在本轮所有工具执行完毕后调用,检查是否陷入停滞死循环。
12704
- * 只有当工具返回内容与元数据完全无变化时才计入无进展 streak;
12705
- * 一旦工具结果发生变化(如合法轮询状态推进),立即重置 identical streak,确保绝不误杀正常长任务。
12706
- */
12707
- observeToolExecution(toolCalls, toolResults) {
12708
- if (this.config.disabled) return { action: "continue" };
12709
- if (!toolCalls || toolCalls.length === 0) return { action: "continue" };
12710
- const toolCallsSig = buildToolCallsSignature(toolCalls);
12711
- const toolResultsSig = buildToolResultsSignature(toolResults);
12712
- const toolCallsUnchanged = this.lastToolCallsSignature !== null && this.lastToolCallsSignature === toolCallsSig;
12713
- const toolResultsUnchanged = this.lastToolResultsSignature !== null && this.lastToolResultsSignature === toolResultsSig;
12714
- if (toolCallsUnchanged && toolResultsUnchanged) {
12715
- this.consecutiveStagnantToolCount += 1;
12716
- if (this.consecutiveIdenticalCount >= this.config.maxConsecutiveIdenticalRounds) {
12717
- return {
12718
- action: "stall",
12719
- reason: "repetition_loop",
12720
- detail: `Consecutive identical assistant response with unchanged tool results for ${this.consecutiveIdenticalCount} rounds.`,
12721
- consecutiveRounds: this.consecutiveIdenticalCount
12722
- };
12723
- }
12724
- if (this.consecutiveStagnantToolCount >= this.config.maxConsecutiveStagnantToolRounds) {
12725
- return {
12726
- action: "stall",
12727
- reason: "stagnant_tool_calls",
12728
- detail: `Consecutive stagnant tool calls with unchanged results for ${this.consecutiveStagnantToolCount} rounds.`,
12729
- consecutiveRounds: this.consecutiveStagnantToolCount
12730
- };
12731
- }
12732
- } else {
12733
- this.lastToolCallsSignature = toolCallsSig;
12734
- this.lastToolResultsSignature = toolResultsSig;
12735
- this.consecutiveStagnantToolCount = 1;
12736
- if (!toolResultsUnchanged) {
12737
- this.consecutiveIdenticalCount = 1;
12738
- }
12739
- }
12740
- return { action: "continue" };
12805
+ return {
12806
+ ...projectExisting(),
12807
+ failureMessage,
12808
+ decision: { outcome: "failed", detail: failureMessage }
12809
+ };
12741
12810
  }
12742
- };
12743
- function createLocalLoopProgressGuard(config, env) {
12744
- return new LocalLoopProgressGuard(config, env);
12745
12811
  }
12746
12812
 
12747
12813
  // packages/agent-runtime/localLoop.ts
@@ -13049,10 +13115,10 @@ var TOOL_METADATA_KEYS = [
13049
13115
  "pasteId",
13050
13116
  "source"
13051
13117
  ];
13052
- function trimHistoryToContextBudget(history, model) {
13118
+ function trimHistoryToContextBudget(history, model, contextWindowOverride) {
13053
13119
  if (history.length === 0) return { history, droppedCount: 0 };
13054
13120
  const { rawMessageBudget } = planContextUsage({
13055
- contextWindow: getModelContextWindow(model ?? ""),
13121
+ contextWindow: contextWindowOverride ?? getModelContextWindow(model ?? ""),
13056
13122
  summaryTokens: 0,
13057
13123
  // localLoop 没有 web 端的负载分档器;medium 是中性默认值,
13058
13124
  // 不为了省几个 token 在这里复制一份分类逻辑。
@@ -13515,7 +13581,14 @@ async function runLocalAgentTurn(input) {
13515
13581
  agentConfig.exposedToolNames ?? agentConfig.toolNames ?? []
13516
13582
  );
13517
13583
  const guidanceBlocks = buildRuntimeGuidanceBlocks(agentTools);
13518
- const toolGuidedSections = resolveToolGuidedSections(agentTools);
13584
+ const toolGuidedSections = resolveToolGuidedSections(agentTools, {
13585
+ // 单调锁定:历史里任一 user 消息命中派发关键词、或已出现过
13586
+ // startAgentRun/controlAgentRun 的 tool_calls,后续每轮都保持 true,
13587
+ // 防止 session-scope 的 agentCollaboration 段逐轮翻转击穿前缀缓存。
13588
+ dispatchIntent: detectDispatchIntentFromMessages(history) || detectDispatchIntent(
13589
+ typeof input.input === "string" ? input.input : void 0
13590
+ )
13591
+ });
13519
13592
  const guidanceScopes = [
13520
13593
  ...input.userLanguage?.trim() ? [buildUserResponseLanguageContext({ language: input.userLanguage })] : [],
13521
13594
  guidanceBlocks.startupProtocol,
@@ -13543,7 +13616,7 @@ async function runLocalAgentTurn(input) {
13543
13616
  }
13544
13617
  return resolvedProvider;
13545
13618
  };
13546
- const contextWindow = getModelContextWindow(agentConfig.model ?? "");
13619
+ const contextWindow = resolveAgentContextWindow(agentConfig);
13547
13620
  const compressionTriggerRatio = resolveCompressionTriggerRatio(contextWindow);
13548
13621
  let realContextUsagePercent;
13549
13622
  if (input.continueDialogId && typeof input.adapter.loadLastContextUsage === "function") {
@@ -13564,19 +13637,29 @@ async function runLocalAgentTurn(input) {
13564
13637
  }
13565
13638
  let compactionUsage;
13566
13639
  const emitCompactionObservation = (compacted) => {
13567
- if (compacted.compressed || compacted.summaryGenerated || compacted.failureMessage) {
13568
- emitLoopEvent(observationBoundary, {
13569
- kind: "compaction",
13570
- atMs: Date.now(),
13571
- reason: compacted.reason ?? "context_budget",
13572
- summaryGenerated: compacted.summaryGenerated,
13573
- compressed: compacted.compressed,
13574
- ...compacted.failureMessage ? { failed: true, detail: compacted.failureMessage } : {},
13575
- ...compacted.beforeTokens !== void 0 ? { beforeTokens: compacted.beforeTokens } : {},
13576
- ...compacted.afterTokens !== void 0 ? { afterTokens: compacted.afterTokens } : {},
13577
- ...compacted.savedTokens !== void 0 ? { savedTokens: compacted.savedTokens } : {}
13578
- });
13640
+ const decision = compacted.decision;
13641
+ if (decision?.outcome === "skipped") {
13642
+ const isProtectionGap = decision.skipReason === "adapter-missing-summary-methods" || decision.skipReason === "load-summary-failed";
13643
+ const approachingBudget = decision.skipReason === "below-trigger" && typeof decision.estimatedTokens === "number" && typeof decision.historyBudget === "number" && decision.historyBudget > 0 && decision.estimatedTokens >= decision.historyBudget * 0.5;
13644
+ if (!isProtectionGap && !approachingBudget) return;
13579
13645
  }
13646
+ emitLoopEvent(observationBoundary, {
13647
+ kind: "compaction",
13648
+ atMs: Date.now(),
13649
+ ...compacted.reason ? { reason: compacted.reason } : {},
13650
+ summaryGenerated: compacted.summaryGenerated,
13651
+ compressed: compacted.compressed,
13652
+ ...compacted.failureMessage ? { failed: true, detail: compacted.failureMessage } : {},
13653
+ ...decision?.outcome === "skipped" ? { skipped: true, skipReason: decision.skipReason } : {},
13654
+ ...decision && "trigger" in decision && decision.trigger ? { trigger: decision.trigger } : {},
13655
+ ...decision && "estimatedTokens" in decision && decision.estimatedTokens !== void 0 ? { estimatedTokens: decision.estimatedTokens } : {},
13656
+ ...decision && "historyBudget" in decision && decision.historyBudget !== void 0 ? { historyBudget: decision.historyBudget } : {},
13657
+ ...decision && "realUsageRatio" in decision && decision.realUsageRatio !== void 0 ? { realUsageRatio: decision.realUsageRatio } : {},
13658
+ ...decision && "triggerRatio" in decision && decision.triggerRatio !== void 0 ? { triggerRatio: decision.triggerRatio } : {},
13659
+ ...compacted.beforeTokens !== void 0 ? { beforeTokens: compacted.beforeTokens } : {},
13660
+ ...compacted.afterTokens !== void 0 ? { afterTokens: compacted.afterTokens } : {},
13661
+ ...compacted.savedTokens !== void 0 ? { savedTokens: compacted.savedTokens } : {}
13662
+ });
13580
13663
  };
13581
13664
  try {
13582
13665
  const compacted = await maybeAutoCompactLocalHistory({
@@ -13585,6 +13668,7 @@ async function runLocalAgentTurn(input) {
13585
13668
  history,
13586
13669
  model: agentConfig.model,
13587
13670
  resolveProvider: resolveProviderOnce,
13671
+ contextWindow,
13588
13672
  realContextUsagePercent
13589
13673
  });
13590
13674
  history = compacted.history;
@@ -13594,7 +13678,7 @@ async function runLocalAgentTurn(input) {
13594
13678
  console.warn("[localLoop] auto-compaction unexpected error:", error);
13595
13679
  }
13596
13680
  loopTimingMark("maybeAutoCompactLocalHistory", 0);
13597
- const trimmedHistory = trimHistoryToContextBudget(history, agentConfig.model);
13681
+ const trimmedHistory = trimHistoryToContextBudget(history, agentConfig.model, contextWindow);
13598
13682
  if (trimmedHistory.droppedCount > 0) {
13599
13683
  history = trimmedHistory.history;
13600
13684
  }
@@ -13656,17 +13740,26 @@ async function runLocalAgentTurn(input) {
13656
13740
  let skipFinalAppend = false;
13657
13741
  let partialContent = "";
13658
13742
  const progressGuard = createLocalLoopProgressGuard(input.progressGuardConfig);
13743
+ let ephemeralSummary = null;
13744
+ const ephemeralDialogId = `ephemeral-${crypto.randomUUID()}`;
13745
+ const compactionAdapter = input.continueDialogId ? input.adapter : {
13746
+ ...input.adapter,
13747
+ loadDialogSummary: async () => ephemeralSummary,
13748
+ saveDialogSummary: async (summaryInput) => {
13749
+ ephemeralSummary = summaryInput;
13750
+ }
13751
+ };
13752
+ const compactionDialogId = input.continueDialogId ?? ephemeralDialogId;
13659
13753
  const maybeCompactInLoop = async () => {
13660
- if (!input.continueDialogId) return;
13661
- if (!contextUsage) return;
13662
- const inputTokens = normalizeUsage(contextUsage).input_tokens;
13663
- if (!inputTokens || inputTokens <= 0) return;
13664
- const ratio = Math.min(1, inputTokens / contextWindow);
13665
- if (ratio < compressionTriggerRatio) return;
13754
+ const inputTokens = contextUsage ? normalizeUsage(contextUsage).input_tokens : 0;
13755
+ const ratio = inputTokens > 0 ? Math.min(1, inputTokens / contextWindow) : void 0;
13756
+ if (ratio !== void 0 && ratio < compressionTriggerRatio * 0.85) {
13757
+ return;
13758
+ }
13666
13759
  try {
13667
13760
  const compacted = await maybeAutoCompactLocalHistory({
13668
- adapter: input.adapter,
13669
- dialogId: input.continueDialogId,
13761
+ adapter: compactionAdapter,
13762
+ dialogId: compactionDialogId,
13670
13763
  // canonical 坐标:store 全量历史 + 本轮消息的「持久化形态」
13671
13764
  // (applyPersistedTurnInput 会把首条 user 消息换成 paste 展开形态)。
13672
13765
  // 锚点与 sourceHash 都必须按持久化形态计算——否则下轮从 store
@@ -13681,7 +13774,8 @@ async function runLocalAgentTurn(input) {
13681
13774
  ],
13682
13775
  model: agentConfig.model,
13683
13776
  resolveProvider: resolveProviderOnce,
13684
- realContextUsagePercent: ratio
13777
+ contextWindow,
13778
+ ...ratio !== void 0 ? { realContextUsagePercent: ratio } : {}
13685
13779
  });
13686
13780
  if (compacted.usage) {
13687
13781
  compactionUsage = addOutOfBandUsage(compactionUsage, compacted.usage);
@@ -13949,6 +14043,7 @@ ${reasoningTailLog}
13949
14043
  const startedAt = Date.now();
13950
14044
  loopTimingMark("toolCallStart", round);
13951
14045
  const argumentsPreview = summarizeToolArguments(toolName, toolCall.function.arguments);
14046
+ const argumentsFingerprint = buildToolArgumentsFingerprint(toolCall.function.arguments);
13952
14047
  emitLoopEvent(
13953
14048
  observationBoundary,
13954
14049
  {
@@ -13957,7 +14052,8 @@ ${reasoningTailLog}
13957
14052
  toolCallId: toolCall.id,
13958
14053
  toolName,
13959
14054
  atMs: startedAt,
13960
- ...argumentsPreview ? { argumentsPreview } : {}
14055
+ ...argumentsPreview ? { argumentsPreview } : {},
14056
+ ...argumentsFingerprint ? { argumentsFingerprint } : {}
13961
14057
  },
13962
14058
  {
13963
14059
  type: "tool-call",
@@ -15571,11 +15667,12 @@ var DEFAULT_LOCAL_TOOLS = /* @__PURE__ */ new Set([
15571
15667
  "execShell",
15572
15668
  "launchProcess",
15573
15669
  "listProcesses",
15574
- // ProcessTask follow-up tools (Phase 1). Same tier as listProcesses: they
15575
- // observe/stop tasks the agent itself already spawned, so their blast radius
15576
- // is strictly narrower than launchProcess (which is default-enabled).
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.
15577
15675
  "taskWait",
15578
- "taskLogs",
15579
15676
  "taskStop",
15580
15677
  // Scheduling tools: forking/observing sub-agents is a declared capability
15581
15678
  // of capable agents, and the blast radius is strictly narrower than
@@ -17573,7 +17670,6 @@ var WORKSPACE_TOOL_NAMES = [
17573
17670
  "launchProcess",
17574
17671
  "listProcesses",
17575
17672
  "taskWait",
17576
- "taskLogs",
17577
17673
  "taskStop",
17578
17674
  "openDesktopPreview"
17579
17675
  ];
@@ -17582,7 +17678,6 @@ var SHELL_TOOL_NAMES = [
17582
17678
  "launchProcess",
17583
17679
  "listProcesses",
17584
17680
  "taskWait",
17585
- "taskLogs",
17586
17681
  "taskStop"
17587
17682
  ];
17588
17683
  var WORKSPACE_TOOL_NAME_SET = new Set(WORKSPACE_TOOL_NAMES);
@@ -17832,7 +17927,7 @@ function buildTaskWaitTool() {
17832
17927
  type: "function",
17833
17928
  function: {
17834
17929
  name: "taskWait",
17835
- description: 'Block until a background task (from launchProcess, or an execShell command that detached) reaches a terminal state, or until timeoutMs elapses. Returns {outcome, status, exitCode?, cursor, events}: outcome "terminal" with status exited|failed|stopped when it finished, outcome "timeout" with status "running" when it outlived the whole budget, outcome "not-found" for an unknown taskId, or outcome "evicted" when the event trail aged out (never wait again in that case). One call already waits the maximum budget and returns the moment the task ends, so a "timeout" means the task is genuinely longer than that \u2014 go do other work and come back, rather than re-waiting in a loop (each re-wait costs a full model round-trip). For a cheap liveness peek use taskLogs (non-blocking) or pass a small timeoutMs. `cursor` is the highest event seq covered by this response \u2014 pass it to taskLogs to read only newer events.',
17930
+ description: 'Block until a background task (from launchProcess, or an execShell command that detached) reaches a terminal state, or until timeoutMs elapses. Returns {outcome, status, exitCode?, cursor, events}: outcome "terminal" with status exited|failed|stopped when it finished, outcome "timeout" with status "running" when it outlived the whole budget, outcome "not-found" for an unknown taskId, or outcome "evicted" when the event trail aged out (never wait again in that case). One call already waits the maximum budget and returns the moment the task ends, so a "timeout" means the task is genuinely longer than that \u2014 go do other work and come back, rather than re-waiting in a loop (each re-wait costs a full model round-trip). Pass a small timeoutMs for a cheap liveness peek instead of the full result. `cursor` is the highest event seq covered by this response.',
17836
17931
  parameters: {
17837
17932
  type: "object",
17838
17933
  properties: {
@@ -17851,30 +17946,6 @@ function buildTaskWaitTool() {
17851
17946
  }
17852
17947
  };
17853
17948
  }
17854
- function buildTaskLogsTool() {
17855
- return {
17856
- type: "function",
17857
- function: {
17858
- name: "taskLogs",
17859
- description: "Read a background task's lifecycle events incrementally. Returns {outcome, status, cursor, events} where events are the started|promoted|exited|killed records with seq greater than the cursor you passed, and the returned `cursor` is the new high-water mark to pass next time (omit cursor to read from the beginning; repeating the same cursor is idempotent). Oversized output is spilled to a file and reported as `logRef`, which you can open with readFile.",
17860
- parameters: {
17861
- type: "object",
17862
- properties: {
17863
- taskId: {
17864
- type: "string",
17865
- minLength: 1,
17866
- description: "Task id returned by launchProcess or by a detached execShell result."
17867
- },
17868
- cursor: {
17869
- type: "number",
17870
- description: "Last event seq you already consumed; only events with a higher seq are returned. Defaults to 0 (all retained events)."
17871
- }
17872
- },
17873
- required: ["taskId"]
17874
- }
17875
- }
17876
- };
17877
- }
17878
17949
  function buildTaskStopTool() {
17879
17950
  return {
17880
17951
  type: "function",
@@ -17914,7 +17985,6 @@ function buildWorkspaceToolDefinition(toolName) {
17914
17985
  if (toolName === "openDesktopPreview") return buildOpenDesktopPreviewTool();
17915
17986
  if (toolName === "listProcesses") return buildListProcessesTool();
17916
17987
  if (toolName === "taskWait") return buildTaskWaitTool();
17917
- if (toolName === "taskLogs") return buildTaskLogsTool();
17918
17988
  if (toolName === "taskStop") return buildTaskStopTool();
17919
17989
  return null;
17920
17990
  }
@@ -19381,15 +19451,23 @@ function buildLoadSkillExecutor(args2) {
19381
19451
  const { resolveSkillByName } = await import("./skillDiscovery-CYUCJMAG.js");
19382
19452
  const resolved = resolveSkillByName(args2.cwd, name);
19383
19453
  if (!resolved) {
19384
- const { resolveCodingBuiltinSlug, buildCodingSkillContentBySlug } = await import("./codingSkills-7S6523WH.js");
19385
- const builtinSlug = resolveCodingBuiltinSlug(name);
19386
- if (builtinSlug) {
19387
- const body = buildCodingSkillContentBySlug(builtinSlug);
19454
+ const { resolveBuiltinSkillByName } = await import("./builtinSkillRegistry-FUAVTRVZ.js");
19455
+ const builtin = resolveBuiltinSkillByName(name);
19456
+ if (builtin) {
19388
19457
  return {
19389
19458
  content: `Skill "${name}" loaded inline. Follow its instructions.
19390
19459
 
19391
- ${body}`,
19392
- metadata: { loadSkill: true, resolved: true, name, requestedName: name, builtin: true }
19460
+ ${builtin.content}`,
19461
+ // toolNames 进 metadata:回退面已扩到全注册表,其中授工具型 skill
19462
+ // (search-* 等)的工具在本宿主未必接线,宿主可据此降级提示。
19463
+ metadata: {
19464
+ loadSkill: true,
19465
+ resolved: true,
19466
+ name,
19467
+ requestedName: name,
19468
+ builtin: true,
19469
+ ...builtin.config.toolNames?.length ? { toolNames: [...builtin.config.toolNames] } : {}
19470
+ }
19393
19471
  };
19394
19472
  }
19395
19473
  return {
@@ -20221,11 +20299,33 @@ function encodeDevinClientMetadata(token) {
20221
20299
  ]);
20222
20300
  }
20223
20301
  function encodeDevinChatMessage(msg) {
20224
- const source = msg.role === "assistant" ? 2 : 1;
20302
+ const source = msg.role === "assistant" ? 2 : msg.role === "tool" ? 4 : 1;
20225
20303
  return Buffer.concat([
20226
20304
  writeStringField(1, randomUUID2()),
20227
20305
  writeVarint(2, source),
20228
- writeStringField(3, msg.text)
20306
+ writeStringField(3, msg.text),
20307
+ ...(msg.toolCalls ?? []).map(
20308
+ (toolCall) => writeMessageField(
20309
+ 6,
20310
+ Buffer.concat([
20311
+ writeStringField(1, toolCall.id),
20312
+ writeStringField(2, toolCall.function.name),
20313
+ writeStringField(3, toolCall.function.arguments)
20314
+ ])
20315
+ )
20316
+ ),
20317
+ ...msg.toolCallId ? [writeStringField(7, msg.toolCallId)] : []
20318
+ ]);
20319
+ }
20320
+ function encodeDevinToolDefinition(tool) {
20321
+ const fn3 = tool.function ?? {};
20322
+ const name = typeof fn3.name === "string" ? fn3.name : "";
20323
+ const description = typeof fn3.description === "string" ? fn3.description : "";
20324
+ const parameters = fn3.parameters ?? {};
20325
+ return Buffer.concat([
20326
+ writeStringField(1, name),
20327
+ writeStringField(2, description.length > 6998 ? `${description.slice(0, 6995)}...` : description),
20328
+ writeStringField(3, JSON.stringify(parameters))
20229
20329
  ]);
20230
20330
  }
20231
20331
  function writeFixed64Field(fieldNum, val) {
@@ -20242,20 +20342,21 @@ function writeFixed64Field(fieldNum, val) {
20242
20342
  return Buffer.concat([Buffer.from(tagBytes), b]);
20243
20343
  }
20244
20344
  function buildCompletionConfig(params) {
20245
- const maxTokens = params?.maxTokens ?? 4096;
20246
- const temp = params?.temperature ?? 0.4;
20345
+ const maxTokens = params?.maxTokens ?? 128e3;
20346
+ const maxNewlines = params?.maxNewlines ?? 400;
20347
+ const temp = params?.temperature ?? 1;
20247
20348
  const topP = params?.topP ?? 0.95;
20248
20349
  return Buffer.concat([
20249
20350
  writeVarint(1, 1),
20250
20351
  writeVarint(2, maxTokens),
20251
- writeVarint(3, 128e3),
20352
+ writeVarint(3, maxNewlines),
20252
20353
  writeFixed64Field(5, temp),
20253
20354
  writeVarint(7, 40),
20254
20355
  writeFixed64Field(8, topP)
20255
20356
  ]);
20256
20357
  }
20257
20358
  function buildGetChatMessageRequest(params) {
20258
- const { token, messages, model } = params;
20359
+ const { token, messages, model, tools = [], temperature } = params;
20259
20360
  const metadata = encodeDevinClientMetadata(token);
20260
20361
  let systemPrompt = "";
20261
20362
  const filteredMessages = [];
@@ -20265,14 +20366,19 @@ function buildGetChatMessageRequest(params) {
20265
20366
  systemPrompt = systemPrompt ? `${systemPrompt}
20266
20367
 
20267
20368
  ${text}` : text;
20268
- } else if (m.role === "user" || m.role === "assistant") {
20269
- filteredMessages.push({ role: m.role, text });
20369
+ } else if (m.role === "user" || m.role === "assistant" || m.role === "tool") {
20370
+ filteredMessages.push({
20371
+ role: m.role,
20372
+ text,
20373
+ ...m.tool_call_id ? { toolCallId: m.tool_call_id } : {},
20374
+ ...m.tool_calls?.length ? { toolCalls: m.tool_calls } : {}
20375
+ });
20270
20376
  }
20271
20377
  }
20272
20378
  const coalesced = [];
20273
20379
  for (const item of filteredMessages) {
20274
20380
  const last = coalesced[coalesced.length - 1];
20275
- if (last && last.role === item.role) {
20381
+ if (last && last.role === item.role && item.role !== "tool" && !last.toolCalls?.length && !item.toolCalls?.length) {
20276
20382
  last.text = `${last.text}
20277
20383
 
20278
20384
  ${item.text}`;
@@ -20281,7 +20387,7 @@ ${item.text}`;
20281
20387
  }
20282
20388
  }
20283
20389
  const chatMessageBuffers = coalesced.map((m) => encodeDevinChatMessage(m));
20284
- const completionConfig = buildCompletionConfig();
20390
+ const completionConfig = buildCompletionConfig({ temperature });
20285
20391
  const modelConfig = Buffer.concat([
20286
20392
  writeStringField(1, randomUUID2()),
20287
20393
  writeVarint(2, 1),
@@ -20293,6 +20399,7 @@ ${item.text}`;
20293
20399
  ...chatMessageBuffers.map((b) => writeMessageField(3, b)),
20294
20400
  writeVarint(7, 5),
20295
20401
  writeMessageField(8, completionConfig),
20402
+ ...tools.map((tool) => writeMessageField(10, encodeDevinToolDefinition(tool))),
20296
20403
  writeMessageField(15, modelConfig),
20297
20404
  writeStringField(16, randomUUID2()),
20298
20405
  writeVarint(20, 1),
@@ -20323,6 +20430,77 @@ function parseDevinConnectFrames(buffer3) {
20323
20430
  remainder: buffer3.subarray(offset)
20324
20431
  };
20325
20432
  }
20433
+ function parseDevinMetadataUsage(metaBytes) {
20434
+ let prompt = 0;
20435
+ let completion;
20436
+ let cacheRead = 0;
20437
+ let cacheWrite = 0;
20438
+ let sawAny = false;
20439
+ let offset = 0;
20440
+ while (offset < metaBytes.length) {
20441
+ let tag = 0;
20442
+ let shift = 0;
20443
+ while (offset < metaBytes.length) {
20444
+ const b = metaBytes[offset++];
20445
+ tag |= (b & 127) << shift;
20446
+ if (!(b & 128)) break;
20447
+ shift += 7;
20448
+ }
20449
+ const fieldNum = tag >>> 3;
20450
+ const wireType = tag & 7;
20451
+ if (wireType === 0) {
20452
+ let val = 0;
20453
+ let s = 0;
20454
+ while (offset < metaBytes.length) {
20455
+ const b = metaBytes[offset++];
20456
+ val |= (b & 127) << s;
20457
+ if (!(b & 128)) break;
20458
+ s += 7;
20459
+ }
20460
+ if (fieldNum === 2) {
20461
+ prompt = val;
20462
+ sawAny = true;
20463
+ } else if (fieldNum === 3) {
20464
+ completion = val;
20465
+ sawAny = true;
20466
+ } else if (fieldNum === 4) {
20467
+ cacheWrite = val;
20468
+ sawAny = true;
20469
+ } else if (fieldNum === 5) {
20470
+ cacheRead = val;
20471
+ sawAny = true;
20472
+ }
20473
+ } else if (wireType === 2) {
20474
+ let len = 0;
20475
+ let s = 0;
20476
+ while (offset < metaBytes.length) {
20477
+ const b = metaBytes[offset++];
20478
+ len |= (b & 127) << s;
20479
+ if (!(b & 128)) break;
20480
+ s += 7;
20481
+ }
20482
+ offset += len;
20483
+ } else {
20484
+ break;
20485
+ }
20486
+ }
20487
+ return sawAny ? { prompt, completion, cacheRead, cacheWrite } : null;
20488
+ }
20489
+ function attachDevinUpstreamFailure(error, failure) {
20490
+ if (failure) {
20491
+ error.devinUpstreamFailure = failure;
20492
+ }
20493
+ return error;
20494
+ }
20495
+ function readDevinUpstreamFailure(error) {
20496
+ if (!error || typeof error !== "object") return void 0;
20497
+ const failure = error.devinUpstreamFailure;
20498
+ if (!failure || typeof failure !== "object") return void 0;
20499
+ if (typeof failure.status !== "number" || !Number.isFinite(failure.status)) {
20500
+ return void 0;
20501
+ }
20502
+ return failure;
20503
+ }
20326
20504
  function parseDevinConnectTrailer(payload) {
20327
20505
  const text = payload.toString("utf8").trim();
20328
20506
  if (!text || text === "{}") return null;
@@ -20333,7 +20511,11 @@ function parseDevinConnectTrailer(payload) {
20333
20511
  }
20334
20512
  const code = typeof parsed.error.code === "string" ? parsed.error.code : "unknown";
20335
20513
  const message = typeof parsed.error.message === "string" ? parsed.error.message : "Unknown error";
20336
- return new Error(`Devin Connect upstream error ${code}: ${message}`);
20514
+ return attachDevinUpstreamFailure(
20515
+ new Error(`Devin Connect upstream error ${code}: ${message}`),
20516
+ // Connect/gRPC 的 resource_exhausted 与 HTTP 429 同义(限流/配额耗尽)。
20517
+ code === "resource_exhausted" ? { status: 429, body: parsed.error } : void 0
20518
+ );
20337
20519
  } catch {
20338
20520
  return new Error(`Devin Connect invalid terminal trailer: ${text}`);
20339
20521
  }
@@ -20343,6 +20525,7 @@ function decodeDevinResponsePayload(payload) {
20343
20525
  let content = "";
20344
20526
  let reasoning = "";
20345
20527
  let finishReason = null;
20528
+ let usage = null;
20346
20529
  const events = [];
20347
20530
  while (offset < payload.length) {
20348
20531
  let tag = 0;
@@ -20365,7 +20548,7 @@ function decodeDevinResponsePayload(payload) {
20365
20548
  s += 7;
20366
20549
  }
20367
20550
  if (fieldNum === 5) {
20368
- finishReason = val === 2 ? "stop" : "length";
20551
+ finishReason = val === 10 ? "tool_calls" : val === 1 || val === 3 ? "length" : "stop";
20369
20552
  }
20370
20553
  } else if (wireType === 2) {
20371
20554
  let len = 0;
@@ -20386,15 +20569,55 @@ function decodeDevinResponsePayload(payload) {
20386
20569
  const text = valBytes.toString("utf8");
20387
20570
  reasoning += text;
20388
20571
  events.push({ type: "reasoning", text });
20572
+ } else if (fieldNum === 6) {
20573
+ let nestedOffset = 0;
20574
+ let id;
20575
+ let name;
20576
+ let argsDelta;
20577
+ while (nestedOffset < valBytes.length) {
20578
+ let nestedTag = 0;
20579
+ let nestedShift = 0;
20580
+ while (nestedOffset < valBytes.length) {
20581
+ const b = valBytes[nestedOffset++];
20582
+ nestedTag |= (b & 127) << nestedShift;
20583
+ if (!(b & 128)) break;
20584
+ nestedShift += 7;
20585
+ }
20586
+ const nestedField = nestedTag >>> 3;
20587
+ const nestedWire = nestedTag & 7;
20588
+ if (nestedWire !== 2) break;
20589
+ let nestedLength = 0;
20590
+ let lengthShift = 0;
20591
+ while (nestedOffset < valBytes.length) {
20592
+ const b = valBytes[nestedOffset++];
20593
+ nestedLength |= (b & 127) << lengthShift;
20594
+ if (!(b & 128)) break;
20595
+ lengthShift += 7;
20596
+ }
20597
+ const nestedValue = valBytes.subarray(nestedOffset, nestedOffset + nestedLength);
20598
+ nestedOffset += nestedLength;
20599
+ const text = nestedValue.toString("utf8");
20600
+ if (nestedField === 1) id = text;
20601
+ else if (nestedField === 2) name = text;
20602
+ else if (nestedField === 3) argsDelta = (argsDelta ?? "") + text;
20603
+ }
20604
+ if (id !== void 0 && name !== void 0) {
20605
+ events.push({ type: "tool-call-start", id, name });
20606
+ }
20607
+ if (argsDelta !== void 0) {
20608
+ events.push({ type: "tool-call-args", argsDelta, ...id ? { id } : {} });
20609
+ }
20610
+ } else if (fieldNum === 7) {
20611
+ usage = parseDevinMetadataUsage(valBytes);
20389
20612
  }
20390
20613
  } else {
20391
20614
  break;
20392
20615
  }
20393
20616
  }
20394
- return { content, reasoning, finishReason, events };
20617
+ return { content, reasoning, finishReason, events, usage };
20395
20618
  }
20396
20619
  function createDevinProvider(options) {
20397
- const { token, model = "swe-2", fetchImpl = fetch } = options;
20620
+ const { token, model = "swe-2-max", fetchImpl = fetch, tools = [], temperature } = options;
20398
20621
  return {
20399
20622
  model,
20400
20623
  async complete(messages, opts) {
@@ -20402,7 +20625,9 @@ function createDevinProvider(options) {
20402
20625
  const requestPayload = buildGetChatMessageRequest({
20403
20626
  token,
20404
20627
  messages,
20405
- model: selectedModel
20628
+ model: selectedModel,
20629
+ tools,
20630
+ temperature
20406
20631
  });
20407
20632
  const frame = buildDevinConnectFrame(requestPayload);
20408
20633
  const response = await fetchImpl(DEVIN_CONNECT_URL, {
@@ -20417,8 +20642,11 @@ function createDevinProvider(options) {
20417
20642
  });
20418
20643
  if (!response.ok) {
20419
20644
  const errText = await response.text().catch(() => "");
20420
- throw new Error(
20421
- `Devin Connect upstream returned error ${response.status}: ${errText}`
20645
+ throw attachDevinUpstreamFailure(
20646
+ new Error(
20647
+ `Devin Connect upstream returned error ${response.status}: ${errText}`
20648
+ ),
20649
+ { status: response.status, body: errText }
20422
20650
  );
20423
20651
  }
20424
20652
  if (!response.body) {
@@ -20426,6 +20654,11 @@ function createDevinProvider(options) {
20426
20654
  }
20427
20655
  let text = "";
20428
20656
  let reasoning = "";
20657
+ let finishReason = null;
20658
+ let frameUsage = null;
20659
+ const toolCallOrder = [];
20660
+ const toolCallById = /* @__PURE__ */ new Map();
20661
+ let currentToolCallId = null;
20429
20662
  let buffer3 = Buffer.alloc(0);
20430
20663
  let sawTrailer = false;
20431
20664
  const reader = response.body.getReader();
@@ -20447,13 +20680,34 @@ function createDevinProvider(options) {
20447
20680
  continue;
20448
20681
  }
20449
20682
  const delta = decodeDevinResponsePayload(f.payload);
20683
+ if (delta.usage?.completion !== void 0) {
20684
+ frameUsage = delta.usage;
20685
+ }
20450
20686
  text += delta.content;
20451
20687
  reasoning += delta.reasoning;
20688
+ if (delta.finishReason) finishReason = delta.finishReason;
20452
20689
  for (const event of delta.events) {
20453
20690
  if (event.type === "reasoning") {
20454
20691
  opts?.onReasoningDelta?.(event.text);
20455
- } else {
20692
+ } else if (event.type === "content") {
20456
20693
  opts?.onTextDelta?.(event.text);
20694
+ } else if (event.type === "tool-call-start") {
20695
+ const existing = toolCallById.get(event.id);
20696
+ if (existing) {
20697
+ existing.name = event.name;
20698
+ } else {
20699
+ toolCallOrder.push(event.id);
20700
+ toolCallById.set(event.id, {
20701
+ id: event.id,
20702
+ name: event.name,
20703
+ argumentsText: ""
20704
+ });
20705
+ }
20706
+ currentToolCallId = event.id;
20707
+ } else {
20708
+ const targetId = event.id ?? currentToolCallId;
20709
+ const target = targetId ? toolCallById.get(targetId) : void 0;
20710
+ if (target) target.argumentsText += event.argsDelta;
20457
20711
  }
20458
20712
  }
20459
20713
  }
@@ -20467,14 +20721,42 @@ function createDevinProvider(options) {
20467
20721
  if (!sawTrailer) {
20468
20722
  throw new Error("Devin Connect upstream ended before the terminal trailer");
20469
20723
  }
20470
- if (!text && !reasoning) {
20471
- throw new Error("Devin Connect upstream completed without content");
20724
+ const toolCalls = toolCallOrder.map((id) => {
20725
+ const call = toolCallById.get(id);
20726
+ return {
20727
+ id: call.id,
20728
+ type: "function",
20729
+ function: {
20730
+ name: call.name,
20731
+ arguments: call.argumentsText || "{}"
20732
+ }
20733
+ };
20734
+ });
20735
+ if (!text && !reasoning && toolCalls.length === 0) {
20736
+ throw new Error("Devin Connect upstream completed without content or tool calls");
20472
20737
  }
20738
+ const usage = frameUsage ? {
20739
+ prompt_tokens: frameUsage.prompt + frameUsage.cacheRead,
20740
+ completion_tokens: frameUsage.completion ?? 0,
20741
+ total_tokens: frameUsage.prompt + frameUsage.cacheRead + (frameUsage.completion ?? 0) + frameUsage.cacheWrite,
20742
+ ...frameUsage.cacheRead > 0 ? { prompt_tokens_details: { cached_tokens: frameUsage.cacheRead } } : {},
20743
+ ...frameUsage.cacheWrite > 0 ? { cache_creation_input_tokens: frameUsage.cacheWrite } : {}
20744
+ } : void 0;
20473
20745
  return {
20474
20746
  content: text,
20475
20747
  model: selectedModel,
20476
20748
  provider: "devin",
20477
- reasoning_content: reasoning || void 0
20749
+ ...usage ? { usage } : {},
20750
+ ...reasoning ? { reasoning_content: reasoning } : {},
20751
+ ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {},
20752
+ // The upstream finish event wins whenever it is present. A tool call
20753
+ // cut off by the length limit has to stay "length" instead of being
20754
+ // relabelled "tool_calls", so probes/telemetry and any downstream
20755
+ // consumer of the final finish_reason no longer see a truncated turn
20756
+ // mislabelled as a normal one. localLoop keeps its own truncation
20757
+ // gates and excludes tool-bearing turns from them.
20758
+ finish_reason: finishReason ?? (toolCalls.length > 0 ? "tool_calls" : "stop"),
20759
+ stream_complete: true
20478
20760
  };
20479
20761
  }
20480
20762
  };
@@ -20533,5 +20815,6 @@ export {
20533
20815
  buildGeminiGenerateContentRequest,
20534
20816
  resolveAntigravityFinishReason,
20535
20817
  fetchAntigravityCloudCodeCompletion,
20818
+ readDevinUpstreamFailure,
20536
20819
  createDevinProvider
20537
20820
  };