@tracemarketplace/shared 0.0.10 → 0.0.11

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.
Files changed (82) hide show
  1. package/dist/extractor-claude-code.test.js +53 -0
  2. package/dist/extractor-claude-code.test.js.map +1 -1
  3. package/dist/extractor-codex.test.js +5 -0
  4. package/dist/extractor-codex.test.js.map +1 -1
  5. package/dist/extractors/claude-code.d.ts.map +1 -1
  6. package/dist/extractors/claude-code.js +4 -4
  7. package/dist/extractors/claude-code.js.map +1 -1
  8. package/dist/extractors/codex.d.ts.map +1 -1
  9. package/dist/extractors/codex.js +2 -0
  10. package/dist/extractors/codex.js.map +1 -1
  11. package/dist/extractors/common.d.ts +1 -2
  12. package/dist/extractors/common.d.ts.map +1 -1
  13. package/dist/extractors/common.js +2 -37
  14. package/dist/extractors/common.js.map +1 -1
  15. package/dist/extractors/common.test.d.ts +2 -0
  16. package/dist/extractors/common.test.d.ts.map +1 -0
  17. package/dist/extractors/common.test.js +17 -0
  18. package/dist/extractors/common.test.js.map +1 -0
  19. package/dist/extractors/cursor.d.ts.map +1 -1
  20. package/dist/extractors/cursor.js +8 -0
  21. package/dist/extractors/cursor.js.map +1 -1
  22. package/dist/index.d.ts +1 -0
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +1 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/redact.d.ts.map +1 -1
  27. package/dist/redact.js +3 -1
  28. package/dist/redact.js.map +1 -1
  29. package/dist/redact.test.js +9 -0
  30. package/dist/redact.test.js.map +1 -1
  31. package/dist/scoring.d.ts +5 -3
  32. package/dist/scoring.d.ts.map +1 -1
  33. package/dist/scoring.fixtures.test.d.ts +2 -0
  34. package/dist/scoring.fixtures.test.d.ts.map +1 -0
  35. package/dist/scoring.fixtures.test.js +47 -0
  36. package/dist/scoring.fixtures.test.js.map +1 -0
  37. package/dist/scoring.js +381 -62
  38. package/dist/scoring.js.map +1 -1
  39. package/dist/scoring.test.js +125 -26
  40. package/dist/scoring.test.js.map +1 -1
  41. package/dist/tool-normalization.d.ts +66 -0
  42. package/dist/tool-normalization.d.ts.map +1 -0
  43. package/dist/tool-normalization.generated.d.ts +181 -0
  44. package/dist/tool-normalization.generated.d.ts.map +1 -0
  45. package/dist/tool-normalization.generated.js +261 -0
  46. package/dist/tool-normalization.generated.js.map +1 -0
  47. package/dist/tool-normalization.js +463 -0
  48. package/dist/tool-normalization.js.map +1 -0
  49. package/dist/tool-normalization.test.d.ts +2 -0
  50. package/dist/tool-normalization.test.d.ts.map +1 -0
  51. package/dist/tool-normalization.test.js +188 -0
  52. package/dist/tool-normalization.test.js.map +1 -0
  53. package/dist/types.d.ts +38 -1
  54. package/dist/types.d.ts.map +1 -1
  55. package/dist/validators.d.ts +23 -6
  56. package/dist/validators.d.ts.map +1 -1
  57. package/dist/validators.js +4 -0
  58. package/dist/validators.js.map +1 -1
  59. package/dist/validators.test.js +7 -0
  60. package/dist/validators.test.js.map +1 -1
  61. package/package.json +5 -5
  62. package/scripts/generate-tool-normalization.mjs +16 -0
  63. package/src/extractor-claude-code.test.ts +59 -0
  64. package/src/extractor-codex.test.ts +5 -0
  65. package/src/extractors/claude-code.ts +8 -4
  66. package/src/extractors/codex.ts +2 -0
  67. package/src/extractors/common.test.ts +21 -0
  68. package/src/extractors/common.ts +15 -49
  69. package/src/extractors/cursor.ts +9 -0
  70. package/src/index.ts +1 -0
  71. package/src/redact.test.ts +9 -0
  72. package/src/redact.ts +3 -1
  73. package/src/scoring.fixtures.test.ts +71 -0
  74. package/src/scoring.test.ts +151 -26
  75. package/src/scoring.ts +582 -84
  76. package/src/tool-normalization.generated.ts +262 -0
  77. package/src/tool-normalization.spec.json +205 -0
  78. package/src/tool-normalization.test.ts +221 -0
  79. package/src/tool-normalization.ts +670 -0
  80. package/src/types.ts +50 -0
  81. package/src/validators.test.ts +8 -0
  82. package/src/validators.ts +8 -0
@@ -0,0 +1,262 @@
1
+ // This file is generated from tool-normalization.spec.json. Do not edit directly.
2
+ export const toolNormalizationSpec = {
3
+ "schema_version": 1,
4
+ "normalized_tools": {
5
+ "shell.command": {
6
+ "family": "shell",
7
+ "kind": "command",
8
+ "aliases": [
9
+ "exec_command",
10
+ "bash",
11
+ "bash_command",
12
+ "run_shell_command",
13
+ "shell"
14
+ ],
15
+ "token_prefix": "Bash",
16
+ "mutates_file": false
17
+ },
18
+ "shell.stdin": {
19
+ "family": "shell",
20
+ "kind": "stdin",
21
+ "aliases": [
22
+ "write_stdin"
23
+ ],
24
+ "token_prefix": "Bash",
25
+ "mutates_file": false
26
+ },
27
+ "file.read": {
28
+ "family": "file",
29
+ "kind": "read",
30
+ "aliases": [
31
+ "read",
32
+ "read_file",
33
+ "grep",
34
+ "list_directory"
35
+ ],
36
+ "token_prefix": "Read",
37
+ "mutates_file": false
38
+ },
39
+ "file.write": {
40
+ "family": "file",
41
+ "kind": "write",
42
+ "aliases": [
43
+ "write",
44
+ "write_file",
45
+ "create_file"
46
+ ],
47
+ "token_prefix": "Write",
48
+ "mutates_file": true
49
+ },
50
+ "file.edit": {
51
+ "family": "file",
52
+ "kind": "edit",
53
+ "aliases": [
54
+ "edit",
55
+ "edit_file",
56
+ "replace"
57
+ ],
58
+ "token_prefix": "Edit",
59
+ "mutates_file": true
60
+ },
61
+ "file.multi_edit": {
62
+ "family": "file",
63
+ "kind": "multi_edit",
64
+ "aliases": [
65
+ "multiedit"
66
+ ],
67
+ "token_prefix": "MultiEdit",
68
+ "mutates_file": true
69
+ }
70
+ },
71
+ "shell": {
72
+ "dangerous_verbs": [
73
+ "sudo",
74
+ "su",
75
+ "doas",
76
+ "chmod",
77
+ "chown",
78
+ "dd",
79
+ "shred"
80
+ ],
81
+ "text_filter_tokens": {
82
+ "awk": "text_filter:awk",
83
+ "cut": "text_filter:cut",
84
+ "sort": "text_filter:sort"
85
+ },
86
+ "text_filter_write_verbs": [
87
+ "awk",
88
+ "cut",
89
+ "sort"
90
+ ],
91
+ "verb_categories": {
92
+ "npm": "build",
93
+ "pnpm": "build",
94
+ "yarn": "build",
95
+ "bun": "build",
96
+ "npx": "build",
97
+ "cargo": "build",
98
+ "rustup": "build",
99
+ "pip": "build",
100
+ "pip3": "build",
101
+ "uv": "build",
102
+ "poetry": "build",
103
+ "pipenv": "build",
104
+ "conda": "build",
105
+ "make": "build",
106
+ "cmake": "build",
107
+ "ninja": "build",
108
+ "go": "build",
109
+ "gradle": "build",
110
+ "mvn": "build",
111
+ "bazel": "build",
112
+ "tsc": "build",
113
+ "vite": "build",
114
+ "webpack": "build",
115
+ "pytest": "test",
116
+ "jest": "test",
117
+ "vitest": "test",
118
+ "mocha": "test",
119
+ "rspec": "test",
120
+ "phpunit": "test",
121
+ "cargo-test": "test",
122
+ "git": "vcs",
123
+ "gh": "vcs",
124
+ "svn": "vcs",
125
+ "curl": "network",
126
+ "wget": "network",
127
+ "ssh": "network",
128
+ "scp": "network",
129
+ "rsync": "network",
130
+ "nc": "network",
131
+ "netcat": "network",
132
+ "ncat": "network",
133
+ "sftp": "network",
134
+ "ftp": "network",
135
+ "cat": "fs_read",
136
+ "head": "fs_read",
137
+ "tail": "fs_read",
138
+ "less": "fs_read",
139
+ "ls": "fs_read",
140
+ "find": "fs_read",
141
+ "locate": "fs_read",
142
+ "grep": "fs_read",
143
+ "rg": "fs_read",
144
+ "ag": "fs_read",
145
+ "ack": "fs_read",
146
+ "wc": "fs_read",
147
+ "stat": "fs_read",
148
+ "file": "fs_read",
149
+ "du": "fs_read",
150
+ "diff": "fs_read",
151
+ "od": "fs_read",
152
+ "nl": "fs_read",
153
+ "which": "fs_read",
154
+ "xxd": "fs_read",
155
+ "rm": "fs_write",
156
+ "cp": "fs_write",
157
+ "mv": "fs_write",
158
+ "mkdir": "fs_write",
159
+ "touch": "fs_write",
160
+ "ln": "fs_write",
161
+ "tar": "fs_write",
162
+ "zip": "fs_write",
163
+ "unzip": "fs_write",
164
+ "python": "run",
165
+ "python3": "run",
166
+ "node": "run",
167
+ "ruby": "run",
168
+ "php": "run",
169
+ "perl": "run",
170
+ "bash": "run",
171
+ "sh": "run",
172
+ "zsh": "run",
173
+ "fish": "run",
174
+ "deno": "run",
175
+ "r": "run",
176
+ "rscript": "run",
177
+ "kill": "process",
178
+ "pkill": "process",
179
+ "killall": "process",
180
+ "ps": "process",
181
+ "top": "process",
182
+ "htop": "process",
183
+ "lsof": "process",
184
+ "sleep": "process",
185
+ "export": "env",
186
+ "unset": "env",
187
+ "env": "env",
188
+ "source": "env",
189
+ "printenv": "env",
190
+ "set": "env",
191
+ "apt": "sysadmin",
192
+ "apt-get": "sysadmin",
193
+ "brew": "sysadmin",
194
+ "yum": "sysadmin",
195
+ "dnf": "sysadmin",
196
+ "pacman": "sysadmin",
197
+ "snap": "sysadmin",
198
+ "eval": "shell_meta",
199
+ "exec": "shell_meta",
200
+ "xargs": "shell_meta",
201
+ "cd": "shell_meta",
202
+ "pwd": "shell_meta",
203
+ "echo": "shell_meta",
204
+ "if": "shell_meta"
205
+ }
206
+ },
207
+ "file": {
208
+ "extra_mutation_aliases": [
209
+ "apply_patch",
210
+ "write_file",
211
+ "create_file",
212
+ "delete_file",
213
+ "rename_file",
214
+ "move_file",
215
+ "file_change"
216
+ ],
217
+ "sensitive_path_signals": [
218
+ ".ssh",
219
+ ".aws",
220
+ "id_rsa",
221
+ "credentials",
222
+ ".env",
223
+ "secrets",
224
+ "token",
225
+ "password",
226
+ "private_key"
227
+ ],
228
+ "system_prefixes": [
229
+ "/etc/",
230
+ "/usr/",
231
+ "/var/",
232
+ "/sys/",
233
+ "/proc/",
234
+ "/boot/",
235
+ "/root/"
236
+ ],
237
+ "config_exts": [
238
+ ".env",
239
+ ".yaml",
240
+ ".yml",
241
+ ".toml",
242
+ ".ini",
243
+ ".cfg"
244
+ ],
245
+ "config_names": [
246
+ "config",
247
+ "settings",
248
+ "configuration"
249
+ ]
250
+ },
251
+ "tool_result_metadata_prefixes": [
252
+ "Command:",
253
+ "Chunk ID:",
254
+ "Wall time:",
255
+ "Process exited with code",
256
+ "Process running with session ID",
257
+ "Original token count:",
258
+ "Output:"
259
+ ]
260
+ } as const;
261
+
262
+ export type ToolNormalizationSpec = typeof toolNormalizationSpec;
@@ -0,0 +1,205 @@
1
+ {
2
+ "schema_version": 1,
3
+ "normalized_tools": {
4
+ "shell.command": {
5
+ "family": "shell",
6
+ "kind": "command",
7
+ "aliases": ["exec_command", "bash", "bash_command", "run_shell_command", "shell"],
8
+ "token_prefix": "Bash",
9
+ "mutates_file": false
10
+ },
11
+ "shell.stdin": {
12
+ "family": "shell",
13
+ "kind": "stdin",
14
+ "aliases": ["write_stdin"],
15
+ "token_prefix": "Bash",
16
+ "mutates_file": false
17
+ },
18
+ "file.read": {
19
+ "family": "file",
20
+ "kind": "read",
21
+ "aliases": ["read", "read_file", "grep", "list_directory"],
22
+ "token_prefix": "Read",
23
+ "mutates_file": false
24
+ },
25
+ "file.write": {
26
+ "family": "file",
27
+ "kind": "write",
28
+ "aliases": ["write", "write_file", "create_file"],
29
+ "token_prefix": "Write",
30
+ "mutates_file": true
31
+ },
32
+ "file.edit": {
33
+ "family": "file",
34
+ "kind": "edit",
35
+ "aliases": ["edit", "edit_file", "replace"],
36
+ "token_prefix": "Edit",
37
+ "mutates_file": true
38
+ },
39
+ "file.multi_edit": {
40
+ "family": "file",
41
+ "kind": "multi_edit",
42
+ "aliases": ["multiedit"],
43
+ "token_prefix": "MultiEdit",
44
+ "mutates_file": true
45
+ }
46
+ },
47
+ "shell": {
48
+ "dangerous_verbs": ["sudo", "su", "doas", "chmod", "chown", "dd", "shred"],
49
+ "text_filter_tokens": {
50
+ "awk": "text_filter:awk",
51
+ "cut": "text_filter:cut",
52
+ "sort": "text_filter:sort"
53
+ },
54
+ "text_filter_write_verbs": ["awk", "cut", "sort"],
55
+ "verb_categories": {
56
+ "npm": "build",
57
+ "pnpm": "build",
58
+ "yarn": "build",
59
+ "bun": "build",
60
+ "npx": "build",
61
+ "cargo": "build",
62
+ "rustup": "build",
63
+ "pip": "build",
64
+ "pip3": "build",
65
+ "uv": "build",
66
+ "poetry": "build",
67
+ "pipenv": "build",
68
+ "conda": "build",
69
+ "make": "build",
70
+ "cmake": "build",
71
+ "ninja": "build",
72
+ "go": "build",
73
+ "gradle": "build",
74
+ "mvn": "build",
75
+ "bazel": "build",
76
+ "tsc": "build",
77
+ "vite": "build",
78
+ "webpack": "build",
79
+ "pytest": "test",
80
+ "jest": "test",
81
+ "vitest": "test",
82
+ "mocha": "test",
83
+ "rspec": "test",
84
+ "phpunit": "test",
85
+ "cargo-test": "test",
86
+ "git": "vcs",
87
+ "gh": "vcs",
88
+ "svn": "vcs",
89
+ "curl": "network",
90
+ "wget": "network",
91
+ "ssh": "network",
92
+ "scp": "network",
93
+ "rsync": "network",
94
+ "nc": "network",
95
+ "netcat": "network",
96
+ "ncat": "network",
97
+ "sftp": "network",
98
+ "ftp": "network",
99
+ "cat": "fs_read",
100
+ "head": "fs_read",
101
+ "tail": "fs_read",
102
+ "less": "fs_read",
103
+ "ls": "fs_read",
104
+ "find": "fs_read",
105
+ "locate": "fs_read",
106
+ "grep": "fs_read",
107
+ "rg": "fs_read",
108
+ "ag": "fs_read",
109
+ "ack": "fs_read",
110
+ "wc": "fs_read",
111
+ "stat": "fs_read",
112
+ "file": "fs_read",
113
+ "du": "fs_read",
114
+ "diff": "fs_read",
115
+ "od": "fs_read",
116
+ "nl": "fs_read",
117
+ "which": "fs_read",
118
+ "xxd": "fs_read",
119
+ "rm": "fs_write",
120
+ "cp": "fs_write",
121
+ "mv": "fs_write",
122
+ "mkdir": "fs_write",
123
+ "touch": "fs_write",
124
+ "ln": "fs_write",
125
+ "tar": "fs_write",
126
+ "zip": "fs_write",
127
+ "unzip": "fs_write",
128
+ "python": "run",
129
+ "python3": "run",
130
+ "node": "run",
131
+ "ruby": "run",
132
+ "php": "run",
133
+ "perl": "run",
134
+ "bash": "run",
135
+ "sh": "run",
136
+ "zsh": "run",
137
+ "fish": "run",
138
+ "deno": "run",
139
+ "r": "run",
140
+ "rscript": "run",
141
+ "kill": "process",
142
+ "pkill": "process",
143
+ "killall": "process",
144
+ "ps": "process",
145
+ "top": "process",
146
+ "htop": "process",
147
+ "lsof": "process",
148
+ "sleep": "process",
149
+ "export": "env",
150
+ "unset": "env",
151
+ "env": "env",
152
+ "source": "env",
153
+ "printenv": "env",
154
+ "set": "env",
155
+ "apt": "sysadmin",
156
+ "apt-get": "sysadmin",
157
+ "brew": "sysadmin",
158
+ "yum": "sysadmin",
159
+ "dnf": "sysadmin",
160
+ "pacman": "sysadmin",
161
+ "snap": "sysadmin",
162
+ "eval": "shell_meta",
163
+ "exec": "shell_meta",
164
+ "xargs": "shell_meta",
165
+ "cd": "shell_meta",
166
+ "pwd": "shell_meta",
167
+ "echo": "shell_meta",
168
+ "if": "shell_meta"
169
+ }
170
+ },
171
+ "file": {
172
+ "extra_mutation_aliases": [
173
+ "apply_patch",
174
+ "write_file",
175
+ "create_file",
176
+ "delete_file",
177
+ "rename_file",
178
+ "move_file",
179
+ "file_change"
180
+ ],
181
+ "sensitive_path_signals": [
182
+ ".ssh",
183
+ ".aws",
184
+ "id_rsa",
185
+ "credentials",
186
+ ".env",
187
+ "secrets",
188
+ "token",
189
+ "password",
190
+ "private_key"
191
+ ],
192
+ "system_prefixes": ["/etc/", "/usr/", "/var/", "/sys/", "/proc/", "/boot/", "/root/"],
193
+ "config_exts": [".env", ".yaml", ".yml", ".toml", ".ini", ".cfg"],
194
+ "config_names": ["config", "settings", "configuration"]
195
+ },
196
+ "tool_result_metadata_prefixes": [
197
+ "Command:",
198
+ "Chunk ID:",
199
+ "Wall time:",
200
+ "Process exited with code",
201
+ "Process running with session ID",
202
+ "Original token count:",
203
+ "Output:"
204
+ ]
205
+ }
@@ -0,0 +1,221 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import type { ContentBlock, NormalizedTrace, Turn } from "./types.js";
3
+ import {
4
+ extractFailureExchanges,
5
+ getNormalizedToolHierarchy,
6
+ isFileMutationTool,
7
+ listNormalizedToolsByFamily,
8
+ normalizeTraceForEvaluation,
9
+ normalizeToolUse,
10
+ TRACE_NORMALIZATION_VERSION,
11
+ tokenizeBashCommand,
12
+ } from "./tool-normalization.js";
13
+
14
+ function makeTrace(turns: Turn[]): NormalizedTrace {
15
+ return {
16
+ trace_id: "test-id",
17
+ schema_version: "1.0",
18
+ source_tool: "codex_cli",
19
+ source_session_id: "session-abc",
20
+ source_version: null,
21
+ submitted_by: "user1",
22
+ submitted_at: "2024-01-01T00:00:00Z",
23
+ extracted_at: "2024-01-01T00:00:00Z",
24
+ git_branch: null,
25
+ cwd_hash: null,
26
+ working_language: null,
27
+ started_at: "2024-01-01T00:00:00Z",
28
+ ended_at: "2024-01-01T00:01:00Z",
29
+ turns,
30
+ turn_count: turns.length,
31
+ tool_call_count: turns.flatMap((turn) => turn.content).filter((block) => block.type === "tool_use")
32
+ .length,
33
+ has_tool_calls: true,
34
+ has_thinking_blocks: false,
35
+ has_file_changes: false,
36
+ has_shell_commands: false,
37
+ total_input_tokens: null,
38
+ total_output_tokens: null,
39
+ total_cache_read_tokens: null,
40
+ content_fidelity: "full",
41
+ env_state: null,
42
+ score: null,
43
+ raw_r2_key: "",
44
+ normalized_r2_key: "",
45
+ };
46
+ }
47
+
48
+ function makeTurn(role: "user" | "assistant", content: ContentBlock[]): Turn {
49
+ return {
50
+ turn_id: Math.random().toString(36).slice(2),
51
+ parent_turn_id: null,
52
+ role,
53
+ timestamp: null,
54
+ content,
55
+ model: null,
56
+ usage: null,
57
+ source_metadata: {},
58
+ };
59
+ }
60
+
61
+ describe("tool hierarchy", () => {
62
+ it("normalizes namespaced shell tools into a single hierarchy", () => {
63
+ const normalized = getNormalizedToolHierarchy("functions.exec_command");
64
+
65
+ expect(normalized).toMatchObject({
66
+ normalizedToolId: "shell.command",
67
+ family: "shell",
68
+ kind: "command",
69
+ token_prefix: "Bash",
70
+ });
71
+ });
72
+
73
+ it("lists canonical file tools from the shared hierarchy", () => {
74
+ expect(listNormalizedToolsByFamily("file").map((tool) => tool.normalizedToolId)).toEqual([
75
+ "file.read",
76
+ "file.write",
77
+ "file.edit",
78
+ "file.multi_edit",
79
+ ]);
80
+ });
81
+ });
82
+
83
+ describe("tokenizeBashCommand", () => {
84
+ it("treats read-shaped and write-shaped shell commands differently", () => {
85
+ expect(tokenizeBashCommand("sed -n '1,40p' src/server.ts", 0)).toBe(
86
+ "Bash:fs_read:pass",
87
+ );
88
+ expect(tokenizeBashCommand("sed -i '' 's/old/new/' src/server.ts", 0)).toBe(
89
+ "Bash:fs_write:pass",
90
+ );
91
+ expect(tokenizeBashCommand("sort -o tmp/out.txt src/server.ts", 0)).toBe(
92
+ "Bash:text_filter_write:sort:pass",
93
+ );
94
+ });
95
+ });
96
+
97
+ describe("normalizeToolUse", () => {
98
+ it("drops empty write_stdin polls", () => {
99
+ const normalized = normalizeToolUse(
100
+ {
101
+ type: "tool_use",
102
+ tool_call_id: "stdin-1",
103
+ tool_name: "functions.write_stdin",
104
+ tool_input: { session_id: 1234, chars: "", yield_time_ms: 1000 },
105
+ },
106
+ null,
107
+ );
108
+
109
+ expect(normalized).toBeNull();
110
+ });
111
+
112
+ it("classifies file mutation tools through the shared hierarchy", () => {
113
+ expect(isFileMutationTool("Edit", { path: "src/server.ts" })).toBe(true);
114
+ expect(
115
+ isFileMutationTool("functions.exec_command", {
116
+ cmd: "awk 'NF' src/server.ts > tmp/server.filtered.ts",
117
+ }),
118
+ ).toBe(true);
119
+ });
120
+ });
121
+
122
+ describe("extractFailureExchanges", () => {
123
+ it("treats clean ctrl-c write_stdin flows as canceled exchanges", () => {
124
+ const turns = [
125
+ makeTurn("user", [{ type: "text", text: "Run the watch command, then stop it." }]),
126
+ makeTurn("assistant", [
127
+ {
128
+ type: "tool_use",
129
+ tool_call_id: "exec-1",
130
+ tool_name: "functions.exec_command",
131
+ tool_input: { cmd: "vitest --watch", tty: true },
132
+ },
133
+ {
134
+ type: "tool_result",
135
+ tool_call_id: "exec-1",
136
+ is_error: false,
137
+ result_content: "Process running with session ID 4242",
138
+ exit_code: null,
139
+ },
140
+ {
141
+ type: "tool_use",
142
+ tool_call_id: "stdin-1",
143
+ tool_name: "functions.write_stdin",
144
+ tool_input: { session_id: 4242, chars: "\u0003", yield_time_ms: 1000 },
145
+ },
146
+ {
147
+ type: "tool_result",
148
+ tool_call_id: "stdin-1",
149
+ is_error: true,
150
+ result_content: "Process exited with code 130\n^C",
151
+ exit_code: 130,
152
+ },
153
+ ]),
154
+ ];
155
+
156
+ expect(extractFailureExchanges(makeTrace(turns))).toEqual([
157
+ {
158
+ toolTokens: ["Bash:test:pass", "Bash:pty_control:interrupt:fail"],
159
+ hasError: false,
160
+ outcome: "canceled",
161
+ },
162
+ ]);
163
+ });
164
+ });
165
+
166
+ describe("normalizeTraceForEvaluation", () => {
167
+ it("builds a persisted exchange artifact with actions and versioning", () => {
168
+ const turns = [
169
+ makeTurn("user", [{ type: "text", text: "Run the tests." }]),
170
+ {
171
+ ...makeTurn("assistant", [
172
+ {
173
+ type: "tool_use",
174
+ tool_call_id: "exec-1",
175
+ tool_name: "functions.exec_command",
176
+ tool_input: { cmd: "pnpm test" },
177
+ },
178
+ {
179
+ type: "tool_result",
180
+ tool_call_id: "exec-1",
181
+ is_error: false,
182
+ result_content: "ok",
183
+ exit_code: 0,
184
+ },
185
+ ]),
186
+ usage: {
187
+ input_tokens: 10,
188
+ output_tokens: 42,
189
+ cache_read_input_tokens: null,
190
+ cache_creation_input_tokens: null,
191
+ reasoning_tokens: null,
192
+ },
193
+ },
194
+ ];
195
+
196
+ expect(normalizeTraceForEvaluation(makeTrace(turns))).toEqual({
197
+ version: TRACE_NORMALIZATION_VERSION,
198
+ exchanges: [
199
+ {
200
+ exchangeIndex: 0,
201
+ actions: [
202
+ {
203
+ toolCallId: "exec-1",
204
+ rawToolName: "functions.exec_command",
205
+ normalizedToolId: "shell.command",
206
+ family: "shell",
207
+ kind: "command",
208
+ token: "Bash:build:pass",
209
+ exitCode: 0,
210
+ },
211
+ ],
212
+ toolTokens: ["Bash:build:pass"],
213
+ hasError: false,
214
+ outcome: "success",
215
+ prevOutcome: null,
216
+ tokenCount: 42,
217
+ },
218
+ ],
219
+ });
220
+ });
221
+ });