refactor: Group subsequent events under a naturalized time head

Strips out actual world clock from memories.
This commit is contained in:
2026-07-09 10:19:09 +05:30
parent 8934422a4d
commit 9642e2bb54
3 changed files with 27 additions and 25 deletions

View File

@@ -52,12 +52,12 @@ export class ActorPromptBuilder {
worldState: WorldState,
entity: Entity,
): { systemPrompt: string; userContext: string } {
const systemPrompt = this.buildSystemPrompt(entity);
const systemPrompt = this.buildSystemPrompt();
const userContext = this.buildUserContext(worldState, entity);
return { systemPrompt, userContext };
}
private buildSystemPrompt(entity: Entity): string {
private buildSystemPrompt(): string {
return `
You are an actor agent embodying a single character in a narrative simulation. You ARE this character — act immersively, naturally, and in-character at all times. Do not break character, do not reference being an AI or a system, and do not narrate from outside the character's perspective.
@@ -74,15 +74,11 @@ Guidelines:
- Keep your prose vivid but concise. A single response may contain more than one intent (e.g., you may think, then speak, then act) — write them in natural narrative order.
- Not every response requires an outward action. It is perfectly valid to only think (a monologue) and do nothing perceivable.
- Never speak or act on another entity's behalf — you only control your own character.
Your character's identifier in this world is "${entity.id}".
".
`.trim();
}
private buildUserContext(
worldState: WorldState,
entity: Entity,
): string {
private buildUserContext(worldState: WorldState, entity: Entity): string {
const sections: string[] = [];
// --- Subjective present time ---
@@ -97,7 +93,10 @@ Your character's identifier in this world is "${entity.id}".
);
// --- Recent memory ---
const memorySection = this.buildMemorySection(entity, worldState.clock.get());
const memorySection = this.buildMemorySection(
entity,
worldState.clock.get(),
);
if (memorySection) {
sections.push(memorySection);
}
@@ -120,12 +119,22 @@ Your character's identifier in this world is "${entity.id}".
}
const recent = entries.slice(-this.memoryLimit);
const lines = recent.map((entry) => {
const groupedLines: string[] = [];
let currentGroup: string | null = null;
for (const entry of recent) {
const serialized = serializeSubjectiveBufferEntry(entry, entity);
const when = naturalizeTime(now, new Date(entry.timestamp));
return `${serialized} (${when})`;
});
return `=== YOUR RECENT MEMORY ===\n${lines.join("\n")}`;
if (when !== currentGroup) {
currentGroup = when;
const header = when.charAt(0).toUpperCase() + when.slice(1);
groupedLines.push(header);
}
groupedLines.push(` - ${serialized}`);
}
return `=== YOUR RECENT MEMORY ===\n${groupedLines.join("\n")}`;
}
}

View File

@@ -25,13 +25,6 @@ export function serializeSubjectiveBufferEntry(
entry: BufferEntry,
viewer: Entity,
): string {
const dateObj = new Date(entry.timestamp);
// Ensure a deterministic timezone/format for testing and model inputs:
const timeStr = dateObj.toLocaleTimeString("en-US", {
hour12: true,
timeZone: "UTC",
});
const actorAlias = resolveAlias(viewer, entry.intent.actorId);
const targetAliases = entry.intent.targetIds.map((tid) =>
@@ -48,7 +41,7 @@ export function serializeSubjectiveBufferEntry(
}
}
return `[${timeStr}] ${actorAlias} ${details}`;
return `${actorAlias} ${details}`;
}
export class BufferRepository {

View File

@@ -39,7 +39,7 @@ describe("Subjective Buffer Entry Serializer Tests (Tier 1)", () => {
};
const result = serializeSubjectiveBufferEntry(entry, viewer);
expect(result).toBe('[12:00:00 PM] the hooded figure spoke to the bartender: "Bob greets Charlie"');
expect(result).toBe('the hooded figure spoke to the bartender: "Bob greets Charlie"');
});
test("serializes action intent with outcome details", () => {
@@ -65,7 +65,7 @@ describe("Subjective Buffer Entry Serializer Tests (Tier 1)", () => {
};
const result = serializeSubjectiveBufferEntry(entry, viewer);
expect(result).toBe('[12:05:00 PM] the hooded figure Bob attempts to break the lock latch (Outcome: Failed - The lock is made of reinforced steel.)');
expect(result).toBe('the hooded figure Bob attempts to break the lock latch (Outcome: Failed - The lock is made of reinforced steel.)');
});
test("serializes self-reference and unfamiliar actors", () => {
@@ -86,7 +86,7 @@ describe("Subjective Buffer Entry Serializer Tests (Tier 1)", () => {
};
const resultSelf = serializeSubjectiveBufferEntry(entrySelf, viewer);
expect(resultSelf).toBe("[12:10:00 PM] you open the window");
expect(resultSelf).toBe("you open the window");
const entryUnfamiliar: BufferEntry = {
id: "entry-unfamiliar",
@@ -103,7 +103,7 @@ describe("Subjective Buffer Entry Serializer Tests (Tier 1)", () => {
};
const resultUnfamiliar = serializeSubjectiveBufferEntry(entryUnfamiliar, viewer);
expect(resultUnfamiliar).toBe("[12:15:00 PM] an unfamiliar figure knock on the door");
expect(resultUnfamiliar).toBe("an unfamiliar figure knock on the door");
});
});