playkit-sdk 1.2.13 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +86 -86
- package/README.md +246 -244
- package/dist/playkit-sdk.cjs.js +1706 -1530
- package/dist/playkit-sdk.cjs.js.map +1 -1
- package/dist/playkit-sdk.d.ts +59 -5
- package/dist/playkit-sdk.esm.js +1706 -1530
- package/dist/playkit-sdk.esm.js.map +1 -1
- package/dist/playkit-sdk.umd.js +1769 -1568
- package/dist/playkit-sdk.umd.js.map +1 -1
- package/package.json +70 -70
package/dist/playkit-sdk.umd.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* playkit-sdk v1.
|
|
2
|
+
* playkit-sdk v1.3.0
|
|
3
3
|
* PlayKit SDK for JavaScript
|
|
4
4
|
* @license SEE LICENSE IN LICENSE
|
|
5
5
|
*/
|
|
6
6
|
(function (global, factory) {
|
|
7
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(
|
|
8
|
-
typeof define === 'function' && define.amd ? define(
|
|
9
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self,
|
|
10
|
-
})(this, (function (
|
|
7
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
|
8
|
+
typeof define === 'function' && define.amd ? define(factory) :
|
|
9
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.PlayKitSDK = factory());
|
|
10
|
+
})(this, (function () { 'use strict';
|
|
11
11
|
|
|
12
12
|
function getDefaultExportFromCjs (x) {
|
|
13
13
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
@@ -467,7 +467,7 @@
|
|
|
467
467
|
* Log level enumeration
|
|
468
468
|
* Lower values = higher priority
|
|
469
469
|
*/
|
|
470
|
-
|
|
470
|
+
var LogLevel;
|
|
471
471
|
(function (LogLevel) {
|
|
472
472
|
/** Disable all logging */
|
|
473
473
|
LogLevel[LogLevel["NONE"] = 0] = "NONE";
|
|
@@ -479,7 +479,7 @@
|
|
|
479
479
|
LogLevel[LogLevel["INFO"] = 3] = "INFO";
|
|
480
480
|
/** Debug level - all logs */
|
|
481
481
|
LogLevel[LogLevel["DEBUG"] = 4] = "DEBUG";
|
|
482
|
-
})(
|
|
482
|
+
})(LogLevel || (LogLevel = {}));
|
|
483
483
|
/**
|
|
484
484
|
* PlayKit Logger
|
|
485
485
|
*
|
|
@@ -603,7 +603,7 @@
|
|
|
603
603
|
* Useful for testing
|
|
604
604
|
*/
|
|
605
605
|
static reset() {
|
|
606
|
-
Logger.globalLevel =
|
|
606
|
+
Logger.globalLevel = LogLevel.WARN;
|
|
607
607
|
Logger.consoleEnabled = true;
|
|
608
608
|
Logger.handlers = [];
|
|
609
609
|
Logger.instances.clear();
|
|
@@ -631,7 +631,7 @@
|
|
|
631
631
|
* @param data Additional data to log
|
|
632
632
|
*/
|
|
633
633
|
debug(message, ...data) {
|
|
634
|
-
this.log(
|
|
634
|
+
this.log(LogLevel.DEBUG, 'debug', message, data);
|
|
635
635
|
}
|
|
636
636
|
/**
|
|
637
637
|
* Log an info message
|
|
@@ -639,7 +639,7 @@
|
|
|
639
639
|
* @param data Additional data to log
|
|
640
640
|
*/
|
|
641
641
|
info(message, ...data) {
|
|
642
|
-
this.log(
|
|
642
|
+
this.log(LogLevel.INFO, 'info', message, data);
|
|
643
643
|
}
|
|
644
644
|
/**
|
|
645
645
|
* Log a warning message
|
|
@@ -647,7 +647,7 @@
|
|
|
647
647
|
* @param data Additional data to log
|
|
648
648
|
*/
|
|
649
649
|
warn(message, ...data) {
|
|
650
|
-
this.log(
|
|
650
|
+
this.log(LogLevel.WARN, 'warn', message, data);
|
|
651
651
|
}
|
|
652
652
|
/**
|
|
653
653
|
* Log an error message
|
|
@@ -655,7 +655,7 @@
|
|
|
655
655
|
* @param data Additional data to log
|
|
656
656
|
*/
|
|
657
657
|
error(message, ...data) {
|
|
658
|
-
this.log(
|
|
658
|
+
this.log(LogLevel.ERROR, 'error', message, data);
|
|
659
659
|
}
|
|
660
660
|
/**
|
|
661
661
|
* Internal log method
|
|
@@ -713,7 +713,7 @@
|
|
|
713
713
|
}
|
|
714
714
|
}
|
|
715
715
|
}
|
|
716
|
-
Logger.globalLevel =
|
|
716
|
+
Logger.globalLevel = LogLevel.WARN;
|
|
717
717
|
Logger.handlers = [];
|
|
718
718
|
Logger.consoleEnabled = true;
|
|
719
719
|
Logger.instances = new Map();
|
|
@@ -1176,6 +1176,15 @@
|
|
|
1176
1176
|
}
|
|
1177
1177
|
}
|
|
1178
1178
|
|
|
1179
|
+
const SDK_TYPE = 'Javascript';
|
|
1180
|
+
const SDK_VERSION = '"1.3.0"';
|
|
1181
|
+
function getSDKHeaders() {
|
|
1182
|
+
return {
|
|
1183
|
+
'X-SDK-Type': SDK_TYPE,
|
|
1184
|
+
'X-SDK-Version': SDK_VERSION,
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1179
1188
|
/**
|
|
1180
1189
|
* Authentication Flow Manager
|
|
1181
1190
|
* Manages the headless authentication flow with automatic UI
|
|
@@ -1280,8 +1289,8 @@
|
|
|
1280
1289
|
constructor(baseURL) {
|
|
1281
1290
|
super();
|
|
1282
1291
|
this.currentSessionId = null;
|
|
1283
|
-
this.
|
|
1284
|
-
this.
|
|
1292
|
+
this._uiContainer = null;
|
|
1293
|
+
this._isSuccess = false;
|
|
1285
1294
|
this.currentLanguage = 'en';
|
|
1286
1295
|
// UI Elements
|
|
1287
1296
|
this.modal = null;
|
|
@@ -1362,84 +1371,84 @@
|
|
|
1362
1371
|
// Create modal container
|
|
1363
1372
|
this.modal = document.createElement('div');
|
|
1364
1373
|
this.modal.className = 'playkit-auth-modal';
|
|
1365
|
-
this.modal.innerHTML = `
|
|
1366
|
-
<div class="playkit-auth-overlay"></div>
|
|
1367
|
-
<div class="playkit-auth-container">
|
|
1368
|
-
<!-- Identifier Panel -->
|
|
1369
|
-
<div class="playkit-auth-panel" id="playkit-identifier-panel">
|
|
1370
|
-
<div class="playkit-auth-header">
|
|
1371
|
-
<h2>${this.t('signIn')}</h2>
|
|
1372
|
-
<p>${this.t('signInSubtitle')}</p>
|
|
1373
|
-
</div>
|
|
1374
|
-
|
|
1375
|
-
<div class="playkit-auth-toggle">
|
|
1376
|
-
<label class="playkit-toggle-option">
|
|
1377
|
-
<input type="radio" name="auth-type" value="email" checked>
|
|
1378
|
-
<span>${this.t('email')}</span>
|
|
1379
|
-
</label>
|
|
1380
|
-
<label class="playkit-toggle-option">
|
|
1381
|
-
<input type="radio" name="auth-type" value="phone">
|
|
1382
|
-
<span>${this.t('phone')}</span>
|
|
1383
|
-
</label>
|
|
1384
|
-
</div>
|
|
1385
|
-
|
|
1386
|
-
<div class="playkit-auth-input-group">
|
|
1387
|
-
<div class="playkit-input-wrapper">
|
|
1388
|
-
<svg class="playkit-input-icon" id="playkit-identifier-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1389
|
-
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
|
|
1390
|
-
<polyline points="22,6 12,13 2,6"></polyline>
|
|
1391
|
-
</svg>
|
|
1392
|
-
<input
|
|
1393
|
-
type="text"
|
|
1394
|
-
id="playkit-identifier-input"
|
|
1395
|
-
placeholder="${this.t('emailPlaceholder')}"
|
|
1396
|
-
autocomplete="off"
|
|
1397
|
-
>
|
|
1398
|
-
</div>
|
|
1399
|
-
</div>
|
|
1400
|
-
|
|
1401
|
-
<button class="playkit-auth-button" id="playkit-send-code-btn">
|
|
1402
|
-
${this.t('sendCode')}
|
|
1403
|
-
</button>
|
|
1404
|
-
|
|
1405
|
-
<div class="playkit-auth-error" id="playkit-error-text"></div>
|
|
1406
|
-
</div>
|
|
1407
|
-
|
|
1408
|
-
<!-- Verification Panel -->
|
|
1409
|
-
<div class="playkit-auth-panel" id="playkit-verification-panel" style="display: none;">
|
|
1410
|
-
<div class="playkit-auth-header">
|
|
1411
|
-
<button class="playkit-back-button" id="playkit-back-btn">
|
|
1412
|
-
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1413
|
-
<path d="M19 12H5M12 19l-7-7 7-7"/>
|
|
1414
|
-
</svg>
|
|
1415
|
-
</button>
|
|
1416
|
-
<h2>${this.t('enterCode')}</h2>
|
|
1417
|
-
<p>${this.t('enterCodeSubtitle')} <span id="playkit-identifier-display"></span></p>
|
|
1418
|
-
</div>
|
|
1419
|
-
|
|
1420
|
-
<div class="playkit-auth-input-group">
|
|
1421
|
-
<div class="playkit-code-inputs">
|
|
1422
|
-
<input type="number" maxlength="1" class="playkit-code-input" data-index="0">
|
|
1423
|
-
<input type="number" maxlength="1" class="playkit-code-input" data-index="1">
|
|
1424
|
-
<input type="number" maxlength="1" class="playkit-code-input" data-index="2">
|
|
1425
|
-
<input type="number" maxlength="1" class="playkit-code-input" data-index="3">
|
|
1426
|
-
<input type="number" maxlength="1" class="playkit-code-input" data-index="4">
|
|
1427
|
-
<input type="number" maxlength="1" class="playkit-code-input" data-index="5">
|
|
1428
|
-
</div>
|
|
1429
|
-
</div>
|
|
1430
|
-
|
|
1431
|
-
<button class="playkit-auth-button" id="playkit-verify-btn">
|
|
1432
|
-
${this.t('verify')}
|
|
1433
|
-
</button>
|
|
1434
|
-
|
|
1435
|
-
<div class="playkit-auth-error" id="playkit-verify-error-text"></div>
|
|
1436
|
-
</div>
|
|
1437
|
-
|
|
1438
|
-
<!-- Loading Overlay -->
|
|
1439
|
-
<div class="playkit-loading-overlay" id="playkit-loading-overlay" style="display: none;">
|
|
1440
|
-
<div class="playkit-spinner"></div>
|
|
1441
|
-
</div>
|
|
1442
|
-
</div>
|
|
1374
|
+
this.modal.innerHTML = `
|
|
1375
|
+
<div class="playkit-auth-overlay"></div>
|
|
1376
|
+
<div class="playkit-auth-container">
|
|
1377
|
+
<!-- Identifier Panel -->
|
|
1378
|
+
<div class="playkit-auth-panel" id="playkit-identifier-panel">
|
|
1379
|
+
<div class="playkit-auth-header">
|
|
1380
|
+
<h2>${this.t('signIn')}</h2>
|
|
1381
|
+
<p>${this.t('signInSubtitle')}</p>
|
|
1382
|
+
</div>
|
|
1383
|
+
|
|
1384
|
+
<div class="playkit-auth-toggle">
|
|
1385
|
+
<label class="playkit-toggle-option">
|
|
1386
|
+
<input type="radio" name="auth-type" value="email" checked>
|
|
1387
|
+
<span>${this.t('email')}</span>
|
|
1388
|
+
</label>
|
|
1389
|
+
<label class="playkit-toggle-option">
|
|
1390
|
+
<input type="radio" name="auth-type" value="phone">
|
|
1391
|
+
<span>${this.t('phone')}</span>
|
|
1392
|
+
</label>
|
|
1393
|
+
</div>
|
|
1394
|
+
|
|
1395
|
+
<div class="playkit-auth-input-group">
|
|
1396
|
+
<div class="playkit-input-wrapper">
|
|
1397
|
+
<svg class="playkit-input-icon" id="playkit-identifier-icon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1398
|
+
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
|
|
1399
|
+
<polyline points="22,6 12,13 2,6"></polyline>
|
|
1400
|
+
</svg>
|
|
1401
|
+
<input
|
|
1402
|
+
type="text"
|
|
1403
|
+
id="playkit-identifier-input"
|
|
1404
|
+
placeholder="${this.t('emailPlaceholder')}"
|
|
1405
|
+
autocomplete="off"
|
|
1406
|
+
>
|
|
1407
|
+
</div>
|
|
1408
|
+
</div>
|
|
1409
|
+
|
|
1410
|
+
<button class="playkit-auth-button" id="playkit-send-code-btn">
|
|
1411
|
+
${this.t('sendCode')}
|
|
1412
|
+
</button>
|
|
1413
|
+
|
|
1414
|
+
<div class="playkit-auth-error" id="playkit-error-text"></div>
|
|
1415
|
+
</div>
|
|
1416
|
+
|
|
1417
|
+
<!-- Verification Panel -->
|
|
1418
|
+
<div class="playkit-auth-panel" id="playkit-verification-panel" style="display: none;">
|
|
1419
|
+
<div class="playkit-auth-header">
|
|
1420
|
+
<button class="playkit-back-button" id="playkit-back-btn">
|
|
1421
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
1422
|
+
<path d="M19 12H5M12 19l-7-7 7-7"/>
|
|
1423
|
+
</svg>
|
|
1424
|
+
</button>
|
|
1425
|
+
<h2>${this.t('enterCode')}</h2>
|
|
1426
|
+
<p>${this.t('enterCodeSubtitle')} <span id="playkit-identifier-display"></span></p>
|
|
1427
|
+
</div>
|
|
1428
|
+
|
|
1429
|
+
<div class="playkit-auth-input-group">
|
|
1430
|
+
<div class="playkit-code-inputs">
|
|
1431
|
+
<input type="number" maxlength="1" class="playkit-code-input" data-index="0">
|
|
1432
|
+
<input type="number" maxlength="1" class="playkit-code-input" data-index="1">
|
|
1433
|
+
<input type="number" maxlength="1" class="playkit-code-input" data-index="2">
|
|
1434
|
+
<input type="number" maxlength="1" class="playkit-code-input" data-index="3">
|
|
1435
|
+
<input type="number" maxlength="1" class="playkit-code-input" data-index="4">
|
|
1436
|
+
<input type="number" maxlength="1" class="playkit-code-input" data-index="5">
|
|
1437
|
+
</div>
|
|
1438
|
+
</div>
|
|
1439
|
+
|
|
1440
|
+
<button class="playkit-auth-button" id="playkit-verify-btn">
|
|
1441
|
+
${this.t('verify')}
|
|
1442
|
+
</button>
|
|
1443
|
+
|
|
1444
|
+
<div class="playkit-auth-error" id="playkit-verify-error-text"></div>
|
|
1445
|
+
</div>
|
|
1446
|
+
|
|
1447
|
+
<!-- Loading Overlay -->
|
|
1448
|
+
<div class="playkit-loading-overlay" id="playkit-loading-overlay" style="display: none;">
|
|
1449
|
+
<div class="playkit-spinner"></div>
|
|
1450
|
+
</div>
|
|
1451
|
+
</div>
|
|
1443
1452
|
`;
|
|
1444
1453
|
// Add styles and load VanillaOTP
|
|
1445
1454
|
this.addStyles();
|
|
@@ -1474,274 +1483,274 @@
|
|
|
1474
1483
|
return;
|
|
1475
1484
|
const style = document.createElement('style');
|
|
1476
1485
|
style.id = styleId;
|
|
1477
|
-
style.textContent = `
|
|
1478
|
-
.playkit-auth-modal {
|
|
1479
|
-
position: fixed;
|
|
1480
|
-
top: 0;
|
|
1481
|
-
left: 0;
|
|
1482
|
-
right: 0;
|
|
1483
|
-
bottom: 0;
|
|
1484
|
-
z-index: 999999;
|
|
1485
|
-
display: flex;
|
|
1486
|
-
justify-content: center;
|
|
1487
|
-
align-items: center;
|
|
1488
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
1489
|
-
}
|
|
1490
|
-
|
|
1491
|
-
.playkit-auth-overlay {
|
|
1492
|
-
position: absolute;
|
|
1493
|
-
top: 0;
|
|
1494
|
-
left: 0;
|
|
1495
|
-
right: 0;
|
|
1496
|
-
bottom: 0;
|
|
1497
|
-
background: rgba(0, 0, 0, 0.8);
|
|
1498
|
-
}
|
|
1499
|
-
|
|
1500
|
-
.playkit-auth-container {
|
|
1501
|
-
position: relative;
|
|
1502
|
-
background: #fff;
|
|
1503
|
-
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
1504
|
-
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.05);
|
|
1505
|
-
width: 90%;
|
|
1506
|
-
max-width: 320px;
|
|
1507
|
-
overflow: hidden;
|
|
1508
|
-
}
|
|
1509
|
-
|
|
1510
|
-
.playkit-auth-panel {
|
|
1511
|
-
padding: 24px;
|
|
1512
|
-
}
|
|
1513
|
-
|
|
1514
|
-
.playkit-auth-header {
|
|
1515
|
-
text-align: center;
|
|
1516
|
-
margin-bottom: 20px;
|
|
1517
|
-
position: relative;
|
|
1518
|
-
}
|
|
1519
|
-
|
|
1520
|
-
.playkit-auth-header h2 {
|
|
1521
|
-
margin: 0 0 8px 0;
|
|
1522
|
-
font-size: 14px;
|
|
1523
|
-
font-weight: 600;
|
|
1524
|
-
color: #171717;
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
|
-
.playkit-auth-header p {
|
|
1528
|
-
margin: 0;
|
|
1529
|
-
font-size: 14px;
|
|
1530
|
-
color: #666;
|
|
1531
|
-
line-height: 1.5;
|
|
1532
|
-
}
|
|
1533
|
-
|
|
1534
|
-
.playkit-back-button {
|
|
1535
|
-
position: absolute;
|
|
1536
|
-
left: 0;
|
|
1537
|
-
top: 0;
|
|
1538
|
-
background: transparent;
|
|
1539
|
-
border: none;
|
|
1540
|
-
cursor: pointer;
|
|
1541
|
-
padding: 4px;
|
|
1542
|
-
color: #666;
|
|
1543
|
-
transition: background-color 0.2s ease, color 0.2s ease;
|
|
1544
|
-
}
|
|
1545
|
-
|
|
1546
|
-
.playkit-back-button:hover {
|
|
1547
|
-
background: #f5f5f5;
|
|
1548
|
-
color: #171717;
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
|
-
.playkit-auth-toggle {
|
|
1552
|
-
display: flex;
|
|
1553
|
-
background: #f5f5f5;
|
|
1554
|
-
padding: 2px;
|
|
1555
|
-
margin-bottom: 20px;
|
|
1556
|
-
gap: 2px;
|
|
1557
|
-
}
|
|
1558
|
-
|
|
1559
|
-
.playkit-toggle-option {
|
|
1560
|
-
flex: 1;
|
|
1561
|
-
display: flex;
|
|
1562
|
-
justify-content: center;
|
|
1563
|
-
align-items: center;
|
|
1564
|
-
padding: 10px 16px;
|
|
1565
|
-
cursor: pointer;
|
|
1566
|
-
transition: background-color 0.2s ease;
|
|
1567
|
-
}
|
|
1568
|
-
|
|
1569
|
-
.playkit-toggle-option input {
|
|
1570
|
-
display: none;
|
|
1571
|
-
}
|
|
1572
|
-
|
|
1573
|
-
.playkit-toggle-option span {
|
|
1574
|
-
font-size: 14px;
|
|
1575
|
-
font-weight: 500;
|
|
1576
|
-
color: #666;
|
|
1577
|
-
transition: color 0.2s ease;
|
|
1578
|
-
}
|
|
1579
|
-
|
|
1580
|
-
.playkit-toggle-option input:checked + span {
|
|
1581
|
-
color: #fff;
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
.playkit-toggle-option:has(input:checked) {
|
|
1585
|
-
background: #171717;
|
|
1586
|
-
}
|
|
1587
|
-
|
|
1588
|
-
.playkit-auth-input-group {
|
|
1589
|
-
margin-bottom: 20px;
|
|
1590
|
-
}
|
|
1591
|
-
|
|
1592
|
-
.playkit-input-wrapper {
|
|
1593
|
-
position: relative;
|
|
1594
|
-
display: flex;
|
|
1595
|
-
align-items: center;
|
|
1596
|
-
}
|
|
1597
|
-
|
|
1598
|
-
.playkit-input-icon {
|
|
1599
|
-
position: absolute;
|
|
1600
|
-
left: 12px;
|
|
1601
|
-
color: #999;
|
|
1602
|
-
pointer-events: none;
|
|
1603
|
-
}
|
|
1604
|
-
|
|
1605
|
-
.playkit-input-wrapper input {
|
|
1606
|
-
width: 100%;
|
|
1607
|
-
padding: 10px 12px 10px 44px;
|
|
1608
|
-
border: 1px solid #e5e7eb;
|
|
1609
|
-
font-size: 14px;
|
|
1610
|
-
transition: border-color 0.2s ease;
|
|
1611
|
-
box-sizing: border-box;
|
|
1612
|
-
background: #fff;
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
|
-
.playkit-input-wrapper input:hover {
|
|
1616
|
-
border-color: #d4d4d4;
|
|
1617
|
-
}
|
|
1618
|
-
|
|
1619
|
-
.playkit-input-wrapper input:focus {
|
|
1620
|
-
outline: none;
|
|
1621
|
-
border-color: #171717;
|
|
1622
|
-
}
|
|
1623
|
-
|
|
1624
|
-
.playkit-code-inputs {
|
|
1625
|
-
display: flex;
|
|
1626
|
-
gap: 8px;
|
|
1627
|
-
justify-content: center;
|
|
1628
|
-
}
|
|
1629
|
-
|
|
1630
|
-
.playkit-code-input {
|
|
1631
|
-
width: 40px !important;
|
|
1632
|
-
height: 48px;
|
|
1633
|
-
text-align: center;
|
|
1634
|
-
font-size: 20px;
|
|
1635
|
-
font-weight: 600;
|
|
1636
|
-
border: 1px solid #e5e7eb !important;
|
|
1637
|
-
padding: 0 !important;
|
|
1638
|
-
transition: border-color 0.2s ease;
|
|
1639
|
-
background: #fff;
|
|
1640
|
-
-moz-appearance: textfield;
|
|
1641
|
-
}
|
|
1642
|
-
|
|
1643
|
-
.playkit-code-input::-webkit-outer-spin-button,
|
|
1644
|
-
.playkit-code-input::-webkit-inner-spin-button {
|
|
1645
|
-
-webkit-appearance: none;
|
|
1646
|
-
margin: 0;
|
|
1647
|
-
}
|
|
1648
|
-
|
|
1649
|
-
.playkit-code-input:hover {
|
|
1650
|
-
border-color: #d4d4d4 !important;
|
|
1651
|
-
}
|
|
1652
|
-
|
|
1653
|
-
.playkit-code-input:focus {
|
|
1654
|
-
outline: none;
|
|
1655
|
-
border-color: #171717 !important;
|
|
1656
|
-
}
|
|
1657
|
-
|
|
1658
|
-
.playkit-auth-button {
|
|
1659
|
-
width: 100%;
|
|
1660
|
-
padding: 10px 16px;
|
|
1661
|
-
background: #171717;
|
|
1662
|
-
color: white;
|
|
1663
|
-
border: none;
|
|
1664
|
-
font-size: 14px;
|
|
1665
|
-
font-weight: 500;
|
|
1666
|
-
cursor: pointer;
|
|
1667
|
-
transition: background 0.2s ease;
|
|
1668
|
-
}
|
|
1669
|
-
|
|
1670
|
-
.playkit-auth-button:hover:not(:disabled) {
|
|
1671
|
-
background: #404040;
|
|
1672
|
-
}
|
|
1673
|
-
|
|
1674
|
-
.playkit-auth-button:active:not(:disabled) {
|
|
1675
|
-
background: #0a0a0a;
|
|
1676
|
-
}
|
|
1677
|
-
|
|
1678
|
-
.playkit-auth-button:disabled {
|
|
1679
|
-
background: #e5e7eb;
|
|
1680
|
-
color: #999;
|
|
1681
|
-
cursor: not-allowed;
|
|
1682
|
-
}
|
|
1683
|
-
|
|
1684
|
-
.playkit-auth-error {
|
|
1685
|
-
margin-top: 16px;
|
|
1686
|
-
padding: 12px 16px;
|
|
1687
|
-
background: #fef2f2;
|
|
1688
|
-
border: 1px solid #fecaca;
|
|
1689
|
-
color: #dc2626;
|
|
1690
|
-
font-size: 13px;
|
|
1691
|
-
text-align: left;
|
|
1692
|
-
display: none;
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1695
|
-
.playkit-auth-error.show {
|
|
1696
|
-
display: block;
|
|
1697
|
-
}
|
|
1698
|
-
|
|
1699
|
-
.playkit-loading-overlay {
|
|
1700
|
-
position: absolute;
|
|
1701
|
-
top: 0;
|
|
1702
|
-
left: 0;
|
|
1703
|
-
right: 0;
|
|
1704
|
-
bottom: 0;
|
|
1705
|
-
background: rgba(255, 255, 255, 0.96);
|
|
1706
|
-
display: flex;
|
|
1707
|
-
justify-content: center;
|
|
1708
|
-
align-items: center;
|
|
1709
|
-
}
|
|
1710
|
-
|
|
1711
|
-
.playkit-spinner {
|
|
1712
|
-
width: 24px;
|
|
1713
|
-
height: 24px;
|
|
1714
|
-
border: 2px solid #e5e7eb;
|
|
1715
|
-
border-top: 2px solid #171717;
|
|
1716
|
-
border-radius: 50%;
|
|
1717
|
-
animation: playkit-spin 1s linear infinite;
|
|
1718
|
-
}
|
|
1719
|
-
|
|
1720
|
-
@keyframes playkit-spin {
|
|
1721
|
-
0% { transform: rotate(0deg); }
|
|
1722
|
-
100% { transform: rotate(360deg); }
|
|
1723
|
-
}
|
|
1724
|
-
|
|
1725
|
-
@media (max-width: 480px) {
|
|
1726
|
-
.playkit-auth-container {
|
|
1727
|
-
width: 95%;
|
|
1728
|
-
max-width: none;
|
|
1729
|
-
}
|
|
1730
|
-
|
|
1731
|
-
.playkit-auth-panel {
|
|
1732
|
-
padding: 20px;
|
|
1733
|
-
}
|
|
1734
|
-
|
|
1735
|
-
.playkit-code-input {
|
|
1736
|
-
width: 36px !important;
|
|
1737
|
-
height: 44px;
|
|
1738
|
-
font-size: 18px;
|
|
1739
|
-
}
|
|
1740
|
-
|
|
1741
|
-
.playkit-code-inputs {
|
|
1742
|
-
gap: 6px;
|
|
1743
|
-
}
|
|
1744
|
-
}
|
|
1486
|
+
style.textContent = `
|
|
1487
|
+
.playkit-auth-modal {
|
|
1488
|
+
position: fixed;
|
|
1489
|
+
top: 0;
|
|
1490
|
+
left: 0;
|
|
1491
|
+
right: 0;
|
|
1492
|
+
bottom: 0;
|
|
1493
|
+
z-index: 999999;
|
|
1494
|
+
display: flex;
|
|
1495
|
+
justify-content: center;
|
|
1496
|
+
align-items: center;
|
|
1497
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
.playkit-auth-overlay {
|
|
1501
|
+
position: absolute;
|
|
1502
|
+
top: 0;
|
|
1503
|
+
left: 0;
|
|
1504
|
+
right: 0;
|
|
1505
|
+
bottom: 0;
|
|
1506
|
+
background: rgba(0, 0, 0, 0.8);
|
|
1507
|
+
}
|
|
1508
|
+
|
|
1509
|
+
.playkit-auth-container {
|
|
1510
|
+
position: relative;
|
|
1511
|
+
background: #fff;
|
|
1512
|
+
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
1513
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.05);
|
|
1514
|
+
width: 90%;
|
|
1515
|
+
max-width: 320px;
|
|
1516
|
+
overflow: hidden;
|
|
1517
|
+
}
|
|
1518
|
+
|
|
1519
|
+
.playkit-auth-panel {
|
|
1520
|
+
padding: 24px;
|
|
1521
|
+
}
|
|
1522
|
+
|
|
1523
|
+
.playkit-auth-header {
|
|
1524
|
+
text-align: center;
|
|
1525
|
+
margin-bottom: 20px;
|
|
1526
|
+
position: relative;
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
.playkit-auth-header h2 {
|
|
1530
|
+
margin: 0 0 8px 0;
|
|
1531
|
+
font-size: 14px;
|
|
1532
|
+
font-weight: 600;
|
|
1533
|
+
color: #171717;
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
.playkit-auth-header p {
|
|
1537
|
+
margin: 0;
|
|
1538
|
+
font-size: 14px;
|
|
1539
|
+
color: #666;
|
|
1540
|
+
line-height: 1.5;
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
.playkit-back-button {
|
|
1544
|
+
position: absolute;
|
|
1545
|
+
left: 0;
|
|
1546
|
+
top: 0;
|
|
1547
|
+
background: transparent;
|
|
1548
|
+
border: none;
|
|
1549
|
+
cursor: pointer;
|
|
1550
|
+
padding: 4px;
|
|
1551
|
+
color: #666;
|
|
1552
|
+
transition: background-color 0.2s ease, color 0.2s ease;
|
|
1553
|
+
}
|
|
1554
|
+
|
|
1555
|
+
.playkit-back-button:hover {
|
|
1556
|
+
background: #f5f5f5;
|
|
1557
|
+
color: #171717;
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
.playkit-auth-toggle {
|
|
1561
|
+
display: flex;
|
|
1562
|
+
background: #f5f5f5;
|
|
1563
|
+
padding: 2px;
|
|
1564
|
+
margin-bottom: 20px;
|
|
1565
|
+
gap: 2px;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
.playkit-toggle-option {
|
|
1569
|
+
flex: 1;
|
|
1570
|
+
display: flex;
|
|
1571
|
+
justify-content: center;
|
|
1572
|
+
align-items: center;
|
|
1573
|
+
padding: 10px 16px;
|
|
1574
|
+
cursor: pointer;
|
|
1575
|
+
transition: background-color 0.2s ease;
|
|
1576
|
+
}
|
|
1577
|
+
|
|
1578
|
+
.playkit-toggle-option input {
|
|
1579
|
+
display: none;
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
.playkit-toggle-option span {
|
|
1583
|
+
font-size: 14px;
|
|
1584
|
+
font-weight: 500;
|
|
1585
|
+
color: #666;
|
|
1586
|
+
transition: color 0.2s ease;
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
.playkit-toggle-option input:checked + span {
|
|
1590
|
+
color: #fff;
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
.playkit-toggle-option:has(input:checked) {
|
|
1594
|
+
background: #171717;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
.playkit-auth-input-group {
|
|
1598
|
+
margin-bottom: 20px;
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
.playkit-input-wrapper {
|
|
1602
|
+
position: relative;
|
|
1603
|
+
display: flex;
|
|
1604
|
+
align-items: center;
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
.playkit-input-icon {
|
|
1608
|
+
position: absolute;
|
|
1609
|
+
left: 12px;
|
|
1610
|
+
color: #999;
|
|
1611
|
+
pointer-events: none;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
.playkit-input-wrapper input {
|
|
1615
|
+
width: 100%;
|
|
1616
|
+
padding: 10px 12px 10px 44px;
|
|
1617
|
+
border: 1px solid #e5e7eb;
|
|
1618
|
+
font-size: 14px;
|
|
1619
|
+
transition: border-color 0.2s ease;
|
|
1620
|
+
box-sizing: border-box;
|
|
1621
|
+
background: #fff;
|
|
1622
|
+
}
|
|
1623
|
+
|
|
1624
|
+
.playkit-input-wrapper input:hover {
|
|
1625
|
+
border-color: #d4d4d4;
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1628
|
+
.playkit-input-wrapper input:focus {
|
|
1629
|
+
outline: none;
|
|
1630
|
+
border-color: #171717;
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
.playkit-code-inputs {
|
|
1634
|
+
display: flex;
|
|
1635
|
+
gap: 8px;
|
|
1636
|
+
justify-content: center;
|
|
1637
|
+
}
|
|
1638
|
+
|
|
1639
|
+
.playkit-code-input {
|
|
1640
|
+
width: 40px !important;
|
|
1641
|
+
height: 48px;
|
|
1642
|
+
text-align: center;
|
|
1643
|
+
font-size: 20px;
|
|
1644
|
+
font-weight: 600;
|
|
1645
|
+
border: 1px solid #e5e7eb !important;
|
|
1646
|
+
padding: 0 !important;
|
|
1647
|
+
transition: border-color 0.2s ease;
|
|
1648
|
+
background: #fff;
|
|
1649
|
+
-moz-appearance: textfield;
|
|
1650
|
+
}
|
|
1651
|
+
|
|
1652
|
+
.playkit-code-input::-webkit-outer-spin-button,
|
|
1653
|
+
.playkit-code-input::-webkit-inner-spin-button {
|
|
1654
|
+
-webkit-appearance: none;
|
|
1655
|
+
margin: 0;
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1658
|
+
.playkit-code-input:hover {
|
|
1659
|
+
border-color: #d4d4d4 !important;
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
.playkit-code-input:focus {
|
|
1663
|
+
outline: none;
|
|
1664
|
+
border-color: #171717 !important;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
.playkit-auth-button {
|
|
1668
|
+
width: 100%;
|
|
1669
|
+
padding: 10px 16px;
|
|
1670
|
+
background: #171717;
|
|
1671
|
+
color: white;
|
|
1672
|
+
border: none;
|
|
1673
|
+
font-size: 14px;
|
|
1674
|
+
font-weight: 500;
|
|
1675
|
+
cursor: pointer;
|
|
1676
|
+
transition: background 0.2s ease;
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
.playkit-auth-button:hover:not(:disabled) {
|
|
1680
|
+
background: #404040;
|
|
1681
|
+
}
|
|
1682
|
+
|
|
1683
|
+
.playkit-auth-button:active:not(:disabled) {
|
|
1684
|
+
background: #0a0a0a;
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
.playkit-auth-button:disabled {
|
|
1688
|
+
background: #e5e7eb;
|
|
1689
|
+
color: #999;
|
|
1690
|
+
cursor: not-allowed;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
.playkit-auth-error {
|
|
1694
|
+
margin-top: 16px;
|
|
1695
|
+
padding: 12px 16px;
|
|
1696
|
+
background: #fef2f2;
|
|
1697
|
+
border: 1px solid #fecaca;
|
|
1698
|
+
color: #dc2626;
|
|
1699
|
+
font-size: 13px;
|
|
1700
|
+
text-align: left;
|
|
1701
|
+
display: none;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
.playkit-auth-error.show {
|
|
1705
|
+
display: block;
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
.playkit-loading-overlay {
|
|
1709
|
+
position: absolute;
|
|
1710
|
+
top: 0;
|
|
1711
|
+
left: 0;
|
|
1712
|
+
right: 0;
|
|
1713
|
+
bottom: 0;
|
|
1714
|
+
background: rgba(255, 255, 255, 0.96);
|
|
1715
|
+
display: flex;
|
|
1716
|
+
justify-content: center;
|
|
1717
|
+
align-items: center;
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
.playkit-spinner {
|
|
1721
|
+
width: 24px;
|
|
1722
|
+
height: 24px;
|
|
1723
|
+
border: 2px solid #e5e7eb;
|
|
1724
|
+
border-top: 2px solid #171717;
|
|
1725
|
+
border-radius: 50%;
|
|
1726
|
+
animation: playkit-spin 1s linear infinite;
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
@keyframes playkit-spin {
|
|
1730
|
+
0% { transform: rotate(0deg); }
|
|
1731
|
+
100% { transform: rotate(360deg); }
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
@media (max-width: 480px) {
|
|
1735
|
+
.playkit-auth-container {
|
|
1736
|
+
width: 95%;
|
|
1737
|
+
max-width: none;
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
.playkit-auth-panel {
|
|
1741
|
+
padding: 20px;
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
.playkit-code-input {
|
|
1745
|
+
width: 36px !important;
|
|
1746
|
+
height: 44px;
|
|
1747
|
+
font-size: 18px;
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
.playkit-code-inputs {
|
|
1751
|
+
gap: 6px;
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1745
1754
|
`;
|
|
1746
1755
|
document.head.appendChild(style);
|
|
1747
1756
|
}
|
|
@@ -1762,14 +1771,14 @@
|
|
|
1762
1771
|
: this.t('phonePlaceholder');
|
|
1763
1772
|
// Update icon
|
|
1764
1773
|
if (isEmail) {
|
|
1765
|
-
identifierIcon.innerHTML = `
|
|
1766
|
-
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
|
|
1767
|
-
<polyline points="22,6 12,13 2,6"></polyline>
|
|
1774
|
+
identifierIcon.innerHTML = `
|
|
1775
|
+
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z"></path>
|
|
1776
|
+
<polyline points="22,6 12,13 2,6"></polyline>
|
|
1768
1777
|
`;
|
|
1769
1778
|
}
|
|
1770
1779
|
else {
|
|
1771
|
-
identifierIcon.innerHTML = `
|
|
1772
|
-
<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"></path>
|
|
1780
|
+
identifierIcon.innerHTML = `
|
|
1781
|
+
<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7A2 2 0 0 1 22 16.92z"></path>
|
|
1773
1782
|
`;
|
|
1774
1783
|
}
|
|
1775
1784
|
};
|
|
@@ -1790,7 +1799,7 @@
|
|
|
1790
1799
|
this.otpInstance = new window.VanillaOTP(codeInputsContainer);
|
|
1791
1800
|
// Auto-submit when all 6 digits entered
|
|
1792
1801
|
const codeInputs = (_d = this.modal) === null || _d === void 0 ? void 0 : _d.querySelectorAll('.playkit-code-input');
|
|
1793
|
-
codeInputs === null || codeInputs === void 0 ? void 0 : codeInputs.forEach((input,
|
|
1802
|
+
codeInputs === null || codeInputs === void 0 ? void 0 : codeInputs.forEach((input, _index) => {
|
|
1794
1803
|
input.addEventListener('input', () => {
|
|
1795
1804
|
// Check if all inputs are filled
|
|
1796
1805
|
const allFilled = Array.from(codeInputs).every(inp => inp.value.length === 1);
|
|
@@ -1882,7 +1891,7 @@
|
|
|
1882
1891
|
async sendVerificationCode(identifier, type) {
|
|
1883
1892
|
const response = await fetch(`${this.baseURL}/api/auth/send-code`, {
|
|
1884
1893
|
method: 'POST',
|
|
1885
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1894
|
+
headers: Object.assign({ 'Content-Type': 'application/json' }, getSDKHeaders()),
|
|
1886
1895
|
body: JSON.stringify({ identifier, type }),
|
|
1887
1896
|
});
|
|
1888
1897
|
if (!response.ok) {
|
|
@@ -1904,7 +1913,7 @@
|
|
|
1904
1913
|
}
|
|
1905
1914
|
const response = await fetch(`${this.baseURL}/api/auth/verify-code`, {
|
|
1906
1915
|
method: 'POST',
|
|
1907
|
-
headers: { 'Content-Type': 'application/json' },
|
|
1916
|
+
headers: Object.assign({ 'Content-Type': 'application/json' }, getSDKHeaders()),
|
|
1908
1917
|
body: JSON.stringify({
|
|
1909
1918
|
sessionId: this.currentSessionId,
|
|
1910
1919
|
code,
|
|
@@ -1925,7 +1934,9 @@
|
|
|
1925
1934
|
async setDefaultAuthTypeByRegion() {
|
|
1926
1935
|
var _a;
|
|
1927
1936
|
try {
|
|
1928
|
-
const response = await fetch(`${this.baseURL}/api/reachability
|
|
1937
|
+
const response = await fetch(`${this.baseURL}/api/reachability`, {
|
|
1938
|
+
headers: Object.assign({}, getSDKHeaders()),
|
|
1939
|
+
});
|
|
1929
1940
|
if (response.ok) {
|
|
1930
1941
|
const data = await response.json();
|
|
1931
1942
|
if (data.region === 'CN') {
|
|
@@ -2182,76 +2193,76 @@
|
|
|
2182
2193
|
// Create modal overlay - dark bg-black/80 style
|
|
2183
2194
|
const overlay = document.createElement('div');
|
|
2184
2195
|
overlay.id = 'playkit-login-modal';
|
|
2185
|
-
overlay.style.cssText = `
|
|
2186
|
-
position: fixed;
|
|
2187
|
-
top: 0;
|
|
2188
|
-
left: 0;
|
|
2189
|
-
right: 0;
|
|
2190
|
-
bottom: 0;
|
|
2191
|
-
background: rgba(0, 0, 0, 0.8);
|
|
2192
|
-
display: flex;
|
|
2193
|
-
align-items: center;
|
|
2194
|
-
justify-content: center;
|
|
2195
|
-
z-index: 999999;
|
|
2196
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
2196
|
+
overlay.style.cssText = `
|
|
2197
|
+
position: fixed;
|
|
2198
|
+
top: 0;
|
|
2199
|
+
left: 0;
|
|
2200
|
+
right: 0;
|
|
2201
|
+
bottom: 0;
|
|
2202
|
+
background: rgba(0, 0, 0, 0.8);
|
|
2203
|
+
display: flex;
|
|
2204
|
+
align-items: center;
|
|
2205
|
+
justify-content: center;
|
|
2206
|
+
z-index: 999999;
|
|
2207
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
|
2197
2208
|
`;
|
|
2198
2209
|
// Create modal card - square corners, shadow-xl style
|
|
2199
2210
|
const card = document.createElement('div');
|
|
2200
|
-
card.style.cssText = `
|
|
2201
|
-
background: #fff;
|
|
2202
|
-
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
2203
|
-
padding: 24px;
|
|
2204
|
-
max-width: 320px;
|
|
2205
|
-
width: 90%;
|
|
2206
|
-
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.05);
|
|
2207
|
-
text-align: center;
|
|
2211
|
+
card.style.cssText = `
|
|
2212
|
+
background: #fff;
|
|
2213
|
+
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
2214
|
+
padding: 24px;
|
|
2215
|
+
max-width: 320px;
|
|
2216
|
+
width: 90%;
|
|
2217
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.05);
|
|
2218
|
+
text-align: center;
|
|
2208
2219
|
`;
|
|
2209
2220
|
// Subtitle / status text
|
|
2210
2221
|
const subtitle = document.createElement('p');
|
|
2211
2222
|
subtitle.id = 'playkit-modal-subtitle';
|
|
2212
2223
|
subtitle.textContent = this.t('loginWithPlayKit');
|
|
2213
|
-
subtitle.style.cssText = `
|
|
2214
|
-
margin: 0 0 20px;
|
|
2215
|
-
font-size: 14px;
|
|
2216
|
-
color: #666;
|
|
2224
|
+
subtitle.style.cssText = `
|
|
2225
|
+
margin: 0 0 20px;
|
|
2226
|
+
font-size: 14px;
|
|
2227
|
+
color: #666;
|
|
2217
2228
|
`;
|
|
2218
2229
|
card.appendChild(subtitle);
|
|
2219
2230
|
// Loading spinner (hidden initially)
|
|
2220
2231
|
const spinner = document.createElement('div');
|
|
2221
2232
|
spinner.id = 'playkit-modal-spinner';
|
|
2222
|
-
spinner.style.cssText = `
|
|
2223
|
-
display: none;
|
|
2224
|
-
width: 24px;
|
|
2225
|
-
height: 24px;
|
|
2226
|
-
margin: 0 auto 16px;
|
|
2227
|
-
border: 2px solid #e5e7eb;
|
|
2228
|
-
border-top-color: #171717;
|
|
2229
|
-
border-radius: 50%;
|
|
2230
|
-
animation: playkit-spin 1s linear infinite;
|
|
2233
|
+
spinner.style.cssText = `
|
|
2234
|
+
display: none;
|
|
2235
|
+
width: 24px;
|
|
2236
|
+
height: 24px;
|
|
2237
|
+
margin: 0 auto 16px;
|
|
2238
|
+
border: 2px solid #e5e7eb;
|
|
2239
|
+
border-top-color: #171717;
|
|
2240
|
+
border-radius: 50%;
|
|
2241
|
+
animation: playkit-spin 1s linear infinite;
|
|
2231
2242
|
`;
|
|
2232
2243
|
card.appendChild(spinner);
|
|
2233
2244
|
// Add keyframes for spinner
|
|
2234
2245
|
const style = document.createElement('style');
|
|
2235
|
-
style.textContent = `
|
|
2236
|
-
@keyframes playkit-spin {
|
|
2237
|
-
to { transform: rotate(360deg); }
|
|
2238
|
-
}
|
|
2246
|
+
style.textContent = `
|
|
2247
|
+
@keyframes playkit-spin {
|
|
2248
|
+
to { transform: rotate(360deg); }
|
|
2249
|
+
}
|
|
2239
2250
|
`;
|
|
2240
2251
|
document.head.appendChild(style);
|
|
2241
2252
|
// Login button - square corners, simple dark style
|
|
2242
2253
|
const loginBtn = document.createElement('button');
|
|
2243
2254
|
loginBtn.id = 'playkit-modal-login-btn';
|
|
2244
2255
|
loginBtn.textContent = this.t('loginToPlay');
|
|
2245
|
-
loginBtn.style.cssText = `
|
|
2246
|
-
width: 100%;
|
|
2247
|
-
padding: 10px 16px;
|
|
2248
|
-
font-size: 14px;
|
|
2249
|
-
font-weight: 500;
|
|
2250
|
-
color: white;
|
|
2251
|
-
background: #171717;
|
|
2252
|
-
border: none;
|
|
2253
|
-
cursor: pointer;
|
|
2254
|
-
transition: background 0.2s ease;
|
|
2256
|
+
loginBtn.style.cssText = `
|
|
2257
|
+
width: 100%;
|
|
2258
|
+
padding: 10px 16px;
|
|
2259
|
+
font-size: 14px;
|
|
2260
|
+
font-weight: 500;
|
|
2261
|
+
color: white;
|
|
2262
|
+
background: #171717;
|
|
2263
|
+
border: none;
|
|
2264
|
+
cursor: pointer;
|
|
2265
|
+
transition: background 0.2s ease;
|
|
2255
2266
|
`;
|
|
2256
2267
|
loginBtn.onmouseenter = () => {
|
|
2257
2268
|
loginBtn.style.background = '#404040';
|
|
@@ -2278,18 +2289,18 @@
|
|
|
2278
2289
|
const cancelBtn = document.createElement('button');
|
|
2279
2290
|
cancelBtn.id = 'playkit-modal-cancel-btn';
|
|
2280
2291
|
cancelBtn.textContent = this.t('cancel');
|
|
2281
|
-
cancelBtn.style.cssText = `
|
|
2282
|
-
display: none;
|
|
2283
|
-
width: 100%;
|
|
2284
|
-
margin-top: 8px;
|
|
2285
|
-
padding: 10px 16px;
|
|
2286
|
-
font-size: 14px;
|
|
2287
|
-
font-weight: 500;
|
|
2288
|
-
color: #666;
|
|
2289
|
-
background: transparent;
|
|
2290
|
-
border: 1px solid #e5e7eb;
|
|
2291
|
-
cursor: pointer;
|
|
2292
|
-
transition: all 0.2s ease;
|
|
2292
|
+
cancelBtn.style.cssText = `
|
|
2293
|
+
display: none;
|
|
2294
|
+
width: 100%;
|
|
2295
|
+
margin-top: 8px;
|
|
2296
|
+
padding: 10px 16px;
|
|
2297
|
+
font-size: 14px;
|
|
2298
|
+
font-weight: 500;
|
|
2299
|
+
color: #666;
|
|
2300
|
+
background: transparent;
|
|
2301
|
+
border: 1px solid #e5e7eb;
|
|
2302
|
+
cursor: pointer;
|
|
2303
|
+
transition: all 0.2s ease;
|
|
2293
2304
|
`;
|
|
2294
2305
|
cancelBtn.onmouseenter = () => {
|
|
2295
2306
|
cancelBtn.style.background = '#f5f5f5';
|
|
@@ -2359,11 +2370,11 @@
|
|
|
2359
2370
|
// Create error title
|
|
2360
2371
|
const errorTitle = document.createElement('h3');
|
|
2361
2372
|
errorTitle.textContent = this.t(titleKey);
|
|
2362
|
-
errorTitle.style.cssText = `
|
|
2363
|
-
margin: 0 0 8px;
|
|
2364
|
-
font-size: 14px;
|
|
2365
|
-
font-weight: 600;
|
|
2366
|
-
color: ${iconColor};
|
|
2373
|
+
errorTitle.style.cssText = `
|
|
2374
|
+
margin: 0 0 8px;
|
|
2375
|
+
font-size: 14px;
|
|
2376
|
+
font-weight: 600;
|
|
2377
|
+
color: ${iconColor};
|
|
2367
2378
|
`;
|
|
2368
2379
|
// Update subtitle with error description
|
|
2369
2380
|
subtitle.textContent = this.t(descKey);
|
|
@@ -2433,9 +2444,7 @@
|
|
|
2433
2444
|
// Step 1: Initiate device auth session
|
|
2434
2445
|
const initResponse = await fetch(`${this.baseURL}/api/device-auth/initiate`, {
|
|
2435
2446
|
method: 'POST',
|
|
2436
|
-
headers: {
|
|
2437
|
-
'Content-Type': 'application/json',
|
|
2438
|
-
},
|
|
2447
|
+
headers: Object.assign({ 'Content-Type': 'application/json' }, getSDKHeaders()),
|
|
2439
2448
|
body: JSON.stringify({
|
|
2440
2449
|
game_id: this.gameId,
|
|
2441
2450
|
code_challenge: codeChallenge,
|
|
@@ -2504,7 +2513,7 @@
|
|
|
2504
2513
|
return;
|
|
2505
2514
|
}
|
|
2506
2515
|
try {
|
|
2507
|
-
const pollResponse = await fetch(`${this.baseURL}/api/device-auth/poll?session_id=${encodeURIComponent(session_id)}&code_verifier=${encodeURIComponent(codeVerifier)}
|
|
2516
|
+
const pollResponse = await fetch(`${this.baseURL}/api/device-auth/poll?session_id=${encodeURIComponent(session_id)}&code_verifier=${encodeURIComponent(codeVerifier)}`, { headers: Object.assign({}, getSDKHeaders()) });
|
|
2508
2517
|
const pollData = await pollResponse.json();
|
|
2509
2518
|
if (pollResponse.ok) {
|
|
2510
2519
|
if (pollData.status === 'pending') {
|
|
@@ -2632,9 +2641,7 @@
|
|
|
2632
2641
|
// Initiate device auth session
|
|
2633
2642
|
const initResponse = await fetch(`${this.baseURL}/api/device-auth/initiate`, {
|
|
2634
2643
|
method: 'POST',
|
|
2635
|
-
headers: {
|
|
2636
|
-
'Content-Type': 'application/json',
|
|
2637
|
-
},
|
|
2644
|
+
headers: Object.assign({ 'Content-Type': 'application/json' }, getSDKHeaders()),
|
|
2638
2645
|
body: JSON.stringify({
|
|
2639
2646
|
game_id: this.gameId,
|
|
2640
2647
|
code_challenge: codeChallenge,
|
|
@@ -2697,7 +2704,7 @@
|
|
|
2697
2704
|
return;
|
|
2698
2705
|
}
|
|
2699
2706
|
try {
|
|
2700
|
-
const pollResponse = await fetch(`${this.baseURL}/api/device-auth/poll?session_id=${encodeURIComponent(sessionId)}&code_verifier=${encodeURIComponent(codeVerifier)}
|
|
2707
|
+
const pollResponse = await fetch(`${this.baseURL}/api/device-auth/poll?session_id=${encodeURIComponent(sessionId)}&code_verifier=${encodeURIComponent(codeVerifier)}`, { headers: Object.assign({}, getSDKHeaders()) });
|
|
2701
2708
|
const pollData = await pollResponse.json();
|
|
2702
2709
|
if (pollResponse.ok) {
|
|
2703
2710
|
if (pollData.status === 'pending') {
|
|
@@ -2966,10 +2973,7 @@
|
|
|
2966
2973
|
try {
|
|
2967
2974
|
const response = await fetch(`${this.baseURL}${JWT_EXCHANGE_ENDPOINT}`, {
|
|
2968
2975
|
method: 'POST',
|
|
2969
|
-
headers: {
|
|
2970
|
-
Authorization: `Bearer ${jwt}`,
|
|
2971
|
-
'Content-Type': 'application/json',
|
|
2972
|
-
},
|
|
2976
|
+
headers: Object.assign({ Authorization: `Bearer ${jwt}`, 'Content-Type': 'application/json' }, getSDKHeaders()),
|
|
2973
2977
|
body: JSON.stringify({ gameId: this.config.gameId }),
|
|
2974
2978
|
});
|
|
2975
2979
|
if (!response.ok) {
|
|
@@ -3319,9 +3323,7 @@
|
|
|
3319
3323
|
this.logger.debug('Refreshing access token');
|
|
3320
3324
|
const response = await fetch(`${this.baseURL}${TOKEN_REFRESH_ENDPOINT}`, {
|
|
3321
3325
|
method: 'POST',
|
|
3322
|
-
headers: {
|
|
3323
|
-
'Content-Type': 'application/json',
|
|
3324
|
-
},
|
|
3326
|
+
headers: Object.assign({ 'Content-Type': 'application/json' }, getSDKHeaders()),
|
|
3325
3327
|
body: JSON.stringify({
|
|
3326
3328
|
refresh_token: this.authState.refreshToken,
|
|
3327
3329
|
}),
|
|
@@ -3424,7 +3426,7 @@
|
|
|
3424
3426
|
* RechargeManager handles the recharge modal UI and recharge window opening
|
|
3425
3427
|
*/
|
|
3426
3428
|
class RechargeManager extends EventEmitter {
|
|
3427
|
-
constructor(playerToken, rechargePortalUrl = 'https://playkit.ai/recharge', gameId) {
|
|
3429
|
+
constructor(playerToken, rechargePortalUrl = 'https://players.playkit.ai/recharge', gameId) {
|
|
3428
3430
|
super();
|
|
3429
3431
|
this.modalContainer = null;
|
|
3430
3432
|
this.styleElement = null;
|
|
@@ -3527,220 +3529,220 @@
|
|
|
3527
3529
|
return;
|
|
3528
3530
|
}
|
|
3529
3531
|
this.styleElement = document.createElement('style');
|
|
3530
|
-
this.styleElement.textContent = `
|
|
3531
|
-
.playkit-recharge-overlay {
|
|
3532
|
-
position: fixed;
|
|
3533
|
-
top: 0;
|
|
3534
|
-
left: 0;
|
|
3535
|
-
right: 0;
|
|
3536
|
-
bottom: 0;
|
|
3537
|
-
background: rgba(0, 0, 0, 0.8);
|
|
3538
|
-
display: flex;
|
|
3539
|
-
justify-content: center;
|
|
3540
|
-
align-items: center;
|
|
3541
|
-
z-index: 999999;
|
|
3542
|
-
animation: playkit-recharge-fadeIn 0.2s ease-out;
|
|
3543
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
3544
|
-
}
|
|
3545
|
-
|
|
3546
|
-
@keyframes playkit-recharge-fadeIn {
|
|
3547
|
-
from {
|
|
3548
|
-
opacity: 0;
|
|
3549
|
-
}
|
|
3550
|
-
to {
|
|
3551
|
-
opacity: 1;
|
|
3552
|
-
}
|
|
3553
|
-
}
|
|
3554
|
-
|
|
3555
|
-
.playkit-recharge-modal {
|
|
3556
|
-
background: #fff;
|
|
3557
|
-
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
3558
|
-
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.05);
|
|
3559
|
-
padding: 24px;
|
|
3560
|
-
max-width: 320px;
|
|
3561
|
-
width: 90%;
|
|
3562
|
-
position: relative;
|
|
3563
|
-
text-align: center;
|
|
3564
|
-
}
|
|
3565
|
-
|
|
3566
|
-
.playkit-recharge-title {
|
|
3567
|
-
font-size: 14px;
|
|
3568
|
-
font-weight: 600;
|
|
3569
|
-
color: #171717;
|
|
3570
|
-
margin: 0 0 8px 0;
|
|
3571
|
-
text-align: center;
|
|
3572
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
3573
|
-
}
|
|
3574
|
-
|
|
3575
|
-
.playkit-recharge-message {
|
|
3576
|
-
font-size: 14px;
|
|
3577
|
-
color: #666;
|
|
3578
|
-
margin: 0 0 20px 0;
|
|
3579
|
-
text-align: center;
|
|
3580
|
-
line-height: 1.5;
|
|
3581
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
3582
|
-
}
|
|
3583
|
-
|
|
3584
|
-
.playkit-recharge-balance {
|
|
3585
|
-
background: #f5f5f5;
|
|
3586
|
-
border: 1px solid #e5e7eb;
|
|
3587
|
-
padding: 16px;
|
|
3588
|
-
margin: 0 0 20px 0;
|
|
3589
|
-
text-align: center;
|
|
3590
|
-
}
|
|
3591
|
-
|
|
3592
|
-
.playkit-recharge-balance-label {
|
|
3593
|
-
font-size: 12px;
|
|
3594
|
-
color: #666;
|
|
3595
|
-
margin: 0 0 8px 0;
|
|
3596
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
3597
|
-
}
|
|
3598
|
-
|
|
3599
|
-
.playkit-recharge-balance-value {
|
|
3600
|
-
font-size: 24px;
|
|
3601
|
-
font-weight: bold;
|
|
3602
|
-
color: #171717;
|
|
3603
|
-
margin: 0;
|
|
3604
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
3605
|
-
}
|
|
3606
|
-
|
|
3607
|
-
.playkit-recharge-balance-unit {
|
|
3608
|
-
font-size: 14px;
|
|
3609
|
-
color: #666;
|
|
3610
|
-
margin-left: 4px;
|
|
3611
|
-
}
|
|
3612
|
-
|
|
3613
|
-
.playkit-recharge-buttons {
|
|
3614
|
-
display: flex;
|
|
3615
|
-
flex-direction: column;
|
|
3616
|
-
gap: 8px;
|
|
3617
|
-
}
|
|
3618
|
-
|
|
3619
|
-
.playkit-recharge-button {
|
|
3620
|
-
width: 100%;
|
|
3621
|
-
padding: 10px 16px;
|
|
3622
|
-
border: none;
|
|
3623
|
-
font-size: 14px;
|
|
3624
|
-
font-weight: 500;
|
|
3625
|
-
cursor: pointer;
|
|
3626
|
-
transition: all 0.2s ease;
|
|
3627
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
3628
|
-
}
|
|
3629
|
-
|
|
3630
|
-
.playkit-recharge-button-primary {
|
|
3631
|
-
background: #171717;
|
|
3632
|
-
color: white;
|
|
3633
|
-
}
|
|
3634
|
-
|
|
3635
|
-
.playkit-recharge-button-primary:hover {
|
|
3636
|
-
background: #404040;
|
|
3637
|
-
}
|
|
3638
|
-
|
|
3639
|
-
.playkit-recharge-button-primary:active {
|
|
3640
|
-
background: #0a0a0a;
|
|
3641
|
-
}
|
|
3642
|
-
|
|
3643
|
-
.playkit-recharge-button-secondary {
|
|
3644
|
-
background: transparent;
|
|
3645
|
-
color: #666;
|
|
3646
|
-
border: 1px solid #e5e7eb;
|
|
3647
|
-
}
|
|
3648
|
-
|
|
3649
|
-
.playkit-recharge-button-secondary:hover {
|
|
3650
|
-
background: #f5f5f5;
|
|
3651
|
-
border-color: #d4d4d4;
|
|
3652
|
-
}
|
|
3653
|
-
|
|
3654
|
-
.playkit-recharge-button-secondary:active {
|
|
3655
|
-
background: #e5e5e5;
|
|
3656
|
-
}
|
|
3657
|
-
|
|
3658
|
-
@media (max-width: 480px) {
|
|
3659
|
-
.playkit-recharge-modal {
|
|
3660
|
-
padding: 20px;
|
|
3661
|
-
}
|
|
3662
|
-
}
|
|
3663
|
-
|
|
3664
|
-
/* Daily Refresh Toast Styles */
|
|
3665
|
-
.playkit-daily-refresh-toast {
|
|
3666
|
-
position: fixed;
|
|
3667
|
-
top: 20px;
|
|
3668
|
-
right: 20px;
|
|
3669
|
-
background: #fff;
|
|
3670
|
-
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
3671
|
-
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.1);
|
|
3672
|
-
padding: 16px 20px;
|
|
3673
|
-
min-width: 240px;
|
|
3674
|
-
max-width: 320px;
|
|
3675
|
-
z-index: 999998;
|
|
3676
|
-
animation: playkit-toast-slideIn 0.3s ease-out;
|
|
3677
|
-
display: flex;
|
|
3678
|
-
align-items: flex-start;
|
|
3679
|
-
gap: 12px;
|
|
3680
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
3681
|
-
}
|
|
3682
|
-
|
|
3683
|
-
.playkit-daily-refresh-toast.hiding {
|
|
3684
|
-
animation: playkit-toast-fadeOut 0.3s ease-out forwards;
|
|
3685
|
-
}
|
|
3686
|
-
|
|
3687
|
-
@keyframes playkit-toast-slideIn {
|
|
3688
|
-
from {
|
|
3689
|
-
transform: translateX(100%);
|
|
3690
|
-
opacity: 0;
|
|
3691
|
-
}
|
|
3692
|
-
to {
|
|
3693
|
-
transform: translateX(0);
|
|
3694
|
-
opacity: 1;
|
|
3695
|
-
}
|
|
3696
|
-
}
|
|
3697
|
-
|
|
3698
|
-
@keyframes playkit-toast-fadeOut {
|
|
3699
|
-
from {
|
|
3700
|
-
transform: translateX(0);
|
|
3701
|
-
opacity: 1;
|
|
3702
|
-
}
|
|
3703
|
-
to {
|
|
3704
|
-
transform: translateX(100%);
|
|
3705
|
-
opacity: 0;
|
|
3706
|
-
}
|
|
3707
|
-
}
|
|
3708
|
-
|
|
3709
|
-
.playkit-toast-icon {
|
|
3710
|
-
width: 24px;
|
|
3711
|
-
height: 24px;
|
|
3712
|
-
background: #171717;
|
|
3713
|
-
border-radius: 50%;
|
|
3714
|
-
display: flex;
|
|
3715
|
-
align-items: center;
|
|
3716
|
-
justify-content: center;
|
|
3717
|
-
flex-shrink: 0;
|
|
3718
|
-
}
|
|
3719
|
-
|
|
3720
|
-
.playkit-toast-icon svg {
|
|
3721
|
-
width: 14px;
|
|
3722
|
-
height: 14px;
|
|
3723
|
-
color: #ffffff;
|
|
3724
|
-
}
|
|
3725
|
-
|
|
3726
|
-
.playkit-toast-message {
|
|
3727
|
-
flex: 1;
|
|
3728
|
-
font-size: 14px;
|
|
3729
|
-
font-weight: 500;
|
|
3730
|
-
color: #171717;
|
|
3731
|
-
line-height: 1.4;
|
|
3732
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
3733
|
-
}
|
|
3734
|
-
|
|
3735
|
-
@media (max-width: 480px) {
|
|
3736
|
-
.playkit-daily-refresh-toast {
|
|
3737
|
-
top: 10px;
|
|
3738
|
-
right: 10px;
|
|
3739
|
-
left: 10px;
|
|
3740
|
-
min-width: auto;
|
|
3741
|
-
max-width: none;
|
|
3742
|
-
}
|
|
3743
|
-
}
|
|
3532
|
+
this.styleElement.textContent = `
|
|
3533
|
+
.playkit-recharge-overlay {
|
|
3534
|
+
position: fixed;
|
|
3535
|
+
top: 0;
|
|
3536
|
+
left: 0;
|
|
3537
|
+
right: 0;
|
|
3538
|
+
bottom: 0;
|
|
3539
|
+
background: rgba(0, 0, 0, 0.8);
|
|
3540
|
+
display: flex;
|
|
3541
|
+
justify-content: center;
|
|
3542
|
+
align-items: center;
|
|
3543
|
+
z-index: 999999;
|
|
3544
|
+
animation: playkit-recharge-fadeIn 0.2s ease-out;
|
|
3545
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
3546
|
+
}
|
|
3547
|
+
|
|
3548
|
+
@keyframes playkit-recharge-fadeIn {
|
|
3549
|
+
from {
|
|
3550
|
+
opacity: 0;
|
|
3551
|
+
}
|
|
3552
|
+
to {
|
|
3553
|
+
opacity: 1;
|
|
3554
|
+
}
|
|
3555
|
+
}
|
|
3556
|
+
|
|
3557
|
+
.playkit-recharge-modal {
|
|
3558
|
+
background: #fff;
|
|
3559
|
+
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
3560
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.05);
|
|
3561
|
+
padding: 24px;
|
|
3562
|
+
max-width: 320px;
|
|
3563
|
+
width: 90%;
|
|
3564
|
+
position: relative;
|
|
3565
|
+
text-align: center;
|
|
3566
|
+
}
|
|
3567
|
+
|
|
3568
|
+
.playkit-recharge-title {
|
|
3569
|
+
font-size: 14px;
|
|
3570
|
+
font-weight: 600;
|
|
3571
|
+
color: #171717;
|
|
3572
|
+
margin: 0 0 8px 0;
|
|
3573
|
+
text-align: center;
|
|
3574
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
3575
|
+
}
|
|
3576
|
+
|
|
3577
|
+
.playkit-recharge-message {
|
|
3578
|
+
font-size: 14px;
|
|
3579
|
+
color: #666;
|
|
3580
|
+
margin: 0 0 20px 0;
|
|
3581
|
+
text-align: center;
|
|
3582
|
+
line-height: 1.5;
|
|
3583
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
3584
|
+
}
|
|
3585
|
+
|
|
3586
|
+
.playkit-recharge-balance {
|
|
3587
|
+
background: #f5f5f5;
|
|
3588
|
+
border: 1px solid #e5e7eb;
|
|
3589
|
+
padding: 16px;
|
|
3590
|
+
margin: 0 0 20px 0;
|
|
3591
|
+
text-align: center;
|
|
3592
|
+
}
|
|
3593
|
+
|
|
3594
|
+
.playkit-recharge-balance-label {
|
|
3595
|
+
font-size: 12px;
|
|
3596
|
+
color: #666;
|
|
3597
|
+
margin: 0 0 8px 0;
|
|
3598
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
3599
|
+
}
|
|
3600
|
+
|
|
3601
|
+
.playkit-recharge-balance-value {
|
|
3602
|
+
font-size: 24px;
|
|
3603
|
+
font-weight: bold;
|
|
3604
|
+
color: #171717;
|
|
3605
|
+
margin: 0;
|
|
3606
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
3607
|
+
}
|
|
3608
|
+
|
|
3609
|
+
.playkit-recharge-balance-unit {
|
|
3610
|
+
font-size: 14px;
|
|
3611
|
+
color: #666;
|
|
3612
|
+
margin-left: 4px;
|
|
3613
|
+
}
|
|
3614
|
+
|
|
3615
|
+
.playkit-recharge-buttons {
|
|
3616
|
+
display: flex;
|
|
3617
|
+
flex-direction: column;
|
|
3618
|
+
gap: 8px;
|
|
3619
|
+
}
|
|
3620
|
+
|
|
3621
|
+
.playkit-recharge-button {
|
|
3622
|
+
width: 100%;
|
|
3623
|
+
padding: 10px 16px;
|
|
3624
|
+
border: none;
|
|
3625
|
+
font-size: 14px;
|
|
3626
|
+
font-weight: 500;
|
|
3627
|
+
cursor: pointer;
|
|
3628
|
+
transition: all 0.2s ease;
|
|
3629
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
3630
|
+
}
|
|
3631
|
+
|
|
3632
|
+
.playkit-recharge-button-primary {
|
|
3633
|
+
background: #171717;
|
|
3634
|
+
color: white;
|
|
3635
|
+
}
|
|
3636
|
+
|
|
3637
|
+
.playkit-recharge-button-primary:hover {
|
|
3638
|
+
background: #404040;
|
|
3639
|
+
}
|
|
3640
|
+
|
|
3641
|
+
.playkit-recharge-button-primary:active {
|
|
3642
|
+
background: #0a0a0a;
|
|
3643
|
+
}
|
|
3644
|
+
|
|
3645
|
+
.playkit-recharge-button-secondary {
|
|
3646
|
+
background: transparent;
|
|
3647
|
+
color: #666;
|
|
3648
|
+
border: 1px solid #e5e7eb;
|
|
3649
|
+
}
|
|
3650
|
+
|
|
3651
|
+
.playkit-recharge-button-secondary:hover {
|
|
3652
|
+
background: #f5f5f5;
|
|
3653
|
+
border-color: #d4d4d4;
|
|
3654
|
+
}
|
|
3655
|
+
|
|
3656
|
+
.playkit-recharge-button-secondary:active {
|
|
3657
|
+
background: #e5e5e5;
|
|
3658
|
+
}
|
|
3659
|
+
|
|
3660
|
+
@media (max-width: 480px) {
|
|
3661
|
+
.playkit-recharge-modal {
|
|
3662
|
+
padding: 20px;
|
|
3663
|
+
}
|
|
3664
|
+
}
|
|
3665
|
+
|
|
3666
|
+
/* Daily Refresh Toast Styles */
|
|
3667
|
+
.playkit-daily-refresh-toast {
|
|
3668
|
+
position: fixed;
|
|
3669
|
+
top: 20px;
|
|
3670
|
+
right: 20px;
|
|
3671
|
+
background: #fff;
|
|
3672
|
+
border: 1px solid rgba(0, 0, 0, 0.1);
|
|
3673
|
+
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.1);
|
|
3674
|
+
padding: 16px 20px;
|
|
3675
|
+
min-width: 240px;
|
|
3676
|
+
max-width: 320px;
|
|
3677
|
+
z-index: 999998;
|
|
3678
|
+
animation: playkit-toast-slideIn 0.3s ease-out;
|
|
3679
|
+
display: flex;
|
|
3680
|
+
align-items: flex-start;
|
|
3681
|
+
gap: 12px;
|
|
3682
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
3683
|
+
}
|
|
3684
|
+
|
|
3685
|
+
.playkit-daily-refresh-toast.hiding {
|
|
3686
|
+
animation: playkit-toast-fadeOut 0.3s ease-out forwards;
|
|
3687
|
+
}
|
|
3688
|
+
|
|
3689
|
+
@keyframes playkit-toast-slideIn {
|
|
3690
|
+
from {
|
|
3691
|
+
transform: translateX(100%);
|
|
3692
|
+
opacity: 0;
|
|
3693
|
+
}
|
|
3694
|
+
to {
|
|
3695
|
+
transform: translateX(0);
|
|
3696
|
+
opacity: 1;
|
|
3697
|
+
}
|
|
3698
|
+
}
|
|
3699
|
+
|
|
3700
|
+
@keyframes playkit-toast-fadeOut {
|
|
3701
|
+
from {
|
|
3702
|
+
transform: translateX(0);
|
|
3703
|
+
opacity: 1;
|
|
3704
|
+
}
|
|
3705
|
+
to {
|
|
3706
|
+
transform: translateX(100%);
|
|
3707
|
+
opacity: 0;
|
|
3708
|
+
}
|
|
3709
|
+
}
|
|
3710
|
+
|
|
3711
|
+
.playkit-toast-icon {
|
|
3712
|
+
width: 24px;
|
|
3713
|
+
height: 24px;
|
|
3714
|
+
background: #171717;
|
|
3715
|
+
border-radius: 50%;
|
|
3716
|
+
display: flex;
|
|
3717
|
+
align-items: center;
|
|
3718
|
+
justify-content: center;
|
|
3719
|
+
flex-shrink: 0;
|
|
3720
|
+
}
|
|
3721
|
+
|
|
3722
|
+
.playkit-toast-icon svg {
|
|
3723
|
+
width: 14px;
|
|
3724
|
+
height: 14px;
|
|
3725
|
+
color: #ffffff;
|
|
3726
|
+
}
|
|
3727
|
+
|
|
3728
|
+
.playkit-toast-message {
|
|
3729
|
+
flex: 1;
|
|
3730
|
+
font-size: 14px;
|
|
3731
|
+
font-weight: 500;
|
|
3732
|
+
color: #171717;
|
|
3733
|
+
line-height: 1.4;
|
|
3734
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
3735
|
+
}
|
|
3736
|
+
|
|
3737
|
+
@media (max-width: 480px) {
|
|
3738
|
+
.playkit-daily-refresh-toast {
|
|
3739
|
+
top: 10px;
|
|
3740
|
+
right: 10px;
|
|
3741
|
+
left: 10px;
|
|
3742
|
+
min-width: auto;
|
|
3743
|
+
max-width: none;
|
|
3744
|
+
}
|
|
3745
|
+
}
|
|
3744
3746
|
`;
|
|
3745
3747
|
document.head.appendChild(this.styleElement);
|
|
3746
3748
|
}
|
|
@@ -3877,7 +3879,8 @@
|
|
|
3877
3879
|
/**
|
|
3878
3880
|
* Player client for managing player information and credits
|
|
3879
3881
|
*/
|
|
3880
|
-
|
|
3882
|
+
// @ts-ignore - replaced at build time
|
|
3883
|
+
const DEFAULT_BASE_URL$4 = "https://api.playkit.ai";
|
|
3881
3884
|
const PLAYER_INFO_ENDPOINT = '/api/external/player-info';
|
|
3882
3885
|
const SET_NICKNAME_ENDPOINT = '/api/external/set-game-player-nickname';
|
|
3883
3886
|
class PlayerClient extends EventEmitter {
|
|
@@ -3895,7 +3898,7 @@
|
|
|
3895
3898
|
autoShowBalanceModal: (_a = rechargeConfig.autoShowBalanceModal) !== null && _a !== void 0 ? _a : true,
|
|
3896
3899
|
balanceCheckInterval: (_b = rechargeConfig.balanceCheckInterval) !== null && _b !== void 0 ? _b : 30000,
|
|
3897
3900
|
checkBalanceAfterApiCall: (_c = rechargeConfig.checkBalanceAfterApiCall) !== null && _c !== void 0 ? _c : true,
|
|
3898
|
-
rechargePortalUrl: rechargeConfig.rechargePortalUrl || 'https://playkit.ai/recharge',
|
|
3901
|
+
rechargePortalUrl: rechargeConfig.rechargePortalUrl || 'https://players.playkit.ai/recharge',
|
|
3899
3902
|
showDailyRefreshToast: (_d = rechargeConfig.showDailyRefreshToast) !== null && _d !== void 0 ? _d : true,
|
|
3900
3903
|
};
|
|
3901
3904
|
}
|
|
@@ -3910,9 +3913,7 @@
|
|
|
3910
3913
|
}
|
|
3911
3914
|
try {
|
|
3912
3915
|
// Build headers with X-Game-Id to support Global Developer Token
|
|
3913
|
-
const headers = {
|
|
3914
|
-
Authorization: `Bearer ${token}`,
|
|
3915
|
-
};
|
|
3916
|
+
const headers = Object.assign({ Authorization: `Bearer ${token}` }, getSDKHeaders());
|
|
3916
3917
|
if (this.gameId) {
|
|
3917
3918
|
headers['X-Game-Id'] = this.gameId;
|
|
3918
3919
|
}
|
|
@@ -4012,10 +4013,7 @@
|
|
|
4012
4013
|
try {
|
|
4013
4014
|
const response = await fetch(`${this.baseURL}${SET_NICKNAME_ENDPOINT}`, {
|
|
4014
4015
|
method: 'POST',
|
|
4015
|
-
headers: {
|
|
4016
|
-
Authorization: `Bearer ${token}`,
|
|
4017
|
-
'Content-Type': 'application/json',
|
|
4018
|
-
},
|
|
4016
|
+
headers: Object.assign({ Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, getSDKHeaders()),
|
|
4019
4017
|
body: JSON.stringify({ nickname: trimmed }),
|
|
4020
4018
|
});
|
|
4021
4019
|
if (!response.ok) {
|
|
@@ -4165,10 +4163,84 @@
|
|
|
4165
4163
|
}
|
|
4166
4164
|
}
|
|
4167
4165
|
|
|
4166
|
+
const VALID_PART_TYPES = new Set([
|
|
4167
|
+
'text',
|
|
4168
|
+
'image',
|
|
4169
|
+
'image_url',
|
|
4170
|
+
'file',
|
|
4171
|
+
'audio',
|
|
4172
|
+
'input_audio',
|
|
4173
|
+
]);
|
|
4174
|
+
function describePart(part) {
|
|
4175
|
+
if (part === null)
|
|
4176
|
+
return 'null';
|
|
4177
|
+
if (typeof part !== 'object')
|
|
4178
|
+
return typeof part;
|
|
4179
|
+
const keys = Object.keys(part).slice(0, 5).join(',');
|
|
4180
|
+
return `{${keys}}`;
|
|
4181
|
+
}
|
|
4182
|
+
/**
|
|
4183
|
+
* Validate that `messages` matches the SDK's `Message[]` runtime contract before
|
|
4184
|
+
* shipping to the chat API. Throws `PlayKitError('INVALID_MESSAGES')` when a
|
|
4185
|
+
* caller has wrapped a Message[] inside one user message's `content` (the
|
|
4186
|
+
* `[{role:'user', content: [{role,...}, ...]}]` anti-pattern that bypasses the
|
|
4187
|
+
* `MessageContentPart` type at runtime).
|
|
4188
|
+
*
|
|
4189
|
+
* Does NOT auto-flatten — silently guessing system/user roles would mask bugs.
|
|
4190
|
+
*/
|
|
4191
|
+
function assertValidMessages(messages) {
|
|
4192
|
+
if (!Array.isArray(messages)) {
|
|
4193
|
+
throw new PlayKitError('messages must be an array of Message', 'INVALID_MESSAGES');
|
|
4194
|
+
}
|
|
4195
|
+
for (let i = 0; i < messages.length; i++) {
|
|
4196
|
+
const msg = messages[i];
|
|
4197
|
+
if (!msg || typeof msg !== 'object') {
|
|
4198
|
+
throw new PlayKitError(`messages[${i}] must be an object with {role, content}`, 'INVALID_MESSAGES');
|
|
4199
|
+
}
|
|
4200
|
+
const content = msg.content;
|
|
4201
|
+
if (typeof content === 'string' || content == null)
|
|
4202
|
+
continue;
|
|
4203
|
+
if (!Array.isArray(content)) {
|
|
4204
|
+
throw new PlayKitError(`messages[${i}].content must be a string or an array of content parts (got ${typeof content})`, 'INVALID_MESSAGES');
|
|
4205
|
+
}
|
|
4206
|
+
for (let j = 0; j < content.length; j++) {
|
|
4207
|
+
const part = content[j];
|
|
4208
|
+
if (!part || typeof part !== 'object') {
|
|
4209
|
+
throw new PlayKitError(`messages[${i}].content[${j}] must be a content part object (got ${typeof part})`, 'INVALID_MESSAGES');
|
|
4210
|
+
}
|
|
4211
|
+
const hasType = typeof part.type === 'string' && VALID_PART_TYPES.has(part.type);
|
|
4212
|
+
if (!hasType) {
|
|
4213
|
+
if ('role' in part && 'content' in part) {
|
|
4214
|
+
throw new PlayKitError(`messages[${i}].content[${j}] is shaped like a Message (has role/content) ` +
|
|
4215
|
+
`but content parts must be {type:'text'|'image'|'image_url'|'file'|'audio'|'input_audio',...}. ` +
|
|
4216
|
+
`Did you mean to pass that array as messages directly? ` +
|
|
4217
|
+
`e.g. \`messages: theArray\` instead of \`messages: [{role:'user', content: theArray}]\`. ` +
|
|
4218
|
+
`Got part ${describePart(part)}`, 'INVALID_MESSAGES');
|
|
4219
|
+
}
|
|
4220
|
+
throw new PlayKitError(`messages[${i}].content[${j}] is missing a recognized 'type' field ` +
|
|
4221
|
+
`(expected one of text|image|image_url|file|audio|input_audio). Got part ${describePart(part)}`, 'INVALID_MESSAGES');
|
|
4222
|
+
}
|
|
4223
|
+
}
|
|
4224
|
+
}
|
|
4225
|
+
}
|
|
4226
|
+
|
|
4168
4227
|
/**
|
|
4169
4228
|
* Chat provider for HTTP communication with chat API
|
|
4170
4229
|
*/
|
|
4171
|
-
|
|
4230
|
+
/**
|
|
4231
|
+
* Helper to extract string from MessageContent
|
|
4232
|
+
*/
|
|
4233
|
+
function contentToString$1(content) {
|
|
4234
|
+
if (!content)
|
|
4235
|
+
return '';
|
|
4236
|
+
if (typeof content === 'string')
|
|
4237
|
+
return content;
|
|
4238
|
+
// For array of content parts, extract text parts
|
|
4239
|
+
const textParts = content.filter(part => part.type === 'text');
|
|
4240
|
+
return textParts.map(part => part.text).join('');
|
|
4241
|
+
}
|
|
4242
|
+
// @ts-ignore - replaced at build time
|
|
4243
|
+
const DEFAULT_BASE_URL$3 = "https://api.playkit.ai";
|
|
4172
4244
|
class ChatProvider {
|
|
4173
4245
|
constructor(authManager, config) {
|
|
4174
4246
|
this.authManager = authManager;
|
|
@@ -4186,6 +4258,7 @@
|
|
|
4186
4258
|
*/
|
|
4187
4259
|
async chatCompletion(chatConfig) {
|
|
4188
4260
|
var _a;
|
|
4261
|
+
assertValidMessages(chatConfig.messages);
|
|
4189
4262
|
// Ensure token is valid, auto-refresh if needed (browser mode only)
|
|
4190
4263
|
await this.authManager.ensureValidToken();
|
|
4191
4264
|
const token = this.authManager.getToken();
|
|
@@ -4207,10 +4280,7 @@
|
|
|
4207
4280
|
try {
|
|
4208
4281
|
const response = await fetch(`${this.baseURL}${endpoint}`, {
|
|
4209
4282
|
method: 'POST',
|
|
4210
|
-
headers: {
|
|
4211
|
-
Authorization: `Bearer ${token}`,
|
|
4212
|
-
'Content-Type': 'application/json',
|
|
4213
|
-
},
|
|
4283
|
+
headers: Object.assign({ Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, getSDKHeaders()),
|
|
4214
4284
|
body: JSON.stringify(requestBody),
|
|
4215
4285
|
});
|
|
4216
4286
|
if (!response.ok) {
|
|
@@ -4245,6 +4315,7 @@
|
|
|
4245
4315
|
*/
|
|
4246
4316
|
async chatCompletionStream(chatConfig) {
|
|
4247
4317
|
var _a;
|
|
4318
|
+
assertValidMessages(chatConfig.messages);
|
|
4248
4319
|
// Ensure token is valid, auto-refresh if needed (browser mode only)
|
|
4249
4320
|
await this.authManager.ensureValidToken();
|
|
4250
4321
|
const token = this.authManager.getToken();
|
|
@@ -4266,10 +4337,7 @@
|
|
|
4266
4337
|
try {
|
|
4267
4338
|
const response = await fetch(`${this.baseURL}${endpoint}`, {
|
|
4268
4339
|
method: 'POST',
|
|
4269
|
-
headers: {
|
|
4270
|
-
Authorization: `Bearer ${token}`,
|
|
4271
|
-
'Content-Type': 'application/json',
|
|
4272
|
-
},
|
|
4340
|
+
headers: Object.assign({ Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, getSDKHeaders()),
|
|
4273
4341
|
body: JSON.stringify(requestBody),
|
|
4274
4342
|
});
|
|
4275
4343
|
if (!response.ok) {
|
|
@@ -4306,6 +4374,7 @@
|
|
|
4306
4374
|
*/
|
|
4307
4375
|
async chatCompletionWithTools(chatConfig) {
|
|
4308
4376
|
var _a, _b;
|
|
4377
|
+
assertValidMessages(chatConfig.messages);
|
|
4309
4378
|
const token = this.authManager.getToken();
|
|
4310
4379
|
if (!token) {
|
|
4311
4380
|
throw new PlayKitError('Not authenticated', 'NOT_AUTHENTICATED');
|
|
@@ -4332,10 +4401,7 @@
|
|
|
4332
4401
|
try {
|
|
4333
4402
|
const response = await fetch(`${this.baseURL}${endpoint}`, {
|
|
4334
4403
|
method: 'POST',
|
|
4335
|
-
headers: {
|
|
4336
|
-
Authorization: `Bearer ${token}`,
|
|
4337
|
-
'Content-Type': 'application/json',
|
|
4338
|
-
},
|
|
4404
|
+
headers: Object.assign({ Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, getSDKHeaders()),
|
|
4339
4405
|
body: JSON.stringify(requestBody),
|
|
4340
4406
|
});
|
|
4341
4407
|
if (!response.ok) {
|
|
@@ -4366,6 +4432,7 @@
|
|
|
4366
4432
|
*/
|
|
4367
4433
|
async chatCompletionWithToolsStream(chatConfig) {
|
|
4368
4434
|
var _a, _b;
|
|
4435
|
+
assertValidMessages(chatConfig.messages);
|
|
4369
4436
|
const token = this.authManager.getToken();
|
|
4370
4437
|
if (!token) {
|
|
4371
4438
|
throw new PlayKitError('Not authenticated', 'NOT_AUTHENTICATED');
|
|
@@ -4392,10 +4459,7 @@
|
|
|
4392
4459
|
try {
|
|
4393
4460
|
const response = await fetch(`${this.baseURL}${endpoint}`, {
|
|
4394
4461
|
method: 'POST',
|
|
4395
|
-
headers: {
|
|
4396
|
-
Authorization: `Bearer ${token}`,
|
|
4397
|
-
'Content-Type': 'application/json',
|
|
4398
|
-
},
|
|
4462
|
+
headers: Object.assign({ Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, getSDKHeaders()),
|
|
4399
4463
|
body: JSON.stringify(requestBody),
|
|
4400
4464
|
});
|
|
4401
4465
|
if (!response.ok) {
|
|
@@ -4465,10 +4529,7 @@
|
|
|
4465
4529
|
try {
|
|
4466
4530
|
const response = await fetch(`${this.baseURL}${endpoint}`, {
|
|
4467
4531
|
method: 'POST',
|
|
4468
|
-
headers: {
|
|
4469
|
-
Authorization: `Bearer ${token}`,
|
|
4470
|
-
'Content-Type': 'application/json',
|
|
4471
|
-
},
|
|
4532
|
+
headers: Object.assign({ Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, getSDKHeaders()),
|
|
4472
4533
|
body: JSON.stringify(requestBody),
|
|
4473
4534
|
});
|
|
4474
4535
|
if (!response.ok) {
|
|
@@ -4486,11 +4547,12 @@
|
|
|
4486
4547
|
this.playerClient.checkBalanceAfterApiCall().catch(() => { });
|
|
4487
4548
|
}
|
|
4488
4549
|
// Parse the response content as JSON
|
|
4489
|
-
const
|
|
4490
|
-
if (!
|
|
4550
|
+
const rawContent = (_a = result.choices[0]) === null || _a === void 0 ? void 0 : _a.message.content;
|
|
4551
|
+
if (!rawContent) {
|
|
4491
4552
|
throw new PlayKitError('No content in response', 'NO_CONTENT');
|
|
4492
4553
|
}
|
|
4493
4554
|
try {
|
|
4555
|
+
const content = contentToString$1(rawContent);
|
|
4494
4556
|
return JSON.parse(content);
|
|
4495
4557
|
}
|
|
4496
4558
|
catch (parseError) {
|
|
@@ -4509,7 +4571,8 @@
|
|
|
4509
4571
|
/**
|
|
4510
4572
|
* Image generation provider for HTTP communication with image API
|
|
4511
4573
|
*/
|
|
4512
|
-
|
|
4574
|
+
// @ts-ignore - replaced at build time
|
|
4575
|
+
const DEFAULT_BASE_URL$2 = "https://api.playkit.ai";
|
|
4513
4576
|
class ImageProvider {
|
|
4514
4577
|
constructor(authManager, config) {
|
|
4515
4578
|
this.authManager = authManager;
|
|
@@ -4562,10 +4625,7 @@
|
|
|
4562
4625
|
try {
|
|
4563
4626
|
const response = await fetch(`${this.baseURL}${endpoint}`, {
|
|
4564
4627
|
method: 'POST',
|
|
4565
|
-
headers: {
|
|
4566
|
-
Authorization: `Bearer ${token}`,
|
|
4567
|
-
'Content-Type': 'application/json',
|
|
4568
|
-
},
|
|
4628
|
+
headers: Object.assign({ Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, getSDKHeaders()),
|
|
4569
4629
|
body: JSON.stringify(requestBody),
|
|
4570
4630
|
});
|
|
4571
4631
|
if (!response.ok) {
|
|
@@ -4600,7 +4660,8 @@
|
|
|
4600
4660
|
/**
|
|
4601
4661
|
* Transcription provider for HTTP communication with audio transcription API
|
|
4602
4662
|
*/
|
|
4603
|
-
|
|
4663
|
+
// @ts-ignore - replaced at build time
|
|
4664
|
+
const DEFAULT_BASE_URL$1 = "https://api.playkit.ai";
|
|
4604
4665
|
class TranscriptionProvider {
|
|
4605
4666
|
constructor(authManager, config) {
|
|
4606
4667
|
this.authManager = authManager;
|
|
@@ -4665,10 +4726,7 @@
|
|
|
4665
4726
|
try {
|
|
4666
4727
|
const response = await fetch(`${this.baseURL}${endpoint}`, {
|
|
4667
4728
|
method: 'POST',
|
|
4668
|
-
headers: {
|
|
4669
|
-
Authorization: `Bearer ${token}`,
|
|
4670
|
-
'Content-Type': 'application/json',
|
|
4671
|
-
},
|
|
4729
|
+
headers: Object.assign({ Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' }, getSDKHeaders()),
|
|
4672
4730
|
body: JSON.stringify(requestBody),
|
|
4673
4731
|
});
|
|
4674
4732
|
if (!response.ok) {
|
|
@@ -4814,9 +4872,18 @@
|
|
|
4814
4872
|
if (text) {
|
|
4815
4873
|
yield yield __await(text);
|
|
4816
4874
|
}
|
|
4817
|
-
|
|
4875
|
+
// Stream termination events
|
|
4876
|
+
if (parsed.type === 'done' || parsed.type === 'finish' || parsed.finish_reason) {
|
|
4818
4877
|
return yield __await(void 0);
|
|
4819
4878
|
}
|
|
4879
|
+
if (parsed.type === 'abort') {
|
|
4880
|
+
// Server-side timeout or cancellation — treat as end of stream
|
|
4881
|
+
return yield __await(void 0);
|
|
4882
|
+
}
|
|
4883
|
+
if (parsed.type === 'error') {
|
|
4884
|
+
// Server-side error event — throw to trigger onError callback
|
|
4885
|
+
throw new Error(parsed.errorText || parsed.error || 'Stream error');
|
|
4886
|
+
}
|
|
4820
4887
|
}
|
|
4821
4888
|
catch (error) {
|
|
4822
4889
|
// If JSON parse fails, treat as plain text
|
|
@@ -4915,6 +4982,18 @@
|
|
|
4915
4982
|
/**
|
|
4916
4983
|
* Chat client for AI text generation
|
|
4917
4984
|
*/
|
|
4985
|
+
/**
|
|
4986
|
+
* Helper to extract string from MessageContent
|
|
4987
|
+
*/
|
|
4988
|
+
function contentToString(content) {
|
|
4989
|
+
if (!content)
|
|
4990
|
+
return '';
|
|
4991
|
+
if (typeof content === 'string')
|
|
4992
|
+
return content;
|
|
4993
|
+
// For array of content parts, extract text parts
|
|
4994
|
+
const textParts = content.filter(part => part.type === 'text');
|
|
4995
|
+
return textParts.map(part => part.text).join('');
|
|
4996
|
+
}
|
|
4918
4997
|
class ChatClient {
|
|
4919
4998
|
constructor(provider, model) {
|
|
4920
4999
|
this.schemaLibrary = null;
|
|
@@ -4964,7 +5043,7 @@
|
|
|
4964
5043
|
throw new Error('No choices in response');
|
|
4965
5044
|
}
|
|
4966
5045
|
return {
|
|
4967
|
-
content: choice.message.content,
|
|
5046
|
+
content: contentToString(choice.message.content),
|
|
4968
5047
|
model: response.model,
|
|
4969
5048
|
finishReason: choice.finish_reason,
|
|
4970
5049
|
usage: response.usage
|
|
@@ -5035,9 +5114,10 @@
|
|
|
5035
5114
|
}
|
|
5036
5115
|
// Extract user message content from the last user message
|
|
5037
5116
|
const lastUserMessage = [...messages].reverse().find(m => m.role === 'user');
|
|
5038
|
-
const prompt = (lastUserMessage === null || lastUserMessage === void 0 ? void 0 : lastUserMessage.content)
|
|
5117
|
+
const prompt = contentToString(lastUserMessage === null || lastUserMessage === void 0 ? void 0 : lastUserMessage.content);
|
|
5039
5118
|
// Build system message from messages array
|
|
5040
|
-
const
|
|
5119
|
+
const systemMessageContent = (_a = messages.find(m => m.role === 'system')) === null || _a === void 0 ? void 0 : _a.content;
|
|
5120
|
+
const systemMessage = contentToString(systemMessageContent) || undefined;
|
|
5041
5121
|
return this.generateStructuredWithSchema(schemaEntry.schema, prompt, Object.assign({ schemaName, schemaDescription: schemaEntry.description, systemMessage }, options));
|
|
5042
5122
|
}
|
|
5043
5123
|
/**
|
|
@@ -5128,7 +5208,7 @@
|
|
|
5128
5208
|
throw new Error('No choices in response');
|
|
5129
5209
|
}
|
|
5130
5210
|
return {
|
|
5131
|
-
content: choice.message.content
|
|
5211
|
+
content: contentToString(choice.message.content),
|
|
5132
5212
|
model: response.model,
|
|
5133
5213
|
finishReason: choice.finish_reason,
|
|
5134
5214
|
usage: response.usage
|
|
@@ -5192,7 +5272,7 @@
|
|
|
5192
5272
|
return new Promise((resolve, reject) => {
|
|
5193
5273
|
const img = new Image();
|
|
5194
5274
|
img.onload = () => resolve(img);
|
|
5195
|
-
img.onerror = (
|
|
5275
|
+
img.onerror = (_e) => reject(new Error('Failed to load image'));
|
|
5196
5276
|
img.src = this.toDataURL();
|
|
5197
5277
|
});
|
|
5198
5278
|
}
|
|
@@ -5206,13 +5286,14 @@
|
|
|
5206
5286
|
* Generate a single image
|
|
5207
5287
|
*/
|
|
5208
5288
|
async generateImage(config) {
|
|
5289
|
+
var _a;
|
|
5209
5290
|
const imageConfig = Object.assign(Object.assign({}, config), { model: config.model || this.model, n: 1 });
|
|
5210
5291
|
const response = await this.provider.generateImages(imageConfig);
|
|
5211
5292
|
const imageData = response.data[0];
|
|
5212
5293
|
if (!imageData || !imageData.b64_json) {
|
|
5213
5294
|
throw new Error('No image data in response');
|
|
5214
5295
|
}
|
|
5215
|
-
return new GeneratedImageImpl(imageData.b64_json, config.prompt, imageData.revised_prompt, config.size, imageData.b64_json_original, imageData.transparent_success);
|
|
5296
|
+
return new GeneratedImageImpl(imageData.b64_json, config.prompt, (_a = imageData.revised_prompt) !== null && _a !== void 0 ? _a : config.prompt, config.size, imageData.b64_json_original, imageData.transparent_success);
|
|
5216
5297
|
}
|
|
5217
5298
|
/**
|
|
5218
5299
|
* Generate multiple images
|
|
@@ -5221,10 +5302,11 @@
|
|
|
5221
5302
|
const imageConfig = Object.assign(Object.assign({}, config), { model: config.model || this.model, n: config.n || 1 });
|
|
5222
5303
|
const response = await this.provider.generateImages(imageConfig);
|
|
5223
5304
|
return response.data.map((imageData) => {
|
|
5305
|
+
var _a;
|
|
5224
5306
|
if (!imageData.b64_json) {
|
|
5225
5307
|
throw new Error('No image data in response');
|
|
5226
5308
|
}
|
|
5227
|
-
return new GeneratedImageImpl(imageData.b64_json, config.prompt, imageData.revised_prompt, config.size, imageData.b64_json_original, imageData.transparent_success);
|
|
5309
|
+
return new GeneratedImageImpl(imageData.b64_json, config.prompt, (_a = imageData.revised_prompt) !== null && _a !== void 0 ? _a : config.prompt, config.size, imageData.b64_json_original, imageData.transparent_success);
|
|
5228
5310
|
});
|
|
5229
5311
|
}
|
|
5230
5312
|
/**
|
|
@@ -5343,1019 +5425,1117 @@
|
|
|
5343
5425
|
}
|
|
5344
5426
|
|
|
5345
5427
|
/**
|
|
5346
|
-
*
|
|
5347
|
-
* Automatically handles conversation history
|
|
5428
|
+
* Global AI Context Manager for managing NPC conversations and player context.
|
|
5348
5429
|
*
|
|
5349
|
-
*
|
|
5350
|
-
* -
|
|
5351
|
-
* -
|
|
5352
|
-
* -
|
|
5353
|
-
* - Automatic conversation history management
|
|
5430
|
+
* Features:
|
|
5431
|
+
* - Player description management
|
|
5432
|
+
* - NPC conversation tracking
|
|
5433
|
+
* - Automatic conversation compaction (AutoCompact)
|
|
5354
5434
|
*/
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5435
|
+
/**
|
|
5436
|
+
* Global AI Context Manager
|
|
5437
|
+
* Manages NPC conversations and player context across the application
|
|
5438
|
+
*/
|
|
5439
|
+
class AIContextManager extends EventEmitter {
|
|
5440
|
+
constructor(config) {
|
|
5441
|
+
var _a, _b, _c, _d, _e;
|
|
5358
5442
|
super();
|
|
5359
|
-
this.
|
|
5360
|
-
this.
|
|
5361
|
-
this.
|
|
5362
|
-
|
|
5363
|
-
this.
|
|
5364
|
-
this.
|
|
5365
|
-
this.
|
|
5366
|
-
this.
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5443
|
+
this.playerDescription = null;
|
|
5444
|
+
this.playerPrompt = null;
|
|
5445
|
+
this.playerMemories = new Map();
|
|
5446
|
+
this.npcStates = new Map();
|
|
5447
|
+
this.autoCompactTimer = null;
|
|
5448
|
+
this.chatClientFactory = null;
|
|
5449
|
+
this.logger = Logger.getLogger('AIContextManager');
|
|
5450
|
+
this.config = {
|
|
5451
|
+
enableAutoCompact: (_a = config === null || config === void 0 ? void 0 : config.enableAutoCompact) !== null && _a !== void 0 ? _a : false,
|
|
5452
|
+
autoCompactMinMessages: (_b = config === null || config === void 0 ? void 0 : config.autoCompactMinMessages) !== null && _b !== void 0 ? _b : 20,
|
|
5453
|
+
autoCompactTimeoutSeconds: (_c = config === null || config === void 0 ? void 0 : config.autoCompactTimeoutSeconds) !== null && _c !== void 0 ? _c : 300,
|
|
5454
|
+
autoCompactCheckInterval: (_d = config === null || config === void 0 ? void 0 : config.autoCompactCheckInterval) !== null && _d !== void 0 ? _d : 60000,
|
|
5455
|
+
fastModel: (_e = config === null || config === void 0 ? void 0 : config.fastModel) !== null && _e !== void 0 ? _e : '',
|
|
5456
|
+
};
|
|
5457
|
+
// Start auto-compact check if enabled
|
|
5458
|
+
if (this.config.enableAutoCompact) {
|
|
5459
|
+
this.startAutoCompactCheck();
|
|
5460
|
+
}
|
|
5371
5461
|
}
|
|
5372
|
-
// =====
|
|
5462
|
+
// ===== Singleton Pattern =====
|
|
5373
5463
|
/**
|
|
5374
|
-
*
|
|
5464
|
+
* Get the singleton instance of AIContextManager
|
|
5465
|
+
* Creates a new instance if one doesn't exist
|
|
5375
5466
|
*/
|
|
5376
|
-
|
|
5377
|
-
|
|
5467
|
+
static getInstance(config) {
|
|
5468
|
+
if (!AIContextManager._instance) {
|
|
5469
|
+
AIContextManager._instance = new AIContextManager(config);
|
|
5470
|
+
}
|
|
5471
|
+
return AIContextManager._instance;
|
|
5378
5472
|
}
|
|
5379
|
-
// ===== Character Design & Memory System =====
|
|
5380
5473
|
/**
|
|
5381
|
-
*
|
|
5382
|
-
* The system prompt is composed of CharacterDesign + all Memories.
|
|
5474
|
+
* Reset the singleton instance (useful for testing)
|
|
5383
5475
|
*/
|
|
5384
|
-
|
|
5385
|
-
|
|
5386
|
-
|
|
5476
|
+
static resetInstance() {
|
|
5477
|
+
if (AIContextManager._instance) {
|
|
5478
|
+
AIContextManager._instance.destroy();
|
|
5479
|
+
AIContextManager._instance = null;
|
|
5480
|
+
}
|
|
5481
|
+
}
|
|
5482
|
+
// ===== Configuration =====
|
|
5387
5483
|
/**
|
|
5388
|
-
*
|
|
5484
|
+
* Set the chat client factory for creating chat clients for summarization
|
|
5485
|
+
* Required for compaction to work
|
|
5389
5486
|
*/
|
|
5390
|
-
|
|
5391
|
-
|
|
5487
|
+
setChatClientFactory(factory) {
|
|
5488
|
+
this.chatClientFactory = factory;
|
|
5392
5489
|
}
|
|
5393
5490
|
/**
|
|
5394
|
-
*
|
|
5395
|
-
* This method is kept for backwards compatibility.
|
|
5491
|
+
* Update configuration
|
|
5396
5492
|
*/
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
this.
|
|
5493
|
+
setConfig(config) {
|
|
5494
|
+
const wasAutoCompactEnabled = this.config.enableAutoCompact;
|
|
5495
|
+
this.config = Object.assign(Object.assign({}, this.config), config);
|
|
5496
|
+
// Handle auto-compact state change
|
|
5497
|
+
if (config.enableAutoCompact !== undefined) {
|
|
5498
|
+
if (config.enableAutoCompact && !wasAutoCompactEnabled) {
|
|
5499
|
+
this.startAutoCompactCheck();
|
|
5500
|
+
}
|
|
5501
|
+
else if (!config.enableAutoCompact && wasAutoCompactEnabled) {
|
|
5502
|
+
this.stopAutoCompactCheck();
|
|
5503
|
+
}
|
|
5504
|
+
}
|
|
5400
5505
|
}
|
|
5506
|
+
// ===== Player Description =====
|
|
5401
5507
|
/**
|
|
5402
|
-
*
|
|
5403
|
-
*
|
|
5508
|
+
* Set the player's description for AI context.
|
|
5509
|
+
* Used when generating reply predictions and for NPC context.
|
|
5510
|
+
* @param description Description of the player character
|
|
5404
5511
|
*/
|
|
5405
|
-
|
|
5406
|
-
|
|
5512
|
+
setPlayerDescription(description) {
|
|
5513
|
+
this.playerDescription = description;
|
|
5514
|
+
this.emit('playerDescriptionChanged', description);
|
|
5407
5515
|
}
|
|
5408
5516
|
/**
|
|
5409
|
-
*
|
|
5410
|
-
*
|
|
5517
|
+
* Get the current player description.
|
|
5518
|
+
* @returns The player description, or null if not set
|
|
5519
|
+
*/
|
|
5520
|
+
getPlayerDescription() {
|
|
5521
|
+
return this.playerDescription;
|
|
5522
|
+
}
|
|
5523
|
+
/**
|
|
5524
|
+
* Clear the player description.
|
|
5525
|
+
*/
|
|
5526
|
+
clearPlayerDescription() {
|
|
5527
|
+
this.playerDescription = null;
|
|
5528
|
+
this.emit('playerDescriptionChanged', null);
|
|
5529
|
+
}
|
|
5530
|
+
// ===== Player Prompt & Memory (for Reply Prediction) =====
|
|
5531
|
+
/**
|
|
5532
|
+
* Set the player's character prompt/persona.
|
|
5533
|
+
* This defines how the player character speaks and behaves.
|
|
5534
|
+
* Used when generating reply predictions to match the player's tone.
|
|
5535
|
+
* @param prompt The player character's persona/prompt
|
|
5536
|
+
*/
|
|
5537
|
+
setPlayerPrompt(prompt) {
|
|
5538
|
+
this.playerPrompt = prompt;
|
|
5539
|
+
}
|
|
5540
|
+
/**
|
|
5541
|
+
* Get the current player prompt.
|
|
5542
|
+
* @returns The player prompt, or null if not set
|
|
5543
|
+
*/
|
|
5544
|
+
getPlayerPrompt() {
|
|
5545
|
+
return this.playerPrompt;
|
|
5546
|
+
}
|
|
5547
|
+
/**
|
|
5548
|
+
* Set or update a memory for the player character.
|
|
5549
|
+
* Memories are appended to the player prompt to form the full player context.
|
|
5411
5550
|
* Set memoryContent to null or empty to remove the memory.
|
|
5412
5551
|
* @param memoryName The name/key of the memory
|
|
5413
5552
|
* @param memoryContent The content of the memory. Null or empty to remove.
|
|
5414
5553
|
*/
|
|
5415
|
-
|
|
5554
|
+
setPlayerMemory(memoryName, memoryContent) {
|
|
5416
5555
|
if (!memoryName) {
|
|
5417
5556
|
this.logger.warn('Memory name cannot be empty');
|
|
5418
5557
|
return;
|
|
5419
5558
|
}
|
|
5420
5559
|
if (!memoryContent) {
|
|
5421
5560
|
// Remove memory if content is null or empty
|
|
5422
|
-
|
|
5423
|
-
this.memories.delete(memoryName);
|
|
5424
|
-
this.emit('memory_removed', memoryName);
|
|
5425
|
-
}
|
|
5561
|
+
this.playerMemories.delete(memoryName);
|
|
5426
5562
|
}
|
|
5427
5563
|
else {
|
|
5428
5564
|
// Add or update memory
|
|
5429
|
-
this.
|
|
5430
|
-
this.emit('memory_set', memoryName, memoryContent);
|
|
5565
|
+
this.playerMemories.set(memoryName, memoryContent);
|
|
5431
5566
|
}
|
|
5432
5567
|
}
|
|
5433
5568
|
/**
|
|
5434
|
-
* Get a specific memory by name.
|
|
5569
|
+
* Get a specific player memory by name.
|
|
5435
5570
|
* @param memoryName The name of the memory to retrieve
|
|
5436
5571
|
* @returns The memory content, or undefined if not found
|
|
5437
5572
|
*/
|
|
5438
|
-
|
|
5439
|
-
return this.
|
|
5573
|
+
getPlayerMemory(memoryName) {
|
|
5574
|
+
return this.playerMemories.get(memoryName);
|
|
5440
5575
|
}
|
|
5441
5576
|
/**
|
|
5442
|
-
* Get all memory names currently stored.
|
|
5577
|
+
* Get all player memory names currently stored.
|
|
5443
5578
|
* @returns Array of memory names
|
|
5444
5579
|
*/
|
|
5445
|
-
|
|
5446
|
-
return Array.from(this.
|
|
5580
|
+
getPlayerMemoryNames() {
|
|
5581
|
+
return Array.from(this.playerMemories.keys());
|
|
5447
5582
|
}
|
|
5448
5583
|
/**
|
|
5449
|
-
* Clear all memories (but keep
|
|
5584
|
+
* Clear all player memories (but keep player prompt).
|
|
5450
5585
|
*/
|
|
5451
|
-
|
|
5452
|
-
this.
|
|
5453
|
-
this.emit('memories_cleared');
|
|
5586
|
+
clearPlayerMemories() {
|
|
5587
|
+
this.playerMemories.clear();
|
|
5454
5588
|
}
|
|
5455
5589
|
/**
|
|
5456
|
-
* Build the complete
|
|
5590
|
+
* Build the complete player context from PlayerPrompt + PlayerMemories.
|
|
5591
|
+
* Used by NPCClient for generating reply predictions.
|
|
5592
|
+
* @returns The combined player context string, or null if no context is set
|
|
5457
5593
|
*/
|
|
5458
|
-
|
|
5594
|
+
buildPlayerContext() {
|
|
5459
5595
|
const parts = [];
|
|
5460
|
-
if (this.
|
|
5461
|
-
parts.push(this.
|
|
5596
|
+
if (this.playerPrompt) {
|
|
5597
|
+
parts.push(this.playerPrompt);
|
|
5462
5598
|
}
|
|
5463
|
-
if (this.
|
|
5464
|
-
const memoryStrings = Array.from(this.
|
|
5599
|
+
if (this.playerMemories.size > 0) {
|
|
5600
|
+
const memoryStrings = Array.from(this.playerMemories.entries())
|
|
5465
5601
|
.map(([name, content]) => `[${name}]: ${content}`);
|
|
5466
|
-
parts.push('Memories:\n' + memoryStrings.join('\n'));
|
|
5602
|
+
parts.push('Player Memories:\n' + memoryStrings.join('\n'));
|
|
5603
|
+
}
|
|
5604
|
+
if (parts.length === 0) {
|
|
5605
|
+
return null;
|
|
5467
5606
|
}
|
|
5468
5607
|
return parts.join('\n\n');
|
|
5469
5608
|
}
|
|
5470
|
-
// =====
|
|
5471
|
-
/**
|
|
5472
|
-
* Enable or disable automatic reply prediction
|
|
5473
|
-
*/
|
|
5474
|
-
setGenerateReplyPrediction(enabled) {
|
|
5475
|
-
this.generateReplyPrediction = enabled;
|
|
5476
|
-
}
|
|
5609
|
+
// ===== NPC Tracking =====
|
|
5477
5610
|
/**
|
|
5478
|
-
*
|
|
5611
|
+
* Register an NPC for context management.
|
|
5612
|
+
* @param npc The NPC client to register
|
|
5479
5613
|
*/
|
|
5480
|
-
|
|
5481
|
-
|
|
5614
|
+
registerNpc(npc) {
|
|
5615
|
+
if (!npc)
|
|
5616
|
+
return;
|
|
5617
|
+
if (!this.npcStates.has(npc)) {
|
|
5618
|
+
this.npcStates.set(npc, {
|
|
5619
|
+
lastConversationTime: new Date(),
|
|
5620
|
+
isCompacted: false,
|
|
5621
|
+
compactionCount: 0,
|
|
5622
|
+
});
|
|
5623
|
+
}
|
|
5482
5624
|
}
|
|
5483
5625
|
/**
|
|
5484
|
-
*
|
|
5485
|
-
*
|
|
5486
|
-
* @param count Number of predictions to generate (default: uses predictionCount property)
|
|
5487
|
-
* @returns Array of predicted player replies, or empty array on failure
|
|
5626
|
+
* Unregister an NPC (call when NPC is destroyed/removed).
|
|
5627
|
+
* @param npc The NPC client to unregister
|
|
5488
5628
|
*/
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
this.logger.info('Not enough conversation history to generate predictions');
|
|
5494
|
-
return [];
|
|
5495
|
-
}
|
|
5496
|
-
try {
|
|
5497
|
-
// Get last NPC message
|
|
5498
|
-
const lastNpcMessage = (_a = [...this.history]
|
|
5499
|
-
.reverse()
|
|
5500
|
-
.find(m => m.role === 'assistant')) === null || _a === void 0 ? void 0 : _a.content;
|
|
5501
|
-
if (!lastNpcMessage) {
|
|
5502
|
-
this.logger.info('No NPC message found to generate predictions from');
|
|
5503
|
-
return [];
|
|
5504
|
-
}
|
|
5505
|
-
// Build recent history (last 6 non-system messages)
|
|
5506
|
-
const recentHistory = this.history
|
|
5507
|
-
.filter(m => m.role !== 'system')
|
|
5508
|
-
.slice(-6)
|
|
5509
|
-
.map(m => `${m.role}: ${m.content}`);
|
|
5510
|
-
// Build prompt for prediction generation
|
|
5511
|
-
const prompt = `Based on the conversation history below, generate exactly ${predictionNum} natural and contextually appropriate responses that the player might say next.
|
|
5512
|
-
|
|
5513
|
-
Context:
|
|
5514
|
-
- This is a conversation between a player and an NPC in a game
|
|
5515
|
-
- The NPC just said: "${lastNpcMessage}"
|
|
5516
|
-
|
|
5517
|
-
Conversation history:
|
|
5518
|
-
${recentHistory.join('\n')}
|
|
5519
|
-
|
|
5520
|
-
Requirements:
|
|
5521
|
-
1. Each response should be 1-2 sentences maximum
|
|
5522
|
-
2. Responses should be diverse in tone and intent
|
|
5523
|
-
3. Include a mix of questions, statements, and action-oriented responses
|
|
5524
|
-
4. Responses should feel natural for a player character
|
|
5525
|
-
|
|
5526
|
-
Output ONLY a JSON array of ${predictionNum} strings, nothing else:
|
|
5527
|
-
["response1", "response2", "response3", "response4"]`;
|
|
5528
|
-
const result = await this.chatClient.textGeneration({
|
|
5529
|
-
messages: [{ role: 'user', content: prompt }],
|
|
5530
|
-
temperature: 0.8,
|
|
5531
|
-
model: this.fastModel,
|
|
5532
|
-
});
|
|
5533
|
-
if (!result.content) {
|
|
5534
|
-
this.logger.warn('Failed to generate predictions: empty response');
|
|
5535
|
-
return [];
|
|
5536
|
-
}
|
|
5537
|
-
// Parse JSON response
|
|
5538
|
-
const predictions = this.parsePredictionsFromJson(result.content, predictionNum);
|
|
5539
|
-
if (predictions.length > 0) {
|
|
5540
|
-
this.emit('replyPredictions', predictions);
|
|
5541
|
-
}
|
|
5542
|
-
return predictions;
|
|
5543
|
-
}
|
|
5544
|
-
catch (error) {
|
|
5545
|
-
this.logger.error('Error generating predictions:', error);
|
|
5546
|
-
return [];
|
|
5547
|
-
}
|
|
5629
|
+
unregisterNpc(npc) {
|
|
5630
|
+
if (!npc)
|
|
5631
|
+
return;
|
|
5632
|
+
this.npcStates.delete(npc);
|
|
5548
5633
|
}
|
|
5549
5634
|
/**
|
|
5550
|
-
*
|
|
5635
|
+
* Record that a conversation occurred with an NPC.
|
|
5636
|
+
* Called after each Talk() exchange.
|
|
5637
|
+
* @param npc The NPC client that had a conversation
|
|
5551
5638
|
*/
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
if (startIndex === -1 || endIndex === -1 || endIndex <= startIndex) {
|
|
5558
|
-
this.logger.warn('Could not find JSON array in prediction response');
|
|
5559
|
-
return this.extractPredictionsFromText(response, expectedCount);
|
|
5560
|
-
}
|
|
5561
|
-
const jsonArray = response.substring(startIndex, endIndex + 1);
|
|
5562
|
-
const parsed = JSON.parse(jsonArray);
|
|
5563
|
-
if (Array.isArray(parsed)) {
|
|
5564
|
-
return parsed
|
|
5565
|
-
.filter(item => typeof item === 'string' && item.trim())
|
|
5566
|
-
.slice(0, expectedCount);
|
|
5567
|
-
}
|
|
5568
|
-
return [];
|
|
5569
|
-
}
|
|
5570
|
-
catch (error) {
|
|
5571
|
-
this.logger.warn('Failed to parse predictions JSON:', error);
|
|
5572
|
-
return this.extractPredictionsFromText(response, expectedCount);
|
|
5639
|
+
recordConversation(npc) {
|
|
5640
|
+
if (!npc)
|
|
5641
|
+
return;
|
|
5642
|
+
if (!this.npcStates.has(npc)) {
|
|
5643
|
+
this.registerNpc(npc);
|
|
5573
5644
|
}
|
|
5645
|
+
const state = this.npcStates.get(npc);
|
|
5646
|
+
state.lastConversationTime = new Date();
|
|
5647
|
+
state.isCompacted = false; // Reset compaction flag on new conversation
|
|
5574
5648
|
}
|
|
5575
5649
|
/**
|
|
5576
|
-
*
|
|
5650
|
+
* Get all registered NPCs
|
|
5577
5651
|
*/
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
const lines = response.split(/[\n\r]+/).filter(line => line.trim());
|
|
5581
|
-
for (const line of lines) {
|
|
5582
|
-
let cleaned = line.trim();
|
|
5583
|
-
// Skip empty lines and JSON brackets
|
|
5584
|
-
if (!cleaned || cleaned === '[' || cleaned === ']')
|
|
5585
|
-
continue;
|
|
5586
|
-
// Remove common prefixes like "1.", "- ", etc.
|
|
5587
|
-
if (/^\d+\./.test(cleaned)) {
|
|
5588
|
-
cleaned = cleaned.replace(/^\d+\.\s*/, '');
|
|
5589
|
-
}
|
|
5590
|
-
else if (cleaned.startsWith('- ')) {
|
|
5591
|
-
cleaned = cleaned.substring(2);
|
|
5592
|
-
}
|
|
5593
|
-
// Remove surrounding quotes
|
|
5594
|
-
if (cleaned.startsWith('"') && cleaned.endsWith('"')) {
|
|
5595
|
-
cleaned = cleaned.slice(1, -1);
|
|
5596
|
-
}
|
|
5597
|
-
// Remove trailing comma
|
|
5598
|
-
if (cleaned.endsWith(',')) {
|
|
5599
|
-
cleaned = cleaned.slice(0, -1).trim();
|
|
5600
|
-
}
|
|
5601
|
-
if (cleaned && predictions.length < expectedCount) {
|
|
5602
|
-
predictions.push(cleaned);
|
|
5603
|
-
}
|
|
5604
|
-
}
|
|
5605
|
-
return predictions;
|
|
5652
|
+
getRegisteredNpcs() {
|
|
5653
|
+
return Array.from(this.npcStates.keys());
|
|
5606
5654
|
}
|
|
5607
5655
|
/**
|
|
5608
|
-
*
|
|
5656
|
+
* Get the conversation state for an NPC
|
|
5609
5657
|
*/
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
return;
|
|
5613
|
-
// Fire and forget - don't block the main response
|
|
5614
|
-
this.generateReplyPredictions().catch(err => {
|
|
5615
|
-
this.logger.error('Background prediction generation failed:', err);
|
|
5616
|
-
});
|
|
5658
|
+
getNpcState(npc) {
|
|
5659
|
+
return this.npcStates.get(npc);
|
|
5617
5660
|
}
|
|
5618
|
-
// =====
|
|
5661
|
+
// ===== Auto Compaction =====
|
|
5619
5662
|
/**
|
|
5620
|
-
*
|
|
5663
|
+
* Check if an NPC is eligible for compaction.
|
|
5664
|
+
* @param npc The NPC to check
|
|
5665
|
+
* @returns True if eligible for compaction
|
|
5621
5666
|
*/
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
// Trim history if needed
|
|
5642
|
-
this.trimHistory();
|
|
5643
|
-
this.emit('response', result.content);
|
|
5644
|
-
// Trigger reply prediction generation (fire and forget)
|
|
5645
|
-
this.triggerReplyPrediction();
|
|
5646
|
-
return result.content;
|
|
5647
|
-
}
|
|
5648
|
-
finally {
|
|
5649
|
-
this._isTalking = false;
|
|
5650
|
-
}
|
|
5667
|
+
isEligibleForCompaction(npc) {
|
|
5668
|
+
if (!npc)
|
|
5669
|
+
return false;
|
|
5670
|
+
const state = this.npcStates.get(npc);
|
|
5671
|
+
if (!state)
|
|
5672
|
+
return false;
|
|
5673
|
+
// Check if already compacted since last conversation
|
|
5674
|
+
if (state.isCompacted)
|
|
5675
|
+
return false;
|
|
5676
|
+
// Check message count
|
|
5677
|
+
const history = npc.getHistory();
|
|
5678
|
+
const nonSystemMessages = history.filter(m => m.role !== 'system').length;
|
|
5679
|
+
if (nonSystemMessages < this.config.autoCompactMinMessages)
|
|
5680
|
+
return false;
|
|
5681
|
+
// Check time since last conversation
|
|
5682
|
+
const timeSinceLastConversation = (Date.now() - state.lastConversationTime.getTime()) / 1000;
|
|
5683
|
+
if (timeSinceLastConversation < this.config.autoCompactTimeoutSeconds)
|
|
5684
|
+
return false;
|
|
5685
|
+
return true;
|
|
5651
5686
|
}
|
|
5652
5687
|
/**
|
|
5653
|
-
*
|
|
5688
|
+
* Manually trigger conversation compaction for a specific NPC.
|
|
5689
|
+
* Summarizes the conversation history and stores it as a memory.
|
|
5690
|
+
* @param npc The NPC to compact
|
|
5691
|
+
* @returns True if compaction succeeded
|
|
5654
5692
|
*/
|
|
5655
|
-
async
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
const userMessage = { role: 'user', content: message };
|
|
5660
|
-
this.history.push(userMessage);
|
|
5661
|
-
// Build messages array with system prompt
|
|
5662
|
-
const messages = [
|
|
5663
|
-
{ role: 'system', content: this.buildSystemPrompt() },
|
|
5664
|
-
...this.history,
|
|
5665
|
-
];
|
|
5666
|
-
// Generate response
|
|
5667
|
-
await this.chatClient.textGenerationStream({
|
|
5668
|
-
messages,
|
|
5669
|
-
temperature: this.temperature,
|
|
5670
|
-
onChunk,
|
|
5671
|
-
onComplete: (fullText) => {
|
|
5672
|
-
this._isTalking = false;
|
|
5673
|
-
// Add assistant response to history
|
|
5674
|
-
const assistantMessage = { role: 'assistant', content: fullText };
|
|
5675
|
-
this.history.push(assistantMessage);
|
|
5676
|
-
// Trim history if needed
|
|
5677
|
-
this.trimHistory();
|
|
5678
|
-
this.emit('response', fullText);
|
|
5679
|
-
// Trigger reply prediction generation (fire and forget)
|
|
5680
|
-
this.triggerReplyPrediction();
|
|
5681
|
-
if (onComplete) {
|
|
5682
|
-
onComplete(fullText);
|
|
5683
|
-
}
|
|
5684
|
-
},
|
|
5685
|
-
});
|
|
5686
|
-
}
|
|
5687
|
-
catch (error) {
|
|
5688
|
-
this._isTalking = false;
|
|
5689
|
-
throw error;
|
|
5693
|
+
async compactConversation(npc) {
|
|
5694
|
+
if (!npc) {
|
|
5695
|
+
this.logger.warn('Cannot compact: NPC is null');
|
|
5696
|
+
return false;
|
|
5690
5697
|
}
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
* @deprecated Use talkWithActions instead for NPC decision-making with actions
|
|
5695
|
-
*/
|
|
5696
|
-
async talkStructured(message, schemaName) {
|
|
5697
|
-
this.logger.warn('talkStructured is deprecated. Use talkWithActions instead for NPC decision-making with actions.');
|
|
5698
|
-
// Add user message to history
|
|
5699
|
-
const userMessage = { role: 'user', content: message };
|
|
5700
|
-
this.history.push(userMessage);
|
|
5701
|
-
// Generate structured response
|
|
5702
|
-
const result = await this.chatClient.generateStructured({
|
|
5703
|
-
schemaName,
|
|
5704
|
-
prompt: message,
|
|
5705
|
-
messages: [{ role: 'system', content: this.buildSystemPrompt() }, ...this.history],
|
|
5706
|
-
temperature: this.temperature,
|
|
5707
|
-
});
|
|
5708
|
-
// Add a text representation to history
|
|
5709
|
-
const assistantMessage = {
|
|
5710
|
-
role: 'assistant',
|
|
5711
|
-
content: JSON.stringify(result),
|
|
5712
|
-
};
|
|
5713
|
-
this.history.push(assistantMessage);
|
|
5714
|
-
this.trimHistory();
|
|
5715
|
-
return result;
|
|
5716
|
-
}
|
|
5717
|
-
/**
|
|
5718
|
-
* Talk to the NPC with available actions (non-streaming)
|
|
5719
|
-
* @param message The message to send
|
|
5720
|
-
* @param actions List of actions the NPC can perform
|
|
5721
|
-
* @returns Response containing text and any action calls
|
|
5722
|
-
*/
|
|
5723
|
-
async talkWithActions(message, actions) {
|
|
5724
|
-
this._isTalking = true;
|
|
5725
|
-
try {
|
|
5726
|
-
// Add user message to history
|
|
5727
|
-
const userMessage = { role: 'user', content: message };
|
|
5728
|
-
this.history.push(userMessage);
|
|
5729
|
-
// Convert NpcActions to ChatTools
|
|
5730
|
-
const tools = actions
|
|
5731
|
-
.filter(a => a && a.enabled !== false)
|
|
5732
|
-
.map(a => npcActionToTool(a));
|
|
5733
|
-
// Build messages array with system prompt
|
|
5734
|
-
const messages = [
|
|
5735
|
-
{ role: 'system', content: this.buildSystemPrompt() },
|
|
5736
|
-
...this.history,
|
|
5737
|
-
];
|
|
5738
|
-
// Generate response with tools
|
|
5739
|
-
const result = await this.chatClient.textGenerationWithTools({
|
|
5740
|
-
messages,
|
|
5741
|
-
temperature: this.temperature,
|
|
5742
|
-
tools,
|
|
5743
|
-
tool_choice: 'auto',
|
|
5744
|
-
});
|
|
5745
|
-
// Build response
|
|
5746
|
-
const response = {
|
|
5747
|
-
text: result.content || '',
|
|
5748
|
-
actionCalls: [],
|
|
5749
|
-
hasActions: false,
|
|
5750
|
-
};
|
|
5751
|
-
// Extract tool calls if any
|
|
5752
|
-
if (result.tool_calls) {
|
|
5753
|
-
response.actionCalls = result.tool_calls.map(tc => ({
|
|
5754
|
-
id: tc.id,
|
|
5755
|
-
actionName: tc.function.name,
|
|
5756
|
-
arguments: this.parseToolArguments(tc.function.arguments),
|
|
5757
|
-
}));
|
|
5758
|
-
response.hasActions = response.actionCalls.length > 0;
|
|
5759
|
-
}
|
|
5760
|
-
// Add assistant response to history
|
|
5761
|
-
const assistantMessage = {
|
|
5762
|
-
role: 'assistant',
|
|
5763
|
-
content: response.text,
|
|
5764
|
-
tool_calls: result.tool_calls,
|
|
5765
|
-
};
|
|
5766
|
-
this.history.push(assistantMessage);
|
|
5767
|
-
this.trimHistory();
|
|
5768
|
-
this.emit('response', response.text);
|
|
5769
|
-
if (response.hasActions) {
|
|
5770
|
-
this.emit('actions', response.actionCalls);
|
|
5771
|
-
}
|
|
5772
|
-
// Trigger reply prediction generation (fire and forget)
|
|
5773
|
-
this.triggerReplyPrediction();
|
|
5774
|
-
return response;
|
|
5698
|
+
if (!this.chatClientFactory) {
|
|
5699
|
+
this.logger.error('Cannot compact: No chat client factory set. Call setChatClientFactory() first.');
|
|
5700
|
+
return false;
|
|
5775
5701
|
}
|
|
5776
|
-
|
|
5777
|
-
|
|
5702
|
+
const history = npc.getHistory();
|
|
5703
|
+
const nonSystemMessages = history.filter(m => m.role !== 'system');
|
|
5704
|
+
if (nonSystemMessages.length < 2) {
|
|
5705
|
+
this.logger.info('Skipping compaction: not enough messages');
|
|
5706
|
+
return false;
|
|
5778
5707
|
}
|
|
5779
|
-
}
|
|
5780
|
-
/**
|
|
5781
|
-
* Talk to the NPC with actions (streaming)
|
|
5782
|
-
* Text streams first, action calls are returned in onComplete
|
|
5783
|
-
*/
|
|
5784
|
-
async talkWithActionsStream(message, actions, onChunk, onComplete) {
|
|
5785
|
-
this._isTalking = true;
|
|
5786
5708
|
try {
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5797
|
-
|
|
5798
|
-
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
const response = {
|
|
5810
|
-
text: result.content || '',
|
|
5811
|
-
actionCalls: [],
|
|
5812
|
-
hasActions: false,
|
|
5813
|
-
};
|
|
5814
|
-
// Extract tool calls if any
|
|
5815
|
-
if (result.tool_calls) {
|
|
5816
|
-
response.actionCalls = result.tool_calls.map(tc => ({
|
|
5817
|
-
id: tc.id,
|
|
5818
|
-
actionName: tc.function.name,
|
|
5819
|
-
arguments: this.parseToolArguments(tc.function.arguments),
|
|
5820
|
-
}));
|
|
5821
|
-
response.hasActions = response.actionCalls.length > 0;
|
|
5822
|
-
}
|
|
5823
|
-
// Add assistant response to history
|
|
5824
|
-
const assistantMessage = {
|
|
5825
|
-
role: 'assistant',
|
|
5826
|
-
content: response.text,
|
|
5827
|
-
tool_calls: result.tool_calls,
|
|
5828
|
-
};
|
|
5829
|
-
this.history.push(assistantMessage);
|
|
5830
|
-
this.trimHistory();
|
|
5831
|
-
this.emit('response', response.text);
|
|
5832
|
-
if (response.hasActions) {
|
|
5833
|
-
this.emit('actions', response.actionCalls);
|
|
5834
|
-
}
|
|
5835
|
-
// Trigger reply prediction generation (fire and forget)
|
|
5836
|
-
this.triggerReplyPrediction();
|
|
5837
|
-
if (onComplete) {
|
|
5838
|
-
onComplete(response);
|
|
5839
|
-
}
|
|
5840
|
-
},
|
|
5709
|
+
this.logger.info(`Starting compaction (${nonSystemMessages.length} messages)`);
|
|
5710
|
+
// Build conversation text for summarization
|
|
5711
|
+
const conversationText = nonSystemMessages
|
|
5712
|
+
.map(m => `${m.role}: ${m.content}`)
|
|
5713
|
+
.join('\n');
|
|
5714
|
+
// Create summarization prompt
|
|
5715
|
+
const summaryPrompt = `Summarize the following conversation concisely. Focus on:
|
|
5716
|
+
1. Key topics discussed
|
|
5717
|
+
2. Important information exchanged
|
|
5718
|
+
3. Any decisions or commitments made
|
|
5719
|
+
4. The emotional tone
|
|
5720
|
+
|
|
5721
|
+
Keep the summary under 200 words. Write in third person.
|
|
5722
|
+
|
|
5723
|
+
Conversation:
|
|
5724
|
+
${conversationText}`;
|
|
5725
|
+
// Use chat client for summarization
|
|
5726
|
+
const chatClient = this.chatClientFactory();
|
|
5727
|
+
const result = await chatClient.textGeneration({
|
|
5728
|
+
messages: [{ role: 'user', content: summaryPrompt }],
|
|
5729
|
+
temperature: 0.5,
|
|
5730
|
+
model: this.config.fastModel || undefined,
|
|
5841
5731
|
});
|
|
5732
|
+
if (!result.content) {
|
|
5733
|
+
const error = 'Empty response from summarization';
|
|
5734
|
+
this.logger.error(`Compaction failed: ${error}`);
|
|
5735
|
+
this.emit('compactionFailed', npc, error);
|
|
5736
|
+
return false;
|
|
5737
|
+
}
|
|
5738
|
+
// Clear history and add summary as memory
|
|
5739
|
+
npc.clearHistory();
|
|
5740
|
+
npc.setMemory('PreviousConversationSummary', result.content);
|
|
5741
|
+
// Update state
|
|
5742
|
+
const state = this.npcStates.get(npc);
|
|
5743
|
+
if (state) {
|
|
5744
|
+
state.isCompacted = true;
|
|
5745
|
+
state.compactionCount++;
|
|
5746
|
+
}
|
|
5747
|
+
this.logger.info(`Compaction completed. Summary: ${result.content.substring(0, 100)}...`);
|
|
5748
|
+
this.emit('npcCompacted', npc);
|
|
5749
|
+
return true;
|
|
5842
5750
|
}
|
|
5843
5751
|
catch (error) {
|
|
5844
|
-
|
|
5845
|
-
|
|
5752
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
5753
|
+
this.logger.error(`Compaction error: ${errorMessage}`);
|
|
5754
|
+
this.emit('compactionFailed', npc, errorMessage);
|
|
5755
|
+
return false;
|
|
5846
5756
|
}
|
|
5847
5757
|
}
|
|
5848
|
-
// ===== Action Results Reporting =====
|
|
5849
5758
|
/**
|
|
5850
|
-
*
|
|
5851
|
-
*
|
|
5759
|
+
* Compact all registered NPCs that meet the eligibility criteria.
|
|
5760
|
+
* @returns Number of NPCs successfully compacted
|
|
5852
5761
|
*/
|
|
5853
|
-
|
|
5854
|
-
|
|
5855
|
-
|
|
5856
|
-
|
|
5857
|
-
|
|
5858
|
-
|
|
5859
|
-
|
|
5762
|
+
async compactAllEligible() {
|
|
5763
|
+
const eligibleNpcs = Array.from(this.npcStates.keys()).filter(npc => this.isEligibleForCompaction(npc));
|
|
5764
|
+
if (eligibleNpcs.length === 0) {
|
|
5765
|
+
return 0;
|
|
5766
|
+
}
|
|
5767
|
+
this.logger.info(`Compacting ${eligibleNpcs.length} eligible NPCs`);
|
|
5768
|
+
let successCount = 0;
|
|
5769
|
+
for (const npc of eligibleNpcs) {
|
|
5770
|
+
const success = await this.compactConversation(npc);
|
|
5771
|
+
if (success)
|
|
5772
|
+
successCount++;
|
|
5860
5773
|
}
|
|
5774
|
+
return successCount;
|
|
5861
5775
|
}
|
|
5776
|
+
// ===== Auto Compact Timer =====
|
|
5862
5777
|
/**
|
|
5863
|
-
*
|
|
5778
|
+
* Start the auto-compact check timer
|
|
5864
5779
|
*/
|
|
5865
|
-
|
|
5866
|
-
this.
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5780
|
+
startAutoCompactCheck() {
|
|
5781
|
+
if (this.autoCompactTimer) {
|
|
5782
|
+
this.stopAutoCompactCheck();
|
|
5783
|
+
}
|
|
5784
|
+
this.autoCompactTimer = setInterval(() => {
|
|
5785
|
+
this.runAutoCompactCheck();
|
|
5786
|
+
}, this.config.autoCompactCheckInterval);
|
|
5871
5787
|
}
|
|
5872
5788
|
/**
|
|
5873
|
-
*
|
|
5789
|
+
* Stop the auto-compact check timer
|
|
5874
5790
|
*/
|
|
5875
|
-
|
|
5876
|
-
|
|
5877
|
-
|
|
5791
|
+
stopAutoCompactCheck() {
|
|
5792
|
+
if (this.autoCompactTimer) {
|
|
5793
|
+
clearInterval(this.autoCompactTimer);
|
|
5794
|
+
this.autoCompactTimer = null;
|
|
5878
5795
|
}
|
|
5879
|
-
|
|
5880
|
-
|
|
5796
|
+
}
|
|
5797
|
+
/**
|
|
5798
|
+
* Run a single auto-compact check
|
|
5799
|
+
*/
|
|
5800
|
+
async runAutoCompactCheck() {
|
|
5801
|
+
if (!this.config.enableAutoCompact)
|
|
5802
|
+
return;
|
|
5803
|
+
const eligibleNpcs = Array.from(this.npcStates.keys()).filter(npc => this.isEligibleForCompaction(npc));
|
|
5804
|
+
for (const npc of eligibleNpcs) {
|
|
5805
|
+
// Fire and forget - don't block
|
|
5806
|
+
this.compactConversation(npc).catch(err => {
|
|
5807
|
+
this.logger.error('Auto-compact error:', err);
|
|
5808
|
+
});
|
|
5881
5809
|
}
|
|
5882
5810
|
}
|
|
5883
|
-
// =====
|
|
5811
|
+
// ===== Lifecycle =====
|
|
5884
5812
|
/**
|
|
5885
|
-
*
|
|
5813
|
+
* Enable auto-compaction
|
|
5886
5814
|
*/
|
|
5887
|
-
|
|
5888
|
-
|
|
5815
|
+
enableAutoCompact() {
|
|
5816
|
+
this.config.enableAutoCompact = true;
|
|
5817
|
+
this.startAutoCompactCheck();
|
|
5889
5818
|
}
|
|
5890
5819
|
/**
|
|
5891
|
-
*
|
|
5820
|
+
* Disable auto-compaction
|
|
5892
5821
|
*/
|
|
5893
|
-
|
|
5894
|
-
|
|
5822
|
+
disableAutoCompact() {
|
|
5823
|
+
this.config.enableAutoCompact = false;
|
|
5824
|
+
this.stopAutoCompactCheck();
|
|
5895
5825
|
}
|
|
5896
5826
|
/**
|
|
5897
|
-
*
|
|
5898
|
-
* The character design and memories will be preserved.
|
|
5827
|
+
* Clean up resources
|
|
5899
5828
|
*/
|
|
5900
|
-
|
|
5829
|
+
destroy() {
|
|
5830
|
+
this.stopAutoCompactCheck();
|
|
5831
|
+
this.npcStates.clear();
|
|
5832
|
+
this.playerDescription = null;
|
|
5833
|
+
this.playerPrompt = null;
|
|
5834
|
+
this.playerMemories.clear();
|
|
5835
|
+
this.removeAllListeners();
|
|
5836
|
+
}
|
|
5837
|
+
}
|
|
5838
|
+
AIContextManager._instance = null;
|
|
5839
|
+
/**
|
|
5840
|
+
* Default AIContextManager instance
|
|
5841
|
+
* Can be used as a global context manager
|
|
5842
|
+
*/
|
|
5843
|
+
const defaultContextManager = AIContextManager.getInstance();
|
|
5844
|
+
|
|
5845
|
+
/**
|
|
5846
|
+
* NPC Client for simplified conversation management
|
|
5847
|
+
* Automatically handles conversation history
|
|
5848
|
+
*
|
|
5849
|
+
* Key Features:
|
|
5850
|
+
* - Call talk() for all interactions - actions are handled automatically
|
|
5851
|
+
* - Memory system for persistent NPC context
|
|
5852
|
+
* - Reply prediction for suggesting player responses
|
|
5853
|
+
* - Automatic conversation history management
|
|
5854
|
+
*/
|
|
5855
|
+
class NPCClient extends EventEmitter {
|
|
5856
|
+
constructor(chatClient, config) {
|
|
5857
|
+
var _a, _b, _c;
|
|
5858
|
+
super();
|
|
5859
|
+
this._isTalking = false;
|
|
5860
|
+
this.logger = Logger.getLogger('NPCClient');
|
|
5861
|
+
this.chatClient = chatClient;
|
|
5862
|
+
// Support both characterDesign and legacy systemPrompt
|
|
5863
|
+
this.characterDesign = (config === null || config === void 0 ? void 0 : config.characterDesign) || (config === null || config === void 0 ? void 0 : config.systemPrompt) || 'You are a helpful assistant.';
|
|
5864
|
+
this.temperature = (_a = config === null || config === void 0 ? void 0 : config.temperature) !== null && _a !== void 0 ? _a : 0.7;
|
|
5865
|
+
this.maxHistoryLength = (config === null || config === void 0 ? void 0 : config.maxHistoryLength) || 50;
|
|
5866
|
+
this.generateReplyPrediction = (_b = config === null || config === void 0 ? void 0 : config.generateReplyPrediction) !== null && _b !== void 0 ? _b : false;
|
|
5867
|
+
this.predictionCount = Math.max(2, Math.min(6, (_c = config === null || config === void 0 ? void 0 : config.predictionCount) !== null && _c !== void 0 ? _c : 4));
|
|
5868
|
+
this.fastModel = config === null || config === void 0 ? void 0 : config.fastModel;
|
|
5901
5869
|
this.history = [];
|
|
5902
|
-
this.
|
|
5870
|
+
this.memories = new Map();
|
|
5903
5871
|
}
|
|
5872
|
+
// ===== State Properties =====
|
|
5904
5873
|
/**
|
|
5905
|
-
*
|
|
5906
|
-
* @returns true if reverted, false if not enough history
|
|
5874
|
+
* Whether the NPC is currently processing a request
|
|
5907
5875
|
*/
|
|
5908
|
-
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5876
|
+
get isTalking() {
|
|
5877
|
+
return this._isTalking;
|
|
5878
|
+
}
|
|
5879
|
+
// ===== Character Design & Memory System =====
|
|
5880
|
+
/**
|
|
5881
|
+
* Set the character design for the NPC.
|
|
5882
|
+
* The system prompt is composed of CharacterDesign + all Memories.
|
|
5883
|
+
*/
|
|
5884
|
+
setCharacterDesign(design) {
|
|
5885
|
+
this.characterDesign = design;
|
|
5886
|
+
}
|
|
5887
|
+
/**
|
|
5888
|
+
* Get the current character design
|
|
5889
|
+
*/
|
|
5890
|
+
getCharacterDesign() {
|
|
5891
|
+
return this.characterDesign;
|
|
5892
|
+
}
|
|
5893
|
+
/**
|
|
5894
|
+
* @deprecated Use setCharacterDesign instead.
|
|
5895
|
+
* This method is kept for backwards compatibility.
|
|
5896
|
+
*/
|
|
5897
|
+
setSystemPrompt(prompt) {
|
|
5898
|
+
this.logger.warn('setSystemPrompt is deprecated. Use setCharacterDesign instead.');
|
|
5899
|
+
this.setCharacterDesign(prompt);
|
|
5900
|
+
}
|
|
5901
|
+
/**
|
|
5902
|
+
* @deprecated Use getCharacterDesign instead.
|
|
5903
|
+
* This method is kept for backwards compatibility.
|
|
5904
|
+
*/
|
|
5905
|
+
getSystemPrompt() {
|
|
5906
|
+
return this.buildSystemPrompt();
|
|
5907
|
+
}
|
|
5908
|
+
/**
|
|
5909
|
+
* Set or update a memory for the NPC.
|
|
5910
|
+
* Memories are appended to the character design to form the system prompt.
|
|
5911
|
+
* Set memoryContent to null or empty to remove the memory.
|
|
5912
|
+
* @param memoryName The name/key of the memory
|
|
5913
|
+
* @param memoryContent The content of the memory. Null or empty to remove.
|
|
5914
|
+
*/
|
|
5915
|
+
setMemory(memoryName, memoryContent) {
|
|
5916
|
+
if (!memoryName) {
|
|
5917
|
+
this.logger.warn('Memory name cannot be empty');
|
|
5918
|
+
return;
|
|
5919
|
+
}
|
|
5920
|
+
if (!memoryContent) {
|
|
5921
|
+
// Remove memory if content is null or empty
|
|
5922
|
+
if (this.memories.has(memoryName)) {
|
|
5923
|
+
this.memories.delete(memoryName);
|
|
5924
|
+
this.emit('memory_removed', memoryName);
|
|
5918
5925
|
}
|
|
5919
5926
|
}
|
|
5920
|
-
|
|
5921
|
-
//
|
|
5922
|
-
this.
|
|
5923
|
-
this.
|
|
5924
|
-
this.emit('history_reverted');
|
|
5925
|
-
return true;
|
|
5927
|
+
else {
|
|
5928
|
+
// Add or update memory
|
|
5929
|
+
this.memories.set(memoryName, memoryContent);
|
|
5930
|
+
this.emit('memory_set', memoryName, memoryContent);
|
|
5926
5931
|
}
|
|
5927
|
-
return false;
|
|
5928
5932
|
}
|
|
5929
5933
|
/**
|
|
5930
|
-
*
|
|
5931
|
-
* @param
|
|
5932
|
-
* @returns
|
|
5934
|
+
* Get a specific memory by name.
|
|
5935
|
+
* @param memoryName The name of the memory to retrieve
|
|
5936
|
+
* @returns The memory content, or undefined if not found
|
|
5933
5937
|
*/
|
|
5934
|
-
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
|
|
5938
|
+
getMemory(memoryName) {
|
|
5939
|
+
return this.memories.get(memoryName);
|
|
5940
|
+
}
|
|
5941
|
+
/**
|
|
5942
|
+
* Get all memory names currently stored.
|
|
5943
|
+
* @returns Array of memory names
|
|
5944
|
+
*/
|
|
5945
|
+
getMemoryNames() {
|
|
5946
|
+
return Array.from(this.memories.keys());
|
|
5947
|
+
}
|
|
5948
|
+
/**
|
|
5949
|
+
* Clear all memories (but keep character design).
|
|
5950
|
+
*/
|
|
5951
|
+
clearMemories() {
|
|
5952
|
+
this.memories.clear();
|
|
5953
|
+
this.emit('memories_cleared');
|
|
5954
|
+
}
|
|
5955
|
+
/**
|
|
5956
|
+
* Build the complete system prompt from CharacterDesign + Memories.
|
|
5957
|
+
*/
|
|
5958
|
+
buildSystemPrompt() {
|
|
5959
|
+
const parts = [];
|
|
5960
|
+
if (this.characterDesign) {
|
|
5961
|
+
parts.push(this.characterDesign);
|
|
5943
5962
|
}
|
|
5944
|
-
|
|
5963
|
+
if (this.memories.size > 0) {
|
|
5964
|
+
const memoryStrings = Array.from(this.memories.entries())
|
|
5965
|
+
.map(([name, content]) => `[${name}]: ${content}`);
|
|
5966
|
+
parts.push('Memories:\n' + memoryStrings.join('\n'));
|
|
5967
|
+
}
|
|
5968
|
+
return parts.join('\n\n');
|
|
5969
|
+
}
|
|
5970
|
+
// ===== Reply Prediction =====
|
|
5971
|
+
/**
|
|
5972
|
+
* Enable or disable automatic reply prediction
|
|
5973
|
+
*/
|
|
5974
|
+
setGenerateReplyPrediction(enabled) {
|
|
5975
|
+
this.generateReplyPrediction = enabled;
|
|
5945
5976
|
}
|
|
5946
5977
|
/**
|
|
5947
|
-
*
|
|
5948
|
-
* @deprecated Use revertHistory() or revertChatMessages() instead
|
|
5978
|
+
* Set the number of predictions to generate
|
|
5949
5979
|
*/
|
|
5950
|
-
|
|
5951
|
-
|
|
5952
|
-
this.history = this.history.slice(0, index + 1);
|
|
5953
|
-
this.emit('history_reverted', index);
|
|
5954
|
-
}
|
|
5980
|
+
setPredictionCount(count) {
|
|
5981
|
+
this.predictionCount = Math.max(2, Math.min(6, count));
|
|
5955
5982
|
}
|
|
5956
5983
|
/**
|
|
5957
|
-
*
|
|
5984
|
+
* Manually generate reply predictions based on current conversation.
|
|
5985
|
+
* Uses the fast model for quick generation.
|
|
5986
|
+
* @param tempPrompt Optional temporary prompt to influence the prediction style/tone
|
|
5987
|
+
* @param count Number of predictions to generate (default: uses predictionCount property)
|
|
5988
|
+
* @returns Array of predicted player replies, or empty array on failure
|
|
5958
5989
|
*/
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
this.
|
|
5990
|
+
async generateReplyPredictions(tempPrompt, count) {
|
|
5991
|
+
var _a;
|
|
5992
|
+
const predictionNum = count !== null && count !== void 0 ? count : this.predictionCount;
|
|
5993
|
+
if (this.history.length < 2) {
|
|
5994
|
+
this.logger.info('Not enough conversation history to generate predictions');
|
|
5995
|
+
return [];
|
|
5996
|
+
}
|
|
5997
|
+
try {
|
|
5998
|
+
// Get last NPC message
|
|
5999
|
+
const lastNpcMessage = (_a = [...this.history]
|
|
6000
|
+
.reverse()
|
|
6001
|
+
.find(m => m.role === 'assistant')) === null || _a === void 0 ? void 0 : _a.content;
|
|
6002
|
+
if (!lastNpcMessage) {
|
|
6003
|
+
this.logger.info('No NPC message found to generate predictions from');
|
|
6004
|
+
return [];
|
|
6005
|
+
}
|
|
6006
|
+
// Build recent history (last 6 non-system messages)
|
|
6007
|
+
const recentHistory = this.history
|
|
6008
|
+
.filter(m => m.role !== 'system')
|
|
6009
|
+
.slice(-6)
|
|
6010
|
+
.map(m => `${m.role}: ${m.content}`);
|
|
6011
|
+
// Get player context from AIContextManager
|
|
6012
|
+
const contextManager = AIContextManager.getInstance();
|
|
6013
|
+
const playerContext = contextManager.buildPlayerContext();
|
|
6014
|
+
// Build player character section
|
|
6015
|
+
let playerCharacterSection = '';
|
|
6016
|
+
if (playerContext || tempPrompt) {
|
|
6017
|
+
playerCharacterSection = '\nPlayer Character:\n';
|
|
6018
|
+
if (playerContext) {
|
|
6019
|
+
playerCharacterSection += playerContext + '\n';
|
|
6020
|
+
}
|
|
6021
|
+
if (tempPrompt) {
|
|
6022
|
+
playerCharacterSection += `Additional guidance: ${tempPrompt}\n`;
|
|
6023
|
+
}
|
|
6024
|
+
}
|
|
6025
|
+
// Build prompt for prediction generation
|
|
6026
|
+
const prompt = `Based on the conversation history below, generate exactly ${predictionNum} natural and contextually appropriate responses that the player might say next.
|
|
6027
|
+
|
|
6028
|
+
Context:
|
|
6029
|
+
- This is a conversation between a player and an NPC in a game
|
|
6030
|
+
- The NPC just said: "${lastNpcMessage}"
|
|
6031
|
+
${playerCharacterSection}
|
|
6032
|
+
Conversation history:
|
|
6033
|
+
${recentHistory.join('\n')}
|
|
6034
|
+
|
|
6035
|
+
Requirements:
|
|
6036
|
+
1. Each response should be 1-2 sentences maximum
|
|
6037
|
+
2. Responses should be diverse in tone and intent
|
|
6038
|
+
3. Include a mix of questions, statements, and action-oriented responses
|
|
6039
|
+
4. Responses should feel natural for the player character${playerContext || tempPrompt ? ' and match their personality/tone' : ''}
|
|
6040
|
+
|
|
6041
|
+
Output ONLY a JSON array of ${predictionNum} strings, nothing else:
|
|
6042
|
+
["response1", "response2", "response3", "response4"]`;
|
|
6043
|
+
const result = await this.chatClient.textGeneration({
|
|
6044
|
+
messages: [{ role: 'user', content: prompt }],
|
|
6045
|
+
temperature: 0.8,
|
|
6046
|
+
model: this.fastModel,
|
|
6047
|
+
});
|
|
6048
|
+
if (!result.content) {
|
|
6049
|
+
this.logger.warn('Failed to generate predictions: empty response');
|
|
6050
|
+
return [];
|
|
6051
|
+
}
|
|
6052
|
+
// Parse JSON response
|
|
6053
|
+
const predictions = this.parsePredictionsFromJson(result.content, predictionNum);
|
|
6054
|
+
if (predictions.length > 0) {
|
|
6055
|
+
this.emit('replyPredictions', predictions);
|
|
6056
|
+
}
|
|
6057
|
+
return predictions;
|
|
6058
|
+
}
|
|
6059
|
+
catch (error) {
|
|
6060
|
+
this.logger.error('Error generating predictions:', error);
|
|
6061
|
+
return [];
|
|
6062
|
+
}
|
|
5962
6063
|
}
|
|
5963
6064
|
/**
|
|
5964
|
-
*
|
|
6065
|
+
* Parse predictions from JSON array response
|
|
5965
6066
|
*/
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
6067
|
+
parsePredictionsFromJson(response, expectedCount) {
|
|
6068
|
+
try {
|
|
6069
|
+
// Try to find JSON array in response
|
|
6070
|
+
const startIndex = response.indexOf('[');
|
|
6071
|
+
const endIndex = response.lastIndexOf(']');
|
|
6072
|
+
if (startIndex === -1 || endIndex === -1 || endIndex <= startIndex) {
|
|
6073
|
+
this.logger.warn('Could not find JSON array in prediction response');
|
|
6074
|
+
return this.extractPredictionsFromText(response, expectedCount);
|
|
6075
|
+
}
|
|
6076
|
+
const jsonArray = response.substring(startIndex, endIndex + 1);
|
|
6077
|
+
const parsed = JSON.parse(jsonArray);
|
|
6078
|
+
if (Array.isArray(parsed)) {
|
|
6079
|
+
return parsed
|
|
6080
|
+
.filter(item => typeof item === 'string' && item.trim())
|
|
6081
|
+
.slice(0, expectedCount);
|
|
6082
|
+
}
|
|
6083
|
+
return [];
|
|
6084
|
+
}
|
|
6085
|
+
catch (error) {
|
|
6086
|
+
this.logger.warn('Failed to parse predictions JSON:', error);
|
|
6087
|
+
return this.extractPredictionsFromText(response, expectedCount);
|
|
5970
6088
|
}
|
|
5971
|
-
this.appendMessage({ role: role, content });
|
|
5972
6089
|
}
|
|
5973
6090
|
/**
|
|
5974
|
-
*
|
|
6091
|
+
* Fallback: Extract predictions from text when JSON parsing fails
|
|
5975
6092
|
*/
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
6093
|
+
extractPredictionsFromText(response, expectedCount) {
|
|
6094
|
+
const predictions = [];
|
|
6095
|
+
const lines = response.split(/[\n\r]+/).filter(line => line.trim());
|
|
6096
|
+
for (const line of lines) {
|
|
6097
|
+
let cleaned = line.trim();
|
|
6098
|
+
// Skip empty lines and JSON brackets
|
|
6099
|
+
if (!cleaned || cleaned === '[' || cleaned === ']')
|
|
6100
|
+
continue;
|
|
6101
|
+
// Remove common prefixes like "1.", "- ", etc.
|
|
6102
|
+
if (/^\d+\./.test(cleaned)) {
|
|
6103
|
+
cleaned = cleaned.replace(/^\d+\.\s*/, '');
|
|
6104
|
+
}
|
|
6105
|
+
else if (cleaned.startsWith('- ')) {
|
|
6106
|
+
cleaned = cleaned.substring(2);
|
|
6107
|
+
}
|
|
6108
|
+
// Remove surrounding quotes
|
|
6109
|
+
if (cleaned.startsWith('"') && cleaned.endsWith('"')) {
|
|
6110
|
+
cleaned = cleaned.slice(1, -1);
|
|
6111
|
+
}
|
|
6112
|
+
// Remove trailing comma
|
|
6113
|
+
if (cleaned.endsWith(',')) {
|
|
6114
|
+
cleaned = cleaned.slice(0, -1).trim();
|
|
6115
|
+
}
|
|
6116
|
+
if (cleaned && predictions.length < expectedCount) {
|
|
6117
|
+
predictions.push(cleaned);
|
|
6118
|
+
}
|
|
5980
6119
|
}
|
|
6120
|
+
return predictions;
|
|
5981
6121
|
}
|
|
5982
|
-
// ===== Save/Load =====
|
|
5983
6122
|
/**
|
|
5984
|
-
*
|
|
5985
|
-
* Includes characterDesign, memories, and history.
|
|
6123
|
+
* Internal method to trigger prediction generation after NPC response
|
|
5986
6124
|
*/
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
|
|
5993
|
-
|
|
6125
|
+
async triggerReplyPrediction() {
|
|
6126
|
+
if (!this.generateReplyPrediction)
|
|
6127
|
+
return;
|
|
6128
|
+
// Fire and forget - don't block the main response
|
|
6129
|
+
this.generateReplyPredictions().catch(err => {
|
|
6130
|
+
this.logger.error('Background prediction generation failed:', err);
|
|
6131
|
+
});
|
|
5994
6132
|
}
|
|
6133
|
+
// ===== Main API - Talk Methods =====
|
|
5995
6134
|
/**
|
|
5996
|
-
*
|
|
5997
|
-
* Restores characterDesign, memories, and history.
|
|
6135
|
+
* Talk to the NPC (non-streaming)
|
|
5998
6136
|
*/
|
|
5999
|
-
|
|
6137
|
+
async talk(message) {
|
|
6138
|
+
this._isTalking = true;
|
|
6000
6139
|
try {
|
|
6001
|
-
|
|
6002
|
-
|
|
6003
|
-
this.
|
|
6004
|
-
//
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
this.
|
|
6020
|
-
|
|
6140
|
+
// Add user message to history
|
|
6141
|
+
const userMessage = { role: 'user', content: message };
|
|
6142
|
+
this.history.push(userMessage);
|
|
6143
|
+
// Build messages array with system prompt
|
|
6144
|
+
const messages = [
|
|
6145
|
+
{ role: 'system', content: this.buildSystemPrompt() },
|
|
6146
|
+
...this.history,
|
|
6147
|
+
];
|
|
6148
|
+
// Generate response
|
|
6149
|
+
const result = await this.chatClient.textGeneration({
|
|
6150
|
+
messages,
|
|
6151
|
+
temperature: this.temperature,
|
|
6152
|
+
});
|
|
6153
|
+
// Add assistant response to history
|
|
6154
|
+
const assistantMessage = { role: 'assistant', content: result.content };
|
|
6155
|
+
this.history.push(assistantMessage);
|
|
6156
|
+
// Trim history if needed
|
|
6157
|
+
this.trimHistory();
|
|
6158
|
+
this.emit('response', result.content);
|
|
6159
|
+
// Trigger reply prediction generation (fire and forget)
|
|
6160
|
+
this.triggerReplyPrediction();
|
|
6161
|
+
return result.content;
|
|
6021
6162
|
}
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
/**
|
|
6026
|
-
* Global AI Context Manager for managing NPC conversations and player context.
|
|
6027
|
-
*
|
|
6028
|
-
* Features:
|
|
6029
|
-
* - Player description management
|
|
6030
|
-
* - NPC conversation tracking
|
|
6031
|
-
* - Automatic conversation compaction (AutoCompact)
|
|
6032
|
-
*/
|
|
6033
|
-
/**
|
|
6034
|
-
* Global AI Context Manager
|
|
6035
|
-
* Manages NPC conversations and player context across the application
|
|
6036
|
-
*/
|
|
6037
|
-
class AIContextManager extends EventEmitter {
|
|
6038
|
-
constructor(config) {
|
|
6039
|
-
var _a, _b, _c, _d, _e;
|
|
6040
|
-
super();
|
|
6041
|
-
this.playerDescription = null;
|
|
6042
|
-
this.npcStates = new Map();
|
|
6043
|
-
this.autoCompactTimer = null;
|
|
6044
|
-
this.chatClientFactory = null;
|
|
6045
|
-
this.logger = Logger.getLogger('AIContextManager');
|
|
6046
|
-
this.config = {
|
|
6047
|
-
enableAutoCompact: (_a = config === null || config === void 0 ? void 0 : config.enableAutoCompact) !== null && _a !== void 0 ? _a : false,
|
|
6048
|
-
autoCompactMinMessages: (_b = config === null || config === void 0 ? void 0 : config.autoCompactMinMessages) !== null && _b !== void 0 ? _b : 20,
|
|
6049
|
-
autoCompactTimeoutSeconds: (_c = config === null || config === void 0 ? void 0 : config.autoCompactTimeoutSeconds) !== null && _c !== void 0 ? _c : 300,
|
|
6050
|
-
autoCompactCheckInterval: (_d = config === null || config === void 0 ? void 0 : config.autoCompactCheckInterval) !== null && _d !== void 0 ? _d : 60000,
|
|
6051
|
-
fastModel: (_e = config === null || config === void 0 ? void 0 : config.fastModel) !== null && _e !== void 0 ? _e : '',
|
|
6052
|
-
};
|
|
6053
|
-
// Start auto-compact check if enabled
|
|
6054
|
-
if (this.config.enableAutoCompact) {
|
|
6055
|
-
this.startAutoCompactCheck();
|
|
6163
|
+
finally {
|
|
6164
|
+
this._isTalking = false;
|
|
6056
6165
|
}
|
|
6057
6166
|
}
|
|
6058
|
-
// ===== Singleton Pattern =====
|
|
6059
6167
|
/**
|
|
6060
|
-
*
|
|
6061
|
-
* Creates a new instance if one doesn't exist
|
|
6168
|
+
* Talk to the NPC with streaming
|
|
6062
6169
|
*/
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6170
|
+
async talkStream(message, onChunk, onComplete) {
|
|
6171
|
+
this._isTalking = true;
|
|
6172
|
+
try {
|
|
6173
|
+
// Add user message to history
|
|
6174
|
+
const userMessage = { role: 'user', content: message };
|
|
6175
|
+
this.history.push(userMessage);
|
|
6176
|
+
// Build messages array with system prompt
|
|
6177
|
+
const messages = [
|
|
6178
|
+
{ role: 'system', content: this.buildSystemPrompt() },
|
|
6179
|
+
...this.history,
|
|
6180
|
+
];
|
|
6181
|
+
// Generate response
|
|
6182
|
+
await this.chatClient.textGenerationStream({
|
|
6183
|
+
messages,
|
|
6184
|
+
temperature: this.temperature,
|
|
6185
|
+
onChunk,
|
|
6186
|
+
onComplete: (fullText) => {
|
|
6187
|
+
this._isTalking = false;
|
|
6188
|
+
// Add assistant response to history
|
|
6189
|
+
const assistantMessage = { role: 'assistant', content: fullText };
|
|
6190
|
+
this.history.push(assistantMessage);
|
|
6191
|
+
// Trim history if needed
|
|
6192
|
+
this.trimHistory();
|
|
6193
|
+
this.emit('response', fullText);
|
|
6194
|
+
// Trigger reply prediction generation (fire and forget)
|
|
6195
|
+
this.triggerReplyPrediction();
|
|
6196
|
+
if (onComplete) {
|
|
6197
|
+
onComplete(fullText);
|
|
6198
|
+
}
|
|
6199
|
+
},
|
|
6200
|
+
});
|
|
6066
6201
|
}
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
* Reset the singleton instance (useful for testing)
|
|
6071
|
-
*/
|
|
6072
|
-
static resetInstance() {
|
|
6073
|
-
if (AIContextManager._instance) {
|
|
6074
|
-
AIContextManager._instance.destroy();
|
|
6075
|
-
AIContextManager._instance = null;
|
|
6202
|
+
catch (error) {
|
|
6203
|
+
this._isTalking = false;
|
|
6204
|
+
throw error;
|
|
6076
6205
|
}
|
|
6077
6206
|
}
|
|
6078
|
-
// ===== Configuration =====
|
|
6079
6207
|
/**
|
|
6080
|
-
*
|
|
6081
|
-
*
|
|
6208
|
+
* Talk with structured output
|
|
6209
|
+
* @deprecated Use talkWithActions instead for NPC decision-making with actions
|
|
6082
6210
|
*/
|
|
6083
|
-
|
|
6084
|
-
this.
|
|
6211
|
+
async talkStructured(message, schemaName) {
|
|
6212
|
+
this.logger.warn('talkStructured is deprecated. Use talkWithActions instead for NPC decision-making with actions.');
|
|
6213
|
+
// Add user message to history
|
|
6214
|
+
const userMessage = { role: 'user', content: message };
|
|
6215
|
+
this.history.push(userMessage);
|
|
6216
|
+
// Generate structured response
|
|
6217
|
+
const result = await this.chatClient.generateStructured({
|
|
6218
|
+
schemaName,
|
|
6219
|
+
prompt: message,
|
|
6220
|
+
messages: [{ role: 'system', content: this.buildSystemPrompt() }, ...this.history],
|
|
6221
|
+
temperature: this.temperature,
|
|
6222
|
+
});
|
|
6223
|
+
// Add a text representation to history
|
|
6224
|
+
const assistantMessage = {
|
|
6225
|
+
role: 'assistant',
|
|
6226
|
+
content: JSON.stringify(result),
|
|
6227
|
+
};
|
|
6228
|
+
this.history.push(assistantMessage);
|
|
6229
|
+
this.trimHistory();
|
|
6230
|
+
return result;
|
|
6085
6231
|
}
|
|
6086
6232
|
/**
|
|
6087
|
-
*
|
|
6233
|
+
* Talk to the NPC with available actions (non-streaming)
|
|
6234
|
+
* @param message The message to send
|
|
6235
|
+
* @param actions List of actions the NPC can perform
|
|
6236
|
+
* @returns Response containing text and any action calls
|
|
6088
6237
|
*/
|
|
6089
|
-
|
|
6090
|
-
|
|
6091
|
-
|
|
6092
|
-
|
|
6093
|
-
|
|
6094
|
-
|
|
6095
|
-
|
|
6238
|
+
async talkWithActions(message, actions) {
|
|
6239
|
+
this._isTalking = true;
|
|
6240
|
+
try {
|
|
6241
|
+
// Add user message to history
|
|
6242
|
+
const userMessage = { role: 'user', content: message };
|
|
6243
|
+
this.history.push(userMessage);
|
|
6244
|
+
// Convert NpcActions to ChatTools
|
|
6245
|
+
const tools = actions
|
|
6246
|
+
.filter(a => a && a.enabled !== false)
|
|
6247
|
+
.map(a => npcActionToTool(a));
|
|
6248
|
+
// Build messages array with system prompt
|
|
6249
|
+
const messages = [
|
|
6250
|
+
{ role: 'system', content: this.buildSystemPrompt() },
|
|
6251
|
+
...this.history,
|
|
6252
|
+
];
|
|
6253
|
+
// Generate response with tools
|
|
6254
|
+
const result = await this.chatClient.textGenerationWithTools({
|
|
6255
|
+
messages,
|
|
6256
|
+
temperature: this.temperature,
|
|
6257
|
+
tools,
|
|
6258
|
+
tool_choice: 'auto',
|
|
6259
|
+
});
|
|
6260
|
+
// Build response
|
|
6261
|
+
const response = {
|
|
6262
|
+
text: result.content || '',
|
|
6263
|
+
actionCalls: [],
|
|
6264
|
+
hasActions: false,
|
|
6265
|
+
};
|
|
6266
|
+
// Extract tool calls if any
|
|
6267
|
+
if (result.tool_calls) {
|
|
6268
|
+
response.actionCalls = result.tool_calls.map(tc => ({
|
|
6269
|
+
id: tc.id,
|
|
6270
|
+
actionName: tc.function.name,
|
|
6271
|
+
arguments: this.parseToolArguments(tc.function.arguments),
|
|
6272
|
+
}));
|
|
6273
|
+
response.hasActions = response.actionCalls.length > 0;
|
|
6096
6274
|
}
|
|
6097
|
-
|
|
6098
|
-
|
|
6275
|
+
// Add assistant response to history
|
|
6276
|
+
const assistantMessage = {
|
|
6277
|
+
role: 'assistant',
|
|
6278
|
+
content: response.text,
|
|
6279
|
+
tool_calls: result.tool_calls,
|
|
6280
|
+
};
|
|
6281
|
+
this.history.push(assistantMessage);
|
|
6282
|
+
this.trimHistory();
|
|
6283
|
+
this.emit('response', response.text);
|
|
6284
|
+
if (response.hasActions) {
|
|
6285
|
+
this.emit('actions', response.actionCalls);
|
|
6099
6286
|
}
|
|
6287
|
+
// Trigger reply prediction generation (fire and forget)
|
|
6288
|
+
this.triggerReplyPrediction();
|
|
6289
|
+
return response;
|
|
6290
|
+
}
|
|
6291
|
+
finally {
|
|
6292
|
+
this._isTalking = false;
|
|
6100
6293
|
}
|
|
6101
|
-
}
|
|
6102
|
-
// ===== Player Description =====
|
|
6103
|
-
/**
|
|
6104
|
-
* Set the player's description for AI context.
|
|
6105
|
-
* Used when generating reply predictions and for NPC context.
|
|
6106
|
-
* @param description Description of the player character
|
|
6107
|
-
*/
|
|
6108
|
-
setPlayerDescription(description) {
|
|
6109
|
-
this.playerDescription = description;
|
|
6110
|
-
this.emit('playerDescriptionChanged', description);
|
|
6111
|
-
}
|
|
6112
|
-
/**
|
|
6113
|
-
* Get the current player description.
|
|
6114
|
-
* @returns The player description, or null if not set
|
|
6115
|
-
*/
|
|
6116
|
-
getPlayerDescription() {
|
|
6117
|
-
return this.playerDescription;
|
|
6118
6294
|
}
|
|
6119
6295
|
/**
|
|
6120
|
-
*
|
|
6296
|
+
* Talk to the NPC with actions (streaming)
|
|
6297
|
+
* Text streams first, action calls are returned in onComplete
|
|
6121
6298
|
*/
|
|
6122
|
-
|
|
6123
|
-
this.
|
|
6124
|
-
|
|
6299
|
+
async talkWithActionsStream(message, actions, onChunk, onComplete) {
|
|
6300
|
+
this._isTalking = true;
|
|
6301
|
+
try {
|
|
6302
|
+
// Add user message to history
|
|
6303
|
+
const userMessage = { role: 'user', content: message };
|
|
6304
|
+
this.history.push(userMessage);
|
|
6305
|
+
// Convert NpcActions to ChatTools
|
|
6306
|
+
const tools = actions
|
|
6307
|
+
.filter(a => a && a.enabled !== false)
|
|
6308
|
+
.map(a => npcActionToTool(a));
|
|
6309
|
+
// Build messages array with system prompt
|
|
6310
|
+
const messages = [
|
|
6311
|
+
{ role: 'system', content: this.buildSystemPrompt() },
|
|
6312
|
+
...this.history,
|
|
6313
|
+
];
|
|
6314
|
+
// Generate response with tools (streaming)
|
|
6315
|
+
await this.chatClient.textGenerationWithToolsStream({
|
|
6316
|
+
messages,
|
|
6317
|
+
temperature: this.temperature,
|
|
6318
|
+
tools,
|
|
6319
|
+
tool_choice: 'auto',
|
|
6320
|
+
onChunk,
|
|
6321
|
+
onComplete: (result) => {
|
|
6322
|
+
this._isTalking = false;
|
|
6323
|
+
// Build response
|
|
6324
|
+
const response = {
|
|
6325
|
+
text: result.content || '',
|
|
6326
|
+
actionCalls: [],
|
|
6327
|
+
hasActions: false,
|
|
6328
|
+
};
|
|
6329
|
+
// Extract tool calls if any
|
|
6330
|
+
if (result.tool_calls) {
|
|
6331
|
+
response.actionCalls = result.tool_calls.map(tc => ({
|
|
6332
|
+
id: tc.id,
|
|
6333
|
+
actionName: tc.function.name,
|
|
6334
|
+
arguments: this.parseToolArguments(tc.function.arguments),
|
|
6335
|
+
}));
|
|
6336
|
+
response.hasActions = response.actionCalls.length > 0;
|
|
6337
|
+
}
|
|
6338
|
+
// Add assistant response to history
|
|
6339
|
+
const assistantMessage = {
|
|
6340
|
+
role: 'assistant',
|
|
6341
|
+
content: response.text,
|
|
6342
|
+
tool_calls: result.tool_calls,
|
|
6343
|
+
};
|
|
6344
|
+
this.history.push(assistantMessage);
|
|
6345
|
+
this.trimHistory();
|
|
6346
|
+
this.emit('response', response.text);
|
|
6347
|
+
if (response.hasActions) {
|
|
6348
|
+
this.emit('actions', response.actionCalls);
|
|
6349
|
+
}
|
|
6350
|
+
// Trigger reply prediction generation (fire and forget)
|
|
6351
|
+
this.triggerReplyPrediction();
|
|
6352
|
+
if (onComplete) {
|
|
6353
|
+
onComplete(response);
|
|
6354
|
+
}
|
|
6355
|
+
},
|
|
6356
|
+
});
|
|
6357
|
+
}
|
|
6358
|
+
catch (error) {
|
|
6359
|
+
this._isTalking = false;
|
|
6360
|
+
throw error;
|
|
6361
|
+
}
|
|
6125
6362
|
}
|
|
6126
|
-
// =====
|
|
6363
|
+
// ===== Action Results Reporting =====
|
|
6127
6364
|
/**
|
|
6128
|
-
*
|
|
6129
|
-
*
|
|
6365
|
+
* Report action results back to the conversation
|
|
6366
|
+
* Call this after executing actions to let the NPC know the results
|
|
6130
6367
|
*/
|
|
6131
|
-
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
isCompacted: false,
|
|
6138
|
-
compactionCount: 0,
|
|
6368
|
+
reportActionResults(results) {
|
|
6369
|
+
for (const [callId, result] of Object.entries(results)) {
|
|
6370
|
+
this.history.push({
|
|
6371
|
+
role: 'tool',
|
|
6372
|
+
tool_call_id: callId,
|
|
6373
|
+
content: result,
|
|
6139
6374
|
});
|
|
6140
6375
|
}
|
|
6141
6376
|
}
|
|
6142
6377
|
/**
|
|
6143
|
-
*
|
|
6144
|
-
* @param npc The NPC client to unregister
|
|
6378
|
+
* Report a single action result
|
|
6145
6379
|
*/
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6380
|
+
reportActionResult(callId, result) {
|
|
6381
|
+
this.history.push({
|
|
6382
|
+
role: 'tool',
|
|
6383
|
+
tool_call_id: callId,
|
|
6384
|
+
content: result,
|
|
6385
|
+
});
|
|
6150
6386
|
}
|
|
6151
6387
|
/**
|
|
6152
|
-
*
|
|
6153
|
-
* Called after each Talk() exchange.
|
|
6154
|
-
* @param npc The NPC client that had a conversation
|
|
6388
|
+
* Parse tool arguments from JSON string
|
|
6155
6389
|
*/
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
return;
|
|
6159
|
-
|
|
6160
|
-
|
|
6390
|
+
parseToolArguments(args) {
|
|
6391
|
+
try {
|
|
6392
|
+
return JSON.parse(args);
|
|
6393
|
+
}
|
|
6394
|
+
catch (_a) {
|
|
6395
|
+
return {};
|
|
6161
6396
|
}
|
|
6162
|
-
const state = this.npcStates.get(npc);
|
|
6163
|
-
state.lastConversationTime = new Date();
|
|
6164
|
-
state.isCompacted = false; // Reset compaction flag on new conversation
|
|
6165
6397
|
}
|
|
6398
|
+
// ===== Conversation History Management =====
|
|
6166
6399
|
/**
|
|
6167
|
-
* Get
|
|
6400
|
+
* Get conversation history
|
|
6168
6401
|
*/
|
|
6169
|
-
|
|
6170
|
-
return
|
|
6402
|
+
getHistory() {
|
|
6403
|
+
return [...this.history];
|
|
6171
6404
|
}
|
|
6172
6405
|
/**
|
|
6173
|
-
* Get the
|
|
6406
|
+
* Get the number of messages in history
|
|
6174
6407
|
*/
|
|
6175
|
-
|
|
6176
|
-
return this.
|
|
6408
|
+
getHistoryLength() {
|
|
6409
|
+
return this.history.length;
|
|
6177
6410
|
}
|
|
6178
|
-
// ===== Auto Compaction =====
|
|
6179
6411
|
/**
|
|
6180
|
-
*
|
|
6181
|
-
*
|
|
6182
|
-
* @returns True if eligible for compaction
|
|
6412
|
+
* Clear conversation history.
|
|
6413
|
+
* The character design and memories will be preserved.
|
|
6183
6414
|
*/
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
const state = this.npcStates.get(npc);
|
|
6188
|
-
if (!state)
|
|
6189
|
-
return false;
|
|
6190
|
-
// Check if already compacted since last conversation
|
|
6191
|
-
if (state.isCompacted)
|
|
6192
|
-
return false;
|
|
6193
|
-
// Check message count
|
|
6194
|
-
const history = npc.getHistory();
|
|
6195
|
-
const nonSystemMessages = history.filter(m => m.role !== 'system').length;
|
|
6196
|
-
if (nonSystemMessages < this.config.autoCompactMinMessages)
|
|
6197
|
-
return false;
|
|
6198
|
-
// Check time since last conversation
|
|
6199
|
-
const timeSinceLastConversation = (Date.now() - state.lastConversationTime.getTime()) / 1000;
|
|
6200
|
-
if (timeSinceLastConversation < this.config.autoCompactTimeoutSeconds)
|
|
6201
|
-
return false;
|
|
6202
|
-
return true;
|
|
6415
|
+
clearHistory() {
|
|
6416
|
+
this.history = [];
|
|
6417
|
+
this.emit('history_cleared');
|
|
6203
6418
|
}
|
|
6204
6419
|
/**
|
|
6205
|
-
*
|
|
6206
|
-
*
|
|
6207
|
-
* @param npc The NPC to compact
|
|
6208
|
-
* @returns True if compaction succeeded
|
|
6420
|
+
* Revert the last exchange (user message and assistant response) from history.
|
|
6421
|
+
* @returns true if reverted, false if not enough history
|
|
6209
6422
|
*/
|
|
6210
|
-
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
this.logger.error('Cannot compact: No chat client factory set. Call setChatClientFactory() first.');
|
|
6217
|
-
return false;
|
|
6218
|
-
}
|
|
6219
|
-
const history = npc.getHistory();
|
|
6220
|
-
const nonSystemMessages = history.filter(m => m.role !== 'system');
|
|
6221
|
-
if (nonSystemMessages.length < 2) {
|
|
6222
|
-
this.logger.info('Skipping compaction: not enough messages');
|
|
6223
|
-
return false;
|
|
6224
|
-
}
|
|
6225
|
-
try {
|
|
6226
|
-
this.logger.info(`Starting compaction (${nonSystemMessages.length} messages)`);
|
|
6227
|
-
// Build conversation text for summarization
|
|
6228
|
-
const conversationText = nonSystemMessages
|
|
6229
|
-
.map(m => `${m.role}: ${m.content}`)
|
|
6230
|
-
.join('\n');
|
|
6231
|
-
// Create summarization prompt
|
|
6232
|
-
const summaryPrompt = `Summarize the following conversation concisely. Focus on:
|
|
6233
|
-
1. Key topics discussed
|
|
6234
|
-
2. Important information exchanged
|
|
6235
|
-
3. Any decisions or commitments made
|
|
6236
|
-
4. The emotional tone
|
|
6237
|
-
|
|
6238
|
-
Keep the summary under 200 words. Write in third person.
|
|
6239
|
-
|
|
6240
|
-
Conversation:
|
|
6241
|
-
${conversationText}`;
|
|
6242
|
-
// Use chat client for summarization
|
|
6243
|
-
const chatClient = this.chatClientFactory();
|
|
6244
|
-
const result = await chatClient.textGeneration({
|
|
6245
|
-
messages: [{ role: 'user', content: summaryPrompt }],
|
|
6246
|
-
temperature: 0.5,
|
|
6247
|
-
model: this.config.fastModel || undefined,
|
|
6248
|
-
});
|
|
6249
|
-
if (!result.content) {
|
|
6250
|
-
const error = 'Empty response from summarization';
|
|
6251
|
-
this.logger.error(`Compaction failed: ${error}`);
|
|
6252
|
-
this.emit('compactionFailed', npc, error);
|
|
6253
|
-
return false;
|
|
6423
|
+
revertHistory() {
|
|
6424
|
+
let lastAssistantIndex = -1;
|
|
6425
|
+
let lastUserIndex = -1;
|
|
6426
|
+
for (let i = this.history.length - 1; i >= 0; i--) {
|
|
6427
|
+
if (this.history[i].role === 'assistant' && lastAssistantIndex === -1) {
|
|
6428
|
+
lastAssistantIndex = i;
|
|
6254
6429
|
}
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
// Update state
|
|
6259
|
-
const state = this.npcStates.get(npc);
|
|
6260
|
-
if (state) {
|
|
6261
|
-
state.isCompacted = true;
|
|
6262
|
-
state.compactionCount++;
|
|
6430
|
+
else if (this.history[i].role === 'user' && lastAssistantIndex !== -1 && lastUserIndex === -1) {
|
|
6431
|
+
lastUserIndex = i;
|
|
6432
|
+
break;
|
|
6263
6433
|
}
|
|
6264
|
-
this.logger.info(`Compaction completed. Summary: ${result.content.substring(0, 100)}...`);
|
|
6265
|
-
this.emit('npcCompacted', npc);
|
|
6266
|
-
return true;
|
|
6267
6434
|
}
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
this.
|
|
6271
|
-
this.
|
|
6272
|
-
|
|
6435
|
+
if (lastAssistantIndex !== -1 && lastUserIndex !== -1) {
|
|
6436
|
+
// Remove in reverse order to maintain indices
|
|
6437
|
+
this.history.splice(lastAssistantIndex, 1);
|
|
6438
|
+
this.history.splice(lastUserIndex, 1);
|
|
6439
|
+
this.emit('history_reverted');
|
|
6440
|
+
return true;
|
|
6273
6441
|
}
|
|
6442
|
+
return false;
|
|
6274
6443
|
}
|
|
6275
6444
|
/**
|
|
6276
|
-
*
|
|
6277
|
-
* @
|
|
6445
|
+
* Revert (remove) the last N chat messages from history
|
|
6446
|
+
* @param count Number of messages to remove
|
|
6447
|
+
* @returns Number of messages actually removed
|
|
6278
6448
|
*/
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
if (eligibleNpcs.length === 0) {
|
|
6449
|
+
revertChatMessages(count) {
|
|
6450
|
+
if (count <= 0)
|
|
6282
6451
|
return 0;
|
|
6452
|
+
const messagesToRemove = Math.min(count, this.history.length);
|
|
6453
|
+
const originalCount = this.history.length;
|
|
6454
|
+
this.history = this.history.slice(0, -messagesToRemove);
|
|
6455
|
+
const actuallyRemoved = originalCount - this.history.length;
|
|
6456
|
+
if (actuallyRemoved > 0) {
|
|
6457
|
+
this.emit('history_reverted', actuallyRemoved);
|
|
6283
6458
|
}
|
|
6284
|
-
|
|
6285
|
-
let successCount = 0;
|
|
6286
|
-
for (const npc of eligibleNpcs) {
|
|
6287
|
-
const success = await this.compactConversation(npc);
|
|
6288
|
-
if (success)
|
|
6289
|
-
successCount++;
|
|
6290
|
-
}
|
|
6291
|
-
return successCount;
|
|
6459
|
+
return actuallyRemoved;
|
|
6292
6460
|
}
|
|
6293
|
-
// ===== Auto Compact Timer =====
|
|
6294
6461
|
/**
|
|
6295
|
-
*
|
|
6462
|
+
* Revert to a specific point in history
|
|
6463
|
+
* @deprecated Use revertHistory() or revertChatMessages() instead
|
|
6296
6464
|
*/
|
|
6297
|
-
|
|
6298
|
-
if (this.
|
|
6299
|
-
this.
|
|
6465
|
+
revertToMessage(index) {
|
|
6466
|
+
if (index >= 0 && index < this.history.length) {
|
|
6467
|
+
this.history = this.history.slice(0, index + 1);
|
|
6468
|
+
this.emit('history_reverted', index);
|
|
6300
6469
|
}
|
|
6301
|
-
this.autoCompactTimer = setInterval(() => {
|
|
6302
|
-
this.runAutoCompactCheck();
|
|
6303
|
-
}, this.config.autoCompactCheckInterval);
|
|
6304
6470
|
}
|
|
6305
6471
|
/**
|
|
6306
|
-
*
|
|
6472
|
+
* Append a message to history manually
|
|
6307
6473
|
*/
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
this.autoCompactTimer = null;
|
|
6312
|
-
}
|
|
6474
|
+
appendMessage(message) {
|
|
6475
|
+
this.history.push(message);
|
|
6476
|
+
this.trimHistory();
|
|
6313
6477
|
}
|
|
6314
6478
|
/**
|
|
6315
|
-
*
|
|
6479
|
+
* Alias for appendMessage (Unity SDK compatibility)
|
|
6316
6480
|
*/
|
|
6317
|
-
|
|
6318
|
-
if (!
|
|
6481
|
+
appendChatMessage(role, content) {
|
|
6482
|
+
if (!role || !content) {
|
|
6483
|
+
this.logger.warn('Role and content cannot be empty');
|
|
6319
6484
|
return;
|
|
6320
|
-
const eligibleNpcs = Array.from(this.npcStates.keys()).filter(npc => this.isEligibleForCompaction(npc));
|
|
6321
|
-
for (const npc of eligibleNpcs) {
|
|
6322
|
-
// Fire and forget - don't block
|
|
6323
|
-
this.compactConversation(npc).catch(err => {
|
|
6324
|
-
this.logger.error('Auto-compact error:', err);
|
|
6325
|
-
});
|
|
6326
6485
|
}
|
|
6486
|
+
this.appendMessage({ role: role, content });
|
|
6327
6487
|
}
|
|
6328
|
-
// ===== Lifecycle =====
|
|
6329
6488
|
/**
|
|
6330
|
-
*
|
|
6489
|
+
* Trim history to max length
|
|
6331
6490
|
*/
|
|
6332
|
-
|
|
6333
|
-
this.
|
|
6334
|
-
|
|
6491
|
+
trimHistory() {
|
|
6492
|
+
if (this.history.length > this.maxHistoryLength) {
|
|
6493
|
+
// Keep the most recent messages
|
|
6494
|
+
this.history = this.history.slice(-this.maxHistoryLength);
|
|
6495
|
+
}
|
|
6335
6496
|
}
|
|
6497
|
+
// ===== Save/Load =====
|
|
6336
6498
|
/**
|
|
6337
|
-
*
|
|
6499
|
+
* Save the current conversation history to a serializable format.
|
|
6500
|
+
* Includes characterDesign, memories, and history.
|
|
6338
6501
|
*/
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6502
|
+
saveHistory() {
|
|
6503
|
+
const saveData = {
|
|
6504
|
+
characterDesign: this.characterDesign,
|
|
6505
|
+
memories: Array.from(this.memories.entries()).map(([name, content]) => ({ name, content })),
|
|
6506
|
+
history: this.history,
|
|
6507
|
+
};
|
|
6508
|
+
return JSON.stringify(saveData);
|
|
6342
6509
|
}
|
|
6343
6510
|
/**
|
|
6344
|
-
*
|
|
6511
|
+
* Load conversation history from serialized data.
|
|
6512
|
+
* Restores characterDesign, memories, and history.
|
|
6345
6513
|
*/
|
|
6346
|
-
|
|
6347
|
-
|
|
6348
|
-
|
|
6349
|
-
|
|
6350
|
-
|
|
6514
|
+
loadHistory(saveData) {
|
|
6515
|
+
try {
|
|
6516
|
+
const data = JSON.parse(saveData);
|
|
6517
|
+
// Load character design (with backwards compatibility for old systemPrompt field)
|
|
6518
|
+
this.characterDesign = data.characterDesign || data.systemPrompt || this.characterDesign;
|
|
6519
|
+
// Load memories
|
|
6520
|
+
this.memories.clear();
|
|
6521
|
+
if (data.memories && Array.isArray(data.memories)) {
|
|
6522
|
+
for (const memory of data.memories) {
|
|
6523
|
+
if (memory.name && memory.content) {
|
|
6524
|
+
this.memories.set(memory.name, memory.content);
|
|
6525
|
+
}
|
|
6526
|
+
}
|
|
6527
|
+
}
|
|
6528
|
+
// Load history (skip system messages as they'll be rebuilt from characterDesign + memories)
|
|
6529
|
+
this.history = (data.history || []).filter(m => m.role !== 'system');
|
|
6530
|
+
this.emit('history_loaded');
|
|
6531
|
+
return true;
|
|
6532
|
+
}
|
|
6533
|
+
catch (error) {
|
|
6534
|
+
this.logger.error('Failed to load history:', error);
|
|
6535
|
+
return false;
|
|
6536
|
+
}
|
|
6351
6537
|
}
|
|
6352
6538
|
}
|
|
6353
|
-
AIContextManager._instance = null;
|
|
6354
|
-
/**
|
|
6355
|
-
* Default AIContextManager instance
|
|
6356
|
-
* Can be used as a global context manager
|
|
6357
|
-
*/
|
|
6358
|
-
const defaultContextManager = AIContextManager.getInstance();
|
|
6359
6539
|
|
|
6360
6540
|
/**
|
|
6361
6541
|
* Schema Library for managing JSON schemas for AI structured output generation
|
|
@@ -6714,20 +6894,20 @@ ${conversationText}`;
|
|
|
6714
6894
|
// Create indicator element
|
|
6715
6895
|
this.devTokenIndicator = document.createElement('div');
|
|
6716
6896
|
this.devTokenIndicator.textContent = 'DeveloperToken';
|
|
6717
|
-
this.devTokenIndicator.style.cssText = `
|
|
6718
|
-
position: fixed;
|
|
6719
|
-
top: 10px;
|
|
6720
|
-
left: 10px;
|
|
6721
|
-
background-color: #dc2626;
|
|
6722
|
-
color: white;
|
|
6723
|
-
padding: 4px 12px;
|
|
6724
|
-
border-radius: 4px;
|
|
6725
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
6726
|
-
font-size: 12px;
|
|
6727
|
-
font-weight: 600;
|
|
6728
|
-
z-index: 999999;
|
|
6729
|
-
pointer-events: none;
|
|
6730
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
6897
|
+
this.devTokenIndicator.style.cssText = `
|
|
6898
|
+
position: fixed;
|
|
6899
|
+
top: 10px;
|
|
6900
|
+
left: 10px;
|
|
6901
|
+
background-color: #dc2626;
|
|
6902
|
+
color: white;
|
|
6903
|
+
padding: 4px 12px;
|
|
6904
|
+
border-radius: 4px;
|
|
6905
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
6906
|
+
font-size: 12px;
|
|
6907
|
+
font-weight: 600;
|
|
6908
|
+
z-index: 999999;
|
|
6909
|
+
pointer-events: none;
|
|
6910
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
6731
6911
|
`;
|
|
6732
6912
|
document.body.appendChild(this.devTokenIndicator);
|
|
6733
6913
|
}
|
|
@@ -6890,7 +7070,7 @@ ${conversationText}`;
|
|
|
6890
7070
|
*/
|
|
6891
7071
|
setDebug(enabled) {
|
|
6892
7072
|
this.config.debug = enabled;
|
|
6893
|
-
Logger.setGlobalLevel(enabled ?
|
|
7073
|
+
Logger.setGlobalLevel(enabled ? LogLevel.DEBUG : LogLevel.WARN);
|
|
6894
7074
|
}
|
|
6895
7075
|
/**
|
|
6896
7076
|
* Configure the logging system
|
|
@@ -6912,7 +7092,7 @@ ${conversationText}`;
|
|
|
6912
7092
|
initializeLogging(config) {
|
|
6913
7093
|
// Handle legacy debug option for backwards compatibility
|
|
6914
7094
|
if (config.debug !== undefined && config.logging === undefined) {
|
|
6915
|
-
Logger.setGlobalLevel(config.debug ?
|
|
7095
|
+
Logger.setGlobalLevel(config.debug ? LogLevel.DEBUG : LogLevel.WARN);
|
|
6916
7096
|
}
|
|
6917
7097
|
// Apply new logging config
|
|
6918
7098
|
if (config.logging) {
|
|
@@ -7116,9 +7296,7 @@ ${conversationText}`;
|
|
|
7116
7296
|
*/
|
|
7117
7297
|
async validateToken(token, gameId) {
|
|
7118
7298
|
var _a, _b;
|
|
7119
|
-
const headers = {
|
|
7120
|
-
'Authorization': `Bearer ${token}`,
|
|
7121
|
-
};
|
|
7299
|
+
const headers = Object.assign({ 'Authorization': `Bearer ${token}` }, getSDKHeaders());
|
|
7122
7300
|
if (gameId) {
|
|
7123
7301
|
headers['X-Game-Id'] = gameId;
|
|
7124
7302
|
}
|
|
@@ -7148,9 +7326,7 @@ ${conversationText}`;
|
|
|
7148
7326
|
*/
|
|
7149
7327
|
async verifyToken(token, gameId) {
|
|
7150
7328
|
var _a, _b;
|
|
7151
|
-
const headers = {
|
|
7152
|
-
'Authorization': `Bearer ${token}`,
|
|
7153
|
-
};
|
|
7329
|
+
const headers = Object.assign({ 'Authorization': `Bearer ${token}` }, getSDKHeaders());
|
|
7154
7330
|
if (gameId) {
|
|
7155
7331
|
headers['X-Game-Id'] = gameId;
|
|
7156
7332
|
}
|
|
@@ -7213,36 +7389,61 @@ ${conversationText}`;
|
|
|
7213
7389
|
*/
|
|
7214
7390
|
const defaultTokenValidator = new TokenValidator();
|
|
7215
7391
|
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
|
|
7219
|
-
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7223
|
-
|
|
7224
|
-
|
|
7225
|
-
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7233
|
-
|
|
7234
|
-
|
|
7235
|
-
|
|
7236
|
-
|
|
7237
|
-
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
|
|
7241
|
-
|
|
7242
|
-
|
|
7243
|
-
|
|
7244
|
-
|
|
7245
|
-
|
|
7392
|
+
/**
|
|
7393
|
+
* PlayKit SDK for JavaScript
|
|
7394
|
+
* AI integration for web-based games
|
|
7395
|
+
*/
|
|
7396
|
+
// Main SDK
|
|
7397
|
+
|
|
7398
|
+
var namespace = /*#__PURE__*/Object.freeze({
|
|
7399
|
+
__proto__: null,
|
|
7400
|
+
AIContextManager: AIContextManager,
|
|
7401
|
+
AuthFlowManager: AuthFlowManager,
|
|
7402
|
+
AuthManager: AuthManager,
|
|
7403
|
+
BrowserStorage: BrowserStorage,
|
|
7404
|
+
BufferLogHandler: BufferLogHandler,
|
|
7405
|
+
CallbackLogHandler: CallbackLogHandler,
|
|
7406
|
+
ChatClient: ChatClient,
|
|
7407
|
+
DeviceAuthFlowManager: DeviceAuthFlowManager,
|
|
7408
|
+
ImageClient: ImageClient,
|
|
7409
|
+
get LogLevel () { return LogLevel; },
|
|
7410
|
+
Logger: Logger,
|
|
7411
|
+
MemoryStorage: MemoryStorage,
|
|
7412
|
+
NPCClient: NPCClient,
|
|
7413
|
+
PlayKitSDK: PlayKitSDK,
|
|
7414
|
+
PlayerClient: PlayerClient,
|
|
7415
|
+
RechargeManager: RechargeManager,
|
|
7416
|
+
SchemaLibrary: SchemaLibrary,
|
|
7417
|
+
StreamParser: StreamParser,
|
|
7418
|
+
TokenStorage: TokenStorage,
|
|
7419
|
+
TokenValidator: TokenValidator,
|
|
7420
|
+
TranscriptionClient: TranscriptionClient,
|
|
7421
|
+
createMultimodalMessage: createMultimodalMessage,
|
|
7422
|
+
createStorage: createStorage,
|
|
7423
|
+
createTextMessage: createTextMessage,
|
|
7424
|
+
default: PlayKitSDK,
|
|
7425
|
+
defaultContextManager: defaultContextManager,
|
|
7426
|
+
defaultSchemaLibrary: defaultSchemaLibrary,
|
|
7427
|
+
defaultTokenValidator: defaultTokenValidator,
|
|
7428
|
+
isLocalStorageAvailable: isLocalStorageAvailable
|
|
7429
|
+
});
|
|
7430
|
+
|
|
7431
|
+
/**
|
|
7432
|
+
* UMD bundle entry.
|
|
7433
|
+
*
|
|
7434
|
+
* Goal: make `window.PlayKitSDK` directly the constructor class while still
|
|
7435
|
+
* exposing every named export as a property on it.
|
|
7436
|
+
*
|
|
7437
|
+
* Result:
|
|
7438
|
+
* new window.PlayKitSDK(cfg) // recommended
|
|
7439
|
+
* new window.PlayKitSDK.PlayKitSDK(cfg) // legacy v1.x form, still works
|
|
7440
|
+
* window.PlayKitSDK.ChatClient // named exports preserved
|
|
7441
|
+
*/
|
|
7442
|
+
Object.assign(PlayKitSDK, namespace);
|
|
7443
|
+
PlayKitSDK.PlayKitSDK = PlayKitSDK;
|
|
7444
|
+
PlayKitSDK.default = PlayKitSDK;
|
|
7445
|
+
|
|
7446
|
+
return PlayKitSDK;
|
|
7246
7447
|
|
|
7247
7448
|
}));
|
|
7248
7449
|
//# sourceMappingURL=playkit-sdk.umd.js.map
|