diff --git a/apps/worker/src/index.ts b/apps/worker/src/index.ts index 5761f0a..ed0fb36 100644 --- a/apps/worker/src/index.ts +++ b/apps/worker/src/index.ts @@ -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": diff --git a/prompts/repo-face-turn-interpreter.prompt.md b/prompts/repo-face-turn-interpreter.prompt.md index 91bb0da..31030f9 100644 --- a/prompts/repo-face-turn-interpreter.prompt.md +++ b/prompts/repo-face-turn-interpreter.prompt.md @@ -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: diff --git a/state/evidence.jsonl b/state/evidence.jsonl index ea250de..d67bfea 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -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."}