吸收数字员工产物生命周期治理

This commit is contained in:
baiyanyun
2026-06-10 10:23:47 +08:00
parent 362f397e37
commit ed2f19d257
15 changed files with 739 additions and 164 deletions

View File

@@ -3,6 +3,7 @@ import type { HandlerContext } from "@shared/types/ipc";
const mockStateService = vi.hoisted(() => ({
recordManagedServiceAction: vi.fn(),
recordArtifactLifecycle: vi.fn(),
}));
const mockArtifactAccess = vi.hoisted(() => ({
@@ -66,6 +67,7 @@ vi.mock("../services/digitalEmployee/stateService", () => ({
recordDigitalEmployeeSnapshot: vi.fn(() => ({ updatedAt: null, events: [] })),
recordDigitalEmployeeGovernanceCommand: vi.fn(),
recordDigitalEmployeeDailyReport: vi.fn(),
recordDigitalEmployeeArtifactLifecycle: mockStateService.recordArtifactLifecycle,
recordDigitalEmployeeManagedServiceAction: mockStateService.recordManagedServiceAction,
readDigitalEmployeeRuntimeRecords: vi.fn(),
readDigitalEmployeeState: vi.fn(),
@@ -133,6 +135,7 @@ describe("digital employee managed service actions", () => {
beforeEach(() => {
vi.clearAllMocks();
mockStateService.recordManagedServiceAction.mockReturnValue({ eventId: "event-1" });
mockStateService.recordArtifactLifecycle.mockReturnValue({ eventId: "artifact-event-1", kind: "artifact_retention_marked", payload: {} });
mockArtifactAccess.accessDigitalEmployeeArtifact.mockResolvedValue({ ok: true, artifact_id: "artifact-1", action: "preview" });
mockServiceManager.startFileServer.mockResolvedValue({ success: true, message: "started" });
mockServiceManager.createServiceManager.mockReturnValue({
@@ -209,4 +212,20 @@ describe("digital employee managed service actions", () => {
expect(result).toMatchObject({ ok: true, artifact_id: "artifact-1", action: "preview" });
expect(mockArtifactAccess.accessDigitalEmployeeArtifact).toHaveBeenCalledWith({ artifactId: "artifact-1", action: "preview" });
});
it("registers artifact lifecycle commands through the digital employee IPC boundary", async () => {
const electron = await import("electron");
const { registerDigitalEmployeeHandlers } = await import("./digitalEmployeeHandlers");
registerDigitalEmployeeHandlers(createCtx());
const calls = vi.mocked(electron.ipcMain.handle).mock.calls;
const lifecycleCall = calls.find(([channel]) => channel === "digitalEmployee:recordArtifactLifecycle");
expect(lifecycleCall).toBeTruthy();
const handler = lifecycleCall?.[1] as (_event: unknown, input: unknown) => Promise<unknown>;
const result = await handler({}, { artifactId: "artifact-1", action: "mark_keep" });
expect(result).toMatchObject({ eventId: "artifact-event-1", kind: "artifact_retention_marked" });
expect(mockStateService.recordArtifactLifecycle).toHaveBeenCalledWith({ artifactId: "artifact-1", action: "mark_keep" });
});
});

View File

@@ -21,6 +21,7 @@ import {
recordDigitalEmployeeSnapshot,
recordDigitalEmployeeGovernanceCommand,
recordDigitalEmployeeDailyReport,
recordDigitalEmployeeArtifactLifecycle,
recordDigitalEmployeeManagedServiceAction,
readDigitalEmployeeRuntimeRecords,
readDigitalEmployeeState,
@@ -39,6 +40,7 @@ import {
type DigitalEmployeeManagedServiceActionInput,
type DigitalEmployeeGovernanceCommandRecord,
type DigitalEmployeeDailyReportRecordInput,
type DigitalEmployeeArtifactLifecycleInput,
type DigitalEmployeeMemoryUpsertInput,
type DigitalEmployeeRouteDecisionInput,
type DigitalEmployeeServiceStatus,
@@ -232,6 +234,13 @@ export function registerDigitalEmployeeHandlers(ctx: HandlerContext): void {
},
);
ipcMain.handle(
"digitalEmployee:recordArtifactLifecycle",
async (_, input: DigitalEmployeeArtifactLifecycleInput) => {
return recordDigitalEmployeeArtifactLifecycle(input ?? { action: "unknown_action" });
},
);
ipcMain.handle(
"digitalEmployee:restartManagedService",
async (_, input: RestartManagedServiceRequest) => {