@link-assistant/hive-mind 2.5.5 → 2.5.6

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,12 @@
1
1
  # @link-assistant/hive-mind
2
2
 
3
+ ## 2.5.6
4
+
5
+ ### Patch Changes
6
+
7
+ - e501237: Warn when malformed or safety-clamped numeric environment settings fall back to a different effective value.
8
+ - 10ddbca: Document same-session live input support and the Codex app-server integration plan for issue #2057.
9
+
3
10
  ## 2.5.5
4
11
 
5
12
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@link-assistant/hive-mind",
3
- "version": "2.5.5",
3
+ "version": "2.5.6",
4
4
  "description": "AI-powered issue solver and hive mind for collaborative problem solving",
5
5
  "main": "src/hive.mjs",
6
6
  "type": "module",
@@ -30,23 +30,13 @@ const getenv = typeof getenvModule === 'function' ? getenvModule : getenvModule.
30
30
  // Use semver package for version comparison (see issue #1146)
31
31
  import semver from 'semver';
32
32
  import { buildClaudeQuietEnv } from './claude-quiet-config.lib.mjs';
33
+ import { clampEnvValue, parseIntegerEnv, parseNumberEnv } from './env-config.lib.mjs';
33
34
 
34
35
  // Import lino for parsing Links Notation format
35
36
  const { lino } = await import('./lino.lib.mjs');
36
37
 
37
- // Helper function to safely parse integers with fallback
38
- const parseIntWithDefault = (envVar, defaultValue) => {
39
- const value = getenv(envVar, defaultValue.toString());
40
- const parsed = parseInt(value);
41
- return isNaN(parsed) ? defaultValue : parsed;
42
- };
43
-
44
- // Helper function to safely parse floats with fallback
45
- const parseFloatWithDefault = (envVar, defaultValue) => {
46
- const value = getenv(envVar, defaultValue.toString());
47
- const parsed = parseFloat(value);
48
- return isNaN(parsed) ? defaultValue : parsed;
49
- };
38
+ const parseIntWithDefault = (envVar, defaultValue) => parseIntegerEnv(envVar, defaultValue);
39
+ const parseFloatWithDefault = (envVar, defaultValue) => parseNumberEnv(envVar, defaultValue);
50
40
 
51
41
  // Timeout configurations (in milliseconds)
52
42
  export const timeouts = {
@@ -737,7 +727,7 @@ export const cacheTtl = {
737
727
  usageApi: parseIntWithDefault('HIVE_MIND_USAGE_API_CACHE_TTL_MS', 13 * 60 * 1000), // 13 minutes
738
728
  // System metrics cache TTL (RAM, CPU, disk). Issue #2015 caps this at
739
729
  // 1 minute so queue decisions do not use stale host pressure data.
740
- system: Math.min(parseIntWithDefault('HIVE_MIND_SYSTEM_CACHE_TTL_MS', 60 * 1000), 60 * 1000), // max 1 minute
730
+ system: clampEnvValue('HIVE_MIND_SYSTEM_CACHE_TTL_MS', parseIntWithDefault('HIVE_MIND_SYSTEM_CACHE_TTL_MS', 60 * 1000), { maximum: 60 * 1000 }), // max 1 minute
741
731
  };
742
732
 
743
733
  // File and path configurations
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Parse numeric environment configuration without silently accepting malformed
3
+ * suffixes or discarding explicit operator input.
4
+ */
5
+
6
+ const emittedWarnings = new Set();
7
+
8
+ function warnOnce(key, message) {
9
+ if (emittedWarnings.has(key)) return;
10
+ emittedWarnings.add(key);
11
+ console.warn(message);
12
+ }
13
+
14
+ function readExplicitEnv(envVar) {
15
+ const rawValue = process.env[envVar];
16
+ return rawValue === undefined ? null : rawValue;
17
+ }
18
+
19
+ function warnInvalid(envVar, rawValue, type, defaultValue, scope) {
20
+ warnOnce(`invalid:${envVar}`, `[${scope}] ${envVar}=${JSON.stringify(rawValue)} is not a valid ${type}; using default ${defaultValue}.`);
21
+ }
22
+
23
+ /**
24
+ * Read a base-10 integer environment variable.
25
+ *
26
+ * @param {string} envVar
27
+ * @param {number} defaultValue
28
+ * @param {{scope?: string}} [options]
29
+ * @returns {number}
30
+ */
31
+ export function parseIntegerEnv(envVar, defaultValue, { scope = 'config' } = {}) {
32
+ const rawValue = readExplicitEnv(envVar);
33
+ if (rawValue === null) return defaultValue;
34
+
35
+ const normalized = rawValue.trim();
36
+ if (!/^[+-]?\d+$/.test(normalized)) {
37
+ warnInvalid(envVar, rawValue, 'integer', defaultValue, scope);
38
+ return defaultValue;
39
+ }
40
+
41
+ const parsed = Number(normalized);
42
+ if (!Number.isSafeInteger(parsed)) {
43
+ warnInvalid(envVar, rawValue, 'integer', defaultValue, scope);
44
+ return defaultValue;
45
+ }
46
+
47
+ return parsed;
48
+ }
49
+
50
+ /**
51
+ * Read a finite decimal environment variable.
52
+ *
53
+ * @param {string} envVar
54
+ * @param {number} defaultValue
55
+ * @param {{scope?: string}} [options]
56
+ * @returns {number}
57
+ */
58
+ export function parseNumberEnv(envVar, defaultValue, { scope = 'config' } = {}) {
59
+ const rawValue = readExplicitEnv(envVar);
60
+ if (rawValue === null) return defaultValue;
61
+
62
+ const normalized = rawValue.trim();
63
+ const isDecimal = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/.test(normalized);
64
+ const parsed = Number(normalized);
65
+ if (!isDecimal || !Number.isFinite(parsed)) {
66
+ warnInvalid(envVar, rawValue, 'number', defaultValue, scope);
67
+ return defaultValue;
68
+ }
69
+
70
+ return parsed;
71
+ }
72
+
73
+ /**
74
+ * Apply a safety bound and explain when it changes an explicitly configured
75
+ * environment value.
76
+ *
77
+ * @param {string} envVar
78
+ * @param {number} value
79
+ * @param {{minimum?: number, maximum?: number, scope?: string, hint?: string}} options
80
+ * @returns {number}
81
+ */
82
+ export function clampEnvValue(envVar, value, { minimum, maximum, scope = 'config', hint = '' }) {
83
+ if (minimum !== undefined && value < minimum) {
84
+ if (readExplicitEnv(envVar) !== null) {
85
+ warnOnce(`minimum:${envVar}`, `[${scope}] ${envVar}=${value} is below the minimum (${minimum}); using ${minimum}.${hint ? ` ${hint}` : ''}`);
86
+ }
87
+ return minimum;
88
+ }
89
+
90
+ if (maximum !== undefined && value > maximum) {
91
+ if (readExplicitEnv(envVar) !== null) {
92
+ warnOnce(`maximum:${envVar}`, `[${scope}] ${envVar}=${value} exceeds the maximum (${maximum}); using ${maximum}.${hint ? ` ${hint}` : ''}`);
93
+ }
94
+ return maximum;
95
+ }
96
+
97
+ return value;
98
+ }
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { ensureUseM } from './use-m-bootstrap.lib.mjs';
3
+ import { clampEnvValue, parseIntegerEnv, parseNumberEnv } from './env-config.lib.mjs';
3
4
 
4
5
  /**
5
6
  * Queue Configuration Module
@@ -178,19 +179,8 @@ export function parseQueueConfig(linoConfig) {
178
179
  }
179
180
  }
180
181
 
181
- // Helper function to safely parse floats with fallback
182
- const parseFloatWithDefault = (envVar, defaultValue) => {
183
- const value = getenv(envVar, defaultValue.toString());
184
- const parsed = parseFloat(value);
185
- return isNaN(parsed) ? defaultValue : parsed;
186
- };
187
-
188
- // Helper function to safely parse integers with fallback
189
- const parseIntWithDefault = (envVar, defaultValue) => {
190
- const value = getenv(envVar, defaultValue.toString());
191
- const parsed = parseInt(value);
192
- return isNaN(parsed) ? defaultValue : parsed;
193
- };
182
+ const parseFloatWithDefault = (envVar, defaultValue) => parseNumberEnv(envVar, defaultValue, { scope: 'queue-config' });
183
+ const parseIntWithDefault = (envVar, defaultValue) => parseIntegerEnv(envVar, defaultValue, { scope: 'queue-config' });
194
184
 
195
185
  const DEFAULT_MINIMUM_START_INTERVAL_MS = 10 * 60 * 1000;
196
186
  const minimumStartIntervalMs = parseIntWithDefault('HIVE_MIND_MIN_START_INTERVAL_FLOOR_MS', DEFAULT_MINIMUM_START_INTERVAL_MS);
@@ -279,7 +269,11 @@ export const QUEUE_CONFIG = {
279
269
  // can kill the next batch before host metrics have time to settle.
280
270
  // Issue #2053: operators can explicitly lower the safety floor on hosts where
281
271
  // resource metrics settle sooner. The default remains 10 minutes.
282
- MIN_START_INTERVAL_MS: Math.max(parseIntWithDefault('HIVE_MIND_MIN_START_INTERVAL_MS', DEFAULT_MINIMUM_START_INTERVAL_MS), minimumStartIntervalMs),
272
+ MIN_START_INTERVAL_MS: clampEnvValue('HIVE_MIND_MIN_START_INTERVAL_MS', parseIntWithDefault('HIVE_MIND_MIN_START_INTERVAL_MS', DEFAULT_MINIMUM_START_INTERVAL_MS), {
273
+ minimum: minimumStartIntervalMs,
274
+ scope: 'queue-config',
275
+ hint: 'Set HIVE_MIND_MIN_START_INTERVAL_FLOOR_MS to lower the minimum.',
276
+ }),
283
277
  CONSUMER_POLL_INTERVAL_MS: parseIntWithDefault('HIVE_MIND_CONSUMER_POLL_INTERVAL_MS', 60000), // 1 minute between queue checks
284
278
  MESSAGE_UPDATE_INTERVAL_MS: parseIntWithDefault('HIVE_MIND_MESSAGE_UPDATE_INTERVAL_MS', 60000), // 1 minute between status message updates
285
279