@yeaft/webchat-agent 0.1.60 → 0.1.62

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/claude.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { query, Stream } from './sdk/index.js';
2
2
  import ctx from './context.js';
3
3
  import { sendConversationList, sendOutput, sendError, handleAskUserQuestion } from './conversation.js';
4
- import { buildVCrewSystemPrompt } from './vcrew.js';
4
+ import { buildRolePlaySystemPrompt } from './roleplay.js';
5
5
 
6
6
  /**
7
7
  * Start a Claude SDK query for a conversation
@@ -10,13 +10,13 @@ import { buildVCrewSystemPrompt } from './vcrew.js';
10
10
  export async function startClaudeQuery(conversationId, workDir, resumeSessionId) {
11
11
  // 如果已存在,先保存 per-session 设置,再关闭
12
12
  let savedDisallowedTools = null;
13
- let savedVcrewConfig = null;
13
+ let savedRolePlayConfig = null;
14
14
  let savedUserId = undefined;
15
15
  let savedUsername = undefined;
16
16
  if (ctx.conversations.has(conversationId)) {
17
17
  const existing = ctx.conversations.get(conversationId);
18
18
  savedDisallowedTools = existing.disallowedTools ?? null;
19
- savedVcrewConfig = existing.vcrewConfig ?? null;
19
+ savedRolePlayConfig = existing.rolePlayConfig ?? null;
20
20
  savedUserId = existing.userId;
21
21
  savedUsername = existing.username;
22
22
  if (existing.abortController) {
@@ -54,8 +54,8 @@ export async function startClaudeQuery(conversationId, workDir, resumeSessionId)
54
54
  backgroundTasks: new Map(),
55
55
  // Per-session 工具禁用设置
56
56
  disallowedTools: savedDisallowedTools,
57
- // Virtual Crew config (for appendSystemPrompt injection)
58
- vcrewConfig: savedVcrewConfig,
57
+ // Role Play config (for appendSystemPrompt injection)
58
+ rolePlayConfig: savedRolePlayConfig,
59
59
  // 保留用户信息(从旧 state 恢复)
60
60
  userId: savedUserId,
61
61
  username: savedUsername,
@@ -85,10 +85,10 @@ export async function startClaudeQuery(conversationId, workDir, resumeSessionId)
85
85
  console.log(`[SDK] Disallowed tools: ${effectiveDisallowedTools.join(', ')}`);
86
86
  }
87
87
 
88
- // Virtual Crew: inject appendSystemPrompt with role descriptions and workflow
89
- if (savedVcrewConfig) {
90
- options.appendSystemPrompt = buildVCrewSystemPrompt(savedVcrewConfig);
91
- console.log(`[SDK] VCrew appendSystemPrompt injected (teamType: ${savedVcrewConfig.teamType})`);
88
+ // Role Play: inject appendSystemPrompt with role descriptions and workflow
89
+ if (savedRolePlayConfig) {
90
+ options.appendSystemPrompt = buildRolePlaySystemPrompt(savedRolePlayConfig);
91
+ console.log(`[SDK] RolePlay appendSystemPrompt injected (teamType: ${savedRolePlayConfig.teamType})`);
92
92
  }
93
93
 
94
94
  // Validate session ID is a valid UUID before using it
package/conversation.js CHANGED
@@ -2,10 +2,10 @@ import ctx from './context.js';
2
2
  import { loadSessionHistory } from './history.js';
3
3
  import { startClaudeQuery } from './claude.js';
4
4
  import { crewSessions, loadCrewIndex } from './crew.js';
5
- import { vcrewSessions, saveVCrewIndex, removeVCrewSession, loadVCrewIndex, validateVCrewConfig } from './vcrew.js';
5
+ import { rolePlaySessions, saveRolePlayIndex, removeRolePlaySession, loadRolePlayIndex, validateRolePlayConfig } from './roleplay.js';
6
6
 
7
- // Restore persisted vcrew sessions on module load (agent startup)
8
- loadVCrewIndex();
7
+ // Restore persisted roleplay sessions on module load (agent startup)
8
+ loadRolePlayIndex();
9
9
 
10
10
  // 不支持的斜杠命令(真正需要交互式 CLI 的命令)
11
11
  const UNSUPPORTED_SLASH_COMMANDS = ['/help', '/bug', '/login', '/logout', '/terminal-setup', '/vim', '/config'];
@@ -38,7 +38,7 @@ export function parseSlashCommand(message) {
38
38
  return { type: null, message };
39
39
  }
40
40
 
41
- // 发送 conversation 列表(含活跃 crew sessions + 索引中已停止的 crew sessions + vcrew sessions)
41
+ // 发送 conversation 列表(含活跃 crew sessions + 索引中已停止的 crew sessions + roleplay sessions)
42
42
  export async function sendConversationList() {
43
43
  const list = [];
44
44
  for (const [id, state] of ctx.conversations) {
@@ -51,10 +51,10 @@ export async function sendConversationList() {
51
51
  userId: state.userId,
52
52
  username: state.username
53
53
  };
54
- // vcrew conversations are stored in ctx.conversations but also tracked in vcrewSessions
55
- if (vcrewSessions.has(id)) {
56
- entry.type = 'virtualCrew';
57
- entry.vcrewRoles = vcrewSessions.get(id).roles;
54
+ // roleplay conversations are stored in ctx.conversations but also tracked in rolePlaySessions
55
+ if (rolePlaySessions.has(id)) {
56
+ entry.type = 'rolePlay';
57
+ entry.rolePlayRoles = rolePlaySessions.get(id).roles;
58
58
  }
59
59
  list.push(entry);
60
60
  }
@@ -118,21 +118,21 @@ export async function createConversation(msg) {
118
118
  const { conversationId, workDir, userId, username, disallowedTools } = msg;
119
119
  const effectiveWorkDir = workDir || ctx.CONFIG.workDir;
120
120
 
121
- // Validate and sanitize vcrewConfig if provided
122
- let vcrewConfig = null;
123
- if (msg.vcrewConfig) {
124
- const result = validateVCrewConfig(msg.vcrewConfig);
121
+ // Validate and sanitize rolePlayConfig if provided
122
+ let rolePlayConfig = null;
123
+ if (msg.rolePlayConfig) {
124
+ const result = validateRolePlayConfig(msg.rolePlayConfig);
125
125
  if (!result.valid) {
126
- console.warn(`[createConversation] Invalid vcrewConfig: ${result.error}`);
127
- sendError(conversationId, `Invalid vcrewConfig: ${result.error}`);
126
+ console.warn(`[createConversation] Invalid rolePlayConfig: ${result.error}`);
127
+ sendError(conversationId, `Invalid rolePlayConfig: ${result.error}`);
128
128
  return;
129
129
  }
130
- vcrewConfig = result.config;
130
+ rolePlayConfig = result.config;
131
131
  }
132
132
 
133
133
  console.log(`Creating conversation: ${conversationId} in ${effectiveWorkDir} (lazy start)`);
134
134
  if (username) console.log(` User: ${username} (${userId})`);
135
- if (vcrewConfig) console.log(` VCrew: teamType=${vcrewConfig.teamType}, roles=${vcrewConfig.roles?.length}`);
135
+ if (rolePlayConfig) console.log(` RolePlay: teamType=${rolePlayConfig.teamType}, roles=${rolePlayConfig.roles?.length}`);
136
136
 
137
137
  // 只创建 conversation 状态,不启动 Claude 进程
138
138
  // Claude 进程会在用户发送第一条消息时启动 (见 handleUserInput)
@@ -149,7 +149,7 @@ export async function createConversation(msg) {
149
149
  userId,
150
150
  username,
151
151
  disallowedTools: disallowedTools || null, // null = 使用全局默认
152
- vcrewConfig: vcrewConfig || null,
152
+ rolePlayConfig: rolePlayConfig || null,
153
153
  usage: {
154
154
  inputTokens: 0,
155
155
  outputTokens: 0,
@@ -159,18 +159,18 @@ export async function createConversation(msg) {
159
159
  }
160
160
  });
161
161
 
162
- // Register in vcrewSessions for type inference in sendConversationList
163
- if (vcrewConfig) {
164
- vcrewSessions.set(conversationId, {
165
- roles: vcrewConfig.roles,
166
- teamType: vcrewConfig.teamType,
167
- language: vcrewConfig.language,
162
+ // Register in rolePlaySessions for type inference in sendConversationList
163
+ if (rolePlayConfig) {
164
+ rolePlaySessions.set(conversationId, {
165
+ roles: rolePlayConfig.roles,
166
+ teamType: rolePlayConfig.teamType,
167
+ language: rolePlayConfig.language,
168
168
  projectDir: effectiveWorkDir,
169
169
  createdAt: Date.now(),
170
170
  userId,
171
171
  username,
172
172
  });
173
- saveVCrewIndex();
173
+ saveRolePlayIndex();
174
174
  }
175
175
 
176
176
  ctx.sendToServer({
@@ -180,7 +180,7 @@ export async function createConversation(msg) {
180
180
  userId,
181
181
  username,
182
182
  disallowedTools: disallowedTools || null,
183
- vcrewConfig: vcrewConfig || null
183
+ rolePlayConfig: rolePlayConfig || null
184
184
  });
185
185
 
186
186
  // 立即发送 agent 级别的 MCP servers 列表(从 ~/.claude.json 读取的)
@@ -232,10 +232,10 @@ export async function resumeConversation(msg) {
232
232
 
233
233
  // 只创建 conversation 状态并保存 claudeSessionId,不启动 Claude 进程
234
234
  // Claude 进程会在用户发送第一条消息时启动 (见 handleUserInput)
235
- // Restore vcrewConfig from persisted vcrewSessions if available
236
- const vcrewEntry = vcrewSessions.get(conversationId);
237
- const vcrewConfig = vcrewEntry
238
- ? { roles: vcrewEntry.roles, teamType: vcrewEntry.teamType, language: vcrewEntry.language }
235
+ // Restore rolePlayConfig from persisted rolePlaySessions if available
236
+ const rolePlayEntry = rolePlaySessions.get(conversationId);
237
+ const rolePlayConfig = rolePlayEntry
238
+ ? { roles: rolePlayEntry.roles, teamType: rolePlayEntry.teamType, language: rolePlayEntry.language }
239
239
  : null;
240
240
 
241
241
  ctx.conversations.set(conversationId, {
@@ -251,7 +251,7 @@ export async function resumeConversation(msg) {
251
251
  userId,
252
252
  username,
253
253
  disallowedTools: disallowedTools || null, // null = 使用全局默认
254
- vcrewConfig,
254
+ rolePlayConfig,
255
255
  usage: {
256
256
  inputTokens: 0,
257
257
  outputTokens: 0,
@@ -317,9 +317,9 @@ export function deleteConversation(msg) {
317
317
  ctx.conversations.delete(conversationId);
318
318
  }
319
319
 
320
- // Clean up vcrew session if applicable
321
- if (vcrewSessions.has(conversationId)) {
322
- removeVCrewSession(conversationId);
320
+ // Clean up roleplay session if applicable
321
+ if (rolePlaySessions.has(conversationId)) {
322
+ removeRolePlaySession(conversationId);
323
323
  }
324
324
 
325
325
  ctx.sendToServer({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.60",
3
+ "version": "0.1.62",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -1,52 +1,68 @@
1
1
  /**
2
- * Virtual Crew — lightweight multi-role collaboration within a single conversation.
2
+ * Role Play — lightweight multi-role collaboration within a single conversation.
3
3
  *
4
- * Manages vcrewSessions (in-memory + persisted to disk) and builds the
4
+ * Manages rolePlaySessions (in-memory + persisted to disk) and builds the
5
5
  * appendSystemPrompt that instructs Claude to role-play multiple characters.
6
6
  */
7
7
 
8
8
  import { join } from 'path';
9
9
  import { homedir } from 'os';
10
- import { readFileSync, writeFileSync, existsSync } from 'fs';
10
+ import { readFileSync, writeFileSync, existsSync, renameSync } from 'fs';
11
11
 
12
- const VCREW_INDEX_PATH = join(homedir(), '.claude', 'vcrew-sessions.json');
12
+ const ROLEPLAY_INDEX_PATH = join(homedir(), '.claude', 'roleplay-sessions.json');
13
+ // ★ backward compat: old filename before rename
14
+ const LEGACY_INDEX_PATH = join(homedir(), '.claude', 'vcrew-sessions.json');
13
15
 
14
16
  // In-memory map: conversationId -> { roles, teamType, language, projectDir, createdAt, userId, username }
15
- export const vcrewSessions = new Map();
17
+ export const rolePlaySessions = new Map();
16
18
 
17
19
  // ---------------------------------------------------------------------------
18
20
  // Persistence
19
21
  // ---------------------------------------------------------------------------
20
22
 
21
- export function saveVCrewIndex() {
23
+ export function saveRolePlayIndex() {
22
24
  const data = [];
23
- for (const [id, session] of vcrewSessions) {
25
+ for (const [id, session] of rolePlaySessions) {
24
26
  data.push({ id, ...session });
25
27
  }
26
28
  try {
27
- writeFileSync(VCREW_INDEX_PATH, JSON.stringify(data, null, 2));
29
+ writeFileSync(ROLEPLAY_INDEX_PATH, JSON.stringify(data, null, 2));
28
30
  } catch (e) {
29
- console.warn('[vcrew] Failed to save index:', e.message);
31
+ console.warn('[roleplay] Failed to save index:', e.message);
30
32
  }
31
33
  }
32
34
 
33
- export function loadVCrewIndex() {
34
- if (!existsSync(VCREW_INDEX_PATH)) return;
35
+ export function loadRolePlayIndex() {
36
+ let indexPath = ROLEPLAY_INDEX_PATH;
37
+
38
+ // ★ backward compat: migrate old vcrew-sessions.json → roleplay-sessions.json
39
+ if (!existsSync(indexPath) && existsSync(LEGACY_INDEX_PATH)) {
40
+ try {
41
+ renameSync(LEGACY_INDEX_PATH, indexPath);
42
+ console.log('[roleplay] Migrated vcrew-sessions.json → roleplay-sessions.json');
43
+ } catch (e) {
44
+ // rename failed (e.g. permissions), fall back to reading old file directly
45
+ console.warn('[roleplay] Could not rename legacy index, reading in-place:', e.message);
46
+ indexPath = LEGACY_INDEX_PATH;
47
+ }
48
+ }
49
+
50
+ if (!existsSync(indexPath)) return;
35
51
  try {
36
- const data = JSON.parse(readFileSync(VCREW_INDEX_PATH, 'utf-8'));
52
+ const data = JSON.parse(readFileSync(indexPath, 'utf-8'));
37
53
  for (const entry of data) {
38
54
  const { id, ...session } = entry;
39
- vcrewSessions.set(id, session);
55
+ rolePlaySessions.set(id, session);
40
56
  }
41
- console.log(`[vcrew] Loaded ${vcrewSessions.size} sessions from index`);
57
+ console.log(`[roleplay] Loaded ${rolePlaySessions.size} sessions from index`);
42
58
  } catch (e) {
43
- console.warn('[vcrew] Failed to load index:', e.message);
59
+ console.warn('[roleplay] Failed to load index:', e.message);
44
60
  }
45
61
  }
46
62
 
47
- export function removeVCrewSession(conversationId) {
48
- vcrewSessions.delete(conversationId);
49
- saveVCrewIndex();
63
+ export function removeRolePlaySession(conversationId) {
64
+ rolePlaySessions.delete(conversationId);
65
+ saveRolePlayIndex();
50
66
  }
51
67
 
52
68
  // ---------------------------------------------------------------------------
@@ -60,15 +76,15 @@ const MAX_CLAUDE_MD_LEN = 4096;
60
76
  const MAX_ROLES = 10;
61
77
 
62
78
  /**
63
- * Validate and sanitize vcrewConfig from the client.
79
+ * Validate and sanitize rolePlayConfig from the client.
64
80
  * Returns { valid: true, config: sanitizedConfig } or { valid: false, error: string }.
65
81
  *
66
- * @param {*} config - raw vcrewConfig from client message
82
+ * @param {*} config - raw rolePlayConfig from client message
67
83
  * @returns {{ valid: boolean, config?: object, error?: string }}
68
84
  */
69
- export function validateVCrewConfig(config) {
85
+ export function validateRolePlayConfig(config) {
70
86
  if (!config || typeof config !== 'object') {
71
- return { valid: false, error: 'vcrewConfig must be an object' };
87
+ return { valid: false, error: 'rolePlayConfig must be an object' };
72
88
  }
73
89
 
74
90
  // teamType
@@ -141,13 +157,13 @@ export function validateVCrewConfig(config) {
141
157
  // ---------------------------------------------------------------------------
142
158
 
143
159
  /**
144
- * Build the appendSystemPrompt that tells Claude about the virtual crew roles
160
+ * Build the appendSystemPrompt that tells Claude about the role play roles
145
161
  * and how to switch between them.
146
162
  *
147
163
  * @param {{ roles: Array, teamType: string, language: string }} config
148
164
  * @returns {string}
149
165
  */
150
- export function buildVCrewSystemPrompt(config) {
166
+ export function buildRolePlaySystemPrompt(config) {
151
167
  const { roles, teamType, language } = config;
152
168
  const isZh = language === 'zh-CN';
153
169