@raquezha/notrace 0.1.1 → 0.2.0

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 (80) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/notrace/index.d.ts +34 -0
  3. package/dist/notrace/index.js +144 -118
  4. package/dist/notrace/report-app/__tests__/analytics.test.d.ts +1 -0
  5. package/dist/notrace/report-app/__tests__/analytics.test.js +35 -0
  6. package/dist/notrace/report-app/__tests__/card.test.d.ts +1 -0
  7. package/dist/notrace/report-app/__tests__/card.test.js +26 -0
  8. package/dist/notrace/report-app/__tests__/event.test.d.ts +1 -0
  9. package/dist/notrace/report-app/__tests__/event.test.js +20 -0
  10. package/dist/notrace/report-app/__tests__/format.test.d.ts +1 -0
  11. package/dist/notrace/report-app/__tests__/format.test.js +41 -0
  12. package/dist/notrace/report-app/__tests__/report.test.d.ts +1 -0
  13. package/dist/notrace/report-app/__tests__/report.test.js +31 -0
  14. package/dist/notrace/report-app/analytics.d.ts +3 -0
  15. package/dist/notrace/report-app/analytics.js +78 -0
  16. package/dist/notrace/report-app/client.d.ts +2 -0
  17. package/dist/notrace/report-app/client.js +105 -0
  18. package/dist/notrace/report-app/components/card.d.ts +4 -0
  19. package/dist/notrace/report-app/components/card.js +36 -0
  20. package/dist/notrace/report-app/components/dashboard.d.ts +1 -0
  21. package/dist/notrace/report-app/components/dashboard.js +16 -0
  22. package/dist/notrace/report-app/components/event.d.ts +5 -0
  23. package/dist/notrace/report-app/components/event.js +42 -0
  24. package/dist/notrace/report-app/components/message.d.ts +2 -0
  25. package/dist/notrace/report-app/components/message.js +43 -0
  26. package/dist/notrace/report-app/dashboard-report.d.ts +1 -0
  27. package/dist/notrace/report-app/dashboard-report.js +6 -0
  28. package/dist/notrace/report-app/escape.d.ts +1 -0
  29. package/dist/notrace/report-app/escape.js +10 -0
  30. package/dist/notrace/report-app/format.d.ts +13 -0
  31. package/dist/notrace/report-app/format.js +102 -0
  32. package/dist/notrace/report-app/report.d.ts +1 -0
  33. package/dist/notrace/report-app/report.js +29 -0
  34. package/dist/notrace/report-app/shell.d.ts +5 -0
  35. package/dist/notrace/report-app/shell.js +19 -0
  36. package/dist/notrace/report-app/styles.d.ts +1 -0
  37. package/dist/notrace/report-app/styles.js +431 -0
  38. package/dist/notrace/report-app/types.d.ts +28 -0
  39. package/dist/notrace/report-app/types.js +1 -0
  40. package/extensions/notrace/__tests__/ghost-session.test.ts +103 -0
  41. package/extensions/notrace/__tests__/helpers.ts +11 -0
  42. package/extensions/notrace/__tests__/lock-race.test.ts +176 -0
  43. package/extensions/notrace/__tests__/usage-normalization.test.ts +80 -0
  44. package/extensions/notrace/index.ts +160 -124
  45. package/extensions/notrace/report-app/__tests__/analytics.test.ts +41 -0
  46. package/extensions/notrace/report-app/__tests__/card.test.ts +29 -0
  47. package/extensions/notrace/report-app/__tests__/event.test.ts +23 -0
  48. package/extensions/notrace/report-app/__tests__/format.test.ts +46 -0
  49. package/extensions/notrace/report-app/__tests__/report.test.ts +33 -0
  50. package/extensions/notrace/report-app/analytics.ts +79 -0
  51. package/extensions/notrace/report-app/client.ts +106 -0
  52. package/extensions/notrace/report-app/components/card.ts +38 -0
  53. package/extensions/notrace/report-app/components/dashboard.ts +17 -0
  54. package/extensions/notrace/report-app/components/event.ts +39 -0
  55. package/extensions/notrace/report-app/components/message.ts +39 -0
  56. package/extensions/notrace/report-app/dashboard-report.ts +7 -0
  57. package/extensions/notrace/report-app/escape.ts +10 -0
  58. package/extensions/notrace/report-app/format.ts +107 -0
  59. package/extensions/notrace/report-app/report.ts +33 -0
  60. package/extensions/notrace/report-app/shell.ts +24 -0
  61. package/extensions/notrace/report-app/styles.ts +431 -0
  62. package/extensions/notrace/report-app/types.ts +35 -0
  63. package/package.json +4 -2
  64. package/templates/dashboard.sample.html +103 -63
  65. package/templates/dashboard.sample.json +73 -10
  66. package/templates/render-samples.mjs +119 -1
  67. package/templates/session.sample.html +125 -168
  68. package/templates/session.sample.json +66 -7
  69. package/templates/sessions/019ed2ee-1000-76ee-b353-000000000001/notrace.html +125 -163
  70. package/templates/sessions/019ed2ee-1000-76ee-b353-000000000001/notrace.json +50 -0
  71. package/templates/sessions/019ed2ee-1001-76ee-b353-000000000002/notrace.html +125 -162
  72. package/templates/sessions/019ed2ee-1001-76ee-b353-000000000002/notrace.json +50 -0
  73. package/templates/sessions/019ed2ee-1002-76ee-b353-000000000003/notrace.html +125 -163
  74. package/templates/sessions/019ed2ee-1002-76ee-b353-000000000003/notrace.json +50 -0
  75. package/templates/sessions/019ed2ee-massive/notrace.html +498 -0
  76. package/templates/sessions/019ed2ee-massive/notrace.json +14660 -0
  77. package/tsconfig.json +1 -1
  78. package/dist/notrace/renderer.d.ts +0 -4
  79. package/dist/notrace/renderer.js +0 -800
  80. package/extensions/notrace/renderer.ts +0 -810
@@ -0,0 +1,431 @@
1
+ export const STYLES = `:root {
2
+ --bg: #0c0b0a;
3
+ --panel: rgba(255,255,255,0.04);
4
+ --panel-strong: rgba(255,255,255,0.06);
5
+ --text: #ece3da;
6
+ --muted: rgba(236,227,218,0.68);
7
+ --accent: #d88462;
8
+ --accent-soft: rgba(216,132,98,0.12);
9
+ --border: rgba(255,255,255,0.08);
10
+ --shadow: 0 20px 50px rgba(0,0,0,0.45);
11
+ --code: #090807;
12
+ --err: #ef7f7f;
13
+ --rpiv-fg: #f3be8a;
14
+ --rpiv-bg: rgba(243,190,138,0.12);
15
+ --rpiv-border: rgba(243,190,138,0.26);
16
+ --research-fg: #8ec5ff;
17
+ --research-bg: rgba(142,197,255,0.12);
18
+ --research-border: rgba(142,197,255,0.24);
19
+ --generic-fg: #b9b4ae;
20
+ --generic-bg: rgba(185,180,174,0.12);
21
+ --generic-border: rgba(185,180,174,0.2);
22
+ }
23
+ * { box-sizing: border-box; }
24
+ html { color-scheme: dark; }
25
+ body {
26
+ margin: 0;
27
+ font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
28
+ background: var(--bg);
29
+ color: var(--text);
30
+ line-height: 1.5;
31
+ background-image: radial-gradient(circle at 50% -10%, rgba(216,132,98,0.14), transparent 45%);
32
+ background-attachment: fixed;
33
+ }
34
+ a { color: inherit; }
35
+ .container { max-width: 1120px; margin: 0 auto; padding: 32px 20px 64px; }
36
+ .hero, .panel {
37
+ background: var(--panel);
38
+ border: 1px solid var(--border);
39
+ border-radius: 24px;
40
+ box-shadow: var(--shadow);
41
+ backdrop-filter: blur(10px);
42
+ }
43
+ .hero { padding: 28px; margin-bottom: 24px; }
44
+ .hero-top {
45
+ display: grid;
46
+ grid-template-columns: minmax(0, 1fr) auto;
47
+ gap: 16px;
48
+ align-items: start;
49
+ }
50
+ .hero-split {
51
+ display: grid;
52
+ grid-template-columns: minmax(0, 1fr) auto;
53
+ gap: 16px;
54
+ align-items: start;
55
+ }
56
+ .hero-right {
57
+ display: grid;
58
+ gap: 12px;
59
+ justify-items: end;
60
+ min-width: 0;
61
+ }
62
+ .hero-session {
63
+ display: grid;
64
+ gap: 4px;
65
+ text-align: right;
66
+ min-width: 0;
67
+ }
68
+ .hero-meta {
69
+ display: flex;
70
+ flex-wrap: wrap;
71
+ gap: 8px;
72
+ justify-content: flex-end;
73
+ }
74
+ .brand { margin-bottom: 10px; }
75
+ .brand-link {
76
+ display: inline-flex;
77
+ align-items: flex-start;
78
+ text-decoration: none;
79
+ }
80
+ .wordmark {
81
+ width: 340px;
82
+ height: 112px;
83
+ display: block;
84
+ overflow: visible;
85
+ }
86
+ .subtitle { margin: 10px 0 0; color: var(--muted); }
87
+ .session-subtitle {
88
+ display: flex;
89
+ align-items: center;
90
+ gap: 10px;
91
+ flex-wrap: wrap;
92
+ }
93
+ .session-id-chip {
94
+ display: inline-flex;
95
+ align-items: center;
96
+ gap: 8px;
97
+ max-width: 100%;
98
+ padding: 6px 8px 6px 10px;
99
+ border: 1px solid var(--border);
100
+ border-radius: 999px;
101
+ background: rgba(0,0,0,0.18);
102
+ color: var(--text);
103
+ font-family: "SFMono-Regular", ui-monospace, Menlo, Monaco, Consolas, monospace;
104
+ font-size: 0.78rem;
105
+ word-break: break-all;
106
+ }
107
+ .copy-btn {
108
+ display: inline-flex;
109
+ align-items: center;
110
+ justify-content: center;
111
+ width: 26px;
112
+ height: 26px;
113
+ border: 1px solid rgba(255,255,255,0.12);
114
+ border-radius: 999px;
115
+ background: rgba(255,255,255,0.04);
116
+ color: var(--muted);
117
+ cursor: pointer;
118
+ transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
119
+ }
120
+ .copy-btn:hover, .copy-btn.copied {
121
+ color: var(--text);
122
+ border-color: rgba(216,132,98,0.45);
123
+ background: var(--accent-soft);
124
+ }
125
+ .meta {
126
+ display: flex;
127
+ gap: 8px;
128
+ flex-wrap: wrap;
129
+ justify-content: flex-end;
130
+ align-items: center;
131
+ margin-top: 16px;
132
+ }
133
+ .pill, .workflow-pill, .sort-btn, .export-btn {
134
+ display: inline-flex;
135
+ align-items: center;
136
+ gap: 6px;
137
+ text-decoration: none;
138
+ padding: 8px 12px;
139
+ border: 1px solid var(--border);
140
+ border-radius: 999px;
141
+ background: rgba(255,255,255,0.03);
142
+ color: var(--muted);
143
+ font-size: 0.86rem;
144
+ font-weight: 600;
145
+ }
146
+ .metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(135px, 1fr)); gap: 16px; margin: 24px 0; }
147
+ .metric-card {
148
+ background: var(--panel-strong);
149
+ border: 1px solid var(--border);
150
+ border-radius: 18px;
151
+ padding: 18px;
152
+ min-width: 0;
153
+ }
154
+ .metric-card small { display: block; color: var(--accent); text-transform: uppercase; letter-spacing: 0.08em; font-size: 0.72rem; font-weight: 700; }
155
+ .metric-card strong { display: block; margin-top: 8px; font-size: clamp(1rem, 2vw, 1.55rem); overflow-wrap: anywhere; }
156
+ .panel { padding: 0; overflow: hidden; }
157
+ .section-title { margin: 0; padding: 20px 22px; border-bottom: 1px solid var(--border); font-size: 1rem; }
158
+ .summary-pills { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
159
+ .kv-grid {
160
+ display: grid;
161
+ grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
162
+ gap: 16px;
163
+ }
164
+ .kv-card {
165
+ background: rgba(0,0,0,0.18);
166
+ border: 1px solid var(--border);
167
+ border-radius: 16px;
168
+ padding: 16px;
169
+ }
170
+ .kv-title {
171
+ font-size: 0.72rem;
172
+ text-transform: uppercase;
173
+ color: var(--accent);
174
+ font-weight: 700;
175
+ letter-spacing: 0.08em;
176
+ margin-bottom: 10px;
177
+ }
178
+ .kv-list { display: grid; gap: 8px; }
179
+ .kv-row { display: flex; justify-content: space-between; gap: 12px; align-items: start; }
180
+ .kv-key { color: var(--muted); }
181
+ .kv-value { color: var(--text); text-align: right; word-break: break-word; }
182
+ .tiny-breakdown {
183
+ margin-top: -10px;
184
+ margin-bottom: 8px;
185
+ color: var(--muted);
186
+ font-size: 0.82rem;
187
+ display: flex;
188
+ flex-wrap: wrap;
189
+ gap: 8px 14px;
190
+ }
191
+ .tiny-breakdown strong { color: var(--text); font-size: inherit; font-weight: 600; }
192
+ .collapsible { margin-top: 24px; }
193
+ .collapsible > summary { list-style: none; cursor: pointer; display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 18px 22px; font-size: 1rem; font-weight: 700; }
194
+ .collapsible > summary::-webkit-details-marker { display: none; }
195
+ .collapsible > summary:hover { background: rgba(255,255,255,0.02); }
196
+ .collapsible > summary::after { content: "▾"; color: var(--muted); font-size: 0.9rem; }
197
+ .collapsible:not([open]) > summary::after { content: "▸"; }
198
+ .empty { padding: 32px 22px; color: var(--muted); }
199
+ table { width: 100%; border-collapse: collapse; }
200
+ th, td { padding: 14px 18px; text-align: left; border-bottom: 1px solid var(--border); vertical-align: top; }
201
+ th { color: var(--muted); font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.08em; }
202
+ .num-cell { text-align: right; }
203
+ tr:last-child td { border-bottom: 0; }
204
+ .col-index { width: 64px; }
205
+ .sortable-head { padding: 10px 18px; }
206
+ .sort-btn {
207
+ padding: 0;
208
+ border: 0;
209
+ border-radius: 0;
210
+ background: transparent;
211
+ font: inherit;
212
+ text-transform: inherit;
213
+ letter-spacing: inherit;
214
+ cursor: pointer;
215
+ }
216
+ .sort-label { color: inherit; }
217
+ .sort-state { color: var(--accent); font-size: 0.9rem; min-width: 16px; text-align: left; line-height: 1; }
218
+ .index-cell { color: var(--muted); font-variant-numeric: tabular-nums; }
219
+ .session-link { text-decoration: none; }
220
+ .session-link strong { display: block; }
221
+ .session-sub { display: block; margin-top: 2px; color: var(--muted); font-size: 0.8rem; }
222
+ .workflow-pill { padding: 6px 10px; font-size: 0.78rem; }
223
+ .workflow-rpiv { color: var(--rpiv-fg); background: var(--rpiv-bg); border-color: var(--rpiv-border); }
224
+ .workflow-research { color: var(--research-fg); background: var(--research-bg); border-color: var(--research-border); }
225
+ .workflow-generic { color: var(--generic-fg); background: var(--generic-bg); border-color: var(--generic-border); }
226
+ .date-cell { display: grid; gap: 2px; }
227
+ .date-cell strong { font-size: 0.92rem; }
228
+ .date-cell span { color: var(--muted); font-size: 0.82rem; }
229
+ .timeline { display: grid; gap: 14px; }
230
+ .event {
231
+ background: var(--panel);
232
+ border: 1px solid var(--border);
233
+ border-radius: 18px;
234
+ overflow: hidden;
235
+ }
236
+ .event summary {
237
+ list-style: none;
238
+ cursor: pointer;
239
+ display: flex;
240
+ justify-content: space-between;
241
+ gap: 14px;
242
+ align-items: center;
243
+ padding: 16px 18px;
244
+ }
245
+ .event summary::-webkit-details-marker { display: none; }
246
+ .event summary:hover { background: rgba(255,255,255,0.02); }
247
+ .event-main { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
248
+ .badge {
249
+ display: inline-flex;
250
+ align-items: center;
251
+ padding: 4px 8px;
252
+ border-radius: 999px;
253
+ font-size: 0.72rem;
254
+ font-weight: 800;
255
+ text-transform: uppercase;
256
+ letter-spacing: 0.06em;
257
+ border: 1px solid var(--border);
258
+ background: rgba(255,255,255,0.03);
259
+ }
260
+ .badge-llm { color: var(--accent); background: var(--accent-soft); border-color: rgba(216,132,98,0.24); }
261
+ .badge-tool { color: #8ec5ff; background: rgba(142,197,255,0.1); border-color: rgba(142,197,255,0.22); }
262
+ .badge-system { color: var(--muted); }
263
+ .badge-error { color: var(--err); background: rgba(239,127,127,0.12); border-color: rgba(239,127,127,0.24); }
264
+ .event-title { font-weight: 700; }
265
+ .event-time { color: var(--muted); font-size: 0.9rem; white-space: nowrap; }
266
+ .event-body { padding: 0 18px 18px; }
267
+ .stack { display: grid; gap: 12px; }
268
+ .block {
269
+ background: rgba(0,0,0,0.18);
270
+ border: 1px solid var(--border);
271
+ border-radius: 14px;
272
+ overflow: hidden;
273
+ }
274
+ .block h4 {
275
+ margin: 0;
276
+ padding: 10px 12px;
277
+ border-bottom: 1px solid var(--border);
278
+ color: var(--muted);
279
+ font-size: 0.8rem;
280
+ text-transform: uppercase;
281
+ letter-spacing: 0.08em;
282
+ }
283
+ pre {
284
+ margin: 0;
285
+ padding: 14px;
286
+ overflow-x: auto;
287
+ white-space: pre-wrap;
288
+ word-break: break-word;
289
+ font-family: "SFMono-Regular", ui-monospace, Menlo, Monaco, Consolas, monospace;
290
+ font-size: 0.84rem;
291
+ background: var(--code);
292
+ }
293
+ .msg { border-bottom: 1px solid var(--border); }
294
+ .msg:last-child { border-bottom: 0; }
295
+ .msg-head {
296
+ display: flex;
297
+ justify-content: space-between;
298
+ gap: 12px;
299
+ padding: 10px 12px;
300
+ border-bottom: 1px solid var(--border);
301
+ background: rgba(255,255,255,0.02);
302
+ }
303
+ .msg-role { font-size: 0.78rem; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase; }
304
+ .msg.user .msg-role { color: #8ec5ff; }
305
+ .msg.assistant .msg-role { color: var(--accent); }
306
+ .msg-content { padding: 14px; }
307
+ .chat-text {
308
+ white-space: pre-wrap;
309
+ word-break: break-word;
310
+ font-size: 0.95rem;
311
+ line-height: 1.6;
312
+ margin-bottom: 12px;
313
+ }
314
+ .chat-text:last-child { margin-bottom: 0; }
315
+ .chat-tool-use {
316
+ background: rgba(0,0,0,0.3);
317
+ border: 1px solid var(--border);
318
+ border-radius: 8px;
319
+ overflow: hidden;
320
+ margin-bottom: 12px;
321
+ }
322
+ .chat-tool-use:last-child { margin-bottom: 0; }
323
+ .chat-tool-header {
324
+ background: rgba(255,255,255,0.04);
325
+ padding: 8px 12px;
326
+ font-size: 0.8rem;
327
+ font-family: "SFMono-Regular", ui-monospace, Menlo, Monaco, Consolas, monospace;
328
+ color: #8ec5ff;
329
+ border-bottom: 1px solid var(--border);
330
+ display: flex;
331
+ align-items: center;
332
+ gap: 8px;
333
+ }
334
+ .chat-tool-body {
335
+ padding: 12px;
336
+ margin: 0;
337
+ background: transparent;
338
+ border: none;
339
+ max-height: 400px;
340
+ overflow-y: auto;
341
+ }
342
+ .footer-note {
343
+ margin-top: 22px;
344
+ color: var(--muted);
345
+ text-align: center;
346
+ padding: 10px 0 0;
347
+ font-family: inherit;
348
+ }
349
+ .footer-note.minimal {
350
+ font-size: 0.84rem;
351
+ font-variant-caps: all-small-caps;
352
+ letter-spacing: 0.14em;
353
+ line-height: 1;
354
+ }
355
+ .footer-note.stack {
356
+ display: grid;
357
+ gap: 6px;
358
+ line-height: 1.2;
359
+ }
360
+ .footer-brand {
361
+ color: var(--text);
362
+ font-size: 0.88rem;
363
+ font-weight: 700;
364
+ font-variant-caps: all-small-caps;
365
+ letter-spacing: 0.16em;
366
+ }
367
+ .footer-tagline {
368
+ font-size: 0.78rem;
369
+ letter-spacing: 0.08em;
370
+ font-variant-caps: all-small-caps;
371
+ }
372
+ .footer-meta {
373
+ font-size: 0.76rem;
374
+ font-variant-caps: all-small-caps;
375
+ letter-spacing: 0.14em;
376
+ }
377
+ .footer-meta a {
378
+ color: inherit;
379
+ text-decoration: none;
380
+ border-bottom: 1px solid rgba(236,227,218,0.22);
381
+ }
382
+ .footer-meta a:hover {
383
+ color: var(--text);
384
+ border-bottom-color: rgba(236,227,218,0.45);
385
+ }
386
+ .export-btn {
387
+ cursor: pointer;
388
+ transition: color 120ms ease, border-color 120ms ease, background 120ms ease;
389
+ }
390
+ .export-btn:hover, .export-btn.copied {
391
+ color: var(--text);
392
+ border-color: rgba(216,132,98,0.45);
393
+ background: var(--accent-soft);
394
+ }
395
+ .back-to-top {
396
+ position: fixed;
397
+ right: 20px;
398
+ bottom: 20px;
399
+ z-index: 20;
400
+ display: inline-flex;
401
+ align-items: center;
402
+ gap: 8px;
403
+ padding: 10px 14px;
404
+ border: 1px solid var(--border);
405
+ border-radius: 999px;
406
+ background: rgba(12,11,10,0.88);
407
+ color: var(--text);
408
+ text-decoration: none;
409
+ box-shadow: var(--shadow);
410
+ backdrop-filter: blur(10px);
411
+ opacity: 0;
412
+ pointer-events: none;
413
+ transform: translateY(8px);
414
+ transition: opacity 160ms ease, transform 160ms ease, border-color 120ms ease, background 120ms ease;
415
+ }
416
+ .back-to-top.visible {
417
+ opacity: 1;
418
+ pointer-events: auto;
419
+ transform: translateY(0);
420
+ }
421
+ .back-to-top:hover { border-color: rgba(216,132,98,0.45); background: rgba(216,132,98,0.12); }
422
+ .container { padding: 20px 14px 48px; }
423
+ .hero { padding: 20px; }
424
+ .hero-top, .hero-split { grid-template-columns: 1fr; }
425
+ .meta, .hero-meta { justify-content: flex-start; margin-top: 8px; }
426
+ .hero-right, .hero-session { justify-items: start; text-align: left; }
427
+ .wordmark { width: min(280px, 100%); height: auto; }
428
+ .metrics { grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); }
429
+ th:nth-child(5), td:nth-child(5) { display: none; }
430
+ .event summary { align-items: flex-start; }
431
+ }`;
@@ -0,0 +1,28 @@
1
+ export type WorkflowName = "norpiv" | "research" | "generic" | string;
2
+ export type TaskLike = {
3
+ workflow?: WorkflowName | null;
4
+ id?: string | null;
5
+ };
6
+ export type Taskish = {
7
+ task?: TaskLike | null;
8
+ workflow?: WorkflowName | null;
9
+ taskId?: string | null;
10
+ };
11
+ export type RepositoryLike = {
12
+ name?: string | null;
13
+ branch?: string | null;
14
+ };
15
+ export type Repoish = {
16
+ repository?: RepositoryLike | null;
17
+ repositoryName?: string | null;
18
+ repoName?: string | null;
19
+ };
20
+ export type StatusLike = {
21
+ status?: string | null;
22
+ };
23
+ export type EventLike = {
24
+ type?: string | null;
25
+ };
26
+ export type DateValue = string | number;
27
+ export type MaybeNumber = number | null | undefined;
28
+ export type MaybeString = string | null | undefined;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,103 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import * as path from "node:path";
3
+ import { afterEach, describe, expect, it, vi } from "vitest";
4
+ import { handleSessionShutdown, type SessionShutdownDeps } from "../index.js";
5
+ import { cleanupTempNotraceDir, makeTempNotraceDir } from "./helpers.js";
6
+
7
+ const tempDirs: string[] = [];
8
+
9
+ function makeCtx(sessionId: string) {
10
+ return {
11
+ cwd: process.cwd(),
12
+ sessionManager: {
13
+ getSessionId: () => sessionId,
14
+ },
15
+ };
16
+ }
17
+
18
+ function makeDeps(notraceDir: string, traceId: string, events: SessionShutdownDeps["events"], attach = vi.fn()): SessionShutdownDeps {
19
+ return {
20
+ events,
21
+ startTime: Date.now() - 1000,
22
+ traceId,
23
+ extensionTelemetry: new Map(),
24
+ captureMode: "full",
25
+ notraceDir,
26
+ adapter: {
27
+ name: "test",
28
+ detect: () => true,
29
+ getContext: () => ({ workflow: "test", taskId: "task-1", taskPath: null, taskDir: null }),
30
+ attach,
31
+ },
32
+ };
33
+ }
34
+
35
+ function sessionDir(notraceDir: string, sessionId: string): string {
36
+ return path.join(notraceDir, "sessions", sessionId.replace(/[^a-z0-9]/gi, "-"));
37
+ }
38
+
39
+ function readJson(filePath: string) {
40
+ return JSON.parse(readFileSync(filePath, "utf-8"));
41
+ }
42
+
43
+ afterEach(() => {
44
+ vi.restoreAllMocks();
45
+ while (tempDirs.length) cleanupTempNotraceDir(tempDirs.pop()!);
46
+ });
47
+
48
+ describe("handleSessionShutdown ghost sessions", () => {
49
+ it("skips session artifact writes, index entry creation, attach, and console log for ghost sessions", async () => {
50
+ const notraceDir = makeTempNotraceDir();
51
+ tempDirs.push(notraceDir);
52
+ const sessionId = "ghost-session";
53
+ const attach = vi.fn();
54
+ const log = vi.spyOn(console, "log").mockImplementation(() => {});
55
+
56
+ await handleSessionShutdown(
57
+ { reason: "ghost" },
58
+ makeCtx(sessionId),
59
+ makeDeps(notraceDir, sessionId, [], attach),
60
+ );
61
+
62
+ const dir = sessionDir(notraceDir, sessionId);
63
+ expect(existsSync(path.join(dir, "notrace.html"))).toBe(false);
64
+ expect(existsSync(path.join(dir, "notrace.json"))).toBe(false);
65
+
66
+ const indexPath = path.join(notraceDir, "index.json");
67
+ if (existsSync(indexPath)) {
68
+ const index = readJson(indexPath);
69
+ expect(index.sessions.filter((session: { sessionId: string }) => session.sessionId === sessionId)).toHaveLength(0);
70
+ } else {
71
+ expect(existsSync(indexPath)).toBe(false);
72
+ }
73
+
74
+ expect(attach).not.toHaveBeenCalled();
75
+ expect(log).not.toHaveBeenCalled();
76
+ });
77
+
78
+ it("still writes artifacts and indexes non-ghost sessions", async () => {
79
+ const notraceDir = makeTempNotraceDir();
80
+ tempDirs.push(notraceDir);
81
+ const sessionId = "real-session";
82
+ const attach = vi.fn();
83
+ vi.spyOn(console, "log").mockImplementation(() => {});
84
+
85
+ await handleSessionShutdown(
86
+ { reason: "normal" },
87
+ makeCtx(sessionId),
88
+ makeDeps(notraceDir, sessionId, [
89
+ { type: "tool_start", toolName: "test-tool", args: {}, timestamp: Date.now() },
90
+ ], attach),
91
+ );
92
+
93
+ const dir = sessionDir(notraceDir, sessionId);
94
+ const htmlPath = path.join(dir, "notrace.html");
95
+ const recordPath = path.join(dir, "notrace.json");
96
+ expect(existsSync(htmlPath)).toBe(true);
97
+ expect(existsSync(recordPath)).toBe(true);
98
+
99
+ const index = readJson(path.join(notraceDir, "index.json"));
100
+ expect(index.sessions.some((session: { sessionId: string }) => session.sessionId === sessionId)).toBe(true);
101
+ expect(attach).toHaveBeenCalledOnce();
102
+ });
103
+ });
@@ -0,0 +1,11 @@
1
+ import { mkdtempSync, rmSync } from "node:fs";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+
5
+ export function makeTempNotraceDir(): string {
6
+ return mkdtempSync(path.join(os.tmpdir(), "notrace-test-"));
7
+ }
8
+
9
+ export function cleanupTempNotraceDir(dir: string): void {
10
+ rmSync(dir, { recursive: true, force: true });
11
+ }