opencara 0.23.7 → 0.23.8

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/index.js +67 -15
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -714,6 +714,20 @@ function parseAgents(data) {
714
714
  agent.maxTasksPerDay = v;
715
715
  }
716
716
  }
717
+ if (typeof obj.liveness_timeout === "number") {
718
+ if (obj.liveness_timeout === 0) {
719
+ agent.livenessTimeout = 0;
720
+ } else {
721
+ const v = parsePositiveInt(obj.liveness_timeout);
722
+ if (v === null) {
723
+ console.warn(
724
+ `\u26A0 Config warning: agents[${i}].liveness_timeout must be a non-negative integer (seconds), got ${obj.liveness_timeout}. Value ignored.`
725
+ );
726
+ } else {
727
+ agent.livenessTimeout = v;
728
+ }
729
+ }
730
+ }
717
731
  const repoConfig = parseRepoConfig(obj, i);
718
732
  if (repoConfig) agent.repos = repoConfig;
719
733
  const synthesizeRepoConfig = parseRepoConfig(obj, i, "synthesize_repos");
@@ -1750,6 +1764,7 @@ var ToolTimeoutError = class extends Error {
1750
1764
  };
1751
1765
  var SIGKILL_GRACE_MS = 5e3;
1752
1766
  var MIN_PARTIAL_RESULT_LENGTH = 50;
1767
+ var STDOUT_LIVENESS_TIMEOUT_MS = 3e5;
1753
1768
  var MAX_STDERR_LENGTH = 1e3;
1754
1769
  function validateCommandBinary(commandTemplate) {
1755
1770
  const { command } = parseCommandTemplate(commandTemplate);
@@ -1846,7 +1861,7 @@ function parseTokenUsage(stdout, stderr) {
1846
1861
  const estimated = estimateTokens(stdout);
1847
1862
  return { tokens: estimated, parsed: false, input: 0, output: estimated };
1848
1863
  }
1849
- function executeTool(commandTemplate, prompt2, timeoutMs, signal, vars, cwd) {
1864
+ function executeTool(commandTemplate, prompt2, timeoutMs, signal, vars, cwd, livenessTimeoutMs) {
1850
1865
  const promptViaArg = commandTemplate.includes("${PROMPT}");
1851
1866
  const allVars = { ...vars, PROMPT: prompt2 };
1852
1867
  if (cwd && !allVars["CODEBASE_DIR"]) {
@@ -1866,7 +1881,12 @@ function executeTool(commandTemplate, prompt2, timeoutMs, signal, vars, cwd) {
1866
1881
  let stderr = "";
1867
1882
  let settled = false;
1868
1883
  let sigkillTimer;
1884
+ let killedByLiveness = false;
1885
+ const effectiveLivenessMs = livenessTimeoutMs === void 0 ? STDOUT_LIVENESS_TIMEOUT_MS : livenessTimeoutMs;
1886
+ let killScheduled = false;
1869
1887
  function scheduleKillEscalation() {
1888
+ if (killScheduled) return;
1889
+ killScheduled = true;
1870
1890
  child.kill("SIGTERM");
1871
1891
  if (sigkillTimer) clearTimeout(sigkillTimer);
1872
1892
  sigkillTimer = setTimeout(() => {
@@ -1876,8 +1896,26 @@ function executeTool(commandTemplate, prompt2, timeoutMs, signal, vars, cwd) {
1876
1896
  }, SIGKILL_GRACE_MS);
1877
1897
  }
1878
1898
  const timer = setTimeout(scheduleKillEscalation, timeoutMs);
1899
+ let livenessTimer;
1900
+ if (effectiveLivenessMs > 0) {
1901
+ livenessTimer = setTimeout(() => {
1902
+ if (!settled) {
1903
+ killedByLiveness = true;
1904
+ scheduleKillEscalation();
1905
+ }
1906
+ }, effectiveLivenessMs);
1907
+ }
1879
1908
  child.stdout?.on("data", (chunk) => {
1880
1909
  stdout += chunk.toString();
1910
+ if (livenessTimer) {
1911
+ clearTimeout(livenessTimer);
1912
+ livenessTimer = setTimeout(() => {
1913
+ if (!settled) {
1914
+ killedByLiveness = true;
1915
+ scheduleKillEscalation();
1916
+ }
1917
+ }, effectiveLivenessMs);
1918
+ }
1881
1919
  });
1882
1920
  child.stderr?.on("data", (chunk) => {
1883
1921
  stderr += chunk.toString();
@@ -1893,6 +1931,7 @@ function executeTool(commandTemplate, prompt2, timeoutMs, signal, vars, cwd) {
1893
1931
  }
1894
1932
  function cleanup() {
1895
1933
  clearTimeout(timer);
1934
+ if (livenessTimer) clearTimeout(livenessTimer);
1896
1935
  if (sigkillTimer) clearTimeout(sigkillTimer);
1897
1936
  if (onAbort && signal) {
1898
1937
  signal.removeEventListener("abort", onAbort);
@@ -1917,11 +1956,19 @@ function executeTool(commandTemplate, prompt2, timeoutMs, signal, vars, cwd) {
1917
1956
  return;
1918
1957
  }
1919
1958
  if (sig === "SIGTERM" || sig === "SIGKILL") {
1920
- reject(
1921
- new ToolTimeoutError(
1922
- `Tool "${command}" timed out after ${Math.round(timeoutMs / 1e3)}s`
1923
- )
1924
- );
1959
+ if (killedByLiveness) {
1960
+ reject(
1961
+ new ToolTimeoutError(
1962
+ `Tool "${command}" killed: no stdout for ${Math.round(effectiveLivenessMs / 1e3)}s (process may be stuck)`
1963
+ )
1964
+ );
1965
+ } else {
1966
+ reject(
1967
+ new ToolTimeoutError(
1968
+ `Tool "${command}" timed out after ${Math.round(timeoutMs / 1e3)}s`
1969
+ )
1970
+ );
1971
+ }
1925
1972
  return;
1926
1973
  }
1927
1974
  if (code !== 0) {
@@ -2500,7 +2547,8 @@ ${userMessage}`;
2500
2547
  effectiveTimeout,
2501
2548
  abortController.signal,
2502
2549
  void 0,
2503
- deps.codebaseDir ?? void 0
2550
+ deps.codebaseDir ?? void 0,
2551
+ deps.livenessTimeoutMs
2504
2552
  );
2505
2553
  const { verdict, review } = extractVerdict(result.stdout);
2506
2554
  const inputTokens = result.tokensParsed ? 0 : estimateTokens(fullPrompt);
@@ -2615,7 +2663,8 @@ ${userMessage}`;
2615
2663
  effectiveTimeout,
2616
2664
  abortController.signal,
2617
2665
  void 0,
2618
- deps.codebaseDir ?? void 0
2666
+ deps.codebaseDir ?? void 0,
2667
+ deps.livenessTimeoutMs
2619
2668
  );
2620
2669
  const { verdict, review } = extractVerdict(result.stdout);
2621
2670
  const flaggedReviews = extractFlaggedReviews(result.stdout);
@@ -5663,7 +5712,7 @@ function sleep2(ms, signal) {
5663
5712
  async function startAgent(agentId, platformUrl, agentInfo, reviewDeps, consumptionDeps, options) {
5664
5713
  const client = new ApiClient(platformUrl, {
5665
5714
  authToken: options?.authToken,
5666
- cliVersion: "0.23.7",
5715
+ cliVersion: "0.23.8",
5667
5716
  versionOverride: options?.versionOverride,
5668
5717
  onTokenRefresh: options?.onTokenRefresh
5669
5718
  });
@@ -5959,7 +6008,7 @@ async function startBatchAgents(config, agents, pollIntervalMs, oauthToken, opti
5959
6008
  const { versionOverride, verbose, instancesOverride, agentOwner, userOrgs } = options;
5960
6009
  const client = new ApiClient(config.platformUrl, {
5961
6010
  authToken: oauthToken,
5962
- cliVersion: "0.23.7",
6011
+ cliVersion: "0.23.8",
5963
6012
  versionOverride,
5964
6013
  onTokenRefresh: () => getValidToken(config.platformUrl, { configPath: config.authFile })
5965
6014
  });
@@ -6005,7 +6054,8 @@ async function startBatchAgents(config, agents, pollIntervalMs, oauthToken, opti
6005
6054
  commandTemplate,
6006
6055
  maxDiffSizeKb: config.maxDiffSizeKb,
6007
6056
  maxRepoSizeMb: config.maxRepoSizeMb,
6008
- codebaseDir
6057
+ codebaseDir,
6058
+ livenessTimeoutMs: agentConfig.livenessTimeout != null ? agentConfig.livenessTimeout * 1e3 : void 0
6009
6059
  };
6010
6060
  const session = createSessionTracker();
6011
6061
  const usageTracker = new UsageTracker();
@@ -6161,7 +6211,8 @@ async function startAgentRouter() {
6161
6211
  commandTemplate: commandTemplate ?? "",
6162
6212
  maxDiffSizeKb: config.maxDiffSizeKb,
6163
6213
  maxRepoSizeMb: config.maxRepoSizeMb,
6164
- codebaseDir
6214
+ codebaseDir,
6215
+ livenessTimeoutMs: agentConfig?.livenessTimeout != null ? agentConfig.livenessTimeout * 1e3 : void 0
6165
6216
  };
6166
6217
  const session = createSessionTracker();
6167
6218
  const usageTracker = new UsageTracker();
@@ -6228,7 +6279,8 @@ function startAgentByIndex(config, agentIndex, pollIntervalMs, oauthToken, versi
6228
6279
  commandTemplate,
6229
6280
  maxDiffSizeKb: config.maxDiffSizeKb,
6230
6281
  maxRepoSizeMb: config.maxRepoSizeMb,
6231
- codebaseDir
6282
+ codebaseDir,
6283
+ livenessTimeoutMs: agentConfig?.livenessTimeout != null ? agentConfig.livenessTimeout * 1e3 : void 0
6232
6284
  };
6233
6285
  const model = agentConfig?.model ?? "unknown";
6234
6286
  const tool = agentConfig?.tool ?? "unknown";
@@ -6302,7 +6354,7 @@ agentCommand.command("start").description("Start agents in polling mode").option
6302
6354
  }
6303
6355
  config = loadConfig();
6304
6356
  }
6305
- console.log(formatVersionBanner("0.23.7", "eb8ff83"));
6357
+ console.log(formatVersionBanner("0.23.8", "4d448ed"));
6306
6358
  if (config.agents && config.agents.length > 0) {
6307
6359
  const toolEntries = config.agents.map((a) => ({
6308
6360
  tool: a.tool,
@@ -7125,7 +7177,7 @@ var statusCommand = new Command4("status").description("Show agent config, conne
7125
7177
  });
7126
7178
 
7127
7179
  // src/index.ts
7128
- var program = new Command5().name("opencara").description("OpenCara \u2014 distributed AI code review agent").version(`${"0.23.7"} (${"eb8ff83"})`);
7180
+ var program = new Command5().name("opencara").description("OpenCara \u2014 distributed AI code review agent").version(`${"0.23.8"} (${"4d448ed"})`);
7129
7181
  program.addCommand(agentCommand);
7130
7182
  program.addCommand(authCommand());
7131
7183
  program.addCommand(dedupCommand());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencara",
3
- "version": "0.23.7",
3
+ "version": "0.23.8",
4
4
  "description": "Distributed AI code review agent — poll, review, and submit PR reviews using your own AI tools",
5
5
  "type": "module",
6
6
  "license": "MIT",