@qiaolei81/copilot-session-viewer 0.3.3 → 0.3.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.
@@ -1,844 +0,0 @@
1
- # Unified Frontend Event Format - Design Document
2
-
3
- ## Executive Summary
4
-
5
- This document proposes a unified event format to eliminate format-specific checks in the frontend and simplify tool call handling across three AI session formats: Copilot, Claude, and Pi-Mono.
6
-
7
- **Current Problem:** The codebase handles three different tool event structures:
8
- - **Copilot:** Independent `tool.execution_start` and `tool.execution_complete` events
9
- - **Claude:** Embedded `tool_use` and `tool_result` in message content, later matched and merged
10
- - **Pi-Mono:** Embedded tools in `assistant.message.data.tools` with results already merged
11
-
12
- The frontend contains multiple format checks (`tool.type === 'tool_use'`, `_matched` flags, etc.) scattered across views.
13
-
14
- **Proposed Solution:** Establish a single normalized tool representation at the API response layer that abstracts away source differences.
15
-
16
- ---
17
-
18
- ## 1. Current State Analysis
19
-
20
- ### 1.1 Current Tool Event Formats
21
-
22
- #### Copilot Format (Independent Events)
23
- ```javascript
24
- // Two separate events in the stream
25
- {
26
- type: 'tool.execution_start',
27
- timestamp: '2024-01-01T10:00:00Z',
28
- data: {
29
- toolCallId: 'abc123',
30
- toolName: 'Read',
31
- arguments: { file_path: '/path/to/file' }
32
- }
33
- }
34
-
35
- {
36
- type: 'tool.execution_complete',
37
- timestamp: '2024-01-01T10:00:05Z',
38
- data: {
39
- toolCallId: 'abc123',
40
- toolName: 'Read',
41
- result: 'File contents...',
42
- error: null
43
- }
44
- }
45
-
46
- // After matching by sessionService._matchCopilotToolCalls():
47
- {
48
- type: 'assistant.message',
49
- data: {
50
- tools: [{
51
- type: 'tool_use',
52
- id: 'abc123',
53
- name: 'Read',
54
- input: { file_path: '/path/to/file' },
55
- result: 'File contents...',
56
- status: 'completed',
57
- _matched: true
58
- }]
59
- }
60
- }
61
- ```
62
-
63
- #### Claude Format (Embedded with Separate Result)
64
- ```javascript
65
- // User or Assistant message with tool_use
66
- {
67
- type: 'assistant',
68
- message: {
69
- content: [
70
- { type: 'text', text: 'Let me read that file.' },
71
- {
72
- type: 'tool_use',
73
- id: 'xyz789',
74
- name: 'Read',
75
- input: { file_path: '/path/to/file' }
76
- }
77
- ]
78
- }
79
- }
80
-
81
- // Later: User message with tool_result
82
- {
83
- type: 'user',
84
- message: {
85
- content: [
86
- {
87
- type: 'tool_result',
88
- tool_use_id: 'xyz789',
89
- content: 'File contents...'
90
- }
91
- ]
92
- }
93
- }
94
-
95
- // After normalization and matching by sessionService._normalizeEvent() + _matchClaudeToolResults():
96
- {
97
- type: 'assistant',
98
- data: {
99
- message: 'Let me read that file.',
100
- tools: [{
101
- type: 'tool_use',
102
- id: 'xyz789',
103
- name: 'Read',
104
- input: { file_path: '/path/to/file' },
105
- result: 'File contents...',
106
- _matched: true
107
- }]
108
- }
109
- }
110
- ```
111
-
112
- #### Pi-Mono Format (Already Merged)
113
- ```javascript
114
- // Pi-Mono parser already merges tools at parse time
115
- {
116
- type: 'assistant.message',
117
- timestamp: '2024-01-01T10:00:00Z',
118
- data: {
119
- message: 'Let me read that file.',
120
- tools: [{
121
- name: 'Read',
122
- input: { file_path: '/path/to/file' },
123
- result: 'File contents...',
124
- status: 'completed',
125
- isError: false
126
- }]
127
- }
128
- }
129
- ```
130
-
131
- ### 1.2 Current Frontend Format Checks
132
-
133
- **In `views/session-vue.ejs` (lines 1581-1605):**
134
- ```javascript
135
- const getToolGroups = (event) => {
136
- if (event.data?.tools && Array.isArray(event.data.tools)) {
137
- return event.data.tools.map(tool => {
138
- // Claude format check: {type: 'tool_use', name, input, _matched, result}
139
- if (tool.type === 'tool_use') {
140
- return {
141
- tool: tool.name,
142
- start: { data: { toolName: tool.name, arguments: tool.input } },
143
- complete: tool._matched ? {
144
- data: { result: tool.result, isError: tool.error ? true : false }
145
- } : null
146
- };
147
- }
148
- // Copilot/Pi-Mono format (already matched)
149
- return tool;
150
- });
151
- }
152
- return [];
153
- };
154
- ```
155
-
156
- **In `views/time-analyze.ejs` (lines 1088-1098, 1350-1360):**
157
- ```javascript
158
- // Multiple places checking for embedded tools
159
- if (e.type === 'assistant.message' && e.data?.tools) {
160
- for (const tool of e.data.tools) {
161
- innerEvents.push({
162
- type: 'tool.execution_start',
163
- timestamp: e.timestamp,
164
- data: { tool: tool.name, toolName: tool.name, arguments: tool.input }
165
- });
166
- // ... similar expansion logic
167
- }
168
- }
169
- ```
170
-
171
- ### 1.3 Issues with Current Approach
172
-
173
- 1. **Scattered Format Logic:** Frontend needs to understand internal matching state (`_matched` flag)
174
- 2. **Inconsistent Tool Schema:** Different fields across formats (`status` vs `_matched`, `isError` vs `error`)
175
- 3. **Duplicate Code:** Multiple places converting between formats
176
- 4. **Brittleness:** Changes to parser matching logic require frontend updates
177
- 5. **Testing Complexity:** Need to test all format variations in frontend code
178
-
179
- ---
180
-
181
- ## 2. Proposed Unified Format
182
-
183
- ### 2.1 Normalized Tool Schema
184
-
185
- **Single canonical tool representation:**
186
-
187
- ```typescript
188
- interface UnifiedToolCall {
189
- // Core identification
190
- id: string; // Unique tool call ID
191
- name: string; // Tool name (e.g., 'Read', 'Write', 'Bash')
192
-
193
- // Timing
194
- startTime: string; // ISO 8601 timestamp
195
- endTime: string | null; // ISO 8601 timestamp, null if still running
196
-
197
- // Status
198
- status: 'pending' | 'running' | 'completed' | 'error';
199
-
200
- // Input/Output
201
- input: Record<string, any>; // Tool parameters
202
- result: string | null; // Tool result (null if not completed)
203
- error: string | null; // Error message (null if no error)
204
-
205
- // Metadata (optional, for advanced use cases)
206
- metadata?: {
207
- source?: string; // 'copilot' | 'claude' | 'pi-mono'
208
- subagentId?: string; // If tool was executed by subagent
209
- retryCount?: number; // Number of retries
210
- duration?: number; // Duration in milliseconds
211
- };
212
- }
213
- ```
214
-
215
- ### 2.2 Normalized Assistant Message Schema
216
-
217
- ```typescript
218
- interface UnifiedAssistantMessage {
219
- type: 'assistant.message';
220
- id: string;
221
- timestamp: string;
222
- parentId?: string;
223
-
224
- data: {
225
- message: string; // Text content
226
- model?: string; // Model used
227
- tools?: UnifiedToolCall[]; // Array of tools (if any)
228
- usage?: { // Token usage
229
- inputTokens?: number;
230
- outputTokens?: number;
231
- };
232
- };
233
-
234
- // Internal metadata (not for frontend consumption)
235
- _fileIndex?: number;
236
- _turnNumber?: number;
237
- _subagent?: {
238
- id: string;
239
- name: string;
240
- };
241
- }
242
- ```
243
-
244
- ### 2.3 Timeline Event Schema (for time-analyze views)
245
-
246
- ```typescript
247
- interface UnifiedTimelineEvent {
248
- type: 'user.message' | 'assistant.turn_start' | 'assistant.turn_complete'
249
- | 'tool.execution_start' | 'tool.execution_complete'
250
- | 'subagent.started' | 'subagent.completed';
251
- id: string;
252
- timestamp: string;
253
- parentId?: string;
254
-
255
- data: {
256
- message?: string;
257
- tool?: UnifiedToolCall; // For tool events
258
- toolCallId?: string; // For subagent events
259
- agentName?: string; // For subagent events
260
- [key: string]: any; // Flexible for other event types
261
- };
262
- }
263
- ```
264
-
265
- ---
266
-
267
- ## 3. Transformation Strategy
268
-
269
- ### 3.1 Where to Transform
270
-
271
- **Recommendation: Transform at the SessionService API layer** (Option B)
272
-
273
- **Rationale:**
274
- - ✅ Single point of transformation
275
- - ✅ Frontend receives clean, consistent data
276
- - ✅ Parsers remain focused on format-specific reading
277
- - ✅ Easier to test (one transformation module vs. three parsers)
278
- - ✅ Simpler to add new formats in the future
279
-
280
- **Architecture:**
281
-
282
- ```
283
- [Raw Files] → [Parser Layer] → [Matching Layer] → [Normalization Layer] → [API Response]
284
- (Pi-Mono, (Source- (Unified Schema) (Clean JSON)
285
- Copilot, Specific)
286
- Claude)
287
- ```
288
-
289
- ### 3.2 Implementation Approach
290
-
291
- #### Phase 1: Add Unified Normalizer Module
292
-
293
- Create `src/services/eventNormalizer.js`:
294
-
295
- ```javascript
296
- class EventNormalizer {
297
- /**
298
- * Normalize all events to unified format
299
- * @param {Array} events - Raw events from parsers
300
- * @param {string} source - 'copilot' | 'claude' | 'pi-mono'
301
- * @returns {Array} - Normalized events
302
- */
303
- normalizeEvents(events, source) {
304
- return events.map(event => this.normalizeEvent(event, source));
305
- }
306
-
307
- /**
308
- * Normalize a single event
309
- */
310
- normalizeEvent(event, source) {
311
- // Handle assistant messages with tools
312
- if (this._isAssistantMessage(event)) {
313
- return this._normalizeAssistantMessage(event, source);
314
- }
315
-
316
- // Handle timeline events
317
- if (this._isTimelineEvent(event)) {
318
- return this._normalizeTimelineEvent(event, source);
319
- }
320
-
321
- // Pass through other events
322
- return event;
323
- }
324
-
325
- /**
326
- * Normalize assistant message with embedded tools
327
- */
328
- _normalizeAssistantMessage(event, source) {
329
- const normalized = { ...event };
330
-
331
- if (event.data?.tools && Array.isArray(event.data.tools)) {
332
- normalized.data.tools = event.data.tools.map(tool =>
333
- this._normalizeToolCall(tool, source, event.timestamp)
334
- );
335
- }
336
-
337
- return normalized;
338
- }
339
-
340
- /**
341
- * Normalize a tool call to unified schema
342
- */
343
- _normalizeToolCall(tool, source, messageTimestamp) {
344
- // Handle Copilot/Claude format with _matched flag
345
- if (tool.type === 'tool_use') {
346
- return {
347
- id: tool.id,
348
- name: tool.name,
349
- startTime: messageTimestamp,
350
- endTime: tool._matched ? messageTimestamp : null,
351
- status: this._computeStatus(tool),
352
- input: tool.input || {},
353
- result: tool.result || null,
354
- error: tool.error || null,
355
- metadata: {
356
- source,
357
- matched: tool._matched
358
- }
359
- };
360
- }
361
-
362
- // Handle Pi-Mono format (already has status)
363
- if (tool.name && tool.status) {
364
- return {
365
- id: tool.id || this._generateToolId(),
366
- name: tool.name,
367
- startTime: messageTimestamp,
368
- endTime: tool.status === 'completed' ? messageTimestamp : null,
369
- status: tool.status,
370
- input: tool.input || {},
371
- result: tool.result || null,
372
- error: tool.isError ? tool.result : null,
373
- metadata: { source }
374
- };
375
- }
376
-
377
- // Fallback: return as-is with minimal normalization
378
- return {
379
- id: tool.id || this._generateToolId(),
380
- name: tool.name || 'unknown',
381
- startTime: messageTimestamp,
382
- endTime: null,
383
- status: 'running',
384
- input: tool.input || {},
385
- result: null,
386
- error: null,
387
- metadata: { source }
388
- };
389
- }
390
-
391
- _computeStatus(tool) {
392
- if (tool.error) return 'error';
393
- if (tool._matched && tool.result !== undefined) return 'completed';
394
- if (tool._matched === false) return 'running';
395
- return 'completed'; // Default for matched tools
396
- }
397
-
398
- _generateToolId() {
399
- return `tool-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
400
- }
401
-
402
- _isAssistantMessage(event) {
403
- return event.type === 'assistant.message' || event.type === 'assistant';
404
- }
405
-
406
- _isTimelineEvent(event) {
407
- return event.type?.startsWith('tool.') ||
408
- event.type?.startsWith('subagent.');
409
- }
410
-
411
- _normalizeTimelineEvent(event, source) {
412
- // For tool.execution_start/complete, ensure consistent schema
413
- if (event.type === 'tool.execution_start' || event.type === 'tool.execution_complete') {
414
- return {
415
- ...event,
416
- data: {
417
- ...event.data,
418
- toolCallId: event.data?.toolCallId || event.data?.id,
419
- toolName: event.data?.toolName || event.data?.tool || event.data?.name
420
- }
421
- };
422
- }
423
-
424
- return event;
425
- }
426
- }
427
-
428
- module.exports = EventNormalizer;
429
- ```
430
-
431
- #### Phase 2: Integrate into SessionService
432
-
433
- Modify `src/services/sessionService.js`:
434
-
435
- ```javascript
436
- const EventNormalizer = require('./eventNormalizer');
437
-
438
- class SessionService {
439
- constructor(sessionDir) {
440
- // ... existing code ...
441
- this.normalizer = new EventNormalizer();
442
- }
443
-
444
- async getSessionEvents(sessionId) {
445
- // ... existing parsing logic ...
446
-
447
- // After expansion but before return:
448
- events = this.normalizer.normalizeEvents(events, session.source);
449
-
450
- return events;
451
- }
452
- }
453
- ```
454
-
455
- #### Phase 3: Simplify Frontend Code
456
-
457
- **Before (session-vue.ejs):**
458
- ```javascript
459
- const getToolGroups = (event) => {
460
- if (event.data?.tools && Array.isArray(event.data.tools)) {
461
- return event.data.tools.map(tool => {
462
- if (tool.type === 'tool_use') {
463
- return {
464
- tool: tool.name,
465
- start: { data: { toolName: tool.name, arguments: tool.input } },
466
- complete: tool._matched ? {
467
- data: { result: tool.result, isError: tool.error ? true : false }
468
- } : null
469
- };
470
- }
471
- return tool;
472
- });
473
- }
474
- return [];
475
- };
476
- ```
477
-
478
- **After:**
479
- ```javascript
480
- const getToolGroups = (event) => {
481
- // Tools are already normalized to unified format
482
- return event.data?.tools || [];
483
- };
484
-
485
- const getToolDuration = (tool) => {
486
- if (tool.startTime && tool.endTime) {
487
- const ms = new Date(tool.endTime) - new Date(tool.startTime);
488
- return formatDuration(ms);
489
- }
490
- return null;
491
- };
492
-
493
- const getToolStatus = (tool) => {
494
- switch (tool.status) {
495
- case 'completed': return { icon: '✓', color: 'text-success' };
496
- case 'error': return { icon: '✗', color: 'text-error' };
497
- case 'running': return { icon: '⋯', color: 'text-warning' };
498
- default: return { icon: '?', color: 'text-muted' };
499
- }
500
- };
501
- ```
502
-
503
- ---
504
-
505
- ## 4. Code Change Checklist
506
-
507
- ### 4.1 Backend Changes
508
-
509
- #### New Files
510
- - [ ] `src/services/eventNormalizer.js` - Main normalization module
511
- - [ ] `src/services/__tests__/eventNormalizer.test.js` - Unit tests
512
-
513
- #### Modified Files
514
- - [ ] `src/services/sessionService.js`
515
- - [ ] Import EventNormalizer
516
- - [ ] Add normalizer instance to constructor
517
- - [ ] Call `normalizer.normalizeEvents()` in `getSessionEvents()` after expansion
518
- - [ ] Remove frontend-specific format conversion logic (keep matching logic)
519
-
520
- - [ ] `lib/parsers/pi-mono-parser.js`
521
- - [ ] No changes needed (parser output will be normalized by eventNormalizer)
522
-
523
- - [ ] `package.json`
524
- - [ ] No new dependencies needed
525
-
526
- #### API Contract
527
- - [ ] Document unified event schema in API docs
528
- - [ ] Add API version header for gradual migration (optional)
529
-
530
- ### 4.2 Frontend Changes
531
-
532
- #### Modified Files
533
- - [ ] `views/session-vue.ejs`
534
- - [ ] Simplify `getToolGroups()` - remove format checks
535
- - [ ] Simplify `getToolStatus()` - use unified `status` field
536
- - [ ] Add `getToolDuration()` - calculate from `startTime`/`endTime`
537
- - [ ] Remove `tool.type === 'tool_use'` checks
538
- - [ ] Remove `_matched` flag checks
539
- - [ ] Use `tool.error` directly instead of `tool.isError`
540
-
541
- - [ ] `views/time-analyze.ejs`
542
- - [ ] Remove embedded tool expansion logic (lines ~1088-1098, 1350-1360)
543
- - [ ] Use timeline events directly (already expanded by backend)
544
- - [ ] Simplify tool event markers (use unified schema)
545
-
546
- - [ ] `views/time-analyze-v2.ejs`
547
- - [ ] Similar changes to time-analyze.ejs
548
- - [ ] Update Gantt chart generation to use unified schema
549
-
550
- #### Removed Code
551
- - [ ] All `tool.type === 'tool_use'` conditionals
552
- - [ ] All `_matched` flag checks
553
- - [ ] Format-specific tool expansion loops
554
- - [ ] Duplicate status/error mapping logic
555
-
556
- ### 4.3 Testing Changes
557
-
558
- #### New Tests
559
- - [ ] `eventNormalizer.test.js`
560
- - [ ] Test Copilot format normalization
561
- - [ ] Test Claude format normalization
562
- - [ ] Test Pi-Mono format normalization
563
- - [ ] Test tool status computation
564
- - [ ] Test error handling
565
- - [ ] Test edge cases (missing fields, orphaned events)
566
-
567
- #### Updated Tests
568
- - [ ] `sessionService.test.js`
569
- - [ ] Update assertions to check for unified format
570
- - [ ] Add integration tests with normalizer
571
-
572
- #### Manual Testing
573
- - [ ] Load Copilot session, verify tools display correctly
574
- - [ ] Load Claude session, verify tools display correctly
575
- - [ ] Load Pi-Mono session, verify tools display correctly
576
- - [ ] Test time-analyze view with all formats
577
- - [ ] Test subagent tool attribution
578
- - [ ] Test error states and edge cases
579
-
580
- ---
581
-
582
- ## 5. Migration Considerations
583
-
584
- ### 5.1 Backward Compatibility
585
-
586
- **No Breaking Changes Required:**
587
- - Current frontend code will continue to work during transition
588
- - Unified format is a superset of existing formats
589
- - Can add feature flag for gradual rollout
590
-
591
- ### 5.2 Feature Flags
592
-
593
- ```javascript
594
- // In sessionService.js
595
- class SessionService {
596
- constructor(sessionDir, options = {}) {
597
- this.useUnifiedFormat = options.useUnifiedFormat ?? true;
598
- this.normalizer = new EventNormalizer();
599
- }
600
-
601
- async getSessionEvents(sessionId) {
602
- // ... existing code ...
603
-
604
- if (this.useUnifiedFormat) {
605
- events = this.normalizer.normalizeEvents(events, session.source);
606
- }
607
-
608
- return events;
609
- }
610
- }
611
- ```
612
-
613
- ### 5.3 Rollout Plan
614
-
615
- **Phase 1: Backend (Week 1-2)**
616
- 1. Implement EventNormalizer module with tests
617
- 2. Integrate into SessionService with feature flag (default ON)
618
- 3. Run backend tests to ensure no regressions
619
- 4. Deploy to dev environment
620
-
621
- **Phase 2: Frontend (Week 3)**
622
- 1. Update session-vue.ejs to use unified format
623
- 2. Test manually with all three formats
624
- 3. Fix any edge cases discovered
625
- 4. Deploy to staging
626
-
627
- **Phase 3: Timeline Views (Week 4)**
628
- 1. Update time-analyze.ejs and time-analyze-v2.ejs
629
- 2. Test Gantt chart generation
630
- 3. Verify performance with large sessions
631
- 4. Deploy to production
632
-
633
- **Phase 4: Cleanup (Week 5)**
634
- 1. Remove feature flag
635
- 2. Remove dead code (old format checks)
636
- 3. Update documentation
637
- 4. Add inline comments for future maintainers
638
-
639
- ### 5.4 Monitoring
640
-
641
- **Metrics to Track:**
642
- - API response time (should not increase)
643
- - Frontend render time (should decrease slightly)
644
- - Error rates (should not increase)
645
- - User-reported bugs (should decrease over time)
646
-
647
- **Rollback Strategy:**
648
- - Keep feature flag for quick rollback
649
- - Monitor logs for normalization errors
650
- - Have old code ready to revert if needed
651
-
652
- ---
653
-
654
- ## 6. Benefits Summary
655
-
656
- ### 6.1 Code Quality
657
- - ✅ **Reduced Complexity:** ~200 lines of frontend code removed
658
- - ✅ **Single Source of Truth:** All format logic in one module
659
- - ✅ **Easier Testing:** One set of tests instead of scattered checks
660
- - ✅ **Better Maintainability:** Changes in one place, not three
661
-
662
- ### 6.2 Developer Experience
663
- - ✅ **Simpler Frontend:** No format checks needed
664
- - ✅ **Clear API Contract:** Well-documented unified schema
665
- - ✅ **Faster Development:** Less time debugging format issues
666
- - ✅ **Easier Onboarding:** New devs learn one format, not three
667
-
668
- ### 6.3 Future-Proofing
669
- - ✅ **Easy to Extend:** Add new formats by updating normalizer only
670
- - ✅ **API Versioning:** Can version schema independently of frontend
671
- - ✅ **Tool Evolution:** Add new tool features without breaking frontends
672
- - ✅ **Multi-Client Support:** Web, mobile, CLI can all use same API
673
-
674
- ---
675
-
676
- ## 7. Alternative Approaches Considered
677
-
678
- ### 7.1 Option A: Normalize in Parsers (REJECTED)
679
-
680
- **Approach:** Make each parser output unified format directly.
681
-
682
- **Pros:**
683
- - Format handling closest to source
684
- - Parser owns full transformation
685
-
686
- **Cons:**
687
- - ❌ Duplicates normalization logic across 3 parsers
688
- - ❌ Harder to ensure consistency
689
- - ❌ Parsers become more complex
690
- - ❌ Testing burden multiplied by 3
691
-
692
- ### 7.2 Option C: Normalize in Frontend (REJECTED)
693
-
694
- **Approach:** Keep backend as-is, do all normalization in frontend views.
695
-
696
- **Pros:**
697
- - No backend changes
698
- - Frontend controls its own format
699
-
700
- **Cons:**
701
- - ❌ Duplicates logic across multiple views
702
- - ❌ Each view must understand all formats
703
- - ❌ API remains inconsistent
704
- - ❌ Harder to add new clients (mobile, CLI)
705
-
706
- ### 7.3 Option D: GraphQL Schema (FUTURE)
707
-
708
- **Approach:** Use GraphQL to define unified schema with resolvers.
709
-
710
- **Pros:**
711
- - ✅ Strongly typed schema
712
- - ✅ Client-controlled queries
713
- - ✅ Better for complex queries
714
-
715
- **Cons:**
716
- - ❌ Much larger implementation effort
717
- - ❌ Overkill for current needs
718
- - ❌ Learning curve for team
719
-
720
- **Status:** Consider for future major refactoring if API complexity grows significantly.
721
-
722
- ---
723
-
724
- ## 8. Open Questions & Risks
725
-
726
- ### 8.1 Open Questions
727
-
728
- 1. **Tool Result Size:** Should we truncate large tool results (>10KB) in the API response?
729
- - **Recommendation:** Add `result_preview` field (first 1000 chars), keep full result available on demand
730
-
731
- 2. **Streaming:** How to handle streaming tool execution in real-time?
732
- - **Recommendation:** Out of scope for initial implementation, revisit if WebSocket support is added
733
-
734
- 3. **Tool Metadata:** What other metadata should be included?
735
- - **Recommendation:** Start minimal, add as needed based on user feedback
736
-
737
- ### 8.2 Risks & Mitigation
738
-
739
- | Risk | Impact | Probability | Mitigation |
740
- |------|--------|-------------|------------|
741
- | Performance regression | Medium | Low | Benchmark before/after, optimize normalizer |
742
- | Breaking existing code | High | Low | Comprehensive tests, feature flag, gradual rollout |
743
- | Incomplete normalization | Medium | Medium | Unit tests for all edge cases, manual testing |
744
- | Schema evolution issues | Low | Medium | Version API, document schema clearly |
745
-
746
- ---
747
-
748
- ## 9. Success Criteria
749
-
750
- ### 9.1 Technical Metrics
751
- - [ ] All tool events use unified schema (100% coverage)
752
- - [ ] Zero frontend format checks remaining
753
- - [ ] Test coverage >90% for normalizer module
754
- - [ ] API response time <10% increase
755
- - [ ] Frontend render time ≥10% decrease (due to simpler logic)
756
-
757
- ### 9.2 Code Metrics
758
- - [ ] ≥150 lines of frontend code removed
759
- - [ ] ≤300 lines of backend code added (normalizer)
760
- - [ ] Net reduction in total LOC
761
- - [ ] Cyclomatic complexity reduced in frontend
762
-
763
- ### 9.3 Quality Metrics
764
- - [ ] Zero P0/P1 bugs introduced
765
- - [ ] All existing features work as before
766
- - [ ] New format handles all edge cases
767
- - [ ] Documentation updated
768
-
769
- ---
770
-
771
- ## 10. Appendix
772
-
773
- ### 10.1 Example API Response (Before vs After)
774
-
775
- **Before (Claude format):**
776
- ```json
777
- {
778
- "type": "assistant",
779
- "data": {
780
- "message": "Let me read that file.",
781
- "tools": [{
782
- "type": "tool_use",
783
- "id": "xyz789",
784
- "name": "Read",
785
- "input": { "file_path": "/path/to/file" },
786
- "result": "File contents...",
787
- "_matched": true,
788
- "error": null
789
- }]
790
- }
791
- }
792
- ```
793
-
794
- **After (Unified format):**
795
- ```json
796
- {
797
- "type": "assistant.message",
798
- "data": {
799
- "message": "Let me read that file.",
800
- "tools": [{
801
- "id": "xyz789",
802
- "name": "Read",
803
- "startTime": "2024-01-01T10:00:00Z",
804
- "endTime": "2024-01-01T10:00:05Z",
805
- "status": "completed",
806
- "input": { "file_path": "/path/to/file" },
807
- "result": "File contents...",
808
- "error": null,
809
- "metadata": {
810
- "source": "claude",
811
- "duration": 5000
812
- }
813
- }]
814
- }
815
- }
816
- ```
817
-
818
- ### 10.2 Schema Changelog
819
-
820
- | Version | Date | Changes |
821
- |---------|------|---------|
822
- | 1.0 | 2026-02 | Initial unified schema |
823
- | 1.1 | TBD | Add `result_preview` field (future) |
824
- | 2.0 | TBD | Add streaming support (future) |
825
-
826
- ### 10.3 References
827
-
828
- - Current code: `src/services/sessionService.js` (lines 206-237, 458-649)
829
- - Frontend format checks: `views/session-vue.ejs` (lines 1581-1605)
830
- - Pi-Mono parser: `lib/parsers/pi-mono-parser.js` (lines 124-217)
831
-
832
- ---
833
-
834
- ## Document History
835
-
836
- | Date | Author | Changes |
837
- |------|--------|---------|
838
- | 2026-02-22 | Claude | Initial design document |
839
-
840
- ---
841
-
842
- **Status:** ✅ Ready for Review
843
- **Priority:** P1 - High Impact, Low Risk
844
- **Estimated Effort:** 3-4 weeks (1 developer)