Humuhumu33 commited on
Commit
86610f3
·
verified ·
1 Parent(s): 00d9a00

retire-provenance: superseded Q b/ objects (sha256 CAS, HOLO-LEAN-RESOLVER P1.3)

Browse files
sha256/3e475a984692e4542e13c7efefeff7454e76121873717880da1fef8ab56ddeda ADDED
@@ -0,0 +1,569 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <meta charset="utf-8">
3
+ <meta name="viewport" content="width=device-width,initial-scale=1">
4
+ <title>Bonsai</title>
5
+ <style>
6
+ /* Claude-desktop look: warm charcoal stage, 11% hairlines, 12-14px radii, serif display, coral accent.
7
+ Type floor is 15px everywhere. Everything fits ONE viewport at 1280×800 AND 375×812 (dvh + clamp). */
8
+ :root { --bg:#262624; --ink:#eceae4; --muted:#b8b5ad; --faint:rgba(232,230,224,.45);
9
+ --hair:rgba(255,255,255,.11); --coral:#d97757; --coral-hi:#e8987a; --lav:#c4b5fd; --r:13px;
10
+ --serif:"Tiempos Text","Charter","Iowan Old Style",Georgia,serif; }
11
+ * { box-sizing:border-box; margin:0; padding:0; }
12
+ html,body { height:100%; }
13
+ body { background:var(--bg); color:var(--ink); font:15px/1.55 system-ui,-apple-system,"Segoe UI",Inter,sans-serif;
14
+ -webkit-font-smoothing:antialiased; overflow:hidden; }
15
+ .num { font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; font-size:15px; }
16
+ .stage { position:fixed; inset:0; height:100dvh; display:flex; flex-direction:column; align-items:center;
17
+ justify-content:center; padding:24px; opacity:0; pointer-events:none; transition:opacity .6s ease; }
18
+ .stage.on { opacity:1; pointer-events:auto; }
19
+
20
+ /* ── arrival: full-bleed hologram canvas behind left-aligned copy ── */
21
+ #arrive { flex-direction:row; align-items:center; justify-content:flex-start; padding:0; }
22
+ #holo { position:absolute; inset:0; width:100%; height:100%; display:block; }
23
+ #arrive .copy { position:relative; z-index:1; max-width:620px; padding:0 clamp(24px,6vw,96px); }
24
+ #arrive h1 { font-family:var(--serif); font-weight:500; font-size:clamp(40px,7vw,84px); line-height:1.06;
25
+ letter-spacing:-.015em; text-wrap:balance; }
26
+ #arrive .sub { margin-top:22px; font-size:clamp(18px,1.6vw,20px); line-height:1.5; color:var(--muted); max-width:46ch; }
27
+ #arrive button { margin-top:34px; background:var(--coral); border:none; color:#faf9f5; border-radius:999px;
28
+ padding:16px 34px; font:500 17px/1 system-ui,sans-serif; cursor:pointer;
29
+ transition:transform .2s ease, filter .2s ease; }
30
+ #arrive button:hover { filter:brightness(1.08); transform:translateY(-1px); }
31
+ #arrive button:disabled { filter:saturate(.3) brightness(.8); cursor:default; transform:none; }
32
+ #arrive .fine { margin-top:16px; font-size:15px; color:var(--faint); }
33
+ @media (max-width:760px) { /* stacked: hologram floats in the upper half, copy anchors low */
34
+ #arrive { align-items:flex-end; }
35
+ #arrive .copy { padding-bottom:max(44px, env(safe-area-inset-bottom, 0px) + 36px); }
36
+ }
37
+
38
+ /* ── chat: the Claude-desktop room — serif greeting, ONE centered input card, quiet metrics ── */
39
+ #chat { justify-content:center; padding:0; }
40
+ #chat .col { display:flex; flex-direction:column; width:min(100%,760px); height:100%; padding:0 22px; justify-content:center; }
41
+ #chat .col.started { justify-content:flex-start; }
42
+ #hello { text-align:center; margin-bottom:26px; }
43
+ #hello .mark { display:inline-block; width:30px; height:30px; vertical-align:-4px; margin-right:12px; color:var(--coral); }
44
+ #hello h2 { display:inline; font-family:var(--serif); font-weight:500; font-size:clamp(30px,4.4vw,44px); letter-spacing:-.01em; }
45
+ .col.started #hello { display:none; }
46
+ #log { display:none; }
47
+ .col.started #log { display:block; flex:1; overflow-y:auto; padding:30px 4px 16px;
48
+ scrollbar-width:none; -ms-overflow-style:none; } /* the content scrolls; no chrome */
49
+ #log::-webkit-scrollbar { width:0; height:0; }
50
+ .msg { margin:0 0 22px; }
51
+ .msg .who { font-size:15px; color:var(--faint); margin-bottom:4px; }
52
+ .msg.user { display:flex; justify-content:flex-end; } /* Grok/Claude: user turn is a right bubble */
53
+ .msg.user .who { display:none; }
54
+ .msg.user .body { background:rgba(255,255,255,.05); border:1px solid var(--hair); border-radius:16px 16px 4px 16px;
55
+ padding:11px 16px; max-width:78%; }
56
+ .msg .body { white-space:pre-wrap; font-size:16px; line-height:1.65; }
57
+ /* "Thought for Ns" — a quiet collapsible reasoning header (Grok's signature, in Claude's palette) */
58
+ details.think { margin:0 0 10px; }
59
+ details.think summary { list-style:none; cursor:pointer; font-size:15px; color:var(--faint);
60
+ display:inline-flex; align-items:center; gap:7px; user-select:none; }
61
+ details.think summary::-webkit-details-marker { display:none; }
62
+ details.think summary .chev { transition:transform .2s ease; font-size:11px; }
63
+ details.think[open] summary .chev { transform:rotate(90deg); }
64
+ details.think .t { color:var(--faint); font-size:15px; line-height:1.6; white-space:pre-wrap; margin-top:8px;
65
+ border-left:2px solid var(--hair); padding-left:12px; }
66
+ /* assistant action row: copy · regenerate (appears when the answer settles) */
67
+ .acts { display:flex; gap:4px; margin-top:8px; opacity:0; transition:opacity .25s ease; }
68
+ .msg:hover .acts, .acts.shown { opacity:1; }
69
+ .acts button { background:none; border:none; color:var(--faint); cursor:pointer; border-radius:8px;
70
+ width:30px; height:30px; display:grid; place-items:center; transition:background .15s, color .15s; }
71
+ .acts button:hover { background:rgba(255,255,255,.06); color:var(--ink); }
72
+ .acts button svg { width:16px; height:16px; stroke:currentColor; fill:none; stroke-width:1.7; }
73
+ .acts .ok { color:var(--coral-hi); }
74
+ /* follow-up suggestions: Grok's placement — borderless rows stacked in the conversation flow */
75
+ .fu { display:flex; flex-direction:column; margin:14px 0 4px; }
76
+ .fu button { display:flex; align-items:center; gap:12px; background:none; border:none; border-top:1px solid transparent;
77
+ color:var(--muted); font-size:16px; text-align:left; padding:11px 6px; cursor:pointer;
78
+ border-radius:10px; transition:background .15s, color .15s; }
79
+ .fu button:hover { background:rgba(255,255,255,.045); color:var(--ink); }
80
+ .fu .arr { color:var(--faint); font-size:15px; flex:none; }
81
+ .note { text-align:center; font-size:15px; color:var(--faint); margin:6px 0 12px; }
82
+ #card { background:rgba(255,255,255,.03); border:1px solid var(--hair); border-radius:18px; padding:6px 8px 8px; }
83
+ #card:focus-within { border-color:rgba(217,119,87,.5); }
84
+ #inp { width:100%; background:none; border:none; color:var(--ink); padding:14px 12px 10px;
85
+ font:16px/1.45 system-ui,sans-serif; outline:none; }
86
+ #cardrow { display:flex; align-items:center; gap:12px; padding:0 8px 4px 12px; }
87
+ #modelname { font-size:15px; color:var(--muted); }
88
+ #cardrow .grow { flex:1; }
89
+ #send { background:var(--coral); border:none; color:#faf9f5; border-radius:999px; width:38px; height:38px;
90
+ font:500 17px/1 system-ui,sans-serif; cursor:pointer; transition:filter .2s ease; }
91
+ #send:hover { filter:brightness(1.08); }
92
+ #send:disabled { background:rgba(255,255,255,.06); color:var(--faint); cursor:default; }
93
+ #metrics { text-align:center; font-size:15px; color:var(--faint); padding:12px 0 16px; min-height:22px; }
94
+ #metrics b { color:var(--muted); font-weight:500; }
95
+ #err { color:#f87171; font-size:15px; padding:0 0 18px; text-align:center; white-space:pre-wrap; }
96
+ .spn { display:inline-block; width:1.1ch; font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
97
+ color:var(--coral-hi); margin-right:7px; }
98
+ /* CSS hologram (fallback when WebGL is unavailable/blocked): layered orb + scanlines + drift */
99
+ #holofb { position:absolute; width:min(46vw,520px); aspect-ratio:1; right:4vw; top:50%;
100
+ transform:translateY(-50%); border-radius:50%; pointer-events:none;
101
+ background:radial-gradient(circle at 38% 34%, rgba(232,152,122,.55), rgba(217,119,87,.28) 42%, rgba(196,181,253,.16) 72%, transparent 76%);
102
+ box-shadow:0 0 90px rgba(217,119,87,.25), inset 0 0 60px rgba(196,181,253,.18);
103
+ animation:holofloat 7s ease-in-out infinite; }
104
+ #holofb::before { content:""; position:absolute; inset:0; border-radius:50%;
105
+ background:repeating-linear-gradient(to bottom, rgba(255,255,255,.09) 0 2px, transparent 2px 7px);
106
+ mix-blend-mode:screen; animation:holoscan 2.6s linear infinite;
107
+ -webkit-mask:radial-gradient(circle, #000 70%, transparent 74%); mask:radial-gradient(circle, #000 70%, transparent 74%); }
108
+ #holofb::after { content:""; position:absolute; inset:-2%; border-radius:50%;
109
+ border:1px solid rgba(196,181,253,.30); filter:blur(.4px); }
110
+ @keyframes holofloat { 50% { transform:translateY(calc(-50% - 14px)); } }
111
+ @keyframes holoscan { to { background-position:0 7px; } }
112
+ @media (max-width:760px){ #holofb { right:50%; top:26%; transform:translate(50%,-50%); width:min(62vw,300px); }
113
+ @keyframes holofloat { 50% { transform:translate(50%,calc(-50% - 10px)); } } }
114
+ @media (prefers-reduced-motion: reduce){ #holofb, #holofb::before { animation:none; } }
115
+ </style>
116
+ <body>
117
+
118
+ <div class="stage on" id="arrive">
119
+ <canvas id="holo"></canvas>
120
+ <div id="holofb" hidden></div>
121
+ <div class="copy">
122
+ <h1>An AI<br>that&rsquo;s yours.</h1>
123
+ <p class="sub">Bonsai streams into your browser like a film. Every piece is checked before it runs. After one visit it answers on your device, even offline.</p>
124
+ <button id="go">Stream the mind</button>
125
+ <div class="fine" id="fine">Free &middot; open weights by Prism ML &middot; nothing leaves your device</div>
126
+ </div>
127
+ </div>
128
+
129
+ <div class="stage" id="chat">
130
+ <div class="col" id="col">
131
+ <div id="hello">
132
+ <svg class="mark" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" aria-hidden="true"><path d="M12 3v18M3 12h18M5.6 5.6l12.8 12.8M18.4 5.6L5.6 18.4"/></svg><h2>Ready when you are.</h2>
133
+ </div>
134
+ <div id="log"></div>
135
+ <div id="card">
136
+ <input id="inp" placeholder="Ask Bonsai anything" autocomplete="off">
137
+ <div id="cardrow">
138
+ <span id="modelname">Bonsai</span>
139
+ <span class="grow"></span>
140
+ <button id="send" aria-label="Send">&uarr;</button>
141
+ </div>
142
+ </div>
143
+ <div id="metrics">on your device</div>
144
+ <div id="err" hidden></div>
145
+ </div>
146
+ </div>
147
+
148
+ <script type="module">
149
+ // ── the holographic centerpiece ─────────────────────────────────────────────────────────────
150
+ // Fresnel rim + animated scan stripes + occasional slice glitch, additive-blended over the warm
151
+ // charcoal. In the spirit of ektogamat/threejs-vanilla-holographic-material (MIT) — this GLSL is
152
+ // written fresh for this page. three.js is VENDORED (no CDN) and imported AFTER first paint so
153
+ // the hero text never waits on it. On "Stream the mind" the loop stops and the GL context is
154
+ // released (dispose + lose-context) — the model engine gets the whole GPU.
155
+ const cv = document.getElementById("holo");
156
+ const reduced = matchMedia("(prefers-reduced-motion: reduce)").matches;
157
+ window.__holoOff = null;
158
+ try {
159
+ // hero paints first: two rAFs when the tab is visible, 250ms fallback when rAF is starved
160
+ // (hidden/embedded tabs never fire rAF — the hologram must still initialize for their return).
161
+ await new Promise((r) => { requestAnimationFrame(() => requestAnimationFrame(r)); setTimeout(r, 250); });
162
+ if (!document.getElementById("arrive").classList.contains("on")) throw new Error("past landing");
163
+ const THREE = await import("./vendor/three.module.min.js");
164
+ const renderer = new THREE.WebGLRenderer({ canvas: cv, antialias: true, alpha: true, powerPreference: "high-performance" });
165
+ if (!renderer.getContext() || renderer.getContext().isContextLost()) throw new Error("webgl unavailable");
166
+ renderer.setPixelRatio(Math.min(devicePixelRatio || 1, 2));
167
+ const scene = new THREE.Scene();
168
+ const cam = new THREE.PerspectiveCamera(45, 1, 0.1, 30); cam.position.z = 5;
169
+ // the OS site's sphere-torus, rebuilt from stock primitives: ~700 instanced spheres laid on a
170
+ // torus lattice, lit by two lights — glossy beads, ONE draw call (fast on any phone). Claude tint.
171
+ const wire = new THREE.MeshStandardMaterial({ color: new THREE.Color("#3a3330"), roughness: 0.25, metalness: 0.55,
172
+ emissive: new THREE.Color("#d97757"), emissiveIntensity: 0.06 });
173
+ const wire2 = wire; // dispose handle below
174
+ const mat = wire;
175
+ scene.add(new THREE.AmbientLight(0xffffff, 0.35));
176
+ const key = new THREE.DirectionalLight(0xffd9c4, 1.5); key.position.set(2.5, 3, 4); scene.add(key);
177
+ const rim = new THREE.DirectionalLight(0xc4b5fd, 0.9); rim.position.set(-3, -1.5, -2); scene.add(rim);
178
+ const SEG_U = 36, SEG_V = 19, R = 1.22, r = 0.56, BEAD = 0.088; // the bead lattice
179
+ const geo = new THREE.SphereGeometry(BEAD, 12, 10);
180
+ const mesh = new THREE.InstancedMesh(geo, wire, SEG_U * SEG_V);
181
+ const m4 = new THREE.Matrix4();
182
+ let bi = 0;
183
+ for (let iu = 0; iu < SEG_U; iu++) for (let iv = 0; iv < SEG_V; iv++) {
184
+ const u = iu / SEG_U * Math.PI * 2, v = (iv / SEG_V + (iu % 2) * 0.5 / SEG_V) * Math.PI * 2; // staggered rows: the woven look
185
+ m4.setPosition((R + r * Math.cos(v)) * Math.cos(u), (R + r * Math.cos(v)) * Math.sin(u), r * Math.sin(v));
186
+ mesh.setMatrixAt(bi++, m4);
187
+ }
188
+ const core = mesh; // dispose handle
189
+ mesh.rotation.x = 0.9; // the OS-site tilt
190
+ scene.add(mesh);
191
+ addEventListener("pointerdown", () => { tPulse = performance.now(); }, { passive: true }); // click: the hologram answers back
192
+ // pointer parallax: mesh tilts toward the cursor (eased); stripes speed up when the cursor is near.
193
+ let tx = 0, ty = 0, rx = 0, ry = 0, boost = 0, tBoost = 0, tPulse = -9e9;
194
+ if (!reduced) addEventListener("pointermove", (e) => {
195
+ const nx = e.clientX / innerWidth * 2 - 1, ny = e.clientY / innerHeight * 2 - 1;
196
+ tx = nx * 0.45; ty = ny * 0.35;
197
+ const c = mesh.position.clone().project(cam); // mesh center in NDC
198
+ tBoost = Math.hypot(nx - c.x, ny - c.y) < 0.55 ? 1 : 0;
199
+ }, { passive: true });
200
+ const size = () => { // right-of-center desktop · upper-half mobile
201
+ renderer.setSize(innerWidth, innerHeight, false);
202
+ cam.aspect = innerWidth / innerHeight; cam.updateProjectionMatrix();
203
+ const hw = Math.tan(cam.fov * Math.PI / 360) * cam.position.z; // half-height of the frustum at z=0
204
+ if (cam.aspect > 0.92) { mesh.position.set(hw * cam.aspect * 0.48, 0, 0); mesh.scale.setScalar(1); }
205
+ else { mesh.position.set(0, hw * 0.42, 0); mesh.scale.setScalar(0.78); }
206
+ };
207
+ size(); addEventListener("resize", size);
208
+ let raf = 0, dead = false;
209
+ const loop = () => {
210
+ const t = performance.now() / 1000;
211
+ boost += (tBoost - boost) * 0.06;
212
+ const pulse = Math.max(0, 1 - (performance.now() - tPulse) / 700); // click pulse decays over 0.7s
213
+ wire.emissiveIntensity = 0.05 + Math.sin(t * 1.3) * 0.02 + boost * 0.12 + pulse * 0.35; // the beads glow to attention
214
+ rx += (ty - rx) * 0.05; ry += (tx - ry) * 0.05; // eased parallax
215
+ mesh.rotation.x = 0.9 + rx * 0.35; mesh.rotation.z = t * 0.09 + ry * 0.5; // slow ring spin + tilt toward the cursor
216
+ renderer.render(scene, cam);
217
+ raf = requestAnimationFrame(loop);
218
+ };
219
+ document.addEventListener("visibilitychange", () => {
220
+ if (dead) return;
221
+ if (document.hidden) cancelAnimationFrame(raf); else raf = requestAnimationFrame(loop);
222
+ });
223
+ loop();
224
+ setTimeout(() => {
225
+ try { if (!dead && cv.toDataURL("image/png").length < 4000) { window.__holoOff && window.__holoOff(); const fb = document.getElementById("holofb"); if (fb) fb.hidden = false; } } catch {}
226
+ }, 900);
227
+ // verification hook: force one frame at time t and return it as a PNG (rAF is starved in
228
+ // hidden/embedded panes — this is how an agent proves the hologram actually draws pixels).
229
+ window.__holoFrame = (t) => { renderer.render(scene, cam); return cv.toDataURL("image/png"); };
230
+ window.__holoOff = () => { // MANDATORY before the engine loads
231
+ dead = true; cancelAnimationFrame(raf); window.__holoFrame = null;
232
+ mesh.geometry.dispose(); wire.dispose(); renderer.dispose();
233
+ try { renderer.getContext().getExtension("WEBGL_lose_context").loseContext(); } catch {}
234
+ cv.remove();
235
+ };
236
+ } catch (e) { // no WebGL (Brave shields, GPU-less) / past landing
237
+ if (document.getElementById("arrive").classList.contains("on")) {
238
+ try { cv.remove(); } catch {}
239
+ const fb = document.getElementById("holofb"); if (fb) fb.hidden = false; // the CSS hologram stands in
240
+ }
241
+ }
242
+ </script>
243
+
244
+ <script type="module">
245
+ import init, { qvac_load_gpu, qvac_gpu_free } from "../q/pkg/holospaces_web.js";
246
+ import { loadKappaObject } from "../q/holo-load2bit.mjs";
247
+ import { createQvacGPU } from "../q/qvac-gpu.js?v=68";
248
+ import { createEngine } from "../q/core/engine.js";
249
+
250
+ const $ = (id) => document.getElementById(id);
251
+ // braille pulse: one 80ms ticker animates every [data-spin] slot (loading, waking, thinking) —
252
+ // a single interval, zero layout work, stops mattering the instant the last spinner is removed.
253
+ const SPIN = ["⠋","⠙","⠹","⠸","⠼","⠴","⠦","⠧","⠇","⠏"];
254
+ let _sf = 0;
255
+ setInterval(() => { const els = document.querySelectorAll("[data-spin]"); if (!els.length) return; const f = SPIN[_sf = (_sf + 1) % SPIN.length]; for (const el of els) el.textContent = f; }, 80);
256
+ const spn = () => `<span class="spn" data-spin>${SPIN[0]}</span>`;
257
+ const show = (id) => { for (const s of document.querySelectorAll(".stage")) s.classList.toggle("on", s.id === id); };
258
+ const short = (k) => String(k).replace(/^(did:holo:)?sha256:/, "").slice(0, 12) + "…";
259
+
260
+ // model source: ?src=<κ-object dir> for dev, HF mirror by default; ?m=27b flips to the 27B
261
+ // κ-object (engine support for its hybrid layers lands separately — the guard below is honest).
262
+ // Everything shown to the user (name, size) is READ FROM THE MANIFEST — swapping the κ URL is a
263
+ // zero-code model change. The manifest pin is the Law-L5 anchor.
264
+ const QS = new URLSearchParams(location.search);
265
+ const SRC = QS.get("src");
266
+ const DEFAULT_27B = false; // flips true the moment the hybrid engine passes parity (κ-object already published)
267
+ const M27 = QS.get("m") === "27b" || (DEFAULT_27B && QS.get("m") !== "8b");
268
+ const BASE = SRC || (M27 ? "https://huggingface.co/HOLOGRAMTECH/q-bonsai-27b/resolve/main" : "https://huggingface.co/HOLOGRAMTECH/q-bonsai-8b/resolve/main");
269
+ const PIN = M27 ? "did:holo:sha256:00beea03509a79f6567879f019c04bb15dda8721f6320f8295d9ea1dd9ba54af" : "did:holo:sha256:a0dc81f26ec5ce98b28ee9c1fab620e91c20720f777e1de6bc416317f54d27e2";
270
+ const CTX = 2048; // room for the reasoning trace + the answer on open-ended questions
271
+
272
+ let engine = null, gpu = null, rootK = "", warm = false, mind = [], thinkOpen = -1, thinkClose = -1;
273
+ let modelName = "Bonsai"; // set from the manifest once loaded
274
+ let story = [], memoryNote = ""; // the WHOLE conversation + the folded memory of its older part
275
+ const STORE = "bonsai.story.v1";
276
+ function persistStory() {
277
+ try { localStorage.setItem(STORE, JSON.stringify({ story: story.slice(-200), memoryNote })); } catch {}
278
+ }
279
+ // fold everything so far into a compact memory the model writes for itself (bounded, abortable-free:
280
+ // runs inside send() before the next turn, with the braille pulse narrating it)
281
+ async function foldMemory() {
282
+ const recent = story.slice(-8).map((t) => `Q: ${t.q}
283
+ A: ${t.a.slice(0, 300)}`).join("\n");
284
+ const prior = memoryNote ? "Earlier memory: " + memoryNote + "\n\n" : "";
285
+ const ask = prior + "Recent conversation:\n" + recent + "\n\nFold ALL of the above into one memory note under 130 words. Keep every name, number, decision and open question. Write only the note.";
286
+ const ids = engine.tokenize(engine.frameTurn(ask, false));
287
+ const res = await engine.generate(ids, { maxNew: 300 });
288
+ const seg = splitThink(res.outIds || []);
289
+ const text = (seg.ans.length ? engine.detokenize(seg.ans) : res.text || "").trim();
290
+ return text.slice(0, 1200);
291
+ }
292
+
293
+ let entered = false, streamStarted = false;
294
+ async function stream() {
295
+ try { if (window.__holoOff) { window.__holoOff(); window.__holoOff = null; } } catch {} // hand the GPU to the engine
296
+ if (entered) { show("chat"); $("inp").focus(); } // welcome stays until the visitor chooses to enter
297
+ const t0 = performance.now();
298
+ const phase = (s) => { $("metrics").innerHTML = s; };
299
+ try {
300
+ if (!navigator.gpu) throw new Error("This browser has no WebGPU. Chrome or Edge 113+ required.");
301
+ // warm-load detect (honest offline copy): the κ-cache already holds the manifest → this visit is disk-speed.
302
+ try { const c = await caches.open("holo-kappa-v1"); warm = !!(await c.match(BASE + "/manifest.json")); } catch {}
303
+ phase(spn() + (warm ? "waking up · from your disk" : "streaming the mind · every piece verified on arrival"));
304
+ await init();
305
+ // PARALLEL κ-BLOCKS FIRST (the measured Netflix path on CDN origins): 399 content-addressed
306
+ // blocks race over 12 connections (HF cdn-lfs caps a single stream ~2 MB/s; the parallel parts
307
+ // path measured 6.8 MB/s on the same line). The ONE-file .holo stays as the fallback for
308
+ // origins that only host the wafer (and its OPFS warm store makes revisits 0-network either way).
309
+ let r, pinned = true, holo = false;
310
+ try {
311
+ r = await loadKappaObject(BASE, { expectKappa: PIN, blake3: false });
312
+ } catch (e1) {
313
+ try {
314
+ const hs = await import("../q/core/holo-stream-load.mjs");
315
+ r = await hs.openHoloKappa(BASE + "/q-bonsai-8b.v1.holo", {});
316
+ holo = true; warm = !!r.warm;
317
+ } catch (e) { if (!SRC) throw e; pinned = false; r = await loadKappaObject(BASE, { allowUnpinned: true, blake3: false }); } // dev fallback only under ?src
318
+ }
319
+ rootK = (r.info && r.info.root) || PIN;
320
+ modelName = String((r.info && r.info.model) || "Bonsai").replace(/-/g, " ");
321
+ $("modelname").textContent = modelName;
322
+ // HONESTY GATE: the hybrid (27B) engine ships separately — never pretend to run what we can't.
323
+ if (r.manifest.hybrid) throw new Error(modelName + " uses a hybrid engine this build does not include yet. It is arriving; the 8B runs today.");
324
+ // progress = ONE quiet line under the input (per-tensor counters; no grid, no takeover)
325
+ const total = (r.manifest.tensors || []).length;
326
+ let done = 0, bytes = 0;
327
+ const tick = () => {
328
+ const dt = (performance.now() - t0) / 1000;
329
+ if (warm) phase(`${spn()}waking up · ${Math.round(done / Math.max(total, 1) * 100)}%`);
330
+ else phase(`${spn()}streaming the mind · <b>${(bytes / 1e6).toFixed(0)} MB</b> · ${(bytes / 1e6 / Math.max(dt, .1)).toFixed(1)} MB/s · every piece verified on arrival`);
331
+ };
332
+ const iv = setInterval(tick, 400);
333
+ const fetchT = async (name) => { // wrap fetchTensor: the progress driver
334
+ const b = await r.fetchTensor(name);
335
+ bytes += b.byteLength; done++;
336
+ return b;
337
+ };
338
+ // tokenizer header (arch + vocab) — embedded in the .holo (0 extra fetches), else same κ-object dir.
339
+ const hdr = r.tokenizerBytes || new Uint8Array(await (await fetch(BASE + "/tokenizer.gguf")).arrayBuffer());
340
+ const lr = JSON.parse(qvac_load_gpu(hdr));
341
+ if (lr.error) throw new Error("tokenizer: " + lr.error);
342
+ try { qvac_gpu_free(); } catch {}
343
+ r.manifest.kv4 = false; // exact f32 KV (the proven first-light config)
344
+ gpu = await createQvacGPU(r.manifest, fetchT, CTX, lr.eos ?? 2, false);
345
+ clearInterval(iv);
346
+ phase(spn() + "waking the mind");
347
+ const m = { fam: "Bonsai", name: modelName, size: "1.16 GB", fmt: "q1 1-bit κ", cap: 200, ctx: CTX, qwen: true, chat: true, rep: 1.05, gpu: true, gpuOnly: true };
348
+ engine = await createEngine(m, { gpu, info: lr, imageKappa: r.info.root });
349
+ // Qwen3 thinking markers are atomic tokens and clean() strips them from text — split by token id instead.
350
+ const o = engine.tokenize("<think>"), c2 = engine.tokenize("</think>");
351
+ thinkOpen = o.length === 1 ? o[0] : -1; thinkClose = c2.length === 1 ? c2[0] : -1;
352
+ enterChat();
353
+ } catch (e) {
354
+ $("metrics").textContent = ""; $("err").hidden = false;
355
+ $("err").textContent = String(e && e.message || e);
356
+ console.error(e);
357
+ }
358
+ }
359
+
360
+ // the ONLY chat metrics, one quiet line: model · time to first word · tok/s (operator spec)
361
+ let lastTTFT = null, lastTokps = null;
362
+ function metrics(state) {
363
+ const bits = [`<b>${modelName}</b>`];
364
+ if (lastTTFT != null) bits.push(`${lastTTFT.toFixed(1)}s to first word`);
365
+ if (lastTokps != null) bits.push(`<b>${lastTokps.toFixed(0)}</b> tokens a second`);
366
+ if (!lastTTFT && !lastTokps) bits.push(state || (warm ? "from your disk · works offline" : "on your device"));
367
+ $("metrics").innerHTML = bits.join(" &nbsp;&middot;&nbsp; ");
368
+ }
369
+ let pendingQ = null, restored = false;
370
+ function restoreStory() {
371
+ if (restored) return; restored = true;
372
+ try {
373
+ const saved = JSON.parse(localStorage.getItem(STORE) || "null");
374
+ if (!saved || !saved.story || !saved.story.length) return;
375
+ story = saved.story; memoryNote = saved.memoryNote || "";
376
+ $("col").classList.add("started");
377
+ for (const t of story.slice(-12)) { // the room re-fills; the model remembers ALL of it via the fold
378
+ msg("You", "user").body.textContent = t.q;
379
+ msg(modelName, "bot").body.textContent = t.a;
380
+ }
381
+ if (!memoryNote && story.length) memoryNote = story.slice(-6).map((t) => `${t.q} → ${t.a.slice(0, 120)}`).join("; ").slice(0, 1200);
382
+ note("picked up where you left off");
383
+ log.scrollTop = log.scrollHeight;
384
+ } catch {}
385
+ }
386
+ function enterChat() {
387
+ ready = true; $("send").disabled = false; restoreStory(); metrics();
388
+ if (pendingQ) { $("inp").value = pendingQ; pendingQ = null; send(); }
389
+ else if (entered) $("inp").focus();
390
+ }
391
+
392
+ const log = $("log");
393
+ function msg(who, cls) {
394
+ const d = document.createElement("div"); d.className = "msg " + cls;
395
+ d.innerHTML = `<div class="who">${who}</div>`;
396
+ const think = document.createElement("details"); think.className = "think"; think.hidden = true;
397
+ think.innerHTML = `<summary><span class="chev">▸</span><span class="sum">thinking…</span></summary><div class="t"></div>`;
398
+ const body = document.createElement("div"); body.className = "body";
399
+ d.appendChild(think); d.appendChild(body);
400
+ let acts = null;
401
+ if (cls === "bot") { acts = document.createElement("div"); acts.className = "acts"; d.appendChild(acts); }
402
+ log.appendChild(d);
403
+ log.scrollTop = log.scrollHeight;
404
+ return { d, body, think, acts, t: think.querySelector(".t") };
405
+ }
406
+ const ICON = {
407
+ copy: '<svg viewBox="0 0 24 24"><rect x="9" y="9" width="11" height="11" rx="2"/><path d="M5 15V5a2 2 0 0 1 2-2h10"/></svg>',
408
+ ok: '<svg viewBox="0 0 24 24"><path d="M5 13l4 4L19 7"/></svg>',
409
+ regen:'<svg viewBox="0 0 24 24"><path d="M21 12a9 9 0 1 1-3-6.7"/><path d="M21 3v5h-5"/></svg>',
410
+ };
411
+ function iconBtn(html, title, fn) { const b = document.createElement("button"); b.type = "button"; b.title = title; b.innerHTML = html; b.onclick = fn; return b; }
412
+ // action row: copy the answer + regenerate the last turn (Grok's row, Claude's restraint)
413
+ function fitActions(ui, text, regen) {
414
+ if (!ui.acts) return; ui.acts.innerHTML = "";
415
+ const copy = iconBtn(ICON.copy, "Copy", async () => {
416
+ try { await navigator.clipboard.writeText(text); copy.classList.add("ok"); copy.innerHTML = ICON.ok;
417
+ setTimeout(() => { copy.classList.remove("ok"); copy.innerHTML = ICON.copy; }, 1200); } catch {}
418
+ });
419
+ ui.acts.appendChild(copy);
420
+ if (regen) ui.acts.appendChild(iconBtn(ICON.regen, "Regenerate", regen));
421
+ ui.acts.classList.add("shown");
422
+ }
423
+ // follow-ups AUTHORED BY THE MODEL about the actual exchange (Grok's behavior, done locally):
424
+ // one bounded extra generation right after the answer; killed instantly if the user starts typing.
425
+ let sugCtl = null;
426
+ async function offerFollowups(lastQ, lastA, host) {
427
+ try { if (sugCtl) sugCtl.abort(); } catch {}
428
+ sugCtl = new AbortController();
429
+ const ask = `Here is a conversation:
430
+ Question: ${lastQ}
431
+ Answer: ${lastA.slice(0, 600)}
432
+
433
+ Write exactly two short, sharp follow-up questions a curious person would ask next. One per line. No numbering, no preamble.`;
434
+ try {
435
+ const ids = engine.tokenize(engine.frameTurn(ask, false));
436
+ const res = await engine.generate(ids, { maxNew: 180, signal: sugCtl.signal });
437
+ const seg = splitThink(res.outIds || []);
438
+ const text = seg.ans.length ? engine.detokenize(seg.ans) : (res.text || "");
439
+ const qs = text.split(/\n+/).map((l) => l.replace(/^[-*\d.\s]+/, "").trim()).filter((l) => l.length > 8 && l.length < 140).slice(0, 2);
440
+ if (!qs.length || busy) return;
441
+ const box = document.createElement("div"); box.className = "fu";
442
+ for (const label of qs) {
443
+ const b = document.createElement("button"); b.type = "button";
444
+ b.innerHTML = `<span class="arr">↳</span>${label.replace(/[<>&]/g, "")}`;
445
+ b.onclick = () => { box.remove(); $("inp").value = label; send(); };
446
+ box.appendChild(b);
447
+ }
448
+ host.appendChild(box);
449
+ log.scrollTop = log.scrollHeight;
450
+ } catch {} // aborted / out of room → no suggestions, no noise
451
+ }
452
+ function note(s) { const d = document.createElement("div"); d.className = "note"; d.textContent = s; log.appendChild(d); }
453
+
454
+ // split streamed outIds into thinking + answer segments by the <think> token ids (robust: no
455
+ // tags at position 0 → everything is answer; unclosed → everything so far is thinking).
456
+ function splitThink(outIds) {
457
+ if (thinkOpen < 0 || outIds[0] !== thinkOpen) return { think: [], ans: outIds };
458
+ const c = outIds.indexOf(thinkClose);
459
+ if (c < 0) return { think: outIds.slice(1), ans: [] };
460
+ return { think: outIds.slice(1, c), ans: outIds.slice(c + 1) };
461
+ }
462
+
463
+ let busy = false, ready = false;
464
+ async function send() {
465
+ const q = $("inp").value.trim();
466
+ if (!q || busy) return;
467
+ if (!ready || !engine) { // the mind is still arriving: hold ONE question, it sends itself
468
+ pendingQ = q; $("inp").value = "";
469
+ $("col").classList.add("started");
470
+ msg("You", "user").body.textContent = q;
471
+ note("your question will send itself the moment the mind arrives");
472
+ return;
473
+ }
474
+ busy = true; $("inp").value = ""; $("send").disabled = true;
475
+ $("col").classList.add("started"); // the greeting yields to the conversation (Claude-room transition)
476
+ msg("You", "user").body.textContent = q;
477
+ let turn = engine.tokenize(engine.frameTurn(q, mind.length > 0));
478
+ if (mind.length + turn.length + 384 > CTX) { // the window fills: the mind FOLDS its history, never drops it
479
+ $("metrics").innerHTML = spn() + "folding memory · nothing is forgotten";
480
+ try { memoryNote = await foldMemory(); persistStory(); } catch {}
481
+ mind = [];
482
+ }
483
+ let qEff = q;
484
+ if (!mind.length && memoryNote) qEff = `(What you remember of our conversation so far: ${memoryNote})
485
+
486
+ ${q}`;
487
+ if (!mind.length) turn = engine.tokenize(engine.frameTurn(qEff, false));
488
+ const ids = mind.concat(turn);
489
+ try { if (sugCtl) sugCtl.abort(); } catch {}
490
+ document.querySelectorAll(".fu").forEach((n) => n.remove());
491
+ const mindBefore = mind.slice();
492
+ const ui = msg(modelName, "bot"); ui.body.innerHTML = spn();
493
+ ui.think.querySelector("summary").innerHTML = spn() + `<span class="sum">thinking</span>`;
494
+ const tSend = performance.now(); let ttftSet = false, thoughtMs = 0;
495
+ lastTTFT = null; lastTokps = null;
496
+ $("metrics").innerHTML = spn() + `<b>${modelName}</b> is thinking`;
497
+ try {
498
+ const res = await engine.generate(ids, {
499
+ maxNew: CTX - ids.length - 8, // the whole remaining KV window (eos stops early)
500
+ onToken: (t) => {
501
+ if (!ttftSet) { ttftSet = true; lastTTFT = (performance.now() - tSend) / 1000; } // TTFT = send → first visible token
502
+ if (t.stats && t.stats.tokps) lastTokps = t.stats.tokps;
503
+ metrics();
504
+ const s = splitThink(t.outIds);
505
+ if (s.think.length) { ui.think.hidden = false; ui.t.textContent = engine.detokenize(s.think); }
506
+ if (s.ans.length && !thoughtMs) { thoughtMs = performance.now() - tSend; // answer flowing: freeze "Thought for Ns"
507
+ const secs = Math.max(1, Math.round(thoughtMs / 1000));
508
+ ui.think.querySelector("summary").innerHTML = `<span class="chev">▸</span><span class="sum">Thought for ${secs}s</span>`; }
509
+ ui.body.textContent = s.ans.length ? engine.detokenize(s.ans) : (s.think.length ? "" : t.text);
510
+ log.scrollTop = log.scrollHeight;
511
+ },
512
+ });
513
+ const s = splitThink(res.outIds || []);
514
+ const secs = Math.max(1, Math.round((thoughtMs || (performance.now() - tSend)) / 1000));
515
+ ui.think.querySelector("summary").innerHTML = `<span class="chev">▸</span><span class="sum">Thought for ${secs}s</span>`;
516
+ if (s.think.length) { ui.think.hidden = false; ui.t.textContent = engine.detokenize(s.think); }
517
+ if (s.ans.length) {
518
+ const answer = engine.detokenize(s.ans);
519
+ ui.body.textContent = answer;
520
+ mind = ids.concat(s.ans); // history carries the ANSWER only (Qwen3 convention: prior-turn thinking dropped)
521
+ story.push({ q, a: answer }); persistStory();
522
+ fitActions(ui, answer, () => { if (!busy) { mind = mindBefore; $("inp").value = q; send(); } }); // regenerate: rewind history, re-ask
523
+ offerFollowups(q, answer, ui.d); // the model writes its own follow-ups, in place
524
+ } else if (s.think.length) {
525
+ ui.body.textContent = "I ran out of room while thinking. Ask again, a bit shorter."; // unclosed <think> at the window edge; turn not kept
526
+ } else {
527
+ ui.body.textContent = res.text;
528
+ mind = ids.concat(res.outIds || []);
529
+ }
530
+ if (res.stats && res.stats.tokps) lastTokps = res.stats.tokps;
531
+ metrics();
532
+ } catch (e) { ui.body.textContent = "Something broke: " + (e && e.message || e); }
533
+ busy = false; $("send").disabled = false; $("inp").focus();
534
+ log.scrollTop = log.scrollHeight;
535
+ }
536
+
537
+ $("go").addEventListener("click", () => {
538
+ entered = true; show("chat"); $("inp").focus();
539
+ if (!streamStarted) { streamStarted = true; stream(); }
540
+ });
541
+ $("send").disabled = true; // until the mind is resident (early questions queue)
542
+ (async () => {
543
+ try {
544
+ if (navigator.connection && navigator.connection.saveData) return; // respect data saver
545
+ if (SRC) return; // dev override: no background pulls
546
+ const B27 = "https://huggingface.co/HOLOGRAMTECH/q-bonsai-27b/resolve/main";
547
+ const cache = await caches.open("holo-kappa-v1");
548
+ const man = await (await fetch(B27 + "/manifest.json")).json();
549
+ const urls = Object.values(man.tensors || {}).map((r) => B27 + "/b/" + String(r.kappa).replace(":", "_") + ".gz");
550
+ let i = 0;
551
+ const worker = async () => {
552
+ while (i < urls.length) {
553
+ const u = urls[i++];
554
+ if (await cache.match(u)) continue;
555
+ while (streamStarted && !ready) await new Promise((r) => setTimeout(r, 1500)); // the live stream owns the wire
556
+ try { const b = await (await fetch(u, { cache: "no-store" })).arrayBuffer(); await cache.put(u, new Response(b, { headers: { "Content-Type": "application/octet-stream" } })); } catch {}
557
+ }
558
+ };
559
+ Promise.all(Array.from({ length: 3 }, worker)).catch(() => {});
560
+ } catch {}
561
+ })();
562
+ (async () => { // returning visitor: the mind wakes BEHIND the welcome screen
563
+ try { const c = await caches.open("holo-kappa-v1"); if (await c.match(BASE + "/manifest.json")) { streamStarted = true; stream(); } } catch {}
564
+ })();
565
+ $("send").addEventListener("click", send);
566
+ $("inp").addEventListener("keydown", (e) => { if (e.key === "Enter") send(); });
567
+ if (!navigator.gpu) { $("go").disabled = true; $("fine").textContent = "This browser has no WebGPU. Chrome or Edge 113 or newer required."; }
568
+ </script>
569
+ </body>
sha256/788ab843ac27c6be1bd2196e54eaac4de156b8395f49e03bb05eab687af620c8 ADDED
@@ -0,0 +1 @@
 
 
1
+ {"@context":["https://www.w3.org/ns/did/v1","https://w3id.org/security/data-integrity/v2",{"dcterms":"http://purl.org/dc/terms/","links":{"@container":"@set","@id":"schema:hasPart"},"prov":"http://www.w3.org/ns/prov#","rel":"schema:additionalType","schema":"https://schema.org/"},{"holstr":"https://hologram.os/ns/strand#"}],"@type":["prov:Entity","holstr:Entry"],"holstr:kind":"release","holstr:op":"did:holo:sha256:87599792c7b13fad9d6c57d3b89d2cef35e4715869f5cde597ab60ca92699f0b","holstr:payload":{"apps":{"bonsai":"3e475a984692e4542e13c7efefeff7454e76121873717880da1fef8ab56ddeda","carry":"9f44164b17e6b7e37f39623bb445d49b6608da5f2b04cef8b1a8b183362a6b9a","hello":"66c1ada5ac08a2d4f73851bbe7458ad5fb6e2226c5a131c3829abfe715900fed","holo-messenger":"01e2db1e847e73881b7ce6f251677a4c0f9629203d0bc913053ffb4047b1df2d","jellyfin":"3fd2a00105ad0e8f01109dce52ff4c7a0cc6c9444532b0453ed9d770cce2d3a4","resolve":"23aec66382775f5773693615338f37c37a5f58257ec95aece33a664baa6fe0db","yourspace":"9ffc94e11c78fbe6b940bc04ac92f0f12238cf7ce82b5092c53d5761146a50df"},"boot":{"entry":"holo-portal.mjs","sha256":"caeb3e57b6eeab09bee6d67c022145a8e2dae610c929a7f51bc169898625e7e8"},"closure":"3c8d7d27d95c14b9eec35d6e5c5f037549cabec72fe43f67ab062ad46aaf15dc","manifest":"792736ddc9a6fa4d6ad612ac0bf851834e7acc46ba3ea335c6f0e32fdf24bda6","runtime":{"apps/q/pkg/holospaces_web.js":"b9f8a887de588c5193dc65ca9ce206a70c6df53595b848e0ce0299c2d88a5753","apps/q/pkg/holospaces_web_bg.wasm":"9d90b4ea8bccee8c239073c7ec7dd5158c6d11f1f668487570d17475d79e661e","apps/workspace/pkg/holospaces_web.js":"8c30aa5582dffe4548d27fa7863163eba689ed65126be472c003f5884fc8e534","usr/lib/holo/holowhat/holospaces_web.js":"a5b5d9c559a1d2812c33e9d7a4b1c24bb03059482503a23356e4c9c32d4d8b88","usr/lib/holo/holowhat/holospaces_web_bg.wasm":"00cfa4960b319ba1ba7291cddb20dea130a407ce8b42426218854f4f68f4dcd7","usr/lib/pkg/holospaces_web.js":"8c30aa5582dffe4548d27fa7863163eba689ed65126be472c003f5884fc8e534","usr/lib/pkg/holospaces_web_bg.wasm":"3370b22070b56218a57df6406e2e4ba7cc171e942d4d1f2ae4297ea2c4c08d9c"},"shell":"0e8ac9d7dd55694dcc30ce1cdcb688a2b624ba54eb90619bdf7251d123ba17b5","world":"b2208598c08adf523fca1e2aec7786b28a4ae0e4cab367c8bfdb252b025b471e"},"holstr:prev":"did:holo:sha256:211ff06c14a404369f42700ef027e4772b95817f427fc4ca584f98e407267ac0","holstr:seq":395,"prov:generatedAtTime":"2026-07-15T21:54:56.719Z"}
sha256/862de496de1aebae9929f2bfcba6bb286d0571fa240baba79d3219a82353c9ac ADDED
The diff for this file is too large to render. See raw diff
 
sha256/a9b7e0bb03f2d6571f7230d79b7b9fb1923c4fe387ced0fe025ac467c6ed0b5b ADDED
The diff for this file is too large to render. See raw diff