@@ -3,18 +3,25 @@ import { cors } from "hono/cors";
33import { streamSSE } from "hono/streaming" ;
44import {
55 agents ,
6+ appealException ,
67 contracts ,
78 controlState ,
89 counterfactualReplay ,
910 createCompany ,
1011 decideException ,
12+ deliberationEntries ,
1113 departments ,
1214 exceptions ,
15+ expireExceptionTtl ,
1316 listSpans ,
1417 resolveProvider ,
1518 runCompanyDay ,
19+ subscribeFirmEvents ,
20+ recentFirmEvents ,
21+ transparencyRecords ,
1622 traces ,
1723 type Company ,
24+ type EnforcementMode ,
1825} from "@corpos/core" ;
1926import { eq } from "drizzle-orm" ;
2027import fs from "node:fs" ;
@@ -45,12 +52,18 @@ export function buildApp(company: Company, mode: "simulation" | "live" = "simula
4552 const app = new Hono ( ) ;
4653 app . use ( "*" , cors ( ) ) ;
4754
55+ const envMode = process . env . CORPOS_ENFORCEMENT ;
56+ if ( envMode === "strict" || envMode === "audit" ) {
57+ company . policy . setEnforcementMode ( envMode ) ;
58+ }
59+
4860 app . get ( "/api/health" , ( c ) =>
4961 c . json ( {
5062 ok : true ,
5163 mode,
5264 product : "autonomous-company-reference" ,
5365 provider : mode === "live" ? "HttpLLMProvider" : "SimulationProvider" ,
66+ enforcement : company . policy . getEnforcementMode ( ) ,
5467 } ) ,
5568 ) ;
5669
@@ -73,8 +86,17 @@ export function buildApp(company: Company, mode: "simulation" | "live" = "simula
7386 c . json ( ( await company . db . select ( ) . from ( exceptions ) ) . filter ( ( e ) => e . state === "pending" ) ) ,
7487 ) ;
7588
89+ app . get ( "/api/exceptions/:id/deliberation" , async ( c ) => {
90+ const rows = await company . db
91+ . select ( )
92+ . from ( deliberationEntries )
93+ . where ( eq ( deliberationEntries . exceptionId , c . req . param ( "id" ) ) ) ;
94+ return c . json ( rows ) ;
95+ } ) ;
96+
7697 app . post ( "/api/exceptions/:id/decide" , async ( c ) => {
7798 if ( ! requireAuth ( c ) ) return c . json ( { error : "dashboard authentication required" } , 401 ) ;
99+ await expireExceptionTtl ( company ) ;
78100 const body = await c . req . json < {
79101 decision : "approved" | "rejected" ;
80102 by ?: string ;
@@ -90,6 +112,19 @@ export function buildApp(company: Company, mode: "simulation" | "live" = "simula
90112 return c . json ( { ok : true , ...out } ) ;
91113 } ) ;
92114
115+ app . post ( "/api/exceptions/:id/appeal" , async ( c ) => {
116+ if ( ! requireAuth ( c ) ) return c . json ( { error : "dashboard authentication required" } , 401 ) ;
117+ const body = await c . req . json < { by ?: string ; reason ?: string } > ( ) ;
118+ const out = await appealException (
119+ company ,
120+ c . req . param ( "id" ) ,
121+ body . by ?? "operator" ,
122+ body . reason ?? "appeal" ,
123+ ) ;
124+ if ( ! out . ok ) return c . json ( out , 400 ) ;
125+ return c . json ( out ) ;
126+ } ) ;
127+
93128 app . post ( "/api/kill" , async ( c ) => {
94129 if ( ! requireAuth ( c ) ) return c . json ( { error : "dashboard authentication required" } , 401 ) ;
95130 const body = await c . req . json < { killed : boolean } > ( ) ;
@@ -98,7 +133,7 @@ export function buildApp(company: Company, mode: "simulation" | "live" = "simula
98133 } ) ;
99134
100135 app . post ( "/api/company-day" , async ( c ) => {
101- // Default off: do not imply human approval. Explicit body opt-in for demos/tests.
136+ await expireExceptionTtl ( company ) ;
102137 let autoApproveException = false ;
103138 try {
104139 const body = await c . req . json < { autoApproveException ?: boolean } > ( ) ;
@@ -117,11 +152,22 @@ export function buildApp(company: Company, mode: "simulation" | "live" = "simula
117152 const ctrl = (
118153 await company . db . select ( ) . from ( controlState ) . where ( eq ( controlState . id , "global" ) )
119154 ) [ 0 ] ;
155+ const transparency = ( await company . db . select ( ) . from ( transparencyRecords ) ) . slice ( - 50 ) ;
120156 return c . json ( {
121157 aibom,
122158 spans,
123159 auditOk : recentDenies ,
124160 killed : Boolean ( ctrl ?. killed ) ,
161+ enforcement : company . policy . getEnforcementMode ( ) ,
162+ transparency,
163+ gLabels : {
164+ G1 : "membership/active" ,
165+ G2 : "deliberation trail" ,
166+ G3 : "quorum N-of-M" ,
167+ G4 : "dissent on reject" ,
168+ G5 : "decision transparency" ,
169+ G6 : "appeal/escalation" ,
170+ } ,
125171 asiControls : {
126172 ASI01 : "untrusted KB/CRM boundary" ,
127173 ASI02 : "fail-closed gateway + draft/settle" ,
@@ -144,6 +190,16 @@ export function buildApp(company: Company, mode: "simulation" | "live" = "simula
144190 } ) ;
145191 } ) ;
146192
193+ app . post ( "/api/governance/enforcement" , async ( c ) => {
194+ if ( ! requireAuth ( c ) ) return c . json ( { error : "dashboard authentication required" } , 401 ) ;
195+ const body = await c . req . json < { mode : EnforcementMode } > ( ) ;
196+ if ( body . mode !== "strict" && body . mode !== "audit" ) {
197+ return c . json ( { error : "mode must be strict|audit" } , 400 ) ;
198+ }
199+ company . policy . setEnforcementMode ( body . mode ) ;
200+ return c . json ( { ok : true , mode : body . mode } ) ;
201+ } ) ;
202+
147203 app . get ( "/api/traces/:taskId" , async ( c ) => {
148204 const row = ( await company . db . select ( ) . from ( traces ) ) . find (
149205 ( t ) => t . taskId === c . req . param ( "taskId" ) ,
@@ -174,6 +230,7 @@ export function buildApp(company: Company, mode: "simulation" | "live" = "simula
174230 await stream . writeSSE ( {
175231 data : JSON . stringify ( {
176232 type : "snapshot" ,
233+ recent : recentFirmEvents ( ) ,
177234 firm : {
178235 agents : await company . db . select ( ) . from ( agents ) ,
179236 exceptions : ( await company . db . select ( ) . from ( exceptions ) ) . filter (
@@ -182,6 +239,18 @@ export function buildApp(company: Company, mode: "simulation" | "live" = "simula
182239 } ,
183240 } ) ,
184241 } ) ;
242+ const unsub = subscribeFirmEvents ( ( event ) => {
243+ void stream . writeSSE ( { data : JSON . stringify ( event ) } ) ;
244+ } ) ;
245+ // Keep stream open; client disconnect ends the handler when write fails.
246+ try {
247+ while ( true ) {
248+ await stream . writeSSE ( { data : JSON . stringify ( { type : "heartbeat" , at : Date . now ( ) } ) } ) ;
249+ await stream . sleep ( 15_000 ) ;
250+ }
251+ } finally {
252+ unsub ( ) ;
253+ }
185254 } ) ,
186255 ) ;
187256
@@ -194,5 +263,12 @@ export async function createDefaultCompany(): Promise<{
194263} > {
195264 const { mode } = resolveProvider ( ) ;
196265 const company = await createCompany ( { dbPath : process . env . CORPOS_DB } ) ;
266+ await expireExceptionTtl ( company ) ;
197267 return { company, mode } ;
198268}
269+
270+ export function startTtlScheduler ( company : Company , intervalMs = 30_000 ) : NodeJS . Timeout {
271+ return setInterval ( ( ) => {
272+ void expireExceptionTtl ( company ) ;
273+ } , intervalMs ) ;
274+ }
0 commit comments