@codemieai/code 0.4.2 → 0.5.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 (132) hide show
  1. package/README.md +7 -1
  2. package/bin/codemie-kimi-acp.js +18 -0
  3. package/bin/codemie-kimi.js +18 -0
  4. package/dist/agents/core/AgentCLI.d.ts.map +1 -1
  5. package/dist/agents/core/AgentCLI.js +4 -0
  6. package/dist/agents/core/AgentCLI.js.map +1 -1
  7. package/dist/agents/core/BaseAgentAdapter.d.ts.map +1 -1
  8. package/dist/agents/core/BaseAgentAdapter.js +26 -6
  9. package/dist/agents/core/BaseAgentAdapter.js.map +1 -1
  10. package/dist/agents/core/metrics/types.d.ts +1 -1
  11. package/dist/agents/core/metrics/types.d.ts.map +1 -1
  12. package/dist/agents/core/session/BaseProcessor.d.ts +2 -0
  13. package/dist/agents/core/session/BaseProcessor.d.ts.map +1 -1
  14. package/dist/agents/core/session/BaseSessionAdapter.d.ts +7 -0
  15. package/dist/agents/core/session/BaseSessionAdapter.d.ts.map +1 -1
  16. package/dist/agents/core/types.d.ts +30 -0
  17. package/dist/agents/core/types.d.ts.map +1 -1
  18. package/dist/agents/plugins/claude/claude.plugin.js +3 -3
  19. package/dist/agents/plugins/claude/claude.session.d.ts.map +1 -1
  20. package/dist/agents/plugins/claude/claude.session.js +21 -6
  21. package/dist/agents/plugins/claude/claude.session.js.map +1 -1
  22. package/dist/agents/plugins/gemini/gemini.plugin.js.map +1 -1
  23. package/dist/agents/plugins/gemini/session/processors/gemini.metrics-processor.d.ts.map +1 -1
  24. package/dist/agents/plugins/gemini/session/processors/gemini.metrics-processor.js +3 -0
  25. package/dist/agents/plugins/gemini/session/processors/gemini.metrics-processor.js.map +1 -1
  26. package/dist/agents/plugins/kimi/extension/SKILL.md +6 -0
  27. package/dist/agents/plugins/kimi/extension/manifest.json +8 -0
  28. package/dist/agents/plugins/kimi/kimi-acp.plugin.d.ts +7 -0
  29. package/dist/agents/plugins/kimi/kimi-acp.plugin.d.ts.map +1 -0
  30. package/dist/agents/plugins/kimi/kimi-acp.plugin.js +28 -0
  31. package/dist/agents/plugins/kimi/kimi-acp.plugin.js.map +1 -0
  32. package/dist/agents/plugins/kimi/kimi.extension-installer.d.ts +56 -0
  33. package/dist/agents/plugins/kimi/kimi.extension-installer.d.ts.map +1 -0
  34. package/dist/agents/plugins/kimi/kimi.extension-installer.js +79 -0
  35. package/dist/agents/plugins/kimi/kimi.extension-installer.js.map +1 -0
  36. package/dist/agents/plugins/kimi/kimi.hook-config-injector.d.ts +34 -0
  37. package/dist/agents/plugins/kimi/kimi.hook-config-injector.d.ts.map +1 -0
  38. package/dist/agents/plugins/kimi/kimi.hook-config-injector.js +158 -0
  39. package/dist/agents/plugins/kimi/kimi.hook-config-injector.js.map +1 -0
  40. package/dist/agents/plugins/kimi/kimi.hook-transformer.d.ts +23 -0
  41. package/dist/agents/plugins/kimi/kimi.hook-transformer.d.ts.map +1 -0
  42. package/dist/agents/plugins/kimi/kimi.hook-transformer.js +46 -0
  43. package/dist/agents/plugins/kimi/kimi.hook-transformer.js.map +1 -0
  44. package/dist/agents/plugins/kimi/kimi.models.d.ts +8 -0
  45. package/dist/agents/plugins/kimi/kimi.models.d.ts.map +1 -0
  46. package/dist/agents/plugins/kimi/kimi.models.js +174 -0
  47. package/dist/agents/plugins/kimi/kimi.models.js.map +1 -0
  48. package/dist/agents/plugins/kimi/kimi.paths.d.ts +65 -0
  49. package/dist/agents/plugins/kimi/kimi.paths.d.ts.map +1 -0
  50. package/dist/agents/plugins/kimi/kimi.paths.js +95 -0
  51. package/dist/agents/plugins/kimi/kimi.paths.js.map +1 -0
  52. package/dist/agents/plugins/kimi/kimi.plugin.d.ts +37 -0
  53. package/dist/agents/plugins/kimi/kimi.plugin.d.ts.map +1 -0
  54. package/dist/agents/plugins/kimi/kimi.plugin.js +318 -0
  55. package/dist/agents/plugins/kimi/kimi.plugin.js.map +1 -0
  56. package/dist/agents/plugins/kimi/kimi.session.d.ts +40 -0
  57. package/dist/agents/plugins/kimi/kimi.session.d.ts.map +1 -0
  58. package/dist/agents/plugins/kimi/kimi.session.js +191 -0
  59. package/dist/agents/plugins/kimi/kimi.session.js.map +1 -0
  60. package/dist/agents/plugins/kimi/session/processors/kimi.metrics-processor.d.ts +43 -0
  61. package/dist/agents/plugins/kimi/session/processors/kimi.metrics-processor.d.ts.map +1 -0
  62. package/dist/agents/plugins/kimi/session/processors/kimi.metrics-processor.js +421 -0
  63. package/dist/agents/plugins/kimi/session/processors/kimi.metrics-processor.js.map +1 -0
  64. package/dist/agents/plugins/kimi/session/types.d.ts +81 -0
  65. package/dist/agents/plugins/kimi/session/types.d.ts.map +1 -0
  66. package/dist/agents/plugins/kimi/session/types.js +5 -0
  67. package/dist/agents/plugins/kimi/session/types.js.map +1 -0
  68. package/dist/agents/registry.d.ts.map +1 -1
  69. package/dist/agents/registry.js +4 -0
  70. package/dist/agents/registry.js.map +1 -1
  71. package/dist/cli/commands/analytics/cost/cost-enricher.d.ts.map +1 -1
  72. package/dist/cli/commands/analytics/cost/cost-enricher.js +67 -1
  73. package/dist/cli/commands/analytics/cost/cost-enricher.js.map +1 -1
  74. package/dist/cli/commands/analytics/cost/dispatch-extractor.d.ts +2 -2
  75. package/dist/cli/commands/analytics/cost/dispatch-extractor.d.ts.map +1 -1
  76. package/dist/cli/commands/analytics/cost/dispatch-extractor.js +6 -4
  77. package/dist/cli/commands/analytics/cost/dispatch-extractor.js.map +1 -1
  78. package/dist/cli/commands/analytics/cost/pricing.json +9 -2
  79. package/dist/cli/commands/analytics/cost/types.d.ts +13 -0
  80. package/dist/cli/commands/analytics/cost/types.d.ts.map +1 -1
  81. package/dist/cli/commands/analytics/cost/types.js.map +1 -1
  82. package/dist/cli/commands/analytics/cost/usage-readers.d.ts +13 -4
  83. package/dist/cli/commands/analytics/cost/usage-readers.d.ts.map +1 -1
  84. package/dist/cli/commands/analytics/cost/usage-readers.js +115 -27
  85. package/dist/cli/commands/analytics/cost/usage-readers.js.map +1 -1
  86. package/dist/cli/commands/analytics/model-normalizer.d.ts +2 -0
  87. package/dist/cli/commands/analytics/model-normalizer.d.ts.map +1 -1
  88. package/dist/cli/commands/analytics/model-normalizer.js +8 -0
  89. package/dist/cli/commands/analytics/model-normalizer.js.map +1 -1
  90. package/dist/cli/commands/analytics/report/client/app.js +233 -120
  91. package/dist/cli/commands/analytics/report/template.html +28 -4
  92. package/dist/cli/commands/hook.d.ts.map +1 -1
  93. package/dist/cli/commands/hook.js +24 -7
  94. package/dist/cli/commands/hook.js.map +1 -1
  95. package/dist/providers/index.d.ts +2 -0
  96. package/dist/providers/index.d.ts.map +1 -1
  97. package/dist/providers/index.js +2 -0
  98. package/dist/providers/index.js.map +1 -1
  99. package/dist/providers/plugins/moonshot-subscription/index.d.ts +9 -0
  100. package/dist/providers/plugins/moonshot-subscription/index.d.ts.map +1 -0
  101. package/dist/providers/plugins/moonshot-subscription/index.js +9 -0
  102. package/dist/providers/plugins/moonshot-subscription/index.js.map +1 -0
  103. package/dist/providers/plugins/moonshot-subscription/moonshot-subscription.setup-steps.d.ts +8 -0
  104. package/dist/providers/plugins/moonshot-subscription/moonshot-subscription.setup-steps.d.ts.map +1 -0
  105. package/dist/providers/plugins/moonshot-subscription/moonshot-subscription.setup-steps.js +80 -0
  106. package/dist/providers/plugins/moonshot-subscription/moonshot-subscription.setup-steps.js.map +1 -0
  107. package/dist/providers/plugins/moonshot-subscription/moonshot-subscription.template.d.ts +11 -0
  108. package/dist/providers/plugins/moonshot-subscription/moonshot-subscription.template.d.ts.map +1 -0
  109. package/dist/providers/plugins/moonshot-subscription/moonshot-subscription.template.js +111 -0
  110. package/dist/providers/plugins/moonshot-subscription/moonshot-subscription.template.js.map +1 -0
  111. package/dist/providers/plugins/sso/proxy/plugins/index.d.ts +2 -1
  112. package/dist/providers/plugins/sso/proxy/plugins/index.d.ts.map +1 -1
  113. package/dist/providers/plugins/sso/proxy/plugins/index.js +3 -1
  114. package/dist/providers/plugins/sso/proxy/plugins/index.js.map +1 -1
  115. package/dist/providers/plugins/sso/proxy/plugins/kimi-request-normalizer.plugin.d.ts +18 -0
  116. package/dist/providers/plugins/sso/proxy/plugins/kimi-request-normalizer.plugin.d.ts.map +1 -0
  117. package/dist/providers/plugins/sso/proxy/plugins/kimi-request-normalizer.plugin.js +56 -0
  118. package/dist/providers/plugins/sso/proxy/plugins/kimi-request-normalizer.plugin.js.map +1 -0
  119. package/dist/providers/plugins/sso/sso.auth.d.ts +5 -0
  120. package/dist/providers/plugins/sso/sso.auth.d.ts.map +1 -1
  121. package/dist/providers/plugins/sso/sso.auth.js +23 -1
  122. package/dist/providers/plugins/sso/sso.auth.js.map +1 -1
  123. package/dist/utils/config.d.ts +24 -0
  124. package/dist/utils/config.d.ts.map +1 -1
  125. package/dist/utils/config.js +83 -8
  126. package/dist/utils/config.js.map +1 -1
  127. package/dist/utils/native-installer.d.ts.map +1 -1
  128. package/dist/utils/native-installer.js +6 -3
  129. package/dist/utils/native-installer.js.map +1 -1
  130. package/package.json +4 -1
  131. package/scripts/copy-plugins.js +5 -0
  132. package/scripts/test-desktop-telemetry.js +1 -1
@@ -155,6 +155,93 @@
155
155
  return cv;
156
156
  }
157
157
 
158
+ var INVOCATION_CHARTS = [
159
+ { kind: 'skill', title: 'Skills invoked', color: '#7c4fff' },
160
+ { kind: 'agent', title: 'Agent subtypes', color: '#ff6b6b' },
161
+ { kind: 'command', title: 'Slash commands', color: '#4a9eff' },
162
+ ];
163
+
164
+ function appendToolsAndModels(host, fs, chartFn) {
165
+ var toolAgg = new Map();
166
+ fs.forEach(function (s) {
167
+ (s.tools || []).forEach(function (t) {
168
+ var cur = toolAgg.get(t.toolName) || { total: 0, success: 0, failure: 0 };
169
+ cur.total += t.totalCalls; cur.success += t.successCount; cur.failure += t.failureCount;
170
+ toolAgg.set(t.toolName, cur);
171
+ });
172
+ });
173
+ var tools = Array.from(toolAgg.entries()).map(function (e) { return { name: e[0], total: e[1].total, success: e[1].success, rate: e[1].total ? Math.round((e[1].success / e[1].total) * 100) : 0 }; })
174
+ .sort(function (a, b) { return b.total - a.total; });
175
+ var maxTool = tools.length ? tools[0].total : 1;
176
+
177
+ var row = el('div', 'grid-2 mb16');
178
+ var toolCard = card('Tool usage & success rate');
179
+ tools.slice(0, 12).forEach(function (t) {
180
+ var bar = el('div', 'bar-row');
181
+ var color = t.rate >= 90 ? '#259F4C' : (t.rate >= 70 ? '#F5A534' : '#F9303C');
182
+ bar.innerHTML = '<span class="nm">' + esc(t.name) + '</span><div class="bar-track"><div class="bar-fill" style="width:' + Math.max(3, (t.total / maxTool) * 100) + '%;background:' + color + '"></div></div><span class="vl">' + fmtNum(t.total) + ' · ' + t.rate + '%</span>';
183
+ toolCard._body.appendChild(bar);
184
+ });
185
+ if (!tools.length) toolCard._body.appendChild(el('div', 'empty', 'No per-tool data.'));
186
+ row.appendChild(toolCard);
187
+
188
+ var modelAgg = new Map();
189
+ fs.forEach(function (s) { (s.perModelCost || []).forEach(function (m) { modelAgg.set(m.model, (modelAgg.get(m.model) || 0) + (m.tokens ? m.tokens.total : 0)); }); });
190
+ var modelCard = card('Tokens by model');
191
+ var mEntries = Array.from(modelAgg.entries()).sort(function (a, b) { return b[1] - a[1]; }).slice(0, 8);
192
+ if (mEntries.length) {
193
+ chartFn(canvasIn(modelCard._body), {
194
+ type: 'bar',
195
+ data: { labels: mEntries.map(function (e) { return e[0]; }), datasets: [{ data: mEntries.map(function (e) { return e[1]; }), backgroundColor: mEntries.map(function (_, i) { return PALETTE[i % PALETTE.length]; }), borderRadius: 5 }] },
196
+ options: { indexAxis: 'y', plugins: { legend: { display: false }, tooltip: { callbacks: { label: function (c) { return fmtTokens(c.parsed.x) + ' tokens'; } } } }, scales: { x: { grid: { color: GRID }, ticks: { callback: function (v) { return fmtTokens(v); } } }, y: { grid: { display: false } } } }
197
+ });
198
+ } else {
199
+ modelCard._body.appendChild(el('div', 'empty', 'No token data (sessions unpriced or no native logs).'));
200
+ }
201
+ row.appendChild(modelCard);
202
+ host.appendChild(row);
203
+ }
204
+
205
+ function appendInvocationCharts(host, fs, chartFn) {
206
+ INVOCATION_CHARTS.forEach(function (def) {
207
+ var agg = {};
208
+ fs.forEach(function (s) {
209
+ dispatchCounts(s, def.kind).forEach(function (x) {
210
+ agg[x.name] = (agg[x.name] || 0) + x.totalCalls;
211
+ });
212
+ });
213
+ var entries = Object.entries(agg)
214
+ .sort(function (a, b) { return b[1] - a[1]; })
215
+ .slice(0, 10);
216
+
217
+ var invCard = card(def.title);
218
+ if (entries.length) {
219
+ chartFn(canvasIn(invCard._body), {
220
+ type: 'bar',
221
+ data: {
222
+ labels: entries.map(function (e) { return e[0]; }),
223
+ datasets: [{
224
+ data: entries.map(function (e) { return e[1]; }),
225
+ backgroundColor: def.color,
226
+ borderRadius: 4,
227
+ }],
228
+ },
229
+ options: {
230
+ indexAxis: 'y',
231
+ plugins: { legend: { display: false } },
232
+ scales: {
233
+ x: { grid: { color: GRID }, ticks: { precision: 0 } },
234
+ y: { grid: { display: false } },
235
+ },
236
+ },
237
+ });
238
+ } else {
239
+ invCard._body.appendChild(el('div', 'empty', 'No data.'));
240
+ }
241
+ host.appendChild(invCard);
242
+ });
243
+ }
244
+
158
245
  // ---- small DOM builders -------------------------------------------------
159
246
  function el(tag, cls, html) { var e = document.createElement(tag); if (cls) e.className = cls; if (html != null) e.innerHTML = html; return e; }
160
247
  function card(title, sub) {
@@ -405,92 +492,8 @@
405
492
  host.appendChild(el('h2', 'view-title', 'Tools & Models'));
406
493
  host.appendChild(el('p', 'view-sub', 'Tool usage across sessions and model token/cost distribution.'));
407
494
  if (!fs.length) { host.appendChild(el('div', 'empty', 'No sessions in view.')); return; }
408
-
409
- // aggregate tools across sessions
410
- var toolAgg = new Map();
411
- fs.forEach(function (s) {
412
- (s.tools || []).forEach(function (t) {
413
- var cur = toolAgg.get(t.toolName) || { total: 0, success: 0, failure: 0 };
414
- cur.total += t.totalCalls; cur.success += t.successCount; cur.failure += t.failureCount;
415
- toolAgg.set(t.toolName, cur);
416
- });
417
- });
418
- var tools = Array.from(toolAgg.entries()).map(function (e) { return { name: e[0], total: e[1].total, success: e[1].success, rate: e[1].total ? Math.round((e[1].success / e[1].total) * 100) : 0 }; })
419
- .sort(function (a, b) { return b.total - a.total; });
420
- var maxTool = tools.length ? tools[0].total : 1;
421
-
422
- var row = el('div', 'grid-2 mb16');
423
- var toolCard = card('Tool usage & success rate');
424
- tools.slice(0, 12).forEach(function (t) {
425
- var bar = el('div', 'bar-row');
426
- var color = t.rate >= 90 ? '#259F4C' : (t.rate >= 70 ? '#F5A534' : '#F9303C');
427
- bar.innerHTML = '<span class="nm">' + esc(t.name) + '</span><div class="bar-track"><div class="bar-fill" style="width:' + Math.max(3, (t.total / maxTool) * 100) + '%;background:' + color + '"></div></div><span class="vl">' + fmtNum(t.total) + ' · ' + t.rate + '%</span>';
428
- toolCard._body.appendChild(bar);
429
- });
430
- if (!tools.length) toolCard._body.appendChild(el('div', 'empty', 'No per-tool data.'));
431
- row.appendChild(toolCard);
432
-
433
- // models by tokens (from perModelCost)
434
- var modelAgg = new Map();
435
- fs.forEach(function (s) { (s.perModelCost || []).forEach(function (m) { modelAgg.set(m.model, (modelAgg.get(m.model) || 0) + (m.tokens ? m.tokens.total : 0)); }); });
436
- var modelCard = card('Tokens by model');
437
- var mEntries = Array.from(modelAgg.entries()).sort(function (a, b) { return b[1] - a[1]; }).slice(0, 8);
438
- if (mEntries.length) {
439
- makeChart(canvasIn(modelCard._body), {
440
- type: 'bar',
441
- data: { labels: mEntries.map(function (e) { return e[0]; }), datasets: [{ data: mEntries.map(function (e) { return e[1]; }), backgroundColor: mEntries.map(function (_, i) { return PALETTE[i % PALETTE.length]; }), borderRadius: 5 }] },
442
- options: { indexAxis: 'y', plugins: { legend: { display: false }, tooltip: { callbacks: { label: function (c) { return fmtTokens(c.parsed.x) + ' tokens'; } } } }, scales: { x: { grid: { color: GRID }, ticks: { callback: function (v) { return fmtTokens(v); } } }, y: { grid: { display: false } } } }
443
- });
444
- } else {
445
- modelCard._body.appendChild(el('div', 'empty', 'No token data (sessions unpriced or no native logs).'));
446
- }
447
- row.appendChild(modelCard);
448
- host.appendChild(row);
449
-
450
- // ── Named invocation charts ──────────────────────────────────────────────
451
- var invocationDefs = [
452
- { field: 'skillInvocations', title: 'Skills invoked', color: '#7c4fff' },
453
- { field: 'agentInvocations', title: 'Agent subtypes', color: '#ff6b6b' },
454
- { field: 'commandInvocations', title: 'Slash commands', color: '#4a9eff' },
455
- ];
456
-
457
- invocationDefs.forEach(function (def) {
458
- var agg = {};
459
- fs.forEach(function (s) {
460
- (s[def.field] || []).forEach(function (x) {
461
- agg[x.name] = (agg[x.name] || 0) + x.totalCalls;
462
- });
463
- });
464
- var entries = Object.entries(agg)
465
- .sort(function (a, b) { return b[1] - a[1]; })
466
- .slice(0, 10);
467
-
468
- var invCard = card(def.title);
469
- if (entries.length) {
470
- makeChart(canvasIn(invCard._body), {
471
- type: 'bar',
472
- data: {
473
- labels: entries.map(function (e) { return e[0]; }),
474
- datasets: [{
475
- data: entries.map(function (e) { return e[1]; }),
476
- backgroundColor: def.color,
477
- borderRadius: 4,
478
- }],
479
- },
480
- options: {
481
- indexAxis: 'y',
482
- plugins: { legend: { display: false } },
483
- scales: {
484
- x: { grid: { color: GRID }, ticks: { precision: 0 } },
485
- y: { grid: { display: false } },
486
- },
487
- },
488
- });
489
- } else {
490
- invCard._body.appendChild(el('div', 'empty', 'No data.'));
491
- }
492
- host.appendChild(invCard);
493
- });
495
+ appendToolsAndModels(host, fs, makeChart);
496
+ appendInvocationCharts(host, fs, makeChart);
494
497
  };
495
498
 
496
499
  VIEWS.activity = function (host, fs) {
@@ -819,10 +822,17 @@
819
822
  };
820
823
 
821
824
  // ---- session-detail modal ----------------------------------------------
822
- var modalChart = null; // owned by the modal; NOT in the global charts[] (decoupled from destroyCharts()).
825
+ var modalCharts = []; // owned by the modal; NOT in the global charts[] (decoupled from destroyCharts()).
823
826
  var modalEsc = null;
827
+ function makeModalChart(canvas, config) {
828
+ if (!window.Chart) return null;
829
+ var c = new Chart(canvas, config);
830
+ modalCharts.push(c);
831
+ return c;
832
+ }
824
833
  function closeSessionModal() {
825
- if (modalChart) { try { modalChart.destroy(); } catch (e) {} modalChart = null; }
834
+ modalCharts.forEach(function (c) { try { c.destroy(); } catch (e) {} });
835
+ modalCharts = [];
826
836
  if (modalEsc) { document.removeEventListener('keydown', modalEsc); modalEsc = null; }
827
837
  var ov = document.getElementById('session-modal'); if (ov && ov.parentNode) ov.parentNode.removeChild(ov);
828
838
  }
@@ -870,33 +880,139 @@
870
880
  row.appendChild(el('div', 'tl-label' + (strong ? ' tl-label-strong' : ''), esc(label)));
871
881
  var track = el('div', 'tl-track');
872
882
  var bar = el('div', 'tl-bar');
873
- bar.style.left = leftPct + '%'; bar.style.width = wPct + '%'; bar.style.background = color;
874
- if (barText) bar.innerHTML = '<span class="tl-bar-text">' + esc(barText) + '</span>'; // only wide (session) bar
883
+ bar.style.left = 'calc(min(' + leftPct + '%, 100% - 28px))'; bar.style.width = wPct + '%'; bar.style.background = color;
884
+ if (barText) {
885
+ bar.classList.add('tl-bar-has-text');
886
+ bar.innerHTML = '<span class="tl-bar-text">' + esc(barText) + '</span>';
887
+ }
875
888
  bar.title = label + ' · ' + durText; // .title property = safe (not parsed as HTML)
876
889
  track.appendChild(bar); row.appendChild(track);
877
890
  row.appendChild(el('div', 'tl-dur', esc(durText)));
878
891
  return row;
879
892
  }
880
- // Gantt of top-level agent dispatches: session span on top, each dispatch positioned by start.
893
+ // Precise wall-clock timing for short timeline steps. The general report formatter rounds
894
+ // sub-minute values up to one minute, which is useful for sessions but misleading here.
895
+ function fmtTimelineDuration(ms) {
896
+ ms = Math.max(0, ms || 0);
897
+ if (ms < 1000) return Math.round(ms) + 'ms';
898
+ if (ms < 60000) return (Math.round(ms / 100) / 10) + 's';
899
+ if (ms < 3600000) return Math.floor(ms / 60000) + 'm ' + Math.round((ms % 60000) / 1000) + 's';
900
+ return Math.floor(ms / 3600000) + 'h ' + Math.round((ms % 3600000) / 60000) + 'm';
901
+ }
902
+ // Side panel content for a selected timeline step.
903
+ function tlDetailEl(d) {
904
+ var panel = el('div', '');
905
+ if (!d) {
906
+ panel.appendChild(el('div', 'tl-side-empty', 'Click a timeline step to see details.'));
907
+ return panel;
908
+ }
909
+ var color = PALETTE[hashStr(d.kind + ':' + d.name) % PALETTE.length];
910
+ var nameEl = el('div', 'tl-side-name');
911
+ var dot = el('span', 'tl-side-dot'); dot.style.background = color;
912
+ nameEl.appendChild(dot);
913
+ nameEl.appendChild(document.createTextNode(d.kind + ' · ' + d.name));
914
+ panel.appendChild(nameEl);
915
+ var hasCost = d.costUSD != null;
916
+ var hasTok = d.tokens != null;
917
+ panel.appendChild(statsEl([
918
+ ['Cost', hasCost ? fmtUSD(d.costUSD) : '—', ''],
919
+ ['Tokens', hasTok ? fmtTokens(d.tokens.total) : '—', ''],
920
+ ['Duration', fmtTimelineDuration(d.durationMs), ''],
921
+ ]));
922
+ if (hasTok) {
923
+ var tok = d.tokens;
924
+ var total = Math.max(tok.total, 1);
925
+ var barEl = el('div', 'tl-tok-bar');
926
+ var segs = [
927
+ { val: tok.input, color: '#7C5CFC' },
928
+ { val: tok.output, color: '#F5A534' },
929
+ { val: tok.cacheRead, color: '#259F4C' },
930
+ { val: tok.cacheCreation, color: '#3B82F6' },
931
+ ].filter(function(s) { return s.val > 0; });
932
+ segs.forEach(function(s) {
933
+ var seg = el('div', 'tl-tok-seg');
934
+ seg.style.flex = String(s.val / total * 100);
935
+ seg.style.background = s.color;
936
+ barEl.appendChild(seg);
937
+ });
938
+ var tokSec = el('div', 'tl-side-section');
939
+ tokSec.appendChild(el('div', 'tl-side-label', 'Token breakdown'));
940
+ tokSec.appendChild(barEl);
941
+ tokSec.appendChild(statsEl([
942
+ ['Input', fmtTokens(tok.input), ''],
943
+ ['Output', fmtTokens(tok.output), ''],
944
+ ['Cache read', fmtTokens(tok.cacheRead), ''],
945
+ ['Cache write', fmtTokens(tok.cacheCreation), ''],
946
+ ]));
947
+ panel.appendChild(tokSec);
948
+ }
949
+ if (d.tools && d.tools.length) {
950
+ var toolSec = el('div', 'tl-side-section');
951
+ toolSec.appendChild(el('div', 'tl-side-label', 'Tools called'));
952
+ var chips = el('div', 'modal-chips');
953
+ d.tools.forEach(function(t) {
954
+ chips.appendChild(el('span', 'chip', esc(t.name) + '<b>\xd7' + fmtNum(t.calls) + '</b>'));
955
+ });
956
+ toolSec.appendChild(chips);
957
+ panel.appendChild(toolSec);
958
+ }
959
+ return panel;
960
+ }
961
+ // Gantt of top-level agent dispatches: session span on top, each agent bar positioned
962
+ // by wall-clock start time relative to the session start (proportional scaling).
963
+ // Skills and commands are excluded — they have no separate transcript or cost to show.
964
+ // min-width 28px (CSS) keeps short agents visible and clickable without distorting the scale.
881
965
  function timelineEl(s) {
882
- var agents = (s.dispatches || []).filter(function (d) { return d.kind === 'agent'; });
883
- var start = s.startTime;
884
- var end = start + (s.durationMs || 0);
885
- (s.dispatches || []).forEach(function (d) { var e = d.start + (d.durationMs || 0); if (e > end) end = e; });
886
- var span = Math.max(1, end - start);
887
- var wrap = el('div', 'timeline');
888
- wrap.appendChild(tlRow('session', '#259F4C', 0, 100, fmtDuration(s.durationMs || 0), fmtUSD(s.costUSD), true));
966
+ var dispatches = (s.dispatches || []).filter(function (d) { return d.kind === 'agent'; }).sort(function (a, b) { return a.start - b.start; });
967
+ // Scale bars to the agent activity window, not the full session duration.
968
+ // Agent bars fill the track proportionally relative to each other; absolute
969
+ // start offset (vs. session start) is shown in the side panel detail view.
970
+ var actStart = dispatches.length ? dispatches.reduce(function (m, d) { return Math.min(m, d.start); }, Infinity) : s.startTime;
971
+ var actEnd = dispatches.length ? dispatches.reduce(function (m, d) { return Math.max(m, d.start + (d.durationMs || 0)); }, -Infinity) : s.startTime + s.durationMs;
972
+ var ganttSpan = Math.max(1, actEnd - actStart);
973
+
974
+ var container = el('div', 'tl-container');
975
+ var gantt = el('div', 'tl-gantt timeline');
976
+ var sidePane = el('div', 'tl-side');
977
+
978
+ var selected = dispatches[0] || null;
979
+ sidePane.appendChild(tlDetailEl(selected));
980
+
981
+ function selectDispatch(d, barEl) {
982
+ gantt.querySelectorAll('.tl-bar[data-dispatch]').forEach(function(b) {
983
+ b.style.outline = b === barEl ? '2px solid rgba(255,255,255,0.55)' : 'none';
984
+ });
985
+ sidePane.innerHTML = '';
986
+ sidePane.appendChild(tlDetailEl(d));
987
+ }
988
+
989
+ gantt.appendChild(tlRow('session', '#259F4C', 0, 100, fmtTimelineDuration(s.durationMs), fmtUSD(s.costUSD), true));
990
+
889
991
  var occ = {};
890
- agents.forEach(function (d) {
891
- var total = agents.filter(function (x) { return x.name === d.name; }).length;
892
- occ[d.name] = (occ[d.name] || 0) + 1;
893
- var label = d.name + (total > 1 ? ' #' + occ[d.name] : '');
894
- var leftPct = ((d.start - start) / span) * 100;
895
- var wPct = Math.max(((d.durationMs || 0) / span) * 100, 1.5);
896
- if (leftPct + wPct > 100) leftPct = Math.max(0, 100 - wPct);
897
- wrap.appendChild(tlRow(label, PALETTE[hashStr(d.name) % PALETTE.length], leftPct, wPct, fmtDuration(d.durationMs || 0), '', false));
992
+ dispatches.forEach(function (d) {
993
+ var key = d.kind + ':' + d.name;
994
+ var total = dispatches.filter(function (x) { return x.kind === d.kind && x.name === d.name; }).length;
995
+ occ[key] = (occ[key] || 0) + 1;
996
+ var label = d.kind + ' · ' + d.name + (total > 1 ? ' #' + occ[key] : '');
997
+ var leftPct = Math.max(0, (d.start - actStart) / ganttSpan * 100);
998
+ var wPct = (d.durationMs || 0) / ganttSpan * 100;
999
+ if (leftPct + wPct > 100) wPct = Math.max(0, 100 - leftPct);
1000
+ var barText = d.costUSD != null && wPct >= 8 ? fmtUSD(d.costUSD) : '';
1001
+ var color = PALETTE[hashStr(key) % PALETTE.length];
1002
+ var row = tlRow(label, color, leftPct, wPct, fmtTimelineDuration(d.durationMs), barText, false);
1003
+ var bar = row.querySelector('.tl-bar');
1004
+ if (bar) {
1005
+ bar.setAttribute('data-dispatch', key);
1006
+ if (d === selected) bar.style.outline = '2px solid rgba(255,255,255,0.55)';
1007
+ }
1008
+ row.style.cursor = 'pointer';
1009
+ row.addEventListener('click', function () { selectDispatch(d, bar); });
1010
+ gantt.appendChild(row);
898
1011
  });
899
- return wrap;
1012
+
1013
+ container.appendChild(gantt);
1014
+ container.appendChild(sidePane);
1015
+ return container;
900
1016
  }
901
1017
  function openSessionModal(s, onBack) {
902
1018
  if (!s) return;
@@ -966,7 +1082,7 @@
966
1082
  var t0 = series[0].t;
967
1083
  var labels = series.map(function (p) { return useTs ? fmtDuration(Math.max(0, p.t - t0)) : ('turn ' + p.t); });
968
1084
  var cv = canvasIn(growth._body, 220);
969
- modalChart = new Chart(cv, {
1085
+ makeModalChart(cv, {
970
1086
  type: 'line',
971
1087
  data: { labels: labels, datasets: [
972
1088
  { label: 'Cost ($)', data: series.map(function (p) { return Math.round(p.cost * 10000) / 10000; }), borderColor: '#7C5CFC', backgroundColor: 'rgba(124,92,252,0.12)', fill: true, yAxisID: 'y', tension: 0.25, pointRadius: 0 },
@@ -982,23 +1098,20 @@
982
1098
  }
983
1099
  body.appendChild(growth);
984
1100
 
985
- // Timeline — Gantt of top-level agent dispatches with durations (per-agent cost is not captured).
986
- var hasAgents = (s.dispatches || []).some(function (d) { return d.kind === 'agent'; });
987
- var tlCard = card('Timeline', hasAgents ? 'agent dispatches over the session · durations (per-agent cost is not captured)' : '');
988
- if (hasAgents) {
1101
+ // Timeline — Gantt of all top-level agent, skill, and command dispatches.
1102
+ var hasDispatches = (s.dispatches || []).length > 0;
1103
+ var hasCostData = (s.dispatches || []).some(function (d) { return d.kind === 'agent' && d.costUSD != null; });
1104
+ var tlSubtitle = hasDispatches ? (hasCostData ? 'click a step for cost, token & timing details · long idle gaps compressed' : 'click a step for timing details · long idle gaps compressed') : '';
1105
+ var tlCard = card('Timeline', tlSubtitle);
1106
+ if (hasDispatches) {
989
1107
  tlCard._body.appendChild(timelineEl(s));
990
1108
  } else {
991
- tlCard._body.appendChild(el('div', 'empty', 'No sub-agent dispatches recorded for this session.'));
1109
+ tlCard._body.appendChild(el('div', 'empty', 'No agent, skill, or command dispatches recorded for this session.'));
992
1110
  }
993
1111
  body.appendChild(tlCard);
994
1112
 
995
- // Skills & commands — invoked names + counts.
996
- var scCard = card('Skills & commands', 'invoked by name');
997
- var cols = el('div', 'dispatch-cols');
998
- cols.appendChild(chipsEl('Skills', dispatchCounts(s, 'skill')));
999
- cols.appendChild(chipsEl('Commands', dispatchCounts(s, 'command')));
1000
- scCard._body.appendChild(cols);
1001
- body.appendChild(scCard);
1113
+ appendToolsAndModels(body, [s], makeModalChart);
1114
+ appendInvocationCharts(body, [s], makeModalChart);
1002
1115
 
1003
1116
  modal.appendChild(body);
1004
1117
  ov.appendChild(modal);
@@ -163,9 +163,21 @@
163
163
  .tl-label { width: 168px; flex-shrink: 0; font-size: 12px; color: var(--color-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
164
164
  .tl-label-strong { font-weight: 700; }
165
165
  .tl-track { position: relative; flex: 1; height: 22px; background: var(--color-bg-tertiary); border-radius: 5px; overflow: hidden; }
166
- .tl-bar { position: absolute; top: 0; height: 100%; min-width: 3px; border-radius: 5px; display: flex; align-items: center; padding: 0 8px; box-sizing: border-box; }
166
+ .tl-bar { position: absolute; top: 0; height: 100%; min-width: 28px; border-radius: 5px; display: flex; align-items: center; padding: 0; box-sizing: border-box; }
167
+ .tl-bar-has-text { padding: 0 8px; }
167
168
  .tl-bar-text { font-size: 10.5px; color: #fff; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 600; }
168
- .tl-dur { width: 58px; flex-shrink: 0; text-align: right; font-size: 11px; color: var(--color-text-muted); font-variant-numeric: tabular-nums; }
169
+ .tl-dur { width: 72px; flex-shrink: 0; text-align: right; font-size: 10.5px; color: var(--color-text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
170
+ /* timeline split layout: gantt left + side panel right */
171
+ .tl-container { display: flex; gap: 16px; align-items: flex-start; }
172
+ .tl-gantt { flex: 1; min-width: 0; }
173
+ .tl-side { width: 230px; flex-shrink: 0; border-left: 1px solid var(--color-border-structural); padding-left: 14px; min-height: 60px; }
174
+ .tl-side-empty { font-size: 12px; color: var(--color-text-muted); }
175
+ .tl-side-name { font-size: 12px; font-weight: 700; color: var(--color-text-primary); margin-bottom: 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: flex; align-items: center; gap: 6px; }
176
+ .tl-side-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; }
177
+ .tl-side-section { margin-top: 8px; }
178
+ .tl-side-label { font-size: 10px; text-transform: uppercase; letter-spacing: .05em; color: var(--color-text-muted); margin-bottom: 4px; }
179
+ .tl-tok-bar { display: flex; gap: 2px; height: 5px; border-radius: 3px; overflow: hidden; margin-bottom: 4px; }
180
+ .tl-tok-seg { border-radius: 2px; }
169
181
 
170
182
  /* session-detail modal */
171
183
  .modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.62); backdrop-filter: blur(4px); display: flex; align-items: center; justify-content: center; z-index: var(--z-modal, 50); padding: 24px; }
@@ -195,9 +207,21 @@
195
207
  .tl-label { width: 168px; flex-shrink: 0; font-size: 12px; color: var(--color-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
196
208
  .tl-label-strong { font-weight: 700; }
197
209
  .tl-track { position: relative; flex: 1; height: 22px; background: var(--color-bg-tertiary); border-radius: 5px; overflow: hidden; }
198
- .tl-bar { position: absolute; top: 0; height: 100%; min-width: 3px; border-radius: 5px; display: flex; align-items: center; padding: 0 8px; box-sizing: border-box; }
210
+ .tl-bar { position: absolute; top: 0; height: 100%; min-width: 28px; border-radius: 5px; display: flex; align-items: center; padding: 0; box-sizing: border-box; }
211
+ .tl-bar-has-text { padding: 0 8px; }
199
212
  .tl-bar-text { font-size: 10.5px; color: #fff; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-weight: 600; }
200
- .tl-dur { width: 58px; flex-shrink: 0; text-align: right; font-size: 11px; color: var(--color-text-muted); font-variant-numeric: tabular-nums; }
213
+ .tl-dur { width: 72px; flex-shrink: 0; text-align: right; font-size: 10.5px; color: var(--color-text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
214
+ /* timeline split layout: gantt left + side panel right */
215
+ .tl-container { display: flex; gap: 16px; align-items: flex-start; }
216
+ .tl-gantt { flex: 1; min-width: 0; }
217
+ .tl-side { width: 230px; flex-shrink: 0; border-left: 1px solid var(--color-border-structural); padding-left: 14px; min-height: 60px; }
218
+ .tl-side-empty { font-size: 12px; color: var(--color-text-muted); }
219
+ .tl-side-name { font-size: 12px; font-weight: 700; color: var(--color-text-primary); margin-bottom: 8px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: flex; align-items: center; gap: 6px; }
220
+ .tl-side-dot { display: inline-block; width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; }
221
+ .tl-side-section { margin-top: 8px; }
222
+ .tl-side-label { font-size: 10px; text-transform: uppercase; letter-spacing: .05em; color: var(--color-text-muted); margin-bottom: 4px; }
223
+ .tl-tok-bar { display: flex; gap: 2px; height: 5px; border-radius: 3px; overflow: hidden; margin-bottom: 4px; }
224
+ .tl-tok-seg { border-radius: 2px; }
201
225
 
202
226
  /* table alignment — the design-system `.table thead tr th` (text-align:left, higher
203
227
  specificity) was overriding `.td-number` on numeric <th>, so numeric headers stayed
@@ -1 +1 @@
1
- {"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/hook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,KAAK,EAAE,aAAa,EAA4D,MAAM,wBAAwB,CAAC;AAGtH;;;;;;GAMG;AAEH;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,eAAe,EAAE,cAAc,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,eAAe,EAAE,YAAY,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,eAAe,EAAE,cAAc,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA0mCD;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBrG;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CA8F3C"}
1
+ {"version":3,"file":"hook.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/hook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,KAAK,EAAE,aAAa,EAA4D,MAAM,wBAAwB,CAAC;AAGtH;;;;;;GAMG;AAEH;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,eAAe,EAAE,cAAc,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,eAAgB,SAAQ,aAAa;IACpD,eAAe,EAAE,YAAY,CAAC;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,aAAa;IACtD,eAAe,EAAE,cAAc,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,yBAAyB;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qCAAqC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+BAA+B;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iBAAiB;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAynCD;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,KAAK,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBrG;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAkG3C"}
@@ -268,6 +268,18 @@ async function buildProcessingContext(sessionId, agentSessionId, agentSessionFil
268
268
  const apiUrl = getConfigValue('CODEMIE_SYNC_API_URL', config) || getConfigValue('CODEMIE_BASE_URL', config) || '';
269
269
  const cliVersion = getConfigValue('CODEMIE_CLI_VERSION', config) || '0.0.0';
270
270
  const clientType = getConfigValue('CODEMIE_CLIENT_TYPE', config) || 'codemie-cli';
271
+ // Load the CodeMie session metadata so processors can use gitBranch and other
272
+ // session-level fields that are not present in the native agent transcript.
273
+ let gitBranch;
274
+ try {
275
+ const { getCodemiePath } = await import('../../utils/paths.js');
276
+ const { readFile } = await import('node:fs/promises');
277
+ const sessionMeta = JSON.parse(await readFile(getCodemiePath('sessions', `${sessionId}.json`), 'utf-8'));
278
+ gitBranch = sessionMeta.gitBranch;
279
+ }
280
+ catch {
281
+ // Session metadata may not exist in all contexts (e.g., tests); proceed without it.
282
+ }
271
283
  // Build context with SSO credentials if available
272
284
  let cookies = config?.cookies || '';
273
285
  let apiKey = config?.apiKey;
@@ -302,7 +314,8 @@ async function buildProcessingContext(sessionId, agentSessionId, agentSessionFil
302
314
  dryRun: false,
303
315
  sessionId,
304
316
  agentSessionId,
305
- agentSessionFile
317
+ agentSessionFile,
318
+ gitBranch,
306
319
  };
307
320
  }
308
321
  /**
@@ -1054,16 +1067,20 @@ export function createHookCommand() {
1054
1067
  logger.debug(`[hook] Received event: ${JSON.stringify(event)}`);
1055
1068
  process.exit(2); // Blocking error
1056
1069
  }
1057
- if (!event.transcript_path) {
1058
- logger.error('[hook] Missing required field: transcript_path');
1059
- logger.debug(`[hook] Received event: ${JSON.stringify(event)}`);
1060
- process.exit(2); // Blocking error
1061
- }
1062
1070
  // Initialize logger context using CODEMIE_SESSION_ID from environment
1063
1071
  // This ensures consistent session ID across all hooks
1064
1072
  const { sessionId, agentName } = initializeHookContext();
1065
- // Apply hook transformation if agent provides a transformer
1073
+ // Apply hook transformation if agent provides a transformer.
1074
+ // Some agents (e.g. Kimi) do not emit a transcript_path in their raw
1075
+ // hook payload; the transformer computes it from agent-specific session
1076
+ // layout before we validate the internal event shape.
1066
1077
  const transformedEvent = applyHookTransformation(event, agentName);
1078
+ // Validate required fields after transformation so agent-specific
1079
+ // transformers can populate fields such as transcript_path.
1080
+ validateHookEvent(transformedEvent);
1081
+ if (process.exitCode === 2) {
1082
+ return; // Validation failed
1083
+ }
1067
1084
  // Normalize event name and log processing info
1068
1085
  normalizeAndLogEvent(transformedEvent, sessionId, agentName);
1069
1086
  // Route to appropriate handler with transformed event and session ID