palabre 0.7.0 → 0.8.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/README.md +102 -68
- package/dist/adapters/cli-pty.js +1 -1
- package/dist/adapters/cli.js +33 -3
- package/dist/adapters/ollama.js +1 -1
- package/dist/agentRegistry.js +3 -3
- package/dist/args.js +21 -1
- package/dist/config.js +58 -24
- package/dist/configWizard.js +12 -2
- package/dist/context.js +5 -1
- package/dist/discovery.js +3 -3
- package/dist/doctor.js +4 -1
- package/dist/history.js +85 -0
- package/dist/index.js +742 -94
- package/dist/messages/agents.js +4 -2
- package/dist/messages/common.js +4 -0
- package/dist/messages/config.js +18 -8
- package/dist/messages/help.js +116 -10
- package/dist/messages/index.js +2 -0
- package/dist/messages/init.js +2 -2
- package/dist/messages/new.js +14 -0
- package/dist/messages/output.js +10 -0
- package/dist/messages/preview.js +4 -2
- package/dist/messages/prompt.js +46 -2
- package/dist/messages/renderers.js +2 -2
- package/dist/messages/tui.js +228 -0
- package/dist/messages/update.js +16 -2
- package/dist/new.js +158 -4
- package/dist/orchestrator.js +168 -9
- package/dist/output.js +31 -8
- package/dist/presets.js +39 -39
- package/dist/prompt.js +61 -10
- package/dist/renderers/console.js +39 -3
- package/dist/renderers/ndjson.js +30 -1
- package/dist/renderers/tui.js +1055 -0
- package/dist/tuiState.js +31 -0
- package/dist/update.js +2 -0
- package/package.json +2 -1
- package/palabre.config.example.json +0 -17
package/dist/orchestrator.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createAgent } from "./adapters/index.js";
|
|
2
2
|
import { AdapterError } from "./errors.js";
|
|
3
3
|
import { createTranslator } from "./i18n.js";
|
|
4
|
+
export const MAX_ASK_AGENTS = 4;
|
|
4
5
|
/**
|
|
5
6
|
* Point d'entrée de l'orchestration.
|
|
6
7
|
* Lance le ping-pong entre `agentA` et `agentB` pendant `options.turns` tours,
|
|
@@ -104,7 +105,8 @@ export async function runDebate(config, options, renderer, messages = createTran
|
|
|
104
105
|
try {
|
|
105
106
|
const cancellation = cancellationFailureIfAborted(options, messages, {
|
|
106
107
|
phase: "summary",
|
|
107
|
-
agent: options
|
|
108
|
+
agent: resolveSummaryAgentName(options),
|
|
109
|
+
role: summaryRole(),
|
|
108
110
|
turn: transcript.length + 1
|
|
109
111
|
});
|
|
110
112
|
if (cancellation) {
|
|
@@ -121,7 +123,8 @@ export async function runDebate(config, options, renderer, messages = createTran
|
|
|
121
123
|
catch (error) {
|
|
122
124
|
failure = toDebateFailure(error, {
|
|
123
125
|
phase: "summary",
|
|
124
|
-
agent: options
|
|
126
|
+
agent: resolveSummaryAgentName(options),
|
|
127
|
+
role: summaryRole(),
|
|
125
128
|
turn: transcript.length + 1
|
|
126
129
|
});
|
|
127
130
|
renderer?.error(failure);
|
|
@@ -135,6 +138,143 @@ export async function runDebate(config, options, renderer, messages = createTran
|
|
|
135
138
|
failure
|
|
136
139
|
};
|
|
137
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Lance le mode ask : plusieurs agents répondent indépendamment au même sujet,
|
|
143
|
+
* puis un agent de synthèse résume fidèlement chaque réponse et les compare.
|
|
144
|
+
*/
|
|
145
|
+
export async function runAsk(config, options, renderer, messages = createTranslator("fr")) {
|
|
146
|
+
const askAgentNames = resolveAskAgentNames(options);
|
|
147
|
+
if (askAgentNames.length === 0) {
|
|
148
|
+
throw new Error(messages.common.noAgentDefined("ask agent"));
|
|
149
|
+
}
|
|
150
|
+
if (askAgentNames.length > MAX_ASK_AGENTS) {
|
|
151
|
+
throw new Error(messages.common.tooManyAskAgents(MAX_ASK_AGENTS));
|
|
152
|
+
}
|
|
153
|
+
const agentEntries = askAgentNames.map((name) => {
|
|
154
|
+
const agentConfig = withRuntimeOverrides(config.agents[name], modelForAgent(options, name), options.pullModels);
|
|
155
|
+
if (!agentConfig) {
|
|
156
|
+
throw new Error(messages.common.unknownAgent(name));
|
|
157
|
+
}
|
|
158
|
+
return [name, agentConfig];
|
|
159
|
+
});
|
|
160
|
+
warnIfOllamaHasNoContext(options, agentEntries.map(([name, agentConfig]) => [name, agentConfig]), renderer, messages);
|
|
161
|
+
renderer?.start(options, agentEntries.map(([name, agentConfig]) => ({
|
|
162
|
+
name,
|
|
163
|
+
role: agentConfig.role,
|
|
164
|
+
type: agentConfig.type
|
|
165
|
+
})));
|
|
166
|
+
const agents = agentEntries.map(([name, agentConfig]) => createAgent(name, agentConfig));
|
|
167
|
+
const transcript = [];
|
|
168
|
+
for (let index = 0; index < agents.length; index += 1) {
|
|
169
|
+
const current = agents[index];
|
|
170
|
+
const response = index + 1;
|
|
171
|
+
const cancellation = cancellationFailureIfAborted(options, messages, {
|
|
172
|
+
phase: "ask",
|
|
173
|
+
agent: current.name,
|
|
174
|
+
role: current.role,
|
|
175
|
+
turn: response
|
|
176
|
+
});
|
|
177
|
+
if (cancellation) {
|
|
178
|
+
renderer?.error(cancellation);
|
|
179
|
+
return {
|
|
180
|
+
options,
|
|
181
|
+
messages: transcript,
|
|
182
|
+
failure: cancellation
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
if (renderer?.askResponseStart) {
|
|
186
|
+
renderer.askResponseStart(response, agents.length, current.name, current.role);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
renderer?.turnStart(response, agents.length, current.name, current.role);
|
|
190
|
+
}
|
|
191
|
+
renderer?.thinkingStart(current.name, current.role);
|
|
192
|
+
let agentResponse;
|
|
193
|
+
try {
|
|
194
|
+
agentResponse = await current.generate({
|
|
195
|
+
topic: options.topic,
|
|
196
|
+
turn: response,
|
|
197
|
+
totalTurns: agents.length,
|
|
198
|
+
selfName: current.name,
|
|
199
|
+
peerName: "independent-agents",
|
|
200
|
+
selfRole: current.role,
|
|
201
|
+
mode: "ask",
|
|
202
|
+
language: options.language,
|
|
203
|
+
session: options.session,
|
|
204
|
+
files: options.files,
|
|
205
|
+
transcript: [],
|
|
206
|
+
signal: options.signal
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
const failure = toDebateFailure(error, {
|
|
211
|
+
phase: "ask",
|
|
212
|
+
agent: current.name,
|
|
213
|
+
role: current.role,
|
|
214
|
+
turn: response
|
|
215
|
+
});
|
|
216
|
+
renderer?.error(failure);
|
|
217
|
+
return {
|
|
218
|
+
options,
|
|
219
|
+
messages: transcript,
|
|
220
|
+
failure
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
renderer?.thinkingEnd();
|
|
225
|
+
}
|
|
226
|
+
const message = {
|
|
227
|
+
agent: current.name,
|
|
228
|
+
role: current.role,
|
|
229
|
+
content: agentResponse.content,
|
|
230
|
+
createdAt: new Date().toISOString()
|
|
231
|
+
};
|
|
232
|
+
transcript.push(message);
|
|
233
|
+
if (renderer?.askResponseMessage) {
|
|
234
|
+
renderer.askResponseMessage(message.content);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
renderer?.message(message.content);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
let summary;
|
|
241
|
+
let failure;
|
|
242
|
+
if (options.summaryEnabled) {
|
|
243
|
+
try {
|
|
244
|
+
const summaryAgentName = resolveSummaryAgentName(options);
|
|
245
|
+
const cancellation = cancellationFailureIfAborted(options, messages, {
|
|
246
|
+
phase: "summary",
|
|
247
|
+
agent: summaryAgentName,
|
|
248
|
+
role: summaryRole(),
|
|
249
|
+
turn: transcript.length + 1
|
|
250
|
+
});
|
|
251
|
+
if (cancellation) {
|
|
252
|
+
renderer?.error(cancellation);
|
|
253
|
+
return {
|
|
254
|
+
options,
|
|
255
|
+
messages: transcript,
|
|
256
|
+
failure: cancellation
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
summary = await generateSummary(config, options, transcript, renderer, messages);
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
failure = toDebateFailure(error, {
|
|
263
|
+
phase: "summary",
|
|
264
|
+
agent: resolveSummaryAgentName(options),
|
|
265
|
+
role: summaryRole(),
|
|
266
|
+
turn: transcript.length + 1
|
|
267
|
+
});
|
|
268
|
+
renderer?.error(failure);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return {
|
|
272
|
+
options,
|
|
273
|
+
messages: transcript,
|
|
274
|
+
summary,
|
|
275
|
+
failure
|
|
276
|
+
};
|
|
277
|
+
}
|
|
138
278
|
/**
|
|
139
279
|
* Heuristique d'arrêt sur accord explicite.
|
|
140
280
|
* Ne s'active qu'après un tour complet (nombre pair de messages) pour éviter les faux positifs.
|
|
@@ -189,22 +329,23 @@ function warnIfOllamaHasNoContext(options, agents, renderer, messages = createTr
|
|
|
189
329
|
* @throws {Error} si l'agent de synthèse est absent de `config.agents`.
|
|
190
330
|
*/
|
|
191
331
|
async function generateSummary(config, options, transcript, renderer, messages = createTranslator("fr")) {
|
|
192
|
-
const summaryAgentName = options
|
|
332
|
+
const summaryAgentName = resolveSummaryAgentName(options);
|
|
193
333
|
const summaryModel = options.summaryModel ?? modelForAgent(options, summaryAgentName);
|
|
194
334
|
const summaryConfig = withRuntimeOverrides(config.agents[summaryAgentName], summaryModel, options.pullModels);
|
|
195
335
|
if (!summaryConfig) {
|
|
196
336
|
throw new Error(messages.orchestrator.unknownSummaryAgent(summaryAgentName));
|
|
197
337
|
}
|
|
198
338
|
const summaryAgent = createAgent(summaryAgentName, summaryConfig);
|
|
199
|
-
|
|
200
|
-
renderer?.
|
|
339
|
+
const role = summaryRole();
|
|
340
|
+
renderer?.summaryStart(summaryAgent.name, role);
|
|
341
|
+
renderer?.thinkingStart(summaryAgent.name, role);
|
|
201
342
|
const response = await summaryAgent.generate({
|
|
202
343
|
topic: options.topic,
|
|
203
344
|
turn: transcript.length + 1,
|
|
204
|
-
totalTurns: options.turns,
|
|
345
|
+
totalTurns: options.mode === "ask" ? transcript.length : options.turns,
|
|
205
346
|
selfName: summaryAgent.name,
|
|
206
|
-
peerName: "transcript",
|
|
207
|
-
selfRole:
|
|
347
|
+
peerName: options.mode === "ask" ? "ask-responses" : "transcript",
|
|
348
|
+
selfRole: role,
|
|
208
349
|
mode: "summary",
|
|
209
350
|
language: options.language,
|
|
210
351
|
session: options.session,
|
|
@@ -214,13 +355,16 @@ async function generateSummary(config, options, transcript, renderer, messages =
|
|
|
214
355
|
}).finally(() => renderer?.thinkingEnd());
|
|
215
356
|
const summary = {
|
|
216
357
|
agent: summaryAgent.name,
|
|
217
|
-
role
|
|
358
|
+
role,
|
|
218
359
|
content: response.content,
|
|
219
360
|
createdAt: new Date().toISOString()
|
|
220
361
|
};
|
|
221
362
|
renderer?.message(summary.content);
|
|
222
363
|
return summary;
|
|
223
364
|
}
|
|
365
|
+
function summaryRole() {
|
|
366
|
+
return "summarizer";
|
|
367
|
+
}
|
|
224
368
|
function cancellationFailureIfAborted(options, messages, context) {
|
|
225
369
|
if (!options.signal?.aborted) {
|
|
226
370
|
return undefined;
|
|
@@ -234,6 +378,21 @@ function cancellationFailureIfAborted(options, messages, context) {
|
|
|
234
378
|
message: messages.orchestrator.cancelled
|
|
235
379
|
};
|
|
236
380
|
}
|
|
381
|
+
function resolveAskAgentNames(options) {
|
|
382
|
+
const agents = options.askAgents && options.askAgents.length > 0
|
|
383
|
+
? options.askAgents
|
|
384
|
+
: [options.agentA, options.agentB];
|
|
385
|
+
return agents.filter((agent, index) => Boolean(agent) && agents.indexOf(agent) === index);
|
|
386
|
+
}
|
|
387
|
+
function resolveSummaryAgentName(options) {
|
|
388
|
+
if (options.summaryAgent) {
|
|
389
|
+
return options.summaryAgent;
|
|
390
|
+
}
|
|
391
|
+
if (options.mode === "ask" && options.askAgents && options.askAgents.length > 0) {
|
|
392
|
+
return options.askAgents[options.askAgents.length - 1] ?? options.agentB;
|
|
393
|
+
}
|
|
394
|
+
return options.agentB;
|
|
395
|
+
}
|
|
237
396
|
function toDebateFailure(error, context) {
|
|
238
397
|
if (error instanceof AdapterError) {
|
|
239
398
|
return {
|
package/dist/output.js
CHANGED
|
@@ -7,7 +7,8 @@ import { createTranslator } from "./i18n.js";
|
|
|
7
7
|
*/
|
|
8
8
|
export async function writeDebateMarkdown(outputDir, options, debateMessages, summary, stopReason, messages = createTranslator("fr"), failure) {
|
|
9
9
|
const safeDate = new Date().toISOString().replace(/[:.]/g, "-");
|
|
10
|
-
const
|
|
10
|
+
const extension = options.mode === "ask" ? "ask" : "debate";
|
|
11
|
+
const fileName = `palabre-${slugifyTopic(options.topic)}-${safeDate}.${extension}.md`;
|
|
11
12
|
const filePath = path.resolve(outputDir, fileName);
|
|
12
13
|
await mkdir(path.dirname(filePath), { recursive: true });
|
|
13
14
|
await writeFile(filePath, renderDebateMarkdown(options, debateMessages, summary, stopReason, messages, failure), "utf8");
|
|
@@ -30,7 +31,7 @@ function slugifyTopic(topic) {
|
|
|
30
31
|
*/
|
|
31
32
|
export function renderDebateMarkdown(options, debateMessages, summary, stopReason, messages = createTranslator("fr"), failure) {
|
|
32
33
|
const lines = [
|
|
33
|
-
messages.output.title,
|
|
34
|
+
options.mode === "ask" ? messages.output.askTitle : messages.output.title,
|
|
34
35
|
"",
|
|
35
36
|
...renderSessionHeader(options, debateMessages, stopReason, messages),
|
|
36
37
|
"",
|
|
@@ -38,7 +39,7 @@ export function renderDebateMarkdown(options, debateMessages, summary, stopReaso
|
|
|
38
39
|
"",
|
|
39
40
|
...renderFileList(options.files, messages),
|
|
40
41
|
"",
|
|
41
|
-
messages.output.exchangesTitle,
|
|
42
|
+
options.mode === "ask" ? messages.output.askResponsesTitle : messages.output.exchangesTitle,
|
|
42
43
|
""
|
|
43
44
|
];
|
|
44
45
|
for (const message of debateMessages) {
|
|
@@ -90,12 +91,19 @@ function normalizeMarkdownForWindowsPreview(content) {
|
|
|
90
91
|
function renderSessionHeader(options, debateMessages, stopReason, messages) {
|
|
91
92
|
const rows = [
|
|
92
93
|
[messages.output.fields.subject, options.topic],
|
|
93
|
-
[messages.output.fields.
|
|
94
|
+
[messages.output.fields.mode, options.mode],
|
|
95
|
+
[messages.output.fields.agents, formatAgentsForHeader(options)],
|
|
94
96
|
[messages.output.fields.autoPullOllama, options.pullModels ? messages.output.yes : messages.output.no],
|
|
95
|
-
[messages.output.fields.summary, options.summaryEnabled ? options
|
|
96
|
-
[
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
[messages.output.fields.summary, options.summaryEnabled ? formatSummaryAgent(options) : messages.output.disabled],
|
|
98
|
+
[
|
|
99
|
+
options.mode === "ask" ? messages.output.fields.requestedResponses : messages.output.fields.requestedTurns,
|
|
100
|
+
String(options.mode === "ask" ? options.askAgents?.length ?? debateMessages.length : options.turns)
|
|
101
|
+
],
|
|
102
|
+
[
|
|
103
|
+
options.mode === "ask" ? messages.output.fields.receivedResponses : messages.output.fields.playedTurns,
|
|
104
|
+
String(debateMessages.length)
|
|
105
|
+
],
|
|
106
|
+
[messages.output.fields.earlyStop, options.mode === "debate" ? stopReason ?? messages.output.no : messages.output.no],
|
|
99
107
|
[messages.output.fields.localDate, options.session.localDate],
|
|
100
108
|
[messages.output.fields.timeZone, options.session.timeZone],
|
|
101
109
|
[messages.output.fields.cwd, options.session.cwd],
|
|
@@ -116,3 +124,18 @@ function renderFileList(files, messages) {
|
|
|
116
124
|
}
|
|
117
125
|
return files.map((file) => `- \`${file.path}\` (${file.sizeBytes} ${messages.output.fileSizeUnit})`);
|
|
118
126
|
}
|
|
127
|
+
function formatSummaryAgent(options) {
|
|
128
|
+
if (options.summaryAgent) {
|
|
129
|
+
return options.summaryAgent;
|
|
130
|
+
}
|
|
131
|
+
if (options.mode === "ask" && options.askAgents && options.askAgents.length > 0) {
|
|
132
|
+
return options.askAgents[options.askAgents.length - 1] ?? options.agentB;
|
|
133
|
+
}
|
|
134
|
+
return options.agentB;
|
|
135
|
+
}
|
|
136
|
+
function formatAgentsForHeader(options) {
|
|
137
|
+
if (options.mode === "ask") {
|
|
138
|
+
return (options.askAgents && options.askAgents.length > 0 ? options.askAgents : [options.agentA, options.agentB]).join(", ");
|
|
139
|
+
}
|
|
140
|
+
return `${options.agentA} <-> ${options.agentB}`;
|
|
141
|
+
}
|
package/dist/presets.js
CHANGED
|
@@ -16,6 +16,14 @@ const presets = {
|
|
|
16
16
|
agentA: "opencode",
|
|
17
17
|
agentB: "codex"
|
|
18
18
|
},
|
|
19
|
+
"codex-vibe": {
|
|
20
|
+
agentA: "codex",
|
|
21
|
+
agentB: "vibe"
|
|
22
|
+
},
|
|
23
|
+
"vibe-codex": {
|
|
24
|
+
agentA: "vibe",
|
|
25
|
+
agentB: "codex"
|
|
26
|
+
},
|
|
19
27
|
"codex-antigravity": {
|
|
20
28
|
agentA: "codex",
|
|
21
29
|
agentB: "antigravity"
|
|
@@ -32,6 +40,14 @@ const presets = {
|
|
|
32
40
|
agentA: "opencode",
|
|
33
41
|
agentB: "claude"
|
|
34
42
|
},
|
|
43
|
+
"claude-vibe": {
|
|
44
|
+
agentA: "claude",
|
|
45
|
+
agentB: "vibe"
|
|
46
|
+
},
|
|
47
|
+
"vibe-claude": {
|
|
48
|
+
agentA: "vibe",
|
|
49
|
+
agentB: "claude"
|
|
50
|
+
},
|
|
35
51
|
"claude-antigravity": {
|
|
36
52
|
agentA: "claude",
|
|
37
53
|
agentB: "antigravity"
|
|
@@ -40,30 +56,30 @@ const presets = {
|
|
|
40
56
|
agentA: "antigravity",
|
|
41
57
|
agentB: "claude"
|
|
42
58
|
},
|
|
43
|
-
"
|
|
44
|
-
agentA: "gemini",
|
|
45
|
-
agentB: "opencode"
|
|
46
|
-
},
|
|
47
|
-
"opencode-gemini": {
|
|
59
|
+
"opencode-antigravity": {
|
|
48
60
|
agentA: "opencode",
|
|
49
|
-
agentB: "gemini"
|
|
50
|
-
},
|
|
51
|
-
"gemini-antigravity": {
|
|
52
|
-
agentA: "gemini",
|
|
53
61
|
agentB: "antigravity"
|
|
54
62
|
},
|
|
55
|
-
"antigravity-
|
|
63
|
+
"antigravity-opencode": {
|
|
56
64
|
agentA: "antigravity",
|
|
57
|
-
agentB: "
|
|
65
|
+
agentB: "opencode"
|
|
58
66
|
},
|
|
59
|
-
"opencode-
|
|
67
|
+
"opencode-vibe": {
|
|
60
68
|
agentA: "opencode",
|
|
61
|
-
agentB: "
|
|
69
|
+
agentB: "vibe"
|
|
62
70
|
},
|
|
63
|
-
"
|
|
64
|
-
agentA: "
|
|
71
|
+
"vibe-opencode": {
|
|
72
|
+
agentA: "vibe",
|
|
65
73
|
agentB: "opencode"
|
|
66
74
|
},
|
|
75
|
+
"antigravity-vibe": {
|
|
76
|
+
agentA: "antigravity",
|
|
77
|
+
agentB: "vibe"
|
|
78
|
+
},
|
|
79
|
+
"vibe-antigravity": {
|
|
80
|
+
agentA: "vibe",
|
|
81
|
+
agentB: "antigravity"
|
|
82
|
+
},
|
|
67
83
|
"opencode-ollama": {
|
|
68
84
|
agentA: "opencode",
|
|
69
85
|
agentB: "ollama-local"
|
|
@@ -72,6 +88,14 @@ const presets = {
|
|
|
72
88
|
agentA: "ollama-local",
|
|
73
89
|
agentB: "opencode"
|
|
74
90
|
},
|
|
91
|
+
"vibe-ollama": {
|
|
92
|
+
agentA: "vibe",
|
|
93
|
+
agentB: "ollama-local"
|
|
94
|
+
},
|
|
95
|
+
"ollama-vibe": {
|
|
96
|
+
agentA: "ollama-local",
|
|
97
|
+
agentB: "vibe"
|
|
98
|
+
},
|
|
75
99
|
"codex-ollama": {
|
|
76
100
|
agentA: "codex",
|
|
77
101
|
agentB: "ollama-local"
|
|
@@ -88,14 +112,6 @@ const presets = {
|
|
|
88
112
|
agentA: "ollama-local",
|
|
89
113
|
agentB: "claude"
|
|
90
114
|
},
|
|
91
|
-
"gemini-ollama": {
|
|
92
|
-
agentA: "gemini",
|
|
93
|
-
agentB: "ollama-local"
|
|
94
|
-
},
|
|
95
|
-
"ollama-gemini": {
|
|
96
|
-
agentA: "ollama-local",
|
|
97
|
-
agentB: "gemini"
|
|
98
|
-
},
|
|
99
115
|
"antigravity-ollama": {
|
|
100
116
|
agentA: "antigravity",
|
|
101
117
|
agentB: "ollama-local"
|
|
@@ -103,22 +119,6 @@ const presets = {
|
|
|
103
119
|
"ollama-antigravity": {
|
|
104
120
|
agentA: "ollama-local",
|
|
105
121
|
agentB: "antigravity"
|
|
106
|
-
},
|
|
107
|
-
"codex-gemini": {
|
|
108
|
-
agentA: "codex",
|
|
109
|
-
agentB: "gemini"
|
|
110
|
-
},
|
|
111
|
-
"gemini-codex": {
|
|
112
|
-
agentA: "gemini",
|
|
113
|
-
agentB: "codex"
|
|
114
|
-
},
|
|
115
|
-
"claude-gemini": {
|
|
116
|
-
agentA: "claude",
|
|
117
|
-
agentB: "gemini"
|
|
118
|
-
},
|
|
119
|
-
"gemini-claude": {
|
|
120
|
-
agentA: "gemini",
|
|
121
|
-
agentB: "claude"
|
|
122
122
|
}
|
|
123
123
|
};
|
|
124
124
|
/** Retourne la paire d'agents pour `name`. Lève une erreur avec la liste des presets disponibles si inconnu. */
|
package/dist/prompt.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { createTranslator } from "./i18n.js";
|
|
2
2
|
/**
|
|
3
3
|
* Formate le prompt complet transmis à l'adapter.
|
|
4
|
-
* Dispatche vers le format
|
|
4
|
+
* Dispatche vers le format ask ou synthèse si demandé, sinon construit le prompt de débat standard.
|
|
5
5
|
*/
|
|
6
6
|
export function formatAgentPrompt(input) {
|
|
7
7
|
const messages = createTranslator(input.language ?? "fr").prompt;
|
|
8
8
|
if (input.mode === "summary") {
|
|
9
9
|
return formatSummaryPrompt(input, messages);
|
|
10
10
|
}
|
|
11
|
+
if (input.mode === "ask") {
|
|
12
|
+
return formatAskPrompt(input, messages);
|
|
13
|
+
}
|
|
11
14
|
const transcript = formatTranscript(input.transcript);
|
|
12
15
|
return [
|
|
13
16
|
messages.subject(input.topic),
|
|
@@ -41,13 +44,43 @@ export function formatAgentPrompt(input) {
|
|
|
41
44
|
.filter(Boolean)
|
|
42
45
|
.join("\n");
|
|
43
46
|
}
|
|
47
|
+
/** Formate le prompt d'une réponse indépendante en mode ask. */
|
|
48
|
+
function formatAskPrompt(input, messages) {
|
|
49
|
+
return [
|
|
50
|
+
messages.subject(input.topic),
|
|
51
|
+
"",
|
|
52
|
+
messages.askIntro(input.selfName),
|
|
53
|
+
messages.role(input.selfName, input.selfRole),
|
|
54
|
+
messages.roleInstruction(input.selfRole),
|
|
55
|
+
"",
|
|
56
|
+
messages.sessionTitle,
|
|
57
|
+
messages.sessionSource,
|
|
58
|
+
messages.localDate(input.session.localDate),
|
|
59
|
+
messages.timeZone(input.session.timeZone),
|
|
60
|
+
messages.cwd(input.session.cwd),
|
|
61
|
+
messages.sessionStartedAt(input.session.startedAt),
|
|
62
|
+
"",
|
|
63
|
+
messages.responseLanguageInstruction,
|
|
64
|
+
"",
|
|
65
|
+
messages.objectiveTitle,
|
|
66
|
+
...messages.askObjectives,
|
|
67
|
+
"",
|
|
68
|
+
input.files.length > 0 ? messages.fileContextTitle : "",
|
|
69
|
+
formatFileContext(input.files),
|
|
70
|
+
input.files.length > 0 ? "" : "",
|
|
71
|
+
messages.answerTitle
|
|
72
|
+
]
|
|
73
|
+
.filter(Boolean)
|
|
74
|
+
.join("\n");
|
|
75
|
+
}
|
|
44
76
|
/** Formate le prompt de synthèse finale. Impose un format structuré : Consensus / Désaccords / Actions / Conclusion. */
|
|
45
77
|
function formatSummaryPrompt(input, messages) {
|
|
46
78
|
const transcript = formatTranscript(input.transcript);
|
|
79
|
+
const isAskSummary = input.peerName === "ask-responses";
|
|
47
80
|
return [
|
|
48
81
|
messages.subject(input.topic),
|
|
49
82
|
"",
|
|
50
|
-
messages.summaryIntro(input.selfName),
|
|
83
|
+
isAskSummary ? messages.askSummaryIntro(input.selfName) : messages.summaryIntro(input.selfName),
|
|
51
84
|
messages.role(input.selfName, input.selfRole),
|
|
52
85
|
messages.roleInstruction("summarizer"),
|
|
53
86
|
"",
|
|
@@ -61,29 +94,47 @@ function formatSummaryPrompt(input, messages) {
|
|
|
61
94
|
messages.responseLanguageInstruction,
|
|
62
95
|
"",
|
|
63
96
|
messages.objectiveTitle,
|
|
64
|
-
...messages.summaryObjectives,
|
|
97
|
+
...(isAskSummary ? messages.askSummaryObjectives : messages.summaryObjectives),
|
|
65
98
|
"",
|
|
66
99
|
input.files.length > 0 ? messages.fileContextTitle : "",
|
|
67
100
|
formatFileContext(input.files),
|
|
68
101
|
input.files.length > 0 ? "" : "",
|
|
69
|
-
messages.transcriptTitle,
|
|
102
|
+
isAskSummary ? messages.askResponsesTitle : messages.transcriptTitle,
|
|
70
103
|
transcript || messages.noMessage,
|
|
71
104
|
"",
|
|
72
105
|
messages.expectedFormatTitle,
|
|
106
|
+
...(isAskSummary ? askSummaryHeadings(messages) : debateSummaryHeadings(messages)),
|
|
107
|
+
"",
|
|
108
|
+
isAskSummary ? messages.askFinalProseInstruction : messages.finalProseInstruction,
|
|
109
|
+
"",
|
|
110
|
+
messages.summaryAnswerTitle
|
|
111
|
+
]
|
|
112
|
+
.filter(Boolean)
|
|
113
|
+
.join("\n");
|
|
114
|
+
}
|
|
115
|
+
function debateSummaryHeadings(messages) {
|
|
116
|
+
return [
|
|
73
117
|
messages.consensusHeading,
|
|
74
118
|
"",
|
|
75
119
|
messages.disagreementsHeading,
|
|
76
120
|
"",
|
|
77
121
|
messages.actionsHeading,
|
|
78
122
|
"",
|
|
79
|
-
messages.conclusionHeading
|
|
123
|
+
messages.conclusionHeading
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
function askSummaryHeadings(messages) {
|
|
127
|
+
return [
|
|
128
|
+
messages.askAgentSummariesHeading,
|
|
80
129
|
"",
|
|
81
|
-
messages.
|
|
130
|
+
messages.askComparisonHeading,
|
|
82
131
|
"",
|
|
83
|
-
messages.
|
|
84
|
-
|
|
85
|
-
.
|
|
86
|
-
|
|
132
|
+
messages.askWatchpointsHeading,
|
|
133
|
+
"",
|
|
134
|
+
messages.actionsHeading,
|
|
135
|
+
"",
|
|
136
|
+
messages.conclusionHeading
|
|
137
|
+
];
|
|
87
138
|
}
|
|
88
139
|
/** Formate les fichiers projet en blocs de code annotés pour l'injection dans le prompt. */
|
|
89
140
|
function formatFileContext(files) {
|
|
@@ -33,7 +33,7 @@ class PrettyConsoleRenderer {
|
|
|
33
33
|
this.c("cyan", `┌─ ${title} ${"─".repeat(Math.max(1, 54 - title.length))}`),
|
|
34
34
|
this.c("cyan", `│`) + ` ${this.messages.renderers.subject(options.topic)}`,
|
|
35
35
|
this.c("cyan", `│`) + ` ${this.messages.renderers.agents(formatAgentPair(options, agents))}`,
|
|
36
|
-
this.c("cyan", `│`) + ` ${this.messages.renderers.responsesSummary(options
|
|
36
|
+
this.c("cyan", `│`) + ` ${this.messages.renderers.responsesSummary(formatResponseCount(options), formatSummary(options, this.messages))}`,
|
|
37
37
|
this.c("cyan", `│`) + ` ${this.messages.renderers.context(formatContext(options, this.messages))}`,
|
|
38
38
|
this.c("cyan", `│`) + ` ${this.messages.renderers.options(options.earlyStopOnAgreement, options.pullModels)}`,
|
|
39
39
|
this.c("cyan", `└${"─".repeat(57)}`),
|
|
@@ -58,6 +58,15 @@ class PrettyConsoleRenderer {
|
|
|
58
58
|
""
|
|
59
59
|
].join("\n"));
|
|
60
60
|
}
|
|
61
|
+
askResponseStart(response, totalResponses, agent, role) {
|
|
62
|
+
this.renderingSummary = false;
|
|
63
|
+
process.stdout.write([
|
|
64
|
+
"",
|
|
65
|
+
this.c("orange", `◆ ${agent}`) + this.dim(` · ${role} · réponse ${response}/${totalResponses}`),
|
|
66
|
+
this.dim("─".repeat(60)),
|
|
67
|
+
""
|
|
68
|
+
].join("\n"));
|
|
69
|
+
}
|
|
61
70
|
/** Démarre le spinner de réflexion (ou affiche une ligne fixe si non interactif). */
|
|
62
71
|
thinkingStart(agent, role) {
|
|
63
72
|
this.thinkingEnd();
|
|
@@ -89,6 +98,9 @@ class PrettyConsoleRenderer {
|
|
|
89
98
|
const trimmed = content.trim();
|
|
90
99
|
process.stdout.write(`${this.renderingSummary ? this.formatSummaryMessage(trimmed) : trimmed}\n`);
|
|
91
100
|
}
|
|
101
|
+
askResponseMessage(content) {
|
|
102
|
+
this.message(content);
|
|
103
|
+
}
|
|
92
104
|
/** Affiche l'en-tête de section synthèse et active le mode formatage de résumé. */
|
|
93
105
|
summaryStart(agent, role) {
|
|
94
106
|
this.renderingSummary = true;
|
|
@@ -153,7 +165,7 @@ class PlainConsoleRenderer {
|
|
|
153
165
|
start(options, agents = []) {
|
|
154
166
|
process.stdout.write(this.messages.renderers.subject(options.topic) + "\n");
|
|
155
167
|
process.stdout.write(this.messages.renderers.agents(formatAgentPair(options, agents)) + "\n");
|
|
156
|
-
process.stdout.write(this.messages.renderers.responsesSummaryContext(options
|
|
168
|
+
process.stdout.write(this.messages.renderers.responsesSummaryContext(formatResponseCount(options), formatSummary(options, this.messages), formatContext(options, this.messages)) + "\n");
|
|
157
169
|
}
|
|
158
170
|
/** Écrit un avertissement sur `stderr`. */
|
|
159
171
|
warning(message) {
|
|
@@ -167,6 +179,9 @@ class PlainConsoleRenderer {
|
|
|
167
179
|
turnStart(turn, totalTurns, agent, role) {
|
|
168
180
|
process.stdout.write(`\n[${turn}/${totalTurns}] ${agent} (${role})...\n`);
|
|
169
181
|
}
|
|
182
|
+
askResponseStart(response, totalResponses, agent, role) {
|
|
183
|
+
process.stdout.write(`\n[${response}/${totalResponses}] ${agent} (${role})...\n`);
|
|
184
|
+
}
|
|
170
185
|
/** No-op : pas de spinner en mode plain. */
|
|
171
186
|
thinkingStart(_agent, _role) { }
|
|
172
187
|
/** No-op : pas de spinner à arrêter en mode plain. */
|
|
@@ -175,6 +190,9 @@ class PlainConsoleRenderer {
|
|
|
175
190
|
message(content) {
|
|
176
191
|
process.stdout.write(`${content.trim()}\n`);
|
|
177
192
|
}
|
|
193
|
+
askResponseMessage(content) {
|
|
194
|
+
this.message(content);
|
|
195
|
+
}
|
|
178
196
|
/** Affiche l'en-tête de la section synthèse en texte brut. */
|
|
179
197
|
summaryStart(agent, role) {
|
|
180
198
|
process.stdout.write(`\n[${this.messages.renderers.summaryTitle}] ${agent} (${role})...\n`);
|
|
@@ -194,6 +212,12 @@ class PlainConsoleRenderer {
|
|
|
194
212
|
* @param agents - Infos de démarrage des agents (type, rôle, nom).
|
|
195
213
|
*/
|
|
196
214
|
function formatAgentPair(options, agents) {
|
|
215
|
+
if (options.mode === "ask") {
|
|
216
|
+
if (agents.length > 0) {
|
|
217
|
+
return agents.map(formatAgentLabel).join(", ");
|
|
218
|
+
}
|
|
219
|
+
return (options.askAgents ?? [options.agentA, options.agentB]).join(", ");
|
|
220
|
+
}
|
|
197
221
|
if (agents.length >= 2) {
|
|
198
222
|
return `${formatAgentLabel(agents[0])} <-> ${formatAgentLabel(agents[1])}`;
|
|
199
223
|
}
|
|
@@ -214,7 +238,19 @@ function formatAgentLabel(agent) {
|
|
|
214
238
|
* @param options - Options du débat.
|
|
215
239
|
*/
|
|
216
240
|
function formatSummary(options, messages) {
|
|
217
|
-
|
|
241
|
+
if (!options.summaryEnabled) {
|
|
242
|
+
return messages.renderers.disabled;
|
|
243
|
+
}
|
|
244
|
+
if (options.summaryAgent) {
|
|
245
|
+
return options.summaryAgent;
|
|
246
|
+
}
|
|
247
|
+
if (options.mode === "ask" && options.askAgents && options.askAgents.length > 0) {
|
|
248
|
+
return options.askAgents[options.askAgents.length - 1] ?? options.agentB;
|
|
249
|
+
}
|
|
250
|
+
return options.agentB;
|
|
251
|
+
}
|
|
252
|
+
function formatResponseCount(options) {
|
|
253
|
+
return options.mode === "ask" ? options.askAgents?.length ?? 2 : options.turns;
|
|
218
254
|
}
|
|
219
255
|
/**
|
|
220
256
|
* Renvoie un résumé du contexte injecté (nombre de fichiers ou mention d'absence).
|