overseer improvements and new social buttones

This commit is contained in:
legop3
2026-05-26 23:58:23 -04:00
parent f56d865c8e
commit 742232b882
15 changed files with 253 additions and 297 deletions
+9
View File
@@ -24,6 +24,7 @@ overseerControl:
profileImageUrl: "https://example.com/overseer.png"
gateIntervalMs: 2000
heartbeatMs: 30000
postChatDelayMs: 20000
media:
# Base address for mediaMTX (scheme + host + optional port/path). The UI will always request
# http://<base>/<roverId>/whep
@@ -119,12 +120,20 @@ socials:
- id: "discord"
label: "Discord"
url: "https://discord.gg/your-invite"
icon: "FaDiscord"
color: "#5865F2"
- id: "kofi"
label: "Ko-fi"
url: "https://ko-fi.com/your-handle"
icon: "FaCoffee"
color: "#29ABE0"
- id: "wiki"
label: "Wiki"
url: "https://wiki.example.com"
icon: "FaBook"
color: "#475569"
- id: "throne"
label: "Throne"
url: "https://throne.me/yourname"
icon: "FaCrown"
color: "#334155"
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -11,8 +11,8 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Roomba Rover" />
<title>Roomba Rover</title>
<script type="module" crossorigin src="/assets/index-EY6LbYPB.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BIQkdXB4.css">
<script type="module" crossorigin src="/assets/index-3MESEWAw.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-C8Hua3Ji.css">
</head>
<body>
<div id="root"></div>
@@ -4,6 +4,7 @@ const PROMPT_PATH = path.join(__dirname, '..', '..', '..', 'prompts', 'overseer_
const DEFAULT_NAME = 'The Overseer';
const DEFAULT_GATE_INTERVAL_MS = 2000;
const DEFAULT_HEARTBEAT_MS = 30000;
const DEFAULT_POST_CHAT_DELAY_MS = 20000;
const MIN_INTERVAL_MS = 250;
const MAX_RUN_HISTORY = 100;
const MAX_CHAT_CONTEXT = 12;
@@ -19,6 +20,7 @@ module.exports = {
DEFAULT_NAME,
DEFAULT_GATE_INTERVAL_MS,
DEFAULT_HEARTBEAT_MS,
DEFAULT_POST_CHAT_DELAY_MS,
MAX_RUN_HISTORY,
MAX_CHAT_CONTEXT,
MAX_BOT_CONTEXT,
@@ -60,16 +60,28 @@ function buildConversation({ recentMessages, name }) {
return messages;
}
function buildModelMessages({ systemPrompt, stateUpdate, memorySummary, conversationMessages, availableTools, blockedTools }) {
function buildModelMessages({
systemPrompt,
stateUpdate,
memorySummary,
recentEvents,
conversationMessages,
availableTools,
blockedTools,
}) {
const messages = [];
messages.push({ role: 'system', content: systemPrompt });
const metadataSections = [];
metadataSections.push(`STATE_UPDATE\n${stateUpdate}`);
if (memorySummary) metadataSections.push(`MEMORY_UPDATE\n${memorySummary}`);
metadataSections.push(
`tool_constraints:\n${blockedTools.map((entry) => `- blocked: ${entry.tool} reason=${entry.reason}`).join('\n') || '- none'}`,
);
messages.push({ role: 'user', content: metadataSections.join('\n\n') });
messages.push({ role: 'system', content: `ROOM_SNAPSHOT\n${stateUpdate}` });
if (memorySummary) {
messages.push({ role: 'system', content: `MEMORY_SUMMARY\n${memorySummary}` });
}
if (recentEvents) {
messages.push({ role: 'system', content: `RECENT_EVENTS\n${recentEvents}` });
}
messages.push({
role: 'system',
content: `TOOL_CONSTRAINTS\n${blockedTools.map((entry) => `- blocked: ${entry.tool} reason=${entry.reason}`).join('\n') || '- none'}`,
});
(conversationMessages || []).forEach((message) => {
if (!message || !message.role || !message.content) return;
messages.push(message);
@@ -21,6 +21,7 @@ const {
DEFAULT_NAME,
DEFAULT_GATE_INTERVAL_MS,
DEFAULT_HEARTBEAT_MS,
DEFAULT_POST_CHAT_DELAY_MS,
MAX_RUN_HISTORY,
MAX_CHAT_CONTEXT,
MAX_BOT_CONTEXT,
@@ -40,6 +41,7 @@ const model = String(overseerConfig.model || '').trim();
const ollamaUrl = String(overseerConfig.ollamaUrl || overseerConfig.ollamaServer || '').trim();
const gateIntervalMs = normalizeMs(Number(overseerConfig.gateIntervalMs), DEFAULT_GATE_INTERVAL_MS);
const heartbeatMs = normalizeMs(Number(overseerConfig.heartbeatMs), DEFAULT_HEARTBEAT_MS);
const postChatDelayMs = normalizeMs(Number(overseerConfig.postChatDelayMs), DEFAULT_POST_CHAT_DELAY_MS);
const alwaysRunModel = Boolean(overseerConfig.alwaysRunModel);
const postToolsOnlyMessages = Boolean(overseerConfig.postToolsOnlyMessages);
const profileImageUrl = String(overseerConfig.profileImageUrl || '').trim() || null;
@@ -67,6 +69,7 @@ let status = {
promptPath: PROMPT_PATH,
gateIntervalMs,
heartbeatMs,
postChatDelayMs,
alwaysRunModel,
postToolsOnlyMessages,
running: false,
@@ -290,6 +293,20 @@ function buildToolCallFeedEntries(requestedActions = [], actionResults = []) {
});
}
function buildRecentEventsSummary() {
const events = (runtime.liveToolCalls || [])
.filter((entry) => entry && (entry.phase === 'ok' || entry.phase === 'error' || entry.phase === 'blocked'))
.slice(-3)
.map((entry) => {
const tool = String(entry.tool || 'unknown');
const phase = String(entry.phase || 'unknown');
const err = entry.error ? ` error=${String(entry.error).slice(0, 60)}` : '';
return `- tool=${tool} phase=${phase}${err}`;
});
if (!events.length) return '- none';
return events.join('\n');
}
async function runDecision(triggerReason) {
const runId = runtime.tickCount;
updateStatus({ phase: 'context_build', currentRunId: runId, lastTriggerReason: triggerReason, lastError: null, lastErrorDetails: null });
@@ -317,6 +334,7 @@ async function runDecision(triggerReason) {
systemPrompt,
stateUpdate,
memorySummary: summarizeMemory(runtime.memoryStore),
recentEvents: buildRecentEventsSummary(),
conversationMessages,
availableTools: toolState.available,
blockedTools: toolState.blocked,
@@ -360,6 +378,7 @@ async function runDecision(triggerReason) {
const actionResults = [];
const requestedActions = toolCalls;
let postedChat = false;
let outcome = observeOnly ? 'observed' : 'executed';
const reason = observeOnly ? 'observe-only mode' : null;
@@ -399,11 +418,13 @@ async function runDecision(triggerReason) {
if (toolCallFeed.length > 0) {
if ((decision === 'CHAT' || decision === 'ACTION+CHAT') && chatDraft) {
sendSystemMessage(chatDraft, { nickname: name, bot: true, profileImage: profileImageUrl, toolCalls: toolCallFeed });
postedChat = true;
} else if (postToolsOnlyMessages) {
sendSystemMessage('', { nickname: name, bot: true, profileImage: profileImageUrl, toolCalls: toolCallFeed });
}
} else if ((decision === 'CHAT' || decision === 'ACTION+CHAT') && chatDraft) {
sendSystemMessage(chatDraft, { nickname: name, bot: true, profileImage: profileImageUrl });
postedChat = true;
}
}
@@ -435,6 +456,8 @@ async function runDecision(triggerReason) {
generationMs,
blockedTools: toolState.blocked,
});
return { postedChat };
}
async function tick() {
@@ -442,12 +465,16 @@ async function tick() {
runtime.inFlight = true;
updateStatus({ inFlight: true, tickCount: runtime.tickCount, lastTickAt: Date.now(), phase: 'gate_check' });
let nextDelayMs = gateIntervalMs;
try {
const triggerReason = computeTriggerReason();
if (!triggerReason) {
updateStatus({ phase: 'idle', lastOutcome: 'skipped', lastReason: 'gate not triggered' });
} else {
await runDecision(triggerReason);
const runResult = await runDecision(triggerReason);
if (runResult?.postedChat) {
nextDelayMs = postChatDelayMs;
}
}
} catch (err) {
const failure = buildFailureInfo(err);
@@ -462,8 +489,8 @@ async function tick() {
} finally {
runtime.inFlight = false;
if (status.running) {
updateStatus({ inFlight: false, currentRunId: null, phase: 'idle', nextRunAt: Date.now() + gateIntervalMs });
runtime.timer = setTimeout(tick, gateIntervalMs);
updateStatus({ inFlight: false, currentRunId: null, phase: 'idle', nextRunAt: Date.now() + nextDelayMs });
runtime.timer = setTimeout(tick, nextDelayMs);
} else {
updateStatus({ inFlight: false, currentRunId: null, nextRunAt: null });
}