@link-assistant/hive-mind 1.57.1 → 1.57.3

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,28 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 1.57.3
4
+
5
+ ### Patch Changes
6
+
7
+ - 5c65c29: Fix /log and /terminal_watch falsely rejecting real `$` isolation sessions (issue #1700)
8
+
9
+ `parseSessionStatusOutput` looked for the isolation backend at `data.isolation`
10
+ or `data.options.isolation`, but the published `link-foundation/start` 0.25.x
11
+ CLI reports it at `options.isolated` in both JSON and the default
12
+ `links-notation` output. As a result, replying `/log` (or `/terminal_watch`) to
13
+ a `Work session finished` message rejected every screen / tmux / docker session
14
+ with `❌ This command currently supports only sessions launched with $
15
+ isolation`. The parser now reads `options.isolated` first and keeps the legacy
16
+ field names as fallbacks. The rejection site additionally emits a `[VERBOSE]`
17
+ diagnostic line so future contract drifts can be triaged from a single bot log
18
+ entry. Regression test in `tests/test-issue-1700-isolation-parsing.mjs`.
19
+
20
+ ## 1.57.2
21
+
22
+ ### Patch Changes
23
+
24
+ - aff6d1d: Add a stable test runner and suite markers to avoid package.json test-script conflicts.
25
+
3
26
  ## 1.57.1
4
27
 
5
28
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "1.57.1",
3
+ "version": "1.57.3",
4
4
  "description": "AI-powered issue solver and hive mind for collaborative problem solving",
5
5
  "main": "src/hive.mjs",
6
6
  "type": "module",
@@ -15,7 +15,7 @@
15
15
  "hive-telegram-bot": "./src/telegram-bot.mjs"
16
16
  },
17
17
  "scripts": {
18
- "test": "node tests/solve-queue.test.mjs && node tests/limits-display.test.mjs && node tests/test-usage-limit.mjs && node tests/test-codex-support.mjs && node tests/test-build-cost-info-string.mjs && node tests/test-claude-code-install-method.mjs && node tests/test-claude-quiet-config.mjs && node tests/test-configure-claude-bin.mjs && node tests/test-docker-release-order.mjs && node tests/test-docker-box-migration.mjs && node tests/test-hive-screens.mjs && node tests/test-issue-1616-pr-issue-link-preservation.mjs && node tests/test-pre-pr-failure-notifier-1640.mjs && node tests/test-ready-to-merge-pagination-1645.mjs && node tests/test-require-gh-paginate-rule.mjs && node tests/test-auto-restart-limits-1664.mjs && node tests/test-log-upload-output-1678.mjs && node tests/test-log-upload-output-1682.mjs && node tests/test-telegram-message-filters.mjs && node tests/test-telegram-bot-command-aliases.mjs && node tests/test-telegram-options-before-url.mjs && node tests/test-telegram-bot-configuration-isolation-links-notation.mjs && node tests/test-extract-isolation-from-args.mjs && node tests/test-solve-queue-command.mjs && node tests/test-queue-display-1267.mjs && node tests/test-issue-467-terminal-watch.mjs && node tests/test-issue-1670-screen-status-monitoring.mjs && node tests/test-issue-1680-session-monitoring.mjs && node tests/test-issue-1684-message-formatting.mjs && node tests/test-issue-1686-log-command.mjs && node tests/test-issue-1688-subscribe-and-pr-link.mjs && node tests/test-issue-1694-stabilized-defaults.mjs && node tests/test-telegram-bot-launcher.mjs",
18
+ "test": "node scripts/run-tests.mjs --suite default",
19
19
  "test:queue": "node tests/solve-queue.test.mjs",
20
20
  "test:limits-display": "node tests/limits-display.test.mjs",
21
21
  "test:usage-limit": "node tests/test-usage-limit.mjs",
@@ -64,7 +64,8 @@
64
64
  "husky": "^9.1.7",
65
65
  "jscpd": "^4.0.5",
66
66
  "lint-staged": "^16.2.7",
67
- "prettier": "^3.6.2"
67
+ "prettier": "^3.6.2",
68
+ "test-anywhere": "^0.9.1"
68
69
  },
69
70
  "dependencies": {
70
71
  "@secretlint/core": "^11.2.5",
@@ -52,7 +52,12 @@ export function parseSessionStatusOutput(output) {
52
52
  try {
53
53
  const parsed = JSON.parse(raw);
54
54
  const data = Array.isArray(parsed) ? parsed[0] : parsed;
55
- const isolationFromOptions = typeof data?.options?.isolation === 'string' ? data.options.isolation.toLowerCase() : null;
55
+ // start-command (link-foundation/start) reports the isolation backend at
56
+ // `options.isolated` in both JSON and links-notation output. Older
57
+ // hypothetical layouts used `options.isolation` or a top-level `isolation`
58
+ // field — keep accepting all three so we are tolerant of future renames.
59
+ // See https://github.com/link-assistant/hive-mind/issues/1700.
60
+ const isolationCandidate = (typeof data?.isolation === 'string' && data.isolation) || (typeof data?.options?.isolated === 'string' && data.options.isolated) || (typeof data?.options?.isolation === 'string' && data.options.isolation) || null;
56
61
  return {
57
62
  exists: true,
58
63
  uuid: data?.uuid || null,
@@ -63,7 +68,7 @@ export function parseSessionStatusOutput(output) {
63
68
  currentTime: data?.currentTime || null,
64
69
  logPath: data?.logPath || null,
65
70
  command: data?.command || null,
66
- isolation: typeof data?.isolation === 'string' ? data.isolation.toLowerCase() : isolationFromOptions,
71
+ isolation: isolationCandidate ? isolationCandidate.toLowerCase() : null,
67
72
  workingDirectory: data?.workingDirectory || null,
68
73
  raw,
69
74
  };
@@ -83,6 +88,13 @@ export function parseSessionStatusOutput(output) {
83
88
 
84
89
  const status = readField('status')?.toLowerCase() || null;
85
90
  const exitCodeText = readField('exitCode');
91
+ // `start-command` links-notation output nests the isolation backend under
92
+ // `options` as `isolated <backend>` (not `isolation`). The leading indent
93
+ // varies by depth, but `readField` is anchored with `^\s*` which already
94
+ // matches indented lines. Older code only looked for `isolation`, which
95
+ // returned null for every real session and made /log + /terminal_watch
96
+ // reject screen/tmux/docker sessions. See issue #1700.
97
+ const isolationText = readField('isolated') || readField('isolation');
86
98
 
87
99
  return {
88
100
  exists: Boolean(status || firstLine),
@@ -94,7 +106,7 @@ export function parseSessionStatusOutput(output) {
94
106
  currentTime: readField('currentTime'),
95
107
  logPath: readField('logPath'),
96
108
  command: readField('command'),
97
- isolation: readField('isolation')?.toLowerCase() || null,
109
+ isolation: isolationText?.toLowerCase() || null,
98
110
  workingDirectory: readField('workingDirectory'),
99
111
  raw,
100
112
  };
@@ -260,6 +260,9 @@ export async function registerLogCommand(bot, options) {
260
260
  // 4. Decide the destination.
261
261
  const decision = decideLogDestination({ statusResult, sessionInfo, repoVisibility, chatType });
262
262
  if (decision.destination === 'reject') {
263
+ // Surface enough state to diagnose false-rejections like issue #1700,
264
+ // where the parser missed the isolation field name reported by the host.
265
+ VERBOSE && console.log(`[VERBOSE] /log rejected session ${sessionId}: reason="${decision.reason}" parsedIsolation=${JSON.stringify(statusResult?.isolation)} sessionInfoBackend=${JSON.stringify(sessionInfo?.isolationBackend)} rawHead=${JSON.stringify((statusResult?.raw || '').slice(0, 240))}`);
263
266
  await ctx.reply(`❌ ${decision.reason}`, { reply_to_message_id: message.message_id });
264
267
  return;
265
268
  }
@@ -382,6 +382,8 @@ export async function registerTerminalWatchCommand(bot, options) {
382
382
  const { repoVisibility, repoDescription } = await resolveTerminalWatchRepository({ sessionInfo, statusResult, parseGitHubUrl, detectRepositoryVisibility });
383
383
  const decision = decideLogDestination({ statusResult, sessionInfo, repoVisibility, chatType: chat.type });
384
384
  if (decision.destination === 'reject') {
385
+ // Surface enough state to diagnose false-rejections like issue #1700.
386
+ VERBOSE && console.log(`[VERBOSE] /terminal_watch rejected session ${sessionId}: reason="${decision.reason}" parsedIsolation=${JSON.stringify(statusResult?.isolation)} sessionInfoBackend=${JSON.stringify(sessionInfo?.isolationBackend)} rawHead=${JSON.stringify((statusResult?.raw || '').slice(0, 240))}`);
385
387
  await ctx.reply(`❌ ${decision.reason}`, { reply_to_message_id: message.message_id });
386
388
  return;
387
389
  }