import { useRef, useState } from 'react'; import { ArrowRight, BookOpen, ChevronDown, LoaderCircle, Pencil } from 'lucide-react'; import { api } from '../api'; import type { KnowledgeMethod, KnowledgeMethods, KnowledgeMethodStatus, ValidationProof } from '../types'; import { displayTime } from '../taskView'; type DisplayStatus = KnowledgeMethodStatus | 'inapplicable'; const STATUS: Record = { preferred: '优先复用', trial: '可试用', exploration: '探索候选', paused: '当前场景暂停', inapplicable: '本阶段不适用', legacy: '历史记录' }; function displayStatus(method: KnowledgeMethod): DisplayStatus { if (method.status === 'paused' && !method.applicability && !method.match_reasons.includes('unresolved_contradiction')) return 'inapplicable'; return STATUS[method.status] ? method.status : 'legacy'; } const ORIGIN: Record = { program_template: '预置程序模板', preset: '预置阶段模板', observed: '真实执行归纳', model: '模型建议', manual: '人工补充', legacy: '历史记录', historical: '历史记录' }; const VERIFIER: Record = { program: '程序验收', system: '系统验收', program_assertion: '程序验收', program_evidence: '程序证据核验', model: '模型报告', human: '人工确认', manual: '人工确认', source_container_quote_and_persisted: '来源归属、原话与保存结果核对', reply_delta_and_parent: '新增回复与所属讨论核对', profile_source_header: '来源作者与主页账号核对', file_manifest_and_current_result: '文件存在及当前版本核对', same_note_saved_sources: '同篇正文与讨论的保存结果核对' }; const OUTCOME: Record = { success: '有证据的成功', contradiction: '相关反证', blocked: '受阻', program_error: '程序异常', unknown: '结果未知', unused: '未使用', not_used: '未使用' }; const MATCH_REASON: Record = { scope_matches: '已通过当前已提供条件的匹配检查', platform_mismatch: '平台不匹配', stage_mismatch: '任务阶段不匹配', capability_mismatch: '所需操作能力不匹配', source_kind_mismatch: '来源类型不匹配', page_variant_mismatch: '页面结构版本不匹配', method_version_mismatch: '方法版本不匹配', dependency_versions_unconfirmed: '依赖能力的版本尚未确认', dependency_version_mismatch: '依赖版本不匹配', action_not_authorized: '本次尚未授权所需动作', unresolved_contradiction: '存在未解决的相关反证', historical_validation_incomplete: '历史验证依据不完整,不参与本次优先推荐', }; const SCOPE: Record = { any: '不限', unknown: '未记录', xiaohongshu: '小红书', note: '笔记', comment: '评论', reply: '回复', source_capture: '来源取证', search_read: '搜索阅读', export: '名单导出', save_source: '保存来源', inspect_profile: '核对主页', expand_replies: '展开回复', follow: '关注', message: '私信' }; const FIELD: Record = { type: '验收类型', checks: '验收条件', description: '说明', text: '说明', title: '说明', required: '必要条件', action: '操作', outcome: '结果', capability: '能力', source_kind: '来源类型', version: '版本', source_id: '来源标识', source_type: '来源类型', content: '来源原文', quote: '来源原话', observed_at: '观察时间', persisted: '已持久保存', verified: '核验状态', count: '数量', reply_count: '回复数量', parent_source_id: '父来源标识', root_source_id: '根来源标识', profile_url: '主页入口', xiaohongshu_id: '页面可见小红书号', result_version: '结果版本', goal_version: '目标版本', filename: '文件名', available: '文件可用', exists: '文件存在', path: '文件路径', matched: '归属匹配', note_id: '笔记标识', container_selector: '来源容器定位', observed_excerpt: '页面实际摘录', quote_matched: '原话与来源相符', relations_checked: '讨论归属已核对', owner_selector: '所属讨论定位', fingerprint: '页面状态指纹', parent_scope_checked: '所属父讨论一致', owner_reply_delta: '本讨论新增回复数', new_visible_replies: '新出现的可见回复数', new_reply_fingerprints: '新增回复指纹', expected_profile_digest: '来源主页关联标识', matched_source_author: '当前主页与来源作者匹配', visible_account_label_checked: '可见账号标签已核对', persisted_profile: '主页结果已保存', export_id: '文件记录标识', current_version: '对应当前结果', row_count: '文件记录数', format: '文件格式', }; function count(value: number | null | undefined): string { return typeof value === 'number' && Number.isFinite(value) ? String(value) : '未记录'; } function lines(value: unknown, depth = 0): string[] { if (depth > 4 || value === null || value === undefined) return []; if (typeof value === 'string') return value ? [value] : []; if (typeof value === 'number') return [String(value)]; if (typeof value === 'boolean') return [value ? '是' : '否']; if (Array.isArray(value)) return value.flatMap(item => lines(item, depth + 1)); if (typeof value === 'object') return Object.entries(value).flatMap(([key, item]) => lines(item, depth + 1).map(line => `${FIELD[key] || key}:${line}`)); return []; } function ValidationProofView({ evidenceRef }: { evidenceRef: KnowledgeMethod['evidence_refs'][number] }) { const reference = Array.isArray(evidenceRef.pre) ? evidenceRef.pre[0] : evidenceRef.pre; const valid = Boolean(evidenceRef.task_id && typeof reference === 'string' && /^[\w.:/\-]{1,512}$/.test(reference) && !reference.includes('://') && !/^(?:https?|file|data|javascript):/i.test(reference)); const [proof, setProof] = useState(null); const [open, setOpen] = useState(false); const [loading, setLoading] = useState(false); const [failed, setFailed] = useState(false); const requesting = useRef(false); async function show(): Promise { if (!valid || requesting.current) return; if (proof) { setOpen(value => !value); return; } requesting.current = true; setLoading(true); setFailed(false); setOpen(true); try { const result = await api.get(`/tasks/${encodeURIComponent(evidenceRef.task_id!)}/validation-proof?reference=${encodeURIComponent(reference!)}${evidenceRef.event_id ? `&event_id=${encodeURIComponent(evidenceRef.event_id)}` : ''}`); setProof(result); } catch { setFailed(true); } finally { requesting.current = false; setLoading(false); } } const before = proof ? lines(proof.pre) : []; const after = proof ? lines(proof.post) : []; return
{!valid &&

历史记录未保存可读取的动作引用。

} {open && failed &&

这条验收快照暂时无法读取,已有成果仍可查看。可以重试或打开任务证据。

} {open && proof &&
{SCOPE[proof.capability] || proof.capability}{OUTCOME[proof.outcome] || proof.outcome}

{VERIFIER[proof.verifier] || proof.verifier || '验证方式未记录'}{proof.observed_at && <> · {displayTime(proof.observed_at)}}

动作前状态

{before.length ?
    {before.map((line, index) =>
  • {line}
  • )}
:

这条记录未保存动作前快照。

}

动作后状态

{after.length ?
    {after.map((line, index) =>
  • {line}
  • )}
:

这条记录未保存动作后快照。

}

这里读取原任务已保存的程序快照,不会重新执行动作。原话和账号资料仅在对应任务证据中展示。

}
; } function MethodFeedbackForm({ methodId, taskId }: { methodId: string; taskId?: string }) { const [open, setOpen] = useState(false); const [text, setText] = useState(''); const [sending, setSending] = useState(false); const [failed, setFailed] = useState(false); const [saved, setSaved] = useState(false); const composing = useRef(false); const submitting = useRef(false); async function submit(): Promise { if (!text.trim() || submitting.current || composing.current) return; submitting.current = true; setSending(true); setFailed(false); setSaved(false); try { const result = await api.post<{ ok: boolean }>(`/knowledge/methods/${encodeURIComponent(methodId)}/feedback`, { text: text.trim(), ...(taskId ? { task_id: taskId } : {}) }); if (result.ok !== true) throw new Error('feedback not confirmed'); setSaved(true); setOpen(false); setText(''); } catch { setFailed(true); } finally { submitting.current = false; setSending(false); } } return
{saved &&

已记录,系统结合后续证据判断。

} {open &&
{ composing.current = true; }} onCompositionEnd={() => { composing.current = false; }} onKeyDown={event => { if (event.key === 'Enter' && (composing.current || event.nativeEvent.isComposing)) event.preventDefault(); }} onSubmit={event => { event.preventDefault(); void submit(); }}>