@rallycry/conveyor-agent 7.2.18 → 7.3.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.
@@ -4915,8 +4915,34 @@ async function handleAskUserQuestion(host, input) {
4915
4915
  }
4916
4916
  return { behavior: "allow", updatedInput: { questions: input.questions, answers } };
4917
4917
  }
4918
+ var EXPLORATION_TOOLS = /* @__PURE__ */ new Set(["Read", "Grep", "Glob"]);
4919
+ function trackExploration(tracker, toolName, input) {
4920
+ if (!EXPLORATION_TOOLS.has(toolName)) return null;
4921
+ if (toolName === "Read") {
4922
+ const filePath = String(input.file_path ?? "");
4923
+ if (filePath) return tracker.recordFileRead(filePath);
4924
+ }
4925
+ if (toolName === "Grep" || toolName === "Glob") {
4926
+ const pattern = String(input.pattern ?? "");
4927
+ if (pattern) return tracker.recordSearch(pattern);
4928
+ }
4929
+ return null;
4930
+ }
4918
4931
  var DENIAL_WARNING_THRESHOLD = 3;
4919
4932
  var DENIAL_FORCE_STOP_THRESHOLD = 8;
4933
+ function handleDenialEscalation(host, consecutiveDenials) {
4934
+ if (consecutiveDenials === DENIAL_WARNING_THRESHOLD) {
4935
+ host.connection.postChatMessage(
4936
+ `\u26A0\uFE0F Multiple tool denials detected. You are in ${host.agentMode} mode \u2014 file writes outside .claude/plans/ are not permitted. Focus on creating a plan instead of implementing code changes.`
4937
+ );
4938
+ }
4939
+ if (consecutiveDenials >= DENIAL_FORCE_STOP_THRESHOLD) {
4940
+ host.connection.postChatMessage(
4941
+ `Agent force-stopped after ${DENIAL_FORCE_STOP_THRESHOLD} consecutive tool denials. The agent appears stuck \u2014 send a message to resume.`
4942
+ );
4943
+ host.requestStop();
4944
+ }
4945
+ }
4920
4946
  function resolveToolAccess(host, toolName, input) {
4921
4947
  switch (host.agentMode) {
4922
4948
  case "discovery":
@@ -4947,19 +4973,15 @@ function buildCanUseTool(host) {
4947
4973
  return await handleAskUserQuestion(host, input);
4948
4974
  }
4949
4975
  const result = resolveToolAccess(host, toolName, input);
4976
+ if (result.behavior === "allow" && host.explorationTracker && !host.hasExitedPlanMode) {
4977
+ const signal = trackExploration(host.explorationTracker, toolName, input);
4978
+ if (signal) {
4979
+ host.connection.postChatMessage(signal.message);
4980
+ }
4981
+ }
4950
4982
  if (result.behavior === "deny") {
4951
4983
  consecutiveDenials++;
4952
- if (consecutiveDenials === DENIAL_WARNING_THRESHOLD) {
4953
- host.connection.postChatMessage(
4954
- `\u26A0\uFE0F Multiple tool denials detected. You are in ${host.agentMode} mode \u2014 file writes outside .claude/plans/ are not permitted. Focus on creating a plan instead of implementing code changes.`
4955
- );
4956
- }
4957
- if (consecutiveDenials >= DENIAL_FORCE_STOP_THRESHOLD) {
4958
- host.connection.postChatMessage(
4959
- `Agent force-stopped after ${DENIAL_FORCE_STOP_THRESHOLD} consecutive tool denials. The agent appears stuck \u2014 send a message to resume.`
4960
- );
4961
- host.requestStop();
4962
- }
4984
+ handleDenialEscalation(host, consecutiveDenials);
4963
4985
  } else {
4964
4986
  consecutiveDenials = 0;
4965
4987
  }
@@ -5383,6 +5405,108 @@ var CostTracker = class {
5383
5405
  }
5384
5406
  };
5385
5407
 
5408
+ // src/execution/exploration-tracker.ts
5409
+ var FILE_REREAD_NUDGE_L1 = 3;
5410
+ var FILE_REREAD_NUDGE_L2 = 5;
5411
+ var SEARCH_REPEAT_NUDGE_L1 = 3;
5412
+ var SEARCH_REPEAT_NUDGE_L2 = 5;
5413
+ var RATIO_MIN_READS_L1 = 10;
5414
+ var RATIO_THRESHOLD_L1 = 0.4;
5415
+ var RATIO_MIN_READS_L2 = 15;
5416
+ var RATIO_THRESHOLD_L2 = 0.3;
5417
+ function normalizePath(filePath) {
5418
+ let normalized = filePath.replace(/^\.\//, "");
5419
+ normalized = normalized.replace(/\/+/g, "/");
5420
+ return normalized;
5421
+ }
5422
+ var ExplorationTracker = class {
5423
+ fileReadCounts = /* @__PURE__ */ new Map();
5424
+ searchCounts = /* @__PURE__ */ new Map();
5425
+ totalReads = 0;
5426
+ highestNudgeEmitted = 0;
5427
+ complexityMultiplier;
5428
+ constructor(isParentTask) {
5429
+ this.complexityMultiplier = isParentTask ? 1.5 : 1;
5430
+ }
5431
+ /** Record a file read and return a staleness signal if thresholds are crossed. */
5432
+ recordFileRead(filePath) {
5433
+ const normalized = normalizePath(filePath);
5434
+ const count = (this.fileReadCounts.get(normalized) ?? 0) + 1;
5435
+ this.fileReadCounts.set(normalized, count);
5436
+ this.totalReads++;
5437
+ return this.checkFileRereadSignal(normalized, count) ?? this.checkExplorationRatio();
5438
+ }
5439
+ /** Record a search pattern and return a staleness signal if thresholds are crossed. */
5440
+ recordSearch(pattern) {
5441
+ const count = (this.searchCounts.get(pattern) ?? 0) + 1;
5442
+ this.searchCounts.set(pattern, count);
5443
+ return this.checkSearchRepeatSignal(pattern, count);
5444
+ }
5445
+ scaledThreshold(base) {
5446
+ return Math.ceil(base * this.complexityMultiplier);
5447
+ }
5448
+ checkFileRereadSignal(filePath, count) {
5449
+ const l2 = this.scaledThreshold(FILE_REREAD_NUDGE_L2);
5450
+ const l1 = this.scaledThreshold(FILE_REREAD_NUDGE_L1);
5451
+ if (count >= l2 && this.highestNudgeEmitted < 2) {
5452
+ this.highestNudgeEmitted = 2;
5453
+ return {
5454
+ level: 2,
5455
+ message: `\u26A0\uFE0F Exploration warning: You've read "${filePath}" ${count} times, suggesting analysis paralysis. Write your plan with the context you have and call ExitPlanMode. If something specific is blocking you, describe the blocker in your plan.`
5456
+ };
5457
+ }
5458
+ if (count >= l1 && this.highestNudgeEmitted < 1) {
5459
+ this.highestNudgeEmitted = 1;
5460
+ return {
5461
+ level: 1,
5462
+ message: `Exploration note: You've read "${filePath}" ${count} times. Consider whether you have enough context to start writing your plan. If you're still exploring, try looking at different files.`
5463
+ };
5464
+ }
5465
+ return null;
5466
+ }
5467
+ checkSearchRepeatSignal(pattern, count) {
5468
+ const l2 = this.scaledThreshold(SEARCH_REPEAT_NUDGE_L2);
5469
+ const l1 = this.scaledThreshold(SEARCH_REPEAT_NUDGE_L1);
5470
+ const displayPattern = pattern.length > 60 ? pattern.slice(0, 57) + "..." : pattern;
5471
+ if (count >= l2 && this.highestNudgeEmitted < 2) {
5472
+ this.highestNudgeEmitted = 2;
5473
+ return {
5474
+ level: 2,
5475
+ message: `\u26A0\uFE0F Exploration warning: You've searched for "${displayPattern}" ${count} times. Repeated searches for the same pattern suggest you may be stuck. Proceed with the information you have.`
5476
+ };
5477
+ }
5478
+ if (count >= l1 && this.highestNudgeEmitted < 1) {
5479
+ this.highestNudgeEmitted = 1;
5480
+ return {
5481
+ level: 1,
5482
+ message: `Exploration note: You've searched for "${displayPattern}" ${count} times. If you're not finding what you need, try a different approach or broader pattern.`
5483
+ };
5484
+ }
5485
+ return null;
5486
+ }
5487
+ checkExplorationRatio() {
5488
+ const uniqueReads = this.fileReadCounts.size;
5489
+ const ratio = this.totalReads > 0 ? uniqueReads / this.totalReads : 1;
5490
+ const minReadsL2 = this.scaledThreshold(RATIO_MIN_READS_L2);
5491
+ if (this.totalReads >= minReadsL2 && ratio < RATIO_THRESHOLD_L2 && this.highestNudgeEmitted < 2) {
5492
+ this.highestNudgeEmitted = 2;
5493
+ return {
5494
+ level: 2,
5495
+ message: `\u26A0\uFE0F Exploration warning: Only ${Math.round(ratio * 100)}% of your ${this.totalReads} file reads are unique. This indicates significant re-reading. Write your plan with the context you have.`
5496
+ };
5497
+ }
5498
+ const minReadsL1 = this.scaledThreshold(RATIO_MIN_READS_L1);
5499
+ if (this.totalReads >= minReadsL1 && ratio < RATIO_THRESHOLD_L1 && this.highestNudgeEmitted < 1) {
5500
+ this.highestNudgeEmitted = 1;
5501
+ return {
5502
+ level: 1,
5503
+ message: `Exploration note: ${Math.round(ratio * 100)}% of your ${this.totalReads} file reads are unique. You're re-reading files more than exploring new ones. Consider starting your plan.`
5504
+ };
5505
+ }
5506
+ return null;
5507
+ }
5508
+ };
5509
+
5386
5510
  // src/runner/query-bridge.ts
5387
5511
  var logger3 = createServiceLogger("QueryBridge");
5388
5512
  var QueryBridge = class {
@@ -5474,6 +5598,7 @@ var QueryBridge = class {
5474
5598
  harness: this.harness,
5475
5599
  setupLog: [],
5476
5600
  costTracker: this.costTracker,
5601
+ explorationTracker: bridge.mode.effectiveMode === "discovery" || bridge.mode.effectiveMode === "auto" && !bridge.mode.hasExitedPlanMode ? new ExplorationTracker(bridge._isParentTask) : null,
5477
5602
  sessionIds: this.sessionIds,
5478
5603
  pendingToolOutputs: this.pendingToolOutputs,
5479
5604
  // Live getters/setters delegating to ModeController + bridge state
@@ -7671,4 +7796,4 @@ export {
7671
7796
  loadForwardPorts,
7672
7797
  loadConveyorConfig
7673
7798
  };
7674
- //# sourceMappingURL=chunk-5OMUMWQR.js.map
7799
+ //# sourceMappingURL=chunk-5MTPAVNZ.js.map