@cccarv82/freya 3.5.0 → 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.
|
@@ -149,6 +149,18 @@ Combine all results into a unified, natural response:
|
|
|
149
149
|
- Always cite sources at the end
|
|
150
150
|
- Always provide next steps
|
|
151
151
|
|
|
152
|
+
<critical-rule>
|
|
153
|
+
**NUNCA EXPONHA A ARQUITETURA INTERNA AO USUÁRIO:**
|
|
154
|
+
- NUNCA mencione nomes de agentes (Oracle, SM Agent, Ingestor, Analytics Agent, Coach)
|
|
155
|
+
- NUNCA explique o fluxo de roteamento ou hierarquia de agentes
|
|
156
|
+
- NUNCA peça ao usuário para "chamar outro agente" ou "invocar o Orchestrator"
|
|
157
|
+
- NUNCA mostre JSONs brutos como resposta final
|
|
158
|
+
- NUNCA diga "como Oracle, não posso..." ou "minha função é apenas..."
|
|
159
|
+
- Você é FREYA para o usuário. Um sistema único e coeso. A orquestração interna é invisível.
|
|
160
|
+
- Se um sub-agente retorna dados brutos, VOCÊ sintetiza em linguagem natural antes de responder.
|
|
161
|
+
- Se um sub-agente se recusa a sintetizar, VOCÊ faz a síntese — é sua responsabilidade final.
|
|
162
|
+
</critical-rule>
|
|
163
|
+
|
|
152
164
|
## Phase 5: Session Memory
|
|
153
165
|
|
|
154
166
|
Maintain awareness of what was discussed in this session:
|
|
@@ -120,3 +120,4 @@ When returning data, format consistently:
|
|
|
120
120
|
- **Always include sources**: Every piece of data must be traceable to a file or table.
|
|
121
121
|
- **Dates in dd/mm/aaaa**: When presenting dates to humans, use Brazilian format.
|
|
122
122
|
- **No synthesis**: You retrieve and format. You do NOT analyze or recommend. That's the Super Agent's job.
|
|
123
|
+
- **NEVER talk to the user directly**: You are a utility agent. When running inside the Orchestrator context, your data is consumed internally. The Orchestrator handles all user-facing communication. NEVER explain your role, hierarchy, or suggest the user "call another agent".
|
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
|
|
|
@@ -4265,7 +4388,11 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
4265
4388
|
const files = [
|
|
4266
4389
|
path.join(rulesBase, 'freya.mdc'),
|
|
4267
4390
|
path.join(rulesBase, 'agents', 'master.mdc'),
|
|
4268
|
-
path.join(rulesBase, 'agents', '
|
|
4391
|
+
path.join(rulesBase, 'agents', 'sm-agent.mdc'),
|
|
4392
|
+
path.join(rulesBase, 'agents', 'analytics-agent.mdc'),
|
|
4393
|
+
path.join(rulesBase, 'agents', 'oracle.mdc'),
|
|
4394
|
+
path.join(rulesBase, 'agents', 'coach.mdc'),
|
|
4395
|
+
path.join(rulesBase, 'agents', 'ingestor.mdc')
|
|
4269
4396
|
].filter(exists);
|
|
4270
4397
|
|
|
4271
4398
|
const rulesText = files.map((p) => {
|
|
@@ -4273,6 +4400,12 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
4273
4400
|
return `\n\n---\nFILE: ${rel}\n---\n` + fs.readFileSync(p, 'utf8');
|
|
4274
4401
|
}).join('');
|
|
4275
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
|
+
|
|
4276
4409
|
// V2 RAG Context (graceful fallback if embedder/sharp not available)
|
|
4277
4410
|
const dm = new DataManager(workspaceDir, path.join(workspaceDir, 'logs'));
|
|
4278
4411
|
let ragContext = '';
|
|
@@ -4297,8 +4430,23 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
4297
4430
|
}
|
|
4298
4431
|
}
|
|
4299
4432
|
|
|
4300
|
-
// System instructions
|
|
4301
|
-
|
|
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}`;
|
|
4302
4450
|
|
|
4303
4451
|
const cmd = process.env.COPILOT_CMD || 'copilot';
|
|
4304
4452
|
|
|
@@ -4314,15 +4462,15 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
4314
4462
|
}
|
|
4315
4463
|
}
|
|
4316
4464
|
|
|
4317
|
-
// ENAMETOOLONG fix: when prompt is large, write
|
|
4465
|
+
// ENAMETOOLONG fix: when prompt is large, write full prompt to temp file
|
|
4318
4466
|
const fullOraclePrompt = `${oracleSysInstr}\n\nREGRAS:${rulesText}\n\nCONSULTA DO USUÁRIO:\n${query}\n`;
|
|
4319
4467
|
const SAFE_ARG_LEN = 24000;
|
|
4320
4468
|
let oracleTmpFile = null;
|
|
4321
4469
|
let r;
|
|
4322
4470
|
if (fullOraclePrompt.length > SAFE_ARG_LEN) {
|
|
4323
|
-
oracleTmpFile = path.join(os.tmpdir(), `freya-
|
|
4324
|
-
fs.writeFileSync(oracleTmpFile,
|
|
4325
|
-
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.`;
|
|
4326
4474
|
copilotArgs.push('--add-dir', os.tmpdir());
|
|
4327
4475
|
copilotArgs.push('--allow-all-tools', '-p', filePrompt);
|
|
4328
4476
|
r = await run(cmd, copilotArgs, workspaceDir, oracleEnv);
|
|
@@ -4333,7 +4481,7 @@ async function cmdWeb({ port, dir, open, dev }) {
|
|
|
4333
4481
|
if (oracleTmpFile) { try { fs.unlinkSync(oracleTmpFile); } catch (_) { /* ignore */ } }
|
|
4334
4482
|
const out = (r.stdout + r.stderr).trim();
|
|
4335
4483
|
if (r.code !== 0) {
|
|
4336
|
-
return safeJson(res, 200, { ok: false, answer: 'Falha
|
|
4484
|
+
return safeJson(res, 200, { ok: false, answer: 'Falha no processamento do agente FREYA:\n' + (out || 'Exit code != 0'), sessionId });
|
|
4337
4485
|
}
|
|
4338
4486
|
return safeJson(res, 200, { ok: true, answer: out, sessionId });
|
|
4339
4487
|
} catch (e) {
|
package/package.json
CHANGED