Isolate Persona memory projection policy
This commit is contained in:
parent
d55f25cd4d
commit
85d60b6155
7 changed files with 88 additions and 52 deletions
|
|
@ -62,6 +62,7 @@ import {
|
|||
readPersonaMemoryRecall,
|
||||
type PersonaMemoryRecallObservation,
|
||||
} from "../apps/persona-scheduler/dist/persona-memory-context-source.js";
|
||||
import { projectPersonaMemorySurface } from "../apps/persona-scheduler/dist/persona-memory-projector.js";
|
||||
import {
|
||||
readDiscordActivitySnapshot,
|
||||
type IdleCoolingSnapshot,
|
||||
|
|
@ -878,13 +879,26 @@ async function renderRepoFaceMemorySurfaceForTurn(
|
|||
curiosityGraphFacts,
|
||||
);
|
||||
if (!config.repoFaceHeartbeats.stateProjectorEnabled) {
|
||||
return rejectLeakyMemorySurface(statePacket);
|
||||
return projectPersonaMemorySurface({
|
||||
identityId: identity.id,
|
||||
characterIdentity: renderRepoCharacterIdentityDoctrine(identity),
|
||||
statePacket,
|
||||
modelProjectionEnabled: false,
|
||||
});
|
||||
}
|
||||
|
||||
return projectRepoFaceMemorySurface({
|
||||
identity,
|
||||
return projectPersonaMemorySurface({
|
||||
identityId: identity.id,
|
||||
characterIdentity: renderRepoCharacterIdentityDoctrine(identity),
|
||||
statePacket,
|
||||
config,
|
||||
modelProjectionEnabled: true,
|
||||
projectText: (prompt) => runCodexTextProjection({
|
||||
prompt,
|
||||
config,
|
||||
command: "repo-face-state-projector",
|
||||
jobId: `state-projector:${identity.id}:${Date.now()}`,
|
||||
timeoutMs: 180_000,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -950,12 +964,25 @@ async function renderNativePersonaMemorySurface(
|
|||
humanPronounGuidance,
|
||||
);
|
||||
if (!config.repoFaceHeartbeats.stateProjectorEnabled) {
|
||||
return rejectLeakyMemorySurface(statePacket);
|
||||
return projectPersonaMemorySurface({
|
||||
identityId: identity.id,
|
||||
characterIdentity: renderRepoCharacterIdentityDoctrine(identity),
|
||||
statePacket,
|
||||
modelProjectionEnabled: false,
|
||||
});
|
||||
}
|
||||
return projectRepoFaceMemorySurface({
|
||||
identity,
|
||||
return projectPersonaMemorySurface({
|
||||
identityId: identity.id,
|
||||
characterIdentity: renderRepoCharacterIdentityDoctrine(identity),
|
||||
statePacket,
|
||||
config,
|
||||
modelProjectionEnabled: true,
|
||||
projectText: (prompt) => runCodexTextProjection({
|
||||
prompt,
|
||||
config,
|
||||
command: "repo-face-state-projector",
|
||||
jobId: `state-projector:${identity.id}:${Date.now()}`,
|
||||
timeoutMs: 180_000,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -2912,47 +2939,6 @@ function joinAsNarrativeList(items: string[]): string {
|
|||
return `${items.slice(0, -1).join(", ")}, and ${items[items.length - 1]}`;
|
||||
}
|
||||
|
||||
function rejectLeakyMemorySurface(surface: string): string {
|
||||
const leaks = [
|
||||
/\bgrants:/i,
|
||||
/\bjurisdictions:/i,
|
||||
/\bFace of\s+[A-Z][A-Za-z0-9_-]+\b/,
|
||||
/\brepo=[^\s]+/i,
|
||||
/\bpath=[^\s]+/i,
|
||||
/\bdo not prompt\b/i,
|
||||
/\bprompt (?:her|him|them|it)\b/i,
|
||||
];
|
||||
|
||||
if (leaks.some((pattern) => pattern.test(surface))) {
|
||||
throw new Error("Repo Face memory surface leaked schema or prompt-construction language.");
|
||||
}
|
||||
|
||||
return surface;
|
||||
}
|
||||
|
||||
async function projectRepoFaceMemorySurface(input: {
|
||||
identity: RepoDiscordIdentity;
|
||||
statePacket: string;
|
||||
config: ReturnType<typeof loadConfig>;
|
||||
}): Promise<string> {
|
||||
const prompt = loadPromptTemplate("repo-face-state-projector.prompt.md", {
|
||||
characterIdentity: renderRepoCharacterIdentityDoctrine(input.identity),
|
||||
statePacket: input.statePacket,
|
||||
});
|
||||
const output = await runCodexTextProjection({
|
||||
prompt,
|
||||
config: input.config,
|
||||
command: "repo-face-state-projector",
|
||||
jobId: `state-projector:${input.identity.id}:${Date.now()}`,
|
||||
timeoutMs: 180_000,
|
||||
});
|
||||
const projected = output.trim();
|
||||
if (projected.length < 80) {
|
||||
throw new Error(`Repo Face state projector returned too little text for ${input.identity.id}.`);
|
||||
}
|
||||
return rejectLeakyMemorySurface(projected);
|
||||
}
|
||||
|
||||
function renderRepoFaceConversationTranscript(input: {
|
||||
identity: RepoDiscordIdentity;
|
||||
recentMessages: SourceMessage[];
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ import {
|
|||
import { readRepoActivity } from "../apps/persona-scheduler/dist/repo-activity-source.js";
|
||||
import { readPersonaStateObservation } from "../apps/persona-scheduler/dist/persona-state-source.js";
|
||||
import { readPersonaMemoryRecall } from "../apps/persona-scheduler/dist/persona-memory-context-source.js";
|
||||
import { projectPersonaMemorySurface } from "../apps/persona-scheduler/dist/persona-memory-projector.js";
|
||||
|
||||
function participant(identityId: string, nextTurnAt: number): InitiativeParticipant {
|
||||
return {
|
||||
|
|
@ -444,6 +445,18 @@ assert.deepEqual(await readPersonaMemoryRecall({
|
|||
status: "unavailable",
|
||||
reason: "No typed Persona state observation was supplied.",
|
||||
}, "semantic recall fails explicitly before touching vector infrastructure when the typed Mind observation is absent");
|
||||
assert.equal(await projectPersonaMemorySurface({
|
||||
identityId: "nibu",
|
||||
characterIdentity: "Nibu",
|
||||
statePacket: "Nibu remembers the ship mind and remains irritated by careless continuity.",
|
||||
modelProjectionEnabled: false,
|
||||
}), "Nibu remembers the ship mind and remains irritated by careless continuity.", "disabled model projection returns the validated deterministic memory surface without invoking a model");
|
||||
await assert.rejects(projectPersonaMemorySurface({
|
||||
identityId: "nibu",
|
||||
characterIdentity: "Nibu",
|
||||
statePacket: "path=private-state",
|
||||
modelProjectionEnabled: false,
|
||||
}), /leaked schema/, "the projector rejects state-path leakage on the model-free path");
|
||||
const stateSourceDirectory = await mkdtemp(join(tmpdir(), "voidbot-persona-state-source-"));
|
||||
try {
|
||||
const statePath = join(stateSourceDirectory, "nibu.cc");
|
||||
|
|
|
|||
Loading…
Reference in a new issue