Stop War Room A2A loops and unlock full chat history with dates.

Detect repeating topics and pause A2A, load older messages beyond the old ~120 cap, and show clear date/time stamps on every bubble.
This commit is contained in:
Aissa
2026-08-16 23:23:48 +00:00
parent c5ea8cbb63
commit f925042a93
5 changed files with 307 additions and 25 deletions
+34
View File
@@ -662,3 +662,37 @@
background: rgba(14, 40, 64, 0.98);
border-color: rgba(56, 189, 248, 0.7);
}
.wr-history-bar {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.25rem;
padding: 0.45rem 0.35rem 0.65rem;
position: sticky;
top: 0;
z-index: 5;
background: linear-gradient(180deg, rgba(8,12,18,0.96), rgba(8,12,18,0.72) 70%, transparent);
}
.wr-time {
font-variant-numeric: tabular-nums;
white-space: nowrap;
opacity: 0.85;
}
.wr-day-sep {
display: flex;
justify-content: center;
margin: 0.75rem 0 0.5rem;
}
.wr-day-sep span {
font-size: 0.72rem;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
padding: 0.2rem 0.65rem;
border-radius: 999px;
background: rgba(148, 163, 184, 0.12);
border: 1px solid rgba(148, 163, 184, 0.2);
opacity: 0.9;
}
+85 -6
View File
@@ -29,6 +29,10 @@ window.WarRoomHub = (function () {
replyHighlightId: null,
stickToBottom: true,
showJumpLatest: false,
hasMoreHistory: false,
loadingHistory: false,
historyTotal: 0,
_loadingOlder: false,
_typingScrollAt: 0,
rosterCollapsed: true,
a2aPausedUntil: 0,
@@ -158,13 +162,15 @@ window.WarRoomHub = (function () {
try {
if (!(this.rooms || []).length) await this.loadRooms();
var meta = (this.rooms || []).find(function (r) { return Number(r.id) === id; }) || null;
var r = await fetch('/api/agents/warroom/rooms/' + id + '/messages?limit=120').then(function (x) {
var r = await fetch('/api/agents/warroom/rooms/' + id + '/messages?limit=800').then(function (x) {
return x.json();
});
// messages API returns {items}, NOT {room} — keep room from rooms list
this.room = meta || r.room || { id: id, kind: 'warroom', title: 'War Room' };
this.messages = this.normalizeList(r.items || []);
this.lastId = this.maxId(this.messages);
this.historyTotal = r.total || this.messages.length;
this.hasMoreHistory = !!r.has_more;
this.stickToBottom = true;
this.showJumpLatest = false;
this.$nextTick(function () {
@@ -174,7 +180,7 @@ window.WarRoomHub = (function () {
}.bind(this));
var last = (this.messages || [])[(this.messages || []).length - 1];
if (last && last.sender_type === 'agent' && this.room && this.room.kind === 'warroom') {
if (this.autoDebate) this.scheduleContinue(60000);
if (this.autoDebate) this.scheduleContinue(120000);
}
} catch (e) {
this.status = e.message || 'Laden mislukt';
@@ -246,6 +252,58 @@ window.WarRoomHub = (function () {
var near = this.isNearBottom(el, 100);
this.stickToBottom = near;
this.showJumpLatest = !near;
// bovenaan: oudere geschiedenis laden
if (el.scrollTop < 80) this.loadOlderMessages();
},
minId(items) {
var n = null;
(items || []).forEach(function (m) {
var id = Number(m && m.id);
if (!isNaN(id) && (n === null || id < n)) n = id;
});
return n;
},
async loadOlderMessages() {
if (!this.room || !this.room.id) return;
if (this.loadingHistory || this._loadingOlder || !this.hasMoreHistory) return;
if (!(this.messages || []).length) return;
this._loadingOlder = true;
this.loadingHistory = true;
var el = this.$refs && this.$refs.wrLog;
var prevHeight = el ? el.scrollHeight : 0;
var prevTop = el ? el.scrollTop : 0;
try {
var before = this.minId(this.messages);
var r = await fetch(
'/api/agents/warroom/rooms/' + this.room.id +
'/messages?limit=400&before_id=' + before,
{ credentials: 'same-origin' }
).then(function (x) { return x.json(); });
var older = this.normalizeList(r.items || []);
if (older.length) {
var existing = {};
(this.messages || []).forEach(function (m) { existing[String(m.id)] = true; });
var merged = older.filter(function (m) { return !existing[String(m.id)]; })
.concat(this.messages || []);
this.messages = merged;
this.historyTotal = r.total || this.historyTotal;
this.hasMoreHistory = !!r.has_more;
var self = this;
this.$nextTick(function () {
self.paintAvatars();
if (el) {
// scrollpositie behouden na prepend
el.scrollTop = el.scrollHeight - prevHeight + prevTop;
}
});
} else {
this.hasMoreHistory = false;
}
} catch (e) { /* quiet */ }
this.loadingHistory = false;
this._loadingOlder = false;
},
scrollLog(force) {
@@ -538,7 +596,7 @@ window.WarRoomHub = (function () {
setTimeout(function () { self.replyHighlightId = null; }, 4000);
});
}
if (this.autoDebate) this.scheduleContinue(60000);
if (this.autoDebate) this.scheduleContinue(120000);
} catch (e) {
// Keep optimistic message; restore draft only on hard fail
this.status = e.message || 'Verzenden mislukt';
@@ -642,6 +700,10 @@ window.WarRoomHub = (function () {
if (window.Cockpit) Cockpit.toast(this.status, 'error');
} else if (type === 'done') {
this.typing = null;
if (ev.loop_break) {
this.pauseA2A(180000);
this.status = 'A2A pauze — onderwerp herhaalde zich';
}
}
},
@@ -733,13 +795,15 @@ window.WarRoomHub = (function () {
opts = opts || {};
if (!this.room || !this.room.id) return;
try {
var r = await fetch('/api/agents/warroom/rooms/' + this.room.id + '/messages?limit=200', {
var r = await fetch('/api/agents/warroom/rooms/' + this.room.id + '/messages?limit=800', {
credentials: 'same-origin',
}).then(function (x) { return x.json(); });
var items = this.normalizeList(r.items || []);
if (items.length) {
this.messages = items;
this.lastId = this.maxId(items);
this.historyTotal = r.total || items.length;
this.hasMoreHistory = !!r.has_more;
}
if (!opts.silent) {
this.$nextTick(function () {
@@ -793,11 +857,11 @@ window.WarRoomHub = (function () {
this.busy = false;
this.status = 'A2A live';
// keep conversation alive
this.scheduleContinue(55000);
this.scheduleContinue(90000);
} catch (e) {
this.busy = false;
this.status = 'A2A hervatten…';
this.scheduleContinue(45000);
this.scheduleContinue(75000);
}
this._continueBusy = false;
},
@@ -871,6 +935,21 @@ window.WarRoomHub = (function () {
return d.toLocaleDateString('nl-NL', { weekday: 'short', day: 'numeric', month: 'short', year: 'numeric' });
},
/** Compact stamp on each bubble: 16 aug · 00:53 */
formatMsgStamp(m) {
var raw = (m && (m.created_at || m.createdAt || m.ts)) || '';
if (!raw) return '';
var d = new Date(raw);
if (isNaN(d.getTime())) return '';
var today = new Date();
var sameDay = d.getFullYear() === today.getFullYear() && d.getMonth() === today.getMonth() && d.getDate() === today.getDate();
var time = d.toLocaleTimeString('nl-NL', { hour: '2-digit', minute: '2-digit' });
if (sameDay) return time;
var date = d.toLocaleDateString('nl-NL', { day: 'numeric', month: 'short', year: d.getFullYear() !== today.getFullYear() ? 'numeric' : undefined });
return date + ' · ' + time;
},
dayKey(m) {
var raw = (m && (m.created_at || m.createdAt || m.ts)) || '';
if (!raw) return '';