palabre 0.6.0 → 0.6.3
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 +33 -3
- package/dist/adapters/cli-pty.js +16 -28
- package/dist/adapters/cli-shared.js +24 -0
- package/dist/adapters/cli.js +61 -23
- package/dist/adapters/ollama.js +2 -1
- package/dist/agentRegistry.js +76 -0
- package/dist/args.js +265 -0
- package/dist/config.js +26 -20
- package/dist/contextScan.js +48 -0
- package/dist/discovery.js +1 -12
- package/dist/doctor.js +2 -27
- package/dist/exec.js +17 -0
- package/dist/index.js +38 -258
- package/dist/messages/adapter-errors.js +2 -0
- package/dist/messages/common.js +6 -0
- package/dist/messages/help.js +22 -0
- package/dist/messages/orchestrator.js +19 -0
- package/dist/messages/output.js +14 -2
- package/dist/messages/prompt.js +6 -2
- package/dist/new.js +1 -26
- package/dist/orchestrator.js +79 -30
- package/dist/output.js +20 -3
- package/dist/presets.js +2 -21
- package/dist/prompt.js +4 -0
- package/dist/renderers/console.js +15 -0
- package/dist/renderers/ndjson.js +4 -0
- package/package.json +3 -2
package/dist/messages/help.js
CHANGED
|
@@ -29,6 +29,16 @@ Usage:
|
|
|
29
29
|
Flags:
|
|
30
30
|
--json sortie structuree pour integrations
|
|
31
31
|
--config <path> chemin de config explicite
|
|
32
|
+
`,
|
|
33
|
+
context: `
|
|
34
|
+
Scanne le contexte projet avec les memes regles que --context.
|
|
35
|
+
|
|
36
|
+
Usage:
|
|
37
|
+
palabre context scan [paths...] [flags]
|
|
38
|
+
|
|
39
|
+
Flags:
|
|
40
|
+
--json sortie structuree pour integrations
|
|
41
|
+
--language <fr|en> force la langue des avertissements
|
|
32
42
|
`,
|
|
33
43
|
config: `
|
|
34
44
|
Configure les agents par defaut, la synthese, le nombre de reponses et la langue.
|
|
@@ -124,6 +134,16 @@ Usage:
|
|
|
124
134
|
Flags:
|
|
125
135
|
--json structured output for integrations
|
|
126
136
|
--config <path> explicit config path
|
|
137
|
+
`,
|
|
138
|
+
context: `
|
|
139
|
+
Scans project context with the same rules as --context.
|
|
140
|
+
|
|
141
|
+
Usage:
|
|
142
|
+
palabre context scan [paths...] [flags]
|
|
143
|
+
|
|
144
|
+
Flags:
|
|
145
|
+
--json structured output for integrations
|
|
146
|
+
--language <fr|en> forces warning language
|
|
127
147
|
`,
|
|
128
148
|
config: `
|
|
129
149
|
Configures default agents, summary, response count, and language.
|
|
@@ -212,6 +232,7 @@ Commandes:
|
|
|
212
232
|
new Assistant interactif de debat
|
|
213
233
|
agents Lister les agents configures
|
|
214
234
|
presets Lister les presets disponibles
|
|
235
|
+
context Scanner le contexte projet
|
|
215
236
|
config Modifier les parametres par defaut
|
|
216
237
|
doctor Verifier la config et les outils locaux
|
|
217
238
|
update Afficher ou appliquer les etapes de mise a jour
|
|
@@ -256,6 +277,7 @@ Commands:
|
|
|
256
277
|
new Interactive debate assistant
|
|
257
278
|
agents List configured agents
|
|
258
279
|
presets List available presets
|
|
280
|
+
context Scan project context
|
|
259
281
|
config Edit default settings
|
|
260
282
|
doctor Check config and local tools
|
|
261
283
|
update Show or apply update steps
|
|
@@ -2,12 +2,31 @@ export const orchestratorMessages = {
|
|
|
2
2
|
fr: {
|
|
3
3
|
agreementStopReason: "Accord clair detecte apres un tour complet.",
|
|
4
4
|
earlyStop: (reason) => `Arret anticipe: ${reason}`,
|
|
5
|
+
agreementPatterns: [
|
|
6
|
+
"accord complet",
|
|
7
|
+
"accord total",
|
|
8
|
+
"aucun desaccord",
|
|
9
|
+
"aucune incertitude",
|
|
10
|
+
"rien a trancher",
|
|
11
|
+
"rien a ajouter",
|
|
12
|
+
"question factuelle resolue"
|
|
13
|
+
],
|
|
5
14
|
ollamaNoContext: (agentNames) => `${agentNames} ne lit pas le filesystem. Ajoute --files ou --context pour fournir un contexte projet.`,
|
|
6
15
|
unknownSummaryAgent: (agentName) => `Agent de synthese inconnu: ${agentName}`
|
|
7
16
|
},
|
|
8
17
|
en: {
|
|
9
18
|
agreementStopReason: "Clear agreement detected after a complete round.",
|
|
10
19
|
earlyStop: (reason) => `Early stop: ${reason}`,
|
|
20
|
+
agreementPatterns: [
|
|
21
|
+
"full agreement",
|
|
22
|
+
"complete agreement",
|
|
23
|
+
"total agreement",
|
|
24
|
+
"no disagreement",
|
|
25
|
+
"no remaining uncertainty",
|
|
26
|
+
"nothing to settle",
|
|
27
|
+
"nothing to add",
|
|
28
|
+
"factual question resolved"
|
|
29
|
+
],
|
|
11
30
|
ollamaNoContext: (agentNames) => `${agentNames} cannot read the filesystem. Add --files or --context to provide project context.`,
|
|
12
31
|
unknownSummaryAgent: (agentName) => `Unknown summary agent: ${agentName}`
|
|
13
32
|
}
|
package/dist/messages/output.js
CHANGED
|
@@ -3,6 +3,7 @@ export const outputMessages = {
|
|
|
3
3
|
title: "# PALABRE Debate",
|
|
4
4
|
contextTitle: "## Contexte",
|
|
5
5
|
exchangesTitle: "## Echanges",
|
|
6
|
+
failureTitle: "## Interruption",
|
|
6
7
|
finalSummaryTitle: "## Synthese finale",
|
|
7
8
|
tableField: "Champ",
|
|
8
9
|
tableValue: "Valeur",
|
|
@@ -27,13 +28,19 @@ export const outputMessages = {
|
|
|
27
28
|
sessionStartedAt: "Session demarree a",
|
|
28
29
|
agent: "Agent",
|
|
29
30
|
role: "Role",
|
|
30
|
-
date: "Date"
|
|
31
|
+
date: "Date",
|
|
32
|
+
failurePhase: "Phase",
|
|
33
|
+
failureAgent: "Agent",
|
|
34
|
+
failureTurn: "Tour",
|
|
35
|
+
failureKind: "Type d'erreur",
|
|
36
|
+
failureMessage: "Message"
|
|
31
37
|
}
|
|
32
38
|
},
|
|
33
39
|
en: {
|
|
34
40
|
title: "# PALABRE Debate",
|
|
35
41
|
contextTitle: "## Context",
|
|
36
42
|
exchangesTitle: "## Exchanges",
|
|
43
|
+
failureTitle: "## Interruption",
|
|
37
44
|
finalSummaryTitle: "## Final summary",
|
|
38
45
|
tableField: "Field",
|
|
39
46
|
tableValue: "Value",
|
|
@@ -58,7 +65,12 @@ export const outputMessages = {
|
|
|
58
65
|
sessionStartedAt: "Session started at",
|
|
59
66
|
agent: "Agent",
|
|
60
67
|
role: "Role",
|
|
61
|
-
date: "Date"
|
|
68
|
+
date: "Date",
|
|
69
|
+
failurePhase: "Phase",
|
|
70
|
+
failureAgent: "Agent",
|
|
71
|
+
failureTurn: "Turn",
|
|
72
|
+
failureKind: "Error kind",
|
|
73
|
+
failureMessage: "Message"
|
|
62
74
|
}
|
|
63
75
|
}
|
|
64
76
|
};
|
package/dist/messages/prompt.js
CHANGED
|
@@ -29,6 +29,7 @@ export const promptMessages = {
|
|
|
29
29
|
cwd: (value) => `- Dossier courant: ${value}`,
|
|
30
30
|
sessionStartedAt: (value) => `- Session demarree a: ${value}`,
|
|
31
31
|
turnProgress: (turn, totalTurns) => `- Tour courant: ${turn}/${totalTurns}`,
|
|
32
|
+
responseLanguageInstruction: "Langue de reponse obligatoire: francais. Reponds uniquement en francais, meme si le sujet ou le transcript contient une autre langue.",
|
|
32
33
|
objectiveTitle: "Objectif:",
|
|
33
34
|
debateObjectives: [
|
|
34
35
|
"- Apporte une reponse utile, concrete et courte.",
|
|
@@ -55,7 +56,8 @@ export const promptMessages = {
|
|
|
55
56
|
actionsHeading: "### Actions proposees",
|
|
56
57
|
conclusionHeading: "### Conclusion",
|
|
57
58
|
finalProseInstruction: "Un court paragraphe de synthese en prose, sans liste, qui resume le sens general du debat et la decision ou direction la plus raisonnable.",
|
|
58
|
-
summaryAnswerTitle: "Synthese:"
|
|
59
|
+
summaryAnswerTitle: "Synthese:",
|
|
60
|
+
ollamaSystemPrompt: "Tu participes a un debat technique orchestre. Reste precis, utile et honnete sur tes limites."
|
|
59
61
|
},
|
|
60
62
|
en: {
|
|
61
63
|
subject: (topic) => `Subject: ${topic}`,
|
|
@@ -71,6 +73,7 @@ export const promptMessages = {
|
|
|
71
73
|
cwd: (value) => `- Current directory: ${value}`,
|
|
72
74
|
sessionStartedAt: (value) => `- Session started at: ${value}`,
|
|
73
75
|
turnProgress: (turn, totalTurns) => `- Current turn: ${turn}/${totalTurns}`,
|
|
76
|
+
responseLanguageInstruction: "Required response language: English. Answer only in English, even if the subject or transcript contains another language.",
|
|
74
77
|
objectiveTitle: "Objective:",
|
|
75
78
|
debateObjectives: [
|
|
76
79
|
"- Provide a useful, concrete, and concise answer.",
|
|
@@ -97,6 +100,7 @@ export const promptMessages = {
|
|
|
97
100
|
actionsHeading: "### Proposed actions",
|
|
98
101
|
conclusionHeading: "### Conclusion",
|
|
99
102
|
finalProseInstruction: "A short prose summary paragraph, without a list, that captures the general meaning of the debate and the most reasonable decision or direction.",
|
|
100
|
-
summaryAnswerTitle: "Summary:"
|
|
103
|
+
summaryAnswerTitle: "Summary:",
|
|
104
|
+
ollamaSystemPrompt: "You are taking part in an orchestrated technical debate. Stay precise, useful, and honest about your limits."
|
|
101
105
|
}
|
|
102
106
|
};
|
package/dist/new.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createInterface } from "node:readline/promises";
|
|
2
2
|
import { stdin as input, stdout as output } from "node:process";
|
|
3
|
+
import { isAgentDetected } from "./agentRegistry.js";
|
|
3
4
|
import { discoverLocalTools } from "./discovery.js";
|
|
4
5
|
import { findPresetNameForPair } from "./presets.js";
|
|
5
6
|
import { MAX_TURNS, turnsOrDefault, validateTurns } from "./limits.js";
|
|
@@ -136,25 +137,6 @@ function buildAgentChoices(config, discovery, messages) {
|
|
|
136
137
|
})
|
|
137
138
|
.sort((left, right) => Number(right.detected) - Number(left.detected) || left.name.localeCompare(right.name));
|
|
138
139
|
}
|
|
139
|
-
function isAgentDetected(name, config, discovery) {
|
|
140
|
-
if (config.type === "ollama") {
|
|
141
|
-
return discovery.ollama.available;
|
|
142
|
-
}
|
|
143
|
-
const normalized = normalizeCommandName(config.command || name);
|
|
144
|
-
if (normalized === "codex")
|
|
145
|
-
return discovery.codex.available;
|
|
146
|
-
if (normalized === "claude")
|
|
147
|
-
return discovery.claude.available;
|
|
148
|
-
if (normalized === "gemini")
|
|
149
|
-
return discovery.gemini.available;
|
|
150
|
-
if (normalized === "agy")
|
|
151
|
-
return discovery.antigravity.available;
|
|
152
|
-
if (normalized === "antigravity")
|
|
153
|
-
return discovery.antigravity.available;
|
|
154
|
-
if (normalized === "opencode")
|
|
155
|
-
return discovery.opencode.available;
|
|
156
|
-
return true;
|
|
157
|
-
}
|
|
158
140
|
function agentStatus(_name, config, discovery, detected, messages) {
|
|
159
141
|
if (config.type === "ollama") {
|
|
160
142
|
return detected
|
|
@@ -248,13 +230,6 @@ function splitPaths(value) {
|
|
|
248
230
|
.map((entry) => entry.trim())
|
|
249
231
|
.filter(Boolean) ?? [];
|
|
250
232
|
}
|
|
251
|
-
function normalizeCommandName(command) {
|
|
252
|
-
return command
|
|
253
|
-
.split(/[\\/]/)
|
|
254
|
-
.pop()
|
|
255
|
-
?.toLowerCase()
|
|
256
|
-
.replace(/\.(exe|cmd|bat|ps1)$/i, "") ?? command.toLowerCase();
|
|
257
|
-
}
|
|
258
233
|
function isQuit(value) {
|
|
259
234
|
return ["q", "quit", "exit"].includes(value.toLowerCase());
|
|
260
235
|
}
|
package/dist/orchestrator.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createAgent } from "./adapters/index.js";
|
|
2
|
+
import { AdapterError } from "./errors.js";
|
|
2
3
|
import { createTranslator } from "./i18n.js";
|
|
3
4
|
/**
|
|
4
5
|
* Point d'entrée de l'orchestration.
|
|
@@ -36,18 +37,39 @@ export async function runDebate(config, options, renderer, messages = createTran
|
|
|
36
37
|
const turn = index + 1;
|
|
37
38
|
renderer?.turnStart(turn, options.turns, current.name, current.role);
|
|
38
39
|
renderer?.thinkingStart(current.name, current.role);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
40
|
+
let response;
|
|
41
|
+
try {
|
|
42
|
+
response = await current.generate({
|
|
43
|
+
topic: options.topic,
|
|
44
|
+
turn,
|
|
45
|
+
totalTurns: options.turns,
|
|
46
|
+
selfName: current.name,
|
|
47
|
+
peerName: peer.name,
|
|
48
|
+
selfRole: current.role,
|
|
49
|
+
language: options.language,
|
|
50
|
+
session: options.session,
|
|
51
|
+
files: options.files,
|
|
52
|
+
transcript
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
const failure = toDebateFailure(error, {
|
|
57
|
+
phase: "debate",
|
|
58
|
+
agent: current.name,
|
|
59
|
+
role: current.role,
|
|
60
|
+
turn
|
|
61
|
+
});
|
|
62
|
+
renderer?.error(failure);
|
|
63
|
+
return {
|
|
64
|
+
options,
|
|
65
|
+
messages: transcript,
|
|
66
|
+
stopReason,
|
|
67
|
+
failure
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
finally {
|
|
71
|
+
renderer?.thinkingEnd();
|
|
72
|
+
}
|
|
51
73
|
const message = {
|
|
52
74
|
agent: current.name,
|
|
53
75
|
role: current.role,
|
|
@@ -56,47 +78,53 @@ export async function runDebate(config, options, renderer, messages = createTran
|
|
|
56
78
|
};
|
|
57
79
|
transcript.push(message);
|
|
58
80
|
renderer?.message(message.content);
|
|
59
|
-
if (shouldStopOnAgreement(options, transcript)) {
|
|
81
|
+
if (shouldStopOnAgreement(options, transcript, messages)) {
|
|
60
82
|
stopReason = messages.orchestrator.agreementStopReason;
|
|
61
83
|
renderer?.notice(messages.orchestrator.earlyStop(stopReason));
|
|
62
84
|
break;
|
|
63
85
|
}
|
|
64
86
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
87
|
+
let summary;
|
|
88
|
+
let failure;
|
|
89
|
+
if (options.summaryEnabled) {
|
|
90
|
+
try {
|
|
91
|
+
summary = await generateSummary(config, options, transcript, renderer, messages);
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
failure = toDebateFailure(error, {
|
|
95
|
+
phase: "summary",
|
|
96
|
+
agent: options.summaryAgent ?? options.agentB,
|
|
97
|
+
turn: transcript.length + 1
|
|
98
|
+
});
|
|
99
|
+
renderer?.error(failure);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
68
102
|
return {
|
|
69
103
|
options,
|
|
70
104
|
messages: transcript,
|
|
71
105
|
summary,
|
|
72
|
-
stopReason
|
|
106
|
+
stopReason,
|
|
107
|
+
failure
|
|
73
108
|
};
|
|
74
109
|
}
|
|
75
110
|
/**
|
|
76
111
|
* Heuristique d'arrêt sur accord explicite.
|
|
77
112
|
* Ne s'active qu'après un tour complet (nombre pair de messages) pour éviter les faux positifs.
|
|
113
|
+
* Les phrases d'accord proviennent du dictionnaire i18n pour suivre la langue d'interface.
|
|
78
114
|
* Intentionnellement prudente : ne remplace pas une évaluation sémantique réelle.
|
|
79
115
|
*/
|
|
80
|
-
function shouldStopOnAgreement(options, messages) {
|
|
81
|
-
if (!options.earlyStopOnAgreement ||
|
|
116
|
+
function shouldStopOnAgreement(options, transcript, messages) {
|
|
117
|
+
if (!options.earlyStopOnAgreement || transcript.length < 2 || transcript.length % 2 !== 0) {
|
|
82
118
|
return false;
|
|
83
119
|
}
|
|
84
|
-
const latest = normalizeForAgreement(
|
|
120
|
+
const latest = normalizeForAgreement(transcript[transcript.length - 1]?.content ?? "");
|
|
85
121
|
if (!latest) {
|
|
86
122
|
return false;
|
|
87
123
|
}
|
|
88
|
-
|
|
89
|
-
"accord complet",
|
|
90
|
-
"accord total",
|
|
91
|
-
"aucun desaccord",
|
|
92
|
-
"aucune incertitude",
|
|
93
|
-
"rien a trancher",
|
|
94
|
-
"rien a ajouter",
|
|
95
|
-
"question factuelle resolue"
|
|
96
|
-
];
|
|
97
|
-
if (positivePatterns.some((pattern) => latest.includes(pattern))) {
|
|
124
|
+
if (messages.orchestrator.agreementPatterns.some((pattern) => latest.includes(pattern))) {
|
|
98
125
|
return true;
|
|
99
126
|
}
|
|
127
|
+
// Combinaison française historique : confirmation explicite + absence de point ouvert.
|
|
100
128
|
return (latest.includes("confirme") || latest.includes("acte")) &&
|
|
101
129
|
(latest.includes("aucun") || latest.includes("rien a trancher") || latest.includes("rien a ajouter"));
|
|
102
130
|
}
|
|
@@ -164,6 +192,27 @@ async function generateSummary(config, options, transcript, renderer, messages =
|
|
|
164
192
|
renderer?.message(summary.content);
|
|
165
193
|
return summary;
|
|
166
194
|
}
|
|
195
|
+
function toDebateFailure(error, context) {
|
|
196
|
+
if (error instanceof AdapterError) {
|
|
197
|
+
return {
|
|
198
|
+
phase: context.phase,
|
|
199
|
+
agent: context.agent ?? error.adapterName,
|
|
200
|
+
role: context.role,
|
|
201
|
+
turn: context.turn,
|
|
202
|
+
kind: error.kind,
|
|
203
|
+
message: error.message,
|
|
204
|
+
details: error.details
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
phase: context.phase,
|
|
209
|
+
agent: context.agent,
|
|
210
|
+
role: context.role,
|
|
211
|
+
turn: context.turn,
|
|
212
|
+
kind: "unknown",
|
|
213
|
+
message: error instanceof Error ? error.message : String(error)
|
|
214
|
+
};
|
|
215
|
+
}
|
|
167
216
|
/** Résout le model override pour un agent donné. Retourne `undefined` si l'agent n'est ni A ni B. */
|
|
168
217
|
function modelForAgent(options, agent) {
|
|
169
218
|
if (agent === options.agentA) {
|
package/dist/output.js
CHANGED
|
@@ -5,12 +5,12 @@ import { createTranslator } from "./i18n.js";
|
|
|
5
5
|
* Écrit le débat au format Markdown dans `outputDir`.
|
|
6
6
|
* Crée le répertoire si absent. Retourne le chemin absolu du fichier créé.
|
|
7
7
|
*/
|
|
8
|
-
export async function writeDebateMarkdown(outputDir, options, debateMessages, summary, stopReason, messages = createTranslator("fr")) {
|
|
8
|
+
export async function writeDebateMarkdown(outputDir, options, debateMessages, summary, stopReason, messages = createTranslator("fr"), failure) {
|
|
9
9
|
const safeDate = new Date().toISOString().replace(/[:.]/g, "-");
|
|
10
10
|
const fileName = `palabre-${slugifyTopic(options.topic)}-${safeDate}.debate.md`;
|
|
11
11
|
const filePath = path.resolve(outputDir, fileName);
|
|
12
12
|
await mkdir(path.dirname(filePath), { recursive: true });
|
|
13
|
-
await writeFile(filePath, renderDebateMarkdown(options, debateMessages, summary, stopReason, messages), "utf8");
|
|
13
|
+
await writeFile(filePath, renderDebateMarkdown(options, debateMessages, summary, stopReason, messages, failure), "utf8");
|
|
14
14
|
return filePath;
|
|
15
15
|
}
|
|
16
16
|
function slugifyTopic(topic) {
|
|
@@ -28,7 +28,7 @@ function slugifyTopic(topic) {
|
|
|
28
28
|
* Produit la représentation Markdown complète du débat.
|
|
29
29
|
* Fonction pure : aucun effet de bord sur le filesystem.
|
|
30
30
|
*/
|
|
31
|
-
export function renderDebateMarkdown(options, debateMessages, summary, stopReason, messages = createTranslator("fr")) {
|
|
31
|
+
export function renderDebateMarkdown(options, debateMessages, summary, stopReason, messages = createTranslator("fr"), failure) {
|
|
32
32
|
const lines = [
|
|
33
33
|
messages.output.title,
|
|
34
34
|
"",
|
|
@@ -44,6 +44,9 @@ export function renderDebateMarkdown(options, debateMessages, summary, stopReaso
|
|
|
44
44
|
for (const message of debateMessages) {
|
|
45
45
|
lines.push(`### ${message.agent} (${message.role})`, "", normalizeMarkdownForWindowsPreview(message.content.trim()), "");
|
|
46
46
|
}
|
|
47
|
+
if (failure) {
|
|
48
|
+
lines.push("---", "", messages.output.failureTitle, "", ...renderFailureBlock(failure, messages), "");
|
|
49
|
+
}
|
|
47
50
|
lines.push("---", "", messages.output.finalSummaryTitle, "", ...renderSummaryBlock(options, summary, messages));
|
|
48
51
|
return `${lines.join("\n")}\n`;
|
|
49
52
|
}
|
|
@@ -67,6 +70,20 @@ function renderSummaryBlock(options, summary, messages) {
|
|
|
67
70
|
""
|
|
68
71
|
];
|
|
69
72
|
}
|
|
73
|
+
function renderFailureBlock(failure, messages) {
|
|
74
|
+
const rows = [
|
|
75
|
+
[messages.output.fields.failurePhase, failure.phase],
|
|
76
|
+
[messages.output.fields.failureAgent, failure.agent ?? messages.output.no],
|
|
77
|
+
[messages.output.fields.failureTurn, failure.turn === undefined ? messages.output.no : String(failure.turn)],
|
|
78
|
+
[messages.output.fields.failureKind, failure.kind],
|
|
79
|
+
[messages.output.fields.failureMessage, failure.message]
|
|
80
|
+
];
|
|
81
|
+
return [
|
|
82
|
+
`| ${messages.output.tableField} | ${messages.output.tableValue} |`,
|
|
83
|
+
"| --- | --- |",
|
|
84
|
+
...rows.map(([label, value]) => `| ${escapeTableCell(label)} | ${escapeTableCell(value)} |`)
|
|
85
|
+
];
|
|
86
|
+
}
|
|
70
87
|
function normalizeMarkdownForWindowsPreview(content) {
|
|
71
88
|
return content.replace(/:\*\*/g, ":**");
|
|
72
89
|
}
|
package/dist/presets.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { detectionForCommand } from "./agentRegistry.js";
|
|
2
2
|
const presets = {
|
|
3
3
|
"codex-claude": {
|
|
4
4
|
agentA: "codex",
|
|
@@ -181,7 +181,7 @@ function checkAgentAvailability(agentName, config, discovery, messages) {
|
|
|
181
181
|
}
|
|
182
182
|
return available(agentName);
|
|
183
183
|
}
|
|
184
|
-
const detection =
|
|
184
|
+
const detection = detectionForCommand(agent.command, discovery);
|
|
185
185
|
if (!detection) {
|
|
186
186
|
// Les CLIs custom déclarées par l'utilisateur restent considérées utilisables :
|
|
187
187
|
// Palabre ne peut pas connaître leur sémantique sans les lancer.
|
|
@@ -191,25 +191,6 @@ function checkAgentAvailability(agentName, config, discovery, messages) {
|
|
|
191
191
|
? available(agentName)
|
|
192
192
|
: unavailable(agentName, messages?.presets.missingCommand(agentName, detection.command) ?? `commande non détectée pour ${agentName}: ${detection.command}`);
|
|
193
193
|
}
|
|
194
|
-
function knownCliDetection(agent, discovery) {
|
|
195
|
-
const command = normalizeCommandName(agent.command);
|
|
196
|
-
if (command === "codex")
|
|
197
|
-
return discovery.codex;
|
|
198
|
-
if (command === "claude")
|
|
199
|
-
return discovery.claude;
|
|
200
|
-
if (command === "gemini")
|
|
201
|
-
return discovery.gemini;
|
|
202
|
-
if (command === "agy")
|
|
203
|
-
return discovery.antigravity;
|
|
204
|
-
if (command === "antigravity")
|
|
205
|
-
return discovery.antigravity;
|
|
206
|
-
if (command === "opencode")
|
|
207
|
-
return discovery.opencode;
|
|
208
|
-
return undefined;
|
|
209
|
-
}
|
|
210
|
-
function normalizeCommandName(command) {
|
|
211
|
-
return path.basename(command).toLowerCase().replace(/\.(exe|cmd|ps1|bat)$/i, "");
|
|
212
|
-
}
|
|
213
194
|
function available(agent) {
|
|
214
195
|
return { agent, available: true, reason: "" };
|
|
215
196
|
}
|
package/dist/prompt.js
CHANGED
|
@@ -25,6 +25,8 @@ export function formatAgentPrompt(input) {
|
|
|
25
25
|
messages.sessionStartedAt(input.session.startedAt),
|
|
26
26
|
messages.turnProgress(input.turn, input.totalTurns),
|
|
27
27
|
"",
|
|
28
|
+
messages.responseLanguageInstruction,
|
|
29
|
+
"",
|
|
28
30
|
messages.objectiveTitle,
|
|
29
31
|
...messages.debateObjectives,
|
|
30
32
|
"",
|
|
@@ -56,6 +58,8 @@ function formatSummaryPrompt(input, messages) {
|
|
|
56
58
|
messages.cwd(input.session.cwd),
|
|
57
59
|
messages.sessionStartedAt(input.session.startedAt),
|
|
58
60
|
"",
|
|
61
|
+
messages.responseLanguageInstruction,
|
|
62
|
+
"",
|
|
59
63
|
messages.objectiveTitle,
|
|
60
64
|
...messages.summaryObjectives,
|
|
61
65
|
"",
|
|
@@ -99,6 +99,10 @@ class PrettyConsoleRenderer {
|
|
|
99
99
|
""
|
|
100
100
|
].join("\n"));
|
|
101
101
|
}
|
|
102
|
+
error(failure) {
|
|
103
|
+
this.thinkingEnd();
|
|
104
|
+
process.stderr.write(`\n${this.c("red", this.messages.common.errorPrefix)} ${formatFailureLocation(failure, this.messages)}: ${failure.message}\n`);
|
|
105
|
+
}
|
|
102
106
|
/** Affiche le chemin du fichier de sortie en vert à la fin du débat. */
|
|
103
107
|
done(outputPath) {
|
|
104
108
|
process.stdout.write(`\n\n${this.c("green", this.messages.renderers.exported(outputPath))}\n\n`);
|
|
@@ -175,6 +179,9 @@ class PlainConsoleRenderer {
|
|
|
175
179
|
summaryStart(agent, role) {
|
|
176
180
|
process.stdout.write(`\n[${this.messages.renderers.summaryTitle}] ${agent} (${role})...\n`);
|
|
177
181
|
}
|
|
182
|
+
error(failure) {
|
|
183
|
+
process.stderr.write(`\n${this.messages.common.errorPrefix}: ${formatFailureLocation(failure, this.messages)}: ${failure.message}\n`);
|
|
184
|
+
}
|
|
178
185
|
/** Affiche le chemin du fichier de sortie à la fin du débat. */
|
|
179
186
|
done(outputPath) {
|
|
180
187
|
process.stdout.write(`\n${this.messages.renderers.exported(outputPath)}\n`);
|
|
@@ -220,6 +227,13 @@ function formatContext(options, messages) {
|
|
|
220
227
|
}
|
|
221
228
|
return messages.renderers.injectedFiles(count);
|
|
222
229
|
}
|
|
230
|
+
function formatFailureLocation(failure, messages) {
|
|
231
|
+
if (failure.phase === "summary") {
|
|
232
|
+
return messages.renderers.summaryTitle;
|
|
233
|
+
}
|
|
234
|
+
const turn = failure.turn === undefined ? "" : `, turn ${failure.turn}`;
|
|
235
|
+
return `${failure.agent ?? "?"} (${failure.role ?? "?"}${turn})`;
|
|
236
|
+
}
|
|
223
237
|
/** Codes d'échappement ANSI utilisés par `PrettyConsoleRenderer`. */
|
|
224
238
|
const codes = {
|
|
225
239
|
reset: "\u001b[0m",
|
|
@@ -228,6 +242,7 @@ const codes = {
|
|
|
228
242
|
cyan: "\u001b[36m",
|
|
229
243
|
green: "\u001b[32m",
|
|
230
244
|
magenta: "\u001b[35m",
|
|
245
|
+
red: "\u001b[31m",
|
|
231
246
|
yellow: "\u001b[33m",
|
|
232
247
|
orange: "\u001b[38;5;208m",
|
|
233
248
|
pink: "\u001b[38;5;205m"
|
package/dist/renderers/ndjson.js
CHANGED
|
@@ -101,6 +101,10 @@ export class NdjsonRenderer {
|
|
|
101
101
|
this.currentRole = role;
|
|
102
102
|
this.emit({ type: "summary-start", agent, role });
|
|
103
103
|
}
|
|
104
|
+
/** Émet une erreur runtime structurée. */
|
|
105
|
+
error(failure) {
|
|
106
|
+
this.emit({ type: "error", ...failure });
|
|
107
|
+
}
|
|
104
108
|
/** Émet `done` avec le chemin du `.debate.md` écrit. */
|
|
105
109
|
done(outputPath) {
|
|
106
110
|
this.emit({ type: "done", outputPath });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "palabre",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "Orchestrateur de debat entre agents IA locaux, CLIs et Ollama.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
"prepack": "pnpm build",
|
|
39
39
|
"start": "node ./dist/index.js",
|
|
40
40
|
"test": "pnpm build:test && node --test .tmp/test-dist/tests/*.test.js",
|
|
41
|
-
"build:test": "node -e \"fs.rmSync('.tmp/test-dist',{recursive:true,force:true})\" && tsc -p tsconfig.test.json"
|
|
41
|
+
"build:test": "node -e \"fs.rmSync('.tmp/test-dist',{recursive:true,force:true})\" && tsc -p tsconfig.test.json",
|
|
42
|
+
"smoke:real-presets": "node -e \"fs.rmSync('.tmp/smoke-real',{recursive:true,force:true})\" && tsc --target ES2022 --module NodeNext --moduleResolution NodeNext --types node --outDir .tmp/smoke-real scripts/smoke_real_presets.ts && node .tmp/smoke-real/smoke_real_presets.js"
|
|
42
43
|
},
|
|
43
44
|
"engines": {
|
|
44
45
|
"node": ">=20"
|