@agentprojectcontext/apx 1.71.0 → 1.73.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/interfaces/web/dist/assets/index-CUeIhw7z.css +1 -0
- package/src/interfaces/web/dist/assets/index-D2b7Sqvg.js +819 -0
- package/src/interfaces/web/dist/assets/index-D2b7Sqvg.js.map +1 -0
- package/src/interfaces/web/dist/index.html +2 -2
- package/src/interfaces/web/src/i18n/en.ts +11 -0
- package/src/interfaces/web/src/i18n/es.ts +11 -0
- package/src/interfaces/web/src/screens/project/AgentBrainGraph.tsx +301 -142
- package/src/interfaces/web/src/screens/project/Overview.tsx +95 -20
- package/src/interfaces/web/dist/assets/index-CDz9OwCP.css +0 -1
- package/src/interfaces/web/dist/assets/index-CX15mZXM.js +0 -798
- package/src/interfaces/web/dist/assets/index-CX15mZXM.js.map +0 -1
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { useMemo } from "react";
|
|
1
|
+
import { useMemo, useState } from "react";
|
|
2
2
|
import useSWR from "swr";
|
|
3
3
|
import { NavLink, useNavigate } from "react-router-dom";
|
|
4
|
-
import { Bot, Briefcase, FileCode2, Heart, MessagesSquare, Puzzle, Zap, Crown, Activity } from "lucide-react";
|
|
5
|
-
import { Agents, Artifacts, Mcps, Routines, Tasks } from "../../lib/api";
|
|
4
|
+
import { Bot, Briefcase, Brain, FileCode2, Heart, MessagesSquare, Puzzle, Zap, Crown, Activity } from "lucide-react";
|
|
5
|
+
import { Agents, Artifacts, Conversations, Mcps, Routines, Tasks } from "../../lib/api";
|
|
6
6
|
import { Section } from "../../components/Section";
|
|
7
7
|
import { StatusIcon, StatusBadge, effectiveStatus, statusLabel, TASK_STATUS_ORDER } from "../../components/tasks/taskStatus";
|
|
8
8
|
import { BrainGraph, type BrainNode, type BrainEdge } from "./AgentBrainGraph";
|
|
9
9
|
import { cn } from "../../lib/cn";
|
|
10
10
|
import { t } from "../../i18n";
|
|
11
|
-
import type { AgentEntry } from "../../types/daemon";
|
|
11
|
+
import type { AgentEntry, RoutineEntry, TaskEntry } from "../../types/daemon";
|
|
12
12
|
|
|
13
13
|
// Floor / mission control: a live per-project summary — what's here (agents,
|
|
14
14
|
// automation), what's in flight (task workflow), and what just happened.
|
|
@@ -59,14 +59,6 @@ export function Overview({ pid }: { pid: string }) {
|
|
|
59
59
|
</div>
|
|
60
60
|
)}
|
|
61
61
|
|
|
62
|
-
{/* Team brain — the whole agent map: orchestrators at the core, their
|
|
63
|
-
specialists clustered around them as satellites, all connected. */}
|
|
64
|
-
{agentList.length > 0 && (
|
|
65
|
-
<Section title={t("project.overview.brain_title")} description={t("project.overview.brain_desc")} className="!p-4">
|
|
66
|
-
<TeamBrain pid={pid} agents={agentList} navigate={navigate} />
|
|
67
|
-
</Section>
|
|
68
|
-
)}
|
|
69
|
-
|
|
70
62
|
<div className="grid gap-4 lg:grid-cols-2">
|
|
71
63
|
{/* Agent roster */}
|
|
72
64
|
<Section title={t("project.overview.roster")} className="!p-4">
|
|
@@ -129,6 +121,15 @@ export function Overview({ pid }: { pid: string }) {
|
|
|
129
121
|
<Card title={t("project.overview.mcps")} value={mcps.data?.length ?? "…"} href={`/p/${pid}/mcps`} icon={Puzzle} />
|
|
130
122
|
<Card title={t("project.overview.routines")} value={routines.data?.length ?? "…"} href={`/p/${pid}/routines`} icon={Heart} />
|
|
131
123
|
</div>
|
|
124
|
+
|
|
125
|
+
{/* Team brain — full-width at the bottom. Collapsed: the agent map.
|
|
126
|
+
Expanded: every agent's full sub-brain (memory / threads / tasks /
|
|
127
|
+
heartbeats), all connected by hierarchy. */}
|
|
128
|
+
{agentList.length > 0 && (
|
|
129
|
+
<Section title={t("project.overview.brain_title")} description={t("project.overview.brain_desc")} className="!p-4">
|
|
130
|
+
<TeamBrain pid={pid} agents={agentList} routines={routines.data ?? []} navigate={navigate} />
|
|
131
|
+
</Section>
|
|
132
|
+
)}
|
|
132
133
|
</div>
|
|
133
134
|
);
|
|
134
135
|
}
|
|
@@ -147,14 +148,54 @@ function groupByArea(agents: AgentEntry[]): { area: string | null; agents: Agent
|
|
|
147
148
|
.map(([area, agents]) => ({ area, agents }));
|
|
148
149
|
}
|
|
149
150
|
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
// One memory-fact-per-line, trimmed of markdown noise (mirrors the per-agent brain).
|
|
152
|
+
function memoryFacts(text: string): string[] {
|
|
153
|
+
return text
|
|
154
|
+
.split("\n")
|
|
155
|
+
.map((l) => l.replace(/^[-*#>\s]+/, "").trim())
|
|
156
|
+
.filter((l) => l.length > 2 && !l.startsWith("```"))
|
|
157
|
+
.slice(0, 5);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
interface TeamDetail {
|
|
161
|
+
tasks: TaskEntry[];
|
|
162
|
+
perAgent: Record<string, { memory: string; threads: { title: string; id: string }[] }>;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Whole-project agent map. Collapsed, the core is the team and every agent hangs
|
|
166
|
+
// off its parent (orchestrators → specialists) as satellite clusters. Expanded,
|
|
167
|
+
// each agent becomes a hub with its own sub-brain — memory / threads / tasks /
|
|
168
|
+
// heartbeats — so the whole company reads as one connected brain-of-brains.
|
|
153
169
|
function TeamBrain({
|
|
154
|
-
pid, agents, navigate,
|
|
170
|
+
pid, agents, routines, navigate,
|
|
155
171
|
}: {
|
|
156
|
-
pid: string; agents: AgentEntry[]; navigate: (to: string) => void;
|
|
172
|
+
pid: string; agents: AgentEntry[]; routines: RoutineEntry[]; navigate: (to: string) => void;
|
|
157
173
|
}) {
|
|
174
|
+
const [expanded, setExpanded] = useState(false);
|
|
175
|
+
|
|
176
|
+
// Only fetch the heavy per-agent data when the user asks to expand.
|
|
177
|
+
const detail = useSWR<TeamDetail | null>(
|
|
178
|
+
expanded ? `/team-brain/${pid}/${agents.map((a) => a.slug).join(",")}` : null,
|
|
179
|
+
async () => {
|
|
180
|
+
const tasks = await Tasks.list(pid, "all");
|
|
181
|
+
const entries = await Promise.all(
|
|
182
|
+
agents.map(async (a) => {
|
|
183
|
+
const [d, threads] = await Promise.all([
|
|
184
|
+
Agents.get(pid, a.slug).catch(() => null),
|
|
185
|
+
Conversations.list(pid, a.slug).catch(() => []),
|
|
186
|
+
]);
|
|
187
|
+
return [a.slug, {
|
|
188
|
+
memory: d?.memory || "",
|
|
189
|
+
threads: (threads || []).slice(0, 4).map((th) => ({ title: th.title || th.filename, id: th.id })),
|
|
190
|
+
}] as const;
|
|
191
|
+
}),
|
|
192
|
+
);
|
|
193
|
+
return { tasks, perAgent: Object.fromEntries(entries) };
|
|
194
|
+
},
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
const showFull = expanded && !!detail.data;
|
|
198
|
+
|
|
158
199
|
const { nodes, edges } = useMemo(() => {
|
|
159
200
|
const nodes: BrainNode[] = [];
|
|
160
201
|
const edges: BrainEdge[] = [];
|
|
@@ -171,7 +212,8 @@ function TeamBrain({
|
|
|
171
212
|
label: a.slug,
|
|
172
213
|
slug: a.slug,
|
|
173
214
|
kind: isOrch ? "agent" : "agentlink",
|
|
174
|
-
|
|
215
|
+
// In full mode every agent is a hub (it carries its own sub-brain).
|
|
216
|
+
role: showFull || isOrch || hasKids(a.slug) ? "hub" : "leaf",
|
|
175
217
|
emoji: a.emoji || undefined,
|
|
176
218
|
relation: a.role || (isOrch ? t("project.agents.orchestrator") : t("project.overview.specialists")),
|
|
177
219
|
detail: a.description || undefined,
|
|
@@ -181,14 +223,47 @@ function TeamBrain({
|
|
|
181
223
|
const parent = a.parent && slugs.has(a.parent) ? a.parent : ROOT;
|
|
182
224
|
edges.push({ source: parent, target: a.slug });
|
|
183
225
|
}
|
|
226
|
+
|
|
227
|
+
// Expanded: graft each agent's own items as leaves off the agent node.
|
|
228
|
+
if (showFull && detail.data) {
|
|
229
|
+
const { tasks, perAgent } = detail.data;
|
|
230
|
+
const push = (id: string, label: string, kind: BrainNode["kind"], parent: string, detailText?: string) => {
|
|
231
|
+
nodes.push({ id, label, kind, detail: detailText });
|
|
232
|
+
edges.push({ source: parent, target: id });
|
|
233
|
+
};
|
|
234
|
+
for (const a of agents) {
|
|
235
|
+
const info = perAgent[a.slug];
|
|
236
|
+
memoryFacts(info?.memory || "").forEach((f, i) => push(`${a.slug}:m${i}`, f, "memory", a.slug, f));
|
|
237
|
+
(info?.threads || []).forEach((th, i) => push(`${a.slug}:th${i}`, th.title, "thread", a.slug));
|
|
238
|
+
tasks.filter((tk) => tk.agent === a.slug).slice(0, 4)
|
|
239
|
+
.forEach((tk, i) => push(`${a.slug}:ts${i}`, tk.title, "task", a.slug, tk.body || undefined));
|
|
240
|
+
routines.filter((r) => (r.spec as { agent?: string })?.agent === a.slug).slice(0, 2)
|
|
241
|
+
.forEach((r, i) => push(`${a.slug}:rt${i}`, r.name, "routine", a.slug, `schedule: ${r.schedule}`));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
184
244
|
return { nodes, edges };
|
|
185
|
-
}, [agents]);
|
|
245
|
+
}, [agents, routines, showFull, detail.data]);
|
|
246
|
+
|
|
247
|
+
const toggle = (
|
|
248
|
+
<button
|
|
249
|
+
type="button"
|
|
250
|
+
onClick={() => setExpanded((e) => !e)}
|
|
251
|
+
className={cn(
|
|
252
|
+
"inline-flex items-center gap-1.5 rounded-md border px-2 py-1 text-[11px] font-medium backdrop-blur transition-colors",
|
|
253
|
+
expanded ? "border-primary/40 bg-primary/15 text-foreground" : "border-border bg-card/80 text-muted-fg hover:text-foreground",
|
|
254
|
+
)}
|
|
255
|
+
>
|
|
256
|
+
<Brain className={cn("size-3.5", detail.isLoading && "animate-pulse")} />
|
|
257
|
+
{expanded ? t("agents_ui.brain_collapse") : t("agents_ui.brain_expand")}
|
|
258
|
+
</button>
|
|
259
|
+
);
|
|
186
260
|
|
|
187
261
|
return (
|
|
188
262
|
<BrainGraph
|
|
189
263
|
nodes={nodes}
|
|
190
264
|
edges={edges}
|
|
191
|
-
height={
|
|
265
|
+
height={620}
|
|
266
|
+
toolbar={toggle}
|
|
192
267
|
onNodeClick={(n) => { if (n.slug) navigate(`/p/${pid}/agents/${n.slug}`); }}
|
|
193
268
|
/>
|
|
194
269
|
);
|