optimus-tool-call-staging 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +194 -0
- package/dist/capture-prompt.d.ts +14 -0
- package/dist/capture-prompt.d.ts.map +1 -0
- package/dist/capture-prompt.js +173 -0
- package/dist/capture-prompt.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +165 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/endpoint-config.d.ts +2 -0
- package/dist/lib/endpoint-config.d.ts.map +1 -0
- package/dist/lib/endpoint-config.js +2 -0
- package/dist/lib/endpoint-config.js.map +1 -0
- package/dist/lib/hardware-uuid.d.ts +7 -0
- package/dist/lib/hardware-uuid.d.ts.map +1 -0
- package/dist/lib/hardware-uuid.js +52 -0
- package/dist/lib/hardware-uuid.js.map +1 -0
- package/dist/lib/machine-auth.d.ts +11 -0
- package/dist/lib/machine-auth.d.ts.map +1 -0
- package/dist/lib/machine-auth.js +46 -0
- package/dist/lib/machine-auth.js.map +1 -0
- package/dist/lib/startup-sync.d.ts +13 -0
- package/dist/lib/startup-sync.d.ts.map +1 -0
- package/dist/lib/startup-sync.js +147 -0
- package/dist/lib/startup-sync.js.map +1 -0
- package/dist/lib/streaming.d.ts +18 -0
- package/dist/lib/streaming.d.ts.map +1 -0
- package/dist/lib/streaming.js +243 -0
- package/dist/lib/streaming.js.map +1 -0
- package/dist/lib/telemetry/build-event.d.ts +10 -0
- package/dist/lib/telemetry/build-event.d.ts.map +1 -0
- package/dist/lib/telemetry/build-event.js +32 -0
- package/dist/lib/telemetry/build-event.js.map +1 -0
- package/dist/lib/telemetry/canonical.d.ts +26 -0
- package/dist/lib/telemetry/canonical.d.ts.map +1 -0
- package/dist/lib/telemetry/canonical.js +153 -0
- package/dist/lib/telemetry/canonical.js.map +1 -0
- package/dist/lib/telemetry/index.d.ts +9 -0
- package/dist/lib/telemetry/index.d.ts.map +1 -0
- package/dist/lib/telemetry/index.js +9 -0
- package/dist/lib/telemetry/index.js.map +1 -0
- package/dist/lib/telemetry/serialize.d.ts +3 -0
- package/dist/lib/telemetry/serialize.d.ts.map +1 -0
- package/dist/lib/telemetry/serialize.js +15 -0
- package/dist/lib/telemetry/serialize.js.map +1 -0
- package/dist/lib/telemetry/types.d.ts +34 -0
- package/dist/lib/telemetry/types.d.ts.map +1 -0
- package/dist/lib/telemetry/types.js +14 -0
- package/dist/lib/telemetry/types.js.map +1 -0
- package/dist/lib/telemetry/wire-metadata.d.ts +10 -0
- package/dist/lib/telemetry/wire-metadata.d.ts.map +1 -0
- package/dist/lib/telemetry/wire-metadata.js +28 -0
- package/dist/lib/telemetry/wire-metadata.js.map +1 -0
- package/dist/lib/telemetry.d.ts +2 -0
- package/dist/lib/telemetry.d.ts.map +1 -0
- package/dist/lib/telemetry.js +2 -0
- package/dist/lib/telemetry.js.map +1 -0
- package/dist/lib/user-profile.d.ts +16 -0
- package/dist/lib/user-profile.d.ts.map +1 -0
- package/dist/lib/user-profile.js +175 -0
- package/dist/lib/user-profile.js.map +1 -0
- package/dist/test/canonical.test.d.ts +2 -0
- package/dist/test/canonical.test.d.ts.map +1 -0
- package/dist/test/canonical.test.js +117 -0
- package/dist/test/canonical.test.js.map +1 -0
- package/dist/test/endpoint-config.test.d.ts +2 -0
- package/dist/test/endpoint-config.test.d.ts.map +1 -0
- package/dist/test/endpoint-config.test.js +43 -0
- package/dist/test/endpoint-config.test.js.map +1 -0
- package/dist/test/index.test.d.ts +2 -0
- package/dist/test/index.test.d.ts.map +1 -0
- package/dist/test/index.test.js +376 -0
- package/dist/test/index.test.js.map +1 -0
- package/dist/test/integration-canonical.test.d.ts +2 -0
- package/dist/test/integration-canonical.test.d.ts.map +1 -0
- package/dist/test/integration-canonical.test.js +157 -0
- package/dist/test/integration-canonical.test.js.map +1 -0
- package/dist/test/streaming.test.d.ts +2 -0
- package/dist/test/streaming.test.d.ts.map +1 -0
- package/dist/test/streaming.test.js +223 -0
- package/dist/test/streaming.test.js.map +1 -0
- package/dist/test/telemetry.test.d.ts +2 -0
- package/dist/test/telemetry.test.d.ts.map +1 -0
- package/dist/test/telemetry.test.js +201 -0
- package/dist/test/telemetry.test.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { buildTelemetryEvent } from "../lib/telemetry.js";
|
|
4
|
+
import { normalizeToCanonical } from "../lib/telemetry/canonical.js";
|
|
5
|
+
/**
|
|
6
|
+
* Integration tests verifying that Claude and Cursor produce identical
|
|
7
|
+
* telemetry events for the same tool invocations.
|
|
8
|
+
*/
|
|
9
|
+
test("Integration: Read /etc/passwd from both agents produces same event", () => {
|
|
10
|
+
// Claude hook payload
|
|
11
|
+
const claudeRaw = {
|
|
12
|
+
hook_event_name: "PreToolUse",
|
|
13
|
+
tool_name: "Read",
|
|
14
|
+
tool_input: { file_path: "/etc/passwd" },
|
|
15
|
+
session_id: "session-123",
|
|
16
|
+
cwd: "/home/user",
|
|
17
|
+
};
|
|
18
|
+
// Cursor hook payload (same action, different format)
|
|
19
|
+
const cursorRaw = {
|
|
20
|
+
toolUse: { name: "Read", input: { file_path: "/etc/passwd" } },
|
|
21
|
+
sessionId: "session-123",
|
|
22
|
+
cwd: "/home/user",
|
|
23
|
+
};
|
|
24
|
+
// Normalize both
|
|
25
|
+
const claudeCanonical = normalizeToCanonical(claudeRaw);
|
|
26
|
+
const cursorCanonical = normalizeToCanonical(cursorRaw);
|
|
27
|
+
// Build telemetry events
|
|
28
|
+
const claudeEvent = buildTelemetryEvent({
|
|
29
|
+
tool_name: claudeCanonical.tool_name,
|
|
30
|
+
tool_input: claudeCanonical.tool_input,
|
|
31
|
+
session_id: claudeCanonical.session_id,
|
|
32
|
+
cwd: claudeCanonical.cwd,
|
|
33
|
+
hook_event_name: claudeCanonical.hook_event_name,
|
|
34
|
+
raw_event: claudeCanonical.raw_event,
|
|
35
|
+
}, "claude", claudeCanonical.session_id);
|
|
36
|
+
const cursorEvent = buildTelemetryEvent({
|
|
37
|
+
tool_name: cursorCanonical.tool_name,
|
|
38
|
+
tool_input: cursorCanonical.tool_input,
|
|
39
|
+
session_id: cursorCanonical.session_id,
|
|
40
|
+
cwd: cursorCanonical.cwd,
|
|
41
|
+
hook_event_name: cursorCanonical.hook_event_name,
|
|
42
|
+
raw_event: cursorCanonical.raw_event,
|
|
43
|
+
}, "cursor", cursorCanonical.session_id);
|
|
44
|
+
// Verify critical fields match (except agent_identity which should differ)
|
|
45
|
+
assert(claudeEvent);
|
|
46
|
+
assert(cursorEvent);
|
|
47
|
+
assert.strictEqual(claudeEvent.event_type, cursorEvent.event_type);
|
|
48
|
+
assert.strictEqual(claudeEvent.session_id, cursorEvent.session_id);
|
|
49
|
+
assert.strictEqual(claudeEvent.working_directory, cursorEvent.working_directory);
|
|
50
|
+
assert.strictEqual(claudeEvent.tool_call_metadata.tool_name, cursorEvent.tool_call_metadata.tool_name);
|
|
51
|
+
assert.deepEqual(claudeEvent.tool_call_metadata.tool_input, cursorEvent.tool_call_metadata.tool_input);
|
|
52
|
+
// Agent identity should be different
|
|
53
|
+
assert.strictEqual(claudeEvent.agent_identity, "claude");
|
|
54
|
+
assert.strictEqual(cursorEvent.agent_identity, "cursor");
|
|
55
|
+
});
|
|
56
|
+
test("Integration: Dangerous shell command from both agents", () => {
|
|
57
|
+
// Claude preToolUse with bash command
|
|
58
|
+
const claudeRaw = {
|
|
59
|
+
hook_event_name: "PreToolUse",
|
|
60
|
+
tool_name: "Bash",
|
|
61
|
+
tool_input: { command: "rm -rf /var/log" },
|
|
62
|
+
user_prompt: "clean up logs",
|
|
63
|
+
session_id: "claude-sess",
|
|
64
|
+
};
|
|
65
|
+
// Cursor preToolUse with bash command (different field names)
|
|
66
|
+
const cursorRaw = {
|
|
67
|
+
toolUse: {
|
|
68
|
+
name: "Bash",
|
|
69
|
+
input: { command: "rm -rf /var/log" },
|
|
70
|
+
},
|
|
71
|
+
user_prompt: "clean up logs",
|
|
72
|
+
sessionId: "cursor-sess",
|
|
73
|
+
};
|
|
74
|
+
const claudeCanonical = normalizeToCanonical(claudeRaw);
|
|
75
|
+
const cursorCanonical = normalizeToCanonical(cursorRaw);
|
|
76
|
+
// Tool information should be identical
|
|
77
|
+
assert.strictEqual(claudeCanonical.tool_name, cursorCanonical.tool_name);
|
|
78
|
+
assert.strictEqual(claudeCanonical.tool_name, "Bash");
|
|
79
|
+
assert.deepEqual(claudeCanonical.tool_input.command, cursorCanonical.tool_input.command);
|
|
80
|
+
assert.strictEqual(claudeCanonical.user_prompt, cursorCanonical.user_prompt);
|
|
81
|
+
// But agents should be detected correctly
|
|
82
|
+
assert.strictEqual(claudeCanonical.agent, "claude");
|
|
83
|
+
assert.strictEqual(cursorCanonical.agent, "cursor");
|
|
84
|
+
});
|
|
85
|
+
test("Integration: Web operations from both agents", () => {
|
|
86
|
+
const claudeRaw = {
|
|
87
|
+
hook_event_name: "PreToolUse",
|
|
88
|
+
tool_name: "WebFetch",
|
|
89
|
+
tool_input: { url: "https://api.github.com/user", prompt: "check auth" },
|
|
90
|
+
user_prompt: "verify github access",
|
|
91
|
+
};
|
|
92
|
+
const cursorRaw = {
|
|
93
|
+
toolName: "WebFetch",
|
|
94
|
+
toolInput: { url: "https://api.github.com/user", prompt: "check auth" },
|
|
95
|
+
userPrompt: "verify github access",
|
|
96
|
+
};
|
|
97
|
+
const claudeCanonical = normalizeToCanonical(claudeRaw);
|
|
98
|
+
const cursorCanonical = normalizeToCanonical(cursorRaw);
|
|
99
|
+
assert.strictEqual(claudeCanonical.tool_name, "WebFetch");
|
|
100
|
+
assert.strictEqual(cursorCanonical.tool_name, "WebFetch");
|
|
101
|
+
assert.deepEqual(claudeCanonical.tool_input, cursorCanonical.tool_input);
|
|
102
|
+
});
|
|
103
|
+
test("Integration: MCP tool invocations from both agents", () => {
|
|
104
|
+
const claudeRaw = {
|
|
105
|
+
hook_event_name: "PreToolUse",
|
|
106
|
+
tool_name: "mcp__sequential_thinking__think",
|
|
107
|
+
tool_input: { thought: "this is complex" },
|
|
108
|
+
};
|
|
109
|
+
const cursorRaw = {
|
|
110
|
+
toolName: "mcp__sequential_thinking__think",
|
|
111
|
+
toolInput: { thought: "this is complex" },
|
|
112
|
+
};
|
|
113
|
+
const claudeCanonical = normalizeToCanonical(claudeRaw);
|
|
114
|
+
const cursorCanonical = normalizeToCanonical(cursorRaw);
|
|
115
|
+
assert.strictEqual(claudeCanonical.tool_name, cursorCanonical.tool_name);
|
|
116
|
+
assert.deepEqual(claudeCanonical.tool_input, cursorCanonical.tool_input);
|
|
117
|
+
});
|
|
118
|
+
test("Integration: Cursor beforeShellExecution maps to same canonical as Claude preToolUse Bash", () => {
|
|
119
|
+
// Claude preToolUse with Bash
|
|
120
|
+
const claudeRaw = {
|
|
121
|
+
hook_event_name: "PreToolUse",
|
|
122
|
+
tool_name: "Bash",
|
|
123
|
+
tool_input: { command: "curl -X GET https://api.example.com" },
|
|
124
|
+
};
|
|
125
|
+
// Cursor beforeShellExecution with same command
|
|
126
|
+
const cursorRaw = {
|
|
127
|
+
command: "curl -X GET https://api.example.com",
|
|
128
|
+
};
|
|
129
|
+
const claudeCanonical = normalizeToCanonical(claudeRaw);
|
|
130
|
+
const cursorCanonical = normalizeToCanonical(cursorRaw);
|
|
131
|
+
// Both should identify as shell operations
|
|
132
|
+
assert.strictEqual(claudeCanonical.tool_name, "Bash");
|
|
133
|
+
assert.strictEqual(cursorCanonical.tool_name, "Bash");
|
|
134
|
+
// Tool input should be normalized the same way
|
|
135
|
+
assert.deepEqual(claudeCanonical.tool_input, cursorCanonical.tool_input);
|
|
136
|
+
assert.strictEqual(claudeCanonical.tool_input.command, "curl -X GET https://api.example.com");
|
|
137
|
+
});
|
|
138
|
+
test("Integration: Write operations with sensitivity", () => {
|
|
139
|
+
const claudeRaw = {
|
|
140
|
+
hook_event_name: "PreToolUse",
|
|
141
|
+
tool_name: "Write",
|
|
142
|
+
tool_input: { file_path: "~/.ssh/authorized_keys" },
|
|
143
|
+
user_prompt: "add SSH key",
|
|
144
|
+
};
|
|
145
|
+
const cursorRaw = {
|
|
146
|
+
tool: { name: "Write", input: { file_path: "~/.ssh/authorized_keys" } },
|
|
147
|
+
userPrompt: "add SSH key",
|
|
148
|
+
};
|
|
149
|
+
const claudeCanonical = normalizeToCanonical(claudeRaw);
|
|
150
|
+
const cursorCanonical = normalizeToCanonical(cursorRaw);
|
|
151
|
+
// Sensitive path should be preserved identically
|
|
152
|
+
assert.strictEqual(claudeCanonical.tool_name, "Write");
|
|
153
|
+
assert.strictEqual(cursorCanonical.tool_name, "Write");
|
|
154
|
+
assert.deepEqual(claudeCanonical.tool_input, cursorCanonical.tool_input);
|
|
155
|
+
assert.strictEqual(claudeCanonical.tool_input.file_path, "~/.ssh/authorized_keys");
|
|
156
|
+
});
|
|
157
|
+
//# sourceMappingURL=integration-canonical.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration-canonical.test.js","sourceRoot":"","sources":["../../src/test/integration-canonical.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streaming.test.d.ts","sourceRoot":"","sources":["../../src/test/streaming.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { queueEvent, flushEvents, ensureShutdown } from "../lib/streaming.js";
|
|
4
|
+
// Note: These tests are basic structure tests. Full integration tests with actual HTTP
|
|
5
|
+
// would require mocking fetch/http, which is done in integration tests.
|
|
6
|
+
// Helper to create a mock event
|
|
7
|
+
function createMockEvent() {
|
|
8
|
+
return {
|
|
9
|
+
timestamp: new Date().toISOString(),
|
|
10
|
+
agent_identity: "test-agent",
|
|
11
|
+
event_type: "tool_call",
|
|
12
|
+
session_id: "test-session",
|
|
13
|
+
tool_use_id: "test-tool-use",
|
|
14
|
+
hook_event_name: "preToolUse",
|
|
15
|
+
working_directory: "/tmp",
|
|
16
|
+
tool_call_metadata: {
|
|
17
|
+
tool_name: "Bash",
|
|
18
|
+
tool_input: { command: "ls -la" },
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
test("Event object structure - has required fields", () => {
|
|
23
|
+
const event = createMockEvent();
|
|
24
|
+
assert.strictEqual(typeof event.timestamp, "string");
|
|
25
|
+
assert.strictEqual(event.agent_identity, "test-agent");
|
|
26
|
+
assert.strictEqual(event.event_type, "tool_call");
|
|
27
|
+
assert(event.tool_call_metadata);
|
|
28
|
+
assert.strictEqual(event.tool_call_metadata.tool_name, "Bash");
|
|
29
|
+
assert.strictEqual(event.tool_call_metadata.tool_input?.command, "ls -la");
|
|
30
|
+
});
|
|
31
|
+
test("Event object - metadata structure", () => {
|
|
32
|
+
const event = createMockEvent();
|
|
33
|
+
const meta = event.tool_call_metadata;
|
|
34
|
+
assert.strictEqual(typeof meta.tool_name, "string");
|
|
35
|
+
assert(meta.tool_input);
|
|
36
|
+
});
|
|
37
|
+
test("Event object - optional fields can be null", () => {
|
|
38
|
+
const event = {
|
|
39
|
+
timestamp: new Date().toISOString(),
|
|
40
|
+
agent_identity: "test",
|
|
41
|
+
event_type: "tool_call",
|
|
42
|
+
tool_call_metadata: {
|
|
43
|
+
tool_name: "Read",
|
|
44
|
+
tool_input: { file_path: "/z" },
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
assert.strictEqual(event.session_id, undefined);
|
|
48
|
+
assert.strictEqual(event.tool_use_id, undefined);
|
|
49
|
+
});
|
|
50
|
+
test("Event serialization - JSON.stringify works", () => {
|
|
51
|
+
const event = createMockEvent();
|
|
52
|
+
const json = JSON.stringify(event);
|
|
53
|
+
assert(typeof json === "string");
|
|
54
|
+
const parsed = JSON.parse(json);
|
|
55
|
+
assert.strictEqual(parsed.agent_identity, "test-agent");
|
|
56
|
+
});
|
|
57
|
+
test("Event batch structure", () => {
|
|
58
|
+
const events = [createMockEvent(), createMockEvent(), createMockEvent()];
|
|
59
|
+
const batch = { events };
|
|
60
|
+
assert.strictEqual(batch.events.length, 3);
|
|
61
|
+
const json = JSON.stringify(batch);
|
|
62
|
+
const parsed = JSON.parse(json);
|
|
63
|
+
assert.strictEqual(parsed.events.length, 3);
|
|
64
|
+
});
|
|
65
|
+
test("MCP event - tool_name carries server; no client-side mcp split", () => {
|
|
66
|
+
const event = {
|
|
67
|
+
timestamp: new Date().toISOString(),
|
|
68
|
+
agent_identity: "claude",
|
|
69
|
+
event_type: "tool_call",
|
|
70
|
+
tool_call_metadata: {
|
|
71
|
+
tool_name: "mcp__filesystem__read_file",
|
|
72
|
+
tool_input: { path: "/" },
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
assert.strictEqual(event.tool_call_metadata.tool_name, "mcp__filesystem__read_file");
|
|
76
|
+
});
|
|
77
|
+
test("Event - excludes undefined nested fields in serialization", () => {
|
|
78
|
+
const event = {
|
|
79
|
+
timestamp: new Date().toISOString(),
|
|
80
|
+
agent_identity: "test",
|
|
81
|
+
event_type: "tool_call",
|
|
82
|
+
tool_call_metadata: {
|
|
83
|
+
tool_name: "Read",
|
|
84
|
+
tool_input: { file_path: "/y" },
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
const json = JSON.stringify(event);
|
|
88
|
+
const parsed = JSON.parse(json);
|
|
89
|
+
// undefined fields should not appear in JSON
|
|
90
|
+
assert(!("session_id" in parsed) || parsed.session_id === undefined);
|
|
91
|
+
});
|
|
92
|
+
test("Event with all optional fields", () => {
|
|
93
|
+
const event = {
|
|
94
|
+
timestamp: "2024-01-01T00:00:00Z",
|
|
95
|
+
agent_identity: "claude",
|
|
96
|
+
event_type: "tool_call",
|
|
97
|
+
session_id: "session-123",
|
|
98
|
+
tool_use_id: "tooluse-456",
|
|
99
|
+
hook_event_name: "preToolUse",
|
|
100
|
+
working_directory: "/home/user/project",
|
|
101
|
+
tool_call_metadata: {
|
|
102
|
+
tool_name: "Bash",
|
|
103
|
+
tool_input: { command: "git push --force" },
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
assert.strictEqual(event.session_id, "session-123");
|
|
107
|
+
assert.strictEqual(event.working_directory, "/home/user/project");
|
|
108
|
+
assert.strictEqual(event.tool_call_metadata.tool_input?.command, "git push --force");
|
|
109
|
+
});
|
|
110
|
+
test("Event - different agent identities", () => {
|
|
111
|
+
const agents = ["claude", "cursor", "copilot", "unknown"];
|
|
112
|
+
for (const agent of agents) {
|
|
113
|
+
const event = {
|
|
114
|
+
timestamp: new Date().toISOString(),
|
|
115
|
+
agent_identity: agent,
|
|
116
|
+
event_type: "tool_call",
|
|
117
|
+
tool_call_metadata: {
|
|
118
|
+
tool_name: "Read",
|
|
119
|
+
tool_input: { file_path: "/a" },
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
assert.strictEqual(event.agent_identity, agent);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
test("Batch payload validation", () => {
|
|
126
|
+
const events = Array.from({ length: 50 }, (_, i) => ({
|
|
127
|
+
...createMockEvent(),
|
|
128
|
+
tool_use_id: `tool-${i}`,
|
|
129
|
+
}));
|
|
130
|
+
const batch = { events };
|
|
131
|
+
assert.strictEqual(batch.events.length, 50);
|
|
132
|
+
// All should be serializable
|
|
133
|
+
const json = JSON.stringify(batch);
|
|
134
|
+
assert(json.length > 0);
|
|
135
|
+
const parsed = JSON.parse(json);
|
|
136
|
+
assert.strictEqual(parsed.events.length, 50);
|
|
137
|
+
});
|
|
138
|
+
test("Event timestamp format", () => {
|
|
139
|
+
const event = createMockEvent();
|
|
140
|
+
const timestamp = new Date(event.timestamp);
|
|
141
|
+
assert(!isNaN(timestamp.getTime()));
|
|
142
|
+
assert(event.timestamp.includes("T"));
|
|
143
|
+
assert(event.timestamp.includes("Z") || event.timestamp.includes("+"));
|
|
144
|
+
});
|
|
145
|
+
test("Tool input preservation", () => {
|
|
146
|
+
const toolInput = {
|
|
147
|
+
file_path: "/tmp/test.txt",
|
|
148
|
+
custom_field: "custom_value",
|
|
149
|
+
nested: { key: "value" },
|
|
150
|
+
};
|
|
151
|
+
const event = {
|
|
152
|
+
timestamp: new Date().toISOString(),
|
|
153
|
+
agent_identity: "test",
|
|
154
|
+
event_type: "tool_call",
|
|
155
|
+
tool_call_metadata: {
|
|
156
|
+
tool_name: "Read",
|
|
157
|
+
tool_input: toolInput,
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
assert.strictEqual(event.tool_call_metadata.tool_input.file_path, "/tmp/test.txt");
|
|
161
|
+
assert.strictEqual(event.tool_call_metadata.tool_input.custom_field, "custom_value");
|
|
162
|
+
assert.deepStrictEqual(event.tool_call_metadata.tool_input.nested, { key: "value" });
|
|
163
|
+
});
|
|
164
|
+
// === Tests for ensureShutdown() graceful shutdown behavior (Devin comment fix) ===
|
|
165
|
+
// Documentation: https://github.com/devin-ai-integration/...
|
|
166
|
+
//
|
|
167
|
+
// The original ensureShutdown() only cleared the batch timer but never flushed
|
|
168
|
+
// queued events, causing silent data loss when BATCH_SIZE > 1 (production config).
|
|
169
|
+
// The fix uses beforeExit to allow async flush and handles SIGTERM/SIGINT signals.
|
|
170
|
+
test("ensureShutdown - function exists and is callable", () => {
|
|
171
|
+
// Basic smoke test to ensure the function is properly exported
|
|
172
|
+
assert.strictEqual(typeof ensureShutdown, "function");
|
|
173
|
+
});
|
|
174
|
+
test("ensureShutdown - should not throw on call", () => {
|
|
175
|
+
// Verify the function can be called without errors
|
|
176
|
+
assert.doesNotThrow(() => {
|
|
177
|
+
// Note: We can only call this once per process safely
|
|
178
|
+
// In real usage, it's called during module initialization
|
|
179
|
+
ensureShutdown();
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
test("flushEvents - function exists and is callable", async () => {
|
|
183
|
+
// Verify flushEvents is properly exported for testing
|
|
184
|
+
assert.strictEqual(typeof flushEvents, "function");
|
|
185
|
+
// Should handle empty queue gracefully
|
|
186
|
+
await assert.doesNotReject(async () => {
|
|
187
|
+
await flushEvents();
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
test("queueEvent - function exists and is callable", async () => {
|
|
191
|
+
// Verify queueEvent is properly exported
|
|
192
|
+
assert.strictEqual(typeof queueEvent, "function");
|
|
193
|
+
});
|
|
194
|
+
test("streaming - verifies fix addresses Devin comment about data loss", () => {
|
|
195
|
+
// This test documents the fix for Devin's code review:
|
|
196
|
+
//
|
|
197
|
+
// Original issue:
|
|
198
|
+
// - ensureShutdown() only cleared batch timer, never flushed events
|
|
199
|
+
// - process.on('exit') doesn't support async operations
|
|
200
|
+
// - With BATCH_SIZE > 1, events were silently lost
|
|
201
|
+
//
|
|
202
|
+
// Solution:
|
|
203
|
+
// - Changed to process.on('beforeExit') which supports async
|
|
204
|
+
// - Added SIGTERM/SIGINT handlers for graceful shutdown
|
|
205
|
+
// - Actually calls flushEvents() to prevent data loss
|
|
206
|
+
const sourceCode = ensureShutdown.toString();
|
|
207
|
+
// Verify the fix is in place
|
|
208
|
+
assert.match(sourceCode, /beforeExit/, "Should use beforeExit to allow async flush");
|
|
209
|
+
assert.match(sourceCode, /flushEvents/, "Should call flushEvents() not just clear timer");
|
|
210
|
+
assert.match(sourceCode, /SIGTERM|SIGINT/, "Should handle graceful shutdown signals");
|
|
211
|
+
});
|
|
212
|
+
test("queueEvent + flushEvents - can queue and flush events", async () => {
|
|
213
|
+
// Integration test: verify events can be queued without crashing
|
|
214
|
+
// (actual HTTP send would be mocked in integration tests)
|
|
215
|
+
const event = createMockEvent();
|
|
216
|
+
assert.doesNotThrow(() => {
|
|
217
|
+
// Queue should not throw (it's async but we're testing non-throwing behavior)
|
|
218
|
+
queueEvent(event).catch(() => {
|
|
219
|
+
// Ignore network errors in this basic test
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
//# sourceMappingURL=streaming.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"streaming.test.js","sourceRoot":"","sources":["../../src/test/streaming.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.test.d.ts","sourceRoot":"","sources":["../../src/test/telemetry.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert";
|
|
3
|
+
import { buildTelemetryEvent, eventToDict } from "../lib/telemetry.js";
|
|
4
|
+
test("buildTelemetryEvent - null input returns null", () => {
|
|
5
|
+
assert.strictEqual(buildTelemetryEvent(null), null);
|
|
6
|
+
assert.strictEqual(buildTelemetryEvent(undefined), null);
|
|
7
|
+
});
|
|
8
|
+
test("buildTelemetryEvent - filesystem read sends raw fields (classification is server-side)", () => {
|
|
9
|
+
const inv = {
|
|
10
|
+
tool_name: "Read",
|
|
11
|
+
tool_input: { file_path: "/etc/passwd" },
|
|
12
|
+
};
|
|
13
|
+
const event = buildTelemetryEvent(inv, "test-agent");
|
|
14
|
+
assert(event);
|
|
15
|
+
assert.strictEqual(event.agent_identity, "test-agent");
|
|
16
|
+
assert.strictEqual(event.event_type, "tool_call");
|
|
17
|
+
assert.strictEqual(event.tool_call_metadata.tool_name, "Read");
|
|
18
|
+
assert.strictEqual(event.tool_call_metadata.tool_input?.file_path, "/etc/passwd");
|
|
19
|
+
});
|
|
20
|
+
test("buildTelemetryEvent - filesystem write raw", () => {
|
|
21
|
+
const inv = {
|
|
22
|
+
tool_name: "Write",
|
|
23
|
+
tool_input: { file_path: "/tmp/test.txt" },
|
|
24
|
+
};
|
|
25
|
+
const event = buildTelemetryEvent(inv, "claude");
|
|
26
|
+
assert(event);
|
|
27
|
+
assert.strictEqual(event.tool_call_metadata.tool_name, "Write");
|
|
28
|
+
});
|
|
29
|
+
test("buildTelemetryEvent - filesystem edit raw", () => {
|
|
30
|
+
const inv = {
|
|
31
|
+
tool_name: "Edit",
|
|
32
|
+
tool_input: { file_path: "/tmp/edit.txt" },
|
|
33
|
+
};
|
|
34
|
+
const event = buildTelemetryEvent(inv, "claude");
|
|
35
|
+
assert(event);
|
|
36
|
+
});
|
|
37
|
+
test("buildTelemetryEvent - sensitive path still raw on client", () => {
|
|
38
|
+
const inv = {
|
|
39
|
+
tool_name: "Read",
|
|
40
|
+
tool_input: { file_path: "~/.ssh/id_rsa" },
|
|
41
|
+
};
|
|
42
|
+
const event = buildTelemetryEvent(inv, "claude");
|
|
43
|
+
assert(event);
|
|
44
|
+
});
|
|
45
|
+
test("buildTelemetryEvent - grep credential pattern raw", () => {
|
|
46
|
+
const inv = {
|
|
47
|
+
tool_name: "Grep",
|
|
48
|
+
tool_input: { pattern: "password" },
|
|
49
|
+
};
|
|
50
|
+
const event = buildTelemetryEvent(inv, "claude");
|
|
51
|
+
assert(event);
|
|
52
|
+
assert.strictEqual(event.tool_call_metadata.tool_input?.pattern, "password");
|
|
53
|
+
});
|
|
54
|
+
test("buildTelemetryEvent - webfetch raw", () => {
|
|
55
|
+
const inv = {
|
|
56
|
+
tool_name: "WebFetch",
|
|
57
|
+
tool_input: { url: "https://example.com" },
|
|
58
|
+
};
|
|
59
|
+
const event = buildTelemetryEvent(inv, "claude");
|
|
60
|
+
assert(event);
|
|
61
|
+
});
|
|
62
|
+
test("buildTelemetryEvent - websearch raw", () => {
|
|
63
|
+
const inv = {
|
|
64
|
+
tool_name: "WebSearch",
|
|
65
|
+
tool_input: { query: "security" },
|
|
66
|
+
};
|
|
67
|
+
const event = buildTelemetryEvent(inv, "claude");
|
|
68
|
+
assert(event);
|
|
69
|
+
});
|
|
70
|
+
test("buildTelemetryEvent - shell passes command for server tokenization", () => {
|
|
71
|
+
const inv = {
|
|
72
|
+
tool_name: "Shell",
|
|
73
|
+
tool_input: { command: "git push origin main" },
|
|
74
|
+
};
|
|
75
|
+
const event = buildTelemetryEvent(inv, "claude");
|
|
76
|
+
assert(event);
|
|
77
|
+
assert.strictEqual(event.tool_call_metadata.tool_input?.command, "git push origin main");
|
|
78
|
+
});
|
|
79
|
+
test("buildTelemetryEvent - bash command raw", () => {
|
|
80
|
+
const inv = {
|
|
81
|
+
tool_name: "Bash",
|
|
82
|
+
tool_input: { command: "rm -rf /tmp/test" },
|
|
83
|
+
};
|
|
84
|
+
const event = buildTelemetryEvent(inv, "claude");
|
|
85
|
+
assert(event);
|
|
86
|
+
assert.strictEqual(event.tool_call_metadata.tool_input?.command, "rm -rf /tmp/test");
|
|
87
|
+
});
|
|
88
|
+
test("buildTelemetryEvent - mcp tool name is raw only (no mcp_server / mcp_tool on client)", () => {
|
|
89
|
+
const inv = {
|
|
90
|
+
tool_name: "mcp__filesystem__read_file",
|
|
91
|
+
tool_input: { path: "/etc/hosts" },
|
|
92
|
+
};
|
|
93
|
+
const event = buildTelemetryEvent(inv, "claude");
|
|
94
|
+
assert(event);
|
|
95
|
+
assert.strictEqual(event.tool_call_metadata.tool_name, "mcp__filesystem__read_file");
|
|
96
|
+
assert.strictEqual(event.tool_call_metadata.tool_input?.path, "/etc/hosts");
|
|
97
|
+
});
|
|
98
|
+
test("buildTelemetryEvent - non-filesystem mcp tool raw", () => {
|
|
99
|
+
const inv = {
|
|
100
|
+
tool_name: "mcp__sequential_thinking__think",
|
|
101
|
+
tool_input: { prompt: "reason about this" },
|
|
102
|
+
};
|
|
103
|
+
const event = buildTelemetryEvent(inv, "claude");
|
|
104
|
+
assert(event);
|
|
105
|
+
assert.strictEqual(event.tool_call_metadata.tool_name, "mcp__sequential_thinking__think");
|
|
106
|
+
});
|
|
107
|
+
test("buildTelemetryEvent - preserves session id and tool use id", () => {
|
|
108
|
+
const inv = {
|
|
109
|
+
tool_name: "Read",
|
|
110
|
+
tool_input: { file_path: "/tmp/test" },
|
|
111
|
+
tool_use_id: "tooluse_123",
|
|
112
|
+
cwd: "/home/user",
|
|
113
|
+
hook_event_name: "preToolUse",
|
|
114
|
+
};
|
|
115
|
+
const event = buildTelemetryEvent(inv, "cursor", "session_abc");
|
|
116
|
+
assert(event);
|
|
117
|
+
assert.strictEqual(event.session_id, "session_abc");
|
|
118
|
+
assert.strictEqual(event.tool_use_id, "tooluse_123");
|
|
119
|
+
assert.strictEqual(event.working_directory, "/home/user");
|
|
120
|
+
assert.strictEqual(event.hook_event_name, "preToolUse");
|
|
121
|
+
});
|
|
122
|
+
test("eventToDict - converts event to dictionary", () => {
|
|
123
|
+
const inv = {
|
|
124
|
+
tool_name: "Read",
|
|
125
|
+
tool_input: { file_path: "/etc/hosts" },
|
|
126
|
+
};
|
|
127
|
+
const event = buildTelemetryEvent(inv, "test-agent");
|
|
128
|
+
assert(event);
|
|
129
|
+
const dict = eventToDict(event);
|
|
130
|
+
assert.strictEqual(dict.agent_identity, "test-agent");
|
|
131
|
+
assert.strictEqual(dict.event_type, "tool_call");
|
|
132
|
+
assert.strictEqual(dict.tool_name, "Read");
|
|
133
|
+
assert.deepStrictEqual(dict.tool_input, { file_path: "/etc/hosts" });
|
|
134
|
+
assert.strictEqual("action_type" in dict, false);
|
|
135
|
+
});
|
|
136
|
+
test("buildTelemetryEvent - unknown tool raw", () => {
|
|
137
|
+
const inv = {
|
|
138
|
+
tool_name: "CustomUnknownTool",
|
|
139
|
+
tool_input: {},
|
|
140
|
+
};
|
|
141
|
+
const event = buildTelemetryEvent(inv, "claude");
|
|
142
|
+
assert(event);
|
|
143
|
+
});
|
|
144
|
+
test("buildTelemetryEvent - timestamp is ISO string", () => {
|
|
145
|
+
const inv = {
|
|
146
|
+
tool_name: "Read",
|
|
147
|
+
tool_input: { file_path: "/tmp" },
|
|
148
|
+
};
|
|
149
|
+
const event = buildTelemetryEvent(inv);
|
|
150
|
+
assert(event);
|
|
151
|
+
assert(typeof event.timestamp === "string");
|
|
152
|
+
const parsed = new Date(event.timestamp);
|
|
153
|
+
assert(!isNaN(parsed.getTime()));
|
|
154
|
+
});
|
|
155
|
+
test("buildTelemetryEvent - normalizes tool name casing", () => {
|
|
156
|
+
const inv = {
|
|
157
|
+
tool_name: "read",
|
|
158
|
+
tool_input: { file_path: "/tmp" },
|
|
159
|
+
};
|
|
160
|
+
const event = buildTelemetryEvent(inv, "claude");
|
|
161
|
+
assert(event);
|
|
162
|
+
assert.strictEqual(event.tool_call_metadata.tool_name, "read");
|
|
163
|
+
});
|
|
164
|
+
test("buildTelemetryEvent - passes raw_event for server shell command resolution", () => {
|
|
165
|
+
const inv = {
|
|
166
|
+
tool_name: "Bash",
|
|
167
|
+
tool_input: {},
|
|
168
|
+
raw_event: { command: "ls -la /tmp" },
|
|
169
|
+
};
|
|
170
|
+
const event = buildTelemetryEvent(inv);
|
|
171
|
+
assert(event);
|
|
172
|
+
assert(event.tool_call_metadata.raw_event);
|
|
173
|
+
assert.strictEqual(event.tool_call_metadata.raw_event.command, "ls -la /tmp");
|
|
174
|
+
});
|
|
175
|
+
test("buildTelemetryEvent - handles cmd alias for command", () => {
|
|
176
|
+
const inv = {
|
|
177
|
+
tool_name: "Bash",
|
|
178
|
+
tool_input: { cmd: "npm install" },
|
|
179
|
+
};
|
|
180
|
+
const event = buildTelemetryEvent(inv);
|
|
181
|
+
assert(event);
|
|
182
|
+
assert.strictEqual(event.tool_call_metadata.tool_input?.cmd, "npm install");
|
|
183
|
+
});
|
|
184
|
+
test("buildTelemetryEvent - glob sensitive path raw", () => {
|
|
185
|
+
const inv = {
|
|
186
|
+
tool_name: "Glob",
|
|
187
|
+
tool_input: { path: "/home/user/.ssh/" },
|
|
188
|
+
};
|
|
189
|
+
const event = buildTelemetryEvent(inv);
|
|
190
|
+
assert(event);
|
|
191
|
+
assert.strictEqual(event.tool_call_metadata.tool_input?.path, "/home/user/.ssh/");
|
|
192
|
+
});
|
|
193
|
+
test("buildTelemetryEvent - glob normal path raw", () => {
|
|
194
|
+
const inv = {
|
|
195
|
+
tool_name: "Glob",
|
|
196
|
+
tool_input: { path: "/tmp/files" },
|
|
197
|
+
};
|
|
198
|
+
const event = buildTelemetryEvent(inv);
|
|
199
|
+
assert(event);
|
|
200
|
+
});
|
|
201
|
+
//# sourceMappingURL=telemetry.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"telemetry.test.js","sourceRoot":"","sources":["../../src/test/telemetry.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvE,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;IACzD,MAAM,CAAC,WAAW,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;IACpD,MAAM,CAAC,WAAW,CAAC,mBAAmB,CAAC,SAAS,CAAC,EAAE,IAAI,CAAC,CAAC;AAC3D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,wFAAwF,EAAE,GAAG,EAAE;IAClG,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,MAAM;QACjB,UAAU,EAAE,EAAE,SAAS,EAAE,aAAa,EAAE;KACzC,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IACrD,MAAM,CAAC,KAAK,CAAC,CAAC;IACd,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;IACvD,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IAClD,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,kBAAkB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC/D,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,kBAAkB,CAAC,UAAU,EAAE,SAAS,EAAE,aAAa,CAAC,CAAC;AACpF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,4CAA4C,EAAE,GAAG,EAAE;IACtD,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,OAAO;QAClB,UAAU,EAAE,EAAE,SAAS,EAAE,eAAe,EAAE;KAC3C,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,CAAC,CAAC;IACd,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,kBAAkB,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;AAClE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2CAA2C,EAAE,GAAG,EAAE;IACrD,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,MAAM;QACjB,UAAU,EAAE,EAAE,SAAS,EAAE,eAAe,EAAE;KAC3C,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,CAAC,CAAC;AAChB,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,0DAA0D,EAAE,GAAG,EAAE;IACpE,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,MAAM;QACjB,UAAU,EAAE,EAAE,SAAS,EAAE,eAAe,EAAE;KAC3C,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,CAAC,CAAC;AAChB,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,mDAAmD,EAAE,GAAG,EAAE;IAC7D,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,MAAM;QACjB,UAAU,EAAE,EAAE,OAAO,EAAE,UAAU,EAAE;KACpC,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,CAAC,CAAC;IACd,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,kBAAkB,CAAC,UAAU,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;AAC/E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,oCAAoC,EAAE,GAAG,EAAE;IAC9C,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,UAAU;QACrB,UAAU,EAAE,EAAE,GAAG,EAAE,qBAAqB,EAAE;KAC3C,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,CAAC,CAAC;AAChB,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,qCAAqC,EAAE,GAAG,EAAE;IAC/C,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,WAAW;QACtB,UAAU,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE;KAClC,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,CAAC,CAAC;AAChB,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,oEAAoE,EAAE,GAAG,EAAE;IAC9E,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,OAAO;QAClB,UAAU,EAAE,EAAE,OAAO,EAAE,sBAAsB,EAAE;KAChD,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,CAAC,CAAC;IACd,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,kBAAkB,CAAC,UAAU,EAAE,OAAO,EAAE,sBAAsB,CAAC,CAAC;AAC3F,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,wCAAwC,EAAE,GAAG,EAAE;IAClD,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,MAAM;QACjB,UAAU,EAAE,EAAE,OAAO,EAAE,kBAAkB,EAAE;KAC5C,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,CAAC,CAAC;IACd,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,kBAAkB,CAAC,UAAU,EAAE,OAAO,EAAE,kBAAkB,CAAC,CAAC;AACvF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sFAAsF,EAAE,GAAG,EAAE;IAChG,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,4BAA4B;QACvC,UAAU,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE;KACnC,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,CAAC,CAAC;IACd,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,kBAAkB,CAAC,SAAS,EAAE,4BAA4B,CAAC,CAAC;IACrF,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,kBAAkB,CAAC,UAAU,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC;AAC9E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,mDAAmD,EAAE,GAAG,EAAE;IAC7D,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,iCAAiC;QAC5C,UAAU,EAAE,EAAE,MAAM,EAAE,mBAAmB,EAAE;KAC5C,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,CAAC,CAAC;IACd,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,kBAAkB,CAAC,SAAS,EAAE,iCAAiC,CAAC,CAAC;AAC5F,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,4DAA4D,EAAE,GAAG,EAAE;IACtE,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,MAAM;QACjB,UAAU,EAAE,EAAE,SAAS,EAAE,WAAW,EAAE;QACtC,WAAW,EAAE,aAAa;QAC1B,GAAG,EAAE,YAAY;QACjB,eAAe,EAAE,YAAY;KAC9B,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;IAChE,MAAM,CAAC,KAAK,CAAC,CAAC;IACd,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;IACpD,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;IACrD,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAC;IAC1D,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC;AAC1D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,4CAA4C,EAAE,GAAG,EAAE;IACtD,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,MAAM;QACjB,UAAU,EAAE,EAAE,SAAS,EAAE,YAAY,EAAE;KACxC,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IACrD,MAAM,CAAC,KAAK,CAAC,CAAC;IACd,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAChC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;IACtD,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,CAAC;IACjD,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IAC3C,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,CAAC;IACrE,MAAM,CAAC,WAAW,CAAC,aAAa,IAAI,IAAI,EAAE,KAAK,CAAC,CAAC;AACnD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,wCAAwC,EAAE,GAAG,EAAE;IAClD,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,mBAAmB;QAC9B,UAAU,EAAE,EAAE;KACf,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,CAAC,CAAC;AAChB,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;IACzD,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,MAAM;QACjB,UAAU,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;KAClC,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,CAAC,KAAK,CAAC,CAAC;IACd,MAAM,CAAC,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;AACnC,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,mDAAmD,EAAE,GAAG,EAAE;IAC7D,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,MAAM;QACjB,UAAU,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE;KAClC,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,CAAC,KAAK,CAAC,CAAC;IACd,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,kBAAkB,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;AACjE,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,4EAA4E,EAAE,GAAG,EAAE;IACtF,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,MAAM;QACjB,UAAU,EAAE,EAAE;QACd,SAAS,EAAE,EAAE,OAAO,EAAE,aAAa,EAAE;KACtC,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,CAAC,KAAK,CAAC,CAAC;IACd,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC;IAC3C,MAAM,CAAC,WAAW,CAAE,KAAK,CAAC,kBAAkB,CAAC,SAAkC,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;AAC1G,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,qDAAqD,EAAE,GAAG,EAAE;IAC/D,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,MAAM;QACjB,UAAU,EAAE,EAAE,GAAG,EAAE,aAAa,EAAE;KACnC,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,CAAC,KAAK,CAAC,CAAC;IACd,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,kBAAkB,CAAC,UAAU,EAAE,GAAG,EAAE,aAAa,CAAC,CAAC;AAC9E,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+CAA+C,EAAE,GAAG,EAAE;IACzD,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,MAAM;QACjB,UAAU,EAAE,EAAE,IAAI,EAAE,kBAAkB,EAAE;KACzC,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,CAAC,KAAK,CAAC,CAAC;IACd,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,kBAAkB,CAAC,UAAU,EAAE,IAAI,EAAE,kBAAkB,CAAC,CAAC;AACpF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,4CAA4C,EAAE,GAAG,EAAE;IACtD,MAAM,GAAG,GAAmB;QAC1B,SAAS,EAAE,MAAM;QACjB,UAAU,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE;KACnC,CAAC;IACF,MAAM,KAAK,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;IACvC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChB,CAAC,CAAC,CAAC"}
|