mirror of
https://github.com/sortedcord/omnia.git
synced 2026-07-22 03:52:48 +05:30
feat(intent): Add additional "thought" intent type
This commit is contained in:
@@ -23,6 +23,7 @@ function IntentTag({
|
||||
}) {
|
||||
const labels: Record<string, string> = {
|
||||
monologue: "thought",
|
||||
thought: "thought",
|
||||
dialogue: "dialogue",
|
||||
action: "action",
|
||||
};
|
||||
|
||||
@@ -73,7 +73,7 @@
|
||||
"initialMemories": [
|
||||
{
|
||||
"id": "ab3f29d2-cf11-4111-9a99-b13c126d123e",
|
||||
"timestamp": "2026-07-09T07:58:00.000Z",
|
||||
"timestamp": "2026-07-01T07:58:00.000Z",
|
||||
"locationId": "white-room",
|
||||
"intent": {
|
||||
"type": "monologue",
|
||||
@@ -82,6 +82,18 @@
|
||||
"actorId": "7c9b83b3-8cfb-4e89-8d77-626a5757d591",
|
||||
"targetIds": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "zz3f29d2-as11-9811-9a99-b13c126d123e",
|
||||
"timestamp": "2026-07-01T09:58:00.000Z",
|
||||
"locationId": "white-room",
|
||||
"intent": {
|
||||
"type": "action",
|
||||
"originalText": "he wakes up from his sleep.",
|
||||
"description": "",
|
||||
"actorId": "bf3f29d2-cf11-4b11-9a99-b13c126d400e",
|
||||
"targetIds": []
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -119,12 +131,12 @@
|
||||
],
|
||||
"initialMemories": [
|
||||
{
|
||||
"id": "7c9b83b3-8cfb-4e89-8d77-626a5757d591",
|
||||
"timestamp": "2026-07-09T07:58:30.000Z",
|
||||
"id": "zx1f29d2-cf11-4111-9a99-b13c126d123e",
|
||||
"timestamp": "2026-07-09T07:58:00.000Z",
|
||||
"locationId": "white-room",
|
||||
"intent": {
|
||||
"type": "action",
|
||||
"originalText": "Why can't I remember anything before the research agreement. It's like my memory was erased.",
|
||||
"originalText": "I wake up in an unfamiliar place.",
|
||||
"description": "",
|
||||
"actorId": "bf3f29d2-cf11-4b11-9a99-b13c126d400e",
|
||||
"targetIds": []
|
||||
|
||||
@@ -112,7 +112,11 @@ Guidelines:
|
||||
}
|
||||
|
||||
// --- Cognitive Buffer ---
|
||||
const memorySection = this.buildMemorySection(entity, recentEntries, now);
|
||||
const memorySection = this.buildCognitiveBufferSection(
|
||||
entity,
|
||||
recentEntries,
|
||||
now,
|
||||
);
|
||||
if (memorySection) {
|
||||
sections.push(memorySection);
|
||||
}
|
||||
@@ -131,7 +135,7 @@ Guidelines:
|
||||
return sections.join("\n\n");
|
||||
}
|
||||
|
||||
private buildMemorySection(
|
||||
private buildCognitiveBufferSection(
|
||||
entity: Entity,
|
||||
entries: BufferEntry[],
|
||||
now: Date,
|
||||
@@ -147,9 +151,16 @@ Guidelines:
|
||||
let currentGroup: string | null = null;
|
||||
|
||||
for (const entry of recent) {
|
||||
const serialized = serializeSubjectiveBufferEntry(entry, entity);
|
||||
let serialized = serializeSubjectiveBufferEntry(entry, entity);
|
||||
const when = naturalizeTime(now, new Date(entry.timestamp));
|
||||
|
||||
if (
|
||||
entry.intent.actorId === entity.id &&
|
||||
entry.intent.type === "dialogue"
|
||||
) {
|
||||
serialized = `You said: ${serialized}`;
|
||||
}
|
||||
|
||||
if (when !== currentGroup) {
|
||||
currentGroup = when;
|
||||
const header = when.charAt(0).toUpperCase() + when.slice(1);
|
||||
|
||||
@@ -78,7 +78,7 @@ describe("ActorPromptBuilder with Memory Ledger Integration", () => {
|
||||
|
||||
// Check Cognitive Buffer exists
|
||||
expect(userContext).toContain("=== COGNITIVE BUFFER ===");
|
||||
expect(userContext).toContain("Alice greets Bob");
|
||||
expect(userContext).toContain("You said: Alice greets Bob");
|
||||
|
||||
// Check Memory Ledger exists
|
||||
expect(userContext).toContain("=== MEMORY LEDGER ===");
|
||||
|
||||
@@ -47,22 +47,22 @@ export class Architect {
|
||||
* Processes, validates, generates deltas, applies them to the world state,
|
||||
* and persists the changes to the database.
|
||||
*
|
||||
* "monologue" intents are internal thoughts — they bypass validation and
|
||||
* "monologue" and "thought" intents are internal thoughts — they bypass validation and
|
||||
* time-delta generation entirely: the clock does not advance, the world
|
||||
* state is not mutated or persisted. The caller is responsible for writing
|
||||
* the monologue to the actor's Cognitive Buffer.
|
||||
* the monologue/thought to the actor's Cognitive Buffer.
|
||||
*/
|
||||
async processIntent(
|
||||
worldState: WorldState,
|
||||
intent: Intent,
|
||||
): Promise<ProcessResult> {
|
||||
// 0. Monologue intents are purely internal — short-circuit before any
|
||||
// 0. Monologue/thought intents are purely internal — short-circuit before any
|
||||
// validation or world mutation.
|
||||
if (intent.type === "monologue") {
|
||||
if (intent.type === "monologue" || intent.type === "thought") {
|
||||
return {
|
||||
isValid: true,
|
||||
reason:
|
||||
"Monologue intent bypasses validation (internal thought, not perceivable).",
|
||||
"Monologue/thought intent bypasses validation (internal thought, not perceivable).",
|
||||
timeDelta: {
|
||||
minutesToAdvance: 0,
|
||||
explanation: "Internal thought — no time elapsed.",
|
||||
|
||||
@@ -26,10 +26,11 @@ export class TimeDeltaGenerator implements IDeltaGenerator<TimeDelta> {
|
||||
explanation: "Dialogue action; 1 minute granted for quick exchange.",
|
||||
};
|
||||
}
|
||||
if (intent.type === "monologue") {
|
||||
if (intent.type === "monologue" || intent.type === "thought") {
|
||||
return {
|
||||
minutesToAdvance: 0,
|
||||
explanation: "Monologue action; no time advanced for internal thought.",
|
||||
explanation:
|
||||
"Monologue/thought action; no time advanced for internal thought.",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export class LLMValidator {
|
||||
/**
|
||||
* Validates an action intent against the objective world state.
|
||||
*
|
||||
* "monologue" intents must never reach this validator — they are internal
|
||||
* "monologue" and "thought" intents must never reach this validator — they are internal
|
||||
* thoughts that bypass validation entirely (see Architect.processIntent).
|
||||
* This guard exists as a defensive safeguard.
|
||||
*/
|
||||
@@ -24,12 +24,12 @@ export class LLMValidator {
|
||||
worldState: WorldState,
|
||||
intent: Intent,
|
||||
): Promise<ValidationResult> {
|
||||
// Defensive guard: monologue intents bypass validation.
|
||||
if (intent.type === "monologue") {
|
||||
// Defensive guard: monologue and thought intents bypass validation.
|
||||
if (intent.type === "monologue" || intent.type === "thought") {
|
||||
return {
|
||||
isValid: true,
|
||||
reason:
|
||||
"Monologue intents are internal thoughts and bypass validation.",
|
||||
"Monologue/thought intents are internal thoughts and bypass validation.",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ For each intent you must:
|
||||
1. Classify its type:
|
||||
- "dialogue": if actor speaking, talking, whispering, murmuring, etc
|
||||
- "action": Any physical or logical action performed in the world (e.g., moving, opening, looking).
|
||||
- "monologue": An inner thought, reflection, or internal monologue/self narration.
|
||||
- "monologue" (or "thought"): An inner thought, reflection, or internal monologue/self narration.
|
||||
2. Extract the original text fragment from the prose that corresponds to this intent.
|
||||
3. Populate "description" and "selfDescription":
|
||||
- "description": No subject or name — a bare third-person verb phrase only (e.g. "clears their throat", "shakes their head slowly")
|
||||
|
||||
@@ -7,8 +7,14 @@ import { z } from "zod";
|
||||
* - "monologue": An inner thought or internal monologue. Not perceivable by
|
||||
* any other entity. Bypasses the Architect/validators entirely and is
|
||||
* written directly to the actor's Cognitive Buffer with no outcome.
|
||||
* - "thought": Equivalent/alias to "monologue".
|
||||
*/
|
||||
export const IntentTypeSchema = z.enum(["dialogue", "action", "monologue"]);
|
||||
export const IntentTypeSchema = z.enum([
|
||||
"dialogue",
|
||||
"action",
|
||||
"monologue",
|
||||
"thought",
|
||||
]);
|
||||
export type IntentType = z.infer<typeof IntentTypeSchema>;
|
||||
|
||||
/**
|
||||
@@ -30,7 +36,7 @@ export const LLMIntentSchema = z.object({
|
||||
/**
|
||||
* Entity IDs of the receiving parties (e.g., who is being spoken to,
|
||||
* what object is being interacted with). Always an empty array for
|
||||
* "monologue" intents, since they are not perceivable by anyone.
|
||||
* "monologue" and "thought" intents, since they are not perceivable by anyone.
|
||||
*/
|
||||
targetIds: z.array(z.string()),
|
||||
|
||||
|
||||
@@ -85,7 +85,9 @@ function checkIdleDecay(bufferEntries: BufferEntry[]): boolean {
|
||||
|
||||
// Check the last N entries
|
||||
const lastN = bufferEntries.slice(-N);
|
||||
return lastN.every((e) => e.intent.type === "monologue");
|
||||
return lastN.every(
|
||||
(e) => e.intent.type === "monologue" || e.intent.type === "thought",
|
||||
);
|
||||
}
|
||||
|
||||
function checkAttributeTrigger(entity: Entity): boolean {
|
||||
|
||||
@@ -30,7 +30,7 @@ export const ScenarioMemoryEntrySchema = z.object({
|
||||
timestamp: z.string(), // ISO string
|
||||
locationId: z.string().nullable(),
|
||||
intent: z.object({
|
||||
type: z.enum(["dialogue", "action", "monologue"]),
|
||||
type: z.enum(["dialogue", "action", "monologue", "thought"]),
|
||||
originalText: z.string(),
|
||||
description: z.string(),
|
||||
selfDescription: z.string().optional(),
|
||||
|
||||
@@ -128,17 +128,22 @@ describe("Talking Room Demo Scenario Test (Tier 1)", () => {
|
||||
|
||||
// 7. Assert initial pre-seeded memories
|
||||
const alphaMemories = bufferRepo.listForOwner(alphaId);
|
||||
expect(alphaMemories).toHaveLength(1);
|
||||
expect(alphaMemories[0].id).toBe("alpha-wake");
|
||||
expect(alphaMemories).toHaveLength(2);
|
||||
expect(alphaMemories[0].id).toBe("ab3f29d2-cf11-4111-9a99-b13c126d123e");
|
||||
expect(alphaMemories[0].intent.type).toBe("monologue");
|
||||
expect(alphaMemories[0].intent.originalText).toContain("jail");
|
||||
expect(alphaMemories[0].intent.description).toBe("");
|
||||
|
||||
expect(alphaMemories[1].id).toBe("zz3f29d2-as11-9811-9a99-b13c126d123e");
|
||||
expect(alphaMemories[1].intent.type).toBe("action");
|
||||
expect(alphaMemories[1].intent.originalText).toContain("sleep");
|
||||
expect(alphaMemories[1].intent.description).toBe("");
|
||||
|
||||
const betaMemories = bufferRepo.listForOwner(betaId);
|
||||
expect(betaMemories).toHaveLength(1);
|
||||
expect(betaMemories[0].id).toBe("beta-wake");
|
||||
expect(betaMemories[0].id).toBe("zx1f29d2-cf11-4111-9a99-b13c126d123e");
|
||||
expect(betaMemories[0].intent.type).toBe("action");
|
||||
expect(betaMemories[0].intent.originalText).toContain("agreement");
|
||||
expect(betaMemories[0].intent.originalText).toContain("unfamiliar");
|
||||
expect(betaMemories[0].intent.description).toBe("");
|
||||
|
||||
db.close();
|
||||
|
||||
Reference in New Issue
Block a user