Replaces the static React-Flow world forest with a real-time WebGL view of the swarm working, plus the live event contract that feeds it. Phase A — the live contract + feed: - frontend/src/lib/live/taxonomy.ts — the 13-event Clawmates Event Taxonomy as typed TS (the shared World/Observe contract). - frontend/src/lib/live/useClawmatesLive.ts — native shared-singleton live client (EventSource to /api/world/live, typed fan-out, replay-of-last-state to late subscribers, refcounted, synthetic fallback so views are always alive). - crates/cm-api/src/routes/world.rs — GET /api/world/live, authed + workspace- scoped SSE emitting the taxonomy (real agent.status from live-container state, a topology.update of the workspace's agents, telemetry with real doorsPending), mirroring run_events_sse. normalize() seam documented for run_events->world.touch. Phase B — the WebGL engine: - frontend/src/components/world/engine.ts — Gource-inspired force-directed model: org/company/team tree (sibling repulsion + parent spring + friction), agent pawns that converge on the touched node and beam it, world nodes that glow with heat and fade when idle. Framework-agnostic (renderer-independent) state+math. - frontend/src/components/world/WorldCanvas.tsx — three.js scene (ortho cam, UnrealBloom), render loop syncing engine state, camera auto-fit, HTML labels, raycast click->select, Hierarchy/Flat/Live formation switch. - Dashboard.tsx: swap <WorldFlow/> -> <WorldCanvas/> at the world-tier seam (shared claw-tier pieces untouched; WorldFlow.tsx kept for now). - Adds three (+ @types/three). Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
200 lines
6.7 KiB
JSON
200 lines
6.7 KiB
JSON
{
|
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
"$id": "https://clawmates.work/schemas/events.schema.json",
|
|
"title": "Clawmates Event Taxonomy",
|
|
"description": "Normalized real-time events emitted by the live bridge and consumed by the visualizations. Each SSE frame is `event: <type>` + `data: <object matching the matching definition>`.",
|
|
"type": "object",
|
|
"oneOf": [
|
|
{ "$ref": "#/definitions/agent.status" },
|
|
{ "$ref": "#/definitions/agent.task.update" },
|
|
{ "$ref": "#/definitions/agent.reasoning.delta" },
|
|
{ "$ref": "#/definitions/agent.computer.frame" },
|
|
{ "$ref": "#/definitions/agent.tool.call" },
|
|
{ "$ref": "#/definitions/door.request" },
|
|
{ "$ref": "#/definitions/door.resolve" },
|
|
{ "$ref": "#/definitions/agent.message" },
|
|
{ "$ref": "#/definitions/world.touch" },
|
|
{ "$ref": "#/definitions/node.activity" },
|
|
{ "$ref": "#/definitions/topology.update" },
|
|
{ "$ref": "#/definitions/telemetry" },
|
|
{ "$ref": "#/definitions/routine.update" }
|
|
],
|
|
"definitions": {
|
|
"agent.status": {
|
|
"type": "object",
|
|
"required": ["agentId", "status"],
|
|
"properties": {
|
|
"agentId": { "type": "string" },
|
|
"status": { "enum": ["online", "working", "idle", "offline"] },
|
|
"role": { "type": "string" }
|
|
}
|
|
},
|
|
"agent.task.update": {
|
|
"type": "object",
|
|
"required": ["agentId", "taskId", "title"],
|
|
"properties": {
|
|
"agentId": { "type": "string" },
|
|
"taskId": { "type": "string" },
|
|
"title": { "type": "string" },
|
|
"elapsedMs": { "type": "integer", "minimum": 0 },
|
|
"steps": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"required": ["label", "state"],
|
|
"properties": {
|
|
"label": { "type": "string" },
|
|
"state": { "enum": ["done", "active", "pending"] }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"agent.reasoning.delta": {
|
|
"type": "object",
|
|
"required": ["agentId", "text"],
|
|
"properties": {
|
|
"agentId": { "type": "string" },
|
|
"text": { "type": "string", "description": "Token chunk to APPEND to the reasoning stream." },
|
|
"channel": { "enum": ["think", "say", "tool"], "default": "think" }
|
|
}
|
|
},
|
|
"agent.computer.frame": {
|
|
"type": "object",
|
|
"required": ["agentId", "app"],
|
|
"properties": {
|
|
"agentId": { "type": "string" },
|
|
"app": { "enum": ["browser", "terminal", "slack", "claw-chat"] },
|
|
"url": { "type": "string" },
|
|
"lines": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"required": ["text"],
|
|
"properties": {
|
|
"text": { "type": "string" },
|
|
"kind": { "enum": ["plain", "add", "del", "ok", "warn", "cursor"], "default": "plain" }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"agent.tool.call": {
|
|
"type": "object",
|
|
"required": ["agentId", "tool"],
|
|
"properties": {
|
|
"agentId": { "type": "string" },
|
|
"tool": { "type": "string" },
|
|
"target": { "type": "string" },
|
|
"doorRequired": { "type": "boolean", "default": false }
|
|
}
|
|
},
|
|
"door.request": {
|
|
"type": "object",
|
|
"required": ["doorId", "agentId", "action"],
|
|
"properties": {
|
|
"doorId": { "type": "string" },
|
|
"agentId": { "type": "string" },
|
|
"action": { "type": "string", "description": "e.g. github.review.comment" },
|
|
"target": { "type": "string" },
|
|
"summary": { "type": "string" }
|
|
}
|
|
},
|
|
"door.resolve": {
|
|
"type": "object",
|
|
"required": ["doorId", "decision"],
|
|
"properties": {
|
|
"doorId": { "type": "string" },
|
|
"decision": { "enum": ["approve", "deny"] },
|
|
"by": { "type": "string" }
|
|
}
|
|
},
|
|
"agent.message": {
|
|
"type": "object",
|
|
"required": ["fromAgentId", "toAgentId", "text"],
|
|
"properties": {
|
|
"fromAgentId": { "type": "string" },
|
|
"toAgentId": { "type": "string" },
|
|
"text": { "type": "string" },
|
|
"ts": { "type": "string", "format": "date-time" }
|
|
}
|
|
},
|
|
"world.touch": {
|
|
"type": "object",
|
|
"description": "An agent is converging on a project/service/event node (Gource). Drives the Live canvas retarget + beam.",
|
|
"required": ["agentId", "nodeId"],
|
|
"properties": {
|
|
"agentId": { "type": "string" },
|
|
"nodeId": { "type": "string" },
|
|
"kind": { "enum": ["service", "event"], "default": "service" },
|
|
"weight": { "type": "number", "minimum": 0, "maximum": 1, "default": 1 }
|
|
}
|
|
},
|
|
"node.activity": {
|
|
"type": "object",
|
|
"required": ["nodeId"],
|
|
"properties": {
|
|
"nodeId": { "type": "string" },
|
|
"label": { "type": "string" },
|
|
"kind": { "enum": ["service", "event"] },
|
|
"heat": { "type": "number", "minimum": 0, "maximum": 1 }
|
|
}
|
|
},
|
|
"topology.update": {
|
|
"type": "object",
|
|
"description": "Full or partial re-layout of the world graph.",
|
|
"required": ["formation"],
|
|
"properties": {
|
|
"formation": { "enum": ["hierarchy", "flat", "live"] },
|
|
"nodes": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"required": ["id", "tier"],
|
|
"properties": {
|
|
"id": { "type": "string" },
|
|
"tier": { "enum": ["org", "company", "team", "agent", "service", "event"] },
|
|
"label": { "type": "string" },
|
|
"parentId": { "type": "string" }
|
|
}
|
|
}
|
|
},
|
|
"edges": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"required": ["from", "to"],
|
|
"properties": {
|
|
"from": { "type": "string" },
|
|
"to": { "type": "string" },
|
|
"kind": { "enum": ["parent", "peer", "flow"], "default": "parent" }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"telemetry": {
|
|
"type": "object",
|
|
"properties": {
|
|
"tokensPerMin": { "type": "number" },
|
|
"costPerHr": { "type": "number" },
|
|
"loops": { "type": "integer" },
|
|
"doorsPending": { "type": "integer" }
|
|
}
|
|
},
|
|
"routine.update": {
|
|
"type": "object",
|
|
"required": ["routineId", "name", "kind"],
|
|
"properties": {
|
|
"routineId": { "type": "string" },
|
|
"name": { "type": "string" },
|
|
"kind": { "enum": ["cron", "loop"] },
|
|
"owner": { "type": "string" },
|
|
"schedule": { "type": "string", "description": "cron expr or next-run hint" },
|
|
"progress": { "type": "number", "minimum": 0, "maximum": 1 },
|
|
"state": { "enum": ["running", "scheduled", "done", "failed", "door"] }
|
|
}
|
|
}
|
|
}
|
|
}
|