@stkxp/cli 0.1.2

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 (371) hide show
  1. package/README.md +273 -0
  2. package/bin/stkxp.mjs +198 -0
  3. package/package.json +97 -0
  4. package/src/bootstrap.mjs +37 -0
  5. package/src/bootstrap.test.mjs +73 -0
  6. package/src/deploy.mjs +145 -0
  7. package/src/deploy.test.mjs +131 -0
  8. package/src/export.mjs +30 -0
  9. package/src/export.test.mjs +56 -0
  10. package/src/secret-input.mjs +64 -0
  11. package/src/secret-input.test.mjs +58 -0
  12. package/src/serve.mjs +83 -0
  13. package/src/serve.test.mjs +93 -0
  14. package/vendor/dist-server/asyncapi/generator.js +1 -0
  15. package/vendor/dist-server/asyncapi/messages.js +1 -0
  16. package/vendor/dist-server/asyncapi/viewer.html +43 -0
  17. package/vendor/dist-server/core/app-config/branding.js +1 -0
  18. package/vendor/dist-server/core/app-config/mantine-theme.js +1 -0
  19. package/vendor/dist-server/core/app-config/mermaid-theme.js +2 -0
  20. package/vendor/dist-server/core/app-config/prompt-optimization.js +1 -0
  21. package/vendor/dist-server/core/app-config/settings.js +1 -0
  22. package/vendor/dist-server/core/config.js +1 -0
  23. package/vendor/dist-server/core/graph/a2a-agent-executor.js +2 -0
  24. package/vendor/dist-server/core/graph/app.js +120 -0
  25. package/vendor/dist-server/core/graph/delegated-agent-adapter.js +1 -0
  26. package/vendor/dist-server/core/graph/graph-builder.js +3 -0
  27. package/vendor/dist-server/core/graph/kibana-agent-executor.js +1 -0
  28. package/vendor/dist-server/core/graph/nodes/context/assistants-context.js +5 -0
  29. package/vendor/dist-server/core/graph/nodes/context/compare-context.js +10 -0
  30. package/vendor/dist-server/core/graph/nodes/context/enrich-context.js +8 -0
  31. package/vendor/dist-server/core/graph/nodes/context/inventory-context.js +4 -0
  32. package/vendor/dist-server/core/graph/nodes/context/namespace-context.js +2 -0
  33. package/vendor/dist-server/core/graph/nodes/context/node-types-context.js +4 -0
  34. package/vendor/dist-server/core/graph/nodes/context/relevant-assistants-context.js +5 -0
  35. package/vendor/dist-server/core/graph/nodes/context/relevant-skills-context.js +5 -0
  36. package/vendor/dist-server/core/graph/nodes/context/skills-context.js +8 -0
  37. package/vendor/dist-server/core/graph/nodes/context/state-setter.js +1 -0
  38. package/vendor/dist-server/core/graph/nodes/control/check-reset.js +1 -0
  39. package/vendor/dist-server/core/graph/nodes/control/topic-detection.js +1 -0
  40. package/vendor/dist-server/core/graph/nodes/governance/human-approval.js +16 -0
  41. package/vendor/dist-server/core/graph/nodes/index.js +1 -0
  42. package/vendor/dist-server/core/graph/nodes/orchestration/suggestion-generator.js +17 -0
  43. package/vendor/dist-server/core/graph/nodes/orchestration/team-decider.js +1 -0
  44. package/vendor/dist-server/core/graph/nodes/orchestration/team-execution/decider-error.js +4 -0
  45. package/vendor/dist-server/core/graph/nodes/orchestration/team-execution/decider-standalone-runner.js +1 -0
  46. package/vendor/dist-server/core/graph/nodes/orchestration/team-execution/decider-synthesis.js +29 -0
  47. package/vendor/dist-server/core/graph/nodes/orchestration/team-execution/pipeline-memory.js +2 -0
  48. package/vendor/dist-server/core/graph/nodes/orchestration/team-execution/pipeline-runner.js +7 -0
  49. package/vendor/dist-server/core/graph/nodes/orchestration/team-execution/team-assistant-utils.js +1 -0
  50. package/vendor/dist-server/core/graph/nodes/orchestration/team-finder.js +13 -0
  51. package/vendor/dist-server/core/graph/nodes/orchestration/team-invoker.js +3 -0
  52. package/vendor/dist-server/core/graph/nodes/orchestration/team-parallel.js +1 -0
  53. package/vendor/dist-server/core/graph/nodes/orchestration/team-pipeline.js +6 -0
  54. package/vendor/dist-server/core/graph/nodes/orchestration/team-router.js +26 -0
  55. package/vendor/dist-server/core/graph/nodes/reasoning/code-executor/index.js +6 -0
  56. package/vendor/dist-server/core/graph/nodes/reasoning/code-executor/resource-bridge.js +2 -0
  57. package/vendor/dist-server/core/graph/nodes/reasoning/code-executor/sandbox.js +6 -0
  58. package/vendor/dist-server/core/graph/nodes/reasoning/code-executor/tool-bridge.js +1 -0
  59. package/vendor/dist-server/core/graph/nodes/reasoning/local-extractor.js +5 -0
  60. package/vendor/dist-server/core/graph/nodes/reasoning/response-generator.js +47 -0
  61. package/vendor/dist-server/core/graph/nodes/reasoning/static-code-executor/index.js +1 -0
  62. package/vendor/dist-server/core/graph/nodes/reasoning/system.js +54 -0
  63. package/vendor/dist-server/core/graph/nodes/reasoning/tool-executor.js +9 -0
  64. package/vendor/dist-server/core/graph/nodes/tools/raw-output.js +4 -0
  65. package/vendor/dist-server/core/graph/nodes/tools/tool-cleaner.js +7 -0
  66. package/vendor/dist-server/core/graph/nodes/tools/tool-execution/execute-tool-calls.js +3 -0
  67. package/vendor/dist-server/core/graph/nodes/tools/tool-execution/invoke.js +1 -0
  68. package/vendor/dist-server/core/graph/nodes/tools/tool-execution/namespace-guard.js +1 -0
  69. package/vendor/dist-server/core/graph/nodes/tools/tool-execution/result-limits.js +3 -0
  70. package/vendor/dist-server/core/graph/nodes/tools/tool-invoker.js +1 -0
  71. package/vendor/dist-server/core/graph/nodes/tools/tool.js +4 -0
  72. package/vendor/dist-server/core/graph/schemas.js +1 -0
  73. package/vendor/dist-server/core/graph/topic-variants.js +1 -0
  74. package/vendor/dist-server/core/graph/types.js +1 -0
  75. package/vendor/dist-server/core/graph/utils/content-utils.js +17 -0
  76. package/vendor/dist-server/core/graph/utils/edge-jsonata-condition.js +1 -0
  77. package/vendor/dist-server/core/graph/utils/engine-vars.js +1 -0
  78. package/vendor/dist-server/core/graph/utils/logging-utils.js +4 -0
  79. package/vendor/dist-server/core/graph/utils/message-utils.js +5 -0
  80. package/vendor/dist-server/core/graph/utils/node-type-defaults-cache.js +1 -0
  81. package/vendor/dist-server/core/graph/utils/sandbox-catalog.js +1 -0
  82. package/vendor/dist-server/core/graph/utils/schema-utils.js +1 -0
  83. package/vendor/dist-server/core/graph/utils/team-context-policy.js +5 -0
  84. package/vendor/dist-server/core/graph/utils/team-node-resolvers.js +1 -0
  85. package/vendor/dist-server/core/graph/utils/tool-result.js +4 -0
  86. package/vendor/dist-server/core/graph/utils/tool-wrapper.js +14 -0
  87. package/vendor/dist-server/core/llm/init-indices.js +1 -0
  88. package/vendor/dist-server/core/llm/models/index.js +1 -0
  89. package/vendor/dist-server/core/llm/models/model.model.js +1 -0
  90. package/vendor/dist-server/core/llm/models/provider.model.js +1 -0
  91. package/vendor/dist-server/core/llm/models/routing-rule.model.js +1 -0
  92. package/vendor/dist-server/core/llm/providers.js +1 -0
  93. package/vendor/dist-server/core/mcp/client.js +1 -0
  94. package/vendor/dist-server/core/runtime/active-runs-registry.js +1 -0
  95. package/vendor/dist-server/core/runtime/system-prompt-cache.js +1 -0
  96. package/vendor/dist-server/core/runtime/trace-bus.js +1 -0
  97. package/vendor/dist-server/core/schema/cluster_health_report.js +1 -0
  98. package/vendor/dist-server/core/schema/cluster_info.js +1 -0
  99. package/vendor/dist-server/core/schema/cluster_license.js +5 -0
  100. package/vendor/dist-server/core/schema/cluster_stats.js +12 -0
  101. package/vendor/dist-server/core/schema/dashboard.js +1 -0
  102. package/vendor/dist-server/core/schema/indices_settings.js +1 -0
  103. package/vendor/dist-server/core/schema/indices_shards.js +1 -0
  104. package/vendor/dist-server/core/schema/indices_stats.js +1 -0
  105. package/vendor/dist-server/core/schema/nodes_info.js +2 -0
  106. package/vendor/dist-server/core/schema/nodes_stats.js +11 -0
  107. package/vendor/dist-server/core/schema/pricing.js +1 -0
  108. package/vendor/dist-server/core/schema/ui.js +1 -0
  109. package/vendor/dist-server/core/schema/zod_client.js +83 -0
  110. package/vendor/dist-server/core/services/a2a-client.js +2 -0
  111. package/vendor/dist-server/core/services/alerts-service.js +1 -0
  112. package/vendor/dist-server/core/services/api-keys-service.js +1 -0
  113. package/vendor/dist-server/core/services/assistant-resolver.js +1 -0
  114. package/vendor/dist-server/core/services/assistants-index-service.js +1 -0
  115. package/vendor/dist-server/core/services/async-conversations-service.js +22 -0
  116. package/vendor/dist-server/core/services/asyncapi-dispatch.js +1 -0
  117. package/vendor/dist-server/core/services/asyncapi-drivers/driver-manager.js +1 -0
  118. package/vendor/dist-server/core/services/asyncapi-drivers/driver-types.js +0 -0
  119. package/vendor/dist-server/core/services/asyncapi-drivers/http-driver.js +1 -0
  120. package/vendor/dist-server/core/services/asyncapi-drivers/nats-driver.js +1 -0
  121. package/vendor/dist-server/core/services/asyncapi-drivers/websocket-driver.js +1 -0
  122. package/vendor/dist-server/core/services/asyncapi-events-service.js +1 -0
  123. package/vendor/dist-server/core/services/asyncapi-reply-service.js +1 -0
  124. package/vendor/dist-server/core/services/asyncapi-sink.js +1 -0
  125. package/vendor/dist-server/core/services/asyncapi-tools-service.js +1 -0
  126. package/vendor/dist-server/core/services/block-repair-service.js +13 -0
  127. package/vendor/dist-server/core/services/chart-renderer.js +1 -0
  128. package/vendor/dist-server/core/services/chat-llms-service.js +1 -0
  129. package/vendor/dist-server/core/services/chat-runs-service.js +1 -0
  130. package/vendor/dist-server/core/services/chat-tools-service.js +1 -0
  131. package/vendor/dist-server/core/services/chat-traces-service.js +1 -0
  132. package/vendor/dist-server/core/services/chats-service.js +5 -0
  133. package/vendor/dist-server/core/services/comparison-service.js +107 -0
  134. package/vendor/dist-server/core/services/default-model.service.js +1 -0
  135. package/vendor/dist-server/core/services/elastic-stack-sync-service.js +2 -0
  136. package/vendor/dist-server/core/services/elasticsearch-wrapper.js +1 -0
  137. package/vendor/dist-server/core/services/embed-rate-limiter.js +1 -0
  138. package/vendor/dist-server/core/services/error-analytics-service.js +1 -0
  139. package/vendor/dist-server/core/services/es-field-resolver.js +1 -0
  140. package/vendor/dist-server/core/services/gateway-platform-types.js +1 -0
  141. package/vendor/dist-server/core/services/gliner-tagging-service.js +1 -0
  142. package/vendor/dist-server/core/services/golden-questions-scoring.js +1 -0
  143. package/vendor/dist-server/core/services/golden-questions-service.js +1 -0
  144. package/vendor/dist-server/core/services/graph-drilldown-service.js +1 -0
  145. package/vendor/dist-server/core/services/graph-registry-service.js +1 -0
  146. package/vendor/dist-server/core/services/hitl-analytics-service.js +1 -0
  147. package/vendor/dist-server/core/services/ingestion-jobs-service.js +1 -0
  148. package/vendor/dist-server/core/services/ingestion-service.js +5 -0
  149. package/vendor/dist-server/core/services/kibana-client-factory.js +1 -0
  150. package/vendor/dist-server/core/services/kibana-client.js +2 -0
  151. package/vendor/dist-server/core/services/kibana-dashboard-extractor.js +1 -0
  152. package/vendor/dist-server/core/services/kibana-service.js +1 -0
  153. package/vendor/dist-server/core/services/llm-analytics-service.js +1 -0
  154. package/vendor/dist-server/core/services/llm-client-resolver.js +1 -0
  155. package/vendor/dist-server/core/services/llm-models-service.js +1 -0
  156. package/vendor/dist-server/core/services/llm-pricing-pure.js +1 -0
  157. package/vendor/dist-server/core/services/llm-pricing-service.js +1 -0
  158. package/vendor/dist-server/core/services/llm-providers-service.js +1 -0
  159. package/vendor/dist-server/core/services/llm-routing-service.js +1 -0
  160. package/vendor/dist-server/core/services/llm-services.js +1 -0
  161. package/vendor/dist-server/core/services/llm-sync-service.js +1 -0
  162. package/vendor/dist-server/core/services/manifest-loader.js +1 -0
  163. package/vendor/dist-server/core/services/mcp-servers-service.js +1 -0
  164. package/vendor/dist-server/core/services/mcp-sync-service.js +1 -0
  165. package/vendor/dist-server/core/services/mcp-tools-service.js +1 -0
  166. package/vendor/dist-server/core/services/memories-service.js +2 -0
  167. package/vendor/dist-server/core/services/memory-analytics-service.js +1 -0
  168. package/vendor/dist-server/core/services/monitoring-analytics-service.js +1 -0
  169. package/vendor/dist-server/core/services/monitoring-service.js +1 -0
  170. package/vendor/dist-server/core/services/multi-kibana-service.js +1 -0
  171. package/vendor/dist-server/core/services/node-latency-service.js +1 -0
  172. package/vendor/dist-server/core/services/package-policy-service.js +2 -0
  173. package/vendor/dist-server/core/services/pdf-service.js +572 -0
  174. package/vendor/dist-server/core/services/plan-service.js +1 -0
  175. package/vendor/dist-server/core/services/platform-direction.js +1 -0
  176. package/vendor/dist-server/core/services/platform-secrets.js +1 -0
  177. package/vendor/dist-server/core/services/platforms-service.js +1 -0
  178. package/vendor/dist-server/core/services/policy-factory.js +1 -0
  179. package/vendor/dist-server/core/services/prompt-cache-service.js +1 -0
  180. package/vendor/dist-server/core/services/provider-rate-limits.js +1 -0
  181. package/vendor/dist-server/core/services/quota-service.js +1 -0
  182. package/vendor/dist-server/core/services/report-assets-service.js +1 -0
  183. package/vendor/dist-server/core/services/run-analytics-collector.js +1 -0
  184. package/vendor/dist-server/core/services/run-stream-sink.js +1 -0
  185. package/vendor/dist-server/core/services/send_mail.js +460 -0
  186. package/vendor/dist-server/core/services/share-service.js +1 -0
  187. package/vendor/dist-server/core/services/slack-signature.js +1 -0
  188. package/vendor/dist-server/core/services/sources-service.js +6 -0
  189. package/vendor/dist-server/core/services/team-mcp-result.js +4 -0
  190. package/vendor/dist-server/core/services/team-mcp-run-tokens.js +1 -0
  191. package/vendor/dist-server/core/services/team-mcp-runner.js +1 -0
  192. package/vendor/dist-server/core/services/team-mcp-server.js +1 -0
  193. package/vendor/dist-server/core/services/team-mcp-tools.js +1 -0
  194. package/vendor/dist-server/core/services/team-mcp-ui.js +551 -0
  195. package/vendor/dist-server/core/services/team-run-progress.js +1 -0
  196. package/vendor/dist-server/core/services/team-runner-headless.js +7 -0
  197. package/vendor/dist-server/core/services/team-search-service.js +1 -0
  198. package/vendor/dist-server/core/services/teams-service.js +1 -0
  199. package/vendor/dist-server/core/services/token-projection-service.js +1 -0
  200. package/vendor/dist-server/core/services/tool-analytics-service.js +1 -0
  201. package/vendor/dist-server/core/services/tool-history-service.js +1 -0
  202. package/vendor/dist-server/core/services/tool-metrics-service.js +1 -0
  203. package/vendor/dist-server/core/services/tool-scoring.js +1 -0
  204. package/vendor/dist-server/core/services/toolbox-import-mappers.js +1 -0
  205. package/vendor/dist-server/core/services/toolbox-import-service.js +1 -0
  206. package/vendor/dist-server/core/services/trigger-service.js +1 -0
  207. package/vendor/dist-server/core/services/version-snapshot-service.js +1 -0
  208. package/vendor/dist-server/core/services/webhook-calls-service.js +1 -0
  209. package/vendor/dist-server/core/tools/compare-chat.js +46 -0
  210. package/vendor/dist-server/core/tools/enrich-chat.js +50 -0
  211. package/vendor/dist-server/core/tools/variable-encoding.js +1 -0
  212. package/vendor/dist-server/core/types/settings.js +1 -0
  213. package/vendor/dist-server/core/utils/ab-evaluator.js +5 -0
  214. package/vendor/dist-server/core/utils/condition-evaluator.js +1 -0
  215. package/vendor/dist-server/core/utils/http-client.js +1 -0
  216. package/vendor/dist-server/core/utils/json-schema-to-form.js +1 -0
  217. package/vendor/dist-server/core/utils/logger.js +3 -0
  218. package/vendor/dist-server/core/utils/owner-scope.js +1 -0
  219. package/vendor/dist-server/core/utils/ownership.js +1 -0
  220. package/vendor/dist-server/core/utils/parallel-tool-executor.js +1 -0
  221. package/vendor/dist-server/core/utils/performance-tracker.js +3 -0
  222. package/vendor/dist-server/core/utils/prompt-optimizer.js +40 -0
  223. package/vendor/dist-server/core/utils/response-cache.js +1 -0
  224. package/vendor/dist-server/core/utils/schema-validator.js +1 -0
  225. package/vendor/dist-server/core/utils/streaming-optimizer.js +2 -0
  226. package/vendor/dist-server/core/utils/string-helpers.js +1 -0
  227. package/vendor/dist-server/core/utils/test-responses.js +9 -0
  228. package/vendor/dist-server/core/utils/text-utils.js +1 -0
  229. package/vendor/dist-server/core/utils/tool-form-trigger.js +1 -0
  230. package/vendor/dist-server/entrypoints/cli-bootstrap-run.js +1 -0
  231. package/vendor/dist-server/entrypoints/cli-deploy-run.js +1 -0
  232. package/vendor/dist-server/entrypoints/cli-deploy.js +1 -0
  233. package/vendor/dist-server/entrypoints/team-runner.js +1 -0
  234. package/vendor/dist-server/generated/toolbox-catalog.js +1 -0
  235. package/vendor/dist-server/index.js +2 -0
  236. package/vendor/dist-server/middleware/integration-logos.js +1 -0
  237. package/vendor/dist-server/middleware/plan-guard.js +1 -0
  238. package/vendor/dist-server/openapi/generator.js +1 -0
  239. package/vendor/dist-server/openapi/html-tool-spec.js +1 -0
  240. package/vendor/dist-server/routes/a2a-routes.js +1 -0
  241. package/vendor/dist-server/routes/a2a-server-routes.js +5 -0
  242. package/vendor/dist-server/routes/admin.js +1 -0
  243. package/vendor/dist-server/routes/assistants-routes.js +1 -0
  244. package/vendor/dist-server/routes/asyncapi-routes.js +1 -0
  245. package/vendor/dist-server/routes/auth.js +1 -0
  246. package/vendor/dist-server/routes/billing-routes.js +1 -0
  247. package/vendor/dist-server/routes/chat-llms.js +1 -0
  248. package/vendor/dist-server/routes/chat-tools.js +1 -0
  249. package/vendor/dist-server/routes/chat-traces.js +1 -0
  250. package/vendor/dist-server/routes/chats.js +1 -0
  251. package/vendor/dist-server/routes/clusters.js +1 -0
  252. package/vendor/dist-server/routes/compare.js +43 -0
  253. package/vendor/dist-server/routes/connectors-routes.js +1 -0
  254. package/vendor/dist-server/routes/consumptions-routes.js +1 -0
  255. package/vendor/dist-server/routes/data-admin-routes.js +1 -0
  256. package/vendor/dist-server/routes/data-transfer-routes.js +1 -0
  257. package/vendor/dist-server/routes/elastic-tool-execution-routes.js +1 -0
  258. package/vendor/dist-server/routes/geo-proxy-routes.js +1 -0
  259. package/vendor/dist-server/routes/golden-questions-routes.js +1 -0
  260. package/vendor/dist-server/routes/graph-registry.js +1 -0
  261. package/vendor/dist-server/routes/graph-templates-routes.js +1 -0
  262. package/vendor/dist-server/routes/helpdesk-routes.js +35 -0
  263. package/vendor/dist-server/routes/html-routes.js +1 -0
  264. package/vendor/dist-server/routes/index.js +1 -0
  265. package/vendor/dist-server/routes/indices.js +1 -0
  266. package/vendor/dist-server/routes/integrations-routes.js +1 -0
  267. package/vendor/dist-server/routes/langgraph.js +7 -0
  268. package/vendor/dist-server/routes/live-resources-routes.js +1 -0
  269. package/vendor/dist-server/routes/llm-analytics.js +1 -0
  270. package/vendor/dist-server/routes/llm-control-plane.js +1 -0
  271. package/vendor/dist-server/routes/llm.js +1 -0
  272. package/vendor/dist-server/routes/mcp-gateway-routes.js +1 -0
  273. package/vendor/dist-server/routes/mcp-query-routes.js +1 -0
  274. package/vendor/dist-server/routes/mcp-servers-routes.js +1 -0
  275. package/vendor/dist-server/routes/mcp-team-routes.js +1 -0
  276. package/vendor/dist-server/routes/mcp-tools-routes.js +1 -0
  277. package/vendor/dist-server/routes/me-routes.js +63 -0
  278. package/vendor/dist-server/routes/memories-routes.js +1 -0
  279. package/vendor/dist-server/routes/monitoring-analytics.js +12 -0
  280. package/vendor/dist-server/routes/monitoring.js +1 -0
  281. package/vendor/dist-server/routes/node-types-routes.js +171 -0
  282. package/vendor/dist-server/routes/nodes.js +1 -0
  283. package/vendor/dist-server/routes/packages.js +1 -0
  284. package/vendor/dist-server/routes/pdf.js +3 -0
  285. package/vendor/dist-server/routes/plan-routes.js +1 -0
  286. package/vendor/dist-server/routes/platform-requests-routes.js +1 -0
  287. package/vendor/dist-server/routes/platforms-routes.js +1 -0
  288. package/vendor/dist-server/routes/prompts-routes.js +1 -0
  289. package/vendor/dist-server/routes/proxy-logos.js +1 -0
  290. package/vendor/dist-server/routes/quality-routes.js +1 -0
  291. package/vendor/dist-server/routes/resources-ingest-routes.js +1 -0
  292. package/vendor/dist-server/routes/resources-routes.js +1 -0
  293. package/vendor/dist-server/routes/schema-routes.js +1 -0
  294. package/vendor/dist-server/routes/settings-original.js +1 -0
  295. package/vendor/dist-server/routes/settings.js +1 -0
  296. package/vendor/dist-server/routes/share-routes.js +1 -0
  297. package/vendor/dist-server/routes/sources-catalog-routes.js +1 -0
  298. package/vendor/dist-server/routes/sources-routes.js +1 -0
  299. package/vendor/dist-server/routes/team-optimizer-routes.js +1 -0
  300. package/vendor/dist-server/routes/team-schedule-routes.js +1 -0
  301. package/vendor/dist-server/routes/teams-routes.js +1 -0
  302. package/vendor/dist-server/routes/tool-analytics.js +1 -0
  303. package/vendor/dist-server/routes/tool-history-routes.js +1 -0
  304. package/vendor/dist-server/routes/tool-metrics-routes.js +1 -0
  305. package/vendor/dist-server/routes/toolbox-import-routes.js +1 -0
  306. package/vendor/dist-server/routes/tools-routes.js +5 -0
  307. package/vendor/dist-server/routes/transcribe-routes.js +1 -0
  308. package/vendor/dist-server/routes/triggers.js +1 -0
  309. package/vendor/dist-server/routes/versions-routes.js +1 -0
  310. package/vendor/dist-server/routes/webhooks-routes.js +1 -0
  311. package/vendor/dist-server/scripts/add-label-to-tools.js +4 -0
  312. package/vendor/dist-server/scripts/migrate-assistants-to-mcp.js +10 -0
  313. package/vendor/dist-server/scripts/migrate-chat-runs.js +8 -0
  314. package/vendor/dist-server/scripts/migrate-mcp-protocol.js +8 -0
  315. package/vendor/dist-server/scripts/migrate-mcp-to-platforms.js +15 -0
  316. package/vendor/dist-server/services/alert-evaluator.js +3 -0
  317. package/vendor/dist-server/services/auth.js +1 -0
  318. package/vendor/dist-server/services/billing-service.js +1 -0
  319. package/vendor/dist-server/services/chat-title-generator.js +12 -0
  320. package/vendor/dist-server/services/clone/clone-executor.js +1 -0
  321. package/vendor/dist-server/services/clone/closure-resolver.js +3 -0
  322. package/vendor/dist-server/services/clone/entity-graph.js +3 -0
  323. package/vendor/dist-server/services/clone/team-bundle-bootstrap.js +1 -0
  324. package/vendor/dist-server/services/clone/team-bundle-crypto.js +1 -0
  325. package/vendor/dist-server/services/clone/team-bundle-encrypted.js +1 -0
  326. package/vendor/dist-server/services/clone/team-bundle.js +1 -0
  327. package/vendor/dist-server/services/cluster-service.js +1 -0
  328. package/vendor/dist-server/services/connection-adapters.js +1 -0
  329. package/vendor/dist-server/services/connectors-service.js +10 -0
  330. package/vendor/dist-server/services/cost-forecast-service.js +1 -0
  331. package/vendor/dist-server/services/eui-ssr-renderer.js +4 -0
  332. package/vendor/dist-server/services/graph-canvas/full-structure-builder.js +1 -0
  333. package/vendor/dist-server/services/graph-templates-service.js +13 -0
  334. package/vendor/dist-server/services/guest-service.js +1 -0
  335. package/vendor/dist-server/services/html-service.js +1 -0
  336. package/vendor/dist-server/services/langgraph-service.js +2 -0
  337. package/vendor/dist-server/services/leaflet-render-service.js +80 -0
  338. package/vendor/dist-server/services/live-resources-service.js +16 -0
  339. package/vendor/dist-server/services/mcp-app-tester-package.js +1 -0
  340. package/vendor/dist-server/services/mcp-gateway/executors/openapi-executor.js +1 -0
  341. package/vendor/dist-server/services/mcp-gateway/gateway-grant-service.js +2 -0
  342. package/vendor/dist-server/services/team-optimizer-service.js +1 -0
  343. package/vendor/dist-server/services/trace-bus-sink.js +1 -0
  344. package/vendor/dist-server/services/user-provisioning-service.js +3 -0
  345. package/vendor/dist-server/templates/mcp-app-tester/src/mcp-http.js +3 -0
  346. package/vendor/dist-server/templates/mcp-app-tester/src/server.js +1 -0
  347. package/vendor/dist-server/utils.js +1 -0
  348. package/vendor/dist-server/ws/assistant-executor.js +7 -0
  349. package/vendor/dist-server/ws/classify-streamed-json-block.js +1 -0
  350. package/vendor/dist-server/ws/collect-response-generator-node-ids.js +1 -0
  351. package/vendor/dist-server/ws/extractors/blockkit-extractor.js +1 -0
  352. package/vendor/dist-server/ws/extractors/echarts-extractor.js +1 -0
  353. package/vendor/dist-server/ws/extractors/eui-extractor.js +1 -0
  354. package/vendor/dist-server/ws/extractors/form-extractor.js +1 -0
  355. package/vendor/dist-server/ws/extractors/index.js +1 -0
  356. package/vendor/dist-server/ws/extractors/leaflet-extractor.js +1 -0
  357. package/vendor/dist-server/ws/extractors/mantine-extractor.js +1 -0
  358. package/vendor/dist-server/ws/extractors/markdown-extractor.js +7 -0
  359. package/vendor/dist-server/ws/extractors/mermaid-extractor.js +1 -0
  360. package/vendor/dist-server/ws/extractors/recharts-extractor.js +1 -0
  361. package/vendor/dist-server/ws/extractors/remotion-extractor.js +1 -0
  362. package/vendor/dist-server/ws/handler.js +66 -0
  363. package/vendor/dist-server/ws/parsers/anthropic-parser.js +7 -0
  364. package/vendor/dist-server/ws/parsers/base-parser.js +3 -0
  365. package/vendor/dist-server/ws/parsers/gemini-parser.js +8 -0
  366. package/vendor/dist-server/ws/parsers/index.js +1 -0
  367. package/vendor/dist-server/ws/parsers/parse-json-blocks.js +1 -0
  368. package/vendor/dist-server/ws/types.js +0 -0
  369. package/vendor/dist-server/ws/utils.js +1 -0
  370. package/vendor/shared/engine-vars.ts +51 -0
  371. package/vendor/shared/llm-providers-config.ts +69 -0
@@ -0,0 +1 @@
1
+ import{Client as _}from"@elastic/elasticsearch";import{getDefaultGraphForOwner as y,getGraph as f}from"./graph-registry-service";const m=process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",v=process.env.ELASTICSEARCH_USER||"elastic",h=process.env.ELASTICSEARCH_PASSWORD||"",g=".stkxp_assistants",u=new _({node:m,auth:{username:v,password:h},tls:{rejectUnauthorized:!1},requestTimeout:6e4,maxRetries:3}),A=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;async function w(t,r){if(!t||!r||t.startsWith(`${r}-`)||A.test(t))return t;const e=`${r}-${t}`;try{if(await f(e))return console.log(`[AssistantResolver] graphId upgraded: "${t}" \u2192 "${e}"`),e}catch{}return t}async function R(t,r){if(!t||t.length===0)return[];try{console.log(`[AssistantResolver] Resolving ${t.length} assistants...`);const e=new Map,l=[];for(const s of t){const i=r?[`${r}-${s}`,s]:[s];e.set(s,i);for(const o of i)l.includes(o)||l.push(o)}const n=await u.mget({index:g,body:{ids:l}}),c=new Map;for(const s of n.docs)s.found&&s._source&&c.set(s._id,s);const p=[];for(const s of t){let i=null;for(const a of e.get(s)??[s])if(c.has(a)){i=c.get(a),a!==s&&console.log(`[AssistantResolver] "${s}" \u2192 user copy "${a}"`);break}if(!i){console.warn(`[AssistantResolver] Assistant not found: ${s}`);continue}const o=i._source;let d=await w(o.graphId,r);if(!d){if(!r){console.error(`[AssistantResolver] Assistant ${s} (${o.name}) has no graphId and no username to resolve default`);continue}const a=await y(r);if(!a)throw new Error(`Assistant "${o.name}" has no graphId and no default graph is configured for user "${r}". Set a default graph in Graph Registry.`);d=a.id,console.log(`[AssistantResolver] Assistant ${s} (${o.name}) has no graphId \u2192 using default graph: ${d}`)}p.push({assistantId:s,name:o.name,package:o.package,topic:o.topic||"",graphId:d,priority:o.routing?.priority||50,tools:(o.tools_policy?.allowed_tools||[]).map(a=>a.id||a),metadata:{status:o.metadata?.status||"unknown",cluster_scope:o.metadata?.cluster_scope},context:o.context,behavior:o.behavior,error_policy:o.error_policy,mcp_servers_policy:o.mcp_servers_policy,tools_policy:o.tools_policy,promptId:o.promptId,promptCompositionMode:o.promptCompositionMode,node_overrides:o.node_overrides,llm_overrides:o.llm_overrides,output_policy:o.output_policy})}return p.sort((s,i)=>i.priority-s.priority),console.log(`[AssistantResolver] Resolved ${p.length}/${t.length} assistants`),p.forEach(s=>{console.log(` - ${s.name} (graphId: ${s.graphId}, priority: ${s.priority})`)}),p}catch(e){throw console.error("[AssistantResolver] Error resolving assistants:",e),new Error(`Failed to resolve assistants: ${e.message}`)}}async function S(t){const r=await R([t]);return r.length>0?r[0]:null}async function x(t,r){try{const e=[];return t&&e.push({term:{package:t}}),r&&e.push({term:{topic:r}}),(await u.search({index:g,body:{query:{bool:{must:e.length>0?e:[{match_all:{}}]}},sort:[{"routing.priority":{order:"desc"}},{name:{order:"asc"}}],size:100}})).hits.hits.map(n=>({assistantId:n._id,name:n._source.name,package:n._source.package,topic:n._source.topic||"",graphId:n._source.graphId,priority:n._source.routing?.priority||50,tools:(n._source.tools_policy?.allowed_tools||[]).map(c=>c.id||c),metadata:{status:n._source.metadata?.status||"unknown",cluster_scope:n._source.metadata?.cluster_scope},context:n._source.context,mcp_servers_policy:n._source.mcp_servers_policy,tools_policy:n._source.tools_policy}))}catch(e){throw console.error("[AssistantResolver] Error getting assistants:",e),new Error(`Failed to get assistants: ${e.message}`)}}export{x as getAssistantsByPackageAndTopic,S as resolveAssistant,R as resolveAssistants,w as resolveLegacyGraphIdUpgrade};
@@ -0,0 +1 @@
1
+ import{Client as a}from"@elastic/elasticsearch";const n=process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",p=process.env.ELASTICSEARCH_USER||"elastic",c=process.env.ELASTICSEARCH_PASSWORD||"diagnostics",e=".stkxp_assistants",s=new a({node:n,auth:{username:p,password:c},tls:{rejectUnauthorized:!1},requestTimeout:6e4,maxRetries:3}),i=".multilingual-e5-small-elasticsearch",r={properties:{name:{type:"keyword",copy_to:"search_semantic"},owner:{type:"keyword"},topic:{type:"keyword",copy_to:"search_semantic"},tags:{type:"keyword",copy_to:"search_semantic"},version:{type:"keyword"},graphId:{type:"keyword"},promptId:{type:"keyword"},promptCompositionMode:{type:"keyword"},created_at:{type:"date"},updated_at:{type:"date"},search_semantic:{type:"semantic_text",inference_id:i},metadata:{properties:{status:{type:"keyword"},cluster_scope:{type:"keyword"}}},routing:{properties:{priority:{type:"integer"}}},context:{properties:{role:{type:"text",copy_to:"search_semantic"},style:{properties:{language:{type:"keyword"},tone:{type:"keyword"},audience:{type:"keyword"}}},constraints:{type:"text"}}},behavior:{properties:{task_brief:{type:"text",copy_to:"search_semantic",fields:{keyword:{type:"keyword",ignore_above:256}}},detailed_instructions:{type:"text",copy_to:"search_semantic",fields:{keyword:{type:"keyword",ignore_above:256}}}}}}};async function d(){try{if(!await s.indices.exists({index:e})){await s.indices.create({index:e,body:{settings:{number_of_shards:1,number_of_replicas:0},mappings:r}}),console.log(`[assistants] Created index ${e} with explicit mapping`);return}try{await s.indices.putMapping({index:e,body:{properties:{search_semantic:{type:"semantic_text",inference_id:i},name:{type:"keyword",copy_to:"search_semantic"},topic:{type:"keyword",copy_to:"search_semantic"},tags:{type:"keyword",copy_to:"search_semantic"},context:{properties:{role:{type:"text",copy_to:"search_semantic"}}},behavior:{properties:{task_brief:{type:"text",copy_to:"search_semantic",fields:{keyword:{type:"keyword",ignore_above:256}}},detailed_instructions:{type:"text",copy_to:"search_semantic",fields:{keyword:{type:"keyword",ignore_above:256}}}}}}}})}catch(o){String(o?.message||"").includes("illegal_argument_exception")||console.warn("[assistants] putMapping warning for search_semantic field:",o?.message)}}catch(t){console.error("[assistants] initializeAssistantsIndex failed:",t?.message??t)}}export{e as ASSISTANTS_INDEX,r as ASSISTANTS_MAPPING,d as initializeAssistantsIndex};
@@ -0,0 +1,22 @@
1
+ import{Client as _}from"@elastic/elasticsearch";import{randomUUID as S}from"crypto";const v=process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",T=process.env.ELASTICSEARCH_USER||"elastic",b=process.env.ELASTICSEARCH_PASSWORD||"",a=".stkxp_async_conversations",l=parseInt(process.env.ASYNC_CONVERSATION_TTL_DAYS??"7",10),w=(Number.isFinite(l)&&l>0?l:7)*24*60*60*1e3,c=new _({node:v,auth:{username:T,password:b},tls:{rejectUnauthorized:!1},requestTimeout:1e4,maxRetries:2});let m=!1;async function E(){if(!m)try{await c.indices.exists({index:a})||(await c.indices.create({index:a,body:{settings:{number_of_shards:1,number_of_replicas:1},mappings:{properties:{conversationId:{type:"keyword"},subscriberId:{type:"keyword"},owner:{type:"keyword"},chatId:{type:"keyword"},threadId:{type:"keyword"},messageCount:{type:"integer"},createdAt:{type:"date"},lastActivityAt:{type:"date"},ttlAt:{type:"date"}}}}}),console.log(`[AsyncConversations] Index created: ${a} (TTL ${l}d)`)),m=!0}catch(t){t?.meta?.statusCode===400?m=!0:console.warn("[AsyncConversations] ensureIndex warning:",t?.message)}}function y(t,e,s){return`${t}::${e}::${s}`}async function D(t){await E();const{conversationId:e,subscriberId:s,owner:o,newChatId:d,newThreadId:A}=t,i=y(o,s,e),g=new Date,p=g.toISOString(),C=new Date(g.getTime()+w).toISOString(),r=d(),u=A?A():r,h={source:`
2
+ def now = params.nowMs;
3
+ def ttlMs = params.ttlMs;
4
+ if (ctx._source.ttlAt != null) {
5
+ def ttlAt = ZonedDateTime.parse(ctx._source.ttlAt).toInstant().toEpochMilli();
6
+ if (ttlAt > now) {
7
+ ctx._source.messageCount = (ctx._source.messageCount == null ? 0 : ctx._source.messageCount) + 1;
8
+ ctx._source.lastActivityAt = params.nowIso;
9
+ ctx._source.ttlAt = params.ttlIso;
10
+ ctx.op = 'index';
11
+ return;
12
+ }
13
+ }
14
+ // Stale or never-was \u2014 reset.
15
+ ctx._source.chatId = params.candidateChatId;
16
+ ctx._source.threadId = params.candidateThreadId;
17
+ ctx._source.messageCount = 1;
18
+ ctx._source.createdAt = params.nowIso;
19
+ ctx._source.lastActivityAt = params.nowIso;
20
+ ctx._source.ttlAt = params.ttlIso;
21
+ ctx.op = 'index';
22
+ `,params:{nowMs:g.getTime(),ttlMs:w,nowIso:p,ttlIso:C,candidateChatId:r,candidateThreadId:u}},f={id:i,conversationId:e,subscriberId:s,owner:o,chatId:r,threadId:u,messageCount:1,createdAt:p,lastActivityAt:p,ttlAt:C};try{await c.update({index:a,id:i,refresh:"wait_for",body:{script:h,upsert:f}})}catch(I){return console.warn(`[AsyncConversations] update failed for ${i}, falling back to fresh chat:`,I?.message),{chatId:r,threadId:u,isNew:!0,turn:1}}try{const n=(await c.get({index:a,id:i}))?._source;if(n){const x=n.chatId===r&&n.messageCount===1;return{chatId:n.chatId,threadId:n.threadId,isNew:x,turn:n.messageCount??1}}}catch(I){console.warn(`[AsyncConversations] readback failed for ${i}:`,I?.message)}return{chatId:r,threadId:u,isNew:!0,turn:1}}async function N(t,e,s){const o=y(t,e,s);try{await c.delete({index:a,id:o,refresh:"wait_for"})}catch(d){d?.meta?.statusCode!==404&&console.warn(`[AsyncConversations] delete failed for ${o}:`,d?.message)}}function k(){return S()}export{k as defaultChatIdFactory,N as deleteConversation,E as ensureConversationsIndex,D as getOrCreateConversation};
@@ -0,0 +1 @@
1
+ import{randomUUID as I}from"crypto";import{logWebhookCall as D,updateWebhookCall as c,hashPayload as j}from"./webhook-calls-service";import{countTeamWebhookRunsSince as O}from"./webhook-calls-service";import{recordSubscribedEvent as E}from"./asyncapi-events-service";import{recordTriggerExecution as P}from"./trigger-service";import{runTeamHeadless as M}from"./team-runner-headless";import{createChat as U,updateChat as H}from"./chats-service";import{createChatRun as x}from"./chat-runs-service";import{getTeamWebhookConfig as W}from"./teams-service";import{getOrCreateConversation as L}from"./async-conversations-service";const $=process.env.ASYNCAPI_AUTOLAUNCH_ENABLED!=="false",R=parseInt(process.env.MAX_WEBHOOK_RUNS_PER_HOUR_PER_TEAM??"100",10);async function V(g){const{trigger:t,payload:o,headers:m={},source:d}=g,u=Date.now(),a=g.webhookCallId??I(),w=!g.webhookCallId;if(typeof o!="object"||o===null||Array.isArray(o))return w?await D({webhookCallId:a,receivedAt:new Date().toISOString(),teamId:"",triggerId:t.id,owner:t.owner??null,sourceIp:typeof d=="string"?d:null,status:"rejected_payload",rejectionReason:"body_not_object",payload:o,payloadHash:j(o),headers:m}):await c(a,{status:"rejected_payload",rejectionReason:"body_not_object",durationMs:Date.now()-u}),{kind:"rejected",status:"rejected_payload",message:"body must be a JSON object"};const s=t.inputTemplate?.asyncapi;if(!s?.platformId||!s?.channel)return w?await D({webhookCallId:a,receivedAt:new Date().toISOString(),teamId:"",triggerId:t.id,owner:t.owner??null,sourceIp:typeof d=="string"?d:null,status:"rejected_payload",rejectionReason:"not_asyncapi_subscriber",payload:o,payloadHash:j(o),headers:m}):await c(a,{status:"rejected_payload",rejectionReason:"not_asyncapi_subscriber",durationMs:Date.now()-u}),{kind:"rejected",status:"rejected_payload",message:"trigger is not an AsyncAPI subscriber"};w?await D({webhookCallId:a,receivedAt:new Date().toISOString(),teamId:"",triggerId:t.id,channel:s.channel,owner:t.owner??null,sourceIp:typeof d=="string"?d:null,status:"pending",payload:o,payloadHash:j(o),headers:m}):await c(a,{owner:t.owner??null,channel:s.channel});let f;try{f=await E({triggerId:t.id,platformId:s.platformId,channel:s.channel,payload:o,owner:t.owner,sourceIp:typeof d=="string"?d:void 0})}catch(e){return await c(a,{status:"failed",error:`event persist failed: ${e?.message??e}`,durationMs:Date.now()-u}),{kind:"failed",message:`event persist failed: ${e?.message??e}`}}try{await P(t.id)}catch{}const n=t.targetTeamId?.trim();if(!$||!n)return await c(a,{status:"accepted_no_target",durationMs:Date.now()-u}),{kind:"accepted_no_target",eventId:f,note:$?"event recorded \u2014 no targetTeamId wired on this subscriber":"event recorded \u2014 autolaunch disabled by ASYNCAPI_AUTOLAUNCH_ENABLED=false"};let r;try{r=await W(n)}catch(e){return await c(a,{teamId:n,status:"failed",error:`team lookup failed: ${e?.message??e}`,durationMs:Date.now()-u}),{kind:"failed",message:`team lookup failed: ${e?.message??e}`}}if(!r||!r.owner)return await c(a,{teamId:n,status:"failed",error:r?"team_no_owner":"team_not_found",durationMs:Date.now()-u}),{kind:"failed",message:"target team not found or has no owner"};const h=await O(n);if(h>=R)return await c(a,{teamId:n,owner:r.owner,status:"rejected_quota",rejectionReason:`last_hour_count_${h}_max_${R}`,durationMs:Date.now()-u}),{kind:"rejected",status:"rejected_quota",message:`Per-team hourly webhook quota exceeded (${h}/${R})`,quotaCount:h};const p=I(),y=new Date().toISOString(),_=m["x-conversation-id"]??m["X-Conversation-Id"],b=o?.conversationId,A=typeof b=="string"&&b.trim()!==""?b.trim():typeof _=="string"&&_.trim()!==""?_.trim():null,i=A&&A.length<=256?A:null;let l,k,v=1,T=!0;if(i){const e=await L({conversationId:i,subscriberId:t.id,owner:r.owner,newChatId:()=>I()});l=e.chatId,k=e.threadId,v=e.turn,T=e.isNew}else l=I(),k=l;try{if(T)await U({id:l,title:i?`AsyncAPI: ${s.channel} (conv ${i.slice(0,16)}) \u2192 ${r.name??n}`:`AsyncAPI: ${s.channel} \u2192 ${r.name??n}`,teamId:n,teamName:r.name??n,status:"draft",messages:[],assistants:[],metadata:{source:"asyncapi_webhook",webhookCallId:a,subscriberId:t.id,platformId:s.platformId,channel:s.channel,messageRef:s.messageRef,...i?{conversationId:i}:{}},username:r.owner,createdAt:y,updatedAt:y});else try{await H(l,r.owner,{updatedAt:y})}catch{}await x(p,l,r.owner,{teamId:n,teamName:r.name??n,assistantId:"__asyncapi_webhook_pending__",assistantName:"AsyncAPI webhook pending",graphId:r.graphId,userMessage:JSON.stringify(o).slice(0,2e3),metadata:{webhookCallId:a,subscriberId:t.id,channel:s.channel,webhookSourceIp:typeof d=="string"?d:void 0}}),await c(a,{teamId:n,owner:r.owner,status:"accepted",runId:p,chatId:l,durationMs:Date.now()-u});const e=m["x-stkxp-webhook-depth"]??m["X-Stkxp-Webhook-Depth"],C=typeof e=="string"&&e!==""?parseInt(e,10):g.depth??0,S=Number.isFinite(C)&&C>=0?C:0;return setImmediate(()=>{M({teamId:n,runId:p,chatId:l,webhookCallId:a,payload:o,depth:S,triggerId:t.id,threadIdOverride:k,conversationId:i??void 0,channel:"webhook"}).catch(N=>{console.error(`[asyncapi/dispatch] Unexpected throw from runTeamHeadless (runId=${p}):`,N)})}),console.log(`[asyncapi/dispatch] Accepted call ${a} on subscriber ${t.id} \u2192 team ${n}, run ${p}`+(i?` (conv ${i.slice(0,32)} turn ${v})`:"")),{kind:"accepted",eventId:f,runId:p,chatId:l,teamId:n,...i?{conversationId:i,conversationTurn:v}:{}}}catch(e){return console.error("[asyncapi/dispatch] Failed to provision chat/run:",e),await c(a,{teamId:n,owner:r.owner,status:"failed",error:String(e?.message??e).slice(0,1e3),durationMs:Date.now()-u}),{kind:"failed",message:e?.message??"provisioning failed"}}}export{V as dispatchInboundAsyncApi};
@@ -0,0 +1 @@
1
+ import{HttpDriver as d}from"./http-driver";import{NatsDriver as p}from"./nats-driver";import{WebSocketDriver as u}from"./websocket-driver";import{dispatchInboundAsyncApi as b}from"../asyncapi-dispatch";import{getEnabledPlatformsByType as g,getPlatformById as f}from"../platforms-service";import{getTrigger as h,getTriggersBySubscriberPlatform as m}from"../trigger-service";class v{drivers=new Map;subscriptions=new Map;booted=!1;bootError;constructor(){this.drivers.set("http",new d),this.drivers.set("nats",new p),this.drivers.set("ws",new u)}async init(){if(this.booted)return;const r=await Promise.allSettled([...this.drivers.values()].map(t=>t.init()));for(const[t,e]of r.entries()){const o=[...this.drivers.keys()][t];e.status==="rejected"?console.warn(`[DriverManager] ${o} driver init failed: ${e.reason?.message??e.reason} \u2014 driver disabled, publishes/subscribes on this protocol will fail-soft`):console.log(`[DriverManager] ${o} driver initialised`)}this.booted=!0}async bootstrapAllPlatformSubscribers(){await this.init();let r;try{r=await g("AsyncAPI")}catch(o){console.warn(`[DriverManager] bootstrap: failed to list platforms: ${o?.message}`),this.bootError=o?.message;return}let t=0,e=0;for(const o of r){const i=o?.config?.broker,n=i?.protocol;if(!n||n==="http"||n==="kafka"){e++;continue}const a=this.drivers.get(n);if(!a){console.warn(`[DriverManager] bootstrap: no driver for protocol "${n}" on platform ${o.id}`);continue}let s;try{s=await m(o.id,o.owner)}catch(c){console.warn(`[DriverManager] bootstrap: failed to list triggers for platform ${o.id}: ${c?.message}`);continue}for(const c of s)if(c.enabled)try{await this.subscribeOne({id:o.id,name:o.name,owner:o.owner},i,n,a,c),t++}catch(l){console.warn(`[DriverManager] subscribe failed for trigger ${c.id} on platform ${o.id}: ${l?.message}`)}}console.log(`[DriverManager] bootstrap complete: ${t} active subscription(s), ${e} HTTP platform(s) skipped (request-driven)`)}async publish(r){await this.init();const t=r.broker.protocol,e=this.drivers.get(t);if(!e)return{ok:!1,error:`no_driver_for_protocol:${t}`,target:r.broker.host};try{return await e.publish(r)}catch(o){return{ok:!1,error:o?.message??String(o),target:r.broker.host}}}async shutdown(){if(!this.booted)return;console.log(`[DriverManager] shutting down \u2014 draining ${this.subscriptions.size} subscription(s)`);const r=[...this.drivers.values()].map(t=>Promise.race([t.shutdown(),new Promise(e=>setTimeout(e,5e3))]).catch(e=>console.warn(`[DriverManager] ${t.protocol} shutdown error: ${e?.message??e}`)));await Promise.allSettled(r),this.subscriptions.clear(),this.booted=!1}async reconcileTrigger(r){await this.init();const t=[...this.subscriptions.values()].find(s=>s.handle.triggerId===r);let e=null;try{e=await h(r)}catch(s){return{ok:!1,action:"noop",error:`trigger_lookup_failed:${s?.message??s}`}}const o=!!(e&&e.enabled&&e.type==="webhook"&&e.inputTemplate?.asyncapi?.platformId&&e.inputTemplate?.asyncapi?.channel);if(!o&&!t)return{ok:!0,action:"noop"};if(!o&&t)return await this.unsubscribeOne(t),console.log(`[DriverManager] reconcile: unsubscribed trigger=${r} (sub=${t.handle.id})`),{ok:!0,action:"unsubscribed"};if(o&&!t){const s=await this.applySubscribe(e);return s.ok&&console.log(`[DriverManager] reconcile: subscribed trigger=${r}`),s}const i=e.inputTemplate.asyncapi.platformId,n=e.inputTemplate.asyncapi.channel;if(t.handle.platformId!==i||t.handle.channel!==n){await this.unsubscribeOne(t);const s=await this.applySubscribe(e);return s.ok?(console.log(`[DriverManager] reconcile: re-subscribed trigger=${r} (key changed)`),{ok:!0,action:"resubscribed"}):s}return t.targetTeamId=e.targetTeamId,{ok:!0,action:"noop"}}listSubscriptions(){return[...this.subscriptions.values()].map(r=>({id:r.handle.id,protocol:r.protocol,platformId:r.handle.platformId,triggerId:r.handle.triggerId,channel:r.handle.channel,targetTeamId:r.targetTeamId,startedAt:r.startedAt}))}async subscribeOne(r,t,e,o,i){const n=await o.subscribe({id:r.id,name:r.name,owner:r.owner,broker:t},i,async(a,s)=>{try{await b({trigger:a,payload:s.payload,headers:s.headers,source:`${e}:${s.source??r.id}`})}catch(c){console.error(`[DriverManager] dispatch failed for trigger ${a.id} (platform=${r.id}, protocol=${e}): ${c?.message}`)}});return this.subscriptions.set(n.id,{handle:n,protocol:e,targetTeamId:i.targetTeamId,startedAt:new Date().toISOString()}),n}async applySubscribe(r){const t=r.inputTemplate?.asyncapi?.platformId;if(!t)return{ok:!1,action:"noop",error:"trigger_missing_platform_id"};const e=await f(t,r.owner,!0);if(!e||!e.enabled)return{ok:!1,action:"noop",error:`platform_unavailable:${t}`};const o=e.config?.broker,i=o?.protocol;if(!i)return{ok:!1,action:"noop",error:"platform_missing_broker_protocol"};if(i==="http"||i==="kafka")return{ok:!1,action:"noop",error:`protocol_not_subscribable:${i}`};const n=this.drivers.get(i);if(!n)return{ok:!1,action:"noop",error:`no_driver_for_protocol:${i}`};try{return await this.subscribeOne({id:e.id,name:e.name,owner:e.owner},o,i,n,r),{ok:!0,action:"subscribed"}}catch(a){return{ok:!1,action:"noop",error:a?.message??String(a)}}}async unsubscribeOne(r){try{await r.handle.unsubscribe()}catch(t){console.warn(`[DriverManager] unsubscribe ${r.handle.id} failed: ${t?.message??t}`)}this.subscriptions.delete(r.handle.id)}health(){const r={};let t=!0;for(const[e,o]of this.drivers.entries()){const i=o.health();r[e]=i,i.ok||(t=!1)}return{ok:t&&!this.bootError,bootError:this.bootError,drivers:r,subscriptions:this.listSubscriptions()}}}const D=new v;export{D as driverManager};
@@ -0,0 +1 @@
1
+ function f(r){return!r||r.type==="none"?{}:r.type==="bearer"&&r.bearerToken?{Authorization:`Bearer ${r.bearerToken}`}:r.type==="basic"&&r.username?{Authorization:`Basic ${Buffer.from(`${r.username}:${r.password??""}`).toString("base64")}`}:r.type==="apiKey"&&r.apiKeyHeader&&r.apiKeyValue?{[r.apiKeyHeader]:r.apiKeyValue}:{}}function g(r,n,e){if(e==="query"){const i=new URL(r);return i.searchParams.set("channel",n),{url:i.toString()}}return e==="header"?{url:r,extraHeader:{name:"X-Channel",value:n}}:{url:`${r}/${encodeURIComponent(n)}`}}class b{protocol="http";lastError;failureCount=0;async init(){}async subscribe(n,e,i){return{id:`http:${e.id}`,platformId:e.inputTemplate?.asyncapi?.platformId??"",triggerId:e.id,channel:e.inputTemplate?.asyncapi?.channel??"",unsubscribe:async()=>{}}}async publish(n){const{broker:e,channel:i,body:u,headers:p,timeoutMs:c}=n,d=String(e.host).replace(/\/+$/,""),m=e.channelStrategy??"path",{url:a,extraHeader:o}=g(d,i,m),y={...f(e.auth),...o?{[o.name]:o.value}:{},...p},l=Buffer.byteLength(u,"utf8");try{const t=await fetch(a,{method:"POST",headers:y,body:u,signal:AbortSignal.timeout(c)});if(!t.ok){let s="";try{s=(await t.text()).slice(0,500)}catch{}return this.failureCount++,this.lastError=`HTTP ${t.status}: ${s}`.slice(0,500),{ok:!1,error:s||`HTTP ${t.status}`,httpStatus:t.status,bodySize:l,target:a}}return{ok:!0,httpStatus:t.status,bodySize:l,target:a}}catch(t){const s=t?.name==="TimeoutError"?`timeout after ${c}ms`:t?.message??String(t);return this.failureCount++,this.lastError=s.slice(0,500),{ok:!1,error:s,bodySize:l,target:a}}}async shutdown(){}health(){return{protocol:"http",ok:!0,activeConnections:1,subscriptions:0,...this.lastError?{lastError:this.lastError}:{}}}}export{b as HttpDriver};
@@ -0,0 +1 @@
1
+ import{getTrigger as y}from"../trigger-service";class b{protocol="nats";connections=new Map;connectFn=null;natsHeaders=null;initFailure;async init(){if(!this.connectFn)try{const n=await import("@nats-io/transport-node"),e=await import("@nats-io/nats-core");if(this.connectFn=n.connect,this.natsHeaders=e.headers,!this.connectFn||!this.natsHeaders)throw new Error("NATS packages loaded but expected symbols missing")}catch(n){this.initFailure=n?.message??String(n),console.warn(`[NatsDriver] init failed: ${this.initFailure} \u2014 nats subscribes/publishes will fail-soft`)}}buildConnectOptions(n,e){const s={servers:String(n.host||"").split(",").map(r=>r.trim()).filter(Boolean),name:`stkxp-app:${e}`,reconnect:!0,maxReconnectAttempts:-1,reconnectTimeWait:2e3,reconnectJitter:500,reconnectJitterTLS:1e3,pingInterval:3e4,waitOnFirstConnect:!1},t=n.auth;return t&&t.type!=="none"&&(t.type==="bearer"&&t.bearerToken?s.token=t.bearerToken:t.type==="basic"&&t.username?(s.user=t.username,s.pass=t.password??""):t.type==="apiKey"&&t.apiKeyValue&&(s.token=t.apiKeyValue)),s}async getOrOpenConnection(n,e){const s=this.connections.get(n);if(s)return s;if(!this.connectFn)throw new Error(`NATS driver not initialised (${this.initFailure??"init pending"})`);const t=this.buildConnectOptions(e,n),r=await this.connectFn(t),a={nc:r,host:e.host,refCount:0,subs:new Map};return this.connections.set(n,a),(async()=>{try{for await(const i of r.status())i.type==="reconnect"?(a.lastReconnectAt=new Date().toISOString(),console.log(`[NatsDriver] reconnect on ${n} (${e.host})`)):i.type==="error"&&(a.lastError=`${i.type}: ${String(i.data??"")}`.slice(0,500),console.warn(`[NatsDriver] status error on ${n}: ${a.lastError}`))}catch(i){i?.message&&(a.lastError=i.message.slice(0,500))}})().catch(()=>{}),r.closed().then(i=>{i&&(a.lastError=String(i?.message??i).slice(0,500),console.warn(`[NatsDriver] connection closed for ${n}: ${a.lastError}`)),this.connections.delete(n)}),a}async subscribe(n,e,s){const t=e.inputTemplate?.asyncapi?.channel;if(!t)throw new Error(`trigger ${e.id} has no inputTemplate.asyncapi.channel`);const r=await this.getOrOpenConnection(n.id,n.broker),a=t,i=r.nc.subscribe(a,{callback:(c,l)=>{if(c){console.warn(`[NatsDriver] subscription error on ${a}: ${c?.message??c}`);return}let u;try{u=JSON.parse(new TextDecoder().decode(l.data))}catch(o){console.warn(`[NatsDriver] payload parse failed on ${a} (trigger=${e.id}): ${o?.message}`);return}const d={};if(l.headers)for(const o of l.headers.keys()){const h=l.headers.get(o);typeof h=="string"&&(d[o.toLowerCase()]=h)}(async()=>{try{const o=await y(e.id)??e;if(!o.enabled)return;await s(o,{channel:a,payload:u,headers:d,source:`nats:${a}`})}catch(o){console.error(`[NatsDriver] dispatch failed on ${a}: ${o?.message??o}`)}})().catch(()=>{})}});return r.refCount++,r.subs.set(e.id,i),{id:`nats:${n.id}:${e.id}`,platformId:n.id,triggerId:e.id,channel:t,unsubscribe:async()=>{try{i.drain?await i.drain():i.unsubscribe?.()}finally{if(r.subs.delete(e.id),r.refCount--,r.refCount<=0){try{await r.nc.drain()}catch{}this.connections.delete(n.id)}}}}}async publish(n){const{platformId:e,broker:s,channel:t,body:r,headers:a,timeoutMs:i}=n;if(!this.connectFn)return{ok:!1,error:`nats_driver_uninitialised:${this.initFailure??"unknown"}`,target:s.host};let c;try{c=await this.getOrOpenConnection(e,s)}catch(o){return{ok:!1,error:`nats_connect_failed:${o?.message??o}`,target:s.host}}const l=`nats://${s.host}/${t}`,u=new TextEncoder().encode(r),d=this.natsHeaders();for(const[o,h]of Object.entries(a))d.set(o,h);try{return c.nc.publish(t,u,{headers:d}),await Promise.race([c.nc.flush(),new Promise((o,h)=>setTimeout(()=>h(new Error(`flush timeout after ${i}ms`)),i))]),{ok:!0,target:l,bodySize:u.byteLength}}catch(o){return c.lastError=(o?.message??String(o)).slice(0,500),{ok:!1,error:c.lastError,target:l,bodySize:u.byteLength}}}async shutdown(){const n=[...this.connections.values()];this.connections.clear(),await Promise.allSettled(n.map(async e=>{try{await e.nc.drain()}catch(s){console.warn(`[NatsDriver] shutdown drain error: ${s?.message??s}`)}}))}health(){const n={};let e=0,s=!1;for(const[t,r]of this.connections.entries())n[t]={connected:r.nc?.isClosed?.()===!1,host:r.host,subscriptions:r.refCount,...r.lastError?{lastError:r.lastError}:{}},e+=r.refCount,r.lastError&&(s=!0);return{protocol:"nats",ok:!this.initFailure&&!s,activeConnections:this.connections.size,subscriptions:e,...this.initFailure?{lastError:this.initFailure}:{},platforms:n}}}export{b as NatsDriver};
@@ -0,0 +1 @@
1
+ import{WebSocket as b}from"ws";import{getTrigger as k}from"../trigger-service";const h=1e3,p=3e4,y=500;function S(i){return!i||i.type==="none"?{}:i.type==="bearer"&&i.bearerToken?{Authorization:`Bearer ${i.bearerToken}`}:i.type==="basic"&&i.username?{Authorization:`Basic ${Buffer.from(`${i.username}:${i.password??""}`).toString("base64")}`}:i.type==="apiKey"&&i.apiKeyHeader&&i.apiKeyValue?{[i.apiKeyHeader]:i.apiKeyValue}:{}}class ${protocol="ws";connections=new Map;async init(){}scheduleReconnect(r,e){if(e.draining)return;const o=Math.random()*y,t=Math.min(e.backoffMs+o,p+y);setTimeout(()=>{e.draining||(e.backoffMs=Math.min(e.backoffMs*2,p),this.openSocket(r,e))},t).unref?.()}openSocket(r,e){if(!e.draining)try{const o=new b(e.host,{headers:S(e.broker.auth),handshakeTimeout:1e4});e.ws=o,o.on("open",()=>{e.lastConnectedAt=new Date().toISOString(),e.backoffMs=h,e.lastError=void 0,console.log(`[WebSocketDriver] connected to ${r} (${e.host})`);const t=e.outboundQueue.splice(0,e.outboundQueue.length);for(const n of t)try{o.send(n.payload),n.resolve({ok:!0,target:n.target,bodySize:Buffer.byteLength(n.payload,"utf8")})}catch(s){n.resolve({ok:!1,error:s?.message??String(s),target:n.target})}}),o.on("message",t=>{let n;try{n=JSON.parse(typeof t=="string"?t:t.toString("utf8"))}catch(u){console.warn(`[WebSocketDriver] bad JSON envelope from ${r}: ${u?.message}`);return}const s=n?.channel,a=n?.body,c=n?.headers??{};if(typeof s!="string"||a===void 0){console.warn(`[WebSocketDriver] envelope missing channel/body on ${r} \u2014 skipping`);return}const l=e.channels.get(s);if(l?.length)for(const{trigger:u,onMessage:d}of l)(async()=>{try{const g=await k(u.id)??u;if(!g.enabled)return;await d(g,{channel:s,payload:a,headers:c,source:`ws:${r}:${s}`})}catch(g){console.error(`[WebSocketDriver] dispatch failed for ${u.id} on ${s}: ${g?.message??g}`)}})().catch(()=>{})}),o.on("close",(t,n)=>{const s=`${t} ${n?.toString?.()??""}`.trim();e.draining||(e.lastError=`closed:${s}`.slice(0,500),console.warn(`[WebSocketDriver] socket closed for ${r} (${s}) \u2014 reconnecting in ${e.backoffMs}ms`),this.scheduleReconnect(r,e)),e.ws=null}),o.on("error",t=>{e.lastError=(t?.message??String(t)).slice(0,500),console.warn(`[WebSocketDriver] socket error for ${r}: ${e.lastError}`)})}catch(o){e.lastError=(o?.message??String(o)).slice(0,500),console.warn(`[WebSocketDriver] failed to open socket for ${r}: ${e.lastError}`),this.scheduleReconnect(r,e)}}getOrOpenConnection(r,e){let o=this.connections.get(r);if(o)return o;const t=String(e.host||"").trim();return o={host:t,broker:{...e,host:t},ws:null,channels:new Map,draining:!1,backoffMs:h,outboundQueue:[]},this.connections.set(r,o),this.openSocket(r,o),o}async subscribe(r,e,o){const t=e.inputTemplate?.asyncapi?.channel;if(!t)throw new Error(`trigger ${e.id} has no inputTemplate.asyncapi.channel`);const n=this.getOrOpenConnection(r.id,r.broker),s=n.channels.get(t)??[];return s.push({trigger:e,onMessage:o}),n.channels.set(t,s),{id:`ws:${r.id}:${e.id}`,platformId:r.id,triggerId:e.id,channel:t,unsubscribe:async()=>{const a=n.channels.get(t);if(a){const c=a.filter(l=>l.trigger.id!==e.id);c.length===0?n.channels.delete(t):n.channels.set(t,c)}n.channels.size===0&&n.outboundQueue.length===0&&(n.draining=!0,n.ws?.close(1e3,"no subscribers left"),this.connections.delete(r.id))}}}async publish(r){const{platformId:e,broker:o,channel:t,body:n,headers:s,timeoutMs:a}=r,c=this.getOrOpenConnection(e,o),l=JSON.stringify({channel:t,headers:s,body:(()=>{try{return JSON.parse(n)}catch{return n}})()}),u=`${o.host}#${t}`;if(c.ws&&c.ws.readyState===b.OPEN)try{return c.ws.send(l),{ok:!0,target:u,bodySize:Buffer.byteLength(l,"utf8")}}catch(d){return{ok:!1,error:d?.message??String(d),target:u,bodySize:Buffer.byteLength(l,"utf8")}}return new Promise(d=>{const g=setTimeout(()=>{const f=c.outboundQueue.findIndex(m=>m.resolve===d);f>=0&&c.outboundQueue.splice(f,1),d({ok:!1,error:`ws_publish_timeout_after_${a}ms_socket_not_ready`,target:u,bodySize:Buffer.byteLength(l,"utf8")})},a);g.unref?.(),c.outboundQueue.push({payload:l,target:u,resolve:f=>{clearTimeout(g),d(f)}})})}async shutdown(){const r=[...this.connections.values()];this.connections.clear();for(const e of r){e.draining=!0;try{e.ws?.close(1e3,"shutting down")}catch{}for(const o of e.outboundQueue)o.resolve({ok:!1,error:"shutdown",target:o.target});e.outboundQueue.length=0}}health(){const r={};let e=0,o=!1;for(const[t,n]of this.connections.entries()){const s=[...n.channels.values()].reduce((a,c)=>a+c.length,0);r[t]={connected:n.ws?.readyState===b.OPEN,host:n.host,subscriptions:s,...n.lastError?{lastError:n.lastError}:{}},e+=s,n.lastError&&(o=!0)}return{protocol:"ws",ok:!o,activeConnections:this.connections.size,subscriptions:e,platforms:r}}}export{$ as WebSocketDriver};
@@ -0,0 +1 @@
1
+ import{Client as c}from"@elastic/elasticsearch";import y from"node:crypto";import{config as p}from"../config";const i=new c(p.elasticsearch),r=".stkxp_subscribed_events";let o=!1;async function d(){if(!o)try{await i.indices.exists({index:r})||(await i.indices.create({index:r,body:{mappings:{properties:{id:{type:"keyword"},triggerId:{type:"keyword"},platformId:{type:"keyword"},channel:{type:"keyword"},payload:{type:"object",enabled:!1},payloadSize:{type:"integer"},receivedAt:{type:"date"},owner:{type:"keyword"},sourceIp:{type:"keyword"},dedupeKey:{type:"keyword"}}}}}),console.log(`[asyncapi-events] created index ${r}`)),o=!0}catch(e){console.error("[asyncapi-events] ensure index failed:",e)}}async function m(e){await d();const t=`evt_${Date.now()}_${y.randomBytes(4).toString("hex")}`,n={id:t,receivedAt:new Date().toISOString(),payloadSize:e.payloadSize??JSON.stringify(e.payload??null).length,...e};return await i.index({index:r,id:t,document:n,refresh:!1}),t}async function S(e,t,n=50){await d();try{const s=await i.search({index:r,size:Math.min(n,200),body:{sort:[{receivedAt:{order:"desc"}}],query:{bool:{must:[{term:{triggerId:e}},{term:{owner:t}}]}}}});return(s?.hits?.hits??s?.body?.hits?.hits??[]).map(a=>a._source)}catch{return[]}}export{r as SUBSCRIBED_EVENTS_INDEX,d as ensureSubscribedEventsIndex,S as listSubscribedEvents,m as recordSubscribedEvent};
@@ -0,0 +1 @@
1
+ import{Client as k}from"@elastic/elasticsearch";import{createHmac as b}from"crypto";import{config as _}from"../config";import{updateWebhookCall as p}from"./webhook-calls-service";import{driverManager as S}from"./asyncapi-drivers/driver-manager";const y=new k(_.elasticsearch),A=".stkxp_tools",R=".stkxp_platforms",d=256*1024,w=parseInt(process.env.ASYNCAPI_REPLY_TIMEOUT_MS??"10000",10);async function I(t,e,o){try{return(await y.search({index:A,size:1,body:{query:{bool:{must:[{term:{type:"asyncapi_publish"}},{term:{platformId:t}},{term:{channel:e}},{term:{owner:o}}]}}}}))?.hits?.hits?.[0]?._source??null}catch(i){return console.warn(`[AsyncApiReply] findPublishToolForChannel(${t}, ${e}) failed:`,i?.message),null}}async function T(t){try{return((await y.get({index:R,id:t}))._source??{})?.config?.broker??null}catch(e){return e?.meta?.statusCode===404||console.warn(`[AsyncApiReply] loadPlatformBroker(${t}) failed:`,e?.message),null}}function B(t){const e=JSON.stringify(t);if(Buffer.byteLength(e,"utf8")<=d||!t.blocks||t.blocks.length===0)return t;let o=1,i=t.blocks.length,r=0;for(;o<=i;){const s=o+i>>1,a={...t,blocks:[...t.blocks.slice(0,s),{type:"truncated_marker",omittedCount:t.blocks.length-s}]};Buffer.byteLength(JSON.stringify(a),"utf8")<=d?(r=s,o=s+1):i=s-1}return{...t,blocks:[...t.blocks.slice(0,r),{type:"truncated_marker",omittedCount:t.blocks.length-r}]}}async function E(t){const{trigger:e,webhookCallId:o,incomingDepth:i,runResult:r}=t,s=new Date().toISOString();if(!e.replyChannel)return;if(!e.replyHmacSecret){console.warn(`[AsyncApiReply] Trigger ${e.id} has replyChannel but no replyHmacSecret \u2014 skipping reply`),await p(o,{status:"completed_reply_failed",replyAttemptedAt:s,replyError:"missing_hmac_secret"});return}const a=e.inputTemplate?.asyncapi?.platformId;if(!a){console.warn(`[AsyncApiReply] Trigger ${e.id} has no inputTemplate.asyncapi.platformId \u2014 cannot resolve reply channel tool`),await p(o,{status:"completed_reply_failed",replyAttemptedAt:s,replyError:"missing_platform_id"});return}const g=i+1;let l=(await I(a,e.replyChannel,e.owner))?.broker;if(l||(l=await T(a)),!l?.host){console.warn(`[AsyncApiReply] No broker.host resolved for trigger ${e.id} channel ${e.replyChannel}`),await p(o,{status:"completed_reply_failed",replyAttemptedAt:s,replyError:"no_publish_tool_for_channel"});return}if(l.protocol&&l.protocol==="kafka"){console.warn('[AsyncApiReply] Broker protocol "kafka" not implemented yet \u2014 deferred to V2.6+'),await p(o,{status:"completed_reply_failed",replyAttemptedAt:s,replyError:`unsupported_protocol:${l.protocol}`});return}const m=B({runId:r.runId,subscriberId:e.id,channel:e.replyChannel,status:r.status,durationMs:r.durationMs,emittedAt:s,...r.blocks?.length?{blocks:r.blocks}:{},...r.errorType?{errorType:r.errorType}:{},...r.errorMessage?{errorMessage:r.errorMessage}:{},...r.conversationId?{conversationId:r.conversationId}:{}}),c=JSON.stringify(m),u=Buffer.byteLength(c,"utf8"),f=b("sha256",e.replyHmacSecret).update(c,"utf8").digest("hex"),h={"Content-Type":"application/json",Accept:"application/json","X-Stkxp-Reply-RunId":r.runId,"X-Stkxp-Reply-Subscriber":e.id,"X-Stkxp-Signature":`sha256=${f}`,"X-Stkxp-Webhook-Depth":String(g),...r.conversationId?{"X-Stkxp-Reply-ConversationId":r.conversationId}:{}},n=await S.publish({platformId:a,broker:l,channel:e.replyChannel,body:c,headers:h,timeoutMs:w});if(!n.ok){console.warn(`[AsyncApiReply] Reply send failed via ${l.protocol} for ${e.id} \u2192 ${n.target}: ${n.error}`),await p(o,{status:"completed_reply_failed",replyTargetUrl:n.target,replyAttemptedAt:s,...n.httpStatus!==void 0?{replyHttpStatus:n.httpStatus}:{},replyBodySize:n.bodySize??u,replyError:(n.error??"unknown").slice(0,500)});return}console.log(`[AsyncApiReply] Reply sent via ${l.protocol} for ${e.id} \u2192 ${n.target} (${n.bodySize??u}B${n.httpStatus?`, HTTP ${n.httpStatus}`:""})`),await p(o,{status:"completed_reply_sent",replyTargetUrl:n.target,replyAttemptedAt:s,...n.httpStatus!==void 0?{replyHttpStatus:n.httpStatus}:{},replyBodySize:n.bodySize??u})}export{E as publishReply};
@@ -0,0 +1 @@
1
+ import{createHmac as h}from"crypto";import{driverManager as c}from"./asyncapi-drivers/driver-manager";import{updateWebhookCall as m}from"./webhook-calls-service";const d=parseInt(process.env.ASYNCAPI_REPLY_TIMEOUT_MS??"10000",10),u=50,p=64*1024;class b{name="AsyncApiSink";trigger;broker;incomingDepth;webhookCallId;batchMs;platformId;replyChannel;hmacSecret;meta=null;batchTimer=null;batchBuffer=[];seq=0;endStats=null;constructor(t){this.trigger=t.trigger,this.broker=t.broker,this.incomingDepth=t.incomingDepth,this.webhookCallId=t.webhookCallId,this.batchMs=Math.max(0,t.trigger.streamingBatchMs??u),this.platformId=t.trigger.inputTemplate?.asyncapi?.platformId??"",this.replyChannel=t.trigger.replyChannel??"",this.hmacSecret=t.trigger.replyHmacSecret??""}static validate(t){return t.streamingEnabled?t.replyChannel?t.replyHmacSecret?{ok:!0}:{ok:!1,reason:"no_hmac_secret"}:{ok:!1,reason:"no_reply_channel"}:{ok:!1,reason:"streaming_not_enabled"}}async runStart(t){this.meta=t,this.seq=0}async onAssistantStart(t){await this.flushBatch(),await this.emitStreamFrame("assistant_start",{assistantName:t.assistantName,assistantIndex:t.assistantIndex,totalAssistants:t.totalAssistants,tools:t.tools??[],ts:t.ts})}async onMarkdownToken(t){if(!t.text)return;if(this.batchBuffer.push(t),this.estimateBatchSize()>p){await this.flushBatch();return}!this.batchTimer&&this.batchMs>0?(this.batchTimer=setTimeout(()=>{this.flushBatch().catch(a=>console.warn(`[AsyncApiSink] batch flush error: ${a?.message??a}`))},this.batchMs),this.batchTimer.unref?.()):this.batchMs===0&&await this.flushBatch()}async onToolStart(t){await this.flushBatch(),await this.emitStreamFrame("tool_start",{toolName:t.toolName,toolInput:t.toolInput,assistantName:t.assistantName,ts:t.ts})}async onToolEnd(t){await this.flushBatch(),await this.emitStreamFrame("tool_end",{toolName:t.toolName,toolResult:t.toolResult,durationMs:t.durationMs,assistantName:t.assistantName,ts:t.ts})}async onBlock(t){await this.flushBatch(),await this.emitStreamFrame("block",{block:t.block,assistantName:t.assistantName,ts:t.ts})}async onTokenUsage(t){await this.emitStreamFrame("token_usage",{inputTokens:t.inputTokens,outputTokens:t.outputTokens,totalTokens:t.totalTokens,modelId:t.modelId,provider:t.provider,assistantName:t.assistantName,ts:t.ts})}async onAssistantEnd(t){await this.flushBatch(),await this.emitStreamFrame("assistant_end",{assistantName:t.assistantName,success:t.success,durationMs:t.durationMs,inputTokens:t.inputTokens,outputTokens:t.outputTokens,toolCallsCount:t.toolCallsCount,ts:t.ts,partial:!0})}async onError(t){await this.flushBatch(),await this.emitStreamFrame("error",{errorType:t.errorType,errorMessage:t.errorMessage,ts:t.ts})}async flush(){await this.flushBatch()}async runEnd(t){if(await this.flushBatch(),!this.meta){console.warn("[AsyncApiSink] runEnd called without runStart \u2014 skipping canonical emit");return}this.endStats=t;const s=new Date().toISOString(),a=this.incomingDepth+1,r={runId:this.meta.runId,subscriberId:this.trigger.id,channel:this.replyChannel,status:t.success?"success":"error",durationMs:t.durationMs,emittedAt:s,...t.blocks?.length?{blocks:t.blocks}:{},...t.errorType?{errorType:t.errorType}:{},...t.errorMessage?{errorMessage:t.errorMessage}:{},...this.meta.conversationId?{conversationId:this.meta.conversationId}:{}},n=JSON.stringify(r),i=Buffer.byteLength(n,"utf8"),l=h("sha256",this.hmacSecret).update(n,"utf8").digest("hex"),o={"Content-Type":"application/json",Accept:"application/json","X-Stkxp-Reply-RunId":this.meta.runId,"X-Stkxp-Reply-Subscriber":this.trigger.id,"X-Stkxp-Signature":`sha256=${l}`,"X-Stkxp-Webhook-Depth":String(a),"X-Stkxp-Frame":"assistant_end","X-Stkxp-Seq":String(++this.seq),...this.meta.conversationId?{"X-Stkxp-Reply-ConversationId":this.meta.conversationId}:{}},e=await c.publish({platformId:this.platformId,broker:this.broker,channel:this.replyChannel,body:n,headers:o,timeoutMs:d});if(!e.ok){console.warn(`[AsyncApiSink] canonical assistant_end send failed for ${this.trigger.id} \u2192 ${e.target}: ${e.error}`),await m(this.webhookCallId,{status:"completed_reply_failed",replyTargetUrl:e.target,replyAttemptedAt:s,...e.httpStatus!==void 0?{replyHttpStatus:e.httpStatus}:{},replyBodySize:e.bodySize??i,replyError:(e.error??"unknown").slice(0,500)});return}console.log(`[AsyncApiSink] canonical assistant_end sent via ${this.broker.protocol} for ${this.trigger.id} \u2192 ${e.target} (${e.bodySize??i}B, ${this.seq} frames total)`),await m(this.webhookCallId,{status:"completed_reply_sent",replyTargetUrl:e.target,replyAttemptedAt:s,...e.httpStatus!==void 0?{replyHttpStatus:e.httpStatus}:{},replyBodySize:e.bodySize??i})}estimateBatchSize(){let t=0;for(const s of this.batchBuffer)t+=Buffer.byteLength(s.text,"utf8");return t}async flushBatch(){if(this.batchTimer&&(clearTimeout(this.batchTimer),this.batchTimer=null),this.batchBuffer.length===0)return;const t=this.batchBuffer;this.batchBuffer=[];const s=t.map(r=>r.text),a=t[t.length-1].assistantName;await this.emitStreamFrame("markdown_token_batch",{tokens:s,text:s.join(""),assistantName:a,tokenCount:s.length,ts:t[t.length-1].ts})}async emitStreamFrame(t,s){if(!this.meta)return;const a={type:t,runId:this.meta.runId,subscriberId:this.trigger.id,seq:++this.seq,...this.meta.conversationId?{conversationId:this.meta.conversationId}:{},...s},r=JSON.stringify(a),n=Buffer.byteLength(r,"utf8"),i=h("sha256",this.hmacSecret).update(r,"utf8").digest("hex"),l={"Content-Type":"application/json","X-Stkxp-Reply-RunId":this.meta.runId,"X-Stkxp-Reply-Subscriber":this.trigger.id,"X-Stkxp-Stream-Signature":`sha256=${i}`,"X-Stkxp-Webhook-Depth":String(this.incomingDepth+1),"X-Stkxp-Frame":t,"X-Stkxp-Seq":String(this.seq),...this.meta.conversationId?{"X-Stkxp-Reply-ConversationId":this.meta.conversationId}:{}},o=await c.publish({platformId:this.platformId,broker:this.broker,channel:this.replyChannel,body:r,headers:l,timeoutMs:d});o.ok||console.warn(`[AsyncApiSink] stream frame "${t}" send failed (${n}B): ${o.error}`)}}export{b as AsyncApiSink};
@@ -0,0 +1 @@
1
+ import{Client as O}from"@elastic/elasticsearch";import C from"node:crypto";import B from"js-yaml";import{config as E}from"../config";import{getPlatformById as x}from"./platforms-service";import{createTrigger as v}from"./trigger-service";const A=new O(E.elasticsearch),P=".stkxp_tools",_=".stkxp_triggers";function R(e,r){const o=e.host.replace(/\/+$/,""),a=e.channelStrategy??"path";if(a==="path")return`${o}/${encodeURIComponent(r)}`;if(a==="query"){const n=new URL(o);return n.searchParams.set("channel",r),n.toString()}return o}function U(e){return!e||e.type==="none"?{type:"none"}:e.type==="bearer"&&e.bearerToken?{type:"bearer",token:e.bearerToken}:e.type==="basic"?{type:"basic",username:e.username,password:e.password}:e.type==="apiKey"&&e.apiKeyHeader&&e.apiKeyValue?{type:"apiKey",headerName:e.apiKeyHeader,apiKey:e.apiKeyValue}:{type:"none"}}function j(e,r){if(typeof r!="string"||!r.startsWith("#/"))return;const o=r.slice(2).split("/").map(n=>n.replace(/~1/g,"/").replace(/~0/g,"~"));let a=e;for(const n of o){if(a==null||typeof a!="object")return;a=a[n]}return a}function S(e){return!!e&&typeof e=="object"&&"$ref"in e&&typeof e.$ref=="string"}function b(e,r,o=10){let a=r;for(;a&&o-- >0;){if(!S(a))return a;const n=j(e,a.$ref);if(n===void 0)return;a=n}return a}function $(e){return e.replace(/[^a-zA-Z0-9_]+/g,"_").replace(/^_+|_+$/g,"").slice(0,64).toLowerCase()||"tool"}function N(e){if(!e||typeof e!="object")throw new Error("Spec must be a parsed object");if(!e.asyncapi||!String(e.asyncapi).startsWith("3."))throw new Error(`Only AsyncAPI 3.x supported (received: ${e.asyncapi??"<none>"})`);if(!e.operations||typeof e.operations!="object")throw new Error("Spec has no `operations` section");const r=[];for(const[o,a]of Object.entries(e.operations)){const n=b(e,a);if(!n)continue;const d=n.action==="send"?"publish":n.action==="receive"?"subscribe":null;if(!d)continue;const y=b(e,n.channel);let g;if(y&&typeof y=="object"&&(g=y.address??void 0),!g&&S(n.channel)&&(g=n.channel.$ref.split("/").pop()),!g){r.push({kind:d,operationId:o,channel:"UNKNOWN",summary:"(channel could not be resolved)"});continue}const f=Array.isArray(n.messages)?n.messages:[];let u,p;if(f.length>0){const m=b(e,f[0]);if(m){u=S(f[0])?f[0].$ref.split("/").pop():m.name;let i=b(e,m.payload);i&&typeof i=="object"&&"schema"in i&&("schemaFormat"in i||"contentType"in i||Object.keys(i).length<=3)&&(i=b(e,i.schema)),i&&typeof i=="object"&&(p=i)}}r.push({kind:d,operationId:o,channel:g,messageName:u,summary:n.summary,description:n.description,tags:Array.isArray(n.tags)?n.tags.map(m=>m?.name).filter(Boolean):void 0,payloadSchema:p})}return r}function q(e){return N(e).filter(r=>r.kind==="publish").map(({kind:r,...o})=>o)}function z(e){return N(e).filter(r=>r.kind==="subscribe").map(({kind:r,...o})=>o)}async function L(e){const{platformId:r,owner:o}=e,a=await x(r,o,!0,!0);if(!a)throw new Error("Platform not found");if(a.type!=="AsyncAPI")throw new Error(`Platform ${r} is not of type AsyncAPI (got: ${a.type})`);const n=a.config;if(!n?.broker?.host)throw new Error("Platform.config.broker.host is required to generate tools");let d;if(e.specBody)d=e.specBody;else if(e.specUrl||n.specUrl){const t=e.specUrl??n.specUrl,c=await fetch(t);if(!c.ok)throw new Error(`Failed to fetch spec from ${t}: HTTP ${c.status}`);d=await c.text()}else if(n.specBody)d=n.specBody;else throw new Error("Provide specBody or specUrl (or set one on the platform)");let y;try{y=B.load(d)}catch(t){throw new Error(`Unable to parse spec: ${t?.message??t}`)}if(!y||typeof y!="object")throw new Error("Parsed spec is not an object");const g=N(y),f=g.filter(t=>t.kind==="publish"),u=g.filter(t=>t.kind==="subscribe");try{await A.deleteByQuery({index:P,refresh:!0,body:{query:{bool:{must:[{term:{platformId:r}},{term:{owner:o}},{term:{type:"asyncapi_publish"}}]}}}})}catch{}const p=new Map;try{const t=await A.search({index:_,size:500,body:{query:{bool:{must:[{term:{owner:o}},{term:{type:"webhook"}},{prefix:{id:`asyncapi_sub_${r}_`}}]}},_source:["id","targetTeamId","replyChannel","replyHmacSecret","streamingEnabled","streamingBatchMs"]}});for(const c of t?.hits?.hits??[]){const s=c._source;if(!s?.id)continue;const l={};s.targetTeamId&&(l.targetTeamId=s.targetTeamId),s.replyChannel&&(l.replyChannel=s.replyChannel),s.replyHmacSecret&&(l.replyHmacSecret=s.replyHmacSecret),s.streamingEnabled!==void 0&&(l.streamingEnabled=!!s.streamingEnabled),typeof s.streamingBatchMs=="number"&&(l.streamingBatchMs=s.streamingBatchMs),Object.keys(l).length>0&&p.set(s.id,l)}if(p.size>0){const c={target:[...p.values()].filter(s=>s.targetTeamId).length,reply:[...p.values()].filter(s=>s.replyChannel).length,secret:[...p.values()].filter(s=>s.replyHmacSecret).length,streaming:[...p.values()].filter(s=>s.streamingEnabled).length};console.log(`[asyncapi/import] Preserving subscriber bindings on ${p.size} re-imported trigger(s) (targets: ${c.target}, replyChannels: ${c.reply}, hmacSecrets: ${c.secret}, streaming: ${c.streaming})`)}}catch{}try{await A.deleteByQuery({index:_,refresh:!0,body:{query:{bool:{must:[{term:{owner:o}},{term:{type:"webhook"}},{prefix:{id:`asyncapi_sub_${r}_`}}]}}}})}catch{}const m=new Date().toISOString(),i=[],h=[],w=[],I=[];for(const t of f){if(t.channel==="UNKNOWN"||!t.payloadSchema){h.push(`${t.operationId}: ${t.channel==="UNKNOWN"?"channel could not be resolved":"no payload schema"}`);continue}const c=`asyncapi_${r}_${$(t.operationId)}`,s=$(t.operationId),l=R(n.broker,t.channel),k={name:s,label:t.summary??t.operationId,description:t.description??`Publish a ${t.messageName??"message"} to the \`${t.channel}\` channel.`,type:"asyncapi_publish",enabled:!0,tags:t.tags,owner:o,platformId:r,platformName:a.name,endpoint:l,httpMethod:"POST",authConfig:U(n.broker.auth),channel:t.channel,operationId:t.operationId,messageRef:t.messageName,inputSchema:JSON.stringify(t.payloadSchema),broker:n.broker,asyncApiMeta:JSON.stringify({channel:t.channel,operationId:t.operationId,messageName:t.messageName}),createdAt:m,updatedAt:m,createdBy:o,updatedBy:o};i.push({index:{_index:P,_id:c}},k),w.push(s)}i.length>0&&await A.bulk({body:i,refresh:"wait_for"});for(const t of u){if(t.channel==="UNKNOWN"){h.push(`${t.operationId}: channel could not be resolved (subscribe)`);continue}t.payloadSchema||h.push(`${t.operationId}: no payload schema (subscribe \u2014 trigger created anyway)`);const c=$(t.operationId),s=`asyncapi_sub_${r}_${c}`,l=C.randomBytes(16).toString("hex"),k={id:s,name:t.summary??t.operationId,description:t.description??`AsyncAPI subscriber for \`${t.channel}\` (message: ${t.messageName??"unknown"}).`,type:"webhook",target:"",...p.get(s)??{},webhookSecret:l,inputTemplate:{asyncapi:{platformId:r,platformName:a.name,channel:t.channel,operationId:t.operationId,messageRef:t.messageName,payloadSchema:t.payloadSchema,tags:t.tags}},enabled:!0,owner:o};try{await v(k),I.push(s)}catch(T){h.push(`${t.operationId}: trigger create failed \u2014 ${T?.message??T}`)}}return{created:w.length,subscribers:I.length,skipped:f.length-w.length+(u.length-I.length),toolNames:w,triggerIds:I,skippedReasons:h}}export{N as generateOperationSpecs,q as generatePublishToolSpecs,z as generateSubscribeTriggerSpecs,L as importAsyncApiSpec};
@@ -0,0 +1,13 @@
1
+ import{resolveLlmClientForAssistant as l}from"./llm-client-resolver";class o extends Error{}function p(e,r){return`You are repairing a malformed UI block from a chat response.
2
+
3
+ The block below rendered incorrectly (e.g. missing or invalid data), even though it did not throw an error. Using the context from the rest of the message below (which may contain the information the block should have visualized \u2014 dates, names, values, etc.), return a corrected version of ONLY this block.
4
+
5
+ Rules:
6
+ - Keep "type" exactly as "${e.type}". Do not change it.
7
+ - Return ONLY the corrected block as a single JSON object. No prose, no markdown code fences, no explanation.
8
+
9
+ Malformed block:
10
+ ${JSON.stringify(e,null,2)}
11
+
12
+ Rest of the message (context):
13
+ ${JSON.stringify(r,null,2)}`}function d(e){return e.trim().replace(/^```(?:json)?\s*/i,"").replace(/```\s*$/i,"").replace(/,\s*",\s*/g,'",').replace(/,\s*,+/g,",").replace(/,(\s*[}\]])/g,"$1").replace(/"\s*:\s*"/g,'":"').trim()}async function m({block:e,otherBlocks:r,assistantId:a,owner:s}){const n=await(await l(a,s)).invoke(p(e,r)),i=typeof n.content=="string"?n.content:String(n.content),c=d(i);let t;try{t=JSON.parse(c)}catch{throw new o("The AI did not return valid JSON for the repaired block.")}if(!t||typeof t!="object"||t.type!==e.type)throw new o(`The AI returned a block of type "${t?.type??"unknown"}", expected "${e.type}".`);return t}export{o as BlockRepairError,m as repairBlock};
@@ -0,0 +1 @@
1
+ import*as u from"echarts";import{createRequire as h}from"module";const f=h(import.meta.url);let s=null;try{s=f("canvas").createCanvas,console.log("Canvas module loaded successfully")}catch(a){console.warn("Canvas module not available, chart rendering disabled",a)}class C{static async renderChartToBase64(r,o={}){const{width:t=800,height:n=400,backgroundColor:c="#ffffff"}=o;try{if(!s)throw new Error("Canvas module not available - chart rendering disabled");const e=s(t,n),i=u.init(e),l={...r,backgroundColor:c,animation:!1};i.setOption(l);const d=`data:image/png;base64,${e.toBuffer("image/png").toString("base64")}`;return i.dispose(),d}catch(e){throw console.error("Chart rendering error:",e),new Error(`Failed to render chart: ${e instanceof Error?e.message:"Unknown error"}`)}}static async renderMultipleCharts(r){return await Promise.all(r.map(({options:t,renderOptions:n})=>this.renderChartToBase64(t,n)))}static validateChartOptions(r){return!r||typeof r!="object"?!1:!!(r.series||r.dataset||r.xAxis||r.yAxis)}}export{C as ChartRenderer};
@@ -0,0 +1 @@
1
+ import{Client as i}from"@elastic/elasticsearch";import{config as d}from"../config";const s=new i(d.elasticsearch),n=".stkxp_chat_llms";async function o(){try{if(!await s.indices.exists({index:n}))await s.indices.create({index:n,body:{settings:{number_of_shards:1,number_of_replicas:1},mappings:{properties:{chatId:{type:"keyword"},runId:{type:"keyword"},username:{type:"keyword"},teamId:{type:"keyword"},assistantId:{type:"keyword"},assistantName:{type:"keyword"},llms:{type:"nested",properties:{callIndex:{type:"integer"},callId:{type:"keyword"},assistantId:{type:"keyword"},assistantName:{type:"keyword"},graphId:{type:"keyword"},nodeId:{type:"keyword"},provider:{type:"keyword"},modelId:{type:"keyword"},input:{type:"object",enabled:!1},output:{type:"object",enabled:!1},timestamp:{type:"date"},status:{type:"keyword"},startTime:{type:"long"},endTime:{type:"long"},duration:{type:"long"},error:{type:"boolean"},empty:{type:"boolean"},inputTokens:{type:"long"},outputTokens:{type:"long"},totalTokens:{type:"long"},cacheCreationTokens:{type:"long"},cacheReadTokens:{type:"long"}}},createdAt:{type:"date"}}}}}),console.log(`Created index ${n}`);else try{await s.indices.putMapping({index:n,body:{properties:{llms:{type:"nested",properties:{cacheCreationTokens:{type:"long"},cacheReadTokens:{type:"long"}}}}}})}catch(e){String(e?.message||"").includes("mapper_parsing_exception")||console.warn(`[chat-llms] putMapping warning: ${e?.message}`)}}catch(t){throw console.error("Error initializing chat llms index:",t),t}}async function l(t){try{await o();const e=new Date().toISOString(),r={...t,createdAt:t.createdAt||e},a=await s.index({index:n,body:r,refresh:"true"});return{...r,id:a._id}}catch(e){throw console.error("Error creating chat llms record:",e),e}}async function y(t){try{await o();const e=await s.search({index:n,body:{query:{bool:{must:[{term:{runId:t}}]}},size:1}});if(e.hits.hits.length===0)return null;const r=e.hits.hits[0];return{id:r._id,...r._source}}catch(e){return console.error("Error getting llms by run ID:",e),null}}async function m(t){try{return await o(),(await s.search({index:n,body:{query:{bool:{must:[{term:{chatId:t}}]}},sort:[{createdAt:{order:"asc",unmapped_type:"date"}}],size:100}})).hits.hits.map(r=>({id:r._id,...r._source}))}catch(e){return console.error("Error getting llms by chat ID:",e),[]}}async function u(t){try{return await o(),await s.deleteByQuery({index:n,body:{query:{bool:{must:[{term:{chatId:t}}]}}},refresh:!0}),!0}catch(e){return console.error("Error deleting llms by chat ID:",e),!1}}export{l as createChatLLMsRecord,u as deleteLLMsByChatId,m as getLLMsByChatId,y as getLLMsByRunId};
@@ -0,0 +1 @@
1
+ import{Client as p}from"@elastic/elasticsearch";import{config as g}from"../config";const i=new p(g.elasticsearch),a=".stkxp_chat_runs";function _(r){const n=r?.message||(typeof r=="string"?r:"")||"Unknown error",s=r?.status??r?.statusCode,t=r?.code,e=n.toLowerCase();let o="unknown";const u=r?.errorType;return typeof u=="string"?{errorType:u,errorMessage:n}:(t==="ABORT_ERR"||e.includes("aborted")?o="abort":t==="ETIMEDOUT"||e.includes("timeout")||e.includes("timed out")?o="timeout":s===429||e.includes("rate limit")||e.includes("too many requests")?o="llm_rate_limit":e.includes("context length")||e.includes("maximum context")||e.includes("token limit")||e.includes("context_length_exceeded")?o="llm_context_overflow":s===401||s===403||e.includes("invalid api key")||e.includes("authentication")?o="llm_auth":e.includes("unreachable_node")||e.includes("graph_recursion")||e.includes("recursion limit")||e.includes("langgraph")?o="graph_error":e.includes("tool ")&&(e.includes("failed")||e.includes("error"))?o="tool_error":(e.includes("anthropic")||e.includes("openai")||e.includes("gemini")||e.includes("llm"))&&(o="llm_other"),{errorType:o,errorMessage:n.slice(0,1e3)})}async function c(){try{if(!await i.indices.exists({index:a}))await i.indices.create({index:a,body:{mappings:{properties:{runId:{type:"keyword"},chatId:{type:"keyword"},username:{type:"keyword"},teamId:{type:"keyword"},teamName:{type:"keyword"},assistantId:{type:"keyword"},assistantName:{type:"keyword"},status:{type:"keyword"},startTime:{type:"date"},endTime:{type:"date"},duration:{type:"long"},topic:{type:"keyword"},source:{type:"keyword"},cluster:{type:"keyword"},namespace:{type:"keyword"},packageName:{type:"keyword"},graphId:{type:"keyword"},llmUsage:{type:"nested",properties:{provider:{type:"keyword"},model:{type:"keyword"},inputTokens:{type:"long"},outputTokens:{type:"long"},totalTokens:{type:"long"}}},totalTokens:{type:"long"},toolsUsed:{type:"keyword"},toolCallsCount:{type:"integer"},messageCount:{type:"integer"},estimatedCost:{type:"double"},metadata:{type:"object",enabled:!1},errorMessage:{type:"text"},errorType:{type:"keyword"},errorNodeId:{type:"keyword"},createdAt:{type:"date"},updatedAt:{type:"date"}}},settings:{number_of_shards:1,number_of_replicas:1}}}),console.log(`Created index: ${a}`);else try{await i.indices.putMapping({index:a,body:{properties:{errorMessage:{type:"text"},errorType:{type:"keyword"},errorNodeId:{type:"keyword"}}}})}catch(n){String(n?.message||"").includes("mapper_parsing_exception")||console.warn(`[chat-runs] putMapping warning: ${n?.message}`)}}catch(r){throw console.error("Error initializing chat runs index:",r),r}}async function k(r,n,s,t){try{await c();const e=new Date().toISOString(),o=t?.assistantId??"",d=typeof o=="string"&&/(?:^|-)[\w]+-generator$/.test(o)?"generator":t?.source==="playground"?"playground":"chat",l={runId:r,chatId:n,username:s,teamId:t?.teamId||"unknown",teamName:t?.teamName||"unknown",assistantId:t?.assistantId||"unknown",assistantName:t?.assistantName||"unknown",status:"running",startTime:e,source:d,userMessage:typeof t?.userMessage=="string"?t.userMessage.slice(0,2e3):void 0,topic:t?.topic,cluster:t?.cluster,namespace:t?.namespace,packageName:t?.packageName,graphId:t?.graphId,metadata:t,createdAt:e,updatedAt:e};return{id:(await i.index({index:a,body:l,refresh:"wait_for"}))._id,...l}}catch(e){throw console.error("Error creating chat run:",e),e}}async function y(r,n){try{await c();const s=await i.search({index:a,body:{query:{bool:{must:[{term:{runId:r}}]}},size:1}});if(s.hits.hits.length===0)return console.warn(`Chat run not found: ${r}`),!1;const t=s.hits.hits[0]._id;return await i.update({index:a,id:t,body:{doc:{...n,updatedAt:new Date().toISOString()}},refresh:"wait_for"}),!0}catch(s){return console.error("Error updating chat run:",s),!1}}async function b(r,n,s){try{const t=new Date().toISOString(),e=await m(r);let o;if(e){const u=new Date(e.startTime).getTime();o=new Date(t).getTime()-u}return await y(r,{status:n,endTime:t,duration:o,...s})}catch(t){return console.error("Error completing chat run:",t),!1}}async function m(r){try{await c();const n=await i.search({index:a,body:{query:{term:{runId:r}},size:1}});if(n.hits.hits.length===0)return null;const s=n.hits.hits[0];return{id:s._id,...s._source}}catch(n){return console.error("Error getting chat run:",n),null}}async function x(r=0,n=50,s){try{await c();let t={match_all:{}};s&&(t={term:{username:s}});const e=await i.search({index:a,body:{query:t,from:r,size:n,sort:[{startTime:"desc"}]}}),o=e.hits.hits.map(d=>({id:d._id,...d._source})),u=typeof e.hits.total=="number"?e.hits.total:e.hits.total?.value||0;return{runs:o,total:u}}catch(t){return console.error("Error getting chat runs:",t),{runs:[],total:0}}}async function C(r,n,s){try{await c();const t=[{range:{startTime:{gte:r,lte:n}}}];return s&&t.push({term:{username:s}}),(await i.search({index:a,body:{query:{bool:{must:t}},size:1e3,sort:[{startTime:"desc"}]}})).hits.hits.map(o=>({id:o._id,...o._source}))}catch(t){return console.error("Error getting chat runs by time range:",t),[]}}async function T(r){try{await c();const n=await i.search({index:a,body:{query:{term:{runId:r}},size:1}});if(n.hits.hits.length===0)return!1;const s=n.hits.hits[0]._id;return await i.delete({index:a,id:s,refresh:"wait_for"}),!0}catch(n){return console.error("Error deleting chat run:",n),!1}}export{_ as classifyError,b as completeChatRun,k as createChatRun,T as deleteChatRun,m as getChatRunByRunId,x as getChatRuns,C as getChatRunsByTimeRange,y as updateChatRun};
@@ -0,0 +1 @@
1
+ import{Client as p}from"@elastic/elasticsearch";import{config as u}from"../config";const n=new p(u.elasticsearch),i=".stkxp_chat_tools",d=".stkxp_tools";async function m(t){const e=new Map;if(t.length===0)return e;try{(await n.search({index:d,body:{query:{terms:{name:t}},_source:["name","description","label"],size:t.length}})).hits.hits.forEach(r=>{const s=r._source;s.name&&s.description&&e.set(s.name,s.description)}),console.log(`[ChatToolsService] Loaded ${e.size} tool descriptions from ${t.length} tools`)}catch(o){console.error("[ChatToolsService] Error loading tool descriptions:",o)}return e}async function f(){const t=new Map;try{const e=await n.search({index:d,body:{query:{match_all:{}},_source:["name","description","label","topic","system","destructiveHint"],size:1e4}});e.hits.hits.forEach(o=>{const r=o._source;if(r.name){const s={name:r.name,description:r.description||r.label||r.name,label:r.label,topic:r.topic,system:r.system,destructiveHint:r.destructiveHint===!0};t.set(r.name,s);const l=r.name.split("_");if(l.length>1){const c=l[l.length-1];c.length>=5&&c.length<=6&&/^[a-z0-9]+$/.test(c)&&t.set(c,s)}}}),console.log(`[ChatToolsService] Loaded ${e.hits.hits.length} tools with ${t.size} total mappings (full names + short IDs)`)}catch(e){console.error("[ChatToolsService] Error loading tools metadata:",e)}return t}async function a(){try{if(!await n.indices.exists({index:i}))await n.indices.create({index:i,body:{settings:{number_of_shards:1,number_of_replicas:1},mappings:{properties:{chatId:{type:"keyword"},runId:{type:"keyword"},username:{type:"keyword"},assistantId:{type:"keyword"},assistantName:{type:"keyword"},tools:{type:"nested",properties:{name:{type:"keyword"},input:{type:"object",enabled:!1},output:{type:"object",enabled:!1},timestamp:{type:"date"},mcpServerId:{type:"keyword"}}},metadata:{properties:{cluster:{type:"keyword"},topic:{type:"keyword"},llmProvider:{type:"keyword"},llmModel:{type:"keyword"}}},createdAt:{type:"date"}}}}}),console.log(`Created index ${i}`);else try{await n.indices.putMapping({index:i,body:{properties:{chatId:{type:"keyword"},runId:{type:"keyword"}}}})}catch(e){e.message?.includes("illegal_argument_exception")||console.warn("[chat-tools] Could not update mapping:",e.message)}}catch(t){throw console.error("Error initializing chat tools index:",t),t}}function y(t){return t==null?null:typeof t=="string"?t:JSON.stringify(t)}async function w(t){try{await a();const e=new Date().toISOString(),o={...t,tools:t.tools.map(s=>({...s,output:y(s.output)})),createdAt:e},r=await n.index({index:i,body:o,refresh:"true"});return{...o,id:r._id}}catch(e){throw console.error("Error creating chat tools record:",e),e}}async function x(t){try{return await a(),(await n.search({index:i,body:{query:{bool:{must:[{term:{chatId:t}}]}},sort:[{createdAt:{order:"asc",unmapped_type:"date"}}],size:100}})).hits.hits.map(o=>({id:o._id,...o._source}))}catch(e){return console.error("Error getting tools by chat ID:",e),[]}}async function b(t){try{await a();const e=await n.search({index:i,body:{query:{bool:{must:[{term:{runId:t}}]}},size:1}});if(console.log(`[ChatToolsService] getToolsByRunId(${t}): ${e.hits.hits.length} hit(s), total=${JSON.stringify(e.hits.total)}`),e.hits.hits.length===0)return null;const o=e.hits.hits[0];return{id:o._id,...o._source}}catch(e){return console.error("Error getting tools by run ID:",e),null}}async function T(t,e){try{await a();const o=await n.search({index:i,body:{query:{bool:{must:[{term:{runId:t}}]}},size:1}});if(o.hits.hits.length===0)return!1;const r=o.hits.hits[0]._id,s=o.hits.hits[0]._source.tools||[];return await n.update({index:i,id:r,body:{doc:{tools:[...s,...e]}},refresh:"true"}),!0}catch(o){return console.error("Error updating chat tools record:",o),!1}}async function _(t){try{return await a(),await n.deleteByQuery({index:i,body:{query:{bool:{must:[{term:{chatId:t}}]}}},refresh:!0}),!0}catch(e){return console.error("Error deleting tools by chat ID:",e),!1}}export{w as createChatToolsRecord,_ as deleteToolsByChatId,x as getToolsByChatId,b as getToolsByRunId,a as initializeChatToolsIndex,f as loadAllToolsMetadata,m as loadToolDescriptions,T as updateChatToolsRecord};
@@ -0,0 +1 @@
1
+ import{Client as y}from"@elastic/elasticsearch";import{config as _}from"../config";const i=new y({..._.elasticsearch,requestTimeout:6e4,maxRetries:0}),a=".stkxp_chat_traces",u=6e4;let l=!1;const h=new Set(["assistant_start","assistant_end","node_start","node_end","tool_start","tool_end","hitl_interrupt","hitl_resumed"]);function b(t){if(!t||!h.has(t.type))return null;const n=t.ts??Date.now();switch(t.type){case"assistant_start":return{kind:"assistant_start",ts:n,assistantName:t.assistantName||"Assistant",assistantIndex:t.assistantIndex??0,totalAssistants:t.totalAssistants??1,tools:t.tools||[]};case"assistant_end":return{kind:"assistant_end",ts:n,assistantName:t.assistantName||"unknown",success:t.success??!0,error:t.success===!1?t.error:void 0,durationMs:t.stats?.duration,toolCallsCount:t.stats?.toolsCount,output:typeof t.output=="string"&&t.output.length>0?t.output:void 0};case"node_start":return{kind:"node_start",ts:n,node:t.node||"unknown"};case"node_end":{const e=t.nodeOutput;return{kind:"node_end",ts:n,node:t.node||"unknown",durationMs:t.durationMs,nodeOutput:e?String(e).slice(0,300)+(String(e).length>300?"\u2026":""):void 0,consoleLogs:t.consoleLogs,stats:t.sandboxStats,script:typeof t.script=="string"?t.script.slice(0,5e3)+(t.script.length>5e3?"\u2026":""):void 0,codeExecTrace:t.codeExecTrace,toolsUsedAccum:t.toolsUsedAccum,toolExecRounds:t.toolExecRounds}}case"tool_start":{let e=t.toolName,r;if(!e&&t.text)try{const s=JSON.parse(t.text);e=s.toolName;const o=s.toolInput?typeof s.toolInput=="string"?s.toolInput:JSON.stringify(s.toolInput):void 0;r=o?o.slice(0,200)+(o.length>200?"\u2026":""):void 0}catch{}return{kind:"tool_start",ts:n,toolName:e||"unknown",toolInput:r}}case"tool_end":{let e=t.toolName,r,s;if(t.text)try{const o=JSON.parse(t.text);e=e||o.toolName,s=o.durationMs;const c=o.output?typeof o.output=="string"?o.output:JSON.stringify(o.output):void 0;r=c?c.slice(0,500)+(c.length>500?"\u2026":""):void 0}catch{}return{kind:"tool_end",ts:n,toolName:e||"unknown",toolResult:r,durationMs:s}}case"hitl_interrupt":{const e=t.interruptValue,r=e?(typeof e=="string"?e:JSON.stringify(e)).slice(0,500):void 0;return{kind:"hitl_interrupt",ts:n,node:t.node,interruptValue:r}}case"hitl_resumed":{const e=t.decision==="rejected"?"rejected":"approved",r=t.values,s=r?(typeof r=="string"?r:JSON.stringify(r)).slice(0,500):void 0;return{kind:"hitl_resume",ts:n,decision:e,values:s,durationMs:typeof t.durationMs=="number"?t.durationMs:void 0}}}return null}async function d(){if(l)return;await i.indices.exists({index:a},{requestTimeout:u})||(await i.indices.create({index:a,body:{settings:{number_of_shards:1,number_of_replicas:1},mappings:{properties:{chatId:{type:"keyword"},runId:{type:"keyword"},username:{type:"keyword"},teamId:{type:"keyword"},events:{type:"object",enabled:!1},createdAt:{type:"date"}}}}},{requestTimeout:u}),console.log(`[chat-traces] Created index ${a}`)),l=!0}async function T(){try{await d()}catch(t){console.warn("[chat-traces] Pre-init failed (non-fatal):",t.message)}}const p=200;async function k(t){try{await d();const n=new Date().toISOString(),e=t.events.length>p?t.events.slice(0,p):t.events;await i.index({index:a,body:{...t,events:e,createdAt:t.createdAt||n}},{requestTimeout:u}),console.log(`[chat-traces] Saved trace for run ${t.runId} (${e.length} events)`)}catch(n){console.error("[chat-traces] Failed to save trace:",n)}}async function g(t){try{await d();const e=(await i.search({index:a,body:{query:{term:{runId:t}},size:1}},{requestTimeout:u})).hits.hits[0];return e?{id:e._id,...e._source}:null}catch(n){return console.error("[chat-traces] Failed to get trace by runId:",n),null}}async function x(t){try{return await d(),(await i.search({index:a,body:{query:{term:{chatId:t}},sort:[{createdAt:{order:"asc"}}],size:100}},{requestTimeout:u})).hits.hits.map(e=>({id:e._id,...e._source}))}catch(n){return console.error("[chat-traces] Failed to get traces by chatId:",n),[]}}async function w(t,n,e=10){try{return await d(),(await i.search({index:a,body:{query:{bool:{filter:[{term:{username:t}},{term:{teamId:n}}]}},sort:[{createdAt:{order:"desc"}}],size:e}},{requestTimeout:u})).hits.hits.map(s=>({id:s._id,...s._source}))}catch(r){return console.error("[chat-traces] Failed to get recent traces by team:",r),[]}}export{k as createChatTrace,w as getRecentTracesByTeam,g as getTraceByRunId,x as getTracesByChatId,T as initializeChatTracesIndex,b as wsMessageToTraceEvent};
@@ -0,0 +1,5 @@
1
+ import{Client as C}from"@elastic/elasticsearch";import{config as A}from"../config";import{getRecentTracesByTeam as E}from"./chat-traces-service";const y=new C(A.elasticsearch),p=".stkxp_chats";async function f(){try{if(!await y.indices.exists({index:p}))await y.indices.create({index:p,body:{settings:{number_of_shards:1,number_of_replicas:1},mappings:{properties:{title:{type:"text"},teamId:{type:"keyword"},teamName:{type:"keyword"},memoryScoped:{type:"boolean"},messages:{type:"nested",properties:{role:{type:"keyword"},content:{type:"object",enabled:!1},timestamp:{type:"date"},id:{type:"keyword"},toolsRunId:{type:"keyword"},assistantId:{type:"keyword"},assistantName:{type:"keyword"}}},assistants:{type:"nested",properties:{assistantId:{type:"keyword"},assistantName:{type:"keyword"},toolCallsCount:{type:"integer"},llmCallsCount:{type:"integer"},success:{type:"boolean"}}},metadata:{properties:{cluster:{type:"keyword"},nodes:{type:"keyword"},roles:{type:"keyword"},phases:{type:"keyword"},topic:{type:"keyword"},namespace:{type:"keyword"},packageName:{type:"keyword"},systemPrompt:{type:"text"},tokenUsage:{properties:{totalInputTokens:{type:"long"},totalOutputTokens:{type:"long"},totalTokens:{type:"long"}}}}},username:{type:"keyword"},createdAt:{type:"date"},updatedAt:{type:"date"}}}}}),console.log(`Created index ${p}`);else try{await y.indices.putMapping({index:p,body:{properties:{teamId:{type:"keyword"},teamName:{type:"keyword"},memoryScoped:{type:"boolean"}}}})}catch(r){r.message?.includes("illegal_argument_exception")||console.warn("[chats] Could not update mapping:",r.message)}}catch(s){throw console.error("Error initializing chats index:",s),s}}async function M(s){try{await f();const r=new Date().toISOString(),{id:t,...n}=s,a={...n,createdAt:r,updatedAt:r},l=await y.index({index:p,...t?{id:t}:{},body:a,refresh:"true"});return{...a,id:l._id}}catch(r){throw console.error("Error creating chat:",r),r}}async function I(s,r=0,t=50,n){try{await f();const a=[{term:{username:s}}];n&&a.push({term:{teamId:n}});const l=await y.search({index:p,body:{query:{bool:{filter:a}},sort:[{updatedAt:{order:"desc"}}],from:r,size:t}});return{chats:l.hits.hits.map(i=>({id:i._id,...i._source})),total:typeof l.hits.total=="number"?l.hits.total:l.hits.total?.value||0}}catch(a){throw console.error("Error getting user chats:",a),a}}async function j(s,r){try{await f();const n=(await y.search({index:p,body:{query:{bool:{filter:[{term:{username:s}},{term:{teamId:r}}]}},sort:[{updatedAt:{order:"desc"}}],size:1}})).hits.hits[0];return n?{id:n._id,...n._source}:null}catch(t){throw console.error("Error getting last chat by team:",t),t}}async function R(s,r,t=3,n=600){try{await f();const l=(await y.search({index:p,body:{query:{bool:{filter:[{term:{username:s}},{term:{teamId:r}}]}},sort:[{updatedAt:{order:"desc"}}],size:t,_source:["messages","updatedAt","title"]}})).hits.hits.map(i=>({id:i._id,...i._source}));if(l.length===0)return null;const d=["[Previous conversations on this team \u2014 for context only, do not repeat unless relevant]"];for(const i of l.reverse()){const e=[...i.messages||[]].reverse().find(c=>c.role==="user"),o=[...i.messages||[]].reverse().find(c=>c.role==="assistant"||c.role==="agent");if(!e&&!o)continue;const u=i.updatedAt?new Date(i.updatedAt).toISOString().slice(0,10):"";if(d.push(`
2
+ --- ${u}${i.title?` | ${i.title}`:""} ---`),e){const c=typeof e.content=="string"?e.content:Array.isArray(e.content)?e.content.map(m=>m.text??"").join(""):JSON.stringify(e.content);d.push(`User: ${c.slice(0,n)}${c.length>n?"\u2026":""}`)}if(o){const m=(typeof o.content=="string"?o.content:Array.isArray(o.content)?o.content.map(g=>g.text??"").join(""):JSON.stringify(o.content)).replace(/```json[\s\S]*?```/g,"[dashboard blocks]").trim();d.push(`Assistant: ${m.slice(0,n)}${m.length>n?"\u2026":""}`)}}return d.length>1?d.join(`
3
+ `):null}catch(a){return console.error("[chats] buildChatMemoryContext error:",a),null}}async function B(s,r,t=3,n=600){try{await f();const[a,l]=await Promise.all([y.search({index:p,body:{query:{bool:{filter:[{term:{username:s}},{term:{teamId:r}}]}},sort:[{updatedAt:{order:"desc"}}],size:t,_source:["messages","updatedAt","title"]}}),E(s,r,t*3)]),d=a.hits.hits.map(o=>({id:o._id,...o._source}));if(d.length===0)return null;const i=new Map;for(const o of l)i.has(o.chatId)||i.set(o.chatId,o);const e=["[Previous conversations on this team \u2014 for context only, do not repeat unless relevant]"];for(const o of d.reverse()){const u=o.updatedAt?new Date(o.updatedAt).toISOString().slice(0,10):"";e.push(`
4
+ --- ${u}${o.title?` | ${o.title}`:""} ---`);const c=[...o.messages||[]].reverse().find(h=>h.role==="user");if(c){const h=typeof c.content=="string"?c.content:Array.isArray(c.content)?c.content.map(w=>w.text??"").join(""):JSON.stringify(c.content);e.push(`User: ${h.slice(0,n)}${h.length>n?"\u2026":""}`)}const m=o.id?i.get(o.id)??i.get(o.id.replace(/-/g,"")):void 0;if(m){const h=m.events;_(e,h);continue}const g=[...o.messages||[]].reverse().find(h=>h.role==="assistant"||h.role==="agent");if(g){const w=(typeof g.content=="string"?g.content:Array.isArray(g.content)?g.content.map(x=>x.text??"").join(""):JSON.stringify(g.content)).replace(/```json[\s\S]*?```/g,"[dashboard blocks]").trim();e.push(`Assistant: ${w.slice(0,n)}${w.length>n?"\u2026":""}`)}}return e.length>1?e.join(`
5
+ `):null}catch(a){return console.error("[chats] buildEnrichedChatMemoryContext error:",a),null}}function _(s,r){const t=r.filter(e=>e.kind==="assistant_end");if(t.length>0){const e=new Set,o=[];for(const u of t)if(!e.has(u.assistantName)){e.add(u.assistantName);const c=u.success?u.toolCallsCount?` (${u.toolCallsCount} tools)`:"":" (failed)";o.push(`${u.assistantName}${c}`)}s.push(`Assistants: ${o.join(", ")}`)}const n=new Map;for(const e of t)!e.success||!e.output||n.set(e.assistantName,e.output);const a=Array.from(n.entries()).slice(0,3);if(a.length>0){s.push("Specialist outputs:");for(const[e,o]of a){const u=o.replace(/```json[\s\S]*?```/g,"[dashboard blocks]").replace(/\{"type":"dashboard"[\s\S]*?\}\]\}/g,"[dashboard blocks]").replace(/\s+/g," ").trim().slice(0,600);s.push(` ${e}: ${u}${o.length>600?"\u2026":""}`)}}const l=[...new Set(r.filter(e=>e.kind==="tool_start").map(e=>e.toolName))];l.length>0&&s.push(`Tools: ${l.join(", ")}`);const d=r.filter(e=>e.kind==="tool_end"&&!!e.toolResult).slice(0,2);if(d.length>0){s.push("Data:");for(const e of d){const o=(e.toolResult||"").slice(0,300);s.push(` ${e.toolName}: ${o}${(e.toolResult||"").length>300?"\u2026":""}`)}}const i=[...r].reverse().find(e=>e.kind==="node_end"&&e.node==="response_generator"&&!!e.nodeOutput);i?.nodeOutput&&s.push(`Conclusion: ${i.nodeOutput}`)}async function P(s,r){try{await f();const n=(await y.search({index:p,body:{query:{bool:{filter:[{term:{username:s}},{term:{"assistants.assistantId":r}}]}},sort:[{updatedAt:{order:"desc"}}],size:1}})).hits.hits[0];return n?{id:n._id,...n._source}:null}catch(t){throw console.error("Error getting last chat by assistant:",t),t}}async function b(s,r){try{const t=await y.get({index:p,id:s}),n={id:t._id,...t._source};return n.username!==r?null:n}catch(t){if(t.meta?.statusCode===404)return null;throw console.error("Error getting chat by ID:",t),t}}async function $(s,r,t){try{if(!await b(s,r))return null;const a=new Date().toISOString(),l={...t,updatedAt:a};return await y.update({index:p,id:s,body:{doc:l},refresh:"true"}),await b(s,r)}catch(n){throw console.error("Error updating chat:",n),n}}class k extends Error{}function S(s){const r=typeof s.content=="string"?s.content:JSON.stringify(s.content??"");try{const t=JSON.parse(r);return Array.isArray(t)?{blocks:t,wrapped:!1}:t&&t.type==="dashboard"&&Array.isArray(t.blocks)?{blocks:t.blocks,wrapped:!0}:null}catch{return null}}async function J(s,r,t,n,a){const l=await b(s,r);if(!l)return null;const d=l.messages.find(c=>c.id===t);if(!d)throw new k(`Message "${t}" not found in chat "${s}"`);const i=S(d);if(!i)throw new k(`Message "${t}" content has no blocks array`);if(n<0||n>=i.blocks.length)throw new k(`Block index ${n} out of range for message "${t}" (${i.blocks.length} blocks)`);const e=[...i.blocks];e[n]=a;const o=i.wrapped?JSON.stringify({type:"dashboard",blocks:e}):JSON.stringify(e),u=l.messages.map(c=>c.id===t?{...c,content:o}:c);return await $(s,r,{messages:u})}async function D(s,r){try{return await b(s,r)?(await y.delete({index:p,id:s,refresh:"true"}),!0):!1}catch(t){if(t.meta?.statusCode===404)return!1;throw console.error("Error deleting chat:",t),t}}async function q(s,r,t=0,n=20){try{await f();const a=await y.search({index:p,body:{query:{bool:{must:[{term:{username:s}},{multi_match:{query:r,fields:["title^2","messages.content"],type:"best_fields"}}]}},sort:[{updatedAt:{order:"desc"}}],from:t,size:n}});return{chats:a.hits.hits.map(d=>({id:d._id,...d._source})),total:typeof a.hits.total=="number"?a.hits.total:a.hits.total?.value||0}}catch(a){throw console.error("Error searching chats:",a),a}}export{k as MessageBlockNotFoundError,R as buildChatMemoryContext,B as buildEnrichedChatMemoryContext,M as createChat,D as deleteChat,b as getChatById,P as getLastChatByAssistant,j as getLastChatByTeam,S as getMessageBlocks,I as getUserChats,f as initializeChatsIndex,q as searchChats,$ as updateChat,J as updateMessageBlock};
@@ -0,0 +1,107 @@
1
+ import{getChatById as x}from"./chats-service";import{getToolsByChatId as N}from"./chat-tools-service";async function y(e,t){console.log(`[ComparisonService] Summarizing chat ${e} for user ${t}`);const s=await x(e,t);if(!s)throw new Error(`Chat ${e} not found`);const n=await N(e);console.log(`[ComparisonService] Found ${n.length} tool records for chat ${e}`);const r=s.messages.filter(a=>a.role==="user"),i=s.messages.filter(a=>a.role==="assistant"),g={totalMessages:s.messages.length,userMessages:r.length,assistantMessages:i.length,firstUserMessage:r.length>0?l(c(r[0].content),300):"",lastUserMessage:r.length>0?l(c(r[r.length-1].content),300):"",lastAssistantResponse:i.length>0?l(c(i[i.length-1].content),500):""},o=n.flatMap(a=>a.tools.map(h=>({toolName:h.name,input:h.input,output:h.output,timestamp:a.timestamp}))),u=s.messages.map(a=>c(a.content)).join(`
2
+ `),m=u.match(/(\d+(?:\.\d+)?)\s*(ms|MB|GB|%|req\/s|errors?|warnings?|docs?|shards?)/gi),$=m?[...new Set(m.slice(0,30))]:[],p=u.split(`
3
+ `).filter(a=>a.toLowerCase().includes("error")&&a.length<200),M=[...new Set(p.slice(0,10))],w=u.split(`
4
+ `).filter(a=>a.toLowerCase().includes("warning")&&a.length<200),A=[...new Set(w.slice(0,10))];return{chatId:s.id||e,title:s.title,createdAt:s.createdAt,updatedAt:s.updatedAt,metadata:s.metadata||{},messagesSummary:g,toolsSummary:o,extractedMetrics:$,extractedErrors:M,extractedWarnings:A}}function D(e,t){return`You are analyzing two Elasticsearch diagnostic sessions to identify changes, improvements, and regressions.
5
+
6
+ # \u{1F4CB} Past Analysis (${b(e.updatedAt)})
7
+ **Title:** ${e.title}
8
+ **Chat ID:** ${e.chatId}
9
+ **Date:** ${e.updatedAt}
10
+ **Package:** ${e.metadata.packageName||"N/A"}
11
+ **Namespace:** ${e.metadata.namespace||"N/A"}
12
+ **Topic:** ${e.metadata.topic||"N/A"}
13
+
14
+ ## Messages
15
+ - Total: ${e.messagesSummary.totalMessages} (${e.messagesSummary.userMessages} user, ${e.messagesSummary.assistantMessages} assistant)
16
+ - First question: ${e.messagesSummary.firstUserMessage}
17
+ - Last question: ${e.messagesSummary.lastUserMessage}
18
+ - Last response: ${e.messagesSummary.lastAssistantResponse}
19
+
20
+ ## Tools Used (${e.toolsSummary.length} total)
21
+ ${f(e.toolsSummary)}
22
+
23
+ ## Extracted Metrics
24
+ ${e.extractedMetrics.length>0?e.extractedMetrics.join(", "):"None detected"}
25
+
26
+ ## Errors Detected (${e.extractedErrors.length})
27
+ ${d(e.extractedErrors,5)}
28
+
29
+ ## Warnings Detected (${e.extractedWarnings.length})
30
+ ${d(e.extractedWarnings,5)}
31
+
32
+ ---
33
+
34
+ # \u{1F504} Present Analysis (Now)
35
+ **Title:** ${t.title}
36
+ **Date:** ${t.updatedAt}
37
+ **Package:** ${t.metadata.packageName||"N/A"}
38
+ **Namespace:** ${t.metadata.namespace||"N/A"}
39
+ **Topic:** ${t.metadata.topic||"N/A"}
40
+
41
+ ## Messages
42
+ - Total: ${t.messagesSummary.totalMessages} (${t.messagesSummary.userMessages} user, ${t.messagesSummary.assistantMessages} assistant)
43
+ - First question: ${t.messagesSummary.firstUserMessage}
44
+ - Last question: ${t.messagesSummary.lastUserMessage}
45
+ - Last response: ${t.messagesSummary.lastAssistantResponse}
46
+
47
+ ## Tools Used (${t.toolsSummary.length} total)
48
+ ${f(t.toolsSummary)}
49
+
50
+ ## Extracted Metrics
51
+ ${t.extractedMetrics.length>0?t.extractedMetrics.join(", "):"None detected"}
52
+
53
+ ## Errors Detected (${t.extractedErrors.length})
54
+ ${d(t.extractedErrors,5)}
55
+
56
+ ## Warnings Detected (${t.extractedWarnings.length})
57
+ ${d(t.extractedWarnings,5)}
58
+
59
+ ---
60
+
61
+ # \u{1F3AF} Your Task
62
+ If you need to call the \`compare_with_previous_chat\` tool, use **chatId="${e.chatId}"** (the Chat ID shown above \u2014 not the title).
63
+
64
+ Provide a comprehensive comparison analysis with the following structure:
65
+
66
+ ## 1. Executive Summary
67
+ Brief overview of the most significant changes (2-3 sentences)
68
+
69
+ ## 2. Key Changes Detected
70
+ List the most important differences between the two contexts
71
+
72
+ ## 3. Improvements \u2705
73
+ What has gotten better? Include specific metrics if available
74
+
75
+ ## 4. Regressions \u26A0\uFE0F
76
+ What has gotten worse? Highlight critical issues
77
+
78
+ ## 5. New Issues Found \u{1F534}
79
+ Problems that weren't present in the past analysis
80
+
81
+ ## 6. Resolved Issues \u2705
82
+ Problems from the past analysis that are now fixed
83
+
84
+ ## 7. Tool Usage Comparison
85
+ Differences in which tools were used and their outputs
86
+
87
+ ## 8. Metrics Comparison
88
+ If metrics are available, provide a comparison with specific numbers
89
+
90
+ ## 9. Recommendations \u{1F4A1}
91
+ Actionable steps based on this comparison
92
+
93
+ Be specific, use numbers and percentages when available, and prioritize critical information.`}function c(e){if(typeof e=="string")return e;if(Array.isArray(e))return e.map(c).join(" ");if(e&&typeof e=="object")try{return JSON.stringify(e)}catch{return String(e)}return String(e)}function l(e,t){return e.length<=t?e:e.substring(0,t)+"..."}function b(e){const t=new Date(e),n=new Date().getTime()-t.getTime(),r=Math.floor(n/(1e3*60)),i=Math.floor(n/(1e3*60*60)),g=Math.floor(n/(1e3*60*60*24));if(g>0){const o=Math.floor(n%864e5/36e5);return`${g} day${g>1?"s":""}${o>0?` and ${o} hour${o>1?"s":""}`:""} ago`}else if(i>0){const o=Math.floor(n%36e5/6e4);return`${i} hour${i>1?"s":""}${o>0?` and ${o} minute${o>1?"s":""}`:""} ago`}else return`${r} minute${r>1?"s":""} ago`}function f(e){if(e.length===0)return"No tools used";const t=e.slice(0,10).map((s,n)=>{const r=l(JSON.stringify(s.input,null,2),200),i=l(JSON.stringify(s.output,null,2),300);return`### Tool ${n+1}: ${s.toolName}
94
+ **Input:**
95
+ \`\`\`json
96
+ ${r}
97
+ \`\`\`
98
+
99
+ **Output:**
100
+ \`\`\`json
101
+ ${i}
102
+ \`\`\`
103
+ `});return e.length>10&&t.push(`
104
+ ... and ${e.length-10} more tools`),t.join(`
105
+ `)}function d(e,t){if(e.length===0)return"None";const s=e.slice(0,t).map(n=>`- ${n}`).join(`
106
+ `);return e.length>t?`${s}
107
+ ... and ${e.length-t} more`:s}export{D as buildComparisonPrompt,y as summarizeChat};
@@ -0,0 +1 @@
1
+ import{Client as u}from"@elastic/elasticsearch";const c=process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",m=process.env.ELASTICSEARCH_USER||"elastic",f=process.env.ELASTICSEARCH_PASSWORD||"diagnostics",s=new u({node:c,auth:{username:m,password:f},tls:{rejectUnauthorized:!1},requestTimeout:6e4,maxRetries:3}),l=["openai","anthropic","google","local","openrouter"];async function p(e){try{const o=(await s.get({index:".stkxp_llm_providers",id:e}))._source?.type;if(o&&l.includes(o))return o}catch{}try{const o=(await s.search({index:".stkxp_llm_providers",body:{query:{bool:{should:[{term:{id:e}},{term:{"id.keyword":e}}]}},size:1,_source:["type"]}})).hits.hits[0]?._source?.type;if(o&&l.includes(o))return o}catch{}const r=e.split("-")[0];return l.includes(r)?r:null}function y(e){const r=e.toLowerCase();return r.startsWith("gpt-")||r.startsWith("o1")||r.startsWith("o3")||r.startsWith("o4")||r==="gpt-4.5"?"openai":r.startsWith("claude-")?"anthropic":r.startsWith("gemini-")?"google":null}async function d(e,r){let t=await p(e.providerId);return t||(t=y(e.modelId),t&&console.log(`[default-model] ${r}: inferred provider '${t}' from model name '${e.modelId}'`)),t?(console.log(`[default-model] ${r}: ${t}:${e.modelId}`),{provider:t,model:e.modelId}):(console.warn(`[default-model] ${r}: could not resolve provider for providerId='${e.providerId}', modelId='${e.modelId}' \u2014 skipping`),null)}async function x(){try{let e=await s.search({index:".stkxp_llm_models",body:{query:{bool:{must:[{term:{enabled:!0}},{term:{"metadata.tags":"default"}}]}},size:1}});if(e.hits.hits.length>0){const r=await d(e.hits.hits[0]._source,"tagged default");if(r)return r}if(e=await s.search({index:".stkxp_llm_models",body:{query:{bool:{must:[{term:{enabled:!0}},{term:{modelId:"gpt-4o-mini"}}]}},size:1}}),e.hits.hits.length>0){const r=await d(e.hits.hits[0]._source,"gpt-4o-mini fallback");if(r)return r}e=await s.search({index:".stkxp_llm_models",body:{query:{term:{enabled:!0}},sort:[{createdAt:{order:"asc",unmapped_type:"date"}}],size:10}});for(const r of e.hits.hits){const t=await d(r._source,"first available");if(t)return t}return console.error("[default-model] No enabled models found in Elasticsearch"),null}catch(e){return console.error("[default-model] Error fetching default model:",e),null}}async function h(e){try{const r=await s.get({index:".stkxp_llm_providers",id:e});if(r.found!==!1)return r._source}catch{}try{return(await s.search({index:".stkxp_llm_providers",body:{query:{bool:{should:[{term:{id:e}},{term:{"id.keyword":e}}]}},size:1}})).hits.hits[0]?._source??null}catch{return null}}async function P(e){if(!e)return null;try{let t=(await s.search({index:".stkxp_llm_routing_rules",body:{query:{bool:{must:[{term:{owner:e}},{term:{isDefault:!0}}]}},size:1}})).hits.hits[0]?._source;if(t||(t=(await s.search({index:".stkxp_llm_routing_rules",body:{query:{term:{owner:e}},size:1}})).hits.hits[0]?._source),!t)return console.warn(`[default-model] Owner '${e}' has no routing rules \u2014 cannot resolve an owner LLM key`),null;const o=t.modelId||t.llmModelId;if(!o)return null;const i=(await s.search({index:".stkxp_llm_models",body:{query:{term:{id:o}},size:1}})).hits.hits[0]?._source;if(!i)return null;const n=await h(i.providerId);if(!n)return null;if(n.owner&&n.owner!==e)return console.warn(`[default-model] Owner '${e}' default rule resolves to provider owned by '${n.owner}' \u2014 refusing cross-tenant key`),null;if(!n.apiKey)return console.warn(`[default-model] Owner '${e}' provider '${n.name||i.providerId}' has no API key`),null;const a=n.type;return l.includes(a)?{provider:a,model:i.modelId,apiKey:n.apiKey,baseURL:n.endpoint}:null}catch(r){return console.error(`[default-model] getOwnerDefaultLLMConfig error for '${e}':`,r?.message),null}}export{x as getDefaultLLMModel,P as getOwnerDefaultLLMConfig};
@@ -0,0 +1,2 @@
1
+ import J from"https";import L from"crypto";import q from"axios";import{Client as z}from"@elastic/elasticsearch";import{getPlatformById as H}from"./platforms-service";import{extractPanelsFromDashboard as G}from"./kibana-dashboard-extractor";const W=process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",X=process.env.ELASTICSEARCH_USER||"elastic",Q=process.env.ELASTICSEARCH_PASSWORD||"",V=process.env.MCP_BASE_URL||"https://51.158.76.76:3001/mcp",de=process.env.STKXP_API_URL||"http://localhost:4000",v=".stkxp_platforms",R=".stkxp_api",$=".stkxp_tools",g=new z({node:W,auth:{username:X,password:Q},tls:{rejectUnauthorized:!1},requestTimeout:6e4,maxRetries:3}),N=new J.Agent({rejectUnauthorized:!1});function O(e){return q.create({httpsAgent:N,headers:{Authorization:`ApiKey ${e}`,"kbn-xsrf":"reporting","Content-Type":"application/json"},timeout:3e4})}async function x(e,n,t){return(await O(t).get(`${e}${n}`)).data}async function j(e,n,t,i){return(await O(t).post(`${e}${n}`,i)).data}function _(e){const n=e?.response?.status??"no-response",t=e?.response?.data,i=t?typeof t=="string"?t.slice(0,300):JSON.stringify(t).slice(0,300):"";return`HTTP ${n}${i?": "+i:" "+(e?.message??"")}`}function K(e){try{const n=new URL(e).hostname;if(n.includes(".kb.")&&n.endsWith(".elastic.cloud"))return"serverless"}catch{}return"self-hosted"}async function Y(e,n,t,i,o="self-hosted"){if(i!=="unknown")try{const r=await x(e,`/api/fleet/epm/packages/${t}/${i}`,n),s=r?.item??r?.response,a=(s?.savedObjects??[]).filter(l=>l.type==="dashboard").map(l=>({id:l.id,title:l.attributes?.title}));if(a.length>0)return console.log(`[ES Sync] Found ${a.length} dashboards via savedObjects for ${t}/${i}`),a;if(Array.isArray(s?.assets)){const l=s.assets.filter(m=>m.type==="dashboard").map(m=>({id:m.id,title:m.title}));if(l.length>0)return console.log(`[ES Sync] Found ${l.length} dashboards via flat assets for ${t}/${i}`),l}const c=s?.assets?.kibana?.dashboard??[];if(c.length>0){const l=c.map(m=>({id:m.id??m.file?.replace(/\.json$/,"").split("/").pop(),title:m.title})).filter(m=>m.id);if(l.length>0)return console.log(`[ES Sync] Found ${l.length} dashboards via nested assets for ${t}/${i}`),l}console.warn(`[ES Sync] Package ${t}/${i} returned no dashboard assets (item keys: ${Object.keys(s??{}).join(", ")})`)}catch(r){console.warn(`[ES Sync] Primary asset fetch failed for ${t}/${i}: ${r.message}`)}if(o!=="serverless")try{const r=await x(e,`/api/kibana/management/saved_objects/_find?type=dashboard&search=${encodeURIComponent(t)}&search_fields=title&per_page=50`,n),p=(r?.saved_objects??r?.response??[]).filter(a=>a.id?.startsWith(`${t}-`));if(p.length>0)return console.log(`[ES Sync] Fallback saved-objects search found ${p.length} dashboards for "${t}"`),p.map(a=>({id:a.id,title:a.attributes?.title}))}catch(r){console.warn(`[ES Sync] Fallback saved-objects search failed: ${r.message}`)}return console.warn(`[ES Sync] No dashboard assets found for package "${t}" (version: ${i})`),[]}async function Z(e,n,t,i="self-hosted"){if(t.length===0)return{objects:[],dataViewMap:new Map};if(i!=="serverless")try{const o=t.map(a=>({type:"dashboard",id:a.id})),r=await j(e,"/api/kibana/management/saved_objects/_bulk_get",n,o),p=(r?.saved_objects??r?.body?.saved_objects??[]).filter(a=>!a.error);if(p.length>0)return console.log(`[ES Sync] Got ${p.length} dashboards via management _bulk_get`),{objects:p,dataViewMap:new Map}}catch(o){console.warn(`[ES Sync] management _bulk_get failed: ${_(o)}`)}try{const o={objects:t.map(l=>({type:"dashboard",id:l.id})),includeReferencesDeep:!0,excludeExportDetails:!0},s=(await O(n).post(`${e}/api/saved_objects/_export`,o,{responseType:"text"})).data.split(`
2
+ `).filter(l=>l.trim()),p=s.map(l=>{try{return JSON.parse(l)}catch{return null}}).filter(Boolean),a=p.filter(l=>l.type==="dashboard"),c=new Map;for(const l of p)l.type==="index-pattern"&&l.id&&l.attributes?.title&&c.set(l.id,l.attributes.title);if(c.size>0&&console.log(`[ES Sync] Built data view map with ${c.size} entries from export`),a.length>0)return console.log(`[ES Sync] Got ${a.length} dashboards via /api/saved_objects/_export (Serverless)`),{objects:a,dataViewMap:c};console.warn(`[ES Sync] /api/saved_objects/_export returned 0 dashboards (lines: ${s.length})`)}catch(o){console.warn(`[ES Sync] /api/saved_objects/_export failed: ${_(o)}`)}try{const o={objects:t.map(a=>({type:"dashboard",id:a.id}))},p=((await j(e,"/api/saved_objects/_bulk_get",n,o))?.saved_objects??[]).filter(a=>!a.error);if(p.length>0)return console.log(`[ES Sync] Got ${p.length} dashboards via /api/saved_objects/_bulk_get`),{objects:p,dataViewMap:new Map}}catch(o){console.warn(`[ES Sync] /api/saved_objects/_bulk_get failed: ${_(o)}`)}for(const o of[3,2,1])try{const r={contentTypeId:"dashboard",version:o,query:{ids:t.map(c=>({id:c.id}))}},s=await j(e,"/api/content_management/rpc/mget",n,r),a=(s?.result?.hits??s?.hits??[]).filter(c=>c&&!c.error).map(c=>({id:c.id,type:"dashboard",attributes:c.attributes??c,references:c.references??[]}));if(a.length>0)return console.log(`[ES Sync] Got ${a.length} dashboards via content_management mget (v${o})`),{objects:a,dataViewMap:new Map}}catch(r){console.warn(`[ES Sync] content_management mget v${o} failed: ${_(r)}`)}console.log(`[ES Sync] Trying content_management individual get for ${t.length} dashboards`);{const o=[];for(const{id:r}of t)for(const s of[3,2,1])try{const a=await j(e,"/api/content_management/rpc/get",n,{contentTypeId:"dashboard",version:s,id:r}),c=a?.result?.item??a?.item;if(c){o.push({id:c.id??r,type:"dashboard",attributes:c.attributes??c,references:c.references??[]}),console.log(`[ES Sync] Got dashboard ${r} via content_management get v${s}`);break}}catch(p){console.warn(`[ES Sync] content_management get ${r} v${s} failed: ${_(p)}`)}if(o.length>0)return{objects:o,dataViewMap:new Map}}console.log(`[ES Sync] Falling back to individual saved object GET for ${t.length} dashboards`);{const o=[];for(const{id:r}of t)try{const s=await x(e,`/api/saved_objects/dashboard/${r}`,n);s&&!s.error?o.push(s):console.warn(`[ES Sync] GET dashboard/${r} error: ${JSON.stringify(s?.error??s)}`)}catch(s){console.warn(`[ES Sync] GET dashboard/${r} failed: ${_(s)}`)}return{objects:o,dataViewMap:new Map}}}async function ee(e,n,t,i,o){if(o.length===0)return{created:0,updated:0,panelsSkipped:0};const r=new Date().toISOString();let s=0,p=0,a=0;console.log(JSON.stringify(o,null,2));const c=50;for(let l=0;l<o.length;l+=c){const m=o.slice(l,l+c),f=[];for(const d of m){const u=d.uniquePath;let h=!1;try{h=await g.exists({index:R,id:u})}catch{}f.push({update:{_index:R,_id:u}},{doc:{name:d.uniquePath,system:e,description:`${d.dashboardTitle} \u2014 ${d.panelName}`,status:"active",category:"dashboard_panel",panel_name:d.panelName,panel_type:d.panelType,dashboard_id:d.dashboardId,dashboard_title:d.dashboardTitle,unique_path:d.uniquePath,query:JSON.stringify(d.query),query_language:"dsl",aggs:JSON.stringify(d.aggs),index_template:JSON.stringify(d.outputSchema),control_panels:JSON.stringify(d.controlPanels),index_pattern:d.indexPattern??"",namespace:n,platformId:t,has_template:!0,owner:i,updatedAt:r,updatedBy:i},doc_as_upsert:!0,upsert:{createdAt:r,createdBy:i}}),h?p++:s++}if(f.length>0){const u=((await g.bulk({body:f,refresh:!1})).items??[]).filter(h=>h.update?.error);u.length>0&&(console.warn(`[ES Sync] ${u.length} bulk errors in upsertApiDocs`),a+=u.length)}}return{created:s,updated:p,panelsSkipped:a}}async function te(e){const n=new Map;try{const t=await g.search({index:$,size:1e3,_source:["name","inputSchema"],query:{term:{system:e}}});for(const i of t.hits.hits){const o=i._source??{},r=o.name;if(!r)continue;let s;try{s=typeof o.inputSchema=="string"?JSON.parse(o.inputSchema):o.inputSchema}catch{continue}const p=s?.properties?.namespace;!p||typeof p!="object"||n.get(r)?.property?.description&&!p.description||n.set(r,{property:p,required:Array.isArray(s?.required)&&s.required.includes("namespace")})}n.size>0&&console.log(`[ES Sync] Preserved namespace param for ${n.size} existing tools of system="${e}"`)}catch(t){console.warn(`[ES Sync] Could not read existing namespace params for "${e}": ${t.message}`)}return n}function D(e,n){if(!n)return e;const t=e&&typeof e=="object"?{...e}:{type:"object"};if(t.properties={...t.properties??{},namespace:n.property},n.required){const i=Array.isArray(t.required)?[...t.required]:[];i.includes("namespace")||i.push("namespace"),t.required=i}return t}function A(e){return e.replace(/_+/g," ").replace(/\b\w/g,n=>n.toUpperCase()).trim()}async function se(e,n,t,i,o,r){const s=r?.writeStkxpTemplate??!0,p=await te(e);try{const u=(await g.deleteByQuery({index:$,refresh:!1,body:{query:{bool:{must:[{term:{system:e}}],should:[{term:{owner:t}},{term:{owner:"__test_provisioning__"}}],minimum_should_match:1}}}})).deleted??0;u>0&&console.log(`[ES Sync] Deleted ${u} stale tools for system="${e}"`)}catch(d){console.warn(`[ES Sync] deleteByQuery failed for tools cleanup: ${d.message}`)}if(i.length===0)return{created:0,updated:0};const a=new Date().toISOString();let c=0;const l=0,m=50;for(let d=0;d<i.length;d+=m){const u=i.slice(d,d+m),h=[];for(const y of u){const b=`es_${y.uniquePath}`;h.push({index:{_index:$,_id:b}},{name:y.toolName,label:A(y.panelName).slice(0,120),description:`${y.dashboardTitle} \u2014 ${A(y.panelName)}`.slice(0,256),type:"mcp",system:e,endpoint:`/api/stack_expert/${e}/${n}/${y.uniquePath}`,path:`/api/stack_expert/${e}/${n}/${y.uniquePath}`,service:y.uniquePath,httpMethod:"POST",inputSchema:JSON.stringify(D(y.inputSchema,p.get(y.toolName))),outputSchema:JSON.stringify(y.outputSchema),indexPattern:y.indexPattern??"",platformId:o,enabled:!0,owner:t,createdAt:a,updatedAt:a,createdBy:t,updatedBy:t}),c++}if(h.length>0){const b=((await g.bulk({body:h,refresh:!1})).items??[]).filter(E=>E.index?.error||E.create?.error);b.length>0&&(console.warn(`[ES Sync] ${b.length} bulk errors in upsertToolsFromPanels`),c-=b.length)}}const f="stkxp";if(s)try{await g.deleteByQuery({index:$,refresh:!1,body:{query:{bool:{must:[{term:{system:e}},{term:{owner:f}}]}}}});const d=[];for(const u of i)d.push({index:{_index:$,_id:`tpl_${u.uniquePath}`}},{name:u.toolName,label:A(u.panelName).slice(0,120),description:`${u.dashboardTitle} \u2014 ${A(u.panelName)}`.slice(0,256),type:"mcp",system:e,endpoint:`/api/stack_expert/${e}/${n}/${u.uniquePath}`,path:`/api/stack_expert/${e}/${n}/${u.uniquePath}`,httpMethod:"POST",inputSchema:JSON.stringify(D(u.inputSchema,p.get(u.toolName))),outputSchema:JSON.stringify(u.outputSchema),indexPattern:u.indexPattern??"",enabled:!0,owner:f,createdAt:a,updatedAt:a,createdBy:t,updatedBy:t});d.length>0&&(await g.bulk({body:d,refresh:!1}),console.log(`[ES Sync] Updated ${i.length} stkxp template tools for system="${e}"`))}catch(d){console.warn(`[ES Sync] Failed to update stkxp templates: ${d.message}`)}return{created:c,updated:l}}function pe(e,n){const t=String(e||"platform").toLowerCase().replace(/[^a-z0-9]+/g,"_").replace(/^_+|_+$/g,"").slice(0,40)||"platform",i=L.createHash("sha1").update(n).digest("hex").slice(0,8);return`esdash_${t}_${i}`}async function ne(e,n,t,i){const o=`${t.replace(/\/$/,"")}/${e}`,r=`${e}-mcp`,s=new Date().toISOString(),a=(await g.search({index:v,body:{query:{bool:{must:[{term:{type:"MCPServer"}},{term:{owner:n}},{term:{"config.packageName":e}}]}},size:1}})).hits.hits;if(a.length>0){const f=a[0]._id,d=a[0]._source;return await g.update({index:v,id:f,body:{doc:{"config.url":o,managedType:i,updated:s}},refresh:!1}),console.log(`[ES Sync] Updated MCPServer platform "${d.name}" (${f}) managedType=${i} for ${e}`),{platformId:f,platformName:d.name}}const c={name:r,type:"MCPServer",managedType:i,owner:n,created:s,updated:s,enabled:!0,config:{url:o,protocol:"http",serverType:"local",packageName:e}},m=(await g.index({index:v,document:c,refresh:!1}))._id;return console.log(`[ES Sync] Created MCPServer platform "${r}" (${m}) for ${e}`),{platformId:m,platformName:r}}async function ue(e,n,t){const i=Buffer.from(e,"base64").toString("utf8"),o=i.split("|");if(o.length<2)throw new Error(`Invalid package_id format. Expected base64("platformId|packageName|namespace"), got: "${i}"`);const[r,s,p="default"]=o;console.log(`[ES Sync] Starting sync for package "${s}" on platform "${r}" (owner: ${n})`);const a=await H(r,n,!0,!1);if(!a)throw new Error(`Platform "${r}" not found or not accessible by user "${n}"`);if(a.type!=="ElasticStack")throw new Error(`Platform "${r}" is not an ElasticStack platform (type: ${a.type})`);const c=a.config,l=c.endpoints.kibana.url||"",m=c.endpoints.kibana.port||"5601";let f=l;f.startsWith("http")||(f=`https://${f}`);try{new URL(f).port||(f=`${f}:${m}`)}catch{f=`https://${l}:${m}`}c.endpoints.kibana.basePath&&(f=`${f}/${c.endpoints.kibana.basePath.replace(/^\/+|\/+$/g,"")}`);const d=c.access.apiKeys?.[0]?.name??"";if(!d)throw new Error(`Platform "${a.name}" has no API key configured`);const u=c.elasticFlavor??K(f);console.log(`[ES Sync] Kibana flavor: ${u}`),console.log(`[ES Sync] Kibana URL: ${f}, package: ${s}`);let h="unknown";try{const S=await x(f,"/api/fleet/epm/packages",d),w=(S?.items??S?.response??[]).find(B=>B.name===s);w?.version&&(h=w.version,console.log(`[ES Sync] Found package version: ${h}`))}catch(S){console.warn(`[ES Sync] Could not determine package version: ${S.message}`)}const y=await Y(f,d,s,h,u);console.log(`[ES Sync] Dashboard assets found: ${y.length}`);const{objects:b,dataViewMap:E}=await Z(f,d,y,u);console.log(`[ES Sync] Dashboard objects fetched: ${b.length}`);const k=b.flatMap(S=>G(S,s,E));console.log(`[ES Sync] Extracted ${k.length} panels from ${b.length} dashboards`);const P=await ee(s,p,r,n,k);console.log(`[ES Sync] API docs: +${P.created} created, ~${P.updated} updated, ${P.panelsSkipped} skipped`);const T=await se(s,p,n,k,r);console.log(`[ES Sync] Tools: +${T.created} created, ~${T.updated} updated`);const M=t?.mcpBaseUrl??V,I=a.managedType==="managed"?"managed":"self-hosted",{platformId:F,platformName:U}=await ne(s,n,M,I);await Promise.allSettled([g.indices.refresh({index:R}),g.indices.refresh({index:$}),g.indices.refresh({index:v})]);try{const C=`${M.replace(/\/mcp\/?$/,"")}/cache/reload`,w=await q.create({httpsAgent:N,timeout:1e4}).post(C);console.log(`[ES Sync] MCP cache reloaded: ${JSON.stringify(w.data)}`)}catch(S){console.warn(`[ES Sync] MCP cache reload failed (non-fatal): ${S.message}`)}return{packageName:s,platformName:a.name,apiDocs:P,tools:T,mcpPlatformId:F,mcpPlatformName:U}}export{pe as buildDashboardSystemName,ue as syncElasticStackPackageTools,ee as upsertApiDocs,ne as upsertMcpPlatform,se as upsertToolsFromPanels};
@@ -0,0 +1 @@
1
+ import{Client as r}from"@elastic/elasticsearch";class s{client;debugMode;constructor(e,o=!0){this.client=new r(e),this.debugMode=o}async search(e){this.debugMode&&console.log("[ES Debug] Search Parameters:",{timestamp:new Date().toISOString(),index:e.index,datastream:e.index,body:JSON.stringify(e.body,null,2),size:e.size,from:e.from});const o=Date.now();try{const t=await this.client.search(e);return this.debugMode&&console.log("[ES Debug] Search Result:",{timestamp:new Date().toISOString(),took:t.took,hits:t.hits.total,duration_ms:Date.now()-o}),t}catch(t){throw this.debugMode&&console.error("[ES Debug] Search Error:",{timestamp:new Date().toISOString(),error:t.message,index:e.index,duration_ms:Date.now()-o}),t}}get raw(){return this.client}get transport(){return this.client.transport}get security(){return this.client.security}setDebugMode(e){this.debugMode=e}isDebugEnabled(){return this.debugMode}}export{s as ElasticsearchWrapper};
@@ -0,0 +1 @@
1
+ const a=6e4,l=5e4,e=new Map;function u(n,c){const i=Date.now(),o=i-6e4,t=(e.get(n)??[]).filter(s=>s>o);if(t.length>=c)return e.set(n,t),!1;if(t.push(i),e.set(n,t),e.size>5e4)for(const[s,f]of e){const r=f.filter(m=>m>o);r.length===0?e.delete(s):e.set(s,r)}return!0}export{u as checkEmbedRateLimit};
@@ -0,0 +1 @@
1
+ import{Client as P}from"@elastic/elasticsearch";import{config as A}from"../config";const M=new P(A.elasticsearch),B=".stkxp_chat_runs",S=".stkxp_chat_tools";function C(e,a=400){return e.length<=a?e:e.slice(0,a)+"\u2026"}function _(e){let a=e;try{const o=JSON.parse(e);if(o&&typeof o=="object"){const p=o.message||o.error||o.errorMessage||o.detail||o.header&&o.header.message;typeof p=="string"&&(a=p)}}catch{}return a=a.replace(/:\d+:\d+/g,":<L>:<C>").replace(/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi,"<uuid>").replace(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z?/g,"<ts>").replace(/\b[0-9a-f]{8,}\b/gi,"<hex>").replace(/\b\d{4,}\b/g,"<n>").replace(/"[^"]{1,200}"/g,'"<v>"').replace(/'[^']{1,200}'/g,"'<v>'").replace(/\s+/g," ").trim(),C(a,200)}function L(e){if(e==null)return"(no message)";if(typeof e=="string")return C(e);if(typeof e=="number"||typeof e=="boolean")return String(e);if(typeof e=="object"){const a=e.error||e.message||e.errorMessage||e.detail||e.header&&e.header.message;if(typeof a=="string")return C(a);try{return C(JSON.stringify(e))}catch{return"(unserializable error)"}}return"(unknown shape)"}function x(e,a){const o=[];return e&&o.push({range:{createdAt:{gte:e.start,lte:e.end}}}),a&&o.push({term:{username:a}}),o}function E(e,a){const o=[];return e&&o.push({range:{startTime:{gte:e.start,lte:e.end}}}),a&&o.push({term:{username:a}}),o}async function G(e,a,o={}){const p=o.topToolsLimit??20,I=o.topPatternsLimit??20,y=o.samplesPerTool??3,h=x(e,a),b=await M.search({index:S,body:{query:h.length?{bool:{must:h}}:{match_all:{}},size:5e3,_source:["chatId","runId","username","tools","createdAt"],sort:[{createdAt:"desc"}]}}),m={},d=new Map;let T=0,f=0;for(const i of b.hits.hits){const r=i._source,t=Array.isArray(r.tools)?r.tools:[];for(const s of t){const u=s.name||"(unknown)";f+=1;const l=m[u]||(m[u]={errorCount:0,totalCalls:0,patternCounts:new Map,samples:[]});if(l.totalCalls+=1,!s.error)continue;T+=1,l.errorCount+=1;const g=L(s.output),R=_(g),k=l.patternCounts.get(R);k?k.count+=1:l.patternCounts.set(R,{count:1,sample:g});const N=d.get(R);N?(N.count+=1,N.toolSet.add(u)):d.set(R,{count:1,sample:g,toolSet:new Set([u])}),l.samples.length<y&&l.samples.push({chatId:r.chatId,runId:r.runId,timestamp:s.timestamp||r.createdAt,message:g})}}const c=Object.entries(m).filter(([,i])=>i.errorCount>0).map(([i,r])=>({toolName:i,errorCount:r.errorCount,totalCalls:r.totalCalls,errorRate:r.totalCalls>0?r.errorCount/r.totalCalls*100:0,topPatterns:Array.from(r.patternCounts.entries()).sort((t,s)=>s[1].count-t[1].count).slice(0,5).map(([t,s])=>({pattern:t,count:s.count,sample:s.sample})),recentSamples:r.samples})).sort((i,r)=>r.errorCount-i.errorCount).slice(0,p),n=Array.from(d.entries()).map(([i,r])=>({pattern:i,sample:r.sample,count:r.count,affectedTools:Array.from(r.toolSet)})).sort((i,r)=>r.count-i.count).slice(0,I);return{totalToolErrors:T,totalToolCalls:f,topTools:c,topPatterns:n}}async function z(e,a,o={}){const p=o.groupLimit??15,I=o.recentIdsPerGroup??5,y=E(e,a),h=await M.search({index:B,body:{query:y.length?{bool:{must:y}}:{match_all:{}},size:5e3,sort:[{startTime:"desc"}],_source:["runId","status","startTime","assistantId","assistantName","teamId","teamName","graphId","errorType","errorMessage","errorNodeId"]}}),b=()=>({failedCount:0,totalCount:0,recentRunIds:[],label:""}),m={},d={},T={},f=(r,t,s,u,l)=>{const g=r[t]||(r[t]=b());g.label=s||t,g.totalCount+=1,u&&(g.failedCount+=1,g.recentRunIds.length<I&&g.recentRunIds.push(l))};let c=0,n=0;for(const r of h.hits.hits){const t=r._source;c+=1;const s=t.status==="error";s&&(n+=1),t.assistantId&&f(m,t.assistantId,t.assistantName,s,t.runId),t.teamId&&f(d,t.teamId,t.teamName,s,t.runId),t.graphId&&f(T,t.graphId,t.graphId,s,t.runId)}const i=r=>Object.entries(r).filter(([,t])=>t.failedCount>0).map(([t,s])=>({groupKey:t,groupLabel:s.label,failedCount:s.failedCount,totalCount:s.totalCount,failureRate:s.totalCount>0?s.failedCount/s.totalCount*100:0,recentRunIds:s.recentRunIds})).sort((t,s)=>s.failedCount-t.failedCount).slice(0,p);return{totalRuns:c,totalFailed:n,byAssistant:i(m),byTeam:i(d),byGraph:i(T)}}async function j(e,a,o={}){const p=o.samplesPerBucket??3,I=o.topNodes??15,y=E(e,a);y.push({term:{status:"error"}});const h=await M.search({index:B,body:{query:{bool:{must:y}},size:5e3,sort:[{startTime:"desc"}],_source:["runId","startTime","assistantId","assistantName","errorType","errorMessage","errorNodeId"]}}),b=new Map,m=new Map;let d=0;for(const c of h.hits.hits){const n=c._source,i=n.errorType,r=n.errorMessage||"(no message)",t=n.errorNodeId;if(!i)continue;d+=1;const s=b.get(i)||(()=>{const u={count:0,assistantSet:new Set,recentMessages:[]};return b.set(i,u),u})();if(s.count+=1,n.assistantName&&s.assistantSet.add(n.assistantName),s.recentMessages.length<p&&s.recentMessages.push({runId:n.runId,assistantName:n.assistantName,message:C(r,400),timestamp:n.startTime}),t){const u=m.get(t)||(()=>{const l={count:0,recentMessages:[]};return m.set(t,l),l})();u.count+=1,u.recentMessages.length<p&&u.recentMessages.push({runId:n.runId,assistantName:n.assistantName,message:C(r,400),timestamp:n.startTime})}}const T=Array.from(b.entries()).map(([c,n])=>({errorType:c,count:n.count,affectedAssistants:Array.from(n.assistantSet),recentMessages:n.recentMessages})).sort((c,n)=>n.count-c.count),f=Array.from(m.entries()).map(([c,n])=>({nodeId:c,count:n.count,recentMessages:n.recentMessages})).sort((c,n)=>n.count-c.count).slice(0,I);return{totalClassified:d,byType:T,byNode:f}}export{z as getFailedRuns,j as getRunErrorTypes,G as getToolErrors};
@@ -0,0 +1 @@
1
+ import{Client as y}from"@elastic/elasticsearch";import{config as g}from"../config";const u=new y(g.elasticsearch),n=new Map;async function f(s,e){const o=`${s}:${e}`,c=n.get(o);if(c)return c;try{const t=(await u.fieldCaps({index:s,fields:[e,`${e}.keyword`]}))?.fields??{},i=t[e]?Object.keys(t[e])[0]:void 0,l=!!t[`${e}.keyword`];let r;return i==="keyword"?r=e:l?r=`${e}.keyword`:r=e,n.set(o,r),r}catch(a){return console.warn(`[es-field-resolver] field_caps failed for ${s}.${e}: ${a.message}`),e}}function p(){n.clear()}export{f as aggField,p as clearFieldResolverCache};
@@ -0,0 +1 @@
1
+ const t={MCPServer:"mcp-relay",Toolbox:"mcp-relay",OpenAPI:"snapshot-execute"};function a(e){return typeof e=="string"&&e in t}function r(e){return t[e]}function o(e){return e?.serverType==="gateway"}export{a as isGatewayEligiblePlatformType,o as isGatewayRelayedConfig,r as strategyForPlatformType};
@@ -0,0 +1 @@
1
+ import{createMcpClient as g}from"../mcp/client";const p=process.env.GLINER_MCP_URL||"http://127.0.0.1:3712/mcp",o=16,m=.5,f=["technology","product","risk","process","system component","team/role","regulation","metric"];function h(t){return typeof t=="string"?JSON.parse(t):t}function y(t){return t.trim().toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g,"").replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,40)}async function R(t){if(t.length===0)return[];const r=t.map(()=>[]);let n=null;try{n=await g(p,{timeout:15e3});for(let e=0;e<t.length;e+=o){const a=t.slice(e,e+o),i=await n.callTool("batch_extract_entities",{texts:a,labels:f,threshold:m}),c=h(i);for(const s of c.results){const l=Array.from(new Set(s.entities.map(u=>y(u.text)).filter(Boolean)));r[e+s.index]=l}}}catch(e){console.warn(`[gliner-tagging] auto-tag extraction failed, continuing without it: ${e.message}`)}finally{n&&await n.close()}return r}export{p as GLINER_MCP_URL,f as GLINER_TAG_LABELS,R as extractTagsForChunks};
@@ -0,0 +1 @@
1
+ function s(e){if(typeof e=="string")return e;if(e==null)return"";try{return JSON.stringify(e)}catch{return String(e)}}function c(e){return s(e).replace(/(['"])userToken\1(\s*:\s*)(['"])[^'"]*\3/gi,"$1userToken$1$2$3<redacted>$3")}function d(e,r=5){const i=new Set,n=[];for(const t of e)i.has(t.runId)||(i.add(t.runId),n.push(t));return n.filter(t=>t.success&&!t.empty).sort((t,o)=>new Date(o.createdAt).getTime()-new Date(t.createdAt).getTime()).slice(0,r)}function u(e,r=20){return e.length<=r?[]:[...e].sort((n,t)=>new Date(t.createdAt).getTime()-new Date(n.createdAt).getTime()).slice(r).map(n=>n.id)}export{d as pickGoldenCandidates,u as pruneHistory,c as redactUserToken};
@@ -0,0 +1 @@
1
+ import{Client as h}from"@elastic/elasticsearch";import{config as w}from"../config";import{isSuccessfulExecution as I,isEmptyOutput as x,coerceNumber as _}from"./tool-scoring";import{redactUserToken as k,pickGoldenCandidates as b}from"./golden-questions-scoring";import{stringifySample as M}from"./tool-metrics-service";const u=new h(w.elasticsearch),S=".stkxp_chat_tools",A=".stkxp_chat_runs",C=".stkxp_chats",O=".stkxp_tool_metrics",g=".stkxp_golden_questions",y=5;async function T(){try{if(await u.indices.exists({index:g}))return;await u.indices.create({index:g,body:{settings:{number_of_shards:1,number_of_replicas:1},mappings:{properties:{toolName:{type:"keyword"},rank:{type:"integer"},system:{type:"keyword"},owner:{type:"keyword"},userMessage:{type:"text"},toolInput:{type:"object",enabled:!1},toolOutput:{type:"object",enabled:!1},responseText:{type:"text"},success:{type:"boolean"},durationMs:{type:"float"},sourceRunId:{type:"keyword"},sourceChatId:{type:"keyword"},minedFromWindow:{type:"keyword"},minedAt:{type:"date"}}}}}),console.log(`[golden-questions] created index ${g}`)}catch(e){throw console.error("[golden-questions] failed to initialize index:",e),e}}async function E(e){const t=new Map,s=await u.search({index:O,body:{query:{bool:{filter:[{term:{window:e}},{term:{quadrant:"boost"}}]}},_source:["name","system","owner"],size:1e3}});for(const n of s.hits?.hits??[]){const o=n._source??{};o.name&&t.set(o.name,{system:o.system??null,owner:o.owner??null})}return t}async function q(e,t){const s=new Map,n=1e3,o={query:{range:{createdAt:{gte:`now-${t}`}}},_source:["chatId","runId","createdAt","tools"],size:n,sort:[{createdAt:{order:"asc"}}]};let a;for(;;){const d=(await u.search({index:S,body:a?{...o,search_after:a}:o})).hits?.hits??[];if(d.length===0)break;for(const p of d){const r=p._source??{},i=r.tools;if(!(!Array.isArray(i)||!r.runId||!r.chatId||!r.createdAt))for(const c of i){const l=typeof c?.name=="string"?c.name:null;if(!l||!e.has(l))continue;const f=s.get(l)??[];f.push({runId:r.runId,chatId:r.chatId,createdAt:r.createdAt,success:I(c),empty:x(c),toolInput:c.input,toolOutput:c.output,durationMs:_(c.duration)}),s.set(l,f);break}}if(d.length<n)break;a=d[d.length-1].sort}return s}async function R(e){return(await u.search({index:A,body:{query:{term:{runId:e}},_source:["userMessage"],size:1}})).hits?.hits?.[0]?._source?.userMessage??null}async function N(e,t){try{const o=((await u.get({index:C,id:e}))._source?.messages??[]).find(a=>a.toolsRunId===t);return typeof o?.content=="string"?o.content:null}catch(s){if(s?.meta?.statusCode===404)return null;throw s}}async function D(e,t){if(t>=y)return;const s=[];for(let n=t;n<y;n++)s.push({delete:{_index:g,_id:`${e}::${n}`}});try{await u.bulk({operations:s,refresh:!0})}catch(n){console.warn(`[golden-questions] prune stale ranks failed for ${e}:`,n)}}async function $(e){if(e.length===0)return;const t=[];for(const n of e)t.push({index:{_index:g,_id:`${n.toolName}::${n.rank}`}}),t.push(n);const s=await u.bulk({operations:t,refresh:!0});if(s.errors){const n=s.items?.find(o=>o.index?.error)?.index?.error;console.error("[golden-questions] bulk upsert had errors:",n)}}async function X(e="30d"){await T();const t=await E(e);if(t.size===0)return console.log(`[golden-questions] window=${e}: no boost-quadrant tools found`),[];const s=await q(new Set(t.keys()),e),n=new Date().toISOString(),o=[];for(const[a,m]of t){const d=s.get(a)??[],p=b(d,y);for(let r=0;r<p.length;r++){const i=p[r],c=await R(i.runId),l=await N(i.chatId,i.runId);o.push({toolName:a,rank:r,system:m.system,owner:m.owner,userMessage:c??"",toolInput:k(i.toolInput),toolOutput:M(i.toolOutput)?.slice(0,1e3)??null,responseText:l?l.slice(0,1500):null,success:i.success,durationMs:i.durationMs,sourceRunId:i.runId,sourceChatId:i.chatId,minedFromWindow:e,minedAt:n})}await D(a,p.length)}return await $(o),console.log(`[golden-questions] window=${e}: upserted ${o.length} golden questions across ${t.size} tools`),o}export{T as initializeGoldenQuestionsIndex,X as mineGoldenQuestions};
@@ -0,0 +1 @@
1
+ import{resolveAssistant as s}from"./assistant-resolver";import{getDefaultGraphForOwner as n}from"./graph-registry-service";import{getTeamById as a}from"./teams-service";async function u(t,e={resolveAssistant:s,getDefaultGraphForOwner:n}){const r=await e.resolveAssistant(t);return r?r.graphId?r.graphId:r.owner?(await e.getDefaultGraphForOwner(r.owner))?.id??null:null:null}async function m(t,e={getTeamById:a}){return(await e.getTeamById(t))?.graphId??null}export{u as resolveAssistantRuntimeGraphId,m as resolveTeamCoordinationGraphId};
@@ -0,0 +1 @@
1
+ import{randomUUID as b}from"crypto";import{makeOwnerScopedClient as E}from"../utils/owner-scope";const _=process.env.ELASTICSEARCH_HOST||"https://51.159.174.230:9205",G=process.env.ELASTICSEARCH_USER||"elastic",A=process.env.ELASTICSEARCH_PASSWORD||"",p=".stkxp_graphs",h=".stkxp_node_templates",m=".stkxp_validators",s=E({node:_,auth:{username:G,password:A},tls:{rejectUnauthorized:!1},requestTimeout:3e4,maxRetries:3});async function P(){const t=[{name:p,mappings:{properties:{id:{type:"keyword"},name:{type:"text",fields:{keyword:{type:"keyword"}}},description:{type:"text"},version:{type:"keyword"},nodes:{type:"object",enabled:!1},edges:{type:"object",enabled:!1},config:{type:"object",enabled:!1},metadata:{type:"object",enabled:!1},owner:{type:"keyword"},createdAt:{type:"date"},updatedAt:{type:"date"},enabled:{type:"boolean"},isDefault:{type:"boolean"}}},patch:{properties:{name:{type:"text",fields:{keyword:{type:"keyword"}}}}}},{name:h,mappings:{properties:{id:{type:"keyword"},name:{type:"text",fields:{keyword:{type:"keyword"}}},type:{type:"keyword"},description:{type:"text"},code:{type:"text"},parameters:{type:"object",enabled:!1},dependencies:{type:"keyword"},owner:{type:"keyword"},createdAt:{type:"date"},updatedAt:{type:"date"}}},patch:{properties:{name:{type:"text",fields:{keyword:{type:"keyword"}}}}}},{name:m,mappings:{properties:{id:{type:"keyword"},name:{type:"text",fields:{keyword:{type:"keyword"}}},description:{type:"text"},schema:{type:"object",enabled:!1},type:{type:"keyword"},rules:{type:"text"},owner:{type:"keyword"},createdAt:{type:"date"},updatedAt:{type:"date"}}},patch:{properties:{name:{type:"text",fields:{keyword:{type:"keyword"}}}}}}];for(const e of t)try{if(!await s.indices.exists({index:e.name}))console.log(`[GraphRegistry] Creating index: ${e.name}`),await s.indices.create({index:e.name,body:{mappings:e.mappings}}),console.log(`[GraphRegistry] \u2705 Index created: ${e.name}`);else try{await s.indices.putMapping({index:e.name,body:e.patch}),console.log(`[GraphRegistry] \u2705 Mapping patched for ${e.name}`)}catch(n){console.warn(`[GraphRegistry] Could not patch mapping for ${e.name}:`,n.message)}}catch(r){throw console.error(`[GraphRegistry] Error creating index ${e.name}:`,r.message),r}}async function R(t){try{const e=new Date().toISOString(),r={...t,createdAt:e,updatedAt:e},n=await s.index({index:p,id:t.id,document:r,refresh:!0});return console.log(`[GraphRegistry] Created graph: ${t.id}`),n._id}catch(e){throw console.error("[GraphRegistry] Error creating graph:",e.message),new Error(`Failed to create graph: ${e.message}`)}}async function O(t,e){const r=b();return await R({...t,id:r,owner:e,clonedFrom:t.id,isDefault:!1,nodes:JSON.parse(JSON.stringify(t.nodes)),edges:JSON.parse(JSON.stringify(t.edges))}),r}async function y(t){try{const e={bool:{must:[{term:{id:t}}]}},r=await s.search({index:p,body:{query:e,size:1}});return r.hits.hits[0]?r.hits.hits[0]._source:null}catch(e){if(e.meta?.statusCode===404)return null;throw console.error(`[GraphRegistry] Error getting graph ${t}:`,e.message),new Error(`Failed to get graph: ${e.message}`)}}async function f(t){try{const r=(await s.search({index:p,body:{query:{term:{id:t}},size:1}})).hits.hits[0];if(r?._id)return r._id}catch{}return t}async function F(t){try{const e=[];t?.owner&&e.push({term:{owner:t.owner}}),t?.owners&&t.owners.length>0&&e.push({bool:{should:t.owners.map(c=>({term:{owner:c}})),minimum_should_match:1}}),t?.enabled!==void 0&&e.push({term:{enabled:t.enabled}}),t?.tags&&t.tags.length>0&&e.push({terms:{"metadata.tags":t.tags}}),t?.search&&e.push({multi_match:{query:t.search,fields:["name","description"]}});const n=t?.owners?.includes("stkxp")||t?.owner==="stkxp"?[]:[{term:{owner:"stkxp"}}],a={bool:{must:e,must_not:n}},o={name:"name.keyword",updatedAt:"updatedAt"}[t?.sortField||""]||"name.keyword",d=t?.sortOrder==="desc"?"desc":"asc",l=o==="updatedAt"?[{updatedAt:{order:d,unmapped_type:"date"}}]:[{[o]:{order:d,unmapped_type:"keyword"}}];return(await s.search({index:p,body:{query:a,size:100,sort:l}})).hits.hits.map(c=>c._source)}catch(e){throw console.error("[GraphRegistry] Error listing graphs:",e.message),new Error(`Failed to list graphs: ${e.message}`)}}async function q(t,e){try{const r={...e,updatedAt:new Date().toISOString()},n=await f(t),a=Object.keys(r).map(i=>`ctx._source['${i}'] = params.doc['${i}']`).join("; ");await s.update({index:p,id:n,script:{source:a,params:{doc:r}},refresh:!0}),console.log(`[GraphRegistry] Updated graph: ${t} (es _id ${n})`)}catch(r){throw console.error(`[GraphRegistry] Error updating graph ${t}:`,r.message),new Error(`Failed to update graph: ${r.message}`)}}async function U(t){try{const e=await f(t);await s.delete({index:p,id:e,refresh:!0}),console.log(`[GraphRegistry] Deleted graph: ${t} (es _id ${e})`)}catch(e){throw e.meta?.statusCode===404?new Error(`Graph not found: ${t}`):(console.error(`[GraphRegistry] Error deleting graph ${t}:`,e.message),new Error(`Failed to delete graph: ${e.message}`))}}async function V(t){try{const e=await s.search({index:p,body:{query:{bool:{must:[{term:{owner:t}},{term:{isDefault:!0}}]}},size:1}});return e.hits.hits[0]?e.hits.hits[0]._source:null}catch(e){throw console.error(`[GraphRegistry] Error getting default graph for owner ${t}:`,e.message),new Error(`Failed to get default graph: ${e.message}`)}}async function j(t,e){try{await s.updateByQuery({index:p,refresh:!0,body:{script:{source:"ctx._source.isDefault = false",lang:"painless"},query:{bool:{must:[{term:{owner:e}},{term:{isDefault:!0}}]}}}});const r=await f(t);await s.update({index:p,id:r,doc:{isDefault:!0,updatedAt:new Date().toISOString()},refresh:!0}),console.log(`[GraphRegistry] Default graph for owner '${e}' set to: ${t} (es _id ${r})`)}catch(r){throw console.error(`[GraphRegistry] Error setting default graph ${t}:`,r.message),new Error(`Failed to set default graph: ${r.message}`)}}async function L(t){try{const e=new Date().toISOString(),r={...t,createdAt:e,updatedAt:e},n=await s.index({index:h,id:t.id,document:r,refresh:!0});return console.log(`[GraphRegistry] Created node template: ${t.id}`),n._id}catch(e){throw console.error("[GraphRegistry] Error creating node template:",e.message),new Error(`Failed to create node template: ${e.message}`)}}async function z(t){try{return(await s.get({index:h,id:t}))._source}catch(e){if(e.meta?.statusCode===404)return null;throw console.error(`[GraphRegistry] Error getting node template ${t}:`,e.message),new Error(`Failed to get node template: ${e.message}`)}}async function K(t){try{const e=[];t?.type&&e.push({term:{type:t.type}}),t?.owner&&e.push({term:{owner:t.owner}});const r=e.length>0?{bool:{must:e}}:{match_all:{}};return(await s.search({index:h,body:{query:r,size:100,sort:[{updatedAt:{order:"desc"}}]}})).hits.hits.map(a=>a._source)}catch(e){throw console.error("[GraphRegistry] Error listing node templates:",e.message),new Error(`Failed to list node templates: ${e.message}`)}}async function M(t){try{await s.delete({index:h,id:t,refresh:!0}),console.log(`[GraphRegistry] Deleted node template: ${t}`)}catch(e){throw e.meta?.statusCode===404?new Error(`Node template not found: ${t}`):(console.error(`[GraphRegistry] Error deleting node template ${t}:`,e.message),new Error(`Failed to delete node template: ${e.message}`))}}async function H(t){try{const e=new Date().toISOString(),r={...t,createdAt:e,updatedAt:e},n=await s.index({index:m,id:t.id,document:r,refresh:!0});return console.log(`[GraphRegistry] Created validator: ${t.id}`),n._id}catch(e){throw console.error("[GraphRegistry] Error creating validator:",e.message),new Error(`Failed to create validator: ${e.message}`)}}async function X(t){try{return(await s.get({index:m,id:t}))._source}catch(e){if(e.meta?.statusCode===404)return null;throw console.error(`[GraphRegistry] Error getting validator ${t}:`,e.message),new Error(`Failed to get validator: ${e.message}`)}}async function J(t){try{const e=[];t?.type&&e.push({term:{type:t.type}}),t?.owner&&e.push({term:{owner:t.owner}});const r=e.length>0?{bool:{must:e}}:{match_all:{}};return(await s.search({index:m,body:{query:r,size:100,sort:[{updatedAt:{order:"desc"}}]}})).hits.hits.map(a=>a._source)}catch(e){throw console.error("[GraphRegistry] Error listing validators:",e.message),new Error(`Failed to list validators: ${e.message}`)}}async function B(t){try{await s.delete({index:m,id:t,refresh:!0}),console.log(`[GraphRegistry] Deleted validator: ${t}`)}catch(e){throw e.meta?.statusCode===404?new Error(`Validator not found: ${t}`):(console.error(`[GraphRegistry] Error deleting validator ${t}:`,e.message),new Error(`Failed to delete validator: ${e.message}`))}}const S=".stkxp_llm_routing_rules";async function D(t,e=new Set){if(e.has(t))return console.warn(`[GraphRegistry] Circular subgraph reference detected, skipping: ${t}`),[];const r=new Set(e);r.add(t);const n=await y(t);if(!n)throw new Error(`Graph not found: ${t}`);const a=8e3,i=0,o=[];for(const d of n.nodes){const l=d.llm_routing_rule_id,g={id:d.id,name:d.name,type:d.type,maxTokens:a,temperature:i,routingRuleId:l,graphId:n.id,graphName:n.name};if(d.type==="subgraph"&&d.graphId){const c=await D(d.graphId,r);o.push({...g,children:c});continue}if(!l){o.push(g);continue}try{const u=(await s.search({index:S,body:{query:{bool:{must:[{term:{id:l}}]}},size:1}})).hits.hits[0]?._source;if(!u){o.push(g);continue}let w;const x=u.llmModelId||u.modelId;if(x)try{w=(await s.search({index:".stkxp_llm_models",body:{query:{bool:{must:[{term:{modelId:x}}]}},size:1}})).hits.hits[0]?._source?.modelId}catch{}o.push({...g,maxTokens:u.maxTokens??a,temperature:u.temperature??i,model:w})}catch(c){console.error(`[GraphRegistry] Failed to resolve routing rule for node ${d.id}:`,c.message),o.push(g)}}return o}function W(t){const e=[];t.id||e.push("Graph ID is required"),t.name||e.push("Graph name is required"),t.version||e.push("Graph version is required");const r=t.graphType==="kibana_agent",n=t.graphType==="a2a_agent";if(!r&&!n&&((!t.nodes||t.nodes.length===0)&&e.push("Graph must have at least one node"),(!t.edges||t.edges.length===0)&&e.push("Graph must have at least one edge")),r){const o=t.kibanaAgent;o?.agentId||e.push("Kibana agent graph requires kibanaAgent.agentId"),o?.platformId||e.push("Kibana agent graph requires kibanaAgent.platformId")}n&&(t.a2aAgent?.agentUrl||e.push("A2A agent graph requires a2aAgent.agentUrl"));const a=new Set;for(const o of t.nodes||[])a.has(o.id)&&e.push(`Duplicate node ID: ${o.id}`),a.add(o.id);const i=["__start__","__end__"];for(const o of t.edges||[])!a.has(o.from)&&!i.includes(o.from)&&e.push(`Edge references invalid source node: ${o.from}`),!a.has(o.to)&&!i.includes(o.to)&&e.push(`Edge references invalid target node: ${o.to}`);return{valid:e.length===0,errors:e}}async function $(t,e,r=new Set,n=[]){r.add(t);const a=[...n,t];for(const i of e){if(i.type!=="subgraph"||!i.graphId)continue;if(r.has(i.graphId))throw new Error(`Circular dependency detected: ${[...a,i.graphId].join(" \u2192 ")}`);const o=await y(i.graphId);o&&await $(i.graphId,o.nodes,new Set(r),a)}}async function k(t,e=[]){for(const r of t){if(r.type!=="subgraph"||!r.graphId)continue;const n=await y(r.graphId);if(n){if(n.graphType==="kibana_agent"||n.graphType==="a2a_agent"){const a=n.graphType==="kibana_agent"?"Kibana Agent Builder":"A2A Agent";throw new Error(`Subgraph node '${r.id}' references graph '${n.name}' (${r.graphId}), which is a ${a} runtime. ${a} graphs delegate execution outside LangGraph and cannot be used as a nested subgraph node \u2014 only as a root-level assistant/team runtime.`)}await k(n.nodes,[...e,r.graphId])}}}async function I(t,e=0){const r=await y(t);if(!r)return{graphId:t,graphName:"(not found)",depth:e,children:[]};const n=[];for(const a of r.nodes)if(a.type==="subgraph"&&a.graphId){const i=await I(a.graphId,e+1);n.push(i)}return{graphId:t,graphName:r.name,depth:e,children:n}}async function Q(t){return{graphId:t,totalRuns:0,successRate:0,avgDuration:0,lastRun:null}}export{O as cloneGraphForOwner,R as createGraph,L as createNodeTemplate,H as createValidator,U as deleteGraph,M as deleteNodeTemplate,B as deleteValidator,$ as detectCircularDependency,k as detectNonComposableSubgraphs,P as ensureIndices,V as getDefaultGraphForOwner,y as getGraph,I as getGraphDependencyTree,Q as getGraphStats,z as getNodeTemplate,X as getValidator,F as listGraphs,K as listNodeTemplates,J as listValidators,D as resolveNodeConfigs,j as setDefaultGraphForOwner,q as updateGraph,W as validateGraphDefinition};
@@ -0,0 +1 @@
1
+ import{Client as L}from"@elastic/elasticsearch";import{config as M}from"../config";const H=new L(M.elasticsearch),S=".stkxp_chat_traces";function w(e,b){if(e.length===0)return 0;const o=b/100*(e.length-1),a=Math.floor(o),s=Math.ceil(o);if(a===s)return e[a];const c=o-a;return e[a]*(1-c)+e[s]*c}async function j(e,b){const o=[];e&&o.push({range:{createdAt:{gte:e.start,lte:e.end}}}),b&&o.push({term:{username:b}});const a=await H.search({index:S,body:{query:o.length?{bool:{must:o}}:{match_all:{}},size:2e3,_source:["runId","events"],sort:[{createdAt:{order:"desc"}}]}});let s=0,c=0,I=0,y=0;const R=[],h=new Map,p=[];for(const i of a.hits.hits){const r=i._source,E=Array.isArray(r.events)?r.events:[],f=[],T=[];for(const t of E){if(!t||t.kind!=="hitl_interrupt"&&t.kind!=="hitl_resume")continue;if(t.kind==="hitl_interrupt"){f.push(t);continue}const n=f.pop();n&&T.push({interrupt:n,resume:t})}for(const t of f)T.push({interrupt:t});for(const t of T){const n=t.interrupt,d=t.resume;s+=1;const v=n.node||"unknown",g=h.get(v)||(()=>{const m={node:v,count:0,resolved:0,abandoned:0};return h.set(v,m),m})();g.count+=1;let l;if(d){c+=1,g.resolved+=1,d.decision==="rejected"?(y+=1,l="rejected"):(I+=1,l="approved");const m=typeof d.durationMs=="number"&&d.durationMs>=0?d.durationMs:Math.max(d.ts-n.ts,0);R.push(m),p.length<20&&p.push({runId:r.runId,node:n.node,decision:l,durationMs:m,interruptValue:n.interruptValue,ts:n.ts})}else g.abandoned+=1,l="abandoned",p.length<20&&p.push({runId:r.runId,node:n.node,decision:l,interruptValue:n.interruptValue,ts:n.ts})}}const k=s-c,u=R.slice().sort((i,r)=>i-r),_=u.length>0?u.reduce((i,r)=>i+r,0)/u.length:0,A=Array.from(h.values()).sort((i,r)=>r.count-i.count);return{windowStart:e?.start,windowEnd:e?.end,tracesScanned:a.hits.hits.length,totalInterrupts:s,totalResolved:c,totalAbandoned:k,abandonRate:s>0?k/s:0,approved:I,rejected:y,meanTTRms:_,p50TTRms:w(u,50),p95TTRms:w(u,95),maxTTRms:u[u.length-1]??0,byNode:A,recentSamples:p}}export{j as getHITLAnalyticsReport};
@@ -0,0 +1 @@
1
+ import{Client as c}from"@elastic/elasticsearch";import{config as g}from"../config";const o=new c(g.elasticsearch),s=".stkxp_ingestion_jobs";async function d(){try{if(await o.indices.exists({index:s}))return;await o.indices.create({index:s,body:{mappings:{properties:{jobId:{type:"keyword"},owner:{type:"keyword"},sourceType:{type:"keyword"},filename:{type:"keyword"},title:{type:"text"},status:{type:"keyword"},chunksCreated:{type:"integer"},resourceIds:{type:"keyword"},errorMessage:{type:"text"},startTime:{type:"date"},endTime:{type:"date"},duration:{type:"long"},createdAt:{type:"date"},updatedAt:{type:"date"}}},settings:{number_of_shards:1,number_of_replicas:0}}}),console.log(`Created index: ${s}`)}catch(e){throw console.error("Error initializing ingestion jobs index:",e),e}}async function y(e,i,n){await d();const t=new Date().toISOString(),r={jobId:e,owner:i,sourceType:"file",filename:n.filename,title:n.title,status:"running",startTime:t,createdAt:t,updatedAt:t};return{id:(await o.index({index:s,body:r,refresh:"wait_for"}))._id,...r}}async function u(e){await d();const i=await o.search({index:s,body:{query:{term:{jobId:e}},size:1}});if(i.hits.hits.length===0)return null;const n=i.hits.hits[0];return{id:n._id,...n._source}}async function m(e,i,n){try{const t=await u(e);if(!t)return console.warn(`Ingestion job not found: ${e}`),!1;const r=new Date().toISOString(),a=new Date(r).getTime()-new Date(t.startTime).getTime();return await o.update({index:s,id:t.id,body:{doc:{status:i,endTime:r,duration:a,updatedAt:r,...n}},refresh:"wait_for"}),!0}catch(t){return console.error("Error completing ingestion job:",t),!1}}export{m as completeIngestionJob,y as createIngestionJob,u as getIngestionJobById};
@@ -0,0 +1,5 @@
1
+ import{createHash as T}from"crypto";const R=2e3,k=200;function v(n){return n.split("\f").map(t=>t.trim()).filter(t=>t.length>0)}function S(n){return v(n)}function A(n,t=R,e=k){const i=n.trim();if(!i)return[];const r=D(i,/\n\s*\n+/);if(r.length===1){const[s]=r;return s.length<=t?r:P(s,t,e)}return r.flatMap(s=>s.length>t?P(s,t,e):[s])}function D(n,t){return n.split(t).map(e=>e.trim()).filter(e=>e.length>0)}function P(n,t,e){const i=D(n,/\n/);return i.length>1?i.flatMap(r=>r.length>t?E(r,t,e):[r]):E(n,t,e)}function E(n,t,e){if(n.length<=t)return[n];const i=[],r=n.length;let s=0;for(;s<r;){const c=Math.min(s+t,r),l=s+Math.floor(t*.6);let o=C(n,". ",l,c);o===-1&&(o=c);const g=n.slice(s,o).trim();g&&i.push(g);const a=Math.max(o-e,o);if(a>=r)break;s=a}return i.filter(c=>c.trim().length>0)}function C(n,t,e,i){const s=n.slice(e,i).lastIndexOf(t);return s===-1?-1:e+s}const U=/chapter\s+(\d+)[^\n]*/i;function H(n,t){const e=U.exec(n.slice(0,200));return e?`Chapter ${e[1]}`:t}function N(n,t){const{title:e,owner:i,sourceFile:r,author:s="",useCase:c="knowledge base reference",tags:l=[],version:o="",chunkMaxChars:g=R,chunkOverlap:a=k}=t,x=new Date().toISOString(),w=[];let p="Preface";return n.forEach((M,O)=>{const f=O+1,d=M.trim();if(!d)return;p=H(d,p);const h=A(d,g,a);h.forEach((u,m)=>{const y=T("sha1").update(`${r}|${f}|${m}|${u.slice(0,100)}`,"utf-8").digest("hex");let _=`${e} | ${p} | p.${f}`;h.length>1&&(_+=` [${m+1}/${h.length}]`);const $=(u.split(`
2
+ `).map(b=>b.trim()).find(b=>b.length>0)??u.slice(0,100)).slice(0,200),F=`${_}
3
+ ${$}
4
+
5
+ ${u}`;w.push({_id:y,title:_,description:$,prompt_text:u,use_case:c,type:"knowledge_base",tags:l,variables:[],examples:"",owner:i,semantic_content:F,source_file:r,author:s,page_no:f,chunk_idx:m,version:o,created_at:x,updated_at:x})})}),w}async function K(n){const{PDFParse:t}=await import("pdf-parse"),e=new t({data:n});try{return(await e.getText()).pages.map(r=>r.text)}finally{await e.destroy()}}export{R as DEFAULT_CHUNK_MAX_CHARS,k as DEFAULT_CHUNK_OVERLAP,N as buildResourceDocs,A as chunkText,H as detectChapter,K as extractPagesFromPdfBuffer,S as extractPagesFromText,v as pageSplit};
@@ -0,0 +1 @@
1
+ import{KibanaClient as o}from"./kibana-client";import{settingsConfig as s}from"../app-config/settings";class K{static createFromPlatform(a){if(a.type!=="ElasticStack")throw new Error(`Platform type ${a.type} is not supported for Kibana. Only ElasticStack platforms are supported.`);const e=a.config,t=e.endpoints.kibana,n=e.endpoints.elasticsearch;if(!t.url||!t.port)throw new Error(`Platform ${a.name} (${a.id}) is missing required Kibana endpoint configuration`);const i=e.access.apiKeys?.[0]?.name||void 0,c=!!(n.username&&n.password);if(!i&&!c)throw new Error(`Platform ${a.name} (${a.id}) has no credentials: set an API key or username/password`);let r=t.url;try{r=new URL(t.url).hostname}catch{r=t.url.replace(/^https?:\/\//,"")}const m={hostname:r,port:parseInt(t.port,10),basePath:t.basePath||"",credentials:{username:n.username,password:n.password},...i?{apiKey:i}:{},certificates:e.access.certificate?.content||void 0,timeout:6e4,platformId:a.id,platformName:a.name,rejectUnauthorized:!1};return new o(m)}static createFromSettings(){const a=s.kibana,e={hostname:a.hostname,port:a.port,basePath:a.basePath,...a.apiKey?{apiKey:a.apiKey}:{credentials:a.credentials},certificates:s.certificates,timeout:s.defaults.timeout,platformId:"settings",platformName:`${a.hostname}:${a.port}`,rejectUnauthorized:!1};return new o(e)}}export{K as KibanaClientFactory};