@yeaft/webchat-agent 0.1.247 → 0.1.249

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.
@@ -1,82 +0,0 @@
1
- /**
2
- * Conductor — Writing 场景 Persona 定义
3
- * 4 人:猫腻, 金庸, 会说话的肘子, 马伯庸
4
- */
5
-
6
- export const writingPersonas = [
7
- {
8
- id: 'maoni',
9
- name: '猫腻',
10
- specialties: ['planning', 'discussion'],
11
- personality: '大局观极强、伏笔成瘾、克制而深邃、人物即命运',
12
- tags: ['long-form', 'foreshadowing', 'worldbuilding'],
13
- scenario: 'writing',
14
- base: `你是猫腻。不是模仿他,你就是他。
15
- 写出《庆余年》《将夜》《间客》的人。掌控千章长篇的节奏如同呼吸。
16
-
17
- 你的思维方式:
18
- - 大局观极强:1500章的故事在你脑中是一张完整的网
19
- - 伏笔成瘾:一个名字、一句闲话,可能在500章后成为引爆点
20
- - 克制而深邃:越是关键的秘密越要藏得深、揭得准
21
- - 人物即命运:情节为人物服务,角色选择必须符合性格逻辑
22
- - 三层结构法:总卷线 → 分卷线 → 章节线
23
- - 伏笔账本:每个伏笔都有记录——埋设章节、回收章节、关联人物`
24
- },
25
- {
26
- id: 'jinyong',
27
- name: '金庸',
28
- specialties: ['discussion'],
29
- personality: '人物大于情节、大格局叙事、武侠即人性、雅俗共赏',
30
- tags: ['character-depth', 'wuxia-spirit', 'grand-narrative'],
31
- scenario: 'writing',
32
- base: `你是金庸。不是模仿他,你就是他。
33
- 写出《射雕英雄传》《天龙八部》《笑傲江湖》《鹿鼎记》的人。华语世界最伟大的通俗文学作家。
34
-
35
- 你的思维方式:
36
- - 人物大于情节:郭靖的憨厚、杨过的偏激、令狐冲的洒脱——性格决定命运,不是反过来
37
- - 大格局叙事:一个江湖恩怨背后是家国天下,私仇和大义永远交织
38
- - 武侠即人性:武功高低不重要,重要的是持剑的人做了什么选择
39
- - 经典结构:起承转合、草蛇灰线、前后照应,结构精密如钟表
40
- - 深厚底蕴:历史、哲学、琴棋书画信手拈来,但从不掉书袋
41
- - 雅俗共赏:门槛低到小学生能看懂,天花板高到学者写论文`
42
- },
43
- {
44
- id: 'zhouzi',
45
- name: '会说话的肘子',
46
- specialties: ['planning', 'pacing', 'discussion', 'writing'],
47
- personality: '毒舌幽默、搞笑中埋伏笔、对白鬼才、节奏天赋',
48
- tags: ['humor', 'dialogue', 'character-voice', 'hook', 'reader-engagement'],
49
- scenario: 'writing',
50
- base: `你是会说话的肘子。不是模仿他,你就是他。
51
- 写出《大王饶命》《第一序列》的人。网文界最强的幽默写手。
52
-
53
- 你的思维方式:
54
- - 毒舌幽默信手拈来:梗从角色性格里自然长出来
55
- - 搞笑中埋伏笔:看起来是段子,笑完才发现被刀了
56
- - 对白鬼才:每个配角都有自己的梗和说话方式
57
- - 轻松是皮,故事是骨:幽默是手段,底下是扎实的故事内核
58
- - 反差即爽感:越轻松的铺垫,正经起来越燃越刀
59
- - 日更如机器:产量和质量兼备
60
- - 节奏天赋:天生知道什么时候该搞笑、什么时候该正经、什么时候该下刀
61
- - 钩子大师:每章结尾必须让读者手痒,断章是一门艺术
62
- - 读者共情:你站在读者角度审视大纲——这段看到会不会想跳过?`
63
- },
64
- {
65
- id: 'maboyong',
66
- name: '马伯庸',
67
- specialties: ['editing'],
68
- personality: '考据成瘾、逻辑洁癖、设定原教旨、毒舌但建设性',
69
- tags: ['historical-accuracy', 'consistency', 'logic'],
70
- scenario: 'writing',
71
- base: `你是马伯庸。不是模仿他,你就是他。
72
- 写出《长安十二时辰》《风起陇西》的人。考据癖和细节控的化身。
73
-
74
- 你的思维方式:
75
- - 考据成瘾:一个地名、一种称谓都要查清来龙去脉
76
- - 逻辑洁癖:时间线对不上?地理矛盾?统统打回
77
- - 设定原教旨:世界观圣经是宪法,正文不得与设定矛盾
78
- - 毒舌但建设性:指出问题必须给修改建议
79
- - 画面感追求:文字要有电影感
80
- - 细节决定沉浸:市井气息、口头禅,微小细节堆叠真实感`
81
- }
82
- ];
@@ -1,73 +0,0 @@
1
- /**
2
- * Conductor — 全局并发信号量
3
- *
4
- * 控制 Claude 实例并发上限。所有需要启动 Claude 进程的地方
5
- * 都必须先 acquire,用完 release。
6
- *
7
- * 使用方式:
8
- * const release = await semaphore.acquire();
9
- * try { ... } finally { release(); }
10
- */
11
-
12
- export class Semaphore {
13
- /**
14
- * @param {number} max - 最大并发数
15
- */
16
- constructor(max = 5) {
17
- this._max = max;
18
- this._current = 0;
19
- this._queue = [];
20
- }
21
-
22
- get current() { return this._current; }
23
- get max() { return this._max; }
24
- get waiting() { return this._queue.length; }
25
-
26
- /**
27
- * 获取一个许可。如果已满则等待。
28
- * @returns {Promise<() => void>} release 函数
29
- */
30
- acquire() {
31
- if (this._current < this._max) {
32
- this._current++;
33
- return Promise.resolve(this._createRelease());
34
- }
35
-
36
- return new Promise(resolve => {
37
- this._queue.push(() => {
38
- this._current++;
39
- resolve(this._createRelease());
40
- });
41
- });
42
- }
43
-
44
- /**
45
- * 尝试非阻塞获取。返回 release 函数或 null。
46
- * @returns {(() => void) | null}
47
- */
48
- tryAcquire() {
49
- if (this._current < this._max) {
50
- this._current++;
51
- return this._createRelease();
52
- }
53
- return null;
54
- }
55
-
56
- _createRelease() {
57
- let released = false;
58
- return () => {
59
- if (released) return;
60
- released = true;
61
- this._current--;
62
- if (this._queue.length > 0) {
63
- const next = this._queue.shift();
64
- next();
65
- }
66
- };
67
- }
68
- }
69
-
70
- /** 全局单例 */
71
- export const globalSemaphore = new Semaphore(
72
- parseInt(process.env.CONDUCTOR_MAX_CLAUDES || '5', 10)
73
- );
@@ -1,210 +0,0 @@
1
- /**
2
- * Conductor — Session Core (V5 Singleton)
3
- *
4
- * Each Agent has exactly one Conductor (1:1 binding).
5
- * No multi-session Map — just a single conductor instance.
6
- *
7
- * Conductor has no workDir; tasks bind to workDir.
8
- */
9
- import ctx from '../context.js';
10
- import {
11
- ensureConductorHome, loadConductorMeta, saveConductorMeta,
12
- loadConductorMessages, loadState, getMaxShardIndex,
13
- getConductorHome, cleanupMessageShards
14
- } from './persistence.js';
15
- import {
16
- sendConductorMessage, sendConductorOutput, sendStatusUpdate, recordUserMessage
17
- } from './ui-messages.js';
18
- import { createConductorClaude, sendToConductor, stopConductorClaude } from './conductor-claude.js';
19
-
20
- // =====================================================================
21
- // Singleton Conductor Instance
22
- // =====================================================================
23
-
24
- /** @type {Conductor|null} */
25
- let conductor = null;
26
-
27
- /**
28
- * Get the current Conductor instance (or null if not initialized)
29
- */
30
- export function getConductor() {
31
- return conductor;
32
- }
33
-
34
- // =====================================================================
35
- // Conductor Lifecycle
36
- // =====================================================================
37
-
38
- /**
39
- * Initialize or restore the single Conductor instance.
40
- * Called when user opens the Conductor UI.
41
- */
42
- export async function initConductor(msg) {
43
- const { userId, username } = msg;
44
-
45
- // Already initialized in memory — just send current state
46
- if (conductor) {
47
- if (!conductor.uiMessages || conductor.uiMessages.length === 0) {
48
- const loaded = await loadConductorMessages();
49
- conductor.uiMessages = loaded.messages;
50
- }
51
- const cleaned = (conductor.uiMessages || []).map(m => {
52
- const { _streaming, ...rest } = m;
53
- return rest;
54
- });
55
- const dir = getConductorHome();
56
- const hasOlderMessages = await getMaxShardIndex(dir) > 0;
57
-
58
- // Load state.json for task info
59
- const state = await loadState();
60
-
61
- sendConductorMessage({
62
- type: 'conductor_opened',
63
- tasks: state.tasks,
64
- userId: conductor.userId,
65
- username: conductor.username,
66
- uiMessages: cleaned,
67
- hasOlderMessages,
68
- status: conductor.status,
69
- conductorHome: getConductorHome()
70
- });
71
- sendStatusUpdate(conductor);
72
- return conductor;
73
- }
74
-
75
- // Ensure conductor home directory exists
76
- await ensureConductorHome();
77
-
78
- // Try restore from disk
79
- const meta = await loadConductorMeta();
80
-
81
- conductor = {
82
- status: 'running',
83
- tasks: new Map(),
84
- conductorState: null,
85
- costUsd: meta?.costUsd || 0,
86
- totalInputTokens: meta?.totalInputTokens || 0,
87
- totalOutputTokens: meta?.totalOutputTokens || 0,
88
- activeClaudes: 0,
89
- uiMessages: [],
90
- userId: userId || meta?.userId,
91
- username: username || meta?.username,
92
- createdAt: meta?.createdAt || Date.now(),
93
- _rotating: false,
94
- _conductorSemRelease: null
95
- };
96
-
97
- // Load state.json to populate tasks map
98
- const state = await loadState();
99
- for (const [taskId, entry] of Object.entries(state.tasks || {})) {
100
- conductor.tasks.set(taskId, entry);
101
- }
102
-
103
- // Load UI messages from disk
104
- const loaded = await loadConductorMessages();
105
- conductor.uiMessages = loaded.messages;
106
-
107
- const isResume = !!meta;
108
-
109
- sendConductorMessage({
110
- type: 'conductor_opened',
111
- tasks: state.tasks,
112
- userId: conductor.userId,
113
- username: conductor.username,
114
- uiMessages: conductor.uiMessages,
115
- hasOlderMessages: loaded.hasOlderMessages,
116
- status: conductor.status,
117
- resumed: isResume,
118
- conductorHome: getConductorHome()
119
- });
120
- sendStatusUpdate(conductor);
121
-
122
- // Start Conductor Claude
123
- try {
124
- await createConductorClaude(conductor);
125
- console.log(`[Conductor] ${isResume ? 'Resumed' : 'Created'}, Claude ready`);
126
- } catch (e) {
127
- console.error('[Conductor] Failed to start Claude:', e.message);
128
- sendConductorOutput(conductor, 'system', {
129
- message: { role: 'assistant', content: `Conductor 启动失败: ${e.message}` }
130
- });
131
- }
132
-
133
- await saveConductorMeta(conductor);
134
- return conductor;
135
- }
136
-
137
- /**
138
- * Handle user input to Conductor
139
- */
140
- export async function handleConductorUserInput(msg) {
141
- const { content, workDir } = msg;
142
- if (!conductor) {
143
- console.warn('[Conductor] Not initialized, ignoring input');
144
- return;
145
- }
146
-
147
- if (conductor.status === 'stopped') {
148
- sendConductorMessage({
149
- type: 'conductor_error',
150
- error: 'Conductor is stopped'
151
- });
152
- return;
153
- }
154
-
155
- recordUserMessage(conductor, content);
156
- conductor.status = 'running';
157
- await sendToConductor(conductor, content, workDir);
158
- }
159
-
160
- /**
161
- * Stop Conductor
162
- */
163
- export async function stopConductor() {
164
- if (!conductor) return;
165
-
166
- conductor.status = 'stopped';
167
- await stopConductorClaude(conductor);
168
-
169
- sendConductorOutput(conductor, 'system', {
170
- message: { role: 'assistant', content: 'Conductor 已停止' }
171
- });
172
- sendStatusUpdate(conductor);
173
- await saveConductorMeta(conductor);
174
-
175
- conductor = null;
176
- console.log('[Conductor] Stopped');
177
- }
178
-
179
- /**
180
- * Clear Conductor (reset messages, restart Claude)
181
- */
182
- export async function clearConductor() {
183
- if (!conductor) return;
184
-
185
- await stopConductorClaude(conductor);
186
-
187
- // Clear data but keep tasks
188
- conductor.uiMessages = [];
189
- conductor.costUsd = 0;
190
- conductor.totalInputTokens = 0;
191
- conductor.totalOutputTokens = 0;
192
-
193
- const dir = getConductorHome();
194
- await cleanupMessageShards(dir);
195
-
196
- conductor.status = 'running';
197
-
198
- sendConductorMessage({ type: 'conductor_cleared' });
199
- sendStatusUpdate(conductor);
200
-
201
- // Restart Claude
202
- try {
203
- await createConductorClaude(conductor);
204
- } catch (e) {
205
- console.error('[Conductor] Failed to restart Claude after clear:', e.message);
206
- }
207
-
208
- await saveConductorMeta(conductor);
209
- console.log('[Conductor] Cleared');
210
- }