feat(client): persist digital governance events

This commit is contained in:
baiyanyun
2026-06-07 18:59:59 +08:00
parent 5e76828afe
commit e37024bdbe
14 changed files with 925 additions and 158 deletions

View File

@@ -66,6 +66,7 @@ export interface DigitalEmployeeSyncFailure {
id: string;
entityType: string;
entityId: string;
entitySummary: DigitalEmployeeSyncEntitySummary | null;
operation: string;
attempts: number;
lastAttemptAt: string | null;
@@ -78,6 +79,12 @@ export interface DigitalEmployeeSyncFailure {
updatedAt: string;
}
export interface DigitalEmployeeSyncEntitySummary {
title: string | null;
status: string | null;
summary: string | null;
}
interface SyncAck {
outbox_id?: string;
id?: string;
@@ -580,6 +587,7 @@ function readRecentFailures(
id: row.id,
entityType: row.entity_type,
entityId: row.entity_id,
entitySummary: readEntitySummary(row.entity_type, row.entity_id),
operation: row.operation,
attempts: row.attempts,
lastAttemptAt: row.last_attempt_at,
@@ -594,6 +602,58 @@ function readRecentFailures(
});
}
function readEntitySummary(
entityType: string,
entityId: string,
): DigitalEmployeeSyncEntitySummary | null {
const table = entityTable(entityType);
const db = getDigitalEmployeeDb();
if (!db || !table) return null;
const select = entitySummarySelect(entityType);
if (!select) return null;
const row = db.prepare(select).get(entityId) as Record<string, unknown> | undefined;
if (!row) return null;
const payload = parsePayload(String(row.payload ?? "{}"));
const payloadSummary = payload && typeof payload === "object" && !Array.isArray(payload)
? payload as Record<string, unknown>
: {};
return {
title: stringField(row.title) || stringField(payloadSummary.title) || null,
status: stringField(row.status) || stringField(payloadSummary.status) || null,
summary:
stringField(row.summary) ||
stringField(row.objective) ||
stringField(row.message) ||
stringField(payloadSummary.objective) ||
stringField(payloadSummary.message) ||
stringField(payloadSummary.summary) ||
null,
};
}
function entitySummarySelect(entityType: string): string | null {
switch (entityType) {
case "plan":
return "SELECT title, status, objective, payload FROM digital_plans WHERE id = ?";
case "task":
return "SELECT title, status, NULL AS objective, payload FROM digital_tasks WHERE id = ?";
case "run":
return "SELECT id AS title, status, NULL AS objective, payload FROM digital_runs WHERE id = ?";
case "event":
return "SELECT kind AS title, kind AS status, message, payload FROM digital_events WHERE id = ?";
case "artifact":
return "SELECT name AS title, kind AS status, uri AS summary, payload FROM digital_artifacts WHERE id = ?";
case "approval":
return "SELECT title, status, NULL AS objective, payload FROM digital_approvals WHERE id = ?";
default:
return null;
}
}
function stringField(value: unknown): string {
return typeof value === "string" && value.trim() ? value.trim() : "";
}
function readAttemptHistory(
outboxId: string,
limit = 5,