palabre 0.9.1 → 0.10.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 +6 -0
- package/dist/adapters/cli-pty.js +30 -10
- package/dist/adapters/cli-shared.js +73 -0
- package/dist/adapters/cli.js +40 -77
- package/dist/adapters/index.js +1 -0
- package/dist/adapters/ollama.js +32 -32
- package/dist/adapters/terminal.js +1 -0
- package/dist/args.js +1 -0
- package/dist/commands/agents.js +7 -1
- package/dist/commands/context.js +7 -1
- package/dist/commands/history.js +6 -1
- package/dist/commands/init.js +8 -3
- package/dist/commands/presets.js +6 -1
- package/dist/commands/shared.js +5 -1
- package/dist/commands/update.js +6 -1
- package/dist/config.js +17 -1
- package/dist/configWizard.js +5 -4
- package/dist/context.js +1 -0
- package/dist/contextScan.js +4 -3
- package/dist/discovery.js +1 -0
- package/dist/doctor.js +1 -0
- package/dist/errors.js +4 -0
- package/dist/exec.js +1 -0
- package/dist/history.js +10 -0
- package/dist/i18n.js +2 -0
- package/dist/index.js +170 -112
- package/dist/limits.js +4 -0
- package/dist/messages/adapter-errors.js +26 -2
- package/dist/messages/config.js +6 -0
- package/dist/messages/index.js +1 -0
- package/dist/messages/renderers.js +10 -2
- package/dist/messages/tui.js +8 -2
- package/dist/new.js +65 -11
- package/dist/ollamaUrl.js +20 -0
- package/dist/orchestrator.js +103 -150
- package/dist/output.js +1 -0
- package/dist/presets.js +1 -0
- package/dist/prompt.js +1 -0
- package/dist/renderers/console.js +1 -1
- package/dist/renderers/tui-prompts.js +343 -0
- package/dist/renderers/tui-renderer.js +228 -0
- package/dist/renderers/tui-screens.js +352 -0
- package/dist/renderers/tui-theme.js +356 -0
- package/dist/renderers/tui.js +7 -1086
- package/dist/runOptions.js +33 -2
- package/dist/session.js +1 -0
- package/dist/tuiController.js +61 -16
- package/dist/tuiState.js +4 -0
- package/dist/types.js +1 -0
- package/dist/update.js +1 -0
- package/dist/version.js +1 -0
- package/package.json +1 -1
package/dist/new.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
/** @file Assistant interactif `palabre new` : compose les mêmes flags qu'un lancement direct, sans second chemin d'exécution. */
|
|
1
2
|
import { createInterface } from "node:readline/promises";
|
|
2
3
|
import { stdin as input, stdout as output } from "node:process";
|
|
3
4
|
import { isAgentDetected } from "./agentRegistry.js";
|
|
4
5
|
import { discoverLocalTools } from "./discovery.js";
|
|
5
6
|
import { findPresetNameForPair } from "./presets.js";
|
|
6
7
|
import { MAX_TURNS, turnsOrDefault, validateTurns } from "./limits.js";
|
|
8
|
+
import { accent, bold, brandHeader, card, clearScreen, dim, padBlock, supportsInteractiveOutput, surfaceWidth } from "./renderers/tui-theme.js";
|
|
9
|
+
const interruptedAnswer = "\u0000palabre-interrupted";
|
|
7
10
|
/**
|
|
8
11
|
* Lance le wizard interactif `palabre new`.
|
|
9
12
|
* Détecte les outils locaux, liste les agents de la config et guide la composition du débat.
|
|
@@ -17,10 +20,7 @@ export async function runNewWizard(config, messages) {
|
|
|
17
20
|
}
|
|
18
21
|
const rl = await createQuestioner();
|
|
19
22
|
try {
|
|
20
|
-
|
|
21
|
-
console.log(messages.new.quitHint);
|
|
22
|
-
console.log(messages.new.defaultHint);
|
|
23
|
-
console.log("");
|
|
23
|
+
renderWizardIntro(messages);
|
|
24
24
|
const mode = await askMode(rl, config.defaults?.mode ?? "debate", messages);
|
|
25
25
|
if (!mode)
|
|
26
26
|
return undefined;
|
|
@@ -175,11 +175,21 @@ async function askMode(rl, defaultMode, messages) {
|
|
|
175
175
|
{ value: "ask", label: messages.new.modeAsk }
|
|
176
176
|
];
|
|
177
177
|
const fallback = choices.find((choice) => choice.value === defaultMode)?.value ?? "debate";
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
178
|
+
if (supportsInteractiveOutput) {
|
|
179
|
+
const lines = choices.map((choice, index) => {
|
|
180
|
+
const marker = choice.value === fallback ? accent("(*)") : " ";
|
|
181
|
+
return `${bold(`${index + 1})`)} ${marker} ${choice.label}`;
|
|
182
|
+
});
|
|
183
|
+
console.log(padBlock(card(lines, surfaceWidth(), messages.new.mode)).join("\n"));
|
|
184
|
+
console.log("");
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
console.log(messages.new.mode);
|
|
188
|
+
choices.forEach((choice, index) => {
|
|
189
|
+
const marker = choice.value === fallback ? "(*)" : " ";
|
|
190
|
+
console.log(` ${index + 1}) ${marker} ${choice.label}`);
|
|
191
|
+
});
|
|
192
|
+
}
|
|
183
193
|
while (true) {
|
|
184
194
|
const answer = await rl.question(`${messages.new.mode} [${fallback}]: `);
|
|
185
195
|
const value = answer.trim().toLowerCase();
|
|
@@ -200,7 +210,33 @@ async function askMode(rl, defaultMode, messages) {
|
|
|
200
210
|
}
|
|
201
211
|
async function createQuestioner() {
|
|
202
212
|
if (input.isTTY) {
|
|
203
|
-
|
|
213
|
+
const rl = createInterface({ input, output });
|
|
214
|
+
return {
|
|
215
|
+
question(prompt) {
|
|
216
|
+
return new Promise((resolve, reject) => {
|
|
217
|
+
let settled = false;
|
|
218
|
+
const cleanup = () => rl.off("SIGINT", onSigint);
|
|
219
|
+
const settle = (value) => {
|
|
220
|
+
if (settled)
|
|
221
|
+
return;
|
|
222
|
+
settled = true;
|
|
223
|
+
cleanup();
|
|
224
|
+
resolve(value);
|
|
225
|
+
};
|
|
226
|
+
const onSigint = () => settle(interruptedAnswer);
|
|
227
|
+
rl.once("SIGINT", onSigint);
|
|
228
|
+
rl.question(prompt).then(settle, (error) => {
|
|
229
|
+
if (settled)
|
|
230
|
+
return;
|
|
231
|
+
cleanup();
|
|
232
|
+
reject(error);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
},
|
|
236
|
+
close() {
|
|
237
|
+
rl.close();
|
|
238
|
+
}
|
|
239
|
+
};
|
|
204
240
|
}
|
|
205
241
|
const lines = await readPipedLines();
|
|
206
242
|
let index = 0;
|
|
@@ -375,7 +411,25 @@ function uniqueNames(names) {
|
|
|
375
411
|
return names.filter((name, index) => names.indexOf(name) === index);
|
|
376
412
|
}
|
|
377
413
|
function isQuit(value) {
|
|
378
|
-
return ["q", "quit", "exit"].includes(value.toLowerCase());
|
|
414
|
+
return value === interruptedAnswer || ["q", "quit", "exit"].includes(value.toLowerCase());
|
|
415
|
+
}
|
|
416
|
+
function renderWizardIntro(messages) {
|
|
417
|
+
if (!supportsInteractiveOutput) {
|
|
418
|
+
console.log(messages.new.title);
|
|
419
|
+
console.log(messages.new.quitHint);
|
|
420
|
+
console.log(messages.new.defaultHint);
|
|
421
|
+
console.log("");
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
clearScreen();
|
|
425
|
+
console.log("");
|
|
426
|
+
console.log(padBlock([brandHeader(messages.new.title)]).join("\n"));
|
|
427
|
+
console.log("");
|
|
428
|
+
console.log(padBlock([
|
|
429
|
+
dim(messages.new.quitHint),
|
|
430
|
+
dim(messages.new.defaultHint)
|
|
431
|
+
]).join("\n"));
|
|
432
|
+
console.log("");
|
|
379
433
|
}
|
|
380
434
|
function printCommandPreview(selection, messages) {
|
|
381
435
|
const explicitCommand = buildExplicitCommand(selection);
|
package/dist/ollamaUrl.js
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
+
/** Serveur Ollama utilisé quand aucune autre source (flag, env, config) n'est fournie. */
|
|
1
2
|
export const DEFAULT_OLLAMA_BASE_URL = "http://localhost:11434";
|
|
3
|
+
/**
|
|
4
|
+
* Erreur levée par `normalizeOllamaBaseUrl` quand la valeur fournie n'est pas une URL HTTP(S) exploitable.
|
|
5
|
+
* Le message du constructeur est un texte technique de repli non localisé ; les appelants CLI/TUI
|
|
6
|
+
* doivent reformater `kind`/`value`/`protocol` via les messages traduits plutôt que d'afficher ce message brut.
|
|
7
|
+
*/
|
|
2
8
|
export class OllamaUrlError extends Error {
|
|
3
9
|
kind;
|
|
4
10
|
value;
|
|
@@ -15,6 +21,20 @@ export class OllamaUrlError extends Error {
|
|
|
15
21
|
this.name = "OllamaUrlError";
|
|
16
22
|
}
|
|
17
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Formate une `OllamaUrlError` en message localisé actionnable.
|
|
26
|
+
* Source unique du mapping `kind` → message, partagée entre le gestionnaire
|
|
27
|
+
* d'erreur du point d'entrée CLI et la conversion en `DebateFailure` de l'orchestrateur.
|
|
28
|
+
*/
|
|
29
|
+
export function formatOllamaUrlError(error, messages) {
|
|
30
|
+
if (error.kind === "empty") {
|
|
31
|
+
return messages.common.ollamaUrlEmpty;
|
|
32
|
+
}
|
|
33
|
+
if (error.kind === "protocol") {
|
|
34
|
+
return messages.common.ollamaUrlProtocol(error.protocol ?? "");
|
|
35
|
+
}
|
|
36
|
+
return messages.common.ollamaUrlInvalid(error.value);
|
|
37
|
+
}
|
|
18
38
|
/**
|
|
19
39
|
* Résout l'adresse client Ollama selon la priorité produit :
|
|
20
40
|
* flag CLI > OLLAMA_HOST > config agent > serveur local par défaut.
|
package/dist/orchestrator.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
/** @file Boucle d'orchestration des modes `debate` et `ask` : tours, arrêt anticipé et synthèse finale. */
|
|
1
2
|
import { createAgent } from "./adapters/index.js";
|
|
2
3
|
import { AdapterError } from "./errors.js";
|
|
3
4
|
import { createTranslator } from "./i18n.js";
|
|
4
5
|
import { MAX_ASK_AGENTS } from "./limits.js";
|
|
5
|
-
import { OllamaUrlError } from "./ollamaUrl.js";
|
|
6
|
+
import { formatOllamaUrlError, OllamaUrlError } from "./ollamaUrl.js";
|
|
6
7
|
export { MAX_ASK_AGENTS } from "./limits.js";
|
|
8
|
+
/** Rôle imposé à l'agent de synthèse, indépendant de son rôle configuré. */
|
|
9
|
+
const SUMMARY_ROLE = "summarizer";
|
|
7
10
|
/**
|
|
8
11
|
* Point d'entrée de l'orchestration.
|
|
9
12
|
* Lance le ping-pong entre `agentA` et `agentB` pendant `options.turns` tours,
|
|
@@ -52,86 +55,36 @@ export async function runDebate(config, options, renderer, messages = createTran
|
|
|
52
55
|
const peer = agents[(index + 1) % agents.length];
|
|
53
56
|
const turn = index + 1;
|
|
54
57
|
renderer?.turnStart(turn, options.turns, current.name, current.role);
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
signal: options.signal
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
catch (error) {
|
|
73
|
-
const failure = toDebateFailure(error, {
|
|
74
|
-
phase: "debate",
|
|
75
|
-
agent: current.name,
|
|
76
|
-
role: current.role,
|
|
77
|
-
turn
|
|
78
|
-
}, messages);
|
|
79
|
-
renderer?.error(failure);
|
|
58
|
+
const outcome = await generateTurnMessage(current, {
|
|
59
|
+
topic: options.topic,
|
|
60
|
+
turn,
|
|
61
|
+
totalTurns: options.turns,
|
|
62
|
+
selfName: current.name,
|
|
63
|
+
peerName: peer.name,
|
|
64
|
+
selfRole: current.role,
|
|
65
|
+
language: options.language,
|
|
66
|
+
session: options.session,
|
|
67
|
+
files: options.files,
|
|
68
|
+
transcript,
|
|
69
|
+
signal: options.signal
|
|
70
|
+
}, { phase: "debate", agent: current.name, role: current.role, turn }, renderer, messages);
|
|
71
|
+
if (outcome.failure) {
|
|
80
72
|
return {
|
|
81
73
|
options,
|
|
82
74
|
messages: transcript,
|
|
83
75
|
stopReason,
|
|
84
|
-
failure
|
|
76
|
+
failure: outcome.failure
|
|
85
77
|
};
|
|
86
78
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
const message = {
|
|
91
|
-
agent: current.name,
|
|
92
|
-
role: current.role,
|
|
93
|
-
content: response.content,
|
|
94
|
-
createdAt: new Date().toISOString()
|
|
95
|
-
};
|
|
96
|
-
transcript.push(message);
|
|
97
|
-
renderer?.message(message.content);
|
|
79
|
+
transcript.push(outcome.message);
|
|
80
|
+
renderer?.message(outcome.message.content);
|
|
98
81
|
if (shouldStopOnAgreement(options, transcript, messages)) {
|
|
99
82
|
stopReason = messages.orchestrator.agreementStopReason;
|
|
100
83
|
renderer?.notice(messages.orchestrator.earlyStop(stopReason));
|
|
101
84
|
break;
|
|
102
85
|
}
|
|
103
86
|
}
|
|
104
|
-
|
|
105
|
-
let failure;
|
|
106
|
-
if (options.summaryEnabled) {
|
|
107
|
-
try {
|
|
108
|
-
const cancellation = cancellationFailureIfAborted(options, messages, {
|
|
109
|
-
phase: "summary",
|
|
110
|
-
agent: options.summaryAgent,
|
|
111
|
-
role: summaryRole(),
|
|
112
|
-
turn: transcript.length + 1
|
|
113
|
-
});
|
|
114
|
-
if (cancellation) {
|
|
115
|
-
renderer?.error(cancellation);
|
|
116
|
-
return {
|
|
117
|
-
options,
|
|
118
|
-
messages: transcript,
|
|
119
|
-
stopReason,
|
|
120
|
-
failure: cancellation
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
summary = await generateSummary(config, options, transcript, renderer, messages);
|
|
124
|
-
}
|
|
125
|
-
catch (error) {
|
|
126
|
-
failure = toDebateFailure(error, {
|
|
127
|
-
phase: "summary",
|
|
128
|
-
agent: options.summaryAgent,
|
|
129
|
-
role: summaryRole(),
|
|
130
|
-
turn: transcript.length + 1
|
|
131
|
-
}, messages);
|
|
132
|
-
renderer?.error(failure);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
87
|
+
const { summary, failure } = await runSummaryPhase(config, options, transcript, renderer, messages);
|
|
135
88
|
return {
|
|
136
89
|
options,
|
|
137
90
|
messages: transcript,
|
|
@@ -145,6 +98,8 @@ export async function runDebate(config, options, renderer, messages = createTran
|
|
|
145
98
|
* puis un agent de synthèse résume fidèlement chaque réponse et les compare.
|
|
146
99
|
*/
|
|
147
100
|
export async function runAsk(config, options, renderer, messages = createTranslator("fr")) {
|
|
101
|
+
// `runAsk` est appelable directement, hors du chemin CLI : on revalide la liste
|
|
102
|
+
// ask même si `resolveRunOptions` l'a déjà normalisée pour `palabre run`.
|
|
148
103
|
const askAgentNames = resolveAskAgentNames(options);
|
|
149
104
|
if (askAgentNames.length === 0) {
|
|
150
105
|
throw new Error(messages.common.noAgentDefined("ask agent"));
|
|
@@ -159,7 +114,7 @@ export async function runAsk(config, options, renderer, messages = createTransla
|
|
|
159
114
|
}
|
|
160
115
|
return [name, agentConfig];
|
|
161
116
|
});
|
|
162
|
-
warnIfOllamaHasNoContext(options, agentEntries
|
|
117
|
+
warnIfOllamaHasNoContext(options, agentEntries, renderer, messages);
|
|
163
118
|
renderer?.start(options, agentEntries.map(([name, agentConfig]) => ({
|
|
164
119
|
name,
|
|
165
120
|
role: agentConfig.role,
|
|
@@ -190,86 +145,36 @@ export async function runAsk(config, options, renderer, messages = createTransla
|
|
|
190
145
|
else {
|
|
191
146
|
renderer?.turnStart(response, agents.length, current.name, current.role);
|
|
192
147
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
signal: options.signal
|
|
209
|
-
});
|
|
210
|
-
}
|
|
211
|
-
catch (error) {
|
|
212
|
-
const failure = toDebateFailure(error, {
|
|
213
|
-
phase: "ask",
|
|
214
|
-
agent: current.name,
|
|
215
|
-
role: current.role,
|
|
216
|
-
turn: response
|
|
217
|
-
}, messages);
|
|
218
|
-
renderer?.error(failure);
|
|
148
|
+
const outcome = await generateTurnMessage(current, {
|
|
149
|
+
topic: options.topic,
|
|
150
|
+
turn: response,
|
|
151
|
+
totalTurns: agents.length,
|
|
152
|
+
selfName: current.name,
|
|
153
|
+
peerName: "independent-agents",
|
|
154
|
+
selfRole: current.role,
|
|
155
|
+
mode: "ask",
|
|
156
|
+
language: options.language,
|
|
157
|
+
session: options.session,
|
|
158
|
+
files: options.files,
|
|
159
|
+
transcript: [],
|
|
160
|
+
signal: options.signal
|
|
161
|
+
}, { phase: "ask", agent: current.name, role: current.role, turn: response }, renderer, messages);
|
|
162
|
+
if (outcome.failure) {
|
|
219
163
|
return {
|
|
220
164
|
options,
|
|
221
165
|
messages: transcript,
|
|
222
|
-
failure
|
|
166
|
+
failure: outcome.failure
|
|
223
167
|
};
|
|
224
168
|
}
|
|
225
|
-
|
|
226
|
-
renderer?.thinkingEnd();
|
|
227
|
-
}
|
|
228
|
-
const message = {
|
|
229
|
-
agent: current.name,
|
|
230
|
-
role: current.role,
|
|
231
|
-
content: agentResponse.content,
|
|
232
|
-
createdAt: new Date().toISOString()
|
|
233
|
-
};
|
|
234
|
-
transcript.push(message);
|
|
169
|
+
transcript.push(outcome.message);
|
|
235
170
|
if (renderer?.askResponseMessage) {
|
|
236
|
-
renderer.askResponseMessage(message.content);
|
|
171
|
+
renderer.askResponseMessage(outcome.message.content);
|
|
237
172
|
}
|
|
238
173
|
else {
|
|
239
|
-
renderer?.message(message.content);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
let summary;
|
|
243
|
-
let failure;
|
|
244
|
-
if (options.summaryEnabled) {
|
|
245
|
-
try {
|
|
246
|
-
const summaryAgentName = options.summaryAgent;
|
|
247
|
-
const cancellation = cancellationFailureIfAborted(options, messages, {
|
|
248
|
-
phase: "summary",
|
|
249
|
-
agent: summaryAgentName,
|
|
250
|
-
role: summaryRole(),
|
|
251
|
-
turn: transcript.length + 1
|
|
252
|
-
});
|
|
253
|
-
if (cancellation) {
|
|
254
|
-
renderer?.error(cancellation);
|
|
255
|
-
return {
|
|
256
|
-
options,
|
|
257
|
-
messages: transcript,
|
|
258
|
-
failure: cancellation
|
|
259
|
-
};
|
|
260
|
-
}
|
|
261
|
-
summary = await generateSummary(config, options, transcript, renderer, messages);
|
|
262
|
-
}
|
|
263
|
-
catch (error) {
|
|
264
|
-
failure = toDebateFailure(error, {
|
|
265
|
-
phase: "summary",
|
|
266
|
-
agent: options.summaryAgent,
|
|
267
|
-
role: summaryRole(),
|
|
268
|
-
turn: transcript.length + 1
|
|
269
|
-
}, messages);
|
|
270
|
-
renderer?.error(failure);
|
|
174
|
+
renderer?.message(outcome.message.content);
|
|
271
175
|
}
|
|
272
176
|
}
|
|
177
|
+
const { summary, failure } = await runSummaryPhase(config, options, transcript, renderer, messages);
|
|
273
178
|
return {
|
|
274
179
|
options,
|
|
275
180
|
messages: transcript,
|
|
@@ -277,6 +182,62 @@ export async function runAsk(config, options, renderer, messages = createTransla
|
|
|
277
182
|
failure
|
|
278
183
|
};
|
|
279
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* Appelle `agent.generate` en encadrant l'état "thinking" du renderer, puis
|
|
187
|
+
* construit le `DebateMessage` horodaté ou convertit l'erreur en `DebateFailure`
|
|
188
|
+
* (notifiée au renderer). Mutualisé entre les tours `debate` et les réponses `ask`.
|
|
189
|
+
*/
|
|
190
|
+
async function generateTurnMessage(agent, prompt, context, renderer, messages) {
|
|
191
|
+
renderer?.thinkingStart(agent.name, agent.role);
|
|
192
|
+
try {
|
|
193
|
+
const response = await agent.generate(prompt);
|
|
194
|
+
return {
|
|
195
|
+
message: {
|
|
196
|
+
agent: agent.name,
|
|
197
|
+
role: agent.role,
|
|
198
|
+
content: response.content,
|
|
199
|
+
createdAt: new Date().toISOString()
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
catch (error) {
|
|
204
|
+
const failure = toDebateFailure(error, context, messages);
|
|
205
|
+
renderer?.error(failure);
|
|
206
|
+
return { failure };
|
|
207
|
+
}
|
|
208
|
+
finally {
|
|
209
|
+
renderer?.thinkingEnd();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Phase de synthèse commune aux modes `debate` et `ask` : vérifie l'annulation,
|
|
214
|
+
* génère la synthèse et convertit tout échec en `DebateFailure` déjà notifiée au
|
|
215
|
+
* renderer. Retourne un objet vide si la synthèse est désactivée.
|
|
216
|
+
*/
|
|
217
|
+
async function runSummaryPhase(config, options, transcript, renderer, messages) {
|
|
218
|
+
if (!options.summaryEnabled) {
|
|
219
|
+
return {};
|
|
220
|
+
}
|
|
221
|
+
const context = {
|
|
222
|
+
phase: "summary",
|
|
223
|
+
agent: options.summaryAgent,
|
|
224
|
+
role: SUMMARY_ROLE,
|
|
225
|
+
turn: transcript.length + 1
|
|
226
|
+
};
|
|
227
|
+
const cancellation = cancellationFailureIfAborted(options, messages, context);
|
|
228
|
+
if (cancellation) {
|
|
229
|
+
renderer?.error(cancellation);
|
|
230
|
+
return { failure: cancellation };
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
return { summary: await generateSummary(config, options, transcript, renderer, messages) };
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
const failure = toDebateFailure(error, context, messages);
|
|
237
|
+
renderer?.error(failure);
|
|
238
|
+
return { failure };
|
|
239
|
+
}
|
|
240
|
+
}
|
|
280
241
|
/**
|
|
281
242
|
* Heuristique d'arrêt sur accord explicite.
|
|
282
243
|
* Ne s'active qu'après un tour complet (nombre pair de messages) pour éviter les faux positifs.
|
|
@@ -338,7 +299,7 @@ async function generateSummary(config, options, transcript, renderer, messages =
|
|
|
338
299
|
throw new Error(messages.orchestrator.unknownSummaryAgent(summaryAgentName));
|
|
339
300
|
}
|
|
340
301
|
const summaryAgent = createAgent(summaryAgentName, summaryConfig, { ollamaUrl: options.ollamaUrl });
|
|
341
|
-
const role =
|
|
302
|
+
const role = SUMMARY_ROLE;
|
|
342
303
|
renderer?.summaryStart(summaryAgent.name, role);
|
|
343
304
|
renderer?.thinkingStart(summaryAgent.name, role);
|
|
344
305
|
const response = await summaryAgent.generate({
|
|
@@ -364,9 +325,6 @@ async function generateSummary(config, options, transcript, renderer, messages =
|
|
|
364
325
|
renderer?.message(summary.content);
|
|
365
326
|
return summary;
|
|
366
327
|
}
|
|
367
|
-
function summaryRole() {
|
|
368
|
-
return "summarizer";
|
|
369
|
-
}
|
|
370
328
|
function cancellationFailureIfAborted(options, messages, context) {
|
|
371
329
|
if (!options.signal?.aborted) {
|
|
372
330
|
return undefined;
|
|
@@ -399,15 +357,10 @@ function toDebateFailure(error, context, messages) {
|
|
|
399
357
|
};
|
|
400
358
|
}
|
|
401
359
|
if (error instanceof OllamaUrlError) {
|
|
402
|
-
const message = error.kind === "empty"
|
|
403
|
-
? messages.common.ollamaUrlEmpty
|
|
404
|
-
: error.kind === "protocol"
|
|
405
|
-
? messages.common.ollamaUrlProtocol(error.protocol ?? "")
|
|
406
|
-
: messages.common.ollamaUrlInvalid(error.value);
|
|
407
360
|
return {
|
|
408
361
|
...context,
|
|
409
362
|
kind: "unknown",
|
|
410
|
-
message
|
|
363
|
+
message: formatOllamaUrlError(error, messages)
|
|
411
364
|
};
|
|
412
365
|
}
|
|
413
366
|
return {
|
package/dist/output.js
CHANGED
package/dist/presets.js
CHANGED
package/dist/prompt.js
CHANGED
|
@@ -255,7 +255,7 @@ function formatContext(options, messages) {
|
|
|
255
255
|
if (count === 0) {
|
|
256
256
|
return messages.renderers.noInjectedFiles;
|
|
257
257
|
}
|
|
258
|
-
return messages.renderers.injectedFiles(count);
|
|
258
|
+
return messages.renderers.injectedFiles(count, options.files.map((file) => file.path));
|
|
259
259
|
}
|
|
260
260
|
function formatFailureLocation(failure, messages) {
|
|
261
261
|
if (failure.phase === "summary") {
|