@statechange/council 0.2.0 → 0.4.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/assets/icon.icns +0 -0
- package/assets/icon.png +0 -0
- package/assets/logo.png +0 -0
- 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 +152 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/electron/main.js +31 -4
- package/dist/electron/main.js.map +1 -1
- 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 +815 -1218
- 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-BEmsGUDo.js +10 -0
- package/dist-renderer/assets/arc-C-3eukZI.js +1 -0
- package/dist-renderer/assets/array-BKyUJesY.js +1 -0
- package/dist-renderer/assets/az-AZ-76LH7QW2-Bf0F1qpB.js +1 -0
- package/dist-renderer/assets/bg-BG-XCXSNQG7-DYOpZlHd.js +5 -0
- package/dist-renderer/assets/blockDiagram-38ab4fdb-D9VICLGW.js +118 -0
- package/dist-renderer/assets/bn-BD-2XOGV67Q-BjEh0t20.js +5 -0
- package/dist-renderer/assets/c4Diagram-3d4e48cf-CZnDXMcn.js +10 -0
- package/dist-renderer/assets/ca-ES-6MX7JW3Y-Df__LthM.js +8 -0
- package/dist-renderer/assets/channel-BlrodDB2.js +1 -0
- package/dist-renderer/assets/classDiagram-70f12bd4-DXF7s66S.js +2 -0
- package/dist-renderer/assets/classDiagram-v2-f2320105-Co3T9cX2.js +2 -0
- package/dist-renderer/assets/clone-Bajg6GS8.js +1 -0
- package/dist-renderer/assets/createText-2e5e7dd3-4yNgbt5z.js +7 -0
- package/dist-renderer/assets/cs-CZ-2BRQDIVT-D02tEETl.js +11 -0
- package/dist-renderer/assets/da-DK-5WZEPLOC-D0Vxz9Vk.js +5 -0
- package/dist-renderer/assets/de-DE-XR44H4JA-COwIKGj-.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-Csy7YN2f.js +4 -0
- package/dist-renderer/assets/el-GR-BZB4AONW-iL6-ntEA.js +10 -0
- package/dist-renderer/assets/erDiagram-9861fffd-DyRcuDtU.js +51 -0
- package/dist-renderer/assets/es-ES-U4NZUMDT-C3_2w4H5.js +9 -0
- package/dist-renderer/assets/eu-ES-A7QVB2H4-B68OTfIa.js +11 -0
- package/dist-renderer/assets/fa-IR-HGAKTJCU-RH_bUdQt.js +8 -0
- package/dist-renderer/assets/fi-FI-Z5N7JZ37-D_8PqlfJ.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-Bwg_Bau-.js +10 -0
- package/dist-renderer/assets/flowDiagram-66a62f08-Ukgy_1TP.js +4 -0
- package/dist-renderer/assets/flowDiagram-v2-96b9c2cf-tYbWRv_s.js +1 -0
- package/dist-renderer/assets/flowchart-elk-definition-4a651766-D9wgUOiJ.js +139 -0
- package/dist-renderer/assets/fr-FR-RHASNOE6-Dm8QCd71.js +9 -0
- package/dist-renderer/assets/ganttDiagram-c361ad54-BLXDD75q.js +257 -0
- package/dist-renderer/assets/gitGraphDiagram-72cf32ee-ByHTP6zu.js +70 -0
- package/dist-renderer/assets/gl-ES-HMX3MZ6V-DUKlmpw0.js +10 -0
- package/dist-renderer/assets/graph-7VOBm4BD.js +1 -0
- package/dist-renderer/assets/he-IL-6SHJWFNN-CoS2sda5.js +10 -0
- package/dist-renderer/assets/hi-IN-IWLTKZ5I-SFghE6jx.js +4 -0
- package/dist-renderer/assets/hu-HU-A5ZG7DT2-BjpWP_uc.js +7 -0
- package/dist-renderer/assets/id-ID-SAP4L64H-DiwgVOXB.js +10 -0
- package/dist-renderer/assets/image-blob-reduce.esm-BLtmMM_J.js +2 -0
- package/dist-renderer/assets/index-3862675e-Cv1-zuCn.js +1 -0
- package/dist-renderer/assets/index-BbXXBOD5.js +36 -0
- package/dist-renderer/assets/index-DqWupD0e.js +95 -0
- package/dist-renderer/assets/index-jTu4f6ys.css +1 -0
- package/dist-renderer/assets/infoDiagram-f8f76790-BWmMhC4P.js +7 -0
- package/dist-renderer/assets/init-Gi6I4Gst.js +1 -0
- package/dist-renderer/assets/it-IT-JPQ66NNP-Ca5US-RN.js +11 -0
- package/dist-renderer/assets/ja-JP-DBVTYXUO-BaDej1C9.js +8 -0
- package/dist-renderer/assets/journeyDiagram-49397b02-DaPuGc0H.js +139 -0
- package/dist-renderer/assets/kaa-6HZHGXH3-DZ1sjUzm.js +1 -0
- package/dist-renderer/assets/kab-KAB-ZGHBKWFO-DGQGFUhW.js +8 -0
- package/dist-renderer/assets/katex-DhXJpUyf.js +261 -0
- package/dist-renderer/assets/kk-KZ-P5N5QNE5-BEZQqWHo.js +1 -0
- package/dist-renderer/assets/km-KH-HSX4SM5Z-CO9vn9kN.js +11 -0
- package/dist-renderer/assets/ko-KR-MTYHY66A-BBZ7Ud6z.js +9 -0
- package/dist-renderer/assets/ku-TR-6OUDTVRD-DKfTGyMV.js +9 -0
- package/dist-renderer/assets/layout-mlXn6Ojp.js +1 -0
- package/dist-renderer/assets/line-BNQpE2Vh.js +1 -0
- package/dist-renderer/assets/linear-D2QI1dee.js +1 -0
- package/dist-renderer/assets/lt-LT-XHIRWOB4-BMmpYFGj.js +3 -0
- package/dist-renderer/assets/lv-LV-5QDEKY6T-Bfb3if1K.js +7 -0
- package/dist-renderer/assets/mindmap-definition-fc14e90a-EDdQyXuv.js +415 -0
- package/dist-renderer/assets/mr-IN-CRQNXWMA-BapNRVzr.js +13 -0
- package/dist-renderer/assets/my-MM-5M5IBNSE-W9rGsh-o.js +1 -0
- package/dist-renderer/assets/nb-NO-T6EIAALU-BZeyzeQ5.js +10 -0
- package/dist-renderer/assets/nl-NL-IS3SIHDZ-BIIy5I6E.js +8 -0
- package/dist-renderer/assets/nn-NO-6E72VCQL-BSs-SHef.js +8 -0
- package/dist-renderer/assets/oc-FR-POXYY2M6-fLrkwS2M.js +8 -0
- package/dist-renderer/assets/ordinal-BENe2yWM.js +1 -0
- package/dist-renderer/assets/pa-IN-N4M65BXN-lqr6ro4k.js +4 -0
- package/dist-renderer/assets/path-CbwjOpE9.js +1 -0
- package/dist-renderer/assets/percentages-BXMCSKIN-CcGqwI82.js +199 -0
- package/dist-renderer/assets/pica-CsoVhDGL.js +2 -0
- package/dist-renderer/assets/pieDiagram-8a3498a8-TG2guad1.js +35 -0
- package/dist-renderer/assets/pl-PL-T2D74RX3-CdSVjK8j.js +9 -0
- package/dist-renderer/assets/pt-BR-5N22H2LF-CLKJESeE.js +9 -0
- package/dist-renderer/assets/pt-PT-UZXXM6DQ-C6P0W49t.js +9 -0
- package/dist-renderer/assets/quadrantDiagram-120e2f19-BRoTbIzZ.js +7 -0
- package/dist-renderer/assets/requirementDiagram-deff3bca-C3uaot9h.js +52 -0
- package/dist-renderer/assets/ro-RO-JPDTUUEW-DriQxnmt.js +11 -0
- package/dist-renderer/assets/roundRect-mAH3dD0p.js +1 -0
- package/dist-renderer/assets/ru-RU-B4JR7IUQ-CxQ01ey-.js +9 -0
- package/dist-renderer/assets/sankeyDiagram-04a897e0-CnvkX9pX.js +8 -0
- package/dist-renderer/assets/sequenceDiagram-704730f1-aEouT8p6.js +122 -0
- package/dist-renderer/assets/si-LK-N5RQ5JYF-CV8Pu1aJ.js +1 -0
- package/dist-renderer/assets/sk-SK-C5VTKIMK-DDgRCRI0.js +6 -0
- package/dist-renderer/assets/sl-SI-NN7IZMDC-YDda-RwR.js +6 -0
- package/dist-renderer/assets/stateDiagram-587899a1-D6akiRDN.js +1 -0
- package/dist-renderer/assets/stateDiagram-v2-d93cdb3a-gvz5zeGn.js +1 -0
- package/dist-renderer/assets/styles-6aaf32cf-BcHCt677.js +207 -0
- package/dist-renderer/assets/styles-9a916d00-BBaXvwsx.js +160 -0
- package/dist-renderer/assets/styles-c10674c1-DqIcUSxt.js +116 -0
- package/dist-renderer/assets/subset-shared.chunk-S20joIBg.js +22 -0
- package/dist-renderer/assets/subset-worker.chunk-pDuYq8bJ.js +1 -0
- package/dist-renderer/assets/sv-SE-XGPEYMSR-Bgr_wBxE.js +10 -0
- package/dist-renderer/assets/svgDrawCommon-08f97a94-BubXvRJb.js +1 -0
- package/dist-renderer/assets/ta-IN-2NMHFXQM-DMEKGupe.js +9 -0
- package/dist-renderer/assets/th-TH-HPSO5L25-Bbz5ouCQ.js +2 -0
- package/dist-renderer/assets/timeline-definition-85554ec2-ClLLYM_x.js +61 -0
- package/dist-renderer/assets/tr-TR-DEFEU3FU-B208BWXV.js +7 -0
- package/dist-renderer/assets/uk-UA-QMV73CPH-B34h_DiI.js +6 -0
- package/dist-renderer/assets/vi-VN-M7AON7JQ-BuDqd7xN.js +5 -0
- package/dist-renderer/assets/xychartDiagram-e933f94c-B176yJLm.js +7 -0
- package/dist-renderer/assets/zh-CN-LNUGB5OW-DXZTQsAk.js +10 -0
- package/dist-renderer/assets/zh-HK-E62DVLB3-ho9ioNpE.js +1 -0
- package/dist-renderer/assets/zh-TW-RAJ6MFWO-Cjm_oNAG.js +9 -0
- package/dist-renderer/icon.png +0 -0
- package/dist-renderer/index.html +13 -0
- package/package.json +8 -3
- package/skills/council-install/SKILL.md +63 -0
package/dist-electron/main.js
CHANGED
|
@@ -1,583 +1,429 @@
|
|
|
1
|
-
import { shell, dialog, protocol,
|
|
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 ee, dialog as ke, app as L, protocol as me, Menu as te, net as ve, BrowserWindow as pe, ipcMain as _e } from "electron";
|
|
2
|
+
import { join as u, basename as C, resolve as J, dirname as Ee } from "node:path";
|
|
3
|
+
import { fileURLToPath as Ae, pathToFileURL as Ie } from "node:url";
|
|
4
|
+
import { existsSync as v, writeFileSync as Se, appendFileSync as Oe } from "node:fs";
|
|
5
|
+
import { readFile as y, readdir as z, stat as fe, mkdir as N, rm as G, writeFile as $, appendFile as Pe } 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 V from "gray-matter";
|
|
10
|
+
import { z as k } from "zod";
|
|
11
|
+
import $e from "@anthropic-ai/sdk";
|
|
12
|
+
import Ce from "openai";
|
|
13
|
+
import { GoogleGenerativeAI as Ne } from "@google/generative-ai";
|
|
14
|
+
import { Ollama as xe } from "ollama";
|
|
15
|
+
import { createRequire as Ue } 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 Re = 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
|
+
}), Te = (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 Le(n, r) {
|
|
33
|
+
for (const t of Te(r, n))
|
|
34
|
+
if (v(t)) {
|
|
35
|
+
const e = await y(t, "utf-8"), { content: o } = V(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 De(n, r) {
|
|
41
|
+
const t = [];
|
|
42
|
+
for (const e of n) {
|
|
43
|
+
const o = await Le(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 Ke(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 je(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 ne(n) {
|
|
69
|
+
const r = J(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 } = V(e), a = Re.parse(o);
|
|
73
|
+
let i = await je(s.trim(), r);
|
|
74
|
+
if (a.skills.length > 0) {
|
|
75
|
+
const l = await De(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: Ke(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 oe(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 ne(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 z(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 ne(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 Be = R(U), he = u(w(), ".ai-council", "config.json"), se = u(w(), ".ai-council", "counsellors");
|
|
111
|
+
async function X() {
|
|
138
112
|
try {
|
|
139
|
-
return JSON.parse(await
|
|
113
|
+
return JSON.parse(await y(he, "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 Q(n) {
|
|
119
|
+
const r = u(w(), ".ai-council");
|
|
120
|
+
await N(r, { recursive: !0 }), await $(he, JSON.stringify(n, null, 2), "utf-8");
|
|
148
121
|
}
|
|
149
|
-
function
|
|
150
|
-
return Object.values(
|
|
122
|
+
function re(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 Ge(n) {
|
|
126
|
+
const r = J(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 X(), 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 Q(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 Fe(n) {
|
|
140
|
+
await N(se, { recursive: !0 });
|
|
141
|
+
const r = C(n, ".git").replace(/\.git$/, ""), t = u(se, r);
|
|
142
|
+
if (v(t))
|
|
143
|
+
throw new Error(`Directory already exists: ${t}. Remove it first or use a different URL.`);
|
|
144
|
+
await Be("git", ["clone", "--depth", "1", n, t]);
|
|
145
|
+
const e = [], o = await X(), 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 z(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 f = (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: f });
|
|
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 G(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 Q(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 We(n, r = !1) {
|
|
180
|
+
const t = await X(), 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 G(o.path, { recursive: !0, force: !0 }), delete e[n], t.counsellors = e, await Q(t);
|
|
245
184
|
}
|
|
246
|
-
function
|
|
247
|
-
const
|
|
248
|
-
apiKey:
|
|
249
|
-
...
|
|
185
|
+
function Me(n) {
|
|
186
|
+
const r = new $e({
|
|
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 Ce({
|
|
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 He(n) {
|
|
289
|
+
const r = n.apiKey ?? process.env.GOOGLE_API_KEY ?? "", t = new Ne(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 xe({
|
|
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 ae = {
|
|
393
|
+
anthropic: Me,
|
|
394
|
+
openai: Ye,
|
|
395
|
+
google: He,
|
|
396
|
+
ollama: Je
|
|
488
397
|
};
|
|
489
|
-
let
|
|
490
|
-
async function
|
|
491
|
-
if (
|
|
492
|
-
const
|
|
398
|
+
let T = null;
|
|
399
|
+
async function ze() {
|
|
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 H = /* @__PURE__ */ new Map();
|
|
411
|
+
function Ve() {
|
|
412
|
+
T = null, H.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 F(n) {
|
|
415
|
+
const r = H.get(n);
|
|
416
|
+
if (r) return r;
|
|
417
|
+
const t = ae[n];
|
|
418
|
+
if (!t)
|
|
419
|
+
throw new Error(`Unknown backend: "${n}". Available: ${Object.keys(ae).join(", ")}`);
|
|
420
|
+
const o = (await ze()).backends[n] ?? {}, s = t(o);
|
|
421
|
+
return H.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 Xe() {
|
|
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 W = "---EXCALIDRAW---", Qe = `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 Ze(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 qe({
|
|
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 F(o.backend), a = o.model ?? s.defaultModel, i = o.systemPrompt || Qe, l = Xe(), 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 \`${W}\` 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 = Ze(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 p = "";
|
|
474
|
+
if (s.chatStream)
|
|
475
|
+
for await (const E of s.chatStream(g)) {
|
|
476
|
+
if (e?.aborted) break;
|
|
477
|
+
p += 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
|
+
p = (await s.chat(g)).content, t && t(p);
|
|
481
|
+
const f = p.indexOf(W);
|
|
482
|
+
if (f === -1)
|
|
483
|
+
return { text: p.trim() };
|
|
484
|
+
const d = p.slice(0, f).trim(), h = p.slice(f + W.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 et = "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 tt({
|
|
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 F(s.backend), i = s.model ?? a.defaultModel, l = n.turns.filter((p) => p.round === r), c = [];
|
|
505
|
+
c.push(`Topic: ${n.topic}`), c.push(`Round ${r}${r === 1 ? " (Constructive)" : " (Rebuttal)"}`), c.push("");
|
|
506
|
+
for (const p of l)
|
|
507
|
+
c.push(`[${p.counsellorName}]:`), c.push(p.content), c.push("");
|
|
508
|
+
const m = {
|
|
509
|
+
model: i,
|
|
510
|
+
systemPrompt: et,
|
|
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 p of a.chatStream(m)) {
|
|
520
|
+
if (o?.aborted) break;
|
|
521
|
+
g += p.delta, p.delta && e && e(p.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 nt({
|
|
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 F(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 ge = u(w(), ".ai-council"), ot = u(ge, "council.log");
|
|
554
|
+
let ie = !1;
|
|
555
|
+
async function st() {
|
|
556
|
+
ie || (await N(ge, { recursive: !0 }), ie = !0);
|
|
757
557
|
}
|
|
758
|
-
function
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
558
|
+
function rt(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 M(n, r, t, e) {
|
|
770
571
|
try {
|
|
771
|
-
await
|
|
772
|
-
await appendFile(LOG_FILE, formatEntry(level, context, message, extra));
|
|
572
|
+
await st(), await Pe(ot, rt(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) => M("INFO", n, r, t),
|
|
578
|
+
warn: (n, r, t) => M("WARN", n, r, t),
|
|
579
|
+
error: (n, r, t) => M("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 at(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 it(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
|
-
}
|
|
811
|
-
const prevRound = currentRound - 1;
|
|
812
|
-
if (prevRound > 1) {
|
|
813
|
-
const prevTurns = turns.filter((t) => t.round === prevRound);
|
|
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
608
|
}
|
|
830
|
-
|
|
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 ct(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 lt(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((p) => `${p.frontmatter.name} (${p.frontmatter.backend}/${p.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 p = 1; p <= s.rounds; p++) {
|
|
665
|
+
const f = m && p > 1 ? ct(s.counsellors, p) : s.counsellors;
|
|
666
|
+
for (const d of f) {
|
|
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: p, 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 F(d.frontmatter.backend), b = d.frontmatter.model ?? h.defaultModel, E = m ? it(s.topic, i, d.id, p) : at(s.topic, i, d.id), Z = {
|
|
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 D, x;
|
|
682
|
+
if (h.chatStream) {
|
|
683
|
+
D = "";
|
|
684
|
+
for await (const S of h.chatStream(Z)) {
|
|
685
|
+
if (s.signal?.aborted) break;
|
|
686
|
+
D += 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(Z);
|
|
690
|
+
D = S.content, x = S.tokenUsage;
|
|
933
691
|
}
|
|
934
|
-
const
|
|
935
|
-
round,
|
|
936
|
-
counsellorId:
|
|
937
|
-
counsellorName:
|
|
938
|
-
content,
|
|
692
|
+
const q = {
|
|
693
|
+
round: p,
|
|
694
|
+
counsellorId: d.id,
|
|
695
|
+
counsellorName: d.frontmatter.name,
|
|
696
|
+
content: D,
|
|
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(q), s.onEvent({ type: "turn_complete", turn: q });
|
|
704
|
+
} catch (h) {
|
|
705
|
+
const b = h instanceof Error ? h.message : String(h);
|
|
706
|
+
O.error("conversation", `Turn failed for ${d.frontmatter.name} (round ${p}, 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: p }), 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: p });
|
|
713
|
+
const h = await tt({
|
|
714
|
+
result: d,
|
|
715
|
+
roundNumber: p,
|
|
716
|
+
config: s.config,
|
|
717
|
+
onChunk: (b) => {
|
|
718
|
+
s.onEvent({ type: "round_summary_chunk", round: p, 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[p] = h, s.onEvent({ type: "round_summary_complete", round: p, summary: h });
|
|
723
|
+
} catch (d) {
|
|
724
|
+
O.error("conversation", `Interim summary failed for round ${p}`, 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 ut(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 dt(n) {
|
|
734
|
+
await N(I, { recursive: !0 });
|
|
735
|
+
const r = new Date(n.startedAt).toISOString().replace(/[:.]/g, "-").slice(0, 19), t = ut(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 mt() {
|
|
739
|
+
await N(I, { recursive: !0 });
|
|
740
|
+
const n = await z(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 ce(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 pt(n) {
|
|
763
|
+
const r = u(I, `${n}.json`);
|
|
764
|
+
await G(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 ht(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 ye = Ue(import.meta.url);
|
|
775
|
+
function gt(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 yt(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 wt(n, r) {
|
|
793
|
+
const t = ye("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 bt(n, r) {
|
|
806
|
+
const { GoogleGenAI: t } = ye("@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 le(n, r, t) {
|
|
822
|
+
const e = t ?? yt(r);
|
|
823
|
+
if (!e) throw new Error("No image-capable backend configured (need OpenAI or Google API key)");
|
|
824
|
+
const o = gt(n);
|
|
825
|
+
return e === "openai" ? wt(o, r) : bt(o, r);
|
|
1114
826
|
}
|
|
1115
|
-
const
|
|
827
|
+
const we = {
|
|
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
|
+
}, ue = [
|
|
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
|
+
], K = [
|
|
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 kt(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 || we.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: ue };
|
|
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: K, 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: K, 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 : K };
|
|
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" ? ue : n === "google" ? K : [], error: e };
|
|
1192
890
|
}
|
|
1193
891
|
}
|
|
1194
|
-
let
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
let config = { backends: {} };
|
|
892
|
+
let A = null, j = [];
|
|
893
|
+
function vt(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 = re(s), i = s.counsellors ?? {};
|
|
903
|
+
return (await oe(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 } = V(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 G(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: we };
|
|
946
|
+
}), n.handle("backend:probe", async (t, e, o) => kt(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"), Ve(), { 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(), j = [];
|
|
953
|
+
const s = u(w(), ".ai-council", "config.json");
|
|
954
|
+
let a = { backends: {} };
|
|
1287
955
|
try {
|
|
1288
|
-
const
|
|
1289
|
-
|
|
956
|
+
const f = await y(s, "utf-8");
|
|
957
|
+
a = JSON.parse(f);
|
|
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 = re(a), l = await oe(e.councilDir, i), c = e.counsellorIds?.length ? l.filter((f) => e.counsellorIds.includes(f.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 = (f) => {
|
|
966
|
+
o.isDestroyed() || o.webContents.send("discussion:event", f);
|
|
967
|
+
}, g = async () => j.length === 0 ? null : {
|
|
968
|
+
round: 0,
|
|
969
|
+
counsellorId: "__user__",
|
|
970
|
+
counsellorName: "You",
|
|
971
|
+
content: j.shift(),
|
|
972
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
973
|
+
model: "human",
|
|
974
|
+
backend: "human"
|
|
975
|
+
}, p = {
|
|
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 f = await lt(p);
|
|
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 qe({
|
|
992
|
+
result: f,
|
|
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
|
+
f.summary = d.text, d.diagram && (f.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 = f.turns.filter((b) => b.round === 1), h = await nt({
|
|
1006
|
+
topic: f.topic,
|
|
1007
|
+
firstRoundTurns: d,
|
|
1008
|
+
config: a
|
|
1358
1009
|
});
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
log$1.error("ipc:discussion", "Title generation failed", err);
|
|
1010
|
+
f.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 le(f, a, d);
|
|
1019
|
+
f.infographics || (f.infographics = []), f.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: f });
|
|
1380
1024
|
try {
|
|
1381
|
-
await
|
|
1382
|
-
} catch (
|
|
1383
|
-
|
|
1025
|
+
await dt(f);
|
|
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 (f) {
|
|
1030
|
+
O.error("ipc:discussion", "Discussion failed", f), m({ type: "error", counsellorName: "", error: f instanceof Error ? f.message : String(f) });
|
|
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) => (j.push(e), { success: !0 })), n.handle("registry:add-local", async (t, e) => Ge(e)), n.handle("registry:add-remote", async (t, e) => Fe(e)), n.handle("registry:remove", async (t, e, o) => (await We(e, o), { success: !0 })), n.handle("shell:open-in-finder", async (t, e) => {
|
|
1035
|
+
ee.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
|
+
ee.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 () => mt()), n.handle("history:get", async (t, e) => ce(e)), n.handle("history:delete", async (t, e) => (await pt(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 ce(e), l = await le(i, a, o);
|
|
1054
|
+
return await ft(e, l), { infographic: l };
|
|
1055
|
+
}), n.handle("infographic:delete", async (t, e, o) => (await ht(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,132 @@ 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 ke.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 _t = Ae(import.meta.url), B = Ee(_t), be = u(B, "..", "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
|
+
Oe(be, t);
|
|
1570
1161
|
}
|
|
1571
|
-
|
|
1162
|
+
Se(be, `=== State Change Council Electron — started ${(/* @__PURE__ */ new Date()).toISOString()} ===
|
|
1572
1163
|
`);
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1164
|
+
L.name = "State Change Council";
|
|
1165
|
+
let _ = null;
|
|
1166
|
+
function de() {
|
|
1167
|
+
P("main", "Creating BrowserWindow"), _ = new pe({
|
|
1577
1168
|
width: 1200,
|
|
1578
1169
|
height: 800,
|
|
1579
1170
|
minWidth: 800,
|
|
1580
1171
|
minHeight: 600,
|
|
1581
|
-
title: "
|
|
1172
|
+
title: "State Change Council",
|
|
1173
|
+
icon: J(B, "..", "assets", "icon.png"),
|
|
1582
1174
|
webPreferences: {
|
|
1583
|
-
contextIsolation:
|
|
1584
|
-
nodeIntegration:
|
|
1585
|
-
preload:
|
|
1175
|
+
contextIsolation: !0,
|
|
1176
|
+
nodeIntegration: !1,
|
|
1177
|
+
preload: u(B, "preload.mjs")
|
|
1586
1178
|
}
|
|
1179
|
+
}), _.webContents.on("console-message", (r, t, e, o, s) => {
|
|
1180
|
+
const a = ["DEBUG", "INFO", "WARN", "ERROR"][t] || "LOG";
|
|
1181
|
+
P(`renderer:${a}`, `${e} (${s}:${o})`);
|
|
1182
|
+
}), _.webContents.on("render-process-gone", (r, t) => {
|
|
1183
|
+
P("main:CRASH", "Renderer process gone:", t);
|
|
1184
|
+
}), _.webContents.on("did-fail-load", (r, t, e) => {
|
|
1185
|
+
P("main:LOAD_ERROR", `Failed to load: ${t} ${e}`);
|
|
1587
1186
|
});
|
|
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);
|
|
1187
|
+
const n = process.env.VITE_DEV_SERVER_URL;
|
|
1188
|
+
if (n)
|
|
1189
|
+
P("main", `Loading dev server URL: ${n}`), _.loadURL(n);
|
|
1190
|
+
else {
|
|
1191
|
+
const r = u(B, "../dist-renderer/index.html");
|
|
1192
|
+
P("main", `Loading file: ${r}`), _.loadFile(r);
|
|
1606
1193
|
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
}
|
|
1610
|
-
mainWindow.on("closed", () => {
|
|
1611
|
-
mainWindow = null;
|
|
1194
|
+
process.env.VITE_DEV_SERVER_URL && _.webContents.openDevTools(), _.on("closed", () => {
|
|
1195
|
+
_ = null;
|
|
1612
1196
|
});
|
|
1613
1197
|
}
|
|
1614
|
-
|
|
1615
|
-
{ scheme: "council-file", privileges: { bypassCSP:
|
|
1198
|
+
me.registerSchemesAsPrivileged([
|
|
1199
|
+
{ scheme: "council-file", privileges: { bypassCSP: !0, supportFetchAPI: !0 } }
|
|
1616
1200
|
]);
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1201
|
+
L.whenReady().then(() => {
|
|
1202
|
+
P("main", "App ready, registering IPC handlers");
|
|
1203
|
+
const n = "State Change Council", r = [
|
|
1204
|
+
{
|
|
1205
|
+
label: n,
|
|
1206
|
+
submenu: [
|
|
1207
|
+
{ role: "about", label: `About ${n}` },
|
|
1208
|
+
{ type: "separator" },
|
|
1209
|
+
{ role: "services" },
|
|
1210
|
+
{ type: "separator" },
|
|
1211
|
+
{ role: "hide", label: `Hide ${n}` },
|
|
1212
|
+
{ role: "hideOthers" },
|
|
1213
|
+
{ role: "unhide" },
|
|
1214
|
+
{ type: "separator" },
|
|
1215
|
+
{ role: "quit", label: `Quit ${n}` }
|
|
1216
|
+
]
|
|
1217
|
+
},
|
|
1218
|
+
{ role: "fileMenu" },
|
|
1219
|
+
{ role: "editMenu" },
|
|
1220
|
+
{ role: "viewMenu" },
|
|
1221
|
+
{ role: "windowMenu" }
|
|
1222
|
+
];
|
|
1223
|
+
te.setApplicationMenu(te.buildFromTemplate(r)), me.handle("council-file", (t) => {
|
|
1224
|
+
const e = decodeURIComponent(t.url.replace("council-file://", ""));
|
|
1225
|
+
return ve.fetch(Ie(e).href);
|
|
1226
|
+
}), vt(_e, () => _), de(), L.on("activate", () => {
|
|
1227
|
+
pe.getAllWindows().length === 0 && de();
|
|
1629
1228
|
});
|
|
1630
1229
|
});
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
app.quit();
|
|
1634
|
-
}
|
|
1230
|
+
L.on("window-all-closed", () => {
|
|
1231
|
+
process.platform !== "darwin" && L.quit();
|
|
1635
1232
|
});
|