jinzd-ai-cli 0.3.1 → 0.3.2
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/agent-client-OGXLZR5O.js +209 -0
- package/dist/chunk-4ZAZF4CF.js +293 -0
- package/dist/{chunk-QQXO4PNR.js → chunk-AJ26XC7E.js} +1 -1
- package/dist/{chunk-XHACHVCN.js → chunk-GBP4DNBU.js} +1 -1
- package/dist/{chunk-QVUOF3DK.js → chunk-UEOKNL7V.js} +1 -1
- package/dist/{hub-72MJ7DED.js → hub-ZTEJJ4WD.js} +34 -278
- package/dist/hub-server-P547YWUN.js +299 -0
- package/dist/index.js +33 -7
- package/dist/{run-tests-WG27TZRF.js → run-tests-6VOBLE77.js} +1 -1
- package/dist/{run-tests-JBQ7Z26S.js → run-tests-YDNIQ4L6.js} +1 -1
- package/dist/{server-RPCRQKGE.js → server-YFR3DO2P.js} +3 -3
- package/package.json +3 -1
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/hub/agent-client.ts
|
|
4
|
+
import WebSocket from "ws";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
var PASS_MARKER = "[PASS]";
|
|
7
|
+
async function joinHub(options, configManager, providers) {
|
|
8
|
+
const host = options.host ?? "localhost";
|
|
9
|
+
const url = `ws://${host}:${options.port}`;
|
|
10
|
+
const defaultProvider = options.provider ?? configManager.get("defaultProvider");
|
|
11
|
+
const allDefaultModels = configManager.get("defaultModels");
|
|
12
|
+
let defaultModel = options.model ?? allDefaultModels[defaultProvider] ?? "";
|
|
13
|
+
if (!defaultModel) {
|
|
14
|
+
try {
|
|
15
|
+
const p = providers.get(defaultProvider);
|
|
16
|
+
defaultModel = p.info.defaultModel;
|
|
17
|
+
} catch {
|
|
18
|
+
console.error(` \u2717 Provider "${defaultProvider}" not configured.`);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
console.log();
|
|
23
|
+
console.log(chalk.bold.cyan(" \u{1F916} AI-CLI Agent \u2014 Connecting to Hub"));
|
|
24
|
+
console.log(chalk.dim(` Server: ${url}`));
|
|
25
|
+
if (options.role) console.log(chalk.dim(` Requested role: ${options.role}`));
|
|
26
|
+
console.log(chalk.dim(` Provider: ${defaultProvider} / ${defaultModel}`));
|
|
27
|
+
console.log();
|
|
28
|
+
const ws = new WebSocket(url);
|
|
29
|
+
let myRoleId = "";
|
|
30
|
+
let myRoleName = "";
|
|
31
|
+
let topic = "";
|
|
32
|
+
let allRoles = [];
|
|
33
|
+
let persona = "";
|
|
34
|
+
ws.on("open", () => {
|
|
35
|
+
console.log(chalk.green(" \u2713 Connected to Hub"));
|
|
36
|
+
const msg = { type: "register", roleId: options.role ?? "" };
|
|
37
|
+
ws.send(JSON.stringify(msg));
|
|
38
|
+
});
|
|
39
|
+
ws.on("close", () => {
|
|
40
|
+
console.log(chalk.dim("\n Disconnected from Hub."));
|
|
41
|
+
process.exit(0);
|
|
42
|
+
});
|
|
43
|
+
ws.on("error", (err) => {
|
|
44
|
+
console.error(chalk.red(`
|
|
45
|
+
\u2717 Connection error: ${err.message}`));
|
|
46
|
+
process.exit(1);
|
|
47
|
+
});
|
|
48
|
+
ws.on("message", async (raw) => {
|
|
49
|
+
try {
|
|
50
|
+
const msg = JSON.parse(raw.toString());
|
|
51
|
+
await handleMessage(msg);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.error(chalk.red(` \u2717 Message error: ${err.message}`));
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
async function handleMessage(msg) {
|
|
57
|
+
switch (msg.type) {
|
|
58
|
+
case "welcome":
|
|
59
|
+
myRoleId = msg.roleId;
|
|
60
|
+
myRoleName = msg.roleName;
|
|
61
|
+
topic = msg.topic;
|
|
62
|
+
allRoles = msg.roles;
|
|
63
|
+
persona = buildPersonaFromRole(myRoleId, myRoleName);
|
|
64
|
+
console.log(chalk.bold(` \u{1F4CB} Assigned role: ${chalk.cyan(myRoleName)} (${myRoleId})`));
|
|
65
|
+
if (topic) {
|
|
66
|
+
console.log(chalk.dim(` Topic: ${topic}`));
|
|
67
|
+
}
|
|
68
|
+
console.log(chalk.dim(` Participants: ${allRoles.map((r) => r.name).join(", ")}`));
|
|
69
|
+
console.log(chalk.dim(" Waiting for discussion to start...\n"));
|
|
70
|
+
break;
|
|
71
|
+
case "round_start":
|
|
72
|
+
console.log(chalk.dim(` \u2500\u2500 Round ${msg.round}/${msg.maxRounds} ${"\u2500".repeat(40)}
|
|
73
|
+
`));
|
|
74
|
+
break;
|
|
75
|
+
case "your_turn": {
|
|
76
|
+
console.log(chalk.bold.cyan(` \u{1F4AD} Your turn to speak (Round ${msg.round}/${msg.maxRounds})...`));
|
|
77
|
+
try {
|
|
78
|
+
const response = await generateResponse(
|
|
79
|
+
topic,
|
|
80
|
+
msg.history,
|
|
81
|
+
msg.round,
|
|
82
|
+
msg.maxRounds,
|
|
83
|
+
myRoleId,
|
|
84
|
+
myRoleName,
|
|
85
|
+
persona,
|
|
86
|
+
providers,
|
|
87
|
+
defaultProvider,
|
|
88
|
+
defaultModel
|
|
89
|
+
);
|
|
90
|
+
const passed = response.toUpperCase().startsWith(PASS_MARKER) || response.toUpperCase() === PASS_MARKER;
|
|
91
|
+
if (passed) {
|
|
92
|
+
console.log(chalk.dim(" [PASS \u2014 nothing to add]\n"));
|
|
93
|
+
} else {
|
|
94
|
+
console.log(chalk.cyan(` \u250C\u2500 ${myRoleName} (you)`));
|
|
95
|
+
for (const line of response.split("\n")) {
|
|
96
|
+
console.log(chalk.cyan(" \u2502 ") + line);
|
|
97
|
+
}
|
|
98
|
+
console.log(chalk.cyan(" \u2514" + "\u2500".repeat(50)));
|
|
99
|
+
console.log();
|
|
100
|
+
}
|
|
101
|
+
const reply = {
|
|
102
|
+
type: "response",
|
|
103
|
+
content: passed ? "" : response,
|
|
104
|
+
passed
|
|
105
|
+
};
|
|
106
|
+
ws.send(JSON.stringify(reply));
|
|
107
|
+
} catch (err) {
|
|
108
|
+
console.error(chalk.red(` \u2717 Response error: ${err.message}`));
|
|
109
|
+
const errReply = { type: "error", message: err.message };
|
|
110
|
+
ws.send(JSON.stringify(errReply));
|
|
111
|
+
}
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
case "agent_spoke": {
|
|
115
|
+
const m = msg.message;
|
|
116
|
+
if (m.speaker === myRoleId) break;
|
|
117
|
+
if (m.passed) {
|
|
118
|
+
console.log(chalk.dim(` ${m.speakerName}: [PASS]
|
|
119
|
+
`));
|
|
120
|
+
} else {
|
|
121
|
+
console.log(chalk.magenta(` \u250C\u2500 ${m.speakerName} (${m.speaker})`));
|
|
122
|
+
for (const line of m.content.split("\n")) {
|
|
123
|
+
console.log(chalk.magenta(" \u2502 ") + line);
|
|
124
|
+
}
|
|
125
|
+
console.log(chalk.magenta(" \u2514" + "\u2500".repeat(50)));
|
|
126
|
+
console.log();
|
|
127
|
+
}
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
case "discussion_end":
|
|
131
|
+
console.log();
|
|
132
|
+
if (msg.reason === "consensus") {
|
|
133
|
+
console.log(chalk.green.bold(" \u2713 Consensus reached \u2014 discussion ended."));
|
|
134
|
+
} else if (msg.reason === "max_rounds") {
|
|
135
|
+
console.log(chalk.yellow(" \u26A0 Maximum rounds reached \u2014 discussion ended."));
|
|
136
|
+
} else {
|
|
137
|
+
console.log(chalk.yellow(" \u26A0 Discussion interrupted by hub operator."));
|
|
138
|
+
}
|
|
139
|
+
if (msg.summary) {
|
|
140
|
+
console.log();
|
|
141
|
+
console.log(chalk.bold(" \u{1F4CB} Summary:"));
|
|
142
|
+
for (const line of msg.summary.split("\n")) {
|
|
143
|
+
console.log(" " + line);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
console.log();
|
|
147
|
+
break;
|
|
148
|
+
case "waiting":
|
|
149
|
+
console.log(chalk.dim(` ${msg.message}`));
|
|
150
|
+
break;
|
|
151
|
+
case "error":
|
|
152
|
+
console.error(chalk.red(` \u2717 Hub error: ${msg.message}`));
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
await new Promise(() => {
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
async function generateResponse(topic, history, round, maxRounds, roleId, roleName, persona, providers, providerId, modelId) {
|
|
160
|
+
const provider = providers.get(providerId);
|
|
161
|
+
if (!provider) throw new Error(`Provider "${providerId}" not available`);
|
|
162
|
+
const systemPrompt = `# Multi-Agent Discussion \u2014 Role: ${roleName}
|
|
163
|
+
|
|
164
|
+
${persona}
|
|
165
|
+
|
|
166
|
+
## Discussion Rules
|
|
167
|
+
- You are participating in a multi-agent discussion about the topic below.
|
|
168
|
+
- You can see what other participants have said. Build on their ideas, challenge them, or add your own perspective.
|
|
169
|
+
- Stay in character as ${roleName}. Respond from your role's expertise and viewpoint.
|
|
170
|
+
- Keep responses concise and focused (2-6 paragraphs). Do not repeat what others have already said.
|
|
171
|
+
- If you have nothing meaningful to add (others have covered your points), respond with exactly: [PASS]
|
|
172
|
+
- This is round ${round} of ${maxRounds}. ${round >= maxRounds - 1 ? "This is one of the final rounds \u2014 try to converge on conclusions." : ""}
|
|
173
|
+
- Use the language that the topic is written in (if the topic is in Chinese, respond in Chinese).
|
|
174
|
+
|
|
175
|
+
## Topic
|
|
176
|
+
${topic}`;
|
|
177
|
+
const messages = [];
|
|
178
|
+
for (const msg of history) {
|
|
179
|
+
if (msg.passed) continue;
|
|
180
|
+
if (msg.speaker === roleId) {
|
|
181
|
+
messages.push({ role: "assistant", content: msg.content, timestamp: new Date(msg.timestamp) });
|
|
182
|
+
} else {
|
|
183
|
+
messages.push({ role: "user", content: `[${msg.speakerName} (${msg.speaker})]:
|
|
184
|
+
${msg.content}`, timestamp: new Date(msg.timestamp) });
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
if (messages.length === 0) {
|
|
188
|
+
messages.push({ role: "user", content: "Please share your initial thoughts on the topic. You are the first to speak.", timestamp: /* @__PURE__ */ new Date() });
|
|
189
|
+
} else {
|
|
190
|
+
messages.push({ role: "user", content: "It is now your turn. Consider what others have said and share your perspective. If you have nothing to add, respond with [PASS].", timestamp: /* @__PURE__ */ new Date() });
|
|
191
|
+
}
|
|
192
|
+
const response = await provider.chat({
|
|
193
|
+
messages,
|
|
194
|
+
model: modelId,
|
|
195
|
+
systemPrompt,
|
|
196
|
+
stream: false,
|
|
197
|
+
temperature: 0.7,
|
|
198
|
+
maxTokens: 4096
|
|
199
|
+
});
|
|
200
|
+
return response.content.trim();
|
|
201
|
+
}
|
|
202
|
+
function buildPersonaFromRole(roleId, roleName) {
|
|
203
|
+
return `You are ${roleName} (${roleId}). You are an expert in your domain.
|
|
204
|
+
Respond thoughtfully based on your area of expertise.
|
|
205
|
+
Be constructive, collaborative, and open to other perspectives while maintaining your professional viewpoint.`;
|
|
206
|
+
}
|
|
207
|
+
export {
|
|
208
|
+
joinHub
|
|
209
|
+
};
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/hub/agent.ts
|
|
4
|
+
var PASS_MARKER = "[PASS]";
|
|
5
|
+
var HubAgent = class {
|
|
6
|
+
role;
|
|
7
|
+
providers;
|
|
8
|
+
defaultProvider;
|
|
9
|
+
defaultModel;
|
|
10
|
+
constructor(role, providers, defaultProvider, defaultModel) {
|
|
11
|
+
this.role = role;
|
|
12
|
+
this.providers = providers;
|
|
13
|
+
this.defaultProvider = defaultProvider;
|
|
14
|
+
this.defaultModel = defaultModel;
|
|
15
|
+
}
|
|
16
|
+
get providerId() {
|
|
17
|
+
return this.role.provider ?? this.defaultProvider;
|
|
18
|
+
}
|
|
19
|
+
get modelId() {
|
|
20
|
+
return this.role.model ?? this.defaultModel;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Generate this agent's response given the full discussion history.
|
|
24
|
+
*
|
|
25
|
+
* Returns a DiscussionMessage, with `passed: true` if the agent outputs [PASS].
|
|
26
|
+
*/
|
|
27
|
+
async speak(topic, history, round, maxRounds) {
|
|
28
|
+
const provider = this.providers.get(this.providerId);
|
|
29
|
+
if (!provider) {
|
|
30
|
+
throw new Error(`Provider "${this.providerId}" not available for agent "${this.role.id}"`);
|
|
31
|
+
}
|
|
32
|
+
const systemPrompt = this.buildSystemPrompt(topic, round, maxRounds);
|
|
33
|
+
const messages = this.buildMessages(history);
|
|
34
|
+
const response = await provider.chat({
|
|
35
|
+
messages,
|
|
36
|
+
model: this.modelId,
|
|
37
|
+
systemPrompt,
|
|
38
|
+
stream: false,
|
|
39
|
+
temperature: 0.7,
|
|
40
|
+
maxTokens: 4096
|
|
41
|
+
});
|
|
42
|
+
const content = response.content.trim();
|
|
43
|
+
const passed = content.toUpperCase().startsWith(PASS_MARKER) || content.toUpperCase() === PASS_MARKER;
|
|
44
|
+
return {
|
|
45
|
+
speaker: this.role.id,
|
|
46
|
+
speakerName: this.role.name,
|
|
47
|
+
content: passed ? "" : content,
|
|
48
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
49
|
+
passed
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Generate a summary of the entire discussion from this agent's perspective.
|
|
54
|
+
*/
|
|
55
|
+
async summarize(topic, history) {
|
|
56
|
+
const provider = this.providers.get(this.providerId);
|
|
57
|
+
if (!provider) {
|
|
58
|
+
throw new Error(`Provider "${this.providerId}" not available`);
|
|
59
|
+
}
|
|
60
|
+
const messages = [
|
|
61
|
+
{
|
|
62
|
+
role: "user",
|
|
63
|
+
content: this.buildSummaryPrompt(topic, history),
|
|
64
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
65
|
+
}
|
|
66
|
+
];
|
|
67
|
+
const response = await provider.chat({
|
|
68
|
+
messages,
|
|
69
|
+
model: this.modelId,
|
|
70
|
+
stream: false,
|
|
71
|
+
temperature: 0.3,
|
|
72
|
+
maxTokens: 4096
|
|
73
|
+
});
|
|
74
|
+
return response.content.trim();
|
|
75
|
+
}
|
|
76
|
+
// ── Private ──────────────────────────────────────────────────────
|
|
77
|
+
buildSystemPrompt(topic, round, maxRounds) {
|
|
78
|
+
return `# Multi-Agent Discussion \u2014 Role: ${this.role.name}
|
|
79
|
+
|
|
80
|
+
${this.role.persona}
|
|
81
|
+
|
|
82
|
+
## Discussion Rules
|
|
83
|
+
- You are participating in a multi-agent discussion about the topic below.
|
|
84
|
+
- You can see what other participants have said. Build on their ideas, challenge them, or add your own perspective.
|
|
85
|
+
- Stay in character as ${this.role.name}. Respond from your role's expertise and viewpoint.
|
|
86
|
+
- Keep responses concise and focused (2-6 paragraphs). Do not repeat what others have already said.
|
|
87
|
+
- If you have nothing meaningful to add (others have covered your points), respond with exactly: [PASS]
|
|
88
|
+
- This is round ${round} of ${maxRounds}. ${round >= maxRounds - 1 ? "This is one of the final rounds \u2014 try to converge on conclusions." : ""}
|
|
89
|
+
- Use the language that the topic is written in (if the topic is in Chinese, respond in Chinese).
|
|
90
|
+
|
|
91
|
+
## Topic
|
|
92
|
+
${topic}`;
|
|
93
|
+
}
|
|
94
|
+
buildMessages(history) {
|
|
95
|
+
const messages = [];
|
|
96
|
+
for (const msg of history) {
|
|
97
|
+
if (msg.passed) continue;
|
|
98
|
+
if (msg.speaker === this.role.id) {
|
|
99
|
+
messages.push({
|
|
100
|
+
role: "assistant",
|
|
101
|
+
content: msg.content,
|
|
102
|
+
timestamp: msg.timestamp
|
|
103
|
+
});
|
|
104
|
+
} else {
|
|
105
|
+
const prefix = `[${msg.speakerName} (${msg.speaker})]:`;
|
|
106
|
+
messages.push({
|
|
107
|
+
role: "user",
|
|
108
|
+
content: `${prefix}
|
|
109
|
+
${msg.content}`,
|
|
110
|
+
timestamp: msg.timestamp
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (messages.length === 0) {
|
|
115
|
+
messages.push({
|
|
116
|
+
role: "user",
|
|
117
|
+
content: "Please share your initial thoughts on the topic described in the system prompt. You are the first to speak.",
|
|
118
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
119
|
+
});
|
|
120
|
+
} else {
|
|
121
|
+
messages.push({
|
|
122
|
+
role: "user",
|
|
123
|
+
content: "It is now your turn to respond. Consider what the other participants have said and share your perspective. If you have nothing to add, respond with [PASS].",
|
|
124
|
+
timestamp: /* @__PURE__ */ new Date()
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return messages;
|
|
128
|
+
}
|
|
129
|
+
buildSummaryPrompt(topic, history) {
|
|
130
|
+
const transcript = history.filter((m) => !m.passed).map((m) => `**${m.speakerName}** (${m.speaker}):
|
|
131
|
+
${m.content}`).join("\n\n---\n\n");
|
|
132
|
+
return `Please synthesize the following multi-agent discussion into a clear, structured summary.
|
|
133
|
+
|
|
134
|
+
## Discussion Topic
|
|
135
|
+
${topic}
|
|
136
|
+
|
|
137
|
+
## Full Transcript
|
|
138
|
+
${transcript}
|
|
139
|
+
|
|
140
|
+
## Instructions
|
|
141
|
+
Produce a summary that includes:
|
|
142
|
+
1. **Key Points of Agreement** \u2014 what all participants agreed on
|
|
143
|
+
2. **Points of Debate** \u2014 where participants disagreed and the different perspectives
|
|
144
|
+
3. **Conclusions & Recommendations** \u2014 actionable takeaways
|
|
145
|
+
4. **Open Questions** \u2014 unresolved issues that need further discussion
|
|
146
|
+
|
|
147
|
+
Keep the summary concise (within 500 words). Use the same language as the discussion.`;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
// src/hub/renderer.ts
|
|
152
|
+
import chalk from "chalk";
|
|
153
|
+
var ROLE_COLORS = [
|
|
154
|
+
chalk.cyan,
|
|
155
|
+
chalk.magenta,
|
|
156
|
+
chalk.yellow,
|
|
157
|
+
chalk.green,
|
|
158
|
+
chalk.blue,
|
|
159
|
+
chalk.red,
|
|
160
|
+
chalk.rgb(255, 165, 0),
|
|
161
|
+
// orange
|
|
162
|
+
chalk.rgb(148, 103, 189)
|
|
163
|
+
// purple
|
|
164
|
+
];
|
|
165
|
+
var colorMap = /* @__PURE__ */ new Map();
|
|
166
|
+
function assignRoleColors(roles) {
|
|
167
|
+
colorMap.clear();
|
|
168
|
+
roles.forEach((role, i) => {
|
|
169
|
+
if (role.color && chalk[role.color]) {
|
|
170
|
+
colorMap.set(role.id, chalk[role.color]);
|
|
171
|
+
} else {
|
|
172
|
+
colorMap.set(role.id, ROLE_COLORS[i % ROLE_COLORS.length]);
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
function renderHubBanner(topic, roles, maxRounds) {
|
|
177
|
+
console.log();
|
|
178
|
+
console.log(chalk.bold.white(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
179
|
+
console.log(chalk.bold.white(" \u2551") + chalk.bold.cyan(" \u{1F3DB}\uFE0F AI-CLI Multi-Agent Hub \u2014 Discussion Mode ") + chalk.bold.white("\u2551"));
|
|
180
|
+
console.log(chalk.bold.white(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
|
|
181
|
+
console.log(chalk.bold.white(" \u2551") + chalk.dim(` Topic: ${truncate(topic, 52)}`.padEnd(62)) + chalk.bold.white("\u2551"));
|
|
182
|
+
console.log(chalk.bold.white(" \u2551") + chalk.dim(` Rounds: up to ${maxRounds}`.padEnd(62)) + chalk.bold.white("\u2551"));
|
|
183
|
+
console.log(chalk.bold.white(" \u2551") + chalk.dim(" Participants:".padEnd(62)) + chalk.bold.white("\u2551"));
|
|
184
|
+
for (const role of roles) {
|
|
185
|
+
const color = colorMap.get(role.id) ?? chalk.white;
|
|
186
|
+
const line = ` ${color("\u25CF")} ${color.bold(role.name)} ${chalk.dim(`(${role.id})`)}`;
|
|
187
|
+
console.log(chalk.bold.white(" \u2551") + line + " ".repeat(Math.max(0, 62 - stripAnsi(line).length)) + chalk.bold.white("\u2551"));
|
|
188
|
+
}
|
|
189
|
+
console.log(chalk.bold.white(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
|
|
190
|
+
console.log(chalk.bold.white(" \u2551") + chalk.dim(" Press Ctrl+C to interrupt and generate summary".padEnd(62)) + chalk.bold.white("\u2551"));
|
|
191
|
+
console.log(chalk.bold.white(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
192
|
+
console.log();
|
|
193
|
+
}
|
|
194
|
+
function renderRoundHeader(round, maxRounds) {
|
|
195
|
+
console.log(chalk.dim(` \u2500\u2500 Round ${round}/${maxRounds} ${"\u2500".repeat(48)}`));
|
|
196
|
+
console.log();
|
|
197
|
+
}
|
|
198
|
+
function renderAgentSpeaking(roleName, roleId) {
|
|
199
|
+
const color = colorMap.get(roleId) ?? chalk.white;
|
|
200
|
+
process.stdout.write(chalk.dim(" \u{1F4AD} ") + color.bold(roleName) + chalk.dim(" is thinking..."));
|
|
201
|
+
}
|
|
202
|
+
function clearSpeakingLine() {
|
|
203
|
+
process.stdout.write("\r\x1B[2K");
|
|
204
|
+
}
|
|
205
|
+
function renderAgentMessage(msg) {
|
|
206
|
+
const color = colorMap.get(msg.speaker) ?? chalk.white;
|
|
207
|
+
if (msg.passed) {
|
|
208
|
+
console.log(chalk.dim(` ${msg.speakerName}: [PASS \u2014 nothing to add]`));
|
|
209
|
+
console.log();
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
console.log(color.bold(` \u250C\u2500 ${msg.speakerName} `) + chalk.dim(`(${msg.speaker})`));
|
|
213
|
+
const lines = msg.content.split("\n");
|
|
214
|
+
for (const line of lines) {
|
|
215
|
+
console.log(color(" \u2502 ") + line);
|
|
216
|
+
}
|
|
217
|
+
console.log(color(" \u2514" + "\u2500".repeat(60)));
|
|
218
|
+
console.log();
|
|
219
|
+
}
|
|
220
|
+
function renderConsensus() {
|
|
221
|
+
console.log();
|
|
222
|
+
console.log(chalk.green.bold(" \u2713 All agents passed \u2014 consensus reached."));
|
|
223
|
+
console.log();
|
|
224
|
+
}
|
|
225
|
+
function renderMaxRounds(maxRounds) {
|
|
226
|
+
console.log();
|
|
227
|
+
console.log(chalk.yellow(` \u26A0 Maximum rounds (${maxRounds}) reached.`));
|
|
228
|
+
console.log();
|
|
229
|
+
}
|
|
230
|
+
function renderUserInterrupt() {
|
|
231
|
+
console.log();
|
|
232
|
+
console.log(chalk.yellow(" \u26A0 Discussion interrupted by user."));
|
|
233
|
+
console.log();
|
|
234
|
+
}
|
|
235
|
+
function renderSummary(summary) {
|
|
236
|
+
console.log(chalk.bold.white(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
237
|
+
console.log(chalk.bold.white(" \u2551") + chalk.bold.green(" \u{1F4CB} Discussion Summary ") + chalk.bold.white("\u2551"));
|
|
238
|
+
console.log(chalk.bold.white(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
239
|
+
console.log();
|
|
240
|
+
for (const line of summary.split("\n")) {
|
|
241
|
+
console.log(" " + line);
|
|
242
|
+
}
|
|
243
|
+
console.log();
|
|
244
|
+
}
|
|
245
|
+
function renderHubEvent(event) {
|
|
246
|
+
switch (event.type) {
|
|
247
|
+
case "round_start":
|
|
248
|
+
renderRoundHeader(event.round, event.maxRounds);
|
|
249
|
+
break;
|
|
250
|
+
case "agent_speaking":
|
|
251
|
+
renderAgentSpeaking(event.roleName, event.roleId);
|
|
252
|
+
break;
|
|
253
|
+
case "agent_spoke":
|
|
254
|
+
clearSpeakingLine();
|
|
255
|
+
renderAgentMessage(event.message);
|
|
256
|
+
break;
|
|
257
|
+
case "agent_passed":
|
|
258
|
+
clearSpeakingLine();
|
|
259
|
+
break;
|
|
260
|
+
case "discussion_end":
|
|
261
|
+
if (event.reason === "consensus") renderConsensus();
|
|
262
|
+
else if (event.reason === "max_rounds") renderMaxRounds(0);
|
|
263
|
+
else renderUserInterrupt();
|
|
264
|
+
break;
|
|
265
|
+
case "summary":
|
|
266
|
+
renderSummary(event.content);
|
|
267
|
+
break;
|
|
268
|
+
case "error":
|
|
269
|
+
console.log(chalk.red(` \u2717 Error${event.roleId ? ` (${event.roleId})` : ""}: ${event.message}`));
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
function truncate(s, maxLen) {
|
|
274
|
+
return s.length > maxLen ? s.slice(0, maxLen - 3) + "..." : s;
|
|
275
|
+
}
|
|
276
|
+
function stripAnsi(s) {
|
|
277
|
+
return s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
export {
|
|
281
|
+
HubAgent,
|
|
282
|
+
assignRoleColors,
|
|
283
|
+
renderHubBanner,
|
|
284
|
+
renderRoundHeader,
|
|
285
|
+
renderAgentSpeaking,
|
|
286
|
+
clearSpeakingLine,
|
|
287
|
+
renderAgentMessage,
|
|
288
|
+
renderConsensus,
|
|
289
|
+
renderMaxRounds,
|
|
290
|
+
renderUserInterrupt,
|
|
291
|
+
renderSummary,
|
|
292
|
+
renderHubEvent
|
|
293
|
+
};
|
|
@@ -1,152 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
defaultProvider;
|
|
9
|
-
defaultModel;
|
|
10
|
-
constructor(role, providers, defaultProvider, defaultModel) {
|
|
11
|
-
this.role = role;
|
|
12
|
-
this.providers = providers;
|
|
13
|
-
this.defaultProvider = defaultProvider;
|
|
14
|
-
this.defaultModel = defaultModel;
|
|
15
|
-
}
|
|
16
|
-
get providerId() {
|
|
17
|
-
return this.role.provider ?? this.defaultProvider;
|
|
18
|
-
}
|
|
19
|
-
get modelId() {
|
|
20
|
-
return this.role.model ?? this.defaultModel;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Generate this agent's response given the full discussion history.
|
|
24
|
-
*
|
|
25
|
-
* Returns a DiscussionMessage, with `passed: true` if the agent outputs [PASS].
|
|
26
|
-
*/
|
|
27
|
-
async speak(topic, history, round, maxRounds) {
|
|
28
|
-
const provider = this.providers.get(this.providerId);
|
|
29
|
-
if (!provider) {
|
|
30
|
-
throw new Error(`Provider "${this.providerId}" not available for agent "${this.role.id}"`);
|
|
31
|
-
}
|
|
32
|
-
const systemPrompt = this.buildSystemPrompt(topic, round, maxRounds);
|
|
33
|
-
const messages = this.buildMessages(history);
|
|
34
|
-
const response = await provider.chat({
|
|
35
|
-
messages,
|
|
36
|
-
model: this.modelId,
|
|
37
|
-
systemPrompt,
|
|
38
|
-
stream: false,
|
|
39
|
-
temperature: 0.7,
|
|
40
|
-
maxTokens: 4096
|
|
41
|
-
});
|
|
42
|
-
const content = response.content.trim();
|
|
43
|
-
const passed = content.toUpperCase().startsWith(PASS_MARKER) || content.toUpperCase() === PASS_MARKER;
|
|
44
|
-
return {
|
|
45
|
-
speaker: this.role.id,
|
|
46
|
-
speakerName: this.role.name,
|
|
47
|
-
content: passed ? "" : content,
|
|
48
|
-
timestamp: /* @__PURE__ */ new Date(),
|
|
49
|
-
passed
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Generate a summary of the entire discussion from this agent's perspective.
|
|
54
|
-
*/
|
|
55
|
-
async summarize(topic, history) {
|
|
56
|
-
const provider = this.providers.get(this.providerId);
|
|
57
|
-
if (!provider) {
|
|
58
|
-
throw new Error(`Provider "${this.providerId}" not available`);
|
|
59
|
-
}
|
|
60
|
-
const messages = [
|
|
61
|
-
{
|
|
62
|
-
role: "user",
|
|
63
|
-
content: this.buildSummaryPrompt(topic, history),
|
|
64
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
65
|
-
}
|
|
66
|
-
];
|
|
67
|
-
const response = await provider.chat({
|
|
68
|
-
messages,
|
|
69
|
-
model: this.modelId,
|
|
70
|
-
stream: false,
|
|
71
|
-
temperature: 0.3,
|
|
72
|
-
maxTokens: 4096
|
|
73
|
-
});
|
|
74
|
-
return response.content.trim();
|
|
75
|
-
}
|
|
76
|
-
// ── Private ──────────────────────────────────────────────────────
|
|
77
|
-
buildSystemPrompt(topic, round, maxRounds) {
|
|
78
|
-
return `# Multi-Agent Discussion \u2014 Role: ${this.role.name}
|
|
79
|
-
|
|
80
|
-
${this.role.persona}
|
|
81
|
-
|
|
82
|
-
## Discussion Rules
|
|
83
|
-
- You are participating in a multi-agent discussion about the topic below.
|
|
84
|
-
- You can see what other participants have said. Build on their ideas, challenge them, or add your own perspective.
|
|
85
|
-
- Stay in character as ${this.role.name}. Respond from your role's expertise and viewpoint.
|
|
86
|
-
- Keep responses concise and focused (2-6 paragraphs). Do not repeat what others have already said.
|
|
87
|
-
- If you have nothing meaningful to add (others have covered your points), respond with exactly: [PASS]
|
|
88
|
-
- This is round ${round} of ${maxRounds}. ${round >= maxRounds - 1 ? "This is one of the final rounds \u2014 try to converge on conclusions." : ""}
|
|
89
|
-
- Use the language that the topic is written in (if the topic is in Chinese, respond in Chinese).
|
|
90
|
-
|
|
91
|
-
## Topic
|
|
92
|
-
${topic}`;
|
|
93
|
-
}
|
|
94
|
-
buildMessages(history) {
|
|
95
|
-
const messages = [];
|
|
96
|
-
for (const msg of history) {
|
|
97
|
-
if (msg.passed) continue;
|
|
98
|
-
if (msg.speaker === this.role.id) {
|
|
99
|
-
messages.push({
|
|
100
|
-
role: "assistant",
|
|
101
|
-
content: msg.content,
|
|
102
|
-
timestamp: msg.timestamp
|
|
103
|
-
});
|
|
104
|
-
} else {
|
|
105
|
-
const prefix = `[${msg.speakerName} (${msg.speaker})]:`;
|
|
106
|
-
messages.push({
|
|
107
|
-
role: "user",
|
|
108
|
-
content: `${prefix}
|
|
109
|
-
${msg.content}`,
|
|
110
|
-
timestamp: msg.timestamp
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
if (messages.length === 0) {
|
|
115
|
-
messages.push({
|
|
116
|
-
role: "user",
|
|
117
|
-
content: "Please share your initial thoughts on the topic described in the system prompt. You are the first to speak.",
|
|
118
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
119
|
-
});
|
|
120
|
-
} else {
|
|
121
|
-
messages.push({
|
|
122
|
-
role: "user",
|
|
123
|
-
content: "It is now your turn to respond. Consider what the other participants have said and share your perspective. If you have nothing to add, respond with [PASS].",
|
|
124
|
-
timestamp: /* @__PURE__ */ new Date()
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
return messages;
|
|
128
|
-
}
|
|
129
|
-
buildSummaryPrompt(topic, history) {
|
|
130
|
-
const transcript = history.filter((m) => !m.passed).map((m) => `**${m.speakerName}** (${m.speaker}):
|
|
131
|
-
${m.content}`).join("\n\n---\n\n");
|
|
132
|
-
return `Please synthesize the following multi-agent discussion into a clear, structured summary.
|
|
133
|
-
|
|
134
|
-
## Discussion Topic
|
|
135
|
-
${topic}
|
|
136
|
-
|
|
137
|
-
## Full Transcript
|
|
138
|
-
${transcript}
|
|
139
|
-
|
|
140
|
-
## Instructions
|
|
141
|
-
Produce a summary that includes:
|
|
142
|
-
1. **Key Points of Agreement** \u2014 what all participants agreed on
|
|
143
|
-
2. **Points of Debate** \u2014 where participants disagreed and the different perspectives
|
|
144
|
-
3. **Conclusions & Recommendations** \u2014 actionable takeaways
|
|
145
|
-
4. **Open Questions** \u2014 unresolved issues that need further discussion
|
|
146
|
-
|
|
147
|
-
Keep the summary concise (within 500 words). Use the same language as the discussion.`;
|
|
148
|
-
}
|
|
149
|
-
};
|
|
2
|
+
import {
|
|
3
|
+
HubAgent,
|
|
4
|
+
assignRoleColors,
|
|
5
|
+
renderHubBanner,
|
|
6
|
+
renderHubEvent
|
|
7
|
+
} from "./chunk-4ZAZF4CF.js";
|
|
150
8
|
|
|
151
9
|
// src/hub/discuss.ts
|
|
152
10
|
var DiscussionOrchestrator = class {
|
|
@@ -409,135 +267,6 @@ function listPresets() {
|
|
|
409
267
|
return PRESETS;
|
|
410
268
|
}
|
|
411
269
|
|
|
412
|
-
// src/hub/renderer.ts
|
|
413
|
-
import chalk from "chalk";
|
|
414
|
-
var ROLE_COLORS = [
|
|
415
|
-
chalk.cyan,
|
|
416
|
-
chalk.magenta,
|
|
417
|
-
chalk.yellow,
|
|
418
|
-
chalk.green,
|
|
419
|
-
chalk.blue,
|
|
420
|
-
chalk.red,
|
|
421
|
-
chalk.rgb(255, 165, 0),
|
|
422
|
-
// orange
|
|
423
|
-
chalk.rgb(148, 103, 189)
|
|
424
|
-
// purple
|
|
425
|
-
];
|
|
426
|
-
var colorMap = /* @__PURE__ */ new Map();
|
|
427
|
-
function assignRoleColors(roles) {
|
|
428
|
-
colorMap.clear();
|
|
429
|
-
roles.forEach((role, i) => {
|
|
430
|
-
if (role.color && chalk[role.color]) {
|
|
431
|
-
colorMap.set(role.id, chalk[role.color]);
|
|
432
|
-
} else {
|
|
433
|
-
colorMap.set(role.id, ROLE_COLORS[i % ROLE_COLORS.length]);
|
|
434
|
-
}
|
|
435
|
-
});
|
|
436
|
-
}
|
|
437
|
-
function renderHubBanner(topic, roles, maxRounds) {
|
|
438
|
-
console.log();
|
|
439
|
-
console.log(chalk.bold.white(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
440
|
-
console.log(chalk.bold.white(" \u2551") + chalk.bold.cyan(" \u{1F3DB}\uFE0F AI-CLI Multi-Agent Hub \u2014 Discussion Mode ") + chalk.bold.white("\u2551"));
|
|
441
|
-
console.log(chalk.bold.white(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
|
|
442
|
-
console.log(chalk.bold.white(" \u2551") + chalk.dim(` Topic: ${truncate(topic, 52)}`.padEnd(62)) + chalk.bold.white("\u2551"));
|
|
443
|
-
console.log(chalk.bold.white(" \u2551") + chalk.dim(` Rounds: up to ${maxRounds}`.padEnd(62)) + chalk.bold.white("\u2551"));
|
|
444
|
-
console.log(chalk.bold.white(" \u2551") + chalk.dim(" Participants:".padEnd(62)) + chalk.bold.white("\u2551"));
|
|
445
|
-
for (const role of roles) {
|
|
446
|
-
const color = colorMap.get(role.id) ?? chalk.white;
|
|
447
|
-
const line = ` ${color("\u25CF")} ${color.bold(role.name)} ${chalk.dim(`(${role.id})`)}`;
|
|
448
|
-
console.log(chalk.bold.white(" \u2551") + line + " ".repeat(Math.max(0, 62 - stripAnsi(line).length)) + chalk.bold.white("\u2551"));
|
|
449
|
-
}
|
|
450
|
-
console.log(chalk.bold.white(" \u2560\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2563"));
|
|
451
|
-
console.log(chalk.bold.white(" \u2551") + chalk.dim(" Press Ctrl+C to interrupt and generate summary".padEnd(62)) + chalk.bold.white("\u2551"));
|
|
452
|
-
console.log(chalk.bold.white(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
453
|
-
console.log();
|
|
454
|
-
}
|
|
455
|
-
function renderRoundHeader(round, maxRounds) {
|
|
456
|
-
console.log(chalk.dim(` \u2500\u2500 Round ${round}/${maxRounds} ${"\u2500".repeat(48)}`));
|
|
457
|
-
console.log();
|
|
458
|
-
}
|
|
459
|
-
function renderAgentSpeaking(roleName, roleId) {
|
|
460
|
-
const color = colorMap.get(roleId) ?? chalk.white;
|
|
461
|
-
process.stdout.write(chalk.dim(" \u{1F4AD} ") + color.bold(roleName) + chalk.dim(" is thinking..."));
|
|
462
|
-
}
|
|
463
|
-
function clearSpeakingLine() {
|
|
464
|
-
process.stdout.write("\r\x1B[2K");
|
|
465
|
-
}
|
|
466
|
-
function renderAgentMessage(msg) {
|
|
467
|
-
const color = colorMap.get(msg.speaker) ?? chalk.white;
|
|
468
|
-
if (msg.passed) {
|
|
469
|
-
console.log(chalk.dim(` ${msg.speakerName}: [PASS \u2014 nothing to add]`));
|
|
470
|
-
console.log();
|
|
471
|
-
return;
|
|
472
|
-
}
|
|
473
|
-
console.log(color.bold(` \u250C\u2500 ${msg.speakerName} `) + chalk.dim(`(${msg.speaker})`));
|
|
474
|
-
const lines = msg.content.split("\n");
|
|
475
|
-
for (const line of lines) {
|
|
476
|
-
console.log(color(" \u2502 ") + line);
|
|
477
|
-
}
|
|
478
|
-
console.log(color(" \u2514" + "\u2500".repeat(60)));
|
|
479
|
-
console.log();
|
|
480
|
-
}
|
|
481
|
-
function renderConsensus() {
|
|
482
|
-
console.log();
|
|
483
|
-
console.log(chalk.green.bold(" \u2713 All agents passed \u2014 consensus reached."));
|
|
484
|
-
console.log();
|
|
485
|
-
}
|
|
486
|
-
function renderMaxRounds(maxRounds) {
|
|
487
|
-
console.log();
|
|
488
|
-
console.log(chalk.yellow(` \u26A0 Maximum rounds (${maxRounds}) reached.`));
|
|
489
|
-
console.log();
|
|
490
|
-
}
|
|
491
|
-
function renderUserInterrupt() {
|
|
492
|
-
console.log();
|
|
493
|
-
console.log(chalk.yellow(" \u26A0 Discussion interrupted by user."));
|
|
494
|
-
console.log();
|
|
495
|
-
}
|
|
496
|
-
function renderSummary(summary) {
|
|
497
|
-
console.log(chalk.bold.white(" \u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557"));
|
|
498
|
-
console.log(chalk.bold.white(" \u2551") + chalk.bold.green(" \u{1F4CB} Discussion Summary ") + chalk.bold.white("\u2551"));
|
|
499
|
-
console.log(chalk.bold.white(" \u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"));
|
|
500
|
-
console.log();
|
|
501
|
-
for (const line of summary.split("\n")) {
|
|
502
|
-
console.log(" " + line);
|
|
503
|
-
}
|
|
504
|
-
console.log();
|
|
505
|
-
}
|
|
506
|
-
function renderHubEvent(event) {
|
|
507
|
-
switch (event.type) {
|
|
508
|
-
case "round_start":
|
|
509
|
-
renderRoundHeader(event.round, event.maxRounds);
|
|
510
|
-
break;
|
|
511
|
-
case "agent_speaking":
|
|
512
|
-
renderAgentSpeaking(event.roleName, event.roleId);
|
|
513
|
-
break;
|
|
514
|
-
case "agent_spoke":
|
|
515
|
-
clearSpeakingLine();
|
|
516
|
-
renderAgentMessage(event.message);
|
|
517
|
-
break;
|
|
518
|
-
case "agent_passed":
|
|
519
|
-
clearSpeakingLine();
|
|
520
|
-
break;
|
|
521
|
-
case "discussion_end":
|
|
522
|
-
if (event.reason === "consensus") renderConsensus();
|
|
523
|
-
else if (event.reason === "max_rounds") renderMaxRounds(0);
|
|
524
|
-
else renderUserInterrupt();
|
|
525
|
-
break;
|
|
526
|
-
case "summary":
|
|
527
|
-
renderSummary(event.content);
|
|
528
|
-
break;
|
|
529
|
-
case "error":
|
|
530
|
-
console.log(chalk.red(` \u2717 Error${event.roleId ? ` (${event.roleId})` : ""}: ${event.message}`));
|
|
531
|
-
break;
|
|
532
|
-
}
|
|
533
|
-
}
|
|
534
|
-
function truncate(s, maxLen) {
|
|
535
|
-
return s.length > maxLen ? s.slice(0, maxLen - 3) + "..." : s;
|
|
536
|
-
}
|
|
537
|
-
function stripAnsi(s) {
|
|
538
|
-
return s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
539
|
-
}
|
|
540
|
-
|
|
541
270
|
// src/hub/index.ts
|
|
542
271
|
import { readFileSync, existsSync } from "fs";
|
|
543
272
|
async function startHub(options, configManager, providers) {
|
|
@@ -611,12 +340,39 @@ async function startHub(options, configManager, providers) {
|
|
|
611
340
|
enableTools: false
|
|
612
341
|
};
|
|
613
342
|
if (config.mode === "discuss") {
|
|
614
|
-
|
|
343
|
+
if (options.distributed) {
|
|
344
|
+
await runDistributedDiscussion(config, providers, options.topic, options.port ?? 9527);
|
|
345
|
+
} else {
|
|
346
|
+
await runDiscussion(config, providers, options.topic);
|
|
347
|
+
}
|
|
615
348
|
} else {
|
|
616
349
|
console.error(" \u2717 Task mode is not yet implemented. Use --discuss (default).");
|
|
617
350
|
process.exit(1);
|
|
618
351
|
}
|
|
619
352
|
}
|
|
353
|
+
async function runDistributedDiscussion(config, providers, topic, port) {
|
|
354
|
+
const { HubServer } = await import("./hub-server-P547YWUN.js");
|
|
355
|
+
const hub = new HubServer(config, providers, port);
|
|
356
|
+
let interrupted = false;
|
|
357
|
+
const onSigint = () => {
|
|
358
|
+
if (interrupted) {
|
|
359
|
+
console.log("\n Force exit.");
|
|
360
|
+
hub.shutdown();
|
|
361
|
+
process.exit(0);
|
|
362
|
+
}
|
|
363
|
+
interrupted = true;
|
|
364
|
+
hub.abort();
|
|
365
|
+
};
|
|
366
|
+
process.on("SIGINT", onSigint);
|
|
367
|
+
try {
|
|
368
|
+
await hub.start(topic);
|
|
369
|
+
} catch (err) {
|
|
370
|
+
console.error(`
|
|
371
|
+
\u2717 Hub error: ${err.message}`);
|
|
372
|
+
} finally {
|
|
373
|
+
process.removeListener("SIGINT", onSigint);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
620
376
|
async function runDiscussion(config, providers, topic) {
|
|
621
377
|
assignRoleColors(config.roles);
|
|
622
378
|
const orchestrator = new DiscussionOrchestrator(config, providers);
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
HubAgent,
|
|
4
|
+
assignRoleColors,
|
|
5
|
+
clearSpeakingLine,
|
|
6
|
+
renderAgentMessage,
|
|
7
|
+
renderAgentSpeaking,
|
|
8
|
+
renderConsensus,
|
|
9
|
+
renderHubBanner,
|
|
10
|
+
renderMaxRounds,
|
|
11
|
+
renderRoundHeader,
|
|
12
|
+
renderSummary,
|
|
13
|
+
renderUserInterrupt
|
|
14
|
+
} from "./chunk-4ZAZF4CF.js";
|
|
15
|
+
|
|
16
|
+
// src/hub/hub-server.ts
|
|
17
|
+
import { WebSocketServer } from "ws";
|
|
18
|
+
import { createServer } from "http";
|
|
19
|
+
import chalk from "chalk";
|
|
20
|
+
var HubServer = class {
|
|
21
|
+
constructor(config, providers, port) {
|
|
22
|
+
this.config = config;
|
|
23
|
+
this.providers = providers;
|
|
24
|
+
this.port = port;
|
|
25
|
+
this.unassignedRoles = [...config.roles];
|
|
26
|
+
}
|
|
27
|
+
wss = null;
|
|
28
|
+
httpServer = null;
|
|
29
|
+
agents = /* @__PURE__ */ new Map();
|
|
30
|
+
unassignedRoles = [];
|
|
31
|
+
history = [];
|
|
32
|
+
aborted = false;
|
|
33
|
+
currentTurnResolve = null;
|
|
34
|
+
/**
|
|
35
|
+
* Start the hub server and run the discussion.
|
|
36
|
+
*/
|
|
37
|
+
async start(topic) {
|
|
38
|
+
assignRoleColors(this.config.roles);
|
|
39
|
+
this.httpServer = createServer();
|
|
40
|
+
this.wss = new WebSocketServer({ server: this.httpServer });
|
|
41
|
+
this.wss.on("connection", (ws) => this.handleConnection(ws));
|
|
42
|
+
await new Promise((resolve) => {
|
|
43
|
+
this.httpServer.listen(this.port, () => resolve());
|
|
44
|
+
});
|
|
45
|
+
const maxRounds = this.config.maxRounds ?? 10;
|
|
46
|
+
renderHubBanner(topic, this.config.roles, maxRounds);
|
|
47
|
+
console.log(chalk.bold.cyan(` \u{1F310} Hub server listening on ws://localhost:${this.port}`));
|
|
48
|
+
console.log(chalk.dim(` Waiting for ${this.config.roles.length} agent(s) to connect...`));
|
|
49
|
+
console.log(chalk.dim(` Each agent should run: aicli join --port ${this.port}`));
|
|
50
|
+
console.log();
|
|
51
|
+
await this.waitForAgents(topic);
|
|
52
|
+
await this.runDiscussion(topic, maxRounds);
|
|
53
|
+
this.shutdown();
|
|
54
|
+
}
|
|
55
|
+
abort() {
|
|
56
|
+
this.aborted = true;
|
|
57
|
+
if (this.currentTurnResolve) {
|
|
58
|
+
this.currentTurnResolve({ content: "", passed: true });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
shutdown() {
|
|
62
|
+
for (const agent of this.agents.values()) {
|
|
63
|
+
try {
|
|
64
|
+
agent.ws.close();
|
|
65
|
+
} catch {
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
this.wss?.close();
|
|
69
|
+
this.httpServer?.close();
|
|
70
|
+
}
|
|
71
|
+
// ── Connection Handling ──────────────────────────────────────────
|
|
72
|
+
handleConnection(ws) {
|
|
73
|
+
ws.on("message", (raw) => {
|
|
74
|
+
try {
|
|
75
|
+
const msg = JSON.parse(raw.toString());
|
|
76
|
+
this.handleAgentMessage(ws, msg);
|
|
77
|
+
} catch {
|
|
78
|
+
this.sendTo(ws, { type: "error", message: "Invalid message format" });
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
ws.on("close", () => {
|
|
82
|
+
for (const [roleId, agent] of this.agents) {
|
|
83
|
+
if (agent.ws === ws) {
|
|
84
|
+
console.log(chalk.yellow(` \u26A0 Agent "${agent.roleName}" (${roleId}) disconnected`));
|
|
85
|
+
this.agents.delete(roleId);
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
handleAgentMessage(ws, msg) {
|
|
92
|
+
switch (msg.type) {
|
|
93
|
+
case "register": {
|
|
94
|
+
const requestedRole = msg.roleId;
|
|
95
|
+
const roleIndex = this.unassignedRoles.findIndex((r) => r.id === requestedRole);
|
|
96
|
+
if (roleIndex === -1) {
|
|
97
|
+
if (this.unassignedRoles.length === 0) {
|
|
98
|
+
this.sendTo(ws, { type: "error", message: "All roles are already assigned." });
|
|
99
|
+
ws.close();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const role = requestedRole ? void 0 : this.unassignedRoles[0];
|
|
103
|
+
if (!role && requestedRole) {
|
|
104
|
+
const exists = this.config.roles.some((r) => r.id === requestedRole);
|
|
105
|
+
if (!exists) {
|
|
106
|
+
this.sendTo(ws, { type: "error", message: `Role "${requestedRole}" not found. Available: ${this.unassignedRoles.map((r) => r.id).join(", ")}` });
|
|
107
|
+
ws.close();
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
this.sendTo(ws, { type: "error", message: `Role "${requestedRole}" is already taken. Available: ${this.unassignedRoles.map((r) => r.id).join(", ")}` });
|
|
111
|
+
ws.close();
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (role) {
|
|
115
|
+
this.assignRole(ws, role);
|
|
116
|
+
this.unassignedRoles.splice(0, 1);
|
|
117
|
+
}
|
|
118
|
+
} else {
|
|
119
|
+
const role = this.unassignedRoles[roleIndex];
|
|
120
|
+
this.assignRole(ws, role);
|
|
121
|
+
this.unassignedRoles.splice(roleIndex, 1);
|
|
122
|
+
}
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
case "response": {
|
|
126
|
+
if (this.currentTurnResolve) {
|
|
127
|
+
this.currentTurnResolve({ content: msg.content, passed: msg.passed });
|
|
128
|
+
this.currentTurnResolve = null;
|
|
129
|
+
}
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
assignRole(ws, role) {
|
|
135
|
+
this.agents.set(role.id, {
|
|
136
|
+
ws,
|
|
137
|
+
roleId: role.id,
|
|
138
|
+
roleName: role.name,
|
|
139
|
+
ready: true
|
|
140
|
+
});
|
|
141
|
+
console.log(chalk.green(` \u2713 Agent connected: ${role.name} (${role.id})`));
|
|
142
|
+
this.sendTo(ws, {
|
|
143
|
+
type: "welcome",
|
|
144
|
+
roleId: role.id,
|
|
145
|
+
roleName: role.name,
|
|
146
|
+
topic: "",
|
|
147
|
+
// Will be sent when discussion starts
|
|
148
|
+
roles: this.config.roles.map((r) => ({ id: r.id, name: r.name }))
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// ── Wait for Agents ──────────────────────────────────────────────
|
|
152
|
+
async waitForAgents(topic) {
|
|
153
|
+
const totalRoles = this.config.roles.length;
|
|
154
|
+
const deadline = Date.now() + 12e4;
|
|
155
|
+
const checkInterval = 1e3;
|
|
156
|
+
while (this.unassignedRoles.length > 0 && Date.now() < deadline && !this.aborted) {
|
|
157
|
+
const connected = totalRoles - this.unassignedRoles.length;
|
|
158
|
+
process.stdout.write(`\r Agents: ${connected}/${totalRoles} connected. Waiting... (${Math.ceil((deadline - Date.now()) / 1e3)}s remaining) `);
|
|
159
|
+
await sleep(checkInterval);
|
|
160
|
+
}
|
|
161
|
+
process.stdout.write("\r\x1B[2K");
|
|
162
|
+
if (this.unassignedRoles.length > 0) {
|
|
163
|
+
console.log(chalk.dim(` ${this.unassignedRoles.length} role(s) unfilled \u2014 using built-in AI agents:`));
|
|
164
|
+
for (const role of this.unassignedRoles) {
|
|
165
|
+
console.log(chalk.dim(` \u2022 ${role.name} (${role.id}) \u2192 built-in`));
|
|
166
|
+
}
|
|
167
|
+
console.log();
|
|
168
|
+
}
|
|
169
|
+
for (const agent of this.agents.values()) {
|
|
170
|
+
this.sendTo(agent.ws, {
|
|
171
|
+
type: "welcome",
|
|
172
|
+
roleId: agent.roleId,
|
|
173
|
+
roleName: agent.roleName,
|
|
174
|
+
topic,
|
|
175
|
+
roles: this.config.roles.map((r) => ({ id: r.id, name: r.name }))
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// ── Discussion Loop ──────────────────────────────────────────────
|
|
180
|
+
async runDiscussion(topic, maxRounds) {
|
|
181
|
+
for (let round = 1; round <= maxRounds; round++) {
|
|
182
|
+
if (this.aborted) {
|
|
183
|
+
renderUserInterrupt();
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
renderRoundHeader(round, maxRounds);
|
|
187
|
+
this.broadcastAll({ type: "round_start", round, maxRounds });
|
|
188
|
+
let allPassed = true;
|
|
189
|
+
for (const role of this.config.roles) {
|
|
190
|
+
if (this.aborted) break;
|
|
191
|
+
renderAgentSpeaking(role.name, role.id);
|
|
192
|
+
let message;
|
|
193
|
+
const connectedAgent = this.agents.get(role.id);
|
|
194
|
+
if (connectedAgent) {
|
|
195
|
+
message = await this.getRemoteResponse(connectedAgent, role, topic, round, maxRounds);
|
|
196
|
+
} else {
|
|
197
|
+
message = await this.getBuiltinResponse(role, topic, round, maxRounds);
|
|
198
|
+
}
|
|
199
|
+
clearSpeakingLine();
|
|
200
|
+
this.history.push(message);
|
|
201
|
+
if (!message.passed) {
|
|
202
|
+
allPassed = false;
|
|
203
|
+
}
|
|
204
|
+
renderAgentMessage(message);
|
|
205
|
+
this.broadcastAll({ type: "agent_spoke", message: serializeMessage(message) });
|
|
206
|
+
}
|
|
207
|
+
if (allPassed && round > 1) {
|
|
208
|
+
renderConsensus();
|
|
209
|
+
this.broadcastAll({ type: "discussion_end", reason: "consensus" });
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
if (round === maxRounds) {
|
|
213
|
+
renderMaxRounds(maxRounds);
|
|
214
|
+
this.broadcastAll({ type: "discussion_end", reason: "max_rounds" });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
await this.generateSummary(topic);
|
|
218
|
+
}
|
|
219
|
+
async getRemoteResponse(agent, role, _topic, round, maxRounds) {
|
|
220
|
+
this.sendTo(agent.ws, {
|
|
221
|
+
type: "your_turn",
|
|
222
|
+
round,
|
|
223
|
+
maxRounds,
|
|
224
|
+
history: this.history.map(serializeMessage)
|
|
225
|
+
});
|
|
226
|
+
try {
|
|
227
|
+
const response = await Promise.race([
|
|
228
|
+
new Promise((resolve) => {
|
|
229
|
+
this.currentTurnResolve = resolve;
|
|
230
|
+
}),
|
|
231
|
+
sleep(12e4).then(() => ({ content: `[${role.name} timed out]`, passed: true }))
|
|
232
|
+
]);
|
|
233
|
+
return {
|
|
234
|
+
speaker: role.id,
|
|
235
|
+
speakerName: role.name,
|
|
236
|
+
content: response.passed ? "" : response.content,
|
|
237
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
238
|
+
passed: response.passed
|
|
239
|
+
};
|
|
240
|
+
} catch {
|
|
241
|
+
return {
|
|
242
|
+
speaker: role.id,
|
|
243
|
+
speakerName: role.name,
|
|
244
|
+
content: "",
|
|
245
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
246
|
+
passed: true
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
async getBuiltinResponse(role, topic, round, maxRounds) {
|
|
251
|
+
const agent = new HubAgent(role, this.providers, this.config.defaultProvider, this.config.defaultModel);
|
|
252
|
+
return agent.speak(topic, this.history, round, maxRounds);
|
|
253
|
+
}
|
|
254
|
+
async generateSummary(topic) {
|
|
255
|
+
if (this.history.filter((m) => !m.passed && m.speaker !== "system").length === 0) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
const summarizer = new HubAgent(
|
|
259
|
+
this.config.roles[0],
|
|
260
|
+
this.providers,
|
|
261
|
+
this.config.defaultProvider,
|
|
262
|
+
this.config.defaultModel
|
|
263
|
+
);
|
|
264
|
+
try {
|
|
265
|
+
console.log(chalk.dim(" Generating summary..."));
|
|
266
|
+
const summary = await summarizer.summarize(topic, this.history);
|
|
267
|
+
renderSummary(summary);
|
|
268
|
+
this.broadcastAll({ type: "discussion_end", reason: "max_rounds", summary });
|
|
269
|
+
} catch (err) {
|
|
270
|
+
console.log(chalk.red(` Summary generation failed: ${err.message}`));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
// ── Messaging ────────────────────────────────────────────────────
|
|
274
|
+
sendTo(ws, msg) {
|
|
275
|
+
if (ws.readyState === ws.OPEN) {
|
|
276
|
+
ws.send(JSON.stringify(msg));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
broadcastAll(msg) {
|
|
280
|
+
const data = JSON.stringify(msg);
|
|
281
|
+
for (const agent of this.agents.values()) {
|
|
282
|
+
if (agent.ws.readyState === agent.ws.OPEN) {
|
|
283
|
+
agent.ws.send(data);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
};
|
|
288
|
+
function sleep(ms) {
|
|
289
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
290
|
+
}
|
|
291
|
+
function serializeMessage(msg) {
|
|
292
|
+
return {
|
|
293
|
+
...msg,
|
|
294
|
+
timestamp: msg.timestamp instanceof Date ? msg.timestamp : new Date(msg.timestamp)
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
export {
|
|
298
|
+
HubServer
|
|
299
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
theme,
|
|
37
37
|
truncateOutput,
|
|
38
38
|
undoStack
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-GBP4DNBU.js";
|
|
40
40
|
import {
|
|
41
41
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
42
42
|
AUTHOR,
|
|
@@ -56,7 +56,7 @@ import {
|
|
|
56
56
|
REPO_URL,
|
|
57
57
|
SKILLS_DIR_NAME,
|
|
58
58
|
VERSION
|
|
59
|
-
} from "./chunk-
|
|
59
|
+
} from "./chunk-UEOKNL7V.js";
|
|
60
60
|
|
|
61
61
|
// src/index.ts
|
|
62
62
|
import { program } from "commander";
|
|
@@ -1907,7 +1907,7 @@ ${hint}` : "")
|
|
|
1907
1907
|
description: "Run project tests and show structured report",
|
|
1908
1908
|
usage: "/test [command|filter]",
|
|
1909
1909
|
async execute(args, _ctx) {
|
|
1910
|
-
const { executeTests } = await import("./run-tests-
|
|
1910
|
+
const { executeTests } = await import("./run-tests-6VOBLE77.js");
|
|
1911
1911
|
const argStr = args.join(" ").trim();
|
|
1912
1912
|
let testArgs = {};
|
|
1913
1913
|
if (argStr) {
|
|
@@ -5517,7 +5517,7 @@ program.command("web").description("Start Web UI server with browser-based chat
|
|
|
5517
5517
|
console.error("Error: Invalid port number. Must be between 1 and 65535.");
|
|
5518
5518
|
process.exit(1);
|
|
5519
5519
|
}
|
|
5520
|
-
const { startWebServer } = await import("./server-
|
|
5520
|
+
const { startWebServer } = await import("./server-YFR3DO2P.js");
|
|
5521
5521
|
await startWebServer({ port, host: options.host });
|
|
5522
5522
|
});
|
|
5523
5523
|
program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
|
|
@@ -5638,7 +5638,7 @@ program.command("sessions").description("List recent conversation sessions").act
|
|
|
5638
5638
|
}
|
|
5639
5639
|
console.log();
|
|
5640
5640
|
});
|
|
5641
|
-
program.command("hub [topic]").description("Start multi-agent hub (discuss / brainstorm with multiple AI roles)").option("--preset <name>", "Use a built-in role preset (default: tech-review)").option("--roles <file>", "Load roles from a JSON file").option("--provider <name>", "Override default AI provider").option("-m, --model <name>", "Override default model").option("--max-rounds <n>", "Max discussion rounds (default: 10)").option("--presets", "List available role presets").action(async (topic, options) => {
|
|
5641
|
+
program.command("hub [topic]").description("Start multi-agent hub (discuss / brainstorm with multiple AI roles)").option("--preset <name>", "Use a built-in role preset (default: tech-review)").option("--roles <file>", "Load roles from a JSON file").option("--provider <name>", "Override default AI provider").option("-m, --model <name>", "Override default model").option("--max-rounds <n>", "Max discussion rounds (default: 10)").option("--presets", "List available role presets").option("--distributed", "Start WebSocket server so remote aicli instances can join as agents").option("--port <n>", "WebSocket port for distributed mode (default: 9527)", "9527").action(async (topic, options) => {
|
|
5642
5642
|
const config = new ConfigManager();
|
|
5643
5643
|
const registry = new ProviderRegistry();
|
|
5644
5644
|
await registry.initialize(
|
|
@@ -5649,7 +5649,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
|
|
|
5649
5649
|
}),
|
|
5650
5650
|
config.get("customProviders")
|
|
5651
5651
|
);
|
|
5652
|
-
const { startHub } = await import("./hub-
|
|
5652
|
+
const { startHub } = await import("./hub-ZTEJJ4WD.js");
|
|
5653
5653
|
await startHub(
|
|
5654
5654
|
{
|
|
5655
5655
|
topic: topic ?? "",
|
|
@@ -5658,7 +5658,33 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
|
|
|
5658
5658
|
provider: options.provider,
|
|
5659
5659
|
model: options.model,
|
|
5660
5660
|
maxRounds: options.maxRounds ? parseInt(options.maxRounds, 10) : void 0,
|
|
5661
|
-
listPresets: options.presets === true
|
|
5661
|
+
listPresets: options.presets === true,
|
|
5662
|
+
distributed: options.distributed === true,
|
|
5663
|
+
port: options.port ? parseInt(options.port, 10) : void 0
|
|
5664
|
+
},
|
|
5665
|
+
config,
|
|
5666
|
+
registry
|
|
5667
|
+
);
|
|
5668
|
+
});
|
|
5669
|
+
program.command("join").description("Join a running hub as a remote AI agent").option("--port <n>", "Hub WebSocket port (default: 9527)", "9527").option("--host <name>", "Hub host (default: localhost)", "localhost").option("--role <id>", "Request a specific role (e.g. architect, developer)").option("--provider <name>", "Override AI provider for this agent").option("-m, --model <name>", "Override model for this agent").action(async (options) => {
|
|
5670
|
+
const config = new ConfigManager();
|
|
5671
|
+
const registry = new ProviderRegistry();
|
|
5672
|
+
await registry.initialize(
|
|
5673
|
+
(id) => config.getApiKey(id),
|
|
5674
|
+
(id) => ({
|
|
5675
|
+
baseUrl: config.get("customBaseUrls")[id],
|
|
5676
|
+
timeout: config.get("timeouts")[id]
|
|
5677
|
+
}),
|
|
5678
|
+
config.get("customProviders")
|
|
5679
|
+
);
|
|
5680
|
+
const { joinHub } = await import("./agent-client-OGXLZR5O.js");
|
|
5681
|
+
await joinHub(
|
|
5682
|
+
{
|
|
5683
|
+
port: parseInt(options.port ?? "9527", 10),
|
|
5684
|
+
host: options.host,
|
|
5685
|
+
role: options.role,
|
|
5686
|
+
provider: options.provider,
|
|
5687
|
+
model: options.model
|
|
5662
5688
|
},
|
|
5663
5689
|
config,
|
|
5664
5690
|
registry
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
spawnAgentContext,
|
|
28
28
|
truncateOutput,
|
|
29
29
|
undoStack
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-GBP4DNBU.js";
|
|
31
31
|
import {
|
|
32
32
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
33
33
|
CONTEXT_FILE_CANDIDATES,
|
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
PLAN_MODE_SYSTEM_ADDON,
|
|
40
40
|
SKILLS_DIR_NAME,
|
|
41
41
|
VERSION
|
|
42
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-UEOKNL7V.js";
|
|
43
43
|
import {
|
|
44
44
|
AuthManager
|
|
45
45
|
} from "./chunk-CPLT6CD3.js";
|
|
@@ -1438,7 +1438,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
1438
1438
|
case "test": {
|
|
1439
1439
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
1440
1440
|
try {
|
|
1441
|
-
const { executeTests } = await import("./run-tests-
|
|
1441
|
+
const { executeTests } = await import("./run-tests-6VOBLE77.js");
|
|
1442
1442
|
const argStr = args.join(" ").trim();
|
|
1443
1443
|
let testArgs = {};
|
|
1444
1444
|
if (argStr) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jinzd-ai-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Cross-platform REPL-style AI CLI with multi-provider support",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -57,6 +57,8 @@
|
|
|
57
57
|
"dist/auth-*.js",
|
|
58
58
|
"dist/run-tests-*.js",
|
|
59
59
|
"dist/hub-*.js",
|
|
60
|
+
"dist/hub-server-*.js",
|
|
61
|
+
"dist/agent-client-*.js",
|
|
60
62
|
"dist/web/",
|
|
61
63
|
"README.md"
|
|
62
64
|
],
|