diff --git a/api/endpoints/arc.php b/api/endpoints/arc.php index 831a45e..ac77f42 100644 --- a/api/endpoints/arc.php +++ b/api/endpoints/arc.php @@ -213,6 +213,29 @@ switch ($action) { echo json_encode(arc_request('GET', '/guardian/chat' . $qs)); break; + // ── COMMS v2 ────────────────────────────────────────────────────────────── + // GET /api/arc?action=comms_sent&limit=50&status=sent + case 'comms_sent': + $limit = min((int)($_GET['limit'] ?? 50), 200); + $status = $_GET['status'] ?? ''; + $qs = http_build_query(array_filter(['limit' => $limit, 'status' => $status])); + echo json_encode(arc_request('GET', '/comms/sent' . ($qs ? "?{$qs}" : ''))); + break; + + // GET /api/arc?action=comms_sent_get&id=123 + case 'comms_sent_get': + $id = (int)($_GET['id'] ?? 0); + if (!$id) { http_response_code(400); echo json_encode(['error' => 'Missing id']); break; } + echo json_encode(arc_request('GET', "/comms/sent/{$id}")); + break; + + // DELETE /api/arc?action=comms_sent_delete&id=123 + case 'comms_sent_delete': + $id = (int)($_GET['id'] ?? 0); + if (!$id) { http_response_code(400); echo json_encode(['error' => 'Missing id']); break; } + echo json_encode(arc_request('DELETE', "/comms/sent/{$id}")); + break; + default: http_response_code(404); echo json_encode(['error' => "Unknown arc action: {$action}"]); diff --git a/api/endpoints/chat.php b/api/endpoints/chat.php index 17b6b07..28bdae9 100644 --- a/api/endpoints/chat.php +++ b/api/endpoints/chat.php @@ -1261,6 +1261,100 @@ if (!$reply) { } } +// ── Tier 0.9f: Comms v2 — send_email, compose_email ───────────────────────── +if (!$reply) { + // "reply to [name/id] saying..." or "send [name] a reply..." + if (preg_match('/^(?:jarvis[,\s]+)?(?:reply\s+to|send\s+(?:a\s+)?reply\s+to)\s+(.+?)\s+(?:saying|that|:)\s+(.+)/i', $message, $m)) { + $target = trim($m[1]); + $content = trim($m[2]); + $arcRes = arcSubmitJob('send_email', [ + 'target' => $target, + 'content' => $content, + ], $sessionId); + if (isset($arcRes['job_id'])) { + $arcJobId = $arcRes['job_id']; + $reply = "◈ COMMS PROTOCOL — Sending reply to **{$target}** (Job #{$arcJobId}). Drafting and transmitting now, {$userAddr}."; + $source = 'arc:send_email'; + } else { + $reply = "Comms Protocol is offline, {$userAddr}. Arc Reactor may be unavailable."; + $source = 'arc:offline'; + } + } + // "send email to [name] about [subject]" or "compose email to..." + elseif (preg_match('/^(?:jarvis[,\s]+)?(?:send\s+(?:an?\s+)?email\s+to|compose\s+(?:an?\s+)?email\s+to|write\s+(?:an?\s+)?email\s+to|draft\s+(?:an?\s+)?email\s+to)\s+(.+?)(?:\s+(?:about|regarding|re:?)\s+(.+))?$/i', $message, $m)) { + $recipient = trim($m[1]); + $instructions = isset($m[2]) ? trim($m[2]) : $message; + $arcRes = arcSubmitJob('compose_email', [ + 'recipient' => $recipient, + 'instructions' => $instructions, + 'auto_send' => false, + ], $sessionId); + if (isset($arcRes['job_id'])) { + $arcJobId = $arcRes['job_id']; + $reply = "◈ COMMS PROTOCOL — Composing email to **{$recipient}** (Job #{$arcJobId}). I'll draft it and show you before sending, {$userAddr}. Check the COMMS tab."; + $source = 'arc:compose_email'; + } else { + $reply = "Comms Protocol is offline, {$userAddr}."; + $source = 'arc:offline'; + } + } +} + +// ── Tier 0.9g: Comms v2 — schedule_event ───────────────────────────────────── +if (!$reply) { + $schedulePatterns = [ + '/^(?:jarvis[,\s]+)?(?:schedule|book|set\s+up|create)\s+(?:a\s+)?(?:meeting|call|appointment|event|session)\s+(?:with|for|about)?\s*(.+)/i', + '/^(?:jarvis[,\s]+)?(?:add\s+(?:a\s+)?(?:meeting|call|appointment|event)\s+(?:to\s+my\s+calendar)?\s*(?:with|for|about)?\s*(.+))/i', + '/^(?:jarvis[,\s]+)?(?:put\s+(?:a\s+)?(?:meeting|call|appointment)\s+(?:on\s+(?:my\s+)?calendar|in\s+my\s+schedule)(?:\s+(?:with|for|about)?\s+(.+))?)/i', + ]; + foreach ($schedulePatterns as $pat) { + if (preg_match($pat, $message, $m)) { + $details = trim($m[1] ?? $message); + $arcRes = arcSubmitJob('schedule_event', [ + 'request' => $message, + 'details' => $details, + 'provider' => 'claude', + ], $sessionId); + if (isset($arcRes['job_id'])) { + $arcJobId = $arcRes['job_id']; + $reply = "◈ SCHEDULING PROTOCOL — Processing your calendar request (Job #{$arcJobId}). I'm parsing the details and creating the appointment now, {$userAddr}."; + $source = 'arc:schedule_event'; + } else { + $reply = "Scheduling Protocol is offline, {$userAddr}. Arc Reactor may be unavailable."; + $source = 'arc:offline'; + } + break; + } + } +} + +// ── Tier 0.9h: Comms v2 — meeting_prep ──────────────────────────────────────── +if (!$reply) { + $meetingPrepPatterns = [ + '/^(?:jarvis[,\s]+)?(?:prep(?:are)?(?:\s+me)?\s+for|brief(?:ing)?\s+(?:me\s+)?(?:for|on|about)?)\s+(?:my\s+)?(?:next\s+)?(?:meeting|call|appointment)/i', + '/^(?:jarvis[,\s]+)?(?:what(?:\'s|\s+do\s+i\s+need\s+to\s+know)?\s+(?:is\s+)?(?:my\s+)?(?:next\s+)?meeting)/i', + '/^(?:jarvis[,\s]+)?(?:meeting\s+prep|pre[- ]meeting\s+(?:brief|notes|prep))/i', + '/^(?:jarvis[,\s]+)?(?:get\s+(?:me\s+)?ready\s+for\s+my\s+(?:next\s+)?(?:meeting|call))/i', + ]; + foreach ($meetingPrepPatterns as $pat) { + if (preg_match($pat, $message)) { + $arcRes = arcSubmitJob('meeting_prep', [ + 'provider' => 'claude', + 'research' => true, + ], $sessionId); + if (isset($arcRes['job_id'])) { + $arcJobId = $arcRes['job_id']; + $reply = "◈ MISSION PROTOCOL — Preparing your meeting briefing (Job #{$arcJobId}). I'm pulling your next appointment details and researching the participants, {$userAddr}. Stand by."; + $source = 'arc:meeting_prep'; + } else { + $reply = "Mission Protocol is offline, {$userAddr}. Arc Reactor may be unavailable."; + $source = 'arc:offline'; + } + break; + } + } +} + // ── Tier 1: Intent Engine (instant, no LLM) ─────────────────────────────── if (!$reply) { $matched = KBEngine::match($message); diff --git a/public_html/admin/index.php b/public_html/admin/index.php index 2181dbd..819cdf0 100644 --- a/public_html/admin/index.php +++ b/public_html/admin/index.php @@ -550,6 +550,49 @@ if ($action) { $raw = curl_exec($ch); curl_close($ch); j(json_decode($raw, true) ?: ['error' => 'Arc Reactor unreachable']); + // ── OUTBOX ──────────────────────────────────────────────────────────── + case 'outbox_list': + $limit = min((int)($_GET['limit'] ?? 50), 200); + $status = $_GET['status'] ?? ''; + $qs = http_build_query(array_filter(['limit' => $limit, 'status' => $status])); + $ch = curl_init('http://127.0.0.1:7474/comms/sent' . ($qs ? "?{$qs}" : '')); + curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>5]); + $raw = curl_exec($ch); curl_close($ch); + j(json_decode($raw, true) ?: []); + + case 'outbox_delete': + $id = (int)($_GET['id'] ?? 0); if (!$id) bad('Missing id'); + $ch = curl_init('http://127.0.0.1:7474/comms/sent/' . $id); + curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER=>true, CURLOPT_TIMEOUT=>5, CURLOPT_CUSTOMREQUEST=>'DELETE']); + $raw = curl_exec($ch); curl_close($ch); + j(json_decode($raw, true) ?: ['error'=>'failed']); + + case 'send_reply': + $id = (int)($_GET['id'] ?? 0); if (!$id) bad('Missing triage id'); + $content = $_GET['content'] ?? ''; + $ch = curl_init('http://127.0.0.1:7474/job'); + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5, CURLOPT_POST => true, + CURLOPT_POSTFIELDS => json_encode(['type'=>'send_email','payload'=>['triage_id'=>$id,'content'=>$content],'priority'=>8,'created_by'=>'admin']), + CURLOPT_HTTPHEADER => ['Content-Type: application/json'], + ]); + $raw = curl_exec($ch); curl_close($ch); + j(json_decode($raw, true) ?: ['error' => 'Arc Reactor unreachable']); + + case 'compose_email': + $to = $_GET['to'] ?? ''; if (!$to) bad('Missing recipient'); + $subject = $_GET['subject'] ?? ''; + $body = $_GET['body'] ?? ''; + $account = $_GET['account'] ?? 'gmail'; + $ch = curl_init('http://127.0.0.1:7474/job'); + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, CURLOPT_TIMEOUT => 5, CURLOPT_POST => true, + CURLOPT_POSTFIELDS => json_encode(['type'=>'compose_email','payload'=>['recipient'=>$to,'subject'=>$subject,'instructions'=>$body,'account'=>$account,'auto_send'=>false],'priority'=>7,'created_by'=>'admin']), + CURLOPT_HTTPHEADER => ['Content-Type: application/json'], + ]); + $raw = curl_exec($ch); curl_close($ch); + j(json_decode($raw, true) ?: ['error' => 'Arc Reactor unreachable']); + // ── VISION PROTOCOL ────────────────────────────────────────────────── case 'vision_list': $limit = min((int)($_GET['limit'] ?? 30), 100); @@ -876,6 +919,7 @@ select.filter-sel:focus{border-color:var(--cyan)}
+ @@ -1264,6 +1308,23 @@ select.filter-sel:focus{border-color:var(--cyan)}| STATUS | TO | SUBJECT | ACCOUNT | SENT AT | ACTIONS |
|---|
${esc(m.body||'(no body)')}
+ `, null, null);
+}
+
+function outboxCompose() {
+ openModal('COMPOSE MESSAGE', `
+