/** * sim/world/gang.ts — gang scheduling across cohorts, ported from mirofish * `engine/world/gang.py`. One cohort = one World + its agents (one device per * case). Gang acquire: a cohort starts only when its FULL device need is free * — half-open worlds distort coupled simulations. Streaming release: a case * returns its device the moment it retires (via WorldRunner.onAgentRetire), * never waiting for the whole cohort; leftovers are returned at cohort end. * FIFO: the queue head waits for enough devices; no small-cohort queue jumping. * Devices are injected by the host (acquire/release callbacks); defaults are * pure counting for offline/mock runs. */ import type { UserSimulatorAgent } from "../agent.ts"; import type { SessionFrame, World } from "../models.ts"; export type AcquireFn = (cohort: Cohort) => Promise | void; export type ReleaseFn = (actorId: string) => Promise | void; export interface Cohort { world: World; agents: UserSimulatorAgent[]; /** Optional label for logs / per-cohort aggregation. */ key?: string; } export declare function cohortNeed(c: Cohort): number; export interface GangSchedulerOptions { /** Concurrent device budget (an ops knob, not simulation semantics). */ budget: number; acquire?: AcquireFn; release?: ReleaseFn; /** Passed through to each cohort's WorldRunner (intra-cohort peak shaving). */ maxConcurrency?: number; } export declare class GangScheduler { private cohorts; private budget; private acquire?; private release?; private maxConcurrency?; private free; private gate; private results; private peakUsed; constructor(cohorts: Cohort[], opts: GangSchedulerOptions); get peak_used(): number; /** FIFO-schedule every cohort; returns frames per cohort in input order. */ run(): Promise; private runCohort; }