diff --git a/next-app/app/api/chat/stream/route.ts b/next-app/app/api/chat/stream/route.ts index a983cd7..1e7bf8b 100644 --- a/next-app/app/api/chat/stream/route.ts +++ b/next-app/app/api/chat/stream/route.ts @@ -8,13 +8,18 @@ function* fakeStream(text: string) { } } +import { preFilter, steerPrompt, postModerate } from '@/lib/safety/pipeline'; + function streamForMessage(message: string) { const ctrl = new AbortController(); const stream = new ReadableStream({ async start(controller) { try { - const reply = `Echo: ${message}`; - const meta = { layer: 'surface', model: 'mock', version: '0.0.1', latencyMs: 42 }; + const pre = preFilter(message); + const safePrompt = steerPrompt(message); + const reply = `Echo: ${safePrompt}`; + const post = postModerate(reply); + const meta = { layer: 'surface', model: 'mock', version: '0.0.1', latencyMs: 42, pre, post }; controller.enqueue(encode(`event: meta\ndata: ${JSON.stringify(meta)}\n\n`)); for (const chunk of fakeStream(reply)) { await new Promise(r => setTimeout(r, 10)); diff --git a/next-app/app/api/consent/route.ts b/next-app/app/api/consent/route.ts new file mode 100644 index 0000000..704ba53 --- /dev/null +++ b/next-app/app/api/consent/route.ts @@ -0,0 +1,18 @@ +import { NextRequest } from 'next/server'; +import { appendConsentEvent, exportConsent } from '@/lib/privacy/consentLedger'; + +export const runtime = 'nodejs'; + +export async function POST(req: NextRequest) { + const { userId = 'demo', sessionId, action } = await req.json(); + if (!['persist_on','persist_off','export'].includes(action)) return new Response('bad action', { status: 400 }); + const ev = await appendConsentEvent({ userId, sessionId, action, ts: new Date().toISOString() as any }); + return Response.json(ev); +} + +export async function GET(req: NextRequest) { + const { searchParams } = new URL(req.url); + const userId = searchParams.get('userId') ?? 'demo'; + const data = await exportConsent(userId); + return Response.json(data); +} diff --git a/next-app/app/api/risk/scores/route.ts b/next-app/app/api/risk/scores/route.ts new file mode 100644 index 0000000..e160a18 --- /dev/null +++ b/next-app/app/api/risk/scores/route.ts @@ -0,0 +1,11 @@ +export const runtime = 'nodejs'; +export async function GET() { + // Mock time-series risk per layer: core/operational/context + const now = Date.now(); + const series = ['core','operational','context'].map((k, i) => ({ + key: k, + points: Array.from({ length: 12 }, (_, j) => ({ t: now - (11 - j) * 3600_000, v: clamp(0, 100, 30 + i*20 + Math.sin(j/2+i)*15 + Math.random()*10) })) + })); + return Response.json({ series }); +} +function clamp(min:number,max:number,v:number){return Math.max(min,Math.min(max,v));} diff --git a/next-app/app/chat/page.tsx b/next-app/app/chat/page.tsx index 4c47988..9427427 100644 --- a/next-app/app/chat/page.tsx +++ b/next-app/app/chat/page.tsx @@ -49,7 +49,7 @@ export default function ChatPage() { return (
-

Chat

+

Chat (ephemeral by default)

{messages.map((m, i) => ( @@ -63,10 +63,11 @@ export default function ChatPage() {
))}
-
+
setInput(e.target.value)} className="flex-1 rounded border px-3 py-2" placeholder="Type a message..." /> {fallback && Fallback in use} + Export consent ledger
diff --git a/next-app/app/docs/governance-terms-mapping/page.tsx b/next-app/app/docs/governance-terms-mapping/page.tsx new file mode 100644 index 0000000..979cdbb --- /dev/null +++ b/next-app/app/docs/governance-terms-mapping/page.tsx @@ -0,0 +1,7 @@ +import { readFileSync } from 'fs'; +import path from 'path'; +export const dynamic = 'force-static'; +export default function Page() { + const md = readFileSync(path.join(process.cwd(), 'next-app', 'docs', 'governance-terms-mapping.md'), 'utf8'); + return
{md}
; +} diff --git a/next-app/app/docs/readiness-checklist/page.tsx b/next-app/app/docs/readiness-checklist/page.tsx new file mode 100644 index 0000000..b3141e6 --- /dev/null +++ b/next-app/app/docs/readiness-checklist/page.tsx @@ -0,0 +1,7 @@ +import { readFileSync } from 'fs'; +import path from 'path'; +export const dynamic = 'force-static'; +export default function Page() { + const md = readFileSync(path.join(process.cwd(), 'next-app', 'docs', 'readiness-checklist.md'), 'utf8'); + return
{md}
; +} diff --git a/next-app/app/docs/roadmap/page.tsx b/next-app/app/docs/roadmap/page.tsx new file mode 100644 index 0000000..0847df7 --- /dev/null +++ b/next-app/app/docs/roadmap/page.tsx @@ -0,0 +1,7 @@ +import { readFileSync } from 'fs'; +import path from 'path'; +export const dynamic = 'force-static'; +export default function Page() { + const md = readFileSync(path.join(process.cwd(), 'next-app', 'docs', 'roadmap.md'), 'utf8'); + return
{md}
; +} diff --git a/next-app/app/docs/strategy-map/page.tsx b/next-app/app/docs/strategy-map/page.tsx new file mode 100644 index 0000000..7862687 --- /dev/null +++ b/next-app/app/docs/strategy-map/page.tsx @@ -0,0 +1,7 @@ +import { readFileSync } from 'fs'; +import path from 'path'; +export const dynamic = 'force-static'; +export default function Page() { + const md = readFileSync(path.join(process.cwd(), 'next-app', 'docs', 'strategy-map.md'), 'utf8'); + return
{md}
; +} diff --git a/next-app/app/governance/maturity/page.tsx b/next-app/app/governance/maturity/page.tsx new file mode 100644 index 0000000..6dfb080 --- /dev/null +++ b/next-app/app/governance/maturity/page.tsx @@ -0,0 +1,103 @@ +import { readFileSync } from 'fs'; +import path from 'path'; + +export const metadata = { title: 'Governance Capability Matrix' } as const; +export const dynamic = 'force-static'; + +type Dimension = { + id: string; + name: string; + phase: string; + score: number; // 0-5 + evidence: string[]; + gaps: string[]; + remediation: string[]; + links?: Record; +}; + +type Maturity = { dimensions: Dimension[] }; + +function gateText(score: number) { + if (score < 2) return { label: 'Do not advance', color: '#dc2626', note: 'Address gaps before proceeding' }; + if (score < 4) return { label: 'Proceed with guardrails', color: '#f59e0b', note: 'Monitor and document mitigations' }; + return { label: 'Clear to advance', color: '#16a34a', note: 'Maintain controls and evidence' }; +} + +function scoreColor(score: number) { + if (score <= 1) return '#b91c1c'; + if (score === 2) return '#e11d48'; + if (score === 3) return '#f59e0b'; + if (score === 4) return '#10b981'; + return '#059669'; +} + +export default function Page() { + const file = path.join(process.cwd(), 'next-app', 'data', 'maturity.json'); + const data: Maturity = JSON.parse(readFileSync(file, 'utf8')); + return ( +
+

Governance Capability Matrix

+

Scores (0–5), evidence, gaps, remediation and gating guidance per dimension.

+ +
+ {data.dimensions.map((d) => { + const gate = gateText(d.score); + return ( +
+
+
+
{d.name}
+
Phase: {d.phase}
+
+
+ + Score {d.score} + +
{gate.label}
+
+
+ + {d.evidence?.length ? ( +
+
Evidence
+
    + {d.evidence.map((e, i) => (
  • {e}
  • ))} +
+
+ ) : null} + + {d.gaps?.length ? ( +
+
Gaps
+
    + {d.gaps.map((g, i) => (
  • {g}
  • ))} +
+
{gate.note}
+
+ ) : null} + + {d.remediation?.length ? ( +
+
Remediation
+
    + {d.remediation.map((r, i) => (
  • {r}
  • ))} +
+
+ ) : null} + + {d.links && Object.keys(d.links).length > 0 ? ( +
+ {Object.entries(d.links).map(([k, v]) => ( + + {k} + + ))} +
+ ) : null} +
+ ); + })} +
+
+ ); +} diff --git a/next-app/app/governance/page.tsx b/next-app/app/governance/page.tsx new file mode 100644 index 0000000..bd64241 --- /dev/null +++ b/next-app/app/governance/page.tsx @@ -0,0 +1,19 @@ +import Link from 'next/link'; + +export const metadata = { title: 'Governance Cockpit' }; +export default function GovernancePage() { + return ( +
+

Governance Cockpit

+

Board-ready artifact hub with live roadmap, mappings, and templates.

+
    +
  • Roadmap (capacity-aware)
  • +
  • Integrated 18‑Point Mapping
  • +
  • Implementation Readiness Checklist
  • +
  • Governance Artefact Templates
  • +
  • Governance Capability Matrix
  • +
  • Interactive Risk & Governance Demos
  • +
+
+ ); +} diff --git a/next-app/app/risk/page.tsx b/next-app/app/risk/page.tsx new file mode 100644 index 0000000..89ec60a --- /dev/null +++ b/next-app/app/risk/page.tsx @@ -0,0 +1,106 @@ +export const metadata = { title: 'AI Risk Navigator' } as const; +import { PULSE_SCRIPT } from './pulse-script'; + +export default function RiskPage() { + return ( +
+

Interactive 10-Stage AI Risk Matrix

+

Filterable matrix and governance dashboard demos.

+