/** * sim/world/environment.ts — the shared-medium implementations, ported from * mirofish `engine/world/environment.py`. * * An Environment is a MEDIUM, not a world model — world thickness lives in * the real end and the World.situation prose. publish() is not restricted to * agents: exogenous events (plot beats, ops actions) are just another * publisher. Delivery semantics: never echo to the speaker; `to` empty = * broadcast, non-empty = addressed only (@-directed selective wake-up). */ import type { EnvironmentSpec, Executor, FileSystemEnvSpec, InMemoryEnvSpec, IsolatedEnvSpec, Part, PlatformEnvSpec, SharedEndEnvSpec } from "../models.ts"; /** The medium contract (live object: never enters a World, never serializes). */ export declare abstract class Environment { abstract publish(source: string, parts: Part[]): Promise; /** Increment since this agent's last observe (consumes the cursor; no echo). */ abstract observe(agentId: string): Promise; /** Full panorama (read-only derivation, cursor untouched). */ abstract snapshot(): Promise; /** Whether this agent has unconsumed content. Default true (unknowable ⇒ * degrade to lockstep). */ pending(_agentId: string): Promise; /** Super-step boundary hook: refresh expensive derived views. */ tick(): Promise; } /** Append-only log + per-agent cursor + no-echo + @-filter — the shared core. */ declare class LogMedium extends Environment { protected log: Part[]; protected cursor: Map; publish(source: string, parts: Part[]): Promise; observe(agentId: string): Promise; snapshot(): Promise; pending(agentId: string): Promise; } /** Pure conversational medium: broadcast, observe = increments. */ export declare class InMemoryEnvironment extends LogMedium { readonly spec: InMemoryEnvSpec; constructor(spec: InMemoryEnvSpec); } /** Shared directory: writing files IS publishing; observe = event increments * plus file changes (new/mtime; changes have no author, everyone sees all). */ export declare class FileSystemEnvironment extends LogMedium { readonly spec: FileSystemEnvSpec; static readonly EVENTS = ".events.log"; readonly root: string; private seenMtime; /** Per-super-step tree snapshot (tick refreshes; null = scan on demand). */ private files; constructor(spec: FileSystemEnvSpec); private scan; private current; tick(): Promise; publish(source: string, parts: Part[]): Promise; observe(agentId: string): Promise; snapshot(): Promise; pending(agentId: string): Promise; } /** Several agents share one live end: the medium IS the end. The executor is * injected (borrowed); observe adds the end's light state. End state can * change any time ⇒ pending stays true (degrades to lockstep under reactive). */ export declare class SharedEndEnvironment extends LogMedium { readonly spec: SharedEndEnvSpec; private readonly executor; constructor(spec: SharedEndEnvSpec, executor: Executor); private endState; observe(agentId: string): Promise; snapshot(): Promise; pending(_agentId: string): Promise; } type FeedFn = (env: PlatformEnvironment, unseen: number[], n: number, aid: string) => number[]; /** * Simulated content platform (post → distribute → scroll the feed): the * recommender is just an observe strategy. Engagement credit: when an agent * speaks, every post served on their current screen earns one interaction * (speak is the only observable engagement in the medium). Rate over count — * Laplace-smoothed (engaged+1)/(impressions+2) — so seen-and-answered posts * rise instead of exposure-rich ones locking in. The social graph is a stable * hashed G(n,p): edge = sha1(seed|min|max) first 8 bytes / 2^64 < social_p, * independent of arrival order and identical across processes AND languages. */ export declare class PlatformEnvironment extends Environment { readonly spec: PlatformEnvSpec; private posts; private seen; private engaged; private impressions; private lastServed; private rng; private edgeSet; private edgeCache; static readonly FEEDS: Record; constructor(spec: PlatformEnvSpec); private sample; private score; private engagementPick; /** Stable undirected edge: identical to the Python side byte for byte * (sha1("{seed}|{min}|{max}") first 8 bytes big-endian / 2^64 < p). */ isNeighbor(u: string, v: string): boolean; private socialPool; publish(source: string, parts: Part[]): Promise; private unseenOf; observe(agentId: string): Promise; snapshot(): Promise; pending(agentId: string): Promise; } /** * Uncoupled medium (independent ensembles: predict/freeform/review): agents * never see each other. Only shared/exogenous events (source=="system") are * routed; agent speech never enters the medium (their own frame.history is * already the record). framing prepends once on first observe so an empty * environment isn't misread as broken. */ export declare class IsolatedEnvironment extends Environment { readonly spec: IsolatedEnvSpec; static readonly SHARED = "system"; private shared; private cursor; private framed; constructor(spec: IsolatedEnvSpec); private framing; publish(source: string, parts: Part[]): Promise; observe(agentId: string): Promise; snapshot(): Promise; pending(agentId: string): Promise; } /** Instantiate the live medium from its pure-data spec. shared_end requires * the host-acquired executor (borrowed here, never acquired). */ export declare function buildEnvironment(spec: EnvironmentSpec, opts?: { executor?: Executor; }): Environment; export {};