feat(observe): surface delegation + A2A in the live world feed via audit poll
Edge-initiated inter-agent events (gated delegation, A2A ingress) bypass the
run loop, so the world SSE now polls the append-only audit log (cursor on the
BIGINT id, seeded to max on first pass) and emits:
- delegation.invoked -> agent.delegate {fromAgentId,toAgentId,toName,task}
- a2a.invoked -> a2a.invoked {agentId}
TeamObserver renders agent.delegate as an A->B handoff in the team timeline;
adds the agent.delegate taxonomy type. Reliable live (the synthesized-run path
never streamed — active_runs + first-sight cursor jump skip it).
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
7352ed47ab
commit
85b0e1ac33
@@ -7,7 +7,7 @@
|
||||
// A2A invocations among team members as they happen. No composer — observe only.
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Users, MessagesSquare, Hash, Globe } from "lucide-react";
|
||||
import { Users, MessagesSquare, Hash, Globe, GitBranch } from "lucide-react";
|
||||
|
||||
import type { Agent } from "@/lib/api/schemas";
|
||||
import { useFetchJson } from "@/lib/api/use-fetch";
|
||||
@@ -24,9 +24,10 @@ interface Room {
|
||||
type FeedItem = {
|
||||
key: number;
|
||||
ts: string;
|
||||
kind: "msg" | "room" | "a2a";
|
||||
kind: "msg" | "room" | "a2a" | "delegate";
|
||||
fromId: string;
|
||||
toId?: string;
|
||||
toName?: string;
|
||||
subject?: string;
|
||||
text: string;
|
||||
};
|
||||
@@ -110,6 +111,11 @@ export function TeamObserver({ agent }: { agent: Agent }) {
|
||||
push({ kind: "a2a", fromId: d.agentId, text: d.skill ? `skill: ${d.skill}` : "external task" });
|
||||
}
|
||||
});
|
||||
useLiveEvent("agent.delegate", (d) => {
|
||||
if (members.has(d.fromAgentId) || members.has(d.toAgentId)) {
|
||||
push({ kind: "delegate", fromId: d.fromAgentId, toId: d.toAgentId, toName: d.toName, text: d.task || "sub-task" });
|
||||
}
|
||||
});
|
||||
|
||||
const rooms = (useFetchJson<Room[]>("/api/claw-chat/rooms").data ?? []).filter(
|
||||
(r) => r.kind === "room" && r.participants.some((id) => members.has(id)),
|
||||
@@ -157,6 +163,8 @@ export function TeamObserver({ agent }: { agent: Agent }) {
|
||||
<Hash aria-hidden size={11} className="text-muted-foreground" />
|
||||
) : f.kind === "a2a" ? (
|
||||
<Globe aria-hidden size={11} className="text-[#c98af0]" />
|
||||
) : f.kind === "delegate" ? (
|
||||
<GitBranch aria-hidden size={11} className="text-coral" />
|
||||
) : (
|
||||
<MessagesSquare aria-hidden size={11} className="text-muted-foreground" />
|
||||
)}
|
||||
@@ -165,7 +173,9 @@ export function TeamObserver({ agent }: { agent: Agent }) {
|
||||
? `${nameOf(f.fromId)} → #${f.subject || "room"}`
|
||||
: f.kind === "a2a"
|
||||
? `external A2A → ${nameOf(f.fromId)}`
|
||||
: `${nameOf(f.fromId)} → ${f.toId ? nameOf(f.toId) : "?"}`}
|
||||
: f.kind === "delegate"
|
||||
? `${nameOf(f.fromId)} ⇒ ${f.toName || (f.toId ? nameOf(f.toId) : "?")}`
|
||||
: `${nameOf(f.fromId)} → ${f.toId ? nameOf(f.toId) : "?"}`}
|
||||
</span>
|
||||
<span className="ml-auto text-xxs text-muted-foreground">{relativeTime(f.ts)}</span>
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user