oomi-ai 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/oomi-ai.js +131 -0
- package/openclaw.extension.js +50 -1
- package/package.json +1 -1
package/bin/oomi-ai.js
CHANGED
|
@@ -1355,6 +1355,127 @@ function extractTextFromGatewayMessage(message) {
|
|
|
1355
1355
|
.join(' ');
|
|
1356
1356
|
}
|
|
1357
1357
|
|
|
1358
|
+
function normalizeWhitespace(value) {
|
|
1359
|
+
return String(value || '')
|
|
1360
|
+
.replace(/[ \t]+\n/g, '\n')
|
|
1361
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
1362
|
+
.replace(/[ \t]{2,}/g, ' ')
|
|
1363
|
+
.trim();
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
function sanitizeUserVisibleText(value) {
|
|
1367
|
+
let text = String(value || '');
|
|
1368
|
+
if (!text.trim()) return '';
|
|
1369
|
+
|
|
1370
|
+
text = text.replace(/<think>[\s\S]*?<\/think>/gi, ' ');
|
|
1371
|
+
if (/<think>/i.test(text)) {
|
|
1372
|
+
text = text.replace(/<think>[\s\S]*$/i, ' ');
|
|
1373
|
+
}
|
|
1374
|
+
text = text.replace(/<\/think>/gi, ' ');
|
|
1375
|
+
|
|
1376
|
+
text = text.replace(/(?:^|\n)System \(untrusted\):[\s\S]*?(?=\n\n|$)/gi, '\n');
|
|
1377
|
+
text = text.replace(
|
|
1378
|
+
/(?:^|\n)An async command you ran earlier has completed\.[\s\S]*?(?:\nCurrent time:[^\n]*(?:\n|$)|$)/gi,
|
|
1379
|
+
'\n'
|
|
1380
|
+
);
|
|
1381
|
+
|
|
1382
|
+
return normalizeWhitespace(text);
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
function shouldSuppressOomiVisibleText(value) {
|
|
1386
|
+
const text = String(value || '').trim();
|
|
1387
|
+
if (!text) return true;
|
|
1388
|
+
|
|
1389
|
+
if (/System \(untrusted\):/i.test(text)) return true;
|
|
1390
|
+
if (/An async command you ran earlier has completed\./i.test(text)) return true;
|
|
1391
|
+
if (/Handle the result internally\. Do not relay it to the user/i.test(text)) return true;
|
|
1392
|
+
if (/Exec (failed|completed) \([^)]+\) ::/i.test(text)) return true;
|
|
1393
|
+
if (/^\s*<think>[\s\S]*$/i.test(text) && !/<\/think>/i.test(text)) return true;
|
|
1394
|
+
|
|
1395
|
+
return !sanitizeUserVisibleText(text);
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
function sanitizeGatewayMessageForOomi(message) {
|
|
1399
|
+
if (!message || typeof message !== 'object') return null;
|
|
1400
|
+
|
|
1401
|
+
const role = typeof message.role === 'string' ? message.role.trim().toLowerCase() : '';
|
|
1402
|
+
if (role && !['assistant', 'user'].includes(role)) {
|
|
1403
|
+
return null;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
const rawText = extractTextFromGatewayMessage(message);
|
|
1407
|
+
if (shouldSuppressOomiVisibleText(rawText)) {
|
|
1408
|
+
return null;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
const content = sanitizeUserVisibleText(rawText);
|
|
1412
|
+
if (!content) return null;
|
|
1413
|
+
|
|
1414
|
+
return {
|
|
1415
|
+
...message,
|
|
1416
|
+
role: role || 'assistant',
|
|
1417
|
+
content,
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
function sanitizeGatewayFrameForOomiClient(frameText) {
|
|
1422
|
+
const frame = parseJsonPayload(frameText);
|
|
1423
|
+
if (!frame || typeof frame !== 'object') {
|
|
1424
|
+
return { frameText, changed: false, suppressed: false };
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
if (frame.type === 'res' && frame.payload && typeof frame.payload === 'object' && Array.isArray(frame.payload.messages)) {
|
|
1428
|
+
const nextMessages = frame.payload.messages
|
|
1429
|
+
.map((message) => sanitizeGatewayMessageForOomi(message))
|
|
1430
|
+
.filter(Boolean);
|
|
1431
|
+
const nextFrame = {
|
|
1432
|
+
...frame,
|
|
1433
|
+
payload: {
|
|
1434
|
+
...frame.payload,
|
|
1435
|
+
messages: nextMessages,
|
|
1436
|
+
},
|
|
1437
|
+
};
|
|
1438
|
+
const nextFrameText = JSON.stringify(nextFrame);
|
|
1439
|
+
return {
|
|
1440
|
+
frameText: nextFrameText,
|
|
1441
|
+
changed: nextFrameText !== frameText,
|
|
1442
|
+
suppressed: nextMessages.length < frame.payload.messages.length,
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
if (frame.type === 'event' && frame.event === 'chat') {
|
|
1447
|
+
const payload = frame.payload && typeof frame.payload === 'object' ? frame.payload : null;
|
|
1448
|
+
const message = payload?.message && typeof payload.message === 'object' ? payload.message : null;
|
|
1449
|
+
if (!payload || !message) {
|
|
1450
|
+
return { frameText, changed: false, suppressed: false };
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
const sanitizedMessage = sanitizeGatewayMessageForOomi(message);
|
|
1454
|
+
const nextFrame = {
|
|
1455
|
+
...frame,
|
|
1456
|
+
payload: {
|
|
1457
|
+
...payload,
|
|
1458
|
+
message: sanitizedMessage || {
|
|
1459
|
+
...message,
|
|
1460
|
+
content: '',
|
|
1461
|
+
metadata: {
|
|
1462
|
+
...(message.metadata && typeof message.metadata === 'object' && !Array.isArray(message.metadata) ? message.metadata : {}),
|
|
1463
|
+
oomiSuppressed: true,
|
|
1464
|
+
},
|
|
1465
|
+
},
|
|
1466
|
+
},
|
|
1467
|
+
};
|
|
1468
|
+
const nextFrameText = JSON.stringify(nextFrame);
|
|
1469
|
+
return {
|
|
1470
|
+
frameText: nextFrameText,
|
|
1471
|
+
changed: nextFrameText !== frameText,
|
|
1472
|
+
suppressed: !sanitizedMessage,
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
return { frameText, changed: false, suppressed: false };
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1358
1479
|
function summarizeVoiceFrameContract(frameText) {
|
|
1359
1480
|
const frame = parseJsonPayload(frameText);
|
|
1360
1481
|
if (!frame || typeof frame !== 'object') {
|
|
@@ -2997,6 +3118,13 @@ async function startOpenclawBridge(flags) {
|
|
|
2997
3118
|
|
|
2998
3119
|
gatewaySocket.on('message', runBridgeCallbackSafely((gatewayRaw) => {
|
|
2999
3120
|
let frame = typeof gatewayRaw === 'string' ? gatewayRaw : gatewayRaw.toString();
|
|
3121
|
+
const visibleSanitized = sanitizeGatewayFrameForOomiClient(frame);
|
|
3122
|
+
if (visibleSanitized.changed) {
|
|
3123
|
+
frame = visibleSanitized.frameText;
|
|
3124
|
+
bridgeDebugLog(
|
|
3125
|
+
`[bridge] oomi.visible_sanitized ${sessionId} suppressed=${visibleSanitized.suppressed ? 'yes' : 'no'}`
|
|
3126
|
+
);
|
|
3127
|
+
}
|
|
3000
3128
|
const spokenNormalized = normalizeAssistantGatewayFrame(sessionId, frame);
|
|
3001
3129
|
if (spokenNormalized.changed) {
|
|
3002
3130
|
frame = spokenNormalized.frameText;
|
|
@@ -4213,6 +4341,9 @@ if (__isDirectExecution) {
|
|
|
4213
4341
|
|
|
4214
4342
|
export {
|
|
4215
4343
|
prepareGatewayFrameForLocalGateway,
|
|
4344
|
+
sanitizeGatewayFrameForOomiClient,
|
|
4345
|
+
sanitizeUserVisibleText,
|
|
4346
|
+
shouldSuppressOomiVisibleText,
|
|
4216
4347
|
ensureAssistantSpokenMetadata,
|
|
4217
4348
|
normalizeAssistantGatewayFrame,
|
|
4218
4349
|
runAssistantFinalDebugCheck,
|
package/openclaw.extension.js
CHANGED
|
@@ -107,6 +107,46 @@ function extractText(payload) {
|
|
|
107
107
|
return '';
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
function normalizeWhitespace(value) {
|
|
111
|
+
return String(value || '')
|
|
112
|
+
.replace(/[ \t]+\n/g, '\n')
|
|
113
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
114
|
+
.replace(/[ \t]{2,}/g, ' ')
|
|
115
|
+
.trim();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function sanitizeUserVisibleText(value) {
|
|
119
|
+
let text = String(value || '');
|
|
120
|
+
if (!text.trim()) return '';
|
|
121
|
+
|
|
122
|
+
text = text.replace(/<think>[\s\S]*?<\/think>/gi, ' ');
|
|
123
|
+
if (/<think>/i.test(text)) {
|
|
124
|
+
text = text.replace(/<think>[\s\S]*$/i, ' ');
|
|
125
|
+
}
|
|
126
|
+
text = text.replace(/<\/think>/gi, ' ');
|
|
127
|
+
|
|
128
|
+
text = text.replace(/(?:^|\n)System \(untrusted\):[\s\S]*?(?=\n\n|$)/gi, '\n');
|
|
129
|
+
text = text.replace(
|
|
130
|
+
/(?:^|\n)An async command you ran earlier has completed\.[\s\S]*?(?:\nCurrent time:[^\n]*(?:\n|$)|$)/gi,
|
|
131
|
+
'\n'
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
return normalizeWhitespace(text);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function shouldSuppressOomiVisibleText(value) {
|
|
138
|
+
const text = String(value || '').trim();
|
|
139
|
+
if (!text) return true;
|
|
140
|
+
|
|
141
|
+
if (/System \(untrusted\):/i.test(text)) return true;
|
|
142
|
+
if (/An async command you ran earlier has completed\./i.test(text)) return true;
|
|
143
|
+
if (/Handle the result internally\. Do not relay it to the user/i.test(text)) return true;
|
|
144
|
+
if (/Exec (failed|completed) \([^)]+\) ::/i.test(text)) return true;
|
|
145
|
+
if (/^\s*<think>[\s\S]*$/i.test(text) && !/<\/think>/i.test(text)) return true;
|
|
146
|
+
|
|
147
|
+
return !sanitizeUserVisibleText(text);
|
|
148
|
+
}
|
|
149
|
+
|
|
110
150
|
function extractConversationKey(payload) {
|
|
111
151
|
const candidates = [
|
|
112
152
|
payload?.conversationKey,
|
|
@@ -290,7 +330,16 @@ const oomiChannelPlugin = {
|
|
|
290
330
|
};
|
|
291
331
|
}
|
|
292
332
|
|
|
293
|
-
const
|
|
333
|
+
const rawContent = extractText(payload);
|
|
334
|
+
if (shouldSuppressOomiVisibleText(rawContent)) {
|
|
335
|
+
return {
|
|
336
|
+
ok: true,
|
|
337
|
+
suppressed: true,
|
|
338
|
+
reason: 'internal_content',
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const content = sanitizeUserVisibleText(rawContent);
|
|
294
343
|
if (!content) {
|
|
295
344
|
return {
|
|
296
345
|
ok: false,
|