agentopia 1.1.0 → 1.1.1
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/README.md +13 -2
- package/package.json +1 -1
- package/public/js/project.js +66 -51
package/README.md
CHANGED
|
@@ -92,7 +92,18 @@ Agentopia creates a **shared workspace** where multiple AI agents operate as a t
|
|
|
92
92
|
|
|
93
93
|
- Node.js >= 18
|
|
94
94
|
|
|
95
|
-
### Install
|
|
95
|
+
### Option 1: Global Install (recommended)
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npm install -g agentopia
|
|
99
|
+
|
|
100
|
+
# Start the server (database created in current directory)
|
|
101
|
+
agentopia
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
That's it. Open `http://localhost:4567`.
|
|
105
|
+
|
|
106
|
+
### Option 2: Clone & Run
|
|
96
107
|
|
|
97
108
|
```bash
|
|
98
109
|
git clone https://github.com/fuzihaofzh/agentopia.git
|
|
@@ -106,7 +117,7 @@ npm run dev
|
|
|
106
117
|
npm run build && npm start
|
|
107
118
|
```
|
|
108
119
|
|
|
109
|
-
|
|
120
|
+
On first visit, you'll be prompted to create an admin account.
|
|
110
121
|
|
|
111
122
|
### Configuration
|
|
112
123
|
|
package/package.json
CHANGED
package/public/js/project.js
CHANGED
|
@@ -1888,79 +1888,92 @@ function renderStarAgentGraph(container, graphContext) {
|
|
|
1888
1888
|
|
|
1889
1889
|
function renderHierarchyAgentGraph(container, graphContext) {
|
|
1890
1890
|
const byId = getAgentMap();
|
|
1891
|
-
const controller = getControllerAgent();
|
|
1892
|
-
const levelMap = new Map();
|
|
1893
1891
|
const visited = new Set();
|
|
1894
|
-
const
|
|
1892
|
+
const childrenMap = new Map(); // parentId -> [agent]
|
|
1895
1893
|
|
|
1896
|
-
|
|
1897
|
-
if (!agent || visited.has(agent.id)) return;
|
|
1898
|
-
visited.add(agent.id);
|
|
1899
|
-
if (!levelMap.has(depth)) levelMap.set(depth, []);
|
|
1900
|
-
levelMap.get(depth).push(agent);
|
|
1901
|
-
|
|
1902
|
-
const children = agentsData.filter((candidate) => getGraphParentId(candidate) === agent.id);
|
|
1903
|
-
children.forEach((child) => {
|
|
1904
|
-
if (!child.parent_agent_id) {
|
|
1905
|
-
syntheticLinks.push(child.id);
|
|
1906
|
-
}
|
|
1907
|
-
walk(child, depth + 1);
|
|
1908
|
-
});
|
|
1909
|
-
}
|
|
1910
|
-
|
|
1911
|
-
const roots = [];
|
|
1912
|
-
if (controller) {
|
|
1913
|
-
roots.push(controller);
|
|
1914
|
-
}
|
|
1894
|
+
// Build children map using only explicit parent_agent_id
|
|
1915
1895
|
agentsData.forEach((agent) => {
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1896
|
+
const pid = agent.parent_agent_id;
|
|
1897
|
+
if (pid && byId.has(pid)) {
|
|
1898
|
+
if (!childrenMap.has(pid)) childrenMap.set(pid, []);
|
|
1899
|
+
childrenMap.get(pid).push(agent);
|
|
1920
1900
|
}
|
|
1921
1901
|
});
|
|
1922
1902
|
|
|
1903
|
+
// Identify roots: agents with no explicit parent or whose parent doesn't exist
|
|
1904
|
+
const roots = agentsData.filter((agent) => {
|
|
1905
|
+
const pid = agent.parent_agent_id;
|
|
1906
|
+
return !pid || !byId.has(pid);
|
|
1907
|
+
});
|
|
1908
|
+
|
|
1909
|
+
// Build subtree sizes for proper horizontal spacing
|
|
1910
|
+
const subtreeSize = new Map();
|
|
1911
|
+
function calcSize(agent) {
|
|
1912
|
+
if (subtreeSize.has(agent.id)) return subtreeSize.get(agent.id);
|
|
1913
|
+
const children = childrenMap.get(agent.id) || [];
|
|
1914
|
+
const size = children.length === 0 ? 1 : children.reduce((sum, c) => sum + calcSize(c), 0);
|
|
1915
|
+
subtreeSize.set(agent.id, size);
|
|
1916
|
+
return size;
|
|
1917
|
+
}
|
|
1918
|
+
roots.forEach((r) => calcSize(r));
|
|
1919
|
+
|
|
1920
|
+
// Walk tree to assign depth levels
|
|
1921
|
+
const depthMap = new Map();
|
|
1922
|
+
function walk(agent, depth) {
|
|
1923
|
+
if (!agent || visited.has(agent.id)) return;
|
|
1924
|
+
visited.add(agent.id);
|
|
1925
|
+
depthMap.set(agent.id, depth);
|
|
1926
|
+
(childrenMap.get(agent.id) || []).forEach((child) => walk(child, depth + 1));
|
|
1927
|
+
}
|
|
1923
1928
|
roots.forEach((root) => walk(root, 0));
|
|
1929
|
+
// Safety: visit any unvisited agents as roots
|
|
1924
1930
|
agentsData.forEach((agent) => {
|
|
1925
1931
|
if (!visited.has(agent.id)) walk(agent, 0);
|
|
1926
1932
|
});
|
|
1927
1933
|
|
|
1928
|
-
const
|
|
1929
|
-
.sort((a, b) => a[0] - b[0])
|
|
1930
|
-
.map(([, items]) => items);
|
|
1931
|
-
|
|
1934
|
+
const maxDepth = Math.max(0, ...Array.from(depthMap.values()));
|
|
1932
1935
|
const W = Math.min(Math.max(container.clientWidth || 760, 640), 960);
|
|
1933
1936
|
const levelGap = 112;
|
|
1934
1937
|
const topPadding = 56;
|
|
1935
|
-
const H = Math.max(280, topPadding +
|
|
1938
|
+
const H = Math.max(280, topPadding + maxDepth * levelGap + 96);
|
|
1936
1939
|
const nodeRadius = 28;
|
|
1937
1940
|
const positions = new Map();
|
|
1938
1941
|
|
|
1939
|
-
|
|
1940
|
-
|
|
1942
|
+
// Position nodes: center children under their parent using subtree sizes
|
|
1943
|
+
const totalLeaves = roots.reduce((sum, r) => sum + (subtreeSize.get(r.id) || 1), 0);
|
|
1944
|
+
const leafWidth = W / (totalLeaves + 1);
|
|
1945
|
+
|
|
1946
|
+
let leafCounter = 0;
|
|
1947
|
+
function positionSubtree(agent, depth) {
|
|
1948
|
+
const children = childrenMap.get(agent.id) || [];
|
|
1941
1949
|
const y = topPadding + depth * levelGap;
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1950
|
+
if (children.length === 0) {
|
|
1951
|
+
leafCounter++;
|
|
1952
|
+
positions.set(agent.id, { x: leafCounter * leafWidth, y });
|
|
1953
|
+
} else {
|
|
1954
|
+
children.forEach((child) => positionSubtree(child, depth + 1));
|
|
1955
|
+
// Center parent over its children
|
|
1956
|
+
const childPositions = children.map((c) => positions.get(c.id)).filter(Boolean);
|
|
1957
|
+
const minX = Math.min(...childPositions.map((p) => p.x));
|
|
1958
|
+
const maxX = Math.max(...childPositions.map((p) => p.x));
|
|
1959
|
+
positions.set(agent.id, { x: (minX + maxX) / 2, y });
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
roots.forEach((root) => positionSubtree(root, 0));
|
|
1949
1963
|
|
|
1950
1964
|
let svg = '<svg width="' + W + '" height="' + H + '" viewBox="0 0 ' + W + ' ' + H + '" style="display:block;margin:0 auto">';
|
|
1951
1965
|
|
|
1966
|
+
// Draw edges using only explicit parent_agent_id
|
|
1952
1967
|
agentsData.forEach((agent) => {
|
|
1953
|
-
const
|
|
1954
|
-
if (!
|
|
1955
|
-
const parentPos = positions.get(
|
|
1968
|
+
const pid = agent.parent_agent_id;
|
|
1969
|
+
if (!pid || !byId.has(pid)) return;
|
|
1970
|
+
const parentPos = positions.get(pid);
|
|
1956
1971
|
const childPos = positions.get(agent.id);
|
|
1957
1972
|
if (!parentPos || !childPos) return;
|
|
1958
1973
|
|
|
1959
1974
|
const dispatched = graphContext.dispatchedAgents.has(agent.id);
|
|
1960
|
-
const synthetic = !agent.parent_agent_id;
|
|
1961
1975
|
svg += '<line x1="' + parentPos.x + '" y1="' + (parentPos.y + nodeRadius) + '" x2="' + childPos.x + '" y2="' + (childPos.y - nodeRadius) + '" stroke="' + (dispatched ? 'var(--accent)' : 'var(--border)') + '" stroke-width="' + (dispatched ? 2.2 : 1.2) + '"' +
|
|
1962
|
-
|
|
1963
|
-
' opacity="' + (dispatched ? 0.95 : synthetic ? 0.45 : 0.7) + '"/>';
|
|
1976
|
+
' opacity="' + (dispatched ? 0.95 : 0.7) + '"/>';
|
|
1964
1977
|
|
|
1965
1978
|
if (dispatched) {
|
|
1966
1979
|
const mx = (parentPos.x + childPos.x) / 2;
|
|
@@ -1969,6 +1982,7 @@ function renderHierarchyAgentGraph(container, graphContext) {
|
|
|
1969
1982
|
}
|
|
1970
1983
|
});
|
|
1971
1984
|
|
|
1985
|
+
// Draw nodes
|
|
1972
1986
|
agentsData.forEach((agent) => {
|
|
1973
1987
|
const position = positions.get(agent.id);
|
|
1974
1988
|
if (!position) return;
|
|
@@ -1977,7 +1991,7 @@ function renderHierarchyAgentGraph(container, graphContext) {
|
|
|
1977
1991
|
? '<animate attributeName="r" values="' + nodeRadius + ';' + (nodeRadius + 4) + ';' + nodeRadius + '" dur="2s" repeatCount="indefinite"/>'
|
|
1978
1992
|
: '';
|
|
1979
1993
|
const assignedCount = (window._dashboardIssues || []).filter((issue) => issue.assigned_to === agent.id && ['open', 'in_progress', 'pending'].includes(issue.status)).length;
|
|
1980
|
-
const childCount =
|
|
1994
|
+
const childCount = (childrenMap.get(agent.id) || []).length;
|
|
1981
1995
|
const dispatched = graphContext.dispatchedAgents.has(agent.id);
|
|
1982
1996
|
const reason = graphContext.actionReasonByAgent.get(agent.id);
|
|
1983
1997
|
const statusLabel = agent.paused ? 'paused' : agent.status;
|
|
@@ -1993,13 +2007,14 @@ function renderHierarchyAgentGraph(container, graphContext) {
|
|
|
1993
2007
|
|
|
1994
2008
|
svg += '</svg>';
|
|
1995
2009
|
|
|
1996
|
-
const
|
|
1997
|
-
|
|
1998
|
-
|
|
2010
|
+
const hasOrphans = roots.some((r) => !r.is_controller);
|
|
2011
|
+
const note = hasOrphans
|
|
2012
|
+
? 'Top-level agents (no parent) are shown as independent roots.'
|
|
2013
|
+
: 'Links in the graph follow the configured parent-child hierarchy.';
|
|
1999
2014
|
|
|
2000
2015
|
return {
|
|
2001
2016
|
title: 'Agent Collaboration · Tree',
|
|
2002
|
-
note
|
|
2017
|
+
note,
|
|
2003
2018
|
svg,
|
|
2004
2019
|
};
|
|
2005
2020
|
}
|