Files
OpenClaw-bot-review/lib/pixel-office/agentBridge.ts
T
Dan LeeandClaude Sonnet 4.6 64cb526758 feat: 像素辦公室顯示 Agent 正在執行的交辦任務
- agent-activity API 新增 lastTask 欄位,回傳 working 中 Agent 最後被交辦的任務內容
- 支援兩種格式:Telegram 訊息(``` fence 後的文字)與子 Agent spawn([Subagent Task]: 內容)
- Character 新增 taskText 欄位,OfficeState 新增 setAgentTaskText() 方法
- agentBridge 同步任務文字至像素辦公室角色
- renderer 在 working 中的 Agent 頭上顯示固定寬度氣泡,內容過長自動跑馬燈捲動

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 10:55:10 +08:00

135 lines
4.3 KiB
TypeScript

import { OfficeState } from './engine/officeState'
export interface SubagentInfo {
toolId: string
label: string
sessionKey?: string
childSessionKey?: string
activityEvents?: Array<{ key: string; text: string; at: number }>
}
export interface AgentActivity {
agentId: string
name: string
emoji: string
state: 'idle' | 'working' | 'waiting' | 'offline'
currentTool?: string
toolStatus?: string
lastActive: number
subagents?: SubagentInfo[]
lastTask?: string
}
/** Track which subagent keys 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 && !office.characters.has(charId)) {
agentIdMap.delete(activity.agentId)
charId = undefined
}
if (charId === undefined) {
charId = nextIdRef.current++
agentIdMap.set(activity.agentId, charId)
// 只有從 offline 恢復時才從門口走進來;
// 頁面初始載入(isNew)時直接放到座位,避免讓使用者以為角色剛去摸魚回來
const wasOffline = prevAgentStates.get(activity.agentId) === 'offline'
office.addAgent(charId, undefined, undefined, undefined, undefined, wasOffline)
}
// Set label, avoiding duplicated values like "main (main)"
const ch = office.characters.get(charId)
if (ch) {
const displayName = activity.name?.trim()
ch.label = displayName && displayName !== activity.agentId
? `${displayName} (${activity.agentId})`
: activity.agentId
}
switch (activity.state) {
case 'working':
office.setAgentActive(charId, true)
office.setAgentTool(charId, activity.currentTool || null)
office.setAgentTaskText(charId, activity.lastTask)
break
case 'idle':
office.setAgentActive(charId, false)
office.setAgentTool(charId, null)
office.setAgentTaskText(charId, undefined)
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) {
const subKey = sub.sessionKey ? `${sub.sessionKey}::${sub.toolId}` : sub.toolId
currentSubKeys.add(subKey)
const existingSubId = office.getSubagentId(charId, subKey)
if (existingSubId === null) {
const subId = office.addSubagent(charId, subKey)
office.setAgentActive(subId, true)
const subCh = office.characters.get(subId)
if (subCh) subCh.label = office.getTempWorkerLabel()
} else {
const subCh = office.characters.get(existingSubId)
if (subCh) {
subCh.label = office.getTempWorkerLabel()
office.setAgentActive(existingSubId, true)
}
}
}
}
// Remove subagents that are no longer active
const prevKeys = prevSubagentKeys.get(activity.agentId)
if (prevKeys) {
for (const subKey of prevKeys) {
if (!currentSubKeys.has(subKey)) {
office.removeSubagent(charId, subKey)
}
}
}
prevSubagentKeys.set(activity.agentId, currentSubKeys)
prevAgentStates.set(activity.agentId, activity.state)
}
}