Extract global agent doctrine source

This commit is contained in:
Meta Vens 2026-07-16 03:58:02 +01:00
commit 4617f3b320
7 changed files with 89 additions and 41 deletions

View file

@ -1,7 +1,6 @@
import "dotenv/config";
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { homedir } from "node:os";
import { dirname, resolve } from "node:path";
import { loadConfig } from "@voidbot/config";
@ -63,6 +62,7 @@ import type { PersonaHumanPronounGuidance as RepoFaceHumanPronounGuidance } from
import { projectPersonaStatePacket } from "../apps/persona-scheduler/dist/persona-state-packet-projector.js";
import { projectPersonaText } from "../apps/persona-scheduler/dist/persona-text-projection-actuator.js";
import { assemblePersonaTurn } from "../apps/persona-scheduler/dist/persona-turn-assembler.js";
import { readGlobalAgentDoctrine } from "../apps/persona-scheduler/dist/global-agent-doctrine-source.js";
import { readPersonaHumanPronounGuidance } from "../apps/persona-scheduler/dist/persona-social-context-source.js";
import {
readDiscordActivitySnapshot,
@ -431,7 +431,7 @@ async function queueRepoFaceTurn(input: {
const repoActivitySurface = identity.identityKind === "native_persona"
? projectNativePersonaBody(identity)
: renderRepoActivityObservation(readRepoActivity({ identity, storageRoot: input.config.storageRoot }));
const globalAgentDoctrine = await loadGlobalAgentDoctrine();
const globalAgentDoctrine = await readGlobalAgentDoctrine({ codexHome: process.env.CODEX_HOME, userProfile: process.env.USERPROFILE });
const assembly = assemblePersonaTurn({
identity,
channelId,
@ -568,7 +568,7 @@ async function assembleRepoFaceTurnPrompt(input: {
recentMessages,
channelSnapshots,
}));
const globalAgentDoctrine = await loadGlobalAgentDoctrine();
const globalAgentDoctrine = await readGlobalAgentDoctrine({ codexHome: process.env.CODEX_HOME, userProfile: process.env.USERPROFILE });
const conversationMemorySurface = input.conversationSurfacePath
? await readOptionalMemorySurface(input.conversationSurfacePath)
: undefined;
@ -610,39 +610,6 @@ async function assembleRepoFaceTurnPrompt(input: {
};
}
async function loadGlobalAgentDoctrine(): Promise<string> {
const candidates = [
process.env.CODEX_HOME ? resolve(process.env.CODEX_HOME, "AGENTS.md") : undefined,
process.env.USERPROFILE ? resolve(process.env.USERPROFILE, ".codex", "AGENTS.md") : undefined,
resolve(homedir(), ".codex", "AGENTS.md"),
].filter((value): value is string => typeof value === "string" && value.trim().length > 0);
const uniqueCandidates = [...new Set(candidates)];
const errors: string[] = [];
for (const candidate of uniqueCandidates) {
try {
const content = await readFile(candidate, "utf8");
if (content.trim().length > 0) {
return content.trim();
}
errors.push(`${candidate}: empty file`);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
errors.push(`${candidate}: ${message}`);
}
}
return [
"# Global Agent Instructions Unavailable",
"",
"The Face prompt attempted to load the global Codex AGENTS.md file, but no readable file was found.",
"This is not a replacement doctrine. Treat it as an inspection failure and avoid claiming global guidance was available for this turn.",
"",
"Attempted paths:",
...errors.map((error) => `- ${error}`),
].join("\n");
}
async function renderRepoFaceMemorySurfaceForTurn(
identity: RepoDiscordIdentity,
config: ReturnType<typeof loadConfig>,

View file

@ -72,6 +72,7 @@ import { projectPersonaConversation, renderPersonaRoomTopicSaturation } from "..
import { buildPersonaJurisdictionDiveDirective, buildPersonaTurnPrompt } from "../apps/persona-scheduler/dist/persona-turn-prompt-projector.js";
import { assemblePersonaTurn } from "../apps/persona-scheduler/dist/persona-turn-assembler.js";
import { buildVoidModerationLaunchCommand, launchVoidModerationTurn, waitForVoidModerationHandshake } from "../apps/persona-scheduler/dist/void-moderation-turn-actuator.js";
import { readGlobalAgentDoctrine } from "../apps/persona-scheduler/dist/global-agent-doctrine-source.js";
import { projectGamecultPersonaState } from "../apps/persona-scheduler/dist/persona-standard-state-projector.js";
import { projectPersonaStatePacket } from "../apps/persona-scheduler/dist/persona-state-packet-projector.js";
import { extractLastPersonaProjectionMessage, isRetryablePersonaProjectionFailure } from "../apps/persona-scheduler/dist/persona-text-projection-actuator.js";
@ -680,7 +681,7 @@ const projectedTurnPrompt = buildPersonaTurnPrompt({
pendingMentions: [{ messageId: "mention", channelId: "aquarium", authorId: "human-2", authorName: "Neighbor", visiblePrompt: "Nibu, answer this", queuedAt: "2026-07-15T21:10:00.000Z" }],
jurisdictionDive,
githubActionsEnabled: false,
globalAgentDoctrine: "Global doctrine.",
globalAgentDoctrine: { status: "ok", path: "C:/Codex/AGENTS.md", doctrine: "Global doctrine.", attemptedPaths: ["C:/Codex/AGENTS.md"] },
});
assert.match(projectedTurnPrompt, /A direct call is tugging at you[\s\S]*Nibu, answer this/, "the pure turn-prompt projector owns situation and pending-call policy from supplied facts");
const assembledTurn = assemblePersonaTurn({
@ -711,13 +712,44 @@ const assembledTurn = assemblePersonaTurn({
pendingMentions: [],
conversationMemorySurface: "Inspection supplied conversation authority.",
githubActionsEnabled: false,
globalAgentDoctrine: "Global doctrine.",
globalAgentDoctrine: { status: "ok", path: "C:/Codex/AGENTS.md", doctrine: "Global doctrine.", attemptedPaths: ["C:/Codex/AGENTS.md"] },
});
assert.match(assembledTurn.prompt, /Inspection supplied conversation authority\./, "queued and inspection paths share one assembler with an explicit conversation override");
assert.doesNotMatch(assembledTurn.prompt, /Visible cross-channel chronology/, "the assembler does not let its derived transcript override an explicit inspection surface");
assert.equal(assembledTurn.imageAttachments.length, 8, "the assembler owns the bounded image evidence budget");
assert.equal(assembledTurn.imageAttachments.filter((attachment) => attachment.localPath === "C:/cache/same.png").length, 1, "the assembler deduplicates image evidence across room and nearby snapshots");
assert.equal(assembledTurn.conversation.focus?.channelId, "aquarium", "the assembler returns the same newest-evidence conversation projection supplied to the turn actuator");
const doctrineReads: string[] = [];
const doctrineObservation = await readGlobalAgentDoctrine({
codexHome: "C:/Codex",
userProfile: "C:/User",
homeDirectory: "C:/User",
readText: async (path) => {
doctrineReads.push(path);
if (path.endsWith("Codex\\AGENTS.md")) throw new Error("missing");
return "Global doctrine from the first readable authority.";
},
});
assert.equal(doctrineObservation.status, "ok", "the doctrine source reports the first readable authority as typed evidence");
assert.equal(doctrineReads.length, 2, "the doctrine source stops after the first readable candidate and deduplicates repeated home paths");
const unavailableDoctrine = await readGlobalAgentDoctrine({
codexHome: "C:/Absent",
homeDirectory: "C:/AlsoAbsent",
readText: async () => { throw new Error("not found"); },
});
assert.equal(unavailableDoctrine.status, "unavailable", "missing global doctrine remains an explicit source failure");
const unavailableDoctrineTurn = assemblePersonaTurn({
identity: routedIdentity,
channelId: "lore",
channelPlan,
channelSnapshots: [],
recentMessages: [],
participant: promptParticipant,
pendingMentions: [],
githubActionsEnabled: false,
globalAgentDoctrine: unavailableDoctrine,
});
assert.match(unavailableDoctrineTurn.prompt, /Global Agent Instructions Unavailable[\s\S]*inspection failure/, "the assembler, not the source or shell, projects unavailable doctrine into the final prompt");
const moderationLaunchCommand = buildVoidModerationLaunchCommand({
pendingMentionsPath: "C:/Void's state/pending.json",
runnerScript: "C:/VoidBot/scripts/run.ps1",