Compare commits
No commits in common. "808b9cbe1a0e51495f80a8c102bad8da736baac1" and "25659ee9f3e3a094ff8a567fc4ec95d5a8284f4e" have entirely different histories.
808b9cbe1a
...
25659ee9f3
4 changed files with 5 additions and 121 deletions
|
|
@ -167,68 +167,21 @@ canvas { display: block; cursor: default; }
|
||||||
if (n === 0) { state = 'error'; return false; }
|
if (n === 0) { state = 'error'; return false; }
|
||||||
const types = drainTo(n);
|
const types = drainTo(n);
|
||||||
if (!types) { chunks.unshift(b); totalBytes += 1; return false; }
|
if (!types) { chunks.unshift(b); totalBytes += 1; return false; }
|
||||||
// Prefer type 1 (None), then type 19 (VeNCrypt — used by neatvnc/weston
|
// Prefer type 1 (None), else use first offered
|
||||||
// even with --disable-transport-layer-security), else first offered.
|
const prefer = types.indexOf(1) !== -1 ? 1 : types[0];
|
||||||
let prefer;
|
|
||||||
if (types.indexOf(1) !== -1) prefer = 1; // plain None
|
|
||||||
else if (types.indexOf(19) !== -1) prefer = 19; // VeNCrypt
|
|
||||||
else prefer = types[0];
|
|
||||||
send(new Uint8Array([prefer]));
|
send(new Uint8Array([prefer]));
|
||||||
if (prefer === 1) state = 'security-result';
|
state = prefer === 1 ? 'security-result' : 'security-vnc-challenge';
|
||||||
else if (prefer === 19) state = 'vencrypt-version';
|
|
||||||
else state = 'security-vnc-challenge';
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case 'security-vnc-challenge': {
|
case 'security-vnc-challenge': {
|
||||||
// VNC auth (type 2): we don't have the password, so send zeros.
|
// VNC auth: skip challenge bytes, respond with zeros (will fail,
|
||||||
// This will fail for password-protected servers; fine for our
|
// but we're in plain-RFB mode for hyperhive — see weston-vnc.nix)
|
||||||
// weston VNC which uses None via VeNCrypt.
|
|
||||||
const b = drainTo(16);
|
const b = drainTo(16);
|
||||||
if (!b) return false;
|
if (!b) return false;
|
||||||
send(new Uint8Array(16));
|
send(new Uint8Array(16));
|
||||||
state = 'security-result';
|
state = 'security-result';
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// ── VeNCrypt (type 19) sub-handshake ───────────────────────────────
|
|
||||||
// neatvnc (weston VNC backend) uses VeNCrypt as the outer type even
|
|
||||||
// with --disable-transport-layer-security, offering sub-type 1 (None).
|
|
||||||
case 'vencrypt-version': {
|
|
||||||
// Server sends: major (u8), minor (u8) — e.g. 0, 2
|
|
||||||
const b = drainTo(2);
|
|
||||||
if (!b) return false;
|
|
||||||
// Echo same version back
|
|
||||||
send(new Uint8Array([b[0], b[1]]));
|
|
||||||
state = 'vencrypt-subtypes';
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case 'vencrypt-subtypes': {
|
|
||||||
// Server sends: nSubtypes (u8), then nSubtypes × u32 sub-type ids
|
|
||||||
const nb = drainTo(1);
|
|
||||||
if (!nb) return false;
|
|
||||||
const nSub = nb[0];
|
|
||||||
const raw = drainTo(nSub * 4);
|
|
||||||
if (!raw) { chunks.unshift(nb); totalBytes += 1; return false; }
|
|
||||||
// Build sub-type array from big-endian u32s
|
|
||||||
const subs = [];
|
|
||||||
for (let i = 0; i < nSub; i++) subs.push(u32be(raw, i * 4));
|
|
||||||
// Prefer sub-type 1 (VeNCrypt None) — no TLS, no password.
|
|
||||||
// Fall back to first offered.
|
|
||||||
const sub = subs.includes(1) ? 1 : subs[0];
|
|
||||||
// Send chosen sub-type as big-endian u32
|
|
||||||
send(new Uint8Array([sub>>>24, (sub>>>16)&0xff, (sub>>>8)&0xff, sub&0xff]));
|
|
||||||
state = 'vencrypt-accept';
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case 'vencrypt-accept': {
|
|
||||||
// Server sends 1 byte: 1=accepted, 0=refused
|
|
||||||
const b = drainTo(1);
|
|
||||||
if (!b) return false;
|
|
||||||
if (b[0] !== 1) { setStatus('VeNCrypt sub-type refused', 'error'); return false; }
|
|
||||||
// Sub-type 1 (None): proceed to SecurityResult
|
|
||||||
state = 'security-result';
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// ──────────────────────────────────────────────────────────────────
|
|
||||||
case 'security-result': {
|
case 'security-result': {
|
||||||
const b = drainTo(4);
|
const b = drainTo(4);
|
||||||
if (!b) return false;
|
if (!b) return false;
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,6 @@
|
||||||
// and tails the unified dashboard event channel over `/dashboard/stream`.
|
// and tails the unified dashboard event channel over `/dashboard/stream`.
|
||||||
|
|
||||||
(() => {
|
(() => {
|
||||||
// ─── constants ──────────────────────────────────────────────────────────
|
|
||||||
// Context-window token thresholds — mirror the harness compaction watermarks
|
|
||||||
// in hive-ag3nt (HIVE_COMPACT_WATERMARK_TOKENS = 150k; auto-reset at 100k).
|
|
||||||
// TODO: source these from model metadata once damocles lands that feature.
|
|
||||||
const CTX_WARN_TOKENS = 150_000; // ≥ this → compact territory (red)
|
|
||||||
const CTX_CAUTION_TOKENS = 100_000; // ≥ this → approaching reset (yellow)
|
|
||||||
|
|
||||||
// ─── helpers ────────────────────────────────────────────────────────────
|
// ─── helpers ────────────────────────────────────────────────────────────
|
||||||
const $ = (id) => document.getElementById(id);
|
const $ = (id) => document.getElementById(id);
|
||||||
const esc = (s) => String(s).replace(/[&<>"]/g, (c) =>
|
const esc = (s) => String(s).replace(/[&<>"]/g, (c) =>
|
||||||
|
|
@ -557,18 +550,6 @@
|
||||||
},
|
},
|
||||||
`⏰ ${c.pending_reminders}`));
|
`⏰ ${c.pending_reminders}`));
|
||||||
}
|
}
|
||||||
if (c.ctx_tokens != null) {
|
|
||||||
const k = Math.round(c.ctx_tokens / 1000);
|
|
||||||
const ctxClass = c.ctx_tokens >= CTX_WARN_TOKENS ? 'badge-ctx-warn'
|
|
||||||
: c.ctx_tokens >= CTX_CAUTION_TOKENS ? 'badge-ctx-caution'
|
|
||||||
: 'badge-ctx-ok';
|
|
||||||
head.append(el('span',
|
|
||||||
{
|
|
||||||
class: `badge ${ctxClass}`,
|
|
||||||
title: `last turn context size: ${c.ctx_tokens.toLocaleString()} tokens`,
|
|
||||||
},
|
|
||||||
`ctx·${k}k`));
|
|
||||||
}
|
|
||||||
li.append(head);
|
li.append(head);
|
||||||
|
|
||||||
// ── line 2: action buttons ───────────────────────────────────
|
// ── line 2: action buttons ───────────────────────────────────
|
||||||
|
|
|
||||||
|
|
@ -123,20 +123,6 @@ a:hover {
|
||||||
color: var(--cyan); border-color: var(--cyan);
|
color: var(--cyan); border-color: var(--cyan);
|
||||||
text-shadow: 0 0 6px rgba(137, 220, 235, 0.4);
|
text-shadow: 0 0 6px rgba(137, 220, 235, 0.4);
|
||||||
}
|
}
|
||||||
/* Context-window usage badges on dashboard container rows.
|
|
||||||
Green < 100k, yellow 100–150k, red ≥ 150k (mirrors harness watermarks). */
|
|
||||||
.badge-ctx-ok {
|
|
||||||
color: var(--green); border-color: var(--green);
|
|
||||||
opacity: 0.85;
|
|
||||||
}
|
|
||||||
.badge-ctx-caution {
|
|
||||||
color: var(--amber); border-color: var(--amber);
|
|
||||||
text-shadow: 0 0 6px rgba(250, 179, 135, 0.5);
|
|
||||||
}
|
|
||||||
.badge-ctx-warn {
|
|
||||||
color: var(--red); border-color: var(--red);
|
|
||||||
text-shadow: 0 0 6px rgba(243, 139, 168, 0.5);
|
|
||||||
}
|
|
||||||
.container-row.tombstone {
|
.container-row.tombstone {
|
||||||
border-style: dashed;
|
border-style: dashed;
|
||||||
background: rgba(24, 24, 37, 0.35);
|
background: rgba(24, 24, 37, 0.35);
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use rusqlite::Connection;
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::coordinator::Coordinator;
|
use crate::coordinator::Coordinator;
|
||||||
|
|
@ -37,13 +36,6 @@ pub struct ContainerView {
|
||||||
/// not real-time on remind/cancel-reminder but close enough.
|
/// not real-time on remind/cancel-reminder but close enough.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub pending_reminders: u64,
|
pub pending_reminders: u64,
|
||||||
/// Context-window size (prompt tokens) from the agent's most recent
|
|
||||||
/// completed turn, read directly from the turn-stats SQLite.
|
|
||||||
/// `None` when the file is absent or the agent has no turns yet.
|
|
||||||
/// Stale by up to one crash-watch cycle (~10s); good enough for
|
|
||||||
/// the "which agent is close to the window?" dashboard glance.
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub ctx_tokens: Option<u64>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Build the full container list. Wraps `lifecycle::list()` and
|
/// Build the full container list. Wraps `lifecycle::list()` and
|
||||||
|
|
@ -82,7 +74,6 @@ pub async fn build_all(coord: &Coordinator) -> Vec<ContainerView> {
|
||||||
.broker
|
.broker
|
||||||
.count_pending_reminders_for(reminder_recipient)
|
.count_pending_reminders_for(reminder_recipient)
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
let ctx_tokens = read_last_ctx_tokens(&logical);
|
|
||||||
out.push(ContainerView {
|
out.push(ContainerView {
|
||||||
port: lifecycle::agent_web_port(&logical),
|
port: lifecycle::agent_web_port(&logical),
|
||||||
running: lifecycle::is_running(&logical).await,
|
running: lifecycle::is_running(&logical).await,
|
||||||
|
|
@ -93,7 +84,6 @@ pub async fn build_all(coord: &Coordinator) -> Vec<ContainerView> {
|
||||||
needs_login,
|
needs_login,
|
||||||
deployed_sha,
|
deployed_sha,
|
||||||
pending_reminders,
|
pending_reminders,
|
||||||
ctx_tokens,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
|
|
@ -112,32 +102,6 @@ pub fn claude_has_session(dir: &Path) -> bool {
|
||||||
.any(|e| e.file_type().is_ok_and(|t| t.is_file()))
|
.any(|e| e.file_type().is_ok_and(|t| t.is_file()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read the most recent completed turn's context-window size (prompt
|
|
||||||
/// tokens) from the agent's turn-stats SQLite. Returns `None` when
|
|
||||||
/// the file is absent or has no rows. Best-effort — any DB error
|
|
||||||
/// silently yields `None` so a missing/corrupt file never blocks
|
|
||||||
/// `build_all`.
|
|
||||||
///
|
|
||||||
/// Context tokens = `last_input_tokens + last_cache_read_input_tokens
|
|
||||||
/// + last_cache_creation_input_tokens`, mirroring
|
|
||||||
/// `hive_ag3nt::events::TokenUsage::context_tokens`.
|
|
||||||
fn read_last_ctx_tokens(name: &str) -> Option<u64> {
|
|
||||||
let path = Coordinator::agent_notes_dir(name).join("hyperhive-turn-stats.sqlite");
|
|
||||||
let conn = Connection::open_with_flags(
|
|
||||||
&path,
|
|
||||||
rusqlite::OpenFlags::SQLITE_OPEN_READ_ONLY,
|
|
||||||
)
|
|
||||||
.ok()?;
|
|
||||||
conn.query_row(
|
|
||||||
"SELECT last_input_tokens + last_cache_read_input_tokens + last_cache_creation_input_tokens \
|
|
||||||
FROM turn_stats ORDER BY started_at DESC LIMIT 1",
|
|
||||||
[],
|
|
||||||
|row| row.get::<_, i64>(0),
|
|
||||||
)
|
|
||||||
.ok()
|
|
||||||
.and_then(|v| u64::try_from(v).ok())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Map of `agent-<n>` → locked sha from meta's flake.lock. Used to
|
/// Map of `agent-<n>` → locked sha from meta's flake.lock. Used to
|
||||||
/// render the `deployed:<sha12>` chip per container row.
|
/// render the `deployed:<sha12>` chip per container row.
|
||||||
fn read_meta_locked_revs() -> HashMap<String, String> {
|
fn read_meta_locked_revs() -> HashMap<String, String> {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue