agentk8 2.2.6 → 2.3.1
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/cli.js +1 -1
- package/dist/components/App.js +322 -62
- package/dist/components/ChatMessage.js +93 -1
- package/dist/components/Confirmation.d.ts +13 -0
- package/dist/components/Confirmation.js +39 -0
- package/dist/components/QuestionWizard.d.ts +21 -0
- package/dist/components/QuestionWizard.js +152 -0
- package/dist/components/StatusBar.d.ts +6 -0
- package/dist/components/StatusBar.js +31 -7
- package/dist/components/WelcomeBox.js +17 -11
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.js +1 -0
- package/dist/lib/claude.js +59 -22
- package/dist/lib/council.d.ts +61 -0
- package/dist/lib/council.js +189 -0
- package/package.json +2 -2
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Council Integration for AGENT-K
|
|
3
|
+
*
|
|
4
|
+
* Connects TypeScript UI to Python council backend.
|
|
5
|
+
* Supports two modes:
|
|
6
|
+
* - Council: Multi-LLM via LiteLLM (GPT, Gemini, Claude)
|
|
7
|
+
* - Solo: Multi-Claude CLI instances with personas
|
|
8
|
+
*/
|
|
9
|
+
import { spawn } from 'child_process';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
13
|
+
const __dirname = path.dirname(__filename);
|
|
14
|
+
/**
|
|
15
|
+
* Run the council process.
|
|
16
|
+
*
|
|
17
|
+
* @param query User's query
|
|
18
|
+
* @param options Council options
|
|
19
|
+
* @param onStage Callback for stage updates
|
|
20
|
+
* @returns Promise resolving to final result
|
|
21
|
+
*/
|
|
22
|
+
export function runCouncil(query, options = {}, onStage) {
|
|
23
|
+
const { mode = 'council', skipScout = false, projectRoot, timeout = 300000, // 5 minutes
|
|
24
|
+
} = options;
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
// Build Python command
|
|
27
|
+
const pythonArgs = [
|
|
28
|
+
'-m', 'agentk',
|
|
29
|
+
'--mode', mode,
|
|
30
|
+
'--json',
|
|
31
|
+
];
|
|
32
|
+
if (skipScout) {
|
|
33
|
+
pythonArgs.push('--skip-scout');
|
|
34
|
+
}
|
|
35
|
+
if (projectRoot) {
|
|
36
|
+
pythonArgs.push('--project', projectRoot);
|
|
37
|
+
}
|
|
38
|
+
pythonArgs.push(query);
|
|
39
|
+
// Get the Python directory
|
|
40
|
+
const pythonDir = path.resolve(__dirname, '../../python');
|
|
41
|
+
const python = spawn('python3', pythonArgs, {
|
|
42
|
+
cwd: pythonDir,
|
|
43
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
44
|
+
env: {
|
|
45
|
+
...process.env,
|
|
46
|
+
PYTHONPATH: pythonDir,
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
let stdout = '';
|
|
50
|
+
let stderr = '';
|
|
51
|
+
let resolved = false;
|
|
52
|
+
let buffer = '';
|
|
53
|
+
const timeoutId = setTimeout(() => {
|
|
54
|
+
if (!resolved) {
|
|
55
|
+
resolved = true;
|
|
56
|
+
python.kill();
|
|
57
|
+
reject(new Error('Council request timed out'));
|
|
58
|
+
}
|
|
59
|
+
}, timeout);
|
|
60
|
+
python.stdout?.on('data', (data) => {
|
|
61
|
+
const chunk = data.toString();
|
|
62
|
+
stdout += chunk;
|
|
63
|
+
buffer += chunk;
|
|
64
|
+
// Handle stream fragmentation
|
|
65
|
+
const lines = buffer.split('\n');
|
|
66
|
+
// Keep the last partial line in the buffer
|
|
67
|
+
buffer = lines.pop() || '';
|
|
68
|
+
for (const line of lines) {
|
|
69
|
+
if (!line.trim())
|
|
70
|
+
continue;
|
|
71
|
+
try {
|
|
72
|
+
const update = JSON.parse(line);
|
|
73
|
+
if (update.stage && onStage) {
|
|
74
|
+
onStage(update);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Ignore incomplete or non-JSON lines during streaming
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
python.stderr?.on('data', (data) => {
|
|
83
|
+
stderr += data.toString();
|
|
84
|
+
});
|
|
85
|
+
python.on('close', (code) => {
|
|
86
|
+
clearTimeout(timeoutId);
|
|
87
|
+
if (resolved)
|
|
88
|
+
return;
|
|
89
|
+
resolved = true;
|
|
90
|
+
if (code !== 0 && !stdout) {
|
|
91
|
+
reject(new Error(stderr || `Council exited with code ${code}`));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
try {
|
|
95
|
+
// Find the last valid JSON object (the final result)
|
|
96
|
+
const lines = stdout.split('\n').filter(Boolean).reverse();
|
|
97
|
+
for (const line of lines) {
|
|
98
|
+
try {
|
|
99
|
+
const result = JSON.parse(line);
|
|
100
|
+
if (result.final_response || result.council) {
|
|
101
|
+
// Handle wrapped result
|
|
102
|
+
const councilResult = result.council || result;
|
|
103
|
+
resolve(councilResult);
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// Try next line
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Fallback: try parsing entire stdout
|
|
112
|
+
const result = JSON.parse(stdout);
|
|
113
|
+
resolve(result.council || result);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
reject(new Error(`Failed to parse council output: ${stdout.slice(0, 200)}`));
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
python.on('error', (err) => {
|
|
120
|
+
clearTimeout(timeoutId);
|
|
121
|
+
if (resolved)
|
|
122
|
+
return;
|
|
123
|
+
resolved = true;
|
|
124
|
+
reject(new Error(`Failed to start council: ${err.message}`));
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Check if the council backend is available.
|
|
130
|
+
*/
|
|
131
|
+
export async function checkCouncilAvailable() {
|
|
132
|
+
return new Promise((resolve) => {
|
|
133
|
+
const python = spawn('python3', ['-c', 'import agentk; print("ok")'], {
|
|
134
|
+
stdio: 'ignore',
|
|
135
|
+
cwd: path.resolve(__dirname, '../../python'),
|
|
136
|
+
env: {
|
|
137
|
+
...process.env,
|
|
138
|
+
PYTHONPATH: path.resolve(__dirname, '../../python'),
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
python.on('close', (code) => resolve(code === 0));
|
|
142
|
+
python.on('error', () => resolve(false));
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Get available models status from the council backend.
|
|
147
|
+
*/
|
|
148
|
+
export async function getAvailableModels() {
|
|
149
|
+
return new Promise((resolve) => {
|
|
150
|
+
const pythonDir = path.resolve(__dirname, '../../python');
|
|
151
|
+
const python = spawn('python3', ['-c', `
|
|
152
|
+
import json
|
|
153
|
+
from agentk.llm import LLMClient
|
|
154
|
+
client = LLMClient()
|
|
155
|
+
print(json.dumps({k: v for k, v in client._available_models.items()}))
|
|
156
|
+
`], {
|
|
157
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
158
|
+
cwd: pythonDir,
|
|
159
|
+
env: {
|
|
160
|
+
...process.env,
|
|
161
|
+
PYTHONPATH: pythonDir,
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
let stdout = '';
|
|
165
|
+
python.stdout?.on('data', (data) => {
|
|
166
|
+
stdout += data.toString();
|
|
167
|
+
});
|
|
168
|
+
python.on('close', (code) => {
|
|
169
|
+
if (code === 0 && stdout) {
|
|
170
|
+
try {
|
|
171
|
+
resolve(JSON.parse(stdout.trim()));
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// Fall through
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
resolve({ claude: true, gpt: false, gemini: false });
|
|
179
|
+
});
|
|
180
|
+
python.on('error', () => {
|
|
181
|
+
resolve({ claude: true, gpt: false, gemini: false });
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
export default {
|
|
186
|
+
runCouncil,
|
|
187
|
+
checkCouncilAvailable,
|
|
188
|
+
getAvailableModels,
|
|
189
|
+
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentk8",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Multi-
|
|
3
|
+
"version": "2.3.1",
|
|
4
|
+
"description": "Multi-LLM Council Terminal Suite - Three-stage consensus with GPT, Gemini, and Claude",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|