nolo-cli 0.83.0 → 0.84.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 (106) hide show
  1. package/{_executeModel-LUNEV235.js → _executeModel-BPUR2QYH.js} +29 -28
  2. package/{action-YVYQEAAV.js → action-P4TNFGAD.js} +3 -3
  3. package/{addContentAction-O7R66HEX.js → addContentAction-MB2HHMDZ.js} +2 -2
  4. package/{addDialogAgentAction-74OA7KYP.js → addDialogAgentAction-S2NRBCWW.js} +2 -2
  5. package/{agentDeleteCommand-2YAHAZFJ.js → agentDeleteCommand-MNAA6MIG.js} +15 -14
  6. package/{agentEmailCommands-G6A7IRPQ.js → agentEmailCommands-SK34WJKU.js} +37 -36
  7. package/{agentListCommands-KMS675GZ.js → agentListCommands-I26MKI4T.js} +31 -19
  8. package/{agentMachineCommands-W2SMNSQ3.js → agentMachineCommands-M6QDR6P3.js} +15 -14
  9. package/{agentProviderCommands-GNPTL5XR.js → agentProviderCommands-X3UNW77C.js} +1 -1
  10. package/{agentRecordCommands-D7BRMFNI.js → agentRecordCommands-HTHLO2MM.js} +36 -35
  11. package/{agentRunCommand-VL6MMEUB.js → agentRunCommand-IAIRCMTH.js} +38 -36
  12. package/{agentRuntimeLocal-VXZSDIGT.js → agentRuntimeLocal-3WTYYIT2.js} +16 -11
  13. package/{agentSlice-5YJHUO47.js → agentSlice-TVP3HINW.js} +2 -2
  14. package/{appDeleteCommands-32MHLR4L.js → appDeleteCommands-SB5TNLKY.js} +16 -15
  15. package/{appDeployCommands-WCQOUSOB.js → appDeployCommands-2IPKZINV.js} +16 -15
  16. package/{appDeployStatusCommands-6QKB7DYN.js → appDeployStatusCommands-K63TFUID.js} +16 -15
  17. package/{appGetCommands-SFAPP5PB.js → appGetCommands-VU2SLNEH.js} +16 -15
  18. package/{appListCommands-EKUBDRUO.js → appListCommands-S3T4DJ36.js} +16 -15
  19. package/{authCommand-F4EFRRX6.js → authCommand-HGP2XTKC.js} +100 -8
  20. package/{builtinSkillRegistry-FUAVTRVZ.js → builtinSkillRegistry-Q2VVY3SC.js} +2 -2
  21. package/{chatQueueReduxAdapter-PQMRR2PK.js → chatQueueReduxAdapter-TWIYKVIZ.js} +9 -9
  22. package/{chunk-Z77CRGTW.js → chunk-3AMA42ZI.js} +1 -1
  23. package/{chunk-5FBTLSFG.js → chunk-3BGIKIPF.js} +317 -153
  24. package/{chunk-ORYWEJO4.js → chunk-3IUWIAHP.js} +3 -3
  25. package/{chunk-Q2XN3EOZ.js → chunk-3VUWPRFW.js} +17 -7
  26. package/{chunk-GEPUP7OO.js → chunk-3YIAR5UI.js} +1 -1
  27. package/{chunk-5H7KJX7J.js → chunk-4MK3I7Y4.js} +1 -1
  28. package/{chunk-6VFARHZW.js → chunk-52Q2IW2X.js} +783 -999
  29. package/{chunk-6Q33GUCC.js → chunk-5MEYHVIB.js} +1 -1
  30. package/{chunk-6YTOJY35.js → chunk-6D2JFXLY.js} +35 -0
  31. package/{chunk-FZGV4FGK.js → chunk-6GFOD3TK.js} +260 -140
  32. package/{chunk-HJUYAD4A.js → chunk-6Z7MY3V7.js} +1 -1
  33. package/{chunk-V4MJDUSR.js → chunk-AHAHBQMU.js} +1 -1
  34. package/{chunk-TOP2VXOR.js → chunk-BFDFRLLQ.js} +2 -2
  35. package/chunk-BMQ2MJUK.js +153 -0
  36. package/{chunk-RG6JBBX5.js → chunk-CWQINZEA.js} +119 -3
  37. package/{chunk-BTI7PJDX.js → chunk-DD6XE56O.js} +12 -65
  38. package/{chunk-ETI2NYVF.js → chunk-FKNWP6SG.js} +1 -1
  39. package/{chunk-SSDAAEC2.js → chunk-FP76KI2Y.js} +280 -40
  40. package/chunk-FV3JEYR3.js +870 -0
  41. package/{chunk-DHDMC6FO.js → chunk-GIVFXPWK.js} +2 -0
  42. package/chunk-GMZLP5DV.js +82 -0
  43. package/{chunk-UZGQNTVW.js → chunk-GNCCNNZD.js} +3 -1
  44. package/{chunk-AFUORTY4.js → chunk-GP5LKBUZ.js} +1 -1
  45. package/{chunk-ZCYXJSTU.js → chunk-H4AHY5BU.js} +4 -81
  46. package/{chunk-OEVAY4J6.js → chunk-HAFJ3NDE.js} +2 -2
  47. package/{chunk-IIVGDG4I.js → chunk-HCT7U36O.js} +3 -0
  48. package/{chunk-4OEFYTWL.js → chunk-HKNXJGEC.js} +1 -1
  49. package/{chunk-VLTIYV3X.js → chunk-HYJFRTLW.js} +1 -1
  50. package/{chunk-NA35QHCF.js → chunk-K36IXSCQ.js} +1 -1
  51. package/{chunk-DRGSDQBL.js → chunk-KAUSRTO6.js} +2 -2
  52. package/{chunk-GPCZ7UXG.js → chunk-LJ5VW2UJ.js} +1 -1
  53. package/{chunk-YFE4A4O7.js → chunk-LMREEBWT.js} +120 -37
  54. package/{chunk-JRQFUXY7.js → chunk-LVIFM7VW.js} +1 -1
  55. package/{chunk-RFCKMYDK.js → chunk-MZYBSTMH.js} +4 -4
  56. package/chunk-OCZKATRC.js +129 -0
  57. package/{chunk-DNCEALKX.js → chunk-P5EVSOPA.js} +2 -2
  58. package/{chunk-BP4XNZRM.js → chunk-PBA4UJBK.js} +1 -1
  59. package/{chunk-G2O6V3HJ.js → chunk-PMAIQX7A.js} +3 -3
  60. package/{chunk-AEOE4Z6Q.js → chunk-QSHEEA5N.js} +2 -2
  61. package/{chunk-KQ4ONNQD.js → chunk-QU7KDSZX.js} +1 -1
  62. package/{chunk-45C5ER5U.js → chunk-RPJUFEGR.js} +4 -4
  63. package/{chunk-X6DN5HBD.js → chunk-U4OKDZFH.js} +3 -3
  64. package/{chunk-4S5QUMJP.js → chunk-UDVH2P4A.js} +4 -4
  65. package/{chunk-UKZDAFYT.js → chunk-UEVXFHQB.js} +2 -2
  66. package/{chunk-H62HCIUP.js → chunk-VRTTFDBM.js} +2 -1
  67. package/{chunk-6IG4XSS6.js → chunk-WP5UXDA5.js} +71 -4
  68. package/{chunk-3DII57IT.js → chunk-XD2MJFUG.js} +1 -1
  69. package/{chunk-QVKRR2SS.js → chunk-XP3VVAPG.js} +878 -175
  70. package/{chunk-L5UXH3B2.js → chunk-Z2PV6O2T.js} +18 -2
  71. package/{chunk-53HEEOTT.js → chunk-Z5625XML.js} +1 -1
  72. package/{contentThunks-GJV22WVZ.js → contentThunks-JYWI7G7I.js} +10 -10
  73. package/{createAgentAutomationAction-6V25TZEO.js → createAgentAutomationAction-Z4SUJ54Y.js} +10 -10
  74. package/{createDialogAction-SX36BGSX.js → createDialogAction-RCHFLVJ2.js} +12 -12
  75. package/{createPageAction-IR4BGJSI.js → createPageAction-YK2253XQ.js} +11 -11
  76. package/{dbSlice-XVWBAGYN.js → dbSlice-A6C6H32X.js} +1 -1
  77. package/{dialogCommands-5J56DFWN.js → dialogCommands-X4UOZQWX.js} +16 -15
  78. package/{dialogSlice-UNX2FAPZ.js → dialogSlice-IVTWLCE2.js} +15 -9
  79. package/{docCreateCommands-KYI2DC2U.js → docCreateCommands-VD6TGHG4.js} +15 -14
  80. package/{docDeleteCommands-67REUSRY.js → docDeleteCommands-OT3R2G4W.js} +16 -15
  81. package/{docReadCommands-HOSVSBDJ.js → docReadCommands-2G7BIXSH.js} +15 -14
  82. package/{docStore-JWVG5NDO.js → docStore-UENKCCDX.js} +2 -2
  83. package/{docUpdateCommands-EMGCSMXY.js → docUpdateCommands-45MVJKZA.js} +15 -14
  84. package/{handleSendMessageAction-B75UOJKF.js → handleSendMessageAction-LM2J4HC3.js} +50 -58
  85. package/index.js +51 -51
  86. package/{machineCommands-GFPTOY6L.js → machineCommands-6N6MQIE6.js} +38 -37
  87. package/{machineWsRunDispatch-46PSZ3DJ.js → machineWsRunDispatch-7MFKVQCR.js} +35 -34
  88. package/{memberThunks-QGFLFVCI.js → memberThunks-P2ECUJ56.js} +2 -2
  89. package/package.json +1 -1
  90. package/pdf-U6KSUEZZ.js +26435 -0
  91. package/{providerPresetApply-FG6NZ57W.js → providerPresetApply-XI3WUPTI.js} +2 -2
  92. package/{providerRegistry-757OWH3O.js → providerRegistry-KEPWWGXD.js} +2 -2
  93. package/{readlineWorkspace-UFSWHFKD.js → readlineWorkspace-JWBON5CX.js} +184 -63
  94. package/{removeDialogAgentAction-IX3MN2ZO.js → removeDialogAgentAction-W66BJ7CF.js} +4 -4
  95. package/{runAgentClientLoop-25LJ4XGV.js → runAgentClientLoop-TA4SSPHE.js} +26 -26
  96. package/{setDialogExtraReferencesAction-X4HCPNZL.js → setDialogExtraReferencesAction-2ALKG3WA.js} +3 -3
  97. package/{setPrimaryDialogAgentAction-4GMIQLOD.js → setPrimaryDialogAgentAction-TQZB55F6.js} +4 -4
  98. package/{settingSlice-ZQZVUPKK.js → settingSlice-P4ZW7K5X.js} +2 -2
  99. package/{spaceCommands-NKJVJCQQ.js → spaceCommands-6NSAXB7D.js} +15 -14
  100. package/{streamAgentChatTurn-A46QFV4O.js → streamAgentChatTurn-X5ENGUIS.js} +94 -34
  101. package/{switchDialogAgentAction-N52LRPA3.js → switchDialogAgentAction-3KI72HHS.js} +2 -2
  102. package/{toolApiClient-QVJ6HDNN.js → toolApiClient-G3M73RTB.js} +2 -2
  103. package/{tools-TTDGRNY6.js → tools-XRQR7ZZR.js} +23 -23
  104. package/{tuiSummaryLlmCaller-WFRWERGK.js → tuiSummaryLlmCaller-UFGFOIIW.js} +5 -4
  105. package/{updateDialogTitleAction-QJPZGF52.js → updateDialogTitleAction-ESUUD3GT.js} +10 -10
  106. package/chunk-Q5FJJ6XI.js +0 -103
@@ -5,13 +5,16 @@ import {
5
5
  finalizeResponsesToolCalls,
6
6
  flushChatCompletionStream,
7
7
  providerHttpFailure,
8
- readSseFrames,
9
- streamSseDataValues,
10
8
  throwIfChatCompletionStreamFailed
11
- } from "./chunk-ZCYXJSTU.js";
9
+ } from "./chunk-H4AHY5BU.js";
12
10
  import {
13
- DEVIN_CONNECT_URL
14
- } from "./chunk-Q5FJJ6XI.js";
11
+ DEVIN_CONNECT_URL,
12
+ parseSseDataLineJson,
13
+ parseSseDataLineObject
14
+ } from "./chunk-FV3JEYR3.js";
15
+ import {
16
+ readSseFrames
17
+ } from "./chunk-GMZLP5DV.js";
15
18
  import {
16
19
  clipToolText,
17
20
  hasImageInRuntimeMessages,
@@ -20,7 +23,7 @@ import {
20
23
  resolveHistoricalToolContentCap,
21
24
  resolveToolOutputProfile,
22
25
  stripImagePartsFromMessages
23
- } from "./chunk-5H7KJX7J.js";
26
+ } from "./chunk-4MK3I7Y4.js";
24
27
  import {
25
28
  downgradeUnparsableToolCalls,
26
29
  hasParsableObjectArguments,
@@ -35,6 +38,7 @@ import {
35
38
  finalizeAccumulatedToolCalls
36
39
  } from "./chunk-N7J63Q6C.js";
37
40
  import {
41
+ DELETE_SAFETY_RED_LINE,
38
42
  TOOL_GUIDED_SECTION_ORDER,
39
43
  buildIdentityBlock,
40
44
  buildRuntimeGuidanceBlocks,
@@ -46,7 +50,7 @@ import {
46
50
  requiresBareImageUrl,
47
51
  resolveToolGuidedSections,
48
52
  toBareImageUrlMessages
49
- } from "./chunk-L5UXH3B2.js";
53
+ } from "./chunk-Z2PV6O2T.js";
50
54
  import {
51
55
  convertMessagesToResponsesInput,
52
56
  extractReasoningFromResponseOutput,
@@ -64,18 +68,14 @@ import {
64
68
  applyToolSurfaceConstraints,
65
69
  buildDialogReadEnvelope,
66
70
  buildNoloWorkspaceCommandArgs,
71
+ buildNoloWorkspaceOpenAiTools,
67
72
  createStreamingDialogReadProjector,
68
73
  noloPositiveIntegerString,
69
74
  noloStringArg,
70
75
  parseNoloWorkspaceToolArguments,
71
76
  readTextStreamUpToChars,
72
77
  resolveNoloDialogInput
73
- } from "./chunk-IIVGDG4I.js";
74
- import {
75
- getAntigravityUserAgent,
76
- readAntigravityProjectId,
77
- resolveAntigravityCloudCodeBaseUrl
78
- } from "./chunk-UZGQNTVW.js";
78
+ } from "./chunk-HCT7U36O.js";
79
79
  import {
80
80
  buildProviderAuthHeaders,
81
81
  buildProviderExecutionPlan
@@ -93,7 +93,7 @@ import {
93
93
  resolveEmptyAssistantFallbackMessage,
94
94
  resolveEmptyAssistantOutcome,
95
95
  resolveTruncatedReasoningTailLog
96
- } from "./chunk-4OEFYTWL.js";
96
+ } from "./chunk-HKNXJGEC.js";
97
97
  import {
98
98
  COMPACTION_SUMMARY_SCHEMA_VERSION,
99
99
  COMPACTION_SUMMARY_SYSTEM_PROMPT,
@@ -112,7 +112,7 @@ import {
112
112
  resolveCompressionTriggerRatio,
113
113
  truncateToolOutputForContext,
114
114
  withProviderCallTimingFields
115
- } from "./chunk-DNCEALKX.js";
115
+ } from "./chunk-P5EVSOPA.js";
116
116
  import {
117
117
  estimateTokenCount
118
118
  } from "./chunk-PSCNXBYA.js";
@@ -11900,6 +11900,7 @@ var toQueue = /* @__PURE__ */ dual(2, (self, options) => toQueueArray(self.chann
11900
11900
  var runIntoQueue = /* @__PURE__ */ dual(2, (self, queue) => runIntoQueueArray(self.channel, queue));
11901
11901
 
11902
11902
  // packages/agent-runtime/abortableKernel.ts
11903
+ var IDLE_WATCHDOG_SLICES = 20;
11903
11904
  function runAbortableWithTimeout(args2) {
11904
11905
  const taskEffect = Effect_exports.callback(
11905
11906
  (resume) => {
@@ -11935,12 +11936,26 @@ function runAbortableWithTimeout(args2) {
11935
11936
  raced = Effect_exports.raceFirst(taskEffect, abortEffect);
11936
11937
  }
11937
11938
  if (typeof args2.timeoutMs === "number") {
11938
- const inner = raced;
11939
+ const outer = raced;
11939
11940
  const timeoutMs = args2.timeoutMs;
11940
- raced = Effect_exports.gen(function* () {
11941
- const outcome = yield* Effect_exports.timeoutOption(inner, Duration_exports.millis(timeoutMs));
11942
- return Option_exports.isNone(outcome) ? { kind: "timeout" } : outcome.value;
11941
+ const activityVersion = args2.activityVersion;
11942
+ const idleWatchdog = Effect_exports.gen(function* () {
11943
+ const sliceCount = activityVersion ? IDLE_WATCHDOG_SLICES : 1;
11944
+ const baseSliceMs = Math.max(1, Math.floor(timeoutMs / sliceCount));
11945
+ let idleMs = 0;
11946
+ for (; ; ) {
11947
+ const versionAtSliceStart = activityVersion?.();
11948
+ const sliceMs = Math.max(1, Math.min(timeoutMs - idleMs, baseSliceMs));
11949
+ yield* Effect_exports.sleep(Duration_exports.millis(sliceMs));
11950
+ idleMs += sliceMs;
11951
+ if (activityVersion && activityVersion() !== versionAtSliceStart) {
11952
+ idleMs = 0;
11953
+ continue;
11954
+ }
11955
+ if (idleMs >= timeoutMs) return { kind: "timeout" };
11956
+ }
11943
11957
  });
11958
+ raced = Effect_exports.raceFirst(outer, idleWatchdog);
11944
11959
  }
11945
11960
  return args2.runtime ? args2.runtime.runPromise(raced) : Effect_exports.runPromise(raced);
11946
11961
  }
@@ -12627,6 +12642,13 @@ function isColdResume(history, nowMs) {
12627
12642
  }
12628
12643
  async function maybeAutoCompactLocalHistory(args2) {
12629
12644
  const { adapter, dialogId, history } = args2;
12645
+ const emitPhase = (phase) => {
12646
+ try {
12647
+ args2.onPhase?.(phase);
12648
+ } catch {
12649
+ }
12650
+ };
12651
+ emitPhase({ kind: "validating-context" });
12630
12652
  const unchanged = (decision) => ({
12631
12653
  history,
12632
12654
  compressed: false,
@@ -12722,8 +12744,15 @@ async function maybeAutoCompactLocalHistory(args2) {
12722
12744
  if (!plan.shouldCompress) {
12723
12745
  return projectExisting();
12724
12746
  }
12747
+ emitPhase({ kind: "compaction-start" });
12725
12748
  try {
12749
+ if (args2.abortSignal?.aborted) {
12750
+ throw Object.assign(new Error("auto-compaction aborted"), { compactionReason: "aborted" });
12751
+ }
12726
12752
  const provider = await args2.resolveProvider();
12753
+ if (args2.abortSignal?.aborted) {
12754
+ throw Object.assign(new Error("auto-compaction aborted"), { compactionReason: "aborted" });
12755
+ }
12727
12756
  const msgsToCompress = plan.msgsToCompress;
12728
12757
  const messagesText = formatMessagesForSummaryWithTruncation(msgsToCompress);
12729
12758
  const fileOpsText = formatFileOperationsFromMessages(
@@ -12735,20 +12764,55 @@ async function maybeAutoCompactLocalHistory(args2) {
12735
12764
  messagesText,
12736
12765
  fileOpsText
12737
12766
  });
12738
- const result4 = await provider.complete([
12739
- { role: "system", content: COMPACTION_SUMMARY_SYSTEM_PROMPT },
12740
- { role: "user", content: promptContent }
12741
- ]);
12767
+ emitPhase({ kind: "waiting-provider" });
12768
+ const timeoutMs = typeof args2.timeoutMs === "number" && Number.isFinite(args2.timeoutMs) && args2.timeoutMs > 0 ? args2.timeoutMs : void 0;
12769
+ const requestController = new AbortController();
12770
+ let timeoutHandle;
12771
+ let abortListener;
12772
+ const request3 = provider.complete(
12773
+ [{ role: "system", content: COMPACTION_SUMMARY_SYSTEM_PROMPT }, { role: "user", content: promptContent }],
12774
+ { signal: requestController.signal, ...timeoutMs ? { timeoutMs } : {} }
12775
+ ).then(
12776
+ (value) => ({ kind: "done", value }),
12777
+ (error) => ({ kind: "provider-error", error })
12778
+ );
12779
+ const guards = [];
12780
+ if (timeoutMs) guards.push(new Promise((resolve5) => {
12781
+ timeoutHandle = setTimeout(() => {
12782
+ requestController.abort();
12783
+ resolve5({ kind: "timeout" });
12784
+ }, timeoutMs);
12785
+ }));
12786
+ if (args2.abortSignal) guards.push(new Promise((resolve5) => {
12787
+ abortListener = () => {
12788
+ requestController.abort(args2.abortSignal?.reason);
12789
+ resolve5({ kind: "aborted" });
12790
+ };
12791
+ args2.abortSignal?.addEventListener("abort", abortListener, { once: true });
12792
+ }));
12793
+ const outcome = guards.length ? await Promise.race([request3, ...guards]) : await request3;
12794
+ if (timeoutHandle) clearTimeout(timeoutHandle);
12795
+ if (abortListener) args2.abortSignal?.removeEventListener("abort", abortListener);
12796
+ if (outcome.kind !== "done") {
12797
+ const reason = outcome.kind === "timeout" ? "timeout" : outcome.kind === "aborted" ? "aborted" : "provider-error";
12798
+ const detail = outcome.kind === "provider-error" ? outcome.error instanceof Error ? outcome.error.message : String(outcome.error) : reason === "timeout" ? `auto-compaction timed out after ${timeoutMs}ms` : "auto-compaction aborted";
12799
+ throw Object.assign(new Error(detail), { compactionReason: reason });
12800
+ }
12801
+ const result4 = outcome.value;
12742
12802
  const newSummary = typeof result4.content === "string" ? result4.content.trim() : "";
12743
12803
  if (!newSummary) {
12744
12804
  console.warn(
12745
12805
  "[localLoop] auto-compaction produced empty summary; keeping prior projection"
12746
12806
  );
12807
+ const failureReason = "provider-error";
12808
+ emitPhase({ kind: "compaction-failed", reason: failureReason });
12747
12809
  return {
12748
12810
  ...projectExisting(),
12749
12811
  failureMessage: "summary model returned empty content",
12812
+ failureReason,
12750
12813
  decision: {
12751
12814
  outcome: "failed",
12815
+ reason: failureReason,
12752
12816
  detail: "summary model returned empty content"
12753
12817
  }
12754
12818
  };
@@ -12775,6 +12839,7 @@ async function maybeAutoCompactLocalHistory(args2) {
12775
12839
  summaryUsage: result4.usage
12776
12840
  });
12777
12841
  console.log(formatCompactionMetricsLog(metrics));
12842
+ emitPhase({ kind: "compaction-end" });
12778
12843
  return {
12779
12844
  history: projectHistoryWithSummary({
12780
12845
  history,
@@ -12802,10 +12867,13 @@ async function maybeAutoCompactLocalHistory(args2) {
12802
12867
  } catch (error) {
12803
12868
  console.warn("[localLoop] auto-compaction failed:", error);
12804
12869
  const failureMessage = error instanceof Error && error.message ? error.message.slice(0, 200) : String(error).slice(0, 200);
12870
+ const failureReason = error instanceof Error && "compactionReason" in error ? error.compactionReason : "provider-error";
12871
+ emitPhase({ kind: "compaction-failed", reason: failureReason });
12805
12872
  return {
12806
12873
  ...projectExisting(),
12807
12874
  failureMessage,
12808
- decision: { outcome: "failed", detail: failureMessage }
12875
+ failureReason,
12876
+ decision: { outcome: "failed", reason: failureReason, detail: failureMessage }
12809
12877
  };
12810
12878
  }
12811
12879
  }
@@ -12862,6 +12930,8 @@ function emitLoopEvent(boundary, event, bridge) {
12862
12930
  boundary.emit(bridge ? { event, bridge } : { event });
12863
12931
  }
12864
12932
  var LLM_REQUEST_TIMEOUT = "LLM_REQUEST_TIMEOUT";
12933
+ var DEFAULT_LLM_REQUEST_TIMEOUT_MS = 10 * 60 * 1e3;
12934
+ var DEFAULT_COMPACTION_TIMEOUT_MS = 60 * 1e3;
12865
12935
  var LOCAL_TURN_ABORTED_CODE = "LOCAL_TURN_ABORTED";
12866
12936
  function buildAbortedError() {
12867
12937
  const error = new Error("local agent turn aborted by user");
@@ -12889,8 +12959,11 @@ async function runAbortableToolTask(input, task, pendingToolName) {
12889
12959
  }
12890
12960
  function resolveLlmRequestTimeoutMs(input) {
12891
12961
  const raw = input.llmRequestTimeoutMs ?? input.timeoutMs;
12892
- if (typeof raw !== "number" || !Number.isFinite(raw) || raw <= 0) return void 0;
12893
- return raw;
12962
+ return typeof raw === "number" && Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_LLM_REQUEST_TIMEOUT_MS;
12963
+ }
12964
+ function resolveCompactionTimeoutMs(input) {
12965
+ const raw = input.compactionTimeoutMs;
12966
+ return typeof raw === "number" && Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_COMPACTION_TIMEOUT_MS;
12894
12967
  }
12895
12968
  async function runCompleteWithTimeout(args2) {
12896
12969
  const { provider, messages, options, timeoutMs, round, input, boundary, context: context3, providerName, model } = args2;
@@ -12913,6 +12986,7 @@ async function runCompleteWithTimeout(args2) {
12913
12986
  const outcome = await runAbortableWithTimeout({
12914
12987
  task: complete,
12915
12988
  timeoutMs: typeof timeoutMs === "number" ? timeoutMs : void 0,
12989
+ activityVersion: args2.activityVersion,
12916
12990
  abortSignal: input.abortSignal,
12917
12991
  runtime: input.effectRuntime
12918
12992
  });
@@ -12923,7 +12997,7 @@ async function runCompleteWithTimeout(args2) {
12923
12997
  }
12924
12998
  if (outcome.kind === "timeout") {
12925
12999
  const timeoutError = new Error(
12926
- `LLM request timed out after ${timeoutMs}ms (round ${round})`
13000
+ `LLM request timed out after ${timeoutMs}ms without new output (idle window, round ${round})`
12927
13001
  );
12928
13002
  timeoutError.code = LLM_REQUEST_TIMEOUT;
12929
13003
  throw timeoutError;
@@ -13604,6 +13678,9 @@ async function runLocalAgentTurn(input) {
13604
13678
  input.contextBlockScopes
13605
13679
  );
13606
13680
  const mergedContextBlockScopes = [
13681
+ // 删除安全红线:系统层最高优先级约束,位于所有块之前(含身份信息块),
13682
+ // owner 要求常驻「根本提示词」的最顶部(2026-09-26 批量误删事故)。
13683
+ { content: DELETE_SAFETY_RED_LINE, cacheScope: "session" },
13607
13684
  { content: identityBlock, cacheScope: "session" },
13608
13685
  ...guidanceScopes,
13609
13686
  ...callerScopes
@@ -13636,6 +13713,16 @@ async function runLocalAgentTurn(input) {
13636
13713
  }
13637
13714
  }
13638
13715
  let compactionUsage;
13716
+ const emitCompactionPhase = (phase, scope3) => {
13717
+ const atMs = Date.now();
13718
+ if (phase.kind === "validating-context") emitLoopEvent(observationBoundary, { kind: "turn-phase", phase: "validating-context", atMs, compactionScope: scope3 });
13719
+ else if (phase.kind === "compaction-start") {
13720
+ emitLoopEvent(observationBoundary, { kind: "turn-phase", phase: "compacting", atMs, compactionScope: scope3 });
13721
+ emitLoopEvent(observationBoundary, { kind: "compaction-start", atMs, scope: scope3 });
13722
+ } else if (phase.kind === "waiting-provider") emitLoopEvent(observationBoundary, { kind: "turn-phase", phase: "waiting-provider", atMs, compactionScope: scope3 });
13723
+ else if (phase.kind === "compaction-end") emitLoopEvent(observationBoundary, { kind: "compaction-end", atMs, scope: scope3 });
13724
+ else emitLoopEvent(observationBoundary, { kind: "compaction-failed", atMs, scope: scope3, reason: phase.reason });
13725
+ };
13639
13726
  const emitCompactionObservation = (compacted) => {
13640
13727
  const decision = compacted.decision;
13641
13728
  if (decision?.outcome === "skipped") {
@@ -13669,7 +13756,10 @@ async function runLocalAgentTurn(input) {
13669
13756
  model: agentConfig.model,
13670
13757
  resolveProvider: resolveProviderOnce,
13671
13758
  contextWindow,
13672
- realContextUsagePercent
13759
+ realContextUsagePercent,
13760
+ abortSignal: input.abortSignal,
13761
+ timeoutMs: resolveCompactionTimeoutMs(input),
13762
+ onPhase: (phase) => emitCompactionPhase(phase, "initial")
13673
13763
  });
13674
13764
  history = compacted.history;
13675
13765
  compactionUsage = compacted.usage;
@@ -13775,7 +13865,10 @@ async function runLocalAgentTurn(input) {
13775
13865
  model: agentConfig.model,
13776
13866
  resolveProvider: resolveProviderOnce,
13777
13867
  contextWindow,
13778
- ...ratio !== void 0 ? { realContextUsagePercent: ratio } : {}
13868
+ ...ratio !== void 0 ? { realContextUsagePercent: ratio } : {},
13869
+ abortSignal: input.abortSignal,
13870
+ timeoutMs: resolveCompactionTimeoutMs(input),
13871
+ onPhase: (phase) => emitCompactionPhase(phase, "in-loop")
13779
13872
  });
13780
13873
  if (compacted.usage) {
13781
13874
  compactionUsage = addOutOfBandUsage(compactionUsage, compacted.usage);
@@ -13845,11 +13938,12 @@ async function runLocalAgentTurn(input) {
13845
13938
  const shouldPassToolEvents = Boolean(
13846
13939
  input.onToolEvent || input.onObservationEvent || input.observationBoundary
13847
13940
  );
13941
+ let llmStreamActivity = 0;
13848
13942
  result4 = await runCompleteWithTimeout({
13849
13943
  provider,
13850
13944
  messages: requestMessages,
13851
13945
  options: {
13852
- ...typeof input.timeoutMs === "number" ? { timeoutMs: input.timeoutMs } : {},
13946
+ timeoutMs: resolveLlmRequestTimeoutMs(input),
13853
13947
  // 用户取消信号穿进 provider options:providerStreamRetry 据此在
13854
13948
  // 取消后跳过重试,provider 分支(如 antigravity/openai-compatible)
13855
13949
  // 也可把 fetch 绑定到同一信号,实现真正的传输层取消。
@@ -13858,6 +13952,7 @@ async function runLocalAgentTurn(input) {
13858
13952
  // 归到具体对话而不是 chat-proxy 兜底桶。新对话首轮 id 尚未分配。
13859
13953
  ...input.continueDialogId ? { dialogId: input.continueDialogId } : {},
13860
13954
  ...shouldStreamDeltas ? { onTextDelta: (chunk) => {
13955
+ llmStreamActivity += 1;
13861
13956
  partialContent += chunk;
13862
13957
  observationBoundary.emit({
13863
13958
  kind: "text-delta",
@@ -13867,6 +13962,7 @@ async function runLocalAgentTurn(input) {
13867
13962
  });
13868
13963
  } } : {},
13869
13964
  ...shouldStreamReasoning ? { onReasoningDelta: (chunk) => {
13965
+ llmStreamActivity += 1;
13870
13966
  observationBoundary.emit({
13871
13967
  kind: "reasoning-delta",
13872
13968
  chunk,
@@ -13875,6 +13971,7 @@ async function runLocalAgentTurn(input) {
13875
13971
  });
13876
13972
  } } : {},
13877
13973
  ...shouldPassToolEvents ? { onToolEvent: (event) => {
13974
+ llmStreamActivity += 1;
13878
13975
  observationBoundary.emit({
13879
13976
  kind: "tool-event",
13880
13977
  event,
@@ -13885,6 +13982,7 @@ async function runLocalAgentTurn(input) {
13885
13982
  ...shouldPassToolEvents ? { toolEventRound: round } : {}
13886
13983
  },
13887
13984
  timeoutMs: resolveLlmRequestTimeoutMs(input),
13985
+ activityVersion: () => llmStreamActivity,
13888
13986
  round,
13889
13987
  input,
13890
13988
  boundary: observationBoundary,
@@ -14366,23 +14464,6 @@ function kimiIdentityHeaders(url) {
14366
14464
  return isKimiEndpoint(url) ? { "User-Agent": KIMI_CODE_USER_AGENT } : {};
14367
14465
  }
14368
14466
 
14369
- // packages/agent-runtime/sseDataLine.ts
14370
- function parseSseDataLineJson(line) {
14371
- const trimmed = line.trim();
14372
- if (!trimmed.startsWith("data:")) return null;
14373
- const payload = trimmed.slice(5).trim();
14374
- if (!payload || payload === "[DONE]") return null;
14375
- try {
14376
- return JSON.parse(payload);
14377
- } catch {
14378
- return null;
14379
- }
14380
- }
14381
- function parseSseDataLineObject(line) {
14382
- const parsed = parseSseDataLineJson(line);
14383
- return parsed && typeof parsed === "object" ? parsed : null;
14384
- }
14385
-
14386
14467
  // packages/integrations/openai/responsesRequestBody.ts
14387
14468
  function buildResponsesRequestBody(body, model) {
14388
14469
  const tools = Array.isArray(body.tools) ? toResponsesTools(body.tools) : void 0;
@@ -15652,132 +15733,6 @@ async function evaluateCapabilityPolicy(capability, normalizedInput, ctx) {
15652
15733
  }
15653
15734
  }
15654
15735
 
15655
- // packages/agent-runtime/localToolPolicy.ts
15656
- var NEVER_LOCAL_TOOLS = /* @__PURE__ */ new Set([
15657
- "deleteSpaces",
15658
- "updateAgent",
15659
- "updateSelf",
15660
- "createAgent"
15661
- ]);
15662
- var DEFAULT_LOCAL_TOOLS = /* @__PURE__ */ new Set([
15663
- "readFile",
15664
- "writeFile",
15665
- "editFile",
15666
- "globFiles",
15667
- "execShell",
15668
- "launchProcess",
15669
- "listProcesses",
15670
- // ProcessTask follow-up tools. Same tier as listProcesses: they observe/stop
15671
- // tasks the agent itself already spawned, so their blast radius is strictly
15672
- // narrower than launchProcess (which is default-enabled). Model-facing
15673
- // follow-ups are taskWait/taskStop only — lifecycle history (taskLogs) stays
15674
- // runtime-internal.
15675
- "taskWait",
15676
- "taskStop",
15677
- // Scheduling tools: forking/observing sub-agents is a declared capability
15678
- // of capable agents, and the blast radius is strictly narrower than
15679
- // execShell (already default-enabled). Requiring an env allowlist for these
15680
- // while execShell is open by default inverted the risk model and forced every
15681
- // new local user to hand-edit .env files before sub-agent tasks worked.
15682
- "startAgentRun",
15683
- "controlAgentRun"
15684
- ]);
15685
- function parseToolAllowlist(value) {
15686
- return (value ?? "").split(",").flatMap((item) => {
15687
- const trimmed = item.trim();
15688
- return trimmed ? [trimmed] : [];
15689
- });
15690
- }
15691
- function normalizeLocalToolName(toolName) {
15692
- return canonicalizeToolName(
15693
- String(toolName ?? "").replace(/^functions\./, "").trim()
15694
- );
15695
- }
15696
- function isRestrictedLocalToolMode(env) {
15697
- return env.NOLO_LOCAL_TOOL_MODE === "restricted";
15698
- }
15699
- function resolveLocalToolPolicy(args2) {
15700
- const toolName = normalizeLocalToolName(args2.toolName);
15701
- const restrictedMode = isRestrictedLocalToolMode(args2.env);
15702
- if (!toolName) {
15703
- return {
15704
- allowed: false,
15705
- toolName,
15706
- reason: "Tool name is required."
15707
- };
15708
- }
15709
- if (NEVER_LOCAL_TOOLS.has(toolName)) {
15710
- return {
15711
- allowed: false,
15712
- toolName,
15713
- reason: `${toolName} is blocked by the local runtime safety policy.`
15714
- };
15715
- }
15716
- const allowedByEnv = new Set(parseToolAllowlist(args2.env.NOLO_LOCAL_ALLOWED_TOOLS));
15717
- const agentTools = new Set(
15718
- (args2.agentToolNames ?? []).flatMap((item) => {
15719
- const normalized = normalizeLocalToolName(item);
15720
- return normalized ? [normalized] : [];
15721
- })
15722
- );
15723
- if (!restrictedMode && agentTools.has(toolName)) {
15724
- return { allowed: true, toolName };
15725
- }
15726
- if (DEFAULT_LOCAL_TOOLS.has(toolName) && agentTools.has(toolName)) {
15727
- return { allowed: true, toolName };
15728
- }
15729
- if (allowedByEnv.has(toolName) && agentTools.has(toolName)) {
15730
- return { allowed: true, toolName };
15731
- }
15732
- return {
15733
- allowed: false,
15734
- toolName,
15735
- reason: `${toolName} is not enabled for local runtime runs. The agent must declare the tool in its manifest; in restricted mode (NOLO_LOCAL_TOOL_MODE=restricted) non-default tools also require adding to NOLO_LOCAL_ALLOWED_TOOLS (e.g. NOLO_LOCAL_ALLOWED_TOOLS=someCustomTool) and restarting the session so the env var loads.`
15736
- };
15737
- }
15738
- async function executeLocalToolWithPolicy(args2) {
15739
- if (args2.runToolNames !== void 0 && !args2.runToolNames.includes(normalizeLocalToolName(args2.call.name))) {
15740
- throw new Error(`${args2.call.name} is outside the final run tool surface.`);
15741
- }
15742
- const decision = resolveLocalToolPolicy({
15743
- env: args2.env,
15744
- agentToolNames: args2.agentToolNames,
15745
- toolName: args2.call.name
15746
- });
15747
- if (!decision.allowed) throw new Error(decision.reason);
15748
- const executor = args2.executors?.[decision.toolName];
15749
- if (!executor) {
15750
- throw new Error(`${decision.toolName} is allowed by policy but no local executor is registered.`);
15751
- }
15752
- const opts = {
15753
- ...args2.abortSignal ? { abortSignal: args2.abortSignal } : {},
15754
- ...args2.detachMs !== void 0 ? { detachMs: args2.detachMs } : {},
15755
- ...args2.confirmed !== void 0 ? { confirmed: args2.confirmed } : {},
15756
- ...args2.enableDestructiveShellGuard !== void 0 ? { enableDestructiveShellGuard: args2.enableDestructiveShellGuard } : {},
15757
- ...args2.confirmDestructiveAction ? { confirmDestructiveAction: args2.confirmDestructiveAction } : {},
15758
- ...args2.blockDestructiveWithoutConfirmation !== void 0 ? { blockDestructiveWithoutConfirmation: args2.blockDestructiveWithoutConfirmation } : {}
15759
- };
15760
- if (decision.toolName === "execShell") {
15761
- let parsedInput = args2.call.arguments;
15762
- if (typeof parsedInput === "string") {
15763
- try {
15764
- parsedInput = JSON.parse(parsedInput);
15765
- } catch {
15766
- parsedInput = { command: parsedInput };
15767
- }
15768
- }
15769
- await evaluateExecShellDestructiveGuard(parsedInput, opts);
15770
- opts.confirmed = true;
15771
- }
15772
- return executor(
15773
- {
15774
- ...args2.call,
15775
- name: decision.toolName
15776
- },
15777
- opts
15778
- );
15779
- }
15780
-
15781
15736
  // packages/agent-runtime/localWorkspaceTools.ts
15782
15737
  import { existsSync as existsSync3 } from "node:fs";
15783
15738
  import { createHash as createHash3 } from "node:crypto";
@@ -16576,29 +16531,6 @@ function getProcessRegistry() {
16576
16531
  return registry;
16577
16532
  }
16578
16533
 
16579
- // packages/agent-runtime/processOwnership.ts
16580
- var asNonEmptyString = (value) => {
16581
- if (typeof value !== "string") return null;
16582
- const trimmed = value.trim();
16583
- return trimmed ? trimmed : null;
16584
- };
16585
- function readProcessOwner(runtimeContext) {
16586
- if (!runtimeContext || typeof runtimeContext !== "object") return null;
16587
- const dialogId = asNonEmptyString(runtimeContext.dialogId) ?? asNonEmptyString(runtimeContext.parentThreadId);
16588
- if (!dialogId) return null;
16589
- const turnId = asNonEmptyString(runtimeContext.turnId);
16590
- const ownerId = asNonEmptyString(runtimeContext.ownerId);
16591
- return {
16592
- dialogId,
16593
- ...turnId ? { turnId } : {},
16594
- ...ownerId ? { ownerId } : {}
16595
- };
16596
- }
16597
-
16598
- // packages/agent-runtime/workspaceShell.ts
16599
- import { resolve as resolve3 } from "node:path";
16600
- import { spawn as spawnChildProcess2 } from "node:child_process";
16601
-
16602
16534
  // packages/agent-runtime/processTaskResult.ts
16603
16535
  var PROCESS_RESULT_CAPSULE_TAIL_CHARS = 4e3;
16604
16536
  var PROCESS_RESULT_CAPSULE_SPILL_THRESHOLD_CHARS = 2e4;
@@ -16618,11 +16550,17 @@ function buildProcessTaskResultCapsule(args2) {
16618
16550
  const exitCode = args2.exitCode;
16619
16551
  const captureError = args2.captureError?.trim() || void 0;
16620
16552
  const durationMs = typeof args2.startedAt === "number" && Number.isFinite(args2.startedAt) ? Math.max(0, (args2.endedAt ?? Date.now()) - args2.startedAt) : void 0;
16621
- const stdoutTail = tailText(stdout, tailChars);
16622
- const stderrTail = tailText(stderr, tailChars);
16553
+ const stdoutProvenance = args2.streamProvenance?.stdout;
16554
+ const stderrProvenance = args2.streamProvenance?.stderr;
16555
+ const ringEvicted = Boolean(stdoutProvenance?.evicted || stderrProvenance?.evicted);
16556
+ const stdoutTailSource = typeof args2.stdoutTail === "string" ? args2.stdoutTail : stdout;
16557
+ const stderrTailSource = typeof args2.stderrTail === "string" ? args2.stderrTail : stderr;
16558
+ const stdoutTail = tailText(stdoutTailSource, tailChars);
16559
+ const stderrTail = tailText(stderrTailSource, tailChars);
16623
16560
  const inlineSize = stdout.length + stderr.length;
16624
16561
  const inlineTruncated = stdoutTail.truncated || stderrTail.truncated;
16625
- const shouldSpill = inlineTruncated || inlineSize > spillThresholdChars;
16562
+ const truncated = inlineTruncated || ringEvicted || Boolean(captureError);
16563
+ const shouldSpill = truncated || inlineSize > spillThresholdChars;
16626
16564
  let spill;
16627
16565
  if (shouldSpill) {
16628
16566
  try {
@@ -16636,8 +16574,11 @@ function buildProcessTaskResultCapsule(args2) {
16636
16574
  });
16637
16575
  spill = {
16638
16576
  displayPath: result4.displayPath,
16639
- totalChars: result4.totalChars,
16640
- totalLines: result4.totalLines
16577
+ // Prefer the caller's live provenance counters: they count every
16578
+ // byte of the original stream, while the buffered content above may
16579
+ // be a capped prefix of it.
16580
+ totalChars: (stdoutProvenance?.totalChars ?? stdout.length) + (stderrProvenance?.totalChars ?? stderr.length),
16581
+ totalLines: (stdoutProvenance?.totalLines ?? countLines(stdout)) + (stderrProvenance?.totalLines ?? countLines(stderr))
16641
16582
  };
16642
16583
  } catch {
16643
16584
  }
@@ -16648,12 +16589,33 @@ function buildProcessTaskResultCapsule(args2) {
16648
16589
  exitCode,
16649
16590
  ...durationMs !== void 0 ? { durationMs } : {},
16650
16591
  ...captureError ? { captureError } : {},
16651
- truncated: inlineTruncated || Boolean(captureError),
16592
+ truncated,
16652
16593
  ...spill ? { spill } : {}
16653
16594
  };
16654
16595
  }
16655
16596
 
16597
+ // packages/agent-runtime/processOwnership.ts
16598
+ var asNonEmptyString = (value) => {
16599
+ if (typeof value !== "string") return null;
16600
+ const trimmed = value.trim();
16601
+ return trimmed ? trimmed : null;
16602
+ };
16603
+ function readProcessOwner(runtimeContext) {
16604
+ if (!runtimeContext || typeof runtimeContext !== "object") return null;
16605
+ const dialogId = asNonEmptyString(runtimeContext.dialogId) ?? asNonEmptyString(runtimeContext.parentThreadId);
16606
+ if (!dialogId) return null;
16607
+ const turnId = asNonEmptyString(runtimeContext.turnId);
16608
+ const ownerId = asNonEmptyString(runtimeContext.ownerId);
16609
+ return {
16610
+ dialogId,
16611
+ ...turnId ? { turnId } : {},
16612
+ ...ownerId ? { ownerId } : {}
16613
+ };
16614
+ }
16615
+
16656
16616
  // packages/agent-runtime/workspaceShell.ts
16617
+ import { resolve as resolve3 } from "node:path";
16618
+ import { spawn as spawnChildProcess2 } from "node:child_process";
16657
16619
  var EXEC_SHELL_TIMEOUT_ENV = "NOLO_EXEC_SHELL_TIMEOUT_MS";
16658
16620
  var EXEC_SHELL_DETACH_ENV = "NOLO_EXEC_SHELL_DETACH_MS";
16659
16621
  var DEFAULT_EXEC_SHELL_DETACH_MS = 12e4;
@@ -17522,68 +17484,230 @@ function runTasks(args2) {
17522
17484
  return { tasks, count: tasks.length };
17523
17485
  }
17524
17486
 
17525
- // packages/agent-runtime/capabilities/execShellCapability.ts
17526
- function buildExecShellToolDefinition(toolName = "execShell") {
17527
- return {
17528
- type: "function",
17529
- function: {
17530
- name: toolName,
17531
- description: `Execute a shell command from the workspace root. Prefer one compound command (e.g. 'git status && git diff --stat') to perform complete verification in one step instead of multiple small roundtrips. Do not cd into guessed paths; commands already run from the workspace root. Commands block until exit. Long-running commands (sleep over ${IMMEDIATE_DETACH_SLEEP_THRESHOLD_SECONDS}s, dev servers, watchers) automatically detach to background returning {detached: true, pid, label}; for persistent services, prefer launchProcess. On Windows the resolved shell may be Windows PowerShell 5.1 (metadata.shellKind="powershell5"): it does not support '&&'/'||' or $PSStyle \u2014 join commands with ';' or split into multiple calls. metadata.shellKind="pwsh" means PowerShell 7+ with full syntax. Read resolvedShell/shellKind from any execShell result before writing Windows-specific syntax.`,
17532
- parameters: {
17533
- type: "object",
17534
- properties: {
17535
- command: {
17536
- type: "string",
17537
- description: "Shell command to run (non-empty)."
17538
- },
17539
- cmd: {
17540
- type: "string",
17541
- description: "Compatibility alias for command."
17542
- }
17543
- }
17487
+ // packages/agent-runtime/execShellCompletionWarnings.ts
17488
+ var BG_ESCAPE_WORD_PATTERN = /(?:^|[\s;|(&/\\])(nohup|disown|setsid|start-process)(?=$|[\s;|)&])/i;
17489
+ var SHELL_SPLIT_CHARS = /* @__PURE__ */ new Set([";", "|", "&", "(", ")", "<", ">", "\n", " ", " "]);
17490
+ function maskQuotedAndEscaped(command) {
17491
+ const out = Array.from(command);
17492
+ let quote = null;
17493
+ for (let i = 0; i < out.length; i += 1) {
17494
+ const ch = out[i];
17495
+ if (quote) {
17496
+ out[i] = " ";
17497
+ if (ch === "\\" && quote !== "'") {
17498
+ if (i + 1 < out.length) out[i + 1] = " ";
17499
+ i += 1;
17500
+ } else if (ch === quote) {
17501
+ quote = null;
17544
17502
  }
17503
+ continue;
17545
17504
  }
17546
- };
17547
- }
17548
- function normalizeExecShellInput(input) {
17549
- if (input === null || input === void 0) {
17550
- throw new Error("execShell requires a non-empty command.");
17551
- }
17552
- if (typeof input === "string") {
17553
- const trimmed = input.trim();
17554
- if (!trimmed) {
17555
- throw new Error("execShell requires a non-empty command.");
17505
+ if (ch === "\\") {
17506
+ out[i] = " ";
17507
+ if (i + 1 < out.length) out[i + 1] = " ";
17508
+ i += 1;
17509
+ continue;
17556
17510
  }
17557
- if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
17558
- try {
17559
- const parsed = JSON.parse(trimmed);
17560
- if (parsed && typeof parsed === "object") {
17561
- return normalizeExecShellInput(parsed);
17562
- }
17563
- } catch {
17511
+ if (ch === "'" || ch === '"' || ch === "`") {
17512
+ quote = ch;
17513
+ out[i] = " ";
17514
+ continue;
17515
+ }
17516
+ if (ch === "#" && (i === 0 || /[\s;]/.test(out[i - 1] ?? " "))) {
17517
+ while (i < out.length && out[i] !== "\n") {
17518
+ out[i] = " ";
17519
+ i += 1;
17564
17520
  }
17565
17521
  }
17566
- return { command: trimmed };
17567
17522
  }
17568
- if (typeof input === "object") {
17569
- const record = input;
17570
- let rawCommand = resolveShellCommandArg(record);
17571
- if (typeof rawCommand !== "string" || !rawCommand.trim()) {
17572
- const fallback = record.command ?? record.cmd;
17573
- if (typeof fallback === "string" && fallback.trim()) {
17574
- rawCommand = fallback;
17523
+ return out.join("");
17524
+ }
17525
+ function readRedirectTarget(original, start) {
17526
+ let i = start;
17527
+ while (i < original.length && /\s/.test(original[i] ?? "")) {
17528
+ i += 1;
17529
+ }
17530
+ if (i >= original.length) return void 0;
17531
+ let text = "";
17532
+ let j = i;
17533
+ while (j < original.length) {
17534
+ const o = original[j];
17535
+ if (o === "'" || o === '"' || o === "`") {
17536
+ const quote = o;
17537
+ j += 1;
17538
+ while (j < original.length && original[j] !== quote) {
17539
+ if (original[j] === "\\" && quote !== "'" && j + 1 < original.length) {
17540
+ text += original[j + 1];
17541
+ j += 2;
17542
+ continue;
17543
+ }
17544
+ text += original[j];
17545
+ j += 1;
17575
17546
  }
17547
+ j += 1;
17548
+ continue;
17576
17549
  }
17577
- const command = typeof rawCommand === "string" ? rawCommand.trim() : "";
17550
+ if (/\s/.test(o) || SHELL_SPLIT_CHARS.has(o)) break;
17551
+ text += o;
17552
+ j += 1;
17553
+ }
17554
+ if (!text || text === "$") return void 0;
17555
+ return { text, end: j };
17556
+ }
17557
+ function isIgnoredRedirectTarget(target) {
17558
+ return target === "/dev/null";
17559
+ }
17560
+ function detectExecShellSynchronousWarnings(command, shell) {
17561
+ const masked = maskQuotedAndEscaped(command);
17562
+ const isPowerShell = shell?.resolvedShell === "powershell";
17563
+ const redirectTargets = [];
17564
+ let backgrounded = false;
17565
+ let i = 0;
17566
+ while (i < masked.length) {
17567
+ const ch = masked[i];
17568
+ if (ch === "&") {
17569
+ const next = masked[i + 1];
17570
+ if (next === "&") {
17571
+ i += 2;
17572
+ continue;
17573
+ }
17574
+ if (next === ">") {
17575
+ let j = i + 2;
17576
+ if (masked[j] === ">") j += 1;
17577
+ const target = readRedirectTarget(command, j);
17578
+ if (target && !isIgnoredRedirectTarget(target.text)) redirectTargets.push(target.text);
17579
+ i = target ? target.end : j;
17580
+ continue;
17581
+ }
17582
+ const prev = masked[i - 1];
17583
+ if (prev === ">" || prev === "<") {
17584
+ i += 1;
17585
+ continue;
17586
+ }
17587
+ if (isPowerShell) {
17588
+ i += 1;
17589
+ continue;
17590
+ }
17591
+ backgrounded = true;
17592
+ i += 1;
17593
+ continue;
17594
+ }
17595
+ if (ch === ">") {
17596
+ const prev = masked[i - 1];
17597
+ if (prev === ">" || prev === "&" || prev === "<") {
17598
+ i += 1;
17599
+ continue;
17600
+ }
17601
+ let j = i + 1;
17602
+ if (masked[j] === ">") j += 1;
17603
+ if (masked[j] === "&") {
17604
+ const ampTarget = readRedirectTarget(command, j + 1);
17605
+ const isFdDuplication = ampTarget !== void 0 && (/^\d+$/.test(ampTarget.text) || ampTarget.text === "-");
17606
+ if (ampTarget && !isFdDuplication && !isIgnoredRedirectTarget(ampTarget.text)) {
17607
+ redirectTargets.push(ampTarget.text);
17608
+ }
17609
+ i = ampTarget ? ampTarget.end : j + 1;
17610
+ continue;
17611
+ }
17612
+ const target = readRedirectTarget(command, j);
17613
+ if (target && !isIgnoredRedirectTarget(target.text)) redirectTargets.push(target.text);
17614
+ i = target ? target.end : j;
17615
+ continue;
17616
+ }
17617
+ if (ch === "<") {
17618
+ i += masked[i + 1] === "<" || masked[i + 1] === "&" ? 2 : 1;
17619
+ continue;
17620
+ }
17621
+ i += 1;
17622
+ }
17623
+ const wordMatch = BG_ESCAPE_WORD_PATTERN.exec(masked);
17624
+ if (wordMatch) backgrounded = true;
17625
+ const warnings = [];
17626
+ if (redirectTargets.length > 0) {
17627
+ const target = redirectTargets[0];
17628
+ warnings.push({
17629
+ kind: "output-redirect",
17630
+ redirectTarget: target,
17631
+ message: `[wake-safety] This execShell call completed synchronously; its output was redirected to ${target}. That file is NOT delivered by any completion notification \u2014 no tracked background task was created. If you need the result, read that file in this turn.`
17632
+ });
17633
+ }
17634
+ if (backgrounded) {
17635
+ warnings.push({
17636
+ kind: "shell-backgrounding",
17637
+ message: `[wake-safety] Detected shell-internal backgrounding (lone \`&\`, nohup/disown/setsid/Start-Process or similar). Descendant processes are NOT tracked as platform tasks and will not produce completion notifications; text such as \`LAUNCHED\` printed by the command is plain stdout, not a platform receipt.`
17638
+ });
17639
+ }
17640
+ return warnings;
17641
+ }
17642
+
17643
+ // packages/agent-runtime/capabilities/execShellCapability.ts
17644
+ function buildExecShellToolDefinition(toolName = "execShell") {
17645
+ return {
17646
+ type: "function",
17647
+ function: {
17648
+ name: toolName,
17649
+ description: `Execute a shell command from the workspace root. Prefer one compound command (e.g. 'git status && git diff --stat') to perform complete verification in one step instead of multiple small roundtrips. Do not cd into guessed paths; commands already run from the workspace root. Commands block until exit. Long-running commands (sleep over ${IMMEDIATE_DETACH_SLEEP_THRESHOLD_SECONDS}s, dev servers, watchers) automatically detach to background returning {detached: true, pid, label}; for persistent services, prefer launchProcess. On Windows the resolved shell may be Windows PowerShell 5.1 (metadata.shellKind="powershell5"): it does not support '&&'/'||' or $PSStyle \u2014 join commands with ';' or split into multiple calls. metadata.shellKind="pwsh" means PowerShell 7+ with full syntax. Read resolvedShell/shellKind from any execShell result before writing Windows-specific syntax. LIFECYCLE CONTRACT (misreading this contract silenced a real dialog for 10 hours): (1) A normal command waits synchronously for at most 120 seconds; if it ends inside that window the call IS finished \u2014 a result containing exitCode means the command has already completed, never "still running". (2) Only a result with metadata.detached === true AND a taskId represents a tracked background task that may trigger completion wake-up (terminal completion turn carrying a bounded result capsule); that handle may be waited on with taskWait and will produce a completion notification. (3) Text the command itself prints (LAUNCHED, STARTED, BACKGROUND, ...) is plain stdout with zero lifecycle meaning \u2014 never read it as a platform receipt. (4) Shell-level backgrounding (\`&\`, nohup, disown, setsid, self-daemonizing scripts, PowerShell Start-Process) is NOT tracked: those descendants produce no completion notification, and the outer shell finishing tells you nothing about them. Use background: true to turn a finite job into a tracked background task immediately. When a synchronous result's core output was redirected to a file, read that file in the SAME turn \u2014 never end the turn promising a later automatic report without a taskId.`,
17650
+ parameters: {
17651
+ type: "object",
17652
+ properties: {
17653
+ command: {
17654
+ type: "string",
17655
+ description: "Shell command to run (non-empty)."
17656
+ },
17657
+ cmd: {
17658
+ type: "string",
17659
+ description: "Compatibility alias for command."
17660
+ },
17661
+ background: {
17662
+ type: "boolean",
17663
+ description: "Immediately detach this command into a tracked background task (no 120s synchronous wait): returns {detached: true, taskId}, and on completion the platform resumes this conversation with a bounded result capsule (exitCode, duration, stdout/stderr tail). Default false. Use for finite jobs (tests, builds, batch) whose result you need later; NOT for dev servers/watchers \u2014 use launchProcess for those."
17664
+ }
17665
+ }
17666
+ }
17667
+ }
17668
+ };
17669
+ }
17670
+ function normalizeExecShellInput(input) {
17671
+ if (input === null || input === void 0) {
17672
+ throw new Error("execShell requires a non-empty command.");
17673
+ }
17674
+ if (typeof input === "string") {
17675
+ const trimmed = input.trim();
17676
+ if (!trimmed) {
17677
+ throw new Error("execShell requires a non-empty command.");
17678
+ }
17679
+ if (trimmed.startsWith("{") && trimmed.endsWith("}")) {
17680
+ try {
17681
+ const parsed = JSON.parse(trimmed);
17682
+ if (parsed && typeof parsed === "object") {
17683
+ return normalizeExecShellInput(parsed);
17684
+ }
17685
+ } catch {
17686
+ }
17687
+ }
17688
+ return { command: trimmed };
17689
+ }
17690
+ if (typeof input === "object") {
17691
+ const record = input;
17692
+ let rawCommand = resolveShellCommandArg(record);
17693
+ if (typeof rawCommand !== "string" || !rawCommand.trim()) {
17694
+ const fallback = record.command ?? record.cmd;
17695
+ if (typeof fallback === "string" && fallback.trim()) {
17696
+ rawCommand = fallback;
17697
+ }
17698
+ }
17699
+ const command = typeof rawCommand === "string" ? rawCommand.trim() : "";
17578
17700
  if (!command) {
17579
17701
  throw new Error("execShell requires a non-empty command.");
17580
17702
  }
17581
17703
  const activity = extractActivity(record);
17704
+ const background = record.background === true || typeof record.background === "string" && record.background.trim().toLowerCase() === "true";
17582
17705
  return {
17583
17706
  command,
17584
17707
  ...record.shell !== void 0 ? { shell: record.shell } : {},
17585
17708
  ...activity ? { activity } : {},
17586
- ...record.input !== void 0 ? { input: record.input } : {}
17709
+ ...record.input !== void 0 ? { input: record.input } : {},
17710
+ ...background ? { background: true } : {}
17587
17711
  };
17588
17712
  }
17589
17713
  throw new Error("execShell requires a non-empty command.");
@@ -17621,6 +17745,7 @@ var execShellCapability = {
17621
17745
  command,
17622
17746
  shell: normalized.shell
17623
17747
  });
17748
+ const background = normalized.background === true;
17624
17749
  const result4 = await runWorkspaceCommand({
17625
17750
  workspaceRoot,
17626
17751
  command: shellPlan.argv,
@@ -17628,15 +17753,18 @@ var execShellCapability = {
17628
17753
  outputLimit: ctx.commandOutputLimit,
17629
17754
  commandPrefix: ctx.commandPrefix,
17630
17755
  abortSignal: ctx.abortSignal,
17631
- detachMs: isImmediateDetachShellCommand({ command }) ? 0 : ctx.detachMs,
17756
+ detachMs: background ? 0 : isImmediateDetachShellCommand({ command }) ? 0 : ctx.detachMs,
17632
17757
  stdin: typeof normalized.input === "string" ? normalized.input : void 0,
17633
17758
  // Ownership of the turn that invoked this command: an auto-detached
17634
17759
  // command keeps its parent dialog so its terminal can resume that
17635
17760
  // conversation (see processOwnership.ts). Absent ctx.owner = unowned.
17636
17761
  owner: ctx.owner ?? null
17637
17762
  });
17763
+ const warnings = result4.detached || result4.spawnFailed ? [] : detectExecShellSynchronousWarnings(command, {
17764
+ resolvedShell: shellPlan.resolvedShell
17765
+ });
17638
17766
  return {
17639
- content: result4.content,
17767
+ content: warnings.length > 0 ? [result4.content, ...warnings.map((warning) => warning.message)].join("\n\n") : result4.content,
17640
17768
  metadata: {
17641
17769
  command,
17642
17770
  exitCode: result4.exitCode,
@@ -17651,8 +17779,21 @@ var execShellCapability = {
17651
17779
  // Phase 0: stable taskId from the pre-registered Execution
17652
17780
  // Envelope (additive; existing detached fields untouched).
17653
17781
  taskId: result4.taskId,
17654
- status: "running"
17655
- } : {},
17782
+ status: "running",
17783
+ // P0 machine-readable execution state: an explicit positive
17784
+ // signal instead of requiring the model to infer "not detached"
17785
+ // from absence. Mirrors the real lifecycle contract.
17786
+ executionState: "detached",
17787
+ trackedBackgroundTask: true,
17788
+ completionNotification: "terminal-resume"
17789
+ } : {
17790
+ // P0 machine-readable execution state: the command already
17791
+ // finished inside the synchronous window; no background task
17792
+ // exists, so no completion notification will ever arrive.
17793
+ executionState: "synchronous-complete",
17794
+ trackedBackgroundTask: false,
17795
+ completionNotification: "none"
17796
+ },
17656
17797
  ...normalized.activity ? { activity: normalized.activity } : {}
17657
17798
  }
17658
17799
  };
@@ -17868,7 +18009,7 @@ function buildLaunchProcessTool() {
17868
18009
  type: "function",
17869
18010
  function: {
17870
18011
  name: "launchProcess",
17871
- description: "Start a long-running background process (dev server, watcher, REPL) and return immediately with {pid, label, status}. Use listProcesses to inspect or stop it.",
18012
+ description: "Start a long-running ambient background process (dev server, watcher, REPL) and return immediately with {pid, label, taskId, status}. Real boundaries: this registers a tracked background task, but completion does NOT automatically start a new turn \u2014 the process is intentionally notice-only (an exited dev server must not silently burn a model turn). When it reaches a terminal state the bounded result (exitCode, duration, stdout/stderr tail) is recorded and surfaced as a notice injected only on your next real turn in the same conversation. Use listProcesses to inspect, taskWait to block for its terminal state, or taskStop to stop it. For a finite job (tests, builds, batch) whose result must resume the conversation automatically, use execShell with background: true instead.",
17872
18013
  parameters: {
17873
18014
  type: "object",
17874
18015
  properties: {
@@ -19009,6 +19150,55 @@ async function execShellTool(args2) {
19009
19150
  }
19010
19151
  );
19011
19152
  }
19153
+ var LAUNCH_PROCESS_STREAM_RING_CHARS = 65536;
19154
+ var LAUNCH_PROCESS_STREAM_CAPTURE_MAX_CHARS = 4e6;
19155
+ function createLaunchProcessStreamCapture() {
19156
+ let ring = "";
19157
+ let full = null;
19158
+ let capturedChars = 0;
19159
+ let totalChars = 0;
19160
+ let totalNewlines = 0;
19161
+ return {
19162
+ push(chunk) {
19163
+ const str = typeof chunk === "string" ? chunk : chunk.toString("utf8");
19164
+ if (!str) return;
19165
+ totalChars += str.length;
19166
+ for (let i = 0; i < str.length; i += 1) {
19167
+ if (str.charCodeAt(i) === 10) totalNewlines += 1;
19168
+ }
19169
+ const combined = ring + str;
19170
+ if (combined.length <= LAUNCH_PROCESS_STREAM_RING_CHARS) {
19171
+ ring = combined;
19172
+ return;
19173
+ }
19174
+ if (full === null) {
19175
+ full = [combined];
19176
+ capturedChars = combined.length;
19177
+ } else if (capturedChars < LAUNCH_PROCESS_STREAM_CAPTURE_MAX_CHARS) {
19178
+ const room = LAUNCH_PROCESS_STREAM_CAPTURE_MAX_CHARS - capturedChars;
19179
+ const slice = str.length <= room ? str : str.slice(0, room);
19180
+ full.push(slice);
19181
+ capturedChars += slice.length;
19182
+ }
19183
+ ring = combined.slice(-LAUNCH_PROCESS_STREAM_RING_CHARS);
19184
+ },
19185
+ text() {
19186
+ return full === null ? ring : full.join("");
19187
+ },
19188
+ tailText() {
19189
+ return ring;
19190
+ },
19191
+ evicted() {
19192
+ return full !== null;
19193
+ },
19194
+ totalChars() {
19195
+ return totalChars;
19196
+ },
19197
+ totalLines() {
19198
+ return totalChars > 0 ? totalNewlines + 1 : 0;
19199
+ }
19200
+ };
19201
+ }
19012
19202
  async function launchProcessTool(args2) {
19013
19203
  const parsed = parseWorkspaceToolArguments(args2.call.arguments);
19014
19204
  const command = requireShellCommand(parsed, args2.call.name);
@@ -19036,6 +19226,14 @@ async function launchProcessTool(args2) {
19036
19226
  stdio: ["ignore", "pipe", "pipe"],
19037
19227
  detached
19038
19228
  });
19229
+ const stdoutCapture = createLaunchProcessStreamCapture();
19230
+ const stderrCapture = createLaunchProcessStreamCapture();
19231
+ proc.stdout?.on("data", (chunk) => stdoutCapture.push(chunk));
19232
+ proc.stderr?.on("data", (chunk) => stderrCapture.push(chunk));
19233
+ proc.stdout?.on("error", () => {
19234
+ });
19235
+ proc.stderr?.on("error", () => {
19236
+ });
19039
19237
  const pid = proc.pid;
19040
19238
  if (typeof pid !== "number") {
19041
19239
  return {
@@ -19076,7 +19274,38 @@ async function launchProcessTool(args2) {
19076
19274
  process.removeListener("SIGTERM", cleanupChildOnHostSignal);
19077
19275
  process.removeListener("SIGINT", cleanupChildOnHostSignal);
19078
19276
  }
19079
- registry2.markExited(pid, code ?? 1);
19277
+ const exitCode = code ?? 1;
19278
+ let capsule;
19279
+ try {
19280
+ capsule = buildProcessTaskResultCapsule({
19281
+ // The full captured output (bounded prefix when the process was
19282
+ // extremely chatty): the module tails it for the inline capsule and
19283
+ // spills it for recovery. Provenance carries the live original-stream
19284
+ // counters so truncated/spill metadata never derives from the ring.
19285
+ stdout: stdoutCapture.text(),
19286
+ stderr: stderrCapture.text(),
19287
+ stdoutTail: stdoutCapture.tailText(),
19288
+ stderrTail: stderrCapture.tailText(),
19289
+ exitCode,
19290
+ // Duration measured from the registry envelope's startedAt (spawn).
19291
+ startedAt: envelope.startedAt,
19292
+ workspaceRoot: args2.workspaceRoot,
19293
+ streamProvenance: {
19294
+ stdout: {
19295
+ evicted: stdoutCapture.evicted(),
19296
+ totalChars: stdoutCapture.totalChars(),
19297
+ totalLines: stdoutCapture.totalLines()
19298
+ },
19299
+ stderr: {
19300
+ evicted: stderrCapture.evicted(),
19301
+ totalChars: stderrCapture.totalChars(),
19302
+ totalLines: stderrCapture.totalLines()
19303
+ }
19304
+ }
19305
+ });
19306
+ } catch {
19307
+ }
19308
+ registry2.markExited(pid, exitCode, capsule);
19080
19309
  });
19081
19310
  const activity = extractActivity(parsed);
19082
19311
  const resultData = { pid, label, taskId: envelope.taskId, status: "running" };
@@ -19088,6 +19317,13 @@ async function launchProcessTool(args2) {
19088
19317
  label,
19089
19318
  status: "running",
19090
19319
  taskId: envelope.taskId,
19320
+ // P0 machine-readable execution state (consistent with the real wake
19321
+ // contract): a real background task from birth, but ambient — its
19322
+ // terminal result rides the next real turn's notice, not an automatic
19323
+ // completion turn.
19324
+ executionState: "background",
19325
+ trackedBackgroundTask: true,
19326
+ completionNotification: "next-turn-notice",
19091
19327
  processLaunch: resultData,
19092
19328
  ...activity ? { activity } : {}
19093
19329
  }
@@ -19312,6 +19548,290 @@ function createLocalWorkspaceToolExecutors(args2) {
19312
19548
  };
19313
19549
  }
19314
19550
 
19551
+ // packages/agent-runtime/toolArgumentGate.ts
19552
+ var FIELD_ALIAS_GROUPS = [
19553
+ // localWorkspaceTools.ts:336 导出常量(readFile/writeFile/editFile/globFiles)
19554
+ PATH_FIELD_ALIASES,
19555
+ // localWorkspaceTools.ts:339-340 导出常量(editFile)
19556
+ OLD_TEXT_FIELD_ALIASES,
19557
+ NEW_TEXT_FIELD_ALIASES,
19558
+ // localWorkspaceTools.ts:733 `args.cmd || args.command`(launchProcess);
19559
+ // launchProcess 的 required ["command"] 见 localWorkspaceToolDefs.ts:283。
19560
+ // 注:execShell 的 schema 无 required(command 在 executor 层强制,
19561
+ // capabilities/execShellCapability.ts:42 声明 cmd 兼容字段),本组对它不产生拦截。
19562
+ ["command", "cmd"],
19563
+ // localWorkspaceTools.ts:698-699 `args.pattern ?? args.glob`(globFiles)
19564
+ ["pattern", "glob"],
19565
+ // noloWorkspaceTools.ts:492 `args.dialog ?? args.dialogId ?? args.id`(readDialog)
19566
+ ["dialog", "dialogId", "id"],
19567
+ // noloWorkspaceTools.ts:545 `args.agent ?? args.agentKey ?? args.id`(readAgent)
19568
+ ["agent", "agentKey", "id"],
19569
+ // noloWorkspaceTools.ts:552 `args.space ?? args.spaceId ?? args.id`(readSpace)
19570
+ ["space", "spaceId", "id"],
19571
+ // noloWorkspaceTools.ts:561/567 `args.doc ?? args.docKey ?? args.pageKey ?? args.key`
19572
+ // (readDoc / readSkillDoc)
19573
+ ["doc", "docKey", "pageKey", "key"],
19574
+ // noloWorkspaceTools.node.ts:238 `parsed.name ?? parsed.skillName ?? parsed.skill`(loadSkill)
19575
+ ["name", "skillName", "skill"],
19576
+ // noloWorkspaceTools.ts:593 `args.table ?? args.tableId ?? args.metaKey`(queryTableRows)
19577
+ ["table", "tableId", "metaKey"]
19578
+ ];
19579
+ var TYPE_MATCHERS = {
19580
+ string: (value) => typeof value === "string",
19581
+ // 数字字符串视为可接受:executor 普遍 Number()/clamp 宽松处理,拦截会误报。
19582
+ number: (value) => typeof value === "number" || typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value)),
19583
+ integer: (value) => typeof value === "number" || typeof value === "string" && value.trim() !== "" && Number.isFinite(Number(value)),
19584
+ boolean: (value) => typeof value === "boolean" || value === "true" || value === "false",
19585
+ object: (value) => typeof value === "object" && value !== null && !Array.isArray(value),
19586
+ array: (value) => Array.isArray(value)
19587
+ };
19588
+ function describeValueType(value) {
19589
+ if (value === null) return "null";
19590
+ if (Array.isArray(value)) return "array";
19591
+ return typeof value;
19592
+ }
19593
+ function isAliasSatisfied(args2, field) {
19594
+ for (const group of FIELD_ALIAS_GROUPS) {
19595
+ if (!group.includes(field)) continue;
19596
+ if (group.some((alias) => args2[alias] !== void 0)) return true;
19597
+ }
19598
+ return false;
19599
+ }
19600
+ function readRequiredFields(parameters) {
19601
+ const required = parameters.required;
19602
+ if (!Array.isArray(required)) return [];
19603
+ return required.filter((field) => typeof field === "string");
19604
+ }
19605
+ function readProperties(parameters) {
19606
+ const properties = parameters.properties;
19607
+ if (!properties || typeof properties !== "object" || Array.isArray(properties)) {
19608
+ return {};
19609
+ }
19610
+ return properties;
19611
+ }
19612
+ function validateToolArguments(args2) {
19613
+ const { toolName, rawArguments, parameters } = args2;
19614
+ if (!parameters || typeof parameters !== "object" || Array.isArray(parameters)) {
19615
+ return { ok: true, args: {} };
19616
+ }
19617
+ const schema = parameters;
19618
+ const requiredFields = readRequiredFields(schema);
19619
+ const properties = readProperties(schema);
19620
+ if (requiredFields.length === 0 && Object.keys(properties).length === 0) {
19621
+ return { ok: true, args: {} };
19622
+ }
19623
+ const raw = typeof rawArguments === "string" ? rawArguments.trim() : "";
19624
+ let record = {};
19625
+ if (raw !== "") {
19626
+ let parsed;
19627
+ try {
19628
+ parsed = JSON.parse(raw);
19629
+ } catch {
19630
+ return { ok: true, args: {} };
19631
+ }
19632
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
19633
+ return { ok: true, args: {} };
19634
+ }
19635
+ record = parsed;
19636
+ }
19637
+ for (const field of requiredFields) {
19638
+ if (record[field] !== void 0) continue;
19639
+ if (isAliasSatisfied(record, field)) continue;
19640
+ return {
19641
+ ok: false,
19642
+ message: `${toolName} \u53C2\u6570\u6821\u9A8C\u5931\u8D25\uFF1A\u7F3A\u5C11\u5FC5\u586B\u53C2\u6570 "${field}"\u3002\u8BF7\u8865\u4E0A\u540E\u91CD\u65B0\u8C03\u7528 ${toolName}\u3002`
19643
+ };
19644
+ }
19645
+ for (const [field, spec] of Object.entries(properties)) {
19646
+ const value = record[field];
19647
+ if (value === void 0 || value === null) continue;
19648
+ if (!spec || typeof spec !== "object" || Array.isArray(spec)) continue;
19649
+ const declared = spec.type;
19650
+ if (typeof declared !== "string") continue;
19651
+ const matcher = TYPE_MATCHERS[declared];
19652
+ if (!matcher) continue;
19653
+ if (!matcher(value)) {
19654
+ return {
19655
+ ok: false,
19656
+ message: `${toolName} \u53C2\u6570\u6821\u9A8C\u5931\u8D25\uFF1A\u53C2\u6570 "${field}" \u5E94\u4E3A ${declared}\uFF0C\u5B9E\u9645\u6536\u5230 ${describeValueType(value)}\u3002\u8BF7\u4FEE\u6B63\u540E\u91CD\u65B0\u8C03\u7528 ${toolName}\u3002`
19657
+ };
19658
+ }
19659
+ }
19660
+ return { ok: true, args: record };
19661
+ }
19662
+ function buildToolParametersIndex(tools) {
19663
+ const index = /* @__PURE__ */ new Map();
19664
+ for (const tool of tools) {
19665
+ if (!tool || typeof tool !== "object") continue;
19666
+ const fn3 = tool.function;
19667
+ if (!fn3 || typeof fn3 !== "object") continue;
19668
+ const name = fn3.name;
19669
+ const parameters = fn3.parameters;
19670
+ if (typeof name === "string" && name && parameters !== void 0) {
19671
+ index.set(name, parameters);
19672
+ }
19673
+ }
19674
+ return index;
19675
+ }
19676
+ var cachedAgentRuntimeIndex = null;
19677
+ function getAgentRuntimeToolParametersIndex() {
19678
+ if (cachedAgentRuntimeIndex) return cachedAgentRuntimeIndex;
19679
+ const index = /* @__PURE__ */ new Map();
19680
+ try {
19681
+ const workspaceTools = buildLocalWorkspaceOpenAiTools({
19682
+ toolNames: [...WORKSPACE_TOOL_NAMES],
19683
+ exposeShellTools: true
19684
+ });
19685
+ for (const [name, parameters] of buildToolParametersIndex(workspaceTools)) {
19686
+ index.set(name, parameters);
19687
+ }
19688
+ const noloTools = buildNoloWorkspaceOpenAiTools({
19689
+ toolNames: [...NOLO_WORKSPACE_TOOL_NAMES]
19690
+ });
19691
+ for (const [name, parameters] of buildToolParametersIndex(noloTools)) {
19692
+ index.set(name, parameters);
19693
+ }
19694
+ } catch {
19695
+ }
19696
+ cachedAgentRuntimeIndex = index;
19697
+ return index;
19698
+ }
19699
+
19700
+ // packages/agent-runtime/localToolPolicy.ts
19701
+ var NEVER_LOCAL_TOOLS = /* @__PURE__ */ new Set([
19702
+ "deleteSpaces",
19703
+ "updateAgent",
19704
+ "updateSelf",
19705
+ "createAgent"
19706
+ ]);
19707
+ var DEFAULT_LOCAL_TOOLS = /* @__PURE__ */ new Set([
19708
+ "readFile",
19709
+ "writeFile",
19710
+ "editFile",
19711
+ "globFiles",
19712
+ "execShell",
19713
+ "launchProcess",
19714
+ "listProcesses",
19715
+ // ProcessTask follow-up tools. Same tier as listProcesses: they observe/stop
19716
+ // tasks the agent itself already spawned, so their blast radius is strictly
19717
+ // narrower than launchProcess (which is default-enabled). Model-facing
19718
+ // follow-ups are taskWait/taskStop only — lifecycle history (taskLogs) stays
19719
+ // runtime-internal.
19720
+ "taskWait",
19721
+ "taskStop",
19722
+ // Scheduling tools: forking/observing sub-agents is a declared capability
19723
+ // of capable agents, and the blast radius is strictly narrower than
19724
+ // execShell (already default-enabled). Requiring an env allowlist for these
19725
+ // while execShell is open by default inverted the risk model and forced every
19726
+ // new local user to hand-edit .env files before sub-agent tasks worked.
19727
+ "startAgentRun",
19728
+ "controlAgentRun"
19729
+ ]);
19730
+ function parseToolAllowlist(value) {
19731
+ return (value ?? "").split(",").flatMap((item) => {
19732
+ const trimmed = item.trim();
19733
+ return trimmed ? [trimmed] : [];
19734
+ });
19735
+ }
19736
+ function normalizeLocalToolName(toolName) {
19737
+ return canonicalizeToolName(
19738
+ String(toolName ?? "").replace(/^functions\./, "").trim()
19739
+ );
19740
+ }
19741
+ function isRestrictedLocalToolMode(env) {
19742
+ return env.NOLO_LOCAL_TOOL_MODE === "restricted";
19743
+ }
19744
+ function resolveLocalToolPolicy(args2) {
19745
+ const toolName = normalizeLocalToolName(args2.toolName);
19746
+ const restrictedMode = isRestrictedLocalToolMode(args2.env);
19747
+ if (!toolName) {
19748
+ return {
19749
+ allowed: false,
19750
+ toolName,
19751
+ reason: "Tool name is required."
19752
+ };
19753
+ }
19754
+ if (NEVER_LOCAL_TOOLS.has(toolName)) {
19755
+ return {
19756
+ allowed: false,
19757
+ toolName,
19758
+ reason: `${toolName} is blocked by the local runtime safety policy.`
19759
+ };
19760
+ }
19761
+ const allowedByEnv = new Set(parseToolAllowlist(args2.env.NOLO_LOCAL_ALLOWED_TOOLS));
19762
+ const agentTools = new Set(
19763
+ (args2.agentToolNames ?? []).flatMap((item) => {
19764
+ const normalized = normalizeLocalToolName(item);
19765
+ return normalized ? [normalized] : [];
19766
+ })
19767
+ );
19768
+ if (!restrictedMode && agentTools.has(toolName)) {
19769
+ return { allowed: true, toolName };
19770
+ }
19771
+ if (DEFAULT_LOCAL_TOOLS.has(toolName) && agentTools.has(toolName)) {
19772
+ return { allowed: true, toolName };
19773
+ }
19774
+ if (allowedByEnv.has(toolName) && agentTools.has(toolName)) {
19775
+ return { allowed: true, toolName };
19776
+ }
19777
+ return {
19778
+ allowed: false,
19779
+ toolName,
19780
+ reason: `${toolName} is not enabled for local runtime runs. The agent must declare the tool in its manifest; in restricted mode (NOLO_LOCAL_TOOL_MODE=restricted) non-default tools also require adding to NOLO_LOCAL_ALLOWED_TOOLS (e.g. NOLO_LOCAL_ALLOWED_TOOLS=someCustomTool) and restarting the session so the env var loads.`
19781
+ };
19782
+ }
19783
+ async function executeLocalToolWithPolicy(args2) {
19784
+ if (args2.runToolNames !== void 0 && !args2.runToolNames.includes(normalizeLocalToolName(args2.call.name))) {
19785
+ throw new Error(`${args2.call.name} is outside the final run tool surface.`);
19786
+ }
19787
+ const decision = resolveLocalToolPolicy({
19788
+ env: args2.env,
19789
+ agentToolNames: args2.agentToolNames,
19790
+ toolName: args2.call.name
19791
+ });
19792
+ if (!decision.allowed) throw new Error(decision.reason);
19793
+ const executor = args2.executors?.[decision.toolName];
19794
+ if (!executor) {
19795
+ throw new Error(`${decision.toolName} is allowed by policy but no local executor is registered.`);
19796
+ }
19797
+ const parametersSchema = args2.toolParametersIndex?.get(decision.toolName);
19798
+ if (parametersSchema !== void 0) {
19799
+ const gate = validateToolArguments({
19800
+ toolName: decision.toolName,
19801
+ rawArguments: args2.call.arguments,
19802
+ parameters: parametersSchema
19803
+ });
19804
+ if (!gate.ok) throw new Error(gate.message);
19805
+ }
19806
+ const opts = {
19807
+ ...args2.abortSignal ? { abortSignal: args2.abortSignal } : {},
19808
+ ...args2.detachMs !== void 0 ? { detachMs: args2.detachMs } : {},
19809
+ ...args2.confirmed !== void 0 ? { confirmed: args2.confirmed } : {},
19810
+ ...args2.enableDestructiveShellGuard !== void 0 ? { enableDestructiveShellGuard: args2.enableDestructiveShellGuard } : {},
19811
+ ...args2.confirmDestructiveAction ? { confirmDestructiveAction: args2.confirmDestructiveAction } : {},
19812
+ ...args2.blockDestructiveWithoutConfirmation !== void 0 ? { blockDestructiveWithoutConfirmation: args2.blockDestructiveWithoutConfirmation } : {}
19813
+ };
19814
+ if (decision.toolName === "execShell") {
19815
+ let parsedInput = args2.call.arguments;
19816
+ if (typeof parsedInput === "string") {
19817
+ try {
19818
+ parsedInput = JSON.parse(parsedInput);
19819
+ } catch {
19820
+ parsedInput = { command: parsedInput };
19821
+ }
19822
+ }
19823
+ await evaluateExecShellDestructiveGuard(parsedInput, opts);
19824
+ opts.confirmed = true;
19825
+ }
19826
+ return executor(
19827
+ {
19828
+ ...args2.call,
19829
+ name: decision.toolName
19830
+ },
19831
+ opts
19832
+ );
19833
+ }
19834
+
19315
19835
  // packages/cli/client/localDialogRecords.ts
19316
19836
  var localDialogMessageRecordToRuntimeMessage = dialogMessageRecordToAgentRuntimeMessage;
19317
19837
  function buildLocalDialogWritePlan(args2) {
@@ -19451,7 +19971,7 @@ function buildLoadSkillExecutor(args2) {
19451
19971
  const { resolveSkillByName } = await import("./skillDiscovery-CYUCJMAG.js");
19452
19972
  const resolved = resolveSkillByName(args2.cwd, name);
19453
19973
  if (!resolved) {
19454
- const { resolveBuiltinSkillByName } = await import("./builtinSkillRegistry-FUAVTRVZ.js");
19974
+ const { resolveBuiltinSkillByName } = await import("./builtinSkillRegistry-Q2VVY3SC.js");
19455
19975
  const builtin = resolveBuiltinSkillByName(name);
19456
19976
  if (builtin) {
19457
19977
  return {
@@ -19509,737 +20029,8 @@ No skills were discovered in this workspace.`;
19509
20029
  Available skills: ${available.join(", ")}`;
19510
20030
  }
19511
20031
 
19512
- // packages/core/chat/upstreamErrorBody.ts
19513
- function parseUpstreamErrorBody(errorText, statusText) {
19514
- if (errorText) {
19515
- try {
19516
- const parsed = JSON.parse(errorText);
19517
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
19518
- const error = parsed.error;
19519
- if (error && typeof error === "object" && !Array.isArray(error)) {
19520
- return parsed;
19521
- }
19522
- }
19523
- } catch {
19524
- }
19525
- }
19526
- return { error: { message: errorText || statusText } };
19527
- }
19528
-
19529
- // packages/agent-runtime/antigravityCloudCodeProvider.ts
19530
- import { randomUUID } from "node:crypto";
19531
-
19532
- // packages/agent-runtime/antigravityWireModel.ts
19533
- var WIRE_PROFILES = {
19534
- // Antigravity discovery exposes 3.7 Flash as one tiered wire model.
19535
- // The UI labels its effort variants (Low/Medium/High), but the literal
19536
- // -medium/-high ids return 404 from streamGenerateContent.
19537
- "gemini-3.7-flash-tiered": { modelEnum: "MODEL_PLACEHOLDER_M301", maxOutputTokens: 65536 },
19538
- // Gemini 3.8 Flash 沿用 3.7 的 tiered 兼容策略:仓库内暂无 Antigravity discovery
19539
- // 证据确认 3.8 的 wire modelEnum,禁止虚构,先只声明同档输出上限;enum 待
19540
- // discovery 证据到位后补齐。
19541
- "gemini-3.8-flash-tiered": { maxOutputTokens: 65536 },
19542
- "gemini-3.6-flash-low": { maxOutputTokens: 65536 },
19543
- "gemini-3.6-flash-medium": { maxOutputTokens: 65536 },
19544
- "gemini-3.6-flash-high": { maxOutputTokens: 65536 },
19545
- // 旧模型 wire 映射保留:存量自定义 agent 仍可能引用(注册表已下线不影响此处)。
19546
- "gemini-3.5-flash-extra-low": { modelEnum: "MODEL_PLACEHOLDER_M187", maxOutputTokens: 65536 },
19547
- "gemini-3.5-flash-low": { modelEnum: "MODEL_PLACEHOLDER_M20", maxOutputTokens: 65536 },
19548
- "gemini-3-flash-agent": { modelEnum: "MODEL_PLACEHOLDER_M132", maxOutputTokens: 65536 },
19549
- "gemini-3.1-pro-low": { modelEnum: "MODEL_PLACEHOLDER_M36", maxOutputTokens: 65535 },
19550
- "gemini-pro-agent": { modelEnum: "MODEL_PLACEHOLDER_M16", maxOutputTokens: 65535 },
19551
- "claude-sonnet-5": { maxOutputTokens: 64e3 },
19552
- "claude-sonnet-4-6": { maxOutputTokens: 64e3 },
19553
- "claude-opus-4-6-thinking": { maxOutputTokens: 64e3 },
19554
- "gemini-2.5-flash": { maxOutputTokens: 65536 },
19555
- "gemini-2.5-pro": { maxOutputTokens: 65536 }
19556
- };
19557
- function resolveAntigravityWireModel(logicalModelId) {
19558
- const id = logicalModelId.trim();
19559
- const lower = id.toLowerCase();
19560
- if (lower === "gemini-3.5-flash") {
19561
- const wireModelId = "gemini-3.5-flash-low";
19562
- return { wireModelId, profile: WIRE_PROFILES[wireModelId] };
19563
- }
19564
- if (lower === "gemini-3.7-flash" || ["gemini-3.7-flash-low", "gemini-3.7-flash-medium", "gemini-3.7-flash-high"].includes(lower)) {
19565
- const wireModelId = "gemini-3.7-flash-tiered";
19566
- return { wireModelId, profile: WIRE_PROFILES[wireModelId] };
19567
- }
19568
- if (lower === "gemini-3.8-flash" || ["gemini-3.8-flash-low", "gemini-3.8-flash-medium", "gemini-3.8-flash-high"].includes(lower)) {
19569
- const wireModelId = "gemini-3.8-flash-tiered";
19570
- return { wireModelId, profile: WIRE_PROFILES[wireModelId] };
19571
- }
19572
- if (lower === "gemini-3.6-flash") {
19573
- const wireModelId = "gemini-3.6-flash-low";
19574
- return { wireModelId, profile: WIRE_PROFILES[wireModelId] };
19575
- }
19576
- if (lower === "gemini-3.1-pro" || lower === "gemini-3.1-pro-preview") {
19577
- const wireModelId = "gemini-3.1-pro-low";
19578
- return { wireModelId, profile: WIRE_PROFILES[wireModelId] };
19579
- }
19580
- if (WIRE_PROFILES[id]) {
19581
- return { wireModelId: id, profile: WIRE_PROFILES[id] };
19582
- }
19583
- return { wireModelId: id };
19584
- }
19585
-
19586
- // packages/agent-runtime/geminiNativeShared.ts
19587
- var SKIP_THOUGHT_SIGNATURE = "skip_thought_signature_validator";
19588
- function resolveGeminiModelQuirks(modelId) {
19589
- const lower = modelId.toLowerCase();
19590
- const isGemini3 = lower.includes("gemini-3");
19591
- const isStrictSignatureModel = lower.includes("gemini-3.5") || lower.includes("flash-preview");
19592
- const isClaude = lower.includes("claude");
19593
- return {
19594
- requiresThoughtSignature: isGemini3,
19595
- allowsThoughtSignatureSentinel: isGemini3 && !isStrictSignatureModel,
19596
- isClaudeCrossModel: isClaude
19597
- };
19598
- }
19599
- function isGemini3Model(modelId) {
19600
- return resolveGeminiModelQuirks(modelId).requiresThoughtSignature;
19601
- }
19602
- function messageText(content) {
19603
- if (typeof content === "string") return content;
19604
- if (!Array.isArray(content)) return "";
19605
- return content.filter(
19606
- (part) => part?.type === "text"
19607
- ).map((part) => part.text).join("\n").trim();
19608
- }
19609
- function parseDataUrlToInlineData(url) {
19610
- const match7 = /^data:([A-Za-z0-9.+-]+\/[A-Za-z0-9.+-]+);base64,([A-Za-z0-9+/=]*)$/.exec(
19611
- url
19612
- );
19613
- if (!match7) return null;
19614
- return { mimeType: match7[1], data: match7[2] };
19615
- }
19616
- function extractInlineDataParts(content) {
19617
- if (!Array.isArray(content)) return [];
19618
- const parts2 = [];
19619
- for (const part of content) {
19620
- if (!part || typeof part !== "object") continue;
19621
- if (part.type !== "image_url") continue;
19622
- const url = part.image_url?.url;
19623
- if (typeof url !== "string" || !url.trim()) continue;
19624
- const inline = parseDataUrlToInlineData(url);
19625
- if (inline) parts2.push({ inlineData: inline });
19626
- }
19627
- return parts2;
19628
- }
19629
- function convertOpenAiMessagesToGemini(messages, options) {
19630
- const contents = [];
19631
- const systemTexts = [];
19632
- let toolCallIndex = 0;
19633
- let pendingFunctionCalls = [];
19634
- const pushOrMergeContent = (role, parts2) => {
19635
- if (parts2.length === 0) return;
19636
- const last = contents[contents.length - 1];
19637
- if (last && last.role === role) {
19638
- last.parts.push(...parts2);
19639
- } else {
19640
- contents.push({ role, parts: parts2 });
19641
- }
19642
- };
19643
- const flushPendingFunctionCalls = () => {
19644
- if (pendingFunctionCalls.length === 0) return;
19645
- const dummyResponses = pendingFunctionCalls.map(
19646
- ({ name, id }) => ({
19647
- functionResponse: {
19648
- name,
19649
- response: { output: "{}" },
19650
- ...id ? { id } : {}
19651
- }
19652
- })
19653
- );
19654
- pendingFunctionCalls = [];
19655
- pushOrMergeContent("user", dummyResponses);
19656
- };
19657
- for (const raw of messages) {
19658
- if (!raw || typeof raw !== "object" || !("role" in raw)) continue;
19659
- const role = String(raw.role);
19660
- const content = "content" in raw ? raw.content : null;
19661
- if (role === "system") {
19662
- const text = messageText(
19663
- content
19664
- );
19665
- if (text) systemTexts.push(text);
19666
- continue;
19667
- }
19668
- if (role === "user") {
19669
- flushPendingFunctionCalls();
19670
- const text = messageText(
19671
- content
19672
- );
19673
- const imageParts = extractInlineDataParts(
19674
- content
19675
- );
19676
- if (!text && imageParts.length === 0) continue;
19677
- const parts2 = [];
19678
- if (text) parts2.push({ text });
19679
- for (const img of imageParts) parts2.push(img);
19680
- pushOrMergeContent("user", parts2);
19681
- continue;
19682
- }
19683
- if (role === "assistant") {
19684
- flushPendingFunctionCalls();
19685
- const text = messageText(
19686
- content
19687
- );
19688
- const toolCalls = "tool_calls" in raw && Array.isArray(raw.tool_calls) ? raw.tool_calls ?? [] : [];
19689
- const parts2 = [];
19690
- if (text) {
19691
- parts2.push({ text });
19692
- }
19693
- for (const call of toolCalls) {
19694
- const name = call?.function?.name?.trim();
19695
- if (!name) continue;
19696
- const id = call.id?.trim() || `${name}_${toolCallIndex++}`;
19697
- pendingFunctionCalls.push({ name, id });
19698
- const realSignature = typeof call.thought_signature === "string" && call.thought_signature ? call.thought_signature : void 0;
19699
- const isFirstFunctionCallPart = !parts2.some(
19700
- (p) => "functionCall" in p
19701
- );
19702
- const signature = realSignature ?? (options.attachSkipThoughtSignature && isFirstFunctionCallPart ? SKIP_THOUGHT_SIGNATURE : void 0);
19703
- const part = {
19704
- functionCall: {
19705
- name,
19706
- args: parseToolArgs(call.function?.arguments),
19707
- ...id ? { id } : {}
19708
- },
19709
- ...signature ? { thoughtSignature: signature } : {}
19710
- };
19711
- parts2.push(part);
19712
- }
19713
- pushOrMergeContent("model", parts2);
19714
- continue;
19715
- }
19716
- if (role === "tool") {
19717
- const toolCallId = "tool_call_id" in raw && typeof raw.tool_call_id === "string" ? raw.tool_call_id : "";
19718
- const rawName = "name" in raw && typeof raw.name === "string" ? raw.name.trim() : "";
19719
- const pendingIndex = pendingFunctionCalls.findIndex(
19720
- (p) => toolCallId ? p.id === toolCallId : rawName ? p.name === rawName : true
19721
- );
19722
- if (pendingIndex === -1) continue;
19723
- const [{ name, id }] = pendingFunctionCalls.splice(pendingIndex, 1);
19724
- const output = messageText(content) || "{}";
19725
- pushOrMergeContent("user", [
19726
- {
19727
- functionResponse: {
19728
- name,
19729
- response: { output },
19730
- // 保留 OpenAI tool_call_id:antigravity Cloud Code Assist 网关把
19731
- // Gemini contents 转成 Claude messages 时需要 functionResponse →
19732
- // tool_result 的 tool_use_id;缺 id 时网关报
19733
- // "tool_result.tool_use_id: Field required"(HTTP 400)。
19734
- ...id ? { id } : {}
19735
- }
19736
- }
19737
- ]);
19738
- }
19739
- }
19740
- flushPendingFunctionCalls();
19741
- if (contents.length > 0 && contents[0]?.role === "model") {
19742
- contents.unshift({
19743
- role: "user",
19744
- parts: [{ text: "Continue the conversation." }]
19745
- });
19746
- }
19747
- return { contents, systemTexts };
19748
- }
19749
- function parseToolArgs(raw) {
19750
- if (typeof raw === "string") {
19751
- try {
19752
- const parsed = JSON.parse(raw);
19753
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
19754
- return parsed;
19755
- }
19756
- } catch {
19757
- }
19758
- }
19759
- if (raw && typeof raw === "object" && !Array.isArray(raw)) {
19760
- return raw;
19761
- }
19762
- return {};
19763
- }
19764
- function sanitizeGeminiSchemaNode(node, active) {
19765
- const out = {};
19766
- if (!node || typeof node !== "object" || Array.isArray(node)) {
19767
- return { type: "string" };
19768
- }
19769
- if (active?.has(node)) {
19770
- return { type: "object", description: "(circular schema reference)" };
19771
- }
19772
- const seen = active ?? /* @__PURE__ */ new WeakSet();
19773
- seen.add(node);
19774
- try {
19775
- return sanitizeGeminiSchemaNodeInner(node, out, seen);
19776
- } finally {
19777
- seen.delete(node);
19778
- }
19779
- }
19780
- function sanitizeGeminiSchemaNodeInner(src, out, seen) {
19781
- if (Array.isArray(src.type)) {
19782
- const firstNonNull = src.type.find((t) => t !== "null");
19783
- if (typeof firstNonNull === "string") out.type = firstNonNull;
19784
- out.nullable = true;
19785
- } else if (typeof src.type === "string") {
19786
- out.type = src.type;
19787
- }
19788
- if (typeof src.description === "string") out.description = src.description;
19789
- if (typeof src.minimum === "number") out.minimum = src.minimum;
19790
- if (typeof src.maximum === "number") out.maximum = src.maximum;
19791
- if (Array.isArray(src.enum) && src.enum.length > 0) out.enum = src.enum;
19792
- if (src.nullable === true) out.nullable = true;
19793
- if ("const" in src && src.const !== void 0 && !out.enum) {
19794
- out.enum = [src.const];
19795
- }
19796
- const variantBranches = [
19797
- ...Array.isArray(src.oneOf) ? src.oneOf : [],
19798
- ...Array.isArray(src.anyOf) ? src.anyOf : []
19799
- ];
19800
- if (variantBranches.length > 0) {
19801
- const mergedProperties = {};
19802
- let requiredIntersection;
19803
- for (const branch of variantBranches) {
19804
- const sanitized = sanitizeGeminiSchemaNode(branch, seen);
19805
- const branchProperties = sanitized.properties ?? {};
19806
- for (const [key, value] of Object.entries(branchProperties)) {
19807
- const previous = mergedProperties[key];
19808
- const prevEnum = previous?.enum;
19809
- const nextEnum = value?.enum;
19810
- if (prevEnum && nextEnum) {
19811
- mergedProperties[key] = {
19812
- ...value,
19813
- enum: [.../* @__PURE__ */ new Set([...prevEnum, ...nextEnum])]
19814
- };
19815
- } else {
19816
- mergedProperties[key] = value;
19817
- }
19818
- }
19819
- const branchRequired = Array.isArray(sanitized.required) ? sanitized.required : [];
19820
- requiredIntersection = requiredIntersection === void 0 ? [...branchRequired] : requiredIntersection.filter((r) => branchRequired.includes(r));
19821
- }
19822
- out.properties = {
19823
- ...out.properties ?? {},
19824
- ...mergedProperties
19825
- };
19826
- if (requiredIntersection !== void 0 && requiredIntersection.length > 0) {
19827
- out.required = requiredIntersection;
19828
- }
19829
- if (typeof out.type !== "string") out.type = "object";
19830
- }
19831
- if (Array.isArray(src.allOf)) {
19832
- for (const branch of src.allOf) {
19833
- const sanitized = sanitizeGeminiSchemaNode(branch, seen);
19834
- out.properties = {
19835
- ...out.properties ?? {},
19836
- ...sanitized.properties ?? {}
19837
- };
19838
- if (Array.isArray(sanitized.required)) {
19839
- out.required = [
19840
- .../* @__PURE__ */ new Set([
19841
- ...out.required ?? [],
19842
- ...sanitized.required ?? []
19843
- ])
19844
- ];
19845
- }
19846
- }
19847
- if (typeof out.type !== "string") out.type = "object";
19848
- }
19849
- if (src.properties && typeof src.properties === "object") {
19850
- const properties = {};
19851
- for (const [key, value] of Object.entries(
19852
- src.properties
19853
- )) {
19854
- properties[key] = sanitizeGeminiSchemaNode(value, seen);
19855
- }
19856
- out.properties = properties;
19857
- if (typeof out.type !== "string") out.type = "object";
19858
- }
19859
- if (Array.isArray(src.required)) {
19860
- const properties = out.properties ?? {};
19861
- const required = [...new Set(src.required)].filter(
19862
- (r) => typeof r === "string" && r in properties
19863
- );
19864
- if (required.length > 0) out.required = required;
19865
- }
19866
- if (src.items !== void 0) {
19867
- out.items = sanitizeGeminiSchemaNode(src.items, seen);
19868
- if (typeof out.type !== "string") out.type = "array";
19869
- }
19870
- if (typeof out.type !== "string") out.type = "object";
19871
- return out;
19872
- }
19873
- function convertOpenAiToolsToGemini(tools) {
19874
- if (!Array.isArray(tools) || tools.length === 0) return void 0;
19875
- const declarations = [];
19876
- for (const tool of tools) {
19877
- if (!tool || typeof tool !== "object" || !("function" in tool)) continue;
19878
- const fn3 = tool.function;
19879
- const name = typeof fn3.name === "string" ? fn3.name : "";
19880
- if (!name) continue;
19881
- declarations.push({
19882
- name,
19883
- description: typeof fn3.description === "string" ? fn3.description : "",
19884
- parameters: sanitizeGeminiSchemaNode(
19885
- fn3.parameters ?? { type: "object", properties: {} }
19886
- )
19887
- });
19888
- }
19889
- if (declarations.length === 0) return void 0;
19890
- return [{ functionDeclarations: declarations }];
19891
- }
19892
- function createGeminiAccumulatorState() {
19893
- return {
19894
- text: "",
19895
- toolCalls: [],
19896
- reasoning: "",
19897
- events: []
19898
- };
19899
- }
19900
- function applyGeminiChunk(chunk, state, options) {
19901
- if (!chunk || typeof chunk !== "object") return;
19902
- const response = "response" in chunk && chunk.response && typeof chunk.response === "object" ? chunk.response : chunk;
19903
- if ("usageMetadata" in response && response.usageMetadata && typeof response.usageMetadata === "object") {
19904
- const meta = response.usageMetadata;
19905
- const prompt = typeof meta.promptTokenCount === "number" ? meta.promptTokenCount : 0;
19906
- const candidates2 = typeof meta.candidatesTokenCount === "number" ? meta.candidatesTokenCount : 0;
19907
- const total = typeof meta.totalTokenCount === "number" ? meta.totalTokenCount : prompt + candidates2;
19908
- const cached3 = typeof meta.cachedContentTokenCount === "number" && Number.isFinite(meta.cachedContentTokenCount) ? (
19909
- // cached ⊆ prompt;对畸形上游防御性钳位,避免 cache_read > input 破坏归一化与计费
19910
- Math.min(Math.max(0, Math.floor(meta.cachedContentTokenCount)), prompt)
19911
- ) : 0;
19912
- state.usage = {
19913
- prompt_tokens: prompt,
19914
- completion_tokens: candidates2,
19915
- total_tokens: total,
19916
- ...cached3 > 0 ? { cache_read_input_tokens: cached3 } : {}
19917
- };
19918
- state.events.push({ kind: "usage", usage: state.usage });
19919
- }
19920
- const candidates = Array.isArray(response.candidates) ? response.candidates : [];
19921
- for (const candidate of candidates) {
19922
- if (!candidate || typeof candidate !== "object") continue;
19923
- const candidateFinishReason = candidate.finishReason;
19924
- if (typeof candidateFinishReason === "string" && candidateFinishReason.trim()) {
19925
- state.finishReason = candidateFinishReason.trim();
19926
- state.events.push({ kind: "finish", reason: state.finishReason });
19927
- }
19928
- const parts2 = Array.isArray(
19929
- candidate.content?.parts
19930
- ) ? candidate.content.parts : [];
19931
- for (const part of parts2) {
19932
- if (!part || typeof part !== "object") continue;
19933
- const partSignature = part.thoughtSignature;
19934
- const isThought = Boolean(part.thought);
19935
- if ("text" in part && typeof part.text === "string") {
19936
- const piece = part.text;
19937
- if (!isThought) {
19938
- state.text += piece;
19939
- if (piece) {
19940
- state.events.push({ kind: "text", text: piece });
19941
- if (options?.onTextDelta) options.onTextDelta(piece);
19942
- }
19943
- } else {
19944
- state.reasoning += piece;
19945
- if (piece) {
19946
- state.events.push({ kind: "reasoning", text: piece });
19947
- if (options?.onReasoningDelta) options.onReasoningDelta(piece);
19948
- }
19949
- }
19950
- }
19951
- if (isThought && typeof partSignature === "string" && partSignature) {
19952
- state.pendingThoughtSignature = partSignature;
19953
- }
19954
- if ("functionCall" in part && part.functionCall) {
19955
- const call = part.functionCall;
19956
- const name = typeof call.name === "string" ? call.name : "tool";
19957
- const id = typeof call.id === "string" ? call.id : `${name}_${state.toolCalls.length}`;
19958
- const argsObj = call.args && typeof call.args === "object" ? call.args : {};
19959
- const resolvedSignature = typeof partSignature === "string" && partSignature ? partSignature : state.pendingThoughtSignature;
19960
- state.pendingThoughtSignature = void 0;
19961
- const toolCall = {
19962
- id,
19963
- type: "function",
19964
- function: { name, arguments: JSON.stringify(argsObj) },
19965
- ...typeof resolvedSignature === "string" && resolvedSignature ? { thought_signature: resolvedSignature } : {}
19966
- };
19967
- state.toolCalls.push(toolCall);
19968
- state.events.push({ kind: "tool_call", toolCall });
19969
- }
19970
- }
19971
- }
19972
- }
19973
- function accumulateGeminiChunks(chunks2, options) {
19974
- const state = createGeminiAccumulatorState();
19975
- for (const chunk of chunks2) {
19976
- applyGeminiChunk(chunk, state, options);
19977
- }
19978
- return {
19979
- text: state.text,
19980
- toolCalls: state.toolCalls,
19981
- usage: state.usage,
19982
- finishReason: state.finishReason,
19983
- reasoningContent: state.reasoning,
19984
- events: state.events
19985
- };
19986
- }
19987
- async function accumulateGeminiStream(stream, options) {
19988
- const state = createGeminiAccumulatorState();
19989
- for await (const chunk of stream) {
19990
- applyGeminiChunk(chunk, state, options);
19991
- }
19992
- return {
19993
- text: state.text,
19994
- toolCalls: state.toolCalls,
19995
- usage: state.usage,
19996
- finishReason: state.finishReason,
19997
- reasoningContent: state.reasoning,
19998
- events: state.events
19999
- };
20000
- }
20001
- function shouldUseGeminiNativeToolRoute(provider, model, tools, isImageModel) {
20002
- return provider === "google" && !isImageModel(model) && isGemini3Model(model) && Array.isArray(tools) && tools.length > 0;
20003
- }
20004
- function buildGeminiGenerateContentRequest(args2) {
20005
- const { contents, systemTexts } = convertOpenAiMessagesToGemini(
20006
- args2.messages,
20007
- { attachSkipThoughtSignature: args2.attachSkipThoughtSignature }
20008
- );
20009
- const request3 = { contents };
20010
- if (systemTexts.length > 0) {
20011
- request3.systemInstruction = {
20012
- role: "user",
20013
- parts: systemTexts.map((text) => ({ text }))
20014
- };
20015
- }
20016
- const toolsResult = convertOpenAiToolsToGemini(args2.tools);
20017
- if (toolsResult) {
20018
- request3.tools = toolsResult;
20019
- request3.toolConfig = { functionCallingConfig: { mode: "VALIDATED" } };
20020
- }
20021
- const generationConfig = {};
20022
- if (typeof args2.maxTokens === "number" && args2.maxTokens > 0) {
20023
- generationConfig.maxOutputTokens = args2.maxTokens;
20024
- }
20025
- if (typeof args2.temperature === "number") {
20026
- generationConfig.temperature = args2.temperature;
20027
- }
20028
- if (Object.keys(generationConfig).length > 0) {
20029
- request3.generationConfig = generationConfig;
20030
- }
20031
- return request3;
20032
- }
20033
-
20034
- // packages/agent-runtime/antigravityCloudCodeProvider.ts
20035
- var STREAM_PATH = "/v1internal:streamGenerateContent?alt=sse";
20036
- function buildCloudCodeAssistPayload(args2) {
20037
- const projectId = readAntigravityProjectId(args2.metadata);
20038
- if (!projectId) {
20039
- throw new Error(
20040
- "Antigravity OAuth credential is missing metadata.projectId. Re-run `nolo auth antigravity`."
20041
- );
20042
- }
20043
- const logicalModel = asOptionalTrimmedString(args2.openAiBody.model) ?? asOptionalTrimmedString(args2.agentConfig.model) ?? "gemini-3.1-pro";
20044
- const { wireModelId: model, profile } = resolveAntigravityWireModel(logicalModel);
20045
- const rawMessages = Array.isArray(args2.openAiBody.messages) ? args2.openAiBody.messages : [];
20046
- const quirks = resolveGeminiModelQuirks(model);
20047
- const isClaude = quirks.isClaudeCrossModel;
20048
- const { contents, systemTexts } = convertOpenAiMessagesToGemini(rawMessages, {
20049
- attachSkipThoughtSignature: quirks.allowsThoughtSignatureSentinel
20050
- });
20051
- if (contents.length === 0) {
20052
- throw new Error("Antigravity Cloud Code Assist request has no user/model contents.");
20053
- }
20054
- const prompt = args2.agentConfig.prompt?.trim();
20055
- if (prompt) systemTexts.unshift(prompt);
20056
- const request3 = { contents };
20057
- if (systemTexts.length > 0) {
20058
- request3.systemInstruction = {
20059
- role: "user",
20060
- parts: systemTexts.map((text) => ({ text }))
20061
- };
20062
- }
20063
- const tools = convertOpenAiToolsToGemini(
20064
- Array.isArray(args2.openAiBody.tools) ? args2.openAiBody.tools : void 0
20065
- );
20066
- if (tools) {
20067
- request3.tools = tools;
20068
- request3.toolConfig = { functionCallingConfig: { mode: "VALIDATED" } };
20069
- }
20070
- const generationConfig = {};
20071
- if (profile?.maxOutputTokens) {
20072
- generationConfig.maxOutputTokens = profile.maxOutputTokens;
20073
- } else if (typeof args2.agentConfig.max_tokens === "number" && args2.agentConfig.max_tokens > 0) {
20074
- generationConfig.maxOutputTokens = args2.agentConfig.max_tokens;
20075
- }
20076
- if (typeof args2.agentConfig.temperature === "number") {
20077
- generationConfig.temperature = args2.agentConfig.temperature;
20078
- }
20079
- if (Object.keys(generationConfig).length > 0) {
20080
- request3.generationConfig = generationConfig;
20081
- }
20082
- const agentId = randomUUID();
20083
- const trajectoryId = randomUUID();
20084
- const step = 2;
20085
- const requestId = `agent/${agentId}/${Date.now()}/${trajectoryId}/${step}`;
20086
- const labels = {
20087
- trajectory_id: trajectoryId,
20088
- last_step_index: String(step - 1),
20089
- used_claude: String(isClaude),
20090
- used_claude_conservative: String(isClaude)
20091
- };
20092
- if (profile?.modelEnum) {
20093
- labels.model_enum = profile.modelEnum;
20094
- }
20095
- request3.labels = labels;
20096
- request3.sessionId = `-${Math.floor(Math.random() * 9e15)}`;
20097
- return {
20098
- url: `${resolveAntigravityCloudCodeBaseUrl(args2.agentConfig.customProviderUrl)}${STREAM_PATH}`,
20099
- envelope: {
20100
- project: projectId,
20101
- model,
20102
- request: request3,
20103
- requestId,
20104
- requestType: "agent",
20105
- userAgent: "antigravity"
20106
- }
20107
- };
20108
- }
20109
- function resolveAntigravityFinishReason(upstream) {
20110
- const normalized = (upstream ?? "").trim().toUpperCase();
20111
- if (normalized === "MAX_TOKENS") return "length";
20112
- if (normalized === "SAFETY" || normalized === "RECITATION" || normalized === "PROHIBITED_CONTENT" || normalized === "BLOCKLIST") {
20113
- return "content_filter";
20114
- }
20115
- return "stop";
20116
- }
20117
- function normalizeAntigravityCompletion(decoded) {
20118
- const { text, toolCalls, usage, finishReason, reasoningContent, events } = decoded;
20119
- const normalizedFinishReason = (finishReason ?? "").trim().toUpperCase();
20120
- if (!text && !reasoningContent && toolCalls.length === 0 && !finishReason && !usage) {
20121
- const body = {
20122
- error: {
20123
- message: "antigravity upstream returned an empty stream (no content, finishReason or usage); channel degradation, not model emptiness"
20124
- },
20125
- provider_events: events
20126
- };
20127
- return {
20128
- status: 502,
20129
- text: "",
20130
- toolCalls: [],
20131
- providerEvents: events,
20132
- body
20133
- };
20134
- }
20135
- if (normalizedFinishReason === "MALFORMED_FUNCTION_CALL") {
20136
- const message2 = "antigravity upstream returned MALFORMED_FUNCTION_CALL (model generated an invalid function call or tool arguments were rejected by upstream)";
20137
- const providerFailure = {
20138
- kind: "provider_failure",
20139
- provider: "antigravity",
20140
- code: "MALFORMED_FUNCTION_CALL",
20141
- message: message2,
20142
- providerReason: "MALFORMED_FUNCTION_CALL",
20143
- retryable: false,
20144
- status: 502
20145
- };
20146
- return {
20147
- status: 502,
20148
- text: "",
20149
- toolCalls: [],
20150
- finishReason,
20151
- providerEvents: events,
20152
- providerFailure,
20153
- runtimeProviderFailure: {
20154
- message: providerFailure.message,
20155
- retryable: providerFailure.retryable
20156
- },
20157
- body: {
20158
- error: {
20159
- message: message2,
20160
- code: "MALFORMED_FUNCTION_CALL",
20161
- category: "malformed_function_call",
20162
- providerReason: "MALFORMED_FUNCTION_CALL",
20163
- retryable: false
20164
- },
20165
- provider_failure: providerFailure,
20166
- provider_events: events
20167
- }
20168
- };
20169
- }
20170
- const message = {
20171
- role: "assistant",
20172
- content: text || null
20173
- };
20174
- if (toolCalls.length > 0) message.tool_calls = toolCalls;
20175
- if (reasoningContent) message.reasoning_content = reasoningContent;
20176
- return {
20177
- status: 200,
20178
- text,
20179
- ...reasoningContent ? { reasoningContent } : {},
20180
- toolCalls,
20181
- ...usage ? { usage } : {},
20182
- ...finishReason ? { finishReason } : {},
20183
- providerEvents: events,
20184
- body: {
20185
- choices: [{
20186
- index: 0,
20187
- message,
20188
- finish_reason: toolCalls.length > 0 ? "tool_calls" : resolveAntigravityFinishReason(finishReason)
20189
- }],
20190
- ...usage ? { usage } : {},
20191
- provider_events: events
20192
- }
20193
- };
20194
- }
20195
- async function fetchAntigravityCloudCodeCompletion(args2) {
20196
- const fetchImpl = args2.fetchImpl ?? fetch;
20197
- const { url, envelope } = buildCloudCodeAssistPayload(args2);
20198
- const response = await fetchImpl(url, {
20199
- method: "POST",
20200
- headers: {
20201
- Authorization: `Bearer ${args2.accessToken}`,
20202
- "Content-Type": "application/json",
20203
- "User-Agent": getAntigravityUserAgent()
20204
- },
20205
- body: JSON.stringify(envelope),
20206
- signal: args2.signal
20207
- });
20208
- if (!response.ok) {
20209
- const errorText = await response.text();
20210
- return {
20211
- status: response.status,
20212
- text: "",
20213
- toolCalls: [],
20214
- providerEvents: [],
20215
- // 保留上游结构:Antigravity 的限流 body 同样携带结构化的重置信息,
20216
- // 压成字符串会让冷却退化成 5 分钟默认值。
20217
- body: parseUpstreamErrorBody(errorText, response.statusText)
20218
- };
20219
- }
20220
- const chunkStream = streamSseDataValues(response, parseSseDataLineJson);
20221
- const { text, toolCalls, usage, finishReason, reasoningContent, events } = await accumulateGeminiStream(chunkStream, {
20222
- onTextDelta: args2.onTextDelta,
20223
- onReasoningDelta: args2.onReasoningDelta
20224
- });
20225
- const normalized = normalizeAntigravityCompletion({
20226
- text,
20227
- toolCalls,
20228
- usage,
20229
- finishReason,
20230
- reasoningContent,
20231
- events
20232
- });
20233
- if (normalized.status !== 200 || !text && toolCalls.length === 0) {
20234
- console.warn(
20235
- `[antigravity] empty completion: finishReason=${JSON.stringify(finishReason)} reasoningLen=${reasoningContent?.length ?? 0} usage=${JSON.stringify(usage) ?? "none"} model=${String(envelope.model)} base=${new URL(url).host}`
20236
- );
20237
- }
20238
- return normalized;
20239
- }
20240
-
20241
20032
  // packages/agent-runtime/devin/devinProvider.ts
20242
- import { randomBytes, randomUUID as randomUUID2 } from "node:crypto";
20033
+ import { randomBytes, randomUUID } from "node:crypto";
20243
20034
  var CLIENT_NAME = "chisel";
20244
20035
  var CLIENT_VERSION = "2026.8.18";
20245
20036
  function writeVarint(fieldNum, value) {
@@ -20301,7 +20092,7 @@ function encodeDevinClientMetadata(token) {
20301
20092
  function encodeDevinChatMessage(msg) {
20302
20093
  const source = msg.role === "assistant" ? 2 : msg.role === "tool" ? 4 : 1;
20303
20094
  return Buffer.concat([
20304
- writeStringField(1, randomUUID2()),
20095
+ writeStringField(1, randomUUID()),
20305
20096
  writeVarint(2, source),
20306
20097
  writeStringField(3, msg.text),
20307
20098
  ...(msg.toolCalls ?? []).map(
@@ -20389,7 +20180,7 @@ ${item.text}`;
20389
20180
  const chatMessageBuffers = coalesced.map((m) => encodeDevinChatMessage(m));
20390
20181
  const completionConfig = buildCompletionConfig({ temperature });
20391
20182
  const modelConfig = Buffer.concat([
20392
- writeStringField(1, randomUUID2()),
20183
+ writeStringField(1, randomUUID()),
20393
20184
  writeVarint(2, 1),
20394
20185
  writeVarint(3, 4)
20395
20186
  ]);
@@ -20401,7 +20192,7 @@ ${item.text}`;
20401
20192
  writeMessageField(8, completionConfig),
20402
20193
  ...tools.map((tool) => writeMessageField(10, encodeDevinToolDefinition(tool))),
20403
20194
  writeMessageField(15, modelConfig),
20404
- writeStringField(16, randomUUID2()),
20195
+ writeStringField(16, randomUUID()),
20405
20196
  writeVarint(20, 1),
20406
20197
  writeStringField(21, model)
20407
20198
  ]);
@@ -20775,8 +20566,6 @@ export {
20775
20566
  LOCAL_AGENT_CONFIG_MISSING_CODE,
20776
20567
  LOCAL_TURN_ABORTED_CODE,
20777
20568
  runLocalAgentTurn,
20778
- parseSseDataLineJson,
20779
- parseSseDataLineObject,
20780
20569
  codexStreamFailureStatus,
20781
20570
  aggregateResponsesStream,
20782
20571
  buildOpenAiCompatibleChatCompletionRequest,
@@ -20792,8 +20581,6 @@ export {
20792
20581
  createHybridRecordStore,
20793
20582
  isDestructiveShellCommand,
20794
20583
  evaluateShellCommandPolicy,
20795
- resolveLocalToolPolicy,
20796
- executeLocalToolWithPolicy,
20797
20584
  filterRetainedLedgerRecords,
20798
20585
  isLineRangeCovered,
20799
20586
  getProcessRegistry,
@@ -20804,17 +20591,14 @@ export {
20804
20591
  internalSearchWorkspace,
20805
20592
  internalListWorkspaceEntries,
20806
20593
  createLocalWorkspaceToolExecutors,
20594
+ buildToolParametersIndex,
20595
+ getAgentRuntimeToolParametersIndex,
20596
+ resolveLocalToolPolicy,
20597
+ executeLocalToolWithPolicy,
20807
20598
  localDialogMessageRecordToRuntimeMessage,
20808
20599
  buildLocalDialogWritePlan,
20809
20600
  buildNoloWorkspaceCliToolExecutors,
20810
20601
  buildLoadSkillExecutor,
20811
- parseUpstreamErrorBody,
20812
- isGemini3Model,
20813
- accumulateGeminiChunks,
20814
- shouldUseGeminiNativeToolRoute,
20815
- buildGeminiGenerateContentRequest,
20816
- resolveAntigravityFinishReason,
20817
- fetchAntigravityCloudCodeCompletion,
20818
20602
  readDevinUpstreamFailure,
20819
20603
  createDevinProvider
20820
20604
  };