/** * An effect a tick may emit. Structured-clonable JSON ONLY — no functions/closures. * * `id` is your stable correlation/idempotency key (e.g. `"npc:7:turn-42"`). It is * MANDATORY for charged kinds (ai/function/agent/onchain) so an in-sim retry of the * SAME logical action reuses the SAME id and never double-charges. For the other * kinds the platform assigns a deterministic `${tick}:${seq}` fallback. * * `onBehalfOf` runs the effect AS that player (their auth/rules downstream). It must * be a player whose intent you processed in the SAME tick (no privilege escalation); * omit it to run as the game's own service principal. */ export type Effect = { kind: "ai"; id: string; model: string; input: unknown; onBehalfOf?: string; } | { kind: "function"; id: string; name: string; args?: Record; onBehalfOf?: string; } | { kind: "agent"; id: string; agent: string; message: unknown; onBehalfOf?: string; } | { kind: "http"; id?: string; url: string; method?: string; headers?: Record; body?: unknown; /** Secret NAME(s) only — the value is injected host-side on egress, never in your code. */ secret?: string | string[]; onBehalfOf?: string; } | { kind: "onchain"; id: string; collection: string; op: "set" | "delete"; data?: Record; onBehalfOf?: string; /** Require the player to co-sign the transaction. */ cosign?: boolean; } | { kind: "data"; id?: string; op: "set" | "delete"; path: string; document?: Record; onBehalfOf?: string; } | { kind: "schedule"; id?: string; name: string; atMs?: number; everyMs?: number; }; export type EffectKind = Effect["kind"]; /** Reserved intent address an effect result arrives on. */ export declare const EFFECT_INTENT_ADDRESS: "@effect"; /** * The result of an effect, re-entered into your tick as an intent on the reserved * `@effect` address. Match `effectId` to the `id` you emitted; read `result` iff * `ok`, else `error` (an enumerated code). */ export interface EffectResult { __effect: true; effectId: string; ok: boolean; result?: T; error?: string; } /** A per-tick input: a player intent, or an `@effect` result the platform re-injected. */ export interface LiveIntent { address: string; intent: unknown; } /** * The tick may return the bare next state, OR `{ state, effects }` to also emit * effects this tick. Use {@link withEffects} to build the wrapper: `effects` is * required, because it is the marker the runtime recognizes the wrapper by (a bare * state that merely has a `state` field must never be unwrapped). */ export type LiveTickResult = S | { state: S; effects: Effect[]; }; /** A Bounded live module (game/realtime-room logic) that can emit effects. */ export interface LiveModule { init(seed: unknown): S; tick(state: S, intents: LiveIntent[], dt: number): LiveTickResult; views?(state: S): Record; } /** * Build the `{ state, effects }` tick return. Always include this (even with an * empty list) when you intend the wrapper, so the runtime never has to guess * whether your state object happens to look like the wrapper. * * return withEffects(next, [{ kind: "ai", id: `npc:${id}:${next.turn}`, model, input }]); */ export declare function withEffects(state: S, effects?: Effect[]): { state: S; effects: Effect[]; }; /** * Narrow an incoming intent to an effect result inside your tick: * * for (const i of intents) { * if (i.address === EFFECT_INTENT_ADDRESS && isEffectResult(i.intent)) { * applyResult(state, i.intent); // i.intent.effectId / .ok / .result / .error * } else { applyPlayerIntent(state, i); } * } */ export declare function isEffectResult(intent: unknown): intent is EffectResult; /** Identity helper purely for type inference when authoring a module. */ export declare function defineLiveModule(mod: LiveModule): LiveModule;