Implement progression archetype system and enhance Discover gallery#22
Merged
Conversation
…n speed Edit page (ourdream-style): - remove tab system -> single scrolling page - fixed W++ trait set (no format picker / dynamic attributes); reuse appearance/gender/age, add species/personality/mind/likes/dislikes/background - read-only warning for non-standard W++ attributes (dropped on save) - remove Voice + Advanced sections - creator auto-set to user handle; characterVersion auto-increments Image: - gallery max 10, pick up to 4 for the LoRA; "Build LoRA" encodes via Z-Image and stores loraName on the character - server-side asset->base64 resolution (SSRF-guarded, avoids CDN CORS) - native generate_image tool: model can request an image mid-chat -> Z-Image with the character's LoRA, appended to the reply - resolve the chat character for every image so the LoRA always applies - size by source: chat 512, character/avatar 768 Progression: - per-character speed (slow/normal/fast) -> XP per message 1/3/7 Other: - summaries no longer charge credits or advance XP - plain/inference prompts sent as user role (endpoint requires a user message) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
Character traits: - align fixed persona traits to the canonical Charluv W++ keys (species, mind, personality, age, job, description, sexuality, likes, loves, zodiac, hates, country, body, appearance) so real characters round-trip instead of being dropped as "extras" - gender moves out of the persona into charluv metadata (char.gender); art style is derived from tags (anime/realistic) - inject charluv metadata (gender, age range) into the prompt at chat time so the model still sees facets that aren't in the W++ persona Gallery: - re-seed the gallery from the saved character once it loads (was captured empty at mount, so images vanished on reopen / only appeared after adding one) - readAssetBase64 reads directly from S3/R2 when storage is enabled, so the Build-LoRA encode can resolve gallery URLs regardless of ASSET_URL/CDN config Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
- Discover card click now opens a character profile/preview page (/discover/:id) with the gallery, age, description, facets and an editable name field instead of instantly matching - "Match & Start Chat" clones the template with the chosen name, creates a chat directly and jumps to /chat/:id — the create-chat form step is bypassed - server: POST /match/:id honors an optional name; GET /match/:id returns the template character for the profile page (registered after /discover) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
- Create wizard rebuilt as an ourdream-style image-card flow: gender/style/age, ethnicity + skin tone, hair + hair/eye color, body/breast/butt, personality, finish. Step-icon progress, two-tone hero, color swatches for skin/hair/eyes. - Option previews load from /assets/wizard/...; appearance groups are gender + style aware: /assets/wizard/<style>/<gender>/<group>/<slug>.png. Missing assets degrade to labelled placeholders. - Profile page: large two-tone "Name, Age" title (name live-editable). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
- srv/memory: server-side embeddings via @xenova/transformers (all-MiniLM-L6-v2,
lazy in-process) and a Mongo-backed vector store with brute-force cosine
retrieval, scoped per user+character (persists across chats)
- native `remember` tool: the model durably saves facts about the user/relationship;
stored on the assistant turn via the existing tool-call meta channel
- RAG: recall top-k relevant memories from the recent turns and inject them into
the {{memory}} prompt slot at generation time
- replaces memory books; backend swappable for pgvector/endpoint later
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
Root cause: image persistence was gated on the SAVE_IMAGES flag (unset), so chat images were uploaded to R2 but never attached to a chat message — they showed live over WS then vanished from history on refresh (orphaning the R2 object). - persist whenever the image belongs to a chat (has a chatId), regardless of SAVE_IMAGES; the flag still forces saving for non-chat images - the native image tool now creates its OWN image message (parented to the reply) instead of appending onto the text turn — appending set adapter:'image' on the text message, which the renderer treats as an image filename and would blank the reply text Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
…tics - memory logs now carry only metadata (length/count/topScore); the actual text is gated behind MEMORY_DEBUG_LOG_TEXT=1 (dev only) - openai adapter: debug-log when tools are attached and whether the model returned any tool_calls (finish_reason + names) to diagnose tool-calling Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
…he field) The single-page editor dropped the culture input, but the form guard still required culture:'string', so getStrictForm rejected save/create with "Request body is invalid: .culture is undefined". - newCharGuard: culture -> optional - getPayload: default culture from state / defaultCulture - create wizard: set culture 'en-us' on the payload Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
Native function-calling doesn't fire on the charluv finetune: the prompt prefills the assistant turn (the "<name>:" steering), so the model generates text mid-turn and can't emit tool_calls (logs showed finish_reason:stop, toolCallCount:0 with tools correctly attached + qwen3_xml parser). Switch to in-band markers the model writes inside its reply: <image>visual description</image> -> fires Z-Image (char LoRA) <remember>fact</remember> -> stores long-term memory Server parses + strips them from the reply and feeds the existing downstream (meta.imageTool / meta.rememberFacts). Markers are also stripped from streaming partials so raw tags never show. Instructions are prepended to the system message when the features are available. Native OpenAI tools/tool_calls remain behind CHARLUV_NATIVE_TOOLS=1. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
The dream-date wizard had no way to set an image. Added a "Portrait (optional)" section on the Finish step: - Generate from choices: text-to-image via Z-Image using a prompt built from the selected style/ethnicity/gender/appearance (no LoRA yet) - Upload: pick an image file The chosen image is set as the new character's avatar in the create payload. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
Reword the marker instructions from a meta "you have special abilities / the
system processes tags" into an in-world platform capability ("on Charluv, every
companion can share photos and remember things"). Characters are far more likely
to use the tags in-character; the previous framing was easy for a restrictive
persona to refuse.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
- Dockerfile: pnpm rebuild sharp — pnpm v10 skips dependency build scripts, so
@xenova/transformers' sharp native binary was missing, breaking long-term
memory ("Cannot find module sharp-linux-x64.node"). Image gen was fine (remote
Z-Image); only the embedder failed.
- Marker instructions made more selective: at most one <image> per reply and only
when fitting; <remember> only for genuinely important facts — the model was
emitting both on nearly every turn. Also told not to narrate doing it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
Both are superseded:
- memory books -> long-term memory (RAG) now fills {{memory}} server-side; the
keyword-book injection is removed (it was leaking the old character_book, e.g.
"Julia's cat...", into unrelated chats)
- attached scenario books -> relationship progression (the LEVEL token). Only the
character's initial scenario (or the chat's overridden scenario) remains, plus
the progression token + charluv meta.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
The native image tool created a detached image message that popped in with no feedback. Now it behaves like normal in-chat image gen: - server attaches the image to the reply (messageId + append) and no longer sets adapter:'image' on append (preserved adapter, so the reply text still renders) - image-generation-started is broadcast to chat members with the messageId - client shows a Spinner on the reply while generating (imagesGenerating state), renders the attached image from extras under the text, clears on message-retry Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
`pnpm rebuild sharp` silently no-ops under pnpm v10's build-script gate, so the native binary was still missing. Run sharp's own install script directly in the pnpm store path to fetch the prebuilt binary, unblocking the memory embedder. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
- One "Images" card: cover (avatar) + gallery in a single grid; every tile (incl. the avatar) is selectable for the LoRA; Generate / Upload / Build LoRA / Make-cover / Remove. Server: encode-lora allows the avatar ref; new POST /character/:id/cover sets the avatar from an existing image. - Gallery "Generate" now composes a real prompt from the persona (appearance/body/species/age + name) instead of the empty appearance field. - Sprite feature removed from the editor UI (schema field kept, no migration; original sprite data forwarded on save). - LoRA re-encode reuses the character's existing loraName so it REPLACES the old LoRA instead of minting a new uuid each time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
…22 bookworm - remove dead deps: all @parcel/* + parcel (Vite is the bundler), localtunnel + @types/localtunnel (dev tunnel); drop their npm scripts and the localtunnel usage in srv/start.ts, srv/bin.ts, srv/config.ts; drop .parcelrc from Docker - add pnpm.overrides (33) pinning patched transitive deps (protobufjs, pbkdf2, sha.js, fast-xml-parser, shell-quote, undici, esbuild, rollup, qs, ws, ...) — clears all 6 criticals + most highs - bump dompurify, exifreader, js-cookie, svgo, vite to patched - Dockerfile: node:22.14.0-bullseye-slim -> node:22-bookworm-slim Remaining 9 are justified (no patch / major-break / non-exploitable): pdfjs-dist+webpack (v4 layout break), vite (dev-server only, v6 major), uuid (v4-only usage, advisory is v3/5/6), showdown/elliptic (no patch). discord.js kept — still imported (Discord rip is a later phase). srv build:server exit 0; web tsc unchanged (790); frozen-lockfile OK. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
…build) The `>=7.29.6` override resolved to Babel 8.0.1, which is incompatible with babel-preset-solid and crashed `vite build` in CI. Pin to `^7.29.6` (resolves 7.29.7) — still patched, builds clean (verified locally, 1m42s). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
The remaining 9 advisories cannot be fixed without breaking the build or removing a kept feature, or are non-exploitable here. Recorded them in pnpm.auditConfig.ignoreGhsas with per-advisory justification in SECURITY-ACCEPTED.md, so `pnpm audit` now exits 0 and each is tracked. - no upstream patch: elliptic, showdown - non-exploitable + fix breaks CJS build: uuid (v4-only usage) - dev-server/Windows-only, fix needs Vite 6 (breaks node-polyfills build): vite x3 - pdfjs-dist high + its webpack peer x2: v4 ESM/worker break entangled with pdfdataextract; exploit is self-inflicted (user's own PDF, text extraction) Net: audit 154 -> 0 active (6 crit + 59/61 high removed via deps/overrides/bumps). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
…rrides
The `path-to-regexp: >=0.1.13` override forced express@4.21.2 onto
path-to-regexp@8.x (incompatible API) -> server crashed at boot
("pathRegexp is not a function" registering routes). Also `lodash: >=4.18.0`
was a bogus range (latest is 4.17.21).
- replace blanket override with scoped `express>path-to-regexp: 0.1.13` (patched
AND keeps the 0.1.x function API express needs; other consumers keep 8.x)
- remove the invalid lodash override
Verified: `pnpm audit` exit 0, build:server ok, and the server BOOTS
(MongoDB + "Server started") — the Docker build alone didn't catch the runtime
crash, so this was boot-tested.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
Broaden the remember instruction (marker + native tool) so the model also saves self-facts it states or invents in the moment — its pet's name, family, where it lives, backstory — not just facts about the user. Keeps the character consistent across chats (these get stored per user+character and recalled via RAG). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
… screen)
- My AI card now opens an owned-character profile (/mine/:id): big cover +
gallery, click any image for fullscreen (settingStore.showImage), Chat + Edit +
Back buttons. Reuses the Discover profile styling.
- startChat(char, navigate, {forceNew?}) helper: resume the most-recent chat or
create one, then go straight to /chat/:id. Replaces per-character
/chats/create/:id navigations across the character views (no more confusing
create-chat screen).
- ChatList: per-chat inline rename (pencil -> editChat) — replaces the only
feature the create-chat screen offered (naming the chat).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
…My AI) Wrap the Discover profile cover + gallery images in buttons that open the fullscreen viewer (settingStore.showImage), matching the My AI profile. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
- Every character carries an imageSeed (loaded or freshly generated). The editor's image generation (cover + gallery) sends this seed so all generated images keep a consistent base look — good for building a coherent LoRA reference set. - Chat image generation omits the seed entirely, so chat images vary (the LoRA provides identity). generateImage now uses opts.seed (editor-supplied) instead of reading character.imageSeed, so chat never locks it. - Editor UI: a "Base look seed" row with the value + a Dice "Reroll" button and a warning that rerolling changes the base appearance (saved images unaffected). - Threaded seed: editor -> generateAvatar -> characterStore.generateAvatar -> generateImageWithPrompt -> POST /character/image -> generateImage. Persisted via the character create/update mapping + FormData. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
… seed) With the locked base-look seed, generating again with the same Appearance prompt yields the same image. Added a note under the gallery actions telling users to vary the Appearance prompt (pose/outfit/setting) between generations for varied reference shots while keeping the core looks consistent. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
Wire isPresent into the PriorityGate singleton so the gate can drop requests from disconnected clients. Start/stop the presence-refresh interval in startQueue(). Mark presence on socket open and user login; clear on socket close, error, and user logout. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
…nect Two paths charged credits before entering the gate. When ClientGoneError is thrown (client gone before generation), image generation now refunds IMAGE_COST (logged-in users only) and guidance refunds 10 credits. IMAGE_COST is now exported from srv/image/index.ts and imported by the image route instead of being redefined locally. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XXNb2Kcdayr1ZvUpy5a3nA
Fixes the PR format gate (prettier --check) that blocked merge to trunk. Formatting only — no behaviour changes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 232378dddf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- match clone: reset published/publishedAt/publishRewarded/moderation/reportCount so a cloned copy of another user's published template isn't silently relisted as a public, unmoderated template. - gallery add: take a published character private for review when a new image is added (mirrors the edit path) so unmoderated gallery content can't go live. - getMatch: apply the premium gate (like discover/getMatches) so guests and free users can't pull a premium template by direct ID. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
setAvatarFile(file) tripped the Setter's `U extends Function` overload branch under CI's TypeScript. Use the functional-updater form. Surfaced now that the prettier gate passes and CI reaches the typecheck step. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Align the test suite with the rewrite: - resolve-scenario: scenario books are ignored (feature disabled) - progression: unknown archetype id returns no steps (no fallback) - prompt: remove keyword memory-book tests (feature removed) - refresh snapshots for CHARLUV_SAFEGUARD_PROMPT and dropped memory entries Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.