Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
6aa02bb
docs: draft llm client and actor input/output interfaces.
Myriad-Dreamin Dec 30, 2025
14d1e2a
fix: valid id
Myriad-Dreamin Dec 30, 2025
c86370c
dev: improve actor docs
Myriad-Dreamin Dec 30, 2025
a653bd9
fix: format
Myriad-Dreamin Dec 30, 2025
27e7116
Update packages/ema/src/concept/actor.ts
Myriad-Dreamin Dec 30, 2025
d79e670
Update packages/ema/src/concept/llm.ts
Myriad-Dreamin Dec 30, 2025
958ea4c
draft
Myriad-Dreamin Jan 3, 2026
1c57e86
feat: finish agent interface
Myriad-Dreamin Jan 6, 2026
7032898
dev: design scheduler
Myriad-Dreamin Jan 6, 2026
87fe8b2
dev: space
Myriad-Dreamin Jan 6, 2026
ad0d3fa
dev: agent arg
Myriad-Dreamin Jan 6, 2026
48cfe16
dev: split cron function
Myriad-Dreamin Jan 6, 2026
2fff0e6
dev: agent events
Myriad-Dreamin Jan 6, 2026
6d216a4
fix: examples
Myriad-Dreamin Jan 6, 2026
0ce8eee
fix: simplify examples
Myriad-Dreamin Jan 6, 2026
3683b96
dev: move accessor
Myriad-Dreamin Jan 6, 2026
19c5682
fix: comments
Myriad-Dreamin Jan 6, 2026
7293b79
Update packages/ema/src/concept/llm.ts
Myriad-Dreamin Jan 6, 2026
c87e3ad
dev: export agent state
Myriad-Dreamin Jan 6, 2026
6d41d28
dev: update description
Myriad-Dreamin Jan 6, 2026
2c4f3c7
dev: update description
Myriad-Dreamin Jan 6, 2026
f7b9c6c
dev: add comments
Myriad-Dreamin Jan 13, 2026
6acadc6
dev: update example
Myriad-Dreamin Jan 13, 2026
9c05868
dev: update example
Myriad-Dreamin Jan 13, 2026
da19975
dev: add stateless api
Myriad-Dreamin Jan 13, 2026
a2ae025
dev: update interface
Myriad-Dreamin Jan 13, 2026
99136c3
dev: update interface
Myriad-Dreamin Jan 13, 2026
e7cad00
feat: synchronized documents
Disviel Mar 1, 2026
67a04ee
fix: align concept task typing with non-generic agent
Disviel Mar 1, 2026
2b599e2
Merge branch 'main' into io-interface
Disviel Mar 1, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions docs/.vitepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ export default {
title: "EverMemoryArchive",
description:
"EverMemoryArchive is a platform for creating and managing memory-based agents.",
head: [
...mermaidHead()
],
themeConfig: {
sidebar: [
// overview
Expand Down Expand Up @@ -50,6 +53,41 @@ export default {
]
};

function mermaidHead() {
return [
[
'script',
{
id: 'mermaid-js',
src: 'https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js',
async: true,
}
],
[
'script',
{},
`;(() => {
const mermaidJS = document.getElementById('mermaid-js');
document.addEventListener('DOMContentLoaded', async () => {
await mermaidJS.ready;
const isDark = document.documentElement.classList.contains('dark');
mermaid.initialize({ startOnLoad: false, theme: isDark ? 'dark' : 'neutral' });
const mermaidElements = document.getElementsByClassName('language-mermaid');
for (const code of mermaidElements) {
const preCode = code.querySelector('pre code');
if (preCode) {
const codeText = preCode.textContent;
const randomId = "mermaid-" + Math.random().toString(36).substring(2, 15);
const {svg} = await mermaid.render(randomId, codeText);
code.innerHTML = svg;
}
}
});
})()`
]
];
}

function flatHttpSidebar(sidebar) {
const routes = [];
walk([], sidebar);
Expand Down
7 changes: 6 additions & 1 deletion packages/ema/src/actor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ export interface ActorScope {
}

/**
* A facade of the actor functionalities between the server (system) and the agent (actor).
* A facade of actor runtime behavior between server and agent.
*
* Note:
* - Current production runtime (memory branch) uses queued inputs, interruptible
* runs, and conversation-scoped memory injection.
* - This branch keeps a simplified compatibility implementation for reference.
*/
export class ActorWorker {
/** Event emitter for actor events. */
Expand Down
132 changes: 132 additions & 0 deletions packages/ema/src/concept/actor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import type { EventEmitter } from "node:events";
import type { Content as InputContent } from "../schema";
import type { AgentEventName, AgentEvent, AgentEventUnion } from "./llm";

/**
* The scope information for the actor.
*/
export interface ActorScope {
actorId: number;
userId: number;
conversationId: number;
}

/**
* A batch of actor inputs in one request.
*/
export type ActorInputs = InputContent[];

/**
* The status of the actor.
*/
export type ActorStatus = "preparing" | "running" | "idle";

/**
* A message from the actor.
*/
export interface ActorMessageEvent {
/** The kind of the event. */
kind: "message";
/** The content of the message. */
content: string;
}

/**
* An event forwarded from the agent.
*/
export interface ActorAgentEvent {
/** The kind of the event. */
kind: AgentEventName;
/** The content of the message. */
content: AgentEventUnion;
}

/**
* The event map for actor events.
*/
export interface ActorEventMap {
message: [ActorMessageEvent];
agent: [ActorAgentEvent];
}

/**
* Union of actor event names.
*/
export type ActorEventName = keyof ActorEventMap;

/**
* Type mapping of actor event names to their corresponding event data types.
*/
export type ActorEvent<K extends ActorEventName> = ActorEventMap[K][0];

/**
* Union type of all actor event contents.
*/
export type ActorEventUnion = ActorEvent<ActorEventName>;

/**
* Constant mapping of actor event names for iteration.
*/
export const ActorEventNames: Record<ActorEventName, ActorEventName> = {
message: "message",
agent: "agent",
};

/**
* Event source interface for the actor.
*/
export interface ActorEventSource {
on<K extends ActorEventName>(
event: K,
handler: (content: ActorEvent<K>) => void,
): this;
off<K extends ActorEventName>(
event: K,
handler: (content: ActorEvent<K>) => void,
): this;
once<K extends ActorEventName>(
event: K,
handler: (content: ActorEvent<K>) => void,
): this;
emit<K extends ActorEventName>(event: K, content: ActorEvent<K>): boolean;
}

/**
* Typed event emitter for actor events.
*/
export type ActorEventsEmitter = EventEmitter<ActorEventMap> & ActorEventSource;

/**
* Type guard that narrows an actor event to a specific agent event (or any agent event).
*/
export function isAgentEvent<K extends AgentEventName | undefined>(
event: ActorEventUnion,
kind?: K,
): event is ActorAgentEvent &
(K extends AgentEventName
? { kind: K; content: AgentEvent<K> }
: ActorAgentEvent) {
if (!event) return false;
if (event.kind === "message") return false;
return kind ? event.kind === kind : true;
}

/**
* A facade of the actor functionalities between the server (system) and the agent (actor).
*/
export declare class ActorWorker {
/**
* Event emitter for actor events.
*/
readonly events: ActorEventsEmitter;
/**
* Enqueues inputs and runs the agent sequentially for this actor.
* @param inputs - Batch of user inputs for a single request.
* @param addToBuffer - Whether to persist inputs to conversation buffer.
*/
work(inputs: ActorInputs, addToBuffer?: boolean): Promise<void>;
/**
* Reports whether the actor is currently preparing or running.
*/
isBusy(): boolean;
}
41 changes: 41 additions & 0 deletions packages/ema/src/concept/compat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { Message } from "../schema";

/**
* Compatibility-only types for legacy actor implementation in this branch.
* These are intentionally not exported from `concept/index.ts`.
*/

export interface ActorStateStorage {
getState(): Promise<ActorState>;
updateState(state: ActorState): Promise<void>;
}

export interface ActorState {
memoryBuffer: Message[];
}

export interface ActorMemory {
search(keywords: string[]): Promise<SearchActorMemoryResult>;
addShortTermMemory(item: ShortTermMemory): Promise<void>;
addLongTermMemory(item: LongTermMemory): Promise<void>;
}

export interface SearchActorMemoryResult {
items: LongTermMemory[];
}

export interface ShortTermMemory {
kind: "year" | "month" | "day";
os: string;
statement: string;
createdAt: number;
}

export interface LongTermMemory {
index0: string;
index1: string;
keywords: string[];
os: string;
statement: string;
createdAt: number;
}
52 changes: 52 additions & 0 deletions packages/ema/src/concept/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Conceptual architecture of EverMemoryArchive (aligned with memory runtime design).
*
* High-level flow:
* 1. UI sends user input to server APIs.
* 2. Server resolves `(user, actor, conversation)` and routes to an `ActorWorker`.
* 3. `ActorWorker` serializes buffer writes, queues inputs, and may interrupt an in-flight run.
* 4. `Agent` executes an LLM+tool loop and emits structured events.
* 5. `MemoryManager` persists conversation buffer and memory records, then injects memory into prompts.
* 6. `Scheduler` drives foreground/background jobs (for reminders and memory organization).
*
* ```mermaid
* graph TD
* subgraph UI["UI Layer"]
* WebUI["Web UI"]
* Clients["Other Clients"]
* end
*
* subgraph Runtime["EMA Runtime"]
* Server["Server"]
* Actor["ActorWorker"]
* Agent["Agent (LLM + Tools Loop)"]
* Memory["MemoryManager"]
* Scheduler["Scheduler"]
* end
*
* subgraph Storage["Storage Layer"]
* Mongo["MongoDB"]
* Lance["LanceDB (Vector Search)"]
* end
*
* subgraph LLM["LLM Providers"]
* OpenAI["OpenAI-Compatible"]
* Google["Google GenAI"]
* end
*
* UI --> Server
* Server --> Actor
* Actor --> Agent
* Agent --> LLM
* Actor --> Memory
* Memory --> Storage
* Server --> Scheduler
* Scheduler --> Actor
* ```
*
* @module @internals/concept
*/

export * from "./actor";
export * from "./llm";
export * from "./storage";
Loading