docs: keep only workflow testing and process guidance

This commit is contained in:
Jiayuan Zhang 2026-02-17 01:15:22 +08:00
parent 88582fe050
commit 2447230ca7
6 changed files with 49 additions and 1731 deletions

View file

@ -1,6 +1,8 @@
# Documentation Index (Priority-Based)
This repo keeps documentation intentionally small to reduce stale AI context.
Only workflow/testing/process documentation should be maintained.
Project-intro and architecture explanation docs are intentionally omitted.
## P0 (Keep Fresh)

View file

@ -1,433 +0,0 @@
<mxfile host="app.diagrams.net" modified="2026-02-15T00:00:00.000Z" agent="Claude" version="24.0.0" type="device">
<diagram id="multica-architecture" name="Architecture Overview">
<mxGraphModel dx="2400" dy="1600" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="0" pageScale="1" pageWidth="3300" pageHeight="2400" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<!-- ==================== TITLE ==================== -->
<mxCell id="title" value="Super Multica — Architecture &amp; Module Map" style="text;html=1;fontSize=24;fontStyle=1;align=center;verticalAlign=middle;whiteSpace=wrap;fontFamily=Helvetica;fontColor=#1a1a2e;" vertex="1" parent="1">
<mxGeometry x="400" y="20" width="800" height="40" as="geometry" />
</mxCell>
<!-- ==================== APPS LAYER ==================== -->
<mxCell id="apps-group" value="Apps Layer" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f0f4ff;strokeColor=#4361ee;strokeWidth=2;fontSize=14;fontStyle=1;verticalAlign=top;align=left;spacingLeft=10;spacingTop=5;arcSize=8;fontColor=#4361ee;dashed=0;" vertex="1" parent="1">
<mxGeometry x="40" y="80" width="1530" height="260" as="geometry" />
</mxCell>
<!-- Desktop App -->
<mxCell id="desktop" value="Desktop App" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#4361ee;strokeColor=#3a56d4;fontSize=13;fontStyle=1;fontColor=#ffffff;arcSize=12;" vertex="1" parent="1">
<mxGeometry x="70" y="120" width="340" height="40" as="geometry" />
</mxCell>
<mxCell id="desktop-main" value="Main Process (Electron)&#xa;├ IPC Handlers (14+ modules)&#xa;├ System Tray&#xa;└ Auto Updater" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e8edff;strokeColor=#4361ee;fontSize=11;align=left;spacingLeft=10;arcSize=8;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="70" y="170" width="165" height="80" as="geometry" />
</mxCell>
<mxCell id="desktop-renderer" value="Renderer (React 19)&#xa;├ Zustand Stores (9)&#xa;├ Chat Interface&#xa;└ Settings / Dashboard" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e8edff;strokeColor=#4361ee;fontSize=11;align=left;spacingLeft=10;arcSize=8;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="245" y="170" width="165" height="80" as="geometry" />
</mxCell>
<mxCell id="desktop-ipc-arrow" value="" style="endArrow=classic;startArrow=classic;html=1;strokeColor=#4361ee;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="desktop-main" target="desktop-renderer">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="desktop-ipc-label" value="IPC" style="edgeLabel;html=1;align=center;verticalAlign=middle;fontSize=9;fontColor=#4361ee;fontStyle=1;" vertex="1" connectable="0" parent="desktop-ipc-arrow">
<mxGeometry x="-0.1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="desktop-tag" value="@multica/desktop · Electron + Vite" style="text;html=1;fontSize=9;fontColor=#888;align=center;" vertex="1" parent="1">
<mxGeometry x="70" y="255" width="340" height="16" as="geometry" />
</mxCell>
<!-- Gateway -->
<mxCell id="gateway" value="Gateway" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#7209b7;strokeColor=#5a078f;fontSize=13;fontStyle=1;fontColor=#ffffff;arcSize=12;" vertex="1" parent="1">
<mxGeometry x="440" y="120" width="240" height="40" as="geometry" />
</mxCell>
<mxCell id="gateway-detail" value="NestJS WebSocket Server&#xa;├ Socket.io (port 3000)&#xa;├ Device Registration&#xa;├ RPC Message Routing&#xa;├ Heartbeat (25s ping)&#xa;└ Telegram Channel" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f3e8ff;strokeColor=#7209b7;fontSize=11;align=left;spacingLeft=10;arcSize=8;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="440" y="170" width="240" height="100" as="geometry" />
</mxCell>
<mxCell id="gateway-tag" value="@multica/gateway · NestJS + Socket.io" style="text;html=1;fontSize=9;fontColor=#888;align=center;" vertex="1" parent="1">
<mxGeometry x="440" y="275" width="240" height="16" as="geometry" />
</mxCell>
<!-- Web App -->
<mxCell id="web" value="Web App" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f72585;strokeColor=#d01f6e;fontSize=13;fontStyle=1;fontColor=#ffffff;arcSize=12;" vertex="1" parent="1">
<mxGeometry x="710" y="120" width="200" height="40" as="geometry" />
</mxCell>
<mxCell id="web-detail" value="Next.js 16 + React 19&#xa;├ App Router&#xa;├ Gateway Client (SDK)&#xa;└ Zustand Store" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffe0ef;strokeColor=#f72585;fontSize=11;align=left;spacingLeft=10;arcSize=8;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="710" y="170" width="200" height="75" as="geometry" />
</mxCell>
<mxCell id="web-tag" value="@multica/web · Next.js 16" style="text;html=1;fontSize=9;fontColor=#888;align=center;" vertex="1" parent="1">
<mxGeometry x="710" y="250" width="200" height="16" as="geometry" />
</mxCell>
<!-- CLI -->
<mxCell id="cli" value="CLI" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ff6d00;strokeColor=#e06200;fontSize=13;fontStyle=1;fontColor=#ffffff;arcSize=12;" vertex="1" parent="1">
<mxGeometry x="940" y="120" width="200" height="40" as="geometry" />
</mxCell>
<mxCell id="cli-detail" value="Interactive &amp; Non-interactive&#xa;├ run / chat / session&#xa;├ profile / skills / tools&#xa;├ credentials / cron / dev&#xa;└ Autocomplete + Help" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff3e0;strokeColor=#ff6d00;fontSize=11;align=left;spacingLeft=10;arcSize=8;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="940" y="170" width="200" height="85" as="geometry" />
</mxCell>
<mxCell id="cli-tag" value="@multica/cli · Node.js" style="text;html=1;fontSize=9;fontColor=#888;align=center;" vertex="1" parent="1">
<mxGeometry x="940" y="260" width="200" height="16" as="geometry" />
</mxCell>
<!-- Mobile -->
<mxCell id="mobile" value="Mobile" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#06d6a0;strokeColor=#05b88a;fontSize=13;fontStyle=1;fontColor=#ffffff;arcSize=12;" vertex="1" parent="1">
<mxGeometry x="1170" y="120" width="170" height="40" as="geometry" />
</mxCell>
<mxCell id="mobile-detail" value="React Native&#xa;└ (In Development)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e0fff5;strokeColor=#06d6a0;fontSize=11;align=left;spacingLeft=10;arcSize=8;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="1170" y="170" width="170" height="45" as="geometry" />
</mxCell>
<mxCell id="mobile-tag" value="@multica/mobile · React Native" style="text;html=1;fontSize=9;fontColor=#888;align=center;" vertex="1" parent="1">
<mxGeometry x="1170" y="220" width="170" height="16" as="geometry" />
</mxCell>
<!-- Server -->
<mxCell id="server" value="Server" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#adb5bd;strokeColor=#868e96;fontSize=13;fontStyle=1;fontColor=#ffffff;arcSize=12;" vertex="1" parent="1">
<mxGeometry x="1370" y="120" width="170" height="40" as="geometry" />
</mxCell>
<mxCell id="server-detail" value="NestJS REST API&#xa;└ Port 4000 (Legacy)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f1f3f5;strokeColor=#adb5bd;fontSize=11;align=left;spacingLeft=10;arcSize=8;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="1370" y="170" width="170" height="45" as="geometry" />
</mxCell>
<!-- ==================== CONNECTION ARROWS (Apps → Core) ==================== -->
<!-- Desktop → Hub (embedded) -->
<mxCell id="arrow-desktop-hub" value="" style="endArrow=classic;html=1;strokeColor=#4361ee;strokeWidth=2;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.15;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="desktop-main" target="core-group">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="arrow-desktop-hub-label" value="Embedded Hub" style="edgeLabel;html=1;align=center;verticalAlign=middle;fontSize=10;fontColor=#4361ee;fontStyle=1;labelBackgroundColor=#ffffff;" vertex="1" connectable="0" parent="arrow-desktop-hub">
<mxGeometry x="0.2" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<!-- Web → Gateway (Socket.io) -->
<mxCell id="arrow-web-gateway" value="" style="endArrow=classic;startArrow=classic;html=1;strokeColor=#f72585;strokeWidth=2;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=1;entryY=0.3;entryDx=0;entryDy=0;" edge="1" parent="1" source="web-detail" target="gateway-detail">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="arrow-web-gateway-label" value="Socket.io" style="edgeLabel;html=1;align=center;verticalAlign=middle;fontSize=10;fontColor=#f72585;fontStyle=1;labelBackgroundColor=#ffffff;" vertex="1" connectable="0" parent="arrow-web-gateway">
<mxGeometry relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<!-- Gateway → Hub -->
<mxCell id="arrow-gateway-hub" value="" style="endArrow=classic;html=1;strokeColor=#7209b7;strokeWidth=2;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.35;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="gateway-detail" target="core-group">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="arrow-gateway-hub-label" value="RPC" style="edgeLabel;html=1;align=center;verticalAlign=middle;fontSize=10;fontColor=#7209b7;fontStyle=1;labelBackgroundColor=#ffffff;" vertex="1" connectable="0" parent="arrow-gateway-hub">
<mxGeometry relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<!-- CLI → Core -->
<mxCell id="arrow-cli-core" value="" style="endArrow=classic;html=1;strokeColor=#ff6d00;strokeWidth=2;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.85;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="cli-detail" target="core-group">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="arrow-cli-core-label" value="Direct Import" style="edgeLabel;html=1;align=center;verticalAlign=middle;fontSize=10;fontColor=#ff6d00;fontStyle=1;labelBackgroundColor=#ffffff;" vertex="1" connectable="0" parent="arrow-cli-core">
<mxGeometry relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<!-- ==================== CORE PACKAGE ==================== -->
<mxCell id="core-group" value="@multica/core — Core Engine" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff8f0;strokeColor=#e76f51;strokeWidth=2;fontSize=14;fontStyle=1;verticalAlign=top;align=left;spacingLeft=10;spacingTop=5;arcSize=6;fontColor=#e76f51;" vertex="1" parent="1">
<mxGeometry x="40" y="370" width="1530" height="590" as="geometry" />
</mxCell>
<!-- Hub -->
<mxCell id="hub" value="Hub" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e76f51;strokeColor=#d05a3e;fontSize=13;fontStyle=1;fontColor=#ffffff;arcSize=10;" vertex="1" parent="1">
<mxGeometry x="70" y="410" width="350" height="35" as="geometry" />
</mxCell>
<mxCell id="hub-detail" value="Multi-Agent Coordination&#xa;&#xa;├ Agent Manager — create / delete / list agents&#xa;├ RPC Dispatcher — 12+ handlers for remote ops&#xa;├ Gateway Client — Socket.io ↔ remote devices&#xa;├ Agent Store — persistent agent list (JSON)&#xa;├ Device Store — device metadata&#xa;├ Auth Store — Hub authentication tokens&#xa;├ Exec Approval Manager — tool approval flow&#xa;├ Message Aggregator — message coalescing&#xa;└ Block Chunker — large payload chunking" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fef0eb;strokeColor=#e76f51;fontSize=11;align=left;spacingLeft=10;arcSize=6;fontColor=#333;verticalAlign=top;spacingTop=6;" vertex="1" parent="1">
<mxGeometry x="70" y="455" width="350" height="180" as="geometry" />
</mxCell>
<!-- Agent Engine -->
<mxCell id="agent-engine" value="Agent Engine" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#264653;strokeColor=#1d3740;fontSize=13;fontStyle=1;fontColor=#ffffff;arcSize=10;" vertex="1" parent="1">
<mxGeometry x="450" y="410" width="530" height="35" as="geometry" />
</mxCell>
<mxCell id="agent-runner" value="Agent Runner&#xa;├ Agent (sync orchestrator)&#xa;├ AsyncAgent (event-driven)&#xa;├ SyncAgent (blocking)&#xa;├ pi-agent-core integration&#xa;└ Run Log (JSONL debug)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e8f4f0;strokeColor=#264653;fontSize=11;align=left;spacingLeft=10;arcSize=6;fontColor=#333;verticalAlign=top;spacingTop=6;" vertex="1" parent="1">
<mxGeometry x="450" y="455" width="165" height="105" as="geometry" />
</mxCell>
<mxCell id="agent-session" value="Session Manager&#xa;├ JSONL Persistence&#xa;├ Compaction (3 modes)&#xa;│ ├ Count-based&#xa;│ ├ Token-aware&#xa;│ └ Summary (LLM)&#xa;├ File Repair&#xa;├ Write Lock&#xa;└ UUIDv7 IDs" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e8f4f0;strokeColor=#264653;fontSize=11;align=left;spacingLeft=10;arcSize=6;fontColor=#333;verticalAlign=top;spacingTop=6;" vertex="1" parent="1">
<mxGeometry x="625" y="455" width="165" height="150" as="geometry" />
</mxCell>
<mxCell id="agent-ctx" value="Context Window Guard&#xa;├ Token Estimation&#xa;├ Budget Enforcement&#xa;├ LLM Summarization&#xa;├ Tool Result Pruning&#xa;└ Compaction Metadata" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e8f4f0;strokeColor=#264653;fontSize=11;align=left;spacingLeft=10;arcSize=6;fontColor=#333;verticalAlign=top;spacingTop=6;" vertex="1" parent="1">
<mxGeometry x="800" y="455" width="175" height="105" as="geometry" />
</mxCell>
<!-- System Prompt -->
<mxCell id="agent-prompt" value="System Prompt Builder&#xa;├ Dynamic Construction&#xa;├ Sections (tools, skills,&#xa;│ memory, channels)&#xa;├ Constitution (safety)&#xa;└ Runtime Info" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e8f4f0;strokeColor=#264653;fontSize=11;align=left;spacingLeft=10;arcSize=6;fontColor=#333;verticalAlign=top;spacingTop=6;" vertex="1" parent="1">
<mxGeometry x="450" y="570" width="165" height="105" as="geometry" />
</mxCell>
<!-- Providers -->
<mxCell id="providers" value="LLM Providers" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#264653;strokeColor=#1d3740;fontSize=13;fontStyle=1;fontColor=#ffffff;arcSize=10;" vertex="1" parent="1">
<mxGeometry x="1010" y="410" width="230" height="35" as="geometry" />
</mxCell>
<mxCell id="providers-detail" value="Multi-Provider Registry&#xa;&#xa;├ Anthropic (Claude)&#xa;├ OpenAI (GPT / o-series)&#xa;├ Google (Gemini)&#xa;├ DeepSeek&#xa;├ Kimi (Moonshot)&#xa;├ Groq&#xa;├ Mistral&#xa;├ Together&#xa;├ OpenRouter&#xa;└ xAI (Grok)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e8f4f0;strokeColor=#264653;fontSize=11;align=left;spacingLeft=10;arcSize=6;fontColor=#333;verticalAlign=top;spacingTop=6;" vertex="1" parent="1">
<mxGeometry x="1010" y="455" width="145" height="185" as="geometry" />
</mxCell>
<!-- Auth Profiles -->
<mxCell id="auth-profiles" value="Auth Profiles&#xa;├ Multi-credential store&#xa;├ Rotation on error&#xa;├ Cooldown mechanism&#xa;├ OAuth support&#xa;└ API key resolution" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e8f4f0;strokeColor=#264653;fontSize=11;align=left;spacingLeft=10;arcSize=6;fontColor=#333;verticalAlign=top;spacingTop=6;" vertex="1" parent="1">
<mxGeometry x="1165" y="455" width="155" height="105" as="geometry" />
</mxCell>
<!-- Tools -->
<mxCell id="tools" value="Tools" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#2a9d8f;strokeColor=#218778;fontSize=13;fontStyle=1;fontColor=#ffffff;arcSize=10;" vertex="1" parent="1">
<mxGeometry x="70" y="650" width="350" height="35" as="geometry" />
</mxCell>
<mxCell id="tools-detail" value="Tool Registry &amp; Execution&#xa;&#xa;├ Exec (shell commands)&#xa;│ ├ Safety Classification&#xa;│ ├ Approval Callbacks&#xa;│ └ Command Allowlist&#xa;├ Web Fetch &amp; Web Search&#xa;│ ├ SSRF Protection&#xa;│ └ Response Cache&#xa;├ File Glob&#xa;├ Memory Search&#xa;├ Sessions Spawn / List (subagents)&#xa;├ Cron Tool&#xa;├ Data / Finance APIs&#xa;├ Send File &amp; Image Resize&#xa;├ Process Management&#xa;└ Policy Engine&#xa; ├ Allow / Deny Lists&#xa; ├ Provider Overrides&#xa; └ Group Patterns (web:*, fs:*)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e8f8f5;strokeColor=#2a9d8f;fontSize=11;align=left;spacingLeft=10;arcSize=6;fontColor=#333;verticalAlign=top;spacingTop=6;" vertex="1" parent="1">
<mxGeometry x="70" y="695" width="350" height="245" as="geometry" />
</mxCell>
<!-- Skills System -->
<mxCell id="skills-sys" value="Skills System" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e9c46a;strokeColor=#d4a843;fontSize=13;fontStyle=1;fontColor=#333;arcSize=10;" vertex="1" parent="1">
<mxGeometry x="450" y="690" width="170" height="35" as="geometry" />
</mxCell>
<mxCell id="skills-detail" value="Modular Skill Loader&#xa;├ YAML Frontmatter&#xa;├ Eligibility Filtering&#xa;│ (OS, binaries, configs)&#xa;├ Hot-Reload Watcher&#xa;├ GitHub Install&#xa;└ OpenClaw Compatible" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fdf6e3;strokeColor=#e9c46a;fontSize=11;align=left;spacingLeft=10;arcSize=6;fontColor=#333;verticalAlign=top;spacingTop=6;" vertex="1" parent="1">
<mxGeometry x="450" y="735" width="170" height="120" as="geometry" />
</mxCell>
<!-- Profile System -->
<mxCell id="profile-sys" value="Profile System" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e9c46a;strokeColor=#d4a843;fontSize=13;fontStyle=1;fontColor=#333;arcSize=10;" vertex="1" parent="1">
<mxGeometry x="640" y="690" width="170" height="35" as="geometry" />
</mxCell>
<mxCell id="profile-detail" value="Agent Identity &amp; Config&#xa;├ Soul (personality)&#xa;├ User info&#xa;├ Workspace context&#xa;├ Memory (long-term)&#xa;├ Heartbeat prompt&#xa;└ Config (YAML)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fdf6e3;strokeColor=#e9c46a;fontSize=11;align=left;spacingLeft=10;arcSize=6;fontColor=#333;verticalAlign=top;spacingTop=6;" vertex="1" parent="1">
<mxGeometry x="640" y="735" width="170" height="120" as="geometry" />
</mxCell>
<!-- Subagent System -->
<mxCell id="subagent-sys" value="Subagent System" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e9c46a;strokeColor=#d4a843;fontSize=13;fontStyle=1;fontColor=#333;arcSize=10;" vertex="1" parent="1">
<mxGeometry x="840" y="690" width="180" height="35" as="geometry" />
</mxCell>
<mxCell id="subagent-detail" value="Child Agent Orchestration&#xa;├ Registry (lifecycle)&#xa;├ JSONL Persistence&#xa;├ Result Announcement&#xa;├ Coalesced Batching&#xa;├ Command Queue&#xa;└ Lane-based Concurrency" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fdf6e3;strokeColor=#e9c46a;fontSize=11;align=left;spacingLeft=10;arcSize=6;fontColor=#333;verticalAlign=top;spacingTop=6;" vertex="1" parent="1">
<mxGeometry x="840" y="735" width="180" height="120" as="geometry" />
</mxCell>
<!-- Cron System -->
<mxCell id="cron-sys" value="Cron" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e9c46a;strokeColor=#d4a843;fontSize=13;fontStyle=1;fontColor=#333;arcSize=10;" vertex="1" parent="1">
<mxGeometry x="1050" y="690" width="130" height="35" as="geometry" />
</mxCell>
<mxCell id="cron-detail" value="Scheduled Tasks&#xa;├ Cron Expressions&#xa;├ Job Execution&#xa;└ JSONL Storage" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fdf6e3;strokeColor=#e9c46a;fontSize=11;align=left;spacingLeft=10;arcSize=6;fontColor=#333;verticalAlign=top;spacingTop=6;" vertex="1" parent="1">
<mxGeometry x="1050" y="735" width="130" height="75" as="geometry" />
</mxCell>
<!-- Heartbeat -->
<mxCell id="heartbeat-sys" value="Heartbeat" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e9c46a;strokeColor=#d4a843;fontSize=13;fontStyle=1;fontColor=#333;arcSize=10;" vertex="1" parent="1">
<mxGeometry x="1200" y="690" width="130" height="35" as="geometry" />
</mxCell>
<mxCell id="heartbeat-detail" value="Always-On Agent&#xa;├ Periodic Runner&#xa;├ Wake-from-sleep&#xa;└ System Events" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fdf6e3;strokeColor=#e9c46a;fontSize=11;align=left;spacingLeft=10;arcSize=6;fontColor=#333;verticalAlign=top;spacingTop=6;" vertex="1" parent="1">
<mxGeometry x="1200" y="735" width="130" height="75" as="geometry" />
</mxCell>
<!-- Channels -->
<mxCell id="channels-sys" value="Channels" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e9c46a;strokeColor=#d4a843;fontSize=13;fontStyle=1;fontColor=#333;arcSize=10;" vertex="1" parent="1">
<mxGeometry x="1350" y="690" width="190" height="35" as="geometry" />
</mxCell>
<mxCell id="channels-detail" value="Messaging Integrations&#xa;├ Channel Manager&#xa;├ Plugin Registry&#xa;├ Inbound Debouncer&#xa;└ Telegram (via Gateway)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fdf6e3;strokeColor=#e9c46a;fontSize=11;align=left;spacingLeft=10;arcSize=6;fontColor=#333;verticalAlign=top;spacingTop=6;" vertex="1" parent="1">
<mxGeometry x="1350" y="735" width="190" height="90" as="geometry" />
</mxCell>
<!-- Media -->
<mxCell id="media-sys" value="Media" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e9c46a;strokeColor=#d4a843;fontSize=11;fontStyle=1;fontColor=#333;arcSize=10;" vertex="1" parent="1">
<mxGeometry x="1350" y="835" width="190" height="30" as="geometry" />
</mxCell>
<mxCell id="media-detail" value="Image / Video / Audio&#xa;├ describe-image (LLM)&#xa;├ describe-video (frames)&#xa;└ transcribe (Whisper)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fdf6e3;strokeColor=#e9c46a;fontSize=10;align=left;spacingLeft=10;arcSize=6;fontColor=#333;verticalAlign=top;spacingTop=4;" vertex="1" parent="1">
<mxGeometry x="1350" y="870" width="190" height="75" as="geometry" />
</mxCell>
<!-- Hub → Agent Engine arrow -->
<mxCell id="hub-agent-arrow" value="" style="endArrow=classic;startArrow=classic;html=1;strokeColor=#264653;strokeWidth=1.5;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="hub" target="agent-engine">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="hub-agent-label" value="manages" style="edgeLabel;html=1;align=center;verticalAlign=middle;fontSize=9;fontColor=#264653;fontStyle=2;" vertex="1" connectable="0" parent="hub-agent-arrow">
<mxGeometry relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<!-- ==================== SHARED PACKAGES ==================== -->
<mxCell id="shared-group" value="Shared Packages" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f0fff4;strokeColor=#38b000;strokeWidth=2;fontSize=14;fontStyle=1;verticalAlign=top;align=left;spacingLeft=10;spacingTop=5;arcSize=6;fontColor=#38b000;" vertex="1" parent="1">
<mxGeometry x="40" y="990" width="1080" height="130" as="geometry" />
</mxCell>
<mxCell id="pkg-sdk" value="SDK&#xa;@multica/sdk&#xa;Gateway Client&#xa;Socket.io" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d4edda;strokeColor=#38b000;fontSize=11;align=center;arcSize=8;fontColor=#333;fontStyle=0;" vertex="1" parent="1">
<mxGeometry x="60" y="1025" width="130" height="75" as="geometry" />
</mxCell>
<mxCell id="pkg-ui" value="UI&#xa;@multica/ui&#xa;Shadcn + Radix&#xa;Tailwind v4" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d4edda;strokeColor=#38b000;fontSize=11;align=center;arcSize=8;fontColor=#333;fontStyle=0;" vertex="1" parent="1">
<mxGeometry x="210" y="1025" width="130" height="75" as="geometry" />
</mxCell>
<mxCell id="pkg-store" value="Store&#xa;@multica/store&#xa;Zustand" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d4edda;strokeColor=#38b000;fontSize=11;align=center;arcSize=8;fontColor=#333;fontStyle=0;" vertex="1" parent="1">
<mxGeometry x="360" y="1025" width="130" height="75" as="geometry" />
</mxCell>
<mxCell id="pkg-hooks" value="Hooks&#xa;@multica/hooks&#xa;React Hooks" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d4edda;strokeColor=#38b000;fontSize=11;align=center;arcSize=8;fontColor=#333;fontStyle=0;" vertex="1" parent="1">
<mxGeometry x="510" y="1025" width="130" height="75" as="geometry" />
</mxCell>
<mxCell id="pkg-types" value="Types&#xa;@multica/types&#xa;Shared TypeScript&#xa;Zero-dep" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d4edda;strokeColor=#38b000;fontSize=11;align=center;arcSize=8;fontColor=#333;fontStyle=0;" vertex="1" parent="1">
<mxGeometry x="660" y="1025" width="130" height="75" as="geometry" />
</mxCell>
<mxCell id="pkg-utils" value="Utils&#xa;@multica/utils&#xa;Paths, Retry&#xa;Errors, Device ID" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d4edda;strokeColor=#38b000;fontSize=11;align=center;arcSize=8;fontColor=#333;fontStyle=0;" vertex="1" parent="1">
<mxGeometry x="810" y="1025" width="140" height="75" as="geometry" />
</mxCell>
<mxCell id="pkg-utils2" value="Credentials&#xa;@multica/core&#xa;credentials.json5&#xa;skills.env.json5" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#d4edda;strokeColor=#38b000;fontSize=11;align=center;arcSize=8;fontColor=#333;fontStyle=0;" vertex="1" parent="1">
<mxGeometry x="970" y="1025" width="130" height="75" as="geometry" />
</mxCell>
<!-- ==================== SKILLS LAYER ==================== -->
<mxCell id="skills-group" value="Bundled Skills" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fffde7;strokeColor=#f9a825;strokeWidth=2;fontSize=14;fontStyle=1;verticalAlign=top;align=left;spacingLeft=10;spacingTop=5;arcSize=6;fontColor=#f9a825;" vertex="1" parent="1">
<mxGeometry x="1150" y="990" width="420" height="130" as="geometry" />
</mxCell>
<mxCell id="skill-pdf" value="pdf" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff9c4;strokeColor=#f9a825;fontSize=10;align=center;arcSize=10;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="1170" y="1025" width="55" height="30" as="geometry" />
</mxCell>
<mxCell id="skill-pptx" value="pptx" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff9c4;strokeColor=#f9a825;fontSize=10;align=center;arcSize=10;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="1235" y="1025" width="55" height="30" as="geometry" />
</mxCell>
<mxCell id="skill-docx" value="docx" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff9c4;strokeColor=#f9a825;fontSize=10;align=center;arcSize=10;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="1300" y="1025" width="55" height="30" as="geometry" />
</mxCell>
<mxCell id="skill-xlsx" value="xlsx" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff9c4;strokeColor=#f9a825;fontSize=10;align=center;arcSize=10;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="1365" y="1025" width="55" height="30" as="geometry" />
</mxCell>
<mxCell id="skill-whisper" value="whisper" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff9c4;strokeColor=#f9a825;fontSize=10;align=center;arcSize=10;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="1430" y="1025" width="55" height="30" as="geometry" />
</mxCell>
<mxCell id="skill-finance" value="finance-&#xa;research" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff9c4;strokeColor=#f9a825;fontSize=10;align=center;arcSize=10;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="1495" y="1025" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="skill-profile" value="profile-&#xa;setup" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff9c4;strokeColor=#f9a825;fontSize=10;align=center;arcSize=10;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="1170" y="1065" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="skill-creator" value="skill-&#xa;creator" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff9c4;strokeColor=#f9a825;fontSize=10;align=center;arcSize=10;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="1240" y="1065" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="skill-earnings" value="earnings-&#xa;analysis" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff9c4;strokeColor=#f9a825;fontSize=10;align=center;arcSize=10;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="1310" y="1065" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="skill-dcf" value="dcf-&#xa;valuation" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fff9c4;strokeColor=#f9a825;fontSize=10;align=center;arcSize=10;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="1380" y="1065" width="60" height="30" as="geometry" />
</mxCell>
<!-- ==================== DATA LAYER ==================== -->
<mxCell id="data-group" value="Persistent Storage (~/.super-multica/)" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f5f5f5;strokeColor=#666;strokeWidth=2;fontSize=14;fontStyle=1;verticalAlign=top;align=left;spacingLeft=10;spacingTop=5;arcSize=6;fontColor=#666;dashed=1;" vertex="1" parent="1">
<mxGeometry x="40" y="1150" width="1530" height="90" as="geometry" />
</mxCell>
<mxCell id="data-sessions" value="sessions/&#xa;{id}.jsonl" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#eee;strokeColor=#999;fontSize=10;align=center;arcSize=8;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="70" y="1185" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="data-profiles" value="agent-profiles/&#xa;{id}/*.md" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#eee;strokeColor=#999;fontSize=10;align=center;arcSize=8;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="190" y="1185" width="110" height="40" as="geometry" />
</mxCell>
<mxCell id="data-creds" value="credentials&#xa;.json5" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#eee;strokeColor=#999;fontSize=10;align=center;arcSize=8;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="320" y="1185" width="90" height="40" as="geometry" />
</mxCell>
<mxCell id="data-agents" value="agents&#xa;.json" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#eee;strokeColor=#999;fontSize=10;align=center;arcSize=8;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="430" y="1185" width="80" height="40" as="geometry" />
</mxCell>
<mxCell id="data-subagents" value="subagents&#xa;.jsonl" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#eee;strokeColor=#999;fontSize=10;align=center;arcSize=8;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="530" y="1185" width="90" height="40" as="geometry" />
</mxCell>
<mxCell id="data-cron" value="cron-jobs&#xa;.jsonl" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#eee;strokeColor=#999;fontSize=10;align=center;arcSize=8;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="640" y="1185" width="80" height="40" as="geometry" />
</mxCell>
<mxCell id="data-skills-env" value="skills.env&#xa;.json5" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#eee;strokeColor=#999;fontSize=10;align=center;arcSize=8;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="740" y="1185" width="80" height="40" as="geometry" />
</mxCell>
<mxCell id="data-runlog" value="run-log&#xa;.jsonl" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#eee;strokeColor=#999;fontSize=10;align=center;arcSize=8;fontColor=#333;" vertex="1" parent="1">
<mxGeometry x="840" y="1185" width="80" height="40" as="geometry" />
</mxCell>
<!-- ==================== EXTERNAL DEPS ==================== -->
<mxCell id="ext-group" value="External Dependencies" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#fafafa;strokeColor=#aaa;strokeWidth=1;fontSize=12;fontStyle=1;verticalAlign=top;align=left;spacingLeft=10;spacingTop=5;arcSize=6;fontColor=#888;dashed=1;dashPattern=5 5;" vertex="1" parent="1">
<mxGeometry x="40" y="1270" width="1530" height="60" as="geometry" />
</mxCell>
<mxCell id="ext-piagent" value="pi-agent-core" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f8f8;strokeColor=#ccc;fontSize=10;align=center;arcSize=8;fontColor=#666;" vertex="1" parent="1">
<mxGeometry x="70" y="1295" width="100" height="25" as="geometry" />
</mxCell>
<mxCell id="ext-electron" value="Electron 33+" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f8f8;strokeColor=#ccc;fontSize=10;align=center;arcSize=8;fontColor=#666;" vertex="1" parent="1">
<mxGeometry x="185" y="1295" width="100" height="25" as="geometry" />
</mxCell>
<mxCell id="ext-nestjs" value="NestJS 11" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f8f8;strokeColor=#ccc;fontSize=10;align=center;arcSize=8;fontColor=#666;" vertex="1" parent="1">
<mxGeometry x="300" y="1295" width="80" height="25" as="geometry" />
</mxCell>
<mxCell id="ext-nextjs" value="Next.js 16" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f8f8;strokeColor=#ccc;fontSize=10;align=center;arcSize=8;fontColor=#666;" vertex="1" parent="1">
<mxGeometry x="395" y="1295" width="80" height="25" as="geometry" />
</mxCell>
<mxCell id="ext-react" value="React 19" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f8f8;strokeColor=#ccc;fontSize=10;align=center;arcSize=8;fontColor=#666;" vertex="1" parent="1">
<mxGeometry x="490" y="1295" width="70" height="25" as="geometry" />
</mxCell>
<mxCell id="ext-socketio" value="Socket.io" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f8f8;strokeColor=#ccc;fontSize=10;align=center;arcSize=8;fontColor=#666;" vertex="1" parent="1">
<mxGeometry x="575" y="1295" width="70" height="25" as="geometry" />
</mxCell>
<mxCell id="ext-zustand" value="Zustand" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f8f8;strokeColor=#ccc;fontSize=10;align=center;arcSize=8;fontColor=#666;" vertex="1" parent="1">
<mxGeometry x="660" y="1295" width="70" height="25" as="geometry" />
</mxCell>
<mxCell id="ext-turborepo" value="Turborepo" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f8f8;strokeColor=#ccc;fontSize=10;align=center;arcSize=8;fontColor=#666;" vertex="1" parent="1">
<mxGeometry x="745" y="1295" width="70" height="25" as="geometry" />
</mxCell>
<mxCell id="ext-pnpm" value="pnpm 10" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f8f8;strokeColor=#ccc;fontSize=10;align=center;arcSize=8;fontColor=#666;" vertex="1" parent="1">
<mxGeometry x="830" y="1295" width="70" height="25" as="geometry" />
</mxCell>
<mxCell id="ext-vitest" value="Vitest" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f8f8;strokeColor=#ccc;fontSize=10;align=center;arcSize=8;fontColor=#666;" vertex="1" parent="1">
<mxGeometry x="915" y="1295" width="60" height="25" as="geometry" />
</mxCell>
<mxCell id="ext-tailwind" value="Tailwind v4" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f8f8;strokeColor=#ccc;fontSize=10;align=center;arcSize=8;fontColor=#666;" vertex="1" parent="1">
<mxGeometry x="990" y="1295" width="80" height="25" as="geometry" />
</mxCell>
<mxCell id="ext-shadcn" value="Shadcn/UI" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#f8f8f8;strokeColor=#ccc;fontSize=10;align=center;arcSize=8;fontColor=#666;" vertex="1" parent="1">
<mxGeometry x="1085" y="1295" width="75" height="25" as="geometry" />
</mxCell>
<!-- ==================== LEGEND ==================== -->
<mxCell id="legend-box" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#ddd;strokeWidth=1;arcSize=6;" vertex="1" parent="1">
<mxGeometry x="1200" y="1270" width="370" height="60" as="geometry" />
</mxCell>
<mxCell id="legend-title" value="Legend" style="text;html=1;fontSize=11;fontStyle=1;align=left;fontColor=#666;" vertex="1" parent="1">
<mxGeometry x="1210" y="1273" width="50" height="16" as="geometry" />
</mxCell>
<mxCell id="legend-1" value="" style="rounded=1;fillColor=#4361ee;strokeColor=none;fontSize=1;" vertex="1" parent="1">
<mxGeometry x="1210" y="1295" width="12" height="12" as="geometry" />
</mxCell>
<mxCell id="legend-1t" value="Apps" style="text;fontSize=9;fontColor=#666;align=left;" vertex="1" parent="1">
<mxGeometry x="1226" y="1293" width="35" height="16" as="geometry" />
</mxCell>
<mxCell id="legend-2" value="" style="rounded=1;fillColor=#264653;strokeColor=none;fontSize=1;" vertex="1" parent="1">
<mxGeometry x="1270" y="1295" width="12" height="12" as="geometry" />
</mxCell>
<mxCell id="legend-2t" value="Engine" style="text;fontSize=9;fontColor=#666;align=left;" vertex="1" parent="1">
<mxGeometry x="1286" y="1293" width="40" height="16" as="geometry" />
</mxCell>
<mxCell id="legend-3" value="" style="rounded=1;fillColor=#e76f51;strokeColor=none;fontSize=1;" vertex="1" parent="1">
<mxGeometry x="1335" y="1295" width="12" height="12" as="geometry" />
</mxCell>
<mxCell id="legend-3t" value="Hub" style="text;fontSize=9;fontColor=#666;align=left;" vertex="1" parent="1">
<mxGeometry x="1351" y="1293" width="30" height="16" as="geometry" />
</mxCell>
<mxCell id="legend-4" value="" style="rounded=1;fillColor=#2a9d8f;strokeColor=none;fontSize=1;" vertex="1" parent="1">
<mxGeometry x="1390" y="1295" width="12" height="12" as="geometry" />
</mxCell>
<mxCell id="legend-4t" value="Tools" style="text;fontSize=9;fontColor=#666;align=left;" vertex="1" parent="1">
<mxGeometry x="1406" y="1293" width="35" height="16" as="geometry" />
</mxCell>
<mxCell id="legend-5" value="" style="rounded=1;fillColor=#e9c46a;strokeColor=none;fontSize=1;" vertex="1" parent="1">
<mxGeometry x="1450" y="1295" width="12" height="12" as="geometry" />
</mxCell>
<mxCell id="legend-5t" value="Systems" style="text;fontSize=9;fontColor=#666;align=left;" vertex="1" parent="1">
<mxGeometry x="1466" y="1293" width="50" height="16" as="geometry" />
</mxCell>
<mxCell id="legend-6" value="" style="rounded=1;fillColor=#38b000;strokeColor=none;fontSize=1;" vertex="1" parent="1">
<mxGeometry x="1520" y="1295" width="12" height="12" as="geometry" />
</mxCell>
<mxCell id="legend-6t" value="Shared" style="text;fontSize=9;fontColor=#666;align=left;" vertex="1" parent="1">
<mxGeometry x="1536" y="1293" width="40" height="16" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

View file

@ -1,347 +0,0 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Super Multica 代码贡献统计</title>
<style>
:root {
--bg: #0b0d10;
--panel: #14181d;
--panel-2: #1a2027;
--line: #2a3440;
--text: #e8edf3;
--muted: #98a7b7;
--ok: #2fbf71;
--danger: #ef4444;
}
* { box-sizing: border-box; }
body {
margin: 0;
font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial;
background: radial-gradient(circle at 20% -10%, #1a2430 0%, #0b0d10 45%) fixed;
color: var(--text);
line-height: 1.4;
}
.wrap { max-width: 1200px; margin: 0 auto; padding: 24px; }
h1 { margin: 0 0 8px; font-size: 28px; }
.sub { color: var(--muted); margin-bottom: 20px; }
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
gap: 12px;
margin-bottom: 18px;
}
.card {
background: linear-gradient(180deg, var(--panel) 0%, var(--panel-2) 100%);
border: 1px solid var(--line);
border-radius: 10px;
padding: 12px;
}
.k { color: var(--muted); font-size: 12px; margin-bottom: 8px; }
.v { font-size: 24px; font-weight: 700; letter-spacing: 0.3px; }
.section { margin-top: 14px; }
.section h2 { margin: 0 0 10px; font-size: 16px; color: #d4dde7; }
.panel {
background: var(--panel);
border: 1px solid var(--line);
border-radius: 10px;
overflow: hidden;
}
table { width: 100%; border-collapse: collapse; }
th, td { padding: 9px 10px; border-bottom: 1px solid var(--line); font-size: 13px; }
th { background: #11161c; text-align: left; color: #c5d0db; position: sticky; top: 0; }
tr:last-child td { border-bottom: 0; }
.num { text-align: right; font-variant-numeric: tabular-nums; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }
.bar-wrap { background: #0f1318; border-radius: 999px; height: 8px; width: 180px; border: 1px solid #273241; }
.bar { height: 100%; border-radius: 999px; background: linear-gradient(90deg, #3f7ef7, #58a6ff); }
.ok { color: var(--ok); }
.danger { color: var(--danger); }
.foot { margin-top: 16px; color: var(--muted); font-size: 12px; }
.scroll { max-height: 420px; overflow: auto; }
</style>
</head>
<body>
<div class="wrap">
<h1>Super Multica 代码贡献统计</h1>
<div class="sub" id="subtitle"></div>
<div class="grid" id="summary"></div>
<div class="section">
<h2>代码量分布(按扩展名)</h2>
<div class="panel scroll"><table id="extTable"></table></div>
</div>
<div class="section">
<h2>人员贡献(人工口径)</h2>
<div class="panel scroll"><table id="authorTable"></table></div>
</div>
<div class="section">
<h2>每日贡献(人工口径)</h2>
<div class="panel scroll"><table id="dayTable"></table></div>
</div>
<div class="section">
<h2>小时段贡献(人工口径)</h2>
<div class="panel scroll"><table id="hourTable"></table></div>
</div>
<div class="foot">数据来源git log --numstat 与当前工作树文件统计。人工口径排除 checkpointer / dependabot。</div>
</div>
<script>
const RAW = {
locTotals: String.raw`
files 669
lines 137510
source_files 500
source_lines 75894
doc_files 47
doc_lines 11701
config_files 82
config_lines 42562
`,
locByExt: String.raw`
mjs 3 25
js 8 357
production 1 11
dockerignore 1 45
xml 5 15
gitignore 4 126
tsx 109 12206
ts 337 55198
json 38 1025
yaml 2 21654
yml 2 66
css 4 412
sh 5 259
icns 1 1992
example 2 115
json5 1 87
svg 1 75
sql 1 17
html 5 2363
md 47 11701
development 1 10
npmrc 1 1
xsd 39 19730
ico 2 315
[noext] 1 44
cjs 1 19
py 28 5055
png 18 4154
drawio 1 433
`,
authorHuman: String.raw`
Jiayuan Zhang <forrestchang7@gmail.com> 233 110606 55597 55009 49.11% 27.67%
Naiyuan Qing <145280634+NevilleQingNY@users.noreply.github.com> 253 61422 24246 37176 27.27% 30.05%
Jiang Bohan <bhjiang@outlook.com> 203 30764 4944 25820 13.66% 24.11%
yushen <ldnvnbl@gmail.com> 128 21171 2185 18986 9.40% 15.20%
yushen <linyushen@proton.me> 25 1279 341 938 0.57% 2.97%
`,
dayHuman: String.raw`
2026-01-28 4 3403 13 3390 14 17
2026-01-29 2 9990 676 9314 16 16
2026-01-30 173 39220 4907 34313 01 23
2026-01-31 28 3353 1117 2236 01 21
2026-02-01 17 4093 539 3554 02 23
2026-02-02 42 3993 2331 1662 00 17
2026-02-03 52 22784 6410 16374 02 21
2026-02-04 64 8893 1733 7160 03 20
2026-02-05 78 12776 5203 7573 02 22
2026-02-06 42 6074 521 5553 12 22
2026-02-09 44 4458 941 3517 07 19
2026-02-10 50 9474 4410 5064 02 23
2026-02-11 58 7069 3222 3847 00 20
2026-02-12 60 79852 4003 75849 09 23
2026-02-13 66 6194 1427 4767 01 23
2026-02-14 30 1479 1049 430 00 23
2026-02-15 32 2137 48811 -46674 00 04
`,
hourHuman: String.raw`
00 18 1236 248 988
01 32 5718 45407 -39689
02 34 4449 2948 1501
03 27 3861 3587 274
04 24 4591 1047 3544
05 7 2752 69 2683
06 0 0 0 0
07 7 809 54 755
08 17 3605 2099 1506
09 19 5612 1801 3811
10 17 2907 1784 1123
11 22 1673 530 1143
12 12 1974 72 1902
13 49 8470 1911 6559
14 67 8581 2303 6278
15 64 10866 2467 8399
16 84 35691 6756 28935
17 111 97435 7908 89527
18 73 8936 2276 6660
19 36 2716 723 1993
20 22 3916 1223 2693
21 24 2296 497 1799
22 48 3545 1016 2529
23 28 3603 587 3016
`,
dayPeak: String.raw`
2026-01-28 16 1 2237 5
2026-01-29 16 2 9990 676
2026-01-30 13 16 4675 55
2026-01-31 02 9 1277 144
2026-02-01 23 7 3036 270
2026-02-02 16 13 1218 280
2026-02-03 16 7 14253 4890
2026-02-04 17 10 2766 143
2026-02-05 17 17 4345 1657
2026-02-06 17 8 2300 120
2026-02-09 19 3 1095 335
2026-02-10 17 5 5778 3625
2026-02-11 17 17 2520 1018
2026-02-12 17 13 73452 132
2026-02-13 12 7 1378 56
2026-02-14 00 8 601 212
2026-02-15 04 4 657 364
`
};
const fmt = (n) => Number(n).toLocaleString("en-US");
const tsv = (txt) => txt.trim().split(/\n+/).map((line) => line.split("\t"));
const toNum = (v) => Number(v || 0);
const locTotalsRows = tsv(RAW.locTotals);
const locTotals = Object.fromEntries(locTotalsRows.map(([k, v]) => [k, toNum(v)]));
const extRows = tsv(RAW.locByExt).map(([ext, files, lines]) => ({
ext,
files: toNum(files),
lines: toNum(lines),
})).sort((a, b) => b.lines - a.lines);
const authors = tsv(RAW.authorHuman).map(([name, commits, add, del, net, addPct, commitPct]) => ({
name,
commits: toNum(commits),
add: toNum(add),
del: toNum(del),
net: toNum(net),
addPct,
commitPct,
})).sort((a, b) => b.add - a.add);
const dayPeaks = Object.fromEntries(tsv(RAW.dayPeak).map(([d, h, c, a, del]) => [d, {
hour: h,
commits: toNum(c),
add: toNum(a),
del: toNum(del),
}]));
const days = tsv(RAW.dayHuman).map(([date, commits, add, del, net, startHour, endHour]) => ({
date,
commits: toNum(commits),
add: toNum(add),
del: toNum(del),
net: toNum(net),
startHour,
endHour,
peak: dayPeaks[date] || null,
})).sort((a, b) => a.date.localeCompare(b.date));
const hours = tsv(RAW.hourHuman).map(([hour, commits, add, del, net]) => ({
hour,
commits: toNum(commits),
add: toNum(add),
del: toNum(del),
net: toNum(net),
})).sort((a, b) => a.hour.localeCompare(b.hour));
const totalHumanCommits = authors.reduce((sum, x) => sum + x.commits, 0);
const totalHumanAdd = authors.reduce((sum, x) => sum + x.add, 0);
const totalHumanDel = authors.reduce((sum, x) => sum + x.del, 0);
const topHour = [...hours].sort((a, b) => b.add - a.add)[0] || { hour: "--", add: 0 };
const startDate = days[0]?.date || "--";
const endDate = days[days.length - 1]?.date || "--";
document.getElementById("subtitle").textContent = `${startDate} ~ ${endDate}`;
const summaryItems = [
["总文件数", fmt(locTotals.files || 0)],
["总行数", fmt(locTotals.lines || 0)],
["源码行数", fmt(locTotals.source_lines || 0)],
["贡献人数", fmt(authors.length)],
["人工提交数", fmt(totalHumanCommits)],
["人工新增", fmt(totalHumanAdd)],
["人工删除", fmt(totalHumanDel)],
["最高产小时", `${topHour.hour}:00 (${fmt(topHour.add)})`],
];
document.getElementById("summary").innerHTML = summaryItems.map(([k, v]) => (
`<div class="card"><div class="k">${k}</div><div class="v">${v}</div></div>`
)).join("");
const maxExtLines = Math.max(...extRows.map((x) => x.lines), 1);
document.getElementById("extTable").innerHTML = `
<thead><tr><th>扩展名</th><th class="num">文件数</th><th class="num">行数</th><th>占比</th><th>可视化</th></tr></thead>
<tbody>
${extRows.map((r) => {
const pct = ((r.lines / (locTotals.lines || 1)) * 100).toFixed(2);
const w = ((r.lines / maxExtLines) * 100).toFixed(1);
return `<tr>
<td class="mono">${r.ext}</td>
<td class="num">${fmt(r.files)}</td>
<td class="num">${fmt(r.lines)}</td>
<td class="num">${pct}%</td>
<td><div class="bar-wrap"><div class="bar" style="width:${w}%"></div></div></td>
</tr>`;
}).join("")}
</tbody>`;
document.getElementById("authorTable").innerHTML = `
<thead><tr><th>作者</th><th class="num">提交</th><th class="num">新增</th><th class="num">删除</th><th class="num">净新增</th><th class="num">新增占比</th><th class="num">提交占比</th></tr></thead>
<tbody>
${authors.map((a) => `<tr>
<td>${a.name}</td>
<td class="num">${fmt(a.commits)}</td>
<td class="num">${fmt(a.add)}</td>
<td class="num">${fmt(a.del)}</td>
<td class="num ${a.net >= 0 ? "ok" : "danger"}">${fmt(a.net)}</td>
<td class="num">${a.addPct}</td>
<td class="num">${a.commitPct}</td>
</tr>`).join("")}
</tbody>`;
document.getElementById("dayTable").innerHTML = `
<thead><tr><th>日期</th><th class="num">提交</th><th class="num">新增</th><th class="num">删除</th><th class="num">净新增</th><th>活跃时段</th><th>峰值小时</th></tr></thead>
<tbody>
${days.map((d) => `<tr>
<td class="mono">${d.date}</td>
<td class="num">${fmt(d.commits)}</td>
<td class="num">${fmt(d.add)}</td>
<td class="num">${fmt(d.del)}</td>
<td class="num ${d.net >= 0 ? "ok" : "danger"}">${fmt(d.net)}</td>
<td class="mono">${d.startHour}:00 - ${d.endHour}:59</td>
<td class="mono">${d.peak ? `${d.peak.hour}:00 (${fmt(d.peak.add)})` : "--"}</td>
</tr>`).join("")}
</tbody>`;
const maxHourAdd = Math.max(...hours.map((h) => h.add), 1);
document.getElementById("hourTable").innerHTML = `
<thead><tr><th>小时</th><th class="num">提交</th><th class="num">新增</th><th class="num">删除</th><th class="num">净新增</th><th>可视化</th></tr></thead>
<tbody>
${hours.map((h) => {
const w = ((h.add / maxHourAdd) * 100).toFixed(1);
return `<tr>
<td class="mono">${h.hour}:00</td>
<td class="num">${fmt(h.commits)}</td>
<td class="num">${fmt(h.add)}</td>
<td class="num">${fmt(h.del)}</td>
<td class="num ${h.net >= 0 ? "ok" : "danger"}">${fmt(h.net)}</td>
<td><div class="bar-wrap"><div class="bar" style="width:${w}%"></div></div></td>
</tr>`;
}).join("")}
</tbody>`;
</script>
</body>
</html>

View file

@ -1,884 +0,0 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Subagent Orchestration Architecture</title>
<style>
:root {
--bg: #0d1117;
--surface: #161b22;
--border: #30363d;
--text: #e6edf3;
--text-muted: #8b949e;
--accent: #58a6ff;
--green: #3fb950;
--orange: #d29922;
--red: #f85149;
--purple: #bc8cff;
--cyan: #39d2c0;
--pink: #f778ba;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;
background: var(--bg);
color: var(--text);
padding: 40px 20px;
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
h1 {
text-align: center;
font-size: 28px;
margin-bottom: 8px;
background: linear-gradient(135deg, var(--accent), var(--purple));
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.subtitle {
text-align: center;
color: var(--text-muted);
font-size: 14px;
margin-bottom: 48px;
}
h2 {
font-size: 20px;
margin: 48px 0 24px;
color: var(--accent);
display: flex;
align-items: center;
gap: 8px;
}
h2::before {
content: '';
display: inline-block;
width: 4px;
height: 20px;
background: var(--accent);
border-radius: 2px;
}
/* ── Architecture Diagram ── */
.arch-diagram {
display: grid;
grid-template-columns: 1fr;
gap: 20px;
position: relative;
}
.arch-row {
display: flex;
justify-content: center;
gap: 24px;
flex-wrap: wrap;
}
.arch-box {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 12px;
padding: 20px 24px;
min-width: 200px;
max-width: 320px;
position: relative;
}
.arch-box.wide { min-width: 500px; }
@media (max-width: 640px) {
.arch-box.wide { min-width: 100%; }
}
.arch-box .title {
font-weight: 600;
font-size: 15px;
margin-bottom: 6px;
display: flex;
align-items: center;
gap: 8px;
}
.arch-box .desc {
font-size: 12px;
color: var(--text-muted);
line-height: 1.5;
}
.arch-box .file {
font-size: 11px;
color: var(--text-muted);
font-family: 'SF Mono', Consolas, monospace;
margin-top: 8px;
padding-top: 8px;
border-top: 1px solid var(--border);
}
.badge {
display: inline-block;
padding: 2px 8px;
border-radius: 10px;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.badge-blue { background: rgba(88,166,255,0.15); color: var(--accent); }
.badge-green { background: rgba(63,185,80,0.15); color: var(--green); }
.badge-purple { background: rgba(188,140,255,0.15); color: var(--purple); }
.badge-orange { background: rgba(210,153,34,0.15); color: var(--orange); }
.badge-cyan { background: rgba(57,210,192,0.15); color: var(--cyan); }
.badge-pink { background: rgba(247,120,186,0.15); color: var(--pink); }
/* ── SVG Arrows ── */
.arrow-section {
display: flex;
justify-content: center;
padding: 4px 0;
}
.arrow-section svg {
overflow: visible;
}
/* ── Call Chain ── */
.call-chain {
position: relative;
padding-left: 32px;
}
.call-chain::before {
content: '';
position: absolute;
left: 15px;
top: 0;
bottom: 0;
width: 2px;
background: linear-gradient(to bottom, var(--accent), var(--purple), var(--green));
border-radius: 1px;
}
.chain-step {
position: relative;
margin-bottom: 20px;
padding: 16px 20px;
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
transition: border-color 0.2s;
}
.chain-step:hover {
border-color: var(--accent);
}
.chain-step::before {
content: attr(data-step);
position: absolute;
left: -32px;
top: 16px;
width: 24px;
height: 24px;
border-radius: 50%;
background: var(--accent);
color: var(--bg);
font-size: 11px;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
transform: translateX(-4px);
}
.chain-step.phase-spawn::before { background: var(--accent); }
.chain-step.phase-watch::before { background: var(--purple); }
.chain-step.phase-complete::before { background: var(--green); }
.chain-step.phase-cleanup::before { background: var(--orange); }
.chain-step .step-title {
font-weight: 600;
font-size: 14px;
margin-bottom: 4px;
}
.chain-step .step-detail {
font-size: 12px;
color: var(--text-muted);
}
.chain-step code {
background: rgba(88,166,255,0.1);
color: var(--accent);
padding: 1px 6px;
border-radius: 4px;
font-size: 12px;
font-family: 'SF Mono', Consolas, monospace;
}
.chain-step .arrow-label {
font-size: 11px;
color: var(--cyan);
font-family: 'SF Mono', Consolas, monospace;
margin-top: 6px;
}
/* ── Sequence Diagram ── */
.sequence-container {
overflow-x: auto;
padding: 20px 0;
}
.sequence-diagram {
min-width: 900px;
margin: 0 auto;
}
/* ── Module Map ── */
.module-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 16px;
}
.module-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 10px;
padding: 16px 20px;
}
.module-card .mod-name {
font-weight: 600;
font-size: 14px;
font-family: 'SF Mono', Consolas, monospace;
margin-bottom: 4px;
}
.module-card .mod-desc {
font-size: 12px;
color: var(--text-muted);
margin-bottom: 8px;
}
.module-card .mod-exports {
font-size: 11px;
color: var(--text-muted);
font-family: 'SF Mono', Consolas, monospace;
}
.module-card .mod-exports span {
color: var(--green);
}
/* ── State Machine ── */
.state-diagram {
display: flex;
justify-content: center;
padding: 20px 0;
}
.state-node {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 18px;
border-radius: 8px;
font-size: 13px;
font-weight: 600;
white-space: nowrap;
}
.state-arrow {
display: inline-flex;
align-items: center;
color: var(--text-muted);
font-size: 12px;
padding: 0 6px;
}
.state-arrow svg { margin: 0 4px; }
.legend {
display: flex;
gap: 20px;
flex-wrap: wrap;
margin-top: 16px;
justify-content: center;
}
.legend-item {
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: var(--text-muted);
}
.legend-dot {
width: 10px;
height: 10px;
border-radius: 50%;
}
</style>
</head>
<body>
<div class="container">
<h1>Subagent Orchestration Architecture</h1>
<p class="subtitle">Super Multica &mdash; Parent-child agent spawning, lifecycle management, and result announcement</p>
<!-- ══════════════════════════════════════════════════════ -->
<h2>System Architecture</h2>
<!-- ══════════════════════════════════════════════════════ -->
<div class="arch-diagram">
<!-- Row 1: Parent Agent -->
<div class="arch-row">
<div class="arch-box wide" style="border-color: var(--accent);">
<div class="title">
<span class="badge badge-blue">Agent</span>
Parent Agent (Interactive Session)
</div>
<div class="desc">
User-facing agent with full tool access. Can spawn child agents via <code>sessions_spawn</code> tool.
Receives announcement messages when child agents complete.
</div>
<div class="file">src/agent/runner.ts &rarr; tools: sessions_spawn, exec, glob, web_fetch, ...</div>
</div>
</div>
<!-- Arrow -->
<div class="arrow-section">
<svg width="400" height="48">
<defs>
<marker id="arrow1" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#58a6ff"/>
</marker>
<marker id="arrow2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#3fb950"/>
</marker>
</defs>
<!-- spawn arrow (down-left) -->
<line x1="140" y1="4" x2="80" y2="40" stroke="#58a6ff" stroke-width="2" marker-end="url(#arrow1)"/>
<text x="70" y="24" fill="#58a6ff" font-size="11" font-family="monospace">spawn</text>
<!-- announce arrow (up-right) -->
<line x1="320" y1="40" x2="260" y2="4" stroke="#3fb950" stroke-width="2" stroke-dasharray="6 3" marker-end="url(#arrow2)"/>
<text x="282" y="24" fill="#3fb950" font-size="11" font-family="monospace">announce</text>
</svg>
</div>
<!-- Row 2: Hub + Registry -->
<div class="arch-row">
<div class="arch-box" style="border-color: var(--purple);">
<div class="title">
<span class="badge badge-purple">Singleton</span>
Hub
</div>
<div class="desc">
Central coordinator. Creates &amp; manages all agents. Provides <code>createSubagent()</code>,
<code>getAgent()</code>, <code>closeAgent()</code>. Calls registry init on startup, shutdown on exit.
</div>
<div class="file">src/hub/hub.ts + hub-singleton.ts</div>
</div>
<div class="arch-box" style="border-color: var(--orange);">
<div class="title">
<span class="badge badge-orange">Module</span>
Subagent Registry
</div>
<div class="desc">
In-memory Map + JSON persistence. Tracks run lifecycle (created &rarr; started &rarr; ended).
Archive sweeper cleans old runs every 60s. Handles crash recovery on restart.
</div>
<div class="file">src/agent/subagent/registry.ts</div>
</div>
</div>
<!-- Arrow -->
<div class="arrow-section">
<svg width="400" height="48">
<defs>
<marker id="arrow3" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#bc8cff"/>
</marker>
<marker id="arrow4" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#d29922"/>
</marker>
</defs>
<line x1="120" y1="4" x2="120" y2="40" stroke="#bc8cff" stroke-width="2" marker-end="url(#arrow3)"/>
<text x="132" y="26" fill="#bc8cff" font-size="11" font-family="monospace">createSubagent()</text>
<line x1="280" y1="4" x2="280" y2="40" stroke="#d29922" stroke-width="2" stroke-dasharray="6 3" marker-end="url(#arrow4)"/>
<text x="292" y="26" fill="#d29922" font-size="11" font-family="monospace">watchChildAgent()</text>
</svg>
</div>
<!-- Row 3: Child Agent + Announce + Store -->
<div class="arch-row">
<div class="arch-box" style="border-color: var(--cyan);">
<div class="title">
<span class="badge badge-cyan">Agent</span>
Child AsyncAgent
</div>
<div class="desc">
Isolated agent with <code>isSubagent: true</code>. Restricted tools (no <code>sessions_spawn</code>).
Custom system prompt: stay focused, no user messaging, no nested spawning.
</div>
<div class="file">src/agent/async-agent.ts</div>
</div>
<div class="arch-box" style="border-color: var(--green);">
<div class="title">
<span class="badge badge-green">Flow</span>
Announce Module
</div>
<div class="desc">
Reads child's last assistant reply from session JSONL. Formats announcement message with findings, duration, status.
Delivers to parent via <code>parentAgent.write()</code>.
</div>
<div class="file">src/agent/subagent/announce.ts</div>
</div>
<div class="arch-box" style="border-color: var(--text-muted);">
<div class="title">
<span class="badge" style="background:rgba(139,148,158,0.15);color:var(--text-muted);">Store</span>
Registry Store
</div>
<div class="desc">
JSON file persistence at <code>~/.super-multica/subagents/runs.json</code>.
Schema: <code>{ version: 1, runs: {...} }</code>. Survives process restarts.
</div>
<div class="file">src/agent/subagent/registry-store.ts</div>
</div>
</div>
</div>
<!-- ══════════════════════════════════════════════════════ -->
<h2>Call Chain &mdash; Spawn &amp; Lifecycle</h2>
<!-- ══════════════════════════════════════════════════════ -->
<div class="call-chain">
<div class="chain-step phase-spawn" data-step="1">
<div class="step-title">Parent Agent invokes <code>sessions_spawn</code> tool</div>
<div class="step-detail">
Agent calls tool with <code>{ task, label?, model?, cleanup?, timeoutSeconds? }</code>.
Guard rejects if <code>isSubagent === true</code>.
</div>
<div class="arrow-label">sessions-spawn.ts &rarr; execute()</div>
</div>
<div class="chain-step phase-spawn" data-step="2">
<div class="step-title">Generate IDs &amp; build system prompt</div>
<div class="step-detail">
<code>runId</code> = UUIDv7, <code>childSessionId</code> = UUIDv7.
<code>buildSubagentSystemPrompt()</code> creates prompt with task context, rules (no nested spawn, stay focused).
</div>
<div class="arrow-label">announce.ts &rarr; buildSubagentSystemPrompt()</div>
</div>
<div class="chain-step phase-spawn" data-step="3">
<div class="step-title">Hub creates child AsyncAgent</div>
<div class="step-detail">
<code>hub.createSubagent(childSessionId, { systemPrompt, model })</code>
creates an <code>AsyncAgent</code> with <code>isSubagent: true</code>. Not persisted to agent store (ephemeral).
</div>
<div class="arrow-label">hub.ts &rarr; createSubagent()</div>
</div>
<div class="chain-step phase-watch" data-step="4">
<div class="step-title">Write task to child (non-blocking)</div>
<div class="step-detail">
<code>childAgent.write(task)</code> enqueues the task to the serial queue.
This happens before registration so <code>waitForIdle()</code> observes queued work.
</div>
<div class="arrow-label">async-agent.ts &rarr; write() (enqueues to serial queue)</div>
</div>
<div class="chain-step phase-spawn" data-step="5">
<div class="step-title">Register run in registry</div>
<div class="step-detail">
<code>registerSubagentRun()</code> saves record to in-memory Map + JSON file.
Sets <code>createdAt</code>, starts archive sweeper.
</div>
<div class="arrow-label">registry.ts &rarr; registerSubagentRun()</div>
</div>
<div class="chain-step phase-watch" data-step="6">
<div class="step-title">Start lifecycle watcher &amp; return to parent</div>
<div class="step-detail">
<code>watchChildAgent()</code> sets <code>startedAt</code>.
Attaches <code>childAgent.waitForIdle()</code> (promise resolves when task queue drained)
and <code>childAgent.onClose()</code> callback. Optionally sets timeout timer.
Tool returns <code>{ status: "accepted", childSessionId, runId }</code> immediately.
</div>
<div class="arrow-label">registry.ts &rarr; watchChildAgent() &rarr; AsyncAgent.waitForIdle() + onClose()</div>
</div>
<div class="chain-step phase-watch" data-step="7">
<div class="step-title">Child agent processes task autonomously</div>
<div class="step-detail">
Child runs LLM inference with restricted tools. Uses its own session.
May call <code>exec</code>, <code>glob</code>, <code>web_fetch</code> etc. but NOT <code>sessions_spawn</code>.
</div>
<div class="arrow-label">runner.ts &rarr; Agent.run() (within AsyncAgent queue)</div>
</div>
<div class="chain-step phase-complete" data-step="8">
<div class="step-title">Child completes &rarr; <code>waitForIdle()</code> resolves</div>
<div class="step-detail">
Task queue drains. Watcher's cleanup callback fires: sets <code>endedAt</code>, <code>outcome: { status: "ok" }</code>.
Persists updated record to JSON.
</div>
<div class="arrow-label">registry.ts &rarr; cleanup() &rarr; handleRunCompletion()</div>
</div>
<div class="chain-step phase-complete" data-step="9">
<div class="step-title">Announce flow: read child reply &amp; deliver to parent</div>
<div class="step-detail">
<code>readLatestAssistantReply(childSessionId)</code> reads session JSONL, extracts last assistant text.
<code>formatAnnouncementMessage()</code> builds summary with task, status, findings, runtime.
<code>parentAgent.write(message)</code> delivers to parent.
</div>
<div class="arrow-label">announce.ts &rarr; runSubagentAnnounceFlow() &rarr; hub.getAgent(parentId).write()</div>
</div>
<div class="chain-step phase-cleanup" data-step="10">
<div class="step-title">Session cleanup &amp; archive</div>
<div class="step-detail">
If <code>cleanup === "delete"</code>: removes child session directory + closes agent in Hub.
Schedules archive at <code>now + 60min</code>. Sweeper removes from registry after TTL.
</div>
<div class="arrow-label">registry.ts &rarr; deleteChildSession() + sweep()</div>
</div>
</div>
<!-- ══════════════════════════════════════════════════════ -->
<h2>Sequence Diagram</h2>
<!-- ══════════════════════════════════════════════════════ -->
<div class="sequence-container">
<svg class="sequence-diagram" viewBox="0 0 920 620" width="920" height="620">
<style>
.seq-text { font-family: -apple-system, sans-serif; font-size: 12px; fill: #e6edf3; }
.seq-mono { font-family: 'SF Mono', Consolas, monospace; font-size: 11px; }
.seq-label { font-size: 13px; font-weight: 600; }
.seq-line { stroke: #30363d; stroke-width: 1; }
.seq-lifeline { stroke: #30363d; stroke-width: 1; stroke-dasharray: 6 4; }
</style>
<!-- Column headers -->
<rect x="40" y="10" width="130" height="36" rx="6" fill="#161b22" stroke="#58a6ff"/>
<text x="105" y="33" text-anchor="middle" class="seq-text seq-label" fill="#58a6ff">Parent Agent</text>
<rect x="230" y="10" width="130" height="36" rx="6" fill="#161b22" stroke="#d29922"/>
<text x="295" y="33" text-anchor="middle" class="seq-text seq-label" fill="#d29922">sessions_spawn</text>
<rect x="420" y="10" width="100" height="36" rx="6" fill="#161b22" stroke="#bc8cff"/>
<text x="470" y="33" text-anchor="middle" class="seq-text seq-label" fill="#bc8cff">Hub</text>
<rect x="580" y="10" width="120" height="36" rx="6" fill="#161b22" stroke="#d29922"/>
<text x="640" y="33" text-anchor="middle" class="seq-text seq-label" fill="#d29922">Registry</text>
<rect x="760" y="10" width="120" height="36" rx="6" fill="#161b22" stroke="#39d2c0"/>
<text x="820" y="33" text-anchor="middle" class="seq-text seq-label" fill="#39d2c0">Child Agent</text>
<!-- Lifelines -->
<line x1="105" y1="46" x2="105" y2="610" class="seq-lifeline"/>
<line x1="295" y1="46" x2="295" y2="610" class="seq-lifeline"/>
<line x1="470" y1="46" x2="470" y2="610" class="seq-lifeline"/>
<line x1="640" y1="46" x2="640" y2="610" class="seq-lifeline"/>
<line x1="820" y1="46" x2="820" y2="610" class="seq-lifeline"/>
<!-- 1. Parent → Tool: call sessions_spawn -->
<line x1="105" y1="80" x2="290" y2="80" stroke="#58a6ff" stroke-width="1.5" marker-end="url(#seq-arrow-blue)"/>
<text x="198" y="74" text-anchor="middle" class="seq-text seq-mono" fill="#58a6ff">execute({ task, label })</text>
<!-- 2. Tool → Hub: createSubagent -->
<line x1="295" y1="115" x2="465" y2="115" stroke="#bc8cff" stroke-width="1.5" marker-end="url(#seq-arrow-purple)"/>
<text x="380" y="109" text-anchor="middle" class="seq-text seq-mono" fill="#bc8cff">createSubagent(id, opts)</text>
<!-- 3. Hub → Child: new AsyncAgent -->
<line x1="470" y1="150" x2="815" y2="150" stroke="#39d2c0" stroke-width="1.5" marker-end="url(#seq-arrow-cyan)"/>
<text x="640" y="144" text-anchor="middle" class="seq-text seq-mono" fill="#39d2c0">new AsyncAgent({ isSubagent: true })</text>
<!-- 4. Tool → Child: write(task) -->
<line x1="295" y1="190" x2="815" y2="190" stroke="#58a6ff" stroke-width="1.5" marker-end="url(#seq-arrow-blue)"/>
<text x="555" y="184" text-anchor="middle" class="seq-text seq-mono" fill="#58a6ff">childAgent.write(task)</text>
<!-- 5. Tool → Registry: registerSubagentRun -->
<line x1="295" y1="225" x2="635" y2="225" stroke="#d29922" stroke-width="1.5" marker-end="url(#seq-arrow-orange)"/>
<text x="465" y="219" text-anchor="middle" class="seq-text seq-mono" fill="#d29922">registerSubagentRun(params)</text>
<!-- 6. Registry → Child: watchChildAgent (waitForIdle) -->
<line x1="640" y1="260" x2="815" y2="260" stroke="#d29922" stroke-width="1.5" stroke-dasharray="6 3" marker-end="url(#seq-arrow-orange)"/>
<text x="727" y="254" text-anchor="middle" class="seq-text seq-mono" fill="#d29922">waitForIdle() + onClose()</text>
<!-- 7. Tool → Parent: return accepted -->
<line x1="290" y1="295" x2="110" y2="295" stroke="#3fb950" stroke-width="1.5" marker-end="url(#seq-arrow-green)"/>
<text x="200" y="289" text-anchor="middle" class="seq-text seq-mono" fill="#3fb950">{ status: "accepted", runId }</text>
<!-- Async boundary -->
<line x1="20" y1="330" x2="900" y2="330" stroke="#30363d" stroke-width="1" stroke-dasharray="3 3"/>
<rect x="390" y="320" width="140" height="20" rx="4" fill="#161b22" stroke="#30363d"/>
<text x="460" y="335" text-anchor="middle" class="seq-text" fill="#8b949e" style="font-size:11px">async (non-blocking)</text>
<!-- 8. Child processes task -->
<rect x="790" y="355" width="60" height="60" rx="4" fill="rgba(57,210,192,0.1)" stroke="#39d2c0" stroke-dasharray="4 2"/>
<text x="820" y="380" text-anchor="middle" class="seq-text" fill="#39d2c0" style="font-size:10px">LLM</text>
<text x="820" y="395" text-anchor="middle" class="seq-text" fill="#39d2c0" style="font-size:10px">inference</text>
<!-- 9. Child → Registry: waitForIdle resolves -->
<line x1="815" y1="440" x2="645" y2="440" stroke="#3fb950" stroke-width="1.5" stroke-dasharray="6 3" marker-end="url(#seq-arrow-green)"/>
<text x="730" y="434" text-anchor="middle" class="seq-text seq-mono" fill="#3fb950">idle (resolved)</text>
<!-- 10. Registry: handleRunCompletion -->
<rect x="610" y="460" width="60" height="30" rx="4" fill="rgba(210,153,34,0.1)" stroke="#d29922" stroke-dasharray="4 2"/>
<text x="640" y="480" text-anchor="middle" class="seq-text" fill="#d29922" style="font-size:10px">announce</text>
<!-- 11. Registry → Parent: announcement -->
<line x1="635" y1="510" x2="110" y2="510" stroke="#3fb950" stroke-width="1.5" marker-end="url(#seq-arrow-green)"/>
<text x="372" y="504" text-anchor="middle" class="seq-text seq-mono" fill="#3fb950">parentAgent.write(announcement)</text>
<!-- 12. Registry: cleanup -->
<line x1="640" y1="545" x2="815" y2="545" stroke="#f85149" stroke-width="1.5" marker-end="url(#seq-arrow-red)"/>
<text x="727" y="539" text-anchor="middle" class="seq-text seq-mono" fill="#f85149">deleteChildSession()</text>
<!-- 13. Registry: schedule archive -->
<rect x="610" y="565" width="60" height="25" rx="4" fill="rgba(210,153,34,0.1)" stroke="#d29922" stroke-dasharray="4 2"/>
<text x="640" y="582" text-anchor="middle" class="seq-text" fill="#d29922" style="font-size:9px">archive 60m</text>
<!-- Arrow markers -->
<defs>
<marker id="seq-arrow-blue" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#58a6ff"/>
</marker>
<marker id="seq-arrow-green" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#3fb950"/>
</marker>
<marker id="seq-arrow-purple" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#bc8cff"/>
</marker>
<marker id="seq-arrow-orange" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#d29922"/>
</marker>
<marker id="seq-arrow-cyan" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#39d2c0"/>
</marker>
<marker id="seq-arrow-red" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#f85149"/>
</marker>
</defs>
</svg>
</div>
<!-- ══════════════════════════════════════════════════════ -->
<h2>Run State Machine</h2>
<!-- ══════════════════════════════════════════════════════ -->
<div class="state-diagram" style="flex-wrap: wrap; gap: 12px;">
<div class="state-node" style="background:rgba(88,166,255,0.12); border:1px solid var(--accent);">
<svg width="10" height="10"><circle cx="5" cy="5" r="5" fill="#58a6ff"/></svg>
created
</div>
<div class="state-arrow">
<svg width="24" height="12">
<defs>
<marker id="sm-arrow-1" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="5" markerHeight="5" orient="auto">
<path d="M0 0 L10 5 L0 10z" fill="#8b949e"/>
</marker>
</defs>
<path d="M0 6 L20 6" stroke="#8b949e" stroke-width="1.5" marker-end="url(#sm-arrow-1)"/>
</svg>
startedAt
</div>
<div class="state-node" style="background:rgba(188,140,255,0.12); border:1px solid var(--purple);">
<svg width="10" height="10"><circle cx="5" cy="5" r="5" fill="#bc8cff"/></svg>
started
</div>
<div class="state-arrow">
<svg width="24" height="12">
<defs>
<marker id="sm-arrow-2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="5" markerHeight="5" orient="auto">
<path d="M0 0 L10 5 L0 10z" fill="#8b949e"/>
</marker>
</defs>
<path d="M0 6 L20 6" stroke="#8b949e" stroke-width="1.5" marker-end="url(#sm-arrow-2)"/>
</svg>
endedAt
</div>
<div class="state-node" style="background:rgba(63,185,80,0.12); border:1px solid var(--green);">
<svg width="10" height="10"><circle cx="5" cy="5" r="5" fill="#3fb950"/></svg>
ended
</div>
<div class="state-arrow">
<svg width="24" height="12">
<defs>
<marker id="sm-arrow-3" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="5" markerHeight="5" orient="auto">
<path d="M0 0 L10 5 L0 10z" fill="#8b949e"/>
</marker>
</defs>
<path d="M0 6 L20 6" stroke="#8b949e" stroke-width="1.5" marker-end="url(#sm-arrow-3)"/>
</svg>
announce
</div>
<div class="state-node" style="background:rgba(210,153,34,0.12); border:1px solid var(--orange);">
<svg width="10" height="10"><circle cx="5" cy="5" r="5" fill="#d29922"/></svg>
cleanup done
</div>
<div class="state-arrow">
<svg width="24" height="12">
<defs>
<marker id="sm-arrow-4" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="5" markerHeight="5" orient="auto">
<path d="M0 0 L10 5 L0 10z" fill="#8b949e"/>
</marker>
</defs>
<path d="M0 6 L20 6" stroke="#8b949e" stroke-width="1.5" marker-end="url(#sm-arrow-4)"/>
</svg>
60 min
</div>
<div class="state-node" style="background:rgba(139,148,158,0.12); border:1px solid var(--text-muted);">
<svg width="10" height="10"><circle cx="5" cy="5" r="5" fill="#8b949e"/></svg>
archived
</div>
</div>
<div style="margin-top: 20px; padding: 16px 20px; background: var(--surface); border: 1px solid var(--border); border-radius: 10px;">
<div style="font-size: 13px; font-weight: 600; margin-bottom: 8px;">Outcome Status Values</div>
<div style="display: flex; gap: 24px; flex-wrap: wrap; font-size: 12px;">
<span><code style="color:var(--green)">ok</code> &mdash; Task completed normally (waitForIdle resolved)</span>
<span><code style="color:var(--red)">error</code> &mdash; Child agent threw an error</span>
<span><code style="color:var(--orange)">timeout</code> &mdash; Exceeded timeoutSeconds limit</span>
<span><code style="color:var(--text-muted)">unknown</code> &mdash; Process crash or Hub shutdown</span>
</div>
</div>
<!-- ══════════════════════════════════════════════════════ -->
<h2>Module Map</h2>
<!-- ══════════════════════════════════════════════════════ -->
<div class="module-grid">
<div class="module-card" style="border-left: 3px solid var(--cyan);">
<div class="mod-name">src/agent/subagent/types.ts</div>
<div class="mod-desc">Core type definitions</div>
<div class="mod-exports">
<span>export</span> SubagentRunOutcome<br>
<span>export</span> SubagentRunRecord<br>
<span>export</span> RegisterSubagentRunParams<br>
<span>export</span> SubagentAnnounceParams<br>
<span>export</span> SubagentSystemPromptParams
</div>
</div>
<div class="module-card" style="border-left: 3px solid var(--orange);">
<div class="mod-name">src/agent/subagent/registry.ts</div>
<div class="mod-desc">In-memory registry + lifecycle watcher</div>
<div class="mod-exports">
<span>export</span> initSubagentRegistry()<br>
<span>export</span> registerSubagentRun()<br>
<span>export</span> listSubagentRuns()<br>
<span>export</span> releaseSubagentRun()<br>
<span>export</span> getSubagentRun()<br>
<span>export</span> shutdownSubagentRegistry()
</div>
</div>
<div class="module-card" style="border-left: 3px solid var(--text-muted);">
<div class="mod-name">src/agent/subagent/registry-store.ts</div>
<div class="mod-desc">JSON file persistence</div>
<div class="mod-exports">
<span>export</span> loadSubagentRuns()<br>
<span>export</span> saveSubagentRuns()<br>
<span>export</span> getSubagentStorePath()
</div>
</div>
<div class="module-card" style="border-left: 3px solid var(--green);">
<div class="mod-name">src/agent/subagent/announce.ts</div>
<div class="mod-desc">Result propagation child &rarr; parent</div>
<div class="mod-exports">
<span>export</span> buildSubagentSystemPrompt()<br>
<span>export</span> readLatestAssistantReply()<br>
<span>export</span> formatAnnouncementMessage()<br>
<span>export</span> runSubagentAnnounceFlow()
</div>
</div>
<div class="module-card" style="border-left: 3px solid var(--accent);">
<div class="mod-name">src/agent/tools/sessions-spawn.ts</div>
<div class="mod-desc">Tool definition for parent agents</div>
<div class="mod-exports">
<span>export</span> createSessionsSpawnTool()<br>
schema: { task, label?, model?,<br>
&nbsp;&nbsp;cleanup?, timeoutSeconds? }
</div>
</div>
<div class="module-card" style="border-left: 3px solid var(--purple);">
<div class="mod-name">src/hub/hub-singleton.ts</div>
<div class="mod-desc">Global Hub access for tools &amp; registry</div>
<div class="mod-exports">
<span>export</span> setHub(hub)<br>
<span>export</span> getHub()<br>
<span>export</span> isHubInitialized()
</div>
</div>
</div>
<!-- ══════════════════════════════════════════════════════ -->
<h2>Key Design Decisions</h2>
<!-- ══════════════════════════════════════════════════════ -->
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 16px;">
<div class="module-card">
<div class="mod-name" style="color: var(--accent); font-family: inherit;">waitForIdle() vs stream consumption</div>
<div class="mod-desc" style="font-size: 13px; margin-top: 8px;">
Channel is single-reader. <code>Hub.consumeAgent()</code> already reads the stream for forwarding events.
Registry uses <code>waitForIdle()</code> (promise on internal task queue) to detect completion without competing for the stream.
</div>
</div>
<div class="module-card">
<div class="mod-name" style="color: var(--accent); font-family: inherit;">Singleton Hub access</div>
<div class="mod-desc" style="font-size: 13px; margin-top: 8px;">
Tools and registry modules cannot receive Hub via constructor injection (tools are created before Hub exists).
A module-level singleton (<code>setHub</code>/<code>getHub</code>) bridges this gap, with <code>isHubInitialized()</code> guard for test safety.
</div>
</div>
<div class="module-card">
<div class="mod-name" style="color: var(--accent); font-family: inherit;">Crash recovery</div>
<div class="mod-desc" style="font-size: 13px; margin-top: 8px;">
Runs are persisted to JSON after every state change. On restart, <code>initSubagentRegistry()</code>
loads persisted runs: completed-but-unannounced runs trigger announce flow; unfinished runs are marked as
<code>status: "unknown"</code>.
</div>
</div>
<div class="module-card">
<div class="mod-name" style="color: var(--accent); font-family: inherit;">Subagent isolation</div>
<div class="mod-desc" style="font-size: 13px; margin-top: 8px;">
Child agents have <code>isSubagent: true</code> which applies tool deny-list (blocks <code>sessions_spawn</code>).
System prompt explicitly forbids nested spawning, direct user communication, and off-topic work.
Sessions are ephemeral &mdash; deleted after announce unless <code>cleanup: "keep"</code>.
</div>
</div>
</div>
<div style="text-align: center; color: var(--text-muted); font-size: 12px; margin-top: 48px; padding-top: 24px; border-top: 1px solid var(--border);">
Super Multica &mdash; Subagent Orchestration System &mdash; Branch: subagent-orchestration
</div>
</div>
</body>
</html>