diff --git a/api/endpoints/chat.php b/api/endpoints/chat.php index 3d1b5b2..3f561bd 100644 --- a/api/endpoints/chat.php +++ b/api/endpoints/chat.php @@ -1063,6 +1063,67 @@ if (!$reply && preg_match('/\b(news|headlines|latest|what.?s happening|current e } } +// ── Tier 0.9: Intel Protocol — research & tool_loop detection ──────────── +$arcJobId = null; + +// Detect "research X", "look up X", "deep dive X", "investigate X", "find out about X" +$intelPatterns = [ + '/^(?:jarvis[,\s]+)?(?:research|investigate|deep[- ]dive|deep dive)\s+(.+)/i' => 'research', + '/^(?:jarvis[,\s]+)?(?:look\s+(?:up|into)|find\s+out\s+(?:about)?)\s+(.+)/i' => 'research', + '/^(?:jarvis[,\s]+)?(?:step[- ]by[- ]step|figure\s+out|analyze\s+and\s+report|work\s+through)\s+(.+)/i' => 'tool_loop', + '/^(?:jarvis[,\s]+)?(?:run\s+a\s+research\s+(?:job|task)\s+on)\s+(.+)/i' => 'research', +]; + +if (!$reply) { + foreach ($intelPatterns as $pattern => $jobType) { + if (preg_match($pattern, $message, $m)) { + $queryOrTask = trim($m[1]); + if (strlen($queryOrTask) < 3) break; + + $depth = 'standard'; + if (preg_match('/\b(?:quick|brief)\b/i', $message)) $depth = 'quick'; + if (preg_match('/\b(?:deep|thorough|comprehensive|full)\b/i', $message)) $depth = 'deep'; + + $jobPayload = $jobType === 'research' + ? ['query' => $queryOrTask, 'depth' => $depth, 'provider' => 'claude'] + : ['task' => $queryOrTask, 'max_iterations' => 12, 'provider' => 'claude']; + + // Submit to Arc Reactor + $arcCh = curl_init('http://127.0.0.1:7474/job'); + curl_setopt_array($arcCh, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => json_encode([ + 'type' => $jobType, + 'payload' => $jobPayload, + 'priority' => 7, + 'created_by' => 'chat:' . $sessionId, + ]), + CURLOPT_HTTPHEADER => ['Content-Type: application/json'], + CURLOPT_TIMEOUT => 5, + CURLOPT_CONNECTTIMEOUT => 3, + ]); + $arcRes = json_decode(curl_exec($arcCh), true); + curl_close($arcCh); + + if (isset($arcRes['job_id'])) { + $arcJobId = $arcRes['job_id']; + if ($jobType === 'research') { + $depthLabel = strtoupper($depth); + $reply = "◈ INTEL PROTOCOL ACTIVATED — Running {$depthLabel} research on **{$queryOrTask}** (Job #{$arcJobId}). I'm searching sources, extracting content, and synthesizing a briefing now, {$userAddr}. Switch to the INTEL tab to watch live progress."; + } else { + $reply = "◈ IRON PROTOCOL ACTIVATED — Multi-step analysis initiated for **{$queryOrTask}** (Job #{$arcJobId}). I'll work through this systematically using available tools, {$userAddr}. Results will appear in the INTEL tab."; + } + $source = "arc:{$jobType}"; + } else { + $reply = "Intel Protocol is offline, {$userAddr}. Arc Reactor may be unavailable — I'll try to answer directly."; + $source = 'arc:offline'; + } + break; + } + } +} + // ── Tier 1: Intent Engine (instant, no LLM) ─────────────────────────────── if (!$reply) { $matched = KBEngine::match($message); @@ -1193,6 +1254,7 @@ if (!$reply) { } } + // ── Tier 2: Ollama local LLM (fast local fallback) ─────────────────────── if (!$reply && defined('OLLAMA_HOST') && OLLAMA_HOST) { $ollamaHost = OLLAMA_HOST; @@ -1413,4 +1475,5 @@ echo json_encode([ 'source' => $source, 'session_id' => $sessionId, 'timestamp' => date('c'), + 'arc_job' => $arcJobId, ]); diff --git a/public_html/index.html b/public_html/index.html index 9a11825..e3ba141 100644 --- a/public_html/index.html +++ b/public_html/index.html @@ -875,6 +875,27 @@ body::after{ transition:filter 1.2s ease; } #app.sleeping #sleepOverlay{display:flex} +/* ── INTEL PROTOCOL — research result cards ──────────────────────── */ +.intel-card{background:rgba(0,212,255,0.04);border:1px solid var(--panel-border);border-radius:var(--r);margin-bottom:8px;overflow:hidden} +.intel-card-head{display:flex;align-items:center;gap:8px;padding:8px 10px;cursor:pointer;user-select:none} +.intel-card-head:hover{background:rgba(0,212,255,0.06)} +.intel-card-query{font-family:var(--font-display);font-size:0.6rem;letter-spacing:1px;color:var(--cyan);flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} +.intel-card-status{font-family:var(--font-mono);font-size:0.55rem;padding:2px 6px;border-radius:2px;flex-shrink:0} +.intel-card-status.running{color:#ffd700;border:1px solid rgba(255,215,0,0.4);animation:pulse 1.5s ease-in-out infinite} +.intel-card-status.done{color:var(--green);border:1px solid rgba(0,255,136,0.3)} +.intel-card-status.failed{color:var(--red);border:1px solid rgba(255,34,68,0.3)} +.intel-card-body{display:none;padding:0 10px 10px;border-top:1px solid var(--panel-border)} +.intel-card.open .intel-card-body{display:block} +.intel-card-body .synthesis{font-size:0.65rem;line-height:1.6;color:var(--text);margin:8px 0;white-space:pre-wrap} +.intel-sources{margin-top:8px} +.intel-source{font-size:0.58rem;color:var(--text-dim);padding:2px 0;border-bottom:1px solid rgba(0,212,255,0.06)} +.intel-source a{color:var(--cyan2);text-decoration:none} +.intel-source a:hover{text-decoration:underline} +.intel-empty{text-align:center;padding:24px 10px;font-family:var(--font-mono);font-size:0.6rem;color:var(--text-dim);letter-spacing:1px} +.intel-new-btn{width:100%;background:rgba(0,212,255,0.06);border:1px solid var(--panel-border);border-radius:4px;padding:5px;color:var(--cyan);font-family:var(--font-display);font-size:0.55rem;letter-spacing:2px;cursor:pointer;margin-bottom:8px} +.intel-new-btn:hover{background:rgba(0,212,255,0.12)} +@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.4}} +
@@ -1077,6 +1098,7 @@ body::after{