mirror of
https://github.com/sortedcord/omnia.git
synced 2026-07-22 12:02:49 +05:30
chore: Format files
This commit is contained in:
@@ -15,7 +15,13 @@ export default defineConfig({
|
||||
src: "./src/assets/img/logo.png",
|
||||
replacesTitle: true,
|
||||
},
|
||||
social: [{ icon: "github", label: "GitHub", href: "https://github.com/sortedcord/omnia-consolidated" }],
|
||||
social: [
|
||||
{
|
||||
icon: "github",
|
||||
label: "GitHub",
|
||||
href: "https://github.com/sortedcord/omnia-consolidated",
|
||||
},
|
||||
],
|
||||
sidebar: [
|
||||
{
|
||||
label: "Introduction",
|
||||
|
||||
@@ -34,11 +34,11 @@ Establishes the role, rules, and output contract:
|
||||
|
||||
Epistemically bounded, with these sections:
|
||||
|
||||
| Section | Content | Source |
|
||||
|---|---|---|
|
||||
| Current moment | The subjective present time | `worldState.clock.get().toISOString()` |
|
||||
| The world as you perceive it | Self-visible attributes, co-located entities + their visible attributes, other presences elsewhere | `serializeSubjectiveWorldState()` |
|
||||
| Your recent memory | Recent `BufferEntry`s, alias-substituted, with relative time phrasing | `serializeSubjectiveBufferEntry()` |
|
||||
| Section | Content | Source |
|
||||
| ---------------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------- |
|
||||
| Current moment | The subjective present time | `worldState.clock.get().toISOString()` |
|
||||
| The world as you perceive it | Self-visible attributes, co-located entities + their visible attributes, other presences elsewhere | `serializeSubjectiveWorldState()` |
|
||||
| Your recent memory | Recent `BufferEntry`s, alias-substituted, with relative time phrasing | `serializeSubjectiveBufferEntry()` |
|
||||
|
||||
No system UUIDs, no private attributes the entity lacks ACL access to, and no objective-world-state dump are present.
|
||||
|
||||
@@ -81,13 +81,13 @@ Monologue (`"monologue"`) is the third intent type. Its properties:
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Role |
|
||||
|---|---|
|
||||
| `packages/actor/src/actor-prompt-builder.ts` | Assembles the epistemically-bounded actor prompt |
|
||||
| `packages/actor/src/actor.ts` | `ActorAgent` class: orchestrates prompt → LLM → decoder flow |
|
||||
| `packages/actor/src/index.ts` | Package exports |
|
||||
| `packages/core/src/world.ts:72` | `serializeSubjectiveWorldState()` |
|
||||
| `packages/intent/src/intent.ts:8` | `IntentTypeSchema` — includes `"monologue"` |
|
||||
| `packages/intent/src/intent-decoder.ts:30` | Decoder system prompt |
|
||||
| `packages/architect/src/architect.ts:35` | Monologue short-circuit |
|
||||
| `packages/architect/src/llm-validator.ts:19` | Defensive monologue guard |
|
||||
| File | Role |
|
||||
| -------------------------------------------- | ------------------------------------------------------------ |
|
||||
| `packages/actor/src/actor-prompt-builder.ts` | Assembles the epistemically-bounded actor prompt |
|
||||
| `packages/actor/src/actor.ts` | `ActorAgent` class: orchestrates prompt → LLM → decoder flow |
|
||||
| `packages/actor/src/index.ts` | Package exports |
|
||||
| `packages/core/src/world.ts:72` | `serializeSubjectiveWorldState()` |
|
||||
| `packages/intent/src/intent.ts:8` | `IntentTypeSchema` — includes `"monologue"` |
|
||||
| `packages/intent/src/intent-decoder.ts:30` | Decoder system prompt |
|
||||
| `packages/architect/src/architect.ts:35` | Monologue short-circuit |
|
||||
| `packages/architect/src/llm-validator.ts:19` | Defensive monologue guard |
|
||||
|
||||
@@ -30,20 +30,24 @@ function checkHandoffTrigger(
|
||||
entity: Entity,
|
||||
bufferEntries: BufferEntry[],
|
||||
now: Date,
|
||||
maxContext?: number
|
||||
maxContext?: number,
|
||||
): HandoffTrigger;
|
||||
```
|
||||
|
||||
### Voluntary Triggers (Soft)
|
||||
|
||||
Voluntary triggers identify natural narrative boundaries where the entity is inactive or has transitioned contexts:
|
||||
* **Scene Exit**: The entity's `locationId` changes relative to the location of the entries currently in the buffer, or the buffer contains entries spanning multiple different locations.
|
||||
* **Idle Decay**: The entity has produced no external interactions (only monologue entries or no entries) for $N$ (default: 5) consecutive turns.
|
||||
* **Attribute-Driven State**: A character attribute (e.g., `status: asleep` or `consciousness: unconscious`) signals low external activity.
|
||||
|
||||
- **Scene Exit**: The entity's `locationId` changes relative to the location of the entries currently in the buffer, or the buffer contains entries spanning multiple different locations.
|
||||
- **Idle Decay**: The entity has produced no external interactions (only monologue entries or no entries) for $N$ (default: 5) consecutive turns.
|
||||
- **Attribute-Driven State**: A character attribute (e.g., `status: asleep` or `consciousness: unconscious`) signals low external activity.
|
||||
|
||||
### Involuntary Triggers (Hard)
|
||||
|
||||
Involuntary triggers enforce context constraints before building the next prompt to prevent token limit overflows:
|
||||
* **Buffer Ceiling**: The serialized length of the working memory section (as generated by `getMemorySectionLength`) exceeds a configured fraction (default: 60%) of the provider's context window.
|
||||
* **Event Velocity**: The number of buffer entries exceeds a safe threshold (default: 20), indicating an event-heavy scene generating history faster than voluntary boundaries occur.
|
||||
|
||||
- **Buffer Ceiling**: The serialized length of the working memory section (as generated by `getMemorySectionLength`) exceeds a configured fraction (default: 60%) of the provider's context window.
|
||||
- **Event Velocity**: The number of buffer entries exceeds a safe threshold (default: 20), indicating an event-heavy scene generating history faster than voluntary boundaries occur.
|
||||
|
||||
If the buffer is empty, trigger detection exits immediately with `"none"`.
|
||||
|
||||
@@ -57,8 +61,8 @@ The watermark boundary is computed as:
|
||||
|
||||
$$\text{Watermark} = \max(K \text{ entries}, \text{entries bucketed as fresh by } \textit{naturalizeTime})$$
|
||||
|
||||
* **Hard Floor ($K = 8$)**: The last 8 entries in the buffer are always preserved.
|
||||
* **Temporal Freshness**: Any entries that `naturalizeTime` classifies within the immediate temporal horizon (`"just now"`, `"moments ago"`, `"a few minutes ago"`, or `"several minutes ago"`) are also protected.
|
||||
- **Hard Floor ($K = 8$)**: The last 8 entries in the buffer are always preserved.
|
||||
- **Temporal Freshness**: Any entries that `naturalizeTime` classifies within the immediate temporal horizon (`"just now"`, `"moments ago"`, `"a few minutes ago"`, or `"several minutes ago"`) are also protected.
|
||||
|
||||
Entries older than the watermark boundary constitute the **Candidate Pool** and are eligible for handoff processing.
|
||||
|
||||
@@ -84,16 +88,20 @@ const HandoffResultSchema = z.object({
|
||||
```
|
||||
|
||||
### LLM Processing Rules
|
||||
|
||||
The prompt instructs the model to apply the following cognitive operations:
|
||||
|
||||
1. **Clustering**: Group related consecutive candidate entries into high-level narrative beats (e.g., consolidating an entire dialogue exchange into one beat).
|
||||
2. **Third-Person Synthesis**: Write the `content` summary from a third-person narrative perspective.
|
||||
3. **Dialogue Salience**: Extract verbatim quotes of high emotional or narrative relevance.
|
||||
4. **Information Pruning (Forgetting)**: Any candidate entry ID omitted from all chunk `sourceEntryIds` is permanently deleted from the buffer and never saved to the ledger. This mirrors natural sensory attenuation.
|
||||
|
||||
### Unresolved Thread Retention (Pinning)
|
||||
When a chunk represents an unresolved high-stakes situation (e.g., an active conflict or standing threat), the LLM sets `retainInBuffer: true`.
|
||||
* The summary is committed to the long-term Ledger as normal.
|
||||
* The source buffer entries are exempted from pruning, remaining in the working memory buffer with `pinned: true` until a future handoff pass determines the thread is resolved.
|
||||
|
||||
When a chunk represents an unresolved high-stakes situation (e.g., an active conflict or standing threat), the LLM sets `retainInBuffer: true`.
|
||||
|
||||
- The summary is committed to the long-term Ledger as normal.
|
||||
- The source buffer entries are exempted from pruning, remaining in the working memory buffer with `pinned: true` until a future handoff pass determines the thread is resolved.
|
||||
|
||||
---
|
||||
|
||||
@@ -123,6 +131,6 @@ If LLM inference, Zod schema validation, or vector embedding generation fails, t
|
||||
|
||||
The handoff pipeline is exposed via `HandoffEngine` and is wired directly into the simulation loop:
|
||||
|
||||
* **Turn Hook**: `SimulationManager.step()` invokes trigger evaluation and handoff resolution for all active entities at the start of the turn, before entities act.
|
||||
* **Task Routing**: Handoff tasks are routed using the `"handoff"` routing key, enabling independent LLM provider selection and max context configuration.
|
||||
* **Buffer Garbage Collection**: By combining memory promotion with automatic pruning, the handoff engine bounds buffer memory usage over long simulation runs, eliminating the need for a separate garbage collection mechanism.
|
||||
- **Turn Hook**: `SimulationManager.step()` invokes trigger evaluation and handoff resolution for all active entities at the start of the turn, before entities act.
|
||||
- **Task Routing**: Handoff tasks are routed using the `"handoff"` routing key, enabling independent LLM provider selection and max context configuration.
|
||||
- **Buffer Garbage Collection**: By combining memory promotion with automatic pruning, the handoff engine bounds buffer memory usage over long simulation runs, eliminating the need for a separate garbage collection mechanism.
|
||||
|
||||
@@ -6,6 +6,7 @@ description: How narrative prose becomes structured, validated actions
|
||||
The simple way of understanding intents is to think of it as a proposal, not an effect.
|
||||
|
||||
Intents are:
|
||||
|
||||
- **Declarative** — they describe what the character intends, not the final outcome.
|
||||
- **High-level** — they capture the gist of an action or dialogue.
|
||||
- **Allowed to be wrong** — validation happens downstream.
|
||||
|
||||
@@ -22,6 +22,7 @@ export interface ILLMProvider {
|
||||
```
|
||||
|
||||
The codebase provides three primary implementations:
|
||||
|
||||
1. **`GeminiProvider`:** The production provider utilizing Google's Gemini Models via the `@langchain/google-genai` SDK.
|
||||
2. **`OpenRouterProvider`:** The production provider utilizing OpenRouter via the `@langchain/openrouter` SDK, allowing routing through various third-party and local models.
|
||||
3. **`MockLLMProvider`:** A stateless, pre-programmed mock provider used for fast, deterministic unit testing and local integration tests.
|
||||
@@ -44,11 +45,12 @@ export interface LLMProviderInstance {
|
||||
```
|
||||
|
||||
Users can register multiple provider instances in the **Configuration Page** under the GUI. Each instance is given:
|
||||
* A friendly, human-readable name (e.g., `"Gemini Production Key"`, `"OpenRouter Claude Key"`).
|
||||
* A provider type (e.g., `google-genai`, `openrouter`, `mock`).
|
||||
* An API key credential.
|
||||
* A custom target model name (e.g., `gemini-2.5-flash`, `anthropic/claude-3-5-sonnet`, or local model paths).
|
||||
* An **Active** status flag (one key is marked as globally active).
|
||||
|
||||
- A friendly, human-readable name (e.g., `"Gemini Production Key"`, `"OpenRouter Claude Key"`).
|
||||
- A provider type (e.g., `google-genai`, `openrouter`, `mock`).
|
||||
- An API key credential.
|
||||
- A custom target model name (e.g., `gemini-2.5-flash`, `anthropic/claude-3-5-sonnet`, or local model paths).
|
||||
- An **Active** status flag (one key is marked as globally active).
|
||||
|
||||
Configurations are stored globally in `data/settings.db` (separated from specific simulation run databases like `data/sim-*.db` to keep key storage and audit logs isolated).
|
||||
|
||||
@@ -58,12 +60,12 @@ Configurations are stored globally in `data/settings.db` (separated from specifi
|
||||
|
||||
During a simulation run, the engine executes four distinct LLM operations. To optimize costs, latency, or model accuracy, you can route each of these tasks to different LLM provider instances:
|
||||
|
||||
| Task Name | Key ID | Description | Default Model |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| **Actor Prose Generation** | `actor-prose` | Generates roleplay and narrative behavioral prose for Non-Player Characters. | `gemini-2.5-flash` / `google/gemini-2.5-flash` |
|
||||
| **LLM Validator** | `llm-validator` | Arbitrates and validates proposed actions against the world state rules and constraints. | `gemini-2.5-flash` / `google/gemini-2.5-flash` |
|
||||
| **Intent Decoder** | `intent-decoder` | Parses and splits free-text actions/prose into structured intent sequences. | `gemini-2.5-flash` / `google/gemini-2.5-flash` |
|
||||
| **TimeDelta Generator** | `timedelta` | Calculates the duration of character actions to advance the game clock. | `gemini-2.5-flash` / `google/gemini-2.5-flash` |
|
||||
| Task Name | Key ID | Description | Default Model |
|
||||
| :------------------------- | :--------------- | :--------------------------------------------------------------------------------------- | :--------------------------------------------- |
|
||||
| **Actor Prose Generation** | `actor-prose` | Generates roleplay and narrative behavioral prose for Non-Player Characters. | `gemini-2.5-flash` / `google/gemini-2.5-flash` |
|
||||
| **LLM Validator** | `llm-validator` | Arbitrates and validates proposed actions against the world state rules and constraints. | `gemini-2.5-flash` / `google/gemini-2.5-flash` |
|
||||
| **Intent Decoder** | `intent-decoder` | Parses and splits free-text actions/prose into structured intent sequences. | `gemini-2.5-flash` / `google/gemini-2.5-flash` |
|
||||
| **TimeDelta Generator** | `timedelta` | Calculates the duration of character actions to advance the game clock. | `gemini-2.5-flash` / `google/gemini-2.5-flash` |
|
||||
|
||||
If no specific provider instance is mapped to a task, the task automatically routes to the globally marked **Active** provider instance.
|
||||
|
||||
|
||||
@@ -64,9 +64,10 @@ CREATE INDEX IF NOT EXISTS idx_ledger_involved_entity ON ledger_involved_entitie
|
||||
|
||||
## 3. The Handoff Pipeline
|
||||
|
||||
Working memory (Tier 1 Buffer) entries are promoted to the Ledger through the automated [Handoff Pipeline](./handoff).
|
||||
Working memory (Tier 1 Buffer) entries are promoted to the Ledger through the automated [Handoff Pipeline](./handoff).
|
||||
|
||||
During handoff:
|
||||
|
||||
1. Candidate entries are clustered into narrative beats.
|
||||
2. Ambient stage business and redundant details are pruned.
|
||||
3. Chunks are synthesized into third-person summaries and assigned importance scores.
|
||||
@@ -92,19 +93,23 @@ flowchart TD
|
||||
```
|
||||
|
||||
### Phase 1: Deterministic Heuristic Filtering
|
||||
|
||||
The primary selection uses indexes to retrieve a candidate pool (capped at 100 entries) from the database:
|
||||
|
||||
1. **Spatial Cues**: Fetch entries matching the character's current `locationId`.
|
||||
2. **Social Cues**: Fetch entries where `involvedEntityIds` intersects with entities currently inside the character's perception radius.
|
||||
3. **High Salience**: Always retrieve high-salience entries where `importance >= 8`.
|
||||
|
||||
### Phase 2: Semantic & Episodic Ranking
|
||||
|
||||
Once the candidate pool is loaded, the ranking engine evaluates entries in application memory:
|
||||
|
||||
1. **Semantic Similarity**: Cosine similarity is computed directly in JS/TS memory between the current prompt context and the candidate embeddings.
|
||||
2. **Multi-Factor Scoring**: Candidates are ranked using a weighted linear combination:
|
||||
$$\text{Score} = (\alpha \times \text{recency}) + (\beta \times \text{importance}) + (\gamma \times \text{relevance})$$
|
||||
* **Recency** is modeled via exponential decay based on elapsed simulation hours: $\text{decayRate}^{\text{hoursElapsed}}$.
|
||||
* **Importance** is the normalized salience score (1-10) assigned during handoff.
|
||||
* **Relevance** is the cosine similarity score.
|
||||
- **Recency** is modeled via exponential decay based on elapsed simulation hours: $\text{decayRate}^{\text{hoursElapsed}}$.
|
||||
- **Importance** is the normalized salience score (1-10) assigned during handoff.
|
||||
- **Relevance** is the cosine similarity score.
|
||||
3. **Chronological Associative Chaining**: When a memory is selected, the system automatically pulls in its adjacent chronological neighbors (preceding and succeeding ledger entries) to preserve episodic continuity in the prompt.
|
||||
|
||||
---
|
||||
@@ -113,11 +118,11 @@ Once the candidate pool is loaded, the ranking engine evaluates entries in appli
|
||||
|
||||
In crowded settings (e.g., a room with many characters), retrieving long-term memories for all co-located entities would exhaust the prompt context window. To prevent this, retrieval uses an **Active Focus** selection strategy:
|
||||
|
||||
* **Active Focus Set**: The prompt builder scans the last 10 entries of the character's working memory buffer. Any entity targeted by, spoken to, or mentioned in these entries is added to the Active Focus set.
|
||||
* **Dynamic Capacity Limits**:
|
||||
* If the number of co-located characters is small ($\le 3$), long-term memory is retrieved for all of them.
|
||||
* If the environment is crowded ($> 3$), long-term retrieval is strictly restricted to the top 3 characters in the Active Focus set.
|
||||
* This creates a realistic attention loop: when a new character interacts with the actor, they enter the working buffer, triggering the retrieval of their long-term history on the subsequent turn.
|
||||
- **Active Focus Set**: The prompt builder scans the last 10 entries of the character's working memory buffer. Any entity targeted by, spoken to, or mentioned in these entries is added to the Active Focus set.
|
||||
- **Dynamic Capacity Limits**:
|
||||
- If the number of co-located characters is small ($\le 3$), long-term memory is retrieved for all of them.
|
||||
- If the environment is crowded ($> 3$), long-term retrieval is strictly restricted to the top 3 characters in the Active Focus set.
|
||||
- This creates a realistic attention loop: when a new character interacts with the actor, they enter the working buffer, triggering the retrieval of their long-term history on the subsequent turn.
|
||||
|
||||
---
|
||||
|
||||
@@ -125,8 +130,8 @@ In crowded settings (e.g., a room with many characters), retrieving long-term me
|
||||
|
||||
Retrieved ledger entries are formatted into the prompt chronologically and mapped to subjective aliases. Internal metrics (e.g., importance numbers and raw system IDs) are omitted to preserve immersion.
|
||||
|
||||
* Working memory entries are injected under `=== RECENT EVENTS ===` (narrated relative to the present moment).
|
||||
* Recalled ledger entries are injected under `=== YOUR MEMORIES ===` (presented as long-term recollections).
|
||||
- Working memory entries are injected under `=== RECENT EVENTS ===` (narrated relative to the present moment).
|
||||
- Recalled ledger entries are injected under `=== YOUR MEMORIES ===` (presented as long-term recollections).
|
||||
|
||||
```text
|
||||
=== RECENT EVENTS ===
|
||||
|
||||
@@ -43,12 +43,13 @@ A subjective `BufferEntry` records a discrete event from the perspective of an e
|
||||
```typescript
|
||||
interface BufferEntry {
|
||||
id: string;
|
||||
ownerId: string; // Whose subjective memory buffer this lives in
|
||||
timestamp: string; // WorldClock.get().toISOString() at write time
|
||||
ownerId: string; // Whose subjective memory buffer this lives in
|
||||
timestamp: string; // WorldClock.get().toISOString() at write time
|
||||
locationId: string | null; // Actor's location when this happened
|
||||
|
||||
intent: Intent; // The actual dialogue/action intent, reused as-is
|
||||
outcome?: { // Present only for "action" intents
|
||||
intent: Intent; // The actual dialogue/action intent, reused as-is
|
||||
outcome?: {
|
||||
// Present only for "action" intents
|
||||
isValid: boolean;
|
||||
reason: string;
|
||||
};
|
||||
@@ -94,10 +95,9 @@ CREATE TABLE IF NOT EXISTS buffer_entries (
|
||||
|
||||
LLMs are poor at tracking quantized clock times, and real entities do not recall exact timestamps for past events. To make memories psychologically realistic, timestamps are converted into relative natural language phrases prior to prompt injection:
|
||||
|
||||
* **Utility**: `naturalizeTime(now: Date, past: Date): string` converts raw dates into subjective relative strings.
|
||||
* **Granularity Tiers**:
|
||||
* **Relative (< 6 hours)**: Returns short offsets like `"just now"`, `"moments ago"`, `"a couple hours ago"`, or `"a few hours ago"`.
|
||||
* **Same Subjective Day (6h to 18h)**: Detects waking hours (05:00 - 21:59). If both times occur within the same waking block, it returns `"earlier today, in the {period}"` (where period is `morning`, `afternoon`, or `evening`).
|
||||
* **Plausible Sleep Boundaries**: Past events from sleep hours are mapped to `"last night"`, `"around midnight"`, or `"late last night"`.
|
||||
* **Coarse (>= 48 hours)**: Returns broad descriptors like `"a couple days ago"`, `"about a week ago"`, `"a couple months ago"`, or `"years ago"`.
|
||||
|
||||
- **Utility**: `naturalizeTime(now: Date, past: Date): string` converts raw dates into subjective relative strings.
|
||||
- **Granularity Tiers**:
|
||||
- **Relative (< 6 hours)**: Returns short offsets like `"just now"`, `"moments ago"`, `"a couple hours ago"`, or `"a few hours ago"`.
|
||||
- **Same Subjective Day (6h to 18h)**: Detects waking hours (05:00 - 21:59). If both times occur within the same waking block, it returns `"earlier today, in the {period}"` (where period is `morning`, `afternoon`, or `evening`).
|
||||
- **Plausible Sleep Boundaries**: Past events from sleep hours are mapped to `"last night"`, `"around midnight"`, or `"late last night"`.
|
||||
- **Coarse (>= 48 hours)**: Returns broad descriptors like `"a couple days ago"`, `"about a week ago"`, `"a couple months ago"`, or `"years ago"`.
|
||||
|
||||
@@ -14,6 +14,7 @@ world → region → location → point of interest
|
||||
These nodes are connected by **portals** with sound and vision propagation values. When something happens, perception information bubbles outward through portals.
|
||||
|
||||
Today, actors perceive:
|
||||
|
||||
- Co-located entities
|
||||
- Their location's visible attributes
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ The central testing challenge: most of Omnia is deterministic and highly testabl
|
||||
Unit tests reside within each package's `tests/` directory. They do not use LLMs and do not perform I/O. This tier covers the majority of the codebase.
|
||||
|
||||
Examples:
|
||||
|
||||
- `hasAccess()` / ACL grant-revoke logic
|
||||
- `addAttribute` rejecting duplicate names
|
||||
- `WorldClock.advance()` / `getTimeOfDay()` boundaries
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
"name": "omnia-docs",
|
||||
"compatibility_date": "2026-07-09",
|
||||
"assets": {
|
||||
"directory": "./dist"
|
||||
"directory": "./dist",
|
||||
},
|
||||
"routes": [
|
||||
{
|
||||
"pattern": "omnia.adityagupta.dev/docs*",
|
||||
"zone_name": "adityagupta.dev"
|
||||
}
|
||||
]
|
||||
"zone_name": "adityagupta.dev",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user