@yeaft/webchat-agent 0.1.308 → 0.1.310

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.
@@ -70,6 +70,10 @@ export async function handleMessage(msg) {
70
70
  await createConversation(msg);
71
71
  break;
72
72
 
73
+ case 'request_slash_commands':
74
+ preloadSlashCommands().catch(() => {});
75
+ break;
76
+
73
77
  case 'check_crew_context':
74
78
  handleCheckCrewContext(msg);
75
79
  break;
package/conversation.js CHANGED
@@ -859,28 +859,59 @@ export function handleAskUserAnswer(msg) {
859
859
  }
860
860
 
861
861
  /**
862
- * Handle /btw side question — ephemeral, no history pollution.
863
- * Spawns an independent CLI process with --resume + --fork-session so
864
- * the answer has full context but writes nothing back to the original session.
862
+ * Handle /btw side question — supports multi-turn and Crew mode.
863
+ *
864
+ * Multi-turn: First question forks the session (forkSession: true).
865
+ * Subsequent questions resume the forked session (no fork).
866
+ * The forked session ID is captured from system init and returned in btw_done.
867
+ *
868
+ * Crew mode: If conversationId is a crew session, use the decision maker's
869
+ * claudeSessionId as the base for forking.
865
870
  */
866
871
  export async function handleBtwQuestion(msg) {
867
- const { conversationId, question } = msg;
868
- const state = ctx.conversations.get(conversationId);
872
+ const { conversationId, question, btwSessionId } = msg;
873
+
874
+ // 1. Find the base session — Chat or Crew decision maker
875
+ let baseSessionId = null;
876
+ let workDir = null;
877
+
878
+ const chatState = ctx.conversations.get(conversationId);
879
+ if (chatState?.claudeSessionId) {
880
+ baseSessionId = chatState.claudeSessionId;
881
+ workDir = chatState.workDir;
882
+ } else {
883
+ // Crew mode: find decision maker's session
884
+ const crewSession = crewSessions.get(conversationId);
885
+ if (crewSession) {
886
+ const dmName = crewSession.decisionMaker;
887
+ const dmState = dmName ? crewSession.roleStates.get(dmName) : null;
888
+ if (dmState?.claudeSessionId) {
889
+ baseSessionId = dmState.claudeSessionId;
890
+ workDir = crewSession.projectDir;
891
+ }
892
+ }
893
+ }
869
894
 
870
- if (!state?.claudeSessionId) {
871
- ctx.sendToServer({ type: 'btw_error', conversationId, question, error: 'No active session' });
895
+ if (!baseSessionId) {
896
+ ctx.sendToServer({ type: 'btw_error', conversationId, error: 'No active session' });
872
897
  return;
873
898
  }
874
899
 
875
- console.log(`[btw] ${conversationId} question: ${question.substring(0, 80)}`);
900
+ // 2. Determine resume target: multi-turn reuses btwSessionId, first question forks
901
+ const resumeTarget = btwSessionId || baseSessionId;
902
+ const shouldFork = !btwSessionId;
903
+
904
+ console.log(`[btw] ${conversationId} question: ${question.substring(0, 80)} (fork: ${shouldFork}, session: ${resumeTarget.substring(0, 20)}...)`);
905
+
906
+ let newBtwSessionId = btwSessionId; // default: keep existing
876
907
 
877
908
  try {
878
909
  const btwQuery = query({
879
910
  prompt: question,
880
911
  options: {
881
- cwd: state.workDir,
882
- resume: state.claudeSessionId,
883
- forkSession: true,
912
+ cwd: workDir || ctx.CONFIG.workDir,
913
+ resume: resumeTarget,
914
+ forkSession: shouldFork,
884
915
  maxTurns: 1,
885
916
  permissionMode: 'bypassPermissions',
886
917
  disallowedTools: [
@@ -894,6 +925,11 @@ export async function handleBtwQuestion(msg) {
894
925
  });
895
926
 
896
927
  for await (const message of btwQuery) {
928
+ // Capture forked session ID from system init
929
+ if (message.type === 'system' && message.session_id) {
930
+ newBtwSessionId = message.session_id;
931
+ }
932
+
897
933
  if (message.type === 'assistant') {
898
934
  const content = message.message?.content;
899
935
  let delta = '';
@@ -908,12 +944,15 @@ export async function handleBtwQuestion(msg) {
908
944
  ctx.sendToServer({ type: 'btw_stream', conversationId, delta });
909
945
  }
910
946
  }
911
- // result / system messages — ignored (no usage tracking for btw)
912
947
  }
913
948
 
914
- ctx.sendToServer({ type: 'btw_done', conversationId, question });
949
+ ctx.sendToServer({
950
+ type: 'btw_done',
951
+ conversationId,
952
+ btwSessionId: newBtwSessionId
953
+ });
915
954
  } catch (err) {
916
955
  console.error(`[btw] ${conversationId} error:`, err.message);
917
- ctx.sendToServer({ type: 'btw_error', conversationId, question, error: err.message });
956
+ ctx.sendToServer({ type: 'btw_error', conversationId, error: err.message });
918
957
  }
919
958
  }
@@ -141,9 +141,11 @@ async function _createRoleQueryInner(session, roleName) {
141
141
  // 尝试加载之前保存的 sessionId
142
142
  const savedSessionId = await loadRoleSessionId(session.sharedDir, roleName);
143
143
 
144
- // cwd 设为角色目录(确保目录存在,否则 spawn() 会报误导性的 ENOENT)
145
- const roleCwd = join(session.sharedDir, 'roles', roleName);
146
- mkdirSync(roleCwd, { recursive: true });
144
+ // cwd 设为项目目录(确保 Claude CLI 能匹配项目级 MCP 配置)
145
+ // 角色的 CLAUDE.md 和工作路径已通过 appendSystemPrompt 注入
146
+ const roleCwd = session.projectDir || join(session.sharedDir, 'roles', roleName);
147
+ const roleDir = join(session.sharedDir, 'roles', roleName);
148
+ mkdirSync(roleDir, { recursive: true });
147
149
 
148
150
  // 继承全局 MCP disallowedTools,避免不必要的 tool schema token 消耗
149
151
  const globalDisallowed = ctx.CONFIG?.disallowedTools || [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.308",
3
+ "version": "0.1.310",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",