total-recall-brain 3.6.4 → 3.6.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/extension/background.js +4 -0
- package/extension/content-script.js +9 -1
- package/extension/manifest.json +0 -1
- package/frontend/src/App.tsx +5 -8
- package/frontend/src/components/BrainSelector.tsx +10 -4
- package/frontend/src/index.css +1 -0
- package/frontend/src/pages/ChatPage.tsx +2 -0
- package/frontend/src/pages/{DeploymentsPage.tsx → ModelsPage.tsx} +183 -87
- package/frontend/src/types.ts +3 -0
- package/frontend/vite.config.ts +3 -3
- package/package.json +1 -1
- package/src/core/daemon-loop.mjs +162 -58
- package/src/core/fact-seeker.mjs +6 -3
- package/src/core/research-queue.mjs +8 -1
- package/src/core/scheduler.mjs +23 -5
- package/src/core/schema.mjs +23 -0
- package/src/server/index.mjs +19 -0
- package/src/server/rest.mjs +43 -189
- package/src/server/route-manifest.json +4 -0
- package/src/server/routes/auth.mjs +32 -0
- package/src/server/routes/research.mjs +55 -0
- package/src/server/routes/sandbox.mjs +55 -0
- package/src/server/routes/skills.mjs +118 -0
package/src/core/daemon-loop.mjs
CHANGED
|
@@ -38,61 +38,81 @@ const RECOMPILE_EVERY_N_TASKS = 20;
|
|
|
38
38
|
// Pause between task iterations (ms)
|
|
39
39
|
const TASK_SLEEP_MS = 10000;
|
|
40
40
|
|
|
41
|
-
// ───
|
|
41
|
+
// ─── Tasks are no longer skipped to conserve resources. We use CLI agents! ───
|
|
42
42
|
|
|
43
|
-
const LLM_REQUIRED_CATEGORIES = new Set([
|
|
44
|
-
'system2-deliberation',
|
|
45
|
-
'conscious-enforcement',
|
|
46
|
-
'cutoff-audit',
|
|
47
|
-
]);
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Check if a task requires an LLM to execute.
|
|
51
|
-
* These tasks are skipped — the daemon is deterministic.
|
|
52
|
-
*/
|
|
53
|
-
function requiresLLM(task) {
|
|
54
|
-
if (LLM_REQUIRED_CATEGORIES.has(task.category)) return true;
|
|
55
|
-
|
|
56
|
-
const slug = task.slug;
|
|
57
|
-
if (slug.startsWith('research-deliberation-')) return true;
|
|
58
|
-
if (slug.startsWith('research-improvement-')) return true;
|
|
59
|
-
if (slug.startsWith('research-monitoring-')) return true;
|
|
60
|
-
if (slug.startsWith('research-expansion-')) return true;
|
|
61
|
-
if (slug.includes('self-diagnosis')) return true;
|
|
62
|
-
if (slug.includes('clarity-review')) return true;
|
|
63
|
-
if (slug.includes('staleness-check')) return true;
|
|
64
|
-
if (slug.includes('inference')) return true;
|
|
65
|
-
if (slug.includes('post-mortem')) return true;
|
|
66
|
-
if (slug.includes('compliance')) return true;
|
|
67
|
-
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Dispatch a task to the appropriate engine.
|
|
73
|
-
* Only deterministic (non-LLM) tasks are executed.
|
|
74
|
-
*/
|
|
75
43
|
async function dispatchTask(task) {
|
|
76
44
|
const category = task.category;
|
|
77
45
|
|
|
78
46
|
try {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
47
|
+
const { loadRuntimeConfig } = await import('./runtime.mjs');
|
|
48
|
+
const runtimeConfig = await loadRuntimeConfig();
|
|
49
|
+
|
|
50
|
+
// ─── Research & Proactive Tasks (LLM + Fetching) ───
|
|
51
|
+
if (task.slug.startsWith('research-') || category === 'proactive-research' || category === 'research-acquisition') {
|
|
52
|
+
if (task.slug.startsWith('staleness-check-')) {
|
|
53
|
+
const { runStalenessCheck } = await import('./clarity-rewriter.mjs');
|
|
54
|
+
const result = await runStalenessCheck(task.target, { vaultDir: VAULT_DIR, queueDir: QUEUE_DIR, runtimeConfig });
|
|
55
|
+
return { success: true, output: `Staleness check complete: ${result.verdict} (conf: ${result.confidence})` };
|
|
56
|
+
}
|
|
57
|
+
return await runResearchTask(task, runtimeConfig);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ─── System 2 Inference ───
|
|
61
|
+
if (category === 'system2-deliberation') {
|
|
62
|
+
const { runInferenceTask } = await import('./inference-engine.mjs');
|
|
63
|
+
const slugs = task.target ? task.target.split(',') : [];
|
|
64
|
+
const result = await runInferenceTask(slugs, {
|
|
65
|
+
vaultDir: VAULT_DIR,
|
|
66
|
+
inboxDir: path.join(BRAIN_DIR, 'memory-inbox', 'pending'),
|
|
67
|
+
runtimeConfig
|
|
68
|
+
});
|
|
69
|
+
return { success: true, output: `Inference complete: ${result.conclusions?.length || 0} conclusions` };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ─── Cutoff Audit ───
|
|
73
|
+
if (category === 'cutoff-audit' || task.slug.startsWith('cutoff-audit-')) {
|
|
74
|
+
const { runCutoffAudit } = await import('./clarity-rewriter.mjs');
|
|
75
|
+
const result = await runCutoffAudit({ vaultDir: VAULT_DIR, queueDir: QUEUE_DIR, runtimeConfig });
|
|
76
|
+
return { success: true, output: `Cutoff audit complete: ${result.audited} audited, ${result.flagged} flagged, ${result.critical} critical` };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ─── Self Diagnosis & Conscious Enforcement ───
|
|
80
|
+
if (category === 'conscious-enforcement' || task.slug.includes('self-diagnosis')) {
|
|
81
|
+
if (task.slug.startsWith('post-mortem-')) {
|
|
82
|
+
const { runPostMortem } = await import('./post-mortem.mjs');
|
|
83
|
+
const sessionPath = path.join(brainDir, 'sessions', task.target);
|
|
84
|
+
const result = await runPostMortem(sessionPath, { vaultDir: VAULT_DIR, inboxDir: path.join(BRAIN_DIR, 'memory-inbox', 'pending'), runtimeConfig });
|
|
85
|
+
return { success: true, output: `Post-mortem complete: ${result.nodesCreated || 0} nodes extracted` };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (task.slug.includes('self-diagnosis')) {
|
|
89
|
+
const { runSelfDiagnosis } = await import('./fact-seeker.mjs');
|
|
90
|
+
await runSelfDiagnosis({ vaultDir: VAULT_DIR, runtimeConfig });
|
|
91
|
+
return { success: true, output: `Self-diagnosis complete` };
|
|
92
|
+
}
|
|
93
|
+
return { success: true, output: `Conscious enforcement handled` };
|
|
95
94
|
}
|
|
95
|
+
|
|
96
|
+
// ─── Memory Maintenance ───
|
|
97
|
+
if (category === 'memory-maintenance') {
|
|
98
|
+
if (task.slug.startsWith('clarity-review-')) {
|
|
99
|
+
const { runClarityReview } = await import('./clarity-rewriter.mjs');
|
|
100
|
+
const result = await runClarityReview(task.target, { vaultDir: VAULT_DIR, inboxDir: path.join(BRAIN_DIR, 'memory-inbox', 'pending'), runtimeConfig });
|
|
101
|
+
return { success: true, output: `Clarity review complete: rewrote=${result.rewrote}` };
|
|
102
|
+
}
|
|
103
|
+
if (task.slug.startsWith('memory-compaction-')) {
|
|
104
|
+
const { runMemoryCompaction } = await import('./fact-seeker.mjs');
|
|
105
|
+
const result = await runMemoryCompaction({ vaultDir: VAULT_DIR, inboxDir: path.join(BRAIN_DIR, 'memory-inbox', 'pending'), runtimeConfig });
|
|
106
|
+
return { success: true, output: `Memory compaction complete. Consolidated nodes: ${result.consolidatedCount || 0}` };
|
|
107
|
+
}
|
|
108
|
+
return await runMaintenanceTask(task);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
logger.info({
|
|
112
|
+
subsystem: 'daemon-loop',
|
|
113
|
+
message: `Skipping unhandled task category: ${task.slug} [${category}]`,
|
|
114
|
+
});
|
|
115
|
+
return { success: true, output: 'Skipped unhandled task' };
|
|
96
116
|
} catch (err) {
|
|
97
117
|
logger.info({
|
|
98
118
|
subsystem: 'daemon-loop',
|
|
@@ -104,10 +124,10 @@ async function dispatchTask(task) {
|
|
|
104
124
|
|
|
105
125
|
// ─── Research Engine (Web Fetching Only — No LLM Synthesis) ─────────────────────
|
|
106
126
|
|
|
107
|
-
async function runResearchTask(task) {
|
|
127
|
+
async function runResearchTask(task, runtimeConfig) {
|
|
108
128
|
const inboxDir = path.join(BRAIN_DIR, 'memory-inbox', 'pending');
|
|
109
129
|
|
|
110
|
-
// Knowledge acquisition
|
|
130
|
+
// Phase 1: Knowledge acquisition
|
|
111
131
|
if (task.slug.startsWith('research-acquisition-') || task.category === 'proactive-research' || task.slug.includes('fact-seeker') || task.slug.includes('knowledge-acquisition')) {
|
|
112
132
|
const { runKnowledgeAcquisitionCycle } = await import('./fact-seeker.mjs');
|
|
113
133
|
const forceTopic = task.target || null;
|
|
@@ -119,6 +139,7 @@ async function runResearchTask(task) {
|
|
|
119
139
|
skillsDir: SKILLS_DIR,
|
|
120
140
|
derivedDir: DERIVED_DIR,
|
|
121
141
|
instructionsFile: INSTRUCTIONS_FILE,
|
|
142
|
+
runtimeConfig
|
|
122
143
|
});
|
|
123
144
|
if (result.skipped) return { success: true, output: `Knowledge acquisition: ${result.skipped}` };
|
|
124
145
|
const surfaceNote = result.surfaced ? ' [SURFACED]' : ' [staged]';
|
|
@@ -129,8 +150,59 @@ async function runResearchTask(task) {
|
|
|
129
150
|
};
|
|
130
151
|
}
|
|
131
152
|
|
|
153
|
+
// Phase 2: Deliberation
|
|
154
|
+
if (task.slug.startsWith('research-deliberation-')) {
|
|
155
|
+
const { runResearchDeliberationCycle } = await import('./fact-seeker.mjs');
|
|
156
|
+
const result = await runResearchDeliberationCycle({
|
|
157
|
+
vaultDir: VAULT_DIR,
|
|
158
|
+
nodeSlug: task._node_slug || 'pending',
|
|
159
|
+
topic: task.title || task.target || 'Unknown Topic',
|
|
160
|
+
runtimeConfig
|
|
161
|
+
});
|
|
162
|
+
return { success: true, output: `Deliberation complete: ${result.slug || 'done'}` };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Phase 3: Improvement
|
|
166
|
+
if (task.slug.startsWith('research-improvement-')) {
|
|
167
|
+
const { runResearchImprovementCycle } = await import('./fact-seeker.mjs');
|
|
168
|
+
const result = await runResearchImprovementCycle({
|
|
169
|
+
vaultDir: VAULT_DIR,
|
|
170
|
+
nodeSlug: task._node_slug || 'pending',
|
|
171
|
+
topic: task.title || task.target || 'Unknown Topic',
|
|
172
|
+
runtimeConfig
|
|
173
|
+
});
|
|
174
|
+
return { success: true, output: `Improvement complete` };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Phase 4: Monitoring
|
|
178
|
+
if (task.slug.startsWith('research-monitoring-')) {
|
|
179
|
+
const { runResearchMonitoringCycle } = await import('./fact-seeker.mjs');
|
|
180
|
+
const result = await runResearchMonitoringCycle({
|
|
181
|
+
vaultDir: VAULT_DIR,
|
|
182
|
+
nodeSlug: task._node_slug || 'pending',
|
|
183
|
+
topic: task.title || task.target || 'Unknown Topic',
|
|
184
|
+
runtimeConfig,
|
|
185
|
+
skillsDir: SKILLS_DIR,
|
|
186
|
+
derivedDir: DERIVED_DIR,
|
|
187
|
+
instructionsFile: INSTRUCTIONS_FILE
|
|
188
|
+
});
|
|
189
|
+
return { success: true, output: `Monitoring complete` };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Phase 5: Expansion
|
|
193
|
+
if (task.slug.startsWith('research-expansion-')) {
|
|
194
|
+
const { runResearchExpansionCycle } = await import('./fact-seeker.mjs');
|
|
195
|
+
const result = await runResearchExpansionCycle({
|
|
196
|
+
vaultDir: VAULT_DIR,
|
|
197
|
+
nodeSlug: task._node_slug || 'pending',
|
|
198
|
+
topic: task.title || task.target || 'Unknown Topic',
|
|
199
|
+
runtimeConfig
|
|
200
|
+
});
|
|
201
|
+
return { success: true, output: `Expansion complete` };
|
|
202
|
+
}
|
|
203
|
+
|
|
132
204
|
// Conclusion writer — validate pending drafts (deterministic checks)
|
|
133
|
-
if (task.slug.includes('validate')
|
|
205
|
+
if (task.slug.includes('validate')) {
|
|
134
206
|
const { runConclusionWriter } = await import('./conclusion-writer.mjs');
|
|
135
207
|
const result = await runConclusionWriter({
|
|
136
208
|
inboxDir,
|
|
@@ -309,7 +381,21 @@ async function main() {
|
|
|
309
381
|
// Mark complete
|
|
310
382
|
if ((source === 'explicit' || source === 'idle') && task._filepath) {
|
|
311
383
|
try {
|
|
312
|
-
|
|
384
|
+
if (result.skippedLLM) {
|
|
385
|
+
updateTaskStatus(task, 'pending', QUEUE_DIR);
|
|
386
|
+
} else if (result.success) {
|
|
387
|
+
updateTaskStatus(task, 'completed', QUEUE_DIR);
|
|
388
|
+
} else {
|
|
389
|
+
// Dead Letter Queue / Retry Logic
|
|
390
|
+
task.retry_count = (task.retry_count || 0) + 1;
|
|
391
|
+
if (task.retry_count >= 3) {
|
|
392
|
+
logger.info({ subsystem: 'daemon-loop', message: `Task ${task.slug} failed 3 times. Moving to DLQ (failed).` });
|
|
393
|
+
updateTaskStatus(task, 'failed', QUEUE_DIR);
|
|
394
|
+
} else {
|
|
395
|
+
logger.info({ subsystem: 'daemon-loop', message: `Task ${task.slug} failed. Retry ${task.retry_count}/3. Requeueing.` });
|
|
396
|
+
updateTaskStatus(task, 'pending', QUEUE_DIR);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
313
399
|
} catch (statusErr) {
|
|
314
400
|
logger.info({
|
|
315
401
|
subsystem: 'daemon-loop',
|
|
@@ -321,15 +407,33 @@ async function main() {
|
|
|
321
407
|
try {
|
|
322
408
|
const patch = {
|
|
323
409
|
status: result.success ? 'pending' : 'failed',
|
|
324
|
-
|
|
410
|
+
// Append the output rather than completely overwriting the original rationale
|
|
411
|
+
notes: result.output ? `Phase output: ${result.output}` : (result.error || null),
|
|
325
412
|
};
|
|
326
413
|
if (result.factSlug) {
|
|
327
414
|
patch.node_slug = result.factSlug;
|
|
328
415
|
}
|
|
329
|
-
//
|
|
330
|
-
if (result.
|
|
331
|
-
|
|
332
|
-
|
|
416
|
+
// If the task was skipped because it requires an LLM, DO NOT complete the research project.
|
|
417
|
+
if (result.skippedLLM) {
|
|
418
|
+
patch.status = 'pending';
|
|
419
|
+
} else if (result.success && result.factSlug) {
|
|
420
|
+
if (task._research_phase === 'acquisition') {
|
|
421
|
+
patch.status = 'pending';
|
|
422
|
+
patch.research_phase = 'deliberation';
|
|
423
|
+
patch.completed_at = new Date().toISOString();
|
|
424
|
+
} else if (task._research_phase === 'deliberation') {
|
|
425
|
+
patch.status = 'pending';
|
|
426
|
+
patch.research_phase = 'improvement';
|
|
427
|
+
} else if (task._research_phase === 'improvement') {
|
|
428
|
+
patch.status = 'pending';
|
|
429
|
+
patch.research_phase = 'monitoring';
|
|
430
|
+
} else if (task._research_phase === 'monitoring') {
|
|
431
|
+
patch.status = 'pending';
|
|
432
|
+
patch.research_phase = 'expansion';
|
|
433
|
+
} else if (task._research_phase === 'expansion') {
|
|
434
|
+
patch.status = 'done';
|
|
435
|
+
patch.completed_at = new Date().toISOString();
|
|
436
|
+
}
|
|
333
437
|
}
|
|
334
438
|
updateQueueItem(task._research_id, patch);
|
|
335
439
|
} catch (err) {
|
package/src/core/fact-seeker.mjs
CHANGED
|
@@ -529,7 +529,8 @@ export function buildMultiNoteGraph(topic, synthesis, parsedSupportingNotes, sta
|
|
|
529
529
|
return { slug, body: bodyLines.join('\n'), frontmatter };
|
|
530
530
|
});
|
|
531
531
|
|
|
532
|
-
const
|
|
532
|
+
const topicHash = crypto.createHash('md5').update(topic).digest('hex').slice(0, 10);
|
|
533
|
+
const masterSlug = `fact-master-${topicHash}`;
|
|
533
534
|
|
|
534
535
|
// Bidirectionally link the master node and supporting notes
|
|
535
536
|
const masterRelated = supportingNodes.map(n => n.slug);
|
|
@@ -658,7 +659,8 @@ async function writeAndSurfaceImmediately(topic, synthesis, sourceResults, {
|
|
|
658
659
|
const topResults = sourceResults.slice(0, 4);
|
|
659
660
|
for (const r of topResults) {
|
|
660
661
|
const summary = await synthesizeSupportingNote(topic, r, runtimeConfig);
|
|
661
|
-
const
|
|
662
|
+
const noteHash = crypto.createHash('md5').update(r.url || r.title || r.snippet).digest('hex').slice(0, 10);
|
|
663
|
+
const slug = `fact-note-${noteHash}`;
|
|
662
664
|
parsedSupportingNotes.push({ result: r, summary, slug });
|
|
663
665
|
}
|
|
664
666
|
|
|
@@ -700,7 +702,8 @@ async function writeDraftToInbox(topic, synthesis, sourceResults, inboxDir, runt
|
|
|
700
702
|
const topResults = sourceResults.slice(0, 4);
|
|
701
703
|
for (const r of topResults) {
|
|
702
704
|
const summary = await synthesizeSupportingNote(topic, r, runtimeConfig);
|
|
703
|
-
const
|
|
705
|
+
const noteHash = crypto.createHash('md5').update(r.url || r.title || r.snippet).digest('hex').slice(0, 10);
|
|
706
|
+
const slug = `fact-note-${noteHash}`;
|
|
704
707
|
parsedSupportingNotes.push({ result: r, summary, slug });
|
|
705
708
|
}
|
|
706
709
|
|
|
@@ -325,6 +325,14 @@ export function listQueue({ status, query, limit = 100, offset = 0, brainDir: ov
|
|
|
325
325
|
*/
|
|
326
326
|
export function addToQueue({ topic, priority = 'medium', notes, brainDir: overrideBrainDir } = {}) {
|
|
327
327
|
if (!topic) throw new Error('topic is required');
|
|
328
|
+
|
|
329
|
+
const items = loadQueue(overrideBrainDir);
|
|
330
|
+
const normalizedTopic = String(topic).trim().toLowerCase();
|
|
331
|
+
const existing = items.find(i => i.topic.trim().toLowerCase() === normalizedTopic);
|
|
332
|
+
if (existing) {
|
|
333
|
+
return existing;
|
|
334
|
+
}
|
|
335
|
+
|
|
328
336
|
const item = {
|
|
329
337
|
id: crypto.randomUUID(),
|
|
330
338
|
topic: String(topic),
|
|
@@ -338,7 +346,6 @@ export function addToQueue({ topic, priority = 'medium', notes, brainDir: overri
|
|
|
338
346
|
completed_at: null,
|
|
339
347
|
};
|
|
340
348
|
item.summary = compileResearchProjectSummary(item, null);
|
|
341
|
-
const items = loadQueue(overrideBrainDir);
|
|
342
349
|
items.unshift(item);
|
|
343
350
|
saveQueue(items, overrideBrainDir);
|
|
344
351
|
|
package/src/core/scheduler.mjs
CHANGED
|
@@ -210,6 +210,7 @@ export function generateIdleTask({ vaultDir, sessionsDir }) {
|
|
|
210
210
|
() => generateInferenceTask(vaultDir),
|
|
211
211
|
() => generatePostMortemTask(sessionsDir),
|
|
212
212
|
() => generateClarityReviewTask(vaultDir),
|
|
213
|
+
() => generateMemoryCompactionTask(vaultDir),
|
|
213
214
|
];
|
|
214
215
|
|
|
215
216
|
const strategy = cleanStrategies[_idleCycleCounter % cleanStrategies.length];
|
|
@@ -248,18 +249,34 @@ function generateClarityReviewTask(vaultDir) {
|
|
|
248
249
|
if (nodes.length === 0) {
|
|
249
250
|
return makeFallbackTask('memory-maintenance', 'No active nodes to review');
|
|
250
251
|
}
|
|
251
|
-
|
|
252
|
-
const target = nodes[crypto.randomInt(0, nodes.length)];
|
|
252
|
+
const target = nodes[Math.floor(Math.random() * nodes.length)];
|
|
253
253
|
return {
|
|
254
254
|
type: 'task',
|
|
255
|
-
slug: `clarity-review-${
|
|
255
|
+
slug: `clarity-review-${crypto.randomBytes(4).toString('hex')}`,
|
|
256
256
|
priority: 30,
|
|
257
257
|
category: 'memory-maintenance',
|
|
258
258
|
target: target.slug,
|
|
259
259
|
status: 'pending',
|
|
260
260
|
created_by: 'scheduler-idle',
|
|
261
|
-
reason: `Idle task:
|
|
262
|
-
body:
|
|
261
|
+
reason: `Idle task: selected active node ${target.slug} for clarity review.`,
|
|
262
|
+
body: `Analyze node ${target.slug} for clarity, deduplication, formatting, and structural integrity. Rewrite via SSSS strict compliance if needed.`,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Generate a memory compaction task to fuse highly overlapping fragmented nodes.
|
|
268
|
+
*/
|
|
269
|
+
function generateMemoryCompactionTask(vaultDir) {
|
|
270
|
+
return {
|
|
271
|
+
type: 'task',
|
|
272
|
+
slug: `memory-compaction-${crypto.randomBytes(4).toString('hex')}`,
|
|
273
|
+
priority: 35,
|
|
274
|
+
category: 'memory-maintenance',
|
|
275
|
+
target: 'global',
|
|
276
|
+
status: 'pending',
|
|
277
|
+
created_by: 'scheduler-idle',
|
|
278
|
+
reason: `Idle task: scan vault for fragmented nodes to merge into comprehensive master documents.`,
|
|
279
|
+
body: `Scan for highly overlapping or fragmented memory nodes and fuse them into comprehensive master nodes, archiving the fragments.`,
|
|
263
280
|
};
|
|
264
281
|
}
|
|
265
282
|
|
|
@@ -523,6 +540,7 @@ export function createScheduler({ queueDir, vaultDir, sessionsDir }) {
|
|
|
523
540
|
body,
|
|
524
541
|
_research_id: item.id,
|
|
525
542
|
_research_phase: phase,
|
|
543
|
+
_node_slug: item.node_slug,
|
|
526
544
|
});
|
|
527
545
|
}
|
|
528
546
|
if (researchItems.length > 0) {
|
package/src/core/schema.mjs
CHANGED
|
@@ -644,6 +644,28 @@ export const PhoneNumberSchema = z.object({
|
|
|
644
644
|
user_id: z.string().optional().nullable(),
|
|
645
645
|
});
|
|
646
646
|
|
|
647
|
+
export const DomainSchema = z.object({
|
|
648
|
+
type: z.literal('domain'),
|
|
649
|
+
domain_name: z.string(),
|
|
650
|
+
registrar: z.string(),
|
|
651
|
+
status: z.string(),
|
|
652
|
+
payment_status: z.string(),
|
|
653
|
+
workspace_id: z.string().optional().nullable(),
|
|
654
|
+
user_id: z.string().optional().nullable(),
|
|
655
|
+
base_price_usd: z.number().optional().nullable(),
|
|
656
|
+
purchase_price_usd: z.number().optional().nullable(),
|
|
657
|
+
annual_renewal_usd: z.number().optional().nullable(),
|
|
658
|
+
markup_amount_usd: z.number().optional().nullable(),
|
|
659
|
+
markup_rate: z.number().optional().nullable(),
|
|
660
|
+
expires_at: z.string().optional().nullable(),
|
|
661
|
+
payment_method: z.string().optional().nullable(),
|
|
662
|
+
vercel_domain_id: z.string().optional().nullable(),
|
|
663
|
+
stripe_payment_intent_id: z.string().optional().nullable(),
|
|
664
|
+
stripe_checkout_session_id: z.string().optional().nullable(),
|
|
665
|
+
credits_charged: z.number().optional().nullable(),
|
|
666
|
+
credit_transaction_id: z.string().optional().nullable(),
|
|
667
|
+
});
|
|
668
|
+
|
|
647
669
|
export const WorkspaceSchema = z.object({
|
|
648
670
|
type: z.literal('workspace'),
|
|
649
671
|
name: z.string(),
|
|
@@ -804,6 +826,7 @@ export const SSSS_SCHEMAS = {
|
|
|
804
826
|
workspace_transfer: WorkspaceTransferSchema,
|
|
805
827
|
extension: ExtensionSchema,
|
|
806
828
|
phone_number: PhoneNumberSchema,
|
|
829
|
+
domain: DomainSchema,
|
|
807
830
|
workspace: WorkspaceSchema,
|
|
808
831
|
commerce_catalog: CommerceCatalogSchema,
|
|
809
832
|
product: ProductSchema,
|
package/src/server/index.mjs
CHANGED
|
@@ -138,6 +138,23 @@ app.get('/health', requireAuthOrLocal, async (req, res) => {
|
|
|
138
138
|
// Determine overall status
|
|
139
139
|
const hasCriticalIssue = emergencyAlerts.length > 0 || daemonStatus === 'dead' || cliAgents.length === 0;
|
|
140
140
|
|
|
141
|
+
// Check Caddy and Cloudflare status
|
|
142
|
+
let caddyStatus = 'inactive';
|
|
143
|
+
let cloudflareStatus = 'inactive';
|
|
144
|
+
try {
|
|
145
|
+
const { exec } = await import('node:child_process');
|
|
146
|
+
const util = await import('node:util');
|
|
147
|
+
const execAsync = util.promisify(exec);
|
|
148
|
+
try {
|
|
149
|
+
await execAsync('pgrep caddy');
|
|
150
|
+
caddyStatus = 'active';
|
|
151
|
+
} catch { }
|
|
152
|
+
try {
|
|
153
|
+
await execAsync('pgrep cloudflared');
|
|
154
|
+
cloudflareStatus = 'active';
|
|
155
|
+
} catch { }
|
|
156
|
+
} catch { }
|
|
157
|
+
|
|
141
158
|
res.json({
|
|
142
159
|
status: hasCriticalIssue ? 'degraded' : 'healthy',
|
|
143
160
|
version: PACKAGE_VERSION,
|
|
@@ -146,6 +163,8 @@ app.get('/health', requireAuthOrLocal, async (req, res) => {
|
|
|
146
163
|
disk,
|
|
147
164
|
cli_agents: cliAgents,
|
|
148
165
|
daemon: daemonStatus,
|
|
166
|
+
caddy: caddyStatus,
|
|
167
|
+
cloudflare: cloudflareStatus,
|
|
149
168
|
emergency_alerts: emergencyAlerts || null,
|
|
150
169
|
vfs: {
|
|
151
170
|
exists: vaultExists,
|