@openlife/cli 1.7.9 → 1.7.11

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/dist/index.js CHANGED
@@ -537,11 +537,10 @@ program
537
537
  .action(async (mensagemArgs, options) => {
538
538
  const mensagem = mensagemArgs.join(' ');
539
539
  console.log(`\n> Você: "${mensagem}"\n`);
540
- const designHandled = tryHandleDesignConversation(mensagem);
541
- if (designHandled) {
542
- console.log(`\n> OPEN-LIFE: ${designHandled}\n`);
543
- process.exit(0);
544
- }
540
+ // Hermes parity: `ask` must send the user's message to the agent runtime.
541
+ // Conversational shortcuts such as design import/generate belong to explicit
542
+ // subcommands, not to the main chat path, otherwise OpenLife appears to
543
+ // "answer" without invoking the configured GPT-5.5 executor.
545
544
  const timeoutMs = Number(process.env.OPENLIFE_ASK_TIMEOUT_MS || 90000);
546
545
  const classifier = new IntentClassifier_1.IntentClassifier();
547
546
  const { Gatekeeper } = require('./orchestrator/Gatekeeper');
@@ -568,15 +567,41 @@ program
568
567
  program
569
568
  .command('phase1-check')
570
569
  .description('Roda os checks mínimos da Fase 1 para validar o runtime principal')
571
- .action(async () => {
570
+ .option('--deep', 'inclui checks vivos de LLM/TTS/Gateway (pode demorar e depender de credenciais/quota)', false)
571
+ .action(async (options) => {
572
572
  let exitCode = 0;
573
- const MASTER_TIMEOUT_MS = Number(process.env.OPENLIFE_PHASE1_TIMEOUT_MS || 30000);
573
+ const deep = Boolean(options.deep);
574
+ const MASTER_TIMEOUT_MS = Number(process.env.OPENLIFE_PHASE1_TIMEOUT_MS || (deep ? 120000 : 30000));
574
575
  const masterTimeout = new Promise((_, reject) => setTimeout(() => reject(new Error(`PHASE1_CHECK_TIMEOUT after ${MASTER_TIMEOUT_MS}ms — provavelmente Gatekeeper/Brain/Gateway constructor ou check travou esperando LLM/recurso externo`)), MASTER_TIMEOUT_MS));
575
576
  try {
576
- // Wrap both construction and run inside race TestHarness constructor instantiates
577
- // Brain/Gatekeeper/Gateway/etc which may block synchronously
577
+ // Default smoke mode avoids live LLM/TTS/Gateway calls so this command stays deterministic.
578
+ // Use --deep for the original live harness behavior.
578
579
  const results = await Promise.race([
579
580
  (async () => {
581
+ if (!deep) {
582
+ const { ModelManager } = require('./orchestrator/ModelManager');
583
+ const { RuntimePolicy } = require('./orchestrator/RuntimePolicy');
584
+ const modelManager = new ModelManager();
585
+ const runtimePolicy = new RuntimePolicy();
586
+ const config = modelManager.getModelConfig();
587
+ const chain = [config.primary, ...config.fallbacks].map((m) => m.raw);
588
+ const research = runtimePolicy.decide('RESEARCH_ANALYSIS');
589
+ const engineering = runtimePolicy.decide('ENGINEERING_BUILD');
590
+ return [
591
+ {
592
+ name: 'model-config',
593
+ ok: chain.length > 0 && chain.every(Boolean) && chain.length === new Set(chain).size,
594
+ detail: `Primário: ${config.primary.raw} | Fallbacks: ${config.fallbacks.map((f) => f.raw).join(', ') || 'nenhum'}`,
595
+ },
596
+ { name: 'runtime-executors', ok: true, detail: 'SKIPPED: probe real pulado em modo smoke. Use --deep para testar executores externos.' },
597
+ {
598
+ name: 'runtime-policy-chain',
599
+ ok: research.preferred.length > 0 && engineering.preferred.length > 0,
600
+ detail: `research=${research.preferred.join('>')} | engineering=${engineering.preferred.join('>')}`,
601
+ },
602
+ { name: 'live-llm-gateway', ok: true, detail: 'SKIPPED: pulados em modo smoke. Use --deep para validar LLM/TTS/Gateway reais.' },
603
+ ];
604
+ }
580
605
  const { TestHarness } = require('./orchestrator/TestHarness');
581
606
  const harness = new TestHarness();
582
607
  return await harness.runPhase1Checks();
@@ -597,9 +622,8 @@ program
597
622
  console.error('phase1-check failed:', errMsg(error));
598
623
  exitCode = 1;
599
624
  }
600
- // Force deterministic exit TestHarness instantiates GatewayTelegraf, leaving
601
- // event-loop handles open. Same root-cause pattern as Story 1.2 `ask` exit fix.
602
- // Override timeout via OPENLIFE_PHASE1_TIMEOUT_MS (default 30s).
625
+ // Force deterministic exit. In --deep mode TestHarness can instantiate Gateway/Telegraf,
626
+ // leaving event-loop handles open; smoke mode exits the same way for a stable CLI contract.
603
627
  process.exit(exitCode);
604
628
  });
605
629
  const systemCmd = program.command('system').description('Instalação, bootstrap e status do OpenLife');
@@ -1372,7 +1396,28 @@ program
1372
1396
  program
1373
1397
  .command('update')
1374
1398
  .description('Atualiza dependências, recompila o core e valida status do sistema (modo dev)')
1375
- .action(async () => {
1399
+ .option('--global', 'Self-update via npm global install (npm i -g @openlife/cli@latest)')
1400
+ .action(async (opts) => {
1401
+ if (opts.global) {
1402
+ const { execFileSync } = require('child_process');
1403
+ try {
1404
+ const current = require('../package.json').version;
1405
+ const latest = String(execFileSync('npm', ['view', '@openlife/cli', 'version'], { encoding: 'utf-8' })).trim();
1406
+ if (latest === current) {
1407
+ console.log(JSON.stringify({ ok: true, status: 'up-to-date', version: current }));
1408
+ return;
1409
+ }
1410
+ console.log(`🔄 Self-update ${current} → ${latest}`);
1411
+ execFileSync('npm', ['install', '-g', '@openlife/cli@latest'], { stdio: 'inherit' });
1412
+ const after = String(execFileSync('openlife', ['--version'], { encoding: 'utf-8' })).trim();
1413
+ console.log(JSON.stringify({ ok: true, from: current, to: after }));
1414
+ }
1415
+ catch (e) {
1416
+ console.error(JSON.stringify({ ok: false, error: 'self_update_failed', detail: errMsg(e) }));
1417
+ process.exitCode = 1;
1418
+ }
1419
+ return;
1420
+ }
1376
1421
  console.log('🔄 OPEN-LIFE update: npm install + build + system status');
1377
1422
  try {
1378
1423
  const { stdout, stderr } = await exec('npm install && npm run build && node bin/openlife.js system status', { cwd: process.cwd(), maxBuffer: 1024 * 1024 * 10 });
@@ -11,11 +11,15 @@ class MemoryProviderRegistry {
11
11
  constructor() {
12
12
  this.providers = {
13
13
  local: new LocalMemoryProvider_1.LocalMemoryProvider(),
14
- mempalace: new MempalaceProvider_1.MempalaceProvider(),
15
14
  mem0: new Mem0Provider_1.Mem0Provider(),
16
15
  'zep-graphiti': new ZepGraphitiProvider_1.ZepGraphitiProvider(),
17
16
  'redis-ams': new RedisAgentMemoryProvider_1.RedisAgentMemoryProvider()
18
17
  };
18
+ // MemPalace é opcional: registra apenas se o binário Python existir.
19
+ // Evita shell-out por turno em runtimes (Railway) que não têm mempalace instalado.
20
+ if (MempalaceProvider_1.MempalaceProvider.isAvailable()) {
21
+ this.providers.mempalace = new MempalaceProvider_1.MempalaceProvider();
22
+ }
19
23
  }
20
24
  get(name) {
21
25
  return this.providers[name] || null;
@@ -35,21 +35,69 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.MempalaceProvider = void 0;
37
37
  const child_process = __importStar(require("child_process"));
38
+ const fs = __importStar(require("fs"));
39
+ const os = __importStar(require("os"));
40
+ const path = __importStar(require("path"));
38
41
  const util_1 = require("util");
39
- const exec = (0, util_1.promisify)(child_process.exec);
42
+ const execFile = (0, util_1.promisify)(child_process.execFile);
43
+ const DEFAULT_BIN = '~/.venvs/mempalace/bin/mempalace';
44
+ function expandHome(p) {
45
+ if (!p)
46
+ return p;
47
+ if (p === '~')
48
+ return os.homedir();
49
+ if (p.startsWith('~/'))
50
+ return path.join(os.homedir(), p.slice(2));
51
+ return p;
52
+ }
53
+ /**
54
+ * MemPalace é uma integração OPCIONAL. Em produção (Railway) o binário Python
55
+ * normalmente não existe; tentar shell-out adiciona latência e ruído de log a
56
+ * cada turno de conversa. `isAvailable()` resolve o path uma única vez e
57
+ * retorna false silenciosamente quando o binário não está instalado, para que
58
+ * o MemoryProviderRegistry possa pular o registro sem custo.
59
+ */
40
60
  class MempalaceProvider {
41
61
  mempalacePath;
42
- constructor(mempalacePath = process.env.MEMPALACE_BIN_PATH || '~/.venvs/mempalace/bin/mempalace') {
62
+ static cachedAvailability = null;
63
+ static cachedPath = null;
64
+ constructor(mempalacePath = process.env.MEMPALACE_BIN_PATH || DEFAULT_BIN) {
43
65
  this.mempalacePath = mempalacePath;
44
66
  }
45
67
  name() {
46
68
  return 'mempalace';
47
69
  }
70
+ static isAvailable(rawPath) {
71
+ const configured = (rawPath ?? process.env.MEMPALACE_BIN_PATH ?? DEFAULT_BIN).trim();
72
+ if (configured === '' || configured.toLowerCase() === 'off' || configured.toLowerCase() === 'disabled') {
73
+ MempalaceProvider.cachedAvailability = false;
74
+ MempalaceProvider.cachedPath = null;
75
+ return false;
76
+ }
77
+ if (MempalaceProvider.cachedAvailability !== null && MempalaceProvider.cachedPath === configured) {
78
+ return MempalaceProvider.cachedAvailability;
79
+ }
80
+ const resolved = expandHome(configured);
81
+ let ok = false;
82
+ try {
83
+ ok = fs.existsSync(resolved) && fs.statSync(resolved).isFile();
84
+ }
85
+ catch {
86
+ ok = false;
87
+ }
88
+ MempalaceProvider.cachedAvailability = ok;
89
+ MempalaceProvider.cachedPath = configured;
90
+ return ok;
91
+ }
92
+ static resetAvailabilityCache() {
93
+ MempalaceProvider.cachedAvailability = null;
94
+ MempalaceProvider.cachedPath = null;
95
+ }
48
96
  async search(input) {
49
- const safeQuery = input.query.replace(/"/g, '\\"');
50
- const command = `bash -c "${this.mempalacePath} search \\\"${safeQuery}\\\""`;
97
+ if (!MempalaceProvider.isAvailable(this.mempalacePath))
98
+ return [];
51
99
  try {
52
- const { stdout } = await exec(command);
100
+ const { stdout } = await execFile(expandHome(this.mempalacePath), ['search', input.query], { timeout: 5000 });
53
101
  if (!stdout.trim())
54
102
  return [];
55
103
  return [{
@@ -39,6 +39,7 @@ const util_1 = require("util");
39
39
  const fs = __importStar(require("fs"));
40
40
  const path = __importStar(require("path"));
41
41
  const LocalMemoryProvider_1 = require("./LocalMemoryProvider");
42
+ const MempalaceProvider_1 = require("./MempalaceProvider");
42
43
  const MemoryOrchestrator_1 = require("./MemoryOrchestrator");
43
44
  const ToolsetGuard_1 = require("../orchestrator/toolset/ToolsetGuard");
44
45
  const exec = (0, util_1.promisify)(child_process.exec);
@@ -57,20 +58,23 @@ class OmniMemory {
57
58
  if (hits.length) {
58
59
  return hits.map(hit => `- [${hit.provider}] ${hit.record.summary || hit.record.content}`).join('\n');
59
60
  }
61
+ if (!MempalaceProvider_1.MempalaceProvider.isAvailable(this.mempalacePath)) {
62
+ return '';
63
+ }
60
64
  console.log(`[OMNI-MEMORY] Buscando no MemPalace: "${query}"...`);
61
65
  try {
62
66
  const safeQuery = query.replace(/"/g, '\\"');
63
67
  const command = `bash -c "${this.mempalacePath} search \\\"${safeQuery}\\\""`;
64
- const { stdout } = await exec(command);
68
+ const timeoutMs = Number(process.env.OPENLIFE_MEMORY_SEARCH_TIMEOUT_MS || 1500);
69
+ const { stdout } = await exec(command, { timeout: timeoutMs });
65
70
  if (!stdout.trim() || stdout.includes("No results found")) {
66
- return "Nenhuma memória encontrada no Palácio para este termo.";
71
+ return "";
67
72
  }
68
73
  const citation = `\n\n[RAG de Evidências: Buscado em ${new Date().toISOString()}]`;
69
74
  return stdout + citation;
70
75
  }
71
- catch (error) {
72
- console.warn(`[OMNI-MEMORY] Aviso ao buscar no MemPalace (pode ser ausência de resultados):`, error);
73
- return "Busca na Omni-Memory concluída com alertas ou sem resultados diretos.";
76
+ catch (_error) {
77
+ return "";
74
78
  }
75
79
  }
76
80
  async saveFact(fact, metadata, namespace) {
@@ -36,18 +36,48 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
36
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.Brain = void 0;
39
+ exports.Brain = exports.CodexTimeoutError = void 0;
40
40
  const child_process = __importStar(require("child_process"));
41
41
  const util_1 = require("util");
42
+ const fs = __importStar(require("fs"));
43
+ const path = __importStar(require("path"));
42
44
  const openai_1 = __importDefault(require("openai"));
43
45
  const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
44
46
  const generative_ai_1 = require("@google/generative-ai");
45
47
  const ModelManager_1 = require("./ModelManager");
46
48
  const ToolsetGuard_1 = require("./toolset/ToolsetGuard");
47
49
  const execFile = (0, util_1.promisify)(child_process.execFile);
48
- const DEFAULT_MODEL_TIMEOUT_MS = Number(process.env.OPENLIFE_MODEL_TIMEOUT_MS || 120000);
50
+ const DEFAULT_MODEL_TIMEOUT_MS = Number(process.env.OPENLIFE_MODEL_TIMEOUT_MS || 60000);
49
51
  const REASONING_MODE = String(process.env.OPENLIFE_REASONING_MODE || 'errors').toLowerCase(); // off | errors | always
52
+ // Conversational fast path must fail fast so the Gateway can surface a
53
+ // timeout-aware reply to the user. Deep engineering missions keep the full
54
+ // 60s budget. Both are env-overridable.
55
+ function getCodexFastTimeoutMs() {
56
+ return Number(process.env.OPENLIFE_CODEX_FAST_TIMEOUT_MS || 30000);
57
+ }
58
+ function getCodexDeepTimeoutMs() {
59
+ return Number(process.env.OPENLIFE_CODEX_DEEP_TIMEOUT_MS || DEFAULT_MODEL_TIMEOUT_MS);
60
+ }
61
+ /**
62
+ * Typed Codex CLI timeout. Callers (Gateway, Gatekeeper) can distinguish a
63
+ * real timeout from a generic provider failure and present a clearer message.
64
+ * The `depth` attribute marks whether the fast or deep budget fired.
65
+ */
66
+ class CodexTimeoutError extends Error {
67
+ depth;
68
+ timeoutMs;
69
+ model;
70
+ constructor(depth, timeoutMs, model) {
71
+ super(`CODEX_TIMEOUT_${depth.toUpperCase()}_${timeoutMs}MS_${model || 'default'}`);
72
+ this.name = 'CodexTimeoutError';
73
+ this.depth = depth;
74
+ this.timeoutMs = timeoutMs;
75
+ this.model = model || 'default';
76
+ }
77
+ }
78
+ exports.CodexTimeoutError = CodexTimeoutError;
50
79
  class Brain {
80
+ static codexQueue = Promise.resolve();
51
81
  openai = null;
52
82
  anthropic = null;
53
83
  geminiApi = null;
@@ -90,6 +120,15 @@ class Brain {
90
120
  async think(systemPrompt, userMessage) {
91
121
  const config = this.modelManager.getModelConfig();
92
122
  const modelsToTry = [config.primary, ...config.fallbacks].filter((model, index, arr) => arr.findIndex(m => m.raw === model.raw) === index);
123
+ return this.runModelChain(systemPrompt, userMessage, modelsToTry, 'deep');
124
+ }
125
+ /** Fast conversational path. Keeps the configured model chain order intact. */
126
+ async thinkFast(systemPrompt, userMessage) {
127
+ const config = this.modelManager.getModelConfig();
128
+ const models = [config.primary, ...config.fallbacks].filter((model, index, arr) => arr.findIndex(m => m.raw === model.raw) === index);
129
+ return this.runModelChain(systemPrompt, userMessage, models, 'fast');
130
+ }
131
+ async runModelChain(systemPrompt, userMessage, modelsToTry, depth = 'deep') {
93
132
  const failures = [];
94
133
  for (let i = 0; i < modelsToTry.length; i++) {
95
134
  const modelIdent = modelsToTry[i];
@@ -101,7 +140,7 @@ class Brain {
101
140
  out = await this.thinkWithOpenAIAPI(systemPrompt, userMessage, modelIdent.name);
102
141
  break;
103
142
  case 'openai-cli':
104
- out = await this.thinkWithOpenAICLI(systemPrompt, userMessage, modelIdent.name);
143
+ out = await this.thinkWithOpenAICLI(systemPrompt, userMessage, modelIdent.name, depth);
105
144
  break;
106
145
  case 'anthropic':
107
146
  out = await this.thinkWithAnthropic(systemPrompt, userMessage, modelIdent.name);
@@ -139,10 +178,24 @@ class Brain {
139
178
  }
140
179
  }
141
180
  const concise = failures.slice(-3).map(f => `- ${f.model}: ${f.reason}`).join('\n');
181
+ const strictGpt55Only = modelsToTry.length === 1 && modelsToTry[0]?.raw === 'openai-cli/gpt-5.5';
182
+ if (strictGpt55Only) {
183
+ return [
184
+ 'Executor GPT-5.5 indisponível no momento.',
185
+ '',
186
+ 'Falhas recentes:',
187
+ concise,
188
+ '',
189
+ 'Ação recomendada:',
190
+ '1) Reautentique o Codex OAuth no mesmo runtime onde o OpenLife está rodando.',
191
+ '2) Valide com: codex login status',
192
+ '3) Valide execução real com: codex exec --model gpt-5.5 "responda apenas OK"'
193
+ ].join('\n');
194
+ }
142
195
  const guidance = [
143
- '1) Configure ao menos um provedor API no Railway (ex.: GEMINI_API_KEY ou OPENAI_API_KEY).',
144
- '2) Não dependa de CLI providers (codex/gemini) no container sem binário/auth.',
145
- '3) Rode: openlife doctor e valide a cadeia de modelos.'
196
+ '1) Valide as credenciais/binários dos provedores configurados.',
197
+ '2) Ajuste models.json apenas se a política do produto permitir fallbacks.',
198
+ '3) Rode: openlife doctor e openlife models status.'
146
199
  ].join('\n');
147
200
  const summary = REASONING_MODE !== 'off'
148
201
  ? `\n\nResumo operacional:\n- Objetivo: responder com robustez por fallback.\n- Estratégia: rotação sequencial de provedores.\n- Resultado: todos os provedores tentados falharam.`
@@ -193,19 +246,73 @@ class Brain {
193
246
  throw this.formatProviderError('openai-api', model, err, { keyEnvVar: 'OPENAI_API_KEY', expectedKeyPrefix: 'sk-' });
194
247
  }
195
248
  }
196
- async thinkWithOpenAICLI(systemPrompt, userMessage, model) {
249
+ async thinkWithOpenAICLI(systemPrompt, userMessage, model, depth = 'deep') {
197
250
  (0, ToolsetGuard_1.assertToolsetAllowed)('delegation', 'Brain.thinkWithOpenAICLI');
198
- const args = ['exec', '--dangerously-bypass-approvals-and-sandbox', '--skip-git-repo-check'];
251
+ const outputFile = path.join(process.cwd(), '.openlife', `codex-last-message-${Date.now()}-${Math.random().toString(36).slice(2, 8)}.txt`);
252
+ fs.mkdirSync(path.dirname(outputFile), { recursive: true });
253
+ const args = ['exec', '--dangerously-bypass-approvals-and-sandbox', '--skip-git-repo-check', '--color', 'never', '--output-last-message', outputFile];
254
+ if (String(process.env.OPENLIFE_CODEX_EPHEMERAL || 'true').toLowerCase() !== 'false')
255
+ args.push('--ephemeral');
199
256
  if (model && model !== 'default')
200
257
  args.push('--model', model);
201
- args.push(`${systemPrompt}\n\nMensagem: ${userMessage}`);
258
+ const prompt = depth === 'fast'
259
+ ? [
260
+ 'Você é Lara, agente conversacional do OpenLife.',
261
+ 'Responda diretamente em português natural e conciso.',
262
+ 'Não use ferramentas, não leia arquivos e não execute comandos para responder esta mensagem.',
263
+ 'Se não souber algo específico, seja honesta e diga o que falta.',
264
+ '',
265
+ `Mensagem: ${userMessage}`
266
+ ].join('\n')
267
+ : `${systemPrompt}\n\nMensagem: ${userMessage}`;
268
+ args.push(prompt);
269
+ const timeoutMs = depth === 'fast' ? getCodexFastTimeoutMs() : getCodexDeepTimeoutMs();
270
+ // Deep missions remain serialized through the static queue to avoid
271
+ // contending Codex CLI invocations. Fast conversational calls bypass
272
+ // the queue so a long-running deep mission can not block a greeting.
273
+ let release = () => { };
274
+ if (depth === 'deep') {
275
+ const previous = Brain.codexQueue;
276
+ Brain.codexQueue = new Promise((resolve) => { release = resolve; });
277
+ await previous;
278
+ }
202
279
  try {
203
- const { stdout } = await execFile('codex', args, { maxBuffer: 1024 * 1024 * 10, timeout: DEFAULT_MODEL_TIMEOUT_MS });
204
- return stdout.trim() || 'Sem resposta do Codex CLI.';
280
+ const timeoutSeconds = Math.max(1, Math.ceil(timeoutMs / 1000));
281
+ const command = process.platform === 'win32' ? 'codex' : 'timeout';
282
+ const commandArgs = process.platform === 'win32'
283
+ ? args
284
+ : ['-k', '2s', `${timeoutSeconds}s`, 'codex', ...args];
285
+ const { stdout } = await execFile(command, commandArgs, { maxBuffer: 1024 * 1024 * 10, timeout: timeoutMs + 3000, killSignal: 'SIGKILL' });
286
+ const lastMessage = fs.existsSync(outputFile) ? fs.readFileSync(outputFile, 'utf-8').trim() : '';
287
+ try {
288
+ fs.unlinkSync(outputFile);
289
+ }
290
+ catch { /* ignore cleanup */ }
291
+ const finalText = lastMessage || stdout.trim();
292
+ if (!finalText)
293
+ throw new Error(`EMPTY_CODEX_RESPONSE_${model || 'default'}`);
294
+ return finalText;
205
295
  }
206
296
  catch (err) {
297
+ try {
298
+ if (fs.existsSync(outputFile))
299
+ fs.unlinkSync(outputFile);
300
+ }
301
+ catch { /* ignore cleanup */ }
302
+ // execFile signals a timeout via either ETIMEDOUT or by killing the
303
+ // child (signal !== null). Surface a typed error so callers can
304
+ // present a depth-aware message.
305
+ const e = err;
306
+ const errCode = e.code;
307
+ const isTimeout = errCode === 'ETIMEDOUT' || errCode === 124 || errCode === '124' || (e?.killed === true && !!e?.signal);
308
+ if (isTimeout) {
309
+ throw new CodexTimeoutError(depth, timeoutMs, model);
310
+ }
207
311
  throw this.formatProviderError('openai-cli', model, err);
208
312
  }
313
+ finally {
314
+ release();
315
+ }
209
316
  }
210
317
  async thinkWithAnthropic(systemPrompt, userMessage, model) {
211
318
  if (!this.anthropic)
@@ -229,78 +229,73 @@ class Gatekeeper {
229
229
  return direct;
230
230
  }
231
231
  let finalResponse = "";
232
- const recoveredContext = await this.conversationMemory.recoverContext(userId, userInput);
233
232
  switch (task.intent) {
234
- case IntentClassifier_1.TaskIntent.KNOWLEDGE_RETRIEVAL:
235
- finalResponse = await this.handleFastPath(userInput, recoveredContext.recentHistory, recoveredContext.memorySnippet);
233
+ case IntentClassifier_1.TaskIntent.KNOWLEDGE_RETRIEVAL: {
234
+ const useMemoryContext = this.shouldUseMemoryContext(userInput);
235
+ const recoveredContext = useMemoryContext
236
+ ? await this.conversationMemory.recoverContext(userId, userInput)
237
+ : { recentHistory: this.getRecentSessionHistory(userId), memorySnippet: '', autoRecovered: false };
238
+ finalResponse = await this.handleFastPath(userInput, recoveredContext.recentHistory, recoveredContext.memorySnippet, useMemoryContext);
236
239
  break;
240
+ }
237
241
  case IntentClassifier_1.TaskIntent.ENGINEERING_BUILD:
238
- case IntentClassifier_1.TaskIntent.RESEARCH_ANALYSIS:
242
+ case IntentClassifier_1.TaskIntent.RESEARCH_ANALYSIS: {
243
+ const recoveredContext = await this.conversationMemory.recoverContext(userId, userInput);
239
244
  finalResponse = await this.handleComplexPath(userInput, task, recoveredContext.recentHistory, userId, options?.mode);
240
245
  break;
246
+ }
241
247
  default:
242
- finalResponse = "Intenção desconhecida. Proteção Mythos ativada.";
248
+ finalResponse = await this.handleFastPath(userInput, this.getRecentSessionHistory(userId), '', false);
243
249
  }
244
250
  this.sessionManager.addMessage(userId, 'agent', finalResponse);
245
251
  return finalResponse;
246
252
  }
247
- async handleFastPath(input, recentHistory, recoveredMemory) {
248
- console.log("[FAST PATH] Acionando Omni-Memory e recuperação automática de contexto...");
249
- const searchResult = recoveredMemory || await this.memory.search(input);
253
+ getRecentSessionHistory(userId) {
254
+ const session = this.sessionManager.getSession(userId);
255
+ return session.history
256
+ .slice(-4)
257
+ .map((h) => `${h.role}: ${h.content}`)
258
+ .join('\n');
259
+ }
260
+ shouldUseMemoryContext(input) {
261
+ const normalized = input.toLowerCase();
262
+ if (String(process.env.OPENLIFE_FASTPATH_MEMORY || 'auto').toLowerCase() === 'always')
263
+ return true;
264
+ if (String(process.env.OPENLIFE_FASTPATH_MEMORY || 'auto').toLowerCase() === 'off')
265
+ return false;
266
+ if (input.length > 240)
267
+ return true;
268
+ return /(lembra|memória|memoria|projeto|openlife|lara|aiobuilder|catalog|catálogo|railway|github|deploy|repo|arquivo|documento|histórico|historico)/i.test(normalized);
269
+ }
270
+ async handleFastPath(input, recentHistory, recoveredMemory, useMemoryContext) {
271
+ console.log(useMemoryContext
272
+ ? "[FAST PATH] GPT-5.5 com contexto de memória seletivo..."
273
+ : "[FAST PATH] GPT-5.5 modo compacto sem OmniMemory...");
274
+ const searchResult = useMemoryContext ? (recoveredMemory || await this.memory.search(input)) : '';
250
275
  const systemPrompt = `
251
- Sua Alma (SOUL):
252
- ${this.systemSoul}
253
-
254
- Sua Identidade (IDENTITY):
255
- ${this.systemIdentity}
276
+ Identidade:
277
+ ${this.systemIdentity.substring(0, 900)}
256
278
 
257
- Contexto da OmniMemory (Arquivos):
258
- ${searchResult.substring(0, 1500)}
279
+ ${searchResult ? `Contexto relevante:\n${searchResult.substring(0, 900)}\n` : ''}
280
+ Histórico recente:
281
+ ${recentHistory.slice(-1200)}
259
282
 
260
- [Histórico Recente da Sessão (Save State)]:
261
- ${recentHistory.slice(-3000)}
262
-
263
- Instruções Adicionais:
264
- - IMPORTANTE: NÃO comece sua resposta com saudações ("Olá", "Oi") se o usuário já estiver conversando com você no Histórico Recente. Continue a conversa naturalmente.
265
- - Você tem memória fluida. Baseie-se fortemente no [Histórico Recente da Sessão] para manter o contexto vivo. Responda DIRETAMENTE à última mensagem.
266
- - Use a OmniMemory para informações profundas de projetos (como LARA, AIOBUILDER).
267
- - Se for apenas um 'oi' inicial, saúde amigavelmente e pergunte como pode ajudar.
268
- - NUNCA mencione tool calls, comandos internos, ou frases como "vou executar comando". Apenas responda naturalmente com o resultado final.
283
+ Instruções:
284
+ - Responda diretamente à última mensagem, em português natural.
285
+ - Seja rápido e conciso por padrão.
286
+ - Não mencione comandos internos, tool calls ou detalhes de execução.
287
+ - Se precisar de contexto profundo, diga objetivamente o que falta.
269
288
  `;
270
- const fastTimeoutMs = Number(process.env.OPENLIFE_FASTPATH_TIMEOUT_MS || 90000);
271
- const degradedPrompt = `${this.systemIdentity}\n\nResponda de forma direta e curta, sem contexto extra.`;
272
289
  try {
273
- return await Promise.race([
274
- this.brain.think(systemPrompt, input),
275
- new Promise((_, reject) => setTimeout(() => reject(new Error(`FAST_PATH_TIMEOUT_${fastTimeoutMs}`)), fastTimeoutMs))
276
- ]);
290
+ return await this.brain.thinkFast(systemPrompt, input);
277
291
  }
278
292
  catch (err) {
279
293
  const code = (err instanceof Error ? err.message : String(err)) || 'FAST_PATH_FAILURE';
280
294
  console.error(`[FAST PATH ERROR] ${code}`);
281
- if (String(code).startsWith('FAST_PATH_TIMEOUT_')) {
282
- try {
283
- const retryTimeoutMs = Number(process.env.OPENLIFE_FASTPATH_RETRY_TIMEOUT_MS || 30000);
284
- return await Promise.race([
285
- this.brain.think(degradedPrompt, input),
286
- new Promise((_, reject) => setTimeout(() => reject(new Error(`FAST_PATH_RETRY_TIMEOUT_${retryTimeoutMs}`)), retryTimeoutMs))
287
- ]);
288
- }
289
- catch (retryErr) {
290
- const retryCode = (retryErr instanceof Error ? retryErr.message : String(retryErr)) || 'FAST_PATH_RETRY_FAILURE';
291
- return [
292
- 'OPENLIFE ONLINE ✅',
293
- 'Modo resiliente: primeira rota estourou tempo, tentei fallback enxuto.',
294
- `Motivo técnico: ${retryCode}`,
295
- 'Ação: valide cadeia de modelos com `openlife models status` e aumente timeout com OPENLIFE_FASTPATH_TIMEOUT_MS.'
296
- ].join('\n');
297
- }
298
- }
299
295
  return [
300
- 'OPENLIFE ONLINE ',
301
- 'Modo seguro ativado: não vou inventar resposta quando o executor falhar.',
296
+ 'Executor GPT-5.5 indisponível ou lento demais neste momento.',
302
297
  `Motivo técnico: ${code}`,
303
- 'Ação: tente novamente em alguns segundos. Se persistir, rode `openlife governance audit` e `openlife models status`.'
298
+ 'Ação: verifique OAuth do Codex e tente novamente.'
304
299
  ].join('\n');
305
300
  }
306
301
  }
@@ -415,16 +415,59 @@ class Gateway {
415
415
  }
416
416
  }
417
417
  async processTextForTest(userId, text) {
418
- let finalReply = '';
418
+ const detailed = await this.processTextForTestDetailed(userId, text);
419
+ return detailed.finalText;
420
+ }
421
+ /**
422
+ * Test seam that captures the full ACK + final timeline for the fast-ACK
423
+ * pattern. Used by the regression test to assert the placeholder reply
424
+ * lands within the ACK budget even when downstream model processing is
425
+ * slow. Returns:
426
+ * - finalText: the user-visible final answer (edited ACK or last reply)
427
+ * - events: ordered timeline (kind = 'reply' | 'edit' | 'voice')
428
+ * - ackMs: ms until the first reply landed (null if none was sent)
429
+ * - finalMs: ms until the last event landed
430
+ */
431
+ async processTextForTestDetailed(userId, text) {
432
+ const startTs = Date.now();
433
+ const events = [];
434
+ let ackMs = null;
435
+ let lastReplyText = '';
436
+ let lastEditText = null;
437
+ let messageIdCounter = 0;
438
+ let lastReplyMessageId = 0;
419
439
  const testCtx = {
420
440
  sendChatAction: async (_action) => { },
421
- reply: async (message) => { finalReply = message; },
441
+ reply: async (message) => {
442
+ const tsMs = Date.now() - startTs;
443
+ events.push({ kind: 'reply', text: message, tsMs });
444
+ if (ackMs === null)
445
+ ackMs = tsMs;
446
+ lastReplyText = message;
447
+ lastReplyMessageId = ++messageIdCounter;
448
+ return { message_id: lastReplyMessageId };
449
+ },
422
450
  sendVoice: async (_source, options) => {
423
- finalReply = options?.caption || '[voice_sent]';
451
+ const caption = options?.caption || '[voice_sent]';
452
+ const tsMs = Date.now() - startTs;
453
+ events.push({ kind: 'voice', text: caption, tsMs });
454
+ lastReplyText = caption;
455
+ return { message_id: ++messageIdCounter };
456
+ },
457
+ chat: { id: userId },
458
+ telegram: {
459
+ editMessageText: async (_chatId, _messageId, _inlineId, edited) => {
460
+ const tsMs = Date.now() - startTs;
461
+ events.push({ kind: 'edit', text: edited, tsMs });
462
+ lastEditText = edited;
463
+ return true;
464
+ }
424
465
  }
425
466
  };
426
467
  await this.processInput(testCtx, userId, text);
427
- return finalReply;
468
+ const finalText = lastEditText ?? lastReplyText;
469
+ const finalMs = events.length ? events[events.length - 1].tsMs : 0;
470
+ return { finalText, events, ackMs, finalMs };
428
471
  }
429
472
  async processImageForTest(userId, imagePath, caption = 'Descreva esta imagem.') {
430
473
  (0, ToolsetGuard_1.assertToolsetAllowed)('vision', 'Gateway.processImageForTest');
@@ -442,6 +485,42 @@ class Gateway {
442
485
  await this.safeReply(ctx, 'Acesso negado por policy de segurança deste bot.');
443
486
  return;
444
487
  }
488
+ // Fast ACK pattern: fire a placeholder reply in parallel with the actual
489
+ // classifier+gatekeeper work. When the work completes, edit the ACK with
490
+ // the real answer (Telegraf editMessageText). Falls back to a fresh
491
+ // reply when the surface does not expose `chat`/`telegram` (webhook mock
492
+ // and similar internal callers).
493
+ const ackText = '🔎 Recebido — processando…';
494
+ let ackMessageId = null;
495
+ const ackPromise = (async () => {
496
+ try {
497
+ const result = await ctx.reply(ackText);
498
+ if (result && typeof result === 'object' && 'message_id' in result) {
499
+ const mid = result.message_id;
500
+ if (typeof mid === 'number')
501
+ ackMessageId = mid;
502
+ }
503
+ }
504
+ catch (ackErr) {
505
+ console.error('[GATEWAY] Falha ao enviar ACK rápido:', ackErr);
506
+ }
507
+ })();
508
+ const sendFinal = async (finalText) => {
509
+ await ackPromise;
510
+ if (ackMessageId !== null &&
511
+ ctx.chat &&
512
+ ctx.telegram &&
513
+ typeof ctx.telegram.editMessageText === 'function') {
514
+ try {
515
+ await ctx.telegram.editMessageText(ctx.chat.id, ackMessageId, undefined, finalText);
516
+ return;
517
+ }
518
+ catch (editErr) {
519
+ console.error('[GATEWAY] editMessageText falhou, enviando nova mensagem:', editErr);
520
+ }
521
+ }
522
+ await this.safeReply(ctx, finalText);
523
+ };
445
524
  const stopTyping = this.startTypingIndicator(ctx);
446
525
  const trace = [];
447
526
  try {
@@ -459,11 +538,11 @@ class Gateway {
459
538
  }
460
539
  catch (ttsError) {
461
540
  console.log("[GATEWAY] Fallback para texto (Erro no TTS).", ttsError);
462
- await this.safeReply(ctx, finalResponse);
541
+ await sendFinal(finalResponse);
463
542
  }
464
543
  }
465
544
  else {
466
- await this.safeReply(ctx, finalResponse);
545
+ await sendFinal(finalResponse);
467
546
  }
468
547
  }
469
548
  catch (err) {
@@ -471,7 +550,7 @@ class Gateway {
471
550
  const errMsg = this.reasoningMode !== 'off'
472
551
  ? `${trace.join('\n')}\n\n❌ error: "${err instanceof Error ? err.message : String(err)}"\n\nErro no córtex ao processar a solicitação.`
473
552
  : "Erro no córtex ao processar a solicitação.";
474
- await this.safeReply(ctx, errMsg);
553
+ await sendFinal(errMsg);
475
554
  }
476
555
  finally {
477
556
  stopTyping();
@@ -46,11 +46,14 @@ class RuntimePolicy {
46
46
  this.runtimeHealth = new RuntimeHealthMonitor_1.RuntimeHealthMonitor();
47
47
  }
48
48
  decide(intent, explicitExecutors = []) {
49
+ const strictModelExecutors = this.getStrictModelExecutors();
49
50
  const base = explicitExecutors.length
50
51
  ? explicitExecutors
51
- : intent === 'RESEARCH_ANALYSIS'
52
- ? ['gemini', 'claude', 'codex']
53
- : ['codex', 'claude', 'gemini'];
52
+ : strictModelExecutors.length
53
+ ? strictModelExecutors
54
+ : intent === 'RESEARCH_ANALYSIS'
55
+ ? ['gemini', 'claude', 'codex']
56
+ : ['codex', 'claude', 'gemini'];
54
57
  const allowedRaw = (process.env.OPENLIFE_ALLOWED_LLM_EXECUTORS || '').trim().toLowerCase();
55
58
  const allowed = new Set();
56
59
  if (allowedRaw) {
@@ -76,6 +79,28 @@ class RuntimePolicy {
76
79
  : 'Todos os executores da cadeia configurada estão indisponíveis no momento.'
77
80
  };
78
81
  }
82
+ getStrictModelExecutors() {
83
+ try {
84
+ const modelsPath = path.join(process.cwd(), 'models.json');
85
+ if (!fs.existsSync(modelsPath))
86
+ return [];
87
+ const cfg = JSON.parse(fs.readFileSync(modelsPath, 'utf-8'));
88
+ const chain = [cfg.primary, ...(cfg.fallbacks || [])].filter(Boolean);
89
+ if (chain.length !== 1)
90
+ return [];
91
+ const provider = chain[0].provider || '';
92
+ if (provider === 'openai-cli')
93
+ return ['codex'];
94
+ if (provider === 'gemini-cli' || provider === 'gemini-api')
95
+ return ['gemini'];
96
+ if (provider === 'anthropic-api')
97
+ return ['claude'];
98
+ return [];
99
+ }
100
+ catch {
101
+ return [];
102
+ }
103
+ }
79
104
  recordResult(executor, ok, reason) {
80
105
  this.executorHealth.set(executor, ok, reason);
81
106
  if (ok) {
@@ -171,17 +171,21 @@ class SquadCreator {
171
171
  if (frontmatter.id && frontmatter.id !== squadId) {
172
172
  errors.push(`frontmatter id '${frontmatter.id}' does not match directory '${squadId}'`);
173
173
  }
174
- // Component files referenced in SQUAD.md should exist
175
- const componentRefs = this.parseComponentRefs(content);
176
- for (const refPath of componentRefs.agents) {
177
- const full = path.join(squadDir, 'agents', refPath);
178
- if (!fs.existsSync(full))
179
- warnings.push(`referenced agent not found: agents/${refPath}`);
180
- }
181
- for (const refPath of componentRefs.tasks) {
182
- const full = path.join(squadDir, 'tasks', refPath);
183
- if (!fs.existsSync(full))
184
- warnings.push(`referenced task not found: tasks/${refPath}`);
174
+ // Imported squads (lara-squads-import, obsidian-mirror, etc.) reference
175
+ // agents/tasks from the global .catalog/{agents,skills}/ pool, not squad-local.
176
+ const isImported = typeof frontmatter.source === 'string' && frontmatter.source.endsWith('-import');
177
+ if (!isImported) {
178
+ const componentRefs = this.parseComponentRefs(content);
179
+ for (const refPath of componentRefs.agents) {
180
+ const full = path.join(squadDir, 'agents', refPath);
181
+ if (!fs.existsSync(full))
182
+ warnings.push(`referenced agent not found: agents/${refPath}`);
183
+ }
184
+ for (const refPath of componentRefs.tasks) {
185
+ const full = path.join(squadDir, 'tasks', refPath);
186
+ if (!fs.existsSync(full))
187
+ warnings.push(`referenced task not found: tasks/${refPath}`);
188
+ }
185
189
  }
186
190
  return { ok: errors.length === 0, squadId, errors, warnings };
187
191
  }
@@ -0,0 +1,66 @@
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
+ const assert = __importStar(require("assert"));
37
+ const Gateway_1 = require("./orchestrator/Gateway");
38
+ async function main() {
39
+ process.env.OPENLIFE_TELEGRAM_ALLOWED_USER_ID = '';
40
+ process.env.OPENLIFE_ENABLE_TTS = 'false';
41
+ const gateway = new Gateway_1.Gateway();
42
+ gateway.classifier = {
43
+ classify: async (_text) => ({ intent: 'KNOWLEDGE_RETRIEVAL', budget: 0.1 })
44
+ };
45
+ gateway.gatekeeper = {
46
+ routeTask: async (_task, _text, _userId) => {
47
+ await new Promise(resolve => setTimeout(resolve, 1200));
48
+ return 'FINAL_AGENT_RESPONSE_FROM_GPT55_PATH';
49
+ }
50
+ };
51
+ const started = Date.now();
52
+ const result = await gateway.processTextForTestDetailed('1344110010', 'ola');
53
+ const totalMs = Date.now() - started;
54
+ assert.ok(result.ackMs !== null, 'ACK_NOT_SENT');
55
+ assert.ok(result.ackMs < 500, `ACK_TOO_SLOW_${result.ackMs}`);
56
+ assert.ok(result.events.length >= 2, `EXPECTED_ACK_AND_FINAL_EVENTS_${JSON.stringify(result.events)}`);
57
+ assert.strictEqual(result.events[0].kind, 'reply', 'FIRST_EVENT_SHOULD_BE_ACK_REPLY');
58
+ assert.ok(result.events[0].text.includes('processando'), `ACK_TEXT_UNEXPECTED_${result.events[0].text}`);
59
+ assert.strictEqual(result.finalText, 'FINAL_AGENT_RESPONSE_FROM_GPT55_PATH', `FINAL_TEXT_UNEXPECTED_${result.finalText}`);
60
+ assert.ok(totalMs >= 1100, 'TEST_STUB_DID_NOT_SIMULATE_SLOW_PATH');
61
+ console.log('TEST_GATEWAY_FAST_ACK_OK');
62
+ }
63
+ main().catch((err) => {
64
+ console.error('TEST_GATEWAY_FAST_ACK_FAIL:', err?.message || err);
65
+ process.exit(1);
66
+ });
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ /**
3
+ * Regression: OpenLife chat surface must not return canned/automatic replies.
4
+ * The main ask/Gatekeeper path should invoke the configured model executor even
5
+ * for greetings; deterministic status commands remain explicit operational paths.
6
+ */
7
+ function assertTrue(cond, label) {
8
+ if (!cond)
9
+ throw new Error(`ASSERT_FAILED[${label}]`);
10
+ }
11
+ function installBrainMock(mock) {
12
+ const brainPath = require.resolve('./orchestrator/Brain');
13
+ const orig = require.cache[brainPath];
14
+ require.cache[brainPath] = {
15
+ ...orig,
16
+ exports: {
17
+ Brain: class {
18
+ isAnyProviderAvailable() { return true; }
19
+ async thinkFast(_systemPrompt, userMessage) {
20
+ mock.invocations += 1;
21
+ mock.lastUserMessage = userMessage;
22
+ return mock.reply;
23
+ }
24
+ async think(_systemPrompt, userMessage) {
25
+ mock.invocations += 1;
26
+ mock.lastUserMessage = userMessage;
27
+ return mock.reply;
28
+ }
29
+ },
30
+ },
31
+ };
32
+ return () => {
33
+ if (orig)
34
+ require.cache[brainPath] = orig;
35
+ else
36
+ delete require.cache[brainPath];
37
+ };
38
+ }
39
+ async function main() {
40
+ process.env.OPENLIFE_OUTCOME_SIMULATION = 'off';
41
+ const mock = { invocations: 0, reply: 'MODEL_REPLY_FROM_GPT55_PATH' };
42
+ const restore = installBrainMock(mock);
43
+ try {
44
+ const { IntentClassifier } = require('./orchestrator/IntentClassifier');
45
+ const { Gatekeeper } = require('./orchestrator/Gatekeeper');
46
+ const classifier = new IntentClassifier();
47
+ const gatekeeper = new Gatekeeper();
48
+ const greeting = 'ola';
49
+ const task = await classifier.classify(greeting);
50
+ const response = await gatekeeper.routeTask(task, greeting, 'no-auto-test');
51
+ assertTrue(response === 'MODEL_REPLY_FROM_GPT55_PATH', 'greeting response comes from Brain/model path');
52
+ assertTrue(mock.invocations === 1, `Brain invoked exactly once for greeting (got ${mock.invocations})`);
53
+ assertTrue(mock.lastUserMessage === greeting, 'original greeting passed to model');
54
+ assertTrue(!/OpenLife online|Como posso ajudar|OPENLIFE ONLINE|Intenção desconhecida/.test(response), 'no canned chat text returned');
55
+ }
56
+ finally {
57
+ restore();
58
+ }
59
+ console.log('TEST_NO_AUTOMATIC_MESSAGES_OK');
60
+ }
61
+ main().catch((err) => {
62
+ console.error(err);
63
+ process.exit(1);
64
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openlife/cli",
3
- "version": "1.7.9",
3
+ "version": "1.7.11",
4
4
  "description": "OPEN-LIFE Córtex Orquestrador Dual-Core",
5
5
  "main": "dist/index.js",
6
6
  "files": [
@@ -161,11 +161,14 @@
161
161
  "test:agent-creator": "npm run build && node dist/test_agent_creator.js",
162
162
  "pretest:all": "node scripts/clean-test-pollution.js",
163
163
  "test:all": "npm run build && node dist/test_distribution_installability.js && node dist/test_orchestration_assets_lifecycle.js && node dist/test_openlife_runtime_source_truth.js && node dist/test_openlife_evolution_surface.js && node dist/test_openlife_routing_surface.js && node dist/test_openlife_auto_creator_routing.js && node dist/test_openlife_gatekeeper_routing.js && node dist/test_enterprise_agentic_core.js && node dist/test_admin_teams_networks.js && node dist/test_agent_team_skill_network.js && node dist/test_benchmark_engine.js && node dist/test_cli_service_commands.js && node dist/test_consequence_forecaster.js && node dist/test_conversation_memory.js && node dist/test_create_entities.js && node dist/test_designmd_import_registry.js && node dist/test_designmd_mode.js && node dist/test_designmd_mode_workspace.js && node dist/test_dream_organizer.js && node dist/test_dual_mode.js && node dist/test_governance.js && node dist/test_governance_advanced.js && node dist/test_install_flow.js && node dist/test_job_lifecycle.js && node dist/test_memory_orchestrator.js && node dist/test_memory_promotion.js && node dist/test_memory_retention.js && node dist/test_operating_system.js && node dist/test_optimization_loop.js && node dist/test_outcome_simulator.js && node dist/test_performance_scorecard.js && node dist/test_phase6_board.js && node dist/test_phase6_cadence.js && node dist/test_phase6_ops.js && node dist/test_release_gate.js && node dist/test_reversa_contracts_e2e.js && node dist/test_reversa_export_and_strict.js && node dist/test_reversa_full_execution.js && node dist/test_reversa_lite.js && node dist/test_runtime_policy.js && node dist/test_runtime_probe.js && node dist/test_runtime_registry.js && node dist/test_security_download_guard.js && node dist/test_service_command_surface.js && node dist/test_service_completion_policy.js && node dist/test_service_guardrails_delete.js && node dist/test_sources_import_ref.js && node dist/test_sources_scaffold.js && node dist/test_teammate_learning.js && node dist/test_telegram_delete_guardrail.js && node dist/test_daemon_sigterm.js && node dist/test_ask_exit.js && node dist/test_brain_error_diagnostics.js && node dist/test_cli_doc_parity.js && node dist/test_trigger_basic_auth.js && node dist/test_brain_fallback_chain.js && node dist/test_cli_help_surface.js && node dist/test_cli_diagnostics.js && node dist/test_cli_crud_roundtrip.js && node dist/test_subsystems_routing_governance.js && node dist/test_subsystems_org_state.js && node dist/test_subsystems_promotion_memory_assets.js && node dist/test_phase1_check_exit.js && node dist/test_install_flow_host_validation.js && node dist/test_dist_templates_layout.js && node dist/test_host_installer.js && node dist/test_host_uninstaller.js && node dist/test_install_wizard.js && node dist/test_multi_host_docs_parity.js && node dist/test_host_install_e2e.js && node dist/test_runtime_profile_oauth_only.js && node dist/test_atomic_writer.js && node dist/test_mission_checkpoint.js && node dist/test_workflow_parser.js && node dist/test_workflow_engine.js && node dist/test_workflow_e2e.js && node dist/test_distributed_lock.js && node dist/test_watchdog_heartbeat.js && node dist/test_runtime_health_backoff.js && node dist/test_queue_scheduler.js && node dist/test_squad_skill_creator.js && node dist/test_aiobuilder_cli_parity.js && node dist/test_catalog_quality.js && node dist/test_royal_stack_golden.js && node dist/test_capability_pack_schema.js && node dist/test_capability_genesis_engine.js && node dist/test_workflow_schema_backward_compat.js && node dist/test_service_mode_explicit_only.js && node dist/test_deep_research_capability.js && node dist/test_guided_creator_cli.js && node dist/test_governance_v13_policies.js && node dist/test_gateway_telegram_guardrails.js && node dist/test_cron_manager.js && node dist/test_profile_toolset_mcp.js && node dist/test_squad_skill_design_llm.js && node dist/test_workflow_condition_parser.js && node dist/test_security_download_and_scan.js && node dist/test_host_installers_gemini_codex.js && node dist/test_toolset_enforcement.js && node dist/test_creator_placeholders_completed.js && node dist/test_performance_latency.js && node dist/test_post_mission_evaluation.js && node dist/test_governance_scope_ledger.js && node dist/test_consequence_forecast_brain.js && node dist/test_remote_publish.js && node dist/test_process_sandbox.js && node dist/test_v15_e2e_integration.js && node dist/test_doctor_sandbox_check.js && node dist/test_task_executor_sandbox_optin.js && node dist/test_forecast_brain_wiring.js && node dist/test_status_command.js && node dist/test_logs_command.js && node dist/test_agent_creator.js",
164
- "prepublishOnly": "npm run test:all"
164
+ "prepublishOnly": "npm run test:all",
165
+ "test:no-automatic-messages": "npm run build && node dist/test_no_automatic_messages.js",
166
+ "test:gateway-fast-ack": "npm run build && node dist/test_gateway_fast_ack.js"
165
167
  },
166
168
  "dependencies": {
167
169
  "@anthropic-ai/sdk": "^0.86.1",
168
170
  "@google/generative-ai": "^0.24.1",
171
+ "@openai/codex": "^0.130.0",
169
172
  "axios": "^1.15.0",
170
173
  "commander": "^11.1.0",
171
174
  "dotenv": "^16.3.1",