Reference Implementationsintermediate
Operations
meeting-notes-agent
Event-triggered extraction of meeting notes from transcripts. Uses prompt registry for all LLM calls, execution memory for transcript buffer, and Fourth Law escalation.
APIs Used
ctx.promptsctx.resourcesctx.memory.executionctx.llmctx.escalate()ctx.provenancectx.telemetry.emitCapabilities Required
meetings/notes/extractWhat this demonstrates
- 1ctx.prompts.load() for extract and owner-resolution — never inline LLM strings
- 2ctx.resources.load() for core.meeting.transcript, ctx.resources.save() for core.meeting.notes
- 3ctx.memory.execution for transcript buffer and re-read during owner resolution
- 4Fourth Law: ctx.escalate() when confidence < 0.65 or transcript too short
- 5ctx.provenance.log() with cost for cost benchmarking pipeline
- 6ctx.telemetry.emit via emitReferenceAuthorSignal when no transcript is available (skip path)
Source
View on GitHubtypescript
/** * Meeting Notes Agent — Reference Implementation * * Canon: kb/105_agent_sdk_architecture.md, kb/130_agent_design_patterns.md * Principles demonstrated: P1, P5, P7, P10 (Human-in-Loop, Fourth Law) * * SDK surfaces demonstrated: * ctx.prompts.load() — extract + owner-resolution prompts (no inline LLM strings) * ctx.resources.load() — loading core.meeting.transcript as primary input * ctx.resources.save() — writing core.meeting.notes with ledger anchoring * ctx.llm.complete() — factual extraction at temperature 0.2 * ctx.escalate() — Fourth Law escalation when confidence < 0.65 or transcript too short * ctx.provenance.log() — cryptographic audit trail with cost: result.cost * ctx.memory.execution — transcript buffer and re-read during owner resolution * * Event triggers: * meeting.transcript.created — dispatched when a transcript resource is created * * This agent is a template. Copy it. Change the kinds and LLM prompt. * Everything else — the memory pattern, the HITL gate, the provenance call — * follows the same structure for any event-triggered processing agent. */
import { handler, withProvenanceContext } from '@human/agent-sdk';import type { ExecutionContext } from '@human/agent-sdk';import { emitReferenceAuthorSignal } from '../../lib/reference-author-telemetry.js';
const AGENT_ID = 'meeting-notes-agent';const VERSION = '1.0.0';
/** Transcript resource shape (metadata from core.meeting.transcript) */interface TranscriptMetadata { content?: string; duration_seconds?: number; participants?: Array<{ name?: string; did?: string }>; source?: string;}
/** Extraction result from the extract prompt (JSON output) */interface ExtractionResult { summary: string; key_decisions: string[]; action_items: Array< { description: string; owner_name?: string }>; follow_up_questions: string[]; confidence: number;}
const execute = async (ctx: ExecutionContext, _input: unknown): Promise<{ saved_id: string; escalated: boolean }> => { // 1. Load transcript from Resource Graph const { data: transcripts } = await ctx.resources.load('core.meeting.transcript', { limit: 1 }); if (!transcripts.length) { await emitReferenceAuthorSignal(ctx, 'meeting_notes_skipped_no_transcript', {}); await ctx.provenance.log( withProvenanceContext(ctx, { action: 'meeting.notes.skipped', status: 'success', input: { reason: 'no_transcript' }, }) ); return { saved_id: '', escalated: false }; }
const transcriptRecord = transcripts[0]!; const meta = (transcriptRecord.metadata ?? {}) as TranscriptMetadata; const content = meta.content ?? ''; const durationSeconds = meta.duration_seconds ?? 0; const participants = meta.participants ?? []; const wordCount = content.split(/\s+/).filter(Boolean).length;
// 2. Buffer to execution memory (correct pattern: no module-level or local variable for intermediate state) await ctx.memory.execution.set('transcript', JSON.stringify({ content, durationSeconds, participants, wordCount }));
// 3. Minimum viability — escalate if transcript too short if (durationSeconds < 120 || wordCount < 50) { await ctx.escalate({ reason: 'transcript_too_short', context: { duration_seconds: durationSeconds, word_count: wordCount }, requiredCapability: 'meetings/notes/review', }); await ctx.provenance.log( withProvenanceContext(ctx, { action: 'meeting.notes.escalated', status: 'success', input: { reason: 'transcript_too_short' }, }) ); return { saved_id: '', escalated: true }; }
// 4. LLM extraction via ctx.prompts (never inline template strings) const extractPrompt = await ctx.prompts.load('core/agents/meeting-notes/extract'); const participantsStr = participants.map((p) => `${p.name ?? 'Unknown'}: ${p.did ?? '—'}`).join('\n'); const renderedExtract = extractPrompt.render({ transcript: content, participants: participantsStr, }); const extractResult = await ctx.llm.complete({ prompt: renderedExtract, promptMetadata: extractPrompt.toCallMetadata(), temperature: 0.2, maxTokens: 2000, });
let extraction: ExtractionResult; try { const raw = extractResult.content.replace(/^[\s\S]*?(\{[\s\S]*\})[\s\S]*$/m, '$1'); extraction = JSON.parse(raw) as ExtractionResult; } catch { await ctx.provenance.log( withProvenanceContext(ctx, { action: 'meeting.notes.extraction_failed', status: 'error', cost: extractResult.cost, }) ); throw new Error('Meeting notes extraction returned invalid JSON'); }
// 5. Fourth Law (P10): low confidence → human review let escalated = false; if (extraction.confidence < 0.65) { await ctx.escalate({ reason: 'low_confidence_extraction', context: { confidence: extraction.confidence, draft_summary: extraction.summary, draft_action_items: extraction.action_items, }, requiredCapability: 'meetings/notes/review', }); escalated = true; }
// 6. Owner resolution — re-read from execution memory (not re-load resource) const bufferedRaw = await ctx.memory.execution.get('transcript'); const buffered = bufferedRaw ? (JSON.parse(bufferedRaw) as { content: string; participants: TranscriptMetadata['participants'] }) : null; const actionItemsWithOwners = extraction.action_items.map((item) => { const ownerName = item.owner_name ?? ''; const participant = (buffered?.participants ?? participants).find( (p) => p.name && ownerName && p.name.toLowerCase().includes(ownerName.toLowerCase()) ); return { description: item.description, owner_name: ownerName, owner_did: participant?.did ?? null, }; });
// 7. Save notes to Resource Graph const notesPayload = { summary: extraction.summary, key_decisions: extraction.key_decisions, action_items: actionItemsWithOwners, follow_up_questions: extraction.follow_up_questions, confidence: extraction.confidence, source_transcript_uri: transcriptRecord.uri, };
const saveResponse = await ctx.resources.save('core.meeting.notes', { data: notesPayload }); const provenanceRef = saveResponse.provenance_ref ?? undefined;
// 8. Provenance log (with cost for marketplace cost_profile pipeline) await ctx.provenance.log( withProvenanceContext(ctx, { action: 'meeting.notes.generated', status: 'success', output: { resource_id: saveResponse.resource_id, confidence: extraction.confidence, escalated, }, cost: extractResult.cost, }) );
return { saved_id: saveResponse.resource_id, escalated, };};
export default handler({ name: AGENT_ID, id: AGENT_ID, version: VERSION, capabilities: ['meetings/notes/generate'], manifest: { operations: [ { name: 'generate', description: 'Generate meeting notes from a transcript; escalates on short transcript or low confidence', paramsSchema: {}, resultKind: 'core.meeting.notes', }, ], resources: { consumes: [{ kind: 'core.meeting.transcript' }], produces: [{ kind: 'core.meeting.notes' }], }, on: [ { event: 'meeting.transcript.created', filter: { source: 'google-calendar' } }, { event: 'meeting.transcript.created', filter: { source: 'zoom' } }, ], }, execute,});Run the tests
From monorepo root
$ pnpm test:agents:reference
$ pnpm test:agents:reference:verbose
The reference suite runs all 23 agents with createMockExecutionContext(), verifying every ctx.* API call and output shape.
See Also
SDK Reference
Patterns