@xpert-ai/plugin-live-dashboard 0.3.1 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/.xpertai-plugin/plugin.json +10 -2
  2. package/README.md +6 -0
  3. package/assets/branding/cover-v1.prompt.txt +27 -0
  4. package/assets/branding/cover-v1.webp +0 -0
  5. package/assets/previews/customer-brief.png +0 -0
  6. package/assets/previews/profit-atlas.png +0 -0
  7. package/assets/previews/trend-workspace.png +0 -0
  8. package/dist/index.js +6 -3
  9. package/dist/lib/agent.schema.d.ts +43 -37
  10. package/dist/lib/agent.schema.js +2 -1
  11. package/dist/lib/app-config.d.ts +62 -0
  12. package/dist/lib/app-config.js +38 -0
  13. package/dist/lib/artifact-type.provider.d.ts +15 -0
  14. package/dist/lib/artifact-type.provider.js +78 -0
  15. package/dist/lib/assistant-template.d.ts +1 -2
  16. package/dist/lib/assistant-template.js +3 -52
  17. package/dist/lib/content.provider.js +13 -5
  18. package/dist/lib/dashboard-navigation.d.ts +58 -14
  19. package/dist/lib/dashboard-navigation.js +11 -10
  20. package/dist/lib/dashboard-view.schema.d.ts +278 -46
  21. package/dist/lib/dashboard-view.schema.js +9 -1
  22. package/dist/lib/dashboard.service.d.ts +49 -3
  23. package/dist/lib/dashboard.service.js +64 -28
  24. package/dist/lib/document-libraries.d.ts +1 -1
  25. package/dist/lib/document-libraries.js +3 -2
  26. package/dist/lib/document-network.d.ts +1 -1
  27. package/dist/lib/document-network.js +2 -6
  28. package/dist/lib/editing-feedback.d.ts +5 -0
  29. package/dist/lib/editing-feedback.js +19 -0
  30. package/dist/lib/editing-patch.d.ts +22 -0
  31. package/dist/lib/editing-patch.js +46 -0
  32. package/dist/lib/editing-session.d.ts +167 -0
  33. package/dist/lib/editing-session.js +53 -0
  34. package/dist/lib/editing.d.ts +92 -60
  35. package/dist/lib/editing.js +36 -24
  36. package/dist/lib/html-guidance.d.ts +1 -1
  37. package/dist/lib/html-guidance.js +1 -1
  38. package/dist/lib/middleware.js +42 -18
  39. package/dist/lib/monitor.d.ts +70 -0
  40. package/dist/lib/monitor.js +101 -0
  41. package/dist/lib/plugin.js +4 -1
  42. package/dist/lib/project.d.ts +106 -0
  43. package/dist/lib/project.js +17 -0
  44. package/dist/lib/projects.schema.d.ts +63 -0
  45. package/dist/lib/projects.schema.js +8 -0
  46. package/dist/lib/projects.service.d.ts +25 -0
  47. package/dist/lib/projects.service.js +75 -0
  48. package/dist/lib/resource-card.js +1 -1
  49. package/dist/lib/runtime-context.d.ts +3 -0
  50. package/dist/lib/runtime-context.js +13 -0
  51. package/dist/lib/save-evidence.d.ts +1 -1
  52. package/dist/lib/templates.d.ts +2 -0
  53. package/dist/lib/templates.js +55 -0
  54. package/dist/lib/view.provider.d.ts +127 -8
  55. package/dist/lib/view.provider.js +49 -12
  56. package/dist/libraries/TREND-WORKSPACE-ICONS-LICENSE.txt +43 -0
  57. package/dist/libraries/catalog.json +1 -1
  58. package/dist/remote/app.css +1 -1
  59. package/dist/remote/app.js +34 -34
  60. package/dist/remote/build-manifest.json +116 -32
  61. package/dist/sandbox-actions/action.json +1 -1
  62. package/dist/sandbox-actions/bundle/host.js +2 -2
  63. package/dist/sandbox-actions/bundle/resource-cache.mjs +2 -1
  64. package/dist/sandbox-actions/bundle/validate.mjs +1 -1
  65. package/docs/docs.json +7 -0
  66. package/docs/frontend-libraries.mdx +12 -4
  67. package/docs/html-mode.md +3 -3
  68. package/docs/quick-start-acceptance.mdx +50 -0
  69. package/docs/quick-start.mdx +64 -0
  70. package/docs/superstore.mdx +38 -0
  71. package/docs/team-sharing.mdx +6 -2
  72. package/docs/trend-workspace-acceptance.mdx +43 -0
  73. package/docs/trend-workspace.mdx +30 -0
  74. package/docs/verification.mdx +4 -2
  75. package/docs/visual-styles-acceptance.mdx +40 -0
  76. package/docs/visual-styles.mdx +46 -0
  77. package/package.json +23 -17
  78. package/skills/live-dashboard/SKILL.md +27 -3
  79. package/skills/live-dashboard/examples/README.md +25 -0
  80. package/skills/live-dashboard/examples/catalog.json +79 -0
  81. package/skills/live-dashboard/examples/production-quality/README.md +25 -0
  82. package/skills/live-dashboard/examples/production-quality/dashboard.html +129 -0
  83. package/skills/live-dashboard/examples/production-quality/definition.json +67 -0
  84. package/skills/live-dashboard/examples/production-quality/demo.csv +7 -0
  85. package/skills/live-dashboard/examples/production-quality/demo.json +44 -0
  86. package/skills/live-dashboard/examples/production-quality/expected.json +26 -0
  87. package/skills/live-dashboard/examples/production-quality/preview.png +0 -0
  88. package/skills/live-dashboard/examples/production-quality/query.json +53 -0
  89. package/skills/live-dashboard/examples/regional-sales/README.md +25 -0
  90. package/skills/live-dashboard/examples/regional-sales/dashboard-create.json +35 -0
  91. package/skills/live-dashboard/examples/regional-sales/definition.json +35 -0
  92. package/skills/live-dashboard/examples/regional-sales/demo.csv +5 -0
  93. package/skills/live-dashboard/examples/regional-sales/demo.json +18 -0
  94. package/skills/live-dashboard/examples/regional-sales/expected.json +19 -0
  95. package/skills/live-dashboard/examples/regional-sales/preview.png +0 -0
  96. package/skills/live-dashboard/examples/regional-sales/query.json +45 -0
  97. package/skills/live-dashboard/examples/sales-overview/README.md +25 -0
  98. package/skills/live-dashboard/examples/sales-overview/dashboard.html +129 -0
  99. package/skills/live-dashboard/examples/sales-overview/definition.json +57 -0
  100. package/skills/live-dashboard/examples/sales-overview/demo.csv +7 -0
  101. package/skills/live-dashboard/examples/sales-overview/demo.json +38 -0
  102. package/skills/live-dashboard/examples/sales-overview/expected.json +22 -0
  103. package/skills/live-dashboard/examples/sales-overview/preview.png +0 -0
  104. package/skills/live-dashboard/examples/sales-overview/query.json +52 -0
  105. package/skills/live-dashboard/examples/superstore/README.md +64 -0
  106. package/skills/live-dashboard/examples/superstore/THIRD_PARTY_LICENSE.txt +207 -0
  107. package/skills/live-dashboard/examples/superstore/build_data.py +155 -0
  108. package/skills/live-dashboard/examples/superstore/build_pages.py +129 -0
  109. package/skills/live-dashboard/examples/superstore/checks.json +104 -0
  110. package/skills/live-dashboard/examples/superstore/customers-mobile-dark.png +0 -0
  111. package/skills/live-dashboard/examples/superstore/customers-select.png +0 -0
  112. package/skills/live-dashboard/examples/superstore/customers.html +316 -0
  113. package/skills/live-dashboard/examples/superstore/customers.png +0 -0
  114. package/skills/live-dashboard/examples/superstore/customers.queries.json +512 -0
  115. package/skills/live-dashboard/examples/superstore/expected.json +4069 -0
  116. package/skills/live-dashboard/examples/superstore/import.json +200 -0
  117. package/skills/live-dashboard/examples/superstore/orders.csv +10195 -0
  118. package/skills/live-dashboard/examples/superstore/overview-mobile-dark.png +0 -0
  119. package/skills/live-dashboard/examples/superstore/overview-select.png +0 -0
  120. package/skills/live-dashboard/examples/superstore/overview.html +310 -0
  121. package/skills/live-dashboard/examples/superstore/overview.png +0 -0
  122. package/skills/live-dashboard/examples/superstore/overview.queries.json +636 -0
  123. package/skills/live-dashboard/examples/superstore/page.js +109 -0
  124. package/skills/live-dashboard/examples/superstore/profitability-mobile-dark.png +0 -0
  125. package/skills/live-dashboard/examples/superstore/profitability-select.png +0 -0
  126. package/skills/live-dashboard/examples/superstore/profitability.html +307 -0
  127. package/skills/live-dashboard/examples/superstore/profitability.png +0 -0
  128. package/skills/live-dashboard/examples/superstore/profitability.queries.json +334 -0
  129. package/skills/live-dashboard/examples/superstore/provenance.json +24 -0
  130. package/skills/live-dashboard/examples/superstore/semantic-model.json +220 -0
  131. package/skills/live-dashboard/examples/superstore/test_data.py +62 -0
  132. package/skills/live-dashboard/references/html-runtime.md +1 -1
  133. package/skills/live-dashboard/themes/README.md +50 -0
  134. package/skills/live-dashboard/themes/customer-brief/README.md +47 -0
  135. package/skills/live-dashboard/themes/customer-brief/charts.js +17 -0
  136. package/skills/live-dashboard/themes/customer-brief/layout.html +22 -0
  137. package/skills/live-dashboard/themes/customer-brief/theme.css +108 -0
  138. package/skills/live-dashboard/themes/profit-atlas/README.md +43 -0
  139. package/skills/live-dashboard/themes/profit-atlas/charts.js +30 -0
  140. package/skills/live-dashboard/themes/profit-atlas/layout.html +19 -0
  141. package/skills/live-dashboard/themes/profit-atlas/theme.css +75 -0
  142. package/skills/live-dashboard/themes/trend-workspace/ICON_LICENSE.txt +43 -0
  143. package/skills/live-dashboard/themes/trend-workspace/README.md +144 -0
  144. package/skills/live-dashboard/themes/trend-workspace/charts.js +60 -0
  145. package/skills/live-dashboard/themes/trend-workspace/icons.js +11 -0
  146. package/skills/live-dashboard/themes/trend-workspace/layout.html +45 -0
  147. package/skills/live-dashboard/themes/trend-workspace/select.js +127 -0
  148. package/skills/live-dashboard/themes/trend-workspace/theme.css +137 -0
@@ -10,9 +10,11 @@ var __metadata = (this && this.__metadata) || function (k, v) {
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.DashboardMiddleware = exports.DASHBOARD_GUIDANCE = void 0;
13
+ const runtime_context_1 = require("./runtime-context");
13
14
  const definition_tools_1 = require("./definition-tools");
14
15
  const analytics_source_1 = require("./analytics-source");
15
16
  const analytics_1 = require("@xpert-ai/plugin-sdk/analytics");
17
+ const langgraph_1 = require("@langchain/langgraph");
16
18
  const messages_1 = require("@langchain/core/messages");
17
19
  const tools_1 = require("@langchain/core/tools");
18
20
  const node_crypto_1 = require("node:crypto");
@@ -27,6 +29,7 @@ const dashboard_service_1 = require("./dashboard.service");
27
29
  const dashboard_schema_1 = require("./dashboard.schema");
28
30
  const view_provider_1 = require("./view.provider");
29
31
  const editing_1 = require("./editing");
32
+ const editing_session_1 = require("./editing-session");
30
33
  const dashboard_receipt_1 = require("./dashboard-receipt");
31
34
  const filter_evidence_1 = require("./filter-evidence");
32
35
  const save_evidence_1 = require("./save-evidence");
@@ -34,7 +37,8 @@ const resource_card_1 = require("./resource-card");
34
37
  const dashboard_icon_1 = require("./dashboard-icon");
35
38
  const preview_retry_1 = require("./preview-retry");
36
39
  const prompt_workflows_1 = require("./prompt-workflows");
37
- const middlewareOptionsSchema = v3_1.z.object({ allowedFinalProviders: v3_1.z.array(v3_1.z.string().min(1)).min(1).max(20).optional() }).strict().default({});
40
+ const middlewareOptionsSchema = v3_1.z.object({ allowedFinalProviders: v3_1.z.array(v3_1.z.string().min(1)).min(1).max(20).optional(),
41
+ maxModelCalls: v3_1.z.number().int().min(1).max(64).default(24) }).strict().default({});
38
42
  const providerOptionSchema = {
39
43
  type: 'string',
40
44
  'x-ui': { component: 'remoteSelect', selectUrl: '/api/live-dashboard/provider-options' }
@@ -49,6 +53,9 @@ let DashboardMiddleware = class DashboardMiddleware {
49
53
  slashCommands: prompt_workflows_1.dashboardPromptWorkflows,
50
54
  // Tools inherit this self-contained icon through the platform middleware metadata.
51
55
  icon: dashboard_icon_1.dashboardIcon, features: [view_provider_1.FEATURE], configSchema: { type: 'object', properties: {
56
+ maxModelCalls: { type: 'integer', minimum: 1, maximum: 64, default: 24,
57
+ title: { en_US: 'Model call limit', zh_Hans: '模型调用上限' },
58
+ description: { en_US: 'Bounded per-run budget, including recipe reads and source setup. Save validation remains required.', zh_Hans: '每次执行的调用预算,包含配方读取和数据源准备;保存校验仍然必须通过。' } },
52
59
  allowedFinalProviders: { type: 'array', minItems: 1, maxItems: 20, items: providerOptionSchema, title: { en_US: 'Allowed final dashboard providers', zh_Hans: '最终看板允许的数据源类型' },
53
60
  description: { en_US: 'Choose at least one provider from sources you can access when setting this policy. Existing unavailable IDs are retained. Upstream previews remain available.', zh_Hans: '配置此限制时至少选择一种当前可访问数据源的 Provider;暂不可用的已有值会保留,不影响上游来源预览。' } }
54
61
  }, additionalProperties: false }
@@ -58,10 +65,8 @@ let DashboardMiddleware = class DashboardMiddleware {
58
65
  const options = middlewareOptionsSchema.parse(_options);
59
66
  if (!context.xpertId)
60
67
  throw new Error('DASHBOARD_ASSISTANT_REQUIRED');
61
- const scope = {
62
- tenantId: context.tenantId ?? plugin_sdk_1.RequestContext.currentTenantId(), organizationId: context.organizationId ?? plugin_sdk_1.RequestContext.getOrganizationId(),
63
- userId: context.userId ?? plugin_sdk_1.RequestContext.currentUserId(), hostType: context.projectId ? 'project' : 'agent', hostId: context.projectId ?? context.xpertId, slots: []
64
- };
68
+ const scope = (0, runtime_context_1.dashboardRuntimeContext)(context);
69
+ const editing = (0, editing_session_1.editingSession)({ ...context, tenantId: scope.tenantId, organizationId: scope.organizationId, userId: scope.userId });
65
70
  // Evidence belongs to this run and actor, never a process-wide cache of business data.
66
71
  const discovered = new Map(), previews = new Map();
67
72
  const discovering = new Map();
@@ -114,13 +119,13 @@ let DashboardMiddleware = class DashboardMiddleware {
114
119
  throw new Error('DASHBOARD_PREVIEW_REQUIRED: use a previewId returned in this run');
115
120
  return binding;
116
121
  });
117
- const validateFinal = (manifest, requirePreviews) => {
122
+ const validateFinal = (manifest, requirePreviews, changedBindingIds) => {
118
123
  const allowedProviders = options.allowedFinalProviders;
119
124
  if (allowedProviders && manifest.bindings.some(binding => !allowedProviders.includes(binding.providerId)))
120
125
  throw new Error(`DASHBOARD_PROVIDER_REQUIRED: final bindings must use ${allowedProviders.join(', ')}`);
121
126
  if (requirePreviews) {
122
127
  const controls = Object.fromEntries(manifest.controls.filter(item => item.defaultValue !== undefined).map(item => [item.controlId, item.defaultValue ?? null]));
123
- for (const binding of manifest.bindings) {
128
+ for (const binding of manifest.bindings.filter(binding => !changedBindingIds || changedBindingIds.includes(binding.id))) {
124
129
  const previewId = previewKey(binding, controls);
125
130
  if (!previews.has(previewId))
126
131
  throw new Error('DASHBOARD_PREVIEW_REQUIRED: preview the exact binding and default controls before saving');
@@ -129,13 +134,16 @@ let DashboardMiddleware = class DashboardMiddleware {
129
134
  }
130
135
  };
131
136
  const save = async (input, config, html, dependencies = []) => {
132
- const { previewIds, supersedesPaths, ...definition } = input;
137
+ const { previewIds, supersedesPaths, creationIntent, ...definition } = input;
133
138
  const bindings = resolvePreviews(previewIds);
134
139
  const manifest = dashboard_schema_1.dashboardSchema.parse({ ...definition, bindings, schema: 'xpert.live-dashboard', id: (0, node_crypto_1.randomUUID)(),
135
140
  ...(html ? { presentation: { type: 'html', html, ...(dependencies.length ? { dependencies } : {}) } } : {}) });
136
141
  validateFinal(manifest, true);
137
142
  const key = hash({ input: { ...definition, previewIds }, html, dependencies });
138
143
  const previous = created.get(key);
144
+ const active = editing.resolve(config);
145
+ if (active && !creationIntent && (!previous || previous.receipt.artifactId !== active.artifactId))
146
+ throw new Error('DASHBOARD_EDIT_TARGET_ACTIVE: use dashboard_open/update for the active dashboard. Creating a new Artifact requires an explicit user request and creationIntent=new or copy.');
139
147
  if (previous) {
140
148
  const receipt = await this.service.save(scope, previous.manifest, previous.receipt.baseVersionId, previous.receipt.draftRevision);
141
149
  created.set(key, { manifest: previous.manifest, receipt });
@@ -143,7 +151,7 @@ let DashboardMiddleware = class DashboardMiddleware {
143
151
  return JSON.stringify({ dashboard: receipt, resourceCard });
144
152
  }
145
153
  const sameTitle = [...created.values()].find(item => item.manifest.title === manifest.title);
146
- if (sameTitle)
154
+ if (sameTitle && !creationIntent)
147
155
  throw new Error(`DASHBOARD_ALREADY_CREATED: this run already saved this dashboard. Use dashboard_open with artifactId ${sameTitle.receipt.artifactId}, then dashboard_update with fresh previewIds for data changes; preserve the Artifact instead of creating another one.`);
148
156
  const result = await this.service.save(scope, manifest, null);
149
157
  created.set(key, { manifest, receipt: result });
@@ -158,7 +166,9 @@ let DashboardMiddleware = class DashboardMiddleware {
158
166
  });
159
167
  const previewRetries = (0, preview_retry_1.createPreviewRetryGuard)();
160
168
  let modelCalls = 0;
161
- return { name: 'LiveDashboard', contextSchema: editing_1.dashboardContextSchema,
169
+ return { name: 'LiveDashboard', contextSchema: editing_1.dashboardContextSchema, stateSchema: editing_session_1.dashboardStateSchema,
170
+ // Platform execution scope and request context occupy different runtime fields.
171
+ beforeAgent: (state, runtime) => editing.restore(state, { live_dashboard: (0, editing_session_1.selectionFromConfig)(runtime) }),
162
172
  wrapToolCall: async (request, handler) => {
163
173
  const name = request.toolCall.name;
164
174
  const args = request.toolCall.args;
@@ -170,6 +180,16 @@ let DashboardMiddleware = class DashboardMiddleware {
170
180
  ? await evidence.trackWrite(path, async () => handler(request)) : await previewRetries.run(request, handler);
171
181
  if (save && 'content' in result && (!('status' in result) || result.status !== 'error'))
172
182
  await evidence.accept(save, result.content);
183
+ if ('content' in result && (!('status' in result) || result.status !== 'error')) {
184
+ const receipt = (0, dashboard_receipt_1.createdDashboard)(result.content);
185
+ if (receipt)
186
+ return new langgraph_1.Command({ update: { messages: [result], ...editing.select({ schemaVersion: 1, artifactId: receipt.artifactId, draftRevision: receipt.draftRevision }) } });
187
+ if (name === 'dashboard_open' && typeof result.content === 'string') {
188
+ const opened = v3_1.z.object({ artifactId: v3_1.z.string().uuid(), readVersionId: v3_1.z.string().uuid().optional(), expectedRevision: v3_1.z.number() }).safeParse(JSON.parse(result.content));
189
+ if (opened.success && !opened.data.readVersionId)
190
+ return new langgraph_1.Command({ update: { messages: [result], ...editing.select({ schemaVersion: 1, artifactId: opened.data.artifactId, ...(opened.data.expectedRevision > 0 ? { draftRevision: opened.data.expectedRevision } : {}) }) } });
191
+ }
192
+ }
173
193
  return result;
174
194
  }, wrapModelCall: async (request, handler) => {
175
195
  if (previewRetries.exhausted) {
@@ -181,12 +201,12 @@ let DashboardMiddleware = class DashboardMiddleware {
181
201
  // A noncompliant model must not restart the failed loop even after tools were removed.
182
202
  return result?.tool_calls?.length ? new messages_1.AIMessage(summary) : result;
183
203
  }
184
- const exhausted = ++modelCalls > 24;
204
+ const exhausted = ++modelCalls > options.maxModelCalls;
185
205
  if (exhausted && evidence.pending)
186
206
  throw new Error('DASHBOARD_SAVE_NOT_CONFIRMED: no successful draft save receipt; Workspace changes have not been saved to the dashboard.');
187
207
  const result = await handler({ ...request, ...(evidence.pending ? { toolChoice: 'required' } : {}), ...(exhausted ? { tools: [] } : {}), systemMessage: new messages_1.SystemMessage({ content: [
188
208
  ...(typeof request.systemMessage?.content === 'string' ? [{ type: 'text', text: request.systemMessage.content }] : request.systemMessage?.content ?? []),
189
- { type: 'text', text: exports.DASHBOARD_GUIDANCE + '\n' + definition_tools_1.DEFINITION_GUIDANCE + '\n' + html_guidance_1.HTML_DASHBOARD_GUIDANCE + '\n' + editing_1.EDITING_GUIDANCE + (evidence.pending ? `\nDashboard changes are pending without a matching draft save receipt. Unsaved files: ${JSON.stringify(evidence.pendingPaths)}. Continue reading/repairing and call dashboard_create_html or dashboard_update with the exact file path; do not finish or claim it was saved. When abandoning earlier unsaved HTML creation candidates, explicitly pass their exact paths as supersedesPaths to dashboard_create or dashboard_create_html, matching the requested presentation mode. If a standard dashboard is already saved, repeat dashboard_create with the same definition/previewIds and supersedesPaths; do not create an unwanted HTML dashboard. This only abandons those candidates after a successful save; it never discards another Artifact editing copy.` : '') + (options.allowedFinalProviders ? `\nThis Assistant requires every saved dashboard binding to use one of these providers: ${options.allowedFinalProviders.join(', ')}. Other providers may be previewed only as upstream inputs. Do not save a direct-source substitute.` : '') + (exhausted ? '\nThe dashboard tool budget is exhausted. Stop calls; report only saved receipts or explain the concrete blocker truthfully.' : '') }
209
+ { type: 'text', text: exports.DASHBOARD_GUIDANCE + (editing.current() ? `\nCurrent editing target: ${JSON.stringify(editing.current())}. Continue this Artifact with dashboard_open/update unless the user explicitly requests a new independent dashboard.` : '') + '\n' + definition_tools_1.DEFINITION_GUIDANCE + '\n' + html_guidance_1.HTML_DASHBOARD_GUIDANCE + '\n' + editing_1.EDITING_GUIDANCE + (evidence.pending ? `\nDashboard changes are pending without a matching draft save receipt. Unsaved files: ${JSON.stringify(evidence.pendingPaths)}. Continue reading/repairing and call dashboard_create_html or dashboard_update with the exact file path; do not finish or claim it was saved. When abandoning earlier unsaved HTML creation candidates, explicitly pass their exact paths as supersedesPaths to dashboard_create or dashboard_create_html, matching the requested presentation mode. If a standard dashboard is already saved, repeat dashboard_create with the same definition/previewIds and supersedesPaths; do not create an unwanted HTML dashboard. This only abandons those candidates after a successful save; it never discards another Artifact editing copy.` : '') + (options.allowedFinalProviders ? `\nThis Assistant requires every saved dashboard binding to use one of these providers: ${options.allowedFinalProviders.join(', ')}. Other providers may be previewed only as upstream inputs. Do not save a direct-source substitute.` : '') + (exhausted ? '\nThe dashboard tool budget is exhausted. Stop calls; report only saved receipts or explain the concrete blocker truthfully.' : '') }
190
210
  ] }) });
191
211
  if (evidence.pending && result && !result.tool_calls?.length)
192
212
  throw new Error('DASHBOARD_SAVE_NOT_CONFIRMED: the model stopped without a successful draft save receipt.');
@@ -194,9 +214,9 @@ let DashboardMiddleware = class DashboardMiddleware {
194
214
  }, tools: [
195
215
  filterEvidence.tool,
196
216
  ...(0, definition_tools_1.createDefinitionTools)(context, () => { listedSources = undefined; }),
197
- (0, tools_1.tool)(async () => JSON.stringify({ libraries: await (0, document_libraries_1.libraryLocks)(['echarts', 'three']),
198
- online: { ...document_network_1.DASHBOARD_NETWORK_POLICY,
199
- usage: 'HTML may load scripts, stylesheets and fonts directly from these exact HTTPS origins. Use script src and link rel="stylesheet"; pin exact versions where available. Tailwind CDN https://cdn.tailwindcss.com is supported. Online resources require connectivity when saving and viewing; loading failures fail preflight. Keep all business data queries on window.xpertLiveArtifact; fetch/XHR, external images, workers, iframes and eval remain unavailable. Online resources do not belong in the packaged libraries array.' },
217
+ (0, tools_1.tool)(async () => JSON.stringify({ libraries: (await (0, document_libraries_1.bundledLibraries)()).map(({ source, ...lock }) => lock),
218
+ online: { enabled: true, ...document_network_1.DASHBOARD_NETWORK_POLICY,
219
+ usage: 'Network access is temporarily unrestricted for functional acceptance: CDN scripts/styles/fonts, external images, CSS imports and fetch/XHR are available, subject to browser CORS and mixed-content rules. Tailwind CDN is supported. Use exact versions when possible. Business data from platform sources must still use window.xpertLiveArtifact and saved Bindings; never embed credentials.' },
200
220
  pageRuntime: {
201
221
  global: 'window.xpertLiveArtifact',
202
222
  bindingIds: [...new Set([...previews.values()].map(binding => binding.id))],
@@ -204,11 +224,15 @@ let DashboardMiddleware = class DashboardMiddleware {
204
224
  refresh: 'live.refresh(bindingId) requires a Binding ID. live.refresh() without an ID is invalid. For a page refresh button or timer, call Promise.all(bindingIds.map(id => live.refresh(id))). Each subscription redraws its KPI, chart and details.',
205
225
  filters: 'Mark native data selects with data-dashboard-control="<exact controlId>". Mark purely visual selects with data-dashboard-presentation instead. Changing a data select must call live.query for every affected Binding, not only filter cached rows in JS. bindSelect updates one Binding only. Read references/html-lifecycle.md before writing the page.'
206
226
  },
207
- usage: { echarts: 'window.echarts: init(element, null, {renderer:"svg"}) then setOption(completeOption, {notMerge:true}). Canvas is also available. Derive active series/legend dimensions from each received Dataset, including filtered results; never retain excluded groups as zero-valued series. Aggregate repeated categories using all grain dimensions, clear empty results, and redraw KPI, chart and details together in subscribe. Use ResizeObserver to resize and reuse the instance on filter/refresh.',
227
+ usage: { 'trend-workspace': 'window.TrendWorkspace: pale-blue KPI rail and aligned trends. Read themes/trend-workspace/README.md.',
228
+ 'profit-atlas': 'window.ProfitAtlas: dark profitability workbench, horizontal KPIs, scatter-first analysis and signed rankings. Read themes/profit-atlas/README.md.',
229
+ 'customer-brief': 'window.CustomerBrief: warm editorial customer brief, serif hierarchy, grouped return metrics and segment comparisons. Read themes/customer-brief/README.md.',
230
+ themes: 'Select exactly one theme with echarts. Each provides enhanceSelect(nativeSelect) and charts.line/horizontal/scatter with isolated CSS and pinned bytes. Read themes/README.md; preserve an existing dashboard theme unless the user requests a change.',
231
+ echarts: 'window.echarts: init(element, null, {renderer:"svg"}) then setOption(completeOption, {notMerge:true}). Canvas is also available. Derive active series/legend dimensions from each received Dataset, including filtered results; never retain excluded groups as zero-valued series. Aggregate repeated categories using all grain dimensions, clear empty results, and redraw KPI, chart and details together in subscribe. Use ResizeObserver to resize and reuse the instance on filter/refresh.',
208
232
  three: 'window.THREE: bundled core including WebGLRenderer. Requires WebGL2; catch unavailable GPU contexts and show a clear fallback. External textures/models, loaders/addons, WebGPU and workers are not bundled.' },
209
- lifecycle: 'Pass packaged library IDs to dashboard_create_html or dashboard_update. Actual version and bytes are pinned on save; omitted libraries on update preserves saved locks. Online resources are listed separately under online, are not pinned in the Artifact, and must load successfully when saving and viewing.' }), { name: 'dashboard_libraries', description: 'Discover packaged frontend libraries, exact versions and allowed HTTPS origins for online scripts, stylesheets and fonts. Read before using Tailwind CDN or ECharts/Three.js. Returns metadata, never library source.',
233
+ lifecycle: 'Pass packaged library IDs to dashboard_create_html or dashboard_update. Actual version and bytes are pinned on save; omitted libraries on update preserves saved locks. Online resources must load successfully during preflight and viewing; they are not frozen with the Artifact.' }), { name: 'dashboard_libraries', description: 'Discover packaged libraries, themes, exact versions and the current unrestricted network policy. Tailwind CDN and other online dependencies are supported. Returns metadata, never library source.',
210
234
  schema: v3_1.z.object({}).strict(), verboseParsingErrors: true, metadata: { toolName: { en_US: 'Discover dashboard libraries', zh_Hans: '查看看板前端图库' } } }),
211
- ...(0, editing_1.createEditingTools)(this.service, scope, context, resolvePreviews, validateFinal, (path, artifactId) => evidence.opened(path, artifactId)),
235
+ ...(0, editing_1.createEditingTools)(this.service, scope, context, resolvePreviews, validateFinal, (path, artifactId) => evidence.opened(path, artifactId), editing.resolve),
212
236
  (0, tools_1.tool)(async () => {
213
237
  listedSources = await this.service.sources(scope);
214
238
  return JSON.stringify(listedSources);
@@ -0,0 +1,70 @@
1
+ import { z } from 'zod/v3';
2
+ import { type AgentMiddleware, type IAgentMiddlewareStrategy, type IAgentMiddlewareContext } from '@xpert-ai/plugin-sdk';
3
+ import type { TAgentMiddlewareMeta, XpertResolvedViewHostContext } from '@xpert-ai/contracts';
4
+ import { DashboardService } from './dashboard.service';
5
+ declare const freshnessRuleSchema: z.ZodObject<{
6
+ bindingId: z.ZodString;
7
+ fieldId: z.ZodString;
8
+ maxAgeSeconds: z.ZodNumber;
9
+ }, "strict", z.ZodTypeAny, {
10
+ bindingId: string;
11
+ fieldId: string;
12
+ maxAgeSeconds: number;
13
+ }, {
14
+ bindingId: string;
15
+ fieldId: string;
16
+ maxAgeSeconds: number;
17
+ }>;
18
+ type FreshnessRule = z.infer<typeof freshnessRuleSchema>;
19
+ export declare class DashboardMonitor {
20
+ private readonly dashboards;
21
+ constructor(dashboards: DashboardService);
22
+ inspect(context: XpertResolvedViewHostContext, artifactId: string, freshnessRule?: FreshnessRule): Promise<{
23
+ artifactId: string;
24
+ versionId: string;
25
+ title: string;
26
+ status: "attention" | "healthy";
27
+ checkedAt: string;
28
+ failures: {
29
+ bindingId: string;
30
+ datasetId: string;
31
+ reason: "unavailable";
32
+ }[];
33
+ datasets: {
34
+ datasetId: string;
35
+ queryFetchedAt: string;
36
+ truncated: boolean;
37
+ returnedRows: number;
38
+ }[];
39
+ businessFreshness: {
40
+ status: "unknown" | "stale" | "fresh";
41
+ latestTimestamp: string | null;
42
+ coverage: "latest_returned_timestamp";
43
+ bindingId: string;
44
+ fieldId: string;
45
+ maxAgeSeconds: number;
46
+ } | "not_asserted";
47
+ availableFreshnessFields: {
48
+ bindingId: string;
49
+ fieldId: string;
50
+ label: string;
51
+ }[];
52
+ }>;
53
+ }
54
+ export declare class DashboardMonitorMiddleware implements IAgentMiddlewareStrategy {
55
+ private readonly monitor;
56
+ private readonly dashboards;
57
+ readonly meta: TAgentMiddlewareMeta;
58
+ constructor(monitor: DashboardMonitor, dashboards: DashboardService);
59
+ createMiddleware(options: unknown, context: IAgentMiddlewareContext): AgentMiddleware;
60
+ }
61
+ /** Business freshness must be an explicit data contract, never the query fetch timestamp. */
62
+ export declare function assessFreshness(manifest: import('./dashboard.schema').DashboardManifest, datasets: import('@xpert-ai/contracts').AnalyticalDataset[], input: FreshnessRule, now: number): {
63
+ status: "unknown" | "stale" | "fresh";
64
+ latestTimestamp: string | null;
65
+ coverage: "latest_returned_timestamp";
66
+ bindingId: string;
67
+ fieldId: string;
68
+ maxAgeSeconds: number;
69
+ };
70
+ export {};
@@ -0,0 +1,101 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.DashboardMonitorMiddleware = exports.DashboardMonitor = void 0;
13
+ exports.assessFreshness = assessFreshness;
14
+ const common_1 = require("@nestjs/common");
15
+ const tools_1 = require("@langchain/core/tools");
16
+ const v3_1 = require("zod/v3");
17
+ const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
18
+ const dashboard_service_1 = require("./dashboard.service");
19
+ const runtime_context_1 = require("./runtime-context");
20
+ const dashboard_icon_1 = require("./dashboard-icon");
21
+ const freshnessRuleSchema = v3_1.z.object({ bindingId: v3_1.z.string().min(1), fieldId: v3_1.z.string().min(1), maxAgeSeconds: v3_1.z.number().int().min(1).max(31536000) }).strict();
22
+ let DashboardMonitor = class DashboardMonitor {
23
+ constructor(dashboards) {
24
+ this.dashboards = dashboards;
25
+ }
26
+ async inspect(context, artifactId, freshnessRule) {
27
+ const access = await this.dashboards.access(context);
28
+ if (!access.project || access.archived)
29
+ throw new common_1.ForbiddenException('DASHBOARD_ACTIVE_PROJECT_REQUIRED');
30
+ const saved = await this.dashboards.open(context, artifactId, undefined, undefined, 'published');
31
+ if (!saved.versionId)
32
+ throw new Error('DASHBOARD_NOT_PUBLISHED');
33
+ const result = await this.dashboards.query(context, artifactId, saved.versionId, {});
34
+ // Reauthorize the target after the queries; a receipt is bound to this formal version.
35
+ const current = await this.dashboards.open(context, artifactId, undefined, undefined, 'published');
36
+ if (current.versionId !== saved.versionId || (await this.dashboards.access(context)).archived)
37
+ throw new Error('DASHBOARD_INSPECTION_CHANGED');
38
+ const freshness = freshnessRule ? assessFreshness(saved.manifest, result.datasets, freshnessRule, Date.now()) : undefined;
39
+ return { artifactId, versionId: saved.versionId, title: saved.manifest.title,
40
+ status: result.failures.length || freshness && freshness.status !== 'fresh' ? 'attention' : 'healthy',
41
+ checkedAt: new Date().toISOString(), failures: result.failures,
42
+ datasets: result.datasets.map(dataset => ({ datasetId: dataset.datasetId, queryFetchedAt: dataset.freshness.sourceFetchedAt,
43
+ truncated: dataset.completeness.truncated, returnedRows: dataset.completeness.returnedRows })),
44
+ businessFreshness: freshness ?? 'not_asserted',
45
+ availableFreshnessFields: saved.manifest.bindings.flatMap(binding => binding.output.fields.filter(field => field.logicalType === 'timestamp')
46
+ .map(field => ({ bindingId: binding.id, fieldId: field.id, label: field.label }))) };
47
+ }
48
+ };
49
+ exports.DashboardMonitor = DashboardMonitor;
50
+ exports.DashboardMonitor = DashboardMonitor = __decorate([
51
+ (0, common_1.Injectable)(),
52
+ __metadata("design:paramtypes", [dashboard_service_1.DashboardService])
53
+ ], DashboardMonitor);
54
+ let DashboardMonitorMiddleware = class DashboardMonitorMiddleware {
55
+ constructor(monitor, dashboards) {
56
+ this.monitor = monitor;
57
+ this.dashboards = dashboards;
58
+ this.meta = { name: 'LiveDashboardMonitor', icon: dashboard_icon_1.dashboardIcon, features: ['live_dashboard.monitor'],
59
+ label: { en_US: 'Dashboard inspection', zh_Hans: '看板巡检' },
60
+ description: { en_US: 'Inspect published Project dashboards without editing or publishing.', zh_Hans: '检查项目正式看板,不修改或发布。' },
61
+ configSchema: { type: 'object', properties: {}, additionalProperties: false } };
62
+ }
63
+ createMiddleware(options, context) {
64
+ v3_1.z.object({}).strict().parse(options ?? {});
65
+ const scope = (0, runtime_context_1.dashboardRuntimeContext)(context);
66
+ return { name: 'LiveDashboardMonitor', tools: [
67
+ (0, tools_1.tool)(async ({ artifactId, freshness }) => JSON.stringify(await this.monitor.inspect(scope, artifactId, freshness)), {
68
+ name: 'dashboard_inspect', description: 'Check one explicitly selected published Project dashboard as the current run actor. Returns compact binding availability and query timestamps, never data rows. Does not modify or publish. Query time is not proof of business-data freshness. Only an explicitly configured freshness rule checks the latest returned declared timestamp, and incomplete results remain unknown. Scheduled runs retain this receipt in platform execution history.',
69
+ schema: v3_1.z.object({ artifactId: v3_1.z.string().uuid(), freshness: freshnessRuleSchema.optional() }).strict(), verboseParsingErrors: true,
70
+ metadata: { toolName: { en_US: 'Inspect published dashboard', zh_Hans: '巡检正式看板' } }
71
+ }),
72
+ (0, tools_1.tool)(async ({ page }) => {
73
+ const access = await this.dashboards.access(scope);
74
+ if (!access.project || access.archived)
75
+ throw new common_1.ForbiddenException('DASHBOARD_ACTIVE_PROJECT_REQUIRED');
76
+ const result = await this.dashboards.listPage(scope, page, 50);
77
+ return JSON.stringify({ ...result, items: result.items.filter(item => !!item.versionId).map(({ artifactId, versionId, versionNumber, title }) => ({ artifactId, versionId, versionNumber, title })) });
78
+ }, { name: 'dashboard_list_published', description: 'List published dashboards in the current Project. Follow hasMore/page; never infer the target from recency.',
79
+ schema: v3_1.z.object({ page: v3_1.z.number().int().positive().default(1) }).strict(), verboseParsingErrors: true,
80
+ metadata: { toolName: { en_US: 'List published dashboards', zh_Hans: '列出正式看板' } } })
81
+ ] };
82
+ }
83
+ };
84
+ exports.DashboardMonitorMiddleware = DashboardMonitorMiddleware;
85
+ exports.DashboardMonitorMiddleware = DashboardMonitorMiddleware = __decorate([
86
+ (0, plugin_sdk_1.AgentMiddlewareStrategy)('LiveDashboardMonitor'),
87
+ __metadata("design:paramtypes", [DashboardMonitor, dashboard_service_1.DashboardService])
88
+ ], DashboardMonitorMiddleware);
89
+ /** Business freshness must be an explicit data contract, never the query fetch timestamp. */
90
+ function assessFreshness(manifest, datasets, input, now) {
91
+ const rule = freshnessRuleSchema.parse(input);
92
+ const binding = manifest.bindings.find(binding => binding.id === rule.bindingId);
93
+ const field = binding?.output.fields.find(field => field.id === rule.fieldId && field.logicalType === 'timestamp');
94
+ const dataset = datasets.find(dataset => dataset.datasetId === binding?.output.datasetId);
95
+ const values = dataset?.rows.map(row => row[rule.fieldId]) ?? [];
96
+ const times = values.map(value => typeof value === 'string' && /(?:Z|[+-]\d{2}:\d{2})$/.test(value) ? Date.parse(value) : NaN);
97
+ const unknown = !field || !dataset || dataset.completeness.truncated || !times.length || times.some(time => !Number.isFinite(time) || time > now + 60000);
98
+ const latest = unknown ? null : times.reduce((latest, time) => Math.max(latest, time), -Infinity);
99
+ return { ...rule, status: unknown ? 'unknown' : now - latest > rule.maxAgeSeconds * 1000 ? 'stale' : 'fresh',
100
+ latestTimestamp: latest === null ? null : new Date(latest).toISOString(), coverage: 'latest_returned_timestamp' };
101
+ }
@@ -7,7 +7,9 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  };
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.LiveDashboardPlugin = void 0;
10
+ const projects_service_1 = require("./projects.service");
10
11
  const content_capability_1 = require("./content-capability");
12
+ const monitor_1 = require("./monitor");
11
13
  const content_provider_1 = require("./content.provider");
12
14
  const plugin_sdk_1 = require("@xpert-ai/plugin-sdk");
13
15
  const dashboard_service_1 = require("./dashboard.service");
@@ -16,10 +18,11 @@ const middleware_1 = require("./middleware");
16
18
  const html_validation_1 = require("./html-validation");
17
19
  const presentation_provider_1 = require("./presentation.provider");
18
20
  const sharing_service_1 = require("./sharing.service");
21
+ const artifact_type_provider_1 = require("./artifact-type.provider");
19
22
  const provider_options_controller_1 = require("./provider-options.controller");
20
23
  let LiveDashboardPlugin = class LiveDashboardPlugin {
21
24
  };
22
25
  exports.LiveDashboardPlugin = LiveDashboardPlugin;
23
26
  exports.LiveDashboardPlugin = LiveDashboardPlugin = __decorate([
24
- (0, plugin_sdk_1.XpertServerPlugin)({ controllers: [provider_options_controller_1.DashboardProviderOptionsController], providers: [content_capability_1.DashboardCapability, content_provider_1.DashboardContentProvider, content_provider_1.DashboardContentEditor, dashboard_service_1.DashboardService, view_provider_1.DashboardViewProvider, middleware_1.DashboardMiddleware, html_validation_1.HtmlDashboardValidation, presentation_provider_1.DashboardPresentationProvider, sharing_service_1.DashboardSharingService] })
27
+ (0, plugin_sdk_1.XpertServerPlugin)({ controllers: [provider_options_controller_1.DashboardProviderOptionsController], providers: [projects_service_1.DashboardProjectsService, monitor_1.DashboardMonitor, monitor_1.DashboardMonitorMiddleware, artifact_type_provider_1.DashboardArtifactType, content_capability_1.DashboardCapability, content_provider_1.DashboardContentProvider, content_provider_1.DashboardContentEditor, dashboard_service_1.DashboardService, view_provider_1.DashboardViewProvider, middleware_1.DashboardMiddleware, html_validation_1.HtmlDashboardValidation, presentation_provider_1.DashboardPresentationProvider, sharing_service_1.DashboardSharingService] })
25
28
  ], LiveDashboardPlugin);
@@ -0,0 +1,106 @@
1
+ export declare const DASHBOARD_PROJECT_TYPE: {
2
+ readonly applicationKey: "@xpert-ai/plugin-live-dashboard:live-dashboard";
3
+ readonly projectTypeKey: "dashboard";
4
+ };
5
+ export declare const dashboardApp: {
6
+ type: "app";
7
+ name: string;
8
+ icon: {
9
+ type: "svg";
10
+ alt: string;
11
+ value: string;
12
+ };
13
+ appConfig: {
14
+ scope: "organization";
15
+ assistantTemplateKey: string;
16
+ workspace: {
17
+ mode: "dedicated";
18
+ sharing: "private";
19
+ name: {
20
+ en_US: string;
21
+ zh_Hans: string;
22
+ };
23
+ description: {
24
+ en_US: string;
25
+ zh_Hans: string;
26
+ };
27
+ };
28
+ knowledgebases: never[];
29
+ modelRequirements: {
30
+ primary: true;
31
+ };
32
+ presentation: {
33
+ developer: string;
34
+ tagline: {
35
+ en_US: string;
36
+ zh_Hans: string;
37
+ };
38
+ longDescription: {
39
+ en_US: string;
40
+ zh_Hans: string;
41
+ };
42
+ screenshots: string[];
43
+ features: {
44
+ key: string;
45
+ title: {
46
+ en_US: string;
47
+ zh_Hans: string;
48
+ };
49
+ description: {
50
+ en_US: string;
51
+ zh_Hans: string;
52
+ };
53
+ }[];
54
+ useCases: {
55
+ en_US: string;
56
+ zh_Hans: string;
57
+ }[];
58
+ dataScope: {
59
+ en_US: string;
60
+ zh_Hans: string;
61
+ };
62
+ initializationSummary: {
63
+ en_US: string;
64
+ zh_Hans: string;
65
+ };
66
+ initializationSteps: {
67
+ en_US: string;
68
+ zh_Hans: string;
69
+ }[];
70
+ };
71
+ entry: {
72
+ type: "assistant-chat";
73
+ };
74
+ };
75
+ displayName: {
76
+ en_US: string;
77
+ zh_Hans: string;
78
+ };
79
+ description: {
80
+ en_US: string;
81
+ zh_Hans: string;
82
+ };
83
+ projectTypes: {
84
+ key: "dashboard";
85
+ title: {
86
+ en_US: string;
87
+ zh_Hans: string;
88
+ };
89
+ icon: {
90
+ type: "svg";
91
+ alt: string;
92
+ value: string;
93
+ };
94
+ binding: {
95
+ kind: "project";
96
+ };
97
+ }[];
98
+ };
99
+ export type DashboardAccess = {
100
+ project: boolean;
101
+ archived: boolean;
102
+ canReadDraft: boolean;
103
+ canEdit: boolean;
104
+ canManage: boolean;
105
+ };
106
+ export type DashboardMode = 'published' | 'draft';
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dashboardApp = exports.DASHBOARD_PROJECT_TYPE = void 0;
4
+ const dashboard_icon_1 = require("./dashboard-icon");
5
+ const app_config_1 = require("./app-config");
6
+ exports.DASHBOARD_PROJECT_TYPE = {
7
+ applicationKey: '@xpert-ai/plugin-live-dashboard:live-dashboard', projectTypeKey: 'dashboard'
8
+ };
9
+ exports.dashboardApp = {
10
+ type: 'app', name: 'live-dashboard', icon: dashboard_icon_1.dashboardIcon,
11
+ appConfig: app_config_1.dashboardAppConfig,
12
+ displayName: { en_US: 'Live Dashboard', zh_Hans: '实时看板' },
13
+ description: { en_US: 'Create and use live dashboards in Assistant ChatKit with a synchronized Project selection.', zh_Hans: '在助理 ChatKit 中按项目制作和使用实时看板,项目选择双向同步。' },
14
+ projectTypes: [{ key: exports.DASHBOARD_PROJECT_TYPE.projectTypeKey,
15
+ title: { en_US: 'Live Dashboard project', zh_Hans: '实时看板项目' }, icon: dashboard_icon_1.dashboardIcon,
16
+ binding: { kind: 'project' } }]
17
+ };
@@ -0,0 +1,63 @@
1
+ import { z } from 'zod/v3';
2
+ export declare const projectSearchSchema: z.ZodObject<{
3
+ search: z.ZodDefault<z.ZodString>;
4
+ page: z.ZodDefault<z.ZodNumber>;
5
+ }, "strict", z.ZodTypeAny, {
6
+ search: string;
7
+ page: number;
8
+ }, {
9
+ search?: string | undefined;
10
+ page?: number | undefined;
11
+ }>;
12
+ export declare const dashboardSearchSchema: z.ZodObject<{
13
+ search: z.ZodDefault<z.ZodString>;
14
+ page: z.ZodDefault<z.ZodNumber>;
15
+ } & {
16
+ projectId: z.ZodOptional<z.ZodString>;
17
+ }, "strict", z.ZodTypeAny, {
18
+ search: string;
19
+ page: number;
20
+ projectId?: string | undefined;
21
+ }, {
22
+ search?: string | undefined;
23
+ page?: number | undefined;
24
+ projectId?: string | undefined;
25
+ }>;
26
+ export declare const dashboardProjectSchema: z.ZodObject<{
27
+ id: z.ZodString;
28
+ name: z.ZodString;
29
+ }, "strip", z.ZodTypeAny, {
30
+ id: string;
31
+ name: string;
32
+ }, {
33
+ id: string;
34
+ name: string;
35
+ }>;
36
+ export declare const dashboardProjectsSchema: z.ZodObject<{
37
+ items: z.ZodArray<z.ZodObject<{
38
+ id: z.ZodString;
39
+ name: z.ZodString;
40
+ }, "strip", z.ZodTypeAny, {
41
+ id: string;
42
+ name: string;
43
+ }, {
44
+ id: string;
45
+ name: string;
46
+ }>, "many">;
47
+ page: z.ZodNumber;
48
+ hasMore: z.ZodBoolean;
49
+ }, "strip", z.ZodTypeAny, {
50
+ page: number;
51
+ items: {
52
+ id: string;
53
+ name: string;
54
+ }[];
55
+ hasMore: boolean;
56
+ }, {
57
+ page: number;
58
+ items: {
59
+ id: string;
60
+ name: string;
61
+ }[];
62
+ hasMore: boolean;
63
+ }>;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.dashboardProjectsSchema = exports.dashboardProjectSchema = exports.dashboardSearchSchema = exports.projectSearchSchema = void 0;
4
+ const v3_1 = require("zod/v3");
5
+ exports.projectSearchSchema = v3_1.z.object({ search: v3_1.z.string().trim().max(200).default(''), page: v3_1.z.number().int().positive().default(1) }).strict();
6
+ exports.dashboardSearchSchema = exports.projectSearchSchema.extend({ projectId: v3_1.z.string().uuid().optional() });
7
+ exports.dashboardProjectSchema = v3_1.z.object({ id: v3_1.z.string().uuid(), name: v3_1.z.string() });
8
+ exports.dashboardProjectsSchema = v3_1.z.object({ items: v3_1.z.array(exports.dashboardProjectSchema), page: v3_1.z.number().int().positive(), hasMore: v3_1.z.boolean() });
@@ -0,0 +1,25 @@
1
+ import { z } from 'zod/v3';
2
+ import type { XpertResolvedViewHostContext } from '@xpert-ai/contracts';
3
+ import { type RuntimeCapabilityRegistry } from '@xpert-ai/plugin-sdk';
4
+ import { projectSearchSchema } from './projects.schema';
5
+ export { projectSearchSchema } from './projects.schema';
6
+ /** Selection uses the host's membership and Assistant binding policy, never a client-supplied data scope. */
7
+ export declare class DashboardProjectsService {
8
+ private readonly runtime;
9
+ constructor(runtime: RuntimeCapabilityRegistry);
10
+ private actor;
11
+ current(context: XpertResolvedViewHostContext): Promise<{
12
+ id: string;
13
+ name: string;
14
+ } | null>;
15
+ /** A picker target is untrusted until membership and the Assistant binding are revalidated. */
16
+ pickerContext(context: XpertResolvedViewHostContext, targetProjectId: string): Promise<XpertResolvedViewHostContext>;
17
+ list(context: XpertResolvedViewHostContext, input: z.infer<typeof projectSearchSchema>): Promise<{
18
+ items: {
19
+ id: string;
20
+ name: string;
21
+ }[];
22
+ page: number;
23
+ hasMore: boolean;
24
+ }>;
25
+ }