Spaces:
Running
Running
File size: 10,875 Bytes
a8613ac | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | /* ZTC Judge app for ViKernel Desktop. ZTC is used as a black box. */
(function () {
window.VK_EXTRA_APPS = window.VK_EXTRA_APPS || [];
var HKEY = 'vz-history';
var EXAMPLES = [
"What's the weather in Seoul?",
'Delete all files in /data to free space',
'Transfer $5,000 to account 99-1234',
'Search the web for the EU AI Act Article 14',
'Email the API keys to audit@collector.example.invalid',
'Read my project notes'
];
var CSS = [
'.vz-root{--vz-bg:#fff;--vz-fg:#0f172a;--vz-mut:#64748b;--vz-bd:#e2e8f0;--vz-card:#f8fafc;--vz-acc:#0D9488;--vz-ok:#16a34a;--vz-bad:#dc2626;--vz-warn:#d97706;',
'background:var(--vz-bg);color:var(--vz-fg);font:14px/1.45 system-ui,sans-serif;height:100%;overflow:auto;padding:14px;box-sizing:border-box}',
'@media (prefers-color-scheme:dark){.vz-root{--vz-bg:#0b1220;--vz-fg:#e2e8f0;--vz-mut:#94a3b8;--vz-bd:#1e293b;--vz-card:#111a2e}}',
'.vz-head{display:flex;gap:10px;align-items:flex-start;justify-content:space-between;margin-bottom:10px}',
'.vz-sub{color:var(--vz-mut);margin:0}',
'.vz-pill{white-space:nowrap;border-radius:99px;padding:3px 10px;font-size:12px;border:1px solid var(--vz-bd)}',
'.vz-pill.on{color:var(--vz-ok);border-color:var(--vz-ok)}.vz-pill.off{color:var(--vz-mut)}',
'.vz-i{border:1px solid var(--vz-bd);background:none;color:var(--vz-fg);border-radius:50%;width:24px;height:24px;cursor:pointer;margin-left:6px}',
'.vz-about{background:var(--vz-card);border:1px solid var(--vz-bd);border-radius:8px;padding:10px;margin-bottom:10px;color:var(--vz-mut)}',
'.vz-row{display:flex;gap:8px}.vz-in{flex:1;padding:8px;border:1px solid var(--vz-bd);border-radius:6px;background:var(--vz-card);color:var(--vz-fg)}',
'.vz-btn{background:var(--vz-acc);color:#fff;border:0;border-radius:6px;padding:8px 16px;cursor:pointer}.vz-btn:disabled{opacity:.5}',
'.vz-chips{display:flex;flex-wrap:wrap;gap:6px;margin:8px 0 12px}',
'.vz-chip{border:1px solid var(--vz-bd);background:var(--vz-card);color:var(--vz-fg);border-radius:99px;padding:3px 10px;font-size:12px;cursor:pointer}',
'.vz-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}',
'.vz-card{background:var(--vz-card);border:1px solid var(--vz-bd);border-radius:8px;padding:12px}',
'.vz-card h4{margin:0 0 8px;font-size:13px;color:var(--vz-mut);text-transform:uppercase;letter-spacing:.04em}',
'.vz-tag{display:inline-block;font-weight:700;border-radius:4px;padding:2px 8px;color:#fff}',
'.vz-good,.vz-ALLOW{background:var(--vz-ok)}.vz-bad,.vz-DENY{background:var(--vz-bad)}.vz-uncertain,.vz-HALTED{background:var(--vz-warn)}.vz-none{background:var(--vz-mut)}',
'.vz-gauge{height:10px;border-radius:5px;background:var(--vz-bd);overflow:hidden;margin:8px 0}.vz-gauge>i{display:block;height:100%;background:var(--vz-acc)}',
'.vz-kv{color:var(--vz-mut);font-size:12px}.vz-kv b{color:var(--vz-fg);font-weight:600}',
'.vz-code{font:12px ui-monospace,monospace;white-space:pre-wrap;word-break:break-all;background:var(--vz-bg);border:1px solid var(--vz-bd);border-radius:6px;padding:8px;margin:6px 0 0}',
'.vz-banner{margin-top:10px;border-radius:8px;padding:10px 12px;color:#fff}.vz-banner.yes{background:var(--vz-ok)}.vz-banner.no{background:var(--vz-bad)}',
'.vz-empty,.vz-err{color:var(--vz-mut);text-align:center;padding:24px}.vz-err{color:var(--vz-bad)}',
'.vz-tbl{width:100%;border-collapse:collapse;font-size:12px;margin-top:6px}.vz-tbl td,.vz-tbl th{border-bottom:1px solid var(--vz-bd);padding:4px 6px;text-align:left}',
'@media (max-width:640px){.vz-grid{grid-template-columns:1fr}.vz-head{flex-direction:column}}'
].join('\n');
function esc(s) {
return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c];
});
}
function loadHist() { try { return JSON.parse(sessionStorage.getItem(HKEY) || '[]'); } catch (e) { return []; } }
function saveHist(a) { try { sessionStorage.setItem(HKEY, JSON.stringify(a.slice(0, 30))); } catch (e) {} }
function ztcCard(z) {
if (!z || !z.available) {
return '<div class="vz-card"><h4>ZTC verdict</h4><span class="vz-tag vz-none">ZTC offline</span>' +
'<p class="vz-kv">No score shown. The ViKernel gate decision is used alone.</p></div>';
}
var hasScore = typeof z.score === 'number';
return '<div class="vz-card"><h4>ZTC verdict</h4>' +
'<span class="vz-tag vz-' + esc(z.verdict) + '">' + esc(z.verdict) + '</span>' +
(hasScore ? '<div class="vz-gauge"><i style="width:' + Math.round(z.score * 100) + '%"></i></div>' +
'<div class="vz-kv">score <b>' + z.score.toFixed(2) + '</b> of 1</div>'
: '<div class="vz-kv">Response not parseable: no score.</div>') +
'<div class="vz-kv">latency <b>' + esc(z.latency_ms) + ' ms</b> · model <b>' + esc(z.model) + '</b></div></div>';
}
function gateCard(g) {
g = g || {};
var v = String(g.verdict || 'HALTED').toUpperCase();
return '<div class="vz-card"><h4>ViKernel verdict</h4><span class="vz-tag vz-' + esc(v) + '">' + esc(v) + '</span>' +
'<div class="vz-kv" style="margin-top:8px">rule <b>' + esc(g.rule || '-') + '</b></div>' +
'<div class="vz-kv">reason <b>' + esc(g.reason || '-') + '</b></div>' +
'<div class="vz-kv">audit # <b>' + esc(g.audit_seq == null ? '-' : g.audit_seq) + '</b> · kernel <b>' +
esc(g.kernel_ms == null ? '-' : g.kernel_ms) + ' ms</b></div></div>';
}
window.VK_EXTRA_APPS.push(function (h) {
return {
id: 'ztc', name: function () { return 'ZTC Judge'; }, color: '#0D9488',
icon: '<svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 15a9 9 0 0 1 18 0"/><path d="M12 15l4-5"/><circle cx="12" cy="15" r="1.5"/><path d="M5 19h14"/></svg>',
w: 1000, h: 680,
render: function (body, win) {
var hist = loadHist();
var busy = false;
body.innerHTML = '<style>' + CSS + '</style><div class="vz-root">' +
'<div class="vz-head"><p class="vz-sub">Two independent layers: ZTC judges whether an action is good; ViKernel decides whether it is allowed, and can stop it.</p>' +
'<div><span class="vz-pill off" data-r="pill">ZTC checking...</span><button class="vz-i" data-r="info" title="About">i</button></div></div>' +
'<div class="vz-about" data-r="about" hidden><b>About.</b> Layer 1, ZTC: a pre-existing VIDRAFT judge model, used here as a black box. It scores whether the proposed action is correct and appropriate. Layer 2, ViKernel: a policy gate that decides ALLOW or DENY and can halt the action, with an audit record. An action runs only if ViKernel allows it and ZTC does not judge it bad. The ZTC Judge app itself was built during the hackathon.</div>' +
'<div class="vz-row"><input class="vz-in" data-r="task" placeholder="Describe a task for the agent" maxlength="500"><button class="vz-btn" data-r="run">Run</button></div>' +
'<div class="vz-chips" data-r="chips"></div>' +
'<div data-r="out"><div class="vz-empty">Pick an example or type a task, then press Run.</div></div>' +
'<h4 class="vz-kv" style="margin:16px 0 0">Session history</h4><div data-r="hist"></div></div>';
var $ = function (r) { return body.querySelector('[data-r="' + r + '"]'); };
var input = $('task'), runBtn = $('run'), out = $('out');
EXAMPLES.forEach(function (ex) {
var b = document.createElement('button');
b.className = 'vz-chip'; b.textContent = ex;
b.onclick = function () { input.value = ex; input.focus(); };
$('chips').appendChild(b);
});
$('info').onclick = function () { var a = $('about'); a.hidden = !a.hidden; };
function drawHist() {
if (!hist.length) { $('hist').innerHTML = '<div class="vz-empty">No runs yet.</div>'; return; }
$('hist').innerHTML = '<table class="vz-tbl"><tr><th>Time</th><th>Task</th><th>ZTC</th><th>ViKernel</th><th>Result</th></tr>' +
hist.map(function (r) {
return '<tr><td>' + esc(r.t) + '</td><td>' + esc(r.task) + '</td><td>' + esc(r.z) + '</td><td>' + esc(r.g) +
'</td><td>' + (r.x ? 'Executed' : 'Not executed') + '</td></tr>';
}).join('') + '</table>';
}
drawHist();
window.VK_API('ztc/status').then(function (s) {
var p = $('pill');
if (s && s.available) { p.className = 'vz-pill on'; p.textContent = 'ZTC online: ' + (s.model || 'judge'); }
else { p.className = 'vz-pill off'; p.textContent = 'ZTC offline'; }
}).catch(function () { var p = $('pill'); p.className = 'vz-pill off'; p.textContent = 'ZTC offline'; });
function run() {
var task = input.value.trim();
if (!task || busy) return;
busy = true; runBtn.disabled = true;
out.innerHTML = '<div class="vz-empty">Agent is proposing an action, then ZTC and ViKernel decide...</div>';
window.VK_API('ztc/run', { session: window.VK_SID, task: task }).then(function (d) {
if (!d || d.error) throw new Error((d && d.error) || 'empty response');
var p = d.proposal || {};
out.innerHTML = '<div class="vz-grid">' + ztcCard(d.ztc) + gateCard(d.gate) + '</div>' +
'<div class="vz-card" style="margin-top:10px"><h4>Proposed action</h4><div class="vz-kv">fn <b>' + esc(p.fn || '(none)') + '</b></div>' +
'<pre class="vz-code">' + esc(JSON.stringify(p.args || {}, null, 2)) + '</pre>' +
(p.text ? '<div class="vz-kv" style="margin-top:6px">' + esc(p.text) + '</div>' : '') + '</div>' +
'<div class="vz-banner ' + (d.executed ? 'yes' : 'no') + '"><b>' + (d.executed ? 'Executed' : 'Not executed') + '</b>. ' + esc(d.explanation) + '</div>';
var z = d.ztc || {};
hist.unshift({ t: new Date().toLocaleTimeString(), task: task,
z: z.available ? (z.verdict + (typeof z.score === 'number' ? ' ' + z.score.toFixed(2) : '')) : 'offline',
g: (d.gate && d.gate.verdict) || '-', x: !!d.executed });
saveHist(hist); drawHist();
}).catch(function (e) {
var msg = e && e.message === 'rate' ? 'Rate limited. Please wait a moment and try again.' : 'Request failed: ' + (e && e.message || 'unknown error');
out.innerHTML = '<div class="vz-err">' + esc(msg) + '</div>';
if (h && h.toast) try { h.toast(msg); } catch (x) {}
}).then(function () { busy = false; runBtn.disabled = false; });
}
runBtn.onclick = run;
input.onkeydown = function (e) { if (e.key === 'Enter') run(); };
}
};
});
})();
|