Files
OpenClaw-bot-review/lib/pixel-office/agentBridge.ts
T
xmanrui 12e43f5983 feat: add idle furniture interaction and doorway entrance animation
Idle characters now have a 40% chance to walk to nearby furniture
(water cooler, bookshelf, whiteboard, fridge) and linger for 5-15s.
Agents spawning from offline walk in from doorway tiles instead of
appearing directly at their seat.
2026-02-25 13:53:07 +08:00

113 lines
3.4 KiB
TypeScript

import { OfficeState } from './engine/officeState'
export interface SubagentInfo {
toolId: string
label: string
}
export interface AgentActivity {
agentId: string
name: string
emoji: string
state: 'idle' | 'working' | 'waiting' | 'offline'
currentTool?: string
toolStatus?: string
lastActive: number
subagents?: SubagentInfo[]
}
/** Track which subagent toolIds were active last sync, per parent agent */
const prevSubagentKeys = new Map<string, Set<string>>()
/** Track previous agent states to detect offline→working transitions */
const prevAgentStates = new Map<string, string>()
export function syncAgentsToOffice(
activities: AgentActivity[],
office: OfficeState,
agentIdMap: Map<string, number>,
nextIdRef: { current: number },
): void {
const currentAgentIds = new Set(activities.map(a => a.agentId))
// Remove agents that are no longer present
for (const [agentId, charId] of agentIdMap) {
if (!currentAgentIds.has(agentId)) {
office.removeAllSubagents(charId)
office.removeAgent(charId)
agentIdMap.delete(agentId)
prevSubagentKeys.delete(agentId)
}
}
for (const activity of activities) {
if (activity.state === 'offline') {
if (agentIdMap.has(activity.agentId)) {
const charId = agentIdMap.get(activity.agentId)!
office.removeAllSubagents(charId)
office.removeAgent(charId)
agentIdMap.delete(activity.agentId)
prevSubagentKeys.delete(activity.agentId)
}
prevAgentStates.set(activity.agentId, 'offline')
continue
}
let charId = agentIdMap.get(activity.agentId)
if (charId === undefined) {
charId = nextIdRef.current++
agentIdMap.set(activity.agentId, charId)
// Spawn at door if agent was previously offline or is brand new
const wasOffline = prevAgentStates.get(activity.agentId) === 'offline'
const isNew = !prevAgentStates.has(activity.agentId)
office.addAgent(charId, undefined, undefined, undefined, undefined, wasOffline || isNew)
}
// Set label (agent name or id)
const ch = office.characters.get(charId)
if (ch) {
ch.label = activity.name || activity.agentId
}
switch (activity.state) {
case 'working':
office.setAgentActive(charId, true)
office.setAgentTool(charId, activity.currentTool || null)
break
case 'idle':
office.setAgentActive(charId, false)
office.setAgentTool(charId, null)
break
case 'waiting':
office.setAgentActive(charId, true)
office.showWaitingBubble(charId)
break
}
// Sync subagents
const currentSubKeys = new Set<string>()
if (activity.subagents) {
for (const sub of activity.subagents) {
currentSubKeys.add(sub.toolId)
const existingSubId = office.getSubagentId(charId, sub.toolId)
if (existingSubId === null) {
const subId = office.addSubagent(charId, sub.toolId)
office.setAgentActive(subId, true)
}
}
}
// Remove subagents that are no longer active
const prevKeys = prevSubagentKeys.get(activity.agentId)
if (prevKeys) {
for (const toolId of prevKeys) {
if (!currentSubKeys.has(toolId)) {
office.removeSubagent(charId, toolId)
}
}
}
prevSubagentKeys.set(activity.agentId, currentSubKeys)
prevAgentStates.set(activity.agentId, activity.state)
}
}