@statechange/council 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +15 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/gui.d.ts +1 -0
- package/dist/commands/gui.js +63 -0
- package/dist/commands/gui.js.map +1 -0
- package/dist/commands/install.d.ts +5 -0
- package/dist/commands/install.js +131 -0
- package/dist/commands/install.js.map +1 -0
- package/dist-electron/index-B7VRloBj.js +1293 -0
- package/dist-electron/index-C03cLUYg.js +5911 -0
- package/dist-electron/index-Dw6I25AL.js +5911 -0
- package/dist-electron/index-NMn1koXq.js +31737 -0
- package/dist-electron/index-zpksia2X.js +5911 -0
- package/dist-electron/main-C_og2Wvn.js +32969 -0
- package/dist-electron/main-DBohq-SO.js +32969 -0
- package/dist-electron/main.js +791 -1217
- package/dist-electron/multipart-parser-Bdb1JOpV.js +353 -0
- package/dist-electron/multipart-parser-Cc_drctG.js +353 -0
- package/dist-electron/multipart-parser-Do9VIIY5.js +353 -0
- package/dist-electron/preload.mjs +1 -0
- package/dist-renderer/assets/Tableau10-B-NsZVaP.js +1 -0
- package/dist-renderer/assets/_commonjs-dynamic-modules-TDtrdbi3.js +1 -0
- package/dist-renderer/assets/ar-SA-G6X2FPQ2-CNUtTBRd.js +10 -0
- package/dist-renderer/assets/arc-BsV2XMg-.js +1 -0
- package/dist-renderer/assets/array-BKyUJesY.js +1 -0
- package/dist-renderer/assets/az-AZ-76LH7QW2-BU-6SB21.js +1 -0
- package/dist-renderer/assets/bg-BG-XCXSNQG7-L1xMEEzs.js +5 -0
- package/dist-renderer/assets/blockDiagram-38ab4fdb-BHS1f8SU.js +118 -0
- package/dist-renderer/assets/bn-BD-2XOGV67Q-BgaYSNWB.js +5 -0
- package/dist-renderer/assets/c4Diagram-3d4e48cf-D2Rl9Buo.js +10 -0
- package/dist-renderer/assets/ca-ES-6MX7JW3Y-DsFYhkLC.js +8 -0
- package/dist-renderer/assets/channel-D6rvl4sk.js +1 -0
- package/dist-renderer/assets/classDiagram-70f12bd4-BGxDdVFC.js +2 -0
- package/dist-renderer/assets/classDiagram-v2-f2320105-BuoUwdmW.js +2 -0
- package/dist-renderer/assets/clone-ya1OS7ZU.js +1 -0
- package/dist-renderer/assets/createText-2e5e7dd3-BX6eAgL1.js +7 -0
- package/dist-renderer/assets/cs-CZ-2BRQDIVT-CXV-i633.js +11 -0
- package/dist-renderer/assets/da-DK-5WZEPLOC-DmYIifsm.js +5 -0
- package/dist-renderer/assets/de-DE-XR44H4JA-B2T52TzN.js +8 -0
- package/dist-renderer/assets/directory-open-01563666-DWU9wJ6I.js +1 -0
- package/dist-renderer/assets/directory-open-4ed118d0-CunoC1EB.js +1 -0
- package/dist-renderer/assets/edges-e0da2a9e-ChSiyO2u.js +4 -0
- package/dist-renderer/assets/el-GR-BZB4AONW-DlS_8ZJJ.js +10 -0
- package/dist-renderer/assets/erDiagram-9861fffd-REAc3tDU.js +51 -0
- package/dist-renderer/assets/es-ES-U4NZUMDT-DL-zHXpT.js +9 -0
- package/dist-renderer/assets/eu-ES-A7QVB2H4-Cq0Kx947.js +11 -0
- package/dist-renderer/assets/fa-IR-HGAKTJCU-DJqZOqAL.js +8 -0
- package/dist-renderer/assets/fi-FI-Z5N7JZ37-CQiIDJrs.js +6 -0
- package/dist-renderer/assets/file-open-002ab408-DIuFHtCF.js +1 -0
- package/dist-renderer/assets/file-open-7c801643-684qeFg4.js +1 -0
- package/dist-renderer/assets/file-save-3189631c-C1wFhQhH.js +1 -0
- package/dist-renderer/assets/file-save-745eba88-Bb9F9Kg7.js +1 -0
- package/dist-renderer/assets/flowDb-956e92f1-iZnRwdLd.js +10 -0
- package/dist-renderer/assets/flowDiagram-66a62f08-Cww5ErID.js +4 -0
- package/dist-renderer/assets/flowDiagram-v2-96b9c2cf-DMQnUrJv.js +1 -0
- package/dist-renderer/assets/flowchart-elk-definition-4a651766-B3GjdX0I.js +139 -0
- package/dist-renderer/assets/fr-FR-RHASNOE6-DXRyDBSp.js +9 -0
- package/dist-renderer/assets/ganttDiagram-c361ad54-Dh5UuCf5.js +257 -0
- package/dist-renderer/assets/gitGraphDiagram-72cf32ee-CUrdiK9f.js +70 -0
- package/dist-renderer/assets/gl-ES-HMX3MZ6V-DB2LhjQl.js +10 -0
- package/dist-renderer/assets/graph-KS9cHKTv.js +1 -0
- package/dist-renderer/assets/he-IL-6SHJWFNN-CWdud4mG.js +10 -0
- package/dist-renderer/assets/hi-IN-IWLTKZ5I-DkfCxaBI.js +4 -0
- package/dist-renderer/assets/hu-HU-A5ZG7DT2-DVX9rddx.js +7 -0
- package/dist-renderer/assets/id-ID-SAP4L64H-CIosyByg.js +10 -0
- package/dist-renderer/assets/image-blob-reduce.esm-BLtmMM_J.js +2 -0
- package/dist-renderer/assets/index-3862675e-CVxUAJo4.js +1 -0
- package/dist-renderer/assets/index-3RkkhxnO.js +36 -0
- package/dist-renderer/assets/index-B61Czmwn.js +95 -0
- package/dist-renderer/assets/index-BF6L2_KC.css +1 -0
- package/dist-renderer/assets/infoDiagram-f8f76790-CWACEGb6.js +7 -0
- package/dist-renderer/assets/init-Gi6I4Gst.js +1 -0
- package/dist-renderer/assets/it-IT-JPQ66NNP-D-TuMToN.js +11 -0
- package/dist-renderer/assets/ja-JP-DBVTYXUO-CK6puaHx.js +8 -0
- package/dist-renderer/assets/journeyDiagram-49397b02-DM-laNCt.js +139 -0
- package/dist-renderer/assets/kaa-6HZHGXH3-CiRWfMKd.js +1 -0
- package/dist-renderer/assets/kab-KAB-ZGHBKWFO-D4w9BQ8H.js +8 -0
- package/dist-renderer/assets/katex-DhXJpUyf.js +261 -0
- package/dist-renderer/assets/kk-KZ-P5N5QNE5-RZi2AGvr.js +1 -0
- package/dist-renderer/assets/km-KH-HSX4SM5Z-Dm-OPxhU.js +11 -0
- package/dist-renderer/assets/ko-KR-MTYHY66A-PbPGVqVz.js +9 -0
- package/dist-renderer/assets/ku-TR-6OUDTVRD-Bw0cMtYj.js +9 -0
- package/dist-renderer/assets/layout-zADYSvqS.js +1 -0
- package/dist-renderer/assets/line-DfBkzhQg.js +1 -0
- package/dist-renderer/assets/linear-O3lyK7Iz.js +1 -0
- package/dist-renderer/assets/lt-LT-XHIRWOB4-06ru_RZe.js +3 -0
- package/dist-renderer/assets/lv-LV-5QDEKY6T-DsGBjFui.js +7 -0
- package/dist-renderer/assets/mindmap-definition-fc14e90a-Bg9UaNPK.js +415 -0
- package/dist-renderer/assets/mr-IN-CRQNXWMA-C1kWOErC.js +13 -0
- package/dist-renderer/assets/my-MM-5M5IBNSE-C8wD2OFs.js +1 -0
- package/dist-renderer/assets/nb-NO-T6EIAALU-B7ac-R_W.js +10 -0
- package/dist-renderer/assets/nl-NL-IS3SIHDZ-Up67nxB7.js +8 -0
- package/dist-renderer/assets/nn-NO-6E72VCQL-BPlkzXM9.js +8 -0
- package/dist-renderer/assets/oc-FR-POXYY2M6-TRLIkgml.js +8 -0
- package/dist-renderer/assets/ordinal-BENe2yWM.js +1 -0
- package/dist-renderer/assets/pa-IN-N4M65BXN-Dl7hF9RX.js +4 -0
- package/dist-renderer/assets/path-CbwjOpE9.js +1 -0
- package/dist-renderer/assets/percentages-BXMCSKIN-wBXtRqNn.js +199 -0
- package/dist-renderer/assets/pica-CGZUgvhx.js +2 -0
- package/dist-renderer/assets/pieDiagram-8a3498a8-ZPRkRtpu.js +35 -0
- package/dist-renderer/assets/pl-PL-T2D74RX3-DkwkT852.js +9 -0
- package/dist-renderer/assets/pt-BR-5N22H2LF-BMFWH_jN.js +9 -0
- package/dist-renderer/assets/pt-PT-UZXXM6DQ-BPSC-yrG.js +9 -0
- package/dist-renderer/assets/quadrantDiagram-120e2f19-DB1TyNBK.js +7 -0
- package/dist-renderer/assets/requirementDiagram-deff3bca-DqVc_A29.js +52 -0
- package/dist-renderer/assets/ro-RO-JPDTUUEW-CgkEXyU-.js +11 -0
- package/dist-renderer/assets/roundRect-mAH3dD0p.js +1 -0
- package/dist-renderer/assets/ru-RU-B4JR7IUQ-BJIahd0q.js +9 -0
- package/dist-renderer/assets/sankeyDiagram-04a897e0-DZqGbQ0e.js +8 -0
- package/dist-renderer/assets/sequenceDiagram-704730f1-D0Q04jBP.js +122 -0
- package/dist-renderer/assets/si-LK-N5RQ5JYF-DZ97xsfW.js +1 -0
- package/dist-renderer/assets/sk-SK-C5VTKIMK-3O0GcaTE.js +6 -0
- package/dist-renderer/assets/sl-SI-NN7IZMDC-x_-Ca4Po.js +6 -0
- package/dist-renderer/assets/stateDiagram-587899a1-BkW3tbK-.js +1 -0
- package/dist-renderer/assets/stateDiagram-v2-d93cdb3a-CZYVILLk.js +1 -0
- package/dist-renderer/assets/styles-6aaf32cf-BQKVAqEs.js +207 -0
- package/dist-renderer/assets/styles-9a916d00-DTjDY-zu.js +160 -0
- package/dist-renderer/assets/styles-c10674c1-CviHLZ2K.js +116 -0
- package/dist-renderer/assets/subset-shared.chunk-DIxPfZhi.js +22 -0
- package/dist-renderer/assets/subset-worker.chunk-DDMwauN2.js +1 -0
- package/dist-renderer/assets/sv-SE-XGPEYMSR-DmtUCQiw.js +10 -0
- package/dist-renderer/assets/svgDrawCommon-08f97a94-B6bAmadW.js +1 -0
- package/dist-renderer/assets/ta-IN-2NMHFXQM-D7fiXwNW.js +9 -0
- package/dist-renderer/assets/th-TH-HPSO5L25-D_F5D_Tj.js +2 -0
- package/dist-renderer/assets/timeline-definition-85554ec2-BZuaxri2.js +61 -0
- package/dist-renderer/assets/tr-TR-DEFEU3FU-CzwSWbpN.js +7 -0
- package/dist-renderer/assets/uk-UA-QMV73CPH-BaaxaBLn.js +6 -0
- package/dist-renderer/assets/vi-VN-M7AON7JQ-BQ9szPme.js +5 -0
- package/dist-renderer/assets/xychartDiagram-e933f94c-7rTYP6vy.js +7 -0
- package/dist-renderer/assets/zh-CN-LNUGB5OW-DDjjE3j0.js +10 -0
- package/dist-renderer/assets/zh-HK-E62DVLB3-CUaw1jmU.js +1 -0
- package/dist-renderer/assets/zh-TW-RAJ6MFWO-BFOuD9P0.js +9 -0
- package/dist-renderer/index.html +13 -0
- package/package.json +7 -3
- package/skills/council-install/SKILL.md +63 -0
package/dist-electron/main.js
CHANGED
|
@@ -1,583 +1,429 @@
|
|
|
1
|
-
import { shell, dialog, protocol, app, net, BrowserWindow, ipcMain } from "electron";
|
|
2
|
-
import { join, basename, resolve, dirname } from "node:path";
|
|
3
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
|
-
import { existsSync, writeFileSync, appendFileSync } from "node:fs";
|
|
5
|
-
import { readFile, readdir, stat, mkdir, rm, writeFile
|
|
6
|
-
import { execFile } from "node:child_process";
|
|
7
|
-
import { promisify } from "node:util";
|
|
8
|
-
import { homedir } from "node:os";
|
|
9
|
-
import
|
|
10
|
-
import { z } from "zod";
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import { GoogleGenerativeAI } from "@google/generative-ai";
|
|
14
|
-
import { Ollama } from "ollama";
|
|
15
|
-
import { createRequire } from "node:module";
|
|
1
|
+
import { shell as q, dialog as be, protocol as ue, app as j, net as ke, BrowserWindow as de, ipcMain as ve } from "electron";
|
|
2
|
+
import { join as u, basename as C, resolve as me, dirname as _e } from "node:path";
|
|
3
|
+
import { fileURLToPath as Ee, pathToFileURL as Ae } from "node:url";
|
|
4
|
+
import { existsSync as v, writeFileSync as Ie, appendFileSync as Se } from "node:fs";
|
|
5
|
+
import { readFile as y, readdir as H, stat as fe, mkdir as N, rm as B, writeFile as $, appendFile as Oe } from "node:fs/promises";
|
|
6
|
+
import { execFile as U } from "node:child_process";
|
|
7
|
+
import { promisify as R } from "node:util";
|
|
8
|
+
import { homedir as w } from "node:os";
|
|
9
|
+
import z from "gray-matter";
|
|
10
|
+
import { z as k } from "zod";
|
|
11
|
+
import Pe from "@anthropic-ai/sdk";
|
|
12
|
+
import $e from "openai";
|
|
13
|
+
import { GoogleGenerativeAI as Ce } from "@google/generative-ai";
|
|
14
|
+
import { Ollama as Ne } from "ollama";
|
|
15
|
+
import { createRequire as xe } from "node:module";
|
|
16
16
|
import "dotenv/config";
|
|
17
|
-
const
|
|
18
|
-
name:
|
|
19
|
-
description:
|
|
20
|
-
interests:
|
|
21
|
-
backend:
|
|
22
|
-
model:
|
|
23
|
-
skills:
|
|
24
|
-
temperature:
|
|
25
|
-
avatar:
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
join(homedir(), ".claude", "skills", skillName, "SKILL.md")
|
|
17
|
+
const Ue = k.object({
|
|
18
|
+
name: k.string(),
|
|
19
|
+
description: k.string(),
|
|
20
|
+
interests: k.array(k.string()).default([]),
|
|
21
|
+
backend: k.enum(["anthropic", "openai", "google", "ollama"]),
|
|
22
|
+
model: k.string().optional(),
|
|
23
|
+
skills: k.array(k.string()).default([]),
|
|
24
|
+
temperature: k.number().min(0).max(2).optional(),
|
|
25
|
+
avatar: k.string().optional()
|
|
26
|
+
}), Re = (n, r) => [
|
|
27
|
+
u(n, "skills", r, "SKILL.md"),
|
|
28
|
+
u(process.cwd(), ".claude", "skills", r, "SKILL.md"),
|
|
29
|
+
u(w(), ".agents", "skills", r, "SKILL.md"),
|
|
30
|
+
u(w(), ".claude", "skills", r, "SKILL.md")
|
|
32
31
|
];
|
|
33
|
-
async function
|
|
34
|
-
for (const
|
|
35
|
-
if (
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
return content.trim();
|
|
32
|
+
async function Te(n, r) {
|
|
33
|
+
for (const t of Re(r, n))
|
|
34
|
+
if (v(t)) {
|
|
35
|
+
const e = await y(t, "utf-8"), { content: o } = z(e);
|
|
36
|
+
return o.trim();
|
|
39
37
|
}
|
|
40
|
-
}
|
|
41
38
|
return null;
|
|
42
39
|
}
|
|
43
|
-
async function
|
|
44
|
-
const
|
|
45
|
-
for (const
|
|
46
|
-
const
|
|
47
|
-
|
|
48
|
-
sections.push(`## Skill: ${name}
|
|
40
|
+
async function Le(n, r) {
|
|
41
|
+
const t = [];
|
|
42
|
+
for (const e of n) {
|
|
43
|
+
const o = await Te(e, r);
|
|
44
|
+
o && t.push(`## Skill: ${e}
|
|
49
45
|
|
|
50
|
-
${
|
|
51
|
-
}
|
|
46
|
+
${o}`);
|
|
52
47
|
}
|
|
53
|
-
return
|
|
48
|
+
return t.join(`
|
|
49
|
+
|
|
50
|
+
`);
|
|
54
51
|
}
|
|
55
|
-
function
|
|
56
|
-
|
|
57
|
-
if (avatar.startsWith("http://") || avatar.startsWith("https://")) return avatar;
|
|
58
|
-
const absPath = avatar.startsWith("/") ? avatar : join(dirPath, avatar);
|
|
59
|
-
return `council-file://${absPath}`;
|
|
52
|
+
function De(n, r) {
|
|
53
|
+
return n ? n.startsWith("http://") || n.startsWith("https://") ? n : `council-file://${n.startsWith("/") ? n : u(r, n)}` : void 0;
|
|
60
54
|
}
|
|
61
|
-
async function
|
|
62
|
-
const
|
|
63
|
-
let
|
|
64
|
-
for (const
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
resolved = resolved.replace(match[0], `[Reference not found: ${refPath}]`);
|
|
72
|
-
}
|
|
55
|
+
async function Ke(n, r) {
|
|
56
|
+
const t = /\{\{(.+?)\}\}/g;
|
|
57
|
+
let e = n;
|
|
58
|
+
for (const o of n.matchAll(t)) {
|
|
59
|
+
const s = o[1].trim(), a = u(r, s);
|
|
60
|
+
if (v(a)) {
|
|
61
|
+
const i = await y(a, "utf-8");
|
|
62
|
+
e = e.replace(o[0], i.trim());
|
|
63
|
+
} else
|
|
64
|
+
e = e.replace(o[0], `[Reference not found: ${s}]`);
|
|
73
65
|
}
|
|
74
|
-
return
|
|
66
|
+
return e;
|
|
75
67
|
}
|
|
76
|
-
async function
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const skillContent = await resolveSkills(frontmatter.skills, absPath);
|
|
88
|
-
if (skillContent) {
|
|
89
|
-
systemPrompt += "\n\n" + skillContent;
|
|
90
|
-
}
|
|
68
|
+
async function ee(n) {
|
|
69
|
+
const r = me(n), t = u(r, "ABOUT.md");
|
|
70
|
+
if (!v(t))
|
|
71
|
+
throw new Error(`No ABOUT.md found in ${r}`);
|
|
72
|
+
const e = await y(t, "utf-8"), { data: o, content: s } = z(e), a = Ue.parse(o);
|
|
73
|
+
let i = await Ke(s.trim(), r);
|
|
74
|
+
if (a.skills.length > 0) {
|
|
75
|
+
const l = await Le(a.skills, r);
|
|
76
|
+
l && (i += `
|
|
77
|
+
|
|
78
|
+
` + l);
|
|
91
79
|
}
|
|
92
80
|
return {
|
|
93
|
-
id:
|
|
94
|
-
frontmatter,
|
|
95
|
-
systemPrompt,
|
|
96
|
-
dirPath:
|
|
97
|
-
avatarUrl:
|
|
81
|
+
id: C(r),
|
|
82
|
+
frontmatter: a,
|
|
83
|
+
systemPrompt: i,
|
|
84
|
+
dirPath: r,
|
|
85
|
+
avatarUrl: De(a.avatar, r)
|
|
98
86
|
};
|
|
99
87
|
}
|
|
100
|
-
async function
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
counsellors.push(c);
|
|
110
|
-
seenIds.add(c.id);
|
|
88
|
+
async function te(n, r) {
|
|
89
|
+
const t = [], e = /* @__PURE__ */ new Set();
|
|
90
|
+
if (r?.length) {
|
|
91
|
+
for (const o of r)
|
|
92
|
+
if (v(u(o, "ABOUT.md")))
|
|
93
|
+
try {
|
|
94
|
+
const s = await ee(o);
|
|
95
|
+
e.has(s.id) || (t.push(s), e.add(s.id));
|
|
96
|
+
} catch {
|
|
111
97
|
}
|
|
112
|
-
} catch {
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
98
|
}
|
|
116
|
-
if (
|
|
117
|
-
const
|
|
118
|
-
for (const
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
if (info.isDirectory() && existsSync(join(entryPath, "ABOUT.md"))) {
|
|
122
|
-
if (!seenIds.has(basename(entryPath))) {
|
|
123
|
-
counsellors.push(await loadSingleCounsellor(entryPath));
|
|
124
|
-
seenIds.add(basename(entryPath));
|
|
125
|
-
}
|
|
126
|
-
}
|
|
99
|
+
if (v(n)) {
|
|
100
|
+
const o = await H(n);
|
|
101
|
+
for (const s of o) {
|
|
102
|
+
const a = u(n, s);
|
|
103
|
+
(await fe(a)).isDirectory() && v(u(a, "ABOUT.md")) && (e.has(C(a)) || (t.push(await ee(a)), e.add(C(a))));
|
|
127
104
|
}
|
|
128
105
|
}
|
|
129
|
-
if (
|
|
130
|
-
throw new Error(`No counsellors found in ${
|
|
131
|
-
|
|
132
|
-
return counsellors;
|
|
106
|
+
if (t.length === 0)
|
|
107
|
+
throw new Error(`No counsellors found in ${n}. Each counsellor needs a directory with an ABOUT.md file.`);
|
|
108
|
+
return t;
|
|
133
109
|
}
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
const CLONES_DIR = join(homedir(), ".ai-council", "counsellors");
|
|
137
|
-
async function loadConfig$1() {
|
|
110
|
+
const je = R(U), pe = u(w(), ".ai-council", "config.json"), ne = u(w(), ".ai-council", "counsellors");
|
|
111
|
+
async function V() {
|
|
138
112
|
try {
|
|
139
|
-
return JSON.parse(await
|
|
113
|
+
return JSON.parse(await y(pe, "utf-8"));
|
|
140
114
|
} catch {
|
|
141
115
|
return { backends: {} };
|
|
142
116
|
}
|
|
143
117
|
}
|
|
144
|
-
async function
|
|
145
|
-
const
|
|
146
|
-
await
|
|
147
|
-
await writeFile(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
|
|
118
|
+
async function X(n) {
|
|
119
|
+
const r = u(w(), ".ai-council");
|
|
120
|
+
await N(r, { recursive: !0 }), await $(pe, JSON.stringify(n, null, 2), "utf-8");
|
|
148
121
|
}
|
|
149
|
-
function
|
|
150
|
-
return Object.values(
|
|
122
|
+
function oe(n) {
|
|
123
|
+
return Object.values(n.counsellors ?? {}).map((r) => r.path);
|
|
151
124
|
}
|
|
152
|
-
async function
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
throw new Error(`Counsellor "${id}" is already registered (path: ${registry[id].path})`);
|
|
163
|
-
}
|
|
164
|
-
const raw = await readFile(aboutPath, "utf-8");
|
|
165
|
-
const nameMatch = raw.match(/^name:\s*["']?(.+?)["']?\s*$/m);
|
|
166
|
-
const displayName = nameMatch?.[1] ?? id;
|
|
167
|
-
registry[id] = {
|
|
168
|
-
path: absPath,
|
|
125
|
+
async function Be(n) {
|
|
126
|
+
const r = me(n), t = u(r, "ABOUT.md");
|
|
127
|
+
if (!v(t))
|
|
128
|
+
throw new Error(`No ABOUT.md found in ${r}`);
|
|
129
|
+
const e = C(r), o = await V(), s = o.counsellors ?? {};
|
|
130
|
+
if (s[e])
|
|
131
|
+
throw new Error(`Counsellor "${e}" is already registered (path: ${s[e].path})`);
|
|
132
|
+
const l = (await y(t, "utf-8")).match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1] ?? e;
|
|
133
|
+
return s[e] = {
|
|
134
|
+
path: r,
|
|
169
135
|
source: "local",
|
|
170
136
|
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
171
|
-
};
|
|
172
|
-
config.counsellors = registry;
|
|
173
|
-
await saveConfig(config);
|
|
174
|
-
return { id, name: displayName };
|
|
137
|
+
}, o.counsellors = s, await X(o), { id: e, name: l };
|
|
175
138
|
}
|
|
176
|
-
async function
|
|
177
|
-
await
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
throw new Error(`Counsellor "${id}" is already registered`);
|
|
191
|
-
}
|
|
192
|
-
const raw = await readFile(join(clonePath, "ABOUT.md"), "utf-8");
|
|
193
|
-
const nameMatch = raw.match(/^name:\s*["']?(.+?)["']?\s*$/m);
|
|
194
|
-
const displayName = nameMatch?.[1] ?? id;
|
|
195
|
-
registry[id] = {
|
|
196
|
-
path: clonePath,
|
|
139
|
+
async function Ge(n) {
|
|
140
|
+
await N(ne, { recursive: !0 });
|
|
141
|
+
const r = C(n, ".git").replace(/\.git$/, ""), t = u(ne, r);
|
|
142
|
+
if (v(t))
|
|
143
|
+
throw new Error(`Directory already exists: ${t}. Remove it first or use a different URL.`);
|
|
144
|
+
await je("git", ["clone", "--depth", "1", n, t]);
|
|
145
|
+
const e = [], o = await V(), s = o.counsellors ?? {};
|
|
146
|
+
if (v(u(t, "ABOUT.md"))) {
|
|
147
|
+
const a = r;
|
|
148
|
+
if (s[a])
|
|
149
|
+
throw new Error(`Counsellor "${a}" is already registered`);
|
|
150
|
+
const c = (await y(u(t, "ABOUT.md"), "utf-8")).match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1] ?? a;
|
|
151
|
+
s[a] = {
|
|
152
|
+
path: t,
|
|
197
153
|
source: "git",
|
|
198
|
-
url,
|
|
154
|
+
url: n,
|
|
199
155
|
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
200
|
-
};
|
|
201
|
-
results.push({ id, name: displayName });
|
|
156
|
+
}, e.push({ id: a, name: c });
|
|
202
157
|
} else {
|
|
203
|
-
const
|
|
204
|
-
for (const
|
|
205
|
-
if (
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const displayName = nameMatch?.[1] ?? id;
|
|
214
|
-
registry[id] = {
|
|
215
|
-
path: entryPath,
|
|
158
|
+
const a = await H(t);
|
|
159
|
+
for (const i of a) {
|
|
160
|
+
if (i.startsWith(".")) continue;
|
|
161
|
+
const l = u(t, i);
|
|
162
|
+
if ((await fe(l)).isDirectory() && v(u(l, "ABOUT.md"))) {
|
|
163
|
+
const m = i;
|
|
164
|
+
if (s[m]) continue;
|
|
165
|
+
const p = (await y(u(l, "ABOUT.md"), "utf-8")).match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1] ?? m;
|
|
166
|
+
s[m] = {
|
|
167
|
+
path: l,
|
|
216
168
|
source: "git",
|
|
217
|
-
url,
|
|
169
|
+
url: n,
|
|
218
170
|
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
219
|
-
};
|
|
220
|
-
results.push({ id, name: displayName });
|
|
171
|
+
}, e.push({ id: m, name: p });
|
|
221
172
|
}
|
|
222
173
|
}
|
|
223
|
-
if (
|
|
224
|
-
await
|
|
225
|
-
throw new Error(`No counsellors found in cloned repository (no ABOUT.md files)`);
|
|
226
|
-
}
|
|
174
|
+
if (e.length === 0)
|
|
175
|
+
throw await B(t, { recursive: !0, force: !0 }), new Error("No counsellors found in cloned repository (no ABOUT.md files)");
|
|
227
176
|
}
|
|
228
|
-
|
|
229
|
-
await saveConfig(config);
|
|
230
|
-
return results;
|
|
177
|
+
return o.counsellors = s, await X(o), e;
|
|
231
178
|
}
|
|
232
|
-
async function
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
throw new Error(`Counsellor "${id}" is not registered`);
|
|
238
|
-
}
|
|
239
|
-
if (deleteFiles && entry.source === "git" && existsSync(entry.path)) {
|
|
240
|
-
await rm(entry.path, { recursive: true, force: true });
|
|
241
|
-
}
|
|
242
|
-
delete registry[id];
|
|
243
|
-
config.counsellors = registry;
|
|
244
|
-
await saveConfig(config);
|
|
179
|
+
async function Fe(n, r = !1) {
|
|
180
|
+
const t = await V(), e = t.counsellors ?? {}, o = e[n];
|
|
181
|
+
if (!o)
|
|
182
|
+
throw new Error(`Counsellor "${n}" is not registered`);
|
|
183
|
+
r && o.source === "git" && v(o.path) && await B(o.path, { recursive: !0, force: !0 }), delete e[n], t.counsellors = e, await X(t);
|
|
245
184
|
}
|
|
246
|
-
function
|
|
247
|
-
const
|
|
248
|
-
apiKey:
|
|
249
|
-
...
|
|
185
|
+
function We(n) {
|
|
186
|
+
const r = new Pe({
|
|
187
|
+
apiKey: n.apiKey ?? process.env.ANTHROPIC_API_KEY,
|
|
188
|
+
...n.baseUrl ? { baseURL: n.baseUrl } : {}
|
|
250
189
|
});
|
|
251
190
|
return {
|
|
252
191
|
name: "anthropic",
|
|
253
192
|
defaultModel: "claude-sonnet-4-5-20250929",
|
|
254
|
-
async chat(
|
|
255
|
-
const
|
|
256
|
-
model:
|
|
193
|
+
async chat(t) {
|
|
194
|
+
const e = await r.messages.create({
|
|
195
|
+
model: t.model,
|
|
257
196
|
max_tokens: 4096,
|
|
258
|
-
system:
|
|
259
|
-
messages:
|
|
260
|
-
role:
|
|
261
|
-
content:
|
|
197
|
+
system: t.systemPrompt,
|
|
198
|
+
messages: t.messages.map((s) => ({
|
|
199
|
+
role: s.role,
|
|
200
|
+
content: s.content
|
|
262
201
|
})),
|
|
263
|
-
...
|
|
202
|
+
...t.temperature !== void 0 ? { temperature: t.temperature } : {}
|
|
264
203
|
});
|
|
265
|
-
const textBlock = response.content.find((b) => b.type === "text");
|
|
266
204
|
return {
|
|
267
|
-
content:
|
|
205
|
+
content: e.content.find((s) => s.type === "text")?.text ?? "",
|
|
268
206
|
tokenUsage: {
|
|
269
|
-
input:
|
|
270
|
-
output:
|
|
207
|
+
input: e.usage.input_tokens,
|
|
208
|
+
output: e.usage.output_tokens
|
|
271
209
|
}
|
|
272
210
|
};
|
|
273
211
|
},
|
|
274
|
-
async *chatStream(
|
|
275
|
-
const
|
|
276
|
-
model:
|
|
212
|
+
async *chatStream(t) {
|
|
213
|
+
const e = r.messages.stream({
|
|
214
|
+
model: t.model,
|
|
277
215
|
max_tokens: 4096,
|
|
278
|
-
system:
|
|
279
|
-
messages:
|
|
280
|
-
role:
|
|
281
|
-
content:
|
|
216
|
+
system: t.systemPrompt,
|
|
217
|
+
messages: t.messages.map((s) => ({
|
|
218
|
+
role: s.role,
|
|
219
|
+
content: s.content
|
|
282
220
|
})),
|
|
283
|
-
...
|
|
221
|
+
...t.temperature !== void 0 ? { temperature: t.temperature } : {}
|
|
284
222
|
});
|
|
285
|
-
for await (const
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
const finalMessage = await stream.finalMessage();
|
|
223
|
+
for await (const s of e)
|
|
224
|
+
s.type === "content_block_delta" && s.delta.type === "text_delta" && (yield { delta: s.delta.text });
|
|
225
|
+
const o = await e.finalMessage();
|
|
291
226
|
yield {
|
|
292
227
|
delta: "",
|
|
293
228
|
tokenUsage: {
|
|
294
|
-
input:
|
|
295
|
-
output:
|
|
229
|
+
input: o.usage.input_tokens,
|
|
230
|
+
output: o.usage.output_tokens
|
|
296
231
|
}
|
|
297
232
|
};
|
|
298
233
|
}
|
|
299
234
|
};
|
|
300
235
|
}
|
|
301
|
-
function
|
|
302
|
-
const
|
|
303
|
-
apiKey:
|
|
304
|
-
...
|
|
236
|
+
function Ye(n) {
|
|
237
|
+
const r = new $e({
|
|
238
|
+
apiKey: n.apiKey ?? process.env.OPENAI_API_KEY,
|
|
239
|
+
...n.baseUrl ? { baseURL: n.baseUrl } : {}
|
|
305
240
|
});
|
|
306
241
|
return {
|
|
307
242
|
name: "openai",
|
|
308
243
|
defaultModel: "gpt-4o",
|
|
309
|
-
async chat(
|
|
310
|
-
const
|
|
311
|
-
model:
|
|
244
|
+
async chat(t) {
|
|
245
|
+
const e = await r.chat.completions.create({
|
|
246
|
+
model: t.model,
|
|
312
247
|
messages: [
|
|
313
|
-
{ role: "system", content:
|
|
314
|
-
...
|
|
315
|
-
role:
|
|
316
|
-
content:
|
|
248
|
+
{ role: "system", content: t.systemPrompt },
|
|
249
|
+
...t.messages.map((s) => ({
|
|
250
|
+
role: s.role,
|
|
251
|
+
content: s.content
|
|
317
252
|
}))
|
|
318
253
|
],
|
|
319
|
-
...
|
|
254
|
+
...t.temperature !== void 0 ? { temperature: t.temperature } : {}
|
|
320
255
|
});
|
|
321
|
-
const choice = response.choices[0];
|
|
322
256
|
return {
|
|
323
|
-
content:
|
|
324
|
-
tokenUsage:
|
|
257
|
+
content: e.choices[0]?.message?.content ?? "",
|
|
258
|
+
tokenUsage: e.usage ? { input: e.usage.prompt_tokens, output: e.usage.completion_tokens } : void 0
|
|
325
259
|
};
|
|
326
260
|
},
|
|
327
|
-
async *chatStream(
|
|
328
|
-
const
|
|
329
|
-
model:
|
|
261
|
+
async *chatStream(t) {
|
|
262
|
+
const e = await r.chat.completions.create({
|
|
263
|
+
model: t.model,
|
|
330
264
|
messages: [
|
|
331
|
-
{ role: "system", content:
|
|
332
|
-
...
|
|
333
|
-
role:
|
|
334
|
-
content:
|
|
265
|
+
{ role: "system", content: t.systemPrompt },
|
|
266
|
+
...t.messages.map((o) => ({
|
|
267
|
+
role: o.role,
|
|
268
|
+
content: o.content
|
|
335
269
|
}))
|
|
336
270
|
],
|
|
337
|
-
...
|
|
338
|
-
stream:
|
|
339
|
-
stream_options: { include_usage:
|
|
271
|
+
...t.temperature !== void 0 ? { temperature: t.temperature } : {},
|
|
272
|
+
stream: !0,
|
|
273
|
+
stream_options: { include_usage: !0 }
|
|
340
274
|
});
|
|
341
|
-
for await (const
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
input: chunk.usage.prompt_tokens,
|
|
351
|
-
output: chunk.usage.completion_tokens
|
|
352
|
-
}
|
|
353
|
-
};
|
|
354
|
-
}
|
|
275
|
+
for await (const o of e) {
|
|
276
|
+
const s = o.choices[0]?.delta?.content;
|
|
277
|
+
s && (yield { delta: s }), o.usage && (yield {
|
|
278
|
+
delta: "",
|
|
279
|
+
tokenUsage: {
|
|
280
|
+
input: o.usage.prompt_tokens,
|
|
281
|
+
output: o.usage.completion_tokens
|
|
282
|
+
}
|
|
283
|
+
});
|
|
355
284
|
}
|
|
356
285
|
}
|
|
357
286
|
};
|
|
358
287
|
}
|
|
359
|
-
function
|
|
360
|
-
const
|
|
361
|
-
const genAI = new GoogleGenerativeAI(apiKey);
|
|
288
|
+
function Me(n) {
|
|
289
|
+
const r = n.apiKey ?? process.env.GOOGLE_API_KEY ?? "", t = new Ce(r);
|
|
362
290
|
return {
|
|
363
291
|
name: "google",
|
|
364
292
|
defaultModel: "gemini-2.0-flash",
|
|
365
|
-
async chat(
|
|
366
|
-
const
|
|
367
|
-
model:
|
|
368
|
-
systemInstruction:
|
|
293
|
+
async chat(e) {
|
|
294
|
+
const o = t.getGenerativeModel({
|
|
295
|
+
model: e.model,
|
|
296
|
+
systemInstruction: e.systemPrompt,
|
|
369
297
|
generationConfig: {
|
|
370
|
-
...
|
|
298
|
+
...e.temperature !== void 0 ? { temperature: e.temperature } : {}
|
|
371
299
|
}
|
|
372
|
-
})
|
|
373
|
-
const history = request.messages.slice(0, -1).map((m) => ({
|
|
300
|
+
}), s = e.messages.slice(0, -1).map((m) => ({
|
|
374
301
|
role: m.role === "assistant" ? "model" : "user",
|
|
375
302
|
parts: [{ text: m.content }]
|
|
376
|
-
}));
|
|
377
|
-
const chat = model.startChat({ history });
|
|
378
|
-
const lastMessage = request.messages[request.messages.length - 1];
|
|
379
|
-
const result = await chat.sendMessage(lastMessage?.content ?? "");
|
|
380
|
-
const response = result.response;
|
|
303
|
+
})), a = o.startChat({ history: s }), i = e.messages[e.messages.length - 1], c = (await a.sendMessage(i?.content ?? "")).response;
|
|
381
304
|
return {
|
|
382
|
-
content:
|
|
383
|
-
tokenUsage:
|
|
384
|
-
input:
|
|
385
|
-
output:
|
|
305
|
+
content: c.text(),
|
|
306
|
+
tokenUsage: c.usageMetadata ? {
|
|
307
|
+
input: c.usageMetadata.promptTokenCount ?? 0,
|
|
308
|
+
output: c.usageMetadata.candidatesTokenCount ?? 0
|
|
386
309
|
} : void 0
|
|
387
310
|
};
|
|
388
311
|
},
|
|
389
|
-
async *chatStream(
|
|
390
|
-
const
|
|
391
|
-
model:
|
|
392
|
-
systemInstruction:
|
|
312
|
+
async *chatStream(e) {
|
|
313
|
+
const o = t.getGenerativeModel({
|
|
314
|
+
model: e.model,
|
|
315
|
+
systemInstruction: e.systemPrompt,
|
|
393
316
|
generationConfig: {
|
|
394
|
-
...
|
|
317
|
+
...e.temperature !== void 0 ? { temperature: e.temperature } : {}
|
|
395
318
|
}
|
|
396
|
-
})
|
|
397
|
-
const history = request.messages.slice(0, -1).map((m) => ({
|
|
319
|
+
}), s = e.messages.slice(0, -1).map((m) => ({
|
|
398
320
|
role: m.role === "assistant" ? "model" : "user",
|
|
399
321
|
parts: [{ text: m.content }]
|
|
400
|
-
}));
|
|
401
|
-
const
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
for await (const chunk of result.stream) {
|
|
405
|
-
const text = chunk.text();
|
|
406
|
-
if (text) {
|
|
407
|
-
yield { delta: text };
|
|
408
|
-
}
|
|
322
|
+
})), a = o.startChat({ history: s }), i = e.messages[e.messages.length - 1], l = await a.sendMessageStream(i?.content ?? "");
|
|
323
|
+
for await (const m of l.stream) {
|
|
324
|
+
const g = m.text();
|
|
325
|
+
g && (yield { delta: g });
|
|
409
326
|
}
|
|
410
|
-
const
|
|
327
|
+
const c = await l.response;
|
|
411
328
|
yield {
|
|
412
329
|
delta: "",
|
|
413
|
-
tokenUsage:
|
|
414
|
-
input:
|
|
415
|
-
output:
|
|
330
|
+
tokenUsage: c.usageMetadata ? {
|
|
331
|
+
input: c.usageMetadata.promptTokenCount ?? 0,
|
|
332
|
+
output: c.usageMetadata.candidatesTokenCount ?? 0
|
|
416
333
|
} : void 0
|
|
417
334
|
};
|
|
418
335
|
}
|
|
419
336
|
};
|
|
420
337
|
}
|
|
421
|
-
function
|
|
422
|
-
const
|
|
423
|
-
host:
|
|
338
|
+
function Je(n) {
|
|
339
|
+
const r = new Ne({
|
|
340
|
+
host: n.baseUrl ?? "http://localhost:11434"
|
|
424
341
|
});
|
|
425
342
|
return {
|
|
426
343
|
name: "ollama",
|
|
427
344
|
defaultModel: "llama3.2",
|
|
428
|
-
async chat(
|
|
429
|
-
const
|
|
430
|
-
model:
|
|
345
|
+
async chat(t) {
|
|
346
|
+
const e = await r.chat({
|
|
347
|
+
model: t.model,
|
|
431
348
|
messages: [
|
|
432
|
-
{ role: "system", content:
|
|
433
|
-
...
|
|
434
|
-
role:
|
|
435
|
-
content:
|
|
349
|
+
{ role: "system", content: t.systemPrompt },
|
|
350
|
+
...t.messages.map((o) => ({
|
|
351
|
+
role: o.role,
|
|
352
|
+
content: o.content
|
|
436
353
|
}))
|
|
437
354
|
],
|
|
438
355
|
options: {
|
|
439
|
-
...
|
|
356
|
+
...t.temperature !== void 0 ? { temperature: t.temperature } : {}
|
|
440
357
|
}
|
|
441
358
|
});
|
|
442
359
|
return {
|
|
443
|
-
content:
|
|
444
|
-
tokenUsage:
|
|
445
|
-
input:
|
|
446
|
-
output:
|
|
360
|
+
content: e.message.content,
|
|
361
|
+
tokenUsage: e.prompt_eval_count !== void 0 ? {
|
|
362
|
+
input: e.prompt_eval_count ?? 0,
|
|
363
|
+
output: e.eval_count ?? 0
|
|
447
364
|
} : void 0
|
|
448
365
|
};
|
|
449
366
|
},
|
|
450
|
-
async *chatStream(
|
|
451
|
-
const
|
|
452
|
-
model:
|
|
367
|
+
async *chatStream(t) {
|
|
368
|
+
const e = await r.chat({
|
|
369
|
+
model: t.model,
|
|
453
370
|
messages: [
|
|
454
|
-
{ role: "system", content:
|
|
455
|
-
...
|
|
456
|
-
role:
|
|
457
|
-
content:
|
|
371
|
+
{ role: "system", content: t.systemPrompt },
|
|
372
|
+
...t.messages.map((a) => ({
|
|
373
|
+
role: a.role,
|
|
374
|
+
content: a.content
|
|
458
375
|
}))
|
|
459
376
|
],
|
|
460
377
|
options: {
|
|
461
|
-
...
|
|
378
|
+
...t.temperature !== void 0 ? { temperature: t.temperature } : {}
|
|
462
379
|
},
|
|
463
|
-
stream:
|
|
380
|
+
stream: !0
|
|
464
381
|
});
|
|
465
|
-
let
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
if (chunk.message.content) {
|
|
469
|
-
yield { delta: chunk.message.content };
|
|
470
|
-
}
|
|
471
|
-
if (chunk.done) {
|
|
472
|
-
promptEvalCount = chunk.prompt_eval_count;
|
|
473
|
-
evalCount = chunk.eval_count;
|
|
474
|
-
}
|
|
475
|
-
}
|
|
382
|
+
let o, s;
|
|
383
|
+
for await (const a of e)
|
|
384
|
+
a.message.content && (yield { delta: a.message.content }), a.done && (o = a.prompt_eval_count, s = a.eval_count);
|
|
476
385
|
yield {
|
|
477
386
|
delta: "",
|
|
478
|
-
tokenUsage:
|
|
387
|
+
tokenUsage: o !== void 0 ? { input: o ?? 0, output: s ?? 0 } : void 0
|
|
479
388
|
};
|
|
480
389
|
}
|
|
481
390
|
};
|
|
482
391
|
}
|
|
483
|
-
const
|
|
484
|
-
anthropic:
|
|
485
|
-
openai:
|
|
486
|
-
google:
|
|
487
|
-
ollama:
|
|
392
|
+
const se = {
|
|
393
|
+
anthropic: We,
|
|
394
|
+
openai: Ye,
|
|
395
|
+
google: Me,
|
|
396
|
+
ollama: Je
|
|
488
397
|
};
|
|
489
|
-
let
|
|
490
|
-
async function
|
|
491
|
-
if (
|
|
492
|
-
const
|
|
398
|
+
let T = null;
|
|
399
|
+
async function He() {
|
|
400
|
+
if (T) return T;
|
|
401
|
+
const n = u(w(), ".ai-council", "config.json");
|
|
493
402
|
try {
|
|
494
|
-
const
|
|
495
|
-
|
|
403
|
+
const r = await y(n, "utf-8");
|
|
404
|
+
T = JSON.parse(r);
|
|
496
405
|
} catch {
|
|
497
|
-
|
|
406
|
+
T = { backends: {} };
|
|
498
407
|
}
|
|
499
|
-
return
|
|
408
|
+
return T;
|
|
500
409
|
}
|
|
501
|
-
const
|
|
502
|
-
function
|
|
503
|
-
|
|
504
|
-
backendCache.clear();
|
|
410
|
+
const M = /* @__PURE__ */ new Map();
|
|
411
|
+
function ze() {
|
|
412
|
+
T = null, M.clear();
|
|
505
413
|
}
|
|
506
|
-
async function
|
|
507
|
-
const
|
|
508
|
-
if (
|
|
509
|
-
const
|
|
510
|
-
if (!
|
|
511
|
-
throw new Error(`Unknown backend: "${
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
const backendConfig = config.backends[name] ?? {};
|
|
515
|
-
const backend = factory(backendConfig);
|
|
516
|
-
backendCache.set(name, backend);
|
|
517
|
-
return backend;
|
|
414
|
+
async function G(n) {
|
|
415
|
+
const r = M.get(n);
|
|
416
|
+
if (r) return r;
|
|
417
|
+
const t = se[n];
|
|
418
|
+
if (!t)
|
|
419
|
+
throw new Error(`Unknown backend: "${n}". Available: ${Object.keys(se).join(", ")}`);
|
|
420
|
+
const o = (await He()).backends[n] ?? {}, s = t(o);
|
|
421
|
+
return M.set(n, s), s;
|
|
518
422
|
}
|
|
519
|
-
function
|
|
520
|
-
return `
|
|
521
|
-
## Excalidraw Element Reference
|
|
522
|
-
|
|
523
|
-
Output a JSON array of Excalidraw elements. Each element needs these fields:
|
|
524
|
-
|
|
525
|
-
### Common Fields (all elements)
|
|
526
|
-
- \`type\`: "rectangle" | "ellipse" | "diamond" | "text" | "arrow" | "line"
|
|
527
|
-
- \`id\`: unique string (e.g. "rect1", "text1", "arrow1")
|
|
528
|
-
- \`x\`, \`y\`: number (top-left origin, x increases right, y increases down)
|
|
529
|
-
- \`width\`, \`height\`: number
|
|
530
|
-
- \`strokeColor\`: hex string (e.g. "#1e1e1e")
|
|
531
|
-
- \`backgroundColor\`: hex string or "transparent"
|
|
532
|
-
- \`fillStyle\`: "solid" | "hachure" | "cross-hatch"
|
|
533
|
-
- \`strokeWidth\`: 1 | 2 | 4
|
|
534
|
-
- \`roughness\`: 0 (sharp) | 1 (sketchy)
|
|
535
|
-
- \`opacity\`: 100
|
|
536
|
-
- \`angle\`: 0
|
|
537
|
-
- \`seed\`: any integer (e.g. 1)
|
|
538
|
-
- \`version\`: 1
|
|
539
|
-
- \`isDeleted\`: false
|
|
540
|
-
- \`groupIds\`: []
|
|
541
|
-
- \`boundElements\`: null or array of { id: string, type: "text" | "arrow" }
|
|
542
|
-
- \`link\`: null
|
|
543
|
-
- \`locked\`: false
|
|
544
|
-
|
|
545
|
-
### Text Elements
|
|
546
|
-
Additional fields: \`text\`, \`fontSize\` (16-24), \`fontFamily\` (1=hand, 2=normal, 3=mono), \`textAlign\` ("left"|"center"|"right"), \`verticalAlign\` ("top"|"middle"), \`baseline\`: 0, \`containerId\`: null or parent shape id
|
|
547
|
-
|
|
548
|
-
### Arrow/Line Elements
|
|
549
|
-
Additional fields: \`points\` (array of [x,y] relative to element x,y — first point always [0,0]), \`startBinding\` and \`endBinding\`: null or { elementId: string, focus: 0, gap: 5 }, \`lastCommittedPoint\`: null, \`startArrowhead\`: null, \`endArrowhead\`: "arrow" | null
|
|
550
|
-
|
|
551
|
-
### Color Palette
|
|
552
|
-
- Blue: "#1971c2", Light blue bg: "#a5d8ff"
|
|
553
|
-
- Green: "#2f9e44", Light green bg: "#b2f2bb"
|
|
554
|
-
- Red: "#e03131", Light red bg: "#ffc9c9"
|
|
555
|
-
- Orange: "#e8590c", Light orange bg: "#ffd8a8"
|
|
556
|
-
- Purple: "#7048e8", Light purple bg: "#d0bfff"
|
|
557
|
-
- Yellow: "#f08c00", Light yellow bg: "#ffec99"
|
|
558
|
-
- Gray: "#868e96", Light gray bg: "#dee2e6"
|
|
559
|
-
- Dark: "#1e1e1e"
|
|
560
|
-
|
|
561
|
-
### Layout Tips
|
|
562
|
-
- Space shapes ~200px apart horizontally, ~150px vertically
|
|
563
|
-
- Typical shape size: 160×80 for rectangles, 120×60 for ellipses
|
|
564
|
-
- Center text inside shapes using containerId
|
|
565
|
-
- Use arrows to show relationships (agreement, disagreement, influence)
|
|
566
|
-
|
|
567
|
-
### Compact Example
|
|
568
|
-
\`\`\`json
|
|
569
|
-
[
|
|
570
|
-
{"type":"rectangle","id":"r1","x":50,"y":50,"width":160,"height":80,"strokeColor":"#1971c2","backgroundColor":"#a5d8ff","fillStyle":"solid","strokeWidth":2,"roughness":1,"opacity":100,"angle":0,"seed":1,"version":1,"isDeleted":false,"groupIds":[],"boundElements":[{"id":"t1","type":"text"},{"id":"a1","type":"arrow"}],"link":null,"locked":false},
|
|
571
|
-
{"type":"text","id":"t1","x":60,"y":70,"width":140,"height":40,"text":"Counsellor A","fontSize":16,"fontFamily":2,"textAlign":"center","verticalAlign":"middle","baseline":0,"containerId":"r1","strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":1,"roughness":0,"opacity":100,"angle":0,"seed":2,"version":1,"isDeleted":false,"groupIds":[],"boundElements":null,"link":null,"locked":false},
|
|
572
|
-
{"type":"rectangle","id":"r2","x":350,"y":50,"width":160,"height":80,"strokeColor":"#2f9e44","backgroundColor":"#b2f2bb","fillStyle":"solid","strokeWidth":2,"roughness":1,"opacity":100,"angle":0,"seed":3,"version":1,"isDeleted":false,"groupIds":[],"boundElements":[{"id":"t2","type":"text"},{"id":"a1","type":"arrow"}],"link":null,"locked":false},
|
|
573
|
-
{"type":"text","id":"t2","x":360,"y":70,"width":140,"height":40,"text":"Counsellor B","fontSize":16,"fontFamily":2,"textAlign":"center","verticalAlign":"middle","baseline":0,"containerId":"r2","strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":1,"roughness":0,"opacity":100,"angle":0,"seed":4,"version":1,"isDeleted":false,"groupIds":[],"boundElements":null,"link":null,"locked":false},
|
|
574
|
-
{"type":"arrow","id":"a1","x":210,"y":90,"width":140,"height":0,"points":[[0,0],[140,0]],"startBinding":{"elementId":"r1","focus":0,"gap":5},"endBinding":{"elementId":"r2","focus":0,"gap":5},"startArrowhead":null,"endArrowhead":"arrow","strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"roughness":1,"opacity":100,"angle":0,"seed":5,"version":1,"isDeleted":false,"groupIds":[],"boundElements":null,"link":null,"locked":false,"lastCommittedPoint":null}
|
|
575
|
-
]
|
|
576
|
-
\`\`\`
|
|
577
|
-
`.trim();
|
|
423
|
+
function Ve() {
|
|
424
|
+
return '\n## Excalidraw Element Reference\n\nOutput a JSON array of Excalidraw elements. Each element needs these fields:\n\n### Common Fields (all elements)\n- `type`: "rectangle" | "ellipse" | "diamond" | "text" | "arrow" | "line"\n- `id`: unique string (e.g. "rect1", "text1", "arrow1")\n- `x`, `y`: number (top-left origin, x increases right, y increases down)\n- `width`, `height`: number\n- `strokeColor`: hex string (e.g. "#1e1e1e")\n- `backgroundColor`: hex string or "transparent"\n- `fillStyle`: "solid" | "hachure" | "cross-hatch"\n- `strokeWidth`: 1 | 2 | 4\n- `roughness`: 0 (sharp) | 1 (sketchy)\n- `opacity`: 100\n- `angle`: 0\n- `seed`: any integer (e.g. 1)\n- `version`: 1\n- `isDeleted`: false\n- `groupIds`: []\n- `boundElements`: null or array of { id: string, type: "text" | "arrow" }\n- `link`: null\n- `locked`: false\n\n### Text Elements\nAdditional fields: `text`, `fontSize` (16-24), `fontFamily` (1=hand, 2=normal, 3=mono), `textAlign` ("left"|"center"|"right"), `verticalAlign` ("top"|"middle"), `baseline`: 0, `containerId`: null or parent shape id\n\n### Arrow/Line Elements\nAdditional fields: `points` (array of [x,y] relative to element x,y — first point always [0,0]), `startBinding` and `endBinding`: null or { elementId: string, focus: 0, gap: 5 }, `lastCommittedPoint`: null, `startArrowhead`: null, `endArrowhead`: "arrow" | null\n\n### Color Palette\n- Blue: "#1971c2", Light blue bg: "#a5d8ff"\n- Green: "#2f9e44", Light green bg: "#b2f2bb"\n- Red: "#e03131", Light red bg: "#ffc9c9"\n- Orange: "#e8590c", Light orange bg: "#ffd8a8"\n- Purple: "#7048e8", Light purple bg: "#d0bfff"\n- Yellow: "#f08c00", Light yellow bg: "#ffec99"\n- Gray: "#868e96", Light gray bg: "#dee2e6"\n- Dark: "#1e1e1e"\n\n### Layout Tips\n- Space shapes ~200px apart horizontally, ~150px vertically\n- Typical shape size: 160×80 for rectangles, 120×60 for ellipses\n- Center text inside shapes using containerId\n- Use arrows to show relationships (agreement, disagreement, influence)\n\n### Compact Example\n```json\n[\n {"type":"rectangle","id":"r1","x":50,"y":50,"width":160,"height":80,"strokeColor":"#1971c2","backgroundColor":"#a5d8ff","fillStyle":"solid","strokeWidth":2,"roughness":1,"opacity":100,"angle":0,"seed":1,"version":1,"isDeleted":false,"groupIds":[],"boundElements":[{"id":"t1","type":"text"},{"id":"a1","type":"arrow"}],"link":null,"locked":false},\n {"type":"text","id":"t1","x":60,"y":70,"width":140,"height":40,"text":"Counsellor A","fontSize":16,"fontFamily":2,"textAlign":"center","verticalAlign":"middle","baseline":0,"containerId":"r1","strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":1,"roughness":0,"opacity":100,"angle":0,"seed":2,"version":1,"isDeleted":false,"groupIds":[],"boundElements":null,"link":null,"locked":false},\n {"type":"rectangle","id":"r2","x":350,"y":50,"width":160,"height":80,"strokeColor":"#2f9e44","backgroundColor":"#b2f2bb","fillStyle":"solid","strokeWidth":2,"roughness":1,"opacity":100,"angle":0,"seed":3,"version":1,"isDeleted":false,"groupIds":[],"boundElements":[{"id":"t2","type":"text"},{"id":"a1","type":"arrow"}],"link":null,"locked":false},\n {"type":"text","id":"t2","x":360,"y":70,"width":140,"height":40,"text":"Counsellor B","fontSize":16,"fontFamily":2,"textAlign":"center","verticalAlign":"middle","baseline":0,"containerId":"r2","strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":1,"roughness":0,"opacity":100,"angle":0,"seed":4,"version":1,"isDeleted":false,"groupIds":[],"boundElements":null,"link":null,"locked":false},\n {"type":"arrow","id":"a1","x":210,"y":90,"width":140,"height":0,"points":[[0,0],[140,0]],"startBinding":{"elementId":"r1","focus":0,"gap":5},"endBinding":{"elementId":"r2","focus":0,"gap":5},"startArrowhead":null,"endArrowhead":"arrow","strokeColor":"#1e1e1e","backgroundColor":"transparent","fillStyle":"solid","strokeWidth":2,"roughness":1,"opacity":100,"angle":0,"seed":5,"version":1,"isDeleted":false,"groupIds":[],"boundElements":null,"link":null,"locked":false,"lastCommittedPoint":null}\n]\n```\n'.trim();
|
|
578
425
|
}
|
|
579
|
-
const
|
|
580
|
-
const DEFAULT_SYSTEM_PROMPT = `You are the Secretary of a council discussion. Your job is to synthesize a clear, structured summary of the conversation that just took place.
|
|
426
|
+
const F = "---EXCALIDRAW---", Xe = `You are the Secretary of a council discussion. Your job is to synthesize a clear, structured summary of the conversation that just took place.
|
|
581
427
|
|
|
582
428
|
Structure your summary with these sections:
|
|
583
429
|
|
|
@@ -594,538 +440,402 @@ Where did they disagree? What are the key tensions?
|
|
|
594
440
|
What are the most important takeaways? What would you recommend based on the full discussion?
|
|
595
441
|
|
|
596
442
|
Be concise but thorough. Use markdown formatting.`;
|
|
597
|
-
function
|
|
598
|
-
const
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
if (turn.round !== currentRound) {
|
|
606
|
-
currentRound = turn.round;
|
|
607
|
-
lines.push(`--- Round ${currentRound} ---`);
|
|
608
|
-
lines.push("");
|
|
609
|
-
}
|
|
610
|
-
lines.push(`[${turn.counsellorName}]:`);
|
|
611
|
-
lines.push(turn.content);
|
|
612
|
-
lines.push("");
|
|
613
|
-
}
|
|
614
|
-
return lines.join("\n");
|
|
443
|
+
function Qe(n) {
|
|
444
|
+
const r = [];
|
|
445
|
+
r.push(`Topic: ${n.topic}`), r.push(`Counsellors: ${n.counsellors.map((e) => e.name).join(", ")}`), r.push(`Rounds: ${n.rounds}`), r.push("");
|
|
446
|
+
let t = 0;
|
|
447
|
+
for (const e of n.turns)
|
|
448
|
+
e.round !== t && (t = e.round, r.push(`--- Round ${t} ---`), r.push("")), r.push(`[${e.counsellorName}]:`), r.push(e.content), r.push("");
|
|
449
|
+
return r.join(`
|
|
450
|
+
`);
|
|
615
451
|
}
|
|
616
|
-
async function
|
|
617
|
-
result,
|
|
618
|
-
config,
|
|
619
|
-
onChunk,
|
|
620
|
-
signal
|
|
452
|
+
async function Ze({
|
|
453
|
+
result: n,
|
|
454
|
+
config: r,
|
|
455
|
+
onChunk: t,
|
|
456
|
+
signal: e
|
|
621
457
|
}) {
|
|
622
|
-
const
|
|
623
|
-
if (!
|
|
458
|
+
const o = r.secretary;
|
|
459
|
+
if (!o?.backend)
|
|
624
460
|
throw new Error("No secretary backend configured");
|
|
625
|
-
}
|
|
626
|
-
const backend = await getBackend(secretaryConfig.backend);
|
|
627
|
-
const model = secretaryConfig.model ?? backend.defaultModel;
|
|
628
|
-
const basePrompt = secretaryConfig.systemPrompt || DEFAULT_SYSTEM_PROMPT;
|
|
629
|
-
const cheatsheet = getExcalidrawCheatsheet();
|
|
630
|
-
const systemPrompt = `${basePrompt}
|
|
461
|
+
const s = await G(o.backend), a = o.model ?? s.defaultModel, i = o.systemPrompt || Xe, l = Ve(), c = `${i}
|
|
631
462
|
|
|
632
|
-
${
|
|
463
|
+
${l}
|
|
633
464
|
|
|
634
|
-
After your text summary, output \`${
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
model,
|
|
638
|
-
systemPrompt,
|
|
465
|
+
After your text summary, output \`${F}\` on its own line, then a JSON array of Excalidraw elements showing a visual map of where each counsellor stands on the topic. Use shapes for each counsellor with their name, arrows to show relationships (agreement/disagreement), and position them to visually represent the discussion dynamics.`, m = Qe(n), g = {
|
|
466
|
+
model: a,
|
|
467
|
+
systemPrompt: c,
|
|
639
468
|
messages: [{ role: "user", content: `Please summarize this council discussion and create a position diagram:
|
|
640
469
|
|
|
641
|
-
${
|
|
470
|
+
${m}` }],
|
|
642
471
|
temperature: 0.5
|
|
643
472
|
};
|
|
644
|
-
let
|
|
645
|
-
if (
|
|
646
|
-
for await (const
|
|
647
|
-
if (
|
|
648
|
-
|
|
649
|
-
if (chunk.delta && onChunk) {
|
|
650
|
-
onChunk(chunk.delta);
|
|
651
|
-
}
|
|
473
|
+
let f = "";
|
|
474
|
+
if (s.chatStream)
|
|
475
|
+
for await (const E of s.chatStream(g)) {
|
|
476
|
+
if (e?.aborted) break;
|
|
477
|
+
f += E.delta, E.delta && t && t(E.delta);
|
|
652
478
|
}
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
const
|
|
659
|
-
|
|
660
|
-
return { text: fullResponse.trim() };
|
|
661
|
-
}
|
|
662
|
-
const text = fullResponse.slice(0, delimiterIndex).trim();
|
|
663
|
-
const diagramRaw = fullResponse.slice(delimiterIndex + EXCALIDRAW_DELIMITER.length).trim();
|
|
664
|
-
let diagram;
|
|
479
|
+
else
|
|
480
|
+
f = (await s.chat(g)).content, t && t(f);
|
|
481
|
+
const p = f.indexOf(F);
|
|
482
|
+
if (p === -1)
|
|
483
|
+
return { text: f.trim() };
|
|
484
|
+
const d = f.slice(0, p).trim(), h = f.slice(p + F.length).trim();
|
|
485
|
+
let b;
|
|
665
486
|
try {
|
|
666
|
-
const
|
|
667
|
-
|
|
668
|
-
diagram = JSON.parse(jsonMatch[0]);
|
|
669
|
-
}
|
|
487
|
+
const E = h.match(/\[[\s\S]*\]/);
|
|
488
|
+
E && (b = JSON.parse(E[0]));
|
|
670
489
|
} catch {
|
|
671
490
|
}
|
|
672
|
-
return { text, diagram };
|
|
491
|
+
return { text: d, diagram: b };
|
|
673
492
|
}
|
|
674
|
-
const
|
|
675
|
-
async function
|
|
676
|
-
result,
|
|
677
|
-
roundNumber,
|
|
678
|
-
config,
|
|
679
|
-
onChunk,
|
|
680
|
-
signal
|
|
493
|
+
const qe = "You are the Secretary of a council debate. Briefly summarize this round of discussion. Note emerging agreements, disagreements, and shifts in position. 2-3 paragraphs max. Use markdown formatting.";
|
|
494
|
+
async function et({
|
|
495
|
+
result: n,
|
|
496
|
+
roundNumber: r,
|
|
497
|
+
config: t,
|
|
498
|
+
onChunk: e,
|
|
499
|
+
signal: o
|
|
681
500
|
}) {
|
|
682
|
-
const
|
|
683
|
-
if (!
|
|
501
|
+
const s = t.secretary;
|
|
502
|
+
if (!s?.backend)
|
|
684
503
|
throw new Error("No secretary backend configured");
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
const
|
|
688
|
-
|
|
689
|
-
const
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
lines.push("");
|
|
693
|
-
for (const turn of roundTurns) {
|
|
694
|
-
lines.push(`[${turn.counsellorName}]:`);
|
|
695
|
-
lines.push(turn.content);
|
|
696
|
-
lines.push("");
|
|
697
|
-
}
|
|
698
|
-
const chatRequest = {
|
|
699
|
-
model,
|
|
700
|
-
systemPrompt: INTERIM_SYSTEM_PROMPT,
|
|
504
|
+
const a = await G(s.backend), i = s.model ?? a.defaultModel, l = n.turns.filter((f) => f.round === r), c = [];
|
|
505
|
+
c.push(`Topic: ${n.topic}`), c.push(`Round ${r}${r === 1 ? " (Constructive)" : " (Rebuttal)"}`), c.push("");
|
|
506
|
+
for (const f of l)
|
|
507
|
+
c.push(`[${f.counsellorName}]:`), c.push(f.content), c.push("");
|
|
508
|
+
const m = {
|
|
509
|
+
model: i,
|
|
510
|
+
systemPrompt: qe,
|
|
701
511
|
messages: [{ role: "user", content: `Please summarize this round:
|
|
702
512
|
|
|
703
|
-
${
|
|
513
|
+
${c.join(`
|
|
514
|
+
`)}` }],
|
|
704
515
|
temperature: 0.5
|
|
705
516
|
};
|
|
706
|
-
let
|
|
707
|
-
if (
|
|
708
|
-
for await (const
|
|
709
|
-
if (
|
|
710
|
-
|
|
711
|
-
if (chunk.delta && onChunk) {
|
|
712
|
-
onChunk(chunk.delta);
|
|
713
|
-
}
|
|
517
|
+
let g = "";
|
|
518
|
+
if (a.chatStream)
|
|
519
|
+
for await (const f of a.chatStream(m)) {
|
|
520
|
+
if (o?.aborted) break;
|
|
521
|
+
g += f.delta, f.delta && e && e(f.delta);
|
|
714
522
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
if (onChunk) onChunk(fullResponse);
|
|
719
|
-
}
|
|
720
|
-
return fullResponse.trim();
|
|
523
|
+
else
|
|
524
|
+
g = (await a.chat(m)).content, e && e(g);
|
|
525
|
+
return g.trim();
|
|
721
526
|
}
|
|
722
|
-
async function
|
|
723
|
-
topic,
|
|
724
|
-
firstRoundTurns,
|
|
725
|
-
config
|
|
527
|
+
async function tt({
|
|
528
|
+
topic: n,
|
|
529
|
+
firstRoundTurns: r,
|
|
530
|
+
config: t
|
|
726
531
|
}) {
|
|
727
|
-
const
|
|
728
|
-
if (!
|
|
532
|
+
const e = t.secretary;
|
|
533
|
+
if (!e?.backend)
|
|
729
534
|
throw new Error("No secretary backend configured");
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
model,
|
|
535
|
+
const o = await G(e.backend), s = e.model ?? o.defaultModel, a = r.map((l) => `[${l.counsellorName}]: ${l.content.slice(0, 300)}`).join(`
|
|
536
|
+
|
|
537
|
+
`);
|
|
538
|
+
return (await o.chat({
|
|
539
|
+
model: s,
|
|
736
540
|
systemPrompt: "Generate a concise title (max 8 words) for this council discussion. Return only the title, no quotes or punctuation at the end.",
|
|
737
541
|
messages: [
|
|
738
542
|
{
|
|
739
543
|
role: "user",
|
|
740
|
-
content: `Topic: ${
|
|
544
|
+
content: `Topic: ${n}
|
|
741
545
|
|
|
742
546
|
First round:
|
|
743
|
-
${
|
|
547
|
+
${a}`
|
|
744
548
|
}
|
|
745
549
|
],
|
|
746
550
|
temperature: 0.3
|
|
747
|
-
});
|
|
748
|
-
return response.content.trim().replace(/^["']+|["']+$/g, "").replace(/[.!?]+$/, "").trim();
|
|
551
|
+
})).content.trim().replace(/^["']+|["']+$/g, "").replace(/[.!?]+$/, "").trim();
|
|
749
552
|
}
|
|
750
|
-
const
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
if (ensured) return;
|
|
755
|
-
await mkdir(LOG_DIR, { recursive: true });
|
|
756
|
-
ensured = true;
|
|
553
|
+
const he = u(w(), ".ai-council"), nt = u(he, "council.log");
|
|
554
|
+
let re = !1;
|
|
555
|
+
async function ot() {
|
|
556
|
+
re || (await N(he, { recursive: !0 }), re = !0);
|
|
757
557
|
}
|
|
758
|
-
function
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
558
|
+
function st(n, r, t, e) {
|
|
559
|
+
let s = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${n} [${r}] ${t}`;
|
|
560
|
+
if (e !== void 0) {
|
|
561
|
+
const a = e instanceof Error ? `${e.message}
|
|
562
|
+
${e.stack ?? ""}` : typeof e == "string" ? e : JSON.stringify(e, null, 2);
|
|
563
|
+
s += `
|
|
564
|
+
${a.replace(/\n/g, `
|
|
565
|
+
`)}`;
|
|
766
566
|
}
|
|
767
|
-
return
|
|
567
|
+
return s + `
|
|
568
|
+
`;
|
|
768
569
|
}
|
|
769
|
-
async function
|
|
570
|
+
async function W(n, r, t, e) {
|
|
770
571
|
try {
|
|
771
|
-
await
|
|
772
|
-
await appendFile(LOG_FILE, formatEntry(level, context, message, extra));
|
|
572
|
+
await ot(), await Oe(nt, st(n, r, t, e));
|
|
773
573
|
} catch {
|
|
774
574
|
}
|
|
775
575
|
}
|
|
776
|
-
const
|
|
777
|
-
info: (
|
|
778
|
-
warn: (
|
|
779
|
-
error: (
|
|
576
|
+
const O = {
|
|
577
|
+
info: (n, r, t) => W("INFO", n, r, t),
|
|
578
|
+
warn: (n, r, t) => W("WARN", n, r, t),
|
|
579
|
+
error: (n, r, t) => W("ERROR", n, r, t)
|
|
780
580
|
};
|
|
781
|
-
function
|
|
782
|
-
const
|
|
783
|
-
for (const
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
content: `[${turn.counsellorName}, Round ${turn.round}]: ${turn.content}`
|
|
790
|
-
});
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
return messages;
|
|
581
|
+
function rt(n, r, t) {
|
|
582
|
+
const e = [{ role: "user", content: n }];
|
|
583
|
+
for (const o of r)
|
|
584
|
+
o.counsellorId === t ? e.push({ role: "assistant", content: o.content }) : e.push({
|
|
585
|
+
role: "user",
|
|
586
|
+
content: `[${o.counsellorName}, Round ${o.round}]: ${o.content}`
|
|
587
|
+
});
|
|
588
|
+
return e;
|
|
794
589
|
}
|
|
795
|
-
function
|
|
796
|
-
const
|
|
797
|
-
if (
|
|
798
|
-
return
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
}
|
|
805
|
-
|
|
590
|
+
function at(n, r, t, e) {
|
|
591
|
+
const o = [{ role: "user", content: n }];
|
|
592
|
+
if (e === 1)
|
|
593
|
+
return o;
|
|
594
|
+
const s = r.filter((i) => i.round === 1);
|
|
595
|
+
for (const i of s)
|
|
596
|
+
i.counsellorId === t ? o.push({ role: "assistant", content: i.content }) : o.push({
|
|
597
|
+
role: "user",
|
|
598
|
+
content: `[${i.counsellorName}, Constructive]: ${i.content}`
|
|
599
|
+
});
|
|
600
|
+
const a = e - 1;
|
|
601
|
+
if (a > 1) {
|
|
602
|
+
const i = r.filter((l) => l.round === a);
|
|
603
|
+
for (const l of i)
|
|
604
|
+
l.counsellorId === t ? o.push({ role: "assistant", content: l.content }) : o.push({
|
|
806
605
|
role: "user",
|
|
807
|
-
content: `[${
|
|
606
|
+
content: `[${l.counsellorName}, Round ${a}]: ${l.content}`
|
|
808
607
|
});
|
|
809
|
-
}
|
|
810
608
|
}
|
|
811
|
-
const
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
for (const turn of prevTurns) {
|
|
815
|
-
if (turn.counsellorId === currentCounsellorId) {
|
|
816
|
-
messages.push({ role: "assistant", content: turn.content });
|
|
817
|
-
} else {
|
|
818
|
-
messages.push({
|
|
819
|
-
role: "user",
|
|
820
|
-
content: `[${turn.counsellorName}, Round ${prevRound}]: ${turn.content}`
|
|
821
|
-
});
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
for (const turn of turns) {
|
|
826
|
-
if (turn.counsellorId === currentCounsellorId && turn.round !== 1 && turn.round !== prevRound && turn.round < currentRound) {
|
|
827
|
-
messages.push({ role: "assistant", content: turn.content });
|
|
828
|
-
}
|
|
829
|
-
}
|
|
830
|
-
return messages;
|
|
609
|
+
for (const i of r)
|
|
610
|
+
i.counsellorId === t && i.round !== 1 && i.round !== a && i.round < e && o.push({ role: "assistant", content: i.content });
|
|
611
|
+
return o;
|
|
831
612
|
}
|
|
832
|
-
function
|
|
833
|
-
const
|
|
834
|
-
let
|
|
835
|
-
const
|
|
836
|
-
|
|
837
|
-
let
|
|
838
|
-
|
|
839
|
-
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
613
|
+
function it(n, r) {
|
|
614
|
+
const t = [...n];
|
|
615
|
+
let e = r | 0;
|
|
616
|
+
const o = () => {
|
|
617
|
+
e = e + 1831565813 | 0;
|
|
618
|
+
let s = Math.imul(e ^ e >>> 15, 1 | e);
|
|
619
|
+
return s = s + Math.imul(s ^ s >>> 7, 61 | s) ^ s, ((s ^ s >>> 14) >>> 0) / 4294967296;
|
|
840
620
|
};
|
|
841
|
-
for (let
|
|
842
|
-
const
|
|
843
|
-
[
|
|
621
|
+
for (let s = t.length - 1; s > 0; s--) {
|
|
622
|
+
const a = Math.floor(o() * (s + 1));
|
|
623
|
+
[t[s], t[a]] = [t[a], t[s]];
|
|
844
624
|
}
|
|
845
|
-
return
|
|
625
|
+
return t;
|
|
846
626
|
}
|
|
847
|
-
function
|
|
627
|
+
function Y(n, r, t, e, o, s) {
|
|
848
628
|
return {
|
|
849
|
-
topic:
|
|
850
|
-
topicSource:
|
|
851
|
-
counsellors:
|
|
852
|
-
id:
|
|
853
|
-
name:
|
|
854
|
-
description:
|
|
855
|
-
backend:
|
|
856
|
-
model:
|
|
857
|
-
avatarUrl:
|
|
629
|
+
topic: n.topic,
|
|
630
|
+
topicSource: n.topicSource,
|
|
631
|
+
counsellors: n.counsellors.map((a) => ({
|
|
632
|
+
id: a.id,
|
|
633
|
+
name: a.frontmatter.name,
|
|
634
|
+
description: a.frontmatter.description,
|
|
635
|
+
backend: a.frontmatter.backend,
|
|
636
|
+
model: a.frontmatter.model ?? "default",
|
|
637
|
+
avatarUrl: a.avatarUrl
|
|
858
638
|
})),
|
|
859
|
-
rounds:
|
|
860
|
-
turns,
|
|
861
|
-
startedAt,
|
|
639
|
+
rounds: n.rounds,
|
|
640
|
+
turns: r,
|
|
641
|
+
startedAt: t,
|
|
862
642
|
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
863
|
-
totalTokenUsage: { input:
|
|
864
|
-
...
|
|
865
|
-
...
|
|
643
|
+
totalTokenUsage: { input: e, output: o },
|
|
644
|
+
...s && Object.keys(s).length > 0 ? { roundSummaries: s } : {},
|
|
645
|
+
...n.mode === "debate" ? { mode: "debate" } : {}
|
|
866
646
|
};
|
|
867
647
|
}
|
|
868
|
-
async function
|
|
869
|
-
let
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
let totalInput = 0;
|
|
884
|
-
let totalOutput = 0;
|
|
885
|
-
const isDebate = opts.mode === "debate";
|
|
886
|
-
const roundSummaries = {};
|
|
887
|
-
log$1.info("conversation", `Starting ${isDebate ? "debate" : "freeform"} — ${opts.counsellors.length} counsellors, ${opts.rounds} rounds`, {
|
|
888
|
-
counsellors: opts.counsellors.map((c) => `${c.frontmatter.name} (${c.frontmatter.backend}/${c.frontmatter.model ?? "default"})`),
|
|
889
|
-
topic: opts.topic.slice(0, 200)
|
|
648
|
+
async function ct(n, r, t, e, o) {
|
|
649
|
+
let s;
|
|
650
|
+
typeof n == "string" ? s = {
|
|
651
|
+
topic: n,
|
|
652
|
+
topicSource: r,
|
|
653
|
+
counsellors: t,
|
|
654
|
+
rounds: e,
|
|
655
|
+
onEvent: o
|
|
656
|
+
} : s = n;
|
|
657
|
+
const a = (/* @__PURE__ */ new Date()).toISOString(), i = [];
|
|
658
|
+
let l = 0, c = 0;
|
|
659
|
+
const m = s.mode === "debate", g = {};
|
|
660
|
+
O.info("conversation", `Starting ${m ? "debate" : "freeform"} — ${s.counsellors.length} counsellors, ${s.rounds} rounds`, {
|
|
661
|
+
counsellors: s.counsellors.map((f) => `${f.frontmatter.name} (${f.frontmatter.backend}/${f.frontmatter.model ?? "default"})`),
|
|
662
|
+
topic: s.topic.slice(0, 200)
|
|
890
663
|
});
|
|
891
|
-
for (let
|
|
892
|
-
const
|
|
893
|
-
for (const
|
|
894
|
-
if (
|
|
895
|
-
return
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
if (injected) {
|
|
900
|
-
turns.push(injected);
|
|
901
|
-
opts.onEvent({ type: "turn_complete", turn: injected });
|
|
902
|
-
}
|
|
664
|
+
for (let f = 1; f <= s.rounds; f++) {
|
|
665
|
+
const p = m && f > 1 ? it(s.counsellors, f) : s.counsellors;
|
|
666
|
+
for (const d of p) {
|
|
667
|
+
if (s.signal?.aborted)
|
|
668
|
+
return Y(s, i, a, l, c, g);
|
|
669
|
+
if (s.beforeTurn) {
|
|
670
|
+
const h = await s.beforeTurn();
|
|
671
|
+
h && (i.push(h), s.onEvent({ type: "turn_complete", turn: h }));
|
|
903
672
|
}
|
|
904
|
-
|
|
673
|
+
s.onEvent({ type: "turn_start", round: f, counsellorName: d.frontmatter.name });
|
|
905
674
|
try {
|
|
906
|
-
const
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
systemPrompt: counsellor.systemPrompt,
|
|
912
|
-
messages,
|
|
913
|
-
temperature: counsellor.frontmatter.temperature
|
|
675
|
+
const h = await G(d.frontmatter.backend), b = d.frontmatter.model ?? h.defaultModel, E = m ? at(s.topic, i, d.id, f) : rt(s.topic, i, d.id), Q = {
|
|
676
|
+
model: b,
|
|
677
|
+
systemPrompt: d.systemPrompt,
|
|
678
|
+
messages: E,
|
|
679
|
+
temperature: d.frontmatter.temperature
|
|
914
680
|
};
|
|
915
|
-
let
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
content += chunk.delta;
|
|
922
|
-
if (chunk.delta) {
|
|
923
|
-
opts.onEvent({ type: "turn_chunk", counsellorName: counsellor.frontmatter.name, delta: chunk.delta });
|
|
924
|
-
}
|
|
925
|
-
if (chunk.tokenUsage) {
|
|
926
|
-
tokenUsage = chunk.tokenUsage;
|
|
927
|
-
}
|
|
681
|
+
let L, x;
|
|
682
|
+
if (h.chatStream) {
|
|
683
|
+
L = "";
|
|
684
|
+
for await (const S of h.chatStream(Q)) {
|
|
685
|
+
if (s.signal?.aborted) break;
|
|
686
|
+
L += S.delta, S.delta && s.onEvent({ type: "turn_chunk", counsellorName: d.frontmatter.name, delta: S.delta }), S.tokenUsage && (x = S.tokenUsage);
|
|
928
687
|
}
|
|
929
688
|
} else {
|
|
930
|
-
const
|
|
931
|
-
content =
|
|
932
|
-
tokenUsage = response.tokenUsage;
|
|
689
|
+
const S = await h.chat(Q);
|
|
690
|
+
L = S.content, x = S.tokenUsage;
|
|
933
691
|
}
|
|
934
|
-
const
|
|
935
|
-
round,
|
|
936
|
-
counsellorId:
|
|
937
|
-
counsellorName:
|
|
938
|
-
content,
|
|
692
|
+
const Z = {
|
|
693
|
+
round: f,
|
|
694
|
+
counsellorId: d.id,
|
|
695
|
+
counsellorName: d.frontmatter.name,
|
|
696
|
+
content: L,
|
|
939
697
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
940
|
-
model,
|
|
941
|
-
backend:
|
|
942
|
-
tokenUsage,
|
|
943
|
-
avatarUrl:
|
|
698
|
+
model: b,
|
|
699
|
+
backend: d.frontmatter.backend,
|
|
700
|
+
tokenUsage: x,
|
|
701
|
+
avatarUrl: d.avatarUrl
|
|
944
702
|
};
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
}
|
|
949
|
-
turns.push(turn);
|
|
950
|
-
opts.onEvent({ type: "turn_complete", turn });
|
|
951
|
-
} catch (err) {
|
|
952
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
953
|
-
log$1.error("conversation", `Turn failed for ${counsellor.frontmatter.name} (round ${round}, model ${counsellor.frontmatter.model ?? "default"}, backend ${counsellor.frontmatter.backend})`, err);
|
|
954
|
-
opts.onEvent({ type: "error", counsellorName: counsellor.frontmatter.name, error: message });
|
|
703
|
+
x && (l += x.input, c += x.output), i.push(Z), s.onEvent({ type: "turn_complete", turn: Z });
|
|
704
|
+
} catch (h) {
|
|
705
|
+
const b = h instanceof Error ? h.message : String(h);
|
|
706
|
+
O.error("conversation", `Turn failed for ${d.frontmatter.name} (round ${f}, model ${d.frontmatter.model ?? "default"}, backend ${d.frontmatter.backend})`, h), s.onEvent({ type: "error", counsellorName: d.frontmatter.name, error: b });
|
|
955
707
|
}
|
|
956
708
|
}
|
|
957
|
-
|
|
958
|
-
if (isDebate && opts.config?.secretary?.backend && !opts.signal?.aborted) {
|
|
709
|
+
if (s.onEvent({ type: "round_complete", round: f }), m && s.config?.secretary?.backend && !s.signal?.aborted)
|
|
959
710
|
try {
|
|
960
|
-
const
|
|
961
|
-
|
|
962
|
-
const
|
|
963
|
-
result:
|
|
964
|
-
roundNumber:
|
|
965
|
-
config:
|
|
966
|
-
onChunk: (
|
|
967
|
-
|
|
711
|
+
const d = Y(s, i, a, l, c, g);
|
|
712
|
+
s.onEvent({ type: "round_summary_start", round: f });
|
|
713
|
+
const h = await et({
|
|
714
|
+
result: d,
|
|
715
|
+
roundNumber: f,
|
|
716
|
+
config: s.config,
|
|
717
|
+
onChunk: (b) => {
|
|
718
|
+
s.onEvent({ type: "round_summary_chunk", round: f, delta: b });
|
|
968
719
|
},
|
|
969
|
-
signal:
|
|
720
|
+
signal: s.signal
|
|
970
721
|
});
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
log$1.error("conversation", `Interim summary failed for round ${round}`, err);
|
|
722
|
+
g[f] = h, s.onEvent({ type: "round_summary_complete", round: f, summary: h });
|
|
723
|
+
} catch (d) {
|
|
724
|
+
O.error("conversation", `Interim summary failed for round ${f}`, d);
|
|
975
725
|
}
|
|
976
|
-
}
|
|
977
726
|
}
|
|
978
|
-
return
|
|
727
|
+
return Y(s, i, a, l, c, g);
|
|
979
728
|
}
|
|
980
|
-
const
|
|
981
|
-
function
|
|
982
|
-
return
|
|
729
|
+
const I = u(w(), ".ai-council", "history");
|
|
730
|
+
function lt(n) {
|
|
731
|
+
return n.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
983
732
|
}
|
|
984
|
-
async function
|
|
985
|
-
await
|
|
986
|
-
const
|
|
987
|
-
|
|
988
|
-
const id = `${timestamp}-${slug}`;
|
|
989
|
-
const filePath = join(HISTORY_DIR, `${id}.json`);
|
|
990
|
-
await writeFile(filePath, JSON.stringify(result, null, 2), "utf-8");
|
|
991
|
-
return id;
|
|
733
|
+
async function ut(n) {
|
|
734
|
+
await N(I, { recursive: !0 });
|
|
735
|
+
const r = new Date(n.startedAt).toISOString().replace(/[:.]/g, "-").slice(0, 19), t = lt(n.topic), e = `${r}-${t}`, o = u(I, `${e}.json`);
|
|
736
|
+
return await $(o, JSON.stringify(n, null, 2), "utf-8"), e;
|
|
992
737
|
}
|
|
993
|
-
async function
|
|
994
|
-
await
|
|
995
|
-
const
|
|
996
|
-
const
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
return entries.sort((a, b) => b.startedAt.localeCompare(a.startedAt));
|
|
738
|
+
async function dt() {
|
|
739
|
+
await N(I, { recursive: !0 });
|
|
740
|
+
const n = await H(I), r = [];
|
|
741
|
+
for (const t of n)
|
|
742
|
+
if (t.endsWith(".json"))
|
|
743
|
+
try {
|
|
744
|
+
const e = await y(u(I, t), "utf-8"), o = JSON.parse(e);
|
|
745
|
+
r.push({
|
|
746
|
+
id: C(t, ".json"),
|
|
747
|
+
topic: o.topic,
|
|
748
|
+
title: o.title,
|
|
749
|
+
counsellors: o.counsellors.map((s) => s.name),
|
|
750
|
+
rounds: o.rounds,
|
|
751
|
+
startedAt: o.startedAt,
|
|
752
|
+
completedAt: o.completedAt
|
|
753
|
+
});
|
|
754
|
+
} catch {
|
|
755
|
+
}
|
|
756
|
+
return r.sort((t, e) => e.startedAt.localeCompare(t.startedAt));
|
|
1015
757
|
}
|
|
1016
|
-
async function
|
|
1017
|
-
const
|
|
1018
|
-
|
|
1019
|
-
const result = JSON.parse(raw);
|
|
1020
|
-
if (result.infographic && !result.infographics) {
|
|
1021
|
-
result.infographics = [result.infographic];
|
|
1022
|
-
delete result.infographic;
|
|
1023
|
-
}
|
|
1024
|
-
return result;
|
|
758
|
+
async function ae(n) {
|
|
759
|
+
const r = u(I, `${n}.json`), t = await y(r, "utf-8"), e = JSON.parse(t);
|
|
760
|
+
return e.infographic && !e.infographics && (e.infographics = [e.infographic], delete e.infographic), e;
|
|
1025
761
|
}
|
|
1026
|
-
async function
|
|
1027
|
-
const
|
|
1028
|
-
await
|
|
762
|
+
async function mt(n) {
|
|
763
|
+
const r = u(I, `${n}.json`);
|
|
764
|
+
await B(r);
|
|
1029
765
|
}
|
|
1030
|
-
async function
|
|
1031
|
-
const
|
|
1032
|
-
|
|
1033
|
-
const result = JSON.parse(raw);
|
|
1034
|
-
if (!result.infographics) result.infographics = [];
|
|
1035
|
-
result.infographics.push(infographic);
|
|
1036
|
-
await writeFile(filePath, JSON.stringify(result, null, 2), "utf-8");
|
|
766
|
+
async function ft(n, r) {
|
|
767
|
+
const t = u(I, `${n}.json`), e = await y(t, "utf-8"), o = JSON.parse(e);
|
|
768
|
+
o.infographics || (o.infographics = []), o.infographics.push(r), await $(t, JSON.stringify(o, null, 2), "utf-8");
|
|
1037
769
|
}
|
|
1038
|
-
async function
|
|
1039
|
-
const
|
|
1040
|
-
|
|
1041
|
-
const result = JSON.parse(raw);
|
|
1042
|
-
if (result.infographics && index >= 0 && index < result.infographics.length) {
|
|
1043
|
-
result.infographics.splice(index, 1);
|
|
1044
|
-
}
|
|
1045
|
-
await writeFile(filePath, JSON.stringify(result, null, 2), "utf-8");
|
|
770
|
+
async function pt(n, r) {
|
|
771
|
+
const t = u(I, `${n}.json`), e = await y(t, "utf-8"), o = JSON.parse(e);
|
|
772
|
+
o.infographics && r >= 0 && r < o.infographics.length && o.infographics.splice(r, 1), await $(t, JSON.stringify(o, null, 2), "utf-8");
|
|
1046
773
|
}
|
|
1047
|
-
const
|
|
1048
|
-
function
|
|
1049
|
-
const
|
|
1050
|
-
|
|
774
|
+
const ge = xe(import.meta.url);
|
|
775
|
+
function ht(n) {
|
|
776
|
+
const r = n.counsellors.map((e) => e.name).join(", "), t = n.summary ?? n.turns.map((e) => `${e.counsellorName}: ${e.content.slice(0, 200)}`).join(`
|
|
777
|
+
`);
|
|
1051
778
|
return [
|
|
1052
779
|
"Create a professional infographic summarizing a panel discussion.",
|
|
1053
|
-
`Topic: ${
|
|
1054
|
-
`Key points: ${
|
|
1055
|
-
`Panelists: ${
|
|
780
|
+
`Topic: ${n.topic.slice(0, 300)}`,
|
|
781
|
+
`Key points: ${t.slice(0, 1500)}`,
|
|
782
|
+
`Panelists: ${r}`,
|
|
1056
783
|
"Use a clean, modern design with sections for convergence points, divergence points, and key takeaways.",
|
|
1057
784
|
"Include relevant icons and visual hierarchy. Use a horizontal landscape layout."
|
|
1058
785
|
].join(" ");
|
|
1059
786
|
}
|
|
1060
|
-
function
|
|
1061
|
-
if (
|
|
1062
|
-
const
|
|
1063
|
-
|
|
1064
|
-
if (hasGoogle) return "google";
|
|
1065
|
-
if (hasOpenai) return "openai";
|
|
1066
|
-
return null;
|
|
787
|
+
function gt(n) {
|
|
788
|
+
if (n.infographic?.backend) return n.infographic.backend;
|
|
789
|
+
const r = !!(n.backends.google?.apiKey || process.env.GOOGLE_API_KEY), t = !!(n.backends.openai?.apiKey || process.env.OPENAI_API_KEY);
|
|
790
|
+
return r ? "google" : t ? "openai" : null;
|
|
1067
791
|
}
|
|
1068
|
-
async function
|
|
1069
|
-
const
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
});
|
|
1074
|
-
const response = await client.images.generate({
|
|
792
|
+
async function yt(n, r) {
|
|
793
|
+
const t = ge("openai").default, s = (await new t({
|
|
794
|
+
apiKey: r.backends.openai?.apiKey || process.env.OPENAI_API_KEY,
|
|
795
|
+
...r.backends.openai?.baseUrl ? { baseURL: r.backends.openai.baseUrl } : {}
|
|
796
|
+
}).images.generate({
|
|
1075
797
|
model: "gpt-image-1",
|
|
1076
|
-
prompt,
|
|
798
|
+
prompt: n,
|
|
1077
799
|
quality: "high",
|
|
1078
800
|
size: "1536x1024"
|
|
1079
|
-
});
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
return b64;
|
|
801
|
+
})).data?.[0]?.b64_json;
|
|
802
|
+
if (!s) throw new Error("No image data returned from OpenAI");
|
|
803
|
+
return s;
|
|
1083
804
|
}
|
|
1084
|
-
async function
|
|
1085
|
-
const { GoogleGenAI } =
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
const ai = new GoogleGenAI({ apiKey });
|
|
1089
|
-
const response = await ai.models.generateContent({
|
|
805
|
+
async function wt(n, r) {
|
|
806
|
+
const { GoogleGenAI: t } = ge("@google/genai"), e = r.backends.google?.apiKey || process.env.GOOGLE_API_KEY;
|
|
807
|
+
if (!e) throw new Error("No Google API key configured");
|
|
808
|
+
const a = (await new t({ apiKey: e }).models.generateContent({
|
|
1090
809
|
model: "gemini-3-pro-image-preview",
|
|
1091
|
-
contents:
|
|
810
|
+
contents: n,
|
|
1092
811
|
config: {
|
|
1093
812
|
responseModalities: ["IMAGE", "TEXT"]
|
|
1094
813
|
}
|
|
1095
|
-
});
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
return part.inlineData.data;
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
814
|
+
})).candidates?.[0]?.content?.parts;
|
|
815
|
+
if (!a) throw new Error("No response parts from Gemini");
|
|
816
|
+
for (const i of a)
|
|
817
|
+
if (i.inlineData?.data)
|
|
818
|
+
return i.inlineData.data;
|
|
1103
819
|
throw new Error("No image data in Gemini response");
|
|
1104
820
|
}
|
|
1105
|
-
async function
|
|
1106
|
-
const
|
|
1107
|
-
if (!
|
|
1108
|
-
const
|
|
1109
|
-
|
|
1110
|
-
return generateViaOpenAI(prompt, config);
|
|
1111
|
-
} else {
|
|
1112
|
-
return generateViaGoogle(prompt, config);
|
|
1113
|
-
}
|
|
821
|
+
async function ie(n, r, t) {
|
|
822
|
+
const e = t ?? gt(r);
|
|
823
|
+
if (!e) throw new Error("No image-capable backend configured (need OpenAI or Google API key)");
|
|
824
|
+
const o = ht(n);
|
|
825
|
+
return e === "openai" ? yt(o, r) : wt(o, r);
|
|
1114
826
|
}
|
|
1115
|
-
const
|
|
827
|
+
const ye = {
|
|
1116
828
|
anthropic: "https://api.anthropic.com",
|
|
1117
829
|
openai: "https://api.openai.com/v1",
|
|
1118
830
|
google: "https://generativelanguage.googleapis.com",
|
|
1119
831
|
ollama: "http://localhost:11434"
|
|
1120
|
-
}
|
|
1121
|
-
const KNOWN_ANTHROPIC_MODELS = [
|
|
832
|
+
}, ce = [
|
|
1122
833
|
"claude-opus-4-20250514",
|
|
1123
834
|
"claude-sonnet-4-5-20250514",
|
|
1124
835
|
"claude-sonnet-4-20250514",
|
|
1125
836
|
"claude-haiku-4-20250414",
|
|
1126
837
|
"claude-3-5-haiku-20241022"
|
|
1127
|
-
]
|
|
1128
|
-
const KNOWN_GOOGLE_MODELS = [
|
|
838
|
+
], D = [
|
|
1129
839
|
"gemini-2.5-pro",
|
|
1130
840
|
"gemini-2.5-flash",
|
|
1131
841
|
"gemini-2.0-flash",
|
|
@@ -1133,80 +843,65 @@ const KNOWN_GOOGLE_MODELS = [
|
|
|
1133
843
|
"gemini-1.5-pro",
|
|
1134
844
|
"gemini-1.5-flash"
|
|
1135
845
|
];
|
|
1136
|
-
async function
|
|
846
|
+
async function bt(n, r) {
|
|
1137
847
|
try {
|
|
1138
|
-
switch (
|
|
848
|
+
switch (n) {
|
|
1139
849
|
case "ollama": {
|
|
1140
|
-
const { Ollama:
|
|
1141
|
-
|
|
1142
|
-
const response = await client.list();
|
|
1143
|
-
const models = response.models.map((m) => m.name).sort();
|
|
1144
|
-
return { connected: true, models };
|
|
850
|
+
const { Ollama: t } = await import("ollama");
|
|
851
|
+
return { connected: !0, models: (await new t({ host: r.baseUrl || ye.ollama }).list()).models.map((a) => a.name).sort() };
|
|
1145
852
|
}
|
|
1146
853
|
case "openai": {
|
|
1147
|
-
const { default:
|
|
1148
|
-
|
|
1149
|
-
apiKey:
|
|
1150
|
-
...
|
|
1151
|
-
});
|
|
1152
|
-
const response = await client.models.list();
|
|
1153
|
-
const models = response.data.map((m) => m.id).filter((id) => id.startsWith("gpt-") || id.startsWith("o") || id.startsWith("chatgpt-")).sort();
|
|
1154
|
-
return { connected: true, models };
|
|
854
|
+
const { default: t } = await import("openai");
|
|
855
|
+
return { connected: !0, models: (await new t({
|
|
856
|
+
apiKey: r.apiKey || process.env.OPENAI_API_KEY,
|
|
857
|
+
...r.baseUrl ? { baseURL: r.baseUrl } : {}
|
|
858
|
+
}).models.list()).data.map((a) => a.id).filter((a) => a.startsWith("gpt-") || a.startsWith("o") || a.startsWith("chatgpt-")).sort() };
|
|
1155
859
|
}
|
|
1156
860
|
case "anthropic": {
|
|
1157
|
-
const { default:
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
...config.baseUrl ? { baseURL: config.baseUrl } : {}
|
|
861
|
+
const { default: t } = await import("@anthropic-ai/sdk"), e = new t({
|
|
862
|
+
apiKey: r.apiKey || process.env.ANTHROPIC_API_KEY,
|
|
863
|
+
...r.baseUrl ? { baseURL: r.baseUrl } : {}
|
|
1161
864
|
});
|
|
1162
865
|
try {
|
|
1163
|
-
|
|
1164
|
-
const models = response.data.map((m) => m.id).sort();
|
|
1165
|
-
return { connected: true, models };
|
|
866
|
+
return { connected: !0, models: (await e.models.list({ limit: 100 })).data.map((a) => a.id).sort() };
|
|
1166
867
|
} catch {
|
|
1167
|
-
return { connected:
|
|
868
|
+
return { connected: !0, models: ce };
|
|
1168
869
|
}
|
|
1169
870
|
}
|
|
1170
871
|
case "google": {
|
|
1171
|
-
const
|
|
1172
|
-
if (!
|
|
1173
|
-
const
|
|
1174
|
-
`https://generativelanguage.googleapis.com/v1beta/models?key=${
|
|
872
|
+
const t = r.apiKey || process.env.GOOGLE_API_KEY || "";
|
|
873
|
+
if (!t) return { connected: !1, models: D, error: "No API key" };
|
|
874
|
+
const e = await fetch(
|
|
875
|
+
`https://generativelanguage.googleapis.com/v1beta/models?key=${t}`
|
|
1175
876
|
);
|
|
1176
|
-
if (!
|
|
1177
|
-
const
|
|
1178
|
-
|
|
1179
|
-
return { connected: false, models: KNOWN_GOOGLE_MODELS, error: msg };
|
|
877
|
+
if (!e.ok) {
|
|
878
|
+
const i = (await e.json().catch(() => ({})))?.error?.message || `HTTP ${e.status}`;
|
|
879
|
+
return { connected: !1, models: D, error: i };
|
|
1180
880
|
}
|
|
1181
|
-
const
|
|
1182
|
-
|
|
1183
|
-
return { connected: true, models: models.length > 0 ? models : KNOWN_GOOGLE_MODELS };
|
|
881
|
+
const s = ((await e.json()).models || []).filter((a) => a.name.includes("gemini") && a.supportedGenerationMethods?.includes("generateContent")).map((a) => a.name.replace("models/", "")).sort();
|
|
882
|
+
return { connected: !0, models: s.length > 0 ? s : D };
|
|
1184
883
|
}
|
|
1185
884
|
default:
|
|
1186
|
-
return { connected:
|
|
885
|
+
return { connected: !1, models: [], error: `Unknown backend: ${n}` };
|
|
1187
886
|
}
|
|
1188
|
-
} catch (
|
|
1189
|
-
const
|
|
1190
|
-
|
|
1191
|
-
return { connected: false, models: fallbackModels, error };
|
|
887
|
+
} catch (t) {
|
|
888
|
+
const e = t instanceof Error ? t.message : String(t);
|
|
889
|
+
return { connected: !1, models: n === "anthropic" ? ce : n === "google" ? D : [], error: e };
|
|
1192
890
|
}
|
|
1193
891
|
}
|
|
1194
|
-
let
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
let config = { backends: {} };
|
|
892
|
+
let A = null, K = [];
|
|
893
|
+
function kt(n, r) {
|
|
894
|
+
n.handle("counsellors:list", async (t, e) => {
|
|
895
|
+
const o = u(w(), ".ai-council", "config.json");
|
|
896
|
+
let s = { backends: {} };
|
|
1200
897
|
try {
|
|
1201
|
-
const
|
|
1202
|
-
|
|
898
|
+
const c = await y(o, "utf-8");
|
|
899
|
+
s = JSON.parse(c);
|
|
1203
900
|
} catch {
|
|
1204
901
|
}
|
|
1205
|
-
const
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
return counsellors.map((c) => {
|
|
1209
|
-
const regEntry = registry[c.id];
|
|
902
|
+
const a = oe(s), i = s.counsellors ?? {};
|
|
903
|
+
return (await te(e, a)).map((c) => {
|
|
904
|
+
const m = i[c.id];
|
|
1210
905
|
return {
|
|
1211
906
|
id: c.id,
|
|
1212
907
|
dirPath: c.dirPath,
|
|
@@ -1217,241 +912,148 @@ function registerIpcHandlers(ipcMain2, getWindow) {
|
|
|
1217
912
|
temperature: c.frontmatter.temperature,
|
|
1218
913
|
interests: c.frontmatter.interests,
|
|
1219
914
|
avatarUrl: c.avatarUrl,
|
|
1220
|
-
source:
|
|
1221
|
-
registryUrl:
|
|
915
|
+
source: m?.source,
|
|
916
|
+
registryUrl: m?.url
|
|
1222
917
|
};
|
|
1223
918
|
});
|
|
1224
|
-
})
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
const
|
|
1229
|
-
return
|
|
1230
|
-
})
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
ipcMain2.handle("counsellors:create", async (_event, councilDir, id, aboutMd) => {
|
|
1237
|
-
const dirPath = join(councilDir, id);
|
|
1238
|
-
await mkdir(dirPath, { recursive: true });
|
|
1239
|
-
await writeFile(join(dirPath, "ABOUT.md"), aboutMd, "utf-8");
|
|
1240
|
-
return { success: true, dirPath };
|
|
1241
|
-
});
|
|
1242
|
-
ipcMain2.handle("counsellors:delete", async (_event, dirPath) => {
|
|
1243
|
-
await rm(dirPath, { recursive: true, force: true });
|
|
1244
|
-
return { success: true };
|
|
1245
|
-
});
|
|
1246
|
-
ipcMain2.handle("config:get", async () => {
|
|
1247
|
-
const configPath = join(homedir(), ".ai-council", "config.json");
|
|
1248
|
-
let config = { backends: {} };
|
|
919
|
+
}), n.handle("counsellors:get", async (t, e) => {
|
|
920
|
+
const o = u(e, "ABOUT.md"), s = await y(o, "utf-8"), { data: a, content: i } = z(s);
|
|
921
|
+
return { frontmatter: a, body: i.trim(), raw: s };
|
|
922
|
+
}), n.handle("counsellors:save", async (t, e, o) => {
|
|
923
|
+
const s = u(e, "ABOUT.md");
|
|
924
|
+
return await $(s, o, "utf-8"), { success: !0 };
|
|
925
|
+
}), n.handle("counsellors:create", async (t, e, o, s) => {
|
|
926
|
+
const a = u(e, o);
|
|
927
|
+
return await N(a, { recursive: !0 }), await $(u(a, "ABOUT.md"), s, "utf-8"), { success: !0, dirPath: a };
|
|
928
|
+
}), n.handle("counsellors:delete", async (t, e) => (await B(e, { recursive: !0, force: !0 }), { success: !0 })), n.handle("config:get", async () => {
|
|
929
|
+
const t = u(w(), ".ai-council", "config.json");
|
|
930
|
+
let e = { backends: {} };
|
|
1249
931
|
try {
|
|
1250
|
-
const
|
|
1251
|
-
|
|
932
|
+
const i = await y(t, "utf-8");
|
|
933
|
+
e = JSON.parse(i);
|
|
1252
934
|
} catch {
|
|
1253
935
|
}
|
|
1254
|
-
const
|
|
936
|
+
const o = {
|
|
1255
937
|
ANTHROPIC_API_KEY: !!process.env.ANTHROPIC_API_KEY,
|
|
1256
938
|
OPENAI_API_KEY: !!process.env.OPENAI_API_KEY,
|
|
1257
939
|
GOOGLE_API_KEY: !!process.env.GOOGLE_API_KEY
|
|
940
|
+
}, s = (i) => i ? "..." + i.slice(-4) : void 0, a = {
|
|
941
|
+
ANTHROPIC_API_KEY: s(process.env.ANTHROPIC_API_KEY),
|
|
942
|
+
OPENAI_API_KEY: s(process.env.OPENAI_API_KEY),
|
|
943
|
+
GOOGLE_API_KEY: s(process.env.GOOGLE_API_KEY)
|
|
1258
944
|
};
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
return {
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
});
|
|
1270
|
-
ipcMain2.handle("config:save", async (_event, config) => {
|
|
1271
|
-
const configDir = join(homedir(), ".ai-council");
|
|
1272
|
-
await mkdir(configDir, { recursive: true });
|
|
1273
|
-
await writeFile(join(configDir, "config.json"), JSON.stringify(config, null, 2), "utf-8");
|
|
1274
|
-
clearCaches();
|
|
1275
|
-
return { success: true };
|
|
1276
|
-
});
|
|
1277
|
-
ipcMain2.handle("discussion:start", async (_event, params) => {
|
|
1278
|
-
const win = getWindow();
|
|
1279
|
-
if (!win) return { error: "No window" };
|
|
1280
|
-
if (activeAbortController) {
|
|
1281
|
-
activeAbortController.abort();
|
|
1282
|
-
}
|
|
1283
|
-
activeAbortController = new AbortController();
|
|
1284
|
-
injectionBuffer = [];
|
|
1285
|
-
const configPath = join(homedir(), ".ai-council", "config.json");
|
|
1286
|
-
let config = { backends: {} };
|
|
945
|
+
return { config: e, envStatus: o, envKeySuffix: a, defaultUrls: ye };
|
|
946
|
+
}), n.handle("backend:probe", async (t, e, o) => bt(e, o)), n.handle("config:save", async (t, e) => {
|
|
947
|
+
const o = u(w(), ".ai-council");
|
|
948
|
+
return await N(o, { recursive: !0 }), await $(u(o, "config.json"), JSON.stringify(e, null, 2), "utf-8"), ze(), { success: !0 };
|
|
949
|
+
}), n.handle("discussion:start", async (t, e) => {
|
|
950
|
+
const o = r();
|
|
951
|
+
if (!o) return { error: "No window" };
|
|
952
|
+
A && A.abort(), A = new AbortController(), K = [];
|
|
953
|
+
const s = u(w(), ".ai-council", "config.json");
|
|
954
|
+
let a = { backends: {} };
|
|
1287
955
|
try {
|
|
1288
|
-
const
|
|
1289
|
-
|
|
956
|
+
const p = await y(s, "utf-8");
|
|
957
|
+
a = JSON.parse(p);
|
|
1290
958
|
} catch {
|
|
1291
959
|
}
|
|
1292
|
-
const
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
if (counsellors.length === 0) {
|
|
1296
|
-
win.webContents.send("discussion:event", { type: "error", counsellorName: "", error: "No counsellors found" });
|
|
960
|
+
const i = oe(a), l = await te(e.councilDir, i), c = e.counsellorIds?.length ? l.filter((p) => e.counsellorIds.includes(p.id)) : l;
|
|
961
|
+
if (c.length === 0) {
|
|
962
|
+
o.webContents.send("discussion:event", { type: "error", counsellorName: "", error: "No counsellors found" });
|
|
1297
963
|
return;
|
|
1298
964
|
}
|
|
1299
|
-
const
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
topicSource: params.topicSource,
|
|
1320
|
-
counsellors,
|
|
1321
|
-
rounds: params.rounds,
|
|
1322
|
-
onEvent: send,
|
|
1323
|
-
beforeTurn,
|
|
1324
|
-
signal: activeAbortController.signal,
|
|
1325
|
-
mode: params.mode,
|
|
1326
|
-
config
|
|
965
|
+
const m = (p) => {
|
|
966
|
+
o.isDestroyed() || o.webContents.send("discussion:event", p);
|
|
967
|
+
}, g = async () => K.length === 0 ? null : {
|
|
968
|
+
round: 0,
|
|
969
|
+
counsellorId: "__user__",
|
|
970
|
+
counsellorName: "You",
|
|
971
|
+
content: K.shift(),
|
|
972
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
973
|
+
model: "human",
|
|
974
|
+
backend: "human"
|
|
975
|
+
}, f = {
|
|
976
|
+
topic: e.topic,
|
|
977
|
+
topicSource: e.topicSource,
|
|
978
|
+
counsellors: c,
|
|
979
|
+
rounds: e.rounds,
|
|
980
|
+
onEvent: m,
|
|
981
|
+
beforeTurn: g,
|
|
982
|
+
signal: A.signal,
|
|
983
|
+
mode: e.mode,
|
|
984
|
+
config: a
|
|
1327
985
|
};
|
|
1328
986
|
try {
|
|
1329
|
-
const
|
|
1330
|
-
if (
|
|
987
|
+
const p = await ct(f);
|
|
988
|
+
if (a.secretary?.backend)
|
|
1331
989
|
try {
|
|
1332
|
-
|
|
1333
|
-
const
|
|
1334
|
-
result,
|
|
1335
|
-
config,
|
|
1336
|
-
onChunk: (
|
|
1337
|
-
|
|
990
|
+
m({ type: "summary_start" });
|
|
991
|
+
const d = await Ze({
|
|
992
|
+
result: p,
|
|
993
|
+
config: a,
|
|
994
|
+
onChunk: (h) => {
|
|
995
|
+
m({ type: "summary_chunk", delta: h });
|
|
1338
996
|
},
|
|
1339
|
-
signal:
|
|
997
|
+
signal: A?.signal
|
|
1340
998
|
});
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
}
|
|
1345
|
-
send({ type: "summary_complete", summary: secretaryResult.text, diagram: secretaryResult.diagram });
|
|
1346
|
-
} catch (err) {
|
|
1347
|
-
log$1.error("ipc:discussion", "Secretary summary failed", err);
|
|
1348
|
-
send({ type: "error", counsellorName: "Secretary", error: err instanceof Error ? err.message : String(err) });
|
|
999
|
+
p.summary = d.text, d.diagram && (p.diagram = d.diagram), m({ type: "summary_complete", summary: d.text, diagram: d.diagram });
|
|
1000
|
+
} catch (d) {
|
|
1001
|
+
O.error("ipc:discussion", "Secretary summary failed", d), m({ type: "error", counsellorName: "Secretary", error: d instanceof Error ? d.message : String(d) });
|
|
1349
1002
|
}
|
|
1350
|
-
|
|
1351
|
-
if (config.secretary?.backend) {
|
|
1003
|
+
if (a.secretary?.backend)
|
|
1352
1004
|
try {
|
|
1353
|
-
const
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
config
|
|
1005
|
+
const d = p.turns.filter((b) => b.round === 1), h = await tt({
|
|
1006
|
+
topic: p.topic,
|
|
1007
|
+
firstRoundTurns: d,
|
|
1008
|
+
config: a
|
|
1358
1009
|
});
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
log$1.error("ipc:discussion", "Title generation failed", err);
|
|
1010
|
+
p.title = h, m({ type: "title_generated", title: h });
|
|
1011
|
+
} catch (d) {
|
|
1012
|
+
O.error("ipc:discussion", "Title generation failed", d);
|
|
1363
1013
|
}
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
for (const backend of params.infographicBackends) {
|
|
1014
|
+
if (e.infographicBackends?.length)
|
|
1015
|
+
for (const d of e.infographicBackends)
|
|
1367
1016
|
try {
|
|
1368
|
-
|
|
1369
|
-
const
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
} catch (err) {
|
|
1374
|
-
log$1.error("ipc:discussion", `Infographic generation failed (${backend})`, err);
|
|
1375
|
-
send({ type: "infographic_error", error: err instanceof Error ? err.message : String(err) });
|
|
1017
|
+
m({ type: "infographic_start" });
|
|
1018
|
+
const h = await ie(p, a, d);
|
|
1019
|
+
p.infographics || (p.infographics = []), p.infographics.push(h), m({ type: "infographic_complete", infographic: h });
|
|
1020
|
+
} catch (h) {
|
|
1021
|
+
O.error("ipc:discussion", `Infographic generation failed (${d})`, h), m({ type: "infographic_error", error: h instanceof Error ? h.message : String(h) });
|
|
1376
1022
|
}
|
|
1377
|
-
|
|
1378
|
-
}
|
|
1379
|
-
send({ type: "complete", result });
|
|
1023
|
+
m({ type: "complete", result: p });
|
|
1380
1024
|
try {
|
|
1381
|
-
await
|
|
1382
|
-
} catch (
|
|
1383
|
-
|
|
1025
|
+
await ut(p);
|
|
1026
|
+
} catch (d) {
|
|
1027
|
+
O.error("ipc:discussion", "Failed to save to history", d);
|
|
1384
1028
|
}
|
|
1385
|
-
} catch (
|
|
1386
|
-
|
|
1387
|
-
send({ type: "error", counsellorName: "", error: err instanceof Error ? err.message : String(err) });
|
|
1029
|
+
} catch (p) {
|
|
1030
|
+
O.error("ipc:discussion", "Discussion failed", p), m({ type: "error", counsellorName: "", error: p instanceof Error ? p.message : String(p) });
|
|
1388
1031
|
} finally {
|
|
1389
|
-
|
|
1390
|
-
}
|
|
1391
|
-
});
|
|
1392
|
-
ipcMain2.handle("discussion:stop", async () => {
|
|
1393
|
-
if (activeAbortController) {
|
|
1394
|
-
activeAbortController.abort();
|
|
1395
|
-
activeAbortController = null;
|
|
1032
|
+
A = null;
|
|
1396
1033
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
ipcMain2.handle("registry:add-local", async (_event, dirPath) => {
|
|
1404
|
-
return addLocalCounsellor(dirPath);
|
|
1405
|
-
});
|
|
1406
|
-
ipcMain2.handle("registry:add-remote", async (_event, url) => {
|
|
1407
|
-
return addRemoteCounsellor(url);
|
|
1408
|
-
});
|
|
1409
|
-
ipcMain2.handle("registry:remove", async (_event, id, deleteFiles) => {
|
|
1410
|
-
await removeCounsellor(id, deleteFiles);
|
|
1411
|
-
return { success: true };
|
|
1412
|
-
});
|
|
1413
|
-
ipcMain2.handle("shell:open-in-finder", async (_event, dirPath) => {
|
|
1414
|
-
shell.showItemInFolder(join(dirPath, "ABOUT.md"));
|
|
1415
|
-
});
|
|
1416
|
-
ipcMain2.handle("shell:open-in-terminal", async (_event, dirPath) => {
|
|
1417
|
-
const execFileAsync2 = promisify(execFile);
|
|
1418
|
-
await execFileAsync2("open", ["-a", "Terminal", dirPath]);
|
|
1419
|
-
});
|
|
1420
|
-
ipcMain2.handle("shell:open-in-editor", async (_event, dirPath) => {
|
|
1421
|
-
const execFileAsync2 = promisify(execFile);
|
|
1034
|
+
}), n.handle("discussion:stop", async () => (A && (A.abort(), A = null), { success: !0 })), n.handle("discussion:inject", async (t, e) => (K.push(e), { success: !0 })), n.handle("registry:add-local", async (t, e) => Be(e)), n.handle("registry:add-remote", async (t, e) => Ge(e)), n.handle("registry:remove", async (t, e, o) => (await Fe(e, o), { success: !0 })), n.handle("shell:open-in-finder", async (t, e) => {
|
|
1035
|
+
q.showItemInFolder(u(e, "ABOUT.md"));
|
|
1036
|
+
}), n.handle("shell:open-in-terminal", async (t, e) => {
|
|
1037
|
+
await R(U)("open", ["-a", "Terminal", e]);
|
|
1038
|
+
}), n.handle("shell:open-in-editor", async (t, e) => {
|
|
1039
|
+
const o = R(U);
|
|
1422
1040
|
try {
|
|
1423
|
-
await
|
|
1041
|
+
await o("code", [e]);
|
|
1424
1042
|
} catch {
|
|
1425
|
-
|
|
1043
|
+
q.openPath(e);
|
|
1426
1044
|
}
|
|
1427
|
-
})
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
ipcMain2.handle("history:delete", async (_event, id) => {
|
|
1431
|
-
await deleteHistoryEntry(id);
|
|
1432
|
-
return { success: true };
|
|
1433
|
-
});
|
|
1434
|
-
ipcMain2.handle("infographic:generate", async (_event, historyId, backend) => {
|
|
1435
|
-
const configPath = join(homedir(), ".ai-council", "config.json");
|
|
1436
|
-
let config = { backends: {} };
|
|
1045
|
+
}), n.handle("history:list", async () => dt()), n.handle("history:get", async (t, e) => ae(e)), n.handle("history:delete", async (t, e) => (await mt(e), { success: !0 })), n.handle("infographic:generate", async (t, e, o) => {
|
|
1046
|
+
const s = u(w(), ".ai-council", "config.json");
|
|
1047
|
+
let a = { backends: {} };
|
|
1437
1048
|
try {
|
|
1438
|
-
const
|
|
1439
|
-
|
|
1049
|
+
const c = await y(s, "utf-8");
|
|
1050
|
+
a = JSON.parse(c);
|
|
1440
1051
|
} catch {
|
|
1441
1052
|
}
|
|
1442
|
-
const
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
});
|
|
1447
|
-
ipcMain2.handle("infographic:delete", async (_event, historyId, index) => {
|
|
1448
|
-
await deleteInfographicFromHistory(historyId, index);
|
|
1449
|
-
return { success: true };
|
|
1450
|
-
});
|
|
1451
|
-
ipcMain2.handle("file:read-as-text", async (_event, filePath) => {
|
|
1452
|
-
const name = basename(filePath);
|
|
1453
|
-
const ext = name.includes(".") ? "." + name.split(".").pop().toLowerCase() : "";
|
|
1454
|
-
const textExtensions = /* @__PURE__ */ new Set([
|
|
1053
|
+
const i = await ae(e), l = await ie(i, a, o);
|
|
1054
|
+
return await ft(e, l), { infographic: l };
|
|
1055
|
+
}), n.handle("infographic:delete", async (t, e, o) => (await pt(e, o), { success: !0 })), n.handle("file:read-as-text", async (t, e) => {
|
|
1056
|
+
const o = C(e), s = o.includes(".") ? "." + o.split(".").pop().toLowerCase() : "", a = /* @__PURE__ */ new Set([
|
|
1455
1057
|
".txt",
|
|
1456
1058
|
".md",
|
|
1457
1059
|
".csv",
|
|
@@ -1488,8 +1090,7 @@ function registerIpcHandlers(ipcMain2, getWindow) {
|
|
|
1488
1090
|
".conf",
|
|
1489
1091
|
".log",
|
|
1490
1092
|
".svg"
|
|
1491
|
-
])
|
|
1492
|
-
const markitdownExtensions = /* @__PURE__ */ new Set([
|
|
1093
|
+
]), i = /* @__PURE__ */ new Set([
|
|
1493
1094
|
".pdf",
|
|
1494
1095
|
".docx",
|
|
1495
1096
|
".pptx",
|
|
@@ -1500,136 +1101,109 @@ function registerIpcHandlers(ipcMain2, getWindow) {
|
|
|
1500
1101
|
".epub",
|
|
1501
1102
|
".rtf"
|
|
1502
1103
|
]);
|
|
1503
|
-
if (
|
|
1104
|
+
if (a.has(s))
|
|
1504
1105
|
try {
|
|
1505
|
-
const
|
|
1506
|
-
return { name, content };
|
|
1507
|
-
} catch (
|
|
1508
|
-
return { name, content: `[Error reading file: ${
|
|
1106
|
+
const l = await y(e, "utf-8");
|
|
1107
|
+
return { name: o, content: l };
|
|
1108
|
+
} catch (l) {
|
|
1109
|
+
return { name: o, content: `[Error reading file: ${l instanceof Error ? l.message : String(l)}]` };
|
|
1509
1110
|
}
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
const execFileAsync2 = promisify(execFile);
|
|
1111
|
+
if (i.has(s)) {
|
|
1112
|
+
const l = R(U);
|
|
1513
1113
|
try {
|
|
1514
|
-
const { stdout } = await
|
|
1114
|
+
const { stdout: c } = await l("markitdown", [e], {
|
|
1515
1115
|
timeout: 3e4,
|
|
1516
|
-
maxBuffer:
|
|
1116
|
+
maxBuffer: 10485760
|
|
1517
1117
|
// 10 MB
|
|
1518
1118
|
});
|
|
1519
|
-
return { name, content:
|
|
1520
|
-
} catch (
|
|
1521
|
-
const
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
};
|
|
1527
|
-
}
|
|
1528
|
-
return { name, content: `[Error converting file: ${msg}]` };
|
|
1119
|
+
return { name: o, content: c };
|
|
1120
|
+
} catch (c) {
|
|
1121
|
+
const m = c instanceof Error ? c.message : String(c);
|
|
1122
|
+
return m.includes("ENOENT") ? {
|
|
1123
|
+
name: o,
|
|
1124
|
+
content: `[Cannot convert ${s} file: markitdown is not installed. Run: pip install 'markitdown[all]']`
|
|
1125
|
+
} : { name: o, content: `[Error converting file: ${m}]` };
|
|
1529
1126
|
}
|
|
1530
1127
|
}
|
|
1531
|
-
return { name, content: `[Unsupported file type: ${
|
|
1532
|
-
})
|
|
1533
|
-
|
|
1534
|
-
const execFileAsync2 = promisify(execFile);
|
|
1128
|
+
return { name: o, content: `[Unsupported file type: ${o}]` };
|
|
1129
|
+
}), n.handle("markitdown:check", async () => {
|
|
1130
|
+
const t = R(U);
|
|
1535
1131
|
try {
|
|
1536
|
-
const { stdout } = await
|
|
1537
|
-
return { installed:
|
|
1132
|
+
const { stdout: e } = await t("markitdown", ["--version"], { timeout: 5e3 });
|
|
1133
|
+
return { installed: !0, version: e.trim() };
|
|
1538
1134
|
} catch {
|
|
1539
|
-
return { installed:
|
|
1135
|
+
return { installed: !1 };
|
|
1540
1136
|
}
|
|
1541
|
-
})
|
|
1542
|
-
|
|
1543
|
-
const execFileAsync2 = promisify(execFile);
|
|
1137
|
+
}), n.handle("markitdown:install", async () => {
|
|
1138
|
+
const t = R(U);
|
|
1544
1139
|
try {
|
|
1545
|
-
await
|
|
1140
|
+
return await t("pip", ["install", "markitdown[all]"], {
|
|
1546
1141
|
timeout: 12e4,
|
|
1547
1142
|
maxBuffer: 10 * 1024 * 1024
|
|
1548
|
-
});
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
return { success: false, error: err instanceof Error ? err.message : String(err) };
|
|
1143
|
+
}), { success: !0 };
|
|
1144
|
+
} catch (e) {
|
|
1145
|
+
return { success: !1, error: e instanceof Error ? e.message : String(e) };
|
|
1552
1146
|
}
|
|
1553
|
-
})
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
const result = await dialog.showOpenDialog(win, {
|
|
1147
|
+
}), n.handle("dialog:selectDirectory", async () => {
|
|
1148
|
+
const t = r();
|
|
1149
|
+
if (!t) return null;
|
|
1150
|
+
const e = await be.showOpenDialog(t, {
|
|
1558
1151
|
properties: ["openDirectory"]
|
|
1559
1152
|
});
|
|
1560
|
-
return
|
|
1153
|
+
return e.canceled ? null : e.filePaths[0];
|
|
1561
1154
|
});
|
|
1562
1155
|
}
|
|
1563
|
-
const
|
|
1564
|
-
|
|
1565
|
-
const
|
|
1566
|
-
function log(source, ...args) {
|
|
1567
|
-
const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${source}] ${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}
|
|
1156
|
+
const vt = Ee(import.meta.url), J = _e(vt), we = u(J, "..", "electron-debug.log");
|
|
1157
|
+
function P(n, ...r) {
|
|
1158
|
+
const t = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${n}] ${r.map((e) => typeof e == "string" ? e : JSON.stringify(e)).join(" ")}
|
|
1568
1159
|
`;
|
|
1569
|
-
|
|
1160
|
+
Se(we, t);
|
|
1570
1161
|
}
|
|
1571
|
-
|
|
1162
|
+
Ie(we, `=== AI Council Electron — started ${(/* @__PURE__ */ new Date()).toISOString()} ===
|
|
1572
1163
|
`);
|
|
1573
|
-
let
|
|
1574
|
-
function
|
|
1575
|
-
|
|
1576
|
-
mainWindow = new BrowserWindow({
|
|
1164
|
+
let _ = null;
|
|
1165
|
+
function le() {
|
|
1166
|
+
P("main", "Creating BrowserWindow"), _ = new de({
|
|
1577
1167
|
width: 1200,
|
|
1578
1168
|
height: 800,
|
|
1579
1169
|
minWidth: 800,
|
|
1580
1170
|
minHeight: 600,
|
|
1581
1171
|
title: "AI Council",
|
|
1582
1172
|
webPreferences: {
|
|
1583
|
-
contextIsolation:
|
|
1584
|
-
nodeIntegration:
|
|
1585
|
-
preload:
|
|
1173
|
+
contextIsolation: !0,
|
|
1174
|
+
nodeIntegration: !1,
|
|
1175
|
+
preload: u(J, "preload.mjs")
|
|
1586
1176
|
}
|
|
1177
|
+
}), _.webContents.on("console-message", (r, t, e, o, s) => {
|
|
1178
|
+
const a = ["DEBUG", "INFO", "WARN", "ERROR"][t] || "LOG";
|
|
1179
|
+
P(`renderer:${a}`, `${e} (${s}:${o})`);
|
|
1180
|
+
}), _.webContents.on("render-process-gone", (r, t) => {
|
|
1181
|
+
P("main:CRASH", "Renderer process gone:", t);
|
|
1182
|
+
}), _.webContents.on("did-fail-load", (r, t, e) => {
|
|
1183
|
+
P("main:LOAD_ERROR", `Failed to load: ${t} ${e}`);
|
|
1587
1184
|
});
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
});
|
|
1595
|
-
mainWindow.webContents.on("did-fail-load", (_event, errorCode, errorDescription) => {
|
|
1596
|
-
log("main:LOAD_ERROR", `Failed to load: ${errorCode} ${errorDescription}`);
|
|
1597
|
-
});
|
|
1598
|
-
const url = process.env.VITE_DEV_SERVER_URL;
|
|
1599
|
-
if (url) {
|
|
1600
|
-
log("main", `Loading dev server URL: ${url}`);
|
|
1601
|
-
mainWindow.loadURL(url);
|
|
1602
|
-
} else {
|
|
1603
|
-
const filePath = join(__dirname$1, "../dist-renderer/index.html");
|
|
1604
|
-
log("main", `Loading file: ${filePath}`);
|
|
1605
|
-
mainWindow.loadFile(filePath);
|
|
1185
|
+
const n = process.env.VITE_DEV_SERVER_URL;
|
|
1186
|
+
if (n)
|
|
1187
|
+
P("main", `Loading dev server URL: ${n}`), _.loadURL(n);
|
|
1188
|
+
else {
|
|
1189
|
+
const r = u(J, "../dist-renderer/index.html");
|
|
1190
|
+
P("main", `Loading file: ${r}`), _.loadFile(r);
|
|
1606
1191
|
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
}
|
|
1610
|
-
mainWindow.on("closed", () => {
|
|
1611
|
-
mainWindow = null;
|
|
1192
|
+
process.env.VITE_DEV_SERVER_URL && _.webContents.openDevTools(), _.on("closed", () => {
|
|
1193
|
+
_ = null;
|
|
1612
1194
|
});
|
|
1613
1195
|
}
|
|
1614
|
-
|
|
1615
|
-
{ scheme: "council-file", privileges: { bypassCSP:
|
|
1196
|
+
ue.registerSchemesAsPrivileged([
|
|
1197
|
+
{ scheme: "council-file", privileges: { bypassCSP: !0, supportFetchAPI: !0 } }
|
|
1616
1198
|
]);
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
registerIpcHandlers(ipcMain, () => mainWindow);
|
|
1624
|
-
createWindow();
|
|
1625
|
-
app.on("activate", () => {
|
|
1626
|
-
if (BrowserWindow.getAllWindows().length === 0) {
|
|
1627
|
-
createWindow();
|
|
1628
|
-
}
|
|
1199
|
+
j.whenReady().then(() => {
|
|
1200
|
+
P("main", "App ready, registering IPC handlers"), ue.handle("council-file", (n) => {
|
|
1201
|
+
const r = decodeURIComponent(n.url.replace("council-file://", ""));
|
|
1202
|
+
return ke.fetch(Ae(r).href);
|
|
1203
|
+
}), kt(ve, () => _), le(), j.on("activate", () => {
|
|
1204
|
+
de.getAllWindows().length === 0 && le();
|
|
1629
1205
|
});
|
|
1630
1206
|
});
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
app.quit();
|
|
1634
|
-
}
|
|
1207
|
+
j.on("window-all-closed", () => {
|
|
1208
|
+
process.platform !== "darwin" && j.quit();
|
|
1635
1209
|
});
|