nolo-cli 0.212.0-alpha.1 → 0.213.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 (124) hide show
  1. package/{_executeModel-KE6MBNUJ.js → _executeModel-CUJLCFWR.js} +35 -35
  2. package/{addContentAction-N2YC5IL3.js → addContentAction-WERAONNY.js} +5 -5
  3. package/{addDialogAgentAction-3NKVQCEC.js → addDialogAgentAction-7VI7YRML.js} +4 -4
  4. package/{agentDeleteCommand-NWK7VU4N.js → agentDeleteCommand-XJORXPGU.js} +42 -42
  5. package/{agentEmailCommands-UZ4DHEX2.js → agentEmailCommands-XTYYFR6Z.js} +43 -43
  6. package/{agentListCommands-HE7L2A6L.js → agentListCommands-OBLRBLHS.js} +60 -51
  7. package/{agentMachineCommands-6TE4MBMP.js → agentMachineCommands-5FMT3OT5.js} +13 -12
  8. package/{agentRecordCommands-L3QQ7HZ4.js → agentRecordCommands-ZQ7SVYGG.js} +42 -42
  9. package/{agentRunCommand-2IFGQHCO.js → agentRunCommand-WS7IGO2W.js} +43 -43
  10. package/{agentRunControl-OI3LOENH.js → agentRunControl-TAMHJQSR.js} +3 -3
  11. package/{agentRuntimeLocal-HL526DNQ.js → agentRuntimeLocal-SOO2OPJG.js} +10 -10
  12. package/{agentSlice-TMYP2UEA.js → agentSlice-CHR6KOZN.js} +5 -5
  13. package/{appDeleteCommands-ZERW4AXY.js → appDeleteCommands-GUP6P5YE.js} +15 -14
  14. package/{appDeployCommands-DUWSMYHL.js → appDeployCommands-MH2FX4O5.js} +15 -14
  15. package/{appDeployStatusCommands-N2KIE4BF.js → appDeployStatusCommands-SUHN3HPJ.js} +15 -14
  16. package/{appGetCommands-YVR4NPVY.js → appGetCommands-YWDEX6Z4.js} +15 -14
  17. package/{appListCommands-JS6HOHZS.js → appListCommands-ZQVMLPAY.js} +15 -14
  18. package/{authCommand-FE2C3ZUM.js → authCommand-PWX2ZNUP.js} +2 -2
  19. package/{builtinSkillRegistry-3YOIGEF6.js → builtinSkillRegistry-2QETTQPM.js} +2 -2
  20. package/{chromeOpsCommands-HEN5AX2J.js → chromeOpsCommands-DIVM2K3T.js} +1 -1
  21. package/{chunk-YUXQAVRD.js → chunk-3UKL3HPD.js} +251 -14
  22. package/{chunk-PDGKOTO7.js → chunk-4T2WNOY7.js} +4 -4
  23. package/{chunk-NOMIGOST.js → chunk-5MQRBSMW.js} +35 -41
  24. package/{chunk-5RYBGBQH.js → chunk-6DWMYIUY.js} +3 -3
  25. package/{chunk-KSTEXKL6.js → chunk-75XIDFBW.js} +4 -3
  26. package/{chunk-CZM5YTZ7.js → chunk-7BT37FK3.js} +193 -305
  27. package/{chunk-GPO7FCEZ.js → chunk-7QTZCMKL.js} +159 -106
  28. package/{chunk-NJR3WKVP.js → chunk-7Y4YTDQF.js} +6 -6
  29. package/{chunk-CKAHAVMK.js → chunk-A7TFUNZO.js} +35 -25
  30. package/{chunk-PJNFNEJE.js → chunk-ALQ3657A.js} +28 -2
  31. package/{chunk-YJMRHHGY.js → chunk-BBJ22CLB.js} +5 -5
  32. package/{chunk-AVTVGMDY.js → chunk-COFEN36Z.js} +1 -1
  33. package/{chunk-DM6UMOEL.js → chunk-CRHOO2ZR.js} +1 -1
  34. package/{chunk-EYUBHHFB.js → chunk-EHGEOAHI.js} +2 -2
  35. package/{chunk-Y2WUYX4I.js → chunk-EHSZD5OI.js} +1 -1
  36. package/{chunk-QT6PKHYJ.js → chunk-FUGMREBO.js} +2 -2
  37. package/{chunk-BFM34N54.js → chunk-FW6YHHLI.js} +4 -4
  38. package/{chunk-3FOQHGHN.js → chunk-G2BQWAVT.js} +2 -2
  39. package/{chunk-VPAZOH2D.js → chunk-GPVRUGGY.js} +1 -1
  40. package/{chunk-JNKSDZFS.js → chunk-H4UOTUOX.js} +162 -48
  41. package/{chunk-UTV2AYEG.js → chunk-H5HF7TJA.js} +1 -1
  42. package/{chunk-IJXYFTV7.js → chunk-H7PPIYWP.js} +1 -1
  43. package/{chunk-NRFKLKRU.js → chunk-HSVBJRF4.js} +1 -1
  44. package/{chunk-GNDJKC4B.js → chunk-HUBHAQK2.js} +8 -8
  45. package/{chunk-LKDF3ATR.js → chunk-INUFILG6.js} +1 -1
  46. package/{chunk-OHNELDTO.js → chunk-IXNGOHV5.js} +2 -2
  47. package/{chunk-WXYLV624.js → chunk-J2NHJ7WX.js} +4 -4
  48. package/{chunk-YPD3VJZW.js → chunk-KFLT5KAD.js} +4 -4
  49. package/{chunk-OOF4UPC5.js → chunk-LLRPJECG.js} +4 -4
  50. package/{chunk-B7NJ3T4A.js → chunk-MDB67IWP.js} +4 -4
  51. package/{chunk-IF45XG7K.js → chunk-MOIJOHA5.js} +9 -4
  52. package/{chunk-IIQ6JGRI.js → chunk-NJLL224W.js} +1 -1
  53. package/{chunk-YOIW77S2.js → chunk-NOKUNJON.js} +2 -2
  54. package/{chunk-HIRC32DV.js → chunk-NSAPDIN2.js} +8 -4
  55. package/{chunk-JIT6FSXJ.js → chunk-O2TZF4AI.js} +1 -1
  56. package/{chunk-4KH4J3G5.js → chunk-OQPKUBPB.js} +1 -1
  57. package/{chunk-OXME3PEE.js → chunk-PCLE46CE.js} +1 -1
  58. package/{chunk-CJRONOYL.js → chunk-QG4AOSON.js} +8 -45
  59. package/{chunk-6OQUPIQF.js → chunk-QHPRUL4O.js} +3 -3
  60. package/{chunk-MMNYUVLW.js → chunk-R5O4QAWG.js} +1 -1
  61. package/{chunk-IAPCBNEE.js → chunk-RLBNLZVC.js} +1 -1
  62. package/{chunk-FHU44LN3.js → chunk-RSHTSUEG.js} +246 -77
  63. package/{chunk-25JZEIXN.js → chunk-RZOV7OMV.js} +11 -11
  64. package/{chunk-D2MDMY2M.js → chunk-SVYO6AO7.js} +7 -1
  65. package/{chunk-INSBKSBB.js → chunk-TAKB25RM.js} +1 -1
  66. package/{chunk-RU4SD66C.js → chunk-TX5EYMGT.js} +5 -5
  67. package/{chunk-D5RCRX4A.js → chunk-UYD5HQ6T.js} +746 -177
  68. package/{chunk-JTDORD5P.js → chunk-V5Y3R6BT.js} +110 -2
  69. package/{chunk-3RQVYR4B.js → chunk-VNYYOX3V.js} +59 -20
  70. package/{chunk-3AKFVZ2I.js → chunk-VO4WSAVP.js} +3 -3
  71. package/{chunk-6NSSLV67.js → chunk-WMTBPBMP.js} +6 -6
  72. package/{chunk-EZFQ6XRV.js → chunk-WRYQ35LQ.js} +4 -4
  73. package/{chunk-SOHTHFV2.js → chunk-WYDI6TGM.js} +14 -14
  74. package/{chunk-M6RL5JCD.js → chunk-X7SDOJUM.js} +3 -3
  75. package/{chunk-CQ46IRNN.js → chunk-XHQC4PXX.js} +48 -34
  76. package/{chunk-2WZXXAQD.js → chunk-YB3YZK56.js} +67 -1
  77. package/{chunk-22CIEBYO.js → chunk-YJAFKXMU.js} +11 -11
  78. package/{chunk-GSQNH63I.js → chunk-YTF4WH7J.js} +3 -19
  79. package/{chunk-5AQR3DGV.js → chunk-ZNWI6OVT.js} +2 -2
  80. package/{chunk-VZKUKCA6.js → chunk-ZXOUUDCG.js} +49 -40
  81. package/{commandRegistry-VVMTPFAX.js → commandRegistry-W63CYRZJ.js} +64 -64
  82. package/{contentThunks-N52O67MI.js → contentThunks-U7PL5MQD.js} +12 -12
  83. package/{cooldownCommands-YXLWOXWR.js → cooldownCommands-TIGUHXTD.js} +3 -3
  84. package/{createAgentAutomationAction-ZC57YRSV.js → createAgentAutomationAction-OLBQDD6R.js} +12 -12
  85. package/{createDialogAction-VQW5WDYX.js → createDialogAction-SVH6B3SB.js} +14 -14
  86. package/{createPageAction-M5VDA7SC.js → createPageAction-F4DIWZ2Z.js} +14 -14
  87. package/{dbSlice-HP7G4LX3.js → dbSlice-BVH2YZPQ.js} +4 -4
  88. package/{deleteFile-7VQGLK2V.js → deleteFile-5YOEKV4U.js} +2 -2
  89. package/{dialogCommands-JHMR2N2Y.js → dialogCommands-KHIEPY3M.js} +18 -18
  90. package/{dialogSlice-GRSVGDTF.js → dialogSlice-ZCTDV2PZ.js} +11 -11
  91. package/{docCreateCommands-F2K3UPSQ.js → docCreateCommands-DPMZI7G5.js} +14 -13
  92. package/{docDeleteCommands-XGXB5RFT.js → docDeleteCommands-IRQQLUBT.js} +16 -16
  93. package/{docReadCommands-7HWZZV6J.js → docReadCommands-LK37XH6L.js} +14 -13
  94. package/{docStore-KNZK6Y2G.js → docStore-V2KECILA.js} +5 -5
  95. package/{docUpdateCommands-OZZ7NQII.js → docUpdateCommands-O6T3HJHH.js} +14 -13
  96. package/{fetchMessages-M3QU7ZCQ.js → fetchMessages-NR4KNQLO.js} +2 -2
  97. package/{handleSendMessageAction-SHIH74GJ.js → handleSendMessageAction-2PE25WM3.js} +25 -25
  98. package/{hostCredentialCreate-X64WK4JD.js → hostCredentialCreate-S233UMAP.js} +42 -42
  99. package/index.js +7 -7
  100. package/{localDialogRead-XRLAH4CZ.js → localDialogRead-7NNDVVF7.js} +1 -1
  101. package/{localRuntimeAdapter-A7K3ABA6.js → localRuntimeAdapter-KY6WXNBR.js} +41 -41
  102. package/{machineCommands-DE3MR7CJ.js → machineCommands-Q6HA3IXF.js} +42 -42
  103. package/{machineWsRunDispatch-R26P5V36.js → machineWsRunDispatch-WWJQ6WR3.js} +42 -42
  104. package/{memberThunks-7WJAY5V6.js → memberThunks-7EDJADWU.js} +5 -5
  105. package/package.json +1 -1
  106. package/{projectArchive-GGL5HLMD.js → projectArchive-DRTDIMEV.js} +14 -14
  107. package/{read-4VZQODSN.js → read-4V2RMG7N.js} +2 -2
  108. package/{readDocTool-AVD32RUJ.js → readDocTool-BH6ATGQO.js} +1 -1
  109. package/{readlineWorkspace-XKYKNL2I.js → readlineWorkspace-2W4WRCOU.js} +322 -159
  110. package/{removeDialogAgentAction-FKL4BYMR.js → removeDialogAgentAction-LYAZGNKW.js} +5 -5
  111. package/{runAgentClientLoop-V7E2WBVM.js → runAgentClientLoop-J2VQBS2Z.js} +33 -33
  112. package/{secretCommands-4QS3HCA2.js → secretCommands-LXDWOLZ6.js} +1 -1
  113. package/{setDialogExtraReferencesAction-TLIOLWWI.js → setDialogExtraReferencesAction-OCRPGPYH.js} +5 -5
  114. package/{setPrimaryDialogAgentAction-2OLWRFYR.js → setPrimaryDialogAgentAction-HFJDQIBU.js} +5 -5
  115. package/{settingThunks-UGBPCNYF.js → settingThunks-RAMHMTGM.js} +7 -7
  116. package/{spaceCommands-4KSLKVNF.js → spaceCommands-KVJZVKVO.js} +14 -14
  117. package/{streamAgentChatTurn-7FR7X2NU.js → streamAgentChatTurn-GHBMJJ6S.js} +37 -37
  118. package/{switchDialogAgentAction-UZSAG2N5.js → switchDialogAgentAction-EBRMW3RA.js} +4 -4
  119. package/{tools-ZLKCC25W.js → tools-EWKCPKMS.js} +28 -28
  120. package/{tuiSummaryLlmCaller-WC54MVGP.js → tuiSummaryLlmCaller-ALQYUUWB.js} +41 -12
  121. package/{updateCommands-4744PL3P.js → updateCommands-ZQYU6SCI.js} +3 -3
  122. package/{updateDialogTitleAction-U53ZT7PJ.js → updateDialogTitleAction-5GGMBTDF.js} +12 -12
  123. package/{windowsSelfUpdate-XSQVF2GK.js → windowsSelfUpdate-257DV2T6.js} +2 -2
  124. package/{workloadLifecycle-4BEQHMSD.js → workloadLifecycle-4C42SVBD.js} +1 -0
@@ -6,7 +6,7 @@ import {
6
6
  flushChatCompletionStream,
7
7
  providerHttpFailure,
8
8
  throwIfChatCompletionStreamFailed
9
- } from "./chunk-4KH4J3G5.js";
9
+ } from "./chunk-OQPKUBPB.js";
10
10
  import {
11
11
  DEVIN_CONNECT_URL,
12
12
  parseSseDataLineJson,
@@ -66,7 +66,7 @@ import {
66
66
  hashStablePrefixContent,
67
67
  resolveResponseGuidelines,
68
68
  resolveToolGuidedSections
69
- } from "./chunk-NOMIGOST.js";
69
+ } from "./chunk-5MQRBSMW.js";
70
70
  import {
71
71
  classifyUnparsableToolArgs,
72
72
  collectStablePromptPrefix,
@@ -90,7 +90,7 @@ import {
90
90
  toOpenAiCompatibleMessages,
91
91
  toResponsesTools,
92
92
  transferDowngradedToolTurnOrigin
93
- } from "./chunk-UTV2AYEG.js";
93
+ } from "./chunk-H5HF7TJA.js";
94
94
  import {
95
95
  TOOL_HAD_IMAGES,
96
96
  TOOL_IMAGES,
@@ -122,15 +122,16 @@ import {
122
122
  parseNoloWorkspaceToolArguments,
123
123
  readTextStreamUpToChars,
124
124
  resolveNoloDialogInput
125
- } from "./chunk-25JZEIXN.js";
125
+ } from "./chunk-RZOV7OMV.js";
126
126
  import {
127
127
  applyToolSurfaceConstraints
128
128
  } from "./chunk-KC4XDTT3.js";
129
129
  import {
130
130
  clipToolText,
131
+ planContextUsage,
131
132
  resolveHistoricalToolContentCap,
132
133
  resolveToolOutputProfile
133
- } from "./chunk-2WZXXAQD.js";
134
+ } from "./chunk-YB3YZK56.js";
134
135
  import {
135
136
  resolveAgentImageInputSupport,
136
137
  resolveToolImagePolicy
@@ -149,25 +150,22 @@ import {
149
150
  resolveEmptyAssistantFallbackMessage,
150
151
  resolveEmptyAssistantOutcome,
151
152
  resolveTruncatedReasoningTailLog
152
- } from "./chunk-JTDORD5P.js";
153
+ } from "./chunk-V5Y3R6BT.js";
153
154
  import {
154
155
  COMPACTION_SUMMARY_SCHEMA_VERSION,
155
156
  COMPACTION_SUMMARY_SYSTEM_PROMPT,
156
- TOOL_RESULT_TRUNCATE_CHARS,
157
+ buildBoundedCompactionRequest,
157
158
  buildCompactionMetricsFromPlan,
158
- buildCompactionUserContent,
159
- computeSummaryInputTokenCap,
160
159
  computeSummarySourceMetadata,
161
160
  formatCompactionMetricsLog,
162
- formatFileOperationsFromMessages,
163
- formatMessagesForSummaryWithTruncation,
161
+ getMessageTokenCount,
164
162
  getModelContextWindow,
165
163
  planCompression,
166
- planContextUsage,
167
164
  resolveCompressionTriggerRatio,
165
+ resolveEffectiveCompactionBudgets,
168
166
  sha256Hex,
169
167
  truncateToolOutputForContext
170
- } from "./chunk-CZM5YTZ7.js";
168
+ } from "./chunk-7BT37FK3.js";
171
169
  import {
172
170
  createProviderCallTimingTracker,
173
171
  finalizeProviderCallTiming,
@@ -219,7 +217,7 @@ import {
219
217
  spillToolOutput,
220
218
  toProcessTaskView,
221
219
  truncateToolOutput
222
- } from "./chunk-JNKSDZFS.js";
220
+ } from "./chunk-H4UOTUOX.js";
223
221
  import {
224
222
  estimateTokenCount
225
223
  } from "./chunk-PSCNXBYA.js";
@@ -15037,14 +15035,14 @@ var configuredConcurrency = Number(process.env.NOLO_VISUAL_RENDERER_CONCURRENCY
15037
15035
  var renderSlots = new VisualRenderSemaphore(Number.isFinite(configuredConcurrency) ? Math.max(1, Math.min(2, Math.floor(configuredConcurrency))) : 2);
15038
15036
  var require2 = createRequire(import.meta.url);
15039
15037
  function visualRendererCapability() {
15040
- if (process.env.NOLO_VISUAL_RENDERER_DISABLED === "1") return { available: false, reason: "Platform renderer disabled by NOLO_VISUAL_RENDERER_DISABLED=1 on this runtime host." };
15038
+ if (process.env.NOLO_VISUAL_RENDERER_DISABLED === "1") return { available: false, reasonCode: "disabled", reason: "Platform renderer disabled by NOLO_VISUAL_RENDERER_DISABLED=1 on this runtime host." };
15041
15039
  try {
15042
15040
  const { chromium } = require2("playwright");
15043
15041
  const executablePath = [chromium.executablePath(), resolveExecutableOnPath("google-chrome"), resolveExecutableOnPath("chromium"), resolveExecutableOnPath("chromium-browser")].find((path) => !!path && existsSync(path));
15044
- if (!executablePath) return { available: false, reason: "No Chromium renderer installed. Install Playwright Chromium (playwright install chromium) or system Chromium/Google Chrome on the runtime host." };
15042
+ if (!executablePath) return { available: false, reasonCode: "missing-browser", reason: "No Chromium renderer installed. Install Playwright Chromium (playwright install chromium) or system Chromium/Google Chrome on the runtime host." };
15045
15043
  return { available: true, chromium, executablePath };
15046
15044
  } catch {
15047
- return { available: false, reason: "Platform renderer unavailable: install the platform Playwright dependency and Chromium on the runtime host (not a workspace npm script)." };
15045
+ return { available: false, reasonCode: "dependency-unavailable", reason: "Platform renderer unavailable: install the platform Playwright dependency and Chromium on the runtime host (not a workspace npm script)." };
15048
15046
  }
15049
15047
  }
15050
15048
  async function renderVisualPage(args2) {
@@ -15557,8 +15555,11 @@ function readProcessOwner(runtimeContext) {
15557
15555
  if (!dialogId) return null;
15558
15556
  const turnId = asNonEmptyString(runtimeContext.turnId);
15559
15557
  const ownerId = asNonEmptyString(runtimeContext.ownerId);
15558
+ const getDepth = runtimeContext.getProcessCausalDepth;
15559
+ const depth = typeof getDepth === "function" ? getDepth() : runtimeContext.processCausalDepth;
15560
15560
  return {
15561
15561
  dialogId,
15562
+ ...typeof depth === "number" && Number.isSafeInteger(depth) && depth >= 0 ? { processCausalDepth: depth } : {},
15562
15563
  ...turnId ? { turnId } : {},
15563
15564
  ...ownerId ? { ownerId } : {}
15564
15565
  };
@@ -18919,6 +18920,7 @@ function toPlanCompressionMessages(history) {
18919
18920
  id: typeof message.id === "string" && message.id.length > 0 ? message.id : `local-${index}`,
18920
18921
  role: message.role,
18921
18922
  content: message.content ?? "",
18923
+ ...typeof message.tool_call_id === "string" ? { tool_call_id: message.tool_call_id } : {},
18922
18924
  ...Array.isArray(message.tool_calls) ? { tool_calls: message.tool_calls } : {}
18923
18925
  }));
18924
18926
  }
@@ -18936,9 +18938,10 @@ function hashSummarySourceSlice(history, summarizedBeforeId) {
18936
18938
  const found = findSummaryAnchorIndex(bridged, summarizedBeforeId);
18937
18939
  if (found === -1) return void 0;
18938
18940
  const slice = bridged.slice(0, found + 1).map((message, index) => ({
18939
- ...message,
18940
18941
  id: `local-${index}`,
18941
- content: message.content ?? ""
18942
+ role: message.role,
18943
+ content: message.content ?? "",
18944
+ ...Array.isArray(message.tool_calls) ? { tool_calls: message.tool_calls } : {}
18942
18945
  }));
18943
18946
  return sha256Hex(JSON.stringify(slice));
18944
18947
  }
@@ -18976,22 +18979,22 @@ function projectHistoryWithSummary(args2) {
18976
18979
  if (found !== -1) startIndex = found + 1;
18977
18980
  }
18978
18981
  const projected = args2.history.slice(startIndex);
18979
- const summaryMsg = args2.summary.trim().length > 0 ? [buildLocalSummaryHistoryMessage(args2.summary)] : [];
18980
- return [...summaryMsg, ...projected];
18981
- }
18982
- var COLD_RESUME_IDLE_MS = 60 * 60 * 1e3;
18983
- function isColdResume(history, nowMs) {
18984
- for (let i = history.length - 1; i >= 0; i -= 1) {
18985
- const at = history[i]?.createdAt;
18986
- if (typeof at === "number" && Number.isFinite(at)) {
18987
- return nowMs - at > COLD_RESUME_IDLE_MS;
18982
+ let latestUser = -1;
18983
+ for (let i = args2.history.length - 1; i >= 0; i--) {
18984
+ if (args2.history[i].role === "user") {
18985
+ latestUser = i;
18986
+ break;
18988
18987
  }
18989
18988
  }
18990
- return false;
18989
+ const preservedUser = latestUser >= 0 && latestUser < startIndex ? [args2.history[latestUser]] : [];
18990
+ const summaryMsg = args2.summary.trim().length > 0 ? [buildLocalSummaryHistoryMessage(args2.summary)] : [];
18991
+ return [...summaryMsg, ...preservedUser, ...projected];
18991
18992
  }
18993
+ var COLD_RESUME_IDLE_MS = 60 * 60 * 1e3;
18992
18994
  async function runSummaryFallback(fallback, content, options) {
18993
18995
  try {
18994
18996
  const text2 = await fallback(content, options);
18997
+ if (text2 === null) return { summary: null, error: "no summary" };
18995
18998
  if (typeof text2 !== "string") return { summary: null, error: "non-string output" };
18996
18999
  const trimmed = text2.trim();
18997
19000
  return trimmed.length > 0 ? { summary: trimmed } : { summary: null, error: "empty summary" };
@@ -19086,13 +19089,12 @@ async function maybeAutoCompactLocalHistory(args2) {
19086
19089
  const planAnchorId = summarizedBeforeId && /^local-(0|[1-9]\d*)$/.test(summarizedBeforeId) && legacyAnchorIndex >= 0 ? bridged[legacyAnchorIndex].id : summarizedBeforeId;
19087
19090
  const contextWindow = typeof args2.contextWindow === "number" && Number.isFinite(args2.contextWindow) && args2.contextWindow > 0 ? args2.contextWindow : getModelContextWindow(args2.model ?? "");
19088
19091
  const allMsgs = toPlanCompressionMessages(history);
19089
- const coldResume = isColdResume(history, args2.now?.() ?? Date.now());
19090
19092
  const plan = planCompression({
19091
19093
  allMsgs,
19092
19094
  summarizedBeforeId: planAnchorId,
19093
19095
  summary: existingSummary,
19094
19096
  contextWindow,
19095
- ...coldResume ? { coldResume: true, reason: "cold_resume" } : {},
19097
+ preserveLatestUserByProjection: true,
19096
19098
  ...args2.realContextUsagePercent !== void 0 ? { realContextUsagePercent: args2.realContextUsagePercent } : {}
19097
19099
  });
19098
19100
  const projectExisting = () => {
@@ -19147,6 +19149,21 @@ async function maybeAutoCompactLocalHistory(args2) {
19147
19149
  }
19148
19150
  };
19149
19151
  }
19152
+ const msgsToCompress = plan.msgsToCompress;
19153
+ const bounded3 = buildBoundedCompactionRequest({
19154
+ msgsToCompress,
19155
+ previousSummary: existingSummary,
19156
+ contextWindow,
19157
+ canonicalizeToolName
19158
+ });
19159
+ if (bounded3.status === "blocked") {
19160
+ const detail = `bounded compaction blocked: ${bounded3.reason}`;
19161
+ console.warn(`[localLoop] ${detail}`);
19162
+ return {
19163
+ ...projectExisting(),
19164
+ decision: { outcome: "skipped", skipReason: "bounded-request-blocked", detail }
19165
+ };
19166
+ }
19150
19167
  emitPhase({ kind: "compaction-start" });
19151
19168
  const compactionAttemptId = randomUUID();
19152
19169
  let compactionT0 = Date.now();
@@ -19187,23 +19204,9 @@ async function maybeAutoCompactLocalHistory(args2) {
19187
19204
  if (args2.abortSignal?.aborted) {
19188
19205
  throw Object.assign(new Error("auto-compaction aborted"), { compactionReason: "aborted" });
19189
19206
  }
19190
- const msgsToCompress = plan.msgsToCompress;
19191
- const messagesText = formatMessagesForSummaryWithTruncation(
19192
- msgsToCompress,
19193
- TOOL_RESULT_TRUNCATE_CHARS,
19194
- computeSummaryInputTokenCap(contextWindow)
19195
- );
19196
- const fileOpsText = formatFileOperationsFromMessages(
19197
- msgsToCompress,
19198
- canonicalizeToolName
19199
- );
19200
- const promptContent = buildCompactionUserContent({
19201
- previousSummary: existingSummary,
19202
- messagesText,
19203
- fileOpsText
19204
- });
19207
+ const promptContent = bounded3.promptContent;
19205
19208
  emitPhase({ kind: "waiting-provider" });
19206
- compactionInputSource = messagesText;
19209
+ compactionInputSource = promptContent;
19207
19210
  compactionPrevSummarySource = existingSummary;
19208
19211
  compactionT0 = Date.now();
19209
19212
  const timeoutMs = typeof args2.timeoutMs === "number" && Number.isFinite(args2.timeoutMs) && args2.timeoutMs > 0 ? args2.timeoutMs : void 0;
@@ -19220,7 +19223,7 @@ async function maybeAutoCompactLocalHistory(args2) {
19220
19223
  // 服务端 x-request-id 可对上,用于关联请求级证据。不进 prompt/摘要/持久化。
19221
19224
  requestId: compactionAttemptId,
19222
19225
  // Room for factual handoff, exact commands and file operations.
19223
- max_tokens: 8192,
19226
+ max_tokens: bounded3.outputTokenBudget,
19224
19227
  reasoning_effort: "low"
19225
19228
  }
19226
19229
  ).then(
@@ -19264,7 +19267,8 @@ async function maybeAutoCompactLocalHistory(args2) {
19264
19267
  {
19265
19268
  signal: args2.abortSignal,
19266
19269
  deadlineMs: Date.now() + 3e4,
19267
- attemptId: compactionAttemptId
19270
+ attemptId: compactionAttemptId,
19271
+ maxOutputTokens: bounded3.outputTokenBudget
19268
19272
  }
19269
19273
  );
19270
19274
  fallbackElapsedMs = Date.now() - fallbackT0;
@@ -19312,15 +19316,69 @@ async function maybeAutoCompactLocalHistory(args2) {
19312
19316
  fallbackElapsedMs
19313
19317
  });
19314
19318
  }
19319
+ const metricsReason = invalidSummary ? "invalid_summary" : "context_budget";
19320
+ const metrics = buildCompactionMetricsFromPlan({
19321
+ reason: metricsReason,
19322
+ previousSummary: existingSummary,
19323
+ plan: {
19324
+ msgsToCompress: bounded3.coveredMessages,
19325
+ msgsToKeep: [...msgsToCompress.slice(bounded3.coveredMessages.length), ...plan.msgsToKeep]
19326
+ },
19327
+ newSummary,
19328
+ summaryUsage: result4?.usage
19329
+ });
19330
+ const projectedHistory = projectHistoryWithSummary({
19331
+ history,
19332
+ summary: newSummary,
19333
+ summarizedBeforeId: bounded3.summarizedBeforeId
19334
+ });
19335
+ const projectedMessages = toPlanCompressionMessages(projectedHistory);
19336
+ const projectedTokens = projectedMessages.reduce((sum2, message) => sum2 + getMessageTokenCount(message), 0);
19337
+ const projectedRetained = projectedMessages.slice(1);
19338
+ metrics.retainedTokens = projectedRetained.reduce((sum2, message) => sum2 + getMessageTokenCount(message), 0);
19339
+ metrics.retainedCount = projectedRetained.length;
19340
+ const { effectiveHistoryBudget } = resolveEffectiveCompactionBudgets({
19341
+ contextWindow,
19342
+ triggerRatio: plan.diagnostics.triggerRatio,
19343
+ historyBudget: plan.diagnostics.historyBudget,
19344
+ rawMessageBudget: plan.diagnostics.historyBudget,
19345
+ summaryTokens: 0
19346
+ });
19347
+ const withinSafetyBudget = projectedTokens <= effectiveHistoryBudget;
19348
+ if (!withinSafetyBudget) {
19349
+ console.warn("[localLoop] compaction projection remains above safety budget", {
19350
+ projectedTokens,
19351
+ effectiveHistoryBudget
19352
+ });
19353
+ }
19354
+ const oldProjection = projectExisting();
19355
+ const beforeProjectedTokens = toPlanCompressionMessages(oldProjection.history).reduce((sum2, message) => sum2 + getMessageTokenCount(message), 0);
19356
+ const historyBudget = plan.diagnostics.historyBudget;
19357
+ if (projectedTokens >= beforeProjectedTokens || projectedTokens >= historyBudget) {
19358
+ const failureMessage = `compaction candidate rejected: ${projectedTokens >= beforeProjectedTokens ? "no token reduction" : "above history budget"} (before=${beforeProjectedTokens}, after=${projectedTokens}, budget=${historyBudget})`;
19359
+ console.warn(`[localLoop] ${failureMessage}`);
19360
+ recordCompactionFailure(dialogId, args2.now?.() ?? Date.now());
19361
+ logCompactionTiming("empty", {});
19362
+ emitPhase({ kind: "compaction-end" });
19363
+ return {
19364
+ ...oldProjection,
19365
+ failureMessage,
19366
+ ...result4?.usage ? { usage: result4.usage } : {},
19367
+ decision: { outcome: "skipped", skipReason: "unsafe-projection", detail: failureMessage }
19368
+ };
19369
+ }
19315
19370
  const sourceMeta = await computeSummarySourceMetadata(
19316
19371
  toPlanCompressionMessages(history),
19317
- plan.newSummarizedBeforeId
19372
+ bounded3.summarizedBeforeId
19318
19373
  );
19374
+ if (args2.abortSignal?.aborted) {
19375
+ throw Object.assign(new Error("auto-compaction aborted"), { compactionReason: "aborted" });
19376
+ }
19319
19377
  const saveT0 = Date.now();
19320
19378
  await adapter.saveDialogSummary({
19321
19379
  dialogId,
19322
19380
  summary: newSummary,
19323
- summarizedBeforeId: plan.newSummarizedBeforeId,
19381
+ summarizedBeforeId: bounded3.summarizedBeforeId,
19324
19382
  // 内容寻址失效检测:存锚点切片哈希与长度,供下轮载入校验。
19325
19383
  sourceHash: sourceMeta?.sourceHash,
19326
19384
  sourceCount: sourceMeta?.sourceCount ?? 0,
@@ -19332,14 +19390,6 @@ async function maybeAutoCompactLocalHistory(args2) {
19332
19390
  )
19333
19391
  });
19334
19392
  saveElapsedMs = Date.now() - saveT0;
19335
- const metricsReason = invalidSummary ? "invalid_summary" : coldResume ? "cold_resume" : "context_budget";
19336
- const metrics = buildCompactionMetricsFromPlan({
19337
- reason: metricsReason,
19338
- previousSummary: existingSummary,
19339
- plan,
19340
- newSummary,
19341
- summaryUsage: result4?.usage
19342
- });
19343
19393
  console.log(formatCompactionMetricsLog(metrics));
19344
19394
  logCompactionTiming("success", {
19345
19395
  model: result4?.model ?? args2.model ?? null,
@@ -19347,21 +19397,18 @@ async function maybeAutoCompactLocalHistory(args2) {
19347
19397
  });
19348
19398
  emitPhase({ kind: "compaction-end" });
19349
19399
  return {
19350
- history: projectHistoryWithSummary({
19351
- history,
19352
- summary: newSummary,
19353
- summarizedBeforeId: plan.newSummarizedBeforeId
19354
- }),
19400
+ history: projectedHistory,
19355
19401
  compressed: true,
19356
19402
  summaryGenerated: true,
19357
19403
  reason: metricsReason,
19358
- beforeTokens: metrics.previousSummaryTokens + metrics.compressedTokens,
19359
- afterTokens: metrics.newSummaryTokens + metrics.retainedTokens,
19404
+ beforeTokens: beforeProjectedTokens,
19405
+ afterTokens: projectedTokens,
19406
+ withinSafetyBudget,
19360
19407
  ...result4?.usage ? { usage: result4.usage } : {},
19361
19408
  metrics,
19362
19409
  decision: {
19363
19410
  outcome: "compressed",
19364
- trigger: invalidSummary ? "invalid-summary" : coldResume ? "cold-resume" : plan.diagnostics?.triggeredByRealUsage ? "real-usage" : "estimate",
19411
+ trigger: invalidSummary ? "invalid-summary" : plan.diagnostics?.triggeredByRealUsage ? "real-usage" : "estimate",
19365
19412
  ...plan.diagnostics ? {
19366
19413
  estimatedTokens: plan.diagnostics.totalUsed,
19367
19414
  historyBudget: plan.diagnostics.historyBudget,
@@ -19399,7 +19446,7 @@ function emitCompactionPhaseEvent(boundary, phase, scope3) {
19399
19446
  function emitCompactionObservationEvent(boundary, compacted) {
19400
19447
  const decision = compacted.decision;
19401
19448
  if (decision?.outcome === "skipped") {
19402
- const isProtectionGap = decision.skipReason === "adapter-missing-summary-methods" || decision.skipReason === "load-summary-failed";
19449
+ const isProtectionGap = decision.skipReason === "bounded-request-blocked" || decision.skipReason === "unsafe-projection" || decision.skipReason === "adapter-missing-summary-methods" || decision.skipReason === "load-summary-failed";
19403
19450
  const approachingBudget = decision.skipReason === "below-trigger" && typeof decision.estimatedTokens === "number" && typeof decision.historyBudget === "number" && decision.historyBudget > 0 && decision.estimatedTokens >= decision.historyBudget * 0.5;
19404
19451
  if (!isProtectionGap && !approachingBudget) return;
19405
19452
  }
@@ -19407,10 +19454,11 @@ function emitCompactionObservationEvent(boundary, compacted) {
19407
19454
  kind: "compaction",
19408
19455
  atMs: Date.now(),
19409
19456
  ...compacted.reason ? { reason: compacted.reason } : {},
19457
+ ...compacted.withinSafetyBudget === false ? { detail: "Estimated projected history remains above the compaction safety budget" } : {},
19410
19458
  summaryGenerated: compacted.summaryGenerated,
19411
19459
  compressed: compacted.compressed,
19412
19460
  ...compacted.failureMessage ? { failed: true, detail: compacted.failureMessage } : {},
19413
- ...decision?.outcome === "skipped" ? { skipped: true, skipReason: decision.skipReason } : {},
19461
+ ...decision?.outcome === "skipped" ? { skipped: true, skipReason: decision.skipReason, ...decision.detail ? { detail: decision.detail } : {} } : {},
19414
19462
  ...decision && "trigger" in decision && decision.trigger ? { trigger: decision.trigger } : {},
19415
19463
  ...decision && "estimatedTokens" in decision && decision.estimatedTokens !== void 0 ? { estimatedTokens: decision.estimatedTokens } : {},
19416
19464
  ...decision && "historyBudget" in decision && decision.historyBudget !== void 0 ? { historyBudget: decision.historyBudget } : {},
@@ -20041,13 +20089,23 @@ async function runLocalAgentTurn(input) {
20041
20089
  }
20042
20090
  return resolvedProvider;
20043
20091
  };
20092
+ let resolvedSummaryProvider;
20093
+ const resolveSummaryProviderOnce = async () => {
20094
+ if (!resolvedSummaryProvider) {
20095
+ resolvedSummaryProvider = await input.adapter.resolveProvider(agentConfig, {
20096
+ toolNames: [],
20097
+ prompt: ""
20098
+ });
20099
+ }
20100
+ return resolvedSummaryProvider;
20101
+ };
20044
20102
  const contextWindow = resolveAgentContextWindow(agentConfig);
20045
20103
  const compressionTriggerRatio = resolveCompressionTriggerRatio(contextWindow);
20046
20104
  const compaction = createTurnCompactionController({
20047
20105
  adapter: input.adapter,
20048
20106
  continueDialogId: input.continueDialogId,
20049
20107
  model: agentConfig.model,
20050
- resolveProvider: resolveProviderOnce,
20108
+ resolveProvider: resolveSummaryProviderOnce,
20051
20109
  contextWindow,
20052
20110
  compressionTriggerRatio,
20053
20111
  abortSignal: input.abortSignal,
@@ -20184,6 +20242,7 @@ async function runLocalAgentTurn(input) {
20184
20242
  };
20185
20243
  const newDialogTelemetryKey = `new:${crypto.randomUUID()}`;
20186
20244
  let roundBudgetWarningInjected = false;
20245
+ let subtaskToolBudgetWarningInjected = false;
20187
20246
  const roundWarningThreshold = input.roundBudgetWarningThreshold ?? DEFAULT_ROUND_BUDGET_WARNING_THRESHOLD;
20188
20247
  while (true) {
20189
20248
  partialContent = "";
@@ -20195,6 +20254,10 @@ async function runLocalAgentTurn(input) {
20195
20254
  transcript.push({ role: "user", content: prompt });
20196
20255
  roundBudgetWarningInjected = true;
20197
20256
  }
20257
+ if (subtaskBudgetTracker && !subtaskToolBudgetWarningInjected && subtaskBudgetTracker.toolCallsExecuted > 0 && subtaskBudgetTracker.remainingToolCalls() > 0 && subtaskBudgetTracker.remainingToolCalls() <= Math.max(1, Math.ceil(subtaskBudgetTracker.limits.maxToolCalls * 0.1))) {
20258
+ transcript.push({ role: "user", content: `\u5B50\u4EFB\u52A1\u5DE5\u5177\u9884\u7B97\u5373\u5C06\u7528\u5C3D\uFF08\u5269\u4F59 ${subtaskBudgetTracker.remainingToolCalls()} \u6B21\uFF09\u3002\u8BF7\u4F18\u5148\u6536\u5C3E\u5E76\u7ED9\u51FA\u53EF\u8BFB\u7684\u9636\u6BB5\u7ED3\u679C\u3002` });
20259
+ subtaskToolBudgetWarningInjected = true;
20260
+ }
20198
20261
  const preparedMessages = prepareMessagesForProviderCall(transcript.working());
20199
20262
  if (!supportsImages && hasImageInRuntimeMessages(preparedMessages.messages) && !imageDowngradeEmitted) {
20200
20263
  emitLoopEvent(observationBoundary, {
@@ -20559,12 +20622,98 @@ ${reasoningTailLog}
20559
20622
  } catch (error) {
20560
20623
  loopError = error;
20561
20624
  }
20625
+ const finalBudgetExhaustion = budgetExhaustion;
20626
+ let budgetWrapupFailure;
20627
+ let budgetWrapupSucceeded = false;
20628
+ if (!loopError && finalBudgetExhaustion && subtaskBudgetTracker && finalBudgetExhaustion.limit === "max-tool-calls" && !input.abortSignal?.aborted && subtaskBudgetTracker.elapsedMs() < subtaskBudgetTracker.limits.maxWallClockMs) {
20629
+ const remainingMs = subtaskBudgetTracker.limits.maxWallClockMs - subtaskBudgetTracker.elapsedMs();
20630
+ const timeoutMs = Math.min(3e4, remainingMs);
20631
+ const controller = new AbortController();
20632
+ let rejectCancelled = () => {
20633
+ };
20634
+ const cancelled = new Promise((_, reject) => {
20635
+ rejectCancelled = reject;
20636
+ });
20637
+ const onAbort = () => {
20638
+ controller.abort();
20639
+ rejectCancelled(buildAbortedError());
20640
+ };
20641
+ input.abortSignal?.addEventListener("abort", onAbort, { once: true });
20642
+ if (input.abortSignal?.aborted) onAbort();
20643
+ let timer;
20644
+ let wrapRequestStarted = false;
20645
+ let wrapUsageCaptured = false;
20646
+ let wrapOutcomeReceived = false;
20647
+ try {
20648
+ const deadline = new Promise((_, reject) => {
20649
+ timer = setTimeout(() => {
20650
+ controller.abort();
20651
+ reject(new Error("budget wrap-up timed out"));
20652
+ }, timeoutMs);
20653
+ });
20654
+ const work = async () => {
20655
+ const wrapProvider = await input.adapter.resolveProvider(agentConfig, { toolNames: [], prompt: "", textOnly: true });
20656
+ if (!wrapProvider.textOnly) throw new Error(`host ${input.adapter.host} does not support isolated text-only provider`);
20657
+ if (controller.signal.aborted || input.abortSignal?.aborted || subtaskBudgetTracker.elapsedMs() >= subtaskBudgetTracker.limits.maxWallClockMs) throw new Error("budget wrap-up cancelled or deadline reached");
20658
+ const wrapMessages = [
20659
+ ...filterImagePartsFromMessages(prepareMessagesForProviderCall(transcript.working()).messages, supportsImages),
20660
+ { role: "user", content: "\u5DE5\u5177\u9884\u7B97\u5DF2\u8017\u5C3D\u3002\u4E0D\u8981\u8C03\u7528\u5DE5\u5177\uFF1B\u4EC5\u57FA\u4E8E\u5DF2\u5B8C\u6210\u5185\u5BB9\uFF0C\u7B80\u8FF0\u7ED3\u679C\u3001\u8BC1\u636E\u4E0E\u672A\u5B8C\u6210\u4E8B\u9879\u3002" }
20661
+ ];
20662
+ const startedAt = Date.now();
20663
+ const telemetry2 = buildRequestTelemetry({ dialogKey: input.continueDialogId || `budget:${crypto.randomUUID()}`, messages: wrapMessages, toolsHash: safeToolNamesHash([]) });
20664
+ wrapRequestStarted = true;
20665
+ const summary2 = await runCompleteWithTimeout({
20666
+ provider: wrapProvider,
20667
+ messages: wrapMessages,
20668
+ options: { max_tokens: 1024, noRetries: true, timeoutMs, signal: controller.signal, ...input.continueDialogId ? { dialogId: input.continueDialogId } : {} },
20669
+ timeoutMs,
20670
+ round,
20671
+ input,
20672
+ boundary: observationBoundary,
20673
+ providerName: agentConfig.provider,
20674
+ model: wrapProvider.model
20675
+ });
20676
+ return { summary: summary2, telemetry: finalizeCallTelemetry(telemetry2, summary2, round, startedAt) };
20677
+ };
20678
+ const { summary, telemetry } = await Promise.race([work(), deadline, cancelled]);
20679
+ wrapOutcomeReceived = true;
20680
+ usageLedger.recordProviderCall({ usage: summary.usage, model: summary.model, provider: summary.provider, telemetry });
20681
+ wrapUsageCaptured = Boolean(summary.usage);
20682
+ if (summary.tool_calls?.length || summary.output?.some((block) => block.type === "toolCall")) throw new Error("text-only provider returned a tool call");
20683
+ if (summary.error || typeof summary.content !== "string" || !summary.content.trim()) throw new Error("text-only provider returned no usable summary");
20684
+ if (input.abortSignal?.aborted) throw buildAbortedError();
20685
+ result4 = { ...result4, content: summary.content.trim().slice(0, 12e3) };
20686
+ budgetWrapupSucceeded = true;
20687
+ } catch (error) {
20688
+ if (input.abortSignal?.aborted) {
20689
+ const aborted = buildAbortedError();
20690
+ if (wrapRequestStarted && !wrapUsageCaptured) {
20691
+ aborted.message += "\uFF08\u65E0\u5DE5\u5177\u603B\u7ED3\u8BF7\u6C42\u53EF\u80FD\u5DF2\u8BA1\u8D39\uFF0C\u672A\u8FD4\u56DE\u7684\u7528\u91CF\u672A\u77E5\uFF09";
20692
+ aborted.usageUnavailable = true;
20693
+ }
20694
+ loopError = aborted;
20695
+ } else {
20696
+ const detail = error instanceof Error ? error.message : "unknown error";
20697
+ budgetWrapupFailure = scrubSecrets(detail).cleaned.slice(0, 300);
20698
+ if (wrapRequestStarted && !wrapOutcomeReceived) budgetWrapupFailure += "\uFF1B\u8BF7\u6C42\u53EF\u80FD\u5DF2\u8BA1\u8D39\uFF0C\u672A\u8FD4\u56DE\u7684\u7528\u91CF\u672A\u77E5";
20699
+ }
20700
+ } finally {
20701
+ if (timer) clearTimeout(timer);
20702
+ controller.abort();
20703
+ input.abortSignal?.removeEventListener("abort", onAbort);
20704
+ }
20705
+ }
20706
+ if (!loopError && input.abortSignal?.aborted) loopError = buildAbortedError();
20562
20707
  if (budgetExhaustion) {
20708
+ if (!budgetWrapupSucceeded && !budgetWrapupFailure) {
20709
+ budgetWrapupFailure = input.abortSignal?.aborted ? "\u7528\u6237\u5DF2\u53D6\u6D88" : "\u5899\u949F\u9884\u7B97\u5DF2\u8017\u5C3D\uFF0C\u672A\u53D1\u8D77\u603B\u7ED3\u8BF7\u6C42";
20710
+ }
20563
20711
  const previousContent = typeof result4?.content === "string" ? result4.content.trim() : "";
20564
20712
  result4 = {
20565
20713
  ...result4,
20566
20714
  content: [
20567
20715
  previousContent,
20716
+ ...budgetWrapupFailure ? [`\u65E0\u5DE5\u5177\u603B\u7ED3\u672A\u5B8C\u6210\uFF08${budgetWrapupFailure}\uFF09\uFF1B\u4EE5\u4E0B\u662F\u4FDD\u7559\u7684\u5DF2\u6709\u5185\u5BB9\u548C\u9884\u7B97\u72B6\u6001\u3002`] : [],
20568
20717
  buildSubtaskBudgetExhaustedContent({
20569
20718
  exhaustion: budgetExhaustion,
20570
20719
  roundsCompleted: round,
@@ -21210,7 +21359,7 @@ async function executeOpenAiCompatibleChatCompletion(args2) {
21210
21359
  terminalIfEnabled(isAbortError(error) ? "aborted" : "network-error");
21211
21360
  throw error;
21212
21361
  }
21213
- if (res.status === 401 && args2.resolveApiKey) {
21362
+ if (res.status === 401 && args2.resolveApiKey && !args2.noRetries) {
21214
21363
  const refreshed = await args2.resolveApiKey({ force: true });
21215
21364
  if (refreshed && refreshed !== apiKey) {
21216
21365
  diagnosticCall?.httpResult(diagnosticAttempt, {
@@ -22435,9 +22584,23 @@ async function fetchHybridRemoteRecord(args2) {
22435
22584
  ...args2.signal ? { signal: args2.signal } : {}
22436
22585
  }
22437
22586
  );
22438
- if (!response.ok) return null;
22439
- const payload = await response.json().catch(() => null);
22440
- return normalizeHybridRecord(args2.dbKey, payload?.data ?? payload, args2.server);
22587
+ if (!response.ok) {
22588
+ if (response.status !== 404) args2.onUnavailable?.();
22589
+ return null;
22590
+ }
22591
+ let payload;
22592
+ try {
22593
+ payload = await response.json();
22594
+ } catch {
22595
+ args2.onUnavailable?.();
22596
+ return null;
22597
+ }
22598
+ const record2 = payload && typeof payload === "object" && "data" in payload ? payload.data : payload;
22599
+ if (record2 !== null && (!record2 || typeof record2 !== "object" || Array.isArray(record2))) {
22600
+ args2.onUnavailable?.();
22601
+ return null;
22602
+ }
22603
+ return normalizeHybridRecord(args2.dbKey, record2, args2.server);
22441
22604
  }
22442
22605
  function shouldCacheHybridRemoteRecord(remoteRecord, localRecord) {
22443
22606
  if (!remoteRecord || typeof remoteRecord !== "object") return false;
@@ -22466,7 +22629,10 @@ function createHybridRecordStore(deps) {
22466
22629
  deps.fallbackServers
22467
22630
  )) {
22468
22631
  const remainingMs = fallbackDeadlineMs === null ? null : fallbackDeadlineMs - Date.now();
22469
- if (remainingMs !== null && remainingMs <= 0) break;
22632
+ if (remainingMs !== null && remainingMs <= 0) {
22633
+ options?.onRemoteUnavailable?.();
22634
+ break;
22635
+ }
22470
22636
  let remoteRecord = null;
22471
22637
  try {
22472
22638
  remoteRecord = await fetchHybridRemoteRecord({
@@ -22474,9 +22640,11 @@ function createHybridRecordStore(deps) {
22474
22640
  server,
22475
22641
  authToken: deps.authToken,
22476
22642
  fetchImpl,
22643
+ onUnavailable: options?.onRemoteUnavailable,
22477
22644
  ...remainingMs !== null ? { signal: AbortSignal.timeout(remainingMs) } : {}
22478
22645
  });
22479
22646
  } catch {
22647
+ options?.onRemoteUnavailable?.();
22480
22648
  continue;
22481
22649
  }
22482
22650
  if (!remoteRecord) continue;
@@ -22648,7 +22816,7 @@ function buildLoadSkillExecutor(args2) {
22648
22816
  const { resolveSkillByName } = await import("./skillDiscovery-XKHROSH2.js");
22649
22817
  const resolved = resolveSkillByName(args2.cwd, name);
22650
22818
  if (!resolved) {
22651
- const { resolveBuiltinSkillByName } = await import("./builtinSkillRegistry-3YOIGEF6.js");
22819
+ const { resolveBuiltinSkillByName } = await import("./builtinSkillRegistry-2QETTQPM.js");
22652
22820
  const builtin = resolveBuiltinSkillByName(name);
22653
22821
  if (builtin) {
22654
22822
  return {
@@ -22707,7 +22875,7 @@ async function loadPreciseSkill(cwd, parsed) {
22707
22875
  if (!nonEmpty(parsed.slug) || has2("projectBinding") || has2("relativePath")) {
22708
22876
  return reject("builtin requires only an exact slug.");
22709
22877
  }
22710
- const { listBuiltinSkills } = await import("./builtinSkillRegistry-3YOIGEF6.js");
22878
+ const { listBuiltinSkills } = await import("./builtinSkillRegistry-2QETTQPM.js");
22711
22879
  const builtin = listBuiltinSkills().find((entry) => entry.slug === parsed.slug);
22712
22880
  if (!builtin) return reject("builtin slug not found.");
22713
22881
  return {
@@ -23423,6 +23591,7 @@ export {
23423
23591
  evaluateShellCommandPolicy,
23424
23592
  filterRetainedLedgerRecords,
23425
23593
  isLineRangeCovered,
23594
+ runTaskStop,
23426
23595
  WORKSPACE_TOOL_NAMES,
23427
23596
  buildLocalWorkspaceToolset,
23428
23597
  buildLocalWorkspacePolicyToolNames,