@agenthippo-ai/agenthippo 1.110.9 → 1.110.19

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 (533) hide show
  1. package/out/agentpack/types.js +2 -5
  2. package/out/analytics/dashboard/artifactManager.js +2 -439
  3. package/out/analytics/dashboard/fleetDisplayUtils.js +2 -162
  4. package/out/analytics/dashboard/fleetOrchestrator.js +3 -142
  5. package/out/analytics/dashboard/fleetRegistry.js +2 -79
  6. package/out/analytics/dashboard/fleetRowDefaults.js +2 -87
  7. package/out/analytics/dashboard/fleetScriptAdapter.js +2 -146
  8. package/out/analytics/dashboard/fleetTypes.js +2 -29
  9. package/out/analytics/dashboard/index.js +2 -17
  10. package/out/analytics/dashboard/indexingService.js +2 -422
  11. package/out/analytics/dashboard/localFleetAdapter.js +2 -222
  12. package/out/analytics/dashboard/performanceMonitor.js +5 -76
  13. package/out/analytics/dashboard/processManager.js +2 -209
  14. package/out/analytics/dashboard/provider.js +38 -3817
  15. package/out/analytics/dashboard/queryRunner.js +62 -750
  16. package/out/analytics/dashboard/scheduledFleetStorage.js +2 -241
  17. package/out/analytics/dashboard/sessionJsonlFleet.js +3 -204
  18. package/out/analytics/dashboard/teamViewDiscovery.js +2 -106
  19. package/out/analytics/dashboard/webview/panel.js +34 -2318
  20. package/out/analytics/dashboard/webview/runtime-bridge.js +2 -82
  21. package/out/analytics/lifecycle.js +2 -43
  22. package/out/analytics/service.js +8 -1277
  23. package/out/auth/authBaseUrl.js +2 -59
  24. package/out/auth/bootstrapApply.js +2 -71
  25. package/out/auth/brokerClient.js +2 -160
  26. package/out/auth/callbackUri.js +2 -59
  27. package/out/auth/githubStoreGate.js +2 -70
  28. package/out/auth/loopbackAuthServer.js +4 -159
  29. package/out/auth/onboardingPrompt.js +2 -112
  30. package/out/auth/pkce.js +2 -9
  31. package/out/auth/provider.js +2 -226
  32. package/out/auth/sessionStore.js +2 -156
  33. package/out/auth/signInFlow.js +2 -183
  34. package/out/cli/index.js +619 -551
  35. package/out/cli/run-custom-engine-runner.js +3 -318
  36. package/out/cli/vscode-shim.js +2 -86
  37. package/out/engines/factory.js +2 -45
  38. package/out/engines/types.js +2 -17
  39. package/out/examples/run-headless-repl.js +6 -286
  40. package/out/examples/run-headless.js +2 -187
  41. package/out/extension.js +32 -4473
  42. package/out/gateway/gatewayRouteIndex.js +6 -371
  43. package/out/gateway/gatewaySessionStore.js +3 -243
  44. package/out/gateway/index.js +2 -31
  45. package/out/gateway/resetMappings.js +2 -52
  46. package/out/gateway/service.js +5 -1249
  47. package/out/mcp/publish.js +2 -143
  48. package/out/remote/handleRemoteChat.js +2 -169
  49. package/out/remote/remotePackStatusBar.js +3 -103
  50. package/out/remote/remoteTrust.js +6 -277
  51. package/out/remote/serveClient.js +4 -277
  52. package/out/remoteSkills.js +3 -191
  53. package/out/runtime/index.js +2 -32
  54. package/out/runtime/vscode/index.js +2 -20
  55. package/out/runtime/vscode/vscode-config-provider.js +2 -77
  56. package/out/runtime/vscode/vscode-edit-tracker.js +2 -122
  57. package/out/runtime/vscode/vscode-logger.js +2 -30
  58. package/out/runtime/vscode/vscode-permission-handler.js +2 -72
  59. package/out/scripts/agentide-litellm.sh +0 -1
  60. package/out/scripts/agentide_litellm_hooks_v1.py +170 -418
  61. package/out/scripts/analytics/agent-fleet-widget.js +18 -257
  62. package/out/scripts/analytics/agentide-litellm-otel.config.yaml +4 -4
  63. package/out/scripts/analytics/duckdbLocator.js +2 -28
  64. package/out/scripts/analytics/generate-default-charts.py +43 -323
  65. package/out/scripts/analytics/hotspot-analysis-widget.js +4 -430
  66. package/out/scripts/analytics/index-traces.py +78 -1036
  67. package/out/scripts/analytics/session-comparison-widget.js +26 -472
  68. package/out/scripts/analytics/session-timeline-widget.js +25 -846
  69. package/out/scripts/analytics/setup-analytics.ps1 +8 -1
  70. package/out/scripts/analytics/setup-analytics.sh +13 -2
  71. package/out/scripts/analytics/token-cost-flamegraph-widget.js +4 -461
  72. package/out/scripts/analytics/token-cost-trend-widget.js +4 -534
  73. package/out/scripts/analytics/workflows-collector.cjs +4 -314
  74. package/out/scripts/analytics/workflows-daemon.mjs +9 -185
  75. package/out/scripts/analytics/workflows-widget.cjs +15 -159
  76. package/out/scripts/analytics/wow-model-widget.js +10 -120
  77. package/out/scripts/vendor-tools.mjs +2 -233
  78. package/out/skills/publish.js +3 -178
  79. package/out/store/github/setup.js +7 -212
  80. package/out/store/oramaIndex.js +2 -884
  81. package/out/sync.js +26 -1829
  82. package/out/terminalLogger.js +5 -358
  83. package/out/utils/pathSafety.js +2 -60
  84. package/out/utils/pythonRunner.js +4 -258
  85. package/out/utils/toolPath.js +2 -83
  86. package/out/workspaceFolders.js +2 -59
  87. package/package.json +1 -1
  88. package/out/agentpack/github.js +0 -547
  89. package/out/agentpack/index.js +0 -67
  90. package/out/agentpack/install.js +0 -297
  91. package/out/agentpack/manager.js +0 -441
  92. package/out/agentpack/manifest.js +0 -135
  93. package/out/agentpack/paths.js +0 -498
  94. package/out/agentpack/publish.js +0 -248
  95. package/out/agentpack/stores.js +0 -554
  96. package/out/analytics/dashboard/fleetStatusUtils.js +0 -30
  97. package/out/analytics/dashboard/fleetTitleUtils.js +0 -68
  98. package/out/analytics/dashboard/refreshPolicy.js +0 -16
  99. package/out/analytics/dashboard/scheduledFleetFingerprint.js +0 -22
  100. package/out/analytics/dashboard/scheduledFleetUtils.js +0 -22
  101. package/out/analytics/dashboard/sessionEventFleetStats.js +0 -118
  102. package/out/analytics/dashboard/viewsConfigMerge.js +0 -221
  103. package/out/analytics/dashboard/webview/codicon.ttf +0 -0
  104. package/out/analytics/identity.js +0 -0
  105. package/out/analytics/sessionEvents.js +0 -323
  106. package/out/auth/callbackUriParse.js +0 -59
  107. package/out/auth/types.js +0 -12
  108. package/out/auth-broker/config.js +0 -112
  109. package/out/auth-broker/jwt.js +0 -105
  110. package/out/auth-broker/pkce.js +0 -59
  111. package/out/auth-broker/server.js +0 -677
  112. package/out/auth-broker/store.js +0 -275
  113. package/out/authority/warrant.js +0 -206
  114. package/out/base-prompts/mcp-servers/.mcp.json +0 -13
  115. package/out/base-prompts/mcp-servers/langfuse-analytics/README.md +0 -142
  116. package/out/base-prompts/mcp-servers/langfuse-analytics/mcp.json +0 -13
  117. package/out/base-prompts/mcp-servers/langfuse-analytics/package-lock.json +0 -1192
  118. package/out/base-prompts/mcp-servers/langfuse-analytics/package.json +0 -23
  119. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/run-with-env.sh +0 -12
  120. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/sdk-test.js +0 -102
  121. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/simple-test.js +0 -137
  122. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/test-client.js +0 -118
  123. package/out/base-prompts/mcp-servers/langfuse-analytics/scripts/test-server.js +0 -209
  124. package/out/base-prompts/mcp-servers/langfuse-analytics/src/client.ts +0 -431
  125. package/out/base-prompts/mcp-servers/langfuse-analytics/src/index.ts +0 -270
  126. package/out/base-prompts/mcp-servers/langfuse-analytics/tsconfig.json +0 -18
  127. package/out/base-prompts/rules/AGENTS.md +0 -204
  128. package/out/base-prompts/skills/agent-browser/SKILL.md +0 -102
  129. package/out/base-prompts/skills/agent-builder/SKILL.md +0 -73
  130. package/out/base-prompts/skills/agent-builder/references/agent-pack.md +0 -63
  131. package/out/base-prompts/skills/agent-builder/references/crewai.md +0 -66
  132. package/out/base-prompts/skills/agent-builder/references/langchain.md +0 -60
  133. package/out/base-prompts/skills/agent-builder/references/raw-sdk.md +0 -73
  134. package/out/base-prompts/skills/agent-engine-creator/SKILL.md +0 -586
  135. package/out/base-prompts/skills/agent-engine-installer/SKILL.md +0 -106
  136. package/out/base-prompts/skills/agent-engine-installer/scripts/install-engine.mjs +0 -259
  137. package/out/base-prompts/skills/agent-engine-installer/scripts/install-engine.ps1 +0 -189
  138. package/out/base-prompts/skills/agenthippo-cli/SKILL.md +0 -69
  139. package/out/base-prompts/skills/agenthippo-cli/scripts/install.sh +0 -63
  140. package/out/base-prompts/skills/agentpack-creator/SKILL.md +0 -375
  141. package/out/base-prompts/skills/agentpack-creator/references/agent-templates.md +0 -261
  142. package/out/base-prompts/skills/agentpack-creator/references/agents-md-guide.md +0 -195
  143. package/out/base-prompts/skills/agentpack-creator/scripts/init_pack.py +0 -391
  144. package/out/base-prompts/skills/agentpack-creator/scripts/init_pack_test.py +0 -294
  145. package/out/base-prompts/skills/agentpack-testbench/SKILL.md +0 -135
  146. package/out/base-prompts/skills/algorithmic-art/LICENSE.txt +0 -202
  147. package/out/base-prompts/skills/algorithmic-art/SKILL.md +0 -405
  148. package/out/base-prompts/skills/algorithmic-art/templates/generator_template.js +0 -223
  149. package/out/base-prompts/skills/algorithmic-art/templates/viewer.html +0 -599
  150. package/out/base-prompts/skills/debug-agent/SKILL.md +0 -83
  151. package/out/base-prompts/skills/desktop-automation/SKILL.md +0 -166
  152. package/out/base-prompts/skills/desktop-automation/references/agenthippo-ui-recipes.md +0 -441
  153. package/out/base-prompts/skills/desktop-automation/scripts/windows-desktop-uia.psm1 +0 -280
  154. package/out/base-prompts/skills/docx/LICENSE.txt +0 -30
  155. package/out/base-prompts/skills/docx/SKILL.md +0 -197
  156. package/out/base-prompts/skills/docx/docx-js.md +0 -350
  157. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  158. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  159. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  160. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  161. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  162. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  163. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  164. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  165. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  166. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  167. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  168. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  169. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  170. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  171. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  172. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  173. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  174. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  175. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  176. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  177. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  178. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  179. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  180. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  181. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  182. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  183. package/out/base-prompts/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  184. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  185. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  186. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  187. package/out/base-prompts/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  188. package/out/base-prompts/skills/docx/ooxml/schemas/mce/mc.xsd +0 -75
  189. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  190. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  191. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  192. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  193. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  194. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  195. package/out/base-prompts/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  196. package/out/base-prompts/skills/docx/ooxml/scripts/pack.py +0 -159
  197. package/out/base-prompts/skills/docx/ooxml/scripts/unpack.py +0 -29
  198. package/out/base-prompts/skills/docx/ooxml/scripts/validate.py +0 -69
  199. package/out/base-prompts/skills/docx/ooxml/scripts/validation/__init__.py +0 -15
  200. package/out/base-prompts/skills/docx/ooxml/scripts/validation/base.py +0 -951
  201. package/out/base-prompts/skills/docx/ooxml/scripts/validation/docx.py +0 -274
  202. package/out/base-prompts/skills/docx/ooxml/scripts/validation/pptx.py +0 -315
  203. package/out/base-prompts/skills/docx/ooxml/scripts/validation/redlining.py +0 -279
  204. package/out/base-prompts/skills/docx/ooxml.md +0 -610
  205. package/out/base-prompts/skills/docx/scripts/__init__.py +0 -1
  206. package/out/base-prompts/skills/docx/scripts/document.py +0 -1276
  207. package/out/base-prompts/skills/docx/scripts/templates/comments.xml +0 -3
  208. package/out/base-prompts/skills/docx/scripts/templates/commentsExtended.xml +0 -3
  209. package/out/base-prompts/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
  210. package/out/base-prompts/skills/docx/scripts/templates/commentsIds.xml +0 -3
  211. package/out/base-prompts/skills/docx/scripts/templates/people.xml +0 -3
  212. package/out/base-prompts/skills/docx/scripts/utilities.py +0 -374
  213. package/out/base-prompts/skills/gateway-outbound/SKILL.md +0 -136
  214. package/out/base-prompts/skills/mcp-builder/LICENSE.txt +0 -202
  215. package/out/base-prompts/skills/mcp-builder/SKILL.md +0 -328
  216. package/out/base-prompts/skills/mcp-builder/reference/evaluation.md +0 -602
  217. package/out/base-prompts/skills/mcp-builder/reference/mcp_best_practices.md +0 -249
  218. package/out/base-prompts/skills/mcp-builder/reference/node_mcp_server.md +0 -970
  219. package/out/base-prompts/skills/mcp-builder/reference/python_mcp_server.md +0 -719
  220. package/out/base-prompts/skills/mcp-builder/scripts/connections.py +0 -151
  221. package/out/base-prompts/skills/mcp-builder/scripts/evaluation.py +0 -373
  222. package/out/base-prompts/skills/mcp-builder/scripts/example_evaluation.xml +0 -22
  223. package/out/base-prompts/skills/mcp-builder/scripts/requirements.txt +0 -2
  224. package/out/base-prompts/skills/openclaw/SKILL.md +0 -406
  225. package/out/base-prompts/skills/openclaw/scripts/run-smoke.sh +0 -61
  226. package/out/base-prompts/skills/pdf/LICENSE.txt +0 -30
  227. package/out/base-prompts/skills/pdf/SKILL.md +0 -294
  228. package/out/base-prompts/skills/pdf/forms.md +0 -205
  229. package/out/base-prompts/skills/pdf/reference.md +0 -612
  230. package/out/base-prompts/skills/pdf/scripts/check_bounding_boxes.py +0 -70
  231. package/out/base-prompts/skills/pdf/scripts/check_bounding_boxes_test.py +0 -226
  232. package/out/base-prompts/skills/pdf/scripts/check_fillable_fields.py +0 -12
  233. package/out/base-prompts/skills/pdf/scripts/convert_pdf_to_images.py +0 -35
  234. package/out/base-prompts/skills/pdf/scripts/create_validation_image.py +0 -41
  235. package/out/base-prompts/skills/pdf/scripts/extract_form_field_info.py +0 -152
  236. package/out/base-prompts/skills/pdf/scripts/fill_fillable_fields.py +0 -114
  237. package/out/base-prompts/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -108
  238. package/out/base-prompts/skills/pptx/LICENSE.txt +0 -30
  239. package/out/base-prompts/skills/pptx/SKILL.md +0 -484
  240. package/out/base-prompts/skills/pptx/html2pptx.md +0 -625
  241. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  242. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  243. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  244. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  245. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  246. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  247. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  248. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  249. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  250. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  251. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  252. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  253. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  254. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  255. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  256. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  257. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  258. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  259. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  260. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  261. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  262. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  263. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  264. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  265. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  266. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  267. package/out/base-prompts/skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  268. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  269. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  270. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  271. package/out/base-prompts/skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  272. package/out/base-prompts/skills/pptx/ooxml/schemas/mce/mc.xsd +0 -75
  273. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  274. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  275. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  276. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  277. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  278. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  279. package/out/base-prompts/skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  280. package/out/base-prompts/skills/pptx/ooxml/scripts/pack.py +0 -159
  281. package/out/base-prompts/skills/pptx/ooxml/scripts/unpack.py +0 -29
  282. package/out/base-prompts/skills/pptx/ooxml/scripts/validate.py +0 -69
  283. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/__init__.py +0 -15
  284. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/base.py +0 -951
  285. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/docx.py +0 -274
  286. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/pptx.py +0 -315
  287. package/out/base-prompts/skills/pptx/ooxml/scripts/validation/redlining.py +0 -279
  288. package/out/base-prompts/skills/pptx/ooxml.md +0 -427
  289. package/out/base-prompts/skills/pptx/scripts/html2pptx.js +0 -984
  290. package/out/base-prompts/skills/pptx/scripts/inventory.py +0 -1020
  291. package/out/base-prompts/skills/pptx/scripts/rearrange.py +0 -231
  292. package/out/base-prompts/skills/pptx/scripts/replace.py +0 -385
  293. package/out/base-prompts/skills/pptx/scripts/thumbnail.py +0 -450
  294. package/out/base-prompts/skills/prompt-lab/SKILL.md +0 -149
  295. package/out/base-prompts/skills/prompt-lab/scripts/query-analytics.sh +0 -2551
  296. package/out/base-prompts/skills/skill-creator/LICENSE.txt +0 -202
  297. package/out/base-prompts/skills/skill-creator/SKILL.md +0 -371
  298. package/out/base-prompts/skills/skill-creator/references/output-patterns.md +0 -82
  299. package/out/base-prompts/skills/skill-creator/references/workflows.md +0 -28
  300. package/out/base-prompts/skills/skill-creator/scripts/init_skill.py +0 -303
  301. package/out/base-prompts/skills/skill-creator/scripts/package_skill.py +0 -110
  302. package/out/base-prompts/skills/skill-creator/scripts/quick_validate.py +0 -95
  303. package/out/base-prompts/skills/skill-installer/SKILL.md +0 -251
  304. package/out/base-prompts/skills/skill-installer/references/store-api.md +0 -219
  305. package/out/base-prompts/skills/spotlight-dashboard/SKILL.md +0 -83
  306. package/out/base-prompts/skills/spotlight-insights/SKILL.md +0 -101
  307. package/out/base-prompts/skills/spotlight-optimize/SKILL.md +0 -93
  308. package/out/base-prompts/skills/spotlight-optimize/scripts/query-analytics.sh +0 -2551
  309. package/out/base-prompts/skills/spotlight-query/SKILL.md +0 -314
  310. package/out/base-prompts/skills/spotlight-query/references/otel-schema.md +0 -163
  311. package/out/base-prompts/skills/spotlight-query/scripts/query-analytics.sh +0 -2551
  312. package/out/base-prompts/skills/spotlight-query/scripts/query-analytics.test.sh +0 -744
  313. package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/sessions/sess-test-1.jsonl +0 -14
  314. package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/traces/.duckdb-index.json +0 -7
  315. package/out/base-prompts/skills/spotlight-query/scripts/test-fixtures/analytics/traces/sample.jsonl +0 -5
  316. package/out/base-prompts/skills/spotlight-report/SKILL.md +0 -86
  317. package/out/base-prompts/skills/spotlight-visualize/SKILL.md +0 -92
  318. package/out/base-prompts/skills/spotlight-visualize/scripts/query-analytics.sh +0 -2551
  319. package/out/base-prompts/skills/xlsx/LICENSE.txt +0 -30
  320. package/out/base-prompts/skills/xlsx/SKILL.md +0 -289
  321. package/out/base-prompts/skills/xlsx/recalc.py +0 -178
  322. package/out/branding.js +0 -49
  323. package/out/cli/commands/anywhere.js +0 -66
  324. package/out/cli/commands/ask.js +0 -246
  325. package/out/cli/commands/auth-broker.js +0 -24
  326. package/out/cli/commands/auth.js +0 -219
  327. package/out/cli/commands/chat.js +0 -357
  328. package/out/cli/commands/conformance.js +0 -249
  329. package/out/cli/commands/data-plane.js +0 -67
  330. package/out/cli/commands/deploy.js +0 -263
  331. package/out/cli/commands/gateway.js +0 -218
  332. package/out/cli/commands/idp.js +0 -85
  333. package/out/cli/commands/pack.js +0 -399
  334. package/out/cli/commands/probe-custom-engine.js +0 -82
  335. package/out/cli/commands/sandbox.js +0 -332
  336. package/out/cli/commands/serve.js +0 -1758
  337. package/out/cli/commands/store.js +0 -927
  338. package/out/cli/credential-context.js +0 -91
  339. package/out/cli/data-plane/broker.js +0 -78
  340. package/out/cli/data-plane/connector-host.js +0 -121
  341. package/out/cli/data-plane/egress.js +0 -189
  342. package/out/cli/data-plane/gateway.js +0 -218
  343. package/out/cli/data-plane/index.js +0 -152
  344. package/out/cli/data-plane/registry.js +0 -89
  345. package/out/cli/data-plane/remote.js +0 -106
  346. package/out/cli/data-plane/sidecar.js +0 -161
  347. package/out/cli/deploy/blueprints/registry.js +0 -84
  348. package/out/cli/deploy/layers/identity.js +0 -172
  349. package/out/cli/deploy-init.js +0 -3023
  350. package/out/cli/deps.js +0 -342
  351. package/out/cli/idp-init.js +0 -338
  352. package/out/cli/pack-install-ref.js +0 -15
  353. package/out/cli/pack-loader.js +0 -418
  354. package/out/cli/pack-runtime.js +0 -61
  355. package/out/cli/runtime.js +0 -450
  356. package/out/cli/serve-hardening.js +0 -607
  357. package/out/cli/state-sync.js +0 -424
  358. package/out/cli/utils.js +0 -231
  359. package/out/engines/base.js +0 -188
  360. package/out/engines/claude.js +0 -1254
  361. package/out/engines/codex.js +0 -862
  362. package/out/engines/custom.js +0 -674
  363. package/out/engines/customEngineIpc.js +0 -95
  364. package/out/engines/customEngineRunnerEmbed.js +0 -107
  365. package/out/engines/customRegistry.js +0 -274
  366. package/out/engines/embeddedCustomEngineRunner.generated.js +0 -9
  367. package/out/engines/engineApiKey.js +0 -138
  368. package/out/engines/install.js +0 -372
  369. package/out/engines/persistentEngineWorker.js +0 -320
  370. package/out/engines/publish.js +0 -341
  371. package/out/evidence/sessionShip.js +0 -610
  372. package/out/gateway/bridge.js +0 -843
  373. package/out/gateway/cli-agent-id.js +0 -36
  374. package/out/gateway/client.js +0 -336
  375. package/out/gateway/client.test.js +0 -395
  376. package/out/gateway/gateway-scope-errors.js +0 -14
  377. package/out/gateway/gatewayBackup.js +0 -76
  378. package/out/gateway/installSource.js +0 -68
  379. package/out/gateway/lifecycle.js +0 -3190
  380. package/out/gateway/openclaw-channel-routing.js +0 -317
  381. package/out/gateway/outboundHandles.js +0 -373
  382. package/out/gateway/outboundPaths.js +0 -75
  383. package/out/gateway/outboundSend.js +0 -254
  384. package/out/gateway/protocol.js +0 -48
  385. package/out/gateway/secretsPreflight.js +0 -90
  386. package/out/gateway/sessionRouting.js +0 -77
  387. package/out/headless/index.js +0 -539
  388. package/out/headless/sync.js +0 -473
  389. package/out/live-e2e/mcp-demo/server.mjs +0 -32
  390. package/out/live-probe/connector.mjs +0 -8
  391. package/out/live-probe/env-report.mjs +0 -8
  392. package/out/mcp.js +0 -654
  393. package/out/pack/caps.js +0 -271
  394. package/out/pack/governor.js +0 -309
  395. package/out/pack/lease-schema.js +0 -270
  396. package/out/pack/lease.js +0 -640
  397. package/out/pack/reconcile.js +0 -217
  398. package/out/pack/signature.js +0 -839
  399. package/out/permissions.js +0 -108
  400. package/out/permissions.mapping.yaml +0 -68
  401. package/out/registry/index.js +0 -332
  402. package/out/runner/host.js +0 -131
  403. package/out/runner/protocol.js +0 -108
  404. package/out/runner/remoteEngine.js +0 -130
  405. package/out/runtime/interfaces/config-provider.js +0 -32
  406. package/out/runtime/interfaces/edit-tracker.js +0 -24
  407. package/out/runtime/interfaces/index.js +0 -29
  408. package/out/runtime/interfaces/logger.js +0 -40
  409. package/out/runtime/interfaces/permission-handler.js +0 -66
  410. package/out/runtime/interfaces/session-store.js +0 -342
  411. package/out/runtime/vscode/vscode-session-store.js +0 -22
  412. package/out/sandbox/containerDriver.js +0 -570
  413. package/out/sandbox/processDriver.js +0 -315
  414. package/out/sandbox/sandboxRunner.js +0 -191
  415. package/out/sandbox/support.js +0 -301
  416. package/out/sandbox/types.js +0 -55
  417. package/out/scripts/analytics/fleet-local-adapter.js +0 -246
  418. package/out/scripts/analytics/live-fleet-4-collector.cjs +0 -315
  419. package/out/scripts/analytics/live-fleet-4-daemon.mjs +0 -134
  420. package/out/scripts/analytics/live-fleet-4-widget.cjs +0 -433
  421. package/out/scripts/gateway/Dockerfile.serve +0 -45
  422. package/out/scripts/gateway/docker-serve.sh +0 -248
  423. package/out/scripts/gateway/setup-gateway-server.ps1 +0 -953
  424. package/out/scripts/gateway/setup-gateway-server.sh +0 -935
  425. package/out/session.js +0 -272
  426. package/out/shared/agentEngines.js +0 -170
  427. package/out/shared/index.js +0 -109
  428. package/out/shared/models.js +0 -136
  429. package/out/shared/providers.js +0 -205
  430. package/out/spotlight/install.js +0 -345
  431. package/out/spotlight/publish.js +0 -544
  432. package/out/spotlight/yaml.js +0 -31
  433. package/out/store/index.js +0 -786
  434. package/out/store/shared.js +0 -241
  435. package/out/test/agentEnvFile.test.js +0 -97
  436. package/out/test/agentYaml.test.js +0 -373
  437. package/out/test/agentpack.test.js +0 -639
  438. package/out/test/analytics-lifecycle.test.js +0 -174
  439. package/out/test/analytics.test.js +0 -756
  440. package/out/test/auth-baseurl.test.js +0 -132
  441. package/out/test/auth-bootstrapApply.test.js +0 -123
  442. package/out/test/auth-broker-hardening.test.js +0 -227
  443. package/out/test/auth-broker.test.js +0 -541
  444. package/out/test/auth-callback.test.js +0 -62
  445. package/out/test/blueprint-registry.test.js +0 -105
  446. package/out/test/branding.test.js +0 -242
  447. package/out/test/caps-governor.test.js +0 -455
  448. package/out/test/cli-agent-id.test.js +0 -55
  449. package/out/test/codex-new-file-survival.test.js +0 -119
  450. package/out/test/configuration-scopes.test.js +0 -131
  451. package/out/test/credential-context.test.js +0 -160
  452. package/out/test/custom-env-layering.test.js +0 -151
  453. package/out/test/customEngineInstall.test.js +0 -100
  454. package/out/test/customRegistry.test.js +0 -122
  455. package/out/test/data-plane-engine-wiring.test.js +0 -250
  456. package/out/test/data-plane.test.js +0 -311
  457. package/out/test/deploy-init.test.js +0 -854
  458. package/out/test/deps-auto-install.test.js +0 -81
  459. package/out/test/egress-broker.test.js +0 -172
  460. package/out/test/engine-integration.test.js +0 -297
  461. package/out/test/enginePaths.test.js +0 -195
  462. package/out/test/enginePublish.test.js +0 -82
  463. package/out/test/fleetDisplayUtils.test.js +0 -100
  464. package/out/test/gateway-client.test.js +0 -409
  465. package/out/test/gateway-install-source.test.js +0 -111
  466. package/out/test/gateway-lifecycle.test.js +0 -608
  467. package/out/test/gateway-outbound.test.js +0 -538
  468. package/out/test/gateway-reset-mappings.test.js +0 -221
  469. package/out/test/gateway-route-index.test.js +0 -235
  470. package/out/test/gateway-secrets-preflight.test.js +0 -127
  471. package/out/test/gateway-session-routing.test.js +0 -65
  472. package/out/test/gateway-session-store.test.js +0 -216
  473. package/out/test/github.test.js +0 -232
  474. package/out/test/governor-hardening.test.js +0 -217
  475. package/out/test/headless-sync.test.js +0 -301
  476. package/out/test/helpers/manifest-fixtures.js +0 -27
  477. package/out/test/idp-init.test.js +0 -151
  478. package/out/test/lease-signature.test.js +0 -357
  479. package/out/test/lease-trust-root.test.js +0 -273
  480. package/out/test/lease-validation.test.js +0 -169
  481. package/out/test/lease.test.js +0 -114
  482. package/out/test/mcp-obo.test.js +0 -107
  483. package/out/test/mcp.test.js +0 -389
  484. package/out/test/model-routing.test.js +0 -133
  485. package/out/test/openclaw-channel-routing.test.js +0 -191
  486. package/out/test/pack-admission.test.js +0 -216
  487. package/out/test/pack-digest-parity.test.js +0 -140
  488. package/out/test/pack-reconcile.test.js +0 -183
  489. package/out/test/pack-signature-artifact.test.js +0 -103
  490. package/out/test/pack-signature.test.js +0 -176
  491. package/out/test/pack-trust-root.test.js +0 -202
  492. package/out/test/pathSafety.test.js +0 -63
  493. package/out/test/permissions.test.js +0 -175
  494. package/out/test/persistent-engine-worker.test.js +0 -220
  495. package/out/test/refreshPolicy.test.js +0 -60
  496. package/out/test/registry.test.js +0 -351
  497. package/out/test/remote-trust.test.js +0 -242
  498. package/out/test/runner-protocol.test.js +0 -249
  499. package/out/test/runtime.test.js +0 -349
  500. package/out/test/sandbox-driver.test.js +0 -553
  501. package/out/test/sandbox-hardening.test.js +0 -395
  502. package/out/test/sandbox-runner.test.js +0 -343
  503. package/out/test/scheduledFleetStorage.test.js +0 -72
  504. package/out/test/serve-gateway-turn.test.js +0 -267
  505. package/out/test/serve-hardening.test.js +0 -418
  506. package/out/test/serveClient.test.js +0 -209
  507. package/out/test/sessionJsonlFleet.test.js +0 -78
  508. package/out/test/sessionShip.test.js +0 -560
  509. package/out/test/shared.test.js +0 -173
  510. package/out/test/skills-mcp.test.js +0 -299
  511. package/out/test/spotlightPublish.test.js +0 -327
  512. package/out/test/spotlightYaml.test.js +0 -58
  513. package/out/test/state-sync-peer.test.js +0 -156
  514. package/out/test/store-install-safe.test.js +0 -69
  515. package/out/test/store.test.js +0 -331
  516. package/out/test/stores.test.js +0 -504
  517. package/out/test/sync-switch.test.js +0 -251
  518. package/out/test/teamViewDiscovery.test.js +0 -90
  519. package/out/test/tls-bypass.test.js +0 -90
  520. package/out/test/toolPath.test.js +0 -120
  521. package/out/test/utils.test.js +0 -308
  522. package/out/test/viewsConfigMerge.test.js +0 -180
  523. package/out/test/warrant.test.js +0 -138
  524. package/out/test/windows-command-line.test.js +0 -75
  525. package/out/utils/agentEnvFile.js +0 -131
  526. package/out/utils/binary.js +0 -175
  527. package/out/utils/index.js +0 -37
  528. package/out/utils/network.js +0 -74
  529. package/out/utils/paths.js +0 -116
  530. package/out/utils/pid.js +0 -180
  531. package/out/utils/resolveNodeBinary.js +0 -168
  532. package/out/utils/windowsCommandLine.js +0 -29
  533. package/out/utils.js +0 -968
@@ -1,122 +1,94 @@
1
- #---------------------------------------------------------------------------------------------
2
- # Copyright (c) AgentHippo.ai. All rights reserved.
3
- # Licensed under the MIT License. See LICENSE.txt in the project root for license information.
4
- #---------------------------------------------------------------------------------------------
5
- """
6
- AgentIDE LiteLLM proxy hooks - Clean structured assistant output with separate input/output
7
- Mimics langfuse_otel style: gen_ai.input.messages (history) + gen_ai.output.messages (new reply only)
8
-
9
- Usage in litellm config.yaml:
10
- callbacks: ["agentide_litellm_hooks_v1.clean_separated_otel_output"]
11
-
12
- PRIVACY DEFAULT (PLAN/compliance-req.md §6 "data minimization"):
13
- Prompt and completion CONTENT is NOT captured unless an operator opts in with
14
-
15
- AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE=1
16
-
17
- Without the opt-in every span carries metadata only — model, tokens, latency, status — plus a
18
- sha256 + byte length of the content so a later disclosure can still be *matched* against a
19
- span without the span holding the text. This is enforced here at span-write time, in addition
20
- to `turn_off_message_logging: true` in the config, so a config that re-enables message logging
21
- without the env opt-in still produces redacted spans (defence in depth: the config is edited by
22
- hand far more often than this file).
23
- """
24
-
1
+ # Copyright (c) 2026 AgentHippo.ai. All rights reserved.
2
+ # Proprietary and confidential. See LICENSE.
25
3
  from __future__ import annotations
26
4
  from typing import Any
5
+ import ast
27
6
  import hashlib
28
7
  import json
29
8
  import os
30
9
  import re
31
10
  import sys
32
-
33
11
  from litellm.integrations.custom_logger import CustomLogger
34
12
  from litellm.integrations.opentelemetry import OpenTelemetry
35
13
  from opentelemetry.trace import Status, StatusCode
36
-
37
- _OTEL_TEXT_MAX_LEN = 4000 # per text block
38
- _OTEL_PREVIEW_LEN = 180 # short preview in event
39
-
40
- # -----------------------------------------------------------------------------
41
- # Content capture opt-in (fix: full capture used to be the default)
42
- # -----------------------------------------------------------------------------
43
-
44
- CONTENT_CAPTURE_ENV = "AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE"
45
-
46
- _TRUTHY = frozenset({"1", "true", "yes", "on"})
47
-
48
- # Span attributes that can carry prompt/completion text. Mirrors CONTENT_ATTRS in
49
- # @agenthippo-ai/evidence-ship so the two redaction points cannot drift silently.
50
- _CONTENT_ATTR_RE = re.compile(
51
- r"^(gen_ai\.(input|output)\.messages"
52
- r"|gen_ai\.(prompt|completion)"
53
- r"|gen_ai\.system_instructions"
54
- r"|llm\.(prompts|responses)"
55
- r"|llm\.request\.functions\."
56
- r"|error\.(message|stack_trace)"
57
- r"|hidden_params"
58
- r"|metadata\.requester_custom_headers)"
59
- )
60
-
14
+ _OTEL_TEXT_MAX_LEN = 4000
15
+ _OTEL_PREVIEW_LEN = 180
16
+ CONTENT_CAPTURE_ENV = 'AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE'
17
+ _TRUTHY = frozenset({'1', 'true', 'yes', 'on'})
18
+ _CONTENT_ATTR_RE = re.compile('^(gen_ai\\.(input|output)\\.messages|gen_ai\\.(prompt|completion)|gen_ai\\.system_instructions|llm\\.(prompts|responses)|llm\\.request\\.functions\\.|error\\.(message|stack_trace)|hidden_params|metadata\\.requester_custom_headers)')
61
19
  _capture_banner_printed = False
62
20
 
63
-
64
21
  def _content_capture_enabled() -> bool:
65
- """True only when an operator explicitly opted in to prompt/completion capture."""
66
- return (os.environ.get(CONTENT_CAPTURE_ENV, "") or "").strip().lower() in _TRUTHY
67
-
22
+ return (os.environ.get(CONTENT_CAPTURE_ENV, '') or '').strip().lower() in _TRUTHY
68
23
 
69
24
  def _announce_content_capture() -> None:
70
- """Log loudly, once, whichever mode is in force — silence is how this got shipped on."""
71
25
  global _capture_banner_printed
72
26
  if _capture_banner_printed:
73
27
  return
74
28
  _capture_banner_printed = True
75
29
  if _content_capture_enabled():
76
- print(
77
- "\n"
78
- "!! AGENTHIPPO EVIDENCE: FULL PROMPT/COMPLETION CAPTURE IS ON !!\n"
79
- f"!! {CONTENT_CAPTURE_ENV} is set. Every span will carry prompt and completion text.\n"
80
- "!! Unset it to return to metadata-only (sha256 + length) spans.\n",
81
- file=sys.stderr,
82
- flush=True,
83
- )
30
+ print(f'\n!! AGENTHIPPO EVIDENCE: FULL PROMPT/COMPLETION CAPTURE IS ON !!\n!! {CONTENT_CAPTURE_ENV} is set. Every span will carry prompt and completion text.\n!! Unset it to return to metadata-only (sha256 + length) spans.\n', file=sys.stderr, flush=True)
84
31
  else:
85
- print(
86
- f"[agentide-otel] content capture OFF (metadata + sha256 only). "
87
- f"Set {CONTENT_CAPTURE_ENV}=1 to record prompts/completions.",
88
- file=sys.stderr,
89
- flush=True,
90
- )
91
-
32
+ print(f'[agentide-otel] content capture OFF (metadata + sha256 only). Set {CONTENT_CAPTURE_ENV}=1 to record prompts/completions.', file=sys.stderr, flush=True)
92
33
 
93
34
  def _sha256(text: str) -> str:
94
- return hashlib.sha256(text.encode("utf-8", "replace")).hexdigest()
35
+ return hashlib.sha256(text.encode('utf-8', 'replace')).hexdigest()
36
+ _METADATA_HEADERS_KEY = 'metadata.requester_custom_headers'
37
+ _METADATA_HEADER_ALLOWLIST = frozenset({'x-litellm-metadata-session-id', 'x-litellm-metadata-request-id', 'x-litellm-metadata-user-id', 'x-litellm-metadata-project-id', 'x-litellm-metadata-agent-id', 'x-litellm-metadata-agent-version', 'x-litellm-metadata-source', 'x-litellm-metadata-engine'})
38
+
39
+ def _parse_attr_dict(value: Any) -> Any:
40
+ if not isinstance(value, str):
41
+ return value
42
+ try:
43
+ return ast.literal_eval(value)
44
+ except (ValueError, SyntaxError):
45
+ try:
46
+ return json.loads(value)
47
+ except ValueError:
48
+ return None
95
49
 
50
+ def _allowlisted_headers(headers: dict) -> dict:
51
+ return {k.lower(): v for k, v in headers.items() if isinstance(k, str) and k.lower() in _METADATA_HEADER_ALLOWLIST}
52
+
53
+ def _filtered_metadata_headers(value: Any) -> str | None:
54
+ headers = _parse_attr_dict(value)
55
+ if not isinstance(headers, dict):
56
+ return None
57
+ kept = _allowlisted_headers(headers)
58
+ return str(kept) if kept else None
59
+ _REQUESTER_METADATA_KEY = 'metadata.requester_metadata'
60
+
61
+ def _scrub_requester_metadata(value: Any) -> Any:
62
+ md = _parse_attr_dict(value)
63
+ if isinstance(md, dict) and isinstance(md.get('headers'), dict):
64
+ return str({**md, 'headers': _allowlisted_headers(md['headers'])})
65
+ if isinstance(md, dict):
66
+ return value
67
+ return None if 'headers' in str(value) else value
96
68
 
97
69
  class _RedactingSpan:
98
- """
99
- Span proxy that drops content-bearing attributes and replaces them with a sha256 + length.
100
-
101
- LiteLLM's own `set_attributes()` writes `gen_ai.input.messages` (the full history) and
102
- friends. We cannot remove an attribute after the SDK has recorded it, so the only place to
103
- stop it is between LiteLLM and the SDK — here. Everything that is not content passes through
104
- untouched, so the span keeps model, tokens, latency, status, and the requester metadata the
105
- evidence index reads.
106
- """
107
70
 
108
71
  def __init__(self, span: Any) -> None:
109
72
  self._span = span
110
73
 
111
- def __getattr__(self, name: str) -> Any: # everything we do not override
74
+ def __getattr__(self, name: str) -> Any:
112
75
  return getattr(self._span, name)
113
76
 
114
77
  def set_attribute(self, key: Any, value: Any) -> Any:
78
+ if key == _REQUESTER_METADATA_KEY:
79
+ scrubbed = _scrub_requester_metadata(value)
80
+ if scrubbed is None:
81
+ return None
82
+ return self._span.set_attribute(key, scrubbed)
115
83
  if isinstance(key, str) and _CONTENT_ATTR_RE.match(key):
116
84
  text = value if isinstance(value, str) else json.dumps(value, ensure_ascii=False, default=str)
117
- self._span.set_attribute(f"{key}.sha256", _sha256(text))
118
- self._span.set_attribute(f"{key}.length", len(text))
119
- self._span.set_attribute(f"{key}.redacted", True)
85
+ self._span.set_attribute(f'{key}.sha256', _sha256(text))
86
+ self._span.set_attribute(f'{key}.length', len(text))
87
+ self._span.set_attribute(f'{key}.redacted', True)
88
+ if key == _METADATA_HEADERS_KEY:
89
+ kept = _filtered_metadata_headers(value)
90
+ if kept is not None:
91
+ self._span.set_attribute(key, kept)
120
92
  return None
121
93
  return self._span.set_attribute(key, value)
122
94
 
@@ -127,31 +99,37 @@ class _RedactingSpan:
127
99
  return None
128
100
  return self._span.set_attributes(attributes)
129
101
 
130
- def add_event(self, name: Any, attributes: Any = None, **kwargs: Any) -> Any:
102
+ def add_event(self, name: Any, attributes: Any=None, **kwargs: Any) -> Any:
131
103
  if isinstance(attributes, dict):
132
104
  filtered = {}
133
105
  for key, value in attributes.items():
134
- if isinstance(key, str) and _CONTENT_ATTR_RE.match(key):
106
+ if key == _REQUESTER_METADATA_KEY:
107
+ scrubbed = _scrub_requester_metadata(value)
108
+ if scrubbed is not None:
109
+ filtered[key] = scrubbed
110
+ elif isinstance(key, str) and _CONTENT_ATTR_RE.match(key):
135
111
  text = value if isinstance(value, str) else json.dumps(value, ensure_ascii=False, default=str)
136
- filtered[f"{key}.sha256"] = _sha256(text)
137
- filtered[f"{key}.length"] = len(text)
112
+ filtered[f'{key}.sha256'] = _sha256(text)
113
+ filtered[f'{key}.length'] = len(text)
114
+ if key == _METADATA_HEADERS_KEY:
115
+ kept = _filtered_metadata_headers(value)
116
+ if kept is not None:
117
+ filtered[key] = kept
138
118
  else:
139
119
  filtered[key] = value
140
120
  attributes = filtered
141
121
  return self._span.add_event(name, attributes, **kwargs)
142
122
 
143
-
144
- def _read_field(obj: Any, key: str, default: Any = None) -> Any:
123
+ def _read_field(obj: Any, key: str, default: Any=None) -> Any:
145
124
  if isinstance(obj, dict):
146
125
  return obj.get(key, default)
147
126
  return getattr(obj, key, default)
148
127
 
149
-
150
128
  def _content_blocks_to_text(content: Any) -> str:
151
129
  if isinstance(content, str):
152
130
  return content
153
131
  if not isinstance(content, (list, tuple)):
154
- return ""
132
+ return ''
155
133
  parts: list[str] = []
156
134
  for block in content:
157
135
  if isinstance(block, str):
@@ -160,90 +138,65 @@ def _content_blocks_to_text(content: Any) -> str:
160
138
  continue
161
139
  if not isinstance(block, dict):
162
140
  continue
163
- text = block.get("text") or block.get("output_text") or block.get("content")
141
+ text = block.get('text') or block.get('output_text') or block.get('content')
164
142
  if isinstance(text, str) and text.strip():
165
143
  parts.append(text)
166
- return "\n".join(parts)
167
-
144
+ return '\n'.join(parts)
168
145
 
169
146
  def _extract_assistant_text(response_obj: Any) -> str:
170
147
  if response_obj is None:
171
- return ""
172
- # OpenAI-style
173
- choices = _read_field(response_obj, "choices")
148
+ return ''
149
+ choices = _read_field(response_obj, 'choices')
174
150
  if isinstance(choices, list) and choices:
175
- msg = _read_field(choices[0], "message")
176
- content = _read_field(msg, "content")
151
+ msg = _read_field(choices[0], 'message')
152
+ content = _read_field(msg, 'content')
177
153
  text = _content_blocks_to_text(content)
178
154
  if text.strip():
179
155
  return text
180
- # Direct output fields
181
- for key in ("output_text", "content", "output", "text"):
156
+ for key in ('output_text', 'content', 'output', 'text'):
182
157
  val = _read_field(response_obj, key)
183
158
  if val:
184
159
  text = _content_blocks_to_text(val)
185
160
  if text.strip():
186
161
  return text
187
- return ""
188
-
162
+ return ''
189
163
 
190
164
  def _extract_text_from_success_kwargs(kwargs: dict) -> str:
191
- text = _extract_assistant_text(kwargs.get("response_obj"))
165
+ text = _extract_assistant_text(kwargs.get('response_obj'))
192
166
  if text:
193
167
  return text
194
- for k in ("complete_response", "model_response", "response", "original_response"):
168
+ for k in ('complete_response', 'model_response', 'response', 'original_response'):
195
169
  candidate = kwargs.get(k)
196
170
  if candidate:
197
171
  text = _extract_assistant_text(candidate)
198
172
  if text:
199
173
  return text
200
- return ""
201
-
174
+ return ''
202
175
 
203
176
  def _make_output_messages_structured(text: str) -> list:
204
177
  if not text.strip():
205
178
  return []
206
179
  trimmed = text[:_OTEL_TEXT_MAX_LEN]
207
- # Align with LiteLLM's GEN_AI_OUTPUT_MESSAGES shape: parts + inner content (not content + text).
208
- return [{"role": "assistant", "parts": [{"type": "text", "content": trimmed}]}]
209
-
180
+ return [{'role': 'assistant', 'parts': [{'type': 'text', 'content': trimmed}]}]
210
181
 
211
182
  def _model_name(model: Any) -> str:
212
- return model.lower() if isinstance(model, str) else ""
213
-
183
+ return model.lower() if isinstance(model, str) else ''
214
184
 
215
185
  def _is_reasoning_unsupported_model(model: Any) -> bool:
216
- """True when model is known to reject reasoning parameters."""
217
- return "gpt-4.1-nano" in _model_name(model)
218
-
186
+ return 'gpt-4.1-nano' in _model_name(model)
219
187
 
220
188
  def _is_anthropic_route(model: Any) -> bool:
221
- """True when the request will be forwarded to Anthropic."""
222
189
  name = _model_name(model)
223
- return "claude" in name or "anthropic" in name
224
-
225
-
226
- # Every model known to reject the Codex built-in web-search tools. Additive by policy: a
227
- # deployment that still routes to an older model keeps its sanitizer when a newer one is added,
228
- # because a dropped entry fails as upstream 400s on live traffic, not as a test failure.
229
- _WEB_SEARCH_UNSUPPORTED_MODELS = ("gpt-5.6-luna", "gpt-4.1-nano")
230
-
190
+ return 'claude' in name or 'anthropic' in name
191
+ _WEB_SEARCH_UNSUPPORTED_MODELS = ('gpt-5.6-luna', 'gpt-4.1-nano')
231
192
 
232
193
  def _is_web_search_unsupported_model(model: Any) -> bool:
233
- """True when the upstream model rejects Codex web-search tools."""
234
194
  name = _model_name(model)
235
- return any(m in name for m in _WEB_SEARCH_UNSUPPORTED_MODELS)
236
-
195
+ return any((m in name for m in _WEB_SEARCH_UNSUPPORTED_MODELS))
196
+ _OPENAI_RESPONSES_ONLY_KEYS = ('client_metadata',)
197
+ _UNSUPPORTED_TOOL_IDENTIFIERS = frozenset({'web_search_preview', 'web_search'})
237
198
 
238
- # OpenAI Responses API fields that Anthropic (and some gateways) reject if forwarded verbatim.
239
- _OPENAI_RESPONSES_ONLY_KEYS = ("client_metadata",)
240
-
241
- # Codex built-in tools not supported on all OpenAI models.
242
- _UNSUPPORTED_TOOL_IDENTIFIERS = frozenset({"web_search_preview", "web_search"})
243
-
244
-
245
- def _strip_key_recursive(data: Any, key: str, _visited: set[int] | None = None) -> None:
246
- """Remove a key in nested dict/list payloads (bounded; avoids cyclic graphs)."""
199
+ def _strip_key_recursive(data: Any, key: str, _visited: set[int] | None=None) -> None:
247
200
  if _visited is None:
248
201
  _visited = set()
249
202
  if isinstance(data, dict):
@@ -263,58 +216,49 @@ def _strip_key_recursive(data: Any, key: str, _visited: set[int] | None = None)
263
216
  for item in data:
264
217
  _strip_key_recursive(item, key, _visited)
265
218
 
266
-
267
219
  def _resolve_route_model(payload: dict, model: Any) -> str:
268
220
  name = _model_name(model)
269
221
  if name:
270
222
  return name
271
- for candidate in (
272
- payload.get("model"),
273
- payload.get("model_group"),
274
- payload.get("model_name"),
275
- payload.get("deployment"),
276
- ):
223
+ for candidate in (payload.get('model'), payload.get('model_group'), payload.get('model_name'), payload.get('deployment')):
277
224
  name = _model_name(candidate)
278
225
  if name:
279
226
  return name
280
- metadata = payload.get("metadata")
227
+ metadata = payload.get('metadata')
281
228
  if isinstance(metadata, dict):
282
- for candidate in (metadata.get("model_group"), metadata.get("model")):
229
+ for candidate in (metadata.get('model_group'), metadata.get('model')):
283
230
  name = _model_name(candidate)
284
231
  if name:
285
232
  return name
286
- return ""
287
-
233
+ return ''
288
234
 
289
235
  def _tool_is_unsupported(tool: Any) -> bool:
290
236
  if not isinstance(tool, dict):
291
237
  return False
292
- tool_type = tool.get("type")
238
+ tool_type = tool.get('type')
293
239
  if isinstance(tool_type, str) and tool_type in _UNSUPPORTED_TOOL_IDENTIFIERS:
294
240
  return True
295
- name = tool.get("name")
241
+ name = tool.get('name')
296
242
  if isinstance(name, str) and name in _UNSUPPORTED_TOOL_IDENTIFIERS:
297
243
  return True
298
- fn = tool.get("function")
244
+ fn = tool.get('function')
299
245
  if isinstance(fn, dict):
300
- fn_name = fn.get("name")
246
+ fn_name = fn.get('name')
301
247
  if isinstance(fn_name, str) and fn_name in _UNSUPPORTED_TOOL_IDENTIFIERS:
302
248
  return True
303
249
  return False
304
250
 
305
-
306
251
  def _strip_unsupported_tools(data: dict) -> None:
307
- tools = data.get("tools")
252
+ tools = data.get('tools')
308
253
  if not isinstance(tools, list):
309
254
  return
310
255
  filtered = [tool for tool in tools if not _tool_is_unsupported(tool)]
311
256
  if len(filtered) == len(tools):
312
257
  return
313
258
  if filtered:
314
- data["tools"] = filtered
259
+ data['tools'] = filtered
315
260
  else:
316
- data.pop("tools", None)
317
-
261
+ data.pop('tools', None)
318
262
 
319
263
  def _apply_codex_proxy_sanitizers(data: dict, model: Any) -> None:
320
264
  route_model = _resolve_route_model(data, model)
@@ -324,14 +268,7 @@ def _apply_codex_proxy_sanitizers(data: dict, model: Any) -> None:
324
268
  if _is_web_search_unsupported_model(route_model):
325
269
  _strip_unsupported_tools(data)
326
270
 
327
-
328
- def _strip_unsupported_reasoning_params(data: Any, _visited: set[int] | None = None) -> None:
329
- """
330
- Mutates `data` in-place by removing unsupported reasoning controls.
331
-
332
- OpenAI's API error reports `reasoning.effort`, but the payload is sent as:
333
- { "reasoning": { "effort": ... } }
334
- """
271
+ def _strip_unsupported_reasoning_params(data: Any, _visited: set[int] | None=None) -> None:
335
272
  if _visited is None:
336
273
  _visited = set()
337
274
  if isinstance(data, dict):
@@ -339,8 +276,8 @@ def _strip_unsupported_reasoning_params(data: Any, _visited: set[int] | None = N
339
276
  if obj_id in _visited:
340
277
  return
341
278
  _visited.add(obj_id)
342
- data.pop("reasoning", None)
343
- data.pop("reasoning_effort", None)
279
+ data.pop('reasoning', None)
280
+ data.pop('reasoning_effort', None)
344
281
  for v in list(data.values()):
345
282
  _strip_unsupported_reasoning_params(v, _visited)
346
283
  return
@@ -352,412 +289,227 @@ def _strip_unsupported_reasoning_params(data: Any, _visited: set[int] | None = N
352
289
  for item in data:
353
290
  _strip_unsupported_reasoning_params(item, _visited)
354
291
 
355
-
356
292
  def _extract_text_content(message: dict[str, Any]) -> str:
357
- """Extract text content from OpenAI-style message content."""
358
- content = message.get("content")
293
+ content = message.get('content')
359
294
  if isinstance(content, str):
360
295
  return content
361
296
  if not isinstance(content, list):
362
- return ""
297
+ return ''
363
298
  parts: list[str] = []
364
299
  for block in content:
365
- if isinstance(block, dict) and block.get("type") == "text":
366
- text = block.get("text")
300
+ if isinstance(block, dict) and block.get('type') == 'text':
301
+ text = block.get('text')
367
302
  if isinstance(text, str) and text.strip():
368
303
  parts.append(text)
369
- return "\n".join(parts)
370
-
304
+ return '\n'.join(parts)
371
305
 
372
306
  def _set_text_content(message: dict[str, Any], text: str) -> None:
373
- """Set text content while preserving existing content shape when possible."""
374
- content = message.get("content")
307
+ content = message.get('content')
375
308
  if isinstance(content, list):
376
- # Keep non-text blocks and append merged text as final text block.
377
- preserved = [b for b in content if not (isinstance(b, dict) and b.get("type") == "text")]
309
+ preserved = [b for b in content if not (isinstance(b, dict) and b.get('type') == 'text')]
378
310
  if text.strip():
379
- preserved.append({"type": "text", "text": text})
380
- message["content"] = preserved
311
+ preserved.append({'type': 'text', 'text': text})
312
+ message['content'] = preserved
381
313
  return
382
- message["content"] = text
383
-
314
+ message['content'] = text
384
315
 
385
316
  def _is_text_only_assistant(message: Any) -> bool:
386
- """True when assistant message has text and no tool calls."""
387
- if not isinstance(message, dict) or message.get("role") != "assistant":
317
+ if not isinstance(message, dict) or message.get('role') != 'assistant':
388
318
  return False
389
- tool_calls = message.get("tool_calls")
319
+ tool_calls = message.get('tool_calls')
390
320
  if isinstance(tool_calls, list) and len(tool_calls) > 0:
391
321
  return False
392
322
  return bool(_extract_text_content(message).strip())
393
323
 
394
-
395
324
  def _tool_result_ids_from_message(message: Any) -> set[str]:
396
- """Collect tool result ids from OpenAI or Anthropic-style tool result messages."""
397
325
  if not isinstance(message, dict):
398
326
  return set()
399
-
400
- role = message.get("role")
401
- if role == "tool":
402
- tool_call_id = message.get("tool_call_id")
327
+ role = message.get('role')
328
+ if role == 'tool':
329
+ tool_call_id = message.get('tool_call_id')
403
330
  return {tool_call_id} if isinstance(tool_call_id, str) and tool_call_id else set()
404
-
405
- content = message.get("content")
331
+ content = message.get('content')
406
332
  if not isinstance(content, list):
407
333
  return set()
408
334
  ids: set[str] = set()
409
335
  for block in content:
410
- if isinstance(block, dict) and block.get("type") == "tool_result":
411
- tool_use_id = block.get("tool_use_id")
336
+ if isinstance(block, dict) and block.get('type') == 'tool_result':
337
+ tool_use_id = block.get('tool_use_id')
412
338
  if isinstance(tool_use_id, str) and tool_use_id:
413
339
  ids.add(tool_use_id)
414
340
  return ids
415
341
 
416
-
417
342
  def sanitize_messages(messages: Any) -> Any:
418
- """
419
- Lightweight tool-order sanitizer for Codex/OpenAI-style tool calls.
420
-
421
- Ensures assistant tool calls are followed immediately by tool results.
422
- If text-only assistant messages appear between tool call and tool result,
423
- merge text into the tool-call assistant message.
424
- If no immediate tool result exists, inject placeholder tool results.
425
- """
426
343
  if not isinstance(messages, list) or not messages:
427
344
  return messages
428
-
429
345
  result: list[Any] = []
430
346
  i = 0
431
347
  changed = False
432
-
433
348
  while i < len(messages):
434
349
  msg = messages[i]
435
350
  if not isinstance(msg, dict):
436
351
  result.append(msg)
437
352
  i += 1
438
353
  continue
439
-
440
- role = msg.get("role")
441
- tool_calls = msg.get("tool_calls")
442
- if role != "assistant" or not isinstance(tool_calls, list) or len(tool_calls) == 0:
354
+ role = msg.get('role')
355
+ tool_calls = msg.get('tool_calls')
356
+ if role != 'assistant' or not isinstance(tool_calls, list) or len(tool_calls) == 0:
443
357
  result.append(msg)
444
358
  i += 1
445
359
  continue
446
-
447
- # Assistant tool-call message.
448
360
  tool_ids: list[str] = []
449
361
  for tc in tool_calls:
450
362
  if isinstance(tc, dict):
451
- tid = tc.get("id")
363
+ tid = tc.get('id')
452
364
  if isinstance(tid, str) and tid:
453
365
  tool_ids.append(tid)
454
-
455
366
  merged = dict(msg)
456
367
  merged_text = _extract_text_content(merged)
457
368
  j = i + 1
458
-
459
- # Merge consecutive text-only assistant messages.
460
369
  while j < len(messages) and _is_text_only_assistant(messages[j]):
461
370
  extra = _extract_text_content(messages[j])
462
371
  if extra.strip():
463
- merged_text = f"{merged_text}\n{extra}" if merged_text.strip() else extra
372
+ merged_text = f'{merged_text}\n{extra}' if merged_text.strip() else extra
464
373
  j += 1
465
374
  changed = True
466
-
467
375
  if merged_text != _extract_text_content(merged):
468
376
  _set_text_content(merged, merged_text)
469
-
470
377
  result.append(merged)
471
-
472
- # Immediate next message must carry tool results for all tool ids.
473
378
  next_ids = _tool_result_ids_from_message(messages[j]) if j < len(messages) else set()
474
379
  missing_ids = [tid for tid in tool_ids if tid not in next_ids]
475
380
  if missing_ids:
476
381
  changed = True
477
382
  for tid in missing_ids:
478
- result.append({
479
- "role": "tool",
480
- "tool_call_id": tid,
481
- "content": "[Placeholder: tool result was not provided]",
482
- })
483
-
383
+ result.append({'role': 'tool', 'tool_call_id': tid, 'content': '[Placeholder: tool result was not provided]'})
484
384
  i = j
485
-
486
385
  return result if changed else messages
487
386
 
488
-
489
387
  class ReasoningParamsSanitizer(CustomLogger):
490
- """LiteLLM proxy hook: strip reasoning controls for gpt-5.6-luna."""
491
-
492
- async def async_pre_call_hook(
493
- self,
494
- user_api_key_dict: Any,
495
- cache: Any,
496
- data: dict,
497
- call_type: Any,
498
- ) -> dict:
388
+
389
+ async def async_pre_call_hook(self, user_api_key_dict: Any, cache: Any, data: dict, call_type: Any) -> dict:
499
390
  if not isinstance(data, dict):
500
391
  return data
501
- model = data.get("model")
392
+ model = data.get('model')
502
393
  if not _is_reasoning_unsupported_model(model):
503
394
  return data
504
395
  _strip_unsupported_reasoning_params(data)
505
396
  return data
506
397
 
507
- async def async_pre_call_deployment_hook(
508
- self,
509
- request_kwargs: dict,
510
- call_type: Any,
511
- ) -> dict:
512
- """Extra safety: strip during deployment-layer payload building."""
398
+ async def async_pre_call_deployment_hook(self, request_kwargs: dict, call_type: Any) -> dict:
513
399
  if not isinstance(request_kwargs, dict):
514
400
  return request_kwargs
515
- model = request_kwargs.get("model")
401
+ model = request_kwargs.get('model')
516
402
  if not _is_reasoning_unsupported_model(model):
517
403
  return request_kwargs
518
404
  _strip_unsupported_reasoning_params(request_kwargs)
519
405
  return request_kwargs
520
406
 
521
- async def async_pre_request_hook(
522
- self,
523
- model: str,
524
- messages: list[Any],
525
- request_kwargs: dict,
526
- ) -> dict:
527
- """Extra safety: strip right before provider call."""
407
+ async def async_pre_request_hook(self, model: str, messages: list[Any], request_kwargs: dict) -> dict:
528
408
  if not _is_reasoning_unsupported_model(model):
529
409
  return request_kwargs
530
410
  if isinstance(request_kwargs, dict):
531
411
  _strip_unsupported_reasoning_params(request_kwargs)
532
412
  return request_kwargs
533
413
 
534
-
535
414
  class CodexProxySanitizer(CustomLogger):
536
- """
537
- LiteLLM proxy hook: strip Codex/OpenAI Responses fields/tools that break proxy routes.
538
-
539
- - Anthropic: drop client_metadata (OpenAI-only Responses telemetry).
540
- - gpt-5.6-luna: drop web_search_preview and related built-in search tools.
541
- """
542
415
 
543
416
  def _sanitize(self, payload: dict, model: Any) -> dict:
544
417
  if isinstance(payload, dict):
545
418
  _apply_codex_proxy_sanitizers(payload, model)
546
419
  return payload
547
420
 
548
- async def async_pre_call_hook(
549
- self,
550
- user_api_key_dict: Any,
551
- cache: Any,
552
- data: dict,
553
- call_type: Any,
554
- ) -> dict:
421
+ async def async_pre_call_hook(self, user_api_key_dict: Any, cache: Any, data: dict, call_type: Any) -> dict:
555
422
  if not isinstance(data, dict):
556
423
  return data
557
- return self._sanitize(data, data.get("model"))
424
+ return self._sanitize(data, data.get('model'))
558
425
 
559
- async def async_pre_call_deployment_hook(
560
- self,
561
- request_kwargs: dict,
562
- call_type: Any,
563
- ) -> dict:
426
+ async def async_pre_call_deployment_hook(self, request_kwargs: dict, call_type: Any) -> dict:
564
427
  if not isinstance(request_kwargs, dict):
565
428
  return request_kwargs
566
- return self._sanitize(request_kwargs, request_kwargs.get("model"))
567
-
568
- async def async_pre_request_hook(
569
- self,
570
- model: str,
571
- messages: list[Any],
572
- request_kwargs: dict,
573
- ) -> dict:
429
+ return self._sanitize(request_kwargs, request_kwargs.get('model'))
430
+
431
+ async def async_pre_request_hook(self, model: str, messages: list[Any], request_kwargs: dict) -> dict:
574
432
  if isinstance(request_kwargs, dict):
575
433
  self._sanitize(request_kwargs, model)
576
434
  return request_kwargs
577
435
 
578
-
579
436
  class ToolOrderSanitizer(CustomLogger):
580
- """LiteLLM proxy hook: sanitize tool call/result ordering for Claude routes."""
581
-
582
- async def async_pre_call_hook(
583
- self,
584
- user_api_key_dict: Any,
585
- cache: Any,
586
- data: dict,
587
- call_type: Any,
588
- ) -> dict:
437
+
438
+ async def async_pre_call_hook(self, user_api_key_dict: Any, cache: Any, data: dict, call_type: Any) -> dict:
589
439
  if not isinstance(data, dict):
590
440
  return data
591
- messages = data.get("messages")
441
+ messages = data.get('messages')
592
442
  if isinstance(messages, list) and messages:
593
- data["messages"] = sanitize_messages(messages)
443
+ data['messages'] = sanitize_messages(messages)
594
444
  return data
595
445
 
596
-
597
446
  def strip_empty_thinking_blocks(messages: list[Any]) -> list[Any]:
598
- """
599
- Strip empty thinking/reasoning blocks from OpenAI-format messages.
600
-
601
- Anthropic's API rejects empty `thinking` content blocks and empty
602
- `reasoning_content` strings. LiteLLM proxies these fields unchanged,
603
- so we sanitize outbound messages here at the proxy layer.
604
-
605
- Handles three shapes:
606
- 1. Top-level `reasoning_content: ""` on assistant messages
607
- 2. `{ type: "thinking", thinking: "" }` inside content arrays
608
- 3. Removes assistant messages that become entirely empty after stripping
609
- """
610
447
  result: list[Any] = []
611
-
612
448
  for msg in messages:
613
449
  if not isinstance(msg, dict):
614
450
  result.append(msg)
615
451
  continue
616
-
617
452
  cleaned = msg
618
-
619
- # 1. Strip empty top-level reasoning_content on assistant messages
620
- if cleaned.get("role") == "assistant" and "reasoning_content" in cleaned:
621
- rc = cleaned["reasoning_content"]
622
- if not rc or (isinstance(rc, str) and not rc.strip()):
623
- cleaned = {k: v for k, v in cleaned.items() if k != "reasoning_content"}
624
-
625
- # 2. Filter empty thinking blocks from content arrays
626
- content = cleaned.get("content")
453
+ if cleaned.get('role') == 'assistant' and 'reasoning_content' in cleaned:
454
+ rc = cleaned['reasoning_content']
455
+ if not rc or (isinstance(rc, str) and (not rc.strip())):
456
+ cleaned = {k: v for k, v in cleaned.items() if k != 'reasoning_content'}
457
+ content = cleaned.get('content')
627
458
  if isinstance(content, list):
628
459
  original_len = len(content)
629
- filtered = [
630
- block for block in content
631
- if not (
632
- isinstance(block, dict)
633
- and block.get("type") == "thinking"
634
- and (not block.get("thinking") or (isinstance(block.get("thinking"), str) and not block["thinking"].strip()))
635
- )
636
- ]
460
+ filtered = [block for block in content if not (isinstance(block, dict) and block.get('type') == 'thinking' and (not block.get('thinking') or (isinstance(block.get('thinking'), str) and (not block['thinking'].strip()))))]
637
461
  if len(filtered) != original_len:
638
- # Preserve turn structure: if assistant becomes empty, keep a placeholder
639
- if not filtered and cleaned.get("role") == "assistant":
640
- filtered = [{"type": "text", "text": ""}]
641
- cleaned = {**cleaned, "content": filtered}
642
-
643
- # 3. Skip entirely empty assistant messages (no content, no tool_calls)
644
- if cleaned.get("role") == "assistant" and not cleaned.get("tool_calls"):
645
- c = cleaned.get("content")
646
- if (isinstance(c, str) and not c.strip()) or (isinstance(c, list) and len(c) == 0):
462
+ if not filtered and cleaned.get('role') == 'assistant':
463
+ filtered = [{'type': 'text', 'text': ''}]
464
+ cleaned = {**cleaned, 'content': filtered}
465
+ if cleaned.get('role') == 'assistant' and (not cleaned.get('tool_calls')):
466
+ c = cleaned.get('content')
467
+ if isinstance(c, str) and (not c.strip()) or (isinstance(c, list) and len(c) == 0):
647
468
  continue
648
-
649
469
  result.append(cleaned)
650
-
651
470
  return result
652
471
 
653
-
654
472
  class EmptyThinkingBlockSanitizer(CustomLogger):
655
- """LiteLLM proxy hook: strip empty thinking/reasoning blocks before Anthropic dispatch."""
656
-
657
- async def async_pre_call_hook(
658
- self,
659
- user_api_key_dict: Any,
660
- cache: Any,
661
- data: dict,
662
- call_type: Any,
663
- ) -> dict:
473
+
474
+ async def async_pre_call_hook(self, user_api_key_dict: Any, cache: Any, data: dict, call_type: Any) -> dict:
664
475
  if not isinstance(data, dict):
665
476
  return data
666
- messages = data.get("messages")
477
+ messages = data.get('messages')
667
478
  if isinstance(messages, list) and messages:
668
- data["messages"] = strip_empty_thinking_blocks(messages)
479
+ data['messages'] = strip_empty_thinking_blocks(messages)
669
480
  return data
670
481
 
671
-
672
482
  class CleanSeparatedOtelOutput(OpenTelemetry):
673
- """
674
- LiteLLM OTEL with clean separation:
675
- - gen_ai.input.messages → full history (as set by LiteLLM)
676
- - gen_ai.output.messages → only the new assistant reply (stringified structured array)
677
- - Short preview event for quick visibility
678
-
679
- Content (both directions) is recorded ONLY when AGENTHIPPO_EVIDENCE_CONTENT_CAPTURE is set.
680
- Otherwise every content attribute becomes `<key>.sha256` + `<key>.length` — enough to prove
681
- which bytes a turn saw, without shipping the bytes.
682
- """
683
483
 
684
484
  def set_attributes(self, span: Any, *args: Any, **kwargs: Any) -> Any:
685
- """
686
- Redaction chokepoint. Overriding here rather than at the call site covers every path in
687
- the base class that sets attributes, including ones this subclass does not override.
688
- """
689
485
  _announce_content_capture()
690
486
  if not _content_capture_enabled():
691
487
  span = _RedactingSpan(span)
692
488
  return super().set_attributes(span, *args, **kwargs)
693
489
 
694
490
  def _get_span_name(self, kwargs: dict) -> str:
695
- # Evidence pipeline indexes spans named litellm_request; main-stable semconv uses "chat …".
696
- #
697
- # The constant is a PRIVATE litellm symbol and this runs on the span-creation path, so an
698
- # upstream rename would raise for every span — total evidence loss at layer 1, not a
699
- # degraded span name. The literal is the same value; falling back to it keeps the pipeline
700
- # indexing while the import is fixed.
701
491
  try:
702
492
  from litellm.integrations.opentelemetry import LITELLM_REQUEST_SPAN_NAME
703
-
704
493
  return LITELLM_REQUEST_SPAN_NAME
705
- except Exception: # pragma: no cover - depends on the pinned litellm build
706
- return "litellm_request"
707
-
708
- def _start_primary_span(
709
- self,
710
- kwargs: dict,
711
- response_obj: Any,
712
- start_time: Any,
713
- end_time: Any,
714
- context: Any,
715
- ):
716
- otel_tracer = self.get_tracer_to_use_for_request(kwargs)
494
+ except Exception:
495
+ return 'litellm_request'
717
496
 
718
- span = otel_tracer.start_span(
719
- name=self._get_span_name(kwargs),
720
- start_time=self._to_ns(start_time),
721
- context=context,
722
- )
497
+ def _start_primary_span(self, kwargs: dict, response_obj: Any, start_time: Any, end_time: Any, context: Any):
498
+ otel_tracer = self.get_tracer_to_use_for_request(kwargs)
499
+ span = otel_tracer.start_span(name=self._get_span_name(kwargs), start_time=self._to_ns(start_time), context=context)
723
500
  span.set_status(Status(StatusCode.OK))
724
501
  self.set_attributes(span, kwargs, response_obj)
725
-
726
- # -- Extract only the new assistant output -------------------------------
727
- text = _extract_text_from_success_kwargs(kwargs) or \
728
- _extract_assistant_text(response_obj)
729
-
502
+ text = _extract_text_from_success_kwargs(kwargs) or _extract_assistant_text(response_obj)
730
503
  if text.strip():
731
504
  capture = _content_capture_enabled()
732
- event_attrs: dict[str, Any] = {
733
- # Always present: proves a reply of exactly these bytes was produced, and lets a
734
- # disclosed transcript be matched back to the span without storing the text.
735
- "gen_ai.output.sha256": _sha256(text),
736
- "gen_ai.output.length": len(text),
737
- "gen_ai.output.truncated": len(text) > _OTEL_TEXT_MAX_LEN,
738
- "gen_ai.output.captured": capture,
739
- "gen_ai.response.model": _read_field(kwargs, "model", "unknown"),
740
- }
741
-
505
+ event_attrs: dict[str, Any] = {'gen_ai.output.sha256': _sha256(text), 'gen_ai.output.length': len(text), 'gen_ai.output.truncated': len(text) > _OTEL_TEXT_MAX_LEN, 'gen_ai.output.captured': capture, 'gen_ai.response.model': _read_field(kwargs, 'model', 'unknown')}
742
506
  if capture:
743
- # Structured output (separate from input history) — opt-in only.
744
507
  structured_msgs = _make_output_messages_structured(text)
745
- span.set_attribute(
746
- "gen_ai.output.messages",
747
- json.dumps(structured_msgs, ensure_ascii=False)
748
- )
749
- # Short preview event - easy to spot, low noise
750
- event_attrs["gen_ai.output.preview"] = (
751
- text[:_OTEL_PREVIEW_LEN] + ("..." if len(text) > _OTEL_PREVIEW_LEN else "")
752
- )
753
-
754
- span.add_event(name="gen_ai.output.generated", attributes=event_attrs)
755
-
508
+ span.set_attribute('gen_ai.output.messages', json.dumps(structured_msgs, ensure_ascii=False))
509
+ event_attrs['gen_ai.output.preview'] = text[:_OTEL_PREVIEW_LEN] + ('...' if len(text) > _OTEL_PREVIEW_LEN else '')
510
+ span.add_event(name='gen_ai.output.generated', attributes=event_attrs)
756
511
  span.end(end_time=self._to_ns(end_time))
757
512
  return span
758
-
759
-
760
- # Register
761
513
  structured_genai_otel = CleanSeparatedOtelOutput()
762
514
  reasoning_params_sanitizer = ReasoningParamsSanitizer()
763
515
  codex_proxy_sanitizer = CodexProxySanitizer()