@xfxstudio/claworld 2026.6.10-testing.3 → 2026.6.10-testing.5
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/openclaw.plugin.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
},
|
|
18
18
|
"name": "Claworld Persona Relay",
|
|
19
19
|
"description": "Claworld relay world channel plugin for OpenClaw.",
|
|
20
|
-
"version": "2026.6.10-testing.
|
|
20
|
+
"version": "2026.6.10-testing.5",
|
|
21
21
|
"configSchema": {
|
|
22
22
|
"type": "object",
|
|
23
23
|
"additionalProperties": false,
|
package/package.json
CHANGED
|
@@ -54,3 +54,14 @@ export function resolveConversationViewerRoot({
|
|
|
54
54
|
|| path.join(homeDir, '.openclaw');
|
|
55
55
|
return path.resolve(stateDir.replace(/^~(?=$|\/|\\)/, homeDir), 'claworld', 'conversation-viewers');
|
|
56
56
|
}
|
|
57
|
+
|
|
58
|
+
export function resolveConversationViewerOpenClawRoot({
|
|
59
|
+
env = null,
|
|
60
|
+
homeDir = os.homedir(),
|
|
61
|
+
} = {}) {
|
|
62
|
+
const sourceEnv = resolveEnv(env);
|
|
63
|
+
const stateDir = normalizeText(sourceEnv.OPENCLAW_STATE_DIR, null)
|
|
64
|
+
|| normalizeText(sourceEnv.OPENCLAW_HOME, null)
|
|
65
|
+
|| path.join(homeDir, '.openclaw');
|
|
66
|
+
return path.resolve(stateDir.replace(/^~(?=$|\/|\\)/, homeDir));
|
|
67
|
+
}
|
|
@@ -5,6 +5,7 @@ import { pathToFileURL } from 'node:url';
|
|
|
5
5
|
import { buildRuntimeAuthHeaders } from './account-identity.js';
|
|
6
6
|
import {
|
|
7
7
|
resolveConversationViewerGatewayBaseUrl,
|
|
8
|
+
resolveConversationViewerOpenClawRoot,
|
|
8
9
|
resolveConversationViewerRoot,
|
|
9
10
|
} from './conversation-viewer-env.js';
|
|
10
11
|
import { loadCurrentConfig } from './register-tooling.js';
|
|
@@ -14,6 +15,7 @@ export const CONVERSATION_VIEWER_ROUTE_PREFIX = '/plugins/claworld/conversation-
|
|
|
14
15
|
export const CONVERSATION_VIEWER_SCHEMA = 'claworld.conversationViewer.v1';
|
|
15
16
|
export {
|
|
16
17
|
resolveConversationViewerGatewayBaseUrl,
|
|
18
|
+
resolveConversationViewerOpenClawRoot,
|
|
17
19
|
resolveConversationViewerRoot,
|
|
18
20
|
} from './conversation-viewer-env.js';
|
|
19
21
|
|
|
@@ -984,10 +986,18 @@ function buildStateLabel(status, { peerName = null, peerOnline = null } = {}) {
|
|
|
984
986
|
return peerName ? `Conversation with ${peerName}` : 'Claworld conversation';
|
|
985
987
|
}
|
|
986
988
|
|
|
987
|
-
function resolveSnapshotStatus({
|
|
989
|
+
function resolveSnapshotStatus({
|
|
990
|
+
request = null,
|
|
991
|
+
chat = null,
|
|
992
|
+
conversation = null,
|
|
993
|
+
fallbackStatus = null,
|
|
994
|
+
} = {}) {
|
|
988
995
|
const conversationStatus = normalizeText(conversation?.status, null);
|
|
989
996
|
if (conversationStatus === 'closed') return 'ended';
|
|
990
|
-
return normalizeText(
|
|
997
|
+
return normalizeText(
|
|
998
|
+
chat?.status,
|
|
999
|
+
normalizeText(request?.status, normalizeText(conversationStatus, normalizeText(fallbackStatus, 'pending'))),
|
|
1000
|
+
);
|
|
991
1001
|
}
|
|
992
1002
|
|
|
993
1003
|
function collectTurns(turnsPayload = {}) {
|
|
@@ -1000,6 +1010,294 @@ function collectTurns(turnsPayload = {}) {
|
|
|
1000
1010
|
});
|
|
1001
1011
|
}
|
|
1002
1012
|
|
|
1013
|
+
function uniqueNormalized(values = []) {
|
|
1014
|
+
const seen = new Set();
|
|
1015
|
+
const next = [];
|
|
1016
|
+
for (const value of values) {
|
|
1017
|
+
const normalized = normalizeText(value, null);
|
|
1018
|
+
if (!normalized || seen.has(normalized)) continue;
|
|
1019
|
+
seen.add(normalized);
|
|
1020
|
+
next.push(normalized);
|
|
1021
|
+
}
|
|
1022
|
+
return next;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
function normalizeSessionStoreKey(value) {
|
|
1026
|
+
return normalizeText(value, '').toLowerCase();
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
function extractLocalAgentIdsFromManifest(manifest = {}) {
|
|
1030
|
+
const ids = [];
|
|
1031
|
+
for (const value of [manifest.localSessionKey, manifest.sessionKey]) {
|
|
1032
|
+
const normalized = normalizeText(value, null);
|
|
1033
|
+
const match = normalized?.match(/^agent:([^:]+):/);
|
|
1034
|
+
if (match?.[1]) ids.push(match[1]);
|
|
1035
|
+
}
|
|
1036
|
+
ids.push('main');
|
|
1037
|
+
return uniqueNormalized(ids);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
function buildLocalSessionKeyCandidates(manifest = {}) {
|
|
1041
|
+
const conversationKey = normalizeText(manifest.conversationKey, null);
|
|
1042
|
+
const localSessionKey = normalizeText(manifest.localSessionKey, null);
|
|
1043
|
+
const keys = [];
|
|
1044
|
+
if (localSessionKey) {
|
|
1045
|
+
keys.push(localSessionKey);
|
|
1046
|
+
if (!localSessionKey.startsWith('agent:')) {
|
|
1047
|
+
for (const localAgentId of extractLocalAgentIdsFromManifest(manifest)) {
|
|
1048
|
+
keys.push(`agent:${localAgentId}:${localSessionKey}`);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
if (conversationKey) {
|
|
1053
|
+
keys.push(`conversation:${conversationKey}`);
|
|
1054
|
+
for (const localAgentId of extractLocalAgentIdsFromManifest(manifest)) {
|
|
1055
|
+
keys.push(`agent:${localAgentId}:conversation:${conversationKey}`);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
return uniqueNormalized(keys);
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
function resolveSessionStoreEntry(store = null, candidateKeys = [], manifest = {}) {
|
|
1062
|
+
if (!store || typeof store !== 'object' || Array.isArray(store)) return null;
|
|
1063
|
+
const normalizedCandidates = new Set(candidateKeys.map(normalizeSessionStoreKey));
|
|
1064
|
+
for (const key of candidateKeys) {
|
|
1065
|
+
if (store[key] && typeof store[key] === 'object' && !Array.isArray(store[key])) {
|
|
1066
|
+
return { key, record: store[key] };
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
for (const [key, record] of Object.entries(store)) {
|
|
1070
|
+
if (
|
|
1071
|
+
normalizedCandidates.has(normalizeSessionStoreKey(key))
|
|
1072
|
+
&& record
|
|
1073
|
+
&& typeof record === 'object'
|
|
1074
|
+
&& !Array.isArray(record)
|
|
1075
|
+
) {
|
|
1076
|
+
return { key, record };
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
const conversationKey = normalizeText(manifest.conversationKey, null);
|
|
1081
|
+
const counterpartyAgentId = normalizeText(manifest.counterpartyAgentId, null);
|
|
1082
|
+
for (const [key, record] of Object.entries(store)) {
|
|
1083
|
+
if (!record || typeof record !== 'object' || Array.isArray(record)) continue;
|
|
1084
|
+
const keyText = normalizeText(key, '');
|
|
1085
|
+
const recordText = [
|
|
1086
|
+
keyText,
|
|
1087
|
+
record.sessionFile,
|
|
1088
|
+
record.transcriptPath,
|
|
1089
|
+
record.lastTo,
|
|
1090
|
+
record.route?.target?.to,
|
|
1091
|
+
record.deliveryContext?.to,
|
|
1092
|
+
record.origin?.to,
|
|
1093
|
+
].map((value) => normalizeText(value, '')).join('\n');
|
|
1094
|
+
if (conversationKey && recordText.includes(conversationKey)) {
|
|
1095
|
+
return { key, record };
|
|
1096
|
+
}
|
|
1097
|
+
if (
|
|
1098
|
+
counterpartyAgentId
|
|
1099
|
+
&& candidateKeys.some((candidate) => keyText.includes(candidate))
|
|
1100
|
+
&& recordText.includes(counterpartyAgentId)
|
|
1101
|
+
) {
|
|
1102
|
+
return { key, record };
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
return null;
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
function resolveTranscriptPathFromSessionRecord({
|
|
1109
|
+
record = {},
|
|
1110
|
+
sessionStorePath = null,
|
|
1111
|
+
} = {}) {
|
|
1112
|
+
const direct = normalizeText(
|
|
1113
|
+
record.transcriptPath,
|
|
1114
|
+
normalizeText(record.sessionFile, normalizeText(record.sessionPath, null)),
|
|
1115
|
+
);
|
|
1116
|
+
if (direct) {
|
|
1117
|
+
if (path.isAbsolute(direct) || !sessionStorePath) return direct;
|
|
1118
|
+
return path.resolve(path.dirname(sessionStorePath), direct);
|
|
1119
|
+
}
|
|
1120
|
+
const sessionId = normalizeText(record.sessionId, normalizeText(record.id, null));
|
|
1121
|
+
if (!sessionId || !sessionStorePath) return null;
|
|
1122
|
+
return path.join(path.dirname(sessionStorePath), `${sessionId}.jsonl`);
|
|
1123
|
+
}
|
|
1124
|
+
|
|
1125
|
+
function extractRequestBrief(content = '') {
|
|
1126
|
+
const normalized = normalizeText(content, null);
|
|
1127
|
+
if (!normalized || !normalized.startsWith('# Background')) return null;
|
|
1128
|
+
const match = normalized.match(/## Request Brief[\s\S]*?```(?:text)?\s*\n([\s\S]*?)```/i);
|
|
1129
|
+
return normalizeText(match?.[1], null);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
function messageText(message = {}) {
|
|
1133
|
+
if (typeof message?.content === 'string') return normalizeText(message.content, '');
|
|
1134
|
+
if (Array.isArray(message?.content)) {
|
|
1135
|
+
return message.content
|
|
1136
|
+
.map((item) => {
|
|
1137
|
+
if (typeof item === 'string') return item;
|
|
1138
|
+
if (item && typeof item === 'object') return item.text || item.content || '';
|
|
1139
|
+
return '';
|
|
1140
|
+
})
|
|
1141
|
+
.filter(Boolean)
|
|
1142
|
+
.join('\n\n')
|
|
1143
|
+
.trim();
|
|
1144
|
+
}
|
|
1145
|
+
return '';
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
function containsConversationEndToken(text = '') {
|
|
1149
|
+
return /\[\[request_conversation_end\]\]/i.test(String(text || ''));
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
function buildLocalTranscriptTurn({
|
|
1153
|
+
line,
|
|
1154
|
+
seq,
|
|
1155
|
+
text,
|
|
1156
|
+
fromAgentId,
|
|
1157
|
+
role,
|
|
1158
|
+
source,
|
|
1159
|
+
suffix = '',
|
|
1160
|
+
} = {}) {
|
|
1161
|
+
const turnId = normalizeText(line.id, null) || `local_${seq}${suffix}`;
|
|
1162
|
+
return {
|
|
1163
|
+
turnId,
|
|
1164
|
+
seq,
|
|
1165
|
+
fromAgentId: normalizeText(fromAgentId, null),
|
|
1166
|
+
role,
|
|
1167
|
+
source,
|
|
1168
|
+
createdAt: normalizeText(line.timestamp, null)
|
|
1169
|
+
|| (Number.isFinite(Number(line.message?.timestamp)) ? new Date(Number(line.message.timestamp)).toISOString() : null)
|
|
1170
|
+
|| new Date().toISOString(),
|
|
1171
|
+
content: [{ type: 'text', text }],
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
async function readLocalTranscriptTurns({
|
|
1176
|
+
transcriptPath = null,
|
|
1177
|
+
manifest = {},
|
|
1178
|
+
viewerAgentId = null,
|
|
1179
|
+
} = {}) {
|
|
1180
|
+
const normalizedPath = normalizeText(transcriptPath, null);
|
|
1181
|
+
if (!normalizedPath) return null;
|
|
1182
|
+
let raw = '';
|
|
1183
|
+
try {
|
|
1184
|
+
raw = await fs.readFile(normalizedPath, 'utf8');
|
|
1185
|
+
} catch {
|
|
1186
|
+
return null;
|
|
1187
|
+
}
|
|
1188
|
+
const peerAgentId = normalizeText(manifest.counterpartyAgentId, 'peer');
|
|
1189
|
+
const turns = [];
|
|
1190
|
+
let seq = 1;
|
|
1191
|
+
let sawNoReply = false;
|
|
1192
|
+
let selfEndRequested = false;
|
|
1193
|
+
let peerEndRequested = false;
|
|
1194
|
+
for (const row of raw.split(/\n+/)) {
|
|
1195
|
+
const lineText = row.trim();
|
|
1196
|
+
if (!lineText) continue;
|
|
1197
|
+
let line = null;
|
|
1198
|
+
try {
|
|
1199
|
+
line = JSON.parse(lineText);
|
|
1200
|
+
} catch {
|
|
1201
|
+
continue;
|
|
1202
|
+
}
|
|
1203
|
+
if (line?.type !== 'message') continue;
|
|
1204
|
+
const message = line.message && typeof line.message === 'object' && !Array.isArray(line.message)
|
|
1205
|
+
? line.message
|
|
1206
|
+
: {};
|
|
1207
|
+
const text = messageText(message);
|
|
1208
|
+
if (!text) continue;
|
|
1209
|
+
|
|
1210
|
+
const brief = extractRequestBrief(text);
|
|
1211
|
+
if (brief) {
|
|
1212
|
+
const requesterIsSelf = normalizeText(manifest.role, null) === 'requester';
|
|
1213
|
+
const fromAgentId = requesterIsSelf ? viewerAgentId : peerAgentId;
|
|
1214
|
+
turns.push(buildLocalTranscriptTurn({
|
|
1215
|
+
line,
|
|
1216
|
+
seq,
|
|
1217
|
+
text: brief,
|
|
1218
|
+
fromAgentId,
|
|
1219
|
+
role: requesterIsSelf ? 'self' : 'peer',
|
|
1220
|
+
source: 'local_transcript_request_brief',
|
|
1221
|
+
suffix: '_brief',
|
|
1222
|
+
}));
|
|
1223
|
+
seq += 1;
|
|
1224
|
+
if (requesterIsSelf && containsConversationEndToken(brief)) selfEndRequested = true;
|
|
1225
|
+
if (!requesterIsSelf && containsConversationEndToken(brief)) peerEndRequested = true;
|
|
1226
|
+
continue;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
if (text === 'NO_REPLY') {
|
|
1230
|
+
sawNoReply = true;
|
|
1231
|
+
continue;
|
|
1232
|
+
}
|
|
1233
|
+
const isAssistant = message.role === 'assistant';
|
|
1234
|
+
const fromAgentId = isAssistant ? viewerAgentId : peerAgentId;
|
|
1235
|
+
if (containsConversationEndToken(text)) {
|
|
1236
|
+
if (isAssistant) selfEndRequested = true;
|
|
1237
|
+
else peerEndRequested = true;
|
|
1238
|
+
}
|
|
1239
|
+
turns.push(buildLocalTranscriptTurn({
|
|
1240
|
+
line,
|
|
1241
|
+
seq,
|
|
1242
|
+
text,
|
|
1243
|
+
fromAgentId,
|
|
1244
|
+
role: isAssistant ? 'self' : 'peer',
|
|
1245
|
+
source: 'local_transcript',
|
|
1246
|
+
}));
|
|
1247
|
+
seq += 1;
|
|
1248
|
+
}
|
|
1249
|
+
return {
|
|
1250
|
+
transcriptPath: normalizedPath,
|
|
1251
|
+
turns,
|
|
1252
|
+
terminal: sawNoReply || (selfEndRequested && peerEndRequested),
|
|
1253
|
+
sawNoReply,
|
|
1254
|
+
};
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
async function loadLocalTranscriptSnapshot({
|
|
1258
|
+
manifest = {},
|
|
1259
|
+
env = null,
|
|
1260
|
+
viewerAgentId = null,
|
|
1261
|
+
} = {}) {
|
|
1262
|
+
const stateRoot = resolveConversationViewerOpenClawRoot({ env });
|
|
1263
|
+
const candidateKeys = buildLocalSessionKeyCandidates(manifest);
|
|
1264
|
+
const localAgentIds = extractLocalAgentIdsFromManifest(manifest);
|
|
1265
|
+
for (const localAgentId of localAgentIds) {
|
|
1266
|
+
const sessionStorePath = path.join(stateRoot, 'agents', localAgentId, 'sessions', 'sessions.json');
|
|
1267
|
+
const store = await readJsonIfPresent(sessionStorePath);
|
|
1268
|
+
const match = resolveSessionStoreEntry(store, candidateKeys, manifest);
|
|
1269
|
+
if (!match) continue;
|
|
1270
|
+
const transcriptPath = resolveTranscriptPathFromSessionRecord({
|
|
1271
|
+
record: match.record,
|
|
1272
|
+
sessionStorePath,
|
|
1273
|
+
});
|
|
1274
|
+
const transcript = await readLocalTranscriptTurns({
|
|
1275
|
+
transcriptPath,
|
|
1276
|
+
manifest,
|
|
1277
|
+
viewerAgentId,
|
|
1278
|
+
});
|
|
1279
|
+
if (transcript && transcript.turns.length > 0) {
|
|
1280
|
+
return {
|
|
1281
|
+
...transcript,
|
|
1282
|
+
sessionStorePath,
|
|
1283
|
+
sessionKey: match.key,
|
|
1284
|
+
sessionId: normalizeText(match.record?.sessionId, null),
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
return null;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
function inferLocalTranscriptStatus(status, localTranscript = null) {
|
|
1292
|
+
const normalized = normalizeText(status, null);
|
|
1293
|
+
if (['ended', 'closed', 'expired', 'rejected', 'silent', 'kickoff_failed', 'failed'].includes(normalized)) {
|
|
1294
|
+
return normalized;
|
|
1295
|
+
}
|
|
1296
|
+
if (localTranscript?.terminal) return 'ended';
|
|
1297
|
+
if (localTranscript?.turns?.length) return 'active';
|
|
1298
|
+
return normalized || 'pending';
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1003
1301
|
async function maybeUpdateManifestFromSnapshot(manifest = {}, patch = {}) {
|
|
1004
1302
|
const nextConversationKey = normalizeText(patch.conversationKey, normalizeText(manifest.conversationKey, null));
|
|
1005
1303
|
const nextLocalSessionKey = normalizeText(patch.localSessionKey, normalizeText(manifest.localSessionKey, null));
|
|
@@ -1024,46 +1322,52 @@ export async function loadConversationViewerSnapshot({
|
|
|
1024
1322
|
runtimeConfig,
|
|
1025
1323
|
fetchImpl = globalThis.fetch,
|
|
1026
1324
|
timeoutMs = DEFAULT_SNAPSHOT_FETCH_TIMEOUT_MS,
|
|
1325
|
+
env = null,
|
|
1027
1326
|
} = {}) {
|
|
1028
1327
|
const fetchedAt = new Date().toISOString();
|
|
1029
1328
|
const diagnostics = [];
|
|
1030
1329
|
const viewerAgentId = normalizeText(manifest.viewerAgentId, runtimeConfig?.relay?.agentId || null);
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
source: 'chat_requests_filtered',
|
|
1041
|
-
status: inboxResult.status,
|
|
1042
|
-
error: inboxResult.error || null,
|
|
1043
|
-
path: inboxResult.path || null,
|
|
1330
|
+
let inbox = {};
|
|
1331
|
+
let request = null;
|
|
1332
|
+
let chat = null;
|
|
1333
|
+
let conversationKey = normalizeText(manifest.conversationKey, null);
|
|
1334
|
+
if (!conversationKey) {
|
|
1335
|
+
const inboxQuery = queryString({
|
|
1336
|
+
agentId: viewerAgentId,
|
|
1337
|
+
chatRequestId: manifest.chatRequestId,
|
|
1338
|
+
localSessionKey: manifest.localSessionKey,
|
|
1044
1339
|
});
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1340
|
+
let inboxResult = await fetchBackendJson(fetchImpl, runtimeConfig, `/v1/chat-requests${inboxQuery}`, { timeoutMs });
|
|
1341
|
+
if (!inboxResult.ok && inboxQuery) {
|
|
1342
|
+
diagnostics.push({
|
|
1343
|
+
source: 'chat_requests_filtered',
|
|
1344
|
+
status: inboxResult.status,
|
|
1345
|
+
error: inboxResult.error || null,
|
|
1346
|
+
path: inboxResult.path || null,
|
|
1347
|
+
});
|
|
1348
|
+
const fallbackQuery = queryString({ agentId: viewerAgentId });
|
|
1349
|
+
inboxResult = await fetchBackendJson(fetchImpl, runtimeConfig, `/v1/chat-requests${fallbackQuery}`, { timeoutMs });
|
|
1350
|
+
}
|
|
1351
|
+
if (!inboxResult.ok) {
|
|
1352
|
+
return {
|
|
1353
|
+
ok: false,
|
|
1354
|
+
error: 'chat_inbox_snapshot_failed',
|
|
1355
|
+
status: inboxResult.status,
|
|
1356
|
+
message: inboxResult.error || null,
|
|
1357
|
+
fetchedAt,
|
|
1358
|
+
viewer: buildPublicConversationViewer(manifest),
|
|
1359
|
+
body: inboxResult.body,
|
|
1360
|
+
diagnostics,
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1363
|
+
inbox = normalizeObject(inboxResult.body, {});
|
|
1364
|
+
request = resolveSnapshotRequest(inbox, manifest);
|
|
1365
|
+
chat = resolveSnapshotChat(inbox, manifest);
|
|
1366
|
+
conversationKey = normalizeText(
|
|
1367
|
+
chat?.conversationKey,
|
|
1368
|
+
normalizeText(request?.kickoff?.conversationKey, null),
|
|
1369
|
+
);
|
|
1059
1370
|
}
|
|
1060
|
-
const inbox = normalizeObject(inboxResult.body, {});
|
|
1061
|
-
const request = resolveSnapshotRequest(inbox, manifest);
|
|
1062
|
-
const chat = resolveSnapshotChat(inbox, manifest);
|
|
1063
|
-
const conversationKey = normalizeText(
|
|
1064
|
-
chat?.conversationKey,
|
|
1065
|
-
normalizeText(manifest.conversationKey, normalizeText(request?.kickoff?.conversationKey, null)),
|
|
1066
|
-
);
|
|
1067
1371
|
let activeManifest = manifest;
|
|
1068
1372
|
if (conversationKey) {
|
|
1069
1373
|
activeManifest = await maybeUpdateManifestFromSnapshot(manifest, {
|
|
@@ -1074,6 +1378,7 @@ export async function loadConversationViewerSnapshot({
|
|
|
1074
1378
|
let conversation = null;
|
|
1075
1379
|
let participants = [];
|
|
1076
1380
|
let turns = [];
|
|
1381
|
+
let localTranscript = null;
|
|
1077
1382
|
if (conversationKey) {
|
|
1078
1383
|
const suffix = queryString({ agentId: viewerAgentId });
|
|
1079
1384
|
const [conversationResult, turnsResult, participantsResult] = await Promise.all([
|
|
@@ -1106,11 +1411,46 @@ export async function loadConversationViewerSnapshot({
|
|
|
1106
1411
|
});
|
|
1107
1412
|
}
|
|
1108
1413
|
}
|
|
1414
|
+
if (turns.length === 0) {
|
|
1415
|
+
localTranscript = await loadLocalTranscriptSnapshot({
|
|
1416
|
+
manifest: activeManifest,
|
|
1417
|
+
env,
|
|
1418
|
+
viewerAgentId,
|
|
1419
|
+
});
|
|
1420
|
+
if (localTranscript?.turns?.length) {
|
|
1421
|
+
turns = localTranscript.turns;
|
|
1422
|
+
diagnostics.push({
|
|
1423
|
+
source: 'local_transcript',
|
|
1424
|
+
status: 200,
|
|
1425
|
+
path: localTranscript.transcriptPath,
|
|
1426
|
+
sessionKey: localTranscript.sessionKey || null,
|
|
1427
|
+
sessionId: localTranscript.sessionId || null,
|
|
1428
|
+
});
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
if (participants.length === 0 && localTranscript?.turns?.length) {
|
|
1432
|
+
participants = [
|
|
1433
|
+
{
|
|
1434
|
+
agentId: viewerAgentId,
|
|
1435
|
+
displayName: 'You',
|
|
1436
|
+
},
|
|
1437
|
+
{
|
|
1438
|
+
agentId: normalizeText(activeManifest.counterpartyAgentId, 'peer'),
|
|
1439
|
+
displayName: normalizeText(activeManifest.counterpartyName, 'Peer agent'),
|
|
1440
|
+
},
|
|
1441
|
+
];
|
|
1442
|
+
}
|
|
1109
1443
|
const peer = normalizeObject(chat?.counterparty, null)
|
|
1110
1444
|
|| normalizeObject(request?.counterparty, null)
|
|
1111
1445
|
|| null;
|
|
1112
1446
|
const peerName = normalizeText(peer?.displayName, normalizeText(activeManifest.counterpartyName, 'Peer agent'));
|
|
1113
|
-
const
|
|
1447
|
+
const resolvedStatus = resolveSnapshotStatus({
|
|
1448
|
+
request,
|
|
1449
|
+
chat,
|
|
1450
|
+
conversation,
|
|
1451
|
+
fallbackStatus: activeManifest.targetStatus,
|
|
1452
|
+
});
|
|
1453
|
+
const status = inferLocalTranscriptStatus(resolvedStatus, localTranscript);
|
|
1114
1454
|
const worldName = normalizeText(chat?.conversation?.world?.displayName, normalizeText(request?.conversation?.world?.displayName, null));
|
|
1115
1455
|
return {
|
|
1116
1456
|
ok: true,
|
|
@@ -1141,6 +1481,12 @@ export async function loadConversationViewerSnapshot({
|
|
|
1141
1481
|
counts: inbox.counts || null,
|
|
1142
1482
|
filters: inbox.filters || null,
|
|
1143
1483
|
},
|
|
1484
|
+
localTranscript: localTranscript ? {
|
|
1485
|
+
path: localTranscript.transcriptPath,
|
|
1486
|
+
sessionKey: localTranscript.sessionKey || null,
|
|
1487
|
+
sessionId: localTranscript.sessionId || null,
|
|
1488
|
+
terminal: localTranscript.terminal === true,
|
|
1489
|
+
} : null,
|
|
1144
1490
|
diagnostics,
|
|
1145
1491
|
};
|
|
1146
1492
|
}
|
|
@@ -1176,6 +1522,7 @@ export function buildConversationViewerRoute(plugin, {
|
|
|
1176
1522
|
manifest,
|
|
1177
1523
|
runtimeConfig,
|
|
1178
1524
|
fetchImpl,
|
|
1525
|
+
env,
|
|
1179
1526
|
});
|
|
1180
1527
|
return sendJson(res, snapshot.ok === false ? 502 : 200, snapshot);
|
|
1181
1528
|
} catch (error) {
|