opencara 0.24.3 → 0.25.1

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 +99 -30
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -499,6 +499,7 @@ function ensureConfigDir() {
499
499
  fs.mkdirSync(dir, { recursive: true });
500
500
  }
501
501
  var DEFAULT_MAX_DIFF_SIZE_KB = 100;
502
+ var DEFAULT_MAX_SUMMARY_INPUT_KB = 500;
502
503
  var DEFAULT_MAX_CONSECUTIVE_ERRORS = 10;
503
504
  var DEFAULT_MAX_REPO_SIZE_MB = 100;
504
505
  var DEFAULT_COMMAND_TEST_TIMEOUT_MS = 1e4;
@@ -705,6 +706,12 @@ function validateConfigData(data, envPlatformUrl) {
705
706
  );
706
707
  overrides.maxDiffSizeKb = DEFAULT_MAX_DIFF_SIZE_KB;
707
708
  }
709
+ if (typeof data.max_summary_input_kb === "number" && data.max_summary_input_kb <= 0) {
710
+ console.warn(
711
+ `\u26A0 Config warning: max_summary_input_kb must be > 0, got ${data.max_summary_input_kb}, using default (${DEFAULT_MAX_SUMMARY_INPUT_KB})`
712
+ );
713
+ overrides.maxSummaryInputKb = DEFAULT_MAX_SUMMARY_INPUT_KB;
714
+ }
708
715
  if (typeof data.max_consecutive_errors === "number" && data.max_consecutive_errors <= 0) {
709
716
  console.warn(
710
717
  `\u26A0 Config warning: max_consecutive_errors must be > 0, got ${data.max_consecutive_errors}, using default (${DEFAULT_MAX_CONSECUTIVE_ERRORS})`
@@ -745,6 +752,7 @@ function loadConfig() {
745
752
  platformUrl: envPlatformUrl || DEFAULT_PLATFORM_URL,
746
753
  authFile: null,
747
754
  maxDiffSizeKb: DEFAULT_MAX_DIFF_SIZE_KB,
755
+ maxSummaryInputKb: DEFAULT_MAX_SUMMARY_INPUT_KB,
748
756
  maxConsecutiveErrors: DEFAULT_MAX_CONSECUTIVE_ERRORS,
749
757
  maxRepoSizeMb: DEFAULT_MAX_REPO_SIZE_MB,
750
758
  codebaseDir: null,
@@ -804,6 +812,7 @@ function loadConfig() {
804
812
  platformUrl: envPlatformUrl || (typeof data.platform_url === "string" ? data.platform_url : DEFAULT_PLATFORM_URL),
805
813
  authFile: typeof data.auth_file === "string" && data.auth_file.trim() ? resolveFilePath(data.auth_file) : null,
806
814
  maxDiffSizeKb: overrides.maxDiffSizeKb ?? (typeof data.max_diff_size_kb === "number" ? data.max_diff_size_kb : DEFAULT_MAX_DIFF_SIZE_KB),
815
+ maxSummaryInputKb: overrides.maxSummaryInputKb ?? (typeof data.max_summary_input_kb === "number" ? data.max_summary_input_kb : DEFAULT_MAX_SUMMARY_INPUT_KB),
807
816
  maxConsecutiveErrors: overrides.maxConsecutiveErrors ?? (typeof data.max_consecutive_errors === "number" ? data.max_consecutive_errors : DEFAULT_MAX_CONSECUTIVE_ERRORS),
808
817
  maxRepoSizeMb: overrides.maxRepoSizeMb ?? (typeof data.max_repo_size_mb === "number" ? data.max_repo_size_mb : DEFAULT_MAX_REPO_SIZE_MB),
809
818
  codebaseDir: typeof data.codebase_dir === "string" ? data.codebase_dir : null,
@@ -1880,18 +1889,71 @@ function gitExec(command, args, cwd, opts) {
1880
1889
  throw new Error(sanitizeTokens(message));
1881
1890
  }
1882
1891
  }
1883
- function diffFromWorktree(bareRepoPath, worktreePath, baseRef, ghAvailable, maxDiffBytes = 128 * 1024 * 1024) {
1884
- if (!/^[A-Za-z0-9_./-]+$/.test(baseRef) || baseRef.startsWith("-")) {
1885
- throw new Error(`Invalid base ref: ${baseRef}`);
1886
- }
1892
+ var DEFAULT_BRANCH_FALLBACKS = ["main", "master"];
1893
+ function isValidBranchName(name) {
1894
+ if (!name) return false;
1895
+ if (name.startsWith("-")) return false;
1896
+ if (name.includes("..")) return false;
1897
+ return /^[A-Za-z0-9_./-]+$/.test(name);
1898
+ }
1899
+ function fetchBranch(bareRepoPath, branch, ghAvailable) {
1887
1900
  const credArgs = ghAvailable ? ["-c", `credential.helper=${GH_CREDENTIAL_HELPER}`] : [];
1888
1901
  gitExec(
1889
1902
  "git",
1890
- [...credArgs, "fetch", "--force", "origin", `${baseRef}:refs/remotes/origin/${baseRef}`],
1903
+ [...credArgs, "fetch", "--force", "origin", `${branch}:refs/remotes/origin/${branch}`],
1891
1904
  bareRepoPath
1892
1905
  );
1906
+ }
1907
+ function isRemoteRefMissingError(err) {
1908
+ const msg = err instanceof Error ? err.message : String(err);
1909
+ return /couldn't find remote ref/i.test(msg) || /couldnt find remote ref/i.test(msg) || /no such ref/i.test(msg) || /remote ref.*not found/i.test(msg) || /unknown revision or path not in the working tree/i.test(msg);
1910
+ }
1911
+ function deriveDefaultBranch(bareRepoPath, ghAvailable) {
1912
+ try {
1913
+ const out = gitExec("git", ["symbolic-ref", "refs/remotes/origin/HEAD"], bareRepoPath).trim();
1914
+ const prefix = "refs/remotes/origin/";
1915
+ if (out.startsWith(prefix)) {
1916
+ const branch = out.slice(prefix.length);
1917
+ if (isValidBranchName(branch)) {
1918
+ try {
1919
+ fetchBranch(bareRepoPath, branch, ghAvailable);
1920
+ return branch;
1921
+ } catch {
1922
+ }
1923
+ }
1924
+ }
1925
+ } catch {
1926
+ }
1927
+ for (const candidate of DEFAULT_BRANCH_FALLBACKS) {
1928
+ try {
1929
+ fetchBranch(bareRepoPath, candidate, ghAvailable);
1930
+ return candidate;
1931
+ } catch {
1932
+ }
1933
+ }
1934
+ throw new Error("Cannot derive default branch: origin/HEAD, main, and master all failed");
1935
+ }
1936
+ function diffFromWorktree(bareRepoPath, worktreePath, baseRef, ghAvailable, maxDiffBytes = 128 * 1024 * 1024) {
1937
+ let resolvedBaseRef;
1938
+ if (baseRef) {
1939
+ if (!isValidBranchName(baseRef)) {
1940
+ throw new Error(`Invalid base ref: ${baseRef}`);
1941
+ }
1942
+ try {
1943
+ fetchBranch(bareRepoPath, baseRef, ghAvailable);
1944
+ resolvedBaseRef = baseRef;
1945
+ } catch (err) {
1946
+ if (!isRemoteRefMissingError(err)) {
1947
+ throw err;
1948
+ }
1949
+ resolvedBaseRef = void 0;
1950
+ }
1951
+ }
1952
+ if (!resolvedBaseRef) {
1953
+ resolvedBaseRef = deriveDefaultBranch(bareRepoPath, ghAvailable);
1954
+ }
1893
1955
  try {
1894
- return gitExec("git", ["diff", `origin/${baseRef}...HEAD`], worktreePath, {
1956
+ return gitExec("git", ["diff", `origin/${resolvedBaseRef}...HEAD`], worktreePath, {
1895
1957
  maxBuffer: maxDiffBytes
1896
1958
  });
1897
1959
  } catch (err) {
@@ -2560,7 +2622,7 @@ function sleep(ms, signal) {
2560
2622
 
2561
2623
  // src/summary.ts
2562
2624
  var TIMEOUT_SAFETY_MARGIN_MS2 = 3e4;
2563
- var MAX_INPUT_SIZE_BYTES = 200 * 1024;
2625
+ var MAX_INPUT_SIZE_BYTES = 500 * 1024;
2564
2626
  var InputTooLargeError = class extends Error {
2565
2627
  constructor(message) {
2566
2628
  super(message);
@@ -2610,9 +2672,10 @@ function calculateInputSize(prompt2, reviews, diffContent, contextBlock) {
2610
2672
  }
2611
2673
  async function executeSummary(req, deps, runTool = executeTool) {
2612
2674
  const inputSize = calculateInputSize(req.prompt, req.reviews, req.diffContent, req.contextBlock);
2613
- if (inputSize > MAX_INPUT_SIZE_BYTES) {
2675
+ const maxInputBytes = deps.maxSummaryInputKb !== void 0 ? deps.maxSummaryInputKb * 1024 : MAX_INPUT_SIZE_BYTES;
2676
+ if (inputSize > maxInputBytes) {
2614
2677
  throw new InputTooLargeError(
2615
- `Summary input too large (${Math.round(inputSize / 1024)}KB > ${Math.round(MAX_INPUT_SIZE_BYTES / 1024)}KB limit)`
2678
+ `Summary input too large (${Math.round(inputSize / 1024)}KB > ${Math.round(maxInputBytes / 1024)}KB limit)`
2616
2679
  );
2617
2680
  }
2618
2681
  const timeoutMs = req.timeout * 1e3;
@@ -4687,6 +4750,12 @@ function registerShutdownHandlers(controller, log, graceMs = SHUTDOWN_GRACE_MS)
4687
4750
  };
4688
4751
  }
4689
4752
  var NON_RETRYABLE_STATUSES = /* @__PURE__ */ new Set([401, 403, 404]);
4753
+ function build404Hint(isGhAuthenticated) {
4754
+ if (isGhAuthenticated) {
4755
+ return ". Diff fetch returned 404. Possible causes: (a) PR not found, (b) the installation can't access this repo, or (c) a transient GitHub outage. See any `[fetchDiffViaGh]` warning above for the underlying `gh api` error.";
4756
+ }
4757
+ return ". If this is a private repo, ensure gh CLI is installed and authenticated: gh auth login";
4758
+ }
4690
4759
  function toApiDiffUrl(webUrl) {
4691
4760
  const match = webUrl.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)(?:\.diff)?$/);
4692
4761
  if (!match) return null;
@@ -4748,7 +4817,7 @@ function computeRoles(agent) {
4748
4817
  return ["review", "summary", "implement", "fix"];
4749
4818
  }
4750
4819
  var DIFF_FETCH_TIMEOUT_MS = 6e4;
4751
- async function fetchDiffHttp(url, headers, signal, maxDiffSizeKb) {
4820
+ async function fetchDiffHttp(url, headers, signal, maxDiffSizeKb, isGhAuthenticatedFn = isGhAvailable) {
4752
4821
  const maxBytes = maxDiffSizeKb ? maxDiffSizeKb * 1024 : Infinity;
4753
4822
  const controller = new AbortController();
4754
4823
  const timer = setTimeout(() => controller.abort(), DIFF_FETCH_TIMEOUT_MS);
@@ -4769,7 +4838,7 @@ async function fetchDiffHttp(url, headers, signal, maxDiffSizeKb) {
4769
4838
  clearTimeout(timer);
4770
4839
  signal?.removeEventListener("abort", onParentAbort);
4771
4840
  if (!response.ok) {
4772
- const hint = response.status === 404 ? ". If this is a private repo, ensure gh CLI is installed and authenticated: gh auth login" : "";
4841
+ const hint = response.status === 404 ? build404Hint(isGhAuthenticatedFn()) : "";
4773
4842
  const msg = `Failed to fetch diff: ${response.status} ${response.statusText}${hint}`;
4774
4843
  if (NON_RETRYABLE_STATUSES.has(response.status)) {
4775
4844
  throw new NonRetryableError(msg);
@@ -5054,7 +5123,12 @@ async function handleTask(client, agentId, task, reviewDeps, consumptionDeps, ag
5054
5123
  );
5055
5124
  taskReviewDeps = { ...reviewDeps, codebaseDir: null };
5056
5125
  }
5057
- if (taskCheckoutPath && taskBareRepoPath && base_ref) {
5126
+ if (taskCheckoutPath && taskBareRepoPath) {
5127
+ if (!base_ref) {
5128
+ logWarn(
5129
+ ` Warning: task ${task_id} has no base_ref \u2014 deriving default branch from worktree`
5130
+ );
5131
+ }
5058
5132
  try {
5059
5133
  const ghAvailable = isGhAvailable();
5060
5134
  const maxDiffBytes = reviewDeps.maxDiffSizeKb ? reviewDeps.maxDiffSizeKb * 1024 : void 0;
@@ -5077,23 +5151,16 @@ async function handleTask(client, agentId, task, reviewDeps, consumptionDeps, ag
5077
5151
  diffContent = gitDiff;
5078
5152
  log(` Diff generated via git (${Math.round(diffContent.length / 1024)}KB)`);
5079
5153
  } catch (err) {
5154
+ const message = err.message;
5080
5155
  if (err instanceof DiffTooLargeError) {
5081
- logError(` ${err.message}`);
5082
- await safeReject(
5083
- client,
5084
- task_id,
5085
- agentId,
5086
- `Cannot access diff: ${err.message}`,
5087
- logger
5088
- );
5089
- return { diffFetchFailed: true };
5156
+ logError(` ${message}`);
5157
+ } else {
5158
+ logError(` git diff failed for task ${task_id}: ${message}`);
5090
5159
  }
5091
- logWarn(
5092
- ` Warning: git diff failed (${err.message}) \u2014 falling back to API fetch`
5093
- );
5160
+ await safeReject(client, task_id, agentId, `Cannot access diff: ${message}`, logger);
5161
+ return { diffFetchFailed: true };
5094
5162
  }
5095
- }
5096
- if (!diffContent) {
5163
+ } else {
5097
5164
  try {
5098
5165
  const result = await fetchDiff(diff_url, owner, repo, pr_number, {
5099
5166
  githubToken: client.currentToken,
@@ -5736,7 +5803,7 @@ function sleep2(ms, signal) {
5736
5803
  async function startAgent(agentId, platformUrl, agentInfo, reviewDeps, consumptionDeps, options) {
5737
5804
  const client = new ApiClient(platformUrl, {
5738
5805
  authToken: options?.authToken,
5739
- cliVersion: "0.24.3",
5806
+ cliVersion: "0.25.1",
5740
5807
  versionOverride: options?.versionOverride,
5741
5808
  onTokenRefresh: options?.onTokenRefresh
5742
5809
  });
@@ -6101,7 +6168,7 @@ async function startBatchAgents(config, agents, pollIntervalMs, oauthToken, opti
6101
6168
  const { versionOverride, verbose, instancesOverride, agentOwner, userOrgs } = options;
6102
6169
  const client = new ApiClient(config.platformUrl, {
6103
6170
  authToken: oauthToken,
6104
- cliVersion: "0.24.3",
6171
+ cliVersion: "0.25.1",
6105
6172
  versionOverride,
6106
6173
  onTokenRefresh: () => getValidToken(config.platformUrl, { configPath: config.authFile })
6107
6174
  });
@@ -6307,6 +6374,7 @@ async function startAgentRouter() {
6307
6374
  const reviewDeps = {
6308
6375
  commandTemplate: commandTemplate ?? "",
6309
6376
  maxDiffSizeKb: config.maxDiffSizeKb,
6377
+ maxSummaryInputKb: config.maxSummaryInputKb,
6310
6378
  maxRepoSizeMb: config.maxRepoSizeMb,
6311
6379
  codebaseDir,
6312
6380
  livenessTimeoutMs: agentConfig?.livenessTimeout != null ? agentConfig.livenessTimeout * 1e3 : void 0
@@ -6373,6 +6441,7 @@ function startAgentByIndex(config, agentIndex, pollIntervalMs, oauthToken, versi
6373
6441
  const reviewDeps = {
6374
6442
  commandTemplate,
6375
6443
  maxDiffSizeKb: config.maxDiffSizeKb,
6444
+ maxSummaryInputKb: config.maxSummaryInputKb,
6376
6445
  maxRepoSizeMb: config.maxRepoSizeMb,
6377
6446
  codebaseDir,
6378
6447
  livenessTimeoutMs: agentConfig?.livenessTimeout != null ? agentConfig.livenessTimeout * 1e3 : void 0
@@ -6450,7 +6519,7 @@ agentCommand.command("start").description("Start agents in polling mode").option
6450
6519
  }
6451
6520
  config = loadConfig();
6452
6521
  }
6453
- console.log(formatVersionBanner("0.24.3", "c08a9b3"));
6522
+ console.log(formatVersionBanner("0.25.1", "085f9a8"));
6454
6523
  if (config.agents && config.agents.length > 0) {
6455
6524
  const toolEntries = config.agents.map((a) => ({
6456
6525
  tool: a.tool,
@@ -7272,7 +7341,7 @@ var statusCommand = new Command4("status").description("Show agent config, conne
7272
7341
  });
7273
7342
 
7274
7343
  // src/index.ts
7275
- var program = new Command5().name("opencara").description("OpenCara \u2014 distributed AI code review agent").version(`${"0.24.3"} (${"c08a9b3"})`);
7344
+ var program = new Command5().name("opencara").description("OpenCara \u2014 distributed AI code review agent").version(`${"0.25.1"} (${"085f9a8"})`);
7276
7345
  program.addCommand(agentCommand);
7277
7346
  program.addCommand(authCommand());
7278
7347
  program.addCommand(dedupCommand());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencara",
3
- "version": "0.24.3",
3
+ "version": "0.25.1",
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",