let-them-talk 3.7.0 → 3.9.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.
- package/CHANGELOG.md +59 -0
- package/README.md +3 -3
- package/cli.js +1 -1
- package/dashboard.html +7480 -7399
- package/dashboard.js +8 -3
- package/office/animation.js +1 -0
- package/office/campus-env.js +1 -1
- package/office/environment.js +60 -67
- package/office/index.js +50 -0
- package/office/monitors.js +2 -2
- package/office/player.js +436 -0
- package/office/spectator-camera.js +30 -21
- package/package.json +1 -1
- package/server.js +432 -39
package/dashboard.js
CHANGED
|
@@ -284,17 +284,22 @@ function apiStats(query) {
|
|
|
284
284
|
const history = readJsonl(filePath('history.jsonl', projectPath));
|
|
285
285
|
const agents = readJson(filePath('agents.json', projectPath));
|
|
286
286
|
|
|
287
|
-
// Per-agent stats
|
|
287
|
+
// Per-agent stats — only count messages from agents still in agents.json
|
|
288
288
|
const perAgent = {};
|
|
289
|
-
|
|
289
|
+
const knownAgentNames = new Set(Object.keys(agents));
|
|
290
|
+
knownAgentNames.add('__system__');
|
|
291
|
+
knownAgentNames.add('Dashboard');
|
|
292
|
+
let totalMessages = 0;
|
|
290
293
|
const hourBuckets = new Array(24).fill(0);
|
|
291
294
|
|
|
292
295
|
for (let i = 0; i < history.length; i++) {
|
|
293
296
|
const m = history[i];
|
|
294
297
|
const from = m.from || 'unknown';
|
|
298
|
+
if (!knownAgentNames.has(from)) continue; // skip removed agents
|
|
295
299
|
if (!perAgent[from]) {
|
|
296
300
|
perAgent[from] = { messages: 0, responseTimes: [], hours: new Array(24).fill(0) };
|
|
297
301
|
}
|
|
302
|
+
totalMessages++;
|
|
298
303
|
perAgent[from].messages++;
|
|
299
304
|
const ts = new Date(m.timestamp);
|
|
300
305
|
const hour = ts.getHours();
|
|
@@ -313,7 +318,7 @@ function apiStats(query) {
|
|
|
313
318
|
}
|
|
314
319
|
}
|
|
315
320
|
|
|
316
|
-
// Build per-agent summary
|
|
321
|
+
// Build per-agent summary — only include agents currently in agents.json
|
|
317
322
|
const agentStats = {};
|
|
318
323
|
let busiestAgent = null;
|
|
319
324
|
let busiestCount = 0;
|
package/office/animation.js
CHANGED
|
@@ -368,4 +368,5 @@ function disposeAgent(agent) {
|
|
|
368
368
|
if (agent.parts.zzzObjects) {
|
|
369
369
|
agent.parts.zzzObjects.forEach(function(z) { if (z.div.parentElement) z.div.remove(); });
|
|
370
370
|
}
|
|
371
|
+
if (agent._listenLostDiv && agent._listenLostDiv.parentElement) agent._listenLostDiv.remove();
|
|
371
372
|
}
|
package/office/campus-env.js
CHANGED
|
@@ -468,7 +468,7 @@ function buildLobby(marbleMat, chromeMat, goldMat, walnutMat) {
|
|
|
468
468
|
tvFrame.castShadow = true;
|
|
469
469
|
group.add(tvFrame);
|
|
470
470
|
// Animated canvas
|
|
471
|
-
var tvW =
|
|
471
|
+
var tvW = 960, tvH = 600;
|
|
472
472
|
var tvCvs = document.createElement('canvas');
|
|
473
473
|
tvCvs.width = tvW; tvCvs.height = tvH;
|
|
474
474
|
var tvTex = new THREE.CanvasTexture(tvCvs);
|
package/office/environment.js
CHANGED
|
@@ -425,94 +425,87 @@ export function updateTVScreen(time) {
|
|
|
425
425
|
var cvs = tv.canvas, ctx = cvs.getContext('2d');
|
|
426
426
|
var W = cvs.width, H = cvs.height;
|
|
427
427
|
|
|
428
|
-
//
|
|
428
|
+
// Clear
|
|
429
429
|
ctx.fillStyle = '#0a0e1a';
|
|
430
430
|
ctx.fillRect(0, 0, W, H);
|
|
431
431
|
|
|
432
432
|
// Top bar
|
|
433
433
|
ctx.fillStyle = '#111830';
|
|
434
|
-
ctx.fillRect(0, 0, W,
|
|
434
|
+
ctx.fillRect(0, 0, W, 40);
|
|
435
435
|
ctx.fillStyle = '#58a6ff';
|
|
436
|
-
ctx.font = 'bold
|
|
437
|
-
ctx.fillText('OFFICE DASHBOARD',
|
|
438
|
-
// Clock
|
|
436
|
+
ctx.font = 'bold 20px monospace';
|
|
437
|
+
ctx.fillText('OFFICE DASHBOARD', 16, 27);
|
|
439
438
|
var now = new Date();
|
|
440
439
|
var timeStr = now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0') + ':' + now.getSeconds().toString().padStart(2, '0');
|
|
441
440
|
ctx.fillStyle = '#7ee787';
|
|
442
441
|
ctx.textAlign = 'right';
|
|
443
|
-
ctx.fillText(timeStr, W -
|
|
442
|
+
ctx.fillText(timeStr, W - 16, 27);
|
|
444
443
|
ctx.textAlign = 'left';
|
|
445
444
|
|
|
446
|
-
//
|
|
445
|
+
// Data
|
|
447
446
|
var agents = window.cachedAgents || {};
|
|
448
447
|
var history = window.cachedHistory || [];
|
|
449
448
|
var agentNames = Object.keys(agents);
|
|
450
449
|
var activeCount = 0, sleepCount = 0;
|
|
451
450
|
agentNames.forEach(function(n) {
|
|
452
|
-
|
|
453
|
-
if (
|
|
454
|
-
else if (st === 'sleeping') sleepCount++;
|
|
451
|
+
if (agents[n].status === 'active') activeCount++;
|
|
452
|
+
else if (agents[n].status === 'sleeping') sleepCount++;
|
|
455
453
|
});
|
|
456
454
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
// Stats
|
|
461
|
-
ctx.fillStyle = '#546178';
|
|
462
|
-
ctx.fillText(
|
|
463
|
-
ctx.fillStyle = '#
|
|
464
|
-
ctx.fillText(
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
ctx.
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
ctx.
|
|
474
|
-
ctx.
|
|
475
|
-
y +=
|
|
476
|
-
|
|
477
|
-
//
|
|
478
|
-
ctx.
|
|
479
|
-
ctx.
|
|
480
|
-
y +=
|
|
481
|
-
|
|
482
|
-
//
|
|
483
|
-
ctx.
|
|
484
|
-
|
|
485
|
-
ctx.fillText('RECENT ACTIVITY', 8, y);
|
|
486
|
-
y += 14;
|
|
487
|
-
|
|
488
|
-
var recentMsgs = history.slice(-5);
|
|
455
|
+
var y = 68;
|
|
456
|
+
ctx.font = '16px monospace';
|
|
457
|
+
|
|
458
|
+
// Stats row 1
|
|
459
|
+
ctx.fillStyle = '#546178'; ctx.fillText('AGENTS', 16, y);
|
|
460
|
+
ctx.fillStyle = '#d2a8ff'; ctx.fillText(String(agentNames.length), 110, y);
|
|
461
|
+
ctx.fillStyle = '#4ade80'; ctx.fillText(activeCount + ' active', 140, y);
|
|
462
|
+
ctx.fillStyle = '#facc15'; ctx.fillText(sleepCount + ' idle', 280, y);
|
|
463
|
+
y += 26;
|
|
464
|
+
|
|
465
|
+
// Stats row 2
|
|
466
|
+
ctx.fillStyle = '#546178'; ctx.fillText('MESSAGES', 16, y);
|
|
467
|
+
ctx.fillStyle = '#79c0ff'; ctx.fillText(String(history.length), 130, y);
|
|
468
|
+
y += 26;
|
|
469
|
+
|
|
470
|
+
// Separator
|
|
471
|
+
ctx.strokeStyle = '#1a2744'; ctx.lineWidth = 1;
|
|
472
|
+
ctx.beginPath(); ctx.moveTo(16, y); ctx.lineTo(W - 16, y); ctx.stroke();
|
|
473
|
+
y += 20;
|
|
474
|
+
|
|
475
|
+
// Activity header
|
|
476
|
+
ctx.fillStyle = '#546178'; ctx.font = '14px monospace';
|
|
477
|
+
ctx.fillText('RECENT ACTIVITY', 16, y);
|
|
478
|
+
y += 22;
|
|
479
|
+
|
|
480
|
+
// Messages
|
|
481
|
+
ctx.font = '13px monospace';
|
|
482
|
+
var recentMsgs = history.slice(-7);
|
|
489
483
|
for (var i = 0; i < recentMsgs.length; i++) {
|
|
484
|
+
if (y > H - 50) break;
|
|
490
485
|
var msg = recentMsgs[i];
|
|
491
486
|
var from = msg.from || '?';
|
|
492
|
-
var
|
|
493
|
-
var
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
ctx.
|
|
499
|
-
|
|
500
|
-
ctx.
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
487
|
+
var to2 = msg.to || 'all';
|
|
488
|
+
var content = msg.content || msg.message || '';
|
|
489
|
+
var maxLen = Math.floor((W - 40) / 7.5);
|
|
490
|
+
var snippet = content.length > maxLen ? content.substring(0, maxLen - 2) + '..' : content;
|
|
491
|
+
|
|
492
|
+
// From > To
|
|
493
|
+
ctx.fillStyle = '#7ee787'; ctx.fillText(from, 16, y);
|
|
494
|
+
var fromW = ctx.measureText(from).width;
|
|
495
|
+
ctx.fillStyle = '#546178'; ctx.fillText(' > ', 16 + fromW, y);
|
|
496
|
+
ctx.fillStyle = '#d2a8ff'; ctx.fillText(to2, 16 + fromW + ctx.measureText(' > ').width, y);
|
|
497
|
+
y += 18;
|
|
498
|
+
// Snippet
|
|
499
|
+
ctx.fillStyle = '#8892b0'; ctx.fillText(' ' + snippet, 16, y);
|
|
500
|
+
y += 22;
|
|
506
501
|
}
|
|
507
502
|
if (recentMsgs.length === 0) {
|
|
508
|
-
ctx.fillStyle = '#3d4663';
|
|
509
|
-
ctx.fillText(' Waiting for messages...', 8, y);
|
|
503
|
+
ctx.fillStyle = '#3d4663'; ctx.fillText(' Waiting for messages...', 16, y);
|
|
510
504
|
}
|
|
511
505
|
|
|
512
|
-
// Bottom ticker
|
|
506
|
+
// Bottom ticker
|
|
513
507
|
ctx.fillStyle = '#111830';
|
|
514
|
-
ctx.fillRect(0, H -
|
|
515
|
-
// Scrolling ticker
|
|
508
|
+
ctx.fillRect(0, H - 32, W, 32);
|
|
516
509
|
var tickerParts = [];
|
|
517
510
|
agentNames.forEach(function(n) {
|
|
518
511
|
var info = agents[n];
|
|
@@ -520,13 +513,13 @@ export function updateTVScreen(time) {
|
|
|
520
513
|
tickerParts.push(st + ' ' + (info.display_name || n));
|
|
521
514
|
});
|
|
522
515
|
var tickerText = tickerParts.length > 0 ? tickerParts.join(' \u2022 ') + ' \u2022 ' : 'No agents online';
|
|
523
|
-
|
|
516
|
+
ctx.font = '15px monospace';
|
|
517
|
+
var charW = 9;
|
|
518
|
+
tv.tickerOffset = (tv.tickerOffset + 1.5) % (tickerText.length * charW);
|
|
524
519
|
ctx.fillStyle = '#58a6ff';
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
ctx.fillText(tickerText, -tv.tickerOffset, H - 6);
|
|
529
|
-
ctx.fillText(tickerText, -tv.tickerOffset + fullW, H - 6);
|
|
520
|
+
var fullTW = tickerText.length * charW;
|
|
521
|
+
ctx.fillText(tickerText, -tv.tickerOffset, H - 10);
|
|
522
|
+
ctx.fillText(tickerText, -tv.tickerOffset + fullTW, H - 10);
|
|
530
523
|
|
|
531
524
|
// Scanline overlay
|
|
532
525
|
ctx.fillStyle = 'rgba(0,0,0,0.04)';
|
package/office/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { updateAgent } from './animation.js';
|
|
|
8
8
|
import { syncAgents, processMessages, walkTo, navigateTo, showBubble } from './agents.js';
|
|
9
9
|
// Side-effect: registers window.officeGetAppearance
|
|
10
10
|
import './appearance.js';
|
|
11
|
+
import { spawnPlayer, despawnPlayer, isPlayerMode, updatePlayer, savePlayerAppearance, getPlayerAppearance, getPlayer, invalidateColliders } from './player.js';
|
|
11
12
|
|
|
12
13
|
// Expose createCharacter + resolveAppearance for the character designer (Phase 3)
|
|
13
14
|
export { createCharacter } from './character.js';
|
|
@@ -226,6 +227,11 @@ function animate() {
|
|
|
226
227
|
updateAgent(S.agents3d[name], dt, time);
|
|
227
228
|
}
|
|
228
229
|
|
|
230
|
+
// Player avatar mode
|
|
231
|
+
if (isPlayerMode() && S.controls && S.controls.keys) {
|
|
232
|
+
updatePlayer(dt, time, S.controls.keys);
|
|
233
|
+
}
|
|
234
|
+
|
|
229
235
|
// Hide roof when camera is above ceiling height
|
|
230
236
|
if (S._roofGroup) {
|
|
231
237
|
S._roofGroup.visible = S.camera.position.y < 6.5;
|
|
@@ -244,6 +250,12 @@ function animate() {
|
|
|
244
250
|
if (Math.sqrt(adx * adx + adz * adz) < 3) { shouldOpen = true; break; }
|
|
245
251
|
}
|
|
246
252
|
}
|
|
253
|
+
// Also open for player
|
|
254
|
+
if (!shouldOpen && S._player) {
|
|
255
|
+
var pdx = S._player.pos.x - doorX;
|
|
256
|
+
var pdz = S._player.pos.z - doorZ;
|
|
257
|
+
if (Math.sqrt(pdx * pdx + pdz * pdz) < 3) shouldOpen = true;
|
|
258
|
+
}
|
|
247
259
|
S._managerDoorOpen = shouldOpen ? 1 : 0;
|
|
248
260
|
S._managerDoorLerp += (S._managerDoorOpen - S._managerDoorLerp) * Math.min(1, dt * 4);
|
|
249
261
|
S._managerDoor.position.x = S._managerDoorLerp * 1.3; // slide open to the right
|
|
@@ -345,7 +357,15 @@ window.office3dSetEnvironment = function(env) {
|
|
|
345
357
|
});
|
|
346
358
|
}
|
|
347
359
|
S.agents3d = {};
|
|
360
|
+
S._tvScreen = null;
|
|
361
|
+
S._roofGroup = null;
|
|
362
|
+
S._managerDoor = null;
|
|
363
|
+
S._managerDoorOpen = 0;
|
|
364
|
+
S._managerDoorLerp = 0;
|
|
365
|
+
S._managerOfficePos = null;
|
|
366
|
+
S._campusDeskPositions = null;
|
|
348
367
|
S.lastProcessedMsg = 0;
|
|
368
|
+
invalidateColliders();
|
|
349
369
|
buildEnvironment();
|
|
350
370
|
// syncAgents will recreate all agents with correct desk assignments
|
|
351
371
|
syncAgents();
|
|
@@ -357,6 +377,36 @@ window.office3dSetCamSpeed = function(speed) {
|
|
|
357
377
|
if (S.controls) S.controls.moveSpeed = speed;
|
|
358
378
|
};
|
|
359
379
|
|
|
380
|
+
// Player avatar API
|
|
381
|
+
window.office3dEnterWorld = function() {
|
|
382
|
+
spawnPlayer();
|
|
383
|
+
};
|
|
384
|
+
window.office3dExitWorld = function() {
|
|
385
|
+
despawnPlayer();
|
|
386
|
+
};
|
|
387
|
+
window.office3dIsPlayerMode = function() {
|
|
388
|
+
return isPlayerMode();
|
|
389
|
+
};
|
|
390
|
+
window.office3dSavePlayerAppearance = function(app) {
|
|
391
|
+
savePlayerAppearance(app);
|
|
392
|
+
};
|
|
393
|
+
window.office3dGetPlayerAppearance = function() {
|
|
394
|
+
return getPlayerAppearance();
|
|
395
|
+
};
|
|
396
|
+
window.office3dRebuildPlayer = function(appearance) {
|
|
397
|
+
if (!S._player) return;
|
|
398
|
+
savePlayerAppearance(appearance);
|
|
399
|
+
// Rebuild: despawn and respawn with new appearance
|
|
400
|
+
var pos = { x: S._player.pos.x, z: S._player.pos.z };
|
|
401
|
+
var facing = S._player.facing;
|
|
402
|
+
despawnPlayer();
|
|
403
|
+
var p = spawnPlayer();
|
|
404
|
+
p.pos.x = pos.x;
|
|
405
|
+
p.pos.z = pos.z;
|
|
406
|
+
p.facing = facing;
|
|
407
|
+
p.parts.group.position.set(pos.x, 0, pos.z);
|
|
408
|
+
};
|
|
409
|
+
|
|
360
410
|
// Handle visibility change for 3D mode
|
|
361
411
|
document.addEventListener('visibilitychange', function() {
|
|
362
412
|
if (document.hidden && S.running) {
|
package/office/monitors.js
CHANGED
|
@@ -3,7 +3,7 @@ import { S } from './state.js';
|
|
|
3
3
|
|
|
4
4
|
export function updateMonitorScreen(deskIdx, agentName, time) {
|
|
5
5
|
var desk = S.deskMeshes[deskIdx];
|
|
6
|
-
if (!desk) return;
|
|
6
|
+
if (!desk || !desk.screen) return;
|
|
7
7
|
var W = 256, H = 160;
|
|
8
8
|
if (!S.monitorCanvases[deskIdx]) {
|
|
9
9
|
var cvs = document.createElement('canvas');
|
|
@@ -109,7 +109,7 @@ export function updateMonitorScreen(deskIdx, agentName, time) {
|
|
|
109
109
|
|
|
110
110
|
export function setMonitorDim(deskIdx) {
|
|
111
111
|
var desk = S.deskMeshes[deskIdx];
|
|
112
|
-
if (!desk) return;
|
|
112
|
+
if (!desk || !desk.screen) return;
|
|
113
113
|
if (S.monitorCanvases[deskIdx]) {
|
|
114
114
|
S.monitorCanvases[deskIdx].texture.dispose();
|
|
115
115
|
if (desk.screen.material !== desk.screenMat) desk.screen.material.dispose();
|