feat: add Kimi & Perplexity as visible Workshop agents (#11)
- Add Kimi (Long Context Analysis) and Perplexity (Real-time Research) to AGENT_DEFS with specialization and external flags - Build distinct 3D bodies for each: Kimi gets a tapered hexagonal obelisk with 3 orbiting torus rings; Perplexity gets an icosahedron core with wireframe shell and 4 orbiting dot spheres - Animate 3D bodies each frame: orbit speed and light intensity scale with agent state (idle = dim/slow, active/working = bright/fast) - Track _extAgentStates and sync via setAgentState/applyAgentStates - Add kimi and perplexity to server-side world-state initial states - HUD labels pick up both agents automatically via AGENT_DEFS iteration - Store specialization per label; show in inspect popup - Export setAgentLastTask from hud-labels for last-task tooltip - Call setAgentLastTask on job_completed events in websocket handler Fixes #11
This commit is contained in:
@@ -16,7 +16,7 @@ const DEFAULT_TIMMY: TimmyState = {
|
|||||||
|
|
||||||
const _state: WorldState = {
|
const _state: WorldState = {
|
||||||
timmyState: { ...DEFAULT_TIMMY },
|
timmyState: { ...DEFAULT_TIMMY },
|
||||||
agentStates: { alpha: "idle", beta: "idle", gamma: "idle", delta: "idle" },
|
agentStates: { alpha: "idle", beta: "idle", gamma: "idle", delta: "idle", kimi: "idle", perplexity: "idle" },
|
||||||
updatedAt: new Date().toISOString(),
|
updatedAt: new Date().toISOString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -9,14 +9,18 @@
|
|||||||
* label — display name shown in the 3D HUD and chat panel
|
* label — display name shown in the 3D HUD and chat panel
|
||||||
* color — hex integer (0xRRGGBB) used for Three.js materials and lights
|
* color — hex integer (0xRRGGBB) used for Three.js materials and lights
|
||||||
* role — human-readable role string shown under the label sprite
|
* role — human-readable role string shown under the label sprite
|
||||||
* direction — cardinal facing direction (for future mesh orientation use)
|
* direction — cardinal facing direction (for future mesh orientation use)
|
||||||
* x, z — world-space position on the horizontal plane (y is always 0)
|
* x, z — world-space position on the horizontal plane (y is always 0)
|
||||||
|
* specialization — optional skill label shown in HUD inspect popup
|
||||||
|
* external — true for external agents (Kimi, Perplexity) that render 3D bodies
|
||||||
*/
|
*/
|
||||||
export const AGENT_DEFS = [
|
export const AGENT_DEFS = [
|
||||||
{ id: 'alpha', label: 'ALPHA', color: 0x00ff88, role: 'orchestrator', direction: 'north', x: 0, z: -6 },
|
{ id: 'alpha', label: 'ALPHA', color: 0x00ff88, role: 'orchestrator', direction: 'north', x: 0, z: -6 },
|
||||||
{ id: 'beta', label: 'BETA', color: 0x00aaff, role: 'worker', direction: 'east', x: 6, z: 0 },
|
{ id: 'beta', label: 'BETA', color: 0x00aaff, role: 'worker', direction: 'east', x: 6, z: 0 },
|
||||||
{ id: 'gamma', label: 'GAMMA', color: 0xff6600, role: 'validator', direction: 'south', x: 0, z: 6 },
|
{ id: 'gamma', label: 'GAMMA', color: 0xff6600, role: 'validator', direction: 'south', x: 0, z: 6 },
|
||||||
{ id: 'delta', label: 'DELTA', color: 0xaa00ff, role: 'monitor', direction: 'west', x: -6, z: 0 },
|
{ id: 'delta', label: 'DELTA', color: 0xaa00ff, role: 'monitor', direction: 'west', x: -6, z: 0 },
|
||||||
|
{ id: 'kimi', label: 'KIMI', color: 0x00d4ff, role: 'analyst', specialization: 'Long Context Analysis', external: true, direction: 'northwest', x: -9, z: -5 },
|
||||||
|
{ id: 'perplexity', label: 'PERPLEXITY', color: 0xff4488, role: 'researcher', specialization: 'Real-time Research', external: true, direction: 'northeast', x: 9, z: -5 },
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { AGENT_DEFS } from './agent-defs.js';
|
import { AGENT_DEFS } from './agent-defs.js';
|
||||||
|
|
||||||
|
// ── External agent 3D bodies (Kimi, Perplexity) ──────────────────────────────
|
||||||
|
const _extAgents = [];
|
||||||
|
const _extAgentStates = {}; // id -> state string
|
||||||
|
|
||||||
const TIMMY_POS = new THREE.Vector3(0, 0, -2);
|
const TIMMY_POS = new THREE.Vector3(0, 0, -2);
|
||||||
export const TIMMY_WORLD_POS = TIMMY_POS.clone();
|
export const TIMMY_WORLD_POS = TIMMY_POS.clone();
|
||||||
const CRYSTAL_POS = new THREE.Vector3(0.6, 1.15, -4.1);
|
const CRYSTAL_POS = new THREE.Vector3(0.6, 1.15, -4.1);
|
||||||
@@ -100,6 +104,15 @@ function _pickMouthGeo(smileAmount) {
|
|||||||
export function initAgents(sceneRef) {
|
export function initAgents(sceneRef) {
|
||||||
scene = sceneRef;
|
scene = sceneRef;
|
||||||
timmy = buildTimmy(scene);
|
timmy = buildTimmy(scene);
|
||||||
|
|
||||||
|
// Build 3D bodies for external agents (Kimi, Perplexity)
|
||||||
|
for (const def of AGENT_DEFS.filter(d => d.external)) {
|
||||||
|
const body = buildExternalAgentBody(def);
|
||||||
|
body.group.position.set(def.x, 0, def.z);
|
||||||
|
scene.add(body.group);
|
||||||
|
_extAgents.push(body);
|
||||||
|
_extAgentStates[def.id] = 'idle';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildTimmy(sc) {
|
function buildTimmy(sc) {
|
||||||
@@ -537,6 +550,52 @@ export function updateAgents(time) {
|
|||||||
if (timmy.mouth.geometry !== nextMouthGeo) {
|
if (timmy.mouth.geometry !== nextMouthGeo) {
|
||||||
timmy.mouth.geometry = nextMouthGeo;
|
timmy.mouth.geometry = nextMouthGeo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Animate external agents (Kimi, Perplexity) ────────────────────────────
|
||||||
|
for (const agent of _extAgents) {
|
||||||
|
const state = _extAgentStates[agent.id] || 'idle';
|
||||||
|
const active = state !== 'idle';
|
||||||
|
|
||||||
|
// Light intensity and orbit speed scale with activity
|
||||||
|
const lightBase = active ? 1.6 : 0.4;
|
||||||
|
const lightPulse = Math.sin(t * (active ? 3.2 : 1.1)) * (active ? 0.5 : 0.15);
|
||||||
|
agent.light.intensity = lightBase + lightPulse;
|
||||||
|
|
||||||
|
const speedMult = active ? 2.2 : 0.55;
|
||||||
|
|
||||||
|
// Animate orbiters
|
||||||
|
for (const orb of agent.orbiters) {
|
||||||
|
const angle = t * orb.speed * speedMult + orb.phase;
|
||||||
|
orb.mesh.position.set(
|
||||||
|
Math.cos(angle) * orb.radius,
|
||||||
|
orb.yOffset,
|
||||||
|
Math.sin(angle) * orb.radius,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gentle whole-group rotation for Kimi's pillar, pulsing Y bob for Perplexity's core
|
||||||
|
if (agent.id === 'kimi') {
|
||||||
|
agent.group.rotation.y += 0.003 * speedMult;
|
||||||
|
// Emissive pulse on pillar
|
||||||
|
const pillarMesh = agent.meshes[0];
|
||||||
|
if (pillarMesh?.material) {
|
||||||
|
pillarMesh.material.emissiveIntensity = active
|
||||||
|
? 0.25 + Math.sin(t * 2.8) * 0.12
|
||||||
|
: 0.08 + Math.sin(t * 0.9) * 0.03;
|
||||||
|
}
|
||||||
|
} else if (agent.id === 'perplexity') {
|
||||||
|
// Slowly spin the icosahedron core and shell
|
||||||
|
const core = agent.meshes[0];
|
||||||
|
const shell = agent.meshes[1];
|
||||||
|
if (core) { core.rotation.y += 0.008 * speedMult; core.rotation.x += 0.004 * speedMult; }
|
||||||
|
if (shell) { shell.rotation.y -= 0.005 * speedMult; }
|
||||||
|
if (core?.material) {
|
||||||
|
core.material.emissiveIntensity = active
|
||||||
|
? 0.30 + Math.sin(t * 3.5) * 0.15
|
||||||
|
: 0.10 + Math.sin(t * 0.8) * 0.04;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── setFaceEmotion — public API ───────────────────────────────────────────────
|
// ── setFaceEmotion — public API ───────────────────────────────────────────────
|
||||||
@@ -807,6 +866,7 @@ export function getCameraShakeStrength() {
|
|||||||
|
|
||||||
export function setAgentState(agentId, state) {
|
export function setAgentState(agentId, state) {
|
||||||
if (agentId in agentStates) agentStates[agentId] = state;
|
if (agentId in agentStates) agentStates[agentId] = state;
|
||||||
|
if (agentId in _extAgentStates) _extAgentStates[agentId] = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setSpeechBubble(text) {
|
export function setSpeechBubble(text) {
|
||||||
@@ -868,6 +928,7 @@ export function applyAgentStates(snapshot) {
|
|||||||
if (!snapshot) return;
|
if (!snapshot) return;
|
||||||
for (const [k, v] of Object.entries(snapshot)) {
|
for (const [k, v] of Object.entries(snapshot)) {
|
||||||
if (k in agentStates) agentStates[k] = v;
|
if (k in agentStates) agentStates[k] = v;
|
||||||
|
if (k in _extAgentStates) _extAgentStates[k] = v;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -875,6 +936,115 @@ export function getAgentDefs() {
|
|||||||
return [{ id: 'timmy', label: 'TIMMY', role: 'wizard', color: 0x5599ff, state: deriveTimmyState() }];
|
return [{ id: 'timmy', label: 'TIMMY', role: 'wizard', color: 0x5599ff, state: deriveTimmyState() }];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── External agent 3D body builder ────────────────────────────────────────────
|
||||||
|
function buildExternalAgentBody(def) {
|
||||||
|
const group = new THREE.Group();
|
||||||
|
const col = def.color;
|
||||||
|
const meshes = []; // for disposal
|
||||||
|
const orbiters = []; // { mesh, radius, speed, phase, yOffset }
|
||||||
|
|
||||||
|
if (def.id === 'kimi') {
|
||||||
|
// Kimi: tall tapered obelisk/pillar (CylinderGeometry) + 3 flat ring-discs orbiting it
|
||||||
|
const pillarMat = new THREE.MeshStandardMaterial({
|
||||||
|
color: col,
|
||||||
|
emissive: col,
|
||||||
|
emissiveIntensity: 0.15,
|
||||||
|
roughness: 0.4,
|
||||||
|
metalness: 0.6,
|
||||||
|
});
|
||||||
|
const pillar = new THREE.Mesh(new THREE.CylinderGeometry(0.10, 0.28, 2.2, 6), pillarMat);
|
||||||
|
pillar.position.y = 1.1;
|
||||||
|
pillar.castShadow = true;
|
||||||
|
group.add(pillar);
|
||||||
|
meshes.push(pillar);
|
||||||
|
|
||||||
|
// 3 flat torus-ring discs at different heights, orbiting the pillar
|
||||||
|
const ringMat = new THREE.MeshStandardMaterial({
|
||||||
|
color: col,
|
||||||
|
emissive: col,
|
||||||
|
emissiveIntensity: 0.35,
|
||||||
|
roughness: 0.3,
|
||||||
|
metalness: 0.7,
|
||||||
|
transparent: true,
|
||||||
|
opacity: 0.82,
|
||||||
|
});
|
||||||
|
const ringHeights = [0.6, 1.1, 1.7];
|
||||||
|
const ringRadii = [0.55, 0.45, 0.38];
|
||||||
|
const ringPhases = [0, Math.PI * 0.66, Math.PI * 1.33];
|
||||||
|
const ringSpeed = [0.55, 0.72, 0.48];
|
||||||
|
for (let i = 0; i < 3; i++) {
|
||||||
|
const ring = new THREE.Mesh(
|
||||||
|
new THREE.TorusGeometry(0.18, 0.028, 6, 20),
|
||||||
|
ringMat.clone()
|
||||||
|
);
|
||||||
|
ring.rotation.x = Math.PI / 2;
|
||||||
|
group.add(ring);
|
||||||
|
meshes.push(ring);
|
||||||
|
orbiters.push({ mesh: ring, radius: ringRadii[i], speed: ringSpeed[i], phase: ringPhases[i], yOffset: ringHeights[i] });
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (def.id === 'perplexity') {
|
||||||
|
// Perplexity: icosahedron core + 4 small orbiting dot-spheres
|
||||||
|
const coreGeo = new THREE.IcosahedronGeometry(0.34, 1);
|
||||||
|
const coreMat = new THREE.MeshStandardMaterial({
|
||||||
|
color: col,
|
||||||
|
emissive: col,
|
||||||
|
emissiveIntensity: 0.18,
|
||||||
|
roughness: 0.25,
|
||||||
|
metalness: 0.55,
|
||||||
|
wireframe: false,
|
||||||
|
});
|
||||||
|
const core = new THREE.Mesh(coreGeo, coreMat);
|
||||||
|
core.position.y = 1.1;
|
||||||
|
core.castShadow = true;
|
||||||
|
group.add(core);
|
||||||
|
meshes.push(core);
|
||||||
|
|
||||||
|
// Thin wireframe shell just slightly larger
|
||||||
|
const shellMat = new THREE.MeshBasicMaterial({ color: col, wireframe: true, transparent: true, opacity: 0.22 });
|
||||||
|
const shell = new THREE.Mesh(new THREE.IcosahedronGeometry(0.46, 1), shellMat);
|
||||||
|
shell.position.y = 1.1;
|
||||||
|
group.add(shell);
|
||||||
|
meshes.push(shell);
|
||||||
|
|
||||||
|
// 4 small orbiting spheres
|
||||||
|
const dotMat = new THREE.MeshStandardMaterial({
|
||||||
|
color: col,
|
||||||
|
emissive: col,
|
||||||
|
emissiveIntensity: 0.5,
|
||||||
|
roughness: 0.3,
|
||||||
|
metalness: 0.4,
|
||||||
|
});
|
||||||
|
const dotCount = 4;
|
||||||
|
for (let i = 0; i < dotCount; i++) {
|
||||||
|
const dot = new THREE.Mesh(new THREE.SphereGeometry(0.07, 8, 8), dotMat.clone());
|
||||||
|
group.add(dot);
|
||||||
|
meshes.push(dot);
|
||||||
|
orbiters.push({
|
||||||
|
mesh: dot,
|
||||||
|
radius: 0.58,
|
||||||
|
speed: 0.9 + i * 0.15,
|
||||||
|
phase: (i / dotCount) * Math.PI * 2,
|
||||||
|
yOffset: 1.1 + Math.sin((i / dotCount) * Math.PI * 2) * 0.22,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shared point light for glow
|
||||||
|
const light = new THREE.PointLight(col, 0.6, 4.5);
|
||||||
|
light.position.y = 1.1;
|
||||||
|
group.add(light);
|
||||||
|
|
||||||
|
// Small base platform
|
||||||
|
const baseMat = new THREE.MeshStandardMaterial({ color: col, emissive: col, emissiveIntensity: 0.08, roughness: 0.8, metalness: 0.3 });
|
||||||
|
const base = new THREE.Mesh(new THREE.CylinderGeometry(0.32, 0.38, 0.08, 8), baseMat);
|
||||||
|
base.position.y = 0.04;
|
||||||
|
group.add(base);
|
||||||
|
meshes.push(base);
|
||||||
|
|
||||||
|
return { id: def.id, group, meshes, orbiters, light, def };
|
||||||
|
}
|
||||||
|
|
||||||
export function disposeAgents() {
|
export function disposeAgents() {
|
||||||
if (!timmy) return;
|
if (!timmy) return;
|
||||||
[timmy.robe, timmy.head, timmy.hat, timmy.cb, timmy.pip].forEach(m => {
|
[timmy.robe, timmy.head, timmy.hat, timmy.cb, timmy.pip].forEach(m => {
|
||||||
@@ -889,5 +1059,17 @@ export function disposeAgents() {
|
|||||||
timmy.bubbleTex?.dispose();
|
timmy.bubbleTex?.dispose();
|
||||||
timmy.bubbleMat?.dispose();
|
timmy.bubbleMat?.dispose();
|
||||||
timmy = null;
|
timmy = null;
|
||||||
|
|
||||||
|
// Dispose external agent bodies
|
||||||
|
for (const agent of _extAgents) {
|
||||||
|
for (const mesh of agent.meshes) {
|
||||||
|
mesh.geometry?.dispose();
|
||||||
|
if (Array.isArray(mesh.material)) mesh.material.forEach(m => m.dispose());
|
||||||
|
else mesh.material?.dispose();
|
||||||
|
}
|
||||||
|
scene?.remove(agent.group);
|
||||||
|
}
|
||||||
|
_extAgents.length = 0;
|
||||||
|
|
||||||
scene = null;
|
scene = null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ let _camera = null;
|
|||||||
let _labels = []; // { el, worldPos: THREE.Vector3, id }
|
let _labels = []; // { el, worldPos: THREE.Vector3, id }
|
||||||
|
|
||||||
// ── State cache (updated from WS) ────────────────────────────────────────────
|
// ── State cache (updated from WS) ────────────────────────────────────────────
|
||||||
const _states = {};
|
const _states = {};
|
||||||
|
const _lastTasks = {}; // id -> last task summary string
|
||||||
|
|
||||||
// ── Inspect popup ─────────────────────────────────────────────────────────────
|
// ── Inspect popup ─────────────────────────────────────────────────────────────
|
||||||
let _inspectEl = null;
|
let _inspectEl = null;
|
||||||
@@ -44,7 +45,7 @@ export function initHudLabels(camera, agentDefs, timmyWorldPos) {
|
|||||||
for (const def of agentDefs) {
|
for (const def of agentDefs) {
|
||||||
const col = colorToCss(def.color);
|
const col = colorToCss(def.color);
|
||||||
const pos = new THREE.Vector3(def.x, 2.8, def.z);
|
const pos = new THREE.Vector3(def.x, 2.8, def.z);
|
||||||
_labels.push(_makeLabel(container, def.id, def.label, def.role, col, pos));
|
_labels.push(_makeLabel(container, def.id, def.label, def.role, col, pos, def.specialization));
|
||||||
_states[def.id] = 'idle';
|
_states[def.id] = 'idle';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ export function initHudLabels(camera, agentDefs, timmyWorldPos) {
|
|||||||
document.body.appendChild(_inspectEl);
|
document.body.appendChild(_inspectEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _makeLabel(container, id, name, role, color, worldPos) {
|
function _makeLabel(container, id, name, role, color, worldPos, specialization) {
|
||||||
const el = document.createElement('div');
|
const el = document.createElement('div');
|
||||||
el.className = 'ar-label';
|
el.className = 'ar-label';
|
||||||
el.dataset.id = id;
|
el.dataset.id = id;
|
||||||
@@ -97,7 +98,7 @@ function _makeLabel(container, id, name, role, color, worldPos) {
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
container.appendChild(el);
|
container.appendChild(el);
|
||||||
return { el, worldPos, id, color };
|
return { el, worldPos, id, color, specialization: specialization || null };
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setLabelState(id, state) {
|
export function setLabelState(id, state) {
|
||||||
@@ -111,18 +112,31 @@ export function setLabelState(id, state) {
|
|||||||
if (dot) dot.style.animation = pulse ? 'ar-pulse 1s ease-in-out infinite' : '';
|
if (dot) dot.style.animation = pulse ? 'ar-pulse 1s ease-in-out infinite' : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setAgentLastTask(id, taskSummary) {
|
||||||
|
_lastTasks[id] = taskSummary;
|
||||||
|
}
|
||||||
|
|
||||||
export function showInspectPopup(id, screenX, screenY) {
|
export function showInspectPopup(id, screenX, screenY) {
|
||||||
if (!_inspectEl) return;
|
if (!_inspectEl) return;
|
||||||
const entry = _labels.find(l => l.id === id);
|
const entry = _labels.find(l => l.id === id);
|
||||||
if (!entry) return;
|
if (!entry) return;
|
||||||
|
|
||||||
const state = _states[id] || 'idle';
|
const state = _states[id] || 'idle';
|
||||||
const uptime = Math.floor(performance.now() / 1000);
|
const uptime = Math.floor(performance.now() / 1000);
|
||||||
|
const specLine = entry.specialization
|
||||||
|
? `<div style="color:#aaa;margin-bottom:2px;">spec : <span style="color:${entry.color}88">${entry.specialization}</span></div>`
|
||||||
|
: '';
|
||||||
|
const lastTask = _lastTasks[id];
|
||||||
|
const taskLine = lastTask
|
||||||
|
? `<div style="color:#aaa;margin-bottom:2px;max-width:200px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">last : <span style="color:#cccccc">${lastTask}</span></div>`
|
||||||
|
: '';
|
||||||
_inspectEl.innerHTML = `
|
_inspectEl.innerHTML = `
|
||||||
<div style="color:${entry.color};font-weight:bold;letter-spacing:2px;font-size:12px;margin-bottom:6px;">
|
<div style="color:${entry.color};font-weight:bold;letter-spacing:2px;font-size:12px;margin-bottom:6px;">
|
||||||
${id.toUpperCase()}
|
${id.toUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
<div style="color:#aaa;margin-bottom:2px;">state : <span style="color:${entry.color}">${state}</span></div>
|
<div style="color:#aaa;margin-bottom:2px;">state : <span style="color:${entry.color}">${state}</span></div>
|
||||||
|
${specLine}
|
||||||
|
${taskLine}
|
||||||
<div style="color:#aaa;margin-bottom:2px;">uptime : ${uptime}s</div>
|
<div style="color:#aaa;margin-bottom:2px;">uptime : ${uptime}s</div>
|
||||||
<div style="color:#aaa;">network: <span style="color:#44ff88">connected</span></div>
|
<div style="color:#aaa;">network: <span style="color:#44ff88">connected</span></div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { setAgentState, setSpeechBubble, applyAgentStates, setMood } from './agents.js';
|
import { setAgentState, setSpeechBubble, applyAgentStates, setMood } from './agents.js';
|
||||||
import { appendSystemMessage, appendDebateMessage, showCostTicker, updateCostTicker } from './ui.js';
|
import { appendSystemMessage, appendDebateMessage, showCostTicker, updateCostTicker } from './ui.js';
|
||||||
import { sentiment } from './edge-worker-client.js';
|
import { sentiment } from './edge-worker-client.js';
|
||||||
import { setLabelState } from './hud-labels.js';
|
import { setLabelState, setAgentLastTask } from './hud-labels.js';
|
||||||
|
|
||||||
function resolveWsUrl() {
|
function resolveWsUrl() {
|
||||||
const explicit = import.meta.env.VITE_WS_URL;
|
const explicit = import.meta.env.VITE_WS_URL;
|
||||||
@@ -103,6 +103,10 @@ function handleMessage(msg) {
|
|||||||
if (msg.agentId) {
|
if (msg.agentId) {
|
||||||
setAgentState(msg.agentId, 'idle');
|
setAgentState(msg.agentId, 'idle');
|
||||||
setLabelState(msg.agentId, 'idle');
|
setLabelState(msg.agentId, 'idle');
|
||||||
|
if (msg.agentId === 'kimi' || msg.agentId === 'perplexity') {
|
||||||
|
const summary = msg.summary || msg.result || `job ${(msg.jobId || '').slice(0, 8)}`;
|
||||||
|
setAgentLastTask(msg.agentId, summary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
appendSystemMessage(`job ${(msg.jobId || '').slice(0, 8)} complete`);
|
appendSystemMessage(`job ${(msg.jobId || '').slice(0, 8)} complete`);
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user