Harden Face interpreter action parsing

This commit is contained in:
Meta Vens 2026-05-22 22:35:05 +01:00
commit a3915775ec
3 changed files with 32 additions and 4 deletions

View file

@ -62,6 +62,22 @@ const REPO_IDENTITY_PROPOSAL_PR_SENTINEL = "VOIDBOT_REPO_IDENTITY_PROPOSAL_PR:";
const REPO_IDENTITY_PR_COMMENT_SENTINEL = "VOIDBOT_REPO_IDENTITY_PR_COMMENT:";
const REPO_IDENTITY_UPDATE_REQUEST_SENTINEL = "VOIDBOT_REPO_IDENTITY_UPDATE_REQUEST:";
const REPO_IDENTITY_BIFROST_TOPIC_SENTINEL = "VOIDBOT_REPO_IDENTITY_BIFROST_TOPIC:";
const CURRENT_REPO_FACE_IDENTITY_SELECTORS = new Set([
"face_id",
"faceid",
"current_face",
"current_face_id",
"currentface",
"currentfaceid",
"current_identity",
"current_identity_id",
"currentidentity",
"currentidentityid",
"job_identity",
"job_identity_id",
"jobidentity",
"jobidentityid",
]);
const REPO_FACE_FORBIDDEN_CHILD_TOOLS = new Set([
"read_repo_face_state",
"list_mcp_resources",
@ -1118,9 +1134,11 @@ async function resolveRepoIdentityForJobIntent(
const jobIdentityId = job.command === "repo-face-rumination"
? parseRepoIdentityIdFromRequestMessageId(job.requestMessageId) ?? parseRepoIdentityIdFromPrompt(job.prompt)
: undefined;
const identitySelectorIsCurrentFaceAlias = isCurrentRepoFaceIdentitySelector(identitySelector);
if (
jobIdentityId &&
identitySelector &&
!identitySelectorIsCurrentFaceAlias &&
normalizeChannelSelector(jobIdentityId) !== normalizeChannelSelector(identitySelector)
) {
console.warn(
@ -1129,7 +1147,7 @@ async function resolveRepoIdentityForJobIntent(
}
const identityId =
jobIdentityId ??
identitySelector ??
(identitySelectorIsCurrentFaceAlias ? undefined : identitySelector) ??
parseRepoIdentityIdFromPrompt(job.prompt) ??
parseRepoIdentityIdFromRequestMessageId(job.requestMessageId);
if (!identityId) {
@ -2019,6 +2037,10 @@ function parseRepoFaceActionBlocks(finalResponse: string): RepoFaceActionBlock[]
const bodyLines: string[] = [];
index += 1;
while (index < lines.length && lines[index].trim() !== "END") {
if (parseRepoFaceActionKind(lines[index])) {
index -= 1;
break;
}
bodyLines.push(lines[index]);
index += 1;
}
@ -2030,6 +2052,11 @@ function parseRepoFaceActionBlocks(finalResponse: string): RepoFaceActionBlock[]
return blocks;
}
function isCurrentRepoFaceIdentitySelector(identitySelector: string | undefined): boolean {
const normalized = identitySelector ? normalizeChannelSelector(identitySelector) : "";
return normalized.length > 0 && CURRENT_REPO_FACE_IDENTITY_SELECTORS.has(normalized);
}
function parseRepoFaceActionKind(line: string): RepoFaceActionBlock["kind"] | undefined {
switch (line.trim().toUpperCase()) {
case "SAY":

View file

@ -88,7 +88,7 @@ Normalized output rules:
- SAY content must be one compact, complete Discord message. Do not end with `...` or an unfinished word.
STATE NOTE
identity: face_id
identity: current_face_id
kind: memory|need|bond|status|mood|agency
target: person_repo_room_topic_or_self
summary:
@ -109,7 +109,7 @@ valence: -1.0_to_1.0
END
ARTICLE
identity: face_id
identity: current_face_id
site: aetheria|gamecult
title:
Article title.
@ -132,7 +132,7 @@ body:
END
SAY
identity: face_id
identity: current_face_id
channel: channel_id
reply_to: message_id_or_blank
content:

View file

@ -286,3 +286,4 @@
{"ts":"2026-05-22T19:48:34Z","type":"fix","status":"ok","note":"Changed Void self-state projection so durable memory is not recency-only recall. Normal context now renders full detail for the newest/salient memories plus compact anchors for the remaining active durable memories; Void's current rendered self-state summary grows from about 14KB to 18KB while keeping all 25 durable memories visible by target/summary/tags."}
{"ts":"2026-05-22T20:04:28Z","type":"implementation","status":"ok","note":"Face name introductions now enqueue CTB obligations by whole-message exact display-name matching, including channels outside regular autonomous Face chatter; scheduler carries the source channel snapshot into the next Face turn."}
{"ts":"2026-05-22T21:11:19Z","type":"implementation","status":"ok","note":"Live retry thawed swarm, proved multi-Face name-intro obligations can dispatch, and exposed avatar presentation as registry data: Kiko/Weksa/Huginn need avatarUrl entries backed by public repo assets for webhook persona faces."}
{"ts":"2026-05-22T21:34:48Z","type":"fix","status":"ok","note":"Repaired Face Interpreter DSL routing after live silence: parser now ends a block when a new action header starts so unterminated STATE NOTE blocks cannot swallow SAY blocks; current_face_id/face_id aliases resolve to the current repo Face identity. Weksa's missed summons was an interrupted job caused by stack restart between Face output and Interpreter routing, not the swallowed-SAY parser case."}