/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. *--------------------------------------------------------------------------------------------*/ package com.github.copilot.sdk.events; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import java.util.logging.Level; import java.util.logging.Logger; import java.util.HashMap; import java.util.Map; /** * Parser for session events that handles polymorphic deserialization. *

* This class deserializes JSON event data into the appropriate * {@link AbstractSessionEvent} subclass based on the "type" field. It is used * internally by the SDK to convert server events to Java objects. * *

Supported Event Types

* * * @see AbstractSessionEvent * @since 1.0.0 */ public class SessionEventParser { private static final Logger LOG = Logger.getLogger(SessionEventParser.class.getName()); private static final ObjectMapper MAPPER; private static final Map> TYPE_MAP = new HashMap<>(); static { MAPPER = new ObjectMapper(); MAPPER.registerModule(new JavaTimeModule()); MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); TYPE_MAP.put("session.start", SessionStartEvent.class); TYPE_MAP.put("session.resume", SessionResumeEvent.class); TYPE_MAP.put("session.error", SessionErrorEvent.class); TYPE_MAP.put("session.idle", SessionIdleEvent.class); TYPE_MAP.put("session.info", SessionInfoEvent.class); TYPE_MAP.put("session.model_change", SessionModelChangeEvent.class); TYPE_MAP.put("session.handoff", SessionHandoffEvent.class); TYPE_MAP.put("session.truncation", SessionTruncationEvent.class); TYPE_MAP.put("session.snapshot_rewind", SessionSnapshotRewindEvent.class); TYPE_MAP.put("session.usage_info", SessionUsageInfoEvent.class); TYPE_MAP.put("session.compaction_start", SessionCompactionStartEvent.class); TYPE_MAP.put("session.compaction_complete", SessionCompactionCompleteEvent.class); TYPE_MAP.put("user.message", UserMessageEvent.class); TYPE_MAP.put("pending_messages.modified", PendingMessagesModifiedEvent.class); TYPE_MAP.put("assistant.turn_start", AssistantTurnStartEvent.class); TYPE_MAP.put("assistant.intent", AssistantIntentEvent.class); TYPE_MAP.put("assistant.reasoning", AssistantReasoningEvent.class); TYPE_MAP.put("assistant.reasoning_delta", AssistantReasoningDeltaEvent.class); TYPE_MAP.put("assistant.message", AssistantMessageEvent.class); TYPE_MAP.put("assistant.message_delta", AssistantMessageDeltaEvent.class); TYPE_MAP.put("assistant.turn_end", AssistantTurnEndEvent.class); TYPE_MAP.put("assistant.usage", AssistantUsageEvent.class); TYPE_MAP.put("abort", AbortEvent.class); TYPE_MAP.put("tool.user_requested", ToolUserRequestedEvent.class); TYPE_MAP.put("tool.execution_start", ToolExecutionStartEvent.class); TYPE_MAP.put("tool.execution_partial_result", ToolExecutionPartialResultEvent.class); TYPE_MAP.put("tool.execution_progress", ToolExecutionProgressEvent.class); TYPE_MAP.put("tool.execution_complete", ToolExecutionCompleteEvent.class); TYPE_MAP.put("subagent.started", SubagentStartedEvent.class); TYPE_MAP.put("subagent.completed", SubagentCompletedEvent.class); TYPE_MAP.put("subagent.failed", SubagentFailedEvent.class); TYPE_MAP.put("subagent.selected", SubagentSelectedEvent.class); TYPE_MAP.put("hook.start", HookStartEvent.class); TYPE_MAP.put("hook.end", HookEndEvent.class); TYPE_MAP.put("system.message", SystemMessageEvent.class); } /** * Parses a JSON string into the appropriate SessionEvent subclass. * * @param json * the JSON string representing an event * @return the parsed event, or {@code null} if parsing fails or type is unknown */ public static AbstractSessionEvent parse(String json) { try { JsonNode node = MAPPER.readTree(json); String type = node.has("type") ? node.get("type").asText() : null; if (type == null) { LOG.warning("Missing 'type' field in event: " + json); return null; } Class eventClass = TYPE_MAP.get(type); if (eventClass == null) { LOG.fine("Unknown event type: " + type); return null; } return MAPPER.treeToValue(node, eventClass); } catch (Exception e) { LOG.log(Level.SEVERE, "Failed to parse session event", e); return null; } } /** * Parses a JsonNode into the appropriate SessionEvent subclass. * * @param node * the JSON node representing an event * @return the parsed event, or {@code null} if parsing fails or type is unknown */ public static AbstractSessionEvent parse(JsonNode node) { try { String type = node.has("type") ? node.get("type").asText() : null; if (type == null) { LOG.warning("Missing 'type' field in event"); return null; } Class eventClass = TYPE_MAP.get(type); if (eventClass == null) { LOG.fine("Unknown event type: " + type); return null; } return MAPPER.treeToValue(node, eventClass); } catch (Exception e) { LOG.log(Level.SEVERE, "Failed to parse session event", e); return null; } } }