Forge
csharpc0b81713
1using System.Collections.Concurrent;
2using System.Text.Json;
3
4namespace AgentNotesMcp.Status;
5
6internal static class AgentNotesToolCallRingBuffer
7{
8 internal const int DefaultCapacity = 64;
9
10 private static readonly ConcurrentQueue<ToolCallEntry> Queue = new();
11
12 internal static void Record(
13 string toolName,
14 IReadOnlyDictionary<string, JsonElement> args,
15 string resultText,
16 bool isError,
17 long durationMs)
18 {
19 var entry = new ToolCallEntry(
20 DateTimeOffset.UtcNow,
21 toolName,
22 TryGetWorkspacePath(args),
23 isError,
24 durationMs,
25 Preview(resultText));
26
27 Queue.Enqueue(entry);
28 while (Queue.Count > DefaultCapacity && Queue.TryDequeue(out _))
29 {
30 }
31 }
32
33 internal static IReadOnlyList<ToolCallEntry> Snapshot()
34 {
35 var list = Queue.ToArray();
36 if (list.Length <= 1)
37 return list;
38
39 Array.Reverse(list);
40 return list;
41 }
42
43 internal static void ClearForTests()
44 {
45 while (Queue.TryDequeue(out _))
46 {
47 }
48 }
49
50 private static string? TryGetWorkspacePath(IReadOnlyDictionary<string, JsonElement> args)
51 {
52 if (!args.TryGetValue("workspace_path", out var el))
53 return null;
54
55 var path = el.GetString();
56 return string.IsNullOrWhiteSpace(path) ? null : path.Trim();
57 }
58
59 private static string Preview(string text)
60 {
61 if (string.IsNullOrEmpty(text))
62 return "";
63
64 var oneLine = text.Replace("\r\n", " ").Replace('\n', ' ').Replace('\r', ' ');
65 const int max = 120;
66 return oneLine.Length <= max ? oneLine : oneLine[..max] + "…";
67 }
68
69 internal sealed record ToolCallEntry(
70 DateTimeOffset AtUtc,
71 string ToolName,
72 string? WorkspacePath,
73 bool IsError,
74 long DurationMs,
75 string ResultPreview);
76}
77
View only · write via MCP/CIDE