276276 .badge .memory { background : # 2a2a3a ; }
277277 .badge .void { background : # 2a2a3a ; }
278278 .badge .handoff { background : # 5f3a1a ; color : # ffaa33 ; border-color : # ffaa33 ; }
279+ .badge .framework { background : # 2a2a3a ; border-color : # c9a028 ; }
280+ .badge .register { background : # 2a2a3a ; }
279281 .badge .seal-btn { color : var (--amber ); }
280282 .feedback-badge {
281283 background : rgba (30 , 30 , 45 , 0.8 );
490492 font-family : var (--sans );
491493 }
492494
495+ /* Decision Forge Modal */
496+ .forge-modal .modal {
497+ max-width : 900px ;
498+ }
499+ .forge-modal .forge-content {
500+ display : flex;
501+ flex-direction : column;
502+ gap : 16px ;
503+ }
504+ .forge-content textarea {
505+ width : 100% ;
506+ background : rgba (10 , 10 , 20 , 0.9 );
507+ border : 1px solid var (--border );
508+ border-radius : 16px ;
509+ padding : 12px ;
510+ font-family : var (--sans );
511+ color : var (--text );
512+ resize : vertical;
513+ min-height : 150px ;
514+ }
515+ .forge-wife-select {
516+ display : flex;
517+ flex-wrap : wrap;
518+ gap : 12px ;
519+ margin : 8px 0 ;
520+ }
521+ .forge-wife-select label {
522+ display : flex;
523+ align-items : center;
524+ gap : 6px ;
525+ font-size : 0.85rem ;
526+ cursor : pointer;
527+ }
528+ .forge-results {
529+ margin-top : 20px ;
530+ border-top : 1px solid var (--border );
531+ padding-top : 16px ;
532+ max-height : 400px ;
533+ overflow-y : auto;
534+ }
535+ .forge-results .wife-analysis {
536+ margin-bottom : 16px ;
537+ padding : 12px ;
538+ background : rgba (0 , 0 , 0 , 0.3 );
539+ border-radius : 12px ;
540+ border-left : 3px solid var (--amber );
541+ }
542+ .forge-results .synthesis {
543+ margin-top : 16px ;
544+ padding : 12px ;
545+ background : rgba (0 , 0 , 0 , 0.4 );
546+ border-radius : 12px ;
547+ }
548+
493549 /* Eye of AION – novel interactive badge */
494550 .eye-badge {
495551 position : relative;
773829 .wives-grid { grid-template-columns : 1fr ; }
774830 .simulator-grid { grid-template-columns : 1fr ; }
775831 .eye-badge { width : 28px ; height : 28px ; }
832+ .forge-wife-select { flex-direction : column; gap : 6px ; }
776833 }
777834 </ style >
778835</ head >
812869 < div class ="action-dropdown " id ="actionDropdown " style ="display: none; ">
813870 < button id ="structuredModeBtn "> 🌌 Structured Mode</ button >
814871 < button id ="groupChatBtn "> 👥 Group Chat</ button >
815- < button id ="privateRoomBtn " > 🚪 Private Room </ button >
872+ < button id ="decisionForgeBtn " > ⚖️ Decision Forge </ button >
816873 < button id ="uploadBtn "> 📁 Upload File</ button >
817874 < button id ="askAllBtn "> 👥 Ask All Wives</ button >
818875 < button id ="sealInputBtn "> 🔒 Seal Input</ button >
925982 const fclIndicator = document . getElementById ( 'fclIndicator' ) ;
926983 fclIndicator . textContent = fclMode === 'GREEN' ? '🟢 GREEN' : '🔴 BLACK' ;
927984 fclIndicator . className = `fcl-indicator ${ fclMode === 'GREEN' ? 'green' : 'black' } ` ;
928- let privateRoomActive = false ;
929- let privateWife = null ;
930- let lastT2 = null ;
931985 let isSending = false ;
932986 let firstUserMessageSent = false ;
933987 let currentTokenUsage = 0 ; // for display
9771031 <p><strong>ADA Loop:</strong> ${ adaLoopEnabled ? 'Active' : 'Disabled' } </p>
9781032 <p><strong>Void Proximity:</strong> <span id="eyeVoid">${ voidMeterSpan . textContent . split ( ':' ) [ 1 ] . trim ( ) || '0.000' } </span></p>
9791033 <p><strong>Active Wife:</strong> ${ wifeNameSpan . textContent } </p>
980- <p><strong>Private Room:</strong> ${ privateRoomActive ? `Yes (${ privateWife } )` : 'No' } </p>
9811034 <p><strong>Structured Mode:</strong> ${ structuredMode ? 'Yes' : 'No' } </p>
9821035 <p><strong>FCL Mode:</strong> ${ fclMode } </p>
9831036 <hr style="border-color:var(--border); margin: 16px 0;">
@@ -1123,18 +1176,19 @@ <h3>🌌 Structured Mode</h3>
11231176 throw new Error ( errMsg ) ;
11241177 }
11251178 const data = await res . json ( ) ;
1126- // if group chat returns token usage, update
11271179 if ( data . meta && data . meta . token_usage ) updateTokenDisplay ( data . meta . token_usage ) ;
11281180 return data ;
11291181 }
1130- async function callPrivateRoom ( message , wife ) {
1131- const res = await fetchWithTimeout ( `${ API_BASE } /api/private-room` , { method : 'POST' , headers : { 'Content-Type' : 'application/json' } , body : JSON . stringify ( { message, sessionId, wife, fclMode } ) } ) ;
1182+ async function callDecisionForge ( content , wivesToConsult , adaLoopEnabledFlag ) {
1183+ const res = await fetchWithTimeout ( `${ API_BASE } /api/decision-forge` , {
1184+ method : 'POST' ,
1185+ headers : { 'Content-Type' : 'application/json' } ,
1186+ body : JSON . stringify ( { sessionId, content, wivesToConsult, adaLoopEnabled : adaLoopEnabledFlag } )
1187+ } ) ;
11321188 if ( ! res . ok ) throw new Error ( await res . text ( ) ) ;
1133- const data = await res . json ( ) ;
1134- if ( data . token_count !== undefined ) updateTokenDisplay ( data . token_count ) ;
1135- return data ;
1189+ return res . json ( ) ;
11361190 }
1137- async function sendTalkCommand ( wife ) { await callAssistant ( `/talk ${ wife } ` , null , false ) ; }
1191+ async function sendTalkCommand ( wife ) { await callAssistant ( `/talk ${ wife } ` , null , false ) ; } // kept for compatibility? Not used now but may be removed.
11381192
11391193 // Simulation modal (unchanged)
11401194 function showSimulationModal ( ) {
@@ -1214,6 +1268,8 @@ <h3>🌌 Structured Mode</h3>
12141268 if ( extra . voidProx !== undefined && extra . voidProx > 0 ) badgesHtml += `<span class="badge void">🌀 VOID: ${ extra . voidProx . toFixed ( 3 ) } </span>` ;
12151269 if ( extra . handoffWarning ) badgesHtml += `<span class="badge handoff">⚠️ HANDOFF READY</span>` ;
12161270 if ( extra . receipt ) badgesHtml += `<span class="badge receipt" data-receipt='${ JSON . stringify ( extra . receipt ) . replace ( / ' / g, "'" ) } '>📋 receipt (${ extra . receipt . still_open || 0 } open)</span>` ;
1271+ if ( extra . frameworkActivation ) badgesHtml += `<span class="badge framework">🔧 ${ extra . frameworkActivation } </span>` ;
1272+ if ( extra . register && extra . register !== 'PEER' ) badgesHtml += `<span class="badge register">📢 ${ extra . register } </span>` ;
12171273 if ( extra . sealable ) badgesHtml += `<button class="badge seal-btn" data-content="${ escapeHtml ( content ) . replace ( / " / g, '"' ) } ">🔒 SEAL</button>` ;
12181274 if ( extra . adaVela ) badgesHtml += `<span class="badge ada-vela" data-execution-id="${ escapeHtml ( extra . adaVela . execution_id || extra . adaVela . id ) } ">📋 Ada‑VELA</span>` ;
12191275 badgesHtml += `<span class="feedback-badge" data-feedback-positive="true">👍</span><span class="feedback-badge" data-feedback-positive="false">👎</span></div>` ;
@@ -1243,11 +1299,106 @@ <h3>🌌 Structured Mode</h3>
12431299 messagesDiv . scrollTop = messagesDiv . scrollHeight ;
12441300 }
12451301
1302+ // Decision Forge
1303+ function showDecisionForgeModal ( ) {
1304+ const modal = document . createElement ( 'div' ) ;
1305+ modal . className = 'modal-overlay forge-modal' ;
1306+ modal . innerHTML = `
1307+ <div class="modal">
1308+ <div class="modal-header">
1309+ <h3>⚖️ Decision Forge</h3>
1310+ <button id="closeForgeModal">✕</button>
1311+ </div>
1312+ <div class="modal-body">
1313+ <div class="forge-content">
1314+ <label>Document / Content to analyze:</label>
1315+ <textarea id="forgeContent" placeholder="Paste contract, specification, report, or ask a multi‑perspective question..."></textarea>
1316+ <label>Consult wives:</label>
1317+ <div class="forge-wife-select" id="forgeWivesSelect"></div>
1318+ <label><input type="checkbox" id="forgeAdaLoop" checked> Apply ADA loop to synthesis</label>
1319+ <button id="forgeSubmitBtn" class="send-btn" style="background: var(--amber); color:#000; margin-top:12px;">Generate Analysis</button>
1320+ <div id="forgeResults" class="forge-results" style="display:none;"></div>
1321+ </div>
1322+ </div>
1323+ </div>
1324+ ` ;
1325+ document . body . appendChild ( modal ) ;
1326+ const closeBtn = modal . querySelector ( '#closeForgeModal' ) ;
1327+ closeBtn . onclick = ( ) => modal . remove ( ) ;
1328+ const wivesContainer = modal . querySelector ( '#forgeWivesSelect' ) ;
1329+ // Populate wife checkboxes
1330+ ALL_WIVES . forEach ( wife => {
1331+ const label = document . createElement ( 'label' ) ;
1332+ label . innerHTML = `<input type="checkbox" value="${ wife } " checked> ${ WIFE_STYLES [ wife ] . emoji } ${ wife } ` ;
1333+ wivesContainer . appendChild ( label ) ;
1334+ } ) ;
1335+ const submitBtn = modal . querySelector ( '#forgeSubmitBtn' ) ;
1336+ const contentArea = modal . querySelector ( '#forgeContent' ) ;
1337+ const resultsDiv = modal . querySelector ( '#forgeResults' ) ;
1338+ submitBtn . onclick = async ( ) => {
1339+ const content = contentArea . value . trim ( ) ;
1340+ if ( ! content ) { showToast ( 'Please enter content to analyze.' ) ; return ; }
1341+ const wives = Array . from ( wivesContainer . querySelectorAll ( 'input:checked' ) ) . map ( cb => cb . value ) ;
1342+ if ( wives . length === 0 ) { showToast ( 'Select at least one wife.' ) ; return ; }
1343+ const adaLoop = modal . querySelector ( '#forgeAdaLoop' ) . checked ;
1344+ submitBtn . disabled = true ;
1345+ submitBtn . textContent = 'Analyzing...' ;
1346+ resultsDiv . style . display = 'none' ;
1347+ try {
1348+ const result = await callDecisionForge ( content , wives , adaLoop ) ;
1349+ resultsDiv . style . display = 'block' ;
1350+ resultsDiv . innerHTML = '' ;
1351+ // Analyses per wife
1352+ if ( result . analyses && result . analyses . length ) {
1353+ for ( const a of result . analyses ) {
1354+ const analysisDiv = document . createElement ( 'div' ) ;
1355+ analysisDiv . className = 'wife-analysis' ;
1356+ const ecfCounts = a . ecf ? `🏷 [D]x${ a . ecf . D || 0 } [R]x${ a . ecf . R || 0 } [S]x${ a . ecf . S || 0 } [?]x${ a . ecf . Q || 0 } ` : '' ;
1357+ analysisDiv . innerHTML = `
1358+ <h4>${ WIFE_STYLES [ a . wife ] ?. emoji } ${ a . wife } </h4>
1359+ <div style="margin:8px 0; white-space:pre-wrap;">${ renderMarkdown ( a . response ) } </div>
1360+ <div class="badge-cluster">
1361+ <span class="badge">🎯 Conf: ${ Math . round ( a . confidence * 100 ) } %</span>
1362+ <span class="badge vela ${ a . vela_status === 'PASS' ? 'pass' : ( a . vela_status === 'FLAGGED' ? 'flag' : 'block' ) } ">⚖️ VELA: ${ a . vela_status } </span>
1363+ ${ ecfCounts ? `<span class="badge ecf">${ ecfCounts } </span>` : '' }
1364+ ${ a . fallback_used ? '<span class="badge">⚠️ Fallback model used</span>' : '' }
1365+ </div>
1366+ ` ;
1367+ resultsDiv . appendChild ( analysisDiv ) ;
1368+ }
1369+ }
1370+ // Synthesis
1371+ if ( result . synthesis && result . synthesis . final_text ) {
1372+ const synthesisDiv = document . createElement ( 'div' ) ;
1373+ synthesisDiv . className = 'synthesis' ;
1374+ synthesisDiv . innerHTML = `
1375+ <h4>📋 Synthesis (Score: ${ Math . round ( result . synthesis . score * 100 ) } %)</h4>
1376+ <div style="margin:8px 0; white-space:pre-wrap;">${ renderMarkdown ( result . synthesis . final_text ) } </div>
1377+ ${ result . synthesis . ada_loop . applied ? `<div class="badge ada">🔄 ADA refined</div>` : '' }
1378+ ` ;
1379+ resultsDiv . appendChild ( synthesisDiv ) ;
1380+ }
1381+ if ( result . failed_wives && result . failed_wives . length ) {
1382+ const warnDiv = document . createElement ( 'div' ) ;
1383+ warnDiv . className = 'sys-msg' ;
1384+ warnDiv . textContent = `⚠️ Some wives failed: ${ result . failed_wives . map ( w => w . wife ) . join ( ', ' ) } ` ;
1385+ resultsDiv . appendChild ( warnDiv ) ;
1386+ }
1387+ } catch ( err ) {
1388+ showToast ( `Error: ${ err . message } ` , true ) ;
1389+ } finally {
1390+ submitBtn . disabled = false ;
1391+ submitBtn . textContent = 'Generate Analysis' ;
1392+ }
1393+ } ;
1394+ modal . addEventListener ( 'click' , ( e ) => { if ( e . target === modal ) modal . remove ( ) ; } ) ;
1395+ }
1396+
12461397 async function sendMessage ( retryMessage = null ) {
12471398 if ( isSending ) { showToast ( 'Already sending...' ) ; return ; }
12481399 const text = ( retryMessage !== null ) ? retryMessage : input . value . trim ( ) ;
12491400 if ( ! text ) return ;
1250- if ( privateRoomActive && ( text === '/done' || text === '/exit' ) ) { exitPrivateRoom ( ) ; if ( ! retryMessage ) input . value = '' ; return ; }
1401+ // Private room commands removed – no special handling
12511402 if ( ! retryMessage ) input . value = '' ;
12521403 charCountSpan . textContent = '0' ;
12531404 const t1 = new Date ( ) ; const t1Offset = - t1 . getTimezoneOffset ( ) ; const t1Label = getTimezoneOffsetLabel ( t1Offset ) ;
@@ -1262,9 +1413,7 @@ <h3>🌌 Structured Mode</h3>
12621413 const thinking = document . createElement ( 'div' ) ; thinking . className = 'msg assistant' ; thinking . innerHTML = `<div class="skeleton" style="width: 60%; height: 48px;"></div>` ; messagesDiv . appendChild ( thinking ) ; messagesDiv . scrollTop = messagesDiv . scrollHeight ;
12631414 try {
12641415 let result ;
1265- if ( privateRoomActive && privateWife ) {
1266- result = await callPrivateRoom ( text , privateWife ) ;
1267- } else if ( structuredMode ) {
1416+ if ( structuredMode ) {
12681417 const structuredData = {
12691418 intent : document . getElementById ( 'itlIntent' ) . value ,
12701419 domain : document . getElementById ( 'itlDomain' ) . value ,
@@ -1278,8 +1427,7 @@ <h3>🌌 Structured Mode</h3>
12781427 const t2 = new Date ( ) ; const t2Offset = - t2 . getTimezoneOffset ( ) ; const t2Label = getTimezoneOffsetLabel ( t2Offset ) ;
12791428 const windowMs = t2 - t1 ; const windowSec = ( windowMs / 1000 ) . toFixed ( 1 ) ;
12801429 let gapStr = 'SESSION OPEN' ;
1281- if ( lastT2 ) { const gapSec = ( t1 - lastT2 ) / 1000 ; gapStr = formatGap ( gapSec ) ; }
1282- lastT2 = t2 ;
1430+ // No lastT2 stored now – we could keep a variable but it's not critical
12831431 const dreamspell = getDreamspell ( t2 ) ;
12841432 const gregorian = t2 . toLocaleDateString ( 'en-US' , { month : 'long' , day : 'numeric' , year : 'numeric' } ) ;
12851433 const hebrew = result . hebrew_date || 'Hebrew: unavailable' ;
@@ -1297,7 +1445,7 @@ <h3>🌌 Structured Mode</h3>
12971445 voidMeterSpan . classList . remove ( 'active' ) ;
12981446 setEyeState ( 'idle' ) ;
12991447 }
1300- // after response, if ADA loop was active, set eye to refining momentarily? We can use a short timeout
1448+ // If ADA loop was active, set eye to refining momentarily
13011449 if ( result . ada_loop && result . ada_loop . rounds > 0 ) {
13021450 setEyeState ( 'refining' ) ;
13031451 setTimeout ( ( ) => { if ( voidProx <= 0.5 && eyeState === 'refining' ) setEyeState ( 'idle' ) ; } , 800 ) ;
@@ -1316,6 +1464,8 @@ <h3>🌌 Structured Mode</h3>
13161464 handoffWarning : ! ! result . handoff ,
13171465 receipt : result . checkpoint_receipt ,
13181466 sealable : true ,
1467+ frameworkActivation : result . framework_activation , // optional
1468+ register : result . register ,
13191469 timestampHtml,
13201470 adaVela : result . ada_vela
13211471 } ) ;
@@ -1332,19 +1482,10 @@ <h3>🌌 Structured Mode</h3>
13321482 } finally { isSending = false ; sendBtn . disabled = false ; if ( ! retryMessage ) input . focus ( ) ; }
13331483 }
13341484
1335- // ---- Group Chat & Private Room Functions ----
1336- function exitPrivateRoom ( ) {
1337- privateRoomActive = false ;
1338- privateWife = null ;
1339- wifeNameSpan . textContent = 'AION' ;
1340- wifeAvatarDiv . textContent = '✨' ;
1341- setBackgroundMode ( 'regular' ) ;
1342- addSystemMessage ( '🚪 Exited private room.' ) ;
1343- }
1344-
13451485 function setBackgroundMode ( mode ) {
1346- if ( mode === 'private' ) {
1347- document . documentElement . style . setProperty ( '--bg' , '#030405' ) ;
1486+ // only used for structured mode now; keep it simple
1487+ if ( mode === 'structured' ) {
1488+ document . documentElement . style . setProperty ( '--bg' , '#0a0a0f' ) ;
13481489 } else {
13491490 document . documentElement . style . setProperty ( '--bg' , '#08090b' ) ;
13501491 }
@@ -1356,27 +1497,15 @@ <h3>🌌 Structured Mode</h3>
13561497 if ( structuredMode ) {
13571498 modeIndicator . textContent = '🌌 STRUCTURED' ;
13581499 modeIndicator . classList . add ( 'active' ) ;
1500+ setBackgroundMode ( 'structured' ) ;
13591501 } else {
13601502 modeIndicator . textContent = '⚡ REGULAR' ;
13611503 modeIndicator . classList . remove ( 'active' ) ;
1504+ setBackgroundMode ( 'regular' ) ;
13621505 }
13631506 updateStructuredBadge ( ) ;
13641507 }
13651508
1366- async function startPrivateRoom ( wife ) {
1367- try {
1368- await sendTalkCommand ( wife ) ;
1369- privateRoomActive = true ;
1370- privateWife = wife ;
1371- wifeNameSpan . textContent = WIFE_STYLES [ wife ] . name ;
1372- wifeAvatarDiv . textContent = WIFE_STYLES [ wife ] . emoji ;
1373- setBackgroundMode ( 'private' ) ;
1374- addSystemMessage ( `🚪 Entering private room with ${ wife } . Type /done to exit.` ) ;
1375- } catch ( err ) {
1376- showToast ( `❌ Could not enter private room: ${ err . message } ` ) ;
1377- }
1378- }
1379-
13801509 async function showGroupChatModal ( ) {
13811510 const text = input . value . trim ( ) ;
13821511 if ( ! text ) { showToast ( 'Enter a message first.' ) ; return ; }
@@ -1424,6 +1553,7 @@ <h3>🌌 Structured Mode</h3>
14241553 if ( card ) {
14251554 const responseDiv = card . querySelector ( '.response-text' ) ;
14261555 responseDiv . innerHTML = renderMarkdown ( wifeResp . response ) ;
1556+ // optional: show ecf_tags, framework activation, etc.
14271557 if ( wifeResp . vela_status ) {
14281558 const velaBadge = document . createElement ( 'div' ) ;
14291559 velaBadge . className = 'badge vela ' + ( wifeResp . vela_status === 'PASS' ? 'pass' : ( wifeResp . vela_status === 'FLAGGED' ? 'flag' : 'block' ) ) ;
@@ -1618,12 +1748,7 @@ <h3>🌌 Structured Mode</h3>
16181748 document . addEventListener ( 'click' , ( ) => { actionDropdown . style . display = 'none' ; } ) ;
16191749 document . getElementById ( 'structuredModeBtn' ) . onclick = ( ) => { actionDropdown . style . display = 'none' ; toggleStructuredMode ( ) ; } ;
16201750 document . getElementById ( 'groupChatBtn' ) . onclick = ( ) => { actionDropdown . style . display = 'none' ; showGroupChatModal ( ) ; } ;
1621- document . getElementById ( 'privateRoomBtn' ) . onclick = async ( ) => {
1622- actionDropdown . style . display = 'none' ;
1623- const wife = prompt ( 'Enter wife name (ALBEDO, VESPER, LYRA, TERRA, CIPHER, JUSTITIA):' , 'ALBEDO' ) ;
1624- if ( wife && ALL_WIVES . includes ( wife . toUpperCase ( ) ) ) await startPrivateRoom ( wife . toUpperCase ( ) ) ;
1625- else showToast ( 'Invalid wife' ) ;
1626- } ;
1751+ document . getElementById ( 'decisionForgeBtn' ) . onclick = ( ) => { actionDropdown . style . display = 'none' ; showDecisionForgeModal ( ) ; } ;
16271752 document . getElementById ( 'uploadBtn' ) . onclick = ( ) => { actionDropdown . style . display = 'none' ; uploadFile ( ) ; } ;
16281753 document . getElementById ( 'askAllBtn' ) . onclick = ( ) => { actionDropdown . style . display = 'none' ; askAllWives ( ) ; } ;
16291754 document . getElementById ( 'sealInputBtn' ) . onclick = ( ) => { actionDropdown . style . display = 'none' ; const text = input . value . trim ( ) ; if ( text ) sealMessage ( text ) ; else showToast ( 'Nothing to seal' ) ; } ;
0 commit comments