diff --git a/packaging/mother/colibri-mcp-ssh b/packaging/mother/colibri-mcp-ssh
index 029e429..ad3230d 100755
--- a/packaging/mother/colibri-mcp-ssh
+++ b/packaging/mother/colibri-mcp-ssh
@@ -27,14 +27,10 @@ case "${SSH_ORIGINAL_COMMAND:-}" in
;;
"report-task-cost")
# Read TaskCostSummary JSON from stdin, INSERT into mother_hive.task_costs.
- # Input: {"node_hostname":"debby","task_id":"abc","provider":"deepseek",
- # "model":"deepseek-chat","input_tokens":150,"output_tokens":80,
- # "cache_read_tokens":200,"cache_write_tokens":50,
- # "cost_usd":0.0042,"success":true,
- # "proof_text":"{\"agent\":\"zot\",\"state\":\"Done\",\"tokens_in\":150}",
- # "screenshot_uuid":"a1b2c3d4e5f6",
- # "finished_at":"2026-06-27T12:00:00Z"}
- psql -d mother_hive -tA -v ON_ERROR_STOP=1 <<'PSQL'
+ # Uses psql variable interpolation (no pg_read_file — works with non-superuser).
+ # Input: {"node_hostname":"debby","task_id":"abc",...}
+ _json=$(cat)
+ psql -d mother_hive -tA -v ON_ERROR_STOP=1 -v json_input="$_json" <<'PSQL'
INSERT INTO task_costs (node_id, task_id, provider, model,
input_tokens, output_tokens, cache_read_tokens, cache_write_tokens,
cost_usd, success, proof_text, screenshot_uuid, finished_at)
@@ -52,7 +48,7 @@ SELECT
NULLIF(j->>'proof_text', ''),
NULLIF(j->>'screenshot_uuid', ''),
COALESCE((j->>'finished_at')::TIMESTAMPTZ, now())
-FROM (SELECT (pg_read_file('/dev/stdin')::JSONB) AS j) AS _;
+FROM (SELECT (:'json_input'::JSONB) AS j) AS _;
PSQL
;;
*)
diff --git a/packaging/mother/dashboard/index.html b/packaging/mother/dashboard/index.html
index dc109de..8584fa9 100644
--- a/packaging/mother/dashboard/index.html
+++ b/packaging/mother/dashboard/index.html
@@ -190,7 +190,6 @@ h1 .dot{display:inline-block; width:8px; height:8px; border-radius:50%; margin-r
-
@@ -306,9 +305,10 @@ function renderCard(t) {
const total = (t.input_tokens||0) + (t.cache_read_tokens||0);
const cachePct = total > 0 ? Math.round((t.cache_read_tokens||0) / total * 100) : 0;
const freshPct = 100 - cachePct;
- const hasProofText = !!t.proof_text;
- const hasScreenshot = !!t.screenshot_uuid;
- const hasProof = hasProofText || hasScreenshot;
+ // Only proof_text exists — daemon captures glasspane state at task exit.
+ // screenshot_uuid is a schema column for future visual capture, never
+ // populated by the current daemon (no screenshot.rs module on main).
+ const hasProof = !!t.proof_text;
const cls = hasProof ? 'card has-proof' : 'card';
const onClick = hasProof
? `onclick="openProof(this,'${esc(t.task_id||'')}')"`
@@ -327,7 +327,7 @@ function renderCard(t) {
${fmtTokens(t.input_tokens||0)} in · ${fmtTokens(t.output_tokens||0)} out
${cachePct>0?` · ${cachePct}% cache` : ''}
- ${hasScreenshot ? '▸ screenshot
' : hasProofText ? '▸ text
' : ''}
+ ${hasProof ? '▸ proof
' : ''}
`;
}
@@ -336,24 +336,16 @@ function openProof(_el, taskId) {
const t = (DATA.tasks || []).find(t => t.task_id === taskId);
const lb = document.getElementById('lightbox');
const meta = document.getElementById('lightbox-meta');
- const img = document.getElementById('lb-img');
const pre = document.getElementById('lb-text');
meta.innerHTML = `${esc(taskId)} ${esc(t?.provider||'')} · $${(t?.cost_usd||0).toFixed(4)}`;
- // Hide both, then show the appropriate one.
- img.style.display = 'none';
- pre.style.display = 'none';
-
- if (t?.screenshot_uuid) {
- img.src = `../screenshots/${t.screenshot_uuid}.png`;
- img.style.display = 'block';
- } else if (t?.proof_text) {
- let display;
- try { display = JSON.stringify(JSON.parse(t.proof_text), null, 2); }
- catch(_) { display = t.proof_text; }
- pre.textContent = display;
- pre.style.display = 'block';
- }
+ // Proof is always inline text (glasspane state JSON).
+ const raw = t?.proof_text || '{}';
+ let display;
+ try { display = JSON.stringify(JSON.parse(raw), null, 2); }
+ catch(_) { display = raw; }
+ pre.textContent = display;
+ pre.style.display = 'block';
lb.classList.add('open');
}
function closeLb() { document.getElementById('lightbox').classList.remove('open'); }