Return LogOutput object and update callers

Introduce a LogOutput dataclass (items, start, end) and change Log.output to return a LogOutput instead of a bare list. Update callers to unwrap items: api_log_get now reads log_output.items, and state_snapshot builds logs and log_end from the LogOutput and uses log_end for log_version. Also re-enable the @extensible decorator on AgentContext.output. These changes expose log metadata (start/end) alongside items for consumers.

frdel committed Mar 3, 2026 at 11:08 UTC 2cf73c4b4a28171e9e134796f92e3e88f08f6ff8
4 files changed +19 -5
agent.py
+1 -1
@@ -177,7 +177,7 @@ class AgentContext:
177 # recursive is not used now, prepared for context hierarchy
178 self.output_data[key] = value
179
180 - # @extensible
180 + @extensible
181 def output(self):
182 return {
183 "id": self.id,
python/api/api_log_get.py
+2 -1
@@ -44,7 +44,8 @@ class ApiLogGet(ApiHandler):
44 start_pos = max(0, total_items - length)
45
46 # Get log items from the calculated start position
47 - log_items = context.log.output(start=start_pos)
47 + log_output = context.log.output(start=start_pos)
48 + log_items = log_output.items
49
50 # Return log data with metadata
51 return {
python/helpers/log.py
+8 -1
@@ -209,6 +209,13 @@ class LogItem:
209 }
210
211
212 +@dataclass(frozen=True)
213 +class LogOutput:
214 + items: list[dict[str, Any]]
215 + start: int
216 + end: int
217 +
218 +
219 class Log:
220
221 def __init__(self):
@@ -400,7 +407,7 @@ class Log:
407 if update not in seen and update < len(logs):
408 out.append(logs[update].output())
409 seen.add(update)
403 - return out
410 + return LogOutput(items=out, start=start, end=end)
411
412 def reset(self):
413 with self._lock:
python/helpers/state_snapshot.py
+8 -2
@@ -224,7 +224,13 @@ async def build_snapshot_from_request(*, request: StateRequestV1) -> SnapshotV1:
224
225 active_context = AgentContext.get(ctxid) if ctxid else None
226
227 - logs = active_context.log.output(start=from_no) if active_context else []
227 + if active_context:
228 + log_output = active_context.log.output(start=from_no)
229 + logs = log_output.items
230 + log_end = log_output.end
231 + else:
232 + logs = []
233 + log_end = 0
234
235 notification_manager = AgentContext.get_notification_manager()
236 notifications = notification_manager.output(start=notifications_from_no)
@@ -290,7 +296,7 @@ async def build_snapshot_from_request(*, request: StateRequestV1) -> SnapshotV1:
296 "tasks": tasks,
297 "logs": logs,
298 "log_guid": active_context.log.guid if active_context else "",
293 - "log_version": len(active_context.log.updates) if active_context else 0,
299 + "log_version": log_end,
300 "log_progress": active_context.log.progress if active_context else 0,
301 "log_progress_active": bool(active_context.log.progress_active) if active_context else False,
302 "paused": active_context.paused if active_context else False,