From 9642e2bb54ab151ac10e7396ac4afe25258cdf2f Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Thu, 9 Jul 2026 10:19:09 +0530 Subject: [PATCH] refactor: Group subsequent events under a naturalized time head Strips out actual world clock from memories. --- packages/actor/src/actor-prompt-builder.ts | 35 ++++++++++++++-------- packages/memory/src/buffer.ts | 9 +----- packages/memory/tests/memory.test.ts | 8 ++--- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/packages/actor/src/actor-prompt-builder.ts b/packages/actor/src/actor-prompt-builder.ts index 4a959ee..fc7059c 100644 --- a/packages/actor/src/actor-prompt-builder.ts +++ b/packages/actor/src/actor-prompt-builder.ts @@ -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")}`; } } diff --git a/packages/memory/src/buffer.ts b/packages/memory/src/buffer.ts index 75c8def..61c5ad7 100644 --- a/packages/memory/src/buffer.ts +++ b/packages/memory/src/buffer.ts @@ -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 { diff --git a/packages/memory/tests/memory.test.ts b/packages/memory/tests/memory.test.ts index d313091..cbb8e26 100644 --- a/packages/memory/tests/memory.test.ts +++ b/packages/memory/tests/memory.test.ts @@ -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"); }); });