@agenthippo-ai/agenthippo 0.1.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 (480) hide show
  1. package/LICENSE +21 -0
  2. package/bin/agenthippo.js +2 -0
  3. package/out/agentpack/github.js +547 -0
  4. package/out/agentpack/index.js +66 -0
  5. package/out/agentpack/install.js +297 -0
  6. package/out/agentpack/manager.js +441 -0
  7. package/out/agentpack/manifest.js +135 -0
  8. package/out/agentpack/paths.js +475 -0
  9. package/out/agentpack/publish.js +248 -0
  10. package/out/agentpack/stores.js +554 -0
  11. package/out/agentpack/types.js +5 -0
  12. package/out/analytics/dashboard/artifactManager.js +439 -0
  13. package/out/analytics/dashboard/fleetDisplayUtils.js +159 -0
  14. package/out/analytics/dashboard/fleetOrchestrator.js +142 -0
  15. package/out/analytics/dashboard/fleetRegistry.js +79 -0
  16. package/out/analytics/dashboard/fleetRowDefaults.js +87 -0
  17. package/out/analytics/dashboard/fleetScriptAdapter.js +146 -0
  18. package/out/analytics/dashboard/fleetStatusUtils.js +30 -0
  19. package/out/analytics/dashboard/fleetTitleUtils.js +68 -0
  20. package/out/analytics/dashboard/fleetTypes.js +29 -0
  21. package/out/analytics/dashboard/index.js +17 -0
  22. package/out/analytics/dashboard/indexingService.js +422 -0
  23. package/out/analytics/dashboard/localFleetAdapter.js +222 -0
  24. package/out/analytics/dashboard/performanceMonitor.js +76 -0
  25. package/out/analytics/dashboard/processManager.js +209 -0
  26. package/out/analytics/dashboard/provider.js +3885 -0
  27. package/out/analytics/dashboard/queryRunner.js +994 -0
  28. package/out/analytics/dashboard/refreshPolicy.js +16 -0
  29. package/out/analytics/dashboard/scheduledFleetFingerprint.js +22 -0
  30. package/out/analytics/dashboard/scheduledFleetStorage.js +241 -0
  31. package/out/analytics/dashboard/scheduledFleetUtils.js +22 -0
  32. package/out/analytics/dashboard/sessionEventFleetStats.js +118 -0
  33. package/out/analytics/dashboard/sessionJsonlFleet.js +204 -0
  34. package/out/analytics/dashboard/teamViewDiscovery.js +106 -0
  35. package/out/analytics/dashboard/viewsConfigMerge.js +221 -0
  36. package/out/analytics/dashboard/webview/analytics-panel.html +155 -0
  37. package/out/analytics/dashboard/webview/codicon.ttf +0 -0
  38. package/out/analytics/dashboard/webview/index.html +78 -0
  39. package/out/analytics/dashboard/webview/panel.js +2295 -0
  40. package/out/analytics/dashboard/webview/runtime-bridge.js +82 -0
  41. package/out/analytics/dashboard/webview/styles.css +1009 -0
  42. package/out/analytics/lifecycle.js +43 -0
  43. package/out/analytics/service.js +1279 -0
  44. package/out/analytics/sessionEvents.js +231 -0
  45. package/out/auth/bootstrapApply.js +72 -0
  46. package/out/auth/brokerClient.js +126 -0
  47. package/out/auth/callbackUri.js +59 -0
  48. package/out/auth/callbackUriParse.js +59 -0
  49. package/out/auth/githubStoreGate.js +70 -0
  50. package/out/auth/loopbackAuthServer.js +165 -0
  51. package/out/auth/pkce.js +9 -0
  52. package/out/auth/provider.js +239 -0
  53. package/out/auth/sessionStore.js +193 -0
  54. package/out/auth/signInFlow.js +182 -0
  55. package/out/auth/types.js +12 -0
  56. package/out/auth-broker/config.js +62 -0
  57. package/out/auth-broker/jwt.js +105 -0
  58. package/out/auth-broker/pkce.js +57 -0
  59. package/out/auth-broker/server.js +539 -0
  60. package/out/auth-broker/store.js +273 -0
  61. package/out/base-prompts/gateway/openclaw-starter.json5 +144 -0
  62. package/out/base-prompts/gateway/slack-socket-mode-minimal.manifest.json +44 -0
  63. package/out/base-prompts/mcp-servers/.mcp.json +13 -0
  64. package/out/base-prompts/mcp-servers/langfuse-analytics/README.md +142 -0
  65. package/out/base-prompts/mcp-servers/langfuse-analytics/mcp.json +13 -0
  66. package/out/base-prompts/mcp-servers/langfuse-analytics/package-lock.json +1192 -0
  67. package/out/base-prompts/mcp-servers/langfuse-analytics/package.json +23 -0
  68. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/run-with-env.sh +12 -0
  69. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/sdk-test.js +102 -0
  70. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/simple-test.js +137 -0
  71. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/test-client.js +118 -0
  72. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/test-server.js +209 -0
  73. package/out/base-prompts/mcp-servers/langfuse-analytics/src/client.ts +431 -0
  74. package/out/base-prompts/mcp-servers/langfuse-analytics/src/index.ts +270 -0
  75. package/out/base-prompts/mcp-servers/langfuse-analytics/tsconfig.json +18 -0
  76. package/out/base-prompts/rules/AGENTS.md +204 -0
  77. package/out/base-prompts/skills/agent-browser/SKILL.md +102 -0
  78. package/out/base-prompts/skills/agent-builder/SKILL.md +73 -0
  79. package/out/base-prompts/skills/agent-builder/references/agent-pack.md +63 -0
  80. package/out/base-prompts/skills/agent-builder/references/crewai.md +66 -0
  81. package/out/base-prompts/skills/agent-builder/references/langchain.md +60 -0
  82. package/out/base-prompts/skills/agent-builder/references/raw-sdk.md +73 -0
  83. package/out/base-prompts/skills/agent-engine-creator/SKILL.md +586 -0
  84. package/out/base-prompts/skills/agent-engine-installer/SKILL.md +106 -0
  85. package/out/base-prompts/skills/agent-engine-installer/scripts/install-engine.mjs +259 -0
  86. package/out/base-prompts/skills/agent-engine-installer/scripts/install-engine.ps1 +189 -0
  87. package/out/base-prompts/skills/agenthippo-cli/SKILL.md +69 -0
  88. package/out/base-prompts/skills/agenthippo-cli/scripts/install.sh +63 -0
  89. package/out/base-prompts/skills/agentpack-creator/SKILL.md +375 -0
  90. package/out/base-prompts/skills/agentpack-creator/references/agent-templates.md +261 -0
  91. package/out/base-prompts/skills/agentpack-creator/references/agents-md-guide.md +195 -0
  92. package/out/base-prompts/skills/agentpack-creator/scripts/init_pack.py +391 -0
  93. package/out/base-prompts/skills/agentpack-creator/scripts/init_pack_test.py +294 -0
  94. package/out/base-prompts/skills/agentpack-testbench/SKILL.md +135 -0
  95. package/out/base-prompts/skills/algorithmic-art/LICENSE.txt +202 -0
  96. package/out/base-prompts/skills/algorithmic-art/SKILL.md +405 -0
  97. package/out/base-prompts/skills/algorithmic-art/templates/generator_template.js +223 -0
  98. package/out/base-prompts/skills/algorithmic-art/templates/viewer.html +599 -0
  99. package/out/base-prompts/skills/debug-agent/SKILL.md +83 -0
  100. package/out/base-prompts/skills/desktop-automation/SKILL.md +166 -0
  101. package/out/base-prompts/skills/desktop-automation/references/agenthippo-ui-recipes.md +441 -0
  102. package/out/base-prompts/skills/desktop-automation/scripts/windows-desktop-uia.psm1 +280 -0
  103. package/out/base-prompts/skills/docx/LICENSE.txt +30 -0
  104. package/out/base-prompts/skills/docx/SKILL.md +197 -0
  105. package/out/base-prompts/skills/docx/docx-js.md +350 -0
  106. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  107. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  108. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  109. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  110. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  111. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  112. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  113. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  114. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  115. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  116. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  117. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  118. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  119. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  120. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  121. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  122. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  123. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  124. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  125. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  126. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  127. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  128. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  129. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  130. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  131. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  132. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  133. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  134. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  135. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  136. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  137. package/out/base-prompts/skills/docx/ooxml/schemas/mce/mc.xsd +75 -0
  138. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  139. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  140. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  141. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  142. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  143. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  144. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  145. package/out/base-prompts/skills/docx/ooxml/scripts/pack.py +159 -0
  146. package/out/base-prompts/skills/docx/ooxml/scripts/unpack.py +29 -0
  147. package/out/base-prompts/skills/docx/ooxml/scripts/validate.py +69 -0
  148. package/out/base-prompts/skills/docx/ooxml/scripts/validation/__init__.py +15 -0
  149. package/out/base-prompts/skills/docx/ooxml/scripts/validation/base.py +951 -0
  150. package/out/base-prompts/skills/docx/ooxml/scripts/validation/docx.py +274 -0
  151. package/out/base-prompts/skills/docx/ooxml/scripts/validation/pptx.py +315 -0
  152. package/out/base-prompts/skills/docx/ooxml/scripts/validation/redlining.py +279 -0
  153. package/out/base-prompts/skills/docx/ooxml.md +610 -0
  154. package/out/base-prompts/skills/docx/scripts/__init__.py +1 -0
  155. package/out/base-prompts/skills/docx/scripts/document.py +1276 -0
  156. package/out/base-prompts/skills/docx/scripts/templates/comments.xml +3 -0
  157. package/out/base-prompts/skills/docx/scripts/templates/commentsExtended.xml +3 -0
  158. package/out/base-prompts/skills/docx/scripts/templates/commentsExtensible.xml +3 -0
  159. package/out/base-prompts/skills/docx/scripts/templates/commentsIds.xml +3 -0
  160. package/out/base-prompts/skills/docx/scripts/templates/people.xml +3 -0
  161. package/out/base-prompts/skills/docx/scripts/utilities.py +374 -0
  162. package/out/base-prompts/skills/gateway-outbound/SKILL.md +136 -0
  163. package/out/base-prompts/skills/mcp-builder/LICENSE.txt +202 -0
  164. package/out/base-prompts/skills/mcp-builder/SKILL.md +328 -0
  165. package/out/base-prompts/skills/mcp-builder/reference/evaluation.md +602 -0
  166. package/out/base-prompts/skills/mcp-builder/reference/mcp_best_practices.md +249 -0
  167. package/out/base-prompts/skills/mcp-builder/reference/node_mcp_server.md +970 -0
  168. package/out/base-prompts/skills/mcp-builder/reference/python_mcp_server.md +719 -0
  169. package/out/base-prompts/skills/mcp-builder/scripts/connections.py +151 -0
  170. package/out/base-prompts/skills/mcp-builder/scripts/evaluation.py +373 -0
  171. package/out/base-prompts/skills/mcp-builder/scripts/example_evaluation.xml +22 -0
  172. package/out/base-prompts/skills/mcp-builder/scripts/requirements.txt +2 -0
  173. package/out/base-prompts/skills/openclaw/SKILL.md +406 -0
  174. package/out/base-prompts/skills/openclaw/scripts/run-smoke.sh +61 -0
  175. package/out/base-prompts/skills/pdf/LICENSE.txt +30 -0
  176. package/out/base-prompts/skills/pdf/SKILL.md +294 -0
  177. package/out/base-prompts/skills/pdf/forms.md +205 -0
  178. package/out/base-prompts/skills/pdf/reference.md +612 -0
  179. package/out/base-prompts/skills/pdf/scripts/check_bounding_boxes.py +70 -0
  180. package/out/base-prompts/skills/pdf/scripts/check_bounding_boxes_test.py +226 -0
  181. package/out/base-prompts/skills/pdf/scripts/check_fillable_fields.py +12 -0
  182. package/out/base-prompts/skills/pdf/scripts/convert_pdf_to_images.py +35 -0
  183. package/out/base-prompts/skills/pdf/scripts/create_validation_image.py +41 -0
  184. package/out/base-prompts/skills/pdf/scripts/extract_form_field_info.py +152 -0
  185. package/out/base-prompts/skills/pdf/scripts/fill_fillable_fields.py +114 -0
  186. package/out/base-prompts/skills/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
  187. package/out/base-prompts/skills/pptx/LICENSE.txt +30 -0
  188. package/out/base-prompts/skills/pptx/SKILL.md +484 -0
  189. package/out/base-prompts/skills/pptx/html2pptx.md +625 -0
  190. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
  191. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
  192. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
  193. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
  194. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
  195. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
  196. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
  197. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
  198. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
  199. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
  200. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
  201. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
  202. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
  203. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
  204. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
  205. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
  206. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
  207. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
  208. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
  209. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
  210. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
  211. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
  212. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
  213. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
  214. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
  215. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
  216. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
  217. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
  218. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
  219. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
  220. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
  221. package/out/base-prompts/skills/pptx/ooxml/schemas/mce/mc.xsd +75 -0
  222. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
  223. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
  224. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
  225. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
  226. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
  227. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
  228. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
  229. package/out/base-prompts/skills/pptx/ooxml/scripts/pack.py +159 -0
  230. package/out/base-prompts/skills/pptx/ooxml/scripts/unpack.py +29 -0
  231. package/out/base-prompts/skills/pptx/ooxml/scripts/validate.py +69 -0
  232. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/__init__.py +15 -0
  233. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/base.py +951 -0
  234. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/docx.py +274 -0
  235. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/pptx.py +315 -0
  236. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/redlining.py +279 -0
  237. package/out/base-prompts/skills/pptx/ooxml.md +427 -0
  238. package/out/base-prompts/skills/pptx/scripts/html2pptx.js +984 -0
  239. package/out/base-prompts/skills/pptx/scripts/inventory.py +1020 -0
  240. package/out/base-prompts/skills/pptx/scripts/rearrange.py +231 -0
  241. package/out/base-prompts/skills/pptx/scripts/replace.py +385 -0
  242. package/out/base-prompts/skills/pptx/scripts/thumbnail.py +450 -0
  243. package/out/base-prompts/skills/prompt-lab/SKILL.md +149 -0
  244. package/out/base-prompts/skills/prompt-lab/scripts/query-analytics.sh +2551 -0
  245. package/out/base-prompts/skills/skill-creator/LICENSE.txt +202 -0
  246. package/out/base-prompts/skills/skill-creator/SKILL.md +371 -0
  247. package/out/base-prompts/skills/skill-creator/references/output-patterns.md +82 -0
  248. package/out/base-prompts/skills/skill-creator/references/workflows.md +28 -0
  249. package/out/base-prompts/skills/skill-creator/scripts/init_skill.py +303 -0
  250. package/out/base-prompts/skills/skill-creator/scripts/package_skill.py +110 -0
  251. package/out/base-prompts/skills/skill-creator/scripts/quick_validate.py +95 -0
  252. package/out/base-prompts/skills/skill-installer/SKILL.md +251 -0
  253. package/out/base-prompts/skills/skill-installer/references/store-api.md +219 -0
  254. package/out/base-prompts/skills/spotlight-dashboard/SKILL.md +83 -0
  255. package/out/base-prompts/skills/spotlight-insights/SKILL.md +101 -0
  256. package/out/base-prompts/skills/spotlight-optimize/SKILL.md +93 -0
  257. package/out/base-prompts/skills/spotlight-optimize/scripts/query-analytics.sh +2551 -0
  258. package/out/base-prompts/skills/spotlight-query/SKILL.md +314 -0
  259. package/out/base-prompts/skills/spotlight-query/references/otel-schema.md +163 -0
  260. package/out/base-prompts/skills/spotlight-query/scripts/query-analytics.sh +2551 -0
  261. package/out/base-prompts/skills/spotlight-query/scripts/query-analytics.test.sh +744 -0
  262. package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/sessions/sess-test-1.jsonl +14 -0
  263. package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/traces/.duckdb-index.json +7 -0
  264. package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/traces/sample.jsonl +5 -0
  265. package/out/base-prompts/skills/spotlight-report/SKILL.md +86 -0
  266. package/out/base-prompts/skills/spotlight-visualize/SKILL.md +92 -0
  267. package/out/base-prompts/skills/spotlight-visualize/scripts/query-analytics.sh +2551 -0
  268. package/out/base-prompts/skills/xlsx/LICENSE.txt +30 -0
  269. package/out/base-prompts/skills/xlsx/SKILL.md +289 -0
  270. package/out/base-prompts/skills/xlsx/recalc.py +178 -0
  271. package/out/branding.js +23 -0
  272. package/out/cli/commands/anywhere.js +66 -0
  273. package/out/cli/commands/ask.js +243 -0
  274. package/out/cli/commands/auth-broker.js +24 -0
  275. package/out/cli/commands/auth.js +219 -0
  276. package/out/cli/commands/chat.js +354 -0
  277. package/out/cli/commands/deploy.js +223 -0
  278. package/out/cli/commands/gateway.js +160 -0
  279. package/out/cli/commands/pack.js +220 -0
  280. package/out/cli/commands/probe-custom-engine.js +82 -0
  281. package/out/cli/commands/serve.js +583 -0
  282. package/out/cli/commands/store.js +919 -0
  283. package/out/cli/credential-context.js +113 -0
  284. package/out/cli/deploy-init.js +2087 -0
  285. package/out/cli/deps.js +238 -0
  286. package/out/cli/index.js +390 -0
  287. package/out/cli/pack-loader.js +376 -0
  288. package/out/cli/run-custom-engine-runner.js +318 -0
  289. package/out/cli/runtime.js +409 -0
  290. package/out/cli/serve-hardening.js +325 -0
  291. package/out/cli/state-sync.js +135 -0
  292. package/out/cli/utils.js +220 -0
  293. package/out/cli/vscode-shim.js +86 -0
  294. package/out/engines/base.js +159 -0
  295. package/out/engines/claude.js +1215 -0
  296. package/out/engines/codex.js +759 -0
  297. package/out/engines/custom.js +632 -0
  298. package/out/engines/customEngineIpc.js +95 -0
  299. package/out/engines/customEngineRunnerEmbed.js +107 -0
  300. package/out/engines/customRegistry.js +274 -0
  301. package/out/engines/embeddedCustomEngineRunner.generated.js +9 -0
  302. package/out/engines/engineApiKey.js +138 -0
  303. package/out/engines/factory.js +45 -0
  304. package/out/engines/install.js +372 -0
  305. package/out/engines/persistentEngineWorker.js +320 -0
  306. package/out/engines/publish.js +341 -0
  307. package/out/engines/types.js +17 -0
  308. package/out/examples/run-headless-repl.js +291 -0
  309. package/out/examples/run-headless.js +187 -0
  310. package/out/extension.js +4472 -0
  311. package/out/gateway/bridge.js +805 -0
  312. package/out/gateway/cli-agent-id.js +36 -0
  313. package/out/gateway/client.js +303 -0
  314. package/out/gateway/client.test.js +395 -0
  315. package/out/gateway/gateway-scope-errors.js +14 -0
  316. package/out/gateway/gatewayBackup.js +76 -0
  317. package/out/gateway/gatewayRouteIndex.js +371 -0
  318. package/out/gateway/gatewaySessionStore.js +243 -0
  319. package/out/gateway/index.js +31 -0
  320. package/out/gateway/lifecycle.js +3056 -0
  321. package/out/gateway/openclaw-channel-routing.js +317 -0
  322. package/out/gateway/outboundHandles.js +373 -0
  323. package/out/gateway/outboundPaths.js +72 -0
  324. package/out/gateway/outboundSend.js +254 -0
  325. package/out/gateway/protocol.js +48 -0
  326. package/out/gateway/resetMappings.js +52 -0
  327. package/out/gateway/service.js +1244 -0
  328. package/out/gateway/sessionRouting.js +77 -0
  329. package/out/headless/index.js +505 -0
  330. package/out/headless/sync.js +473 -0
  331. package/out/mcp/publish.js +143 -0
  332. package/out/mcp.js +597 -0
  333. package/out/permissions.js +108 -0
  334. package/out/permissions.mapping.yaml +68 -0
  335. package/out/registry/index.js +332 -0
  336. package/out/remote/handleRemoteChat.js +122 -0
  337. package/out/remote/remotePackStatusBar.js +103 -0
  338. package/out/remote/serveClient.js +216 -0
  339. package/out/remoteSkills.js +191 -0
  340. package/out/runtime/index.js +32 -0
  341. package/out/runtime/interfaces/config-provider.js +32 -0
  342. package/out/runtime/interfaces/edit-tracker.js +22 -0
  343. package/out/runtime/interfaces/index.js +29 -0
  344. package/out/runtime/interfaces/logger.js +40 -0
  345. package/out/runtime/interfaces/permission-handler.js +66 -0
  346. package/out/runtime/interfaces/session-store.js +342 -0
  347. package/out/runtime/vscode/index.js +20 -0
  348. package/out/runtime/vscode/vscode-config-provider.js +77 -0
  349. package/out/runtime/vscode/vscode-edit-tracker.js +120 -0
  350. package/out/runtime/vscode/vscode-logger.js +30 -0
  351. package/out/runtime/vscode/vscode-permission-handler.js +72 -0
  352. package/out/runtime/vscode/vscode-session-store.js +22 -0
  353. package/out/scripts/agentide-litellm.ps1 +501 -0
  354. package/out/scripts/agentide-litellm.sh +700 -0
  355. package/out/scripts/agentide_litellm_hooks_v1.py +604 -0
  356. package/out/scripts/analytics/agent-fleet-widget.js +954 -0
  357. package/out/scripts/analytics/agentide-litellm-otel.config.yaml +160 -0
  358. package/out/scripts/analytics/default-seed-scripts.json +12 -0
  359. package/out/scripts/analytics/duckdbLocator.js +29 -0
  360. package/out/scripts/analytics/fleet-local-adapter.js +246 -0
  361. package/out/scripts/analytics/fleet-workflow.sh +263 -0
  362. package/out/scripts/analytics/generate-default-charts.py +685 -0
  363. package/out/scripts/analytics/hotspot-analysis-widget.js +909 -0
  364. package/out/scripts/analytics/index-traces.py +1394 -0
  365. package/out/scripts/analytics/install-analytics.sh +286 -0
  366. package/out/scripts/analytics/live-fleet-4-collector.cjs +315 -0
  367. package/out/scripts/analytics/live-fleet-4-daemon.mjs +134 -0
  368. package/out/scripts/analytics/live-fleet-4-widget.cjs +433 -0
  369. package/out/scripts/analytics/otel-collector.yaml +55 -0
  370. package/out/scripts/analytics/query-analytics.ps1 +396 -0
  371. package/out/scripts/analytics/query-analytics.sh +2551 -0
  372. package/out/scripts/analytics/session-comparison-widget.js +1468 -0
  373. package/out/scripts/analytics/session-timeline-widget.js +2137 -0
  374. package/out/scripts/analytics/setup-analytics.ps1 +432 -0
  375. package/out/scripts/analytics/setup-analytics.sh +854 -0
  376. package/out/scripts/analytics/start-analytics.ps1 +169 -0
  377. package/out/scripts/analytics/start-analytics.sh +323 -0
  378. package/out/scripts/analytics/token-cost-flamegraph-widget.js +954 -0
  379. package/out/scripts/analytics/token-cost-trend-widget.js +1062 -0
  380. package/out/scripts/analytics/workflows-collector.cjs +315 -0
  381. package/out/scripts/analytics/workflows-daemon.mjs +186 -0
  382. package/out/scripts/analytics/workflows-widget.cjs +433 -0
  383. package/out/scripts/analytics/wow-model-widget.js +191 -0
  384. package/out/scripts/gateway/Dockerfile.serve +45 -0
  385. package/out/scripts/gateway/docker-serve.sh +248 -0
  386. package/out/scripts/gateway/setup-gateway-server.ps1 +953 -0
  387. package/out/scripts/gateway/setup-gateway-server.sh +935 -0
  388. package/out/scripts/gateway/setup-gateway.ps1 +1813 -0
  389. package/out/scripts/gateway/setup-gateway.sh +1541 -0
  390. package/out/scripts/setup-cli.ps1 +86 -0
  391. package/out/scripts/setup-cli.sh +117 -0
  392. package/out/scripts/tools-manifest.json +83 -0
  393. package/out/scripts/vendor-tools.mjs +234 -0
  394. package/out/session.js +272 -0
  395. package/out/shared/agentEngines.js +170 -0
  396. package/out/shared/index.js +109 -0
  397. package/out/shared/models.js +235 -0
  398. package/out/shared/providers.js +202 -0
  399. package/out/skills/publish.js +178 -0
  400. package/out/spotlight/install.js +339 -0
  401. package/out/spotlight/publish.js +544 -0
  402. package/out/spotlight/yaml.js +31 -0
  403. package/out/store/github/setup.js +235 -0
  404. package/out/store/github/templates/build-index.ts +339 -0
  405. package/out/store/github/templates/build-index.yml +97 -0
  406. package/out/store/github/templates/package.json +11 -0
  407. package/out/store/index.js +755 -0
  408. package/out/store/oramaIndex.js +884 -0
  409. package/out/store/shared.js +241 -0
  410. package/out/sync.js +1827 -0
  411. package/out/terminalLogger.js +358 -0
  412. package/out/test/agentEnvFile.test.js +97 -0
  413. package/out/test/agentYaml.test.js +373 -0
  414. package/out/test/agentpack.test.js +639 -0
  415. package/out/test/analytics-lifecycle.test.js +174 -0
  416. package/out/test/analytics.test.js +524 -0
  417. package/out/test/auth-bootstrapApply.test.js +102 -0
  418. package/out/test/auth-broker.test.js +434 -0
  419. package/out/test/auth-callback.test.js +62 -0
  420. package/out/test/branding.test.js +229 -0
  421. package/out/test/cli-agent-id.test.js +55 -0
  422. package/out/test/credential-context.test.js +105 -0
  423. package/out/test/customEngineInstall.test.js +100 -0
  424. package/out/test/customRegistry.test.js +122 -0
  425. package/out/test/deploy-init.test.js +532 -0
  426. package/out/test/deps-auto-install.test.js +81 -0
  427. package/out/test/engine-integration.test.js +297 -0
  428. package/out/test/enginePaths.test.js +195 -0
  429. package/out/test/enginePublish.test.js +82 -0
  430. package/out/test/fleetDisplayUtils.test.js +96 -0
  431. package/out/test/gateway-client.test.js +381 -0
  432. package/out/test/gateway-lifecycle.test.js +608 -0
  433. package/out/test/gateway-outbound.test.js +531 -0
  434. package/out/test/gateway-reset-mappings.test.js +221 -0
  435. package/out/test/gateway-route-index.test.js +235 -0
  436. package/out/test/gateway-session-routing.test.js +47 -0
  437. package/out/test/gateway-session-store.test.js +216 -0
  438. package/out/test/github.test.js +232 -0
  439. package/out/test/headless-sync.test.js +279 -0
  440. package/out/test/helpers/manifest-fixtures.js +27 -0
  441. package/out/test/mcp-obo.test.js +82 -0
  442. package/out/test/mcp.test.js +307 -0
  443. package/out/test/openclaw-channel-routing.test.js +191 -0
  444. package/out/test/pathSafety.test.js +63 -0
  445. package/out/test/permissions.test.js +175 -0
  446. package/out/test/persistent-engine-worker.test.js +220 -0
  447. package/out/test/refreshPolicy.test.js +60 -0
  448. package/out/test/registry.test.js +351 -0
  449. package/out/test/runtime.test.js +349 -0
  450. package/out/test/scheduledFleetStorage.test.js +72 -0
  451. package/out/test/serve-hardening.test.js +192 -0
  452. package/out/test/serveClient.test.js +173 -0
  453. package/out/test/sessionJsonlFleet.test.js +78 -0
  454. package/out/test/shared.test.js +173 -0
  455. package/out/test/skills-mcp.test.js +299 -0
  456. package/out/test/spotlightPublish.test.js +299 -0
  457. package/out/test/spotlightYaml.test.js +58 -0
  458. package/out/test/state-sync-peer.test.js +74 -0
  459. package/out/test/store.test.js +331 -0
  460. package/out/test/stores.test.js +504 -0
  461. package/out/test/sync-switch.test.js +251 -0
  462. package/out/test/teamViewDiscovery.test.js +90 -0
  463. package/out/test/toolPath.test.js +120 -0
  464. package/out/test/utils.test.js +308 -0
  465. package/out/test/viewsConfigMerge.test.js +180 -0
  466. package/out/test/windows-command-line.test.js +75 -0
  467. package/out/utils/agentEnvFile.js +131 -0
  468. package/out/utils/binary.js +175 -0
  469. package/out/utils/index.js +37 -0
  470. package/out/utils/network.js +74 -0
  471. package/out/utils/pathSafety.js +60 -0
  472. package/out/utils/paths.js +116 -0
  473. package/out/utils/pid.js +180 -0
  474. package/out/utils/pythonRunner.js +258 -0
  475. package/out/utils/resolveNodeBinary.js +168 -0
  476. package/out/utils/toolPath.js +83 -0
  477. package/out/utils/windowsCommandLine.js +29 -0
  478. package/out/utils.js +915 -0
  479. package/out/workspaceFolders.js +59 -0
  480. package/package.json +41 -0
@@ -0,0 +1,1468 @@
1
+ #!/usr/bin/env node
2
+ /* Copyright (c) AgentHippo.ai. All rights reserved. */
3
+ /* eslint-disable no-console */
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+ const { execFileSync } = require('child_process');
7
+ const { findDuckdb } = require('./duckdbLocator');
8
+
9
+ const SESSION_LIMIT = 28;
10
+ const MAX_LLM_EVENTS_PER_SESSION = 220;
11
+ const MAX_TOOL_EVENTS_PER_SESSION = 260;
12
+ const MAX_SEQUENCE_RENDER = 36;
13
+ const MAX_MODELS_PER_SESSION = 14;
14
+ const MAX_TOOLS_PER_SESSION = 18;
15
+
16
+ function parseJsonArraysFromOutput(stdout) {
17
+ const results = [];
18
+ let depth = 0, start = -1;
19
+ for (let i = 0; i < stdout.length; i++) {
20
+ const c = stdout[i];
21
+ if (c === '[') { if (depth === 0) start = i; depth++; }
22
+ else if (c === ']') {
23
+ depth--;
24
+ if (depth === 0 && start >= 0) {
25
+ try { results.push(JSON.parse(stdout.slice(start, i + 1))); }
26
+ catch { results.push([]); }
27
+ start = -1;
28
+ }
29
+ }
30
+ }
31
+ return results;
32
+ }
33
+
34
+ function runSingleQuery(duckdbPath, sql, dbPath = '') {
35
+ const settings = "SET memory_limit='4GB';\nSET threads TO 1;\nSET preserve_insertion_order=false";
36
+ const args = [];
37
+ if (dbPath) {
38
+ args.push(dbPath);
39
+ }
40
+ args.push('-json', '-c', `${settings};\n${sql}`);
41
+ const stdout = execFileSync(duckdbPath, args, {
42
+ encoding: 'utf8',
43
+ maxBuffer: 30 * 1024 * 1024,
44
+ });
45
+ const arrays = parseJsonArraysFromOutput(stdout);
46
+ return arrays[0] || [];
47
+ }
48
+
49
+ function runBatchQueries(duckdbPath, queries, dbPath = '') {
50
+ const settings = "SET memory_limit='4GB';\nSET threads TO 1;\nSET preserve_insertion_order=false";
51
+ const allSql = settings + ';\n' + queries.map((q) => q.sql).join('\n');
52
+ const args = [];
53
+ if (dbPath) {
54
+ args.push(dbPath);
55
+ }
56
+ args.push('-json', '-c', allSql);
57
+ try {
58
+ const stdout = execFileSync(duckdbPath, args, {
59
+ encoding: 'utf8',
60
+ maxBuffer: 30 * 1024 * 1024,
61
+ });
62
+ const arrays = parseJsonArraysFromOutput(stdout);
63
+ const result = {};
64
+ queries.forEach((q, i) => { result[q.name] = arrays[i] || []; });
65
+ return { result, failed: false };
66
+ } catch (err) {
67
+ console.error(`session-comparison-widget batch query failed: ${err instanceof Error ? err.message : err}`);
68
+ const fallbackResult = {};
69
+ let anySuccess = false;
70
+ for (const query of queries) {
71
+ try {
72
+ fallbackResult[query.name] = runSingleQuery(duckdbPath, query.sql, dbPath);
73
+ anySuccess = true;
74
+ } catch (singleErr) {
75
+ console.error(`session-comparison-widget query fallback failed (${query.name}): ${singleErr instanceof Error ? singleErr.message : singleErr}`);
76
+ fallbackResult[query.name] = [];
77
+ }
78
+ }
79
+ return { result: fallbackResult, failed: !anySuccess };
80
+ }
81
+ }
82
+
83
+ function getIndexedDbPath(tracesDir) {
84
+ const configuredDbPath = String(process.env.AGENTIDE_INDEX_DB_PATH || '').trim();
85
+ const fallbackDbPath = path.join(tracesDir, 'analytics.duckdb');
86
+ const candidates = [configuredDbPath, fallbackDbPath].filter(Boolean);
87
+ for (const candidate of candidates) {
88
+ try {
89
+ const stat = fs.statSync(candidate);
90
+ if (stat.isFile()) {
91
+ return candidate;
92
+ }
93
+ } catch {
94
+ // continue searching
95
+ }
96
+ }
97
+ return '';
98
+ }
99
+
100
+ function hasJsonlFiles(dirPath) {
101
+ try {
102
+ const entries = fs.readdirSync(dirPath, { withFileTypes: true });
103
+ return entries.some((entry) => entry.isFile() && entry.name.endsWith('.jsonl'));
104
+ } catch {
105
+ return false;
106
+ }
107
+ }
108
+
109
+ function buildBaseQuery(tracesDir, useIndexed = false) {
110
+ if (useIndexed) {
111
+ return `
112
+ WITH extracted AS (
113
+ SELECT
114
+ trace_id,
115
+ COALESCE(span_name, 'Received Proxy Server Request') AS span_name,
116
+ COALESCE(duration_ms, 0) AS duration_ms,
117
+ start_ms,
118
+ 0 AS status_code,
119
+ session_id,
120
+ '' AS request_id,
121
+ COALESCE(agent_id, '') AS agent_id,
122
+ '' AS agent_version,
123
+ '' AS engine,
124
+ COALESCE(model, 'unknown') AS model,
125
+ COALESCE(cost, 0) AS cost,
126
+ COALESCE(input_tokens, 0) AS input_tokens,
127
+ COALESCE(output_tokens, 0) AS output_tokens
128
+ FROM spans
129
+ WHERE session_id IS NOT NULL AND session_id != ''
130
+ )
131
+ `;
132
+ }
133
+
134
+ const tracesGlob = `${tracesDir.replace(/'/g, "''")}/*.jsonl`;
135
+ return `
136
+ WITH raw_spans AS (
137
+ SELECT unnest(resourceSpans) AS rs
138
+ FROM read_json('${tracesGlob}', format='newline_delimited', ignore_errors=true)
139
+ ),
140
+ spans AS (
141
+ SELECT unnest(rs.scopeSpans) AS ss
142
+ FROM raw_spans
143
+ ),
144
+ flat_spans AS (
145
+ SELECT unnest(ss.spans) AS span
146
+ FROM spans
147
+ ),
148
+ parsed AS (
149
+ SELECT
150
+ span.traceId AS trace_id,
151
+ span.spanId AS span_id,
152
+ span.name AS span_name,
153
+ span.startTimeUnixNano::BIGINT / 1000000 AS start_ms,
154
+ span.endTimeUnixNano::BIGINT / 1000000 AS end_ms,
155
+ (span.endTimeUnixNano::BIGINT - span.startTimeUnixNano::BIGINT) / 1000000 AS duration_ms,
156
+ CAST(json_extract(to_json(span), '$.status.code') AS BIGINT) AS status_code,
157
+ json_extract(to_json(span), '$.attributes') AS attrs_json
158
+ FROM flat_spans
159
+ ),
160
+ span_filter AS (
161
+ SELECT trace_id, span_name, duration_ms, start_ms, status_code, attrs_json
162
+ FROM parsed
163
+ WHERE span_name = 'Received Proxy Server Request'
164
+ OR span_name = 'Failed Proxy Server Request'
165
+ OR EXISTS (
166
+ SELECT 1
167
+ FROM json_each(coalesce(attrs_json, '[]'::JSON)) AS attr
168
+ WHERE json_extract_string(attr.value, '$.key') = 'gen_ai.cost.total_cost'
169
+ )
170
+ ),
171
+ attrs AS (
172
+ SELECT
173
+ trace_id,
174
+ span_name,
175
+ duration_ms,
176
+ start_ms,
177
+ status_code,
178
+ json_extract_string(value, '$.key') AS attr_key,
179
+ json_extract_string(value, '$.value.stringValue') AS attr_string,
180
+ json_extract_string(value, '$.value.intValue') AS attr_int,
181
+ json_extract(value, '$.value.doubleValue') AS attr_double
182
+ FROM span_filter, json_each(coalesce(attrs_json, '[]'::JSON))
183
+ ),
184
+ extracted AS (
185
+ SELECT
186
+ trace_id,
187
+ span_name,
188
+ duration_ms,
189
+ start_ms,
190
+ max(status_code) AS status_code,
191
+ COALESCE(
192
+ NULLIF(regexp_extract(
193
+ max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END),
194
+ 'x-litellm-metadata-session-id'': ''([^'']+)''', 1
195
+ ), ''),
196
+ NULLIF(regexp_extract(
197
+ max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END),
198
+ 'session_id'': ''([^'']+)''', 1
199
+ ), '')
200
+ ) AS session_id,
201
+ COALESCE(
202
+ NULLIF(regexp_extract(
203
+ max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END),
204
+ 'x-litellm-metadata-request-id'': ''([^'']+)''', 1
205
+ ), ''),
206
+ NULLIF(regexp_extract(
207
+ max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END),
208
+ 'request_id'': ''([^'']+)''', 1
209
+ ), '')
210
+ ) AS request_id,
211
+ COALESCE(
212
+ NULLIF(regexp_extract(
213
+ max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END),
214
+ 'x-litellm-metadata-agent-id'': ''([^'']+)''', 1
215
+ ), ''),
216
+ NULLIF(regexp_extract(
217
+ max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END),
218
+ 'agent_id'': ''([^'']+)''', 1
219
+ ), '')
220
+ ) AS agent_id,
221
+ COALESCE(
222
+ NULLIF(regexp_extract(
223
+ max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END),
224
+ 'x-litellm-metadata-agent-version'': ''([^'']+)''', 1
225
+ ), ''),
226
+ NULLIF(regexp_extract(
227
+ max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END),
228
+ 'agent_version'': ''([^'']+)''', 1
229
+ ), '')
230
+ ) AS agent_version,
231
+ COALESCE(
232
+ NULLIF(regexp_extract(
233
+ max(CASE WHEN attr_key = 'metadata.requester_custom_headers' THEN attr_string END),
234
+ 'x-litellm-metadata-engine'': ''([^'']+)''', 1
235
+ ), ''),
236
+ NULLIF(regexp_extract(
237
+ max(CASE WHEN attr_key = 'metadata.requester_metadata' THEN attr_string END),
238
+ 'engine'': ''([^'']+)''', 1
239
+ ), '')
240
+ ) AS engine,
241
+ max(CASE WHEN attr_key IN ('gen_ai.request.model', 'gen_ai.response.model', 'model') THEN attr_string END) AS model,
242
+ max(CASE WHEN attr_key = 'gen_ai.cost.total_cost' THEN CAST(attr_double AS DOUBLE) END) AS cost,
243
+ max(CASE WHEN attr_key = 'gen_ai.usage.input_tokens' THEN CAST(attr_int AS BIGINT) END) AS input_tokens,
244
+ max(CASE WHEN attr_key = 'gen_ai.usage.output_tokens' THEN CAST(attr_int AS BIGINT) END) AS output_tokens
245
+ FROM attrs
246
+ GROUP BY trace_id, span_name, duration_ms, start_ms
247
+ )
248
+ `;
249
+ }
250
+
251
+ function buildFallbackHtml(message) {
252
+ return `<!doctype html><html><head><meta charset="utf-8"><title>Session Comparison</title></head><body style="font-family: ui-sans-serif, system-ui; background:#0f111a; color:#e5e7eb; margin:0; padding:20px;"><h2 style="margin:0 0 8px;">Session Comparison</h2><p style="opacity:.8;">${message}</p></body></html>`;
253
+ }
254
+
255
+ function isOutputDisabled(filename) {
256
+ const raw = String(process.env.AGENTIDE_DISABLED_CHARTS || '').trim();
257
+ if (!raw) {
258
+ return false;
259
+ }
260
+ const disabled = new Set(
261
+ raw
262
+ .split(',')
263
+ .map((entry) => path.basename(entry.trim()).toLowerCase())
264
+ .filter(Boolean),
265
+ );
266
+ return disabled.has(path.basename(filename).toLowerCase());
267
+ }
268
+
269
+ function toNumber(value, fallback = 0) {
270
+ const numeric = Number(value);
271
+ return Number.isFinite(numeric) ? numeric : fallback;
272
+ }
273
+
274
+ function sessionHandle(sessionId) {
275
+ const raw = String(sessionId || '').trim();
276
+ if (!raw) {
277
+ return 'unknown';
278
+ }
279
+ if (raw.length <= 20) {
280
+ return raw;
281
+ }
282
+ return `${raw.slice(0, 8)}...${raw.slice(-8)}`;
283
+ }
284
+
285
+ function parseSessionEventFile(filePath) {
286
+ if (!fs.existsSync(filePath)) {
287
+ return [];
288
+ }
289
+ try {
290
+ const raw = fs.readFileSync(filePath, 'utf8');
291
+ if (!raw.trim()) {
292
+ return [];
293
+ }
294
+ const rows = [];
295
+ for (const line of raw.split(/\r?\n/)) {
296
+ const trimmed = line.trim();
297
+ if (!trimmed) {
298
+ continue;
299
+ }
300
+ try {
301
+ rows.push(JSON.parse(trimmed));
302
+ } catch {
303
+ // ignore malformed line
304
+ }
305
+ }
306
+ rows.sort((a, b) => toNumber(a.timestamp) - toNumber(b.timestamp));
307
+ return rows;
308
+ } catch {
309
+ return [];
310
+ }
311
+ }
312
+
313
+ function computeToolSignals(sessionId, eventRows) {
314
+ const starts = [];
315
+ const toolCounts = new Map();
316
+ const perRequestToolCounts = new Map();
317
+ const consecutiveRuns = [];
318
+
319
+ let previousByRequest = new Map();
320
+ for (const row of eventRows) {
321
+ if (row.event_type !== 'tool_start') {
322
+ continue;
323
+ }
324
+ const payload = row && typeof row.payload === 'object' ? row.payload : {};
325
+ const toolName = String(payload.toolName || '(unknown tool)').trim() || '(unknown tool)';
326
+ const requestId = String(row.request_id || '(unknown)').trim() || '(unknown)';
327
+ const timestamp = Math.round(toNumber(row.timestamp));
328
+ starts.push({ requestId, toolName, timestamp });
329
+ toolCounts.set(toolName, (toolCounts.get(toolName) || 0) + 1);
330
+
331
+ const requestKey = `${requestId}::${toolName}`;
332
+ perRequestToolCounts.set(requestKey, (perRequestToolCounts.get(requestKey) || 0) + 1);
333
+
334
+ const prev = previousByRequest.get(requestId);
335
+ if (!prev || prev.toolName !== toolName) {
336
+ if (prev && prev.runLength >= 4) {
337
+ consecutiveRuns.push({
338
+ requestId,
339
+ toolName: prev.toolName,
340
+ count: prev.runLength,
341
+ startTs: prev.startTs,
342
+ endTs: prev.endTs,
343
+ });
344
+ }
345
+ previousByRequest.set(requestId, {
346
+ toolName,
347
+ runLength: 1,
348
+ startTs: timestamp,
349
+ endTs: timestamp,
350
+ });
351
+ } else {
352
+ prev.runLength += 1;
353
+ prev.endTs = timestamp;
354
+ previousByRequest.set(requestId, prev);
355
+ }
356
+ }
357
+
358
+ for (const [requestId, prev] of previousByRequest.entries()) {
359
+ if (prev.runLength >= 4) {
360
+ consecutiveRuns.push({
361
+ requestId,
362
+ toolName: prev.toolName,
363
+ count: prev.runLength,
364
+ startTs: prev.startTs,
365
+ endTs: prev.endTs,
366
+ });
367
+ }
368
+ }
369
+
370
+ const highFrequencyLoops = [];
371
+ for (const [requestTool, count] of perRequestToolCounts.entries()) {
372
+ if (count > 5) {
373
+ const pivot = requestTool.indexOf('::');
374
+ const requestId = requestTool.slice(0, pivot);
375
+ const toolName = requestTool.slice(pivot + 2);
376
+ highFrequencyLoops.push({ requestId, toolName, count });
377
+ }
378
+ }
379
+
380
+ highFrequencyLoops.sort((a, b) => b.count - a.count);
381
+ consecutiveRuns.sort((a, b) => b.count - a.count);
382
+ const toolSequence = starts
383
+ .slice(0, MAX_TOOL_EVENTS_PER_SESSION)
384
+ .map((item) => ({ ...item }));
385
+
386
+ const sortedCounts = [...toolCounts.entries()]
387
+ .map(([tool, count]) => ({ tool, count }))
388
+ .sort((a, b) => b.count - a.count || a.tool.localeCompare(b.tool))
389
+ .slice(0, MAX_TOOLS_PER_SESSION);
390
+
391
+ return {
392
+ sessionId,
393
+ toolCounts: sortedCounts,
394
+ toolSequence,
395
+ loops: {
396
+ highFrequency: highFrequencyLoops.slice(0, 8),
397
+ consecutive: consecutiveRuns.slice(0, 8),
398
+ },
399
+ };
400
+ }
401
+
402
+ function buildSessionSummariesSql(baseQuery) {
403
+ return `${baseQuery}
404
+ SELECT
405
+ session_id,
406
+ COALESCE(MAX(agent_id), '') AS agent_id,
407
+ COALESCE(MAX(agent_version), '') AS agent_version,
408
+ COALESCE(MAX(engine), '') AS engine,
409
+ ROUND(COALESCE(SUM(cost), 0), 6) AS total_cost,
410
+ COUNT(*) AS llm_calls,
411
+ COUNT(DISTINCT COALESCE(request_id, trace_id)) AS requests,
412
+ COALESCE(SUM(input_tokens), 0) + COALESCE(SUM(output_tokens), 0) AS total_tokens,
413
+ ROUND(COALESCE(AVG(duration_ms), 0), 0) AS avg_latency_ms,
414
+ ROUND(COALESCE(SUM(duration_ms), 0), 0) AS total_latency_ms,
415
+ MIN(start_ms) AS started_ms,
416
+ MAX(start_ms) AS latest_ms
417
+ FROM extracted
418
+ WHERE session_id IS NOT NULL AND session_id != ''
419
+ GROUP BY session_id
420
+ ORDER BY latest_ms DESC
421
+ LIMIT ${SESSION_LIMIT};`;
422
+ }
423
+
424
+ function buildModelBreakdownSql(baseQuery) {
425
+ return `${baseQuery}
426
+ SELECT
427
+ session_id,
428
+ COALESCE(model, 'unknown') AS model,
429
+ ROUND(COALESCE(SUM(cost), 0), 6) AS total_cost,
430
+ COUNT(*) AS calls,
431
+ COALESCE(SUM(input_tokens), 0) + COALESCE(SUM(output_tokens), 0) AS tokens
432
+ FROM extracted
433
+ WHERE session_id IN (
434
+ SELECT session_id FROM extracted
435
+ WHERE session_id IS NOT NULL AND session_id != ''
436
+ GROUP BY session_id ORDER BY MAX(start_ms) DESC LIMIT ${SESSION_LIMIT}
437
+ )
438
+ GROUP BY session_id, model
439
+ ORDER BY session_id, total_cost DESC;`;
440
+ }
441
+
442
+ function buildLlmSequenceSql(baseQuery) {
443
+ return `${baseQuery}
444
+ , ranked AS (
445
+ SELECT
446
+ session_id,
447
+ COALESCE(request_id, trace_id) AS request_id,
448
+ start_ms,
449
+ COALESCE(model, 'unknown') AS model,
450
+ COALESCE(cost, 0) AS cost,
451
+ ROW_NUMBER() OVER (PARTITION BY session_id ORDER BY start_ms ASC) AS rn
452
+ FROM extracted
453
+ WHERE session_id IN (
454
+ SELECT session_id FROM extracted
455
+ WHERE session_id IS NOT NULL AND session_id != ''
456
+ GROUP BY session_id ORDER BY MAX(start_ms) DESC LIMIT ${SESSION_LIMIT}
457
+ )
458
+ )
459
+ SELECT
460
+ session_id,
461
+ request_id,
462
+ start_ms,
463
+ model,
464
+ cost
465
+ FROM ranked
466
+ WHERE rn <= ${MAX_LLM_EVENTS_PER_SESSION}
467
+ ORDER BY session_id, start_ms ASC;`;
468
+ }
469
+
470
+ function buildSessionPayload(
471
+ sessionRows,
472
+ modelRows,
473
+ llmRows,
474
+ sessionToolSignals,
475
+ activeSessionId,
476
+ ) {
477
+ const bySessionModels = new Map();
478
+ for (const row of modelRows) {
479
+ const sessionId = String(row.session_id || '').trim();
480
+ if (!sessionId) {
481
+ continue;
482
+ }
483
+ if (!bySessionModels.has(sessionId)) {
484
+ bySessionModels.set(sessionId, []);
485
+ }
486
+ bySessionModels.get(sessionId).push({
487
+ model: String(row.model || 'unknown'),
488
+ cost: toNumber(row.total_cost),
489
+ calls: Math.max(0, Math.round(toNumber(row.calls))),
490
+ tokens: Math.max(0, Math.round(toNumber(row.tokens))),
491
+ });
492
+ }
493
+
494
+ const bySessionLlm = new Map();
495
+ for (const row of llmRows) {
496
+ const sessionId = String(row.session_id || '').trim();
497
+ if (!sessionId) {
498
+ continue;
499
+ }
500
+ if (!bySessionLlm.has(sessionId)) {
501
+ bySessionLlm.set(sessionId, []);
502
+ }
503
+ bySessionLlm.get(sessionId).push({
504
+ requestId: String(row.request_id || '(unknown)').trim() || '(unknown)',
505
+ startMs: Math.round(toNumber(row.start_ms)),
506
+ model: String(row.model || 'unknown'),
507
+ cost: toNumber(row.cost),
508
+ });
509
+ }
510
+
511
+ const toolSignalMap = new Map();
512
+ for (const entry of sessionToolSignals) {
513
+ toolSignalMap.set(entry.sessionId, entry);
514
+ }
515
+
516
+ const normalizedActive = String(activeSessionId || '').trim();
517
+ return sessionRows.map((row, index) => {
518
+ const sessionId = String(row.session_id || '').trim();
519
+ const models = (bySessionModels.get(sessionId) || [])
520
+ .sort((a, b) => b.cost - a.cost || a.model.localeCompare(b.model))
521
+ .slice(0, MAX_MODELS_PER_SESSION);
522
+ const llmSequence = (bySessionLlm.get(sessionId) || []).map((item) => ({ ...item }));
523
+ const toolSignal = toolSignalMap.get(sessionId) || {
524
+ toolCounts: [],
525
+ toolSequence: [],
526
+ loops: { highFrequency: [], consecutive: [] },
527
+ };
528
+ return {
529
+ sessionId,
530
+ handle: sessionHandle(sessionId),
531
+ isActive: normalizedActive ? sessionId === normalizedActive : index === 0,
532
+ agentId: String(row.agent_id || ''),
533
+ agentVersion: String(row.agent_version || ''),
534
+ engine: String(row.engine || ''),
535
+ totalCost: toNumber(row.total_cost),
536
+ llmCalls: Math.max(0, Math.round(toNumber(row.llm_calls))),
537
+ requestCount: Math.max(0, Math.round(toNumber(row.requests))),
538
+ totalTokens: Math.max(0, Math.round(toNumber(row.total_tokens))),
539
+ avgLatencyMs: Math.max(0, Math.round(toNumber(row.avg_latency_ms))),
540
+ totalLatencyMs: Math.max(0, Math.round(toNumber(row.total_latency_ms))),
541
+ startedMs: Math.round(toNumber(row.started_ms)),
542
+ latestMs: Math.round(toNumber(row.latest_ms)),
543
+ modelBreakdown: models,
544
+ llmSequence,
545
+ toolCounts: toolSignal.toolCounts || [],
546
+ toolSequence: toolSignal.toolSequence || [],
547
+ loops: toolSignal.loops || { highFrequency: [], consecutive: [] },
548
+ };
549
+ });
550
+ }
551
+
552
+ function buildHtml(payload) {
553
+ const safeJson = JSON.stringify(payload)
554
+ .replace(/</g, '\\u003c')
555
+ .replace(/>/g, '\\u003e')
556
+ .replace(/&/g, '\\u0026')
557
+ .replace(/\u2028/g, '\\u2028')
558
+ .replace(/\u2029/g, '\\u2029');
559
+
560
+ return `<!doctype html>
561
+ <html>
562
+ <head>
563
+ <meta charset="utf-8" />
564
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
565
+ <title>Session Comparison</title>
566
+ <style>
567
+ :root {
568
+ color-scheme: dark;
569
+ --bg: #0b1220;
570
+ --panel: #111b2f;
571
+ --panel-soft: #15243d;
572
+ --text: #e2e8f0;
573
+ --muted: #94a3b8;
574
+ --border: #21324d;
575
+ --accent: #3b82f6;
576
+ --good: #10b981;
577
+ --warn: #f59e0b;
578
+ --bad: #ef4444;
579
+ }
580
+ * { box-sizing: border-box; }
581
+ body {
582
+ margin: 0;
583
+ font: 13px/1.45 ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
584
+ background: var(--bg);
585
+ color: var(--text);
586
+ }
587
+ .root {
588
+ display: grid;
589
+ grid-template-columns: minmax(240px, 300px) 1fr;
590
+ min-height: 96vh;
591
+ }
592
+ .sidebar {
593
+ border-right: 1px solid var(--border);
594
+ background: #0d172b;
595
+ padding: 12px;
596
+ display: grid;
597
+ gap: 10px;
598
+ align-content: start;
599
+ }
600
+ .title-row {
601
+ display: flex;
602
+ align-items: center;
603
+ justify-content: space-between;
604
+ gap: 8px;
605
+ }
606
+ .title {
607
+ font-weight: 700;
608
+ font-size: 14px;
609
+ }
610
+ .pill {
611
+ border: 1px solid #25467d;
612
+ color: #93c5fd;
613
+ background: #132341;
614
+ border-radius: 999px;
615
+ padding: 2px 8px;
616
+ font-size: 11px;
617
+ }
618
+ .search {
619
+ width: 100%;
620
+ background: #0a1324;
621
+ border: 1px solid var(--border);
622
+ border-radius: 8px;
623
+ color: var(--text);
624
+ padding: 8px 10px;
625
+ outline: none;
626
+ }
627
+ .search:focus { border-color: var(--accent); }
628
+ .session-list {
629
+ max-height: 74vh;
630
+ overflow: auto;
631
+ display: grid;
632
+ gap: 8px;
633
+ padding-right: 2px;
634
+ }
635
+ .session-item {
636
+ border: 1px solid var(--border);
637
+ border-radius: 10px;
638
+ background: var(--panel);
639
+ padding: 9px;
640
+ display: grid;
641
+ gap: 6px;
642
+ }
643
+ .session-item-title {
644
+ font-weight: 600;
645
+ display: flex;
646
+ justify-content: space-between;
647
+ gap: 8px;
648
+ align-items: center;
649
+ }
650
+ .live {
651
+ border: 1px solid #14532d;
652
+ color: #86efac;
653
+ background: #052e16;
654
+ border-radius: 999px;
655
+ font-size: 10px;
656
+ padding: 1px 6px;
657
+ font-weight: 700;
658
+ }
659
+ .session-meta {
660
+ color: var(--muted);
661
+ font-size: 11px;
662
+ display: flex;
663
+ flex-wrap: wrap;
664
+ gap: 7px;
665
+ }
666
+ .assign {
667
+ display: flex;
668
+ gap: 6px;
669
+ }
670
+ .assign button {
671
+ flex: 1;
672
+ border: 1px solid #2f4f7d;
673
+ background: #11213c;
674
+ color: #cbd5e1;
675
+ border-radius: 7px;
676
+ font-size: 11px;
677
+ padding: 4px 7px;
678
+ cursor: pointer;
679
+ }
680
+ .assign button:hover { border-color: #3b82f6; color: #dbeafe; }
681
+ .main {
682
+ padding: 14px;
683
+ display: grid;
684
+ gap: 10px;
685
+ min-width: 0;
686
+ align-content: start;
687
+ }
688
+ .controls {
689
+ border: 1px solid var(--border);
690
+ border-radius: 12px;
691
+ background: var(--panel);
692
+ padding: 10px;
693
+ display: grid;
694
+ gap: 8px;
695
+ }
696
+ .selectors {
697
+ display: grid;
698
+ grid-template-columns: 1fr auto 1fr;
699
+ gap: 10px;
700
+ align-items: center;
701
+ }
702
+ .selectors label {
703
+ display: grid;
704
+ gap: 4px;
705
+ color: var(--muted);
706
+ font-size: 11px;
707
+ }
708
+ select {
709
+ width: 100%;
710
+ background: #0a1324;
711
+ border: 1px solid var(--border);
712
+ border-radius: 8px;
713
+ color: var(--text);
714
+ padding: 7px 8px;
715
+ font-size: 12px;
716
+ outline: none;
717
+ }
718
+ select:focus { border-color: var(--accent); }
719
+ .vs {
720
+ text-align: center;
721
+ font-size: 11px;
722
+ color: var(--muted);
723
+ border: 1px solid var(--border);
724
+ border-radius: 999px;
725
+ padding: 5px 8px;
726
+ background: #0f1b2f;
727
+ }
728
+ .quick-actions {
729
+ display: flex;
730
+ gap: 8px;
731
+ flex-wrap: wrap;
732
+ }
733
+ .quick-actions button {
734
+ border: 1px solid #2f4f7d;
735
+ background: #11213c;
736
+ color: #cbd5e1;
737
+ border-radius: 7px;
738
+ font-size: 11px;
739
+ padding: 5px 9px;
740
+ cursor: pointer;
741
+ }
742
+ .quick-actions button:hover { border-color: #3b82f6; color: #dbeafe; }
743
+ .summary {
744
+ border: 1px solid var(--border);
745
+ border-radius: 12px;
746
+ background: #0f1a2f;
747
+ padding: 10px;
748
+ display: grid;
749
+ gap: 8px;
750
+ }
751
+ .summary-title {
752
+ font-weight: 700;
753
+ color: #bfdbfe;
754
+ }
755
+ .summary-list {
756
+ margin: 0;
757
+ padding-left: 18px;
758
+ display: grid;
759
+ gap: 4px;
760
+ color: #cbd5e1;
761
+ font-size: 12px;
762
+ }
763
+ .metrics {
764
+ display: grid;
765
+ grid-template-columns: repeat(4, minmax(130px, 1fr));
766
+ gap: 8px;
767
+ }
768
+ .metric {
769
+ border: 1px solid var(--border);
770
+ border-radius: 8px;
771
+ background: #0d1729;
772
+ padding: 8px;
773
+ display: grid;
774
+ gap: 3px;
775
+ }
776
+ .metric-label { color: var(--muted); font-size: 11px; }
777
+ .metric-value { font-size: 15px; font-weight: 700; }
778
+ .metric-delta { font-size: 11px; }
779
+ .delta-good { color: #86efac; }
780
+ .delta-bad { color: #fca5a5; }
781
+ .delta-neutral { color: #cbd5e1; }
782
+ .panes {
783
+ display: grid;
784
+ grid-template-columns: 1fr 1fr;
785
+ gap: 8px;
786
+ }
787
+ .pane {
788
+ border: 1px solid var(--border);
789
+ border-radius: 10px;
790
+ background: var(--panel-soft);
791
+ padding: 9px;
792
+ display: grid;
793
+ gap: 6px;
794
+ }
795
+ .pane h3 {
796
+ margin: 0;
797
+ font-size: 12px;
798
+ color: #bfdbfe;
799
+ display: flex;
800
+ align-items: center;
801
+ justify-content: space-between;
802
+ gap: 8px;
803
+ }
804
+ .small {
805
+ color: var(--muted);
806
+ font-size: 11px;
807
+ }
808
+ table {
809
+ width: 100%;
810
+ border-collapse: collapse;
811
+ font-size: 12px;
812
+ background: #0b1628;
813
+ border: 1px solid var(--border);
814
+ border-radius: 8px;
815
+ overflow: hidden;
816
+ }
817
+ th, td {
818
+ padding: 7px 8px;
819
+ border-top: 1px solid rgba(148, 163, 184, 0.2);
820
+ text-align: left;
821
+ vertical-align: top;
822
+ }
823
+ th {
824
+ background: #101f37;
825
+ color: #93c5fd;
826
+ font-size: 11px;
827
+ border-top: none;
828
+ position: sticky;
829
+ top: 0;
830
+ z-index: 1;
831
+ }
832
+ .table-wrap {
833
+ overflow: auto;
834
+ max-height: 300px;
835
+ border-radius: 8px;
836
+ border: 1px solid var(--border);
837
+ }
838
+ .seq-wrap {
839
+ display: grid;
840
+ gap: 8px;
841
+ grid-template-columns: 1fr 1fr;
842
+ }
843
+ .seq-card {
844
+ border: 1px solid var(--border);
845
+ border-radius: 8px;
846
+ background: #0b1628;
847
+ padding: 8px;
848
+ display: grid;
849
+ gap: 6px;
850
+ }
851
+ .seq-list {
852
+ margin: 0;
853
+ padding-left: 18px;
854
+ color: #cbd5e1;
855
+ max-height: 220px;
856
+ overflow: auto;
857
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
858
+ font-size: 11px;
859
+ line-height: 1.4;
860
+ }
861
+ .loop-badge {
862
+ color: #fde68a;
863
+ border: 1px solid #78350f;
864
+ background: #422006;
865
+ border-radius: 999px;
866
+ font-size: 10px;
867
+ padding: 1px 6px;
868
+ margin-left: 6px;
869
+ white-space: nowrap;
870
+ }
871
+ .empty {
872
+ border: 1px dashed var(--border);
873
+ border-radius: 8px;
874
+ color: var(--muted);
875
+ background: #0c1525;
876
+ padding: 12px;
877
+ text-align: center;
878
+ }
879
+ @media (max-width: 1080px) {
880
+ .root { grid-template-columns: 1fr; }
881
+ .sidebar { border-right: none; border-bottom: 1px solid var(--border); }
882
+ .selectors { grid-template-columns: 1fr; }
883
+ .vs { justify-self: start; }
884
+ .metrics { grid-template-columns: repeat(2, minmax(120px, 1fr)); }
885
+ .panes { grid-template-columns: 1fr; }
886
+ .seq-wrap { grid-template-columns: 1fr; }
887
+ }
888
+ </style>
889
+ </head>
890
+ <body>
891
+ <div class="root">
892
+ <aside class="sidebar">
893
+ <div class="title-row">
894
+ <div class="title">Session Comparison</div>
895
+ <div class="pill">Interactive HTML</div>
896
+ </div>
897
+ <input id="session-search" class="search" placeholder="Search session / agent / version" />
898
+ <div id="session-list" class="session-list"></div>
899
+ </aside>
900
+ <main class="main">
901
+ <section class="controls">
902
+ <div class="selectors">
903
+ <label>
904
+ Session A
905
+ <select id="session-a"></select>
906
+ </label>
907
+ <div class="vs">VS</div>
908
+ <label>
909
+ Session B
910
+ <select id="session-b"></select>
911
+ </label>
912
+ </div>
913
+ <div class="quick-actions">
914
+ <button id="copy-a" type="button">Copy A session id</button>
915
+ <button id="copy-b" type="button">Copy B session id</button>
916
+ <button id="swap" type="button">Swap A/B</button>
917
+ </div>
918
+ </section>
919
+
920
+ <section class="summary">
921
+ <div class="summary-title">What changed</div>
922
+ <ul id="summary-list" class="summary-list"></ul>
923
+ </section>
924
+
925
+ <section class="metrics" id="metrics"></section>
926
+
927
+ <section class="panes" id="session-panes"></section>
928
+
929
+ <section class="table-wrap">
930
+ <table>
931
+ <thead>
932
+ <tr>
933
+ <th>Tool</th>
934
+ <th>A</th>
935
+ <th>B</th>
936
+ <th>Delta (B-A)</th>
937
+ </tr>
938
+ </thead>
939
+ <tbody id="tool-diff-body"></tbody>
940
+ </table>
941
+ </section>
942
+
943
+ <section class="table-wrap">
944
+ <table>
945
+ <thead>
946
+ <tr>
947
+ <th>Model</th>
948
+ <th>A Cost</th>
949
+ <th>B Cost</th>
950
+ <th>Calls A/B</th>
951
+ <th>Delta (B-A)</th>
952
+ </tr>
953
+ </thead>
954
+ <tbody id="model-diff-body"></tbody>
955
+ </table>
956
+ </section>
957
+
958
+ <section class="seq-wrap">
959
+ <div class="seq-card">
960
+ <div class="summary-title">Tool call order (first ${MAX_SEQUENCE_RENDER})</div>
961
+ <ol id="tool-seq-a" class="seq-list"></ol>
962
+ </div>
963
+ <div class="seq-card">
964
+ <div class="summary-title">Tool call order (first ${MAX_SEQUENCE_RENDER})</div>
965
+ <ol id="tool-seq-b" class="seq-list"></ol>
966
+ </div>
967
+ </section>
968
+
969
+ <section class="seq-wrap">
970
+ <div class="seq-card">
971
+ <div class="summary-title">LLM model order (first ${MAX_SEQUENCE_RENDER})</div>
972
+ <ol id="llm-seq-a" class="seq-list"></ol>
973
+ </div>
974
+ <div class="seq-card">
975
+ <div class="summary-title">LLM model order (first ${MAX_SEQUENCE_RENDER})</div>
976
+ <ol id="llm-seq-b" class="seq-list"></ol>
977
+ </div>
978
+ </section>
979
+ </main>
980
+ </div>
981
+ <script>
982
+ const data = ${safeJson};
983
+ const sessions = Array.isArray(data.sessions) ? data.sessions : [];
984
+ const byId = new Map(sessions.map((session) => [session.sessionId, session]));
985
+
986
+ const state = {
987
+ search: '',
988
+ selectedA: (() => {
989
+ const active = sessions.find((session) => session.isActive);
990
+ return active ? active.sessionId : (sessions[0] ? sessions[0].sessionId : '');
991
+ })(),
992
+ selectedB: (() => {
993
+ const active = sessions.find((session) => session.isActive);
994
+ const fallback = sessions.find((session) => !active || session.sessionId !== active.sessionId);
995
+ return fallback ? fallback.sessionId : (sessions[0] ? sessions[0].sessionId : '');
996
+ })(),
997
+ };
998
+
999
+ const esc = (v) => String(v ?? '')
1000
+ .replaceAll('&', '&amp;')
1001
+ .replaceAll('<', '&lt;')
1002
+ .replaceAll('>', '&gt;')
1003
+ .replaceAll('"', '&quot;')
1004
+ .replaceAll("'", '&#39;');
1005
+ const num = (v) => Number(v || 0);
1006
+ const fmtNum = (v) => num(v).toLocaleString();
1007
+ const fmtMoney = (v) => '$' + num(v).toFixed(4);
1008
+ const fmtMs = (v) => {
1009
+ const value = num(v);
1010
+ if (!value) return '-';
1011
+ if (value < 1000) return Math.round(value) + 'ms';
1012
+ return (value / 1000).toFixed(2) + 's';
1013
+ };
1014
+ const fmtAgo = (ms) => {
1015
+ const value = num(ms);
1016
+ if (!value) return '-';
1017
+ const deltaSec = Math.floor((Date.now() - value) / 1000);
1018
+ if (deltaSec < 60) return 'just now';
1019
+ if (deltaSec < 3600) return Math.floor(deltaSec / 60) + 'm ago';
1020
+ if (deltaSec < 86400) return Math.floor(deltaSec / 3600) + 'h ago';
1021
+ return Math.floor(deltaSec / 86400) + 'd ago';
1022
+ };
1023
+
1024
+ const el = {
1025
+ search: document.getElementById('session-search'),
1026
+ list: document.getElementById('session-list'),
1027
+ a: document.getElementById('session-a'),
1028
+ b: document.getElementById('session-b'),
1029
+ copyA: document.getElementById('copy-a'),
1030
+ copyB: document.getElementById('copy-b'),
1031
+ swap: document.getElementById('swap'),
1032
+ summaryList: document.getElementById('summary-list'),
1033
+ metrics: document.getElementById('metrics'),
1034
+ panes: document.getElementById('session-panes'),
1035
+ toolDiffBody: document.getElementById('tool-diff-body'),
1036
+ modelDiffBody: document.getElementById('model-diff-body'),
1037
+ toolSeqA: document.getElementById('tool-seq-a'),
1038
+ toolSeqB: document.getElementById('tool-seq-b'),
1039
+ llmSeqA: document.getElementById('llm-seq-a'),
1040
+ llmSeqB: document.getElementById('llm-seq-b'),
1041
+ };
1042
+
1043
+ function postCopy(sessionId) {
1044
+ if (!sessionId) return;
1045
+ window.parent.postMessage({
1046
+ source: 'agentide-wow-widget',
1047
+ type: 'copySessionId',
1048
+ sessionId,
1049
+ }, '*');
1050
+ }
1051
+
1052
+ function getFilteredSessions() {
1053
+ const query = state.search.trim().toLowerCase();
1054
+ if (!query) return sessions;
1055
+ return sessions.filter((session) => {
1056
+ const sid = String(session.sessionId || '').toLowerCase();
1057
+ const agent = String(session.agentId || '').toLowerCase();
1058
+ const engine = String(session.engine || '').toLowerCase();
1059
+ const version = String(session.agentVersion || '').toLowerCase();
1060
+ return sid.includes(query) || agent.includes(query) || engine.includes(query) || version.includes(query);
1061
+ });
1062
+ }
1063
+
1064
+ function renderSessionList() {
1065
+ const filtered = getFilteredSessions();
1066
+ if (!filtered.length) {
1067
+ el.list.innerHTML = '<div class="empty">No matching sessions.</div>';
1068
+ return;
1069
+ }
1070
+ el.list.innerHTML = filtered.map((session) => {
1071
+ const live = session.isActive ? '<span class="live">LIVE</span>' : '';
1072
+ return '<div class="session-item">' +
1073
+ '<div class="session-item-title"><span title="' + esc(session.sessionId) + '">' + esc(session.handle || session.sessionId) + '</span>' + live + '</div>' +
1074
+ '<div class="session-meta">' +
1075
+ '<span>' + fmtMoney(session.totalCost) + '</span>' +
1076
+ '<span>' + fmtNum(session.llmCalls) + ' LLM</span>' +
1077
+ '<span>' + fmtNum(session.requestCount) + ' req</span>' +
1078
+ '<span>' + esc(fmtAgo(session.latestMs)) + '</span>' +
1079
+ '</div>' +
1080
+ '<div class="session-meta">' +
1081
+ '<span>agent ' + esc(session.agentId || '(n/a)') + '</span>' +
1082
+ '<span>engine ' + esc(session.engine || '(n/a)') + '</span>' +
1083
+ '<span>version ' + esc(session.agentVersion || '(n/a)') + '</span>' +
1084
+ '</div>' +
1085
+ '<div class="assign">' +
1086
+ '<button type="button" data-slot="a" data-session-id="' + esc(session.sessionId) + '">Set A</button>' +
1087
+ '<button type="button" data-slot="b" data-session-id="' + esc(session.sessionId) + '">Set B</button>' +
1088
+ '</div>' +
1089
+ '</div>';
1090
+ }).join('');
1091
+
1092
+ el.list.querySelectorAll('button[data-slot]').forEach((btn) => {
1093
+ btn.addEventListener('click', () => {
1094
+ const slot = btn.getAttribute('data-slot');
1095
+ const sessionId = btn.getAttribute('data-session-id');
1096
+ if (!sessionId) return;
1097
+ if (slot === 'a') {
1098
+ state.selectedA = sessionId;
1099
+ if (state.selectedA === state.selectedB) {
1100
+ const other = sessions.find((session) => session.sessionId !== state.selectedA);
1101
+ if (other) state.selectedB = other.sessionId;
1102
+ }
1103
+ } else {
1104
+ state.selectedB = sessionId;
1105
+ if (state.selectedA === state.selectedB) {
1106
+ const other = sessions.find((session) => session.sessionId !== state.selectedB);
1107
+ if (other) state.selectedA = other.sessionId;
1108
+ }
1109
+ }
1110
+ render();
1111
+ });
1112
+ });
1113
+ }
1114
+
1115
+ function renderSelectors() {
1116
+ const options = sessions.map((session) =>
1117
+ '<option value="' + esc(session.sessionId) + '">' + esc((session.handle || session.sessionId) + ' · ' + (session.engine || 'engine:n/a') + ' · ' + fmtMoney(session.totalCost)) + '</option>'
1118
+ ).join('');
1119
+ el.a.innerHTML = options;
1120
+ el.b.innerHTML = options;
1121
+ if (!byId.has(state.selectedA) && sessions[0]) {
1122
+ state.selectedA = sessions[0].sessionId;
1123
+ }
1124
+ if (!byId.has(state.selectedB) && sessions[1]) {
1125
+ state.selectedB = sessions[1].sessionId;
1126
+ }
1127
+ if (state.selectedA === state.selectedB) {
1128
+ const other = sessions.find((session) => session.sessionId !== state.selectedA);
1129
+ if (other) {
1130
+ state.selectedB = other.sessionId;
1131
+ }
1132
+ }
1133
+ el.a.value = state.selectedA || '';
1134
+ el.b.value = state.selectedB || '';
1135
+ }
1136
+
1137
+ function deltaClass(delta, betterWhenLower = true) {
1138
+ if (delta === 0) return 'delta-neutral';
1139
+ const improved = betterWhenLower ? delta < 0 : delta > 0;
1140
+ return improved ? 'delta-good' : 'delta-bad';
1141
+ }
1142
+
1143
+ function ratioText(a, b) {
1144
+ const left = Math.max(0.000001, num(a));
1145
+ const right = Math.max(0.000001, num(b));
1146
+ return (right / left).toFixed(2) + 'x';
1147
+ }
1148
+
1149
+ function buildWhatChanged(a, b, toolRows, modelRows) {
1150
+ const lines = [];
1151
+ const costDelta = num(b.totalCost) - num(a.totalCost);
1152
+ if (Math.abs(costDelta) > 0.0005) {
1153
+ if (costDelta > 0) {
1154
+ lines.push('Session B cost ' + ratioText(a.totalCost, b.totalCost) + ' more (' + fmtMoney(b.totalCost) + ' vs ' + fmtMoney(a.totalCost) + ').');
1155
+ } else {
1156
+ lines.push('Session B cost less (' + fmtMoney(b.totalCost) + ' vs ' + fmtMoney(a.totalCost) + ').');
1157
+ }
1158
+ }
1159
+ const latencyDelta = num(b.totalLatencyMs) - num(a.totalLatencyMs);
1160
+ if (Math.abs(latencyDelta) > 100) {
1161
+ lines.push('Total latency changed by ' + fmtMs(Math.abs(latencyDelta)) + (latencyDelta > 0 ? ' higher in B.' : ' lower in B.'));
1162
+ }
1163
+ const tokenDelta = num(b.totalTokens) - num(a.totalTokens);
1164
+ if (Math.abs(tokenDelta) > 200) {
1165
+ lines.push('Token usage delta: ' + (tokenDelta > 0 ? '+' : '') + fmtNum(tokenDelta) + ' (B-A).');
1166
+ }
1167
+ const topToolShift = toolRows[0];
1168
+ if (topToolShift && Math.abs(topToolShift.delta) >= 3) {
1169
+ lines.push('Biggest tool shift: ' + topToolShift.tool + ' changed by ' + (topToolShift.delta > 0 ? '+' : '') + topToolShift.delta + ' calls.');
1170
+ }
1171
+ const loopB = Array.isArray(b.loops?.highFrequency) ? b.loops.highFrequency[0] : null;
1172
+ if (loopB) {
1173
+ lines.push('Loop risk in B: ' + loopB.toolName + ' called ' + loopB.count + ' times in request ' + loopB.requestId + '.');
1174
+ }
1175
+ const loopA = Array.isArray(a.loops?.highFrequency) ? a.loops.highFrequency[0] : null;
1176
+ if (loopA) {
1177
+ lines.push('Loop seen in A too: ' + loopA.toolName + ' x' + loopA.count + '.');
1178
+ }
1179
+ const newModelInB = modelRows.find((row) => row.callsA === 0 && row.callsB > 0);
1180
+ if (newModelInB) {
1181
+ lines.push('Session B used new model ' + newModelInB.model + ' (' + fmtMoney(newModelInB.costB) + ').');
1182
+ }
1183
+ if (!lines.length) {
1184
+ lines.push('No major regressions or shifts detected between these two sessions.');
1185
+ }
1186
+ return lines.slice(0, 7);
1187
+ }
1188
+
1189
+ function buildToolDiff(a, b) {
1190
+ const mapA = new Map((a.toolCounts || []).map((item) => [item.tool, item.count]));
1191
+ const mapB = new Map((b.toolCounts || []).map((item) => [item.tool, item.count]));
1192
+ const keys = new Set([...mapA.keys(), ...mapB.keys()]);
1193
+ const loopsA = new Set((a.loops?.highFrequency || []).map((loop) => loop.toolName));
1194
+ const loopsB = new Set((b.loops?.highFrequency || []).map((loop) => loop.toolName));
1195
+ return [...keys].map((tool) => {
1196
+ const countA = num(mapA.get(tool));
1197
+ const countB = num(mapB.get(tool));
1198
+ return {
1199
+ tool,
1200
+ countA,
1201
+ countB,
1202
+ delta: countB - countA,
1203
+ loopA: loopsA.has(tool),
1204
+ loopB: loopsB.has(tool),
1205
+ };
1206
+ }).sort((x, y) => Math.abs(y.delta) - Math.abs(x.delta) || y.countB - x.countB || x.tool.localeCompare(y.tool));
1207
+ }
1208
+
1209
+ function buildModelDiff(a, b) {
1210
+ const mapA = new Map((a.modelBreakdown || []).map((item) => [item.model, item]));
1211
+ const mapB = new Map((b.modelBreakdown || []).map((item) => [item.model, item]));
1212
+ const keys = new Set([...mapA.keys(), ...mapB.keys()]);
1213
+ return [...keys].map((model) => {
1214
+ const left = mapA.get(model) || { cost: 0, calls: 0 };
1215
+ const right = mapB.get(model) || { cost: 0, calls: 0 };
1216
+ return {
1217
+ model,
1218
+ costA: num(left.cost),
1219
+ costB: num(right.cost),
1220
+ callsA: num(left.calls),
1221
+ callsB: num(right.calls),
1222
+ delta: num(right.cost) - num(left.cost),
1223
+ };
1224
+ }).sort((x, y) => Math.abs(y.delta) - Math.abs(x.delta) || y.costB - x.costB || x.model.localeCompare(y.model));
1225
+ }
1226
+
1227
+ function renderMetrics(a, b) {
1228
+ const metrics = [
1229
+ { label: 'Cost', a: a.totalCost, b: b.totalCost, format: fmtMoney, lowerIsBetter: true },
1230
+ { label: 'Total latency', a: a.totalLatencyMs, b: b.totalLatencyMs, format: fmtMs, lowerIsBetter: true },
1231
+ { label: 'Tokens', a: a.totalTokens, b: b.totalTokens, format: fmtNum, lowerIsBetter: true },
1232
+ { label: 'LLM calls', a: a.llmCalls, b: b.llmCalls, format: fmtNum, lowerIsBetter: true },
1233
+ ];
1234
+ el.metrics.innerHTML = metrics.map((metric) => {
1235
+ const delta = num(metric.b) - num(metric.a);
1236
+ const sign = delta > 0 ? '+' : '';
1237
+ const cls = deltaClass(delta, metric.lowerIsBetter);
1238
+ return '<div class="metric">' +
1239
+ '<div class="metric-label">' + esc(metric.label) + '</div>' +
1240
+ '<div class="metric-value">' + esc(metric.format(metric.a)) + ' → ' + esc(metric.format(metric.b)) + '</div>' +
1241
+ '<div class="metric-delta ' + cls + '">' + esc(sign + (metric.label === 'Cost' ? fmtMoney(delta) : metric.format(delta)) + ' (B-A)') + '</div>' +
1242
+ '</div>';
1243
+ }).join('');
1244
+ }
1245
+
1246
+ function renderPanes(a, b) {
1247
+ const renderPane = (session, label) => {
1248
+ return '<div class="pane">' +
1249
+ '<h3>' + esc(label) + '<span class="small">' + esc(session.handle) + '</span></h3>' +
1250
+ '<div class="small">session: ' + esc(session.sessionId) + '</div>' +
1251
+ '<div class="small">agent: ' + esc(session.agentId || '(n/a)') + ' · engine: ' + esc(session.engine || '(n/a)') + ' · version: ' + esc(session.agentVersion || '(n/a)') + '</div>' +
1252
+ '<div class="small">started: ' + esc(new Date(session.startedMs).toLocaleString()) + ' · latest: ' + esc(fmtAgo(session.latestMs)) + '</div>' +
1253
+ '<div class="small">cost ' + fmtMoney(session.totalCost) + ' · requests ' + fmtNum(session.requestCount) + ' · avg latency ' + fmtMs(session.avgLatencyMs) + '</div>' +
1254
+ '<div class="small">loops: ' + fmtNum((session.loops?.highFrequency || []).length) + ' high-frequency, ' + fmtNum((session.loops?.consecutive || []).length) + ' consecutive-run</div>' +
1255
+ '</div>';
1256
+ };
1257
+ el.panes.innerHTML = renderPane(a, 'Session A') + renderPane(b, 'Session B');
1258
+ }
1259
+
1260
+ function renderToolDiff(toolRows) {
1261
+ if (!toolRows.length) {
1262
+ el.toolDiffBody.innerHTML = '<tr><td colspan="4"><div class="empty">No tool data available.</div></td></tr>';
1263
+ return;
1264
+ }
1265
+ el.toolDiffBody.innerHTML = toolRows.map((row) => {
1266
+ const cls = deltaClass(row.delta, true);
1267
+ const loopBadgeA = row.loopA ? '<span class="loop-badge">LOOP A</span>' : '';
1268
+ const loopBadgeB = row.loopB ? '<span class="loop-badge">LOOP B</span>' : '';
1269
+ return '<tr>' +
1270
+ '<td>' + esc(row.tool) + loopBadgeA + loopBadgeB + '</td>' +
1271
+ '<td>' + esc(fmtNum(row.countA)) + '</td>' +
1272
+ '<td>' + esc(fmtNum(row.countB)) + '</td>' +
1273
+ '<td class="' + cls + '">' + esc((row.delta > 0 ? '+' : '') + fmtNum(row.delta)) + '</td>' +
1274
+ '</tr>';
1275
+ }).join('');
1276
+ }
1277
+
1278
+ function renderModelDiff(modelRows) {
1279
+ if (!modelRows.length) {
1280
+ el.modelDiffBody.innerHTML = '<tr><td colspan="5"><div class="empty">No model data available.</div></td></tr>';
1281
+ return;
1282
+ }
1283
+ el.modelDiffBody.innerHTML = modelRows.map((row) => {
1284
+ const cls = deltaClass(row.delta, true);
1285
+ return '<tr>' +
1286
+ '<td>' + esc(row.model) + '</td>' +
1287
+ '<td>' + esc(fmtMoney(row.costA)) + '</td>' +
1288
+ '<td>' + esc(fmtMoney(row.costB)) + '</td>' +
1289
+ '<td>' + esc(fmtNum(row.callsA) + ' / ' + fmtNum(row.callsB)) + '</td>' +
1290
+ '<td class="' + cls + '">' + esc((row.delta > 0 ? '+' : '') + fmtMoney(row.delta)) + '</td>' +
1291
+ '</tr>';
1292
+ }).join('');
1293
+ }
1294
+
1295
+ function renderSequence(listEl, sequence, formatter) {
1296
+ if (!sequence.length) {
1297
+ listEl.innerHTML = '<li>(no events)</li>';
1298
+ return;
1299
+ }
1300
+ listEl.innerHTML = sequence
1301
+ .slice(0, ${MAX_SEQUENCE_RENDER})
1302
+ .map((item, index) => '<li>' + esc((index + 1) + '. ' + formatter(item)) + '</li>')
1303
+ .join('');
1304
+ }
1305
+
1306
+ function renderComparison() {
1307
+ const a = byId.get(state.selectedA);
1308
+ const b = byId.get(state.selectedB);
1309
+ if (!a || !b) {
1310
+ el.summaryList.innerHTML = '<li>Select two sessions to compare.</li>';
1311
+ el.metrics.innerHTML = '';
1312
+ el.panes.innerHTML = '';
1313
+ el.toolDiffBody.innerHTML = '<tr><td colspan="4"><div class="empty">Select sessions.</div></td></tr>';
1314
+ el.modelDiffBody.innerHTML = '<tr><td colspan="5"><div class="empty">Select sessions.</div></td></tr>';
1315
+ return;
1316
+ }
1317
+
1318
+ const toolRows = buildToolDiff(a, b);
1319
+ const modelRows = buildModelDiff(a, b);
1320
+ const summaryLines = buildWhatChanged(a, b, toolRows, modelRows);
1321
+ el.summaryList.innerHTML = summaryLines.map((line) => '<li>' + esc(line) + '</li>').join('');
1322
+ renderMetrics(a, b);
1323
+ renderPanes(a, b);
1324
+ renderToolDiff(toolRows);
1325
+ renderModelDiff(modelRows);
1326
+
1327
+ renderSequence(el.toolSeqA, a.toolSequence || [], (item) => (item.requestId || '(req)') + ' · ' + (item.toolName || '(tool)'));
1328
+ renderSequence(el.toolSeqB, b.toolSequence || [], (item) => (item.requestId || '(req)') + ' · ' + (item.toolName || '(tool)'));
1329
+ renderSequence(el.llmSeqA, a.llmSequence || [], (item) => (item.requestId || '(req)') + ' · ' + (item.model || 'unknown'));
1330
+ renderSequence(el.llmSeqB, b.llmSequence || [], (item) => (item.requestId || '(req)') + ' · ' + (item.model || 'unknown'));
1331
+ }
1332
+
1333
+ function render() {
1334
+ renderSelectors();
1335
+ renderSessionList();
1336
+ renderComparison();
1337
+ }
1338
+
1339
+ el.search.addEventListener('input', () => {
1340
+ state.search = el.search.value || '';
1341
+ renderSessionList();
1342
+ });
1343
+ el.a.addEventListener('change', () => {
1344
+ state.selectedA = el.a.value;
1345
+ if (state.selectedA === state.selectedB) {
1346
+ const other = sessions.find((session) => session.sessionId !== state.selectedA);
1347
+ if (other) {
1348
+ state.selectedB = other.sessionId;
1349
+ }
1350
+ }
1351
+ render();
1352
+ });
1353
+ el.b.addEventListener('change', () => {
1354
+ state.selectedB = el.b.value;
1355
+ if (state.selectedA === state.selectedB) {
1356
+ const other = sessions.find((session) => session.sessionId !== state.selectedB);
1357
+ if (other) {
1358
+ state.selectedA = other.sessionId;
1359
+ }
1360
+ }
1361
+ render();
1362
+ });
1363
+ el.copyA.addEventListener('click', () => {
1364
+ postCopy(state.selectedA);
1365
+ el.copyA.textContent = 'Copied A';
1366
+ setTimeout(() => { el.copyA.textContent = 'Copy A session id'; }, 1100);
1367
+ });
1368
+ el.copyB.addEventListener('click', () => {
1369
+ postCopy(state.selectedB);
1370
+ el.copyB.textContent = 'Copied B';
1371
+ setTimeout(() => { el.copyB.textContent = 'Copy B session id'; }, 1100);
1372
+ });
1373
+ el.swap.addEventListener('click', () => {
1374
+ const temp = state.selectedA;
1375
+ state.selectedA = state.selectedB;
1376
+ state.selectedB = temp;
1377
+ render();
1378
+ });
1379
+
1380
+ render();
1381
+ </script>
1382
+ </body>
1383
+ </html>`;
1384
+ }
1385
+
1386
+ function main() {
1387
+ const home = process.env.HOME || '';
1388
+ const tracesDir = process.env.AGENTIDE_TRACES_DIR || path.join(home, '.agent-hippo', 'analytics', 'traces');
1389
+ const sessionsDir = process.env.AGENTIDE_SESSIONS_DIR || path.join(home, '.agent-hippo', 'analytics', 'sessions');
1390
+ const chartDir = process.env.AGENTIDE_CHART_DIR || path.join(home, '.agent-hippo', 'analytics', 'views', 'global', '__all', '__all', 'charts');
1391
+ const outputPath = path.join(chartDir, 'session-comparison-widget.html');
1392
+ const activeSessionId = String(process.env.AGENTIDE_ACTIVE_SESSION_ID || '').trim();
1393
+
1394
+ fs.mkdirSync(chartDir, { recursive: true });
1395
+ if (isOutputDisabled('session-comparison-widget.html')) {
1396
+ try {
1397
+ fs.unlinkSync(outputPath);
1398
+ } catch {
1399
+ // ignore missing file
1400
+ }
1401
+ return;
1402
+ }
1403
+
1404
+ const duckdb = findDuckdb();
1405
+ if (!duckdb) {
1406
+ fs.writeFileSync(outputPath, buildFallbackHtml('DuckDB not found. Run Spotlight setup first.'), 'utf8');
1407
+ return;
1408
+ }
1409
+
1410
+ const indexedDbPath = getIndexedDbPath(tracesDir);
1411
+ if (!indexedDbPath && !hasJsonlFiles(tracesDir)) {
1412
+ fs.writeFileSync(outputPath, buildFallbackHtml('No session trace data available yet.'), 'utf8');
1413
+ return;
1414
+ }
1415
+
1416
+ const runQueries = (baseQuery, dbPath = '') => runBatchQueries(duckdb, [
1417
+ { name: 'sessions', sql: buildSessionSummariesSql(baseQuery) },
1418
+ { name: 'models', sql: buildModelBreakdownSql(baseQuery) },
1419
+ { name: 'llm', sql: buildLlmSequenceSql(baseQuery) },
1420
+ ], dbPath);
1421
+
1422
+ // Run against indexed DB first when available; fall back to JSONL if indexed query fails.
1423
+ let batch = runQueries(buildBaseQuery(tracesDir, Boolean(indexedDbPath)), indexedDbPath);
1424
+ if (indexedDbPath && batch.failed) {
1425
+ console.error('session-comparison-widget indexed query failed; falling back to JSONL parsing');
1426
+ batch = runQueries(buildBaseQuery(tracesDir, false));
1427
+ }
1428
+
1429
+ const batchResults = batch.result;
1430
+ const sessionRows = batchResults.sessions;
1431
+ if (!sessionRows.length) {
1432
+ fs.writeFileSync(outputPath, buildFallbackHtml('No sessions found for comparison.'), 'utf8');
1433
+ return;
1434
+ }
1435
+
1436
+ const modelRows = batchResults.models;
1437
+ const llmRows = batchResults.llm;
1438
+ const sessionIds = sessionRows
1439
+ .map((row) => String(row.session_id || '').trim())
1440
+ .filter(Boolean);
1441
+ const sessionToolSignals = sessionIds.map((sessionId) => {
1442
+ const eventRows = parseSessionEventFile(path.join(sessionsDir, `${sessionId}.jsonl`));
1443
+ return computeToolSignals(sessionId, eventRows);
1444
+ });
1445
+
1446
+ const sessions = buildSessionPayload(
1447
+ sessionRows,
1448
+ modelRows,
1449
+ llmRows,
1450
+ sessionToolSignals,
1451
+ activeSessionId,
1452
+ );
1453
+ const payload = {
1454
+ version: 1,
1455
+ generatedAt: new Date().toISOString(),
1456
+ activeSessionId: activeSessionId || null,
1457
+ limits: {
1458
+ sessionLimit: SESSION_LIMIT,
1459
+ maxLlmEventsPerSession: MAX_LLM_EVENTS_PER_SESSION,
1460
+ maxToolEventsPerSession: MAX_TOOL_EVENTS_PER_SESSION,
1461
+ maxSequenceRender: MAX_SEQUENCE_RENDER,
1462
+ },
1463
+ sessions,
1464
+ };
1465
+ fs.writeFileSync(outputPath, buildHtml(payload), 'utf8');
1466
+ }
1467
+
1468
+ main();