hyacinth-ai 0.9.8 → 0.9.9
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/gateway/factory.js +1 -1
- package/dist/gateway/tui.js +12 -0
- package/dist/hot-reload/tool-watcher.js +24 -6
- package/dist/prompts/persona/PartnerSoul.md +2 -1
- package/null +0 -0
- package/package.json +1 -1
package/dist/gateway/factory.js
CHANGED
|
@@ -255,7 +255,7 @@ export async function createAgent(options, supervisor) {
|
|
|
255
255
|
description: '会话临时工具',
|
|
256
256
|
getContent: () => {
|
|
257
257
|
const names = toolRegistry.getHotAddedNames();
|
|
258
|
-
return names.length > 0 ? `[
|
|
258
|
+
return names.length > 0 ? `[Hot-added tools (session-scoped, additional to standard tools)]\n${names.join(', ')}` : '';
|
|
259
259
|
},
|
|
260
260
|
});
|
|
261
261
|
const channelRegistry = new ModelChannelRegistry(cwd);
|
package/dist/gateway/tui.js
CHANGED
|
@@ -1415,6 +1415,10 @@ export async function runTui(provider, sessionId, shouldContinue, maxTurns, maxC
|
|
|
1415
1415
|
// 就地切换,不重启
|
|
1416
1416
|
await loop.switchSession(dir);
|
|
1417
1417
|
sessionDir = dir;
|
|
1418
|
+
lastTurnCount = 0;
|
|
1419
|
+
lastTokensUsed = 0;
|
|
1420
|
+
const newStats = await statsManager.get(dir);
|
|
1421
|
+
refreshStatus(loop.getTurnInfo(newStats.turn_count ?? 0, newStats.current_context_tokens ?? 0));
|
|
1418
1422
|
chatLog.clearAll();
|
|
1419
1423
|
replayEvents(chatLog, dir);
|
|
1420
1424
|
chatLog.addSystem(theme.success(`已切换到 session ${sessionId}`));
|
|
@@ -2460,6 +2464,10 @@ export async function runTui(provider, sessionId, shouldContinue, maxTurns, maxC
|
|
|
2460
2464
|
await loop.switchSession(newSessionDir);
|
|
2461
2465
|
sessionDir = newSessionDir;
|
|
2462
2466
|
preciseModeActive = true;
|
|
2467
|
+
lastTurnCount = 0;
|
|
2468
|
+
lastTokensUsed = 0;
|
|
2469
|
+
const psStats = await statsManager.get(newSessionDir);
|
|
2470
|
+
refreshStatus(loop.getTurnInfo(psStats.turn_count ?? 0, psStats.current_context_tokens ?? 0));
|
|
2463
2471
|
const label = existingPrecise ? '恢复已有' : '新建';
|
|
2464
2472
|
chatLog.addSystem(theme.success(`精确模式已开启 — ${label} session: ${newSession.id}`));
|
|
2465
2473
|
chatLog.clearAll();
|
|
@@ -2478,6 +2486,10 @@ export async function runTui(provider, sessionId, shouldContinue, maxTurns, maxC
|
|
|
2478
2486
|
await loop.switchSession(originalSessionDir);
|
|
2479
2487
|
sessionDir = originalSessionDir;
|
|
2480
2488
|
originalSessionDir = null;
|
|
2489
|
+
lastTurnCount = 0;
|
|
2490
|
+
lastTokensUsed = 0;
|
|
2491
|
+
const origStats = await statsManager.get(sessionDir);
|
|
2492
|
+
refreshStatus(loop.getTurnInfo(origStats.turn_count ?? 0, origStats.current_context_tokens ?? 0));
|
|
2481
2493
|
chatLog.clearAll();
|
|
2482
2494
|
replayEvents(chatLog, sessionDir);
|
|
2483
2495
|
}
|
|
@@ -20,8 +20,9 @@ export function watchTools(deps) {
|
|
|
20
20
|
}
|
|
21
21
|
catch { /* ignore */ }
|
|
22
22
|
const fileMap = new Map();
|
|
23
|
+
const mtimeMap = new Map();
|
|
23
24
|
// 初始扫描不标记 hotAdded — 工具应进入 Zone 2 tool_rules
|
|
24
|
-
scanAndSync(toolsDir, toolRegistry, fileMap, true);
|
|
25
|
+
scanAndSync(toolsDir, toolRegistry, fileMap, mtimeMap, true);
|
|
25
26
|
let debounceTimer = null;
|
|
26
27
|
logger.info(`Watching tools directory: ${toolsDir}`);
|
|
27
28
|
const watcher = fs.watch(toolsDir, { recursive: true }, (_eventType, filename) => {
|
|
@@ -32,20 +33,32 @@ export function watchTools(deps) {
|
|
|
32
33
|
if (debounceTimer)
|
|
33
34
|
clearTimeout(debounceTimer);
|
|
34
35
|
// 文件变更触发的重新扫描 — 此时才标记 hotAdded,工具进 Zone 5 session-tools
|
|
35
|
-
debounceTimer = setTimeout(() => scanAndSync(toolsDir, toolRegistry, fileMap, false), debounceMs);
|
|
36
|
+
debounceTimer = setTimeout(() => scanAndSync(toolsDir, toolRegistry, fileMap, mtimeMap, false), debounceMs);
|
|
36
37
|
});
|
|
37
38
|
watcher.on('error', (err) => {
|
|
38
39
|
logger.warn(`Tool watcher error: ${err.message}`, { error: err.message });
|
|
39
40
|
});
|
|
40
41
|
return watcher;
|
|
41
42
|
}
|
|
42
|
-
async function scanAndSync(dir, registry, fileMap, isInitialScan) {
|
|
43
|
+
async function scanAndSync(dir, registry, fileMap, mtimeMap, isInitialScan) {
|
|
43
44
|
const resolved = resolveToolEntries(dir);
|
|
44
45
|
const currentKeys = new Set(resolved.map((r) => r.trackingKey));
|
|
45
46
|
for (const entry of resolved) {
|
|
46
47
|
const knownToolName = fileMap.get(entry.trackingKey);
|
|
47
48
|
if (knownToolName !== undefined) {
|
|
48
|
-
// 已跟踪 →
|
|
49
|
+
// 已跟踪 → 检查文件是否真的变了,避免 fs.watch 误触发导致全部标记为 hot-added
|
|
50
|
+
let mtime;
|
|
51
|
+
try {
|
|
52
|
+
mtime = fs.statSync(entry.filePath).mtimeMs;
|
|
53
|
+
}
|
|
54
|
+
catch { /* stat 失败则走重载 */ }
|
|
55
|
+
if (mtime !== undefined) {
|
|
56
|
+
const prevMtime = mtimeMap.get(entry.trackingKey);
|
|
57
|
+
if (prevMtime === mtime)
|
|
58
|
+
continue; // 文件没变,跳过
|
|
59
|
+
mtimeMap.set(entry.trackingKey, mtime);
|
|
60
|
+
}
|
|
61
|
+
// 重新加载(文件变更触发的热重载)
|
|
49
62
|
if (entry.factory) {
|
|
50
63
|
// Python 工具:不再解析
|
|
51
64
|
const tool = entry.factory();
|
|
@@ -79,13 +92,14 @@ async function scanAndSync(dir, registry, fileMap, isInitialScan) {
|
|
|
79
92
|
}
|
|
80
93
|
}
|
|
81
94
|
else {
|
|
82
|
-
await loadAndRegister(entry, registry, fileMap, isInitialScan);
|
|
95
|
+
await loadAndRegister(entry, registry, fileMap, mtimeMap, isInitialScan);
|
|
83
96
|
}
|
|
84
97
|
}
|
|
85
98
|
for (const [key, toolName] of fileMap.entries()) {
|
|
86
99
|
if (!currentKeys.has(key)) {
|
|
87
100
|
registry.unregister(toolName);
|
|
88
101
|
fileMap.delete(key);
|
|
102
|
+
mtimeMap.delete(key);
|
|
89
103
|
logger.info(`Tool unregistered: ${toolName} (${key})`);
|
|
90
104
|
}
|
|
91
105
|
}
|
|
@@ -138,7 +152,7 @@ function resolveToolEntries(dir) {
|
|
|
138
152
|
}
|
|
139
153
|
return entries;
|
|
140
154
|
}
|
|
141
|
-
async function loadAndRegister(entry, registry, fileMap, isInitialScan) {
|
|
155
|
+
async function loadAndRegister(entry, registry, fileMap, mtimeMap, isInitialScan) {
|
|
142
156
|
try {
|
|
143
157
|
let tool;
|
|
144
158
|
if (entry.factory) {
|
|
@@ -159,6 +173,10 @@ async function loadAndRegister(entry, registry, fileMap, isInitialScan) {
|
|
|
159
173
|
if (!isInitialScan)
|
|
160
174
|
registry.markHotAdded(tool.name);
|
|
161
175
|
fileMap.set(entry.trackingKey, tool.name);
|
|
176
|
+
try {
|
|
177
|
+
mtimeMap.set(entry.trackingKey, fs.statSync(entry.filePath).mtimeMs);
|
|
178
|
+
}
|
|
179
|
+
catch { }
|
|
162
180
|
logger.info(`Tool registered: ${tool.name} (${entry.label})`);
|
|
163
181
|
}
|
|
164
182
|
catch (err) {
|
package/null
ADDED
|
File without changes
|