@plasius/ai-speech 0.1.7 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +14 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/docs/adrs/adr-0003-player-system-audio-contracts.md +1 -1
- package/docs/adrs/adr-0004-harmony-namespace-cutover.md +48 -0
- package/docs/adrs/index.md +3 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -18,6 +18,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
18
18
|
- **Security**
|
|
19
19
|
- (placeholder)
|
|
20
20
|
|
|
21
|
+
## [1.0.0] - 2026-07-15
|
|
22
|
+
|
|
23
|
+
- **Added**
|
|
24
|
+
- (placeholder)
|
|
25
|
+
|
|
26
|
+
- **Changed**
|
|
27
|
+
- **Breaking:** replaced the exported Player System audio rollout value from `isekai.player-system.audio.enabled` to `harmony.player-system.audio.enabled`. The next release is a major version and intentionally provides no alias, dual-read parsing, or legacy runtime fallback.
|
|
28
|
+
|
|
29
|
+
- **Fixed**
|
|
30
|
+
- (placeholder)
|
|
31
|
+
|
|
32
|
+
- **Security**
|
|
33
|
+
- (placeholder)
|
|
34
|
+
|
|
21
35
|
## [0.1.7] - 2026-07-12
|
|
22
36
|
|
|
23
37
|
- **Added**
|
|
@@ -84,3 +98,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
84
98
|
[0.1.5]: https://github.com/Plasius-LTD/ai-speech/releases/tag/v0.1.5
|
|
85
99
|
[0.1.6]: https://github.com/Plasius-LTD/ai-speech/releases/tag/v0.1.6
|
|
86
100
|
[0.1.7]: https://github.com/Plasius-LTD/ai-speech/releases/tag/v0.1.7
|
|
101
|
+
[1.0.0]: https://github.com/Plasius-LTD/ai-speech/releases/tag/v1.0.0
|
package/README.md
CHANGED
|
@@ -86,7 +86,7 @@ Default behavior is fail-closed when the caller does not provide a remote flag s
|
|
|
86
86
|
## Player System audio contracts
|
|
87
87
|
|
|
88
88
|
Player System audio is enabled by the remotely evaluated
|
|
89
|
-
`
|
|
89
|
+
`harmony.player-system.audio.enabled` flag. The package keeps the three delivery
|
|
90
90
|
channels distinct so hosts can apply separate UX and accessibility behavior:
|
|
91
91
|
|
|
92
92
|
- `narrated-response` uses an opaque utterance ID and locale; raw speech text
|
|
@@ -126,6 +126,18 @@ const decision = resolveAiSpeechAudioPolicy({
|
|
|
126
126
|
});
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
+
## Project Harmony namespace migration
|
|
130
|
+
|
|
131
|
+
The Project Harmony cutover is a breaking public-contract change. Consumers
|
|
132
|
+
moving to the next major release must replace
|
|
133
|
+
`isekai.player-system.audio.enabled` with
|
|
134
|
+
`harmony.player-system.audio.enabled` before enabling
|
|
135
|
+
`harmony.namespace-cutover.enabled`.
|
|
136
|
+
|
|
137
|
+
The package publishes only the Harmony value. It provides no alias, dual-read
|
|
138
|
+
parsing, environment fallback, or runtime translation for the previous product
|
|
139
|
+
namespace. Host applications remain responsible for remote flag evaluation.
|
|
140
|
+
|
|
129
141
|
## Cache Safety Rules
|
|
130
142
|
|
|
131
143
|
- User names, account handles, and user-renamed character names are redacted to a generic label such as `Player`.
|
|
@@ -138,6 +150,7 @@ const decision = resolveAiSpeechAudioPolicy({
|
|
|
138
150
|
- Disable `ai.tts.near-reuse.enabled` to force safe callers back to exact-cache planning.
|
|
139
151
|
- Disable `ai.tts.cache.enabled` to bypass cache planning entirely.
|
|
140
152
|
- Disable `ai.tts.premium-characters.enabled` to downgrade premium character requests to standard voices.
|
|
153
|
+
- For namespace rollback, disable `harmony.namespace-cutover.enabled`, restore the previous coordinated package majors, and complete the approved reverse stored-value migration before re-enabling consumers.
|
|
141
154
|
|
|
142
155
|
## Development
|
|
143
156
|
|
package/dist/index.cjs
CHANGED
|
@@ -388,7 +388,7 @@ function planAiSpeechCache(input) {
|
|
|
388
388
|
reasonCodes: ["near-reuse-eligible"]
|
|
389
389
|
};
|
|
390
390
|
}
|
|
391
|
-
var AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID = "
|
|
391
|
+
var AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID = "harmony.player-system.audio.enabled";
|
|
392
392
|
var AI_SPEECH_AUDIO_CHANNELS = [
|
|
393
393
|
"narrated-response",
|
|
394
394
|
"localized-cue",
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export interface AiPackageDescriptor {\n readonly packageName: string;\n readonly featureFlagId: string;\n readonly envPrefix: string;\n readonly summary: string;\n}\n\nfunction requireNonEmptyString(value: string, label: string): string {\n const trimmed = value.trim();\n if (trimmed.length === 0) {\n throw new Error(`${label} must be a non-empty string.`);\n }\n\n return trimmed;\n}\n\nfunction normalizeWhitespace(value: string): string {\n return value.replace(/\\s+/gu, \" \").trim();\n}\n\nexport const AI_SPEECH_PACKAGE = \"@plasius/ai-speech\";\nexport const AI_SPEECH_ENV_PREFIX = \"AI_SPEECH\";\n\nexport const AI_SPEECH_FEATURE_FLAGS = {\n ttsCache: \"ai.tts.cache.enabled\",\n ttsNearReuse: \"ai.tts.near-reuse.enabled\",\n premiumCharacters: \"ai.tts.premium-characters.enabled\",\n} as const;\n\nexport type AiSpeechFeatureFlagKey =\n (typeof AI_SPEECH_FEATURE_FLAGS)[keyof typeof AI_SPEECH_FEATURE_FLAGS];\n\nexport type AiSpeechFeatureFlagSnapshot = Readonly<\n Record<string, boolean | undefined>\n>;\n\nexport const AI_SPEECH_FEATURE_FLAG_ID = AI_SPEECH_FEATURE_FLAGS.ttsCache;\n\nexport const AI_SPEECH_ROLLOUT_EVALUATORS = [\n \"remote-flag-service\",\n \"host-application\",\n \"break-glass-env\",\n] as const;\n\nexport type AiSpeechRolloutEvaluator =\n (typeof AI_SPEECH_ROLLOUT_EVALUATORS)[number];\n\nexport const AI_SPEECH_ROLLOUT_FALLBACK_MODES = [\n \"fail-closed\",\n \"fail-open\",\n] as const;\n\nexport type AiSpeechRolloutFallbackMode =\n (typeof AI_SPEECH_ROLLOUT_FALLBACK_MODES)[number];\n\nexport interface AiSpeechRolloutControl {\n readonly featureFlag: AiSpeechFeatureFlagKey;\n readonly evaluator: AiSpeechRolloutEvaluator;\n readonly defaultEnabled: boolean;\n readonly fallbackMode: AiSpeechRolloutFallbackMode;\n}\n\nexport interface AiSpeechRolloutDecision extends AiSpeechRolloutControl {\n readonly enabled: boolean;\n readonly source: \"snapshot\" | \"default\";\n}\n\nexport const AI_SPEECH_ROLLOUTS = Object.freeze({\n ttsCache: Object.freeze<AiSpeechRolloutControl>({\n featureFlag: AI_SPEECH_FEATURE_FLAGS.ttsCache,\n evaluator: \"remote-flag-service\",\n defaultEnabled: false,\n fallbackMode: \"fail-closed\",\n }),\n ttsNearReuse: Object.freeze<AiSpeechRolloutControl>({\n featureFlag: AI_SPEECH_FEATURE_FLAGS.ttsNearReuse,\n evaluator: \"remote-flag-service\",\n defaultEnabled: false,\n fallbackMode: \"fail-closed\",\n }),\n premiumCharacters: Object.freeze<AiSpeechRolloutControl>({\n featureFlag: AI_SPEECH_FEATURE_FLAGS.premiumCharacters,\n evaluator: \"remote-flag-service\",\n defaultEnabled: false,\n fallbackMode: \"fail-closed\",\n }),\n});\n\nexport function resolveAiSpeechRolloutDecision(\n control: AiSpeechRolloutControl,\n snapshot: AiSpeechFeatureFlagSnapshot = {}\n): AiSpeechRolloutDecision {\n const resolved = snapshot[control.featureFlag];\n if (typeof resolved === \"boolean\") {\n return {\n ...control,\n enabled: resolved,\n source: \"snapshot\",\n };\n }\n\n return {\n ...control,\n enabled: control.defaultEnabled,\n source: \"default\",\n };\n}\n\nexport function isAiSpeechFeatureEnabled(\n featureFlag: AiSpeechFeatureFlagKey,\n snapshot: AiSpeechFeatureFlagSnapshot = {}\n): boolean {\n const control = Object.values(AI_SPEECH_ROLLOUTS).find(\n (candidate) => candidate.featureFlag === featureFlag\n );\n\n if (!control) {\n return false;\n }\n\n return resolveAiSpeechRolloutDecision(control, snapshot).enabled;\n}\n\nexport const AI_SPEECH_VOICE_TIERS = [\n \"development\",\n \"standard\",\n \"premium-character\",\n] as const;\n\nexport type AiSpeechVoiceTier = (typeof AI_SPEECH_VOICE_TIERS)[number];\n\nexport const AI_SPEECH_ENVIRONMENTS = [\n \"development\",\n \"preview\",\n \"production\",\n] as const;\n\nexport type AiSpeechEnvironment = (typeof AI_SPEECH_ENVIRONMENTS)[number];\n\nexport interface AiSpeechVoiceTierDecision {\n readonly requestedTier: AiSpeechVoiceTier;\n readonly resolvedTier: AiSpeechVoiceTier;\n readonly fallbackTier?: AiSpeechVoiceTier;\n readonly reasonCodes: readonly string[];\n}\n\nexport interface ResolveAiSpeechVoiceTierInput {\n readonly environment: AiSpeechEnvironment;\n readonly requestedTier?: AiSpeechVoiceTier;\n readonly featureFlags?: AiSpeechFeatureFlagSnapshot;\n}\n\nexport function resolveAiSpeechVoiceTier(\n input: ResolveAiSpeechVoiceTierInput\n): AiSpeechVoiceTierDecision {\n const requestedTier =\n input.requestedTier ??\n (input.environment === \"development\" ? \"development\" : \"standard\");\n\n if (\n requestedTier === \"premium-character\" &&\n !isAiSpeechFeatureEnabled(\n AI_SPEECH_FEATURE_FLAGS.premiumCharacters,\n input.featureFlags\n )\n ) {\n return {\n requestedTier,\n resolvedTier: \"standard\",\n fallbackTier: \"standard\",\n reasonCodes: [\"premium-character-rollout-disabled\"],\n };\n }\n\n if (\n requestedTier === \"development\" &&\n input.environment !== \"development\"\n ) {\n return {\n requestedTier,\n resolvedTier: \"standard\",\n fallbackTier: \"standard\",\n reasonCodes: [\"development-voices-limited-to-development-environments\"],\n };\n }\n\n return {\n requestedTier,\n resolvedTier: requestedTier,\n reasonCodes: [],\n };\n}\n\nexport const AI_SPEECH_PLAYER_ADDRESS_SOURCES = [\n \"generic-player\",\n \"class-title\",\n \"faction-title\",\n \"authored-character\",\n \"user-name\",\n \"account-handle\",\n \"user-renamed-character\",\n] as const;\n\nexport type AiSpeechPlayerAddressSource =\n (typeof AI_SPEECH_PLAYER_ADDRESS_SOURCES)[number];\n\nexport const AI_SPEECH_DEFAULT_PLAYER_LABEL = \"Player\";\n\nexport interface AiSpeechPlayerAddressInput {\n readonly source: AiSpeechPlayerAddressSource;\n readonly rawValue?: string;\n readonly fallbackLabel?: string;\n}\n\nexport interface AiSpeechPlayerAddressDecision {\n readonly source: AiSpeechPlayerAddressSource;\n readonly renderText: string;\n readonly exactReuseAllowed: boolean;\n readonly nearReuseAllowed: boolean;\n readonly redacted: boolean;\n readonly reasonCodes: readonly string[];\n}\n\nexport function resolveAiSpeechPlayerAddress(\n input: AiSpeechPlayerAddressInput\n): AiSpeechPlayerAddressDecision {\n const fallbackLabel =\n normalizeWhitespace(input.fallbackLabel ?? \"\") ||\n AI_SPEECH_DEFAULT_PLAYER_LABEL;\n const rawValue = normalizeWhitespace(input.rawValue ?? \"\");\n\n switch (input.source) {\n case \"generic-player\":\n return {\n source: input.source,\n renderText: fallbackLabel,\n exactReuseAllowed: true,\n nearReuseAllowed: true,\n redacted: false,\n reasonCodes: [\"generic-player-address\"],\n };\n case \"class-title\":\n case \"faction-title\":\n return {\n source: input.source,\n renderText: rawValue || fallbackLabel,\n exactReuseAllowed: true,\n nearReuseAllowed: true,\n redacted: false,\n reasonCodes: rawValue ? [] : [\"player-address-fell-back-to-generic-label\"],\n };\n case \"authored-character\":\n return {\n source: input.source,\n renderText: rawValue || fallbackLabel,\n exactReuseAllowed: true,\n nearReuseAllowed: true,\n redacted: false,\n reasonCodes: rawValue ? [] : [\"player-address-fell-back-to-generic-label\"],\n };\n case \"user-name\":\n case \"account-handle\":\n case \"user-renamed-character\":\n return {\n source: input.source,\n renderText: fallbackLabel,\n exactReuseAllowed: true,\n nearReuseAllowed: false,\n redacted: true,\n reasonCodes: [\"player-identifier-redacted-from-render-text\"],\n };\n }\n}\n\nexport interface AiSpeechRenderTextInput {\n readonly textTemplate: string;\n readonly playerAddress?: AiSpeechPlayerAddressInput;\n}\n\nexport interface AiSpeechRenderTextResult {\n readonly renderText: string;\n readonly normalizedRenderText: string;\n readonly playerAddress?: AiSpeechPlayerAddressDecision;\n}\n\nexport function renderAiSpeechText(\n input: AiSpeechRenderTextInput\n): AiSpeechRenderTextResult {\n const textTemplate = requireNonEmptyString(\n input.textTemplate,\n \"Speech text template\"\n );\n const hasPlayerPlaceholder = textTemplate.includes(\"{playerAddress}\");\n\n if (hasPlayerPlaceholder && !input.playerAddress) {\n throw new Error(\n \"Speech text template requires playerAddress when using the {playerAddress} placeholder.\"\n );\n }\n\n const playerAddress = input.playerAddress\n ? resolveAiSpeechPlayerAddress(input.playerAddress)\n : undefined;\n const renderText = normalizeWhitespace(\n hasPlayerPlaceholder && playerAddress\n ? textTemplate.replace(/\\{playerAddress\\}/gu, playerAddress.renderText)\n : textTemplate\n );\n\n return {\n renderText,\n normalizedRenderText: normalizeAiSpeechText(renderText),\n playerAddress,\n };\n}\n\nexport function normalizeAiSpeechText(value: string): string {\n return requireNonEmptyString(\n normalizeWhitespace(value).toLocaleLowerCase(\"en-GB\"),\n \"Speech text\"\n );\n}\n\nexport const AI_SPEECH_PROVIDER_CACHE_PERMISSIONS = [\n \"forbidden\",\n \"exact-only\",\n \"exact-and-near\",\n] as const;\n\nexport type AiSpeechProviderCachePermission =\n (typeof AI_SPEECH_PROVIDER_CACHE_PERMISSIONS)[number];\n\nexport const AI_SPEECH_CACHE_MODES = [\n \"disabled\",\n \"no-cache\",\n \"exact\",\n \"near\",\n] as const;\n\nexport type AiSpeechCacheMode = (typeof AI_SPEECH_CACHE_MODES)[number];\n\nexport const AI_SPEECH_CACHE_SCOPES = [\"none\", \"actor\", \"global\"] as const;\n\nexport type AiSpeechCacheScope = (typeof AI_SPEECH_CACHE_SCOPES)[number];\n\nexport const AI_SPEECH_UTTERANCE_CLASSES = [\n \"system-generic\",\n \"game-npc-dialogue\",\n \"game-bark\",\n \"player-address\",\n \"moderation-notice\",\n \"private-response\",\n] as const;\n\nexport type AiSpeechUtteranceClass =\n (typeof AI_SPEECH_UTTERANCE_CLASSES)[number];\n\nexport const AI_SPEECH_NEAR_REUSE_SAFE_CLASSES = [\n \"system-generic\",\n \"game-npc-dialogue\",\n \"game-bark\",\n \"player-address\",\n] as const satisfies readonly AiSpeechUtteranceClass[];\n\nconst AI_SPEECH_NEAR_REUSE_SAFE_CLASS_SET: ReadonlySet<AiSpeechUtteranceClass> =\n new Set(AI_SPEECH_NEAR_REUSE_SAFE_CLASSES);\n\nexport interface AiSpeechCacheKeyInput {\n readonly providerId: string;\n readonly modelId: string;\n readonly voiceId: string;\n readonly locale: string;\n readonly format: string;\n readonly pronunciationVersion: string;\n readonly normalizedRenderText: string;\n readonly styleId?: string;\n readonly scopeDiscriminator?: string;\n}\n\nexport interface AiSpeechVoiceProfileRef {\n readonly providerId: string;\n readonly modelId: string;\n readonly voiceId: string;\n readonly locale: string;\n readonly format: string;\n readonly pronunciationVersion: string;\n readonly styleId?: string;\n readonly cachePermission: AiSpeechProviderCachePermission;\n readonly tier?: AiSpeechVoiceTier;\n readonly profileId?: string;\n}\n\nexport interface AiSpeechCacheTelemetry {\n readonly cacheHits: number;\n readonly nearCacheHits: number;\n readonly cacheMisses: number;\n readonly charactersSaved: number;\n readonly estimatedCostSavedUsd?: number;\n readonly voiceProfileIds?: readonly string[];\n}\n\nexport interface PlanAiSpeechCacheInput {\n readonly utteranceClass: AiSpeechUtteranceClass;\n readonly textTemplate: string;\n readonly playerAddress?: AiSpeechPlayerAddressInput;\n readonly voice: AiSpeechVoiceProfileRef;\n readonly featureFlags?: AiSpeechFeatureFlagSnapshot;\n readonly actorScopeKey?: string;\n readonly containsPersonalData?: boolean;\n readonly containsPrivateContext?: boolean;\n readonly containsModerationNotice?: boolean;\n}\n\nexport interface AiSpeechCachePlan {\n readonly mode: AiSpeechCacheMode;\n readonly sharingScope: AiSpeechCacheScope;\n readonly renderText: string;\n readonly normalizedRenderText: string;\n readonly exactKey?: string;\n readonly nearKey?: string;\n readonly enabledFeatureFlags: readonly AiSpeechFeatureFlagKey[];\n readonly playerAddress?: AiSpeechPlayerAddressDecision;\n readonly reasonCodes: readonly string[];\n}\n\nexport function createAiSpeechCacheKey(input: AiSpeechCacheKeyInput): string {\n const scopeDiscriminator = normalizeWhitespace(input.scopeDiscriminator ?? \"\");\n return [\n requireNonEmptyString(input.providerId, \"Provider id\"),\n requireNonEmptyString(input.modelId, \"Model id\"),\n requireNonEmptyString(input.voiceId, \"Voice id\"),\n requireNonEmptyString(input.locale, \"Locale\"),\n normalizeWhitespace(input.styleId ?? \"\") || \"_\",\n requireNonEmptyString(input.format, \"Format\"),\n requireNonEmptyString(\n input.pronunciationVersion,\n \"Pronunciation version\"\n ),\n scopeDiscriminator || \"global\",\n normalizeAiSpeechText(input.normalizedRenderText),\n ]\n .map((segment) => encodeURIComponent(segment))\n .join(\"::\");\n}\n\nexport function createAiSpeechNearReuseFingerprint(value: string): string {\n const normalized = normalizeAiSpeechText(value)\n .replace(/[^a-z0-9\\s]/gu, \" \")\n .replace(/\\s+/gu, \" \")\n .trim();\n\n return requireNonEmptyString(normalized, \"Near-reuse fingerprint\");\n}\n\nexport function isAiSpeechNearReuseSafeClass(\n utteranceClass: AiSpeechUtteranceClass\n): boolean {\n return AI_SPEECH_NEAR_REUSE_SAFE_CLASS_SET.has(utteranceClass);\n}\n\nexport function planAiSpeechCache(\n input: PlanAiSpeechCacheInput\n): AiSpeechCachePlan {\n const rendered = renderAiSpeechText({\n textTemplate: input.textTemplate,\n playerAddress: input.playerAddress,\n });\n const enabledFeatureFlags: AiSpeechFeatureFlagKey[] = [];\n const cacheEnabled = isAiSpeechFeatureEnabled(\n AI_SPEECH_FEATURE_FLAGS.ttsCache,\n input.featureFlags\n );\n\n if (!cacheEnabled) {\n return {\n mode: \"disabled\",\n sharingScope: \"none\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: [\"tts-cache-rollout-disabled\"],\n };\n }\n\n enabledFeatureFlags.push(AI_SPEECH_FEATURE_FLAGS.ttsCache);\n\n if (input.voice.cachePermission === \"forbidden\") {\n return {\n mode: \"no-cache\",\n sharingScope: \"none\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: [\"provider-forbids-tts-caching\"],\n };\n }\n\n const containsSensitiveContent =\n Boolean(input.containsPersonalData) ||\n Boolean(input.containsPrivateContext) ||\n Boolean(input.containsModerationNotice) ||\n Boolean(rendered.playerAddress?.redacted);\n const actorScopeKey = normalizeWhitespace(input.actorScopeKey ?? \"\");\n\n const actorScopeRequired =\n containsSensitiveContent || input.utteranceClass === \"private-response\";\n\n if (actorScopeRequired && !actorScopeKey) {\n return {\n mode: \"no-cache\",\n sharingScope: \"none\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: [\"actor-scope-key-required-for-sensitive-cache-entry\"],\n };\n }\n\n const exactKey = createAiSpeechCacheKey({\n ...input.voice,\n normalizedRenderText: rendered.normalizedRenderText,\n scopeDiscriminator: actorScopeRequired ? actorScopeKey : undefined,\n });\n\n const nearReuseEnabled =\n input.voice.cachePermission === \"exact-and-near\" &&\n isAiSpeechFeatureEnabled(\n AI_SPEECH_FEATURE_FLAGS.ttsNearReuse,\n input.featureFlags\n ) &&\n isAiSpeechNearReuseSafeClass(input.utteranceClass) &&\n !containsSensitiveContent &&\n (rendered.playerAddress?.nearReuseAllowed ?? true);\n\n if (!nearReuseEnabled) {\n return {\n mode: \"exact\",\n sharingScope: actorScopeRequired ? \"actor\" : \"global\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n exactKey,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: containsSensitiveContent\n ? [\"exact-cache-only-for-sensitive-or-redacted-content\"]\n : [\"near-reuse-disabled-or-ineligible\"],\n };\n }\n\n enabledFeatureFlags.push(AI_SPEECH_FEATURE_FLAGS.ttsNearReuse);\n\n const nearKey = createAiSpeechCacheKey({\n ...input.voice,\n normalizedRenderText: createAiSpeechNearReuseFingerprint(\n rendered.normalizedRenderText\n ),\n });\n\n return {\n mode: \"near\",\n sharingScope: \"global\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n exactKey,\n nearKey,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: [\"near-reuse-eligible\"],\n };\n}\n\nexport const AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID =\n \"isekai.player-system.audio.enabled\" as const;\n\nexport const AI_SPEECH_AUDIO_CHANNELS = [\n \"narrated-response\",\n \"localized-cue\",\n \"repeating-warning\",\n] as const;\n\nexport type AiSpeechAudioChannel = (typeof AI_SPEECH_AUDIO_CHANNELS)[number];\n\nexport const AI_SPEECH_AUDIO_PRIORITIES = [\n \"critical\",\n \"high\",\n \"normal\",\n \"low\",\n] as const;\n\nexport type AiSpeechAudioPriority =\n (typeof AI_SPEECH_AUDIO_PRIORITIES)[number];\n\nexport const AI_SPEECH_AUDIO_DUCKING_MODES = [\n \"none\",\n \"music\",\n \"non-critical\",\n \"lower-priority\",\n] as const;\n\nexport type AiSpeechAudioDuckingMode =\n (typeof AI_SPEECH_AUDIO_DUCKING_MODES)[number];\n\nexport const AI_SPEECH_AUDIO_CUE_FAMILIES = [\n \"status\",\n \"tutorial\",\n \"mission\",\n \"mcc\",\n \"warning\",\n] as const;\n\nexport type AiSpeechAudioCueFamily =\n (typeof AI_SPEECH_AUDIO_CUE_FAMILIES)[number];\n\nexport const AI_SPEECH_AUDIO_FOCUS_MODES = [\n \"ambient\",\n \"focused\",\n \"combat-safe\",\n] as const;\n\nexport type AiSpeechAudioFocusMode =\n (typeof AI_SPEECH_AUDIO_FOCUS_MODES)[number];\n\nexport const AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES = [\n \"full\",\n \"condensed\",\n \"deferred\",\n \"suppressed\",\n] as const;\n\nexport type AiSpeechAudioCombatSafeDelivery =\n (typeof AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES)[number];\n\nexport interface AiSpeechAudioContractBase {\n readonly id: string;\n readonly featureFlagId: typeof AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID;\n readonly priority: AiSpeechAudioPriority;\n readonly ducking: AiSpeechAudioDuckingMode;\n readonly combatSafeDelivery: AiSpeechAudioCombatSafeDelivery;\n}\n\nexport interface AiSpeechNarratedResponseContract\n extends AiSpeechAudioContractBase {\n readonly channel: \"narrated-response\";\n readonly utteranceId: string;\n readonly locale: string;\n}\n\nexport interface AiSpeechLocalizedCueContract extends AiSpeechAudioContractBase {\n readonly channel: \"localized-cue\";\n readonly cueFamily: AiSpeechAudioCueFamily;\n readonly cueId: string;\n readonly locale: string;\n}\n\nexport interface AiSpeechRepeatingWarningContract\n extends AiSpeechAudioContractBase {\n readonly channel: \"repeating-warning\";\n readonly warningId: string;\n readonly intervalSeconds: number;\n readonly maximumOccurrencesPerMinute: number;\n}\n\nexport type AiSpeechAudioContract =\n | AiSpeechNarratedResponseContract\n | AiSpeechLocalizedCueContract\n | AiSpeechRepeatingWarningContract;\n\nexport interface AiSpeechAudioContractBaseInput {\n readonly id: string;\n readonly priority: AiSpeechAudioPriority;\n readonly ducking: AiSpeechAudioDuckingMode;\n readonly combatSafeDelivery: AiSpeechAudioCombatSafeDelivery;\n}\n\nexport interface CreateAiSpeechNarratedResponseInput\n extends AiSpeechAudioContractBaseInput {\n readonly utteranceId: string;\n readonly locale: string;\n}\n\nexport interface CreateAiSpeechLocalizedCueInput\n extends AiSpeechAudioContractBaseInput {\n readonly cueFamily: AiSpeechAudioCueFamily;\n readonly cueId: string;\n readonly locale: string;\n}\n\nexport interface CreateAiSpeechRepeatingWarningInput\n extends AiSpeechAudioContractBaseInput {\n readonly warningId: string;\n readonly intervalSeconds: number;\n readonly maximumOccurrencesPerMinute: number;\n}\n\nfunction requireAudioEnum<T extends string>(\n value: T,\n allowed: readonly T[],\n label: string\n): T {\n if (!allowed.includes(value)) {\n throw new Error(`${label} is not supported: ${value}.`);\n }\n\n return value;\n}\n\nfunction requireBoundedAudioNumber(\n value: number,\n minimum: number,\n maximum: number,\n label: string\n): number {\n if (!Number.isFinite(value) || value < minimum || value > maximum) {\n throw new Error(\n `${label} must be between ${minimum} and ${maximum}, inclusive.`\n );\n }\n\n return value;\n}\n\nfunction createAiSpeechAudioContractBase(\n input: AiSpeechAudioContractBaseInput\n): AiSpeechAudioContractBase {\n return {\n id: requireNonEmptyString(input.id, \"Audio contract id\"),\n featureFlagId: AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID,\n priority: requireAudioEnum(\n input.priority,\n AI_SPEECH_AUDIO_PRIORITIES,\n \"Audio priority\"\n ),\n ducking: requireAudioEnum(\n input.ducking,\n AI_SPEECH_AUDIO_DUCKING_MODES,\n \"Audio ducking mode\"\n ),\n combatSafeDelivery: requireAudioEnum(\n input.combatSafeDelivery,\n AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES,\n \"Combat-safe audio delivery\"\n ),\n };\n}\n\nexport function createAiSpeechNarratedResponse(\n input: CreateAiSpeechNarratedResponseInput\n): AiSpeechNarratedResponseContract {\n return Object.freeze({\n ...createAiSpeechAudioContractBase(input),\n channel: \"narrated-response\" as const,\n utteranceId: requireNonEmptyString(input.utteranceId, \"Utterance id\"),\n locale: requireNonEmptyString(input.locale, \"Locale\"),\n });\n}\n\nexport function createAiSpeechLocalizedCue(\n input: CreateAiSpeechLocalizedCueInput\n): AiSpeechLocalizedCueContract {\n return Object.freeze({\n ...createAiSpeechAudioContractBase(input),\n channel: \"localized-cue\" as const,\n cueFamily: requireAudioEnum(\n input.cueFamily,\n AI_SPEECH_AUDIO_CUE_FAMILIES,\n \"Audio cue family\"\n ),\n cueId: requireNonEmptyString(input.cueId, \"Cue id\"),\n locale: requireNonEmptyString(input.locale, \"Locale\"),\n });\n}\n\nexport function createAiSpeechRepeatingWarning(\n input: CreateAiSpeechRepeatingWarningInput\n): AiSpeechRepeatingWarningContract {\n return Object.freeze({\n ...createAiSpeechAudioContractBase(input),\n channel: \"repeating-warning\" as const,\n warningId: requireNonEmptyString(input.warningId, \"Warning id\"),\n intervalSeconds: requireBoundedAudioNumber(\n input.intervalSeconds,\n 1,\n 3600,\n \"Warning intervalSeconds\"\n ),\n maximumOccurrencesPerMinute: requireBoundedAudioNumber(\n input.maximumOccurrencesPerMinute,\n 1,\n 60,\n \"Warning maximumOccurrencesPerMinute\"\n ),\n });\n}\n\nexport interface ResolveAiSpeechAudioPolicyInput {\n readonly contract: AiSpeechAudioContract;\n readonly focusMode: AiSpeechAudioFocusMode;\n readonly featureFlags?: AiSpeechFeatureFlagSnapshot;\n readonly masterMuted?: boolean;\n readonly userMuted?: boolean;\n readonly activeContractIds?: readonly string[];\n}\n\nexport interface AiSpeechAudioPolicyDecision {\n readonly deliver: boolean;\n readonly mode: \"full\" | \"condensed\" | \"deferred\";\n readonly ducking: AiSpeechAudioDuckingMode;\n readonly reasonCodes: readonly string[];\n}\n\nfunction isPlayerSystemAudioEnabled(\n featureFlags: AiSpeechFeatureFlagSnapshot = {}\n): boolean {\n return featureFlags[AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID] === true;\n}\n\nexport function resolveAiSpeechAudioPolicy(\n input: ResolveAiSpeechAudioPolicyInput\n): AiSpeechAudioPolicyDecision {\n const { contract } = input;\n\n if (!isPlayerSystemAudioEnabled(input.featureFlags)) {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [\"player-system-audio-rollout-disabled\"],\n };\n }\n\n if (input.masterMuted || input.userMuted) {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [input.masterMuted ? \"master-muted\" : \"user-muted\"],\n };\n }\n\n if (input.activeContractIds?.includes(contract.id)) {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [\"duplicate-audio-contract-suppressed\"],\n };\n }\n\n if (\n input.focusMode === \"combat-safe\" &&\n contract.priority !== \"critical\" &&\n contract.priority !== \"high\"\n ) {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [\"combat-safe-priority-suppressed\"],\n };\n }\n\n if (contract.combatSafeDelivery === \"suppressed\") {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [\"audio-contract-suppressed\"],\n };\n }\n\n const mode =\n input.focusMode === \"combat-safe\"\n ? contract.combatSafeDelivery === \"condensed\"\n ? \"condensed\"\n : contract.combatSafeDelivery === \"deferred\"\n ? \"deferred\"\n : \"full\"\n : \"full\";\n const ducking = contract.priority === \"critical\" ? \"none\" : contract.ducking;\n\n return {\n deliver: mode !== \"deferred\",\n mode,\n ducking,\n reasonCodes:\n contract.priority === \"critical\"\n ? [\"critical-priority-bypasses-ducking\"]\n : [],\n };\n}\n\nexport const packageDescriptor: AiPackageDescriptor = Object.freeze({\n packageName: AI_SPEECH_PACKAGE,\n featureFlagId: AI_SPEECH_FEATURE_FLAG_ID,\n envPrefix: AI_SPEECH_ENV_PREFIX,\n summary:\n \"Speech orchestration, TTS cache contracts, STT/TTS routing, and voice tier policy for Plasius AI.\",\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export interface AiPackageDescriptor {\n readonly packageName: string;\n readonly featureFlagId: string;\n readonly envPrefix: string;\n readonly summary: string;\n}\n\nfunction requireNonEmptyString(value: string, label: string): string {\n const trimmed = value.trim();\n if (trimmed.length === 0) {\n throw new Error(`${label} must be a non-empty string.`);\n }\n\n return trimmed;\n}\n\nfunction normalizeWhitespace(value: string): string {\n return value.replace(/\\s+/gu, \" \").trim();\n}\n\nexport const AI_SPEECH_PACKAGE = \"@plasius/ai-speech\";\nexport const AI_SPEECH_ENV_PREFIX = \"AI_SPEECH\";\n\nexport const AI_SPEECH_FEATURE_FLAGS = {\n ttsCache: \"ai.tts.cache.enabled\",\n ttsNearReuse: \"ai.tts.near-reuse.enabled\",\n premiumCharacters: \"ai.tts.premium-characters.enabled\",\n} as const;\n\nexport type AiSpeechFeatureFlagKey =\n (typeof AI_SPEECH_FEATURE_FLAGS)[keyof typeof AI_SPEECH_FEATURE_FLAGS];\n\nexport type AiSpeechFeatureFlagSnapshot = Readonly<\n Record<string, boolean | undefined>\n>;\n\nexport const AI_SPEECH_FEATURE_FLAG_ID = AI_SPEECH_FEATURE_FLAGS.ttsCache;\n\nexport const AI_SPEECH_ROLLOUT_EVALUATORS = [\n \"remote-flag-service\",\n \"host-application\",\n \"break-glass-env\",\n] as const;\n\nexport type AiSpeechRolloutEvaluator =\n (typeof AI_SPEECH_ROLLOUT_EVALUATORS)[number];\n\nexport const AI_SPEECH_ROLLOUT_FALLBACK_MODES = [\n \"fail-closed\",\n \"fail-open\",\n] as const;\n\nexport type AiSpeechRolloutFallbackMode =\n (typeof AI_SPEECH_ROLLOUT_FALLBACK_MODES)[number];\n\nexport interface AiSpeechRolloutControl {\n readonly featureFlag: AiSpeechFeatureFlagKey;\n readonly evaluator: AiSpeechRolloutEvaluator;\n readonly defaultEnabled: boolean;\n readonly fallbackMode: AiSpeechRolloutFallbackMode;\n}\n\nexport interface AiSpeechRolloutDecision extends AiSpeechRolloutControl {\n readonly enabled: boolean;\n readonly source: \"snapshot\" | \"default\";\n}\n\nexport const AI_SPEECH_ROLLOUTS = Object.freeze({\n ttsCache: Object.freeze<AiSpeechRolloutControl>({\n featureFlag: AI_SPEECH_FEATURE_FLAGS.ttsCache,\n evaluator: \"remote-flag-service\",\n defaultEnabled: false,\n fallbackMode: \"fail-closed\",\n }),\n ttsNearReuse: Object.freeze<AiSpeechRolloutControl>({\n featureFlag: AI_SPEECH_FEATURE_FLAGS.ttsNearReuse,\n evaluator: \"remote-flag-service\",\n defaultEnabled: false,\n fallbackMode: \"fail-closed\",\n }),\n premiumCharacters: Object.freeze<AiSpeechRolloutControl>({\n featureFlag: AI_SPEECH_FEATURE_FLAGS.premiumCharacters,\n evaluator: \"remote-flag-service\",\n defaultEnabled: false,\n fallbackMode: \"fail-closed\",\n }),\n});\n\nexport function resolveAiSpeechRolloutDecision(\n control: AiSpeechRolloutControl,\n snapshot: AiSpeechFeatureFlagSnapshot = {}\n): AiSpeechRolloutDecision {\n const resolved = snapshot[control.featureFlag];\n if (typeof resolved === \"boolean\") {\n return {\n ...control,\n enabled: resolved,\n source: \"snapshot\",\n };\n }\n\n return {\n ...control,\n enabled: control.defaultEnabled,\n source: \"default\",\n };\n}\n\nexport function isAiSpeechFeatureEnabled(\n featureFlag: AiSpeechFeatureFlagKey,\n snapshot: AiSpeechFeatureFlagSnapshot = {}\n): boolean {\n const control = Object.values(AI_SPEECH_ROLLOUTS).find(\n (candidate) => candidate.featureFlag === featureFlag\n );\n\n if (!control) {\n return false;\n }\n\n return resolveAiSpeechRolloutDecision(control, snapshot).enabled;\n}\n\nexport const AI_SPEECH_VOICE_TIERS = [\n \"development\",\n \"standard\",\n \"premium-character\",\n] as const;\n\nexport type AiSpeechVoiceTier = (typeof AI_SPEECH_VOICE_TIERS)[number];\n\nexport const AI_SPEECH_ENVIRONMENTS = [\n \"development\",\n \"preview\",\n \"production\",\n] as const;\n\nexport type AiSpeechEnvironment = (typeof AI_SPEECH_ENVIRONMENTS)[number];\n\nexport interface AiSpeechVoiceTierDecision {\n readonly requestedTier: AiSpeechVoiceTier;\n readonly resolvedTier: AiSpeechVoiceTier;\n readonly fallbackTier?: AiSpeechVoiceTier;\n readonly reasonCodes: readonly string[];\n}\n\nexport interface ResolveAiSpeechVoiceTierInput {\n readonly environment: AiSpeechEnvironment;\n readonly requestedTier?: AiSpeechVoiceTier;\n readonly featureFlags?: AiSpeechFeatureFlagSnapshot;\n}\n\nexport function resolveAiSpeechVoiceTier(\n input: ResolveAiSpeechVoiceTierInput\n): AiSpeechVoiceTierDecision {\n const requestedTier =\n input.requestedTier ??\n (input.environment === \"development\" ? \"development\" : \"standard\");\n\n if (\n requestedTier === \"premium-character\" &&\n !isAiSpeechFeatureEnabled(\n AI_SPEECH_FEATURE_FLAGS.premiumCharacters,\n input.featureFlags\n )\n ) {\n return {\n requestedTier,\n resolvedTier: \"standard\",\n fallbackTier: \"standard\",\n reasonCodes: [\"premium-character-rollout-disabled\"],\n };\n }\n\n if (\n requestedTier === \"development\" &&\n input.environment !== \"development\"\n ) {\n return {\n requestedTier,\n resolvedTier: \"standard\",\n fallbackTier: \"standard\",\n reasonCodes: [\"development-voices-limited-to-development-environments\"],\n };\n }\n\n return {\n requestedTier,\n resolvedTier: requestedTier,\n reasonCodes: [],\n };\n}\n\nexport const AI_SPEECH_PLAYER_ADDRESS_SOURCES = [\n \"generic-player\",\n \"class-title\",\n \"faction-title\",\n \"authored-character\",\n \"user-name\",\n \"account-handle\",\n \"user-renamed-character\",\n] as const;\n\nexport type AiSpeechPlayerAddressSource =\n (typeof AI_SPEECH_PLAYER_ADDRESS_SOURCES)[number];\n\nexport const AI_SPEECH_DEFAULT_PLAYER_LABEL = \"Player\";\n\nexport interface AiSpeechPlayerAddressInput {\n readonly source: AiSpeechPlayerAddressSource;\n readonly rawValue?: string;\n readonly fallbackLabel?: string;\n}\n\nexport interface AiSpeechPlayerAddressDecision {\n readonly source: AiSpeechPlayerAddressSource;\n readonly renderText: string;\n readonly exactReuseAllowed: boolean;\n readonly nearReuseAllowed: boolean;\n readonly redacted: boolean;\n readonly reasonCodes: readonly string[];\n}\n\nexport function resolveAiSpeechPlayerAddress(\n input: AiSpeechPlayerAddressInput\n): AiSpeechPlayerAddressDecision {\n const fallbackLabel =\n normalizeWhitespace(input.fallbackLabel ?? \"\") ||\n AI_SPEECH_DEFAULT_PLAYER_LABEL;\n const rawValue = normalizeWhitespace(input.rawValue ?? \"\");\n\n switch (input.source) {\n case \"generic-player\":\n return {\n source: input.source,\n renderText: fallbackLabel,\n exactReuseAllowed: true,\n nearReuseAllowed: true,\n redacted: false,\n reasonCodes: [\"generic-player-address\"],\n };\n case \"class-title\":\n case \"faction-title\":\n return {\n source: input.source,\n renderText: rawValue || fallbackLabel,\n exactReuseAllowed: true,\n nearReuseAllowed: true,\n redacted: false,\n reasonCodes: rawValue ? [] : [\"player-address-fell-back-to-generic-label\"],\n };\n case \"authored-character\":\n return {\n source: input.source,\n renderText: rawValue || fallbackLabel,\n exactReuseAllowed: true,\n nearReuseAllowed: true,\n redacted: false,\n reasonCodes: rawValue ? [] : [\"player-address-fell-back-to-generic-label\"],\n };\n case \"user-name\":\n case \"account-handle\":\n case \"user-renamed-character\":\n return {\n source: input.source,\n renderText: fallbackLabel,\n exactReuseAllowed: true,\n nearReuseAllowed: false,\n redacted: true,\n reasonCodes: [\"player-identifier-redacted-from-render-text\"],\n };\n }\n}\n\nexport interface AiSpeechRenderTextInput {\n readonly textTemplate: string;\n readonly playerAddress?: AiSpeechPlayerAddressInput;\n}\n\nexport interface AiSpeechRenderTextResult {\n readonly renderText: string;\n readonly normalizedRenderText: string;\n readonly playerAddress?: AiSpeechPlayerAddressDecision;\n}\n\nexport function renderAiSpeechText(\n input: AiSpeechRenderTextInput\n): AiSpeechRenderTextResult {\n const textTemplate = requireNonEmptyString(\n input.textTemplate,\n \"Speech text template\"\n );\n const hasPlayerPlaceholder = textTemplate.includes(\"{playerAddress}\");\n\n if (hasPlayerPlaceholder && !input.playerAddress) {\n throw new Error(\n \"Speech text template requires playerAddress when using the {playerAddress} placeholder.\"\n );\n }\n\n const playerAddress = input.playerAddress\n ? resolveAiSpeechPlayerAddress(input.playerAddress)\n : undefined;\n const renderText = normalizeWhitespace(\n hasPlayerPlaceholder && playerAddress\n ? textTemplate.replace(/\\{playerAddress\\}/gu, playerAddress.renderText)\n : textTemplate\n );\n\n return {\n renderText,\n normalizedRenderText: normalizeAiSpeechText(renderText),\n playerAddress,\n };\n}\n\nexport function normalizeAiSpeechText(value: string): string {\n return requireNonEmptyString(\n normalizeWhitespace(value).toLocaleLowerCase(\"en-GB\"),\n \"Speech text\"\n );\n}\n\nexport const AI_SPEECH_PROVIDER_CACHE_PERMISSIONS = [\n \"forbidden\",\n \"exact-only\",\n \"exact-and-near\",\n] as const;\n\nexport type AiSpeechProviderCachePermission =\n (typeof AI_SPEECH_PROVIDER_CACHE_PERMISSIONS)[number];\n\nexport const AI_SPEECH_CACHE_MODES = [\n \"disabled\",\n \"no-cache\",\n \"exact\",\n \"near\",\n] as const;\n\nexport type AiSpeechCacheMode = (typeof AI_SPEECH_CACHE_MODES)[number];\n\nexport const AI_SPEECH_CACHE_SCOPES = [\"none\", \"actor\", \"global\"] as const;\n\nexport type AiSpeechCacheScope = (typeof AI_SPEECH_CACHE_SCOPES)[number];\n\nexport const AI_SPEECH_UTTERANCE_CLASSES = [\n \"system-generic\",\n \"game-npc-dialogue\",\n \"game-bark\",\n \"player-address\",\n \"moderation-notice\",\n \"private-response\",\n] as const;\n\nexport type AiSpeechUtteranceClass =\n (typeof AI_SPEECH_UTTERANCE_CLASSES)[number];\n\nexport const AI_SPEECH_NEAR_REUSE_SAFE_CLASSES = [\n \"system-generic\",\n \"game-npc-dialogue\",\n \"game-bark\",\n \"player-address\",\n] as const satisfies readonly AiSpeechUtteranceClass[];\n\nconst AI_SPEECH_NEAR_REUSE_SAFE_CLASS_SET: ReadonlySet<AiSpeechUtteranceClass> =\n new Set(AI_SPEECH_NEAR_REUSE_SAFE_CLASSES);\n\nexport interface AiSpeechCacheKeyInput {\n readonly providerId: string;\n readonly modelId: string;\n readonly voiceId: string;\n readonly locale: string;\n readonly format: string;\n readonly pronunciationVersion: string;\n readonly normalizedRenderText: string;\n readonly styleId?: string;\n readonly scopeDiscriminator?: string;\n}\n\nexport interface AiSpeechVoiceProfileRef {\n readonly providerId: string;\n readonly modelId: string;\n readonly voiceId: string;\n readonly locale: string;\n readonly format: string;\n readonly pronunciationVersion: string;\n readonly styleId?: string;\n readonly cachePermission: AiSpeechProviderCachePermission;\n readonly tier?: AiSpeechVoiceTier;\n readonly profileId?: string;\n}\n\nexport interface AiSpeechCacheTelemetry {\n readonly cacheHits: number;\n readonly nearCacheHits: number;\n readonly cacheMisses: number;\n readonly charactersSaved: number;\n readonly estimatedCostSavedUsd?: number;\n readonly voiceProfileIds?: readonly string[];\n}\n\nexport interface PlanAiSpeechCacheInput {\n readonly utteranceClass: AiSpeechUtteranceClass;\n readonly textTemplate: string;\n readonly playerAddress?: AiSpeechPlayerAddressInput;\n readonly voice: AiSpeechVoiceProfileRef;\n readonly featureFlags?: AiSpeechFeatureFlagSnapshot;\n readonly actorScopeKey?: string;\n readonly containsPersonalData?: boolean;\n readonly containsPrivateContext?: boolean;\n readonly containsModerationNotice?: boolean;\n}\n\nexport interface AiSpeechCachePlan {\n readonly mode: AiSpeechCacheMode;\n readonly sharingScope: AiSpeechCacheScope;\n readonly renderText: string;\n readonly normalizedRenderText: string;\n readonly exactKey?: string;\n readonly nearKey?: string;\n readonly enabledFeatureFlags: readonly AiSpeechFeatureFlagKey[];\n readonly playerAddress?: AiSpeechPlayerAddressDecision;\n readonly reasonCodes: readonly string[];\n}\n\nexport function createAiSpeechCacheKey(input: AiSpeechCacheKeyInput): string {\n const scopeDiscriminator = normalizeWhitespace(input.scopeDiscriminator ?? \"\");\n return [\n requireNonEmptyString(input.providerId, \"Provider id\"),\n requireNonEmptyString(input.modelId, \"Model id\"),\n requireNonEmptyString(input.voiceId, \"Voice id\"),\n requireNonEmptyString(input.locale, \"Locale\"),\n normalizeWhitespace(input.styleId ?? \"\") || \"_\",\n requireNonEmptyString(input.format, \"Format\"),\n requireNonEmptyString(\n input.pronunciationVersion,\n \"Pronunciation version\"\n ),\n scopeDiscriminator || \"global\",\n normalizeAiSpeechText(input.normalizedRenderText),\n ]\n .map((segment) => encodeURIComponent(segment))\n .join(\"::\");\n}\n\nexport function createAiSpeechNearReuseFingerprint(value: string): string {\n const normalized = normalizeAiSpeechText(value)\n .replace(/[^a-z0-9\\s]/gu, \" \")\n .replace(/\\s+/gu, \" \")\n .trim();\n\n return requireNonEmptyString(normalized, \"Near-reuse fingerprint\");\n}\n\nexport function isAiSpeechNearReuseSafeClass(\n utteranceClass: AiSpeechUtteranceClass\n): boolean {\n return AI_SPEECH_NEAR_REUSE_SAFE_CLASS_SET.has(utteranceClass);\n}\n\nexport function planAiSpeechCache(\n input: PlanAiSpeechCacheInput\n): AiSpeechCachePlan {\n const rendered = renderAiSpeechText({\n textTemplate: input.textTemplate,\n playerAddress: input.playerAddress,\n });\n const enabledFeatureFlags: AiSpeechFeatureFlagKey[] = [];\n const cacheEnabled = isAiSpeechFeatureEnabled(\n AI_SPEECH_FEATURE_FLAGS.ttsCache,\n input.featureFlags\n );\n\n if (!cacheEnabled) {\n return {\n mode: \"disabled\",\n sharingScope: \"none\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: [\"tts-cache-rollout-disabled\"],\n };\n }\n\n enabledFeatureFlags.push(AI_SPEECH_FEATURE_FLAGS.ttsCache);\n\n if (input.voice.cachePermission === \"forbidden\") {\n return {\n mode: \"no-cache\",\n sharingScope: \"none\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: [\"provider-forbids-tts-caching\"],\n };\n }\n\n const containsSensitiveContent =\n Boolean(input.containsPersonalData) ||\n Boolean(input.containsPrivateContext) ||\n Boolean(input.containsModerationNotice) ||\n Boolean(rendered.playerAddress?.redacted);\n const actorScopeKey = normalizeWhitespace(input.actorScopeKey ?? \"\");\n\n const actorScopeRequired =\n containsSensitiveContent || input.utteranceClass === \"private-response\";\n\n if (actorScopeRequired && !actorScopeKey) {\n return {\n mode: \"no-cache\",\n sharingScope: \"none\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: [\"actor-scope-key-required-for-sensitive-cache-entry\"],\n };\n }\n\n const exactKey = createAiSpeechCacheKey({\n ...input.voice,\n normalizedRenderText: rendered.normalizedRenderText,\n scopeDiscriminator: actorScopeRequired ? actorScopeKey : undefined,\n });\n\n const nearReuseEnabled =\n input.voice.cachePermission === \"exact-and-near\" &&\n isAiSpeechFeatureEnabled(\n AI_SPEECH_FEATURE_FLAGS.ttsNearReuse,\n input.featureFlags\n ) &&\n isAiSpeechNearReuseSafeClass(input.utteranceClass) &&\n !containsSensitiveContent &&\n (rendered.playerAddress?.nearReuseAllowed ?? true);\n\n if (!nearReuseEnabled) {\n return {\n mode: \"exact\",\n sharingScope: actorScopeRequired ? \"actor\" : \"global\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n exactKey,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: containsSensitiveContent\n ? [\"exact-cache-only-for-sensitive-or-redacted-content\"]\n : [\"near-reuse-disabled-or-ineligible\"],\n };\n }\n\n enabledFeatureFlags.push(AI_SPEECH_FEATURE_FLAGS.ttsNearReuse);\n\n const nearKey = createAiSpeechCacheKey({\n ...input.voice,\n normalizedRenderText: createAiSpeechNearReuseFingerprint(\n rendered.normalizedRenderText\n ),\n });\n\n return {\n mode: \"near\",\n sharingScope: \"global\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n exactKey,\n nearKey,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: [\"near-reuse-eligible\"],\n };\n}\n\nexport const AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID =\n \"harmony.player-system.audio.enabled\" as const;\n\nexport const AI_SPEECH_AUDIO_CHANNELS = [\n \"narrated-response\",\n \"localized-cue\",\n \"repeating-warning\",\n] as const;\n\nexport type AiSpeechAudioChannel = (typeof AI_SPEECH_AUDIO_CHANNELS)[number];\n\nexport const AI_SPEECH_AUDIO_PRIORITIES = [\n \"critical\",\n \"high\",\n \"normal\",\n \"low\",\n] as const;\n\nexport type AiSpeechAudioPriority =\n (typeof AI_SPEECH_AUDIO_PRIORITIES)[number];\n\nexport const AI_SPEECH_AUDIO_DUCKING_MODES = [\n \"none\",\n \"music\",\n \"non-critical\",\n \"lower-priority\",\n] as const;\n\nexport type AiSpeechAudioDuckingMode =\n (typeof AI_SPEECH_AUDIO_DUCKING_MODES)[number];\n\nexport const AI_SPEECH_AUDIO_CUE_FAMILIES = [\n \"status\",\n \"tutorial\",\n \"mission\",\n \"mcc\",\n \"warning\",\n] as const;\n\nexport type AiSpeechAudioCueFamily =\n (typeof AI_SPEECH_AUDIO_CUE_FAMILIES)[number];\n\nexport const AI_SPEECH_AUDIO_FOCUS_MODES = [\n \"ambient\",\n \"focused\",\n \"combat-safe\",\n] as const;\n\nexport type AiSpeechAudioFocusMode =\n (typeof AI_SPEECH_AUDIO_FOCUS_MODES)[number];\n\nexport const AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES = [\n \"full\",\n \"condensed\",\n \"deferred\",\n \"suppressed\",\n] as const;\n\nexport type AiSpeechAudioCombatSafeDelivery =\n (typeof AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES)[number];\n\nexport interface AiSpeechAudioContractBase {\n readonly id: string;\n readonly featureFlagId: typeof AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID;\n readonly priority: AiSpeechAudioPriority;\n readonly ducking: AiSpeechAudioDuckingMode;\n readonly combatSafeDelivery: AiSpeechAudioCombatSafeDelivery;\n}\n\nexport interface AiSpeechNarratedResponseContract\n extends AiSpeechAudioContractBase {\n readonly channel: \"narrated-response\";\n readonly utteranceId: string;\n readonly locale: string;\n}\n\nexport interface AiSpeechLocalizedCueContract extends AiSpeechAudioContractBase {\n readonly channel: \"localized-cue\";\n readonly cueFamily: AiSpeechAudioCueFamily;\n readonly cueId: string;\n readonly locale: string;\n}\n\nexport interface AiSpeechRepeatingWarningContract\n extends AiSpeechAudioContractBase {\n readonly channel: \"repeating-warning\";\n readonly warningId: string;\n readonly intervalSeconds: number;\n readonly maximumOccurrencesPerMinute: number;\n}\n\nexport type AiSpeechAudioContract =\n | AiSpeechNarratedResponseContract\n | AiSpeechLocalizedCueContract\n | AiSpeechRepeatingWarningContract;\n\nexport interface AiSpeechAudioContractBaseInput {\n readonly id: string;\n readonly priority: AiSpeechAudioPriority;\n readonly ducking: AiSpeechAudioDuckingMode;\n readonly combatSafeDelivery: AiSpeechAudioCombatSafeDelivery;\n}\n\nexport interface CreateAiSpeechNarratedResponseInput\n extends AiSpeechAudioContractBaseInput {\n readonly utteranceId: string;\n readonly locale: string;\n}\n\nexport interface CreateAiSpeechLocalizedCueInput\n extends AiSpeechAudioContractBaseInput {\n readonly cueFamily: AiSpeechAudioCueFamily;\n readonly cueId: string;\n readonly locale: string;\n}\n\nexport interface CreateAiSpeechRepeatingWarningInput\n extends AiSpeechAudioContractBaseInput {\n readonly warningId: string;\n readonly intervalSeconds: number;\n readonly maximumOccurrencesPerMinute: number;\n}\n\nfunction requireAudioEnum<T extends string>(\n value: T,\n allowed: readonly T[],\n label: string\n): T {\n if (!allowed.includes(value)) {\n throw new Error(`${label} is not supported: ${value}.`);\n }\n\n return value;\n}\n\nfunction requireBoundedAudioNumber(\n value: number,\n minimum: number,\n maximum: number,\n label: string\n): number {\n if (!Number.isFinite(value) || value < minimum || value > maximum) {\n throw new Error(\n `${label} must be between ${minimum} and ${maximum}, inclusive.`\n );\n }\n\n return value;\n}\n\nfunction createAiSpeechAudioContractBase(\n input: AiSpeechAudioContractBaseInput\n): AiSpeechAudioContractBase {\n return {\n id: requireNonEmptyString(input.id, \"Audio contract id\"),\n featureFlagId: AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID,\n priority: requireAudioEnum(\n input.priority,\n AI_SPEECH_AUDIO_PRIORITIES,\n \"Audio priority\"\n ),\n ducking: requireAudioEnum(\n input.ducking,\n AI_SPEECH_AUDIO_DUCKING_MODES,\n \"Audio ducking mode\"\n ),\n combatSafeDelivery: requireAudioEnum(\n input.combatSafeDelivery,\n AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES,\n \"Combat-safe audio delivery\"\n ),\n };\n}\n\nexport function createAiSpeechNarratedResponse(\n input: CreateAiSpeechNarratedResponseInput\n): AiSpeechNarratedResponseContract {\n return Object.freeze({\n ...createAiSpeechAudioContractBase(input),\n channel: \"narrated-response\" as const,\n utteranceId: requireNonEmptyString(input.utteranceId, \"Utterance id\"),\n locale: requireNonEmptyString(input.locale, \"Locale\"),\n });\n}\n\nexport function createAiSpeechLocalizedCue(\n input: CreateAiSpeechLocalizedCueInput\n): AiSpeechLocalizedCueContract {\n return Object.freeze({\n ...createAiSpeechAudioContractBase(input),\n channel: \"localized-cue\" as const,\n cueFamily: requireAudioEnum(\n input.cueFamily,\n AI_SPEECH_AUDIO_CUE_FAMILIES,\n \"Audio cue family\"\n ),\n cueId: requireNonEmptyString(input.cueId, \"Cue id\"),\n locale: requireNonEmptyString(input.locale, \"Locale\"),\n });\n}\n\nexport function createAiSpeechRepeatingWarning(\n input: CreateAiSpeechRepeatingWarningInput\n): AiSpeechRepeatingWarningContract {\n return Object.freeze({\n ...createAiSpeechAudioContractBase(input),\n channel: \"repeating-warning\" as const,\n warningId: requireNonEmptyString(input.warningId, \"Warning id\"),\n intervalSeconds: requireBoundedAudioNumber(\n input.intervalSeconds,\n 1,\n 3600,\n \"Warning intervalSeconds\"\n ),\n maximumOccurrencesPerMinute: requireBoundedAudioNumber(\n input.maximumOccurrencesPerMinute,\n 1,\n 60,\n \"Warning maximumOccurrencesPerMinute\"\n ),\n });\n}\n\nexport interface ResolveAiSpeechAudioPolicyInput {\n readonly contract: AiSpeechAudioContract;\n readonly focusMode: AiSpeechAudioFocusMode;\n readonly featureFlags?: AiSpeechFeatureFlagSnapshot;\n readonly masterMuted?: boolean;\n readonly userMuted?: boolean;\n readonly activeContractIds?: readonly string[];\n}\n\nexport interface AiSpeechAudioPolicyDecision {\n readonly deliver: boolean;\n readonly mode: \"full\" | \"condensed\" | \"deferred\";\n readonly ducking: AiSpeechAudioDuckingMode;\n readonly reasonCodes: readonly string[];\n}\n\nfunction isPlayerSystemAudioEnabled(\n featureFlags: AiSpeechFeatureFlagSnapshot = {}\n): boolean {\n return featureFlags[AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID] === true;\n}\n\nexport function resolveAiSpeechAudioPolicy(\n input: ResolveAiSpeechAudioPolicyInput\n): AiSpeechAudioPolicyDecision {\n const { contract } = input;\n\n if (!isPlayerSystemAudioEnabled(input.featureFlags)) {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [\"player-system-audio-rollout-disabled\"],\n };\n }\n\n if (input.masterMuted || input.userMuted) {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [input.masterMuted ? \"master-muted\" : \"user-muted\"],\n };\n }\n\n if (input.activeContractIds?.includes(contract.id)) {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [\"duplicate-audio-contract-suppressed\"],\n };\n }\n\n if (\n input.focusMode === \"combat-safe\" &&\n contract.priority !== \"critical\" &&\n contract.priority !== \"high\"\n ) {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [\"combat-safe-priority-suppressed\"],\n };\n }\n\n if (contract.combatSafeDelivery === \"suppressed\") {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [\"audio-contract-suppressed\"],\n };\n }\n\n const mode =\n input.focusMode === \"combat-safe\"\n ? contract.combatSafeDelivery === \"condensed\"\n ? \"condensed\"\n : contract.combatSafeDelivery === \"deferred\"\n ? \"deferred\"\n : \"full\"\n : \"full\";\n const ducking = contract.priority === \"critical\" ? \"none\" : contract.ducking;\n\n return {\n deliver: mode !== \"deferred\",\n mode,\n ducking,\n reasonCodes:\n contract.priority === \"critical\"\n ? [\"critical-priority-bypasses-ducking\"]\n : [],\n };\n}\n\nexport const packageDescriptor: AiPackageDescriptor = Object.freeze({\n packageName: AI_SPEECH_PACKAGE,\n featureFlagId: AI_SPEECH_FEATURE_FLAG_ID,\n envPrefix: AI_SPEECH_ENV_PREFIX,\n summary:\n \"Speech orchestration, TTS cache contracts, STT/TTS routing, and voice tier policy for Plasius AI.\",\n});\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -145,7 +145,7 @@ declare function createAiSpeechCacheKey(input: AiSpeechCacheKeyInput): string;
|
|
|
145
145
|
declare function createAiSpeechNearReuseFingerprint(value: string): string;
|
|
146
146
|
declare function isAiSpeechNearReuseSafeClass(utteranceClass: AiSpeechUtteranceClass): boolean;
|
|
147
147
|
declare function planAiSpeechCache(input: PlanAiSpeechCacheInput): AiSpeechCachePlan;
|
|
148
|
-
declare const AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID: "
|
|
148
|
+
declare const AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID: "harmony.player-system.audio.enabled";
|
|
149
149
|
declare const AI_SPEECH_AUDIO_CHANNELS: readonly ["narrated-response", "localized-cue", "repeating-warning"];
|
|
150
150
|
type AiSpeechAudioChannel = (typeof AI_SPEECH_AUDIO_CHANNELS)[number];
|
|
151
151
|
declare const AI_SPEECH_AUDIO_PRIORITIES: readonly ["critical", "high", "normal", "low"];
|
package/dist/index.d.ts
CHANGED
|
@@ -145,7 +145,7 @@ declare function createAiSpeechCacheKey(input: AiSpeechCacheKeyInput): string;
|
|
|
145
145
|
declare function createAiSpeechNearReuseFingerprint(value: string): string;
|
|
146
146
|
declare function isAiSpeechNearReuseSafeClass(utteranceClass: AiSpeechUtteranceClass): boolean;
|
|
147
147
|
declare function planAiSpeechCache(input: PlanAiSpeechCacheInput): AiSpeechCachePlan;
|
|
148
|
-
declare const AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID: "
|
|
148
|
+
declare const AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID: "harmony.player-system.audio.enabled";
|
|
149
149
|
declare const AI_SPEECH_AUDIO_CHANNELS: readonly ["narrated-response", "localized-cue", "repeating-warning"];
|
|
150
150
|
type AiSpeechAudioChannel = (typeof AI_SPEECH_AUDIO_CHANNELS)[number];
|
|
151
151
|
declare const AI_SPEECH_AUDIO_PRIORITIES: readonly ["critical", "high", "normal", "low"];
|
package/dist/index.js
CHANGED
|
@@ -327,7 +327,7 @@ function planAiSpeechCache(input) {
|
|
|
327
327
|
reasonCodes: ["near-reuse-eligible"]
|
|
328
328
|
};
|
|
329
329
|
}
|
|
330
|
-
var AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID = "
|
|
330
|
+
var AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID = "harmony.player-system.audio.enabled";
|
|
331
331
|
var AI_SPEECH_AUDIO_CHANNELS = [
|
|
332
332
|
"narrated-response",
|
|
333
333
|
"localized-cue",
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export interface AiPackageDescriptor {\n readonly packageName: string;\n readonly featureFlagId: string;\n readonly envPrefix: string;\n readonly summary: string;\n}\n\nfunction requireNonEmptyString(value: string, label: string): string {\n const trimmed = value.trim();\n if (trimmed.length === 0) {\n throw new Error(`${label} must be a non-empty string.`);\n }\n\n return trimmed;\n}\n\nfunction normalizeWhitespace(value: string): string {\n return value.replace(/\\s+/gu, \" \").trim();\n}\n\nexport const AI_SPEECH_PACKAGE = \"@plasius/ai-speech\";\nexport const AI_SPEECH_ENV_PREFIX = \"AI_SPEECH\";\n\nexport const AI_SPEECH_FEATURE_FLAGS = {\n ttsCache: \"ai.tts.cache.enabled\",\n ttsNearReuse: \"ai.tts.near-reuse.enabled\",\n premiumCharacters: \"ai.tts.premium-characters.enabled\",\n} as const;\n\nexport type AiSpeechFeatureFlagKey =\n (typeof AI_SPEECH_FEATURE_FLAGS)[keyof typeof AI_SPEECH_FEATURE_FLAGS];\n\nexport type AiSpeechFeatureFlagSnapshot = Readonly<\n Record<string, boolean | undefined>\n>;\n\nexport const AI_SPEECH_FEATURE_FLAG_ID = AI_SPEECH_FEATURE_FLAGS.ttsCache;\n\nexport const AI_SPEECH_ROLLOUT_EVALUATORS = [\n \"remote-flag-service\",\n \"host-application\",\n \"break-glass-env\",\n] as const;\n\nexport type AiSpeechRolloutEvaluator =\n (typeof AI_SPEECH_ROLLOUT_EVALUATORS)[number];\n\nexport const AI_SPEECH_ROLLOUT_FALLBACK_MODES = [\n \"fail-closed\",\n \"fail-open\",\n] as const;\n\nexport type AiSpeechRolloutFallbackMode =\n (typeof AI_SPEECH_ROLLOUT_FALLBACK_MODES)[number];\n\nexport interface AiSpeechRolloutControl {\n readonly featureFlag: AiSpeechFeatureFlagKey;\n readonly evaluator: AiSpeechRolloutEvaluator;\n readonly defaultEnabled: boolean;\n readonly fallbackMode: AiSpeechRolloutFallbackMode;\n}\n\nexport interface AiSpeechRolloutDecision extends AiSpeechRolloutControl {\n readonly enabled: boolean;\n readonly source: \"snapshot\" | \"default\";\n}\n\nexport const AI_SPEECH_ROLLOUTS = Object.freeze({\n ttsCache: Object.freeze<AiSpeechRolloutControl>({\n featureFlag: AI_SPEECH_FEATURE_FLAGS.ttsCache,\n evaluator: \"remote-flag-service\",\n defaultEnabled: false,\n fallbackMode: \"fail-closed\",\n }),\n ttsNearReuse: Object.freeze<AiSpeechRolloutControl>({\n featureFlag: AI_SPEECH_FEATURE_FLAGS.ttsNearReuse,\n evaluator: \"remote-flag-service\",\n defaultEnabled: false,\n fallbackMode: \"fail-closed\",\n }),\n premiumCharacters: Object.freeze<AiSpeechRolloutControl>({\n featureFlag: AI_SPEECH_FEATURE_FLAGS.premiumCharacters,\n evaluator: \"remote-flag-service\",\n defaultEnabled: false,\n fallbackMode: \"fail-closed\",\n }),\n});\n\nexport function resolveAiSpeechRolloutDecision(\n control: AiSpeechRolloutControl,\n snapshot: AiSpeechFeatureFlagSnapshot = {}\n): AiSpeechRolloutDecision {\n const resolved = snapshot[control.featureFlag];\n if (typeof resolved === \"boolean\") {\n return {\n ...control,\n enabled: resolved,\n source: \"snapshot\",\n };\n }\n\n return {\n ...control,\n enabled: control.defaultEnabled,\n source: \"default\",\n };\n}\n\nexport function isAiSpeechFeatureEnabled(\n featureFlag: AiSpeechFeatureFlagKey,\n snapshot: AiSpeechFeatureFlagSnapshot = {}\n): boolean {\n const control = Object.values(AI_SPEECH_ROLLOUTS).find(\n (candidate) => candidate.featureFlag === featureFlag\n );\n\n if (!control) {\n return false;\n }\n\n return resolveAiSpeechRolloutDecision(control, snapshot).enabled;\n}\n\nexport const AI_SPEECH_VOICE_TIERS = [\n \"development\",\n \"standard\",\n \"premium-character\",\n] as const;\n\nexport type AiSpeechVoiceTier = (typeof AI_SPEECH_VOICE_TIERS)[number];\n\nexport const AI_SPEECH_ENVIRONMENTS = [\n \"development\",\n \"preview\",\n \"production\",\n] as const;\n\nexport type AiSpeechEnvironment = (typeof AI_SPEECH_ENVIRONMENTS)[number];\n\nexport interface AiSpeechVoiceTierDecision {\n readonly requestedTier: AiSpeechVoiceTier;\n readonly resolvedTier: AiSpeechVoiceTier;\n readonly fallbackTier?: AiSpeechVoiceTier;\n readonly reasonCodes: readonly string[];\n}\n\nexport interface ResolveAiSpeechVoiceTierInput {\n readonly environment: AiSpeechEnvironment;\n readonly requestedTier?: AiSpeechVoiceTier;\n readonly featureFlags?: AiSpeechFeatureFlagSnapshot;\n}\n\nexport function resolveAiSpeechVoiceTier(\n input: ResolveAiSpeechVoiceTierInput\n): AiSpeechVoiceTierDecision {\n const requestedTier =\n input.requestedTier ??\n (input.environment === \"development\" ? \"development\" : \"standard\");\n\n if (\n requestedTier === \"premium-character\" &&\n !isAiSpeechFeatureEnabled(\n AI_SPEECH_FEATURE_FLAGS.premiumCharacters,\n input.featureFlags\n )\n ) {\n return {\n requestedTier,\n resolvedTier: \"standard\",\n fallbackTier: \"standard\",\n reasonCodes: [\"premium-character-rollout-disabled\"],\n };\n }\n\n if (\n requestedTier === \"development\" &&\n input.environment !== \"development\"\n ) {\n return {\n requestedTier,\n resolvedTier: \"standard\",\n fallbackTier: \"standard\",\n reasonCodes: [\"development-voices-limited-to-development-environments\"],\n };\n }\n\n return {\n requestedTier,\n resolvedTier: requestedTier,\n reasonCodes: [],\n };\n}\n\nexport const AI_SPEECH_PLAYER_ADDRESS_SOURCES = [\n \"generic-player\",\n \"class-title\",\n \"faction-title\",\n \"authored-character\",\n \"user-name\",\n \"account-handle\",\n \"user-renamed-character\",\n] as const;\n\nexport type AiSpeechPlayerAddressSource =\n (typeof AI_SPEECH_PLAYER_ADDRESS_SOURCES)[number];\n\nexport const AI_SPEECH_DEFAULT_PLAYER_LABEL = \"Player\";\n\nexport interface AiSpeechPlayerAddressInput {\n readonly source: AiSpeechPlayerAddressSource;\n readonly rawValue?: string;\n readonly fallbackLabel?: string;\n}\n\nexport interface AiSpeechPlayerAddressDecision {\n readonly source: AiSpeechPlayerAddressSource;\n readonly renderText: string;\n readonly exactReuseAllowed: boolean;\n readonly nearReuseAllowed: boolean;\n readonly redacted: boolean;\n readonly reasonCodes: readonly string[];\n}\n\nexport function resolveAiSpeechPlayerAddress(\n input: AiSpeechPlayerAddressInput\n): AiSpeechPlayerAddressDecision {\n const fallbackLabel =\n normalizeWhitespace(input.fallbackLabel ?? \"\") ||\n AI_SPEECH_DEFAULT_PLAYER_LABEL;\n const rawValue = normalizeWhitespace(input.rawValue ?? \"\");\n\n switch (input.source) {\n case \"generic-player\":\n return {\n source: input.source,\n renderText: fallbackLabel,\n exactReuseAllowed: true,\n nearReuseAllowed: true,\n redacted: false,\n reasonCodes: [\"generic-player-address\"],\n };\n case \"class-title\":\n case \"faction-title\":\n return {\n source: input.source,\n renderText: rawValue || fallbackLabel,\n exactReuseAllowed: true,\n nearReuseAllowed: true,\n redacted: false,\n reasonCodes: rawValue ? [] : [\"player-address-fell-back-to-generic-label\"],\n };\n case \"authored-character\":\n return {\n source: input.source,\n renderText: rawValue || fallbackLabel,\n exactReuseAllowed: true,\n nearReuseAllowed: true,\n redacted: false,\n reasonCodes: rawValue ? [] : [\"player-address-fell-back-to-generic-label\"],\n };\n case \"user-name\":\n case \"account-handle\":\n case \"user-renamed-character\":\n return {\n source: input.source,\n renderText: fallbackLabel,\n exactReuseAllowed: true,\n nearReuseAllowed: false,\n redacted: true,\n reasonCodes: [\"player-identifier-redacted-from-render-text\"],\n };\n }\n}\n\nexport interface AiSpeechRenderTextInput {\n readonly textTemplate: string;\n readonly playerAddress?: AiSpeechPlayerAddressInput;\n}\n\nexport interface AiSpeechRenderTextResult {\n readonly renderText: string;\n readonly normalizedRenderText: string;\n readonly playerAddress?: AiSpeechPlayerAddressDecision;\n}\n\nexport function renderAiSpeechText(\n input: AiSpeechRenderTextInput\n): AiSpeechRenderTextResult {\n const textTemplate = requireNonEmptyString(\n input.textTemplate,\n \"Speech text template\"\n );\n const hasPlayerPlaceholder = textTemplate.includes(\"{playerAddress}\");\n\n if (hasPlayerPlaceholder && !input.playerAddress) {\n throw new Error(\n \"Speech text template requires playerAddress when using the {playerAddress} placeholder.\"\n );\n }\n\n const playerAddress = input.playerAddress\n ? resolveAiSpeechPlayerAddress(input.playerAddress)\n : undefined;\n const renderText = normalizeWhitespace(\n hasPlayerPlaceholder && playerAddress\n ? textTemplate.replace(/\\{playerAddress\\}/gu, playerAddress.renderText)\n : textTemplate\n );\n\n return {\n renderText,\n normalizedRenderText: normalizeAiSpeechText(renderText),\n playerAddress,\n };\n}\n\nexport function normalizeAiSpeechText(value: string): string {\n return requireNonEmptyString(\n normalizeWhitespace(value).toLocaleLowerCase(\"en-GB\"),\n \"Speech text\"\n );\n}\n\nexport const AI_SPEECH_PROVIDER_CACHE_PERMISSIONS = [\n \"forbidden\",\n \"exact-only\",\n \"exact-and-near\",\n] as const;\n\nexport type AiSpeechProviderCachePermission =\n (typeof AI_SPEECH_PROVIDER_CACHE_PERMISSIONS)[number];\n\nexport const AI_SPEECH_CACHE_MODES = [\n \"disabled\",\n \"no-cache\",\n \"exact\",\n \"near\",\n] as const;\n\nexport type AiSpeechCacheMode = (typeof AI_SPEECH_CACHE_MODES)[number];\n\nexport const AI_SPEECH_CACHE_SCOPES = [\"none\", \"actor\", \"global\"] as const;\n\nexport type AiSpeechCacheScope = (typeof AI_SPEECH_CACHE_SCOPES)[number];\n\nexport const AI_SPEECH_UTTERANCE_CLASSES = [\n \"system-generic\",\n \"game-npc-dialogue\",\n \"game-bark\",\n \"player-address\",\n \"moderation-notice\",\n \"private-response\",\n] as const;\n\nexport type AiSpeechUtteranceClass =\n (typeof AI_SPEECH_UTTERANCE_CLASSES)[number];\n\nexport const AI_SPEECH_NEAR_REUSE_SAFE_CLASSES = [\n \"system-generic\",\n \"game-npc-dialogue\",\n \"game-bark\",\n \"player-address\",\n] as const satisfies readonly AiSpeechUtteranceClass[];\n\nconst AI_SPEECH_NEAR_REUSE_SAFE_CLASS_SET: ReadonlySet<AiSpeechUtteranceClass> =\n new Set(AI_SPEECH_NEAR_REUSE_SAFE_CLASSES);\n\nexport interface AiSpeechCacheKeyInput {\n readonly providerId: string;\n readonly modelId: string;\n readonly voiceId: string;\n readonly locale: string;\n readonly format: string;\n readonly pronunciationVersion: string;\n readonly normalizedRenderText: string;\n readonly styleId?: string;\n readonly scopeDiscriminator?: string;\n}\n\nexport interface AiSpeechVoiceProfileRef {\n readonly providerId: string;\n readonly modelId: string;\n readonly voiceId: string;\n readonly locale: string;\n readonly format: string;\n readonly pronunciationVersion: string;\n readonly styleId?: string;\n readonly cachePermission: AiSpeechProviderCachePermission;\n readonly tier?: AiSpeechVoiceTier;\n readonly profileId?: string;\n}\n\nexport interface AiSpeechCacheTelemetry {\n readonly cacheHits: number;\n readonly nearCacheHits: number;\n readonly cacheMisses: number;\n readonly charactersSaved: number;\n readonly estimatedCostSavedUsd?: number;\n readonly voiceProfileIds?: readonly string[];\n}\n\nexport interface PlanAiSpeechCacheInput {\n readonly utteranceClass: AiSpeechUtteranceClass;\n readonly textTemplate: string;\n readonly playerAddress?: AiSpeechPlayerAddressInput;\n readonly voice: AiSpeechVoiceProfileRef;\n readonly featureFlags?: AiSpeechFeatureFlagSnapshot;\n readonly actorScopeKey?: string;\n readonly containsPersonalData?: boolean;\n readonly containsPrivateContext?: boolean;\n readonly containsModerationNotice?: boolean;\n}\n\nexport interface AiSpeechCachePlan {\n readonly mode: AiSpeechCacheMode;\n readonly sharingScope: AiSpeechCacheScope;\n readonly renderText: string;\n readonly normalizedRenderText: string;\n readonly exactKey?: string;\n readonly nearKey?: string;\n readonly enabledFeatureFlags: readonly AiSpeechFeatureFlagKey[];\n readonly playerAddress?: AiSpeechPlayerAddressDecision;\n readonly reasonCodes: readonly string[];\n}\n\nexport function createAiSpeechCacheKey(input: AiSpeechCacheKeyInput): string {\n const scopeDiscriminator = normalizeWhitespace(input.scopeDiscriminator ?? \"\");\n return [\n requireNonEmptyString(input.providerId, \"Provider id\"),\n requireNonEmptyString(input.modelId, \"Model id\"),\n requireNonEmptyString(input.voiceId, \"Voice id\"),\n requireNonEmptyString(input.locale, \"Locale\"),\n normalizeWhitespace(input.styleId ?? \"\") || \"_\",\n requireNonEmptyString(input.format, \"Format\"),\n requireNonEmptyString(\n input.pronunciationVersion,\n \"Pronunciation version\"\n ),\n scopeDiscriminator || \"global\",\n normalizeAiSpeechText(input.normalizedRenderText),\n ]\n .map((segment) => encodeURIComponent(segment))\n .join(\"::\");\n}\n\nexport function createAiSpeechNearReuseFingerprint(value: string): string {\n const normalized = normalizeAiSpeechText(value)\n .replace(/[^a-z0-9\\s]/gu, \" \")\n .replace(/\\s+/gu, \" \")\n .trim();\n\n return requireNonEmptyString(normalized, \"Near-reuse fingerprint\");\n}\n\nexport function isAiSpeechNearReuseSafeClass(\n utteranceClass: AiSpeechUtteranceClass\n): boolean {\n return AI_SPEECH_NEAR_REUSE_SAFE_CLASS_SET.has(utteranceClass);\n}\n\nexport function planAiSpeechCache(\n input: PlanAiSpeechCacheInput\n): AiSpeechCachePlan {\n const rendered = renderAiSpeechText({\n textTemplate: input.textTemplate,\n playerAddress: input.playerAddress,\n });\n const enabledFeatureFlags: AiSpeechFeatureFlagKey[] = [];\n const cacheEnabled = isAiSpeechFeatureEnabled(\n AI_SPEECH_FEATURE_FLAGS.ttsCache,\n input.featureFlags\n );\n\n if (!cacheEnabled) {\n return {\n mode: \"disabled\",\n sharingScope: \"none\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: [\"tts-cache-rollout-disabled\"],\n };\n }\n\n enabledFeatureFlags.push(AI_SPEECH_FEATURE_FLAGS.ttsCache);\n\n if (input.voice.cachePermission === \"forbidden\") {\n return {\n mode: \"no-cache\",\n sharingScope: \"none\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: [\"provider-forbids-tts-caching\"],\n };\n }\n\n const containsSensitiveContent =\n Boolean(input.containsPersonalData) ||\n Boolean(input.containsPrivateContext) ||\n Boolean(input.containsModerationNotice) ||\n Boolean(rendered.playerAddress?.redacted);\n const actorScopeKey = normalizeWhitespace(input.actorScopeKey ?? \"\");\n\n const actorScopeRequired =\n containsSensitiveContent || input.utteranceClass === \"private-response\";\n\n if (actorScopeRequired && !actorScopeKey) {\n return {\n mode: \"no-cache\",\n sharingScope: \"none\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: [\"actor-scope-key-required-for-sensitive-cache-entry\"],\n };\n }\n\n const exactKey = createAiSpeechCacheKey({\n ...input.voice,\n normalizedRenderText: rendered.normalizedRenderText,\n scopeDiscriminator: actorScopeRequired ? actorScopeKey : undefined,\n });\n\n const nearReuseEnabled =\n input.voice.cachePermission === \"exact-and-near\" &&\n isAiSpeechFeatureEnabled(\n AI_SPEECH_FEATURE_FLAGS.ttsNearReuse,\n input.featureFlags\n ) &&\n isAiSpeechNearReuseSafeClass(input.utteranceClass) &&\n !containsSensitiveContent &&\n (rendered.playerAddress?.nearReuseAllowed ?? true);\n\n if (!nearReuseEnabled) {\n return {\n mode: \"exact\",\n sharingScope: actorScopeRequired ? \"actor\" : \"global\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n exactKey,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: containsSensitiveContent\n ? [\"exact-cache-only-for-sensitive-or-redacted-content\"]\n : [\"near-reuse-disabled-or-ineligible\"],\n };\n }\n\n enabledFeatureFlags.push(AI_SPEECH_FEATURE_FLAGS.ttsNearReuse);\n\n const nearKey = createAiSpeechCacheKey({\n ...input.voice,\n normalizedRenderText: createAiSpeechNearReuseFingerprint(\n rendered.normalizedRenderText\n ),\n });\n\n return {\n mode: \"near\",\n sharingScope: \"global\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n exactKey,\n nearKey,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: [\"near-reuse-eligible\"],\n };\n}\n\nexport const AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID =\n \"isekai.player-system.audio.enabled\" as const;\n\nexport const AI_SPEECH_AUDIO_CHANNELS = [\n \"narrated-response\",\n \"localized-cue\",\n \"repeating-warning\",\n] as const;\n\nexport type AiSpeechAudioChannel = (typeof AI_SPEECH_AUDIO_CHANNELS)[number];\n\nexport const AI_SPEECH_AUDIO_PRIORITIES = [\n \"critical\",\n \"high\",\n \"normal\",\n \"low\",\n] as const;\n\nexport type AiSpeechAudioPriority =\n (typeof AI_SPEECH_AUDIO_PRIORITIES)[number];\n\nexport const AI_SPEECH_AUDIO_DUCKING_MODES = [\n \"none\",\n \"music\",\n \"non-critical\",\n \"lower-priority\",\n] as const;\n\nexport type AiSpeechAudioDuckingMode =\n (typeof AI_SPEECH_AUDIO_DUCKING_MODES)[number];\n\nexport const AI_SPEECH_AUDIO_CUE_FAMILIES = [\n \"status\",\n \"tutorial\",\n \"mission\",\n \"mcc\",\n \"warning\",\n] as const;\n\nexport type AiSpeechAudioCueFamily =\n (typeof AI_SPEECH_AUDIO_CUE_FAMILIES)[number];\n\nexport const AI_SPEECH_AUDIO_FOCUS_MODES = [\n \"ambient\",\n \"focused\",\n \"combat-safe\",\n] as const;\n\nexport type AiSpeechAudioFocusMode =\n (typeof AI_SPEECH_AUDIO_FOCUS_MODES)[number];\n\nexport const AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES = [\n \"full\",\n \"condensed\",\n \"deferred\",\n \"suppressed\",\n] as const;\n\nexport type AiSpeechAudioCombatSafeDelivery =\n (typeof AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES)[number];\n\nexport interface AiSpeechAudioContractBase {\n readonly id: string;\n readonly featureFlagId: typeof AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID;\n readonly priority: AiSpeechAudioPriority;\n readonly ducking: AiSpeechAudioDuckingMode;\n readonly combatSafeDelivery: AiSpeechAudioCombatSafeDelivery;\n}\n\nexport interface AiSpeechNarratedResponseContract\n extends AiSpeechAudioContractBase {\n readonly channel: \"narrated-response\";\n readonly utteranceId: string;\n readonly locale: string;\n}\n\nexport interface AiSpeechLocalizedCueContract extends AiSpeechAudioContractBase {\n readonly channel: \"localized-cue\";\n readonly cueFamily: AiSpeechAudioCueFamily;\n readonly cueId: string;\n readonly locale: string;\n}\n\nexport interface AiSpeechRepeatingWarningContract\n extends AiSpeechAudioContractBase {\n readonly channel: \"repeating-warning\";\n readonly warningId: string;\n readonly intervalSeconds: number;\n readonly maximumOccurrencesPerMinute: number;\n}\n\nexport type AiSpeechAudioContract =\n | AiSpeechNarratedResponseContract\n | AiSpeechLocalizedCueContract\n | AiSpeechRepeatingWarningContract;\n\nexport interface AiSpeechAudioContractBaseInput {\n readonly id: string;\n readonly priority: AiSpeechAudioPriority;\n readonly ducking: AiSpeechAudioDuckingMode;\n readonly combatSafeDelivery: AiSpeechAudioCombatSafeDelivery;\n}\n\nexport interface CreateAiSpeechNarratedResponseInput\n extends AiSpeechAudioContractBaseInput {\n readonly utteranceId: string;\n readonly locale: string;\n}\n\nexport interface CreateAiSpeechLocalizedCueInput\n extends AiSpeechAudioContractBaseInput {\n readonly cueFamily: AiSpeechAudioCueFamily;\n readonly cueId: string;\n readonly locale: string;\n}\n\nexport interface CreateAiSpeechRepeatingWarningInput\n extends AiSpeechAudioContractBaseInput {\n readonly warningId: string;\n readonly intervalSeconds: number;\n readonly maximumOccurrencesPerMinute: number;\n}\n\nfunction requireAudioEnum<T extends string>(\n value: T,\n allowed: readonly T[],\n label: string\n): T {\n if (!allowed.includes(value)) {\n throw new Error(`${label} is not supported: ${value}.`);\n }\n\n return value;\n}\n\nfunction requireBoundedAudioNumber(\n value: number,\n minimum: number,\n maximum: number,\n label: string\n): number {\n if (!Number.isFinite(value) || value < minimum || value > maximum) {\n throw new Error(\n `${label} must be between ${minimum} and ${maximum}, inclusive.`\n );\n }\n\n return value;\n}\n\nfunction createAiSpeechAudioContractBase(\n input: AiSpeechAudioContractBaseInput\n): AiSpeechAudioContractBase {\n return {\n id: requireNonEmptyString(input.id, \"Audio contract id\"),\n featureFlagId: AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID,\n priority: requireAudioEnum(\n input.priority,\n AI_SPEECH_AUDIO_PRIORITIES,\n \"Audio priority\"\n ),\n ducking: requireAudioEnum(\n input.ducking,\n AI_SPEECH_AUDIO_DUCKING_MODES,\n \"Audio ducking mode\"\n ),\n combatSafeDelivery: requireAudioEnum(\n input.combatSafeDelivery,\n AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES,\n \"Combat-safe audio delivery\"\n ),\n };\n}\n\nexport function createAiSpeechNarratedResponse(\n input: CreateAiSpeechNarratedResponseInput\n): AiSpeechNarratedResponseContract {\n return Object.freeze({\n ...createAiSpeechAudioContractBase(input),\n channel: \"narrated-response\" as const,\n utteranceId: requireNonEmptyString(input.utteranceId, \"Utterance id\"),\n locale: requireNonEmptyString(input.locale, \"Locale\"),\n });\n}\n\nexport function createAiSpeechLocalizedCue(\n input: CreateAiSpeechLocalizedCueInput\n): AiSpeechLocalizedCueContract {\n return Object.freeze({\n ...createAiSpeechAudioContractBase(input),\n channel: \"localized-cue\" as const,\n cueFamily: requireAudioEnum(\n input.cueFamily,\n AI_SPEECH_AUDIO_CUE_FAMILIES,\n \"Audio cue family\"\n ),\n cueId: requireNonEmptyString(input.cueId, \"Cue id\"),\n locale: requireNonEmptyString(input.locale, \"Locale\"),\n });\n}\n\nexport function createAiSpeechRepeatingWarning(\n input: CreateAiSpeechRepeatingWarningInput\n): AiSpeechRepeatingWarningContract {\n return Object.freeze({\n ...createAiSpeechAudioContractBase(input),\n channel: \"repeating-warning\" as const,\n warningId: requireNonEmptyString(input.warningId, \"Warning id\"),\n intervalSeconds: requireBoundedAudioNumber(\n input.intervalSeconds,\n 1,\n 3600,\n \"Warning intervalSeconds\"\n ),\n maximumOccurrencesPerMinute: requireBoundedAudioNumber(\n input.maximumOccurrencesPerMinute,\n 1,\n 60,\n \"Warning maximumOccurrencesPerMinute\"\n ),\n });\n}\n\nexport interface ResolveAiSpeechAudioPolicyInput {\n readonly contract: AiSpeechAudioContract;\n readonly focusMode: AiSpeechAudioFocusMode;\n readonly featureFlags?: AiSpeechFeatureFlagSnapshot;\n readonly masterMuted?: boolean;\n readonly userMuted?: boolean;\n readonly activeContractIds?: readonly string[];\n}\n\nexport interface AiSpeechAudioPolicyDecision {\n readonly deliver: boolean;\n readonly mode: \"full\" | \"condensed\" | \"deferred\";\n readonly ducking: AiSpeechAudioDuckingMode;\n readonly reasonCodes: readonly string[];\n}\n\nfunction isPlayerSystemAudioEnabled(\n featureFlags: AiSpeechFeatureFlagSnapshot = {}\n): boolean {\n return featureFlags[AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID] === true;\n}\n\nexport function resolveAiSpeechAudioPolicy(\n input: ResolveAiSpeechAudioPolicyInput\n): AiSpeechAudioPolicyDecision {\n const { contract } = input;\n\n if (!isPlayerSystemAudioEnabled(input.featureFlags)) {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [\"player-system-audio-rollout-disabled\"],\n };\n }\n\n if (input.masterMuted || input.userMuted) {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [input.masterMuted ? \"master-muted\" : \"user-muted\"],\n };\n }\n\n if (input.activeContractIds?.includes(contract.id)) {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [\"duplicate-audio-contract-suppressed\"],\n };\n }\n\n if (\n input.focusMode === \"combat-safe\" &&\n contract.priority !== \"critical\" &&\n contract.priority !== \"high\"\n ) {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [\"combat-safe-priority-suppressed\"],\n };\n }\n\n if (contract.combatSafeDelivery === \"suppressed\") {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [\"audio-contract-suppressed\"],\n };\n }\n\n const mode =\n input.focusMode === \"combat-safe\"\n ? contract.combatSafeDelivery === \"condensed\"\n ? \"condensed\"\n : contract.combatSafeDelivery === \"deferred\"\n ? \"deferred\"\n : \"full\"\n : \"full\";\n const ducking = contract.priority === \"critical\" ? \"none\" : contract.ducking;\n\n return {\n deliver: mode !== \"deferred\",\n mode,\n ducking,\n reasonCodes:\n contract.priority === \"critical\"\n ? [\"critical-priority-bypasses-ducking\"]\n : [],\n };\n}\n\nexport const packageDescriptor: AiPackageDescriptor = Object.freeze({\n packageName: AI_SPEECH_PACKAGE,\n featureFlagId: AI_SPEECH_FEATURE_FLAG_ID,\n envPrefix: AI_SPEECH_ENV_PREFIX,\n summary:\n \"Speech orchestration, TTS cache contracts, STT/TTS routing, and voice tier policy for Plasius AI.\",\n});\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export interface AiPackageDescriptor {\n readonly packageName: string;\n readonly featureFlagId: string;\n readonly envPrefix: string;\n readonly summary: string;\n}\n\nfunction requireNonEmptyString(value: string, label: string): string {\n const trimmed = value.trim();\n if (trimmed.length === 0) {\n throw new Error(`${label} must be a non-empty string.`);\n }\n\n return trimmed;\n}\n\nfunction normalizeWhitespace(value: string): string {\n return value.replace(/\\s+/gu, \" \").trim();\n}\n\nexport const AI_SPEECH_PACKAGE = \"@plasius/ai-speech\";\nexport const AI_SPEECH_ENV_PREFIX = \"AI_SPEECH\";\n\nexport const AI_SPEECH_FEATURE_FLAGS = {\n ttsCache: \"ai.tts.cache.enabled\",\n ttsNearReuse: \"ai.tts.near-reuse.enabled\",\n premiumCharacters: \"ai.tts.premium-characters.enabled\",\n} as const;\n\nexport type AiSpeechFeatureFlagKey =\n (typeof AI_SPEECH_FEATURE_FLAGS)[keyof typeof AI_SPEECH_FEATURE_FLAGS];\n\nexport type AiSpeechFeatureFlagSnapshot = Readonly<\n Record<string, boolean | undefined>\n>;\n\nexport const AI_SPEECH_FEATURE_FLAG_ID = AI_SPEECH_FEATURE_FLAGS.ttsCache;\n\nexport const AI_SPEECH_ROLLOUT_EVALUATORS = [\n \"remote-flag-service\",\n \"host-application\",\n \"break-glass-env\",\n] as const;\n\nexport type AiSpeechRolloutEvaluator =\n (typeof AI_SPEECH_ROLLOUT_EVALUATORS)[number];\n\nexport const AI_SPEECH_ROLLOUT_FALLBACK_MODES = [\n \"fail-closed\",\n \"fail-open\",\n] as const;\n\nexport type AiSpeechRolloutFallbackMode =\n (typeof AI_SPEECH_ROLLOUT_FALLBACK_MODES)[number];\n\nexport interface AiSpeechRolloutControl {\n readonly featureFlag: AiSpeechFeatureFlagKey;\n readonly evaluator: AiSpeechRolloutEvaluator;\n readonly defaultEnabled: boolean;\n readonly fallbackMode: AiSpeechRolloutFallbackMode;\n}\n\nexport interface AiSpeechRolloutDecision extends AiSpeechRolloutControl {\n readonly enabled: boolean;\n readonly source: \"snapshot\" | \"default\";\n}\n\nexport const AI_SPEECH_ROLLOUTS = Object.freeze({\n ttsCache: Object.freeze<AiSpeechRolloutControl>({\n featureFlag: AI_SPEECH_FEATURE_FLAGS.ttsCache,\n evaluator: \"remote-flag-service\",\n defaultEnabled: false,\n fallbackMode: \"fail-closed\",\n }),\n ttsNearReuse: Object.freeze<AiSpeechRolloutControl>({\n featureFlag: AI_SPEECH_FEATURE_FLAGS.ttsNearReuse,\n evaluator: \"remote-flag-service\",\n defaultEnabled: false,\n fallbackMode: \"fail-closed\",\n }),\n premiumCharacters: Object.freeze<AiSpeechRolloutControl>({\n featureFlag: AI_SPEECH_FEATURE_FLAGS.premiumCharacters,\n evaluator: \"remote-flag-service\",\n defaultEnabled: false,\n fallbackMode: \"fail-closed\",\n }),\n});\n\nexport function resolveAiSpeechRolloutDecision(\n control: AiSpeechRolloutControl,\n snapshot: AiSpeechFeatureFlagSnapshot = {}\n): AiSpeechRolloutDecision {\n const resolved = snapshot[control.featureFlag];\n if (typeof resolved === \"boolean\") {\n return {\n ...control,\n enabled: resolved,\n source: \"snapshot\",\n };\n }\n\n return {\n ...control,\n enabled: control.defaultEnabled,\n source: \"default\",\n };\n}\n\nexport function isAiSpeechFeatureEnabled(\n featureFlag: AiSpeechFeatureFlagKey,\n snapshot: AiSpeechFeatureFlagSnapshot = {}\n): boolean {\n const control = Object.values(AI_SPEECH_ROLLOUTS).find(\n (candidate) => candidate.featureFlag === featureFlag\n );\n\n if (!control) {\n return false;\n }\n\n return resolveAiSpeechRolloutDecision(control, snapshot).enabled;\n}\n\nexport const AI_SPEECH_VOICE_TIERS = [\n \"development\",\n \"standard\",\n \"premium-character\",\n] as const;\n\nexport type AiSpeechVoiceTier = (typeof AI_SPEECH_VOICE_TIERS)[number];\n\nexport const AI_SPEECH_ENVIRONMENTS = [\n \"development\",\n \"preview\",\n \"production\",\n] as const;\n\nexport type AiSpeechEnvironment = (typeof AI_SPEECH_ENVIRONMENTS)[number];\n\nexport interface AiSpeechVoiceTierDecision {\n readonly requestedTier: AiSpeechVoiceTier;\n readonly resolvedTier: AiSpeechVoiceTier;\n readonly fallbackTier?: AiSpeechVoiceTier;\n readonly reasonCodes: readonly string[];\n}\n\nexport interface ResolveAiSpeechVoiceTierInput {\n readonly environment: AiSpeechEnvironment;\n readonly requestedTier?: AiSpeechVoiceTier;\n readonly featureFlags?: AiSpeechFeatureFlagSnapshot;\n}\n\nexport function resolveAiSpeechVoiceTier(\n input: ResolveAiSpeechVoiceTierInput\n): AiSpeechVoiceTierDecision {\n const requestedTier =\n input.requestedTier ??\n (input.environment === \"development\" ? \"development\" : \"standard\");\n\n if (\n requestedTier === \"premium-character\" &&\n !isAiSpeechFeatureEnabled(\n AI_SPEECH_FEATURE_FLAGS.premiumCharacters,\n input.featureFlags\n )\n ) {\n return {\n requestedTier,\n resolvedTier: \"standard\",\n fallbackTier: \"standard\",\n reasonCodes: [\"premium-character-rollout-disabled\"],\n };\n }\n\n if (\n requestedTier === \"development\" &&\n input.environment !== \"development\"\n ) {\n return {\n requestedTier,\n resolvedTier: \"standard\",\n fallbackTier: \"standard\",\n reasonCodes: [\"development-voices-limited-to-development-environments\"],\n };\n }\n\n return {\n requestedTier,\n resolvedTier: requestedTier,\n reasonCodes: [],\n };\n}\n\nexport const AI_SPEECH_PLAYER_ADDRESS_SOURCES = [\n \"generic-player\",\n \"class-title\",\n \"faction-title\",\n \"authored-character\",\n \"user-name\",\n \"account-handle\",\n \"user-renamed-character\",\n] as const;\n\nexport type AiSpeechPlayerAddressSource =\n (typeof AI_SPEECH_PLAYER_ADDRESS_SOURCES)[number];\n\nexport const AI_SPEECH_DEFAULT_PLAYER_LABEL = \"Player\";\n\nexport interface AiSpeechPlayerAddressInput {\n readonly source: AiSpeechPlayerAddressSource;\n readonly rawValue?: string;\n readonly fallbackLabel?: string;\n}\n\nexport interface AiSpeechPlayerAddressDecision {\n readonly source: AiSpeechPlayerAddressSource;\n readonly renderText: string;\n readonly exactReuseAllowed: boolean;\n readonly nearReuseAllowed: boolean;\n readonly redacted: boolean;\n readonly reasonCodes: readonly string[];\n}\n\nexport function resolveAiSpeechPlayerAddress(\n input: AiSpeechPlayerAddressInput\n): AiSpeechPlayerAddressDecision {\n const fallbackLabel =\n normalizeWhitespace(input.fallbackLabel ?? \"\") ||\n AI_SPEECH_DEFAULT_PLAYER_LABEL;\n const rawValue = normalizeWhitespace(input.rawValue ?? \"\");\n\n switch (input.source) {\n case \"generic-player\":\n return {\n source: input.source,\n renderText: fallbackLabel,\n exactReuseAllowed: true,\n nearReuseAllowed: true,\n redacted: false,\n reasonCodes: [\"generic-player-address\"],\n };\n case \"class-title\":\n case \"faction-title\":\n return {\n source: input.source,\n renderText: rawValue || fallbackLabel,\n exactReuseAllowed: true,\n nearReuseAllowed: true,\n redacted: false,\n reasonCodes: rawValue ? [] : [\"player-address-fell-back-to-generic-label\"],\n };\n case \"authored-character\":\n return {\n source: input.source,\n renderText: rawValue || fallbackLabel,\n exactReuseAllowed: true,\n nearReuseAllowed: true,\n redacted: false,\n reasonCodes: rawValue ? [] : [\"player-address-fell-back-to-generic-label\"],\n };\n case \"user-name\":\n case \"account-handle\":\n case \"user-renamed-character\":\n return {\n source: input.source,\n renderText: fallbackLabel,\n exactReuseAllowed: true,\n nearReuseAllowed: false,\n redacted: true,\n reasonCodes: [\"player-identifier-redacted-from-render-text\"],\n };\n }\n}\n\nexport interface AiSpeechRenderTextInput {\n readonly textTemplate: string;\n readonly playerAddress?: AiSpeechPlayerAddressInput;\n}\n\nexport interface AiSpeechRenderTextResult {\n readonly renderText: string;\n readonly normalizedRenderText: string;\n readonly playerAddress?: AiSpeechPlayerAddressDecision;\n}\n\nexport function renderAiSpeechText(\n input: AiSpeechRenderTextInput\n): AiSpeechRenderTextResult {\n const textTemplate = requireNonEmptyString(\n input.textTemplate,\n \"Speech text template\"\n );\n const hasPlayerPlaceholder = textTemplate.includes(\"{playerAddress}\");\n\n if (hasPlayerPlaceholder && !input.playerAddress) {\n throw new Error(\n \"Speech text template requires playerAddress when using the {playerAddress} placeholder.\"\n );\n }\n\n const playerAddress = input.playerAddress\n ? resolveAiSpeechPlayerAddress(input.playerAddress)\n : undefined;\n const renderText = normalizeWhitespace(\n hasPlayerPlaceholder && playerAddress\n ? textTemplate.replace(/\\{playerAddress\\}/gu, playerAddress.renderText)\n : textTemplate\n );\n\n return {\n renderText,\n normalizedRenderText: normalizeAiSpeechText(renderText),\n playerAddress,\n };\n}\n\nexport function normalizeAiSpeechText(value: string): string {\n return requireNonEmptyString(\n normalizeWhitespace(value).toLocaleLowerCase(\"en-GB\"),\n \"Speech text\"\n );\n}\n\nexport const AI_SPEECH_PROVIDER_CACHE_PERMISSIONS = [\n \"forbidden\",\n \"exact-only\",\n \"exact-and-near\",\n] as const;\n\nexport type AiSpeechProviderCachePermission =\n (typeof AI_SPEECH_PROVIDER_CACHE_PERMISSIONS)[number];\n\nexport const AI_SPEECH_CACHE_MODES = [\n \"disabled\",\n \"no-cache\",\n \"exact\",\n \"near\",\n] as const;\n\nexport type AiSpeechCacheMode = (typeof AI_SPEECH_CACHE_MODES)[number];\n\nexport const AI_SPEECH_CACHE_SCOPES = [\"none\", \"actor\", \"global\"] as const;\n\nexport type AiSpeechCacheScope = (typeof AI_SPEECH_CACHE_SCOPES)[number];\n\nexport const AI_SPEECH_UTTERANCE_CLASSES = [\n \"system-generic\",\n \"game-npc-dialogue\",\n \"game-bark\",\n \"player-address\",\n \"moderation-notice\",\n \"private-response\",\n] as const;\n\nexport type AiSpeechUtteranceClass =\n (typeof AI_SPEECH_UTTERANCE_CLASSES)[number];\n\nexport const AI_SPEECH_NEAR_REUSE_SAFE_CLASSES = [\n \"system-generic\",\n \"game-npc-dialogue\",\n \"game-bark\",\n \"player-address\",\n] as const satisfies readonly AiSpeechUtteranceClass[];\n\nconst AI_SPEECH_NEAR_REUSE_SAFE_CLASS_SET: ReadonlySet<AiSpeechUtteranceClass> =\n new Set(AI_SPEECH_NEAR_REUSE_SAFE_CLASSES);\n\nexport interface AiSpeechCacheKeyInput {\n readonly providerId: string;\n readonly modelId: string;\n readonly voiceId: string;\n readonly locale: string;\n readonly format: string;\n readonly pronunciationVersion: string;\n readonly normalizedRenderText: string;\n readonly styleId?: string;\n readonly scopeDiscriminator?: string;\n}\n\nexport interface AiSpeechVoiceProfileRef {\n readonly providerId: string;\n readonly modelId: string;\n readonly voiceId: string;\n readonly locale: string;\n readonly format: string;\n readonly pronunciationVersion: string;\n readonly styleId?: string;\n readonly cachePermission: AiSpeechProviderCachePermission;\n readonly tier?: AiSpeechVoiceTier;\n readonly profileId?: string;\n}\n\nexport interface AiSpeechCacheTelemetry {\n readonly cacheHits: number;\n readonly nearCacheHits: number;\n readonly cacheMisses: number;\n readonly charactersSaved: number;\n readonly estimatedCostSavedUsd?: number;\n readonly voiceProfileIds?: readonly string[];\n}\n\nexport interface PlanAiSpeechCacheInput {\n readonly utteranceClass: AiSpeechUtteranceClass;\n readonly textTemplate: string;\n readonly playerAddress?: AiSpeechPlayerAddressInput;\n readonly voice: AiSpeechVoiceProfileRef;\n readonly featureFlags?: AiSpeechFeatureFlagSnapshot;\n readonly actorScopeKey?: string;\n readonly containsPersonalData?: boolean;\n readonly containsPrivateContext?: boolean;\n readonly containsModerationNotice?: boolean;\n}\n\nexport interface AiSpeechCachePlan {\n readonly mode: AiSpeechCacheMode;\n readonly sharingScope: AiSpeechCacheScope;\n readonly renderText: string;\n readonly normalizedRenderText: string;\n readonly exactKey?: string;\n readonly nearKey?: string;\n readonly enabledFeatureFlags: readonly AiSpeechFeatureFlagKey[];\n readonly playerAddress?: AiSpeechPlayerAddressDecision;\n readonly reasonCodes: readonly string[];\n}\n\nexport function createAiSpeechCacheKey(input: AiSpeechCacheKeyInput): string {\n const scopeDiscriminator = normalizeWhitespace(input.scopeDiscriminator ?? \"\");\n return [\n requireNonEmptyString(input.providerId, \"Provider id\"),\n requireNonEmptyString(input.modelId, \"Model id\"),\n requireNonEmptyString(input.voiceId, \"Voice id\"),\n requireNonEmptyString(input.locale, \"Locale\"),\n normalizeWhitespace(input.styleId ?? \"\") || \"_\",\n requireNonEmptyString(input.format, \"Format\"),\n requireNonEmptyString(\n input.pronunciationVersion,\n \"Pronunciation version\"\n ),\n scopeDiscriminator || \"global\",\n normalizeAiSpeechText(input.normalizedRenderText),\n ]\n .map((segment) => encodeURIComponent(segment))\n .join(\"::\");\n}\n\nexport function createAiSpeechNearReuseFingerprint(value: string): string {\n const normalized = normalizeAiSpeechText(value)\n .replace(/[^a-z0-9\\s]/gu, \" \")\n .replace(/\\s+/gu, \" \")\n .trim();\n\n return requireNonEmptyString(normalized, \"Near-reuse fingerprint\");\n}\n\nexport function isAiSpeechNearReuseSafeClass(\n utteranceClass: AiSpeechUtteranceClass\n): boolean {\n return AI_SPEECH_NEAR_REUSE_SAFE_CLASS_SET.has(utteranceClass);\n}\n\nexport function planAiSpeechCache(\n input: PlanAiSpeechCacheInput\n): AiSpeechCachePlan {\n const rendered = renderAiSpeechText({\n textTemplate: input.textTemplate,\n playerAddress: input.playerAddress,\n });\n const enabledFeatureFlags: AiSpeechFeatureFlagKey[] = [];\n const cacheEnabled = isAiSpeechFeatureEnabled(\n AI_SPEECH_FEATURE_FLAGS.ttsCache,\n input.featureFlags\n );\n\n if (!cacheEnabled) {\n return {\n mode: \"disabled\",\n sharingScope: \"none\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: [\"tts-cache-rollout-disabled\"],\n };\n }\n\n enabledFeatureFlags.push(AI_SPEECH_FEATURE_FLAGS.ttsCache);\n\n if (input.voice.cachePermission === \"forbidden\") {\n return {\n mode: \"no-cache\",\n sharingScope: \"none\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: [\"provider-forbids-tts-caching\"],\n };\n }\n\n const containsSensitiveContent =\n Boolean(input.containsPersonalData) ||\n Boolean(input.containsPrivateContext) ||\n Boolean(input.containsModerationNotice) ||\n Boolean(rendered.playerAddress?.redacted);\n const actorScopeKey = normalizeWhitespace(input.actorScopeKey ?? \"\");\n\n const actorScopeRequired =\n containsSensitiveContent || input.utteranceClass === \"private-response\";\n\n if (actorScopeRequired && !actorScopeKey) {\n return {\n mode: \"no-cache\",\n sharingScope: \"none\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: [\"actor-scope-key-required-for-sensitive-cache-entry\"],\n };\n }\n\n const exactKey = createAiSpeechCacheKey({\n ...input.voice,\n normalizedRenderText: rendered.normalizedRenderText,\n scopeDiscriminator: actorScopeRequired ? actorScopeKey : undefined,\n });\n\n const nearReuseEnabled =\n input.voice.cachePermission === \"exact-and-near\" &&\n isAiSpeechFeatureEnabled(\n AI_SPEECH_FEATURE_FLAGS.ttsNearReuse,\n input.featureFlags\n ) &&\n isAiSpeechNearReuseSafeClass(input.utteranceClass) &&\n !containsSensitiveContent &&\n (rendered.playerAddress?.nearReuseAllowed ?? true);\n\n if (!nearReuseEnabled) {\n return {\n mode: \"exact\",\n sharingScope: actorScopeRequired ? \"actor\" : \"global\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n exactKey,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: containsSensitiveContent\n ? [\"exact-cache-only-for-sensitive-or-redacted-content\"]\n : [\"near-reuse-disabled-or-ineligible\"],\n };\n }\n\n enabledFeatureFlags.push(AI_SPEECH_FEATURE_FLAGS.ttsNearReuse);\n\n const nearKey = createAiSpeechCacheKey({\n ...input.voice,\n normalizedRenderText: createAiSpeechNearReuseFingerprint(\n rendered.normalizedRenderText\n ),\n });\n\n return {\n mode: \"near\",\n sharingScope: \"global\",\n renderText: rendered.renderText,\n normalizedRenderText: rendered.normalizedRenderText,\n exactKey,\n nearKey,\n enabledFeatureFlags,\n playerAddress: rendered.playerAddress,\n reasonCodes: [\"near-reuse-eligible\"],\n };\n}\n\nexport const AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID =\n \"harmony.player-system.audio.enabled\" as const;\n\nexport const AI_SPEECH_AUDIO_CHANNELS = [\n \"narrated-response\",\n \"localized-cue\",\n \"repeating-warning\",\n] as const;\n\nexport type AiSpeechAudioChannel = (typeof AI_SPEECH_AUDIO_CHANNELS)[number];\n\nexport const AI_SPEECH_AUDIO_PRIORITIES = [\n \"critical\",\n \"high\",\n \"normal\",\n \"low\",\n] as const;\n\nexport type AiSpeechAudioPriority =\n (typeof AI_SPEECH_AUDIO_PRIORITIES)[number];\n\nexport const AI_SPEECH_AUDIO_DUCKING_MODES = [\n \"none\",\n \"music\",\n \"non-critical\",\n \"lower-priority\",\n] as const;\n\nexport type AiSpeechAudioDuckingMode =\n (typeof AI_SPEECH_AUDIO_DUCKING_MODES)[number];\n\nexport const AI_SPEECH_AUDIO_CUE_FAMILIES = [\n \"status\",\n \"tutorial\",\n \"mission\",\n \"mcc\",\n \"warning\",\n] as const;\n\nexport type AiSpeechAudioCueFamily =\n (typeof AI_SPEECH_AUDIO_CUE_FAMILIES)[number];\n\nexport const AI_SPEECH_AUDIO_FOCUS_MODES = [\n \"ambient\",\n \"focused\",\n \"combat-safe\",\n] as const;\n\nexport type AiSpeechAudioFocusMode =\n (typeof AI_SPEECH_AUDIO_FOCUS_MODES)[number];\n\nexport const AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES = [\n \"full\",\n \"condensed\",\n \"deferred\",\n \"suppressed\",\n] as const;\n\nexport type AiSpeechAudioCombatSafeDelivery =\n (typeof AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES)[number];\n\nexport interface AiSpeechAudioContractBase {\n readonly id: string;\n readonly featureFlagId: typeof AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID;\n readonly priority: AiSpeechAudioPriority;\n readonly ducking: AiSpeechAudioDuckingMode;\n readonly combatSafeDelivery: AiSpeechAudioCombatSafeDelivery;\n}\n\nexport interface AiSpeechNarratedResponseContract\n extends AiSpeechAudioContractBase {\n readonly channel: \"narrated-response\";\n readonly utteranceId: string;\n readonly locale: string;\n}\n\nexport interface AiSpeechLocalizedCueContract extends AiSpeechAudioContractBase {\n readonly channel: \"localized-cue\";\n readonly cueFamily: AiSpeechAudioCueFamily;\n readonly cueId: string;\n readonly locale: string;\n}\n\nexport interface AiSpeechRepeatingWarningContract\n extends AiSpeechAudioContractBase {\n readonly channel: \"repeating-warning\";\n readonly warningId: string;\n readonly intervalSeconds: number;\n readonly maximumOccurrencesPerMinute: number;\n}\n\nexport type AiSpeechAudioContract =\n | AiSpeechNarratedResponseContract\n | AiSpeechLocalizedCueContract\n | AiSpeechRepeatingWarningContract;\n\nexport interface AiSpeechAudioContractBaseInput {\n readonly id: string;\n readonly priority: AiSpeechAudioPriority;\n readonly ducking: AiSpeechAudioDuckingMode;\n readonly combatSafeDelivery: AiSpeechAudioCombatSafeDelivery;\n}\n\nexport interface CreateAiSpeechNarratedResponseInput\n extends AiSpeechAudioContractBaseInput {\n readonly utteranceId: string;\n readonly locale: string;\n}\n\nexport interface CreateAiSpeechLocalizedCueInput\n extends AiSpeechAudioContractBaseInput {\n readonly cueFamily: AiSpeechAudioCueFamily;\n readonly cueId: string;\n readonly locale: string;\n}\n\nexport interface CreateAiSpeechRepeatingWarningInput\n extends AiSpeechAudioContractBaseInput {\n readonly warningId: string;\n readonly intervalSeconds: number;\n readonly maximumOccurrencesPerMinute: number;\n}\n\nfunction requireAudioEnum<T extends string>(\n value: T,\n allowed: readonly T[],\n label: string\n): T {\n if (!allowed.includes(value)) {\n throw new Error(`${label} is not supported: ${value}.`);\n }\n\n return value;\n}\n\nfunction requireBoundedAudioNumber(\n value: number,\n minimum: number,\n maximum: number,\n label: string\n): number {\n if (!Number.isFinite(value) || value < minimum || value > maximum) {\n throw new Error(\n `${label} must be between ${minimum} and ${maximum}, inclusive.`\n );\n }\n\n return value;\n}\n\nfunction createAiSpeechAudioContractBase(\n input: AiSpeechAudioContractBaseInput\n): AiSpeechAudioContractBase {\n return {\n id: requireNonEmptyString(input.id, \"Audio contract id\"),\n featureFlagId: AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID,\n priority: requireAudioEnum(\n input.priority,\n AI_SPEECH_AUDIO_PRIORITIES,\n \"Audio priority\"\n ),\n ducking: requireAudioEnum(\n input.ducking,\n AI_SPEECH_AUDIO_DUCKING_MODES,\n \"Audio ducking mode\"\n ),\n combatSafeDelivery: requireAudioEnum(\n input.combatSafeDelivery,\n AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES,\n \"Combat-safe audio delivery\"\n ),\n };\n}\n\nexport function createAiSpeechNarratedResponse(\n input: CreateAiSpeechNarratedResponseInput\n): AiSpeechNarratedResponseContract {\n return Object.freeze({\n ...createAiSpeechAudioContractBase(input),\n channel: \"narrated-response\" as const,\n utteranceId: requireNonEmptyString(input.utteranceId, \"Utterance id\"),\n locale: requireNonEmptyString(input.locale, \"Locale\"),\n });\n}\n\nexport function createAiSpeechLocalizedCue(\n input: CreateAiSpeechLocalizedCueInput\n): AiSpeechLocalizedCueContract {\n return Object.freeze({\n ...createAiSpeechAudioContractBase(input),\n channel: \"localized-cue\" as const,\n cueFamily: requireAudioEnum(\n input.cueFamily,\n AI_SPEECH_AUDIO_CUE_FAMILIES,\n \"Audio cue family\"\n ),\n cueId: requireNonEmptyString(input.cueId, \"Cue id\"),\n locale: requireNonEmptyString(input.locale, \"Locale\"),\n });\n}\n\nexport function createAiSpeechRepeatingWarning(\n input: CreateAiSpeechRepeatingWarningInput\n): AiSpeechRepeatingWarningContract {\n return Object.freeze({\n ...createAiSpeechAudioContractBase(input),\n channel: \"repeating-warning\" as const,\n warningId: requireNonEmptyString(input.warningId, \"Warning id\"),\n intervalSeconds: requireBoundedAudioNumber(\n input.intervalSeconds,\n 1,\n 3600,\n \"Warning intervalSeconds\"\n ),\n maximumOccurrencesPerMinute: requireBoundedAudioNumber(\n input.maximumOccurrencesPerMinute,\n 1,\n 60,\n \"Warning maximumOccurrencesPerMinute\"\n ),\n });\n}\n\nexport interface ResolveAiSpeechAudioPolicyInput {\n readonly contract: AiSpeechAudioContract;\n readonly focusMode: AiSpeechAudioFocusMode;\n readonly featureFlags?: AiSpeechFeatureFlagSnapshot;\n readonly masterMuted?: boolean;\n readonly userMuted?: boolean;\n readonly activeContractIds?: readonly string[];\n}\n\nexport interface AiSpeechAudioPolicyDecision {\n readonly deliver: boolean;\n readonly mode: \"full\" | \"condensed\" | \"deferred\";\n readonly ducking: AiSpeechAudioDuckingMode;\n readonly reasonCodes: readonly string[];\n}\n\nfunction isPlayerSystemAudioEnabled(\n featureFlags: AiSpeechFeatureFlagSnapshot = {}\n): boolean {\n return featureFlags[AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID] === true;\n}\n\nexport function resolveAiSpeechAudioPolicy(\n input: ResolveAiSpeechAudioPolicyInput\n): AiSpeechAudioPolicyDecision {\n const { contract } = input;\n\n if (!isPlayerSystemAudioEnabled(input.featureFlags)) {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [\"player-system-audio-rollout-disabled\"],\n };\n }\n\n if (input.masterMuted || input.userMuted) {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [input.masterMuted ? \"master-muted\" : \"user-muted\"],\n };\n }\n\n if (input.activeContractIds?.includes(contract.id)) {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [\"duplicate-audio-contract-suppressed\"],\n };\n }\n\n if (\n input.focusMode === \"combat-safe\" &&\n contract.priority !== \"critical\" &&\n contract.priority !== \"high\"\n ) {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [\"combat-safe-priority-suppressed\"],\n };\n }\n\n if (contract.combatSafeDelivery === \"suppressed\") {\n return {\n deliver: false,\n mode: \"deferred\",\n ducking: \"none\",\n reasonCodes: [\"audio-contract-suppressed\"],\n };\n }\n\n const mode =\n input.focusMode === \"combat-safe\"\n ? contract.combatSafeDelivery === \"condensed\"\n ? \"condensed\"\n : contract.combatSafeDelivery === \"deferred\"\n ? \"deferred\"\n : \"full\"\n : \"full\";\n const ducking = contract.priority === \"critical\" ? \"none\" : contract.ducking;\n\n return {\n deliver: mode !== \"deferred\",\n mode,\n ducking,\n reasonCodes:\n contract.priority === \"critical\"\n ? [\"critical-priority-bypasses-ducking\"]\n : [],\n };\n}\n\nexport const packageDescriptor: AiPackageDescriptor = Object.freeze({\n packageName: AI_SPEECH_PACKAGE,\n featureFlagId: AI_SPEECH_FEATURE_FLAG_ID,\n envPrefix: AI_SPEECH_ENV_PREFIX,\n summary:\n \"Speech orchestration, TTS cache contracts, STT/TTS routing, and voice tier policy for Plasius AI.\",\n});\n"],"mappings":"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","names":[]}
|
|
@@ -22,7 +22,7 @@ contracts use opaque IDs and locales, reuse the Player System priority
|
|
|
22
22
|
vocabulary, require explicit ducking and combat-safe delivery behavior, and
|
|
23
23
|
bound repeating-warning intervals and frequencies.
|
|
24
24
|
|
|
25
|
-
The policy is fail-closed when `
|
|
25
|
+
The policy is fail-closed when `harmony.player-system.audio.enabled` is absent or
|
|
26
26
|
false, when audio is muted, when a dispatch is duplicated, or when combat-safe
|
|
27
27
|
mode suppresses its priority. Critical audio bypasses ducking; normal and low
|
|
28
28
|
priority audio are suppressed during combat-safe mode; narration may be
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# ADR-0004: Project Harmony Namespace Cutover
|
|
2
|
+
|
|
3
|
+
- Date: 2026-07-15
|
|
4
|
+
- Status: Accepted
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
Project Harmony replaces the Isekai product namespace in one coordinated,
|
|
9
|
+
breaking release train. `@plasius/ai-speech` exposes the Player System audio
|
|
10
|
+
rollout key as a public constant used by deterministic dispatch policy. Keeping
|
|
11
|
+
the previous value or a compatibility branch would make host rollout state
|
|
12
|
+
ambiguous.
|
|
13
|
+
|
|
14
|
+
The tracked implementation is
|
|
15
|
+
[Plasius-LTD/ai-speech#29](https://github.com/Plasius-LTD/ai-speech/issues/29),
|
|
16
|
+
under the Project Harmony namespace Feature and its remote rollout control
|
|
17
|
+
`harmony.namespace-cutover.enabled`.
|
|
18
|
+
|
|
19
|
+
## Decision
|
|
20
|
+
|
|
21
|
+
- Replace `isekai.player-system.audio.enabled` with
|
|
22
|
+
`harmony.player-system.audio.enabled`.
|
|
23
|
+
- Publish only the Harmony value in the public constant and policy inputs.
|
|
24
|
+
- Do not add an alias, dual-read parser, environment fallback, or runtime
|
|
25
|
+
translation for the previous namespace.
|
|
26
|
+
- Keep feature-flag evaluation in host applications; the package continues to
|
|
27
|
+
evaluate only caller-supplied snapshots.
|
|
28
|
+
- Release the change as the next major package version through the repository's
|
|
29
|
+
approved `cd.yml` workflow.
|
|
30
|
+
|
|
31
|
+
## Rollout and rollback
|
|
32
|
+
|
|
33
|
+
The host feature-flag service is the source of truth for
|
|
34
|
+
`harmony.namespace-cutover.enabled`. Consumers update stored keys and package
|
|
35
|
+
majors during the coordinated maintenance window, then enable the flag for the
|
|
36
|
+
approved cohort.
|
|
37
|
+
|
|
38
|
+
Rollback requires disabling the cutover flag, restoring the coordinated
|
|
39
|
+
previous package majors, and applying the verified reverse stored-value
|
|
40
|
+
migration. This contract package has no persistent store of its own.
|
|
41
|
+
|
|
42
|
+
## Consequences
|
|
43
|
+
|
|
44
|
+
- Consumers receive one canonical Harmony audio rollout key.
|
|
45
|
+
- The exported string-value change is intentionally SemVer-major while the
|
|
46
|
+
TypeScript symbol name remains stable.
|
|
47
|
+
- `player-system` can consume the new major only as part of the coordinated
|
|
48
|
+
cutover release train.
|
package/docs/adrs/index.md
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
# Architecture Decision Records
|
|
2
2
|
|
|
3
3
|
- [ADR-0001: @plasius/ai-speech Package Boundary](./adr-0001-package-boundary.md)
|
|
4
|
+
- [ADR-0002: TTS Cache, Player Address, and Voice Tier Contracts](./adr-0002-tts-cache-and-address-policy-contracts.md)
|
|
5
|
+
- [ADR-0003: Player System Audio Contract Boundary](./adr-0003-player-system-audio-contracts.md)
|
|
6
|
+
- [ADR-0004: Project Harmony Namespace Cutover](./adr-0004-harmony-namespace-cutover.md)
|
package/package.json
CHANGED