@yeaft/webchat-agent 1.0.189 → 1.0.191
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/connection/index.js +13 -6
- package/connection/message-router.js +23 -4
- package/local-runtime/server/handlers/agent-output.js +1 -0
- package/local-runtime/server/handlers/agent-sync.js +2 -0
- package/local-runtime/server/handlers/client-misc.js +2 -0
- package/local-runtime/server/ws-agent.js +20 -14
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +90 -90
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +1 -1
- package/package.json +1 -1
- package/yeaft/config-api.js +57 -2
- package/yeaft/engine.js +16 -8
- package/yeaft/llm/router.js +16 -3
- package/yeaft/sessions/session-config.js +159 -76
- package/yeaft/status-cache.js +74 -17
- package/yeaft/web-bridge.js +99 -47
package/yeaft/status-cache.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* never clear the model list just because a refresh failed.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { createHash } from 'node:crypto';
|
|
9
10
|
import ctx from '../context.js';
|
|
10
11
|
import { sendToServer } from '../connection/buffer.js';
|
|
11
12
|
import { loadConfig } from './config.js';
|
|
@@ -23,6 +24,16 @@ function normalizeAvailableModels(models) {
|
|
|
23
24
|
.filter(Boolean);
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
function catalogDigest(model, availableModels) {
|
|
28
|
+
return createHash('sha256')
|
|
29
|
+
.update(JSON.stringify({ model: model || null, availableModels: normalizeAvailableModels(availableModels) }))
|
|
30
|
+
.digest('hex');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function createCatalogEpoch(now) {
|
|
34
|
+
return `${process.pid}-${now().toString(36)}-${Math.random().toString(36).slice(2)}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
26
37
|
function buildEvent(snapshot) {
|
|
27
38
|
return {
|
|
28
39
|
type: 'yeaft_status',
|
|
@@ -33,7 +44,12 @@ function buildEvent(snapshot) {
|
|
|
33
44
|
tools: snapshot.tools,
|
|
34
45
|
yeaftDir: snapshot.yeaftDir || null,
|
|
35
46
|
refreshedAt: snapshot.refreshedAt || null,
|
|
47
|
+
catalogRefreshedAt: snapshot.catalogRefreshedAt || null,
|
|
48
|
+
catalogEpoch: snapshot.catalogEpoch || null,
|
|
49
|
+
catalogRevision: snapshot.catalogRevision || null,
|
|
50
|
+
catalogDigest: snapshot.catalogDigest || null,
|
|
36
51
|
refreshStartedAt: snapshot.refreshStartedAt || null,
|
|
52
|
+
refreshReason: snapshot.refreshReason || null,
|
|
37
53
|
refreshError: snapshot.refreshError || null,
|
|
38
54
|
refreshing: !!snapshot.refreshing,
|
|
39
55
|
};
|
|
@@ -54,6 +70,13 @@ export function createYeaftStatusCache(options = {}) {
|
|
|
54
70
|
let timer = null;
|
|
55
71
|
let inFlight = null;
|
|
56
72
|
let generation = 0;
|
|
73
|
+
let forceTail = Promise.resolve();
|
|
74
|
+
let pendingForceRefreshes = 0;
|
|
75
|
+
let hasConfigCatalog = false;
|
|
76
|
+
let lastCatalogRefreshedAt = 0;
|
|
77
|
+
let catalogEpoch = createCatalogEpoch(now);
|
|
78
|
+
let catalogRevision = 0;
|
|
79
|
+
let lastCatalogDigest = null;
|
|
57
80
|
|
|
58
81
|
function current() {
|
|
59
82
|
return snapshot ? { ...snapshot, availableModels: normalizeAvailableModels(snapshot.availableModels) } : null;
|
|
@@ -80,15 +103,26 @@ export function createYeaftStatusCache(options = {}) {
|
|
|
80
103
|
const config = await load({ ...(yeaftDir && { dir: yeaftDir }) });
|
|
81
104
|
if (refreshGeneration !== generation) return current();
|
|
82
105
|
const previous = snapshot || {};
|
|
106
|
+
const nextModel = config.primaryModel || config.model || previous.model || null;
|
|
107
|
+
const nextModels = normalizeAvailableModels(config.availableModels);
|
|
108
|
+
const nextDigest = catalogDigest(nextModel, nextModels);
|
|
109
|
+
hasConfigCatalog = true;
|
|
110
|
+
lastCatalogRefreshedAt = now();
|
|
111
|
+
if (nextDigest !== lastCatalogDigest) catalogRevision += 1;
|
|
112
|
+
lastCatalogDigest = nextDigest;
|
|
83
113
|
snapshot = {
|
|
84
114
|
...previous,
|
|
85
|
-
model:
|
|
86
|
-
availableModels:
|
|
115
|
+
model: nextModel,
|
|
116
|
+
availableModels: nextModels,
|
|
87
117
|
yeaftDir: config.dir || yeaftDir || previous.yeaftDir || null,
|
|
88
118
|
skills: sessionStatus?.skills ?? previous.skills,
|
|
89
119
|
mcpServers: sessionStatus?.mcpServers ?? previous.mcpServers,
|
|
90
120
|
tools: sessionStatus?.tools ?? previous.tools,
|
|
91
|
-
refreshedAt:
|
|
121
|
+
refreshedAt: lastCatalogRefreshedAt,
|
|
122
|
+
catalogRefreshedAt: lastCatalogRefreshedAt,
|
|
123
|
+
catalogEpoch,
|
|
124
|
+
catalogRevision,
|
|
125
|
+
catalogDigest: lastCatalogDigest,
|
|
92
126
|
refreshStartedAt: startedAt,
|
|
93
127
|
refreshReason: reason,
|
|
94
128
|
refreshError: null,
|
|
@@ -104,6 +138,10 @@ export function createYeaftStatusCache(options = {}) {
|
|
|
104
138
|
...previous,
|
|
105
139
|
availableModels: normalizeAvailableModels(previous.availableModels),
|
|
106
140
|
refreshedAt: previous.refreshedAt || null,
|
|
141
|
+
catalogRefreshedAt: previous.catalogRefreshedAt || null,
|
|
142
|
+
catalogEpoch: previous.catalogEpoch || null,
|
|
143
|
+
catalogRevision: previous.catalogRevision || null,
|
|
144
|
+
catalogDigest: previous.catalogDigest || null,
|
|
107
145
|
refreshStartedAt: startedAt,
|
|
108
146
|
refreshReason: reason,
|
|
109
147
|
refreshError: message,
|
|
@@ -118,35 +156,54 @@ export function createYeaftStatusCache(options = {}) {
|
|
|
118
156
|
return refreshPromise;
|
|
119
157
|
}
|
|
120
158
|
|
|
121
|
-
|
|
122
|
-
//
|
|
123
|
-
//
|
|
124
|
-
//
|
|
159
|
+
function forceRefresh(options = {}) {
|
|
160
|
+
// Serialize post-save reads. Every caller invalidates work that started
|
|
161
|
+
// before its config write, then reads only after earlier forced refreshes
|
|
162
|
+
// have drained. A counter keeps Session hydration from cancelling the
|
|
163
|
+
// newest refresh when an older caller finishes first.
|
|
125
164
|
generation += 1;
|
|
126
|
-
|
|
127
|
-
|
|
165
|
+
pendingForceRefreshes += 1;
|
|
166
|
+
const run = async () => {
|
|
167
|
+
try {
|
|
168
|
+
if (inFlight) await inFlight;
|
|
169
|
+
return await refresh({ ...options, emitRefreshing: options.emitRefreshing ?? false });
|
|
170
|
+
} finally {
|
|
171
|
+
pendingForceRefreshes -= 1;
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
const result = forceTail.then(run, run);
|
|
175
|
+
forceTail = result.catch(() => null);
|
|
176
|
+
return result;
|
|
128
177
|
}
|
|
129
178
|
|
|
130
179
|
function hydrateFromSession(sessionLike, { reason = 'session_ready', emitEvent = true } = {}) {
|
|
131
180
|
if (!sessionLike) return null;
|
|
132
|
-
// Session hydration
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
generation += 1;
|
|
181
|
+
// Session hydration fills runtime status only. The provider/model catalog
|
|
182
|
+
// is owned by config.json, so hydration never invalidates a config read.
|
|
183
|
+
// A config write invalidates stale reads through forceRefresh() instead.
|
|
136
184
|
const previous = snapshot || {};
|
|
185
|
+
const sessionModels = normalizeAvailableModels(sessionLike.config?.availableModels);
|
|
137
186
|
snapshot = {
|
|
138
187
|
...previous,
|
|
139
|
-
model:
|
|
140
|
-
|
|
188
|
+
model: hasConfigCatalog
|
|
189
|
+
? (previous.model || sessionLike.config?.model || null)
|
|
190
|
+
: (sessionLike.config?.model || previous.model || null),
|
|
191
|
+
availableModels: hasConfigCatalog
|
|
192
|
+
? normalizeAvailableModels(previous.availableModels)
|
|
193
|
+
: (sessionModels.length > 0 ? sessionModels : normalizeAvailableModels(previous.availableModels)),
|
|
141
194
|
yeaftDir: sessionLike.yeaftDir || sessionLike.config?.dir || previous.yeaftDir || null,
|
|
142
195
|
skills: sessionLike.status?.skills ?? previous.skills,
|
|
143
196
|
mcpServers: sessionLike.status?.mcpServers ?? previous.mcpServers,
|
|
144
197
|
tools: sessionLike.status?.tools ?? previous.tools,
|
|
145
198
|
refreshedAt: now(),
|
|
199
|
+
catalogRefreshedAt: hasConfigCatalog ? lastCatalogRefreshedAt : null,
|
|
200
|
+
catalogEpoch: hasConfigCatalog ? catalogEpoch : null,
|
|
201
|
+
catalogRevision: hasConfigCatalog ? catalogRevision : null,
|
|
202
|
+
catalogDigest: hasConfigCatalog ? lastCatalogDigest : null,
|
|
146
203
|
refreshStartedAt: previous.refreshStartedAt || null,
|
|
147
204
|
refreshReason: reason,
|
|
148
|
-
refreshError: null,
|
|
149
|
-
refreshing:
|
|
205
|
+
refreshError: previous.refreshError || null,
|
|
206
|
+
refreshing: pendingForceRefreshes > 0 || !!previous.refreshing,
|
|
150
207
|
};
|
|
151
208
|
return emitEvent ? emitSnapshot() : buildEvent(snapshot);
|
|
152
209
|
}
|
package/yeaft/web-bridge.js
CHANGED
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
resolveSessionYeaftDir,
|
|
54
54
|
} from './sessions/session-crud.js';
|
|
55
55
|
import { openSession, loadSessionMeta } from './sessions/session-store.js';
|
|
56
|
-
import { loadSessionConfig, resolveSessionConfig, SessionConfigError } from './sessions/session-config.js';
|
|
56
|
+
import { loadSessionConfig, normalizeSessionConfig, resolveSessionConfig, SessionConfigError } from './sessions/session-config.js';
|
|
57
57
|
import { updateSessionConfig } from './sessions/session-crud.js';
|
|
58
58
|
import { createCoordinator } from './sessions/coordinator.js';
|
|
59
59
|
import { seedDefaultSession } from './sessions/seed-default.js';
|
|
@@ -131,49 +131,93 @@ function applyLiveLanguage(language) {
|
|
|
131
131
|
try { session?.engine?.setLanguage?.(language); } catch { /* best-effort */ }
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
function
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
134
|
+
function modelRefIdentity(value) {
|
|
135
|
+
const text = String(value || '');
|
|
136
|
+
const slash = text.indexOf('/');
|
|
137
|
+
return slash < 0
|
|
138
|
+
? { provider: '', modelId: text }
|
|
139
|
+
: { provider: text.slice(0, slash), modelId: text.slice(slash + 1) };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function modelRefsEquivalent(left, right) {
|
|
143
|
+
if (!left || !right) return false;
|
|
144
|
+
if (left === right) return true;
|
|
145
|
+
const a = modelRefIdentity(left);
|
|
146
|
+
const b = modelRefIdentity(right);
|
|
147
|
+
if (a.modelId !== b.modelId) return false;
|
|
148
|
+
return !a.provider || !b.provider || a.provider === b.provider;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function resolveLiveSessionConfig(baseConfig, sessionId, options = {}) {
|
|
152
|
+
const configRoot = baseConfig?.dir || liveConfigRoot();
|
|
153
|
+
const sessionConfig = normalizeSessionConfig(configRoot, sessionId, baseConfig, options);
|
|
154
|
+
return resolveSessionConfig(baseConfig, sessionConfig);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
let sessionConfigRefreshRevision = 0;
|
|
158
|
+
|
|
159
|
+
/** Reload the Agent-owned config and install it into every live Engine. */
|
|
160
|
+
export async function refreshLiveSessionConfig(options = {}) {
|
|
161
|
+
sessionConfigRefreshRevision += 1;
|
|
162
|
+
if (!session && sessionLoadPromise) {
|
|
163
|
+
// The loader may run its own catch-up refresh, but it does not know this
|
|
164
|
+
// config transaction's pre-save default. Wait for it, then continue once
|
|
165
|
+
// with the original normalization input.
|
|
166
|
+
await sessionLoadPromise;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const configRoot = liveConfigRoot();
|
|
170
|
+
const freshConfig = loadConfig({ dir: configRoot });
|
|
171
|
+
const previousDefaultModel = options.previousDefaultModel
|
|
172
|
+
|| session?.config?.primaryModel
|
|
173
|
+
|| session?.config?.model
|
|
174
|
+
|| null;
|
|
175
|
+
// Normalize disk state even before the runtime is loaded. The config-save
|
|
176
|
+
// transaction supplies the pre-write default so legacy automatic seeds can
|
|
177
|
+
// become inheritance without depending on page/session lifecycle.
|
|
178
|
+
for (const row of snapshotSessions(configRoot)) {
|
|
179
|
+
normalizeSessionConfig(configRoot, row.id, freshConfig, { previousDefaultModel });
|
|
180
|
+
}
|
|
181
|
+
if (!session) return freshConfig;
|
|
182
|
+
|
|
183
|
+
const liveSession = session;
|
|
184
|
+
const currentConfig = liveSession.config || {};
|
|
185
|
+
const runtimeOnly = {};
|
|
186
|
+
for (const key of ['serverMode', '_readOnly', 'modelEffort']) {
|
|
187
|
+
if (Object.prototype.hasOwnProperty.call(currentConfig, key)) runtimeOnly[key] = currentConfig[key];
|
|
188
|
+
}
|
|
189
|
+
const inheritedAgentDefault = !currentConfig.model
|
|
190
|
+
|| modelRefsEquivalent(currentConfig.model, currentConfig.primaryModel);
|
|
191
|
+
const nextModel = inheritedAgentDefault
|
|
192
|
+
? (freshConfig.primaryModel || freshConfig.model)
|
|
193
|
+
: currentConfig.model;
|
|
194
|
+
const nextConfig = { ...freshConfig, ...runtimeOnly, model: nextModel };
|
|
195
|
+
const vpConfigSnapshots = [];
|
|
196
|
+
for (const [key, engine] of vpEngines) {
|
|
197
|
+
const separator = key.indexOf('::');
|
|
198
|
+
if (separator < 1) continue;
|
|
199
|
+
const sessionId = key.slice(0, separator);
|
|
200
|
+
vpConfigSnapshots.push({
|
|
201
|
+
key,
|
|
202
|
+
engine,
|
|
203
|
+
config: resolveLiveSessionConfig(nextConfig, sessionId, { previousDefaultModel }),
|
|
204
|
+
});
|
|
154
205
|
}
|
|
155
|
-
}
|
|
156
206
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const out = {};
|
|
160
|
-
const model = typeof config.primaryModel === 'string' && config.primaryModel.trim()
|
|
161
|
-
? config.primaryModel.trim()
|
|
162
|
-
: (typeof config.model === 'string' && config.model.trim() ? config.model.trim() : '');
|
|
163
|
-
if (model) out.model = model;
|
|
164
|
-
if (typeof config.modelEffort === 'string' && config.modelEffort.trim()) {
|
|
165
|
-
out.modelEffort = config.modelEffort.trim();
|
|
207
|
+
if (typeof liveSession.adapter?.refreshProviders === 'function') {
|
|
208
|
+
liveSession.adapter.refreshProviders(freshConfig.providers || []);
|
|
166
209
|
}
|
|
167
|
-
|
|
210
|
+
for (const key of Object.keys(currentConfig)) delete currentConfig[key];
|
|
211
|
+
Object.assign(currentConfig, nextConfig);
|
|
212
|
+
liveSession.engine?.refreshConfig?.(currentConfig);
|
|
213
|
+
for (const { key, engine, config } of vpConfigSnapshots) {
|
|
214
|
+
engine.refreshConfig?.(config);
|
|
215
|
+
vpEngineConfigKeys.set(key, engineConfigKey(config));
|
|
216
|
+
}
|
|
217
|
+
if (freshConfig.language) applyLiveLanguage(freshConfig.language);
|
|
218
|
+
return currentConfig;
|
|
168
219
|
}
|
|
169
220
|
|
|
170
|
-
function withDefaultSessionConfig(payload) {
|
|
171
|
-
const next = payload && typeof payload === 'object' ? { ...payload } : {};
|
|
172
|
-
const existing = next.config && typeof next.config === 'object' ? next.config : {};
|
|
173
|
-
const seeded = { ...defaultSessionModelConfig(), ...existing };
|
|
174
|
-
if (Object.keys(seeded).length > 0) next.config = seeded;
|
|
175
|
-
return next;
|
|
176
|
-
}
|
|
177
221
|
|
|
178
222
|
/** Test-only: replace the lightweight VP thread classifier. */
|
|
179
223
|
export function __testSetThreadClassifier(fn) {
|
|
@@ -210,7 +254,11 @@ function scheduleYeaftLoadHistoryMetadataReplay(sessionId) {
|
|
|
210
254
|
setTimeout(async () => {
|
|
211
255
|
try {
|
|
212
256
|
if (!replaySession) return;
|
|
213
|
-
|
|
257
|
+
try {
|
|
258
|
+
await refreshLiveSessionConfig();
|
|
259
|
+
} catch (err) {
|
|
260
|
+
console.warn('[Yeaft] load-history config refresh failed:', err?.message || err);
|
|
261
|
+
}
|
|
214
262
|
let projectRuntime = null;
|
|
215
263
|
if (sessionId) {
|
|
216
264
|
try {
|
|
@@ -517,6 +565,7 @@ function engineConfigKey(config) {
|
|
|
517
565
|
fastModel: config?.fastModel || '',
|
|
518
566
|
fastModelId: config?.fastModelId || '',
|
|
519
567
|
fallbackModel: config?.fallbackModel || '',
|
|
568
|
+
providers: Array.isArray(config?.providers) ? config.providers : [],
|
|
520
569
|
});
|
|
521
570
|
}
|
|
522
571
|
|
|
@@ -833,7 +882,7 @@ function queryTimeoutMsForSessionConfig(config = null) {
|
|
|
833
882
|
function queryTimeoutMsForSession(sessionId = null) {
|
|
834
883
|
if (!sessionId || !session) return queryTimeoutMsForSessionConfig(session?.config);
|
|
835
884
|
try {
|
|
836
|
-
return queryTimeoutMsForSessionConfig(
|
|
885
|
+
return queryTimeoutMsForSessionConfig(resolveLiveSessionConfig(session.config, sessionId));
|
|
837
886
|
} catch {
|
|
838
887
|
return queryTimeoutMsForSessionConfig(session?.config);
|
|
839
888
|
}
|
|
@@ -1337,8 +1386,7 @@ export function __testGroupHistory(sessionId) {
|
|
|
1337
1386
|
|
|
1338
1387
|
export function __testResolveVpEffectiveConfig(sessionId) {
|
|
1339
1388
|
if (!session) return null;
|
|
1340
|
-
|
|
1341
|
-
return resolveSessionConfig(session.config, loadSessionConfig(sessionConfigRoot, sessionId));
|
|
1389
|
+
return resolveLiveSessionConfig(session.config, sessionId);
|
|
1342
1390
|
}
|
|
1343
1391
|
|
|
1344
1392
|
/**
|
|
@@ -1466,9 +1514,7 @@ function getOrCreateVpEngine(sessionId, vpId, threadId = 'main') {
|
|
|
1466
1514
|
// Per-session config overlay (v1: model only). Falls back to the
|
|
1467
1515
|
// session's user-level config when no override is set. Session config is
|
|
1468
1516
|
// always resolved from the agent-local root; project `.yeaft` is ignored.
|
|
1469
|
-
const
|
|
1470
|
-
const groupCfg = loadSessionConfig(sessionConfigRoot, sessionId);
|
|
1471
|
-
const effectiveConfig = resolveSessionConfig(session.config, groupCfg);
|
|
1517
|
+
const effectiveConfig = resolveLiveSessionConfig(session.config, sessionId);
|
|
1472
1518
|
const configKey = engineConfigKey(effectiveConfig);
|
|
1473
1519
|
let eng = vpEngines.get(key);
|
|
1474
1520
|
if (eng && vpEngineConfigKeys.get(key) === configKey) return eng;
|
|
@@ -2684,7 +2730,7 @@ export function handleYeaftCreateSession(msg) {
|
|
|
2684
2730
|
const payload = (msg && msg.payload) || {};
|
|
2685
2731
|
try {
|
|
2686
2732
|
const yeaftDir = ctx.CONFIG?.yeaftDir;
|
|
2687
|
-
const group = createSessionFromSpec(yeaftDir,
|
|
2733
|
+
const group = createSessionFromSpec(yeaftDir, payload);
|
|
2688
2734
|
recordAgentSessionCreated();
|
|
2689
2735
|
group.config = loadSessionConfig(yeaftDir, group.id);
|
|
2690
2736
|
sendSessionCrudResult({ op: 'create', requestId, ok: true, session: group });
|
|
@@ -4212,6 +4258,7 @@ export async function ensureSessionLoaded(opts = {}) {
|
|
|
4212
4258
|
if (sessionLoadPromise) return sessionLoadPromise;
|
|
4213
4259
|
|
|
4214
4260
|
sessionLoadPromise = (async () => {
|
|
4261
|
+
const bootConfigRevision = sessionConfigRefreshRevision;
|
|
4215
4262
|
const yeaftDir = ctx.CONFIG?.yeaftDir;
|
|
4216
4263
|
const normalizedWorkDir = normalizeSessionWorkDir(opts?.workDir || opts?.sessionMeta?.workDir);
|
|
4217
4264
|
session = await loadSession({
|
|
@@ -4223,6 +4270,11 @@ export async function ensureSessionLoaded(opts = {}) {
|
|
|
4223
4270
|
});
|
|
4224
4271
|
|
|
4225
4272
|
installYeaftRuntimeBridge(session);
|
|
4273
|
+
// A save may complete after loadSession read config.json but before this
|
|
4274
|
+
// runtime became visible. Refresh before any status/session_ready emit.
|
|
4275
|
+
if (sessionConfigRefreshRevision !== bootConfigRevision) {
|
|
4276
|
+
await refreshLiveSessionConfig();
|
|
4277
|
+
}
|
|
4226
4278
|
|
|
4227
4279
|
try {
|
|
4228
4280
|
if (session.engine && typeof session.engine.setSubAgentEventSink === 'function') {
|