diff --git a/heicode/VERSION b/heicode/VERSION index ac9f79c..079d7f6 100644 --- a/heicode/VERSION +++ b/heicode/VERSION @@ -1 +1 @@ -1.4.10 +1.4.11 diff --git a/heicode/web/default/src/components/command-menu.tsx b/heicode/web/default/src/components/command-menu.tsx index 74cb4c5..feace7b 100644 --- a/heicode/web/default/src/components/command-menu.tsx +++ b/heicode/web/default/src/components/command-menu.tsx @@ -37,7 +37,13 @@ export function CommandMenu() { ) return ( - + @@ -79,7 +85,7 @@ export function CommandMenu() { ))} - + runCommand(() => setTheme('light'))}> {t('Light')} diff --git a/heicode/web/default/src/features/agnet-console/pages.tsx b/heicode/web/default/src/features/agnet-console/pages.tsx index 69635ca..82bc519 100644 --- a/heicode/web/default/src/features/agnet-console/pages.tsx +++ b/heicode/web/default/src/features/agnet-console/pages.tsx @@ -90,6 +90,7 @@ const STATUS_TO_KEY: Record = { running: 'running', active: 'running', in_progress: 'running', + stopped: 'success', succeeded: 'success', success: 'success', completed: 'success', @@ -105,7 +106,44 @@ function classifyStatus(status: string): StatusKey { return STATUS_TO_KEY[(status || '').toLowerCase()] ?? 'pending' } +function formatStatusLabel( + status: string | undefined, + t: (key: string) => string +): string { + const value = (status || '').trim() + if (!value) return t('Unknown') + const normalized = value.toLowerCase() + if (normalized.startsWith('handoff:')) { + const tail = normalized.replace('handoff:', '') + return `${t('Handoff')}: ${formatStatusLabel(tail, t)}` + } + const labels: Record = { + running: t('Running'), + active: t('Running'), + in_progress: t('In progress'), + stopped: t('Stopped'), + succeeded: t('Success'), + success: t('Success'), + completed: t('Completed'), + failed: t('Failed'), + error: t('Failed'), + rejected: t('Rejected'), + pending: t('Pending'), + queued: t('Queued'), + awaiting: t('Awaiting'), + observed: t('Observed'), + blocked: t('Blocked'), + requested: t('Requested'), + high: t('High'), + medium: t('Medium'), + mid: t('Medium'), + low: t('Low'), + } + return labels[normalized] || value +} + function StatusBadge({ phase }: { phase: string }) { + const { t } = useTranslation() const k = classifyStatus(phase) const map: Record = { running: { @@ -134,7 +172,7 @@ function StatusBadge({ phase }: { phase: string }) { )} > {m.icon} - {phase || 'unknown'} + {formatStatusLabel(phase, t)} ) } @@ -238,22 +276,37 @@ function describeRiskLevel(dep: AgnetDeployment): { return { label: 'low', tone: 'low' } } +function formatRiskLabel(label: string, t: (key: string) => string): string { + const value = label.toLowerCase() + if (value === 'high') return t('High') + if (value === 'medium' || value === 'mid') return t('Medium') + if (value === 'low') return t('Low') + return label +} + function describeSubMode(dep: AgnetDeployment): string { return dep.sub_mode || dep.orchestration_plan?.sub_mode || 'agile' } +function formatSubModeLabel(mode: string, t: (key: string) => string): string { + const value = mode.toLowerCase() + if (value === 'agile') return t('Agile') + if (value === 'waterfall') return t('Waterfall') + return mode +} + function describeBudget(dep: AgnetDeployment): string { const budget = dep.orchestration_plan?.budget if (!budget) { const agents = dep.orchestration_plan?.agents?.length ?? 0 - return `${agents} agents` + return `${agents} 个 Agent` } const parts = [] if (budget.max_cost_usd > 0) parts.push(`$${budget.max_cost_usd}`) - if (budget.max_tokens > 0) parts.push(`${budget.max_tokens} tokens`) + if (budget.max_tokens > 0) parts.push(`${budget.max_tokens} 个 token`) if (budget.max_duration_sec > 0) - parts.push(`${Math.round(budget.max_duration_sec / 60)}m`) + parts.push(`${Math.round(budget.max_duration_sec / 60)} 分钟`) return parts.length > 0 ? parts.join(' / ') : '—' } @@ -277,9 +330,11 @@ function compactRuntimeRef(value: string | undefined): string { function formatRecordSource(value: unknown): string { const source = String(value || '').trim() - if (!source) return 'manager' - if (source.includes('simulate')) return 'simulated' - if (source.includes('runtime') || source.includes('swarm')) return 'runtime' + const normalized = source.toLowerCase() + if (!source || normalized.includes('manager')) return 'Manager' + if (normalized.includes('simulate')) return '模拟' + if (normalized.includes('runtime') || normalized.includes('swarm')) + return 'Runtime' return source } @@ -324,11 +379,11 @@ function taskFlowDetail(item: Record): string { const attempt = String(recordPayloadValue(item, 'attempt') || '') const agentRole = String(recordPayloadValue(item, 'agent_role') || '') const parts = [ - taskId && `task ${taskId}`, - agentRole && `role ${agentRole}`, + taskId && `任务 ${taskId}`, + agentRole && `角色 ${agentRole}`, fromRole && toRole && `${fromRole} -> ${toRole}`, - reason && `reason ${reason}`, - attempt && `attempt ${attempt}`, + reason && `原因 ${reason}`, + attempt && `第 ${attempt} 次`, ].filter(Boolean) return parts.join(' / ') || '—' } @@ -415,13 +470,13 @@ function formatRelativeTime(value: string | undefined): string { if (Number.isNaN(date.getTime())) return value const diff = Date.now() - date.getTime() const sec = Math.round(diff / 1000) - if (sec < 60) return `${sec}s ago` + if (sec < 60) return `${sec} 秒前` const min = Math.round(sec / 60) - if (min < 60) return `${min}m ago` + if (min < 60) return `${min} 分钟前` const hr = Math.round(min / 60) - if (hr < 24) return `${hr}h ago` + if (hr < 24) return `${hr} 小时前` const day = Math.round(hr / 24) - return `${day}d ago` + return `${day} 天前` } // maskSecretRef shows enough of a secret_ref to identify which vault @@ -461,6 +516,18 @@ function grantStatusToneClass(status: string | undefined): string { } } +function formatGrantStatus( + status: string | undefined, + t: (key: string) => string +): string { + const value = (status || 'active').toLowerCase() + if (value === 'active') return t('Active') + if (value === 'pending') return t('Pending') + if (value === 'disabled') return t('Disabled') + if (value === 'revoked') return t('Revoked') + return status || t('Active') +} + function RunDetailPanel({ dep }: { dep: AgnetDeployment }) { const { t } = useTranslation() const queryClient = useQueryClient() @@ -527,13 +594,21 @@ function RunDetailPanel({ dep }: { dep: AgnetDeployment }) {
- + + -
@@ -555,16 +630,16 @@ function RunDetailPanel({ dep }: { dep: AgnetDeployment }) { @@ -694,7 +769,7 @@ function RunDetailPanel({ dep }: { dep: AgnetDeployment }) { grantStatusToneClass(status) )} > - {status} + {formatGrantStatus(status, t)} @@ -1112,7 +1187,7 @@ function RunRelatedRecordsPanel({ deploymentId }: { deploymentId: string }) { ) : (
    {taskFlowRecords.slice(0, 8).map((item, idx) => { - const event = String(item.event_type || item.event || 'event') + const event = String(item.event_type || item.event || t('Event')) const source = formatRecordSource(item.source) const detail = taskFlowDetail(item) return ( @@ -1222,7 +1297,7 @@ function RunRelatedRecordsPanel({ deploymentId }: { deploymentId: string }) { artifactTypeToneClass(item.artifact_type) )} > - {String(item.artifact_type || 'artifact')} + {String(item.artifact_type || t('Artifact'))}

    @@ -1254,10 +1329,10 @@ function RunRelatedRecordsPanel({ deploymentId }: { deploymentId: string }) { className='bg-muted/25 rounded-lg p-2' >

    - {String(item.snapshot_id || 'snapshot')} + {String(item.snapshot_id || t('Snapshot'))}

    - {String(item.source_type || 'source')} ·{' '} + {String(item.source_type || t('Source'))} ·{' '} {String(item.source_ref || '—')}

    @@ -1278,7 +1353,7 @@ function RunRelatedRecordsPanel({ deploymentId }: { deploymentId: string }) { ) : (
      {timeline.slice(0, 8).map((item, idx) => { - const event = String(item.event_type || item.event || 'event') + const event = String(item.event_type || item.event || t('Event')) const source = formatRecordSource(item.source) return (
    1. string +): string { + if (level === 'all') return t('All levels') + if (level === 'info') return t('Info') + if (level === 'warn') return t('Warning') + if (level === 'error') return t('Error') + return level +} + function classifyEventLevel(entry: Record): EventLevel { const candidate = String( entry.level || entry.severity || entry.status || '' @@ -1391,7 +1477,7 @@ export function AgnetEventsPage() { {EVENT_LEVELS.map((lv) => ( - {lv} + {formatEventLevelLabel(lv, t)} ))} @@ -1655,6 +1741,7 @@ function AgnetApprovalCard({ onApprove: () => void onReject: () => void }) { + const { t } = useTranslation() return (
      @@ -1675,7 +1762,7 @@ function AgnetApprovalCard({
      @@ -1685,14 +1772,14 @@ function AgnetApprovalCard({ disabled={rejectBusy || approveBusy} onClick={onReject} > - Reject + {t('Reject')}
      @@ -1708,6 +1795,7 @@ function AgnetLeaseCard({ busy: boolean onRevoke: () => void }) { + const { t } = useTranslation() return (
      @@ -1727,7 +1815,7 @@ function AgnetLeaseCard({
      diff --git a/heicode/web/default/src/i18n/locales/zh.json b/heicode/web/default/src/i18n/locales/zh.json index 6cb15a0..c7867aa 100644 --- a/heicode/web/default/src/i18n/locales/zh.json +++ b/heicode/web/default/src/i18n/locales/zh.json @@ -4081,9 +4081,75 @@ "Resource binding": "资源绑定", "Show manifest": "展开权限清单", "Hide manifest": "收起权限清单", + "Resource": "资源", "Allowed actions": "允许动作", "Constraints": "约束", "secret_ref": "密钥引用", + "runtime_id": "运行 ID", + "swarm_id": "蜂群 ID", + "mode": "模式", + "runtime": "运行时", + "Control plane": "控制面", + "Stopped": "已停止", + "In progress": "进行中", + "Awaiting": "等待中", + "Observed": "已观测", + "Blocked": "已阻塞", + "Requested": "已请求", + "Rejected": "已拒绝", + "Active": "启用中", + "Disabled": "已禁用", + "Revoked": "已撤销", + "High": "高", + "Medium": "中", + "Low": "低", + "Agile": "敏捷", + "Waterfall": "瀑布", + "Handoff": "交接", + "Event": "事件", + "Snapshot": "快照", + "Artifact": "产物", + "Command Palette": "命令面板", + "Search for a command to run...": "搜索可执行命令...", + "Related records": "关联记录", + "Callbacks, artifacts, SK snapshots and merged timeline": "回调、产物、SK 快照和合并时间线", + "callbacks": "回调", + "artifacts": "产物", + "timeline": "时间线", + "Sub task flow": "子任务流", + "No task, retry or handoff callback records yet": "暂无任务、重试或交接回调记录", + "Agent task map": "Agent 任务图", + "Waiting for Runtime task graph callbacks. Manager will show task, Agent role, handoff and source here when callbacks arrive.": "等待 Runtime 回传任务图。收到回调后,这里会展示任务、Agent 角色、交接关系和来源。", + "Agent role": "Agent 角色", + "Artifacts": "产物", + "No artifacts yet": "暂无产物", + "SK snapshots": "SK 快照", + "No SK snapshots yet": "暂无 SK 快照", + "Merged timeline": "合并时间线", + "No timeline records yet": "暂无时间线记录", + "Simulate": "模拟事件", + "Simulated events recorded": "模拟事件已记录", + "Failed to simulate events": "模拟事件失败", + "All levels": "全部级别", + "Info": "信息", + "Active credential leases": "有效凭证租约", + "Approve or reject high-risk Agnet operations before credentials are leased.": "在下发凭证租约前,审批或拒绝高危 Agnet 操作。", + "No pending approvals": "暂无待审批项", + "High-risk operations will appear here.": "高危操作会显示在这里。", + "No active credential leases": "暂无有效凭证租约", + "Only short-lived lease references are shown. Revoke after the task ends.": "这里只展示短期租约引用。任务结束后请及时撤销。", + "Approved credential leases will appear here.": "已审批的凭证租约会显示在这里。", + "Approve": "批准", + "Reject": "拒绝", + "Revoke": "撤销", + "Not required": "不需要", + "Approval accepted": "审批已通过", + "Approval rejected": "审批已拒绝", + "Credential lease revoked": "凭证租约已撤销", + "Approved from Manager audit page": "从 Manager 审计页批准", + "Rejected from Manager audit page": "从 Manager 审计页拒绝", + "Revoked from Manager audit page": "从 Manager 审计页撤销", + "Connect code, docs and cloud resources for the current task, then confirm the recommendation before launching Agnet.": "为当前任务绑定代码、文档和云资源,然后确认推荐方案再启动 Agnet。", "Plaintext credentials are never shown. The secret_ref column is a vault pointer, not the secret itself.": "永远不展示明文密钥。表中的 secret_ref 只是密钥保管器里的引用指针,不是密钥本体。", "Confirm before launch": "确认后启动", "Heicode summarises what this run will do, what resources it can use, what stays off-limits, and the expected cost. Confirm to launch.": "Heicode 已汇总本次会做什么、能用哪些资源、哪些事情不会做、以及预计消耗。请确认后启动。",