File size: 4,651 Bytes
7694ee5
90bab83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7694ee5
 
90bab83
 
 
 
7694ee5
90bab83
 
 
 
 
 
 
 
 
 
7694ee5
90bab83
 
 
 
 
 
 
 
 
 
 
7694ee5
90bab83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7694ee5
 
90bab83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
"""Patch useAgentChat.ts - auto-continue feature."""

USE_AGENT_CHAT = "/source/frontend/src/hooks/useAgentChat.ts"

def patch():
    import os
    if not os.path.exists(USE_AGENT_CHAT):
        print(f"SKIP: {USE_AGENT_CHAT} not found")
        return
        
    with open(USE_AGENT_CHAT, "r", encoding="utf-8") as f:
        content = f.read()
    
    # 1. Add useState to imports
    if "useState" not in content:
        content = content.replace(
            "import { useCallback, useEffect, useMemo, useRef } from 'react';",
            "import { useCallback, useEffect, useMemo, useRef, useState } from 'react';"
        )
    
    # 2. Add state declarations after callbacksRef.current = { ... }
    state_decl = '''
  // Auto-continue state for free models when task incomplete
  const autoContinueTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const [showAutoContinue, setShowAutoContinue] = useState(false);
  const [taskIncompleteInfo, setTaskIncompleteInfo] = useState<{
    incompletePlan: Array<{ id: string; content: string; status: string }>;
  } | null>(null);
'''
    
    if "autoContinueTimerRef" not in content:
        content = content.replace(
            "callbacksRef.current = { onReady, onError, onSessionDead };",
            "callbacksRef.current = { onReady, onError, onSessionDead };" + state_decl
        )
    
    # 3. Add onTaskIncomplete to SideChannelCallbacks interface
    if "onTaskIncomplete:" not in content and "onInterrupted:" in content:
        content = content.replace(
            "  onInterrupted: () => void;",
            "  onInterrupted: () => void;\n  onTaskIncomplete: (incompletePlan: Array<{ id: string; content: string; status: string }>) => void;"
        )
    
    # 4. Add onTaskIncomplete handler in sideChannel
    sidechannel_handler = '''      onTaskIncomplete: (incompletePlan) => {
        setTaskIncompleteInfo({ incompletePlan });
        setShowAutoContinue(true);
        // Auto-start 10s timer for free models
        if (autoContinueTimerRef.current) {
          clearTimeout(autoContinueTimerRef.current);
        }
        autoContinueTimerRef.current = setTimeout(() => {
          startAutoContinue();
        }, 10000);
      },'''
    
    if "onTaskIncomplete: (incompletePlan)" not in content:
        content = content.replace(
            "      onInterrupted: () => { /* no-op — handled by stop() caller */ },",
            sidechannel_handler + "\n      onInterrupted: () => { /* no-op — handled by stop() caller */ },"
        )
    
    # 5. Add auto-continue functions before return
    auto_funcs = '''
  // -- Auto-continue for free models when task incomplete -----------------
  const startAutoContinue = useCallback(() => {
    setShowAutoContinue(false);
    setTaskIncompleteInfo(null);
    if (autoContinueTimerRef.current) {
      clearTimeout(autoContinueTimerRef.current);
      autoContinueTimerRef.current = null;
    }
    // Build continuation message from incomplete plan
    const incompleteItems = taskIncompleteInfo?.incompletePlan || [];
    const continuationText = incompleteItems.length > 0
      ? `Tiếp tục từ các task chưa hoàn thành:\\n${incompleteItems.map(i => `- ${i.content}`).join('\\n')}`
      : 'Tiếp tục nhiệm vụ.';
    chat.sendMessage({
      text: `[TỰ ĐỘNG TIẾP TỤC] ${continuationText}`,
      metadata: { createdAt: new Date().toISOString() },
    });
  }, [taskIncompleteInfo, chat]);

  const cancelAutoContinue = useCallback(() => {
    setShowAutoContinue(false);
    setTaskIncompleteInfo(null);
    if (autoContinueTimerRef.current) {
      clearTimeout(autoContinueTimerRef.current);
      autoContinueTimerRef.current = null;
    }
  }, []);

  // Cleanup timer on unmount
  useEffect(() => {
    return () => {
      if (autoContinueTimerRef.current) {
        clearTimeout(autoContinueTimerRef.current);
      }
    };
  }, []);
'''
    
    if "startAutoContinue" not in content:
        content = content.replace(
            '\n  return {\n    messages: chat.messages,',
            auto_funcs + '\n  return {\n    messages: chat.messages,'
        )
    
    # 6. Update return to include new values
    if "showAutoContinue," not in content:
        content = content.replace(
            "refreshMessages,\n  };",
            "refreshMessages,\n    showAutoContinue,\n    taskIncompleteInfo,\n    startAutoContinue,\n    cancelAutoContinue,\n  };"
        )
    
    with open(USE_AGENT_CHAT, "w", encoding="utf-8") as f:
        f.write(content)
    print(f"OK: Complete patch applied to {USE_AGENT_CHAT}")

if __name__ == "__main__":
    patch()