(agentId: string, getToken?: TokenProvider, skipWhitelistCheck = false)
| 76 | } |
| 77 | |
| 78 | export async function startAgentLoop(agentId: string, getToken?: TokenProvider, skipWhitelistCheck = false): Promise<void> { |
| 79 | if (activeLoops[agentId]?.isRunning) { |
| 80 | Logger.warn(agentId, `Agent is already running`); |
| 81 | return; |
| 82 | } |
| 83 | |
| 84 | const isFirstAgent = getRunningAgentIds().length === 0; |
| 85 | |
| 86 | |
| 87 | try { |
| 88 | const agent = await getAgent(agentId); |
| 89 | if (!agent) throw new Error(`Agent ${agentId} not found`); |
| 90 | |
| 91 | // Check phone whitelist before starting (unless explicitly skipped) |
| 92 | if (!skipWhitelistCheck) { |
| 93 | const agentCode = await getAgentCode(agentId) || ''; |
| 94 | const { phoneNumbers, hasTools, channel } = await checkPhoneWhitelist(agentCode, getToken); |
| 95 | |
| 96 | // If phone tools are used and there are issues, throw error for UI to handle |
| 97 | if (hasTools && (phoneNumbers.length === 0 || phoneNumbers.some(p => !p.isWhitelisted))) { |
| 98 | const error: any = new Error('Phone whitelist check failed'); |
| 99 | error.whitelistCheck = { phoneNumbers, hasTools, channel }; |
| 100 | throw error; |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | const requiredStreams = getRequiredStreamsForPrompt(agent.system_prompt); |
| 105 | |
| 106 | if (requiredStreams.length > 0) { |
| 107 | // A single, transactional call to the StreamManager. |
| 108 | await StreamManager.requestStreamsForAgent(agentId, requiredStreams); |
| 109 | } |
| 110 | |
| 111 | if (isFirstAgent) { |
| 112 | recordingManager.initialize(); |
| 113 | } |
| 114 | |
| 115 | // Generate sessionId and start new session |
| 116 | const sessionId = `session_${new Date().toISOString()}_${Math.random().toString(36).substring(2, 9)}`; |
| 117 | IterationStore.startSession(agentId, sessionId); |
| 118 | |
| 119 | // Clear any previous change detection data for fresh start |
| 120 | clearAgentChangeData(agentId); |
| 121 | |
| 122 | const intervalMs = agent.loop_interval_seconds * 1000; |
| 123 | activeLoops[agentId] = { |
| 124 | intervalId: null, |
| 125 | isRunning: true, |
| 126 | isExecuting: false, |
| 127 | intervalMs, |
| 128 | getToken |
| 129 | }; |
| 130 | |
| 131 | // Logger dispatches the window event automatically |
| 132 | Logger.info(agentId, 'Agent started', { |
| 133 | logType: 'agent-status-changed', |
| 134 | content: { agentId, status: 'running' } |
| 135 | }); |
no test coverage detected