@yeaft/webchat-agent 0.1.411 → 0.1.413
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/package.json +3 -2
- package/service/index.js +10 -0
- package/unify/cli.js +208 -106
- package/unify/config.js +174 -181
- package/unify/engine.js +22 -4
- package/unify/eval/cases/e2e.js +154 -0
- package/unify/eval/cases/memory.js +182 -0
- package/unify/eval/cases/skills.js +51 -0
- package/unify/eval/cases/tool-use.js +356 -0
- package/unify/eval/run-eval.js +250 -0
- package/unify/eval/runner.js +525 -0
- package/unify/index.js +2 -1
- package/unify/init.js +51 -54
- package/unify/llm/adapter.js +12 -0
- package/unify/llm/router.js +148 -0
- package/unify/models.js +46 -2
- package/unify/session.js +11 -6
- package/unify/stop-hooks.js +6 -1
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yeaft/webchat-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.413",
|
|
4
4
|
"description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
|
-
"yeaft-agent": "./cli.js"
|
|
8
|
+
"yeaft-agent": "./cli.js",
|
|
9
|
+
"yeaft": "./unify/cli.js"
|
|
9
10
|
},
|
|
10
11
|
"scripts": {
|
|
11
12
|
"start": "node index.js",
|
package/service/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
saveServiceConfig, loadServiceConfig,
|
|
10
10
|
parseServiceArgs, validateConfig
|
|
11
11
|
} from './config.js';
|
|
12
|
+
import { initYeaftDir } from '../unify/init.js';
|
|
12
13
|
import { getSystemdServicePath, linuxInstall, linuxUninstall, linuxStart, linuxStop, linuxRestart, linuxStatus, linuxLogs } from './linux.js';
|
|
13
14
|
import { getLaunchdPlistPath, macInstall, macUninstall, macStart, macStop, macRestart, macStatus, macLogs } from './macos.js';
|
|
14
15
|
import { winInstall, winUninstall, winStart, winStop, winRestart, winStatus, winLogs } from './windows.js';
|
|
@@ -42,6 +43,15 @@ export function install(args) {
|
|
|
42
43
|
validateConfig(config);
|
|
43
44
|
saveServiceConfig(config);
|
|
44
45
|
|
|
46
|
+
// Initialize ~/.yeaft/ directory + default config.json
|
|
47
|
+
// so `yeaft` CLI is ready to use immediately after install
|
|
48
|
+
const { dir, created } = initYeaftDir();
|
|
49
|
+
if (created.length > 0) {
|
|
50
|
+
console.log(`Initialized ${dir}`);
|
|
51
|
+
console.log(` Edit ${dir}/config.json to configure LLM providers.`);
|
|
52
|
+
console.log('');
|
|
53
|
+
}
|
|
54
|
+
|
|
45
55
|
console.log(`Installing ${SERVICE_NAME} service...`);
|
|
46
56
|
console.log(` Server: ${config.serverUrl}`);
|
|
47
57
|
console.log(` Name: ${config.agentName || '(auto)'}`);
|
package/unify/cli.js
CHANGED
|
@@ -2,33 +2,36 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* cli.js — Yeaft Unify CLI entry point
|
|
4
4
|
*
|
|
5
|
+
* Uses loadSession() to wire all subsystems (Engine, ToolRegistry, SkillManager,
|
|
6
|
+
* MCPManager, Dream, StopHooks, Memory) into a single session.
|
|
7
|
+
*
|
|
5
8
|
* Features:
|
|
6
9
|
* --dry-run "prompt" — Assemble system prompt + messages, don't call LLM
|
|
7
10
|
* --trace stats|recent|search <keyword> — Query debug.db
|
|
8
11
|
* -i / --interactive — REPL mode with / commands
|
|
9
12
|
* <prompt> — One-shot query (Phase 1: engine.query)
|
|
13
|
+
* --skip-mcp — Skip MCP server connections (faster startup)
|
|
14
|
+
* --skip-skills — Skip skill loading
|
|
10
15
|
*
|
|
11
|
-
*
|
|
16
|
+
* REPL commands:
|
|
12
17
|
* /memory — Show memory status or manage entries
|
|
13
18
|
* /history [n] — Show last N messages from conversation
|
|
14
19
|
* /search <keyword> — Search conversation history
|
|
15
20
|
* /compact — Trigger manual consolidation
|
|
21
|
+
* /tools — List registered tools
|
|
22
|
+
* /skills — List loaded skills
|
|
16
23
|
* Conversation persistence across REPL sessions
|
|
17
24
|
*/
|
|
18
25
|
|
|
19
26
|
import { createInterface } from 'readline';
|
|
20
27
|
import { join } from 'path';
|
|
21
|
-
import { initYeaftDir } from './init.js';
|
|
22
28
|
import { loadConfig } from './config.js';
|
|
23
|
-
import { DebugTrace
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import { listModels, resolveModel } from './models.js';
|
|
29
|
+
import { DebugTrace } from './debug-trace.js';
|
|
30
|
+
import { loadSession } from './session.js';
|
|
31
|
+
import { listModels, resolveModel, parseModelRef } from './models.js';
|
|
27
32
|
import { buildSystemPrompt } from './prompts.js';
|
|
28
|
-
import { ConversationStore } from './conversation/persist.js';
|
|
29
33
|
import { searchMessages } from './conversation/search.js';
|
|
30
|
-
import {
|
|
31
|
-
import { shouldConsolidate, consolidate } from './memory/consolidate.js';
|
|
34
|
+
import { consolidate } from './memory/consolidate.js';
|
|
32
35
|
|
|
33
36
|
// ─── Argument parsing ──────────────────────────────────────────
|
|
34
37
|
|
|
@@ -43,6 +46,8 @@ function parseArgs(argv) {
|
|
|
43
46
|
trace: null, // 'stats' | 'recent' | 'search' | 'tools' | null
|
|
44
47
|
traceArg: null, // search keyword or tool name
|
|
45
48
|
dryRun: false,
|
|
49
|
+
skipMCP: false,
|
|
50
|
+
skipSkills: false,
|
|
46
51
|
prompt: null,
|
|
47
52
|
};
|
|
48
53
|
|
|
@@ -83,6 +88,12 @@ function parseArgs(argv) {
|
|
|
83
88
|
case '--dry-run':
|
|
84
89
|
args.dryRun = true;
|
|
85
90
|
break;
|
|
91
|
+
case '--skip-mcp':
|
|
92
|
+
args.skipMCP = true;
|
|
93
|
+
break;
|
|
94
|
+
case '--skip-skills':
|
|
95
|
+
args.skipSkills = true;
|
|
96
|
+
break;
|
|
86
97
|
default:
|
|
87
98
|
if (!arg.startsWith('-') && !args.prompt) {
|
|
88
99
|
args.prompt = arg;
|
|
@@ -197,17 +208,16 @@ function handleDryRun(args, config) {
|
|
|
197
208
|
// ─── REPL ──────────────────────────────────────────────────────
|
|
198
209
|
|
|
199
210
|
async function runREPL(config, args) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
211
|
+
// Use loadSession() to wire all subsystems
|
|
212
|
+
const session = await loadSession({
|
|
213
|
+
model: args.model || config.model,
|
|
214
|
+
language: args.language || config.language,
|
|
215
|
+
debug: args.debug || config.debug,
|
|
216
|
+
skipMCP: args.skipMCP,
|
|
217
|
+
skipSkills: args.skipSkills,
|
|
203
218
|
});
|
|
204
219
|
|
|
205
|
-
|
|
206
|
-
const conversationStore = new ConversationStore(config.dir);
|
|
207
|
-
const memoryStore = new MemoryStore(config.dir);
|
|
208
|
-
|
|
209
|
-
let adapter;
|
|
210
|
-
let engine;
|
|
220
|
+
const { engine, conversationStore, memoryStore, trace, skillManager, mcpManager, toolRegistry } = session;
|
|
211
221
|
let currentMode = args.mode;
|
|
212
222
|
|
|
213
223
|
// Load persisted conversation as initial messages
|
|
@@ -218,21 +228,19 @@ async function runREPL(config, args) {
|
|
|
218
228
|
...(m.toolCalls && { toolCalls: m.toolCalls }),
|
|
219
229
|
}));
|
|
220
230
|
|
|
221
|
-
// Lazy adapter creation (don't fail on start if no API key for --trace-only usage)
|
|
222
|
-
async function ensureEngine() {
|
|
223
|
-
if (!engine) {
|
|
224
|
-
adapter = await createLLMAdapter(config);
|
|
225
|
-
engine = new Engine({ adapter, trace, config, conversationStore, memoryStore });
|
|
226
|
-
}
|
|
227
|
-
return engine;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
231
|
const hotCount = conversationStore.countHot();
|
|
231
232
|
const coldCount = conversationStore.countCold();
|
|
232
233
|
const memStats = memoryStore.stats();
|
|
233
234
|
|
|
234
|
-
console.log(`Yeaft Unify REPL (model: ${config.model}, mode: ${currentMode})`);
|
|
235
|
+
console.log(`Yeaft Unify REPL (model: ${session.config.model}, mode: ${currentMode})`);
|
|
235
236
|
console.log(`Conversation: ${hotCount} hot, ${coldCount} cold | Memory: ${memStats.entryCount} entries`);
|
|
237
|
+
console.log(`Tools: ${session.status.tools} | Skills: ${session.status.skills}`);
|
|
238
|
+
if (session.status.mcpServers.length > 0) {
|
|
239
|
+
console.log(`MCP: ${session.status.mcpServers.join(', ')}`);
|
|
240
|
+
}
|
|
241
|
+
if (session.status.mcpFailed.length > 0) {
|
|
242
|
+
console.log(`MCP failed: ${session.status.mcpFailed.map(f => f.name || f).join(', ')}`);
|
|
243
|
+
}
|
|
236
244
|
console.log('Type /help for commands, /quit to exit.');
|
|
237
245
|
console.log();
|
|
238
246
|
|
|
@@ -267,9 +275,12 @@ async function runREPL(config, args) {
|
|
|
267
275
|
console.log(' /context — Show context info');
|
|
268
276
|
console.log(' /dry-run — Toggle dry-run mode');
|
|
269
277
|
console.log(' /stats — Show session stats');
|
|
270
|
-
console.log(' /model <name> — Switch model');
|
|
278
|
+
console.log(' /model <name> — Switch model (supports provider/model)');
|
|
271
279
|
console.log(' /models — List available models');
|
|
280
|
+
console.log(' /providers — List configured providers');
|
|
272
281
|
console.log(' /language <en|zh> — Switch language');
|
|
282
|
+
console.log(' /tools — List registered tools');
|
|
283
|
+
console.log(' /skills — List loaded skills');
|
|
273
284
|
console.log(' /clear — Clear conversation history');
|
|
274
285
|
console.log(' /quit — Exit');
|
|
275
286
|
break;
|
|
@@ -285,14 +296,14 @@ async function runREPL(config, args) {
|
|
|
285
296
|
break;
|
|
286
297
|
|
|
287
298
|
case 'debug':
|
|
288
|
-
config.debug = !config.debug;
|
|
289
|
-
console.log(`Debug mode: ${config.debug ? 'ON' : 'OFF'}`);
|
|
299
|
+
session.config.debug = !session.config.debug;
|
|
300
|
+
console.log(`Debug mode: ${session.config.debug ? 'ON' : 'OFF'}`);
|
|
290
301
|
break;
|
|
291
302
|
|
|
292
303
|
case 'trace': {
|
|
293
304
|
const subcmd = cmdArgs[0] || 'stats';
|
|
294
305
|
try {
|
|
295
|
-
handleTraceQuery({ trace: subcmd, traceArg: cmdArgs[1] }, config);
|
|
306
|
+
handleTraceQuery({ trace: subcmd, traceArg: cmdArgs[1] }, session.config);
|
|
296
307
|
} catch (e) {
|
|
297
308
|
console.error(`Trace error: ${e.message}`);
|
|
298
309
|
}
|
|
@@ -373,7 +384,7 @@ async function runREPL(config, args) {
|
|
|
373
384
|
if (!keyword) {
|
|
374
385
|
console.log('Usage: /search <keyword>');
|
|
375
386
|
} else {
|
|
376
|
-
const results = searchMessages(
|
|
387
|
+
const results = searchMessages(session.yeaftDir, keyword, 20);
|
|
377
388
|
if (results.length === 0) {
|
|
378
389
|
console.log(`No messages matching "${keyword}".`);
|
|
379
390
|
} else {
|
|
@@ -390,8 +401,7 @@ async function runREPL(config, args) {
|
|
|
390
401
|
|
|
391
402
|
case 'compact': {
|
|
392
403
|
try {
|
|
393
|
-
const
|
|
394
|
-
const budget = config.messageTokenBudget || 8192;
|
|
404
|
+
const budget = session.config.messageTokenBudget || 8192;
|
|
395
405
|
const hotTokens = conversationStore.hotTokens();
|
|
396
406
|
console.log(`Hot tokens: ${hotTokens} / budget: ${budget}`);
|
|
397
407
|
|
|
@@ -404,8 +414,8 @@ async function runREPL(config, args) {
|
|
|
404
414
|
const result = await consolidate({
|
|
405
415
|
conversationStore,
|
|
406
416
|
memoryStore,
|
|
407
|
-
adapter: adapter
|
|
408
|
-
config,
|
|
417
|
+
adapter: session.adapter,
|
|
418
|
+
config: session.config,
|
|
409
419
|
budget: Math.min(budget, hotTokens), // force trigger
|
|
410
420
|
});
|
|
411
421
|
console.log(`Consolidation complete:`);
|
|
@@ -422,63 +432,115 @@ async function runREPL(config, args) {
|
|
|
422
432
|
|
|
423
433
|
case 'context':
|
|
424
434
|
console.log(`Context info:`);
|
|
425
|
-
console.log(` Model: ${config.model}`);
|
|
435
|
+
console.log(` Model: ${session.config.model}`);
|
|
426
436
|
console.log(` Mode: ${currentMode}`);
|
|
427
|
-
console.log(` Language: ${config.language}`);
|
|
428
|
-
console.log(` Max context: ${config.maxContextTokens} tokens`);
|
|
429
|
-
console.log(` System prompt: ${buildSystemPrompt({ language: config.language, mode: currentMode }).length} chars`);
|
|
437
|
+
console.log(` Language: ${session.config.language}`);
|
|
438
|
+
console.log(` Max context: ${session.config.maxContextTokens} tokens`);
|
|
439
|
+
console.log(` System prompt: ${buildSystemPrompt({ language: session.config.language, mode: currentMode }).length} chars`);
|
|
430
440
|
console.log(` Hot messages: ${conversationStore.countHot()}`);
|
|
431
441
|
console.log(` Hot tokens: ${conversationStore.hotTokens()}`);
|
|
432
442
|
console.log(` Cold messages: ${conversationStore.countCold()}`);
|
|
433
443
|
console.log(` Memory entries: ${memoryStore.stats().entryCount}`);
|
|
444
|
+
console.log(` Tools: ${toolRegistry.size}`);
|
|
445
|
+
console.log(` Skills: ${skillManager.size}`);
|
|
434
446
|
break;
|
|
435
447
|
|
|
436
448
|
case 'dry-run':
|
|
437
|
-
handleDryRun({ ...args, mode: currentMode, prompt: cmdArgs.join(' ') || null }, config);
|
|
449
|
+
handleDryRun({ ...args, mode: currentMode, prompt: cmdArgs.join(' ') || null }, session.config);
|
|
438
450
|
break;
|
|
439
451
|
|
|
440
452
|
case 'stats': {
|
|
441
453
|
const s = trace.stats();
|
|
442
454
|
console.log(`Session stats:`);
|
|
443
455
|
console.log(` Mode: ${currentMode}`);
|
|
444
|
-
console.log(` Debug: ${config.debug}`);
|
|
456
|
+
console.log(` Debug: ${session.config.debug}`);
|
|
445
457
|
console.log(` Turns: ${s.turnCount}`);
|
|
446
458
|
console.log(` Tools: ${s.toolCount}`);
|
|
447
459
|
console.log(` Hot messages: ${conversationStore.countHot()}`);
|
|
448
460
|
console.log(` Cold messages: ${conversationStore.countCold()}`);
|
|
449
461
|
console.log(` Memory entries: ${memoryStore.stats().entryCount}`);
|
|
462
|
+
console.log(` Registered tools: ${toolRegistry.size}`);
|
|
463
|
+
console.log(` Loaded skills: ${skillManager.size}`);
|
|
450
464
|
break;
|
|
451
465
|
}
|
|
452
466
|
|
|
453
467
|
case 'model':
|
|
454
468
|
if (cmdArgs[0]) {
|
|
455
469
|
try {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
470
|
+
const modelRef = cmdArgs[0];
|
|
471
|
+
const { providerName, modelId } = parseModelRef(modelRef);
|
|
472
|
+
|
|
473
|
+
if (providerName) {
|
|
474
|
+
// Full ref: provider/model — update primaryModel
|
|
475
|
+
session.config.model = modelId;
|
|
476
|
+
session.config.primaryModel = modelRef;
|
|
477
|
+
} else {
|
|
478
|
+
// Bare model ID
|
|
479
|
+
session.config.model = modelRef;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Re-resolve model info from registry
|
|
483
|
+
const newModelInfo = resolveModel(session.config.model);
|
|
459
484
|
if (newModelInfo) {
|
|
460
|
-
config.
|
|
461
|
-
config.
|
|
462
|
-
config.
|
|
463
|
-
config.maxOutputTokens = newModelInfo.maxOutputTokens;
|
|
464
|
-
config.modelInfo = newModelInfo;
|
|
485
|
+
session.config.maxContextTokens = newModelInfo.contextWindow;
|
|
486
|
+
session.config.maxOutputTokens = newModelInfo.maxOutputTokens;
|
|
487
|
+
session.config.modelInfo = newModelInfo;
|
|
465
488
|
}
|
|
466
|
-
|
|
467
|
-
console.log(`Model switched to: ${config.model}
|
|
489
|
+
const providerStr = providerName ? ` (provider: ${providerName})` : '';
|
|
490
|
+
console.log(`Model switched to: ${session.config.model}${providerStr}`);
|
|
491
|
+
console.log('Note: Model change takes effect on next query.');
|
|
468
492
|
} catch (e) {
|
|
469
493
|
console.error(`Error switching model: ${e.message}`);
|
|
470
494
|
}
|
|
471
495
|
} else {
|
|
472
|
-
|
|
496
|
+
const primaryRef = session.config.primaryModel || session.config.model;
|
|
497
|
+
console.log(`Current model: ${primaryRef}`);
|
|
498
|
+
if (session.config.fastModel && session.config.fastModel !== session.config.primaryModel) {
|
|
499
|
+
console.log(`Fast model: ${session.config.fastModel}`);
|
|
500
|
+
}
|
|
473
501
|
}
|
|
474
502
|
break;
|
|
475
503
|
|
|
476
504
|
case 'models': {
|
|
477
505
|
const models = listModels();
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
506
|
+
// If providers are configured, show provider-based model list
|
|
507
|
+
if (session.config.providers && session.config.providers.length > 0) {
|
|
508
|
+
console.log('Available models (from providers):');
|
|
509
|
+
for (const provider of session.config.providers) {
|
|
510
|
+
console.log(` [${provider.name}] ${provider.baseUrl}`);
|
|
511
|
+
if (Array.isArray(provider.models)) {
|
|
512
|
+
for (const m of provider.models) {
|
|
513
|
+
const current = (`${provider.name}/${m}` === session.config.primaryModel) ? ' ← primary' :
|
|
514
|
+
(`${provider.name}/${m}` === session.config.fastModel) ? ' ← fast' : '';
|
|
515
|
+
const info = resolveModel(m);
|
|
516
|
+
const displayName = info ? ` (${info.displayName})` : '';
|
|
517
|
+
console.log(` ${provider.name}/${m}${displayName}${current}`);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
} else {
|
|
522
|
+
// Legacy: show registry models
|
|
523
|
+
console.log('Available models (from registry):');
|
|
524
|
+
for (const m of models) {
|
|
525
|
+
const current = m.name === session.config.model ? ' ← current' : '';
|
|
526
|
+
console.log(` ${m.name} (${m.displayName}) — ${m.adapter}, ${(m.contextWindow / 1000).toFixed(0)}K ctx${current}`);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
break;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
case 'providers': {
|
|
533
|
+
const providers = session.config.providers;
|
|
534
|
+
if (!providers || providers.length === 0) {
|
|
535
|
+
console.log('No providers configured. Using legacy adapter mode.');
|
|
536
|
+
console.log('Add providers to ~/.yeaft/config.json to enable provider routing.');
|
|
537
|
+
} else {
|
|
538
|
+
console.log(`Configured providers (${providers.length}):`);
|
|
539
|
+
for (const p of providers) {
|
|
540
|
+
const protocol = p.protocol || 'openai';
|
|
541
|
+
const modelCount = Array.isArray(p.models) ? p.models.length : 0;
|
|
542
|
+
console.log(` ${p.name} — ${p.baseUrl} (${protocol}, ${modelCount} models)`);
|
|
543
|
+
}
|
|
482
544
|
}
|
|
483
545
|
break;
|
|
484
546
|
}
|
|
@@ -486,13 +548,40 @@ async function runREPL(config, args) {
|
|
|
486
548
|
case 'language':
|
|
487
549
|
case 'lang':
|
|
488
550
|
if (cmdArgs[0]) {
|
|
489
|
-
config.language = cmdArgs[0];
|
|
490
|
-
console.log(`Language switched to: ${config.language}`);
|
|
551
|
+
session.config.language = cmdArgs[0];
|
|
552
|
+
console.log(`Language switched to: ${session.config.language}`);
|
|
491
553
|
} else {
|
|
492
|
-
console.log(`Current language: ${config.language}`);
|
|
554
|
+
console.log(`Current language: ${session.config.language}`);
|
|
493
555
|
}
|
|
494
556
|
break;
|
|
495
557
|
|
|
558
|
+
case 'tools': {
|
|
559
|
+
const names = toolRegistry.names || [];
|
|
560
|
+
if (names.length === 0) {
|
|
561
|
+
console.log('No tools registered.');
|
|
562
|
+
} else {
|
|
563
|
+
console.log(`Registered tools (${names.length}):`);
|
|
564
|
+
for (const name of names) {
|
|
565
|
+
console.log(` - ${name}`);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
break;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
case 'skills': {
|
|
572
|
+
const skillList = skillManager.list() || [];
|
|
573
|
+
if (skillList.length === 0) {
|
|
574
|
+
console.log('No skills loaded.');
|
|
575
|
+
} else {
|
|
576
|
+
console.log(`Loaded skills (${skillList.length}):`);
|
|
577
|
+
for (const skill of skillList) {
|
|
578
|
+
const desc = skill.description ? ` — ${skill.description}` : '';
|
|
579
|
+
console.log(` - ${skill.name}${desc}`);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
break;
|
|
583
|
+
}
|
|
584
|
+
|
|
496
585
|
case 'clear':
|
|
497
586
|
conversationStore.clear();
|
|
498
587
|
conversationMessages = [];
|
|
@@ -502,7 +591,7 @@ async function runREPL(config, args) {
|
|
|
502
591
|
case 'quit':
|
|
503
592
|
case 'exit':
|
|
504
593
|
case 'q':
|
|
505
|
-
rl.close(); // close handler does
|
|
594
|
+
rl.close(); // close handler does shutdown + process.exit()
|
|
506
595
|
return; // don't call rl.prompt() below
|
|
507
596
|
|
|
508
597
|
default:
|
|
@@ -514,10 +603,9 @@ async function runREPL(config, args) {
|
|
|
514
603
|
|
|
515
604
|
// Regular input → engine.query
|
|
516
605
|
try {
|
|
517
|
-
const eng = await ensureEngine();
|
|
518
606
|
let responseText = '';
|
|
519
607
|
|
|
520
|
-
for await (const event of
|
|
608
|
+
for await (const event of engine.query({
|
|
521
609
|
prompt: input,
|
|
522
610
|
mode: currentMode,
|
|
523
611
|
messages: conversationMessages,
|
|
@@ -528,24 +616,33 @@ async function runREPL(config, args) {
|
|
|
528
616
|
process.stdout.write(event.text);
|
|
529
617
|
break;
|
|
530
618
|
case 'tool_start':
|
|
531
|
-
if (config.debug) {
|
|
619
|
+
if (session.config.debug) {
|
|
532
620
|
process.stderr.write(`\n[tool] ${event.name}(${JSON.stringify(event.input)})\n`);
|
|
621
|
+
} else {
|
|
622
|
+
process.stderr.write(`\n⚙ ${event.name}...\n`);
|
|
533
623
|
}
|
|
534
624
|
break;
|
|
535
625
|
case 'tool_end':
|
|
536
|
-
if (config.debug) {
|
|
626
|
+
if (session.config.debug) {
|
|
537
627
|
const status = event.isError ? 'ERROR' : 'OK';
|
|
538
628
|
process.stderr.write(`[tool] ${event.name} → ${status}\n`);
|
|
539
629
|
}
|
|
540
630
|
break;
|
|
541
631
|
case 'recall':
|
|
542
|
-
if (config.debug) {
|
|
632
|
+
if (session.config.debug) {
|
|
543
633
|
process.stderr.write(`[recall] ${event.entryCount} entries${event.cached ? ' (cached)' : ''}\n`);
|
|
544
634
|
}
|
|
545
635
|
break;
|
|
546
636
|
case 'consolidate':
|
|
547
|
-
if (config.debug) {
|
|
637
|
+
if (session.config.debug) {
|
|
548
638
|
process.stderr.write(`[consolidate] archived=${event.archivedCount}, extracted=${event.extractedCount}\n`);
|
|
639
|
+
} else {
|
|
640
|
+
process.stderr.write(`[compact] Memory consolidated\n`);
|
|
641
|
+
}
|
|
642
|
+
break;
|
|
643
|
+
case 'dream_triggered':
|
|
644
|
+
if (session.config.debug) {
|
|
645
|
+
process.stderr.write(`[dream] Dream cycle triggered (background)\n`);
|
|
549
646
|
}
|
|
550
647
|
break;
|
|
551
648
|
case 'fallback':
|
|
@@ -555,7 +652,7 @@ async function runREPL(config, args) {
|
|
|
555
652
|
process.stderr.write(`\nError: ${event.error.message}\n`);
|
|
556
653
|
break;
|
|
557
654
|
case 'turn_start':
|
|
558
|
-
if (config.debug && event.turnNumber > 1) {
|
|
655
|
+
if (session.config.debug && event.turnNumber > 1) {
|
|
559
656
|
process.stderr.write(`\n--- Turn ${event.turnNumber} ---\n`);
|
|
560
657
|
}
|
|
561
658
|
break;
|
|
@@ -564,7 +661,7 @@ async function runREPL(config, args) {
|
|
|
564
661
|
console.log(); // newline after response
|
|
565
662
|
|
|
566
663
|
// Update in-memory conversation for multi-turn context
|
|
567
|
-
// (engine.js already persists to disk via
|
|
664
|
+
// (engine.js already persists to disk via StopHooks when yeaftDir is set)
|
|
568
665
|
conversationMessages.push({ role: 'user', content: input });
|
|
569
666
|
if (responseText) {
|
|
570
667
|
conversationMessages.push({ role: 'assistant', content: responseText });
|
|
@@ -575,8 +672,8 @@ async function runREPL(config, args) {
|
|
|
575
672
|
rl.prompt();
|
|
576
673
|
});
|
|
577
674
|
|
|
578
|
-
rl.on('close', () => {
|
|
579
|
-
|
|
675
|
+
rl.on('close', async () => {
|
|
676
|
+
await session.shutdown();
|
|
580
677
|
console.log('\nBye!');
|
|
581
678
|
process.exit(0);
|
|
582
679
|
});
|
|
@@ -585,24 +682,23 @@ async function runREPL(config, args) {
|
|
|
585
682
|
// ─── One-shot handler ──────────────────────────────────────────
|
|
586
683
|
|
|
587
684
|
async function runOnce(config, args) {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
685
|
+
if (args.dryRun) {
|
|
686
|
+
handleDryRun(args, config);
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
// Use loadSession() to wire all subsystems
|
|
691
|
+
const session = await loadSession({
|
|
692
|
+
model: args.model || config.model,
|
|
693
|
+
language: args.language || config.language,
|
|
694
|
+
debug: args.debug || config.debug,
|
|
695
|
+
skipMCP: args.skipMCP,
|
|
696
|
+
skipSkills: args.skipSkills,
|
|
591
697
|
});
|
|
592
698
|
|
|
593
|
-
|
|
594
|
-
const conversationStore = new ConversationStore(config.dir);
|
|
595
|
-
const memoryStore = new MemoryStore(config.dir);
|
|
699
|
+
const { engine, conversationStore } = session;
|
|
596
700
|
|
|
597
701
|
try {
|
|
598
|
-
if (args.dryRun) {
|
|
599
|
-
handleDryRun(args, config);
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
const adapter = await createLLMAdapter(config);
|
|
604
|
-
const engine = new Engine({ adapter, trace, config, conversationStore, memoryStore });
|
|
605
|
-
|
|
606
702
|
// Load recent conversation as context
|
|
607
703
|
const priorMessages = conversationStore.loadRecent(20).map(m => ({
|
|
608
704
|
role: m.role,
|
|
@@ -621,12 +717,14 @@ async function runOnce(config, args) {
|
|
|
621
717
|
process.stdout.write(event.text);
|
|
622
718
|
break;
|
|
623
719
|
case 'tool_start':
|
|
624
|
-
if (args.verbose) {
|
|
720
|
+
if (args.verbose || args.debug) {
|
|
625
721
|
process.stderr.write(`\n[tool] ${event.name}(${JSON.stringify(event.input)})\n`);
|
|
722
|
+
} else {
|
|
723
|
+
process.stderr.write(`\n⚙ ${event.name}...\n`);
|
|
626
724
|
}
|
|
627
725
|
break;
|
|
628
726
|
case 'tool_end':
|
|
629
|
-
if (args.verbose) {
|
|
727
|
+
if (args.verbose || args.debug) {
|
|
630
728
|
const status = event.isError ? 'ERROR' : 'OK';
|
|
631
729
|
process.stderr.write(`[tool] ${event.name} → ${status}\n`);
|
|
632
730
|
}
|
|
@@ -641,6 +739,11 @@ async function runOnce(config, args) {
|
|
|
641
739
|
process.stderr.write(`[consolidate] archived=${event.archivedCount}, extracted=${event.extractedCount}\n`);
|
|
642
740
|
}
|
|
643
741
|
break;
|
|
742
|
+
case 'dream_triggered':
|
|
743
|
+
if (args.verbose || args.debug) {
|
|
744
|
+
process.stderr.write(`[dream] Dream cycle triggered (background)\n`);
|
|
745
|
+
}
|
|
746
|
+
break;
|
|
644
747
|
case 'fallback':
|
|
645
748
|
process.stderr.write(`\n[fallback] ${event.from} → ${event.to}: ${event.reason}\n`);
|
|
646
749
|
break;
|
|
@@ -657,7 +760,7 @@ async function runOnce(config, args) {
|
|
|
657
760
|
// Final newline after streaming text
|
|
658
761
|
console.log();
|
|
659
762
|
} finally {
|
|
660
|
-
|
|
763
|
+
await session.shutdown();
|
|
661
764
|
}
|
|
662
765
|
}
|
|
663
766
|
|
|
@@ -666,17 +769,14 @@ async function runOnce(config, args) {
|
|
|
666
769
|
async function main() {
|
|
667
770
|
const args = parseArgs(process.argv);
|
|
668
771
|
|
|
669
|
-
// Load config with CLI overrides
|
|
772
|
+
// Load config with CLI overrides (for --trace queries and dry-run, no session needed)
|
|
670
773
|
const config = loadConfig({
|
|
671
774
|
model: args.model,
|
|
672
775
|
language: args.language,
|
|
673
776
|
debug: args.debug || undefined,
|
|
674
777
|
});
|
|
675
778
|
|
|
676
|
-
//
|
|
677
|
-
initYeaftDir(config.dir);
|
|
678
|
-
|
|
679
|
-
// Handle --trace queries (no LLM needed)
|
|
779
|
+
// Handle --trace queries (no LLM needed, no session needed)
|
|
680
780
|
if (args.trace) {
|
|
681
781
|
handleTraceQuery(args, config);
|
|
682
782
|
return;
|
|
@@ -711,22 +811,24 @@ async function main() {
|
|
|
711
811
|
console.log('Yeaft Unify CLI');
|
|
712
812
|
console.log();
|
|
713
813
|
console.log('Usage:');
|
|
714
|
-
console.log(' node cli.js "your prompt"
|
|
715
|
-
console.log(' node cli.js -i
|
|
716
|
-
console.log(' node cli.js --dry-run "prompt"
|
|
717
|
-
console.log(' node cli.js --trace stats
|
|
814
|
+
console.log(' node cli.js "your prompt" — One-shot query');
|
|
815
|
+
console.log(' node cli.js -i — Interactive REPL');
|
|
816
|
+
console.log(' node cli.js --dry-run "prompt" — Show what would be sent');
|
|
817
|
+
console.log(' node cli.js --trace stats — Debug trace statistics');
|
|
718
818
|
console.log(' node cli.js --trace recent — Recent turns');
|
|
719
819
|
console.log(' node cli.js --trace search "keyword" — Search traces');
|
|
720
820
|
console.log();
|
|
721
821
|
console.log('Options:');
|
|
722
|
-
console.log(' -m, --mode <mode>
|
|
723
|
-
console.log(' -d, --debug
|
|
724
|
-
console.log(' -i, --interactive
|
|
725
|
-
console.log(' -v, --verbose
|
|
726
|
-
console.log(' --model <name>
|
|
727
|
-
console.log(' --language <code>
|
|
728
|
-
console.log(' --trace <cmd>
|
|
729
|
-
console.log(' --dry-run
|
|
822
|
+
console.log(' -m, --mode <mode> Mode: chat, work, dream (default: chat)');
|
|
823
|
+
console.log(' -d, --debug Enable debug tracing');
|
|
824
|
+
console.log(' -i, --interactive Start REPL');
|
|
825
|
+
console.log(' -v, --verbose Verbose output');
|
|
826
|
+
console.log(' --model <name> Override model');
|
|
827
|
+
console.log(' --language <code> Language: en, zh (default: en)');
|
|
828
|
+
console.log(' --trace <cmd> Query debug trace');
|
|
829
|
+
console.log(' --dry-run Show prompt without calling LLM');
|
|
830
|
+
console.log(' --skip-mcp Skip MCP server connections');
|
|
831
|
+
console.log(' --skip-skills Skip skill loading');
|
|
730
832
|
}
|
|
731
833
|
|
|
732
834
|
main().catch(err => {
|