specmem-hardwicksoftware 3.7.3 → 3.7.5
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/package.json +1 -1
- package/scripts/specmem-init.cjs +33 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specmem-hardwicksoftware",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Persistent memory system for coding sessions - semantic search with pgvector, token compression, team coordination, file watching. Needs root: installs system-wide hooks, manages docker/PostgreSQL, writes global configs, handles screen sessions. justcalljon.pro",
|
|
6
6
|
"main": "dist/index.js",
|
package/scripts/specmem-init.cjs
CHANGED
|
@@ -3129,6 +3129,14 @@ async function indexCodebase(projectPath, ui, embeddingResult) {
|
|
|
3129
3129
|
// Use Python directly - built-in thread limiting via torch.set_num_threads()
|
|
3130
3130
|
initLog('[EMBED] Starting frankenstein-embeddings.py directly');
|
|
3131
3131
|
const pythonPath = getPythonPath();
|
|
3132
|
+
|
|
3133
|
+
// Redirect stdout/stderr to log file instead of piping
|
|
3134
|
+
// CRITICAL: Piped stdio keeps a reference to the child process even after unref(),
|
|
3135
|
+
// which can cause SIGTERM/SIGPIPE when the parent's event loop is under heavy load
|
|
3136
|
+
// (e.g. during batch embedding). Using 'ignore' or file descriptors prevents this.
|
|
3137
|
+
const embedLogPath = path.join(projectPath, 'specmem', 'sockets', 'embedding-autostart.log');
|
|
3138
|
+
const embedLogFd = fs.openSync(embedLogPath, 'a');
|
|
3139
|
+
|
|
3132
3140
|
const embeddingProcess = spawn(pythonPath, [embeddingScript], {
|
|
3133
3141
|
cwd: path.dirname(embeddingScript),
|
|
3134
3142
|
env: {
|
|
@@ -3137,7 +3145,7 @@ async function indexCodebase(projectPath, ui, embeddingResult) {
|
|
|
3137
3145
|
SPECMEM_PROJECT_PATH: projectPath
|
|
3138
3146
|
},
|
|
3139
3147
|
detached: true,
|
|
3140
|
-
stdio: ['ignore',
|
|
3148
|
+
stdio: ['ignore', embedLogFd, embedLogFd]
|
|
3141
3149
|
});
|
|
3142
3150
|
|
|
3143
3151
|
// error handler BEFORE unref - prevents silent spawn failures
|
|
@@ -3145,13 +3153,8 @@ async function indexCodebase(projectPath, ui, embeddingResult) {
|
|
|
3145
3153
|
ui.setSubStatus('Embedding spawn error: ' + err.message);
|
|
3146
3154
|
});
|
|
3147
3155
|
|
|
3148
|
-
//
|
|
3149
|
-
|
|
3150
|
-
initLog('[EMBED-STDOUT] ' + chunk.toString().trim());
|
|
3151
|
-
});
|
|
3152
|
-
embeddingProcess.stderr.on('data', (chunk) => {
|
|
3153
|
-
initLog('[EMBED-STDERR] ' + chunk.toString().trim());
|
|
3154
|
-
});
|
|
3156
|
+
// Close the fd in the parent so only the child holds it
|
|
3157
|
+
fs.closeSync(embedLogFd);
|
|
3155
3158
|
|
|
3156
3159
|
embeddingProcess.unref();
|
|
3157
3160
|
|
|
@@ -3731,8 +3734,20 @@ async function indexCodebase(projectPath, ui, embeddingResult) {
|
|
|
3731
3734
|
|
|
3732
3735
|
client.setTimeout(TIMEOUTS.EMBEDDING_REQUEST * 2); // Longer timeout for batch
|
|
3733
3736
|
|
|
3737
|
+
// HARD WALL-CLOCK TIMEOUT: Cannot be reset by data/heartbeats
|
|
3738
|
+
// Prevents infinite hangs on large files where server keeps sending heartbeats
|
|
3739
|
+
const HARD_TIMEOUT_MS = TIMEOUTS.EMBEDDING_REQUEST * 3; // 270s absolute max
|
|
3740
|
+
const hardTimeout = setTimeout(() => {
|
|
3741
|
+
if (!settled) {
|
|
3742
|
+
settled = true;
|
|
3743
|
+
if (!client.destroyed) client.destroy();
|
|
3744
|
+
reject(new Error(`Hard wall-clock timeout after ${HARD_TIMEOUT_MS}ms - embedding took too long (heartbeats: ${heartbeatCount})`));
|
|
3745
|
+
}
|
|
3746
|
+
}, HARD_TIMEOUT_MS);
|
|
3747
|
+
|
|
3734
3748
|
// Cleanup helper to avoid leaked resources
|
|
3735
3749
|
const cleanup = () => {
|
|
3750
|
+
clearTimeout(hardTimeout);
|
|
3736
3751
|
if (!client.destroyed) client.destroy();
|
|
3737
3752
|
};
|
|
3738
3753
|
|
|
@@ -4253,6 +4268,16 @@ async function indexCodebase(projectPath, ui, embeddingResult) {
|
|
|
4253
4268
|
setPhase('Embedding');
|
|
4254
4269
|
|
|
4255
4270
|
let fileEmbeddings = [];
|
|
4271
|
+
// FIX: If activeSocketPath is gone (socket died), try to revalidate before skipping
|
|
4272
|
+
if (fileEmbedTexts.length > 0 && !activeSocketPath) {
|
|
4273
|
+
initLog('No active socket for embedding phase - attempting revalidation...');
|
|
4274
|
+
const revalidated = await revalidateSocket();
|
|
4275
|
+
if (revalidated) {
|
|
4276
|
+
initLog(`Socket revalidated successfully: ${activeSocketPath}`);
|
|
4277
|
+
} else {
|
|
4278
|
+
initLog('Socket revalidation failed - embeddings will be skipped for this batch');
|
|
4279
|
+
}
|
|
4280
|
+
}
|
|
4256
4281
|
if (fileEmbedTexts.length > 0 && activeSocketPath) {
|
|
4257
4282
|
try {
|
|
4258
4283
|
ui.setSubStatus(`Generating ${fileEmbedTexts.length} embeddings in batch...`);
|