@link-assistant/hive-mind 1.58.0 → 1.59.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,222 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 1.59.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 65d7b99: Fix misleading `/merge` verbose logs that read as "no CI configured" when CI was actually
8
+ running — addresses issue [#1712](https://github.com/link-assistant/hive-mind/issues/1712)
9
+ where a user mistakenly Ctrl+C'd the auto-restart-until-mergeable watcher after seeing:
10
+
11
+ ```
12
+ [VERBOSE] /merge: PR #83 has no CI checks yet - treating as no_checks
13
+ [VERBOSE] /merge: PR #83 has no CI check-runs yet, but 1 workflow run(s) were triggered ...
14
+ ⏳ Waiting for CI: Build and Release Docker Image
15
+ ```
16
+
17
+ The classification logic was correct — `/merge` was waiting on the legitimate 30-120s gap
18
+ between GitHub registering a `workflow_run` and publishing the corresponding `check_runs`.
19
+ The wording was the bug: "no CI checks yet" is parseable as "this repo has no CI", and the
20
+ listing showed run IDs without URLs, so the user couldn't quickly verify what `/merge` was
21
+ watching.
22
+
23
+ Changes:
24
+ - **`src/github-merge.lib.mjs`** — `getDetailedCIStatus` and `checkPRCIStatus` reword the
25
+ `no_checks` verbose lines to "has no check-runs or commit statuses registered yet",
26
+ including the short SHA. `getWorkflowRunsForSha` now appends `run.html_url` to every
27
+ entry. Normalized check-run / commit-status entries carry an `html_url` field
28
+ (falling back to `details_url` / `target_url`).
29
+ - **`src/solve.auto-merge-helpers.lib.mjs::getMergeBlockers`** — the `no_checks`,
30
+ `pending`, and `cancelled` branches now produce blocker `details` strings of the form
31
+ `"<name> [<status>] — <html_url>"`. The user-facing `⏳ Waiting for CI: …` line in
32
+ `solve.auto-merge.lib.mjs` (which joins `details` with commas) automatically picks up
33
+ the URLs, so the user can click through to the run.
34
+ - **`tests/test-misleading-merge-logs-1712.mjs`** — 13 unit tests covering the wording
35
+ guard, blocker enrichment for the no_checks / pending / cancelled paths, regression
36
+ guard for #1466, and the joined user-facing line format.
37
+ - **`docs/case-studies/issue-1712/README.md`** — full case study with raw logs, timeline,
38
+ root cause, fix description, and verification on the original PR
39
+ [link-foundation/box#83](https://github.com/link-foundation/box/pull/83) (which CI
40
+ passed for, after the user killed the watcher prematurely).
41
+
42
+ Also extends the `useWithRetry` helper (originally added in #1710 to recover from corrupt
43
+ hosted-CI npm-install state) with a third failure mode: `ERR_INVALID_PACKAGE_CONFIG` —
44
+ seen in this branch's own CI run when Node refused to parse a truncated
45
+ `getenv-v-latest/package.json`. `src/queue-config.lib.mjs` now loads `getenv` and
46
+ `links-notation` through the retry wrapper, matching `config.lib.mjs` and `lino.lib.mjs`.
47
+ Three new unit tests in `tests/test-use-with-retry.mjs` cover the new mode.
48
+
49
+ No upstream issue is needed — the bug was entirely in `link-assistant/hive-mind`. The
50
+ external workflow finished successfully (`check-runs-dfc4c14.json` shows `total_count: 22`).
51
+
52
+ **Follow-up round** (after review feedback in
53
+ [PR #1713 comment](https://github.com/link-assistant/hive-mind/pull/1713#issuecomment-4342387674)):
54
+ - **List active runs across ALL PR commits, not just HEAD.** New
55
+ `getActivePRWorkflowRuns()` in `src/github-merge-repo-actions.lib.mjs` walks every
56
+ commit on the PR (`/repos/.../pulls/N/commits`), dedupes by `run.id`, returns groups
57
+ marked `head` / `older`. The verbose log now lists active runs on older commits under
58
+ per-commit URL headers, so the GitHub Actions tab (which shows yellow dots for older
59
+ commits) reconciles with the log.
60
+ - **Eliminate duplicate logging.** `getWorkflowRunsForSha(verbose=true)` already prints
61
+ every run; the no_checks branch no longer re-iterates `workflowRuns`, just emits a
62
+ single explanatory summary line.
63
+ - **Commit URLs instead of short SHAs.** Verbose lines that referenced
64
+ `${sha.substring(0, 7)}` now use `https://github.com/${owner}/${repo}/commit/${sha}`
65
+ (or `/pull/N/commits/${sha}` where the PR context matters).
66
+ - **Inline plain-English explanations.** New `STATUS_HINTS` / `CONCLUSION_HINTS`
67
+ dictionaries plus `explainStatus()` helper — verbose lines read
68
+ `[in_progress] (currently executing)` instead of bare `in_progress`.
69
+ - **Multi-line user-facing waiting message.** The `⏳ Waiting for CI:` line is now
70
+ rendered by `renderBlocker()` — single-line for the common case (one run), but each
71
+ detail on its own indented line when there are multiple.
72
+ - 8 new tests added to `tests/test-misleading-merge-logs-1712.mjs` (Groups 5–8); 21
73
+ total. #1480 (31/31) and #1466 (14/14) regression suites still pass.
74
+
75
+ ## 1.59.0
76
+
77
+ ### Minor Changes
78
+
79
+ - 903b10e: Add `--auto-input-until-mergeable` (issue #1708): a new experimental
80
+ mode that extends a single Claude session for as long as possible by
81
+ streaming PR/issue comments, CI/CD failures, uncommitted-changes
82
+ status, and PR/issue title/body updates as NDJSON `user` frames into
83
+ the live `claude --input-format stream-json` process — instead of
84
+ killing the process and restarting with the feedback prepended to a
85
+ fresh prompt.
86
+
87
+ What it ships:
88
+ - Three new flags in `src/solve.config.lib.mjs`, all defaulting to
89
+ `false` and marked `[EXPERIMENTAL]`:
90
+ - `--auto-input-until-mergeable` — top-level opt-in for the new
91
+ behavior. Implies `--accept-incomming-comments-as-input` and
92
+ defaults to `--queue-comments-to-input` so the AI can finish its
93
+ current step before being interrupted.
94
+ - `--stream-comments-to-input` — forward each comment immediately
95
+ as it arrives. Default for `--accept-incomming-comments-as-input`
96
+ on its own (preserves the existing #817 behavior).
97
+ - `--queue-comments-to-input` — buffer comments while the AI is
98
+ busy and flush them only on `result` events. Default delivery
99
+ mode for `--auto-input-until-mergeable`. Mutually exclusive with
100
+ `--stream-comments-to-input`; queue mode wins if both are set.
101
+ - Queue-vs-stream delivery wired into
102
+ `src/bidirectional-interactive.lib.mjs#createBidirectionalHandler`:
103
+ - New `deliveryMode` option (`'stream'` / `'queue'`) plus
104
+ `markAiBusy()` / `markAiIdle()` lifecycle methods exposed on the
105
+ handler.
106
+ - In queue mode, comment frames and status frames are buffered in
107
+ `pendingFrames` while busy and FIFO-flushed to stdin on the next
108
+ `result` event. In stream mode, frames go to stdin immediately as
109
+ today.
110
+ - Status streaming (only when `--auto-input-until-mergeable` is on)
111
+ in `src/bidirectional-interactive.lib.mjs#checkForStatusChanges`:
112
+ - New parallel poller emits one-shot NDJSON frames for: PR
113
+ title/body changes, issue title/body changes (Issue #1708 G1),
114
+ uncommitted local changes (`git status --porcelain`), and CI
115
+ blockers (via `getMergeBlockers`).
116
+ - Each change is keyed by a stable signature so the same failing
117
+ check doesn't re-emit on every poll; failures in any sub-check
118
+ are swallowed and logged so the poller never breaks the live
119
+ Claude session.
120
+ - Stream parser in `src/claude.lib.mjs#executeClaudeCommand` now
121
+ signals `markAiBusy()` on `assistant` / `tool_use` / `tool_result`
122
+ events and `markAiIdle()` on `result` events, so queue-mode
123
+ buffering tracks the actual AI lifecycle.
124
+ - `src/solve.auto-merge.lib.mjs#watchUntilMergeable` logs a
125
+ "streaming-first" banner when `--auto-input-until-mergeable` was
126
+ active, so it is clear the auto-restart loop is the fallback rather
127
+ than the primary handler.
128
+ - For non-Claude tools, the validator continues to warn and disable
129
+ all four flags — the existing #817 fallback path. The default
130
+ behavior of every existing flag
131
+ (`--auto-restart-until-mergeable`, `--auto-merge`, etc.) is
132
+ preserved (R4: "must not break any existing features").
133
+ - Tests:
134
+ `tests/test-auto-input-until-mergeable-1708.mjs` (59 assertions)
135
+ and 11 new assertions in
136
+ `tests/test-bidirectional-interactive.mjs` cover flag composition,
137
+ queue-vs-stream routing, FIFO flushing on idle, busy-flag
138
+ preservation across stream-mode writes, default-deliveryMode is
139
+ stream, status-frame stamping with the right header per kind
140
+ (`comment` / `ci` / `uncommitted` / `metadata`), and metadata
141
+ diff/snapshot helpers.
142
+
143
+ The case study at `docs/case-studies/issue-1708/` is updated to
144
+ reflect that R1, R2 (Claude path), R3 (PR/issue title+body, CI,
145
+ uncommitted, comments), R4, R5, R6, plus G1, G5, G7 are addressed
146
+ here. Codex/Agent/OpenCode still degrade gracefully (no mid-session
147
+ NDJSON channel upstream) and use the existing `watchUntilMergeable`
148
+ loop as documented in G4.
149
+
150
+ - 6efcab4: Fix cost / token calculation correctness, unify Total / sub-session format,
151
+ add verbose budget trace, and case study for issue #1710
152
+
153
+ Resolves the four "strange things" the issue reported by changing both the
154
+ public-pricing math and the rendered output:
155
+ - **R1 — `$0.040000` residual eliminated.** `calculateModelCost`
156
+ ([`src/claude.lib.mjs`](./src/claude.lib.mjs)) now bills Anthropic
157
+ server-side tools. `web_search` is charged at the documented
158
+ $10 / 1 000 requests rate (= $0.01 / req) via the new constants module
159
+ [`src/anthropic-server-tool-pricing.lib.mjs`](./src/anthropic-server-tool-pricing.lib.mjs).
160
+ For the issue's PR #1707 run that comes out to exactly the previously-shown
161
+ $0.040000 / +0.16% delta, so the public-pricing total now reconciles with
162
+ Anthropic's reported `total_cost_usd`. `accumulateModelUsage`
163
+ ([`src/claude.budget-stats.lib.mjs`](./src/claude.budget-stats.lib.mjs))
164
+ also picks up `usage.server_tool_use.web_search_requests` from JSONL.
165
+ - **R2 — Haiku sub-session line includes input information.** Sub-agent
166
+ models never appear as the responding model in the parent JSONL, so
167
+ `peakContextUsage` stays at `0`. The fallback in `buildBudgetStatsString`
168
+ now emits the cumulative `(X new + Y cache writes [+ Z cache reads])`
169
+ phrase instead of dropping the input information entirely.
170
+ - **R3/R5 — Sub-session and Total reconcile.** The bullet line is now
171
+ labelled `peak request: …` so it cannot be confused with the cumulative
172
+ Total line. `requestContext` (the source of `peakContextByModel`) excludes
173
+ cache reads, so the bullet figure is `input + cache_creation` and is
174
+ reconcilable with the cumulative non-cached total. Cache reads remain
175
+ visible — and visible separately — on the Total line.
176
+ - **R4 — Total always splits cache reads / cache writes when present.**
177
+ The conditional that previously keyed on `cacheReadTokens` only is replaced
178
+ with a `buildCumulativeInputPhrase` helper that emits
179
+ `(X new + W cache writes + Y cache reads) input tokens` when both kinds of
180
+ cache activity exist, `(X new + W cache writes)` when only writes exist
181
+ (the Haiku case that triggered the issue), and the back-compat
182
+ `(X + Y cached)` form when only reads exist (so common Opus-only output
183
+ is unchanged). Cache writes are billed at 1.25× / 2× of input — fusing
184
+ them silently into the input figure was a real semantic bug, not a
185
+ cosmetic one.
186
+
187
+ Both `displayBudgetStats` (solver-log renderer) and `buildBudgetStatsString`
188
+ (PR-comment renderer) share the helper, so the two paths render identically.
189
+
190
+ Also adds **`dumpBudgetTrace`**
191
+ ([`src/claude.budget-stats.lib.mjs`](./src/claude.budget-stats.lib.mjs)),
192
+ a verbose-only structured per-model trace (peak request, cumulative
193
+ input/cache_write 5m+1h split/cache_read/output, server-tool counts with
194
+ implied dollar cost, public and Anthropic-reported costs, and the data
195
+ source) that fires from `displayBudgetStats` only when `{verbose: true}` is
196
+ set, so the default solver output is unchanged. The trace captures all the
197
+ inputs that drive the renderer in one place, so the next "calculation
198
+ correctness" report can be triaged from a saved log alone.
199
+
200
+ Tests:
201
+ - `tests/test-issue-1710-budget-trace.mjs` — 10 cases for the verbose trace.
202
+ - `tests/test-issue-1710-format-fixes.mjs` — 8 cases locking each requirement
203
+ to numbers from `docs/case-studies/issue-1710/facts.md` (the actual
204
+ PR #1707 result event the issue quotes).
205
+
206
+ Documentation: `docs/case-studies/issue-1710/` contains the root-cause
207
+ analysis (per symptom, with file:line citations), the captured facts, and
208
+ the (now-implemented) solution plans.
209
+
210
+ Also fixes the hosted-CI flake that surfaced while validating this PR:
211
+ `use-m` occasionally hands back a truncated/corrupt global package after
212
+ `npm install -g`, surfacing as either
213
+ `Failed to import module from '...': SyntaxError: Unexpected end of input`
214
+ or `Failed to resolve the path to '<pkg>'` when use-m loads `getenv` /
215
+ `links-notation` from `src/config.lib.mjs` and `src/lino.lib.mjs`. Adds
216
+ `src/use-with-retry.lib.mjs`, a small wrapper around `use(...)` that
217
+ recognises both flake modes, removes the broken alias directory, and
218
+ re-fetches once. Covered by `tests/test-use-with-retry.mjs` (13 cases).
219
+
3
220
  ## 1.58.0
4
221
 
5
222
  ### Minor Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "1.58.0",
3
+ "version": "1.59.1",
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,34 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Issue #1710: Anthropic server-side tool pricing.
5
+ *
6
+ * `calculateModelCost` historically only billed token-based usage (input,
7
+ * cache_creation, cache_read, output). When a sub-agent uses Anthropic's
8
+ * server-side web_search tool, the result event reports `webSearchRequests`,
9
+ * which Anthropic bills at $10 / 1 000 searches ($0.01 / request) per
10
+ * <https://platform.claude.com/docs/en/about-claude/pricing#web-search-tool>.
11
+ *
12
+ * Without billing it locally, the public-pricing estimate disagreed with
13
+ * Anthropic's reported `total_cost_usd` by exactly that amount — the
14
+ * "Difference: $0.040000 (+0.16%)" line that issue #1710 quotes.
15
+ *
16
+ * Centralising the constants in this module keeps the source-of-truth in one
17
+ * file: bumping a price is a one-line edit, and `calculateModelCost` /
18
+ * `dumpBudgetTrace` both read from the same map.
19
+ */
20
+ export const SERVER_TOOL_PRICING_USD = Object.freeze({
21
+ // $10 per 1 000 searches = $0.01 per request.
22
+ // https://platform.claude.com/docs/en/about-claude/pricing#web-search-tool
23
+ web_search: { costPerRequest: 0.01, source: 'https://platform.claude.com/docs/en/about-claude/pricing#web-search-tool' },
24
+ // web_fetch is currently free for paying customers; kept here for
25
+ // completeness and so a future price change is a one-line edit.
26
+ web_fetch: { costPerRequest: 0, source: 'https://platform.claude.com/docs/en/about-claude/pricing#web-fetch-tool' },
27
+ });
28
+
29
+ /**
30
+ * Returns the per-request USD price for a server-side tool, or 0 if unknown.
31
+ * @param {string} tool - canonical tool name (e.g. "web_search")
32
+ * @returns {number} per-request price in USD
33
+ */
34
+ export const getServerToolPrice = tool => SERVER_TOOL_PRICING_USD[tool]?.costPerRequest || 0;