learning_ai_notes/backend/src/lib/ecosystem-phase3.ts

354 lines
10 KiB
TypeScript

import { randomUUID } from 'node:crypto';
import { mkdir, readFile, writeFile } from 'node:fs/promises';
import { homedir } from 'node:os';
import { dirname, join } from 'node:path';
import type { NoteArtifactDoc } from '../modules/note-artifacts/types.js';
import type { NoteDoc } from '../modules/notes/types.js';
const DEFAULT_PHASE3_ROOT = join(homedir(), '.bytelyst', 'ecosystem', 'phase3');
type TrailReportArtifact = {
id: string;
title: string;
summary: string;
ownership: {
userId: string;
orgId?: string | null;
};
provenance: {
sessionId?: string | null;
correlationId?: string | null;
lineage: Array<{
stepType: string;
productId: string;
actorType: 'user' | 'agent' | 'system';
timestamp: string;
}>;
};
payload: {
sourceProduct: 'claw-cowork';
sourceTaskId?: string | null;
reportGeneratedAt: string;
actionCount: number;
toolCallCount: number;
approvalCount: number;
failureCount: number;
safetySignalCount: number;
actionBreakdown: Array<{
action: string;
count: number;
}>;
entries: Array<{
timestamp: string;
taskId: string | null;
action: string;
tool?: string | null;
result?: string | null;
approval?: string | null;
inputSummary?: string | null;
}>;
};
};
type TrailReportCreatedEvent = {
eventId: string;
eventName: 'artifact.created';
trace: {
correlationId: string | null;
causationId: string | null;
parentEventId: string | null;
};
};
type NoteArtifactEnvelope = {
id: string;
artifactType: 'note';
schemaVersion: 1;
productId: 'notelett';
sourceSurface: 'backend';
title: string;
summary: string;
createdAt: string;
updatedAt: string;
createdBy: {
actorType: 'agent';
actorId: string;
};
ownership: {
userId: string;
orgId?: string | null;
};
visibility: {
scope: 'private';
allowedProducts: ['learning_multimodal_memory_agents'];
};
status: 'draft';
tags: string[];
links: Array<{
relation: 'summarizes';
targetArtifactId: string;
}>;
provenance: TrailReportArtifact['provenance'] & {
runId: string;
};
payload: {
noteFormat: 'markdown';
body: string;
excerpt: string;
};
};
type EcosystemEvent = {
eventId: string;
eventName: 'artifact.created' | 'artifact.linked';
eventVersion: 1;
occurredAt: string;
productId: 'notelett';
sourceSurface: 'backend';
userId: string;
orgId?: string | null;
sessionId?: string | null;
runId: string;
artifactId: string;
actor: {
actorType: 'agent';
actorId: string;
};
trace: {
correlationId: string | null;
causationId: string | null;
parentEventId: string | null;
};
payload: Record<string, unknown>;
};
export function getPhase3Root(): string {
return process.env.BYTELYST_ECOSYSTEM_DIR ?? DEFAULT_PHASE3_ROOT;
}
export async function loadLatestTrailReportArtifact(root = getPhase3Root()): Promise<TrailReportArtifact> {
const raw = await readFile(join(root, 'indexes', 'latest-trail-report.json'), 'utf-8');
return JSON.parse(raw) as TrailReportArtifact;
}
export async function loadLatestTrailReportCreatedEvent(
root = getPhase3Root()
): Promise<TrailReportCreatedEvent | null> {
try {
const raw = await readFile(join(root, 'indexes', 'latest-trail-report-created-event.json'), 'utf-8');
return JSON.parse(raw) as TrailReportCreatedEvent;
} catch (error) {
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
return null;
}
throw error;
}
}
export function buildPhase3TrailNoteImport(params: {
trailReportArtifact: TrailReportArtifact;
trailReportCreatedEvent?: TrailReportCreatedEvent | null;
workspaceId: string;
userId: string;
root?: string;
now?: string;
}) {
const now = params.now ?? new Date().toISOString();
const noteId = `note_phase3_${randomUUID().replace(/-/g, '').slice(0, 12)}`;
const noteArtifactId = `art_note_${randomUUID().replace(/-/g, '').slice(0, 12)}`;
const runId = `run_note_${randomUUID().replace(/-/g, '').slice(0, 10)}`;
const title = params.trailReportArtifact.title || 'Imported audit note';
const breakdown = params.trailReportArtifact.payload.actionBreakdown
.slice(0, 5)
.map(item => `- ${item.action}: ${item.count}`)
.join('\n');
const evidence = params.trailReportArtifact.payload.entries
.slice(0, 3)
.map(
entry =>
`- ${entry.timestamp} | ${entry.action}${entry.tool ? ` (${entry.tool})` : ''}${entry.inputSummary ? `${entry.inputSummary}` : ''}`
)
.join('\n');
const body = [
`# ${title}`,
'',
params.trailReportArtifact.summary,
'',
`Source product: ${params.trailReportArtifact.payload.sourceProduct}`,
`Source task: ${params.trailReportArtifact.payload.sourceTaskId ?? 'n/a'}`,
`Action count: ${params.trailReportArtifact.payload.actionCount}`,
`Safety signals: ${params.trailReportArtifact.payload.safetySignalCount}`,
'',
'## Action Breakdown',
breakdown || '- none',
'',
'## Evidence',
evidence || '- none',
].join('\n');
const excerpt = `${params.trailReportArtifact.payload.actionCount} audited actions imported from Cowork`;
const note: NoteDoc = {
id: noteId,
productId: 'notelett',
workspaceId: params.workspaceId,
userId: params.userId,
title,
body,
status: 'draft',
tags: ['ecosystem', 'phase3', 'audit'],
links: [params.trailReportArtifact.id],
sourceType: 'ecosystem-trail-report',
sourceUri: params.trailReportArtifact.id,
createdAt: now,
updatedAt: now,
createdBy: params.userId,
updatedBy: params.userId,
agentId: 'phase3-trail-importer',
};
const noteArtifactDoc: NoteArtifactDoc = {
id: `na_${randomUUID().replace(/-/g, '').slice(0, 12)}`,
productId: 'notelett',
workspaceId: params.workspaceId,
userId: params.userId,
noteId,
artifactType: 'summary',
title: 'Imported trail report artifact',
description: `Audit trail source ${params.trailReportArtifact.id}`,
blobPath: join(params.root ?? getPhase3Root(), 'artifacts', 'note', `${noteArtifactId}.json`),
contentType: 'application/json',
createdAt: now,
createdBy: params.userId,
updatedAt: now,
updatedBy: params.userId,
};
const ecosystemNoteArtifact: NoteArtifactEnvelope = {
id: noteArtifactId,
artifactType: 'note',
schemaVersion: 1,
productId: 'notelett',
sourceSurface: 'backend',
title,
summary: excerpt,
createdAt: now,
updatedAt: now,
createdBy: {
actorType: 'agent',
actorId: 'phase3-trail-importer',
},
ownership: {
userId: params.userId,
orgId: params.trailReportArtifact.ownership.orgId ?? null,
},
visibility: {
scope: 'private',
allowedProducts: ['learning_multimodal_memory_agents'],
},
status: 'draft',
tags: ['ecosystem', 'phase3', 'audit'],
links: [
{
relation: 'summarizes',
targetArtifactId: params.trailReportArtifact.id,
},
],
provenance: {
...params.trailReportArtifact.provenance,
runId,
lineage: [
...params.trailReportArtifact.provenance.lineage,
{
stepType: 'audit-note-created',
productId: 'notelett',
actorType: 'agent',
timestamp: now,
},
],
},
payload: {
noteFormat: 'markdown',
body,
excerpt,
},
};
const createdEvent: EcosystemEvent = {
eventId: `evt_note_created_${randomUUID().replace(/-/g, '').slice(0, 12)}`,
eventName: 'artifact.created',
eventVersion: 1,
occurredAt: now,
productId: 'notelett',
sourceSurface: 'backend',
userId: params.userId,
orgId: params.trailReportArtifact.ownership.orgId ?? null,
sessionId: params.trailReportArtifact.provenance.sessionId ?? null,
runId,
artifactId: ecosystemNoteArtifact.id,
actor: {
actorType: 'agent',
actorId: 'phase3-trail-importer',
},
trace: {
correlationId: params.trailReportArtifact.provenance.correlationId ?? null,
causationId: params.trailReportCreatedEvent?.eventId ?? null,
parentEventId: params.trailReportCreatedEvent?.eventId ?? null,
},
payload: {
artifactType: 'note',
title,
status: 'draft',
},
};
const linkedEvent: EcosystemEvent = {
eventId: `evt_note_linked_${randomUUID().replace(/-/g, '').slice(0, 12)}`,
eventName: 'artifact.linked',
eventVersion: 1,
occurredAt: now,
productId: 'notelett',
sourceSurface: 'backend',
userId: params.userId,
orgId: params.trailReportArtifact.ownership.orgId ?? null,
sessionId: params.trailReportArtifact.provenance.sessionId ?? null,
runId,
artifactId: ecosystemNoteArtifact.id,
actor: {
actorType: 'agent',
actorId: 'phase3-trail-importer',
},
trace: {
correlationId: params.trailReportArtifact.provenance.correlationId ?? null,
causationId: createdEvent.eventId,
parentEventId: createdEvent.eventId,
},
payload: {
sourceArtifactId: ecosystemNoteArtifact.id,
targetArtifactId: params.trailReportArtifact.id,
relation: 'summarizes',
},
};
return { note, noteArtifactDoc, ecosystemNoteArtifact, createdEvent, linkedEvent };
}
export async function persistPhase3NoteOutputs(params: {
ecosystemNoteArtifact: NoteArtifactEnvelope;
createdEvent: EcosystemEvent;
linkedEvent: EcosystemEvent;
root?: string;
}) {
const root = params.root ?? getPhase3Root();
await writeJson(join(root, 'artifacts', 'note', `${params.ecosystemNoteArtifact.id}.json`), params.ecosystemNoteArtifact);
await writeJson(join(root, 'events', 'artifact.created', `${params.createdEvent.eventId}.json`), params.createdEvent);
await writeJson(join(root, 'events', 'artifact.linked', `${params.linkedEvent.eventId}.json`), params.linkedEvent);
await writeJson(join(root, 'indexes', 'latest-note.json'), params.ecosystemNoteArtifact);
await writeJson(join(root, 'indexes', 'latest-note-created-event.json'), params.createdEvent);
await writeJson(join(root, 'indexes', 'latest-note-linked-event.json'), params.linkedEvent);
}
async function writeJson(path: string, payload: unknown) {
await mkdir(dirname(path), { recursive: true });
await writeFile(path, `${JSON.stringify(payload, null, 2)}\n`, 'utf-8');
}