@pentoshi/clai 3.8.23 → 3.8.25

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.
@@ -916,53 +916,27 @@ export async function runAgentTurn(prompt, options = {}) {
916
916
  detail: String(retryReasonRaw.detail ?? ""),
917
917
  }
918
918
  : undefined;
919
+ // Only block identical *failed* re-tries without changed context.
920
+ // Successful re-calls always run for real — no "already succeeded /
921
+ // use prior results" nags (those caused model abnormalities).
919
922
  const loopCheck = loopGuard.shouldBlock(call.name, call.args, {
920
923
  dependenciesChanged: retryDependenciesChanged,
921
924
  environmentChanged: retryEnvironmentChanged,
922
925
  ...(retryReason ? { retryReason } : {}),
923
926
  });
924
- // Reuse prior SUCCESS instead of re-running or returning ok=false.
925
- // Models often thrash "tools failed after resume" and re-call the same
926
- // tool.check/fs.list; answering with a fake failure made the thrash worse.
927
- const cachedSuccess = loopGuard.getCachedSuccessOutput(call.name, call.args);
928
- if (cachedSuccess !== undefined && loopGuard.hasSucceeded(call.name, call.args)) {
929
- const reuseNote = loopCheck.reason ??
930
- `${call.name} already succeeded with identical arguments earlier this turn — reusing that result. Do not re-call it; proceed to the next step.`;
931
- writeNotice("info", reuseNote, chalk.dim(` ℹ ${reuseNote}\n`));
932
- const body = `Prior successful ${call.name} result (reused; identical args — tools are working):\n` +
933
- cachedSuccess;
934
- const result = { ok: true, output: body, exitCode: 0 };
935
- emitToolResult(toolEventId, result, reuseNote);
936
- return {
937
- ok: true,
938
- call,
939
- result,
940
- contextOutput: body,
941
- };
942
- }
943
927
  if (loopCheck.block) {
944
928
  const reason = loopCheck.reason ??
945
- `${call.name} was already called with the same arguments. Use the prior result and choose a different next step.`;
929
+ `${call.name} previously failed with identical arguments. Change the command/args and retry.`;
946
930
  writeNotice("warn", reason, chalk.yellow(` ⚠ ${reason}\n`));
947
- // Prefer ok=true when we previously succeeded but have no cache body,
948
- // so the model does not treat loop-guard as a tool outage.
949
- const softOk = loopGuard.hasSucceeded(call.name, call.args);
950
- const result = {
951
- ok: softOk,
952
- output: reason,
953
- exitCode: softOk ? 0 : 1,
954
- };
931
+ const result = { ok: false, output: reason, exitCode: 1 };
955
932
  emitToolResult(toolEventId, result, reason);
956
933
  return {
957
- ok: softOk,
934
+ ok: false,
958
935
  call,
959
936
  result,
960
937
  contextOutput: reason,
961
938
  };
962
939
  }
963
- if (loopCheck.reason) {
964
- writeNotice("info", loopCheck.reason, chalk.dim(` ℹ ${loopCheck.reason}\n`));
965
- }
966
940
  if (call.name === "plan.create" || call.name === "task.update") {
967
941
  // Evidence gate: refuse done until at least one successful work tool
968
942
  // ran under this task (model must see results and be satisfied).