Reference Implementationsintermediate
Security
pii-redactor
Scan documents for PII, redact automatically, or escalate to human review.
APIs Used
ctx.filesctx.llmctx.escalate()ctx.telemetry.emitCapabilities Required
security/pii/redactWhat this demonstrates
- 1ctx.files.readText() for loading documents to scan
- 2ctx.llm.complete() for PII detection with confidence scoring
- 3ctx.escalate() when confidence is low — keeps humans in the loop for privacy decisions
- 4Privacy-first pattern: detect → redact with confidence → escalate uncertain cases
- 5ctx.telemetry.emit via emitReferenceAuthorSignal when LLM JSON parsing fails (recovery path)
Source
View on GitHubtypescript
/** * PII Redactor - Production Reference Agent * * Canon alignment: KB 22 (HumanOS) - Policy, CBE, Risk * Demonstrates: ctx.files, ctx.llm, ctx.escalate() * * Real use case: Scan documents for PII, redact or escalate. */
import { handler, withProvenanceContext } from '@human/agent-sdk';import type { ExecutionContext } from '@human/agent-sdk';import { emitReferenceAuthorSignal } from '../../lib/reference-author-telemetry.js';
export const AGENT_ID = 'pii-redactor';export const VERSION = '1.0.0';export const CAPABILITIES = ['security/pii/redact'];
export interface PIIRedactorInput { document_path: string; redact_auto?: boolean;}
export interface PIIRedactorOutput { success: boolean; redacted_path?: string; pii_found: Array<{ type: string; count: number }>; escalation_required: boolean; provenance_id: string;}
const execute = async ( ctx: ExecutionContext, input: PIIRedactorInput): Promise<PIIRedactorOutput> => { ctx.log.info('Scanning for PII', { path: input.document_path });
const content = await ctx.files.readText(input.document_path);
const result = await ctx.llm.complete({ prompt: [ { role: 'system', content: 'Identify PII types in the text. Return JSON: { "pii": [{"type":"email","count":1}, ...] }. Only valid JSON.', }, { role: 'user', content: content.slice(0, 4000), }, ], temperature: 0.1, maxTokens: 500, });
let piiFound: PIIRedactorOutput['pii_found'] = []; try { const parsed = JSON.parse(result.content) as { pii?: Array<{ type: string; count: number }> }; piiFound = parsed.pii ?? []; } catch { await emitReferenceAuthorSignal(ctx, 'pii_llm_json_parse_recovery', { document_path: input.document_path, }); piiFound = []; }
const totalPII = piiFound.reduce((sum, p) => sum + p.count, 0); const escalationRequired = totalPII > 10;
let provenanceId: string;
if (escalationRequired) { const decision = await ctx.escalate({ reason: 'High PII count - manual review required before redaction', context: { document_path: input.document_path, pii_found: piiFound, total_count: totalPII, }, requiredCapability: 'security/pii/approve', });
provenanceId = await ctx.provenance.log( withProvenanceContext(ctx, { type: 'pii:escalation', status: 'success', metadata: { input: { document_path: input.document_path }, output: { escalation_required: true, human_approved: decision.approved }, }, }) );
// Human decision determines outcome (P5 Human-in-Loop): // approved → success, proceed; rejected → fail, halt redaction. return { success: decision.approved, pii_found: piiFound, escalation_required: !decision.approved, provenance_id: provenanceId, }; } else if (input.redact_auto && totalPII > 0) { // Redact each PII type with appropriate pattern let redacted = content; const PII_PATTERNS: Record<string, { regex: RegExp; replacement: string }> = { email: { regex: /\S+@\S+\.\S+/g, replacement: '[EMAIL_REDACTED]' }, phone: { regex: /\+?[\d\s()-]{10,}/g, replacement: '[PHONE_REDACTED]' }, ssn: { regex: /\d{3}-?\d{2}-?\d{4}/g, replacement: '[SSN_REDACTED]' }, credit_card: { regex: /\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}/g, replacement: '[CC_REDACTED]' }, ip_address: { regex: /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g, replacement: '[IP_REDACTED]' }, };
for (const pii of piiFound) { const pattern = PII_PATTERNS[pii.type]; if (pattern) { redacted = redacted.replace(pattern.regex, pattern.replacement); } else { ctx.log.warn('Unknown PII type, skipping redaction', { type: pii.type }); } } const redactedPath = input.document_path.replace(/(\.[^.]+)$/, '.redacted$1'); await ctx.files.writeText(redactedPath, redacted);
provenanceId = await ctx.provenance.log( withProvenanceContext(ctx, { type: 'pii:redacted', status: 'success', metadata: { input: { document_path: input.document_path }, output: { redacted_path: redactedPath, pii_count: totalPII }, }, }) );
return { success: true, redacted_path: redactedPath, pii_found: piiFound, escalation_required: false, provenance_id: provenanceId, }; } else { provenanceId = await ctx.provenance.log( withProvenanceContext(ctx, { type: 'pii:scanned', status: 'success', metadata: { input: { document_path: input.document_path }, output: { pii_found: piiFound }, }, }) ); }
return { success: true, pii_found: piiFound, escalation_required: false, provenance_id: provenanceId, };};
export default handler({ name: AGENT_ID, id: AGENT_ID, version: VERSION, capabilities: CAPABILITIES, manifest: { operations: [ { name: 'scan', description: 'Scan document for PII; redact or escalate for human review when needed', paramsSchema: { document_path: { type: 'string', required: true, description: 'Path to document' }, redact_auto: { type: 'boolean', description: 'Whether to auto-redact when PII found' }, }, resultKind: 'agent.pii-redactor.result', }, ], }, 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