feat(events): add phase3 trail report contracts

This commit is contained in:
Saravana Achu Mac 2026-04-03 19:47:54 -07:00
parent 27c4879668
commit e6b58b7187
2 changed files with 154 additions and 1 deletions

View File

@ -15,6 +15,7 @@ import {
Phase1EcosystemEventSchema,
Phase1EcosystemEventSchemas,
RoutineArtifactEnvelopeSchema,
TrailReportArtifactEnvelopeSchema,
} from './ecosystem.js';
describe('phase1 ecosystem contracts', () => {
@ -290,3 +291,111 @@ describe('phase2 ecosystem contract extensions', () => {
expect(linked.payload.relation).toBe('generated-habit');
});
});
describe('phase3 ecosystem contract extensions', () => {
it('validates canonical trail-report artifacts', () => {
const trailReport = TrailReportArtifactEnvelopeSchema.parse({
id: 'art_trail_demo',
artifactType: 'trail-report',
schemaVersion: 1,
productId: 'actiontrail',
sourceSurface: 'backend',
title: 'Cowork audit report for task task-123',
summary: '4 audited actions with 1 safety signal',
createdAt: '2026-04-03T14:00:00.000Z',
updatedAt: '2026-04-03T14:00:00.000Z',
createdBy: { actorType: 'agent', actorId: 'phase3-audit-importer' },
ownership: { userId: 'saravana', orgId: null },
visibility: {
scope: 'private',
allowedProducts: ['learning_ai_notes', 'learning_multimodal_memory_agents'],
},
status: 'recorded',
tags: ['ecosystem', 'phase3', 'audit'],
links: [],
provenance: {
originProductId: 'claw-cowork',
originActionId: 'task-123',
sessionId: 'sess_phase3',
runId: 'run_phase3_trail',
approvalId: null,
correlationId: 'corr_phase3',
lineage: [
{
stepType: 'audit-exported',
productId: 'claw-cowork',
actorType: 'system',
timestamp: '2026-04-03T13:55:00.000Z',
},
{
stepType: 'trail-report-created',
productId: 'actiontrail',
actorType: 'agent',
timestamp: '2026-04-03T14:00:00.000Z',
},
],
},
payload: {
sourceProduct: 'claw-cowork',
sourceTaskId: 'task-123',
generatedFrom: 'audit-export-json',
reportGeneratedAt: '2026-04-03T14:00:00.000Z',
actionCount: 4,
toolCallCount: 2,
approvalCount: 1,
failureCount: 0,
safetySignalCount: 1,
tasks: ['task-123'],
actionBreakdown: [
{ action: 'TaskStarted', count: 1 },
{ action: 'ToolCall', count: 2 },
{ action: 'InjectionDetected', count: 1 },
],
entries: [
{
timestamp: '2026-04-03T13:55:00.000Z',
taskId: 'task-123',
action: 'TaskStarted',
tool: null,
result: 'Success',
approval: null,
inputSummary: 'Audit seed task',
metadata: { surface: 'desktop' },
},
],
},
});
expect(trailReport.payload.sourceProduct).toBe('claw-cowork');
expect(trailReport.payload.safetySignalCount).toBe(1);
});
it('accepts generic artifact.created events for trail-report artifacts', () => {
const created = ArtifactCreatedEventSchema.parse({
eventId: 'evt_phase3_created',
eventName: 'artifact.created',
eventVersion: 1,
occurredAt: '2026-04-03T14:00:00.000Z',
productId: 'actiontrail',
sourceSurface: 'backend',
userId: 'saravana',
orgId: null,
sessionId: 'sess_phase3',
runId: 'run_phase3_trail',
artifactId: 'art_trail_demo',
actor: { actorType: 'agent', actorId: 'phase3-audit-importer' },
trace: {
correlationId: 'corr_phase3',
causationId: 'evt_cowork_export',
parentEventId: 'evt_cowork_export',
},
payload: {
artifactType: 'trail-report',
title: 'Cowork audit report for task task-123',
status: 'recorded',
},
});
expect(created.payload.artifactType).toBe('trail-report');
});
});

View File

@ -162,6 +162,37 @@ export const HabitPayloadSchema = z.object({
sourceRoutineId: z.string().min(1),
});
export const TrailReportPayloadSchema = z.object({
sourceProduct: z.literal('claw-cowork'),
sourceTaskId: z.string().min(1).nullable().optional(),
generatedFrom: z.enum(['audit-export-json', 'audit-query-json']),
reportGeneratedAt: z.string().datetime(),
actionCount: z.number().int().nonnegative(),
toolCallCount: z.number().int().nonnegative(),
approvalCount: z.number().int().nonnegative(),
failureCount: z.number().int().nonnegative(),
safetySignalCount: z.number().int().nonnegative(),
tasks: z.array(z.string().min(1)),
actionBreakdown: z.array(
z.object({
action: z.string().min(1),
count: z.number().int().positive(),
})
),
entries: z.array(
z.object({
timestamp: z.string().datetime(),
taskId: z.string().min(1).nullable(),
action: z.string().min(1),
tool: z.string().min(1).nullable().optional(),
result: z.string().min(1).nullable().optional(),
approval: z.string().min(1).nullable().optional(),
inputSummary: z.string().min(1).nullable().optional(),
metadata: z.record(z.unknown()).nullable().optional(),
})
),
});
export const TranscriptArtifactEnvelopeSchema = BaseArtifactEnvelopeSchema.extend({
artifactType: z.literal('transcript'),
payload: TranscriptPayloadSchema,
@ -192,6 +223,11 @@ export const HabitArtifactEnvelopeSchema = BaseArtifactEnvelopeSchema.extend({
payload: HabitPayloadSchema,
});
export const TrailReportArtifactEnvelopeSchema = BaseArtifactEnvelopeSchema.extend({
artifactType: z.literal('trail-report'),
payload: TrailReportPayloadSchema,
});
export const Phase1ArtifactEnvelopeSchema = z.discriminatedUnion('artifactType', [
TranscriptArtifactEnvelopeSchema,
NoteArtifactEnvelopeSchema,
@ -234,7 +270,15 @@ export const CaptureTranscriptCreatedPayloadSchema = z.object({
});
export const ArtifactCreatedPayloadSchema = z.object({
artifactType: z.enum(['transcript', 'note', 'memory', 'plan', 'routine', 'habit']),
artifactType: z.enum([
'transcript',
'note',
'memory',
'plan',
'routine',
'habit',
'trail-report',
]),
title: z.string().min(1).nullable(),
status: z.string().min(1),
});