nolo-cli 0.18.0 → 0.24.0-alpha.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/README.md +9 -18
  2. package/{_executeModel-X5PCIZN6.js → _executeModel-ZPNYAEFW.js} +46 -44
  3. package/{action-LT7BRFLK.js → action-5MNAXR5B.js} +7 -6
  4. package/{addContentAction-PL3KKBDH.js → addContentAction-UNSYTVTB.js} +5 -5
  5. package/{addDialogAgentAction-FCMSKYAF.js → addDialogAgentAction-LJGNP2AO.js} +5 -5
  6. package/{agentDeleteCommand-2BZDQV36.js → agentDeleteCommand-5ZV4433K.js} +27 -26
  7. package/{agentEmailCommands-2QJDGRMB.js → agentEmailCommands-Z7KOYDVQ.js} +57 -55
  8. package/{agentListCommands-6VEPTKBI.js → agentListCommands-UQUXJYUN.js} +30 -26
  9. package/{agentMachineCommands-NVVHNWYI.js → agentMachineCommands-53O6P6L5.js} +25 -24
  10. package/{agentRecordCommands-4GGRDMD4.js → agentRecordCommands-ZRYNBSSS.js} +52 -50
  11. package/{agentRunCommand-XOLQNKBZ.js → agentRunCommand-B73MKVOG.js} +55 -54
  12. package/{agentRunControl-Y5AFH5PQ.js → agentRunControl-SNUKXN6J.js} +6 -2
  13. package/{agentRuntimeLocal-7WYWCGL4.js → agentRuntimeLocal-GVNERRKQ.js} +21 -18
  14. package/{agentSlice-EAXPGEYV.js → agentSlice-OJBHIL34.js} +5 -5
  15. package/{appDeleteCommands-77KR2RJ4.js → appDeleteCommands-23Z3Z6KE.js} +23 -22
  16. package/{appDeployCommands-K7CAHGBI.js → appDeployCommands-3MFAR4Z7.js} +23 -22
  17. package/{appDeployStatusCommands-4OEKLTBT.js → appDeployStatusCommands-JHU6GSN4.js} +23 -22
  18. package/{appGetCommands-VM2WDLAZ.js → appGetCommands-24J55H2Y.js} +23 -22
  19. package/{appListCommands-GX6BBA6U.js → appListCommands-HE5KP3PH.js} +23 -22
  20. package/{authCommand-DY6UUL6O.js → authCommand-KGRK6MNU.js} +12 -13
  21. package/{authCommands-XTBV7OFZ.js → authCommands-JSOG2H4E.js} +4 -5
  22. package/{chunk-OWOHNGCN.js → chunk-2MBTWINN.js} +42 -3
  23. package/{chunk-MVQ4TBSD.js → chunk-54VHTARN.js} +1 -1
  24. package/{chunk-RQN4SEKL.js → chunk-5DWZ46RJ.js} +1 -1
  25. package/{chunk-VYHDJEQD.js → chunk-5EDKWSQN.js} +136 -78
  26. package/{chunk-Z4NR32XR.js → chunk-5RGU3J7P.js} +13 -0
  27. package/{chunk-7BUYEZ6U.js → chunk-7H3UTGPM.js} +12 -72
  28. package/{chunk-3ZX7PERL.js → chunk-AAWJ5S3G.js} +20 -7
  29. package/{chunk-SCTYAHYK.js → chunk-ADE6577S.js} +237 -142
  30. package/{chunk-PJSV3IZ5.js → chunk-AOPOTHFK.js} +1 -1
  31. package/{chunk-3ND52WIL.js → chunk-ATWUOTJP.js} +180 -24
  32. package/{chunk-SWDPIPOY.js → chunk-AZW5AHMW.js} +1 -1
  33. package/chunk-CF7S2RYZ.js +376 -0
  34. package/{chunk-L4F3I4SR.js → chunk-CV6BM7LU.js} +4 -6
  35. package/{chunk-G5U6LDLG.js → chunk-CWTT2NZ6.js} +6 -6
  36. package/{chunk-MKA6QE5E.js → chunk-CZHE6LLV.js} +135 -55
  37. package/{chunk-KFEVY3ST.js → chunk-D34RI6NI.js} +18 -8
  38. package/{chunk-XRZJGPFI.js → chunk-FON47ACC.js} +4 -4
  39. package/{chunk-K6EUAOW3.js → chunk-FXFGNOT2.js} +67 -1
  40. package/{chunk-LLWKHDG4.js → chunk-G36SUBHA.js} +14 -6
  41. package/{chunk-REA4JLBH.js → chunk-GD23ZX7Z.js} +7 -6
  42. package/{chunk-ROWQEJRP.js → chunk-GE6IWA5N.js} +12 -4
  43. package/{chunk-EDV7EBOI.js → chunk-GGB6MPHD.js} +2155 -1503
  44. package/{chunk-IYEXFJPC.js → chunk-GNGOZMU2.js} +64 -57
  45. package/{chunk-4T5F7YZQ.js → chunk-I6RJGORZ.js} +1 -1
  46. package/{chunk-DFCFMCY6.js → chunk-J2WT5CEO.js} +2 -2
  47. package/{chunk-AHCY2S2I.js → chunk-JJ2WUBFN.js} +1 -1
  48. package/{chunk-UGUW3FHG.js → chunk-JMSCXQUG.js} +2 -2
  49. package/{chunk-VDXCXCXH.js → chunk-KURQBS7S.js} +1 -1
  50. package/{chunk-FQZZLA3C.js → chunk-KXKQO4Q6.js} +17 -0
  51. package/{chunk-3Q7WHGCB.js → chunk-L65QKSVJ.js} +1 -1
  52. package/{chunk-UAIVG4J6.js → chunk-LEHTC2FI.js} +1 -8
  53. package/{chunk-UBHAWFQ6.js → chunk-LLDGWLML.js} +42 -19
  54. package/{chunk-JLA4FXYC.js → chunk-MALVZUYL.js} +1 -1
  55. package/{chunk-N54HIJVJ.js → chunk-MCXQNEJ3.js} +101 -9
  56. package/{chunk-GFUIQ4RY.js → chunk-MXFQHRSS.js} +1 -1
  57. package/{chunk-LSFAWXKC.js → chunk-N7J63Q6C.js} +1 -0
  58. package/{chunk-6Y7A3OQQ.js → chunk-NRY4GCKJ.js} +2 -0
  59. package/{chunk-ELT65PZW.js → chunk-OXLLFZNF.js} +1 -1
  60. package/{chunk-YGW4G5W5.js → chunk-P3RZ6DHN.js} +133 -25
  61. package/{chunk-5MIDPSSK.js → chunk-PB7G5MAM.js} +3 -3
  62. package/{chunk-HEGV7BH3.js → chunk-PMCKFQYC.js} +1 -1
  63. package/{chunk-23FZYPLC.js → chunk-PTKI7TIN.js} +911 -1064
  64. package/{chunk-MHSGJW4Z.js → chunk-PXPISYEH.js} +44 -14
  65. package/{chunk-I2ZJKTG5.js → chunk-PY7VGC5M.js} +29 -10
  66. package/{chunk-PIZYJVHQ.js → chunk-RM464B7I.js} +4 -4
  67. package/{chunk-XHG22OEX.js → chunk-ROJI6DRT.js} +2 -2
  68. package/{chunk-VNAEBGDK.js → chunk-RZNYL4LW.js} +123 -26
  69. package/{chunk-RVOUXDFU.js → chunk-S6KVFOUG.js} +6 -34
  70. package/{chunk-BV4VEE3W.js → chunk-S7AV3NCK.js} +277 -357
  71. package/{chunk-R35XMF6F.js → chunk-SIUEKVK4.js} +484 -67
  72. package/{chunk-B5TOXZAK.js → chunk-SIZQB7BD.js} +1 -1
  73. package/{chunk-DFFIZ25G.js → chunk-SLOGKB7M.js} +4 -1
  74. package/{chunk-MPVYDS7I.js → chunk-TLYHUIWF.js} +3 -3
  75. package/{chunk-HWW2M4SN.js → chunk-TNM27YZ7.js} +5 -4
  76. package/{chunk-OK6YSWLH.js → chunk-TWA4L7J7.js} +1 -1
  77. package/chunk-UZ6XXCVR.js +50 -0
  78. package/{chunk-DKZFOK24.js → chunk-WCP5FDTF.js} +16 -304
  79. package/{chunk-TRT4I4UD.js → chunk-WSQLK2KR.js} +2 -2
  80. package/{chunk-FXUSIQC2.js → chunk-XNZJVYFS.js} +1 -1
  81. package/{chunk-V3ED5AWK.js → chunk-YJY7HODU.js} +27 -16
  82. package/chunk-YYLHWIW6.js +48 -0
  83. package/{chunk-CZ6NYQTV.js → chunk-ZFL3LTU7.js} +2 -2
  84. package/{chunk-34WPV75U.js → chunk-ZUXIXYJI.js} +1 -1
  85. package/{chunk-VC6OZXO5.js → chunk-ZWJOGMKH.js} +2 -26
  86. package/{cliExecutor-DNE7TCC4.js → cliExecutor-K3F7DMRO.js} +2 -2
  87. package/{codingSkills-36KN5NQT.js → codingSkills-EN6Z6G6U.js} +4 -4
  88. package/{createAgentAutomationAction-GNFF524X.js → createAgentAutomationAction-PO62NUR7.js} +19 -18
  89. package/{createDialogAction-RB55KCLO.js → createDialogAction-ORFRYI3C.js} +22 -20
  90. package/{createPageAction-Y7343GLU.js → createPageAction-XWVMNTYH.js} +20 -19
  91. package/{db-W3O47YGF.js → db-W5UHK4P3.js} +150 -0
  92. package/{dbSlice-CGSRFJN3.js → dbSlice-5BKDURDL.js} +4 -4
  93. package/{dialogCommands-IAZK6YHQ.js → dialogCommands-XTJHROXM.js} +24 -23
  94. package/{dialogSlice-UQAXF23P.js → dialogSlice-DJXC53YW.js} +24 -18
  95. package/{docCreateCommands-I2EWZGCI.js → docCreateCommands-T2XRVXYS.js} +24 -27
  96. package/{docDeleteCommands-IZ45UZKG.js → docDeleteCommands-L7RXFXY3.js} +25 -24
  97. package/{docReadCommands-R6Z4QJYU.js → docReadCommands-M5PYNADQ.js} +24 -23
  98. package/{docStore-5HOJ4D7D.js → docStore-6LKLLFDW.js} +20 -19
  99. package/{docUpdateCommands-2LRBKQPY.js → docUpdateCommands-OA5EGPLQ.js} +24 -27
  100. package/{fetchMessages-BMGVZVSE.js → fetchMessages-W4IKHPCS.js} +2 -3
  101. package/{getPricing-G7DLAST6.js → getPricing-6C3C47Y4.js} +3 -3
  102. package/{handleSendMessageAction-Q4HY35WX.js → handleSendMessageAction-ILFGZTN5.js} +23 -20
  103. package/index.js +74 -74
  104. package/{localDialogRead-4H3QJFLW.js → localDialogRead-JXTAHW2E.js} +2 -2
  105. package/{machineCommands-PKBPQKTY.js → machineCommands-AKAQGPAO.js} +59 -57
  106. package/{machineWsRunDispatch-UZAW4OXJ.js → machineWsRunDispatch-RSBERM64.js} +51 -49
  107. package/{models-DJFSJ7SW.js → models-ILWKIX2Y.js} +2 -2
  108. package/package.json +1 -1
  109. package/{read-3FMPZPER.js → read-F3L3T2WJ.js} +2 -1
  110. package/{readDocTool-VSYHKK3V.js → readDocTool-FOHWVA4R.js} +1 -1
  111. package/{readlineWorkspace-Y2RDOFZG.js → readlineWorkspace-TUH3SFHO.js} +937 -430
  112. package/{removeDialogAgentAction-OAGA2G3U.js → removeDialogAgentAction-QCFGIUIH.js} +8 -7
  113. package/{runAgentClientLoop-NBPIKJ6J.js → runAgentClientLoop-C3QB2EXV.js} +45 -38
  114. package/{setDialogExtraReferencesAction-JZEXA5SL.js → setDialogExtraReferencesAction-DK4CKERU.js} +5 -5
  115. package/{setPrimaryDialogAgentAction-4ZUQQKDG.js → setPrimaryDialogAgentAction-SJSO6N27.js} +8 -7
  116. package/{settingSlice-7MNKHZ6R.js → settingSlice-XNM5AFPU.js} +6 -5
  117. package/{spaceCommands-DHCZ7J5I.js → spaceCommands-SXYOFGUJ.js} +25 -24
  118. package/{spaceSlice-ZXLLG3CR.js → spaceSlice-6BHMRP5O.js} +21 -18
  119. package/{streamAgentChatTurn-L3YW6LSB.js → streamAgentChatTurn-GF5W5OIP.js} +140 -98
  120. package/switchDialogAgentAction-4OAC6QRN.js +61 -0
  121. package/{tableCommands-BEQAGE4I.js → tableCommands-QSZILI7I.js} +1 -1
  122. package/{toolApiClient-QI3YBNWB.js → toolApiClient-ISM4ZQX4.js} +2 -2
  123. package/{tools-ECGXR5QF.js → tools-KBCLJ43G.js} +36 -33
  124. package/{tuiSummaryLlmCaller-OMSCNHXA.js → tuiSummaryLlmCaller-ARIMGILF.js} +6 -6
  125. package/{updateCommands-XTW55TOT.js → updateCommands-AK2RDOFR.js} +8 -2
  126. package/{updateDialogTitleAction-HQ2EUDFT.js → updateDialogTitleAction-LMHZUY4Q.js} +28 -27
  127. package/chunk-DL6EPNIK.js +0 -50
  128. package/chunk-V5R4NZP2.js +0 -78
  129. package/chunk-X4Q4UZEY.js +0 -26
  130. package/queryRunOverlay-O4LOMMPO.js +0 -161
  131. package/runOverlayPresentation-ZO4VCWEU.js +0 -11
  132. package/types-ZSTJ7MPR.js +0 -7
  133. package/{chunk-X7W4TNB4.js → chunk-CMKUBIGW.js} +3 -3
  134. package/{runtimeDoctorCommands-E2OUORHK.js → runtimeDoctorCommands-HSU632DY.js} +3 -3
@@ -4,45 +4,50 @@ import {
4
4
  providerHttpFailure,
5
5
  readSseFrames,
6
6
  throwIfChatCompletionStreamFailed
7
- } from "./chunk-N54HIJVJ.js";
7
+ } from "./chunk-MCXQNEJ3.js";
8
8
  import {
9
9
  parseToolArgumentsJson
10
- } from "./chunk-YGW4G5W5.js";
10
+ } from "./chunk-P3RZ6DHN.js";
11
11
  import {
12
12
  createThinkParserState,
13
13
  createToolCallAccumulator,
14
14
  createToolCallTextParserState,
15
15
  extractThinkContent,
16
16
  finalizeAccumulatedToolCalls
17
- } from "./chunk-LSFAWXKC.js";
17
+ } from "./chunk-N7J63Q6C.js";
18
18
  import {
19
19
  requiresBareImageUrl,
20
20
  toBareImageUrlMessages
21
- } from "./chunk-34WPV75U.js";
21
+ } from "./chunk-ZUXIXYJI.js";
22
22
  import {
23
23
  shouldStripReasoningContentForOutbound,
24
24
  toOpenAiCompatibleMessages
25
- } from "./chunk-KFEVY3ST.js";
25
+ } from "./chunk-D34RI6NI.js";
26
26
  import {
27
27
  buildProviderAuthHeaders,
28
28
  buildProviderExecutionPlan
29
- } from "./chunk-MHSGJW4Z.js";
29
+ } from "./chunk-PXPISYEH.js";
30
30
  import {
31
- createGlob,
32
- resolveExecutableOnPath
33
- } from "./chunk-DKZFOK24.js";
31
+ NOLO_WORKSPACE_TOOL_NAMES,
32
+ buildNoloWorkspaceCommandArgs,
33
+ noloStringArg,
34
+ parseNoloWorkspaceToolArguments
35
+ } from "./chunk-WCP5FDTF.js";
34
36
  import {
35
- toErrorMessage
36
- } from "./chunk-HNMXYYJR.js";
37
+ buildDialogFallbackTitleFromUserInput
38
+ } from "./chunk-2MBTWINN.js";
37
39
  import {
38
40
  canonicalizeToolName
39
41
  } from "./chunk-ZZ2MH7LE.js";
42
+ import {
43
+ toErrorMessage
44
+ } from "./chunk-HNMXYYJR.js";
40
45
  import {
41
46
  asRecordOrEmpty
42
47
  } from "./chunk-KX6YBIHG.js";
43
48
  import {
44
49
  dialogMessageKey
45
- } from "./chunk-6Y7A3OQQ.js";
50
+ } from "./chunk-NRY4GCKJ.js";
46
51
  import {
47
52
  asOptionalFiniteNumber
48
53
  } from "./chunk-WWC7I6TA.js";
@@ -68,6 +73,44 @@ import {
68
73
  asOptionalTrimmedString
69
74
  } from "./chunk-G3SUCT4W.js";
70
75
 
76
+ // packages/agent-runtime/agentAvailability.ts
77
+ var DEFAULT_PROVIDER_RETRY_MS = 5 * 60 * 1e3;
78
+ function parseResetText(value) {
79
+ const m = value.match(/(?:resets?\s+in|retry[- ]after)\s*(?:(\d+)\s*hr)?\s*(?:(\d+)\s*min)?\s*(?:(\d+)\s*sec)?/i);
80
+ if (!m) return void 0;
81
+ return (Number(m[1] ?? 0) * 3600 + Number(m[2] ?? 0) * 60 + Number(m[3] ?? 0)) * 1e3;
82
+ }
83
+ function resolveAgentNextAvailableAt(body, now = Date.now()) {
84
+ const root = body && typeof body === "object" ? body : {};
85
+ const error = root.error && typeof root.error === "object" ? root.error : root;
86
+ const details = error.details && typeof error.details === "object" ? error.details : void 0;
87
+ const retryRaw = error.retryAfter ?? error.retry_after ?? error["Retry-After"];
88
+ if (typeof retryRaw === "number" && Number.isFinite(retryRaw)) {
89
+ return retryRaw > 1e12 ? retryRaw : now + Math.max(0, retryRaw) * 1e3;
90
+ }
91
+ if (typeof retryRaw === "string") {
92
+ const numeric = Number(retryRaw.trim());
93
+ if (Number.isFinite(numeric)) return now + Math.max(0, numeric) * 1e3;
94
+ const date = Date.parse(retryRaw);
95
+ if (Number.isFinite(date) && date > now) return date;
96
+ }
97
+ const resetRaw = error.resets_at ?? error.resetsAt ?? details?.resets_at ?? details?.resetsAt;
98
+ if (typeof resetRaw === "number" && Number.isFinite(resetRaw)) {
99
+ const absolute = resetRaw > 1e12 ? resetRaw : resetRaw * 1e3;
100
+ if (absolute > now) return absolute;
101
+ }
102
+ if (typeof resetRaw === "string") {
103
+ const parsed = Date.parse(resetRaw);
104
+ if (Number.isFinite(parsed) && parsed > now) return parsed;
105
+ }
106
+ const text = typeof body === "string" ? body : JSON.stringify(body ?? "");
107
+ return now + (parseResetText(text) ?? DEFAULT_PROVIDER_RETRY_MS);
108
+ }
109
+ function isAgentCoolingDown(agent, now = Date.now()) {
110
+ const at = agent && typeof agent === "object" ? agent.nextAvailableAt : void 0;
111
+ return typeof at === "number" && Number.isFinite(at) && at > now;
112
+ }
113
+
71
114
  // packages/agent-runtime/kimiUserAgent.ts
72
115
  var KIMI_CODE_CLI_VERSION = "0.29.2";
73
116
  var KIMI_CODE_USER_AGENT = `kimi-code-cli/${KIMI_CODE_CLI_VERSION} (nolo)`;
@@ -443,21 +486,37 @@ function extractLastUserText(messages) {
443
486
  return "";
444
487
  }
445
488
  function resolveDialogTitle(args) {
446
- if (typeof args.existingDialog?.title === "string" && args.existingDialog.title.trim()) {
447
- return args.existingDialog.title;
489
+ const existing = args.existingDialog?.title;
490
+ if (typeof existing === "string" && existing.trim()) {
491
+ return existing.trim();
448
492
  }
449
493
  const lastUserText = extractLastUserText(args.messages).trim();
450
- return lastUserText ? lastUserText.slice(0, 80) : "Local agent run";
494
+ return buildDialogFallbackTitleFromUserInput(lastUserText) || "Local agent run";
451
495
  }
452
496
  function pickDialogTitle(args) {
453
- const existing = args.existingDialog?.title;
454
- if (typeof existing === "string" && existing.trim()) return existing.trim();
497
+ const existingSource = args.existingDialog?.titleSource;
498
+ const existingIsManual = existingSource === "manual";
455
499
  const override = args.titleOverride?.trim();
456
- if (override) return override;
457
- return resolveDialogTitle({
458
- existingDialog: args.existingDialog,
459
- messages: args.messages
460
- });
500
+ if (override && !existingIsManual) {
501
+ return { title: override, titleSource: "generated" };
502
+ }
503
+ const existing = args.existingDialog?.title;
504
+ if (typeof existing === "string" && existing.trim()) {
505
+ return {
506
+ title: existing.trim(),
507
+ titleSource: existingIsManual ? "manual" : "generated"
508
+ };
509
+ }
510
+ if (override) {
511
+ return { title: override, titleSource: "generated" };
512
+ }
513
+ return {
514
+ title: resolveDialogTitle({
515
+ existingDialog: args.existingDialog,
516
+ messages: args.messages
517
+ }),
518
+ titleSource: "generated"
519
+ };
461
520
  }
462
521
  function normalizeSubjectRef(ref) {
463
522
  if (!ref || typeof ref !== "object") return null;
@@ -472,6 +531,15 @@ function normalizeSubjectRef(ref) {
472
531
  ...role ? { role } : {}
473
532
  };
474
533
  }
534
+ function parseTitleUpdatedAtMs(existingDialog) {
535
+ const raw = existingDialog?.titleUpdatedAt;
536
+ if (typeof raw === "number" && !Number.isNaN(raw)) return raw;
537
+ if (typeof raw === "string" && raw.trim()) {
538
+ const parsed = Date.parse(raw);
539
+ if (!Number.isNaN(parsed)) return parsed;
540
+ }
541
+ return 0;
542
+ }
475
543
  function mergeSubjectRefs(...groups) {
476
544
  const refs = [];
477
545
  const seen = /* @__PURE__ */ new Set();
@@ -550,6 +618,14 @@ function buildAgentRuntimeDialogWritePlan(args) {
550
618
  cybots: [args.input.agentKey],
551
619
  primaryAgentKey: args.input.agentKey
552
620
  };
621
+ const pickedTitle = pickDialogTitle({
622
+ existingDialog: args.existingDialog,
623
+ titleOverride: args.titleOverride,
624
+ messages: args.input.messages
625
+ });
626
+ const existingTitleUpdatedAtMs = parseTitleUpdatedAtMs(args.existingDialog);
627
+ const titleChanged = typeof args.titleOverride === "string" && args.titleOverride.trim().length > 0;
628
+ const titleUpdatedAtIso = titleChanged ? nowIso : existingTitleUpdatedAtMs > 0 ? new Date(existingTitleUpdatedAtMs).toISOString() : args.existingDialog?.createdAt ?? nowIso;
553
629
  const dialogRecord = {
554
630
  ...asRecordOrEmpty(args.existingDialog),
555
631
  id: dialogId,
@@ -557,11 +633,9 @@ function buildAgentRuntimeDialogWritePlan(args) {
557
633
  type: "dialog",
558
634
  userId: args.userId,
559
635
  ...dialogAgentFields,
560
- title: pickDialogTitle({
561
- existingDialog: args.existingDialog,
562
- titleOverride: args.titleOverride,
563
- messages: args.input.messages
564
- }),
636
+ title: pickedTitle.title,
637
+ titleSource: pickedTitle.titleSource,
638
+ titleUpdatedAt: titleUpdatedAtIso,
565
639
  status: args.input.result.error === true ? "failed" : "done",
566
640
  triggerType: `${args.runtimeHost}-local`,
567
641
  executionMode: args.existingDialog?.executionMode ?? "foreground",
@@ -687,6 +761,11 @@ function shouldCacheHybridRemoteRecord(remoteRecord, localRecord) {
687
761
  if (!localRecord || typeof localRecord !== "object") return true;
688
762
  return shouldReplaceWithHybridRecord(remoteRecord, localRecord);
689
763
  }
764
+ function parseSyncServersEnv(env) {
765
+ const raw = env?.NOLO_SYNC_SERVERS?.trim();
766
+ if (!raw) return [];
767
+ return raw.split(",").map((value) => value.trim()).filter((value) => value.length > 0);
768
+ }
690
769
  function createHybridRecordStore(deps) {
691
770
  const fetchImpl = deps.fetchImpl ?? fetch;
692
771
  return {
@@ -1124,11 +1203,185 @@ async function executeLocalToolWithPolicy(args) {
1124
1203
  }
1125
1204
 
1126
1205
  // packages/agent-runtime/localWorkspaceTools.ts
1127
- import { existsSync } from "node:fs";
1206
+ import { existsSync as existsSync2 } from "node:fs";
1128
1207
  import { mkdir, readdir, readFile, stat, writeFile } from "node:fs/promises";
1129
1208
  import { dirname, relative, resolve, sep } from "node:path";
1130
1209
  import { spawnSync, spawn as spawnChildProcess } from "node:child_process";
1131
1210
 
1211
+ // packages/agent-runtime/runtimeCompat.ts
1212
+ import { existsSync, readdirSync, statSync } from "node:fs";
1213
+ import { join } from "node:path";
1214
+ import { spawn } from "node:child_process";
1215
+ var IS_WINDOWS = process.platform === "win32";
1216
+ function isExecutableFile(candidate) {
1217
+ if (!existsSync(candidate)) return false;
1218
+ try {
1219
+ return statSync(candidate).isFile();
1220
+ } catch {
1221
+ return false;
1222
+ }
1223
+ }
1224
+ function resolveExecutableOnPath(name) {
1225
+ if (IS_WINDOWS && !/\.[a-z]+$/i.test(name)) {
1226
+ const exts = (process.env.PATHEXT || ".EXE;.CMD;.BAT").split(";").filter(Boolean);
1227
+ for (const ext of exts) {
1228
+ const found = resolveExecutableOnPath(name + ext);
1229
+ if (found) return found;
1230
+ }
1231
+ return null;
1232
+ }
1233
+ const pathEnv = process.env.PATH ?? "";
1234
+ const sep2 = pathEnv.includes(";") && IS_WINDOWS ? ";" : ":";
1235
+ const pathDirs = pathEnv.split(sep2).filter(Boolean);
1236
+ const fallbackDirs = IS_WINDOWS ? [] : [
1237
+ "/opt/homebrew/bin",
1238
+ "/usr/local/bin",
1239
+ "/usr/bin",
1240
+ "/bin",
1241
+ `${process.env.HOME ?? ""}/.local/bin`
1242
+ ].filter(Boolean);
1243
+ for (const dir of [...pathDirs, ...fallbackDirs]) {
1244
+ const candidate = join(dir, name);
1245
+ if (isExecutableFile(candidate)) return candidate;
1246
+ }
1247
+ return null;
1248
+ }
1249
+ function createGlob(pattern) {
1250
+ const regex = globToRegExp(pattern);
1251
+ return {
1252
+ match(path) {
1253
+ return regex.test(path);
1254
+ },
1255
+ scanSync(opts) {
1256
+ const dot = opts.dot ?? false;
1257
+ const results = [];
1258
+ walkDir(opts.cwd, "", dot, results);
1259
+ return results.filter((rel) => regex.test(rel));
1260
+ }
1261
+ };
1262
+ }
1263
+ function walkDir(dir, prefix, dot, out) {
1264
+ let entries;
1265
+ try {
1266
+ entries = readdirSync(dir, { withFileTypes: true, encoding: "utf8" });
1267
+ } catch {
1268
+ return;
1269
+ }
1270
+ for (const entry of entries) {
1271
+ const name = String(entry.name);
1272
+ if (!dot && name.startsWith(".")) continue;
1273
+ const rel = prefix ? `${prefix}/${name}` : name;
1274
+ const full = join(dir, name);
1275
+ if (entry.isDirectory()) {
1276
+ walkDir(full, rel, dot, out);
1277
+ } else if (entry.isFile()) {
1278
+ out.push(rel);
1279
+ }
1280
+ }
1281
+ }
1282
+ function globToRegExp(pattern) {
1283
+ const trailingDir = pattern.endsWith("/");
1284
+ const src = trailingDir ? pattern.slice(0, -1) : pattern;
1285
+ const anchored = src.startsWith("/");
1286
+ const body = anchored ? src.slice(1) : src;
1287
+ let regex = "^";
1288
+ if (!anchored) regex += "(?:.*/)?";
1289
+ regex += convertBody(body);
1290
+ if (trailingDir) {
1291
+ regex += "(?:/.*)?";
1292
+ }
1293
+ regex += "$";
1294
+ return new RegExp(regex);
1295
+ }
1296
+ function convertBody(p) {
1297
+ let out = "";
1298
+ let i = 0;
1299
+ while (i < p.length) {
1300
+ const c = p[i];
1301
+ if (c === "*") {
1302
+ if (p[i + 1] === "*") {
1303
+ i += 2;
1304
+ if (p[i] === "/") {
1305
+ out += "(?:.*/)?";
1306
+ i += 1;
1307
+ } else {
1308
+ out += ".*";
1309
+ }
1310
+ } else {
1311
+ out += "[^/]*";
1312
+ i += 1;
1313
+ }
1314
+ } else if (c === "?") {
1315
+ out += "[^/]";
1316
+ i += 1;
1317
+ } else if (c === "[") {
1318
+ out += "[";
1319
+ i += 1;
1320
+ if (p[i] === "!") {
1321
+ out += "^";
1322
+ i += 1;
1323
+ }
1324
+ while (i < p.length && p[i] !== "]") {
1325
+ out += p[i] === "\\" ? "\\\\" : p[i];
1326
+ i += 1;
1327
+ }
1328
+ if (p[i] === "]") {
1329
+ out += "]";
1330
+ i += 1;
1331
+ }
1332
+ } else if (c === "{") {
1333
+ let depth = 1;
1334
+ let j = i + 1;
1335
+ let bodyStr = "";
1336
+ while (j < p.length && depth > 0) {
1337
+ if (p[j] === "{") depth += 1;
1338
+ else if (p[j] === "}") depth -= 1;
1339
+ if (depth > 0) bodyStr += p[j];
1340
+ j += 1;
1341
+ }
1342
+ const alts = bodyStr.split(",").map((alt) => convertBody(alt));
1343
+ out += `(?:${alts.join("|")})`;
1344
+ i = j;
1345
+ } else {
1346
+ out += escapeRegexChar(c);
1347
+ i += 1;
1348
+ }
1349
+ }
1350
+ return out;
1351
+ }
1352
+ function escapeRegexChar(c) {
1353
+ if ("\\^$.|+()".includes(c)) {
1354
+ return `\\${c}`;
1355
+ }
1356
+ return c;
1357
+ }
1358
+ function spawnToWebStreams(options) {
1359
+ const [command, ...args] = options.cmd;
1360
+ const child = spawn(command, args, {
1361
+ env: options.env,
1362
+ stdio: ["ignore", "pipe", "pipe"]
1363
+ });
1364
+ const exited = new Promise((resolve2) => {
1365
+ child.on("exit", (code) => resolve2(code ?? 1));
1366
+ child.on("error", () => resolve2(127));
1367
+ });
1368
+ const toWeb = (stream) => {
1369
+ if (!stream) return null;
1370
+ return stream.toWeb?.() ?? new ReadableStream({
1371
+ start(controller) {
1372
+ stream.on("data", (chunk) => controller.enqueue(new Uint8Array(chunk)));
1373
+ stream.on("end", () => controller.close());
1374
+ stream.on("error", (e) => controller.error(e));
1375
+ }
1376
+ });
1377
+ };
1378
+ return {
1379
+ stdout: toWeb(child.stdout),
1380
+ stderr: toWeb(child.stderr),
1381
+ exited
1382
+ };
1383
+ }
1384
+
1132
1385
  // packages/agent-runtime/processRegistry.ts
1133
1386
  var ProcessRegistry = class {
1134
1387
  processes = /* @__PURE__ */ new Map();
@@ -1228,7 +1481,8 @@ function tokenizeShellPrefix(command) {
1228
1481
  function buildWorkspacePathProperty() {
1229
1482
  return {
1230
1483
  type: "string",
1231
- description: "Path relative to the workspace root."
1484
+ minLength: 1,
1485
+ description: "Path relative to the workspace root. Required and must be non-empty for readFile/writeFile/editFile; for listFiles/searchFiles/globFiles it is optional and defaults to the workspace root when omitted."
1232
1486
  };
1233
1487
  }
1234
1488
  function buildListWorkspaceDescription(variant) {
@@ -1236,12 +1490,12 @@ function buildListWorkspaceDescription(variant) {
1236
1490
  return "List files and directories inside a workspace directory.";
1237
1491
  }
1238
1492
  if (variant === "workflow") {
1239
- return "List a bounded directory overview inside the workspace. Use first when you need to understand nearby structure, choose a subdirectory, or inspect a non-code project. Then use globFiles for path-pattern discovery, searchFiles for text/content discovery, and readFile only for the specific files or line ranges you need.";
1493
+ return "List directory overview inside workspace. Then use globFiles for path patterns, searchFiles for text discovery, and readFile for content.";
1240
1494
  }
1241
1495
  if (variant === "antiShell") {
1242
- return "List a bounded directory overview inside the workspace. Do not use execShell with ls/find/tree for normal directory overviews when listFiles can show the needed depth and limit. Use globFiles for path-pattern discovery and searchFiles for content search.";
1496
+ return "List directory overview inside workspace. Prefer listFiles over shell ls/find commands.";
1243
1497
  }
1244
- return "List a bounded directory overview inside the workspace. Use for scanning nearby structure, not for path-pattern discovery across the repo. Prefer globFiles for finding files by name/extension and searchFiles for text inside files.";
1498
+ return "List files and directories inside a workspace folder. Defaults to shallow overview (depth 1).";
1245
1499
  }
1246
1500
  function buildListWorkspaceParameters(variant) {
1247
1501
  const path = buildWorkspacePathProperty();
@@ -1298,18 +1552,18 @@ function buildReadWorkspaceDescription(variant) {
1298
1552
  return "Read a UTF-8 text file inside the workspace.";
1299
1553
  }
1300
1554
  if (variant === "workflow") {
1301
- return 'Read a UTF-8 text file inside the workspace after listFiles, globFiles, or searchFiles has narrowed the target. Use lines with a range from searchFiles matches, or lines: "-50" for logs and generated output. Read the whole file only when the task truly needs all content or exact edit context.';
1555
+ return 'Read a UTF-8 text file inside the workspace. Use lines with a range from searchFiles matches, or lines: "-50" for logs. Read the whole file only when the task needs all content.';
1302
1556
  }
1303
1557
  if (variant === "antiShell") {
1304
- return "Read a UTF-8 text file inside the workspace. Do not use execShell with cat/sed/head/tail for normal text reads when readFile can return the needed file or line range. Use searchFiles first for content search, then readFile only the relevant lines when possible.";
1558
+ return "Read a UTF-8 text file inside the workspace. Prefer readFile over shell commands (cat/head/tail).";
1305
1559
  }
1306
- return "Read a UTF-8 text file inside the workspace. Read before editing when you need exact text for editFile. For discovery or classification tasks, avoid batch-reading every candidate; report candidate paths first, or read only one to three representative files with a small lines count. Use lines ranges for large code, docs, data, configs, and logs after searchFiles returns line numbers. Read the whole file only when the task truly needs all content or exact edit context.";
1560
+ return "Read a UTF-8 text file inside the workspace. Use lines for focused range reads after searchFiles to save tokens.";
1307
1561
  }
1308
1562
  function buildReadWorkspaceParameters(variant) {
1309
1563
  const path = buildWorkspacePathProperty();
1310
1564
  const lines = {
1311
1565
  type: "string",
1312
- description: 'Which lines to return: "40-120" for a range (1-based, inclusive), "120-" from a line to the end, "-50" for the last N lines (logs, generated output), "50" for the first N. Omit to read the whole file. Use a range after searchFiles gives a line number, and a small count to preview candidates instead of reading many whole files. When the user gives a read/preview budget, each readFile preview consumes one budget slot.'
1566
+ description: 'Line slice: "40-120" (range, 1-based, inclusive), "120-" (from line to end), "-50" (tail N lines), "50" (head N lines). Omit to read full file.'
1313
1567
  };
1314
1568
  if (variant === "minimal") {
1315
1569
  return {
@@ -1339,7 +1593,7 @@ function buildWriteWorkspaceFileTool() {
1339
1593
  type: "function",
1340
1594
  function: {
1341
1595
  name: "writeFile",
1342
- description: "Write full UTF-8 file content inside the workspace. For new files or deliberate whole-file rewrites only. For existing files, prefer editFile for targeted edits. Warn that whole-file rewrites can cause line-ending churn.",
1596
+ description: "Write full UTF-8 file content inside the workspace (new files or whole-file rewrites). Prefer editFile for targeted edits.",
1343
1597
  parameters: {
1344
1598
  type: "object",
1345
1599
  properties: {
@@ -1359,7 +1613,7 @@ function buildReplaceWorkspaceTextTool() {
1359
1613
  type: "function",
1360
1614
  function: {
1361
1615
  name: "editFile",
1362
- description: "Use for small, exact edits in one workspace file without constructing a patch. Read the file first when you need the exact oldText; set expectedReplacements to avoid accidental broad edits. When expected replacement count fails, report a blocker instead of falling back to a full-file rewrite.",
1616
+ description: "Replace exact text occurrences in a workspace file. If expected replacement count fails, report the error instead of falling back to a whole-file rewrite.",
1363
1617
  parameters: {
1364
1618
  type: "object",
1365
1619
  properties: {
@@ -1387,35 +1641,35 @@ function buildSearchWorkspaceDescription(variant) {
1387
1641
  return "Search workspace file contents.";
1388
1642
  }
1389
1643
  if (variant === "workflow") {
1390
- return "Search file contents inside the current workspace after listFiles or globFiles has scoped the likely area, or directly when the task names text to find. Use for code, docs, data, configs, logs, and other text files. Return line numbers, then use readFile with focused ranges for interpretation or editing.";
1644
+ return "Search file contents inside workspace. Returns line numbers; use readFile with line ranges for focused reading.";
1391
1645
  }
1392
1646
  if (variant === "antiShell") {
1393
- return "Search text inside workspace files for code, docs, data, configs, logs, or other text-like assets. Do not use execShell for content search when searchFiles can directly find matching lines. Use globFiles instead for path-only discovery, then readFile for exact surrounding content when needed.";
1647
+ return "Search text inside workspace files using ripgrep. Prefer searchFiles over shell grep.";
1394
1648
  }
1395
- return "Search file contents inside the current workspace using ripgrep when available. Use for grep-like text or regex search across code, docs, data, configs, logs, and other text files. Use globFiles instead when you only need to find files by name or extension before readFile.";
1649
+ return "Search file contents inside the workspace using ripgrep. Returns matching lines and line numbers. Prefer globFiles when only finding file paths.";
1396
1650
  }
1397
1651
  function buildSearchWorkspaceParameters(variant) {
1398
1652
  const query = {
1399
1653
  type: "string",
1400
- description: "Search query. Treated as a regular expression unless literal is true."
1654
+ description: "Search query (regex or literal string)."
1401
1655
  };
1402
1656
  const path = buildWorkspacePathProperty();
1403
1657
  const includeIgnored = {
1404
1658
  type: "boolean",
1405
- description: "When true, search files ignored by .gitignore such as .tmp. Defaults to false. Build artifacts (dist/, *.tsbuildinfo, *.min.js, etc.) are always excluded regardless of this flag. .git and node_modules remain excluded."
1659
+ description: "When true, search gitignored files. Build artifacts (dist/, node_modules, .git) remain excluded."
1406
1660
  };
1407
1661
  const exclude = {
1408
1662
  type: "array",
1409
1663
  items: { type: "string" },
1410
- description: "Glob patterns to exclude from content search, for example ['dist/**', 'build/**', 'exports/**', '*.log']."
1664
+ description: "Glob patterns to exclude from content search (e.g. ['dist/**', '*.log'])."
1411
1665
  };
1412
1666
  const maxResults = {
1413
1667
  type: "integer",
1414
- description: "Maximum number of matching output lines to return. Use a small value when you only need candidate files or examples."
1668
+ description: "Max matching output lines to return."
1415
1669
  };
1416
1670
  const literal = {
1417
1671
  type: "boolean",
1418
- description: "When true, search for query as literal text instead of a regular expression."
1672
+ description: "When true, search query as literal text instead of regex."
1419
1673
  };
1420
1674
  const caseSensitive = {
1421
1675
  type: "boolean",
@@ -1423,7 +1677,7 @@ function buildSearchWorkspaceParameters(variant) {
1423
1677
  };
1424
1678
  const contextLines = {
1425
1679
  type: "integer",
1426
- description: "Number of surrounding context lines to include around each match. Defaults to 0."
1680
+ description: "Number of surrounding context lines around each match. Defaults to 0."
1427
1681
  };
1428
1682
  if (variant === "minimal") {
1429
1683
  return {
@@ -1474,31 +1728,31 @@ function buildGlobWorkspaceDescription(variant) {
1474
1728
  return "Find workspace files by path glob without reading file contents.";
1475
1729
  }
1476
1730
  if (variant === "workflow") {
1477
- return "Find files by glob pattern inside the current workspace without reading file contents. Use after listFiles when you know the likely area, or first when the task names a filename, extension, asset type, config, or document pattern. Then use searchFiles for text inside candidates or readFile for the specific candidate paths.";
1731
+ return "Find files by glob pattern. Use searchFiles for text inside candidates, and readFile for specific paths.";
1478
1732
  }
1479
1733
  if (variant === "antiShell") {
1480
- return "Find workspace files by path glob without reading file contents. Use for code, docs, data, images, configs, logs, or any file discovery task. Do not use execShell or listFiles for path discovery when globFiles can directly narrow candidate files. Use before opening or reading files, and use searchFiles instead for searching text inside files.";
1734
+ return "Find workspace files by path glob. Prefer globFiles over shell find/ls commands.";
1481
1735
  }
1482
- return "Find files by glob pattern inside the current workspace without reading file contents. Use for code, docs, data, images, configs, logs, or any file discovery task. Use one bounded glob with brace groups before repeated narrow globs when a task names several extensions or config names, for example **/*.{png,jpg,svg}, **/{package.json,bunfig.toml,tsconfig*.json}, or src/**/*.ts. Prefer searchFiles for searching text inside files, and readFile only after candidate paths are narrow enough.";
1736
+ return "Find file paths by glob pattern without reading file contents. Use brace groups (e.g. '**/*.{ts,tsx}') to match multiple patterns in one call.";
1483
1737
  }
1484
1738
  function buildGlobWorkspaceParameters(variant) {
1485
1739
  const pattern = {
1486
1740
  type: "string",
1487
- description: "Glob pattern for files. For candidate discovery, combine related names or extensions in one brace-group pattern before making repeated narrow calls, for example '**/*.ts', 'packages/**/local*.test.ts', '**/*.{png,jpg,svg}', or '**/{package.json,bunfig.toml,tsconfig*.json}'."
1741
+ description: "Glob pattern for files (supports brace groups like '**/*.{ts,tsx}', '**/{package.json,tsconfig*.json}')."
1488
1742
  };
1489
1743
  const path = buildWorkspacePathProperty();
1490
1744
  const includeIgnored = {
1491
1745
  type: "boolean",
1492
- description: "When true, include files ignored by .gitignore. Defaults to false; .git and node_modules remain excluded."
1746
+ description: "When true, include gitignored files (.git and node_modules remain excluded)."
1493
1747
  };
1494
1748
  const maxResults = {
1495
1749
  type: "integer",
1496
- description: "Maximum number of file paths to return. Use a small value when you only need candidate paths before readFile; if results are truncated, report that and narrow the pattern or path before reading."
1750
+ description: "Max file paths to return."
1497
1751
  };
1498
1752
  const exclude = {
1499
1753
  type: "array",
1500
1754
  items: { type: "string" },
1501
- description: "Glob patterns to exclude from results, for example ['dist/**', 'build/**', 'exports/**', '*.tmp']."
1755
+ description: "Glob patterns to exclude from results."
1502
1756
  };
1503
1757
  if (variant === "minimal") {
1504
1758
  return {
@@ -1526,7 +1780,7 @@ function buildGlobWorkspaceParameters(variant) {
1526
1780
  pattern,
1527
1781
  glob: {
1528
1782
  type: "string",
1529
- description: "Alias for pattern, kept for compatibility with codeSearch-style prompts."
1783
+ description: "Alias for pattern, kept for compatibility."
1530
1784
  },
1531
1785
  path,
1532
1786
  exclude,
@@ -1597,17 +1851,17 @@ function buildExecShellTool(toolName) {
1597
1851
  type: "function",
1598
1852
  function: {
1599
1853
  name: toolName,
1600
- description: "Run a shell command in the local workspace. Commands already execute from the workspace root; do not cd into guessed paths such as /workspace, /repo, /home/user, or /home/user/workspace. Prefer one command that performs a complete verification or related git operation instead of many tiny commands. For branch setup, prefer idempotent commands such as git switch -C <branch> when replacing or recreating a benchmark branch is acceptable. Use portable POSIX commands for macOS/BSD shells; avoid GNU-only flags such as cat -A. Do not use cat -A. Do not use brittle byte-offset commands such as xxd -s -32. For separator lines, use echo or printf '%s\\n'. For text-file content or trailing-newline checks, prefer readFile over shell byte inspection when that tool is available. Do not run repeated git status, git log, git rev-parse, or branch checks after a successful command already returned the needed clean status and commit information; use one final verification command. If a command fails, use the error output to adjust the next command rather than repeating the same shape. For long-running commands that should keep running in the background (dev servers, watchers, REPLs, `--watch`/`serve`/`dev` scripts), prefer launchProcess. Obviously long-running commands run via execShell (sleep over 5s, tail -f, watch, infinite loops, dev/serve/watch scripts) are moved to the background automatically and return {detached: true, pid, label} \u2014 use listProcesses to inspect or stop them, and tell the user the command is running in the background. Any other command blocks until it exits, so keep those short.",
1854
+ description: `Execute a shell command from the workspace root. Prefer one compound command (e.g. 'git status && git diff --stat') to perform complete verification instead of multiple small roundtrips. Do not cd into guessed paths (e.g. /workspace, /repo, /home/user); commands already run from the workspace root. Use portable POSIX commands for macOS/BSD shells; do not use cat -A or brittle byte-offset tools such as xxd -s -32. For text-file content or trailing-newline checks, prefer readFile over shell byte inspection. After a successful command returned the needed status/commit info, do not run repeated git status/log/rev-parse checks; use one final verification command. 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.`,
1601
1855
  parameters: {
1602
1856
  type: "object",
1603
1857
  properties: {
1604
1858
  cmd: {
1605
1859
  type: "string",
1606
- description: "Shell command to run."
1860
+ description: "Shell command to run. Must be a non-empty command string \u2014 never pass an empty string, whitespace-only text, or null."
1607
1861
  },
1608
1862
  command: {
1609
1863
  type: "string",
1610
- description: "Shell command to run."
1864
+ description: "Shell command to run. Compatibility field for providers that model the command under this name; the executor reads cmd first, then falls back to command. Must also be non-empty when used."
1611
1865
  }
1612
1866
  }
1613
1867
  }
@@ -1619,7 +1873,7 @@ function buildLaunchProcessTool() {
1619
1873
  type: "function",
1620
1874
  function: {
1621
1875
  name: "launchProcess",
1622
- description: "Launch a long-running process (dev server, watcher, REPL) in the background and return immediately without blocking the conversation. Use this instead of execShell for commands that do not exit on their own. Returns {pid, label, status:'running'}. The process keeps running after the tool returns; use listProcesses to check status or the host UI (/procs, /stop) to stop it.",
1876
+ 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.",
1623
1877
  parameters: {
1624
1878
  type: "object",
1625
1879
  properties: {
@@ -1633,7 +1887,7 @@ function buildLaunchProcessTool() {
1633
1887
  },
1634
1888
  persist: {
1635
1889
  type: "boolean",
1636
- description: "Mark the process as persistent so it survives the current TUI/session exit (kept running in the background when the conversation closes). Defaults to false. Use for long-lived services (e.g. a desktop dev server) you want to keep alive after the session ends."
1890
+ description: "Keep process alive after session/conversation closes. Defaults to false."
1637
1891
  }
1638
1892
  },
1639
1893
  required: ["command"]
@@ -1646,7 +1900,7 @@ function buildListProcessesTool() {
1646
1900
  type: "function",
1647
1901
  function: {
1648
1902
  name: "listProcesses",
1649
- description: "List currently registered background processes launched via launchProcess. Returns array of {pid, label, command, status, startedAt, persist}.",
1903
+ description: "List active background processes launched via launchProcess. Returns {pid, label, command, status, startedAt, persist}[].",
1650
1904
  parameters: {
1651
1905
  type: "object",
1652
1906
  properties: {}
@@ -2037,7 +2291,7 @@ function isPathInsideWorkspace(args) {
2037
2291
  const relativePath = relative(args.workspaceRoot, args.targetPath);
2038
2292
  return relativePath === "" || !relativePath.startsWith("..") && !relativePath.includes(`..${sep}`);
2039
2293
  }
2040
- var PATH_FIELD_ALIASES = ["path", "file_path", "filePath", "filename", "file", "target_file", "targetFile", "file_to_read", "fileToRead"];
2294
+ var PATH_FIELD_ALIASES = ["path", "file_path", "filePath", "filename", "file", "target_file", "targetFile", "file_to_read", "fileToRead", "target_path", "read_path", "path_to_read", "pathToRead"];
2041
2295
  var OLD_TEXT_FIELD_ALIASES = ["oldText", "old_string", "oldString", "search", "search_string", "find", "match"];
2042
2296
  var NEW_TEXT_FIELD_ALIASES = ["newText", "new_string", "newString", "replacement", "replace"];
2043
2297
  var SEARCH_QUERY_FIELD_ALIASES = ["query", "search_query", "searchQuery", "q", "pattern", "search"];
@@ -2494,7 +2748,7 @@ function resolveRipgrepBinary(env = process.env) {
2494
2748
  const fromEnv = asOptionalTrimmedString(env.NOLO_BUNDLED_RG);
2495
2749
  if (fromEnv) {
2496
2750
  try {
2497
- if (existsSync(fromEnv)) return fromEnv;
2751
+ if (existsSync2(fromEnv)) return fromEnv;
2498
2752
  } catch {
2499
2753
  }
2500
2754
  }
@@ -2801,6 +3055,48 @@ async function resolveLocalWorkspaceToolPath(args) {
2801
3055
  }
2802
3056
  return targetPath;
2803
3057
  }
3058
+ var READ_FILE_MAX_CHAR_LIMIT = 32e3;
3059
+ function defensivelyTruncateReadFile(args) {
3060
+ const limit = args.limit ?? READ_FILE_MAX_CHAR_LIMIT;
3061
+ if (args.content.length <= limit) {
3062
+ return { content: args.content, truncatedByCharLimit: false };
3063
+ }
3064
+ const maxBudget = Math.floor(limit * 0.4);
3065
+ const lines = splitTextLines(args.content);
3066
+ if (lines.length <= 10) {
3067
+ const head2 = args.content.slice(0, maxBudget);
3068
+ const tail2 = args.content.slice(-maxBudget);
3069
+ const omitted = args.content.length - head2.length - tail2.length;
3070
+ return {
3071
+ content: `${head2}
3072
+
3073
+ [... ${omitted} characters omitted. Content exceeds limit (${args.content.length} chars). Use lines slice or searchFiles to inspect specific parts. ...]
3074
+
3075
+ ${tail2}`,
3076
+ truncatedByCharLimit: true
3077
+ };
3078
+ }
3079
+ const headLinesCount = Math.min(150, Math.floor(lines.length * 0.4));
3080
+ const tailLinesCount = Math.min(50, Math.floor(lines.length * 0.2));
3081
+ let head = lines.slice(0, headLinesCount).join("\n");
3082
+ let tail = lines.slice(-tailLinesCount).join("\n");
3083
+ if (head.length > maxBudget) {
3084
+ head = head.slice(0, maxBudget);
3085
+ }
3086
+ if (tail.length > maxBudget) {
3087
+ tail = tail.slice(-maxBudget);
3088
+ }
3089
+ const omittedLines = lines.length - headLinesCount - tailLinesCount;
3090
+ const omittedChars = args.content.length - head.length - tail.length;
3091
+ return {
3092
+ content: `${head}
3093
+
3094
+ [... Omitted ${Math.max(1, omittedLines)} lines (${omittedChars} chars). File exceeds safety limit (${args.content.length} chars, ${args.totalLines} lines). Use lines="start-end" (e.g. lines="151-300") or searchFiles to read specific sections. ...]
3095
+
3096
+ ${tail}`,
3097
+ truncatedByCharLimit: true
3098
+ };
3099
+ }
2804
3100
  async function readFileTool(args) {
2805
3101
  const parsed = parseWorkspaceToolArguments(args.call.arguments);
2806
3102
  const requestedPath = requireWorkspaceToolPath(parsed);
@@ -2816,19 +3112,25 @@ async function readFileTool(args) {
2816
3112
  content,
2817
3113
  ...sliceArgs
2818
3114
  });
2819
- return {
3115
+ const truncatedRead = defensivelyTruncateReadFile({
2820
3116
  content: sliced.content,
3117
+ totalLines: sliced.totalLines
3118
+ });
3119
+ const isTruncated = sliced.truncated || truncatedRead.truncatedByCharLimit;
3120
+ return {
3121
+ content: truncatedRead.content,
2821
3122
  metadata: {
2822
3123
  path: normalizeWorkspaceRelativePath({
2823
3124
  workspaceRoot: resolve(args.workspaceRoot),
2824
3125
  targetPath: absolutePath
2825
3126
  }),
2826
- bytes: Buffer.byteLength(sliced.content),
3127
+ bytes: Buffer.byteLength(truncatedRead.content),
2827
3128
  totalBytes: Buffer.byteLength(content),
2828
3129
  startLine: sliced.startLine,
2829
3130
  endLine: sliced.endLine,
2830
3131
  totalLines: sliced.totalLines,
2831
- truncated: sliced.truncated,
3132
+ truncated: isTruncated,
3133
+ ...truncatedRead.truncatedByCharLimit ? { truncatedByByteLimit: true, truncatedByCharLimit: true } : {},
2832
3134
  ...sliceArgs.maxLines ? { maxLines: sliceArgs.maxLines } : {},
2833
3135
  ...sliceArgs.tailLines ? { tailLines: sliceArgs.tailLines } : {},
2834
3136
  ...warnings.length ? { warnings: [...warnings, buildReadFileSliceHint(sliced.totalLines)] } : {},
@@ -3656,7 +3958,119 @@ function createLocalWorkspaceToolExecutors(args) {
3656
3958
  };
3657
3959
  }
3658
3960
 
3961
+ // packages/agent-runtime/noloWorkspaceTools.node.ts
3962
+ async function readNoloProcessStream(readable) {
3963
+ if (!readable) return "";
3964
+ return new Response(readable).text();
3965
+ }
3966
+ async function runNoloWorkspaceCliTool(call, args) {
3967
+ const cliArgs = buildNoloWorkspaceCommandArgs(call);
3968
+ const bunSpawn = globalThis.Bun?.spawn;
3969
+ const spawn2 = args.spawn ?? (typeof bunSpawn === "function" ? bunSpawn : nodeSpawnFallback);
3970
+ const execPath = args.processExecPath ?? process.execPath;
3971
+ const entrypoint = args.cliEntrypoint;
3972
+ const cmd = entrypoint && entrypoint !== execPath ? [execPath, entrypoint, ...cliArgs] : [execPath, ...cliArgs];
3973
+ const proc = spawn2({
3974
+ cmd,
3975
+ stdout: "pipe",
3976
+ stderr: "pipe",
3977
+ env: args.env
3978
+ });
3979
+ const [stdout, stderr, exitCode] = await Promise.all([
3980
+ readNoloProcessStream(proc.stdout),
3981
+ readNoloProcessStream(proc.stderr),
3982
+ proc.exited
3983
+ ]);
3984
+ const content = `${stdout}${stderr}`;
3985
+ if (exitCode !== 0) {
3986
+ throw new Error(content.trim() || `nolo ${cliArgs.join(" ")} exited ${exitCode}`);
3987
+ }
3988
+ return {
3989
+ content,
3990
+ metadata: {
3991
+ [args.metadataKind ?? "noloWorkspaceTool"]: true,
3992
+ command: ["nolo", ...cliArgs].join(" "),
3993
+ exitCode
3994
+ }
3995
+ };
3996
+ }
3997
+ function buildNoloWorkspaceCliToolExecutors(args) {
3998
+ const executors = {};
3999
+ for (const toolName of NOLO_WORKSPACE_TOOL_NAMES) {
4000
+ if (toolName === "loadSkill") continue;
4001
+ executors[toolName] = (call) => runNoloWorkspaceCliTool(call, args);
4002
+ }
4003
+ return executors;
4004
+ }
4005
+ var nodeSpawnFallback = (options) => {
4006
+ return spawnToWebStreams({ cmd: options.cmd, env: options.env });
4007
+ };
4008
+ function buildLoadSkillExecutor(args) {
4009
+ return async (call) => {
4010
+ const parsed = parseNoloWorkspaceToolArguments(call.arguments);
4011
+ const name = noloStringArg(parsed.name ?? parsed.skillName ?? parsed.skill);
4012
+ if (!name) {
4013
+ return {
4014
+ content: await formatUnknownSkillMessage(args.cwd, "(no name provided)"),
4015
+ metadata: { loadSkill: true, resolved: false }
4016
+ };
4017
+ }
4018
+ const { resolveSkillByName } = await import("./skillDiscovery-KJWT7JBU.js");
4019
+ const resolved = resolveSkillByName(args.cwd, name);
4020
+ if (!resolved) {
4021
+ const { resolveCodingBuiltinSlug, buildCodingSkillContentBySlug } = await import("./codingSkills-EN6Z6G6U.js");
4022
+ const builtinSlug = resolveCodingBuiltinSlug(name);
4023
+ if (builtinSlug) {
4024
+ const body = buildCodingSkillContentBySlug(builtinSlug);
4025
+ return {
4026
+ content: `Skill "${name}" loaded inline. Follow its instructions.
4027
+
4028
+ ${body}`,
4029
+ metadata: { loadSkill: true, resolved: true, name, requestedName: name, builtin: true }
4030
+ };
4031
+ }
4032
+ return {
4033
+ content: await formatUnknownSkillMessage(args.cwd, name),
4034
+ metadata: { loadSkill: true, resolved: false, name, requestedName: name }
4035
+ };
4036
+ }
4037
+ try {
4038
+ const { readFileSync } = await import("node:fs");
4039
+ const body = readFileSync(resolved, "utf8");
4040
+ return {
4041
+ content: `Skill "${name}" loaded inline. Follow its instructions.
4042
+
4043
+ ${body}`,
4044
+ metadata: { loadSkill: true, resolved: true, name, requestedName: name, path: resolved }
4045
+ };
4046
+ } catch (err) {
4047
+ const message = err instanceof Error ? err.message : String(err);
4048
+ return {
4049
+ content: `Skill "${name}" could not be read: ${message}`,
4050
+ metadata: { loadSkill: true, resolved: true, name, requestedName: name, path: resolved, readError: message }
4051
+ };
4052
+ }
4053
+ };
4054
+ }
4055
+ async function formatUnknownSkillMessage(cwd, requestedName) {
4056
+ const { discoverSkills } = await import("./skillDiscovery-KJWT7JBU.js");
4057
+ let available = [];
4058
+ try {
4059
+ available = discoverSkills(cwd).map((s) => s.name);
4060
+ } catch {
4061
+ }
4062
+ const header = `Skill "${requestedName}" not found in this workspace's skill directory (.agents/skills/<name>/SKILL.md).`;
4063
+ if (available.length === 0) return `${header}
4064
+
4065
+ No skills were discovered in this workspace.`;
4066
+ return `${header}
4067
+
4068
+ Available skills: ${available.join(", ")}`;
4069
+ }
4070
+
3659
4071
  export {
4072
+ resolveAgentNextAvailableAt,
4073
+ isAgentCoolingDown,
3660
4074
  parseSseDataLineJson,
3661
4075
  parseSseDataLineObject,
3662
4076
  buildOpenAiCompatibleChatCompletionRequest,
@@ -3669,6 +4083,7 @@ export {
3669
4083
  dialogMessageRecordToAgentRuntimeMessage,
3670
4084
  buildAgentRuntimeDialogWritePlan,
3671
4085
  shouldCacheHybridRemoteRecord,
4086
+ parseSyncServersEnv,
3672
4087
  createHybridRecordStore,
3673
4088
  isDestructiveShellCommand,
3674
4089
  evaluateShellCommandPolicy,
@@ -3678,5 +4093,7 @@ export {
3678
4093
  buildLocalWorkspaceToolset,
3679
4094
  buildLocalWorkspacePolicyToolNames,
3680
4095
  buildLocalWorkspaceOpenAiTools,
3681
- createLocalWorkspaceToolExecutors
4096
+ createLocalWorkspaceToolExecutors,
4097
+ buildNoloWorkspaceCliToolExecutors,
4098
+ buildLoadSkillExecutor
3682
4099
  };