feat: ship subagent runtime and remove autonomy/task legacy

This commit is contained in:
lpf
2026-03-05 21:54:54 +08:00
parent 29d6480058
commit e8e1cdae32
32 changed files with 1844 additions and 2867 deletions

View File

@@ -10,11 +10,23 @@ type SubagentProfile = {
system_prompt?: string;
tool_allowlist?: string[];
memory_namespace?: string;
max_retries?: number;
retry_backoff_ms?: number;
timeout_sec?: number;
max_task_chars?: number;
max_result_chars?: number;
status?: 'active' | 'disabled' | string;
created_at?: number;
updated_at?: number;
};
type ToolAllowlistGroup = {
name: string;
description?: string;
aliases?: string[];
tools?: string[];
};
const emptyDraft: SubagentProfile = {
agent_id: '',
name: '',
@@ -23,6 +35,11 @@ const emptyDraft: SubagentProfile = {
memory_namespace: '',
status: 'active',
tool_allowlist: [],
max_retries: 0,
retry_backoff_ms: 1000,
timeout_sec: 0,
max_task_chars: 0,
max_result_chars: 0,
};
const SubagentProfiles: React.FC = () => {
@@ -34,6 +51,7 @@ const SubagentProfiles: React.FC = () => {
const [selectedId, setSelectedId] = useState<string>('');
const [draft, setDraft] = useState<SubagentProfile>(emptyDraft);
const [saving, setSaving] = useState(false);
const [groups, setGroups] = useState<ToolAllowlistGroup[]>([]);
const selected = useMemo(
() => items.find((p) => p.agent_id === selectedId) || null,
@@ -62,6 +80,11 @@ const SubagentProfiles: React.FC = () => {
memory_namespace: next.memory_namespace || '',
status: (next.status as string) || 'active',
tool_allowlist: Array.isArray(next.tool_allowlist) ? next.tool_allowlist : [],
max_retries: Number(next.max_retries || 0),
retry_backoff_ms: Number(next.retry_backoff_ms || 1000),
timeout_sec: Number(next.timeout_sec || 0),
max_task_chars: Number(next.max_task_chars || 0),
max_result_chars: Number(next.max_result_chars || 0),
});
};
@@ -69,6 +92,17 @@ const SubagentProfiles: React.FC = () => {
load().catch(() => {});
}, [q]);
useEffect(() => {
const loadGroups = async () => {
const r = await fetch(`/webui/api/tool_allowlist_groups${q}`);
if (!r.ok) return;
const j = await r.json();
const arr = Array.isArray(j.groups) ? j.groups : [];
setGroups(arr);
};
loadGroups().catch(() => {});
}, [q]);
const onSelect = (p: SubagentProfile) => {
setSelectedId(p.agent_id || '');
setDraft({
@@ -79,6 +113,11 @@ const SubagentProfiles: React.FC = () => {
memory_namespace: p.memory_namespace || '',
status: (p.status as string) || 'active',
tool_allowlist: Array.isArray(p.tool_allowlist) ? p.tool_allowlist : [],
max_retries: Number(p.max_retries || 0),
retry_backoff_ms: Number(p.retry_backoff_ms || 1000),
timeout_sec: Number(p.timeout_sec || 0),
max_task_chars: Number(p.max_task_chars || 0),
max_result_chars: Number(p.max_result_chars || 0),
});
};
@@ -96,6 +135,14 @@ const SubagentProfiles: React.FC = () => {
const allowlistText = (draft.tool_allowlist || []).join(', ');
const addAllowlistToken = (token: string) => {
const list = Array.isArray(draft.tool_allowlist) ? [...draft.tool_allowlist] : [];
if (!list.includes(token)) {
list.push(token);
setDraft({ ...draft, tool_allowlist: list });
}
};
const save = async () => {
const agentId = String(draft.agent_id || '').trim();
if (!agentId) {
@@ -118,6 +165,11 @@ const SubagentProfiles: React.FC = () => {
memory_namespace: draft.memory_namespace || '',
status: draft.status || 'active',
tool_allowlist: draft.tool_allowlist || [],
max_retries: Number(draft.max_retries || 0),
retry_backoff_ms: Number(draft.retry_backoff_ms || 0),
timeout_sec: Number(draft.timeout_sec || 0),
max_task_chars: Number(draft.max_task_chars || 0),
max_result_chars: Number(draft.max_result_chars || 0),
}),
});
if (!r.ok) {
@@ -262,6 +314,21 @@ const SubagentProfiles: React.FC = () => {
className="w-full px-2 py-1 text-xs bg-zinc-900 border border-zinc-700 rounded"
placeholder="read_file, list_files, memory_search"
/>
{groups.length > 0 && (
<div className="mt-2 flex flex-wrap gap-2">
{groups.map((g) => (
<button
key={g.name}
type="button"
onClick={() => addAllowlistToken(`group:${g.name}`)}
className="px-2 py-1 text-[11px] rounded bg-zinc-800 hover:bg-zinc-700 text-zinc-200"
title={g.description || g.name}
>
{`group:${g.name}`}
</button>
))}
</div>
)}
</div>
<div className="md:col-span-2">
<div className="text-xs text-zinc-400 mb-1">System Prompt</div>
@@ -272,6 +339,56 @@ const SubagentProfiles: React.FC = () => {
placeholder="You are a coding specialist..."
/>
</div>
<div>
<div className="text-xs text-zinc-400 mb-1">Max Retries</div>
<input
type="number"
min={0}
value={Number(draft.max_retries || 0)}
onChange={(e) => setDraft({ ...draft, max_retries: Number(e.target.value) || 0 })}
className="w-full px-2 py-1 text-xs bg-zinc-900 border border-zinc-700 rounded"
/>
</div>
<div>
<div className="text-xs text-zinc-400 mb-1">Retry Backoff (ms)</div>
<input
type="number"
min={0}
value={Number(draft.retry_backoff_ms || 0)}
onChange={(e) => setDraft({ ...draft, retry_backoff_ms: Number(e.target.value) || 0 })}
className="w-full px-2 py-1 text-xs bg-zinc-900 border border-zinc-700 rounded"
/>
</div>
<div>
<div className="text-xs text-zinc-400 mb-1">Timeout (sec)</div>
<input
type="number"
min={0}
value={Number(draft.timeout_sec || 0)}
onChange={(e) => setDraft({ ...draft, timeout_sec: Number(e.target.value) || 0 })}
className="w-full px-2 py-1 text-xs bg-zinc-900 border border-zinc-700 rounded"
/>
</div>
<div>
<div className="text-xs text-zinc-400 mb-1">Max Task Chars</div>
<input
type="number"
min={0}
value={Number(draft.max_task_chars || 0)}
onChange={(e) => setDraft({ ...draft, max_task_chars: Number(e.target.value) || 0 })}
className="w-full px-2 py-1 text-xs bg-zinc-900 border border-zinc-700 rounded"
/>
</div>
<div className="md:col-span-2">
<div className="text-xs text-zinc-400 mb-1">Max Result Chars</div>
<input
type="number"
min={0}
value={Number(draft.max_result_chars || 0)}
onChange={(e) => setDraft({ ...draft, max_result_chars: Number(e.target.value) || 0 })}
className="w-full px-2 py-1 text-xs bg-zinc-900 border border-zinc-700 rounded"
/>
</div>
</div>
<div className="flex items-center gap-2">
@@ -311,4 +428,3 @@ const SubagentProfiles: React.FC = () => {
};
export default SubagentProfiles;