Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
204e95a
fix: resolve UV path override not being detected in System Requirements
whatevertogo Jan 11, 2026
25e5d05
fix: improve uv/uvx detection robustness on macOS and Linux
whatevertogo Jan 11, 2026
cc22320
Merge branch 'CoplayDev:main' into main
whatevertogo Jan 11, 2026
8d5fa2f
refactor: unify process execution with ExecPath.TryRun and add Window…
whatevertogo Jan 11, 2026
84cee8c
fix: improve version parsing to handle both spaces and parentheses
whatevertogo Jan 11, 2026
510e631
refactor: improve platform detectors with absolute path resolution
whatevertogo Jan 11, 2026
bf41479
fix: improve error handling in PathResolverService by logging exceptions
whatevertogo Jan 11, 2026
f39857c
Remove .meta files added after fork and update .gitignore
whatevertogo Jan 11, 2026
c4be11c
Merge branch 'CoplayDev:main' into main
whatevertogo Jan 12, 2026
1832715
Update .gitignore
whatevertogo Jan 12, 2026
554ddd0
save .meta
whatevertogo Jan 12, 2026
fb5909d
refactor: unify uv/uvx naming and path detection across platforms
whatevertogo Jan 12, 2026
254125a
fix: improve validation light(uvxPathStatus) logic for UVX path overr…
whatevertogo Jan 12, 2026
f9ae5d5
refactor: streamline UV version validation and unify path detection m…
whatevertogo Jan 12, 2026
84cb9c6
fix: add type handling for Claude Code client in config JSON builder
whatevertogo Jan 12, 2026
ee33077
fix: correct command from 'uvx' to 'uv' for Python version listing in…
whatevertogo Jan 12, 2026
2c3ebcd
Merge branch 'CoplayDev:main' into main
whatevertogo Jan 14, 2026
66fe194
Merge branch 'CoplayDev:main' into main
whatevertogo Jan 15, 2026
79bce47
Merge branch 'CoplayDev:main' into main
whatevertogo Jan 16, 2026
b0eb274
feat: Add ActionTrace system for editor event tracking and replay
whatevertogo Jan 16, 2026
ec59a59
refactor: split EventStore into partial classes for better maintainab…
whatevertogo Jan 16, 2026
4e54591
fix: update stylesheet references and comment unsupported styles in A…
whatevertogo Jan 16, 2026
e599807
refactor: update comments and documentation in action trace files for…
whatevertogo Jan 16, 2026
f475bee
feat: enhance ActionTrace with layered settings, preset system, and q…
whatevertogo Jan 17, 2026
af77674
feat: optimize performance in ActionTrace by replacing LINQ with manu…
whatevertogo Jan 17, 2026
f547208
feat: enhance ActionTrace settings with Range attributes and dynamic …
whatevertogo Jan 17, 2026
44b7045
feat: add GlobalIdHelper for cross-session stable object identificati…
whatevertogo Jan 17, 2026
a86506c
fix: replace delayCall with EditorApplication.update to avoid memory …
whatevertogo Jan 18, 2026
f340636
Merge branch 'main' into feature/ActionTrace
whatevertogo Jan 18, 2026
061a821
Merge branch 'CoplayDev:main' into feature/ActionTrace
whatevertogo Jan 19, 2026
25e1253
feat: add built-in HookRegistry system with Event Args pattern
whatevertogo Jan 19, 2026
13c0bc5
fix: resolve P1 issues in ActionTrace system
whatevertogo Jan 20, 2026
05e7674
Merge branch 'CoplayDev:main' into feature/ActionTrace
whatevertogo Jan 20, 2026
1e727eb
Enhance ActionTrace Editor Window UI
whatevertogo Jan 20, 2026
2e161b7
Merge remote-tracking branch 'CoplayDev:main' into feature/ActionTrace
whatevertogo Jan 20, 2026
9b24a2e
fix: enhance event handling and error logging in ActionTrace system
whatevertogo Jan 20, 2026
4609817
refactor: improve ActionTrace system architecture and UI
whatevertogo Jan 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
450 changes: 450 additions & 0 deletions MCPForUnity/Editor/ActionTrace/Analysis/Context/ContextCompressor.cs

Large diffs are not rendered by default.

324 changes: 324 additions & 0 deletions MCPForUnity/Editor/ActionTrace/Analysis/Query/ActionTraceQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
using System;
using System.Collections.Generic;
using System.Linq;
using MCPForUnity.Editor.ActionTrace.Context;
using MCPForUnity.Editor.ActionTrace.Core;
using MCPForUnity.Editor.ActionTrace.Core.Models;
using MCPForUnity.Editor.ActionTrace.Semantics;
using UnityEngine;

namespace MCPForUnity.Editor.ActionTrace.Analysis.Query
{
/// <summary>
/// Query engine that projects events with semantic information.
/// All semantic data (importance, category, intent) is computed at query time
/// and does not modify the original events.
/// </summary>
public sealed class ActionTraceQuery
{
// Static color caches to avoid repeated Color allocations during UI rendering
private static readonly Dictionary<string, Color> EventTypeColors = new()
{
["ComponentAdded"] = new Color(0.3f, 0.8f, 0.3f),
["PropertyModified"] = new Color(0.3f, 0.6f, 0.8f),
["SelectionPropertyModified"] = new Color(0.5f, 0.8f, 0.9f),
["GameObjectCreated"] = new Color(0.8f, 0.3f, 0.8f),
["HierarchyChanged"] = new Color(0.8f, 0.8f, 0.3f),
["AINote"] = new Color(0.3f, 0.8f, 0.8f),
};

private static readonly Dictionary<string, Color?> ImportanceColors = new()
{
["critical"] = new Color(1f, 0.3f, 0.3f, 0.1f),
["high"] = new Color(1f, 0.6f, 0f, 0.08f),
["medium"] = new Color(1f, 1f, 0.3f, 0.06f),
["low"] = null,
};

private static readonly Dictionary<string, Color> ImportanceBadgeColors = new()
{
["critical"] = new Color(0.8f, 0.2f, 0.2f),
["high"] = new Color(1f, 0.5f, 0f),
["medium"] = new Color(1f, 0.8f, 0.2f),
["low"] = new Color(0.5f, 0.5f, 0.5f),
};

private readonly IEventScorer _scorer;
private readonly IEventCategorizer _categorizer;
private readonly IIntentInferrer _inferrer;

/// <summary>
/// Create a new ActionTraceQuery with optional custom semantic components.
/// If null, default implementations are used.
/// </summary>
public ActionTraceQuery(
IEventScorer scorer = null,
IEventCategorizer categorizer = null,
IIntentInferrer inferrer = null)
{
_scorer = scorer ?? new Semantics.DefaultEventScorer();
_categorizer = categorizer ?? new Semantics.DefaultCategorizer();
_inferrer = inferrer ?? new Semantics.DefaultIntentInferrer();
}

/// <summary>
/// Project events with computed semantic information.
/// Returns ActionTraceViewItem objects containing the original event plus
/// dynamically calculated importance, category, and intent.
/// </summary>
public IReadOnlyList<ActionTraceViewItem> Project(IReadOnlyList<EditorEvent> events)
{
if (events == null || events.Count == 0)
return Array.Empty<ActionTraceViewItem>();

var result = new ActionTraceViewItem[events.Count];

for (int i = 0; i < events.Count; i++)
{
var evt = events[i];

// Compute importance score
var score = _scorer.Score(evt);

// Categorize the score
var category = _categorizer.Categorize(score);

// Compute context window (5 events before and after current event) for intent inference
int contextWindow = 5;
int contextStart = Math.Max(0, i - contextWindow);
int contextEnd = Math.Min(events.Count, i + contextWindow + 1);
int contextLength = contextEnd - contextStart;

EditorEvent[] surrounding = null;
if (contextLength > 0)
{
surrounding = new EditorEvent[contextLength];

// Performance: EventStore queries are usually in chronological order (but Query returns may be descending).
// Detect order in O(1) (compare first/last sequence) and fill surrounding in chronological order if needed
bool isDescending = events.Count > 1 && events[0].Sequence > events[events.Count - 1].Sequence;

if (!isDescending)
{
for (int j = 0; j < contextLength; j++)
{
surrounding[j] = events[contextStart + j];
}
}
else
{
// events are descending (newest first), need to build surrounding in ascending order (oldest->newest)
// Fill from contextEnd-1 down to contextStart to produce ascending window
for (int j = 0; j < contextLength; j++)
{
surrounding[j] = events[contextEnd - 1 - j];
}
}
}

// Use surrounding parameter for intent inference (in chronological order)
var intent = _inferrer.Infer(evt, surrounding);

// Use EditorEvent's GetSummary() method, which automatically handles dehydrated events
var displaySummary = evt.GetSummary();
var displaySummaryLower = (displaySummary ?? string.Empty).ToLowerInvariant();
var displayTargetIdLower = (evt.TargetId ?? string.Empty).ToLowerInvariant();

// Format as local time including date: MM-dd HH:mm
var localTime = DateTimeOffset.FromUnixTimeMilliseconds(evt.TimestampUnixMs).ToLocalTime();
var displayTime = localTime.ToString("MM-dd HH:mm");
var displaySequence = evt.Sequence.ToString();

// Precompute colors
var typeColor = GetEventTypeColor(evt.Type);
var importanceColor = GetImportanceColor(category);
var importanceBadgeColor = GetImportanceBadgeColor(category);

result[i] = new ActionTraceViewItem
{
Event = evt,
ImportanceScore = score,
ImportanceCategory = category,
InferredIntent = intent,
// Set display cache
DisplaySummary = displaySummary,
DisplaySummaryLower = displaySummaryLower,
DisplayTargetIdLower = displayTargetIdLower,
DisplayTime = displayTime,
DisplaySequence = displaySequence,
TypeColor = typeColor,
ImportanceColor = importanceColor,
ImportanceBadgeColor = importanceBadgeColor
};
}

return result;
}

/// <summary>
/// Project events with context associations.
/// Overload for QueryWithContext results.
/// </summary>
public IReadOnlyList<ActionTraceViewItem> ProjectWithContext(
IReadOnlyList<(EditorEvent Event, ContextMapping Context)> eventsWithContext)
{
if (eventsWithContext == null || eventsWithContext.Count == 0)
return Array.Empty<ActionTraceViewItem>();

var result = new ActionTraceViewItem[eventsWithContext.Count];

for (int i = 0; i < eventsWithContext.Count; i++)
{
var (evt, ctx) = eventsWithContext[i];

var score = _scorer.Score(evt);
var category = _categorizer.Categorize(score);

// Use simple inference to avoid List allocation
var intent = _inferrer.Infer(evt, surrounding: null);

// Use EditorEvent's GetSummary() method, which automatically handles dehydrated events
var displaySummary = evt.GetSummary();
var displaySummaryLower = (displaySummary ?? string.Empty).ToLowerInvariant();
var displayTargetIdLower = (evt.TargetId ?? string.Empty).ToLowerInvariant();

// Format as local time including date: MM-dd HH:mm
var localTime = DateTimeOffset.FromUnixTimeMilliseconds(evt.TimestampUnixMs).ToLocalTime();
var displayTime = localTime.ToString("MM-dd HH:mm");
var displaySequence = evt.Sequence.ToString();

// Precompute colors
var typeColor = GetEventTypeColor(evt.Type);
var importanceColor = GetImportanceColor(category);
var importanceBadgeColor = GetImportanceBadgeColor(category);

result[i] = new ActionTraceViewItem
{
Event = evt,
Context = ctx,
ImportanceScore = score,
ImportanceCategory = category,
InferredIntent = intent,
// Set display cache
DisplaySummary = displaySummary,
DisplaySummaryLower = displaySummaryLower,
DisplayTargetIdLower = displayTargetIdLower,
DisplayTime = displayTime,
DisplaySequence = displaySequence,
TypeColor = typeColor,
ImportanceColor = importanceColor,
ImportanceBadgeColor = importanceBadgeColor
};
}

return result;
}

/// <summary>
/// Get event type color for display.
/// Uses cached values to avoid repeated allocations.
/// </summary>
private static Color GetEventTypeColor(string eventType)
{
return EventTypeColors.TryGetValue(eventType, out var color) ? color : Color.gray;
}

/// <summary>
/// Get importance background color (nullable).
/// Uses cached values to avoid repeated allocations.
/// </summary>
private static Color? GetImportanceColor(string category)
{
return ImportanceColors.TryGetValue(category, out var color) ? color : null;
}

/// <summary>
/// Get importance badge color.
/// Uses cached values to avoid repeated allocations.
/// </summary>
private static Color GetImportanceBadgeColor(string category)
{
return ImportanceBadgeColors.TryGetValue(category, out var color) ? color : Color.gray;
}

/// <summary>
/// A view of an event with projected semantic information.
/// This is a computed projection, not stored data.
///
/// Performance optimization: All display strings are precomputed at projection time
/// to avoid repeated allocations in OnGUI.
/// </summary>
public sealed class ActionTraceViewItem
{
/// <summary>
/// The original immutable event.
/// </summary>
public EditorEvent Event { get; set; }

/// <summary>
/// Optional context association (may be null).
/// </summary>
public ContextMapping Context { get; set; }

/// <summary>
/// Computed importance score (0.0 to 1.0).
/// Higher values indicate more important events.
/// </summary>
public float ImportanceScore { get; set; }

/// <summary>
/// Category label derived from importance score.
/// Values: "critical", "high", "medium", "low"
/// </summary>
public string ImportanceCategory { get; set; }

/// <summary>
/// Inferred user intent or purpose.
/// May be null if intent cannot be determined.
/// </summary>
public string InferredIntent { get; set; }

// ========== Display cache (avoid repeated allocations in OnGUI) ==========

/// <summary>
/// Precomputed event summary for display.
/// </summary>
public string DisplaySummary { get; set; }

/// <summary>
/// Precomputed summary in lowercase for search filtering.
/// </summary>
public string DisplaySummaryLower { get; set; }

/// <summary>
/// Precomputed target ID in lowercase for search filtering.
/// </summary>
public string DisplayTargetIdLower { get; set; }

/// <summary>
/// Precomputed formatted time (HH:mm:ss).
/// </summary>
public string DisplayTime { get; set; }

/// <summary>
/// Precomputed sequence number as string.
/// </summary>
public string DisplaySequence { get; set; }

/// <summary>
/// Precomputed event type color (avoid switch during rendering).
/// </summary>
public Color TypeColor { get; set; }

/// <summary>
/// Precomputed importance background color.
/// </summary>
public Color? ImportanceColor { get; set; }

/// <summary>
/// Precomputed importance badge color.
/// </summary>
public Color ImportanceBadgeColor { get; set; }
}
}
}
Loading