phaibel 4.0.35 → 4.0.37
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/README.md +26 -31
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +39 -53
- package/dist/commands/chat.js.map +1 -1
- package/dist/context/context-tree-serializer.d.ts +8 -0
- package/dist/context/context-tree-serializer.d.ts.map +1 -0
- package/dist/context/context-tree-serializer.js +150 -0
- package/dist/context/context-tree-serializer.js.map +1 -0
- package/dist/context/context-tree.d.ts +67 -0
- package/dist/context/context-tree.d.ts.map +1 -0
- package/dist/context/context-tree.js +207 -0
- package/dist/context/context-tree.js.map +1 -0
- package/dist/context/scope-classifier.d.ts +9 -0
- package/dist/context/scope-classifier.d.ts.map +1 -0
- package/dist/context/scope-classifier.js +43 -0
- package/dist/context/scope-classifier.js.map +1 -0
- package/dist/feral/bootstrap.d.ts.map +1 -1
- package/dist/feral/bootstrap.js +9 -1
- package/dist/feral/bootstrap.js.map +1 -1
- package/dist/feral/catalog/usage-catalog-source.d.ts +7 -0
- package/dist/feral/catalog/usage-catalog-source.d.ts.map +1 -0
- package/dist/feral/catalog/usage-catalog-source.js +48 -0
- package/dist/feral/catalog/usage-catalog-source.js.map +1 -0
- package/dist/feral/node-code/data/chart-token-usage-node-code.d.ts +11 -0
- package/dist/feral/node-code/data/chart-token-usage-node-code.d.ts.map +1 -0
- package/dist/feral/node-code/data/chart-token-usage-node-code.js +130 -0
- package/dist/feral/node-code/data/chart-token-usage-node-code.js.map +1 -0
- package/dist/feral/node-code/data/query-token-usage-node-code.d.ts +10 -0
- package/dist/feral/node-code/data/query-token-usage-node-code.d.ts.map +1 -0
- package/dist/feral/node-code/data/query-token-usage-node-code.js +45 -0
- package/dist/feral/node-code/data/query-token-usage-node-code.js.map +1 -0
- package/dist/llm/providers/claude.d.ts.map +1 -1
- package/dist/llm/providers/claude.js +5 -0
- package/dist/llm/providers/claude.js.map +1 -1
- package/dist/llm/providers/openai.d.ts.map +1 -1
- package/dist/llm/providers/openai.js +9 -0
- package/dist/llm/providers/openai.js.map +1 -1
- package/dist/llm/token-usage.d.ts +39 -0
- package/dist/llm/token-usage.d.ts.map +1 -0
- package/dist/llm/token-usage.js +156 -0
- package/dist/llm/token-usage.js.map +1 -0
- package/dist/service/api-router.d.ts.map +1 -1
- package/dist/service/api-router.js +41 -0
- package/dist/service/api-router.js.map +1 -1
- package/dist/service/cron/scheduler.d.ts.map +1 -1
- package/dist/service/cron/scheduler.js +8 -0
- package/dist/service/cron/scheduler.js.map +1 -1
- package/dist/service/cron/world-model-synthesis.d.ts +22 -0
- package/dist/service/cron/world-model-synthesis.d.ts.map +1 -0
- package/dist/service/cron/world-model-synthesis.js +202 -0
- package/dist/service/cron/world-model-synthesis.js.map +1 -0
- package/dist/service/web-client.html +323 -1
- package/dist/service/web-server.d.ts.map +1 -1
- package/dist/service/web-server.js +1 -0
- package/dist/service/web-server.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
// World Model Synthesis — proactive intelligence about the user's world
|
|
3
|
+
//
|
|
4
|
+
// Periodically reads all entities, synthesizes them into a coherent model of
|
|
5
|
+
// the user's current state, and generates proactive insights/nudges. Results
|
|
6
|
+
// are stored in {vault}/.phaibel/world-model.json for the web client to display.
|
|
7
|
+
// Users grade each insight so the system learns what's useful over time.
|
|
8
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
9
|
+
import { promises as fs } from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { findVaultRoot, getUserName, getAgentName } from '../../state/manager.js';
|
|
12
|
+
import { getVaultConfigDir } from '../../paths.js';
|
|
13
|
+
import { getModelForCapability } from '../../llm/router.js';
|
|
14
|
+
import { listEntities } from '../../entities/entity.js';
|
|
15
|
+
import { loadEntityTypes } from '../../entities/entity-type-config.js';
|
|
16
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
17
|
+
// FILE I/O
|
|
18
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
19
|
+
async function getWorldModelPath() {
|
|
20
|
+
const dir = await getVaultConfigDir();
|
|
21
|
+
return path.join(dir, 'world-model.json');
|
|
22
|
+
}
|
|
23
|
+
export async function loadWorldModel() {
|
|
24
|
+
try {
|
|
25
|
+
const raw = await fs.readFile(await getWorldModelPath(), 'utf-8');
|
|
26
|
+
return JSON.parse(raw);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export async function saveWorldModel(model) {
|
|
33
|
+
const dir = await getVaultConfigDir();
|
|
34
|
+
await fs.mkdir(dir, { recursive: true });
|
|
35
|
+
await fs.writeFile(await getWorldModelPath(), JSON.stringify(model, null, 2));
|
|
36
|
+
}
|
|
37
|
+
export async function gradeInsight(insightId, grade) {
|
|
38
|
+
const model = await loadWorldModel();
|
|
39
|
+
if (!model)
|
|
40
|
+
return false;
|
|
41
|
+
const insight = model.insights.find(i => i.id === insightId);
|
|
42
|
+
if (!insight)
|
|
43
|
+
return false;
|
|
44
|
+
insight.grade = grade;
|
|
45
|
+
insight.gradedAt = new Date().toISOString();
|
|
46
|
+
await saveWorldModel(model);
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
50
|
+
// SYNTHESIS
|
|
51
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
52
|
+
export async function synthesizeWorldModel() {
|
|
53
|
+
const vaultRoot = await findVaultRoot();
|
|
54
|
+
if (!vaultRoot)
|
|
55
|
+
return 'skipped (no vault)';
|
|
56
|
+
const userName = await getUserName() || 'User';
|
|
57
|
+
const agentName = await getAgentName() || 'Phaibel';
|
|
58
|
+
const types = await loadEntityTypes();
|
|
59
|
+
const today = new Date().toISOString().split('T')[0];
|
|
60
|
+
const now = new Date();
|
|
61
|
+
// ── Gather all entities ──────────────────────────────────────────────
|
|
62
|
+
const entityCounts = {};
|
|
63
|
+
const summaries = [];
|
|
64
|
+
for (const type of types) {
|
|
65
|
+
try {
|
|
66
|
+
const entities = await listEntities(type.name);
|
|
67
|
+
entityCounts[type.name] = entities.length;
|
|
68
|
+
if (entities.length === 0)
|
|
69
|
+
continue;
|
|
70
|
+
// Build a compact summary of each entity type
|
|
71
|
+
const lines = [`## ${type.plural} (${entities.length})`];
|
|
72
|
+
for (const e of entities.slice(0, 50)) { // cap at 50 per type
|
|
73
|
+
const title = e.meta.title || '(untitled)';
|
|
74
|
+
const status = e.meta.status || '';
|
|
75
|
+
const dueDate = e.meta.dueDate || e.meta.startDate || '';
|
|
76
|
+
const priority = e.meta.priority || '';
|
|
77
|
+
const tags = Array.isArray(e.meta.tags) ? e.meta.tags.join(', ') : '';
|
|
78
|
+
const parts = [`- **${title}**`];
|
|
79
|
+
if (status)
|
|
80
|
+
parts.push(`[${status}]`);
|
|
81
|
+
if (priority)
|
|
82
|
+
parts.push(`(${priority})`);
|
|
83
|
+
if (dueDate)
|
|
84
|
+
parts.push(`due: ${dueDate}`);
|
|
85
|
+
if (tags)
|
|
86
|
+
parts.push(`tags: ${tags}`);
|
|
87
|
+
// Include body snippet for context
|
|
88
|
+
if (e.content.trim()) {
|
|
89
|
+
parts.push(`— ${e.content.trim().slice(0, 120)}`);
|
|
90
|
+
}
|
|
91
|
+
lines.push(parts.join(' '));
|
|
92
|
+
}
|
|
93
|
+
if (entities.length > 50) {
|
|
94
|
+
lines.push(`- ... and ${entities.length - 50} more`);
|
|
95
|
+
}
|
|
96
|
+
summaries.push(lines.join('\n'));
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
entityCounts[type.name] = 0;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (summaries.length === 0) {
|
|
103
|
+
return 'skipped (no entities)';
|
|
104
|
+
}
|
|
105
|
+
// ── Load previous model and check capacity ─────────────────────────
|
|
106
|
+
const MAX_INSIGHTS = 5;
|
|
107
|
+
const previousModel = await loadWorldModel();
|
|
108
|
+
const existingInsights = previousModel?.insights.filter(i => !i.grade) ?? [];
|
|
109
|
+
const slotsAvailable = MAX_INSIGHTS - existingInsights.length;
|
|
110
|
+
if (slotsAvailable <= 0) {
|
|
111
|
+
return `skipped (${existingInsights.length} ungraded insights already at cap)`;
|
|
112
|
+
}
|
|
113
|
+
let gradeFeedback = '';
|
|
114
|
+
if (previousModel?.insights.some(i => i.grade)) {
|
|
115
|
+
const graded = previousModel.insights.filter(i => i.grade);
|
|
116
|
+
const useful = graded.filter(i => i.grade === 'useful');
|
|
117
|
+
const notUseful = graded.filter(i => i.grade === 'not_useful');
|
|
118
|
+
gradeFeedback = `\n\nPREVIOUS INSIGHT FEEDBACK:
|
|
119
|
+
${useful.length} insights were graded "useful": ${useful.map(i => `"${i.title}" (${i.category})`).join(', ') || 'none'}
|
|
120
|
+
${notUseful.length} insights were graded "not useful": ${notUseful.map(i => `"${i.title}" (${i.category})`).join(', ') || 'none'}
|
|
121
|
+
|
|
122
|
+
Generate MORE insights like the useful ones and FEWER like the not-useful ones.`;
|
|
123
|
+
}
|
|
124
|
+
// ── Call LLM ─────────────────────────────────────────────────────────
|
|
125
|
+
const llm = await getModelForCapability('reason');
|
|
126
|
+
const prompt = `You are ${agentName}, a proactive personal assistant for ${userName}. Today is ${today}.
|
|
127
|
+
|
|
128
|
+
Analyze the following complete picture of ${userName}'s world — all their tasks, events, goals, people, notes, and other tracked items. Your job is to be PROACTIVE: surface things ${userName} should know, act on, or think about BEFORE they ask.
|
|
129
|
+
|
|
130
|
+
${summaries.join('\n\n')}
|
|
131
|
+
${gradeFeedback}
|
|
132
|
+
|
|
133
|
+
Generate exactly ${slotsAvailable} proactive insight${slotsAvailable === 1 ? '' : 's'}. Each insight should be something ${userName} would genuinely find valuable — not obvious observations, but connections, risks, opportunities, and timely nudges.
|
|
134
|
+
|
|
135
|
+
CATEGORIES:
|
|
136
|
+
- "reminder": Something time-sensitive that needs attention soon
|
|
137
|
+
- "opportunity": A chance to make progress or take advantage of timing
|
|
138
|
+
- "risk": Something that could go wrong if not addressed
|
|
139
|
+
- "progress": Positive momentum or milestone worth acknowledging
|
|
140
|
+
- "connection": A relationship between items ${userName} might not have noticed
|
|
141
|
+
- "suggestion": A proactive recommendation to improve their workflow or life
|
|
142
|
+
|
|
143
|
+
RULES:
|
|
144
|
+
- Be specific — reference actual entity titles and dates
|
|
145
|
+
- Be actionable — tell ${userName} what they could do, not just what you noticed
|
|
146
|
+
- Prioritize time-sensitive items (overdue tasks, upcoming events, approaching deadlines)
|
|
147
|
+
- Look for conflicts (double-booked events, competing deadlines)
|
|
148
|
+
- Notice stalled goals or forgotten tasks
|
|
149
|
+
- Spot opportunities to link people with events or tasks
|
|
150
|
+
- Keep each insight concise (1-3 sentences)
|
|
151
|
+
|
|
152
|
+
Respond with ONLY valid JSON (no markdown fences), matching this schema:
|
|
153
|
+
{
|
|
154
|
+
"insights": [
|
|
155
|
+
{
|
|
156
|
+
"category": "reminder|opportunity|risk|progress|connection|suggestion",
|
|
157
|
+
"title": "Short headline (under 60 chars)",
|
|
158
|
+
"body": "1-3 sentence explanation with specific details",
|
|
159
|
+
"priority": "high|medium|low",
|
|
160
|
+
"relatedEntities": ["type:id", "type:id"]
|
|
161
|
+
}
|
|
162
|
+
]
|
|
163
|
+
}`;
|
|
164
|
+
const response = await llm.chat([{ role: 'user', content: prompt }], {
|
|
165
|
+
systemPrompt: `You are a proactive personal intelligence engine. You analyze a person's complete set of commitments, goals, relationships, and plans to surface timely, actionable insights. Respond with ONLY valid JSON — no markdown, no explanation, no fences.`,
|
|
166
|
+
temperature: 0.4,
|
|
167
|
+
});
|
|
168
|
+
// ── Parse response ───────────────────────────────────────────────────
|
|
169
|
+
let insights;
|
|
170
|
+
try {
|
|
171
|
+
const cleaned = response.replace(/```json?\n?/g, '').replace(/```\n?/g, '').trim();
|
|
172
|
+
const parsed = JSON.parse(cleaned);
|
|
173
|
+
insights = parsed.insights.map((raw, i) => ({
|
|
174
|
+
id: `wm-${Date.now()}-${i}`,
|
|
175
|
+
category: raw.category || 'suggestion',
|
|
176
|
+
title: raw.title || 'Insight',
|
|
177
|
+
body: raw.body || '',
|
|
178
|
+
priority: raw.priority || 'medium',
|
|
179
|
+
relatedEntities: raw.relatedEntities || [],
|
|
180
|
+
grade: null,
|
|
181
|
+
gradedAt: null,
|
|
182
|
+
}));
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
console.error('[world-model] Failed to parse LLM response:', err);
|
|
186
|
+
return 'error: failed to parse insights';
|
|
187
|
+
}
|
|
188
|
+
// ── Merge with existing insights ────────────────────────────────────
|
|
189
|
+
// Keep existing ungraded insights + append new ones (cap at MAX_INSIGHTS)
|
|
190
|
+
const mergedInsights = [...existingInsights, ...insights].slice(0, MAX_INSIGHTS);
|
|
191
|
+
const model = {
|
|
192
|
+
synthesizedAt: now.toISOString(),
|
|
193
|
+
userName,
|
|
194
|
+
agentName,
|
|
195
|
+
entityCounts,
|
|
196
|
+
insights: mergedInsights,
|
|
197
|
+
};
|
|
198
|
+
await saveWorldModel(model);
|
|
199
|
+
const highCount = insights.filter(i => i.priority === 'high').length;
|
|
200
|
+
return `${insights.length} insights generated (${highCount} high priority)`;
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=world-model-synthesis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"world-model-synthesis.js","sourceRoot":"","sources":["../../../src/service/cron/world-model-synthesis.ts"],"names":[],"mappings":"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"}
|
|
@@ -289,6 +289,160 @@
|
|
|
289
289
|
flex-shrink: 0;
|
|
290
290
|
}
|
|
291
291
|
|
|
292
|
+
/* ── Content Types panel ────────────────────────────── */
|
|
293
|
+
|
|
294
|
+
.ctype-item {
|
|
295
|
+
padding: 6px 8px;
|
|
296
|
+
border-bottom: 1px solid rgba(255,255,255,0.04);
|
|
297
|
+
border-radius: 4px;
|
|
298
|
+
cursor: pointer;
|
|
299
|
+
transition: background 0.15s;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.ctype-item:last-child { border-bottom: none; }
|
|
303
|
+
.ctype-item:hover { background: rgba(255,255,255,0.06); }
|
|
304
|
+
|
|
305
|
+
.ctype-header {
|
|
306
|
+
display: flex;
|
|
307
|
+
align-items: center;
|
|
308
|
+
gap: 6px;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
.ctype-name {
|
|
312
|
+
font-size: 12px;
|
|
313
|
+
font-weight: 600;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.ctype-count {
|
|
317
|
+
font-size: 10px;
|
|
318
|
+
color: var(--dim);
|
|
319
|
+
margin-left: auto;
|
|
320
|
+
flex-shrink: 0;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.ctype-desc {
|
|
324
|
+
font-size: 10px;
|
|
325
|
+
color: var(--dim);
|
|
326
|
+
line-height: 1.3;
|
|
327
|
+
margin-top: 2px;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
.ctype-fields {
|
|
331
|
+
display: flex;
|
|
332
|
+
flex-wrap: wrap;
|
|
333
|
+
gap: 3px;
|
|
334
|
+
margin-top: 4px;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
.ctype-field {
|
|
338
|
+
font-size: 9px;
|
|
339
|
+
padding: 1px 5px;
|
|
340
|
+
border-radius: 3px;
|
|
341
|
+
background: rgba(255,255,255,0.06);
|
|
342
|
+
color: var(--dim);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/* ── Insights panel ─────────────────────────────────── */
|
|
346
|
+
|
|
347
|
+
.insight-item {
|
|
348
|
+
padding: 8px 0;
|
|
349
|
+
border-bottom: 1px solid rgba(255,255,255,0.04);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
.insight-item:last-child { border-bottom: none; }
|
|
353
|
+
|
|
354
|
+
.insight-header {
|
|
355
|
+
display: flex;
|
|
356
|
+
align-items: center;
|
|
357
|
+
gap: 6px;
|
|
358
|
+
margin-bottom: 4px;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.insight-category {
|
|
362
|
+
font-size: 9px;
|
|
363
|
+
font-weight: 600;
|
|
364
|
+
text-transform: uppercase;
|
|
365
|
+
letter-spacing: 0.05em;
|
|
366
|
+
padding: 1px 5px;
|
|
367
|
+
border-radius: 3px;
|
|
368
|
+
flex-shrink: 0;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
.insight-category.reminder { background: rgba(239,68,68,0.2); color: #f87171; }
|
|
372
|
+
.insight-category.risk { background: rgba(239,68,68,0.15); color: #fca5a5; }
|
|
373
|
+
.insight-category.opportunity { background: rgba(34,197,94,0.2); color: #4ade80; }
|
|
374
|
+
.insight-category.progress { background: rgba(59,130,246,0.2); color: #60a5fa; }
|
|
375
|
+
.insight-category.connection { background: rgba(168,85,247,0.2); color: #c084fc; }
|
|
376
|
+
.insight-category.suggestion { background: rgba(245,158,11,0.2); color: #fbbf24; }
|
|
377
|
+
|
|
378
|
+
.insight-priority {
|
|
379
|
+
font-size: 9px;
|
|
380
|
+
color: var(--dim);
|
|
381
|
+
margin-left: auto;
|
|
382
|
+
flex-shrink: 0;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.insight-priority.high { color: #f87171; }
|
|
386
|
+
|
|
387
|
+
.insight-title {
|
|
388
|
+
font-size: 12px;
|
|
389
|
+
font-weight: 600;
|
|
390
|
+
margin-bottom: 2px;
|
|
391
|
+
line-height: 1.3;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.insight-body {
|
|
395
|
+
font-size: 11px;
|
|
396
|
+
color: var(--dim);
|
|
397
|
+
line-height: 1.4;
|
|
398
|
+
margin-bottom: 6px;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
.insight-grade {
|
|
402
|
+
display: flex;
|
|
403
|
+
gap: 4px;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.insight-grade-btn {
|
|
407
|
+
background: rgba(255,255,255,0.06);
|
|
408
|
+
border: 1px solid rgba(255,255,255,0.1);
|
|
409
|
+
color: var(--dim);
|
|
410
|
+
font-size: 11px;
|
|
411
|
+
padding: 2px 8px;
|
|
412
|
+
border-radius: 3px;
|
|
413
|
+
cursor: pointer;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.insight-grade-btn:hover {
|
|
417
|
+
background: rgba(255,255,255,0.12);
|
|
418
|
+
color: var(--text);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
.insight-grade-btn.graded {
|
|
422
|
+
cursor: default;
|
|
423
|
+
opacity: 0.7;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
.insight-grade-btn.graded.useful {
|
|
427
|
+
background: rgba(34,197,94,0.2);
|
|
428
|
+
border-color: rgba(34,197,94,0.3);
|
|
429
|
+
color: #4ade80;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
.insight-grade-btn.graded.not-useful {
|
|
433
|
+
background: rgba(239,68,68,0.15);
|
|
434
|
+
border-color: rgba(239,68,68,0.2);
|
|
435
|
+
color: #fca5a5;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.insights-meta {
|
|
439
|
+
font-size: 10px;
|
|
440
|
+
color: var(--dim);
|
|
441
|
+
margin-top: 6px;
|
|
442
|
+
padding-top: 6px;
|
|
443
|
+
border-top: 1px solid rgba(255,255,255,0.04);
|
|
444
|
+
}
|
|
445
|
+
|
|
292
446
|
/* ── Scheduler panel ────────────────────────────────── */
|
|
293
447
|
|
|
294
448
|
.sched-item {
|
|
@@ -1035,6 +1189,14 @@
|
|
|
1035
1189
|
<div id="timeline-body">
|
|
1036
1190
|
<div class="empty-state">Loading…</div>
|
|
1037
1191
|
</div>
|
|
1192
|
+
<div class="panel-section-title" style="margin-top: 16px;">Content Types</div>
|
|
1193
|
+
<div id="content-types-body">
|
|
1194
|
+
<div class="empty-state">Loading…</div>
|
|
1195
|
+
</div>
|
|
1196
|
+
<div class="panel-section-title" style="margin-top: 16px;">Insights</div>
|
|
1197
|
+
<div id="insights-body">
|
|
1198
|
+
<div class="empty-state">No insights yet</div>
|
|
1199
|
+
</div>
|
|
1038
1200
|
<div class="panel-section-title" style="margin-top: 16px;">Scheduler</div>
|
|
1039
1201
|
<div id="scheduler-body">
|
|
1040
1202
|
<div class="empty-state">Loading scheduler…</div>
|
|
@@ -1301,6 +1463,163 @@ async function markTaskDone(checkEl) {
|
|
|
1301
1463
|
} catch { await fetchScheduler(); }
|
|
1302
1464
|
};
|
|
1303
1465
|
|
|
1466
|
+
// ── Insights (World Model Synthesis) ─────────────────────
|
|
1467
|
+
var insightsBody = document.getElementById('insights-body');
|
|
1468
|
+
|
|
1469
|
+
async function fetchInsights() {
|
|
1470
|
+
try {
|
|
1471
|
+
const res = await fetch('/api/insights');
|
|
1472
|
+
const data = await res.json();
|
|
1473
|
+
renderInsights(data);
|
|
1474
|
+
} catch {
|
|
1475
|
+
insightsBody.innerHTML = '<div class="empty-state">Failed to load insights.</div>';
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
function renderInsights(data) {
|
|
1480
|
+
if (!data.insights || data.insights.length === 0) {
|
|
1481
|
+
insightsBody.innerHTML = '<div class="empty-state">No insights yet. The World Model runs every 10 minutes.</div>';
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1484
|
+
|
|
1485
|
+
// Sort: high priority first, then ungraded first
|
|
1486
|
+
var sorted = data.insights.slice().sort(function(a, b) {
|
|
1487
|
+
var pOrd = { high: 0, medium: 1, low: 2 };
|
|
1488
|
+
var pDiff = (pOrd[a.priority] || 1) - (pOrd[b.priority] || 1);
|
|
1489
|
+
if (pDiff !== 0) return pDiff;
|
|
1490
|
+
// Ungraded before graded
|
|
1491
|
+
if (!a.grade && b.grade) return -1;
|
|
1492
|
+
if (a.grade && !b.grade) return 1;
|
|
1493
|
+
return 0;
|
|
1494
|
+
});
|
|
1495
|
+
|
|
1496
|
+
var html = '';
|
|
1497
|
+
for (var i = 0; i < sorted.length; i++) {
|
|
1498
|
+
var ins = sorted[i];
|
|
1499
|
+
var gradeHtml = '';
|
|
1500
|
+
if (ins.grade) {
|
|
1501
|
+
var cls = ins.grade === 'useful' ? 'graded useful' : 'graded not-useful';
|
|
1502
|
+
var label = ins.grade === 'useful' ? 'Useful' : 'Not useful';
|
|
1503
|
+
gradeHtml = '<span class="insight-grade-btn ' + cls + '">' + esc(label) + '</span>';
|
|
1504
|
+
} else {
|
|
1505
|
+
gradeHtml = '<button class="insight-grade-btn" onclick="gradeInsight(\'' + esc(ins.id) + '\', \'useful\')">Useful</button>'
|
|
1506
|
+
+ '<button class="insight-grade-btn" onclick="gradeInsight(\'' + esc(ins.id) + '\', \'not_useful\')">Not useful</button>';
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
html += '<div class="insight-item">'
|
|
1510
|
+
+ '<div class="insight-header">'
|
|
1511
|
+
+ '<span class="insight-category ' + esc(ins.category) + '">' + esc(ins.category) + '</span>'
|
|
1512
|
+
+ '<span class="insight-priority ' + esc(ins.priority) + '">' + esc(ins.priority) + '</span>'
|
|
1513
|
+
+ '</div>'
|
|
1514
|
+
+ '<div class="insight-title">' + esc(ins.title) + '</div>'
|
|
1515
|
+
+ '<div class="insight-body">' + esc(ins.body) + '</div>'
|
|
1516
|
+
+ '<div class="insight-grade">' + gradeHtml + '</div>'
|
|
1517
|
+
+ '</div>';
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
if (data.synthesizedAt) {
|
|
1521
|
+
var ago = timeAgo(new Date(data.synthesizedAt));
|
|
1522
|
+
html += '<div class="insights-meta">Synthesized ' + esc(ago) + '</div>';
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
insightsBody.innerHTML = html;
|
|
1526
|
+
}
|
|
1527
|
+
|
|
1528
|
+
window.gradeInsight = async function(insightId, grade) {
|
|
1529
|
+
try {
|
|
1530
|
+
await fetch('/api/insights/' + encodeURIComponent(insightId) + '/grade', {
|
|
1531
|
+
method: 'POST',
|
|
1532
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1533
|
+
body: JSON.stringify({ grade: grade }),
|
|
1534
|
+
});
|
|
1535
|
+
await fetchInsights();
|
|
1536
|
+
} catch { /* ignore */ }
|
|
1537
|
+
};
|
|
1538
|
+
|
|
1539
|
+
function timeAgo(date) {
|
|
1540
|
+
var seconds = Math.floor((Date.now() - date.getTime()) / 1000);
|
|
1541
|
+
if (seconds < 60) return 'just now';
|
|
1542
|
+
var minutes = Math.floor(seconds / 60);
|
|
1543
|
+
if (minutes < 60) return minutes + 'm ago';
|
|
1544
|
+
var hours = Math.floor(minutes / 60);
|
|
1545
|
+
if (hours < 24) return hours + 'h ago';
|
|
1546
|
+
var days = Math.floor(hours / 24);
|
|
1547
|
+
return days + 'd ago';
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
// ── Content Types ────────────────────────────────────────
|
|
1551
|
+
var contentTypesBody = document.getElementById('content-types-body');
|
|
1552
|
+
|
|
1553
|
+
async function fetchContentTypes() {
|
|
1554
|
+
try {
|
|
1555
|
+
const [typesRes, calRes] = await Promise.all([
|
|
1556
|
+
fetch('/api/types'),
|
|
1557
|
+
fetch('/api/calendar'),
|
|
1558
|
+
]);
|
|
1559
|
+
const types = await typesRes.json();
|
|
1560
|
+
const calItems = await calRes.json();
|
|
1561
|
+
|
|
1562
|
+
// Count entities per type from calendar data (which includes all dated entities)
|
|
1563
|
+
// Also fetch entity counts per type via listing
|
|
1564
|
+
const counts = {};
|
|
1565
|
+
for (const item of calItems) {
|
|
1566
|
+
var t = item.entityType || '';
|
|
1567
|
+
counts[t] = (counts[t] || 0) + 1;
|
|
1568
|
+
}
|
|
1569
|
+
|
|
1570
|
+
renderContentTypes(types, counts);
|
|
1571
|
+
} catch {
|
|
1572
|
+
contentTypesBody.innerHTML = '<div class="empty-state">Failed to load.</div>';
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
function renderContentTypes(types, counts) {
|
|
1577
|
+
if (!types || types.length === 0) {
|
|
1578
|
+
contentTypesBody.innerHTML = '<div class="empty-state">No content types.</div>';
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
var html = '';
|
|
1583
|
+
for (var i = 0; i < types.length; i++) {
|
|
1584
|
+
var t = types[i];
|
|
1585
|
+
var count = counts[t.name] || '';
|
|
1586
|
+
var countLabel = count ? count + '' : '';
|
|
1587
|
+
var desc = t.description || '';
|
|
1588
|
+
var fields = (t.fields || []).slice(0, 6);
|
|
1589
|
+
|
|
1590
|
+
html += '<div class="ctype-item" onclick="chatContentType(\'' + esc(t.plural) + '\')">'
|
|
1591
|
+
+ '<div class="ctype-header">'
|
|
1592
|
+
+ '<span class="ctype-name">' + esc(t.plural) + '</span>'
|
|
1593
|
+
+ (countLabel ? '<span class="ctype-count">' + esc(countLabel) + '</span>' : '')
|
|
1594
|
+
+ '</div>';
|
|
1595
|
+
|
|
1596
|
+
if (desc) {
|
|
1597
|
+
html += '<div class="ctype-desc">' + esc(desc) + '</div>';
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
if (fields.length > 0) {
|
|
1601
|
+
html += '<div class="ctype-fields">';
|
|
1602
|
+
for (var j = 0; j < fields.length; j++) {
|
|
1603
|
+
html += '<span class="ctype-field">' + esc(fields[j].key) + '</span>';
|
|
1604
|
+
}
|
|
1605
|
+
if (t.fields.length > 6) {
|
|
1606
|
+
html += '<span class="ctype-field">+' + (t.fields.length - 6) + '</span>';
|
|
1607
|
+
}
|
|
1608
|
+
html += '</div>';
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
html += '</div>';
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
contentTypesBody.innerHTML = html;
|
|
1615
|
+
}
|
|
1616
|
+
|
|
1617
|
+
window.chatContentType = function(plural) {
|
|
1618
|
+
var msg = 'show me the content for ' + plural;
|
|
1619
|
+
chatInput.value = msg;
|
|
1620
|
+
chatForm.dispatchEvent(new Event('submit'));
|
|
1621
|
+
};
|
|
1622
|
+
|
|
1304
1623
|
async function fetchStatus() {
|
|
1305
1624
|
try {
|
|
1306
1625
|
const res = await fetch('/api/status');
|
|
@@ -2036,7 +2355,9 @@ async function markTaskDone(checkEl) {
|
|
|
2036
2355
|
checkOnboarding().then(function(needsOnboarding) {
|
|
2037
2356
|
if (!needsOnboarding) {
|
|
2038
2357
|
fetchCalendarNames().then(function() { fetchTimeline(); });
|
|
2358
|
+
fetchContentTypes();
|
|
2039
2359
|
fetchScheduler();
|
|
2360
|
+
fetchInsights();
|
|
2040
2361
|
connectWs();
|
|
2041
2362
|
} else {
|
|
2042
2363
|
// Still connect WS and fetch basic status even during onboarding
|
|
@@ -2045,9 +2366,10 @@ async function markTaskDone(checkEl) {
|
|
|
2045
2366
|
fetchStatus();
|
|
2046
2367
|
});
|
|
2047
2368
|
|
|
2048
|
-
// Poll status and
|
|
2369
|
+
// Poll status, scheduler, and insights periodically
|
|
2049
2370
|
setInterval(fetchStatus, 30000);
|
|
2050
2371
|
setInterval(fetchScheduler, 30000);
|
|
2372
|
+
setInterval(fetchInsights, 120000); // every 2 minutes
|
|
2051
2373
|
})();
|
|
2052
2374
|
</script>
|
|
2053
2375
|
</body>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"web-server.d.ts","sourceRoot":"","sources":["../../src/service/web-server.ts"],"names":[],"mappings":"AAyBA,qBAAa,SAAS;IAClB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,GAAG,CAAgC;IAC3C,OAAO,CAAC,WAAW,CAAc;IAE3B,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BlC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB3B,4DAA4D;IAC5D,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI;YAY9B,UAAU;
|
|
1
|
+
{"version":3,"file":"web-server.d.ts","sourceRoot":"","sources":["../../src/service/web-server.ts"],"names":[],"mappings":"AAyBA,qBAAa,SAAS;IAClB,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,GAAG,CAAgC;IAC3C,OAAO,CAAC,WAAW,CAAc;IAE3B,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0BlC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB3B,4DAA4D;IAC5D,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,GAAG,IAAI;YAY9B,UAAU;IAiJxB,OAAO,CAAC,QAAQ;YAqDF,UAAU;YA0DV,aAAa;YAwBb,SAAS;YAmCT,SAAS;YAuDT,YAAY;YAqBZ,WAAW;IA2JzB,OAAO,CAAC,QAAQ;CAQnB"}
|
|
@@ -88,6 +88,7 @@ export class WebServer {
|
|
|
88
88
|
url.pathname.startsWith('/api/entities') ||
|
|
89
89
|
url.pathname.startsWith('/api/search') ||
|
|
90
90
|
url.pathname.startsWith('/api/processes') ||
|
|
91
|
+
url.pathname.startsWith('/api/insights') ||
|
|
91
92
|
url.pathname === '/api/calendar') {
|
|
92
93
|
const handled = await handleApiRoute(req, res, url);
|
|
93
94
|
if (handled)
|