# ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= import json from dataclasses import asdict, dataclass from mirage.observe.record import OpRecord EVENT_OP = "op" EVENT_COMMAND = "command" EVENT_CLEAR = "clear" EVENT_DELETE = "delete" STDOUT_TRUNCATE = 4096 @dataclass class LogEntry: """Unified log entry for ops, commands, and history control events. Args: type (str): One of EVENT_OP, EVENT_COMMAND, EVENT_CLEAR, EVENT_DELETE. agent (str): Agent ID. session (str): Session ID. timestamp (int): UTC epoch milliseconds. op (str | None): Operation name (for type="op"). path (str | None): Virtual path (for type="op"). source (str | None): Resource name (for type="op"). bytes (int | None): Bytes transferred (for type="op"). duration_ms (int | None): Duration in ms (for type="op"). command (str | None): Shell command (for type="command"). exit_code (int | None): Exit code (for type="command"). stdout (str | None): Truncated stdout (for type="command"). offset (int | None): 1-based listing position (for type="delete"); negative counts back from the end. """ type: str agent: str session: str timestamp: int cwd: str | None = None op: str | None = None path: str | None = None source: str | None = None bytes: int | None = None duration_ms: int | None = None command: str | None = None exit_code: int | None = None stdout: str | None = None offset: int | None = None @staticmethod def from_op_record( rec: OpRecord, agent: str, session: str, cwd: str | None = None, ) -> "LogEntry": """Create a LogEntry from an OpRecord. Args: rec (OpRecord): The operation record. agent (str): Agent ID. session (str): Session ID. cwd (str | None): Session cwd at log time. Returns: LogEntry: Unified log entry. """ return LogEntry( type=EVENT_OP, agent=agent, session=session, timestamp=rec.timestamp, cwd=cwd, op=rec.op, path=rec.path, source=rec.source, bytes=rec.bytes, duration_ms=rec.duration_ms, ) def to_json_line(self) -> str: d = {k: v for k, v in asdict(self).items() if v is not None} return json.dumps(d, separators=(",", ":"))