Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
286 commits
Select commit Hold shift + click to select a range
119bbe3
gallery
rondlite Jun 20, 2026
4a5dc07
gallery cors
rondlite Jun 20, 2026
603ba87
gallery cors
rondlite Jun 20, 2026
c24ec36
feat: single-page editor, native image tool, LoRA in chat, progressio…
rondlite Jun 20, 2026
1550eb9
feat: real Charluv W++ trait set, metadata-driven facets, gallery fixes
rondlite Jun 20, 2026
137f5aa
feat: character profile page before matching; skip create-chat form
rondlite Jun 20, 2026
e07ce34
inbetween
rondlite Jun 20, 2026
bb13e83
feat: image-card create wizard + character profile title
rondlite Jun 20, 2026
4151dee
feat: long-term memory — remember tool + RAG recall (xenova + Mongo)
rondlite Jun 20, 2026
b488cf1
fix: persist chat images so they survive refresh
rondlite Jun 20, 2026
5b57a2a
memory
rondlite Jun 20, 2026
c54bd8c
fix(memory): don't log memory text by default (PII); add tool diagnos…
rondlite Jun 20, 2026
0e1890e
fix(editor): culture no longer required (single-page editor removed t…
rondlite Jun 20, 2026
d33f74c
feat: in-band marker tools (image/memory) for the roleplay prompt format
rondlite Jun 20, 2026
82ea38c
feat(create): portrait generate/upload on the final wizard step
rondlite Jun 20, 2026
7b4049e
tweak: frame image/memory markers as an in-world Charluv feature
rondlite Jun 20, 2026
794c5de
fix: build sharp in container (memory embeddings); tune marker prompts
rondlite Jun 20, 2026
9d1072b
feat: disable legacy memory books + attached scenarios
rondlite Jun 20, 2026
5450486
feat: tool image attaches to its reply with a loading spinner
rondlite Jun 20, 2026
19adec4
fix: run sharp's install script directly (pnpm v10 gates rebuild too)
rondlite Jun 20, 2026
9785cc8
feat: unified character images, remove sprite, reuse LoRA name
rondlite Jun 20, 2026
c9780b5
chore(security): cut audit 154 -> 9; remove parcel/localtunnel; Node …
rondlite Jun 20, 2026
185ea48
fix(security): pin @babel/core override to 7.x (8.0.1 broke the Vite …
rondlite Jun 20, 2026
fec473f
chore(security): accept 9 irreducible advisories -> pnpm audit clean (0)
rondlite Jun 20, 2026
5d02588
fix(security): scope path-to-regexp override to express; drop bad ove…
rondlite Jun 21, 2026
0c677d8
feat(memory): remember the character's own invented details too
rondlite Jun 21, 2026
1848a2f
feat: My AI character profile + go straight to chat (drop create-chat…
rondlite Jun 21, 2026
a4d04f4
feat: fullscreen images on the Discover profile too (consistent with …
rondlite Jun 21, 2026
ef98cca
feat: locked base-look seed in the character editor (reroll + warning)
rondlite Jun 21, 2026
e12dfd9
ui: hint that gallery Generate needs appearance-prompt tweaks (locked…
rondlite Jun 21, 2026
2a58542
fix: cover updates immediately on Make-cover (no refresh needed)
rondlite Jun 21, 2026
86c31d3
perf: faster + tunable image gen (steps/size via env)
rondlite Jun 21, 2026
2ccb2a2
perf: split image steps — fast gallery, keep chat quality
rondlite Jun 21, 2026
9f4182f
fix: demote non-leading system messages so strict chat templates acce…
rondlite Jun 21, 2026
e48092c
fix: gallery generation spinner gets its own tile, not the cover slot
rondlite Jun 21, 2026
55658bc
fix: name {{char}}/{{user}} in stage definitions and fix HARDCORE gra…
rondlite Jun 21, 2026
b00d4d1
fix: stop characters sending unprompted opening images
rondlite Jun 21, 2026
99ef47d
feat: chat Memory pane manages long-term memories, not memory books
rondlite Jun 21, 2026
cf8b6f9
ignore
rondlite Jun 21, 2026
518258a
progression
rondlite Jun 21, 2026
800b6e6
fix: long-term memory rejects duplicates and transient scene facts
rondlite Jun 21, 2026
484a95a
feat: long-term memory supersedes contradicting/outdated facts
rondlite Jun 21, 2026
7e4ba22
feat: enable LLM scene-summarized image captions for the charluv service
rondlite Jun 21, 2026
0a24985
feat: auto-summarize long chats at ~8K tokens instead of a manual button
rondlite Jun 21, 2026
b124b14
feat: replace the preset editor with fixed temperature-only presets
rondlite Jun 21, 2026
a1675be
feat: wizard finish step auto-generates a portrait from an LLM-writte…
rondlite Jun 21, 2026
8c515db
feat: tune top_p per preset and transmit top_k/min_p to vLLM
rondlite Jun 22, 2026
5a0c732
feat: Patreon-only subscriptions; remove legacy shop and Stripe new-c…
rondlite Jun 22, 2026
a11e9ae
fix: SubscriptionPage type/null bugs (premium access, paypal date, un…
rondlite Jun 22, 2026
700f7b6
feat: remove chat graph + per-message fork (confusing chat splitting)
rondlite Jun 22, 2026
1630692
feat: reduce "Edit Chat" pane to a dedicated Reply Style picker
rondlite Jun 22, 2026
88e4a06
feat: auto-select model by tier; remove the chat "Select a Model" picker
rondlite Jun 22, 2026
784a53b
feat: force charluv reply-style presets; delete orphaned AgnaisticMod…
rondlite Jun 22, 2026
c26614f
fix: settings gear opens a blank modal (corrupted version expression)
rondlite Jun 22, 2026
f540918
feat: make UI Settings the only setting; drop chat UI pane and other …
rondlite Jun 22, 2026
c7512dd
feat: remove the entire guided tour system
rondlite Jun 22, 2026
7abc04b
chore: drop unused shepherd.js dependency
rondlite Jun 22, 2026
31b19d2
fix: stop orphaning ephemeral preview images on R2
rondlite Jun 22, 2026
31f1f6b
feat: charge character creation on entering the final step; resumable…
rondlite Jun 22, 2026
7f3c3be
feat: gate the final creation step behind registration for guests
rondlite Jun 22, 2026
510dbc4
feat: return guests to the create wizard after registering
rondlite Jun 22, 2026
104e116
feat: redesign the final create step (portrait + editable trait cards)
rondlite Jun 22, 2026
9b09524
feat: free character imports + import entry on Create, New-companion …
rondlite Jun 22, 2026
f2c34ab
fix(security): make free imports route-based, not a client flag
rondlite Jun 22, 2026
7e2734b
in between
rondlite Jun 22, 2026
456196f
fix: omit min_p/logit_bias on self-hosted endpoint (speculative decod…
rondlite Jun 22, 2026
09dac80
feat: declutter chat drawer; label the menu/chat toggle
rondlite Jun 22, 2026
28f3428
feat: dark-only theme; remove the light/dark toggle
rondlite Jun 22, 2026
00ca173
feat: full-width guest top bar; drop the side menu for logged-out vis…
rondlite Jun 22, 2026
9ec36fc
feat: let guests browse Discover; gate cloning behind registration
rondlite Jun 22, 2026
7a57526
feat: polish the Discover landing; run it full-bleed
rondlite Jun 22, 2026
cb9e2ab
feat(publish): schema + Discover union for user-published characters
rondlite Jun 22, 2026
3b2f973
feat(publish): server publish + report endpoints
rondlite Jun 22, 2026
fb32b6a
feat(publish): admin moderation queue; remove old submission funnel
rondlite Jun 22, 2026
612c3be
feat(publish): Make-Public modal + Report modal (user UI)
rondlite Jun 22, 2026
ff0f7af
feat(publish): admin config for publish caps, reward, and audience
rondlite Jun 22, 2026
0e8213a
fix: enforce dark theme at updateTheme (guests no longer get white)
rondlite Jun 22, 2026
e0e6bf9
fix: drop the duplicate Create CTA for guests on Discover
rondlite Jun 22, 2026
cb845df
feat(publish): re-moderate public characters on edit
rondlite Jun 22, 2026
a14c0de
feat: PUBLIC/PRIVATE status stamp on My AI cards
rondlite Jun 22, 2026
6306dd7
fix: drop the always-"Novice" stage badge from Discover cards
rondlite Jun 22, 2026
4aa5bcb
feat: restore favourite/copy/export/delete actions on My AI profile
rondlite Jun 22, 2026
4255c75
feat(publish): finish the admin section (nav link + configurable requ…
rondlite Jun 22, 2026
eb97181
fix(admin): backfill missing fields in server configuration
rondlite Jun 22, 2026
9b0ff7d
fix(admin): stop the user-info endpoint from hanging (524)
rondlite Jun 22, 2026
e330348
feat: move credits/premium to a top bar; use logoDark everywhere
rondlite Jun 23, 2026
6a40a8b
feat: switch icons from Lucide to Phosphor (duotone)
rondlite Jun 23, 2026
a80f5ef
feat: prepend the Charluv level-system preamble to every prompt
rondlite Jun 23, 2026
eef472e
fix: quote LEVEL tokens in the preamble to match the trained form
rondlite Jun 23, 2026
71a6799
fix: skip the level preamble for non-roleplay generations
rondlite Jun 23, 2026
d2dff77
fix: drop the "+" premium marker next to the logo
rondlite Jun 23, 2026
b67f1d4
fix: use events.removeListener (not events.off) in My AI profile cleanup
rondlite Jun 23, 2026
009d94c
feat(publish): built-in moderation (no preset) + enforce Discover/pro…
rondlite Jun 23, 2026
a222c04
feat: inference ignores custom user presets; always use the platform …
rondlite Jun 23, 2026
b4c279e
feat: remove the client-side embeddings worker and document-embed fea…
rondlite Jun 23, 2026
9f95d4c
fix: shared characters always present as XP 0
rondlite Jun 23, 2026
0ad7947
fix(admin): published characters now show in the Moderation queue
rondlite Jun 23, 2026
b57ff97
fix: refresh My AI list on mount so the Public/Private tag is current
rondlite Jun 23, 2026
1eecc1f
fix: brand-green logo wrapper; drop the chat-return toggle
rondlite Jun 23, 2026
dfcc0af
fix: token counting no longer depends on the removed embeddings worker
rondlite Jun 23, 2026
ec53ba8
feat: green icons + dusk-tinted menu/top-bar to match Discover
rondlite Jun 23, 2026
7d1a0c2
feat: remove chat auto-summarisation
rondlite Jun 23, 2026
9e61abe
fix: rewrite the Premium page with current info
rondlite Jun 23, 2026
a0fdd77
feat: credit caps free 500 / premium 5000; drop the Ronnie-evil path
rondlite Jun 23, 2026
ee3eccc
feat: charge 25 credits for image generation/regeneration
rondlite Jun 23, 2026
d4cfb1f
feat: charge 25 credits for auto-generated images in replies
rondlite Jun 23, 2026
84488ff
feat: migrate legacy memory books into long-term memory on chat open
rondlite Jun 23, 2026
130fe17
chore: delete dead Match pages; show level for all (drop parent gate)
rondlite Jun 23, 2026
173eed4
fix: "None" archetype is fixed — no fallback to romantic
rondlite Jun 23, 2026
c644097
feat: align archetype labels with vibe cards; AI-imagine persona on f…
rondlite Jun 24, 2026
56729f7
feat: raise create to 100 and edit to 30 credits
rondlite Jun 24, 2026
731546c
docs: add CLAUDE.md with codegraph-first workflow and project constra…
rondlite Jun 24, 2026
706bf74
fix: make My AI background full-bleed like Discover
rondlite Jun 24, 2026
819f32c
feat: remove the legacy scenario event state-machine
rondlite Jun 24, 2026
2acfe91
feat: remove the Library (memory-book authoring) route
rondlite Jun 24, 2026
144701a
feat: live favourite on profile, add New chat button, drop menu + sign
rondlite Jun 24, 2026
d352d61
feat: let moderators attach a reason when unpublishing, relayed to th…
rondlite Jun 24, 2026
cc90c68
fix: actually send the avatar to the vision model during moderation
rondlite Jun 24, 2026
43231cf
feat: moderate avatar + gallery; nudity auto-marks NSFW instead of de…
rondlite Jun 24, 2026
e533ae1
fix: keep Memory/EditMemory — it's shared by chat + Chub, not just th…
rondlite Jun 24, 2026
08b9e31
style: deepen the menu background to anchor it against the dusk content
rondlite Jun 24, 2026
2d5bac3
feat: remove the Sagas preview
rondlite Jun 24, 2026
229b652
feat: remove the Matches section (superseded by Discover + My AI)
rondlite Jun 24, 2026
4e7bd1d
feat: replace per-chat impersonation with a fixed profile self-persona
rondlite Jun 24, 2026
d329e8c
feat: drop invite codes from registration
rondlite Jun 24, 2026
c4df3b1
fix: align main menu icons with the profile avatar
rondlite Jun 24, 2026
97a0a73
feat: trim the edit-character header and stale toggles
rondlite Jun 24, 2026
30e6367
fix: keep the menu footer icons small and green
rondlite Jun 24, 2026
9d09db9
chore: tighten moderation wording — NSFW label, flag possible under-2…
rondlite Jun 24, 2026
2edabed
feat: Discover creator name + clone-driven popularity/trending
rondlite Jun 24, 2026
4f39df5
feat: distinct filled-rose notification bell when there are unseen items
rondlite Jun 24, 2026
5fe448c
docs: Event mode design spec + implementation plan
rondlite Jun 24, 2026
5afd86e
feat: add event mode + memoryDisabled fields to Chat schema
rondlite Jun 24, 2026
a5fbbd6
feat: add event scene/director prompt helpers + constants
rondlite Jun 24, 2026
84f75fc
feat: derive progression stage from the replying character
rondlite Jun 24, 2026
702c939
feat: add director electSpeaker inference for event turns
rondlite Jun 24, 2026
bdc68db
refactor: extract generateOneReply from generateMessageV2
rondlite Jun 24, 2026
2f395cb
feat: event director loop — multi-character replies per user turn
rondlite Jun 24, 2026
d64a854
fix: wrap event-branch body in try/finally to guarantee lock release
rondlite Jun 24, 2026
49acb9f
feat: gate long-term memory write+recall on chat.memoryDisabled
rondlite Jun 24, 2026
9f2b150
feat: add createEventChat server endpoint (POST /chat/event)
rondlite Jun 24, 2026
c5b7b81
feat: add createEventChat data fn and createEvent store action
rondlite Jun 24, 2026
a54c379
feat: add Event menu button and CreateEventModal
rondlite Jun 24, 2026
cbce106
feat: per-chat memory-disable toggle in chat settings
rondlite Jun 24, 2026
58ef521
fix: guard event-turn credit check before the generating ack
rondlite Jun 24, 2026
8bd837e
style: prettier-format event-mode + discover files
rondlite Jun 24, 2026
6811e99
fix: run event director on send + show event banner
rondlite Jun 24, 2026
5c1bac7
event
rondlite Jun 24, 2026
6af0317
update
rondlite Jun 24, 2026
64accbc
event fix
rondlite Jun 24, 2026
57c00a5
event fixes
rondlite Jun 24, 2026
ba440a2
fixes
rondlite Jun 24, 2026
04e1f9d
intermediate
rondlite Jun 24, 2026
5d45bcc
fix: scope long-term memory to the speaking character, not chat.chara…
rondlite Jun 25, 2026
7244fc5
director
rondlite Jun 25, 2026
f6f26ad
feat(promo): add promo-code + promo-redemption schema and indexes
rondlite Jun 25, 2026
532d223
feat(promo): add promo code CRUD and race-safe redemption
rondlite Jun 25, 2026
4dfc6b8
fix(promo): read findOneAndUpdate ModifyResult.value (mongodb v5)
rondlite Jun 25, 2026
6c317c1
feat(promo): add POST /user/promo/redeem route
rondlite Jun 25, 2026
b519bbb
feat(promo): add admin promo CRUD routes
rondlite Jun 25, 2026
1918b03
feat(promo): add redeemPromo user-store action
rondlite Jun 25, 2026
b05adc3
feat(promo): add redeem card and remove Stripe/legacy UI from subscri…
rondlite Jun 25, 2026
7878def
feat(promo): add admin-store promo actions
rondlite Jun 25, 2026
057ca0d
feat(promo): add admin Promo Codes page, route and nav link
rondlite Jun 25, 2026
32feb60
style(promo): prettier-format new/edited subscription + admin pages
rondlite Jun 25, 2026
0593fc3
fix(promo): apply final code review fixes — compensate on grant failu…
rondlite Jun 25, 2026
3197a69
fix(web): correct api callApi result typing and exclude service worke…
rondlite Jun 25, 2026
d9fe164
fix(web): resolve all 373 web typecheck errors
rondlite Jun 25, 2026
6d1b647
event fixes
rondlite Jun 25, 2026
953f871
feat(abuse): add fingerprint/restriction schema, index, and classifyR…
rondlite Jun 25, 2026
2da9b96
feat(abuse): checkFingerprint, restricted createUser opts, clearRestr…
rondlite Jun 25, 2026
bda8a5d
feat(abuse): gate registration on consent + fingerprint/IP verdict
rondlite Jun 25, 2026
a0caee1
feat(abuse): admin view + clear for credit-restricted accounts
rondlite Jun 25, 2026
009439e
feat(abuse): consented FingerprintJS device id at registration + priv…
rondlite Jun 25, 2026
2c75c4d
style(abuse): prettier-format touched server files
rondlite Jun 25, 2026
74db4bb
fix(abuse): strip abuse-internal fields from toSafeUser; clean clearR…
rondlite Jun 25, 2026
6157100
fix(build): pin esbuild override to ~0.25.0 so the vite dev server boots
rondlite Jun 25, 2026
6ed0736
fix(audio): import easy-speech from npm package instead of vendored C…
rondlite Jun 25, 2026
7811c9c
chore: remove unused CHUB character-hub and Venus (chub.ai) adapter
rondlite Jun 25, 2026
a08c107
feat(ui): use Phosphor mask-happy for the user-avatar fallback
rondlite Jun 25, 2026
5933109
refactor(ui): use mask-happy for all former VenetianMask (FaceMask) u…
rondlite Jun 25, 2026
e1a0d99
refactor(ui): extract shared warm-dusk palette partial
rondlite Jun 25, 2026
705663f
feat(ui): add warm-dusk blog page stylesheet
rondlite Jun 25, 2026
a84c9ff
feat(ui): rewrite home page as warm-dusk blog, drop dead content
rondlite Jun 25, 2026
1b0f606
feat(routing): make Discover the landing, move blog to /blog
rondlite Jun 25, 2026
a260057
fix(ui): pass 200ms delay to RecentChats loaded emit (was comma opera…
rondlite Jun 25, 2026
1d5b97c
style(ui): prettier-format blog restyle files
rondlite Jun 25, 2026
bc66d3d
director and other fixes
rondlite Jun 25, 2026
8d834f2
nsfw
rondlite Jun 25, 2026
d197f95
nsfw
rondlite Jun 25, 2026
a4db099
gaslight
rondlite Jun 25, 2026
13c8048
gaslight
rondlite Jun 25, 2026
0fa4b71
director
rondlite Jun 26, 2026
64ce33b
message
rondlite Jun 26, 2026
f0b3938
moderation
rondlite Jun 26, 2026
0bdf9c0
moderate
rondlite Jun 26, 2026
910d8f9
pwa
rondlite Jun 26, 2026
ee95592
fixes
rondlite Jun 26, 2026
4831bda
moderation
rondlite Jun 26, 2026
7844eef
moderation
rondlite Jun 26, 2026
e905824
Merge remote-tracking branch 'origin/trunk' into feat/charluv-rewrite
rondlite Jun 26, 2026
b38253f
google
rondlite Jun 26, 2026
20d9eff
css
rondlite Jun 26, 2026
380cd91
event page
rondlite Jun 26, 2026
34eccd0
patreon etc
rondlite Jun 26, 2026
5641c29
flow
rondlite Jun 26, 2026
4b5336a
faq
rondlite Jun 26, 2026
ff5f8dc
patreon fix
rondlite Jun 26, 2026
d224bb6
patreon fix
rondlite Jun 26, 2026
0fc41c2
feat(queue): scheduling core + config
rondlite Jun 26, 2026
7ed6150
feat(queue): in-process LocalBackend with acquire/release/setPauseText
rondlite Jun 26, 2026
d6800c8
feat(queue): vLLM metrics parser + circuit breaker
rondlite Jun 26, 2026
84809e7
feat(queue): PriorityGate facade + priority mapping
rondlite Jun 26, 2026
0776134
feat(queue): redis-backed distributed gate
rondlite Jun 26, 2026
a8d4b8d
feat(queue): singleton gate wiring + metrics poller startup (task 6)
rondlite Jun 26, 2026
ac76ebc
fix(queue): guard release on backend failure + defer redis backend se…
rondlite Jun 26, 2026
e95ea5b
feat(queue): gate text + image generation through the priority queue
rondlite Jun 26, 2026
437751e
feat(queue): show queue position to waiting users
rondlite Jun 26, 2026
32d0d3b
chore(queue): prettier formatting
rondlite Jun 26, 2026
0f37167
fix(queue): ZREM waiter on acquire failure + restore image domain com…
rondlite Jun 26, 2026
0fdd713
queue
rondlite Jun 26, 2026
7843b3f
feat(queue): cross-process presence registry
rondlite Jun 26, 2026
2abc2b9
feat(queue): drop disconnected clients at gate admission
rondlite Jun 26, 2026
1d62660
feat(queue): track websocket presence for disconnect-aware admission
rondlite Jun 26, 2026
281770d
fix(queue): refund credits when a request is dropped on client discon…
rondlite Jun 27, 2026
94976ec
fix edit character in chat
rondlite Jun 27, 2026
377d5b8
lora pill
rondlite Jun 27, 2026
28cc02c
fixes
rondlite Jun 27, 2026
9d70327
fixes
rondlite Jun 27, 2026
a1d76cf
appearance
rondlite Jun 27, 2026
6fee2f0
stages
rondlite Jun 27, 2026
2ee12d1
update
rondlite Jun 27, 2026
b6bb317
discover skip copy for existing
rondlite Jun 27, 2026
ddb271e
fixes
rondlite Jun 28, 2026
b10aece
fixes
rondlite Jun 28, 2026
5950dce
fixes
rondlite Jun 28, 2026
9124111
admin publish limit
rondlite Jun 28, 2026
de37484
add tutu
rondlite Jun 28, 2026
b8049cd
fixes
rondlite Jun 29, 2026
f6da3a0
fixes
rondlite Jun 29, 2026
51a89c0
director
rondlite Jun 29, 2026
bc08944
director
rondlite Jun 29, 2026
038e30c
director
rondlite Jun 29, 2026
c9178bb
director
rondlite Jun 29, 2026
45a7170
events fix
rondlite Jun 29, 2026
232378d
voice gone
rondlite Jun 29, 2026
1bb9a85
chore: run prettier across branch
rondlite Jun 29, 2026
bcda822
fix: close publish/moderation gaps in the discover clone + gallery flow
rondlite Jun 29, 2026
57bfc14
fix: Solid Setter<File> typecheck error in Create wizard
rondlite Jun 29, 2026
92f769b
test: update specs for disabled memory-book feature and safeguard prompt
rondlite Jun 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
16 changes: 16 additions & 0 deletions .codegraph/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# CodeGraph data files
# These are local to each machine and should not be committed

# Database
*.db
*.db-wal
*.db-shm

# Cache
cache/

# Logs
*.log

# Hook markers
.dirty
6 changes: 6 additions & 0 deletions .codegraph/daemon.pid
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"pid": 2074,
"version": "0.9.9",
"socketPath": "/home/dlite/charluv/.codegraph/daemon.sock",
"startedAt": 1782692670234
}
40 changes: 40 additions & 0 deletions .github/workflows/dev-container.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Build DEV container

# Manually build a dev container image from the current branch (e.g.
# feat/charluv-rewrite) and push it to Docker Hub tagged `:dev`. Run it on
# dev.charluv.com by pulling demandcluster/charluv:dev.
on:
push:
branches:
- feat/charluv-rewrite
workflow_dispatch:

permissions:
packages: write
contents: read

jobs:
push_dev_image:
name: Build and push DEV Docker image
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v5

- name: Log in to Docker Hub
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_TOKEN }}

- name: Build and push DEV image
uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671
with:
context: .
file: ./Dockerfile
push: true
build-args: |
SHA=${{ github.sha }}
tags: |
demandcluster/charluv:dev
demandcluster/charluv:dev-${{ github.sha }}
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/.codegraph
/.parcel-cache
/dist
/node_modules
Expand All @@ -24,3 +25,6 @@ settings.json
!web/pkg/*.js
!llama3.js


# Playwright MCP scratch artifacts
.playwright-mcp/
60 changes: 60 additions & 0 deletions .superpowers/sdd/presence-A-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Presence Registry — Task A Report

## Status: GREEN

## TDD Evidence

### RED step (before presence.ts existed)
Could not run: the file did not exist. The test import `/srv/queue/presence` would have failed with a module-not-found error. The RED phase is implicit: tests were written against the not-yet-created module.

### GREEN step (after presence.ts created and compiled)

```
presence key derivation
✔ derives user and guest keys

isPresentLocal
✔ user present iff they have a live socket
✔ guest present iff their socket is in allSockets
✔ no identity → present (fail open)

presence redis seam (requires local redis)
✔ mark → present, clear → absent
✔ expired members are reaped → absent
```

All 5 presence tests PASSED. Redis seam tests RAN (not skipped) against local Redis at 127.0.0.1:6379.

## Test Summary

- Total passing: 106
- Total failing: 18 (all pre-existing, unrelated to presence)
- New failures introduced: 0

## Typecheck

`NODE_OPTIONS=--max-old-space-size=6144 pnpm exec tsc -p srv.tsconfig.json --noEmit` → clean (no output, exit 0)

No import cycle issues detected. `presence.ts` imports `bus.ts`; `bus.ts` does NOT import `presence.ts`.

## Prettier

`pnpm exec prettier --check srv/queue/presence.ts tests/queue-presence.spec.ts` → "All matched files use Prettier code style!"

## Files Created

- `/home/dlite/charluv/srv/queue/presence.ts` — registry module (source)
- `/home/dlite/charluv/srv/queue/presence.js` — compiled output (force-added)
- `/home/dlite/charluv/srv/queue/presence.js.map` — source map (force-added)
- `/home/dlite/charluv/tests/queue-presence.spec.ts` — test suite (source)
- `/home/dlite/charluv/tests/queue-presence.spec.js` — compiled output (force-added)
- `/home/dlite/charluv/tests/queue-presence.spec.js.map` — source map (force-added)

## Self-Review Checklist

- [x] All exports present with exact signatures (userKey, guestKey, keyFor, PresenceId, PresenceCmd, markRedis, clearRedis, isPresentRedis, isPresentLocal, markPresent, clearPresent, isPresent, startPresenceRefresh)
- [x] Redis seam tests actually RAN against real redis (not skipped)
- [x] isPresent fails open (returns true) on errors and when no identity given
- [x] redisActive() gates redis vs local path
- [x] Typecheck clean — no import cycle
- [x] Prettier clean
33 changes: 33 additions & 0 deletions .superpowers/sdd/presence-C-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Presence Registry Wiring — C Report

## Edits

### `srv/queue/index.ts`
- Added `import { isPresent, startPresenceRefresh } from './presence'`
- Changed `inferenceGate` construction to pass `isPresent` as 3rd arg
- Updated `startQueue()` to call `startPresenceRefresh()`, fold stop-fn into cleanup

### `srv/api/ws/handlers.ts`
- Added `import { markPresent, clearPresent } from '../../queue/presence'`
- `login`: added `markPresent({ userId: client.userId }, client.uid)` after `userSockets.set`
- `logout`: added `clearPresent({ userId }, client.uid)` after `const userId = client.userId`

### `srv/api/ws/handle.ts`
- Added `import { markPresent, clearPresent } from '../../queue/presence'`
- End of `handleMessage`: added `markPresent({ socketId: client.uid }, client.uid)` after `allSockets.set`
- `client.on('close')`: added `clearPresent({ socketId: client.uid }, client.uid)` before `handlers.logout`
- `client.on('error')`: added `clearPresent({ socketId: client.uid }, client.uid)` before `handlers.logout`

## Typecheck
`NODE_OPTIONS=--max-old-space-size=6144 pnpm exec tsc -p srv.tsconfig.json --noEmit` — **clean (no output)**

## Prettier
`pnpm exec prettier --check srv/queue/index.ts srv/api/ws/handlers.ts srv/api/ws/handle.ts` — **All matched files use Prettier code style!**

## Tests
`pnpm test`: **109 passing, 18 failing** — matches pre-existing baseline; 0 new failures.

## Files Changed
- `/home/dlite/charluv/srv/queue/index.ts`
- `/home/dlite/charluv/srv/api/ws/handlers.ts`
- `/home/dlite/charluv/srv/api/ws/handle.ts`
80 changes: 80 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# CLAUDE.md

Guidance for working in this repo. These instructions override default behaviour.

## What this is

Charluv is an AI-companion / dating app — a fork of Agnaistic (Agnai). It carries a lot of
unused Agnai power-user machinery that is being stripped. Architecture:

- **`web/`** — SolidJS + Tailwind frontend, bundled with **Vite**.
- **`srv/`** — Express + MongoDB + `ws` backend, compiled with `tsc`.
- **`common/`** — isomorphic code shared by both (types, prompt building, progression, tokenize).
- **Inference** — self-hosted **OpenAI-compatible** streaming endpoint for text and a separate
self-hosted **SD-compatible** image endpoint. The text model is a custom 24B trained on
relationship **stage tokens** `LEVEL("NOVICE" | "BEGINNER" | "LOVER" | "GIRLFRIEND" | "SEX" |
"HARDCORE" | "MARRIED" | "DIVORCED" | "BDSM/SLAVE" | "BDSM/DOMINATRIX")`.

## Use codegraph first

A **codegraph** MCP server indexes every symbol/edge/file in the workspace (SQLite knowledge
graph, sub-ms reads). **Consult it before writing or editing code — do not hand-grep or spawn a
file-reading sub-agent to explore.**

- Almost any "how does X work / where is X / trace the flow" question → **`codegraph_explore`**
(one call returns the verbatim source of the relevant symbols, grouped by file). Usually the
only call you need.
- "What is symbol X" (location only) → `codegraph_search`.
- "What calls this / what does it call / what breaks if I change it" →
`codegraph_callers` / `codegraph_callees` / `codegraph_impact`.

Fall back to Read/Grep only to confirm a specific detail codegraph didn't cover.

## Hard constraints

- **NO data migrations.** `dev.charluv.com` runs on **live production data**. Schema changes must
be additive (nullable fields, backward-compatible). Never rename/repurpose a stored id or write
a migration job. (E.g. archetype display labels were changed while keeping the stored ids.)
- **Use `pnpm`, never `npm`** (repo is a pnpm workspace; `npm install` fails).
- **Moderation runs on the local vision LLM**, not external OpenAI.
- **Never sexualize minors** — the 18+ safeguard in the levels preamble (`CHARLUV_LEVELS_PROMPT`)
applies to all characters.
- Fix bugs on sight, even pre-existing or unrelated ones — don't just flag them.

## Commands

```bash
pnpm checks # format + typecheck + test (run before declaring done)
pnpm typecheck # tsc -p tsconfig.json --noEmit (web)
pnpm test # mocha tests/**.spec.js
pnpm start # concurrently: vite web + watched server + tsc
```

- **srv typecheck** (heavier, needs more memory):
`NODE_OPTIONS=--max-old-space-size=6144 npx tsc -p srv.tsconfig.json --noEmit`
- Web files can be quickly syntax-checked with `ts.transpileModule` (the bundler is Vite, not tsc).
- Vite resolves `.ts` before `.js`. Do not check in compiled web `.js`/`.js.map`.

## Domain notes

- **Progression archetypes** (`common/progression.ts`): `ARCHETYPES` (ids `romantic`,
`girlfriend`, `casual`, `submissive`, `dominant`) map XP level → stage token. `getArchetype`,
`getProgressionSteps`, `resolveStage`. No archetype on a character = **fixed** (no fallback to
romantic). Display the `.label`, not the raw id. XP lives on `char.xp`, granted per message.
- **Credits**: message 10, image gen/regen 25, **create 100** (charged when the AI is finalized
on the final wizard step — the hidden draft made on entering that step is free), **edit 30**,
LoRA train 300, publish **+500** reward. Free tier refills +5/2min up to 500;
premium +20/2min up to 5,000.
- **Publishing**: any user can make a character public (`published`/`moderation`/`reportCount`
fields); auto-publish on the local vision-LLM moderation pass, also surfaced in admin for a
second stage; auto-hide at 3 reports.

## Git

- Default branch for PRs is **`trunk`**. Active work is on `feat/charluv-rewrite`.
- **Commit/push only when explicitly asked.** End commit messages with:

```
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
```
13 changes: 10 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:22.14.0-bullseye-slim
FROM node:22-bookworm-slim

WORKDIR /app
VOLUME [ "/app/db" ]
Expand All @@ -12,8 +12,15 @@ ARG SHA=unknown

ADD package.json pnpm-lock.yaml ./
RUN pnpm i --frozen-lockfile

ADD tailwind.config.js tsconfig.json .babelrc .postcssrc .parcelrc .prettierrc srv.tsconfig.json ./
# pnpm v10 does not run dependency install scripts (even via `pnpm rebuild`), so
# sharp's native binary — pulled in by @xenova/transformers for long-term memory
# embeddings — is never fetched. Run sharp's own install script directly to
# download the prebuilt binary for this platform.
RUN for d in /app/node_modules/.pnpm/sharp@*/node_modules/sharp; do \
if [ -d "$d" ]; then echo "Installing sharp binary in $d"; (cd "$d" && npm run install); fi; \
done

ADD tailwind.config.js tsconfig.json .babelrc .postcssrc .prettierrc srv.tsconfig.json vite.config.ts ./
ADD common/ ./common/
ADD srv/ ./srv/
ADD web/ ./web
Expand Down
21 changes: 21 additions & 0 deletions SECURITY-ACCEPTED.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Accepted security advisories

These advisories are listed in `package.json` → `pnpm.auditConfig.ignoreGhsas`
so `pnpm audit` stays clean. Each is genuinely unfixable without breaking the
build or removing a kept feature, or is non-exploitable in this app. Revisit
when upstream patches land.

| GHSA | Pkg | Sev | Why accepted |
|------|-----|-----|--------------|
| GHSA-848j-6mx2-7j84 | elliptic | low | No patch published (`patched: <0.0.0`). Transitive browser polyfill (`crypto-browserify`). |
| GHSA-rmmh-p597-ppvv | showdown | mod | No patch published. ReDoS in markdown rendering; swapping to another lib is a core-renderer refactor with regression risk. |
| GHSA-w5hq-g745-h8pq | uuid | mod | Non-exploitable: advisory affects `v3/v5/v6` with a `buf` arg; this app uses only `v4`. The fix (uuid v11) is ESM-only and breaks the CommonJS server build. |
| GHSA-4w7w-66w2-5vf9 | vite | mod | Dev-server only (optimized-deps `.map` path traversal). Patched in Vite 6, which breaks `vite-plugin-node-polyfills` (build verified to fail). Dev server is not exposed in prod. |
| GHSA-v6wh-96g9-6wx3 | vite | mod | Windows-only (launch-editor UNC NTLM). Prod runs Linux. Fix requires Vite 6 (breaks build). |
| GHSA-fx2h-pf6j-xcff | vite | high | `server.fs.deny` bypass on Windows alternate paths — dev-server + Windows only; prod is Linux and the dev server isn't exposed. Fix requires Vite 6 (breaks build). |
| GHSA-wgrm-67xf-hhpq | pdfjs-dist | high | Malicious-PDF JS execution. Fix is pdfjs v4 (ESM/worker API break, entangled with `pdfdataextract`). Exploit is self-inflicted — a user importing their **own** PDF for text extraction into their **own** library; no cross-user vector. |
| GHSA-8fgc-7cc6-rx7x | webpack | low | Transitive **peer** dep of `worker-loader` under `pdfjs-dist`; `pnpm.overrides` don't apply to peer resolution. Tied to the pdfjs-dist item above. |
| GHSA-38r7-794h-5758 | webpack | low | Same as above. |

All 6 criticals and 59 of 61 highs were eliminated by removals + overrides + bumps
(audit went 154 → 0 active). See `pnpm.overrides` in `package.json`.
16 changes: 16 additions & 0 deletions common/abuse.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Decide how to treat a new registration given whether its device fingerprint
* and/or IP already belong to an existing account.
*
* - both match -> block (very high confidence: same person)
* - one matches -> restrict (likely duplicate: create, but withhold farmable credits)
* - neither -> allow (normal signup)
*/
export function classifyRegistration(opts: {
fpMatch: boolean
ipMatch: boolean
}): 'block' | 'restrict' | 'allow' {
if (opts.fpMatch && opts.ipMatch) return 'block'
if (opts.fpMatch || opts.ipMatch) return 'restrict'
return 'allow'
}
Loading
Loading