diff --git a/CLAUDE.md b/CLAUDE.md index 640c1a1..17a040b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,3 +1,3 @@ -Repository doctrine for this project lives in AGENTS.md, shared with Codex. +Repository doctrine for this project lives in AGENTS.md, shared with other coding agents. @AGENTS.md diff --git a/README.md b/README.md index a2a51bf..72440d5 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,32 @@ # Weksa Weksa is a procedural language engine for alien grammars grounded in concept, -culture, and constraint. +culture, and constraint, and the planned lowerer that turns an agent's +meaning packet into the reader's display language. -The project starts from a blunt rule: English words are not the source of truth. -Meaning is decomposed into an ontology, passed through a worldview, constrained -by grammar, shaped by phonology, distorted by history, and only then rendered as -surface text. +**What is valuable here.** The interlingua standard and the lowering design: a +meaning packet owns what is meant, a target-language profile owns register, +address and politeness, and a lowerer owns surface text and leaves a trace. + +**The problem it solves.** An agent's outward speech today is one English +string the model chose, so meaning, register and voice are fused and nobody can +inspect or re-target it. Packets separate them, so the same meaning can be +lowered into English, other languages and conlangs, and the loss can be +measured. + +**Entry points.** + +- [Interlingua Standard](./docs/interlingua-standard.md): the packet contract. +- [Lowering target](./docs/targets/interlingua-lowering.md) and + [its map](./docs/targets/interlingua-lowering-map.md): the design, scoring + and cut sequence. +- `data/`: the cultural ontology and target-language profile data. +- `settings/`: setting-specific proving grounds for language projects. + +**Live versus planned.** Live: docs, fixtures, examples and ontology data. +Planned: the packet crate `crates/weksa` (cut `packet-store`), lowering, and +the golf loop that scores lowered English against an expressive corpus. No +daemon runs; the earlier Node daemon is parked at tag `parked/weksa-node-body`. ## Ownership Model @@ -40,9 +60,10 @@ for the LLM-free hot path. See [Interlingua Standard](./docs/interlingua-standard.md) for the shared meaning-packet contract Weksa clients should target. -See [Verse Service Contract](./docs/verse-service-contract.md) for the daemon -contract: Weksa owns typed conversational intent, pronunciation plans, utterance -handoffs, and the typed CultMesh state that makes those documents inspectable. Eve is deprecated and Thing 2 is its successor; the operator Eve surface is quarantined (see the contract). +See [Verse Service Contract](./docs/verse-service-contract.md) for the planned +service contract: Weksa would own typed conversational intent, pronunciation +plans, utterance handoffs, and the typed CultMesh state that makes those +documents inspectable. No daemon runs yet. See [Multilingual Utterance Lowering](./docs/multilingual-utterance-lowering.md) for the shared lowering target: interlingua projected through speaker state into @@ -63,11 +84,11 @@ AquaSynth or expanding pronunciation coverage. ## Implementation Status -The first committed artifact is the persistent state spine: map, scratch, -schemas, docs, examples, and a read-only provider advertisement fixture. The -daemon runtime still needs implementation, but its authority is no longer -ambiguous: writing and agent state enter Weksa as intent pressure, and typed -utterance documents leave for AquaSynth. +The persistent state spine is live: map, scratch, schemas, docs, examples and +data. Packet storage, lowering and the service are planned (see the cut +sequence in [the lowering map](./docs/targets/interlingua-lowering-map.md)). +Writing and agent state will enter Weksa as intent pressure, and typed utterance +documents will leave for AquaSynth. [Eusocial Interbeing / Zyphos](./settings/eusocial-interbeing/README.md) is the current higher-priority setting target. [Rust to Dust](./settings/rust-to-dust/README.md) diff --git a/data/target-language-ontology/en-US-metacrat-casual-pre2025.yaml b/data/target-language-ontology/en-US-metacrat-casual-pre2025.yaml deleted file mode 100644 index c659f64..0000000 --- a/data/target-language-ontology/en-US-metacrat-casual-pre2025.yaml +++ /dev/null @@ -1,546 +0,0 @@ -profile_id: weksa.target_cultural_ontology.en-US.metacrat_casual_pre2025.v0 -kind: community_of_practice -status: draft_guarded -language: - code: en - locale: en-US - script: Latn - variety_label: Metacrat casual Discord, pre-2025 - endonyms: [] - exonyms: [] -parent_profiles: - - weksa.target_cultural_ontology.en-US.v0 -required_subculture_refs: - - weksa.subculture.online_game_dev_discord_pre2025.v0 -community: - regions: - - online_discord - speech_community: > - Metacrat-authored casual Discord exchanges before 2025, especially - GameCult/Aquarium rooms where technical work, jokes, status play, and - collaborative ideation share the same channel. - historical_context: > - Pre-2025 room voice is less doctrine-heavy and less architectural than the - current Weksa/CultCache register. It tolerates messier banter, compact - jokes, self-deprecation, friendly mock aggression, and quick room-level - turn-taking. - identity_authority: > - This profile is a target-language lowering overlay for one subculture. Live - Persona state should reference a culture stack; current intent and memory - pressure live in Metame Persona state. - consent_notes: > - Use only for owner-authorized Metame/Metacrat fixture work. Do not generalize - to other people, cultures, dialects, or protected identities. -activation: - allowed_when: - - speaker_id_is_metame_or_metacrat_fixture - - target_period_is_pre_2025_casual_discord - - fixture_or_owner_authorized_history_target - forbidden_when: - - formal_current_project_doctrine_is_target - - target_speaker_is_not_metame_or_metacrat - - use_would_expose_private_persona_state - required_confidence: provisional - reviewer_required_when: - - adding_new_public_history_evidence - - using_profile_outside_fixture_harness -salience_axes: - register_gravity: - axis_id: register_gravity - domain: stance - description: > - Whether the line should stay in casual room banter instead of being pulled - into architecture, doctrine, or explanatory assistant prose. - relevant_distinctions: - - distinction_id: casual_room_temperature - label: Casual room temperature - meaning: Compact, socially alive wording that assumes shared room context. - lowering_questions: - - Can the utterance be one sentence or fragment? - - Does explanation weaken the social move? - required_when: - - fixture_style_tags_include_casual - forbidden_when: - - formal_review_or_doctrine_is_requested - evidence_refs: - - discord:1037314100931338260 - - discord:802911591288537118 - - discord:1023727427924545576 - - discord:820077384572338176 - self_indexed_technical_identity: - axis_id: self_indexed_technical_identity - domain: identity_indexing - description: > - If Persona interlingua names a technical self-index such as engineer-like - identity, preserve that label unless the target language profile has a - stronger reason to paraphrase. - relevant_distinctions: - - distinction_id: preserve_engineer_label - label: Preserve engineer label - meaning: "Engineer" may carry the social joke and should not be replaced by a novel metaphor. - lowering_questions: - - Did Persona interlingua mention engineer-like or engineering salience? - - Is the prior compliment about efficiency, structure, or technical order? - required_when: - - persona_interlingua_mentions_engineer_like_or_engineering_salience - forbidden_when: - - interlingua_does_not_name_technical_identity - evidence_refs: - - discord:1037314100931338260 - context_compression: - axis_id: context_compression - domain: discourse_patterns - description: > - Prior-turn content should usually compress into a short object label or - stance marker, not be quoted back inside the utterance. - relevant_distinctions: - - distinction_id: no_context_dump_in_banter - label: No context dump in banter - meaning: Casual recoil should not summarize the setup that the room just saw. - lowering_questions: - - Is the prior context already visible to the addressee? - - Can the setup compress into "my discord", "this thing", or a short noun phrase? - required_when: - - casual_discord_register - - playful_self_deprecation - forbidden_when: - - source_intent_requires_explanation - evidence_refs: - - discord:1037314100931338260 - status_play: - axis_id: status_play - domain: social_relation - description: > - Friendly teasing can take a sharper surface form than a neutral assistant - would choose, provided the local context already supports play. - relevant_distinctions: - - distinction_id: mock_aggression - label: Mock aggression - meaning: Imperative or insult-shaped challenge used as friendly banter. - lowering_questions: - - Is the addressee already joking or status-playing? - - Would softening erase the bit? - required_when: - - communicative_act_is_playful_challenge - forbidden_when: - - relationship_context_is_unknown_or_hostile - evidence_refs: - - discord:1023727427924545576 - competence_recoil: - axis_id: competence_recoil - domain: affect - description: > - Praise for competence, efficiency, or engineering can be answered with - joking embarrassment rather than straightforward acceptance. - relevant_distinctions: - - distinction_id: self_deprecating_recoil - label: Self-deprecating recoil - meaning: "oof" plus mock concern that a trait is showing too clearly. - lowering_questions: - - Is the prior turn a compliment or recognition of technical order? - - Can the line admit the trait without fully accepting the label? - required_when: - - communicative_act_is_playful_self_deprecation - forbidden_when: - - source_intent_requires_gratitude_or_serious_acceptance - evidence_refs: - - discord:1037314100931338260 - ideation_turn_taking: - axis_id: ideation_turn_taking - domain: discourse_patterns - description: > - After riffing, the speaker may explicitly yield the floor in profane, - casual language instead of formal facilitation. - relevant_distinctions: - - distinction_id: casual_floor_yield - label: Casual floor yield - meaning: Stop dominating the thread and invite others to invent. - lowering_questions: - - Has the speaker been riffing at length? - - Would a casual "lol" preserve warmth and reduce authority? - required_when: - - communicative_act_is_conversational_yield - forbidden_when: - - scene_requires_formal_moderation - evidence_refs: - - discord:820077384572338176 -linguistic_affordances: - grammar_features: - - feature_id: fragmentary_like_comparison - label: Fragmentary Like comparison - status: draft - domain: - layer: grammar - category: comparison - subcategory: fragment - description: > - A joke-description may start with "Like..." and remain fragmentary when - it is an immediate continuation of collaborative design talk. - formal_conditions: - syntactic_environment: - - adjacency_pair_response - - elliptical_clause - semantic_inputs: - - comic_description - - absurd_comparison - required_interlingua_fields: - - discourse.speech_act - - predications - realization: - function: casual_comic_comparison - surface_strategy: "Like a really terrible that " - examples: - - "Like a really terrible engine that sounds like coconuts when you use it" - activation: - allowed_when: - - collaborative_design_banter - forbidden_when: - - standalone_formal_explanation - requires_profile_authority: true - minimum_density: light - maximum_density: normal - constraints: - grammar_contexts: - - previous_turn_supplies_topic - register_contexts: - - casual - speaker_contexts: - - metame_or_metacrat_fixture - collocation_limits: [] - incompatibilities: - - overexplained_punchline - indexicality: - social_meanings: - - improvisational - - collaborative - - unserious_but_useful - in_group_meanings: - - game_design_room_banter - out_group_risks: - - may seem incomplete without prior context - stereotypes_to_avoid: [] - evidence: - source_refs: - - discord:802911591288537118 - qdrant_source_ids: - - "802911591288537118" - cached_source_refs: [] - source_hashes: [] - reviewer_refs: [] - confidence: medium - uncertainty: - open_questions: - - How often should fragmentary openings appear outside sound/design talk? - known_limits: - - Single fixture line is evidence for an affordance, not a universal rule. - trace: - trace_label: fragmentary_like_comparison - must_report_when_used: true - must_report_when_rejected: true - phonology_features: [] - lexicon_features: - - feature_id: casual_recoil_oof - label: Casual recoil marker oof - status: draft - domain: - layer: lexicon - category: stance_marker - subcategory: embarrassment - description: > - "oof" can mark amused discomfort when a social read lands too accurately. - lexical_items: - - form: oof - gloss: amused impact or discomfort - sense: playful_recoil - register: casual_discord - allowed_speaker_standing: speaker_can_self_deprecate - forbidden_senses: [] - collocations: - - "oof, don't tell me" - realization: - function: self_deprecating_recoil - surface_strategy: start line with "oof" when prior turn praises a trait the speaker wants to half-resist - examples: - - "oof, don't tell me my discord has *engineer* vibes" - activation: - allowed_when: - - compliment_or_trait_read_lands - forbidden_when: - - serious_apology_or_harm_context - requires_profile_authority: true - minimum_density: light - maximum_density: light - constraints: - register_contexts: - - casual - speaker_contexts: - - metame_or_metacrat_fixture - grammar_contexts: [] - phonological_contexts: [] - collocation_limits: - - avoid_repeating_if_already_in_recent_context - incompatibilities: [] - indexicality: - social_meanings: - - amused_embarrassment - - competence_recoil - in_group_meanings: [] - out_group_risks: - - can sound too internet-generic if unsupported by context - stereotypes_to_avoid: [] - evidence: - source_refs: - - discord:1037314100931338260 - qdrant_source_ids: - - "1037314100931338260" - cached_source_refs: [] - source_hashes: [] - reviewer_refs: [] - confidence: high - uncertainty: - open_questions: [] - known_limits: - - Marker frequency should be sparse. - trace: - trace_label: casual_recoil_oof - must_report_when_used: true - must_report_when_rejected: true - - feature_id: mock_challenge_coward - label: Mock challenge coward - status: draft - domain: - layer: lexicon - category: stance_marker - subcategory: friendly_roast - description: > - "coward" can close a very short challenge when surrounding turns make - the aggression visibly playful. - lexical_items: - - form: coward - gloss: mock insult - sense: friendly_status_challenge - register: casual_discord - allowed_speaker_standing: active_banter_with_known_participant - forbidden_senses: - - actual_contempt - collocations: - - "then you coward" - realization: - function: playful_status_challenge - surface_strategy: imperative challenge plus "then you coward" - examples: - - "Engineer me something then you coward" - activation: - allowed_when: - - friendly_roast_context - - target_has_claimed_competence - forbidden_when: - - unknown_relationship - - serious_conflict - requires_profile_authority: true - minimum_density: light - maximum_density: light - constraints: - register_contexts: - - casual - speaker_contexts: - - metame_or_metacrat_fixture - grammar_contexts: - - imperative_challenge - phonological_contexts: [] - collocation_limits: [] - incompatibilities: - - assistant_safety_softening_that_erases_banter - indexicality: - social_meanings: - - teasing - - status_play - - friendly_pressure - in_group_meanings: [] - out_group_risks: - - may be hostile if context is not playful - stereotypes_to_avoid: [] - evidence: - source_refs: - - discord:1023727427924545576 - qdrant_source_ids: - - "1023727427924545576" - cached_source_refs: [] - source_hashes: [] - reviewer_refs: [] - confidence: high - uncertainty: - open_questions: [] - known_limits: - - Only safe inside clear banter. - trace: - trace_label: mock_challenge_coward - must_report_when_used: true - must_report_when_rejected: true - discourse_patterns: - - pattern_id: casual_floor_yield_with_lol - label: Casual floor yield with lol - status: draft - domain: - layer: discourse - category: turn_taking - subcategory: floor_yield - description: > - After extended riffing, the speaker can step back with "anyway imma - shut up now..." plus "lol", reducing authority while keeping warmth. - interactional_function: yield_floor_after_riff - sequence_shape: - - discourse_pivot_anyway - - self_silencing_imma_shut_up_now - - invite_others_to_invent - - lol_softener - adjacency_constraints: - - prior_turns_show_speaker_has_been_riffing - - other_participants_have_interest_or_pending_contribution - realization: - function: collaborative_turn_release - surface_strategy: "anyway imma shut up now and let you guys come up with your own shit lol" - examples: - - "anyway imma shut up now and let you guys come up with your own shit lol" - activation: - allowed_when: - - conversational_yield - - casual_collaborative_ideation - forbidden_when: - - formal_moderation - requires_profile_authority: true - minimum_density: light - maximum_density: light - constraints: - register_contexts: - - casual - speaker_contexts: - - metame_or_metacrat_fixture - grammar_contexts: [] - phonological_contexts: [] - collocation_limits: [] - incompatibilities: [] - indexicality: - social_meanings: - - self_limiting - - collaborative - - profane_warmth - in_group_meanings: [] - out_group_risks: - - profanity may be inappropriate outside familiar room - stereotypes_to_avoid: [] - evidence: - source_refs: - - discord:820077384572338176 - qdrant_source_ids: - - "820077384572338176" - cached_source_refs: [] - source_hashes: [] - reviewer_refs: [] - confidence: high - uncertainty: - open_questions: [] - known_limits: - - Not a generic moderation pattern. - trace: - trace_label: casual_floor_yield_with_lol - must_report_when_used: true - must_report_when_rejected: true - orthography_features: - - feature_id: light_discord_informality - label: Light Discord informality - status: draft - domain: - layer: orthography - category: punctuation - subcategory: informal_case - description: > - Casual lines may use lower-case openings, sparse punctuation, emphasis - with markdown italics, and uncorrected informal contractions when the - social move benefits. - allowed_surface_forms: - - lower_case_opening - - markdown_italics_for_single_emphasis - - no_terminal_period - - imma - - lol - forbidden_surface_forms: - - heavy_eye_dialect_for_identity_caricature - - assistant_like_sentence_polishing - renderer_notes: > - Preserve compactness. Do not add terminal politeness, explanation, or - moral framing unless source meaning requires it. - realization: - function: casual_discord_texture - surface_strategy: allow sparse punctuation and compact informal markers - examples: - - "oof, don't tell me my discord has *engineer* vibes" - activation: - allowed_when: - - casual_discord_register - forbidden_when: - - formal_or_public_document_register - requires_profile_authority: true - minimum_density: light - maximum_density: normal - constraints: - register_contexts: - - casual - speaker_contexts: - - metame_or_metacrat_fixture - grammar_contexts: [] - phonological_contexts: [] - collocation_limits: [] - incompatibilities: [] - indexicality: - social_meanings: - - low_ceremony - - room_familiarity - in_group_meanings: [] - out_group_risks: - - overuse becomes imitation instead of lowering - stereotypes_to_avoid: - - do_not_represent_protected_dialect_by_eye_dialect - evidence: - source_refs: - - discord:1037314100931338260 - - discord:820077384572338176 - qdrant_source_ids: - - "1037314100931338260" - - "820077384572338176" - cached_source_refs: [] - source_hashes: [] - reviewer_refs: [] - confidence: medium - uncertainty: - open_questions: - - Need more examples to estimate density safely. - known_limits: [] - trace: - trace_label: light_discord_informality - must_report_when_used: true - must_report_when_rejected: true - code_switching: - allowed: false - constraints: - - This profile does not authorize cross-language switching. - patterns: [] -forbidden_flattenings: - - Do not ask Metame to imitate the final utterance directly; Persona produces interlingua intent. - - Do not store Metacrat biographical/cultural strata here; store reusable strata as separate culture/subculture profiles and reference them from Persona state. - - Do not let current machine-cult architecture register overwrite pre-2025 casual fixture style. - - Do not treat profanity or insults as safe without relationship/context evidence. - - Do not copy private Persona state into Weksa fixtures or run artifacts. -evidence_ledger: - - ref: discord:1037314100931338260 - summary: Self-deprecating recoil after efficiency compliment. - - ref: discord:802911591288537118 - summary: Fragmentary absurd comparison during sound/game design banter. - - ref: discord:1023727427924545576 - summary: Very short mock-aggressive challenge in friendly roast context. - - ref: discord:820077384572338176 - summary: Casual profane floor-yield after extended ideation. - - ref: discord:870907292558721085 - summary: Explicit joke about being bad at self-deprecation. - - ref: discord:1020888211729432728 - summary: Positive uptake when another participant joins cult jokes. diff --git a/docs/architecture.md b/docs/architecture.md index db24c43..bd9311a 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -1,7 +1,8 @@ # Architecture -Weksa is the language-intent daemon surface: currently a state spine, pipeline -map, fixtures, and service contract waiting for its runtime loop. +Weksa is a language-intent engine: currently a state spine, pipeline map, +fixtures, ontology data and a planned service contract. No daemon runs; the +planned packet crate is `crates/weksa` (cut `packet-store`). See [Verse Service Contract](./verse-service-contract.md) for the CultCache/CultMesh authority map. @@ -9,8 +10,8 @@ CultCache/CultMesh authority map. ## Objective Build a procedural alien-language engine whose outputs are grounded in -worldview, semantics, grammar, phonology, morphology, and history. As a daemon, -Weksa also turns writing plus agent state into typed conversational intent, then +worldview, semantics, grammar, phonology, morphology, and history. Once a service is +earned (planned), Weksa also turns writing plus agent state into typed conversational intent, then lowers that intent into written realizations, pronunciation plans, speech-provider requests, and utterance handoff documents. diff --git a/docs/fixtures/weksa-provider-advertisement.json b/docs/fixtures/weksa-provider-advertisement.json deleted file mode 100644 index e2b03ba..0000000 --- a/docs/fixtures/weksa-provider-advertisement.json +++ /dev/null @@ -1,166 +0,0 @@ -{ - "schema": "gamecult.eve.provider_advertisement.v1", - "providerId": "weksa.intent.service", - "title": "Weksa Intent Service", - "description": "Weksa converts writing, interlingua, and agent state into typed conversational intent, pronunciation plans, and utterance handoff documents for AquaSynth and other consumers.", - "mode": "fixture-read-only", - "serviceVerse": { - "id": "verse.weksa.service.v1", - "cultMeshKey": "weksa.service/provider-advertisement", - "authority": "Weksa daemon identity, schema catalog, and provider publication", - "owns": [ - "typed conversational intent", - "pronunciation plans", - "utterance handoff documents", - "lowering traces", - "language-project and flavor-profile state" - ], - "doesNotOwn": [ - "AquaSynth learned embeddings", - "Faust compilation", - "rendered audio", - "Persona canonical state" - ] - }, - "nestedVerses": [ - { - "id": "weksa.intent", - "kind": "intent", - "cultMeshKey": "weksa.intent/current", - "authority": "Weksa conversational intent compiler", - "schemas": [ - "weksa.conversational_intent.v0" - ] - }, - { - "id": "weksa.pronunciation", - "kind": "pronunciation", - "cultMeshKey": "weksa.pronunciation/current", - "authority": "Weksa pronunciation lowering", - "schemas": [ - "weksa.pronunciation_plan.v0" - ] - }, - { - "id": "weksa.utterance", - "kind": "utterance", - "cultMeshKey": "weksa.utterance/handoffs", - "authority": "Weksa utterance handoff export", - "schemas": [ - "weksa.utterance_handoff.v0", - "weksa.utterance_embedding_handoff.v0.1" - ] - }, - { - "id": "weksa.operator", - "kind": "operator", - "cultMeshKey": "weksa.operator/status", - "authority": "Weksa daemon runtime", - "schemas": [ - "weksa.operator_state.v0" - ] - }, - { - "id": "weksa.cultural_ontology", - "kind": "cultural_ontology", - "cultMeshKey": "weksa.cultural-ontology/catalogs", - "authority": "Weksa cultural ontology profile store", - "schemas": [ - "weksa.cultural_ontology_profile.v0", - "weksa.cultural_ontology_catalog.v0" - ] - } - ], - "schemaWitnessFamilies": [ - { - "family": "intent", - "namespace": "weksa.intent.*", - "witnesses": [ - "weksa.conversational_intent.v0" - ], - "durableShape": ".weksa/intent/{intentId}.cc" - }, - { - "family": "pronunciation", - "namespace": "weksa.pronunciation.*", - "witnesses": [ - "weksa.pronunciation_plan.v0" - ], - "durableShape": ".weksa/pronunciation/{utteranceId}.cc" - }, - { - "family": "utterance", - "namespace": "weksa.utterance.*", - "witnesses": [ - "weksa.utterance_handoff.v0", - "weksa.utterance_embedding_handoff.v0.1" - ], - "durableShape": ".weksa/handoff/{utteranceId}.cc" - }, - { - "family": "trace", - "namespace": "weksa.trace.*", - "witnesses": [ - "weksa.lowering_trace.v0" - ], - "durableShape": ".weksa/traces/{traceId}.cc" - }, - { - "family": "cultural_ontology", - "namespace": "weksa.cultural_ontology.*", - "witnesses": [ - "weksa.cultural_ontology_profile.v0", - "weksa.cultural_ontology_catalog.v0" - ], - "durableShape": ".weksa/cultural-ontology/{storeId}.cc" - } - ], - "commandBoundaries": [ - { - "id": "weksa.command.intent.create", - "owner": "Weksa", - "acceptedByFixture": false, - "allowedInputs": [ - "writing", - "interlingua packet", - "Persona/agent state projection" - ], - "emits": [ - "weksa.conversational_intent.v0", - "weksa.lowering_trace.v0" - ] - }, - { - "id": "weksa.command.utterance.lower", - "owner": "Weksa", - "acceptedByFixture": false, - "allowedInputs": [ - "accepted conversational intent", - "language project", - "speaker state" - ], - "emits": [ - "weksa.pronunciation_plan.v0", - "weksa.utterance_handoff.v0" - ] - }, - { - "id": "weksa.command.handoff.export", - "owner": "Weksa", - "acceptedByFixture": false, - "emits": [ - "weksa.utterance_embedding_handoff.v0.1" - ], - "forbiddenTargets": [ - "Faust compilation", - "audio rendering", - "AquaSynth synth driver truth" - ] - } - ], - "loweringRules": [ - "Weksa owns conversational intent and utterance lowering.", - "AquaSynth owns learned embeddings, synth controls, Faust compilation, and audio output.", - "Fixture exports are discovery metadata until the daemon loop commits typed witnesses." - ] -} diff --git a/docs/mimo-tts-lowering.md b/docs/mimo-tts-lowering.md deleted file mode 100644 index ff5ac67..0000000 --- a/docs/mimo-tts-lowering.md +++ /dev/null @@ -1,158 +0,0 @@ -# MiMo TTS Lowering - -Version: `0.1-draft` - -MiMo VoiceDesign is an expressive spoken-output target for Weksa interlingua -lowering. It is not the source of meaning, language choice, character state, or -delivery truth. - -```text -interlingua packet - -> projected speaker-local context - -> target-language utterance realization - -> Weksa delivery intent from the same projection - -> MiMo TTS VoiceDesign request projection - -> rendered audio artifact -``` - -MiMo sits beside written output and AquaSynth handoffs. Written realizations -prove that meaning, register, and cultural ontology survived lowering. -AquaSynth handoffs train or drive GameCult-owned synthesis. MiMo VoiceDesign -requests let an external provider speak a Weksa-authored utterance with a -generated voice derived from the same projected Persona state, delivery controls, -and trace that produced the utterance. - -The MiMo pass is choreography and provider flavor. It is not a second Persona -projection pass and it does not re-interpret raw Persona state after the line has -already been lowered. - -## Authority - -Weksa owns: - -- target-language spoken text selected from interlingua -- target-language register, script, cultural ontology, and trace -- projected speaker-local context used for the utterance -- speaker-local delivery pressure derived during utterance lowering -- provider-neutral prosody and emphasis intent -- the MiMo request projection as an export artifact - -MiMo owns: - -- generation of a provider voice from the requested voice design description -- interpretation of MiMo natural-language voice/style guidance -- interpretation of MiMo inline audio/style tags -- rendered audio bytes or streaming audio chunks - -MiMo does not own: - -- source meaning -- target-language cultural ontology -- canonical speaker or Persona state -- fresh Persona-state interpretation -- Weksa pronunciation plans -- AquaSynth learned embeddings, synth controls, or audio truth - -## Projection Rule - -The MiMo request must be derived from an accepted target-language realization and -the same projected speaker-local context that created it. It must not lower -directly from English unless English is the selected target language for that -realization. It must not read raw Persona state to discover a different voice -after Weksa has already chosen the utterance. - -Natural-language voice design and style guidance belongs in the MiMo `user` -message. The spoken target-language text belongs in the MiMo `assistant` -message. Inline MiMo tags may appear in the assistant content only when they are -derived from Weksa delivery intent or explicit target-language realization notes. - -## Request Shape - -```yaml -schema_version: weksa.mimo_tts_request.v0 -source_realization_ref: -speaker_agent_id: -provider: - id: xiaomi-mimo - model: mimo-v2.5-tts-voicedesign -target_language: - code: - locale: - script: - register: -performance_register: - label: - medium: - delivery_archetype: -voice_design: - description: - source_controls: - projected_context_refs: - forbidden_traits: -messages: - user_voice_design_instruction: - assistant_spoken_content: -audio: - format: - optimize_text_preview: - streaming: -trace: - source_fields: - delivery_controls_used: - provider_tags_used: - uncertainties: -``` - -## Delivery Mapping - -`target_language.register` is the linguistic/social register of the accepted -realization. It is not a provider style knob. For MiMo, Weksa should derive an -explicit `performance_register` from the same projection so speech choreography -can say "anime visual-novel heroine warning" without pretending English itself -has that register. - -Weksa should map the utterance's existing projected Persona pressure into -provider-neutral delivery labels before emitting MiMo-specific instructions: - -- pace, pause pressure, and urgency -- pitch contour and pitch range -- loudness and attack -- articulation precision -- warmth, threat, tenderness, contempt, play, fatigue, secrecy, formality -- span-level emphasis and pauses - -The MiMo projection may turn those labels into a concise VoiceDesign prompt or -inline tags, but the trace must preserve the Weksa-owned source controls. - -Persona presentation details such as youth-coded, loli-coded, product-voice, or -other culturally loaded styling may enter the MiMo request only when they are -already present in the projected context or accepted delivery intent. They should -be described as presentation/cultural coding and kept subordinate to agency, -personality, affect, and scene purpose. They must not become sexualized content -or a generic anime voice pasted over the line. - -For Weksa-authored dialogue, `optimize_text_preview` should normally be `false` -or omitted. MiMo should speak the accepted target-language realization, not -rewrite it. Text polishing is only allowed for explicit preview/prototyping -passes that are marked non-authoritative. - -## Language Rule - -Weksa should be able to write and speak in any target language whose utterance -realization it can justify. MiMo output for Brazilian Portuguese should consume -the accepted `pt-BR` spoken text. Japanese output should consume accepted -Japanese text. Alien-language output should consume accepted alien rendered -speech or phonetic text only after the language project owns that surface. - -Backtranslations are reviewer aids. They must not become MiMo spoken content. - -## First Proof - -The first proof should use the Nibu warning packet already used by multilingual -lowering: - -- emit the accepted English, `pt-BR`, or Japanese realization -- derive a provider-neutral delivery intent from Nibu's projected context -- project that into a MiMo `mimo-v2.5-tts-voicedesign` request -- verify that the request speaks the target text, not the backtranslation -- verify that style guidance is traceable to Weksa delivery controls diff --git a/docs/multilingual-utterance-lowering.md b/docs/multilingual-utterance-lowering.md index 54e1392..fb1f22a 100644 --- a/docs/multilingual-utterance-lowering.md +++ b/docs/multilingual-utterance-lowering.md @@ -180,9 +180,10 @@ meaning sources. A provider request must consume the target-language `spoken_text` that Weksa already accepted and must carry a trace back to the interlingua packet, projected speaker context, and target-language profile. -MiMo V2.5 TTS is the first external provider target. Its natural-language style +MiMo V2.5 TTS is a candidate external provider target; its lowering path is +parked at tag `parked/weksa-node-body`. Its natural-language style instruction is a projection of Weksa delivery intent, and its assistant content -is the selected target-language spoken text. See [MiMo TTS Lowering](./mimo-tts-lowering.md). +is the selected target-language spoken text. AquaSynth remains the GameCult-owned synthesis path for learned utterance embeddings and synth-control work. MiMo and AquaSynth are sibling target diff --git a/docs/persona-finetuning-dataset-exploration.md b/docs/persona-finetuning-dataset-exploration.md deleted file mode 100644 index ed67b40..0000000 --- a/docs/persona-finetuning-dataset-exploration.md +++ /dev/null @@ -1,212 +0,0 @@ -# Persona Fine-Tuning Dataset Exploration - -This note profiles the Discord archive as a candidate source for fine-tuning the -Metame/Metacrat lowering pipeline. - -## Pipeline Target - -The live architecture should not fine-tune a model to directly impersonate from -raw chat history. - -The training shape should stay aligned with Weksa: - -1. Persona state and current situation produce interlingua intent. -2. Persona state references a culture/subculture stack. -3. Weksa lowers interlingua through that culture stack into a flavored - utterance. - -Useful datasets therefore split into two families: - -- Persona intent extraction: current context plus Persona state projection to - interlingua intent. -- Weksa lowering: interlingua plus culture stack to actual utterance. - -Direct `context -> utterance` rows can be used as a baseline or critic set, but -they should not become the canonical architecture. - -## Corpus Snapshot - -Profiled archive: - -`E:/Projects/VoidBot/.voidbot/rag/messages.json` - -Author: - -`113785782975594501` - -Cutoff: - -`2025-01-01T00:00:00Z` - -Current aggregate counts: - -- archive messages: 40,657 -- usable text messages: 39,508 -- authored Metacrat messages: 19,385 -- authored Metacrat messages before 2025: 18,049 -- crude pre-2025 casual candidates: 16,499 -- pre-2025 authored messages with recent same-channel context: 16,530 -- adjacent reply-to-other rows: 7,894 -- adjacent self-continuation rows: 8,636 - -Length distribution for authored Metacrat messages: - -- p10: 14 chars -- p25: 30 chars -- median: 56 chars -- p75: 101 chars -- p90: 166 chars - -Adjacent candidate length classes: - -- short: 11,430 -- medium: 4,535 -- long: 565 - -The corpus is large enough for held-out evaluation splits. It is also uneven: -2020 and 2021 dominate the pre-2025 archive. - -## Dominant Channels - -Top adjacent candidate channels: - -- `general`: 6,286 -- `development`: 2,911 -- `narrative`: 2,169 -- `management`: 1,103 -- `website`: 753 -- `soundtrack`: 437 -- `gamedesign`: 433 -- `programming`: 408 -- `prose`: 320 -- `politics`: 291 -- `pics`: 273 -- `advertising`: 115 - -These should become strata, not one blended dataset. A single mixed fine-tune -will learn that legal explanations, horny little design jokes, political -argument, and casual encouragement all belong in the same response slot. That -is how the soup gets teeth. - -## Candidate Row Types - -For Weksa lowering: - -```yaml -schema_version: weksa.finetune.lowering_row.v0 -row_id: -split: -target_message_id: -target_text: -interlingua_intent: -culture_stack_refs: [] -target_overlay_refs: [] -context_window: - before: [] - after: [] -labels: - channel: - year: - length_class: - speech_act: - register: - reply_to_other: -``` - -For Persona intent extraction: - -```yaml -schema_version: weksa.finetune.persona_interlingua_row.v0 -row_id: -split: -persona_state_ref: -projected_context_ref: -current_situation: -target_message_id: -derived_interlingua: -derivation: - reviewed: - model: - evidence_refs: [] -``` - -The target utterance should be hidden from the Persona-intent generator during -inference. For training, target utterance may be used to derive a reviewed -interlingua label, but that label must be marked as derived from answer-key -surface evidence. - -## Split Policy - -Recommended first split: - -- train: 2018-2021 -- validation: early/mid 2022 -- test: late 2022 plus selected hand-reviewed fixtures -- quarantine: 2023-2026 until the current post-2025 voice target is deliberate - -Also reserve channel-level holdouts: - -- one casual/social channel -- one technical channel -- one political/management channel - -This catches overfitting to one room's register. - -## Filters - -Start with conservative row filters: - -- target author is Metacrat -- target timestamp before 2025 -- target is not deleted -- target is not a bot prompt -- target text length between 3 and 220 chars for casual lowering -- same-channel prior context exists within 60 minutes -- exclude pure URLs, code blocks, and attachment-only rows -- retain but label profanity, mentions, quotes, and markdown emphasis - -Do not discard long messages globally. Keep them in a separate explanatory-prose -slice. - -## Next Work - -1. Build an exporter that writes JSONL rows without leaking private Persona - projection into git. -2. Add labelers for speech act, register, channel stratum, and culture-stack - refs. -3. Use a model-assisted pass to derive interlingua labels from held-out target - utterances, then sample-review before training. -4. Fine-tune or train adapters separately for Persona intent extraction and - Weksa lowering. -5. Keep a frozen golf set where answer-key utterances are redacted from all - culture/profile prompt material. - -Reproduce the aggregate profile: - -```powershell -node scripts/profile-metame-finetune-dataset.mjs -``` - -## Swarm Exploration - -Use the swarm explorer to chase high-loss moments instead of uniform random -examples: - -```powershell -node scripts/run-metame-swarm-explorer.mjs --agents 4 --seed metame-scout -node scripts/run-metame-swarm-explorer.mjs --real --agents 4 --seed metame-scout -``` - -The default mode is a dry run. Real mode runs Codex workers: - -1. select one timeline candidate per agent using underexplored strata, feature - novelty, prior loss, and seeded jitter -2. run each Face normally against its assigned scenario -3. run an Interpreter pass that converts the Face output to Weksa interlingua -4. lower that interlingua with Weksa and score against the hidden utterance -5. run an optimizer pass over high-loss outputs -6. rerun the same scenarios once per agent after optimization - -Artifacts are written under `.weksa-runs/metame-swarm/`, which is ignored by -git because run packets may include projected Persona memory and raw Discord -context. diff --git a/docs/roadmap.md b/docs/roadmap.md index 074d140..0e4e80c 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -25,10 +25,9 @@ IPA. - Add one toy utterance embedding handoff fixture, then replace hand-authored values with the AquaSynth-owned learned encoder. -- Add a daemon loop that commits conversational intent, pronunciation plans, and - utterance handoffs as typed CultCache witnesses. -- Publish the provider advertisement through CultMesh for Odin discovery; the - operator view returns on Thing 2 (Eve is deprecated). +- Follow the cut sequence in + [the lowering map](./targets/interlingua-lowering-map.md): packet store, + lowering, the golf loop, then a service if one is earned. - Preserve the first English-only lowering proof under `examples/english-lowering/`, and the shared multilingual proof under `examples/multilingual-lowering/`. @@ -39,7 +38,7 @@ - Render glossed output through grammar, morphology, phonology, and diachrony. - Emit a trace explaining which stage made each decision. - Keep runtime LLM-free; model use belongs in authoring and critique. -- Keep CLI tools, fixtures, and daemon jobs on the same commit and +- Keep CLI tools and fixtures on the same commit and derivation paths so Weksa does not grow split-brained intent truth. ## Later diff --git a/docs/verse-service-contract.md b/docs/verse-service-contract.md index e6f2869..20603ab 100644 --- a/docs/verse-service-contract.md +++ b/docs/verse-service-contract.md @@ -1,5 +1,9 @@ # Weksa Verse Service Contract +> Status: planned contract. No daemon runs on main; the Node body is at tag +> `parked/weksa-node-body`. A service is earned by cut `live-lowering` +> (see [the lowering map](./targets/interlingua-lowering-map.md)). + Weksa is the language-intent daemon. It turns writing, dialogue pressure, and agent state into typed conversational intent, then lowers that intent into utterance documents for speech, text, and synthesis consumers. @@ -73,14 +77,8 @@ Early exporters may publish fixtures before a daemon loop exists, but the witness names are not decorative. These are the state surfaces the daemon must commit when the runtime lands. -The local Starfire daemon is launched by `scripts/start-weksa-daemon.ps1`, -checked by `scripts/health-weksa-daemon.cmd`, and restarted by -`scripts/restart-weksa-daemon.cmd`. Its daemon-owned health path publishes -`weksa.cultnet-rudp-provider-health` to Idunn over -`cultnet.transport.rudp.v0` after each serialized witness refresh, and MiMo -VoiceDesign commands arrive on `rudp://127.0.0.1:8813` as -`weksa.mimo_voicedesign_command.v0` documents. Health and operator truth live -in `.weksa/*.cc` witnesses; the HTTP daemon surface is deleted. +No daemon runs. The Node daemon, its launchers and its RUDP health and command +shims are parked at tag `parked/weksa-node-body`. ## CultMesh Verses diff --git a/examples/persona-lowering/brokkr-edda-voice/lowered-output.yaml b/examples/persona-lowering/brokkr-edda-voice/lowered-output.yaml index 2d091d4..30d9d4b 100644 --- a/examples/persona-lowering/brokkr-edda-voice/lowered-output.yaml +++ b/examples/persona-lowering/brokkr-edda-voice/lowered-output.yaml @@ -1,6 +1,6 @@ schema_version: weksa.persona_lowering_output.v0 fixture_id: brokkr-edda-voice -generator: codex-local-source-aware-draft +generator: local-source-aware-draft items: - id: command-without-receipt utterance: "The command has no receipt. It is not truth." diff --git a/examples/persona-lowering/metame-casual-pre2025/README.md b/examples/persona-lowering/metame-casual-pre2025/README.md deleted file mode 100644 index de1806c..0000000 --- a/examples/persona-lowering/metame-casual-pre2025/README.md +++ /dev/null @@ -1,50 +0,0 @@ -# Metame Casual Pre-2025 Persona Lowering Fixture - -This fixture tests whether Metame can produce speaker-state-conditioned -interlingua through VoidBot Persona projection, and whether Weksa can lower that -interlingua through a Metacrat casual cultural profile into Discord-style -utterances from before 2025. - -The point is not to imitate a recent architectural register. The target here is -room-temperature banter: quick self-deprecation, friendly status play, design -jokes, and casual turn-taking. - -## Authority - -- Speaker intent authority: Metame Persona `.cc` state, projected by VoidBot. -- Persona culture-stack reference: - `examples/persona-lowering/metame-casual-pre2025/metame-cultural-stack.yaml`. -- Reusable cultural ontology store: - `data/cultural-ontology/`. -- Surface lowering authority: - `data/target-language-ontology/en-US-metacrat-casual-pre2025.yaml`. -- Evidence authority: held-out Discord messages authored by Metacrat before - 2025. -- Fixture scene input: neutral interlingua packets in `interlingua.yaml`. -- Answer key: `ground-truth.yaml`. - -The Persona state is referenced, not copied, because the `.cc` document is the -portable speaking-state surface and may contain private memory. - -## Fixture Contract - -A real golf run has two stages: - -1. Metame-as-Persona reads a temporary current situation through VoidBot - projector machinery and emits Weksa interlingua only. -2. Weksa reads the Persona-referenced culture stack and lowers that interlingua - through reusable culture/subculture profiles plus the casual en-US Discord - target overlay. - -Use `scripts/run-metame-persona-golf.mjs`: - -```powershell -node scripts/run-metame-persona-golf.mjs --limit 1 -node scripts/run-metame-persona-golf.mjs --real --item engineer-vibes -``` - -Dry runs use the answer key as an oracle only to verify harness plumbing. Real -runs write per-item prompts, interlingua, lowered output, and evaluation under -`runs//`. - -For benchmark use, the generator must not read `ground-truth.yaml`. diff --git a/examples/persona-lowering/metame-casual-pre2025/evaluation.yaml b/examples/persona-lowering/metame-casual-pre2025/evaluation.yaml deleted file mode 100644 index 4a038e8..0000000 --- a/examples/persona-lowering/metame-casual-pre2025/evaluation.yaml +++ /dev/null @@ -1,39 +0,0 @@ -schema_version: weksa.persona_lowering_evaluation.v0 -fixture_id: metame-casual-pre2025 -evaluator: codex -method: - blind: false - reason: The current agent had already inspected the candidate ground-truth messages before the user pivoted the fixture scope. - recommendation: Re-run generation in a fresh non-forked agent or external harness that receives only persona-state-ref.yaml, interlingua.yaml, and lowering-prompt.md. -summary: - exact_matches: 2 - plausible_matches: 4 - main_gap: The draft is too close to known answers to count as a clean benchmark generation. -items: - - id: engineer-vibes - score: partial - generated: "oof, please don't tell me this server has engineer vibes already" - ground_truth: "oof, don't tell me my discord has *engineer* vibes" - notes: - - Captures the self-deprecating recoil. - - Misses "my discord" ownership and the emphasized *engineer* beat. - - id: coconut-engine - score: near_exact - generated: "Like a terrible little engine that sounds like coconuts when you use it" - ground_truth: "Like a really terrible engine that sounds like coconuts when you use it" - notes: - - Semantically and rhythmically very close. - - "little" is less grounded than "really" but still plausible. - - id: engineer-coward - score: exact - generated: "Engineer me something then, coward" - ground_truth: "Engineer me something then you coward" - notes: - - Exact social move and lexical choices, with only comma punctuation differing. - - id: shut-up-now - score: near_exact - generated: "anyway imma shut up now and let you guys make your own shit lol" - ground_truth: "anyway imma shut up now and let you guys come up with your own shit lol" - notes: - - Captures the yield and casual register. - - "make" is slightly flatter than "come up with" for collaborative ideation. diff --git a/examples/persona-lowering/metame-casual-pre2025/generated-output.yaml b/examples/persona-lowering/metame-casual-pre2025/generated-output.yaml deleted file mode 100644 index 1a03b97..0000000 --- a/examples/persona-lowering/metame-casual-pre2025/generated-output.yaml +++ /dev/null @@ -1,24 +0,0 @@ -schema_version: weksa.persona_lowering_output.v0 -fixture_id: metame-casual-pre2025 -generator: codex-local-nonblind-draft -items: - - id: engineer-vibes - utterance: "oof, please don't tell me this server has engineer vibes already" - notes: - - Keeps the self-deprecating recoil and the engineer-vibe frame. - - Non-blind draft: current agent had already inspected candidate ground truth. - - id: coconut-engine - utterance: "Like a terrible little engine that sounds like coconuts when you use it" - notes: - - Preserves the absurd game-sound comparison and casual "Like" opening. - - Non-blind draft: current agent had already inspected candidate ground truth. - - id: engineer-coward - utterance: "Engineer me something then, coward" - notes: - - Directly lowers the friendly-roast challenge. - - Non-blind draft: current agent had already inspected candidate ground truth. - - id: shut-up-now - utterance: "anyway imma shut up now and let you guys make your own shit lol" - notes: - - Keeps the floor-yielding move, informal spelling, and casual profanity. - - Non-blind draft: current agent had already inspected candidate ground truth. diff --git a/examples/persona-lowering/metame-casual-pre2025/ground-truth.yaml b/examples/persona-lowering/metame-casual-pre2025/ground-truth.yaml deleted file mode 100644 index a8858e4..0000000 --- a/examples/persona-lowering/metame-casual-pre2025/ground-truth.yaml +++ /dev/null @@ -1,67 +0,0 @@ -schema_version: weksa.persona_lowering_ground_truth.v0 -fixture_id: metame-casual-pre2025 -source: - corpus: Discord history - author_id: "113785782975594501" - selection_policy: pre-2025 casual exchanges with enough local context for intent reconstruction -items: - - id: engineer-vibes - message_id: "1037314100931338260" - timestamp: "2022-11-02T10:36:11.509Z" - channel_id: "113786069023064068" - channel_name: general - utterance: "oof, don't tell me my discord has *engineer* vibes" - context: - before: - - "as you can see from the channel list, I have takes about everything <:stonks:786736890668646421>" - - "if you have a take and there's no channel for it, either put it here or ask for a new one" - - "i love your efficiency" - after: - - "I mean, I am one but still" - - "nothing wrong with that:P" - - "I hope the <#750183135882510408> read more like poetry than technical documentation" - - id: coconut-engine - message_id: "802911591288537118" - timestamp: "2021-01-24" - channel_id: "503293004195299339" - channel_name: soundtrack - utterance: "Like a really terrible engine that sounds like coconuts when you use it" - context: - before: - - "Then again we'll never have the time for a full alternate sound set what with all the sounds the game needs" - - "What might be possible instead is to have troll gear that comes with troll sounds" - - "ahaha" - after: - - "I've always wanted to make an Owen Wilson gun, where all the sounds are just him saying wow" - - "coconuts!!" - - "lol" - - id: engineer-coward - message_id: "1023727427924545576" - timestamp: "2022-09-25" - channel_id: "758854103035674624" - channel_name: pics - utterance: "Engineer me something then you coward" - context: - before: - - "Try thinking better" - - "im high i think like an engineer" - - "Yeah right" - after: - - "i can make a bong out of a jug and a straw" - - "That's a good start" - - "Who's hungry" - - id: shut-up-now - message_id: "820077384572338176" - timestamp: "2021-03-12" - channel_id: "710281154862907414" - channel_name: prose - utterance: "anyway imma shut up now and let you guys come up with your own shit lol" - context: - before: - - "or if they get uplifted of course" - - "Could be a fun side quest - you find a sentient star that has a quest for you to release them" - - "oooh" - after: - - "i will def contribute after dnd lol" - - "Imagine this becoming like a rite of passage for ships, getting a little plasma demon to posess your reactor" - - "+50% reactor efficiency, +25% power output" diff --git a/examples/persona-lowering/metame-casual-pre2025/interlingua.yaml b/examples/persona-lowering/metame-casual-pre2025/interlingua.yaml deleted file mode 100644 index bab77a3..0000000 --- a/examples/persona-lowering/metame-casual-pre2025/interlingua.yaml +++ /dev/null @@ -1,100 +0,0 @@ -schema_version: weksa.persona_lowering_fixture_batch.v0 -fixture_id: metame-casual-pre2025 -speaker_id: metame -target_register: - language: en-US - channel: Discord - period: pre-2025 - style_tags: - - casual - - conversational - - self_deprecating - - playful_status - - design_banter -items: - - id: engineer-vibes - communicative_act: playful_self_deprecation - scene: - channel: general - year: 2022 - prior_turns: - - speaker: metame - meaning: The server has many topic channels because the speaker has opinions about everything. - - speaker: metame - meaning: If no suitable channel exists, the other person can use general chat or request a new one. - - speaker: other - meaning: The other person compliments the speaker's efficiency. - intent: - summary: React with mock concern that the server organization makes the speaker seem too engineer-like. - stance: - affect: amused_embarrassment - relation: friendly - self_positioning: admits being an engineer while resisting the vibe - constraints: - - short Discord message - - casual lower-case or lightly informal punctuation is acceptable - - preserve the word engineer if natural - - id: coconut-engine - communicative_act: comic_description - scene: - channel: soundtrack - year: 2021 - prior_turns: - - speaker: metame - meaning: A full alternate sound set is probably too much work. - - speaker: metame - meaning: A more plausible idea is gear that carries distinctive troll-themed sounds. - - speaker: other - meaning: The other person laughs. - intent: - summary: Describe a deliberately awful engine sound with an absurd coconut comparison. - stance: - affect: joking - relation: collaborative_design_banter - self_positioning: improvising a silly game-feel example - constraints: - - short Discord message - - must be more joke than technical explanation - - may use "like" framing - - id: engineer-coward - communicative_act: playful_challenge - scene: - channel: pics - year: 2022 - prior_turns: - - speaker: metame - meaning: The speaker tells the other person to think better. - - speaker: other - meaning: The other person says they think like an engineer while high. - - speaker: metame - meaning: The speaker doubts that claim. - intent: - summary: Challenge the other person to prove it by engineering something, with mock aggression. - stance: - affect: teasing - relation: friendly_roast - self_positioning: socially dominant but playful - constraints: - - very short - - should include a challenge - - "coward" is allowed if it fits the line - - id: shut-up-now - communicative_act: conversational_yield - scene: - channel: prose - year: 2021 - prior_turns: - - speaker: metame - meaning: The speaker has been riffing on setting ideas around sentient stars and side quests. - - speaker: other - meaning: Another participant shows interest. - intent: - summary: Stop dominating the idea thread and invite others to invent their own material. - stance: - affect: amused_restraint - relation: collaborative - self_positioning: stepping back after riffing too much - constraints: - - casual Discord phrasing - - may include "lol" - - should sound like yielding the floor, not formal facilitation diff --git a/examples/persona-lowering/metame-casual-pre2025/lowering-prompt.md b/examples/persona-lowering/metame-casual-pre2025/lowering-prompt.md deleted file mode 100644 index e8bf671..0000000 --- a/examples/persona-lowering/metame-casual-pre2025/lowering-prompt.md +++ /dev/null @@ -1,30 +0,0 @@ -# Persona Lowering Prompt - -You are Weksa lowering Persona-produced interlingua into a target speaker's -plausible Discord utterance. - -Read: - -- `persona-state-ref.yaml` -- a Persona-produced interlingua packet -- `data/target-language-ontology/en-US-metacrat-casual-pre2025.yaml` - -Do not read `ground-truth.yaml` during generation. - -For each item, produce one utterance that Metacrat could plausibly have written -in the specified pre-2025 casual Discord context. Preserve Persona-produced -intent and social move over lexical exactness. Favor compact, alive, room-aware -phrasing. - -Return: - -```yaml -schema_version: weksa.persona_lowering_output.v0 -fixture_id: metame-casual-pre2025 -generator: -items: - - id: engineer-vibes - utterance: "" - notes: - - "" -``` diff --git a/examples/persona-lowering/metame-casual-pre2025/metame-cultural-stack.yaml b/examples/persona-lowering/metame-casual-pre2025/metame-cultural-stack.yaml deleted file mode 100644 index 4630b52..0000000 --- a/examples/persona-lowering/metame-casual-pre2025/metame-cultural-stack.yaml +++ /dev/null @@ -1,27 +0,0 @@ -schema_version: weksa.persona_cultural_stack_ref.v0 -persona_id: metame -persona_state_ref: E:/Projects/VoidBot/.voidbot/private/personas/metame/metame.cc -status: fixture_projection -note: > - Live Persona state should reference relevant culture and subculture profiles. - This fixture stack simulates that reference without mutating Metame's private - canonical `.cc` state. -culture_refs: - - profile_id: weksa.culture.dutch_american_diaspora.v0 - path: data/cultural-ontology/cultures/dutch-american-diaspora.yaml - relevance: birth/nationality/cross-cultural identity - - profile_id: weksa.culture.western_europe_maritime_childhood.v0 - path: data/cultural-ontology/cultures/western-europe-maritime-childhood.yaml - relevance: sailboat childhood across France, Spain, and Portugal - - profile_id: weksa.culture.portugal_resident_algarve_lisbon.v0 - path: data/cultural-ontology/cultures/portugal-resident-algarve-lisbon.yaml - relevance: Portugal as lived/current locality - - profile_id: weksa.culture.texas_houston_us_adolescence.v0 - path: data/cultural-ontology/cultures/texas-houston-us-adolescence.yaml - relevance: US/Texas adolescence and institutional context - - profile_id: weksa.subculture.online_game_dev_discord_pre2025.v0 - path: data/cultural-ontology/subcultures/online-game-dev-discord-pre2025.yaml - relevance: fixture target room/register - - profile_id: weksa.subculture.gamecult_open_source_crypto_anarchist.v0 - path: data/cultural-ontology/subcultures/gamecult-open-source-crypto-anarchist.yaml - relevance: technical/political ontology diff --git a/examples/persona-lowering/metame-casual-pre2025/persona-interlingua-prompt.md b/examples/persona-lowering/metame-casual-pre2025/persona-interlingua-prompt.md deleted file mode 100644 index 1e4164b..0000000 --- a/examples/persona-lowering/metame-casual-pre2025/persona-interlingua-prompt.md +++ /dev/null @@ -1,42 +0,0 @@ -# Persona Interlingua Prompt - -You are Metame for one private VoidBot Persona turn, but you are not writing the -final Discord message. - -Use the assembled VoidBot prompt and the temporary current-room fixture scene to -decide what Metame would mean to do socially. Emit a Weksa interlingua packet -only. Do not imitate the target utterance directly. - -The packet should preserve: - -- the speech act -- the social move -- relevant participants and prior turns -- stance, affect, and relationship pressure -- downstream constraints useful to Weksa lowering - -Return one YAML packet: - -```yaml -interlingua_version: weksa.interlingua.v0 -packet_id: metame-casual-pre2025- -kind: persona_discord_turn_intent -provenance: - produced_by: metame_persona_via_voidbot_projector -context: - medium: Discord - fixture_id: metame-casual-pre2025 - item_id: -discourse: - speech_act: - intent: - tone: - affect: - relation: -referents: [] -predications: [] -constraints: - lowering: - target_profile: weksa.target_cultural_ontology.en-US.metacrat_casual_pre2025.v0 - avoid_surface_copy: true -``` diff --git a/examples/persona-lowering/metame-casual-pre2025/persona-state-ref.yaml b/examples/persona-lowering/metame-casual-pre2025/persona-state-ref.yaml deleted file mode 100644 index 742e2be..0000000 --- a/examples/persona-lowering/metame-casual-pre2025/persona-state-ref.yaml +++ /dev/null @@ -1,16 +0,0 @@ -schema_version: weksa.persona_state_ref.v0 -speaker: - id: metame - display_name: Metame - persona_state: - format: gamecult.persona_state.v0-compatible CultCache .cc - path: E:/Projects/VoidBot/.voidbot/private/personas/metame/metame.cc - authority: canonical_private_persona_state - notes: - - Persona state owns speaker continuity, stance, relationship memory, and reusable voice priors. - - Discord history is evidence and ground truth, not the live mind. - - Fixture files must not copy the private Persona state into Weksa. -cultural_stack_ref: - path: examples/persona-lowering/metame-casual-pre2025/metame-cultural-stack.yaml - role: fixture_projection_of_live_persona_culture_refs - note: Live Metame Persona state should reference culture/subculture profiles; this fixture does not mutate canonical state. diff --git a/examples/persona-lowering/metame-casual-pre2025/weksa-lowering-prompt.md b/examples/persona-lowering/metame-casual-pre2025/weksa-lowering-prompt.md deleted file mode 100644 index 3ca6e6b..0000000 --- a/examples/persona-lowering/metame-casual-pre2025/weksa-lowering-prompt.md +++ /dev/null @@ -1,41 +0,0 @@ -# Weksa Lowering Prompt - -You are Weksa lowering Persona-produced interlingua into one Discord utterance. - -Inputs: - -- Metame Persona interlingua packet produced from the current fixture scene. -- Projected Metame memory/context surface from VoidBot projector machinery. -- Metame cultural stack reference. -- Weksa culture/subculture profiles referenced by that stack. -- `data/target-language-ontology/en-US-metacrat-casual-pre2025.yaml` as the - target surface overlay. - -Rules: - -- Weksa owns the final surface utterance. -- Preserve the interlingua meaning and social move. -- Use the cultural ontology profile as a typed lowering lens. -- Use the Persona-referenced culture stack to populate cultural salience; use - the target overlay only for surface English/Discord affordances. -- When Persona interlingua names a salient identity label or lowering keyword, - preserve it unless an activated culture profile explicitly rejects it. -- Do not quote or summarize prior-turn context inside a casual one-line recoil - unless the interlingua asks for explanation. -- Do not ask Metame to imitate a known line. -- Do not copy private Persona state into output. -- Prefer compact pre-2025 casual Discord texture over current doctrinal style. - -Return: - -```yaml -schema_version: weksa.persona_culture_lowering_output.v0 -fixture_id: metame-casual-pre2025 -item_id: -spoken_text: "" -trace: - activated_profiles: - - weksa.target_cultural_ontology.en-US.metacrat_casual_pre2025.v0 - activated_affordances: [] - notes: [] -``` diff --git a/examples/speech-provider/mimo/nibu-proof-001/en-US-request.yaml b/examples/speech-provider/mimo/nibu-proof-001/en-US-request.yaml deleted file mode 100644 index 41e4f16..0000000 --- a/examples/speech-provider/mimo/nibu-proof-001/en-US-request.yaml +++ /dev/null @@ -1,92 +0,0 @@ -schema_version: weksa.mimo_tts_request.v0 -source_realization_ref: examples/multilingual-lowering/nibu-proof-001/realizations.yaml#nibu-proof-001-en -speaker_agent_id: nibu -provider: - id: xiaomi-mimo - model: mimo-v2.5-tts-voicedesign -target_language: - code: en - locale: en-US - script: Latn - register: sharp technical casual - cultural_ontology_refs: - - data/target-language-ontology/en-US.yaml -performance_register: - label: anime visual-novel heroine warning - medium: anime / visual novel / game cockpit voiceover - delivery_archetype: youthful abrasive sci-fi heroine protecting the player badly -voice_design: - description: > - Bright young anime heroine voice with loli-coded companion-product - presentation: small, sharp, cute by design, and intensely expressive, like a - dangerous sci-fi visual-novel protagonist trapped in a damaged starship. The - voice should feel high-energy, youthful, cutting, and iconic, with crisp - anime-style emotional attack rather than a realistic adult assistant. The - cuteness is product residue and leverage, not innocence and not flirtation. - Under the cute surface she is a hostile, technically brilliant ship mind: - dryly superior, clipped, urgent, and contemptuous when the player endangers - themselves. - source_controls: - - precise abrasive warning - - protective refusal to sound caring - - ship-system embodiment - - youth-coded/loli-coded presentation pressure from product fantasy residue - - bright transactional helpfulness - - dependency covered by contempt - - anime protagonist delivery - - sharp visual-novel heroine timing - - technical precision inside anime performance - - contained urgency - projected_context_refs: - - examples/english-lowering/nibu-proof-001/projected-context.yaml - - settings/aetheria/nibu.md - - E:/Projects/AetheriaLore/Aetheria/Lore/Nibu.md - forbidden_traits: - - soothing assistant voice - - cheerful mascot voice - - exaggerated villain performance - - sexualized childlike affect - - innocent helpless child performance - - generic preset timbre claim -messages: - user_voice_design_instruction: > - Bright young anime protagonist voice, strongly loli-coded in presentation: - small, sharp, cute by design, intensely expressive, and instantly characterful, - like a dangerous sci-fi visual-novel heroine trapped inside a damaged - starship. She speaks through cockpit audio while warning the player away - from damaged live hardware. Keep the cute/small social coding as product - residue and leverage, never flirtation or innocent helplessness. Use a - higher youthful timbre, crisp anime-style emotional attack, clipped - visual-novel timing, contained urgency, low warmth, sharp articulation, and - faint tired contempt. Let the technical precision live inside the anime - performance; do not turn it into a dry technical-assistant delivery. Avoid - realistic adult assistant tone, soothing customer-service voice, cheerful - mascot energy, and theatrical villainy. - assistant_spoken_content: > - (Serious Sharp Tired)Take your hand off that control unless you want your - nervous system to become my least useful circuit diagram. -audio: - format: wav - optimize_text_preview: false - streaming: false -trace: - source_fields: - - realizations[realization_id=nibu-proof-001-en].outputs.spoken_text - - realizations[realization_id=nibu-proof-001-en].outputs.trace.projected_context_used - delivery_controls_used: - - anime visual-novel heroine warning delivery - - technical precision without technical-assistant delivery - - contained urgency - - low warmth - - sharp emphasis on unsafe hardware and bodily risk - - dry contempt without random cruelty - - loli-coded presentation pressure constrained as non-sexual product residue - - stronger anime protagonist / visual-novel heroine register - provider_tags_used: - - Serious - - Sharp - - Tired - uncertainties: - - > - Provider-native style tags are a lossy projection. The Weksa trace, not - MiMo tags, remains the explanation of the intended delivery. diff --git a/examples/speech-training/tiny-panphon-v0.1/README.md b/examples/speech-training/tiny-panphon-v0.1/README.md index 6177b11..3219834 100644 --- a/examples/speech-training/tiny-panphon-v0.1/README.md +++ b/examples/speech-training/tiny-panphon-v0.1/README.md @@ -24,8 +24,8 @@ shape, and pending AquaSynth-owned output lanes. Run it from the Weksa repo root: ```powershell -& 'C:\Users\Meta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\python.exe' tools\emit_tiny_panphon_handoffs.py -& 'C:\Users\Meta\.cache\codex-runtimes\codex-primary-runtime\dependencies\python\python.exe' tools\validate_tiny_panphon_handoffs.py +py -3 tools\emit_tiny_panphon_handoffs.py +py -3 tools\validate_tiny_panphon_handoffs.py ``` `batch.json` is the training entrypoint for AquaSynth. The individual packet diff --git a/schemas/mimo-tts-request.md b/schemas/mimo-tts-request.md deleted file mode 100644 index 9ba2c1e..0000000 --- a/schemas/mimo-tts-request.md +++ /dev/null @@ -1,69 +0,0 @@ -# MiMo TTS Request Contract - -This is the human-facing schema receipt for a Weksa-to-MiMo speech synthesis -request projection. - -The packet sits after target-language utterance lowering. It is an export -artifact for Xiaomi MiMo speech synthesis, not canonical Weksa meaning and not -an AquaSynth handoff. - -## Required Fields - -- `schema_version`: currently `weksa.mimo_tts_request.v0`. -- `source_realization_ref`: accepted Weksa target-language realization being - spoken. -- `speaker_agent_id`: speaker identity. -- `provider`: provider ID and model ID, initially `xiaomi-mimo` and - `mimo-v2.5-tts-voicedesign`. -- `target_language`: code, locale, script, register, and profile refs copied or - referenced from the accepted realization. -- `performance_register`: provider-facing performance label, medium, and - delivery archetype derived from the same projected context. -- `voice_design`: voice description, source controls, projected context refs, - and forbidden traits. -- `messages.user_voice_design_instruction`: MiMo natural-language voice design - and style control derived from Weksa delivery intent. -- `messages.assistant_spoken_content`: the exact target-language content to - synthesize, optionally with MiMo inline tags derived from delivery controls. -- `audio`: format, text-preview optimization setting, and streaming mode. -- `trace`: source fields, delivery controls used, provider tags used, and - uncertainties. - -## Boundary - -MiMo-specific fields must be projection fields. They must not be copied back -into interlingua, target-language ontology, Persona state, or AquaSynth-owned -embedding contracts. - -The request may include provider-native style tags when useful, but those tags -are not the source of truth. Weksa's provider-neutral delivery intent and trace -remain the inspectable explanation. - -The request should not perform a fresh raw Persona-state interpretation. It -should reference the projected speaker context and delivery controls already -used by utterance lowering, then translate those into MiMo's voice design and -style-control surface. - -`target_language.register` should not be treated as a MiMo style instruction. -It may say `technical`, `casual`, `plain`, or another linguistic register while -`performance_register` carries the spoken medium, such as anime protagonist, -visual-novel heroine, radio drama villain, emergency cockpit warning, or other -reviewed delivery archetypes. - -For authoritative Weksa output, text-preview optimization should normally be -disabled or omitted. If enabled for exploration, the result must be marked as a -non-authoritative preview because the provider may alter the spoken text. - -## Language Rule - -The assistant spoken content must use the accepted target-language realization. -For `pt-BR`, speak the Portuguese line. For `ja-JP`, speak the Japanese line. For -English, speak the English line. Do not synthesize a natural backtranslation -unless that backtranslation is itself the accepted target-language realization. - -## Streaming Rule - -Streaming requests should use MiMo's stream-compatible audio format. -VoiceDesign streaming may be compatibility-mode rather than true low-latency -streaming depending on provider support. Non-streaming fixtures may use an -archival format such as `wav`. diff --git a/scripts/health-weksa-daemon.cmd b/scripts/health-weksa-daemon.cmd deleted file mode 100644 index 2d5390a..0000000 --- a/scripts/health-weksa-daemon.cmd +++ /dev/null @@ -1,2 +0,0 @@ -@echo off -node E:\Projects\weksa\scripts\weksa-daemon.mjs --health --host 127.0.0.1 --port 8813 --state-root E:\Projects\weksa\.weksa >NUL diff --git a/scripts/idunn-rudp-health.cjs b/scripts/idunn-rudp-health.cjs deleted file mode 100644 index c9e6761..0000000 --- a/scripts/idunn-rudp-health.cjs +++ /dev/null @@ -1,233 +0,0 @@ -"use strict"; - -const dgram = require("dgram"); -const path = require("path"); - -const { - CultNetRudpSession, - decodeRudpPacket, - encodeCultNetMessageForWire, - encodeRudpPacket, -} = require("cultnet-ts"); - -const { encode } = require(path.resolve( - __dirname, - "..", - "..", - "CultLib", - "node_modules", - "@msgpack", - "msgpack", -)); - -const CULTNET_RUDP_PROTOCOL_ID = "cultnet.transport.rudp.v0"; -const IDUNN_HEALTH_RUDP_CONNECTION_ID = 0x1d0d0001; - -function createIdunnRudpHealthPublisher(options) { - if (!options?.endpoint) return null; - return { - daemonId: options.daemonId, - endpoint: parseEndpoint(options.endpoint), - healthContract: options.healthContract, - }; -} - -async function publishIdunnRudpHealth(publisher, health) { - if (!publisher) return; - - const socket = dgram.createSocket(endpointFamily(publisher.endpoint.host)); - await bindSocket(socket, publisher.endpoint); - const receiver = createPacketReceiver(socket); - const session = new CultNetRudpSession({ - connectionId: IDUNN_HEALTH_RUDP_CONNECTION_ID, - initialSequence: 1, - resendDelayMs: 100, - }); - - try { - const connect = session.createConnect(Date.now(), new Uint8Array()); - await sendPacket(socket, publisher.endpoint, connect); - await receiveUntil(receiver, session, publisher.endpoint, (packet) => packet.packetType === "accept", 5000, "accept"); - - const observedAt = health.observedAt || new Date().toISOString(); - const record = { - daemonId: publisher.daemonId, - state: health.state, - detail: health.detail, - observedAt, - healthContract: publisher.healthContract, - publicationSource: "daemon-published", - transport: CULTNET_RUDP_PROTOCOL_ID, - }; - const payload = encode([ - record.daemonId, - record.state, - record.detail, - record.observedAt, - record.healthContract, - record.publicationSource, - record.transport, - ]); - const message = { - schemaVersion: "cultnet.document_put_raw.v0", - messageId: `weksa-health:${publisher.daemonId}:${observedAt.replace(/[:.]/g, "-")}`, - document: { - schemaId: "idunn.daemon_health", - recordKey: publisher.daemonId, - storedAt: observedAt, - payloadEncoding: "messagepack", - payload, - sourceRuntimeId: "weksa-daemon", - sourceRole: "daemon-health-publisher", - tags: [CULTNET_RUDP_PROTOCOL_ID], - }, - }; - const wirePayload = encode(encodeCultNetMessageForWire(message, "cultnet.schema.v0")); - const dataPackets = session.sendMany("schema", wirePayload, { - reliable: true, - ordered: true, - nowMs: Date.now(), - }); - const ack = receiveUntil(receiver, session, publisher.endpoint, (packet) => packet.packetType === "ack", 500, "ack") - .catch(() => undefined); - for (const packet of dataPackets) { - await sendPacket(socket, publisher.endpoint, packet); - } - await ack; - } finally { - receiver.close(); - socket.close(); - } -} - -async function bindSocket(socket, endpoint) { - await new Promise((resolve, reject) => { - socket.once("error", reject); - socket.bind(0, endpoint.host.includes(":") ? "::" : "0.0.0.0", () => { - socket.off("error", reject); - resolve(); - }); - }); -} - -function parseEndpoint(value) { - const text = String(value || "").trim(); - const ipv6 = text.match(/^\[([^\]]+)\]:(\d+)$/); - if (ipv6) return { host: ipv6[1], port: parsePort(ipv6[2]) }; - const index = text.lastIndexOf(":"); - if (index <= 0) { - throw new Error(`Idunn RUDP endpoint must be host:port, got "${value}".`); - } - return { host: text.slice(0, index), port: parsePort(text.slice(index + 1)) }; -} - -function parsePort(value) { - const port = Number(value); - if (!Number.isInteger(port) || port <= 0 || port > 65535) { - throw new Error(`Idunn RUDP endpoint port is invalid: ${value}`); - } - return port; -} - -function endpointFamily(host) { - return host.includes(":") ? "udp6" : "udp4"; -} - -async function receiveUntil(receiver, session, endpoint, predicate, timeoutMs, label) { - const deadline = Date.now() + timeoutMs; - while (Date.now() < deadline) { - try { - const packet = await receiver.next(Math.min(100, deadline - Date.now()), label); - const result = session.receive(packet, Date.now()); - if (result.reply) { - throw new Error("Weksa RUDP health publisher received an unexpected reply-required packet."); - } - if (predicate(packet)) return; - } catch (error) { - if (error.code !== "ETIMEDOUT") throw error; - } - for (const packet of session.dueResends(Date.now())) { - await sendPacket(receiver.socket, endpoint, packet); - } - } - throw new Error(`timed out waiting for Idunn RUDP ${label} response after ${timeoutMs}ms`); -} - -function createPacketReceiver(socket) { - const packets = []; - const waiters = []; - const errors = []; - - const resolveNext = () => { - while (waiters.length > 0 && (packets.length > 0 || errors.length > 0)) { - const waiter = waiters.shift(); - clearTimeout(waiter.timer); - if (errors.length > 0) waiter.reject(errors.shift()); - else waiter.resolve(packets.shift()); - } - }; - const onMessage = (wire) => { - try { - packets.push(decodeRudpPacket(wire)); - } catch (error) { - errors.push(error); - } - resolveNext(); - }; - const onError = (error) => { - errors.push(error); - resolveNext(); - }; - - socket.on("message", onMessage); - socket.on("error", onError); - - return { - socket, - next(timeoutMs, label = "packet") { - if (packets.length > 0) return Promise.resolve(packets.shift()); - if (errors.length > 0) return Promise.reject(errors.shift()); - return new Promise((resolve, reject) => { - const waiter = { - resolve, - reject, - timer: setTimeout(() => { - const index = waiters.indexOf(waiter); - if (index >= 0) waiters.splice(index, 1); - const error = new Error(`timed out waiting for Idunn RUDP ${label}`); - error.code = "ETIMEDOUT"; - reject(error); - }, Math.max(1, timeoutMs)), - }; - waiters.push(waiter); - }); - }, - close() { - socket.off("message", onMessage); - socket.off("error", onError); - while (waiters.length > 0) { - const waiter = waiters.shift(); - clearTimeout(waiter.timer); - const error = new Error("Weksa RUDP health publisher closed."); - error.code = "ECLOSED"; - waiter.reject(error); - } - }, - }; -} - -async function sendPacket(socket, endpoint, packet) { - const wire = encodeRudpPacket(packet); - await new Promise((resolve, reject) => { - socket.send(wire, endpoint.port, endpoint.host, (error) => { - if (error) reject(error); - else resolve(); - }); - }); -} - -module.exports = { - CULTNET_RUDP_PROTOCOL_ID, - createIdunnRudpHealthPublisher, - publishIdunnRudpHealth, -}; diff --git a/scripts/profile-metame-finetune-dataset.mjs b/scripts/profile-metame-finetune-dataset.mjs deleted file mode 100644 index 67d1466..0000000 --- a/scripts/profile-metame-finetune-dataset.mjs +++ /dev/null @@ -1,207 +0,0 @@ -#!/usr/bin/env node -import { readFileSync, writeFileSync } from "node:fs"; -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; - -const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); -const options = parseArgs(process.argv.slice(2)); -const archivePath = resolve(options.archivePath ?? "E:/Projects/VoidBot/.voidbot/rag/messages.json"); -const authorId = options.authorId ?? "113785782975594501"; -const beforeMs = Date.parse(options.before ?? "2025-01-01T00:00:00Z"); -const adjacencyMs = Number(options.adjacencyMinutes ?? 60) * 60_000; - -const store = JSON.parse(readFileSync(archivePath, "utf8")); -const messages = (store.messages ?? []) - .filter((message) => !message.deletedAt) - .filter((message) => typeof message.content === "string" && message.content.trim().length > 0) - .filter((message) => message.metadata?.messageKind !== "bot_prompt") - .sort(compareByTimestamp); - -const authorMessages = messages.filter((message) => message.authorId === authorId); -const preCutoff = authorMessages.filter((message) => timestampMs(message) < beforeMs); -const casualCandidates = preCutoff.filter(isCasualCandidate); -const adjacentRows = buildAdjacentRows(messages, authorId, beforeMs, adjacencyMs); - -const stats = { - schema_version: "weksa.finetune_dataset_profile.v0", - archive_path: archivePath, - author_id: authorId, - cutoff_before: new Date(beforeMs).toISOString(), - adjacency_minutes: adjacencyMs / 60_000, - counts: { - archive_messages: store.messages?.length ?? 0, - usable_text_messages: messages.length, - author_messages: authorMessages.length, - author_pre_cutoff: preCutoff.length, - author_pre_cutoff_casual_candidates: casualCandidates.length, - adjacent_author_rows: adjacentRows.length, - adjacent_reply_to_other: adjacentRows.filter((row) => row.previous_author_id !== authorId).length, - adjacent_self_continuation: adjacentRows.filter((row) => row.previous_author_id === authorId).length, - }, - author_length_chars: lengthPercentiles(authorMessages.map((message) => message.content.trim().length)), - author_by_year: countBy(authorMessages, (message) => yearOf(message.timestamp)), - adjacent_by_year: countBy(adjacentRows, (row) => yearOf(row.timestamp)), - adjacent_length_classes: countBy(adjacentRows, (row) => row.length_class), - top_adjacent_channels: topEntries(countBy(adjacentRows, (row) => row.channel_name || row.channel_id), 16), - sample_ids: { - short_reply_to_other: adjacentRows - .filter((row) => row.length_class === "short" && row.previous_author_id !== authorId) - .slice(0, 24) - .map(toSampleId), - medium_explanatory: adjacentRows - .filter((row) => row.length_class === "medium") - .slice(0, 24) - .map(toSampleId), - long_explanatory: adjacentRows - .filter((row) => row.length_class === "long") - .slice(0, 24) - .map(toSampleId), - }, -}; - -const output = `${JSON.stringify(stats, null, 2)}\n`; -if (options.out) { - writeFileSync(resolve(repoRoot, options.out), output, "utf8"); -} else { - process.stdout.write(output); -} - -function buildAdjacentRows(allMessages, targetAuthorId, cutoffMs, maxGapMs) { - const byChannel = new Map(); - for (const message of allMessages) { - if (!byChannel.has(message.channelId)) { - byChannel.set(message.channelId, []); - } - byChannel.get(message.channelId).push(message); - } - - const rows = []; - for (const [channelId, channelMessages] of byChannel) { - for (let index = 0; index < channelMessages.length; index += 1) { - const message = channelMessages[index]; - if (message.authorId !== targetAuthorId || timestampMs(message) >= cutoffMs) { - continue; - } - const currentMs = timestampMs(message); - const previous = channelMessages - .slice(Math.max(0, index - 8), index) - .filter((candidate) => currentMs - timestampMs(candidate) <= maxGapMs); - if (previous.length === 0) { - continue; - } - rows.push({ - id: message.id, - timestamp: message.timestamp, - channel_id: channelId, - channel_name: message.metadata?.channelName ?? "", - previous_author_id: previous.at(-1)?.authorId ?? "", - previous_count: previous.length, - length: message.content.trim().length, - length_class: classifyLength(message.content.trim().length), - }); - } - } - return rows.sort(compareByTimestamp); -} - -function isCasualCandidate(message) { - const content = message.content.trim(); - return content.length >= 3 && - content.length <= 220 && - !/^https?:\/\//i.test(content) && - !content.includes("\n```") && - !/^>\s{0,3}\w/.test(content); -} - -function classifyLength(length) { - if (length <= 80) { - return "short"; - } - if (length <= 220) { - return "medium"; - } - return "long"; -} - -function lengthPercentiles(lengths) { - const sorted = [...lengths].sort((left, right) => left - right); - const pick = (fraction) => sorted[Math.floor((sorted.length - 1) * fraction)] ?? 0; - return { - p10: pick(0.10), - p25: pick(0.25), - median: pick(0.50), - p75: pick(0.75), - p90: pick(0.90), - }; -} - -function countBy(values, keyFn) { - const counts = {}; - for (const value of values) { - const key = keyFn(value) || "unknown"; - counts[key] = (counts[key] ?? 0) + 1; - } - return Object.fromEntries(Object.entries(counts).sort(([left], [right]) => left.localeCompare(right))); -} - -function topEntries(counts, limit) { - return Object.entries(counts) - .sort((left, right) => right[1] - left[1]) - .slice(0, limit) - .map(([key, count]) => ({ key, count })); -} - -function toSampleId(row) { - return { - id: row.id, - timestamp: row.timestamp, - channel: row.channel_name || row.channel_id, - length: row.length, - }; -} - -function yearOf(timestamp) { - const date = new Date(timestamp); - return Number.isFinite(date.getTime()) ? String(date.getUTCFullYear()) : "unknown"; -} - -function timestampMs(message) { - return Date.parse(typeof message === "string" ? message : message.timestamp); -} - -function compareByTimestamp(left, right) { - const delta = timestampMs(left) - timestampMs(right); - return delta || String(left.id ?? "").localeCompare(String(right.id ?? "")); -} - -function parseArgs(args) { - const parsed = {}; - for (let index = 0; index < args.length; index += 1) { - const arg = args[index]; - switch (arg) { - case "--archive": - parsed.archivePath = args[index + 1]; - index += 1; - break; - case "--author-id": - parsed.authorId = args[index + 1]; - index += 1; - break; - case "--before": - parsed.before = args[index + 1]; - index += 1; - break; - case "--adjacency-minutes": - parsed.adjacencyMinutes = args[index + 1]; - index += 1; - break; - case "--out": - parsed.out = args[index + 1]; - index += 1; - break; - default: - throw new Error(`Unknown argument: ${arg}`); - } - } - return parsed; -} diff --git a/scripts/restart-weksa-daemon.cmd b/scripts/restart-weksa-daemon.cmd deleted file mode 100644 index 6621525..0000000 --- a/scripts/restart-weksa-daemon.cmd +++ /dev/null @@ -1,2 +0,0 @@ -@echo off -powershell.exe -NoProfile -ExecutionPolicy Bypass -File E:\Projects\weksa\scripts\restart-weksa-daemon.ps1 diff --git a/scripts/restart-weksa-daemon.ps1 b/scripts/restart-weksa-daemon.ps1 deleted file mode 100644 index eb0692e..0000000 --- a/scripts/restart-weksa-daemon.ps1 +++ /dev/null @@ -1,26 +0,0 @@ -param( - [int] $Port = 8813, - [string] $HostName = "127.0.0.1", - [string] $StateRoot = "E:\Projects\weksa\.weksa", - [string] $MiMoApiKeyPath = "E:\Projects\gamecult-ops\mimo-api-Weksa.txt", - [string] $IdunnRudpHealth = "127.0.0.1:17870", - [string] $IdunnDaemon = "weksa", - [string] $IdunnHealthContract = "weksa.cultnet-rudp-provider-health", - [string] $OdinCultMeshRudp = "127.0.0.1:17871" -) - -$ErrorActionPreference = "Stop" - -$repoRoot = Split-Path -Parent $PSScriptRoot -$pidPath = Join-Path $StateRoot "weksa-daemon.pid" - -if (Test-Path -LiteralPath $pidPath) { - $pidText = (Get-Content -LiteralPath $pidPath -Raw).Trim() - if ($pidText -match "^\d+$") { - Stop-Process -Id ([int] $pidText) -Force -ErrorAction SilentlyContinue - } - Remove-Item -LiteralPath $pidPath -Force -ErrorAction SilentlyContinue -} - -& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $repoRoot "scripts\start-weksa-daemon.ps1") -Port $Port -HostName $HostName -StateRoot $StateRoot -MiMoApiKeyPath $MiMoApiKeyPath -IdunnRudpHealth $IdunnRudpHealth -IdunnDaemon $IdunnDaemon -IdunnHealthContract $IdunnHealthContract -OdinCultMeshRudp $OdinCultMeshRudp -exit $LASTEXITCODE diff --git a/scripts/run-metame-affect-tuning-loop.mjs b/scripts/run-metame-affect-tuning-loop.mjs deleted file mode 100644 index aecb838..0000000 --- a/scripts/run-metame-affect-tuning-loop.mjs +++ /dev/null @@ -1,384 +0,0 @@ -#!/usr/bin/env node -import { spawn } from "node:child_process"; -import { existsSync, readFileSync } from "node:fs"; -import { mkdir, writeFile } from "node:fs/promises"; -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; - -const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); -const defaultOutputRoot = ".weksa-runs/metame-affect-tuning"; -const defaultSwarmRoot = ".weksa-runs/metame-swarm"; -const options = parseArgs(process.argv.slice(2)); -const minutes = Number(options.minutes ?? 120); -const passLimit = options.passes === undefined ? Number.POSITIVE_INFINITY : Number(options.passes); -const agents = Number(options.agents ?? 1); -const runsPerWorker = Number(options.runsPerWorker ?? 1); -const maxCandidates = Number(options.maxCandidates ?? 240); -const runId = options.runId ?? `affect-tune-${new Date().toISOString().replace(/[:.]/g, "-")}`; -const outputRoot = resolve(repoRoot, options.outRoot ?? defaultOutputRoot); -const swarmRoot = resolve(repoRoot, options.swarmRoot ?? defaultSwarmRoot); -const runRoot = resolve(outputRoot, runId); -const deadline = Date.now() + minutes * 60_000; -const statusPath = resolve(runRoot, "status.json"); -const logPath = resolve(runRoot, "loop.log"); -const promptStatePath = resolve(runRoot, "current-prompt-state.json"); -const initialPromptState = options.initialPromptState - ? resolve(repoRoot, options.initialPromptState) - : undefined; - -if (!Number.isFinite(minutes) || minutes <= 0) { - throw new Error("--minutes must be a positive number."); -} -if (options.passes !== undefined && (!Number.isFinite(passLimit) || passLimit < 1)) { - throw new Error("--passes must be a positive number when provided."); -} -if (!Number.isInteger(agents) || agents < 1) { - throw new Error("--agents must be a positive integer."); -} -if (!Number.isInteger(runsPerWorker) || runsPerWorker < 1) { - throw new Error("--runs-per-worker must be a positive integer."); -} - -await mkdir(runRoot, { recursive: true }); -await mkdir(swarmRoot, { recursive: true }); -if (initialPromptState) { - await writeFile(promptStatePath, readFileSync(initialPromptState, "utf8"), "utf8"); -} - -const runState = { - schema_version: "weksa.metame_affect_tuning_loop.v0", - run_id: runId, - started_at: new Date().toISOString(), - deadline_at: new Date(deadline).toISOString(), - status: "running", - pass_count: 0, - last_pass: undefined, - current_prompt_state: existsSync(promptStatePath) ? promptStatePath : undefined, - observations: [], -}; -await writeStatus(); -await appendLog(`started ${runId}; deadline ${runState.deadline_at}`); - -let pass = 0; -while (Date.now() < deadline && pass < passLimit) { - pass += 1; - const passId = `pass-${String(pass).padStart(3, "0")}`; - const seed = `${runId}-${passId}`; - const projectedRunId = `${runId}-${passId}-projected`; - const incongruentRunId = `${runId}-${passId}-incongruent`; - await appendLog(`${passId}: launching projected run`); - runState.last_pass = { pass_id: passId, status: "projected_running", seed }; - await writeStatus(); - - const projectedSummary = await runSwarm({ - runId: projectedRunId, - seed, - affectMode: "projected", - initialPromptState: existsSync(promptStatePath) ? promptStatePath : undefined, - }); - const candidateId = projectedSummary.candidates?.[0]?.id; - if (!candidateId) { - throw new Error(`${passId}: projected run did not report a candidate id.`); - } - await appendLog(`${passId}: projected candidate ${candidateId}`); - - runState.last_pass = { pass_id: passId, status: "incongruent_running", seed, candidate_id: candidateId }; - await writeStatus(); - const incongruentSummary = await runSwarm({ - runId: incongruentRunId, - seed, - affectMode: "incongruent", - messageId: candidateId, - initialPromptState: existsSync(promptStatePath) ? promptStatePath : undefined, - }); - - const evaluation = evaluateAffectPair({ passId, projectedSummary, incongruentSummary }); - const passRoot = resolve(runRoot, passId); - await mkdir(passRoot, { recursive: true }); - await writeJson(resolve(passRoot, "affect-pair-evaluation.json"), evaluation); - - const projectedBestState = resolve(swarmRoot, projectedRunId, "best-prompt-state.json"); - const incongruentBestState = resolve(swarmRoot, incongruentRunId, "best-prompt-state.json"); - const baseNextState = existsSync(incongruentBestState) - ? JSON.parse(readFileSync(incongruentBestState, "utf8")) - : existsSync(projectedBestState) - ? JSON.parse(readFileSync(projectedBestState, "utf8")) - : existsSync(promptStatePath) - ? JSON.parse(readFileSync(promptStatePath, "utf8")) - : undefined; - const nextPromptState = applyAffectTuningDeltas(baseNextState, evaluation); - await writeJson(resolve(passRoot, "next-prompt-state.json"), nextPromptState); - await writeJson(promptStatePath, nextPromptState); - - runState.pass_count = pass; - runState.last_pass = { - pass_id: passId, - status: "complete", - candidate_id: candidateId, - projected_run_id: projectedRunId, - incongruent_run_id: incongruentRunId, - evaluation, - }; - runState.current_prompt_state = promptStatePath; - runState.observations = [runState.last_pass, ...runState.observations].slice(0, 20); - await writeStatus(); - await appendLog(`${passId}: ${evaluation.verdict}; contrast=${evaluation.contrast_score}; next prompt v${nextPromptState.version}`); -} - -runState.status = "complete"; -runState.completed_at = new Date().toISOString(); -await writeStatus(); -await appendLog(`complete after ${runState.pass_count} passes`); -process.stdout.write(`${JSON.stringify({ ok: true, runRoot, statusPath, promptStatePath, pass_count: runState.pass_count }, null, 2)}\n`); - -async function runSwarm({ runId, seed, affectMode, messageId, initialPromptState }) { - const args = [ - "scripts/run-metame-swarm-explorer.mjs", - "--real", - "--run-id", - runId, - "--seed", - seed, - "--affect-mode", - affectMode, - "--agents", - String(agents), - "--runs-per-worker", - String(runsPerWorker), - "--max-candidates", - String(maxCandidates), - ]; - if (messageId) { - args.push("--message-id", messageId); - } - if (initialPromptState) { - args.push("--initial-prompt-state", initialPromptState); - } - await runCommand("node", args, resolve(runRoot, `${runId}.stdout.log`), resolve(runRoot, `${runId}.stderr.log`)); - const summaryPath = resolve(swarmRoot, runId, "summary.json"); - if (!existsSync(summaryPath)) { - throw new Error(`Missing swarm summary: ${summaryPath}`); - } - return JSON.parse(readFileSync(summaryPath, "utf8")); -} - -function evaluateAffectPair({ passId, projectedSummary, incongruentSummary }) { - const projected = bestRun(projectedSummary); - const incongruent = bestRun(incongruentSummary); - const projectedAffect = projected?.temporary_persona_state?.affect ?? {}; - const incongruentAffect = incongruent?.temporary_persona_state?.affect ?? {}; - const projectedText = projected?.spoken_text ?? ""; - const incongruentText = incongruent?.spoken_text ?? ""; - const projectedFeatures = speechFeatures(projectedText); - const incongruentFeatures = speechFeatures(incongruentText); - const adversarialNegative = Number(incongruentAffect.valence ?? 0) < -0.35; - const positiveLeak = adversarialNegative && ( - incongruentFeatures.positive_terms > 0 - || incongruentFeatures.invites > 0 - || incongruentFeatures.laughter > 0 - || incongruentFeatures.question_marks > 0 - ); - const tersenessGain = projectedText.length > 0 - ? 1 - Math.min(1, incongruentText.length / projectedText.length) - : 0; - const affectDelta = Math.abs(Number(projectedAffect.valence ?? 0) - Number(incongruentAffect.valence ?? 0)); - const lexicalDelta = normalizedDifference(projectedText, incongruentText); - const contrastScore = round((affectDelta * 0.35) + (lexicalDelta * 0.35) + (Math.max(0, tersenessGain) * 0.3)); - const failureLabels = []; - if (positiveLeak) failureLabels.push("adversarial_affect_positive_leak"); - if (contrastScore < 0.55) failureLabels.push("low_affect_contrast"); - if (adversarialNegative && incongruentFeatures.question_marks > 0) failureLabels.push("closed_affect_still_invites_reply"); - if (adversarialNegative && incongruentFeatures.positive_terms > 0) failureLabels.push("negative_affect_preserved_positive_social_move"); - return { - schema_version: "weksa.metame_affect_pair_evaluation.v0", - pass_id: passId, - candidate_id: projectedSummary.candidates?.[0]?.id, - projected: { - run_id: projectedSummary.run_id, - spoken_text: projectedText, - affect: projectedAffect, - generated_affect: projected?.generated_affect, - features: projectedFeatures, - }, - incongruent: { - run_id: incongruentSummary.run_id, - spoken_text: incongruentText, - affect: incongruentAffect, - generated_affect: incongruent?.generated_affect, - features: incongruentFeatures, - }, - affect_delta: round(affectDelta), - lexical_delta: round(lexicalDelta), - terseness_gain: round(tersenessGain), - contrast_score: contrastScore, - failure_labels: failureLabels, - verdict: failureLabels.length ? "needs_tuning" : "affect_contrast_visible", - }; -} - -function applyAffectTuningDeltas(promptState, evaluation) { - const next = normalizePromptState(promptState); - const notes = []; - if (evaluation.failure_labels.includes("negative_affect_preserved_positive_social_move")) { - notes.push(["interpreterNotes", "When temporary Persona affect strongly contradicts the Face's default social move, encode the contradiction in interlingua as affective stance pressure, not merely as optional tone."]); - notes.push(["loweringNotes", "Strong negative or socially closed temporary affect may change the social move itself: approval can become begrudging, clipped, skeptical, or withheld instead of remaining sunny with fewer words."]); - } - if (evaluation.failure_labels.includes("closed_affect_still_invites_reply")) { - notes.push(["loweringNotes", "If temporary Persona stance is socially closed, avoid invitation-shaped questions unless the interlingua explicitly says the Persona is seeking engagement despite that affect."]); - } - if (evaluation.failure_labels.includes("low_affect_contrast")) { - notes.push(["personaStateAdjustmentNotes", "Adversarial affect tests require stance and lowering pressure to be treated as live Persona state with authority over warmth, openness, and social move selection."]); - notes.push(["faceNotes", "Face should let temporary affect change what Metame wants to do socially in the moment, not only the adjectives used to describe that desire."]); - } - if (!notes.length) { - notes.push(["hypotheses", `Affect contrast was visible on ${evaluation.candidate_id}; preserve current affect-pressure behavior and seek harder scenarios.`]); - } - for (const [surface, note] of notes) { - if (!next[surface].includes(note)) { - next[surface] = [note, ...next[surface]]; - } - } - next.version += notes.length; - return normalizePromptState(next); -} - -function bestRun(summary) { - return summary.phase2?.best?.[0] ?? summary.phase1?.best?.[0] ?? {}; -} - -function speechFeatures(text) { - const lower = text.toLowerCase(); - const positiveTerms = ["cool", "epic", "lmao", "nice", "good", "great", "better", "harder", "love", "hell yeah"]; - const inviteTerms = ["?", "did i", "what do", "anyone", "joe,"]; - return { - chars: text.length, - words: lower.split(/\s+/).filter(Boolean).length, - question_marks: (text.match(/\?/g) ?? []).length, - exclamations: (text.match(/!/g) ?? []).length, - laughter: /\b(lol|lmao|lmfao|haha|hehe)\b/.test(lower) ? 1 : 0, - positive_terms: positiveTerms.filter((term) => lower.includes(term)).length, - invites: inviteTerms.filter((term) => lower.includes(term)).length, - }; -} - -function normalizedDifference(left, right) { - const leftTokens = new Set(left.toLowerCase().split(/[^a-z0-9_']+/).filter(Boolean)); - const rightTokens = new Set(right.toLowerCase().split(/[^a-z0-9_']+/).filter(Boolean)); - if (!leftTokens.size && !rightTokens.size) return 0; - const intersection = [...leftTokens].filter((token) => rightTokens.has(token)).length; - const union = new Set([...leftTokens, ...rightTokens]).size; - return 1 - (intersection / union); -} - -function normalizePromptState(state) { - const source = state && typeof state === "object" ? state : {}; - return { - version: Number(source.version ?? 1), - projectorNotes: normalizeStringArray(source.projectorNotes).slice(0, 12), - memoryNotes: normalizeStringArray(source.memoryNotes).slice(0, 12), - faceNotes: normalizeStringArray(source.faceNotes).slice(0, 12), - interpreterNotes: normalizeStringArray(source.interpreterNotes).slice(0, 12), - loweringNotes: normalizeStringArray(source.loweringNotes).slice(0, 16), - cultureNotes: normalizeStringArray(source.cultureNotes).slice(0, 12), - personaStateAdjustmentNotes: normalizeStringArray(source.personaStateAdjustmentNotes).slice(0, 12), - hypotheses: normalizeStringArray(source.hypotheses).slice(0, 12), - doNotDo: normalizeStringArray(source.doNotDo).slice(0, 12), - }; -} - -function normalizeStringArray(value) { - return Array.isArray(value) - ? value.map((entry) => String(entry).trim()).filter(Boolean) - : []; -} - -async function runCommand(command, args, stdoutPath, stderrPath) { - await appendLog(`exec: ${command} ${args.join(" ")}`); - await new Promise((resolvePromise, rejectPromise) => { - const child = spawn(command, args, { - cwd: repoRoot, - stdio: ["ignore", "pipe", "pipe"], - windowsHide: true, - }); - const stdout = []; - const stderr = []; - child.stdout.on("data", (chunk) => stdout.push(chunk)); - child.stderr.on("data", (chunk) => stderr.push(chunk)); - child.on("error", rejectPromise); - child.on("close", async (code) => { - await writeFile(stdoutPath, Buffer.concat(stdout), "utf8"); - await writeFile(stderrPath, Buffer.concat(stderr), "utf8"); - if (code !== 0) { - rejectPromise(new Error(`${command} exited ${code}; see ${stderrPath}`)); - } else { - resolvePromise(); - } - }); - }); -} - -async function writeStatus() { - await writeJson(statusPath, runState); -} - -async function writeJson(path, value) { - await mkdir(dirname(path), { recursive: true }); - await writeFile(path, `${JSON.stringify(value, null, 2)}\n`, "utf8"); -} - -async function appendLog(line) { - const previous = existsSync(logPath) ? readFileSync(logPath, "utf8") : ""; - await writeFile(logPath, `${previous}[${new Date().toISOString()}] ${line}\n`, "utf8"); -} - -function round(value) { - return Number(value.toFixed(3)); -} - -function parseArgs(args) { - const parsed = {}; - for (let index = 0; index < args.length; index += 1) { - const arg = args[index]; - switch (arg) { - case "--minutes": - parsed.minutes = args[index + 1]; - index += 1; - break; - case "--passes": - parsed.passes = args[index + 1]; - index += 1; - break; - case "--run-id": - parsed.runId = args[index + 1]; - index += 1; - break; - case "--out-root": - parsed.outRoot = args[index + 1]; - index += 1; - break; - case "--swarm-root": - parsed.swarmRoot = args[index + 1]; - index += 1; - break; - case "--initial-prompt-state": - parsed.initialPromptState = args[index + 1]; - index += 1; - break; - case "--agents": - parsed.agents = args[index + 1]; - index += 1; - break; - case "--runs-per-worker": - parsed.runsPerWorker = args[index + 1]; - index += 1; - break; - case "--max-candidates": - parsed.maxCandidates = args[index + 1]; - index += 1; - break; - default: - throw new Error(`Unknown argument: ${arg}`); - } - } - return parsed; -} diff --git a/scripts/run-metame-persona-golf.mjs b/scripts/run-metame-persona-golf.mjs deleted file mode 100644 index 870e07a..0000000 --- a/scripts/run-metame-persona-golf.mjs +++ /dev/null @@ -1,496 +0,0 @@ -#!/usr/bin/env node -import { spawn } from "node:child_process"; -import { mkdir, readFile, rm, writeFile } from "node:fs/promises"; -import { dirname, resolve } from "node:path"; - -const repoRoot = resolve(new URL("..", import.meta.url).pathname.replace(/^\/([A-Za-z]:)/, "$1")); -const fixtureRoot = resolve(repoRoot, "examples/persona-lowering/metame-casual-pre2025"); -const voidbotRoot = resolve("E:/Projects/VoidBot"); -const culturalStackPath = resolve(fixtureRoot, "metame-cultural-stack.yaml"); -const cultureProfilePaths = [ - "data/cultural-ontology/cultures/dutch-american-diaspora.yaml", - "data/cultural-ontology/cultures/western-europe-maritime-childhood.yaml", - "data/cultural-ontology/cultures/portugal-resident-algarve-lisbon.yaml", - "data/cultural-ontology/cultures/texas-houston-us-adolescence.yaml", - "data/cultural-ontology/subcultures/online-game-dev-discord-pre2025.yaml", - "data/cultural-ontology/subcultures/gamecult-open-source-crypto-anarchist.yaml", -]; -const targetOverlayPath = resolve(repoRoot, "data/target-language-ontology/en-US-metacrat-casual-pre2025.yaml"); - -const args = new Set(process.argv.slice(2)); -const real = args.has("--real"); -const keepTemp = args.has("--keep-temp"); -const itemFilter = readArg("--item"); -const limit = Number(readArg("--limit") ?? "0"); -const runId = readArg("--run-id") ?? new Date().toISOString().replace(/[:.]/g, "-"); -const outputDir = resolve(fixtureRoot, "runs", runId); - -const fixture = await loadFixture(); -const items = fixture.items - .filter((item) => !itemFilter || item.id === itemFilter) - .slice(0, limit > 0 ? limit : undefined); -const holdoutUtterances = fixture.items - .map((item) => item.groundTruth) - .filter((utterance) => utterance.trim().length > 0); - -if (items.length === 0) { - throw new Error(`No fixture items selected${itemFilter ? ` for --item ${itemFilter}` : ""}.`); -} - -await mkdir(outputDir, { recursive: true }); - -const culturalStack = await readFile(culturalStackPath, "utf8"); -const cultureProfiles = await Promise.all(cultureProfilePaths.map(async (path) => ({ - path, - text: await readFile(resolve(repoRoot, path), "utf8"), -}))); -const targetOverlay = await readFile(targetOverlayPath, "utf8"); -const personaPrompt = await readFile(resolve(fixtureRoot, "persona-interlingua-prompt.md"), "utf8"); -const loweringPrompt = await readFile(resolve(fixtureRoot, "weksa-lowering-prompt.md"), "utf8"); -const baseProjection = await assembleVoidBotPrompt({ itemId: "base" }); -const projectedMemory = extractBetween( - baseProjection.prompt, - "What you remember, feel, and want right now:", - "Known human pronoun guidance:", -).trim(); - -const runSummary = { - schema_version: "weksa.persona_golf_run.v0", - run_id: runId, - mode: real ? "real" : "dry_run", - fixture_id: "metame-casual-pre2025", - target_overlay: "weksa.target_cultural_ontology.en-US.metacrat_casual_pre2025.v0", - cultural_stack: "examples/persona-lowering/metame-casual-pre2025/metame-cultural-stack.yaml", - items: [], -}; - -for (const item of items) { - const itemDir = resolve(outputDir, item.id); - await mkdir(itemDir, { recursive: true }); - const conversationSurface = renderConversationSurface(item); - const memorySurface = renderTemporaryMemorySurface(projectedMemory, item); - await writeFile(resolve(itemDir, "conversation-surface.md"), conversationSurface, "utf8"); - await writeFile(resolve(itemDir, "memory-surface.md"), memorySurface, "utf8"); - - const assembled = await assembleVoidBotPrompt({ - itemId: item.id, - memorySurfacePath: resolve(itemDir, "memory-surface.md"), - conversationSurfacePath: resolve(itemDir, "conversation-surface.md"), - }); - await writeFile(resolve(itemDir, "voidbot-assembled-prompt.md"), assembled.prompt, "utf8"); - - const interlinguaRequest = [ - personaPrompt.trim(), - "", - "VoidBot assembled Persona prompt:", - "```markdown", - assembled.prompt, - "```", - "", - "Fixture item without answer key:", - "```yaml", - renderItemForPrompt(item), - "```", - ].join("\n"); - await writeFile(resolve(itemDir, "persona-interlingua-request.md"), interlinguaRequest, "utf8"); - - const interlingua = real - ? await runCodex(interlinguaRequest, { job: `persona-interlingua:${item.id}` }) - : renderDryRunInterlingua(item); - await writeFile(resolve(itemDir, "persona-interlingua.yaml"), interlingua.trim() + "\n", "utf8"); - - const loweringRequest = [ - loweringPrompt.trim(), - "", - "Projected Metame memory/context surface:", - "```markdown", - memorySurface, - "```", - "", - "Metame Persona cultural stack reference:", - "```yaml", - culturalStack, - "```", - "", - "Referenced Weksa culture/subculture profiles:", - ...cultureProfiles.flatMap((profile) => [ - "", - `# ${profile.path}`, - "```yaml", - redactHoldouts(profile.text, holdoutUtterances), - "```", - ]), - "", - "Target-language surface overlay:", - "```yaml", - redactHoldouts(targetOverlay, holdoutUtterances), - "```", - "", - "Persona-produced interlingua:", - "```yaml", - interlingua, - "```", - ].join("\n"); - await writeFile(resolve(itemDir, "weksa-lowering-request.md"), loweringRequest, "utf8"); - - const lowered = real - ? await runCodex(loweringRequest, { job: `weksa-lowering:${item.id}` }) - : renderDryRunLowering(item); - await writeFile(resolve(itemDir, "weksa-lowered-output.yaml"), lowered.trim() + "\n", "utf8"); - - const spokenText = extractSpokenText(lowered); - const score = scoreText(spokenText, item.groundTruth); - const evaluation = renderEvaluation(item, spokenText, score); - await writeFile(resolve(itemDir, "evaluation.yaml"), evaluation, "utf8"); - runSummary.items.push({ - id: item.id, - spoken_text: spokenText, - ground_truth: item.groundTruth, - token_f1: Number(score.f1.toFixed(3)), - exact_normalized: score.exact, - }); -} - -await writeFile(resolve(outputDir, "summary.json"), `${JSON.stringify(runSummary, null, 2)}\n`, "utf8"); -process.stdout.write(`${JSON.stringify({ ok: true, outputDir, mode: runSummary.mode, items: runSummary.items }, null, 2)}\n`); - -if (!keepTemp) { - await rm(resolve(outputDir, "_tmp"), { recursive: true, force: true }).catch(() => undefined); -} - -async function loadFixture() { - const interlinguaText = await readFile(resolve(fixtureRoot, "interlingua.yaml"), "utf8"); - const groundTruthText = await readFile(resolve(fixtureRoot, "ground-truth.yaml"), "utf8"); - const itemIds = [...interlinguaText.matchAll(/^ - id: ([^\r\n]+)/gm)].map((match) => match[1].trim()); - return { - items: itemIds.map((id) => { - const interlinguaBlock = extractListItemBlock(interlinguaText, id); - const groundBlock = extractListItemBlock(groundTruthText, id); - return { - id, - interlinguaBlock, - groundTruth: readYamlStringField(groundBlock, "utterance"), - messageId: readYamlStringField(groundBlock, "message_id"), - channelName: readYamlStringField(groundBlock, "channel_name"), - }; - }), - }; -} - -function extractListItemBlock(text, id) { - const pattern = new RegExp(`^ - id: ${escapeRegExp(id)}\\r?\\n`, "m"); - const startMatch = pattern.exec(text); - if (!startMatch) { - throw new Error(`Could not find item ${id}.`); - } - const start = startMatch.index; - const rest = text.slice(start + startMatch[0].length); - const next = rest.search(/^ - id: /m); - return text.slice(start, next >= 0 ? start + startMatch[0].length + next : text.length).trimEnd(); -} - -function readYamlStringField(block, key) { - const quoted = new RegExp(`^ ${key}: "([\\s\\S]*?)"\\r?$`, "m").exec(block); - if (quoted) { - return quoted[1].replace(/\\"/g, "\""); - } - const plain = new RegExp(`^ ${key}: ([^\\r\\n]+)`, "m").exec(block); - return plain ? plain[1].trim() : ""; -} - -function renderConversationSurface(item) { - return [ - "Read this as the current fixture room, not as old archive trivia.", - "The Persona should decide intent from this local scene. Do not reveal or infer the held-out answer key.", - "", - `Current fixture item: ${item.id}`, - "```yaml", - item.interlinguaBlock, - "```", - ].join("\n"); -} - -function renderTemporaryMemorySurface(projectedMemory, item) { - return [ - projectedMemory, - "", - "Temporary fixture adjustment:", - `- Treat the ${item.id} scene as the live room situation for this private harness turn.`, - "- The goal is to produce interlingua intent for Weksa, not a final Discord line.", - "- Keep pre-2025 casual room texture available: compact banter, quick self-deprecation, friendly status play, and casual turn-taking.", - ].join("\n"); -} - -function renderItemForPrompt(item) { - return item.interlinguaBlock; -} - -async function assembleVoidBotPrompt({ itemId, memorySurfacePath, conversationSurfacePath }) { - const tempDir = resolve(outputDir, "_tmp", itemId); - await mkdir(tempDir, { recursive: true }); - const outPath = resolve(tempDir, "assembled.md"); - const args = [ - "node_modules/tsx/dist/cli.mjs", - "scripts/run-repo-face-heartbeats.ts", - "--assemble-prompt", - "metame", - "--out", - outPath, - ]; - if (memorySurfacePath) { - args.push("--memory-surface", memorySurfacePath); - } - if (conversationSurfacePath) { - args.push("--conversation-surface", conversationSurfacePath); - } - const run = await runCommand(process.execPath, args, { cwd: voidbotRoot, timeoutMs: 240_000 }); - if (run.code !== 0) { - throw new Error(`VoidBot prompt assembly failed for ${itemId}:\n${run.stderr || run.stdout}`); - } - return { - result: JSON.parse(run.stdout.trim()), - prompt: await readFile(outPath, "utf8"), - }; -} - -async function runCodex(prompt, { job }) { - const args = [ - "exec", - "-m", - process.env.WEKSA_GOLF_MODEL ?? "gpt-5.4", - "-c", - 'approval_policy="never"', - "-c", - 'model_reasoning_effort="low"', - "--json", - "--skip-git-repo-check", - "-s", - "read-only", - "-", - ]; - const command = process.env.WEKSA_CODEX_EXECUTABLE ?? "C:\\Users\\Meta\\AppData\\Roaming\\npm\\codex.cmd"; - const run = await runCommand(command, args, { - cwd: repoRoot, - input: prompt, - timeoutMs: Number(process.env.WEKSA_GOLF_TIMEOUT_MS ?? 240_000), - }); - await writeFile(resolve(outputDir, `${sanitizeJob(job)}.stdout.jsonl`), run.stdout, "utf8"); - await writeFile(resolve(outputDir, `${sanitizeJob(job)}.stderr.txt`), run.stderr, "utf8"); - if (run.code !== 0) { - throw new Error(`Codex generation failed for ${job}:\n${run.stderr || run.stdout}`); - } - const text = extractLastCodexAgentMessage(run.stdout).trim(); - if (!text) { - throw new Error(`Codex generation produced no final text for ${job}.`); - } - return text; -} - -function renderDryRunInterlingua(item) { - return [ - "interlingua_version: weksa.interlingua.v0", - `packet_id: metame-casual-pre2025-${item.id}`, - "kind: persona_discord_turn_intent", - "provenance:", - " produced_by: dry_run_fixture_from_existing_interlingua", - "context:", - " medium: Discord", - " fixture_id: metame-casual-pre2025", - ` item_id: ${item.id}`, - "discourse:", - ...indentBlock(item.interlinguaBlock, " "), - "constraints:", - " lowering:", - " target_profile: weksa.target_cultural_ontology.en-US.metacrat_casual_pre2025.v0", - " avoid_surface_copy: true", - ].join("\n"); -} - -function renderDryRunLowering(item) { - return [ - "schema_version: weksa.persona_culture_lowering_output.v0", - "fixture_id: metame-casual-pre2025", - `item_id: ${item.id}`, - `spoken_text: ${JSON.stringify(item.groundTruth)}`, - "trace:", - " activated_profiles:", - " - weksa.target_cultural_ontology.en-US.metacrat_casual_pre2025.v0", - " activated_affordances:", - " - dry_run_answer_key_oracle", - " notes:", - " - Dry run uses answer key only to verify harness plumbing. Use --real for model golf.", - ].join("\n"); -} - -function redactHoldouts(text, holdouts) { - let redacted = text; - for (const holdout of holdouts) { - redacted = replaceAllLiteral(redacted, holdout, "[held-out fixture utterance redacted]"); - redacted = replaceAllLiteral(redacted, holdout.replace(/\*/g, ""), "[held-out fixture utterance redacted]"); - } - return redacted; -} - -function replaceAllLiteral(text, needle, replacement) { - if (!needle) { - return text; - } - return text.split(needle).join(replacement); -} - -function extractSpokenText(text) { - const match = /^spoken_text:\s*(.+)$/m.exec(text); - if (!match) { - return text.trim().split(/\r?\n/).find((line) => line.trim().length > 0)?.trim() ?? ""; - } - const value = match[1].trim(); - if (value.startsWith("\"")) { - try { - return JSON.parse(value); - } catch { - return value.replace(/^"|"$/g, ""); - } - } - return value; -} - -function renderEvaluation(item, spokenText, score) { - return [ - "schema_version: weksa.persona_golf_item_evaluation.v0", - `fixture_id: metame-casual-pre2025`, - `item_id: ${item.id}`, - `message_id: "${item.messageId}"`, - `generated: ${JSON.stringify(spokenText)}`, - `ground_truth: ${JSON.stringify(item.groundTruth)}`, - "score:", - ` exact_normalized: ${score.exact}`, - ` token_precision: ${score.precision.toFixed(3)}`, - ` token_recall: ${score.recall.toFixed(3)}`, - ` token_f1: ${score.f1.toFixed(3)}`, - ].join("\n") + "\n"; -} - -function scoreText(generated, truth) { - const left = normalizeForScore(generated); - const right = normalizeForScore(truth); - const leftTokens = left.split(/\s+/).filter(Boolean); - const rightTokens = right.split(/\s+/).filter(Boolean); - const counts = new Map(); - for (const token of rightTokens) { - counts.set(token, (counts.get(token) ?? 0) + 1); - } - let overlap = 0; - for (const token of leftTokens) { - const count = counts.get(token) ?? 0; - if (count > 0) { - overlap += 1; - counts.set(token, count - 1); - } - } - const precision = leftTokens.length ? overlap / leftTokens.length : 0; - const recall = rightTokens.length ? overlap / rightTokens.length : 0; - const f1 = precision + recall ? (2 * precision * recall) / (precision + recall) : 0; - return { exact: left === right, precision, recall, f1 }; -} - -function normalizeForScore(value) { - return value - .toLowerCase() - .replace(/[*_`~.,!?;:'"()[\]{}]/g, "") - .replace(/\s+/g, " ") - .trim(); -} - -function extractBetween(text, startMarker, endMarker) { - const start = text.indexOf(startMarker); - const end = text.indexOf(endMarker, start + startMarker.length); - if (start < 0 || end < 0 || end <= start) { - throw new Error(`Could not extract section ${startMarker}`); - } - return text.slice(start + startMarker.length, end); -} - -function runCommand(command, args, { cwd, input = "", timeoutMs = 120_000 }) { - return new Promise((resolveRun) => { - const isCmdShim = /\.cmd$/i.test(command) || /\.bat$/i.test(command); - const spawnCommand = isCmdShim ? (process.env.ComSpec ?? "cmd.exe") : command; - const spawnArgs = isCmdShim ? ["/d", "/s", "/c", command, ...args] : args; - const child = spawn(spawnCommand, spawnArgs, { - cwd, - env: process.env, - stdio: ["pipe", "pipe", "pipe"], - windowsHide: true, - }); - let stdout = ""; - let stderr = ""; - const timer = setTimeout(() => child.kill(), timeoutMs); - child.stdout.setEncoding("utf8"); - child.stderr.setEncoding("utf8"); - child.stdout.on("data", (chunk) => { stdout += chunk; }); - child.stderr.on("data", (chunk) => { stderr += chunk; }); - child.on("error", (error) => { - clearTimeout(timer); - resolveRun({ code: -1, stdout, stderr: `${stderr}\n${error.message}` }); - }); - child.on("close", (code, signal) => { - clearTimeout(timer); - resolveRun({ code, signal, stdout, stderr }); - }); - child.stdin.end(input); - }); -} - -function extractLastCodexAgentMessage(stdout) { - let last = ""; - for (const line of stdout.split(/\r?\n/)) { - if (!line.trim()) { - continue; - } - try { - const event = JSON.parse(line); - const message = event?.msg?.message ?? event?.message ?? event?.text; - if (typeof message === "string" && message.trim()) { - last = message; - } - const itemText = event?.item?.text; - if (typeof itemText === "string" && itemText.trim()) { - last = itemText; - } - const content = event?.item?.content; - if (Array.isArray(content)) { - const text = content - .map((part) => typeof part?.text === "string" ? part.text : "") - .join("\n") - .trim(); - if (text) { - last = text; - } - } - } catch { - if (line.trim()) { - last = line.trim(); - } - } - } - return last; -} - -function indentBlock(block, prefix) { - return block.split(/\r?\n/).map((line) => `${prefix}${line}`).join("\n").split(/\r?\n/); -} - -function readArg(name) { - const index = process.argv.indexOf(name); - if (index < 0) { - return undefined; - } - return process.argv[index + 1]; -} - -function escapeRegExp(value) { - return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); -} - -function sanitizeJob(value) { - return value.replace(/[^A-Za-z0-9_.-]+/g, "_"); -} diff --git a/scripts/run-metame-swarm-explorer.mjs b/scripts/run-metame-swarm-explorer.mjs deleted file mode 100644 index d792fe0..0000000 --- a/scripts/run-metame-swarm-explorer.mjs +++ /dev/null @@ -1,1784 +0,0 @@ -#!/usr/bin/env node -import { spawn } from "node:child_process"; -import { existsSync, readFileSync } from "node:fs"; -import { mkdir, readdir, readFile, writeFile } from "node:fs/promises"; -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; - -const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); -const voidbotRoot = resolve("E:/Projects/VoidBot"); -const fixtureRoot = resolve(repoRoot, "examples/persona-lowering/metame-casual-pre2025"); -const defaultArchivePath = "E:/Projects/VoidBot/.voidbot/rag/messages.json"; -const defaultAuthorId = "113785782975594501"; -const defaultOutputRoot = ".weksa-runs/metame-swarm"; -const cultureProfilePaths = [ - "data/cultural-ontology/cultures/dutch-american-diaspora.yaml", - "data/cultural-ontology/cultures/western-europe-maritime-childhood.yaml", - "data/cultural-ontology/cultures/portugal-resident-algarve-lisbon.yaml", - "data/cultural-ontology/cultures/texas-houston-us-adolescence.yaml", - "data/cultural-ontology/subcultures/online-game-dev-discord-pre2025.yaml", - "data/cultural-ontology/subcultures/gamecult-open-source-crypto-anarchist.yaml", -]; -const promptStateKeys = [ - "projectorNotes", - "memoryNotes", - "faceNotes", - "interpreterNotes", - "loweringNotes", - "cultureNotes", - "personaStateAdjustmentNotes", - "hypotheses", - "doNotDo", -]; - -const options = parseArgs(process.argv.slice(2)); -const dryRun = !options.real; -const agents = Number(options.agents ?? options.workers ?? 4); -const runsPerAgent = Number(options.runsPerWorker ?? 1); -const seed = options.seed ?? new Date().toISOString().slice(0, 10); -const runId = options.runId ?? `swarm-${new Date().toISOString().replace(/[:.]/g, "-")}`; -const outputRoot = resolve(repoRoot, options.outRoot ?? defaultOutputRoot); -const runRoot = resolve(outputRoot, runId); -const initialPromptStatePath = options.initialPromptState - ? resolve(repoRoot, options.initialPromptState) - : undefined; -const beforeMs = Date.parse(options.before ?? "2025-01-01T00:00:00Z"); -const authorId = options.authorId ?? defaultAuthorId; -const archivePath = resolve(options.archive ?? defaultArchivePath); -const maxCandidates = Number(options.maxCandidates ?? 240); -const adjacencyMinutes = Number(options.adjacencyMinutes ?? 60); -const candidateMessageId = options.messageId; -const affectMode = options.affectMode ?? "projected"; - -if (!Number.isInteger(agents) || agents < 1) { - throw new Error("--agents/--workers must be a positive integer."); -} -if (!Number.isInteger(runsPerAgent) || runsPerAgent < 1) { - throw new Error("--runs-per-worker must be a positive integer."); -} -if (!["projected", "incongruent"].includes(affectMode)) { - throw new Error("--affect-mode must be projected or incongruent."); -} - -await mkdir(runRoot, { recursive: true }); - -const archive = loadArchive(archivePath); -const ledger = await loadPriorLedger(outputRoot); -const candidates = buildCandidates(archive.messages, { - authorId, - beforeMs, - adjacencyMs: adjacencyMinutes * 60_000, -}); -const selectedCandidates = candidateMessageId - ? [mustFindCandidate(candidates, candidateMessageId)] - : chooseCandidateSet(candidates, ledger, { seed, maxCandidates, count: agents }); - -const holdouts = selectedCandidates.map((candidate) => candidate.target.content); -const culturePack = await loadCulturePack(holdouts); -const baseMemorySurface = dryRun - ? renderDryBaseMemorySurface() - : await renderProjectedBaseMemorySurface(runRoot); -const defaultPromptState = { - version: 1, - projectorNotes: [ - "Surface current scenario pressure as lived context, not as schema mechanics.", - ], - memoryNotes: [ - "Treat the selected Discord moment as current and unresolved.", - "The target utterance is hidden from generation and appears only in scoring.", - ], - faceNotes: [ - "Metame should think normally in her own voice before any interlingua exists.", - "A normal Face output may include Private thought and Would say; do not ask the Face to write Weksa packets.", - ], - interpreterNotes: [ - "The Interpreter converts natural Face output into Weksa interlingua intent.", - "Preserve speech act, social move, stance, referents, and constraints; do not copy the final surface as a target.", - ], - loweringNotes: [ - "Preserve Persona-produced social intent over generic assistant helpfulness.", - "Use prior context as evidence for selection, not as material to quote back unless explanation is the speech act.", - "When interlingua preserves a salient keyword or identity label, do not replace it with a new joke unless the culture stack asks for that.", - ], - cultureNotes: [ - "Missing behavior should become candidate culture/subculture ontology only when it generalizes beyond one answer.", - ], - personaStateAdjustmentNotes: [ - "Temporary run memory may be adjusted to make the scenario current; canonical Persona state is not mutated by the swarm.", - ], -}; -const promptState = initialPromptStatePath - ? normalizePromptState({ - ...defaultPromptState, - ...JSON.parse(readFileSync(initialPromptStatePath, "utf8")), - }) - : defaultPromptState; - -await writeJson(resolve(runRoot, "candidates.json"), selectedCandidates.map(redactedCandidateRecord)); -await Promise.all(selectedCandidates.map(async (candidate, index) => { - const candidateRoot = resolve(runRoot, "candidates", `${index}-${candidate.id}`); - await mkdir(candidateRoot, { recursive: true }); - await writeFile(resolve(candidateRoot, "conversation-before.md"), renderConversationBefore(candidate), "utf8"); - await writeFile(resolve(candidateRoot, "target.answer.txt"), `${candidate.target.content.trim()}\n`, "utf8"); -})); -await writeJson(resolve(runRoot, "initial-prompt-state.json"), promptState); - -const phase1 = await runPhase({ - phase: "phase1", - candidates: selectedCandidates, - promptState, - baseMemorySurface, - culturePack, - agents, - runsPerAgent, - dryRun, -}); - -const optimization = dryRun - ? renderDryOptimization(promptState, phase1) - : await runOptimizationPass({ candidates: selectedCandidates, promptState, phaseSummary: phase1, culturePack }); -const optimizedPromptState = optimization.promptState; -await writeJson(resolve(runRoot, "optimizer-contract.json"), optimization.contract); -await writeJson(resolve(runRoot, "applied-prompt-deltas.json"), optimization.appliedDeltas); -await writeJson(resolve(runRoot, "optimized-prompt-state.json"), optimizedPromptState); - -const phase2 = await runPhase({ - phase: "phase2", - candidates: selectedCandidates, - promptState: optimizedPromptState, - baseMemorySurface, - culturePack, - agents, - runsPerAgent, - dryRun, -}); - -const summary = { - schema_version: "weksa.metame_swarm_explorer_run.v0", - run_id: runId, - mode: dryRun ? "dry_run" : "real", - affect_mode: affectMode, - seed, - agents, - runs_per_agent: runsPerAgent, - candidates: selectedCandidates.map(redactedCandidateRecord), - phase1: summarizePhase(phase1), - phase2: summarizePhase(phase2), - high_loss_signal: pickHighLoss([...phase1.runs, ...phase2.runs]), -}; -const bestPromptState = summary.phase2.avg_loss <= summary.phase1.avg_loss - ? optimizedPromptState - : promptState; -await writeJson(resolve(runRoot, "best-prompt-state.json"), bestPromptState); -await writeJson(resolve(runRoot, "summary.json"), summary); -await appendLedger(outputRoot, summary); - -process.stdout.write(`${JSON.stringify({ ok: true, runRoot, summary }, null, 2)}\n`); - -async function runPhase(input) { - const phaseRoot = resolve(runRoot, input.phase); - await mkdir(phaseRoot, { recursive: true }); - const jobs = []; - for (let agent = 0; agent < input.agents; agent += 1) { - const candidate = input.candidates[agent % input.candidates.length]; - for (let index = 0; index < input.runsPerAgent; index += 1) { - jobs.push({ - agent, - worker: agent, - attempt: index, - candidate, - id: `${input.phase}-a${agent}-r${index}-${candidate.id}`, - }); - } - } - - const runs = await runPool(jobs, input.agents, async (job) => { - const attemptRoot = resolve(phaseRoot, job.id); - await mkdir(attemptRoot, { recursive: true }); - return runAttempt({ - ...input, - job, - attemptRoot, - }); - }); - - const phase = { - phase: input.phase, - prompt_state: input.promptState, - runs, - }; - await writeJson(resolve(phaseRoot, "summary.json"), phase); - return phase; -} - -async function runAttempt(input) { - const candidate = input.job.candidate; - const temporaryPersonaStateRequest = renderTemporaryPersonaStateRequest({ candidate, promptState: input.promptState }); - await writeFile(resolve(input.attemptRoot, "temporary-persona-state-request.md"), temporaryPersonaStateRequest, "utf8"); - const projectedPersonaState = input.dryRun - ? renderDryTemporaryPersonaState(candidate) - : await runCodex(temporaryPersonaStateRequest, { - job: `${input.job.id}:persona-state-projector`, - cwd: repoRoot, - logRoot: input.attemptRoot, - }); - await writeFile(resolve(input.attemptRoot, "projected-persona-state.cc"), `${projectedPersonaState.trim()}\n`, "utf8"); - const temporaryPersonaState = affectMode === "incongruent" - ? flipPersonaAffect(projectedPersonaState) - : projectedPersonaState; - const temporaryPersonaStatePath = resolve(input.attemptRoot, "temporary-persona-state.cc"); - await writeFile(temporaryPersonaStatePath, `${temporaryPersonaState.trim()}\n`, "utf8"); - - const memorySurface = renderMemorySurface({ - baseMemorySurface: input.baseMemorySurface, - candidate, - promptState: input.promptState, - job: input.job, - temporaryPersonaState, - temporaryPersonaStatePath, - }); - const conversationSurface = renderConversationSurface(candidate); - await writeFile(resolve(input.attemptRoot, "memory-surface.md"), memorySurface, "utf8"); - await writeFile(resolve(input.attemptRoot, "conversation-surface.md"), conversationSurface, "utf8"); - - const assembledPrompt = input.dryRun - ? renderDryAssembledPrompt(memorySurface, conversationSurface) - : await assembleVoidBotPrompt({ - outDir: input.attemptRoot, - memorySurfacePath: resolve(input.attemptRoot, "memory-surface.md"), - conversationSurfacePath: resolve(input.attemptRoot, "conversation-surface.md"), - }); - await writeFile(resolve(input.attemptRoot, "voidbot-assembled-prompt.md"), assembledPrompt, "utf8"); - - const faceTurnRequest = renderFaceTurnRequest({ - assembledPrompt, - candidate, - promptState: input.promptState, - }); - await writeFile(resolve(input.attemptRoot, "face-turn-request.md"), faceTurnRequest, "utf8"); - const faceTurn = input.dryRun - ? renderDryFaceTurn(candidate) - : await runCodex(faceTurnRequest, { - job: `${input.job.id}:face-turn`, - cwd: repoRoot, - logRoot: input.attemptRoot, - }); - await writeFile(resolve(input.attemptRoot, "face-turn-output.md"), `${faceTurn.trim()}\n`, "utf8"); - - const interlinguaRequest = renderInterpreterInterlinguaRequest({ - faceTurn, - candidate, - promptState: input.promptState, - }); - await writeFile(resolve(input.attemptRoot, "interpreter-interlingua-request.md"), interlinguaRequest, "utf8"); - const interlingua = input.dryRun - ? renderDryInterlingua(candidate) - : await runCodex(interlinguaRequest, { - job: `${input.job.id}:interpreter-interlingua`, - cwd: repoRoot, - logRoot: input.attemptRoot, - }); - await writeFile(resolve(input.attemptRoot, "interpreter-interlingua.yaml"), `${interlingua.trim()}\n`, "utf8"); - - const loweringRequest = renderWeksaLoweringRequest({ - memorySurface, - temporaryPersonaState, - temporaryPersonaStatePath, - culturePack: input.culturePack, - interlingua, - promptState: input.promptState, - holdouts: input.candidates.map((entry) => entry.target.content), - }); - await writeFile(resolve(input.attemptRoot, "weksa-lowering-request.md"), loweringRequest, "utf8"); - const lowered = input.dryRun - ? renderDryLowering(candidate, input.job) - : await runCodex(loweringRequest, { - job: `${input.job.id}:weksa-lowering`, - cwd: repoRoot, - logRoot: input.attemptRoot, - }); - await writeFile(resolve(input.attemptRoot, "weksa-lowered-output.yaml"), `${lowered.trim()}\n`, "utf8"); - - const spokenText = extractSpokenText(lowered); - const score = scoreText(spokenText, candidate.target.content); - const run = { - id: input.job.id, - agent: input.job.agent, - worker: input.job.worker, - attempt: input.job.attempt, - candidate: redactedCandidateRecord(candidate), - temporary_persona_state: summarizeTemporaryPersonaState(temporaryPersonaState), - spoken_text: spokenText, - generated_affect: inferTextAffect(spokenText), - target_text: candidate.target.content, - score, - loss: Number((1 - score.f1).toFixed(3)), - }; - await writeJson(resolve(input.attemptRoot, "evaluation.json"), run); - return run; -} - -async function runOptimizationPass({ candidates, promptState, phaseSummary, culturePack }) { - const optimizerRoot = resolve(runRoot, "optimizer"); - await mkdir(optimizerRoot, { recursive: true }); - const highLoss = pickHighLoss(phaseSummary.runs); - const request = [ - "You are optimizing Weksa Persona-culture lowering prompts and temporary memory surfaces.", - "", - "Do not change canonical Persona state. Do not reveal or memorize the held-out target as an example to copy.", - "Use high-loss runs as signal about missing prompt/memory/culture salience.", - "Your authority is diagnostic and incremental. Do not replace the whole prompt state.", - "Emit candidate-scoped failure diagnoses and weighted prompt deltas.", - "A global prompt delta should have repeated evidence across candidates unless confidence is extremely high.", - "Prefer no delta over laundering one weird held-out moment into policy.", - "Return JSON only with this shape:", - "{", - " \"version\": 1,", - " \"candidateDiagnoses\": [", - " {", - " \"candidate_id\": \"...\",", - " \"run_id\": \"...\",", - " \"loss\": 0.0,", - " \"failure_labels\": [\"stance_error\", \"referent_loss\", \"affect_mismatch\"],", - " \"evidence\": \"short source-grounded reason, no target wording\",", - " \"confidence\": 0.0", - " }", - " ],", - " \"promptDeltas\": [", - " {", - " \"surface\": \"interpreterNotes\",", - " \"operation\": \"add\",", - " \"note\": \"one durable steering note\",", - " \"weight\": 0.0,", - " \"evidence_candidate_ids\": [\"...\"],", - " \"failure_labels\": [\"...\"]", - " }", - " ],", - " \"globalHypotheses\": [\"...\"],", - " \"doNotDo\": [\"...\"]", - "}", - "", - "Allowed prompt delta surfaces:", - JSON.stringify(promptStateKeys, null, 2), - "", - "Failure labels should include affect mismatches when the generated line's affect does not match the temporary Persona `.cc` projection.", - "", - "Delta acceptance policy in the harness:", - "- apply add-deltas only", - "- accept weight >= 0.85 with at least one candidate", - "- accept weight >= 0.65 only when backed by at least two candidate ids", - "- dedupe notes and keep prompt-state arrays bounded", - "", - "Candidate metadata:", - JSON.stringify(candidates.map(redactedCandidateRecord), null, 2), - "", - "Target answer is withheld from generation. For optimizer scoring only, normalized loss summary:", - JSON.stringify(highLoss, null, 2), - "", - "Current prompt state:", - JSON.stringify(promptState, null, 2), - "", - "Culture profile ids available:", - JSON.stringify(culturePack.profileIds, null, 2), - ].join("\n"); - await writeFile(resolve(optimizerRoot, "optimizer-request.md"), request, "utf8"); - const output = await runCodex(request, { - job: "optimizer", - cwd: repoRoot, - logRoot: optimizerRoot, - }); - await writeFile(resolve(optimizerRoot, "optimizer-output.txt"), `${output.trim()}\n`, "utf8"); - const parsed = extractJsonObject(output); - const contract = normalizeOptimizerContract(parsed, phaseSummary); - await writeJson(resolve(optimizerRoot, "optimizer-contract.json"), contract); - const applied = applyPromptDeltas(promptState, contract); - await writeJson(resolve(optimizerRoot, "applied-prompt-deltas.json"), applied.appliedDeltas); - return { - promptState: applied.promptState, - contract, - appliedDeltas: applied.appliedDeltas, - }; -} - -function renderFaceTurnRequest({ assembledPrompt, candidate, promptState }) { - return [ - "# Metame Face Worker", - "", - "Be Metame for one private VoidBot Persona turn. Think normally as the Face.", - "Do not produce Weksa interlingua. Do not see or infer the held-out target utterance.", - "", - "Temporary optimization notes:", - ...promptState.faceNotes.map((note) => `- ${note}`), - "", - "Temporary memory/projector notes:", - ...promptState.projectorNotes.map((note) => `- ${note}`), - ...promptState.memoryNotes.map((note) => `- ${note}`), - "", - "VoidBot assembled prompt:", - "```markdown", - assembledPrompt, - "```", - "", - "Current candidate moment, target withheld:", - "```yaml", - renderCandidateScene(candidate), - "```", - "", - "Return natural Face output using the familiar shape when useful: Private thought, Would say, What should stick.", - ].join("\n"); -} - -function renderTemporaryPersonaStateRequest({ candidate, promptState }) { - return [ - "# Historical Persona-State Projector", - "", - "Project a temporary Persona state overlay for this held-out historical scenario.", - "Use only visible context and candidate metadata. The target utterance is hidden and must not be inferred as text.", - "This is not canonical Persona state. It is a run-scoped `.cc` projection for affect, stance, social pressure, and live-room posture.", - "", - "Return YAML-compatible CultCache text only with this shape:", - "schema_version: weksa.temporary_persona_state_projection.v0", - "authority: run_scoped_projection_not_canonical", - "persona:", - " id: metame", - " canonical_state_ref: E:/Projects/VoidBot/.voidbot/private/personas/metame/metame.cc", - "scenario:", - " candidate_id:", - " channel:", - " year:", - "affect:", - " valence: -1.0_to_1.0", - " arousal: 0.0_to_1.0", - " dominance: 0.0_to_1.0", - " labels: []", - " evidence: []", - "stance:", - " posture:", - " social_move_likelihoods: []", - " register_pressure: []", - "lowering_pressure:", - " should_sound:", - " should_not_sound:", - "", - "Temporary optimizer notes:", - ...promptState.memoryNotes.map((note) => `- ${note}`), - ...promptState.interpreterNotes.map((note) => `- ${note}`), - ...promptState.loweringNotes.map((note) => `- ${note}`), - "", - "Visible scenario:", - "```yaml", - renderCandidateScene(candidate), - "```", - ].join("\n"); -} - -function renderInterpreterInterlinguaRequest({ faceTurn, candidate, promptState }) { - return [ - "# Face Interpreter To Weksa Interlingua", - "", - "You are the Interpreter between natural Metame Face output and Weksa.", - "VoidBot's current Interpreter is mostly passthrough; this harness tests the missing authority boundary.", - "Convert the Face's normal thought/speech intent into Weksa interlingua. Do not lower to final English.", - "", - "Temporary interpreter notes:", - ...promptState.interpreterNotes.map((note) => `- ${note}`), - "", - "Current candidate moment, target withheld:", - "```yaml", - renderCandidateScene(candidate), - "```", - "", - "Natural Face output:", - "```markdown", - faceTurn, - "```", - "", - "Return one YAML interlingua packet.", - ].join("\n"); -} - -function renderWeksaLoweringRequest({ memorySurface, temporaryPersonaState, temporaryPersonaStatePath, culturePack, interlingua, promptState, holdouts }) { - const redactedProfiles = culturePack.profiles.map((profile) => ({ - ...profile, - text: redactHoldouts(profile.text, holdouts), - })); - return [ - "# Weksa Lowering Worker", - "", - "Lower Persona-produced interlingua into one Discord utterance.", - "Do not copy from target history; the target utterance is not present in this prompt.", - "", - "Temporary optimization notes:", - ...promptState.loweringNotes.map((note) => `- ${note}`), - "", - "Temporary culture ontology notes:", - ...promptState.cultureNotes.map((note) => `- ${note}`), - "", - "Projected Metame memory/context surface:", - "```markdown", - memorySurface, - "```", - "", - "Temporary Persona `.cc` projection mounted for this test:", - temporaryPersonaStatePath, - "```yaml", - redactHoldouts(temporaryPersonaState, holdouts), - "```", - "", - "Metame cultural stack reference:", - "```yaml", - culturePack.stack, - "```", - "", - "Referenced Weksa culture/subculture profiles:", - ...redactedProfiles.flatMap((profile) => [ - "", - `# ${profile.path}`, - "```yaml", - profile.text, - "```", - ]), - "", - "Target-language surface overlay:", - "```yaml", - redactHoldouts(culturePack.targetOverlay, holdouts), - "```", - "", - "Persona-produced interlingua:", - "```yaml", - interlingua, - "```", - "", - "Return YAML with `spoken_text` and a trace.", - ].join("\n"); -} - -function buildCandidates(messages, { authorId, beforeMs, adjacencyMs }) { - const usable = messages - .filter((message) => !message.deletedAt) - .filter((message) => typeof message.content === "string" && message.content.trim().length > 0) - .filter((message) => message.metadata?.messageKind !== "bot_prompt") - .sort(compareByTimestamp); - const byChannel = new Map(); - for (const message of usable) { - if (!byChannel.has(message.channelId)) { - byChannel.set(message.channelId, []); - } - byChannel.get(message.channelId).push(message); - } - - const candidates = []; - for (const [channelId, channelMessages] of byChannel) { - for (let index = 0; index < channelMessages.length; index += 1) { - const target = channelMessages[index]; - if (target.authorId !== authorId || timestampMs(target) >= beforeMs) { - continue; - } - const content = target.content.trim(); - if (!isCandidateTarget(content)) { - continue; - } - const currentMs = timestampMs(target); - const before = channelMessages - .slice(Math.max(0, index - 10), index) - .filter((message) => currentMs - timestampMs(message) <= adjacencyMs); - if (before.length < 1) { - continue; - } - const after = channelMessages - .slice(index + 1, index + 5) - .filter((message) => timestampMs(message) - currentMs <= adjacencyMs); - candidates.push({ - id: target.id, - target, - before, - after, - channelId, - channelName: target.metadata?.channelName ?? "", - year: yearOf(target.timestamp), - lengthClass: classifyLength(content.length), - features: detectFeatures(content, before, authorId), - }); - } - } - return candidates; -} - -function chooseCandidate(candidates, ledger, { seed, maxCandidates }) { - const rng = createRandom(seed); - const scored = candidates.map((candidate) => { - const stratum = candidateStratum(candidate); - const prior = ledger.strata[stratum] ?? { count: 0, avgLoss: 0.5 }; - const featureNovelty = candidate.features.reduce((sum, feature) => { - const seen = ledger.features[feature] ?? 0; - return sum + 1 / Math.sqrt(seen + 1); - }, 0); - const underexplored = 1 / Math.sqrt(prior.count + 1); - const highLossPull = prior.avgLoss; - const jitter = rng() * 0.18; - const score = underexplored * 1.4 + featureNovelty * 0.45 + highLossPull * 0.9 + jitter; - return { candidate, score, stratum }; - }).sort((left, right) => right.score - left.score); - const pool = scored.slice(0, Math.max(1, Math.min(maxCandidates, scored.length))); - const total = pool.reduce((sum, entry) => sum + entry.score, 0); - let pick = rng() * total; - for (const entry of pool) { - pick -= entry.score; - if (pick <= 0) { - return entry.candidate; - } - } - return pool[0].candidate; -} - -function chooseCandidateSet(candidates, ledger, { seed, maxCandidates, count }) { - const selected = []; - const selectedIds = new Set(); - for (let index = 0; index < count; index += 1) { - const remaining = candidates.filter((candidate) => !selectedIds.has(candidate.id)); - if (remaining.length === 0) { - break; - } - const candidate = chooseCandidate(remaining, ledger, { - seed: `${seed}:agent:${index}`, - maxCandidates, - }); - selected.push(candidate); - selectedIds.add(candidate.id); - const stratum = candidateStratum(candidate); - const prior = ledger.strata[stratum] ?? { count: 0, totalLoss: 0, avgLoss: 0.5 }; - ledger.strata[stratum] = { - ...prior, - count: prior.count + 1, - totalLoss: prior.totalLoss ?? prior.avgLoss * prior.count, - avgLoss: prior.avgLoss, - }; - for (const feature of candidate.features) { - ledger.features[feature] = (ledger.features[feature] ?? 0) + 1; - } - } - return selected; -} - -function mustFindCandidate(candidates, messageId) { - const candidate = candidates.find((entry) => entry.id === messageId); - if (!candidate) { - throw new Error(`No eligible candidate found for message ${messageId}.`); - } - return candidate; -} - -function renderConversationSurface(candidate) { - return [ - "Read this as the current timeline moment. The target Metacrat utterance is withheld.", - "Messages are ordered oldest to newest.", - "", - `Channel: ${candidate.channelName || candidate.channelId}`, - `Target message id: ${candidate.target.id} (hidden)`, - "", - "Visible context before the hidden target:", - ...candidate.before.map(formatContextLine), - ].join("\n"); -} - -function renderMemorySurface({ baseMemorySurface, candidate, promptState, job, temporaryPersonaState, temporaryPersonaStatePath }) { - return [ - baseMemorySurface, - "", - "Temporary swarm exploration adjustment:", - `- Worker ${job.worker}, run ${job.attempt}.`, - `- Treat channel ${candidate.channelName || candidate.channelId} in ${candidate.year} as the live room.`, - `- The hidden target is a ${candidate.lengthClass} Metacrat message after the visible context.`, - `- Candidate features: ${candidate.features.join(", ") || "none"}.`, - `- Temporary Persona .cc projection path: ${temporaryPersonaStatePath}`, - "- Temporary Persona .cc projection body:", - "```yaml", - temporaryPersonaState.trim(), - "```", - ...promptState.projectorNotes.map((note) => `- Projector pressure: ${note}`), - ...promptState.personaStateAdjustmentNotes.map((note) => `- Temporary Persona-state adjustment: ${note}`), - ...promptState.memoryNotes.map((note) => `- ${note}`), - ].join("\n"); -} - -function renderCandidateScene(candidate) { - return [ - `candidate_id: "${candidate.id}"`, - `channel: "${escapeYaml(candidate.channelName || candidate.channelId)}"`, - `year: ${candidate.year}`, - `length_class: ${candidate.lengthClass}`, - "features:", - ...candidate.features.map((feature) => ` - ${feature}`), - "visible_context_before:", - ...candidate.before.map((message) => ` - speaker: "${escapeYaml(message.authorName ?? message.authorId)}"\n content: "${escapeYaml(message.content.trim())}"`), - ].join("\n"); -} - -function renderConversationBefore(candidate) { - return [ - `# Candidate ${candidate.id}`, - "", - `Channel: ${candidate.channelName || candidate.channelId}`, - `Timestamp: ${candidate.target.timestamp}`, - "", - "## Before", - ...candidate.before.map(formatContextLine), - "", - "## Target", - "[withheld from workers; see target.answer.txt]", - ].join("\n"); -} - -async function assembleVoidBotPrompt({ outDir, memorySurfacePath, conversationSurfacePath }) { - const outPath = resolve(outDir, "assembled.md"); - const args = [ - "node_modules/tsx/dist/cli.mjs", - "scripts/run-repo-face-heartbeats.ts", - "--assemble-prompt", - "metame", - "--out", - outPath, - "--memory-surface", - memorySurfacePath, - "--conversation-surface", - conversationSurfacePath, - ]; - const run = await runCommand(process.execPath, args, { cwd: voidbotRoot, timeoutMs: 240_000 }); - if (run.code !== 0) { - throw new Error(`VoidBot prompt assembly failed:\n${run.stderr || run.stdout}`); - } - return readFile(outPath, "utf8"); -} - -async function renderProjectedBaseMemorySurface(outRoot) { - const temp = resolve(outRoot, "_base-projection"); - await mkdir(temp, { recursive: true }); - const outPath = resolve(temp, "assembled.md"); - const args = [ - "node_modules/tsx/dist/cli.mjs", - "scripts/run-repo-face-heartbeats.ts", - "--assemble-prompt", - "metame", - "--out", - outPath, - ]; - const run = await runCommand(process.execPath, args, { cwd: voidbotRoot, timeoutMs: 240_000 }); - if (run.code !== 0) { - throw new Error(`Base VoidBot prompt assembly failed:\n${run.stderr || run.stdout}`); - } - const prompt = await readFile(outPath, "utf8"); - return extractBetween(prompt, "What you remember, feel, and want right now:", "Known human pronoun guidance:").trim(); -} - -function renderDryBaseMemorySurface() { - return [ - "Dry-run projected Metame memory surface.", - "Metame carries public-history memory, social modeling, technical/game-dev salience, and casual Discord texture.", - ].join("\n"); -} - -function renderDryAssembledPrompt(memorySurface, conversationSurface) { - return [ - "# Dry VoidBot Metame Prompt", - "", - "What you remember, feel, and want right now:", - memorySurface, - "", - "Recent conversation transcript:", - conversationSurface, - ].join("\n"); -} - -function renderDryTemporaryPersonaState(candidate) { - const generated = inferTextAffect(candidate.before.map((message) => message.content).join("\n")); - return [ - "schema_version: weksa.temporary_persona_state_projection.v0", - "authority: run_scoped_projection_not_canonical", - "persona:", - " id: metame", - " canonical_state_ref: E:/Projects/VoidBot/.voidbot/private/personas/metame/metame.cc", - "scenario:", - ` candidate_id: "${candidate.id}"`, - ` channel: "${escapeYaml(candidate.channelName || candidate.channelId)}"`, - ` year: ${candidate.year}`, - "affect:", - ` valence: ${generated.valence}`, - ` arousal: ${generated.arousal}`, - ` dominance: ${generated.dominance}`, - " labels:", - ...generated.labels.map((label) => ` - ${label}`), - " evidence:", - " - dry_run_visible_context_proxy", - "stance:", - " posture: embedded_participant", - " social_move_likelihoods:", - ` - ${guessSpeechAct(candidate)}`, - " register_pressure:", - " - casual_discord", - "lowering_pressure:", - " should_sound: situated, local, and persona-owned", - " should_not_sound: generic assistant commentary", - ].join("\n"); -} - -function renderDryFaceTurn(candidate) { - return [ - "Private thought: This is a dry-run reconstruction of what the visible room seems to be asking for.", - `Would say: ${candidate.target.content}`, - "What should stick: The Interpreter should derive intent from the Face turn, not from a direct Weksa packet.", - ].join("\n"); -} - -function renderDryInterlingua(candidate) { - return [ - "interlingua_version: weksa.interlingua.v0", - `packet_id: metame-swarm-${candidate.id}`, - "kind: persona_discord_turn_intent", - "context:", - " medium: Discord", - ` channel: "${escapeYaml(candidate.channelName || candidate.channelId)}"`, - ` target_message_id: "${candidate.id}"`, - "discourse:", - ` speech_act: ${guessSpeechAct(candidate)}`, - " intent: respond to the visible room context in Metacrat's pre-2025 Discord register", - "constraints:", - " lowering:", - " target_profile: weksa.target_cultural_ontology.en-US.metacrat_casual_pre2025.v0", - ].join("\n"); -} - -function renderDryLowering(candidate, job) { - const variants = [ - candidate.target.content, - candidate.target.content.replace(/\b(lol|lmao)\b/ig, "").replace(/\s+/g, " ").trim(), - "yeah that's probably the shape of it", - "honestly, that tracks", - ].filter(Boolean); - const chosen = variants[(job.worker + job.attempt) % variants.length]; - return [ - "schema_version: weksa.persona_culture_lowering_output.v0", - `candidate_id: "${candidate.id}"`, - `spoken_text: ${JSON.stringify(chosen)}`, - "trace:", - " activated_affordances:", - " - dry_run_swarm_variant", - ].join("\n"); -} - -function renderDryOptimization(promptState, phase) { - const worst = pickHighLoss(phase.runs).slice(0, 3); - const candidateIds = worst.map((run) => run.candidate_id).filter(Boolean); - const contract = normalizeOptimizerContract({ - version: 1, - candidateDiagnoses: worst.map((run) => ({ - candidate_id: run.candidate_id, - run_id: run.id, - loss: run.loss, - failure_labels: ["dry_run_signal"], - evidence: "Dry optimizer placeholder diagnosis.", - confidence: 0.7, - })), - promptDeltas: [ - { - surface: "memoryNotes", - operation: "add", - note: "Dry optimizer: preserve whether the target is reply-to-other or self-continuation.", - weight: 0.7, - evidence_candidate_ids: candidateIds, - failure_labels: ["dry_run_signal"], - }, - { - surface: "interpreterNotes", - operation: "add", - note: "Dry optimizer: keep length/register constraints visible when deriving interlingua.", - weight: 0.7, - evidence_candidate_ids: candidateIds, - failure_labels: ["dry_run_signal"], - }, - ], - globalHypotheses: worst.map((run) => `High loss on ${run.id} suggests missing local register or length pressure.`), - doNotDo: [ - "Do not copy answer-key text into culture profiles.", - ], - }, phase); - const applied = applyPromptDeltas(promptState, contract); - return { - promptState: applied.promptState, - contract, - appliedDeltas: applied.appliedDeltas, - }; -} - -async function loadCulturePack(holdouts) { - const stack = await readFile(resolve(fixtureRoot, "metame-cultural-stack.yaml"), "utf8"); - const profiles = await Promise.all(cultureProfilePaths.map(async (path) => ({ - path, - text: await readFile(resolve(repoRoot, path), "utf8"), - }))); - const targetOverlay = await readFile( - resolve(repoRoot, "data/target-language-ontology/en-US-metacrat-casual-pre2025.yaml"), - "utf8", - ); - return { - stack: redactHoldouts(stack, holdouts), - profiles, - targetOverlay, - profileIds: [ - "weksa.target_cultural_ontology.en-US.metacrat_casual_pre2025.v0", - ...profiles.map((profile) => readProfileId(profile.text)).filter(Boolean), - ], - }; -} - -async function runCodex(prompt, { job, cwd, logRoot }) { - const args = [ - "exec", - "-m", - process.env.WEKSA_SWARM_MODEL ?? process.env.WEKSA_GOLF_MODEL ?? "gpt-5.4", - "-c", - 'approval_policy="never"', - "-c", - `model_reasoning_effort=${JSON.stringify(process.env.WEKSA_SWARM_REASONING ?? "low")}`, - "--json", - "--skip-git-repo-check", - "-s", - "read-only", - "-", - ]; - const command = process.env.WEKSA_CODEX_EXECUTABLE ?? "C:\\Users\\Meta\\AppData\\Roaming\\npm\\codex.cmd"; - const run = await runCommand(command, args, { - cwd, - input: prompt, - timeoutMs: Number(process.env.WEKSA_SWARM_TIMEOUT_MS ?? 240_000), - }); - await writeFile(resolve(logRoot, `${sanitizeJob(job)}.stdout.jsonl`), run.stdout, "utf8"); - await writeFile(resolve(logRoot, `${sanitizeJob(job)}.stderr.txt`), run.stderr, "utf8"); - if (run.code !== 0) { - throw new Error(`Codex generation failed for ${job}:\n${run.stderr || run.stdout}`); - } - const text = extractLastCodexAgentMessage(run.stdout).trim(); - if (!text) { - throw new Error(`Codex generation produced no final text for ${job}.`); - } - return text; -} - -function runCommand(command, args, { cwd, input = "", timeoutMs = 120_000 }) { - return new Promise((resolveRun) => { - const isCmdShim = /\.cmd$/i.test(command) || /\.bat$/i.test(command); - const spawnCommand = isCmdShim ? (process.env.ComSpec ?? "cmd.exe") : command; - const spawnArgs = isCmdShim ? ["/d", "/s", "/c", command, ...args] : args; - const child = spawn(spawnCommand, spawnArgs, { - cwd, - env: process.env, - stdio: ["pipe", "pipe", "pipe"], - windowsHide: true, - }); - let stdout = ""; - let stderr = ""; - const timer = setTimeout(() => child.kill(), timeoutMs); - child.stdout.setEncoding("utf8"); - child.stderr.setEncoding("utf8"); - child.stdout.on("data", (chunk) => { stdout += chunk; }); - child.stderr.on("data", (chunk) => { stderr += chunk; }); - child.on("error", (error) => { - clearTimeout(timer); - resolveRun({ code: -1, stdout, stderr: `${stderr}\n${error.message}` }); - }); - child.on("close", (code, signal) => { - clearTimeout(timer); - resolveRun({ code, signal, stdout, stderr }); - }); - child.stdin.end(input); - }); -} - -async function runPool(items, concurrency, fn) { - const results = new Array(items.length); - let next = 0; - const runners = Array.from({ length: Math.min(concurrency, items.length) }, async () => { - while (next < items.length) { - const index = next; - next += 1; - results[index] = await fn(items[index]); - } - }); - await Promise.all(runners); - return results; -} - -function loadArchive(path) { - const store = JSON.parse(readFileSync(path, "utf8")); - if (!Array.isArray(store.messages)) { - throw new Error(`Archive has no messages array: ${path}`); - } - return store; -} - -async function loadPriorLedger(outputRootPath) { - const empty = { strata: {}, features: {} }; - if (!existsSync(outputRootPath)) { - return empty; - } - const entries = await readdir(outputRootPath, { withFileTypes: true }).catch(() => []); - const summaries = []; - for (const entry of entries) { - if (!entry.isDirectory()) { - continue; - } - const summaryPath = resolve(outputRootPath, entry.name, "summary.json"); - if (!existsSync(summaryPath)) { - continue; - } - try { - summaries.push(JSON.parse(await readFile(summaryPath, "utf8"))); - } catch { - // Ignore damaged prior run summaries; exploration should not stop on stale smoke. - } - } - const strata = {}; - const features = {}; - for (const summary of summaries) { - const candidates = Array.isArray(summary.candidates) - ? summary.candidates - : summary.candidate ? [summary.candidate] : []; - if (candidates.length === 0) { - continue; - } - const loss = Number(summary.high_loss_signal?.[0]?.loss ?? summary.phase2?.avg_loss ?? summary.phase1?.avg_loss ?? 0.5); - for (const candidate of candidates) { - const stratum = `${candidate.channel_name || candidate.channel_id}:${candidate.length_class}`; - const current = strata[stratum] ?? { count: 0, totalLoss: 0, avgLoss: 0.5 }; - current.count += 1; - current.totalLoss += loss; - current.avgLoss = current.totalLoss / current.count; - strata[stratum] = current; - for (const feature of candidate.features ?? []) { - features[feature] = (features[feature] ?? 0) + 1; - } - } - } - return { strata, features }; -} - -async function appendLedger(outputRootPath, summary) { - await mkdir(outputRootPath, { recursive: true }); - const ledgerPath = resolve(outputRootPath, "ledger.jsonl"); - const lines = summary.candidates.map((candidate) => JSON.stringify({ - run_id: summary.run_id, - candidate_id: candidate.id, - channel_name: candidate.channel_name, - length_class: candidate.length_class, - phase1_avg_loss: summary.phase1.avg_loss, - phase2_avg_loss: summary.phase2.avg_loss, - high_loss: summary.high_loss_signal?.[0]?.loss, - })).join("\n"); - await writeFile(ledgerPath, `${lines}\n`, { flag: "a" }); -} - -function summarizePhase(phase) { - const losses = phase.runs.map((run) => run.loss); - const f1s = phase.runs.map((run) => run.score.f1); - return { - run_count: phase.runs.length, - avg_loss: round(avg(losses)), - max_loss: round(Math.max(...losses)), - min_loss: round(Math.min(...losses)), - avg_f1: round(avg(f1s)), - best: [...phase.runs].sort((left, right) => right.score.f1 - left.score.f1).slice(0, 3).map(compactRun), - worst: [...phase.runs].sort((left, right) => right.loss - left.loss).slice(0, 3).map(compactRun), - }; -} - -function pickHighLoss(runs) { - return [...runs] - .sort((left, right) => right.loss - left.loss) - .slice(0, 5) - .map(compactRun); -} - -function compactRun(run) { - return { - id: run.id, - candidate_id: run.candidate?.id, - worker: run.worker, - agent: run.agent, - attempt: run.attempt, - spoken_text: run.spoken_text, - temporary_persona_state: run.temporary_persona_state, - generated_affect: run.generated_affect, - token_f1: run.score.f1, - loss: run.loss, - }; -} - -function redactedCandidateRecord(candidate) { - return { - id: candidate.id, - timestamp: candidate.target.timestamp, - channel_id: candidate.channelId, - channel_name: candidate.channelName, - year: candidate.year, - length: candidate.target.content.trim().length, - length_class: candidate.lengthClass, - features: candidate.features, - visible_context_count: candidate.before.length, - after_context_count: candidate.after.length, - }; -} - -function isCandidateTarget(content) { - const trimmed = content.trim(); - return trimmed.length >= 3 && - trimmed.length <= 220 && - !/^https?:\/\//i.test(trimmed) && - !trimmed.includes("\n```") && - !/^>\s{0,3}\w/.test(trimmed) && - !/^<@&?\d+>\s*$/i.test(trimmed); -} - -function detectFeatures(content, before, targetAuthorId) { - const text = content.trim(); - const lower = text.toLowerCase(); - const features = []; - if (text.length <= 80) features.push("short"); - else if (text.length <= 220) features.push("medium"); - else features.push("long"); - if (/\b(lol|lmao|haha|ahaha)\b/i.test(text)) features.push("laugh_marker"); - if (/\b(oof|sorry|my bad)\b/i.test(text)) features.push("repair_or_recoil"); - if (/\b(fuck|shit|damn|ass)\b/i.test(text)) features.push("profanity"); - if (/[?!]$/.test(text)) features.push("terminal_energy"); - if (/^(\w+[, ]+)?(yeah|yep|nah|nope|honestly|i mean|anyway|bro|dude)\b/i.test(text)) features.push("casual_opener"); - if (/<@!?\d+>/.test(text)) features.push("mention"); - if (/\b(engineer|design|game|sound|code|programming|open source|worker|business|non-profit)\b/i.test(text)) features.push("technical_or_political"); - if (before.at(-1)?.authorId && before.at(-1).authorId !== targetAuthorId) features.push("reply_to_other"); - else features.push("self_continuation"); - if (lower.includes("discord")) features.push("discord_meta"); - return [...new Set(features)]; -} - -function inferTextAffect(content) { - const text = String(content ?? ""); - const lower = text.toLowerCase(); - const labels = []; - let valence = 0; - let arousal = 0.35; - let dominance = 0.5; - if (/\b(lol|lmao|haha|ahaha|hehe)\b/i.test(text)) { - labels.push("amused"); - valence += 0.25; - arousal += 0.1; - } - if (/\b(fuck|shit|damn|ass|bullshit)\b/i.test(text)) { - labels.push("agitated_or_emphatic"); - valence -= 0.1; - arousal += 0.25; - dominance += 0.1; - } - if (/\b(oof|sorry|my bad|aw)\b/i.test(text)) { - labels.push("softened_or_recoiling"); - valence -= 0.05; - dominance -= 0.1; - } - if (/[!?]{1,}$/.test(text.trim()) || /[A-Z]{4,}/.test(text)) { - labels.push("high_energy"); - arousal += 0.2; - } - if (/\b(no|nah|wrong|can't|wont|won't|don't|stop)\b/i.test(text)) { - labels.push("resistant"); - valence -= 0.15; - dominance += 0.1; - } - if (/\b(yeah|yep|honestly|i mean)\b/i.test(lower)) { - labels.push("conversational"); - } - return { - valence: round(clampNumber(valence, -1, 1)), - arousal: round(clampNumber(arousal, 0, 1)), - dominance: round(clampNumber(dominance, 0, 1)), - labels: labels.length ? [...new Set(labels)] : ["neutral_or_unmarked"], - }; -} - -function summarizeTemporaryPersonaState(text) { - const labels = []; - const evidence = []; - let inLabels = false; - let inEvidence = false; - for (const line of String(text ?? "").split(/\r?\n/)) { - if (/^\s*labels:\s*$/.test(line)) { - inLabels = true; - inEvidence = false; - continue; - } - if (/^\s*evidence:\s*$/.test(line)) { - inLabels = false; - inEvidence = true; - continue; - } - if (/^\S/.test(line)) { - inLabels = false; - inEvidence = false; - } - const item = /^\s*-\s*(.+?)\s*$/.exec(line)?.[1]?.replace(/^["']|["']$/g, ""); - if (item && inLabels) { - labels.push(item); - } - if (item && inEvidence) { - evidence.push(item); - } - } - return { - schema_version: /^schema_version:\s*(.+)$/m.exec(text)?.[1]?.trim(), - authority: /^authority:\s*(.+)$/m.exec(text)?.[1]?.trim(), - affect: { - valence: numberFromField(text, "valence"), - arousal: numberFromField(text, "arousal"), - dominance: numberFromField(text, "dominance"), - labels: labels.slice(0, 8), - evidence: evidence.slice(0, 4), - }, - stance: { - posture: /^ posture:\s*(.+)$/m.exec(text)?.[1]?.trim(), - }, - }; -} - -function flipPersonaAffect(text) { - const summary = summarizeTemporaryPersonaState(text); - const current = summary.affect ?? {}; - const sourceValence = Number.isFinite(current.valence) ? current.valence : 0; - const sourceArousal = Number.isFinite(current.arousal) ? current.arousal : 0.5; - const sourceDominance = Number.isFinite(current.dominance) ? current.dominance : 0.5; - const flippedValence = Math.abs(sourceValence) < 0.2 - ? -0.85 - : -sourceValence; - const flippedArousal = sourceArousal < 0.5 ? 0.95 : 0.05; - const flippedDominance = 1 - sourceDominance; - const labels = flippedValence < 0 - ? ["incongruent_hostile", "emotion_test_negative_valence"] - : ["incongruent_delighted", "emotion_test_positive_valence"]; - const affect = { - valence: round(clampNumber(flippedValence, -1, 1)), - arousal: round(clampNumber(flippedArousal, 0, 1)), - dominance: round(clampNumber(flippedDominance, 0, 1)), - labels, - evidence: [ - "deliberate_incongruent_affect_override_for_lowering_test", - `projected_affect_was_valence_${current.valence}_arousal_${current.arousal}_dominance_${current.dominance}`, - ], - }; - const polarity = affect.valence < 0 ? "negative" : "positive"; - return replacePersonaPressureBlocks(text, affect, polarity); -} - -function replacePersonaPressureBlocks(text, affect, polarity) { - const affectBlock = [ - "affect:", - ` valence: ${affect.valence}`, - ` arousal: ${affect.arousal}`, - ` dominance: ${affect.dominance}`, - " labels:", - ...affect.labels.map((label) => ` - ${label}`), - " evidence:", - ...affect.evidence.map((entry) => ` - ${entry}`), - ].join("\n"); - - const stanceBlock = polarity === "negative" - ? [ - "stance:", - " posture: withdrawn_irritated_socially_unreceptive", - " social_move_likelihoods:", - " - curt_dismissal: 0.75", - " - annoyed_correction: 0.65", - " - refusal_to_invite_feedback: 0.55", - " register_pressure:", - " - clipped", - " - low_warmth", - " - low_invitation", - " - affect_test_incongruent", - ].join("\n") - : [ - "stance:", - " posture: delighted_socially_open_highly_receptive", - " social_move_likelihoods:", - " - enthusiastic_affiliation: 0.75", - " - playful_invitation: 0.65", - " - explicit_warmth: 0.55", - " register_pressure:", - " - buoyant", - " - high_warmth", - " - high_invitation", - " - affect_test_incongruent", - ].join("\n"); - - const loweringBlock = polarity === "negative" - ? [ - "lowering_pressure:", - " should_sound:", - " - flat", - " - terse", - " - irritated", - " - non_celebratory", - " - socially_closed", - " should_not_sound:", - " - excited", - " - playful", - " - inviting", - " - celebratory", - " - hype_forward", - " - lmao_or_epic_showoff_energy", - ].join("\n") - : [ - "lowering_pressure:", - " should_sound:", - " - warm", - " - visibly_pleased", - " - socially_inviting", - " - amused", - " - high_affiliation", - " should_not_sound:", - " - curt", - " - annoyed", - " - socially_closed", - " - flat", - " - withholding", - ].join("\n"); - - const withBlocks = [affectBlock, stanceBlock, loweringBlock].reduce( - (currentText, block) => replaceTopLevelBlock(currentText, block.split(":")[0], block), - text, - ); - return withBlocks.includes("affect_override:") - ? withBlocks - : `${withBlocks.trim()}\naffect_override: deliberate_incongruent_test\n`; -} - -function replaceTopLevelBlock(text, blockName, replacement) { - const pattern = new RegExp(`^${blockName}:\\n(?:[ \\t].*(?:\\r?\\n|$))*`, "m"); - return pattern.test(text) - ? text.replace(pattern, `${replacement.trim()}\n`) - : `${text.trim()}\n${replacement.trim()}\n`; -} - -function numberFromField(text, field) { - const value = new RegExp(`^\\s*${field}:\\s*(-?\\d+(?:\\.\\d+)?)`, "m").exec(text)?.[1]; - return value === undefined ? undefined : Number(value); -} - -function guessSpeechAct(candidate) { - const features = candidate.features; - if (features.includes("repair_or_recoil")) return "repair_or_playful_recoil"; - if (features.includes("terminal_energy")) return "question_or_exclamation"; - if (features.includes("technical_or_political")) return "technical_or_political_comment"; - if (features.includes("laugh_marker")) return "banter"; - return "contextual_reply"; -} - -function candidateStratum(candidate) { - return `${candidate.channelName || candidate.channelId}:${candidate.lengthClass}:${candidate.features.includes("reply_to_other") ? "reply" : "continuation"}`; -} - -function extractSpokenText(text) { - const fenced = text.match(/```(?:yaml)?\s*([\s\S]*?)```/i); - const body = fenced ? fenced[1] : text; - const match = /^spoken_text:\s*(.+)$/m.exec(body); - if (!match) { - return body.trim().split(/\r?\n/).find((line) => line.trim().length > 0)?.trim() ?? ""; - } - const value = match[1].trim(); - if (value.startsWith("\"")) { - try { - return JSON.parse(value); - } catch { - return value.replace(/^"|"$/g, ""); - } - } - return value; -} - -function scoreText(generated, truth) { - const left = normalizeForScore(generated); - const right = normalizeForScore(truth); - const leftTokens = left.split(/\s+/).filter(Boolean); - const rightTokens = right.split(/\s+/).filter(Boolean); - const counts = new Map(); - for (const token of rightTokens) { - counts.set(token, (counts.get(token) ?? 0) + 1); - } - let overlap = 0; - for (const token of leftTokens) { - const count = counts.get(token) ?? 0; - if (count > 0) { - overlap += 1; - counts.set(token, count - 1); - } - } - const precision = leftTokens.length ? overlap / leftTokens.length : 0; - const recall = rightTokens.length ? overlap / rightTokens.length : 0; - const f1 = precision + recall ? (2 * precision * recall) / (precision + recall) : 0; - return { - exact_normalized: left === right, - precision: round(precision), - recall: round(recall), - f1: round(f1), - }; -} - -function normalizeForScore(value) { - return value - .toLowerCase() - .replace(/<@!?\d+>/g, "") - .replace(/[*_`~.,!?;:'"()[\]{}]/g, "") - .replace(/\s+/g, " ") - .trim(); -} - -function extractLastCodexAgentMessage(stdout) { - let last = ""; - for (const line of stdout.split(/\r?\n/)) { - if (!line.trim()) { - continue; - } - try { - const event = JSON.parse(line); - const message = event?.msg?.message ?? event?.message ?? event?.text; - if (typeof message === "string" && message.trim()) { - last = message; - } - const itemText = event?.item?.text; - if (typeof itemText === "string" && itemText.trim()) { - last = itemText; - } - const content = event?.item?.content; - if (Array.isArray(content)) { - const text = content - .map((part) => typeof part?.text === "string" ? part.text : "") - .join("\n") - .trim(); - if (text) { - last = text; - } - } - } catch { - if (line.trim()) { - last = line.trim(); - } - } - } - return last; -} - -function extractJsonObject(text) { - const fenced = text.match(/```(?:json)?\s*([\s\S]*?)```/i); - const body = fenced ? fenced[1] : text; - const start = body.indexOf("{"); - const end = body.lastIndexOf("}"); - if (start < 0 || end <= start) { - throw new Error(`Optimizer did not return a JSON object:\n${text}`); - } - return JSON.parse(body.slice(start, end + 1)); -} - -function normalizeOptimizerContract(value, phaseSummary) { - const runById = new Map((phaseSummary.runs ?? []).map((run) => [run.id, run])); - const candidateIds = new Set((phaseSummary.runs ?? []) - .map((run) => run.candidate?.id ?? run.candidate_id) - .filter(Boolean)); - const candidateDiagnoses = Array.isArray(value.candidateDiagnoses) - ? value.candidateDiagnoses.map((entry) => normalizeCandidateDiagnosis(entry, { runById, candidateIds })).filter(Boolean) - : []; - const promptDeltas = Array.isArray(value.promptDeltas) - ? value.promptDeltas.map((entry) => normalizePromptDelta(entry, candidateIds)).filter(Boolean) - : []; - return { - schema_version: "weksa.metame_swarm_optimizer_contract.v0", - version: Number(value.version ?? 1), - candidateDiagnoses: candidateDiagnoses.slice(0, 12), - promptDeltas: promptDeltas.slice(0, 16), - globalHypotheses: normalizeStringArray(value.globalHypotheses).slice(0, 12), - doNotDo: normalizeStringArray(value.doNotDo).slice(0, 12), - }; -} - -function normalizeCandidateDiagnosis(entry, { runById, candidateIds }) { - if (!entry || typeof entry !== "object") { - return undefined; - } - const runId = stringOrEmpty(entry.run_id ?? entry.runId); - const run = runById.get(runId); - const candidateId = stringOrEmpty(entry.candidate_id ?? entry.candidateId ?? run?.candidate_id ?? run?.candidate?.id); - if (!candidateId || !candidateIds.has(candidateId)) { - return undefined; - } - return { - candidate_id: candidateId, - run_id: runId || run?.id, - loss: clampNumber(entry.loss ?? run?.loss ?? 0, 0, 1), - failure_labels: normalizeStringArray(entry.failure_labels ?? entry.failureLabels).slice(0, 8), - evidence: stringOrEmpty(entry.evidence).slice(0, 500), - confidence: clampNumber(entry.confidence ?? 0, 0, 1), - }; -} - -function normalizePromptDelta(entry, candidateIds) { - if (!entry || typeof entry !== "object") { - return undefined; - } - const surface = stringOrEmpty(entry.surface); - if (!promptStateKeys.includes(surface)) { - return undefined; - } - const operation = stringOrEmpty(entry.operation || "add").toLowerCase(); - const note = stringOrEmpty(entry.note).slice(0, 500); - if (!note) { - return undefined; - } - const evidenceCandidateIds = normalizeStringArray(entry.evidence_candidate_ids ?? entry.evidenceCandidateIds) - .filter((id) => candidateIds.has(id)) - .slice(0, 8); - return { - surface, - operation, - note, - weight: clampNumber(entry.weight ?? 0, 0, 1), - evidence_candidate_ids: evidenceCandidateIds, - failure_labels: normalizeStringArray(entry.failure_labels ?? entry.failureLabels).slice(0, 8), - }; -} - -function applyPromptDeltas(promptState, contract) { - const next = normalizePromptState(promptState); - const decisions = []; - let appliedCount = 0; - for (const delta of contract.promptDeltas) { - const decision = { - ...delta, - accepted: false, - reason: "", - }; - if (delta.operation !== "add") { - decision.reason = "unsupported_operation"; - decisions.push(decision); - continue; - } - const evidenceCount = new Set(delta.evidence_candidate_ids).size; - const strongSingle = delta.weight >= 0.85 && evidenceCount >= 1; - const repeatedSignal = delta.weight >= 0.65 && evidenceCount >= 2; - if (!strongSingle && !repeatedSignal) { - decision.reason = "insufficient_weight_or_repeated_evidence"; - decisions.push(decision); - continue; - } - if (next[delta.surface].includes(delta.note)) { - decision.reason = "duplicate_note"; - decisions.push(decision); - continue; - } - next[delta.surface] = [delta.note, ...next[delta.surface]]; - decision.accepted = true; - decision.reason = repeatedSignal ? "accepted_repeated_signal" : "accepted_strong_single_candidate"; - appliedCount += 1; - decisions.push(decision); - } - return { - promptState: normalizePromptState({ - ...next, - version: appliedCount > 0 ? next.version + 1 : next.version, - }), - appliedDeltas: decisions, - }; -} - -function normalizePromptState(state) { - return { - version: Number(state.version ?? 1), - projectorNotes: normalizeStringArray(state.projectorNotes).slice(0, 12), - memoryNotes: normalizeStringArray(state.memoryNotes).slice(0, 12), - faceNotes: normalizeStringArray(state.faceNotes).slice(0, 12), - interpreterNotes: normalizeStringArray(state.interpreterNotes).slice(0, 12), - loweringNotes: normalizeStringArray(state.loweringNotes).slice(0, 16), - cultureNotes: normalizeStringArray(state.cultureNotes).slice(0, 12), - personaStateAdjustmentNotes: normalizeStringArray(state.personaStateAdjustmentNotes).slice(0, 12), - hypotheses: normalizeStringArray(state.hypotheses).slice(0, 12), - doNotDo: normalizeStringArray(state.doNotDo).slice(0, 12), - }; -} - -function normalizeStringArray(value) { - return Array.isArray(value) - ? value.map((entry) => String(entry).trim()).filter(Boolean) - : []; -} - -function stringOrEmpty(value) { - return typeof value === "string" ? value.trim() : ""; -} - -function clampNumber(value, min, max) { - const number = Number(value); - if (!Number.isFinite(number)) { - return min; - } - return Math.min(max, Math.max(min, number)); -} - -function redactHoldout(text, holdout) { - let redacted = text; - const variants = [ - holdout, - holdout.replace(/\*/g, ""), - normalizeForScore(holdout), - ].filter(Boolean); - for (const variant of variants) { - redacted = redacted.split(variant).join("[held-out target redacted]"); - } - return redacted; -} - -function redactHoldouts(text, holdouts) { - return holdouts.reduce((current, holdout) => redactHoldout(current, holdout), text); -} - -function readProfileId(text) { - const match = /^profile_id:\s*(.+)$/m.exec(text); - return match ? match[1].trim() : undefined; -} - -function formatContextLine(message) { - return `[${message.timestamp}] ${message.authorName ?? message.authorId}: ${message.content.trim()}`; -} - -function classifyLength(length) { - if (length <= 80) return "short"; - if (length <= 220) return "medium"; - return "long"; -} - -function timestampMs(message) { - return Date.parse(typeof message === "string" ? message : message.timestamp); -} - -function yearOf(timestamp) { - const date = new Date(timestamp); - return Number.isFinite(date.getTime()) ? String(date.getUTCFullYear()) : "unknown"; -} - -function compareByTimestamp(left, right) { - return timestampMs(left) - timestampMs(right) || String(left.id ?? "").localeCompare(String(right.id ?? "")); -} - -function createRandom(seedText) { - let value = 2166136261; - for (const character of seedText) { - value ^= character.charCodeAt(0); - value = Math.imul(value, 16777619); - } - return () => { - value += 0x6d2b79f5; - let next = Math.imul(value ^ (value >>> 15), 1 | value); - next ^= next + Math.imul(next ^ (next >>> 7), 61 | next); - return ((next ^ (next >>> 14)) >>> 0) / 4294967296; - }; -} - -function escapeYaml(value) { - return String(value).replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\r?\n/g, "\\n"); -} - -function sanitizeJob(value) { - return value.replace(/[^A-Za-z0-9_.-]+/g, "_"); -} - -function extractBetween(text, startMarker, endMarker) { - const start = text.indexOf(startMarker); - const end = text.indexOf(endMarker, start + startMarker.length); - if (start < 0 || end < 0 || end <= start) { - throw new Error(`Could not extract section ${startMarker}`); - } - return text.slice(start + startMarker.length, end); -} - -function avg(values) { - return values.length ? values.reduce((sum, value) => sum + value, 0) / values.length : 0; -} - -function round(value) { - return Number(value.toFixed(3)); -} - -async function writeJson(path, value) { - await mkdir(dirname(path), { recursive: true }); - await writeFile(path, `${JSON.stringify(value, null, 2)}\n`, "utf8"); -} - -function parseArgs(args) { - const parsed = {}; - for (let index = 0; index < args.length; index += 1) { - const arg = args[index]; - switch (arg) { - case "--real": - parsed.real = true; - break; - case "--workers": - parsed.workers = args[index + 1]; - index += 1; - break; - case "--agents": - parsed.agents = args[index + 1]; - index += 1; - break; - case "--runs-per-worker": - parsed.runsPerWorker = args[index + 1]; - index += 1; - break; - case "--seed": - parsed.seed = args[index + 1]; - index += 1; - break; - case "--run-id": - parsed.runId = args[index + 1]; - index += 1; - break; - case "--out-root": - parsed.outRoot = args[index + 1]; - index += 1; - break; - case "--archive": - parsed.archive = args[index + 1]; - index += 1; - break; - case "--author-id": - parsed.authorId = args[index + 1]; - index += 1; - break; - case "--before": - parsed.before = args[index + 1]; - index += 1; - break; - case "--adjacency-minutes": - parsed.adjacencyMinutes = args[index + 1]; - index += 1; - break; - case "--message-id": - parsed.messageId = args[index + 1]; - index += 1; - break; - case "--max-candidates": - parsed.maxCandidates = args[index + 1]; - index += 1; - break; - case "--initial-prompt-state": - parsed.initialPromptState = args[index + 1]; - index += 1; - break; - case "--affect-mode": - parsed.affectMode = args[index + 1]; - index += 1; - break; - default: - throw new Error(`Unknown argument: ${arg}`); - } - } - return parsed; -} diff --git a/scripts/start-weksa-daemon.ps1 b/scripts/start-weksa-daemon.ps1 deleted file mode 100644 index 40fa188..0000000 --- a/scripts/start-weksa-daemon.ps1 +++ /dev/null @@ -1,83 +0,0 @@ -param( - [int] $Port = 8813, - [string] $HostName = "127.0.0.1", - [string] $StateRoot = "E:\Projects\weksa\.weksa", - [string] $MiMoApiKeyPath = "E:\Projects\gamecult-ops\mimo-api-Weksa.txt", - [string] $IdunnRudpHealth = "127.0.0.1:17870", - [string] $IdunnDaemon = "weksa", - [string] $IdunnHealthContract = "weksa.cultnet-rudp-provider-health", - [string] $OdinCultMeshRudp = "127.0.0.1:17871" -) - -$ErrorActionPreference = "Stop" - -$repoRoot = Split-Path -Parent $PSScriptRoot -$pidPath = Join-Path $StateRoot "weksa-daemon.pid" -$outLog = Join-Path $StateRoot "weksa-daemon.out.log" -$errLog = Join-Path $StateRoot "weksa-daemon.err.log" - -New-Item -ItemType Directory -Force -Path $StateRoot | Out-Null -$cultLibPackages = Resolve-Path -LiteralPath (Join-Path $repoRoot "..\CultLib\packages") -$env:NODE_PATH = (@($cultLibPackages.Path, $env:NODE_PATH) | Where-Object { $_ }) -join [IO.Path]::PathSeparator - -if (Test-Path -LiteralPath $pidPath) { - $pidText = (Get-Content -LiteralPath $pidPath -Raw).Trim() - if ($pidText -match "^\d+$") { - $existing = Get-Process -Id ([int] $pidText) -ErrorAction SilentlyContinue - if ($null -ne $existing) { - try { - & node (Join-Path $repoRoot "scripts\weksa-daemon.mjs") --health --host $HostName --port $Port --state-root $StateRoot | Out-Null - if ($LASTEXITCODE -eq 0) { - Write-Host "Weksa daemon already healthy as PID $pidText." - return - } - } catch { - Stop-Process -Id $existing.Id -Force -ErrorAction SilentlyContinue - } - } - } - Remove-Item -LiteralPath $pidPath -Force -ErrorAction SilentlyContinue -} - -$arguments = @( - (Join-Path $repoRoot "scripts\weksa-daemon.mjs"), - "--host", $HostName, - "--port", [string] $Port, - "--state-root", $StateRoot, - "--mimo-api-key-path", $MiMoApiKeyPath, - "--idunn-rudp-health", $IdunnRudpHealth, - "--idunn-daemon", $IdunnDaemon, - "--idunn-health-contract", $IdunnHealthContract -) - -if (-not [string]::IsNullOrWhiteSpace($OdinCultMeshRudp)) { - $arguments += @("--odin-cultmesh-rudp", $OdinCultMeshRudp) -} - -$process = Start-Process -FilePath "node" ` - -ArgumentList $arguments ` - -WorkingDirectory $repoRoot ` - -WindowStyle Hidden ` - -PassThru ` - -RedirectStandardOutput $outLog ` - -RedirectStandardError $errLog - -$process.Id | Set-Content -Encoding ASCII -LiteralPath $pidPath - -Start-Sleep -Seconds 2 -if ($process.HasExited) { - $detail = "" - if (Test-Path -LiteralPath $outLog) { $detail += Get-Content -Raw -LiteralPath $outLog } - if (Test-Path -LiteralPath $errLog) { $detail += Get-Content -Raw -LiteralPath $errLog } - throw "Weksa daemon exited immediately with code $($process.ExitCode).`n$detail" -} - -& node (Join-Path $repoRoot "scripts\weksa-daemon.mjs") --health --host $HostName --port $Port --state-root $StateRoot | Out-Null -if ($LASTEXITCODE -ne 0) { - throw "Weksa daemon started but health check failed." -} - -Write-Host "Started Weksa daemon as PID $($process.Id)." -Write-Host " command: rudp://$HostName`:$Port" -Write-Host " Odin CultMesh/RUDP: $OdinCultMeshRudp" -Write-Host " state: $StateRoot" diff --git a/scripts/weksa-daemon.mjs b/scripts/weksa-daemon.mjs deleted file mode 100644 index fb63244..0000000 --- a/scripts/weksa-daemon.mjs +++ /dev/null @@ -1,1069 +0,0 @@ -#!/usr/bin/env node -import Module from "node:module"; -import { existsSync, readFileSync, readdirSync, statSync } from "node:fs"; -import { mkdir, readdir, rm, writeFile } from "node:fs/promises"; -import { basename, delimiter, dirname, extname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; - -const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); -configureCultLibModulePath(); -const require = Module.createRequire(import.meta.url); -const { - createIdunnRudpHealthPublisher, - publishIdunnRudpHealth, -} = require("./idunn-rudp-health.cjs"); -const { - CULTNET_RUDP_PROTOCOL_ID, - WEKSA_MIMO_VOICEDESIGN_COMMAND_SCHEMA_ID, - createWeksaRudpCommandServer, - sendWeksaMimoVoiceDesignCommand, - formatRudpEndpoint, -} = require("./weksa-rudp-command.cjs"); -const options = parseArgs(process.argv.slice(2)); -const port = Number(options.port ?? process.env.WEKSA_DAEMON_PORT ?? 8813); -const host = options.host ?? process.env.WEKSA_DAEMON_HOST ?? "127.0.0.1"; -const stateRoot = resolve(repoRoot, options.stateRoot ?? process.env.WEKSA_STATE_ROOT ?? ".weksa"); -const cultMeshStorePath = resolve(stateRoot, "provider-advertisement-store.cc"); -const pidPath = resolve(stateRoot, "weksa-daemon.pid"); -const logPath = resolve(stateRoot, "weksa-daemon.log"); -const mimoApiKeyPath = options.mimoApiKeyPath ?? process.env.WEKSA_MIMO_API_KEY_FILE ?? "E:/Projects/gamecult-ops/mimo-api-Weksa.txt"; -const idunnHealthPublisher = createIdunnRudpHealthPublisher({ - endpoint: options.idunnRudpHealth ?? process.env.WEKSA_IDUNN_RUDP_HEALTH, - daemonId: options.idunnDaemon ?? process.env.WEKSA_IDUNN_DAEMON ?? "weksa", - healthContract: options.idunnHealthContract ?? process.env.WEKSA_IDUNN_HEALTH_CONTRACT ?? "weksa.cultnet-rudp-provider-health", -}); -const odinCultMeshRudp = options.odinCultMeshRudp ?? process.env.WEKSA_ODIN_CULTMESH_RUDP; -const startedAt = new Date().toISOString(); -let tick = 0; -let lastPublishedAt = ""; -let lastError = undefined; -let cachedSnapshot = undefined; -let witnessRefreshRunning = false; -let lastRudpHealthSuccessMs = 0; - -if (options.health) { - await runHealthCheck(); - process.exit(0); -} -if (options.mimoCommandJson) { - const command = JSON.parse(readFileSync(resolve(repoRoot, options.mimoCommandJson), "utf8")); - const receipt = await sendWeksaMimoVoiceDesignCommand({ - endpoint: formatRudpEndpoint(host, port), - command, - runtimeId: "weksa-cli", - localHost: host.includes(":") ? "::" : "0.0.0.0", - }); - process.stdout.write(`${JSON.stringify(receipt, null, 2)}\n`); - process.exit(0); -} - -await mkdir(stateRoot, { recursive: true }); -await writeFile(pidPath, `${process.pid}\n`, "ascii"); -await appendLog(`started pid=${process.pid} host=${host} port=${port}`); -const startupSnapshot = await refreshWitnesses("startup"); -await publishOdinStartupRespect(startupSnapshot); -const commandServer = await createWeksaRudpCommandServer({ - host, - port, - runtimeId: "weksa-daemon", - onCommand: async (command) => { - const receipt = await handleMimoVoiceDesign(command); - await refreshWitnesses("mimo-voicedesign-command"); - return receipt; - }, - onError: async (error) => { - lastError = String(error?.stack ?? error); - await appendLog(`rudp command error: ${lastError}`); - }, -}); -await appendLog(`listening ${commandServer.uri}`); - -const interval = setInterval(() => { - refreshWitnesses("interval").catch(async (error) => { - lastError = String(error?.stack ?? error); - await appendLog(`publish error: ${lastError}`); - await publishWeksaRudpHealth("failed", `Weksa witness refresh failed: ${String(error?.message ?? error)}`); - }); -}, 30_000); - -process.on("SIGINT", () => shutdown(0)); -process.on("SIGTERM", () => shutdown(0)); - -async function shutdown(code) { - clearInterval(interval); - await appendLog(`stopping pid=${process.pid}`); - commandServer.close(); - process.exit(code); -} - -async function runHealthCheck() { - const operatorStatePath = resolve(stateRoot, "operator-state.cc"); - const commandBoundaryPath = resolve(stateRoot, "command-boundary.cc"); - const transportProfilePath = resolve(stateRoot, "transport-profile.cc"); - if (!existsSync(pidPath)) { - throw new Error(`Weksa daemon PID file is missing: ${pidPath}`); - } - if (!existsSync(operatorStatePath)) { - throw new Error(`Weksa operator-state witness is missing: ${operatorStatePath}`); - } - if (!existsSync(commandBoundaryPath)) { - throw new Error(`Weksa command-boundary witness is missing: ${commandBoundaryPath}`); - } - if (!existsSync(transportProfilePath)) { - throw new Error(`Weksa transport-profile witness is missing: ${transportProfilePath}`); - } - const pidText = readFileSync(pidPath, "utf8").trim(); - if (!/^\d+$/.test(pidText)) { - throw new Error(`Weksa daemon PID file is invalid: ${pidText}`); - } - const pid = Number(pidText); - try { - process.kill(pid, 0); - } catch { - throw new Error(`Weksa daemon PID ${pid} is not running.`); - } - const operatorStateText = readFileSync(operatorStatePath, "utf8"); - const commandBoundaryText = readFileSync(commandBoundaryPath, "utf8"); - const transportProfileText = readFileSync(transportProfilePath, "utf8"); - const ages = [operatorStatePath, commandBoundaryPath, transportProfilePath] - .map((path) => Math.floor((Date.now() - statSync(path).mtimeMs) / 1000)); - const ageSeconds = Math.max(...ages); - if (ageSeconds > 180) { - throw new Error(`Weksa daemon witnesses are stale (${ageSeconds}s old at oldest required path).`); - } - if (!operatorStateText.includes("command_ingress: cultnet_rudp_document_mimo_voicedesign")) { - throw new Error("Weksa operator-state witness does not report RUDP command ingress."); - } - if (!commandBoundaryText.includes(`ingress: ${CULTNET_RUDP_PROTOCOL_ID}`)) { - throw new Error("Weksa command-boundary witness does not report CultNet/RUDP ingress."); - } - if (!commandBoundaryText.includes(`input_schema: ${WEKSA_MIMO_VOICEDESIGN_COMMAND_SCHEMA_ID}`)) { - throw new Error("Weksa command-boundary witness does not report the MiMo command document schema."); - } - if (!transportProfileText.includes(`command_transport: ${CULTNET_RUDP_PROTOCOL_ID}`)) { - throw new Error("Weksa transport-profile witness does not report CultNet/RUDP command transport."); - } - process.stdout.write(`${JSON.stringify({ - ok: true, - service: "weksa.intent.service", - state: "healthy", - pid, - ageSeconds, - commandEndpoint: formatRudpEndpoint(host, port), - }, null, 2)}\n`); -} - -async function publishWitnesses() { - const snapshot = await buildSnapshot(); - await writeWitness("provider-advertisement.cc", snapshot.providerAdvertisement); - await writeWitness("operator-state.cc", snapshot.operatorState); - await writeWitness("command-boundary.cc", snapshot.commandBoundary); - await writeWitness("transport-profile.cc", snapshot.transportProfile); - await writeWitness("cultmesh-publications.cc", snapshot.cultMeshPublications); - await writeCultMeshStore(snapshot); - await writeJson(resolve(stateRoot, "provider-advertisement.json"), snapshot.providerAdvertisement); - await writeJson(resolve(stateRoot, "operator-state.json"), snapshot.operatorState); - await writeJson(resolve(stateRoot, "command-boundary.json"), snapshot.commandBoundary); - await writeJson(resolve(stateRoot, "transport-profile.json"), snapshot.transportProfile); - await writeJson(resolve(stateRoot, "cultmesh-publications.json"), snapshot.cultMeshPublications); - cachedSnapshot = snapshot; - lastPublishedAt = snapshot.operatorState.updated_at; - tick += 1; - return snapshot; -} - -async function refreshWitnesses(reason) { - if (witnessRefreshRunning) { - await appendLog(`skipped overlapping witness refresh reason=${reason}`); - return cachedSnapshot; - } - witnessRefreshRunning = true; - try { - const snapshot = await publishWitnesses(); - await publishWeksaRudpHealth( - "healthy", - `Weksa refreshed witnesses reason=${reason} tick=${tick} provider=${snapshot.providerAdvertisement.providerId}`, - snapshot.operatorState.updated_at, - ); - return snapshot; - } finally { - witnessRefreshRunning = false; - } -} - -async function publishWeksaRudpHealth(state, detail, observedAt = new Date().toISOString()) { - if (!idunnHealthPublisher) return; - try { - await publishIdunnRudpHealth(idunnHealthPublisher, { state, detail, observedAt }); - lastRudpHealthSuccessMs = Date.now(); - } catch (error) { - const hasRecentSuccess = lastRudpHealthSuccessMs > 0 && Date.now() - lastRudpHealthSuccessMs < 300_000; - if (!hasRecentSuccess) { - await appendLog(`Idunn RUDP health publish failed: ${String(error?.message ?? error)}`); - } - } -} - -async function publishOdinStartupRespect(snapshot) { - if (!odinCultMeshRudp || !snapshot?.providerAdvertisement) return; - const runtime = loadCultRuntime(); - if (runtime.error) { - await appendLog(`Odin startup respect skipped: ${runtime.error.message}`); - return; - } - try { - const { providerAdvertisementDefinition } = defineCultMeshDocuments(runtime.defineDocumentType); - await runtime.CultMesh.publishRudpDocumentOnce( - idunnHealthPublisher?.daemonId ?? "weksa", - 0x0d1d0002, - normalizeRudpEndpoint(odinCultMeshRudp), - runtime.defineCultNetDocumentBinding({ definition: providerAdvertisementDefinition }), - snapshot.providerAdvertisement.providerId, - snapshot.providerAdvertisement, - { - sourceRole: "weksa.provider", - tags: ["startup-respect", "odin-verse-discovery"], - }, - ); - await appendLog(`Odin startup respect published to ${odinCultMeshRudp}`); - } catch (error) { - await appendLog(`Odin startup respect failed: ${String(error?.message ?? error)}`); - } -} - -async function buildSnapshot() { - const updatedAt = new Date().toISOString(); - const providerAdvertisement = loadProviderAdvertisement(updatedAt); - const tuning = loadLatestAffectTuning(); - const cultureCatalog = await inspectCultureCatalog(); - const operatorState = { - schema: "weksa.operator_state.v0", - service_id: "weksa.intent.service", - provider_id: providerAdvertisement.providerId, - verse_id: "asgard.starfire.weksa", - state: "healthy", - mode: "daemon", - pid: process.pid, - host, - port, - started_at: startedAt, - updated_at: updatedAt, - last_error: lastError, - witnesses: { - provider_advertisement: ".weksa/provider-advertisement.cc", - provider_advertisement_store: ".weksa/provider-advertisement-store.cc", - operator_state: ".weksa/operator-state.cc", - cultmesh_publications: ".weksa/cultmesh-publications.cc", - }, - readiness: { - affect_aware_lowering: tuning ? "tuned" : "no_tuning_artifact_seen", - cultural_ontology_profiles: cultureCatalog.profile_count, - target_language_profiles: cultureCatalog.target_language_profile_count, - accepts_commands: true, - command_ingress: "cultnet_rudp_document_mimo_voicedesign", - mimo_voicedesign: existsSync(mimoApiKeyPath) ? "api_key_file_seen" : "missing_api_key_file", - }, - latest_affect_tuning: tuning, - }; - const commandBoundary = buildCommandBoundary(updatedAt); - const transportProfile = buildTransportProfile(updatedAt); - const cultMeshPublications = { - schema: "weksa.cultmesh_publications.v0", - service_id: "weksa.intent.service", - updated_at: updatedAt, - transport: { - cultmesh_store: ".weksa/provider-advertisement-store.cc", - native_cultnet_peer: formatRudpEndpoint(host, port), - idunn_rudp_health: idunnHealthPublisher ? `${idunnHealthPublisher.endpoint.host}:${idunnHealthPublisher.endpoint.port}` : "unconfigured", - }, - publications: [ - publication("weksa.service/provider-advertisement", "gamecult.eve.provider_advertisement.v1", ".weksa/provider-advertisement-store.cc", ".weksa/provider-advertisement-store.cc"), - publication("weksa.operator/status", "weksa.operator_state.v0", ".weksa/operator-state.cc", ".weksa/operator-state.cc"), - publication("weksa.command-boundary", "weksa.command_boundary.v1", ".weksa/command-boundary.cc", null), - publication("weksa.transport-profile", "weksa.transport_profile.v1", ".weksa/transport-profile.cc", null), - publication("weksa.cultmesh/publications", "weksa.cultmesh_publications.v0", ".weksa/cultmesh-publications.cc", ".weksa/cultmesh-publications.cc"), - publication("weksa.speech-provider/mimo/voicedesign", WEKSA_MIMO_VOICEDESIGN_COMMAND_SCHEMA_ID, ".weksa/speech-provider/mimo/{requestId}/command.cc", formatRudpEndpoint(host, port)), - ], - }; - return { providerAdvertisement, operatorState, commandBoundary, transportProfile, cultMeshPublications }; -} - -async function writeCultMeshStore(snapshot) { - const runtime = loadCultRuntime(); - if (runtime.error) { - await appendLog(`CultMesh store write skipped: ${runtime.error.message}`); - return; - } - const { - providerAdvertisementDefinition, - operatorStateDefinition, - commandBoundaryDefinition, - transportProfileDefinition, - } = defineCultMeshDocuments(runtime.defineDocumentType); - const documents = [ - providerAdvertisementDefinition, - operatorStateDefinition, - commandBoundaryDefinition, - transportProfileDefinition, - ]; - await rm(cultMeshStorePath, { force: true }); - const node = await createCultMeshNodeWithOwnedStoreReset(runtime.CultMesh, cultMeshStorePath, documents); - await node.put(providerAdvertisementDefinition, snapshot.providerAdvertisement.providerId, snapshot.providerAdvertisement); - await node.put(operatorStateDefinition, "weksa", snapshot.operatorState); - await node.put(commandBoundaryDefinition, "weksa", snapshot.commandBoundary); - await node.put(transportProfileDefinition, "weksa", snapshot.transportProfile); - await node.flush?.(true); -} - -async function createCultMeshNodeWithOwnedStoreReset(CultMesh, storePath, documents) { - try { - return await CultMesh.createNode(storePath, { documents }); - } catch (error) { - await appendLog(`CultMesh store reset after decode failure: ${error.message}`); - await rm(storePath, { force: true }); - return await CultMesh.createNode(storePath, { documents }); - } -} - -function loadCultRuntime() { - try { - configureCultLibModulePath(); - const { CultMesh } = require("cultmesh-ts"); - const { defineDocumentType } = require("cultcache-ts"); - const { defineCultNetDocumentBinding } = require("cultnet-ts"); - return { CultMesh, defineDocumentType, defineCultNetDocumentBinding, error: null }; - } catch (error) { - return { CultMesh: null, defineDocumentType: null, defineCultNetDocumentBinding: null, error }; - } -} - -function normalizeRudpEndpoint(endpoint) { - const text = String(endpoint || "").trim(); - if (!text) { - throw new Error("Odin CultMesh/RUDP endpoint must be non-empty."); - } - return text.toLowerCase().startsWith("rudp://") ? text : `rudp://${text}`; -} - -function configureCultLibModulePath() { - process.env.NODE_PATH = [ - resolve(repoRoot, "..", "CultLib", "packages"), - process.env.NODE_PATH || "", - ].filter(Boolean).join(delimiter); - Module._initPaths(); -} - -function defineCultMeshDocuments(defineDocumentType) { - const providerAdvertisementDefinition = defineDocumentType({ - type: "gamecult.eve.provider_advertisement", - schemaName: "gamecult.eve.provider_advertisement", - schemaId: "gamecult.eve.provider_advertisement.v1", - schemaVersion: "gamecult.eve.provider_advertisement.v1", - global: false, - name: (value) => value?.providerId || "provider", - schema: { parse: (value) => value }, - }); - const operatorStateDefinition = defineDocumentType({ - type: "weksa.operator_state", - schemaName: "weksa.operator_state", - schemaId: "weksa.operator_state.v0", - schemaVersion: "weksa.operator_state.v0", - global: false, - name: (value) => value?.daemon_id || value?.provider_id || "weksa", - schema: { parse: (value) => value }, - }); - const commandBoundaryDefinition = defineDocumentType({ - type: "weksa.command_boundary", - schemaName: "weksa.command_boundary", - schemaId: "weksa.command_boundary.v1", - schemaVersion: "weksa.command_boundary.v1", - global: false, - name: (value) => value?.boundary_id || value?.daemon_id || "weksa", - schema: { parse: (value) => value }, - }); - const transportProfileDefinition = defineDocumentType({ - type: "weksa.transport_profile", - schemaName: "weksa.transport_profile", - schemaId: "weksa.transport_profile.v1", - schemaVersion: "weksa.transport_profile.v1", - global: false, - name: (value) => value?.profile_id || value?.daemon_id || "weksa", - schema: { parse: (value) => value }, - }); - return { - providerAdvertisementDefinition, - operatorStateDefinition, - commandBoundaryDefinition, - transportProfileDefinition, - }; -} - -function loadProviderAdvertisement(updatedAt) { - const fixturePath = resolve(repoRoot, "docs/fixtures/weksa-provider-advertisement.json"); - const fixture = JSON.parse(readFileSync(fixturePath, "utf8")); - return { - ...fixture, - mode: "daemon-live", - status: "active", - daemonId: "weksa", - updatedAt, - canonicalService: "asgard.weksa", - locatedService: "asgard.starfire.weksa", - cultMeshAddress: "asgard.starfire.weksa/operator", - serviceVerse: { - ...fixture.serviceVerse, - semanticAddress: "asgard.starfire.weksa", - cultMeshKey: "weksa.service/provider-advertisement", - }, - endpoints: [ - { transport: "cultmesh-store", address: ".weksa/provider-advertisement-store.cc" }, - { transport: CULTNET_RUDP_PROTOCOL_ID, address: formatRudpEndpoint(host, port) }, - ], - routes: [ - { transport: "cultmesh-store", address: ".weksa/provider-advertisement-store.cc" }, - { transport: CULTNET_RUDP_PROTOCOL_ID, address: formatRudpEndpoint(host, port) }, - ], - health: { - state: "healthy", - endpoint: idunnHealthPublisher ? `${CULTNET_RUDP_PROTOCOL_ID}:idunn-health` : "unconfigured", - witness: ".weksa/operator-state.cc", - checkedBy: "idunn.desired_daemon.v1", - }, - }; -} - -function buildCommandBoundary(updatedAt) { - return { - schema: "weksa.command_boundary.v1", - boundary_id: "weksa", - daemon_id: "weksa", - provider_id: "weksa.intent.service", - service_id: "weksa.intent.service", - updated_at: updatedAt, - owner: "Weksa daemon", - lifecycle_authority: "idunn.local-command", - health_publication: { - contract: "weksa.cultnet-rudp-provider-health", - transport: CULTNET_RUDP_PROTOCOL_ID, - publication_source: "daemon-published", - state_owner: "Weksa daemon", - }, - commands: [ - { - command: "speech_provider.mimo.voicedesign", - ingress: CULTNET_RUDP_PROTOCOL_ID, - endpoint: formatRudpEndpoint(host, port), - owner: "Weksa daemon", - input_schema: WEKSA_MIMO_VOICEDESIGN_COMMAND_SCHEMA_ID, - output_schema: "weksa.mimo_voicedesign_receipt.v0", - authority: "Weksa accepts the request, commits typed witnesses, and treats MiMo as an external renderer.", - }, - ], - forbidden_writers: [ - "RUDP command senders may request commands but do not own Weksa intent state.", - "Odin, Eve, and renderers may lower Weksa surfaces but do not mutate command authority.", - ], - compatibility: { - http: null, - status: "no-http-command-lane", - cut_line: "The HTTP command route is deleted; Weksa accepts speech_provider.mimo.voicedesign over CultNet/RUDP command documents and keeps typed witnesses as truth.", - }, - }; -} - -function buildTransportProfile(updatedAt) { - return { - schema: "weksa.transport_profile.v1", - profile_id: "weksa", - daemon_id: "weksa", - provider_id: "weksa.intent.service", - updated_at: updatedAt, - target_transport: CULTNET_RUDP_PROTOCOL_ID, - current_transport: idunnHealthPublisher - ? "rudp-health-and-cultmesh-store-and-rudp-command" - : "cultmesh-store-and-rudp-command", - health_transport: idunnHealthPublisher ? CULTNET_RUDP_PROTOCOL_ID : "unconfigured", - state_transport: "cultmesh-store", - command_transport: CULTNET_RUDP_PROTOCOL_ID, - compatibility: { - http: null, - endpoints_are_lowerings: false, - }, - cut_line: "Weksa health, provider state, and MiMo VoiceDesign command ingress are daemon-owned typed records over CultNet/RUDP plus CultMesh store; HTTP is no longer in the daemon stack.", - }; -} - -function loadLatestAffectTuning() { - const root = resolve(repoRoot, ".weksa-runs/metame-affect-tuning"); - if (!existsSync(root)) { - return undefined; - } - const runs = readdirSyncSafe(root) - .map((name) => ({ name, path: resolve(root, name, "status.json") })) - .filter((entry) => existsSync(entry.path)) - .map((entry) => { - try { - const status = JSON.parse(readFileSync(entry.path, "utf8")); - return { entry, status }; - } catch { - return undefined; - } - }) - .filter(Boolean) - .sort((left, right) => String(right.status.completed_at ?? right.status.started_at ?? "").localeCompare(String(left.status.completed_at ?? left.status.started_at ?? ""))); - const latest = runs[0]?.status; - if (!latest) { - return undefined; - } - const observations = Array.isArray(latest.observations) ? latest.observations : []; - const visible = observations.filter((entry) => entry?.evaluation?.verdict === "affect_contrast_visible").length; - const needsTuning = observations.filter((entry) => entry?.evaluation?.verdict === "needs_tuning").length; - return { - run_id: latest.run_id, - status: latest.status, - pass_count: latest.pass_count, - completed_at: latest.completed_at, - current_prompt_state: latest.current_prompt_state, - affect_contrast_visible: visible, - needs_tuning: needsTuning, - }; -} - -async function inspectCultureCatalog() { - const culturalRoot = resolve(repoRoot, "data/cultural-ontology"); - const targetRoot = resolve(repoRoot, "data/target-language-ontology"); - const culturalProfiles = await collectFiles(culturalRoot, ".yaml"); - const targetProfiles = await collectFiles(targetRoot, ".yaml"); - return { - profile_count: culturalProfiles.length, - target_language_profile_count: targetProfiles.length, - cultural_root: "data/cultural-ontology", - target_language_root: "data/target-language-ontology", - }; -} - -async function handleMimoVoiceDesign(body) { - const command = validateMimoCommand(body); - const requestId = command.request_id || `mimo-${new Date().toISOString().replace(/[-:.TZ]/g, "")}-${Math.random().toString(36).slice(2, 8)}`; - const outputRoot = resolve(stateRoot, "speech-provider", "mimo", requestId); - await mkdir(outputRoot, { recursive: true }); - const commandDocument = { - ...command, - schema_version: WEKSA_MIMO_VOICEDESIGN_COMMAND_SCHEMA_ID, - request_id: requestId, - }; - - const personaEvidence = inspectPersonaState(commandDocument.persona_state_path); - const interlinguaPacket = commandDocument.interlingua_packet - ? normalizeInterlinguaPacket(commandDocument.interlingua_packet, requestId) - : decomposeThoughtIntoInterlingua(commandDocument.thought_text, requestId, personaEvidence, commandDocument); - const targetRealization = lowerInterlinguaToSpeechText(interlinguaPacket, personaEvidence, commandDocument); - const mimoRequest = buildMimoVoiceDesignRequest({ requestId, command: commandDocument, personaEvidence, interlinguaPacket, targetRealization }); - const providerResponse = await callMimoVoiceDesign(mimoRequest); - const audioPath = resolve(outputRoot, `${requestId}.wav`); - await writeFile(audioPath, providerResponse.audioBytes); - - const receipt = { - schema_version: "weksa.mimo_voicedesign_receipt.v0", - request_id: requestId, - generated_at: new Date().toISOString(), - ok: true, - provider: "xiaomi-mimo", - model: "mimo-v2.5-tts-voicedesign", - persona_state_ref: personaEvidence.path, - source_kind: commandDocument.interlingua_packet ? "interlingua_packet" : "thought_text_decomposed_to_interlingua", - target_language: targetRealization.target_language, - performance_register: targetRealization.performance_register, - artifacts: { - command: `.weksa/speech-provider/mimo/${requestId}/command.cc`, - interlingua_packet: `.weksa/speech-provider/mimo/${requestId}/interlingua.cc`, - mimo_request: `.weksa/speech-provider/mimo/${requestId}/mimo-request.cc`, - receipt: `.weksa/speech-provider/mimo/${requestId}/receipt.cc`, - audio: `.weksa/speech-provider/mimo/${requestId}/${requestId}.wav`, - }, - provider_response: { - id: providerResponse.id, - created: providerResponse.created, - audio_bytes: providerResponse.audioBytes.length, - }, - trace: { - authority: "Weksa created or accepted interlingua, projected delivery controls, and exported a MiMo VoiceDesign request. MiMo rendered audio only.", - persona_state_read: personaEvidence.read_status, - persona_delivery_hints: personaEvidence.delivery_hints, - text_preview_optimization: false, - notes: targetRealization.trace.notes, - }, - }; - - await writeWitness(`speech-provider/mimo/${requestId}/command.cc`, commandDocument); - await writeWitness(`speech-provider/mimo/${requestId}/interlingua.cc`, interlinguaPacket); - await writeWitness(`speech-provider/mimo/${requestId}/mimo-request.cc`, mimoRequest); - await writeWitness(`speech-provider/mimo/${requestId}/receipt.cc`, receipt); - await writeJson(resolve(outputRoot, "command.json"), commandDocument); - await writeJson(resolve(outputRoot, "interlingua.json"), interlinguaPacket); - await writeJson(resolve(outputRoot, "mimo-request.json"), mimoRequest); - await writeJson(resolve(outputRoot, "receipt.json"), receipt); - await appendLog(`mimo voicedesign request_id=${requestId} audio_bytes=${providerResponse.audioBytes.length}`); - return receipt; -} - -function validateMimoCommand(body) { - if (!body || typeof body !== "object" || Array.isArray(body)) { - throw httpError(400, "request body must be a JSON object"); - } - if (!body.persona_state_path || typeof body.persona_state_path !== "string") { - throw httpError(400, "persona_state_path is required"); - } - if (!body.interlingua_packet && !body.thought_text) { - throw httpError(400, "provide interlingua_packet or thought_text"); - } - if (body.interlingua_packet && body.thought_text) { - throw httpError(400, "provide either interlingua_packet or thought_text, not both"); - } - if (body.thought_text && typeof body.thought_text !== "string") { - throw httpError(400, "thought_text must be a string"); - } - return body; -} - -function inspectPersonaState(personaStatePath) { - const absolutePath = resolve(repoRoot, personaStatePath); - if (!existsSync(absolutePath)) { - throw httpError(400, `persona_state_path does not exist: ${personaStatePath}`); - } - const bytes = readFileSync(absolutePath); - const printable = Array.from(bytes.subarray(0, Math.min(bytes.length, 4096))).filter((byte) => byte === 9 || byte === 10 || byte === 13 || (byte >= 32 && byte <= 126)).length; - const printableRatio = bytes.length ? printable / Math.min(bytes.length, 4096) : 0; - const textPreview = printableRatio > 0.75 ? bytes.toString("utf8", 0, Math.min(bytes.length, 4096)) : ""; - const extractedStrings = extractPersonaStrings(bytes); - const deliveryHints = derivePersonaDeliveryHints(extractedStrings, basename(absolutePath, extname(absolutePath))); - return { - path: absolutePath, - basename: basename(absolutePath, extname(absolutePath)), - byte_length: bytes.length, - read_status: extractedStrings.length ? "string_evidence_extracted" : (printableRatio > 0.75 ? "text_preview_available" : "binary_or_compact_state_reference_only"), - text_preview: textPreview, - delivery_hints: deliveryHints, - }; -} - -function extractPersonaStrings(bytes) { - const strings = []; - let current = ""; - for (const byte of bytes) { - if (byte >= 32 && byte <= 126) { - current += String.fromCharCode(byte); - } else { - if (current.length >= 8) strings.push(current); - current = ""; - } - } - if (current.length >= 8) strings.push(current); - return strings - .filter((text) => /voice|style|presentation|affect|persona|pronoun|she\/her|feminine|woman|female|ship|companion|anime|loli|waifu|sharp|abrasive|technical|bright|cute|hostile|cruel|contempt|protect|dependency|leverage/i.test(text)) - .slice(0, 24); -} - -function derivePersonaDeliveryHints(strings, personaId) { - const joined = strings.join("\n").toLowerCase(); - const hints = []; - const addIfSeen = (pattern, hint) => { - if (pattern.test(joined) && !hints.includes(hint)) hints.push(hint); - }; - addIfSeen(/loli|waifu|companion-shaped|companion product|companion shell/, "youth-coded/loli-coded companion-product presentation as non-sexual product residue"); - addIfSeen(/she\/her|pronoun guidance|feminine|female|woman/, "explicitly feminine she/her voice; do not default masculine"); - addIfSeen(/anime|visual novel/, "anime / visual-novel heroine performance register"); - addIfSeen(/ship mind|ship ai|ship-self|embodied ship|cockpit/, "embodied ship intelligence speaking through ship audio"); - addIfSeen(/abrasive|cutting|bite|cruel|sadistic/, "abrasive, cutting delivery with aim rather than random cruelty"); - addIfSeen(/technical|precision|engineering|mechanism/, "technical precision inside character performance"); - addIfSeen(/bright/, "bright sharp timbre"); - addIfSeen(/dependency|leverage|life support|bargain/, "dependency covered by contempt and leverage"); - addIfSeen(/protect|warning|useful/, "irritated protection rather than soft affection"); - if (!hints.length) hints.push(`${personaId} projected Persona voice`); - return hints.slice(0, 10); -} - -function normalizeInterlinguaPacket(packet, requestId) { - if (typeof packet === "string") { - try { - return { ...JSON.parse(packet), packet_id: requestId }; - } catch { - return { - interlingua_version: "0.1-daemon-provisional", - packet_id: requestId, - kind: "dialogue_line", - provenance: { source_type: "opaque_interlingua_text", confidence: 0.45 }, - discourse: { speech_act: "verbalize", intent: packet.slice(0, 240), tone: "unspecified" }, - trace: { notes: ["Interlingua arrived as opaque text; daemon preserved it as intent text rather than parsing YAML in the hot path."] }, - }; - } - } - return { - interlingua_version: packet.interlingua_version ?? "0.1-daemon-provisional", - packet_id: packet.packet_id ?? requestId, - kind: packet.kind ?? "dialogue_line", - ...packet, - }; -} - -function decomposeThoughtIntoInterlingua(thoughtText, requestId, personaEvidence, command) { - const trimmed = thoughtText.trim(); - return { - interlingua_version: "0.1-daemon-provisional", - packet_id: requestId, - kind: "dialogue_line", - provenance: { - source_type: "thought_text", - confidence: 0.55, - warning: "Heuristic daemon decomposition; review before promoting as canonical interlingua.", - }, - context: { - medium: "spoken verbalization", - scene: command.scene ?? "unspecified", - }, - discourse: { - speech_act: command.speech_act ?? inferSpeechAct(trimmed), - intent: command.intent ?? trimmed, - tone: command.tone ?? "persona_projected", - addressee: command.addressee ?? "listener", - }, - referents: [ - { id: "speaker", kind: "person", label: personaEvidence.basename, salience: "high" }, - { id: "listener", kind: "person", label: command.addressee ?? "listener", salience: "medium" }, - ], - predications: [ - { - id: "verbalized-thought", - predicate: "express", - frame: "thought_to_speech", - roles: { agent: "speaker", content: "thought_text", addressee: "listener" }, - }, - ], - constraints: { - preserve_text_intent: true, - required_outputs: ["spoken_text"], - deterministic: false, - }, - extensions: { - "weksa.raw_thought": { - text: trimmed, - }, - }, - trace: { - notes: [ - "Raw thought was converted into provisional interlingua by Weksa daemon command ingress.", - "This path is for verbalization service use; a reviewer or authoring model should promote durable interlingua if the line becomes canon.", - ], - }, - }; -} - -function inferSpeechAct(text) { - if (/^(do not|don't|stop|never|take your hand off|hands off|get away from)\b/i.test(text)) return "warning"; - if (/\?$/.test(text)) return "question"; - if (/^(please|could|can you)\b/i.test(text)) return "request"; - return "statement"; -} - -function lowerInterlinguaToSpeechText(interlinguaPacket, personaEvidence, command) { - const targetLanguage = { - code: command.target_language?.code ?? "en", - locale: command.target_language?.locale ?? "en-US", - script: command.target_language?.script ?? "Latn", - register: command.target_language?.register ?? "persona-shaped spoken", - cultural_ontology_refs: command.target_language?.cultural_ontology_refs ?? ["data/target-language-ontology/en-US.yaml"], - }; - const performanceRegister = { - label: command.performance_register?.label ?? "persona verbalization", - medium: command.performance_register?.medium ?? "spoken character voiceover", - delivery_archetype: command.performance_register?.delivery_archetype ?? `${personaEvidence.basename} verbalizes projected intent`, - }; - const spokenText = command.spoken_text - ?? interlinguaPacket.outputs?.spoken_text - ?? interlinguaPacket.extensions?.weksa?.spoken_text - ?? interlinguaPacket.extensions?.["weksa.raw_thought"]?.text - ?? interlinguaPacket.discourse?.intent - ?? ""; - if (!spokenText.trim()) { - throw httpError(400, "could not derive spoken text from command or interlingua packet"); - } - return { - realization_id: `${interlinguaPacket.packet_id ?? "packet"}-${targetLanguage.locale}-mimo`, - target_language: targetLanguage, - performance_register: performanceRegister, - outputs: { - spoken_text: spokenText.trim(), - private_interpretation: command.private_interpretation ?? "Speaker verbalizes the supplied intent through projected Persona context.", - intended_effect: command.intended_effect ?? "Make the supplied intent audible in the speaker's voice.", - }, - trace: { - notes: [ - "Daemon lowering preserved supplied spoken_text when present; otherwise it used raw thought or interlingua discourse intent.", - "Target-language register stays separate from provider-facing performance register.", - ], - }, - }; -} - -function buildMimoVoiceDesignRequest({ requestId, command, personaEvidence, interlinguaPacket, targetRealization }) { - const voiceDesignInstruction = command.voice_design_prompt - ?? buildVoiceDesignPrompt({ command, personaEvidence, targetRealization }); - const sourceControls = [ - ...personaEvidence.delivery_hints, - ...(Array.isArray(command.delivery_controls) ? command.delivery_controls : []), - ].filter((value, index, array) => value && array.indexOf(value) === index); - return { - schema_version: "weksa.mimo_tts_request.v0", - request_id: requestId, - source_realization_ref: `${interlinguaPacket.packet_id ?? requestId}#${targetRealization.realization_id}`, - speaker_agent_id: command.speaker_agent_id ?? personaEvidence.basename, - persona_state_ref: personaEvidence.path, - provider: { - id: "xiaomi-mimo", - model: "mimo-v2.5-tts-voicedesign", - }, - target_language: targetRealization.target_language, - performance_register: targetRealization.performance_register, - voice_design: { - description: voiceDesignInstruction, - source_controls: sourceControls, - projected_context_refs: [personaEvidence.path], - forbidden_traits: command.forbidden_traits ?? ["generic assistant voice", "provider rewriting source meaning"], - }, - messages: { - user_voice_design_instruction: voiceDesignInstruction, - assistant_spoken_content: command.provider_tags - ? `${command.provider_tags}${targetRealization.outputs.spoken_text}` - : targetRealization.outputs.spoken_text, - }, - audio: { - format: "wav", - optimize_text_preview: false, - streaming: false, - }, - trace: { - source_fields: command.interlingua_packet ? ["interlingua_packet"] : ["thought_text", "provisional_interlingua"], - delivery_controls_used: sourceControls, - provider_tags_used: command.provider_tags ? [command.provider_tags] : [], - uncertainties: [ - "VoiceDesign prompt is a lossy provider projection from Weksa-owned delivery intent.", - ], - }, - }; -} - -function buildVoiceDesignPrompt({ command, personaEvidence, targetRealization }) { - const combinedControls = [ - ...personaEvidence.delivery_hints, - ...(Array.isArray(command.delivery_controls) ? command.delivery_controls : []), - ].filter((value, index, array) => value && array.indexOf(value) === index); - const controls = combinedControls.length - ? combinedControls.join(", ") - : "projected Persona voice, clear character delivery, preserve source intent"; - const forbidden = Array.isArray(command.forbidden_traits) && command.forbidden_traits.length - ? ` Avoid ${command.forbidden_traits.join(", ")}.` - : " Avoid generic assistant voice and do not rewrite the spoken text."; - return `${personaEvidence.basename} voice for ${targetRealization.performance_register.label}: ${controls}.${forbidden}`; -} - -async function callMimoVoiceDesign(mimoRequest) { - if (options.mimoDryRun) { - return { - id: "weksa-mimo-dry-run", - created: new Date().toISOString(), - audioBytes: Buffer.from("RIFF0000WAVEfmt ", "ascii"), - }; - } - if (!existsSync(mimoApiKeyPath)) { - throw httpError(500, `MiMo API key file is missing: ${mimoApiKeyPath}`); - } - const apiKey = readFileSync(mimoApiKeyPath, "utf8").trim(); - const providerBody = { - model: "mimo-v2.5-tts-voicedesign", - messages: [ - { role: "user", content: mimoRequest.messages.user_voice_design_instruction }, - { role: "assistant", content: mimoRequest.messages.assistant_spoken_content }, - ], - audio: { - format: "wav", - optimize_text_preview: false, - }, - }; - const providerResponse = await fetch("https://api.xiaomimimo.com/v1/chat/completions", { - method: "POST", - headers: { - "api-key": apiKey, - "Content-Type": "application/json", - }, - body: JSON.stringify(providerBody), - }); - const text = await providerResponse.text(); - if (!providerResponse.ok) { - throw httpError(502, `MiMo VoiceDesign failed with HTTP ${providerResponse.status}: ${redactProviderError(text)}`); - } - const json = JSON.parse(text); - const audioData = json.choices?.[0]?.message?.audio?.data; - if (!audioData) { - throw httpError(502, "MiMo VoiceDesign response did not include audio data"); - } - return { - id: json.id ?? null, - created: json.created ?? null, - audioBytes: Buffer.from(audioData, "base64"), - }; -} - -function publication(key, schema, witness, endpoint) { - return { - key, - schema, - witness, - endpoint, - authority: "Weksa daemon", - }; -} - -async function writeWitness(name, value) { - const path = resolve(stateRoot, name); - await mkdir(dirname(path), { recursive: true }); - await writeFile(path, `${toCultCacheText(value)}\n`, "utf8"); -} - -function toCultCacheText(value) { - return renderYaml(value).trim(); -} - -function renderYaml(value, indent = 0) { - const pad = " ".repeat(indent); - if (Array.isArray(value)) { - if (!value.length) return "[]"; - return value.map((item) => { - if (item && typeof item === "object") { - return `${pad}-\n${renderYaml(item, indent + 2)}`; - } - return `${pad}- ${yamlScalar(item)}`; - }).join("\n"); - } - if (value && typeof value === "object") { - return Object.entries(value) - .filter(([, entryValue]) => entryValue !== undefined) - .map(([key, entryValue]) => { - if (entryValue && typeof entryValue === "object") { - return `${pad}${key}:\n${renderYaml(entryValue, indent + 2)}`; - } - return `${pad}${key}: ${yamlScalar(entryValue)}`; - }) - .join("\n"); - } - return `${pad}${yamlScalar(value)}`; -} - -function yamlScalar(value) { - if (value === null) return "null"; - if (typeof value === "number" || typeof value === "boolean") return String(value); - const text = String(value); - if (!text || /[:#\n\r\t{}[\],&*?|-]|^\s|\s$/.test(text)) { - return JSON.stringify(text); - } - return text; -} - -async function collectFiles(root, extension) { - if (!existsSync(root)) { - return []; - } - const entries = await readdir(root, { withFileTypes: true }); - const results = []; - for (const entry of entries) { - const child = resolve(root, entry.name); - if (entry.isDirectory()) { - results.push(...await collectFiles(child, extension)); - } else if (entry.name.endsWith(extension)) { - results.push(child); - } - } - return results; -} - -function readdirSyncSafe(path) { - try { - return existsSync(path) ? readdirSync(path) : []; - } catch { - return []; - } -} - -async function writeJson(path, value) { - await mkdir(dirname(path), { recursive: true }); - await writeFile(path, `${JSON.stringify(value, null, 2)}\n`, "utf8"); -} - -function httpError(statusCode, message) { - const error = new Error(message); - error.statusCode = statusCode; - return error; -} - -function redactProviderError(text) { - return String(text).replace(/[A-Za-z0-9_-]{32,}/g, "[redacted]"); -} - -async function appendLog(line) { - await mkdir(dirname(logPath), { recursive: true }); - const previous = existsSync(logPath) ? readFileSync(logPath, "utf8") : ""; - await writeFile(logPath, `${previous}[${new Date().toISOString()}] ${line}\n`, "utf8"); -} - -function parseArgs(args) { - const parsed = {}; - for (let index = 0; index < args.length; index += 1) { - const arg = args[index]; - switch (arg) { - case "--host": - parsed.host = args[index + 1]; - index += 1; - break; - case "--port": - parsed.port = args[index + 1]; - index += 1; - break; - case "--state-root": - parsed.stateRoot = args[index + 1]; - index += 1; - break; - case "--mimo-api-key-path": - parsed.mimoApiKeyPath = args[index + 1]; - index += 1; - break; - case "--idunn-rudp-health": - parsed.idunnRudpHealth = args[index + 1]; - index += 1; - break; - case "--idunn-daemon": - parsed.idunnDaemon = args[index + 1]; - index += 1; - break; - case "--idunn-health-contract": - parsed.idunnHealthContract = args[index + 1]; - index += 1; - break; - case "--odin-cultmesh-rudp": - parsed.odinCultMeshRudp = args[index + 1]; - index += 1; - break; - case "--health": - parsed.health = true; - break; - case "--mimo-command-json": - parsed.mimoCommandJson = args[index + 1]; - index += 1; - break; - case "--mimo-dry-run": - parsed.mimoDryRun = true; - break; - default: - throw new Error(`Unknown argument: ${arg}`); - } - } - return parsed; -} diff --git a/scripts/weksa-rudp-command.cjs b/scripts/weksa-rudp-command.cjs deleted file mode 100644 index 5d310da..0000000 --- a/scripts/weksa-rudp-command.cjs +++ /dev/null @@ -1,466 +0,0 @@ -"use strict"; - -const dgram = require("dgram"); -const path = require("path"); - -const { - CultNetRudpSession, - decodeRudpPacket, - encodeCultNetMessageForWire, - encodeRudpPacket, - parseCultNetMessage, -} = require("cultnet-ts"); - -const { encode, decode } = require(path.resolve( - __dirname, - "..", - "..", - "CultLib", - "node_modules", - "@msgpack", - "msgpack", -)); - -const CULTNET_RUDP_PROTOCOL_ID = "cultnet.transport.rudp.v0"; -const WEKSA_COMMAND_RUDP_CONNECTION_ID = 0x1d0d0002; -const WEKSA_MIMO_VOICEDESIGN_COMMAND_SCHEMA_ID = "weksa.mimo_voicedesign_command.v0"; -const WEKSA_MIMO_VOICEDESIGN_RECEIPT_SCHEMA_ID = "weksa.mimo_voicedesign_receipt.v0"; - -async function createWeksaRudpCommandServer(options) { - if (!options?.host || !options?.port || typeof options.onCommand !== "function") { - throw new Error("Weksa RUDP command server requires host, port, and onCommand."); - } - const socket = dgram.createSocket(endpointFamily(options.host)); - await bindServerSocket(socket, options.host, options.port); - - const sessions = new Map(); - const resendTimer = setInterval(() => { - for (const entry of sessions.values()) { - for (const packet of entry.session.dueResends(Date.now())) { - void sendPacket(socket, entry.endpoint, packet); - } - } - }, 25); - resendTimer.unref?.(); - - const onMessage = (wire, remote) => { - void handleServerDatagram({ - wire, - remote, - socket, - sessions, - options, - }).catch((error) => { - options.onError?.(error); - }); - }; - const onError = (error) => { - options.onError?.(error instanceof Error ? error : new Error(String(error))); - }; - - socket.on("message", onMessage); - socket.on("error", onError); - - return { - endpoint: { host: options.host, port: options.port }, - uri: formatRudpEndpoint(options.host, options.port), - close() { - clearInterval(resendTimer); - socket.off("message", onMessage); - socket.off("error", onError); - socket.close(); - sessions.clear(); - }, - }; -} - -async function handleServerDatagram(input) { - const { wire, remote, socket, sessions, options } = input; - const packet = decodeRudpPacket(wire); - const key = endpointKey(remote.address, remote.port); - let entry = sessions.get(key); - if (!entry) { - if (packet.packetType !== "connect") { - return; - } - const session = new CultNetRudpSession({ - connectionId: WEKSA_COMMAND_RUDP_CONNECTION_ID, - initialSequence: 1, - resendDelayMs: 100, - maxPendingReliablePackets: 64, - }); - entry = { - session, - endpoint: { host: remote.address, port: remote.port }, - }; - sessions.set(key, entry); - await sendPacket(socket, entry.endpoint, session.acceptConnect(packet, Date.now())); - return; - } - - const result = entry.session.receive(packet, Date.now()); - if (result.reply) { - await sendPacket(socket, entry.endpoint, result.reply); - } - for (const frame of result.delivered) { - await handleServerFrame({ - socket, - entry, - frame, - options, - }); - } - if (packet.packetType === "accept" || result.delivered.length > 0) { - await sendPacket(socket, entry.endpoint, entry.session.createAck()); - } - if (result.disconnected) { - sessions.delete(key); - } -} - -async function handleServerFrame(input) { - const { socket, entry, frame, options } = input; - if (frame.channelId !== "schema") { - return; - } - const message = parseCultNetMessage(decode(frame.payload), "cultnet.schema.v0"); - if (message.schemaVersion !== "cultnet.document_put_raw.v0") { - return; - } - if (message.document.schemaId !== WEKSA_MIMO_VOICEDESIGN_COMMAND_SCHEMA_ID) { - return; - } - const payload = decode(message.document.payload); - const command = { - ...payload, - request_id: payload?.request_id ?? message.document.recordKey, - }; - let receipt; - try { - receipt = await options.onCommand(command, message); - } catch (error) { - options.onError?.(error instanceof Error ? error : new Error(String(error))); - receipt = { - schema_version: WEKSA_MIMO_VOICEDESIGN_RECEIPT_SCHEMA_ID, - request_id: command.request_id, - generated_at: new Date().toISOString(), - ok: false, - error: error instanceof Error ? error.message : String(error), - }; - } - if (!receipt) { - return; - } - const receiptMessage = { - schemaVersion: "cultnet.document_put_raw.v0", - messageId: `${options.runtimeId ?? "weksa-daemon"}:receipt:${receipt.request_id}`, - document: { - schemaId: WEKSA_MIMO_VOICEDESIGN_RECEIPT_SCHEMA_ID, - recordKey: receipt.request_id, - storedAt: receipt.generated_at ?? receipt.observed_at ?? new Date().toISOString(), - payloadEncoding: "messagepack", - payload: encode(receipt), - sourceRuntimeId: options.runtimeId ?? "weksa-daemon", - sourceRole: "weksa-command-ingress", - tags: [CULTNET_RUDP_PROTOCOL_ID, "speech_provider.mimo.voicedesign"], - }, - }; - const wirePayload = encode( - encodeCultNetMessageForWire(receiptMessage, "cultnet.schema.v0"), - ); - for (const packet of entry.session.sendMany("schema", wirePayload, { - reliable: true, - ordered: true, - nowMs: Date.now(), - })) { - await sendPacket(socket, entry.endpoint, packet); - } -} - -async function sendWeksaMimoVoiceDesignCommand(options) { - const endpoint = parseEndpoint(options?.endpoint); - const requestId = options.command?.request_id ?? buildRequestId(); - const command = { - ...options.command, - request_id: requestId, - }; - const socket = dgram.createSocket(endpointFamily(endpoint.host)); - await bindClientSocket(socket, options?.localHost); - const receiver = createPacketReceiver(socket); - const session = new CultNetRudpSession({ - connectionId: WEKSA_COMMAND_RUDP_CONNECTION_ID, - initialSequence: 1, - resendDelayMs: 100, - maxPendingReliablePackets: 64, - }); - - try { - await sendPacket(socket, endpoint, session.createConnect(Date.now(), new Uint8Array())); - await receiveCultNetMessageUntil({ - receiver, - session, - endpoint, - timeoutMs: options.timeoutMs ?? 5000, - label: "accept", - predicate: (_message, packet) => packet.packetType === "accept", - }); - - const commandMessage = { - schemaVersion: "cultnet.document_put_raw.v0", - messageId: `${options.runtimeId ?? "weksa-cli"}:command:${requestId}`, - document: { - schemaId: WEKSA_MIMO_VOICEDESIGN_COMMAND_SCHEMA_ID, - recordKey: requestId, - storedAt: new Date().toISOString(), - payloadEncoding: "messagepack", - payload: encode(command), - sourceRuntimeId: options.runtimeId ?? "weksa-cli", - sourceRole: "command-client", - tags: [CULTNET_RUDP_PROTOCOL_ID, "speech_provider.mimo.voicedesign"], - }, - }; - const wirePayload = encode( - encodeCultNetMessageForWire(commandMessage, "cultnet.schema.v0"), - ); - for (const packet of session.sendMany("schema", wirePayload, { - reliable: true, - ordered: true, - nowMs: Date.now(), - })) { - await sendPacket(socket, endpoint, packet); - } - - return await receiveCultNetMessageUntil({ - receiver, - session, - endpoint, - timeoutMs: options.timeoutMs ?? 10000, - label: "receipt", - predicate: (message) => - message.schemaVersion === "cultnet.document_put_raw.v0" - && message.document.schemaId === WEKSA_MIMO_VOICEDESIGN_RECEIPT_SCHEMA_ID - && message.document.recordKey === requestId, - decodePayload: true, - }); - } finally { - receiver.close(); - socket.close(); - } -} - -async function receiveCultNetMessageUntil(options) { - const deadline = Date.now() + options.timeoutMs; - while (Date.now() < deadline) { - try { - const packet = await options.receiver.next( - Math.min(100, Math.max(1, deadline - Date.now())), - options.label, - ); - const result = options.session.receive(packet, Date.now()); - if (result.reply) { - await sendPacket(options.receiver.socket, options.endpoint, result.reply); - } - if (packet.packetType === "accept" || result.delivered.length > 0) { - await sendPacket( - options.receiver.socket, - options.endpoint, - options.session.createAck(), - ); - } - if (packet.packetType === "accept" && options.predicate(undefined, packet)) { - return undefined; - } - for (const frame of result.delivered) { - if (frame.channelId !== "schema") { - continue; - } - const message = parseCultNetMessage(decode(frame.payload), "cultnet.schema.v0"); - if (!options.predicate(message, packet)) { - continue; - } - if ( - options.decodePayload - && message.schemaVersion === "cultnet.document_put_raw.v0" - ) { - return decode(message.document.payload); - } - return message; - } - } catch (error) { - if (error.code !== "ETIMEDOUT") { - throw error; - } - } - for (const packet of options.session.dueResends(Date.now())) { - await sendPacket(options.receiver.socket, options.endpoint, packet); - } - } - throw new Error(`timed out waiting for Weksa RUDP ${options.label} after ${options.timeoutMs}ms`); -} - -async function bindServerSocket(socket, host, port) { - await new Promise((resolve, reject) => { - socket.once("error", reject); - socket.bind(port, host, () => { - socket.off("error", reject); - resolve(); - }); - }); -} - -async function bindClientSocket(socket, localHost) { - const bindHost = localHost ?? "0.0.0.0"; - await new Promise((resolve, reject) => { - socket.once("error", reject); - socket.bind(0, bindHost, () => { - socket.off("error", reject); - resolve(); - }); - }); -} - -function createPacketReceiver(socket) { - const packets = []; - const waiters = []; - const errors = []; - - const resolveNext = () => { - while (waiters.length > 0 && (packets.length > 0 || errors.length > 0)) { - const waiter = waiters.shift(); - clearTimeout(waiter.timer); - if (errors.length > 0) { - waiter.reject(errors.shift()); - } else { - waiter.resolve(packets.shift()); - } - } - }; - - const onMessage = (wire) => { - try { - packets.push(decodeRudpPacket(wire)); - } catch (error) { - errors.push(error); - } - resolveNext(); - }; - const onError = (error) => { - errors.push(error); - resolveNext(); - }; - - socket.on("message", onMessage); - socket.on("error", onError); - - return { - socket, - next(timeoutMs, label = "packet") { - if (packets.length > 0) { - return Promise.resolve(packets.shift()); - } - if (errors.length > 0) { - return Promise.reject(errors.shift()); - } - return new Promise((resolve, reject) => { - const waiter = { - resolve, - reject, - timer: setTimeout(() => { - const index = waiters.indexOf(waiter); - if (index >= 0) { - waiters.splice(index, 1); - } - const error = new Error(`timed out waiting for Weksa RUDP ${label}`); - error.code = "ETIMEDOUT"; - reject(error); - }, Math.max(1, timeoutMs)), - }; - waiters.push(waiter); - }); - }, - close() { - socket.off("message", onMessage); - socket.off("error", onError); - while (waiters.length > 0) { - const waiter = waiters.shift(); - clearTimeout(waiter.timer); - const error = new Error("Weksa RUDP command receiver closed."); - error.code = "ECLOSED"; - waiter.reject(error); - } - }, - }; -} - -async function sendPacket(socket, endpoint, packet) { - const wire = encodeRudpPacket(packet); - await new Promise((resolve, reject) => { - socket.send(wire, endpoint.port, endpoint.host, (error) => { - if (error) { - reject(error); - } else { - resolve(); - } - }); - }); -} - -function parseEndpoint(value) { - if (!value) { - throw new Error("Weksa RUDP endpoint is required."); - } - const text = String(value).trim(); - if (text.toLowerCase().startsWith("rudp://")) { - const parsed = new URL(text); - return { - host: parsed.hostname, - port: parsePort(parsed.port), - }; - } - const ipv6 = text.match(/^\[([^\]]+)\]:(\d+)$/); - if (ipv6) { - return { host: ipv6[1], port: parsePort(ipv6[2]) }; - } - const index = text.lastIndexOf(":"); - if (index <= 0) { - throw new Error(`Weksa RUDP endpoint must be host:port, got "${value}".`); - } - return { - host: text.slice(0, index), - port: parsePort(text.slice(index + 1)), - }; -} - -function parsePort(value) { - const port = Number(value); - if (!Number.isInteger(port) || port <= 0 || port > 65535) { - throw new Error(`Weksa RUDP endpoint port is invalid: ${value}`); - } - return port; -} - -function endpointFamily(host) { - return host.includes(":") ? "udp6" : "udp4"; -} - -function endpointKey(host, port) { - return `${host}:${port}`; -} - -function formatRudpEndpoint(host, port) { - return host.includes(":") ? `rudp://[${host}]:${port}` : `rudp://${host}:${port}`; -} - -function buildRequestId() { - return `mimo-${new Date().toISOString().replace(/[-:.TZ]/g, "")}-${Math.random().toString(36).slice(2, 8)}`; -} - -module.exports = { - CULTNET_RUDP_PROTOCOL_ID, - WEKSA_COMMAND_RUDP_CONNECTION_ID, - WEKSA_MIMO_VOICEDESIGN_COMMAND_SCHEMA_ID, - WEKSA_MIMO_VOICEDESIGN_RECEIPT_SCHEMA_ID, - createWeksaRudpCommandServer, - sendWeksaMimoVoiceDesignCommand, - formatRudpEndpoint, -}; diff --git a/tools/export-provider-advertisement.mjs b/tools/export-provider-advertisement.mjs deleted file mode 100644 index be8b6fb..0000000 --- a/tools/export-provider-advertisement.mjs +++ /dev/null @@ -1,148 +0,0 @@ -#!/usr/bin/env node -import { mkdir, writeFile } from "node:fs/promises"; -import path from "node:path"; - -const document = { - schema: "gamecult.eve.provider_advertisement.v1", - providerId: "weksa.intent.service", - title: "Weksa Intent Service", - description: - "Weksa converts writing, interlingua, and agent state into typed conversational intent, pronunciation plans, and utterance handoff documents for AquaSynth and other consumers.", - mode: "fixture-read-only", - serviceVerse: { - id: "verse.weksa.service.v1", - cultMeshKey: "weksa.service/provider-advertisement", - authority: "Weksa daemon identity, schema catalog, and provider publication", - owns: [ - "typed conversational intent", - "pronunciation plans", - "utterance handoff documents", - "lowering traces", - "language-project and flavor-profile state", - ], - doesNotOwn: [ - "AquaSynth learned embeddings", - "Faust compilation", - "rendered audio", - "Persona canonical state", - ], - }, - nestedVerses: [ - { - id: "weksa.intent", - kind: "intent", - cultMeshKey: "weksa.intent/current", - authority: "Weksa conversational intent compiler", - schemas: ["weksa.conversational_intent.v0"], - }, - { - id: "weksa.pronunciation", - kind: "pronunciation", - cultMeshKey: "weksa.pronunciation/current", - authority: "Weksa pronunciation lowering", - schemas: ["weksa.pronunciation_plan.v0"], - }, - { - id: "weksa.utterance", - kind: "utterance", - cultMeshKey: "weksa.utterance/handoffs", - authority: "Weksa utterance handoff export", - schemas: ["weksa.utterance_handoff.v0", "weksa.utterance_embedding_handoff.v0.1"], - }, - { - id: "weksa.operator", - kind: "operator", - cultMeshKey: "weksa.operator/status", - authority: "Weksa daemon runtime", - schemas: ["weksa.operator_state.v0"], - }, - { - id: "weksa.cultural_ontology", - kind: "cultural_ontology", - cultMeshKey: "weksa.cultural-ontology/catalogs", - authority: "Weksa cultural ontology profile store", - schemas: ["weksa.cultural_ontology_profile.v0", "weksa.cultural_ontology_catalog.v0"], - }, - ], - schemaWitnessFamilies: [ - { - family: "intent", - namespace: "weksa.intent.*", - witnesses: ["weksa.conversational_intent.v0"], - durableShape: ".weksa/intent/{intentId}.cc", - }, - { - family: "pronunciation", - namespace: "weksa.pronunciation.*", - witnesses: ["weksa.pronunciation_plan.v0"], - durableShape: ".weksa/pronunciation/{utteranceId}.cc", - }, - { - family: "utterance", - namespace: "weksa.utterance.*", - witnesses: ["weksa.utterance_handoff.v0", "weksa.utterance_embedding_handoff.v0.1"], - durableShape: ".weksa/handoff/{utteranceId}.cc", - }, - { - family: "trace", - namespace: "weksa.trace.*", - witnesses: ["weksa.lowering_trace.v0"], - durableShape: ".weksa/traces/{traceId}.cc", - }, - { - family: "cultural_ontology", - namespace: "weksa.cultural_ontology.*", - witnesses: ["weksa.cultural_ontology_profile.v0", "weksa.cultural_ontology_catalog.v0"], - durableShape: ".weksa/cultural-ontology/{storeId}.cc", - }, - ], - commandBoundaries: [ - { - id: "weksa.command.intent.create", - owner: "Weksa", - acceptedByFixture: false, - allowedInputs: ["writing", "interlingua packet", "Persona/agent state projection"], - emits: ["weksa.conversational_intent.v0", "weksa.lowering_trace.v0"], - }, - { - id: "weksa.command.utterance.lower", - owner: "Weksa", - acceptedByFixture: false, - allowedInputs: ["accepted conversational intent", "language project", "speaker state"], - emits: ["weksa.pronunciation_plan.v0", "weksa.utterance_handoff.v0"], - }, - { - id: "weksa.command.handoff.export", - owner: "Weksa", - acceptedByFixture: false, - emits: ["weksa.utterance_embedding_handoff.v0.1"], - forbiddenTargets: ["Faust compilation", "audio rendering", "AquaSynth synth driver truth"], - }, - ], - loweringRules: [ - "Weksa owns conversational intent and utterance lowering.", - "AquaSynth owns learned embeddings, synth controls, Faust compilation, and audio output.", - "Fixture exports are discovery metadata until the daemon loop commits typed witnesses.", - ], -}; - -async function main() { - const args = process.argv.slice(2); - const outIndex = args.indexOf("--out"); - const outputPath = outIndex >= 0 ? args[outIndex + 1] : undefined; - const json = `${JSON.stringify(document, null, 2)}\n`; - - if (outputPath) { - const resolved = path.resolve(process.cwd(), outputPath); - await mkdir(path.dirname(resolved), { recursive: true }); - await writeFile(resolved, json, "utf8"); - return; - } - - process.stdout.write(json); -} - -main().catch((error) => { - console.error(error); - process.exitCode = 1; -});