drizzle-cube 0.4.18 → 0.4.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const R=require("express"),M=require("cors"),c=require("../mcp-transport-8u9G5oNa.cjs"),n=require("../utils.cjs");function $(g){const{cubes:h,drizzle:w,schema:P,extractSecurityContext:y,engineType:A,cors:x,basePath:f="/cubejs-api/v1",jsonLimit:S="10mb",cache:H,mcp:v={enabled:!0},agent:j}=g;if(!h||h.length===0)throw new Error("At least one cube must be provided in the cubes array");const d=R.Router();x&&d.use(M(x)),d.use(R.json({limit:S})),d.use(R.urlencoded({extended:!0,limit:S}));const u=new c.SemanticLayerCompiler({drizzle:w,schema:P,engineType:A,cache:H});if(h.forEach(r=>{u.registerCube(r)}),d.post(`${f}/load`,async(r,t)=>{try{const e=r.body.query||r.body,o=await y(r,t),s=u.validateQuery(e);if(!s.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${s.errors.join(", ")}`,400));const a=r.headers["x-cache-control"]==="no-cache",i=await u.executeMultiCubeQuery(e,o,{skipCache:a});t.json(n.formatCubeResponse(e,i,u))}catch(e){console.error("Query execution error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),d.get(`${f}/load`,async(r,t)=>{try{const e=r.query.query;if(!e)return t.status(400).json(n.formatErrorResponse("Query parameter is required",400));let o;try{o=JSON.parse(e)}catch{return t.status(400).json(n.formatErrorResponse("Invalid JSON in query parameter",400))}const s=await y(r,t),a=u.validateQuery(o);if(!a.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${a.errors.join(", ")}`,400));const i=r.headers["x-cache-control"]==="no-cache",m=await u.executeMultiCubeQuery(o,s,{skipCache:i});t.json(n.formatCubeResponse(o,m,u))}catch(e){console.error("Query execution error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),d.post(`${f}/batch`,async(r,t)=>{try{const{queries:e}=r.body;if(!e||!Array.isArray(e))return t.status(400).json(n.formatErrorResponse('Request body must contain a "queries" array',400));if(e.length===0)return t.status(400).json(n.formatErrorResponse("Queries array cannot be empty",400));const o=await y(r,t),s=r.headers["x-cache-control"]==="no-cache",a=await n.handleBatchRequest(e,o,u,{skipCache:s});t.json(a)}catch(e){console.error("Batch execution error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Batch execution failed",500))}}),d.get(`${f}/meta`,(r,t)=>{try{const e=u.getMetadata();t.json(n.formatMetaResponse(e))}catch(e){console.error("Metadata error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Failed to fetch metadata",500))}}),d.post(`${f}/sql`,async(r,t)=>{try{const e=r.body,o=await y(r,t),s=u.validateQuery(e);if(!s.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${s.errors.join(", ")}`,400));const a=e.measures?.[0]||e.dimensions?.[0];if(!a)return t.status(400).json(n.formatErrorResponse("No measures or dimensions specified",400));const i=a.split(".")[0],m=await u.generateSQL(i,e,o);t.json(n.formatSqlResponse(e,m))}catch(e){console.error("SQL generation error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),d.get(`${f}/sql`,async(r,t)=>{try{const e=r.query.query;if(!e)return t.status(400).json(n.formatErrorResponse("Query parameter is required",400));const o=JSON.parse(e),s=await y(r,t),a=u.validateQuery(o);if(!a.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${a.errors.join(", ")}`,400));const i=o.measures?.[0]||o.dimensions?.[0];if(!i)return t.status(400).json(n.formatErrorResponse("No measures or dimensions specified",400));const m=i.split(".")[0],p=await u.generateSQL(m,o,s);t.json(n.formatSqlResponse(o,p))}catch(e){console.error("SQL generation error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),d.post(`${f}/dry-run`,async(r,t)=>{try{const e=r.body.query||r.body,o=await y(r,t),s=await n.handleDryRun(e,o,u);t.json(s)}catch(e){console.error("Dry-run error:",e),t.status(400).json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),d.get(`${f}/dry-run`,async(r,t)=>{try{const e=r.query.query;if(!e)return t.status(400).json({error:"Query parameter is required",valid:!1});const o=JSON.parse(e),s=await y(r,t),a=await n.handleDryRun(o,s,u);t.json(a)}catch(e){console.error("Dry-run error:",e),t.status(400).json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),d.post(`${f}/explain`,async(r,t)=>{try{const e=r.body.query||r.body,o=r.body.options||{},s=await y(r,t),a=u.validateQuery(e);if(!a.isValid)return t.status(400).json({error:`Query validation failed: ${a.errors.join(", ")}`});const i=await u.explainQuery(e,s,o);t.json(i)}catch(e){console.error("Explain error:",e),t.status(500).json({error:e instanceof Error?e.message:"Explain query failed"})}}),j&&d.post(`${f}/agent/chat`,async(r,t)=>{try{const{handleAgentChat:e}=await Promise.resolve().then(()=>require("../handler-hwoGzGex.cjs")),{message:o,sessionId:s,history:a}=r.body;if(!o||typeof o!="string")return t.status(400).json({error:"message is required and must be a string"});let i=(j.apiKey||"").trim();if(j.allowClientApiKey){const p=r.headers["x-agent-api-key"];p&&(i=p.trim())}if(!i)return t.status(401).json({error:"No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header."});const m=await y(r,t);t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"});try{const p=e({message:o,sessionId:s,history:a,semanticLayer:u,securityContext:m,agentConfig:j,apiKey:i});for await(const l of p)t.write(`data: ${JSON.stringify(l)}
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const R=require("express"),M=require("cors"),c=require("../mcp-transport-8u9G5oNa.cjs"),n=require("../utils.cjs");function $(g){const{cubes:h,drizzle:w,schema:P,extractSecurityContext:y,engineType:A,cors:x,basePath:f="/cubejs-api/v1",jsonLimit:S="10mb",cache:H,mcp:v={enabled:!0},agent:j}=g;if(!h||h.length===0)throw new Error("At least one cube must be provided in the cubes array");const d=R.Router();x&&d.use(M(x)),d.use(R.json({limit:S})),d.use(R.urlencoded({extended:!0,limit:S}));const u=new c.SemanticLayerCompiler({drizzle:w,schema:P,engineType:A,cache:H});if(h.forEach(r=>{u.registerCube(r)}),d.post(`${f}/load`,async(r,t)=>{try{const e=r.body.query||r.body,o=await y(r,t),s=u.validateQuery(e);if(!s.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${s.errors.join(", ")}`,400));const a=r.headers["x-cache-control"]==="no-cache",i=await u.executeMultiCubeQuery(e,o,{skipCache:a});t.json(n.formatCubeResponse(e,i,u))}catch(e){console.error("Query execution error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),d.get(`${f}/load`,async(r,t)=>{try{const e=r.query.query;if(!e)return t.status(400).json(n.formatErrorResponse("Query parameter is required",400));let o;try{o=JSON.parse(e)}catch{return t.status(400).json(n.formatErrorResponse("Invalid JSON in query parameter",400))}const s=await y(r,t),a=u.validateQuery(o);if(!a.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${a.errors.join(", ")}`,400));const i=r.headers["x-cache-control"]==="no-cache",m=await u.executeMultiCubeQuery(o,s,{skipCache:i});t.json(n.formatCubeResponse(o,m,u))}catch(e){console.error("Query execution error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),d.post(`${f}/batch`,async(r,t)=>{try{const{queries:e}=r.body;if(!e||!Array.isArray(e))return t.status(400).json(n.formatErrorResponse('Request body must contain a "queries" array',400));if(e.length===0)return t.status(400).json(n.formatErrorResponse("Queries array cannot be empty",400));const o=await y(r,t),s=r.headers["x-cache-control"]==="no-cache",a=await n.handleBatchRequest(e,o,u,{skipCache:s});t.json(a)}catch(e){console.error("Batch execution error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Batch execution failed",500))}}),d.get(`${f}/meta`,(r,t)=>{try{const e=u.getMetadata();t.json(n.formatMetaResponse(e))}catch(e){console.error("Metadata error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"Failed to fetch metadata",500))}}),d.post(`${f}/sql`,async(r,t)=>{try{const e=r.body,o=await y(r,t),s=u.validateQuery(e);if(!s.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${s.errors.join(", ")}`,400));const a=e.measures?.[0]||e.dimensions?.[0];if(!a)return t.status(400).json(n.formatErrorResponse("No measures or dimensions specified",400));const i=a.split(".")[0],m=await u.generateSQL(i,e,o);t.json(n.formatSqlResponse(e,m))}catch(e){console.error("SQL generation error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),d.get(`${f}/sql`,async(r,t)=>{try{const e=r.query.query;if(!e)return t.status(400).json(n.formatErrorResponse("Query parameter is required",400));const o=JSON.parse(e),s=await y(r,t),a=u.validateQuery(o);if(!a.isValid)return t.status(400).json(n.formatErrorResponse(`Query validation failed: ${a.errors.join(", ")}`,400));const i=o.measures?.[0]||o.dimensions?.[0];if(!i)return t.status(400).json(n.formatErrorResponse("No measures or dimensions specified",400));const m=i.split(".")[0],p=await u.generateSQL(m,o,s);t.json(n.formatSqlResponse(o,p))}catch(e){console.error("SQL generation error:",e),t.status(500).json(n.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),d.post(`${f}/dry-run`,async(r,t)=>{try{const e=r.body.query||r.body,o=await y(r,t),s=await n.handleDryRun(e,o,u);t.json(s)}catch(e){console.error("Dry-run error:",e),t.status(400).json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),d.get(`${f}/dry-run`,async(r,t)=>{try{const e=r.query.query;if(!e)return t.status(400).json({error:"Query parameter is required",valid:!1});const o=JSON.parse(e),s=await y(r,t),a=await n.handleDryRun(o,s,u);t.json(a)}catch(e){console.error("Dry-run error:",e),t.status(400).json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),d.post(`${f}/explain`,async(r,t)=>{try{const e=r.body.query||r.body,o=r.body.options||{},s=await y(r,t),a=u.validateQuery(e);if(!a.isValid)return t.status(400).json({error:`Query validation failed: ${a.errors.join(", ")}`});const i=await u.explainQuery(e,s,o);t.json(i)}catch(e){console.error("Explain error:",e),t.status(500).json({error:e instanceof Error?e.message:"Explain query failed"})}}),j&&d.post(`${f}/agent/chat`,async(r,t)=>{try{const{handleAgentChat:e}=await Promise.resolve().then(()=>require("../handler-D4MVKkVy.cjs")),{message:o,sessionId:s,history:a}=r.body;if(!o||typeof o!="string")return t.status(400).json({error:"message is required and must be a string"});let i=(j.apiKey||"").trim();if(j.allowClientApiKey){const p=r.headers["x-agent-api-key"];p&&(i=p.trim())}if(!i)return t.status(401).json({error:"No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header."});const m=await y(r,t);t.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"});try{const p=e({message:o,sessionId:s,history:a,semanticLayer:u,securityContext:m,agentConfig:j,apiKey:i});for await(const l of p)t.write(`data: ${JSON.stringify(l)}
2
2
 
3
3
  `)}catch(p){const l={type:"error",data:{message:p instanceof Error?p.message:"Stream failed"}};t.write(`data: ${JSON.stringify(l)}
4
4
 
@@ -198,7 +198,7 @@ function W(h) {
198
198
  }
199
199
  }), j && c.post(`${p}/agent/chat`, async (r, t) => {
200
200
  try {
201
- const { handleAgentChat: e } = await import("../handler-DefTXJpi.js"), { message: o, sessionId: a, history: n } = r.body;
201
+ const { handleAgentChat: e } = await import("../handler-BV4JuWNW.js"), { message: o, sessionId: a, history: n } = r.body;
202
202
  if (!o || typeof o != "string")
203
203
  return t.status(400).json({ error: "message is required and must be a string" });
204
204
  let s = (j.apiKey || "").trim();
@@ -1,4 +1,4 @@
1
- "use strict";var O=Object.create;var j=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var L=Object.getPrototypeOf,D=Object.prototype.hasOwnProperty;var H=(p,n,b,C)=>{if(n&&typeof n=="object"||typeof n=="function")for(let h of J(n))!D.call(p,h)&&h!==b&&j(p,h,{get:()=>n[h],enumerable:!(C=k(n,h))||C.enumerable});return p};var T=(p,n,b)=>(b=p!=null?O(L(p)):{},H(n||!p||!p.__esModule?j(b,"default",{value:p,enumerable:!0}):b,p));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("../mcp-transport-8u9G5oNa.cjs"),a=require("../utils.cjs"),q=function(n,b,C){const{cubes:h,drizzle:I,schema:A,extractSecurityContext:m,engineType:M,cors:P,basePath:g="/cubejs-api/v1",bodyLimit:v=10485760,cache:N,mcp:R={enabled:!0},agent:w}=b;if(!h||h.length===0)return C(new Error("At least one cube must be provided in the cubes array"));P&&n.register(import("@fastify/cors"),P),n.addHook("onRequest",async(r,t)=>{r.method==="POST"&&(r.body=void 0)});const d=new u.SemanticLayerCompiler({drizzle:I,schema:A,engineType:M,cache:N});if(h.forEach(r=>{d.registerCube(r)}),n.post(`${g}/load`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=e.query||e,s=await m(r),i=d.validateQuery(o);if(!i.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${i.errors.join(", ")}`,400));const c=r.headers["x-cache-control"]==="no-cache",y=await d.executeMultiCubeQuery(o,s,{skipCache:c});return a.formatCubeResponse(o,y,d)}catch(e){return r.log.error(e,"Query execution error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),n.get(`${g}/load`,{schema:{querystring:{type:"object",properties:{query:{type:"string"}},required:["query"]}}},async(r,t)=>{try{const{query:e}=r.query;let o;try{o=JSON.parse(e)}catch{return t.status(400).send(a.formatErrorResponse("Invalid JSON in query parameter",400))}const s=await m(r),i=d.validateQuery(o);if(!i.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${i.errors.join(", ")}`,400));const c=r.headers["x-cache-control"]==="no-cache",y=await d.executeMultiCubeQuery(o,s,{skipCache:c});return a.formatCubeResponse(o,y,d)}catch(e){return r.log.error(e,"Query execution error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),n.post(`${g}/batch`,{bodyLimit:v,schema:{body:{type:"object",required:["queries"],properties:{queries:{type:"array",items:{type:"object"}}}}}},async(r,t)=>{try{const{queries:e}=r.body;if(!e||!Array.isArray(e))return t.status(400).send(a.formatErrorResponse('Request body must contain a "queries" array',400));if(e.length===0)return t.status(400).send(a.formatErrorResponse("Queries array cannot be empty",400));const o=await m(r),s=r.headers["x-cache-control"]==="no-cache";return await a.handleBatchRequest(e,o,d,{skipCache:s})}catch(e){return r.log.error(e,"Batch execution error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Batch execution failed",500))}}),n.get(`${g}/meta`,async(r,t)=>{try{const e=d.getMetadata();return a.formatMetaResponse(e)}catch(e){return r.log.error(e,"Metadata error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Failed to fetch metadata",500))}}),n.post(`${g}/sql`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=await m(r),s=d.validateQuery(e);if(!s.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${s.errors.join(", ")}`,400));const i=e.measures?.[0]||e.dimensions?.[0];if(!i)return t.status(400).send(a.formatErrorResponse("No measures or dimensions specified",400));const c=i.split(".")[0],y=await d.generateSQL(c,e,o);return a.formatSqlResponse(e,y)}catch(e){return r.log.error(e,"SQL generation error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),n.get(`${g}/sql`,{schema:{querystring:{type:"object",properties:{query:{type:"string"}},required:["query"]}}},async(r,t)=>{try{const{query:e}=r.query,o=JSON.parse(e),s=await m(r),i=d.validateQuery(o);if(!i.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${i.errors.join(", ")}`,400));const c=o.measures?.[0]||o.dimensions?.[0];if(!c)return t.status(400).send(a.formatErrorResponse("No measures or dimensions specified",400));const y=c.split(".")[0],E=await d.generateSQL(y,o,s);return a.formatSqlResponse(o,E)}catch(e){return r.log.error(e,"SQL generation error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),n.post(`${g}/dry-run`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=e.query||e,s=await m(r);return await a.handleDryRun(o,s,d)}catch(e){return r.log.error(e,"Dry-run error"),t.status(400).send({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),n.get(`${g}/dry-run`,{schema:{querystring:{type:"object",properties:{query:{type:"string"}},required:["query"]}}},async(r,t)=>{try{const{query:e}=r.query,o=JSON.parse(e),s=await m(r);return await a.handleDryRun(o,s,d)}catch(e){return r.log.error(e,"Dry-run error"),t.status(400).send({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),n.post(`${g}/explain`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=e.query||e,s=e.options||{},i=await m(r),c=d.validateQuery(o);return c.isValid?await d.explainQuery(o,i,s):t.status(400).send({error:`Query validation failed: ${c.errors.join(", ")}`})}catch(e){return r.log.error(e,"Explain error"),t.status(500).send({error:e instanceof Error?e.message:"Explain query failed"})}}),w&&n.post(`${g}/agent/chat`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const{handleAgentChat:e}=await Promise.resolve().then(()=>require("../handler-hwoGzGex.cjs")),o=r.body,{message:s,sessionId:i,history:c}=o;if(!s||typeof s!="string")return t.status(400).send({error:"message is required and must be a string"});let y=(w.apiKey||"").trim();if(w.allowClientApiKey){const l=r.headers["x-agent-api-key"];l&&(y=l.trim())}if(!y)return t.status(401).send({error:"No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header."});const E=await m(r);t.raw.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"});try{const l=e({message:s,sessionId:i,history:c,semanticLayer:d,securityContext:E,agentConfig:w,apiKey:y});for await(const f of l)t.raw.write(`data: ${JSON.stringify(f)}
1
+ "use strict";var O=Object.create;var j=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var J=Object.getOwnPropertyNames;var L=Object.getPrototypeOf,D=Object.prototype.hasOwnProperty;var H=(p,n,b,C)=>{if(n&&typeof n=="object"||typeof n=="function")for(let h of J(n))!D.call(p,h)&&h!==b&&j(p,h,{get:()=>n[h],enumerable:!(C=k(n,h))||C.enumerable});return p};var T=(p,n,b)=>(b=p!=null?O(L(p)):{},H(n||!p||!p.__esModule?j(b,"default",{value:p,enumerable:!0}):b,p));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("../mcp-transport-8u9G5oNa.cjs"),a=require("../utils.cjs"),q=function(n,b,C){const{cubes:h,drizzle:I,schema:A,extractSecurityContext:m,engineType:M,cors:P,basePath:g="/cubejs-api/v1",bodyLimit:v=10485760,cache:N,mcp:R={enabled:!0},agent:w}=b;if(!h||h.length===0)return C(new Error("At least one cube must be provided in the cubes array"));P&&n.register(import("@fastify/cors"),P),n.addHook("onRequest",async(r,t)=>{r.method==="POST"&&(r.body=void 0)});const d=new u.SemanticLayerCompiler({drizzle:I,schema:A,engineType:M,cache:N});if(h.forEach(r=>{d.registerCube(r)}),n.post(`${g}/load`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=e.query||e,s=await m(r),i=d.validateQuery(o);if(!i.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${i.errors.join(", ")}`,400));const c=r.headers["x-cache-control"]==="no-cache",y=await d.executeMultiCubeQuery(o,s,{skipCache:c});return a.formatCubeResponse(o,y,d)}catch(e){return r.log.error(e,"Query execution error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),n.get(`${g}/load`,{schema:{querystring:{type:"object",properties:{query:{type:"string"}},required:["query"]}}},async(r,t)=>{try{const{query:e}=r.query;let o;try{o=JSON.parse(e)}catch{return t.status(400).send(a.formatErrorResponse("Invalid JSON in query parameter",400))}const s=await m(r),i=d.validateQuery(o);if(!i.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${i.errors.join(", ")}`,400));const c=r.headers["x-cache-control"]==="no-cache",y=await d.executeMultiCubeQuery(o,s,{skipCache:c});return a.formatCubeResponse(o,y,d)}catch(e){return r.log.error(e,"Query execution error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Query execution failed",500))}}),n.post(`${g}/batch`,{bodyLimit:v,schema:{body:{type:"object",required:["queries"],properties:{queries:{type:"array",items:{type:"object"}}}}}},async(r,t)=>{try{const{queries:e}=r.body;if(!e||!Array.isArray(e))return t.status(400).send(a.formatErrorResponse('Request body must contain a "queries" array',400));if(e.length===0)return t.status(400).send(a.formatErrorResponse("Queries array cannot be empty",400));const o=await m(r),s=r.headers["x-cache-control"]==="no-cache";return await a.handleBatchRequest(e,o,d,{skipCache:s})}catch(e){return r.log.error(e,"Batch execution error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Batch execution failed",500))}}),n.get(`${g}/meta`,async(r,t)=>{try{const e=d.getMetadata();return a.formatMetaResponse(e)}catch(e){return r.log.error(e,"Metadata error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"Failed to fetch metadata",500))}}),n.post(`${g}/sql`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=await m(r),s=d.validateQuery(e);if(!s.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${s.errors.join(", ")}`,400));const i=e.measures?.[0]||e.dimensions?.[0];if(!i)return t.status(400).send(a.formatErrorResponse("No measures or dimensions specified",400));const c=i.split(".")[0],y=await d.generateSQL(c,e,o);return a.formatSqlResponse(e,y)}catch(e){return r.log.error(e,"SQL generation error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),n.get(`${g}/sql`,{schema:{querystring:{type:"object",properties:{query:{type:"string"}},required:["query"]}}},async(r,t)=>{try{const{query:e}=r.query,o=JSON.parse(e),s=await m(r),i=d.validateQuery(o);if(!i.isValid)return t.status(400).send(a.formatErrorResponse(`Query validation failed: ${i.errors.join(", ")}`,400));const c=o.measures?.[0]||o.dimensions?.[0];if(!c)return t.status(400).send(a.formatErrorResponse("No measures or dimensions specified",400));const y=c.split(".")[0],E=await d.generateSQL(y,o,s);return a.formatSqlResponse(o,E)}catch(e){return r.log.error(e,"SQL generation error"),t.status(500).send(a.formatErrorResponse(e instanceof Error?e.message:"SQL generation failed",500))}}),n.post(`${g}/dry-run`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=e.query||e,s=await m(r);return await a.handleDryRun(o,s,d)}catch(e){return r.log.error(e,"Dry-run error"),t.status(400).send({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),n.get(`${g}/dry-run`,{schema:{querystring:{type:"object",properties:{query:{type:"string"}},required:["query"]}}},async(r,t)=>{try{const{query:e}=r.query,o=JSON.parse(e),s=await m(r);return await a.handleDryRun(o,s,d)}catch(e){return r.log.error(e,"Dry-run error"),t.status(400).send({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1})}}),n.post(`${g}/explain`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const e=r.body,o=e.query||e,s=e.options||{},i=await m(r),c=d.validateQuery(o);return c.isValid?await d.explainQuery(o,i,s):t.status(400).send({error:`Query validation failed: ${c.errors.join(", ")}`})}catch(e){return r.log.error(e,"Explain error"),t.status(500).send({error:e instanceof Error?e.message:"Explain query failed"})}}),w&&n.post(`${g}/agent/chat`,{bodyLimit:v,schema:{body:{type:"object",additionalProperties:!0}}},async(r,t)=>{try{const{handleAgentChat:e}=await Promise.resolve().then(()=>require("../handler-D4MVKkVy.cjs")),o=r.body,{message:s,sessionId:i,history:c}=o;if(!s||typeof s!="string")return t.status(400).send({error:"message is required and must be a string"});let y=(w.apiKey||"").trim();if(w.allowClientApiKey){const l=r.headers["x-agent-api-key"];l&&(y=l.trim())}if(!y)return t.status(401).send({error:"No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header."});const E=await m(r);t.raw.writeHead(200,{"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"});try{const l=e({message:s,sessionId:i,history:c,semanticLayer:d,securityContext:E,agentConfig:w,apiKey:y});for await(const f of l)t.raw.write(`data: ${JSON.stringify(f)}
2
2
 
3
3
  `)}catch(l){const f={type:"error",data:{message:l instanceof Error?l.message:"Stream failed"}};t.raw.write(`data: ${JSON.stringify(f)}
4
4
 
@@ -262,7 +262,7 @@ const N = function(i, k, R) {
262
262
  }
263
263
  }, async (t, r) => {
264
264
  try {
265
- const { handleAgentChat: e } = await import("../handler-DefTXJpi.js"), a = t.body, { message: n, sessionId: s, history: o } = a;
265
+ const { handleAgentChat: e } = await import("../handler-BV4JuWNW.js"), a = t.body, { message: n, sessionId: s, history: o } = a;
266
266
  if (!n || typeof n != "string")
267
267
  return r.status(400).send({ error: "message is required and must be a string" });
268
268
  let l = (v.apiKey || "").trim();
@@ -1752,11 +1752,11 @@ function Te(l, t, a) {
1752
1752
  return t ?? {};
1753
1753
  const d = { ...t }, u = a.measures ?? [], e = a.dimensions ?? [], r = (a.timeDimensions ?? []).map((s) => s.dimension);
1754
1754
  for (const s of i.dropZones) {
1755
- const m = d[s.key];
1756
- if (Array.isArray(m) ? m.length > 0 : !!m) continue;
1757
- const y = s.acceptTypes ?? [];
1755
+ const h = d[s.key];
1756
+ if (Array.isArray(h) ? h.length > 0 : !!h) continue;
1757
+ const c = s.acceptTypes ?? [];
1758
1758
  if (s.key === "sizeField" || s.key === "colorField") {
1759
- if (y.includes("measure")) {
1759
+ if (c.includes("measure")) {
1760
1760
  const g = /* @__PURE__ */ new Set();
1761
1761
  for (const C of i.dropZones) {
1762
1762
  if (C.key === s.key) continue;
@@ -1769,7 +1769,7 @@ function Te(l, t, a) {
1769
1769
  continue;
1770
1770
  }
1771
1771
  const n = [];
1772
- if (y.includes("dimension") && n.push(...e), y.includes("timeDimension") && n.push(...r), y.includes("measure") && n.push(...u), n.length === 0) continue;
1772
+ if (c.includes("dimension") && n.push(...e), c.includes("timeDimension") && n.push(...r), c.includes("measure") && n.push(...u), n.length === 0) continue;
1773
1773
  let x = n;
1774
1774
  if (s.key === "series") {
1775
1775
  const g = new Set(
@@ -1777,8 +1777,8 @@ function Te(l, t, a) {
1777
1777
  );
1778
1778
  if (x = n.filter((A) => !g.has(A)), x.length === 0) continue;
1779
1779
  }
1780
- const b = s.maxItems ?? 1 / 0, c = x.slice(0, b);
1781
- c.length > 0 && (d[s.key] = c);
1780
+ const b = s.maxItems ?? 1 / 0, y = x.slice(0, b);
1781
+ y.length > 0 && (d[s.key] = y);
1782
1782
  }
1783
1783
  return d;
1784
1784
  }
@@ -1788,7 +1788,7 @@ Chart config requirements by type:`];
1788
1788
  for (const a of l) {
1789
1789
  const i = P[a];
1790
1790
  if (!i) continue;
1791
- const d = i.description ?? "", u = i.useCase ?? "", e = [d, u].filter(Boolean).join(". "), o = e ? ` — ${e}.` : "", r = i.dropZones.filter((m) => m.mandatory);
1791
+ const d = i.description ?? "", u = i.useCase ?? "", e = [d, u].filter(Boolean).join(". "), o = e ? ` — ${e}.` : "", r = i.dropZones.filter((h) => h.mandatory);
1792
1792
  if (r.length === 0 && !i.skipQuery) {
1793
1793
  t.push(` ${a}${o} chartConfig auto-inferred from query.`);
1794
1794
  continue;
@@ -1797,9 +1797,9 @@ Chart config requirements by type:`];
1797
1797
  t.push(` ${a}${o} No query needed.`);
1798
1798
  continue;
1799
1799
  }
1800
- const s = r.map((m) => {
1801
- const f = m.acceptTypes?.join("/") ?? "any", y = m.maxItems ? ` (max ${m.maxItems})` : "";
1802
- return `${m.key}=[${f}]${y}`;
1800
+ const s = r.map((h) => {
1801
+ const m = h.acceptTypes?.join("/") ?? "any", c = h.maxItems ? ` (max ${h.maxItems})` : "";
1802
+ return `${h.key}=[${m}]${c}`;
1803
1803
  });
1804
1804
  t.push(` ${a}${o} Requires ${s.join(", ")}.`);
1805
1805
  }
@@ -2143,51 +2143,57 @@ function Se(l) {
2143
2143
  });
2144
2144
  const u = (e, o) => {
2145
2145
  const r = d.get(e), s = o === "measures" ? r?.measures : r?.dimensions;
2146
- return !s || s.length === 0 ? "" : ` Available ${o}: ${s.slice(0, 5).map((m) => `"${m}"`).join(", ")}`;
2146
+ return !s || s.length === 0 ? "" : ` Available ${o}: ${s.slice(0, 5).map((h) => `"${h}"`).join(", ")}`;
2147
2147
  };
2148
2148
  return i.set("execute_query", async (e) => {
2149
2149
  try {
2150
- const o = (f, y) => {
2151
- if (!Array.isArray(f)) return;
2150
+ const o = (m, c) => {
2151
+ if (!Array.isArray(m)) return;
2152
2152
  const n = [], x = [];
2153
- for (const b of f) {
2153
+ for (const b of m) {
2154
2154
  if (typeof b != "string") {
2155
2155
  x.push(b);
2156
2156
  continue;
2157
2157
  }
2158
- const c = b.split(".");
2159
- if (c.length === 1) {
2160
- n.push(`"${b}" is not valid — must be "CubeName.fieldName".${u(b, y)}`);
2158
+ const y = b.split(".");
2159
+ if (y.length === 1) {
2160
+ n.push(`"${b}" is not valid — must be "CubeName.fieldName".${u(b, c)}`);
2161
2161
  continue;
2162
2162
  }
2163
- if (c.length === 3 && c[0] === c[1]) {
2164
- const g = `${c[0]}.${c[2]}`;
2163
+ if (y.length === 3 && y[0] === y[1]) {
2164
+ const g = `${y[0]}.${y[2]}`;
2165
2165
  x.push(g);
2166
2166
  continue;
2167
2167
  }
2168
- if (c.length === 2 && c[0] === c[1]) {
2169
- n.push(`"${b}" is WRONG — "${c[0]}" is the cube name, not a ${y.replace(/s$/, "")}.${u(c[0], y)}`);
2168
+ if (y.length === 2 && y[0] === y[1]) {
2169
+ n.push(`"${b}" is WRONG — "${y[0]}" is the cube name, not a ${c.replace(/s$/, "")}.${u(y[0], c)}`);
2170
2170
  continue;
2171
2171
  }
2172
2172
  x.push(b);
2173
2173
  }
2174
2174
  if (n.length > 0)
2175
- throw new Error(`Invalid ${y}:
2175
+ throw new Error(`Invalid ${c}:
2176
2176
  ${n.join(`
2177
2177
  `)}`);
2178
2178
  return x;
2179
2179
  };
2180
2180
  e.measures = o(e.measures, "measures") ?? e.measures, e.dimensions = o(e.dimensions, "dimensions") ?? e.dimensions;
2181
- const r = (f) => {
2182
- const y = f.split(".");
2183
- return y.length === 3 && y[0] === y[1] ? `${y[0]}.${y[2]}` : f;
2181
+ const r = (m) => {
2182
+ const c = m.split(".");
2183
+ return c.length === 3 && c[0] === c[1] ? `${c[0]}.${c[2]}` : m;
2184
2184
  };
2185
2185
  if (Array.isArray(e.filters))
2186
- for (const f of e.filters)
2187
- typeof f.member == "string" && (f.member = r(f.member));
2186
+ for (const m of e.filters)
2187
+ typeof m.member == "string" && (m.member = r(m.member));
2188
2188
  if (Array.isArray(e.timeDimensions))
2189
- for (const f of e.timeDimensions)
2190
- typeof f.dimension == "string" && (f.dimension = r(f.dimension));
2189
+ for (const m of e.timeDimensions)
2190
+ typeof m.dimension == "string" && (m.dimension = r(m.dimension));
2191
+ if (e.order && typeof e.order == "object" && !Array.isArray(e.order)) {
2192
+ const m = {};
2193
+ for (const [c, n] of Object.entries(e.order))
2194
+ m[r(c)] = n;
2195
+ e.order = m;
2196
+ }
2191
2197
  let s;
2192
2198
  e.funnel ? s = { funnel: e.funnel } : e.flow ? s = { flow: e.flow } : e.retention ? s = { retention: e.retention } : s = {
2193
2199
  measures: e.measures,
@@ -2197,12 +2203,12 @@ ${n.join(`
2197
2203
  order: e.order,
2198
2204
  limit: e.limit
2199
2205
  };
2200
- const m = await G(t, a, { query: s });
2206
+ const h = await G(t, a, { query: s });
2201
2207
  return {
2202
2208
  result: JSON.stringify({
2203
- rowCount: m.data.length,
2204
- data: m.data,
2205
- annotation: m.annotation
2209
+ rowCount: h.data.length,
2210
+ data: h.data,
2211
+ annotation: h.annotation
2206
2212
  }, null, 2)
2207
2213
  };
2208
2214
  } catch (o) {
@@ -2239,38 +2245,38 @@ ${JSON.stringify(r, null, 2)}`,
2239
2245
  isError: !0
2240
2246
  };
2241
2247
  }
2242
- const m = t.validateQuery(s);
2243
- if (!m.isValid)
2248
+ const h = t.validateQuery(s);
2249
+ if (!h.isValid)
2244
2250
  return {
2245
2251
  result: `Invalid query — fix these errors and retry:
2246
- ${m.errors.join(`
2252
+ ${h.errors.join(`
2247
2253
  `)}
2248
2254
 
2249
2255
  Attempted query:
2250
2256
  ${JSON.stringify(s, null, 2)}`,
2251
2257
  isError: !0
2252
2258
  };
2253
- const f = !!(s.funnel || s.flow || s.retention);
2254
- let y;
2255
- if (f)
2256
- y = e.chartConfig ?? {};
2259
+ const m = !!(s.funnel || s.flow || s.retention);
2260
+ let c;
2261
+ if (m)
2262
+ c = e.chartConfig ?? {};
2257
2263
  else {
2258
- const b = Te(r, e.chartConfig, s), c = Ce(r, b, s);
2259
- if (!c.isValid)
2264
+ const b = Te(r, e.chartConfig, s), y = Ce(r, b, s);
2265
+ if (!y.isValid)
2260
2266
  return {
2261
2267
  result: `Chart config invalid — fix these errors and retry:
2262
- ${c.errors.join(`
2268
+ ${y.errors.join(`
2263
2269
  `)}`,
2264
2270
  isError: !0
2265
2271
  };
2266
- y = b;
2272
+ c = b;
2267
2273
  }
2268
2274
  const n = `portlet-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`, x = {
2269
2275
  id: n,
2270
2276
  title: e.title,
2271
2277
  query: e.query,
2272
2278
  chartType: r,
2273
- chartConfig: y,
2279
+ chartConfig: c,
2274
2280
  displayConfig: e.displayConfig
2275
2281
  };
2276
2282
  return {
@@ -2300,14 +2306,14 @@ ${c.errors.join(`
2300
2306
  r.push(`Portlet "${n.title}": missing query`);
2301
2307
  continue;
2302
2308
  }
2303
- let c;
2309
+ let y;
2304
2310
  try {
2305
- c = JSON.parse(b);
2311
+ y = JSON.parse(b);
2306
2312
  } catch {
2307
2313
  r.push(`Portlet "${n.title}": invalid JSON query`);
2308
2314
  continue;
2309
2315
  }
2310
- const g = t.validateQuery(c);
2316
+ const g = t.validateQuery(y);
2311
2317
  g.isValid || r.push(`Portlet "${n.title}": ${g.errors.join(", ")}`);
2312
2318
  }
2313
2319
  if (r.length > 0)
@@ -2319,7 +2325,7 @@ ${r.join(`
2319
2325
  };
2320
2326
  const s = {
2321
2327
  portlets: o.map((n) => {
2322
- const x = n.chartType, b = x === "markdown", c = b ? "query" : n.analysisType || "query", g = c === "funnel" ? "funnel" : c === "flow" ? "flow" : c === "retention" ? "retention" : "query", A = n.query || "{}";
2328
+ const x = n.chartType, b = x === "markdown", y = b ? "query" : n.analysisType || "query", g = y === "funnel" ? "funnel" : y === "flow" ? "flow" : y === "retention" ? "retention" : "query", A = n.query || "{}";
2323
2329
  let C;
2324
2330
  try {
2325
2331
  C = JSON.parse(A);
@@ -2352,13 +2358,13 @@ ${r.join(`
2352
2358
  }),
2353
2359
  filters: e.filters,
2354
2360
  colorPalette: e.colorPalette
2355
- }, m = e.title, f = s.portlets.length, y = s.filters?.length || 0;
2361
+ }, h = e.title, m = s.portlets.length, c = s.filters?.length || 0;
2356
2362
  return {
2357
- result: `Dashboard "${m}" created with ${f} portlets and ${y} filters.`,
2363
+ result: `Dashboard "${h}" created with ${m} portlets and ${c} filters.`,
2358
2364
  sideEffect: {
2359
2365
  type: "dashboard_saved",
2360
2366
  data: {
2361
- title: m,
2367
+ title: h,
2362
2368
  description: e.description,
2363
2369
  dashboardConfig: s
2364
2370
  }
@@ -2373,14 +2379,14 @@ ${r.join(`
2373
2379
  }), i;
2374
2380
  }
2375
2381
  async function* _e(l) {
2376
- const { message: t, history: a, semanticLayer: i, securityContext: d, agentConfig: u, apiKey: e } = l, o = l.sessionId || crypto.randomUUID(), r = u.observability, s = crypto.randomUUID(), m = Date.now();
2377
- let f;
2382
+ const { message: t, history: a, semanticLayer: i, securityContext: d, agentConfig: u, apiKey: e } = l, o = l.sessionId || crypto.randomUUID(), r = u.observability, s = crypto.randomUUID(), h = Date.now();
2383
+ let m;
2378
2384
  try {
2379
- const h = await import(
2385
+ const f = await import(
2380
2386
  /* webpackIgnore: true */
2381
2387
  "@anthropic-ai/sdk"
2382
2388
  );
2383
- f = h.default || h.Anthropic || h;
2389
+ m = f.default || f.Anthropic || f;
2384
2390
  } catch {
2385
2391
  yield {
2386
2392
  type: "error",
@@ -2390,7 +2396,7 @@ async function* _e(l) {
2390
2396
  };
2391
2397
  return;
2392
2398
  }
2393
- const y = new f({ apiKey: e }), n = De(), x = Se({ semanticLayer: i, securityContext: d }), b = i.getMetadata(), c = W(b), g = u.model || "claude-sonnet-4-6", A = u.maxTurns || 25, C = u.maxTokens || 4096;
2399
+ const c = new m({ apiKey: e }), n = De(), x = Se({ semanticLayer: i, securityContext: d }), b = i.getMetadata(), y = W(b), g = u.model || "claude-sonnet-4-6", A = u.maxTurns || 25, C = u.maxTokens || 4096;
2394
2400
  try {
2395
2401
  r?.onChatStart?.({
2396
2402
  traceId: s,
@@ -2403,13 +2409,13 @@ async function* _e(l) {
2403
2409
  }
2404
2410
  const k = [];
2405
2411
  if (a && a.length > 0) {
2406
- for (const h of a)
2407
- if (h.role === "user")
2408
- k.push({ role: "user", content: h.content });
2409
- else if (h.role === "assistant") {
2412
+ for (const f of a)
2413
+ if (f.role === "user")
2414
+ k.push({ role: "user", content: f.content });
2415
+ else if (f.role === "assistant") {
2410
2416
  const F = [];
2411
- if (h.content && F.push({ type: "text", text: h.content }), h.toolCalls && h.toolCalls.length > 0) {
2412
- for (const w of h.toolCalls)
2417
+ if (f.content && F.push({ type: "text", text: f.content }), f.toolCalls && f.toolCalls.length > 0) {
2418
+ for (const w of f.toolCalls)
2413
2419
  F.push({
2414
2420
  type: "tool_use",
2415
2421
  id: w.id,
@@ -2418,25 +2424,25 @@ async function* _e(l) {
2418
2424
  });
2419
2425
  k.push({ role: "assistant", content: F }), k.push({
2420
2426
  role: "user",
2421
- content: h.toolCalls.map((w) => ({
2427
+ content: f.toolCalls.map((w) => ({
2422
2428
  type: "tool_result",
2423
2429
  tool_use_id: w.id,
2424
2430
  content: typeof w.result == "string" ? w.result : JSON.stringify(w.result ?? ""),
2425
2431
  ...w.status === "error" ? { is_error: !0 } : {}
2426
2432
  }))
2427
2433
  });
2428
- } else F.length > 0 && k.push({ role: "assistant", content: h.content });
2434
+ } else F.length > 0 && k.push({ role: "assistant", content: f.content });
2429
2435
  }
2430
2436
  }
2431
2437
  k.push({ role: "user", content: t });
2432
2438
  let E = 0;
2433
2439
  try {
2434
- for (let h = 0; h < A; h++) {
2435
- E = h + 1;
2436
- const F = await y.messages.create({
2440
+ for (let f = 0; f < A; f++) {
2441
+ E = f + 1;
2442
+ const F = await c.messages.create({
2437
2443
  model: g,
2438
2444
  max_tokens: C,
2439
- system: c,
2445
+ system: y,
2440
2446
  tools: n,
2441
2447
  messages: k,
2442
2448
  stream: !0
@@ -2488,7 +2494,7 @@ async function* _e(l) {
2488
2494
  try {
2489
2495
  r?.onGenerationEnd?.({
2490
2496
  traceId: s,
2491
- turn: h,
2497
+ turn: f,
2492
2498
  model: g,
2493
2499
  stopReason: O,
2494
2500
  inputTokens: L,
@@ -2530,7 +2536,7 @@ async function* _e(l) {
2530
2536
  try {
2531
2537
  r?.onToolEnd?.({
2532
2538
  traceId: s,
2533
- turn: h,
2539
+ turn: f,
2534
2540
  toolName: p,
2535
2541
  toolUseId: S,
2536
2542
  isError: !!T.isError,
@@ -2552,7 +2558,7 @@ async function* _e(l) {
2552
2558
  try {
2553
2559
  r?.onToolEnd?.({
2554
2560
  traceId: s,
2555
- turn: h,
2561
+ turn: f,
2556
2562
  toolName: p,
2557
2563
  toolUseId: S,
2558
2564
  isError: !0,
@@ -2569,7 +2575,7 @@ async function* _e(l) {
2569
2575
  traceId: s,
2570
2576
  sessionId: o,
2571
2577
  totalTurns: E,
2572
- durationMs: Date.now() - m
2578
+ durationMs: Date.now() - h
2573
2579
  });
2574
2580
  } catch {
2575
2581
  }
@@ -2577,21 +2583,21 @@ async function* _e(l) {
2577
2583
  type: "done",
2578
2584
  data: { sessionId: o || "", traceId: s }
2579
2585
  };
2580
- } catch (h) {
2586
+ } catch (f) {
2581
2587
  try {
2582
2588
  r?.onChatEnd?.({
2583
2589
  traceId: s,
2584
2590
  sessionId: o,
2585
2591
  totalTurns: 0,
2586
- durationMs: Date.now() - m,
2587
- error: h instanceof Error ? h.message : "Unknown error"
2592
+ durationMs: Date.now() - h,
2593
+ error: f instanceof Error ? f.message : "Unknown error"
2588
2594
  });
2589
2595
  } catch {
2590
2596
  }
2591
2597
  yield {
2592
2598
  type: "error",
2593
2599
  data: {
2594
- message: Fe(h)
2600
+ message: Fe(f)
2595
2601
  }
2596
2602
  };
2597
2603
  }
@@ -12,22 +12,22 @@ Add your **markdown** content here:
12
12
 
13
13
  ---
14
14
 
15
- Use --- for horizontal rules.`,description:"Enter markdown text. Supports headers (#), bold (**text**), italic (*text*), links ([text](url)), lists (- item), and horizontal rules (---)."},{key:"accentColorIndex",label:"Accent Color",type:"paletteColor",defaultValue:0,description:"Color from the dashboard palette for headers, bullets, and links"},{key:"fontSize",label:"Font Size",type:"select",defaultValue:"medium",options:[{value:"small",label:"Small"},{value:"medium",label:"Medium"},{value:"large",label:"Large"}],description:"Overall text size for the markdown content"},{key:"alignment",label:"Text Alignment",type:"select",defaultValue:"left",options:[{value:"left",label:"Left"},{value:"center",label:"Center"},{value:"right",label:"Right"}],description:"Horizontal alignment of the markdown content"},{key:"hideHeader",label:"Hide Header",type:"boolean",defaultValue:!0,description:"Hide the portlet header bar (title and action buttons)"},{key:"transparentBackground",label:"Transparent Background",type:"boolean",defaultValue:!1,description:"Remove card background, border, and shadow for seamless integration as section headers"},{key:"autoHeight",label:"Auto Height",type:"boolean",defaultValue:!0,description:"In row and mobile layouts, size to markdown content instead of fixed row height"},{key:"accentBorder",label:"Accent Border",type:"select",defaultValue:"none",options:[{value:"none",label:"None"},{value:"left",label:"Left"},{value:"top",label:"Top"},{value:"bottom",label:"Bottom"}],description:"Add an accent-colored border on one side of the content"}]},fe={label:"Funnel Chart",description:"Show conversion through sequential steps",useCase:"Best for visualizing user journey funnels, sales pipelines, or multi-step processes",dropZones:[{key:"xAxis",label:"Step Name",description:"Step names (auto-populated from funnel steps)",mandatory:!1,maxItems:1,acceptTypes:["dimension"],emptyText:"Steps defined in funnel config"},{key:"yAxis",label:"Step Count",description:"Count at each step (auto-calculated)",mandatory:!1,maxItems:1,acceptTypes:["measure"],emptyText:"Counts calculated from funnel execution"}],displayOptions:["hideHeader"],displayOptionsConfig:[{key:"funnelStyle",label:"Funnel Style",type:"buttonGroup",defaultValue:"bars",options:[{value:"bars",label:"Bars"},{value:"funnel",label:"Funnel"}],description:"Visualization style"},{key:"funnelOrientation",label:"Orientation",type:"buttonGroup",defaultValue:"horizontal",options:[{value:"horizontal",label:"Horizontal"},{value:"vertical",label:"Vertical"}]},{key:"hideSummaryFooter",label:"Hide Summary Footer",type:"boolean",defaultValue:!1,description:"Hide the summary footer showing steps count and overall conversion"},{key:"showFunnelConversion",label:"Show Conversion Rate",type:"boolean",defaultValue:!0,description:"Display step-to-step conversion percentage"},{key:"showFunnelAvgTime",label:"Show Avg Time",type:"boolean",defaultValue:!1,description:"Display average time to convert"},{key:"showFunnelMedianTime",label:"Show Median Time",type:"boolean",defaultValue:!1,description:"Display median time to convert"},{key:"showFunnelP90Time",label:"Show P90 Time",type:"boolean",defaultValue:!1,description:"Display 90th percentile time to convert"}]},be={label:"Sankey Chart",description:"Show flow between states or steps",useCase:"Best for visualizing user journey flows, path analysis, or state transitions",dropZones:[{key:"xAxis",label:"Event Type",description:"Event dimension that categorizes flow nodes",mandatory:!1,maxItems:1,acceptTypes:["dimension"],emptyText:"Auto-populated from flow config"},{key:"yAxis",label:"Flow Count",description:"Count of entities following each path",mandatory:!1,maxItems:1,acceptTypes:["measure"],emptyText:"Calculated from flow execution"}],displayOptions:["hideHeader"],displayOptionsConfig:[{key:"linkOpacity",label:"Link Opacity",type:"buttonGroup",defaultValue:"0.5",options:[{value:"0.3",label:"Light"},{value:"0.5",label:"Medium"},{value:"0.7",label:"Dark"}],description:"Opacity of flow links"},{key:"showNodeLabels",label:"Show Node Labels",type:"boolean",defaultValue:!0,description:"Display labels on flow nodes"},{key:"hideSummaryFooter",label:"Hide Summary Footer",type:"boolean",defaultValue:!0,description:"Hide the statistics footer below the chart"}]},ge={label:"Sunburst Chart",description:"Show hierarchical flow as radial rings",useCase:"Best for visualizing forward paths from a starting event in a compact radial layout",dropZones:[{key:"xAxis",label:"Event Type",description:"Event dimension that categorizes flow nodes",mandatory:!1,maxItems:1,acceptTypes:["dimension"],emptyText:"Auto-populated from flow config"},{key:"yAxis",label:"Flow Count",description:"Count of entities following each path",mandatory:!1,maxItems:1,acceptTypes:["measure"],emptyText:"Calculated from flow execution"}],displayOptions:["hideHeader"],displayOptionsConfig:[{key:"innerRadius",label:"Inner Radius",type:"number",defaultValue:40,min:0,max:100,step:10,description:"Size of the center hole (0 for full circle)"},{key:"hideSummaryFooter",label:"Hide Summary Footer",type:"boolean",defaultValue:!0,description:"Hide the statistics footer below the chart"}]},xe={label:"Heatmap",description:"Visualize intensity across two dimensions",useCase:"Best for showing patterns in matrix data like correlations, schedules, or category comparisons",dropZones:[{key:"xAxis",label:"Columns (X-Axis)",description:"Dimension for column categories",mandatory:!0,maxItems:1,acceptTypes:["dimension"],emptyText:"Drop one dimension here"},{key:"yAxis",label:"Rows (Y-Axis)",description:"Dimension for row categories",mandatory:!0,maxItems:1,acceptTypes:["dimension"],emptyText:"Drop one dimension here"},{key:"valueField",label:"Value (Color Intensity)",description:"Measure that determines cell color",mandatory:!0,maxItems:1,acceptTypes:["measure"],emptyText:"Drop one measure here"}],displayOptions:["showLegend","showTooltip"],displayOptionsConfig:[{key:"showLabels",label:"Show Cell Values",type:"boolean",defaultValue:!1,description:"Display values inside each cell"},{key:"cellShape",label:"Cell Shape",type:"select",defaultValue:"rect",options:[{value:"rect",label:"Rectangle"},{value:"circle",label:"Circle"}]},{key:"xAxisFormat",label:"X-Axis Format",type:"axisFormat",description:"Number formatting for X-axis labels"},{key:"yAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for Y-axis labels"},{key:"valueFormat",label:"Value Format",type:"axisFormat",description:"Number formatting for cell values and legend"}],validate:i=>i.xAxis?.length?i.yAxis?.length?i.valueField?.length?{isValid:!0}:{isValid:!1,message:"Value measure required"}:{isValid:!1,message:"Y-axis dimension required"}:{isValid:!1,message:"X-axis dimension required"}},we={label:"Retention Matrix",dropZones:[],displayOptionsConfig:[{key:"showLegend",label:"Show Legend",type:"boolean",defaultValue:!0,description:"Show the color intensity legend"},{key:"showTooltip",label:"Show Tooltip",type:"boolean",defaultValue:!0,description:"Show tooltip on hover with detailed stats"}],description:"Cohort retention matrix visualization",useCase:"Visualize user retention over time by cohort"},ke={label:"Retention Chart",dropZones:[],displayOptionsConfig:[{key:"retentionDisplayMode",label:"Display Mode",type:"select",defaultValue:"line",options:[{value:"line",label:"Line Chart"},{value:"heatmap",label:"Heatmap Table"},{value:"combined",label:"Combined"}],description:"Choose how to visualize retention data"},{key:"showLegend",label:"Show Legend",type:"boolean",defaultValue:!0,description:"Show the legend for breakdown segments"},{key:"showGrid",label:"Show Grid",type:"boolean",defaultValue:!0,description:"Show grid lines on the chart"},{key:"showTooltip",label:"Show Tooltip",type:"boolean",defaultValue:!0,description:"Show tooltip on hover with detailed stats"}],description:"Combined retention visualization with line chart and heatmap modes",useCase:"Visualize user retention over time with optional breakdown segmentation"},ve={label:"Box Plot",description:"Show statistical distribution (median, IQR, whiskers) across categories",useCase:"Best for P&L spread per symbol, trade size distribution, latency distribution across platforms",displayOptions:["hideHeader"],dropZones:[{key:"xAxis",label:"X-Axis (Groups)",description:"Dimension to group boxes by (e.g. symbol, platform)",mandatory:!0,maxItems:1,acceptTypes:["dimension","timeDimension"],emptyText:"Drop a dimension here"},{key:"yAxis",label:"Y-Axis (Measures)",description:"Drop 1 measure for auto mode, 3 for avg/stddev/median mode, or 5 for min/q1/median/q3/max mode",mandatory:!0,maxItems:5,acceptTypes:["measure"],emptyText:"Drop 1, 3, or 5 measures here"}],displayOptionsConfig:[{key:"leftYAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for the value axis"}]},Ce={label:"Waterfall Chart",description:"Show cumulative effect of sequential positive and negative values",useCase:"Best for P&L decomposition, cash flow analysis, budget variance, or any sequential contribution breakdown",clickableElements:{bar:!0},displayOptions:["showTooltip","hideHeader"],dropZones:[{key:"xAxis",label:"X-Axis (Categories)",description:"Dimension labels for each bar segment (e.g. symbol, transaction type)",mandatory:!0,maxItems:1,acceptTypes:["dimension","timeDimension"],emptyText:"Drop a dimension here"},{key:"yAxis",label:"Y-Axis (Value)",description:"Single measure whose values are summed cumulatively",mandatory:!0,maxItems:1,acceptTypes:["measure"],emptyText:"Drop a measure here"}],displayOptionsConfig:[{key:"showTotal",label:"Show Total Bar",type:"boolean",defaultValue:!0,description:"Append a final bar showing the running total"},{key:"showConnectorLine",label:"Show Connector Line",type:"boolean",defaultValue:!0,description:"Draw a dashed step-line connecting bar tops"},{key:"showDataLabels",label:"Show Data Labels",type:"boolean",defaultValue:!1,description:"Display the value above each bar segment"},{key:"leftYAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for the Y-axis"}]},Te={label:"Candlestick Chart",description:"Financial candlestick chart showing open/close body and high/low wicks",useCase:"Best for EOD quotes (bid/ask spread per date/symbol), markout distribution bands, or OHLC price data",clickableElements:{bar:!0},displayOptions:["hideHeader"],dropZones:[{key:"xAxis",label:"X-Axis (Time / Category)",description:"Time dimension or category for each candle (e.g. date, symbol)",mandatory:!0,maxItems:1,acceptTypes:["timeDimension","dimension"],emptyText:"Drop a time or dimension here"},{key:"yAxis",label:"OHLC Measures (open, close, high, low)",description:"Drop 2–4 measures in order: open, close, high, low (OHLC mode). For range mode drop 2: high, low.",mandatory:!0,acceptTypes:["measure"],emptyText:"Drop 2+ measures here"}],displayOptionsConfig:[{key:"rangeMode",label:"Chart Mode",type:"select",defaultValue:"ohlc",options:[{value:"ohlc",label:"OHLC (open, close, high, low)"},{value:"range",label:"Range (high, low / bid, ask)"}],description:"OHLC: 4 measures. Range: 2 measures (high + low)."},{key:"bullColor",label:"Bullish Colour",type:"color",defaultValue:"#22c55e",description:"Candle colour when close ≥ open"},{key:"bearColor",label:"Bearish Colour",type:"color",defaultValue:"#ef4444",description:"Candle colour when close < open"},{key:"showWicks",label:"Show Wicks",type:"boolean",defaultValue:!0,description:"Draw high/low wicks above and below the body"},{key:"leftYAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for the price axis"}]},Ae={label:"Measure Profile",description:"Plot N measures as sequential X-axis points to visualise a profile or shape across intervals",useCase:"Best for markout interval analysis (e.g. avgMinus2m → avgAtEvent → avgPlus2h), metric profiles, or any pattern across ordered measures",displayOptions:["showLegend","showTooltip","hideHeader"],dropZones:[{key:"yAxis",label:"Measures (X-Axis Order)",description:"Add 2 or more measures — they become the X-axis categories in the order listed",mandatory:!0,acceptTypes:["measure"],emptyText:"Drop 2+ measures here (displayed left → right)"},{key:"series",label:"Series (Split into Multiple Lines)",description:"Dimension to split data into separate profile lines (e.g. symbol, platform)",mandatory:!1,maxItems:1,acceptTypes:["dimension"],emptyText:"Drop a dimension here to create multiple lines"}],displayOptionsConfig:[{key:"showReferenceLineAtZero",label:"Show Zero Reference Line",type:"boolean",defaultValue:!0,description:"Draw a dashed line at Y = 0"},{key:"showDataLabels",label:"Show Data Labels",type:"boolean",defaultValue:!1,description:"Display value at each data point"},{key:"showLegend",label:"Show Legend",type:"boolean",defaultValue:!0,description:"Show series legend (only visible with a Series dimension)"},{key:"lineType",label:"Line Interpolation",type:"select",defaultValue:"monotone",options:[{value:"monotone",label:"Smooth (monotone)"},{value:"linear",label:"Linear"},{value:"step",label:"Step"}],description:"How data points are connected"},{key:"leftYAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for the Y-axis"}]},De={label:"Gauge Chart",description:"Half-circle arc gauge for a single KPI value versus a maximum target",useCase:"Best for high-water marks vs equity, margin utilisation, or any single value progress toward a goal",clickableElements:{},displayOptions:["hideHeader"],dropZones:[{key:"yAxis",label:"Value Measure",description:"Current value to display on the gauge (e.g. current equity, margin used)",mandatory:!0,maxItems:2,acceptTypes:["measure"],emptyText:"Drop 1 measure here (optional 2nd for dynamic max)"}],displayOptionsConfig:[{key:"minValue",label:"Minimum Value",type:"number",defaultValue:0,description:"Lower bound of the gauge arc (default 0)"},{key:"maxValue",label:"Maximum Value (static)",type:"number",description:"Upper bound of the gauge. Leave empty to use yAxis[1] or default 100"},{key:"thresholds",label:"Threshold Bands",type:"string",placeholder:'[{"value":0.33,"color":"#22c55e"},{"value":0.66,"color":"#f59e0b"},{"value":1,"color":"#ef4444"}]',description:"Array of {value (0–1 fraction), color} bands shown as outer arc markers"},{key:"showCenterLabel",label:"Show Centre Label",type:"boolean",defaultValue:!0,description:"Display current value and field name in the centre of the gauge"},{key:"showPercentage",label:"Show as Percentage",type:"boolean",defaultValue:!1,description:"Display value as % of max instead of raw number"},{key:"leftYAxisFormat",label:"Value Format",type:"axisFormat",description:"Number formatting for the displayed value and axis labels"}]},L={bar:te,line:ae,area:se,pie:ie,scatter:oe,bubble:re,radar:ne,radialBar:le,treemap:de,table:pe,activityGrid:ue,kpiNumber:ce,kpiDelta:me,kpiText:ye,markdown:he,funnel:fe,sankey:be,sunburst:ge,heatmap:xe,retentionHeatmap:we,retentionCombined:ke,boxPlot:ve,waterfall:Ce,candlestick:Te,measureProfile:Ae,gauge:De};function Se(i,t,a){const s=L[i];if(!s)return{isValid:!0,errors:[]};if(s.skipQuery)return{isValid:!0,errors:[]};const n=[];for(const u of s.dropZones){if(!u.mandatory)continue;const e=t?.[u.key];if(!(Array.isArray(e)?e.length>0:!!e)){const l=u.acceptTypes?.join("/")??"fields";n.push(`chartConfig.${u.key} is required for ${i} chart (${u.label}). Accepts: ${l}.`)}}if(i==="bar"){const u=t?.xAxis;if(!(Array.isArray(u)?u.length>0:!!u)){const r=a.dimensions??[],l=a.timeDimensions??[];r.length>0||l.length>0?n.push("chartConfig.xAxis is required for bar charts. Put a dimension in xAxis so bars have category labels."):n.push('Bar charts need an xAxis dimension for category labels. Add a dimension to the query or use "table" chart type instead.')}}if(t?.xAxis&&t?.series){const u=new Set(Array.isArray(t.xAxis)?t.xAxis:[t.xAxis]),r=(Array.isArray(t.series)?t.series:[t.series]).filter(l=>u.has(l));r.length>0&&n.push(`chartConfig.series must not contain the same field as xAxis (found: ${r.join(", ")}). The series field is only for splitting into grouped/stacked sub-series by a DIFFERENT dimension. Remove the duplicate from series.`)}return{isValid:n.length===0,errors:n}}function Fe(i,t,a){const s=L[i];if(!s)return t??{};const n={...t},u=a.measures??[],e=a.dimensions??[],l=(a.timeDimensions??[]).map(o=>o.dimension);for(const o of s.dropZones){const m=n[o.key];if(Array.isArray(m)?m.length>0:!!m)continue;const y=o.acceptTypes??[];if(o.key==="sizeField"||o.key==="colorField"){if(y.includes("measure")){const g=new Set;for(const C of s.dropZones){if(C.key===o.key)continue;const k=n[C.key];Array.isArray(k)?k.forEach(E=>g.add(E)):typeof k=="string"&&g.add(k)}const A=u.filter(C=>!g.has(C));A.length>0&&(n[o.key]=A[0])}continue}const d=[];if(y.includes("dimension")&&d.push(...e),y.includes("timeDimension")&&d.push(...l),y.includes("measure")&&d.push(...u),d.length===0)continue;let x=d;if(o.key==="series"){const g=new Set(Array.isArray(n.xAxis)?n.xAxis:n.xAxis?[n.xAxis]:[]);if(x=d.filter(A=>!g.has(A)),x.length===0)continue}const b=o.maxItems??1/0,c=x.slice(0,b);c.length>0&&(n[o.key]=c)}return n}function Ve(i){const t=[`
16
- Chart config requirements by type:`];for(const a of i){const s=L[a];if(!s)continue;const n=s.description??"",u=s.useCase??"",e=[n,u].filter(Boolean).join(". "),r=e?` — ${e}.`:"",l=s.dropZones.filter(m=>m.mandatory);if(l.length===0&&!s.skipQuery){t.push(` ${a}${r} chartConfig auto-inferred from query.`);continue}if(s.skipQuery){t.push(` ${a}${r} No query needed.`);continue}const o=l.map(m=>{const f=m.acceptTypes?.join("/")??"any",y=m.maxItems?` (max ${m.maxItems})`:"";return`${m.key}=[${f}]${y}`});t.push(` ${a}${r} Requires ${o.join(", ")}.`)}return t.join(`
15
+ Use --- for horizontal rules.`,description:"Enter markdown text. Supports headers (#), bold (**text**), italic (*text*), links ([text](url)), lists (- item), and horizontal rules (---)."},{key:"accentColorIndex",label:"Accent Color",type:"paletteColor",defaultValue:0,description:"Color from the dashboard palette for headers, bullets, and links"},{key:"fontSize",label:"Font Size",type:"select",defaultValue:"medium",options:[{value:"small",label:"Small"},{value:"medium",label:"Medium"},{value:"large",label:"Large"}],description:"Overall text size for the markdown content"},{key:"alignment",label:"Text Alignment",type:"select",defaultValue:"left",options:[{value:"left",label:"Left"},{value:"center",label:"Center"},{value:"right",label:"Right"}],description:"Horizontal alignment of the markdown content"},{key:"hideHeader",label:"Hide Header",type:"boolean",defaultValue:!0,description:"Hide the portlet header bar (title and action buttons)"},{key:"transparentBackground",label:"Transparent Background",type:"boolean",defaultValue:!1,description:"Remove card background, border, and shadow for seamless integration as section headers"},{key:"autoHeight",label:"Auto Height",type:"boolean",defaultValue:!0,description:"In row and mobile layouts, size to markdown content instead of fixed row height"},{key:"accentBorder",label:"Accent Border",type:"select",defaultValue:"none",options:[{value:"none",label:"None"},{value:"left",label:"Left"},{value:"top",label:"Top"},{value:"bottom",label:"Bottom"}],description:"Add an accent-colored border on one side of the content"}]},fe={label:"Funnel Chart",description:"Show conversion through sequential steps",useCase:"Best for visualizing user journey funnels, sales pipelines, or multi-step processes",dropZones:[{key:"xAxis",label:"Step Name",description:"Step names (auto-populated from funnel steps)",mandatory:!1,maxItems:1,acceptTypes:["dimension"],emptyText:"Steps defined in funnel config"},{key:"yAxis",label:"Step Count",description:"Count at each step (auto-calculated)",mandatory:!1,maxItems:1,acceptTypes:["measure"],emptyText:"Counts calculated from funnel execution"}],displayOptions:["hideHeader"],displayOptionsConfig:[{key:"funnelStyle",label:"Funnel Style",type:"buttonGroup",defaultValue:"bars",options:[{value:"bars",label:"Bars"},{value:"funnel",label:"Funnel"}],description:"Visualization style"},{key:"funnelOrientation",label:"Orientation",type:"buttonGroup",defaultValue:"horizontal",options:[{value:"horizontal",label:"Horizontal"},{value:"vertical",label:"Vertical"}]},{key:"hideSummaryFooter",label:"Hide Summary Footer",type:"boolean",defaultValue:!1,description:"Hide the summary footer showing steps count and overall conversion"},{key:"showFunnelConversion",label:"Show Conversion Rate",type:"boolean",defaultValue:!0,description:"Display step-to-step conversion percentage"},{key:"showFunnelAvgTime",label:"Show Avg Time",type:"boolean",defaultValue:!1,description:"Display average time to convert"},{key:"showFunnelMedianTime",label:"Show Median Time",type:"boolean",defaultValue:!1,description:"Display median time to convert"},{key:"showFunnelP90Time",label:"Show P90 Time",type:"boolean",defaultValue:!1,description:"Display 90th percentile time to convert"}]},be={label:"Sankey Chart",description:"Show flow between states or steps",useCase:"Best for visualizing user journey flows, path analysis, or state transitions",dropZones:[{key:"xAxis",label:"Event Type",description:"Event dimension that categorizes flow nodes",mandatory:!1,maxItems:1,acceptTypes:["dimension"],emptyText:"Auto-populated from flow config"},{key:"yAxis",label:"Flow Count",description:"Count of entities following each path",mandatory:!1,maxItems:1,acceptTypes:["measure"],emptyText:"Calculated from flow execution"}],displayOptions:["hideHeader"],displayOptionsConfig:[{key:"linkOpacity",label:"Link Opacity",type:"buttonGroup",defaultValue:"0.5",options:[{value:"0.3",label:"Light"},{value:"0.5",label:"Medium"},{value:"0.7",label:"Dark"}],description:"Opacity of flow links"},{key:"showNodeLabels",label:"Show Node Labels",type:"boolean",defaultValue:!0,description:"Display labels on flow nodes"},{key:"hideSummaryFooter",label:"Hide Summary Footer",type:"boolean",defaultValue:!0,description:"Hide the statistics footer below the chart"}]},ge={label:"Sunburst Chart",description:"Show hierarchical flow as radial rings",useCase:"Best for visualizing forward paths from a starting event in a compact radial layout",dropZones:[{key:"xAxis",label:"Event Type",description:"Event dimension that categorizes flow nodes",mandatory:!1,maxItems:1,acceptTypes:["dimension"],emptyText:"Auto-populated from flow config"},{key:"yAxis",label:"Flow Count",description:"Count of entities following each path",mandatory:!1,maxItems:1,acceptTypes:["measure"],emptyText:"Calculated from flow execution"}],displayOptions:["hideHeader"],displayOptionsConfig:[{key:"innerRadius",label:"Inner Radius",type:"number",defaultValue:40,min:0,max:100,step:10,description:"Size of the center hole (0 for full circle)"},{key:"hideSummaryFooter",label:"Hide Summary Footer",type:"boolean",defaultValue:!0,description:"Hide the statistics footer below the chart"}]},xe={label:"Heatmap",description:"Visualize intensity across two dimensions",useCase:"Best for showing patterns in matrix data like correlations, schedules, or category comparisons",dropZones:[{key:"xAxis",label:"Columns (X-Axis)",description:"Dimension for column categories",mandatory:!0,maxItems:1,acceptTypes:["dimension"],emptyText:"Drop one dimension here"},{key:"yAxis",label:"Rows (Y-Axis)",description:"Dimension for row categories",mandatory:!0,maxItems:1,acceptTypes:["dimension"],emptyText:"Drop one dimension here"},{key:"valueField",label:"Value (Color Intensity)",description:"Measure that determines cell color",mandatory:!0,maxItems:1,acceptTypes:["measure"],emptyText:"Drop one measure here"}],displayOptions:["showLegend","showTooltip"],displayOptionsConfig:[{key:"showLabels",label:"Show Cell Values",type:"boolean",defaultValue:!1,description:"Display values inside each cell"},{key:"cellShape",label:"Cell Shape",type:"select",defaultValue:"rect",options:[{value:"rect",label:"Rectangle"},{value:"circle",label:"Circle"}]},{key:"xAxisFormat",label:"X-Axis Format",type:"axisFormat",description:"Number formatting for X-axis labels"},{key:"yAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for Y-axis labels"},{key:"valueFormat",label:"Value Format",type:"axisFormat",description:"Number formatting for cell values and legend"}],validate:i=>i.xAxis?.length?i.yAxis?.length?i.valueField?.length?{isValid:!0}:{isValid:!1,message:"Value measure required"}:{isValid:!1,message:"Y-axis dimension required"}:{isValid:!1,message:"X-axis dimension required"}},we={label:"Retention Matrix",dropZones:[],displayOptionsConfig:[{key:"showLegend",label:"Show Legend",type:"boolean",defaultValue:!0,description:"Show the color intensity legend"},{key:"showTooltip",label:"Show Tooltip",type:"boolean",defaultValue:!0,description:"Show tooltip on hover with detailed stats"}],description:"Cohort retention matrix visualization",useCase:"Visualize user retention over time by cohort"},ke={label:"Retention Chart",dropZones:[],displayOptionsConfig:[{key:"retentionDisplayMode",label:"Display Mode",type:"select",defaultValue:"line",options:[{value:"line",label:"Line Chart"},{value:"heatmap",label:"Heatmap Table"},{value:"combined",label:"Combined"}],description:"Choose how to visualize retention data"},{key:"showLegend",label:"Show Legend",type:"boolean",defaultValue:!0,description:"Show the legend for breakdown segments"},{key:"showGrid",label:"Show Grid",type:"boolean",defaultValue:!0,description:"Show grid lines on the chart"},{key:"showTooltip",label:"Show Tooltip",type:"boolean",defaultValue:!0,description:"Show tooltip on hover with detailed stats"}],description:"Combined retention visualization with line chart and heatmap modes",useCase:"Visualize user retention over time with optional breakdown segmentation"},ve={label:"Box Plot",description:"Show statistical distribution (median, IQR, whiskers) across categories",useCase:"Best for P&L spread per symbol, trade size distribution, latency distribution across platforms",displayOptions:["hideHeader"],dropZones:[{key:"xAxis",label:"X-Axis (Groups)",description:"Dimension to group boxes by (e.g. symbol, platform)",mandatory:!0,maxItems:1,acceptTypes:["dimension","timeDimension"],emptyText:"Drop a dimension here"},{key:"yAxis",label:"Y-Axis (Measures)",description:"Drop 1 measure for auto mode, 3 for avg/stddev/median mode, or 5 for min/q1/median/q3/max mode",mandatory:!0,maxItems:5,acceptTypes:["measure"],emptyText:"Drop 1, 3, or 5 measures here"}],displayOptionsConfig:[{key:"leftYAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for the value axis"}]},Ce={label:"Waterfall Chart",description:"Show cumulative effect of sequential positive and negative values",useCase:"Best for P&L decomposition, cash flow analysis, budget variance, or any sequential contribution breakdown",clickableElements:{bar:!0},displayOptions:["showTooltip","hideHeader"],dropZones:[{key:"xAxis",label:"X-Axis (Categories)",description:"Dimension labels for each bar segment (e.g. symbol, transaction type)",mandatory:!0,maxItems:1,acceptTypes:["dimension","timeDimension"],emptyText:"Drop a dimension here"},{key:"yAxis",label:"Y-Axis (Value)",description:"Single measure whose values are summed cumulatively",mandatory:!0,maxItems:1,acceptTypes:["measure"],emptyText:"Drop a measure here"}],displayOptionsConfig:[{key:"showTotal",label:"Show Total Bar",type:"boolean",defaultValue:!0,description:"Append a final bar showing the running total"},{key:"showConnectorLine",label:"Show Connector Line",type:"boolean",defaultValue:!0,description:"Draw a dashed step-line connecting bar tops"},{key:"showDataLabels",label:"Show Data Labels",type:"boolean",defaultValue:!1,description:"Display the value above each bar segment"},{key:"leftYAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for the Y-axis"}]},Te={label:"Candlestick Chart",description:"Financial candlestick chart showing open/close body and high/low wicks",useCase:"Best for EOD quotes (bid/ask spread per date/symbol), markout distribution bands, or OHLC price data",clickableElements:{bar:!0},displayOptions:["hideHeader"],dropZones:[{key:"xAxis",label:"X-Axis (Time / Category)",description:"Time dimension or category for each candle (e.g. date, symbol)",mandatory:!0,maxItems:1,acceptTypes:["timeDimension","dimension"],emptyText:"Drop a time or dimension here"},{key:"yAxis",label:"OHLC Measures (open, close, high, low)",description:"Drop 2–4 measures in order: open, close, high, low (OHLC mode). For range mode drop 2: high, low.",mandatory:!0,acceptTypes:["measure"],emptyText:"Drop 2+ measures here"}],displayOptionsConfig:[{key:"rangeMode",label:"Chart Mode",type:"select",defaultValue:"ohlc",options:[{value:"ohlc",label:"OHLC (open, close, high, low)"},{value:"range",label:"Range (high, low / bid, ask)"}],description:"OHLC: 4 measures. Range: 2 measures (high + low)."},{key:"bullColor",label:"Bullish Colour",type:"color",defaultValue:"#22c55e",description:"Candle colour when close ≥ open"},{key:"bearColor",label:"Bearish Colour",type:"color",defaultValue:"#ef4444",description:"Candle colour when close < open"},{key:"showWicks",label:"Show Wicks",type:"boolean",defaultValue:!0,description:"Draw high/low wicks above and below the body"},{key:"leftYAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for the price axis"}]},Ae={label:"Measure Profile",description:"Plot N measures as sequential X-axis points to visualise a profile or shape across intervals",useCase:"Best for markout interval analysis (e.g. avgMinus2m → avgAtEvent → avgPlus2h), metric profiles, or any pattern across ordered measures",displayOptions:["showLegend","showTooltip","hideHeader"],dropZones:[{key:"yAxis",label:"Measures (X-Axis Order)",description:"Add 2 or more measures — they become the X-axis categories in the order listed",mandatory:!0,acceptTypes:["measure"],emptyText:"Drop 2+ measures here (displayed left → right)"},{key:"series",label:"Series (Split into Multiple Lines)",description:"Dimension to split data into separate profile lines (e.g. symbol, platform)",mandatory:!1,maxItems:1,acceptTypes:["dimension"],emptyText:"Drop a dimension here to create multiple lines"}],displayOptionsConfig:[{key:"showReferenceLineAtZero",label:"Show Zero Reference Line",type:"boolean",defaultValue:!0,description:"Draw a dashed line at Y = 0"},{key:"showDataLabels",label:"Show Data Labels",type:"boolean",defaultValue:!1,description:"Display value at each data point"},{key:"showLegend",label:"Show Legend",type:"boolean",defaultValue:!0,description:"Show series legend (only visible with a Series dimension)"},{key:"lineType",label:"Line Interpolation",type:"select",defaultValue:"monotone",options:[{value:"monotone",label:"Smooth (monotone)"},{value:"linear",label:"Linear"},{value:"step",label:"Step"}],description:"How data points are connected"},{key:"leftYAxisFormat",label:"Y-Axis Format",type:"axisFormat",description:"Number formatting for the Y-axis"}]},De={label:"Gauge Chart",description:"Half-circle arc gauge for a single KPI value versus a maximum target",useCase:"Best for high-water marks vs equity, margin utilisation, or any single value progress toward a goal",clickableElements:{},displayOptions:["hideHeader"],dropZones:[{key:"yAxis",label:"Value Measure",description:"Current value to display on the gauge (e.g. current equity, margin used)",mandatory:!0,maxItems:2,acceptTypes:["measure"],emptyText:"Drop 1 measure here (optional 2nd for dynamic max)"}],displayOptionsConfig:[{key:"minValue",label:"Minimum Value",type:"number",defaultValue:0,description:"Lower bound of the gauge arc (default 0)"},{key:"maxValue",label:"Maximum Value (static)",type:"number",description:"Upper bound of the gauge. Leave empty to use yAxis[1] or default 100"},{key:"thresholds",label:"Threshold Bands",type:"string",placeholder:'[{"value":0.33,"color":"#22c55e"},{"value":0.66,"color":"#f59e0b"},{"value":1,"color":"#ef4444"}]',description:"Array of {value (0–1 fraction), color} bands shown as outer arc markers"},{key:"showCenterLabel",label:"Show Centre Label",type:"boolean",defaultValue:!0,description:"Display current value and field name in the centre of the gauge"},{key:"showPercentage",label:"Show as Percentage",type:"boolean",defaultValue:!1,description:"Display value as % of max instead of raw number"},{key:"leftYAxisFormat",label:"Value Format",type:"axisFormat",description:"Number formatting for the displayed value and axis labels"}]},L={bar:te,line:ae,area:se,pie:ie,scatter:oe,bubble:re,radar:ne,radialBar:le,treemap:de,table:pe,activityGrid:ue,kpiNumber:ce,kpiDelta:me,kpiText:ye,markdown:he,funnel:fe,sankey:be,sunburst:ge,heatmap:xe,retentionHeatmap:we,retentionCombined:ke,boxPlot:ve,waterfall:Ce,candlestick:Te,measureProfile:Ae,gauge:De};function Se(i,t,a){const s=L[i];if(!s)return{isValid:!0,errors:[]};if(s.skipQuery)return{isValid:!0,errors:[]};const n=[];for(const u of s.dropZones){if(!u.mandatory)continue;const e=t?.[u.key];if(!(Array.isArray(e)?e.length>0:!!e)){const l=u.acceptTypes?.join("/")??"fields";n.push(`chartConfig.${u.key} is required for ${i} chart (${u.label}). Accepts: ${l}.`)}}if(i==="bar"){const u=t?.xAxis;if(!(Array.isArray(u)?u.length>0:!!u)){const r=a.dimensions??[],l=a.timeDimensions??[];r.length>0||l.length>0?n.push("chartConfig.xAxis is required for bar charts. Put a dimension in xAxis so bars have category labels."):n.push('Bar charts need an xAxis dimension for category labels. Add a dimension to the query or use "table" chart type instead.')}}if(t?.xAxis&&t?.series){const u=new Set(Array.isArray(t.xAxis)?t.xAxis:[t.xAxis]),r=(Array.isArray(t.series)?t.series:[t.series]).filter(l=>u.has(l));r.length>0&&n.push(`chartConfig.series must not contain the same field as xAxis (found: ${r.join(", ")}). The series field is only for splitting into grouped/stacked sub-series by a DIFFERENT dimension. Remove the duplicate from series.`)}return{isValid:n.length===0,errors:n}}function Fe(i,t,a){const s=L[i];if(!s)return t??{};const n={...t},u=a.measures??[],e=a.dimensions??[],l=(a.timeDimensions??[]).map(o=>o.dimension);for(const o of s.dropZones){const h=n[o.key];if(Array.isArray(h)?h.length>0:!!h)continue;const c=o.acceptTypes??[];if(o.key==="sizeField"||o.key==="colorField"){if(c.includes("measure")){const g=new Set;for(const C of s.dropZones){if(C.key===o.key)continue;const k=n[C.key];Array.isArray(k)?k.forEach(E=>g.add(E)):typeof k=="string"&&g.add(k)}const A=u.filter(C=>!g.has(C));A.length>0&&(n[o.key]=A[0])}continue}const d=[];if(c.includes("dimension")&&d.push(...e),c.includes("timeDimension")&&d.push(...l),c.includes("measure")&&d.push(...u),d.length===0)continue;let x=d;if(o.key==="series"){const g=new Set(Array.isArray(n.xAxis)?n.xAxis:n.xAxis?[n.xAxis]:[]);if(x=d.filter(A=>!g.has(A)),x.length===0)continue}const b=o.maxItems??1/0,y=x.slice(0,b);y.length>0&&(n[o.key]=y)}return n}function Ve(i){const t=[`
16
+ Chart config requirements by type:`];for(const a of i){const s=L[a];if(!s)continue;const n=s.description??"",u=s.useCase??"",e=[n,u].filter(Boolean).join(". "),r=e?` — ${e}.`:"",l=s.dropZones.filter(h=>h.mandatory);if(l.length===0&&!s.skipQuery){t.push(` ${a}${r} chartConfig auto-inferred from query.`);continue}if(s.skipQuery){t.push(` ${a}${r} No query needed.`);continue}const o=l.map(h=>{const m=h.acceptTypes?.join("/")??"any",c=h.maxItems?` (max ${h.maxItems})`:"";return`${h.key}=[${m}]${c}`});t.push(` ${a}${r} Requires ${o.join(", ")}.`)}return t.join(`
17
17
  `)}const P=["bar","line","area","pie","scatter","radar","bubble","table","kpiNumber","kpiDelta","funnel","heatmap","sankey","sunburst","retentionHeatmap","retentionCombined","boxPlot","markdown"];function Ee(){return[{name:"discover_cubes",description:"Search for available data cubes by topic or intent. Call this FIRST to understand what data is available. Returns cube names, measures, dimensions, and relationships.",input_schema:{type:"object",properties:{topic:{type:"string",description:'Keyword to search (e.g., "sales", "employees")'},intent:{type:"string",description:'Natural language goal (e.g., "analyze productivity trends")'},limit:{type:"number",description:"Max results (default: 10)"},minScore:{type:"number",description:"Min relevance 0-1 (default: 0.1)"}}}},{name:"get_cube_metadata",description:"Get full metadata for all registered cubes including all measures, dimensions, types, and relationships. Use this for detailed schema information.",input_schema:{type:"object",properties:{}}},{name:"execute_query",description:"Execute a semantic query and return data results. Supports standard queries (measures/dimensions) and analysis modes (funnel/flow/retention). Only provide ONE mode per call.",input_schema:{type:"object",properties:{measures:{type:"array",items:{type:"string",pattern:"^[A-Z][a-zA-Z0-9]*\\.[a-zA-Z][a-zA-Z0-9]*$"},description:'Aggregation measures — MUST be "CubeName.measureName" format (e.g., ["PullRequests.count", "Issues.openCount"]). NEVER use just the cube name.'},dimensions:{type:"array",items:{type:"string",pattern:"^[A-Z][a-zA-Z0-9]*\\.[a-zA-Z][a-zA-Z0-9]*$"},description:'Grouping dimensions — MUST be "CubeName.dimensionName" format (e.g., ["Teams.name", "Employees.department"]). NEVER use just the cube name.'},filters:{type:"array",items:{type:"object",properties:{member:{type:"string"},operator:{type:"string"},values:{type:"array",items:{}}},required:["member","operator"]},description:"Filter conditions"},timeDimensions:{type:"array",items:{type:"object",properties:{dimension:{type:"string"},granularity:{type:"string"},dateRange:{}},required:["dimension"]},description:"Time dimensions with optional granularity"},order:{type:"object",description:'Sort order (e.g., {"Employees.count": "desc"})'},limit:{type:"number",description:"Row limit"},funnel:{type:"object",properties:{bindingKey:{type:"string",description:'Entity binding key (e.g., "Events.userId")'},timeDimension:{type:"string",description:'Time dimension (e.g., "Events.timestamp")'},steps:{type:"array",items:{type:"object",properties:{name:{type:"string"},filter:{},timeToConvert:{type:"string",description:'ISO 8601 duration (e.g., "P7D")'}},required:["name"]},description:"Funnel steps (min 2)"},includeTimeMetrics:{type:"boolean"},globalTimeWindow:{type:"string"}},required:["bindingKey","timeDimension","steps"],description:"Funnel analysis config. When provided, measures/dimensions are ignored."},flow:{type:"object",properties:{bindingKey:{type:"string"},timeDimension:{type:"string"},eventDimension:{type:"string",description:"Dimension whose values become node labels"},startingStep:{type:"object",properties:{name:{type:"string"},filter:{}},required:["name"]},stepsBefore:{type:"number",description:"Steps before starting step (0-5)"},stepsAfter:{type:"number",description:"Steps after starting step (0-5)"},entityLimit:{type:"number"},outputMode:{type:"string",enum:["sankey","sunburst"]}},required:["bindingKey","timeDimension","eventDimension","startingStep"],description:"Flow analysis config. When provided, measures/dimensions are ignored."},retention:{type:"object",properties:{timeDimension:{type:"string"},bindingKey:{type:"string"},dateRange:{type:"object",properties:{start:{type:"string",description:"YYYY-MM-DD"},end:{type:"string",description:"YYYY-MM-DD"}},required:["start","end"]},granularity:{type:"string",enum:["day","week","month"]},periods:{type:"number"},retentionType:{type:"string",enum:["classic","rolling"]},cohortFilters:{},activityFilters:{},breakdownDimensions:{type:"array",items:{type:"string"}}},required:["timeDimension","bindingKey","dateRange","granularity","periods"],description:"Retention analysis config. When provided, measures/dimensions are ignored."}}}},{name:"add_portlet",description:`Add a chart visualization to the notebook.
18
18
  `+Ve(P)+`
19
- The query is validated before adding. The portlet fetches its own data.`,input_schema:{type:"object",properties:{title:{type:"string",description:"Title for the visualization"},query:{type:"string",description:'JSON string of the query. Standard: {"measures":[...],"dimensions":[...]}. Funnel: {"funnel":{"bindingKey":"...","timeDimension":"...","steps":[...]}}. Flow: {"flow":{"bindingKey":"...","timeDimension":"...","eventDimension":"...","startingStep":{...}}}. Retention: {"retention":{"timeDimension":"...","bindingKey":"...","dateRange":{"start":"...","end":"..."},"granularity":"...","periods":N}}.'},chartType:{type:"string",enum:P,description:"Chart type to render"},chartConfig:{type:"object",properties:{xAxis:{type:"array",items:{type:"string"}},yAxis:{type:"array",items:{type:"string"}},series:{type:"array",items:{type:"string"}},sizeField:{type:"string"},colorField:{type:"string"}},description:"Chart axis configuration"},displayConfig:{type:"object",properties:{showLegend:{type:"boolean"},showGrid:{type:"boolean"},showTooltip:{type:"boolean"},stacked:{type:"boolean"},orientation:{type:"string",enum:["horizontal","vertical"]}},description:"Chart display configuration"}},required:["title","query","chartType"]}},{name:"add_markdown",description:"Add an explanation or analysis text block to the notebook. Use markdown formatting. Use this to explain findings, methodology, and insights alongside visualizations.",input_schema:{type:"object",properties:{title:{type:"string",description:"Optional title for the text block"},content:{type:"string",description:"Markdown content to display"}},required:["content"]}},{name:"save_as_dashboard",description:"Convert the current notebook analysis into a persistent dashboard. Constructs a professional DashboardConfig with proper grid layout, section headers (markdown portlets), and dashboard-level filters. Call this when the user asks to save/export the notebook as a dashboard.",input_schema:{type:"object",properties:{title:{type:"string",description:"Dashboard title"},description:{type:"string",description:"Optional dashboard description"},portlets:{type:"array",items:{type:"object",properties:{id:{type:"string",description:"Unique portlet ID"},title:{type:"string",description:"Portlet title"},chartType:{type:"string",enum:P,description:'Chart type. Use "markdown" for section headers.'},query:{type:"string",description:"JSON string of the query. Omit or leave empty for markdown portlets."},chartConfig:{type:"object",properties:{xAxis:{type:"array",items:{type:"string"}},yAxis:{type:"array",items:{type:"string"}},series:{type:"array",items:{type:"string"}},sizeField:{type:"string"},colorField:{type:"string"}},description:"Chart axis configuration"},displayConfig:{type:"object",description:"Chart display configuration (for markdown: { content, hideHeader, transparentBackground, autoHeight })"},dashboardFilterMapping:{type:"array",items:{type:"string"},description:"Array of dashboard filter IDs that apply to this portlet"},analysisType:{type:"string",enum:["query","funnel","flow","retention"],description:'Analysis type (default: "query")'},w:{type:"number",description:"Grid width (1-12)"},h:{type:"number",description:"Grid height in row units"},x:{type:"number",description:"Grid x position (0-11)"},y:{type:"number",description:"Grid y position"}},required:["id","title","chartType","w","h","x","y"]},description:"Array of portlet configurations for the dashboard"},filters:{type:"array",items:{type:"object",properties:{id:{type:"string",description:"Unique filter ID"},label:{type:"string",description:"Display label for the filter"},filter:{type:"object",properties:{member:{type:"string"},operator:{type:"string"},values:{type:"array",items:{}}},required:["member","operator"],description:"The filter definition"},isUniversalTime:{type:"boolean",description:"When true, applies to all time dimensions in portlets"}},required:["id","label","filter"]},description:"Dashboard-level filters"},colorPalette:{type:"string",description:"Color palette name"}},required:["title","portlets"]}}]}function _e(i){const{semanticLayer:t,securityContext:a}=i,s=new Map;s.set("discover_cubes",async e=>{const r=await B.handleDiscover(t,{topic:e.topic,intent:e.intent,limit:e.limit,minScore:e.minScore});return{result:JSON.stringify(r,null,2)}}),s.set("get_cube_metadata",async()=>{const e=t.getMetadata();return{result:JSON.stringify(e,null,2)}});const n=new Map;for(const e of t.getMetadata())n.set(e.name,{measures:(e.measures||[]).map(r=>r.name),dimensions:(e.dimensions||[]).map(r=>r.name)});const u=(e,r)=>{const l=n.get(e),o=r==="measures"?l?.measures:l?.dimensions;return!o||o.length===0?"":` Available ${r}: ${o.slice(0,5).map(m=>`"${m}"`).join(", ")}`};return s.set("execute_query",async e=>{try{const r=(f,y)=>{if(!Array.isArray(f))return;const d=[],x=[];for(const b of f){if(typeof b!="string"){x.push(b);continue}const c=b.split(".");if(c.length===1){d.push(`"${b}" is not valid — must be "CubeName.fieldName".${u(b,y)}`);continue}if(c.length===3&&c[0]===c[1]){const g=`${c[0]}.${c[2]}`;x.push(g);continue}if(c.length===2&&c[0]===c[1]){d.push(`"${b}" is WRONG — "${c[0]}" is the cube name, not a ${y.replace(/s$/,"")}.${u(c[0],y)}`);continue}x.push(b)}if(d.length>0)throw new Error(`Invalid ${y}:
19
+ The query is validated before adding. The portlet fetches its own data.`,input_schema:{type:"object",properties:{title:{type:"string",description:"Title for the visualization"},query:{type:"string",description:'JSON string of the query. Standard: {"measures":[...],"dimensions":[...]}. Funnel: {"funnel":{"bindingKey":"...","timeDimension":"...","steps":[...]}}. Flow: {"flow":{"bindingKey":"...","timeDimension":"...","eventDimension":"...","startingStep":{...}}}. Retention: {"retention":{"timeDimension":"...","bindingKey":"...","dateRange":{"start":"...","end":"..."},"granularity":"...","periods":N}}.'},chartType:{type:"string",enum:P,description:"Chart type to render"},chartConfig:{type:"object",properties:{xAxis:{type:"array",items:{type:"string"}},yAxis:{type:"array",items:{type:"string"}},series:{type:"array",items:{type:"string"}},sizeField:{type:"string"},colorField:{type:"string"}},description:"Chart axis configuration"},displayConfig:{type:"object",properties:{showLegend:{type:"boolean"},showGrid:{type:"boolean"},showTooltip:{type:"boolean"},stacked:{type:"boolean"},orientation:{type:"string",enum:["horizontal","vertical"]}},description:"Chart display configuration"}},required:["title","query","chartType"]}},{name:"add_markdown",description:"Add an explanation or analysis text block to the notebook. Use markdown formatting. Use this to explain findings, methodology, and insights alongside visualizations.",input_schema:{type:"object",properties:{title:{type:"string",description:"Optional title for the text block"},content:{type:"string",description:"Markdown content to display"}},required:["content"]}},{name:"save_as_dashboard",description:"Convert the current notebook analysis into a persistent dashboard. Constructs a professional DashboardConfig with proper grid layout, section headers (markdown portlets), and dashboard-level filters. Call this when the user asks to save/export the notebook as a dashboard.",input_schema:{type:"object",properties:{title:{type:"string",description:"Dashboard title"},description:{type:"string",description:"Optional dashboard description"},portlets:{type:"array",items:{type:"object",properties:{id:{type:"string",description:"Unique portlet ID"},title:{type:"string",description:"Portlet title"},chartType:{type:"string",enum:P,description:'Chart type. Use "markdown" for section headers.'},query:{type:"string",description:"JSON string of the query. Omit or leave empty for markdown portlets."},chartConfig:{type:"object",properties:{xAxis:{type:"array",items:{type:"string"}},yAxis:{type:"array",items:{type:"string"}},series:{type:"array",items:{type:"string"}},sizeField:{type:"string"},colorField:{type:"string"}},description:"Chart axis configuration"},displayConfig:{type:"object",description:"Chart display configuration (for markdown: { content, hideHeader, transparentBackground, autoHeight })"},dashboardFilterMapping:{type:"array",items:{type:"string"},description:"Array of dashboard filter IDs that apply to this portlet"},analysisType:{type:"string",enum:["query","funnel","flow","retention"],description:'Analysis type (default: "query")'},w:{type:"number",description:"Grid width (1-12)"},h:{type:"number",description:"Grid height in row units"},x:{type:"number",description:"Grid x position (0-11)"},y:{type:"number",description:"Grid y position"}},required:["id","title","chartType","w","h","x","y"]},description:"Array of portlet configurations for the dashboard"},filters:{type:"array",items:{type:"object",properties:{id:{type:"string",description:"Unique filter ID"},label:{type:"string",description:"Display label for the filter"},filter:{type:"object",properties:{member:{type:"string"},operator:{type:"string"},values:{type:"array",items:{}}},required:["member","operator"],description:"The filter definition"},isUniversalTime:{type:"boolean",description:"When true, applies to all time dimensions in portlets"}},required:["id","label","filter"]},description:"Dashboard-level filters"},colorPalette:{type:"string",description:"Color palette name"}},required:["title","portlets"]}}]}function _e(i){const{semanticLayer:t,securityContext:a}=i,s=new Map;s.set("discover_cubes",async e=>{const r=await B.handleDiscover(t,{topic:e.topic,intent:e.intent,limit:e.limit,minScore:e.minScore});return{result:JSON.stringify(r,null,2)}}),s.set("get_cube_metadata",async()=>{const e=t.getMetadata();return{result:JSON.stringify(e,null,2)}});const n=new Map;for(const e of t.getMetadata())n.set(e.name,{measures:(e.measures||[]).map(r=>r.name),dimensions:(e.dimensions||[]).map(r=>r.name)});const u=(e,r)=>{const l=n.get(e),o=r==="measures"?l?.measures:l?.dimensions;return!o||o.length===0?"":` Available ${r}: ${o.slice(0,5).map(h=>`"${h}"`).join(", ")}`};return s.set("execute_query",async e=>{try{const r=(m,c)=>{if(!Array.isArray(m))return;const d=[],x=[];for(const b of m){if(typeof b!="string"){x.push(b);continue}const y=b.split(".");if(y.length===1){d.push(`"${b}" is not valid — must be "CubeName.fieldName".${u(b,c)}`);continue}if(y.length===3&&y[0]===y[1]){const g=`${y[0]}.${y[2]}`;x.push(g);continue}if(y.length===2&&y[0]===y[1]){d.push(`"${b}" is WRONG — "${y[0]}" is the cube name, not a ${c.replace(/s$/,"")}.${u(y[0],c)}`);continue}x.push(b)}if(d.length>0)throw new Error(`Invalid ${c}:
20
20
  ${d.join(`
21
- `)}`);return x};e.measures=r(e.measures,"measures")??e.measures,e.dimensions=r(e.dimensions,"dimensions")??e.dimensions;const l=f=>{const y=f.split(".");return y.length===3&&y[0]===y[1]?`${y[0]}.${y[2]}`:f};if(Array.isArray(e.filters))for(const f of e.filters)typeof f.member=="string"&&(f.member=l(f.member));if(Array.isArray(e.timeDimensions))for(const f of e.timeDimensions)typeof f.dimension=="string"&&(f.dimension=l(f.dimension));let o;e.funnel?o={funnel:e.funnel}:e.flow?o={flow:e.flow}:e.retention?o={retention:e.retention}:o={measures:e.measures,dimensions:e.dimensions,filters:e.filters,timeDimensions:e.timeDimensions,order:e.order,limit:e.limit};const m=await B.handleLoad(t,a,{query:o});return{result:JSON.stringify({rowCount:m.data.length,data:m.data,annotation:m.annotation},null,2)}}catch(r){const l={measures:e.measures,dimensions:e.dimensions,filters:e.filters,timeDimensions:e.timeDimensions,order:e.order,limit:e.limit,...e.funnel?{funnel:e.funnel}:{},...e.flow?{flow:e.flow}:{},...e.retention?{retention:e.retention}:{}};return{result:`Query execution failed: ${r instanceof Error?r.message:"Unknown error"}
21
+ `)}`);return x};e.measures=r(e.measures,"measures")??e.measures,e.dimensions=r(e.dimensions,"dimensions")??e.dimensions;const l=m=>{const c=m.split(".");return c.length===3&&c[0]===c[1]?`${c[0]}.${c[2]}`:m};if(Array.isArray(e.filters))for(const m of e.filters)typeof m.member=="string"&&(m.member=l(m.member));if(Array.isArray(e.timeDimensions))for(const m of e.timeDimensions)typeof m.dimension=="string"&&(m.dimension=l(m.dimension));if(e.order&&typeof e.order=="object"&&!Array.isArray(e.order)){const m={};for(const[c,d]of Object.entries(e.order))m[l(c)]=d;e.order=m}let o;e.funnel?o={funnel:e.funnel}:e.flow?o={flow:e.flow}:e.retention?o={retention:e.retention}:o={measures:e.measures,dimensions:e.dimensions,filters:e.filters,timeDimensions:e.timeDimensions,order:e.order,limit:e.limit};const h=await B.handleLoad(t,a,{query:o});return{result:JSON.stringify({rowCount:h.data.length,data:h.data,annotation:h.annotation},null,2)}}catch(r){const l={measures:e.measures,dimensions:e.dimensions,filters:e.filters,timeDimensions:e.timeDimensions,order:e.order,limit:e.limit,...e.funnel?{funnel:e.funnel}:{},...e.flow?{flow:e.flow}:{},...e.retention?{retention:e.retention}:{}};return{result:`Query execution failed: ${r instanceof Error?r.message:"Unknown error"}
22
22
 
23
23
  Attempted query:
24
- ${JSON.stringify(l,null,2)}`,isError:!0}}}),s.set("add_portlet",async e=>{const l={number:"kpiNumber",retention:"retentionHeatmap"}[e.chartType]??e.chartType;let o;try{o=JSON.parse(e.query)}catch{return{result:"Invalid query: could not parse JSON string. Ensure `query` is a valid JSON string.",isError:!0}}const m=t.validateQuery(o);if(!m.isValid)return{result:`Invalid query — fix these errors and retry:
25
- ${m.errors.join(`
24
+ ${JSON.stringify(l,null,2)}`,isError:!0}}}),s.set("add_portlet",async e=>{const l={number:"kpiNumber",retention:"retentionHeatmap"}[e.chartType]??e.chartType;let o;try{o=JSON.parse(e.query)}catch{return{result:"Invalid query: could not parse JSON string. Ensure `query` is a valid JSON string.",isError:!0}}const h=t.validateQuery(o);if(!h.isValid)return{result:`Invalid query — fix these errors and retry:
25
+ ${h.errors.join(`
26
26
  `)}
27
27
 
28
28
  Attempted query:
29
- ${JSON.stringify(o,null,2)}`,isError:!0};const f=!!(o.funnel||o.flow||o.retention);let y;if(f)y=e.chartConfig??{};else{const b=Fe(l,e.chartConfig,o),c=Se(l,b,o);if(!c.isValid)return{result:`Chart config invalid — fix these errors and retry:
30
- ${c.errors.join(`
31
- `)}`,isError:!0};y=b}const d=`portlet-${Date.now()}-${Math.random().toString(36).slice(2,7)}`,x={id:d,title:e.title,query:e.query,chartType:l,chartConfig:y,displayConfig:e.displayConfig};return{result:`Portlet "${e.title}" added to notebook (id: ${d}, chart: ${l}). [Reminder: in your next response, start with a brief sentence about what you will do next BEFORE making any tool calls.]`,sideEffect:{type:"add_portlet",data:x}}}),s.set("add_markdown",async e=>{const r=`markdown-${Date.now()}-${Math.random().toString(36).slice(2,7)}`,l={id:r,title:e.title,content:e.content};return{result:`Markdown block added to notebook (id: ${r}). [Reminder: in your next response, start with a brief sentence about what you will do next BEFORE making any tool calls.]`,sideEffect:{type:"add_markdown",data:l}}}),s.set("save_as_dashboard",async e=>{try{const r=e.portlets;if(!r||r.length===0)return{result:"Dashboard must contain at least one portlet.",isError:!0};const l=[];for(const d of r){if(d.chartType==="markdown")continue;const b=d.query;if(!b){l.push(`Portlet "${d.title}": missing query`);continue}let c;try{c=JSON.parse(b)}catch{l.push(`Portlet "${d.title}": invalid JSON query`);continue}const g=t.validateQuery(c);g.isValid||l.push(`Portlet "${d.title}": ${g.errors.join(", ")}`)}if(l.length>0)return{result:`Dashboard has invalid portlets — fix these errors and retry:
29
+ ${JSON.stringify(o,null,2)}`,isError:!0};const m=!!(o.funnel||o.flow||o.retention);let c;if(m)c=e.chartConfig??{};else{const b=Fe(l,e.chartConfig,o),y=Se(l,b,o);if(!y.isValid)return{result:`Chart config invalid — fix these errors and retry:
30
+ ${y.errors.join(`
31
+ `)}`,isError:!0};c=b}const d=`portlet-${Date.now()}-${Math.random().toString(36).slice(2,7)}`,x={id:d,title:e.title,query:e.query,chartType:l,chartConfig:c,displayConfig:e.displayConfig};return{result:`Portlet "${e.title}" added to notebook (id: ${d}, chart: ${l}). [Reminder: in your next response, start with a brief sentence about what you will do next BEFORE making any tool calls.]`,sideEffect:{type:"add_portlet",data:x}}}),s.set("add_markdown",async e=>{const r=`markdown-${Date.now()}-${Math.random().toString(36).slice(2,7)}`,l={id:r,title:e.title,content:e.content};return{result:`Markdown block added to notebook (id: ${r}). [Reminder: in your next response, start with a brief sentence about what you will do next BEFORE making any tool calls.]`,sideEffect:{type:"add_markdown",data:l}}}),s.set("save_as_dashboard",async e=>{try{const r=e.portlets;if(!r||r.length===0)return{result:"Dashboard must contain at least one portlet.",isError:!0};const l=[];for(const d of r){if(d.chartType==="markdown")continue;const b=d.query;if(!b){l.push(`Portlet "${d.title}": missing query`);continue}let y;try{y=JSON.parse(b)}catch{l.push(`Portlet "${d.title}": invalid JSON query`);continue}const g=t.validateQuery(y);g.isValid||l.push(`Portlet "${d.title}": ${g.errors.join(", ")}`)}if(l.length>0)return{result:`Dashboard has invalid portlets — fix these errors and retry:
32
32
  ${l.join(`
33
- `)}`,isError:!0};const o={portlets:r.map(d=>{const x=d.chartType,b=x==="markdown",c=b?"query":d.analysisType||"query",g=c==="funnel"?"funnel":c==="flow"?"flow":c==="retention"?"retention":"query",A=d.query||"{}";let C;try{C=JSON.parse(A)}catch{C={}}const k={version:1,analysisType:g,activeView:"chart",charts:{[g]:{chartType:x,chartConfig:d.chartConfig||{},displayConfig:d.displayConfig||{}}},query:b?{}:C};return{id:d.id,title:d.title,analysisConfig:k,dashboardFilterMapping:d.dashboardFilterMapping,w:d.w,h:d.h,x:d.x,y:d.y}}),filters:e.filters,colorPalette:e.colorPalette},m=e.title,f=o.portlets.length,y=o.filters?.length||0;return{result:`Dashboard "${m}" created with ${f} portlets and ${y} filters.`,sideEffect:{type:"dashboard_saved",data:{title:m,description:e.description,dashboardConfig:o}}}}catch(r){return{result:`Failed to save dashboard: ${r instanceof Error?r.message:"Unknown error"}`,isError:!0}}}),s}async function*qe(i){const{message:t,history:a,semanticLayer:s,securityContext:n,agentConfig:u,apiKey:e}=i,r=i.sessionId||crypto.randomUUID(),l=u.observability,o=crypto.randomUUID(),m=Date.now();let f;try{const h=await import("@anthropic-ai/sdk");f=h.default||h.Anthropic||h}catch{yield{type:"error",data:{message:"@anthropic-ai/sdk is required. Install it with: npm install @anthropic-ai/sdk"}};return}const y=new f({apiKey:e}),d=Ee(),x=_e({semanticLayer:s,securityContext:n}),b=s.getMetadata(),c=ee(b),g=u.model||"claude-sonnet-4-6",A=u.maxTurns||25,C=u.maxTokens||4096;try{l?.onChatStart?.({traceId:o,sessionId:r,message:t,model:g,historyLength:a?.length??0})}catch{}const k=[];if(a&&a.length>0){for(const h of a)if(h.role==="user")k.push({role:"user",content:h.content});else if(h.role==="assistant"){const F=[];if(h.content&&F.push({type:"text",text:h.content}),h.toolCalls&&h.toolCalls.length>0){for(const w of h.toolCalls)F.push({type:"tool_use",id:w.id,name:w.name,input:w.input||{}});k.push({role:"assistant",content:F}),k.push({role:"user",content:h.toolCalls.map(w=>({type:"tool_result",tool_use_id:w.id,content:typeof w.result=="string"?w.result:JSON.stringify(w.result??""),...w.status==="error"?{is_error:!0}:{}}))})}else F.length>0&&k.push({role:"assistant",content:h.content})}}k.push({role:"user",content:t});let E=0;try{for(let h=0;h<A;h++){E=h+1;const F=await y.messages.create({model:g,max_tokens:C,system:c,tools:d,messages:k,stream:!0}),w=[];let O=-1,V="",N="",M,R;const j=Date.now();for await(const v of F)switch(v.type){case"content_block_start":{O++;const p=v.content_block;p.type==="tool_use"?(w.push({type:"tool_use",id:p.id,name:p.name,input:{}}),V="",yield{type:"tool_use_start",data:{id:p.id,name:p.name,input:void 0}}):p.type==="text"&&w.push({type:"text",text:""});break}case"content_block_delta":{const p=v.delta;if(p.type==="text_delta"&&p.text){const D=w[O];D&&(D.text=(D.text||"")+p.text),yield{type:"text_delta",data:p.text}}else p.type==="input_json_delta"&&p.partial_json&&(V+=p.partial_json);break}case"content_block_stop":{const p=w[O];if(p?.type==="tool_use"&&V){try{p.input=JSON.parse(V)}catch{p.input={}}V=""}break}case"message_start":{const p=v.message;p?.usage?.input_tokens!=null&&(M=p.usage.input_tokens);break}case"message_delta":{const p=v.delta,D=v.usage;D?.output_tokens!=null&&(R=D.output_tokens),p.stop_reason&&(N=p.stop_reason);break}}try{l?.onGenerationEnd?.({traceId:o,turn:h,model:g,stopReason:N,inputTokens:M,outputTokens:R,durationMs:Date.now()-j})}catch{}if(k.push({role:"assistant",content:w}),N!=="tool_use")break;const _=[];for(const v of w){if(v.type!=="tool_use")continue;const p=v.name,D=v.input||{},S=v.id,Y=x.get(p);if(!Y){_.push({type:"tool_result",tool_use_id:S,content:`Unknown tool: ${p}`,is_error:!0}),yield{type:"tool_use_result",data:{id:S,name:p,result:`Unknown tool: ${p}`,isError:!0}};continue}const $=Date.now();try{const T=await Y(D);T.sideEffect&&(yield T.sideEffect),_.push({type:"tool_result",tool_use_id:S,content:T.result,...T.isError?{is_error:!0}:{}}),yield{type:"tool_use_result",data:{id:S,name:p,result:T.result,...T.isError?{isError:!0}:{}}};try{l?.onToolEnd?.({traceId:o,turn:h,toolName:p,toolUseId:S,isError:!!T.isError,durationMs:Date.now()-$})}catch{}}catch(T){const z=T instanceof Error?T.message:"Tool execution failed";_.push({type:"tool_result",tool_use_id:S,content:z,is_error:!0}),yield{type:"tool_use_result",data:{id:S,name:p,result:z,isError:!0}};try{l?.onToolEnd?.({traceId:o,turn:h,toolName:p,toolUseId:S,isError:!0,durationMs:Date.now()-$})}catch{}}}yield{type:"turn_complete",data:{}},k.push({role:"user",content:_})}try{l?.onChatEnd?.({traceId:o,sessionId:r,totalTurns:E,durationMs:Date.now()-m})}catch{}yield{type:"done",data:{sessionId:r||"",traceId:o}}}catch(h){try{l?.onChatEnd?.({traceId:o,sessionId:r,totalTurns:0,durationMs:Date.now()-m,error:h instanceof Error?h.message:"Unknown error"})}catch{}yield{type:"error",data:{message:Ie(h)}}}}function Ie(i){if(!i||!(i instanceof Error))return"Something went wrong. Please try again.";const t=i.message||"",a={overloaded_error:"The AI service is temporarily overloaded. Please try again in a moment.",rate_limit_error:"Too many requests. Please wait a moment and try again.",api_error:"The AI service encountered an error. Please try again.",authentication_error:"Authentication failed. Please check your API key configuration.",invalid_request_error:"There was a problem with the request. Please try again."},s=i;if(s.status||s.type){const n=s.error?.type||s.type||"";if(a[n])return a[n]}if(t.startsWith("{")||t.startsWith("Error: {")){try{const n=JSON.parse(t.replace(/^Error:\s*/,"")),u=n.error?.type||n.type||"";if(a[u])return a[u]}catch{}return"The AI service encountered an error. Please try again."}return t}exports.handleAgentChat=qe;
33
+ `)}`,isError:!0};const o={portlets:r.map(d=>{const x=d.chartType,b=x==="markdown",y=b?"query":d.analysisType||"query",g=y==="funnel"?"funnel":y==="flow"?"flow":y==="retention"?"retention":"query",A=d.query||"{}";let C;try{C=JSON.parse(A)}catch{C={}}const k={version:1,analysisType:g,activeView:"chart",charts:{[g]:{chartType:x,chartConfig:d.chartConfig||{},displayConfig:d.displayConfig||{}}},query:b?{}:C};return{id:d.id,title:d.title,analysisConfig:k,dashboardFilterMapping:d.dashboardFilterMapping,w:d.w,h:d.h,x:d.x,y:d.y}}),filters:e.filters,colorPalette:e.colorPalette},h=e.title,m=o.portlets.length,c=o.filters?.length||0;return{result:`Dashboard "${h}" created with ${m} portlets and ${c} filters.`,sideEffect:{type:"dashboard_saved",data:{title:h,description:e.description,dashboardConfig:o}}}}catch(r){return{result:`Failed to save dashboard: ${r instanceof Error?r.message:"Unknown error"}`,isError:!0}}}),s}async function*qe(i){const{message:t,history:a,semanticLayer:s,securityContext:n,agentConfig:u,apiKey:e}=i,r=i.sessionId||crypto.randomUUID(),l=u.observability,o=crypto.randomUUID(),h=Date.now();let m;try{const f=await import("@anthropic-ai/sdk");m=f.default||f.Anthropic||f}catch{yield{type:"error",data:{message:"@anthropic-ai/sdk is required. Install it with: npm install @anthropic-ai/sdk"}};return}const c=new m({apiKey:e}),d=Ee(),x=_e({semanticLayer:s,securityContext:n}),b=s.getMetadata(),y=ee(b),g=u.model||"claude-sonnet-4-6",A=u.maxTurns||25,C=u.maxTokens||4096;try{l?.onChatStart?.({traceId:o,sessionId:r,message:t,model:g,historyLength:a?.length??0})}catch{}const k=[];if(a&&a.length>0){for(const f of a)if(f.role==="user")k.push({role:"user",content:f.content});else if(f.role==="assistant"){const F=[];if(f.content&&F.push({type:"text",text:f.content}),f.toolCalls&&f.toolCalls.length>0){for(const w of f.toolCalls)F.push({type:"tool_use",id:w.id,name:w.name,input:w.input||{}});k.push({role:"assistant",content:F}),k.push({role:"user",content:f.toolCalls.map(w=>({type:"tool_result",tool_use_id:w.id,content:typeof w.result=="string"?w.result:JSON.stringify(w.result??""),...w.status==="error"?{is_error:!0}:{}}))})}else F.length>0&&k.push({role:"assistant",content:f.content})}}k.push({role:"user",content:t});let E=0;try{for(let f=0;f<A;f++){E=f+1;const F=await c.messages.create({model:g,max_tokens:C,system:y,tools:d,messages:k,stream:!0}),w=[];let O=-1,V="",N="",M,R;const j=Date.now();for await(const v of F)switch(v.type){case"content_block_start":{O++;const p=v.content_block;p.type==="tool_use"?(w.push({type:"tool_use",id:p.id,name:p.name,input:{}}),V="",yield{type:"tool_use_start",data:{id:p.id,name:p.name,input:void 0}}):p.type==="text"&&w.push({type:"text",text:""});break}case"content_block_delta":{const p=v.delta;if(p.type==="text_delta"&&p.text){const D=w[O];D&&(D.text=(D.text||"")+p.text),yield{type:"text_delta",data:p.text}}else p.type==="input_json_delta"&&p.partial_json&&(V+=p.partial_json);break}case"content_block_stop":{const p=w[O];if(p?.type==="tool_use"&&V){try{p.input=JSON.parse(V)}catch{p.input={}}V=""}break}case"message_start":{const p=v.message;p?.usage?.input_tokens!=null&&(M=p.usage.input_tokens);break}case"message_delta":{const p=v.delta,D=v.usage;D?.output_tokens!=null&&(R=D.output_tokens),p.stop_reason&&(N=p.stop_reason);break}}try{l?.onGenerationEnd?.({traceId:o,turn:f,model:g,stopReason:N,inputTokens:M,outputTokens:R,durationMs:Date.now()-j})}catch{}if(k.push({role:"assistant",content:w}),N!=="tool_use")break;const _=[];for(const v of w){if(v.type!=="tool_use")continue;const p=v.name,D=v.input||{},S=v.id,Y=x.get(p);if(!Y){_.push({type:"tool_result",tool_use_id:S,content:`Unknown tool: ${p}`,is_error:!0}),yield{type:"tool_use_result",data:{id:S,name:p,result:`Unknown tool: ${p}`,isError:!0}};continue}const $=Date.now();try{const T=await Y(D);T.sideEffect&&(yield T.sideEffect),_.push({type:"tool_result",tool_use_id:S,content:T.result,...T.isError?{is_error:!0}:{}}),yield{type:"tool_use_result",data:{id:S,name:p,result:T.result,...T.isError?{isError:!0}:{}}};try{l?.onToolEnd?.({traceId:o,turn:f,toolName:p,toolUseId:S,isError:!!T.isError,durationMs:Date.now()-$})}catch{}}catch(T){const z=T instanceof Error?T.message:"Tool execution failed";_.push({type:"tool_result",tool_use_id:S,content:z,is_error:!0}),yield{type:"tool_use_result",data:{id:S,name:p,result:z,isError:!0}};try{l?.onToolEnd?.({traceId:o,turn:f,toolName:p,toolUseId:S,isError:!0,durationMs:Date.now()-$})}catch{}}}yield{type:"turn_complete",data:{}},k.push({role:"user",content:_})}try{l?.onChatEnd?.({traceId:o,sessionId:r,totalTurns:E,durationMs:Date.now()-h})}catch{}yield{type:"done",data:{sessionId:r||"",traceId:o}}}catch(f){try{l?.onChatEnd?.({traceId:o,sessionId:r,totalTurns:0,durationMs:Date.now()-h,error:f instanceof Error?f.message:"Unknown error"})}catch{}yield{type:"error",data:{message:Ie(f)}}}}function Ie(i){if(!i||!(i instanceof Error))return"Something went wrong. Please try again.";const t=i.message||"",a={overloaded_error:"The AI service is temporarily overloaded. Please try again in a moment.",rate_limit_error:"Too many requests. Please wait a moment and try again.",api_error:"The AI service encountered an error. Please try again.",authentication_error:"Authentication failed. Please check your API key configuration.",invalid_request_error:"There was a problem with the request. Please try again."},s=i;if(s.status||s.type){const n=s.error?.type||s.type||"";if(a[n])return a[n]}if(t.startsWith("{")||t.startsWith("Error: {")){try{const n=JSON.parse(t.replace(/^Error:\s*/,"")),u=n.error?.type||n.type||"";if(a[u])return a[u]}catch{}return"The AI service encountered an error. Please try again."}return t}exports.handleAgentChat=qe;
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const N=require("hono"),a=require("../mcp-transport-8u9G5oNa.cjs"),b=require("../utils.cjs");var D=j=>{const p={...{origin:"*",allowMethods:["GET","HEAD","PUT","POST","DELETE","PATCH"],allowHeaders:[],exposeHeaders:[]},...j},I=(d=>typeof d=="string"?d==="*"?()=>d:c=>d===c?c:null:typeof d=="function"?d:c=>d.includes(c)?c:null)(p.origin),m=(d=>typeof d=="function"?d:Array.isArray(d)?()=>d:()=>[])(p.allowMethods);return async function(c,h){function g(C,i){c.res.headers.set(C,i)}const x=await I(c.req.header("origin")||"",c);if(x&&g("Access-Control-Allow-Origin",x),p.credentials&&g("Access-Control-Allow-Credentials","true"),p.exposeHeaders?.length&&g("Access-Control-Expose-Headers",p.exposeHeaders.join(",")),c.req.method==="OPTIONS"){p.origin!=="*"&&g("Vary","Origin"),p.maxAge!=null&&g("Access-Control-Max-Age",p.maxAge.toString());const C=await m(c.req.header("origin")||"",c);C.length&&g("Access-Control-Allow-Methods",C.join(","));let i=p.allowHeaders;if(!i?.length){const s=c.req.header("Access-Control-Request-Headers");s&&(i=s.split(/\s*,\s*/))}return i?.length&&(g("Access-Control-Allow-Headers",i.join(",")),c.res.headers.append("Vary","Access-Control-Request-Headers")),c.res.headers.delete("Content-Length"),c.res.headers.delete("Content-Type"),new Response(null,{headers:c.res.headers,status:204,statusText:"No Content"})}await h(),p.origin!=="*"&&c.header("Vary","Origin",{append:!0})}};function O(j){const{cubes:v,drizzle:p,schema:I,extractSecurityContext:m,engineType:d,cors:c,basePath:h="/cubejs-api/v1",cache:g,mcp:x={enabled:!0},agent:C}=j;if(!v||v.length===0)throw new Error("At least one cube must be provided in the cubes array");const i=new N.Hono;c&&i.use("/*",D(c));const s=new a.SemanticLayerCompiler({drizzle:p,schema:I,engineType:d,cache:g});if(v.forEach(r=>{s.registerCube(r)}),i.post(`${h}/load`,async r=>{try{const e=await r.req.json(),n=e.query||e,o=await m(r),t=s.validateQuery(n);if(!t.isValid)return r.json({error:`Query validation failed: ${t.errors.join(", ")}`},400);const u=r.req.header("x-cache-control")==="no-cache",l=await s.executeMultiCubeQuery(n,o,{skipCache:u});return r.json(b.formatCubeResponse(n,l,s))}catch(e){return console.error("Query execution error:",e),r.json({error:e instanceof Error?e.message:"Query execution failed"},500)}}),i.get(`${h}/load`,async r=>{try{const e=r.req.query("query");if(!e)return r.json({error:"Query parameter is required"},400);let n;try{n=JSON.parse(e)}catch{return r.json({error:"Invalid JSON in query parameter"},400)}const o=await m(r),t=s.validateQuery(n);if(!t.isValid)return r.json({error:`Query validation failed: ${t.errors.join(", ")}`},400);const u=r.req.header("x-cache-control")==="no-cache",l=await s.executeMultiCubeQuery(n,o,{skipCache:u});return r.json(b.formatCubeResponse(n,l,s))}catch(e){return console.error("Query execution error:",e),r.json({error:e instanceof Error?e.message:"Query execution failed"},500)}}),i.post(`${h}/batch`,async r=>{try{const e=await r.req.json(),{queries:n}=e;if(!n||!Array.isArray(n))return r.json({error:'Request body must contain a "queries" array'},400);if(n.length===0)return r.json({error:"Queries array cannot be empty"},400);const o=await m(r),t=r.req.header("x-cache-control")==="no-cache",u=await b.handleBatchRequest(n,o,s,{skipCache:t});return r.json(u)}catch(e){return console.error("Batch execution error:",e),r.json({error:e instanceof Error?e.message:"Batch execution failed"},500)}}),i.get(`${h}/meta`,r=>{try{const e=s.getMetadata();return r.json(b.formatMetaResponse(e))}catch(e){return console.error("Metadata error:",e),r.json({error:e instanceof Error?e.message:"Failed to fetch metadata"},500)}}),i.post(`${h}/sql`,async r=>{try{const e=await r.req.json(),n=await m(r),o=s.validateQuery(e);if(!o.isValid)return r.json({error:`Query validation failed: ${o.errors.join(", ")}`},400);const t=e.measures?.[0]||e.dimensions?.[0];if(!t)return r.json({error:"No measures or dimensions specified"},400);const u=t.split(".")[0],l=await s.generateSQL(u,e,n);return r.json(b.formatSqlResponse(e,l))}catch(e){return console.error("SQL generation error:",e),r.json({error:e instanceof Error?e.message:"SQL generation failed"},500)}}),i.get(`${h}/sql`,async r=>{try{const e=r.req.query("query");if(!e)return r.json({error:"Query parameter is required"},400);const n=JSON.parse(e),o=await m(r),t=s.validateQuery(n);if(!t.isValid)return r.json({error:`Query validation failed: ${t.errors.join(", ")}`},400);const u=n.measures?.[0]||n.dimensions?.[0];if(!u)return r.json({error:"No measures or dimensions specified"},400);const l=u.split(".")[0],q=await s.generateSQL(l,n,o);return r.json(b.formatSqlResponse(n,q))}catch(e){return console.error("SQL generation error:",e),r.json({error:e instanceof Error?e.message:"SQL generation failed"},500)}}),i.post(`${h}/dry-run`,async r=>{try{const e=await r.req.json(),n=e.query||e,o=await m(r),t=await b.handleDryRun(n,o,s);return r.json(t)}catch(e){return console.error("Dry-run error:",e),r.json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1},400)}}),i.get(`${h}/dry-run`,async r=>{try{const e=r.req.query("query");if(!e)return r.json({error:"Query parameter is required",valid:!1},400);const n=JSON.parse(e),o=await m(r),t=await b.handleDryRun(n,o,s);return r.json(t)}catch(e){return console.error("Dry-run error:",e),r.json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1},400)}}),i.post(`${h}/explain`,async r=>{try{const e=await r.req.json(),n=e.query||e,o=e.options||{},t=await m(r),u=s.validateQuery(n);if(!u.isValid)return r.json({error:`Query validation failed: ${u.errors.join(", ")}`},400);const l=await s.explainQuery(n,t,o);return r.json(l)}catch(e){return console.error("Explain error:",e),r.json({error:e instanceof Error?e.message:"Explain query failed"},500)}}),C&&i.post(`${h}/agent/chat`,async r=>{try{const{handleAgentChat:e}=await Promise.resolve().then(()=>require("../handler-hwoGzGex.cjs")),n=await r.req.json(),{message:o,sessionId:t,history:u}=n;if(!o||typeof o!="string")return r.json({error:"message is required and must be a string"},400);let l=(C.apiKey||"").trim();if(C.allowClientApiKey){const w=r.req.header("x-agent-api-key");w&&(l=w.trim())}if(!l)return r.json({error:"No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header."},401);const q=await m(r),R=new TextEncoder,f=new ReadableStream({async start(w){try{const E=e({message:o,sessionId:t,history:u,semanticLayer:s,securityContext:q,agentConfig:C,apiKey:l});for await(const y of E){const S=`data: ${JSON.stringify(y)}
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const N=require("hono"),a=require("../mcp-transport-8u9G5oNa.cjs"),b=require("../utils.cjs");var D=j=>{const p={...{origin:"*",allowMethods:["GET","HEAD","PUT","POST","DELETE","PATCH"],allowHeaders:[],exposeHeaders:[]},...j},I=(d=>typeof d=="string"?d==="*"?()=>d:c=>d===c?c:null:typeof d=="function"?d:c=>d.includes(c)?c:null)(p.origin),m=(d=>typeof d=="function"?d:Array.isArray(d)?()=>d:()=>[])(p.allowMethods);return async function(c,h){function g(C,i){c.res.headers.set(C,i)}const x=await I(c.req.header("origin")||"",c);if(x&&g("Access-Control-Allow-Origin",x),p.credentials&&g("Access-Control-Allow-Credentials","true"),p.exposeHeaders?.length&&g("Access-Control-Expose-Headers",p.exposeHeaders.join(",")),c.req.method==="OPTIONS"){p.origin!=="*"&&g("Vary","Origin"),p.maxAge!=null&&g("Access-Control-Max-Age",p.maxAge.toString());const C=await m(c.req.header("origin")||"",c);C.length&&g("Access-Control-Allow-Methods",C.join(","));let i=p.allowHeaders;if(!i?.length){const s=c.req.header("Access-Control-Request-Headers");s&&(i=s.split(/\s*,\s*/))}return i?.length&&(g("Access-Control-Allow-Headers",i.join(",")),c.res.headers.append("Vary","Access-Control-Request-Headers")),c.res.headers.delete("Content-Length"),c.res.headers.delete("Content-Type"),new Response(null,{headers:c.res.headers,status:204,statusText:"No Content"})}await h(),p.origin!=="*"&&c.header("Vary","Origin",{append:!0})}};function O(j){const{cubes:v,drizzle:p,schema:I,extractSecurityContext:m,engineType:d,cors:c,basePath:h="/cubejs-api/v1",cache:g,mcp:x={enabled:!0},agent:C}=j;if(!v||v.length===0)throw new Error("At least one cube must be provided in the cubes array");const i=new N.Hono;c&&i.use("/*",D(c));const s=new a.SemanticLayerCompiler({drizzle:p,schema:I,engineType:d,cache:g});if(v.forEach(r=>{s.registerCube(r)}),i.post(`${h}/load`,async r=>{try{const e=await r.req.json(),n=e.query||e,o=await m(r),t=s.validateQuery(n);if(!t.isValid)return r.json({error:`Query validation failed: ${t.errors.join(", ")}`},400);const u=r.req.header("x-cache-control")==="no-cache",l=await s.executeMultiCubeQuery(n,o,{skipCache:u});return r.json(b.formatCubeResponse(n,l,s))}catch(e){return console.error("Query execution error:",e),r.json({error:e instanceof Error?e.message:"Query execution failed"},500)}}),i.get(`${h}/load`,async r=>{try{const e=r.req.query("query");if(!e)return r.json({error:"Query parameter is required"},400);let n;try{n=JSON.parse(e)}catch{return r.json({error:"Invalid JSON in query parameter"},400)}const o=await m(r),t=s.validateQuery(n);if(!t.isValid)return r.json({error:`Query validation failed: ${t.errors.join(", ")}`},400);const u=r.req.header("x-cache-control")==="no-cache",l=await s.executeMultiCubeQuery(n,o,{skipCache:u});return r.json(b.formatCubeResponse(n,l,s))}catch(e){return console.error("Query execution error:",e),r.json({error:e instanceof Error?e.message:"Query execution failed"},500)}}),i.post(`${h}/batch`,async r=>{try{const e=await r.req.json(),{queries:n}=e;if(!n||!Array.isArray(n))return r.json({error:'Request body must contain a "queries" array'},400);if(n.length===0)return r.json({error:"Queries array cannot be empty"},400);const o=await m(r),t=r.req.header("x-cache-control")==="no-cache",u=await b.handleBatchRequest(n,o,s,{skipCache:t});return r.json(u)}catch(e){return console.error("Batch execution error:",e),r.json({error:e instanceof Error?e.message:"Batch execution failed"},500)}}),i.get(`${h}/meta`,r=>{try{const e=s.getMetadata();return r.json(b.formatMetaResponse(e))}catch(e){return console.error("Metadata error:",e),r.json({error:e instanceof Error?e.message:"Failed to fetch metadata"},500)}}),i.post(`${h}/sql`,async r=>{try{const e=await r.req.json(),n=await m(r),o=s.validateQuery(e);if(!o.isValid)return r.json({error:`Query validation failed: ${o.errors.join(", ")}`},400);const t=e.measures?.[0]||e.dimensions?.[0];if(!t)return r.json({error:"No measures or dimensions specified"},400);const u=t.split(".")[0],l=await s.generateSQL(u,e,n);return r.json(b.formatSqlResponse(e,l))}catch(e){return console.error("SQL generation error:",e),r.json({error:e instanceof Error?e.message:"SQL generation failed"},500)}}),i.get(`${h}/sql`,async r=>{try{const e=r.req.query("query");if(!e)return r.json({error:"Query parameter is required"},400);const n=JSON.parse(e),o=await m(r),t=s.validateQuery(n);if(!t.isValid)return r.json({error:`Query validation failed: ${t.errors.join(", ")}`},400);const u=n.measures?.[0]||n.dimensions?.[0];if(!u)return r.json({error:"No measures or dimensions specified"},400);const l=u.split(".")[0],q=await s.generateSQL(l,n,o);return r.json(b.formatSqlResponse(n,q))}catch(e){return console.error("SQL generation error:",e),r.json({error:e instanceof Error?e.message:"SQL generation failed"},500)}}),i.post(`${h}/dry-run`,async r=>{try{const e=await r.req.json(),n=e.query||e,o=await m(r),t=await b.handleDryRun(n,o,s);return r.json(t)}catch(e){return console.error("Dry-run error:",e),r.json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1},400)}}),i.get(`${h}/dry-run`,async r=>{try{const e=r.req.query("query");if(!e)return r.json({error:"Query parameter is required",valid:!1},400);const n=JSON.parse(e),o=await m(r),t=await b.handleDryRun(n,o,s);return r.json(t)}catch(e){return console.error("Dry-run error:",e),r.json({error:e instanceof Error?e.message:"Dry-run validation failed",valid:!1},400)}}),i.post(`${h}/explain`,async r=>{try{const e=await r.req.json(),n=e.query||e,o=e.options||{},t=await m(r),u=s.validateQuery(n);if(!u.isValid)return r.json({error:`Query validation failed: ${u.errors.join(", ")}`},400);const l=await s.explainQuery(n,t,o);return r.json(l)}catch(e){return console.error("Explain error:",e),r.json({error:e instanceof Error?e.message:"Explain query failed"},500)}}),C&&i.post(`${h}/agent/chat`,async r=>{try{const{handleAgentChat:e}=await Promise.resolve().then(()=>require("../handler-D4MVKkVy.cjs")),n=await r.req.json(),{message:o,sessionId:t,history:u}=n;if(!o||typeof o!="string")return r.json({error:"message is required and must be a string"},400);let l=(C.apiKey||"").trim();if(C.allowClientApiKey){const w=r.req.header("x-agent-api-key");w&&(l=w.trim())}if(!l)return r.json({error:"No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header."},401);const q=await m(r),R=new TextEncoder,f=new ReadableStream({async start(w){try{const E=e({message:o,sessionId:t,history:u,semanticLayer:s,securityContext:q,agentConfig:C,apiKey:l});for await(const y of E){const S=`data: ${JSON.stringify(y)}
2
2
 
3
3
  `;w.enqueue(R.encode(S))}}catch(E){const y={type:"error",data:{message:E instanceof Error?E.message:"Stream failed"}};w.enqueue(R.encode(`data: ${JSON.stringify(y)}
4
4
 
@@ -212,7 +212,7 @@ function te(C) {
212
212
  }
213
213
  }), q && a.post(`${m}/agent/chat`, async (r) => {
214
214
  try {
215
- const { handleAgentChat: e } = await import("../handler-DefTXJpi.js"), n = await r.req.json(), { message: o, sessionId: t, history: c } = n;
215
+ const { handleAgentChat: e } = await import("../handler-BV4JuWNW.js"), n = await r.req.json(), { message: o, sessionId: t, history: c } = n;
216
216
  if (!o || typeof o != "string")
217
217
  return r.json({ error: "message is required and must be a string" }, 400);
218
218
  let u = (q.apiKey || "").trim();
@@ -2,7 +2,7 @@
2
2
 
3
3
  `)),H.enqueue(h.encode(f.serializeSseEvent(x,E))),H.close()}}),O=new Headers({"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive",...g});if(s){const H=R(t,s);Object.entries(H).forEach(([b,P])=>O.set(b,P))}return new r.NextResponse(S,{status:200,headers:O})}return v(x,200,g)}catch(m){if(f.isNotification(p))return process.env.NODE_ENV!=="test"&&console.error("Next.js MCP notification processing error:",m),new r.NextResponse(null,{status:202});process.env.NODE_ENV!=="test"&&console.error("Next.js MCP RPC handler error:",m);const C=m?.code??-32603,g=m?.data,x=m.message||"MCP request failed",h=f.buildJsonRpcError(p.id??null,C,x,g);if(w){const E=new TextEncoder,S=f.primeEventId(),O=new ReadableStream({start(b){b.enqueue(E.encode(`id: ${S}
4
4
 
5
- `)),b.enqueue(E.encode(f.serializeSseEvent(h,S))),b.close()}}),H=new Headers({"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"});if(s){const b=R(t,s);Object.entries(b).forEach(([P,I])=>H.set(P,I))}return new r.NextResponse(O,{status:200,headers:H})}return v(h,200)}}}function Q(n){const{extractSecurityContext:o,cors:s,agent:c}=n;if(!c)throw new Error("agent config is required for createAgentChatHandler");const d=j(n);return async function(t,e){try{if(t.method!=="POST")return r.NextResponse.json({error:"Method not allowed - use POST"},{status:405});const{handleAgentChat:l}=await Promise.resolve().then(()=>require("../handler-hwoGzGex.cjs")),y=await t.json(),{message:u,sessionId:p,history:w}=y;if(!u||typeof u!="string")return r.NextResponse.json({error:"message is required and must be a string"},{status:400});let N=(c.apiKey||"").trim();if(c.allowClientApiKey){const x=t.headers.get("x-agent-api-key");x&&(N=x.trim())}if(!N)return r.NextResponse.json({error:"No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header."},{status:401});const v=await o(t,e),m=new TextEncoder,C=new ReadableStream({async start(x){try{const h=l({message:u,sessionId:p,history:w,semanticLayer:d,securityContext:v,agentConfig:c,apiKey:N});for await(const E of h){const S=`data: ${JSON.stringify(E)}
5
+ `)),b.enqueue(E.encode(f.serializeSseEvent(h,S))),b.close()}}),H=new Headers({"Content-Type":"text/event-stream","Cache-Control":"no-cache",Connection:"keep-alive"});if(s){const b=R(t,s);Object.entries(b).forEach(([P,I])=>H.set(P,I))}return new r.NextResponse(O,{status:200,headers:H})}return v(h,200)}}}function Q(n){const{extractSecurityContext:o,cors:s,agent:c}=n;if(!c)throw new Error("agent config is required for createAgentChatHandler");const d=j(n);return async function(t,e){try{if(t.method!=="POST")return r.NextResponse.json({error:"Method not allowed - use POST"},{status:405});const{handleAgentChat:l}=await Promise.resolve().then(()=>require("../handler-D4MVKkVy.cjs")),y=await t.json(),{message:u,sessionId:p,history:w}=y;if(!u||typeof u!="string")return r.NextResponse.json({error:"message is required and must be a string"},{status:400});let N=(c.apiKey||"").trim();if(c.allowClientApiKey){const x=t.headers.get("x-agent-api-key");x&&(N=x.trim())}if(!N)return r.NextResponse.json({error:"No API key configured. Set agent.apiKey in server config or send X-Agent-Api-Key header."},{status:401});const v=await o(t,e),m=new TextEncoder,C=new ReadableStream({async start(x){try{const h=l({message:u,sessionId:p,history:w,semanticLayer:d,securityContext:v,agentConfig:c,apiKey:N});for await(const E of h){const S=`data: ${JSON.stringify(E)}
6
6
 
7
7
  `;x.enqueue(m.encode(S))}}catch(h){const E={type:"error",data:{message:h instanceof Error?h.message:"Stream failed"}};x.enqueue(m.encode(`data: ${JSON.stringify(E)}
8
8
 
@@ -519,7 +519,7 @@ function oe(n) {
519
519
  { error: "Method not allowed - use POST" },
520
520
  { status: 405 }
521
521
  );
522
- const { handleAgentChat: u } = await import("../handler-DefTXJpi.js"), y = await r.json(), { message: l, sessionId: f, history: v } = y;
522
+ const { handleAgentChat: u } = await import("../handler-BV4JuWNW.js"), y = await r.json(), { message: l, sessionId: f, history: v } = y;
523
523
  if (!l || typeof l != "string")
524
524
  return t.json(
525
525
  { error: "message is required and must be a string" },
@@ -743,7 +743,7 @@ Chart config requirements by type:`];for(const t of i){const s=ct[t];if(!s)conti
743
743
  `+HT(rt)+`
744
744
  The query is validated before adding. The portlet fetches its own data.`,input_schema:{type:"object",properties:{title:{type:"string",description:"Title for the visualization"},query:{type:"string",description:'JSON string of the query. Standard: {"measures":[...],"dimensions":[...]}. Funnel: {"funnel":{"bindingKey":"...","timeDimension":"...","steps":[...]}}. Flow: {"flow":{"bindingKey":"...","timeDimension":"...","eventDimension":"...","startingStep":{...}}}. Retention: {"retention":{"timeDimension":"...","bindingKey":"...","dateRange":{"start":"...","end":"..."},"granularity":"...","periods":N}}.'},chartType:{type:"string",enum:rt,description:"Chart type to render"},chartConfig:{type:"object",properties:{xAxis:{type:"array",items:{type:"string"}},yAxis:{type:"array",items:{type:"string"}},series:{type:"array",items:{type:"string"}},sizeField:{type:"string"},colorField:{type:"string"}},description:"Chart axis configuration"},displayConfig:{type:"object",properties:{showLegend:{type:"boolean"},showGrid:{type:"boolean"},showTooltip:{type:"boolean"},stacked:{type:"boolean"},orientation:{type:"string",enum:["horizontal","vertical"]}},description:"Chart display configuration"}},required:["title","query","chartType"]}},{name:"add_markdown",description:"Add an explanation or analysis text block to the notebook. Use markdown formatting. Use this to explain findings, methodology, and insights alongside visualizations.",input_schema:{type:"object",properties:{title:{type:"string",description:"Optional title for the text block"},content:{type:"string",description:"Markdown content to display"}},required:["content"]}},{name:"save_as_dashboard",description:"Convert the current notebook analysis into a persistent dashboard. Constructs a professional DashboardConfig with proper grid layout, section headers (markdown portlets), and dashboard-level filters. Call this when the user asks to save/export the notebook as a dashboard.",input_schema:{type:"object",properties:{title:{type:"string",description:"Dashboard title"},description:{type:"string",description:"Optional dashboard description"},portlets:{type:"array",items:{type:"object",properties:{id:{type:"string",description:"Unique portlet ID"},title:{type:"string",description:"Portlet title"},chartType:{type:"string",enum:rt,description:'Chart type. Use "markdown" for section headers.'},query:{type:"string",description:"JSON string of the query. Omit or leave empty for markdown portlets."},chartConfig:{type:"object",properties:{xAxis:{type:"array",items:{type:"string"}},yAxis:{type:"array",items:{type:"string"}},series:{type:"array",items:{type:"string"}},sizeField:{type:"string"},colorField:{type:"string"}},description:"Chart axis configuration"},displayConfig:{type:"object",description:"Chart display configuration (for markdown: { content, hideHeader, transparentBackground, autoHeight })"},dashboardFilterMapping:{type:"array",items:{type:"string"},description:"Array of dashboard filter IDs that apply to this portlet"},analysisType:{type:"string",enum:["query","funnel","flow","retention"],description:'Analysis type (default: "query")'},w:{type:"number",description:"Grid width (1-12)"},h:{type:"number",description:"Grid height in row units"},x:{type:"number",description:"Grid x position (0-11)"},y:{type:"number",description:"Grid y position"}},required:["id","title","chartType","w","h","x","y"]},description:"Array of portlet configurations for the dashboard"},filters:{type:"array",items:{type:"object",properties:{id:{type:"string",description:"Unique filter ID"},label:{type:"string",description:"Display label for the filter"},filter:{type:"object",properties:{member:{type:"string"},operator:{type:"string"},values:{type:"array",items:{}}},required:["member","operator"],description:"The filter definition"},isUniversalTime:{type:"boolean",description:"When true, applies to all time dimensions in portlets"}},required:["id","label","filter"]},description:"Dashboard-level filters"},colorPalette:{type:"string",description:"Color palette name"}},required:["title","portlets"]}}]}function Zs(i){const{semanticLayer:e,securityContext:t}=i,s=new Map;s.set("discover_cubes",async E=>{const a=await Xo(e,{topic:E.topic,intent:E.intent,limit:E.limit,minScore:E.minScore});return{result:JSON.stringify(a,null,2)}}),s.set("get_cube_metadata",async()=>{const E=e.getMetadata();return{result:JSON.stringify(E,null,2)}});const n=new Map;for(const E of e.getMetadata())n.set(E.name,{measures:(E.measures||[]).map(a=>a.name),dimensions:(E.dimensions||[]).map(a=>a.name)});const r=(E,a)=>{const A=n.get(E),T=a==="measures"?A?.measures:A?.dimensions;return!T||T.length===0?"":` Available ${a}: ${T.slice(0,5).map(R=>`"${R}"`).join(", ")}`};return s.set("execute_query",async E=>{try{const a=(l,S)=>{if(!Array.isArray(l))return;const N=[],I=[];for(const u of l){if(typeof u!="string"){I.push(u);continue}const c=u.split(".");if(c.length===1){N.push(`"${u}" is not valid — must be "CubeName.fieldName".${r(u,S)}`);continue}if(c.length===3&&c[0]===c[1]){const O=`${c[0]}.${c[2]}`;I.push(O);continue}if(c.length===2&&c[0]===c[1]){N.push(`"${u}" is WRONG — "${c[0]}" is the cube name, not a ${S.replace(/s$/,"")}.${r(c[0],S)}`);continue}I.push(u)}if(N.length>0)throw new Error(`Invalid ${S}:
745
745
  ${N.join(`
746
- `)}`);return I};E.measures=a(E.measures,"measures")??E.measures,E.dimensions=a(E.dimensions,"dimensions")??E.dimensions;const A=l=>{const S=l.split(".");return S.length===3&&S[0]===S[1]?`${S[0]}.${S[2]}`:l};if(Array.isArray(E.filters))for(const l of E.filters)typeof l.member=="string"&&(l.member=A(l.member));if(Array.isArray(E.timeDimensions))for(const l of E.timeDimensions)typeof l.dimension=="string"&&(l.dimension=A(l.dimension));let T;E.funnel?T={funnel:E.funnel}:E.flow?T={flow:E.flow}:E.retention?T={retention:E.retention}:T={measures:E.measures,dimensions:E.dimensions,filters:E.filters,timeDimensions:E.timeDimensions,order:E.order,limit:E.limit};const R=await Ko(e,t,{query:T});return{result:JSON.stringify({rowCount:R.data.length,data:R.data,annotation:R.annotation},null,2)}}catch(a){const A={measures:E.measures,dimensions:E.dimensions,filters:E.filters,timeDimensions:E.timeDimensions,order:E.order,limit:E.limit,...E.funnel?{funnel:E.funnel}:{},...E.flow?{flow:E.flow}:{},...E.retention?{retention:E.retention}:{}};return{result:`Query execution failed: ${a instanceof Error?a.message:"Unknown error"}
746
+ `)}`);return I};E.measures=a(E.measures,"measures")??E.measures,E.dimensions=a(E.dimensions,"dimensions")??E.dimensions;const A=l=>{const S=l.split(".");return S.length===3&&S[0]===S[1]?`${S[0]}.${S[2]}`:l};if(Array.isArray(E.filters))for(const l of E.filters)typeof l.member=="string"&&(l.member=A(l.member));if(Array.isArray(E.timeDimensions))for(const l of E.timeDimensions)typeof l.dimension=="string"&&(l.dimension=A(l.dimension));if(E.order&&typeof E.order=="object"&&!Array.isArray(E.order)){const l={};for(const[S,N]of Object.entries(E.order))l[A(S)]=N;E.order=l}let T;E.funnel?T={funnel:E.funnel}:E.flow?T={flow:E.flow}:E.retention?T={retention:E.retention}:T={measures:E.measures,dimensions:E.dimensions,filters:E.filters,timeDimensions:E.timeDimensions,order:E.order,limit:E.limit};const R=await Ko(e,t,{query:T});return{result:JSON.stringify({rowCount:R.data.length,data:R.data,annotation:R.annotation},null,2)}}catch(a){const A={measures:E.measures,dimensions:E.dimensions,filters:E.filters,timeDimensions:E.timeDimensions,order:E.order,limit:E.limit,...E.funnel?{funnel:E.funnel}:{},...E.flow?{flow:E.flow}:{},...E.retention?{retention:E.retention}:{}};return{result:`Query execution failed: ${a instanceof Error?a.message:"Unknown error"}
747
747
 
748
748
  Attempted query:
749
749
  ${JSON.stringify(A,null,2)}`,isError:!0}}}),s.set("add_portlet",async E=>{const A={number:"kpiNumber",retention:"retentionHeatmap"}[E.chartType]??E.chartType;let T;try{T=JSON.parse(E.query)}catch{return{result:"Invalid query: could not parse JSON string. Ensure `query` is a valid JSON string.",isError:!0}}const R=e.validateQuery(T);if(!R.isValid)return{result:`Invalid query — fix these errors and retry:
@@ -32919,6 +32919,12 @@ ${N.join(`
32919
32919
  if (Array.isArray(E.timeDimensions))
32920
32920
  for (const S of E.timeDimensions)
32921
32921
  typeof S.dimension == "string" && (S.dimension = A(S.dimension));
32922
+ if (E.order && typeof E.order == "object" && !Array.isArray(E.order)) {
32923
+ const S = {};
32924
+ for (const [l, N] of Object.entries(E.order))
32925
+ S[A(l)] = N;
32926
+ E.order = S;
32927
+ }
32922
32928
  let o;
32923
32929
  E.funnel ? o = { funnel: E.funnel } : E.flow ? o = { flow: E.flow } : E.retention ? o = { retention: E.retention } : o = {
32924
32930
  measures: E.measures,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "drizzle-cube",
3
- "version": "0.4.18",
3
+ "version": "0.4.19",
4
4
  "description": "Drizzle ORM-first semantic layer with Cube.js compatibility. Type-safe analytics and dashboards with SQL injection protection.",
5
5
  "main": "./dist/server/index.js",
6
6
  "types": "./dist/server/index.d.ts",