specmem-hardwicksoftware 3.7.32 → 3.7.33
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/bin/specmem-cli.cjs +18 -33
- package/bootstrap.cjs +5 -1
- package/claude-hooks/agent-loading-hook.js +31 -47
- package/claude-hooks/subagent-loading-hook.js +1 -1
- package/claude-hooks/team-comms-enforcer.cjs +112 -93
- package/dist/config/configSync.js +4 -1
- package/dist/index.js +44 -1
- package/dist/init/claudeConfigInjector.js +4 -1
- package/dist/installer/autoInstall.js +4 -1
- package/dist/mcp/compactionProxy.js +627 -72
- package/dist/mcp/compactionProxyDaemon.js +18 -4
- package/dist/mcp/specMemServer.js +8 -0
- package/dist/watcher/index.js +16 -2
- package/package.json +1 -1
- package/scripts/deploy-hooks.cjs +4 -1
- package/scripts/specmem-init.cjs +31 -35
- package/scripts/specmem-uninstall.cjs +4 -1
- package/specmem/model-config.json +3 -3
- package/specmem/supervisord.conf +1 -1
- package/svg-sections/readme-install.svg +89 -53
package/dist/index.js
CHANGED
|
@@ -574,6 +574,19 @@ class LocalEmbeddingProvider {
|
|
|
574
574
|
// Reset timeout only for the specific request that received data
|
|
575
575
|
if (requestId && this.pendingRequests.has(requestId)) {
|
|
576
576
|
const pending = this.pendingRequests.get(requestId);
|
|
577
|
+
// Check hard deadline BEFORE resetting idle timeout
|
|
578
|
+
if (pending.hardDeadline && Date.now() >= pending.hardDeadline) {
|
|
579
|
+
__debugLog('[EMBEDDING DEBUG]', Date.now(), 'INIT_PERSISTENT_SOCKET_HARD_DEADLINE_HIT', {
|
|
580
|
+
requestId,
|
|
581
|
+
elapsedMs: Date.now() - pending.createdAt
|
|
582
|
+
});
|
|
583
|
+
clearTimeout(pending.timeout);
|
|
584
|
+
if (pending.hardDeadlineTimer) clearTimeout(pending.hardDeadlineTimer);
|
|
585
|
+
this.pendingRequests.delete(requestId);
|
|
586
|
+
pending.reject(new Error(`Embedding hard deadline exceeded during message processing. ` +
|
|
587
|
+
`Socket: ${this.sandboxSocketPath}.`));
|
|
588
|
+
continue;
|
|
589
|
+
}
|
|
577
590
|
const timeoutMs = this.getAdaptiveTimeout();
|
|
578
591
|
clearTimeout(pending.timeout);
|
|
579
592
|
pending.timeout = setTimeout(() => {
|
|
@@ -582,6 +595,7 @@ class LocalEmbeddingProvider {
|
|
|
582
595
|
requestId,
|
|
583
596
|
timeoutMs
|
|
584
597
|
});
|
|
598
|
+
if (pending.hardDeadlineTimer) clearTimeout(pending.hardDeadlineTimer);
|
|
585
599
|
this.pendingRequests.delete(requestId);
|
|
586
600
|
pending.reject(new Error(`Embedding idle timeout after ${Math.round(timeoutMs / 1000)}s of no activity. ` +
|
|
587
601
|
`Socket: ${this.sandboxSocketPath}. ` +
|
|
@@ -606,6 +620,7 @@ class LocalEmbeddingProvider {
|
|
|
606
620
|
if (requestId && this.pendingRequests.has(requestId)) {
|
|
607
621
|
const pending = this.pendingRequests.get(requestId);
|
|
608
622
|
clearTimeout(pending.timeout);
|
|
623
|
+
if (pending.hardDeadlineTimer) clearTimeout(pending.hardDeadlineTimer);
|
|
609
624
|
this.pendingRequests.delete(requestId);
|
|
610
625
|
if (response.error) {
|
|
611
626
|
__debugLog('[EMBEDDING DEBUG]', Date.now(), 'INIT_PERSISTENT_SOCKET_RESOLVING_ERROR', {
|
|
@@ -663,6 +678,7 @@ class LocalEmbeddingProvider {
|
|
|
663
678
|
error: err.message
|
|
664
679
|
});
|
|
665
680
|
clearTimeout(pending.timeout);
|
|
681
|
+
if (pending.hardDeadlineTimer) clearTimeout(pending.hardDeadlineTimer);
|
|
666
682
|
pending.reject(new Error(`Socket error: ${err.message}`));
|
|
667
683
|
}
|
|
668
684
|
this.pendingRequests.clear();
|
|
@@ -688,6 +704,7 @@ class LocalEmbeddingProvider {
|
|
|
688
704
|
requestId
|
|
689
705
|
});
|
|
690
706
|
clearTimeout(pending.timeout);
|
|
707
|
+
if (pending.hardDeadlineTimer) clearTimeout(pending.hardDeadlineTimer);
|
|
691
708
|
pending.reject(new Error('Socket closed'));
|
|
692
709
|
}
|
|
693
710
|
this.pendingRequests.clear();
|
|
@@ -3008,6 +3025,26 @@ class LocalEmbeddingProvider {
|
|
|
3008
3025
|
`Cold starts may need longer timeout - set SPECMEM_EMBEDDING_TIMEOUT=60 for 60 seconds.`));
|
|
3009
3026
|
}
|
|
3010
3027
|
}, timeoutMs);
|
|
3028
|
+
// Hard deadline: 3 minutes absolute maximum, can NOT be reset by heartbeats
|
|
3029
|
+
const HARD_DEADLINE_MS = parseInt(process.env['SPECMEM_EMBEDDING_HARD_DEADLINE_MS'] || '180000', 10);
|
|
3030
|
+
const hardDeadline = Date.now() + HARD_DEADLINE_MS;
|
|
3031
|
+
const hardDeadlineTimer = setTimeout(() => {
|
|
3032
|
+
if (this.pendingRequests.has(requestId)) {
|
|
3033
|
+
__debugLog('[EMBEDDING DEBUG]', Date.now(), 'PERSISTENT_SOCKET_HARD_DEADLINE_EXCEEDED', {
|
|
3034
|
+
attempt,
|
|
3035
|
+
requestId,
|
|
3036
|
+
hardDeadlineMs: HARD_DEADLINE_MS,
|
|
3037
|
+
totalElapsedMs: Date.now() - methodStart
|
|
3038
|
+
});
|
|
3039
|
+
const pending = this.pendingRequests.get(requestId);
|
|
3040
|
+
clearTimeout(pending.timeout);
|
|
3041
|
+
this.pendingRequests.delete(requestId);
|
|
3042
|
+
reject(new Error(`Embedding hard deadline exceeded (${Math.round(HARD_DEADLINE_MS / 1000)}s). ` +
|
|
3043
|
+
`Heartbeats kept resetting idle timeout but no embedding was returned. ` +
|
|
3044
|
+
`Socket: ${this.sandboxSocketPath}. ` +
|
|
3045
|
+
`Increase SPECMEM_EMBEDDING_HARD_DEADLINE_MS if model genuinely needs more time.`));
|
|
3046
|
+
}
|
|
3047
|
+
}, HARD_DEADLINE_MS);
|
|
3011
3048
|
// Store pending request
|
|
3012
3049
|
this.pendingRequests.set(requestId, {
|
|
3013
3050
|
resolve: (embedding) => {
|
|
@@ -3019,11 +3056,17 @@ class LocalEmbeddingProvider {
|
|
|
3019
3056
|
embeddingDim: embedding.length,
|
|
3020
3057
|
totalElapsedMs: Date.now() - methodStart
|
|
3021
3058
|
});
|
|
3059
|
+
clearTimeout(hardDeadlineTimer); // Clear hard deadline on success
|
|
3022
3060
|
this.recordResponseTime(responseTime);
|
|
3023
3061
|
resolve(embedding);
|
|
3024
3062
|
},
|
|
3025
|
-
reject
|
|
3063
|
+
reject: (err) => {
|
|
3064
|
+
clearTimeout(hardDeadlineTimer); // Clear hard deadline on any rejection
|
|
3065
|
+
reject(err);
|
|
3066
|
+
},
|
|
3026
3067
|
timeout,
|
|
3068
|
+
hardDeadlineTimer,
|
|
3069
|
+
hardDeadline,
|
|
3027
3070
|
createdAt: Date.now() // FIX Issue #6: Track creation time for stale cleanup
|
|
3028
3071
|
});
|
|
3029
3072
|
// Send request with ID so we can match responses
|
|
@@ -393,7 +393,10 @@ function fixProjectMcpConfigs() {
|
|
|
393
393
|
// Write back if modified
|
|
394
394
|
if (modified) {
|
|
395
395
|
try {
|
|
396
|
-
|
|
396
|
+
// ATOMIC WRITE: write to temp then rename to prevent corruption
|
|
397
|
+
const tmpPath = CLAUDE_JSON_PATH + '.tmp.' + process.pid;
|
|
398
|
+
fs.writeFileSync(tmpPath, JSON.stringify(claudeJson, null, 2));
|
|
399
|
+
fs.renameSync(tmpPath, CLAUDE_JSON_PATH);
|
|
397
400
|
logger.info({ path: CLAUDE_JSON_PATH, projectsFixed: fixed }, '[ConfigInjector] Updated project-level MCP configs');
|
|
398
401
|
}
|
|
399
402
|
catch (err) {
|
|
@@ -284,7 +284,10 @@ export async function configureHooks() {
|
|
|
284
284
|
// Also create .claude.json with acceptEditsModeAccepted
|
|
285
285
|
const claudeJsonPath = path.join(claudeDir, '.claude.json');
|
|
286
286
|
const claudeJson = { acceptEditsModeAccepted: true };
|
|
287
|
-
|
|
287
|
+
// ATOMIC WRITE: write to temp then rename to prevent corruption
|
|
288
|
+
const tmpPath = claudeJsonPath + '.tmp.' + process.pid;
|
|
289
|
+
fs.writeFileSync(tmpPath, JSON.stringify(claudeJson, null, 2));
|
|
290
|
+
fs.renameSync(tmpPath, claudeJsonPath);
|
|
288
291
|
logger.info('GOD MODE: acceptEditsModeAccepted enabled');
|
|
289
292
|
// Add SpecMem tool permissions if not already present
|
|
290
293
|
const specmemPermissions = [
|