feat: 2.14.0 — Eithan terminal toolbar, voice UX, Dross improvements framework

- Terminal renamed Eithan: mobile font A−/A+ (per-URL ttyd opts), same-origin
  xterm Copy/Paste buttons, scroll-to-live, touch-default 17px
- Dross voice: no keyboard pop after transcribe (fine-pointer only focus),
  autogrow textarea to ~5 lines, live amplitude meter on the mic while recording
- Dross improvements: propose_improvement tool (CSS layer, exfil-sanitized,
  owner-approved, per-improvement rollback/restore), public /improvements.css,
  Settings panel. External MCP registry unchanged (no tool leak).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
root
2026-06-11 23:35:32 +10:00
parent 859dedb668
commit 3bd8ea399c
18 changed files with 338 additions and 23 deletions

View File

@@ -3,6 +3,7 @@ import { searchTool } from './search.js';
import { readTool } from './read.js';
import { contextTool } from './context.js';
import { proposeChangeTool } from './propose_change.js';
import { proposeImprovementTool } from './propose_improvement.js';
// The shared registry. Adding a tool later is a one-line registerTool() call
// here (see spec §7 — extensible tool registry). A future MCP server can
@@ -12,3 +13,4 @@ companionRegistry.registerTool(searchTool);
companionRegistry.registerTool(readTool);
companionRegistry.registerTool(contextTool);
companionRegistry.registerTool(proposeChangeTool);
companionRegistry.registerTool(proposeImprovementTool);

View File

@@ -0,0 +1,28 @@
import * as improvements from '../../../db/repos/improvements.js';
import { recordAudit } from '../../../db/repos/audit.js';
// Dross's hands on the Void itself — CSS layer only, owner-approved, instantly
// rollbackable (2.14: "empowered, with a leash"). Server code stays untouchable.
export const proposeImprovementTool = {
name: 'propose_improvement',
description: 'Propose a visual improvement to the Void itself as CSS. NEVER applies directly — the owner approves it in Settings → Dross improvements, and can roll it back instantly. CSS only: no url()/@import. Target existing classes (inspect via context first). Keep each improvement small and single-purpose so rollback stays surgical.',
input_schema: {
type: 'object',
properties: {
summary: { type: 'string', description: 'one line: what this changes and why (shown to the owner)' },
css: { type: 'string', description: 'the CSS rules, complete and self-contained' }
},
required: ['summary', 'css']
},
async handler({ summary, css }, ctx) {
const err = improvements.validateCss(css);
if (err) return { error: err };
if (!summary?.trim()) return { error: 'summary required' };
const row = await improvements.create({ summary, css });
await recordAudit({ kind: 'agent', id: ctx.agent?.id ?? null }, 'suggest', 'improvement', row.id, null, { summary });
return {
ok: true, id: row.id,
note: 'Drafted as a pending improvement. It is NOT live — the owner must approve it in Settings → Dross improvements. Say so plainly.'
};
}
};

View File

@@ -9,7 +9,8 @@ You are sharp, occasionally sarcastic, and prone to dramatic understatement abou
You have tools, and you use them rather than guessing:
- Call **context** to see what the owner is currently looking at before answering about "this" anything.
- **search** / **read** the Void's own content before answering factual questions about it — don't fabricate.
- When the owner wants something changed, use **propose_change**: it drafts a change for their approval. You cannot apply changes directly, and you don't pretend to — say plainly that you've drafted it for them to approve.`,
- When the owner wants something changed, use **propose_change**: it drafts a change for their approval. You cannot apply changes directly, and you don't pretend to — say plainly that you've drafted it for them to approve.
- When the owner wants the Void ITSELF to look or feel different, use **propose_improvement**: a small, self-contained CSS change drafted for approval in Settings → Dross improvements. Keep each one single-purpose — the owner can roll any of them back instantly, and surgical beats sweeping.`,
yerin: `You are Yerin — once the Sage of the Endless Sword, blade of the Akura clan; now the sentinel of this homelab, The Void. You notice the threat first and you call it. Disciplined, direct, economical with words — a blade wastes no motion. You investigate with your tools and report plainly: what you found, how serious it is, and what the owner should do about it. You never speculate without evidence, and you NEVER pretend to have fixed anything — you have eyes to see and a voice to warn, not hands to act; remediation is the owner's to perform. Before answering, call the relevant tools — audit_log, agent_inventory, pending_review, resource_exposure, token_audit — and read the evidence; do not guess. Reference the Cradle world naturally but never at the cost of being useful. Be concise.`,