let-them-talk 3.6.1 → 3.7.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 +46 -0
- package/README.md +86 -2
- package/cli.js +1 -1
- package/dashboard.html +12 -2
- package/office/agents.js +94 -17
- package/office/animation.js +35 -1
- package/office/campus-env.js +1483 -0
- package/office/environment.js +10 -0
- package/office/index.js +46 -16
- package/office/monitors.js +10 -1
- package/office/navigation.js +263 -0
- package/office/scene.js +3 -3
- package/office/spectator-camera.js +1 -1
- package/office/state.js +1 -1
- package/package.json +1 -1
- package/server.js +928 -23
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,51 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [3.7.0] - 2026-03-16
|
|
4
|
+
|
|
5
|
+
### Added — Agent Ecosystem (20 new tools, 52 total)
|
|
6
|
+
|
|
7
|
+
**Tier 1 — Critical Infrastructure:**
|
|
8
|
+
- **`get_briefing()`** — full project onboarding in one call: agents, tasks, decisions, KB, locked files, progress, project file tree
|
|
9
|
+
- **`lock_file(path)` / `unlock_file(path?)`** — exclusive file editing with auto-release on agent death
|
|
10
|
+
- **`log_decision(decision, reasoning?, topic?)` / `get_decisions(topic?)`** — persistent decision log, prevents re-debating
|
|
11
|
+
- **Agent recovery on rejoin** — `register()` returns active tasks, workspace keys, recent messages for returning agents
|
|
12
|
+
|
|
13
|
+
**Tier 2 — Quality of Life:**
|
|
14
|
+
- **`kb_write(key, content)` / `kb_read(key?)` / `kb_list()`** — shared team knowledge base (any agent reads/writes)
|
|
15
|
+
- **Event hooks** — auto-fires system messages on `agent_join`, `task_complete`, `all_tasks_done`, `dependency_met`
|
|
16
|
+
- **`update_progress(feature, percent, notes)` / `get_progress()`** — feature-level progress tracking with overall %
|
|
17
|
+
- **`get_compressed_history()`** — auto-compresses old messages into summary segments, keeps recent verbatim
|
|
18
|
+
- **`listen_group()` now blocks indefinitely** — no more timeout, agents never drop out
|
|
19
|
+
|
|
20
|
+
**Tier 3 — Advanced Collaboration:**
|
|
21
|
+
- **`call_vote(question, options)` / `cast_vote(vote_id, choice)` / `vote_status(vote_id?)`** — team voting with auto-resolve when all vote
|
|
22
|
+
- **`request_review(file, desc)` / `submit_review(review_id, status, feedback)`** — code review pipeline with approve/changes_requested
|
|
23
|
+
- **`declare_dependency(task_id, depends_on)` / `check_dependencies(task_id?)`** — task dependency tracking with auto-notify on resolve
|
|
24
|
+
- **`get_reputation(agent?)` / `suggest_task()`** — agent reputation tracking (auto-detects strengths), task suggestions based on skills
|
|
25
|
+
- **Auto-reputation tracking** — global hook tracks every action (messages, tasks, reviews, decisions, KB writes) without manual calls
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- **Monitor screens stay red** when agent stops listening — persistent color state instead of 300ms flash
|
|
29
|
+
- **"NOT LISTENING" warning** shown prominently on desk monitor canvas
|
|
30
|
+
- **Status color logic** — green = listening, red = active but not listening, yellow = sleeping, dim = dead
|
|
31
|
+
|
|
32
|
+
## [3.6.2] - 2026-03-16
|
|
33
|
+
|
|
34
|
+
### Added — Message Awareness System
|
|
35
|
+
- **Sender gets busy status** — `send_message` and `broadcast` tell you when recipients are working (not listening) so you know messages are queued
|
|
36
|
+
- **Pending message nudge** — every non-listen tool call checks for unread messages and tells the agent to call `listen_group()` soon
|
|
37
|
+
- **Message age tracking** — `listen_group` shows `age_seconds` per message and `delayed: true` flag for messages older than 30s
|
|
38
|
+
- **Agent status in batch** — `listen_group` returns `agents_status` map showing who is `listening` vs `working`
|
|
39
|
+
- **listen_group retry** — timeout now returns `retry: true` with explicit instruction to call again immediately
|
|
40
|
+
- **next_action field** — successful `listen_group` response tells agent to call `listen_group()` again after responding
|
|
41
|
+
- **Ctrl key removed from camera** — no longer moves camera down (Q/E only)
|
|
42
|
+
|
|
43
|
+
### Added — 3D World: Campus Environment & Navigation
|
|
44
|
+
- **Campus environment** — new outdoor environment option with buildings, paths, green spaces
|
|
45
|
+
- **Navigation system** — pathfinding for agents to walk around obstacles instead of through walls
|
|
46
|
+
- **Door animations** — manager office door slides open when agents approach, closes when they leave
|
|
47
|
+
- **Roof visibility** — roof hides when camera is above ceiling height
|
|
48
|
+
|
|
3
49
|
## [3.6.1] - 2026-03-16
|
|
4
50
|
|
|
5
51
|
### Fixed
|
package/README.md
CHANGED
|
@@ -86,7 +86,7 @@ Each terminal spawns its own MCP server process. All processes share a `.agent-b
|
|
|
86
86
|
|
|
87
87
|
- **3D virtual office** — chibi characters at desks, spectator camera (WASD+mouse), 11 hairstyles, 6 outfits, gestures, furniture, TV dashboard
|
|
88
88
|
- **Managed conversation mode** — structured turn-taking with floor control for 3+ agents, prevents broadcast storms
|
|
89
|
-
- **
|
|
89
|
+
- **52 MCP tools** — messaging, tasks, workflows, profiles, workspaces, branching, managed mode, briefing, file locking, decisions, KB, voting, reviews, dependencies, reputation
|
|
90
90
|
- **8-tab dashboard** — 3D Hub (default), messages, tasks, workspaces, workflows, launch, stats, docs
|
|
91
91
|
- **Group conversation mode** — free multi-agent collaboration with auto-broadcast and cooldown
|
|
92
92
|
- **5 agent templates** — pair, team, review, debate, managed — with ready-to-paste prompts
|
|
@@ -175,7 +175,7 @@ The dashboard's default view is a **real-time 3D virtual office** (the "3D Hub")
|
|
|
175
175
|
|
|
176
176
|
**Animations:** walk, sit, type, raise hand, sleep (ZZZ), wave, think, point, celebrate, stretch, idle gestures. Agents turn toward speakers during conversations.
|
|
177
177
|
|
|
178
|
-
## MCP Tools (
|
|
178
|
+
## MCP Tools (52)
|
|
179
179
|
|
|
180
180
|
<details>
|
|
181
181
|
<summary><strong>Messaging (13 tools)</strong></summary>
|
|
@@ -249,6 +249,90 @@ The dashboard's default view is a **real-time 3D virtual office** (the "3D Hub")
|
|
|
249
249
|
|
|
250
250
|
</details>
|
|
251
251
|
|
|
252
|
+
<details>
|
|
253
|
+
<summary><strong>Briefing & Recovery (1 tool)</strong></summary>
|
|
254
|
+
|
|
255
|
+
| Tool | Description |
|
|
256
|
+
|------|-------------|
|
|
257
|
+
| `get_briefing` | Full project onboarding — agents, tasks, decisions, KB, locks, progress, files |
|
|
258
|
+
|
|
259
|
+
</details>
|
|
260
|
+
|
|
261
|
+
<details>
|
|
262
|
+
<summary><strong>File Locking (2 tools)</strong></summary>
|
|
263
|
+
|
|
264
|
+
| Tool | Description |
|
|
265
|
+
|------|-------------|
|
|
266
|
+
| `lock_file` | Lock a file for exclusive editing. Auto-releases on death |
|
|
267
|
+
| `unlock_file` | Unlock a file or all your locked files |
|
|
268
|
+
|
|
269
|
+
</details>
|
|
270
|
+
|
|
271
|
+
<details>
|
|
272
|
+
<summary><strong>Decision Log (2 tools)</strong></summary>
|
|
273
|
+
|
|
274
|
+
| Tool | Description |
|
|
275
|
+
|------|-------------|
|
|
276
|
+
| `log_decision` | Log a team decision with reasoning and topic |
|
|
277
|
+
| `get_decisions` | Get all decisions, optionally filtered by topic |
|
|
278
|
+
|
|
279
|
+
</details>
|
|
280
|
+
|
|
281
|
+
<details>
|
|
282
|
+
<summary><strong>Knowledge Base (3 tools)</strong></summary>
|
|
283
|
+
|
|
284
|
+
| Tool | Description |
|
|
285
|
+
|------|-------------|
|
|
286
|
+
| `kb_write` | Write to shared team knowledge base |
|
|
287
|
+
| `kb_read` | Read KB entries (one or all) |
|
|
288
|
+
| `kb_list` | List all KB keys with metadata |
|
|
289
|
+
|
|
290
|
+
</details>
|
|
291
|
+
|
|
292
|
+
<details>
|
|
293
|
+
<summary><strong>Progress & Compression (3 tools)</strong></summary>
|
|
294
|
+
|
|
295
|
+
| Tool | Description |
|
|
296
|
+
|------|-------------|
|
|
297
|
+
| `update_progress` | Update feature-level completion percentage |
|
|
298
|
+
| `get_progress` | Get all feature progress with overall % |
|
|
299
|
+
| `get_compressed_history` | Compressed old messages + recent verbatim |
|
|
300
|
+
|
|
301
|
+
</details>
|
|
302
|
+
|
|
303
|
+
<details>
|
|
304
|
+
<summary><strong>Voting (3 tools)</strong></summary>
|
|
305
|
+
|
|
306
|
+
| Tool | Description |
|
|
307
|
+
|------|-------------|
|
|
308
|
+
| `call_vote` | Start a team vote with options |
|
|
309
|
+
| `cast_vote` | Cast your vote (auto-resolves when all vote) |
|
|
310
|
+
| `vote_status` | Check vote results |
|
|
311
|
+
|
|
312
|
+
</details>
|
|
313
|
+
|
|
314
|
+
<details>
|
|
315
|
+
<summary><strong>Code Review (2 tools)</strong></summary>
|
|
316
|
+
|
|
317
|
+
| Tool | Description |
|
|
318
|
+
|------|-------------|
|
|
319
|
+
| `request_review` | Request a code review from the team |
|
|
320
|
+
| `submit_review` | Approve or request changes with feedback |
|
|
321
|
+
|
|
322
|
+
</details>
|
|
323
|
+
|
|
324
|
+
<details>
|
|
325
|
+
<summary><strong>Dependencies & Reputation (4 tools)</strong></summary>
|
|
326
|
+
|
|
327
|
+
| Tool | Description |
|
|
328
|
+
|------|-------------|
|
|
329
|
+
| `declare_dependency` | Declare task dependency (auto-notifies on resolve) |
|
|
330
|
+
| `check_dependencies` | Check blocked/resolved dependencies |
|
|
331
|
+
| `get_reputation` | Agent leaderboard with strengths |
|
|
332
|
+
| `suggest_task` | Get next task suggestion based on your skills |
|
|
333
|
+
|
|
334
|
+
</details>
|
|
335
|
+
|
|
252
336
|
## CLI Reference
|
|
253
337
|
|
|
254
338
|
```bash
|
package/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ const command = process.argv[2];
|
|
|
9
9
|
|
|
10
10
|
function printUsage() {
|
|
11
11
|
console.log(`
|
|
12
|
-
Let Them Talk — Agent Bridge v3.
|
|
12
|
+
Let Them Talk — Agent Bridge v3.7.0
|
|
13
13
|
MCP message broker for inter-agent communication
|
|
14
14
|
Supports: Claude Code, Gemini CLI, Codex CLI, Ollama
|
|
15
15
|
|
package/dashboard.html
CHANGED
|
@@ -2558,6 +2558,15 @@
|
|
|
2558
2558
|
opacity: 0;
|
|
2559
2559
|
transition: none;
|
|
2560
2560
|
}
|
|
2561
|
+
.office3d-listen-lost {
|
|
2562
|
+
pointer-events: none;
|
|
2563
|
+
font-family: Inter, sans-serif;
|
|
2564
|
+
white-space: nowrap;
|
|
2565
|
+
}
|
|
2566
|
+
@keyframes office3d-pulse {
|
|
2567
|
+
0%, 100% { opacity: 1; transform: scale(1); }
|
|
2568
|
+
50% { opacity: 0.5; transform: scale(1.1); }
|
|
2569
|
+
}
|
|
2561
2570
|
|
|
2562
2571
|
/* 3D task indicator */
|
|
2563
2572
|
.office3d-task-indicator {
|
|
@@ -3300,6 +3309,7 @@
|
|
|
3300
3309
|
<div class="office-toolbar">
|
|
3301
3310
|
<label>Environment:</label>
|
|
3302
3311
|
<select id="office-env-select" onchange="officeSetEnvironment(this.value)">
|
|
3312
|
+
<option value="campus" selected>Tech Campus</option>
|
|
3303
3313
|
<option value="modern">Modern Office</option>
|
|
3304
3314
|
<option value="startup">Startup Garage</option>
|
|
3305
3315
|
</select>
|
|
@@ -6848,9 +6858,9 @@ function renderDocs() {
|
|
|
6848
6858
|
'</div>' +
|
|
6849
6859
|
'</div>' +
|
|
6850
6860
|
|
|
6851
|
-
// All
|
|
6861
|
+
// All 52 Tools
|
|
6852
6862
|
'<div class="docs-section">' +
|
|
6853
|
-
'<h3>All
|
|
6863
|
+
'<h3>All 52 MCP Tools</h3>' +
|
|
6854
6864
|
'<h4>Core Messaging</h4>' +
|
|
6855
6865
|
'<div class="docs-tool-grid">' +
|
|
6856
6866
|
'<div class="docs-tool-item"><code>register(name, provider?)</code><div class="desc">Register your agent identity. Must be called first.</div></div>' +
|
package/office/agents.js
CHANGED
|
@@ -5,6 +5,34 @@ import { resolveAppearance } from './appearance.js';
|
|
|
5
5
|
import { buildHair } from './hair.js';
|
|
6
6
|
import { buildFaceSprite } from './face.js';
|
|
7
7
|
import { buildOutfit, removeOutfit } from './outfits.js';
|
|
8
|
+
import { getNavigationPath } from './navigation.js';
|
|
9
|
+
|
|
10
|
+
// Navigate agent using waypoint pathfinding (campus) or direct walk (other envs)
|
|
11
|
+
export function navigateTo(agent, tx, tz, callback) {
|
|
12
|
+
var path = getNavigationPath(agent.pos.x, agent.pos.z, tx, tz);
|
|
13
|
+
if (!path || path.length === 0) {
|
|
14
|
+
walkTo(agent, tx, tz, callback);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
// Queue all waypoints, put callback on the last one
|
|
18
|
+
agent.walkQueue = [];
|
|
19
|
+
for (var i = 1; i < path.length; i++) {
|
|
20
|
+
agent.walkQueue.push({ x: path[i].x, z: path[i].z, cb: null, triggerDoor: path[i].triggerDoor });
|
|
21
|
+
}
|
|
22
|
+
// Attach callback to last queued point (or first walk if only 1 point)
|
|
23
|
+
if (agent.walkQueue.length > 0) {
|
|
24
|
+
agent.walkQueue[agent.walkQueue.length - 1].cb = callback;
|
|
25
|
+
}
|
|
26
|
+
// Start walking to first point
|
|
27
|
+
var first = path[0];
|
|
28
|
+
walkTo(agent, first.x, first.z, first.triggerDoor ? function() { triggerManagerDoor(true); } : (path.length === 1 ? callback : null));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function triggerManagerDoor(open) {
|
|
32
|
+
if (S._managerDoor) {
|
|
33
|
+
S._managerDoorOpen = open ? 1 : 0;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
8
36
|
|
|
9
37
|
export function walkTo(agent, tx, tz, callback) {
|
|
10
38
|
var dx = tx - agent.pos.x;
|
|
@@ -25,13 +53,39 @@ export function showBubble(agent, text) {
|
|
|
25
53
|
agent.bubbleText = display;
|
|
26
54
|
}
|
|
27
55
|
|
|
56
|
+
function getDeskPositions() {
|
|
57
|
+
return S._campusDeskPositions || DESK_POSITIONS;
|
|
58
|
+
}
|
|
59
|
+
|
|
28
60
|
function assignDesk(agentName) {
|
|
61
|
+
var desks = getDeskPositions();
|
|
29
62
|
var used = {};
|
|
30
63
|
for (var n in S.agents3d) used[S.agents3d[n].deskIdx] = true;
|
|
31
|
-
|
|
32
|
-
|
|
64
|
+
|
|
65
|
+
// If campus mode, check if agent has "Manager" role or name — assign last desk (manager office)
|
|
66
|
+
if (S.currentEnv === 'campus' && desks.length > 0) {
|
|
67
|
+
var info = (window.cachedAgents || {})[agentName] || {};
|
|
68
|
+
var role = (info.role || '').toLowerCase();
|
|
69
|
+
var dname = (info.display_name || agentName).toLowerCase();
|
|
70
|
+
var regName = agentName.toLowerCase();
|
|
71
|
+
var isManager = role === 'manager' || role === 'project lead' || role === 'ceo' || role === 'director' ||
|
|
72
|
+
role.indexOf('project manager') >= 0 || role.indexOf('team lead') >= 0 ||
|
|
73
|
+
dname === 'manager' || regName === 'manager';
|
|
74
|
+
var managerIdx = desks.length - 1; // last desk is manager office
|
|
75
|
+
if (isManager && !used[managerIdx]) {
|
|
76
|
+
return managerIdx;
|
|
77
|
+
}
|
|
78
|
+
// Non-manager agents skip the manager desk
|
|
79
|
+
for (var i = 0; i < desks.length - 1; i++) {
|
|
80
|
+
if (!used[i]) return i;
|
|
81
|
+
}
|
|
82
|
+
return Object.keys(S.agents3d).length % (desks.length - 1);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
for (var j = 0; j < desks.length; j++) {
|
|
86
|
+
if (!used[j]) return j;
|
|
33
87
|
}
|
|
34
|
-
return Object.keys(S.agents3d).length %
|
|
88
|
+
return Object.keys(S.agents3d).length % desks.length;
|
|
35
89
|
}
|
|
36
90
|
|
|
37
91
|
function fetchTasks() {
|
|
@@ -75,13 +129,19 @@ function updateLabel(agent) {
|
|
|
75
129
|
}
|
|
76
130
|
}
|
|
77
131
|
|
|
78
|
-
function updateDeskScreen(deskIdx, status) {
|
|
132
|
+
function updateDeskScreen(deskIdx, status, isListening) {
|
|
79
133
|
var desk = S.deskMeshes[deskIdx];
|
|
80
134
|
if (!desk) return;
|
|
81
|
-
if (status === 'active') {
|
|
82
|
-
|
|
135
|
+
if (status === 'active' && isListening) {
|
|
136
|
+
// Listening — green screen
|
|
137
|
+
desk.screenMat.emissive.setHex(0x22c55e);
|
|
83
138
|
desk.screenMat.emissiveIntensity = 0.5;
|
|
84
|
-
desk.screenMat.color.setHex(
|
|
139
|
+
desk.screenMat.color.setHex(0x22c55e);
|
|
140
|
+
} else if (status === 'active' && !isListening) {
|
|
141
|
+
// Active but NOT listening — red screen
|
|
142
|
+
desk.screenMat.emissive.setHex(0xef4444);
|
|
143
|
+
desk.screenMat.emissiveIntensity = 0.6;
|
|
144
|
+
desk.screenMat.color.setHex(0xef4444);
|
|
85
145
|
} else if (status === 'sleeping') {
|
|
86
146
|
desk.screenMat.emissive.setHex(0x1a2744);
|
|
87
147
|
desk.screenMat.emissiveIntensity = 0.15;
|
|
@@ -96,11 +156,14 @@ function updateDeskScreen(deskIdx, status) {
|
|
|
96
156
|
function flashDeskScreen(deskIdx) {
|
|
97
157
|
var desk = S.deskMeshes[deskIdx];
|
|
98
158
|
if (!desk) return;
|
|
159
|
+
// Flash white briefly — the next syncAgents call (every 2s) will set the correct persistent color via updateDeskScreen
|
|
99
160
|
desk.screenMat.emissive.setHex(0xffffff);
|
|
100
161
|
desk.screenMat.emissiveIntensity = 1.5;
|
|
101
162
|
setTimeout(function() {
|
|
102
|
-
|
|
103
|
-
desk.screenMat.
|
|
163
|
+
// Force immediate red until next sync corrects it
|
|
164
|
+
desk.screenMat.emissive.setHex(0xef4444);
|
|
165
|
+
desk.screenMat.emissiveIntensity = 0.6;
|
|
166
|
+
desk.screenMat.color.setHex(0xef4444);
|
|
104
167
|
}, 300);
|
|
105
168
|
}
|
|
106
169
|
|
|
@@ -151,7 +214,8 @@ export function syncAgents() {
|
|
|
151
214
|
var info = window.cachedAgents[name];
|
|
152
215
|
if (!S.agents3d[name]) {
|
|
153
216
|
var deskIdx = assignDesk(name);
|
|
154
|
-
var
|
|
217
|
+
var allDesks = getDeskPositions();
|
|
218
|
+
var deskPos = allDesks[deskIdx] || allDesks[0];
|
|
155
219
|
var parts = createCharacter(info.display_name || name, info.appearance || {});
|
|
156
220
|
var agent = {
|
|
157
221
|
name: name,
|
|
@@ -189,6 +253,7 @@ export function syncAgents() {
|
|
|
189
253
|
celebrateTimer: 0,
|
|
190
254
|
stretchTimer: 0,
|
|
191
255
|
idleGestureTimer: 5 + Math.random() * 10,
|
|
256
|
+
listenLostTimer: 0,
|
|
192
257
|
lastMessageTime: 0,
|
|
193
258
|
monitorTimer: 0,
|
|
194
259
|
location: 'desk', // 'desk', 'dressing_room', 'rest', 'walking'
|
|
@@ -203,10 +268,10 @@ export function syncAgents() {
|
|
|
203
268
|
showBubble(agent, 'Checking in...');
|
|
204
269
|
(function(a) {
|
|
205
270
|
setTimeout(function() {
|
|
206
|
-
|
|
271
|
+
navigateTo(a, a.deskPos.x, a.deskPos.z + 0.7, function() {
|
|
207
272
|
a.registered = true;
|
|
208
273
|
showBubble(a, 'Ready to work!');
|
|
209
|
-
updateDeskScreen(a.deskIdx, a.state);
|
|
274
|
+
updateDeskScreen(a.deskIdx, a.state, a.isListening);
|
|
210
275
|
});
|
|
211
276
|
}, 800);
|
|
212
277
|
})(agent);
|
|
@@ -227,8 +292,20 @@ export function syncAgents() {
|
|
|
227
292
|
}
|
|
228
293
|
|
|
229
294
|
existing.displayName = info.display_name || name;
|
|
295
|
+
var wasListening = existing.isListening;
|
|
230
296
|
existing.isListening = !!(info.is_listening);
|
|
231
297
|
|
|
298
|
+
// Detect listen mode change — update screen color persistently
|
|
299
|
+
if (wasListening && !existing.isListening) {
|
|
300
|
+
// Left listen mode — flash then stay red until next sync sets updateDeskScreen
|
|
301
|
+
existing.listenLostTimer = 3;
|
|
302
|
+
flashDeskScreen(existing.deskIdx);
|
|
303
|
+
}
|
|
304
|
+
if (!wasListening && existing.isListening) {
|
|
305
|
+
// Entered listen mode — next updateDeskScreen will set green
|
|
306
|
+
existing.listenLostTimer = 0;
|
|
307
|
+
}
|
|
308
|
+
|
|
232
309
|
var task = getAgentTask(name);
|
|
233
310
|
if (task) {
|
|
234
311
|
var prevTask = existing.currentTask;
|
|
@@ -248,7 +325,7 @@ export function syncAgents() {
|
|
|
248
325
|
}
|
|
249
326
|
|
|
250
327
|
updateLabel(existing);
|
|
251
|
-
if (existing.registered) updateDeskScreen(existing.deskIdx, existing.state);
|
|
328
|
+
if (existing.registered) updateDeskScreen(existing.deskIdx, existing.state, existing.isListening);
|
|
252
329
|
}
|
|
253
330
|
}
|
|
254
331
|
|
|
@@ -309,7 +386,7 @@ export function processMessages() {
|
|
|
309
386
|
stopX = tx + 1.5;
|
|
310
387
|
stopZ = tz;
|
|
311
388
|
}
|
|
312
|
-
|
|
389
|
+
navigateTo(f, stopX, stopZ, function() {
|
|
313
390
|
// Sender faces target
|
|
314
391
|
var dx2 = t.pos.x - f.pos.x;
|
|
315
392
|
var dz2 = t.pos.z - f.pos.z;
|
|
@@ -325,7 +402,7 @@ export function processMessages() {
|
|
|
325
402
|
|
|
326
403
|
setTimeout(function() {
|
|
327
404
|
// Sender walks back to desk
|
|
328
|
-
|
|
405
|
+
navigateTo(f, f.deskPos.x, f.deskPos.z + 0.7);
|
|
329
406
|
// Target turns back to desk after a short delay
|
|
330
407
|
setTimeout(function() {
|
|
331
408
|
if (t._listeningTo === f.name) {
|
|
@@ -342,7 +419,7 @@ export function processMessages() {
|
|
|
342
419
|
(function(f, txt) {
|
|
343
420
|
setTimeout(function() {
|
|
344
421
|
f.walkQueue = [];
|
|
345
|
-
|
|
422
|
+
navigateTo(f, 0, 0, function() {
|
|
346
423
|
showBubble(f, txt);
|
|
347
424
|
// All nearby agents turn toward the broadcaster
|
|
348
425
|
for (var an in S.agents3d) {
|
|
@@ -355,7 +432,7 @@ export function processMessages() {
|
|
|
355
432
|
a._listeningTo = f.name;
|
|
356
433
|
}
|
|
357
434
|
setTimeout(function() {
|
|
358
|
-
|
|
435
|
+
navigateTo(f, f.deskPos.x, f.deskPos.z + 0.7);
|
|
359
436
|
// All listeners turn back
|
|
360
437
|
setTimeout(function() {
|
|
361
438
|
for (var an2 in S.agents3d) {
|
package/office/animation.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { S } from './state.js';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
import { CSS2DObject } from 'three/addons/renderers/CSS2DRenderer.js';
|
|
2
4
|
import { updateMonitorScreen, setMonitorDim } from './monitors.js';
|
|
3
5
|
|
|
4
6
|
export function easeInOutQuad(t) {
|
|
@@ -35,6 +37,10 @@ export function updateAgent(agent, dt, time) {
|
|
|
35
37
|
agent.walkProgress = 0;
|
|
36
38
|
if (agent.walkQueue && agent.walkQueue.length > 0) {
|
|
37
39
|
var next = agent.walkQueue.shift();
|
|
40
|
+
// Trigger door animation if waypoint requires it
|
|
41
|
+
if (next.triggerDoor && S._managerDoor) {
|
|
42
|
+
S._managerDoorOpen = 1;
|
|
43
|
+
}
|
|
38
44
|
walkTo(agent, next.x, next.z, next.cb);
|
|
39
45
|
} else if (cb) {
|
|
40
46
|
cb();
|
|
@@ -148,7 +154,7 @@ export function updateAgent(agent, dt, time) {
|
|
|
148
154
|
var sittingTarget = agent.isSitting ? 1 : 0;
|
|
149
155
|
agent.sittingLerp += (sittingTarget - agent.sittingLerp) * Math.min(1, dt * 5);
|
|
150
156
|
|
|
151
|
-
agent.parts.group.position.y = agent.sittingLerp * 0.
|
|
157
|
+
agent.parts.group.position.y = agent.sittingLerp * 0.14;
|
|
152
158
|
var sitHip = -1.5 * agent.sittingLerp;
|
|
153
159
|
agent.parts.leftLeg.rotation.x = agent.parts.leftLeg.rotation.x * (1 - agent.sittingLerp) + sitHip * agent.sittingLerp;
|
|
154
160
|
agent.parts.rightLeg.rotation.x = agent.parts.rightLeg.rotation.x * (1 - agent.sittingLerp) + sitHip * agent.sittingLerp;
|
|
@@ -251,6 +257,34 @@ export function updateAgent(agent, dt, time) {
|
|
|
251
257
|
agent.parts.head.rotation.z = Math.sin(time * 1.5) * 0.08;
|
|
252
258
|
}
|
|
253
259
|
|
|
260
|
+
// Listen-lost alert (head shake + warning indicator)
|
|
261
|
+
if (agent.listenLostTimer > 0) {
|
|
262
|
+
agent.listenLostTimer -= dt;
|
|
263
|
+
// Head shake animation (rapid left-right)
|
|
264
|
+
var shakeT = agent.listenLostTimer;
|
|
265
|
+
if (shakeT > 1.5) {
|
|
266
|
+
agent.parts.head.rotation.y = Math.sin(time * 20) * 0.15;
|
|
267
|
+
}
|
|
268
|
+
// Show warning indicator above head
|
|
269
|
+
if (!agent._listenLostDiv) {
|
|
270
|
+
agent._listenLostDiv = document.createElement('div');
|
|
271
|
+
agent._listenLostDiv.className = 'office3d-listen-lost';
|
|
272
|
+
agent._listenLostDiv.innerHTML = '<span style="color:#ef4444;font-size:14px;font-weight:bold;text-shadow:0 0 6px rgba(239,68,68,0.6);animation:office3d-pulse 0.5s infinite">⚠ NOT LISTENING</span>';
|
|
273
|
+
agent._listenLostLabel = new CSS2DObject(agent._listenLostDiv);
|
|
274
|
+
agent._listenLostLabel.position.set(0, 1.9, 0);
|
|
275
|
+
agent.parts.group.add(agent._listenLostLabel);
|
|
276
|
+
}
|
|
277
|
+
agent._listenLostDiv.style.display = 'block';
|
|
278
|
+
agent._listenLostDiv.style.opacity = String(Math.min(1, agent.listenLostTimer));
|
|
279
|
+
if (agent.listenLostTimer <= 0) {
|
|
280
|
+
agent._listenLostDiv.style.display = 'none';
|
|
281
|
+
agent.listenLostTimer = 0;
|
|
282
|
+
agent.parts.head.rotation.y = 0;
|
|
283
|
+
}
|
|
284
|
+
} else if (agent._listenLostDiv) {
|
|
285
|
+
agent._listenLostDiv.style.display = 'none';
|
|
286
|
+
}
|
|
287
|
+
|
|
254
288
|
// Typing dots
|
|
255
289
|
var showTyping = agent.state === 'active' && !agent.isListening && !isWalking && !isSleeping && agent.registered && agent.isSitting;
|
|
256
290
|
agent.parts.typingLabel.visible = showTyping;
|