@xpert-ai/plugin-analytics-data-xpert 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  # data-xpert analytics provider
2
2
 
3
+ The Live Dashboard Agent generates this provider’s Definition through `analytics_definition_connections` → `analytics_definition_inspect` → `analytics_definition_register`. Existing toolset/Integration credentials stay in their original configuration entries. Settings → Analytics is for viewing, correction and version management; users do not fill internal IDs. See [the authoring guide](../../../../docs/analytics-providers.md#automatic-definition-generation).
4
+ Configure API URL and audience in Settings → Integrations → data-xpert Analytics. The Agent selects this integration from the authorized catalog.
5
+
3
6
  System plugin `@xpert-ai/plugin-analytics-data-xpert`; provider `data-xpert.ontology`, operation `action`, version 1.
4
7
 
5
- Contributes the data-xpert DataSource type through DataSourceType sync. Server configuration: baseUrl (including /api), audience, assistantCode, assistantXpertId, workspaceId. No database runner is required. Platform code has no ontology dependency.
6
8
 
7
9
  ```json
8
10
  {"resourceId":"datax-semantic-model-<published-model-id>","actionTypeCode":"sql.query_cube_slice","target":{"entityTypeCode":"semantic_cube","entityRef":"<published-model-id>::Sales"},"params":{"dimensions":["Region"],"measures":["Amount"],"filters":[{"field":"Region","op":"contains","value":"west"}],"limit":100},"parameterPaths":{"region":["filters","0","value"]}}
@@ -16,4 +18,28 @@ Run `corepack pnpm exec nx run @xpert-ai/plugin-analytics-data-xpert:test` with
16
18
 
17
19
  Discovery preserves each adapter's `definition` (identity, version, native metadata and instructions) and each Action's input schema. Semantic Model, OData and future registered read-only adapters share this transport without sharing query syntax.
18
20
 
21
+ Before constructing a dashboard binding, call `dashboard_discover` with the selected source/provider and `requestJson` encoding:
22
+
23
+ ```json
24
+ {"kind":"query-schema","payload":{"resourceId":"<discovered-resource>","actionTypeCode":"<discovered-read-action>","target":{"entityRef":"<canonical-target>"},"params":{},"parameterPaths":{}},"parameters":{}}
25
+ ```
26
+
27
+ Omit `datasetId` to let the provider generate a valid local output key from the native payload, including its projection. An optional `datasetId` accepts a nonempty string of up to 512 characters: valid output keys (a letter followed by up to 79 letters, digits, `_`, `.` or `-`) are preserved; native references or other aliases are replaced with a generated key. Native resource IDs and targets are unchanged. Copy the returned `output.datasetId` as well as its fields.
28
+
29
+ Use the Action's actual bounded query arguments in `params`. This plugin-owned discovery request executes the same authorized `/agent-workbench/analytics/query` path as preview, with the current actor, host row limit and cancellation signal. It does not require a new DataXpert discovery endpoint. Copy `items[0].metadata.output` into the preview binding and use the same native payload and parameter values. The response includes no business rows and is not a preview receipt; a successful `dashboard_preview` is still required before saving.
30
+
31
+ Output fields retain exact native keys and declared types, roles and nullability, including empty results. Decimal/int64 cells remain exact strings. Stable field IDs are derived from native keys; duplicate semantic selectors use exact keys instead. Grain is left empty until query semantics establish uniqueness. Missing declarations fail with `DATAX_RESULT_SCHEMA_UNAVAILABLE`; duplicate native keys fail with `DATAX_RESULT_SCHEMA_AMBIGUOUS`. Legacy authored bindings can still execute without these optional declarations. Rediscover after changing the projection; existing incorrect saved bindings must be corrected and previewed before saving.
32
+
33
+ Release compatibility: this path uses existing exports from the published SDK/contracts 3.26.1 and does not require a new host capability. The companion SDK change only adds optional column metadata to its TypeScript contract. Merge and publish this plugin's Changeset, then update the installed plugin; merging source alone does not update an existing deployment. Schema authoring requires DataXpert to supply declared column metadata (including the decimal fix in data-xpert PR #108). SAP query errors, authentication, and model-generated malformed tool calls remain separate acceptance checks.
34
+
35
+ After building this plugin and Live Dashboard, run `node plugin-dev-harness/verify-datax-dashboard-schema.mjs` from the repository root. It invokes the real discovery and preview tools, provider and HTTP parser with published SDKs. Its local HTTP source and host-service fixtures do not establish customer SSO, SAP or browser acceptance.
36
+
19
37
  See [architecture and Binding usage](docs/index.mdx) and [compatibility and verification](docs/verification.mdx).
38
+ # Public query diagnostics
39
+
40
+ Non-success HTTP responses may contain the shared `xpert.analytics.diagnostic` v1 envelope.
41
+ Only validated public causes and opaque audit references cross into `AnalyticsProviderError`.
42
+ Malformed, sensitive, oversized or unfinished bodies use the existing status-only fallback.
43
+ Parameter mapping failures use provider-authored repair guidance without echoing values.
44
+
45
+ Structured diagnostics require `@xpert-ai/contracts` and `@xpert-ai/plugin-sdk` 3.32.0 or a compatible later 3.x release.
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ const node_path_1 = require("node:path");
5
5
  const plugin_1 = require("./lib/plugin");
6
6
  const packageJson = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, '../package.json'), 'utf8'));
7
7
  const plugin = {
8
- meta: { name: packageJson.name, version: packageJson.version, category: 'database', displayName: 'data-xpert Analytics', description: 'data-xpert Analytics', author: 'XpertAI', level: packageJson.xpert.plugin.level, artifactNamespace: packageJson.xpert.plugin.artifactNamespace },
8
+ meta: { name: packageJson.name, version: packageJson.version, category: 'tools', displayName: 'data-xpert Analytics', description: 'data-xpert Analytics', author: 'XpertAI', level: packageJson.xpert.plugin.level, artifactNamespace: packageJson.xpert.plugin.artifactNamespace },
9
9
  register: () => ({ module: plugin_1.AnalyticsDataXpertPlugin })
10
10
  };
11
11
  exports.default = plugin;
@@ -0,0 +1,14 @@
1
+ import type { AnalyticsBinding, AnalyticsValue, JSONValue, AnalyticsDiscoveryResult } from '@xpert-ai/contracts';
2
+ export { dataXpertPayloadSchema } from './payload';
3
+ import { type AnalyticsProviderContext, type AnalyticsProviderResult, type RuntimeCapabilityRegistry } from '@xpert-ai/plugin-sdk';
4
+ export type DataXpertClientContext = Pick<AnalyticsProviderContext, 'actor' | 'options' | 'signal' | 'deadlineAt' | 'maxRows'>;
5
+ export declare class DataXpertAnalyticsClient {
6
+ private readonly runtime;
7
+ constructor(runtime: RuntimeCapabilityRegistry);
8
+ discover(request: JSONValue, context: DataXpertClientContext): Promise<AnalyticsDiscoveryResult>;
9
+ private request;
10
+ execute(binding: AnalyticsBinding, parameters: Record<string, AnalyticsValue>, context: DataXpertClientContext): Promise<AnalyticsProviderResult>;
11
+ private query;
12
+ }
13
+ /** Typed paths replace whole parameter values, never splice text into SQL/MDX/OData. */
14
+ export declare function bindParameters(params: Record<string, JSONValue>, paths: Record<string, string[]>, parameters: Record<string, AnalyticsValue>): Record<string, JSONValue>;
@@ -0,0 +1,169 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.DataXpertAnalyticsClient = exports.dataXpertPayloadSchema = void 0;
16
+ exports.bindParameters = bindParameters;
17
+ const common_1 = require("@nestjs/common");
18
+ const v3_1 = require("zod/v3");
19
+ const discovery_1 = require("./discovery");
20
+ const diagnostic_1 = require("./diagnostic");
21
+ const payload_1 = require("./payload");
22
+ var payload_2 = require("./payload");
23
+ Object.defineProperty(exports, "dataXpertPayloadSchema", { enumerable: true, get: function () { return payload_2.dataXpertPayloadSchema; } });
24
+ const import_targets_1 = require("./import-targets");
25
+ const result_schema_1 = require("./result-schema");
26
+ const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
27
+ const scalar = v3_1.z.union([v3_1.z.string(), v3_1.z.number().finite(), v3_1.z.boolean(), v3_1.z.null()]);
28
+ const json = v3_1.z.lazy(() => v3_1.z.union([scalar, v3_1.z.array(json), v3_1.z.record(json)]));
29
+ const querySchemaRequestSchema = v3_1.z.object({
30
+ kind: v3_1.z.literal('query-schema'), datasetId: v3_1.z.string().min(1).max(512).optional(),
31
+ payload: payload_1.dataXpertPayloadSchema, parameters: v3_1.z.record(scalar).default({})
32
+ }).strict();
33
+ /** Trusted DataSource configuration; never merge it with payloads, controls or discovery requests. */
34
+ const optionsSchema = v3_1.z.object({
35
+ baseUrl: v3_1.z.string().url(), audience: v3_1.z.string().min(1),
36
+ assistantCode: v3_1.z.string().min(1), assistantXpertId: v3_1.z.string().uuid(), workspaceId: v3_1.z.string().uuid()
37
+ }).strict();
38
+ const responseSchema = v3_1.z.object({
39
+ effect: v3_1.z.object({ version: v3_1.z.literal(2), kind: v3_1.z.enum(['rows', 'series', 'entity']),
40
+ columns: v3_1.z.array(result_schema_1.dataXpertResultColumnSchema),
41
+ rows: v3_1.z.array(v3_1.z.record(scalar)),
42
+ summary: v3_1.z.object({ rowCount: v3_1.z.number().int().nonnegative(), returnedRowCount: v3_1.z.number().int().nonnegative(), truncated: v3_1.z.boolean() }) }),
43
+ fetchedAt: v3_1.z.string().datetime(), executionId: v3_1.z.string(), auditRef: v3_1.z.string(), fromCache: v3_1.z.literal(false)
44
+ });
45
+ let DataXpertAnalyticsClient = class DataXpertAnalyticsClient {
46
+ constructor(runtime) {
47
+ this.runtime = runtime;
48
+ }
49
+ async discover(request, context) {
50
+ const importing = import_targets_1.importTargetRequest.safeParse(request);
51
+ if (importing.success)
52
+ return (0, import_targets_1.discoverImportTargets)(importing.data, browse => this.discover(browse, context));
53
+ if (request && typeof request === 'object' && !Array.isArray(request) && request.kind === 'query-schema') {
54
+ const parsed = querySchemaRequestSchema.parse(request);
55
+ const datasetId = (0, result_schema_1.createDataXpertDatasetId)(parsed.payload, parsed.datasetId);
56
+ const result = await this.query(datasetId, parsed.payload, parsed.parameters, context);
57
+ return plugin_sdk_1.analyticsDiscoveryResultSchema.parse({
58
+ items: [{ id: datasetId, name: datasetId, kind: 'dataset',
59
+ metadata: { output: (0, result_schema_1.createDataXpertOutput)(datasetId, result.effect.columns) } }],
60
+ queryContract: discovery_1.dataXpertQueryContract
61
+ });
62
+ }
63
+ const empty = request && typeof request === 'object' && !Array.isArray(request) && Object.keys(request).length === 0;
64
+ const parsed = discovery_1.dataXpertDiscoveryRequestSchema.parse(empty ? { kind: 'resources' } : request);
65
+ const response = await this.request('discover', context, { request: parsed });
66
+ const result = v3_1.z.object({ items: v3_1.z.array(v3_1.z.unknown()),
67
+ nextRequest: json.optional() }).strict().parse(await boundedJson(response));
68
+ return plugin_sdk_1.analyticsDiscoveryResultSchema.parse({ ...result, queryContract: discovery_1.dataXpertQueryContract });
69
+ }
70
+ async request(endpoint, context, body) {
71
+ context.signal.throwIfAborted();
72
+ const options = optionsSchema.parse(context.options);
73
+ const base = new URL(options.baseUrl);
74
+ if (!['http:', 'https:'].includes(base.protocol) || base.username || base.password || base.search || base.hash)
75
+ throw new Error('DATAX_URL_INVALID');
76
+ const token = await this.runtime.require(plugin_sdk_1.ActorTokenRuntimeFactoryCapability).createScopedApi(context.actor).getToken({ audience: options.audience });
77
+ context.signal.throwIfAborted();
78
+ const response = await fetch(`${base.href.replace(/\/$/, '')}/agent-workbench/analytics/${endpoint}`, {
79
+ method: 'POST', redirect: 'error', signal: context.signal,
80
+ headers: { Authorization: `Bearer ${token.token}`, 'Content-Type': 'application/json', 'organization-id': context.actor.organizationId },
81
+ body: JSON.stringify({ assistant: { code: options.assistantCode, xpertId: options.assistantXpertId, workspaceId: options.workspaceId }, ...body })
82
+ });
83
+ if (!response.ok) {
84
+ const error = await (0, diagnostic_1.readAnalyticsHttpError)(response, context.signal, context.deadlineAt);
85
+ context.signal.throwIfAborted();
86
+ throw error ?? new Error(`DATAX_QUERY_HTTP_${response.status}`);
87
+ }
88
+ return response;
89
+ }
90
+ async execute(binding, parameters, context) {
91
+ const payload = payload_1.dataXpertPayloadSchema.parse(binding.payload);
92
+ const result = await this.query(binding.id, payload, parameters, context);
93
+ return { columns: result.effect.columns, rows: result.effect.rows,
94
+ completeness: { returnedRows: result.effect.summary.returnedRowCount, totalRows: result.effect.summary.rowCount,
95
+ totalAccuracy: result.effect.summary.truncated ? 'unknown' : 'exact', scope: result.effect.summary.truncated ? 'page' : 'full_query_result',
96
+ truncated: result.effect.summary.truncated }, sourceFetchedAt: result.fetchedAt,
97
+ upstreamExecutionId: result.executionId, auditRef: result.auditRef };
98
+ }
99
+ async query(id, payload, parameters, context) {
100
+ const response = await this.request('query', context, {
101
+ binding: { id, resourceId: payload.resourceId, actionTypeCode: payload.actionTypeCode, target: payload.target,
102
+ params: bindParameters(payload.params, payload.parameterPaths, parameters) }, maxRows: context.maxRows
103
+ });
104
+ return responseSchema.parse(await boundedJson(response));
105
+ }
106
+ };
107
+ exports.DataXpertAnalyticsClient = DataXpertAnalyticsClient;
108
+ exports.DataXpertAnalyticsClient = DataXpertAnalyticsClient = __decorate([
109
+ (0, common_1.Injectable)(),
110
+ __param(0, (0, common_1.Inject)(plugin_sdk_1.XPERT_RUNTIME_CAPABILITIES_TOKEN)),
111
+ __metadata("design:paramtypes", [Object])
112
+ ], DataXpertAnalyticsClient);
113
+ /** Typed paths replace whole parameter values, never splice text into SQL/MDX/OData. */
114
+ function bindParameters(params, paths, parameters) {
115
+ const result = structuredClone(params);
116
+ if (Object.keys(parameters).some((name) => !Object.hasOwn(paths, name)) || Object.keys(paths).some((name) => !Object.hasOwn(parameters, name)))
117
+ throw (0, diagnostic_1.dataXpertParameterError)('DATAX_PARAMETER_MAPPING_INVALID');
118
+ if (new Set(Object.values(paths).map((path) => JSON.stringify(path))).size !== Object.keys(paths).length)
119
+ throw (0, diagnostic_1.dataXpertParameterError)('DATAX_PARAMETER_MAPPING_INVALID');
120
+ for (const [name, path] of Object.entries(paths)) {
121
+ let current = result;
122
+ for (const [index, key] of path.entries()) {
123
+ if (['__proto__', 'prototype', 'constructor'].includes(key) || !current || typeof current !== 'object' || !Object.hasOwn(current, key))
124
+ throw (0, diagnostic_1.dataXpertParameterError)('DATAX_PARAMETER_PATH_INVALID');
125
+ if (Array.isArray(current)) {
126
+ if (!/^(0|[1-9]\d*)$/.test(key) || Number(key) >= current.length)
127
+ throw (0, diagnostic_1.dataXpertParameterError)('DATAX_PARAMETER_PATH_INVALID');
128
+ const position = Number(key);
129
+ if (index === path.length - 1) {
130
+ if (current[position] !== null && typeof current[position] === 'object')
131
+ throw (0, diagnostic_1.dataXpertParameterError)('DATAX_PARAMETER_PATH_NOT_SCALAR');
132
+ current[position] = parameters[name];
133
+ }
134
+ else
135
+ current = current[position];
136
+ }
137
+ else if (index === path.length - 1) {
138
+ if (current[key] !== null && typeof current[key] === 'object')
139
+ throw (0, diagnostic_1.dataXpertParameterError)('DATAX_PARAMETER_PATH_NOT_SCALAR');
140
+ current[key] = parameters[name];
141
+ }
142
+ else
143
+ current = current[key];
144
+ }
145
+ }
146
+ return result;
147
+ }
148
+ async function boundedJson(response) {
149
+ if (!response.body)
150
+ throw new Error('DATAX_EMPTY_RESPONSE');
151
+ const reader = response.body.getReader();
152
+ const chunks = [];
153
+ let size = 0;
154
+ try {
155
+ for (;;) {
156
+ const chunk = await reader.read();
157
+ if (chunk.done)
158
+ break;
159
+ size += chunk.value.byteLength;
160
+ if (size > 2_500_000)
161
+ throw new Error('DATAX_RESPONSE_TOO_LARGE');
162
+ chunks.push(chunk.value);
163
+ }
164
+ return JSON.parse(Buffer.concat(chunks).toString('utf8'));
165
+ }
166
+ finally {
167
+ await reader.cancel();
168
+ }
169
+ }
@@ -0,0 +1,10 @@
1
+ import { AnalyticsProviderError } from '@xpert-ai/plugin-sdk';
2
+ declare const parameterMessages: {
3
+ DATAX_PARAMETER_MAPPING_INVALID: string;
4
+ DATAX_PARAMETER_PATH_INVALID: string;
5
+ DATAX_PARAMETER_PATH_NOT_SCALAR: string;
6
+ };
7
+ export declare function dataXpertParameterError(code: keyof typeof parameterMessages): AnalyticsProviderError;
8
+ /** Error responses get a smaller byte/time budget than results. Never retain or echo an unknown body. */
9
+ export declare function readAnalyticsHttpError(response: Response, signal: AbortSignal, deadlineAt?: number): Promise<AnalyticsProviderError | undefined>;
10
+ export {};
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dataXpertParameterError = dataXpertParameterError;
4
+ exports.readAnalyticsHttpError = readAnalyticsHttpError;
5
+ const contracts_1 = require("@xpert-ai/contracts");
6
+ const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
7
+ const parameterMessages = {
8
+ DATAX_PARAMETER_MAPPING_INVALID: 'Parameter names must match parameterPaths exactly, with a distinct target path for each parameter.',
9
+ DATAX_PARAMETER_PATH_INVALID: 'parameterPaths are relative to params. Omit the leading params segment and target an existing scalar, for example ["slicers","1","value"].',
10
+ DATAX_PARAMETER_PATH_NOT_SCALAR: 'A parameter path must target a scalar value, not an entire object or array. Select the existing filter value field.'
11
+ };
12
+ function dataXpertParameterError(code) {
13
+ return new plugin_sdk_1.AnalyticsProviderError({ schema: 'xpert.analytics.diagnostic', version: 1,
14
+ code, stage: 'validation', retryable: false, message: parameterMessages[code] });
15
+ }
16
+ /** Error responses get a smaller byte/time budget than results. Never retain or echo an unknown body. */
17
+ async function readAnalyticsHttpError(response, signal, deadlineAt) {
18
+ if (!/^application\/json(?:\s*;|$)/i.test(response.headers.get('content-type') ?? '') || !response.body) {
19
+ await response.body?.cancel().catch(() => undefined);
20
+ return undefined;
21
+ }
22
+ const reader = response.body.getReader();
23
+ const budget = Math.min(1000, Math.max(0, (deadlineAt ?? Infinity) - Date.now()));
24
+ let timer;
25
+ let abort = () => undefined;
26
+ const cancelled = new Promise(resolve => {
27
+ abort = () => resolve(undefined);
28
+ timer = setTimeout(abort, budget);
29
+ signal.addEventListener('abort', abort, { once: true });
30
+ });
31
+ const read = async () => {
32
+ const chunks = [];
33
+ let size = 0;
34
+ for (;;) {
35
+ const chunk = await reader.read();
36
+ if (chunk.done)
37
+ break;
38
+ size += chunk.value.byteLength;
39
+ if (size > 16_384)
40
+ return undefined;
41
+ chunks.push(chunk.value);
42
+ }
43
+ const diagnostic = (0, contracts_1.parseAnalyticsDiagnostic)(JSON.parse(Buffer.concat(chunks).toString('utf8')));
44
+ return diagnostic && new plugin_sdk_1.AnalyticsProviderError(diagnostic);
45
+ };
46
+ try {
47
+ signal.throwIfAborted();
48
+ return await Promise.race([read(), cancelled]);
49
+ }
50
+ catch {
51
+ return undefined;
52
+ }
53
+ finally {
54
+ clearTimeout(timer);
55
+ signal.removeEventListener('abort', abort);
56
+ await reader.cancel().catch(() => undefined);
57
+ }
58
+ }
@@ -22,6 +22,7 @@ exports.dataXpertDiscoveryRequestSchema = v3_1.z.discriminatedUnion('kind', [
22
22
  exports.dataXpertQueryContract = {
23
23
  operation: 'action', contractVersion: 1,
24
24
  payloadSchema: {
25
+ 'x-xpert-import': { version: 1, request: { kind: 'import-targets' } },
25
26
  type: 'object', additionalProperties: false,
26
27
  required: ['resourceId', 'actionTypeCode', 'target', 'params', 'parameterPaths'],
27
28
  properties: {
@@ -47,6 +48,7 @@ exports.dataXpertQueryContract = {
47
48
  'Entity metadata.definition contains adapterId, contractVersion, metadata and instructions. Follow that adapter-specific definition and the discovered Action inputSchema/inputHint. Preserve canonical targets, resource IDs and exact field names. Do not infer adapter types from names or apply one adapter\'s query, filter or time rules to another.',
48
49
  'Use only published metadata and authorized previews to establish business metrics, time axes, supported filters and complete aggregation. Missing metadata does not authorize guessing; obtain sufficient evidence before generating a time filter.',
49
50
  'Use parameterPaths relative to params to replace an existing scalar value, for example {region:["filters","0","value"]}. Paths must be unique. Never interpolate values into query text. Controls supply scalar values; arrays remain static unless the native Action explicitly supports another declared scalar mapping. Null handling and filter operators are defined by the Action schema and adapter instructions.',
50
- 'Only read-only Actions allowed by the current viewer policy are returned. Map output sourceField to returned column keys (or an explicit semanticRef). Numeric JSON cells require float64; decimal/int64 require exact strings. Preview to verify types, grain and completeness before binding charts. A partial page is not a complete business aggregate.'
51
+ 'Before authoring a binding, use dashboard_discover with the same source/providerId and requestJson encoding {kind:"query-schema",payload:<native Action envelope>,parameters:<mapped scalar values, or {}>}. This runs that bounded read-only Action under the current viewer policy; respect the native Action limits and filters. It returns items[0].metadata.output without business rows. Omit datasetId to let the provider generate a valid output key. Copy this output into the dashboard_preview binding, preserving datasetId, field IDs, sourceField, logicalType, role and nullable. Decimal/int64 remain exact strings; do not infer types from sample cells or names. If declarations are missing, obtain source metadata instead of guessing.',
52
+ 'The discovered output has empty grain because column roles alone do not prove uniqueness. Declare grain only when supported by query semantics. Always run dashboard_preview with the same payload and parameter values to validate the binding and obtain a preview receipt before saving; schema discovery is not a preview receipt. Re-discover after changing the query projection. A partial page is not a complete business aggregate.'
51
53
  ].join(' ')
52
54
  };
@@ -0,0 +1,164 @@
1
+ import { z } from 'zod/v3';
2
+ import type { AnalyticsDiscoveryResult, JSONValue } from '@xpert-ai/contracts';
3
+ /** Provider-owned reference migration. Native Action arguments and output contracts remain unchanged. */
4
+ export declare const importTargetRequest: z.ZodObject<{
5
+ kind: z.ZodLiteral<"import-targets">;
6
+ payload: z.ZodObject<{
7
+ resourceId: z.ZodString;
8
+ actionTypeCode: z.ZodString;
9
+ target: z.ZodEffects<z.ZodObject<{
10
+ entityId: z.ZodOptional<z.ZodString>;
11
+ entityTypeCode: z.ZodOptional<z.ZodString>;
12
+ entityRef: z.ZodOptional<z.ZodString>;
13
+ }, "strict", z.ZodTypeAny, {
14
+ entityTypeCode?: string | undefined;
15
+ entityRef?: string | undefined;
16
+ entityId?: string | undefined;
17
+ }, {
18
+ entityTypeCode?: string | undefined;
19
+ entityRef?: string | undefined;
20
+ entityId?: string | undefined;
21
+ }>, {
22
+ entityTypeCode?: string | undefined;
23
+ entityRef?: string | undefined;
24
+ entityId?: string | undefined;
25
+ }, {
26
+ entityTypeCode?: string | undefined;
27
+ entityRef?: string | undefined;
28
+ entityId?: string | undefined;
29
+ }>;
30
+ params: z.ZodRecord<z.ZodString, z.ZodType<JSONValue, z.ZodTypeDef, JSONValue>>;
31
+ parameterPaths: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>;
32
+ }, "strict", z.ZodTypeAny, {
33
+ params: Record<string, JSONValue>;
34
+ resourceId: string;
35
+ target: {
36
+ entityTypeCode?: string | undefined;
37
+ entityRef?: string | undefined;
38
+ entityId?: string | undefined;
39
+ };
40
+ actionTypeCode: string;
41
+ parameterPaths: Record<string, string[]>;
42
+ }, {
43
+ params: Record<string, JSONValue>;
44
+ resourceId: string;
45
+ target: {
46
+ entityTypeCode?: string | undefined;
47
+ entityRef?: string | undefined;
48
+ entityId?: string | undefined;
49
+ };
50
+ actionTypeCode: string;
51
+ parameterPaths?: Record<string, string[]> | undefined;
52
+ }>;
53
+ browse: z.ZodDefault<z.ZodDiscriminatedUnion<"kind", [z.ZodObject<{
54
+ kind: z.ZodLiteral<"resources">;
55
+ after: z.ZodOptional<z.ZodString>;
56
+ }, "strict", z.ZodTypeAny, {
57
+ kind: "resources";
58
+ after?: string | undefined;
59
+ }, {
60
+ kind: "resources";
61
+ after?: string | undefined;
62
+ }>, z.ZodObject<{
63
+ kind: z.ZodLiteral<"entities">;
64
+ resourceId: z.ZodString;
65
+ entityTypeCode: z.ZodOptional<z.ZodString>;
66
+ query: z.ZodOptional<z.ZodString>;
67
+ }, "strict", z.ZodTypeAny, {
68
+ kind: "entities";
69
+ resourceId: string;
70
+ entityTypeCode?: string | undefined;
71
+ query?: string | undefined;
72
+ }, {
73
+ kind: "entities";
74
+ resourceId: string;
75
+ entityTypeCode?: string | undefined;
76
+ query?: string | undefined;
77
+ }>, z.ZodObject<{
78
+ kind: z.ZodLiteral<"actions">;
79
+ resourceId: z.ZodString;
80
+ target: z.ZodObject<{
81
+ entityTypeCode: z.ZodString;
82
+ entityRef: z.ZodString;
83
+ }, "strict", z.ZodTypeAny, {
84
+ entityTypeCode: string;
85
+ entityRef: string;
86
+ }, {
87
+ entityTypeCode: string;
88
+ entityRef: string;
89
+ }>;
90
+ }, "strict", z.ZodTypeAny, {
91
+ kind: "actions";
92
+ resourceId: string;
93
+ target: {
94
+ entityTypeCode: string;
95
+ entityRef: string;
96
+ };
97
+ }, {
98
+ kind: "actions";
99
+ resourceId: string;
100
+ target: {
101
+ entityTypeCode: string;
102
+ entityRef: string;
103
+ };
104
+ }>]>>;
105
+ }, "strict", z.ZodTypeAny, {
106
+ kind: "import-targets";
107
+ payload: {
108
+ params: Record<string, JSONValue>;
109
+ resourceId: string;
110
+ target: {
111
+ entityTypeCode?: string | undefined;
112
+ entityRef?: string | undefined;
113
+ entityId?: string | undefined;
114
+ };
115
+ actionTypeCode: string;
116
+ parameterPaths: Record<string, string[]>;
117
+ };
118
+ browse: {
119
+ kind: "resources";
120
+ after?: string | undefined;
121
+ } | {
122
+ kind: "entities";
123
+ resourceId: string;
124
+ entityTypeCode?: string | undefined;
125
+ query?: string | undefined;
126
+ } | {
127
+ kind: "actions";
128
+ resourceId: string;
129
+ target: {
130
+ entityTypeCode: string;
131
+ entityRef: string;
132
+ };
133
+ };
134
+ }, {
135
+ kind: "import-targets";
136
+ payload: {
137
+ params: Record<string, JSONValue>;
138
+ resourceId: string;
139
+ target: {
140
+ entityTypeCode?: string | undefined;
141
+ entityRef?: string | undefined;
142
+ entityId?: string | undefined;
143
+ };
144
+ actionTypeCode: string;
145
+ parameterPaths?: Record<string, string[]> | undefined;
146
+ };
147
+ browse?: {
148
+ kind: "resources";
149
+ after?: string | undefined;
150
+ } | {
151
+ kind: "entities";
152
+ resourceId: string;
153
+ entityTypeCode?: string | undefined;
154
+ query?: string | undefined;
155
+ } | {
156
+ kind: "actions";
157
+ resourceId: string;
158
+ target: {
159
+ entityTypeCode: string;
160
+ entityRef: string;
161
+ };
162
+ } | undefined;
163
+ }>;
164
+ export declare function discoverImportTargets(input: z.infer<typeof importTargetRequest>, discover: (request: JSONValue) => Promise<AnalyticsDiscoveryResult>): Promise<AnalyticsDiscoveryResult>;
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.importTargetRequest = void 0;
4
+ exports.discoverImportTargets = discoverImportTargets;
5
+ const v3_1 = require("zod/v3");
6
+ const payload_1 = require("./payload");
7
+ const discovery_1 = require("./discovery");
8
+ /** Provider-owned reference migration. Native Action arguments and output contracts remain unchanged. */
9
+ exports.importTargetRequest = v3_1.z.object({
10
+ kind: v3_1.z.literal('import-targets'), payload: payload_1.dataXpertPayloadSchema,
11
+ browse: discovery_1.dataXpertDiscoveryRequestSchema.default({ kind: 'resources' })
12
+ }).strict();
13
+ async function discoverImportTargets(input, discover) {
14
+ const result = await discover(input.browse);
15
+ const wrap = (browse) => ({ kind: 'import-targets', payload: { ...input.payload, params: {}, parameterPaths: {} }, browse });
16
+ return { ...result,
17
+ items: result.items.flatMap(item => {
18
+ const next = discovery_1.dataXpertDiscoveryRequestSchema.safeParse(item.inspectRequest);
19
+ if (next.success && next.data.kind === 'actions') {
20
+ const target = next.data.target;
21
+ if (input.payload.target.entityTypeCode && target.entityTypeCode !== input.payload.target.entityTypeCode)
22
+ return [];
23
+ return [{ id: item.id, name: item.name, kind: 'dataset', description: item.description,
24
+ metadata: { schema: 'xpert.analytics.import-target', version: 1,
25
+ payloadPatch: { resourceId: next.data.resourceId, target } } }];
26
+ }
27
+ return item.inspectRequest ? [{ ...item, inspectRequest: wrap(item.inspectRequest) }] : [];
28
+ }),
29
+ ...(result.nextRequest ? { nextRequest: wrap(result.nextRequest) } : {}) };
30
+ }
@@ -0,0 +1,47 @@
1
+ import { z } from 'zod/v3';
2
+ import type { IIntegration } from '@xpert-ai/contracts';
3
+ import { type IntegrationStrategy, type TIntegrationStrategyParams } from '@xpert-ai/plugin-sdk';
4
+ export declare const DATA_XPERT_INTEGRATION = "analytics-data-xpert";
5
+ export declare const connectionSchema: z.ZodObject<{
6
+ baseUrl: z.ZodEffects<z.ZodString, string, string>;
7
+ audience: z.ZodString;
8
+ }, "strict", z.ZodTypeAny, {
9
+ baseUrl: string;
10
+ audience: string;
11
+ }, {
12
+ baseUrl: string;
13
+ audience: string;
14
+ }>;
15
+ export declare class DataXpertAnalyticsIntegration implements IntegrationStrategy<z.output<typeof connectionSchema>> {
16
+ readonly meta: {
17
+ name: string;
18
+ label: {
19
+ en_US: string;
20
+ zh_Hans: string;
21
+ };
22
+ icon: {
23
+ type: "font";
24
+ value: string;
25
+ };
26
+ schema: {
27
+ type: "object";
28
+ properties: {
29
+ baseUrl: {
30
+ type: "string";
31
+ title: string;
32
+ };
33
+ audience: {
34
+ type: "string";
35
+ title: string;
36
+ };
37
+ };
38
+ required: string[];
39
+ };
40
+ };
41
+ prepareConfig(config: z.output<typeof connectionSchema>): {
42
+ baseUrl: string;
43
+ audience: string;
44
+ };
45
+ validateConfig(config: z.output<typeof connectionSchema>): Promise<void>;
46
+ execute(_integration: IIntegration<z.output<typeof connectionSchema>>, _payload: TIntegrationStrategyParams): Promise<never>;
47
+ }
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.DataXpertAnalyticsIntegration = exports.connectionSchema = exports.DATA_XPERT_INTEGRATION = void 0;
10
+ const v3_1 = require("zod/v3");
11
+ const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
12
+ exports.DATA_XPERT_INTEGRATION = 'analytics-data-xpert';
13
+ exports.connectionSchema = v3_1.z.object({
14
+ baseUrl: v3_1.z.string().url().refine(value => { const url = new URL(value); return ['http:', 'https:'].includes(url.protocol) && !url.username && !url.password && !url.search && !url.hash; }),
15
+ audience: v3_1.z.string().min(1)
16
+ }).strict();
17
+ let DataXpertAnalyticsIntegration = class DataXpertAnalyticsIntegration {
18
+ constructor() {
19
+ this.meta = { name: exports.DATA_XPERT_INTEGRATION, label: { en_US: 'data-xpert Analytics', zh_Hans: 'data-xpert 分析服务' }, icon: { type: 'font', value: 'ri-bar-chart-box-line' },
20
+ schema: { type: 'object', properties: { baseUrl: { type: 'string', title: 'API URL' }, audience: { type: 'string', title: 'OIDC audience' } }, required: ['baseUrl', 'audience'] } };
21
+ }
22
+ prepareConfig(config) { return exports.connectionSchema.parse(config); }
23
+ async validateConfig(config) { exports.connectionSchema.parse(config); }
24
+ async execute(_integration, _payload) { throw new Error('DATAX_USE_ANALYTICS_RUNTIME'); }
25
+ };
26
+ exports.DataXpertAnalyticsIntegration = DataXpertAnalyticsIntegration;
27
+ exports.DataXpertAnalyticsIntegration = DataXpertAnalyticsIntegration = __decorate([
28
+ (0, plugin_sdk_1.IntegrationStrategyKey)(exports.DATA_XPERT_INTEGRATION)
29
+ ], DataXpertAnalyticsIntegration);
@@ -0,0 +1,50 @@
1
+ import { z } from 'zod/v3';
2
+ import type { JSONValue } from '@xpert-ai/contracts';
3
+ /** Native Action arguments stay plugin-owned; the host only interprets Dataset/Binding. */
4
+ export declare const dataXpertPayloadSchema: z.ZodObject<{
5
+ resourceId: z.ZodString;
6
+ actionTypeCode: z.ZodString;
7
+ target: z.ZodEffects<z.ZodObject<{
8
+ entityId: z.ZodOptional<z.ZodString>;
9
+ entityTypeCode: z.ZodOptional<z.ZodString>;
10
+ entityRef: z.ZodOptional<z.ZodString>;
11
+ }, "strict", z.ZodTypeAny, {
12
+ entityTypeCode?: string | undefined;
13
+ entityRef?: string | undefined;
14
+ entityId?: string | undefined;
15
+ }, {
16
+ entityTypeCode?: string | undefined;
17
+ entityRef?: string | undefined;
18
+ entityId?: string | undefined;
19
+ }>, {
20
+ entityTypeCode?: string | undefined;
21
+ entityRef?: string | undefined;
22
+ entityId?: string | undefined;
23
+ }, {
24
+ entityTypeCode?: string | undefined;
25
+ entityRef?: string | undefined;
26
+ entityId?: string | undefined;
27
+ }>;
28
+ params: z.ZodRecord<z.ZodString, z.ZodType<JSONValue, z.ZodTypeDef, JSONValue>>;
29
+ parameterPaths: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>;
30
+ }, "strict", z.ZodTypeAny, {
31
+ params: Record<string, JSONValue>;
32
+ resourceId: string;
33
+ target: {
34
+ entityTypeCode?: string | undefined;
35
+ entityRef?: string | undefined;
36
+ entityId?: string | undefined;
37
+ };
38
+ actionTypeCode: string;
39
+ parameterPaths: Record<string, string[]>;
40
+ }, {
41
+ params: Record<string, JSONValue>;
42
+ resourceId: string;
43
+ target: {
44
+ entityTypeCode?: string | undefined;
45
+ entityRef?: string | undefined;
46
+ entityId?: string | undefined;
47
+ };
48
+ actionTypeCode: string;
49
+ parameterPaths?: Record<string, string[]> | undefined;
50
+ }>;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dataXpertPayloadSchema = void 0;
4
+ const v3_1 = require("zod/v3");
5
+ const scalar = v3_1.z.union([v3_1.z.string(), v3_1.z.number().finite(), v3_1.z.boolean(), v3_1.z.null()]);
6
+ const json = v3_1.z.lazy(() => v3_1.z.union([scalar, v3_1.z.array(json), v3_1.z.record(json)]));
7
+ /** Native Action arguments stay plugin-owned; the host only interprets Dataset/Binding. */
8
+ exports.dataXpertPayloadSchema = v3_1.z.object({
9
+ resourceId: v3_1.z.string().min(1).max(160), actionTypeCode: v3_1.z.string().min(1).max(160),
10
+ target: v3_1.z.object({
11
+ entityId: v3_1.z.string().trim().min(1).max(256).optional(),
12
+ entityTypeCode: v3_1.z.string().trim().min(1).max(256).optional(),
13
+ entityRef: v3_1.z.string().trim().min(1).max(512).optional()
14
+ }).strict().refine(target => Boolean(target.entityId || target.entityRef), 'Target reference is required'),
15
+ params: v3_1.z.record(json),
16
+ parameterPaths: v3_1.z.record(v3_1.z.array(v3_1.z.string().min(1)).min(1).max(8)).default({})
17
+ }).strict();
@@ -7,11 +7,14 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  };
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.AnalyticsDataXpertPlugin = void 0;
10
+ const client_1 = require("./client");
10
11
  const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
11
12
  const provider_1 = require("./provider");
13
+ const cqrs_1 = require("@nestjs/cqrs");
14
+ const integration_1 = require("./integration");
12
15
  let AnalyticsDataXpertPlugin = class AnalyticsDataXpertPlugin {
13
16
  };
14
17
  exports.AnalyticsDataXpertPlugin = AnalyticsDataXpertPlugin;
15
18
  exports.AnalyticsDataXpertPlugin = AnalyticsDataXpertPlugin = __decorate([
16
- (0, plugin_sdk_1.XpertServerPlugin)({ providers: [provider_1.DataXpertAnalyticsProvider] })
19
+ (0, plugin_sdk_1.XpertServerPlugin)({ imports: [cqrs_1.CqrsModule], providers: [provider_1.DataXpertAnalyticsProvider, client_1.DataXpertAnalyticsClient, integration_1.DataXpertAnalyticsIntegration] })
17
20
  ], AnalyticsDataXpertPlugin);
@@ -1,61 +1,61 @@
1
- import { z } from 'zod/v3';
2
- import type { AnalyticsBinding, AnalyticsProviderDescriptor, AnalyticsValue, JSONValue, AnalyticsDiscoveryResult } from '@xpert-ai/contracts';
3
- import { type AnalyticsQueryProvider, type AnalyticsProviderContext, type AnalyticsProviderResult, type RuntimeCapabilityRegistry } from '@xpert-ai/plugin-sdk';
4
- /** Native Action arguments stay plugin-owned; the host only interprets Dataset/Binding. */
5
- export declare const dataXpertPayloadSchema: z.ZodObject<{
6
- resourceId: z.ZodString;
7
- actionTypeCode: z.ZodString;
8
- target: z.ZodEffects<z.ZodObject<{
9
- entityId: z.ZodOptional<z.ZodString>;
10
- entityTypeCode: z.ZodOptional<z.ZodString>;
11
- entityRef: z.ZodOptional<z.ZodString>;
12
- }, "strict", z.ZodTypeAny, {
13
- entityTypeCode?: string | undefined;
14
- entityRef?: string | undefined;
15
- entityId?: string | undefined;
16
- }, {
17
- entityTypeCode?: string | undefined;
18
- entityRef?: string | undefined;
19
- entityId?: string | undefined;
20
- }>, {
21
- entityTypeCode?: string | undefined;
22
- entityRef?: string | undefined;
23
- entityId?: string | undefined;
24
- }, {
25
- entityTypeCode?: string | undefined;
26
- entityRef?: string | undefined;
27
- entityId?: string | undefined;
28
- }>;
29
- params: z.ZodRecord<z.ZodString, z.ZodType<JSONValue, z.ZodTypeDef, JSONValue>>;
30
- parameterPaths: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString, "many">>>;
31
- }, "strict", z.ZodTypeAny, {
32
- params: Record<string, JSONValue>;
33
- resourceId: string;
34
- target: {
35
- entityTypeCode?: string | undefined;
36
- entityRef?: string | undefined;
37
- entityId?: string | undefined;
1
+ import { QueryBus } from '@nestjs/cqrs';
2
+ import type { AnalyticsBinding, AnalyticsValue, JSONValue } from '@xpert-ai/contracts';
3
+ import { type AnalyticsQueryProvider, type AnalyticsProviderContext, type AnalyticsDefinitionAuthoring, type DataSourceActor } from '@xpert-ai/plugin-sdk';
4
+ import { DataXpertAnalyticsClient } from './client';
5
+ export declare class DataXpertAnalyticsProvider implements AnalyticsQueryProvider {
6
+ private readonly engine;
7
+ private readonly queries;
8
+ constructor(engine: DataXpertAnalyticsClient, queries: QueryBus);
9
+ readonly descriptor: {
10
+ id: string;
11
+ name: string;
12
+ sourceKinds: ["definition"];
13
+ operations: {
14
+ name: string;
15
+ contractVersion: number;
16
+ readOnly: true;
17
+ }[];
38
18
  };
39
- actionTypeCode: string;
40
- parameterPaths: Record<string, string[]>;
41
- }, {
42
- params: Record<string, JSONValue>;
43
- resourceId: string;
44
- target: {
45
- entityTypeCode?: string | undefined;
46
- entityRef?: string | undefined;
47
- entityId?: string | undefined;
19
+ readonly authoring: AnalyticsDefinitionAuthoring;
20
+ private authoringContext;
21
+ readonly configuration: {
22
+ kind: "definition";
23
+ connection: "integration";
24
+ schema: {
25
+ type: string;
26
+ properties: {
27
+ integrationId: {
28
+ type: string;
29
+ format: string;
30
+ title: string;
31
+ };
32
+ assistantCode: {
33
+ type: string;
34
+ title: string;
35
+ };
36
+ assistantXpertId: {
37
+ type: string;
38
+ format: string;
39
+ title: string;
40
+ };
41
+ workspaceId: {
42
+ type: string;
43
+ format: string;
44
+ title: string;
45
+ };
46
+ };
47
+ required: string[];
48
+ additionalProperties: boolean;
49
+ };
48
50
  };
49
- actionTypeCode: string;
50
- parameterPaths?: Record<string, string[]> | undefined;
51
- }>;
52
- export declare class DataXpertAnalyticsProvider implements AnalyticsQueryProvider {
53
- private readonly runtime;
54
- readonly descriptor: AnalyticsProviderDescriptor;
55
- constructor(runtime: RuntimeCapabilityRegistry);
56
- discover(request: JSONValue, context: AnalyticsProviderContext): Promise<AnalyticsDiscoveryResult>;
57
- private request;
58
- execute(binding: AnalyticsBinding, parameters: Record<string, AnalyticsValue>, context: AnalyticsProviderContext): Promise<AnalyticsProviderResult>;
51
+ validateConfiguration(options: Record<string, JSONValue>, actor: DataSourceActor): Promise<{
52
+ assistantCode: string;
53
+ assistantXpertId: string;
54
+ workspaceId: string;
55
+ integrationId: string;
56
+ }>;
57
+ private connection;
58
+ private run;
59
+ discover(request: JSONValue, context: AnalyticsProviderContext): Promise<import("@xpert-ai/contracts").AnalyticsDiscoveryResult>;
60
+ execute(binding: AnalyticsBinding, parameters: Record<string, AnalyticsValue>, context: AnalyticsProviderContext): Promise<import("@xpert-ai/plugin-sdk").AnalyticsProviderResult>;
59
61
  }
60
- /** Typed paths replace whole parameter values, never splice text into SQL/MDX/OData. */
61
- export declare function bindParameters(params: Record<string, JSONValue>, paths: Record<string, string[]>, parameters: Record<string, AnalyticsValue>): Record<string, JSONValue>;
@@ -8,157 +8,70 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
8
8
  var __metadata = (this && this.__metadata) || function (k, v) {
9
9
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
10
  };
11
- var __param = (this && this.__param) || function (paramIndex, decorator) {
12
- return function (target, key) { decorator(target, key, paramIndex); }
13
- };
14
11
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.DataXpertAnalyticsProvider = exports.dataXpertPayloadSchema = void 0;
16
- exports.bindParameters = bindParameters;
17
- const common_1 = require("@nestjs/common");
12
+ exports.DataXpertAnalyticsProvider = void 0;
13
+ const cqrs_1 = require("@nestjs/cqrs");
18
14
  const v3_1 = require("zod/v3");
19
- const discovery_1 = require("./discovery");
20
15
  const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
21
- const scalar = v3_1.z.union([v3_1.z.string(), v3_1.z.number().finite(), v3_1.z.boolean(), v3_1.z.null()]);
22
- const json = v3_1.z.lazy(() => v3_1.z.union([scalar, v3_1.z.array(json), v3_1.z.record(json)]));
23
- /** Native Action arguments stay plugin-owned; the host only interprets Dataset/Binding. */
24
- exports.dataXpertPayloadSchema = v3_1.z.object({
25
- resourceId: v3_1.z.string().min(1).max(160), actionTypeCode: v3_1.z.string().min(1).max(160),
26
- target: v3_1.z.object({
27
- entityId: v3_1.z.string().trim().min(1).max(256).optional(),
28
- entityTypeCode: v3_1.z.string().trim().min(1).max(256).optional(),
29
- entityRef: v3_1.z.string().trim().min(1).max(512).optional()
30
- }).strict().refine(target => Boolean(target.entityId || target.entityRef), 'Target reference is required'),
31
- params: v3_1.z.record(json),
32
- parameterPaths: v3_1.z.record(v3_1.z.array(v3_1.z.string().min(1)).min(1).max(8)).default({})
33
- }).strict();
34
- /** Trusted DataSource configuration; never merge it with payloads, controls or discovery requests. */
35
- const optionsSchema = v3_1.z.object({
36
- baseUrl: v3_1.z.string().url(), audience: v3_1.z.string().min(1),
37
- assistantCode: v3_1.z.string().min(1), assistantXpertId: v3_1.z.string().uuid(), workspaceId: v3_1.z.string().uuid()
38
- }).strict();
39
- const responseSchema = v3_1.z.object({
40
- effect: v3_1.z.object({ version: v3_1.z.literal(2), kind: v3_1.z.enum(['rows', 'series', 'entity']),
41
- columns: v3_1.z.array(v3_1.z.object({ key: v3_1.z.string(), semanticRef: v3_1.z.string().optional() })),
42
- rows: v3_1.z.array(v3_1.z.record(scalar)),
43
- summary: v3_1.z.object({ rowCount: v3_1.z.number().int().nonnegative(), returnedRowCount: v3_1.z.number().int().nonnegative(), truncated: v3_1.z.boolean() }) }),
44
- fetchedAt: v3_1.z.string().datetime(), executionId: v3_1.z.string(), auditRef: v3_1.z.string(), fromCache: v3_1.z.literal(false)
45
- });
16
+ const client_1 = require("./client");
17
+ const integration_1 = require("./integration");
18
+ const optionsSchema = v3_1.z.object({ integrationId: v3_1.z.string().uuid(), assistantCode: v3_1.z.string().min(1), assistantXpertId: v3_1.z.string().uuid(), workspaceId: v3_1.z.string().uuid() }).strict();
46
19
  let DataXpertAnalyticsProvider = class DataXpertAnalyticsProvider {
47
- constructor(runtime) {
48
- this.runtime = runtime;
49
- this.descriptor = {
50
- id: 'data-xpert.ontology', name: 'data-xpert Ontology', dataSourceTypes: ['data-xpert'],
51
- operations: [{ name: 'action', contractVersion: 1, readOnly: true }],
52
- connectionType: { type: 'data-xpert', name: 'data-xpert', configuration: {
53
- type: 'object', properties: { baseUrl: { type: 'string', title: 'API URL' }, audience: { type: 'string', title: 'OIDC audience' },
54
- assistantCode: { type: 'string', title: 'Assistant code' }, assistantXpertId: { type: 'string', title: 'Assistant Xpert ID' },
55
- workspaceId: { type: 'string', title: 'Workspace ID' } },
56
- required: ['baseUrl', 'audience', 'assistantCode', 'assistantXpertId', 'workspaceId'], additionalProperties: false
57
- } }
20
+ constructor(engine, queries) {
21
+ this.engine = engine;
22
+ this.queries = queries;
23
+ this.descriptor = { id: 'data-xpert.ontology', name: 'data-xpert Ontology', sourceKinds: ['definition'], operations: [{ name: 'action', contractVersion: 1, readOnly: true }] };
24
+ this.authoring = {
25
+ integrationProvider: integration_1.DATA_XPERT_INTEGRATION,
26
+ inspectSchema: { type: 'object', additionalProperties: true },
27
+ definitionSchema: { type: 'object', properties: {}, additionalProperties: false },
28
+ instructions: 'The system supplies integration and current Assistant identity. Inspect the authorized ontology, following nextRequest/inspectRequest. Register with definition {} then discover and preview the returned source. Do not ask the user for integration, workspace or Assistant IDs.',
29
+ inspect: (input, context) => this.run(this.authoringContext(context), resolved => this.engine.discover(input, resolved)).then(result => JSON.parse(JSON.stringify(result))),
30
+ prepare: async (input, context) => {
31
+ v3_1.z.object({}).strict().parse(input);
32
+ const resolved = this.authoringContext(context);
33
+ await this.run(resolved, current => this.engine.discover({}, current));
34
+ return resolved.options;
35
+ }
58
36
  };
37
+ this.configuration = { kind: 'definition', connection: 'integration', schema: {
38
+ type: 'object', properties: { integrationId: { type: 'string', format: 'uuid', title: 'Integration ID' }, assistantCode: { type: 'string', title: 'Assistant code' }, assistantXpertId: { type: 'string', format: 'uuid', title: 'Assistant ID' }, workspaceId: { type: 'string', format: 'uuid', title: 'Workspace ID' } },
39
+ required: ['integrationId', 'assistantCode', 'assistantXpertId', 'workspaceId'], additionalProperties: false
40
+ } };
59
41
  }
60
- async discover(request, context) {
61
- const empty = request && typeof request === 'object' && !Array.isArray(request) && Object.keys(request).length === 0;
62
- const parsed = discovery_1.dataXpertDiscoveryRequestSchema.parse(empty ? { kind: 'resources' } : request);
63
- const response = await this.request('discover', context, { request: parsed });
64
- const result = v3_1.z.object({ items: v3_1.z.array(v3_1.z.unknown()),
65
- nextRequest: json.optional() }).strict().parse(await boundedJson(response));
66
- return plugin_sdk_1.analyticsDiscoveryResultSchema.parse({ ...result, schemaVersion: 1, queryContract: discovery_1.dataXpertQueryContract });
42
+ authoringContext(context) {
43
+ if (context.connection.kind !== 'integration')
44
+ throw new Error('ANALYTICS_CONNECTION_INVALID');
45
+ // The host uses the Assistant UUID as x-assistant-code for data-xpert calls.
46
+ return { actor: context.actor, signal: context.signal, deadlineAt: context.deadlineAt, maxRows: 5,
47
+ options: { integrationId: context.connection.integrationId, assistantCode: context.xpertId,
48
+ assistantXpertId: context.xpertId, workspaceId: context.workspaceId } };
67
49
  }
68
- async request(endpoint, context, body) {
69
- context.signal.throwIfAborted();
70
- const options = optionsSchema.parse(context.options);
71
- const base = new URL(options.baseUrl);
72
- if (!['http:', 'https:'].includes(base.protocol) || base.username || base.password || base.search || base.hash)
73
- throw new Error('DATAX_URL_INVALID');
74
- const token = await this.runtime.require(plugin_sdk_1.ActorTokenRuntimeFactoryCapability).createScopedApi(context.actor).getToken({ audience: options.audience });
75
- context.signal.throwIfAborted();
76
- const response = await fetch(`${base.href.replace(/\/$/, '')}/agent-workbench/analytics/${endpoint}`, {
77
- method: 'POST', redirect: 'error', signal: context.signal,
78
- headers: { Authorization: `Bearer ${token.token}`, 'Content-Type': 'application/json', 'organization-id': context.actor.organizationId },
79
- body: JSON.stringify({ assistant: { code: options.assistantCode, xpertId: options.assistantXpertId, workspaceId: options.workspaceId }, ...body })
80
- });
81
- if (!response.ok) {
82
- // Do not retain an unconsumed upstream error stream or expose its potentially sensitive body.
83
- await response.body?.cancel().catch(() => undefined);
84
- throw new Error(`DATAX_QUERY_HTTP_${response.status}`);
85
- }
86
- return response;
50
+ async validateConfiguration(options, actor) {
51
+ const parsed = optionsSchema.parse(options);
52
+ await this.connection(parsed.integrationId, actor);
53
+ return parsed;
87
54
  }
88
- async execute(binding, parameters, context) {
89
- const payload = exports.dataXpertPayloadSchema.parse(binding.payload);
90
- const response = await this.request('query', context, {
91
- binding: { id: binding.id, resourceId: payload.resourceId, actionTypeCode: payload.actionTypeCode, target: payload.target,
92
- params: bindParameters(payload.params, payload.parameterPaths, parameters) }, maxRows: context.maxRows
93
- });
94
- const result = responseSchema.parse(await boundedJson(response));
95
- return { columns: result.effect.columns, rows: result.effect.rows,
96
- completeness: { returnedRows: result.effect.summary.returnedRowCount, totalRows: result.effect.summary.rowCount,
97
- totalAccuracy: result.effect.summary.truncated ? 'unknown' : 'exact', scope: result.effect.summary.truncated ? 'page' : 'full_query_result',
98
- truncated: result.effect.summary.truncated }, sourceFetchedAt: result.fetchedAt,
99
- upstreamExecutionId: result.executionId, auditRef: result.auditRef };
55
+ async connection(id, actor) {
56
+ const integration = await this.queries.execute(new plugin_sdk_1.ResolveConnectorIntegrationQuery(id, integration_1.DATA_XPERT_INTEGRATION, { tenantId: actor.tenantId, organizationId: actor.organizationId }));
57
+ if (!integration || integration.tenantId !== actor.tenantId || (integration.organizationId && integration.organizationId !== actor.organizationId))
58
+ throw new Error('DATAX_INTEGRATION_UNAVAILABLE');
59
+ return integration_1.connectionSchema.parse(integration.options);
100
60
  }
61
+ async run(context, execute) {
62
+ const { integrationId, ...target } = optionsSchema.parse(context.options);
63
+ const connection = await this.connection(integrationId, context.actor);
64
+ const result = await execute({ ...context, options: { ...target, ...connection } });
65
+ const current = await this.connection(integrationId, context.actor);
66
+ if (JSON.stringify(current) !== JSON.stringify(connection))
67
+ throw new Error('DATAX_INTEGRATION_CHANGED');
68
+ return result;
69
+ }
70
+ discover(request, context) { return this.run(context, resolved => this.engine.discover(request, resolved)); }
71
+ execute(binding, parameters, context) { return this.run(context, resolved => this.engine.execute(binding, parameters, resolved)); }
101
72
  };
102
73
  exports.DataXpertAnalyticsProvider = DataXpertAnalyticsProvider;
103
74
  exports.DataXpertAnalyticsProvider = DataXpertAnalyticsProvider = __decorate([
104
75
  (0, plugin_sdk_1.AnalyticsProvider)('data-xpert.ontology'),
105
- __param(0, (0, common_1.Inject)(plugin_sdk_1.XPERT_RUNTIME_CAPABILITIES_TOKEN)),
106
- __metadata("design:paramtypes", [Object])
76
+ __metadata("design:paramtypes", [client_1.DataXpertAnalyticsClient, cqrs_1.QueryBus])
107
77
  ], DataXpertAnalyticsProvider);
108
- /** Typed paths replace whole parameter values, never splice text into SQL/MDX/OData. */
109
- function bindParameters(params, paths, parameters) {
110
- const result = structuredClone(params);
111
- if (Object.keys(parameters).some((name) => !Object.hasOwn(paths, name)) || Object.keys(paths).some((name) => !Object.hasOwn(parameters, name)))
112
- throw new Error('DATAX_PARAMETER_MAPPING_INVALID');
113
- if (new Set(Object.values(paths).map((path) => JSON.stringify(path))).size !== Object.keys(paths).length)
114
- throw new Error('DATAX_PARAMETER_MAPPING_INVALID');
115
- for (const [name, path] of Object.entries(paths)) {
116
- let current = result;
117
- for (const [index, key] of path.entries()) {
118
- if (['__proto__', 'prototype', 'constructor'].includes(key) || !current || typeof current !== 'object' || !Object.hasOwn(current, key))
119
- throw new common_1.BadRequestException('DATAX_PARAMETER_PATH_INVALID: parameterPaths are relative to params. Omit the leading params segment and target an existing scalar, for example ["slicers","1","value"].');
120
- if (Array.isArray(current)) {
121
- if (!/^(0|[1-9]\d*)$/.test(key) || Number(key) >= current.length)
122
- throw new Error('DATAX_PARAMETER_PATH_INVALID');
123
- const position = Number(key);
124
- if (index === path.length - 1) {
125
- if (current[position] !== null && typeof current[position] === 'object')
126
- throw new Error('DATAX_PARAMETER_PATH_NOT_SCALAR');
127
- current[position] = parameters[name];
128
- }
129
- else
130
- current = current[position];
131
- }
132
- else if (index === path.length - 1) {
133
- if (current[key] !== null && typeof current[key] === 'object')
134
- throw new Error('DATAX_PARAMETER_PATH_NOT_SCALAR');
135
- current[key] = parameters[name];
136
- }
137
- else
138
- current = current[key];
139
- }
140
- }
141
- return result;
142
- }
143
- async function boundedJson(response) {
144
- if (!response.body)
145
- throw new Error('DATAX_EMPTY_RESPONSE');
146
- const reader = response.body.getReader();
147
- const chunks = [];
148
- let size = 0;
149
- try {
150
- for (;;) {
151
- const chunk = await reader.read();
152
- if (chunk.done)
153
- break;
154
- size += chunk.value.byteLength;
155
- if (size > 2_500_000)
156
- throw new Error('DATAX_RESPONSE_TOO_LARGE');
157
- chunks.push(chunk.value);
158
- }
159
- return JSON.parse(Buffer.concat(chunks).toString('utf8'));
160
- }
161
- finally {
162
- await reader.cancel();
163
- }
164
- }
@@ -0,0 +1,101 @@
1
+ import { z } from 'zod/v3';
2
+ import type { JSONValue } from '@xpert-ai/contracts';
3
+ export declare const dataXpertResultColumnSchema: z.ZodObject<{
4
+ label: z.ZodOptional<z.ZodString>;
5
+ logicalType: z.ZodOptional<z.ZodEnum<["string", "boolean", "int64", "float64", "decimal", "date", "timestamp"]>>;
6
+ nullable: z.ZodOptional<z.ZodBoolean>;
7
+ role: z.ZodOptional<z.ZodEnum<["dimension", "measure", "time", "identifier"]>>;
8
+ semanticRef: z.ZodOptional<z.ZodOptional<z.ZodString>>;
9
+ unit: z.ZodOptional<z.ZodOptional<z.ZodString>>;
10
+ } & {
11
+ key: z.ZodString;
12
+ }, "strip", z.ZodTypeAny, {
13
+ key: string;
14
+ label?: string | undefined;
15
+ logicalType?: "string" | "boolean" | "date" | "int64" | "float64" | "decimal" | "timestamp" | undefined;
16
+ nullable?: boolean | undefined;
17
+ role?: "dimension" | "measure" | "time" | "identifier" | undefined;
18
+ semanticRef?: string | undefined;
19
+ unit?: string | undefined;
20
+ }, {
21
+ key: string;
22
+ label?: string | undefined;
23
+ logicalType?: "string" | "boolean" | "date" | "int64" | "float64" | "decimal" | "timestamp" | undefined;
24
+ nullable?: boolean | undefined;
25
+ role?: "dimension" | "measure" | "time" | "identifier" | undefined;
26
+ semanticRef?: string | undefined;
27
+ unit?: string | undefined;
28
+ }>;
29
+ export declare const dataXpertOutputSchema: z.ZodObject<{
30
+ datasetId: z.ZodString;
31
+ fields: z.ZodArray<z.ZodObject<{
32
+ id: z.ZodString;
33
+ sourceField: z.ZodString;
34
+ label: z.ZodString;
35
+ logicalType: z.ZodEnum<["string", "boolean", "int64", "float64", "decimal", "date", "timestamp"]>;
36
+ nullable: z.ZodBoolean;
37
+ role: z.ZodEnum<["dimension", "measure", "time", "identifier"]>;
38
+ semanticRef: z.ZodOptional<z.ZodString>;
39
+ unit: z.ZodOptional<z.ZodString>;
40
+ }, "strict", z.ZodTypeAny, {
41
+ id?: string;
42
+ sourceField?: string;
43
+ label?: string;
44
+ logicalType?: "string" | "boolean" | "date" | "int64" | "float64" | "decimal" | "timestamp";
45
+ nullable?: boolean;
46
+ role?: "dimension" | "measure" | "time" | "identifier";
47
+ semanticRef?: string;
48
+ unit?: string;
49
+ }, {
50
+ id?: string;
51
+ sourceField?: string;
52
+ label?: string;
53
+ logicalType?: "string" | "boolean" | "date" | "int64" | "float64" | "decimal" | "timestamp";
54
+ nullable?: boolean;
55
+ role?: "dimension" | "measure" | "time" | "identifier";
56
+ semanticRef?: string;
57
+ unit?: string;
58
+ }>, "many">;
59
+ grain: z.ZodArray<z.ZodString, "many">;
60
+ }, "strict", z.ZodTypeAny, {
61
+ datasetId?: string;
62
+ fields?: {
63
+ id?: string;
64
+ sourceField?: string;
65
+ label?: string;
66
+ logicalType?: "string" | "boolean" | "date" | "int64" | "float64" | "decimal" | "timestamp";
67
+ nullable?: boolean;
68
+ role?: "dimension" | "measure" | "time" | "identifier";
69
+ semanticRef?: string;
70
+ unit?: string;
71
+ }[];
72
+ grain?: string[];
73
+ }, {
74
+ datasetId?: string;
75
+ fields?: {
76
+ id?: string;
77
+ sourceField?: string;
78
+ label?: string;
79
+ logicalType?: "string" | "boolean" | "date" | "int64" | "float64" | "decimal" | "timestamp";
80
+ nullable?: boolean;
81
+ role?: "dimension" | "measure" | "time" | "identifier";
82
+ semanticRef?: string;
83
+ unit?: string;
84
+ }[];
85
+ grain?: string[];
86
+ }>;
87
+ export declare function createDataXpertDatasetId(payload: JSONValue, requestedId?: string): string;
88
+ export declare function createDataXpertOutput(datasetId: string, columns: z.output<typeof dataXpertResultColumnSchema>[]): {
89
+ datasetId?: string;
90
+ fields?: {
91
+ id?: string;
92
+ sourceField?: string;
93
+ label?: string;
94
+ logicalType?: "string" | "boolean" | "date" | "int64" | "float64" | "decimal" | "timestamp";
95
+ nullable?: boolean;
96
+ role?: "dimension" | "measure" | "time" | "identifier";
97
+ semanticRef?: string;
98
+ unit?: string;
99
+ }[];
100
+ grain?: string[];
101
+ };
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dataXpertOutputSchema = exports.dataXpertResultColumnSchema = void 0;
4
+ exports.createDataXpertDatasetId = createDataXpertDatasetId;
5
+ exports.createDataXpertOutput = createDataXpertOutput;
6
+ const node_crypto_1 = require("node:crypto");
7
+ const v3_1 = require("zod/v3");
8
+ const analytics_1 = require("@xpert-ai/plugin-sdk/analytics");
9
+ // Optional on legacy responses; schema authoring requires explicit declarations.
10
+ exports.dataXpertResultColumnSchema = analytics_1.analyticsFieldSchema
11
+ .omit({ id: true, sourceField: true })
12
+ .partial()
13
+ .extend({ key: v3_1.z.string() })
14
+ .strip();
15
+ exports.dataXpertOutputSchema = analytics_1.analyticsBindingObjectSchema.shape.output;
16
+ function createDataXpertDatasetId(payload, requestedId) {
17
+ const parsed = exports.dataXpertOutputSchema.shape.datasetId.safeParse(requestedId);
18
+ if (parsed.success)
19
+ return parsed.data;
20
+ // Native references are not output keys. Include the query projection to separate bindings.
21
+ return `dataset_${(0, node_crypto_1.createHash)('sha256').update(JSON.stringify(payload)).digest('hex')}`;
22
+ }
23
+ function createDataXpertOutput(datasetId, columns) {
24
+ if (columns.length === 0 || columns.length > 80)
25
+ throw new Error('DATAX_RESULT_SCHEMA_UNAVAILABLE: expected 1 to 80 declared columns');
26
+ if (new Set(columns.map(column => column.key)).size !== columns.length)
27
+ throw new Error('DATAX_RESULT_SCHEMA_AMBIGUOUS');
28
+ const fields = columns.map(column => {
29
+ if (column.logicalType === undefined || column.role === undefined || column.nullable === undefined)
30
+ throw new Error('DATAX_RESULT_SCHEMA_UNAVAILABLE');
31
+ // Repeated semantic selectors cannot identify a column; its unique native key can.
32
+ const semanticRef = columns.filter(item => item.semanticRef === column.semanticRef).length === 1
33
+ ? column.semanticRef : undefined;
34
+ return {
35
+ id: `field_${(0, node_crypto_1.createHash)('sha256').update(column.key).digest('hex')}`,
36
+ sourceField: column.key, label: column.label ?? column.key.slice(0, 160),
37
+ logicalType: column.logicalType, role: column.role, nullable: column.nullable,
38
+ ...(semanticRef === undefined ? {} : { semanticRef }),
39
+ ...(column.unit === undefined ? {} : { unit: column.unit })
40
+ };
41
+ });
42
+ // Column roles do not establish row uniqueness or business aggregation grain.
43
+ return exports.dataXpertOutputSchema.parse({ datasetId, fields, grain: [] });
44
+ }
package/docs/index.mdx CHANGED
@@ -7,8 +7,7 @@ description: "Adapt governed ontology Actions to Xpert Dataset/Binding without c
7
7
 
8
8
  Xpert owns the shared Dataset/Binding protocol, current actor, source configuration,
9
9
  parameter validation, execution budget and Artifact persistence. This system plugin registers
10
- `data-xpert.ontology`, operation `action`, contract version `1`, and the `data-xpert`
11
- DataSource type. It converts declared scalar parameters into native Action arguments and
10
+ `data-xpert.ontology`, operation `action`, contract version `1`, a Definition configuration entry and the `analytics-data-xpert` Integration strategy. It converts declared scalar parameters into native Action arguments and
12
11
  maps canonical rows, completeness and audit receipts back to the shared Dataset path.
13
12
 
14
13
  Data-xpert owns published resource discovery, ontology adapter metadata, Action input
@@ -20,8 +19,7 @@ The provider ID is a stable Binding reference; package ownership uses the
20
19
 
21
20
  ## Server configuration and identity
22
21
 
23
- Configure `baseUrl` (including `/api`), `audience`, `assistantCode`, `assistantXpertId`
24
- and `workspaceId` on the platform DataSource. The Assistant and workspace must already
22
+ Create a **data-xpert Analytics** Integration with `baseUrl` (including `/api`) and `audience`. In **Settings → Analytics**, publish a Definition with `integrationId`, `assistantCode`, `assistantXpertId` and `workspaceId`. The Integration is resolved in the current tenant/organization before and after each request. The Assistant and workspace must already
25
23
  have the intended published resources and viewer policy in data-xpert.
26
24
 
27
25
  The provider gets a short-lived token through
@@ -5,9 +5,10 @@ description: "Separate transport-contract tests from real SSO and end-to-end das
5
5
 
6
6
  ## Compatibility
7
7
 
8
- Development dependencies pin published SDK/contracts 3.25.0, which export AnalyticsProvider,
9
- AnalyticsQueryProvider, ActorTokenRuntimeFactoryCapability and the shared Dataset/Binding
10
- contracts. The host must provide the corresponding runtime capabilities.
8
+ Development dependencies pin `@xpert-ai/contracts` and `@xpert-ai/plugin-sdk` to 3.32.0.
9
+ Both peer dependencies require `^3.32.0`. The plugin validates upstream error responses with
10
+ `parseAnalyticsDiagnostic` and returns accepted diagnostics through `AnalyticsProviderError`.
11
+ The host must provide the Analytics and Actor Token runtime capabilities.
11
12
 
12
13
  Data-xpert must expose `POST /agent-workbench/analytics/discover` and
13
14
  `POST /agent-workbench/analytics/query` under the configured API base. Discovery returns
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xpert-ai/plugin-analytics-data-xpert",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Governed data-xpert ontology analytics provider",
5
5
  "main": "./index.cjs",
6
6
  "types": "./dist/index.d.ts",
@@ -27,21 +27,23 @@
27
27
  }
28
28
  },
29
29
  "peerDependencies": {
30
- "@xpert-ai/plugin-sdk": "^3.25.0",
31
- "@xpert-ai/contracts": "^3.25.0",
30
+ "@xpert-ai/plugin-sdk": "^3.32.0",
31
+ "@xpert-ai/contracts": "^3.32.0",
32
32
  "@nestjs/common": "^11.1.6",
33
33
  "@nestjs/core": "^11.1.6",
34
- "zod": "3.25.67"
34
+ "zod": "3.25.67",
35
+ "@nestjs/cqrs": "^11.0.3"
35
36
  },
36
37
  "devDependencies": {
37
- "@xpert-ai/plugin-sdk": "3.25.0",
38
- "@xpert-ai/contracts": "3.25.0",
38
+ "@xpert-ai/plugin-sdk": "3.32.0",
39
+ "@xpert-ai/contracts": "3.32.0",
39
40
  "@nestjs/common": "^11.1.6",
40
41
  "@nestjs/core": "^11.1.6",
41
42
  "zod": "3.25.67",
42
43
  "typescript": "^5.9.2",
43
44
  "@types/node": "^20.19.9",
44
- "reflect-metadata": "^0.2.2"
45
+ "reflect-metadata": "^0.2.2",
46
+ "@nestjs/cqrs": "^11.0.3"
45
47
  },
46
48
  "repository": {
47
49
  "type": "git",