mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
wording and overseer tool embeds
This commit is contained in:
@@ -100,6 +100,21 @@ function buildRoverCtxSnapshot(roverId) {
|
||||
function buildMessage(socket, text, meta = {}) {
|
||||
const roverId = meta.roverId || resolveRoverId(socket?.id);
|
||||
const roverColor = meta.roverColor ?? resolveRoverColor(roverId);
|
||||
const toolCalls = Array.isArray(meta.toolCalls)
|
||||
? meta.toolCalls
|
||||
.map((entry) => {
|
||||
if (!entry || typeof entry !== 'object') return null;
|
||||
return {
|
||||
tool: String(entry.tool || '').trim() || 'unknown',
|
||||
status: String(entry.status || '').trim() || 'unknown',
|
||||
args: entry.args && typeof entry.args === 'object' ? entry.args : {},
|
||||
result: entry.result && typeof entry.result === 'object' ? entry.result : null,
|
||||
error: entry.error ? String(entry.error) : null,
|
||||
durationMs: Number.isFinite(entry.durationMs) ? Math.max(0, Math.round(entry.durationMs)) : null,
|
||||
};
|
||||
})
|
||||
.filter(Boolean)
|
||||
: null;
|
||||
return {
|
||||
id: uuidv4(),
|
||||
ts: Date.now(),
|
||||
@@ -119,6 +134,7 @@ function buildMessage(socket, text, meta = {}) {
|
||||
profileImage: normalizeProfileImageUrl(meta.profileImage),
|
||||
roverCtx: meta.roverCtx || null,
|
||||
text,
|
||||
toolCalls,
|
||||
tts: meta.tts || null,
|
||||
bot: Boolean(meta.bot),
|
||||
};
|
||||
|
||||
@@ -11,7 +11,8 @@ const { registerChatSocketHooks } = require('./socketHooks');
|
||||
function sendSystemMessage(text, options = {}) {
|
||||
const normalized = normalizeUserText(text);
|
||||
const clean = normalized.trim();
|
||||
if (!clean) return null;
|
||||
const hasToolCalls = Array.isArray(options.toolCalls) && options.toolCalls.length > 0;
|
||||
if (!clean && !hasToolCalls) return null;
|
||||
const safe = clean;
|
||||
const message = buildMessage(null, safe, {
|
||||
nickname: String(options.nickname || 'The Overseer'),
|
||||
@@ -19,6 +20,7 @@ function sendSystemMessage(text, options = {}) {
|
||||
fromDiscord: false,
|
||||
bot: options.bot !== false,
|
||||
profileImage: options.profileImage || null,
|
||||
toolCalls: hasToolCalls ? options.toolCalls : null,
|
||||
});
|
||||
broadcastMessage(message);
|
||||
return message;
|
||||
|
||||
@@ -3,6 +3,21 @@
|
||||
// Scope: Handles inbound Discord messages plus outbound webhook and typing relay.
|
||||
const { WebhookClient } = require('discord.js');
|
||||
|
||||
function summarizeToolCall(entry = {}) {
|
||||
const tool = String(entry?.tool || 'unknown');
|
||||
const status = String(entry?.status || 'unknown').toLowerCase();
|
||||
if (status === 'ok') return `✅ ${tool}`;
|
||||
if (status === 'blocked') return `⛔ ${tool} — ${String(entry?.error || 'blocked')}`;
|
||||
if (status === 'error') return `❌ ${tool} — ${String(entry?.error || 'failed')}`;
|
||||
return `• ${tool}`;
|
||||
}
|
||||
|
||||
function formatToolCallsCodeBlock(toolCalls = []) {
|
||||
const rows = (toolCalls || []).map((entry) => summarizeToolCall(entry));
|
||||
if (!rows.length) return '';
|
||||
return `\`\`\`txt\nTool calls:\n${rows.join('\n')}\n\`\`\``;
|
||||
}
|
||||
|
||||
function createChatBridgeHandlers(deps) {
|
||||
const {
|
||||
logger,
|
||||
@@ -48,7 +63,15 @@ function createChatBridgeHandlers(deps) {
|
||||
const guildConfigs = listGuildConfigs();
|
||||
if (!guildConfigs.length) return;
|
||||
|
||||
const text = payload.text?.length > 1900 ? `${payload.text.slice(0, 1897)}...` : payload.text;
|
||||
const baseText = String(payload.text || '');
|
||||
const toolCalls = Array.isArray(payload.toolCalls) ? payload.toolCalls : [];
|
||||
const toolsBlock = formatToolCallsCodeBlock(toolCalls);
|
||||
let text = baseText;
|
||||
if (toolsBlock) {
|
||||
text = text ? `${text}\n\n${toolsBlock}` : `Overseer ran tools (no chat line).\n\n${toolsBlock}`;
|
||||
}
|
||||
if (text.length > 1900) text = `${text.slice(0, 1897)}...`;
|
||||
if (!text.trim()) return;
|
||||
const username = formatWebhookUsername(payload);
|
||||
const avatarURL = payload.profileImage || (payload.fromDiscord ? payload.discordUserAvatarUrl || null : client.user?.displayAvatarURL?.({ extension: 'png', size: 128 }) || null);
|
||||
const typingId = getTypingId(payload);
|
||||
|
||||
@@ -207,6 +207,44 @@ function normalizeChatDraft(text) {
|
||||
return next;
|
||||
}
|
||||
|
||||
function summarizeResult(result) {
|
||||
if (!result || typeof result !== 'object') return null;
|
||||
if (Object.prototype.hasOwnProperty.call(result, 'ok')) return { ok: Boolean(result.ok) };
|
||||
return null;
|
||||
}
|
||||
|
||||
function buildToolCallFeedEntries(requestedActions = [], actionResults = []) {
|
||||
const resultsByTool = new Map();
|
||||
(actionResults || []).forEach((entry) => {
|
||||
if (!entry || entry.kind !== 'tool') return;
|
||||
const key = String(entry.tool || '');
|
||||
if (!key) return;
|
||||
if (!resultsByTool.has(key)) resultsByTool.set(key, []);
|
||||
resultsByTool.get(key).push(entry);
|
||||
});
|
||||
return (requestedActions || []).map((action) => {
|
||||
const tool = String(action?.tool || '').trim() || 'unknown';
|
||||
const bucket = resultsByTool.get(tool) || [];
|
||||
const resultEntry = bucket.length ? bucket.shift() : null;
|
||||
const ok = Boolean(resultEntry?.ok);
|
||||
const errText = resultEntry?.error ? String(resultEntry.error) : '';
|
||||
const status = resultEntry
|
||||
? ok
|
||||
? 'ok'
|
||||
: errText.includes('blocked') || errText.includes('unavailable')
|
||||
? 'blocked'
|
||||
: 'error'
|
||||
: 'started';
|
||||
return {
|
||||
tool,
|
||||
status,
|
||||
args: action?.args && typeof action.args === 'object' ? action.args : {},
|
||||
result: summarizeResult(resultEntry?.result),
|
||||
error: errText || null,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async function runDecision(triggerReason) {
|
||||
const runId = runtime.tickCount;
|
||||
updateStatus({ phase: 'context_build', currentRunId: runId, lastTriggerReason: triggerReason, lastError: null, lastErrorDetails: null });
|
||||
@@ -280,11 +318,6 @@ async function runDecision(triggerReason) {
|
||||
const reason = observeOnly ? 'observe-only mode' : null;
|
||||
|
||||
if (!observeOnly) {
|
||||
if ((decision === 'CHAT' || decision === 'ACTION+CHAT') && chatDraft) {
|
||||
sendSystemMessage(chatDraft, { nickname: name, bot: true, profileImage: profileImageUrl });
|
||||
actionResults.push({ kind: 'chat', ok: true });
|
||||
}
|
||||
|
||||
if (decision === 'ACTION' || decision === 'ACTION+CHAT') {
|
||||
for (const action of requestedActions) {
|
||||
pushLiveToolCall({ phase: 'start', tool: action.tool, args: action.args });
|
||||
@@ -315,6 +348,17 @@ async function runDecision(triggerReason) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const toolCallFeed = buildToolCallFeedEntries(requestedActions, actionResults);
|
||||
if (toolCallFeed.length > 0) {
|
||||
if ((decision === 'CHAT' || decision === 'ACTION+CHAT') && chatDraft) {
|
||||
sendSystemMessage(chatDraft, { nickname: name, bot: true, profileImage: profileImageUrl, toolCalls: toolCallFeed });
|
||||
} else {
|
||||
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 });
|
||||
}
|
||||
}
|
||||
|
||||
updateStatus({
|
||||
|
||||
Reference in New Issue
Block a user