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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user