tuna-agent 0.1.55 → 0.1.57
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.
|
@@ -32,6 +32,7 @@ export declare class ClaudeCodeAdapter implements AgentAdapter {
|
|
|
32
32
|
private static readonly PATTERN_CHECK_INTERVAL;
|
|
33
33
|
private metricsMap;
|
|
34
34
|
private learnedRulesMap;
|
|
35
|
+
private agentFolderMap;
|
|
35
36
|
private currentAgentId;
|
|
36
37
|
/** Folder basename of current agent (e.g. "co-founder"). Used as Mem0 user_id. */
|
|
37
38
|
private currentAgentName;
|
|
@@ -40,6 +41,8 @@ export declare class ClaudeCodeAdapter implements AgentAdapter {
|
|
|
40
41
|
constructor(config: AgentConfig);
|
|
41
42
|
/** Get all per-agent metrics for heartbeat. */
|
|
42
43
|
getMetrics(): Record<string, unknown>;
|
|
44
|
+
/** Register an agent folder path for rules parsing (called from daemon). */
|
|
45
|
+
registerAgentFolder(agentId: string, folderPath: string): void;
|
|
43
46
|
checkHealth(): Promise<{
|
|
44
47
|
ok: boolean;
|
|
45
48
|
message: string;
|
|
@@ -17,6 +17,7 @@ export class ClaudeCodeAdapter {
|
|
|
17
17
|
// Per-agent state (one daemon handles multiple agents)
|
|
18
18
|
metricsMap = new Map();
|
|
19
19
|
learnedRulesMap = new Map();
|
|
20
|
+
agentFolderMap = new Map(); // agentId -> folder path
|
|
20
21
|
currentAgentId = '';
|
|
21
22
|
/** Folder basename of current agent (e.g. "co-founder"). Used as Mem0 user_id. */
|
|
22
23
|
currentAgentName = '';
|
|
@@ -48,6 +49,13 @@ export class ClaudeCodeAdapter {
|
|
|
48
49
|
}
|
|
49
50
|
/** Get all per-agent metrics for heartbeat. */
|
|
50
51
|
getMetrics() {
|
|
52
|
+
// Re-parse rules from CLAUDE.md for all known agents
|
|
53
|
+
this.agentFolderMap.forEach((folder, agentId) => {
|
|
54
|
+
const savedId = this.currentAgentId;
|
|
55
|
+
this.currentAgentId = agentId;
|
|
56
|
+
this.parseLearnedRules(path.join(folder, 'CLAUDE.md'));
|
|
57
|
+
this.currentAgentId = savedId;
|
|
58
|
+
});
|
|
51
59
|
const agentMetricsMap = {};
|
|
52
60
|
this.metricsMap.forEach((m, agentId) => {
|
|
53
61
|
const entry = { ...m };
|
|
@@ -59,6 +67,10 @@ export class ClaudeCodeAdapter {
|
|
|
59
67
|
});
|
|
60
68
|
return { agentMetricsMap };
|
|
61
69
|
}
|
|
70
|
+
/** Register an agent folder path for rules parsing (called from daemon). */
|
|
71
|
+
registerAgentFolder(agentId, folderPath) {
|
|
72
|
+
this.agentFolderMap.set(agentId, folderPath);
|
|
73
|
+
}
|
|
62
74
|
async checkHealth() {
|
|
63
75
|
try {
|
|
64
76
|
execSync('which claude', { stdio: 'ignore' });
|
|
@@ -97,11 +109,9 @@ export class ClaudeCodeAdapter {
|
|
|
97
109
|
this.currentAgentId = task.agentId || '';
|
|
98
110
|
const defaultWorkspaceEarly = path.join(os.homedir(), 'tuna-workspace');
|
|
99
111
|
this.currentAgentName = path.basename(task.repoPath || defaultWorkspaceEarly);
|
|
100
|
-
//
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
this.parseLearnedRules(path.join(cwd, 'CLAUDE.md'));
|
|
104
|
-
}
|
|
112
|
+
// Track agent folder for rules parsing in heartbeat
|
|
113
|
+
const cwd = task.repoPath || defaultWorkspaceEarly;
|
|
114
|
+
this.agentFolderMap.set(this.currentAgentId, cwd);
|
|
105
115
|
if (task.mode !== 'tuna') {
|
|
106
116
|
console.log(`[ClaudeCode] Agent Team mode — direct chat with Claude CLI`);
|
|
107
117
|
ws.sendProgress(task.id, 'executing', { startedAt: new Date().toISOString() });
|
|
@@ -885,20 +895,35 @@ export class ClaudeCodeAdapter {
|
|
|
885
895
|
const rulesText = nextSection !== -1 ? sectionContent.substring(0, nextSection) : sectionContent;
|
|
886
896
|
const rules = [];
|
|
887
897
|
const ruleRegex = /^- (.+?)(?:\s*\(confidence:\s*([\d.]+)\))?$/;
|
|
898
|
+
// First pass: collect raw confidence values
|
|
899
|
+
const rawEntries = [];
|
|
888
900
|
for (const line of rulesText.split('\n')) {
|
|
889
901
|
const trimmed = line.trim();
|
|
890
902
|
if (!trimmed || !trimmed.startsWith('-'))
|
|
891
903
|
continue;
|
|
892
904
|
const match = ruleRegex.exec(trimmed);
|
|
893
905
|
if (match) {
|
|
894
|
-
|
|
906
|
+
rawEntries.push({
|
|
895
907
|
content: match[1].trim(),
|
|
896
|
-
|
|
897
|
-
sourceCount: 1,
|
|
898
|
-
createdAt: new Date().toISOString(),
|
|
908
|
+
rawConf: match[2] ? parseFloat(match[2]) : 0.3,
|
|
899
909
|
});
|
|
900
910
|
}
|
|
901
911
|
}
|
|
912
|
+
// Normalize confidence: if any value > 1, treat as source_count and scale to 0-1
|
|
913
|
+
const maxConf = Math.max(...rawEntries.map(e => e.rawConf), 1);
|
|
914
|
+
const needsNormalization = maxConf > 1;
|
|
915
|
+
for (const entry of rawEntries) {
|
|
916
|
+
const sourceCount = needsNormalization ? Math.round(entry.rawConf) : 1;
|
|
917
|
+
const confidence = needsNormalization
|
|
918
|
+
? Math.min(Math.max(entry.rawConf / maxConf, 0.1), 1.0)
|
|
919
|
+
: entry.rawConf;
|
|
920
|
+
rules.push({
|
|
921
|
+
content: entry.content,
|
|
922
|
+
confidence: Math.round(confidence * 100) / 100,
|
|
923
|
+
sourceCount,
|
|
924
|
+
createdAt: new Date().toISOString(),
|
|
925
|
+
});
|
|
926
|
+
}
|
|
902
927
|
if (rules.length > 0) {
|
|
903
928
|
this.learnedRulesMap.set(this.currentAgentId, rules);
|
|
904
929
|
this.metrics.rulesCount = rules.length;
|
package/dist/daemon/index.js
CHANGED
|
@@ -181,6 +181,19 @@ export async function startDaemon(config) {
|
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
183
|
}
|
|
184
|
+
// Register agent folders for rules parsing from server-provided list
|
|
185
|
+
if (Array.isArray(msg.agentFolders) && adapter.type === 'claude-code') {
|
|
186
|
+
const ccAdapter = adapter;
|
|
187
|
+
for (const af of msg.agentFolders) {
|
|
188
|
+
if (af.id && af.folder_path) {
|
|
189
|
+
const folder = af.folder_path.startsWith('~')
|
|
190
|
+
? path.join(os.homedir(), af.folder_path.slice(1))
|
|
191
|
+
: af.folder_path;
|
|
192
|
+
ccAdapter.registerAgentFolder(af.id, folder);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
console.log(`[Daemon] Registered ${msg.agentFolders.length} agent folder(s) for rules sync`);
|
|
196
|
+
}
|
|
184
197
|
// Recover orphaned tasks — pass activeTaskId so API won't fail a task we're still running
|
|
185
198
|
ws.send({ action: 'recover_orphaned_tasks', activeTaskId: currentTaskId ?? undefined });
|
|
186
199
|
break;
|
|
@@ -264,6 +277,10 @@ export async function startDaemon(config) {
|
|
|
264
277
|
? path.join(os.homedir(), rawFolder.slice(1))
|
|
265
278
|
: rawFolder;
|
|
266
279
|
console.log(`[Daemon] Rescan skills for agent ${agentId}, folder: ${folder || '(none)'}`);
|
|
280
|
+
// Register folder for rules parsing in heartbeat
|
|
281
|
+
if (folder && agentId && adapter.type === 'claude-code') {
|
|
282
|
+
adapter.registerAgentFolder(agentId, folder);
|
|
283
|
+
}
|
|
267
284
|
const folders = folder ? [folder] : [];
|
|
268
285
|
const skills = scanSkills(wsPath, folders);
|
|
269
286
|
ws.send({ action: 'agent_skills_scanned', agent_id: agentId, skills });
|
|
@@ -297,6 +314,10 @@ export async function startDaemon(config) {
|
|
|
297
314
|
else {
|
|
298
315
|
console.log(`[Daemon] .claude/CLAUDE.md already exists, skipping`);
|
|
299
316
|
}
|
|
317
|
+
// Register folder for rules parsing in heartbeat
|
|
318
|
+
if (adapter.type === 'claude-code') {
|
|
319
|
+
adapter.registerAgentFolder(agentId, folderPath);
|
|
320
|
+
}
|
|
300
321
|
ws.send({ action: 'agent_folder_created', agent_id: agentId, success: true });
|
|
301
322
|
console.log(`[Daemon] Agent folder created: ${folderPath}`);
|
|
302
323
|
}
|