@statechange/council 0.5.0 → 0.7.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/README.md +27 -27
- package/dist/cli.js +15 -15
- package/dist/cli.js.map +1 -1
- package/dist/commands/councilor.d.ts +7 -0
- package/dist/commands/councilor.js +98 -0
- package/dist/commands/councilor.js.map +1 -0
- package/dist/commands/discuss.d.ts +2 -2
- package/dist/commands/discuss.js +14 -14
- package/dist/commands/discuss.js.map +1 -1
- package/dist/commands/history.js +2 -2
- package/dist/commands/history.js.map +1 -1
- package/dist/commands/list.js +7 -7
- package/dist/commands/list.js.map +1 -1
- package/dist/core/conversation-engine.d.ts +5 -3
- package/dist/core/conversation-engine.js +59 -34
- package/dist/core/conversation-engine.js.map +1 -1
- package/dist/core/councilor-loader.d.ts +4 -0
- package/dist/core/councilor-loader.js +98 -0
- package/dist/core/councilor-loader.js.map +1 -0
- package/dist/core/councilor-registry.d.ts +12 -0
- package/dist/core/councilor-registry.js +131 -0
- package/dist/core/councilor-registry.js.map +1 -0
- package/dist/core/counsellor-loader.js +2 -1
- package/dist/core/counsellor-loader.js.map +1 -1
- package/dist/core/excalidraw-cheatsheet.js +2 -2
- package/dist/core/history.d.ts +1 -1
- package/dist/core/history.js +1 -1
- package/dist/core/history.js.map +1 -1
- package/dist/core/infographic.js +2 -2
- package/dist/core/infographic.js.map +1 -1
- package/dist/core/output-formatter.js +2 -2
- package/dist/core/output-formatter.js.map +1 -1
- package/dist/core/secretary.js +7 -7
- package/dist/core/secretary.js.map +1 -1
- package/dist/core/skill-loader.d.ts +2 -2
- package/dist/core/skill-loader.js +6 -6
- package/dist/core/skill-loader.js.map +1 -1
- package/dist/electron/ipc-handlers.js +73 -64
- package/dist/electron/ipc-handlers.js.map +1 -1
- package/dist/electron/main.js +5 -2
- package/dist/electron/main.js.map +1 -1
- package/dist/electron/preload.js +5 -5
- package/dist/electron/preload.js.map +1 -1
- package/dist/types.d.ts +16 -13
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/dist-electron/main.js +619 -595
- package/dist-electron/preload.mjs +1 -1
- package/dist-renderer/assets/{ar-SA-G6X2FPQ2-DIdoFnw2.js → ar-SA-G6X2FPQ2-BgPx2ItE.js} +1 -1
- package/dist-renderer/assets/{arc-BTotlQZy.js → arc-uuSymAy6.js} +1 -1
- package/dist-renderer/assets/{az-AZ-76LH7QW2-vKvyE204.js → az-AZ-76LH7QW2-CXxeQJ5N.js} +1 -1
- package/dist-renderer/assets/{bg-BG-XCXSNQG7-CUw6XMat.js → bg-BG-XCXSNQG7-B-v6aJJk.js} +1 -1
- package/dist-renderer/assets/{blockDiagram-38ab4fdb-KuBxDdIg.js → blockDiagram-38ab4fdb-B2x5B_qZ.js} +1 -1
- package/dist-renderer/assets/{bn-BD-2XOGV67Q-jAYEmCju.js → bn-BD-2XOGV67Q-CmhmKt_C.js} +1 -1
- package/dist-renderer/assets/{c4Diagram-3d4e48cf-BNBdnuO2.js → c4Diagram-3d4e48cf-qv_cNClp.js} +1 -1
- package/dist-renderer/assets/{ca-ES-6MX7JW3Y-CI1kukXq.js → ca-ES-6MX7JW3Y-Cnypgm9v.js} +1 -1
- package/dist-renderer/assets/channel-CbR4ut5U.js +1 -0
- package/dist-renderer/assets/{classDiagram-70f12bd4-CupH4cFs.js → classDiagram-70f12bd4-DdjxeBAm.js} +1 -1
- package/dist-renderer/assets/{classDiagram-v2-f2320105-BahsG3Tc.js → classDiagram-v2-f2320105-DjGhSmzz.js} +1 -1
- package/dist-renderer/assets/clone-CdbunAYL.js +1 -0
- package/dist-renderer/assets/{createText-2e5e7dd3-B3UjfUG4.js → createText-2e5e7dd3-DPeQ2O5Z.js} +1 -1
- package/dist-renderer/assets/{cs-CZ-2BRQDIVT-UI0wC0-v.js → cs-CZ-2BRQDIVT-CLjGATXX.js} +1 -1
- package/dist-renderer/assets/{da-DK-5WZEPLOC-CQAf8fls.js → da-DK-5WZEPLOC-D5L0qoxT.js} +1 -1
- package/dist-renderer/assets/{de-DE-XR44H4JA-CjKBhliG.js → de-DE-XR44H4JA-C32FRrv8.js} +1 -1
- package/dist-renderer/assets/{edges-e0da2a9e-2iE4P-5f.js → edges-e0da2a9e-C9UtuZtL.js} +1 -1
- package/dist-renderer/assets/{el-GR-BZB4AONW-CQB6VLsx.js → el-GR-BZB4AONW-DbhHCmA8.js} +1 -1
- package/dist-renderer/assets/{erDiagram-9861fffd-BBDZcr4V.js → erDiagram-9861fffd-BkLC6VUM.js} +1 -1
- package/dist-renderer/assets/{es-ES-U4NZUMDT-DiI8d1UY.js → es-ES-U4NZUMDT-DXQY7ys3.js} +1 -1
- package/dist-renderer/assets/{eu-ES-A7QVB2H4-DkxqpvcF.js → eu-ES-A7QVB2H4-B4VgVqIF.js} +1 -1
- package/dist-renderer/assets/{fa-IR-HGAKTJCU-D6yQ0OLT.js → fa-IR-HGAKTJCU-elHgBamS.js} +1 -1
- package/dist-renderer/assets/{fi-FI-Z5N7JZ37-YXeS6J7A.js → fi-FI-Z5N7JZ37-BQe_Oc8x.js} +1 -1
- package/dist-renderer/assets/{flowDb-956e92f1-BUyPBg4x.js → flowDb-956e92f1-DR2fF74n.js} +1 -1
- package/dist-renderer/assets/{flowDiagram-66a62f08-DlzNPiyJ.js → flowDiagram-66a62f08-wDDwHPpp.js} +1 -1
- package/dist-renderer/assets/flowDiagram-v2-96b9c2cf-DYxWopzM.js +1 -0
- package/dist-renderer/assets/{flowchart-elk-definition-4a651766-Chzxgp5p.js → flowchart-elk-definition-4a651766-m7cC1ndJ.js} +1 -1
- package/dist-renderer/assets/{fr-FR-RHASNOE6-f9mGiK-9.js → fr-FR-RHASNOE6-U8yDW4RB.js} +1 -1
- package/dist-renderer/assets/{ganttDiagram-c361ad54-WJyNxrSM.js → ganttDiagram-c361ad54-SpXIeRET.js} +1 -1
- package/dist-renderer/assets/{gitGraphDiagram-72cf32ee-BVsVFoEf.js → gitGraphDiagram-72cf32ee-CqmKGlMY.js} +1 -1
- package/dist-renderer/assets/{gl-ES-HMX3MZ6V-BjCm-PNJ.js → gl-ES-HMX3MZ6V-ti0GznU7.js} +1 -1
- package/dist-renderer/assets/{graph-CkTunRXI.js → graph-KjGTQ8qd.js} +1 -1
- package/dist-renderer/assets/{he-IL-6SHJWFNN-DfYxNI2P.js → he-IL-6SHJWFNN-M5av5hFk.js} +1 -1
- package/dist-renderer/assets/{hi-IN-IWLTKZ5I-DoZMu26E.js → hi-IN-IWLTKZ5I-Ci72nkHu.js} +1 -1
- package/dist-renderer/assets/{hu-HU-A5ZG7DT2-DTrSQZCB.js → hu-HU-A5ZG7DT2-CJZteS36.js} +1 -1
- package/dist-renderer/assets/{id-ID-SAP4L64H-C_U2ELPS.js → id-ID-SAP4L64H-DmDvIS5c.js} +1 -1
- package/dist-renderer/assets/{index-3862675e-BNtj693g.js → index-3862675e-CCE1hBgs.js} +1 -1
- package/dist-renderer/assets/index-BUIE-P96.js +51 -0
- package/dist-renderer/assets/{index-CMFrvjlh.js → index-CsJIZuMr.js} +4 -4
- package/dist-renderer/assets/{index-Dfvdrrq2.css → index-HXjW9ZG3.css} +1 -1
- package/dist-renderer/assets/{infoDiagram-f8f76790-BgR_oHkS.js → infoDiagram-f8f76790-CcZfsGA-.js} +1 -1
- package/dist-renderer/assets/{it-IT-JPQ66NNP-DUHgayhE.js → it-IT-JPQ66NNP-Bzpfv7zO.js} +1 -1
- package/dist-renderer/assets/{ja-JP-DBVTYXUO-BvvEmKLB.js → ja-JP-DBVTYXUO-Bybhg0KI.js} +1 -1
- package/dist-renderer/assets/{journeyDiagram-49397b02-DMpZKzA6.js → journeyDiagram-49397b02-D5LB2Wfi.js} +1 -1
- package/dist-renderer/assets/{kaa-6HZHGXH3-g6rUcGHV.js → kaa-6HZHGXH3-n7D3u8lS.js} +1 -1
- package/dist-renderer/assets/{kab-KAB-ZGHBKWFO-7y2loFp3.js → kab-KAB-ZGHBKWFO-BsHCeWxt.js} +1 -1
- package/dist-renderer/assets/{kk-KZ-P5N5QNE5-CrUBYCyh.js → kk-KZ-P5N5QNE5-Dvo1EL3s.js} +1 -1
- package/dist-renderer/assets/{km-KH-HSX4SM5Z-BmTnQD1U.js → km-KH-HSX4SM5Z-Bo9Koyui.js} +1 -1
- package/dist-renderer/assets/{ko-KR-MTYHY66A-B13GdnWI.js → ko-KR-MTYHY66A-Dqbq9Ios.js} +1 -1
- package/dist-renderer/assets/{ku-TR-6OUDTVRD-BLvm9HOc.js → ku-TR-6OUDTVRD-B_bI20Zq.js} +1 -1
- package/dist-renderer/assets/{layout-BRp1ZWud.js → layout-BMJBnVbc.js} +1 -1
- package/dist-renderer/assets/{line-CBnb-2iQ.js → line-DYvIrKnf.js} +1 -1
- package/dist-renderer/assets/{linear-DflXVGQM.js → linear-CzUW4n8y.js} +1 -1
- package/dist-renderer/assets/{lt-LT-XHIRWOB4-o21UyUem.js → lt-LT-XHIRWOB4-DMcSSx20.js} +1 -1
- package/dist-renderer/assets/{lv-LV-5QDEKY6T-Co_yvRsA.js → lv-LV-5QDEKY6T-WQFa3Exi.js} +1 -1
- package/dist-renderer/assets/{mindmap-definition-fc14e90a-DpcRC-Qx.js → mindmap-definition-fc14e90a-CbbLnbfT.js} +1 -1
- package/dist-renderer/assets/{mr-IN-CRQNXWMA-4PVnnNcU.js → mr-IN-CRQNXWMA-CeiPk7t6.js} +1 -1
- package/dist-renderer/assets/{my-MM-5M5IBNSE-Dk5W3fXX.js → my-MM-5M5IBNSE-9E7W6sP8.js} +1 -1
- package/dist-renderer/assets/{nb-NO-T6EIAALU-DMkPaluQ.js → nb-NO-T6EIAALU-CmEniZIK.js} +1 -1
- package/dist-renderer/assets/{nl-NL-IS3SIHDZ-CYOTB3Eg.js → nl-NL-IS3SIHDZ-zXTYcbKu.js} +1 -1
- package/dist-renderer/assets/{nn-NO-6E72VCQL-D4OuF2An.js → nn-NO-6E72VCQL-h3sqNMWN.js} +1 -1
- package/dist-renderer/assets/{oc-FR-POXYY2M6-Dgdddkga.js → oc-FR-POXYY2M6-DJ0D2dhd.js} +1 -1
- package/dist-renderer/assets/{pa-IN-N4M65BXN-DB2Y0wYm.js → pa-IN-N4M65BXN-DZ6nLqLL.js} +1 -1
- package/dist-renderer/assets/{percentages-BXMCSKIN-bsDgM1tI.js → percentages-BXMCSKIN-C_WTAk1u.js} +7 -7
- package/dist-renderer/assets/{pica-B0wN7u7a.js → pica-41wOwwIp.js} +1 -1
- package/dist-renderer/assets/{pieDiagram-8a3498a8-D8QY6THh.js → pieDiagram-8a3498a8-DbqgEWOz.js} +1 -1
- package/dist-renderer/assets/{pl-PL-T2D74RX3-BvikiN3J.js → pl-PL-T2D74RX3-TTq9_VVs.js} +1 -1
- package/dist-renderer/assets/{pt-BR-5N22H2LF-DsWDLPF-.js → pt-BR-5N22H2LF-D8eWF-rx.js} +1 -1
- package/dist-renderer/assets/{pt-PT-UZXXM6DQ-BnTfNmgE.js → pt-PT-UZXXM6DQ-Ck4YSJms.js} +1 -1
- package/dist-renderer/assets/{quadrantDiagram-120e2f19-CYknF173.js → quadrantDiagram-120e2f19-astysE66.js} +1 -1
- package/dist-renderer/assets/{requirementDiagram-deff3bca-DpK6qRCG.js → requirementDiagram-deff3bca-BczvOV-q.js} +1 -1
- package/dist-renderer/assets/{ro-RO-JPDTUUEW-WzwdaLQU.js → ro-RO-JPDTUUEW-Cix9Zgf2.js} +1 -1
- package/dist-renderer/assets/{ru-RU-B4JR7IUQ-kSoy6NPm.js → ru-RU-B4JR7IUQ-DOEAc_qv.js} +1 -1
- package/dist-renderer/assets/{sankeyDiagram-04a897e0-qq5SY-Ue.js → sankeyDiagram-04a897e0-BdKNjOGj.js} +1 -1
- package/dist-renderer/assets/{sequenceDiagram-704730f1-WIIr9Ug4.js → sequenceDiagram-704730f1-DmhSJhf3.js} +1 -1
- package/dist-renderer/assets/{si-LK-N5RQ5JYF-AuePWwxD.js → si-LK-N5RQ5JYF-CdpuKOSp.js} +1 -1
- package/dist-renderer/assets/{sk-SK-C5VTKIMK-DC2HAG_f.js → sk-SK-C5VTKIMK-B_9Y1a-J.js} +1 -1
- package/dist-renderer/assets/{sl-SI-NN7IZMDC-BYS14Fmd.js → sl-SI-NN7IZMDC-6_HfcMn2.js} +1 -1
- package/dist-renderer/assets/{stateDiagram-587899a1-BoO-tW9h.js → stateDiagram-587899a1--iD3nJ-S.js} +1 -1
- package/dist-renderer/assets/{stateDiagram-v2-d93cdb3a-_GDLfMOw.js → stateDiagram-v2-d93cdb3a-D_ikJOMZ.js} +1 -1
- package/dist-renderer/assets/{styles-6aaf32cf-Bq02XG30.js → styles-6aaf32cf-CmNrPQpM.js} +1 -1
- package/dist-renderer/assets/{styles-9a916d00-DrDuz5OD.js → styles-9a916d00-DF6mmpc8.js} +1 -1
- package/dist-renderer/assets/{styles-c10674c1-BiUI5sb2.js → styles-c10674c1-DrNd7u60.js} +1 -1
- package/dist-renderer/assets/{subset-shared.chunk-B4D4aZVR.js → subset-shared.chunk-DESKTf4i.js} +1 -1
- package/dist-renderer/assets/{subset-worker.chunk-iv_EGnGX.js → subset-worker.chunk-foHYh0Wd.js} +1 -1
- package/dist-renderer/assets/{sv-SE-XGPEYMSR-C_IQ_dRL.js → sv-SE-XGPEYMSR-CIuCW4P8.js} +1 -1
- package/dist-renderer/assets/{svgDrawCommon-08f97a94-CGR4dkyx.js → svgDrawCommon-08f97a94-z7vJpB0X.js} +1 -1
- package/dist-renderer/assets/{ta-IN-2NMHFXQM-Bv6L9r0f.js → ta-IN-2NMHFXQM-BzAn6f_T.js} +1 -1
- package/dist-renderer/assets/{th-TH-HPSO5L25-Y-RfnO5u.js → th-TH-HPSO5L25-F8UKokOj.js} +1 -1
- package/dist-renderer/assets/{timeline-definition-85554ec2-DNqe_2XL.js → timeline-definition-85554ec2-CqgD9A1Q.js} +1 -1
- package/dist-renderer/assets/{tr-TR-DEFEU3FU-B46ZqcXK.js → tr-TR-DEFEU3FU-Blb21beE.js} +1 -1
- package/dist-renderer/assets/{uk-UA-QMV73CPH-DzETOoLm.js → uk-UA-QMV73CPH-BNT7k4n6.js} +1 -1
- package/dist-renderer/assets/{vi-VN-M7AON7JQ-CKt3VFx4.js → vi-VN-M7AON7JQ-Cfx0gaLT.js} +1 -1
- package/dist-renderer/assets/{xychartDiagram-e933f94c-IFYih3oh.js → xychartDiagram-e933f94c-FbPSsBHD.js} +1 -1
- package/dist-renderer/assets/{zh-CN-LNUGB5OW-DDBoaB0P.js → zh-CN-LNUGB5OW-CpiybZS1.js} +1 -1
- package/dist-renderer/assets/{zh-HK-E62DVLB3-CtYuCfrU.js → zh-HK-E62DVLB3-UIQkTr8J.js} +1 -1
- package/dist-renderer/assets/{zh-TW-RAJ6MFWO-61MRQ0N-.js → zh-TW-RAJ6MFWO-GvyNND72.js} +1 -1
- package/dist-renderer/index.html +2 -2
- package/package.json +2 -2
- package/skills/council-manage/SKILL.md +54 -43
- package/skills/council-setup-keys/SKILL.md +2 -2
- package/dist-renderer/assets/channel-CYpnUKSx.js +0 -1
- package/dist-renderer/assets/clone-DFY_WtGN.js +0 -1
- package/dist-renderer/assets/flowDiagram-v2-96b9c2cf-D9BHRMv4.js +0 -1
- package/dist-renderer/assets/index-B-vvA6a2.js +0 -36
package/dist-electron/main.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { shell as
|
|
2
|
-
import { join as
|
|
3
|
-
import { fileURLToPath as
|
|
4
|
-
import { existsSync as
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import
|
|
1
|
+
import { shell as te, dialog as Ee, app as L, protocol as pe, Menu as ne, net as Se, BrowserWindow as fe, ipcMain as Ae } from "electron";
|
|
2
|
+
import { join as d, basename as C, resolve as G, dirname as Ie } from "node:path";
|
|
3
|
+
import { fileURLToPath as Oe, pathToFileURL as $e } from "node:url";
|
|
4
|
+
import { existsSync as E, mkdirSync as Pe, writeFileSync as Ce, appendFileSync as Ne } from "node:fs";
|
|
5
|
+
import { homedir as v } from "node:os";
|
|
6
|
+
import { readFile as w, readdir as V, stat as he, mkdir as N, rm as F, writeFile as P, appendFile as xe } from "node:fs/promises";
|
|
7
|
+
import { execFile as R } from "node:child_process";
|
|
8
|
+
import { promisify as U } from "node:util";
|
|
9
|
+
import X from "gray-matter";
|
|
10
10
|
import { z as k } from "zod";
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import { GoogleGenerativeAI as
|
|
14
|
-
import { Ollama as
|
|
15
|
-
import { createRequire as
|
|
11
|
+
import Re from "@anthropic-ai/sdk";
|
|
12
|
+
import Ue from "openai";
|
|
13
|
+
import { GoogleGenerativeAI as Te } from "@google/generative-ai";
|
|
14
|
+
import { Ollama as Le } from "ollama";
|
|
15
|
+
import { createRequire as De } from "node:module";
|
|
16
16
|
import "dotenv/config";
|
|
17
|
-
const
|
|
17
|
+
const Ke = k.object({
|
|
18
18
|
name: k.string(),
|
|
19
19
|
description: k.string(),
|
|
20
20
|
interests: k.array(k.string()).default([]),
|
|
@@ -23,186 +23,192 @@ const Re = k.object({
|
|
|
23
23
|
skills: k.array(k.string()).default([]),
|
|
24
24
|
temperature: k.number().min(0).max(2).optional(),
|
|
25
25
|
avatar: k.string().optional()
|
|
26
|
-
}),
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
}), je = (o, s) => [
|
|
27
|
+
d(o, "skills", s, "SKILL.md"),
|
|
28
|
+
d(process.cwd(), ".claude", "skills", s, "SKILL.md"),
|
|
29
|
+
d(v(), ".agents", "skills", s, "SKILL.md"),
|
|
30
|
+
d(v(), ".claude", "skills", s, "SKILL.md")
|
|
31
31
|
];
|
|
32
|
-
async function
|
|
33
|
-
for (const t of
|
|
34
|
-
if (
|
|
35
|
-
const e = await
|
|
36
|
-
return
|
|
32
|
+
async function Be(o, s) {
|
|
33
|
+
for (const t of je(s, o))
|
|
34
|
+
if (E(t)) {
|
|
35
|
+
const e = await w(t, "utf-8"), { content: r } = X(e);
|
|
36
|
+
return r.trim();
|
|
37
37
|
}
|
|
38
38
|
return null;
|
|
39
39
|
}
|
|
40
|
-
async function
|
|
40
|
+
async function Ge(o, s) {
|
|
41
41
|
const t = [];
|
|
42
|
-
for (const e of
|
|
43
|
-
const
|
|
44
|
-
|
|
42
|
+
for (const e of o) {
|
|
43
|
+
const r = await Be(e, s);
|
|
44
|
+
r && t.push(`## Skill: ${e}
|
|
45
45
|
|
|
46
|
-
${
|
|
46
|
+
${r}`);
|
|
47
47
|
}
|
|
48
48
|
return t.join(`
|
|
49
49
|
|
|
50
50
|
`);
|
|
51
51
|
}
|
|
52
|
-
function
|
|
53
|
-
return
|
|
52
|
+
function Fe(o, s) {
|
|
53
|
+
return o ? o.startsWith("http://") || o.startsWith("https://") ? o : `council-file://${o.startsWith("/") ? o : d(s, o)}` : void 0;
|
|
54
54
|
}
|
|
55
|
-
async function
|
|
55
|
+
async function We(o, s) {
|
|
56
56
|
const t = /\{\{(.+?)\}\}/g;
|
|
57
|
-
let e =
|
|
58
|
-
for (const
|
|
59
|
-
const
|
|
60
|
-
if (
|
|
61
|
-
const i = await
|
|
62
|
-
e = e.replace(
|
|
57
|
+
let e = o;
|
|
58
|
+
for (const r of o.matchAll(t)) {
|
|
59
|
+
const n = r[1].trim(), a = d(s, n);
|
|
60
|
+
if (E(a)) {
|
|
61
|
+
const i = await w(a, "utf-8");
|
|
62
|
+
e = e.replace(r[0], i.trim());
|
|
63
63
|
} else
|
|
64
|
-
e = e.replace(
|
|
64
|
+
e = e.replace(r[0], `[Reference not found: ${n}]`);
|
|
65
65
|
}
|
|
66
66
|
return e;
|
|
67
67
|
}
|
|
68
|
-
async function
|
|
69
|
-
const
|
|
70
|
-
if (!
|
|
71
|
-
throw new Error(`No ABOUT.md found in ${
|
|
72
|
-
const e = await
|
|
73
|
-
let i = await
|
|
68
|
+
async function oe(o) {
|
|
69
|
+
const s = G(o), t = d(s, "ABOUT.md");
|
|
70
|
+
if (!E(t))
|
|
71
|
+
throw new Error(`No ABOUT.md found in ${s}`);
|
|
72
|
+
const e = await w(t, "utf-8"), { data: r, content: n } = X(e), a = Ke.parse(r);
|
|
73
|
+
let i = await We(n.trim(), s);
|
|
74
74
|
if (a.skills.length > 0) {
|
|
75
|
-
const
|
|
76
|
-
|
|
75
|
+
const u = await Ge(a.skills, s);
|
|
76
|
+
u && (i += `
|
|
77
77
|
|
|
78
|
-
` +
|
|
78
|
+
` + u);
|
|
79
79
|
}
|
|
80
80
|
return {
|
|
81
|
-
id: C(
|
|
81
|
+
id: C(s),
|
|
82
82
|
frontmatter: a,
|
|
83
83
|
systemPrompt: i,
|
|
84
|
-
dirPath:
|
|
85
|
-
avatarUrl:
|
|
84
|
+
dirPath: s,
|
|
85
|
+
avatarUrl: Fe(a.avatar, s)
|
|
86
86
|
};
|
|
87
87
|
}
|
|
88
|
-
async function
|
|
88
|
+
async function re(o, s) {
|
|
89
89
|
const t = [], e = /* @__PURE__ */ new Set();
|
|
90
|
-
if (
|
|
91
|
-
for (const
|
|
92
|
-
if (
|
|
90
|
+
if (s?.length) {
|
|
91
|
+
for (const r of s)
|
|
92
|
+
if (E(d(r, "ABOUT.md")))
|
|
93
93
|
try {
|
|
94
|
-
const
|
|
95
|
-
e.has(
|
|
94
|
+
const n = await oe(r);
|
|
95
|
+
e.has(n.id) || (t.push(n), e.add(n.id));
|
|
96
96
|
} catch {
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
|
-
if (
|
|
100
|
-
const
|
|
101
|
-
for (const
|
|
102
|
-
const a =
|
|
103
|
-
(await
|
|
99
|
+
if (E(o)) {
|
|
100
|
+
const r = await V(o);
|
|
101
|
+
for (const n of r) {
|
|
102
|
+
const a = d(o, n);
|
|
103
|
+
(await he(a)).isDirectory() && E(d(a, "ABOUT.md")) && (e.has(C(a)) || (t.push(await oe(a)), e.add(C(a))));
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
if (t.length === 0)
|
|
107
|
-
throw new Error(
|
|
107
|
+
throw new Error(
|
|
108
|
+
`No councilors found. Searched ${o}${s?.length ? ` and ${s.length} registered path(s)` : ""}.
|
|
109
|
+
Create one with: mkdir -p ~/.ai-council/councilors/my-councilor && council councilor add ~/.ai-council/councilors/my-councilor`
|
|
110
|
+
);
|
|
108
111
|
return t;
|
|
109
112
|
}
|
|
110
|
-
const
|
|
111
|
-
async function
|
|
113
|
+
const Me = U(R), ge = d(v(), ".ai-council", "config.json"), se = d(v(), ".ai-council", "councilors");
|
|
114
|
+
async function Q() {
|
|
112
115
|
try {
|
|
113
|
-
return JSON.parse(await
|
|
116
|
+
return JSON.parse(await w(ge, "utf-8"));
|
|
114
117
|
} catch {
|
|
115
118
|
return { backends: {} };
|
|
116
119
|
}
|
|
117
120
|
}
|
|
118
|
-
async function
|
|
119
|
-
const
|
|
120
|
-
await N(
|
|
121
|
+
async function Z(o) {
|
|
122
|
+
const s = d(v(), ".ai-council");
|
|
123
|
+
await N(s, { recursive: !0 }), await P(ge, JSON.stringify(o, null, 2), "utf-8");
|
|
121
124
|
}
|
|
122
|
-
function
|
|
123
|
-
return
|
|
125
|
+
function Ye(o) {
|
|
126
|
+
return o.councilors ?? o.counsellors ?? {};
|
|
124
127
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
if (
|
|
131
|
-
throw new Error(`
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
path:
|
|
128
|
+
function ae(o) {
|
|
129
|
+
return Object.values(Ye(o)).map((s) => s.path);
|
|
130
|
+
}
|
|
131
|
+
async function He(o) {
|
|
132
|
+
const s = G(o), t = d(s, "ABOUT.md");
|
|
133
|
+
if (!E(t))
|
|
134
|
+
throw new Error(`No ABOUT.md found in ${s}`);
|
|
135
|
+
const e = C(s), r = await Q(), n = r.councilors ?? {};
|
|
136
|
+
if (n[e])
|
|
137
|
+
throw new Error(`Councilor "${e}" is already registered (path: ${n[e].path})`);
|
|
138
|
+
const u = (await w(t, "utf-8")).match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1] ?? e;
|
|
139
|
+
return n[e] = {
|
|
140
|
+
path: s,
|
|
135
141
|
source: "local",
|
|
136
142
|
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
137
|
-
},
|
|
143
|
+
}, r.councilors = n, await Z(r), { id: e, name: u };
|
|
138
144
|
}
|
|
139
|
-
async function
|
|
145
|
+
async function Je(o) {
|
|
140
146
|
await N(se, { recursive: !0 });
|
|
141
|
-
const
|
|
142
|
-
if (
|
|
147
|
+
const s = C(o, ".git").replace(/\.git$/, ""), t = d(se, s);
|
|
148
|
+
if (E(t))
|
|
143
149
|
throw new Error(`Directory already exists: ${t}. Remove it first or use a different URL.`);
|
|
144
|
-
await
|
|
145
|
-
const e = [],
|
|
146
|
-
if (
|
|
147
|
-
const a =
|
|
148
|
-
if (
|
|
149
|
-
throw new Error(`
|
|
150
|
-
const c = (await
|
|
151
|
-
|
|
150
|
+
await Me("git", ["clone", "--depth", "1", o, t]);
|
|
151
|
+
const e = [], r = await Q(), n = r.councilors ?? {};
|
|
152
|
+
if (E(d(t, "ABOUT.md"))) {
|
|
153
|
+
const a = s;
|
|
154
|
+
if (n[a])
|
|
155
|
+
throw new Error(`Councilor "${a}" is already registered`);
|
|
156
|
+
const c = (await w(d(t, "ABOUT.md"), "utf-8")).match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1] ?? a;
|
|
157
|
+
n[a] = {
|
|
152
158
|
path: t,
|
|
153
159
|
source: "git",
|
|
154
|
-
url:
|
|
160
|
+
url: o,
|
|
155
161
|
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
156
162
|
}, e.push({ id: a, name: c });
|
|
157
163
|
} else {
|
|
158
|
-
const a = await
|
|
164
|
+
const a = await V(t);
|
|
159
165
|
for (const i of a) {
|
|
160
166
|
if (i.startsWith(".")) continue;
|
|
161
|
-
const
|
|
162
|
-
if ((await
|
|
167
|
+
const u = d(t, i);
|
|
168
|
+
if ((await he(u)).isDirectory() && E(d(u, "ABOUT.md"))) {
|
|
163
169
|
const m = i;
|
|
164
|
-
if (
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
path:
|
|
170
|
+
if (n[m]) continue;
|
|
171
|
+
const p = (await w(d(u, "ABOUT.md"), "utf-8")).match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1] ?? m;
|
|
172
|
+
n[m] = {
|
|
173
|
+
path: u,
|
|
168
174
|
source: "git",
|
|
169
|
-
url:
|
|
175
|
+
url: o,
|
|
170
176
|
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
171
|
-
}, e.push({ id: m, name:
|
|
177
|
+
}, e.push({ id: m, name: p });
|
|
172
178
|
}
|
|
173
179
|
}
|
|
174
180
|
if (e.length === 0)
|
|
175
|
-
throw await F(t, { recursive: !0, force: !0 }), new Error("No
|
|
181
|
+
throw await F(t, { recursive: !0, force: !0 }), new Error("No councilors found in cloned repository (no ABOUT.md files)");
|
|
176
182
|
}
|
|
177
|
-
return
|
|
183
|
+
return r.councilors = n, await Z(r), e;
|
|
178
184
|
}
|
|
179
|
-
async function
|
|
180
|
-
const t = await
|
|
181
|
-
if (!
|
|
182
|
-
throw new Error(`
|
|
183
|
-
|
|
185
|
+
async function ze(o, s = !1) {
|
|
186
|
+
const t = await Q(), e = t.councilors ?? {}, r = e[o];
|
|
187
|
+
if (!r)
|
|
188
|
+
throw new Error(`Councilor "${o}" is not registered`);
|
|
189
|
+
s && r.source === "git" && E(r.path) && await F(r.path, { recursive: !0, force: !0 }), delete e[o], t.councilors = e, await Z(t);
|
|
184
190
|
}
|
|
185
|
-
function
|
|
186
|
-
const
|
|
187
|
-
apiKey:
|
|
188
|
-
...
|
|
191
|
+
function Ve(o) {
|
|
192
|
+
const s = new Re({
|
|
193
|
+
apiKey: o.apiKey ?? process.env.ANTHROPIC_API_KEY,
|
|
194
|
+
...o.baseUrl ? { baseURL: o.baseUrl } : {}
|
|
189
195
|
});
|
|
190
196
|
return {
|
|
191
197
|
name: "anthropic",
|
|
192
198
|
defaultModel: "claude-sonnet-4-5-20250929",
|
|
193
199
|
async chat(t) {
|
|
194
|
-
const e = await
|
|
200
|
+
const e = await s.messages.create({
|
|
195
201
|
model: t.model,
|
|
196
202
|
max_tokens: 4096,
|
|
197
203
|
system: t.systemPrompt,
|
|
198
|
-
messages: t.messages.map((
|
|
199
|
-
role:
|
|
200
|
-
content:
|
|
204
|
+
messages: t.messages.map((n) => ({
|
|
205
|
+
role: n.role,
|
|
206
|
+
content: n.content
|
|
201
207
|
})),
|
|
202
208
|
...t.temperature !== void 0 ? { temperature: t.temperature } : {}
|
|
203
209
|
});
|
|
204
210
|
return {
|
|
205
|
-
content: e.content.find((
|
|
211
|
+
content: e.content.find((n) => n.type === "text")?.text ?? "",
|
|
206
212
|
tokenUsage: {
|
|
207
213
|
input: e.usage.input_tokens,
|
|
208
214
|
output: e.usage.output_tokens
|
|
@@ -210,45 +216,45 @@ function Me(n) {
|
|
|
210
216
|
};
|
|
211
217
|
},
|
|
212
218
|
async *chatStream(t) {
|
|
213
|
-
const e =
|
|
219
|
+
const e = s.messages.stream({
|
|
214
220
|
model: t.model,
|
|
215
221
|
max_tokens: 4096,
|
|
216
222
|
system: t.systemPrompt,
|
|
217
|
-
messages: t.messages.map((
|
|
218
|
-
role:
|
|
219
|
-
content:
|
|
223
|
+
messages: t.messages.map((n) => ({
|
|
224
|
+
role: n.role,
|
|
225
|
+
content: n.content
|
|
220
226
|
})),
|
|
221
227
|
...t.temperature !== void 0 ? { temperature: t.temperature } : {}
|
|
222
228
|
});
|
|
223
|
-
for await (const
|
|
224
|
-
|
|
225
|
-
const
|
|
229
|
+
for await (const n of e)
|
|
230
|
+
n.type === "content_block_delta" && n.delta.type === "text_delta" && (yield { delta: n.delta.text });
|
|
231
|
+
const r = await e.finalMessage();
|
|
226
232
|
yield {
|
|
227
233
|
delta: "",
|
|
228
234
|
tokenUsage: {
|
|
229
|
-
input:
|
|
230
|
-
output:
|
|
235
|
+
input: r.usage.input_tokens,
|
|
236
|
+
output: r.usage.output_tokens
|
|
231
237
|
}
|
|
232
238
|
};
|
|
233
239
|
}
|
|
234
240
|
};
|
|
235
241
|
}
|
|
236
|
-
function
|
|
237
|
-
const
|
|
238
|
-
apiKey:
|
|
239
|
-
...
|
|
242
|
+
function Xe(o) {
|
|
243
|
+
const s = new Ue({
|
|
244
|
+
apiKey: o.apiKey ?? process.env.OPENAI_API_KEY,
|
|
245
|
+
...o.baseUrl ? { baseURL: o.baseUrl } : {}
|
|
240
246
|
});
|
|
241
247
|
return {
|
|
242
248
|
name: "openai",
|
|
243
249
|
defaultModel: "gpt-4o",
|
|
244
250
|
async chat(t) {
|
|
245
|
-
const e = await
|
|
251
|
+
const e = await s.chat.completions.create({
|
|
246
252
|
model: t.model,
|
|
247
253
|
messages: [
|
|
248
254
|
{ role: "system", content: t.systemPrompt },
|
|
249
|
-
...t.messages.map((
|
|
250
|
-
role:
|
|
251
|
-
content:
|
|
255
|
+
...t.messages.map((n) => ({
|
|
256
|
+
role: n.role,
|
|
257
|
+
content: n.content
|
|
252
258
|
}))
|
|
253
259
|
],
|
|
254
260
|
...t.temperature !== void 0 ? { temperature: t.temperature } : {}
|
|
@@ -259,48 +265,48 @@ function Ye(n) {
|
|
|
259
265
|
};
|
|
260
266
|
},
|
|
261
267
|
async *chatStream(t) {
|
|
262
|
-
const e = await
|
|
268
|
+
const e = await s.chat.completions.create({
|
|
263
269
|
model: t.model,
|
|
264
270
|
messages: [
|
|
265
271
|
{ role: "system", content: t.systemPrompt },
|
|
266
|
-
...t.messages.map((
|
|
267
|
-
role:
|
|
268
|
-
content:
|
|
272
|
+
...t.messages.map((r) => ({
|
|
273
|
+
role: r.role,
|
|
274
|
+
content: r.content
|
|
269
275
|
}))
|
|
270
276
|
],
|
|
271
277
|
...t.temperature !== void 0 ? { temperature: t.temperature } : {},
|
|
272
278
|
stream: !0,
|
|
273
279
|
stream_options: { include_usage: !0 }
|
|
274
280
|
});
|
|
275
|
-
for await (const
|
|
276
|
-
const
|
|
277
|
-
|
|
281
|
+
for await (const r of e) {
|
|
282
|
+
const n = r.choices[0]?.delta?.content;
|
|
283
|
+
n && (yield { delta: n }), r.usage && (yield {
|
|
278
284
|
delta: "",
|
|
279
285
|
tokenUsage: {
|
|
280
|
-
input:
|
|
281
|
-
output:
|
|
286
|
+
input: r.usage.prompt_tokens,
|
|
287
|
+
output: r.usage.completion_tokens
|
|
282
288
|
}
|
|
283
289
|
});
|
|
284
290
|
}
|
|
285
291
|
}
|
|
286
292
|
};
|
|
287
293
|
}
|
|
288
|
-
function
|
|
289
|
-
const
|
|
294
|
+
function Qe(o) {
|
|
295
|
+
const s = o.apiKey ?? process.env.GOOGLE_API_KEY ?? "", t = new Te(s);
|
|
290
296
|
return {
|
|
291
297
|
name: "google",
|
|
292
298
|
defaultModel: "gemini-2.0-flash",
|
|
293
299
|
async chat(e) {
|
|
294
|
-
const
|
|
300
|
+
const r = t.getGenerativeModel({
|
|
295
301
|
model: e.model,
|
|
296
302
|
systemInstruction: e.systemPrompt,
|
|
297
303
|
generationConfig: {
|
|
298
304
|
...e.temperature !== void 0 ? { temperature: e.temperature } : {}
|
|
299
305
|
}
|
|
300
|
-
}),
|
|
306
|
+
}), n = e.messages.slice(0, -1).map((m) => ({
|
|
301
307
|
role: m.role === "assistant" ? "model" : "user",
|
|
302
308
|
parts: [{ text: m.content }]
|
|
303
|
-
})), a =
|
|
309
|
+
})), a = r.startChat({ history: n }), i = e.messages[e.messages.length - 1], c = (await a.sendMessage(i?.content ?? "")).response;
|
|
304
310
|
return {
|
|
305
311
|
content: c.text(),
|
|
306
312
|
tokenUsage: c.usageMetadata ? {
|
|
@@ -310,21 +316,21 @@ function He(n) {
|
|
|
310
316
|
};
|
|
311
317
|
},
|
|
312
318
|
async *chatStream(e) {
|
|
313
|
-
const
|
|
319
|
+
const r = t.getGenerativeModel({
|
|
314
320
|
model: e.model,
|
|
315
321
|
systemInstruction: e.systemPrompt,
|
|
316
322
|
generationConfig: {
|
|
317
323
|
...e.temperature !== void 0 ? { temperature: e.temperature } : {}
|
|
318
324
|
}
|
|
319
|
-
}),
|
|
325
|
+
}), n = e.messages.slice(0, -1).map((m) => ({
|
|
320
326
|
role: m.role === "assistant" ? "model" : "user",
|
|
321
327
|
parts: [{ text: m.content }]
|
|
322
|
-
})), a =
|
|
323
|
-
for await (const m of
|
|
324
|
-
const
|
|
325
|
-
|
|
328
|
+
})), a = r.startChat({ history: n }), i = e.messages[e.messages.length - 1], u = await a.sendMessageStream(i?.content ?? "");
|
|
329
|
+
for await (const m of u.stream) {
|
|
330
|
+
const y = m.text();
|
|
331
|
+
y && (yield { delta: y });
|
|
326
332
|
}
|
|
327
|
-
const c = await
|
|
333
|
+
const c = await u.response;
|
|
328
334
|
yield {
|
|
329
335
|
delta: "",
|
|
330
336
|
tokenUsage: c.usageMetadata ? {
|
|
@@ -335,21 +341,21 @@ function He(n) {
|
|
|
335
341
|
}
|
|
336
342
|
};
|
|
337
343
|
}
|
|
338
|
-
function
|
|
339
|
-
const
|
|
340
|
-
host:
|
|
344
|
+
function Ze(o) {
|
|
345
|
+
const s = new Le({
|
|
346
|
+
host: o.baseUrl ?? "http://localhost:11434"
|
|
341
347
|
});
|
|
342
348
|
return {
|
|
343
349
|
name: "ollama",
|
|
344
350
|
defaultModel: "llama3.2",
|
|
345
351
|
async chat(t) {
|
|
346
|
-
const e = await
|
|
352
|
+
const e = await s.chat({
|
|
347
353
|
model: t.model,
|
|
348
354
|
messages: [
|
|
349
355
|
{ role: "system", content: t.systemPrompt },
|
|
350
|
-
...t.messages.map((
|
|
351
|
-
role:
|
|
352
|
-
content:
|
|
356
|
+
...t.messages.map((r) => ({
|
|
357
|
+
role: r.role,
|
|
358
|
+
content: r.content
|
|
353
359
|
}))
|
|
354
360
|
],
|
|
355
361
|
options: {
|
|
@@ -365,7 +371,7 @@ function Je(n) {
|
|
|
365
371
|
};
|
|
366
372
|
},
|
|
367
373
|
async *chatStream(t) {
|
|
368
|
-
const e = await
|
|
374
|
+
const e = await s.chat({
|
|
369
375
|
model: t.model,
|
|
370
376
|
messages: [
|
|
371
377
|
{ role: "system", content: t.systemPrompt },
|
|
@@ -379,59 +385,59 @@ function Je(n) {
|
|
|
379
385
|
},
|
|
380
386
|
stream: !0
|
|
381
387
|
});
|
|
382
|
-
let
|
|
388
|
+
let r, n;
|
|
383
389
|
for await (const a of e)
|
|
384
|
-
a.message.content && (yield { delta: a.message.content }), a.done && (
|
|
390
|
+
a.message.content && (yield { delta: a.message.content }), a.done && (r = a.prompt_eval_count, n = a.eval_count);
|
|
385
391
|
yield {
|
|
386
392
|
delta: "",
|
|
387
|
-
tokenUsage:
|
|
393
|
+
tokenUsage: r !== void 0 ? { input: r ?? 0, output: n ?? 0 } : void 0
|
|
388
394
|
};
|
|
389
395
|
}
|
|
390
396
|
};
|
|
391
397
|
}
|
|
392
|
-
const
|
|
393
|
-
anthropic:
|
|
394
|
-
openai:
|
|
395
|
-
google:
|
|
396
|
-
ollama:
|
|
398
|
+
const ie = {
|
|
399
|
+
anthropic: Ve,
|
|
400
|
+
openai: Xe,
|
|
401
|
+
google: Qe,
|
|
402
|
+
ollama: Ze
|
|
397
403
|
};
|
|
398
404
|
let T = null;
|
|
399
|
-
async function
|
|
405
|
+
async function qe() {
|
|
400
406
|
if (T) return T;
|
|
401
|
-
const
|
|
407
|
+
const o = d(v(), ".ai-council", "config.json");
|
|
402
408
|
try {
|
|
403
|
-
const
|
|
404
|
-
T = JSON.parse(
|
|
409
|
+
const s = await w(o, "utf-8");
|
|
410
|
+
T = JSON.parse(s);
|
|
405
411
|
} catch {
|
|
406
412
|
T = { backends: {} };
|
|
407
413
|
}
|
|
408
414
|
return T;
|
|
409
415
|
}
|
|
410
|
-
const
|
|
411
|
-
function
|
|
412
|
-
T = null,
|
|
416
|
+
const z = /* @__PURE__ */ new Map();
|
|
417
|
+
function et() {
|
|
418
|
+
T = null, z.clear();
|
|
413
419
|
}
|
|
414
|
-
async function W(
|
|
415
|
-
const
|
|
416
|
-
if (
|
|
417
|
-
const t =
|
|
420
|
+
async function W(o) {
|
|
421
|
+
const s = z.get(o);
|
|
422
|
+
if (s) return s;
|
|
423
|
+
const t = ie[o];
|
|
418
424
|
if (!t)
|
|
419
|
-
throw new Error(`Unknown backend: "${
|
|
420
|
-
const
|
|
421
|
-
return
|
|
425
|
+
throw new Error(`Unknown backend: "${o}". Available: ${Object.keys(ie).join(", ")}`);
|
|
426
|
+
const r = (await qe()).backends[o] ?? {}, n = t(r);
|
|
427
|
+
return z.set(o, n), n;
|
|
422
428
|
}
|
|
423
|
-
function
|
|
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":"
|
|
429
|
+
function tt() {
|
|
430
|
+
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":"Councilor 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":"Councilor 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();
|
|
425
431
|
}
|
|
426
|
-
const M = "---EXCALIDRAW---",
|
|
432
|
+
const M = "---EXCALIDRAW---", nt = `You are the Secretary of a council discussion. Your job is to synthesize a clear, structured summary of the conversation that just took place.
|
|
427
433
|
|
|
428
434
|
Structure your summary with these sections:
|
|
429
435
|
|
|
430
436
|
## Individual Positions
|
|
431
|
-
Briefly summarize each
|
|
437
|
+
Briefly summarize each councilor's key arguments and stance.
|
|
432
438
|
|
|
433
439
|
## Points of Convergence
|
|
434
|
-
Where did the
|
|
440
|
+
Where did the councilors agree? What common ground emerged?
|
|
435
441
|
|
|
436
442
|
## Points of Divergence
|
|
437
443
|
Where did they disagree? What are the key tensions?
|
|
@@ -440,29 +446,29 @@ Where did they disagree? What are the key tensions?
|
|
|
440
446
|
What are the most important takeaways? What would you recommend based on the full discussion?
|
|
441
447
|
|
|
442
448
|
Be concise but thorough. Use markdown formatting.`;
|
|
443
|
-
function
|
|
444
|
-
const
|
|
445
|
-
|
|
449
|
+
function ot(o) {
|
|
450
|
+
const s = [];
|
|
451
|
+
s.push(`Topic: ${o.topic}`), s.push(`Councilors: ${o.councilors.map((e) => e.name).join(", ")}`), s.push(`Rounds: ${o.rounds}`), s.push("");
|
|
446
452
|
let t = 0;
|
|
447
|
-
for (const e of
|
|
448
|
-
e.round !== t && (t = e.round,
|
|
449
|
-
return
|
|
453
|
+
for (const e of o.turns)
|
|
454
|
+
e.round !== t && (t = e.round, s.push(`--- Round ${t} ---`), s.push("")), s.push(`[${e.councilorName}]:`), s.push(e.content), s.push("");
|
|
455
|
+
return s.join(`
|
|
450
456
|
`);
|
|
451
457
|
}
|
|
452
|
-
async function
|
|
453
|
-
result:
|
|
454
|
-
config:
|
|
458
|
+
async function rt({
|
|
459
|
+
result: o,
|
|
460
|
+
config: s,
|
|
455
461
|
onChunk: t,
|
|
456
462
|
signal: e
|
|
457
463
|
}) {
|
|
458
|
-
const
|
|
459
|
-
if (!
|
|
464
|
+
const r = s.secretary;
|
|
465
|
+
if (!r?.backend)
|
|
460
466
|
throw new Error("No secretary backend configured");
|
|
461
|
-
const
|
|
467
|
+
const n = await W(r.backend), a = r.model ?? n.defaultModel, i = r.systemPrompt || nt, u = tt(), c = `${i}
|
|
462
468
|
|
|
463
|
-
${
|
|
469
|
+
${u}
|
|
464
470
|
|
|
465
|
-
After your text summary, output \`${M}\` on its own line, then a JSON array of Excalidraw elements showing a visual map of where each
|
|
471
|
+
After your text summary, output \`${M}\` on its own line, then a JSON array of Excalidraw elements showing a visual map of where each councilor stands on the topic. Use shapes for each councilor with their name, arrows to show relationships (agreement/disagreement), and position them to visually represent the discussion dynamics.`, m = ot(o), y = {
|
|
466
472
|
model: a,
|
|
467
473
|
systemPrompt: c,
|
|
468
474
|
messages: [{ role: "user", content: `Please summarize this council discussion and create a position diagram:
|
|
@@ -470,78 +476,78 @@ After your text summary, output \`${M}\` on its own line, then a JSON array of E
|
|
|
470
476
|
${m}` }],
|
|
471
477
|
temperature: 0.5
|
|
472
478
|
};
|
|
473
|
-
let
|
|
474
|
-
if (
|
|
475
|
-
for await (const
|
|
479
|
+
let h = "";
|
|
480
|
+
if (n.chatStream)
|
|
481
|
+
for await (const f of n.chatStream(y)) {
|
|
476
482
|
if (e?.aborted) break;
|
|
477
|
-
|
|
483
|
+
h += f.delta, f.delta && t && t(f.delta);
|
|
478
484
|
}
|
|
479
485
|
else
|
|
480
|
-
|
|
481
|
-
const
|
|
482
|
-
if (
|
|
483
|
-
return { text:
|
|
484
|
-
const
|
|
485
|
-
let
|
|
486
|
+
h = (await n.chat(y)).content, t && t(h);
|
|
487
|
+
const p = h.indexOf(M);
|
|
488
|
+
if (p === -1)
|
|
489
|
+
return { text: h.trim() };
|
|
490
|
+
const b = h.slice(0, p).trim(), l = h.slice(p + M.length).trim();
|
|
491
|
+
let g;
|
|
486
492
|
try {
|
|
487
|
-
const
|
|
488
|
-
|
|
493
|
+
const f = l.match(/\[[\s\S]*\]/);
|
|
494
|
+
f && (g = JSON.parse(f[0]));
|
|
489
495
|
} catch {
|
|
490
496
|
}
|
|
491
|
-
return { text:
|
|
497
|
+
return { text: b, diagram: g };
|
|
492
498
|
}
|
|
493
|
-
const
|
|
494
|
-
async function
|
|
495
|
-
result:
|
|
496
|
-
roundNumber:
|
|
499
|
+
const st = "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.";
|
|
500
|
+
async function at({
|
|
501
|
+
result: o,
|
|
502
|
+
roundNumber: s,
|
|
497
503
|
config: t,
|
|
498
504
|
onChunk: e,
|
|
499
|
-
signal:
|
|
505
|
+
signal: r
|
|
500
506
|
}) {
|
|
501
|
-
const
|
|
502
|
-
if (!
|
|
507
|
+
const n = t.secretary;
|
|
508
|
+
if (!n?.backend)
|
|
503
509
|
throw new Error("No secretary backend configured");
|
|
504
|
-
const a = await W(
|
|
505
|
-
c.push(`Topic: ${
|
|
506
|
-
for (const
|
|
507
|
-
c.push(`[${
|
|
510
|
+
const a = await W(n.backend), i = n.model ?? a.defaultModel, u = o.turns.filter((h) => h.round === s), c = [];
|
|
511
|
+
c.push(`Topic: ${o.topic}`), c.push(`Round ${s}${s === 1 ? " (Constructive)" : " (Rebuttal)"}`), c.push("");
|
|
512
|
+
for (const h of u)
|
|
513
|
+
c.push(`[${h.councilorName}]:`), c.push(h.content), c.push("");
|
|
508
514
|
const m = {
|
|
509
515
|
model: i,
|
|
510
|
-
systemPrompt:
|
|
516
|
+
systemPrompt: st,
|
|
511
517
|
messages: [{ role: "user", content: `Please summarize this round:
|
|
512
518
|
|
|
513
519
|
${c.join(`
|
|
514
520
|
`)}` }],
|
|
515
521
|
temperature: 0.5
|
|
516
522
|
};
|
|
517
|
-
let
|
|
523
|
+
let y = "";
|
|
518
524
|
if (a.chatStream)
|
|
519
|
-
for await (const
|
|
520
|
-
if (
|
|
521
|
-
|
|
525
|
+
for await (const h of a.chatStream(m)) {
|
|
526
|
+
if (r?.aborted) break;
|
|
527
|
+
y += h.delta, h.delta && e && e(h.delta);
|
|
522
528
|
}
|
|
523
529
|
else
|
|
524
|
-
|
|
525
|
-
return
|
|
530
|
+
y = (await a.chat(m)).content, e && e(y);
|
|
531
|
+
return y.trim();
|
|
526
532
|
}
|
|
527
|
-
async function
|
|
528
|
-
topic:
|
|
529
|
-
firstRoundTurns:
|
|
533
|
+
async function it({
|
|
534
|
+
topic: o,
|
|
535
|
+
firstRoundTurns: s,
|
|
530
536
|
config: t
|
|
531
537
|
}) {
|
|
532
538
|
const e = t.secretary;
|
|
533
539
|
if (!e?.backend)
|
|
534
540
|
throw new Error("No secretary backend configured");
|
|
535
|
-
const
|
|
541
|
+
const r = await W(e.backend), n = e.model ?? r.defaultModel, a = s.map((u) => `[${u.councilorName}]: ${u.content.slice(0, 300)}`).join(`
|
|
536
542
|
|
|
537
543
|
`);
|
|
538
|
-
return (await
|
|
539
|
-
model:
|
|
544
|
+
return (await r.chat({
|
|
545
|
+
model: n,
|
|
540
546
|
systemPrompt: "Generate a concise title (max 8 words) for this council discussion. Return only the title, no quotes or punctuation at the end.",
|
|
541
547
|
messages: [
|
|
542
548
|
{
|
|
543
549
|
role: "user",
|
|
544
|
-
content: `Topic: ${
|
|
550
|
+
content: `Topic: ${o}
|
|
545
551
|
|
|
546
552
|
First round:
|
|
547
553
|
${a}`
|
|
@@ -550,85 +556,96 @@ ${a}`
|
|
|
550
556
|
temperature: 0.3
|
|
551
557
|
})).content.trim().replace(/^["']+|["']+$/g, "").replace(/[.!?]+$/, "").trim();
|
|
552
558
|
}
|
|
553
|
-
const
|
|
554
|
-
let
|
|
555
|
-
async function
|
|
556
|
-
|
|
559
|
+
const ye = d(v(), ".ai-council"), ct = d(ye, "council.log");
|
|
560
|
+
let ce = !1;
|
|
561
|
+
async function lt() {
|
|
562
|
+
ce || (await N(ye, { recursive: !0 }), ce = !0);
|
|
557
563
|
}
|
|
558
|
-
function
|
|
559
|
-
let
|
|
564
|
+
function ut(o, s, t, e) {
|
|
565
|
+
let n = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${o} [${s}] ${t}`;
|
|
560
566
|
if (e !== void 0) {
|
|
561
567
|
const a = e instanceof Error ? `${e.message}
|
|
562
568
|
${e.stack ?? ""}` : typeof e == "string" ? e : JSON.stringify(e, null, 2);
|
|
563
|
-
|
|
569
|
+
n += `
|
|
564
570
|
${a.replace(/\n/g, `
|
|
565
571
|
`)}`;
|
|
566
572
|
}
|
|
567
|
-
return
|
|
573
|
+
return n + `
|
|
568
574
|
`;
|
|
569
575
|
}
|
|
570
|
-
async function Y(
|
|
576
|
+
async function Y(o, s, t, e) {
|
|
571
577
|
try {
|
|
572
|
-
await
|
|
578
|
+
await lt(), await xe(ct, ut(o, s, t, e));
|
|
573
579
|
} catch {
|
|
574
580
|
}
|
|
575
581
|
}
|
|
576
|
-
const
|
|
577
|
-
info: (
|
|
578
|
-
warn: (
|
|
579
|
-
error: (
|
|
582
|
+
const _ = {
|
|
583
|
+
info: (o, s, t) => Y("INFO", o, s, t),
|
|
584
|
+
warn: (o, s, t) => Y("WARN", o, s, t),
|
|
585
|
+
error: (o, s, t) => Y("ERROR", o, s, t)
|
|
580
586
|
};
|
|
581
|
-
function
|
|
582
|
-
const
|
|
583
|
-
|
|
584
|
-
|
|
587
|
+
function dt(o, s, t, e, r) {
|
|
588
|
+
const n = [{ role: "user", content: o }];
|
|
589
|
+
if (e?.length) {
|
|
590
|
+
for (const a of e)
|
|
591
|
+
a.councilorId === t ? n.push({ role: "assistant", content: a.content }) : n.push({
|
|
592
|
+
role: "user",
|
|
593
|
+
content: `[${a.councilorName}, Round ${a.round}]: ${a.content}`
|
|
594
|
+
});
|
|
595
|
+
r && n.push({
|
|
585
596
|
role: "user",
|
|
586
|
-
content: `[
|
|
597
|
+
content: `[Secretary Summary]: ${r}`
|
|
587
598
|
});
|
|
588
|
-
|
|
599
|
+
}
|
|
600
|
+
for (const a of s)
|
|
601
|
+
a.councilorId === t ? n.push({ role: "assistant", content: a.content }) : n.push({
|
|
602
|
+
role: "user",
|
|
603
|
+
content: `[${a.councilorName}, Round ${a.round}]: ${a.content}`
|
|
604
|
+
});
|
|
605
|
+
return n;
|
|
589
606
|
}
|
|
590
|
-
function
|
|
591
|
-
const
|
|
607
|
+
function mt(o, s, t, e) {
|
|
608
|
+
const r = [{ role: "user", content: o }];
|
|
592
609
|
if (e === 1)
|
|
593
|
-
return
|
|
594
|
-
const
|
|
595
|
-
for (const i of
|
|
596
|
-
i.
|
|
610
|
+
return r;
|
|
611
|
+
const n = s.filter((i) => i.round === 1);
|
|
612
|
+
for (const i of n)
|
|
613
|
+
i.councilorId === t ? r.push({ role: "assistant", content: i.content }) : r.push({
|
|
597
614
|
role: "user",
|
|
598
|
-
content: `[${i.
|
|
615
|
+
content: `[${i.councilorName}, Constructive]: ${i.content}`
|
|
599
616
|
});
|
|
600
617
|
const a = e - 1;
|
|
601
618
|
if (a > 1) {
|
|
602
|
-
const i =
|
|
603
|
-
for (const
|
|
604
|
-
|
|
619
|
+
const i = s.filter((u) => u.round === a);
|
|
620
|
+
for (const u of i)
|
|
621
|
+
u.councilorId === t ? r.push({ role: "assistant", content: u.content }) : r.push({
|
|
605
622
|
role: "user",
|
|
606
|
-
content: `[${
|
|
623
|
+
content: `[${u.councilorName}, Round ${a}]: ${u.content}`
|
|
607
624
|
});
|
|
608
625
|
}
|
|
609
|
-
for (const i of
|
|
610
|
-
i.
|
|
611
|
-
return
|
|
626
|
+
for (const i of s)
|
|
627
|
+
i.councilorId === t && i.round !== 1 && i.round !== a && i.round < e && r.push({ role: "assistant", content: i.content });
|
|
628
|
+
return r;
|
|
612
629
|
}
|
|
613
|
-
function
|
|
614
|
-
const t = [...
|
|
615
|
-
let e =
|
|
616
|
-
const
|
|
630
|
+
function pt(o, s) {
|
|
631
|
+
const t = [...o];
|
|
632
|
+
let e = s | 0;
|
|
633
|
+
const r = () => {
|
|
617
634
|
e = e + 1831565813 | 0;
|
|
618
|
-
let
|
|
619
|
-
return
|
|
635
|
+
let n = Math.imul(e ^ e >>> 15, 1 | e);
|
|
636
|
+
return n = n + Math.imul(n ^ n >>> 7, 61 | n) ^ n, ((n ^ n >>> 14) >>> 0) / 4294967296;
|
|
620
637
|
};
|
|
621
|
-
for (let
|
|
622
|
-
const a = Math.floor(
|
|
623
|
-
[t[
|
|
638
|
+
for (let n = t.length - 1; n > 0; n--) {
|
|
639
|
+
const a = Math.floor(r() * (n + 1));
|
|
640
|
+
[t[n], t[a]] = [t[a], t[n]];
|
|
624
641
|
}
|
|
625
642
|
return t;
|
|
626
643
|
}
|
|
627
|
-
function H(
|
|
644
|
+
function H(o, s, t, e, r, n) {
|
|
628
645
|
return {
|
|
629
|
-
topic:
|
|
630
|
-
topicSource:
|
|
631
|
-
|
|
646
|
+
topic: o.topic,
|
|
647
|
+
topicSource: o.topicSource,
|
|
648
|
+
councilors: o.councilors.map((a) => ({
|
|
632
649
|
id: a.id,
|
|
633
650
|
name: a.frontmatter.name,
|
|
634
651
|
description: a.frontmatter.description,
|
|
@@ -636,178 +653,179 @@ function H(n, r, t, e, o, s) {
|
|
|
636
653
|
model: a.frontmatter.model ?? "default",
|
|
637
654
|
avatarUrl: a.avatarUrl
|
|
638
655
|
})),
|
|
639
|
-
rounds:
|
|
640
|
-
turns:
|
|
656
|
+
rounds: o.rounds,
|
|
657
|
+
turns: s,
|
|
641
658
|
startedAt: t,
|
|
642
659
|
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
643
|
-
totalTokenUsage: { input: e, output:
|
|
644
|
-
...
|
|
645
|
-
...
|
|
660
|
+
totalTokenUsage: { input: e, output: r },
|
|
661
|
+
...n && Object.keys(n).length > 0 ? { roundSummaries: n } : {},
|
|
662
|
+
...o.mode === "debate" ? { mode: "debate" } : {}
|
|
646
663
|
};
|
|
647
664
|
}
|
|
648
|
-
async function
|
|
649
|
-
let
|
|
650
|
-
typeof
|
|
651
|
-
topic:
|
|
652
|
-
topicSource:
|
|
653
|
-
|
|
665
|
+
async function ft(o, s, t, e, r) {
|
|
666
|
+
let n;
|
|
667
|
+
typeof o == "string" ? n = {
|
|
668
|
+
topic: o,
|
|
669
|
+
topicSource: s,
|
|
670
|
+
councilors: t,
|
|
654
671
|
rounds: e,
|
|
655
|
-
onEvent:
|
|
656
|
-
} :
|
|
672
|
+
onEvent: r
|
|
673
|
+
} : n = o;
|
|
657
674
|
const a = (/* @__PURE__ */ new Date()).toISOString(), i = [];
|
|
658
|
-
let
|
|
659
|
-
const m =
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
topic:
|
|
675
|
+
let u = 0, c = 0;
|
|
676
|
+
const m = n.mode === "debate", y = {}, h = n.previousTurns?.length ? Math.max(...n.previousTurns.map((p) => p.round)) : 0;
|
|
677
|
+
_.info("conversation", `Starting ${m ? "debate" : "freeform"} — ${n.councilors.length} councilors, ${n.rounds} rounds`, {
|
|
678
|
+
councilors: n.councilors.map((p) => `${p.frontmatter.name} (${p.frontmatter.backend}/${p.frontmatter.model ?? "default"})`),
|
|
679
|
+
topic: n.topic.slice(0, 200)
|
|
663
680
|
});
|
|
664
|
-
for (let p = 1; p <=
|
|
665
|
-
const
|
|
666
|
-
for (const
|
|
667
|
-
if (
|
|
668
|
-
return H(
|
|
669
|
-
if (
|
|
670
|
-
const
|
|
671
|
-
|
|
681
|
+
for (let p = 1; p <= n.rounds; p++) {
|
|
682
|
+
const b = m && p > 1 ? pt(n.councilors, p) : n.councilors;
|
|
683
|
+
for (const l of b) {
|
|
684
|
+
if (n.signal?.aborted)
|
|
685
|
+
return H(n, i, a, u, c, y);
|
|
686
|
+
if (n.beforeTurn) {
|
|
687
|
+
const f = await n.beforeTurn();
|
|
688
|
+
f && (i.push(f), n.onEvent({ type: "turn_complete", turn: f }));
|
|
672
689
|
}
|
|
673
|
-
|
|
690
|
+
const g = p + h;
|
|
691
|
+
n.onEvent({ type: "turn_start", round: g, councilorName: l.frontmatter.name });
|
|
674
692
|
try {
|
|
675
|
-
const
|
|
676
|
-
model:
|
|
677
|
-
systemPrompt:
|
|
678
|
-
messages:
|
|
679
|
-
temperature:
|
|
693
|
+
const f = await W(l.frontmatter.backend), D = l.frontmatter.model ?? f.defaultModel, _e = m ? mt(n.topic, i, l.id, p) : dt(n.topic, i, l.id, n.previousTurns, n.previousSummary), q = {
|
|
694
|
+
model: D,
|
|
695
|
+
systemPrompt: l.systemPrompt,
|
|
696
|
+
messages: _e,
|
|
697
|
+
temperature: l.frontmatter.temperature
|
|
680
698
|
};
|
|
681
|
-
let
|
|
682
|
-
if (
|
|
683
|
-
|
|
684
|
-
for await (const
|
|
685
|
-
if (
|
|
686
|
-
|
|
699
|
+
let K, x;
|
|
700
|
+
if (f.chatStream) {
|
|
701
|
+
K = "";
|
|
702
|
+
for await (const O of f.chatStream(q)) {
|
|
703
|
+
if (n.signal?.aborted) break;
|
|
704
|
+
K += O.delta, O.delta && n.onEvent({ type: "turn_chunk", councilorName: l.frontmatter.name, delta: O.delta }), O.tokenUsage && (x = O.tokenUsage);
|
|
687
705
|
}
|
|
688
706
|
} else {
|
|
689
|
-
const
|
|
690
|
-
|
|
707
|
+
const O = await f.chat(q);
|
|
708
|
+
K = O.content, x = O.tokenUsage;
|
|
691
709
|
}
|
|
692
|
-
const
|
|
693
|
-
round:
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
content:
|
|
710
|
+
const ee = {
|
|
711
|
+
round: g,
|
|
712
|
+
councilorId: l.id,
|
|
713
|
+
councilorName: l.frontmatter.name,
|
|
714
|
+
content: K,
|
|
697
715
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
698
|
-
model:
|
|
699
|
-
backend:
|
|
716
|
+
model: D,
|
|
717
|
+
backend: l.frontmatter.backend,
|
|
700
718
|
tokenUsage: x,
|
|
701
|
-
avatarUrl:
|
|
719
|
+
avatarUrl: l.avatarUrl
|
|
702
720
|
};
|
|
703
|
-
x && (
|
|
704
|
-
} catch (
|
|
705
|
-
const
|
|
706
|
-
|
|
721
|
+
x && (u += x.input, c += x.output), i.push(ee), n.onEvent({ type: "turn_complete", turn: ee });
|
|
722
|
+
} catch (f) {
|
|
723
|
+
const D = f instanceof Error ? f.message : String(f);
|
|
724
|
+
_.error("conversation", `Turn failed for ${l.frontmatter.name} (round ${p}, model ${l.frontmatter.model ?? "default"}, backend ${l.frontmatter.backend})`, f), n.onEvent({ type: "error", councilorName: l.frontmatter.name, error: D });
|
|
707
725
|
}
|
|
708
726
|
}
|
|
709
|
-
if (
|
|
727
|
+
if (n.onEvent({ type: "round_complete", round: p }), m && n.config?.secretary?.backend && !n.signal?.aborted)
|
|
710
728
|
try {
|
|
711
|
-
const
|
|
712
|
-
|
|
713
|
-
const
|
|
714
|
-
result:
|
|
729
|
+
const l = H(n, i, a, u, c, y);
|
|
730
|
+
n.onEvent({ type: "round_summary_start", round: p });
|
|
731
|
+
const g = await at({
|
|
732
|
+
result: l,
|
|
715
733
|
roundNumber: p,
|
|
716
|
-
config:
|
|
717
|
-
onChunk: (
|
|
718
|
-
|
|
734
|
+
config: n.config,
|
|
735
|
+
onChunk: (f) => {
|
|
736
|
+
n.onEvent({ type: "round_summary_chunk", round: p, delta: f });
|
|
719
737
|
},
|
|
720
|
-
signal:
|
|
738
|
+
signal: n.signal
|
|
721
739
|
});
|
|
722
|
-
|
|
723
|
-
} catch (
|
|
724
|
-
|
|
740
|
+
y[p] = g, n.onEvent({ type: "round_summary_complete", round: p, summary: g });
|
|
741
|
+
} catch (l) {
|
|
742
|
+
_.error("conversation", `Interim summary failed for round ${p}`, l);
|
|
725
743
|
}
|
|
726
744
|
}
|
|
727
|
-
return H(
|
|
745
|
+
return H(n, i, a, u, c, y);
|
|
728
746
|
}
|
|
729
|
-
const I =
|
|
730
|
-
function
|
|
731
|
-
return
|
|
747
|
+
const I = d(v(), ".ai-council", "history");
|
|
748
|
+
function ht(o) {
|
|
749
|
+
return o.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
732
750
|
}
|
|
733
|
-
async function
|
|
751
|
+
async function gt(o) {
|
|
734
752
|
await N(I, { recursive: !0 });
|
|
735
|
-
const
|
|
736
|
-
return await
|
|
753
|
+
const s = new Date(o.startedAt).toISOString().replace(/[:.]/g, "-").slice(0, 19), t = ht(o.topic), e = `${s}-${t}`, r = d(I, `${e}.json`);
|
|
754
|
+
return await P(r, JSON.stringify(o, null, 2), "utf-8"), e;
|
|
737
755
|
}
|
|
738
|
-
async function
|
|
756
|
+
async function yt() {
|
|
739
757
|
await N(I, { recursive: !0 });
|
|
740
|
-
const
|
|
741
|
-
for (const t of
|
|
758
|
+
const o = await V(I), s = [];
|
|
759
|
+
for (const t of o)
|
|
742
760
|
if (t.endsWith(".json"))
|
|
743
761
|
try {
|
|
744
|
-
const e = await
|
|
745
|
-
|
|
762
|
+
const e = await w(d(I, t), "utf-8"), r = JSON.parse(e);
|
|
763
|
+
s.push({
|
|
746
764
|
id: C(t, ".json"),
|
|
747
|
-
topic:
|
|
748
|
-
title:
|
|
749
|
-
|
|
750
|
-
rounds:
|
|
751
|
-
startedAt:
|
|
752
|
-
completedAt:
|
|
765
|
+
topic: r.topic,
|
|
766
|
+
title: r.title,
|
|
767
|
+
councilors: r.councilors.map((n) => n.name),
|
|
768
|
+
rounds: r.rounds,
|
|
769
|
+
startedAt: r.startedAt,
|
|
770
|
+
completedAt: r.completedAt
|
|
753
771
|
});
|
|
754
772
|
} catch {
|
|
755
773
|
}
|
|
756
|
-
return
|
|
774
|
+
return s.sort((t, e) => e.startedAt.localeCompare(t.startedAt));
|
|
757
775
|
}
|
|
758
|
-
async function
|
|
759
|
-
const
|
|
776
|
+
async function le(o) {
|
|
777
|
+
const s = d(I, `${o}.json`), t = await w(s, "utf-8"), e = JSON.parse(t);
|
|
760
778
|
return e.infographic && !e.infographics && (e.infographics = [e.infographic], delete e.infographic), e;
|
|
761
779
|
}
|
|
762
|
-
async function
|
|
763
|
-
const
|
|
764
|
-
await F(
|
|
780
|
+
async function wt(o) {
|
|
781
|
+
const s = d(I, `${o}.json`);
|
|
782
|
+
await F(s);
|
|
765
783
|
}
|
|
766
|
-
async function
|
|
767
|
-
const t =
|
|
768
|
-
|
|
784
|
+
async function bt(o, s) {
|
|
785
|
+
const t = d(I, `${o}.json`), e = await w(t, "utf-8"), r = JSON.parse(e);
|
|
786
|
+
r.infographics || (r.infographics = []), r.infographics.push(s), await P(t, JSON.stringify(r, null, 2), "utf-8");
|
|
769
787
|
}
|
|
770
|
-
async function
|
|
771
|
-
const t =
|
|
772
|
-
|
|
788
|
+
async function vt(o, s) {
|
|
789
|
+
const t = d(I, `${o}.json`), e = await w(t, "utf-8"), r = JSON.parse(e);
|
|
790
|
+
r.infographics && s >= 0 && s < r.infographics.length && r.infographics.splice(s, 1), await P(t, JSON.stringify(r, null, 2), "utf-8");
|
|
773
791
|
}
|
|
774
|
-
const
|
|
775
|
-
function
|
|
776
|
-
const
|
|
792
|
+
const we = De(import.meta.url);
|
|
793
|
+
function kt(o) {
|
|
794
|
+
const s = o.councilors.map((e) => e.name).join(", "), t = o.summary ?? o.turns.map((e) => `${e.councilorName}: ${e.content.slice(0, 200)}`).join(`
|
|
777
795
|
`);
|
|
778
796
|
return [
|
|
779
797
|
"Create a professional infographic summarizing a panel discussion.",
|
|
780
|
-
`Topic: ${
|
|
798
|
+
`Topic: ${o.topic.slice(0, 300)}`,
|
|
781
799
|
`Key points: ${t.slice(0, 1500)}`,
|
|
782
|
-
`Panelists: ${
|
|
800
|
+
`Panelists: ${s}`,
|
|
783
801
|
"Use a clean, modern design with sections for convergence points, divergence points, and key takeaways.",
|
|
784
802
|
"Include relevant icons and visual hierarchy. Use a horizontal landscape layout."
|
|
785
803
|
].join(" ");
|
|
786
804
|
}
|
|
787
|
-
function
|
|
788
|
-
if (
|
|
789
|
-
const
|
|
790
|
-
return
|
|
805
|
+
function _t(o) {
|
|
806
|
+
if (o.infographic?.backend) return o.infographic.backend;
|
|
807
|
+
const s = !!(o.backends.google?.apiKey || process.env.GOOGLE_API_KEY), t = !!(o.backends.openai?.apiKey || process.env.OPENAI_API_KEY);
|
|
808
|
+
return s ? "google" : t ? "openai" : null;
|
|
791
809
|
}
|
|
792
|
-
async function
|
|
793
|
-
const t =
|
|
794
|
-
apiKey:
|
|
795
|
-
...
|
|
810
|
+
async function Et(o, s) {
|
|
811
|
+
const t = we("openai").default, n = (await new t({
|
|
812
|
+
apiKey: s.backends.openai?.apiKey || process.env.OPENAI_API_KEY,
|
|
813
|
+
...s.backends.openai?.baseUrl ? { baseURL: s.backends.openai.baseUrl } : {}
|
|
796
814
|
}).images.generate({
|
|
797
815
|
model: "gpt-image-1",
|
|
798
|
-
prompt:
|
|
816
|
+
prompt: o,
|
|
799
817
|
quality: "high",
|
|
800
818
|
size: "1536x1024"
|
|
801
819
|
})).data?.[0]?.b64_json;
|
|
802
|
-
if (!
|
|
803
|
-
return
|
|
820
|
+
if (!n) throw new Error("No image data returned from OpenAI");
|
|
821
|
+
return n;
|
|
804
822
|
}
|
|
805
|
-
async function
|
|
806
|
-
const { GoogleGenAI: t } =
|
|
823
|
+
async function St(o, s) {
|
|
824
|
+
const { GoogleGenAI: t } = we("@google/genai"), e = s.backends.google?.apiKey || process.env.GOOGLE_API_KEY;
|
|
807
825
|
if (!e) throw new Error("No Google API key configured");
|
|
808
826
|
const a = (await new t({ apiKey: e }).models.generateContent({
|
|
809
827
|
model: "gemini-3-pro-image-preview",
|
|
810
|
-
contents:
|
|
828
|
+
contents: o,
|
|
811
829
|
config: {
|
|
812
830
|
responseModalities: ["IMAGE", "TEXT"]
|
|
813
831
|
}
|
|
@@ -818,24 +836,24 @@ async function bt(n, r) {
|
|
|
818
836
|
return i.inlineData.data;
|
|
819
837
|
throw new Error("No image data in Gemini response");
|
|
820
838
|
}
|
|
821
|
-
async function
|
|
822
|
-
const e = t ??
|
|
839
|
+
async function ue(o, s, t) {
|
|
840
|
+
const e = t ?? _t(s);
|
|
823
841
|
if (!e) throw new Error("No image-capable backend configured (need OpenAI or Google API key)");
|
|
824
|
-
const
|
|
825
|
-
return e === "openai" ?
|
|
842
|
+
const r = kt(o);
|
|
843
|
+
return e === "openai" ? Et(r, s) : St(r, s);
|
|
826
844
|
}
|
|
827
|
-
const
|
|
845
|
+
const be = {
|
|
828
846
|
anthropic: "https://api.anthropic.com",
|
|
829
847
|
openai: "https://api.openai.com/v1",
|
|
830
848
|
google: "https://generativelanguage.googleapis.com",
|
|
831
849
|
ollama: "http://localhost:11434"
|
|
832
|
-
},
|
|
850
|
+
}, de = [
|
|
833
851
|
"claude-opus-4-20250514",
|
|
834
852
|
"claude-sonnet-4-5-20250514",
|
|
835
853
|
"claude-sonnet-4-20250514",
|
|
836
854
|
"claude-haiku-4-20250414",
|
|
837
855
|
"claude-3-5-haiku-20241022"
|
|
838
|
-
],
|
|
856
|
+
], j = [
|
|
839
857
|
"gemini-2.5-pro",
|
|
840
858
|
"gemini-2.5-flash",
|
|
841
859
|
"gemini-2.0-flash",
|
|
@@ -843,67 +861,67 @@ const we = {
|
|
|
843
861
|
"gemini-1.5-pro",
|
|
844
862
|
"gemini-1.5-flash"
|
|
845
863
|
];
|
|
846
|
-
async function
|
|
864
|
+
async function At(o, s) {
|
|
847
865
|
try {
|
|
848
|
-
switch (
|
|
866
|
+
switch (o) {
|
|
849
867
|
case "ollama": {
|
|
850
868
|
const { Ollama: t } = await import("ollama");
|
|
851
|
-
return { connected: !0, models: (await new t({ host:
|
|
869
|
+
return { connected: !0, models: (await new t({ host: s.baseUrl || be.ollama }).list()).models.map((a) => a.name).sort() };
|
|
852
870
|
}
|
|
853
871
|
case "openai": {
|
|
854
872
|
const { default: t } = await import("openai");
|
|
855
873
|
return { connected: !0, models: (await new t({
|
|
856
|
-
apiKey:
|
|
857
|
-
...
|
|
874
|
+
apiKey: s.apiKey || process.env.OPENAI_API_KEY,
|
|
875
|
+
...s.baseUrl ? { baseURL: s.baseUrl } : {}
|
|
858
876
|
}).models.list()).data.map((a) => a.id).filter((a) => a.startsWith("gpt-") || a.startsWith("o") || a.startsWith("chatgpt-")).sort() };
|
|
859
877
|
}
|
|
860
878
|
case "anthropic": {
|
|
861
879
|
const { default: t } = await import("@anthropic-ai/sdk"), e = new t({
|
|
862
|
-
apiKey:
|
|
863
|
-
...
|
|
880
|
+
apiKey: s.apiKey || process.env.ANTHROPIC_API_KEY,
|
|
881
|
+
...s.baseUrl ? { baseURL: s.baseUrl } : {}
|
|
864
882
|
});
|
|
865
883
|
try {
|
|
866
884
|
return { connected: !0, models: (await e.models.list({ limit: 100 })).data.map((a) => a.id).sort() };
|
|
867
885
|
} catch {
|
|
868
|
-
return { connected: !0, models:
|
|
886
|
+
return { connected: !0, models: de };
|
|
869
887
|
}
|
|
870
888
|
}
|
|
871
889
|
case "google": {
|
|
872
|
-
const t =
|
|
873
|
-
if (!t) return { connected: !1, models:
|
|
890
|
+
const t = s.apiKey || process.env.GOOGLE_API_KEY || "";
|
|
891
|
+
if (!t) return { connected: !1, models: j, error: "No API key" };
|
|
874
892
|
const e = await fetch(
|
|
875
893
|
`https://generativelanguage.googleapis.com/v1beta/models?key=${t}`
|
|
876
894
|
);
|
|
877
895
|
if (!e.ok) {
|
|
878
896
|
const i = (await e.json().catch(() => ({})))?.error?.message || `HTTP ${e.status}`;
|
|
879
|
-
return { connected: !1, models:
|
|
897
|
+
return { connected: !1, models: j, error: i };
|
|
880
898
|
}
|
|
881
|
-
const
|
|
882
|
-
return { connected: !0, models:
|
|
899
|
+
const n = ((await e.json()).models || []).filter((a) => a.name.includes("gemini") && a.supportedGenerationMethods?.includes("generateContent")).map((a) => a.name.replace("models/", "")).sort();
|
|
900
|
+
return { connected: !0, models: n.length > 0 ? n : j };
|
|
883
901
|
}
|
|
884
902
|
default:
|
|
885
|
-
return { connected: !1, models: [], error: `Unknown backend: ${
|
|
903
|
+
return { connected: !1, models: [], error: `Unknown backend: ${o}` };
|
|
886
904
|
}
|
|
887
905
|
} catch (t) {
|
|
888
906
|
const e = t instanceof Error ? t.message : String(t);
|
|
889
|
-
return { connected: !1, models:
|
|
907
|
+
return { connected: !1, models: o === "anthropic" ? de : o === "google" ? j : [], error: e };
|
|
890
908
|
}
|
|
891
909
|
}
|
|
892
|
-
let A = null,
|
|
893
|
-
function
|
|
894
|
-
|
|
895
|
-
const t = process.env.COUNCIL_CWD ||
|
|
910
|
+
let A = null, B = [];
|
|
911
|
+
function It(o, s) {
|
|
912
|
+
o.handle("app:getCouncilDir", async () => {
|
|
913
|
+
const t = process.env.COUNCIL_CWD || process.cwd();
|
|
896
914
|
return G(t, "council");
|
|
897
|
-
}),
|
|
898
|
-
const
|
|
899
|
-
let
|
|
915
|
+
}), o.handle("councilors:list", async (t, e) => {
|
|
916
|
+
const r = d(v(), ".ai-council", "config.json");
|
|
917
|
+
let n = { backends: {} };
|
|
900
918
|
try {
|
|
901
|
-
const c = await
|
|
902
|
-
|
|
919
|
+
const c = await w(r, "utf-8");
|
|
920
|
+
n = JSON.parse(c);
|
|
903
921
|
} catch {
|
|
904
922
|
}
|
|
905
|
-
const a =
|
|
906
|
-
return (await
|
|
923
|
+
const a = ae(n), i = n.councilors ?? {};
|
|
924
|
+
return (await re(e, a)).map((c) => {
|
|
907
925
|
const m = i[c.id];
|
|
908
926
|
return {
|
|
909
927
|
id: c.id,
|
|
@@ -919,144 +937,148 @@ function vt(n, r) {
|
|
|
919
937
|
registryUrl: m?.url
|
|
920
938
|
};
|
|
921
939
|
});
|
|
922
|
-
}),
|
|
923
|
-
const
|
|
924
|
-
return { frontmatter: a, body: i.trim(), raw:
|
|
925
|
-
}),
|
|
926
|
-
const
|
|
927
|
-
return await
|
|
928
|
-
}),
|
|
929
|
-
const a =
|
|
930
|
-
return await N(a, { recursive: !0 }), await
|
|
931
|
-
}),
|
|
932
|
-
const t =
|
|
940
|
+
}), o.handle("councilors:get", async (t, e) => {
|
|
941
|
+
const r = d(e, "ABOUT.md"), n = await w(r, "utf-8"), { data: a, content: i } = X(n);
|
|
942
|
+
return { frontmatter: a, body: i.trim(), raw: n };
|
|
943
|
+
}), o.handle("councilors:save", async (t, e, r) => {
|
|
944
|
+
const n = d(e, "ABOUT.md");
|
|
945
|
+
return await P(n, r, "utf-8"), { success: !0 };
|
|
946
|
+
}), o.handle("councilors:create", async (t, e, r, n) => {
|
|
947
|
+
const a = d(e, r);
|
|
948
|
+
return await N(a, { recursive: !0 }), await P(d(a, "ABOUT.md"), n, "utf-8"), { success: !0, dirPath: a };
|
|
949
|
+
}), o.handle("councilors:delete", async (t, e) => (await F(e, { recursive: !0, force: !0 }), { success: !0 })), o.handle("config:get", async () => {
|
|
950
|
+
const t = d(v(), ".ai-council", "config.json");
|
|
933
951
|
let e = { backends: {} };
|
|
934
952
|
try {
|
|
935
|
-
const i = await
|
|
953
|
+
const i = await w(t, "utf-8");
|
|
936
954
|
e = JSON.parse(i);
|
|
937
955
|
} catch {
|
|
938
956
|
}
|
|
939
|
-
const
|
|
957
|
+
const r = {
|
|
940
958
|
ANTHROPIC_API_KEY: !!process.env.ANTHROPIC_API_KEY,
|
|
941
959
|
OPENAI_API_KEY: !!process.env.OPENAI_API_KEY,
|
|
942
960
|
GOOGLE_API_KEY: !!process.env.GOOGLE_API_KEY
|
|
943
|
-
},
|
|
944
|
-
ANTHROPIC_API_KEY:
|
|
945
|
-
OPENAI_API_KEY:
|
|
946
|
-
GOOGLE_API_KEY:
|
|
961
|
+
}, n = (i) => i ? "..." + i.slice(-4) : void 0, a = {
|
|
962
|
+
ANTHROPIC_API_KEY: n(process.env.ANTHROPIC_API_KEY),
|
|
963
|
+
OPENAI_API_KEY: n(process.env.OPENAI_API_KEY),
|
|
964
|
+
GOOGLE_API_KEY: n(process.env.GOOGLE_API_KEY)
|
|
947
965
|
};
|
|
948
|
-
return { config: e, envStatus:
|
|
949
|
-
}),
|
|
950
|
-
const
|
|
951
|
-
return await N(
|
|
952
|
-
}),
|
|
953
|
-
const
|
|
954
|
-
if (!
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
let a = { backends: {} };
|
|
958
|
-
try {
|
|
959
|
-
const f = await y(s, "utf-8");
|
|
960
|
-
a = JSON.parse(f);
|
|
961
|
-
} catch {
|
|
962
|
-
}
|
|
963
|
-
const i = re(a), l = await oe(e.councilDir, i), c = e.counsellorIds?.length ? l.filter((f) => e.counsellorIds.includes(f.id)) : l;
|
|
964
|
-
if (c.length === 0) {
|
|
965
|
-
o.webContents.send("discussion:event", { type: "error", counsellorName: "", error: "No counsellors found" });
|
|
966
|
-
return;
|
|
967
|
-
}
|
|
968
|
-
const m = (f) => {
|
|
969
|
-
o.isDestroyed() || o.webContents.send("discussion:event", f);
|
|
970
|
-
}, g = async () => j.length === 0 ? null : {
|
|
971
|
-
round: 0,
|
|
972
|
-
counsellorId: "__user__",
|
|
973
|
-
counsellorName: "You",
|
|
974
|
-
content: j.shift(),
|
|
975
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
976
|
-
model: "human",
|
|
977
|
-
backend: "human"
|
|
978
|
-
}, p = {
|
|
979
|
-
topic: e.topic,
|
|
980
|
-
topicSource: e.topicSource,
|
|
981
|
-
counsellors: c,
|
|
982
|
-
rounds: e.rounds,
|
|
983
|
-
onEvent: m,
|
|
984
|
-
beforeTurn: g,
|
|
985
|
-
signal: A.signal,
|
|
986
|
-
mode: e.mode,
|
|
987
|
-
config: a
|
|
966
|
+
return { config: e, envStatus: r, envKeySuffix: a, defaultUrls: be };
|
|
967
|
+
}), o.handle("backend:probe", async (t, e, r) => At(e, r)), o.handle("config:save", async (t, e) => {
|
|
968
|
+
const r = d(v(), ".ai-council");
|
|
969
|
+
return await N(r, { recursive: !0 }), await P(d(r, "config.json"), JSON.stringify(e, null, 2), "utf-8"), et(), { success: !0 };
|
|
970
|
+
}), o.handle("discussion:start", async (t, e) => {
|
|
971
|
+
const r = s();
|
|
972
|
+
if (!r) return { error: "No window" };
|
|
973
|
+
const n = (a) => {
|
|
974
|
+
r.isDestroyed() || r.webContents.send("discussion:event", a);
|
|
988
975
|
};
|
|
989
976
|
try {
|
|
990
|
-
|
|
991
|
-
|
|
977
|
+
A && A.abort(), A = new AbortController(), B = [], _.info("ipc:discussion", "Starting discussion", { councilDir: e.councilDir, councilorIds: e.councilorIds, rounds: e.rounds, mode: e.mode });
|
|
978
|
+
const a = d(v(), ".ai-council", "config.json");
|
|
979
|
+
let i = { backends: {} };
|
|
980
|
+
try {
|
|
981
|
+
const l = await w(a, "utf-8");
|
|
982
|
+
i = JSON.parse(l);
|
|
983
|
+
} catch {
|
|
984
|
+
}
|
|
985
|
+
const u = ae(i);
|
|
986
|
+
_.info("ipc:discussion", `Loading councilors from ${e.councilDir} + ${u.length} registered paths`);
|
|
987
|
+
const c = await re(e.councilDir, u), m = e.councilorIds?.length ? c.filter((l) => e.councilorIds.includes(l.id)) : c;
|
|
988
|
+
if (_.info("ipc:discussion", `Resolved ${m.length} councilors: ${m.map((l) => l.id).join(", ")}`), m.length === 0) {
|
|
989
|
+
n({ type: "error", councilorName: "", error: "No councilors found" });
|
|
990
|
+
return;
|
|
991
|
+
}
|
|
992
|
+
const y = async () => B.length === 0 ? null : {
|
|
993
|
+
round: 0,
|
|
994
|
+
councilorId: "__user__",
|
|
995
|
+
councilorName: "You",
|
|
996
|
+
content: B.shift(),
|
|
997
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
998
|
+
model: "human",
|
|
999
|
+
backend: "human"
|
|
1000
|
+
}, h = !!e.previousTurns?.length, p = {
|
|
1001
|
+
topic: e.topic,
|
|
1002
|
+
topicSource: e.topicSource,
|
|
1003
|
+
councilors: m,
|
|
1004
|
+
rounds: e.rounds,
|
|
1005
|
+
onEvent: n,
|
|
1006
|
+
beforeTurn: y,
|
|
1007
|
+
signal: A.signal,
|
|
1008
|
+
mode: h ? "freeform" : e.mode,
|
|
1009
|
+
config: i,
|
|
1010
|
+
previousTurns: e.previousTurns,
|
|
1011
|
+
previousSummary: e.previousSummary
|
|
1012
|
+
}, b = await ft(p);
|
|
1013
|
+
if (i.secretary?.backend)
|
|
992
1014
|
try {
|
|
993
|
-
|
|
994
|
-
const
|
|
995
|
-
result:
|
|
996
|
-
config:
|
|
997
|
-
onChunk: (
|
|
998
|
-
|
|
1015
|
+
n({ type: "summary_start" });
|
|
1016
|
+
const l = await rt({
|
|
1017
|
+
result: b,
|
|
1018
|
+
config: i,
|
|
1019
|
+
onChunk: (g) => {
|
|
1020
|
+
n({ type: "summary_chunk", delta: g });
|
|
999
1021
|
},
|
|
1000
1022
|
signal: A?.signal
|
|
1001
1023
|
});
|
|
1002
|
-
|
|
1003
|
-
} catch (
|
|
1004
|
-
|
|
1024
|
+
b.summary = l.text, l.diagram && (b.diagram = l.diagram), n({ type: "summary_complete", summary: l.text, diagram: l.diagram });
|
|
1025
|
+
} catch (l) {
|
|
1026
|
+
_.error("ipc:discussion", "Secretary summary failed", l), n({ type: "error", councilorName: "Secretary", error: l instanceof Error ? l.message : String(l) });
|
|
1005
1027
|
}
|
|
1006
|
-
if (
|
|
1028
|
+
if (i.secretary?.backend)
|
|
1007
1029
|
try {
|
|
1008
|
-
const
|
|
1009
|
-
topic:
|
|
1010
|
-
firstRoundTurns:
|
|
1011
|
-
config:
|
|
1030
|
+
const l = b.turns.filter((f) => f.round === 1), g = await it({
|
|
1031
|
+
topic: b.topic,
|
|
1032
|
+
firstRoundTurns: l,
|
|
1033
|
+
config: i
|
|
1012
1034
|
});
|
|
1013
|
-
|
|
1014
|
-
} catch (
|
|
1015
|
-
|
|
1035
|
+
b.title = g, n({ type: "title_generated", title: g });
|
|
1036
|
+
} catch (l) {
|
|
1037
|
+
_.error("ipc:discussion", "Title generation failed", l);
|
|
1016
1038
|
}
|
|
1017
1039
|
if (e.infographicBackends?.length)
|
|
1018
|
-
for (const
|
|
1040
|
+
for (const l of e.infographicBackends)
|
|
1019
1041
|
try {
|
|
1020
|
-
|
|
1021
|
-
const
|
|
1022
|
-
|
|
1023
|
-
} catch (
|
|
1024
|
-
|
|
1042
|
+
n({ type: "infographic_start" });
|
|
1043
|
+
const g = await ue(b, i, l);
|
|
1044
|
+
b.infographics || (b.infographics = []), b.infographics.push(g), n({ type: "infographic_complete", infographic: g });
|
|
1045
|
+
} catch (g) {
|
|
1046
|
+
_.error("ipc:discussion", `Infographic generation failed (${l})`, g), n({ type: "infographic_error", error: g instanceof Error ? g.message : String(g) });
|
|
1025
1047
|
}
|
|
1026
|
-
|
|
1048
|
+
e.continuedFrom && (b.continuedFrom = e.continuedFrom), n({ type: "complete", result: b });
|
|
1027
1049
|
try {
|
|
1028
|
-
await
|
|
1029
|
-
} catch (
|
|
1030
|
-
|
|
1050
|
+
await gt(b);
|
|
1051
|
+
} catch (l) {
|
|
1052
|
+
_.error("ipc:discussion", "Failed to save to history", l);
|
|
1031
1053
|
}
|
|
1032
|
-
} catch (
|
|
1033
|
-
|
|
1054
|
+
} catch (a) {
|
|
1055
|
+
_.error("ipc:discussion", "Discussion failed", a), n({ type: "error", councilorName: "", error: a instanceof Error ? a.message : String(a) });
|
|
1034
1056
|
} finally {
|
|
1035
1057
|
A = null;
|
|
1036
1058
|
}
|
|
1037
|
-
}),
|
|
1038
|
-
|
|
1039
|
-
}),
|
|
1040
|
-
await R
|
|
1041
|
-
}),
|
|
1042
|
-
const
|
|
1059
|
+
}), o.handle("discussion:stop", async () => (A && (A.abort(), A = null), { success: !0 })), o.handle("discussion:inject", async (t, e) => (B.push(e), { success: !0 })), o.handle("registry:add-local", async (t, e) => He(e)), o.handle("registry:add-remote", async (t, e) => Je(e)), o.handle("registry:remove", async (t, e, r) => (await ze(e, r), { success: !0 })), o.handle("shell:open-in-finder", async (t, e) => {
|
|
1060
|
+
te.showItemInFolder(d(e, "ABOUT.md"));
|
|
1061
|
+
}), o.handle("shell:open-in-terminal", async (t, e) => {
|
|
1062
|
+
await U(R)("open", ["-a", "Terminal", e]);
|
|
1063
|
+
}), o.handle("shell:open-in-editor", async (t, e) => {
|
|
1064
|
+
const r = U(R);
|
|
1043
1065
|
try {
|
|
1044
|
-
await
|
|
1066
|
+
await r("code", [e]);
|
|
1045
1067
|
} catch {
|
|
1046
|
-
|
|
1068
|
+
te.openPath(e);
|
|
1047
1069
|
}
|
|
1048
|
-
}),
|
|
1049
|
-
const
|
|
1070
|
+
}), o.handle("history:list", async () => yt()), o.handle("history:get", async (t, e) => le(e)), o.handle("history:delete", async (t, e) => (await wt(e), { success: !0 })), o.handle("infographic:generate", async (t, e, r) => {
|
|
1071
|
+
const n = d(v(), ".ai-council", "config.json");
|
|
1050
1072
|
let a = { backends: {} };
|
|
1051
1073
|
try {
|
|
1052
|
-
const c = await
|
|
1074
|
+
const c = await w(n, "utf-8");
|
|
1053
1075
|
a = JSON.parse(c);
|
|
1054
1076
|
} catch {
|
|
1055
1077
|
}
|
|
1056
|
-
const i = await
|
|
1057
|
-
return await
|
|
1058
|
-
}),
|
|
1059
|
-
const
|
|
1078
|
+
const i = await le(e), u = await ue(i, a, r);
|
|
1079
|
+
return await bt(e, u), { infographic: u };
|
|
1080
|
+
}), o.handle("infographic:delete", async (t, e, r) => (await vt(e, r), { success: !0 })), o.handle("file:read-as-text", async (t, e) => {
|
|
1081
|
+
const r = C(e), n = r.includes(".") ? "." + r.split(".").pop().toLowerCase() : "", a = /* @__PURE__ */ new Set([
|
|
1060
1082
|
".txt",
|
|
1061
1083
|
".md",
|
|
1062
1084
|
".csv",
|
|
@@ -1104,41 +1126,41 @@ function vt(n, r) {
|
|
|
1104
1126
|
".epub",
|
|
1105
1127
|
".rtf"
|
|
1106
1128
|
]);
|
|
1107
|
-
if (a.has(
|
|
1129
|
+
if (a.has(n))
|
|
1108
1130
|
try {
|
|
1109
|
-
const
|
|
1110
|
-
return { name:
|
|
1111
|
-
} catch (
|
|
1112
|
-
return { name:
|
|
1131
|
+
const u = await w(e, "utf-8");
|
|
1132
|
+
return { name: r, content: u };
|
|
1133
|
+
} catch (u) {
|
|
1134
|
+
return { name: r, content: `[Error reading file: ${u instanceof Error ? u.message : String(u)}]` };
|
|
1113
1135
|
}
|
|
1114
|
-
if (i.has(
|
|
1115
|
-
const
|
|
1136
|
+
if (i.has(n)) {
|
|
1137
|
+
const u = U(R);
|
|
1116
1138
|
try {
|
|
1117
|
-
const { stdout: c } = await
|
|
1139
|
+
const { stdout: c } = await u("markitdown", [e], {
|
|
1118
1140
|
timeout: 3e4,
|
|
1119
1141
|
maxBuffer: 10485760
|
|
1120
1142
|
// 10 MB
|
|
1121
1143
|
});
|
|
1122
|
-
return { name:
|
|
1144
|
+
return { name: r, content: c };
|
|
1123
1145
|
} catch (c) {
|
|
1124
1146
|
const m = c instanceof Error ? c.message : String(c);
|
|
1125
1147
|
return m.includes("ENOENT") ? {
|
|
1126
|
-
name:
|
|
1127
|
-
content: `[Cannot convert ${
|
|
1128
|
-
} : { name:
|
|
1148
|
+
name: r,
|
|
1149
|
+
content: `[Cannot convert ${n} file: markitdown is not installed. Run: pip install 'markitdown[all]']`
|
|
1150
|
+
} : { name: r, content: `[Error converting file: ${m}]` };
|
|
1129
1151
|
}
|
|
1130
1152
|
}
|
|
1131
|
-
return { name:
|
|
1132
|
-
}),
|
|
1133
|
-
const t = R
|
|
1153
|
+
return { name: r, content: `[Unsupported file type: ${r}]` };
|
|
1154
|
+
}), o.handle("markitdown:check", async () => {
|
|
1155
|
+
const t = U(R);
|
|
1134
1156
|
try {
|
|
1135
1157
|
const { stdout: e } = await t("markitdown", ["--version"], { timeout: 5e3 });
|
|
1136
1158
|
return { installed: !0, version: e.trim() };
|
|
1137
1159
|
} catch {
|
|
1138
1160
|
return { installed: !1 };
|
|
1139
1161
|
}
|
|
1140
|
-
}),
|
|
1141
|
-
const t = R
|
|
1162
|
+
}), o.handle("markitdown:install", async () => {
|
|
1163
|
+
const t = U(R);
|
|
1142
1164
|
try {
|
|
1143
1165
|
return await t("pip", ["install", "markitdown[all]"], {
|
|
1144
1166
|
timeout: 12e4,
|
|
@@ -1147,75 +1169,77 @@ function vt(n, r) {
|
|
|
1147
1169
|
} catch (e) {
|
|
1148
1170
|
return { success: !1, error: e instanceof Error ? e.message : String(e) };
|
|
1149
1171
|
}
|
|
1150
|
-
}),
|
|
1151
|
-
const t =
|
|
1172
|
+
}), o.handle("dialog:selectDirectory", async () => {
|
|
1173
|
+
const t = s();
|
|
1152
1174
|
if (!t) return null;
|
|
1153
|
-
const e = await
|
|
1175
|
+
const e = await Ee.showOpenDialog(t, {
|
|
1154
1176
|
properties: ["openDirectory"]
|
|
1155
1177
|
});
|
|
1156
1178
|
return e.canceled ? null : e.filePaths[0];
|
|
1157
1179
|
});
|
|
1158
1180
|
}
|
|
1159
|
-
const
|
|
1160
|
-
|
|
1161
|
-
|
|
1181
|
+
const Ot = Oe(import.meta.url), J = Ie(Ot), ve = d(v(), ".ai-council");
|
|
1182
|
+
Pe(ve, { recursive: !0 });
|
|
1183
|
+
const ke = d(ve, "electron-debug.log");
|
|
1184
|
+
function $(o, ...s) {
|
|
1185
|
+
const t = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${o}] ${s.map((e) => typeof e == "string" ? e : JSON.stringify(e)).join(" ")}
|
|
1162
1186
|
`;
|
|
1163
|
-
|
|
1187
|
+
Ne(ke, t);
|
|
1164
1188
|
}
|
|
1165
|
-
|
|
1189
|
+
Ce(ke, `=== State Change Council Electron — started ${(/* @__PURE__ */ new Date()).toISOString()} ===
|
|
1166
1190
|
`);
|
|
1167
1191
|
L.name = "State Change Council";
|
|
1168
|
-
let
|
|
1169
|
-
function
|
|
1170
|
-
|
|
1192
|
+
let S = null;
|
|
1193
|
+
function me() {
|
|
1194
|
+
$("main", "Creating BrowserWindow"), S = new fe({
|
|
1171
1195
|
width: 1200,
|
|
1172
1196
|
height: 800,
|
|
1173
1197
|
minWidth: 800,
|
|
1174
1198
|
minHeight: 600,
|
|
1175
1199
|
title: "State Change Council",
|
|
1176
|
-
icon: G(
|
|
1200
|
+
icon: G(J, "..", "assets", "icon.png"),
|
|
1177
1201
|
webPreferences: {
|
|
1178
1202
|
contextIsolation: !0,
|
|
1179
1203
|
nodeIntegration: !1,
|
|
1180
|
-
preload:
|
|
1204
|
+
preload: d(J, "preload.mjs")
|
|
1181
1205
|
}
|
|
1182
|
-
}),
|
|
1206
|
+
}), S.webContents.on("console-message", (s, t, e, r, n) => {
|
|
1183
1207
|
const a = ["DEBUG", "INFO", "WARN", "ERROR"][t] || "LOG";
|
|
1184
|
-
|
|
1185
|
-
}),
|
|
1186
|
-
|
|
1187
|
-
}),
|
|
1188
|
-
|
|
1208
|
+
$(`renderer:${a}`, `${e} (${n}:${r})`);
|
|
1209
|
+
}), S.webContents.on("render-process-gone", (s, t) => {
|
|
1210
|
+
$("main:CRASH", "Renderer process gone:", t);
|
|
1211
|
+
}), S.webContents.on("did-fail-load", (s, t, e) => {
|
|
1212
|
+
$("main:LOAD_ERROR", `Failed to load: ${t} ${e}`);
|
|
1189
1213
|
});
|
|
1190
|
-
const
|
|
1191
|
-
if (
|
|
1192
|
-
|
|
1214
|
+
const o = process.env.VITE_DEV_SERVER_URL;
|
|
1215
|
+
if (o)
|
|
1216
|
+
$("main", `Loading dev server URL: ${o}`), S.loadURL(o);
|
|
1193
1217
|
else {
|
|
1194
|
-
const
|
|
1195
|
-
|
|
1218
|
+
const s = d(J, "../dist-renderer/index.html");
|
|
1219
|
+
$("main", `Loading file: ${s}`), S.loadFile(s);
|
|
1196
1220
|
}
|
|
1197
|
-
process.env.VITE_DEV_SERVER_URL &&
|
|
1198
|
-
|
|
1221
|
+
process.env.VITE_DEV_SERVER_URL && S.webContents.openDevTools(), S.on("closed", () => {
|
|
1222
|
+
S = null;
|
|
1199
1223
|
});
|
|
1200
1224
|
}
|
|
1201
|
-
|
|
1225
|
+
pe.registerSchemesAsPrivileged([
|
|
1202
1226
|
{ scheme: "council-file", privileges: { bypassCSP: !0, supportFetchAPI: !0 } }
|
|
1203
1227
|
]);
|
|
1204
1228
|
L.whenReady().then(() => {
|
|
1205
|
-
|
|
1206
|
-
const
|
|
1229
|
+
$("main", "App ready, registering IPC handlers");
|
|
1230
|
+
const o = "State Change Council", s = [
|
|
1207
1231
|
{
|
|
1208
|
-
label:
|
|
1232
|
+
label: o,
|
|
1209
1233
|
submenu: [
|
|
1210
|
-
{ role: "about", label: `About ${
|
|
1234
|
+
{ role: "about", label: `About ${o}` },
|
|
1211
1235
|
{ type: "separator" },
|
|
1212
1236
|
{ role: "services" },
|
|
1213
1237
|
{ type: "separator" },
|
|
1214
|
-
{ role: "hide", label: `Hide ${
|
|
1238
|
+
{ role: "hide", label: `Hide ${o}` },
|
|
1215
1239
|
{ role: "hideOthers" },
|
|
1216
1240
|
{ role: "unhide" },
|
|
1217
1241
|
{ type: "separator" },
|
|
1218
|
-
{ role: "quit", label: `Quit ${
|
|
1242
|
+
{ role: "quit", label: `Quit ${o}` }
|
|
1219
1243
|
]
|
|
1220
1244
|
},
|
|
1221
1245
|
{ role: "fileMenu" },
|
|
@@ -1223,11 +1247,11 @@ L.whenReady().then(() => {
|
|
|
1223
1247
|
{ role: "viewMenu" },
|
|
1224
1248
|
{ role: "windowMenu" }
|
|
1225
1249
|
];
|
|
1226
|
-
|
|
1250
|
+
ne.setApplicationMenu(ne.buildFromTemplate(s)), pe.handle("council-file", (t) => {
|
|
1227
1251
|
const e = decodeURIComponent(t.url.replace("council-file://", ""));
|
|
1228
|
-
return
|
|
1229
|
-
}),
|
|
1230
|
-
|
|
1252
|
+
return Se.fetch($e(e).href);
|
|
1253
|
+
}), It(Ae, () => S), me(), L.on("activate", () => {
|
|
1254
|
+
fe.getAllWindows().length === 0 && me();
|
|
1231
1255
|
});
|
|
1232
1256
|
});
|
|
1233
1257
|
L.on("window-all-closed", () => {
|