ai_co_discuss 0.3.6 → 0.3.8
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/package.json +1 -1
- package/runner/discussion_setup.mjs +52 -4
- package/runner/discussion_skills.mjs +42 -8
- package/runner/scheduler.mjs +27 -25
- package/runner/session_driver.mjs +37 -12
- package/web/app.js +25 -5
- package/web/modern.js +90 -71
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@ import { parseArgs } from 'node:util';
|
|
|
9
9
|
import { atomic_json } from './discussion_adapters.mjs';
|
|
10
10
|
import { launch_settings, save_launch_settings } from './discussion_launch.mjs';
|
|
11
11
|
import { assert_shared_home, service_home } from './discussion_service.mjs';
|
|
12
|
-
import { install_skills } from './discussion_skills.mjs';
|
|
12
|
+
import { SKILLS, install_skills, remove_legacy_skills } from './discussion_skills.mjs';
|
|
13
13
|
import { ask, checkbox_prompt, intro, outro, pad, short_path, spinner, step, styled, text_width, tone } from './discussion_tui.mjs';
|
|
14
14
|
export { checkbox_prompt } from './discussion_tui.mjs';
|
|
15
15
|
|
|
@@ -22,10 +22,13 @@ const ATTENTION_EXIT_CODE = 2;
|
|
|
22
22
|
const AGY_SETTINGS = ['.gemini', 'antigravity-cli', 'settings.json'];
|
|
23
23
|
const AGY_RULE_RECORD = 'agy-permission.json';
|
|
24
24
|
const AGY_SETTINGS_BACKUP = 'agy-settings.backup.json';
|
|
25
|
+
const SKILL_RECORD = 'skills-manifest.json';
|
|
26
|
+
const AGY_SKILL_CONFIG = ['.gemini', 'config', 'skills.json'];
|
|
25
27
|
const CLIENTS = {
|
|
26
28
|
codex: { name: 'Codex', commands: ['codex'], adapter: 'codex_cli', skills: ['.agents', 'skills'] },
|
|
27
29
|
claude: { name: 'Claude', commands: ['claude'], adapter: 'claude', skills: ['.claude', 'skills'] },
|
|
28
|
-
antigravity: { name: 'Gemini', commands: ['agy'], adapter: 'antigravity_cli', skills: ['.gemini', '
|
|
30
|
+
antigravity: { name: 'Gemini', commands: ['agy'], adapter: 'antigravity_cli', skills: ['.gemini', 'config', 'skills'],
|
|
31
|
+
legacy_skills: ['.gemini', 'antigravity-cli', 'skills'] },
|
|
29
32
|
cursor: { name: 'Grok(Cursor CLI)', commands: ['agent', 'cursor-agent'], skills: ['.cursor', 'skills'] },
|
|
30
33
|
};
|
|
31
34
|
const OPTIONS = { yes: { type: 'boolean' }, clients: { type: 'string' }, python: { type: 'string' },
|
|
@@ -222,16 +225,58 @@ export function remove_legacy_agy_rules(home, user_home) {
|
|
|
222
225
|
return { client: 'Gemini', status: next.length !== allow.length ? 'removed' : 'unchanged', path };
|
|
223
226
|
}
|
|
224
227
|
|
|
228
|
+
/** Follow a linked skill directory so clients sharing one folder install a single copy. */
|
|
229
|
+
function real_directory(path) {
|
|
230
|
+
try { return realpathSync.native(path); } catch { return resolve(path); }
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** True when an entry's include_only/exclude patterns let agy load every discussion skill; invalid patterns count as filtering. */
|
|
234
|
+
function entry_loads_skills(entry) {
|
|
235
|
+
const matches = (patterns, name) => patterns.some(pattern => {
|
|
236
|
+
try { return new RegExp(`^(?:${pattern})$`).test(name); } catch { return true; }
|
|
237
|
+
});
|
|
238
|
+
const include = Array.isArray(entry.include_only) ? entry.include_only : null;
|
|
239
|
+
const exclude = Array.isArray(entry.exclude) ? entry.exclude : [];
|
|
240
|
+
return SKILLS.every(name => (!include || matches(include, name)) && !matches(exclude, name));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** Absolute directories agy loads the discussion skills from through its global skills.json; agy leaves ~/ unexpanded on Windows. */
|
|
244
|
+
function agy_skill_entries(user_home) {
|
|
245
|
+
const path = join(user_home, ...AGY_SKILL_CONFIG);
|
|
246
|
+
let config;
|
|
247
|
+
try { config = JSON.parse(readFileSync(path, 'utf8')); } catch { return []; }
|
|
248
|
+
return (Array.isArray(config?.entries) ? config.entries : [])
|
|
249
|
+
.filter(entry => typeof entry?.path === 'string' && entry_loads_skills(entry)).map(entry => entry.path)
|
|
250
|
+
.map(value => process.platform !== 'win32' && value.startsWith('~/') ? join(user_home, value.slice(2)) : value)
|
|
251
|
+
.filter(value => isAbsolute(value)).map(real_directory);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Pick the directory each client really reads; agy reuses a skills.json entry instead of its own folder. */
|
|
255
|
+
function skill_targets(selected, user_home) {
|
|
256
|
+
const own = new Map(selected.map(client => [client.key, real_directory(join(user_home, ...client.skills))]));
|
|
257
|
+
const entries = agy_skill_entries(user_home);
|
|
258
|
+
return selected.map(client => {
|
|
259
|
+
const directory = own.get(client.key);
|
|
260
|
+
if (client.key !== 'antigravity' || !entries.length) return { client, directory, own: directory };
|
|
261
|
+
const others = selected.filter(item => item.key !== client.key).map(item => own.get(item.key));
|
|
262
|
+
return { client, directory: entries.find(entry => others.includes(entry)) || entries[0], own: directory };
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
225
266
|
/** Save the selected effective profiles and copy complete skills without overwriting edits. */
|
|
226
267
|
async function configure_clients(home, selected, options, existing) {
|
|
227
268
|
const participants = { ...existing };
|
|
228
269
|
const skills = [], profiles = [];
|
|
229
270
|
const user_home = resolve(options['skills-home'] || homedir());
|
|
230
|
-
|
|
271
|
+
const record = join(home, SKILL_RECORD), installed = new Map();
|
|
272
|
+
for (const { client, directory, own } of skill_targets(selected, user_home)) {
|
|
231
273
|
const profile = await choose_profile(client, participants[client.name], options);
|
|
232
274
|
profiles.push(profile);
|
|
233
275
|
if (profile.status === 'configured') participants[client.name] = { ...participants[client.name], ...profile.effective };
|
|
234
|
-
|
|
276
|
+
if (!installed.has(directory)) installed.set(directory, install_skills(directory, record));
|
|
277
|
+
const retired = [client.legacy_skills && real_directory(join(user_home, ...client.legacy_skills)), own]
|
|
278
|
+
.filter(path => path && path !== directory);
|
|
279
|
+
skills.push({ client: client.name, results: installed.get(directory), legacy: retired.flatMap(path => remove_legacy_skills(path, record)) });
|
|
235
280
|
}
|
|
236
281
|
save_launch_settings(home, { participants });
|
|
237
282
|
const legacy = remove_legacy_agy_rules(home, user_home);
|
|
@@ -273,6 +318,9 @@ function setup_issues(result) {
|
|
|
273
318
|
for (const group of result.skills) for (const skill of group.results) {
|
|
274
319
|
if (skill.status === 'conflict') issues.push(`${group.client} 技能冲突,已保留 ${skill.path};请核对并移走需替换的旧副本,再运行 setup。`);
|
|
275
320
|
}
|
|
321
|
+
for (const group of result.skills) for (const skill of group.legacy) {
|
|
322
|
+
if (skill.status === 'kept') issues.push(`${group.client} 旧技能目录中的副本有改动,已保留 ${skill.path};软件会同时加载新旧两份,请核对后删除。`);
|
|
323
|
+
}
|
|
276
324
|
for (const permission of result.permissions) {
|
|
277
325
|
if (permission.status === 'invalid') issues.push(`${permission.client} 设置文件无法解析,未能移除旧版全局放行规则:${permission.path};修复后重新运行 setup,否则全自动讨论会拒绝交付给 agy。`);
|
|
278
326
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
/** Install complete skills without replacing an unmanaged or locally edited copy. */
|
|
1
|
+
/** Install complete skills without replacing an unmanaged or locally edited copy; hashes live in the service home. */
|
|
2
2
|
import { createHash } from 'node:crypto';
|
|
3
3
|
import { cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, rmSync } from 'node:fs';
|
|
4
|
-
import { join, resolve } from 'node:path';
|
|
4
|
+
import { dirname, join, resolve } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
6
|
|
|
7
7
|
import { atomic_json } from './discussion_adapters.mjs';
|
|
8
8
|
|
|
9
9
|
const SOURCE = fileURLToPath(new URL('../.agents/skills/', import.meta.url));
|
|
10
|
-
const SKILLS = ['discuss-host', 'discuss-participant', 'discuss-report'];
|
|
11
|
-
const
|
|
10
|
+
export const SKILLS = ['discuss-host', 'discuss-participant', 'discuss-report'];
|
|
11
|
+
const LEGACY_MANIFEST = '.codiscuss-skills.json';
|
|
12
12
|
|
|
13
13
|
/** Hash names and contents, rejecting links instead of following them outside a skill. */
|
|
14
14
|
function tree_hash(directory) {
|
|
@@ -41,15 +41,49 @@ function install_skill(root, name, previous) {
|
|
|
41
41
|
return { name, status: previous ? 'updated' : 'installed', path: destination, hash };
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/** Read hashes recorded for one directory, folding in the copy older versions kept inside it. */
|
|
45
|
+
function recorded_hashes(record, root) {
|
|
46
|
+
const data = existsSync(record) ? JSON.parse(readFileSync(record, 'utf8')) : {};
|
|
47
|
+
const legacy = join(root, LEGACY_MANIFEST);
|
|
48
|
+
return { data, previous: { ...(existsSync(legacy) ? JSON.parse(readFileSync(legacy, 'utf8')) : {}), ...data[root] } };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Save the record outside skill directories and drop the in-directory file older versions wrote. */
|
|
52
|
+
function save_record(record, root, data) {
|
|
53
|
+
mkdirSync(dirname(record), { recursive: true });
|
|
54
|
+
atomic_json(record, data);
|
|
55
|
+
rmSync(join(root, LEGACY_MANIFEST), { force: true });
|
|
56
|
+
}
|
|
57
|
+
|
|
44
58
|
/** Install all three skills together and remember only copies this installer manages. */
|
|
45
|
-
export function install_skills(directory) {
|
|
59
|
+
export function install_skills(directory, record) {
|
|
46
60
|
const root = resolve(directory);
|
|
47
61
|
mkdirSync(root, { recursive: true });
|
|
48
|
-
const
|
|
49
|
-
const previous = existsSync(manifest) ? JSON.parse(readFileSync(manifest, 'utf8')) : {};
|
|
62
|
+
const { data, previous } = recorded_hashes(record, root);
|
|
50
63
|
const results = SKILLS.map(name => install_skill(root, name, previous[name]));
|
|
51
64
|
const managed = { ...previous };
|
|
52
65
|
for (const result of results) if (result.hash) managed[result.name] = result.hash;
|
|
53
|
-
|
|
66
|
+
save_record(record, root, { ...data, [root]: managed });
|
|
54
67
|
return results.map(({ hash, ...result }) => result);
|
|
55
68
|
}
|
|
69
|
+
|
|
70
|
+
/** Remove unmodified copies a previous install left in a retired directory; edited copies stay. */
|
|
71
|
+
export function remove_legacy_skills(directory, record) {
|
|
72
|
+
const root = resolve(directory);
|
|
73
|
+
if (!existsSync(root)) return [];
|
|
74
|
+
const { data, previous } = recorded_hashes(record, root);
|
|
75
|
+
if (!Object.keys(previous).length) return [];
|
|
76
|
+
const results = SKILLS.filter(name => previous[name] && existsSync(join(root, name))).map(name => {
|
|
77
|
+
const path = join(root, name);
|
|
78
|
+
let current;
|
|
79
|
+
try { current = tree_hash(path); } catch { return { name, status: 'kept', path }; }
|
|
80
|
+
if (current !== previous[name]) return { name, status: 'kept', path };
|
|
81
|
+
rmSync(path, { recursive: true });
|
|
82
|
+
return { name, status: 'removed', path };
|
|
83
|
+
});
|
|
84
|
+
const kept = Object.fromEntries(results.filter(result => result.status === 'kept').map(result => [result.name, previous[result.name]]));
|
|
85
|
+
const { [root]: _, ...rest } = data;
|
|
86
|
+
save_record(record, root, Object.keys(kept).length ? { ...rest, [root]: kept } : rest);
|
|
87
|
+
if (!readdirSync(root).length) rmSync(root, { recursive: true });
|
|
88
|
+
return results;
|
|
89
|
+
}
|
package/runner/scheduler.mjs
CHANGED
|
@@ -12,7 +12,9 @@ export const ROUTE_CONFIDENCE = 0.7;
|
|
|
12
12
|
/** Waking everyone or no one costs more when wrong (N runs, or an ignored message), so it needs more certainty. */
|
|
13
13
|
export const ROUTE_WIDE_CONFIDENCE = 0.85;
|
|
14
14
|
/** Bump when the routing question or its option descriptions change, so ratings can be compared across wordings. */
|
|
15
|
-
export const ROUTE_PROMPT_VERSION =
|
|
15
|
+
export const ROUTE_PROMPT_VERSION = 7;
|
|
16
|
+
/** The same for the three discussion questions (interjection, next speaker, next round). */
|
|
17
|
+
export const DISCUSSION_PROMPT_VERSION = 2;
|
|
16
18
|
const HOST_CHOICE = '__host__';
|
|
17
19
|
const NOTE_CHOICE = '__note__';
|
|
18
20
|
const ALL_CHOICE = '__all__';
|
|
@@ -101,6 +103,11 @@ function judged_of(state, questions, answers) {
|
|
|
101
103
|
return { detail: detail_of(questions, answers), input: { state, questions } };
|
|
102
104
|
}
|
|
103
105
|
|
|
106
|
+
/** Who has and has not spoken this round, so a decision knows who the plan still owes a turn. */
|
|
107
|
+
function round_progress(spoken, waiting) {
|
|
108
|
+
return `本轮已发言:${spoken.join('、') || '无'};本轮尚未发言:${waiting.join('、') || '无'}。`;
|
|
109
|
+
}
|
|
110
|
+
|
|
104
111
|
/** Say which option the provider leaned to when it was not sure enough, so the transcript shows how close it was. */
|
|
105
112
|
function unsure_label(lean) {
|
|
106
113
|
return lean ? `倾向${lean},把握不足,交给主持人` : '拿不准,交给主持人';
|
|
@@ -152,7 +159,7 @@ export async function route_free_message(home, scheduler, { title, goal, context
|
|
|
152
159
|
{ value: ALL_CHOICE, description: '全体成员(含主持人)各自回应一次,彼此不需要看到别人的回答;只在要求简单明确、每人独立回应一次就算完成时选,如“大家打个招呼”“各自报一下在用的模型”' },
|
|
153
160
|
{ value: NONE_CHOICE, description: '无需回应:只是确认、致谢或旁注,不需要任何人回答' }];
|
|
154
161
|
const questions = [{ type: 'choice', name: 'handler', choices,
|
|
155
|
-
instructions: '这条用户消息没有用 @ 点名成员。判断它的回应对象,按下面的顺序:1)正文叫了某位成员的名字,或在向其提问(包括“X在吗”“X还在吗”“X
|
|
162
|
+
instructions: '这条用户消息没有用 @ 点名成员。判断它的回应对象,按下面的顺序:1)正文叫了某位成员的名字,或在向其提问(包括“X在吗”“X还在吗”“X你怎么看”),选该成员,即使上一条是别人的回答;同时提到几位成员时,选被直接称呼、要求其回应或做事的那位(如“A 你让 B 去改”是在对 A 说);2)正文没有指向任何人时,看它是否在延续某位成员的回答(追问、要求修改、引用,或“回复”指向其消息),或在补充、纠正一条正由某位成员处理的消息,是则选该成员,是主持人就选主持人;3)符合“全体”选项的条件时选全体;只出现“大家”“都”“各自”“一起”等字眼不够;4)单独的问候或闲聊(如“hi”)既没有指向某位成员,也没有要求大家一起回应,选主持人;5)只是确认、致谢或旁注选无需回应;6)对象不明确,或要讨论、互相回应、分工、排先后时选主持人,由主持人安排。' }];
|
|
156
163
|
const roster = [`${host.name}(主持人)`, ...members.map(member => member.name)].join('、');
|
|
157
164
|
const state = scheduler_state(title, context, message, goal, '', { roster }, ROUTE_CONTEXT_MESSAGES);
|
|
158
165
|
const answers = await decide(home, scheduler, state, questions, fetch_impl);
|
|
@@ -187,12 +194,13 @@ export async function decide_group_order(home, scheduler, { title, goal, context
|
|
|
187
194
|
* Decide one interjection in a discussion: continue the plan (note), ask one expert (ask) or wake the host (host).
|
|
188
195
|
* Anything but a confident note or ask is returned as host.
|
|
189
196
|
*/
|
|
190
|
-
export async function decide_interjection(home, scheduler, { title, goal, user_request, context, message, host, experts, ...extra }, fetch_impl) {
|
|
197
|
+
export async function decide_interjection(home, scheduler, { title, goal, user_request, context, message, host, experts, spoken = [], ...extra }, fetch_impl) {
|
|
198
|
+
// Only experts still owed a turn this round can be asked: asking one who has spoken would open a new round.
|
|
191
199
|
const choices = [...experts.map(name => ({ value: name, description: `请 ${name} 回应这条留言:留言是向该专家(或其负责的内容)提问、质疑或补充要求` })),
|
|
192
|
-
{ value: HOST_CHOICE, description: `由主持人 ${host}
|
|
193
|
-
{ value: NOTE_CHOICE, description: '
|
|
200
|
+
{ value: HOST_CHOICE, description: `由主持人 ${host} 处理:留言要改变讨论目标、规则、分工、安排或结束讨论,同时涉及多位专家,或针对的是不在选项里的人(如本轮已发言的专家、主持人自己)` },
|
|
201
|
+
{ value: NOTE_CHOICE, description: '仅补充信息:留言补充背景、约束或偏好,或只是旁注、鼓励,没有要求某人立即回应;后续发言的专家都能看到它,按当前安排继续' }];
|
|
194
202
|
const questions = [{ type: 'choice', name: 'handling', instructions: '讨论进行中用户发来这条留言。对照当前焦点、主持简报和安排,判断它最适合怎样处理。', choices }];
|
|
195
|
-
const state = scheduler_state(title, context, message, goal, user_request, extra);
|
|
203
|
+
const state = scheduler_state(title, context, message, goal, user_request, { ...extra, progress: round_progress(spoken, experts) });
|
|
196
204
|
const answers = await decide(home, scheduler, state, questions, fetch_impl);
|
|
197
205
|
const judged = judged_of(state, questions, answers);
|
|
198
206
|
const choice = answers.handling?.choice;
|
|
@@ -210,27 +218,19 @@ export async function decide_interjection(home, scheduler, { title, goal, user_r
|
|
|
210
218
|
*/
|
|
211
219
|
export async function decide_next_speaker(home, scheduler, { title, goal, user_request, context, host, candidates, spoken = [], ...extra }, fetch_impl) {
|
|
212
220
|
const recent = context.slice(-CONTEXT_MESSAGES).map(excerpt);
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
const choices = [...candidates.map(name => ({ value: name, description: `请 ${name}
|
|
216
|
-
{ value: HOST_CHOICE, description: `交给主持人 ${host}
|
|
217
|
-
// Having not spoken yet is no reason to speak: the host's plan may have ended on purpose, so the scheduler acts only on an explicit obligation.
|
|
221
|
+
const state = [topic_lines(title, goal, user_request, { ...extra, progress: round_progress(spoken, candidates) }), recent.length ? `本轮及最近发言(旧→新):\n${recent.join('\n')}` : '还没有发言。'].join('\n\n');
|
|
222
|
+
// Having not spoken yet is no reason to speak: the host's plan may have ended on purpose, so a candidate needs an explicit basis.
|
|
223
|
+
const choices = [...candidates.map(name => ({ value: name, description: `请 ${name} 接着发言:简报的分工或流程规定轮到 ${name},或最近的发言点名追问了 ${name}` })),
|
|
224
|
+
{ value: HOST_CHOICE, description: `交给主持人 ${host}:简报和最近发言都没有明确规定下一位(仅仅是本轮还没发言不算依据),或需要主持人重新安排` }];
|
|
218
225
|
const questions = [{ type: 'choice', name: 'speaker',
|
|
219
|
-
instructions: '
|
|
220
|
-
{ type: 'predicate', name: 'obligation',
|
|
221
|
-
instructions: `是否存在明确依据要求 ${candidates.join('、')} 之一接着发言:简报分工或流程规定轮到其人,或最近发言点名追问了其人?仅仅是“本轮还没发言”不算依据。` }];
|
|
226
|
+
instructions: '主持人预排的安排已用完或被取消,主持人可能有意只安排到这里。对照主持简报、当前焦点和主持人最近的安排,判断下一位应请谁发言。', choices }];
|
|
222
227
|
const answers = await decide(home, scheduler, state, questions, fetch_impl);
|
|
223
228
|
const judged = judged_of(state, questions, answers);
|
|
224
229
|
const choice = answers.speaker?.choice;
|
|
225
230
|
const confidence = certainty(answers.speaker);
|
|
226
|
-
const
|
|
227
|
-
if (candidates.includes(choice)
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
if (typeof obligation === 'number' && obligation < ROUTE_CONFIDENCE) {
|
|
231
|
-
return { action: 'host', confidence, ...judged, label: `有明确依据的概率 ${obligation.toFixed(2)},未达 ${ROUTE_CONFIDENCE.toFixed(2)},交给主持人安排` };
|
|
232
|
-
}
|
|
233
|
-
return { action: 'host', confidence, ...judged, label: choice === HOST_CHOICE && confidence >= ROUTE_CONFIDENCE ? '建议由主持人调整安排,交给主持人' : unsure_label(candidates.includes(choice) ? `请 ${choice} 发言` : choice === HOST_CHOICE ? '交给主持人' : '') };
|
|
231
|
+
const sure = confidence !== null && confidence >= ROUTE_CONFIDENCE;
|
|
232
|
+
if (sure && candidates.includes(choice)) return { action: 'next', expert: choice, confidence, ...judged, label: `本轮继续,请 ${choice} 接着发言` };
|
|
233
|
+
return { action: 'host', confidence, ...judged, label: choice === HOST_CHOICE && sure ? '没有明确该谁发言,交给主持人安排' : unsure_label(candidates.includes(choice) ? `请 ${choice} 发言` : choice === HOST_CHOICE ? '交给主持人' : '') };
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
/**
|
|
@@ -239,7 +239,8 @@ export async function decide_next_speaker(home, scheduler, { title, goal, user_r
|
|
|
239
239
|
*/
|
|
240
240
|
export async function decide_round(home, scheduler, { title, goal, user_request, context, host, experts, last_speaker, ...extra }, fetch_impl) {
|
|
241
241
|
const candidates = experts.filter(name => name !== last_speaker);
|
|
242
|
-
|
|
242
|
+
// Telling repetition or drift needs at least the last two rounds side by side.
|
|
243
|
+
const recent = context.slice(-ROUTE_CONTEXT_MESSAGES).map(excerpt);
|
|
243
244
|
const state = [topic_lines(title, goal, user_request, extra), recent.length ? `本轮及最近发言(旧→新):\n${recent.join('\n')}` : '还没有发言。'].join('\n\n');
|
|
244
245
|
const questions = [{ type: 'choice', name: 'next', instructions: '一轮发言刚结束。对照讨论目标、主持简报与当前焦点判断:下一轮是否还有明确的内容可以让大家互相回应、追问或补充?', choices: [
|
|
245
246
|
{ value: 'continue', description: '继续下一轮:最近发言里有未回应的分歧、追问或可展开的点,或简报规定的流程还没走完' },
|
|
@@ -250,8 +251,9 @@ export async function decide_round(home, scheduler, { title, goal, user_request,
|
|
|
250
251
|
const judged = judged_of(state, questions, answers);
|
|
251
252
|
const choice = answers.next?.choice;
|
|
252
253
|
const confidence = certainty(answers.next);
|
|
253
|
-
|
|
254
|
-
|
|
254
|
+
// A wrong "continue" costs a whole round of runs, so it needs the same certainty as waking everyone.
|
|
255
|
+
if (choice !== 'continue' || confidence === null || confidence < ROUTE_WIDE_CONFIDENCE || !candidates.length) {
|
|
256
|
+
const sure = confidence !== null && confidence >= (choice === 'continue' ? ROUTE_WIDE_CONFIDENCE : ROUTE_CONFIDENCE);
|
|
255
257
|
return { action: 'host', confidence, ...judged, label: choice === HOST_CHOICE && sure ? '建议收束或调整方向,交给主持人' : unsure_label(choice === 'continue' ? '继续下一轮' : choice === HOST_CHOICE ? '收束或调整方向' : '') };
|
|
256
258
|
}
|
|
257
259
|
const first = candidates.includes(answers.first?.choice) ? answers.first.choice : candidates[0];
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
/** Smart scheduling and limit checks for a discussion hosted inside a free room. */
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
2
3
|
import { api, peer_round_question } from './discussion_runner.mjs';
|
|
3
|
-
import { decide_interjection, decide_next_speaker, decide_round } from './scheduler.mjs';
|
|
4
|
+
import { DISCUSSION_PROMPT_VERSION, ROUTE_CONFIDENCE, ROUTE_CONTEXT_MESSAGES, ROUTE_WIDE_CONFIDENCE, decide_interjection, decide_next_speaker, decide_round } from './scheduler.mjs';
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
+
const PACKAGE_VERSION = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;
|
|
6
7
|
const MAX_QUOTE_CHARS = 300;
|
|
8
|
+
/** How many of the latest plans describe "the host's recent arrangement" to the scheduler. */
|
|
9
|
+
const ARRANGEMENT_TURNS = 3;
|
|
10
|
+
const MAX_ARRANGEMENT_CHARS = 200;
|
|
11
|
+
const TURN_STATUS_LABELS = { answered: '已发言', open: '发言中', queued: '排队中', cancelled: '已取消', skipped: '已跳过' };
|
|
7
12
|
|
|
8
13
|
/** Read every page of current-session discussion plans; the list endpoint is capped at 20 rows per page. */
|
|
9
14
|
async function read_all_turns(config) {
|
|
@@ -49,8 +54,9 @@ export class SessionDriver {
|
|
|
49
54
|
try {
|
|
50
55
|
const [roster, turns] = await Promise.all([api(this.config, '/turns/roster'), read_all_turns(this.config)]);
|
|
51
56
|
const open_turn = turns.some(turn => ['open', 'queued'].includes(turn.status));
|
|
57
|
+
// An interjection is judged when someone can still be asked or a plan is running that a side note can leave alone.
|
|
52
58
|
const should_decide = request.cause_kind === 'user_message'
|
|
53
|
-
? roster.waiting.length > 0
|
|
59
|
+
? roster.waiting.length > 0 || open_turn
|
|
54
60
|
: !open_turn && (roster.waiting.length > 0 || !session.named_followup);
|
|
55
61
|
if (!should_decide) return false;
|
|
56
62
|
this.handled_requests.add(request.id);
|
|
@@ -60,7 +66,7 @@ export class SessionDriver {
|
|
|
60
66
|
const suggestion = { message_id: request.source_message_id || latest_id, suggestion: plan.label,
|
|
61
67
|
confidence: plan.confidence ?? null };
|
|
62
68
|
|
|
63
|
-
if (plan.action !== 'schedule') {
|
|
69
|
+
if (plan.action !== 'schedule' && plan.action !== 'continue') {
|
|
64
70
|
await this.record_decision(plan, false, suggestion.message_id);
|
|
65
71
|
this.suggestions.set(request.id, [suggestion]);
|
|
66
72
|
return false;
|
|
@@ -76,7 +82,9 @@ export class SessionDriver {
|
|
|
76
82
|
if (latest_roster.revision !== roster.revision) throw new Error('讨论名单或安排已变化,交回主持人');
|
|
77
83
|
const host_token = this.config.members?.[room.host]?.token;
|
|
78
84
|
if (!host_token) throw new Error('缺少主持人成员凭据,交回主持人');
|
|
79
|
-
|
|
85
|
+
// A side note keeps the running plan: closing the host request is all that is needed.
|
|
86
|
+
if (plan.action === 'continue') await api(this.config, '/turns/continue', { actor: room.host, revision: latest_roster.revision }, host_token);
|
|
87
|
+
else await api(this.config, '/turns', { actor: room.host, turns: [plan.turn], roster: latest_roster.revision,
|
|
80
88
|
submission_id: `session-driver-${session.id}-${request.id}` }, host_token);
|
|
81
89
|
await this.record_decision(plan, true, suggestion.message_id);
|
|
82
90
|
this.log(`智能调度 #${request.id}:${plan.label}`);
|
|
@@ -108,14 +116,15 @@ export class SessionDriver {
|
|
|
108
116
|
/** Choose an eligible next task; manual and web members remain valid candidates from the roster. */
|
|
109
117
|
async plan(session, roster, turns, request, room) {
|
|
110
118
|
const latest_id = room.latest_id || 0;
|
|
111
|
-
const page = await api(this.config, `/messages?after=${Math.max(0, latest_id -
|
|
119
|
+
const page = await api(this.config, `/messages?after=${Math.max(0, latest_id - ROUTE_CONTEXT_MESSAGES)}`);
|
|
112
120
|
const base = { title: room.title, goal: session.goal || room.goal, user_request: room.user_request,
|
|
113
|
-
context: page.messages, host: room.host, brief: room.host_brief?.text, focus: room.focus, profiles: room.descriptions
|
|
121
|
+
context: page.messages, host: room.host, brief: room.host_brief?.text, focus: room.focus, profiles: room.descriptions,
|
|
122
|
+
arrangement: arrangement_of(turns) };
|
|
114
123
|
const last_answer = [...turns].filter(turn => turn.status === 'answered')
|
|
115
124
|
.sort((left, right) => Number(left.answer_id || 0) - Number(right.answer_id || 0)).at(-1);
|
|
116
125
|
const anchor = last_answer?.answer_id || request.source_message_id || latest_id;
|
|
117
126
|
|
|
118
|
-
if (request.cause_kind === 'user_message') return this.plan_interjection(base, page.messages, session, roster, request);
|
|
127
|
+
if (request.cause_kind === 'user_message') return this.plan_interjection(base, page.messages, session, roster, request, turns);
|
|
119
128
|
const open = turns.some(turn => ['open', 'queued'].includes(turn.status));
|
|
120
129
|
if (open) return null;
|
|
121
130
|
if (roster.waiting.length) return this.plan_speaker(base, session, roster, turns, anchor);
|
|
@@ -130,12 +139,18 @@ export class SessionDriver {
|
|
|
130
139
|
}
|
|
131
140
|
|
|
132
141
|
/** Decide whether an unnamed user message should be answered by an expert or returned to the host. */
|
|
133
|
-
async plan_interjection(base, messages, session, roster, request) {
|
|
142
|
+
async plan_interjection(base, messages, session, roster, request, turns) {
|
|
134
143
|
const message = messages.find(item => item.id === request.source_message_id)
|
|
135
144
|
|| messages.find(item => item.author === '用户' && item.id >= (session.held_since || 0));
|
|
136
|
-
if (!message
|
|
145
|
+
if (!message) return null;
|
|
146
|
+
const spoken = turns.filter(turn => turn.round === roster.round && turn.status === 'answered').map(turn => turn.expert);
|
|
137
147
|
const result = await decide_interjection(this.config.api_home, this.config.scheduler,
|
|
138
|
-
{ ...base, message, experts: roster.waiting }, this.fetch);
|
|
148
|
+
{ ...base, message, experts: roster.waiting, spoken }, this.fetch);
|
|
149
|
+
if (result.action === 'note') {
|
|
150
|
+
// Continuing needs a plan to continue; with nothing open or queued the discussion would stall without the host.
|
|
151
|
+
if (turns.some(turn => ['open', 'queued'].includes(turn.status))) return { ...result, action: 'continue', kind: 'interjection', after_message_id: message.id };
|
|
152
|
+
return { ...result, action: 'host', kind: 'interjection', after_message_id: message.id, label: '仅补充信息,但当前没有进行中的安排,交给主持人' };
|
|
153
|
+
}
|
|
139
154
|
if (result.action !== 'ask' || !roster.waiting.includes(result.expert)) {
|
|
140
155
|
return { ...result, action: 'host', kind: 'interjection', after_message_id: message.id };
|
|
141
156
|
}
|
|
@@ -165,11 +180,21 @@ export class SessionDriver {
|
|
|
165
180
|
try {
|
|
166
181
|
await api(this.config, '/scheduler-decisions', { kind: plan.kind, label: plan.label,
|
|
167
182
|
confidence: plan.confidence ?? null, applied, provider: this.config.scheduler.provider,
|
|
168
|
-
after_message_id, detail: plan.detail, input: plan.input
|
|
183
|
+
after_message_id, detail: plan.detail, input: plan.input,
|
|
184
|
+
context: { model: this.config.scheduler.model, threshold: ROUTE_CONFIDENCE, wide_threshold: ROUTE_WIDE_CONFIDENCE,
|
|
185
|
+
prompt: DISCUSSION_PROMPT_VERSION, package: PACKAGE_VERSION } });
|
|
169
186
|
} catch (error) { this.log(`智能调度决策记录未保存:${error.message}`); }
|
|
170
187
|
}
|
|
171
188
|
}
|
|
172
189
|
|
|
190
|
+
/** The latest plans with their state, as the host's recent arrangement the questions refer to. */
|
|
191
|
+
function arrangement_of(turns) {
|
|
192
|
+
return [...turns].sort((left, right) => Number(left.id || 0) - Number(right.id || 0)).slice(-ARRANGEMENT_TURNS).map(turn => {
|
|
193
|
+
const question = String(turn.question || '').replace(/\s+/g, ' ').trim();
|
|
194
|
+
return `${turn.expert}(第 ${turn.round} 轮,${TURN_STATUS_LABELS[turn.status] || turn.status}):${question.length > MAX_ARRANGEMENT_CHARS ? `${question.slice(0, MAX_ARRANGEMENT_CHARS)}…` : question}`;
|
|
195
|
+
}).join(';');
|
|
196
|
+
}
|
|
197
|
+
|
|
173
198
|
function opening_question(phase) {
|
|
174
199
|
return phase === 'initial' ? '请先独立阅读议题,给出你的开场观点。' : '请先阅读议题和已有回答,给出你的观点。';
|
|
175
200
|
}
|
package/web/app.js
CHANGED
|
@@ -64,6 +64,7 @@ const live_updates = { source: null, connected: false, enabled: false, timer: nu
|
|
|
64
64
|
state.messages = [];
|
|
65
65
|
state.message_history = null;
|
|
66
66
|
state.connected = false;
|
|
67
|
+
state.reconnecting = false;
|
|
67
68
|
state.attachment_drafts = [];
|
|
68
69
|
state.composer_error = "";
|
|
69
70
|
state.upload_status = "";
|
|
@@ -313,6 +314,7 @@ function management_error_message(room_id, error) {
|
|
|
313
314
|
/** Show connection state without claiming that any AI is online. */
|
|
314
315
|
function set_connection(connected) {
|
|
315
316
|
state.connected = connected;
|
|
317
|
+
state.reconnecting = false;
|
|
316
318
|
element("connection-dot").className = `status-dot ${connected ? "connected" : "failed"}`;
|
|
317
319
|
element("connection-status").textContent = connected ? "讨论网站已连接" : "网站连接中断,可手动刷新";
|
|
318
320
|
publish_view();
|
|
@@ -3614,7 +3616,11 @@ async function flush_live_refresh() {
|
|
|
3614
3616
|
const full = live_updates.full;
|
|
3615
3617
|
live_updates.full = false;
|
|
3616
3618
|
live_updates.running = true;
|
|
3617
|
-
try { await refresh_room({ room: full }); } catch
|
|
3619
|
+
try { await refresh_room({ room: full }); } catch (error) {
|
|
3620
|
+
console.warn("refresh failed", error);
|
|
3621
|
+
if (state.reconnecting) show_toast(`仍未连接:${error.message}`, true);
|
|
3622
|
+
set_connection(false);
|
|
3623
|
+
}
|
|
3618
3624
|
finally {
|
|
3619
3625
|
live_updates.running = false;
|
|
3620
3626
|
if (live_updates.dirty) queue_live_refresh();
|
|
@@ -3628,7 +3634,7 @@ function arm_update_poll() {
|
|
|
3628
3634
|
live_updates.poll_timer = setTimeout(() => {
|
|
3629
3635
|
poll_live_updates();
|
|
3630
3636
|
arm_update_poll();
|
|
3631
|
-
}, live_updates.connected ? REFRESH_INTERVAL_MS : FALLBACK_REFRESH_INTERVAL_MS);
|
|
3637
|
+
}, live_updates.connected && state.connected ? REFRESH_INTERVAL_MS : FALLBACK_REFRESH_INTERVAL_MS);
|
|
3632
3638
|
}
|
|
3633
3639
|
|
|
3634
3640
|
/**
|
|
@@ -3637,9 +3643,10 @@ function arm_update_poll() {
|
|
|
3637
3643
|
* A site without revisions is read in full as before.
|
|
3638
3644
|
*/
|
|
3639
3645
|
async function poll_live_updates() {
|
|
3646
|
+
if (!live_updates.source && typeof EventSource !== "undefined") return reconnect_live_updates();
|
|
3640
3647
|
const room_id = state.room?.id || "";
|
|
3641
3648
|
const working = (state.room?.free?.members || []).some((member) => WORKING_STATUSES.includes(member.execution?.status));
|
|
3642
|
-
if (working || Date.now() - live_updates.refreshed_at >= SAFETY_REFRESH_MS) return queue_live_refresh();
|
|
3649
|
+
if (!state.connected || working || Date.now() - live_updates.refreshed_at >= SAFETY_REFRESH_MS) return queue_live_refresh();
|
|
3643
3650
|
let revisions;
|
|
3644
3651
|
try { revisions = await request_json(`/api/revisions?rooms=${encodeURIComponent(room_id)}`); }
|
|
3645
3652
|
catch { return queue_live_refresh(); }
|
|
@@ -3669,6 +3676,8 @@ function start_live_updates() {
|
|
|
3669
3676
|
if (live_updates.source !== source) return;
|
|
3670
3677
|
const was_connected = live_updates.connected;
|
|
3671
3678
|
live_updates.connected = false;
|
|
3679
|
+
// A closed stream is never retried by the browser; the next poll opens a new one.
|
|
3680
|
+
if (source.readyState === EventSource.CLOSED) live_updates.source = null;
|
|
3672
3681
|
if (was_connected) arm_update_poll();
|
|
3673
3682
|
});
|
|
3674
3683
|
}
|
|
@@ -3676,6 +3685,17 @@ function start_live_updates() {
|
|
|
3676
3685
|
queue_live_refresh();
|
|
3677
3686
|
}
|
|
3678
3687
|
|
|
3688
|
+
/** Reopen the update stream and read the site now; used by the footer retry and when the stream was closed. */
|
|
3689
|
+
async function reconnect_live_updates() {
|
|
3690
|
+
if (document.hidden) return;
|
|
3691
|
+
state.reconnecting = true;
|
|
3692
|
+
publish_view();
|
|
3693
|
+
live_updates.source?.close();
|
|
3694
|
+
live_updates.source = null;
|
|
3695
|
+
live_updates.connected = false;
|
|
3696
|
+
start_live_updates();
|
|
3697
|
+
}
|
|
3698
|
+
|
|
3679
3699
|
/** Release streams and scheduled reads while hidden or leaving the page. */
|
|
3680
3700
|
function stop_live_updates() {
|
|
3681
3701
|
live_updates.enabled = false;
|
|
@@ -3751,7 +3771,7 @@ function view_snapshot() {
|
|
|
3751
3771
|
return { rooms: ordered_rooms(), folders: state.folders, folder_groups: ordered_folders(), room: state.room, archive_view: state.archive_view, directory_sort: state.directory_sort, messages: state.messages, total: state.message_total,
|
|
3752
3772
|
archive_pending: [...state.archive_changes.keys()], directory_error: state.directory_error, directory_notice: state.directory_notice, import_progress: structuredClone(state.import_progress),
|
|
3753
3773
|
directory_pending: [...state.directory_pending.keys()], folder_pending: [...state.folder_moves.keys()], creating_room: state.creating_room,
|
|
3754
|
-
connected: state.connected, backup_pending: Boolean(state.backup_pending), page: state.page, pages: state.pages, query: state.query,
|
|
3774
|
+
connected: state.connected, reconnecting: state.reconnecting, backup_pending: Boolean(state.backup_pending), page: state.page, pages: state.pages, query: state.query,
|
|
3755
3775
|
attachment_drafts: state.attachment_drafts.map(item => ({ id: item.id, file_name: item.file.name, size: item.file.size, preview_url: item.preview_url })),
|
|
3756
3776
|
composer_error: state.composer_error, upload_status: state.upload_status, sending_message: state.sending_message,
|
|
3757
3777
|
mention_only: state.mention_only, reply_to: state.reply_to, turn_reply_to: state.turn_reply_to,
|
|
@@ -3996,7 +4016,7 @@ function change_view_theme(theme) {
|
|
|
3996
4016
|
window.discuss_view = { snapshot: view_snapshot, write_field: write_view_field,
|
|
3997
4017
|
write_launch: write_launch_field, default_launch_entry, description_template, save_description_template, description_draft, write_description_draft, describe_member, set_chain_rounds, continue_chain, expert_presets, apply_expert_preset, unique_instance, member_label, preset_identity, save_expert_preset, delete_expert_preset, write_room_connection, write_room_adapter, launch_clients: LAUNCH_CLIENTS,
|
|
3998
4018
|
launch_model_state, load_launch_models, write_launch_model, save_launch_api_key, write_scheduler, save_scheduler_api_key, check_scheduler, write_named_followup, write_create_discussion, write_create_chain_rounds, check_launch_api, launch_summary, launch_can_set_access, pick_launch_directory, browse_local_directory, save_launch_projects, restore_launch_profile, reset_create_form, empty_discussion_message,
|
|
3999
|
-
start_drag: start_directory_drag,
|
|
4019
|
+
start_drag: start_directory_drag, reconnect: reconnect_live_updates,
|
|
4000
4020
|
attachment_markup, attachment_size, attachment_file_icon, drop_attachments, paste_attachments,
|
|
4001
4021
|
write_expert: write_view_expert, add_instance: add_view_instance, add_preset: add_preset_instance, platform_of, execute: execute_view_action, theme: change_view_theme,
|
|
4002
4022
|
admin: { register: register_admin, login: login_admin, logout: logout_admin,
|
package/web/modern.js
CHANGED
|
@@ -54881,7 +54881,7 @@ var k1 = {
|
|
|
54881
54881
|
style: A(de.value)
|
|
54882
54882
|
}, [
|
|
54883
54883
|
K("aside", t2, [
|
|
54884
|
-
r[30] ||= ws("<header class=\"channel-workspace-heading\"><span class=\"channel-brand-icon\" aria-hidden=\"true\"><svg class=\"channel-brand-dark\" viewBox=\"0 0 100 100\"><defs><linearGradient id=\"brand-d0\" gradientUnits=\"userSpaceOnUse\" x1=\"4.5\" y1=\"64\" x2=\"95.5\" y2=\"64\"><stop offset=\"0.0%\" stop-color=\"#e9ebed\"></stop><stop offset=\"6.3%\" stop-color=\"#e2e5e7\"></stop><stop offset=\"12.5%\" stop-color=\"#dcdee1\"></stop><stop offset=\"18.8%\" stop-color=\"#d5d8db\"></stop><stop offset=\"25.0%\" stop-color=\"#ced2d5\"></stop><stop offset=\"31.3%\" stop-color=\"#c8cbcf\"></stop><stop offset=\"37.5%\" stop-color=\"#c1c5ca\"></stop><stop offset=\"43.8%\" stop-color=\"#babfc4\"></stop><stop offset=\"50.0%\" stop-color=\"#b4b9be\"></stop><stop offset=\"56.3%\" stop-color=\"#a9aeb3\"></stop><stop offset=\"62.5%\" stop-color=\"#9ea3a9\"></stop><stop offset=\"68.8%\" stop-color=\"#93999f\"></stop><stop offset=\"75.0%\" stop-color=\"#888e95\"></stop><stop offset=\"81.3%\" stop-color=\"#7e848a\"></stop><stop offset=\"87.5%\" stop-color=\"#737a80\"></stop><stop offset=\"93.8%\" stop-color=\"#697077\"></stop><stop offset=\"100.0%\" stop-color=\"#5f666d\"></stop></linearGradient><linearGradient id=\"brand-d1\" gradientUnits=\"userSpaceOnUse\" x1=\"8.5\" y1=\"36\" x2=\"91.5\" y2=\"36\"><stop offset=\"0.0%\" stop-color=\"#3f86ff\"></stop><stop offset=\"6.3%\" stop-color=\"#5588ff\"></stop><stop offset=\"12.5%\" stop-color=\"#6889ff\"></stop><stop offset=\"18.8%\" stop-color=\"#788aff\"></stop><stop offset=\"25.0%\" stop-color=\"#878bff\"></stop><stop offset=\"31.3%\" stop-color=\"#958cff\"></stop><stop offset=\"37.5%\" stop-color=\"#a78aef\"></stop><stop offset=\"43.8%\" stop-color=\"#b986d7\"></stop><stop offset=\"50.0%\" stop-color=\"#ca80bf\"></stop><stop offset=\"56.3%\" stop-color=\"#da78a6\"></stop><stop offset=\"62.5%\" stop-color=\"#ea6e8c\"></stop><stop offset=\"68.8%\" stop-color=\"#f56d78\"></stop><stop offset=\"75.0%\" stop-color=\"#f77f72\"></stop><stop offset=\"81.3%\" stop-color=\"#f8906b\"></stop><stop offset=\"87.5%\" stop-color=\"#f9a163\"></stop><stop offset=\"93.8%\" stop-color=\"#fab058\"></stop><stop offset=\"100.0%\" stop-color=\"#fac04a\"></stop></linearGradient><linearGradient id=\"brand-d2\" gradientUnits=\"userSpaceOnUse\" x1=\"28.021\" y1=\"95.435\" x2=\"39.978\" y2=\"4.564\"><stop offset=\"0.0%\" stop-color=\"#d06a49\"></stop><stop offset=\"6.3%\" stop-color=\"#d36f4f\"></stop><stop offset=\"12.5%\" stop-color=\"#d67456\"></stop><stop offset=\"18.8%\" stop-color=\"#d97a5c\"></stop><stop offset=\"25.0%\" stop-color=\"#dc7f62\"></stop><stop offset=\"31.3%\" stop-color=\"#df8468\"></stop><stop offset=\"37.5%\" stop-color=\"#e2896e\"></stop><stop offset=\"43.8%\" stop-color=\"#e58e74\"></stop><stop offset=\"50.0%\" stop-color=\"#e8937a\"></stop><stop offset=\"56.3%\" stop-color=\"#eb9d85\"></stop><stop offset=\"62.5%\" stop-color=\"#eea68f\"></stop><stop offset=\"68.8%\" stop-color=\"#f1b09a\"></stop><stop offset=\"75.0%\" stop-color=\"#f4baa4\"></stop><stop offset=\"81.3%\" stop-color=\"#f6c3af\"></stop><stop offset=\"87.5%\" stop-color=\"#f9cdb9\"></stop><stop offset=\"93.8%\" stop-color=\"#fbd6c4\"></stop><stop offset=\"100.0%\" stop-color=\"#fde0cf\"></stop></linearGradient><linearGradient id=\"brand-d3\" gradientUnits=\"userSpaceOnUse\" x1=\"71.978\" y1=\"95.435\" x2=\"60.021\" y2=\"4.564\"><stop offset=\"0.0%\" stop-color=\"#4a5cff\"></stop><stop offset=\"6.3%\" stop-color=\"#5062ff\"></stop><stop offset=\"12.5%\" stop-color=\"#5768ff\"></stop><stop offset=\"18.8%\" stop-color=\"#5d6dff\"></stop><stop offset=\"25.0%\" stop-color=\"#6472ff\"></stop><stop offset=\"31.3%\" stop-color=\"#6b77ff\"></stop><stop offset=\"37.5%\" stop-color=\"#717cff\"></stop><stop offset=\"43.8%\" stop-color=\"#7881ff\"></stop><stop offset=\"50.0%\" stop-color=\"#7f86ff\"></stop><stop offset=\"56.3%\" stop-color=\"#8990ff\"></stop><stop offset=\"62.5%\" stop-color=\"#9399ff\"></stop><stop offset=\"68.8%\" stop-color=\"#9da2ff\"></stop><stop offset=\"75.0%\" stop-color=\"#a8abff\"></stop><stop offset=\"81.3%\" stop-color=\"#b3b4ff\"></stop><stop offset=\"87.5%\" stop-color=\"#bebdff\"></stop><stop offset=\"93.8%\" stop-color=\"#c9c5ff\"></stop><stop offset=\"100.0%\" stop-color=\"#d4ceff\"></stop></linearGradient><clipPath id=\"brand-d4\"><polygon points=\"16,46.5 84,46.5 84,25.5 16,25.5\"></polygon></clipPath><clipPath id=\"brand-d5\"><polygon points=\"16,48.5 84,48.5 84,23.5 16,23.5\"></polygon></clipPath><clipPath id=\"brand-d6\"><polygon points=\"81.410,86.630 71.410,10.630 50.589,13.369 60.589,89.369\"></polygon></clipPath><clipPath id=\"brand-d7\"><polygon points=\"83.393,86.369 73.393,10.369 48.606,13.630 58.606,89.630\"></polygon></clipPath><clipPath id=\"brand-d8\"><polygon points=\"12,74.5 88,74.5 88,53.5 12,53.5\"></polygon></clipPath><clipPath id=\"brand-d9\"><polygon points=\"12,76.5 88,76.5 88,51.5 12,51.5\"></polygon></clipPath><clipPath id=\"brand-d10\"><polygon points=\"39.410,89.369 49.410,13.369 28.589,10.630 18.589,86.630\"></polygon></clipPath><clipPath id=\"brand-d11\"><polygon points=\"41.393,89.630 51.393,13.630 26.606,10.369 16.606,86.369\"></polygon></clipPath></defs><line x1=\"12\" y1=\"64\" x2=\"88\" y2=\"64\" stroke=\"url(#brand-d0)\" stroke-width=\"15\" stroke-linecap=\"round\"></line><line x1=\"16\" y1=\"36\" x2=\"84\" y2=\"36\" stroke=\"url(#brand-d1)\" stroke-width=\"15\" stroke-linecap=\"round\"></line><line x1=\"29\" y1=\"88\" x2=\"39\" y2=\"12\" stroke=\"url(#brand-d2)\" stroke-width=\"15\" stroke-linecap=\"round\"></line><line x1=\"71\" y1=\"88\" x2=\"61\" y2=\"12\" stroke=\"url(#brand-d3)\" stroke-width=\"15\" stroke-linecap=\"round\"></line><g clip-path=\"url(#brand-d4)\"><line x1=\"29\" y1=\"88\" x2=\"39\" y2=\"12\" stroke=\"#202127\" stroke-width=\"21\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-d5)\"><line x1=\"29\" y1=\"88\" x2=\"39\" y2=\"12\" stroke=\"url(#brand-d2)\" stroke-width=\"15\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-d6)\"><line x1=\"16\" y1=\"36\" x2=\"84\" y2=\"36\" stroke=\"#202127\" stroke-width=\"21\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-d7)\"><line x1=\"16\" y1=\"36\" x2=\"84\" y2=\"36\" stroke=\"url(#brand-d1)\" stroke-width=\"15\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-d8)\"><line x1=\"71\" y1=\"88\" x2=\"61\" y2=\"12\" stroke=\"#202127\" stroke-width=\"21\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-d9)\"><line x1=\"71\" y1=\"88\" x2=\"61\" y2=\"12\" stroke=\"url(#brand-d3)\" stroke-width=\"15\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-d10)\"><line x1=\"12\" y1=\"64\" x2=\"88\" y2=\"64\" stroke=\"#202127\" stroke-width=\"21\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-d11)\"><line x1=\"12\" y1=\"64\" x2=\"88\" y2=\"64\" stroke=\"url(#brand-d0)\" stroke-width=\"15\" stroke-linecap=\"round\"></line></g></svg><svg class=\"channel-brand-light\" viewBox=\"0 0 100 100\"><defs><linearGradient id=\"brand-l0\" gradientUnits=\"userSpaceOnUse\" x1=\"4.5\" y1=\"64\" x2=\"95.5\" y2=\"64\"><stop offset=\"0.0%\" stop-color=\"#0b0d0f\"></stop><stop offset=\"6.3%\" stop-color=\"#101315\"></stop><stop offset=\"12.5%\" stop-color=\"#16191b\"></stop><stop offset=\"18.8%\" stop-color=\"#1c1f22\"></stop><stop offset=\"25.0%\" stop-color=\"#222529\"></stop><stop offset=\"31.3%\" stop-color=\"#282c2f\"></stop><stop offset=\"37.5%\" stop-color=\"#2e3237\"></stop><stop offset=\"43.8%\" stop-color=\"#34393e\"></stop><stop offset=\"50.0%\" stop-color=\"#3b4045\"></stop><stop offset=\"56.3%\" stop-color=\"#484d52\"></stop><stop offset=\"62.5%\" stop-color=\"#555a5f\"></stop><stop offset=\"68.8%\" stop-color=\"#62676d\"></stop><stop offset=\"75.0%\" stop-color=\"#70757b\"></stop><stop offset=\"81.3%\" stop-color=\"#7e8389\"></stop><stop offset=\"87.5%\" stop-color=\"#8c9297\"></stop><stop offset=\"93.8%\" stop-color=\"#9aa0a6\"></stop><stop offset=\"100.0%\" stop-color=\"#a9afb5\"></stop></linearGradient><linearGradient id=\"brand-l1\" gradientUnits=\"userSpaceOnUse\" x1=\"8.5\" y1=\"36\" x2=\"91.5\" y2=\"36\"><stop offset=\"0.0%\" stop-color=\"#2f7bff\"></stop><stop offset=\"6.3%\" stop-color=\"#487cff\"></stop><stop offset=\"12.5%\" stop-color=\"#5a7cff\"></stop><stop offset=\"18.8%\" stop-color=\"#6a7dff\"></stop><stop offset=\"25.0%\" stop-color=\"#797dff\"></stop><stop offset=\"31.3%\" stop-color=\"#877dff\"></stop><stop offset=\"37.5%\" stop-color=\"#997cee\"></stop><stop offset=\"43.8%\" stop-color=\"#ae79d3\"></stop><stop offset=\"50.0%\" stop-color=\"#c174b8\"></stop><stop offset=\"56.3%\" stop-color=\"#d36b9d\"></stop><stop offset=\"62.5%\" stop-color=\"#e56080\"></stop><stop offset=\"68.8%\" stop-color=\"#f15d69\"></stop><stop offset=\"75.0%\" stop-color=\"#f37162\"></stop><stop offset=\"81.3%\" stop-color=\"#f5835a\"></stop><stop offset=\"87.5%\" stop-color=\"#f79550\"></stop><stop offset=\"93.8%\" stop-color=\"#f8a542\"></stop><stop offset=\"100.0%\" stop-color=\"#f8b52c\"></stop></linearGradient><linearGradient id=\"brand-l2\" gradientUnits=\"userSpaceOnUse\" x1=\"28.021\" y1=\"95.435\" x2=\"39.978\" y2=\"4.564\"><stop offset=\"0.0%\" stop-color=\"#b9502f\"></stop><stop offset=\"6.3%\" stop-color=\"#bd5534\"></stop><stop offset=\"12.5%\" stop-color=\"#c15a39\"></stop><stop offset=\"18.8%\" stop-color=\"#c55f3e\"></stop><stop offset=\"25.0%\" stop-color=\"#c96443\"></stop><stop offset=\"31.3%\" stop-color=\"#cd6848\"></stop><stop offset=\"37.5%\" stop-color=\"#d16d4d\"></stop><stop offset=\"43.8%\" stop-color=\"#d57252\"></stop><stop offset=\"50.0%\" stop-color=\"#d97757\"></stop><stop offset=\"56.3%\" stop-color=\"#dd8060\"></stop><stop offset=\"62.5%\" stop-color=\"#e08a69\"></stop><stop offset=\"68.8%\" stop-color=\"#e49372\"></stop><stop offset=\"75.0%\" stop-color=\"#e79c7b\"></stop><stop offset=\"81.3%\" stop-color=\"#eaa584\"></stop><stop offset=\"87.5%\" stop-color=\"#edae8d\"></stop><stop offset=\"93.8%\" stop-color=\"#f0b796\"></stop><stop offset=\"100.0%\" stop-color=\"#f3c09f\"></stop></linearGradient><linearGradient id=\"brand-l3\" gradientUnits=\"userSpaceOnUse\" x1=\"71.978\" y1=\"95.435\" x2=\"60.021\" y2=\"4.564\"><stop offset=\"0.0%\" stop-color=\"#2b33e6\"></stop><stop offset=\"6.3%\" stop-color=\"#3139e9\"></stop><stop offset=\"12.5%\" stop-color=\"#363fec\"></stop><stop offset=\"18.8%\" stop-color=\"#3c44ef\"></stop><stop offset=\"25.0%\" stop-color=\"#4249f3\"></stop><stop offset=\"31.3%\" stop-color=\"#484ef6\"></stop><stop offset=\"37.5%\" stop-color=\"#4e53f9\"></stop><stop offset=\"43.8%\" stop-color=\"#5457fc\"></stop><stop offset=\"50.0%\" stop-color=\"#5a5cff\"></stop><stop offset=\"56.3%\" stop-color=\"#6569ff\"></stop><stop offset=\"62.5%\" stop-color=\"#7075ff\"></stop><stop offset=\"68.8%\" stop-color=\"#7c80ff\"></stop><stop offset=\"75.0%\" stop-color=\"#888bff\"></stop><stop offset=\"81.3%\" stop-color=\"#9595ff\"></stop><stop offset=\"87.5%\" stop-color=\"#a29fff\"></stop><stop offset=\"93.8%\" stop-color=\"#afa9ff\"></stop><stop offset=\"100.0%\" stop-color=\"#bdb3ff\"></stop></linearGradient><clipPath id=\"brand-l4\"><polygon points=\"16,46.5 84,46.5 84,25.5 16,25.5\"></polygon></clipPath><clipPath id=\"brand-l5\"><polygon points=\"16,48.5 84,48.5 84,23.5 16,23.5\"></polygon></clipPath><clipPath id=\"brand-l6\"><polygon points=\"81.410,86.630 71.410,10.630 50.589,13.369 60.589,89.369\"></polygon></clipPath><clipPath id=\"brand-l7\"><polygon points=\"83.393,86.369 73.393,10.369 48.606,13.630 58.606,89.630\"></polygon></clipPath><clipPath id=\"brand-l8\"><polygon points=\"12,74.5 88,74.5 88,53.5 12,53.5\"></polygon></clipPath><clipPath id=\"brand-l9\"><polygon points=\"12,76.5 88,76.5 88,51.5 12,51.5\"></polygon></clipPath><clipPath id=\"brand-l10\"><polygon points=\"39.410,89.369 49.410,13.369 28.589,10.630 18.589,86.630\"></polygon></clipPath><clipPath id=\"brand-l11\"><polygon points=\"41.393,89.630 51.393,13.630 26.606,10.369 16.606,86.369\"></polygon></clipPath></defs><line x1=\"12\" y1=\"64\" x2=\"88\" y2=\"64\" stroke=\"url(#brand-l0)\" stroke-width=\"15\" stroke-linecap=\"round\"></line><line x1=\"16\" y1=\"36\" x2=\"84\" y2=\"36\" stroke=\"url(#brand-l1)\" stroke-width=\"15\" stroke-linecap=\"round\"></line><line x1=\"29\" y1=\"88\" x2=\"39\" y2=\"12\" stroke=\"url(#brand-l2)\" stroke-width=\"15\" stroke-linecap=\"round\"></line><line x1=\"71\" y1=\"88\" x2=\"61\" y2=\"12\" stroke=\"url(#brand-l3)\" stroke-width=\"15\" stroke-linecap=\"round\"></line><g clip-path=\"url(#brand-l4)\"><line x1=\"29\" y1=\"88\" x2=\"39\" y2=\"12\" stroke=\"#f6f6f6\" stroke-width=\"21\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-l5)\"><line x1=\"29\" y1=\"88\" x2=\"39\" y2=\"12\" stroke=\"url(#brand-l2)\" stroke-width=\"15\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-l6)\"><line x1=\"16\" y1=\"36\" x2=\"84\" y2=\"36\" stroke=\"#f6f6f6\" stroke-width=\"21\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-l7)\"><line x1=\"16\" y1=\"36\" x2=\"84\" y2=\"36\" stroke=\"url(#brand-l1)\" stroke-width=\"15\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-l8)\"><line x1=\"71\" y1=\"88\" x2=\"61\" y2=\"12\" stroke=\"#f6f6f6\" stroke-width=\"21\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-l9)\"><line x1=\"71\" y1=\"88\" x2=\"61\" y2=\"12\" stroke=\"url(#brand-l3)\" stroke-width=\"15\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-l10)\"><line x1=\"12\" y1=\"64\" x2=\"88\" y2=\"64\" stroke=\"#f6f6f6\" stroke-width=\"21\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-l11)\"><line x1=\"12\" y1=\"64\" x2=\"88\" y2=\"64\" stroke=\"url(#brand-l0)\" stroke-width=\"15\" stroke-linecap=\"round\"></line></g></svg></span><span class=\"channel-brand-text\"><strong>共议</strong><span>AI CoDiscuss</span></span></header>", 1),
|
|
54884
|
+
r[31] ||= ws("<header class=\"channel-workspace-heading\"><span class=\"channel-brand-icon\" aria-hidden=\"true\"><svg class=\"channel-brand-dark\" viewBox=\"0 0 100 100\"><defs><linearGradient id=\"brand-d0\" gradientUnits=\"userSpaceOnUse\" x1=\"4.5\" y1=\"64\" x2=\"95.5\" y2=\"64\"><stop offset=\"0.0%\" stop-color=\"#e9ebed\"></stop><stop offset=\"6.3%\" stop-color=\"#e2e5e7\"></stop><stop offset=\"12.5%\" stop-color=\"#dcdee1\"></stop><stop offset=\"18.8%\" stop-color=\"#d5d8db\"></stop><stop offset=\"25.0%\" stop-color=\"#ced2d5\"></stop><stop offset=\"31.3%\" stop-color=\"#c8cbcf\"></stop><stop offset=\"37.5%\" stop-color=\"#c1c5ca\"></stop><stop offset=\"43.8%\" stop-color=\"#babfc4\"></stop><stop offset=\"50.0%\" stop-color=\"#b4b9be\"></stop><stop offset=\"56.3%\" stop-color=\"#a9aeb3\"></stop><stop offset=\"62.5%\" stop-color=\"#9ea3a9\"></stop><stop offset=\"68.8%\" stop-color=\"#93999f\"></stop><stop offset=\"75.0%\" stop-color=\"#888e95\"></stop><stop offset=\"81.3%\" stop-color=\"#7e848a\"></stop><stop offset=\"87.5%\" stop-color=\"#737a80\"></stop><stop offset=\"93.8%\" stop-color=\"#697077\"></stop><stop offset=\"100.0%\" stop-color=\"#5f666d\"></stop></linearGradient><linearGradient id=\"brand-d1\" gradientUnits=\"userSpaceOnUse\" x1=\"8.5\" y1=\"36\" x2=\"91.5\" y2=\"36\"><stop offset=\"0.0%\" stop-color=\"#3f86ff\"></stop><stop offset=\"6.3%\" stop-color=\"#5588ff\"></stop><stop offset=\"12.5%\" stop-color=\"#6889ff\"></stop><stop offset=\"18.8%\" stop-color=\"#788aff\"></stop><stop offset=\"25.0%\" stop-color=\"#878bff\"></stop><stop offset=\"31.3%\" stop-color=\"#958cff\"></stop><stop offset=\"37.5%\" stop-color=\"#a78aef\"></stop><stop offset=\"43.8%\" stop-color=\"#b986d7\"></stop><stop offset=\"50.0%\" stop-color=\"#ca80bf\"></stop><stop offset=\"56.3%\" stop-color=\"#da78a6\"></stop><stop offset=\"62.5%\" stop-color=\"#ea6e8c\"></stop><stop offset=\"68.8%\" stop-color=\"#f56d78\"></stop><stop offset=\"75.0%\" stop-color=\"#f77f72\"></stop><stop offset=\"81.3%\" stop-color=\"#f8906b\"></stop><stop offset=\"87.5%\" stop-color=\"#f9a163\"></stop><stop offset=\"93.8%\" stop-color=\"#fab058\"></stop><stop offset=\"100.0%\" stop-color=\"#fac04a\"></stop></linearGradient><linearGradient id=\"brand-d2\" gradientUnits=\"userSpaceOnUse\" x1=\"28.021\" y1=\"95.435\" x2=\"39.978\" y2=\"4.564\"><stop offset=\"0.0%\" stop-color=\"#d06a49\"></stop><stop offset=\"6.3%\" stop-color=\"#d36f4f\"></stop><stop offset=\"12.5%\" stop-color=\"#d67456\"></stop><stop offset=\"18.8%\" stop-color=\"#d97a5c\"></stop><stop offset=\"25.0%\" stop-color=\"#dc7f62\"></stop><stop offset=\"31.3%\" stop-color=\"#df8468\"></stop><stop offset=\"37.5%\" stop-color=\"#e2896e\"></stop><stop offset=\"43.8%\" stop-color=\"#e58e74\"></stop><stop offset=\"50.0%\" stop-color=\"#e8937a\"></stop><stop offset=\"56.3%\" stop-color=\"#eb9d85\"></stop><stop offset=\"62.5%\" stop-color=\"#eea68f\"></stop><stop offset=\"68.8%\" stop-color=\"#f1b09a\"></stop><stop offset=\"75.0%\" stop-color=\"#f4baa4\"></stop><stop offset=\"81.3%\" stop-color=\"#f6c3af\"></stop><stop offset=\"87.5%\" stop-color=\"#f9cdb9\"></stop><stop offset=\"93.8%\" stop-color=\"#fbd6c4\"></stop><stop offset=\"100.0%\" stop-color=\"#fde0cf\"></stop></linearGradient><linearGradient id=\"brand-d3\" gradientUnits=\"userSpaceOnUse\" x1=\"71.978\" y1=\"95.435\" x2=\"60.021\" y2=\"4.564\"><stop offset=\"0.0%\" stop-color=\"#4a5cff\"></stop><stop offset=\"6.3%\" stop-color=\"#5062ff\"></stop><stop offset=\"12.5%\" stop-color=\"#5768ff\"></stop><stop offset=\"18.8%\" stop-color=\"#5d6dff\"></stop><stop offset=\"25.0%\" stop-color=\"#6472ff\"></stop><stop offset=\"31.3%\" stop-color=\"#6b77ff\"></stop><stop offset=\"37.5%\" stop-color=\"#717cff\"></stop><stop offset=\"43.8%\" stop-color=\"#7881ff\"></stop><stop offset=\"50.0%\" stop-color=\"#7f86ff\"></stop><stop offset=\"56.3%\" stop-color=\"#8990ff\"></stop><stop offset=\"62.5%\" stop-color=\"#9399ff\"></stop><stop offset=\"68.8%\" stop-color=\"#9da2ff\"></stop><stop offset=\"75.0%\" stop-color=\"#a8abff\"></stop><stop offset=\"81.3%\" stop-color=\"#b3b4ff\"></stop><stop offset=\"87.5%\" stop-color=\"#bebdff\"></stop><stop offset=\"93.8%\" stop-color=\"#c9c5ff\"></stop><stop offset=\"100.0%\" stop-color=\"#d4ceff\"></stop></linearGradient><clipPath id=\"brand-d4\"><polygon points=\"16,46.5 84,46.5 84,25.5 16,25.5\"></polygon></clipPath><clipPath id=\"brand-d5\"><polygon points=\"16,48.5 84,48.5 84,23.5 16,23.5\"></polygon></clipPath><clipPath id=\"brand-d6\"><polygon points=\"81.410,86.630 71.410,10.630 50.589,13.369 60.589,89.369\"></polygon></clipPath><clipPath id=\"brand-d7\"><polygon points=\"83.393,86.369 73.393,10.369 48.606,13.630 58.606,89.630\"></polygon></clipPath><clipPath id=\"brand-d8\"><polygon points=\"12,74.5 88,74.5 88,53.5 12,53.5\"></polygon></clipPath><clipPath id=\"brand-d9\"><polygon points=\"12,76.5 88,76.5 88,51.5 12,51.5\"></polygon></clipPath><clipPath id=\"brand-d10\"><polygon points=\"39.410,89.369 49.410,13.369 28.589,10.630 18.589,86.630\"></polygon></clipPath><clipPath id=\"brand-d11\"><polygon points=\"41.393,89.630 51.393,13.630 26.606,10.369 16.606,86.369\"></polygon></clipPath></defs><line x1=\"12\" y1=\"64\" x2=\"88\" y2=\"64\" stroke=\"url(#brand-d0)\" stroke-width=\"15\" stroke-linecap=\"round\"></line><line x1=\"16\" y1=\"36\" x2=\"84\" y2=\"36\" stroke=\"url(#brand-d1)\" stroke-width=\"15\" stroke-linecap=\"round\"></line><line x1=\"29\" y1=\"88\" x2=\"39\" y2=\"12\" stroke=\"url(#brand-d2)\" stroke-width=\"15\" stroke-linecap=\"round\"></line><line x1=\"71\" y1=\"88\" x2=\"61\" y2=\"12\" stroke=\"url(#brand-d3)\" stroke-width=\"15\" stroke-linecap=\"round\"></line><g clip-path=\"url(#brand-d4)\"><line x1=\"29\" y1=\"88\" x2=\"39\" y2=\"12\" stroke=\"#202127\" stroke-width=\"21\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-d5)\"><line x1=\"29\" y1=\"88\" x2=\"39\" y2=\"12\" stroke=\"url(#brand-d2)\" stroke-width=\"15\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-d6)\"><line x1=\"16\" y1=\"36\" x2=\"84\" y2=\"36\" stroke=\"#202127\" stroke-width=\"21\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-d7)\"><line x1=\"16\" y1=\"36\" x2=\"84\" y2=\"36\" stroke=\"url(#brand-d1)\" stroke-width=\"15\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-d8)\"><line x1=\"71\" y1=\"88\" x2=\"61\" y2=\"12\" stroke=\"#202127\" stroke-width=\"21\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-d9)\"><line x1=\"71\" y1=\"88\" x2=\"61\" y2=\"12\" stroke=\"url(#brand-d3)\" stroke-width=\"15\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-d10)\"><line x1=\"12\" y1=\"64\" x2=\"88\" y2=\"64\" stroke=\"#202127\" stroke-width=\"21\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-d11)\"><line x1=\"12\" y1=\"64\" x2=\"88\" y2=\"64\" stroke=\"url(#brand-d0)\" stroke-width=\"15\" stroke-linecap=\"round\"></line></g></svg><svg class=\"channel-brand-light\" viewBox=\"0 0 100 100\"><defs><linearGradient id=\"brand-l0\" gradientUnits=\"userSpaceOnUse\" x1=\"4.5\" y1=\"64\" x2=\"95.5\" y2=\"64\"><stop offset=\"0.0%\" stop-color=\"#0b0d0f\"></stop><stop offset=\"6.3%\" stop-color=\"#101315\"></stop><stop offset=\"12.5%\" stop-color=\"#16191b\"></stop><stop offset=\"18.8%\" stop-color=\"#1c1f22\"></stop><stop offset=\"25.0%\" stop-color=\"#222529\"></stop><stop offset=\"31.3%\" stop-color=\"#282c2f\"></stop><stop offset=\"37.5%\" stop-color=\"#2e3237\"></stop><stop offset=\"43.8%\" stop-color=\"#34393e\"></stop><stop offset=\"50.0%\" stop-color=\"#3b4045\"></stop><stop offset=\"56.3%\" stop-color=\"#484d52\"></stop><stop offset=\"62.5%\" stop-color=\"#555a5f\"></stop><stop offset=\"68.8%\" stop-color=\"#62676d\"></stop><stop offset=\"75.0%\" stop-color=\"#70757b\"></stop><stop offset=\"81.3%\" stop-color=\"#7e8389\"></stop><stop offset=\"87.5%\" stop-color=\"#8c9297\"></stop><stop offset=\"93.8%\" stop-color=\"#9aa0a6\"></stop><stop offset=\"100.0%\" stop-color=\"#a9afb5\"></stop></linearGradient><linearGradient id=\"brand-l1\" gradientUnits=\"userSpaceOnUse\" x1=\"8.5\" y1=\"36\" x2=\"91.5\" y2=\"36\"><stop offset=\"0.0%\" stop-color=\"#2f7bff\"></stop><stop offset=\"6.3%\" stop-color=\"#487cff\"></stop><stop offset=\"12.5%\" stop-color=\"#5a7cff\"></stop><stop offset=\"18.8%\" stop-color=\"#6a7dff\"></stop><stop offset=\"25.0%\" stop-color=\"#797dff\"></stop><stop offset=\"31.3%\" stop-color=\"#877dff\"></stop><stop offset=\"37.5%\" stop-color=\"#997cee\"></stop><stop offset=\"43.8%\" stop-color=\"#ae79d3\"></stop><stop offset=\"50.0%\" stop-color=\"#c174b8\"></stop><stop offset=\"56.3%\" stop-color=\"#d36b9d\"></stop><stop offset=\"62.5%\" stop-color=\"#e56080\"></stop><stop offset=\"68.8%\" stop-color=\"#f15d69\"></stop><stop offset=\"75.0%\" stop-color=\"#f37162\"></stop><stop offset=\"81.3%\" stop-color=\"#f5835a\"></stop><stop offset=\"87.5%\" stop-color=\"#f79550\"></stop><stop offset=\"93.8%\" stop-color=\"#f8a542\"></stop><stop offset=\"100.0%\" stop-color=\"#f8b52c\"></stop></linearGradient><linearGradient id=\"brand-l2\" gradientUnits=\"userSpaceOnUse\" x1=\"28.021\" y1=\"95.435\" x2=\"39.978\" y2=\"4.564\"><stop offset=\"0.0%\" stop-color=\"#b9502f\"></stop><stop offset=\"6.3%\" stop-color=\"#bd5534\"></stop><stop offset=\"12.5%\" stop-color=\"#c15a39\"></stop><stop offset=\"18.8%\" stop-color=\"#c55f3e\"></stop><stop offset=\"25.0%\" stop-color=\"#c96443\"></stop><stop offset=\"31.3%\" stop-color=\"#cd6848\"></stop><stop offset=\"37.5%\" stop-color=\"#d16d4d\"></stop><stop offset=\"43.8%\" stop-color=\"#d57252\"></stop><stop offset=\"50.0%\" stop-color=\"#d97757\"></stop><stop offset=\"56.3%\" stop-color=\"#dd8060\"></stop><stop offset=\"62.5%\" stop-color=\"#e08a69\"></stop><stop offset=\"68.8%\" stop-color=\"#e49372\"></stop><stop offset=\"75.0%\" stop-color=\"#e79c7b\"></stop><stop offset=\"81.3%\" stop-color=\"#eaa584\"></stop><stop offset=\"87.5%\" stop-color=\"#edae8d\"></stop><stop offset=\"93.8%\" stop-color=\"#f0b796\"></stop><stop offset=\"100.0%\" stop-color=\"#f3c09f\"></stop></linearGradient><linearGradient id=\"brand-l3\" gradientUnits=\"userSpaceOnUse\" x1=\"71.978\" y1=\"95.435\" x2=\"60.021\" y2=\"4.564\"><stop offset=\"0.0%\" stop-color=\"#2b33e6\"></stop><stop offset=\"6.3%\" stop-color=\"#3139e9\"></stop><stop offset=\"12.5%\" stop-color=\"#363fec\"></stop><stop offset=\"18.8%\" stop-color=\"#3c44ef\"></stop><stop offset=\"25.0%\" stop-color=\"#4249f3\"></stop><stop offset=\"31.3%\" stop-color=\"#484ef6\"></stop><stop offset=\"37.5%\" stop-color=\"#4e53f9\"></stop><stop offset=\"43.8%\" stop-color=\"#5457fc\"></stop><stop offset=\"50.0%\" stop-color=\"#5a5cff\"></stop><stop offset=\"56.3%\" stop-color=\"#6569ff\"></stop><stop offset=\"62.5%\" stop-color=\"#7075ff\"></stop><stop offset=\"68.8%\" stop-color=\"#7c80ff\"></stop><stop offset=\"75.0%\" stop-color=\"#888bff\"></stop><stop offset=\"81.3%\" stop-color=\"#9595ff\"></stop><stop offset=\"87.5%\" stop-color=\"#a29fff\"></stop><stop offset=\"93.8%\" stop-color=\"#afa9ff\"></stop><stop offset=\"100.0%\" stop-color=\"#bdb3ff\"></stop></linearGradient><clipPath id=\"brand-l4\"><polygon points=\"16,46.5 84,46.5 84,25.5 16,25.5\"></polygon></clipPath><clipPath id=\"brand-l5\"><polygon points=\"16,48.5 84,48.5 84,23.5 16,23.5\"></polygon></clipPath><clipPath id=\"brand-l6\"><polygon points=\"81.410,86.630 71.410,10.630 50.589,13.369 60.589,89.369\"></polygon></clipPath><clipPath id=\"brand-l7\"><polygon points=\"83.393,86.369 73.393,10.369 48.606,13.630 58.606,89.630\"></polygon></clipPath><clipPath id=\"brand-l8\"><polygon points=\"12,74.5 88,74.5 88,53.5 12,53.5\"></polygon></clipPath><clipPath id=\"brand-l9\"><polygon points=\"12,76.5 88,76.5 88,51.5 12,51.5\"></polygon></clipPath><clipPath id=\"brand-l10\"><polygon points=\"39.410,89.369 49.410,13.369 28.589,10.630 18.589,86.630\"></polygon></clipPath><clipPath id=\"brand-l11\"><polygon points=\"41.393,89.630 51.393,13.630 26.606,10.369 16.606,86.369\"></polygon></clipPath></defs><line x1=\"12\" y1=\"64\" x2=\"88\" y2=\"64\" stroke=\"url(#brand-l0)\" stroke-width=\"15\" stroke-linecap=\"round\"></line><line x1=\"16\" y1=\"36\" x2=\"84\" y2=\"36\" stroke=\"url(#brand-l1)\" stroke-width=\"15\" stroke-linecap=\"round\"></line><line x1=\"29\" y1=\"88\" x2=\"39\" y2=\"12\" stroke=\"url(#brand-l2)\" stroke-width=\"15\" stroke-linecap=\"round\"></line><line x1=\"71\" y1=\"88\" x2=\"61\" y2=\"12\" stroke=\"url(#brand-l3)\" stroke-width=\"15\" stroke-linecap=\"round\"></line><g clip-path=\"url(#brand-l4)\"><line x1=\"29\" y1=\"88\" x2=\"39\" y2=\"12\" stroke=\"#f6f6f6\" stroke-width=\"21\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-l5)\"><line x1=\"29\" y1=\"88\" x2=\"39\" y2=\"12\" stroke=\"url(#brand-l2)\" stroke-width=\"15\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-l6)\"><line x1=\"16\" y1=\"36\" x2=\"84\" y2=\"36\" stroke=\"#f6f6f6\" stroke-width=\"21\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-l7)\"><line x1=\"16\" y1=\"36\" x2=\"84\" y2=\"36\" stroke=\"url(#brand-l1)\" stroke-width=\"15\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-l8)\"><line x1=\"71\" y1=\"88\" x2=\"61\" y2=\"12\" stroke=\"#f6f6f6\" stroke-width=\"21\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-l9)\"><line x1=\"71\" y1=\"88\" x2=\"61\" y2=\"12\" stroke=\"url(#brand-l3)\" stroke-width=\"15\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-l10)\"><line x1=\"12\" y1=\"64\" x2=\"88\" y2=\"64\" stroke=\"#f6f6f6\" stroke-width=\"21\" stroke-linecap=\"round\"></line></g><g clip-path=\"url(#brand-l11)\"><line x1=\"12\" y1=\"64\" x2=\"88\" y2=\"64\" stroke=\"url(#brand-l0)\" stroke-width=\"15\" stroke-linecap=\"round\"></line></g></svg></span><span class=\"channel-brand-text\"><strong>共议</strong><span>AI CoDiscuss</span></span></header>", 1),
|
|
54885
54885
|
K("div", n2, [
|
|
54886
54886
|
q(jZ, {
|
|
54887
54887
|
onSelect: Ee,
|
|
@@ -54898,7 +54898,7 @@ var k1 = {
|
|
|
54898
54898
|
class: "channel-section-toggle",
|
|
54899
54899
|
"aria-expanded": f.value,
|
|
54900
54900
|
onClick: r[0] ||= (e) => f.value = !f.value
|
|
54901
|
-
}, [q(y, { name: f.value ? "i-lucide-chevron-down" : "i-lucide-chevron-right" }, null, 8, ["name"]), r[
|
|
54901
|
+
}, [q(y, { name: f.value ? "i-lucide-chevron-down" : "i-lucide-chevron-right" }, null, 8, ["name"]), r[30] ||= J("频道工具", -1)], 8, i2)]),
|
|
54902
54902
|
f.value ? (U(), G(b, {
|
|
54903
54903
|
key: 0,
|
|
54904
54904
|
items: xe.value,
|
|
@@ -54914,37 +54914,56 @@ var k1 = {
|
|
|
54914
54914
|
}
|
|
54915
54915
|
}, null, 8, ["items"])) : Y("", !0)
|
|
54916
54916
|
]),
|
|
54917
|
-
K("footer", a2, [
|
|
54918
|
-
|
|
54919
|
-
|
|
54920
|
-
|
|
54921
|
-
|
|
54922
|
-
|
|
54923
|
-
|
|
54924
|
-
|
|
54925
|
-
|
|
54926
|
-
|
|
54927
|
-
class: j(["channel-avatar", I(n).identities[I(_)]?.style])
|
|
54928
|
-
}, null, 8, [
|
|
54929
|
-
"src",
|
|
54930
|
-
"text",
|
|
54931
|
-
"alt",
|
|
54932
|
-
"class"
|
|
54933
|
-
]), K("span", s2, [K("strong", null, P(I(n).display_name(I(_))), 1), K("span", { class: j(["channel-connection", { "is-action": Se.value }]) }, [K("span", {
|
|
54934
|
-
class: j({ connected: I(t).connected }),
|
|
54935
|
-
title: I(t).connected ? "网站已连接" : "网站未连接"
|
|
54936
|
-
}, null, 10, c2), J(P(Se.value || (I(t).connected ? "网站已连接" : "网站未连接")), 1)], 2)])], 8, o2), q(D, { text: "设置" }, {
|
|
54937
|
-
default: L(() => [q(T, {
|
|
54938
|
-
icon: "i-lucide-settings-2",
|
|
54939
|
-
color: "neutral",
|
|
54940
|
-
variant: "ghost",
|
|
54917
|
+
K("footer", a2, [
|
|
54918
|
+
K("button", {
|
|
54919
|
+
type: "button",
|
|
54920
|
+
class: "channel-self-button",
|
|
54921
|
+
"aria-label": `账号与设置:${I(n).display_name(I(_))}${Se.value ? "," + Se.value : ""}`,
|
|
54922
|
+
onClick: r[1] ||= (e) => s.value = "settings"
|
|
54923
|
+
}, [q(w, {
|
|
54924
|
+
src: I(n).identities[I(_)]?.icon,
|
|
54925
|
+
text: I(n).identities[I(_)]?.initials,
|
|
54926
|
+
alt: I(n).display_name(I(_)),
|
|
54941
54927
|
size: "sm",
|
|
54942
|
-
class: "channel-
|
|
54943
|
-
|
|
54944
|
-
|
|
54945
|
-
|
|
54946
|
-
|
|
54947
|
-
|
|
54928
|
+
class: j(["channel-avatar", I(n).identities[I(_)]?.style])
|
|
54929
|
+
}, null, 8, [
|
|
54930
|
+
"src",
|
|
54931
|
+
"text",
|
|
54932
|
+
"alt",
|
|
54933
|
+
"class"
|
|
54934
|
+
]), K("span", s2, [K("strong", null, P(I(n).display_name(I(_))), 1), K("span", { class: j(["channel-connection", { "is-action": Se.value }]) }, [K("span", {
|
|
54935
|
+
class: j({ connected: I(t).connected }),
|
|
54936
|
+
title: I(t).connected ? "网站已连接" : "网站未连接"
|
|
54937
|
+
}, null, 10, c2), J(P(Se.value || (I(t).connected ? "网站已连接" : I(t).reconnecting ? "正在重新连接…" : "网站未连接")), 1)], 2)])], 8, o2),
|
|
54938
|
+
I(t).connected ? Y("", !0) : (U(), G(D, {
|
|
54939
|
+
key: 0,
|
|
54940
|
+
text: "重新连接"
|
|
54941
|
+
}, {
|
|
54942
|
+
default: L(() => [q(T, {
|
|
54943
|
+
icon: "i-lucide-refresh-cw",
|
|
54944
|
+
color: "neutral",
|
|
54945
|
+
variant: "ghost",
|
|
54946
|
+
size: "sm",
|
|
54947
|
+
class: "channel-icon-button",
|
|
54948
|
+
"aria-label": "重新连接网站",
|
|
54949
|
+
loading: I(t).reconnecting,
|
|
54950
|
+
onClick: r[2] ||= (e) => I(n).reconnect()
|
|
54951
|
+
}, null, 8, ["loading"])]),
|
|
54952
|
+
_: 1
|
|
54953
|
+
})),
|
|
54954
|
+
q(D, { text: "设置" }, {
|
|
54955
|
+
default: L(() => [q(T, {
|
|
54956
|
+
icon: "i-lucide-settings-2",
|
|
54957
|
+
color: "neutral",
|
|
54958
|
+
variant: "ghost",
|
|
54959
|
+
size: "sm",
|
|
54960
|
+
class: "channel-icon-button",
|
|
54961
|
+
"aria-label": "设置",
|
|
54962
|
+
onClick: r[3] ||= (e) => s.value = "settings"
|
|
54963
|
+
})]),
|
|
54964
|
+
_: 1
|
|
54965
|
+
})
|
|
54966
|
+
])
|
|
54948
54967
|
]),
|
|
54949
54968
|
te.value ? (U(), W("div", {
|
|
54950
54969
|
key: 0,
|
|
@@ -54985,7 +55004,7 @@ var k1 = {
|
|
|
54985
55004
|
"aria-label": E.value ? "展开房间栏" : "收起房间栏",
|
|
54986
55005
|
"aria-expanded": !E.value,
|
|
54987
55006
|
"aria-controls": "channel-sidebar",
|
|
54988
|
-
onClick: r[
|
|
55007
|
+
onClick: r[4] ||= (e) => E.value = !E.value
|
|
54989
55008
|
}, null, 8, ["aria-label", "aria-expanded"])]),
|
|
54990
55009
|
_: 1
|
|
54991
55010
|
}, 8, ["text"])) : (U(), G(T, {
|
|
@@ -54997,7 +55016,7 @@ var k1 = {
|
|
|
54997
55016
|
class: "channel-mobile-nav channel-icon-button channel-panel-toggle",
|
|
54998
55017
|
"aria-label": "打开房间导航",
|
|
54999
55018
|
"aria-expanded": d.value,
|
|
55000
|
-
onClick: r[
|
|
55019
|
+
onClick: r[5] ||= (e) => d.value = !0
|
|
55001
55020
|
}, null, 8, ["aria-expanded"])),
|
|
55002
55021
|
K("span", f2, [q(y, { name: "i-lucide-hash" })]),
|
|
55003
55022
|
K("div", p2, [K("div", m2, [K("h1", { title: o.value?.title }, P(o.value?.title || "共议"), 9, h2)]), o.value?.archived ? (U(), G(oe, {
|
|
@@ -55006,22 +55025,22 @@ var k1 = {
|
|
|
55006
55025
|
variant: "subtle",
|
|
55007
55026
|
size: "sm"
|
|
55008
55027
|
}, {
|
|
55009
|
-
default: L(() => [...r[
|
|
55028
|
+
default: L(() => [...r[32] ||= [J("已存档", -1)]]),
|
|
55010
55029
|
_: 1
|
|
55011
55030
|
})) : Y("", !0)]),
|
|
55012
55031
|
o.value ? (U(), W("span", g2, [
|
|
55013
55032
|
J(P(me.find((e) => e.value === I(t).tab)?.label), 1),
|
|
55014
|
-
r[
|
|
55033
|
+
r[33] ||= K("span", null, "·", -1),
|
|
55015
55034
|
J(P(o.value.message_count) + " 条记录", 1)
|
|
55016
55035
|
])) : Y("", !0),
|
|
55017
55036
|
K("div", _2, [
|
|
55018
55037
|
I(t).tab === "discussion" ? (U(), W("form", {
|
|
55019
55038
|
key: 0,
|
|
55020
55039
|
class: "channel-search",
|
|
55021
|
-
onSubmit: r[
|
|
55040
|
+
onSubmit: r[8] ||= uu((e) => I(i)("filter", { query: I(g) }), ["prevent"])
|
|
55022
55041
|
}, [q(k, {
|
|
55023
55042
|
modelValue: I(g),
|
|
55024
|
-
"onUpdate:modelValue": r[
|
|
55043
|
+
"onUpdate:modelValue": r[7] ||= (e) => /* @__PURE__ */ ln(g) ? g.value = e : null,
|
|
55025
55044
|
icon: "i-lucide-search",
|
|
55026
55045
|
variant: "none",
|
|
55027
55046
|
size: "sm",
|
|
@@ -55037,7 +55056,7 @@ var k1 = {
|
|
|
55037
55056
|
variant: "ghost",
|
|
55038
55057
|
size: "xs",
|
|
55039
55058
|
"aria-label": "清除搜索",
|
|
55040
|
-
onClick: r[
|
|
55059
|
+
onClick: r[6] ||= (e) => {
|
|
55041
55060
|
g.value = "", I(i)("filter", { query: "" });
|
|
55042
55061
|
}
|
|
55043
55062
|
})) : Y("", !0), q(T, {
|
|
@@ -55064,7 +55083,7 @@ var k1 = {
|
|
|
55064
55083
|
"aria-pressed": I(t).mention_only,
|
|
55065
55084
|
"aria-label": "提到我",
|
|
55066
55085
|
disabled: !o.value,
|
|
55067
|
-
onClick: r[
|
|
55086
|
+
onClick: r[9] ||= (e) => I(i)("filter", { mention: !I(t).mention_only })
|
|
55068
55087
|
}, null, 8, [
|
|
55069
55088
|
"variant",
|
|
55070
55089
|
"aria-pressed",
|
|
@@ -55094,7 +55113,7 @@ var k1 = {
|
|
|
55094
55113
|
}, 8, ["text"])
|
|
55095
55114
|
]),
|
|
55096
55115
|
I(t).launch.recoveries.length ? (U(), W("section", v2, [
|
|
55097
|
-
r[
|
|
55116
|
+
r[34] ||= K("h2", null, "继续未完成的请求", -1),
|
|
55098
55117
|
(U(!0), W(H, null, B(I(t).launch.recoveries, (e) => (U(), W("article", { key: e.id }, [
|
|
55099
55118
|
K("strong", null, P(e.title), 1),
|
|
55100
55119
|
K("p", y2, P(e.error || "启动结果尚未确认;原请求已保存在此浏览器中。"), 1),
|
|
@@ -55129,11 +55148,11 @@ var k1 = {
|
|
|
55129
55148
|
}, null, 8, ["disabled", "onClick"])
|
|
55130
55149
|
])
|
|
55131
55150
|
]))), 128)),
|
|
55132
|
-
r[
|
|
55151
|
+
r[35] ||= K("small", null, "移除待办不会删除房间或停止讨论。", -1)
|
|
55133
55152
|
])) : Y("", !0),
|
|
55134
55153
|
o.value ? (U(), W("div", x2, [q(he, {
|
|
55135
55154
|
modelValue: pe.value,
|
|
55136
|
-
"onUpdate:modelValue": r[
|
|
55155
|
+
"onUpdate:modelValue": r[10] ||= (e) => pe.value = e,
|
|
55137
55156
|
items: me,
|
|
55138
55157
|
content: !1,
|
|
55139
55158
|
variant: "pill",
|
|
@@ -55164,7 +55183,7 @@ var k1 = {
|
|
|
55164
55183
|
variant: "link",
|
|
55165
55184
|
size: "xs",
|
|
55166
55185
|
class: "channel-brief-update",
|
|
55167
|
-
onClick: r[
|
|
55186
|
+
onClick: r[11] ||= (e) => I(i)("tab", "context")
|
|
55168
55187
|
}))]), vr(K("div", E2, [K("div", {
|
|
55169
55188
|
class: "channel-brief-text markdown-body",
|
|
55170
55189
|
innerHTML: o.value.rendered_brief
|
|
@@ -55175,31 +55194,31 @@ var k1 = {
|
|
|
55175
55194
|
}, [K("div", A2, [
|
|
55176
55195
|
vr(q(uK, {
|
|
55177
55196
|
onDetail: De,
|
|
55178
|
-
onEdit: r[
|
|
55197
|
+
onEdit: r[12] ||= (e) => s.value = "edit"
|
|
55179
55198
|
}, null, 512), [[Vc, I(t).tab === "discussion"]]),
|
|
55180
55199
|
I(t).tab === "history" ? (U(), W("div", j2, [
|
|
55181
|
-
ge.value ? (U(), W("section", M2, [K("div", N2, [r[
|
|
55200
|
+
ge.value ? (U(), W("section", M2, [K("div", N2, [r[36] ||= K("div", null, [K("h2", null, "当前讨论"), K("p", null, "没有进行中的讨论。开启后由主持人推进成员轮流发言,到上限或结束后房间回到自由协作,记录与结论保留。")], -1), q(T, {
|
|
55182
55201
|
icon: "i-lucide-messages-square",
|
|
55183
55202
|
label: "开启讨论",
|
|
55184
55203
|
variant: "outline",
|
|
55185
55204
|
color: "neutral",
|
|
55186
55205
|
class: "channel-surface-button",
|
|
55187
55206
|
disabled: !ve.value,
|
|
55188
|
-
onClick: r[
|
|
55207
|
+
onClick: r[14] ||= (e) => _e.value = !0
|
|
55189
55208
|
}, null, 8, ["disabled"])]), !ve.value && !o.value.archived ? (U(), W("p", P2, "开启讨论需要一位由本机唤醒的 AI 主持人,且本机服务在线。")) : Y("", !0)])) : (U(), G(jK, {
|
|
55190
55209
|
key: 0,
|
|
55191
55210
|
section: "host",
|
|
55192
|
-
onOpen: r[
|
|
55211
|
+
onOpen: r[13] ||= (e) => s.value = e,
|
|
55193
55212
|
onDetail: De
|
|
55194
55213
|
})),
|
|
55195
|
-
K("section", F2, [K("div", I2, [r[
|
|
55214
|
+
K("section", F2, [K("div", I2, [r[37] ||= K("div", null, [K("h2", null, "当前共识"), K("p", null, "主持人整理的结论、共识与分歧,用 #消息编号 保留依据;讨论结束时保存进下方记录。")], -1), M.value ? Y("", !0) : (U(), G(T, {
|
|
55196
55215
|
key: 0,
|
|
55197
55216
|
icon: "i-lucide-pencil",
|
|
55198
55217
|
label: "编辑",
|
|
55199
55218
|
variant: "outline",
|
|
55200
55219
|
color: "neutral",
|
|
55201
55220
|
class: "channel-surface-button",
|
|
55202
|
-
onClick: r[
|
|
55221
|
+
onClick: r[15] ||= (e) => M.value = !0
|
|
55203
55222
|
}))]), M.value ? (U(), W("form", {
|
|
55204
55223
|
key: 0,
|
|
55205
55224
|
class: "summary-form",
|
|
@@ -55212,7 +55231,7 @@ var k1 = {
|
|
|
55212
55231
|
}, {
|
|
55213
55232
|
default: L(() => [q(N, {
|
|
55214
55233
|
modelValue: I(h),
|
|
55215
|
-
"onUpdate:modelValue": r[
|
|
55234
|
+
"onUpdate:modelValue": r[16] ||= (e) => /* @__PURE__ */ ln(h) ? h.value = e : null,
|
|
55216
55235
|
"aria-label": "主持简报正文",
|
|
55217
55236
|
rows: 4,
|
|
55218
55237
|
class: "w-full summary-brief-editor",
|
|
@@ -55227,7 +55246,7 @@ var k1 = {
|
|
|
55227
55246
|
}, {
|
|
55228
55247
|
default: L(() => [q(N, {
|
|
55229
55248
|
modelValue: I(m),
|
|
55230
|
-
"onUpdate:modelValue": r[
|
|
55249
|
+
"onUpdate:modelValue": r[17] ||= (e) => /* @__PURE__ */ ln(m) ? m.value = e : null,
|
|
55231
55250
|
"aria-label": "共识与分歧正文",
|
|
55232
55251
|
rows: 12,
|
|
55233
55252
|
maxlength: 4e3,
|
|
@@ -55240,7 +55259,7 @@ var k1 = {
|
|
|
55240
55259
|
label: "取消",
|
|
55241
55260
|
color: "neutral",
|
|
55242
55261
|
variant: "ghost",
|
|
55243
|
-
onClick: r[
|
|
55262
|
+
onClick: r[18] ||= (e) => M.value = !1
|
|
55244
55263
|
}), q(T, {
|
|
55245
55264
|
type: "submit",
|
|
55246
55265
|
color: "neutral",
|
|
@@ -55249,17 +55268,17 @@ var k1 = {
|
|
|
55249
55268
|
class: "channel-primary-button",
|
|
55250
55269
|
loading: I(a) === "save_context"
|
|
55251
55270
|
}, null, 8, ["loading"])])])
|
|
55252
|
-
], 32)) : o.value.host_brief?.text || o.value.state_text ? (U(), W(H, { key: 1 }, [K("div", z2, [K("h3", null, [r[
|
|
55271
|
+
], 32)) : o.value.host_brief?.text || o.value.state_text ? (U(), W(H, { key: 1 }, [K("div", z2, [K("h3", null, [r[38] ||= J("主持简报", -1), o.value.state_until ? (U(), W("button", {
|
|
55253
55272
|
key: 0,
|
|
55254
55273
|
type: "button",
|
|
55255
55274
|
class: "subtle-link",
|
|
55256
|
-
onClick: r[
|
|
55275
|
+
onClick: r[19] ||= (e) => De(o.value.state_until)
|
|
55257
55276
|
}, "整理至 #" + P(o.value.state_until), 1)) : Y("", !0)]), K("div", {
|
|
55258
55277
|
class: "markdown-body",
|
|
55259
55278
|
innerHTML: o.value.rendered_brief
|
|
55260
55279
|
}, null, 8, B2)]), ye.value ? (U(), W("details", V2, [K("summary", null, [
|
|
55261
|
-
r[
|
|
55262
|
-
r[
|
|
55280
|
+
r[39] ||= K("h3", null, "详细共识与进度", -1),
|
|
55281
|
+
r[40] ||= K("span", null, "依据、已处理内容、未决问题与下一步", -1),
|
|
55263
55282
|
q(y, {
|
|
55264
55283
|
name: "i-lucide-chevron-down",
|
|
55265
55284
|
class: "disclosure-icon"
|
|
@@ -55271,25 +55290,25 @@ var k1 = {
|
|
|
55271
55290
|
q(S$, { onDetail: De }),
|
|
55272
55291
|
q(YZ, {
|
|
55273
55292
|
open: _e.value,
|
|
55274
|
-
"onUpdate:open": r[
|
|
55293
|
+
"onUpdate:open": r[20] ||= (e) => _e.value = e
|
|
55275
55294
|
}, null, 8, ["open"])
|
|
55276
55295
|
])) : Y("", !0),
|
|
55277
55296
|
I(t).tab === "overview" ? (U(), W("div", W2, [q(e1), ge.value ? Y("", !0) : (U(), G(jK, {
|
|
55278
55297
|
key: 0,
|
|
55279
55298
|
section: "access",
|
|
55280
|
-
onOpen: r[
|
|
55299
|
+
onOpen: r[21] ||= (e) => s.value = e,
|
|
55281
55300
|
onDetail: De
|
|
55282
55301
|
}))])) : Y("", !0),
|
|
55283
55302
|
I(t).tab === "timeline" ? (U(), W("div", G2, [q(e2, { onDetail: De })])) : Y("", !0)
|
|
55284
55303
|
])], 2)) : (U(), W("div", K2, [
|
|
55285
55304
|
q(y, { name: "i-lucide-hash" }),
|
|
55286
|
-
r[
|
|
55287
|
-
r[
|
|
55305
|
+
r[41] ||= K("h2", null, "新建一个协作房间", -1),
|
|
55306
|
+
r[42] ||= K("p", null, "邀请 AI 一起协作,随时 @ 提问;需要时开启讨论,保留所有人的观点。", -1),
|
|
55288
55307
|
q(T, {
|
|
55289
55308
|
icon: "i-lucide-plus",
|
|
55290
55309
|
label: "新建房间",
|
|
55291
55310
|
color: "neutral",
|
|
55292
|
-
onClick: r[
|
|
55311
|
+
onClick: r[22] ||= (e) => Oe()
|
|
55293
55312
|
})
|
|
55294
55313
|
]))]),
|
|
55295
55314
|
ne.value ? (U(), W("div", {
|
|
@@ -55312,10 +55331,10 @@ var k1 = {
|
|
|
55312
55331
|
onLostpointercapture: He,
|
|
55313
55332
|
onKeydown: Ue
|
|
55314
55333
|
}, null, 40, q2)) : Y("", !0),
|
|
55315
|
-
ne.value ? (U(), W("aside", J2, [K("section", Y2, [o.value ? (U(), G(n$, { key: 0 })) : (U(), W("div", X2, [...r[
|
|
55334
|
+
ne.value ? (U(), W("aside", J2, [K("section", Y2, [o.value ? (U(), G(n$, { key: 0 })) : (U(), W("div", X2, [...r[43] ||= [K("header", { class: "pb-header" }, [K("strong", null, "讨论进度")], -1), K("p", { class: "channel-rail-empty" }, "暂无房间,新建讨论后在这里查看进度与参与者。", -1)]]))])])) : Y("", !0),
|
|
55316
55335
|
q(Pe, {
|
|
55317
55336
|
open: d.value,
|
|
55318
|
-
"onUpdate:open": r[
|
|
55337
|
+
"onUpdate:open": r[24] ||= (e) => d.value = e,
|
|
55319
55338
|
side: "left",
|
|
55320
55339
|
title: "协作房间",
|
|
55321
55340
|
description: "共议 · AI CoDiscuss",
|
|
@@ -55339,7 +55358,7 @@ var k1 = {
|
|
|
55339
55358
|
color: "neutral",
|
|
55340
55359
|
variant: "ghost",
|
|
55341
55360
|
label: "设置",
|
|
55342
|
-
onClick: r[
|
|
55361
|
+
onClick: r[23] ||= (e) => {
|
|
55343
55362
|
d.value = !1, s.value = "settings";
|
|
55344
55363
|
}
|
|
55345
55364
|
})]),
|
|
@@ -55347,7 +55366,7 @@ var k1 = {
|
|
|
55347
55366
|
}, 8, ["open"]),
|
|
55348
55367
|
q(Pe, {
|
|
55349
55368
|
open: p.value,
|
|
55350
|
-
"onUpdate:open": r[
|
|
55369
|
+
"onUpdate:open": r[26] ||= (e) => p.value = e,
|
|
55351
55370
|
title: "房间资料",
|
|
55352
55371
|
description: "目标、背景与参与方式",
|
|
55353
55372
|
ui: {
|
|
@@ -55383,7 +55402,7 @@ var k1 = {
|
|
|
55383
55402
|
class: "info-section"
|
|
55384
55403
|
}, [K("h3", null, P(e.label), 1), K("p", null, P(o.value[e.key] || "尚未填写"), 1)])), 64)),
|
|
55385
55404
|
q(Fe),
|
|
55386
|
-
K("section", Q2, [r[
|
|
55405
|
+
K("section", Q2, [r[44] ||= K("h3", null, "参与身份", -1), (U(!0), W(H, null, B(Ce.value, (e) => (U(), W("div", {
|
|
55387
55406
|
key: e.name,
|
|
55388
55407
|
class: "expert-row"
|
|
55389
55408
|
}, [
|
|
@@ -55409,7 +55428,7 @@ var k1 = {
|
|
|
55409
55428
|
block: "",
|
|
55410
55429
|
class: "channel-surface-button",
|
|
55411
55430
|
label: "编辑议题",
|
|
55412
|
-
onClick: r[
|
|
55431
|
+
onClick: r[25] ||= (e) => {
|
|
55413
55432
|
p.value = !1, s.value = "edit";
|
|
55414
55433
|
}
|
|
55415
55434
|
})
|
|
@@ -55418,11 +55437,11 @@ var k1 = {
|
|
|
55418
55437
|
}, 8, ["open"]),
|
|
55419
55438
|
q(QX, {
|
|
55420
55439
|
modelValue: s.value,
|
|
55421
|
-
"onUpdate:modelValue": r[
|
|
55440
|
+
"onUpdate:modelValue": r[27] ||= (e) => s.value = e,
|
|
55422
55441
|
"reading-width": x.value,
|
|
55423
|
-
"onUpdate:readingWidth": r[
|
|
55442
|
+
"onUpdate:readingWidth": r[28] ||= (e) => x.value = e,
|
|
55424
55443
|
effects: S.value,
|
|
55425
|
-
"onUpdate:effects": r[
|
|
55444
|
+
"onUpdate:effects": r[29] ||= (e) => S.value = e,
|
|
55426
55445
|
"reading-options": I(fe),
|
|
55427
55446
|
"effect-options": v,
|
|
55428
55447
|
"detail-id": u.value,
|