feat(events): add phase1 ecosystem contracts

This commit is contained in:
Saravana Achu Mac 2026-04-03 16:29:30 -07:00
parent 3d59e7bbed
commit 76f1b47484
10 changed files with 623 additions and 0 deletions

View File

@ -0,0 +1,27 @@
{
"eventId": "evt_phase1_002",
"eventName": "artifact.created",
"eventVersion": 1,
"occurredAt": "2026-04-03T18:17:01.000Z",
"productId": "notelett",
"sourceSurface": "web",
"userId": "user_saravana",
"orgId": null,
"sessionId": "sess_lysnr_001",
"runId": "run_notes_001",
"artifactId": "art_note_001",
"actor": {
"actorType": "agent",
"actorId": "notes_ingest_agent"
},
"trace": {
"correlationId": "corr_phase1_001",
"causationId": "evt_phase1_001",
"parentEventId": "evt_phase1_001"
},
"payload": {
"artifactType": "note",
"title": "Standup follow-up",
"status": "draft"
}
}

View File

@ -0,0 +1,27 @@
{
"eventId": "evt_phase1_003",
"eventName": "artifact.linked",
"eventVersion": 1,
"occurredAt": "2026-04-03T18:17:02.000Z",
"productId": "notelett",
"sourceSurface": "web",
"userId": "user_saravana",
"orgId": null,
"sessionId": "sess_lysnr_001",
"runId": "run_notes_001",
"artifactId": "art_note_001",
"actor": {
"actorType": "agent",
"actorId": "notes_ingest_agent"
},
"trace": {
"correlationId": "corr_phase1_001",
"causationId": "evt_phase1_002",
"parentEventId": "evt_phase1_002"
},
"payload": {
"sourceArtifactId": "art_note_001",
"targetArtifactId": "art_transcript_001",
"relation": "summarizes"
}
}

View File

@ -0,0 +1,28 @@
{
"eventId": "evt_phase1_001",
"eventName": "capture.transcript.created",
"eventVersion": 1,
"occurredAt": "2026-04-03T18:15:01.000Z",
"productId": "lysnrai",
"sourceSurface": "mobile",
"userId": "user_saravana",
"orgId": null,
"sessionId": "sess_lysnr_001",
"runId": null,
"artifactId": "art_transcript_001",
"actor": {
"actorType": "user",
"actorId": "user_saravana"
},
"trace": {
"correlationId": "corr_phase1_001",
"causationId": null,
"parentEventId": null
},
"payload": {
"artifactId": "art_transcript_001",
"durationMs": 42150,
"language": "en",
"transcriptSource": "microphone"
}
}

View File

@ -0,0 +1,69 @@
{
"id": "art_memory_001",
"artifactType": "memory",
"schemaVersion": 1,
"productId": "mindlyst",
"sourceSurface": "service",
"title": "Saravana prefers deployment checklist reminders after standup",
"summary": "Memory candidate inferred from the standup transcript and note.",
"createdAt": "2026-04-03T18:20:00.000Z",
"updatedAt": "2026-04-03T18:20:00.000Z",
"createdBy": {
"actorType": "agent",
"actorId": "memory_ingest_agent"
},
"ownership": {
"userId": "user_saravana",
"orgId": null
},
"visibility": {
"scope": "private"
},
"status": "proposed",
"tags": ["memory", "workflow"],
"links": [
{
"relation": "derived-from",
"targetArtifactId": "art_transcript_001"
},
{
"relation": "generated-memory",
"targetArtifactId": "art_note_001"
}
],
"provenance": {
"originProductId": "lysnrai",
"originActionId": "capture_001",
"sessionId": "sess_lysnr_001",
"runId": "run_memory_001",
"approvalId": null,
"correlationId": "corr_phase1_001",
"lineage": [
{
"stepType": "captured",
"productId": "lysnrai",
"actorType": "user",
"timestamp": "2026-04-03T18:15:00.000Z"
},
{
"stepType": "note-created",
"productId": "notelett",
"actorType": "agent",
"timestamp": "2026-04-03T18:17:00.000Z"
},
{
"stepType": "memory-proposed",
"productId": "mindlyst",
"actorType": "agent",
"timestamp": "2026-04-03T18:20:00.000Z"
}
]
},
"payload": {
"memoryKind": "preference",
"text": "Saravana benefits from deployment checklist reminders immediately after standup capture.",
"confidence": 0.82,
"sourceArtifactIds": ["art_transcript_001", "art_note_001"],
"reviewState": "proposed"
}
}

View File

@ -0,0 +1,29 @@
{
"eventId": "evt_phase1_004",
"eventName": "memory.entry.created",
"eventVersion": 1,
"occurredAt": "2026-04-03T18:20:01.000Z",
"productId": "mindlyst",
"sourceSurface": "service",
"userId": "user_saravana",
"orgId": null,
"sessionId": "sess_lysnr_001",
"runId": "run_memory_001",
"artifactId": "art_memory_001",
"actor": {
"actorType": "agent",
"actorId": "memory_ingest_agent"
},
"trace": {
"correlationId": "corr_phase1_001",
"causationId": "evt_phase1_003",
"parentEventId": "evt_phase1_003"
},
"payload": {
"artifactId": "art_memory_001",
"memoryKind": "preference",
"reviewState": "proposed",
"confidence": 0.82,
"sourceArtifactIds": ["art_transcript_001", "art_note_001"]
}
}

View File

@ -0,0 +1,57 @@
{
"id": "art_note_001",
"artifactType": "note",
"schemaVersion": 1,
"productId": "notelett",
"sourceSurface": "web",
"title": "Standup follow-up",
"summary": "Structured note created from a LysnrAI transcript.",
"createdAt": "2026-04-03T18:17:00.000Z",
"updatedAt": "2026-04-03T18:17:00.000Z",
"createdBy": {
"actorType": "agent",
"actorId": "notes_ingest_agent"
},
"ownership": {
"userId": "user_saravana",
"orgId": null
},
"visibility": {
"scope": "private"
},
"status": "draft",
"tags": ["derived", "standup"],
"links": [
{
"relation": "summarizes",
"targetArtifactId": "art_transcript_001"
}
],
"provenance": {
"originProductId": "lysnrai",
"originActionId": "capture_001",
"sessionId": "sess_lysnr_001",
"runId": "run_notes_001",
"approvalId": null,
"correlationId": "corr_phase1_001",
"lineage": [
{
"stepType": "captured",
"productId": "lysnrai",
"actorType": "user",
"timestamp": "2026-04-03T18:15:00.000Z"
},
{
"stepType": "note-created",
"productId": "notelett",
"actorType": "agent",
"timestamp": "2026-04-03T18:17:00.000Z"
}
]
},
"payload": {
"noteFormat": "markdown",
"body": "# Standup follow-up\n\n- Billing sync finished\n- Clean up follow-up notes\n- Review deployment checklist",
"excerpt": "Billing sync finished; follow-up notes and deployment checklist remain."
}
}

View File

@ -0,0 +1,56 @@
{
"id": "art_transcript_001",
"artifactType": "transcript",
"schemaVersion": 1,
"productId": "lysnrai",
"sourceSurface": "mobile",
"title": "Daily standup voice capture",
"summary": "Transcript captured from Saravana's daily standup reflection.",
"createdAt": "2026-04-03T18:15:00.000Z",
"updatedAt": "2026-04-03T18:15:00.000Z",
"createdBy": {
"actorType": "user",
"actorId": "user_saravana"
},
"ownership": {
"userId": "user_saravana",
"orgId": null
},
"visibility": {
"scope": "private",
"allowedProducts": ["learning_ai_notes", "learning_multimodal_memory_agents"]
},
"status": "completed",
"tags": ["voice", "standup"],
"links": [],
"provenance": {
"originProductId": "lysnrai",
"originActionId": "capture_001",
"sessionId": "sess_lysnr_001",
"runId": null,
"approvalId": null,
"correlationId": "corr_phase1_001",
"lineage": [
{
"stepType": "captured",
"productId": "lysnrai",
"actorType": "user",
"timestamp": "2026-04-03T18:15:00.000Z"
}
]
},
"payload": {
"transcriptText": "Today I finished the billing sync, I need to clean up follow-up notes, and I should remember to review the deployment checklist.",
"transcriptSource": "microphone",
"language": "en",
"durationMs": 42150,
"segments": [
{
"speaker": null,
"startedAtMs": 0,
"endedAtMs": 42150,
"text": "Today I finished the billing sync, I need to clean up follow-up notes, and I should remember to review the deployment checklist."
}
]
}
}

View File

@ -0,0 +1,59 @@
import { describe, expect, it } from 'vitest';
import transcriptArtifact from '../fixtures/ecosystem/phase1/transcript-artifact.json' with { type: 'json' };
import noteArtifact from '../fixtures/ecosystem/phase1/note-artifact.json' with { type: 'json' };
import memoryArtifact from '../fixtures/ecosystem/phase1/memory-artifact.json' with { type: 'json' };
import captureTranscriptCreatedEvent from '../fixtures/ecosystem/phase1/capture-transcript-created.event.json' with { type: 'json' };
import artifactCreatedEvent from '../fixtures/ecosystem/phase1/artifact-created.event.json' with { type: 'json' };
import artifactLinkedEvent from '../fixtures/ecosystem/phase1/artifact-linked.event.json' with { type: 'json' };
import memoryEntryCreatedEvent from '../fixtures/ecosystem/phase1/memory-entry-created.event.json' with { type: 'json' };
import {
Phase1ArtifactEnvelopeSchema,
Phase1EcosystemEventSchema,
Phase1EcosystemEventSchemas,
} from './ecosystem.js';
describe('phase1 ecosystem contracts', () => {
it('validates canonical transcript, note, and memory artifacts', () => {
const transcript = Phase1ArtifactEnvelopeSchema.parse(transcriptArtifact);
const note = Phase1ArtifactEnvelopeSchema.parse(noteArtifact);
const memory = Phase1ArtifactEnvelopeSchema.parse(memoryArtifact);
expect(transcript.artifactType).toBe('transcript');
expect(note.links).toContainEqual({
relation: 'summarizes',
targetArtifactId: transcript.id,
});
expect(memory.links).toEqual(
expect.arrayContaining([
{
relation: 'generated-memory',
targetArtifactId: note.id,
},
])
);
});
it('validates canonical phase1 events', () => {
const events = [
captureTranscriptCreatedEvent,
artifactCreatedEvent,
artifactLinkedEvent,
memoryEntryCreatedEvent,
].map(event => Phase1EcosystemEventSchema.parse(event));
expect(events.map(event => event.eventName)).toEqual([
'capture.transcript.created',
'artifact.created',
'artifact.linked',
'memory.entry.created',
]);
});
it('exposes event-specific schemas keyed by canonical event name', () => {
const created = Phase1EcosystemEventSchemas['artifact.created'].parse(artifactCreatedEvent);
const linked = Phase1EcosystemEventSchemas['artifact.linked'].parse(artifactLinkedEvent);
expect(created.payload.artifactType).toBe('note');
expect(linked.payload.relation).toBe('summarizes');
});
});

View File

@ -0,0 +1,232 @@
import { z } from 'zod';
export const EcosystemArtifactTypeSchema = z.enum([
'transcript',
'note',
'memory',
'plan',
'routine',
'habit-checkin',
'trail-report',
'route-session',
'agent-output',
'document',
'digest',
]);
export const ArtifactLinkRelationSchema = z.enum([
'derived-from',
'summarizes',
'generated-task',
'generated-routine',
'generated-memory',
'evidence-for',
'review-of',
'attached-to',
]);
export const ArtifactLinkSchema = z.object({
relation: ArtifactLinkRelationSchema,
targetArtifactId: z.string().min(1),
});
export const ArtifactCreatedBySchema = z.object({
actorType: z.enum(['user', 'agent', 'system', 'mixed']),
actorId: z.string().min(1).nullable(),
});
export const ArtifactOwnershipSchema = z.object({
userId: z.string().min(1),
orgId: z.string().min(1).nullable().optional(),
});
export const ArtifactVisibilitySchema = z.object({
scope: z.enum(['private', 'org', 'shared', 'local-only']),
allowedProducts: z.array(z.string().min(1)).optional(),
});
export const ArtifactLineageStepSchema = z.object({
stepType: z.string().min(1),
productId: z.string().min(1),
actorType: z.enum(['user', 'agent', 'system']),
timestamp: z.string().datetime(),
});
export const ArtifactProvenanceSchema = z.object({
originProductId: z.string().min(1),
originActionId: z.string().min(1).nullable().optional(),
sessionId: z.string().min(1).nullable().optional(),
runId: z.string().min(1).nullable().optional(),
approvalId: z.string().min(1).nullable().optional(),
correlationId: z.string().min(1).nullable().optional(),
lineage: z.array(ArtifactLineageStepSchema).min(1),
});
export const BaseArtifactEnvelopeSchema = z.object({
id: z.string().min(1),
artifactType: EcosystemArtifactTypeSchema,
schemaVersion: z.literal(1),
productId: z.string().min(1),
sourceSurface: z.string().min(1),
title: z.string().min(1).nullable(),
summary: z.string().min(1).nullable(),
createdAt: z.string().datetime(),
updatedAt: z.string().datetime(),
createdBy: ArtifactCreatedBySchema,
ownership: ArtifactOwnershipSchema,
visibility: ArtifactVisibilitySchema,
status: z.string().min(1),
tags: z.array(z.string().min(1)),
links: z.array(ArtifactLinkSchema),
provenance: ArtifactProvenanceSchema,
payload: z.record(z.unknown()),
});
export const TranscriptPayloadSchema = z.object({
transcriptText: z.string().min(1),
transcriptSource: z.enum(['microphone', 'upload', 'call', 'browser', 'other']),
language: z.string().min(1),
durationMs: z.number().int().nonnegative(),
segments: z
.array(
z.object({
speaker: z.string().min(1).nullable().optional(),
startedAtMs: z.number().int().nonnegative(),
endedAtMs: z.number().int().nonnegative(),
text: z.string().min(1),
})
)
.default([]),
});
export const NotePayloadSchema = z.object({
noteFormat: z.enum(['markdown', 'plain-text', 'rich-text']),
body: z.string().min(1),
excerpt: z.string().min(1).nullable().optional(),
});
export const MemoryPayloadSchema = z.object({
memoryKind: z.enum(['fact', 'preference', 'person', 'project', 'insight', 'todo']),
text: z.string().min(1),
confidence: z.number().min(0).max(1),
sourceArtifactIds: z.array(z.string().min(1)).min(1),
reviewState: z.enum(['proposed', 'accepted', 'rejected']),
});
export const TranscriptArtifactEnvelopeSchema = BaseArtifactEnvelopeSchema.extend({
artifactType: z.literal('transcript'),
payload: TranscriptPayloadSchema,
});
export const NoteArtifactEnvelopeSchema = BaseArtifactEnvelopeSchema.extend({
artifactType: z.literal('note'),
payload: NotePayloadSchema,
});
export const MemoryArtifactEnvelopeSchema = BaseArtifactEnvelopeSchema.extend({
artifactType: z.literal('memory'),
payload: MemoryPayloadSchema,
});
export const Phase1ArtifactEnvelopeSchema = z.discriminatedUnion('artifactType', [
TranscriptArtifactEnvelopeSchema,
NoteArtifactEnvelopeSchema,
MemoryArtifactEnvelopeSchema,
]);
export const EcosystemEventActorSchema = z.object({
actorType: z.enum(['user', 'agent', 'system', 'device']),
actorId: z.string().min(1).nullable().optional(),
});
export const EcosystemEventTraceSchema = z.object({
correlationId: z.string().min(1).nullable(),
causationId: z.string().min(1).nullable(),
parentEventId: z.string().min(1).nullable(),
});
export const BaseEcosystemEventSchema = z.object({
eventId: z.string().min(1),
eventName: z.string().min(1),
eventVersion: z.literal(1),
occurredAt: z.string().datetime(),
productId: z.string().min(1),
sourceSurface: z.string().min(1),
userId: z.string().min(1).nullable().optional(),
orgId: z.string().min(1).nullable().optional(),
sessionId: z.string().min(1).nullable().optional(),
runId: z.string().min(1).nullable().optional(),
artifactId: z.string().min(1).nullable().optional(),
actor: EcosystemEventActorSchema,
trace: EcosystemEventTraceSchema,
payload: z.record(z.unknown()),
});
export const CaptureTranscriptCreatedPayloadSchema = z.object({
artifactId: z.string().min(1),
durationMs: z.number().int().nonnegative(),
language: z.string().min(1),
transcriptSource: z.enum(['microphone', 'upload', 'call', 'browser', 'other']),
});
export const ArtifactCreatedPayloadSchema = z.object({
artifactType: z.enum(['transcript', 'note', 'memory']),
title: z.string().min(1).nullable(),
status: z.string().min(1),
});
export const ArtifactLinkedPayloadSchema = z.object({
sourceArtifactId: z.string().min(1),
targetArtifactId: z.string().min(1),
relation: z.enum(['summarizes', 'generated-memory']),
});
export const MemoryEntryCreatedPayloadSchema = z.object({
artifactId: z.string().min(1),
memoryKind: MemoryPayloadSchema.shape.memoryKind,
reviewState: MemoryPayloadSchema.shape.reviewState,
confidence: MemoryPayloadSchema.shape.confidence,
sourceArtifactIds: z.array(z.string().min(1)).min(1),
});
export const CaptureTranscriptCreatedEventSchema = BaseEcosystemEventSchema.extend({
eventName: z.literal('capture.transcript.created'),
payload: CaptureTranscriptCreatedPayloadSchema,
});
export const ArtifactCreatedEventSchema = BaseEcosystemEventSchema.extend({
eventName: z.literal('artifact.created'),
payload: ArtifactCreatedPayloadSchema,
});
export const ArtifactLinkedEventSchema = BaseEcosystemEventSchema.extend({
eventName: z.literal('artifact.linked'),
payload: ArtifactLinkedPayloadSchema,
});
export const MemoryEntryCreatedEventSchema = BaseEcosystemEventSchema.extend({
eventName: z.literal('memory.entry.created'),
payload: MemoryEntryCreatedPayloadSchema,
});
export const Phase1EcosystemEventSchema = z.discriminatedUnion('eventName', [
CaptureTranscriptCreatedEventSchema,
ArtifactCreatedEventSchema,
ArtifactLinkedEventSchema,
MemoryEntryCreatedEventSchema,
]);
export const Phase1EcosystemEventSchemas = {
'capture.transcript.created': CaptureTranscriptCreatedEventSchema,
'artifact.created': ArtifactCreatedEventSchema,
'artifact.linked': ArtifactLinkedEventSchema,
'memory.entry.created': MemoryEntryCreatedEventSchema,
} as const;
export type ArtifactEnvelope = z.infer<typeof BaseArtifactEnvelopeSchema>;
export type TranscriptArtifactEnvelope = z.infer<typeof TranscriptArtifactEnvelopeSchema>;
export type NoteArtifactEnvelope = z.infer<typeof NoteArtifactEnvelopeSchema>;
export type MemoryArtifactEnvelope = z.infer<typeof MemoryArtifactEnvelopeSchema>;
export type Phase1ArtifactEnvelope = z.infer<typeof Phase1ArtifactEnvelopeSchema>;
export type EcosystemEvent = z.infer<typeof BaseEcosystemEventSchema>;
export type Phase1EcosystemEvent = z.infer<typeof Phase1EcosystemEventSchema>;

View File

@ -3,6 +3,37 @@ export type { EmitResult, EmitError } from './memory.js';
export { DurableEventBus } from './durable.js';
export type { DurableEventBusOptions } from './durable.js';
export { PlatformEventSchemas } from './types.js';
export {
ArtifactCreatedBySchema,
ArtifactLineageStepSchema,
ArtifactLinkRelationSchema,
ArtifactLinkSchema,
ArtifactOwnershipSchema,
ArtifactProvenanceSchema,
ArtifactVisibilitySchema,
BaseArtifactEnvelopeSchema,
BaseEcosystemEventSchema,
CaptureTranscriptCreatedEventSchema,
CaptureTranscriptCreatedPayloadSchema,
EcosystemArtifactTypeSchema,
EcosystemEventActorSchema,
EcosystemEventTraceSchema,
MemoryArtifactEnvelopeSchema,
MemoryEntryCreatedEventSchema,
MemoryEntryCreatedPayloadSchema,
MemoryPayloadSchema,
NoteArtifactEnvelopeSchema,
NotePayloadSchema,
Phase1ArtifactEnvelopeSchema,
Phase1EcosystemEventSchema,
Phase1EcosystemEventSchemas,
TranscriptArtifactEnvelopeSchema,
TranscriptPayloadSchema,
ArtifactCreatedEventSchema,
ArtifactCreatedPayloadSchema,
ArtifactLinkedEventSchema,
ArtifactLinkedPayloadSchema,
} from './ecosystem.js';
export type {
PlatformEventName,
PlatformEventPayload,
@ -10,3 +41,11 @@ export type {
EventHandler,
EventSubscription,
} from './types.js';
export type {
EcosystemEvent,
MemoryArtifactEnvelope,
NoteArtifactEnvelope,
Phase1ArtifactEnvelope,
Phase1EcosystemEvent,
TranscriptArtifactEnvelope,
} from './ecosystem.js';