feat(agents): Phase 8.4-10.5 built-in plugins, bundled skills, coordinator
This commit is contained in:
@@ -25,6 +25,123 @@ export interface AgentConfig {
|
||||
selected_skill_ids?: string[]
|
||||
}
|
||||
|
||||
export interface AgentVisibilityEvent {
|
||||
event_id: string
|
||||
event_type: string
|
||||
timestamp: string
|
||||
conversation_id?: string | null
|
||||
agent_id?: string | null
|
||||
task_id?: string | null
|
||||
thread_id?: string | null
|
||||
severity: string
|
||||
payload: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface AgentVisibilityVerifier {
|
||||
conversation_id: string
|
||||
status?: string | null
|
||||
summary?: string | null
|
||||
evidence: Array<Record<string, unknown>>
|
||||
}
|
||||
|
||||
export interface AgentVisibilityTaskSummary {
|
||||
task_id: string
|
||||
role?: string | null
|
||||
owner_agent_id?: string | null
|
||||
status?: string | null
|
||||
summary?: string | null
|
||||
evidence_count: number
|
||||
}
|
||||
|
||||
export interface AgentVisibilityTopologyNode {
|
||||
agent_id: string
|
||||
role?: string | null
|
||||
parent_agent_id?: string | null
|
||||
source: string
|
||||
task_count: number
|
||||
completed_task_count: number
|
||||
}
|
||||
|
||||
export interface AgentVisibilityTopology {
|
||||
conversation_id: string
|
||||
root_agent_id?: string | null
|
||||
current_agent?: string | null
|
||||
nodes: AgentVisibilityTopologyNode[]
|
||||
edges: Array<Record<string, string>>
|
||||
tasks: AgentVisibilityTaskSummary[]
|
||||
task_hierarchy: Record<string, string[]>
|
||||
}
|
||||
|
||||
export interface AgentVisibilityIsolation {
|
||||
mode: string
|
||||
isolation_id?: string | null
|
||||
workspace_path?: string | null
|
||||
parent_conversation_id?: string | null
|
||||
metadata: Record<string, unknown>
|
||||
}
|
||||
|
||||
export interface AgentVisibilityCost {
|
||||
input_tokens: number
|
||||
output_tokens: number
|
||||
total_tokens: number
|
||||
estimated_cost?: number | null
|
||||
budget_warning: boolean
|
||||
currency: string
|
||||
}
|
||||
|
||||
export interface AgentVisibilityCostByAgent {
|
||||
agent_id: string
|
||||
input_tokens: number
|
||||
output_tokens: number
|
||||
total_tokens: number
|
||||
estimated_cost?: number | null
|
||||
budget_warning: boolean
|
||||
}
|
||||
|
||||
export interface AgentVisibilityCostSummary {
|
||||
conversation_id: string
|
||||
total: AgentVisibilityCost
|
||||
thresholds: Record<string, number>
|
||||
by_agent: AgentVisibilityCostByAgent[]
|
||||
}
|
||||
|
||||
export interface AgentVisibilityToolGovernanceItem {
|
||||
capability_id: string
|
||||
tool_name: string
|
||||
permission_class: string
|
||||
side_effect_scope: string
|
||||
supports_retry: boolean
|
||||
idempotent: boolean
|
||||
safe_for_parallel_use: boolean
|
||||
requires_confirmation: boolean
|
||||
usage_count: number
|
||||
last_result_preview?: string | null
|
||||
}
|
||||
|
||||
export interface AgentVisibilityToolGovernance {
|
||||
conversation_id: string
|
||||
total_tools: number
|
||||
used_tools: number
|
||||
items: AgentVisibilityToolGovernanceItem[]
|
||||
upgrade_candidates: string[]
|
||||
}
|
||||
|
||||
export interface AgentVisibilityRuntimeSummary {
|
||||
conversation_id: string
|
||||
execution_mode?: string | null
|
||||
current_phase?: string | null
|
||||
current_checkpoint?: string | null
|
||||
phase_history: Array<Record<string, unknown>>
|
||||
checkpoint_history: Array<Record<string, unknown>>
|
||||
verifier: AgentVisibilityVerifier
|
||||
isolation: AgentVisibilityIsolation
|
||||
cost: AgentVisibilityCost
|
||||
topology_node_count: number
|
||||
active_task_count: number
|
||||
completed_task_count: number
|
||||
recent_events: AgentVisibilityEvent[]
|
||||
}
|
||||
|
||||
export const agentApi = {
|
||||
async getStats(): Promise<AgentStats[]> {
|
||||
const res = await api.get('/api/agents/stats')
|
||||
@@ -45,4 +162,39 @@ export const agentApi = {
|
||||
const res = await api.put(`/api/agents/config/${id}`, data)
|
||||
return res.data
|
||||
},
|
||||
|
||||
async getRuntimeSummary(conversationId: string): Promise<AgentVisibilityRuntimeSummary> {
|
||||
const res = await api.get('/api/agents/visibility/runtime-summary', {
|
||||
params: { conversation_id: conversationId },
|
||||
})
|
||||
return res.data
|
||||
},
|
||||
|
||||
async getVisibilityTopology(conversationId: string): Promise<AgentVisibilityTopology> {
|
||||
const res = await api.get('/api/agents/visibility/topology', {
|
||||
params: { conversation_id: conversationId },
|
||||
})
|
||||
return res.data
|
||||
},
|
||||
|
||||
async getVisibilityVerifier(conversationId: string): Promise<AgentVisibilityVerifier> {
|
||||
const res = await api.get('/api/agents/visibility/verifier', {
|
||||
params: { conversation_id: conversationId },
|
||||
})
|
||||
return res.data
|
||||
},
|
||||
|
||||
async getVisibilityCost(conversationId: string): Promise<AgentVisibilityCostSummary> {
|
||||
const res = await api.get('/api/agents/visibility/cost', {
|
||||
params: { conversation_id: conversationId },
|
||||
})
|
||||
return res.data
|
||||
},
|
||||
|
||||
async getVisibilityTools(conversationId: string): Promise<AgentVisibilityToolGovernance> {
|
||||
const res = await api.get('/api/agents/visibility/tools', {
|
||||
params: { conversation_id: conversationId },
|
||||
})
|
||||
return res.data
|
||||
},
|
||||
}
|
||||
|
||||
@@ -90,7 +90,10 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
width: 260px;
|
||||
width: 320px;
|
||||
max-height: calc(100% - 36px);
|
||||
overflow: auto;
|
||||
padding-right: 4px;
|
||||
}
|
||||
.hud-panel {
|
||||
border: 1px solid rgba(0,245,212,0.12);
|
||||
@@ -120,6 +123,116 @@
|
||||
color: var(--accent-cyan);
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.runtime-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 8px 12px;
|
||||
}
|
||||
.runtime-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
.runtime-label {
|
||||
font-size: 10px;
|
||||
color: var(--text-dim);
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
.runtime-item strong {
|
||||
font-size: 12px;
|
||||
color: var(--text-primary);
|
||||
word-break: break-word;
|
||||
}
|
||||
.runtime-note {
|
||||
margin-top: 10px;
|
||||
font-size: 11px;
|
||||
color: var(--text-secondary);
|
||||
line-height: 1.5;
|
||||
}
|
||||
.runtime-meta {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
font-size: 10px;
|
||||
color: var(--accent-cyan);
|
||||
}
|
||||
.runtime-warning {
|
||||
color: var(--accent-amber);
|
||||
}
|
||||
.runtime-workspace {
|
||||
margin-top: 8px;
|
||||
font-size: 10px;
|
||||
color: var(--text-dim);
|
||||
word-break: break-all;
|
||||
}
|
||||
.stack-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
.stack-item {
|
||||
padding: 8px 10px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid rgba(0,245,212,0.08);
|
||||
background: rgba(10, 18, 30, 0.78);
|
||||
}
|
||||
.stack-line {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
font-size: 11px;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
.stack-subline {
|
||||
margin-top: 4px;
|
||||
font-size: 10px;
|
||||
color: var(--text-dim);
|
||||
word-break: break-word;
|
||||
}
|
||||
.stack-empty {
|
||||
font-size: 11px;
|
||||
color: var(--text-dim);
|
||||
}
|
||||
.mini-section + .mini-section {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.mini-title {
|
||||
margin-bottom: 8px;
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.08em;
|
||||
color: var(--accent-cyan);
|
||||
}
|
||||
.event-severity {
|
||||
text-transform: uppercase;
|
||||
font-size: 10px;
|
||||
}
|
||||
.event-severity.warning {
|
||||
color: var(--accent-amber);
|
||||
}
|
||||
.event-severity.error {
|
||||
color: var(--accent-red);
|
||||
}
|
||||
.threshold-line {
|
||||
margin-top: 8px;
|
||||
}
|
||||
.candidate-list {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
}
|
||||
.candidate-chip {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4px 8px;
|
||||
border-radius: 999px;
|
||||
font-size: 10px;
|
||||
color: var(--accent-cyan);
|
||||
border: 1px solid rgba(0,245,212,0.12);
|
||||
background: rgba(0,245,212,0.06);
|
||||
}
|
||||
.canvas-controls {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { mount, flushPromises } from '@vue/test-utils'
|
||||
import { createPinia, setActivePinia } from 'pinia'
|
||||
import { useConversationStore } from '@/stores/conversation'
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
getHierarchyStats: vi.fn(),
|
||||
getRuntimeSummary: vi.fn(),
|
||||
getVisibilityTopology: vi.fn(),
|
||||
getVisibilityVerifier: vi.fn(),
|
||||
getVisibilityCost: vi.fn(),
|
||||
getVisibilityTools: vi.fn(),
|
||||
getConfig: vi.fn(),
|
||||
updateConfig: vi.fn(),
|
||||
listSkills: vi.fn(),
|
||||
@@ -11,6 +18,11 @@ const mocks = vi.hoisted(() => ({
|
||||
vi.mock('@/api/agent', () => ({
|
||||
agentApi: {
|
||||
getHierarchyStats: mocks.getHierarchyStats,
|
||||
getRuntimeSummary: mocks.getRuntimeSummary,
|
||||
getVisibilityTopology: mocks.getVisibilityTopology,
|
||||
getVisibilityVerifier: mocks.getVisibilityVerifier,
|
||||
getVisibilityCost: mocks.getVisibilityCost,
|
||||
getVisibilityTools: mocks.getVisibilityTools,
|
||||
getConfig: mocks.getConfig,
|
||||
updateConfig: mocks.updateConfig,
|
||||
},
|
||||
@@ -69,6 +81,136 @@ const hierarchyStats = {
|
||||
],
|
||||
}
|
||||
|
||||
const runtimeSummaryFixture = {
|
||||
conversation_id: 'conv-1',
|
||||
execution_mode: 'collaboration',
|
||||
current_phase: 'phase_4_visibility_and_verification',
|
||||
current_checkpoint: 'visibility.runtime_summary_ready',
|
||||
phase_history: [],
|
||||
checkpoint_history: [],
|
||||
verifier: {
|
||||
conversation_id: 'conv-1',
|
||||
status: 'passed',
|
||||
summary: 'Runtime summary is available.',
|
||||
evidence: [],
|
||||
},
|
||||
isolation: {
|
||||
mode: 'worktree',
|
||||
isolation_id: 'iso-1',
|
||||
workspace_path: '/tmp/jarvis/worktree-1',
|
||||
parent_conversation_id: 'parent-1',
|
||||
metadata: { branch: 'jarvis/test-worker' },
|
||||
},
|
||||
cost: {
|
||||
input_tokens: 120,
|
||||
output_tokens: 80,
|
||||
total_tokens: 200,
|
||||
estimated_cost: 0.00156,
|
||||
budget_warning: true,
|
||||
currency: 'USD',
|
||||
},
|
||||
topology_node_count: 2,
|
||||
active_task_count: 2,
|
||||
completed_task_count: 1,
|
||||
recent_events: [
|
||||
{
|
||||
event_id: 'evt-1',
|
||||
event_type: 'agent.cost.warning',
|
||||
timestamp: '2026-04-04T10:00:00Z',
|
||||
severity: 'warning',
|
||||
payload: {},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const topologyFixture = {
|
||||
conversation_id: 'conv-1',
|
||||
root_agent_id: 'master',
|
||||
current_agent: 'analyst-1234abcd',
|
||||
nodes: [
|
||||
{
|
||||
agent_id: 'master',
|
||||
role: 'master',
|
||||
parent_agent_id: null,
|
||||
source: 'root',
|
||||
task_count: 0,
|
||||
completed_task_count: 0,
|
||||
},
|
||||
{
|
||||
agent_id: 'analyst-1234abcd',
|
||||
role: 'analyst',
|
||||
parent_agent_id: 'master',
|
||||
source: 'spawned',
|
||||
task_count: 2,
|
||||
completed_task_count: 1,
|
||||
},
|
||||
],
|
||||
edges: [{ parent_agent_id: 'master', child_agent_id: 'analyst-1234abcd' }],
|
||||
tasks: [],
|
||||
task_hierarchy: {},
|
||||
}
|
||||
|
||||
const verifierFixture = {
|
||||
conversation_id: 'conv-1',
|
||||
status: 'passed',
|
||||
summary: 'Runtime summary is available.',
|
||||
evidence: [
|
||||
{ task_id: 'task-1', status: 'passed' },
|
||||
],
|
||||
}
|
||||
|
||||
const costFixture = {
|
||||
conversation_id: 'conv-1',
|
||||
total: runtimeSummaryFixture.cost,
|
||||
thresholds: {
|
||||
total_tokens: 300,
|
||||
estimated_cost: 0.01,
|
||||
},
|
||||
by_agent: [
|
||||
{
|
||||
agent_id: 'analyst-1234abcd',
|
||||
input_tokens: 80,
|
||||
output_tokens: 70,
|
||||
total_tokens: 150,
|
||||
estimated_cost: 0.00129,
|
||||
budget_warning: true,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const toolGovernanceFixture = {
|
||||
conversation_id: 'conv-1',
|
||||
total_tools: 12,
|
||||
used_tools: 2,
|
||||
items: [
|
||||
{
|
||||
capability_id: 'web_search',
|
||||
tool_name: 'web_search',
|
||||
permission_class: 'external',
|
||||
side_effect_scope: 'network',
|
||||
supports_retry: true,
|
||||
idempotent: true,
|
||||
safe_for_parallel_use: true,
|
||||
requires_confirmation: false,
|
||||
usage_count: 1,
|
||||
last_result_preview: 'ok',
|
||||
},
|
||||
{
|
||||
capability_id: 'create_reminder',
|
||||
tool_name: 'create_reminder',
|
||||
permission_class: 'write',
|
||||
side_effect_scope: 'local_state',
|
||||
supports_retry: false,
|
||||
idempotent: false,
|
||||
safe_for_parallel_use: false,
|
||||
requires_confirmation: true,
|
||||
usage_count: 1,
|
||||
last_result_preview: 'created',
|
||||
},
|
||||
],
|
||||
upgrade_candidates: ['worktree_manager', 'cost_inspector'],
|
||||
}
|
||||
|
||||
const skillFixtures = [
|
||||
{
|
||||
id: 'skill-schedule-1',
|
||||
@@ -124,6 +266,8 @@ describe('agents page pcb command center', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
vi.useFakeTimers()
|
||||
setActivePinia(createPinia())
|
||||
useConversationStore().setCurrentConversation('conv-1')
|
||||
vi.stubGlobal('ResizeObserver', class {
|
||||
observe() {}
|
||||
disconnect() {}
|
||||
@@ -143,6 +287,11 @@ describe('agents page pcb command center', () => {
|
||||
})),
|
||||
})
|
||||
mocks.getHierarchyStats.mockResolvedValue(hierarchyStats)
|
||||
mocks.getRuntimeSummary.mockResolvedValue(runtimeSummaryFixture)
|
||||
mocks.getVisibilityTopology.mockResolvedValue(topologyFixture)
|
||||
mocks.getVisibilityVerifier.mockResolvedValue(verifierFixture)
|
||||
mocks.getVisibilityCost.mockResolvedValue(costFixture)
|
||||
mocks.getVisibilityTools.mockResolvedValue(toolGovernanceFixture)
|
||||
mocks.getConfig.mockImplementation(async (id: string) => ({
|
||||
id,
|
||||
name: id === 'schedule_planner' ? 'SCHEDULE PLANNER' : id.toUpperCase(),
|
||||
@@ -161,6 +310,8 @@ describe('agents page pcb command center', () => {
|
||||
await Promise.resolve()
|
||||
await Promise.resolve()
|
||||
|
||||
expect(mocks.getRuntimeSummary).toHaveBeenCalledWith('conv-1')
|
||||
expect(mocks.getVisibilityTopology).toHaveBeenCalledWith('conv-1')
|
||||
expect(wrapper.find('[data-testid="commander-skills"]').exists()).toBe(false)
|
||||
|
||||
const plannerBus = wrapper.get('[data-testid="bus-link-schedule_planner"]')
|
||||
@@ -367,5 +518,49 @@ describe('agents page pcb command center', () => {
|
||||
|
||||
expect(wrapper.get('[data-testid="linked-skills-empty"]').text()).toContain('暂无可关联技能')
|
||||
})
|
||||
|
||||
it('renders runtime summary from the active conversation', async () => {
|
||||
const wrapper = mount(AgentsPage)
|
||||
await flushPromises()
|
||||
await flushPromises()
|
||||
|
||||
expect(mocks.getRuntimeSummary).toHaveBeenCalledWith('conv-1')
|
||||
const runtimeSummary = wrapper.get('[data-testid="runtime-summary"]')
|
||||
expect(runtimeSummary.text()).toContain('collaboration')
|
||||
expect(runtimeSummary.text()).toContain('phase_4_visibility_and_verification')
|
||||
expect(runtimeSummary.text()).toContain('visibility.runtime_summary_ready')
|
||||
expect(runtimeSummary.text()).toContain('passed')
|
||||
expect(runtimeSummary.text()).toContain('worktree')
|
||||
expect(runtimeSummary.text()).toContain('200')
|
||||
expect(runtimeSummary.text()).toContain('Cost $0.001560')
|
||||
expect(runtimeSummary.text()).toContain('Tasks 1/2')
|
||||
expect(runtimeSummary.text()).toContain('Nodes 2')
|
||||
expect(runtimeSummary.text()).toContain('Budget warning')
|
||||
expect(wrapper.text()).toContain('/tmp/jarvis/worktree-1')
|
||||
})
|
||||
|
||||
it('renders operator drilldown panels for events topology verifier and tool governance', async () => {
|
||||
const wrapper = mount(AgentsPage)
|
||||
await flushPromises()
|
||||
await flushPromises()
|
||||
|
||||
expect(wrapper.get('[data-testid="runtime-events-panel"]').text()).toContain('agent.cost.warning')
|
||||
expect(wrapper.get('[data-testid="runtime-drilldown-panel"]').text()).toContain('analyst-1234abcd')
|
||||
expect(wrapper.get('[data-testid="runtime-drilldown-panel"]').text()).toContain('task-1')
|
||||
expect(wrapper.get('[data-testid="runtime-governance-panel"]').text()).toContain('web_search')
|
||||
expect(wrapper.get('[data-testid="runtime-governance-panel"]').text()).toContain('worktree_manager')
|
||||
expect(wrapper.get('[data-testid="runtime-governance-panel"]').text()).toContain('Thresholds: 300 tk / $0.010000')
|
||||
})
|
||||
|
||||
it('shows a prompt when no conversation is selected', async () => {
|
||||
useConversationStore().setCurrentConversation(null)
|
||||
const wrapper = mount(AgentsPage)
|
||||
await flushPromises()
|
||||
await flushPromises()
|
||||
|
||||
expect(mocks.getRuntimeSummary).not.toHaveBeenCalled()
|
||||
expect(mocks.getVisibilityTopology).not.toHaveBeenCalled()
|
||||
expect(wrapper.get('[data-testid="runtime-summary"]').text()).toContain('请选择一条会话以查看运行时摘要')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1,10 +1,23 @@
|
||||
import { computed, onMounted, onUnmounted, reactive, ref } from 'vue'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { COMMANDER_SKILLS, DEFAULT_AGENTS, MAIN_AGENT_ORDER, RELATION_LABELS, SUB_COMMANDERS } from '@/data/agents'
|
||||
import type { Agent, CommanderSkill, MainAgentId, SubCommander } from '@/data/agents'
|
||||
import { agentApi, type AgentHierarchyStats, type AgentStats } from '@/api/agent'
|
||||
import {
|
||||
agentApi,
|
||||
type AgentHierarchyStats,
|
||||
type AgentStats,
|
||||
type AgentVisibilityCostSummary,
|
||||
type AgentVisibilityRuntimeSummary,
|
||||
type AgentVisibilityToolGovernance,
|
||||
type AgentVisibilityTopology,
|
||||
type AgentVisibilityVerifier,
|
||||
} from '@/api/agent'
|
||||
import { skillApi, type Skill } from '@/api/skill'
|
||||
import { useConversationStore } from '@/stores/conversation'
|
||||
|
||||
export function useAgentsPage() {
|
||||
const conversationStore = useConversationStore()
|
||||
const { currentConversationId } = storeToRefs(conversationStore)
|
||||
|
||||
const NODE_W = 200
|
||||
const NODE_H = 170
|
||||
@@ -46,6 +59,59 @@ interface AgentDraft {
|
||||
selectedSkillIds: string[]
|
||||
}
|
||||
|
||||
interface RuntimeSummaryCard {
|
||||
executionMode: string
|
||||
currentPhase: string
|
||||
currentCheckpoint: string
|
||||
verifierStatus: string
|
||||
verifierSummary: string
|
||||
verifierEvidence: Array<{
|
||||
label: string
|
||||
status: string
|
||||
}>
|
||||
isolationMode: string
|
||||
workspacePath: string | null
|
||||
totalTokens: number
|
||||
estimatedCost: string
|
||||
budgetWarning: boolean
|
||||
activeTaskCount: number
|
||||
completedTaskCount: number
|
||||
topologyNodeCount: number
|
||||
topologyNodes: Array<{
|
||||
agentId: string
|
||||
role: string
|
||||
taskCount: number
|
||||
completedTaskCount: number
|
||||
}>
|
||||
costByAgent: Array<{
|
||||
agentId: string
|
||||
totalTokens: number
|
||||
estimatedCost: string
|
||||
budgetWarning: boolean
|
||||
}>
|
||||
costThresholds: {
|
||||
totalTokens: number
|
||||
estimatedCost: string
|
||||
}
|
||||
toolGovernance: Array<{
|
||||
toolName: string
|
||||
permissionClass: string
|
||||
sideEffectScope: string
|
||||
usageCount: number
|
||||
}>
|
||||
toolGovernanceTotals: {
|
||||
totalTools: number
|
||||
usedTools: number
|
||||
}
|
||||
upgradeCandidates: string[]
|
||||
recentEvents: Array<{
|
||||
eventId: string
|
||||
eventType: string
|
||||
timestamp: string
|
||||
severity: string
|
||||
}>
|
||||
}
|
||||
|
||||
const mainAgents = computed(() => MAIN_AGENT_ORDER.map(id => localAgents[id]))
|
||||
const childAgents = SUB_COMMANDERS
|
||||
const relationLabels = RELATION_LABELS
|
||||
@@ -102,11 +168,41 @@ const isPanning = ref(false)
|
||||
const panStart = reactive({ x: 0, y: 0 })
|
||||
const panOrigin = reactive({ x: 0, y: 0 })
|
||||
const connectionStatus = ref<'connected' | 'disconnected'>('disconnected')
|
||||
const connectionLabel = computed(() => connectionStatus.value === 'connected' ? '瀹炴椂鍚屾' : '绂荤嚎妯″紡')
|
||||
const connectionLabel = computed(() => connectionStatus.value === 'connected' ? '实时同步' : '离线模式')
|
||||
const zoomPercent = computed(() => `${Math.round(zoom.value * 100)}%`)
|
||||
const activeMainId = ref<string | null>(null)
|
||||
const activeChildId = ref<string | null>(null)
|
||||
const agentData = reactive<Record<string, AgentRuntimeState>>({})
|
||||
const runtimeSummary = ref<RuntimeSummaryCard>({
|
||||
executionMode: 'direct',
|
||||
currentPhase: 'phase_0_bootstrap',
|
||||
currentCheckpoint: 'bootstrap.initialized',
|
||||
verifierStatus: 'unknown',
|
||||
verifierSummary: '暂无运行时摘要',
|
||||
verifierEvidence: [],
|
||||
isolationMode: 'none',
|
||||
workspacePath: null,
|
||||
totalTokens: 0,
|
||||
estimatedCost: '$0.000000',
|
||||
budgetWarning: false,
|
||||
activeTaskCount: 0,
|
||||
completedTaskCount: 0,
|
||||
topologyNodeCount: 0,
|
||||
topologyNodes: [],
|
||||
costByAgent: [],
|
||||
costThresholds: {
|
||||
totalTokens: 0,
|
||||
estimatedCost: '$0.000000',
|
||||
},
|
||||
toolGovernance: [],
|
||||
toolGovernanceTotals: {
|
||||
totalTools: 0,
|
||||
usedTools: 0,
|
||||
},
|
||||
upgradeCandidates: [],
|
||||
recentEvents: [],
|
||||
})
|
||||
const runtimeConversationId = computed(() => currentConversationId.value)
|
||||
|
||||
const localAgents = reactive<Record<string, Agent>>(
|
||||
Object.fromEntries([
|
||||
@@ -117,7 +213,7 @@ const localAgents = reactive<Record<string, Agent>>(
|
||||
role: child.role,
|
||||
roleKey: child.id,
|
||||
description: child.description,
|
||||
systemPrompt: `${child.role}锛?{child.description}`,
|
||||
systemPrompt: `${child.role}: ${child.description}`,
|
||||
enabled: true,
|
||||
})),
|
||||
].map(agent => [agent.id, { ...agent }]))
|
||||
@@ -482,7 +578,7 @@ function setRuntimeState(agentId: string, state: AgentStats) {
|
||||
}
|
||||
|
||||
function applyHierarchyStats(stats: AgentHierarchyStats) {
|
||||
agentData.master = { callCount: 47, currentTask: '鍗忚皟缁勭粐閾捐矾', status: 'active' }
|
||||
agentData.master = { callCount: 47, currentTask: '协调协作链路', status: 'active' }
|
||||
let nextMain: string | null = null
|
||||
let nextChild: string | null = null
|
||||
|
||||
@@ -533,14 +629,14 @@ function buildOfflineStats() {
|
||||
current_task: null,
|
||||
status: 'active',
|
||||
sub_commanders: [
|
||||
{ agent_id: 'schedule_analysis', call_count: 4, current_task: '姊崇悊浠婃棩閲嶇偣', status: 'active' },
|
||||
{ agent_id: 'schedule_analysis', call_count: 4, current_task: '梳理今日重点', status: 'active' },
|
||||
{ agent_id: 'schedule_planning', call_count: 9, current_task: null, status: 'idle' },
|
||||
],
|
||||
},
|
||||
{
|
||||
agent_id: 'executor',
|
||||
call_count: 8,
|
||||
current_task: '鍒涘缓鏂囨。',
|
||||
current_task: '创建文档',
|
||||
status: 'idle',
|
||||
sub_commanders: [
|
||||
{ agent_id: 'executor_tasks', call_count: 8, current_task: null, status: 'idle' },
|
||||
@@ -571,16 +667,135 @@ function buildOfflineStats() {
|
||||
} satisfies AgentHierarchyStats
|
||||
}
|
||||
|
||||
function applyRuntimeSummary(
|
||||
summary: AgentVisibilityRuntimeSummary,
|
||||
topology?: AgentVisibilityTopology,
|
||||
verifier?: AgentVisibilityVerifier,
|
||||
costSummary?: AgentVisibilityCostSummary,
|
||||
toolGovernance?: AgentVisibilityToolGovernance,
|
||||
) {
|
||||
const verifierPayload = verifier || summary.verifier
|
||||
const costPayload = costSummary || {
|
||||
conversation_id: summary.conversation_id,
|
||||
total: summary.cost,
|
||||
thresholds: {},
|
||||
by_agent: [],
|
||||
}
|
||||
runtimeSummary.value = {
|
||||
executionMode: summary.execution_mode || 'direct',
|
||||
currentPhase: summary.current_phase || 'phase_0_bootstrap',
|
||||
currentCheckpoint: summary.current_checkpoint || 'bootstrap.initialized',
|
||||
verifierStatus: verifierPayload.status || 'unknown',
|
||||
verifierSummary: verifierPayload.summary || '暂无 verifier 结论',
|
||||
verifierEvidence: (verifierPayload.evidence || []).slice(0, 4).map((entry, index) => ({
|
||||
label: String(entry.task_id || entry.event_type || `evidence-${index}`),
|
||||
status: String(entry.status || entry.summary || 'available'),
|
||||
})),
|
||||
isolationMode: summary.isolation.mode || 'none',
|
||||
workspacePath: summary.isolation.workspace_path || null,
|
||||
totalTokens: summary.cost.total_tokens,
|
||||
estimatedCost: `$${(summary.cost.estimated_cost || 0).toFixed(6)}`,
|
||||
budgetWarning: Boolean(summary.cost.budget_warning),
|
||||
activeTaskCount: summary.active_task_count,
|
||||
completedTaskCount: summary.completed_task_count,
|
||||
topologyNodeCount: summary.topology_node_count,
|
||||
topologyNodes: (topology?.nodes || []).slice(0, 5).map(node => ({
|
||||
agentId: node.agent_id,
|
||||
role: node.role || 'unknown',
|
||||
taskCount: node.task_count,
|
||||
completedTaskCount: node.completed_task_count,
|
||||
})),
|
||||
costByAgent: (costPayload.by_agent || []).slice(0, 4).map(item => ({
|
||||
agentId: item.agent_id,
|
||||
totalTokens: item.total_tokens,
|
||||
estimatedCost: `$${(item.estimated_cost || 0).toFixed(6)}`,
|
||||
budgetWarning: Boolean(item.budget_warning),
|
||||
})),
|
||||
costThresholds: {
|
||||
totalTokens: Number(costPayload.thresholds.total_tokens || 0),
|
||||
estimatedCost: `$${Number(costPayload.thresholds.estimated_cost || 0).toFixed(6)}`,
|
||||
},
|
||||
toolGovernance: (toolGovernance?.items || []).slice(0, 6).map(item => ({
|
||||
toolName: item.tool_name,
|
||||
permissionClass: item.permission_class,
|
||||
sideEffectScope: item.side_effect_scope,
|
||||
usageCount: item.usage_count,
|
||||
})),
|
||||
toolGovernanceTotals: {
|
||||
totalTools: Number(toolGovernance?.total_tools || 0),
|
||||
usedTools: Number(toolGovernance?.used_tools || 0),
|
||||
},
|
||||
upgradeCandidates: [...(toolGovernance?.upgrade_candidates || [])],
|
||||
recentEvents: summary.recent_events.map(event => ({
|
||||
eventId: event.event_id,
|
||||
eventType: event.event_type,
|
||||
timestamp: event.timestamp,
|
||||
severity: event.severity,
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshStats() {
|
||||
loading.value = true
|
||||
try {
|
||||
const stats = await agentApi.getHierarchyStats()
|
||||
applyHierarchyStats(stats)
|
||||
if (runtimeConversationId.value) {
|
||||
try {
|
||||
const [summary, topology, verifier, costSummary, tools] = await Promise.all([
|
||||
agentApi.getRuntimeSummary(runtimeConversationId.value),
|
||||
agentApi.getVisibilityTopology(runtimeConversationId.value),
|
||||
agentApi.getVisibilityVerifier(runtimeConversationId.value),
|
||||
agentApi.getVisibilityCost(runtimeConversationId.value),
|
||||
agentApi.getVisibilityTools(runtimeConversationId.value),
|
||||
])
|
||||
applyRuntimeSummary(summary, topology, verifier, costSummary, tools)
|
||||
} catch {
|
||||
runtimeSummary.value = {
|
||||
...runtimeSummary.value,
|
||||
verifierSummary: '运行时摘要暂不可用',
|
||||
}
|
||||
}
|
||||
} else {
|
||||
runtimeSummary.value = {
|
||||
...runtimeSummary.value,
|
||||
verifierSummary: '请选择一条会话以查看运行时摘要',
|
||||
}
|
||||
}
|
||||
connectionStatus.value = 'connected'
|
||||
stopDemoRouteCycle()
|
||||
} catch {
|
||||
connectionStatus.value = 'disconnected'
|
||||
applyHierarchyStats(buildOfflineStats())
|
||||
runtimeSummary.value = {
|
||||
executionMode: 'direct',
|
||||
currentPhase: 'offline_demo',
|
||||
currentCheckpoint: 'offline.demo_mode',
|
||||
verifierStatus: 'offline',
|
||||
verifierSummary: '当前使用离线演示数据',
|
||||
verifierEvidence: [],
|
||||
isolationMode: 'none',
|
||||
workspacePath: null,
|
||||
totalTokens: 0,
|
||||
estimatedCost: '$0.000000',
|
||||
budgetWarning: false,
|
||||
activeTaskCount: 0,
|
||||
completedTaskCount: 0,
|
||||
topologyNodeCount: 0,
|
||||
topologyNodes: [],
|
||||
costByAgent: [],
|
||||
costThresholds: {
|
||||
totalTokens: 0,
|
||||
estimatedCost: '$0.000000',
|
||||
},
|
||||
toolGovernance: [],
|
||||
toolGovernanceTotals: {
|
||||
totalTools: 0,
|
||||
usedTools: 0,
|
||||
},
|
||||
upgradeCandidates: [],
|
||||
recentEvents: [],
|
||||
}
|
||||
startDemoRouteCycle()
|
||||
} finally {
|
||||
loading.value = false
|
||||
@@ -710,6 +925,7 @@ onUnmounted(() => {
|
||||
selectedNodePackages,
|
||||
selectedNodeSkills,
|
||||
agentData,
|
||||
runtimeSummary,
|
||||
localAgents,
|
||||
viewportStyle,
|
||||
stageStyle,
|
||||
|
||||
@@ -38,6 +38,146 @@
|
||||
<div class="route-main">{{ activeMainRouteLabel }}</div>
|
||||
<div class="route-child">{{ activeChildRouteLabel }}</div>
|
||||
</div>
|
||||
<div class="hud-panel runtime-summary" data-testid="runtime-summary">
|
||||
<div class="hud-title">RUNTIME SUMMARY</div>
|
||||
<div class="runtime-grid">
|
||||
<div class="runtime-item">
|
||||
<span class="runtime-label">MODE</span>
|
||||
<strong>{{ runtimeSummary.executionMode }}</strong>
|
||||
</div>
|
||||
<div class="runtime-item">
|
||||
<span class="runtime-label">PHASE</span>
|
||||
<strong>{{ runtimeSummary.currentPhase }}</strong>
|
||||
</div>
|
||||
<div class="runtime-item">
|
||||
<span class="runtime-label">CHECKPOINT</span>
|
||||
<strong>{{ runtimeSummary.currentCheckpoint }}</strong>
|
||||
</div>
|
||||
<div class="runtime-item">
|
||||
<span class="runtime-label">VERIFIER</span>
|
||||
<strong>{{ runtimeSummary.verifierStatus }}</strong>
|
||||
</div>
|
||||
<div class="runtime-item">
|
||||
<span class="runtime-label">ISOLATION</span>
|
||||
<strong>{{ runtimeSummary.isolationMode }}</strong>
|
||||
</div>
|
||||
<div class="runtime-item">
|
||||
<span class="runtime-label">TOKENS</span>
|
||||
<strong>{{ runtimeSummary.totalTokens }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="runtime-note">{{ runtimeSummary.verifierSummary }}</div>
|
||||
<div class="runtime-meta">
|
||||
<span>Cost {{ runtimeSummary.estimatedCost }}</span>
|
||||
<span>Tasks {{ runtimeSummary.completedTaskCount }}/{{ runtimeSummary.activeTaskCount }}</span>
|
||||
<span>Nodes {{ runtimeSummary.topologyNodeCount }}</span>
|
||||
<span v-if="runtimeSummary.budgetWarning" class="runtime-warning">Budget warning</span>
|
||||
</div>
|
||||
<div v-if="runtimeSummary.workspacePath" class="runtime-workspace">{{ runtimeSummary.workspacePath }}</div>
|
||||
</div>
|
||||
|
||||
<div class="hud-panel runtime-events" data-testid="runtime-events-panel">
|
||||
<div class="hud-title">RECENT EVENTS</div>
|
||||
<div v-if="runtimeSummary.recentEvents.length" class="stack-list">
|
||||
<div
|
||||
v-for="event in runtimeSummary.recentEvents.slice(0, 5)"
|
||||
:key="event.eventId"
|
||||
class="stack-item"
|
||||
>
|
||||
<div class="stack-line">
|
||||
<strong>{{ event.eventType }}</strong>
|
||||
<span class="event-severity" :class="event.severity">{{ event.severity }}</span>
|
||||
</div>
|
||||
<div class="stack-subline">{{ event.timestamp }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="stack-empty">暂无 recent events。</div>
|
||||
</div>
|
||||
|
||||
<div class="hud-panel runtime-drilldown" data-testid="runtime-drilldown-panel">
|
||||
<div class="hud-title">TOPOLOGY & VERIFIER</div>
|
||||
<div class="mini-section">
|
||||
<div class="mini-title">Topology</div>
|
||||
<div v-if="runtimeSummary.topologyNodes.length" class="stack-list">
|
||||
<div
|
||||
v-for="node in runtimeSummary.topologyNodes"
|
||||
:key="node.agentId"
|
||||
class="stack-item"
|
||||
>
|
||||
<div class="stack-line">
|
||||
<strong>{{ node.agentId }}</strong>
|
||||
<span>{{ node.role }}</span>
|
||||
</div>
|
||||
<div class="stack-subline">Tasks {{ node.completedTaskCount }}/{{ node.taskCount }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="stack-empty">暂无 topology 详情。</div>
|
||||
</div>
|
||||
<div class="mini-section">
|
||||
<div class="mini-title">Verifier Evidence</div>
|
||||
<div v-if="runtimeSummary.verifierEvidence.length" class="stack-list">
|
||||
<div
|
||||
v-for="entry in runtimeSummary.verifierEvidence"
|
||||
:key="entry.label"
|
||||
class="stack-item"
|
||||
>
|
||||
<div class="stack-line">
|
||||
<strong>{{ entry.label }}</strong>
|
||||
<span>{{ entry.status }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="stack-empty">暂无 verifier evidence。</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hud-panel runtime-governance" data-testid="runtime-governance-panel">
|
||||
<div class="hud-title">COST & TOOLS</div>
|
||||
<div class="mini-section">
|
||||
<div class="mini-title">Cost By Agent</div>
|
||||
<div v-if="runtimeSummary.costByAgent.length" class="stack-list">
|
||||
<div
|
||||
v-for="item in runtimeSummary.costByAgent"
|
||||
:key="item.agentId"
|
||||
class="stack-item"
|
||||
>
|
||||
<div class="stack-line">
|
||||
<strong>{{ item.agentId }}</strong>
|
||||
<span>{{ item.totalTokens }} tk</span>
|
||||
</div>
|
||||
<div class="stack-subline">
|
||||
{{ item.estimatedCost }}
|
||||
<span v-if="item.budgetWarning" class="runtime-warning">warning</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="stack-empty">暂无 cost breakdown。</div>
|
||||
<div class="stack-subline threshold-line">
|
||||
Thresholds: {{ runtimeSummary.costThresholds.totalTokens }} tk / {{ runtimeSummary.costThresholds.estimatedCost }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="mini-section">
|
||||
<div class="mini-title">Tool Governance</div>
|
||||
<div class="stack-subline">Used {{ runtimeSummary.toolGovernanceTotals.usedTools }}/{{ runtimeSummary.toolGovernanceTotals.totalTools }}</div>
|
||||
<div v-if="runtimeSummary.toolGovernance.length" class="stack-list">
|
||||
<div
|
||||
v-for="tool in runtimeSummary.toolGovernance"
|
||||
:key="tool.toolName"
|
||||
class="stack-item"
|
||||
>
|
||||
<div class="stack-line">
|
||||
<strong>{{ tool.toolName }}</strong>
|
||||
<span>{{ tool.usageCount }}x</span>
|
||||
</div>
|
||||
<div class="stack-subline">{{ tool.permissionClass }} · {{ tool.sideEffectScope }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="stack-empty">暂无 tool governance 数据。</div>
|
||||
<div v-if="runtimeSummary.upgradeCandidates.length" class="candidate-list">
|
||||
<span v-for="candidate in runtimeSummary.upgradeCandidates" :key="candidate" class="candidate-chip">{{ candidate }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="nodes-viewport" :style="viewportStyle">
|
||||
@@ -104,7 +244,7 @@
|
||||
<div class="node-desc">{{ getAgentDesc('master') }}</div>
|
||||
<div class="node-footer">
|
||||
<span class="node-stat">
|
||||
<span class="stat-label">璋冪敤</span>
|
||||
<span class="stat-label">调用</span>
|
||||
<span class="stat-val">{{ agentData.master?.callCount || 0 }}</span>
|
||||
</span>
|
||||
<span v-if="agentData.master?.currentTask" class="node-task-tag">{{ agentData.master.currentTask }}</span>
|
||||
@@ -135,7 +275,7 @@
|
||||
<div class="node-desc">{{ getAgentDesc(agent.id) }}</div>
|
||||
<div class="node-footer">
|
||||
<span class="node-stat">
|
||||
<span class="stat-label">璋冪敤</span>
|
||||
<span class="stat-label">调用</span>
|
||||
<span class="stat-val">{{ agentData[agent.id]?.callCount || 0 }}</span>
|
||||
</span>
|
||||
<span v-if="agentData[agent.id]?.currentTask" class="node-task-tag">{{ agentData[agent.id]?.currentTask }}</span>
|
||||
@@ -168,7 +308,7 @@
|
||||
<div class="node-desc">{{ child.description }}</div>
|
||||
<div class="node-footer">
|
||||
<span class="node-stat">
|
||||
<span class="stat-label">璋冪敤</span>
|
||||
<span class="stat-label">调用</span>
|
||||
<span class="stat-val">{{ agentData[child.id]?.callCount || 0 }}</span>
|
||||
</span>
|
||||
<span v-if="agentData[child.id]?.currentTask" class="node-task-tag">{{ agentData[child.id]?.currentTask }}</span>
|
||||
@@ -181,13 +321,13 @@
|
||||
</div>
|
||||
|
||||
<div class="canvas-controls">
|
||||
<button class="control-chip zoom-chip" @click="zoomOut" title="缂╁皬瑙嗗浘">
|
||||
<button class="control-chip zoom-chip" @click="zoomOut" title="缩小视图">
|
||||
<span class="chip-symbol">-</span>
|
||||
</button>
|
||||
<button class="control-chip zoom-readout" @click="resetView" title="閲嶇疆瑙嗗浘">
|
||||
<button class="control-chip zoom-readout" @click="resetView" title="重置视图">
|
||||
<span class="chip-value">{{ zoomPercent }}</span>
|
||||
</button>
|
||||
<button class="control-chip zoom-chip" @click="zoomIn" title="鏀惧ぇ瑙嗗浘">
|
||||
<button class="control-chip zoom-chip" @click="zoomIn" title="放大视图">
|
||||
<span class="chip-symbol">+</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -239,7 +379,7 @@
|
||||
<span>{{ pkg.stateLabel }}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="skillsLoading" class="linked-skills-state" data-testid="linked-skills-loading">鍔犺浇鎶€鑳戒腑...</div>
|
||||
<div v-if="skillsLoading" class="linked-skills-state" data-testid="linked-skills-loading">加载技能中...</div>
|
||||
<div v-else-if="skillsError" class="linked-skills-state linked-skills-error" data-testid="linked-skills-error">{{ skillsError }}</div>
|
||||
<div v-else-if="saveError" class="linked-skills-state linked-skills-error" data-testid="linked-skills-save-error">{{ saveError }}</div>
|
||||
<div v-else-if="selectedNodeSkills.length === 0" class="linked-skills-state" data-testid="linked-skills-empty">
|
||||
@@ -264,7 +404,7 @@
|
||||
<span class="linked-skill-name">{{ skill.name }}</span>
|
||||
<span class="linked-skill-agent-type">{{ skill.agent_type }}</span>
|
||||
</div>
|
||||
<div class="linked-skill-desc">{{ skill.description || '鏆傛棤鎻忚堪' }}</div>
|
||||
<div class="linked-skill-desc">{{ skill.description || '暂无描述' }}</div>
|
||||
<div v-if="skill.tools.length > 0" class="linked-skill-tools">
|
||||
<span v-for="tool in skill.tools" :key="tool" class="linked-skill-tool">{{ tool }}</span>
|
||||
</div>
|
||||
@@ -273,10 +413,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="drawer-actions">
|
||||
<button class="btn-secondary" data-testid="linked-skills-reset" @click="resetConfig">閲嶇疆</button>
|
||||
<button class="btn-secondary" data-testid="linked-skills-reset" @click="resetConfig">重置</button>
|
||||
<button class="btn-primary" data-testid="linked-skills-save" @click="saveConfig" :disabled="saving">
|
||||
<span v-if="saving" class="btn-loader"></span>
|
||||
{{ saving ? '淇濆瓨涓?..' : '淇濆瓨閰嶇疆' }}
|
||||
{{ saving ? '保存中...' : '保存配置' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -293,11 +433,11 @@
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label class="form-label">// AGENT NAME</label>
|
||||
<input v-model="newAgent.name" type="text" class="form-input" placeholder="渚嬪: CODER" />
|
||||
<input v-model="newAgent.name" type="text" class="form-input" placeholder="例如: CODER" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">// ROLE KEY (鑻辨枃鍞竴鏍囪瘑)</label>
|
||||
<input v-model="newAgent.roleKey" type="text" class="form-input" placeholder="渚嬪: coder" />
|
||||
<label class="form-label">// ROLE KEY (英文唯一标识)</label>
|
||||
<input v-model="newAgent.roleKey" type="text" class="form-input" placeholder="例如: coder" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">// ROLE</label>
|
||||
@@ -305,15 +445,15 @@
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">// DESCRIPTION</label>
|
||||
<textarea v-model="newAgent.description" class="form-textarea" rows="2" placeholder="鎻忚堪姝?Agent 鐨勮亴璐?.."></textarea>
|
||||
<textarea v-model="newAgent.description" class="form-textarea" rows="2" placeholder="描述该 Agent 的职责..."></textarea>
|
||||
</div>
|
||||
<div class="form-group flex-1">
|
||||
<label class="form-label">// SYSTEM PROMPT</label>
|
||||
<textarea v-model="newAgent.systemPrompt" class="form-textarea code-textarea" rows="6" placeholder="杈撳叆绯荤粺鎻愮ず璇?.."></textarea>
|
||||
<textarea v-model="newAgent.systemPrompt" class="form-textarea code-textarea" rows="6" placeholder="输入系统提示词..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn-secondary" @click="addModalOpen = false">鍙栨秷</button>
|
||||
<button class="btn-secondary" @click="addModalOpen = false">取消</button>
|
||||
<button class="btn-primary" @click="addAgent" :disabled="!newAgent.name || !newAgent.roleKey">创建智能体</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -360,6 +500,7 @@ const {
|
||||
selectedNodePackages,
|
||||
selectedNodeSkills,
|
||||
agentData,
|
||||
runtimeSummary,
|
||||
localAgents,
|
||||
viewportStyle,
|
||||
stageStyle,
|
||||
|
||||
Reference in New Issue
Block a user