feat: Add agent workspace system and runtime management

- Add agent core modules (agent_core, factory, registry, skill_loader)
- Add runtime system for agent execution management
- Add REST API for agents, workspaces, and runtime control
- Add process supervisor for agent lifecycle management
- Add workspace template system with agent profiles
- Add frontend RuntimeView and runtime API integration
- Add per-agent skill workspaces for smoke_fullstack run
- Refactor skill system with active/installed separation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-17 16:43:29 +08:00
parent 2daf5717ba
commit 59b44545d0
121 changed files with 8384 additions and 358 deletions

View File

@@ -16,8 +16,8 @@ import GlobalStyles from './styles/GlobalStyles';
import NetValueChart from './components/NetValueChart';
import StockLogo from './components/StockLogo';
import Header from './components/Header.jsx';
import WatchlistPanel from './components/WatchlistPanel.jsx';
import RuntimeSettingsPanel from './components/RuntimeSettingsPanel.jsx';
import RuntimeView from './components/RuntimeView.jsx';
// Utils
import { formatNumber, formatTickerPrice } from './utils/formatters';
@@ -64,7 +64,7 @@ export default function LiveTradingApp() {
const [progress, setProgress] = useState({ current: 0, total: 0 });
const [now, setNow] = useState(() => new Date());
// View toggle: 'traders' | 'room' | 'explain' | 'chart' | 'statistics'
// View toggle: 'traders' | 'room' | 'explain' | 'chart' | 'statistics' | 'runtime'
const [currentView, setCurrentView] = useState('traders');
const [isInitialAnimating, setIsInitialAnimating] = useState(true);
const [lastUpdate, setLastUpdate] = useState(new Date());
@@ -124,6 +124,9 @@ export default function LiveTradingApp() {
const [intervalMinutesDraft, setIntervalMinutesDraft] = useState('60');
const [triggerTimeDraft, setTriggerTimeDraft] = useState('09:30');
const [maxCommCyclesDraft, setMaxCommCyclesDraft] = useState('2');
const [initialCashDraft, setInitialCashDraft] = useState('100000');
const [marginRequirementDraft, setMarginRequirementDraft] = useState('0');
const [enableMemoryDraft, setEnableMemoryDraft] = useState(false);
const [runtimeConfigFeedback, setRuntimeConfigFeedback] = useState(null);
const [isRuntimeConfigSaving, setIsRuntimeConfigSaving] = useState(false);
const [selectedSkillAgentId, setSelectedSkillAgentId] = useState(AGENTS[0]?.id || 'portfolio_manager');
@@ -302,6 +305,9 @@ export default function LiveTradingApp() {
setIntervalMinutesDraft(String(runtimeConfig.interval_minutes || 60));
setTriggerTimeDraft(String(runtimeConfig.trigger_time || '09:30'));
setMaxCommCyclesDraft(String(runtimeConfig.max_comm_cycles || 2));
setInitialCashDraft(String(runtimeConfig.initial_cash ?? 100000));
setMarginRequirementDraft(String(runtimeConfig.margin_requirement ?? 0));
setEnableMemoryDraft(Boolean(runtimeConfig.enable_memory ?? false));
}, [runtimeConfig]);
const watchlistSuggestions = useMemo(
@@ -537,20 +543,101 @@ export default function LiveTradingApp() {
schedule_mode: scheduleModeDraft,
interval_minutes: interval,
trigger_time: triggerTimeDraft,
max_comm_cycles: maxCommCycles
max_comm_cycles: maxCommCycles,
initial_cash: Number(initialCashDraft),
margin_requirement: Number(marginRequirementDraft),
enable_memory: Boolean(enableMemoryDraft)
});
if (!success) {
setIsRuntimeConfigSaving(false);
setRuntimeConfigFeedback({ type: 'error', text: '发送失败,请检查连接状态' });
}
}, [intervalMinutesDraft, maxCommCyclesDraft, scheduleModeDraft, triggerTimeDraft]);
}, [enableMemoryDraft, initialCashDraft, intervalMinutesDraft, marginRequirementDraft, maxCommCyclesDraft, scheduleModeDraft, triggerTimeDraft]);
const handleLaunchConfigSave = useCallback(() => {
const pendingTickers = parseWatchlistInput(watchlistInputValue);
const nextTickers = Array.from(new Set([...watchlistDraftSymbols, ...pendingTickers]));
if (nextTickers.length === 0) {
setRuntimeConfigFeedback({ type: 'error', text: '至少输入 1 个有效股票代码' });
return;
}
if (!clientRef.current) {
setRuntimeConfigFeedback({ type: 'error', text: '连接未就绪,稍后重试' });
return;
}
const interval = Number(intervalMinutesDraft);
const maxCommCycles = Number(maxCommCyclesDraft);
const initialCash = Number(initialCashDraft);
const marginRequirement = Number(marginRequirementDraft);
if (!Number.isInteger(interval) || interval <= 0) {
setRuntimeConfigFeedback({ type: 'error', text: '间隔必须是正整数分钟' });
return;
}
if (!Number.isInteger(maxCommCycles) || maxCommCycles <= 0) {
setRuntimeConfigFeedback({ type: 'error', text: '讨论轮数必须是正整数' });
return;
}
if (!Number.isFinite(initialCash) || initialCash <= 0) {
setRuntimeConfigFeedback({ type: 'error', text: '初始资金必须是正数' });
return;
}
if (!Number.isFinite(marginRequirement) || marginRequirement < 0) {
setRuntimeConfigFeedback({ type: 'error', text: '保证金要求不能为负数' });
return;
}
setIsRuntimeConfigSaving(true);
setIsWatchlistSaving(true);
setRuntimeConfigFeedback(null);
setWatchlistFeedback(null);
setWatchlistDraftSymbols(nextTickers);
setWatchlistInputValue('');
const watchlistSuccess = clientRef.current.send({
type: 'update_watchlist',
tickers: nextTickers
});
const runtimeSuccess = clientRef.current.send({
type: 'update_runtime_config',
schedule_mode: scheduleModeDraft,
interval_minutes: interval,
trigger_time: triggerTimeDraft,
max_comm_cycles: maxCommCycles,
initial_cash: initialCash,
margin_requirement: marginRequirement,
enable_memory: Boolean(enableMemoryDraft)
});
if (!watchlistSuccess || !runtimeSuccess) {
setIsRuntimeConfigSaving(false);
setIsWatchlistSaving(false);
setRuntimeConfigFeedback({ type: 'error', text: '发送失败,请检查连接状态' });
}
}, [
intervalMinutesDraft,
maxCommCyclesDraft,
parseWatchlistInput,
scheduleModeDraft,
triggerTimeDraft,
initialCashDraft,
marginRequirementDraft,
enableMemoryDraft,
watchlistDraftSymbols,
watchlistInputValue
]);
const handleRuntimeDefaultsRestore = useCallback(() => {
setScheduleModeDraft('daily');
setIntervalMinutesDraft('60');
setTriggerTimeDraft('09:30');
setMaxCommCyclesDraft('2');
setInitialCashDraft('100000');
setMarginRequirementDraft('0');
setEnableMemoryDraft(false);
setRuntimeConfigFeedback(null);
}, []);
@@ -2273,43 +2360,39 @@ export default function LiveTradingApp() {
</button>
)}
<WatchlistPanel
isOpen={isWatchlistPanelOpen}
isConnected={isConnected}
isSaving={isWatchlistSaving}
draftSymbols={watchlistDraftSymbols}
inputValue={watchlistInputValue}
feedback={watchlistFeedback}
suggestions={watchlistSuggestions}
onToggle={handleWatchlistPanelToggle}
onClose={() => setIsWatchlistPanelOpen(false)}
onInputChange={handleWatchlistInputChange}
onInputKeyDown={handleWatchlistInputKeyDown}
onAdd={() => commitWatchlistInput(watchlistInputValue)}
onRemove={handleWatchlistRemove}
onRestoreCurrent={handleWatchlistRestoreCurrent}
onRestoreDefault={handleWatchlistRestoreDefault}
onSuggestionClick={handleWatchlistSuggestionClick}
onSave={handleWatchlistSave}
/>
<RuntimeSettingsPanel
showTrigger={false}
isOpen={isRuntimeSettingsOpen}
isConnected={isConnected}
isSaving={isRuntimeConfigSaving}
feedback={runtimeConfigFeedback}
runtimeConfig={runtimeConfig}
isSaving={isRuntimeConfigSaving || isWatchlistSaving}
feedback={runtimeConfigFeedback || watchlistFeedback}
scheduleMode={scheduleModeDraft}
intervalMinutes={intervalMinutesDraft}
triggerTime={triggerTimeDraft}
maxCommCycles={maxCommCyclesDraft}
initialCash={initialCashDraft}
marginRequirement={marginRequirementDraft}
enableMemory={enableMemoryDraft}
watchlistSymbols={watchlistDraftSymbols}
watchlistInputValue={watchlistInputValue}
watchlistSuggestions={watchlistSuggestions}
onToggle={handleRuntimeSettingsToggle}
onClose={() => setIsRuntimeSettingsOpen(false)}
onScheduleModeChange={setScheduleModeDraft}
onIntervalMinutesChange={setIntervalMinutesDraft}
onTriggerTimeChange={setTriggerTimeDraft}
onMaxCommCyclesChange={setMaxCommCyclesDraft}
onSave={handleRuntimeConfigSave}
onInitialCashChange={setInitialCashDraft}
onMarginRequirementChange={setMarginRequirementDraft}
onEnableMemoryChange={setEnableMemoryDraft}
onWatchlistInputChange={handleWatchlistInputChange}
onWatchlistInputKeyDown={handleWatchlistInputKeyDown}
onWatchlistAdd={() => commitWatchlistInput(watchlistInputValue)}
onWatchlistRemove={handleWatchlistRemove}
onWatchlistRestoreCurrent={handleWatchlistRestoreCurrent}
onWatchlistRestoreDefault={handleWatchlistRestoreDefault}
onWatchlistSuggestionClick={handleWatchlistSuggestionClick}
onSave={handleLaunchConfigSave}
onRestoreDefaults={handleRuntimeDefaultsRestore}
/>
</div>
@@ -2393,8 +2476,33 @@ export default function LiveTradingApp() {
>
统计
</button>
<button
className={`view-nav-btn ${currentView === 'runtime' ? 'active' : ''}`}
onClick={() => setCurrentView('runtime')}
>
运行态
</button>
</div>
{currentView === 'runtime' ? (
<div
style={{
position: 'absolute',
top: 40,
left: 0,
right: 0,
bottom: 0,
display: 'flex',
flexDirection: 'column',
overflow: 'hidden',
minWidth: 0,
minHeight: 0
}}
>
<RuntimeView />
</div>
) : (
<div className={`view-slider-five ${
currentView === 'traders'
? 'show-traders'
@@ -2454,6 +2562,7 @@ export default function LiveTradingApp() {
leaderboard={leaderboard}
feed={feed}
onJumpToMessage={handleJumpToMessage}
onOpenLaunchConfig={() => setIsRuntimeSettingsOpen(true)}
/>
</Suspense>
</div>
@@ -2535,6 +2644,7 @@ export default function LiveTradingApp() {
</Suspense>
</div>
</div>
)}
</div>
</div>
</div>