diff --git a/.gitignore b/.gitignore index 3d8b18d..ad400cb 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,10 @@ state/scratch-compaction-*.md state/scratch-runtime/ .weksa-runs/ .weksa-smoke/ +.weksa/*.cc +.weksa/*.json +.weksa/*.log +.weksa/*.pid examples/persona-lowering/*/runs/ node_modules/ dist/ diff --git a/docs/mimo-tts-lowering.md b/docs/mimo-tts-lowering.md index ea31d4a..ff5ac67 100644 --- a/docs/mimo-tts-lowering.md +++ b/docs/mimo-tts-lowering.md @@ -80,6 +80,10 @@ target_language: locale: script: register: +performance_register: + label: + medium: + delivery_archetype: voice_design: description: source_controls: @@ -101,6 +105,12 @@ trace: ## 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: diff --git a/docs/verse-service-contract.md b/docs/verse-service-contract.md index 2946141..8b861c6 100644 --- a/docs/verse-service-contract.md +++ b/docs/verse-service-contract.md @@ -32,6 +32,15 @@ compilation, live instrument handles, and rendered audio. ## CultCache Witnesses - `.weksa/provider-advertisement.cc`: read-only provider advertisement witness. +- `.weksa/provider-advertisement-store.cc`: CultMesh/CultCache provider store + containing `gamecult.eve.provider_advertisement.v1`, + `gamecult.eve.interface_binding.v1`, and `gamecult.eve.surface_state.v1` + records for Odin discovery. +- `.weksa/operator-state.cc`: daemon health, readiness, and runtime witness + index used by Idunn health checks and operator inspection. +- `.weksa/eve-surfaces.cc`: current Weksa operator Eve surface witness. +- `.weksa/cultmesh-publications.cc`: publication index naming current Weksa + witness surfaces and compatibility routes. - `.weksa/intent/{intentId}.cc`: conversational intent documents. - `.weksa/pronunciation/{utteranceId}.cc`: pronunciation plans and phonetic event sequences. @@ -47,6 +56,12 @@ 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 compatibility HTTP surface listens on +`http://127.0.0.1:8813` with `/health`, `/provider-advertisement`, +`/operator-state`, `/eve/operator`, and `/cultmesh/publications`. + ## CultMesh Verses - `weksa.service`: service identity, schema catalog, version, and health. @@ -62,6 +77,11 @@ commit when the runtime lands. - `weksa.operator`: daemon status, queue pressure, schema drift, and witness freshness. +Odin ingests the Starfire Weksa provider through +`.weksa/provider-advertisement-store.cc`. Idunn tracks the daemon as target +`weksa` in the `starfire-local` swarm profile and owns restart authority through +the Weksa restart script. + ## Eve Surfaces - `weksa.eve.intent_review.v0`: inspect typed conversational intent before it 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 index 771ae69..41e4f16 100644 --- a/examples/speech-provider/mimo/nibu-proof-001/en-US-request.yaml +++ b/examples/speech-provider/mimo/nibu-proof-001/en-US-request.yaml @@ -11,6 +11,10 @@ target_language: 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 @@ -31,7 +35,7 @@ voice_design: - dependency covered by contempt - anime protagonist delivery - sharp visual-novel heroine timing - - clipped technical casual delivery + - technical precision inside anime performance - contained urgency projected_context_refs: - examples/english-lowering/nibu-proof-001/projected-context.yaml @@ -53,9 +57,11 @@ messages: 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 - technical casual delivery, contained urgency, low warmth, sharp articulation, - and faint tired contempt. Avoid realistic adult assistant tone, soothing - customer-service voice, cheerful mascot energy, and theatrical villainy. + 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. @@ -68,7 +74,8 @@ trace: - 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: - - clipped technical casual delivery + - anime visual-novel heroine warning delivery + - technical precision without technical-assistant delivery - contained urgency - low warmth - sharp emphasis on unsafe hardware and bodily risk diff --git a/schemas/mimo-tts-request.md b/schemas/mimo-tts-request.md index 34c481a..9ba2c1e 100644 --- a/schemas/mimo-tts-request.md +++ b/schemas/mimo-tts-request.md @@ -17,6 +17,8 @@ an AquaSynth handoff. `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 @@ -42,6 +44,12 @@ 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. diff --git a/scripts/health-weksa-daemon.cmd b/scripts/health-weksa-daemon.cmd new file mode 100644 index 0000000..2d5390a --- /dev/null +++ b/scripts/health-weksa-daemon.cmd @@ -0,0 +1,2 @@ +@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/restart-weksa-daemon.cmd b/scripts/restart-weksa-daemon.cmd new file mode 100644 index 0000000..6621525 --- /dev/null +++ b/scripts/restart-weksa-daemon.cmd @@ -0,0 +1,2 @@ +@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 new file mode 100644 index 0000000..2a9fbb8 --- /dev/null +++ b/scripts/restart-weksa-daemon.ps1 @@ -0,0 +1,21 @@ +param( + [int] $Port = 8813, + [string] $HostName = "127.0.0.1", + [string] $StateRoot = "E:\Projects\weksa\.weksa" +) + +$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 +exit $LASTEXITCODE diff --git a/scripts/start-weksa-daemon.ps1 b/scripts/start-weksa-daemon.ps1 new file mode 100644 index 0000000..21f88cf --- /dev/null +++ b/scripts/start-weksa-daemon.ps1 @@ -0,0 +1,67 @@ +param( + [int] $Port = 8813, + [string] $HostName = "127.0.0.1", + [string] $StateRoot = "E:\Projects\weksa\.weksa" +) + +$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 + +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 +) + +$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 " health: http://$HostName`:$Port/health" +Write-Host " state: $StateRoot" diff --git a/scripts/weksa-daemon.mjs b/scripts/weksa-daemon.mjs new file mode 100644 index 0000000..9010476 --- /dev/null +++ b/scripts/weksa-daemon.mjs @@ -0,0 +1,539 @@ +#!/usr/bin/env node +import http from "node:http"; +import Module from "node:module"; +import { existsSync, readFileSync, readdirSync } from "node:fs"; +import { mkdir, readdir, rm, writeFile } from "node:fs/promises"; +import { delimiter, 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 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 startedAt = new Date().toISOString(); +let tick = 0; +let lastPublishedAt = ""; +let lastError = undefined; +let cachedSnapshot = undefined; + +if (options.health) { + await runHealthCheck(); + 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}`); +await publishWitnesses(); + +const server = http.createServer(async (request, response) => { + try { + const url = new URL(request.url ?? "/", `http://${request.headers.host ?? `${host}:${port}`}`); + const snapshot = await currentSnapshot(); + if (url.pathname === "/health") { + return sendJson(response, 200, { + ok: true, + service: "weksa.intent.service", + providerId: "weksa.intent.service", + state: "healthy", + startedAt, + lastPublishedAt, + tick, + }); + } + if (url.pathname === "/provider-advertisement") { + return sendJson(response, 200, snapshot.providerAdvertisement); + } + if (url.pathname === "/operator-state") { + return sendJson(response, 200, snapshot.operatorState); + } + if (url.pathname === "/eve/operator") { + return sendJson(response, 200, snapshot.operatorSurface); + } + if (url.pathname === "/cultmesh/publications") { + return sendJson(response, 200, snapshot.cultMeshPublications); + } + sendJson(response, 404, { ok: false, error: "unknown endpoint" }); + } catch (error) { + lastError = String(error?.stack ?? error); + await appendLog(`request error: ${lastError}`); + sendJson(response, 500, { ok: false, error: String(error?.message ?? error) }); + } +}); + +server.listen(port, host, () => { + appendLog(`listening http://${host}:${port}`).catch(() => {}); +}); + +const interval = setInterval(() => { + publishWitnesses().catch(async (error) => { + lastError = String(error?.stack ?? error); + await appendLog(`publish error: ${lastError}`); + }); +}, 30_000); + +process.on("SIGINT", () => shutdown(0)); +process.on("SIGTERM", () => shutdown(0)); + +async function shutdown(code) { + clearInterval(interval); + await appendLog(`stopping pid=${process.pid}`); + server.close(() => process.exit(code)); +} + +async function runHealthCheck() { + const healthUrl = `http://${host}:${port}/health`; + await new Promise((resolvePromise, rejectPromise) => { + const request = http.get(healthUrl, (response) => { + let body = ""; + response.setEncoding("utf8"); + response.on("data", (chunk) => { + body += chunk; + }); + response.on("end", () => { + if (response.statusCode === 200) { + process.stdout.write(body); + resolvePromise(); + } else { + rejectPromise(new Error(`health returned ${response.statusCode}: ${body}`)); + } + }); + }); + request.on("error", rejectPromise); + request.setTimeout(5_000, () => { + request.destroy(new Error(`health timed out: ${healthUrl}`)); + }); + }); +} + +async function publishWitnesses() { + const snapshot = await buildSnapshot(); + await writeWitness("provider-advertisement.cc", snapshot.providerAdvertisement); + await writeWitness("operator-state.cc", snapshot.operatorState); + await writeWitness("eve-surfaces.cc", snapshot.operatorSurface); + 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, "eve-operator-surface.json"), snapshot.operatorSurface); + await writeJson(resolve(stateRoot, "cultmesh-publications.json"), snapshot.cultMeshPublications); + cachedSnapshot = snapshot; + lastPublishedAt = snapshot.operatorState.updated_at; + tick += 1; +} + +async function currentSnapshot() { + if (!cachedSnapshot) { + await publishWitnesses(); + } + return cachedSnapshot; +} + +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", + eve_surface: ".weksa/eve-surfaces.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: false, + command_ingress: "not_enabled_until_command_commit_path_exists", + }, + latest_affect_tuning: tuning, + }; + const operatorSurface = buildOperatorSurface({ providerAdvertisement, operatorState, cultureCatalog, tuning }); + 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: "pending", + compatibility_http: `http://${host}:${port}`, + }, + publications: [ + publication("weksa.service/provider-advertisement", "gamecult.eve.provider_advertisement.v1", ".weksa/provider-advertisement-store.cc", "/provider-advertisement"), + publication("weksa.operator/status", "weksa.operator_state.v0", ".weksa/operator-state.cc", "/operator-state"), + publication("weksa.eve.surface.operator", "gamecult.eve.surface.v1", ".weksa/eve-surfaces.cc", "/eve/operator"), + publication("weksa.cultmesh/publications", "weksa.cultmesh_publications.v0", ".weksa/cultmesh-publications.cc", "/cultmesh/publications"), + ], + }; + return { providerAdvertisement, operatorState, operatorSurface, cultMeshPublications }; +} + +async function writeCultMeshStore(snapshot) { + const runtime = loadCultRuntime(); + if (runtime.error) { + await appendLog(`CultMesh store write skipped: ${runtime.error.message}`); + return; + } + const { providerAdvertisementDefinition, interfaceBindingDefinition, surfaceDefinition } = defineCultMeshDocuments(runtime.defineDocumentType); + const documents = [providerAdvertisementDefinition, interfaceBindingDefinition, surfaceDefinition]; + 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(interfaceBindingDefinition, "weksa.operator", { + bindingId: "weksa.operator", + providerId: snapshot.providerAdvertisement.providerId, + title: "Weksa Operator", + kind: "operator", + updatedAt: snapshot.operatorState.updated_at, + provider: { + providerId: snapshot.providerAdvertisement.providerId, + canonicalService: "asgard.weksa", + locatedService: "asgard.starfire.weksa", + cultMeshAddress: "asgard.starfire.weksa/eve/operator", + endpoints: [ + { transport: "cultmesh-store", address: ".weksa/provider-advertisement-store.cc" }, + { transport: "http", address: `http://${host}:${port}/eve/operator` }, + ], + routes: [ + { transport: "cultmesh-store", address: ".weksa/provider-advertisement-store.cc" }, + { transport: "compatibility-http", address: `http://${host}:${port}` }, + ], + }, + surface: snapshot.operatorSurface, + }); + await node.put(surfaceDefinition, snapshot.providerAdvertisement.providerId, { + providerId: snapshot.providerAdvertisement.providerId, + title: "Weksa Operator", + version: tick + 1, + updatedAt: snapshot.operatorState.updated_at, + surface: snapshot.operatorSurface, + }); + 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 { + process.env.NODE_PATH = [ + resolve(repoRoot, "..", "CultLib", "packages"), + process.env.NODE_PATH || "", + ].filter(Boolean).join(delimiter); + Module._initPaths(); + const require = Module.createRequire(import.meta.url); + const { CultMesh } = require("cultmesh-ts"); + const { defineDocumentType } = require("cultcache-ts"); + return { CultMesh, defineDocumentType, error: null }; + } catch (error) { + return { CultMesh: null, defineDocumentType: null, error }; + } +} + +function defineCultMeshDocuments(defineDocumentType) { + const surfaceDefinition = defineDocumentType({ + type: "gamecult.eve.surface_state", + schemaName: "gamecult.eve.surface_state", + schemaId: "gamecult.eve.surface_state.v1", + schemaVersion: "gamecult.eve.surface_state.v1", + global: false, + name: (value) => value?.providerId || "surface", + schema: { parse: (value) => value }, + members: [ + { slot: 0, memberName: "providerId", typeName: "string", isName: true }, + { slot: 1, memberName: "title", typeName: "string" }, + { slot: 2, memberName: "version", typeName: "long" }, + { slot: 3, memberName: "updatedAt", typeName: "string" }, + { slot: 4, memberName: "surface", typeName: "object" }, + ], + }); + const interfaceBindingDefinition = defineDocumentType({ + type: "gamecult.eve.interface_binding", + schemaName: "gamecult.eve.interface_binding", + schemaId: "gamecult.eve.interface_binding.v1", + schemaVersion: "gamecult.eve.interface_binding.v1", + global: false, + name: (value) => value?.bindingId || value?.providerId || "interface", + schema: { parse: (value) => value }, + }); + 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 }, + }); + return { providerAdvertisementDefinition, interfaceBindingDefinition, surfaceDefinition }; +} + +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", + updatedAt, + canonicalService: "asgard.weksa", + locatedService: "asgard.starfire.weksa", + cultMeshAddress: "asgard.starfire.weksa/eve/operator", + serviceVerse: { + ...fixture.serviceVerse, + semanticAddress: "asgard.starfire.weksa", + cultMeshKey: "weksa.service/provider-advertisement", + }, + endpoints: [ + { transport: "cultmesh-store", address: ".weksa/provider-advertisement-store.cc" }, + { transport: "http", address: `http://${host}:${port}` }, + ], + routes: [ + { transport: "cultmesh-store", address: ".weksa/provider-advertisement-store.cc" }, + { transport: "compatibility-http", address: `http://${host}:${port}` }, + ], + compatibilityRoutes: [ + { kind: "http", url: `http://${host}:${port}`, lossy: true }, + ], + health: { + state: "healthy", + endpoint: `http://${host}:${port}/health`, + witness: ".weksa/operator-state.cc", + checkedBy: "idunn.desired_daemon.v1", + }, + }; +} + +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", + }; +} + +function buildOperatorSurface({ providerAdvertisement, operatorState, cultureCatalog, tuning }) { + return { + schema: "gamecult.eve.surface.v1", + providerId: providerAdvertisement.providerId, + surfaceId: "weksa.eve.operator.v0", + title: "Weksa", + updatedAt: operatorState.updated_at, + root: { + id: "weksa.operator.root", + kind: "panel", + props: { + title: "Weksa Intent Service", + providerId: providerAdvertisement.providerId, + status: operatorState.state, + }, + children: [ + fact("health", operatorState.state), + fact("verse", operatorState.verse_id), + fact("affect", operatorState.readiness.affect_aware_lowering), + fact("affect passes", tuning ? `${tuning.affect_contrast_visible}/${tuning.pass_count} visible` : "none"), + fact("culture profiles", String(cultureCatalog.profile_count)), + fact("target profiles", String(cultureCatalog.target_language_profile_count)), + fact("commands", operatorState.readiness.command_ingress), + ], + }, + }; +} + +function fact(label, value) { + return { + id: `weksa.operator.fact.${label.replace(/[^a-z0-9]+/gi, "-").toLowerCase()}`, + kind: "text", + props: { label, text: `${label}: ${value}` }, + }; +} + +function publication(key, schema, witness, endpoint) { + return { + key, + schema, + witness, + compatibility_endpoint: 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 sendJson(response, statusCode, value) { + response.writeHead(statusCode, { "content-type": "application/json; charset=utf-8" }); + response.end(`${JSON.stringify(value, null, 2)}\n`); +} + +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 "--health": + parsed.health = true; + break; + default: + throw new Error(`Unknown argument: ${arg}`); + } + } + return parsed; +}