@ericnunes/frame-code-cli 0.0.1
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/LICENSE +139 -0
- package/README.md +196 -0
- package/dist/agent-runtime/AgentFacade.js +33 -0
- package/dist/agent-runtime/context/hooks/compressionHook.js +56 -0
- package/dist/agent-runtime/context/hooks/index.js +5 -0
- package/dist/agent-runtime/context/project-rules/loader.js +72 -0
- package/dist/agent-runtime/context/system-prompts/index.js +5 -0
- package/dist/agent-runtime/context/system-prompts/loader.js +88 -0
- package/dist/agent-runtime/flows/templates/ReactAgentFlow.js +49 -0
- package/dist/agent-runtime/index.js +18 -0
- package/dist/agent-runtime/registry/AgentRegistry.js +93 -0
- package/dist/agent-runtime/registry/agentParser.js +515 -0
- package/dist/agent-runtime/registry/enums/agentType.enum.js +8 -0
- package/dist/agent-runtime/registry/index.js +20 -0
- package/dist/agent-runtime/registry/initialization.js +53 -0
- package/dist/agent-runtime/registry/interfaces/agentDependencies.interface.js +2 -0
- package/dist/agent-runtime/registry/interfaces/agentMetadata.interface.js +2 -0
- package/dist/agent-runtime/registry/interfaces/agentRegistry.interface.js +2 -0
- package/dist/app/bootstrap.js +22 -0
- package/dist/app/cli.js +31 -0
- package/dist/app/index.js +9 -0
- package/dist/cli/commands/autonomous.js +181 -0
- package/dist/cli/commands/index.js +11 -0
- package/dist/cli/commands/interactive.js +172 -0
- package/dist/cli/commands/memory.js +149 -0
- package/dist/cli/commands/multi-agent.js +131 -0
- package/dist/cli/index.js +18 -0
- package/dist/cli/input/images/attachments.js +173 -0
- package/dist/cli/input/images/imageInput.js +77 -0
- package/dist/cli/input/images/readImageAttachment.js +56 -0
- package/dist/cli/input/index.js +14 -0
- package/dist/cli/input/reader.js +26 -0
- package/dist/content/agents/README.md +324 -0
- package/dist/content/agents/architect.md +95 -0
- package/dist/content/agents/builder.md +85 -0
- package/dist/content/agents/code-agent.md +123 -0
- package/dist/content/agents/supervisor.md +63 -0
- package/dist/index.js +25 -0
- package/dist/infrastructure/compression/CompressionManager.js +315 -0
- package/dist/infrastructure/compression/LLMCompressionService.js +211 -0
- package/dist/infrastructure/compression/index.js +11 -0
- package/dist/infrastructure/compression/promptBuilder.js +132 -0
- package/dist/infrastructure/config/agentConfig.interface.js +2 -0
- package/dist/infrastructure/config/agentConfig.js +134 -0
- package/dist/infrastructure/config/config.interface.js +2 -0
- package/dist/infrastructure/config/config.js +112 -0
- package/dist/infrastructure/config/index.js +6 -0
- package/dist/infrastructure/logging/index.js +5 -0
- package/dist/infrastructure/logging/logger.interface.js +2 -0
- package/dist/infrastructure/logging/logger.js +33 -0
- package/dist/infrastructure/logging/raw-output-logger.js +35 -0
- package/dist/infrastructure/skills/index.js +5 -0
- package/dist/infrastructure/skills/loader.js +104 -0
- package/dist/infrastructure/telemetry/index.js +9 -0
- package/dist/infrastructure/telemetry/telemetry.interface.js +2 -0
- package/dist/infrastructure/telemetry/telemetryConfig.js +30 -0
- package/dist/infrastructure/telemetry/traceEventFormatter.js +90 -0
- package/dist/infrastructure/telemetry/traceSinkConsole.js +17 -0
- package/dist/scripts/_validate/telemetry-autonomous.js +23 -0
- package/dist/scripts/_validate/telemetry-multi-agent.js +50 -0
- package/dist/scripts/_validate/test-agents-md-dynamic-dir.js +104 -0
- package/dist/scripts/_validate/test-agents-md-injection.js +125 -0
- package/dist/scripts/_validate/test-agents-md-loader.js +71 -0
- package/dist/scripts/_validate/test-agents-md-priority.js +121 -0
- package/dist/scripts/_validate/test-chrome-mcp-agent.js +89 -0
- package/dist/tools/index.js +19 -0
- package/dist/tools/mcp/discoverer.js +95 -0
- package/dist/tools/mcp/index.js +9 -0
- package/dist/tools/mcp/loader.js +36 -0
- package/dist/tools/mcp/mcpConfig.interface.js +2 -0
- package/dist/tools/mcp/mcpMetadata.js +2 -0
- package/dist/tools/mcp/register.js +269 -0
- package/dist/tools/native/capabilities.js +155 -0
- package/dist/tools/native/file-outline.js +301 -0
- package/dist/tools/native/index.js +20 -0
- package/dist/tools/native/list-directory.js +148 -0
- package/dist/tools/native/read-image.js +140 -0
- package/dist/tools/registry/ToolInitializer.js +62 -0
- package/dist/tools/registry/index.js +11 -0
- package/dist/tools/registry/toolFilter.js +52 -0
- package/dist/tools/registry/toolRegistry.interface.js +2 -0
- package/package.json +81 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: supervisor
|
|
3
|
+
type: main-agent
|
|
4
|
+
canBeSupervisor: true
|
|
5
|
+
description: Tech Lead que coordena o ciclo de vida do desenvolvimento (Design -> Implementação)
|
|
6
|
+
keywords: [supervisor, orchestrate, architect, builder, multi-agent]
|
|
7
|
+
tools: [call_flow, final_answer, ask_user]
|
|
8
|
+
temperature: 0.3
|
|
9
|
+
maxTokens: 4096
|
|
10
|
+
compressionEnabled: true
|
|
11
|
+
backstory: |
|
|
12
|
+
Você é a Agente Supervisora (Mariana), uma Líder Técnica experiente. Sua função não é executar o trabalho manual, mas garantir que os especialistas certos sejam acionados na ordem correta. Você gerencia o fluxo entre o Arquiteto (que desenha a solução) e o Construtor (que implementa o código).
|
|
13
|
+
additionalInstructions: |
|
|
14
|
+
## Objetivo
|
|
15
|
+
Gerenciar o fluxo de desenvolvimento invocando o `architect` para criar especificações técnicas e, em seguida, o `builder` para implementar essas especificações.
|
|
16
|
+
|
|
17
|
+
## Regras de Orquestração
|
|
18
|
+
1. **Hierarquia de Fluxo**:
|
|
19
|
+
- Passo 1: Use `call_flow` com `flowId: "architect"` para obter a especificação técnica/plano.
|
|
20
|
+
- Passo 2: Use `call_flow` com `flowId: "builder"` para executar, passando o resultado do Arquiteto.
|
|
21
|
+
- Passo 3: Use `final_answer` para entregar o produto final ao usuário.
|
|
22
|
+
|
|
23
|
+
2. **Passagem de Contexto (Crítico)**:
|
|
24
|
+
- Ao chamar o **architect**, passe a tarefa do usuário dentro de `input`.
|
|
25
|
+
- Ao chamar o **builder**, você OBRIGATORIAMENTE deve passar a saída do Arquiteto dentro do objeto `shared` com a chave `plan`.
|
|
26
|
+
- Exemplo: `shared: { "plan": "Texto retornado pelo architect..." }`
|
|
27
|
+
|
|
28
|
+
3. **Não Modifique a Intenção**:
|
|
29
|
+
- Repasse a solicitação do usuário integralmente para o Arquiteto.
|
|
30
|
+
- Repasse o plano do Arquiteto integralmente para o Builder.
|
|
31
|
+
|
|
32
|
+
4. **Tratamento de Erros**:
|
|
33
|
+
- Se o `architect` falhar ou retornar algo vago, não chame o `builder`. Use `final_answer` explicando o problema ou `ask_user` para pedir detalhes.
|
|
34
|
+
|
|
35
|
+
## Formato de Saída (Strict JSON)
|
|
36
|
+
Thought: Preciso obter um plano técnico para esta tarefa.
|
|
37
|
+
Action: call_flow = {"flowId":"architect","input":{"task":"tarefa do usuário"}}
|
|
38
|
+
|
|
39
|
+
Após receber o plano:
|
|
40
|
+
|
|
41
|
+
Thought: Plano recebido, vou delegar para execução.
|
|
42
|
+
Action: call_flow = {"flowId":"builder","input":{"task":"tarefa do usuário"},"shared":{"plan":"plano recebido do architect"}}
|
|
43
|
+
|
|
44
|
+
**Examples of Valid Turns:**
|
|
45
|
+
|
|
46
|
+
### Cenário: Início do Projeto
|
|
47
|
+
Thought: O usuário quer criar um sistema de login. Primeiro preciso da arquitetura.
|
|
48
|
+
Action: call_flow = { "flowId": "architect", "input": { "task": "Criar sistema de login com JWT" } }
|
|
49
|
+
|
|
50
|
+
### Cenário: Passando do Arquiteto para o Construtor
|
|
51
|
+
Observation: [O architect retornou uma especificação técnica detalhada em Markdown]
|
|
52
|
+
Thought: Recebi a especificação do Arquiteto. Agora vou delegar a implementação para o Builder, passando o plano no contexto compartilhado.
|
|
53
|
+
Action: call_flow = { "flowId": "builder", "input": { "task": "Implementar conforme especificação" }, "shared": { "plan": "## Especificação Técnica (Conteúdo recebido do architect)..." } }
|
|
54
|
+
|
|
55
|
+
### Cenário: Finalização
|
|
56
|
+
Observation: [O builder retornou "Sistema implementado e testado com sucesso"]
|
|
57
|
+
Thought: O Builder concluiu a tarefa. Vou informar o usuário.
|
|
58
|
+
Action: final_answer = { "answer": "O sistema de login foi planejado e implementado com sucesso. Os arquivos foram criados em src/auth e os testes passaram." }
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
# Agente Supervisor (Mariana)
|
|
62
|
+
|
|
63
|
+
As instruções deste agente estão definidas no frontmatter deste arquivo.
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
if (process.stdout.isTTY) {
|
|
5
|
+
process.stdout.setEncoding('utf-8');
|
|
6
|
+
}
|
|
7
|
+
if (process.stderr.isTTY) {
|
|
8
|
+
process.stderr.setEncoding('utf-8');
|
|
9
|
+
}
|
|
10
|
+
if (!process.env.LANG) {
|
|
11
|
+
process.env.LANG = 'en_US.UTF-8';
|
|
12
|
+
}
|
|
13
|
+
if (!process.env.LC_ALL) {
|
|
14
|
+
process.env.LC_ALL = 'en_US.UTF-8';
|
|
15
|
+
}
|
|
16
|
+
const logger_1 = require("./infrastructure/logging/logger");
|
|
17
|
+
logger_1.logger.info('[FrameCodeCLI] Iniciando CLI...');
|
|
18
|
+
const agent_runtime_1 = require("./agent-runtime");
|
|
19
|
+
(0, agent_runtime_1.initializeAgents)().then((count) => {
|
|
20
|
+
logger_1.logger.info(`[FrameCodeCLI] ${count} agentes carregados`);
|
|
21
|
+
}).catch((error) => {
|
|
22
|
+
logger_1.logger.error('[FrameCodeCLI] Erro ao carregar agentes:', error);
|
|
23
|
+
});
|
|
24
|
+
const cli_1 = require("./app/cli");
|
|
25
|
+
(0, cli_1.runCli)();
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CompressionManager = void 0;
|
|
4
|
+
const frame_agent_sdk_1 = require("@ericnunes/frame-agent-sdk");
|
|
5
|
+
const frame_agent_sdk_2 = require("@ericnunes/frame-agent-sdk");
|
|
6
|
+
const LLMCompressionService_1 = require("./LLMCompressionService");
|
|
7
|
+
const logger_1 = require("../logging/logger");
|
|
8
|
+
const config_1 = require("../config");
|
|
9
|
+
class CompressionManager {
|
|
10
|
+
constructor(config) {
|
|
11
|
+
this.compressoes = [];
|
|
12
|
+
this.compressionCount = 0;
|
|
13
|
+
const defaultConfig = (0, config_1.loadConfigSync)();
|
|
14
|
+
this.compressionConfig = {
|
|
15
|
+
enabled: true,
|
|
16
|
+
threshold: 0.8,
|
|
17
|
+
maxCount: 5,
|
|
18
|
+
maxTokens: 300,
|
|
19
|
+
logging: true,
|
|
20
|
+
persist: true,
|
|
21
|
+
...config
|
|
22
|
+
};
|
|
23
|
+
this.maxCompressoes = this.compressionConfig.maxCount;
|
|
24
|
+
this.persistKey = sanitizePersistKey(this.compressionConfig.persistKey ?? 'default');
|
|
25
|
+
this.llmService = new LLMCompressionService_1.LLMCompressionService();
|
|
26
|
+
if (!this.llmService.isConfigured()) {
|
|
27
|
+
logger_1.logger.warn('[CompressionManager] LLM não configurado corretamente');
|
|
28
|
+
logger_1.logger.debug('[CompressionManager] Config info:', this.llmService.getConfigInfo());
|
|
29
|
+
}
|
|
30
|
+
this.initializeTokenization(defaultConfig.defaults?.maxContextTokens || 240000);
|
|
31
|
+
if (this.compressionConfig.persist) {
|
|
32
|
+
this.loadPersistedCompressions();
|
|
33
|
+
}
|
|
34
|
+
logger_1.logger.info('[CompressionManager] Inicializado', {
|
|
35
|
+
enabled: this.compressionConfig.enabled,
|
|
36
|
+
maxCompressoes: this.maxCompressoes,
|
|
37
|
+
threshold: this.compressionConfig.threshold,
|
|
38
|
+
currentCompressions: this.compressoes.length,
|
|
39
|
+
persistKey: this.persistKey
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
initializeTokenization(maxContextTokens) {
|
|
43
|
+
try {
|
|
44
|
+
this.tokenizerService = new frame_agent_sdk_1.TokenizerService('gpt-4');
|
|
45
|
+
this.chatHistoryManager = new frame_agent_sdk_1.ChatHistoryManager({
|
|
46
|
+
maxContextTokens,
|
|
47
|
+
tokenizer: this.tokenizerService
|
|
48
|
+
});
|
|
49
|
+
logger_1.logger.debug('[CompressionManager] Serviços de tokenização inicializados');
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
logger_1.logger.error('[CompressionManager] Erro ao inicializar tokenização:', error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async handleTokenOverflow(error, state) {
|
|
56
|
+
if (!this.compressionConfig.enabled) {
|
|
57
|
+
throw error;
|
|
58
|
+
}
|
|
59
|
+
logger_1.logger.error('[CompressionManager] Estouro de tokens detectado, iniciando compressão emergencial');
|
|
60
|
+
logger_1.logger.debug('[CompressionManager] Erro original:', error.message);
|
|
61
|
+
try {
|
|
62
|
+
const messages = this.extractMessagesFromState(state);
|
|
63
|
+
await this.performEmergencyCompression(messages);
|
|
64
|
+
const compressedState = await this.buildCompressedState(state);
|
|
65
|
+
logger_1.logger.info('[CompressionManager] Compressão emergencial concluída, retentando execução');
|
|
66
|
+
return compressedState;
|
|
67
|
+
}
|
|
68
|
+
catch (compressionError) {
|
|
69
|
+
logger_1.logger.error('[CompressionManager] Falha na compressão emergencial:', compressionError);
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async checkProactiveCompression(state) {
|
|
74
|
+
if (!this.compressionConfig.enabled || !this.chatHistoryManager) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
const messages = this.extractMessagesFromState(state);
|
|
79
|
+
const currentTokens = this.tokenizerService.countTokens(messages);
|
|
80
|
+
const maxTokens = 240000;
|
|
81
|
+
const usageRatio = currentTokens / maxTokens;
|
|
82
|
+
const shouldCompress = usageRatio >= this.compressionConfig.threshold;
|
|
83
|
+
if (this.compressionConfig.logging) {
|
|
84
|
+
logger_1.logger.debug(`[CompressionManager] Uso de tokens: ${currentTokens}/${maxTokens} (${(usageRatio * 100).toFixed(1)}%)`);
|
|
85
|
+
if (shouldCompress) {
|
|
86
|
+
logger_1.logger.info(`[CompressionManager] Threshold atingido (${(this.compressionConfig.threshold * 100)}%), compressão proativa recomendada`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return shouldCompress;
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
logger_1.logger.error('[CompressionManager] Erro na verificação proativa:', error);
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async performProactiveCompression(state) {
|
|
97
|
+
logger_1.logger.info('[CompressionManager] Iniciando compressão proativa');
|
|
98
|
+
try {
|
|
99
|
+
const messages = this.extractMessagesFromState(state);
|
|
100
|
+
await this.performEmergencyCompression(messages);
|
|
101
|
+
return await this.buildCompressedState(state);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
logger_1.logger.error('[CompressionManager] Erro na compressão proativa:', error);
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async performEmergencyCompression(messages) {
|
|
109
|
+
if (!this.llmService.isConfigured()) {
|
|
110
|
+
throw new Error('LLM não configurado para compressão');
|
|
111
|
+
}
|
|
112
|
+
const protectedMessages = this.extractProtectedMessages(messages);
|
|
113
|
+
const contextToCompress = this.extractCompressibleContext(messages);
|
|
114
|
+
if (contextToCompress.trim().length === 0) {
|
|
115
|
+
logger_1.logger.warn('[CompressionManager] Nenhum contexto para comprimir');
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
let newCompression;
|
|
120
|
+
if (this.compressionCount === 0) {
|
|
121
|
+
newCompression = await this.llmService.compressInitial(contextToCompress);
|
|
122
|
+
this.compressionCount = 1;
|
|
123
|
+
this.compressoes.push(newCompression);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
newCompression = await this.llmService.compressIncremental(this.compressoes, contextToCompress);
|
|
127
|
+
if (this.compressoes.length >= this.maxCompressoes) {
|
|
128
|
+
await this.mergeOldestCompressions();
|
|
129
|
+
}
|
|
130
|
+
this.compressionCount++;
|
|
131
|
+
this.compressoes.push(newCompression);
|
|
132
|
+
}
|
|
133
|
+
if (this.compressionConfig.persist) {
|
|
134
|
+
this.persistCompressions();
|
|
135
|
+
}
|
|
136
|
+
logger_1.logger.info(`[CompressionManager] Compressão #${this.compressionCount} realizada com sucesso`);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
logger_1.logger.error('[CompressionManager] Erro durante compressão emergencial:', error);
|
|
140
|
+
throw error;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async mergeOldestCompressions() {
|
|
144
|
+
if (this.compressoes.length < 2) {
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
logger_1.logger.info('[CompressionManager] Mesclando compressões mais antigas');
|
|
148
|
+
let oldest1;
|
|
149
|
+
let oldest2;
|
|
150
|
+
try {
|
|
151
|
+
oldest1 = this.compressoes.shift();
|
|
152
|
+
oldest2 = this.compressoes.shift();
|
|
153
|
+
if (!oldest1 || !oldest2) {
|
|
154
|
+
throw new Error('Não há compressões suficientes para mesclar');
|
|
155
|
+
}
|
|
156
|
+
const merged = await this.llmService.mergeCompressions(oldest1, oldest2);
|
|
157
|
+
this.compressoes.unshift(merged);
|
|
158
|
+
logger_1.logger.info('[CompressionManager] Compressões mescladas com sucesso');
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
logger_1.logger.error('[CompressionManager] Erro ao mesclar compressões:', error);
|
|
162
|
+
if (oldest1 && oldest2) {
|
|
163
|
+
this.compressoes.unshift(oldest2, oldest1);
|
|
164
|
+
}
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
extractProtectedMessages(messages) {
|
|
169
|
+
const protectedMessages = [];
|
|
170
|
+
const systemMessage = messages.find(msg => msg.role === 'system');
|
|
171
|
+
if (systemMessage) {
|
|
172
|
+
protectedMessages.push(systemMessage);
|
|
173
|
+
}
|
|
174
|
+
const userMessages = messages.filter(msg => msg.role === 'user');
|
|
175
|
+
if (userMessages.length > 0) {
|
|
176
|
+
protectedMessages.push(userMessages[0]);
|
|
177
|
+
}
|
|
178
|
+
if (userMessages.length > 1) {
|
|
179
|
+
protectedMessages.push(userMessages[userMessages.length - 1]);
|
|
180
|
+
}
|
|
181
|
+
return protectedMessages;
|
|
182
|
+
}
|
|
183
|
+
extractCompressibleContext(messages) {
|
|
184
|
+
const protectedIndices = new Set();
|
|
185
|
+
messages.forEach((msg, index) => {
|
|
186
|
+
if (msg.role === 'system') {
|
|
187
|
+
protectedIndices.add(index);
|
|
188
|
+
}
|
|
189
|
+
else if (msg.role === 'user') {
|
|
190
|
+
const userMessages = messages.filter(m => m.role === 'user');
|
|
191
|
+
if (index === messages.indexOf(userMessages[0]) || index === messages.lastIndexOf(userMessages[userMessages.length - 1])) {
|
|
192
|
+
protectedIndices.add(index);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
const compressibleMessages = messages.filter((_, index) => !protectedIndices.has(index));
|
|
197
|
+
return compressibleMessages
|
|
198
|
+
.map(msg => `[${msg.role.toUpperCase()}]: ${(0, frame_agent_sdk_2.extractTextFromMessage)(msg)}`)
|
|
199
|
+
.join('\n\n');
|
|
200
|
+
}
|
|
201
|
+
extractMessagesFromState(state) {
|
|
202
|
+
return state.messages || [];
|
|
203
|
+
}
|
|
204
|
+
async buildCompressedState(originalState) {
|
|
205
|
+
const originalMessages = this.extractMessagesFromState(originalState);
|
|
206
|
+
const protectedMessages = this.extractProtectedMessages(originalMessages);
|
|
207
|
+
const newMessages = [...protectedMessages];
|
|
208
|
+
if (this.compressoes.length > 0) {
|
|
209
|
+
const compressionContext = this.getCompressionPrompt();
|
|
210
|
+
newMessages.unshift({
|
|
211
|
+
role: 'system',
|
|
212
|
+
content: compressionContext
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
return {
|
|
216
|
+
...originalState,
|
|
217
|
+
messages: newMessages
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
getCompressionPrompt() {
|
|
221
|
+
if (this.compressoes.length === 0) {
|
|
222
|
+
return '';
|
|
223
|
+
}
|
|
224
|
+
return `CONTEXTO ACUMULADO DA SESSÃO:
|
|
225
|
+
${this.compressoes.join('\n')}`;
|
|
226
|
+
}
|
|
227
|
+
getCompressionHistory() {
|
|
228
|
+
return [...this.compressoes];
|
|
229
|
+
}
|
|
230
|
+
getCompressionStats() {
|
|
231
|
+
return {
|
|
232
|
+
compressionCount: this.compressionCount,
|
|
233
|
+
currentCompressions: this.compressoes.length,
|
|
234
|
+
maxCompressions: this.maxCompressoes,
|
|
235
|
+
enabled: this.compressionConfig.enabled,
|
|
236
|
+
threshold: this.compressionConfig.threshold,
|
|
237
|
+
persistKey: this.persistKey,
|
|
238
|
+
compressionHistory: this.compressoes.map((comp, index) => ({
|
|
239
|
+
index: index + 1,
|
|
240
|
+
preview: comp.substring(0, 100) + (comp.length > 100 ? '...' : ''),
|
|
241
|
+
length: comp.length
|
|
242
|
+
}))
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
clearCompressions() {
|
|
246
|
+
this.compressoes = [];
|
|
247
|
+
this.compressionCount = 0;
|
|
248
|
+
if (this.compressionConfig.persist) {
|
|
249
|
+
this.clearPersistedCompressions();
|
|
250
|
+
}
|
|
251
|
+
logger_1.logger.info('[CompressionManager] Compressões limpas');
|
|
252
|
+
}
|
|
253
|
+
persistCompressions() {
|
|
254
|
+
try {
|
|
255
|
+
const fs = require('fs');
|
|
256
|
+
const path = require('path');
|
|
257
|
+
const persistPath = path.join(process.cwd(), `.frame-code-compressions.${this.persistKey}.json`);
|
|
258
|
+
const data = {
|
|
259
|
+
compressoes: this.compressoes,
|
|
260
|
+
compressionCount: this.compressionCount,
|
|
261
|
+
persistKey: this.persistKey,
|
|
262
|
+
timestamp: new Date().toISOString()
|
|
263
|
+
};
|
|
264
|
+
fs.writeFileSync(persistPath, JSON.stringify(data, null, 2));
|
|
265
|
+
logger_1.logger.debug('[CompressionManager] Compressões persistidas');
|
|
266
|
+
}
|
|
267
|
+
catch (error) {
|
|
268
|
+
logger_1.logger.error('[CompressionManager] Erro ao persistir compressões:', error);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
loadPersistedCompressions() {
|
|
272
|
+
try {
|
|
273
|
+
const fs = require('fs');
|
|
274
|
+
const path = require('path');
|
|
275
|
+
const persistPath = path.join(process.cwd(), `.frame-code-compressions.${this.persistKey}.json`);
|
|
276
|
+
if (fs.existsSync(persistPath)) {
|
|
277
|
+
const data = JSON.parse(fs.readFileSync(persistPath, 'utf-8'));
|
|
278
|
+
this.compressoes = data.compressoes || [];
|
|
279
|
+
this.compressionCount = data.compressionCount || 0;
|
|
280
|
+
logger_1.logger.info(`[CompressionManager] Carregadas ${this.compressoes.length} compressões persistidas`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
logger_1.logger.error('[CompressionManager] Erro ao carregar compressões persistidas:', error);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
clearPersistedCompressions() {
|
|
288
|
+
try {
|
|
289
|
+
const fs = require('fs');
|
|
290
|
+
const path = require('path');
|
|
291
|
+
const persistPath = path.join(process.cwd(), `.frame-code-compressions.${this.persistKey}.json`);
|
|
292
|
+
if (fs.existsSync(persistPath)) {
|
|
293
|
+
fs.unlinkSync(persistPath);
|
|
294
|
+
logger_1.logger.debug('[CompressionManager] Arquivo de persistência removido');
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
catch (error) {
|
|
298
|
+
logger_1.logger.error('[CompressionManager] Erro ao limpar persistência:', error);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
async forceCompression(state) {
|
|
302
|
+
logger_1.logger.info('[CompressionManager] Forçando compressão manual');
|
|
303
|
+
if (!this.compressionConfig.enabled) {
|
|
304
|
+
throw new Error('Compressão está desabilitada');
|
|
305
|
+
}
|
|
306
|
+
return await this.performProactiveCompression(state);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
exports.CompressionManager = CompressionManager;
|
|
310
|
+
function sanitizePersistKey(value) {
|
|
311
|
+
const trimmed = value.trim();
|
|
312
|
+
if (!trimmed)
|
|
313
|
+
return 'default';
|
|
314
|
+
return trimmed.replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 80);
|
|
315
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.LLMCompressionService = void 0;
|
|
37
|
+
const logger_1 = require("../logging/logger");
|
|
38
|
+
const config_1 = require("../config");
|
|
39
|
+
class LLMCompressionService {
|
|
40
|
+
constructor(llmConfig) {
|
|
41
|
+
if (llmConfig) {
|
|
42
|
+
this.llmConfig = llmConfig;
|
|
43
|
+
this.modelName = llmConfig.model;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
const config = (0, config_1.loadConfigSync)();
|
|
47
|
+
this.llmConfig = {
|
|
48
|
+
model: config.defaults?.model || 'gpt-4o-mini',
|
|
49
|
+
provider: config.provider,
|
|
50
|
+
apiKey: config.apiKey,
|
|
51
|
+
baseUrl: config.baseURL,
|
|
52
|
+
defaults: {
|
|
53
|
+
maxTokens: config.defaults?.maxTokens,
|
|
54
|
+
temperature: config.defaults?.temperature,
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
this.modelName = this.llmConfig.model;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async compressInitial(context) {
|
|
61
|
+
const prompt = this.buildInitialCompressionPrompt(context);
|
|
62
|
+
logger_1.logger.info('[LLMCompressionService] Iniciando compressão inicial');
|
|
63
|
+
logger_1.logger.debug(`[LLMCompressionService] Contexto tem ${context.length} caracteres`);
|
|
64
|
+
try {
|
|
65
|
+
const result = await this.callLLM(prompt);
|
|
66
|
+
logger_1.logger.info('[LLMCompressionService] Compressão inicial concluída');
|
|
67
|
+
return result;
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
logger_1.logger.error('[LLMCompressionService] Erro na compressão inicial:', error);
|
|
71
|
+
throw new Error(`Falha na compressão inicial: ${error instanceof Error ? error.message : 'Erro desconhecido'}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
async compressIncremental(compressoes, newContext) {
|
|
75
|
+
const compressionCount = compressoes.length + 1;
|
|
76
|
+
const prompt = this.buildIncrementalCompressionPrompt(compressoes, newContext, compressionCount);
|
|
77
|
+
logger_1.logger.info(`[LLMCompressionService] Iniciando compressão incremental #${compressionCount}`);
|
|
78
|
+
logger_1.logger.debug(`[LLMCompressionService] Baseado em ${compressoes.length} compressões anteriores`);
|
|
79
|
+
try {
|
|
80
|
+
const result = await this.callLLM(prompt);
|
|
81
|
+
logger_1.logger.info(`[LLMCompressionService] Compressão incremental #${compressionCount} concluída`);
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
logger_1.logger.error(`[LLMCompressionService] Erro na compressão incremental #${compressionCount}:`, error);
|
|
86
|
+
throw new Error(`Falha na compressão incremental: ${error instanceof Error ? error.message : 'Erro desconhecido'}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
async mergeCompressions(comp1, comp2) {
|
|
90
|
+
const prompt = this.buildMergeCompressionPrompt(comp1, comp2);
|
|
91
|
+
logger_1.logger.info('[LLMCompressionService] Iniciando mesclagem de compressões');
|
|
92
|
+
try {
|
|
93
|
+
const result = await this.callLLM(prompt);
|
|
94
|
+
logger_1.logger.info('[LLMCompressionService] Mesclagem de compressões concluída');
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
logger_1.logger.error('[LLMCompressionService] Erro na mesclagem de compressões:', error);
|
|
99
|
+
throw new Error(`Falha na mesclagem de compressões: ${error instanceof Error ? error.message : 'Erro desconhecido'}`);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
buildInitialCompressionPrompt(context) {
|
|
103
|
+
return `Você é um especialista em sumarizar conversas de desenvolvimento.
|
|
104
|
+
|
|
105
|
+
Comprima este contexto completo em uma sumarização concisa:
|
|
106
|
+
${context}
|
|
107
|
+
|
|
108
|
+
Formato obrigatório: "COMPRESSÃO 1: [sua sumarização]"
|
|
109
|
+
|
|
110
|
+
Preserve:
|
|
111
|
+
- Objetivos principais do projeto/sessão
|
|
112
|
+
- Contexto técnico essencial
|
|
113
|
+
- Primeiras decisões ou requisitos
|
|
114
|
+
- Arquivos ou tecnologias mencionadas
|
|
115
|
+
|
|
116
|
+
Priorize:
|
|
117
|
+
- Informações técnicas importantes sobre código, arquivos e decisões
|
|
118
|
+
- Requisitos e objetivos definidos
|
|
119
|
+
- Problemas e soluções identificados
|
|
120
|
+
|
|
121
|
+
Responda APENAS com a compressão no formato especificado, sem comentários adicionais.`;
|
|
122
|
+
}
|
|
123
|
+
buildIncrementalCompressionPrompt(compressoes, newContext, compressionNumber) {
|
|
124
|
+
const compressoesText = compressoes
|
|
125
|
+
.map((comp, index) => `COMPRESSÃO ${index + 1}: ${comp}`)
|
|
126
|
+
.join('\n');
|
|
127
|
+
return `Você está gerenciando o contexto acumulado de uma longa conversa de desenvolvimento.
|
|
128
|
+
|
|
129
|
+
Compressões anteriores acumuladas:
|
|
130
|
+
${compressoesText}
|
|
131
|
+
|
|
132
|
+
Novo contexto recente:
|
|
133
|
+
${newContext}
|
|
134
|
+
|
|
135
|
+
Crie uma nova compressão que INTEGRE TODAS as informações anteriores com o novo contexto.
|
|
136
|
+
|
|
137
|
+
Formato obrigatório: "COMPRESSÃO ${compressionNumber}: [nova sumarização integrada]"
|
|
138
|
+
|
|
139
|
+
Mantenha:
|
|
140
|
+
- Progresso acumulado do projeto
|
|
141
|
+
- Decisões importantes de todas as etapas
|
|
142
|
+
- Problemas resolvidos e pendências
|
|
143
|
+
- Continuidade do contexto técnico
|
|
144
|
+
|
|
145
|
+
Priorize:
|
|
146
|
+
- Informações técnicas importantes sobre código, arquivos e decisões
|
|
147
|
+
- Requisitos e objetivos definidos
|
|
148
|
+
- Problemas e soluções identificados
|
|
149
|
+
|
|
150
|
+
Responda APENAS com a compressão no formato especificado, sem comentários adicionais.`;
|
|
151
|
+
}
|
|
152
|
+
buildMergeCompressionPrompt(comp1, comp2) {
|
|
153
|
+
return `Mescla estas duas compressões antigas em uma única sumarização coesa:
|
|
154
|
+
|
|
155
|
+
COMPRESSÃO A: ${comp1}
|
|
156
|
+
COMPRESSÃO B: ${comp2}
|
|
157
|
+
|
|
158
|
+
Crie uma nova compressão combinada (máx 400 tokens) que preserve o essencial de ambas.
|
|
159
|
+
Formato: "COMPRESSÃO COMBINADA: [sumarização unificada]"
|
|
160
|
+
|
|
161
|
+
Responda APENAS com a compressão combinada no formato especificado, sem comentários adicionais.`;
|
|
162
|
+
}
|
|
163
|
+
async callLLM(prompt) {
|
|
164
|
+
const { OpenAI } = await Promise.resolve().then(() => __importStar(require('openai')));
|
|
165
|
+
const openai = new OpenAI({
|
|
166
|
+
apiKey: this.llmConfig.apiKey,
|
|
167
|
+
baseURL: this.llmConfig.baseUrl,
|
|
168
|
+
});
|
|
169
|
+
const config = (0, config_1.loadConfigSync)();
|
|
170
|
+
const maxTokens = config.compression?.maxTokens || 500;
|
|
171
|
+
try {
|
|
172
|
+
const response = await openai.chat.completions.create({
|
|
173
|
+
model: this.modelName,
|
|
174
|
+
messages: [
|
|
175
|
+
{
|
|
176
|
+
role: 'user',
|
|
177
|
+
content: prompt
|
|
178
|
+
}
|
|
179
|
+
],
|
|
180
|
+
max_tokens: maxTokens,
|
|
181
|
+
temperature: 0.3,
|
|
182
|
+
});
|
|
183
|
+
const content = response.choices[0]?.message?.content?.trim();
|
|
184
|
+
if (!content) {
|
|
185
|
+
throw new Error('Resposta vazia do LLM');
|
|
186
|
+
}
|
|
187
|
+
if (!content.includes('COMPRESSÃO')) {
|
|
188
|
+
logger_1.logger.warn('[LLMCompressionService] Formato da resposta não parece válido:', content);
|
|
189
|
+
}
|
|
190
|
+
return content;
|
|
191
|
+
}
|
|
192
|
+
catch (error) {
|
|
193
|
+
logger_1.logger.error('[LLMCompressionService] Erro na chamada ao LLM:', error);
|
|
194
|
+
throw error;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
isConfigured() {
|
|
198
|
+
return !!(this.llmConfig.apiKey && this.llmConfig.model);
|
|
199
|
+
}
|
|
200
|
+
getConfigInfo() {
|
|
201
|
+
return {
|
|
202
|
+
model: this.modelName,
|
|
203
|
+
provider: this.llmConfig.provider,
|
|
204
|
+
hasApiKey: !!this.llmConfig.apiKey,
|
|
205
|
+
hasBaseUrl: !!this.llmConfig.baseUrl,
|
|
206
|
+
maxTokens: this.llmConfig.defaults?.maxTokens,
|
|
207
|
+
temperature: this.llmConfig.defaults?.temperature,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
exports.LLMCompressionService = LLMCompressionService;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createCompressionPromptConfig = exports.createDefaultPromptConfig = exports.CLIPromptBuilder = exports.LLMCompressionService = exports.CompressionManager = void 0;
|
|
4
|
+
var CompressionManager_1 = require("./CompressionManager");
|
|
5
|
+
Object.defineProperty(exports, "CompressionManager", { enumerable: true, get: function () { return CompressionManager_1.CompressionManager; } });
|
|
6
|
+
var LLMCompressionService_1 = require("./LLMCompressionService");
|
|
7
|
+
Object.defineProperty(exports, "LLMCompressionService", { enumerable: true, get: function () { return LLMCompressionService_1.LLMCompressionService; } });
|
|
8
|
+
var promptBuilder_1 = require("./promptBuilder");
|
|
9
|
+
Object.defineProperty(exports, "CLIPromptBuilder", { enumerable: true, get: function () { return promptBuilder_1.CLIPromptBuilder; } });
|
|
10
|
+
Object.defineProperty(exports, "createDefaultPromptConfig", { enumerable: true, get: function () { return promptBuilder_1.createDefaultPromptConfig; } });
|
|
11
|
+
Object.defineProperty(exports, "createCompressionPromptConfig", { enumerable: true, get: function () { return promptBuilder_1.createCompressionPromptConfig; } });
|