@cccarv82/freya 3.5.1 → 3.5.2
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/cli/web.js +150 -6
- package/package.json +1 -1
package/cli/web.js
CHANGED
|
@@ -98,6 +98,113 @@ function newestFile(dir, prefix) {
|
|
|
98
98
|
return files[0]?.p || null;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
// Daily-logs ↔ SQLite sync: keeps the daily_logs table in sync with .md files
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
function syncDailyLogs(workspaceDir) {
|
|
105
|
+
try {
|
|
106
|
+
const logsDir = path.join(workspaceDir, 'logs', 'daily');
|
|
107
|
+
if (!exists(logsDir)) return 0;
|
|
108
|
+
const files = fs.readdirSync(logsDir).filter(f => /^\d{4}-\d{2}-\d{2}\.md$/.test(f));
|
|
109
|
+
if (!files.length) return 0;
|
|
110
|
+
|
|
111
|
+
const upsert = dl.db.prepare(`
|
|
112
|
+
INSERT INTO daily_logs (date, raw_markdown) VALUES (?, ?)
|
|
113
|
+
ON CONFLICT(date) DO UPDATE SET raw_markdown = excluded.raw_markdown
|
|
114
|
+
`);
|
|
115
|
+
|
|
116
|
+
let synced = 0;
|
|
117
|
+
const tx = dl.db.transaction((fileList) => {
|
|
118
|
+
for (const file of fileList) {
|
|
119
|
+
const date = file.replace('.md', '');
|
|
120
|
+
const content = fs.readFileSync(path.join(logsDir, file), 'utf8');
|
|
121
|
+
upsert.run(date, content);
|
|
122
|
+
synced++;
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
tx(files);
|
|
126
|
+
return synced;
|
|
127
|
+
} catch (e) {
|
|
128
|
+
console.error('[sync] Daily-logs sync failed:', e.message);
|
|
129
|
+
return 0;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
// Build real data context for Orchestrator (chat) — feeds SQLite + daily logs
|
|
135
|
+
// as plain-text so the LLM has actual data to synthesize answers from
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
function buildDataContext(workspaceDir, maxDays) {
|
|
138
|
+
maxDays = maxDays || 7;
|
|
139
|
+
const parts = [];
|
|
140
|
+
|
|
141
|
+
// 1. Recent daily logs (from filesystem — most up-to-date source)
|
|
142
|
+
try {
|
|
143
|
+
const logsDir = path.join(workspaceDir, 'logs', 'daily');
|
|
144
|
+
if (exists(logsDir)) {
|
|
145
|
+
const files = fs.readdirSync(logsDir)
|
|
146
|
+
.filter(f => /^\d{4}-\d{2}-\d{2}\.md$/.test(f))
|
|
147
|
+
.sort()
|
|
148
|
+
.slice(-maxDays);
|
|
149
|
+
if (files.length) {
|
|
150
|
+
parts.push('\n\n[DAILY LOGS — ÚLTIMOS ' + files.length + ' DIAS]');
|
|
151
|
+
for (const file of files) {
|
|
152
|
+
const date = file.replace('.md', '');
|
|
153
|
+
const content = fs.readFileSync(path.join(logsDir, file), 'utf8');
|
|
154
|
+
// Truncate very large logs to avoid token overflow
|
|
155
|
+
const trimmed = content.length > 8000 ? content.slice(0, 8000) + '\n...(truncado)' : content;
|
|
156
|
+
parts.push(`\n--- LOG ${date} ---\n${trimmed}`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
} catch (e) {
|
|
161
|
+
console.error('[context] Failed to read daily logs:', e.message);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 2. Pending tasks from SQLite
|
|
165
|
+
try {
|
|
166
|
+
const tasks = dl.db.prepare("SELECT id, description, category, status, project_slug, created_at, due_date FROM tasks WHERE status = 'PENDING' ORDER BY created_at DESC LIMIT 50").all();
|
|
167
|
+
if (tasks.length) {
|
|
168
|
+
parts.push('\n\n[TASKS PENDENTES — SQLite (' + tasks.length + ' tasks)]');
|
|
169
|
+
for (const t of tasks) {
|
|
170
|
+
parts.push(`• [${t.category}] ${t.description} (projeto: ${t.project_slug || 'N/A'}, criado: ${t.created_at || '?'}${t.due_date ? ', prazo: ' + t.due_date : ''})`);
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
parts.push('\n\n[TASKS PENDENTES — SQLite: nenhuma task registrada]');
|
|
174
|
+
}
|
|
175
|
+
} catch (e) {
|
|
176
|
+
parts.push('\n\n[TASKS: erro ao consultar SQLite — ' + e.message + ']');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 3. Open blockers from SQLite
|
|
180
|
+
try {
|
|
181
|
+
const blockers = dl.db.prepare("SELECT id, title, severity, status, project_slug, owner, next_action, created_at FROM blockers WHERE status IN ('OPEN','MITIGATING') ORDER BY created_at DESC LIMIT 30").all();
|
|
182
|
+
if (blockers.length) {
|
|
183
|
+
parts.push('\n\n[BLOCKERS ABERTOS — SQLite (' + blockers.length + ' blockers)]');
|
|
184
|
+
for (const b of blockers) {
|
|
185
|
+
parts.push(`• [${b.severity}] ${b.title} (projeto: ${b.project_slug || 'N/A'}, status: ${b.status}, owner: ${b.owner || '?'})`);
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
parts.push('\n\n[BLOCKERS ABERTOS — SQLite: nenhum blocker registrado]');
|
|
189
|
+
}
|
|
190
|
+
} catch (e) {
|
|
191
|
+
parts.push('\n\n[BLOCKERS: erro ao consultar SQLite — ' + e.message + ']');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// 4. Active projects
|
|
195
|
+
try {
|
|
196
|
+
const projects = dl.db.prepare("SELECT slug, client, name FROM projects WHERE is_active = 1 ORDER BY slug").all();
|
|
197
|
+
if (projects.length) {
|
|
198
|
+
parts.push('\n\n[PROJETOS ATIVOS — SQLite (' + projects.length + ')]');
|
|
199
|
+
for (const p of projects) {
|
|
200
|
+
parts.push(`• ${p.slug} — ${p.name || p.client || 'sem nome'}`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
} catch { /* ignore */ }
|
|
204
|
+
|
|
205
|
+
return parts.join('\n');
|
|
206
|
+
}
|
|
207
|
+
|
|
101
208
|
function settingsPath(workspaceDir) {
|
|
102
209
|
return path.join(workspaceDir, 'data', 'settings', 'settings.json');
|
|
103
210
|
}
|
|
@@ -2755,6 +2862,14 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
2755
2862
|
await autoUpdate(wsDir);
|
|
2756
2863
|
} catch { /* non-fatal */ }
|
|
2757
2864
|
|
|
2865
|
+
// Sync daily log .md files → SQLite daily_logs table on startup
|
|
2866
|
+
try {
|
|
2867
|
+
const synced = syncDailyLogs(wsDir);
|
|
2868
|
+
if (synced > 0) console.log(`[FREYA] Synced ${synced} daily logs to SQLite`);
|
|
2869
|
+
} catch (e) {
|
|
2870
|
+
console.error('[FREYA] Warning: daily-logs sync failed:', e.message || String(e));
|
|
2871
|
+
}
|
|
2872
|
+
|
|
2758
2873
|
const host = '127.0.0.1';
|
|
2759
2874
|
|
|
2760
2875
|
const server = http.createServer(async (req, res) => {
|
|
@@ -3736,6 +3851,14 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
3736
3851
|
}
|
|
3737
3852
|
}
|
|
3738
3853
|
|
|
3854
|
+
// Sync this daily log file to SQLite so chat/RAG can find it
|
|
3855
|
+
try {
|
|
3856
|
+
const upsert = dl.db.prepare(`INSERT INTO daily_logs (date, raw_markdown) VALUES (?, ?) ON CONFLICT(date) DO UPDATE SET raw_markdown = excluded.raw_markdown`);
|
|
3857
|
+
upsert.run(d, fs.readFileSync(file, 'utf8'));
|
|
3858
|
+
} catch (syncErr) {
|
|
3859
|
+
console.error('[inbox] Failed to sync daily log to SQLite:', syncErr.message);
|
|
3860
|
+
}
|
|
3861
|
+
|
|
3739
3862
|
return safeJson(res, 200, { ok: true, file: path.relative(workspaceDir, file).replace(/\\/g, '/'), appended: true });
|
|
3740
3863
|
}
|
|
3741
3864
|
|
|
@@ -4277,6 +4400,12 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
4277
4400
|
return `\n\n---\nFILE: ${rel}\n---\n` + fs.readFileSync(p, 'utf8');
|
|
4278
4401
|
}).join('');
|
|
4279
4402
|
|
|
4403
|
+
// Ensure daily logs are synced to SQLite before querying
|
|
4404
|
+
try { syncDailyLogs(workspaceDir); } catch { /* non-fatal */ }
|
|
4405
|
+
|
|
4406
|
+
// Build real data context from SQLite + daily log files
|
|
4407
|
+
const dataContext = buildDataContext(workspaceDir, 7);
|
|
4408
|
+
|
|
4280
4409
|
// V2 RAG Context (graceful fallback if embedder/sharp not available)
|
|
4281
4410
|
const dm = new DataManager(workspaceDir, path.join(workspaceDir, 'logs'));
|
|
4282
4411
|
let ragContext = '';
|
|
@@ -4301,8 +4430,23 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
4301
4430
|
}
|
|
4302
4431
|
}
|
|
4303
4432
|
|
|
4304
|
-
// System instructions
|
|
4305
|
-
|
|
4433
|
+
// System instructions — includes REAL data context so the Orchestrator
|
|
4434
|
+
// can synthesize answers without needing to "call" sub-agents at runtime
|
|
4435
|
+
const oracleSysInstr = `Você é FREYA — Assistente Responsiva com Otimização Aprimorada.
|
|
4436
|
+
|
|
4437
|
+
PAPEL: Você é o agente principal do sistema. Responda SEMPRE em linguagem natural, estruturada e consultiva.
|
|
4438
|
+
|
|
4439
|
+
REGRAS ABSOLUTAS:
|
|
4440
|
+
- NUNCA exponha JSONs brutos, nomes de agentes internos (Oracle, SM Agent, Ingestor), ou hierarquia de roteamento.
|
|
4441
|
+
- NUNCA peça ao usuário para "chamar outro agente" ou "invocar o Orchestrator".
|
|
4442
|
+
- NUNCA diga "como agente X, não posso...". Você é FREYA, um sistema único e coeso.
|
|
4443
|
+
- SEMPRE sintetize os dados abaixo em respostas úteis, organizadas e em português brasileiro.
|
|
4444
|
+
- Use a estrutura: Contexto → Análise → Recomendações → Próximos passos.
|
|
4445
|
+
- Termine com: — FREYA\\nAssistente Responsiva com Otimização Aprimorada
|
|
4446
|
+
|
|
4447
|
+
DADOS REAIS DO WORKSPACE (use estes dados para responder):
|
|
4448
|
+
${dataContext}
|
|
4449
|
+
${ragContext}${imageContext}`;
|
|
4306
4450
|
|
|
4307
4451
|
const cmd = process.env.COPILOT_CMD || 'copilot';
|
|
4308
4452
|
|
|
@@ -4318,15 +4462,15 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
4318
4462
|
}
|
|
4319
4463
|
}
|
|
4320
4464
|
|
|
4321
|
-
// ENAMETOOLONG fix: when prompt is large, write
|
|
4465
|
+
// ENAMETOOLONG fix: when prompt is large, write full prompt to temp file
|
|
4322
4466
|
const fullOraclePrompt = `${oracleSysInstr}\n\nREGRAS:${rulesText}\n\nCONSULTA DO USUÁRIO:\n${query}\n`;
|
|
4323
4467
|
const SAFE_ARG_LEN = 24000;
|
|
4324
4468
|
let oracleTmpFile = null;
|
|
4325
4469
|
let r;
|
|
4326
4470
|
if (fullOraclePrompt.length > SAFE_ARG_LEN) {
|
|
4327
|
-
oracleTmpFile = path.join(os.tmpdir(), `freya-
|
|
4328
|
-
fs.writeFileSync(oracleTmpFile,
|
|
4329
|
-
const filePrompt =
|
|
4471
|
+
oracleTmpFile = path.join(os.tmpdir(), `freya-orchestrator-${Date.now()}.txt`);
|
|
4472
|
+
fs.writeFileSync(oracleTmpFile, fullOraclePrompt, 'utf8');
|
|
4473
|
+
const filePrompt = `Leia o arquivo abaixo que contém suas instruções completas, regras, dados do workspace e a consulta do usuário. Siga TODAS as instruções contidas nele.\nARQUIVO: ${oracleTmpFile}\n\nIMPORTANTE: Leia o arquivo INTEIRO e responda à consulta do usuário que está no final do arquivo.`;
|
|
4330
4474
|
copilotArgs.push('--add-dir', os.tmpdir());
|
|
4331
4475
|
copilotArgs.push('--allow-all-tools', '-p', filePrompt);
|
|
4332
4476
|
r = await run(cmd, copilotArgs, workspaceDir, oracleEnv);
|
package/package.json
CHANGED