@rely-ai/caliber 1.40.4 → 1.41.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 (2) hide show
  1. package/dist/bin.js +156 -6
  2. package/package.json +1 -1
package/dist/bin.js CHANGED
@@ -4945,6 +4945,37 @@ var CORE_MAX_TOKENS = 16e3;
4945
4945
  var GENERATION_MAX_TOKENS = 64e3;
4946
4946
  var MODEL_MAX_OUTPUT_TOKENS = 128e3;
4947
4947
  var MAX_RETRIES2 = 5;
4948
+ var DEFAULT_INACTIVITY_TIMEOUT_MS = 12e4;
4949
+ var DEFAULT_TOTAL_TIMEOUT_MS = 6e5;
4950
+ function parseEnvTimeout(envVar, defaultMs, minMs = 5e3) {
4951
+ const val = process.env[envVar];
4952
+ if (!val) return defaultMs;
4953
+ const parsed = parseInt(val, 10);
4954
+ if (!Number.isFinite(parsed) || parsed < minMs) return defaultMs;
4955
+ return parsed;
4956
+ }
4957
+ function buildDiagnostic(stopReason, raw, charsReceived) {
4958
+ if (stopReason === "timeout_inactivity") {
4959
+ const timeoutSec = Math.round(
4960
+ parseEnvTimeout("CALIBER_STREAM_INACTIVITY_TIMEOUT_MS", DEFAULT_INACTIVITY_TIMEOUT_MS) / 1e3
4961
+ );
4962
+ if (charsReceived === 0) {
4963
+ return `Model produced no output for ${timeoutSec}s. Check your API key and model name, or increase timeout with CALIBER_STREAM_INACTIVITY_TIMEOUT_MS.`;
4964
+ }
4965
+ return `Model stopped responding for ${timeoutSec}s after producing ${charsReceived} chars. The model may be stuck. Try a different model or increase timeout with CALIBER_STREAM_INACTIVITY_TIMEOUT_MS.`;
4966
+ }
4967
+ if (stopReason === "timeout_total") {
4968
+ const timeoutSec = Math.round(
4969
+ parseEnvTimeout("CALIBER_GENERATION_TIMEOUT_MS", DEFAULT_TOTAL_TIMEOUT_MS) / 1e3
4970
+ );
4971
+ return `Generation exceeded ${timeoutSec}s total time limit. Try a different model or increase timeout with CALIBER_GENERATION_TIMEOUT_MS.`;
4972
+ }
4973
+ if (!raw || raw.trim().length === 0) {
4974
+ return "Model produced no output. Check your API key and model name.";
4975
+ }
4976
+ return `Model responded but output was not valid JSON. First 200 chars:
4977
+ ${raw.slice(0, 200)}`;
4978
+ }
4948
4979
  function isTransientError2(error) {
4949
4980
  const msg = error.message.toLowerCase();
4950
4981
  return TRANSIENT_ERRORS.some((e) => msg.includes(e.toLowerCase()));
@@ -5126,18 +5157,59 @@ Generate the skill content following the instructions in the system prompt.`;
5126
5157
  async function streamGeneration(config) {
5127
5158
  const provider = getProvider();
5128
5159
  let attempt = 0;
5160
+ const inactivityTimeoutMs = parseEnvTimeout(
5161
+ "CALIBER_STREAM_INACTIVITY_TIMEOUT_MS",
5162
+ DEFAULT_INACTIVITY_TIMEOUT_MS
5163
+ );
5164
+ const totalTimeoutMs = parseEnvTimeout("CALIBER_GENERATION_TIMEOUT_MS", DEFAULT_TOTAL_TIMEOUT_MS);
5165
+ let totalTimedOut = false;
5166
+ let totalResolve = null;
5167
+ const totalTimer = setTimeout(() => {
5168
+ totalTimedOut = true;
5169
+ if (config.callbacks) config.callbacks.onError(buildDiagnostic("timeout_total", "", 0));
5170
+ if (totalResolve) {
5171
+ totalResolve({ setup: null, raw: "", stopReason: "timeout_total" });
5172
+ totalResolve = null;
5173
+ }
5174
+ }, totalTimeoutMs);
5129
5175
  const attemptGeneration = async () => {
5176
+ if (totalTimedOut) {
5177
+ return { setup: null, raw: "", stopReason: "timeout_total" };
5178
+ }
5130
5179
  attempt++;
5131
5180
  const maxTokensForAttempt = Math.min(
5132
5181
  config.baseMaxTokens + attempt * config.tokenIncrement,
5133
5182
  config.maxTokensCap
5134
5183
  );
5135
5184
  return new Promise((resolve3) => {
5185
+ totalResolve = resolve3;
5136
5186
  let preJsonBuffer = "";
5137
5187
  let jsonContent = "";
5138
5188
  let inJson = false;
5139
5189
  let sentStatuses = 0;
5140
5190
  let stopReason = null;
5191
+ let charsReceived = 0;
5192
+ let settled = false;
5193
+ let inactivityTimer = null;
5194
+ function clearInactivityTimer() {
5195
+ if (inactivityTimer) {
5196
+ clearTimeout(inactivityTimer);
5197
+ inactivityTimer = null;
5198
+ }
5199
+ }
5200
+ function resetInactivityTimer() {
5201
+ if (inactivityTimer) clearTimeout(inactivityTimer);
5202
+ inactivityTimer = setTimeout(() => {
5203
+ if (settled) return;
5204
+ settled = true;
5205
+ clearInactivityTimer();
5206
+ const raw = preJsonBuffer + jsonContent;
5207
+ if (config.callbacks)
5208
+ config.callbacks.onError(buildDiagnostic("timeout_inactivity", raw, charsReceived));
5209
+ resolve3({ setup: null, raw, stopReason: "timeout_inactivity" });
5210
+ }, inactivityTimeoutMs);
5211
+ }
5212
+ resetInactivityTimer();
5141
5213
  provider.stream(
5142
5214
  {
5143
5215
  system: config.systemPrompt,
@@ -5146,6 +5218,9 @@ async function streamGeneration(config) {
5146
5218
  },
5147
5219
  {
5148
5220
  onText: (text) => {
5221
+ if (settled || totalTimedOut) return;
5222
+ charsReceived += text.length;
5223
+ resetInactivityTimer();
5149
5224
  if (!inJson) {
5150
5225
  preJsonBuffer += text;
5151
5226
  const lines = preJsonBuffer.split("\n");
@@ -5173,6 +5248,9 @@ async function streamGeneration(config) {
5173
5248
  }
5174
5249
  },
5175
5250
  onEnd: (meta) => {
5251
+ clearInactivityTimer();
5252
+ if (settled || totalTimedOut) return;
5253
+ settled = true;
5176
5254
  stopReason = meta?.stopReason ?? null;
5177
5255
  let setup = null;
5178
5256
  let jsonToParse = (jsonContent || preJsonBuffer).replace(/```\s*$/g, "").trim();
@@ -5215,23 +5293,37 @@ async function streamGeneration(config) {
5215
5293
  }
5216
5294
  },
5217
5295
  onError: (error) => {
5296
+ clearInactivityTimer();
5297
+ if (settled || totalTimedOut) return;
5218
5298
  if (isTransientError2(error) && attempt < MAX_RETRIES2) {
5299
+ settled = true;
5219
5300
  if (config.callbacks)
5220
5301
  config.callbacks.onStatus("Connection interrupted, retrying...");
5221
5302
  setTimeout(() => attemptGeneration().then(resolve3), 2e3);
5222
5303
  return;
5223
5304
  }
5305
+ settled = true;
5224
5306
  if (config.callbacks) config.callbacks.onError(error.message);
5225
5307
  resolve3({ setup: null, raw: error.message, stopReason: "error" });
5226
5308
  }
5227
5309
  }
5228
5310
  ).catch((error) => {
5311
+ clearInactivityTimer();
5312
+ if (settled || totalTimedOut) return;
5313
+ settled = true;
5229
5314
  if (config.callbacks) config.callbacks.onError(error.message);
5230
5315
  resolve3({ setup: null, raw: error.message, stopReason: "error" });
5231
5316
  });
5232
5317
  });
5233
5318
  };
5234
- return attemptGeneration();
5319
+ try {
5320
+ const result = await attemptGeneration();
5321
+ clearTimeout(totalTimer);
5322
+ return totalTimedOut ? { setup: null, raw: result.raw, stopReason: "timeout_total" } : result;
5323
+ } catch (err) {
5324
+ clearTimeout(totalTimer);
5325
+ throw err;
5326
+ }
5235
5327
  }
5236
5328
  async function generateCore(fingerprint, targetAgent, prompt, callbacks, failingChecks, currentScore, passingChecks) {
5237
5329
  const userMessage = buildGeneratePrompt(
@@ -9501,6 +9593,10 @@ import fs30 from "fs";
9501
9593
  // src/ai/refine.ts
9502
9594
  async function refineSetup(currentSetup, message, conversationHistory, callbacks) {
9503
9595
  const provider = getProvider();
9596
+ const inactivityTimeoutMs = parseEnvTimeout(
9597
+ "CALIBER_STREAM_INACTIVITY_TIMEOUT_MS",
9598
+ DEFAULT_INACTIVITY_TIMEOUT_MS
9599
+ );
9504
9600
  const prompt = `Current setup:
9505
9601
  ${JSON.stringify(currentSetup, null, 2)}
9506
9602
 
@@ -9509,6 +9605,25 @@ User request: ${message}
9509
9605
  Return the complete updated AgentSetup JSON incorporating the user's changes. Respond with ONLY the JSON.`;
9510
9606
  return new Promise((resolve3) => {
9511
9607
  let buffer = "";
9608
+ let settled = false;
9609
+ let inactivityTimer = null;
9610
+ function clearInactivityTimer() {
9611
+ if (inactivityTimer) {
9612
+ clearTimeout(inactivityTimer);
9613
+ inactivityTimer = null;
9614
+ }
9615
+ }
9616
+ function resetInactivityTimer() {
9617
+ clearInactivityTimer();
9618
+ inactivityTimer = setTimeout(() => {
9619
+ if (settled) return;
9620
+ settled = true;
9621
+ const msg = buffer.length === 0 ? "Model produced no output. Try a different model." : "Model stopped responding. Try rephrasing your request or using a different model.";
9622
+ if (callbacks) callbacks.onError(msg);
9623
+ resolve3(null);
9624
+ }, inactivityTimeoutMs);
9625
+ }
9626
+ resetInactivityTimer();
9512
9627
  provider.stream(
9513
9628
  {
9514
9629
  system: REFINE_SYSTEM_PROMPT,
@@ -9518,9 +9633,14 @@ Return the complete updated AgentSetup JSON incorporating the user's changes. Re
9518
9633
  },
9519
9634
  {
9520
9635
  onText: (text) => {
9636
+ if (settled) return;
9521
9637
  buffer += text;
9638
+ resetInactivityTimer();
9522
9639
  },
9523
9640
  onEnd: () => {
9641
+ clearInactivityTimer();
9642
+ if (settled) return;
9643
+ settled = true;
9524
9644
  const cleaned = stripMarkdownFences(buffer);
9525
9645
  const jsonStart = cleaned.indexOf("{");
9526
9646
  const jsonToParse = jsonStart !== -1 ? cleaned.slice(jsonStart) : cleaned;
@@ -9529,16 +9649,23 @@ Return the complete updated AgentSetup JSON incorporating the user's changes. Re
9529
9649
  if (callbacks) callbacks.onComplete(setup);
9530
9650
  resolve3(setup);
9531
9651
  } catch {
9532
- if (callbacks) callbacks.onError("Failed to parse AI response. Try rephrasing your request.");
9652
+ if (callbacks)
9653
+ callbacks.onError("Failed to parse AI response. Try rephrasing your request.");
9533
9654
  resolve3(null);
9534
9655
  }
9535
9656
  },
9536
9657
  onError: (error) => {
9658
+ clearInactivityTimer();
9659
+ if (settled) return;
9660
+ settled = true;
9537
9661
  if (callbacks) callbacks.onError(error.message);
9538
9662
  resolve3(null);
9539
9663
  }
9540
9664
  }
9541
9665
  ).catch((error) => {
9666
+ clearInactivityTimer();
9667
+ if (settled) return;
9668
+ settled = true;
9542
9669
  if (callbacks) callbacks.onError(error.message);
9543
9670
  resolve3(null);
9544
9671
  });
@@ -10033,6 +10160,18 @@ function writeErrorLog(config, rawOutput, error, stopReason) {
10033
10160
  lines.push("## Error", "```", error, "```", "");
10034
10161
  }
10035
10162
  lines.push("## Raw LLM Output", "```", rawOutput || "(empty)", "```");
10163
+ if (stopReason?.startsWith("timeout")) {
10164
+ lines.push("", "## Troubleshooting", "");
10165
+ lines.push("This failure was caused by a timeout. You can adjust timeouts with:");
10166
+ lines.push("```bash");
10167
+ lines.push("# Increase inactivity timeout (default: 120s)");
10168
+ lines.push("export CALIBER_STREAM_INACTIVITY_TIMEOUT_MS=180000");
10169
+ lines.push("");
10170
+ lines.push("# Increase total generation timeout (default: 600s)");
10171
+ lines.push("export CALIBER_GENERATION_TIMEOUT_MS=900000");
10172
+ lines.push("```");
10173
+ lines.push("", "If timeouts persist, try a different model.");
10174
+ }
10036
10175
  fs32.mkdirSync(path26.join(process.cwd(), ".caliber"), { recursive: true });
10037
10176
  fs32.writeFileSync(logPath, lines.join("\n"));
10038
10177
  console.log(chalk13.dim(`
@@ -10049,7 +10188,9 @@ async function evaluateDismissals(failingChecks, fingerprint) {
10049
10188
  suggestion: c.suggestion
10050
10189
  }));
10051
10190
  const hasBuildFiles = fingerprint.fileTree.some(
10052
- (f) => /^(package\.json|Makefile|Cargo\.toml|go\.mod|pyproject\.toml|requirements\.txt|build\.gradle|pom\.xml)$/i.test(f.split("/").pop() || "")
10191
+ (f) => /^(package\.json|Makefile|Cargo\.toml|go\.mod|pyproject\.toml|requirements\.txt|build\.gradle|pom\.xml)$/i.test(
10192
+ f.split("/").pop() || ""
10193
+ )
10053
10194
  );
10054
10195
  const topFiles = fingerprint.fileTree.slice(0, 30).join(", ");
10055
10196
  try {
@@ -10284,8 +10425,16 @@ async function initCommand(options) {
10284
10425
  installSessionStartHook();
10285
10426
  console.log(` ${chalk14.green("\u2713")} Freshness hook \u2014 warns when configs are stale`);
10286
10427
  if (IS_WINDOWS5) {
10287
- console.log(chalk14.yellow("\n Note: hooks use shell syntax and require Git Bash (included with Git for Windows)."));
10288
- console.log(chalk14.dim(" If hooks don't run, ensure Git for Windows is installed and git is using its bundled sh."));
10428
+ console.log(
10429
+ chalk14.yellow(
10430
+ "\n Note: hooks use shell syntax and require Git Bash (included with Git for Windows)."
10431
+ )
10432
+ );
10433
+ console.log(
10434
+ chalk14.dim(
10435
+ " If hooks don't run, ensure Git for Windows is installed and git is using its bundled sh."
10436
+ )
10437
+ );
10289
10438
  }
10290
10439
  const { ensureBuiltinSkills: ensureBuiltinSkills2 } = await Promise.resolve().then(() => (init_builtin_skills(), builtin_skills_exports));
10291
10440
  for (const agent of targetAgent) {
@@ -10584,7 +10733,8 @@ async function initCommand(options) {
10584
10733
  rawOutput = result.raw;
10585
10734
  genStopReason = result.stopReason;
10586
10735
  if (!generatedSetup) {
10587
- display.update(TASK_CONFIG, "failed", "Could not parse LLM response");
10736
+ const diagnostic = buildDiagnostic(genStopReason, rawOutput, rawOutput?.length ?? 0);
10737
+ display.update(TASK_CONFIG, "failed", diagnostic.split("\n")[0].slice(0, 80));
10588
10738
  display.update(TASK_SKILLS_GEN, "failed", "Skipped");
10589
10739
  return;
10590
10740
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rely-ai/caliber",
3
- "version": "1.40.4",
3
+ "version": "1.41.0",
4
4
  "description": "AI context infrastructure for coding agents — keeps CLAUDE.md, Cursor rules, and skills in sync as your codebase evolves",
5
5
  "type": "module",
6
6
  "bin": {