@voicethere/agent 0.7.13 → 0.8.1
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/README.md +14 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/runtime.d.ts +10 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +28 -2
- package/dist/runtime.js.map +1 -1
- package/dist/templates/echo/agent.js +16 -5
- package/dist/templates/echo-dc/agent.js +1 -1
- package/dist/templates/game-sync/agent.js +162 -125
- package/dist/templates/recording-consent/agent.js +12 -12
- package/dist/templates/registry.d.ts +1 -1
- package/dist/templates/registry.d.ts.map +1 -1
- package/dist/templates/registry.js +42 -26
- package/dist/templates/registry.js.map +1 -1
- package/dist/templates/voice-showcase/agent.js +64 -25
- package/dist/templates/voice-starter/agent.js +1 -1
- package/dist/templates/webhooks/agent.js +2 -2
- package/dist/templates/webhooks-redis/agent.js +11 -4
- package/dist/templates/world-sync/agent.js +829 -0
- package/dist/templates/world-sync-binary/agent.js +1031 -0
- package/package.json +3 -2
- package/templates/README.md +41 -69
- package/templates/crash/README.md +13 -0
- package/templates/echo/README.md +13 -0
- package/templates/{echo.ts → echo/agent.ts} +4 -6
- package/templates/echo-dc/README.md +13 -0
- package/templates/{echo-dc.ts → echo-dc/agent.ts} +1 -1
- package/templates/echo-smoke/README.md +13 -0
- package/templates/game-sync/README.md +21 -0
- package/templates/{game-sync.ts → game-sync/agent.ts} +126 -94
- package/templates/{game-sync-redis.ts → game-sync/redis.ts} +4 -13
- package/templates/{game-sync-sim.ts → game-sync/sim.ts} +14 -7
- package/templates/{game-sync-world-layout.ts → game-sync/world-layout.ts} +72 -30
- package/templates/game-sync-smoke/README.md +13 -0
- package/templates/mix-smoke/README.md +13 -0
- package/templates/positional-tts/README.md +13 -0
- package/templates/recording-consent/README.md +13 -0
- package/templates/redis-sync/README.md +23 -0
- package/templates/redis-sync/agent.ts +72 -95
- package/templates/redis-sync/world-layout.ts +103 -26
- package/templates/spatial-showcase/README.md +13 -0
- package/templates/voice-showcase/README.md +13 -0
- package/templates/voice-showcase/agent.ts +3 -1
- package/templates/voice-showcase/conversation.ts +9 -5
- package/templates/voice-showcase/delivery.ts +40 -10
- package/templates/voice-starter/README.md +13 -0
- package/templates/webhooks/README.md +13 -0
- package/templates/{webhooks.ts → webhooks/agent.ts} +1 -1
- package/templates/webhooks-redis/README.md +13 -0
- package/templates/{webhooks-redis.ts → webhooks-redis/agent.ts} +9 -9
- package/templates/world-sync/README.md +20 -0
- package/templates/world-sync/agent.ts +62 -0
- package/templates/world-sync-binary/README.md +21 -0
- package/templates/world-sync-binary/agent.ts +107 -0
- package/templates/world-sync-binary/protocol.ts +281 -0
- /package/templates/{crash.ts → crash/agent.ts} +0 -0
- /package/templates/{echo-smoke.ts → echo-smoke/agent.ts} +0 -0
- /package/templates/{game-sync-protocol.ts → game-sync/protocol.ts} +0 -0
- /package/templates/{game-sync-smoke.ts → game-sync-smoke/agent.ts} +0 -0
- /package/templates/{mix-smoke.ts → mix-smoke/agent.ts} +0 -0
- /package/templates/{agent.ts → voice-starter/agent.ts} +0 -0
|
@@ -10753,7 +10753,7 @@ var require_built3 = __commonJS({
|
|
|
10753
10753
|
}
|
|
10754
10754
|
});
|
|
10755
10755
|
|
|
10756
|
-
// templates/game-sync.ts
|
|
10756
|
+
// templates/game-sync/agent.ts
|
|
10757
10757
|
var import_ioredis = __toESM(require_built3(), 1);
|
|
10758
10758
|
|
|
10759
10759
|
// dist/runtime.js
|
|
@@ -11477,14 +11477,19 @@ function buildIdleEnv(sessionId) {
|
|
|
11477
11477
|
function sendToClient(sessionId, payload) {
|
|
11478
11478
|
sendParentMessage({ type: "send_to_client", sessionId, payload });
|
|
11479
11479
|
}
|
|
11480
|
-
function
|
|
11481
|
-
|
|
11482
|
-
|
|
11483
|
-
|
|
11484
|
-
|
|
11485
|
-
|
|
11486
|
-
|
|
11487
|
-
|
|
11480
|
+
function asOutboundBuffer(data) {
|
|
11481
|
+
return Buffer.isBuffer(data) ? data : Buffer.from(data.buffer, data.byteOffset, data.byteLength);
|
|
11482
|
+
}
|
|
11483
|
+
function broadCastBinaryToClients(data, sessionIds, channel = "sync") {
|
|
11484
|
+
const buffer = asOutboundBuffer(data);
|
|
11485
|
+
for (const sessionId of sessionIds) {
|
|
11486
|
+
sendParentMessage({
|
|
11487
|
+
type: "send_binary_to_client",
|
|
11488
|
+
sessionId,
|
|
11489
|
+
data: buffer,
|
|
11490
|
+
channel
|
|
11491
|
+
});
|
|
11492
|
+
}
|
|
11488
11493
|
}
|
|
11489
11494
|
var AGENT_LOG_MESSAGE_MAX_CHARS = 2048;
|
|
11490
11495
|
var AGENT_LOG_FIELDS_MAX_CHARS = 8192;
|
|
@@ -11542,7 +11547,7 @@ function agentLog(level, message, fieldsOrSessionId, sessionId) {
|
|
|
11542
11547
|
sendParentMessage(buildAgentLogPayload(level, message, fields, resolvedSessionId));
|
|
11543
11548
|
}
|
|
11544
11549
|
|
|
11545
|
-
// templates/game-sync
|
|
11550
|
+
// templates/game-sync/protocol.ts
|
|
11546
11551
|
var MAX_LIVE_OBJECTS = 25;
|
|
11547
11552
|
var REGISTER_NACK_REASON_WORLD_FULL = "world_full";
|
|
11548
11553
|
var UNREGISTER_NACK_REASON_NOT_FOUND = "not_found";
|
|
@@ -11586,7 +11591,7 @@ function parseChatCommand(message) {
|
|
|
11586
11591
|
return { text };
|
|
11587
11592
|
}
|
|
11588
11593
|
|
|
11589
|
-
// templates/game-sync
|
|
11594
|
+
// templates/game-sync/world-layout.ts
|
|
11590
11595
|
var OBJECT_STRIDE = 9;
|
|
11591
11596
|
var WORLD_FLOAT_COUNT = MAX_LIVE_OBJECTS * OBJECT_STRIDE;
|
|
11592
11597
|
var WORLD_BYTE_LENGTH = WORLD_FLOAT_COUNT * 4;
|
|
@@ -11629,6 +11634,12 @@ function markSlotFree(world, slot) {
|
|
|
11629
11634
|
world[start + i] = 0;
|
|
11630
11635
|
}
|
|
11631
11636
|
}
|
|
11637
|
+
var OBJECT_SLOT_SCRATCH = new Float32Array(OBJECT_STRIDE);
|
|
11638
|
+
var OBJECT_SLOT_SCRATCH_BUF = Buffer.from(
|
|
11639
|
+
OBJECT_SLOT_SCRATCH.buffer,
|
|
11640
|
+
OBJECT_SLOT_SCRATCH.byteOffset,
|
|
11641
|
+
OBJECT_SLOT_BYTE_LENGTH
|
|
11642
|
+
);
|
|
11632
11643
|
function writeObjectSlot(world, slot, objectId, posX, posY, posZ, posW, dirX, dirY, dirZ, dirW) {
|
|
11633
11644
|
const start = slot * OBJECT_STRIDE;
|
|
11634
11645
|
world[start] = objectId;
|
|
@@ -11651,6 +11662,18 @@ function collectActiveObjectIds(world) {
|
|
|
11651
11662
|
}
|
|
11652
11663
|
return ids;
|
|
11653
11664
|
}
|
|
11665
|
+
function collectActiveObjectIdsInto(world, dest) {
|
|
11666
|
+
let count = 0;
|
|
11667
|
+
const limit = Math.min(MAX_LIVE_OBJECTS, dest.length);
|
|
11668
|
+
for (let slot = 0; slot < MAX_LIVE_OBJECTS && count < limit; slot += 1) {
|
|
11669
|
+
const objectId = readSlotObjectId(world, slot);
|
|
11670
|
+
if (objectId !== 0) {
|
|
11671
|
+
dest[count] = objectId;
|
|
11672
|
+
count += 1;
|
|
11673
|
+
}
|
|
11674
|
+
}
|
|
11675
|
+
return count;
|
|
11676
|
+
}
|
|
11654
11677
|
function liveWorldSnapshot(world, owners) {
|
|
11655
11678
|
const ids = collectActiveObjectIds(world);
|
|
11656
11679
|
ids.sort((a, b) => a - b);
|
|
@@ -11677,39 +11700,37 @@ function clampSimulationDtSec(elapsedMs, fallbackHz) {
|
|
|
11677
11700
|
const elapsedSec = elapsedMs / 1e3;
|
|
11678
11701
|
return Math.min(maxDt, Math.max(minDt, elapsedSec));
|
|
11679
11702
|
}
|
|
11680
|
-
function normalizeWorldBuffer(raw) {
|
|
11703
|
+
function normalizeWorldBuffer(raw, dest = createEmptyWorldBuffer()) {
|
|
11704
|
+
const destBytes = new Uint8Array(
|
|
11705
|
+
dest.buffer,
|
|
11706
|
+
dest.byteOffset,
|
|
11707
|
+
dest.byteLength
|
|
11708
|
+
);
|
|
11681
11709
|
if (!raw || raw.byteLength === 0) {
|
|
11682
|
-
|
|
11710
|
+
destBytes.fill(0);
|
|
11711
|
+
return dest;
|
|
11683
11712
|
}
|
|
11684
|
-
const
|
|
11685
|
-
|
|
11686
|
-
|
|
11713
|
+
const byteCount = Math.min(
|
|
11714
|
+
Math.floor(raw.byteLength / 4) * 4,
|
|
11715
|
+
destBytes.byteLength
|
|
11716
|
+
);
|
|
11717
|
+
if (byteCount > 0) {
|
|
11718
|
+
destBytes.set(raw.subarray(0, byteCount));
|
|
11687
11719
|
}
|
|
11688
|
-
|
|
11689
|
-
|
|
11690
|
-
if (decoded.length === WORLD_FLOAT_COUNT) {
|
|
11691
|
-
return decoded;
|
|
11720
|
+
if (byteCount < destBytes.byteLength) {
|
|
11721
|
+
destBytes.fill(0, byteCount);
|
|
11692
11722
|
}
|
|
11693
|
-
|
|
11694
|
-
normalized.set(
|
|
11695
|
-
decoded.subarray(0, Math.min(decoded.length, WORLD_FLOAT_COUNT))
|
|
11696
|
-
);
|
|
11697
|
-
return normalized;
|
|
11698
|
-
}
|
|
11699
|
-
function objectIdHeaderBytes(objectId) {
|
|
11700
|
-
const header = new Float32Array([objectId]);
|
|
11701
|
-
return Buffer.from(header.buffer, header.byteOffset, 4);
|
|
11723
|
+
return dest;
|
|
11702
11724
|
}
|
|
11703
|
-
|
|
11704
|
-
|
|
11705
|
-
function buildObjectIdHeadersBlob() {
|
|
11706
|
-
const parts = [];
|
|
11725
|
+
var OBJECT_ID_HEADERS_BLOB = (() => {
|
|
11726
|
+
const blob = Buffer.alloc(MAX_LIVE_OBJECTS * 4);
|
|
11707
11727
|
for (let objectId = 1; objectId <= MAX_LIVE_OBJECTS; objectId += 1) {
|
|
11708
|
-
|
|
11728
|
+
blob.writeFloatLE(objectId, (objectId - 1) * 4);
|
|
11709
11729
|
}
|
|
11710
|
-
return
|
|
11711
|
-
}
|
|
11712
|
-
|
|
11730
|
+
return blob;
|
|
11731
|
+
})();
|
|
11732
|
+
|
|
11733
|
+
// templates/game-sync/redis.ts
|
|
11713
11734
|
var LUA_ALLOCATE_OBJECT = `
|
|
11714
11735
|
local key = KEYS[1]
|
|
11715
11736
|
local size = tonumber(ARGV[1])
|
|
@@ -11797,14 +11818,15 @@ var REDIS_EVAL_KEYS = {
|
|
|
11797
11818
|
headers: OBJECT_ID_HEADERS_BLOB
|
|
11798
11819
|
};
|
|
11799
11820
|
|
|
11800
|
-
// templates/game-sync
|
|
11821
|
+
// templates/game-sync/sim.ts
|
|
11801
11822
|
var BOARD_WIDTH = 1280;
|
|
11802
11823
|
var BOARD_HEIGHT = 720;
|
|
11803
11824
|
var OBJECT_RADIUS = 25;
|
|
11804
11825
|
var COLLISION_RESTITUTION = 1;
|
|
11805
11826
|
var OBJECT_STRIDE2 = 9;
|
|
11806
|
-
function simulateWorldStep(worldState2, dtSec,
|
|
11807
|
-
for (
|
|
11827
|
+
function simulateWorldStep(worldState2, dtSec, activeObjectIds2, count = activeObjectIds2.length) {
|
|
11828
|
+
for (let i = 0; i < count; i += 1) {
|
|
11829
|
+
const objectId = activeObjectIds2[i] ?? 0;
|
|
11808
11830
|
const slot = objectIdToSlot(objectId);
|
|
11809
11831
|
const start = slot * OBJECT_STRIDE2;
|
|
11810
11832
|
if (start < 0 || start + OBJECT_STRIDE2 > worldState2.length) continue;
|
|
@@ -11827,13 +11849,13 @@ function simulateWorldStep(worldState2, dtSec, activeObjectIds) {
|
|
|
11827
11849
|
worldState2[start + 5] = vx;
|
|
11828
11850
|
worldState2[start + 6] = vy;
|
|
11829
11851
|
}
|
|
11830
|
-
for (let i = 0; i <
|
|
11831
|
-
const aId =
|
|
11852
|
+
for (let i = 0; i < count; i += 1) {
|
|
11853
|
+
const aId = activeObjectIds2[i] ?? 0;
|
|
11832
11854
|
const aSlot = objectIdToSlot(aId);
|
|
11833
11855
|
const aStart = aSlot * OBJECT_STRIDE2;
|
|
11834
11856
|
if (aStart < 0 || aStart + OBJECT_STRIDE2 > worldState2.length) continue;
|
|
11835
|
-
for (let j = i + 1; j <
|
|
11836
|
-
const bId =
|
|
11857
|
+
for (let j = i + 1; j < count; j += 1) {
|
|
11858
|
+
const bId = activeObjectIds2[j] ?? 0;
|
|
11837
11859
|
const bSlot = objectIdToSlot(bId);
|
|
11838
11860
|
const bStart = bSlot * OBJECT_STRIDE2;
|
|
11839
11861
|
if (bStart < 0 || bStart + OBJECT_STRIDE2 > worldState2.length) continue;
|
|
@@ -11900,7 +11922,7 @@ function simulateWorldStep(worldState2, dtSec, activeObjectIds) {
|
|
|
11900
11922
|
}
|
|
11901
11923
|
}
|
|
11902
11924
|
|
|
11903
|
-
// templates/game-sync.ts
|
|
11925
|
+
// templates/game-sync/agent.ts
|
|
11904
11926
|
var BROADCAST_HZ = 60;
|
|
11905
11927
|
var BROADCAST_INTERVAL_MS = Math.floor(1e3 / BROADCAST_HZ);
|
|
11906
11928
|
var SIM_LOCK_TTL_MS = BROADCAST_INTERVAL_MS * 2;
|
|
@@ -11908,10 +11930,16 @@ var SIM_LOCK_RETRY_MS = 5;
|
|
|
11908
11930
|
var MIN_SPEED = 90;
|
|
11909
11931
|
var MAX_SPEED = 180;
|
|
11910
11932
|
var connectedSessions = /* @__PURE__ */ new Set();
|
|
11933
|
+
var connectedSessionList = [];
|
|
11911
11934
|
var objectOwners = /* @__PURE__ */ new Map();
|
|
11912
11935
|
var sessionObjects = /* @__PURE__ */ new Map();
|
|
11913
11936
|
var freeSlots = [];
|
|
11914
11937
|
var worldState = createEmptyWorldBuffer();
|
|
11938
|
+
var worldStateBytes = Buffer.from(
|
|
11939
|
+
worldState.buffer,
|
|
11940
|
+
worldState.byteOffset,
|
|
11941
|
+
worldState.byteLength
|
|
11942
|
+
);
|
|
11915
11943
|
var redis = null;
|
|
11916
11944
|
var broadcastTimer = null;
|
|
11917
11945
|
var worldMutationChain = Promise.resolve();
|
|
@@ -11961,18 +11989,22 @@ function rand(min, max) {
|
|
|
11961
11989
|
function randomVelocity() {
|
|
11962
11990
|
return (Math.random() < 0.5 ? -1 : 1) * rand(MIN_SPEED, MAX_SPEED);
|
|
11963
11991
|
}
|
|
11992
|
+
var INITIAL_TAIL = new Float32Array(8);
|
|
11993
|
+
var INITIAL_TAIL_BUF = Buffer.from(
|
|
11994
|
+
INITIAL_TAIL.buffer,
|
|
11995
|
+
INITIAL_TAIL.byteOffset,
|
|
11996
|
+
INITIAL_TAIL.byteLength
|
|
11997
|
+
);
|
|
11964
11998
|
function randomInitialTail() {
|
|
11965
|
-
|
|
11966
|
-
|
|
11967
|
-
|
|
11968
|
-
|
|
11969
|
-
|
|
11970
|
-
|
|
11971
|
-
|
|
11972
|
-
|
|
11973
|
-
|
|
11974
|
-
]);
|
|
11975
|
-
return Buffer.from(floats.buffer, floats.byteOffset, floats.byteLength);
|
|
11999
|
+
INITIAL_TAIL[0] = rand(OBJECT_RADIUS, BOARD_WIDTH - OBJECT_RADIUS);
|
|
12000
|
+
INITIAL_TAIL[1] = rand(OBJECT_RADIUS, BOARD_HEIGHT - OBJECT_RADIUS);
|
|
12001
|
+
INITIAL_TAIL[2] = 0;
|
|
12002
|
+
INITIAL_TAIL[3] = 1;
|
|
12003
|
+
INITIAL_TAIL[4] = randomVelocity();
|
|
12004
|
+
INITIAL_TAIL[5] = randomVelocity();
|
|
12005
|
+
INITIAL_TAIL[6] = 0;
|
|
12006
|
+
INITIAL_TAIL[7] = 0;
|
|
12007
|
+
return INITIAL_TAIL_BUF;
|
|
11976
12008
|
}
|
|
11977
12009
|
function attachObjectToSession(sessionId, objectId) {
|
|
11978
12010
|
let owned = sessionObjects.get(sessionId);
|
|
@@ -12054,7 +12086,7 @@ async function registerObjectInRedis(sessionId) {
|
|
|
12054
12086
|
return null;
|
|
12055
12087
|
}
|
|
12056
12088
|
attachObjectToSession(sessionId, objectId);
|
|
12057
|
-
|
|
12089
|
+
await loadWorldFromRedis();
|
|
12058
12090
|
return objectId;
|
|
12059
12091
|
}
|
|
12060
12092
|
async function releaseObjectInRedis(objectId) {
|
|
@@ -12074,7 +12106,7 @@ async function releaseObjectInRedis(objectId) {
|
|
|
12074
12106
|
REDIS_EVAL_KEYS.headers
|
|
12075
12107
|
);
|
|
12076
12108
|
if (Number(released) === 1) {
|
|
12077
|
-
|
|
12109
|
+
await loadWorldFromRedis();
|
|
12078
12110
|
}
|
|
12079
12111
|
return Number(released) === 1;
|
|
12080
12112
|
}
|
|
@@ -12126,83 +12158,84 @@ function notifyObjectReleased(objectId, ownerSessionId) {
|
|
|
12126
12158
|
});
|
|
12127
12159
|
}
|
|
12128
12160
|
}
|
|
12129
|
-
function
|
|
12130
|
-
|
|
12131
|
-
|
|
12132
|
-
function broadcastWorldBuffer(world) {
|
|
12133
|
-
if (connectedSessions.size === 0) return;
|
|
12134
|
-
const payload = copyWorldBuffer(world);
|
|
12135
|
-
for (const sessionId of connectedSessions) {
|
|
12136
|
-
sendBinaryToClient(sessionId, payload, "sync");
|
|
12137
|
-
}
|
|
12161
|
+
function broadcastWorldBuffer() {
|
|
12162
|
+
if (connectedSessionList.length === 0) return;
|
|
12163
|
+
broadCastBinaryToClients(worldStateBytes, connectedSessionList, "sync");
|
|
12138
12164
|
}
|
|
12139
12165
|
async function loadWorldFromRedis() {
|
|
12140
|
-
if (!redis)
|
|
12141
|
-
return new Float32Array(worldState);
|
|
12142
|
-
}
|
|
12166
|
+
if (!redis) return;
|
|
12143
12167
|
const raw = await redis.getBuffer(REDIS_WORLD_KEY);
|
|
12144
|
-
|
|
12168
|
+
normalizeWorldBuffer(raw, worldState);
|
|
12145
12169
|
}
|
|
12146
|
-
async function saveWorldToRedis(
|
|
12170
|
+
async function saveWorldToRedis() {
|
|
12147
12171
|
if (!redis) return;
|
|
12148
|
-
await redis.set(REDIS_WORLD_KEY,
|
|
12172
|
+
await redis.set(REDIS_WORLD_KEY, worldStateBytes);
|
|
12149
12173
|
}
|
|
12150
|
-
|
|
12174
|
+
var activeObjectIds = new Int32Array(MAX_LIVE_OBJECTS);
|
|
12175
|
+
var SIM_LOCK_TRY_ONCE = { retryUntilAcquired: false };
|
|
12176
|
+
var SIM_TICK_PLAN_INPUT = { lockAcquired: false, connectedSessions };
|
|
12177
|
+
function takeTickDtSec() {
|
|
12151
12178
|
const now = Date.now();
|
|
12152
12179
|
const dt = clampSimulationDtSec(
|
|
12153
12180
|
lastTickTimeMs === 0 ? 0 : now - lastTickTimeMs,
|
|
12154
12181
|
BROADCAST_HZ
|
|
12155
12182
|
);
|
|
12156
12183
|
lastTickTimeMs = now;
|
|
12184
|
+
return dt;
|
|
12185
|
+
}
|
|
12186
|
+
function stepWorldInPlace() {
|
|
12187
|
+
const dt = takeTickDtSec();
|
|
12188
|
+
const count = collectActiveObjectIdsInto(worldState, activeObjectIds);
|
|
12189
|
+
simulateWorldStep(worldState, dt, activeObjectIds, count);
|
|
12190
|
+
}
|
|
12191
|
+
async function stepWorldInMemory() {
|
|
12192
|
+
stepWorldInPlace();
|
|
12193
|
+
}
|
|
12194
|
+
async function stepWorldWithRedis() {
|
|
12195
|
+
await loadWorldFromRedis();
|
|
12196
|
+
stepWorldInPlace();
|
|
12197
|
+
await saveWorldToRedis();
|
|
12198
|
+
broadcastWorldBuffer();
|
|
12199
|
+
return true;
|
|
12200
|
+
}
|
|
12201
|
+
async function runRedisTick() {
|
|
12202
|
+
const lockResult = await withRedisSimLock(
|
|
12203
|
+
stepWorldWithRedis,
|
|
12204
|
+
SIM_LOCK_TRY_ONCE
|
|
12205
|
+
);
|
|
12206
|
+
if (lockResult !== null) return;
|
|
12207
|
+
takeTickDtSec();
|
|
12208
|
+
if (planRedisSimTick(SIM_TICK_PLAN_INPUT) === "relay") {
|
|
12209
|
+
await loadWorldFromRedis();
|
|
12210
|
+
broadcastWorldBuffer();
|
|
12211
|
+
}
|
|
12212
|
+
}
|
|
12213
|
+
async function runSimulationTick() {
|
|
12157
12214
|
if (!redis) {
|
|
12158
|
-
await withWorldMutation(
|
|
12159
|
-
|
|
12160
|
-
});
|
|
12161
|
-
broadcastWorldBuffer(worldState);
|
|
12215
|
+
await withWorldMutation(stepWorldInMemory);
|
|
12216
|
+
broadcastWorldBuffer();
|
|
12162
12217
|
return;
|
|
12163
12218
|
}
|
|
12164
|
-
await withWorldMutation(
|
|
12165
|
-
|
|
12166
|
-
|
|
12167
|
-
|
|
12168
|
-
|
|
12169
|
-
|
|
12170
|
-
|
|
12171
|
-
|
|
12172
|
-
|
|
12173
|
-
|
|
12174
|
-
|
|
12175
|
-
{ retryUntilAcquired: false }
|
|
12176
|
-
);
|
|
12177
|
-
if (lockResult === null) {
|
|
12178
|
-
const plan = planRedisSimTick({
|
|
12179
|
-
lockAcquired: false,
|
|
12180
|
-
connectedSessions
|
|
12181
|
-
});
|
|
12182
|
-
if (plan === "relay") {
|
|
12183
|
-
const world = await loadWorldFromRedis();
|
|
12184
|
-
worldState = world;
|
|
12185
|
-
broadcastWorldBuffer(world);
|
|
12186
|
-
}
|
|
12219
|
+
await withWorldMutation(runRedisTick);
|
|
12220
|
+
}
|
|
12221
|
+
function logTickError(error) {
|
|
12222
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
12223
|
+
agentLog("error", `world tick failed: ${detail}`);
|
|
12224
|
+
}
|
|
12225
|
+
function onBroadcastTick() {
|
|
12226
|
+
if (!redis && connectedSessions.size < 1) {
|
|
12227
|
+
if (broadcastTimer) {
|
|
12228
|
+
clearInterval(broadcastTimer);
|
|
12229
|
+
broadcastTimer = null;
|
|
12187
12230
|
}
|
|
12188
|
-
|
|
12231
|
+
return;
|
|
12232
|
+
}
|
|
12233
|
+
void runSimulationTick().catch(logTickError);
|
|
12189
12234
|
}
|
|
12190
12235
|
function startBroadcastLoopIfNeeded() {
|
|
12191
12236
|
if (broadcastTimer) return;
|
|
12192
12237
|
if (!redis && connectedSessions.size < 1) return;
|
|
12193
|
-
broadcastTimer = setInterval(
|
|
12194
|
-
if (!redis && connectedSessions.size < 1) {
|
|
12195
|
-
if (broadcastTimer) {
|
|
12196
|
-
clearInterval(broadcastTimer);
|
|
12197
|
-
broadcastTimer = null;
|
|
12198
|
-
}
|
|
12199
|
-
return;
|
|
12200
|
-
}
|
|
12201
|
-
void runSimulationTick().catch((error) => {
|
|
12202
|
-
const detail = error instanceof Error ? error.message : String(error);
|
|
12203
|
-
agentLog("error", `world tick failed: ${detail}`);
|
|
12204
|
-
});
|
|
12205
|
-
}, BROADCAST_INTERVAL_MS);
|
|
12238
|
+
broadcastTimer = setInterval(onBroadcastTick, BROADCAST_INTERVAL_MS);
|
|
12206
12239
|
agentLog("info", `world loop started (${BROADCAST_HZ}Hz)`);
|
|
12207
12240
|
}
|
|
12208
12241
|
function stopBroadcastLoopIfNeeded() {
|
|
@@ -12217,8 +12250,7 @@ async function ensureRedisWorldInitialized() {
|
|
|
12217
12250
|
if (!redis) return;
|
|
12218
12251
|
const existing = await redis.getBuffer(REDIS_WORLD_KEY);
|
|
12219
12252
|
if (!existing || existing.byteLength === 0) {
|
|
12220
|
-
|
|
12221
|
-
await redis.set(REDIS_WORLD_KEY, copyWorldBuffer(empty));
|
|
12253
|
+
await redis.set(REDIS_WORLD_KEY, worldStateBytes);
|
|
12222
12254
|
}
|
|
12223
12255
|
}
|
|
12224
12256
|
defineAgent({
|
|
@@ -12229,25 +12261,25 @@ defineAgent({
|
|
|
12229
12261
|
"warn",
|
|
12230
12262
|
"AGENT_REDIS_URL unset \u2014 game-sync uses per-worker in-memory world only"
|
|
12231
12263
|
);
|
|
12232
|
-
worldState = createEmptyWorldBuffer();
|
|
12233
12264
|
return;
|
|
12234
12265
|
}
|
|
12235
|
-
redis = new import_ioredis.
|
|
12266
|
+
redis = new import_ioredis.Redis(redisUrl, {
|
|
12236
12267
|
maxRetriesPerRequest: 3,
|
|
12237
12268
|
lazyConnect: true
|
|
12238
12269
|
});
|
|
12239
12270
|
await redis.connect();
|
|
12240
12271
|
await ensureRedisWorldInitialized();
|
|
12241
|
-
|
|
12272
|
+
await loadWorldFromRedis();
|
|
12242
12273
|
startBroadcastLoopIfNeeded();
|
|
12243
12274
|
agentLog("info", "game-sync agent connected to project Redis world buffer");
|
|
12244
12275
|
},
|
|
12245
12276
|
async onClientJoin({ sessionId }) {
|
|
12246
|
-
connectedSessions.
|
|
12247
|
-
|
|
12248
|
-
|
|
12249
|
-
worldState = await loadWorldFromRedis();
|
|
12277
|
+
if (!connectedSessions.has(sessionId)) {
|
|
12278
|
+
connectedSessions.add(sessionId);
|
|
12279
|
+
connectedSessionList.push(sessionId);
|
|
12250
12280
|
}
|
|
12281
|
+
startBroadcastLoopIfNeeded();
|
|
12282
|
+
await loadWorldFromRedis();
|
|
12251
12283
|
sendToClient(sessionId, {
|
|
12252
12284
|
type: "world_snapshot",
|
|
12253
12285
|
objects: liveWorldSnapshot(worldState, objectOwners)
|
|
@@ -12255,7 +12287,12 @@ defineAgent({
|
|
|
12255
12287
|
agentLog("info", `join ${sessionId} connected=${connectedSessions.size}`);
|
|
12256
12288
|
},
|
|
12257
12289
|
async onClientLeave({ sessionId }) {
|
|
12258
|
-
connectedSessions.delete(sessionId)
|
|
12290
|
+
if (connectedSessions.delete(sessionId)) {
|
|
12291
|
+
const index = connectedSessionList.indexOf(sessionId);
|
|
12292
|
+
if (index !== -1) {
|
|
12293
|
+
connectedSessionList.splice(index, 1);
|
|
12294
|
+
}
|
|
12295
|
+
}
|
|
12259
12296
|
for (const [objectId, ownerSessionId] of objectOwners) {
|
|
12260
12297
|
if (ownerSessionId === sessionId) {
|
|
12261
12298
|
objectOwners.delete(objectId);
|
|
@@ -849,7 +849,7 @@ function createInitialState(recordingAvailable) {
|
|
|
849
849
|
consentSkipped: true
|
|
850
850
|
};
|
|
851
851
|
}
|
|
852
|
-
function
|
|
852
|
+
function speakAndChat2(text) {
|
|
853
853
|
return {
|
|
854
854
|
speakLines: [text],
|
|
855
855
|
messages: [{ type: "chat_reply", text }]
|
|
@@ -858,7 +858,7 @@ function speakAndChat(text) {
|
|
|
858
858
|
function beginSession(recordingAvailable) {
|
|
859
859
|
const state = createInitialState(recordingAvailable);
|
|
860
860
|
if (recordingAvailable) {
|
|
861
|
-
const prompt =
|
|
861
|
+
const prompt = speakAndChat2(CONSENT_PROMPT);
|
|
862
862
|
return {
|
|
863
863
|
state,
|
|
864
864
|
speakLines: prompt.speakLines,
|
|
@@ -866,8 +866,8 @@ function beginSession(recordingAvailable) {
|
|
|
866
866
|
recordingAction: null
|
|
867
867
|
};
|
|
868
868
|
}
|
|
869
|
-
const skip =
|
|
870
|
-
const name =
|
|
869
|
+
const skip = speakAndChat2(RECORDING_DISABLED_SKIP_MESSAGE);
|
|
870
|
+
const name = speakAndChat2(NAME_PROMPT);
|
|
871
871
|
return {
|
|
872
872
|
state,
|
|
873
873
|
speakLines: [...skip.speakLines, ...name.speakLines],
|
|
@@ -951,7 +951,7 @@ function sanitizeToken(raw, maxLen) {
|
|
|
951
951
|
return value;
|
|
952
952
|
}
|
|
953
953
|
function askNameAgain(state) {
|
|
954
|
-
const prompt =
|
|
954
|
+
const prompt = speakAndChat2(
|
|
955
955
|
"Sorry, I didn't catch your name. May I have your name please?"
|
|
956
956
|
);
|
|
957
957
|
return {
|
|
@@ -962,7 +962,7 @@ function askNameAgain(state) {
|
|
|
962
962
|
};
|
|
963
963
|
}
|
|
964
964
|
function askBirthdateAgain(state) {
|
|
965
|
-
const prompt =
|
|
965
|
+
const prompt = speakAndChat2(
|
|
966
966
|
"Sorry, I didn't catch your date of birth. Could you repeat it?"
|
|
967
967
|
);
|
|
968
968
|
return {
|
|
@@ -974,7 +974,7 @@ function askBirthdateAgain(state) {
|
|
|
974
974
|
}
|
|
975
975
|
function finishAfterPii(state) {
|
|
976
976
|
const next = { ...state, phase: "complete" };
|
|
977
|
-
const thankYou =
|
|
977
|
+
const thankYou = speakAndChat2(
|
|
978
978
|
`Thank you, ${state.name}. We have your date of birth on file.`
|
|
979
979
|
);
|
|
980
980
|
let recordingAction = null;
|
|
@@ -997,7 +997,7 @@ function handleUtterance(state, utterance) {
|
|
|
997
997
|
phase: "awaitingName",
|
|
998
998
|
consent: false
|
|
999
999
|
};
|
|
1000
|
-
const name =
|
|
1000
|
+
const name = speakAndChat2(NAME_PROMPT);
|
|
1001
1001
|
return {
|
|
1002
1002
|
state: next,
|
|
1003
1003
|
speakLines: name.speakLines,
|
|
@@ -1011,7 +1011,7 @@ function handleUtterance(state, utterance) {
|
|
|
1011
1011
|
phase: "awaitingName",
|
|
1012
1012
|
consent: true
|
|
1013
1013
|
};
|
|
1014
|
-
const name =
|
|
1014
|
+
const name = speakAndChat2(NAME_PROMPT);
|
|
1015
1015
|
return {
|
|
1016
1016
|
state: next,
|
|
1017
1017
|
speakLines: name.speakLines,
|
|
@@ -1019,7 +1019,7 @@ function handleUtterance(state, utterance) {
|
|
|
1019
1019
|
recordingAction: "pause"
|
|
1020
1020
|
};
|
|
1021
1021
|
}
|
|
1022
|
-
const retry =
|
|
1022
|
+
const retry = speakAndChat2(
|
|
1023
1023
|
"Please say yes or no \u2014 may we record this conversation?"
|
|
1024
1024
|
);
|
|
1025
1025
|
return {
|
|
@@ -1039,7 +1039,7 @@ function handleUtterance(state, utterance) {
|
|
|
1039
1039
|
phase: "awaitingBirthdate",
|
|
1040
1040
|
name
|
|
1041
1041
|
};
|
|
1042
|
-
const birthdate =
|
|
1042
|
+
const birthdate = speakAndChat2(BIRTHDATE_PROMPT);
|
|
1043
1043
|
return {
|
|
1044
1044
|
state: next,
|
|
1045
1045
|
speakLines: birthdate.speakLines,
|
|
@@ -1055,7 +1055,7 @@ function handleUtterance(state, utterance) {
|
|
|
1055
1055
|
return finishAfterPii({ ...state, birthdate });
|
|
1056
1056
|
}
|
|
1057
1057
|
case "complete": {
|
|
1058
|
-
const done =
|
|
1058
|
+
const done = speakAndChat2("We are all set. How can I help you today?");
|
|
1059
1059
|
return {
|
|
1060
1060
|
state,
|
|
1061
1061
|
speakLines: done.speakLines,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type TemplateKind = "product" | "e2e";
|
|
2
2
|
export interface AgentTemplateDefinition {
|
|
3
3
|
id: string;
|
|
4
|
-
/** Path relative to `templates/` (e.g. `echo.ts`, `redis-sync/agent.ts`). */
|
|
4
|
+
/** Path relative to `templates/` (e.g. `echo/agent.ts`, `redis-sync/agent.ts`). */
|
|
5
5
|
entry: string;
|
|
6
6
|
kind: TemplateKind;
|
|
7
7
|
/** When true, a prebuilt bundle is published at `dist/templates/<id>/agent.js`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/templates/registry.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,KAAK,CAAC;AAE7C,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/templates/registry.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,KAAK,CAAC;AAE7C,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,mFAAmF;IACnF,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,YAAY,CAAC;IACnB,mFAAmF;IACnF,YAAY,EAAE,OAAO,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,oFAAoF;IACpF,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C;AAED,eAAO,MAAM,eAAe,EAAE,SAAS,uBAAuB,EAqLpD,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;AAMrE,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,MAAM,GAAG,EAAE,IAAI,eAAe,CAEnE;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,uBAAuB,CAMnE;AAED,wBAAgB,yBAAyB,IAAI,uBAAuB,EAAE,CAErE"}
|