create-yonderclaw 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/LICENSE +44 -0
  2. package/README.md +288 -0
  3. package/bin/create-yonderclaw.mjs +43 -0
  4. package/docs/assets/favicon.png +0 -0
  5. package/docs/assets/metaclaw-banner.svg +86 -0
  6. package/docs/assets/qis-logo.png +0 -0
  7. package/docs/assets/yz-favicon.png +0 -0
  8. package/docs/assets/yz-logo.png +0 -0
  9. package/docs/index.html +1155 -0
  10. package/installer/assets/favicon.png +0 -0
  11. package/installer/auto-start.ts +330 -0
  12. package/installer/brand.ts +115 -0
  13. package/installer/core-scaffold.ts +448 -0
  14. package/installer/dashboard-generator.ts +657 -0
  15. package/installer/detect.ts +129 -0
  16. package/installer/index.ts +355 -0
  17. package/installer/module-loader.ts +412 -0
  18. package/installer/modules/boardroom/boardroom/client.ts.txt +201 -0
  19. package/installer/modules/boardroom/boardroom/db.ts.txt +322 -0
  20. package/installer/modules/boardroom/boardroom/meeting-agent.ts.txt +129 -0
  21. package/installer/modules/boardroom/boardroom/meeting-scheduler.ts.txt +194 -0
  22. package/installer/modules/boardroom/boardroom/server.ts.txt +473 -0
  23. package/installer/modules/boardroom/boardroom/start-boardroom.bat.txt +26 -0
  24. package/installer/modules/boardroom/boardroom/summons.ts.txt +76 -0
  25. package/installer/modules/boardroom/boardroom/turn-v2.ts.txt +172 -0
  26. package/installer/modules/boardroom/boardroom/turn.ts.txt +208 -0
  27. package/installer/modules/boardroom/boardroom/types.ts.txt +100 -0
  28. package/installer/modules/boardroom/metaclaw-module.json +35 -0
  29. package/installer/modules/boardroom/scripts/meeting-check.bat.txt +38 -0
  30. package/installer/modules/core/metaclaw-module.json +51 -0
  31. package/installer/modules/core/src/db.ts.txt +277 -0
  32. package/installer/modules/core/src/health-check.ts.txt +128 -0
  33. package/installer/modules/core/src/observability.ts.txt +20 -0
  34. package/installer/modules/core/src/safety.ts.txt +26 -0
  35. package/installer/modules/core/src/scan-capabilities.ts.txt +196 -0
  36. package/installer/modules/core/src/self-improve.ts.txt +48 -0
  37. package/installer/modules/core/src/self-update.ts.txt +345 -0
  38. package/installer/modules/core/src/sync-context.ts.txt +133 -0
  39. package/installer/modules/core/src/tasks.ts.txt +159 -0
  40. package/installer/modules/custom/metaclaw-module.json +15 -0
  41. package/installer/modules/custom/src/agent-custom.ts.txt +100 -0
  42. package/installer/modules/dashboard/metaclaw-module.json +23 -0
  43. package/installer/modules/dashboard/scripts/build-dashboard.cjs.txt +51 -0
  44. package/installer/modules/dashboard/src/update-dashboard.ts.txt +126 -0
  45. package/installer/modules/outreach/metaclaw-module.json +29 -0
  46. package/installer/modules/outreach/src/agent-outreach.ts.txt +193 -0
  47. package/installer/modules/outreach/src/inbox-agent.ts.txt +283 -0
  48. package/installer/modules/outreach/src/morning-report.ts.txt +124 -0
  49. package/installer/modules/research/metaclaw-module.json +15 -0
  50. package/installer/modules/research/src/agent-research.ts.txt +127 -0
  51. package/installer/modules/scheduler/metaclaw-module.json +27 -0
  52. package/installer/modules/scheduler/scripts/agent-cycle.bat.txt +85 -0
  53. package/installer/modules/scheduler/scripts/detect-session.bat.txt +41 -0
  54. package/installer/modules/scheduler/scripts/launch.bat.txt +120 -0
  55. package/installer/modules/scheduler/src/cron-manager.ts.txt +273 -0
  56. package/installer/modules/social/metaclaw-module.json +15 -0
  57. package/installer/modules/social/src/agent-social.ts.txt +110 -0
  58. package/installer/modules/support/metaclaw-module.json +15 -0
  59. package/installer/modules/support/src/agent-support.ts.txt +60 -0
  60. package/installer/modules/swarm/metaclaw-module.json +25 -0
  61. package/installer/modules/swarm/swarm/dht-client.ts.txt +376 -0
  62. package/installer/modules/swarm/swarm/relay-server.ts.txt +348 -0
  63. package/installer/modules/swarm/swarm/swarm-client.ts.txt +303 -0
  64. package/installer/modules/swarm/swarm/types.ts.txt +51 -0
  65. package/installer/modules/voice/metaclaw-module.json +16 -0
  66. package/installer/questionnaire.ts +277 -0
  67. package/installer/research.ts +258 -0
  68. package/installer/scaffold-from-config.ts +270 -0
  69. package/installer/task-generator.ts +324 -0
  70. package/installer/templates/agent-custom.ts.txt +100 -0
  71. package/installer/templates/agent-cycle.bat.txt +19 -0
  72. package/installer/templates/agent-outreach.ts.txt +193 -0
  73. package/installer/templates/agent-research.ts.txt +127 -0
  74. package/installer/templates/agent-social.ts.txt +110 -0
  75. package/installer/templates/agent-support.ts.txt +60 -0
  76. package/installer/templates/build-dashboard.cjs.txt +51 -0
  77. package/installer/templates/cron-manager.ts.txt +273 -0
  78. package/installer/templates/dashboard.html.txt +450 -0
  79. package/installer/templates/db.ts.txt +277 -0
  80. package/installer/templates/detect-session.bat.txt +41 -0
  81. package/installer/templates/health-check.ts.txt +128 -0
  82. package/installer/templates/inbox-agent.ts.txt +283 -0
  83. package/installer/templates/launch.bat.txt +120 -0
  84. package/installer/templates/morning-report.ts.txt +124 -0
  85. package/installer/templates/observability.ts.txt +20 -0
  86. package/installer/templates/safety.ts.txt +26 -0
  87. package/installer/templates/self-improve.ts.txt +48 -0
  88. package/installer/templates/self-update.ts.txt +345 -0
  89. package/installer/templates/state.json.txt +33 -0
  90. package/installer/templates/system-context.json.txt +33 -0
  91. package/installer/templates/update-dashboard.ts.txt +126 -0
  92. package/package.json +31 -0
  93. package/setup.bat +178 -0
@@ -0,0 +1,450 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <meta name="mobile-web-app-capable" content="yes">
7
+ <meta name="apple-mobile-web-app-capable" content="yes">
8
+ <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
9
+ <title>__AGENT_NAME__ Command Center</title>
10
+ <link rel="icon" type="image/png" href="favicon.png">
11
+ <link href="https://fonts.googleapis.com/css2?family=Rajdhani:wght@300;400;500;600;700&family=Orbitron:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@300;400;500&display=swap" rel="stylesheet">
12
+ <style>
13
+ :root {
14
+ --cyan: #00BEEA; --cyan-bright: #00D9FF; --purple: #8B5CF6;
15
+ --green: #10B981; --gold: #F59E0B; --red: #EF4444;
16
+ --bg: #0A0A0A; --border: rgba(0,190,234,0.15);
17
+ --text: #E8F5E9; --text-muted: rgba(232,245,233,0.45);
18
+ }
19
+ * { margin:0; padding:0; box-sizing:border-box; }
20
+ body { background: var(--bg); color: var(--text); font-family: 'Rajdhani', sans-serif; overflow: hidden; height: 100vh; }
21
+ h1,h2,.label { font-family: 'Orbitron', sans-serif; }
22
+ .mono { font-family: 'JetBrains Mono', monospace; font-size: 12px; }
23
+
24
+ /* === OFFICE LAYER (background) === */
25
+ #office {
26
+ position: fixed; inset: 0; z-index: 1;
27
+ background: linear-gradient(180deg, #0d1117 0%, #0a0e14 100%);
28
+ transition: opacity 0.5s;
29
+ }
30
+
31
+ /* Floor grid */
32
+ #office::before {
33
+ content: '';
34
+ position: absolute; bottom: 0; left: 0; right: 0; height: 60%;
35
+ background:
36
+ repeating-linear-gradient(90deg, rgba(0,190,234,0.03) 0px, transparent 1px, transparent 80px),
37
+ repeating-linear-gradient(0deg, rgba(0,190,234,0.03) 0px, transparent 1px, transparent 80px);
38
+ transform: perspective(500px) rotateX(30deg);
39
+ transform-origin: bottom;
40
+ }
41
+
42
+ /* Desk styling */
43
+ .desk {
44
+ position: absolute;
45
+ width: 100px; height: 50px;
46
+ background: rgba(30,40,55,0.8);
47
+ border: 1px solid rgba(0,190,234,0.15);
48
+ border-radius: 4px;
49
+ transition: all 0.5s;
50
+ }
51
+ .desk.active { border-color: rgba(0,190,234,0.4); box-shadow: 0 0 15px rgba(0,190,234,0.1); }
52
+ .desk-label {
53
+ position: absolute; bottom: -18px; left: 0; right: 0; text-align: center;
54
+ font-family: 'JetBrains Mono', monospace; font-size: 9px; color: var(--text-muted);
55
+ letter-spacing: 1px; text-transform: uppercase;
56
+ }
57
+
58
+ /* Agent avatars */
59
+ .agent {
60
+ position: absolute;
61
+ width: 24px; height: 36px;
62
+ transition: all 0.8s ease-in-out;
63
+ opacity: 0;
64
+ }
65
+ .agent.visible { opacity: 1; }
66
+ .agent svg { width: 100%; height: 100%; }
67
+
68
+ /* Typing animation */
69
+ .agent.working::after {
70
+ content: '...';
71
+ position: absolute; top: -12px; left: 50%; transform: translateX(-50%);
72
+ font-family: 'JetBrains Mono', monospace; font-size: 10px; color: var(--cyan);
73
+ animation: typing 1.5s infinite;
74
+ }
75
+ @keyframes typing { 0%{content:'.'} 33%{content:'..'} 66%{content:'...'} }
76
+
77
+ /* Meeting pulse */
78
+ .agent.meeting { animation: meetPulse 2s infinite; }
79
+ @keyframes meetPulse { 0%,100%{filter:brightness(1)} 50%{filter:brightness(1.3)} }
80
+
81
+ /* Boardroom table */
82
+ #boardroom-table {
83
+ position: absolute; top: 15%; left: 50%; transform: translateX(-50%);
84
+ width: 180px; height: 90px;
85
+ background: rgba(20,30,45,0.9);
86
+ border: 1px solid rgba(139,92,246,0.3);
87
+ border-radius: 50%;
88
+ opacity: 0; transition: opacity 0.5s;
89
+ box-shadow: 0 0 30px rgba(139,92,246,0.1);
90
+ }
91
+ #boardroom-table.active { opacity: 1; }
92
+ #boardroom-label {
93
+ position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%);
94
+ font-family: 'Orbitron', sans-serif; font-size: 8px; letter-spacing: 2px;
95
+ color: rgba(139,92,246,0.5); text-transform: uppercase;
96
+ opacity: 0; transition: opacity 0.5s;
97
+ }
98
+ #boardroom-table.active + #boardroom-label,
99
+ #boardroom-table.active ~ #boardroom-label { opacity: 1; }
100
+
101
+ /* Envelope animation for courier */
102
+ .envelope {
103
+ position: absolute; font-size: 14px;
104
+ animation: float 3s ease-in-out infinite;
105
+ opacity: 0; transition: opacity 0.3s;
106
+ }
107
+ .envelope.visible { opacity: 1; }
108
+ @keyframes float { 0%,100%{transform:translateY(0)} 50%{transform:translateY(-5px)} }
109
+
110
+ /* === HUD OVERLAY (foreground) === */
111
+ #hud {
112
+ position: fixed; inset: 0; z-index: 10;
113
+ pointer-events: none;
114
+ display: flex; flex-direction: column;
115
+ }
116
+ #hud > * { pointer-events: auto; }
117
+
118
+ /* Top bar */
119
+ #topbar {
120
+ display: flex; justify-content: space-between; align-items: center;
121
+ padding: 10px 16px;
122
+ background: rgba(10,10,10,0.85);
123
+ backdrop-filter: blur(10px);
124
+ border-bottom: 1px solid var(--border);
125
+ }
126
+ #topbar h1 {
127
+ font-size: 14px; letter-spacing: 2px;
128
+ background: linear-gradient(180deg, #fff, var(--cyan));
129
+ -webkit-background-clip: text; -webkit-text-fill-color: transparent;
130
+ }
131
+
132
+ /* Status dot */
133
+ .dot { display:inline-block; width:8px; height:8px; border-radius:50%; margin-right:6px; animation:pulse 2s infinite; }
134
+ .dot.g { background:var(--green); box-shadow:0 0 6px var(--green); }
135
+ .dot.r { background:var(--red); box-shadow:0 0 6px var(--red); }
136
+ @keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.5} }
137
+
138
+ /* KPI strip */
139
+ #kpis {
140
+ display: flex; gap: 2px; padding: 8px 16px;
141
+ background: rgba(10,10,10,0.7);
142
+ }
143
+ .kpi {
144
+ flex: 1; text-align: center; padding: 8px 4px;
145
+ background: rgba(0,0,0,0.3); border: 1px solid rgba(0,190,234,0.08); border-radius: 4px;
146
+ }
147
+ .kpi-label { font-family:'Orbitron',sans-serif; font-size:8px; letter-spacing:1px; color:var(--text-muted); text-transform:uppercase; }
148
+ .kpi-value { font-family:'Orbitron',sans-serif; font-size:22px; font-weight:700; line-height:1.2; }
149
+
150
+ /* Bottom panel */
151
+ #bottom {
152
+ margin-top: auto;
153
+ background: rgba(10,10,10,0.85);
154
+ backdrop-filter: blur(10px);
155
+ border-top: 1px solid var(--border);
156
+ max-height: 35vh;
157
+ display: flex;
158
+ }
159
+ #activity {
160
+ flex: 1; padding: 10px 16px; overflow-y: auto;
161
+ font-family: 'JetBrains Mono', monospace; font-size: 11px; line-height: 1.8;
162
+ }
163
+ #activity::-webkit-scrollbar { width: 3px; }
164
+ #activity::-webkit-scrollbar-thumb { background: var(--border); }
165
+
166
+ #agents-panel {
167
+ width: 220px; padding: 10px 16px; border-left: 1px solid var(--border);
168
+ overflow-y: auto;
169
+ }
170
+ .agent-row {
171
+ display: flex; align-items: center; gap: 8px; padding: 4px 0;
172
+ border-bottom: 1px solid rgba(255,255,255,0.03);
173
+ }
174
+ .agent-dot { width:6px; height:6px; border-radius:50%; flex-shrink:0; }
175
+ .agent-dot.idle { background: var(--text-muted); }
176
+ .agent-dot.working { background: var(--green); animation: pulse 1.5s infinite; }
177
+ .agent-dot.meeting { background: var(--purple); animation: pulse 1.5s infinite; }
178
+ .agent-dot.sending { background: var(--gold); animation: pulse 1s infinite; }
179
+ .agent-dot.alert { background: var(--red); animation: pulse 0.5s infinite; }
180
+
181
+ .cyan { color: var(--cyan); }
182
+ .green { color: var(--green); }
183
+ .purple { color: var(--purple); }
184
+ .gold { color: var(--gold); }
185
+ .red { color: var(--red); }
186
+ </style>
187
+ </head>
188
+ <body>
189
+
190
+ <!-- OFFICE SCENE -->
191
+ <div id="office">
192
+ <!-- Desks -->
193
+ <div class="desk" id="desk-scout" style="left:15%;top:40%"><div class="desk-label">Scout</div></div>
194
+ <div class="desk" id="desk-writer" style="left:38%;top:40%"><div class="desk-label">Writer</div></div>
195
+ <div class="desk" id="desk-guard" style="left:15%;top:60%"><div class="desk-label">Guard</div></div>
196
+ <div class="desk" id="desk-analyst" style="left:38%;top:60%"><div class="desk-label">Analyst</div></div>
197
+ <div class="desk" id="desk-courier" style="left:62%;top:40%"><div class="desk-label">Courier</div></div>
198
+ <div class="desk" id="desk-chief" style="left:62%;top:60%;width:120px;border-color:rgba(0,190,234,0.3)"><div class="desk-label">Chief</div></div>
199
+
200
+ <!-- Boardroom table (hidden until meeting) -->
201
+ <div id="boardroom-table"></div>
202
+ <div id="boardroom-label">Board Meeting</div>
203
+
204
+ <!-- Agent avatars -->
205
+ <div class="agent" id="agent-scout" data-role="scout">
206
+ <svg viewBox="0 0 24 36"><circle cx="12" cy="8" r="6" fill="#00BEEA" opacity="0.8"/><rect x="6" y="16" width="12" height="16" rx="3" fill="#00BEEA" opacity="0.6"/><circle cx="12" cy="8" r="3" fill="#0A0A0A"/></svg>
207
+ </div>
208
+ <div class="agent" id="agent-writer" data-role="writer">
209
+ <svg viewBox="0 0 24 36"><circle cx="12" cy="8" r="6" fill="#8B5CF6" opacity="0.8"/><rect x="6" y="16" width="12" height="16" rx="3" fill="#8B5CF6" opacity="0.6"/><circle cx="12" cy="8" r="3" fill="#0A0A0A"/></svg>
210
+ </div>
211
+ <div class="agent" id="agent-guard" data-role="guard">
212
+ <svg viewBox="0 0 24 36"><circle cx="12" cy="8" r="6" fill="#EF4444" opacity="0.8"/><rect x="6" y="16" width="12" height="16" rx="3" fill="#EF4444" opacity="0.6"/><circle cx="12" cy="8" r="3" fill="#0A0A0A"/></svg>
213
+ </div>
214
+ <div class="agent" id="agent-analyst" data-role="analyst">
215
+ <svg viewBox="0 0 24 36"><circle cx="12" cy="8" r="6" fill="#F59E0B" opacity="0.8"/><rect x="6" y="16" width="12" height="16" rx="3" fill="#F59E0B" opacity="0.6"/><circle cx="12" cy="8" r="3" fill="#0A0A0A"/></svg>
216
+ </div>
217
+ <div class="agent" id="agent-courier" data-role="courier">
218
+ <svg viewBox="0 0 24 36"><circle cx="12" cy="8" r="6" fill="#10B981" opacity="0.8"/><rect x="6" y="16" width="12" height="16" rx="3" fill="#10B981" opacity="0.6"/><circle cx="12" cy="8" r="3" fill="#0A0A0A"/></svg>
219
+ </div>
220
+ <div class="agent" id="agent-chief" data-role="chief">
221
+ <svg viewBox="0 0 24 36"><circle cx="12" cy="8" r="7" fill="#00D9FF" opacity="0.9"/><rect x="5" y="16" width="14" height="17" rx="3" fill="#00D9FF" opacity="0.7"/><circle cx="12" cy="8" r="3.5" fill="#0A0A0A"/><rect x="8" y="2" width="8" height="3" rx="1" fill="#FFD700" opacity="0.6"/></svg>
222
+ </div>
223
+
224
+ <!-- Courier envelope -->
225
+ <div class="envelope" id="envelope">&#9993;</div>
226
+ </div>
227
+
228
+ <!-- HUD OVERLAY -->
229
+ <div id="hud">
230
+ <div id="topbar">
231
+ <div style="display:flex;align-items:center;gap:10px">
232
+ <img src="favicon.png" alt="YZ" style="height:24px;opacity:0.85">
233
+ <div>
234
+ <h1>__AGENT_NAME__</h1>
235
+ <div class="mono" style="color:var(--text-muted);font-size:10px">__CLAW_TYPE__ &mdash; MetaClaw v3.0</div>
236
+ </div>
237
+ </div>
238
+ <div style="text-align:right">
239
+ <div id="statusDot"><span class="dot g"></span><span class="mono">ONLINE</span></div>
240
+ <div class="mono" style="color:var(--text-muted);font-size:10px" id="lastUpdate">--</div>
241
+ </div>
242
+ </div>
243
+
244
+ <div id="kpis">
245
+ <div class="kpi"><div class="kpi-label">Actions</div><div class="kpi-value cyan" id="kActions">--</div></div>
246
+ <div class="kpi"><div class="kpi-label">Success</div><div class="kpi-value green" id="kSuccess">--</div></div>
247
+ <div class="kpi"><div class="kpi-label">Cost</div><div class="kpi-value purple" id="kCost">--</div></div>
248
+ <div class="kpi"><div class="kpi-label">Circuit</div><div class="kpi-value green" id="kCircuit">--</div></div>
249
+ <div class="kpi"><div class="kpi-label">Prompt</div><div class="kpi-value gold" id="kPrompt">--</div></div>
250
+ </div>
251
+
252
+ <div id="bottom">
253
+ <div id="activity">
254
+ <div style="color:var(--text-muted);font-size:10px;letter-spacing:1px;font-family:'Orbitron',sans-serif;margin-bottom:6px">ACTIVITY FEED</div>
255
+ <div id="activityFeed">Waiting for data...</div>
256
+ </div>
257
+ <div id="agents-panel">
258
+ <div style="color:var(--text-muted);font-size:10px;letter-spacing:1px;font-family:'Orbitron',sans-serif;margin-bottom:6px">BOARD MEMBERS</div>
259
+ <div class="agent-row"><div class="agent-dot idle" id="dot-chief"></div><span class="mono" style="color:var(--cyan)">Chief</span><span class="mono" style="color:var(--text-muted);margin-left:auto" id="state-chief">idle</span></div>
260
+ <div class="agent-row"><div class="agent-dot idle" id="dot-scout"></div><span class="mono">Scout</span><span class="mono" style="color:var(--text-muted);margin-left:auto" id="state-scout">idle</span></div>
261
+ <div class="agent-row"><div class="agent-dot idle" id="dot-writer"></div><span class="mono">Writer</span><span class="mono" style="color:var(--text-muted);margin-left:auto" id="state-writer">idle</span></div>
262
+ <div class="agent-row"><div class="agent-dot idle" id="dot-guard"></div><span class="mono">Guard</span><span class="mono" style="color:var(--text-muted);margin-left:auto" id="state-guard">idle</span></div>
263
+ <div class="agent-row"><div class="agent-dot idle" id="dot-analyst"></div><span class="mono">Analyst</span><span class="mono" style="color:var(--text-muted);margin-left:auto" id="state-analyst">idle</span></div>
264
+ <div class="agent-row"><div class="agent-dot idle" id="dot-courier"></div><span class="mono">Courier</span><span class="mono" style="color:var(--text-muted);margin-left:auto" id="state-courier">idle</span></div>
265
+ </div>
266
+ </div>
267
+ </div>
268
+
269
+ <script>
270
+ const DATA_URL = 'data/dashboard.json';
271
+ const AGENTS = ['chief','scout','writer','guard','analyst','courier'];
272
+
273
+ // Desk positions (where agents sit when working)
274
+ const DESK_POS = {
275
+ scout: { left:'17%', top:'37%' },
276
+ writer: { left:'40%', top:'37%' },
277
+ guard: { left:'17%', top:'57%' },
278
+ analyst: { left:'40%', top:'57%' },
279
+ courier: { left:'64%', top:'37%' },
280
+ chief: { left:'66%', top:'57%' },
281
+ };
282
+
283
+ // Meeting positions (around the boardroom table)
284
+ const MEETING_POS = {
285
+ chief: { left:'48%', top:'10%' },
286
+ scout: { left:'38%', top:'14%' },
287
+ writer: { left:'58%', top:'14%' },
288
+ guard: { left:'35%', top:'22%' },
289
+ analyst: { left:'61%', top:'22%' },
290
+ courier: { left:'48%', top:'26%' },
291
+ };
292
+
293
+ let currentStates = {};
294
+
295
+ function setAgentState(id, state) {
296
+ const el = document.getElementById('agent-' + id);
297
+ const dot = document.getElementById('dot-' + id);
298
+ const stateEl = document.getElementById('state-' + id);
299
+ const desk = document.getElementById('desk-' + id);
300
+ if (!el) return;
301
+
302
+ // Remove old classes
303
+ el.classList.remove('visible','working','meeting');
304
+ if (desk) desk.classList.remove('active');
305
+
306
+ currentStates[id] = state;
307
+
308
+ if (state === 'idle') {
309
+ el.classList.remove('visible');
310
+ dot.className = 'agent-dot idle';
311
+ stateEl.textContent = 'idle';
312
+ stateEl.style.color = 'var(--text-muted)';
313
+ } else if (state === 'working' || state === 'researching') {
314
+ el.classList.add('visible','working');
315
+ el.style.left = DESK_POS[id].left;
316
+ el.style.top = DESK_POS[id].top;
317
+ if (desk) desk.classList.add('active');
318
+ dot.className = 'agent-dot working';
319
+ stateEl.textContent = state === 'researching' ? 'researching' : 'working';
320
+ stateEl.style.color = 'var(--green)';
321
+ } else if (state === 'meeting') {
322
+ el.classList.add('visible','meeting');
323
+ el.style.left = MEETING_POS[id].left;
324
+ el.style.top = MEETING_POS[id].top;
325
+ dot.className = 'agent-dot meeting';
326
+ stateEl.textContent = 'meeting';
327
+ stateEl.style.color = 'var(--purple)';
328
+ } else if (state === 'sending') {
329
+ el.classList.add('visible','working');
330
+ el.style.left = DESK_POS[id].left;
331
+ el.style.top = DESK_POS[id].top;
332
+ dot.className = 'agent-dot sending';
333
+ stateEl.textContent = 'sending';
334
+ stateEl.style.color = 'var(--gold)';
335
+ } else if (state === 'alert') {
336
+ el.classList.add('visible');
337
+ el.style.left = DESK_POS[id].left;
338
+ el.style.top = DESK_POS[id].top;
339
+ if (desk) desk.classList.add('active');
340
+ dot.className = 'agent-dot alert';
341
+ stateEl.textContent = 'ALERT';
342
+ stateEl.style.color = 'var(--red)';
343
+ }
344
+
345
+ // Boardroom table visibility
346
+ const anyMeeting = Object.values(currentStates).some(s => s === 'meeting');
347
+ document.getElementById('boardroom-table').classList.toggle('active', anyMeeting);
348
+ }
349
+
350
+ // Determine agent states from dashboard data
351
+ function deriveAgentStates(d) {
352
+ const m = d.today_metrics || {};
353
+ const cb = d.circuit_breaker || {};
354
+ const actions = d.recent_actions || [];
355
+
356
+ // Default all to idle
357
+ const states = { chief:'idle', scout:'idle', writer:'idle', guard:'idle', analyst:'idle', courier:'idle' };
358
+
359
+ // Chief is always working if there are any actions today
360
+ if (m.actions_taken > 0) states.chief = 'working';
361
+
362
+ // Guard alerts on circuit breaker open
363
+ if (cb.state === 'open') states.guard = 'alert';
364
+ else if (m.actions_taken > 0) states.guard = 'working';
365
+
366
+ // Check recent actions to determine who's active
367
+ if (actions.length > 0) {
368
+ const recent = actions[0];
369
+ const age = Date.now() - new Date(recent.created_at).getTime();
370
+ const isRecent = age < 300000; // 5 minutes
371
+
372
+ if (isRecent) {
373
+ const type = recent.action_type || '';
374
+ if (type.includes('research') || type.includes('discover') || type.includes('search')) states.scout = 'researching';
375
+ if (type.includes('draft') || type.includes('write') || type.includes('content')) states.writer = 'working';
376
+ if (type.includes('send') || type.includes('email') || type.includes('outreach')) states.courier = 'sending';
377
+ if (type.includes('optimize') || type.includes('self_improve') || type.includes('update')) states.analyst = 'working';
378
+ }
379
+ }
380
+
381
+ // If self-improvement is running, everyone goes to meeting
382
+ if (d.self_improvement && d.self_improvement.last_update) {
383
+ const lastUpdate = new Date(d.self_improvement.last_update).getTime();
384
+ const age = Date.now() - lastUpdate;
385
+ if (age < 60000) { // Within last minute = meeting just happened
386
+ for (const k of AGENTS) states[k] = 'meeting';
387
+ }
388
+ }
389
+
390
+ return states;
391
+ }
392
+
393
+ async function loadDashboard() {
394
+ try {
395
+ const resp = await fetch(DATA_URL + '?t=' + Date.now());
396
+ if (!resp.ok) throw new Error('No data');
397
+ const d = await resp.json();
398
+
399
+ // Update HUD
400
+ document.getElementById('lastUpdate').textContent = d.generated_at || '--';
401
+ const m = d.today_metrics || {};
402
+ document.getElementById('kActions').textContent = m.actions_taken || 0;
403
+ const rate = m.actions_taken > 0 ? ((m.actions_succeeded||0) / m.actions_taken * 100).toFixed(0) : '100';
404
+ document.getElementById('kSuccess').textContent = rate + '%';
405
+ document.getElementById('kSuccess').className = 'kpi-value ' + (parseInt(rate) >= 80 ? 'green' : 'gold');
406
+ document.getElementById('kCost').textContent = '$' + (m.total_cost_usd || 0).toFixed(2);
407
+ const cb = d.circuit_breaker || {};
408
+ document.getElementById('kCircuit').textContent = (cb.state || 'closed').toUpperCase();
409
+ document.getElementById('kCircuit').className = 'kpi-value ' + (cb.state === 'open' ? 'red' : 'green');
410
+ if (d.prompt_version) document.getElementById('kPrompt').textContent = 'v' + d.prompt_version.version;
411
+
412
+ // Status dot
413
+ const sd = document.getElementById('statusDot');
414
+ sd.innerHTML = cb.state === 'open'
415
+ ? '<span class="dot r"></span><span class="mono">CIRCUIT OPEN</span>'
416
+ : '<span class="dot g"></span><span class="mono">ONLINE</span>';
417
+
418
+ // Activity feed
419
+ const actions = d.recent_actions || [];
420
+ if (actions.length > 0) {
421
+ document.getElementById('activityFeed').innerHTML = actions.slice(0,15).map(a =>
422
+ '<div style="padding:1px 0;border-bottom:1px solid rgba(255,255,255,0.03)">' +
423
+ '<span style="color:var(--text-muted)">' + (a.created_at||'').slice(11,16) + '</span> ' +
424
+ '<span style="color:' + (a.status==='success'?'var(--green)':'var(--red)') + '">' + (a.action_type||'') + '</span> ' +
425
+ '<span style="color:var(--text-muted)">' + ((a.target||'').length > 25 ? (a.target||'').slice(0,25)+'...' : (a.target||'')) + '</span>' +
426
+ '</div>'
427
+ ).join('');
428
+ }
429
+
430
+ // Agent states
431
+ const states = deriveAgentStates(d);
432
+ for (const [id, state] of Object.entries(states)) {
433
+ setAgentState(id, state);
434
+ }
435
+
436
+ } catch (e) {
437
+ document.getElementById('activityFeed').innerHTML = '<span style="color:var(--text-muted)">Waiting for data... Run: npm run dashboard</span>';
438
+ // Show chief as idle, everyone else hidden
439
+ setAgentState('chief', 'working');
440
+ }
441
+ }
442
+
443
+ // Ambient: chief always visible at desk even with no data
444
+ setAgentState('chief', 'working');
445
+
446
+ loadDashboard();
447
+ setInterval(loadDashboard, 15000);
448
+ </script>
449
+ </body>
450
+ </html>