@byte5ai/palaia 2.0.5 → 2.0.7
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/package.json +1 -1
- package/src/hooks.ts +97 -19
package/package.json
CHANGED
package/src/hooks.ts
CHANGED
|
@@ -1154,44 +1154,122 @@ export function getLastUserMessage(messages: unknown[]): string | null {
|
|
|
1154
1154
|
}
|
|
1155
1155
|
|
|
1156
1156
|
// ============================================================================
|
|
1157
|
-
//
|
|
1157
|
+
// Channel Envelope Stripping (v2.0.6)
|
|
1158
1158
|
// ============================================================================
|
|
1159
1159
|
|
|
1160
1160
|
/**
|
|
1161
|
-
*
|
|
1161
|
+
* Strip OpenClaw channel envelope from message text.
|
|
1162
|
+
* Matches the pattern: [TIMESTAMP] or [CHANNEL TIMESTAMP] prefix
|
|
1163
|
+
* that OpenClaw adds to inbound messages from all channels.
|
|
1164
|
+
* Based on OpenClaw's internal stripEnvelope() logic.
|
|
1165
|
+
*/
|
|
1166
|
+
const ENVELOPE_PREFIX_RE = /^\[([^\]]+)\]\s*/;
|
|
1167
|
+
const ENVELOPE_CHANNELS = [
|
|
1168
|
+
"WebChat", "WhatsApp", "Telegram", "Signal", "Slack",
|
|
1169
|
+
"Discord", "Google Chat", "iMessage", "Teams", "Matrix",
|
|
1170
|
+
"Zalo", "Zalo Personal", "BlueBubbles",
|
|
1171
|
+
];
|
|
1172
|
+
|
|
1173
|
+
function looksLikeEnvelopeHeader(header: string): boolean {
|
|
1174
|
+
// ISO timestamp pattern
|
|
1175
|
+
if (/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}Z\b/.test(header)) return true;
|
|
1176
|
+
// Space-separated timestamp
|
|
1177
|
+
if (/\d{4}-\d{2}-\d{2} \d{2}:\d{2}\b/.test(header)) return true;
|
|
1178
|
+
// Channel prefix
|
|
1179
|
+
return ENVELOPE_CHANNELS.some(ch => header.startsWith(`${ch} `));
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
export function stripChannelEnvelope(text: string): string {
|
|
1183
|
+
const match = text.match(ENVELOPE_PREFIX_RE);
|
|
1184
|
+
if (!match) return text;
|
|
1185
|
+
if (!looksLikeEnvelopeHeader(match[1] ?? "")) return text;
|
|
1186
|
+
return text.slice(match[0].length);
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
/**
|
|
1190
|
+
* Strip "System: [timestamp] Channel message in #channel from User: " prefix.
|
|
1191
|
+
* OpenClaw wraps inbound messages with this pattern for all channel providers.
|
|
1192
|
+
*/
|
|
1193
|
+
const SYSTEM_PREFIX_RE = /^System:\s*\[\d{4}-\d{2}-\d{2}[^\]]*\]\s*(?:Slack message|Telegram message|Discord message|WhatsApp message|Signal message|message).*?(?:from \w+:\s*)?/i;
|
|
1194
|
+
|
|
1195
|
+
export function stripSystemPrefix(text: string): string {
|
|
1196
|
+
const match = text.match(SYSTEM_PREFIX_RE);
|
|
1197
|
+
if (match) return text.slice(match[0].length);
|
|
1198
|
+
return text;
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
// ============================================================================
|
|
1202
|
+
// Recall Query Builder (v2.0.6: envelope-aware, provenance-based)
|
|
1203
|
+
// ============================================================================
|
|
1204
|
+
|
|
1205
|
+
/**
|
|
1206
|
+
* Messages that are purely system content (no user text).
|
|
1207
|
+
* Used to skip edited notifications, sync events, inter-session messages, etc.
|
|
1208
|
+
*/
|
|
1209
|
+
function isSystemOnlyContent(text: string): boolean {
|
|
1210
|
+
if (!text) return true;
|
|
1211
|
+
if (text.startsWith("System:")) return true;
|
|
1212
|
+
if (text.startsWith("[Queued")) return true;
|
|
1213
|
+
if (text.startsWith("[Inter-session")) return true;
|
|
1214
|
+
if (/^Slack message (edited|deleted)/.test(text)) return true;
|
|
1215
|
+
if (/^\[auto\]/.test(text)) return true;
|
|
1216
|
+
if (text.length < 3) return true;
|
|
1217
|
+
return false;
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
/**
|
|
1221
|
+
* Build a recall query from message history.
|
|
1222
|
+
*
|
|
1223
|
+
* v2.0.6: Strips OpenClaw channel envelopes (System: [...] Slack message from ...:)
|
|
1224
|
+
* and inter-session prefixes before building the query. This prevents envelope
|
|
1225
|
+
* metadata from polluting semantic search and causing timeouts / false-high scores.
|
|
1162
1226
|
*
|
|
1163
|
-
* -
|
|
1227
|
+
* - Filters out inter_session and internal_system provenance messages.
|
|
1164
1228
|
* - Falls back to any user message for backward compat (OpenClaw without provenance).
|
|
1165
|
-
* -
|
|
1229
|
+
* - Strips channel envelopes and system prefixes from message text.
|
|
1230
|
+
* - Skips system-only content (edited notifications, sync events).
|
|
1231
|
+
* - Short messages (< 30 chars): prepends previous for context.
|
|
1166
1232
|
* - Hard-caps at 500 characters.
|
|
1167
|
-
*
|
|
1168
|
-
* Provenance makes the old heuristic cleaners (DAY_PREFIXES, system marker stripping) obsolete.
|
|
1169
1233
|
*/
|
|
1170
1234
|
export function buildRecallQuery(messages: unknown[]): string {
|
|
1171
1235
|
const texts = extractMessageTexts(messages);
|
|
1172
1236
|
|
|
1173
|
-
//
|
|
1174
|
-
const
|
|
1175
|
-
t => t.role === "user" && t.provenance
|
|
1237
|
+
// Step 1: Filter out inter_session messages (sub-agent results, sessions_send)
|
|
1238
|
+
const candidates = texts.filter(
|
|
1239
|
+
t => t.role === "user" && t.provenance !== "inter_session" && t.provenance !== "internal_system"
|
|
1176
1240
|
);
|
|
1177
1241
|
|
|
1178
|
-
// Fallback:
|
|
1179
|
-
const userMsgs =
|
|
1180
|
-
?
|
|
1242
|
+
// Fallback: if no messages without provenance, use all user messages
|
|
1243
|
+
const userMsgs = candidates.length > 0
|
|
1244
|
+
? candidates
|
|
1181
1245
|
: texts.filter(t => t.role === "user");
|
|
1182
1246
|
|
|
1183
1247
|
if (userMsgs.length === 0) return "";
|
|
1184
1248
|
|
|
1185
|
-
|
|
1249
|
+
// Step 2: Strip envelopes from the last user message(s)
|
|
1250
|
+
let lastText = stripSystemPrefix(stripChannelEnvelope(userMsgs[userMsgs.length - 1].text.trim()));
|
|
1186
1251
|
|
|
1187
|
-
//
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1252
|
+
// Skip system-only messages (edited notifications, sync events, etc.)
|
|
1253
|
+
// Walk backwards to find a message with actual content
|
|
1254
|
+
let idx = userMsgs.length - 1;
|
|
1255
|
+
while (idx >= 0 && (!lastText || isSystemOnlyContent(lastText))) {
|
|
1256
|
+
idx--;
|
|
1257
|
+
if (idx >= 0) {
|
|
1258
|
+
lastText = stripSystemPrefix(stripChannelEnvelope(userMsgs[idx].text.trim()));
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
if (!lastText) return "";
|
|
1263
|
+
|
|
1264
|
+
// Step 3: Short messages → include previous for context
|
|
1265
|
+
if (lastText.length < 30 && idx > 0) {
|
|
1266
|
+
const prevText = stripSystemPrefix(stripChannelEnvelope(userMsgs[idx - 1].text.trim()));
|
|
1267
|
+
if (prevText && !isSystemOnlyContent(prevText)) {
|
|
1268
|
+
return `${prevText} ${lastText}`.slice(0, 500);
|
|
1269
|
+
}
|
|
1192
1270
|
}
|
|
1193
1271
|
|
|
1194
|
-
return
|
|
1272
|
+
return lastText.slice(0, 500);
|
|
1195
1273
|
}
|
|
1196
1274
|
|
|
1197
1275
|
// ============================================================================
|