codexmate 0.0.20 → 0.0.22

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.
Files changed (96) hide show
  1. package/README.md +289 -152
  2. package/README.zh.md +321 -0
  3. package/cli/agents-files.js +224 -0
  4. package/cli/archive-helpers.js +446 -0
  5. package/cli/auth-profiles.js +359 -0
  6. package/cli/builtin-proxy.js +1044 -0
  7. package/cli/claude-proxy.js +998 -0
  8. package/cli/config-bootstrap.js +384 -0
  9. package/cli/openai-bridge.js +950 -0
  10. package/cli/openclaw-config.js +629 -0
  11. package/cli/session-usage.concurrent.js +28 -0
  12. package/cli/session-usage.js +112 -0
  13. package/cli/session-usage.models.js +176 -0
  14. package/cli/skills.js +1141 -0
  15. package/cli/zip-commands.js +510 -0
  16. package/cli.js +9408 -9719
  17. package/lib/cli-models-utils.js +109 -1
  18. package/lib/cli-path-utils.js +69 -0
  19. package/lib/cli-sessions.js +386 -0
  20. package/lib/download-artifacts.js +77 -0
  21. package/lib/task-orchestrator.js +869 -0
  22. package/package.json +14 -10
  23. package/res/logo.png +0 -0
  24. package/res/vue.global.prod.js +13 -0
  25. package/web-ui/app.js +193 -15
  26. package/web-ui/index.html +5 -1
  27. package/web-ui/logic.agents-diff.mjs +1 -1
  28. package/web-ui/logic.claude.mjs +60 -0
  29. package/web-ui/logic.runtime.mjs +11 -7
  30. package/web-ui/logic.sessions.mjs +372 -21
  31. package/web-ui/modules/api.mjs +22 -1
  32. package/web-ui/modules/app.computed.dashboard.mjs +23 -10
  33. package/web-ui/modules/app.computed.index.mjs +4 -0
  34. package/web-ui/modules/app.computed.main-tabs.mjs +198 -0
  35. package/web-ui/modules/app.computed.session.mjs +521 -9
  36. package/web-ui/modules/app.methods.agents.mjs +62 -11
  37. package/web-ui/modules/app.methods.codex-config.mjs +189 -34
  38. package/web-ui/modules/app.methods.index.mjs +7 -1
  39. package/web-ui/modules/app.methods.install.mjs +24 -20
  40. package/web-ui/modules/app.methods.navigation.mjs +142 -1
  41. package/web-ui/modules/app.methods.openclaw-core.mjs +339 -39
  42. package/web-ui/modules/app.methods.openclaw-editing.mjs +39 -4
  43. package/web-ui/modules/app.methods.openclaw-persist.mjs +122 -4
  44. package/web-ui/modules/app.methods.providers.mjs +192 -53
  45. package/web-ui/modules/app.methods.session-actions.mjs +99 -19
  46. package/web-ui/modules/app.methods.session-browser.mjs +196 -5
  47. package/web-ui/modules/app.methods.session-timeline.mjs +22 -15
  48. package/web-ui/modules/app.methods.session-trash.mjs +3 -0
  49. package/web-ui/modules/app.methods.startup-claude.mjs +70 -71
  50. package/web-ui/modules/app.methods.task-orchestration.mjs +471 -0
  51. package/web-ui/modules/config-mode.computed.mjs +2 -0
  52. package/web-ui/modules/config-template-confirm-pref.mjs +33 -0
  53. package/web-ui/modules/i18n.mjs +1609 -0
  54. package/web-ui/modules/plugins.computed.mjs +220 -0
  55. package/web-ui/modules/plugins.methods.mjs +620 -0
  56. package/web-ui/modules/plugins.storage.mjs +37 -0
  57. package/web-ui/partials/index/layout-footer.html +1 -57
  58. package/web-ui/partials/index/layout-header.html +299 -175
  59. package/web-ui/partials/index/modal-config-template-agents.html +79 -29
  60. package/web-ui/partials/index/modal-confirm-toast.html +1 -1
  61. package/web-ui/partials/index/modal-health-check.html +14 -14
  62. package/web-ui/partials/index/modal-openclaw-config.html +47 -42
  63. package/web-ui/partials/index/modal-skills.html +130 -114
  64. package/web-ui/partials/index/modals-basic.html +71 -102
  65. package/web-ui/partials/index/panel-config-claude.html +50 -12
  66. package/web-ui/partials/index/panel-config-codex.html +34 -37
  67. package/web-ui/partials/index/panel-config-openclaw.html +10 -16
  68. package/web-ui/partials/index/panel-docs.html +147 -0
  69. package/web-ui/partials/index/panel-market.html +38 -38
  70. package/web-ui/partials/index/panel-orchestration.html +397 -0
  71. package/web-ui/partials/index/panel-plugins.html +243 -0
  72. package/web-ui/partials/index/panel-sessions.html +51 -146
  73. package/web-ui/partials/index/panel-settings.html +188 -96
  74. package/web-ui/partials/index/panel-usage.html +353 -0
  75. package/web-ui/session-helpers.mjs +221 -10
  76. package/web-ui/styles/base-theme.css +120 -229
  77. package/web-ui/styles/controls-forms.css +59 -51
  78. package/web-ui/styles/docs-panel.css +247 -0
  79. package/web-ui/styles/layout-shell.css +394 -128
  80. package/web-ui/styles/modals-core.css +18 -3
  81. package/web-ui/styles/navigation-panels.css +184 -183
  82. package/web-ui/styles/plugins-panel.css +518 -0
  83. package/web-ui/styles/responsive.css +102 -62
  84. package/web-ui/styles/sessions-list.css +13 -27
  85. package/web-ui/styles/sessions-preview.css +13 -7
  86. package/web-ui/styles/sessions-toolbar-trash.css +25 -0
  87. package/web-ui/styles/sessions-usage.css +581 -6
  88. package/web-ui/styles/settings-panel.css +166 -0
  89. package/web-ui/styles/skills-list.css +16 -11
  90. package/web-ui/styles/skills-market.css +63 -2
  91. package/web-ui/styles/task-orchestration.css +776 -0
  92. package/web-ui/styles/titles-cards.css +67 -66
  93. package/web-ui/styles.css +4 -0
  94. package/README.en.md +0 -259
  95. package/res/screenshot.png +0 -0
  96. package/res/vue.global.js +0 -18552
@@ -11,10 +11,83 @@ function clearSessionTimelineRefs(vm) {
11
11
  }
12
12
  }
13
13
 
14
+ function hasOwnOption(options, key) {
15
+ return !!options && typeof options === 'object' && Object.prototype.hasOwnProperty.call(options, key);
16
+ }
17
+
18
+ function normalizeSessionLoadOptions(options = {}) {
19
+ const normalized = options && typeof options === 'object' ? options : {};
20
+ const hasIncludeActiveDetail = hasOwnOption(normalized, 'includeActiveDetail');
21
+ return {
22
+ hasIncludeActiveDetail,
23
+ includeActiveDetail: hasIncludeActiveDetail
24
+ ? normalized.includeActiveDetail !== false
25
+ : false,
26
+ forceRefresh: !!normalized.forceRefresh
27
+ };
28
+ }
29
+
30
+ function mergeSessionLoadOptions(baseOptions = {}, nextOptions = {}) {
31
+ const base = normalizeSessionLoadOptions(baseOptions);
32
+ const next = normalizeSessionLoadOptions(nextOptions);
33
+ return {
34
+ hasIncludeActiveDetail: base.hasIncludeActiveDetail || next.hasIncludeActiveDetail,
35
+ includeActiveDetail: (base.hasIncludeActiveDetail && base.includeActiveDetail)
36
+ || (next.hasIncludeActiveDetail && next.includeActiveDetail),
37
+ forceRefresh: base.forceRefresh || next.forceRefresh
38
+ };
39
+ }
40
+
41
+ function shouldIncludeActiveSessionDetail(vm, options = {}) {
42
+ if (options.hasIncludeActiveDetail) {
43
+ return options.includeActiveDetail;
44
+ }
45
+ return vm.mainTab === 'sessions' || !!vm.sessionStandalone;
46
+ }
47
+
48
+ function emitSessionLoadDebug(vm, step, details = '') {
49
+ if (!vm || typeof vm.emitSessionLoadNativeDialog !== 'function') {
50
+ return;
51
+ }
52
+ vm.emitSessionLoadNativeDialog(step, details);
53
+ }
54
+
55
+ function scheduleSessionDetailHydration(vm, options = {}) {
56
+ if (!vm || typeof vm.loadActiveSessionDetail !== 'function') {
57
+ return;
58
+ }
59
+ const hydrationTicket = (Number(vm.__sessionDetailHydrationTicket) || 0) + 1;
60
+ vm.__sessionDetailHydrationTicket = hydrationTicket;
61
+ const task = () => {
62
+ if (hydrationTicket !== Number(vm.__sessionDetailHydrationTicket || 0)) return;
63
+ if (!vm.activeSession) return;
64
+ if (vm.mainTab !== 'sessions' && !vm.sessionStandalone) return;
65
+ if (vm.sessionDetailLoading) return;
66
+ const currentMessages = Array.isArray(vm.activeSessionMessages) ? vm.activeSessionMessages : [];
67
+ if (!options.force && currentMessages.length > 0) {
68
+ emitSessionLoadDebug(vm, 'scheduleSessionDetailHydration:skip-existing-messages', `messages=${currentMessages.length}`);
69
+ return;
70
+ }
71
+ emitSessionLoadDebug(vm, 'scheduleSessionDetailHydration:run', `sessionId=${vm.activeSession && vm.activeSession.sessionId ? vm.activeSession.sessionId : ''}`);
72
+ void vm.loadActiveSessionDetail(options);
73
+ };
74
+ if (typeof vm.scheduleAfterFrame === 'function') {
75
+ emitSessionLoadDebug(vm, 'scheduleSessionDetailHydration:queued');
76
+ vm.scheduleAfterFrame(task);
77
+ return;
78
+ }
79
+ task();
80
+ }
81
+
14
82
  export function switchMainTab(tab) {
15
83
  const nextTab = typeof tab === 'string' ? tab : '';
16
84
  const previousTab = this.mainTab;
17
85
  const leavingSessions = previousTab === 'sessions' && nextTab !== 'sessions';
86
+ const enteringSessionsTab = nextTab === 'sessions';
87
+ const enteringUsageTab = nextTab === 'usage';
88
+ const enteringOrchestrationTab = nextTab === 'orchestration';
89
+ const enteringPluginsTab = nextTab === 'plugins';
90
+ emitSessionLoadDebug(this, 'switchMainTab:start', `from=${previousTab}\nto=${nextTab}`);
18
91
  this.mainTab = nextTab;
19
92
 
20
93
  if (leavingSessions) {
@@ -40,8 +113,48 @@ export function switchMainTab(tab) {
40
113
  }
41
114
  }
42
115
 
43
- if (nextTab === 'sessions' && !this.sessionsLoadedOnce) {
44
- this.loadSessions();
116
+ if (enteringSessionsTab && !this.sessionsLoadedOnce) {
117
+ const canStageInitialSessionDetail = typeof this.scheduleAfterFrame === 'function';
118
+ emitSessionLoadDebug(
119
+ this,
120
+ 'switchMainTab:enter-sessions',
121
+ `sessionsLoadedOnce=${!!this.sessionsLoadedOnce}\nstagedDetail=${canStageInitialSessionDetail}`
122
+ );
123
+ const loadResult = this.loadSessions({
124
+ includeActiveDetail: !canStageInitialSessionDetail
125
+ });
126
+ if (canStageInitialSessionDetail) {
127
+ void Promise.resolve(loadResult)
128
+ .then(() => {
129
+ emitSessionLoadDebug(this, 'switchMainTab:loadSessions-resolved');
130
+ scheduleSessionDetailHydration(this);
131
+ })
132
+ .catch((error) => {
133
+ emitSessionLoadDebug(
134
+ this,
135
+ 'switchMainTab:loadSessions-rejected',
136
+ error && error.message ? error.message : String(error)
137
+ );
138
+ });
139
+ }
140
+ }
141
+ if (enteringUsageTab && !this.sessionsUsageLoadedOnce && typeof this.loadSessionsUsage === 'function') {
142
+ this.loadSessionsUsage();
143
+ }
144
+ if (enteringOrchestrationTab && typeof this.loadTaskOrchestrationOverview === 'function') {
145
+ let orchestrationOverviewLoad = null;
146
+ try {
147
+ orchestrationOverviewLoad = this.loadTaskOrchestrationOverview({
148
+ silent: true,
149
+ includeDetail: true
150
+ });
151
+ } catch (_) {
152
+ orchestrationOverviewLoad = null;
153
+ }
154
+ void Promise.resolve(orchestrationOverviewLoad).catch(() => {});
155
+ }
156
+ if (nextTab !== 'orchestration' && typeof this.stopTaskOrchestrationPolling === 'function') {
157
+ this.stopTaskOrchestrationPolling();
45
158
  }
46
159
  if (nextTab === 'sessions') {
47
160
  this.prepareSessionTabRender();
@@ -73,6 +186,19 @@ export function switchMainTab(tab) {
73
186
  }
74
187
  void Promise.resolve(marketOverviewLoad).catch(() => {});
75
188
  }
189
+ if (enteringPluginsTab && typeof this.loadPluginsOverview === 'function') {
190
+ // Default behavior: always land on Prompt Templates + Compose when entering Plugins.
191
+ this.pluginsActiveId = 'prompt-templates';
192
+ this.promptTemplatesMode = 'compose';
193
+ this.promptComposerPickerVisible = false;
194
+ let pluginsLoad = null;
195
+ try {
196
+ pluginsLoad = this.loadPluginsOverview({ silent: true });
197
+ } catch (_) {
198
+ pluginsLoad = null;
199
+ }
200
+ void Promise.resolve(pluginsLoad).catch(() => {});
201
+ }
76
202
  if (nextTab === 'config' && this.configMode === 'claude') {
77
203
  const expectedTab = nextTab;
78
204
  const expectedConfigMode = this.configMode;
@@ -88,8 +214,17 @@ export function switchMainTab(tab) {
88
214
  }
89
215
  }
90
216
 
91
- export async function loadSessions(api) {
92
- if (this.sessionsLoading) return;
217
+ export async function loadSessions(api, options = {}) {
218
+ if (this.sessionsLoading) {
219
+ this.__sessionPendingLoadOptions = mergeSessionLoadOptions(
220
+ this.__sessionPendingLoadOptions,
221
+ options
222
+ );
223
+ emitSessionLoadDebug(this, 'loadSessions:queued-while-busy');
224
+ return;
225
+ }
226
+ const normalizedOptions = normalizeSessionLoadOptions(options);
227
+ const includeActiveDetail = shouldIncludeActiveSessionDetail(this, normalizedOptions);
93
228
  this.sessionsLoading = true;
94
229
  this.activeSessionDetailError = '';
95
230
  let loadSucceeded = false;
@@ -98,13 +233,24 @@ export async function loadSessions(api) {
98
233
  pathFilter: this.sessionPathFilter,
99
234
  query: this.sessionQuery,
100
235
  roleFilter: this.sessionRoleFilter,
101
- timeRangePreset: this.sessionTimePreset
236
+ timeRangePreset: this.sessionTimePreset,
237
+ forceRefresh: normalizedOptions.forceRefresh
102
238
  });
239
+ emitSessionLoadDebug(
240
+ this,
241
+ 'loadSessions:start',
242
+ `source=${params.source || ''}\nforceRefresh=${!!params.forceRefresh}\nincludeActiveDetail=${includeActiveDetail}`
243
+ );
244
+ let pendingOptions = null;
103
245
  try {
104
246
  const res = await api('list-sessions', params);
105
247
  if (res.error) {
248
+ emitSessionLoadDebug(this, 'loadSessions:error-response', `error=${res.error}`);
106
249
  this.showMessage(res.error, 'error');
107
250
  this.sessionsList = [];
251
+ if (typeof this.primeSessionListRender === 'function') {
252
+ this.primeSessionListRender();
253
+ }
108
254
  this.activeSession = null;
109
255
  this.activeSessionMessages = [];
110
256
  this.resetSessionDetailPagination();
@@ -116,12 +262,17 @@ export async function loadSessions(api) {
116
262
  } else {
117
263
  loadSucceeded = true;
118
264
  this.sessionsList = Array.isArray(res.sessions) ? res.sessions : [];
265
+ emitSessionLoadDebug(this, 'loadSessions:response', `sessions=${this.sessionsList.length}`);
266
+ if (typeof this.primeSessionListRender === 'function') {
267
+ this.primeSessionListRender();
268
+ }
119
269
  this.syncSessionPathOptionsForSource(
120
270
  this.sessionFilterSource,
121
271
  this.extractPathOptionsFromSessions(this.sessionsList),
122
272
  true
123
273
  );
124
274
  if (this.sessionsList.length === 0) {
275
+ emitSessionLoadDebug(this, 'loadSessions:empty');
125
276
  this.activeSession = null;
126
277
  this.activeSessionMessages = [];
127
278
  this.resetSessionDetailPagination();
@@ -134,6 +285,11 @@ export async function loadSessions(api) {
134
285
  const oldKey = this.activeSession ? this.getSessionExportKey(this.activeSession) : '';
135
286
  const matched = this.sessionsList.find(item => this.getSessionExportKey(item) === oldKey);
136
287
  this.activeSession = matched || this.sessionsList[0];
288
+ emitSessionLoadDebug(
289
+ this,
290
+ 'loadSessions:active-session-selected',
291
+ `sessionId=${this.activeSession && this.activeSession.sessionId ? this.activeSession.sessionId : ''}`
292
+ );
137
293
  this.activeSessionMessages = [];
138
294
  this.resetSessionDetailPagination();
139
295
  this.resetSessionPreviewMessageRender();
@@ -142,12 +298,22 @@ export async function loadSessions(api) {
142
298
  this.cancelSessionTimelineSync();
143
299
  this.sessionTimelineActiveKey = '';
144
300
  clearSessionTimelineRefs(this);
145
- await this.loadActiveSessionDetail();
301
+ if (includeActiveDetail) {
302
+ emitSessionLoadDebug(
303
+ this,
304
+ 'loadSessions:hydrate-active-detail',
305
+ `sessionId=${this.activeSession && this.activeSession.sessionId ? this.activeSession.sessionId : ''}`
306
+ );
307
+ await this.loadActiveSessionDetail();
308
+ }
146
309
  }
147
- void this.loadSessionPathOptions({ source: this.sessionFilterSource });
148
310
  }
149
311
  } catch (e) {
312
+ emitSessionLoadDebug(this, 'loadSessions:exception', e && e.message ? e.message : String(e));
150
313
  this.sessionsList = [];
314
+ if (typeof this.primeSessionListRender === 'function') {
315
+ this.primeSessionListRender();
316
+ }
151
317
  this.activeSession = null;
152
318
  this.activeSessionMessages = [];
153
319
  this.resetSessionDetailPagination();
@@ -162,11 +328,23 @@ export async function loadSessions(api) {
162
328
  if (loadSucceeded) {
163
329
  this.sessionsLoadedOnce = true;
164
330
  }
331
+ pendingOptions = this.__sessionPendingLoadOptions || null;
332
+ this.__sessionPendingLoadOptions = null;
333
+ emitSessionLoadDebug(
334
+ this,
335
+ 'loadSessions:complete',
336
+ `loadSucceeded=${loadSucceeded}\npendingReload=${!!pendingOptions}`
337
+ );
338
+ }
339
+ if (pendingOptions) {
340
+ emitSessionLoadDebug(this, 'loadSessions:replay-pending-request');
341
+ return loadSessions.call(this, api, pendingOptions);
165
342
  }
166
343
  }
167
344
 
168
345
  export async function loadActiveSessionDetail(api, options = {}) {
169
346
  if (!this.activeSession) {
347
+ emitSessionLoadDebug(this, 'loadActiveSessionDetail:skip-no-active-session');
170
348
  this.activeSessionMessages = [];
171
349
  this.resetSessionDetailPagination();
172
350
  this.resetSessionPreviewMessageRender();
@@ -182,6 +360,11 @@ export async function loadActiveSessionDetail(api, options = {}) {
182
360
  const requestSeq = ++this.sessionDetailRequestSeq;
183
361
  this.sessionDetailLoading = true;
184
362
  this.activeSessionDetailError = '';
363
+ emitSessionLoadDebug(
364
+ this,
365
+ 'loadActiveSessionDetail:start',
366
+ `sessionId=${currentActiveSession && currentActiveSession.sessionId ? currentActiveSession.sessionId : ''}\nrequestSeq=${requestSeq}`
367
+ );
185
368
  const fallbackLimit = Number.isFinite(this.sessionDetailInitialMessageLimit)
186
369
  ? Math.max(1, Math.floor(this.sessionDetailInitialMessageLimit))
187
370
  : 80;
@@ -189,22 +372,27 @@ export async function loadActiveSessionDetail(api, options = {}) {
189
372
  const messageLimit = Number.isFinite(rawLimit)
190
373
  ? Math.max(1, Math.floor(rawLimit))
191
374
  : fallbackLimit;
375
+ const preview = this.mainTab === 'sessions' && !this.sessionStandalone;
192
376
  try {
193
377
  const res = await api('session-detail', {
194
378
  source: this.activeSession.source,
195
379
  sessionId: this.activeSession.sessionId,
196
380
  filePath: this.activeSession.filePath,
197
- messageLimit
381
+ messageLimit,
382
+ preview
198
383
  });
199
384
 
200
385
  if (requestSeq !== this.sessionDetailRequestSeq) {
386
+ emitSessionLoadDebug(this, 'loadActiveSessionDetail:stale-request-seq', `requestSeq=${requestSeq}`);
201
387
  return;
202
388
  }
203
389
  if (!this.activeSession || this.activeSession !== currentActiveSession) {
390
+ emitSessionLoadDebug(this, 'loadActiveSessionDetail:active-session-changed', `requestSeq=${requestSeq}`);
204
391
  return;
205
392
  }
206
393
 
207
394
  if (res.error) {
395
+ emitSessionLoadDebug(this, 'loadActiveSessionDetail:error-response', `error=${res.error}`);
208
396
  this.activeSessionMessages = [];
209
397
  this.resetSessionPreviewMessageRender();
210
398
  this.activeSessionDetailClipped = false;
@@ -218,6 +406,11 @@ export async function loadActiveSessionDetail(api, options = {}) {
218
406
  const rawMessages = Array.isArray(res.messages) ? res.messages : [];
219
407
  const normalizedMessages = rawMessages.map((message) => Object.freeze(this.normalizeSessionMessage(message)));
220
408
  this.activeSessionMessages = Object.freeze(normalizedMessages);
409
+ emitSessionLoadDebug(
410
+ this,
411
+ 'loadActiveSessionDetail:messages-ready',
412
+ `sessionId=${currentActiveSession && currentActiveSession.sessionId ? currentActiveSession.sessionId : ''}\nmessages=${normalizedMessages.length}\nclipped=${!!res.clipped}`
413
+ );
221
414
  if (typeof this.invalidateSessionTimelineMeasurementCache === 'function') {
222
415
  this.invalidateSessionTimelineMeasurementCache(true);
223
416
  }
@@ -266,16 +459,33 @@ export async function loadActiveSessionDetail(api, options = {}) {
266
459
  }
267
460
  this.updateSessionTimelineOffset();
268
461
  if (this.sessionTimelineEnabled) {
269
- this.scheduleSessionTimelineSync();
462
+ const currentSession = this.activeSession;
463
+ const syncTask = () => {
464
+ if (this.mainTab !== 'sessions' || !this.sessionPreviewRenderEnabled) {
465
+ return;
466
+ }
467
+ if (!this.activeSession || this.activeSession !== currentSession) {
468
+ return;
469
+ }
470
+ this.scheduleSessionTimelineSync();
471
+ };
472
+ if (typeof this.scheduleAfterFrame === 'function') {
473
+ this.scheduleAfterFrame(syncTask);
474
+ return;
475
+ }
476
+ syncTask();
270
477
  }
271
478
  });
272
479
  } catch (e) {
273
480
  if (requestSeq !== this.sessionDetailRequestSeq) {
481
+ emitSessionLoadDebug(this, 'loadActiveSessionDetail:ignore-exception-stale-request', `requestSeq=${requestSeq}`);
274
482
  return;
275
483
  }
276
484
  if (!this.activeSession || this.activeSession !== currentActiveSession) {
485
+ emitSessionLoadDebug(this, 'loadActiveSessionDetail:ignore-exception-session-changed', `requestSeq=${requestSeq}`);
277
486
  return;
278
487
  }
488
+ emitSessionLoadDebug(this, 'loadActiveSessionDetail:exception', e && e.message ? e.message : String(e));
279
489
  this.activeSessionMessages = [];
280
490
  this.sessionPreviewPendingVisibleCount = 0;
281
491
  this.resetSessionPreviewMessageRender();
@@ -288,6 +498,7 @@ export async function loadActiveSessionDetail(api, options = {}) {
288
498
  if (requestSeq === this.sessionDetailRequestSeq) {
289
499
  this.sessionDetailLoading = false;
290
500
  }
501
+ emitSessionLoadDebug(this, 'loadActiveSessionDetail:complete', `requestSeq=${requestSeq}`);
291
502
  }
292
503
  }
293
504
 
@@ -343,7 +554,7 @@ export async function loadMoreSessionMessages(stepSize) {
343
554
  : 1000;
344
555
 
345
556
  let nextLimit = Math.max(currentLimit + fetchStep, targetVisible);
346
- if (totalKnown > 0) {
557
+ if (totalKnown > total) {
347
558
  nextLimit = Math.min(nextLimit, totalKnown);
348
559
  }
349
560
  nextLimit = Math.min(nextLimit, limitCap);