@gamaze/hicortex 0.20.10 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/dashboard.html +202 -16
- package/package.json +1 -1
package/assets/dashboard.html
CHANGED
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
visual on real endpoints, zero new write paths (Update/Remove ride the
|
|
7
7
|
existing /update + /delete; identity Save rides PUT /identity).
|
|
8
8
|
|
|
9
|
+
NAMING (owner ruling 2026-09-14): this surface is "the Hicortex server
|
|
10
|
+
dashboard" — the word "console" never appears in USER-VISIBLE strings
|
|
11
|
+
(titles, tooltips, aria-labels, placeholders, toasts, empty states).
|
|
12
|
+
Historical "console" wording in these comments is internal shorthand and
|
|
13
|
+
stays.
|
|
14
|
+
|
|
9
15
|
PORTED FROM mockups/409-r6-flight.html (the design of record — its banner
|
|
10
16
|
documents every behavior; the engine below is that page with the seeded
|
|
11
17
|
data layer replaced by fetches). Live-data substitutions:
|
|
@@ -87,6 +93,28 @@
|
|
|
87
93
|
within ~2–2.5× (owner: no huge green balls, nothing barely visible);
|
|
88
94
|
stage colors untouched.
|
|
89
95
|
|
|
96
|
+
FIX ROUND 6 (owner-approved proto of record mockups/progress-bar.html,
|
|
97
|
+
2026-09-14 — variant 3 + copy A): ENTRY LOADING SCREEN. Pure inline
|
|
98
|
+
HTML+CSS at the very top of the body, BEFORE the app markup and the
|
|
99
|
+
engine script — it paints on URL entry with ZERO script (the real
|
|
100
|
+
page is black for seconds while the engine parses/boots; the splash
|
|
101
|
+
must beat that). All motion is opacity/transform only, so the
|
|
102
|
+
compositor keeps it animating straight through the engine's main-
|
|
103
|
+
thread freeze. Fully opaque black in BOTH themes (the proto's light
|
|
104
|
+
"paper twin" was review chrome, not shipped behavior): a breathing
|
|
105
|
+
green dot above an indeterminate flowing line (two brand-orange
|
|
106
|
+
segments, uneven pace, looping — never a percentage, never an ETA)
|
|
107
|
+
under the copy line "Entering the Hicortex fleet brain". The engine
|
|
108
|
+
only ever fades it away: entrySplashDismiss hooks the boot REVEAL —
|
|
109
|
+
the existing single handover (data landed → world built → app
|
|
110
|
+
unhidden), NOT DOMContentLoaded — fades .4s, then removes the node.
|
|
111
|
+
Failure seams swap the copy line honestly and never loop silently:
|
|
112
|
+
both core sources rejected → "could not reach the Hicortex server…"
|
|
113
|
+
(held readable ≥2.8 s); boot() throws → same treatment + the static
|
|
114
|
+
shell; 30 s without any settlement → "still waiting…" (no dismiss —
|
|
115
|
+
data may still land). #still removes it synchronously (screenshot
|
|
116
|
+
contract); reduced motion rests on the bar's static base transform.
|
|
117
|
+
|
|
90
118
|
FIX ROUND 3 (owner findings on the live console, 2026-09-14):
|
|
91
119
|
- FIELD LINKS (owner: "the white lines … get very thick — they kind of
|
|
92
120
|
overtake most of the image"): settled strokes are hairline-floored and
|
|
@@ -442,6 +470,22 @@
|
|
|
442
470
|
padding:2px 11px; white-space:nowrap; width:fit-content;
|
|
443
471
|
}
|
|
444
472
|
#lim-ag .limit-cta:hover{ border-color:var(--amber); }
|
|
473
|
+
/* #409 fix round 4 — the Model card's configure pill: the mock of record
|
|
474
|
+
puts it at the card's BOTTOM-RIGHT, grey/muted (the neutral twin of the
|
|
475
|
+
Agents Connect pill above). margin-top:auto pins it to the card's foot
|
|
476
|
+
however tall the row stretches — row-2 siblings flex-fill, so
|
|
477
|
+
align-self:flex-end alone would leave it floating mid-card. The
|
|
478
|
+
:not([hidden]) guard keeps the degrade (old server / GET failure)
|
|
479
|
+
working: a plain display rule would override the hidden attribute. All
|
|
480
|
+
colors ride theme vars — both themes come free. */
|
|
481
|
+
#model-card .limit-cta:not([hidden]){
|
|
482
|
+
display:inline-block; align-self:flex-end; margin-top:auto;
|
|
483
|
+
color:var(--dim); border:1px solid var(--line2);
|
|
484
|
+
background:color-mix(in srgb, var(--text) 4%, transparent);
|
|
485
|
+
font-size:10.5px; font-weight:600; border-radius:999px;
|
|
486
|
+
padding:2px 11px; white-space:nowrap; width:fit-content;
|
|
487
|
+
}
|
|
488
|
+
#model-card .limit-cta:not([hidden]):hover{ border-color:var(--dim); color:var(--text); }
|
|
445
489
|
|
|
446
490
|
/* ---- row-2 operator cards share the .bseg base; .gh is THE card-header
|
|
447
491
|
component (titles + non-numeric descriptors only) ---- */
|
|
@@ -943,6 +987,62 @@
|
|
|
943
987
|
</script>
|
|
944
988
|
</head>
|
|
945
989
|
<body class="booting">
|
|
990
|
+
<!-- ============ entry loading screen (#409 fix round 6 — the owner-
|
|
991
|
+
approved proto of record mockups/progress-bar.html, variant 3 + copy
|
|
992
|
+
A). Sits at the very top of the body with its OWN style block: it paints
|
|
993
|
+
on URL entry with ZERO script, fully opaque black in both themes, and
|
|
994
|
+
the engine only ever fades it away (entrySplashDismiss at the boot
|
|
995
|
+
reveal). On failure the copy line swaps to an honest error — never an
|
|
996
|
+
infinite loop. All motion is opacity/transform only: the compositor
|
|
997
|
+
keeps it animating while the engine below blocks the main thread. ============ -->
|
|
998
|
+
<style>
|
|
999
|
+
#entry-load{
|
|
1000
|
+
position:fixed; inset:0; z-index:1000; background:#0a0a0a;
|
|
1001
|
+
display:flex; flex-direction:column; align-items:center; justify-content:center; gap:17px;
|
|
1002
|
+
transition:opacity .4s ease;
|
|
1003
|
+
}
|
|
1004
|
+
#entry-load.gone{ opacity:0; pointer-events:none; }
|
|
1005
|
+
/* variant 3's dot: the header healthy mark's look, breathing */
|
|
1006
|
+
#entry-load .el-dot{
|
|
1007
|
+
width:10px; height:10px; border-radius:50%;
|
|
1008
|
+
background:radial-gradient(circle at 35% 35%, color-mix(in srgb, #3dff7a 95%, white), color-mix(in srgb, #3dff7a 25%, transparent) 55%, transparent 70%);
|
|
1009
|
+
box-shadow:0 0 12px color-mix(in srgb, #3dff7a 55%, transparent);
|
|
1010
|
+
animation:el-breathe 3.4s ease-in-out infinite;
|
|
1011
|
+
}
|
|
1012
|
+
@keyframes el-breathe{ 0%,100%{opacity:1} 50%{opacity:.5} }
|
|
1013
|
+
#entry-load .el-line{ font-size:13.5px; color:#9a9a9a; text-align:center; padding:0 24px; }
|
|
1014
|
+
/* the indeterminate line: two brand-orange segments on a faint track,
|
|
1015
|
+
uneven pace, varying length, looping forever; the base transform is
|
|
1016
|
+
the reduced-motion rest pose. Never a percentage, never an ETA. */
|
|
1017
|
+
#entry-load .el-bar{
|
|
1018
|
+
width:min(300px,72%); height:2.5px; border-radius:99px;
|
|
1019
|
+
background:#1e1e1e; overflow:hidden; position:relative;
|
|
1020
|
+
}
|
|
1021
|
+
#entry-load .el-bar i{
|
|
1022
|
+
position:absolute; top:0; bottom:0; left:0; width:100%;
|
|
1023
|
+
background:#ff9f43; border-radius:99px;
|
|
1024
|
+
transform-origin:0 50%; transform:translateX(32%) scaleX(.42);
|
|
1025
|
+
box-shadow:0 0 8px color-mix(in srgb, #ff9f43 45%, transparent);
|
|
1026
|
+
}
|
|
1027
|
+
#entry-load .el-bar i.a{ animation:el-indet-a 2.2s cubic-bezier(.55,.1,.7,.5) infinite backwards; }
|
|
1028
|
+
#entry-load .el-bar i.b{ opacity:.6; animation:el-indet-b 2.2s cubic-bezier(.65,.05,.75,.5) 1.15s infinite backwards; }
|
|
1029
|
+
@keyframes el-indet-a{
|
|
1030
|
+
0% { transform:translateX(-38%) scaleX(.12); animation-timing-function:cubic-bezier(.6,.05,.8,.5); }
|
|
1031
|
+
55% { transform:translateX(46%) scaleX(.62); animation-timing-function:cubic-bezier(.25,.65,.35,1); }
|
|
1032
|
+
80% { transform:translateX(84%) scaleX(.68); animation-timing-function:cubic-bezier(.55,.05,.8,.45); }
|
|
1033
|
+
100% { transform:translateX(140%) scaleX(.16); }
|
|
1034
|
+
}
|
|
1035
|
+
@keyframes el-indet-b{
|
|
1036
|
+
0% { transform:translateX(-24%) scaleX(.08); animation-timing-function:cubic-bezier(.65,.05,.8,.5); }
|
|
1037
|
+
70% { transform:translateX(52%) scaleX(.30); animation-timing-function:cubic-bezier(.2,.6,.45,1); }
|
|
1038
|
+
100% { transform:translateX(116%) scaleX(.10); }
|
|
1039
|
+
}
|
|
1040
|
+
</style>
|
|
1041
|
+
<div id="entry-load" role="status" aria-live="polite">
|
|
1042
|
+
<span class="el-dot" aria-hidden="true"></span>
|
|
1043
|
+
<div class="el-line" id="entry-load-line">Entering the Hicortex fleet brain</div>
|
|
1044
|
+
<div class="el-bar" aria-hidden="true"><i class="a"></i><i class="b"></i></div>
|
|
1045
|
+
</div>
|
|
946
1046
|
<header>
|
|
947
1047
|
<div class="h-left">
|
|
948
1048
|
<div class="h-search">
|
|
@@ -1045,10 +1145,11 @@
|
|
|
1045
1145
|
<div id="feel-rows"></div>
|
|
1046
1146
|
</div>
|
|
1047
1147
|
<div class="bseg c-grey" id="model-card">
|
|
1048
|
-
<div class="gh"><span class="t">Model</span
|
|
1049
|
-
<div class="big"><span class="n" id="model-provider">—</span
|
|
1148
|
+
<div class="gh"><span class="t">Model</span></div>
|
|
1149
|
+
<div class="big"><span class="n" id="model-provider">—</span></div>
|
|
1050
1150
|
<div class="csub mono" id="model-id"> </div>
|
|
1051
1151
|
<div class="csub">detected from config</div>
|
|
1152
|
+
<button class="limit-cta" id="model-cfg" hidden title="edit the model knobs (config.json llm* keys)">configure →</button>
|
|
1052
1153
|
</div>
|
|
1053
1154
|
</div>
|
|
1054
1155
|
</div>
|
|
@@ -1151,9 +1252,57 @@
|
|
|
1151
1252
|
</div>
|
|
1152
1253
|
<script>
|
|
1153
1254
|
'use strict';
|
|
1255
|
+
/* =====================================================================
|
|
1256
|
+
ENTRY SPLASH (#409 fix round 6) — the loading screen is already painted
|
|
1257
|
+
by the static markup at the top of the body; the engine only ever fades it
|
|
1258
|
+
away. entrySplashDismiss hooks the boot REVEAL (reveal() in boot(): the
|
|
1259
|
+
single handover — data landed, world built, app unhidden — NOT
|
|
1260
|
+
DOMContentLoaded). State rides the node's dataset so both functions stay
|
|
1261
|
+
pure w.r.t. the DOM (extract-and-run test convention). Failure seams
|
|
1262
|
+
swap the copy line honestly and NEVER loop silently: total data failure
|
|
1263
|
+
holds the error line readable >= ENTRY_ERROR_HOLD before fading (even if
|
|
1264
|
+
the reveal fires earlier — and on its own if the reveal never comes); a
|
|
1265
|
+
boot() throw does the same and clears the booting class; a 30 s deadline
|
|
1266
|
+
(below) flags a server that never answered without dismissing — data
|
|
1267
|
+
may still land, and the reveal then owns the dismissal.
|
|
1268
|
+
===================================================================== */
|
|
1269
|
+
const ENTRY_FAIL_COPY='could not reach the Hicortex server — check that it is running, then reload';
|
|
1270
|
+
const ENTRY_BOOTFAIL_COPY='the dashboard failed to start — reload to retry';
|
|
1271
|
+
const ENTRY_SLOW_COPY='still waiting for the server — it has not answered yet';
|
|
1272
|
+
const ENTRY_ERROR_HOLD=2800; /* ms the failure line stays readable */
|
|
1273
|
+
function entrySplashFail(msg){
|
|
1274
|
+
const s=document.getElementById('entry-load'), l=document.getElementById('entry-load-line');
|
|
1275
|
+
if(!s || !l || s.dataset.gone==='1') return;
|
|
1276
|
+
l.textContent=msg;
|
|
1277
|
+
s.dataset.failed='1'; s.dataset.failAt=String(Date.now());
|
|
1278
|
+
setTimeout(entrySplashDismiss, ENTRY_ERROR_HOLD); /* owns the beat even if the reveal never comes */
|
|
1279
|
+
}
|
|
1280
|
+
function entrySplashDismiss(){
|
|
1281
|
+
const s=document.getElementById('entry-load');
|
|
1282
|
+
if(!s) return;
|
|
1283
|
+
if(s.dataset.failed==='1'){ /* hold the error line readable */
|
|
1284
|
+
const remain=ENTRY_ERROR_HOLD-(Date.now()-Number(s.dataset.failAt||0));
|
|
1285
|
+
if(remain>0){ setTimeout(entrySplashDismiss, remain); return; }
|
|
1286
|
+
}
|
|
1287
|
+
if(s.dataset.gone==='1') return; /* idempotent */
|
|
1288
|
+
s.dataset.gone='1';
|
|
1289
|
+
const instant=document.body.classList.contains('instant') ||
|
|
1290
|
+
(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches);
|
|
1291
|
+
if(instant){ s.remove(); return; } /* #still / reduced motion: no fade */
|
|
1292
|
+
s.classList.add('gone');
|
|
1293
|
+
s.addEventListener('transitionend', function(){ s.remove(); }, {once:true});
|
|
1294
|
+
setTimeout(function(){ s.remove(); }, 900); /* fallback — transitionend is not guaranteed */
|
|
1295
|
+
}
|
|
1296
|
+
/* honest slow-wait flag: boot fetches still unsettled 30 s in → say so on
|
|
1297
|
+
the splash (no dismissal — the reveal still owns that if data lands) */
|
|
1298
|
+
setTimeout(function(){
|
|
1299
|
+
const s=document.getElementById('entry-load'), l=document.getElementById('entry-load-line');
|
|
1300
|
+
if(s && l && s.dataset.gone!=='1' && s.dataset.failed!=='1') l.textContent=ENTRY_SLOW_COPY;
|
|
1301
|
+
}, 30000);
|
|
1302
|
+
|
|
1154
1303
|
/* #still (or ?still): skip the boot reveal — deterministic screenshots/tests. */
|
|
1155
1304
|
const STILL = /(?:#|[?&])still\b/.test(location.hash + ' ' + location.search);
|
|
1156
|
-
if(STILL){ document.body.classList.remove('booting'); document.body.classList.add('instant'); }
|
|
1305
|
+
if(STILL){ document.body.classList.remove('booting'); document.body.classList.add('instant'); entrySplashDismiss(); }
|
|
1157
1306
|
|
|
1158
1307
|
/* =====================================================================
|
|
1159
1308
|
The approved #409 r6-flight engine, ported to live data. One canvas,
|
|
@@ -2904,26 +3053,49 @@ function renderRecBand(ups){
|
|
|
2904
3053
|
}
|
|
2905
3054
|
|
|
2906
3055
|
let MODEL_LABEL='';
|
|
3056
|
+
/* #409 fix round 5 — owner ruling 2026-09-14 (verbatim): "I do NOT want to
|
|
3057
|
+
know what it is compatible with, I want to know the fucking model." The
|
|
3058
|
+
card face shows THE MODEL, nothing else: hero = the namespace-stripped
|
|
3059
|
+
model name (fit-scaled, floor 20px), mono line = the raw config id,
|
|
3060
|
+
caption "detected from config", configure pill. NO protocol line, NO
|
|
3061
|
+
provider line — local and cloud endpoints render identically. (The
|
|
3062
|
+
configure modal keeps its technical fields: that is the operator surface.)
|
|
3063
|
+
The degrade fires ONLY when there is no model AT ALL — neither the config
|
|
3064
|
+
echo (M.model) nor the runtime label (/health/detail) carries one. A
|
|
3065
|
+
configured model with null provider/backend still renders: the daemon may
|
|
3066
|
+
legitimately run no LLM (resolveExplicitLlmConfig requires llmApiKey
|
|
3067
|
+
alongside llmBaseUrl; a keyless local endpoint is recall-only) while the
|
|
3068
|
+
config declares the model — the card keys on the MODEL, not the runtime. */
|
|
2907
3069
|
function renderModelCard(){
|
|
2908
3070
|
/* configure pill only when GET /dashboard/model succeeded (M truthy) —
|
|
2909
3071
|
an old server / load error degrades to the phase-1 label-only card */
|
|
2910
3072
|
el('model-cfg').hidden=!M;
|
|
2911
|
-
|
|
3073
|
+
const hero=el('model-provider');
|
|
3074
|
+
hero.style.fontSize=''; // undo a previous hero fit (below)
|
|
3075
|
+
/* The config echo is the display truth (the RAW config id, never a
|
|
3076
|
+
provider-prefixed composition); the runtime label is only the fallback
|
|
3077
|
+
for pre-#422 servers without /dashboard/model. */
|
|
3078
|
+
const raw=(M&&M.model)||(MODEL_LABEL&&MODEL_LABEL!=='not configured'?MODEL_LABEL:'');
|
|
3079
|
+
if(!raw){
|
|
2912
3080
|
/* #409 fix round — the no-LLM state says so ON THE FACE (a muted hint),
|
|
2913
3081
|
not a bare dash: correct degraded state, self-explanatory rendering */
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
el('model-tail').textContent='';
|
|
3082
|
+
hero.textContent='no model configured';
|
|
3083
|
+
hero.classList.add('noconf');
|
|
2917
3084
|
el('model-id').textContent='';
|
|
2918
3085
|
return;
|
|
2919
3086
|
}
|
|
2920
|
-
|
|
2921
|
-
const
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
3087
|
+
hero.classList.remove('noconf');
|
|
3088
|
+
const ns=raw.lastIndexOf('/');
|
|
3089
|
+
hero.textContent=ns>=0?raw.slice(ns+1):raw; // the model name = hero
|
|
3090
|
+
/* Long ids are the norm (mlx-community/Qwen3.8-27B-4bit et al); the hero
|
|
3091
|
+
NEVER clips — it scales down to the slot (floor 20px) while the full id
|
|
3092
|
+
rides the mono line. One pass is exact: text width is linear in
|
|
3093
|
+
font-size. */
|
|
3094
|
+
if(hero.scrollWidth>hero.clientWidth){
|
|
3095
|
+
const fs=parseFloat(getComputedStyle(hero).fontSize);
|
|
3096
|
+
hero.style.fontSize=Math.max(20,Math.floor(fs*hero.clientWidth/hero.scrollWidth))+'px';
|
|
3097
|
+
}
|
|
3098
|
+
el('model-id').textContent=raw;
|
|
2927
3099
|
}
|
|
2928
3100
|
|
|
2929
3101
|
/* ---------- model settings modal (#422 phase 2) — the configure pill's
|
|
@@ -4194,6 +4366,12 @@ async function boot(){
|
|
|
4194
4366
|
if(hd.status==='fulfilled'&&hd.value&&typeof hd.value.llm==='string') MODEL_LABEL=hd.value.llm;
|
|
4195
4367
|
else if(hd.status==='fulfilled'&&hd.value&&hd.value.model) MODEL_LABEL=String(hd.value.model);
|
|
4196
4368
|
if(hh.status==='fulfilled'&&hh.value&&hh.value.status) { /* health body shape varies; the poll handles the dot */ }
|
|
4369
|
+
/* entry splash (#409 fix round 6): BOTH core sources rejected = total
|
|
4370
|
+
failure — swap the splash line to the honest error (the dismissal holds
|
|
4371
|
+
it readable, then fades into this degraded page, whose own error
|
|
4372
|
+
surfaces — the toast above, the unreachable dot — carry the detail). */
|
|
4373
|
+
if(dd.status==='rejected' && ff.status==='rejected')
|
|
4374
|
+
entrySplashFail(ENTRY_FAIL_COPY);
|
|
4197
4375
|
|
|
4198
4376
|
buildWorld(); // no-op when F failed — the empty field still renders
|
|
4199
4377
|
buildPaths();
|
|
@@ -4219,13 +4397,21 @@ async function boot(){
|
|
|
4219
4397
|
loadAccount();
|
|
4220
4398
|
pollHealth();
|
|
4221
4399
|
|
|
4222
|
-
const reveal=()=>{ booting=false; document.body.classList.remove('booting'); dirty=true; };
|
|
4400
|
+
const reveal=()=>{ booting=false; document.body.classList.remove('booting'); dirty=true; entrySplashDismiss(); };
|
|
4223
4401
|
if(REDUCED||STILL) reveal();
|
|
4224
4402
|
else setTimeout(reveal, 1400);
|
|
4225
4403
|
setTimeout(()=>{ const h=el('hint'); if(h) h.style.opacity='0'; }, 9500);
|
|
4226
4404
|
setTimeout(()=>{ if(!REDUCED&&!STILL){ spawnSignal(true); nextSpawnAt=performance.now()+4200; } }, (REDUCED||STILL)?0:2650);
|
|
4227
4405
|
}
|
|
4228
|
-
boot()
|
|
4406
|
+
boot().catch(err=>{
|
|
4407
|
+
/* a thrown boot must never leave the splash looping: honest line on the
|
|
4408
|
+
splash, the static shell visible after the readable hold, toast per the
|
|
4409
|
+
page's error pattern (the catch mirrors reveal minus the redraw). */
|
|
4410
|
+
try{ console.error('dashboard boot failed', err); }catch(e2){}
|
|
4411
|
+
booting=false; document.body.classList.remove('booting');
|
|
4412
|
+
toast('Boot failed — '+(err && err.message ? err.message : 'unknown error'));
|
|
4413
|
+
entrySplashFail(ENTRY_BOOTFAIL_COPY);
|
|
4414
|
+
});
|
|
4229
4415
|
rafId=requestAnimationFrame(frame);
|
|
4230
4416
|
resize();
|
|
4231
4417
|
draw(performance.now(),16); /* one synchronous paint — the field exists before the first rAF */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gamaze/hicortex",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "Persistent agent identity for AI agents \u2014 a hand-edited identity layer, nightly-distilled experience, and lessons injected every session, shared across your whole fleet. Works with Hermes, OpenClaw, Claude Code, Pi, and opencode.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|