@plasius/ai-speech 0.1.5 → 0.1.7
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 +30 -0
- package/README.md +45 -0
- package/dist/index.cjs +187 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +77 -1
- package/dist/index.d.ts +77 -1
- package/dist/index.js +176 -0
- package/dist/index.js.map +1 -1
- package/docs/adrs/adr-0003-player-system-audio-contracts.md +52 -0
- package/package.json +8 -8
package/CHANGELOG.md
CHANGED
|
@@ -18,6 +18,34 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
18
18
|
- **Security**
|
|
19
19
|
- (placeholder)
|
|
20
20
|
|
|
21
|
+
## [0.1.7] - 2026-07-12
|
|
22
|
+
|
|
23
|
+
- **Added**
|
|
24
|
+
- Player System audio contracts for narrated responses, localized cues, repeating warnings, priority/ducking policy, and combat-safe delivery under `isekai.player-system.audio.enabled`.
|
|
25
|
+
|
|
26
|
+
- **Changed**
|
|
27
|
+
- (none)
|
|
28
|
+
|
|
29
|
+
- **Fixed**
|
|
30
|
+
- (none)
|
|
31
|
+
|
|
32
|
+
- **Security**
|
|
33
|
+
- Audio contracts use opaque utterance/cue identifiers and bounded warning delivery; raw speech text and provider credentials remain outside the package.
|
|
34
|
+
|
|
35
|
+
## [0.1.6] - 2026-06-28
|
|
36
|
+
|
|
37
|
+
- **Added**
|
|
38
|
+
- (placeholder)
|
|
39
|
+
|
|
40
|
+
- **Changed**
|
|
41
|
+
- Refreshed development dependency baselines to `@types/node@26.0.1`, `@typescript-eslint/*@8.62.0`, `eslint@10.6.0`, `globals@17.7.0`, and `vitest@4.1.9`.
|
|
42
|
+
|
|
43
|
+
- **Fixed**
|
|
44
|
+
- (placeholder)
|
|
45
|
+
|
|
46
|
+
- **Security**
|
|
47
|
+
- (placeholder)
|
|
48
|
+
|
|
21
49
|
## [0.1.5] - 2026-06-22
|
|
22
50
|
|
|
23
51
|
- **Added**
|
|
@@ -54,3 +82,5 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
54
82
|
[0.1.1]: https://github.com/Plasius-LTD/ai-speech/releases/tag/v0.1.1
|
|
55
83
|
[0.1.4]: https://github.com/Plasius-LTD/ai-speech/releases/tag/v0.1.4
|
|
56
84
|
[0.1.5]: https://github.com/Plasius-LTD/ai-speech/releases/tag/v0.1.5
|
|
85
|
+
[0.1.6]: https://github.com/Plasius-LTD/ai-speech/releases/tag/v0.1.6
|
|
86
|
+
[0.1.7]: https://github.com/Plasius-LTD/ai-speech/releases/tag/v0.1.7
|
package/README.md
CHANGED
|
@@ -10,6 +10,8 @@ This package defines public contracts for:
|
|
|
10
10
|
- cache-safe player address rendering
|
|
11
11
|
- development, standard, and premium-character voice tier routing
|
|
12
12
|
- cache telemetry payloads
|
|
13
|
+
- Player System narrated responses, localized one-shot cues, and repeating warnings
|
|
14
|
+
- priority, ducking, suppression, and combat-safe delivery policy
|
|
13
15
|
|
|
14
16
|
It does not fetch remote feature flags, talk to providers, or store cache entries directly. Host applications and provider adapters remain responsible for runtime I/O.
|
|
15
17
|
|
|
@@ -81,6 +83,49 @@ console.log(voiceTier.resolvedTier);
|
|
|
81
83
|
|
|
82
84
|
Default behavior is fail-closed when the caller does not provide a remote flag snapshot.
|
|
83
85
|
|
|
86
|
+
## Player System audio contracts
|
|
87
|
+
|
|
88
|
+
Player System audio is enabled by the remotely evaluated
|
|
89
|
+
`isekai.player-system.audio.enabled` flag. The package keeps the three delivery
|
|
90
|
+
channels distinct so hosts can apply separate UX and accessibility behavior:
|
|
91
|
+
|
|
92
|
+
- `narrated-response` uses an opaque utterance ID and locale; raw speech text
|
|
93
|
+
stays with the host/provider pipeline.
|
|
94
|
+
- `localized-cue` uses a stable cue ID, locale, and one of the exported families
|
|
95
|
+
`status`, `tutorial`, `mission`, `mcc`, or `warning`.
|
|
96
|
+
- `repeating-warning` requires bounded repeat intervals and per-minute limits.
|
|
97
|
+
|
|
98
|
+
Contracts carry `critical`, `high`, `normal`, or `low` priority plus an explicit
|
|
99
|
+
ducking mode. Critical audio bypasses ducking. Combat-safe policy suppresses
|
|
100
|
+
normal and low priority audio, allows high and critical audio, and can condense
|
|
101
|
+
or defer narration according to the contract. Rollout-disabled, muted, duplicate,
|
|
102
|
+
and explicitly suppressed dispatches fail closed with a reason code.
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import {
|
|
106
|
+
AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID,
|
|
107
|
+
createAiSpeechNarratedResponse,
|
|
108
|
+
resolveAiSpeechAudioPolicy,
|
|
109
|
+
} from "@plasius/ai-speech";
|
|
110
|
+
|
|
111
|
+
const narration = createAiSpeechNarratedResponse({
|
|
112
|
+
id: "tutorial-combat-hint",
|
|
113
|
+
utteranceId: "tutorial-combat-hint-v1",
|
|
114
|
+
locale: "en-GB",
|
|
115
|
+
priority: "high",
|
|
116
|
+
ducking: "music",
|
|
117
|
+
combatSafeDelivery: "condensed",
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const decision = resolveAiSpeechAudioPolicy({
|
|
121
|
+
contract: narration,
|
|
122
|
+
focusMode: "combat-safe",
|
|
123
|
+
featureFlags: {
|
|
124
|
+
[AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID]: true,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
84
129
|
## Cache Safety Rules
|
|
85
130
|
|
|
86
131
|
- User names, account handles, and user-renamed character names are redacted to a generic label such as `Player`.
|
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
AI_SPEECH_AUDIO_CHANNELS: () => AI_SPEECH_AUDIO_CHANNELS,
|
|
24
|
+
AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES: () => AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES,
|
|
25
|
+
AI_SPEECH_AUDIO_CUE_FAMILIES: () => AI_SPEECH_AUDIO_CUE_FAMILIES,
|
|
26
|
+
AI_SPEECH_AUDIO_DUCKING_MODES: () => AI_SPEECH_AUDIO_DUCKING_MODES,
|
|
27
|
+
AI_SPEECH_AUDIO_FOCUS_MODES: () => AI_SPEECH_AUDIO_FOCUS_MODES,
|
|
28
|
+
AI_SPEECH_AUDIO_PRIORITIES: () => AI_SPEECH_AUDIO_PRIORITIES,
|
|
23
29
|
AI_SPEECH_CACHE_MODES: () => AI_SPEECH_CACHE_MODES,
|
|
24
30
|
AI_SPEECH_CACHE_SCOPES: () => AI_SPEECH_CACHE_SCOPES,
|
|
25
31
|
AI_SPEECH_DEFAULT_PLAYER_LABEL: () => AI_SPEECH_DEFAULT_PLAYER_LABEL,
|
|
@@ -30,6 +36,7 @@ __export(index_exports, {
|
|
|
30
36
|
AI_SPEECH_NEAR_REUSE_SAFE_CLASSES: () => AI_SPEECH_NEAR_REUSE_SAFE_CLASSES,
|
|
31
37
|
AI_SPEECH_PACKAGE: () => AI_SPEECH_PACKAGE,
|
|
32
38
|
AI_SPEECH_PLAYER_ADDRESS_SOURCES: () => AI_SPEECH_PLAYER_ADDRESS_SOURCES,
|
|
39
|
+
AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID: () => AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID,
|
|
33
40
|
AI_SPEECH_PROVIDER_CACHE_PERMISSIONS: () => AI_SPEECH_PROVIDER_CACHE_PERMISSIONS,
|
|
34
41
|
AI_SPEECH_ROLLOUTS: () => AI_SPEECH_ROLLOUTS,
|
|
35
42
|
AI_SPEECH_ROLLOUT_EVALUATORS: () => AI_SPEECH_ROLLOUT_EVALUATORS,
|
|
@@ -37,13 +44,17 @@ __export(index_exports, {
|
|
|
37
44
|
AI_SPEECH_UTTERANCE_CLASSES: () => AI_SPEECH_UTTERANCE_CLASSES,
|
|
38
45
|
AI_SPEECH_VOICE_TIERS: () => AI_SPEECH_VOICE_TIERS,
|
|
39
46
|
createAiSpeechCacheKey: () => createAiSpeechCacheKey,
|
|
47
|
+
createAiSpeechLocalizedCue: () => createAiSpeechLocalizedCue,
|
|
48
|
+
createAiSpeechNarratedResponse: () => createAiSpeechNarratedResponse,
|
|
40
49
|
createAiSpeechNearReuseFingerprint: () => createAiSpeechNearReuseFingerprint,
|
|
50
|
+
createAiSpeechRepeatingWarning: () => createAiSpeechRepeatingWarning,
|
|
41
51
|
isAiSpeechFeatureEnabled: () => isAiSpeechFeatureEnabled,
|
|
42
52
|
isAiSpeechNearReuseSafeClass: () => isAiSpeechNearReuseSafeClass,
|
|
43
53
|
normalizeAiSpeechText: () => normalizeAiSpeechText,
|
|
44
54
|
packageDescriptor: () => packageDescriptor,
|
|
45
55
|
planAiSpeechCache: () => planAiSpeechCache,
|
|
46
56
|
renderAiSpeechText: () => renderAiSpeechText,
|
|
57
|
+
resolveAiSpeechAudioPolicy: () => resolveAiSpeechAudioPolicy,
|
|
47
58
|
resolveAiSpeechPlayerAddress: () => resolveAiSpeechPlayerAddress,
|
|
48
59
|
resolveAiSpeechRolloutDecision: () => resolveAiSpeechRolloutDecision,
|
|
49
60
|
resolveAiSpeechVoiceTier: () => resolveAiSpeechVoiceTier
|
|
@@ -377,6 +388,171 @@ function planAiSpeechCache(input) {
|
|
|
377
388
|
reasonCodes: ["near-reuse-eligible"]
|
|
378
389
|
};
|
|
379
390
|
}
|
|
391
|
+
var AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID = "isekai.player-system.audio.enabled";
|
|
392
|
+
var AI_SPEECH_AUDIO_CHANNELS = [
|
|
393
|
+
"narrated-response",
|
|
394
|
+
"localized-cue",
|
|
395
|
+
"repeating-warning"
|
|
396
|
+
];
|
|
397
|
+
var AI_SPEECH_AUDIO_PRIORITIES = [
|
|
398
|
+
"critical",
|
|
399
|
+
"high",
|
|
400
|
+
"normal",
|
|
401
|
+
"low"
|
|
402
|
+
];
|
|
403
|
+
var AI_SPEECH_AUDIO_DUCKING_MODES = [
|
|
404
|
+
"none",
|
|
405
|
+
"music",
|
|
406
|
+
"non-critical",
|
|
407
|
+
"lower-priority"
|
|
408
|
+
];
|
|
409
|
+
var AI_SPEECH_AUDIO_CUE_FAMILIES = [
|
|
410
|
+
"status",
|
|
411
|
+
"tutorial",
|
|
412
|
+
"mission",
|
|
413
|
+
"mcc",
|
|
414
|
+
"warning"
|
|
415
|
+
];
|
|
416
|
+
var AI_SPEECH_AUDIO_FOCUS_MODES = [
|
|
417
|
+
"ambient",
|
|
418
|
+
"focused",
|
|
419
|
+
"combat-safe"
|
|
420
|
+
];
|
|
421
|
+
var AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES = [
|
|
422
|
+
"full",
|
|
423
|
+
"condensed",
|
|
424
|
+
"deferred",
|
|
425
|
+
"suppressed"
|
|
426
|
+
];
|
|
427
|
+
function requireAudioEnum(value, allowed, label) {
|
|
428
|
+
if (!allowed.includes(value)) {
|
|
429
|
+
throw new Error(`${label} is not supported: ${value}.`);
|
|
430
|
+
}
|
|
431
|
+
return value;
|
|
432
|
+
}
|
|
433
|
+
function requireBoundedAudioNumber(value, minimum, maximum, label) {
|
|
434
|
+
if (!Number.isFinite(value) || value < minimum || value > maximum) {
|
|
435
|
+
throw new Error(
|
|
436
|
+
`${label} must be between ${minimum} and ${maximum}, inclusive.`
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
return value;
|
|
440
|
+
}
|
|
441
|
+
function createAiSpeechAudioContractBase(input) {
|
|
442
|
+
return {
|
|
443
|
+
id: requireNonEmptyString(input.id, "Audio contract id"),
|
|
444
|
+
featureFlagId: AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID,
|
|
445
|
+
priority: requireAudioEnum(
|
|
446
|
+
input.priority,
|
|
447
|
+
AI_SPEECH_AUDIO_PRIORITIES,
|
|
448
|
+
"Audio priority"
|
|
449
|
+
),
|
|
450
|
+
ducking: requireAudioEnum(
|
|
451
|
+
input.ducking,
|
|
452
|
+
AI_SPEECH_AUDIO_DUCKING_MODES,
|
|
453
|
+
"Audio ducking mode"
|
|
454
|
+
),
|
|
455
|
+
combatSafeDelivery: requireAudioEnum(
|
|
456
|
+
input.combatSafeDelivery,
|
|
457
|
+
AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES,
|
|
458
|
+
"Combat-safe audio delivery"
|
|
459
|
+
)
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
function createAiSpeechNarratedResponse(input) {
|
|
463
|
+
return Object.freeze({
|
|
464
|
+
...createAiSpeechAudioContractBase(input),
|
|
465
|
+
channel: "narrated-response",
|
|
466
|
+
utteranceId: requireNonEmptyString(input.utteranceId, "Utterance id"),
|
|
467
|
+
locale: requireNonEmptyString(input.locale, "Locale")
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
function createAiSpeechLocalizedCue(input) {
|
|
471
|
+
return Object.freeze({
|
|
472
|
+
...createAiSpeechAudioContractBase(input),
|
|
473
|
+
channel: "localized-cue",
|
|
474
|
+
cueFamily: requireAudioEnum(
|
|
475
|
+
input.cueFamily,
|
|
476
|
+
AI_SPEECH_AUDIO_CUE_FAMILIES,
|
|
477
|
+
"Audio cue family"
|
|
478
|
+
),
|
|
479
|
+
cueId: requireNonEmptyString(input.cueId, "Cue id"),
|
|
480
|
+
locale: requireNonEmptyString(input.locale, "Locale")
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
function createAiSpeechRepeatingWarning(input) {
|
|
484
|
+
return Object.freeze({
|
|
485
|
+
...createAiSpeechAudioContractBase(input),
|
|
486
|
+
channel: "repeating-warning",
|
|
487
|
+
warningId: requireNonEmptyString(input.warningId, "Warning id"),
|
|
488
|
+
intervalSeconds: requireBoundedAudioNumber(
|
|
489
|
+
input.intervalSeconds,
|
|
490
|
+
1,
|
|
491
|
+
3600,
|
|
492
|
+
"Warning intervalSeconds"
|
|
493
|
+
),
|
|
494
|
+
maximumOccurrencesPerMinute: requireBoundedAudioNumber(
|
|
495
|
+
input.maximumOccurrencesPerMinute,
|
|
496
|
+
1,
|
|
497
|
+
60,
|
|
498
|
+
"Warning maximumOccurrencesPerMinute"
|
|
499
|
+
)
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
function isPlayerSystemAudioEnabled(featureFlags = {}) {
|
|
503
|
+
return featureFlags[AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID] === true;
|
|
504
|
+
}
|
|
505
|
+
function resolveAiSpeechAudioPolicy(input) {
|
|
506
|
+
const { contract } = input;
|
|
507
|
+
if (!isPlayerSystemAudioEnabled(input.featureFlags)) {
|
|
508
|
+
return {
|
|
509
|
+
deliver: false,
|
|
510
|
+
mode: "deferred",
|
|
511
|
+
ducking: "none",
|
|
512
|
+
reasonCodes: ["player-system-audio-rollout-disabled"]
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
if (input.masterMuted || input.userMuted) {
|
|
516
|
+
return {
|
|
517
|
+
deliver: false,
|
|
518
|
+
mode: "deferred",
|
|
519
|
+
ducking: "none",
|
|
520
|
+
reasonCodes: [input.masterMuted ? "master-muted" : "user-muted"]
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
if (input.activeContractIds?.includes(contract.id)) {
|
|
524
|
+
return {
|
|
525
|
+
deliver: false,
|
|
526
|
+
mode: "deferred",
|
|
527
|
+
ducking: "none",
|
|
528
|
+
reasonCodes: ["duplicate-audio-contract-suppressed"]
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
if (input.focusMode === "combat-safe" && contract.priority !== "critical" && contract.priority !== "high") {
|
|
532
|
+
return {
|
|
533
|
+
deliver: false,
|
|
534
|
+
mode: "deferred",
|
|
535
|
+
ducking: "none",
|
|
536
|
+
reasonCodes: ["combat-safe-priority-suppressed"]
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
if (contract.combatSafeDelivery === "suppressed") {
|
|
540
|
+
return {
|
|
541
|
+
deliver: false,
|
|
542
|
+
mode: "deferred",
|
|
543
|
+
ducking: "none",
|
|
544
|
+
reasonCodes: ["audio-contract-suppressed"]
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
const mode = input.focusMode === "combat-safe" ? contract.combatSafeDelivery === "condensed" ? "condensed" : contract.combatSafeDelivery === "deferred" ? "deferred" : "full" : "full";
|
|
548
|
+
const ducking = contract.priority === "critical" ? "none" : contract.ducking;
|
|
549
|
+
return {
|
|
550
|
+
deliver: mode !== "deferred",
|
|
551
|
+
mode,
|
|
552
|
+
ducking,
|
|
553
|
+
reasonCodes: contract.priority === "critical" ? ["critical-priority-bypasses-ducking"] : []
|
|
554
|
+
};
|
|
555
|
+
}
|
|
380
556
|
var packageDescriptor = Object.freeze({
|
|
381
557
|
packageName: AI_SPEECH_PACKAGE,
|
|
382
558
|
featureFlagId: AI_SPEECH_FEATURE_FLAG_ID,
|
|
@@ -385,6 +561,12 @@ var packageDescriptor = Object.freeze({
|
|
|
385
561
|
});
|
|
386
562
|
// Annotate the CommonJS export names for ESM import in node:
|
|
387
563
|
0 && (module.exports = {
|
|
564
|
+
AI_SPEECH_AUDIO_CHANNELS,
|
|
565
|
+
AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES,
|
|
566
|
+
AI_SPEECH_AUDIO_CUE_FAMILIES,
|
|
567
|
+
AI_SPEECH_AUDIO_DUCKING_MODES,
|
|
568
|
+
AI_SPEECH_AUDIO_FOCUS_MODES,
|
|
569
|
+
AI_SPEECH_AUDIO_PRIORITIES,
|
|
388
570
|
AI_SPEECH_CACHE_MODES,
|
|
389
571
|
AI_SPEECH_CACHE_SCOPES,
|
|
390
572
|
AI_SPEECH_DEFAULT_PLAYER_LABEL,
|
|
@@ -395,6 +577,7 @@ var packageDescriptor = Object.freeze({
|
|
|
395
577
|
AI_SPEECH_NEAR_REUSE_SAFE_CLASSES,
|
|
396
578
|
AI_SPEECH_PACKAGE,
|
|
397
579
|
AI_SPEECH_PLAYER_ADDRESS_SOURCES,
|
|
580
|
+
AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID,
|
|
398
581
|
AI_SPEECH_PROVIDER_CACHE_PERMISSIONS,
|
|
399
582
|
AI_SPEECH_ROLLOUTS,
|
|
400
583
|
AI_SPEECH_ROLLOUT_EVALUATORS,
|
|
@@ -402,13 +585,17 @@ var packageDescriptor = Object.freeze({
|
|
|
402
585
|
AI_SPEECH_UTTERANCE_CLASSES,
|
|
403
586
|
AI_SPEECH_VOICE_TIERS,
|
|
404
587
|
createAiSpeechCacheKey,
|
|
588
|
+
createAiSpeechLocalizedCue,
|
|
589
|
+
createAiSpeechNarratedResponse,
|
|
405
590
|
createAiSpeechNearReuseFingerprint,
|
|
591
|
+
createAiSpeechRepeatingWarning,
|
|
406
592
|
isAiSpeechFeatureEnabled,
|
|
407
593
|
isAiSpeechNearReuseSafeClass,
|
|
408
594
|
normalizeAiSpeechText,
|
|
409
595
|
packageDescriptor,
|
|
410
596
|
planAiSpeechCache,
|
|
411
597
|
renderAiSpeechText,
|
|
598
|
+
resolveAiSpeechAudioPolicy,
|
|
412
599
|
resolveAiSpeechPlayerAddress,
|
|
413
600
|
resolveAiSpeechRolloutDecision,
|
|
414
601
|
resolveAiSpeechVoiceTier
|
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 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 \"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":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -145,6 +145,82 @@ 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: "isekai.player-system.audio.enabled";
|
|
149
|
+
declare const AI_SPEECH_AUDIO_CHANNELS: readonly ["narrated-response", "localized-cue", "repeating-warning"];
|
|
150
|
+
type AiSpeechAudioChannel = (typeof AI_SPEECH_AUDIO_CHANNELS)[number];
|
|
151
|
+
declare const AI_SPEECH_AUDIO_PRIORITIES: readonly ["critical", "high", "normal", "low"];
|
|
152
|
+
type AiSpeechAudioPriority = (typeof AI_SPEECH_AUDIO_PRIORITIES)[number];
|
|
153
|
+
declare const AI_SPEECH_AUDIO_DUCKING_MODES: readonly ["none", "music", "non-critical", "lower-priority"];
|
|
154
|
+
type AiSpeechAudioDuckingMode = (typeof AI_SPEECH_AUDIO_DUCKING_MODES)[number];
|
|
155
|
+
declare const AI_SPEECH_AUDIO_CUE_FAMILIES: readonly ["status", "tutorial", "mission", "mcc", "warning"];
|
|
156
|
+
type AiSpeechAudioCueFamily = (typeof AI_SPEECH_AUDIO_CUE_FAMILIES)[number];
|
|
157
|
+
declare const AI_SPEECH_AUDIO_FOCUS_MODES: readonly ["ambient", "focused", "combat-safe"];
|
|
158
|
+
type AiSpeechAudioFocusMode = (typeof AI_SPEECH_AUDIO_FOCUS_MODES)[number];
|
|
159
|
+
declare const AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES: readonly ["full", "condensed", "deferred", "suppressed"];
|
|
160
|
+
type AiSpeechAudioCombatSafeDelivery = (typeof AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES)[number];
|
|
161
|
+
interface AiSpeechAudioContractBase {
|
|
162
|
+
readonly id: string;
|
|
163
|
+
readonly featureFlagId: typeof AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID;
|
|
164
|
+
readonly priority: AiSpeechAudioPriority;
|
|
165
|
+
readonly ducking: AiSpeechAudioDuckingMode;
|
|
166
|
+
readonly combatSafeDelivery: AiSpeechAudioCombatSafeDelivery;
|
|
167
|
+
}
|
|
168
|
+
interface AiSpeechNarratedResponseContract extends AiSpeechAudioContractBase {
|
|
169
|
+
readonly channel: "narrated-response";
|
|
170
|
+
readonly utteranceId: string;
|
|
171
|
+
readonly locale: string;
|
|
172
|
+
}
|
|
173
|
+
interface AiSpeechLocalizedCueContract extends AiSpeechAudioContractBase {
|
|
174
|
+
readonly channel: "localized-cue";
|
|
175
|
+
readonly cueFamily: AiSpeechAudioCueFamily;
|
|
176
|
+
readonly cueId: string;
|
|
177
|
+
readonly locale: string;
|
|
178
|
+
}
|
|
179
|
+
interface AiSpeechRepeatingWarningContract extends AiSpeechAudioContractBase {
|
|
180
|
+
readonly channel: "repeating-warning";
|
|
181
|
+
readonly warningId: string;
|
|
182
|
+
readonly intervalSeconds: number;
|
|
183
|
+
readonly maximumOccurrencesPerMinute: number;
|
|
184
|
+
}
|
|
185
|
+
type AiSpeechAudioContract = AiSpeechNarratedResponseContract | AiSpeechLocalizedCueContract | AiSpeechRepeatingWarningContract;
|
|
186
|
+
interface AiSpeechAudioContractBaseInput {
|
|
187
|
+
readonly id: string;
|
|
188
|
+
readonly priority: AiSpeechAudioPriority;
|
|
189
|
+
readonly ducking: AiSpeechAudioDuckingMode;
|
|
190
|
+
readonly combatSafeDelivery: AiSpeechAudioCombatSafeDelivery;
|
|
191
|
+
}
|
|
192
|
+
interface CreateAiSpeechNarratedResponseInput extends AiSpeechAudioContractBaseInput {
|
|
193
|
+
readonly utteranceId: string;
|
|
194
|
+
readonly locale: string;
|
|
195
|
+
}
|
|
196
|
+
interface CreateAiSpeechLocalizedCueInput extends AiSpeechAudioContractBaseInput {
|
|
197
|
+
readonly cueFamily: AiSpeechAudioCueFamily;
|
|
198
|
+
readonly cueId: string;
|
|
199
|
+
readonly locale: string;
|
|
200
|
+
}
|
|
201
|
+
interface CreateAiSpeechRepeatingWarningInput extends AiSpeechAudioContractBaseInput {
|
|
202
|
+
readonly warningId: string;
|
|
203
|
+
readonly intervalSeconds: number;
|
|
204
|
+
readonly maximumOccurrencesPerMinute: number;
|
|
205
|
+
}
|
|
206
|
+
declare function createAiSpeechNarratedResponse(input: CreateAiSpeechNarratedResponseInput): AiSpeechNarratedResponseContract;
|
|
207
|
+
declare function createAiSpeechLocalizedCue(input: CreateAiSpeechLocalizedCueInput): AiSpeechLocalizedCueContract;
|
|
208
|
+
declare function createAiSpeechRepeatingWarning(input: CreateAiSpeechRepeatingWarningInput): AiSpeechRepeatingWarningContract;
|
|
209
|
+
interface ResolveAiSpeechAudioPolicyInput {
|
|
210
|
+
readonly contract: AiSpeechAudioContract;
|
|
211
|
+
readonly focusMode: AiSpeechAudioFocusMode;
|
|
212
|
+
readonly featureFlags?: AiSpeechFeatureFlagSnapshot;
|
|
213
|
+
readonly masterMuted?: boolean;
|
|
214
|
+
readonly userMuted?: boolean;
|
|
215
|
+
readonly activeContractIds?: readonly string[];
|
|
216
|
+
}
|
|
217
|
+
interface AiSpeechAudioPolicyDecision {
|
|
218
|
+
readonly deliver: boolean;
|
|
219
|
+
readonly mode: "full" | "condensed" | "deferred";
|
|
220
|
+
readonly ducking: AiSpeechAudioDuckingMode;
|
|
221
|
+
readonly reasonCodes: readonly string[];
|
|
222
|
+
}
|
|
223
|
+
declare function resolveAiSpeechAudioPolicy(input: ResolveAiSpeechAudioPolicyInput): AiSpeechAudioPolicyDecision;
|
|
148
224
|
declare const packageDescriptor: AiPackageDescriptor;
|
|
149
225
|
|
|
150
|
-
export { AI_SPEECH_CACHE_MODES, AI_SPEECH_CACHE_SCOPES, AI_SPEECH_DEFAULT_PLAYER_LABEL, AI_SPEECH_ENVIRONMENTS, AI_SPEECH_ENV_PREFIX, AI_SPEECH_FEATURE_FLAGS, AI_SPEECH_FEATURE_FLAG_ID, AI_SPEECH_NEAR_REUSE_SAFE_CLASSES, AI_SPEECH_PACKAGE, AI_SPEECH_PLAYER_ADDRESS_SOURCES, AI_SPEECH_PROVIDER_CACHE_PERMISSIONS, AI_SPEECH_ROLLOUTS, AI_SPEECH_ROLLOUT_EVALUATORS, AI_SPEECH_ROLLOUT_FALLBACK_MODES, AI_SPEECH_UTTERANCE_CLASSES, AI_SPEECH_VOICE_TIERS, type AiPackageDescriptor, type AiSpeechCacheKeyInput, type AiSpeechCacheMode, type AiSpeechCachePlan, type AiSpeechCacheScope, type AiSpeechCacheTelemetry, type AiSpeechEnvironment, type AiSpeechFeatureFlagKey, type AiSpeechFeatureFlagSnapshot, type AiSpeechPlayerAddressDecision, type AiSpeechPlayerAddressInput, type AiSpeechPlayerAddressSource, type AiSpeechProviderCachePermission, type AiSpeechRenderTextInput, type AiSpeechRenderTextResult, type AiSpeechRolloutControl, type AiSpeechRolloutDecision, type AiSpeechRolloutEvaluator, type AiSpeechRolloutFallbackMode, type AiSpeechUtteranceClass, type AiSpeechVoiceProfileRef, type AiSpeechVoiceTier, type AiSpeechVoiceTierDecision, type PlanAiSpeechCacheInput, type ResolveAiSpeechVoiceTierInput, createAiSpeechCacheKey, createAiSpeechNearReuseFingerprint, isAiSpeechFeatureEnabled, isAiSpeechNearReuseSafeClass, normalizeAiSpeechText, packageDescriptor, planAiSpeechCache, renderAiSpeechText, resolveAiSpeechPlayerAddress, resolveAiSpeechRolloutDecision, resolveAiSpeechVoiceTier };
|
|
226
|
+
export { AI_SPEECH_AUDIO_CHANNELS, AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES, AI_SPEECH_AUDIO_CUE_FAMILIES, AI_SPEECH_AUDIO_DUCKING_MODES, AI_SPEECH_AUDIO_FOCUS_MODES, AI_SPEECH_AUDIO_PRIORITIES, AI_SPEECH_CACHE_MODES, AI_SPEECH_CACHE_SCOPES, AI_SPEECH_DEFAULT_PLAYER_LABEL, AI_SPEECH_ENVIRONMENTS, AI_SPEECH_ENV_PREFIX, AI_SPEECH_FEATURE_FLAGS, AI_SPEECH_FEATURE_FLAG_ID, AI_SPEECH_NEAR_REUSE_SAFE_CLASSES, AI_SPEECH_PACKAGE, AI_SPEECH_PLAYER_ADDRESS_SOURCES, AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID, AI_SPEECH_PROVIDER_CACHE_PERMISSIONS, AI_SPEECH_ROLLOUTS, AI_SPEECH_ROLLOUT_EVALUATORS, AI_SPEECH_ROLLOUT_FALLBACK_MODES, AI_SPEECH_UTTERANCE_CLASSES, AI_SPEECH_VOICE_TIERS, type AiPackageDescriptor, type AiSpeechAudioChannel, type AiSpeechAudioCombatSafeDelivery, type AiSpeechAudioContract, type AiSpeechAudioContractBase, type AiSpeechAudioContractBaseInput, type AiSpeechAudioCueFamily, type AiSpeechAudioDuckingMode, type AiSpeechAudioFocusMode, type AiSpeechAudioPolicyDecision, type AiSpeechAudioPriority, type AiSpeechCacheKeyInput, type AiSpeechCacheMode, type AiSpeechCachePlan, type AiSpeechCacheScope, type AiSpeechCacheTelemetry, type AiSpeechEnvironment, type AiSpeechFeatureFlagKey, type AiSpeechFeatureFlagSnapshot, type AiSpeechLocalizedCueContract, type AiSpeechNarratedResponseContract, type AiSpeechPlayerAddressDecision, type AiSpeechPlayerAddressInput, type AiSpeechPlayerAddressSource, type AiSpeechProviderCachePermission, type AiSpeechRenderTextInput, type AiSpeechRenderTextResult, type AiSpeechRepeatingWarningContract, type AiSpeechRolloutControl, type AiSpeechRolloutDecision, type AiSpeechRolloutEvaluator, type AiSpeechRolloutFallbackMode, type AiSpeechUtteranceClass, type AiSpeechVoiceProfileRef, type AiSpeechVoiceTier, type AiSpeechVoiceTierDecision, type CreateAiSpeechLocalizedCueInput, type CreateAiSpeechNarratedResponseInput, type CreateAiSpeechRepeatingWarningInput, type PlanAiSpeechCacheInput, type ResolveAiSpeechAudioPolicyInput, type ResolveAiSpeechVoiceTierInput, createAiSpeechCacheKey, createAiSpeechLocalizedCue, createAiSpeechNarratedResponse, createAiSpeechNearReuseFingerprint, createAiSpeechRepeatingWarning, isAiSpeechFeatureEnabled, isAiSpeechNearReuseSafeClass, normalizeAiSpeechText, packageDescriptor, planAiSpeechCache, renderAiSpeechText, resolveAiSpeechAudioPolicy, resolveAiSpeechPlayerAddress, resolveAiSpeechRolloutDecision, resolveAiSpeechVoiceTier };
|
package/dist/index.d.ts
CHANGED
|
@@ -145,6 +145,82 @@ 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: "isekai.player-system.audio.enabled";
|
|
149
|
+
declare const AI_SPEECH_AUDIO_CHANNELS: readonly ["narrated-response", "localized-cue", "repeating-warning"];
|
|
150
|
+
type AiSpeechAudioChannel = (typeof AI_SPEECH_AUDIO_CHANNELS)[number];
|
|
151
|
+
declare const AI_SPEECH_AUDIO_PRIORITIES: readonly ["critical", "high", "normal", "low"];
|
|
152
|
+
type AiSpeechAudioPriority = (typeof AI_SPEECH_AUDIO_PRIORITIES)[number];
|
|
153
|
+
declare const AI_SPEECH_AUDIO_DUCKING_MODES: readonly ["none", "music", "non-critical", "lower-priority"];
|
|
154
|
+
type AiSpeechAudioDuckingMode = (typeof AI_SPEECH_AUDIO_DUCKING_MODES)[number];
|
|
155
|
+
declare const AI_SPEECH_AUDIO_CUE_FAMILIES: readonly ["status", "tutorial", "mission", "mcc", "warning"];
|
|
156
|
+
type AiSpeechAudioCueFamily = (typeof AI_SPEECH_AUDIO_CUE_FAMILIES)[number];
|
|
157
|
+
declare const AI_SPEECH_AUDIO_FOCUS_MODES: readonly ["ambient", "focused", "combat-safe"];
|
|
158
|
+
type AiSpeechAudioFocusMode = (typeof AI_SPEECH_AUDIO_FOCUS_MODES)[number];
|
|
159
|
+
declare const AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES: readonly ["full", "condensed", "deferred", "suppressed"];
|
|
160
|
+
type AiSpeechAudioCombatSafeDelivery = (typeof AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES)[number];
|
|
161
|
+
interface AiSpeechAudioContractBase {
|
|
162
|
+
readonly id: string;
|
|
163
|
+
readonly featureFlagId: typeof AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID;
|
|
164
|
+
readonly priority: AiSpeechAudioPriority;
|
|
165
|
+
readonly ducking: AiSpeechAudioDuckingMode;
|
|
166
|
+
readonly combatSafeDelivery: AiSpeechAudioCombatSafeDelivery;
|
|
167
|
+
}
|
|
168
|
+
interface AiSpeechNarratedResponseContract extends AiSpeechAudioContractBase {
|
|
169
|
+
readonly channel: "narrated-response";
|
|
170
|
+
readonly utteranceId: string;
|
|
171
|
+
readonly locale: string;
|
|
172
|
+
}
|
|
173
|
+
interface AiSpeechLocalizedCueContract extends AiSpeechAudioContractBase {
|
|
174
|
+
readonly channel: "localized-cue";
|
|
175
|
+
readonly cueFamily: AiSpeechAudioCueFamily;
|
|
176
|
+
readonly cueId: string;
|
|
177
|
+
readonly locale: string;
|
|
178
|
+
}
|
|
179
|
+
interface AiSpeechRepeatingWarningContract extends AiSpeechAudioContractBase {
|
|
180
|
+
readonly channel: "repeating-warning";
|
|
181
|
+
readonly warningId: string;
|
|
182
|
+
readonly intervalSeconds: number;
|
|
183
|
+
readonly maximumOccurrencesPerMinute: number;
|
|
184
|
+
}
|
|
185
|
+
type AiSpeechAudioContract = AiSpeechNarratedResponseContract | AiSpeechLocalizedCueContract | AiSpeechRepeatingWarningContract;
|
|
186
|
+
interface AiSpeechAudioContractBaseInput {
|
|
187
|
+
readonly id: string;
|
|
188
|
+
readonly priority: AiSpeechAudioPriority;
|
|
189
|
+
readonly ducking: AiSpeechAudioDuckingMode;
|
|
190
|
+
readonly combatSafeDelivery: AiSpeechAudioCombatSafeDelivery;
|
|
191
|
+
}
|
|
192
|
+
interface CreateAiSpeechNarratedResponseInput extends AiSpeechAudioContractBaseInput {
|
|
193
|
+
readonly utteranceId: string;
|
|
194
|
+
readonly locale: string;
|
|
195
|
+
}
|
|
196
|
+
interface CreateAiSpeechLocalizedCueInput extends AiSpeechAudioContractBaseInput {
|
|
197
|
+
readonly cueFamily: AiSpeechAudioCueFamily;
|
|
198
|
+
readonly cueId: string;
|
|
199
|
+
readonly locale: string;
|
|
200
|
+
}
|
|
201
|
+
interface CreateAiSpeechRepeatingWarningInput extends AiSpeechAudioContractBaseInput {
|
|
202
|
+
readonly warningId: string;
|
|
203
|
+
readonly intervalSeconds: number;
|
|
204
|
+
readonly maximumOccurrencesPerMinute: number;
|
|
205
|
+
}
|
|
206
|
+
declare function createAiSpeechNarratedResponse(input: CreateAiSpeechNarratedResponseInput): AiSpeechNarratedResponseContract;
|
|
207
|
+
declare function createAiSpeechLocalizedCue(input: CreateAiSpeechLocalizedCueInput): AiSpeechLocalizedCueContract;
|
|
208
|
+
declare function createAiSpeechRepeatingWarning(input: CreateAiSpeechRepeatingWarningInput): AiSpeechRepeatingWarningContract;
|
|
209
|
+
interface ResolveAiSpeechAudioPolicyInput {
|
|
210
|
+
readonly contract: AiSpeechAudioContract;
|
|
211
|
+
readonly focusMode: AiSpeechAudioFocusMode;
|
|
212
|
+
readonly featureFlags?: AiSpeechFeatureFlagSnapshot;
|
|
213
|
+
readonly masterMuted?: boolean;
|
|
214
|
+
readonly userMuted?: boolean;
|
|
215
|
+
readonly activeContractIds?: readonly string[];
|
|
216
|
+
}
|
|
217
|
+
interface AiSpeechAudioPolicyDecision {
|
|
218
|
+
readonly deliver: boolean;
|
|
219
|
+
readonly mode: "full" | "condensed" | "deferred";
|
|
220
|
+
readonly ducking: AiSpeechAudioDuckingMode;
|
|
221
|
+
readonly reasonCodes: readonly string[];
|
|
222
|
+
}
|
|
223
|
+
declare function resolveAiSpeechAudioPolicy(input: ResolveAiSpeechAudioPolicyInput): AiSpeechAudioPolicyDecision;
|
|
148
224
|
declare const packageDescriptor: AiPackageDescriptor;
|
|
149
225
|
|
|
150
|
-
export { AI_SPEECH_CACHE_MODES, AI_SPEECH_CACHE_SCOPES, AI_SPEECH_DEFAULT_PLAYER_LABEL, AI_SPEECH_ENVIRONMENTS, AI_SPEECH_ENV_PREFIX, AI_SPEECH_FEATURE_FLAGS, AI_SPEECH_FEATURE_FLAG_ID, AI_SPEECH_NEAR_REUSE_SAFE_CLASSES, AI_SPEECH_PACKAGE, AI_SPEECH_PLAYER_ADDRESS_SOURCES, AI_SPEECH_PROVIDER_CACHE_PERMISSIONS, AI_SPEECH_ROLLOUTS, AI_SPEECH_ROLLOUT_EVALUATORS, AI_SPEECH_ROLLOUT_FALLBACK_MODES, AI_SPEECH_UTTERANCE_CLASSES, AI_SPEECH_VOICE_TIERS, type AiPackageDescriptor, type AiSpeechCacheKeyInput, type AiSpeechCacheMode, type AiSpeechCachePlan, type AiSpeechCacheScope, type AiSpeechCacheTelemetry, type AiSpeechEnvironment, type AiSpeechFeatureFlagKey, type AiSpeechFeatureFlagSnapshot, type AiSpeechPlayerAddressDecision, type AiSpeechPlayerAddressInput, type AiSpeechPlayerAddressSource, type AiSpeechProviderCachePermission, type AiSpeechRenderTextInput, type AiSpeechRenderTextResult, type AiSpeechRolloutControl, type AiSpeechRolloutDecision, type AiSpeechRolloutEvaluator, type AiSpeechRolloutFallbackMode, type AiSpeechUtteranceClass, type AiSpeechVoiceProfileRef, type AiSpeechVoiceTier, type AiSpeechVoiceTierDecision, type PlanAiSpeechCacheInput, type ResolveAiSpeechVoiceTierInput, createAiSpeechCacheKey, createAiSpeechNearReuseFingerprint, isAiSpeechFeatureEnabled, isAiSpeechNearReuseSafeClass, normalizeAiSpeechText, packageDescriptor, planAiSpeechCache, renderAiSpeechText, resolveAiSpeechPlayerAddress, resolveAiSpeechRolloutDecision, resolveAiSpeechVoiceTier };
|
|
226
|
+
export { AI_SPEECH_AUDIO_CHANNELS, AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES, AI_SPEECH_AUDIO_CUE_FAMILIES, AI_SPEECH_AUDIO_DUCKING_MODES, AI_SPEECH_AUDIO_FOCUS_MODES, AI_SPEECH_AUDIO_PRIORITIES, AI_SPEECH_CACHE_MODES, AI_SPEECH_CACHE_SCOPES, AI_SPEECH_DEFAULT_PLAYER_LABEL, AI_SPEECH_ENVIRONMENTS, AI_SPEECH_ENV_PREFIX, AI_SPEECH_FEATURE_FLAGS, AI_SPEECH_FEATURE_FLAG_ID, AI_SPEECH_NEAR_REUSE_SAFE_CLASSES, AI_SPEECH_PACKAGE, AI_SPEECH_PLAYER_ADDRESS_SOURCES, AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID, AI_SPEECH_PROVIDER_CACHE_PERMISSIONS, AI_SPEECH_ROLLOUTS, AI_SPEECH_ROLLOUT_EVALUATORS, AI_SPEECH_ROLLOUT_FALLBACK_MODES, AI_SPEECH_UTTERANCE_CLASSES, AI_SPEECH_VOICE_TIERS, type AiPackageDescriptor, type AiSpeechAudioChannel, type AiSpeechAudioCombatSafeDelivery, type AiSpeechAudioContract, type AiSpeechAudioContractBase, type AiSpeechAudioContractBaseInput, type AiSpeechAudioCueFamily, type AiSpeechAudioDuckingMode, type AiSpeechAudioFocusMode, type AiSpeechAudioPolicyDecision, type AiSpeechAudioPriority, type AiSpeechCacheKeyInput, type AiSpeechCacheMode, type AiSpeechCachePlan, type AiSpeechCacheScope, type AiSpeechCacheTelemetry, type AiSpeechEnvironment, type AiSpeechFeatureFlagKey, type AiSpeechFeatureFlagSnapshot, type AiSpeechLocalizedCueContract, type AiSpeechNarratedResponseContract, type AiSpeechPlayerAddressDecision, type AiSpeechPlayerAddressInput, type AiSpeechPlayerAddressSource, type AiSpeechProviderCachePermission, type AiSpeechRenderTextInput, type AiSpeechRenderTextResult, type AiSpeechRepeatingWarningContract, type AiSpeechRolloutControl, type AiSpeechRolloutDecision, type AiSpeechRolloutEvaluator, type AiSpeechRolloutFallbackMode, type AiSpeechUtteranceClass, type AiSpeechVoiceProfileRef, type AiSpeechVoiceTier, type AiSpeechVoiceTierDecision, type CreateAiSpeechLocalizedCueInput, type CreateAiSpeechNarratedResponseInput, type CreateAiSpeechRepeatingWarningInput, type PlanAiSpeechCacheInput, type ResolveAiSpeechAudioPolicyInput, type ResolveAiSpeechVoiceTierInput, createAiSpeechCacheKey, createAiSpeechLocalizedCue, createAiSpeechNarratedResponse, createAiSpeechNearReuseFingerprint, createAiSpeechRepeatingWarning, isAiSpeechFeatureEnabled, isAiSpeechNearReuseSafeClass, normalizeAiSpeechText, packageDescriptor, planAiSpeechCache, renderAiSpeechText, resolveAiSpeechAudioPolicy, resolveAiSpeechPlayerAddress, resolveAiSpeechRolloutDecision, resolveAiSpeechVoiceTier };
|
package/dist/index.js
CHANGED
|
@@ -327,6 +327,171 @@ function planAiSpeechCache(input) {
|
|
|
327
327
|
reasonCodes: ["near-reuse-eligible"]
|
|
328
328
|
};
|
|
329
329
|
}
|
|
330
|
+
var AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID = "isekai.player-system.audio.enabled";
|
|
331
|
+
var AI_SPEECH_AUDIO_CHANNELS = [
|
|
332
|
+
"narrated-response",
|
|
333
|
+
"localized-cue",
|
|
334
|
+
"repeating-warning"
|
|
335
|
+
];
|
|
336
|
+
var AI_SPEECH_AUDIO_PRIORITIES = [
|
|
337
|
+
"critical",
|
|
338
|
+
"high",
|
|
339
|
+
"normal",
|
|
340
|
+
"low"
|
|
341
|
+
];
|
|
342
|
+
var AI_SPEECH_AUDIO_DUCKING_MODES = [
|
|
343
|
+
"none",
|
|
344
|
+
"music",
|
|
345
|
+
"non-critical",
|
|
346
|
+
"lower-priority"
|
|
347
|
+
];
|
|
348
|
+
var AI_SPEECH_AUDIO_CUE_FAMILIES = [
|
|
349
|
+
"status",
|
|
350
|
+
"tutorial",
|
|
351
|
+
"mission",
|
|
352
|
+
"mcc",
|
|
353
|
+
"warning"
|
|
354
|
+
];
|
|
355
|
+
var AI_SPEECH_AUDIO_FOCUS_MODES = [
|
|
356
|
+
"ambient",
|
|
357
|
+
"focused",
|
|
358
|
+
"combat-safe"
|
|
359
|
+
];
|
|
360
|
+
var AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES = [
|
|
361
|
+
"full",
|
|
362
|
+
"condensed",
|
|
363
|
+
"deferred",
|
|
364
|
+
"suppressed"
|
|
365
|
+
];
|
|
366
|
+
function requireAudioEnum(value, allowed, label) {
|
|
367
|
+
if (!allowed.includes(value)) {
|
|
368
|
+
throw new Error(`${label} is not supported: ${value}.`);
|
|
369
|
+
}
|
|
370
|
+
return value;
|
|
371
|
+
}
|
|
372
|
+
function requireBoundedAudioNumber(value, minimum, maximum, label) {
|
|
373
|
+
if (!Number.isFinite(value) || value < minimum || value > maximum) {
|
|
374
|
+
throw new Error(
|
|
375
|
+
`${label} must be between ${minimum} and ${maximum}, inclusive.`
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
return value;
|
|
379
|
+
}
|
|
380
|
+
function createAiSpeechAudioContractBase(input) {
|
|
381
|
+
return {
|
|
382
|
+
id: requireNonEmptyString(input.id, "Audio contract id"),
|
|
383
|
+
featureFlagId: AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID,
|
|
384
|
+
priority: requireAudioEnum(
|
|
385
|
+
input.priority,
|
|
386
|
+
AI_SPEECH_AUDIO_PRIORITIES,
|
|
387
|
+
"Audio priority"
|
|
388
|
+
),
|
|
389
|
+
ducking: requireAudioEnum(
|
|
390
|
+
input.ducking,
|
|
391
|
+
AI_SPEECH_AUDIO_DUCKING_MODES,
|
|
392
|
+
"Audio ducking mode"
|
|
393
|
+
),
|
|
394
|
+
combatSafeDelivery: requireAudioEnum(
|
|
395
|
+
input.combatSafeDelivery,
|
|
396
|
+
AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES,
|
|
397
|
+
"Combat-safe audio delivery"
|
|
398
|
+
)
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
function createAiSpeechNarratedResponse(input) {
|
|
402
|
+
return Object.freeze({
|
|
403
|
+
...createAiSpeechAudioContractBase(input),
|
|
404
|
+
channel: "narrated-response",
|
|
405
|
+
utteranceId: requireNonEmptyString(input.utteranceId, "Utterance id"),
|
|
406
|
+
locale: requireNonEmptyString(input.locale, "Locale")
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
function createAiSpeechLocalizedCue(input) {
|
|
410
|
+
return Object.freeze({
|
|
411
|
+
...createAiSpeechAudioContractBase(input),
|
|
412
|
+
channel: "localized-cue",
|
|
413
|
+
cueFamily: requireAudioEnum(
|
|
414
|
+
input.cueFamily,
|
|
415
|
+
AI_SPEECH_AUDIO_CUE_FAMILIES,
|
|
416
|
+
"Audio cue family"
|
|
417
|
+
),
|
|
418
|
+
cueId: requireNonEmptyString(input.cueId, "Cue id"),
|
|
419
|
+
locale: requireNonEmptyString(input.locale, "Locale")
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
function createAiSpeechRepeatingWarning(input) {
|
|
423
|
+
return Object.freeze({
|
|
424
|
+
...createAiSpeechAudioContractBase(input),
|
|
425
|
+
channel: "repeating-warning",
|
|
426
|
+
warningId: requireNonEmptyString(input.warningId, "Warning id"),
|
|
427
|
+
intervalSeconds: requireBoundedAudioNumber(
|
|
428
|
+
input.intervalSeconds,
|
|
429
|
+
1,
|
|
430
|
+
3600,
|
|
431
|
+
"Warning intervalSeconds"
|
|
432
|
+
),
|
|
433
|
+
maximumOccurrencesPerMinute: requireBoundedAudioNumber(
|
|
434
|
+
input.maximumOccurrencesPerMinute,
|
|
435
|
+
1,
|
|
436
|
+
60,
|
|
437
|
+
"Warning maximumOccurrencesPerMinute"
|
|
438
|
+
)
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
function isPlayerSystemAudioEnabled(featureFlags = {}) {
|
|
442
|
+
return featureFlags[AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID] === true;
|
|
443
|
+
}
|
|
444
|
+
function resolveAiSpeechAudioPolicy(input) {
|
|
445
|
+
const { contract } = input;
|
|
446
|
+
if (!isPlayerSystemAudioEnabled(input.featureFlags)) {
|
|
447
|
+
return {
|
|
448
|
+
deliver: false,
|
|
449
|
+
mode: "deferred",
|
|
450
|
+
ducking: "none",
|
|
451
|
+
reasonCodes: ["player-system-audio-rollout-disabled"]
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
if (input.masterMuted || input.userMuted) {
|
|
455
|
+
return {
|
|
456
|
+
deliver: false,
|
|
457
|
+
mode: "deferred",
|
|
458
|
+
ducking: "none",
|
|
459
|
+
reasonCodes: [input.masterMuted ? "master-muted" : "user-muted"]
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
if (input.activeContractIds?.includes(contract.id)) {
|
|
463
|
+
return {
|
|
464
|
+
deliver: false,
|
|
465
|
+
mode: "deferred",
|
|
466
|
+
ducking: "none",
|
|
467
|
+
reasonCodes: ["duplicate-audio-contract-suppressed"]
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
if (input.focusMode === "combat-safe" && contract.priority !== "critical" && contract.priority !== "high") {
|
|
471
|
+
return {
|
|
472
|
+
deliver: false,
|
|
473
|
+
mode: "deferred",
|
|
474
|
+
ducking: "none",
|
|
475
|
+
reasonCodes: ["combat-safe-priority-suppressed"]
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
if (contract.combatSafeDelivery === "suppressed") {
|
|
479
|
+
return {
|
|
480
|
+
deliver: false,
|
|
481
|
+
mode: "deferred",
|
|
482
|
+
ducking: "none",
|
|
483
|
+
reasonCodes: ["audio-contract-suppressed"]
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
const mode = input.focusMode === "combat-safe" ? contract.combatSafeDelivery === "condensed" ? "condensed" : contract.combatSafeDelivery === "deferred" ? "deferred" : "full" : "full";
|
|
487
|
+
const ducking = contract.priority === "critical" ? "none" : contract.ducking;
|
|
488
|
+
return {
|
|
489
|
+
deliver: mode !== "deferred",
|
|
490
|
+
mode,
|
|
491
|
+
ducking,
|
|
492
|
+
reasonCodes: contract.priority === "critical" ? ["critical-priority-bypasses-ducking"] : []
|
|
493
|
+
};
|
|
494
|
+
}
|
|
330
495
|
var packageDescriptor = Object.freeze({
|
|
331
496
|
packageName: AI_SPEECH_PACKAGE,
|
|
332
497
|
featureFlagId: AI_SPEECH_FEATURE_FLAG_ID,
|
|
@@ -334,6 +499,12 @@ var packageDescriptor = Object.freeze({
|
|
|
334
499
|
summary: "Speech orchestration, TTS cache contracts, STT/TTS routing, and voice tier policy for Plasius AI."
|
|
335
500
|
});
|
|
336
501
|
export {
|
|
502
|
+
AI_SPEECH_AUDIO_CHANNELS,
|
|
503
|
+
AI_SPEECH_AUDIO_COMBAT_SAFE_DELIVERIES,
|
|
504
|
+
AI_SPEECH_AUDIO_CUE_FAMILIES,
|
|
505
|
+
AI_SPEECH_AUDIO_DUCKING_MODES,
|
|
506
|
+
AI_SPEECH_AUDIO_FOCUS_MODES,
|
|
507
|
+
AI_SPEECH_AUDIO_PRIORITIES,
|
|
337
508
|
AI_SPEECH_CACHE_MODES,
|
|
338
509
|
AI_SPEECH_CACHE_SCOPES,
|
|
339
510
|
AI_SPEECH_DEFAULT_PLAYER_LABEL,
|
|
@@ -344,6 +515,7 @@ export {
|
|
|
344
515
|
AI_SPEECH_NEAR_REUSE_SAFE_CLASSES,
|
|
345
516
|
AI_SPEECH_PACKAGE,
|
|
346
517
|
AI_SPEECH_PLAYER_ADDRESS_SOURCES,
|
|
518
|
+
AI_SPEECH_PLAYER_SYSTEM_AUDIO_FLAG_ID,
|
|
347
519
|
AI_SPEECH_PROVIDER_CACHE_PERMISSIONS,
|
|
348
520
|
AI_SPEECH_ROLLOUTS,
|
|
349
521
|
AI_SPEECH_ROLLOUT_EVALUATORS,
|
|
@@ -351,13 +523,17 @@ export {
|
|
|
351
523
|
AI_SPEECH_UTTERANCE_CLASSES,
|
|
352
524
|
AI_SPEECH_VOICE_TIERS,
|
|
353
525
|
createAiSpeechCacheKey,
|
|
526
|
+
createAiSpeechLocalizedCue,
|
|
527
|
+
createAiSpeechNarratedResponse,
|
|
354
528
|
createAiSpeechNearReuseFingerprint,
|
|
529
|
+
createAiSpeechRepeatingWarning,
|
|
355
530
|
isAiSpeechFeatureEnabled,
|
|
356
531
|
isAiSpeechNearReuseSafeClass,
|
|
357
532
|
normalizeAiSpeechText,
|
|
358
533
|
packageDescriptor,
|
|
359
534
|
planAiSpeechCache,
|
|
360
535
|
renderAiSpeechText,
|
|
536
|
+
resolveAiSpeechAudioPolicy,
|
|
361
537
|
resolveAiSpeechPlayerAddress,
|
|
362
538
|
resolveAiSpeechRolloutDecision,
|
|
363
539
|
resolveAiSpeechVoiceTier
|
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 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 \"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":[]}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# ADR-0003: Player System Audio Contract Boundary
|
|
2
|
+
|
|
3
|
+
- Date: 2026-07-12
|
|
4
|
+
- Status: Accepted
|
|
5
|
+
|
|
6
|
+
## Context
|
|
7
|
+
|
|
8
|
+
The Player System needs stable speech-domain contracts for narrated responses,
|
|
9
|
+
localized one-shot cues, and repeating warnings. If each host defines these
|
|
10
|
+
channels and combat behavior independently, priority, ducking, suppression, and
|
|
11
|
+
fallback behavior will drift between the site and package consumers.
|
|
12
|
+
|
|
13
|
+
The package must remain provider- and runtime-independent. Raw narration text,
|
|
14
|
+
provider credentials, audio assets, playback engines, and feature-flag fetching
|
|
15
|
+
must remain outside the public contract package.
|
|
16
|
+
|
|
17
|
+
## Decision
|
|
18
|
+
|
|
19
|
+
`@plasius/ai-speech` owns immutable metadata contracts for the three audio
|
|
20
|
+
channels and deterministic policy evaluation over caller-supplied state. The
|
|
21
|
+
contracts use opaque IDs and locales, reuse the Player System priority
|
|
22
|
+
vocabulary, require explicit ducking and combat-safe delivery behavior, and
|
|
23
|
+
bound repeating-warning intervals and frequencies.
|
|
24
|
+
|
|
25
|
+
The policy is fail-closed when `isekai.player-system.audio.enabled` is absent or
|
|
26
|
+
false, when audio is muted, when a dispatch is duplicated, or when combat-safe
|
|
27
|
+
mode suppresses its priority. Critical audio bypasses ducking; normal and low
|
|
28
|
+
priority audio are suppressed during combat-safe mode; narration may be
|
|
29
|
+
condensed or deferred according to its contract.
|
|
30
|
+
|
|
31
|
+
Hosts own feature-flag evaluation, translations, asset resolution, playback,
|
|
32
|
+
captions, and non-audio accessibility fallbacks.
|
|
33
|
+
|
|
34
|
+
## Alternatives considered
|
|
35
|
+
|
|
36
|
+
- Keep the audio channel model in each host: rejected because consumers would
|
|
37
|
+
reinterpret priority and combat-safe rules independently.
|
|
38
|
+
- Store raw narration text in the package contract: rejected because it would
|
|
39
|
+
increase privacy and telemetry exposure without helping playback adapters.
|
|
40
|
+
- Depend on a playback engine or provider SDK: rejected because native, server,
|
|
41
|
+
browser, and test consumers need the same portable contract surface.
|
|
42
|
+
|
|
43
|
+
## Impact and validation
|
|
44
|
+
|
|
45
|
+
- Public consumers can construct and evaluate deterministic, bounded dispatch
|
|
46
|
+
metadata without runtime dependencies.
|
|
47
|
+
- The package remains compatible with existing TTS cache and voice-tier APIs.
|
|
48
|
+
- Unit tests cover channel separation, validation bounds, rollout fail-closed
|
|
49
|
+
behavior, combat-safe reduction, critical ducking bypass, duplicate
|
|
50
|
+
suppression, and mute handling.
|
|
51
|
+
- Release remains the normal package CI and approved `cd.yml` path; no provider
|
|
52
|
+
or runtime deployment is introduced by this decision.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@plasius/ai-speech",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "Speech orchestration, TTS cache contracts, STT/TTS routing, and voice tier policy for Plasius AI.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -64,16 +64,16 @@
|
|
|
64
64
|
"homepage": "https://github.com/Plasius-LTD/ai-speech#readme",
|
|
65
65
|
"devDependencies": {
|
|
66
66
|
"@eslint/js": "^10.0.1",
|
|
67
|
-
"@types/node": "^
|
|
68
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
69
|
-
"@typescript-eslint/parser": "^8.
|
|
70
|
-
"@vitest/coverage-v8": "^4.1.
|
|
71
|
-
"eslint": "^10.
|
|
72
|
-
"globals": "^17.
|
|
67
|
+
"@types/node": "^26.0.1",
|
|
68
|
+
"@typescript-eslint/eslint-plugin": "^8.62.0",
|
|
69
|
+
"@typescript-eslint/parser": "^8.62.0",
|
|
70
|
+
"@vitest/coverage-v8": "^4.1.9",
|
|
71
|
+
"eslint": "^10.6.0",
|
|
72
|
+
"globals": "^17.7.0",
|
|
73
73
|
"rimraf": "^6.1.3",
|
|
74
74
|
"tsup": "^8.5.1",
|
|
75
75
|
"typescript": "^6.0.3",
|
|
76
|
-
"vitest": "^4.1.
|
|
76
|
+
"vitest": "^4.1.9"
|
|
77
77
|
},
|
|
78
78
|
"publishConfig": {
|
|
79
79
|
"access": "public"
|