Sim (npc.py)
- Add reproduction: in-conversation
askSexPossibleChild-> the partner's next menu carriesacceptSexPossibleChild(any other action = silent decline, the refusal lives in their words); an accepted pair is expecting, due inGESTATION_DAYS = 21(~9 months at 28-day years, pure countdown). - Add parent naming at birth: on the due day the accepter's turn becomes the naming turn through the normal full-history pipeline (any name agreement from the gestation weeks is in context); 1 retry on collision/multiword, then a
Kid{day}placeholder announced clearly; dead names honorable, living names collide; namer falls back to the surviving parent; no living parent -> pregnancy culled. - Add newborn = full agent from breath one: spawns at Home, age 0, energy 100, standard $10, empty memory; one rule-broadcast "You were born today by X and Y. Live your life!"; the birth is announced to everyone like a death, so both parents always learn the name.
- Add generic mod seams (
mod_conv_menu/mod_conv_action/mod_turn_override): a new feature = functions in one section, zero core-loop edits - reproduction is the first mod; hallucinated keywords outside the menu stay inert. - Add
REPRODUCTION_HOME_ONLY(False= any location; restrains reproduction to the more intimate setting: in conversation and at Home) and theADULT_AGEmenu gate (both partners adult or the action is simply never offered; agents still see their age each morning). - Add per-pair gestation lock (decision): the same pair can't stack a second child while expecting (re-ask -> "already expecting, due day X"); different pairings stay independent; after a birth the pair can conceive again.
- Fix
talkTofirst-match resolution: a dead namesake (a child named after the dead) can no longer shadow the living target, and the namesake's undelivered notices die with them instead of haunting the child. - Fix the JSON-failure sentinel leaking into naming (a double extraction failure used to birth a child called "First"; now it lands on the
Kid{day}placeholder). - Fix pregnancy bookkeeping to object identity (
objs): a child named after a dead parent can no longer stand in for that parent in their other pregnancy (a newborn "giving birth"), and a living namesake can't keep a dead couple's countdown alive. - Fix
talkToself-match: a hallucinated talkTo-self froze the agent in a solo conversation forever (and blocked the all-dead exit) - a latent core bug older than this release. - Fix same-day naming-failure twins (
Kid{day}placeholders uniquified) and the very-late all-asleep skip that swallowed a whole simulated day. - Add DRY helpers (
capacity/cafe_workers/available_others/notify/strip_think/diary_notes/fail) - behavior-neutral, model-visible prompts byte-identical; hardened all agent lookups to object identity. - Fix action-prefix parsing to slicing (replace-all mangled names containing "talkTo"/"go"), guard
strip_thinkagainst a quadratic scan on unclosed tags (measured 30s browser freeze), and strip whitespace from model action strings. Vetted by a 5-round adversarial audit loop (44 regression checks green). - Add
MAX_CONV_TURNS = 8config (was hardcoded): utterances per conversation, both speakers count - 8 = 4 lines each.
Browser harness (index.html)
- Fix the player input line editor: input that wrapped past the terminal width duplicated itself on every keystroke, and left/right arrows could not cross wrapped rows - the redraw is now wrap-aware (move to logical start, clear down, reposition), verified live.
Browser harness (index.html) - ChatGPT account tier
- Add a ChatGPT account engine: sign in with the OAuth device flow and the NPCs run on your own ChatGPT plan. Entirely in-page - the token is held in this browser's localStorage (deliberately outside the resume log and the shared archive) and calls go out over libcurl/WISP, so a static page needs no backend.
- Add an account-scoped model list: the catalog is fetched per plan, so a free account sees its models and Pro sees its own; internal entries are filtered out and the API's own priority ordering is kept.
- Add a per-model reasoning Effort control, built from each model's declared levels (low through ultra where offered) and removed entirely for models that do not reason. Editable mid-run - the next NPC turn uses it.
- Add auto model fallback: on a quota refusal the tier walks the account's remaining models, shows the switch in the dropdown, and reports when the quota resets.
- Fix a set of harness issues found in review: a bounded wait-out retry so an outage no longer floods the diary with fallback lines; the resume log now latches instead of failing silently at the storage limit; the final upload is no longer lost to a race; a changed local GGUF path is honored instead of silently ignored; per-tier model fields no longer overwrite each other; a failed mid-run engine switch reverts the dropdown instead of misreporting the tier.
- Harden: SRI-pinned CDN scripts, control-char stripping on upstream error text, and single-flight sign-in.
Sim (npc.py) - memory
- Fix the write-only diary:
diary_notesreturneddiary[-3:], but consolidation puts the digest at index 0 followed by 6 raw entries, so the diary is always >=7 long and the digest could never be read back. Every consolidation the sim paid an LLM call for was written and shown to nobody, and the real memory horizon was 3 nights. The digest is now pinned and span-labelled (D1-D19), but only once it is a real digest - pinning the oldest raw note froze day 1 into every wake prompt. - Fix the summariser overwriting 10 days with
"Various events occurred"when the call failed: the raw entries are kept and the retry moves to the next sleep. Digests also carry the span forward, so the second cycle readsD1-D19instead ofD10-D19. - Add snapshot restore: a saved envelope rebuilds the cast, their money, energy, location, age and diaries, plus pregnancies (bound by object identity) and conception counters. Restored state is state, not a replay, so it survives an
npc.pychange.
Sim (npc.py) - clock and sleep
- Fix a crash that ended runs permanently:
sleep_remaining_hoursdecrements by0.5, so the all-asleep jump could handadvance_timea float, making every clock field a float untilformat_time's:02draised. - Night is no longer skipped while anyone is awake. Dawn is skipped only when every agent is asleep or dead, the jump lands on the first waking rather than the last, and midnight is bookkeeping only - an awake agent keeps playing instead of being force-skipped to 06:00.
- Widen the bedtime window to 20:00-08:00. Sleep duration stays one constant for everyone and only bedtime differs, so schedules drift apart on their own and agents can genuinely miss each other.
Sim (npc.py) - reproduction
- Replace the conception roll with an energy-banded attempt count (
CONCEIVE_BANDS): rested pairs conceive on the first try, worn-out pairs need up to three, never more, and the tired partner governs. Deterministic, so record/replay still holds, and discoverable without a stat being shown. - An expecting pair keeps the proposal. Sex is not only for conceiving, so the menu no longer withholds it; it simply cannot conceive twice, and it says so with the days remaining instead of failing mutely.
- Fix a fumbled keyword eating consent: an accepter emitting the asker's token while saying a plain yes had their proposal silently consumed and read as a refusal. Only a legitimate
noneorleaveConversationexpires it now. - Fix the naming notice promising a joint deadline the sim never checked. Only the accepter is prompted at birth, so the message names whoever will actually be asked.
- Add notice supersession: several attempts inside one unflushed batch used to arrive as "we failed" plus "we are expecting" plus "we are already expecting" in a single prompt, reading as contradiction. A later notice on the same subject replaces the stale one.
Sim (npc.py) - information delivery
- Conversation turns now deliver queued notices and the wake greeting. Both previously waited for the next open-world turn, so the only way to learn the outcome of your own action was to leave the conversation or hit the turn cap, and "You just wake-up" could print an hour and several turns after actually waking. System facts go first and the partner's words stay last, where attention is strongest.
- Fix
extract_clean_jsondying on hostile replies: deeply nested JSON or a 4301-digit integer raised past the handler and killed the run permanently, and the reply was recorded before Python saw it, so the reload replayed straight back into it. - Write history with
ensure_ascii=False: a dash character reached the model as the literal text\u2014instead. - Refuse
goXto your current location (it silently dropped a cafe shift) andleaveConversationoutside a conversation (it logged a social event that never happened).
Browser harness (index.html) - rewind, save and quota
- Add a rewind picker on double-Escape: arrows pick a point, Enter rewinds, and an NPC's spoken line can be edited before continuing. The action is deliberately never editable - a rewritten action would replay as a real move and the history after it would describe something that never happened. Rewind reaches into restored history, including your own turns. Double-click equals Enter, and the list opens on the newest turn.
- Add save and resume in one file: a ShareGPT array that trains as-is under LlamaFactory, Unsloth and TRL, with the resume envelope carried on the first row. Verified against the real toolchain rather than assumed - a top-level object yields one row with no
conversationscolumn, an array yields N rows. - Add a Codex quota readout on the Engine control: remaining percent in the option, a drain-fill tinted moss to amber to blood, and the lanes with reset times in the tooltip. Lanes are keyed by window duration, never by
primary/secondaryposition, because a Plus account puts the 7-day window inprimarywhile free is monthly. Refreshes after each turn, unawaited so it adds no latency, floored at once a minute. - Rename the engine to ChatGPT Codex: the tier calls
backend-api/codex/responses, so it spends Codex quota shared with thecodexCLI, not ChatGPT chat messages. - Add word-boundary wrapping so output no longer breaks mid-word, applied at the display seam only, so the transcript and archive keep the raw line.
- Add cmd-style arrow history to the response prompt: arrows cross wrapped rows first, then walk history, and the half-typed draft comes back on the way down.
- Reproduction events get their own colour family, so a life event never reads as ordinary sim chatter.
- Fix a stdout forgery hole: a model reply containing a forged
DAYbanner or time line could move the harness's own day counter. - Fix the resume cursor never resetting in
run(): a post-crash resume rebuilt from day 1 with the cursor mid-log, and on an AI-only run every event is a reply, so the divergence check could never fire. - Speaker attribution on the echoed player line, the per-day share message replaced by an animation on the checkbox itself, control borders lifted to the 3:1 contrast floor, and focus rings on the remaining controls.
Worker (cf-worker)
- Lower
DB_CUTOFFto 900 MB: the 1 GB limit is per Durable Object, while the 5 GB figure is account-wide. - Refuse growth past the cutoff for existing rows too, not just new ones, while still accepting a shrinking or flat write. A full database answers 507 so the client latches instead of retrying; a transient failure answers 503.
- Rewrite
/exportto take the key list synchronously and pull one row at a time: Cloudflare states there is no snapshot isolation across an await, so a cursor held acrosspull()could miss or duplicate rows. Adds aneofsentinel carrying the planned count so a truncated download is detectable. dbSizenow fails closed on both the value and the throw path.
Repo
- Add an Apache-2.0
LICENSE. - Fold the philosophy and architecture notes into one collapsible block, so the technical detail stops standing between a reader and the emergence table. Adds an origin section: what each earlier project of mine was actually testing.
Browser harness (index.html) - engine reliability
- Paint the Codex quota as soon as the bridge exists. A reload restores the tier programmatically and fires no
changeevent, so the control readChatGPT Codexwith no number until a turn happened to finish. - The free tier now caches its last working model list. It had no cache and no hardcoded default, unlike the ChatGPT tier, so one refused probe emptied the model dropdown and pushed the run into a ~2 GB local download. A transient refusal now continues on the last known-good model instead.
- Replace the
Relay downmessage. The relay answers fine while a single destination is refused, so that wording pointed at the wrong layer. The real reason now reaches the terminal rather than only the devtools console, with the upstream URL scrubbed out, since that text gets archived and shared.
Full Changelog: 0.8.1...0.9.2