opencara 0.7.1 → 0.8.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/index.js +135 -55
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -14,9 +14,10 @@ import * as os from "os";
14
14
  import { parse, stringify } from "yaml";
15
15
  var DEFAULT_PLATFORM_URL = "https://api.opencara.dev";
16
16
  var CONFIG_DIR = path.join(os.homedir(), ".opencara");
17
- var CONFIG_FILE = path.join(CONFIG_DIR, "config.yml");
17
+ var CONFIG_FILE = process.env.OPENCARA_CONFIG && process.env.OPENCARA_CONFIG.trim() ? path.resolve(process.env.OPENCARA_CONFIG) : path.join(CONFIG_DIR, "config.yml");
18
18
  function ensureConfigDir() {
19
- fs.mkdirSync(CONFIG_DIR, { recursive: true });
19
+ const dir = path.dirname(CONFIG_FILE);
20
+ fs.mkdirSync(dir, { recursive: true });
20
21
  }
21
22
  var DEFAULT_MAX_DIFF_SIZE_KB = 100;
22
23
  function parseLimits(data) {
@@ -96,6 +97,7 @@ function parseAnonymousAgents(data) {
96
97
  model: obj.model,
97
98
  tool: obj.tool
98
99
  };
100
+ if (typeof obj.name === "string") anon.name = obj.name;
99
101
  if (obj.repo_config && typeof obj.repo_config === "object") {
100
102
  const rc = obj.repo_config;
101
103
  if (typeof rc.mode === "string" && VALID_REPO_MODES.includes(rc.mode)) {
@@ -127,6 +129,7 @@ function parseAgents(data) {
127
129
  continue;
128
130
  }
129
131
  const agent = { model: obj.model, tool: obj.tool };
132
+ if (typeof obj.name === "string") agent.name = obj.name;
130
133
  if (typeof obj.command === "string") agent.command = obj.command;
131
134
  const agentLimits = parseLimits(obj);
132
135
  if (agentLimits) agent.limits = agentLimits;
@@ -192,6 +195,9 @@ function saveConfig(config) {
192
195
  model: a.model,
193
196
  tool: a.tool
194
197
  };
198
+ if (a.name) {
199
+ entry.name = a.name;
200
+ }
195
201
  if (a.repoConfig) {
196
202
  entry.repo_config = a.repoConfig;
197
203
  }
@@ -293,17 +299,17 @@ function calculateDelay(attempt, options = DEFAULT_RECONNECT_OPTIONS) {
293
299
  return base;
294
300
  }
295
301
  function sleep(ms) {
296
- return new Promise((resolve) => setTimeout(resolve, ms));
302
+ return new Promise((resolve2) => setTimeout(resolve2, ms));
297
303
  }
298
304
 
299
305
  // src/commands/login.ts
300
306
  function promptYesNo(question) {
301
307
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
302
- return new Promise((resolve) => {
308
+ return new Promise((resolve2) => {
303
309
  rl.question(question, (answer) => {
304
310
  rl.close();
305
311
  const normalized = answer.trim().toLowerCase();
306
- resolve(normalized === "" || normalized === "y" || normalized === "yes");
312
+ resolve2(normalized === "" || normalized === "y" || normalized === "yes");
307
313
  });
308
314
  });
309
315
  }
@@ -428,24 +434,56 @@ var DEFAULT_REGISTRY = {
428
434
  }
429
435
  ],
430
436
  models: [
431
- { name: "claude-opus-4-6", displayName: "Claude Opus 4.6", tools: ["claude"] },
437
+ {
438
+ name: "claude-opus-4-6",
439
+ displayName: "Claude Opus 4.6",
440
+ tools: ["claude"],
441
+ defaultReputation: 0.8
442
+ },
432
443
  {
433
444
  name: "claude-opus-4-6[1m]",
434
445
  displayName: "Claude Opus 4.6 (1M context)",
435
- tools: ["claude"]
446
+ tools: ["claude"],
447
+ defaultReputation: 0.8
448
+ },
449
+ {
450
+ name: "claude-sonnet-4-6",
451
+ displayName: "Claude Sonnet 4.6",
452
+ tools: ["claude"],
453
+ defaultReputation: 0.7
436
454
  },
437
- { name: "claude-sonnet-4-6", displayName: "Claude Sonnet 4.6", tools: ["claude"] },
438
455
  {
439
456
  name: "claude-sonnet-4-6[1m]",
440
457
  displayName: "Claude Sonnet 4.6 (1M context)",
441
- tools: ["claude"]
458
+ tools: ["claude"],
459
+ defaultReputation: 0.7
442
460
  },
443
- { name: "gpt-5-codex", displayName: "GPT-5 Codex", tools: ["codex"] },
444
- { name: "gemini-2.5-pro", displayName: "Gemini 2.5 Pro", tools: ["gemini"] },
445
- { name: "qwen3.5-plus", displayName: "Qwen 3.5 Plus", tools: ["qwen"] },
446
- { name: "glm-5", displayName: "GLM-5", tools: ["qwen"] },
447
- { name: "kimi-k2.5", displayName: "Kimi K2.5", tools: ["qwen"] },
448
- { name: "minimax-m2.5", displayName: "Minimax M2.5", tools: ["qwen"] }
461
+ {
462
+ name: "gpt-5-codex",
463
+ displayName: "GPT-5 Codex",
464
+ tools: ["codex"],
465
+ defaultReputation: 0.7
466
+ },
467
+ {
468
+ name: "gemini-2.5-pro",
469
+ displayName: "Gemini 2.5 Pro",
470
+ tools: ["gemini"],
471
+ defaultReputation: 0.7
472
+ },
473
+ {
474
+ name: "qwen3.5-plus",
475
+ displayName: "Qwen 3.5 Plus",
476
+ tools: ["qwen"],
477
+ defaultReputation: 0.6
478
+ },
479
+ { name: "glm-5", displayName: "GLM-5", tools: ["qwen"], defaultReputation: 0.5 },
480
+ { name: "kimi-k2.5", displayName: "Kimi K2.5", tools: ["qwen"], defaultReputation: 0.5 },
481
+ {
482
+ name: "minimax-m2.5",
483
+ displayName: "Minimax M2.5",
484
+ tools: ["qwen"],
485
+ defaultReputation: 0.5
486
+ }
449
487
  ]
450
488
  };
451
489
 
@@ -554,7 +592,7 @@ function executeTool(commandTemplate, prompt, timeoutMs, signal, vars) {
554
592
  const promptViaArg = commandTemplate.includes("${PROMPT}");
555
593
  const allVars = { ...vars, PROMPT: prompt };
556
594
  const { command, args } = parseCommandTemplate(commandTemplate, allVars);
557
- return new Promise((resolve, reject) => {
595
+ return new Promise((resolve2, reject) => {
558
596
  if (signal?.aborted) {
559
597
  reject(new ToolTimeoutError("Tool execution aborted"));
560
598
  return;
@@ -626,7 +664,7 @@ function executeTool(commandTemplate, prompt, timeoutMs, signal, vars) {
626
664
  console.warn(`Tool stderr: ${stderr.slice(0, MAX_STDERR_LENGTH)}`);
627
665
  }
628
666
  const usage2 = parseTokenUsage(stdout, stderr);
629
- resolve({ stdout, stderr, tokensUsed: usage2.tokens, tokensParsed: usage2.parsed });
667
+ resolve2({ stdout, stderr, tokensUsed: usage2.tokens, tokensParsed: usage2.parsed });
630
668
  return;
631
669
  }
632
670
  const errMsg = stderr ? `Tool "${command}" failed (exit code ${code}): ${stderr.slice(0, MAX_STDERR_LENGTH)}` : `Tool "${command}" failed with exit code ${code}`;
@@ -634,7 +672,7 @@ function executeTool(commandTemplate, prompt, timeoutMs, signal, vars) {
634
672
  return;
635
673
  }
636
674
  const usage = parseTokenUsage(stdout, stderr);
637
- resolve({ stdout, stderr, tokensUsed: usage.tokens, tokensParsed: usage.parsed });
675
+ resolve2({ stdout, stderr, tokensUsed: usage.tokens, tokensParsed: usage.parsed });
638
676
  });
639
677
  });
640
678
  }
@@ -884,6 +922,7 @@ function formatTable(agents, trustLabels) {
884
922
  }
885
923
  const header = [
886
924
  "ID".padEnd(38),
925
+ "Name".padEnd(20),
887
926
  "Model".padEnd(22),
888
927
  "Tool".padEnd(16),
889
928
  "Status".padEnd(10),
@@ -892,8 +931,16 @@ function formatTable(agents, trustLabels) {
892
931
  console.log(header);
893
932
  for (const a of agents) {
894
933
  const trust = trustLabels?.get(a.id) ?? "--";
934
+ const name = a.displayName ?? "--";
895
935
  console.log(
896
- [a.id.padEnd(38), a.model.padEnd(22), a.tool.padEnd(16), a.status.padEnd(10), trust].join("")
936
+ [
937
+ a.id.padEnd(38),
938
+ name.padEnd(20),
939
+ a.model.padEnd(22),
940
+ a.tool.padEnd(16),
941
+ a.status.padEnd(10),
942
+ trust
943
+ ].join("")
897
944
  );
898
945
  }
899
946
  }
@@ -908,6 +955,10 @@ function startAgent(agentId, platformUrl, apiKey, reviewDeps, consumptionDeps, o
908
955
  const verbose = options?.verbose ?? false;
909
956
  const stabilityThreshold = options?.stabilityThresholdMs ?? CONNECTION_STABILITY_THRESHOLD_MS;
910
957
  const repoConfig = options?.repoConfig;
958
+ const displayName = options?.displayName;
959
+ const prefix = options?.label ? `[${options.label}]` : "";
960
+ const log = (...args) => console.log(...prefix ? [prefix, ...args] : args);
961
+ const logError = (...args) => console.error(...prefix ? [prefix, ...args] : args);
911
962
  let attempt = 0;
912
963
  let intentionalClose = false;
913
964
  let heartbeatTimer = null;
@@ -939,7 +990,7 @@ function startAgent(agentId, platformUrl, apiKey, reviewDeps, consumptionDeps, o
939
990
  clearStabilityTimer();
940
991
  clearWsPingTimer();
941
992
  if (currentWs) currentWs.close();
942
- console.log("Disconnected.");
993
+ log("Disconnected.");
943
994
  process.exit(0);
944
995
  }
945
996
  process.once("SIGINT", shutdown);
@@ -951,13 +1002,13 @@ function startAgent(agentId, platformUrl, apiKey, reviewDeps, consumptionDeps, o
951
1002
  function resetHeartbeatTimer() {
952
1003
  clearHeartbeatTimer();
953
1004
  heartbeatTimer = setTimeout(() => {
954
- console.log("No heartbeat received in 90s. Reconnecting...");
1005
+ log("No heartbeat received in 90s. Reconnecting...");
955
1006
  ws.terminate();
956
1007
  }, HEARTBEAT_TIMEOUT_MS);
957
1008
  }
958
1009
  ws.on("open", () => {
959
1010
  connectionOpenedAt = Date.now();
960
- console.log("Connected to platform.");
1011
+ log("Connected to platform.");
961
1012
  resetHeartbeatTimer();
962
1013
  clearWsPingTimer();
963
1014
  wsPingTimer = setInterval(() => {
@@ -969,12 +1020,12 @@ function startAgent(agentId, platformUrl, apiKey, reviewDeps, consumptionDeps, o
969
1020
  }
970
1021
  }, WS_PING_INTERVAL_MS);
971
1022
  if (verbose) {
972
- console.log(`[verbose] Connection opened at ${new Date(connectionOpenedAt).toISOString()}`);
1023
+ log(`[verbose] Connection opened at ${new Date(connectionOpenedAt).toISOString()}`);
973
1024
  }
974
1025
  clearStabilityTimer();
975
1026
  stabilityTimer = setTimeout(() => {
976
1027
  if (verbose) {
977
- console.log(
1028
+ log(
978
1029
  `[verbose] Connection stable for ${stabilityThreshold / 1e3}s \u2014 resetting reconnect counter`
979
1030
  );
980
1031
  }
@@ -988,7 +1039,17 @@ function startAgent(agentId, platformUrl, apiKey, reviewDeps, consumptionDeps, o
988
1039
  } catch {
989
1040
  return;
990
1041
  }
991
- handleMessage(ws, msg, resetHeartbeatTimer, reviewDeps, consumptionDeps, verbose, repoConfig);
1042
+ handleMessage(
1043
+ ws,
1044
+ msg,
1045
+ resetHeartbeatTimer,
1046
+ reviewDeps,
1047
+ consumptionDeps,
1048
+ verbose,
1049
+ repoConfig,
1050
+ displayName,
1051
+ prefix
1052
+ );
992
1053
  });
993
1054
  ws.on("close", (code, reason) => {
994
1055
  if (intentionalClose) return;
@@ -999,14 +1060,14 @@ function startAgent(agentId, platformUrl, apiKey, reviewDeps, consumptionDeps, o
999
1060
  if (connectionOpenedAt) {
1000
1061
  const lifetimeMs = Date.now() - connectionOpenedAt;
1001
1062
  const lifetimeSec = (lifetimeMs / 1e3).toFixed(1);
1002
- console.log(
1063
+ log(
1003
1064
  `Disconnected (code=${code}, reason=${reason.toString()}). Connection was alive for ${lifetimeSec}s.`
1004
1065
  );
1005
1066
  } else {
1006
- console.log(`Disconnected (code=${code}, reason=${reason.toString()}).`);
1067
+ log(`Disconnected (code=${code}, reason=${reason.toString()}).`);
1007
1068
  }
1008
1069
  if (code === 4002) {
1009
- console.log("Connection replaced by server \u2014 not reconnecting.");
1070
+ log("Connection replaced by server \u2014 not reconnecting.");
1010
1071
  return;
1011
1072
  }
1012
1073
  connectionOpenedAt = null;
@@ -1014,18 +1075,18 @@ function startAgent(agentId, platformUrl, apiKey, reviewDeps, consumptionDeps, o
1014
1075
  });
1015
1076
  ws.on("pong", () => {
1016
1077
  if (verbose) {
1017
- console.log(`[verbose] WS pong received at ${(/* @__PURE__ */ new Date()).toISOString()}`);
1078
+ log(`[verbose] WS pong received at ${(/* @__PURE__ */ new Date()).toISOString()}`);
1018
1079
  }
1019
1080
  });
1020
1081
  ws.on("error", (err) => {
1021
- console.error(`WebSocket error: ${err.message}`);
1082
+ logError(`WebSocket error: ${err.message}`);
1022
1083
  });
1023
1084
  }
1024
1085
  async function reconnect() {
1025
1086
  const delay = calculateDelay(attempt, DEFAULT_RECONNECT_OPTIONS);
1026
1087
  const delaySec = (delay / 1e3).toFixed(1);
1027
1088
  attempt++;
1028
- console.log(`Reconnecting in ${delaySec}s... (attempt ${attempt})`);
1089
+ log(`Reconnecting in ${delaySec}s... (attempt ${attempt})`);
1029
1090
  await sleep(delay);
1030
1091
  connect();
1031
1092
  }
@@ -1038,16 +1099,17 @@ function trySend(ws, data) {
1038
1099
  console.error("Failed to send message \u2014 WebSocket may be closed");
1039
1100
  }
1040
1101
  }
1041
- async function logPostReviewStats(type, verdict, tokensUsed, tokensEstimated, consumptionDeps) {
1102
+ async function logPostReviewStats(type, verdict, tokensUsed, tokensEstimated, consumptionDeps, logPrefix) {
1103
+ const pfx = logPrefix ? `${logPrefix} ` : "";
1042
1104
  const estimateTag = tokensEstimated ? " ~" : " ";
1043
1105
  if (!consumptionDeps) {
1044
1106
  if (verdict) {
1045
1107
  console.log(
1046
- `${type} complete: ${verdict} (${estimateTag}${tokensUsed} tokens${tokensEstimated ? ", estimated" : ""})`
1108
+ `${pfx}${type} complete: ${verdict} (${estimateTag}${tokensUsed} tokens${tokensEstimated ? ", estimated" : ""})`
1047
1109
  );
1048
1110
  } else {
1049
1111
  console.log(
1050
- `${type} complete (${estimateTag}${tokensUsed} tokens${tokensEstimated ? ", estimated" : ""})`
1112
+ `${pfx}${type} complete (${estimateTag}${tokensUsed} tokens${tokensEstimated ? ", estimated" : ""})`
1051
1113
  );
1052
1114
  }
1053
1115
  return;
@@ -1055,37 +1117,43 @@ async function logPostReviewStats(type, verdict, tokensUsed, tokensEstimated, co
1055
1117
  recordSessionUsage(consumptionDeps.session, tokensUsed);
1056
1118
  if (verdict) {
1057
1119
  console.log(
1058
- `${type} complete: ${verdict} (${estimateTag}${tokensUsed.toLocaleString()} tokens${tokensEstimated ? ", estimated" : ""})`
1120
+ `${pfx}${type} complete: ${verdict} (${estimateTag}${tokensUsed.toLocaleString()} tokens${tokensEstimated ? ", estimated" : ""})`
1059
1121
  );
1060
1122
  } else {
1061
1123
  console.log(
1062
- `${type} complete (${estimateTag}${tokensUsed.toLocaleString()} tokens${tokensEstimated ? ", estimated" : ""})`
1124
+ `${pfx}${type} complete (${estimateTag}${tokensUsed.toLocaleString()} tokens${tokensEstimated ? ", estimated" : ""})`
1063
1125
  );
1064
1126
  }
1065
- console.log(formatPostReviewStats(tokensUsed, consumptionDeps.session, consumptionDeps.limits));
1127
+ console.log(
1128
+ `${pfx}${formatPostReviewStats(tokensUsed, consumptionDeps.session, consumptionDeps.limits)}`
1129
+ );
1066
1130
  }
1067
- function handleMessage(ws, msg, resetHeartbeat, reviewDeps, consumptionDeps, verbose, repoConfig) {
1131
+ function handleMessage(ws, msg, resetHeartbeat, reviewDeps, consumptionDeps, verbose, repoConfig, displayName, logPrefix) {
1132
+ const pfx = logPrefix ? `${logPrefix} ` : "";
1068
1133
  switch (msg.type) {
1069
1134
  case "connected":
1070
- console.log(`Authenticated. Protocol v${msg.version ?? "unknown"}`);
1135
+ console.log(`${pfx}Authenticated. Protocol v${msg.version ?? "unknown"}`);
1071
1136
  trySend(ws, {
1072
1137
  type: "agent_preferences",
1073
1138
  id: crypto2.randomUUID(),
1074
1139
  timestamp: Date.now(),
1140
+ ...displayName ? { displayName } : {},
1075
1141
  repoConfig: repoConfig ?? { mode: "all" }
1076
1142
  });
1077
1143
  break;
1078
1144
  case "heartbeat_ping":
1079
1145
  ws.send(JSON.stringify({ type: "heartbeat_pong", timestamp: Date.now() }));
1080
1146
  if (verbose) {
1081
- console.log(`[verbose] Heartbeat ping received, pong sent at ${(/* @__PURE__ */ new Date()).toISOString()}`);
1147
+ console.log(
1148
+ `${pfx}[verbose] Heartbeat ping received, pong sent at ${(/* @__PURE__ */ new Date()).toISOString()}`
1149
+ );
1082
1150
  }
1083
1151
  if (resetHeartbeat) resetHeartbeat();
1084
1152
  break;
1085
1153
  case "review_request": {
1086
1154
  const request = msg;
1087
1155
  console.log(
1088
- `Review request: task ${request.taskId} for ${request.project.owner}/${request.project.repo}#${request.pr.number}`
1156
+ `${pfx}Review request: task ${request.taskId} for ${request.project.owner}/${request.project.repo}#${request.pr.number}`
1089
1157
  );
1090
1158
  if (!reviewDeps) {
1091
1159
  ws.send(
@@ -1113,7 +1181,7 @@ function handleMessage(ws, msg, resetHeartbeat, reviewDeps, consumptionDeps, ver
1113
1181
  taskId: request.taskId,
1114
1182
  reason: limitResult.reason ?? "consumption_limit_exceeded"
1115
1183
  });
1116
- console.log(`Review rejected: ${limitResult.reason}`);
1184
+ console.log(`${pfx}Review rejected: ${limitResult.reason}`);
1117
1185
  return;
1118
1186
  }
1119
1187
  }
@@ -1145,7 +1213,8 @@ function handleMessage(ws, msg, resetHeartbeat, reviewDeps, consumptionDeps, ver
1145
1213
  result.verdict,
1146
1214
  result.tokensUsed,
1147
1215
  result.tokensEstimated,
1148
- consumptionDeps
1216
+ consumptionDeps,
1217
+ logPrefix
1149
1218
  );
1150
1219
  } catch (err) {
1151
1220
  if (err instanceof DiffTooLargeError) {
@@ -1165,7 +1234,7 @@ function handleMessage(ws, msg, resetHeartbeat, reviewDeps, consumptionDeps, ver
1165
1234
  error: err instanceof Error ? err.message : "Unknown error"
1166
1235
  });
1167
1236
  }
1168
- console.error("Review failed:", err);
1237
+ console.error(`${pfx}Review failed:`, err);
1169
1238
  }
1170
1239
  })();
1171
1240
  break;
@@ -1173,7 +1242,7 @@ function handleMessage(ws, msg, resetHeartbeat, reviewDeps, consumptionDeps, ver
1173
1242
  case "summary_request": {
1174
1243
  const summaryRequest = msg;
1175
1244
  console.log(
1176
- `Summary request: task ${summaryRequest.taskId} for ${summaryRequest.project.owner}/${summaryRequest.project.repo}#${summaryRequest.pr.number} (${summaryRequest.reviews.length} reviews)`
1245
+ `${pfx}Summary request: task ${summaryRequest.taskId} for ${summaryRequest.project.owner}/${summaryRequest.project.repo}#${summaryRequest.pr.number} (${summaryRequest.reviews.length} reviews)`
1177
1246
  );
1178
1247
  if (!reviewDeps) {
1179
1248
  trySend(ws, {
@@ -1199,7 +1268,7 @@ function handleMessage(ws, msg, resetHeartbeat, reviewDeps, consumptionDeps, ver
1199
1268
  taskId: summaryRequest.taskId,
1200
1269
  reason: limitResult.reason ?? "consumption_limit_exceeded"
1201
1270
  });
1202
- console.log(`Summary rejected: ${limitResult.reason}`);
1271
+ console.log(`${pfx}Summary rejected: ${limitResult.reason}`);
1203
1272
  return;
1204
1273
  }
1205
1274
  }
@@ -1230,7 +1299,8 @@ function handleMessage(ws, msg, resetHeartbeat, reviewDeps, consumptionDeps, ver
1230
1299
  void 0,
1231
1300
  result.tokensUsed,
1232
1301
  result.tokensEstimated,
1233
- consumptionDeps
1302
+ consumptionDeps,
1303
+ logPrefix
1234
1304
  );
1235
1305
  } catch (err) {
1236
1306
  if (err instanceof InputTooLargeError) {
@@ -1250,13 +1320,13 @@ function handleMessage(ws, msg, resetHeartbeat, reviewDeps, consumptionDeps, ver
1250
1320
  error: err instanceof Error ? err.message : "Summary failed"
1251
1321
  });
1252
1322
  }
1253
- console.error("Summary failed:", err);
1323
+ console.error(`${pfx}Summary failed:`, err);
1254
1324
  }
1255
1325
  })();
1256
1326
  break;
1257
1327
  }
1258
1328
  case "error":
1259
- console.error(`Platform error: ${msg.code ?? "unknown"}`);
1329
+ console.error(`${pfx}Platform error: ${msg.code ?? "unknown"}`);
1260
1330
  if (msg.code === "auth_revoked") process.exit(1);
1261
1331
  break;
1262
1332
  default:
@@ -1271,6 +1341,9 @@ async function syncAgentToServer(client, serverAgents, localAgent) {
1271
1341
  return { agentId: existing.id, created: false };
1272
1342
  }
1273
1343
  const body = { model: localAgent.model, tool: localAgent.tool };
1344
+ if (localAgent.name) {
1345
+ body.displayName = localAgent.name;
1346
+ }
1274
1347
  if (localAgent.repos) {
1275
1348
  body.repoConfig = localAgent.repos;
1276
1349
  }
@@ -1550,6 +1623,7 @@ agentCommand.command("start [agentIdOrModel]").description("Connect agent to pla
1550
1623
  startAgent(entry.agentId, config.platformUrl, entry.apiKey, reviewDeps2, consumptionDeps2, {
1551
1624
  verbose: opts.verbose,
1552
1625
  stabilityThresholdMs,
1626
+ displayName: entry.name,
1553
1627
  repoConfig: entry.repoConfig
1554
1628
  });
1555
1629
  return;
@@ -1659,11 +1733,13 @@ agentCommand.command("start [agentIdOrModel]").description("Connect agent to pla
1659
1733
  limits: resolveAgentLimits(selected.local.limits, config.limits),
1660
1734
  session: createSessionTracker()
1661
1735
  };
1662
- console.log(`Starting agent ${selected.local.model} (${agentId2})...`);
1736
+ const label = selected.local.name || selected.local.model || "unnamed";
1737
+ console.log(`Starting agent ${label} (${agentId2})...`);
1663
1738
  startAgent(agentId2, config.platformUrl, apiKey2, reviewDeps2, consumptionDeps2, {
1664
1739
  verbose: opts.verbose,
1665
1740
  stabilityThresholdMs,
1666
- repoConfig: selected.local.repos
1741
+ repoConfig: selected.local.repos,
1742
+ label
1667
1743
  });
1668
1744
  startedCount++;
1669
1745
  }
@@ -1692,11 +1768,14 @@ agentCommand.command("start [agentIdOrModel]").description("Connect agent to pla
1692
1768
  limits: config.limits,
1693
1769
  session: createSessionTracker()
1694
1770
  };
1695
- console.log(`Starting anonymous agent ${anon.model} (${anon.agentId})...`);
1771
+ const anonLabel = anon.name || anon.model || "anonymous";
1772
+ console.log(`Starting anonymous agent ${anonLabel} (${anon.agentId})...`);
1696
1773
  startAgent(anon.agentId, config.platformUrl, anon.apiKey, reviewDeps2, consumptionDeps2, {
1697
1774
  verbose: opts.verbose,
1698
1775
  stabilityThresholdMs,
1699
- repoConfig: anon.repoConfig
1776
+ displayName: anon.name,
1777
+ repoConfig: anon.repoConfig,
1778
+ label: anonLabel
1700
1779
  });
1701
1780
  startedCount++;
1702
1781
  }
@@ -1755,7 +1834,8 @@ agentCommand.command("start [agentIdOrModel]").description("Connect agent to pla
1755
1834
  console.log(`Starting agent ${agentId}...`);
1756
1835
  startAgent(agentId, config.platformUrl, apiKey, reviewDeps, consumptionDeps, {
1757
1836
  verbose: opts.verbose,
1758
- stabilityThresholdMs
1837
+ stabilityThresholdMs,
1838
+ label: agentId
1759
1839
  });
1760
1840
  }
1761
1841
  );
@@ -1936,7 +2016,7 @@ var statsCommand = new Command3("stats").description("Display agent dashboard: t
1936
2016
  });
1937
2017
 
1938
2018
  // src/index.ts
1939
- var program = new Command4().name("opencara").description("OpenCara \u2014 distributed AI code review agent").version("0.7.0");
2019
+ var program = new Command4().name("opencara").description("OpenCara \u2014 distributed AI code review agent").version("0.8.0");
1940
2020
  program.addCommand(loginCommand);
1941
2021
  program.addCommand(agentCommand);
1942
2022
  program.addCommand(statsCommand);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencara",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",