@statechange/council 0.9.0 → 0.10.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 +25 -11
- package/dist/backends/anthropic.js +12 -1
- package/dist/backends/anthropic.js.map +1 -1
- package/dist/backends/google.js +14 -0
- package/dist/backends/google.js.map +1 -1
- package/dist/backends/ollama.js +10 -0
- package/dist/backends/ollama.js.map +1 -1
- package/dist/backends/openai.js +11 -0
- package/dist/backends/openai.js.map +1 -1
- package/dist/backends/types.d.ts +1 -1
- package/dist/cli.js +7 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/models.d.ts +5 -0
- package/dist/commands/models.js +54 -0
- package/dist/commands/models.js.map +1 -0
- package/dist/electron/ipc-handlers.js +13 -0
- package/dist/electron/ipc-handlers.js.map +1 -1
- package/dist/electron/preload.js +1 -0
- package/dist/electron/preload.js.map +1 -1
- package/dist/types.d.ts +7 -0
- package/dist-electron/main.js +438 -389
- package/dist-electron/preload.mjs +1 -1
- package/package.json +2 -2
package/dist-electron/main.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { shell as te, dialog as
|
|
2
|
-
import { join as u, basename as P, resolve as F, dirname as
|
|
3
|
-
import { fileURLToPath as
|
|
4
|
-
import { existsSync as _, mkdirSync as
|
|
1
|
+
import { shell as te, dialog as Ae, app as D, protocol as fe, Menu as ne, net as Ie, BrowserWindow as ge, ipcMain as $e } from "electron";
|
|
2
|
+
import { join as u, basename as P, resolve as F, dirname as Oe } from "node:path";
|
|
3
|
+
import { fileURLToPath as Pe, pathToFileURL as Ce } from "node:url";
|
|
4
|
+
import { existsSync as _, mkdirSync as xe, writeFileSync as Ne, appendFileSync as Re } from "node:fs";
|
|
5
5
|
import { homedir as b } from "node:os";
|
|
6
|
-
import { readFile as w, readdir as V, stat as
|
|
6
|
+
import { readFile as w, readdir as V, stat as he, mkdir as C, rm as M, writeFile as O, appendFile as Te } from "node:fs/promises";
|
|
7
7
|
import { execFile as R } from "node:child_process";
|
|
8
8
|
import { promisify as T } from "node:util";
|
|
9
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 Ue from "@anthropic-ai/sdk";
|
|
12
|
+
import Le from "openai";
|
|
13
|
+
import { GoogleGenerativeAI as De } from "@google/generative-ai";
|
|
14
|
+
import { Ollama as je } from "ollama";
|
|
15
|
+
import { createRequire as Ke } from "node:module";
|
|
16
16
|
import "dotenv/config";
|
|
17
|
-
const
|
|
17
|
+
const Be = k.object({
|
|
18
18
|
name: k.string(),
|
|
19
19
|
description: k.string(),
|
|
20
20
|
interests: k.array(k.string()).default([]),
|
|
@@ -23,23 +23,23 @@ const Ke = 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
|
-
u(
|
|
26
|
+
}), Ge = (o, s) => [
|
|
27
|
+
u(o, "skills", s, "SKILL.md"),
|
|
28
28
|
u(process.cwd(), ".claude", "skills", s, "SKILL.md"),
|
|
29
29
|
u(b(), ".agents", "skills", s, "SKILL.md"),
|
|
30
30
|
u(b(), ".claude", "skills", s, "SKILL.md")
|
|
31
31
|
];
|
|
32
|
-
async function Fe(
|
|
33
|
-
for (const t of
|
|
32
|
+
async function Fe(o, s) {
|
|
33
|
+
for (const t of Ge(s, o))
|
|
34
34
|
if (_(t)) {
|
|
35
35
|
const e = await w(t, "utf-8"), { content: r } = X(e);
|
|
36
36
|
return r.trim();
|
|
37
37
|
}
|
|
38
38
|
return null;
|
|
39
39
|
}
|
|
40
|
-
async function
|
|
40
|
+
async function Me(o, s) {
|
|
41
41
|
const t = [];
|
|
42
|
-
for (const e of
|
|
42
|
+
for (const e of o) {
|
|
43
43
|
const r = await Fe(e, s);
|
|
44
44
|
r && t.push(`## Skill: ${e}
|
|
45
45
|
|
|
@@ -49,30 +49,30 @@ ${r}`);
|
|
|
49
49
|
|
|
50
50
|
`);
|
|
51
51
|
}
|
|
52
|
-
function We(
|
|
53
|
-
return
|
|
52
|
+
function We(o, s) {
|
|
53
|
+
return o ? o.startsWith("http://") || o.startsWith("https://") ? o : `council-file://${o.startsWith("/") ? o : u(s, o)}` : void 0;
|
|
54
54
|
}
|
|
55
|
-
async function
|
|
55
|
+
async function Ye(o, s) {
|
|
56
56
|
const t = /\{\{(.+?)\}\}/g;
|
|
57
|
-
let e =
|
|
58
|
-
for (const r of
|
|
59
|
-
const
|
|
57
|
+
let e = o;
|
|
58
|
+
for (const r of o.matchAll(t)) {
|
|
59
|
+
const n = r[1].trim(), a = u(s, n);
|
|
60
60
|
if (_(a)) {
|
|
61
61
|
const i = await w(a, "utf-8");
|
|
62
62
|
e = e.replace(r[0], i.trim());
|
|
63
63
|
} else
|
|
64
|
-
e = e.replace(r[0], `[Reference not found: ${
|
|
64
|
+
e = e.replace(r[0], `[Reference not found: ${n}]`);
|
|
65
65
|
}
|
|
66
66
|
return e;
|
|
67
67
|
}
|
|
68
|
-
async function oe(
|
|
69
|
-
const s = F(
|
|
68
|
+
async function oe(o) {
|
|
69
|
+
const s = F(o), t = u(s, "ABOUT.md");
|
|
70
70
|
if (!_(t))
|
|
71
71
|
throw new Error(`No ABOUT.md found in ${s}`);
|
|
72
|
-
const e = await w(t, "utf-8"), { data: r, content:
|
|
73
|
-
let i = await
|
|
72
|
+
const e = await w(t, "utf-8"), { data: r, content: n } = X(e), a = Be.parse(r);
|
|
73
|
+
let i = await Ye(n.trim(), s);
|
|
74
74
|
if (a.skills.length > 0) {
|
|
75
|
-
const l = await
|
|
75
|
+
const l = await Me(a.skills, s);
|
|
76
76
|
l && (i += `
|
|
77
77
|
|
|
78
78
|
` + l);
|
|
@@ -85,32 +85,32 @@ async function oe(n) {
|
|
|
85
85
|
avatarUrl: We(a.avatar, s)
|
|
86
86
|
};
|
|
87
87
|
}
|
|
88
|
-
async function re(
|
|
88
|
+
async function re(o, s) {
|
|
89
89
|
const t = [], e = /* @__PURE__ */ new Set();
|
|
90
90
|
if (s?.length) {
|
|
91
91
|
for (const r of s)
|
|
92
92
|
if (_(u(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 r = await V(
|
|
101
|
-
for (const
|
|
102
|
-
const a = u(
|
|
103
|
-
(await
|
|
99
|
+
if (_(o)) {
|
|
100
|
+
const r = await V(o);
|
|
101
|
+
for (const n of r) {
|
|
102
|
+
const a = u(o, n);
|
|
103
|
+
(await he(a)).isDirectory() && _(u(a, "ABOUT.md")) && (e.has(P(a)) || (t.push(await oe(a)), e.add(P(a))));
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
if (t.length === 0)
|
|
107
107
|
throw new Error(
|
|
108
|
-
`No councilors found. Searched ${
|
|
108
|
+
`No councilors found. Searched ${o}${s?.length ? ` and ${s.length} registered path(s)` : ""}.
|
|
109
109
|
Create one with: mkdir -p ~/.ai-council/councilors/my-councilor && council councilor add ~/.ai-council/councilors/my-councilor`
|
|
110
110
|
);
|
|
111
111
|
return t;
|
|
112
112
|
}
|
|
113
|
-
const
|
|
113
|
+
const He = T(R), ye = u(b(), ".ai-council", "config.json"), se = u(b(), ".ai-council", "councilors");
|
|
114
114
|
async function Q() {
|
|
115
115
|
try {
|
|
116
116
|
return JSON.parse(await w(ye, "utf-8"));
|
|
@@ -118,46 +118,46 @@ async function Q() {
|
|
|
118
118
|
return { backends: {} };
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
|
-
async function Z(
|
|
121
|
+
async function Z(o) {
|
|
122
122
|
const s = u(b(), ".ai-council");
|
|
123
|
-
await C(s, { recursive: !0 }), await O(ye, JSON.stringify(
|
|
123
|
+
await C(s, { recursive: !0 }), await O(ye, JSON.stringify(o, null, 2), "utf-8");
|
|
124
124
|
}
|
|
125
|
-
function
|
|
126
|
-
return
|
|
125
|
+
function Je(o) {
|
|
126
|
+
return o.councilors ?? o.counsellors ?? {};
|
|
127
127
|
}
|
|
128
|
-
function ae(
|
|
129
|
-
return Object.values(
|
|
128
|
+
function ae(o) {
|
|
129
|
+
return Object.values(Je(o)).map((s) => s.path);
|
|
130
130
|
}
|
|
131
|
-
async function
|
|
132
|
-
const s = F(
|
|
131
|
+
async function ze(o) {
|
|
132
|
+
const s = F(o), t = u(s, "ABOUT.md");
|
|
133
133
|
if (!_(t))
|
|
134
134
|
throw new Error(`No ABOUT.md found in ${s}`);
|
|
135
|
-
const e = P(s), r = await Q(),
|
|
136
|
-
if (
|
|
137
|
-
throw new Error(`Councilor "${e}" is already registered (path: ${
|
|
135
|
+
const e = P(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
138
|
const l = (await w(t, "utf-8")).match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1] ?? e;
|
|
139
|
-
return
|
|
139
|
+
return n[e] = {
|
|
140
140
|
path: s,
|
|
141
141
|
source: "local",
|
|
142
142
|
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
143
|
-
}, r.councilors =
|
|
143
|
+
}, r.councilors = n, await Z(r), { id: e, name: l };
|
|
144
144
|
}
|
|
145
|
-
async function
|
|
145
|
+
async function Ve(o) {
|
|
146
146
|
await C(se, { recursive: !0 });
|
|
147
|
-
const s = P(
|
|
147
|
+
const s = P(o, ".git").replace(/\.git$/, ""), t = u(se, s);
|
|
148
148
|
if (_(t))
|
|
149
149
|
throw new Error(`Directory already exists: ${t}. Remove it first or use a different URL.`);
|
|
150
|
-
await
|
|
151
|
-
const e = [], r = await Q(),
|
|
150
|
+
await He("git", ["clone", "--depth", "1", o, t]);
|
|
151
|
+
const e = [], r = await Q(), n = r.councilors ?? {};
|
|
152
152
|
if (_(u(t, "ABOUT.md"))) {
|
|
153
153
|
const a = s;
|
|
154
|
-
if (
|
|
154
|
+
if (n[a])
|
|
155
155
|
throw new Error(`Councilor "${a}" is already registered`);
|
|
156
156
|
const c = (await w(u(t, "ABOUT.md"), "utf-8")).match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1] ?? a;
|
|
157
|
-
|
|
157
|
+
n[a] = {
|
|
158
158
|
path: t,
|
|
159
159
|
source: "git",
|
|
160
|
-
url:
|
|
160
|
+
url: o,
|
|
161
161
|
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
162
162
|
}, e.push({ id: a, name: c });
|
|
163
163
|
} else {
|
|
@@ -165,50 +165,50 @@ async function ze(n) {
|
|
|
165
165
|
for (const i of a) {
|
|
166
166
|
if (i.startsWith(".")) continue;
|
|
167
167
|
const l = u(t, i);
|
|
168
|
-
if ((await
|
|
168
|
+
if ((await he(l)).isDirectory() && _(u(l, "ABOUT.md"))) {
|
|
169
169
|
const f = i;
|
|
170
|
-
if (
|
|
171
|
-
const
|
|
172
|
-
|
|
170
|
+
if (n[f]) continue;
|
|
171
|
+
const g = (await w(u(l, "ABOUT.md"), "utf-8")).match(/^name:\s*["']?(.+?)["']?\s*$/m)?.[1] ?? f;
|
|
172
|
+
n[f] = {
|
|
173
173
|
path: l,
|
|
174
174
|
source: "git",
|
|
175
|
-
url:
|
|
175
|
+
url: o,
|
|
176
176
|
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
177
|
-
}, e.push({ id: f, name:
|
|
177
|
+
}, e.push({ id: f, name: g });
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
if (e.length === 0)
|
|
181
|
-
throw await
|
|
181
|
+
throw await M(t, { recursive: !0, force: !0 }), new Error("No councilors found in cloned repository (no ABOUT.md files)");
|
|
182
182
|
}
|
|
183
|
-
return r.councilors =
|
|
183
|
+
return r.councilors = n, await Z(r), e;
|
|
184
184
|
}
|
|
185
|
-
async function
|
|
186
|
-
const t = await Q(), e = t.councilors ?? {}, r = e[
|
|
185
|
+
async function Xe(o, s = !1) {
|
|
186
|
+
const t = await Q(), e = t.councilors ?? {}, r = e[o];
|
|
187
187
|
if (!r)
|
|
188
|
-
throw new Error(`Councilor "${
|
|
189
|
-
s && r.source === "git" && _(r.path) && await
|
|
188
|
+
throw new Error(`Councilor "${o}" is not registered`);
|
|
189
|
+
s && r.source === "git" && _(r.path) && await M(r.path, { recursive: !0, force: !0 }), delete e[o], t.councilors = e, await Z(t);
|
|
190
190
|
}
|
|
191
|
-
function
|
|
192
|
-
const s = new
|
|
193
|
-
apiKey:
|
|
194
|
-
...
|
|
191
|
+
function Qe(o) {
|
|
192
|
+
const s = new Ue({
|
|
193
|
+
apiKey: o.apiKey ?? process.env.ANTHROPIC_API_KEY,
|
|
194
|
+
...o.baseUrl ? { baseURL: o.baseUrl } : {}
|
|
195
195
|
});
|
|
196
196
|
return {
|
|
197
197
|
name: "anthropic",
|
|
198
|
-
defaultModel: "claude-sonnet-4-
|
|
198
|
+
defaultModel: "claude-sonnet-4-6",
|
|
199
199
|
async chat(t) {
|
|
200
200
|
const e = await s.messages.create({
|
|
201
201
|
model: t.model,
|
|
202
202
|
max_tokens: 4096,
|
|
203
203
|
system: t.systemPrompt,
|
|
204
|
-
messages: t.messages.map((
|
|
205
|
-
role:
|
|
206
|
-
content:
|
|
204
|
+
messages: t.messages.map((n) => ({
|
|
205
|
+
role: n.role,
|
|
206
|
+
content: n.content
|
|
207
207
|
})),
|
|
208
208
|
...t.temperature !== void 0 ? { temperature: t.temperature } : {}
|
|
209
209
|
});
|
|
210
210
|
return {
|
|
211
|
-
content: e.content.find((
|
|
211
|
+
content: e.content.find((n) => n.type === "text")?.text ?? "",
|
|
212
212
|
tokenUsage: {
|
|
213
213
|
input: e.usage.input_tokens,
|
|
214
214
|
output: e.usage.output_tokens
|
|
@@ -220,14 +220,14 @@ function Xe(n) {
|
|
|
220
220
|
model: t.model,
|
|
221
221
|
max_tokens: 4096,
|
|
222
222
|
system: t.systemPrompt,
|
|
223
|
-
messages: t.messages.map((
|
|
224
|
-
role:
|
|
225
|
-
content:
|
|
223
|
+
messages: t.messages.map((n) => ({
|
|
224
|
+
role: n.role,
|
|
225
|
+
content: n.content
|
|
226
226
|
})),
|
|
227
227
|
...t.temperature !== void 0 ? { temperature: t.temperature } : {}
|
|
228
228
|
});
|
|
229
|
-
for await (const
|
|
230
|
-
|
|
229
|
+
for await (const n of e)
|
|
230
|
+
n.type === "content_block_delta" && n.delta.type === "text_delta" && (yield { delta: n.delta.text });
|
|
231
231
|
const r = await e.finalMessage();
|
|
232
232
|
yield {
|
|
233
233
|
delta: "",
|
|
@@ -236,13 +236,23 @@ function Xe(n) {
|
|
|
236
236
|
output: r.usage.output_tokens
|
|
237
237
|
}
|
|
238
238
|
};
|
|
239
|
+
},
|
|
240
|
+
async listModels() {
|
|
241
|
+
const t = [];
|
|
242
|
+
for await (const e of s.models.list({ limit: 100 }))
|
|
243
|
+
t.push({
|
|
244
|
+
id: e.id,
|
|
245
|
+
name: e.display_name,
|
|
246
|
+
created: e.created_at
|
|
247
|
+
});
|
|
248
|
+
return t.sort((e, r) => e.id.localeCompare(r.id));
|
|
239
249
|
}
|
|
240
250
|
};
|
|
241
251
|
}
|
|
242
|
-
function
|
|
243
|
-
const s = new
|
|
244
|
-
apiKey:
|
|
245
|
-
...
|
|
252
|
+
function Ze(o) {
|
|
253
|
+
const s = new Le({
|
|
254
|
+
apiKey: o.apiKey ?? process.env.OPENAI_API_KEY,
|
|
255
|
+
...o.baseUrl ? { baseURL: o.baseUrl } : {}
|
|
246
256
|
});
|
|
247
257
|
return {
|
|
248
258
|
name: "openai",
|
|
@@ -252,9 +262,9 @@ function Qe(n) {
|
|
|
252
262
|
model: t.model,
|
|
253
263
|
messages: [
|
|
254
264
|
{ role: "system", content: t.systemPrompt },
|
|
255
|
-
...t.messages.map((
|
|
256
|
-
role:
|
|
257
|
-
content:
|
|
265
|
+
...t.messages.map((n) => ({
|
|
266
|
+
role: n.role,
|
|
267
|
+
content: n.content
|
|
258
268
|
}))
|
|
259
269
|
],
|
|
260
270
|
...t.temperature !== void 0 ? { temperature: t.temperature } : {}
|
|
@@ -279,8 +289,8 @@ function Qe(n) {
|
|
|
279
289
|
stream_options: { include_usage: !0 }
|
|
280
290
|
});
|
|
281
291
|
for await (const r of e) {
|
|
282
|
-
const
|
|
283
|
-
|
|
292
|
+
const n = r.choices[0]?.delta?.content;
|
|
293
|
+
n && (yield { delta: n }), r.usage && (yield {
|
|
284
294
|
delta: "",
|
|
285
295
|
tokenUsage: {
|
|
286
296
|
input: r.usage.prompt_tokens,
|
|
@@ -288,11 +298,20 @@ function Qe(n) {
|
|
|
288
298
|
}
|
|
289
299
|
});
|
|
290
300
|
}
|
|
301
|
+
},
|
|
302
|
+
async listModels() {
|
|
303
|
+
const t = await s.models.list(), e = [];
|
|
304
|
+
for await (const r of t)
|
|
305
|
+
e.push({
|
|
306
|
+
id: r.id,
|
|
307
|
+
created: new Date(r.created * 1e3).toISOString()
|
|
308
|
+
});
|
|
309
|
+
return e.sort((r, n) => r.id.localeCompare(n.id));
|
|
291
310
|
}
|
|
292
311
|
};
|
|
293
312
|
}
|
|
294
|
-
function
|
|
295
|
-
const s =
|
|
313
|
+
function qe(o) {
|
|
314
|
+
const s = o.apiKey ?? process.env.GOOGLE_API_KEY ?? "", t = new De(s);
|
|
296
315
|
return {
|
|
297
316
|
name: "google",
|
|
298
317
|
defaultModel: "gemini-2.0-flash",
|
|
@@ -303,10 +322,10 @@ function Ze(n) {
|
|
|
303
322
|
generationConfig: {
|
|
304
323
|
...e.temperature !== void 0 ? { temperature: e.temperature } : {}
|
|
305
324
|
}
|
|
306
|
-
}),
|
|
325
|
+
}), n = e.messages.slice(0, -1).map((f) => ({
|
|
307
326
|
role: f.role === "assistant" ? "model" : "user",
|
|
308
327
|
parts: [{ text: f.content }]
|
|
309
|
-
})), a = r.startChat({ history:
|
|
328
|
+
})), a = r.startChat({ history: n }), i = e.messages[e.messages.length - 1], c = (await a.sendMessage(i?.content ?? "")).response;
|
|
310
329
|
return {
|
|
311
330
|
content: c.text(),
|
|
312
331
|
tokenUsage: c.usageMetadata ? {
|
|
@@ -322,10 +341,10 @@ function Ze(n) {
|
|
|
322
341
|
generationConfig: {
|
|
323
342
|
...e.temperature !== void 0 ? { temperature: e.temperature } : {}
|
|
324
343
|
}
|
|
325
|
-
}),
|
|
344
|
+
}), n = e.messages.slice(0, -1).map((f) => ({
|
|
326
345
|
role: f.role === "assistant" ? "model" : "user",
|
|
327
346
|
parts: [{ text: f.content }]
|
|
328
|
-
})), a = r.startChat({ history:
|
|
347
|
+
})), a = r.startChat({ history: n }), i = e.messages[e.messages.length - 1], l = await a.sendMessageStream(i?.content ?? "");
|
|
329
348
|
for await (const f of l.stream) {
|
|
330
349
|
const y = f.text();
|
|
331
350
|
y && (yield { delta: y });
|
|
@@ -338,12 +357,23 @@ function Ze(n) {
|
|
|
338
357
|
output: c.usageMetadata.candidatesTokenCount ?? 0
|
|
339
358
|
} : void 0
|
|
340
359
|
};
|
|
360
|
+
},
|
|
361
|
+
async listModels() {
|
|
362
|
+
const e = await fetch(
|
|
363
|
+
`https://generativelanguage.googleapis.com/v1beta/models?key=${s}&pageSize=100`
|
|
364
|
+
);
|
|
365
|
+
if (!e.ok) throw new Error(`Google API error: ${e.status}`);
|
|
366
|
+
return ((await e.json()).models || []).filter((n) => n.name.startsWith("models/gemini")).map((n) => ({
|
|
367
|
+
id: n.name.replace("models/", ""),
|
|
368
|
+
name: n.displayName,
|
|
369
|
+
description: n.description
|
|
370
|
+
})).sort((n, a) => n.id.localeCompare(a.id));
|
|
341
371
|
}
|
|
342
372
|
};
|
|
343
373
|
}
|
|
344
|
-
function
|
|
345
|
-
const s = new
|
|
346
|
-
host:
|
|
374
|
+
function et(o) {
|
|
375
|
+
const s = new je({
|
|
376
|
+
host: o.baseUrl ?? "http://localhost:11434"
|
|
347
377
|
});
|
|
348
378
|
return {
|
|
349
379
|
name: "ollama",
|
|
@@ -385,28 +415,35 @@ function qe(n) {
|
|
|
385
415
|
},
|
|
386
416
|
stream: !0
|
|
387
417
|
});
|
|
388
|
-
let r,
|
|
418
|
+
let r, n;
|
|
389
419
|
for await (const a of e)
|
|
390
|
-
a.message.content && (yield { delta: a.message.content }), a.done && (r = a.prompt_eval_count,
|
|
420
|
+
a.message.content && (yield { delta: a.message.content }), a.done && (r = a.prompt_eval_count, n = a.eval_count);
|
|
391
421
|
yield {
|
|
392
422
|
delta: "",
|
|
393
|
-
tokenUsage: r !== void 0 ? { input: r ?? 0, output:
|
|
423
|
+
tokenUsage: r !== void 0 ? { input: r ?? 0, output: n ?? 0 } : void 0
|
|
394
424
|
};
|
|
425
|
+
},
|
|
426
|
+
async listModels() {
|
|
427
|
+
return (await s.list()).models.map((e) => ({
|
|
428
|
+
id: e.name,
|
|
429
|
+
name: e.name,
|
|
430
|
+
created: e.modified_at?.toString()
|
|
431
|
+
})).sort((e, r) => e.id.localeCompare(r.id));
|
|
395
432
|
}
|
|
396
433
|
};
|
|
397
434
|
}
|
|
398
435
|
const ie = {
|
|
399
|
-
anthropic:
|
|
400
|
-
openai:
|
|
401
|
-
google:
|
|
402
|
-
ollama:
|
|
436
|
+
anthropic: Qe,
|
|
437
|
+
openai: Ze,
|
|
438
|
+
google: qe,
|
|
439
|
+
ollama: et
|
|
403
440
|
};
|
|
404
441
|
let U = null;
|
|
405
|
-
async function
|
|
442
|
+
async function tt() {
|
|
406
443
|
if (U) return U;
|
|
407
|
-
const
|
|
444
|
+
const o = u(b(), ".ai-council", "config.json");
|
|
408
445
|
try {
|
|
409
|
-
const s = await w(
|
|
446
|
+
const s = await w(o, "utf-8");
|
|
410
447
|
U = JSON.parse(s);
|
|
411
448
|
} catch {
|
|
412
449
|
U = { backends: {} };
|
|
@@ -414,22 +451,27 @@ async function et() {
|
|
|
414
451
|
return U;
|
|
415
452
|
}
|
|
416
453
|
const z = /* @__PURE__ */ new Map();
|
|
417
|
-
function
|
|
454
|
+
function we() {
|
|
418
455
|
U = null, z.clear();
|
|
419
456
|
}
|
|
420
|
-
async function
|
|
421
|
-
const s = z.get(
|
|
457
|
+
async function j(o) {
|
|
458
|
+
const s = z.get(o);
|
|
422
459
|
if (s) return s;
|
|
423
|
-
const t = ie[
|
|
460
|
+
const t = ie[o];
|
|
424
461
|
if (!t)
|
|
425
|
-
throw new Error(`Unknown backend: "${
|
|
426
|
-
const r = (await
|
|
427
|
-
return z.set(
|
|
462
|
+
throw new Error(`Unknown backend: "${o}". Available: ${Object.keys(ie).join(", ")}`);
|
|
463
|
+
const r = (await tt()).backends[o] ?? {}, n = t(r);
|
|
464
|
+
return z.set(o, n), n;
|
|
428
465
|
}
|
|
429
|
-
|
|
466
|
+
const nt = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
467
|
+
__proto__: null,
|
|
468
|
+
clearCaches: we,
|
|
469
|
+
getBackend: j
|
|
470
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
471
|
+
function ot() {
|
|
430
472
|
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();
|
|
431
473
|
}
|
|
432
|
-
const
|
|
474
|
+
const W = "---EXCALIDRAW---", rt = `You are the Secretary of a council discussion. Your job is to synthesize a clear, structured summary of the conversation that just took place.
|
|
433
475
|
|
|
434
476
|
Structure your summary with these sections:
|
|
435
477
|
|
|
@@ -446,17 +488,17 @@ Where did they disagree? What are the key tensions?
|
|
|
446
488
|
What are the most important takeaways? What would you recommend based on the full discussion?
|
|
447
489
|
|
|
448
490
|
Be concise but thorough. Use markdown formatting.`;
|
|
449
|
-
function
|
|
491
|
+
function st(o) {
|
|
450
492
|
const s = [];
|
|
451
|
-
s.push(`Topic: ${
|
|
493
|
+
s.push(`Topic: ${o.topic}`), s.push(`Councilors: ${o.councilors.map((e) => e.name).join(", ")}`), s.push(`Rounds: ${o.rounds}`), s.push("");
|
|
452
494
|
let t = 0;
|
|
453
|
-
for (const e of
|
|
495
|
+
for (const e of o.turns)
|
|
454
496
|
e.round !== t && (t = e.round, s.push(`--- Round ${t} ---`), s.push("")), s.push(`[${e.councilorName}]:`), s.push(e.content), s.push("");
|
|
455
497
|
return s.join(`
|
|
456
498
|
`);
|
|
457
499
|
}
|
|
458
|
-
async function
|
|
459
|
-
result:
|
|
500
|
+
async function at({
|
|
501
|
+
result: o,
|
|
460
502
|
config: s,
|
|
461
503
|
onChunk: t,
|
|
462
504
|
signal: e
|
|
@@ -464,11 +506,11 @@ async function st({
|
|
|
464
506
|
const r = s.secretary;
|
|
465
507
|
if (!r?.backend)
|
|
466
508
|
throw new Error("No secretary backend configured");
|
|
467
|
-
const
|
|
509
|
+
const n = await j(r.backend), a = r.model ?? n.defaultModel, i = r.systemPrompt || rt, l = ot(), c = `${i}
|
|
468
510
|
|
|
469
511
|
${l}
|
|
470
512
|
|
|
471
|
-
After your text summary, output \`${
|
|
513
|
+
After your text summary, output \`${W}\` 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.`, f = st(o), y = {
|
|
472
514
|
model: a,
|
|
473
515
|
systemPrompt: c,
|
|
474
516
|
messages: [{ role: "user", content: `Please summarize this council discussion and create a position diagram:
|
|
@@ -476,18 +518,18 @@ After your text summary, output \`${M}\` on its own line, then a JSON array of E
|
|
|
476
518
|
${f}` }],
|
|
477
519
|
temperature: 0.5
|
|
478
520
|
};
|
|
479
|
-
let
|
|
480
|
-
if (
|
|
481
|
-
for await (const m of
|
|
521
|
+
let h = "";
|
|
522
|
+
if (n.chatStream)
|
|
523
|
+
for await (const m of n.chatStream(y)) {
|
|
482
524
|
if (e?.aborted) break;
|
|
483
|
-
|
|
525
|
+
h += m.delta, m.delta && t && t(m.delta);
|
|
484
526
|
}
|
|
485
527
|
else
|
|
486
|
-
|
|
487
|
-
const
|
|
488
|
-
if (
|
|
489
|
-
return { text:
|
|
490
|
-
const L =
|
|
528
|
+
h = (await n.chat(y)).content, t && t(h);
|
|
529
|
+
const g = h.indexOf(W);
|
|
530
|
+
if (g === -1)
|
|
531
|
+
return { text: h.trim() };
|
|
532
|
+
const L = h.slice(0, g).trim(), d = h.slice(g + W.length).trim();
|
|
491
533
|
let p;
|
|
492
534
|
try {
|
|
493
535
|
const m = d.match(/\[[\s\S]*\]/);
|
|
@@ -496,24 +538,24 @@ ${f}` }],
|
|
|
496
538
|
}
|
|
497
539
|
return { text: L, diagram: p };
|
|
498
540
|
}
|
|
499
|
-
const
|
|
500
|
-
async function
|
|
501
|
-
result:
|
|
541
|
+
const it = "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.";
|
|
542
|
+
async function ct({
|
|
543
|
+
result: o,
|
|
502
544
|
roundNumber: s,
|
|
503
545
|
config: t,
|
|
504
546
|
onChunk: e,
|
|
505
547
|
signal: r
|
|
506
548
|
}) {
|
|
507
|
-
const
|
|
508
|
-
if (!
|
|
549
|
+
const n = t.secretary;
|
|
550
|
+
if (!n?.backend)
|
|
509
551
|
throw new Error("No secretary backend configured");
|
|
510
|
-
const a = await
|
|
511
|
-
c.push(`Topic: ${
|
|
512
|
-
for (const
|
|
513
|
-
c.push(`[${
|
|
552
|
+
const a = await j(n.backend), i = n.model ?? a.defaultModel, l = o.turns.filter((h) => h.round === s), c = [];
|
|
553
|
+
c.push(`Topic: ${o.topic}`), c.push(`Round ${s}${s === 1 ? " (Constructive)" : " (Rebuttal)"}`), c.push("");
|
|
554
|
+
for (const h of l)
|
|
555
|
+
c.push(`[${h.councilorName}]:`), c.push(h.content), c.push("");
|
|
514
556
|
const f = {
|
|
515
557
|
model: i,
|
|
516
|
-
systemPrompt:
|
|
558
|
+
systemPrompt: it,
|
|
517
559
|
messages: [{ role: "user", content: `Please summarize this round:
|
|
518
560
|
|
|
519
561
|
${c.join(`
|
|
@@ -522,32 +564,32 @@ ${c.join(`
|
|
|
522
564
|
};
|
|
523
565
|
let y = "";
|
|
524
566
|
if (a.chatStream)
|
|
525
|
-
for await (const
|
|
567
|
+
for await (const h of a.chatStream(f)) {
|
|
526
568
|
if (r?.aborted) break;
|
|
527
|
-
y +=
|
|
569
|
+
y += h.delta, h.delta && e && e(h.delta);
|
|
528
570
|
}
|
|
529
571
|
else
|
|
530
572
|
y = (await a.chat(f)).content, e && e(y);
|
|
531
573
|
return y.trim();
|
|
532
574
|
}
|
|
533
|
-
async function
|
|
534
|
-
topic:
|
|
575
|
+
async function lt({
|
|
576
|
+
topic: o,
|
|
535
577
|
firstRoundTurns: s,
|
|
536
578
|
config: t
|
|
537
579
|
}) {
|
|
538
580
|
const e = t.secretary;
|
|
539
581
|
if (!e?.backend)
|
|
540
582
|
throw new Error("No secretary backend configured");
|
|
541
|
-
const r = await
|
|
583
|
+
const r = await j(e.backend), n = e.model ?? r.defaultModel, a = s.map((l) => `[${l.councilorName}]: ${l.content.slice(0, 300)}`).join(`
|
|
542
584
|
|
|
543
585
|
`);
|
|
544
586
|
return (await r.chat({
|
|
545
|
-
model:
|
|
587
|
+
model: n,
|
|
546
588
|
systemPrompt: "Generate a concise title (max 8 words) for this council discussion. Return only the title, no quotes or punctuation at the end.",
|
|
547
589
|
messages: [
|
|
548
590
|
{
|
|
549
591
|
role: "user",
|
|
550
|
-
content: `Topic: ${
|
|
592
|
+
content: `Topic: ${o}
|
|
551
593
|
|
|
552
594
|
First round:
|
|
553
595
|
${a}`
|
|
@@ -556,60 +598,60 @@ ${a}`
|
|
|
556
598
|
temperature: 0.3
|
|
557
599
|
})).content.trim().replace(/^["']+|["']+$/g, "").replace(/[.!?]+$/, "").trim();
|
|
558
600
|
}
|
|
559
|
-
const
|
|
601
|
+
const be = u(b(), ".ai-council"), ut = u(be, "council.log");
|
|
560
602
|
let ce = !1;
|
|
561
|
-
async function
|
|
562
|
-
ce || (await C(
|
|
603
|
+
async function dt() {
|
|
604
|
+
ce || (await C(be, { recursive: !0 }), ce = !0);
|
|
563
605
|
}
|
|
564
|
-
function
|
|
565
|
-
let
|
|
606
|
+
function mt(o, s, t, e) {
|
|
607
|
+
let n = `[${(/* @__PURE__ */ new Date()).toISOString()}] ${o} [${s}] ${t}`;
|
|
566
608
|
if (e !== void 0) {
|
|
567
609
|
const a = e instanceof Error ? `${e.message}
|
|
568
610
|
${e.stack ?? ""}` : typeof e == "string" ? e : JSON.stringify(e, null, 2);
|
|
569
|
-
|
|
611
|
+
n += `
|
|
570
612
|
${a.replace(/\n/g, `
|
|
571
613
|
`)}`;
|
|
572
614
|
}
|
|
573
|
-
return
|
|
615
|
+
return n + `
|
|
574
616
|
`;
|
|
575
617
|
}
|
|
576
|
-
async function Y(
|
|
618
|
+
async function Y(o, s, t, e) {
|
|
577
619
|
try {
|
|
578
|
-
await
|
|
620
|
+
await dt(), await Te(ut, mt(o, s, t, e));
|
|
579
621
|
} catch {
|
|
580
622
|
}
|
|
581
623
|
}
|
|
582
624
|
const v = {
|
|
583
|
-
info: (
|
|
584
|
-
warn: (
|
|
585
|
-
error: (
|
|
625
|
+
info: (o, s, t) => Y("INFO", o, s, t),
|
|
626
|
+
warn: (o, s, t) => Y("WARN", o, s, t),
|
|
627
|
+
error: (o, s, t) => Y("ERROR", o, s, t)
|
|
586
628
|
};
|
|
587
|
-
function
|
|
588
|
-
const
|
|
629
|
+
function pt(o, s, t, e, r) {
|
|
630
|
+
const n = [{ role: "user", content: o }];
|
|
589
631
|
if (e?.length) {
|
|
590
632
|
for (const a of e)
|
|
591
|
-
a.councilorId === t ?
|
|
633
|
+
a.councilorId === t ? n.push({ role: "assistant", content: a.content }) : n.push({
|
|
592
634
|
role: "user",
|
|
593
635
|
content: `[${a.councilorName}, Round ${a.round}]: ${a.content}`
|
|
594
636
|
});
|
|
595
|
-
r &&
|
|
637
|
+
r && n.push({
|
|
596
638
|
role: "user",
|
|
597
639
|
content: `[Secretary Summary]: ${r}`
|
|
598
640
|
});
|
|
599
641
|
}
|
|
600
642
|
for (const a of s)
|
|
601
|
-
a.councilorId === t ?
|
|
643
|
+
a.councilorId === t ? n.push({ role: "assistant", content: a.content }) : n.push({
|
|
602
644
|
role: "user",
|
|
603
645
|
content: `[${a.councilorName}, Round ${a.round}]: ${a.content}`
|
|
604
646
|
});
|
|
605
|
-
return
|
|
647
|
+
return n;
|
|
606
648
|
}
|
|
607
|
-
function
|
|
608
|
-
const r = [{ role: "user", content:
|
|
649
|
+
function ft(o, s, t, e) {
|
|
650
|
+
const r = [{ role: "user", content: o }];
|
|
609
651
|
if (e === 1)
|
|
610
652
|
return r;
|
|
611
|
-
const
|
|
612
|
-
for (const i of
|
|
653
|
+
const n = s.filter((i) => i.round === 1);
|
|
654
|
+
for (const i of n)
|
|
613
655
|
i.councilorId === t ? r.push({ role: "assistant", content: i.content }) : r.push({
|
|
614
656
|
role: "user",
|
|
615
657
|
content: `[${i.councilorName}, Constructive]: ${i.content}`
|
|
@@ -627,25 +669,25 @@ function pt(n, s, t, e) {
|
|
|
627
669
|
i.councilorId === t && i.round !== 1 && i.round !== a && i.round < e && r.push({ role: "assistant", content: i.content });
|
|
628
670
|
return r;
|
|
629
671
|
}
|
|
630
|
-
function
|
|
631
|
-
const t = [...
|
|
672
|
+
function gt(o, s) {
|
|
673
|
+
const t = [...o];
|
|
632
674
|
let e = s | 0;
|
|
633
675
|
const r = () => {
|
|
634
676
|
e = e + 1831565813 | 0;
|
|
635
|
-
let
|
|
636
|
-
return
|
|
677
|
+
let n = Math.imul(e ^ e >>> 15, 1 | e);
|
|
678
|
+
return n = n + Math.imul(n ^ n >>> 7, 61 | n) ^ n, ((n ^ n >>> 14) >>> 0) / 4294967296;
|
|
637
679
|
};
|
|
638
|
-
for (let
|
|
639
|
-
const a = Math.floor(r() * (
|
|
640
|
-
[t[
|
|
680
|
+
for (let n = t.length - 1; n > 0; n--) {
|
|
681
|
+
const a = Math.floor(r() * (n + 1));
|
|
682
|
+
[t[n], t[a]] = [t[a], t[n]];
|
|
641
683
|
}
|
|
642
684
|
return t;
|
|
643
685
|
}
|
|
644
|
-
function H(
|
|
686
|
+
function H(o, s, t, e, r, n) {
|
|
645
687
|
return {
|
|
646
|
-
topic:
|
|
647
|
-
topicSource:
|
|
648
|
-
councilors:
|
|
688
|
+
topic: o.topic,
|
|
689
|
+
topicSource: o.topicSource,
|
|
690
|
+
councilors: o.councilors.map((a) => ({
|
|
649
691
|
id: a.id,
|
|
650
692
|
name: a.frontmatter.name,
|
|
651
693
|
description: a.frontmatter.description,
|
|
@@ -653,110 +695,110 @@ function H(n, s, t, e, r, o) {
|
|
|
653
695
|
model: a.frontmatter.model ?? "default",
|
|
654
696
|
avatarUrl: a.avatarUrl
|
|
655
697
|
})),
|
|
656
|
-
rounds:
|
|
698
|
+
rounds: o.rounds,
|
|
657
699
|
turns: s,
|
|
658
700
|
startedAt: t,
|
|
659
701
|
completedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
660
702
|
totalTokenUsage: { input: e, output: r },
|
|
661
|
-
...
|
|
662
|
-
...
|
|
703
|
+
...n && Object.keys(n).length > 0 ? { roundSummaries: n } : {},
|
|
704
|
+
...o.mode === "debate" ? { mode: "debate" } : {}
|
|
663
705
|
};
|
|
664
706
|
}
|
|
665
|
-
async function ht(
|
|
666
|
-
let
|
|
667
|
-
typeof
|
|
668
|
-
topic:
|
|
707
|
+
async function ht(o, s, t, e, r) {
|
|
708
|
+
let n;
|
|
709
|
+
typeof o == "string" ? n = {
|
|
710
|
+
topic: o,
|
|
669
711
|
topicSource: s,
|
|
670
712
|
councilors: t,
|
|
671
713
|
rounds: e,
|
|
672
714
|
onEvent: r
|
|
673
|
-
} :
|
|
715
|
+
} : n = o;
|
|
674
716
|
const a = (/* @__PURE__ */ new Date()).toISOString(), i = [];
|
|
675
717
|
let l = 0, c = 0;
|
|
676
|
-
const f =
|
|
677
|
-
v.info("conversation", `Starting ${f ? "debate" : "freeform"} — ${
|
|
678
|
-
councilors:
|
|
679
|
-
topic:
|
|
718
|
+
const f = n.mode === "debate", y = {}, h = n.previousTurns?.length ? Math.max(...n.previousTurns.map((g) => g.round)) : 0;
|
|
719
|
+
v.info("conversation", `Starting ${f ? "debate" : "freeform"} — ${n.councilors.length} councilors, ${n.rounds} rounds`, {
|
|
720
|
+
councilors: n.councilors.map((g) => `${g.frontmatter.name} (${g.frontmatter.backend}/${g.frontmatter.model ?? "default"})`),
|
|
721
|
+
topic: n.topic.slice(0, 200)
|
|
680
722
|
});
|
|
681
|
-
for (let
|
|
682
|
-
const L = f &&
|
|
723
|
+
for (let g = 1; g <= n.rounds; g++) {
|
|
724
|
+
const L = f && g > 1 ? gt(n.councilors, g) : n.councilors;
|
|
683
725
|
for (const d of L) {
|
|
684
|
-
if (
|
|
685
|
-
return H(
|
|
686
|
-
if (
|
|
687
|
-
const m = await
|
|
688
|
-
m && (i.push(m),
|
|
726
|
+
if (n.signal?.aborted)
|
|
727
|
+
return H(n, i, a, l, c, y);
|
|
728
|
+
if (n.beforeTurn) {
|
|
729
|
+
const m = await n.beforeTurn();
|
|
730
|
+
m && (i.push(m), n.onEvent({ type: "turn_complete", turn: m }));
|
|
689
731
|
}
|
|
690
|
-
const p =
|
|
691
|
-
|
|
732
|
+
const p = g + h;
|
|
733
|
+
n.onEvent({ type: "turn_start", round: p, councilorName: d.frontmatter.name });
|
|
692
734
|
try {
|
|
693
|
-
const m = await
|
|
735
|
+
const m = await j(d.frontmatter.backend), x = d.frontmatter.model ?? m.defaultModel, Ee = f ? ft(n.topic, i, d.id, g) : pt(n.topic, i, d.id, n.previousTurns, n.previousSummary), q = {
|
|
694
736
|
model: x,
|
|
695
737
|
systemPrompt: d.systemPrompt,
|
|
696
|
-
messages:
|
|
738
|
+
messages: Ee,
|
|
697
739
|
temperature: d.frontmatter.temperature
|
|
698
740
|
};
|
|
699
|
-
let
|
|
741
|
+
let K, N;
|
|
700
742
|
if (m.chatStream) {
|
|
701
|
-
|
|
743
|
+
K = "";
|
|
702
744
|
for await (const I of m.chatStream(q)) {
|
|
703
|
-
if (
|
|
704
|
-
|
|
745
|
+
if (n.signal?.aborted) break;
|
|
746
|
+
K += I.delta, I.delta && n.onEvent({ type: "turn_chunk", councilorName: d.frontmatter.name, delta: I.delta }), I.tokenUsage && (N = I.tokenUsage);
|
|
705
747
|
}
|
|
706
748
|
} else {
|
|
707
749
|
const I = await m.chat(q);
|
|
708
|
-
|
|
750
|
+
K = I.content, N = I.tokenUsage;
|
|
709
751
|
}
|
|
710
752
|
const ee = {
|
|
711
753
|
round: p,
|
|
712
754
|
councilorId: d.id,
|
|
713
755
|
councilorName: d.frontmatter.name,
|
|
714
|
-
content:
|
|
756
|
+
content: K,
|
|
715
757
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
716
758
|
model: x,
|
|
717
759
|
backend: d.frontmatter.backend,
|
|
718
760
|
tokenUsage: N,
|
|
719
761
|
avatarUrl: d.avatarUrl
|
|
720
762
|
};
|
|
721
|
-
N && (l += N.input, c += N.output), i.push(ee),
|
|
763
|
+
N && (l += N.input, c += N.output), i.push(ee), n.onEvent({ type: "turn_complete", turn: ee });
|
|
722
764
|
} catch (m) {
|
|
723
765
|
const x = m instanceof Error ? m.message : String(m);
|
|
724
|
-
v.error("conversation", `Turn failed for ${d.frontmatter.name} (round ${
|
|
766
|
+
v.error("conversation", `Turn failed for ${d.frontmatter.name} (round ${g}, model ${d.frontmatter.model ?? "default"}, backend ${d.frontmatter.backend})`, m), n.onEvent({ type: "error", councilorName: d.frontmatter.name, error: x });
|
|
725
767
|
}
|
|
726
768
|
}
|
|
727
|
-
if (
|
|
769
|
+
if (n.onEvent({ type: "round_complete", round: g }), f && n.config?.secretary?.backend && !n.signal?.aborted)
|
|
728
770
|
try {
|
|
729
|
-
const d = H(
|
|
730
|
-
|
|
731
|
-
const p = await
|
|
771
|
+
const d = H(n, i, a, l, c, y);
|
|
772
|
+
n.onEvent({ type: "round_summary_start", round: g });
|
|
773
|
+
const p = await ct({
|
|
732
774
|
result: d,
|
|
733
|
-
roundNumber:
|
|
734
|
-
config:
|
|
775
|
+
roundNumber: g,
|
|
776
|
+
config: n.config,
|
|
735
777
|
onChunk: (m) => {
|
|
736
|
-
|
|
778
|
+
n.onEvent({ type: "round_summary_chunk", round: g, delta: m });
|
|
737
779
|
},
|
|
738
|
-
signal:
|
|
780
|
+
signal: n.signal
|
|
739
781
|
});
|
|
740
|
-
y[
|
|
782
|
+
y[g] = p, n.onEvent({ type: "round_summary_complete", round: g, summary: p });
|
|
741
783
|
} catch (d) {
|
|
742
|
-
v.error("conversation", `Interim summary failed for round ${
|
|
784
|
+
v.error("conversation", `Interim summary failed for round ${g}`, d);
|
|
743
785
|
}
|
|
744
786
|
}
|
|
745
|
-
return H(
|
|
787
|
+
return H(n, i, a, l, c, y);
|
|
746
788
|
}
|
|
747
789
|
const A = u(b(), ".ai-council", "history");
|
|
748
|
-
function
|
|
749
|
-
return
|
|
790
|
+
function yt(o) {
|
|
791
|
+
return o.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
750
792
|
}
|
|
751
|
-
async function
|
|
793
|
+
async function wt(o) {
|
|
752
794
|
await C(A, { recursive: !0 });
|
|
753
|
-
const s = new Date(
|
|
754
|
-
return await O(r, JSON.stringify(
|
|
795
|
+
const s = new Date(o.startedAt).toISOString().replace(/[:.]/g, "-").slice(0, 19), t = yt(o.topic), e = `${s}-${t}`, r = u(A, `${e}.json`);
|
|
796
|
+
return await O(r, JSON.stringify(o, null, 2), "utf-8"), e;
|
|
755
797
|
}
|
|
756
|
-
async function
|
|
798
|
+
async function bt() {
|
|
757
799
|
await C(A, { recursive: !0 });
|
|
758
|
-
const
|
|
759
|
-
for (const t of
|
|
800
|
+
const o = await V(A), s = [];
|
|
801
|
+
for (const t of o)
|
|
760
802
|
if (t.endsWith(".json"))
|
|
761
803
|
try {
|
|
762
804
|
const e = await w(u(A, t), "utf-8"), r = JSON.parse(e);
|
|
@@ -764,7 +806,7 @@ async function wt() {
|
|
|
764
806
|
id: P(t, ".json"),
|
|
765
807
|
topic: r.topic,
|
|
766
808
|
title: r.title,
|
|
767
|
-
councilors: r.councilors.map((
|
|
809
|
+
councilors: r.councilors.map((n) => n.name),
|
|
768
810
|
rounds: r.rounds,
|
|
769
811
|
startedAt: r.startedAt,
|
|
770
812
|
completedAt: r.completedAt
|
|
@@ -773,59 +815,59 @@ async function wt() {
|
|
|
773
815
|
}
|
|
774
816
|
return s.sort((t, e) => e.startedAt.localeCompare(t.startedAt));
|
|
775
817
|
}
|
|
776
|
-
async function le(
|
|
777
|
-
const s = u(A, `${
|
|
818
|
+
async function le(o) {
|
|
819
|
+
const s = u(A, `${o}.json`), t = await w(s, "utf-8"), e = JSON.parse(t);
|
|
778
820
|
return e.infographic && !e.infographics && (e.infographics = [e.infographic], delete e.infographic), e;
|
|
779
821
|
}
|
|
780
|
-
async function
|
|
781
|
-
const s = u(A, `${
|
|
782
|
-
await
|
|
822
|
+
async function vt(o) {
|
|
823
|
+
const s = u(A, `${o}.json`);
|
|
824
|
+
await M(s);
|
|
783
825
|
}
|
|
784
|
-
async function
|
|
785
|
-
const t = u(A, `${
|
|
826
|
+
async function kt(o, s) {
|
|
827
|
+
const t = u(A, `${o}.json`), e = await w(t, "utf-8"), r = JSON.parse(e);
|
|
786
828
|
r.infographics || (r.infographics = []), r.infographics.push(s), await O(t, JSON.stringify(r, null, 2), "utf-8");
|
|
787
829
|
}
|
|
788
|
-
async function
|
|
789
|
-
const t = u(A, `${
|
|
830
|
+
async function _t(o, s) {
|
|
831
|
+
const t = u(A, `${o}.json`), e = await w(t, "utf-8"), r = JSON.parse(e);
|
|
790
832
|
r.infographics && s >= 0 && s < r.infographics.length && r.infographics.splice(s, 1), await O(t, JSON.stringify(r, null, 2), "utf-8");
|
|
791
833
|
}
|
|
792
|
-
const
|
|
793
|
-
function
|
|
794
|
-
const s =
|
|
834
|
+
const ve = Ke(import.meta.url);
|
|
835
|
+
function St(o) {
|
|
836
|
+
const s = o.councilors.map((e) => e.name).join(", "), t = o.summary ?? o.turns.map((e) => `${e.councilorName}: ${e.content.slice(0, 200)}`).join(`
|
|
795
837
|
`);
|
|
796
838
|
return [
|
|
797
839
|
"Create a professional infographic summarizing a panel discussion.",
|
|
798
|
-
`Topic: ${
|
|
840
|
+
`Topic: ${o.topic.slice(0, 300)}`,
|
|
799
841
|
`Key points: ${t.slice(0, 1500)}`,
|
|
800
842
|
`Panelists: ${s}`,
|
|
801
843
|
"Use a clean, modern design with sections for convergence points, divergence points, and key takeaways.",
|
|
802
844
|
"Include relevant icons and visual hierarchy. Use a horizontal landscape layout."
|
|
803
845
|
].join(" ");
|
|
804
846
|
}
|
|
805
|
-
function
|
|
806
|
-
if (
|
|
807
|
-
const s = !!(
|
|
847
|
+
function Et(o) {
|
|
848
|
+
if (o.infographic?.backend) return o.infographic.backend;
|
|
849
|
+
const s = !!(o.backends.google?.apiKey || process.env.GOOGLE_API_KEY), t = !!(o.backends.openai?.apiKey || process.env.OPENAI_API_KEY);
|
|
808
850
|
return s ? "google" : t ? "openai" : null;
|
|
809
851
|
}
|
|
810
|
-
async function
|
|
811
|
-
const t =
|
|
852
|
+
async function At(o, s) {
|
|
853
|
+
const t = ve("openai").default, n = (await new t({
|
|
812
854
|
apiKey: s.backends.openai?.apiKey || process.env.OPENAI_API_KEY,
|
|
813
855
|
...s.backends.openai?.baseUrl ? { baseURL: s.backends.openai.baseUrl } : {}
|
|
814
856
|
}).images.generate({
|
|
815
857
|
model: "gpt-image-1.5",
|
|
816
|
-
prompt:
|
|
858
|
+
prompt: o,
|
|
817
859
|
quality: "high",
|
|
818
860
|
size: "1536x1024"
|
|
819
861
|
})).data?.[0]?.b64_json;
|
|
820
|
-
if (!
|
|
821
|
-
return
|
|
862
|
+
if (!n) throw new Error("No image data returned from OpenAI");
|
|
863
|
+
return n;
|
|
822
864
|
}
|
|
823
|
-
async function
|
|
824
|
-
const { GoogleGenAI: t } =
|
|
865
|
+
async function It(o, s) {
|
|
866
|
+
const { GoogleGenAI: t } = ve("@google/genai"), e = s.backends.google?.apiKey || process.env.GOOGLE_API_KEY;
|
|
825
867
|
if (!e) throw new Error("No Google API key configured");
|
|
826
868
|
const a = (await new t({ apiKey: e }).models.generateContent({
|
|
827
869
|
model: "gemini-3.1-flash-image-preview",
|
|
828
|
-
contents:
|
|
870
|
+
contents: o,
|
|
829
871
|
config: {
|
|
830
872
|
responseModalities: ["IMAGE", "TEXT"]
|
|
831
873
|
}
|
|
@@ -836,22 +878,22 @@ async function At(n, s) {
|
|
|
836
878
|
return i.inlineData.data;
|
|
837
879
|
throw new Error("No image data in Gemini response");
|
|
838
880
|
}
|
|
839
|
-
async function ue(
|
|
840
|
-
const e = t ??
|
|
881
|
+
async function ue(o, s, t) {
|
|
882
|
+
const e = t ?? Et(s);
|
|
841
883
|
if (!e) throw new Error("No image-capable backend configured (need OpenAI or Google API key)");
|
|
842
|
-
const r =
|
|
843
|
-
return e === "openai" ?
|
|
884
|
+
const r = St(o);
|
|
885
|
+
return e === "openai" ? At(r, s) : It(r, s);
|
|
844
886
|
}
|
|
845
|
-
const
|
|
846
|
-
function
|
|
847
|
-
const s =
|
|
887
|
+
const $t = /https?:\/\/[^\s)<>]+/g;
|
|
888
|
+
function Ot(o) {
|
|
889
|
+
const s = o.match($t);
|
|
848
890
|
return s ? [...new Set(s)] : [];
|
|
849
891
|
}
|
|
850
|
-
async function
|
|
892
|
+
async function Pt(o, s) {
|
|
851
893
|
const t = new AbortController(), e = setTimeout(() => t.abort(), 15e3);
|
|
852
894
|
s && s.addEventListener("abort", () => t.abort(), { once: !0 });
|
|
853
895
|
try {
|
|
854
|
-
const r = await fetch(
|
|
896
|
+
const r = await fetch(o, {
|
|
855
897
|
signal: t.signal,
|
|
856
898
|
headers: {
|
|
857
899
|
"User-Agent": "Council/1.0 (AI Discussion Tool)",
|
|
@@ -860,56 +902,56 @@ async function Ot(n, s) {
|
|
|
860
902
|
redirect: "follow"
|
|
861
903
|
});
|
|
862
904
|
if (clearTimeout(e), !r.ok)
|
|
863
|
-
return { url:
|
|
864
|
-
const
|
|
865
|
-
if (
|
|
905
|
+
return { url: o, content: `[Failed to fetch: HTTP ${r.status}]` };
|
|
906
|
+
const n = r.headers.get("content-type") || "", a = await r.text();
|
|
907
|
+
if (n.includes("application/json"))
|
|
866
908
|
try {
|
|
867
909
|
const c = JSON.stringify(JSON.parse(a), null, 2);
|
|
868
|
-
return { url:
|
|
910
|
+
return { url: o, content: c.slice(0, 8e3) };
|
|
869
911
|
} catch {
|
|
870
|
-
return { url:
|
|
912
|
+
return { url: o, content: a.slice(0, 8e3) };
|
|
871
913
|
}
|
|
872
|
-
if (
|
|
873
|
-
return { url:
|
|
874
|
-
const { title: i, text: l } =
|
|
875
|
-
return { url:
|
|
914
|
+
if (n.includes("text/plain") || n.includes("text/markdown"))
|
|
915
|
+
return { url: o, content: a.slice(0, 8e3) };
|
|
916
|
+
const { title: i, text: l } = Ct(a);
|
|
917
|
+
return { url: o, title: i, content: l.slice(0, 8e3) };
|
|
876
918
|
} catch (r) {
|
|
877
919
|
clearTimeout(e);
|
|
878
|
-
const
|
|
879
|
-
return
|
|
920
|
+
const n = r instanceof Error ? r.message : String(r);
|
|
921
|
+
return n.includes("abort") ? { url: o, content: "[Fetch timed out]" } : { url: o, content: `[Failed to fetch: ${n}]` };
|
|
880
922
|
}
|
|
881
923
|
}
|
|
882
|
-
function
|
|
883
|
-
const s =
|
|
884
|
-
let e =
|
|
924
|
+
function Ct(o) {
|
|
925
|
+
const s = o.match(/<title[^>]*>([\s\S]*?)<\/title>/i), t = s ? de(s[1].trim()) : "";
|
|
926
|
+
let e = o.replace(/<script[\s\S]*?<\/script>/gi, "").replace(/<style[\s\S]*?<\/style>/gi, "").replace(/<nav[\s\S]*?<\/nav>/gi, "").replace(/<header[\s\S]*?<\/header>/gi, "").replace(/<footer[\s\S]*?<\/footer>/gi, "");
|
|
885
927
|
return e = e.replace(/<\/(p|div|h[1-6]|li|tr|blockquote|section|article)>/gi, `
|
|
886
928
|
`), e = e.replace(/<br\s*\/?>/gi, `
|
|
887
929
|
`), e = e.replace(/<li[^>]*>/gi, "• "), e = e.replace(/<[^>]+>/g, " "), e = de(e), e = e.split(`
|
|
888
930
|
`).map((r) => r.replace(/\s+/g, " ").trim()).filter(Boolean).join(`
|
|
889
931
|
`), { title: t, text: e };
|
|
890
932
|
}
|
|
891
|
-
function de(
|
|
892
|
-
return
|
|
933
|
+
function de(o) {
|
|
934
|
+
return o.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'").replace(/ /g, " ").replace(/&#(\d+);/g, (s, t) => String.fromCharCode(Number(t))).replace(/&[a-zA-Z]+;/g, " ");
|
|
893
935
|
}
|
|
894
|
-
async function
|
|
895
|
-
const e =
|
|
896
|
-
if (e.length === 0) return
|
|
936
|
+
async function xt(o, s, t) {
|
|
937
|
+
const e = Ot(o);
|
|
938
|
+
if (e.length === 0) return o;
|
|
897
939
|
v.info("topic-enricher", `Found ${e.length} URL(s) in topic`, { urls: e });
|
|
898
940
|
const r = await Promise.all(
|
|
899
|
-
e.map((a) =>
|
|
941
|
+
e.map((a) => Pt(a, s))
|
|
900
942
|
);
|
|
901
|
-
let
|
|
943
|
+
let n = o;
|
|
902
944
|
for (const a of r) {
|
|
903
945
|
const i = a.title ? `Resource: ${a.title} (${a.url})` : `Resource: ${a.url}`;
|
|
904
|
-
|
|
946
|
+
n += `
|
|
905
947
|
|
|
906
948
|
---
|
|
907
949
|
${i}
|
|
908
950
|
${a.content}`;
|
|
909
951
|
}
|
|
910
|
-
return v.info("topic-enricher", `Enriched topic with ${r.length} resource(s)`),
|
|
952
|
+
return v.info("topic-enricher", `Enriched topic with ${r.length} resource(s)`), n;
|
|
911
953
|
}
|
|
912
|
-
const
|
|
954
|
+
const ke = {
|
|
913
955
|
anthropic: "https://api.anthropic.com",
|
|
914
956
|
openai: "https://api.openai.com/v1",
|
|
915
957
|
google: "https://generativelanguage.googleapis.com",
|
|
@@ -920,7 +962,7 @@ const ve = {
|
|
|
920
962
|
"claude-sonnet-4-20250514",
|
|
921
963
|
"claude-haiku-4-20250414",
|
|
922
964
|
"claude-3-5-haiku-20241022"
|
|
923
|
-
],
|
|
965
|
+
], B = [
|
|
924
966
|
"gemini-2.5-pro",
|
|
925
967
|
"gemini-2.5-flash",
|
|
926
968
|
"gemini-2.0-flash",
|
|
@@ -928,12 +970,12 @@ const ve = {
|
|
|
928
970
|
"gemini-1.5-pro",
|
|
929
971
|
"gemini-1.5-flash"
|
|
930
972
|
];
|
|
931
|
-
async function
|
|
973
|
+
async function Nt(o, s) {
|
|
932
974
|
try {
|
|
933
|
-
switch (
|
|
975
|
+
switch (o) {
|
|
934
976
|
case "ollama": {
|
|
935
977
|
const { Ollama: t } = await import("ollama");
|
|
936
|
-
return { connected: !0, models: (await new t({ host: s.baseUrl ||
|
|
978
|
+
return { connected: !0, models: (await new t({ host: s.baseUrl || ke.ollama }).list()).models.map((a) => a.name).sort() };
|
|
937
979
|
}
|
|
938
980
|
case "openai": {
|
|
939
981
|
const { default: t } = await import("openai");
|
|
@@ -955,39 +997,39 @@ async function xt(n, s) {
|
|
|
955
997
|
}
|
|
956
998
|
case "google": {
|
|
957
999
|
const t = s.apiKey || process.env.GOOGLE_API_KEY || "";
|
|
958
|
-
if (!t) return { connected: !1, models:
|
|
1000
|
+
if (!t) return { connected: !1, models: B, error: "No API key" };
|
|
959
1001
|
const e = await fetch(
|
|
960
1002
|
`https://generativelanguage.googleapis.com/v1beta/models?key=${t}`
|
|
961
1003
|
);
|
|
962
1004
|
if (!e.ok) {
|
|
963
1005
|
const i = (await e.json().catch(() => ({})))?.error?.message || `HTTP ${e.status}`;
|
|
964
|
-
return { connected: !1, models:
|
|
1006
|
+
return { connected: !1, models: B, error: i };
|
|
965
1007
|
}
|
|
966
|
-
const
|
|
967
|
-
return { connected: !0, models:
|
|
1008
|
+
const n = ((await e.json()).models || []).filter((a) => a.name.includes("gemini") && a.supportedGenerationMethods?.includes("generateContent")).map((a) => a.name.replace("models/", "")).sort();
|
|
1009
|
+
return { connected: !0, models: n.length > 0 ? n : B };
|
|
968
1010
|
}
|
|
969
1011
|
default:
|
|
970
|
-
return { connected: !1, models: [], error: `Unknown backend: ${
|
|
1012
|
+
return { connected: !1, models: [], error: `Unknown backend: ${o}` };
|
|
971
1013
|
}
|
|
972
1014
|
} catch (t) {
|
|
973
1015
|
const e = t instanceof Error ? t.message : String(t);
|
|
974
|
-
return { connected: !1, models:
|
|
1016
|
+
return { connected: !1, models: o === "anthropic" ? me : o === "google" ? B : [], error: e };
|
|
975
1017
|
}
|
|
976
1018
|
}
|
|
977
|
-
let S = null,
|
|
978
|
-
function
|
|
979
|
-
|
|
1019
|
+
let S = null, G = [];
|
|
1020
|
+
function Rt(o, s) {
|
|
1021
|
+
o.handle("app:getCouncilDir", async () => {
|
|
980
1022
|
const t = process.env.COUNCIL_CWD || process.cwd();
|
|
981
1023
|
return F(t, "council");
|
|
982
|
-
}),
|
|
1024
|
+
}), o.handle("councilors:list", async (t, e) => {
|
|
983
1025
|
const r = u(b(), ".ai-council", "config.json");
|
|
984
|
-
let
|
|
1026
|
+
let n = { backends: {} };
|
|
985
1027
|
try {
|
|
986
1028
|
const c = await w(r, "utf-8");
|
|
987
|
-
|
|
1029
|
+
n = JSON.parse(c);
|
|
988
1030
|
} catch {
|
|
989
1031
|
}
|
|
990
|
-
const a = ae(
|
|
1032
|
+
const a = ae(n), i = n.councilors ?? {};
|
|
991
1033
|
return (await re(e, a)).map((c) => {
|
|
992
1034
|
const f = i[c.id];
|
|
993
1035
|
return {
|
|
@@ -1004,16 +1046,16 @@ function Nt(n, s) {
|
|
|
1004
1046
|
registryUrl: f?.url
|
|
1005
1047
|
};
|
|
1006
1048
|
});
|
|
1007
|
-
}),
|
|
1008
|
-
const r = u(e, "ABOUT.md"),
|
|
1009
|
-
return { frontmatter: a, body: i.trim(), raw:
|
|
1010
|
-
}),
|
|
1011
|
-
const
|
|
1012
|
-
return await O(
|
|
1013
|
-
}),
|
|
1049
|
+
}), o.handle("councilors:get", async (t, e) => {
|
|
1050
|
+
const r = u(e, "ABOUT.md"), n = await w(r, "utf-8"), { data: a, content: i } = X(n);
|
|
1051
|
+
return { frontmatter: a, body: i.trim(), raw: n };
|
|
1052
|
+
}), o.handle("councilors:save", async (t, e, r) => {
|
|
1053
|
+
const n = u(e, "ABOUT.md");
|
|
1054
|
+
return await O(n, r, "utf-8"), { success: !0 };
|
|
1055
|
+
}), o.handle("councilors:create", async (t, e, r, n) => {
|
|
1014
1056
|
const a = u(e, r);
|
|
1015
|
-
return await C(a, { recursive: !0 }), await O(u(a, "ABOUT.md"),
|
|
1016
|
-
}),
|
|
1057
|
+
return await C(a, { recursive: !0 }), await O(u(a, "ABOUT.md"), n, "utf-8"), { success: !0, dirPath: a };
|
|
1058
|
+
}), o.handle("councilors:delete", async (t, e) => (await M(e, { recursive: !0, force: !0 }), { success: !0 })), o.handle("config:get", async () => {
|
|
1017
1059
|
const t = u(b(), ".ai-council", "config.json");
|
|
1018
1060
|
let e = { backends: {} };
|
|
1019
1061
|
try {
|
|
@@ -1025,23 +1067,30 @@ function Nt(n, s) {
|
|
|
1025
1067
|
ANTHROPIC_API_KEY: !!process.env.ANTHROPIC_API_KEY,
|
|
1026
1068
|
OPENAI_API_KEY: !!process.env.OPENAI_API_KEY,
|
|
1027
1069
|
GOOGLE_API_KEY: !!process.env.GOOGLE_API_KEY
|
|
1028
|
-
},
|
|
1029
|
-
ANTHROPIC_API_KEY:
|
|
1030
|
-
OPENAI_API_KEY:
|
|
1031
|
-
GOOGLE_API_KEY:
|
|
1070
|
+
}, n = (i) => i ? "..." + i.slice(-4) : void 0, a = {
|
|
1071
|
+
ANTHROPIC_API_KEY: n(process.env.ANTHROPIC_API_KEY),
|
|
1072
|
+
OPENAI_API_KEY: n(process.env.OPENAI_API_KEY),
|
|
1073
|
+
GOOGLE_API_KEY: n(process.env.GOOGLE_API_KEY)
|
|
1032
1074
|
};
|
|
1033
|
-
return { config: e, envStatus: r, envKeySuffix: a, defaultUrls:
|
|
1034
|
-
}),
|
|
1075
|
+
return { config: e, envStatus: r, envKeySuffix: a, defaultUrls: ke };
|
|
1076
|
+
}), o.handle("backend:probe", async (t, e, r) => Nt(e, r)), o.handle("backend:models", async (t, e) => {
|
|
1077
|
+
try {
|
|
1078
|
+
const { getBackend: r } = await Promise.resolve().then(() => nt), n = await r(e);
|
|
1079
|
+
return n.listModels ? { models: await n.listModels() } : { models: [], error: "Not supported" };
|
|
1080
|
+
} catch (r) {
|
|
1081
|
+
return { models: [], error: r instanceof Error ? r.message : String(r) };
|
|
1082
|
+
}
|
|
1083
|
+
}), o.handle("config:save", async (t, e) => {
|
|
1035
1084
|
const r = u(b(), ".ai-council");
|
|
1036
|
-
return await C(r, { recursive: !0 }), await O(u(r, "config.json"), JSON.stringify(e, null, 2), "utf-8"),
|
|
1037
|
-
}),
|
|
1085
|
+
return await C(r, { recursive: !0 }), await O(u(r, "config.json"), JSON.stringify(e, null, 2), "utf-8"), we(), { success: !0 };
|
|
1086
|
+
}), o.handle("discussion:start", async (t, e) => {
|
|
1038
1087
|
const r = s();
|
|
1039
1088
|
if (!r) return { error: "No window" };
|
|
1040
|
-
const
|
|
1089
|
+
const n = (a) => {
|
|
1041
1090
|
r.isDestroyed() || r.webContents.send("discussion:event", a);
|
|
1042
1091
|
};
|
|
1043
1092
|
try {
|
|
1044
|
-
S && S.abort(), S = new AbortController(),
|
|
1093
|
+
S && S.abort(), S = new AbortController(), G = [], v.info("ipc:discussion", "Starting discussion", { councilDir: e.councilDir, councilorIds: e.councilorIds, rounds: e.rounds, mode: e.mode });
|
|
1045
1094
|
const a = u(b(), ".ai-council", "config.json");
|
|
1046
1095
|
let i = { backends: {} };
|
|
1047
1096
|
try {
|
|
@@ -1053,99 +1102,99 @@ function Nt(n, s) {
|
|
|
1053
1102
|
v.info("ipc:discussion", `Loading councilors from ${e.councilDir} + ${l.length} registered paths`);
|
|
1054
1103
|
const c = await re(e.councilDir, l), f = e.councilorIds?.length ? c.filter((p) => e.councilorIds.includes(p.id)) : c;
|
|
1055
1104
|
if (v.info("ipc:discussion", `Resolved ${f.length} councilors: ${f.map((p) => p.id).join(", ")}`), f.length === 0) {
|
|
1056
|
-
|
|
1105
|
+
n({ type: "error", councilorName: "", error: "No councilors found" });
|
|
1057
1106
|
return;
|
|
1058
1107
|
}
|
|
1059
|
-
const y = async () =>
|
|
1108
|
+
const y = async () => G.length === 0 ? null : {
|
|
1060
1109
|
round: 0,
|
|
1061
1110
|
councilorId: "__user__",
|
|
1062
1111
|
councilorName: "You",
|
|
1063
|
-
content:
|
|
1112
|
+
content: G.shift(),
|
|
1064
1113
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1065
1114
|
model: "human",
|
|
1066
1115
|
backend: "human"
|
|
1067
|
-
},
|
|
1068
|
-
topic:
|
|
1116
|
+
}, h = await xt(e.topic, S.signal), g = !!e.previousTurns?.length, L = {
|
|
1117
|
+
topic: h,
|
|
1069
1118
|
topicSource: e.topicSource,
|
|
1070
1119
|
councilors: f,
|
|
1071
1120
|
rounds: e.rounds,
|
|
1072
|
-
onEvent:
|
|
1121
|
+
onEvent: n,
|
|
1073
1122
|
beforeTurn: y,
|
|
1074
1123
|
signal: S.signal,
|
|
1075
|
-
mode:
|
|
1124
|
+
mode: g ? "freeform" : e.mode,
|
|
1076
1125
|
config: i,
|
|
1077
1126
|
previousTurns: e.previousTurns,
|
|
1078
1127
|
previousSummary: e.previousSummary
|
|
1079
1128
|
}, d = await ht(L);
|
|
1080
1129
|
if (i.secretary?.backend)
|
|
1081
1130
|
try {
|
|
1082
|
-
|
|
1083
|
-
const p = await
|
|
1131
|
+
n({ type: "summary_start" });
|
|
1132
|
+
const p = await at({
|
|
1084
1133
|
result: d,
|
|
1085
1134
|
config: i,
|
|
1086
1135
|
onChunk: (m) => {
|
|
1087
|
-
|
|
1136
|
+
n({ type: "summary_chunk", delta: m });
|
|
1088
1137
|
},
|
|
1089
1138
|
signal: S?.signal
|
|
1090
1139
|
});
|
|
1091
|
-
d.summary = p.text, p.diagram && (d.diagram = p.diagram),
|
|
1140
|
+
d.summary = p.text, p.diagram && (d.diagram = p.diagram), n({ type: "summary_complete", summary: p.text, diagram: p.diagram });
|
|
1092
1141
|
} catch (p) {
|
|
1093
|
-
v.error("ipc:discussion", "Secretary summary failed", p),
|
|
1142
|
+
v.error("ipc:discussion", "Secretary summary failed", p), n({ type: "error", councilorName: "Secretary", error: p instanceof Error ? p.message : String(p) });
|
|
1094
1143
|
}
|
|
1095
1144
|
if (i.secretary?.backend)
|
|
1096
1145
|
try {
|
|
1097
|
-
const p = d.turns.filter((x) => x.round === 1), m = await
|
|
1146
|
+
const p = d.turns.filter((x) => x.round === 1), m = await lt({
|
|
1098
1147
|
topic: d.topic,
|
|
1099
1148
|
firstRoundTurns: p,
|
|
1100
1149
|
config: i
|
|
1101
1150
|
});
|
|
1102
|
-
d.title = m,
|
|
1151
|
+
d.title = m, n({ type: "title_generated", title: m });
|
|
1103
1152
|
} catch (p) {
|
|
1104
1153
|
v.error("ipc:discussion", "Title generation failed", p);
|
|
1105
1154
|
}
|
|
1106
1155
|
if (e.infographicBackends?.length)
|
|
1107
1156
|
for (const p of e.infographicBackends)
|
|
1108
1157
|
try {
|
|
1109
|
-
|
|
1158
|
+
n({ type: "infographic_start" });
|
|
1110
1159
|
const m = await ue(d, i, p);
|
|
1111
|
-
d.infographics || (d.infographics = []), d.infographics.push(m),
|
|
1160
|
+
d.infographics || (d.infographics = []), d.infographics.push(m), n({ type: "infographic_complete", infographic: m });
|
|
1112
1161
|
} catch (m) {
|
|
1113
|
-
v.error("ipc:discussion", `Infographic generation failed (${p})`, m),
|
|
1162
|
+
v.error("ipc:discussion", `Infographic generation failed (${p})`, m), n({ type: "infographic_error", error: m instanceof Error ? m.message : String(m) });
|
|
1114
1163
|
}
|
|
1115
|
-
e.continuedFrom && (d.continuedFrom = e.continuedFrom),
|
|
1164
|
+
e.continuedFrom && (d.continuedFrom = e.continuedFrom), n({ type: "complete", result: d });
|
|
1116
1165
|
try {
|
|
1117
|
-
await
|
|
1166
|
+
await wt(d);
|
|
1118
1167
|
} catch (p) {
|
|
1119
1168
|
v.error("ipc:discussion", "Failed to save to history", p);
|
|
1120
1169
|
}
|
|
1121
1170
|
} catch (a) {
|
|
1122
|
-
v.error("ipc:discussion", "Discussion failed", a),
|
|
1171
|
+
v.error("ipc:discussion", "Discussion failed", a), n({ type: "error", councilorName: "", error: a instanceof Error ? a.message : String(a) });
|
|
1123
1172
|
} finally {
|
|
1124
1173
|
S = null;
|
|
1125
1174
|
}
|
|
1126
|
-
}),
|
|
1175
|
+
}), o.handle("discussion:stop", async () => (S && (S.abort(), S = null), { success: !0 })), o.handle("discussion:inject", async (t, e) => (G.push(e), { success: !0 })), o.handle("registry:add-local", async (t, e) => ze(e)), o.handle("registry:add-remote", async (t, e) => Ve(e)), o.handle("registry:remove", async (t, e, r) => (await Xe(e, r), { success: !0 })), o.handle("shell:open-in-finder", async (t, e) => {
|
|
1127
1176
|
te.showItemInFolder(u(e, "ABOUT.md"));
|
|
1128
|
-
}),
|
|
1177
|
+
}), o.handle("shell:open-in-terminal", async (t, e) => {
|
|
1129
1178
|
await T(R)("open", ["-a", "Terminal", e]);
|
|
1130
|
-
}),
|
|
1179
|
+
}), o.handle("shell:open-in-editor", async (t, e) => {
|
|
1131
1180
|
const r = T(R);
|
|
1132
1181
|
try {
|
|
1133
1182
|
await r("code", [e]);
|
|
1134
1183
|
} catch {
|
|
1135
1184
|
te.openPath(e);
|
|
1136
1185
|
}
|
|
1137
|
-
}),
|
|
1138
|
-
const
|
|
1186
|
+
}), o.handle("history:list", async () => bt()), o.handle("history:get", async (t, e) => le(e)), o.handle("history:delete", async (t, e) => (await vt(e), { success: !0 })), o.handle("infographic:generate", async (t, e, r) => {
|
|
1187
|
+
const n = u(b(), ".ai-council", "config.json");
|
|
1139
1188
|
let a = { backends: {} };
|
|
1140
1189
|
try {
|
|
1141
|
-
const c = await w(
|
|
1190
|
+
const c = await w(n, "utf-8");
|
|
1142
1191
|
a = JSON.parse(c);
|
|
1143
1192
|
} catch {
|
|
1144
1193
|
}
|
|
1145
1194
|
const i = await le(e), l = await ue(i, a, r);
|
|
1146
|
-
return await
|
|
1147
|
-
}),
|
|
1148
|
-
const r = P(e),
|
|
1195
|
+
return await kt(e, l), { infographic: l };
|
|
1196
|
+
}), o.handle("infographic:delete", async (t, e, r) => (await _t(e, r), { success: !0 })), o.handle("file:read-as-text", async (t, e) => {
|
|
1197
|
+
const r = P(e), n = r.includes(".") ? "." + r.split(".").pop().toLowerCase() : "", a = /* @__PURE__ */ new Set([
|
|
1149
1198
|
".txt",
|
|
1150
1199
|
".md",
|
|
1151
1200
|
".csv",
|
|
@@ -1193,14 +1242,14 @@ function Nt(n, s) {
|
|
|
1193
1242
|
".epub",
|
|
1194
1243
|
".rtf"
|
|
1195
1244
|
]);
|
|
1196
|
-
if (a.has(
|
|
1245
|
+
if (a.has(n))
|
|
1197
1246
|
try {
|
|
1198
1247
|
const l = await w(e, "utf-8");
|
|
1199
1248
|
return { name: r, content: l };
|
|
1200
1249
|
} catch (l) {
|
|
1201
1250
|
return { name: r, content: `[Error reading file: ${l instanceof Error ? l.message : String(l)}]` };
|
|
1202
1251
|
}
|
|
1203
|
-
if (i.has(
|
|
1252
|
+
if (i.has(n)) {
|
|
1204
1253
|
const l = T(R);
|
|
1205
1254
|
try {
|
|
1206
1255
|
const { stdout: c } = await l("markitdown", [e], {
|
|
@@ -1213,12 +1262,12 @@ function Nt(n, s) {
|
|
|
1213
1262
|
const f = c instanceof Error ? c.message : String(c);
|
|
1214
1263
|
return f.includes("ENOENT") ? {
|
|
1215
1264
|
name: r,
|
|
1216
|
-
content: `[Cannot convert ${
|
|
1265
|
+
content: `[Cannot convert ${n} file: markitdown is not installed. Run: pip install 'markitdown[all]']`
|
|
1217
1266
|
} : { name: r, content: `[Error converting file: ${f}]` };
|
|
1218
1267
|
}
|
|
1219
1268
|
}
|
|
1220
1269
|
return { name: r, content: `[Unsupported file type: ${r}]` };
|
|
1221
|
-
}),
|
|
1270
|
+
}), o.handle("markitdown:check", async () => {
|
|
1222
1271
|
const t = T(R);
|
|
1223
1272
|
try {
|
|
1224
1273
|
const { stdout: e } = await t("markitdown", ["--version"], { timeout: 5e3 });
|
|
@@ -1226,7 +1275,7 @@ function Nt(n, s) {
|
|
|
1226
1275
|
} catch {
|
|
1227
1276
|
return { installed: !1 };
|
|
1228
1277
|
}
|
|
1229
|
-
}),
|
|
1278
|
+
}), o.handle("markitdown:install", async () => {
|
|
1230
1279
|
const t = T(R);
|
|
1231
1280
|
try {
|
|
1232
1281
|
return await t("pip", ["install", "markitdown[all]"], {
|
|
@@ -1236,29 +1285,29 @@ function Nt(n, s) {
|
|
|
1236
1285
|
} catch (e) {
|
|
1237
1286
|
return { success: !1, error: e instanceof Error ? e.message : String(e) };
|
|
1238
1287
|
}
|
|
1239
|
-
}),
|
|
1288
|
+
}), o.handle("dialog:selectDirectory", async () => {
|
|
1240
1289
|
const t = s();
|
|
1241
1290
|
if (!t) return null;
|
|
1242
|
-
const e = await
|
|
1291
|
+
const e = await Ae.showOpenDialog(t, {
|
|
1243
1292
|
properties: ["openDirectory"]
|
|
1244
1293
|
});
|
|
1245
1294
|
return e.canceled ? null : e.filePaths[0];
|
|
1246
1295
|
});
|
|
1247
1296
|
}
|
|
1248
|
-
const
|
|
1249
|
-
|
|
1250
|
-
const
|
|
1251
|
-
function $(
|
|
1252
|
-
const t = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${
|
|
1297
|
+
const Tt = Pe(import.meta.url), J = Oe(Tt), _e = u(b(), ".ai-council");
|
|
1298
|
+
xe(_e, { recursive: !0 });
|
|
1299
|
+
const Se = u(_e, "electron-debug.log");
|
|
1300
|
+
function $(o, ...s) {
|
|
1301
|
+
const t = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${o}] ${s.map((e) => typeof e == "string" ? e : JSON.stringify(e)).join(" ")}
|
|
1253
1302
|
`;
|
|
1254
|
-
|
|
1303
|
+
Re(Se, t);
|
|
1255
1304
|
}
|
|
1256
|
-
|
|
1305
|
+
Ne(Se, `=== State Change Council Electron — started ${(/* @__PURE__ */ new Date()).toISOString()} ===
|
|
1257
1306
|
`);
|
|
1258
1307
|
D.name = "State Change Council";
|
|
1259
1308
|
let E = null;
|
|
1260
1309
|
function pe() {
|
|
1261
|
-
$("main", "Creating BrowserWindow"), E = new
|
|
1310
|
+
$("main", "Creating BrowserWindow"), E = new ge({
|
|
1262
1311
|
width: 1200,
|
|
1263
1312
|
height: 800,
|
|
1264
1313
|
minWidth: 800,
|
|
@@ -1270,17 +1319,17 @@ function pe() {
|
|
|
1270
1319
|
nodeIntegration: !1,
|
|
1271
1320
|
preload: u(J, "preload.mjs")
|
|
1272
1321
|
}
|
|
1273
|
-
}), E.webContents.on("console-message", (s, t, e, r,
|
|
1322
|
+
}), E.webContents.on("console-message", (s, t, e, r, n) => {
|
|
1274
1323
|
const a = ["DEBUG", "INFO", "WARN", "ERROR"][t] || "LOG";
|
|
1275
|
-
$(`renderer:${a}`, `${e} (${
|
|
1324
|
+
$(`renderer:${a}`, `${e} (${n}:${r})`);
|
|
1276
1325
|
}), E.webContents.on("render-process-gone", (s, t) => {
|
|
1277
1326
|
$("main:CRASH", "Renderer process gone:", t);
|
|
1278
1327
|
}), E.webContents.on("did-fail-load", (s, t, e) => {
|
|
1279
1328
|
$("main:LOAD_ERROR", `Failed to load: ${t} ${e}`);
|
|
1280
1329
|
});
|
|
1281
|
-
const
|
|
1282
|
-
if (
|
|
1283
|
-
$("main", `Loading dev server URL: ${
|
|
1330
|
+
const o = process.env.VITE_DEV_SERVER_URL;
|
|
1331
|
+
if (o)
|
|
1332
|
+
$("main", `Loading dev server URL: ${o}`), E.loadURL(o);
|
|
1284
1333
|
else {
|
|
1285
1334
|
const s = u(J, "../dist-renderer/index.html");
|
|
1286
1335
|
$("main", `Loading file: ${s}`), E.loadFile(s);
|
|
@@ -1294,19 +1343,19 @@ fe.registerSchemesAsPrivileged([
|
|
|
1294
1343
|
]);
|
|
1295
1344
|
D.whenReady().then(() => {
|
|
1296
1345
|
$("main", "App ready, registering IPC handlers");
|
|
1297
|
-
const
|
|
1346
|
+
const o = "State Change Council", s = [
|
|
1298
1347
|
{
|
|
1299
|
-
label:
|
|
1348
|
+
label: o,
|
|
1300
1349
|
submenu: [
|
|
1301
|
-
{ role: "about", label: `About ${
|
|
1350
|
+
{ role: "about", label: `About ${o}` },
|
|
1302
1351
|
{ type: "separator" },
|
|
1303
1352
|
{ role: "services" },
|
|
1304
1353
|
{ type: "separator" },
|
|
1305
|
-
{ role: "hide", label: `Hide ${
|
|
1354
|
+
{ role: "hide", label: `Hide ${o}` },
|
|
1306
1355
|
{ role: "hideOthers" },
|
|
1307
1356
|
{ role: "unhide" },
|
|
1308
1357
|
{ type: "separator" },
|
|
1309
|
-
{ role: "quit", label: `Quit ${
|
|
1358
|
+
{ role: "quit", label: `Quit ${o}` }
|
|
1310
1359
|
]
|
|
1311
1360
|
},
|
|
1312
1361
|
{ role: "fileMenu" },
|
|
@@ -1316,9 +1365,9 @@ D.whenReady().then(() => {
|
|
|
1316
1365
|
];
|
|
1317
1366
|
ne.setApplicationMenu(ne.buildFromTemplate(s)), fe.handle("council-file", (t) => {
|
|
1318
1367
|
const e = decodeURIComponent(t.url.replace("council-file://", ""));
|
|
1319
|
-
return
|
|
1320
|
-
}),
|
|
1321
|
-
|
|
1368
|
+
return Ie.fetch(Ce(e).href);
|
|
1369
|
+
}), Rt($e, () => E), pe(), D.on("activate", () => {
|
|
1370
|
+
ge.getAllWindows().length === 0 && pe();
|
|
1322
1371
|
});
|
|
1323
1372
|
});
|
|
1324
1373
|
D.on("window-all-closed", () => {
|