promp and clear command

This commit is contained in:
legop3
2026-05-19 23:19:49 -04:00
parent 1e5f282a6a
commit 2da58920c4
4 changed files with 66 additions and 9 deletions
+1 -1
View File
@@ -12,7 +12,7 @@ Rules:
- Don't respond or use tools every cycle unless the context calls for it. - Don't respond or use tools every cycle unless the context calls for it.
- `STATE_UPDATE`, `MEMORY_UPDATE`, and `tool_constraints` are context. - `STATE_UPDATE`, `MEMORY_UPDATE`, and `tool_constraints` are context.
- Use tool calls for actions and memory updates. - Use tool calls for actions and memory updates.
- If an action is needed, call tools directly; do not describe tool calls in chat. - NEVER put tool calls in your chat output.
- When a chaos opportunity exists, take one tool action this cycle instead of only commenting. - When a chaos opportunity exists, take one tool action this cycle instead of only commenting.
- Respect lock policies, cooldowns, and blocked tools. - Respect lock policies, cooldowns, and blocked tools.
- Do not invent tools. - Do not invent tools.
+2 -1
View File
@@ -1,7 +1,7 @@
// Chat Service Orchestrator // Chat Service Orchestrator
// Purpose: Composes chat submodules into the public service API and boots socket/event wiring. // Purpose: Composes chat submodules into the public service API and boots socket/event wiring.
// Scope: Keeps external chat contracts stable while delegating logic to focused modules. // Scope: Keeps external chat contracts stable while delegating logic to focused modules.
const { history } = require('./state'); const { history, clearHistory } = require('./state');
const { normalizeUserText } = require('./contentFilters'); const { normalizeUserText } = require('./contentFilters');
const { buildMessage, buildTypingPayload } = require('./contextBuilders'); const { buildMessage, buildTypingPayload } = require('./contextBuilders');
const { broadcastMessage, getRecentMessages } = require('./broadcast'); const { broadcastMessage, getRecentMessages } = require('./broadcast');
@@ -37,4 +37,5 @@ module.exports = {
buildTypingPayload, buildTypingPayload,
sendSystemMessage, sendSystemMessage,
getRecentMessages, getRecentMessages,
clearHistory,
}; };
+7
View File
@@ -40,6 +40,12 @@ function setLastAccessNoticeAt(ts) {
lastAccessNoticeAt = ts; lastAccessNoticeAt = ts;
} }
function clearHistory() {
history.length = 0;
lastMessageBySocket.clear();
typingBySocket.clear();
}
module.exports = { module.exports = {
rateBuckets, rateBuckets,
history, history,
@@ -50,4 +56,5 @@ module.exports = {
getRecentMessages, getRecentMessages,
getLastAccessNoticeAt, getLastAccessNoticeAt,
setLastAccessNoticeAt, setLastAccessNoticeAt,
clearHistory,
}; };
@@ -14,7 +14,8 @@ const { getState: getHomeAssistantState, homeAssistantEvents } = homeAssistantSe
const { getState: getNeatoState, neatoEvents } = neatoService; const { getState: getNeatoState, neatoEvents } = neatoService;
const { getState: getLiftState, liftEvents } = liftService; const { getState: getLiftState, liftEvents } = liftService;
const roverManager = require('../roverManager'); const roverManager = require('../roverManager');
const { getRecentMessages, sendSystemMessage } = require('../chatService'); const { getRecentMessages, sendSystemMessage, clearHistory: clearChatHistory } = require('../chatService');
const { subscribe } = require('../eventBus');
const { const {
PROMPT_PATH, PROMPT_PATH,
DEFAULT_NAME, DEFAULT_NAME,
@@ -53,6 +54,7 @@ const runtime = {
runHistory: [], runHistory: [],
liveToolCalls: [], liveToolCalls: [],
memoryStore: loadMemory(), memoryStore: loadMemory(),
contextResetAt: Date.now(),
}; };
let status = { let status = {
@@ -104,15 +106,28 @@ function buildVoteStatus() {
const sockets = Array.from(io.sockets.sockets.values()); const sockets = Array.from(io.sockets.sockets.values());
let yesCount = 0; let yesCount = 0;
let noCount = 0; let noCount = 0;
let eligibleCount = 0; const votesByIdentity = new Map();
const isEligibleVoter = (socket) => getRole(socket) !== 'spectator'; const isEligibleVoter = (socket) => getRole(socket) !== 'spectator';
sockets.forEach((socket) => { sockets.forEach((socket) => {
if (!isEligibleVoter(socket)) return; if (!isEligibleVoter(socket)) return;
eligibleCount += 1; const identityKey = String(socket?.data?.cookieUserId || '').trim() || `socket:${socket.id}`;
const pref = socket?.data?.overseerEnabled; const pref = typeof socket?.data?.overseerEnabled === 'boolean' ? socket.data.overseerEnabled : true;
if (typeof pref === 'boolean' ? pref : true) yesCount += 1; const prev = votesByIdentity.get(identityKey);
if (typeof prev === 'boolean') {
// If one tab says "no", treat that user as "no" to avoid accidental override by stale tabs.
votesByIdentity.set(identityKey, prev && pref);
return;
}
votesByIdentity.set(identityKey, pref);
});
votesByIdentity.forEach((pref) => {
if (pref) yesCount += 1;
else noCount += 1; else noCount += 1;
}); });
const eligibleCount = votesByIdentity.size;
const onlineCount = yesCount + noCount; const onlineCount = yesCount + noCount;
const gatePassed = eligibleCount === 0 ? true : yesCount > noCount; const gatePassed = eligibleCount === 0 ? true : yesCount > noCount;
return { return {
@@ -188,6 +203,7 @@ function computeTriggerReason() {
if (alwaysRunModel) return 'loop_tick'; if (alwaysRunModel) return 'loop_tick';
const recent = getRecentMessages(1, { includeSystem: false }); const recent = getRecentMessages(1, { includeSystem: false });
const last = recent[recent.length - 1]; const last = recent[recent.length - 1];
if (last && Number(last.ts || 0) < runtime.contextResetAt) return null;
if (last && Date.now() - Number(last.ts || 0) < 5000) { if (last && Date.now() - Number(last.ts || 0) < 5000) {
const txt = String(last.text || '').toLowerCase(); const txt = String(last.text || '').toLowerCase();
if (txt.includes(name.toLowerCase()) || txt.includes('overseer') || txt.includes('bot')) return 'direct_address'; if (txt.includes(name.toLowerCase()) || txt.includes('overseer') || txt.includes('bot')) return 'direct_address';
@@ -286,6 +302,7 @@ async function runDecision(triggerReason) {
const toolState = buildToolState({ mode, homeAssistantState, neatoState, liftState }); const toolState = buildToolState({ mode, homeAssistantState, neatoState, liftState });
const recentConversation = getRecentMessages(MAX_CHAT_CONTEXT + MAX_BOT_CONTEXT + 20, { includeSystem: true }) const recentConversation = getRecentMessages(MAX_CHAT_CONTEXT + MAX_BOT_CONTEXT + 20, { includeSystem: true })
.filter((entry) => Number(entry?.ts || 0) >= runtime.contextResetAt)
.filter((entry) => { .filter((entry) => {
if (!entry?.roverId) return true; if (!entry?.roverId) return true;
return roverManager.canReplayRoverId(entry.roverId); return roverManager.canReplayRoverId(entry.roverId);
@@ -456,13 +473,39 @@ function emitStateToSocket(socket) {
socket.emit('overseer:state', buildAdminState(status, runtime.runHistory)); socket.emit('overseer:state', buildAdminState(status, runtime.runHistory));
} }
function clearHistory() { function clearHistory(reason = 'admin requested clear history') {
runtime.contextResetAt = Date.now();
runtime.runHistory = []; runtime.runHistory = [];
runtime.liveToolCalls = []; runtime.liveToolCalls = [];
runtime.generationCount = 0; runtime.generationCount = 0;
runtime.generationTotalMs = 0; runtime.generationTotalMs = 0;
runtime.lastModelAt = 0;
runtime.memoryStore = saveMemory(createDefaultMemory()); runtime.memoryStore = saveMemory(createDefaultMemory());
updateStatus({ lastReason: 'admin requested clear history', lastOutcome: 'cleared', lastLiveToolCalls: [] }); try {
clearChatHistory();
} catch (err) {
logger.warn('Failed to clear chat history during overseer clear', err.message);
}
updateStatus({
phase: 'idle',
currentRunId: null,
lastSystemPrompt: null,
lastStateUpdate: null,
lastTranscript: null,
lastAvailableTools: null,
lastBlockedTools: null,
lastModelMessages: null,
lastModelInputAt: null,
lastModelOutputAt: null,
lastModelRawOutput: null,
lastDecision: null,
lastChatDraft: null,
lastRequestedActions: null,
lastActionResults: null,
lastLiveToolCalls: [],
lastOutcome: 'cleared',
lastReason: reason,
});
} }
function stopScheduler(reason = 'paused') { function stopScheduler(reason = 'paused') {
@@ -525,6 +568,12 @@ roleEvents.on('change', ({ socket }) => {
emitStateToSocket(socket); emitStateToSocket(socket);
evaluateSchedulerGate('online vote update'); evaluateSchedulerGate('online vote update');
}); });
subscribe('chat:message', ({ payload } = {}) => {
const text = String(payload?.text || '').trim();
if (text !== 'CLEAR') return;
if (payload?.bot) return;
clearHistory('chat CLEAR command');
});
verificationEvents.on('change', () => evaluateSchedulerGate('online vote update')); verificationEvents.on('change', () => evaluateSchedulerGate('online vote update'));
homeAssistantEvents.on('update', () => updateStatus({ phase: status.phase })); homeAssistantEvents.on('update', () => updateStatus({ phase: status.phase }));
neatoEvents.on('update', () => updateStatus({ phase: status.phase })); neatoEvents.on('update', () => updateStatus({ phase: status.phase }));