feat(client): show digital artifact sources
This commit is contained in:
@@ -1974,6 +1974,11 @@
|
|||||||
.de-workday-result { overflow: auto; min-height: 0; }
|
.de-workday-result { overflow: auto; min-height: 0; }
|
||||||
.de-workday-result-summary { margin: 0; color: #173550; font-size: 13px; line-height: 1.55; }
|
.de-workday-result-summary { margin: 0; color: #173550; font-size: 13px; line-height: 1.55; }
|
||||||
.de-artifact-pills { display: flex; flex-wrap: wrap; gap: 8px; }
|
.de-artifact-pills { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||||
|
.de-artifact-source-list { display: grid; gap: 8px; max-height: 320px; overflow-y: auto; padding-right: 4px; }
|
||||||
|
.de-artifact-source-row { display: grid; grid-template-columns: minmax(120px, 1fr) minmax(88px, 140px); gap: 4px 10px; align-items: center; padding: 9px 10px; border-radius: 8px; border: 1px solid rgba(79,172,254,0.12); background: rgba(244,250,255,0.62); }
|
||||||
|
.de-artifact-source-row strong { min-width: 0; color: var(--de-text-primary); font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
|
.de-artifact-source-row span { color: var(--de-text-muted); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; text-align: right; }
|
||||||
|
.de-artifact-source-row code, .de-artifact-source-row em { grid-column: 1 / -1; min-width: 0; color: var(--de-text-body); font-size: 12px; font-style: normal; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
.de-decision-card { background: rgba(247,251,255,0.96); }
|
.de-decision-card { background: rgba(247,251,255,0.96); }
|
||||||
.de-decision-chain-note {
|
.de-decision-chain-note {
|
||||||
display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
|
display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
|
||||||
|
|||||||
@@ -1324,7 +1324,14 @@ function buildArtifacts(snapshot: QimingclawSnapshot | null): ArtifactEntry[] {
|
|||||||
const artifacts = [
|
const artifacts = [
|
||||||
...runtimeArtifacts(snapshot).map(artifactFromRuntimeRecord),
|
...runtimeArtifacts(snapshot).map(artifactFromRuntimeRecord),
|
||||||
...runtimeRuns(snapshot).flatMap((run) => artifactsForRun(run, runtimeEvents(snapshot))),
|
...runtimeRuns(snapshot).flatMap((run) => artifactsForRun(run, runtimeEvents(snapshot))),
|
||||||
...runtimeEvents(snapshot).flatMap((event) => artifactsFromPayload(event.id, event.payload, event.occurredAt)),
|
...runtimeEvents(snapshot).flatMap((event) => artifactsFromPayload(event.id, event.payload, event.occurredAt, {
|
||||||
|
sourceType: 'event',
|
||||||
|
sourceEventId: event.id,
|
||||||
|
sourceLabel: `Event ${event.kind}`,
|
||||||
|
planId: event.planId,
|
||||||
|
taskId: event.taskId,
|
||||||
|
runId: event.runId,
|
||||||
|
})),
|
||||||
];
|
];
|
||||||
return dedupeArtifacts(artifacts);
|
return dedupeArtifacts(artifacts);
|
||||||
}
|
}
|
||||||
@@ -1343,6 +1350,8 @@ function artifactFromRuntimeRecord(artifact: QimingclawArtifactRecord): Artifact
|
|||||||
run_id: artifact.runId ?? null,
|
run_id: artifact.runId ?? null,
|
||||||
created_at: artifact.createdAt,
|
created_at: artifact.createdAt,
|
||||||
source: 'qimingclaw-runtime',
|
source: 'qimingclaw-runtime',
|
||||||
|
source_type: 'artifact',
|
||||||
|
source_label: artifactSourceLabel('artifact', artifact.id),
|
||||||
sync_status: artifact.syncStatus,
|
sync_status: artifact.syncStatus,
|
||||||
sync_error: artifact.syncError,
|
sync_error: artifact.syncError,
|
||||||
payload: artifact.payload,
|
payload: artifact.payload,
|
||||||
@@ -1351,16 +1360,43 @@ function artifactFromRuntimeRecord(artifact: QimingclawArtifactRecord): Artifact
|
|||||||
|
|
||||||
function artifactsForRun(run: QimingclawRunRecord, events: QimingclawEventRecord[]): ArtifactEntry[] {
|
function artifactsForRun(run: QimingclawRunRecord, events: QimingclawEventRecord[]): ArtifactEntry[] {
|
||||||
return dedupeArtifacts([
|
return dedupeArtifacts([
|
||||||
...artifactsFromPayload(run.id, run.payload, run.finishedAt ?? run.startedAt ?? run.createdAt),
|
...artifactsFromPayload(run.id, run.payload, run.finishedAt ?? run.startedAt ?? run.createdAt, {
|
||||||
|
sourceType: 'run',
|
||||||
|
sourceLabel: artifactSourceLabel('run', run.id),
|
||||||
|
planId: run.planId,
|
||||||
|
taskId: run.taskId,
|
||||||
|
runId: run.id,
|
||||||
|
}),
|
||||||
...events
|
...events
|
||||||
.filter((event) => event.runId === run.id)
|
.filter((event) => event.runId === run.id)
|
||||||
.flatMap((event) => artifactsFromPayload(run.id, event.payload, event.occurredAt)),
|
.flatMap((event) => artifactsFromPayload(run.id, event.payload, event.occurredAt, {
|
||||||
|
sourceType: 'event',
|
||||||
|
sourceEventId: event.id,
|
||||||
|
sourceLabel: `Event ${event.kind}`,
|
||||||
|
planId: event.planId,
|
||||||
|
taskId: event.taskId,
|
||||||
|
runId: event.runId,
|
||||||
|
})),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function artifactsFromPayload(subjectId: string, payload: unknown, createdAt?: string | null): ArtifactEntry[] {
|
interface ArtifactSourceContext {
|
||||||
|
sourceType: string;
|
||||||
|
sourceLabel: string;
|
||||||
|
sourceEventId?: string | null;
|
||||||
|
planId?: string | null;
|
||||||
|
taskId?: string | null;
|
||||||
|
runId?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function artifactsFromPayload(
|
||||||
|
subjectId: string,
|
||||||
|
payload: unknown,
|
||||||
|
createdAt?: string | null,
|
||||||
|
sourceContext?: ArtifactSourceContext,
|
||||||
|
): ArtifactEntry[] {
|
||||||
const record = asRecord(payload);
|
const record = asRecord(payload);
|
||||||
if (!record) return typeof payload === 'string' ? [artifactFromValue(subjectId, payload, 0, createdAt)] : [];
|
if (!record) return typeof payload === 'string' ? [artifactFromValue(subjectId, payload, 0, createdAt, sourceContext)] : [];
|
||||||
|
|
||||||
const explicitArtifacts = [
|
const explicitArtifacts = [
|
||||||
...arrayValue(record.artifacts),
|
...arrayValue(record.artifacts),
|
||||||
@@ -1368,16 +1404,22 @@ function artifactsFromPayload(subjectId: string, payload: unknown, createdAt?: s
|
|||||||
...arrayValue(record.files),
|
...arrayValue(record.files),
|
||||||
...arrayValue(record.attachments),
|
...arrayValue(record.attachments),
|
||||||
];
|
];
|
||||||
const artifacts = explicitArtifacts.map((item, index) => artifactFromValue(subjectId, item, index, createdAt));
|
const artifacts = explicitArtifacts.map((item, index) => artifactFromValue(subjectId, item, index, createdAt, sourceContext));
|
||||||
|
|
||||||
for (const key of ['artifact', 'outputPath', 'output_path', 'filePath', 'file_path', 'path', 'uri', 'url']) {
|
for (const key of ['artifact', 'outputPath', 'output_path', 'filePath', 'file_path', 'path', 'uri', 'url']) {
|
||||||
if (record[key]) artifacts.push(artifactFromValue(subjectId, record[key], artifacts.length, createdAt));
|
if (record[key]) artifacts.push(artifactFromValue(subjectId, record[key], artifacts.length, createdAt, sourceContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
return artifacts.filter((artifact) => artifact.name || artifact.uri || artifact.value);
|
return artifacts.filter((artifact) => artifact.name || artifact.uri || artifact.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function artifactFromValue(subjectId: string, value: unknown, index: number, createdAt?: string | null): ArtifactEntry {
|
function artifactFromValue(
|
||||||
|
subjectId: string,
|
||||||
|
value: unknown,
|
||||||
|
index: number,
|
||||||
|
createdAt?: string | null,
|
||||||
|
sourceContext?: ArtifactSourceContext,
|
||||||
|
): ArtifactEntry {
|
||||||
const record = asRecord(value);
|
const record = asRecord(value);
|
||||||
if (record) {
|
if (record) {
|
||||||
const uri = stringValue(record.uri ?? record.url ?? record.path ?? record.filePath ?? record.file_path ?? record.outputPath ?? record.output_path);
|
const uri = stringValue(record.uri ?? record.url ?? record.path ?? record.filePath ?? record.file_path ?? record.outputPath ?? record.output_path);
|
||||||
@@ -1391,6 +1433,12 @@ function artifactFromValue(subjectId: string, value: unknown, index: number, cre
|
|||||||
value: stringValue(record.value ?? record.summary ?? record.text) || uri,
|
value: stringValue(record.value ?? record.summary ?? record.text) || uri,
|
||||||
created_at: (stringValue(record.created_at ?? record.createdAt) || createdAt) ?? undefined,
|
created_at: (stringValue(record.created_at ?? record.createdAt) || createdAt) ?? undefined,
|
||||||
source: 'qimingclaw-runtime',
|
source: 'qimingclaw-runtime',
|
||||||
|
source_type: stringValue(record.source_type) || sourceContext?.sourceType || 'payload',
|
||||||
|
source_event_id: stringValue(record.source_event_id) || sourceContext?.sourceEventId || null,
|
||||||
|
source_label: stringValue(record.source_label) || sourceContext?.sourceLabel || artifactSourceLabel('payload', subjectId),
|
||||||
|
plan_id: stringValue(record.plan_id) || sourceContext?.planId || null,
|
||||||
|
task_id: stringValue(record.task_id) || sourceContext?.taskId || null,
|
||||||
|
run_id: stringValue(record.run_id) || sourceContext?.runId || null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1404,9 +1452,27 @@ function artifactFromValue(subjectId: string, value: unknown, index: number, cre
|
|||||||
value: text,
|
value: text,
|
||||||
created_at: createdAt ?? undefined,
|
created_at: createdAt ?? undefined,
|
||||||
source: 'qimingclaw-runtime',
|
source: 'qimingclaw-runtime',
|
||||||
|
source_type: sourceContext?.sourceType || 'payload',
|
||||||
|
source_event_id: sourceContext?.sourceEventId || null,
|
||||||
|
source_label: sourceContext?.sourceLabel || artifactSourceLabel('payload', subjectId),
|
||||||
|
plan_id: sourceContext?.planId || null,
|
||||||
|
task_id: sourceContext?.taskId || null,
|
||||||
|
run_id: sourceContext?.runId || null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function artifactSourceLabel(sourceType: string, sourceId?: string | null): string {
|
||||||
|
const suffix = sourceId ? ` ${sourceId}` : '';
|
||||||
|
switch (sourceType) {
|
||||||
|
case 'artifact': return `Artifact${suffix}`;
|
||||||
|
case 'event': return `Event${suffix}`;
|
||||||
|
case 'run': return `Run${suffix}`;
|
||||||
|
case 'task': return `Task${suffix}`;
|
||||||
|
case 'plan': return `Plan${suffix}`;
|
||||||
|
default: return `Payload${suffix}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function dedupeArtifacts(artifacts: ArtifactEntry[]): ArtifactEntry[] {
|
function dedupeArtifacts(artifacts: ArtifactEntry[]): ArtifactEntry[] {
|
||||||
const seen = new Set<string>();
|
const seen = new Set<string>();
|
||||||
return artifacts.filter((artifact) => {
|
return artifacts.filter((artifact) => {
|
||||||
@@ -1636,6 +1702,7 @@ function buildWorkday(date: string, snapshot: QimingclawSnapshot | null): Digita
|
|||||||
const liveRecordCount = runtimePlans(snapshot).length + runtimeTasks(snapshot).length;
|
const liveRecordCount = runtimePlans(snapshot).length + runtimeTasks(snapshot).length;
|
||||||
const hasLiveRuntime = hasRuntimeRecords(snapshot);
|
const hasLiveRuntime = hasRuntimeRecords(snapshot);
|
||||||
const sync = snapshot?.sync;
|
const sync = snapshot?.sync;
|
||||||
|
const artifactSources = reportArtifactSources(buildArtifacts(snapshot));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
source: 'live',
|
source: 'live',
|
||||||
@@ -1709,6 +1776,13 @@ function buildWorkday(date: string, snapshot: QimingclawSnapshot | null): Digita
|
|||||||
],
|
],
|
||||||
artifacts: [
|
artifacts: [
|
||||||
{ name: '数字员工运行摘要', status: generatedAt ? 'ready' : 'pending' },
|
{ name: '数字员工运行摘要', status: generatedAt ? 'ready' : 'pending' },
|
||||||
|
...artifactSources.slice(0, 4).map((artifact) => ({
|
||||||
|
name: artifact.name,
|
||||||
|
status: generatedAt ? 'ready' : 'available',
|
||||||
|
uri: artifact.uri ?? null,
|
||||||
|
source_label: artifact.source_label ?? null,
|
||||||
|
source_type: artifact.source_type ?? null,
|
||||||
|
})),
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
daily_report: {
|
daily_report: {
|
||||||
@@ -1732,12 +1806,29 @@ function buildWorkday(date: string, snapshot: QimingclawSnapshot | null): Digita
|
|||||||
},
|
},
|
||||||
task_sections: executionSummaries,
|
task_sections: executionSummaries,
|
||||||
detail_lines: events.map((event) => `${event.time} ${event.task_title}:${event.detail}`),
|
detail_lines: events.map((event) => `${event.time} ${event.task_title}:${event.detail}`),
|
||||||
|
artifact_sources: artifactSources,
|
||||||
pdf_available: Boolean(generatedAt),
|
pdf_available: Boolean(generatedAt),
|
||||||
pdf_url: null,
|
pdf_url: null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function reportArtifactSources(artifacts: ArtifactEntry[]) {
|
||||||
|
return artifacts.slice(0, 20).map((artifact) => ({
|
||||||
|
id: stringValue(artifact.artifact_id) || `${stringValue(artifact.subject_id)}:${stringValue(artifact.uri ?? artifact.name)}`,
|
||||||
|
name: stringValue(artifact.name) || stringValue(artifact.uri) || stringValue(artifact.value) || '未命名产物',
|
||||||
|
kind: stringValue(artifact.kind) || null,
|
||||||
|
uri: stringValue(artifact.uri) || null,
|
||||||
|
source_label: stringValue(artifact.source_label) || null,
|
||||||
|
source_type: stringValue(artifact.source_type) || null,
|
||||||
|
source_event_id: stringValue(artifact.source_event_id) || null,
|
||||||
|
plan_id: stringValue(artifact.plan_id) || null,
|
||||||
|
task_id: stringValue(artifact.task_id) || null,
|
||||||
|
run_id: stringValue(artifact.run_id) || null,
|
||||||
|
created_at: stringValue(artifact.created_at) || null,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
async function handleWorkdayAction(body: DigitalEmployeeWorkdayActionRequest, snapshot: QimingclawSnapshot | null): Promise<DigitalEmployeeWorkdayProjection> {
|
async function handleWorkdayAction(body: DigitalEmployeeWorkdayActionRequest, snapshot: QimingclawSnapshot | null): Promise<DigitalEmployeeWorkdayProjection> {
|
||||||
const state = snapshot?.uiState ?? await readBridgeUiState() ?? readState();
|
const state = snapshot?.uiState ?? await readBridgeUiState() ?? readState();
|
||||||
const date = body.date || todayInputDate();
|
const date = body.date || todayInputDate();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
} from '@/lib/api';
|
} from '@/lib/api';
|
||||||
import type {
|
import type {
|
||||||
DigitalEmployeeDailyReport,
|
DigitalEmployeeDailyReport,
|
||||||
|
DigitalEmployeeReportArtifactSource,
|
||||||
DigitalEmployeeRunDetail,
|
DigitalEmployeeRunDetail,
|
||||||
DigitalEmployeeTaskExecutionSummary,
|
DigitalEmployeeTaskExecutionSummary,
|
||||||
DigitalEmployeeWorkdayProjection,
|
DigitalEmployeeWorkdayProjection,
|
||||||
@@ -94,6 +95,7 @@ function buildReportDownloadText(
|
|||||||
report: DigitalEmployeeDailyReport,
|
report: DigitalEmployeeDailyReport,
|
||||||
sections: DigitalEmployeeTaskExecutionSummary[],
|
sections: DigitalEmployeeTaskExecutionSummary[],
|
||||||
details: DigitalEmployeeRunDetail[],
|
details: DigitalEmployeeRunDetail[],
|
||||||
|
artifacts: DigitalEmployeeReportArtifactSource[],
|
||||||
): string {
|
): string {
|
||||||
return [
|
return [
|
||||||
report.title || `${date} 数字员工日报`,
|
report.title || `${date} 数字员工日报`,
|
||||||
@@ -121,6 +123,11 @@ function buildReportDownloadText(
|
|||||||
? details.map((detail) => `- ${detail.time || '--:--'} ${detail.task_name} / ${detail.step_name || detail.operation_name || '任务执行'}:${detail.status_label},${detail.business_detail}`)
|
? details.map((detail) => `- ${detail.time || '--:--'} ${detail.task_name} / ${detail.step_name || detail.operation_name || '任务执行'}:${detail.status_label},${detail.business_detail}`)
|
||||||
: ['暂无运行明细。']),
|
: ['暂无运行明细。']),
|
||||||
'',
|
'',
|
||||||
|
'## 产物来源',
|
||||||
|
...(artifacts.length > 0
|
||||||
|
? artifacts.map((artifact) => `- ${artifact.name}:${artifact.source_label || artifact.source_type || '未知来源'}${artifact.uri ? `,${artifact.uri}` : ''}`)
|
||||||
|
: ['暂无产物来源。']),
|
||||||
|
'',
|
||||||
'## 详情',
|
'## 详情',
|
||||||
...((report.detail_lines ?? []).length > 0 ? report.detail_lines : ['暂无详情。']),
|
...((report.detail_lines ?? []).length > 0 ? report.detail_lines : ['暂无详情。']),
|
||||||
].join('\n');
|
].join('\n');
|
||||||
@@ -173,6 +180,24 @@ function DetailList({ details }: { details: DigitalEmployeeRunDetail[] }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ArtifactSourceList({ artifacts }: { artifacts: DigitalEmployeeReportArtifactSource[] }) {
|
||||||
|
if (artifacts.length === 0) {
|
||||||
|
return <p className="de-empty-text" style={{ padding: '18px 0 6px' }}>运行产物出现后,这里会展示文件、输出和来源记录。</p>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="de-artifact-source-list" aria-label="日报产物来源">
|
||||||
|
{artifacts.map((artifact) => (
|
||||||
|
<div key={artifact.id} className="de-artifact-source-row">
|
||||||
|
<strong>{artifact.name}</strong>
|
||||||
|
<span>{artifact.source_label || artifact.source_type || '未知来源'}</span>
|
||||||
|
{artifact.uri ? <code>{artifact.uri}</code> : <em>{artifact.kind || 'artifact'}</em>}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function DailyReport({ focusSection }: { focusSection?: string | null }) {
|
export default function DailyReport({ focusSection }: { focusSection?: string | null }) {
|
||||||
const today = useMemo(() => todayInputDate(), []);
|
const today = useMemo(() => todayInputDate(), []);
|
||||||
const [projection, setProjection] = useState<DigitalEmployeeWorkdayProjection | null>(null);
|
const [projection, setProjection] = useState<DigitalEmployeeWorkdayProjection | null>(null);
|
||||||
@@ -186,6 +211,7 @@ export default function DailyReport({ focusSection }: { focusSection?: string |
|
|||||||
const report = isLiveProjection ? (projection?.daily_report ?? null) : null;
|
const report = isLiveProjection ? (projection?.daily_report ?? null) : null;
|
||||||
const taskSections = isLiveProjection ? (report?.task_sections ?? projection?.execution_summaries ?? []) : [];
|
const taskSections = isLiveProjection ? (report?.task_sections ?? projection?.execution_summaries ?? []) : [];
|
||||||
const runDetails = isLiveProjection ? (projection?.run_details ?? []) : [];
|
const runDetails = isLiveProjection ? (projection?.run_details ?? []) : [];
|
||||||
|
const artifactSources = isLiveProjection ? (report?.artifact_sources ?? []) : [];
|
||||||
const sourceNotice = isLiveProjection ? null : (projection?.demo_reason ?? '当前没有真实运行投影,日报区域暂不展示兜底数据。');
|
const sourceNotice = isLiveProjection ? null : (projection?.demo_reason ?? '当前没有真实运行投影,日报区域暂不展示兜底数据。');
|
||||||
const totals = report?.totals ?? {
|
const totals = report?.totals ?? {
|
||||||
task_count: taskSections.length,
|
task_count: taskSections.length,
|
||||||
@@ -272,8 +298,8 @@ export default function DailyReport({ focusSection }: { focusSection?: string |
|
|||||||
|
|
||||||
const downloadDailyReport = useCallback(() => {
|
const downloadDailyReport = useCallback(() => {
|
||||||
if (!report || report.status !== 'ready') return;
|
if (!report || report.status !== 'ready') return;
|
||||||
downloadTextFile(reportFileName(today), buildReportDownloadText(today, report, taskSections, runDetails));
|
downloadTextFile(reportFileName(today), buildReportDownloadText(today, report, taskSections, runDetails, artifactSources));
|
||||||
}, [report, runDetails, taskSections, today]);
|
}, [artifactSources, report, runDetails, taskSections, today]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ padding: '14px 18px' }}>
|
<div style={{ padding: '14px 18px' }}>
|
||||||
@@ -355,6 +381,10 @@ export default function DailyReport({ focusSection }: { focusSection?: string |
|
|||||||
<h3 className="de-settings-heading" style={{ marginTop: 0 }}>运行明细</h3>
|
<h3 className="de-settings-heading" style={{ marginTop: 0 }}>运行明细</h3>
|
||||||
<DetailList details={runDetails} />
|
<DetailList details={runDetails} />
|
||||||
</section>
|
</section>
|
||||||
|
<section id="report-section-artifacts" style={panelStyle}>
|
||||||
|
<h3 className="de-settings-heading" style={{ marginTop: 0 }}>产物来源</h3>
|
||||||
|
<ArtifactSourceList artifacts={artifactSources} />
|
||||||
|
</section>
|
||||||
<section id="report-section-summary" style={panelStyle}>
|
<section id="report-section-summary" style={panelStyle}>
|
||||||
<h3 className="de-settings-heading" style={{ marginTop: 0 }}>日报内容</h3>
|
<h3 className="de-settings-heading" style={{ marginTop: 0 }}>日报内容</h3>
|
||||||
{(report?.detail_lines ?? []).length === 0 ? (
|
{(report?.detail_lines ?? []).length === 0 ? (
|
||||||
|
|||||||
@@ -654,6 +654,23 @@ export interface DigitalEmployeeDecision {
|
|||||||
export interface DigitalEmployeeDeliveryArtifact {
|
export interface DigitalEmployeeDeliveryArtifact {
|
||||||
name: string;
|
name: string;
|
||||||
status: string;
|
status: string;
|
||||||
|
uri?: string | null;
|
||||||
|
source_label?: string | null;
|
||||||
|
source_type?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DigitalEmployeeReportArtifactSource {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
kind?: string | null;
|
||||||
|
uri?: string | null;
|
||||||
|
source_label?: string | null;
|
||||||
|
source_type?: string | null;
|
||||||
|
source_event_id?: string | null;
|
||||||
|
plan_id?: string | null;
|
||||||
|
task_id?: string | null;
|
||||||
|
run_id?: string | null;
|
||||||
|
created_at?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DigitalEmployeeDelivery {
|
export interface DigitalEmployeeDelivery {
|
||||||
@@ -682,6 +699,7 @@ export interface DigitalEmployeeDailyReport {
|
|||||||
};
|
};
|
||||||
task_sections: DigitalEmployeeTaskExecutionSummary[];
|
task_sections: DigitalEmployeeTaskExecutionSummary[];
|
||||||
detail_lines: string[];
|
detail_lines: string[];
|
||||||
|
artifact_sources?: DigitalEmployeeReportArtifactSource[];
|
||||||
pdf_available: boolean;
|
pdf_available: boolean;
|
||||||
pdf_url?: string | null;
|
pdf_url?: string | null;
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -15,8 +15,8 @@
|
|||||||
console.warn("[qimingclaw] digital employee auth bootstrap skipped", error);
|
console.warn("[qimingclaw] digital employee auth bootstrap skipped", error);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<script type="module" crossorigin src="./assets/index-qn2ia0Ac.js"></script>
|
<script type="module" crossorigin src="./assets/index-BZWWD4XK.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="./assets/index-DeYFPN7i.css">
|
<link rel="stylesheet" crossorigin href="./assets/index-BgRGGB2I.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -996,14 +996,23 @@ Plan status
|
|||||||
- 风险提示
|
- 风险提示
|
||||||
- 可下载报告
|
- 可下载报告
|
||||||
|
|
||||||
|
当前客户端实现:
|
||||||
|
|
||||||
|
- adapter 从 `digital_artifacts`、Run payload、Event payload 中抽取 artifact。
|
||||||
|
- artifact 会携带 `source_type`、`source_label`、`source_event_id`、`plan_id`、`task_id`、`run_id`。
|
||||||
|
- 日报数据新增 `artifact_sources`,页面展示产物名称、来源和 URI。
|
||||||
|
- 下载的本地日报文本包含“产物来源”段落。
|
||||||
|
|
||||||
### 产出
|
### 产出
|
||||||
|
|
||||||
- qimingclaw 的执行结果可沉淀、可回看、可汇报。
|
- qimingclaw 的执行结果可沉淀、可回看、可汇报。
|
||||||
|
- 日报可以追溯到具体 Run / Event / Artifact 来源。
|
||||||
|
|
||||||
### 验收
|
### 验收
|
||||||
|
|
||||||
- 完成任务后能在日报区域看到执行摘要。
|
- 完成任务后能在日报区域看到执行摘要。
|
||||||
- Artifact 可从任务详情进入查看。
|
- Artifact 可从任务详情进入查看。
|
||||||
|
- 日报区域能看到产物来源和 URI。
|
||||||
|
|
||||||
## Phase 9:ManagedService / 常驻能力治理
|
## Phase 9:ManagedService / 常驻能力治理
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user