underworld-graph
v0.3.1
Published
Bi-temporal narrative state graph for fiction-writing engines.
Maintainers
Readme
underworld-graph
Bi-temporal narrative state graph for fiction-writing engines.
Documentation: https://uw.emaostudio.online/
Standalone narrative state management library. Stores entities, relations, facts, events, and visibility declarations on a bi-temporal SQLite + TypeGraph backend.
Originally extracted from the narrative-engine monorepo as its core data asset,
now an independent package consumable by any narrative tool, visualizer, or importer.
Features
- Bi-temporal model: every state declaration carries
validFrom/validTo(story time) andrecordedAt(transaction time, via SDK recorded instant) - Entity lifecycle: birth / death with cascading fact + relation closure
- Visibility tracking: per-character knowledge with explicit / inferred sources
- Event causality: append-only JSONL event log with
causedBychain tracing - Full-text + vector search: via
@nicia-ai/typegraph+sqlite-vec
Install
npm install underworld-graphQuick start
import { WorldGraph } from "underworld-graph";
const wg = await WorldGraph.create({
dbPath: "./world.db",
eventLogPath: "./events.jsonl",
});
await wg.birthEntity("ent-macbeth", "character", { title: "Thane of Glamis" }, "act1-scene1");
await wg.processEvent({
eventId: "evt-1",
type: "change",
storyTime: "act1-scene4",
entityId: "ent-macbeth",
invalidated: [],
newFacts: [
{ entityId: "ent-macbeth", property: "mood", description: "ambitious", modality: "fact" },
],
});
const snap = await wg.getEntityAt("ent-macbeth", "act1-scene4");
console.log(snap);
wg.close();storyTime 约定
storyTime / validFrom / validTo 是纯字符串,时态查询按字典序比较
(validFrom <= storyTime < validTo)。请使用可字典序比较的格式(如
act01-scene01 零填充,或 ISO 8601),避免 act1-scene10 排在
act1-scene2 之前的问题。INFINITY("Infinity")表示未闭合,比较时须特判。
API
Factory
WorldGraph.create(opts): Promise<WorldGraph>— async factory; initializes SQLite + sqlite-vec + TypeGraph store.opts.storyTimePattern?: RegExp可启用 storyTime 格式校验(推荐/^ch\d{3}\.ev\d{3}$/);opts.embedder供 updateEntitySummary 重嵌入。WorldGraph.migrate(opts): Promise<MigrateResult>— migrate TypeGraph schema version (e.g. after graph definition changes); acceptsWorldGraphOptionsor{ dbPath }; real legacy data migration is handled by the consumer's importer.
Entity lifecycle
birthEntity(entityId, type, initialProps, storyTime, summary?, extraFacts?, opts?)—extraFacts逐条写 Fact(透传 entityId/modality,同 property 多条保留);opts.strict严格模式下实体已存活抛错birthEntityUpsert(...)— 同 birthEntity 签名;实体已存活则幂等跳过killEntity(entityId, storyTime)— cascades to close open facts and relationsgetEntityAt(entityId, storyTime, opts?)— bi-temporal snapshotgetAllEntities(storyTime, opts?)getEntityHistory(entityId, opts?)— all versions incl. closedupdateEntitySummary(entityId, summary, storyTime)— 覆盖 summary + 写 change 事件(可回溯);配置embedder时触发重嵌入
Relations
addRelation(sourceId, targetId, label, storyTime, opts?)—opts.strict校验两端实体存活closeRelation(sourceId, targetId, label, storyTime)getRelations(entityId, storyTime, opts?)getRelationHistory(entityId?, opts?)
Events
processEvent(input)— append event + apply side effects (birth / death / change);input.strict严格模式校验引用完整性(不落日志)traceCauses(eventId): Promise<EventRecord[] | null>— walkcausedBychain backwards;eventId 不存在返回null,前驱丢失抛错getAllEvents()
Visibility
setVisibility(characterId, declarationId, visOpts, strictOpts?)setVisibilityIfAbsent(...)— 同 setVisibility 签名;已有未闭合记录则幂等跳过closeVisibility(characterId, declarationId, storyTime)getVisibilityForCharacter(characterId, storyTime, opts?)getVisibilityForDeclaration(declarationId, storyTime?, opts?)inferVisibility(storyTime, opts?)— auto-derive fromlocated_inrelationsgetCharacterView(characterId, storyTime, opts?)— declarations visible to a character
Declarations
getAllDeclarationsAt(storyTime, opts?)— valid at story timegetAllDeclarations(opts?)— all incl. closed (knowledge persistence)
Search & query
wg.search— passthrough TypeGraphStoreSearch(fulltext / vector / hybrid)wg.query()— TypeGraphQueryBuilderentrywg.recordedNow()— current transaction instantreembedAll(embedder),updateFactEmbedding(id, vec),updateEntityEmbedding(id, vec)
Utilities
listStoryTimes()— all distinct story time pointsclose()— release db handle
Architecture
- Storage: SQLite (via
better-sqlite3) +sqlite-vecfor vector index - Graph SDK:
@nicia-ai/typegraphprovides bi-temporal node/edge store with schema migration, full-text search (zh), and vector search - ORM:
drizzle-ormfor SQLite backend - Validation:
zodschemas for all public types
The graph defines four node types: Entity, Fact, Relation, Visibility,
with validFrom / validTo schema fields managed by this library to encode
bi-temporal semantics on top of TypeGraph's transaction-time history.
License
MIT
