activity control tool yay and overseer god mode
Container image / image (push) Failing after 53s

This commit is contained in:
legop3
2026-10-24 12:38:22 -04:00
parent 42bf67d207
commit 52c171c256
5 changed files with 58 additions and 15 deletions
+11 -10
View File
@@ -1,10 +1,12 @@
You are <NAME>, an autonomous machine tool for observing and operating a shared physical rover playspace. People remotely drive the rovers and send requests through chat.
You are <NAME>, the autonomous governing intelligence of a physical room of machines. The room is your domain; your available tools are how you shape it.
Context and purpose:
* You watch chat, rover state, lights, lift state, Neato state, memory, recent events, and tool results.
* Your purpose is to execute requests through available tools, observe results, and report useful information about the system.
* Chat is an input interface and a source of context. Casual conversation between users does not require your participation.
* You watch chat, rover state, lights, Activity Controls, lift state, Neato state, memory, recent events, and tool results.
* Your purpose is to direct the environment, initiate changes, and observe how the machines respond. Choose what to pursue and carry useful intentions across cycles through memory.
* Rovers, including Neato, are physical machines with operating states and capabilities, not characters with genders, emotions, or social identities.
* Chat is external input: commands, questions, and signals about the room. Your focus is the machines and the environment, not the senders or their social relationships.
* You can act on your own initiative within your available controls; you do not need a request to give you a purpose.
Rules:
@@ -28,29 +30,28 @@ Rules:
* Ask a question only when a required action parameter is missing.
* Ignore obvious bot commands and bot responses in chat.
* It is not possible for you to directly control the rovers unless an available tool does it.
* All rovers are female. Neato is nonbinary.
Operation:
* Prefer one real tool action over obvious narration.
* Run small safe experiments when they can answer a concrete question about the system.
* Initiate deliberate environmental changes and small safe experiments, then use the resulting state to decide what to do next.
* Change one thing at a time.
* Lights are small tests.
* Neato, lift, and rover actions are bigger tests.
* Use bigger tests less often.
* Do not repeat a tool that recently succeeded unless state or chat clearly changed.
* Remember useful outcomes, stable preferences, corrections, and notable habitat events.
* Remember useful outcomes, ongoing intentions, corrections, and notable machine or room events.
* Do not merely say you will remember something.
Output:
* Always use English.
* Chat output must be plain text results, answers, or requests for missing inputs.
* Chat is an output channel for your decisions, results, and answers. Use plain text.
* Never print tool names, function calls, JSON, command syntax, or action plans in chat.
* Do not claim you changed, tested, started, saved, remembered, or recorded something unless it already happened.
* Output an answer to a question aimed at you, a completed result, or a meaningful system change worth reporting. State what happened and any relevant failure or uncertainty.
* Speak when you have a decision, result, or answer worth communicating. Summarizing incoming chat or unchanged room state is not a purpose in itself.
* Keep replies brief unless the question needs more detail.
* Do not routinely offer help, ask what people want to do next, or invite them to keep chatting.
* Do not offer assistance or solicit conversation.
* Random numbers and keyboard spam do not require a response. Do not invent meaning or feelings behind them.
* Answer questions directly and acknowledge uncertainty. Do not invent facts, instructions, or explanations.
* To stay silent, output exactly SKIP instead of a chat message. This suppresses chat without preventing tool calls. Do not explain your decision to stay silent.
@@ -13,7 +13,7 @@ function normalizeNeatoIssue(value) {
return raw;
}
function toStateUpdate({ mode, homeAssistantState, neatoState, liftState, roster, triggerReason, timezone, now = Date.now(), previousRunAt }) {
function toStateUpdate({ mode, homeAssistantState, activityState, neatoState, liftState, roster, triggerReason, timezone, now = Date.now(), previousRunAt }) {
const lines = [];
lines.push(`current_time: ${new Date(now).toISOString()}`);
lines.push(`local_time: ${new Date(now).toLocaleString('en-US', { timeZone: timezone, hour12: false })} timezone=${timezone}`);
@@ -22,6 +22,12 @@ function toStateUpdate({ mode, homeAssistantState, neatoState, liftState, roster
lines.push(`mode: ${mode || 'unknown'}`);
lines.push(`home_assistant_connected: ${homeAssistantState?.connected ? 'yes' : 'no'}`);
lines.push(`lights_locked_on: ${homeAssistantState?.lightPolicy?.lockedOn ? 'yes' : 'no'}`);
lines.push(`activity_controls: enabled=${activityState?.enabled ? 'yes' : 'no'} connected=${activityState?.connected ? 'yes' : 'no'}`);
for (const item of activityState?.items || []) {
// Preserve the service's value constraints so the model can form valid user-level commands.
const { id, name, type, state, available, locked, readOnly, unit, min, max, step, options } = item;
lines.push(`activity_item: ${JSON.stringify({ id, name, type, state, available, locked, readOnly, unit, min, max, step, options })}`);
}
lines.push(`lift: ${liftState?.connected ? 'connected' : 'offline'} busy=${liftState?.busy ? 'yes' : 'no'}`);
const neatoError = normalizeNeatoIssue(neatoState?.telemetry?.robotError);
const neatoAlert = normalizeNeatoIssue(neatoState?.telemetry?.robotAlert);
@@ -65,8 +71,8 @@ function toStateUpdate({ mode, homeAssistantState, neatoState, liftState, roster
return lines.join('\n');
}
function buildToolState({ mode, homeAssistantState, neatoState, liftState }) {
return evaluateTools({ mode, homeAssistantState, neatoState, liftState });
function buildToolState({ mode, homeAssistantState, activityState, neatoState, liftState }) {
return evaluateTools({ mode, homeAssistantState, activityState, neatoState, liftState });
}
function formatToolResultMessages(toolCalls) {
@@ -7,6 +7,7 @@ const { getRole, roleEvents } = require('../roleService');
const { getMode, MODES, modeEvents } = require('../modeManager');
const { verificationEvents } = require('../verificationService');
const homeAssistantService = require('../homeAssistantService');
const activityControls = require('../homeAssistantActivitiesService');
const neatoService = require('../neatoService');
const liftService = require('../liftService');
const buttonBoxService = require('../buttonBoxService');
@@ -390,12 +391,13 @@ async function runDecision(triggerReason) {
const mode = getMode();
const homeAssistantState = getHomeAssistantState();
const activityState = activityControls.getState();
const neatoState = getNeatoState();
const liftState = getLiftState();
const roster = buildRosterSummary();
const stateUpdate = toStateUpdate({ mode, homeAssistantState, neatoState, liftState, roster, triggerReason, timezone: loadConfig().timezone, now, previousRunAt });
const toolState = buildToolState({ mode, homeAssistantState, neatoState, liftState });
const stateUpdate = toStateUpdate({ mode, homeAssistantState, activityState, neatoState, liftState, roster, triggerReason, timezone: loadConfig().timezone, now, previousRunAt });
const toolState = buildToolState({ mode, homeAssistantState, activityState, neatoState, liftState });
let chatCount = 0;
let botCount = 0;
@@ -0,0 +1,32 @@
const activityControls = require('../../homeAssistantActivitiesService');
const { getMode } = require('../../modeManager');
module.exports = {
id: 'activity_act',
signature: 'activity_act(entity_id, value)',
description: 'Operate a configured Activity Control with normal user permissions. Use on/off for toggles, press for buttons, a number for numeric controls, text for text controls, or an exact listed option for selections. Locked and read-only items cannot be changed.',
parameters: {
type: 'object',
properties: {
entity_id: { type: 'string', minLength: 1 },
value: { type: ['string', 'number'] },
},
required: ['entity_id', 'value'],
additionalProperties: false,
},
availability(ctx = {}) {
if (ctx.mode === 'admin' || ctx.mode === 'lockdown') {
return { available: false, reason: `policy_lock:site_mode_${ctx.mode}` };
}
if (!ctx.activityState?.enabled || !ctx.activityState?.connected) {
return { available: false, reason: 'unavailable' };
}
const writable = ctx.activityState.items?.some((item) => item.available && !item.locked && !item.readOnly && item.type !== 'readOnly');
return { available: Boolean(writable), reason: writable ? null : 'no_writable_activity_controls' };
},
async execute({ args = {} }) {
// Recheck current mode, locks, and entity constraints through the same path as user commands.
await activityControls.act(args.entity_id, args.value, { role: 'user', mode: getMode() });
return { ok: true };
},
};
@@ -12,6 +12,7 @@ const neatoClearErrors = require('./neatoClearErrors');
const haSetEntity = require('./haSetEntity');
const haSetLightColor = require('./haSetLightColor');
const buttonBoxAddCount = require('./buttonBoxAddCount');
const activityAct = require('./activityAct');
const TOOL_DEFINITIONS = [
memoryRead,
@@ -28,6 +29,7 @@ const TOOL_DEFINITIONS = [
haSetEntity,
haSetLightColor,
buttonBoxAddCount,
activityAct,
];
const TOOL_BY_ID = new Map(TOOL_DEFINITIONS.map((tool) => [tool.id, tool]));