@link-assistant/hive-mind 2.11.6 → 2.11.7

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 2.11.7
4
+
5
+ ### Patch Changes
6
+
7
+ - 815a63c: Keep context/cost budget statistics out of the working session summary (issue #2132).
8
+
9
+ Cost estimation and token/context usage are now published only in the working
10
+ session log comment, and only when `--attach-logs` is enabled — previously every
11
+ session posted the identical blocks twice, and the summary could publish them even
12
+ with log attachment disabled. The per-session budget stats derivation used by the
13
+ top-level run, watch iterations and auto-restart-until-mergeable iterations is now
14
+ a single shared implementation.
15
+
3
16
  ## 2.11.6
4
17
 
5
18
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "2.11.6",
3
+ "version": "2.11.7",
4
4
  "description": "AI-powered issue solver and hive mind for collaborative problem solving",
5
5
  "main": "src/hive.mjs",
6
6
  "type": "module",
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Issue #2132: single source of truth for *where* context/cost budget statistics
5
+ * may be published.
6
+ *
7
+ * Rules encoded here:
8
+ * 1. Budget stats belong to the working session **log** comment only. The
9
+ * "Working session summary" comment reports what the AI did, never how many
10
+ * tokens or dollars it took (see `attachSolutionSummary`).
11
+ * 2. `--attach-logs` disabled ⇒ no log comment ⇒ no published budget stats.
12
+ * `--tokens-budget-stats` alone is not enough to publish them to GitHub; it
13
+ * only controls the local terminal rendering of the same facts.
14
+ *
15
+ * Keeping this in one module means the top-level run, the watch loop and the
16
+ * auto-restart-until-mergeable loop cannot drift apart again.
17
+ *
18
+ * @see https://github.com/link-assistant/hive-mind/issues/2132
19
+ */
20
+
21
+ /** Whether `--attach-logs` is enabled (both camelCase and kebab-case forms). */
22
+ export const isAttachLogsEnabled = argv => !!(argv && (argv.attachLogs || argv['attach-logs']));
23
+
24
+ /** Whether `--tokens-budget-stats` is enabled (both camelCase and kebab-case forms). */
25
+ export const isTokensBudgetStatsEnabled = argv => !!(argv && (argv.tokensBudgetStats || argv['tokens-budget-stats']));
26
+
27
+ /**
28
+ * Whether context/cost budget statistics may be published to GitHub for this run.
29
+ * Requires BOTH `--tokens-budget-stats` and `--attach-logs`.
30
+ */
31
+ export const shouldPublishBudgetStats = argv => isTokensBudgetStatsEnabled(argv) && isAttachLogsEnabled(argv);
@@ -1052,19 +1052,20 @@ Once the billing issue is resolved, you can re-run the CI checks or push a new c
1052
1052
  latestAnthropicCost = toolResult.anthropicTotalCostUSD;
1053
1053
  }
1054
1054
 
1055
- // Issue #1508: Compute budget stats for auto-restart-until-mergeable log comment
1056
- let autoMergeBudgetStatsData = null;
1057
- if (argv.tokensBudgetStats && latestSessionId && tempDir) {
1058
- try {
1059
- const { calculateSessionTokens } = await import('./claude.lib.mjs');
1060
- const tokenUsage = await calculateSessionTokens(latestSessionId, tempDir, toolResult.resultModelUsage);
1061
- if (tokenUsage) {
1062
- autoMergeBudgetStatsData = { tokenUsage, streamTokenUsage: toolResult.streamTokenUsage || null };
1063
- }
1064
- } catch (budgetError) {
1065
- if (argv.verbose) await log(` ⚠️ Could not calculate budget stats: ${budgetError.message}`, { verbose: true });
1066
- }
1067
- }
1055
+ // Issue #1508: Compute budget stats for auto-restart-until-mergeable log comment.
1056
+ // Issue #2132: shared with the top-level run and the watch loop via
1057
+ // buildSessionBudgetStatsData, so every working session derives its own
1058
+ // stats the same way (and skips them when `--attach-logs` is disabled).
1059
+ const { buildSessionBudgetStatsData } = await import('./solve.results.lib.mjs');
1060
+ const autoMergeBudgetStatsData = await buildSessionBudgetStatsData({
1061
+ argv,
1062
+ sessionId: latestSessionId,
1063
+ tempDir,
1064
+ resultModelUsage: toolResult.resultModelUsage,
1065
+ streamTokenUsage: toolResult.streamTokenUsage || null,
1066
+ subAgentCalls: toolResult.subAgentCalls || null,
1067
+ pricingInfo: toolResult.pricingInfo || null,
1068
+ });
1068
1069
 
1069
1070
  // Issue #1761: Post the working session **summary** BEFORE uploading
1070
1071
  // the working session **log** so the summary always appears above
@@ -1090,8 +1091,6 @@ Once the billing issue is resolved, you can re-run the CI checks or push a new c
1090
1091
  prNumber,
1091
1092
  issueNumber,
1092
1093
  success: true,
1093
- publicPricingEstimate: toolResult.publicPricingEstimate,
1094
- anthropicTotalCostUSD: latestAnthropicCost,
1095
1094
  pricingInfo: toolResult.pricingInfo,
1096
1095
  budgetStatsData: autoMergeBudgetStatsData,
1097
1096
  });
@@ -136,7 +136,7 @@ export const SOLVE_OPTION_DEFINITIONS = {
136
136
  },
137
137
  'attach-logs': {
138
138
  type: 'boolean',
139
- description: 'Upload the solution draft log file to the Pull Request on completion (⚠️ WARNING: May expose sensitive data)',
139
+ description: "Upload the solution draft log file to the Pull Request on completion, together with that working session's cost estimation and context/token budget statistics (⚠️ WARNING: May expose sensitive data). Disabled means no log comment and no published budget statistics.",
140
140
  default: false,
141
141
  },
142
142
  'dangerously-skip-output-sanitization': {
@@ -505,7 +505,7 @@ export const SOLVE_OPTION_DEFINITIONS = {
505
505
  },
506
506
  'tokens-budget-stats': {
507
507
  type: 'boolean',
508
- description: 'Show detailed token budget statistics including context window usage and ratios (enabled by default, use --no-tokens-budget-stats to disable). Supported for --tool claude, --tool codex, and any tool that returns detailed token usage.',
508
+ description: 'Show detailed token budget statistics including context window usage and ratios (enabled by default, use --no-tokens-budget-stats to disable). Shown in the terminal; publishing them to the pull request additionally requires --attach-logs, and they appear only in the working session log comment (never in the working session summary). Supported for --tool claude, --tool codex, and any tool that returns detailed token usage.',
509
509
  default: true,
510
510
  },
511
511
  'prompt-issue-reporting': {
package/src/solve.mjs CHANGED
@@ -1238,8 +1238,6 @@ try {
1238
1238
  prNumber,
1239
1239
  issueNumber,
1240
1240
  success,
1241
- publicPricingEstimate,
1242
- anthropicTotalCostUSD,
1243
1241
  pricingInfo,
1244
1242
  sessionUsage: { sessionId, tempDir, resultModelUsage, streamTokenUsage, subAgentCalls },
1245
1243
  });
@@ -29,8 +29,6 @@ import { safeExit } from './exit-handler.lib.mjs';
29
29
  // Import GitHub-related functions
30
30
  const githubLib = await import('./github.lib.mjs');
31
31
  const { sanitizeLogContent, attachLogToGitHub } = githubLib;
32
- const { buildCostInfoString } = await import('./github-cost-info.lib.mjs');
33
- const { buildBudgetStatsString } = await import('./claude.budget-stats.lib.mjs');
34
32
 
35
33
  // Issue #1745: process-wide sanitization counters used to print a one-line
36
34
  // "we masked N secrets" summary at the end of each run.
@@ -674,7 +672,18 @@ export const showSessionSummary = async (sessionId, limitReached, argv, issueUrl
674
672
  // same observed facts (working-session summary and attached log alike).
675
673
  export const buildSessionBudgetStatsData = async ({ argv, sessionId = null, tempDir = null, resultModelUsage = null, streamTokenUsage = null, subAgentCalls = null, pricingInfo = null }) => {
676
674
  let budgetStatsData = null;
677
- if (argv.tokensBudgetStats && sessionId && tempDir) {
675
+ // Issue #2132: budget stats are a property of the working session **log**.
676
+ // With `--attach-logs` disabled there is no log comment, so they must not be
677
+ // computed or published anywhere.
678
+ const { shouldPublishBudgetStats, isAttachLogsEnabled, isTokensBudgetStatsEnabled } = await import('./budget-stats-policy.lib.mjs');
679
+ if (!shouldPublishBudgetStats(argv)) {
680
+ if (argv?.verbose) {
681
+ const reason = !isTokensBudgetStatsEnabled(argv) ? '--no-tokens-budget-stats' : !isAttachLogsEnabled(argv) ? '--attach-logs is disabled' : 'unknown';
682
+ await log(` ℹ️ Skipping context/cost budget stats publication (${reason})`, { verbose: true });
683
+ }
684
+ return null;
685
+ }
686
+ if (sessionId && tempDir) {
678
687
  try {
679
688
  const { calculateSessionTokens } = await import('./claude.lib.mjs');
680
689
  const tokenUsage = await calculateSessionTokens(sessionId, tempDir, resultModelUsage);
@@ -686,7 +695,7 @@ export const buildSessionBudgetStatsData = async ({ argv, sessionId = null, temp
686
695
  }
687
696
  }
688
697
  // Issue #1526: Build budget stats from Agent CLI token/context data when no JSONL session available
689
- if (!budgetStatsData && argv.tokensBudgetStats && pricingInfo?.tokenUsage) {
698
+ if (!budgetStatsData && pricingInfo?.tokenUsage) {
690
699
  try {
691
700
  const { buildAgentBudgetStats } = await import('./claude.budget-stats.lib.mjs');
692
701
  const agentBudgetData = buildAgentBudgetStats(pricingInfo.tokenUsage, pricingInfo);
@@ -1264,15 +1273,19 @@ export const checkForAiCreatedComments = async (sessionStartTime, owner, repo, p
1264
1273
  * @param {string} options.repo - Repository name
1265
1274
  * @returns {Promise<boolean>} - True if comment was posted successfully
1266
1275
  */
1267
- export const buildWorkingSessionSummaryDetails = ({ publicPricingEstimate = null, anthropicTotalCostUSD = null, pricingInfo = null, budgetStatsData = null } = {}) => {
1268
- const costInfo = buildCostInfoString(publicPricingEstimate, anthropicTotalCostUSD, pricingInfo, {
1269
- includeTokenUsage: false,
1270
- });
1271
- const budgetStats = budgetStatsData ? buildBudgetStatsString(budgetStatsData.tokenUsage, budgetStatsData.subAgentCalls) : '';
1272
- return `${costInfo}${budgetStats}`.trim();
1273
- };
1276
+ /**
1277
+ * Issue #2132: the working session summary must describe *what the AI did* and
1278
+ * nothing else. Cost estimation and context/token budget statistics belong to
1279
+ * the working session log comment (`--attach-logs`), where they are already
1280
+ * published once per working session. Rendering them in the summary as well
1281
+ * duplicated the very same block in two consecutive comments.
1282
+ *
1283
+ * Kept as an exported function returning an empty string so the invariant is
1284
+ * directly testable and any future caller cannot silently re-add the block.
1285
+ */
1286
+ export const buildWorkingSessionSummaryDetails = () => '';
1274
1287
 
1275
- export const attachSolutionSummary = async ({ resultSummary, prNumber, issueNumber, owner, repo, publicPricingEstimate = null, anthropicTotalCostUSD = null, pricingInfo = null, budgetStatsData = null, changeStats = null }) => {
1288
+ export const attachSolutionSummary = async ({ resultSummary, prNumber, issueNumber, owner, repo, changeStats = null }) => {
1276
1289
  if (!resultSummary || typeof resultSummary !== 'string') {
1277
1290
  await log('⚠️ No working session summary available to attach', { verbose: true });
1278
1291
  return false;
@@ -1287,12 +1300,6 @@ export const attachSolutionSummary = async ({ resultSummary, prNumber, issueNumb
1287
1300
  }
1288
1301
 
1289
1302
  try {
1290
- const usageDetails = buildWorkingSessionSummaryDetails({
1291
- publicPricingEstimate,
1292
- anthropicTotalCostUSD,
1293
- pricingInfo,
1294
- budgetStatsData,
1295
- });
1296
1303
  // Issue #2119: publish what the session actually produced. The reported
1297
1304
  // summary said "The `pwd` command completed" and printed the solver's own
1298
1305
  // /tmp workspace, on a pull request that was still empty.
@@ -1302,7 +1309,7 @@ export const attachSolutionSummary = async ({ resultSummary, prNumber, issueNumb
1302
1309
  const comment = `${toolComments.WORKING_SESSION_SUMMARY_AUTOMATION_MARKER}
1303
1310
  ## ${toolComments.WORKING_SESSION_SUMMARY_MARKER}
1304
1311
 
1305
- ${summaryBody}${noChangesNotice ? `\n\n${noChangesNotice}` : ''}${usageDetails ? `\n\n${usageDetails}` : ''}
1312
+ ${summaryBody}${noChangesNotice ? `\n\n${noChangesNotice}` : ''}
1306
1313
 
1307
1314
  ---
1308
1315
  *${toolComments.WORKING_SESSION_SUMMARY_AUTOMATED_FOOTER}*`;
@@ -1359,7 +1366,7 @@ ${summaryBody}${noChangesNotice ? `\n\n${noChangesNotice}` : ''}${usageDetails ?
1359
1366
  * @param {boolean} [options.success=true] - skip attachment for failed iterations
1360
1367
  * @returns {Promise<{attached: boolean, reason: string}>}
1361
1368
  */
1362
- export const maybeAttachWorkingSessionSummary = async ({ argv, resultSummary, workStartTime, owner, repo, prNumber, issueNumber, success = true, publicPricingEstimate = null, anthropicTotalCostUSD = null, pricingInfo = null, budgetStatsData = null, sessionUsage = null }) => {
1369
+ export const maybeAttachWorkingSessionSummary = async ({ argv, resultSummary, workStartTime, owner, repo, prNumber, issueNumber, success = true, pricingInfo = null, budgetStatsData = null, sessionUsage = null }) => {
1363
1370
  if (!success) {
1364
1371
  return { attached: false, reason: 'iteration_failed' };
1365
1372
  }
@@ -1408,16 +1415,14 @@ export const maybeAttachWorkingSessionSummary = async ({ argv, resultSummary, wo
1408
1415
  // say so, instead of reading as a report of completed work.
1409
1416
  const changeStats = prNumber ? await getPullRequestChangeStats({ owner, repo, prNumber, $ }) : null;
1410
1417
 
1418
+ // Issue #2132: the summary carries no cost/budget block. `resolvedBudgetStatsData`
1419
+ // is computed only so the caller can reuse it for this session's log comment.
1411
1420
  const ok = await attachSolutionSummary({
1412
1421
  resultSummary,
1413
1422
  prNumber,
1414
1423
  issueNumber,
1415
1424
  owner,
1416
1425
  repo,
1417
- publicPricingEstimate,
1418
- anthropicTotalCostUSD,
1419
- pricingInfo,
1420
- budgetStatsData: resolvedBudgetStatsData,
1421
1426
  changeStats,
1422
1427
  });
1423
1428
  return { attached: !!ok, reason: ok ? 'attached' : 'post_failed', budgetStatsData: resolvedBudgetStatsData };
@@ -567,19 +567,21 @@ export const watchForFeedback = async params => {
567
567
  latestResultModelUsage = toolResult.resultModelUsage;
568
568
  }
569
569
 
570
- // Issue #1508: Compute budget stats for auto-restart log comment
571
- let autoRestartBudgetStatsData = null;
572
- if (argv.tokensBudgetStats && latestSessionId && tempDir) {
573
- try {
574
- const { calculateSessionTokens } = await import('./claude.lib.mjs');
575
- const tokenUsage = await calculateSessionTokens(latestSessionId, tempDir, toolResult.resultModelUsage);
576
- if (tokenUsage) {
577
- autoRestartBudgetStatsData = { tokenUsage, streamTokenUsage: toolResult.streamTokenUsage || null, subAgentCalls: toolResult.subAgentCalls || null };
578
- }
579
- } catch (budgetError) {
580
- if (argv.verbose) await log(` ⚠️ Could not calculate budget stats: ${budgetError.message}`, { verbose: true });
581
- }
582
- }
570
+ // Issue #1508: Compute budget stats for auto-restart log comment.
571
+ // Issue #2132: shared with the top-level run and the
572
+ // auto-restart-until-mergeable loop via buildSessionBudgetStatsData,
573
+ // so every working session derives its own stats the same way (and
574
+ // skips them entirely when `--attach-logs` is disabled).
575
+ const { buildSessionBudgetStatsData } = await import('./solve.results.lib.mjs');
576
+ const autoRestartBudgetStatsData = await buildSessionBudgetStatsData({
577
+ argv,
578
+ sessionId: latestSessionId,
579
+ tempDir,
580
+ resultModelUsage: toolResult.resultModelUsage,
581
+ streamTokenUsage: toolResult.streamTokenUsage || null,
582
+ subAgentCalls: toolResult.subAgentCalls || null,
583
+ pricingInfo: toolResult.pricingInfo || null,
584
+ });
583
585
 
584
586
  // Issue #1761: Post the working session **summary** BEFORE uploading
585
587
  // the working session **log** so the summary always appears above
@@ -604,8 +606,6 @@ export const watchForFeedback = async params => {
604
606
  prNumber,
605
607
  issueNumber,
606
608
  success: true,
607
- publicPricingEstimate: toolResult.publicPricingEstimate,
608
- anthropicTotalCostUSD: latestAnthropicCost,
609
609
  pricingInfo: toolResult.pricingInfo,
610
610
  budgetStatsData: autoRestartBudgetStatsData,
611
611
  });