@yeaft/webchat-agent 0.1.124 → 0.1.125

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,17 +1,6 @@
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 {
5
- buildRolePlaySystemPrompt,
6
- rolePlaySessions,
7
- initRolePlayRouteState,
8
- detectRoleSignal,
9
- processRolePlayRoutes,
10
- buildRouteEventMessage,
11
- getRolePlayRouteState,
12
- refreshCrewContext,
13
- writeBackRouteContext
14
- } from './roleplay.js';
15
4
 
16
5
  /**
17
6
  * Determine maxContextTokens and autoCompactThreshold from model name.
@@ -36,13 +25,11 @@ export function getModelContextConfig(modelName) {
36
25
  export async function startClaudeQuery(conversationId, workDir, resumeSessionId) {
37
26
  // 如果已存在,先保存 per-session 设置,再关闭
38
27
  let savedDisallowedTools = null;
39
- let savedRolePlayConfig = null;
40
28
  let savedUserId = undefined;
41
29
  let savedUsername = undefined;
42
30
  if (ctx.conversations.has(conversationId)) {
43
31
  const existing = ctx.conversations.get(conversationId);
44
32
  savedDisallowedTools = existing.disallowedTools ?? null;
45
- savedRolePlayConfig = existing.rolePlayConfig ?? null;
46
33
  savedUserId = existing.userId;
47
34
  savedUsername = existing.username;
48
35
  if (existing.abortController) {
@@ -80,8 +67,6 @@ export async function startClaudeQuery(conversationId, workDir, resumeSessionId)
80
67
  backgroundTasks: new Map(),
81
68
  // Per-session 工具禁用设置
82
69
  disallowedTools: savedDisallowedTools,
83
- // Role Play config (for appendSystemPrompt injection)
84
- rolePlayConfig: savedRolePlayConfig,
85
70
  // 保留用户信息(从旧 state 恢复)
86
71
  userId: savedUserId,
87
72
  username: savedUsername,
@@ -111,18 +96,6 @@ export async function startClaudeQuery(conversationId, workDir, resumeSessionId)
111
96
  console.log(`[SDK] Disallowed tools: ${effectiveDisallowedTools.join(', ')}`);
112
97
  }
113
98
 
114
- // Role Play: inject appendSystemPrompt with role descriptions and workflow
115
- if (savedRolePlayConfig) {
116
- options.appendSystemPrompt = buildRolePlaySystemPrompt(savedRolePlayConfig);
117
- console.log(`[SDK] RolePlay appendSystemPrompt injected (teamType: ${savedRolePlayConfig.teamType})`);
118
-
119
- // Initialize RolePlay route state if session exists
120
- const rpSession = rolePlaySessions.get(conversationId);
121
- if (rpSession) {
122
- initRolePlayRouteState(rpSession, state);
123
- }
124
- }
125
-
126
99
  // Validate session ID is a valid UUID before using it
127
100
  const isValidUUID = (id) => {
128
101
  if (!id) return false;
@@ -461,57 +434,6 @@ async function processClaudeOutput(conversationId, claudeQuery, state) {
461
434
  continue;
462
435
  }
463
436
 
464
- // ★ RolePlay ROUTE detection: check accumulated text for ROUTE blocks
465
- const rpSession = state.rolePlayConfig ? rolePlaySessions.get(conversationId) : null;
466
- let roleplayAutoContinue = false;
467
- let roleplayContinueRoles = [];
468
-
469
- if (rpSession && rpSession._routeInitialized && state._roleplayAccumulated) {
470
- const { routes, hasHumanRoute, continueRoles } = processRolePlayRoutes(
471
- state._roleplayAccumulated, rpSession
472
- );
473
-
474
- if (routes.length > 0) {
475
- // Send route events to frontend
476
- for (const route of routes) {
477
- const routeEvent = buildRouteEventMessage(
478
- conversationId, rpSession.currentRole || 'unknown', route
479
- );
480
- ctx.sendToServer(routeEvent);
481
- }
482
-
483
- // ★ Write-back: persist route task info to .crew/context/features
484
- const taskRoutes = routes.filter(r => r.taskId && r.summary);
485
- if (taskRoutes.length > 0 && state.workDir) {
486
- writeBackRouteContext(state.workDir, taskRoutes, rpSession.currentRole || 'unknown', rpSession);
487
- }
488
-
489
- // Send route state update
490
- const routeState = getRolePlayRouteState(conversationId);
491
- if (routeState) {
492
- ctx.sendToServer({
493
- type: 'roleplay_status',
494
- conversationId,
495
- ...routeState
496
- });
497
- }
498
-
499
- if (hasHumanRoute) {
500
- // Stop auto-continue, wait for user input
501
- ctx.sendToServer({
502
- type: 'roleplay_waiting_human',
503
- conversationId,
504
- fromRole: rpSession.currentRole,
505
- message: rpSession.waitingHumanContext?.message || ''
506
- });
507
- } else if (continueRoles.length > 0) {
508
- // Auto-continue: pick the first route target and send the prompt
509
- roleplayAutoContinue = true;
510
- roleplayContinueRoles = continueRoles;
511
- }
512
- }
513
- }
514
-
515
437
  // ★ Turn 完成:发送 turn_completed,进程继续运行等待下一条消息
516
438
  // stream-json 模式下 Claude 进程是持久运行的,for-await 在 result 后继续等待
517
439
  // 不清空 state.query 和 state.inputStream,下次用户消息直接通过同一个 inputStream 发送
@@ -523,89 +445,6 @@ async function processClaudeOutput(conversationId, claudeQuery, state) {
523
445
  // 不 await 会导致 encrypt 失败时消息静默丢失,前端卡在"思考中"
524
446
  await sendOutput(conversationId, message);
525
447
 
526
- // ★ RolePlay auto-continue: inject next role's prompt into the same conversation
527
- if (roleplayAutoContinue && rpSession && state.inputStream) {
528
- // Reset accumulated text for next turn
529
- state._roleplayAccumulated = '';
530
-
531
- for (const { to, prompt, taskId, taskTitle } of roleplayContinueRoles) {
532
- rpSession.currentRole = to;
533
- if (rpSession.roleStates[to]) {
534
- rpSession.roleStates[to].status = 'active';
535
- }
536
-
537
- console.log(`[RolePlay] Auto-continuing to role: ${to}`);
538
-
539
- // ★ Send roleplay_status with updated currentRole so frontend knows which role is active
540
- ctx.sendToServer({
541
- type: 'roleplay_status',
542
- conversationId,
543
- currentRole: rpSession.currentRole,
544
- round: rpSession.round,
545
- features: rpSession.features ? Array.from(rpSession.features.values()) : [],
546
- roleStates: rpSession.roleStates || {},
547
- waitingHuman: false
548
- });
549
-
550
- // ★ Pre-send compact check for RolePlay auto-continue
551
- const rpAutoCompactThreshold = state.autoCompactThreshold || ctx.CONFIG?.autoCompactThreshold || 110000;
552
- const rpEstimatedNewTokens = Math.ceil(prompt.length / 3);
553
- // Include output_tokens: the assistant's output becomes part of context for the next turn
554
- const rpOutputTokens = message.usage?.output_tokens || 0;
555
- const rpEstimatedTotal = inputTokens + rpOutputTokens + rpEstimatedNewTokens;
556
-
557
- if (rpEstimatedTotal > rpAutoCompactThreshold) {
558
- console.log(`[RolePlay] Pre-send compact: estimated ${rpEstimatedTotal} tokens (input: ${inputTokens} + output: ${rpOutputTokens} + new: ~${rpEstimatedNewTokens}) exceeds threshold ${rpAutoCompactThreshold}`);
559
- ctx.sendToServer({
560
- type: 'compact_status',
561
- conversationId,
562
- status: 'compacting',
563
- message: `Auto-compacting before RolePlay continue: estimated ${rpEstimatedTotal} tokens (threshold: ${rpAutoCompactThreshold})`
564
- });
565
- // Store pending message and compact first
566
- const userMessage = {
567
- type: 'user',
568
- message: { role: 'user', content: prompt }
569
- };
570
- state._pendingUserMessage = userMessage;
571
- state.turnActive = true;
572
- state.turnResultReceived = false;
573
- state.inputStream.enqueue({
574
- type: 'user',
575
- message: { role: 'user', content: '/compact' }
576
- });
577
- sendConversationList();
578
- break;
579
- }
580
-
581
- // Re-activate the turn
582
- state.turnActive = true;
583
- state.turnResultReceived = false;
584
-
585
- // Send the continuation prompt through the same input stream
586
- const userMessage = {
587
- type: 'user',
588
- message: { role: 'user', content: prompt }
589
- };
590
- state._lastUserMessage = userMessage; // Save for prompt-overflow retry
591
- sendOutput(conversationId, userMessage);
592
- state.inputStream.enqueue(userMessage);
593
-
594
- // RolePlay uses a single conversation — only one target can be active
595
- // at a time. Additional route targets are ignored.
596
- break;
597
- }
598
-
599
- // Send status update (don't send turn_completed yet since we're continuing)
600
- sendConversationList();
601
- continue;
602
- }
603
-
604
- // Reset accumulated text
605
- if (state._roleplayAccumulated !== undefined) {
606
- state._roleplayAccumulated = '';
607
- }
608
-
609
448
  await ctx.sendToServer({
610
449
  type: 'turn_completed',
611
450
  conversationId,
@@ -630,72 +469,9 @@ async function processClaudeOutput(conversationId, claudeQuery, state) {
630
469
  continue;
631
470
  }
632
471
 
633
- // ★ RolePlay: accumulate assistant text and detect ROLE signals
634
- if (state.rolePlayConfig && message.type === 'assistant' && message.message?.content) {
635
- const content = message.message.content;
636
- let textChunk = '';
637
- if (typeof content === 'string') {
638
- textChunk = content;
639
- } else if (Array.isArray(content)) {
640
- textChunk = content.filter(b => b.type === 'text').map(b => b.text).join('');
641
- }
642
- if (textChunk) {
643
- if (state._roleplayAccumulated === undefined) {
644
- state._roleplayAccumulated = '';
645
- }
646
- state._roleplayAccumulated += textChunk;
647
-
648
- // Detect ROLE signal for current role tracking
649
- const rpSession = rolePlaySessions.get(conversationId);
650
- if (rpSession && rpSession._routeInitialized) {
651
- const detectedRole = detectRoleSignal(textChunk);
652
- if (detectedRole) {
653
- const prevRole = rpSession.currentRole;
654
- rpSession.currentRole = detectedRole;
655
- if (rpSession.roleStates[detectedRole]) {
656
- rpSession.roleStates[detectedRole].status = 'active';
657
- }
658
- if (prevRole && prevRole !== detectedRole && rpSession.roleStates[prevRole]) {
659
- rpSession.roleStates[prevRole].status = 'idle';
660
- }
661
- console.log(`[RolePlay] Role switched: ${prevRole || 'none'} -> ${detectedRole}`);
662
-
663
- // ★ Send roleplay_status so frontend fallbackRole stays in sync
664
- ctx.sendToServer({
665
- type: 'roleplay_status',
666
- conversationId,
667
- currentRole: rpSession.currentRole,
668
- round: rpSession.round,
669
- features: rpSession.features ? Array.from(rpSession.features.values()) : [],
670
- roleStates: rpSession.roleStates || {},
671
- waitingHuman: rpSession.waitingHuman || false
672
- });
673
- }
674
- }
675
- }
676
- }
677
-
678
472
  // 检测后台任务
679
473
  detectAndTrackBackgroundTask(conversationId, state, message);
680
474
 
681
- // ★ RolePlay: attach role metadata to messages sent to frontend
682
- if (state.rolePlayConfig) {
683
- const rpSession = rolePlaySessions.get(conversationId);
684
- if (rpSession && rpSession._routeInitialized) {
685
- // Attach current role info to the message as metadata
686
- const enrichedMessage = {
687
- ...message,
688
- _roleplay: {
689
- role: rpSession.currentRole,
690
- features: rpSession.features ? Array.from(rpSession.features.values()) : [],
691
- round: rpSession.round
692
- }
693
- };
694
- sendOutput(conversationId, enrichedMessage);
695
- continue;
696
- }
697
- }
698
-
699
475
  sendOutput(conversationId, message);
700
476
  }
701
477
  } catch (error) {
@@ -12,13 +12,13 @@ import {
12
12
  createConversation, resumeConversation, deleteConversation,
13
13
  handleRefreshConversation, handleCancelExecution,
14
14
  handleUserInput, handleUpdateConversationSettings, handleAskUserAnswer,
15
- sendConversationList, handleCheckCrewContext, handleCheckRolePlaySessions
15
+ sendConversationList
16
16
  } from '../conversation.js';
17
17
  import {
18
18
  createCrewSession, handleCrewHumanInput, handleCrewControl,
19
19
  addRoleToSession, removeRoleFromSession,
20
20
  handleListCrewSessions, handleCheckCrewExists, handleDeleteCrewDir, resumeCrewSession, removeFromCrewIndex, hideCrewSession,
21
- handleLoadCrewHistory
21
+ handleLoadCrewHistory, handleCheckCrewContext
22
22
  } from '../crew.js';
23
23
  import { sendToServer, flushMessageBuffer } from './buffer.js';
24
24
  import { handleRestartAgent, handleUpgradeAgent } from './upgrade.js';
@@ -70,10 +70,6 @@ export async function handleMessage(msg) {
70
70
  handleCheckCrewContext(msg);
71
71
  break;
72
72
 
73
- case 'check_roleplay_sessions':
74
- handleCheckRolePlaySessions(msg);
75
- break;
76
-
77
73
  case 'resume_conversation':
78
74
  await resumeConversation(msg);
79
75
  break;