continued log+rendering refactor
frdel committed
Jan 23, 2026 at 22:08 UTC
c4846e7e9d1ea157175f78842fdb07a65f230e60
15 files changed
+217
-294
initialize.py
+1
-1
@@ -109,7 +109,7 @@ def initialize_agent(override_settings: dict | None = None):
109
# if first_context:
110
# (
111
# first_context.log
112
- # .log(type="warning", content=f"Failed to update MCP settings: {e}", temp=False)
112
+ # .log(type="warning", content=f"Failed to update MCP settings: {e}")
113
# )
114
# (
115
# print_style_helper.PrintStyle(background_color="black", font_color="red", padding=True)
python/extensions/monologue_end/_50_memorize_fragments.py
-3
@@ -114,7 +114,6 @@ class MemorizeMemories(Extension):
114
# memory_log = self.agent.context.log.log(
115
# type="util",
116
# heading=f"Processing memory fragment: {txt[:50]}...",
117
- # temp=False,
117
# update_progress="none" # Don't affect status bar
118
# )
119
@@ -133,7 +132,6 @@ class MemorizeMemories(Extension):
132
memory_log.update(
133
result="Fragment processed successfully",
134
heading=f"Memory fragment completed: {txt[:50]}...",
136
- temp=False, # Show completion message
135
update_progress="none" # Show briefly then disappear
136
)
137
else:
@@ -141,7 +139,6 @@ class MemorizeMemories(Extension):
139
memory_log.update(
140
result="Fragment processing failed",
141
heading=f"Memory fragment failed: {txt[:50]}...",
144
- temp=False, # Show completion message
142
update_progress="none" # Show briefly then disappear
143
)
144
total_processed += 1
python/extensions/monologue_end/_51_memorize_solutions.py
-3
@@ -121,7 +121,6 @@ class MemorizeSolutions(Extension):
121
# solution_log = self.agent.context.log.log(
122
# type="util",
123
# heading=f"Processing solution: {txt[:50]}...",
124
- # temp=False,
124
# update_progress="none" # Don't affect status bar
125
# )
126
@@ -140,7 +139,6 @@ class MemorizeSolutions(Extension):
139
solution_log.update(
140
result="Solution processed successfully",
141
heading=f"Solution completed: {txt[:50]}...",
143
- temp=False, # Show completion message
142
update_progress="none" # Show briefly then disappear
143
)
144
else:
@@ -148,7 +146,6 @@ class MemorizeSolutions(Extension):
146
solution_log.update(
147
result="Solution processing failed",
148
heading=f"Solution failed: {txt[:50]}...",
151
- temp=False, # Show completion message
149
update_progress="none" # Show briefly then disappear
150
)
151
total_processed += 1
python/helpers/docker.py
+2
-2
@@ -73,7 +73,7 @@ class DockerContainerManager:
73
if existing_container:
74
if existing_container.status != 'running':
75
PrintStyle.standard(f"Starting existing container: {self.name} for safe code execution...")
76
- if self.logger: self.logger.log(type="info", content=f"Starting existing container: {self.name} for safe code execution...", temp=True)
76
+ if self.logger: self.logger.log(type="info", content=f"Starting existing container: {self.name} for safe code execution...")
77
78
existing_container.start()
79
self.container = existing_container
@@ -84,7 +84,7 @@ class DockerContainerManager:
84
# PrintStyle.standard(f"Container with name '{self.name}' is already running with ID: {existing_container.id}")
85
else:
86
PrintStyle.standard(f"Initializing docker container {self.name} for safe code execution...")
87
- if self.logger: self.logger.log(type="info", content=f"Initializing docker container {self.name} for safe code execution...", temp=True)
87
+ if self.logger: self.logger.log(type="info", content=f"Initializing docker container {self.name} for safe code execution...")
88
89
self.container = self.client.containers.run(
90
self.image,
python/helpers/fasta2a_server.py
-1
@@ -98,7 +98,6 @@ class AgentZeroWorker(Worker): # type: ignore[misc]
98
heading="Remote user message",
99
content=agent_message.message,
100
kvps={"from": "A2A"},
101
- temp=False,
101
)
102
103
# Process message through Agent Zero (includes response)
python/helpers/log.py
+5
-18
@@ -127,14 +127,12 @@ class LogItem:
127
type: Type
128
heading: str = ""
129
content: str = ""
130
- temp: bool = False
130
update_progress: Optional[ProgressUpdate] = "persistent"
131
kvps: Optional[OrderedDict] = None # Use OrderedDict for kvps
132
id: Optional[str] = None # Add id field
133
guid: str = ""
134
timestamp: float = 0.0
136
- duration_ms: Optional[int] = None
137
- agent_number: int = 0
135
+ agentno: int = 0
136
137
def __post_init__(self):
138
self.guid = self.log.guid
@@ -146,7 +144,6 @@ class LogItem:
144
heading: str | None = None,
145
content: str | None = None,
146
kvps: dict | None = None,
149
- temp: bool | None = None,
147
update_progress: ProgressUpdate | None = None,
148
**kwargs,
149
):
@@ -157,7 +154,6 @@ class LogItem:
154
heading=heading,
155
content=content,
156
kvps=kvps,
160
- temp=temp,
157
update_progress=update_progress,
158
**kwargs,
159
)
@@ -184,11 +180,9 @@ class LogItem:
180
"type": self.type,
181
"heading": self.heading,
182
"content": self.content,
187
- "temp": self.temp,
183
"kvps": self.kvps,
184
"timestamp": self.timestamp,
190
- "duration_ms": self.duration_ms,
191
- "agent_number": self.agent_number,
185
+ "agentno": self.agentno,
186
}
187
188
@@ -207,7 +201,6 @@ class Log:
201
heading: str | None = None,
202
content: str | None = None,
203
kvps: dict | None = None,
210
- temp: bool | None = None,
204
update_progress: ProgressUpdate | None = None,
205
id: Optional[str] = None,
206
**kwargs,
@@ -215,20 +208,19 @@ class Log:
208
209
# add a minimal item to the log
210
# Determine agent number from streaming agent
218
- agent_number = 0
211
+ agentno = 0
212
if self.context and self.context.streaming_agent:
220
- agent_number = self.context.streaming_agent.number
213
+ agentno = self.context.streaming_agent.number
214
215
item = LogItem(
216
log=self,
217
no=len(self.logs),
218
type=type,
226
- agent_number=agent_number,
219
+ agentno=agentno,
220
)
221
# Set duration on previous item and mark it as updated
222
if self.logs:
223
prev = self.logs[-1]
231
- prev.duration_ms = int((item.timestamp - prev.timestamp) * 1000)
224
self.updates += [prev.no]
225
self.logs.append(item)
226
@@ -239,7 +231,6 @@ class Log:
231
heading=heading,
232
content=content,
233
kvps=kvps,
242
- temp=temp,
234
update_progress=update_progress,
235
id=id,
236
**kwargs,
@@ -253,7 +244,6 @@ class Log:
244
heading: str | None = None,
245
content: str | None = None,
246
kvps: dict | None = None,
256
- temp: bool | None = None,
247
update_progress: ProgressUpdate | None = None,
248
id: Optional[str] = None,
249
**kwargs,
@@ -266,9 +256,6 @@ class Log:
256
if type is not None:
257
item.type = type
258
269
- if temp is not None:
270
- item.temp = temp
271
-
259
if update_progress is not None:
260
item.update_progress = update_progress
261
python/helpers/mcp_handler.py
-1
@@ -90,7 +90,6 @@ def initialize_mcp(mcp_servers_config: str):
90
AgentContext.log_to_all(
91
type="warning",
92
content=f"Failed to update MCP settings: {e}",
93
- temp=False,
93
)
94
95
PrintStyle(
python/helpers/memory_consolidation.py
-5
@@ -130,7 +130,6 @@ class MemoryConsolidator:
130
if log_item:
131
log_item.update(
132
progress="No similar memories found, inserting new memory",
133
- temp=True
133
)
134
try:
135
db = await Memory.get(self.agent)
@@ -153,7 +152,6 @@ class MemoryConsolidator:
152
if log_item:
153
log_item.update(
154
progress=f"Found {len(similar_memories)} similar memories, analyzing...",
156
- temp=True,
155
similar_memories_count=len(similar_memories)
156
)
157
@@ -174,7 +172,6 @@ class MemoryConsolidator:
172
if log_item:
173
log_item.update(
174
progress=f"Filtered out {deleted_count} deleted memories, {len(valid_similar_memories)} remain for analysis",
177
- temp=True,
175
race_condition_detected=True,
176
deleted_similar_memories_count=deleted_count
177
)
@@ -185,7 +182,6 @@ class MemoryConsolidator:
182
if log_item:
183
log_item.update(
184
progress="No valid similar memories remain, inserting new memory",
188
- temp=True
185
)
186
try:
187
db = await Memory.get(self.agent)
@@ -220,7 +216,6 @@ class MemoryConsolidator:
216
if log_item:
217
log_item.update(
218
progress="LLM analysis suggests skipping consolidation",
223
- temp=True
219
)
220
try:
221
db = await Memory.get(self.agent)
python/helpers/persist_chat.py
+2
-4
@@ -269,11 +269,9 @@ def _deserialize_log(data: dict[str, Any]) -> "Log":
269
heading=item_data.get("heading", ""),
270
content=item_data.get("content", ""),
271
kvps=OrderedDict(item_data["kvps"]) if item_data["kvps"] else None,
272
- temp=item_data.get("temp", False),
273
- # Pass metrics directly to constructor
272
timestamp=item_data.get("timestamp", 0.0),
275
- duration_ms=item_data.get("duration_ms"),
276
- agent_number=item_data.get("agent_number", 0),
273
+ agentno=item_data.get("agentno", 0),
274
+ id=item_data.get("id"),
275
))
276
log.updates.append(i)
277
i += 1
python/helpers/shell_ssh.py
-1
@@ -82,7 +82,6 @@ class SSHInteractiveSession:
82
self.logger.log(
83
type="info",
84
content=f"SSH Connection attempt {errors}...",
85
- temp=True,
85
)
86
time.sleep(5)
87
else:
webui/components/messages/process-group/process-group.css
+11
-19
@@ -145,87 +145,83 @@
145
/* GEN - agent type (blue/cyan) */
146
.status-gen {
147
--step-accent: #38bdf8;
148
- color: var(--step-accent);
148
}
149
150
/* END - response/done type (green) */
151
.status-end {
152
--step-accent: #22c55e;
154
- color: var(--step-accent);
153
}
154
155
/* USE - tool usage (amber/yellow) */
156
.status-tool {
157
--step-accent: #fbbf24;
160
- color: var(--step-accent);
158
}
159
160
/* MCP - mcp type (amber/yellow) */
161
.status-mcp {
162
--step-accent: #fbbf24;
166
- color: var(--step-accent);
163
}
164
165
/* SUB - subagent type (teal) */
166
.status-sub {
167
--step-accent: #14b8a6;
172
- color: var(--step-accent);
168
}
169
170
/* EXE - code_exe type (magenta/purple) */
171
.status-exe {
172
--step-accent: #ba68c8;
178
- color: var(--step-accent);
173
}
174
175
/* WWW - browser type (indigo) */
176
.status-www {
177
--step-accent: #818cf8;
184
- color: var(--step-accent);
178
}
179
180
/* WAIT - progress type (slate) */
181
.status-wait {
182
--step-accent: #94a3b8;
190
- color: var(--step-accent);
183
}
184
185
/* INF - info type (gray) */
186
.status-inf {
187
--step-accent: #94a3b8;
196
- color: var(--step-accent);
188
}
189
190
/* HNT - hint type (yellow-green) */
191
.status-hnt {
192
--step-accent: #a3e635;
202
- color: var(--step-accent);
193
}
194
195
/* WRN - warning type (orange) */
196
.status-wrn {
197
--step-accent: #f97316;
208
- color: var(--step-accent);
198
}
199
200
/* ERR - error type (red) */
201
.status-err {
202
--step-accent: var(--color-error-text);
214
- color: var(--step-accent);
203
}
204
205
/* UTL - util type (gray-blue) */
206
.status-utl {
207
--step-accent: #64748b;
220
- color: var(--step-accent);
208
}
209
210
/* USR - user type (sky) */
211
.status-usr {
212
--step-accent: #38bdf8;
213
+}
214
+
215
+
216
+.process-step .status-badge{
217
+ color: var(--step-accent);
218
+}
219
+
220
+.process-step .kvps-key{
221
color: var(--step-accent);
222
}
223
224
+
225
/* Completed process group styling */
226
.process-group-completed {
227
opacity: 0.95;
@@ -302,7 +298,7 @@
298
299
/* Process Group Content - Animated expand/collapse */
300
.process-group-content {
305
- display: grid;
301
+ /* display: grid; */
302
grid-template-rows: 0fr;
303
opacity: 0;
304
margin-top: 0;
@@ -321,7 +317,7 @@
317
}
318
319
.process-group.expanded .process-group-content {
324
- grid-template-rows: 1fr;
320
+ /* grid-template-rows: 1fr; */
321
opacity: 1;
322
margin-top: var(--spacing-xs);
323
padding-top: var(--spacing-xs);
@@ -791,10 +787,6 @@
787
display: none;
788
}
789
794
-.process-step.message-util.show-util {
795
- display: flex;
796
-}
797
-
790
/* Thoughts KVP row */
791
.step-kvp.msg-thoughts {
792
display: none;
webui/css/messages.css
+38
-40
@@ -17,7 +17,7 @@
17
scrollbar-color: #555 transparent;
18
padding-left: var(--spacing-sm);
19
padding-right: var(--spacing-sm);
20
- padding-bottom:5em;
20
+ padding-bottom: 5em;
21
}
22
23
#chat-history > *:first-child {
@@ -70,7 +70,6 @@
70
margin: 0;
71
}
72
73
-
73
.message.message-user {
74
text-align: end;
75
margin-bottom: var(--spacing-md);
@@ -117,13 +116,11 @@
116
color: #2e2e2e;
117
} */
118
120
-
119
.message-center {
120
align-self: center;
121
/* border-bottom-left-radius: unset; */
122
}
123
126
-
124
.message-followup .message {
125
border-radius: 0;
126
/* border-top-left-radius: var(--spacing-xxs); */
@@ -333,7 +330,9 @@
330
border-width: 4px 0 4px 6px;
331
border-color: transparent transparent transparent #ef4444;
332
opacity: 0.7;
336
- transition: transform 0.2s ease, opacity 0.15s ease;
333
+ transition:
334
+ transform 0.2s ease,
335
+ opacity 0.15s ease;
336
flex-shrink: 0;
337
font-size: 0;
338
margin-right: 2px;
@@ -375,10 +374,11 @@
374
opacity: 0;
375
margin-top: 0;
376
padding-top: 0;
378
- transition: grid-template-rows 0.25s ease-out,
379
- opacity 0.2s ease-out,
380
- margin-top 0.25s ease-out,
381
- padding-top 0.25s ease-out;
377
+ transition:
378
+ grid-template-rows 0.25s ease-out,
379
+ opacity 0.2s ease-out,
380
+ margin-top 0.25s ease-out,
381
+ padding-top 0.25s ease-out;
382
overflow: hidden;
383
}
384
@@ -415,7 +415,6 @@
415
scrollbar-color: var(--color-error-text) transparent;
416
}
417
418
-
418
/* Terminal styling moved to new terminal block above */
419
420
/* Agent and AI Info */
@@ -433,21 +432,24 @@
432
width: 100%;
433
}
434
435
+.kvps-val{
436
+ font-size: 0.75rem;
437
+}
438
+
439
.kvps-val pre {
440
white-space: pre-wrap; /* keep \n, collapse no spaces, allow wrapping */
441
word-break: break-word; /* optional – forces really long “words” to break */
442
font-family: var(--font-family-code);
443
font-optical-sizing: auto;
444
-webkit-font-optical-sizing: auto;
442
- font-size: 0.75rem;
445
}
446
447
.msg-kvps th,
448
.msg-kvps td {
447
- align-content: center;
449
+ align-content: start;
450
padding: 0.25rem;
451
padding-left: 0;
450
- text-align: left;
452
+ /* text-align: left; */
453
}
454
455
.msg-kvps th {
@@ -510,12 +512,19 @@
512
.kvps-key {
513
font-weight: 500;
514
font-size: var(--font-size-small);
513
- min-width: 7em;
515
+ /* min-width: 7em; */
516
+ max-width: 10em;
517
+ text-align: right;
518
+}
519
+
520
+.kvps-key .material-symbols-outlined{
521
+ font-size: var(--font-size-smaller);
522
}
523
524
.kvps-val {
525
/* margin: 0.65rem 0 0.65rem 0.4rem; */
526
white-space: pre-wrap;
527
+ font-size: var(--font-size-small);
528
}
529
530
.kvps-img {
@@ -531,18 +540,6 @@
540
width: 100%;
541
}
542
534
-.msg-json {
535
- display: none;
536
-}
537
-
538
-.msg-thoughts {
539
- display: auto;
540
-}
541
-
542
-.msg-thoughts .kvps-val {
543
- max-height: 20em;
544
- overflow: auto;
545
-}
543
544
.msg-content {
545
margin-bottom: 0;
@@ -550,13 +547,6 @@
547
overflow: hidden;
548
}
549
553
-.message-temp {
554
- display: none;
555
-}
556
-
557
-.message-temp:not([style*="display: none"]):last-of-type {
558
- display: block; /* or any style you want for visibility */
559
-}
550
551
/* Math (KaTeX) */
552
.katex {
@@ -568,8 +558,14 @@
558
/* Chat width controlled by --chat-max-width CSS variable (set via preferences, default 55em) */
559
@media (min-width: 1025px) {
560
#chat-history {
571
- padding-left: max(var(--spacing-sm), calc((100% - var(--chat-max-width, 55em)) / 2)) !important;
572
- padding-right: max(var(--spacing-sm), calc((100% - var(--chat-max-width, 55em)) / 2)) !important;
561
+ padding-left: max(
562
+ var(--spacing-sm),
563
+ calc((100% - var(--chat-max-width, 55em)) / 2)
564
+ ) !important;
565
+ padding-right: max(
566
+ var(--spacing-sm),
567
+ calc((100% - var(--chat-max-width, 55em)) / 2)
568
+ ) !important;
569
}
570
}
571
@@ -628,7 +624,6 @@
624
border-bottom: none;
625
}
626
631
-
627
.light-mode .message-user {
628
color: #4e4e4e;
629
}
@@ -647,14 +642,17 @@
642
max-width: 25em;
643
max-height: 25em;
644
border-radius: var(--border-radius);
650
- background: repeating-linear-gradient(45deg, var(--color-panel) 0 10px, var(--color-chat-background) 10px 20px);
645
+ background: repeating-linear-gradient(
646
+ 45deg,
647
+ var(--color-panel) 0 10px,
648
+ var(--color-chat-background) 10px 20px
649
+ );
650
}
651
653
-.message-agent-response .msg-content .message-markdown-image-wrap img:hover{
652
+.message-agent-response .msg-content .message-markdown-image-wrap img:hover {
653
transform: translateY(-2px);
654
}
655
657
-
656
.msg-content h1 {
657
font-size: 1.25em;
658
font-weight: 800;
@@ -767,7 +765,7 @@
765
width: 100%;
766
justify-content: end;
767
margin-top: 4em;
770
- margin-bottom:4em;
768
+ margin-bottom: 4em;
769
}
770
771
.message-container {
webui/index.css
-39
@@ -1099,45 +1099,6 @@ input:checked + .slider:before {
1099
}
1100
}
1101
1102
-@media (max-width: 768px) {
1103
- /* .copy-button {
1104
- display: none !important;
1105
- } */
1106
-
1107
- /* .msg-content span,
1108
- .kvps-val,
1109
- .message-text span {
1110
- cursor: pointer;
1111
- position: relative;
1112
- } */
1113
-
1114
- /* .msg-thoughts span::after,
1115
- .msg-content span::after,
1116
- .kvps-val::after,
1117
- .message-text::after {
1118
- content: "Copied!";
1119
- position: absolute;
1120
- opacity: 0;
1121
- font-family: "Rubik", Arial, Helvetica, sans-serif;
1122
- font-size: 0.7rem;
1123
- padding: 6px var(--spacing-sm);
1124
- -webkit-transition: opacity var(--transition-speed) ease-in-out;
1125
- transition: opacity var(--transition-speed) ease-in-out;
1126
- right: 0px;
1127
- top: 0px;
1128
- background-color: var(--color-background);
1129
- border: none;
1130
- border-radius: 5px;
1131
- color: inherit;
1132
- } */
1133
-
1134
- /* .msg-thoughts span.copied::after,
1135
- .msg-content span.copied::after,
1136
- .kvps-val.copied::after,
1137
- .message-text.copied::after {
1138
- opacity: 1;
1139
- } */
1140
-}
1102
1103
@media (max-height: 600px) {
1104
/* consistent font sizing */
webui/index.js
+5
-17
@@ -66,9 +66,9 @@ export async function sendMessage() {
66
: "";
67
68
// Render user message with attachments
69
- setMessage(messageId, "user", heading, message, {
69
+ setMessage({ id: messageId, type: "user", heading, content: message, kvps: {
70
// attachments: attachmentsWithUrls, // skip here, let the backend properly log them
71
- });
71
+ }});
72
73
// sleep one frame to render the message before upload starts - better UX
74
sleep(0);
@@ -200,8 +200,8 @@ async function updateUserTime() {
200
updateUserTime();
201
setInterval(updateUserTime, 1000);
202
203
-function setMessage(id, type, heading, content, kvps = null, timestamp = null, durationMs = null, /* tokensIn = 0, tokensOut = 0, */ agentNumber = 0) {
204
- const result = msgs.setMessage(id, type, heading, content, kvps, timestamp, durationMs, /* tokensIn, tokensOut, */ agentNumber);
203
+function setMessage(...params) {
204
+ const result = msgs.setMessage(...params);
205
const chatHistoryEl = document.getElementById("chat-history");
206
if (preferencesStore.autoScroll && chatHistoryEl) {
207
chatHistoryEl.scrollTop = chatHistoryEl.scrollHeight;
@@ -317,19 +317,7 @@ export async function poll() {
317
if (lastLogVersion != response.log_version) {
318
updated = true;
319
for (const log of response.logs) {
320
- const messageId = log.id || log.no; // Use log.id if available
321
- setMessage(
322
- messageId,
323
- log.type,
324
- log.heading,
325
- log.content,
326
- log.kvps,
327
- log.timestamp,
328
- log.duration_ms,
329
- // log.tokens_in,
330
- // log.tokens_out,
331
- log.agent_number || 0 // Agent number for identifying main/subordinate agents
332
- );
320
+ setMessage(log);
321
}
322
afterMessagesUpdate(response.logs);
323
applyModeSteps(preferencesStore.detailMode, preferencesStore.showUtils);
webui/js/messages.js
+153
-140
@@ -73,7 +73,7 @@ const TYPE_STATUS_CLASSES = {
73
done: "status-end"
74
};
75
76
-const chatHistory = document.getElementById("chat-history");
76
+let chatHistory = null;
77
78
// handlers for log message rendering
79
export function getMessageHandler(type) {
@@ -124,7 +124,7 @@ function setActiveProcessGroup(group) {
124
if (group.classList.contains("active")) return;
125
126
// Clear active + shiny from all other groups
127
- document.querySelectorAll(".process-group.active").forEach(g => {
127
+ getChatHistoryEl().querySelectorAll(".process-group.active").forEach(g => {
128
if (g !== group) {
129
g.classList.remove("active");
130
g.querySelectorAll(".step-title.shiny-text").forEach(el => el.classList.remove("shiny-text"));
@@ -137,13 +137,14 @@ function setActiveProcessGroup(group) {
137
138
export function clearActiveStepShine() {
139
// Clear all shiny step titles in process steps
140
- document.querySelectorAll(".process-step .step-title.shiny-text").forEach((el) => {
140
+ getChatHistoryEl().querySelectorAll(".process-step .step-title.shiny-text").forEach((el) => {
141
el.classList.remove("shiny-text");
142
});
143
}
144
145
function getChatHistoryEl() {
146
- return chatHistory || document.getElementById("chat-history");
146
+ if(!chatHistory) chatHistory = document.getElementById("chat-history");
147
+ return chatHistory;
148
}
149
150
function getLastMessageContainer() {
@@ -224,9 +225,11 @@ function updateBadgeText(badge, newCode) {
225
226
227
// entrypoint called from poll/WS communication, this is how all messages are rendered and updated
227
-export function setMessage(id, type, heading, content, kvps = null, timestamp = null, durationMs = null, agentNumber = 0) {
228
+// input is raw log format
229
+export function setMessage({ no, id, type, heading, content, kvps, timestamp, agentno, ...additional }) {
230
const handler = getMessageHandler(type);
229
- return handler(id, type, heading, content, kvps, timestamp, durationMs, agentNumber);
231
+ // prefer log ID if set to match user message created on frontend with backend updates
232
+ return handler({ id: id || no, type, heading, content, kvps, timestamp, agentno, ...additional });
233
}
234
235
function getOrCreateMessageContainer(id, position, containerClasses = [], forceNewGroup = false) {
@@ -287,8 +290,7 @@ function buildDetailPayload(stepData) {
290
content: stepData.content,
291
kvps: stepData.kvps,
292
timestamp: stepData.timestamp,
290
- durationMs: stepData.durationMs,
291
- agentNumber: stepData.agentNumber,
293
+ agentno: stepData.agentno,
294
toolName: stepData.toolName,
295
statusCode: stepData.statusCode,
296
statusClass: stepData.statusClass
@@ -310,17 +312,24 @@ function buildStepCopyContent(stepData) {
312
return parts.join("\n\n");
313
}
314
313
-function drawProcessStep(id, title, statusClass, statusCode, kvps = null, detailHandler = null, copyContent = null, speakContent = null, options = {}) {
314
- const {
315
- type = "agent",
316
- heading = null,
317
- content = null,
318
- timestamp = null,
319
- durationMs = null,
320
- agentNumber = 0,
321
- toolName = null,
322
- detailPayload = null
323
- } = options;
315
+function drawProcessStep({
316
+ id,
317
+ title,
318
+ statusClass,
319
+ statusCode,
320
+ kvps = null,
321
+ detailHandler = null,
322
+ copyContent = null,
323
+ speakContent = null,
324
+ type = "agent",
325
+ heading = null,
326
+ content = null,
327
+ timestamp = null,
328
+ agentno = 0,
329
+ toolName = null,
330
+ detailPayload = null,
331
+ ...additional
332
+}) {
333
334
const group = getOrCreateProcessGroup(id);
335
const stepId = `process-step-${id}`;
@@ -332,8 +341,7 @@ function drawProcessStep(id, title, statusClass, statusCode, kvps = null, detail
341
content,
342
kvps,
343
timestamp,
335
- durationMs,
336
- agentNumber,
344
+ agentno,
345
toolName,
346
statusCode,
347
statusClass
@@ -344,12 +352,10 @@ function drawProcessStep(id, title, statusClass, statusCode, kvps = null, detail
352
const copyText = copyContent ?? buildStepCopyContent(stepData);
353
const speakText = speakContent ?? copyText;
354
347
- if (step) {
355
+ if (step)
356
updateProcessStep(step, stepData, detailData, copyText, speakText, detailHandler);
349
- return step;
350
- }
351
-
352
- step = addProcessStep(group, stepData, detailData, copyText, speakText, detailHandler);
357
+ else
358
+ step = addProcessStep(group, stepData, detailData, copyText, speakText, detailHandler);
359
return step;
360
}
361
@@ -537,7 +543,7 @@ export function addBlankTargetsToLinks(str) {
543
return doc.body.innerHTML;
544
}
545
540
-export function drawMessageDefault(id, type, heading, content, kvps = null) {
546
+export function drawMessageDefault({ id, heading, content, kvps = null, ...additional }) {
547
return drawStandaloneMessage(id, heading, content, {
548
position: "left",
549
containerClasses: ["ai-container"],
@@ -548,37 +554,44 @@ export function drawMessageDefault(id, type, heading, content, kvps = null) {
554
});
555
}
556
551
-export function drawMessageAgent(id, type, heading, content, kvps = null, timestamp = null, durationMs = null, agentNumber = 0) {
557
+export function drawMessageAgent({ id, type, heading, content, kvps = null, timestamp = null, agentno = 0, ...additional }) {
558
const title = getStepTitle(heading, kvps, type);
559
const statusCode = getStatusCode(type);
560
const statusClass = getStatusClass(type);
561
const toolName = kvps?.tool_name || null;
562
+ let displayKvps = { "icon://lightbulb": kvps?.thoughts, "icon://step": heading, "icon school":"dummy" }
563
557
- return drawProcessStep(id, title, statusClass, statusCode, kvps, null, null, null, {
564
+ return drawProcessStep({
565
+ id,
566
+ title,
567
+ statusClass,
568
+ statusCode,
569
+ kvps: displayKvps,
570
type,
571
heading,
572
content,
561
- kvps,
573
timestamp,
563
- durationMs,
564
- agentNumber,
574
+ agentno,
575
toolName
576
});
577
}
578
569
-export function drawMessageResponse(id, type, heading, content, kvps = null, timestamp = null, durationMs = null, agentNumber = 0) {
570
- if (agentNumber && agentNumber > 0) {
579
+export function drawMessageResponse({ id, type, heading, content, kvps = null, timestamp = null, agentno = 0, ...additional }) {
580
+ if (agentno && agentno > 0) {
581
const title = getStepTitle(heading, kvps, type);
582
const statusCode = getStatusCode(type);
583
const statusClass = getStatusClass(type);
574
- return drawProcessStep(id, title, statusClass, statusCode, kvps, null, null, null, {
584
+ return drawProcessStep({
585
+ id,
586
+ title,
587
+ statusClass,
588
+ statusCode,
589
+ kvps,
590
type,
591
heading,
592
content,
578
- kvps,
593
timestamp,
580
- durationMs,
581
- agentNumber
594
+ agentno
595
});
596
}
597
@@ -599,7 +612,7 @@ export function drawMessageResponse(id, type, heading, content, kvps = null, tim
612
}
613
614
602
-export function drawMessageUser(id, type, heading, content, kvps = null) {
615
+export function drawMessageUser({ id, heading, content, kvps = null, ...additional }) {
616
const messageContainer = getOrCreateMessageContainer(id, "right", ["user-container"], true);
617
618
// Find existing message div or create new one
@@ -707,92 +720,107 @@ export function drawMessageUser(id, type, heading, content, kvps = null) {
720
addActionButtonsToElement(messageDiv, { copyContent: content || "", speakContent: content || "" });
721
}
722
710
-export function drawMessageTool(id, type, heading, content, kvps = null, timestamp = null, durationMs = null, agentNumber = 0) {
723
+export function drawMessageTool({ id, type, heading, content, kvps = null, timestamp = null, agentno = 0, ...additional }) {
724
const toolName = kvps?.tool_name || null;
725
const title = getStepTitle(heading, kvps, type);
726
const statusCode = getStatusCode(type, toolName);
727
const statusClass = getStatusClass(type, toolName);
728
716
- return drawProcessStep(id, title, statusClass, statusCode, kvps, null, null, null, {
729
+ return drawProcessStep({
730
+ id,
731
+ title,
732
+ statusClass,
733
+ statusCode,
734
+ kvps,
735
type,
736
heading,
737
content,
720
- kvps,
738
timestamp,
722
- durationMs,
723
- agentNumber,
739
+ agentno,
740
toolName
741
});
742
}
743
728
-export function drawMessageCodeExe(id, type, heading, content, kvps = null, timestamp = null, durationMs = null, agentNumber = 0) {
744
+export function drawMessageCodeExe({ id, type, heading, content, kvps = null, timestamp = null, agentno = 0, ...additional }) {
745
const title = getStepTitle(heading, kvps, type);
746
const statusCode = getStatusCode(type);
747
const statusClass = getStatusClass(type);
748
733
- return drawProcessStep(id, title, statusClass, statusCode, kvps, null, null, null, {
749
+ return drawProcessStep({
750
+ id,
751
+ title,
752
+ statusClass,
753
+ statusCode,
754
+ kvps,
755
type,
756
heading,
757
content,
737
- kvps,
758
timestamp,
739
- durationMs,
740
- agentNumber
759
+ agentno
760
});
761
}
762
744
-export function drawMessageBrowser(id, type, heading, content, kvps = null, timestamp = null, durationMs = null, agentNumber = 0) {
763
+export function drawMessageBrowser({ id, type, heading, content, kvps = null, timestamp = null, agentno = 0, ...additional }) {
764
const title = getStepTitle(heading, kvps, type);
765
const statusCode = getStatusCode(type);
766
const statusClass = getStatusClass(type);
767
749
- return drawProcessStep(id, title, statusClass, statusCode, kvps, null, null, null, {
768
+ return drawProcessStep({
769
+ id,
770
+ title,
771
+ statusClass,
772
+ statusCode,
773
+ kvps,
774
type,
775
heading,
776
content,
753
- kvps,
777
timestamp,
755
- durationMs,
756
- agentNumber
778
+ agentno
779
});
780
}
781
760
-export function drawMessageMcp(id, type, heading, content, kvps = null, timestamp = null, durationMs = null, agentNumber = 0) {
782
+export function drawMessageMcp({ id, type, heading, content, kvps = null, timestamp = null, agentno = 0, ...additional }) {
783
const toolName = kvps?.tool_name || null;
784
const title = getStepTitle(heading, kvps, type);
785
const statusCode = getStatusCode(type, toolName);
786
const statusClass = getStatusClass(type, toolName);
787
766
- return drawProcessStep(id, title, statusClass, statusCode, kvps, null, null, null, {
788
+ return drawProcessStep({
789
+ id,
790
+ title,
791
+ statusClass,
792
+ statusCode,
793
+ kvps,
794
type,
795
heading,
796
content,
770
- kvps,
797
timestamp,
772
- durationMs,
773
- agentNumber,
798
+ agentno,
799
toolName
800
});
801
}
802
778
-export function drawMessageSubagent(id, type, heading, content, kvps = null, timestamp = null, durationMs = null, agentNumber = 0) {
803
+export function drawMessageSubagent({ id, type, heading, content, kvps = null, timestamp = null, agentno = 0, ...additional }) {
804
const title = getStepTitle(heading, kvps, type);
805
const statusCode = getStatusCode(type);
806
const statusClass = getStatusClass(type);
807
783
- return drawProcessStep(id, title, statusClass, statusCode, kvps, null, null, null, {
808
+ return drawProcessStep({
809
+ id,
810
+ title,
811
+ statusClass,
812
+ statusCode,
813
+ kvps,
814
type,
815
heading,
816
content,
787
- kvps,
817
timestamp,
789
- durationMs,
790
- agentNumber
818
+ agentno
819
});
820
}
821
822
795
-export function drawMessageInfo(id, type, heading, content, kvps = null) {
823
+export function drawMessageInfo({ id, heading, content, kvps = null, ...additional }) {
824
return drawStandaloneMessage(id, heading, content, {
825
position: "mid",
826
containerClasses: ["ai-container", "center-container"],
@@ -801,55 +829,64 @@ export function drawMessageInfo(id, type, heading, content, kvps = null) {
829
});
830
}
831
804
-export function drawMessageUtil(id, type, heading, content, kvps = null, timestamp = null, durationMs = null, agentNumber = 0) {
832
+export function drawMessageUtil({ id, type, heading, content, kvps = null, timestamp = null, agentno = 0, ...additional }) {
833
const title = getStepTitle(heading, kvps, type);
834
const statusCode = getStatusCode(type);
835
const statusClass = getStatusClass(type);
836
809
- return drawProcessStep(id, title, statusClass, statusCode, kvps, null, null, null, {
837
+ return drawProcessStep({
838
+ id,
839
+ title,
840
+ statusClass,
841
+ statusCode,
842
+ kvps,
843
type,
844
heading,
845
content,
813
- kvps,
846
timestamp,
815
- durationMs,
816
- agentNumber
847
+ agentno
848
});
849
}
850
820
-export function drawMessageHint(id, type, heading, content, kvps = null, timestamp = null, durationMs = null, agentNumber = 0) {
851
+export function drawMessageHint({ id, type, heading, content, kvps = null, timestamp = null, agentno = 0, ...additional }) {
852
const title = getStepTitle(heading, kvps, type);
853
const statusCode = getStatusCode(type);
854
const statusClass = getStatusClass(type);
855
825
- return drawProcessStep(id, title, statusClass, statusCode, kvps, null, null, null, {
856
+ return drawProcessStep({
857
+ id,
858
+ title,
859
+ statusClass,
860
+ statusCode,
861
+ kvps,
862
type,
863
heading,
864
content,
829
- kvps,
865
timestamp,
831
- durationMs,
832
- agentNumber
866
+ agentno
867
});
868
}
869
836
-export function drawMessageProgress(id, type, heading, content, kvps = null, timestamp = null, durationMs = null, agentNumber = 0) {
870
+export function drawMessageProgress({ id, type, heading, content, kvps = null, timestamp = null, agentno = 0, ...additional }) {
871
const title = getStepTitle(heading, kvps, type);
872
const statusCode = getStatusCode(type);
873
const statusClass = getStatusClass(type);
874
841
- return drawProcessStep(id, title, statusClass, statusCode, kvps, null, null, null, {
875
+ return drawProcessStep({
876
+ id,
877
+ title,
878
+ statusClass,
879
+ statusCode,
880
+ kvps,
881
type,
882
heading,
883
content,
845
- kvps,
884
timestamp,
847
- durationMs,
848
- agentNumber
885
+ agentno
886
});
887
}
888
852
-export function drawMessageWarning(id, type, heading, content, kvps = null) {
889
+export function drawMessageWarning({ id, heading, content, kvps = null, ...additional }) {
890
return drawStandaloneMessage(id, heading, content, {
891
position: "mid",
892
containerClasses: ["ai-container", "center-container"],
@@ -858,7 +895,7 @@ export function drawMessageWarning(id, type, heading, content, kvps = null) {
895
});
896
}
897
861
-export function drawMessageError(id, type, heading, content, kvps = null) {
898
+export function drawMessageError({ id, heading, content, kvps = null, ...additional }) {
899
const messageContainer = getOrCreateMessageContainer(id, "mid", ["ai-container", "center-container"]);
900
901
// Create or get the message div
@@ -1010,9 +1047,6 @@ function drawKvpsIncremental(container, kvps, latex) {
1047
1048
// Update row classes
1049
row.className = "kvps-row";
1013
- if (key === "thoughts") {
1014
- row.classList.add("msg-thoughts");
1015
- }
1050
1051
// Handle key cell
1052
let th = row.querySelector(".kvps-key");
@@ -1083,11 +1117,12 @@ function drawKvpsIncremental(container, kvps, latex) {
1117
imageViewerStore.open(imgElement.src, { refreshInterval: 1000 });
1118
});
1119
} else {
1086
- const pre = document.createElement("pre");
1120
+ // const pre = document.createElement("pre");
1121
const span = document.createElement("span");
1122
span.innerHTML = convertHTML(value);
1089
- pre.appendChild(span);
1090
- tdiv.appendChild(pre);
1123
+ // pre.appendChild(span);
1124
+ // tdiv.appendChild(pre);
1125
+ tdiv.appendChild(span)
1126
1127
// Add action buttons to the row
1128
// const row = tdiv.closest(".kvps-row");
@@ -1430,13 +1465,13 @@ function addStepCollapseInteractionHandlers(stepElement) {
1465
/**
1466
* Find parent delegation step for nested agents (DOM-first, reverse scan).
1467
*/
1433
-function findParentDelegationStep(group, agentNumber) {
1434
- if (!group || agentNumber <= 0) return null;
1468
+function findParentDelegationStep(group, agentno) {
1469
+ if (!group || agentno <= 0) return null;
1470
const steps = group.querySelectorAll(".process-step");
1471
for (let i = steps.length - 1; i >= 0; i -= 1) {
1472
const step = steps[i];
1473
const stepAgent = Number(step.getAttribute("data-agent-number"));
1439
- if (stepAgent === agentNumber - 1 && step.getAttribute("data-tool-name") === "call_subordinate") {
1474
+ if (stepAgent === agentno - 1 && step.getAttribute("data-tool-name") === "call_subordinate") {
1475
return step;
1476
}
1477
}
@@ -1455,8 +1490,7 @@ function addProcessStep(group, stepData, detailPayload, copyContent, speakConten
1490
content,
1491
kvps,
1492
timestamp,
1458
- durationMs,
1459
- agentNumber,
1493
+ agentno,
1494
toolName,
1495
statusCode,
1496
statusClass
@@ -1471,7 +1505,7 @@ function addProcessStep(group, stepData, detailPayload, copyContent, speakConten
1505
step.classList.add("process-step");
1506
step.setAttribute("data-type", type);
1507
step.setAttribute("data-step-id", id);
1474
- step.setAttribute("data-agent-number", agentNumber);
1508
+ step.setAttribute("data-agent-number", agentno);
1509
1510
if (toolName) {
1511
step.setAttribute("data-tool-name", toolName);
@@ -1495,19 +1529,14 @@ function addProcessStep(group, stepData, detailPayload, copyContent, speakConten
1529
}
1530
}
1531
1498
- // Store duration from backend (used for final duration calculation)
1499
- if (durationMs != null) {
1500
- step.setAttribute("data-duration-ms", durationMs);
1501
- }
1502
-
1503
- // Add message-util class for utility/info types (controlled by showUtils preference)
1504
- if (type === "util" || type === "info" || type === "hint") {
1505
- step.classList.add("message-util");
1506
- // Apply current preference state
1507
- if (preferencesStore.showUtils) {
1508
- step.classList.add("show-util");
1509
- }
1510
- }
1532
+ // // Add message-util class for utility/info types (controlled by showUtils preference)
1533
+ // if (type === "util" || type === "info" || type === "hint") {
1534
+ // step.classList.add("message-util");
1535
+ // // Apply current preference state
1536
+ // if (preferencesStore.showUtils) {
1537
+ // step.classList.add("show-util");
1538
+ // }
1539
+ // }
1540
1541
// Determine if this new step should be expanded
1542
const detailMode = preferencesStore.detailMode;
@@ -1608,7 +1637,7 @@ function addProcessStep(group, stepData, detailPayload, copyContent, speakConten
1637
let appendTarget = stepsContainer;
1638
1639
// Check if this step belongs to a subordinate agent
1611
- const parentStep = findParentDelegationStep(group, agentNumber);
1640
+ const parentStep = findParentDelegationStep(group, agentno);
1641
if (parentStep) {
1642
appendTarget = getNestedContainer(parentStep);
1643
step.classList.add("nested-step");
@@ -1655,8 +1684,7 @@ function updateProcessStep(stepElement, stepData, detailPayload, copyContent, sp
1684
content,
1685
kvps,
1686
timestamp,
1658
- durationMs,
1659
- agentNumber,
1687
+ agentno,
1688
toolName,
1689
statusCode,
1690
statusClass
@@ -1672,12 +1700,8 @@ function updateProcessStep(stepElement, stepData, detailPayload, copyContent, sp
1700
stepElement.setAttribute("data-timestamp", timestamp);
1701
}
1702
1675
- if (durationMs != null) {
1676
- stepElement.setAttribute("data-duration-ms", durationMs);
1677
- }
1678
-
1679
- if (agentNumber !== undefined) {
1680
- stepElement.setAttribute("data-agent-number", agentNumber);
1703
+ if (agentno !== undefined) {
1704
+ stepElement.setAttribute("data-agent-number", agentno);
1705
}
1706
1707
const toolNameToUse = resolveToolName(type, kvps, stepElement) || toolName;
@@ -1837,6 +1861,8 @@ function cleanStepTitle(text, maxLength) {
1861
function renderStepDetailContent(container, content, kvps, type = null) {
1862
container.innerHTML = "";
1863
1864
+ drawKvpsIncremental(container, kvps)
1865
+
1866
// Special handling for response type - show content as markdown (for subordinate responses)
1867
if (type === "response" && content && content.trim()) {
1868
const responseDiv = document.createElement("div");
@@ -2121,25 +2147,14 @@ function updateProcessGroupHeader(group) {
2147
// Update duration metric
2148
const durationMetricEl = metricsEl?.querySelector(".metric-duration .metric-value");
2149
if (durationMetricEl && steps.length > 0) {
2124
- // Calculate accumulated duration from backend data
2125
- let accumulatedMs = 0;
2126
- steps.forEach(step => {
2127
- accumulatedMs += parseInt(step.getAttribute("data-duration-ms") || "0", 10);
2128
- });
2129
-
2130
- // Check if last step is still in progress (no duration_ms set yet)
2150
+ const firstTimestampMs = parseInt(steps[0]?.getAttribute("data-timestamp") || "0", 10);
2151
+
2152
const lastStep = steps[steps.length - 1];
2132
- const lastStepDuration = lastStep.getAttribute("data-duration-ms");
2133
- const lastStepTimestamp = lastStep.getAttribute("data-timestamp");
2134
-
2135
- if (lastStepDuration == null && lastStepTimestamp) {
2136
- // Last step is in progress - add live elapsed time for this step only
2137
- const lastStepStartMs = parseFloat(lastStepTimestamp) * 1000;
2138
- const liveElapsedMs = Math.max(0, Date.now() - lastStepStartMs);
2139
- accumulatedMs += liveElapsedMs;
2140
- }
2141
-
2142
- durationMetricEl.textContent = formatDuration(accumulatedMs);
2153
+ const lastTimestampMs = parseInt(lastStep.getAttribute("data-timestamp") || "0", 10);
2154
+
2155
+ const totalDurationMs = Math.max(0, lastTimestampMs - firstTimestampMs);
2156
+
2157
+ durationMetricEl.textContent = formatDuration(totalDurationMs);
2158
}
2159
2160
if (steps.length > 0) {
@@ -2226,13 +2241,11 @@ function markProcessGroupComplete(group, responseTitle) {
2241
});
2242
}
2243
2229
- // Calculate final duration from backend data (sum of all step durations)
2244
+ // Calculate final duration from backend data (difference between first and last timestamps)
2245
const steps = group.querySelectorAll(".process-step");
2231
- let totalDurationMs = 0;
2232
- steps.forEach(step => {
2233
- const durationMs = parseInt(step.getAttribute("data-duration-ms") || "0", 10);
2234
- totalDurationMs += durationMs;
2235
- });
2246
+ const firstTimestampMs = parseInt(steps[0]?.getAttribute("data-timestamp") || "0", 10);
2247
+ const lastTimestampMs = parseInt(steps[steps.length - 1]?.getAttribute("data-timestamp") || "0", 10);
2248
+ const totalDurationMs = Math.max(0, lastTimestampMs - firstTimestampMs);
2249
2250
// Update duration metric with final value from backend
2251
const metricsEl = group.querySelector(".group-metrics");