@yeaft/webchat-agent 0.1.410 → 0.1.411
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 +1 -1
- package/unify/config.js +36 -0
- package/unify/engine.js +124 -16
- package/unify/index.js +2 -1
- package/unify/prompts.js +6 -0
- package/unify/session.js +191 -0
package/package.json
CHANGED
package/unify/config.js
CHANGED
|
@@ -267,3 +267,39 @@ export function loadConfig(overrides = {}) {
|
|
|
267
267
|
|
|
268
268
|
return config;
|
|
269
269
|
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Load MCP server configuration from ~/.yeaft/mcp.json.
|
|
273
|
+
*
|
|
274
|
+
* JSON format (frontmatter parser can't handle nested objects):
|
|
275
|
+
* {
|
|
276
|
+
* "servers": [
|
|
277
|
+
* {
|
|
278
|
+
* "name": "github",
|
|
279
|
+
* "command": "npx",
|
|
280
|
+
* "args": ["@mcp/github"],
|
|
281
|
+
* "env": { "GITHUB_TOKEN": "ghp_..." }
|
|
282
|
+
* }
|
|
283
|
+
* ]
|
|
284
|
+
* }
|
|
285
|
+
*
|
|
286
|
+
* @param {string} yeaftDir — e.g. ~/.yeaft
|
|
287
|
+
* @returns {{ servers: object[] }}
|
|
288
|
+
*/
|
|
289
|
+
export function loadMCPConfig(yeaftDir) {
|
|
290
|
+
const mcpPath = join(yeaftDir, 'mcp.json');
|
|
291
|
+
if (!existsSync(mcpPath)) return { servers: [] };
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
const raw = readFileSync(mcpPath, 'utf8');
|
|
295
|
+
const parsed = JSON.parse(raw);
|
|
296
|
+
if (!parsed.servers || !Array.isArray(parsed.servers)) {
|
|
297
|
+
return { servers: [] };
|
|
298
|
+
}
|
|
299
|
+
// Each server must have at least name + command
|
|
300
|
+
const valid = parsed.servers.filter(s => s.name && s.command);
|
|
301
|
+
return { servers: valid };
|
|
302
|
+
} catch {
|
|
303
|
+
return { servers: [] };
|
|
304
|
+
}
|
|
305
|
+
}
|
package/unify/engine.js
CHANGED
|
@@ -22,6 +22,7 @@ import { buildSystemPrompt } from './prompts.js';
|
|
|
22
22
|
import { LLMContextError } from './llm/adapter.js';
|
|
23
23
|
import { recall } from './memory/recall.js';
|
|
24
24
|
import { shouldConsolidate, consolidate } from './memory/consolidate.js';
|
|
25
|
+
import { runStopHooks } from './stop-hooks.js';
|
|
25
26
|
|
|
26
27
|
/** Maximum number of turns before the engine stops to prevent infinite loops. */
|
|
27
28
|
const MAX_TURNS = 25;
|
|
@@ -67,16 +68,32 @@ export class Engine {
|
|
|
67
68
|
/** @type {import('./memory/store.js').MemoryStore|null} */
|
|
68
69
|
#memoryStore;
|
|
69
70
|
|
|
71
|
+
/** @type {import('./tools/registry.js').ToolRegistry|null} */
|
|
72
|
+
#toolRegistry;
|
|
73
|
+
|
|
74
|
+
/** @type {import('./skills.js').SkillManager|null} */
|
|
75
|
+
#skillManager;
|
|
76
|
+
|
|
77
|
+
/** @type {import('./mcp.js').MCPManager|null} */
|
|
78
|
+
#mcpManager;
|
|
79
|
+
|
|
80
|
+
/** @type {string|null} */
|
|
81
|
+
#yeaftDir;
|
|
82
|
+
|
|
70
83
|
/**
|
|
71
84
|
* @param {{
|
|
72
85
|
* adapter: import('./llm/adapter.js').LLMAdapter,
|
|
73
86
|
* trace: object,
|
|
74
87
|
* config: object,
|
|
75
88
|
* conversationStore?: import('./conversation/persist.js').ConversationStore,
|
|
76
|
-
* memoryStore?: import('./memory/store.js').MemoryStore
|
|
89
|
+
* memoryStore?: import('./memory/store.js').MemoryStore,
|
|
90
|
+
* toolRegistry?: import('./tools/registry.js').ToolRegistry,
|
|
91
|
+
* skillManager?: import('./skills.js').SkillManager,
|
|
92
|
+
* mcpManager?: import('./mcp.js').MCPManager,
|
|
93
|
+
* yeaftDir?: string,
|
|
77
94
|
* }} params
|
|
78
95
|
*/
|
|
79
|
-
constructor({ adapter, trace, config, conversationStore, memoryStore }) {
|
|
96
|
+
constructor({ adapter, trace, config, conversationStore, memoryStore, toolRegistry, skillManager, mcpManager, yeaftDir }) {
|
|
80
97
|
this.#adapter = adapter;
|
|
81
98
|
this.#trace = trace;
|
|
82
99
|
this.#config = config;
|
|
@@ -84,6 +101,10 @@ export class Engine {
|
|
|
84
101
|
this.#traceId = randomUUID();
|
|
85
102
|
this.#conversationStore = conversationStore || null;
|
|
86
103
|
this.#memoryStore = memoryStore || null;
|
|
104
|
+
this.#toolRegistry = toolRegistry || null;
|
|
105
|
+
this.#skillManager = skillManager || null;
|
|
106
|
+
this.#mcpManager = mcpManager || null;
|
|
107
|
+
this.#yeaftDir = yeaftDir || null;
|
|
87
108
|
}
|
|
88
109
|
|
|
89
110
|
/**
|
|
@@ -106,10 +127,16 @@ export class Engine {
|
|
|
106
127
|
|
|
107
128
|
/**
|
|
108
129
|
* Get the list of registered tool definitions (for passing to the adapter).
|
|
130
|
+
* Prefers ToolRegistry (mode-aware) when available, falls back to legacy #tools Map.
|
|
109
131
|
*
|
|
132
|
+
* @param {string} [mode]
|
|
110
133
|
* @returns {import('./llm/adapter.js').UnifiedToolDef[]}
|
|
111
134
|
*/
|
|
112
|
-
#getToolDefs() {
|
|
135
|
+
#getToolDefs(mode) {
|
|
136
|
+
if (this.#toolRegistry) {
|
|
137
|
+
return this.#toolRegistry.getToolDefs(mode || 'chat');
|
|
138
|
+
}
|
|
139
|
+
// Legacy path: no mode filtering
|
|
113
140
|
const defs = [];
|
|
114
141
|
for (const [, tool] of this.#tools) {
|
|
115
142
|
defs.push({
|
|
@@ -122,23 +149,58 @@ export class Engine {
|
|
|
122
149
|
}
|
|
123
150
|
|
|
124
151
|
/**
|
|
125
|
-
* Build the system prompt with memory and
|
|
152
|
+
* Build the system prompt with memory, compact summary, and skill content.
|
|
126
153
|
*
|
|
127
154
|
* @param {string} mode
|
|
128
155
|
* @param {{ profile?: string, entries?: object[] }} [memory]
|
|
129
156
|
* @param {string} [compactSummary]
|
|
157
|
+
* @param {string} [prompt] — user prompt (for skill relevance matching)
|
|
130
158
|
* @returns {string}
|
|
131
159
|
*/
|
|
132
|
-
#buildSystemPrompt(mode, memory, compactSummary) {
|
|
160
|
+
#buildSystemPrompt(mode, memory, compactSummary, prompt) {
|
|
161
|
+
// Get relevant skill content if SkillManager is wired
|
|
162
|
+
let skillContent = '';
|
|
163
|
+
if (this.#skillManager && prompt) {
|
|
164
|
+
skillContent = this.#skillManager.getRelevantPromptContent(prompt, mode);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Get tool names from the appropriate source
|
|
168
|
+
const toolNames = this.#toolRegistry
|
|
169
|
+
? this.#toolRegistry.getToolNames(mode || 'chat')
|
|
170
|
+
: Array.from(this.#tools.keys());
|
|
171
|
+
|
|
133
172
|
return buildSystemPrompt({
|
|
134
173
|
language: this.#config.language || 'en',
|
|
135
174
|
mode,
|
|
136
|
-
toolNames
|
|
175
|
+
toolNames,
|
|
137
176
|
memory,
|
|
138
177
|
compactSummary,
|
|
178
|
+
skillContent,
|
|
139
179
|
});
|
|
140
180
|
}
|
|
141
181
|
|
|
182
|
+
/**
|
|
183
|
+
* Build the full tool context for Phase 5 tools.
|
|
184
|
+
*
|
|
185
|
+
* @param {AbortSignal} [signal]
|
|
186
|
+
* @param {string} [mode]
|
|
187
|
+
* @returns {object}
|
|
188
|
+
*/
|
|
189
|
+
#buildToolContext(signal, mode) {
|
|
190
|
+
return {
|
|
191
|
+
signal,
|
|
192
|
+
yeaftDir: this.#yeaftDir,
|
|
193
|
+
cwd: process.cwd(),
|
|
194
|
+
mcpManager: this.#mcpManager,
|
|
195
|
+
skillManager: this.#skillManager,
|
|
196
|
+
memoryStore: this.#memoryStore,
|
|
197
|
+
conversationStore: this.#conversationStore,
|
|
198
|
+
adapter: this.#adapter,
|
|
199
|
+
config: this.#config,
|
|
200
|
+
mode,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
142
204
|
/**
|
|
143
205
|
* Perform memory recall for a given prompt.
|
|
144
206
|
*
|
|
@@ -262,7 +324,7 @@ export class Engine {
|
|
|
262
324
|
}
|
|
263
325
|
|
|
264
326
|
const compactSummary = this.#getCompactSummary();
|
|
265
|
-
const systemPrompt = this.#buildSystemPrompt(mode, memory, compactSummary);
|
|
327
|
+
const systemPrompt = this.#buildSystemPrompt(mode, memory, compactSummary, prompt);
|
|
266
328
|
|
|
267
329
|
// Build conversation: existing messages + new user message
|
|
268
330
|
const conversationMessages = [
|
|
@@ -270,7 +332,7 @@ export class Engine {
|
|
|
270
332
|
{ role: 'user', content: prompt },
|
|
271
333
|
];
|
|
272
334
|
|
|
273
|
-
const toolDefs = this.#getToolDefs();
|
|
335
|
+
const toolDefs = this.#getToolDefs(mode);
|
|
274
336
|
let turnNumber = 0;
|
|
275
337
|
let continueTurns = 0; // auto-continue counter
|
|
276
338
|
let fullResponseText = '';
|
|
@@ -416,33 +478,66 @@ export class Engine {
|
|
|
416
478
|
if (stopReason !== 'tool_use' || toolCalls.length === 0) {
|
|
417
479
|
yield { type: 'turn_end', turnNumber, stopReason };
|
|
418
480
|
|
|
419
|
-
// ─── Post-query:
|
|
420
|
-
this.#
|
|
481
|
+
// ─── Post-query: StopHooks or Legacy ─────────────
|
|
482
|
+
if (this.#yeaftDir && this.#conversationStore) {
|
|
483
|
+
// Full pipeline: persist + consolidate + dream gate
|
|
484
|
+
const hookResult = await runStopHooks({
|
|
485
|
+
yeaftDir: this.#yeaftDir,
|
|
486
|
+
mode,
|
|
487
|
+
conversationStore: this.#conversationStore,
|
|
488
|
+
memoryStore: this.#memoryStore,
|
|
489
|
+
adapter: this.#adapter,
|
|
490
|
+
config: this.#config,
|
|
491
|
+
messages: conversationMessages,
|
|
492
|
+
trace: this.#trace,
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
if (hookResult.consolidated) {
|
|
496
|
+
yield { type: 'consolidate', archivedCount: 0, extractedCount: 0 };
|
|
497
|
+
}
|
|
498
|
+
if (hookResult.dreamTriggered) {
|
|
499
|
+
yield { type: 'dream_triggered' };
|
|
500
|
+
}
|
|
501
|
+
} else {
|
|
502
|
+
// Legacy path (no yeaftDir → use old behavior)
|
|
503
|
+
this.#persistMessages(prompt, fullResponseText, mode, assistantMsg.toolCalls);
|
|
421
504
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
505
|
+
const consolidated = await this.#maybeConsolidate();
|
|
506
|
+
if (consolidated && consolidated.archivedCount > 0) {
|
|
507
|
+
yield { type: 'consolidate', archivedCount: consolidated.archivedCount, extractedCount: consolidated.extractedCount };
|
|
508
|
+
}
|
|
425
509
|
}
|
|
426
510
|
|
|
427
511
|
break;
|
|
428
512
|
}
|
|
429
513
|
|
|
430
514
|
// Execute tool calls and feed results back
|
|
515
|
+
const toolCtx = this.#buildToolContext(signal, mode);
|
|
516
|
+
|
|
431
517
|
for (const tc of toolCalls) {
|
|
432
|
-
const tool = this.#tools.get(tc.name);
|
|
433
518
|
const toolStartTime = Date.now();
|
|
434
519
|
|
|
435
520
|
let output;
|
|
436
521
|
let isError = false;
|
|
437
522
|
|
|
438
|
-
|
|
523
|
+
// Resolve tool: prefer ToolRegistry, fallback to legacy #tools Map
|
|
524
|
+
const hasTool = this.#toolRegistry
|
|
525
|
+
? this.#toolRegistry.has(tc.name)
|
|
526
|
+
: this.#tools.has(tc.name);
|
|
527
|
+
|
|
528
|
+
if (!hasTool) {
|
|
439
529
|
output = `Error: unknown tool "${tc.name}"`;
|
|
440
530
|
isError = true;
|
|
441
531
|
yield { type: 'tool_end', id: tc.id, name: tc.name, output, isError: true };
|
|
442
532
|
} else {
|
|
443
533
|
try {
|
|
444
534
|
yield { type: 'tool_start', id: tc.id, name: tc.name, input: tc.input };
|
|
445
|
-
|
|
535
|
+
if (this.#toolRegistry) {
|
|
536
|
+
output = await this.#toolRegistry.execute(tc.name, tc.input, toolCtx);
|
|
537
|
+
} else {
|
|
538
|
+
const tool = this.#tools.get(tc.name);
|
|
539
|
+
output = await tool.execute(tc.input, { signal });
|
|
540
|
+
}
|
|
446
541
|
yield { type: 'tool_end', id: tc.id, name: tc.name, output, isError: false };
|
|
447
542
|
} catch (err) {
|
|
448
543
|
output = `Error: ${err.message}`;
|
|
@@ -490,6 +585,7 @@ export class Engine {
|
|
|
490
585
|
* @returns {string[]}
|
|
491
586
|
*/
|
|
492
587
|
get toolNames() {
|
|
588
|
+
if (this.#toolRegistry) return this.#toolRegistry.names;
|
|
493
589
|
return Array.from(this.#tools.keys());
|
|
494
590
|
}
|
|
495
591
|
|
|
@@ -508,4 +604,16 @@ export class Engine {
|
|
|
508
604
|
get memoryStore() {
|
|
509
605
|
return this.#memoryStore;
|
|
510
606
|
}
|
|
607
|
+
|
|
608
|
+
/** @returns {import('./tools/registry.js').ToolRegistry|null} */
|
|
609
|
+
get toolRegistry() { return this.#toolRegistry; }
|
|
610
|
+
|
|
611
|
+
/** @returns {import('./skills.js').SkillManager|null} */
|
|
612
|
+
get skillManager() { return this.#skillManager; }
|
|
613
|
+
|
|
614
|
+
/** @returns {import('./mcp.js').MCPManager|null} */
|
|
615
|
+
get mcpManager() { return this.#mcpManager; }
|
|
616
|
+
|
|
617
|
+
/** @returns {string|null} */
|
|
618
|
+
get yeaftDir() { return this.#yeaftDir; }
|
|
511
619
|
}
|
package/unify/index.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
export { initYeaftDir, DEFAULT_YEAFT_DIR } from './init.js';
|
|
8
|
-
export { loadConfig, parseFrontmatter } from './config.js';
|
|
8
|
+
export { loadConfig, parseFrontmatter, loadMCPConfig } from './config.js';
|
|
9
9
|
export { DebugTrace, NullTrace, createTrace } from './debug-trace.js';
|
|
10
10
|
export {
|
|
11
11
|
LLMAdapter,
|
|
@@ -36,4 +36,5 @@ export { MCPManager, createMCPManager } from './mcp.js';
|
|
|
36
36
|
export { SkillManager, createSkillManager, parseSkill, serializeSkill } from './skills.js';
|
|
37
37
|
export { defineTool } from './tools/types.js';
|
|
38
38
|
export { ToolRegistry, createEmptyRegistry } from './tools/registry.js';
|
|
39
|
+
export { loadSession } from './session.js';
|
|
39
40
|
|
package/unify/prompts.js
CHANGED
|
@@ -61,6 +61,7 @@ export function buildSystemPrompt({
|
|
|
61
61
|
toolNames = [],
|
|
62
62
|
memory,
|
|
63
63
|
compactSummary,
|
|
64
|
+
skillContent,
|
|
64
65
|
} = {}) {
|
|
65
66
|
// Fallback to English for unknown languages
|
|
66
67
|
const lang = PROMPTS[language] || PROMPTS.en;
|
|
@@ -81,6 +82,11 @@ export function buildSystemPrompt({
|
|
|
81
82
|
parts.push(lang.tools(toolNames.join(', ')));
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
// ─── Skills Section ─────────────────────────────────────
|
|
86
|
+
if (skillContent) {
|
|
87
|
+
parts.push(skillContent);
|
|
88
|
+
}
|
|
89
|
+
|
|
84
90
|
// ─── Memory Section ─────────────────────────────────────
|
|
85
91
|
if (memory && (memory.profile || (memory.entries && memory.entries.length > 0))) {
|
|
86
92
|
const memoryParts = [lang.memoryHeader];
|
package/unify/session.js
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session.js — Session orchestrator for Yeaft Unify
|
|
3
|
+
*
|
|
4
|
+
* Single entry point: loadSession(options?) → Session
|
|
5
|
+
*
|
|
6
|
+
* Wires all subsystems together:
|
|
7
|
+
* initYeaftDir → loadConfig → createTrace → createLLMAdapter →
|
|
8
|
+
* ConversationStore → MemoryStore → SkillManager → MCPManager →
|
|
9
|
+
* ToolRegistry → Engine → Session
|
|
10
|
+
*
|
|
11
|
+
* The ~/.yeaft/ directory is the agent's persistent workspace.
|
|
12
|
+
* loadSession() loads (or initializes) this workspace and returns
|
|
13
|
+
* a fully wired Session ready for queries.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { initYeaftDir } from './init.js';
|
|
17
|
+
import { loadConfig, loadMCPConfig } from './config.js';
|
|
18
|
+
import { createTrace } from './debug-trace.js';
|
|
19
|
+
import { createLLMAdapter } from './llm/adapter.js';
|
|
20
|
+
import { ConversationStore } from './conversation/persist.js';
|
|
21
|
+
import { MemoryStore } from './memory/store.js';
|
|
22
|
+
import { SkillManager, createSkillManager } from './skills.js';
|
|
23
|
+
import { MCPManager } from './mcp.js';
|
|
24
|
+
import { createEmptyRegistry } from './tools/registry.js';
|
|
25
|
+
import { Engine } from './engine.js';
|
|
26
|
+
import { join } from 'path';
|
|
27
|
+
|
|
28
|
+
// Built-in tools
|
|
29
|
+
import mcpTools from './tools/mcp-tools.js';
|
|
30
|
+
import skillTool from './tools/skill.js';
|
|
31
|
+
import enterWorktree from './tools/enter-worktree.js';
|
|
32
|
+
import exitWorktree from './tools/exit-worktree.js';
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {Object} SessionOptions
|
|
36
|
+
* @property {string} [dir] — Yeaft data directory override (default: ~/.yeaft)
|
|
37
|
+
* @property {string} [model] — Model override
|
|
38
|
+
* @property {string} [language] — Language override ('en' | 'zh')
|
|
39
|
+
* @property {boolean} [debug] — Debug mode override
|
|
40
|
+
* @property {boolean} [skipMCP] — Skip MCP server connections (faster startup)
|
|
41
|
+
* @property {boolean} [skipSkills] — Skip skill loading
|
|
42
|
+
* @property {object[]} [extraTools] — Additional ToolDef objects to register
|
|
43
|
+
* @property {object} [configOverrides] — Additional config overrides
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @typedef {Object} Session
|
|
48
|
+
* @property {Engine} engine — The wired engine, ready for .query()
|
|
49
|
+
* @property {object} config — Resolved configuration
|
|
50
|
+
* @property {ConversationStore} conversationStore — Conversation persistence
|
|
51
|
+
* @property {MemoryStore} memoryStore — Memory persistence
|
|
52
|
+
* @property {SkillManager} skillManager — Skill manager
|
|
53
|
+
* @property {MCPManager} mcpManager — MCP manager
|
|
54
|
+
* @property {import('./tools/registry.js').ToolRegistry} toolRegistry — Tool registry
|
|
55
|
+
* @property {import('./debug-trace.js').DebugTrace|import('./debug-trace.js').NullTrace} trace
|
|
56
|
+
* @property {string} yeaftDir — Resolved data directory path
|
|
57
|
+
* @property {{ skills: number, mcpServers: string[], mcpFailed: object[], tools: number }} status
|
|
58
|
+
* @property {() => Promise<void>} shutdown — Graceful shutdown
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Load (or initialize) a Yeaft session.
|
|
63
|
+
*
|
|
64
|
+
* This is the main entry point for using Yeaft programmatically.
|
|
65
|
+
* It creates the directory structure if needed, loads config, connects
|
|
66
|
+
* to services, registers tools, and returns a ready-to-use Session.
|
|
67
|
+
*
|
|
68
|
+
* @param {SessionOptions} [options={}]
|
|
69
|
+
* @returns {Promise<Session>}
|
|
70
|
+
*/
|
|
71
|
+
export async function loadSession(options = {}) {
|
|
72
|
+
const {
|
|
73
|
+
dir,
|
|
74
|
+
model,
|
|
75
|
+
language,
|
|
76
|
+
debug,
|
|
77
|
+
skipMCP = false,
|
|
78
|
+
skipSkills = false,
|
|
79
|
+
extraTools = [],
|
|
80
|
+
configOverrides = {},
|
|
81
|
+
} = options;
|
|
82
|
+
|
|
83
|
+
// ─── 1. Load config (determines yeaftDir) ──────────────
|
|
84
|
+
const overrides = { ...configOverrides };
|
|
85
|
+
if (dir) overrides.dir = dir;
|
|
86
|
+
if (model) overrides.model = model;
|
|
87
|
+
if (language) overrides.language = language;
|
|
88
|
+
if (debug !== undefined) overrides.debug = debug;
|
|
89
|
+
|
|
90
|
+
const config = loadConfig(overrides);
|
|
91
|
+
const yeaftDir = config.dir;
|
|
92
|
+
|
|
93
|
+
// ─── 2. Ensure directory structure ─────────────────────
|
|
94
|
+
initYeaftDir(yeaftDir);
|
|
95
|
+
|
|
96
|
+
// ─── 3. Create debug trace ─────────────────────────────
|
|
97
|
+
const trace = createTrace({
|
|
98
|
+
enabled: config.debug,
|
|
99
|
+
dbPath: join(yeaftDir, 'debug.db'),
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// ─── 4. Create LLM adapter ────────────────────────────
|
|
103
|
+
const adapter = await createLLMAdapter(config);
|
|
104
|
+
|
|
105
|
+
// ─── 5. Create stores ──────────────────────────────────
|
|
106
|
+
const conversationStore = new ConversationStore(yeaftDir);
|
|
107
|
+
const memoryStore = new MemoryStore(yeaftDir);
|
|
108
|
+
|
|
109
|
+
// ─── 6. Load skills ────────────────────────────────────
|
|
110
|
+
let skillManager;
|
|
111
|
+
if (skipSkills) {
|
|
112
|
+
skillManager = new SkillManager(yeaftDir);
|
|
113
|
+
// Don't call .load() — empty skill manager
|
|
114
|
+
} else {
|
|
115
|
+
skillManager = createSkillManager(yeaftDir);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ─── 7. Connect MCP servers ────────────────────────────
|
|
119
|
+
const mcpConfig = loadMCPConfig(yeaftDir);
|
|
120
|
+
const mcpManager = new MCPManager();
|
|
121
|
+
let mcpStatus = { connected: [], failed: [] };
|
|
122
|
+
|
|
123
|
+
if (!skipMCP && mcpConfig.servers.length > 0) {
|
|
124
|
+
mcpStatus = await mcpManager.connectAll(mcpConfig.servers);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ─── 8. Build tool registry ────────────────────────────
|
|
128
|
+
const toolRegistry = createEmptyRegistry();
|
|
129
|
+
|
|
130
|
+
// Register built-in tools
|
|
131
|
+
for (const tool of mcpTools) {
|
|
132
|
+
toolRegistry.register(tool);
|
|
133
|
+
}
|
|
134
|
+
toolRegistry.register(skillTool);
|
|
135
|
+
toolRegistry.register(enterWorktree);
|
|
136
|
+
toolRegistry.register(exitWorktree);
|
|
137
|
+
|
|
138
|
+
// Register any extra tools from caller
|
|
139
|
+
for (const tool of extraTools) {
|
|
140
|
+
toolRegistry.register(tool);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ─── 9. Create engine (wires everything) ───────────────
|
|
144
|
+
const engine = new Engine({
|
|
145
|
+
adapter,
|
|
146
|
+
trace,
|
|
147
|
+
config,
|
|
148
|
+
conversationStore,
|
|
149
|
+
memoryStore,
|
|
150
|
+
toolRegistry,
|
|
151
|
+
skillManager,
|
|
152
|
+
mcpManager,
|
|
153
|
+
yeaftDir,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// ─── 10. Build session ─────────────────────────────────
|
|
157
|
+
const status = {
|
|
158
|
+
skills: skillManager.size,
|
|
159
|
+
mcpServers: mcpStatus.connected,
|
|
160
|
+
mcpFailed: mcpStatus.failed,
|
|
161
|
+
tools: toolRegistry.size,
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/** Graceful shutdown: disconnect MCP, close trace DB. */
|
|
165
|
+
async function shutdown() {
|
|
166
|
+
try {
|
|
167
|
+
await mcpManager.disconnectAll();
|
|
168
|
+
} catch {
|
|
169
|
+
// Best-effort cleanup
|
|
170
|
+
}
|
|
171
|
+
try {
|
|
172
|
+
trace.close();
|
|
173
|
+
} catch {
|
|
174
|
+
// Trace might not have close() (NullTrace)
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
engine,
|
|
180
|
+
config,
|
|
181
|
+
conversationStore,
|
|
182
|
+
memoryStore,
|
|
183
|
+
skillManager,
|
|
184
|
+
mcpManager,
|
|
185
|
+
toolRegistry,
|
|
186
|
+
trace,
|
|
187
|
+
yeaftDir,
|
|
188
|
+
status,
|
|
189
|
+
shutdown,
|
|
190
|
+
};
|
|
191
|
+
}
|