@xpert-ai/plugin-live-dashboard 0.3.0 → 0.4.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.
Files changed (153) hide show
  1. package/.xpertai-plugin/plugin.json +10 -2
  2. package/README.md +70 -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 +14 -1
  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 +2 -0
  16. package/dist/lib/assistant-template.js +7 -0
  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 +197 -46
  21. package/dist/lib/dashboard-view.schema.js +8 -1
  22. package/dist/lib/dashboard.service.d.ts +39 -2
  23. package/dist/lib/dashboard.service.js +52 -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 +48 -20
  39. package/dist/lib/monitor.d.ts +70 -0
  40. package/dist/lib/monitor.js +101 -0
  41. package/dist/lib/plugin.js +5 -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 +49 -0
  45. package/dist/lib/projects.schema.js +7 -0
  46. package/dist/lib/projects.service.d.ts +23 -0
  47. package/dist/lib/projects.service.js +65 -0
  48. package/dist/lib/prompt-workflows.d.ts +3 -2
  49. package/dist/lib/prompt-workflows.js +17 -3
  50. package/dist/lib/provider-options.controller.d.ts +10 -0
  51. package/dist/lib/provider-options.controller.js +49 -0
  52. package/dist/lib/resource-card.js +1 -1
  53. package/dist/lib/runtime-context.d.ts +3 -0
  54. package/dist/lib/runtime-context.js +13 -0
  55. package/dist/lib/save-evidence.d.ts +1 -1
  56. package/dist/lib/templates.d.ts +2 -0
  57. package/dist/lib/templates.js +55 -0
  58. package/dist/lib/view.provider.d.ts +97 -7
  59. package/dist/lib/view.provider.js +39 -10
  60. package/dist/libraries/TREND-WORKSPACE-ICONS-LICENSE.txt +43 -0
  61. package/dist/libraries/catalog.json +1 -1
  62. package/dist/remote/app.css +1 -1
  63. package/dist/remote/app.js +33 -33
  64. package/dist/remote/build-manifest.json +118 -32
  65. package/dist/sandbox-actions/action.json +1 -1
  66. package/dist/sandbox-actions/bundle/host.js +2 -2
  67. package/dist/sandbox-actions/bundle/resource-cache.mjs +2 -1
  68. package/dist/sandbox-actions/bundle/validate.mjs +1 -1
  69. package/docs/docs.json +7 -0
  70. package/docs/frontend-libraries.mdx +12 -4
  71. package/docs/html-mode.md +3 -3
  72. package/docs/quick-start-acceptance.mdx +50 -0
  73. package/docs/quick-start.mdx +64 -0
  74. package/docs/superstore.mdx +38 -0
  75. package/docs/team-sharing.mdx +6 -2
  76. package/docs/trend-workspace-acceptance.mdx +43 -0
  77. package/docs/trend-workspace.mdx +30 -0
  78. package/docs/verification.mdx +4 -2
  79. package/docs/visual-styles-acceptance.mdx +40 -0
  80. package/docs/visual-styles.mdx +46 -0
  81. package/package.json +25 -18
  82. package/scripts/import-prompts.mjs +108 -0
  83. package/skills/live-dashboard/SKILL.md +27 -3
  84. package/skills/live-dashboard/examples/README.md +25 -0
  85. package/skills/live-dashboard/examples/catalog.json +79 -0
  86. package/skills/live-dashboard/examples/production-quality/README.md +25 -0
  87. package/skills/live-dashboard/examples/production-quality/dashboard.html +129 -0
  88. package/skills/live-dashboard/examples/production-quality/definition.json +67 -0
  89. package/skills/live-dashboard/examples/production-quality/demo.csv +7 -0
  90. package/skills/live-dashboard/examples/production-quality/demo.json +44 -0
  91. package/skills/live-dashboard/examples/production-quality/expected.json +26 -0
  92. package/skills/live-dashboard/examples/production-quality/preview.png +0 -0
  93. package/skills/live-dashboard/examples/production-quality/query.json +53 -0
  94. package/skills/live-dashboard/examples/regional-sales/README.md +25 -0
  95. package/skills/live-dashboard/examples/regional-sales/dashboard-create.json +35 -0
  96. package/skills/live-dashboard/examples/regional-sales/definition.json +35 -0
  97. package/skills/live-dashboard/examples/regional-sales/demo.csv +5 -0
  98. package/skills/live-dashboard/examples/regional-sales/demo.json +18 -0
  99. package/skills/live-dashboard/examples/regional-sales/expected.json +19 -0
  100. package/skills/live-dashboard/examples/regional-sales/preview.png +0 -0
  101. package/skills/live-dashboard/examples/regional-sales/query.json +45 -0
  102. package/skills/live-dashboard/examples/sales-overview/README.md +25 -0
  103. package/skills/live-dashboard/examples/sales-overview/dashboard.html +129 -0
  104. package/skills/live-dashboard/examples/sales-overview/definition.json +57 -0
  105. package/skills/live-dashboard/examples/sales-overview/demo.csv +7 -0
  106. package/skills/live-dashboard/examples/sales-overview/demo.json +38 -0
  107. package/skills/live-dashboard/examples/sales-overview/expected.json +22 -0
  108. package/skills/live-dashboard/examples/sales-overview/preview.png +0 -0
  109. package/skills/live-dashboard/examples/sales-overview/query.json +52 -0
  110. package/skills/live-dashboard/examples/superstore/README.md +64 -0
  111. package/skills/live-dashboard/examples/superstore/THIRD_PARTY_LICENSE.txt +207 -0
  112. package/skills/live-dashboard/examples/superstore/build_data.py +155 -0
  113. package/skills/live-dashboard/examples/superstore/build_pages.py +129 -0
  114. package/skills/live-dashboard/examples/superstore/checks.json +104 -0
  115. package/skills/live-dashboard/examples/superstore/customers-mobile-dark.png +0 -0
  116. package/skills/live-dashboard/examples/superstore/customers-select.png +0 -0
  117. package/skills/live-dashboard/examples/superstore/customers.html +316 -0
  118. package/skills/live-dashboard/examples/superstore/customers.png +0 -0
  119. package/skills/live-dashboard/examples/superstore/customers.queries.json +512 -0
  120. package/skills/live-dashboard/examples/superstore/expected.json +4069 -0
  121. package/skills/live-dashboard/examples/superstore/import.json +200 -0
  122. package/skills/live-dashboard/examples/superstore/orders.csv +10195 -0
  123. package/skills/live-dashboard/examples/superstore/overview-mobile-dark.png +0 -0
  124. package/skills/live-dashboard/examples/superstore/overview-select.png +0 -0
  125. package/skills/live-dashboard/examples/superstore/overview.html +310 -0
  126. package/skills/live-dashboard/examples/superstore/overview.png +0 -0
  127. package/skills/live-dashboard/examples/superstore/overview.queries.json +636 -0
  128. package/skills/live-dashboard/examples/superstore/page.js +109 -0
  129. package/skills/live-dashboard/examples/superstore/profitability-mobile-dark.png +0 -0
  130. package/skills/live-dashboard/examples/superstore/profitability-select.png +0 -0
  131. package/skills/live-dashboard/examples/superstore/profitability.html +307 -0
  132. package/skills/live-dashboard/examples/superstore/profitability.png +0 -0
  133. package/skills/live-dashboard/examples/superstore/profitability.queries.json +334 -0
  134. package/skills/live-dashboard/examples/superstore/provenance.json +24 -0
  135. package/skills/live-dashboard/examples/superstore/semantic-model.json +220 -0
  136. package/skills/live-dashboard/examples/superstore/test_data.py +62 -0
  137. package/skills/live-dashboard/references/html-runtime.md +1 -1
  138. package/skills/live-dashboard/themes/README.md +50 -0
  139. package/skills/live-dashboard/themes/customer-brief/README.md +47 -0
  140. package/skills/live-dashboard/themes/customer-brief/charts.js +17 -0
  141. package/skills/live-dashboard/themes/customer-brief/layout.html +22 -0
  142. package/skills/live-dashboard/themes/customer-brief/theme.css +108 -0
  143. package/skills/live-dashboard/themes/profit-atlas/README.md +43 -0
  144. package/skills/live-dashboard/themes/profit-atlas/charts.js +30 -0
  145. package/skills/live-dashboard/themes/profit-atlas/layout.html +19 -0
  146. package/skills/live-dashboard/themes/profit-atlas/theme.css +75 -0
  147. package/skills/live-dashboard/themes/trend-workspace/ICON_LICENSE.txt +43 -0
  148. package/skills/live-dashboard/themes/trend-workspace/README.md +144 -0
  149. package/skills/live-dashboard/themes/trend-workspace/charts.js +60 -0
  150. package/skills/live-dashboard/themes/trend-workspace/icons.js +11 -0
  151. package/skills/live-dashboard/themes/trend-workspace/layout.html +45 -0
  152. package/skills/live-dashboard/themes/trend-workspace/select.js +127 -0
  153. package/skills/live-dashboard/themes/trend-workspace/theme.css +137 -0
@@ -3,56 +3,9 @@ import type { AnalyticsBinding, XpertResolvedViewHostContext } from '@xpert-ai/c
3
3
  import { type IAgentMiddlewareContext } from '@xpert-ai/plugin-sdk';
4
4
  import { type DashboardManifest } from './dashboard.schema';
5
5
  import { DashboardService } from './dashboard.service';
6
- export declare const dashboardSelectionSchema: z.ZodObject<{
7
- schemaVersion: z.ZodLiteral<1>;
8
- artifactId: z.ZodString;
9
- versionId: z.ZodOptional<z.ZodString>;
10
- draftRevision: z.ZodOptional<z.ZodNumber>;
11
- }, "strict", z.ZodTypeAny, {
12
- artifactId: string;
13
- schemaVersion: 1;
14
- versionId?: string | undefined;
15
- draftRevision?: number | undefined;
16
- }, {
17
- artifactId: string;
18
- schemaVersion: 1;
19
- versionId?: string | undefined;
20
- draftRevision?: number | undefined;
21
- }>;
22
- export declare const dashboardContextSchema: z.ZodObject<{
23
- live_dashboard: z.ZodOptional<z.ZodNullable<z.ZodObject<{
24
- schemaVersion: z.ZodLiteral<1>;
25
- artifactId: z.ZodString;
26
- versionId: z.ZodOptional<z.ZodString>;
27
- draftRevision: z.ZodOptional<z.ZodNumber>;
28
- }, "strict", z.ZodTypeAny, {
29
- artifactId: string;
30
- schemaVersion: 1;
31
- versionId?: string | undefined;
32
- draftRevision?: number | undefined;
33
- }, {
34
- artifactId: string;
35
- schemaVersion: 1;
36
- versionId?: string | undefined;
37
- draftRevision?: number | undefined;
38
- }>>>;
39
- }, "strip", z.ZodTypeAny, {
40
- live_dashboard?: {
41
- artifactId: string;
42
- schemaVersion: 1;
43
- versionId?: string | undefined;
44
- draftRevision?: number | undefined;
45
- } | null | undefined;
46
- }, {
47
- live_dashboard?: {
48
- artifactId: string;
49
- schemaVersion: 1;
50
- versionId?: string | undefined;
51
- draftRevision?: number | undefined;
52
- } | null | undefined;
53
- }>;
6
+ import { type DashboardSelection } from './editing-session';
7
+ export { dashboardSelectionSchema, dashboardContextSchema } from './editing-session';
54
8
  export declare const updateToolSchema: z.ZodObject<{
55
- supersedesPaths: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
56
9
  controls: z.ZodOptional<z.ZodEffects<z.ZodArray<z.ZodObject<{
57
10
  controlId: z.ZodString;
58
11
  type: z.ZodEnum<["string", "number", "boolean", "date"]>;
@@ -133,14 +86,31 @@ export declare const updateToolSchema: z.ZodObject<{
133
86
  datasetId: string;
134
87
  fields: string[];
135
88
  }[], unknown>>;
89
+ supersedesPaths: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
136
90
  previewIds: z.ZodOptional<z.ZodEffects<z.ZodArray<z.ZodString, "many">, string[], unknown>>;
137
91
  } & {
92
+ removals: z.ZodOptional<z.ZodObject<{
93
+ bindingIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
94
+ controlIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
95
+ viewIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
96
+ htmlElementIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
97
+ }, "strict", z.ZodTypeAny, {
98
+ bindingIds: string[];
99
+ controlIds: string[];
100
+ viewIds: string[];
101
+ htmlElementIds: string[];
102
+ }, {
103
+ bindingIds?: string[] | undefined;
104
+ controlIds?: string[] | undefined;
105
+ viewIds?: string[] | undefined;
106
+ htmlElementIds?: string[] | undefined;
107
+ }>>;
138
108
  title: z.ZodString;
139
109
  editId: z.ZodString;
140
110
  expectedRevision: z.ZodNumber;
141
111
  expectedCurrentVersionId: z.ZodNullable<z.ZodString>;
142
112
  htmlPath: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>>;
143
- libraries: z.ZodOptional<z.ZodEffects<z.ZodArray<z.ZodEnum<["echarts", "three"]>, "many">, ("echarts" | "three")[], ("echarts" | "three")[]>>;
113
+ libraries: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodArray<z.ZodEnum<["echarts", "three", "trend-workspace", "profit-atlas", "customer-brief"]>, "many">, ("echarts" | "three" | "trend-workspace" | "profit-atlas" | "customer-brief")[], ("echarts" | "three" | "trend-workspace" | "profit-atlas" | "customer-brief")[]>, ("echarts" | "three" | "trend-workspace" | "profit-atlas" | "customer-brief")[], ("echarts" | "three" | "trend-workspace" | "profit-atlas" | "customer-brief")[]>>;
144
114
  }, "strict", z.ZodTypeAny, {
145
115
  title: string;
146
116
  expectedRevision: number;
@@ -170,7 +140,13 @@ export declare const updateToolSchema: z.ZodObject<{
170
140
  }[] | undefined;
171
141
  supersedesPaths?: string[] | undefined;
172
142
  previewIds?: string[] | undefined;
173
- libraries?: ("echarts" | "three")[] | undefined;
143
+ libraries?: ("echarts" | "three" | "trend-workspace" | "profit-atlas" | "customer-brief")[] | undefined;
144
+ removals?: {
145
+ bindingIds: string[];
146
+ controlIds: string[];
147
+ viewIds: string[];
148
+ htmlElementIds: string[];
149
+ } | undefined;
174
150
  htmlPath?: string | undefined;
175
151
  }, {
176
152
  title: string;
@@ -182,12 +158,28 @@ export declare const updateToolSchema: z.ZodObject<{
182
158
  tables?: unknown;
183
159
  supersedesPaths?: string[] | undefined;
184
160
  previewIds?: unknown;
185
- libraries?: ("echarts" | "three")[] | undefined;
161
+ libraries?: ("echarts" | "three" | "trend-workspace" | "profit-atlas" | "customer-brief")[] | undefined;
162
+ removals?: {
163
+ bindingIds?: string[] | undefined;
164
+ controlIds?: string[] | undefined;
165
+ viewIds?: string[] | undefined;
166
+ htmlElementIds?: string[] | undefined;
167
+ } | undefined;
186
168
  htmlPath?: string | undefined;
187
169
  }>;
188
- export declare const EDITING_GUIDANCE = "Opening or reading a dashboard does not modify it. Do not save an unchanged editing copy or reopen/update merely to confirm a successful save; use the mutation receipt. For existing dashboards, call dashboard_open first and inspect presentation.type. For standard dashboards, update definition fields directly: a rename needs only title, editId, expectedRevision and expectedCurrentVersionId. No HTML file exists; omit htmlPath and libraries. For HTML dashboards only, open prepares a unique editing copy at htmlPath. Read with sandbox_read_file; edit with sandbox_edit_file/multi_edit_file (sandbox_write_file is create-only), then call dashboard_update with that exact htmlPath and read revisions. If the user requests a rename, include the new title in this same update; editing the HTML heading alone does not rename the Artifact. Save each batch automatically without human confirmation. Files are not dashboard saves. Preserve Binding semantics and dependency locks unless requested. On conflict retain the editing file, open the latest draft and reconcile; never silently overwrite. Historical versions are read-only references. All Agent saves are drafts; only the Workbench human publish action creates formal versions. Read the live-dashboard skill for the complete workflow.";
170
+ export declare const EDITING_GUIDANCE = "Opening or reading a dashboard does not modify it. A selection_required result is normal: show its message to the user and wait for an explicit selection; do not retry, infer a target from history, or create a replacement. Do not save an unchanged editing copy or reopen/update merely to confirm a successful save; use the mutation receipt. For existing dashboards, call dashboard_open first and inspect presentation.type. For standard dashboards, update definition fields directly: a rename needs only title, editId, expectedRevision and expectedCurrentVersionId. No HTML file exists; omit htmlPath and libraries. For HTML dashboards only, open prepares a unique editing copy at htmlPath. Read with sandbox_read_file. If a long HTML line is truncated, do not repeat the same read: use dashboard_read_html with editId and follow nextOffset until null to inspect the saved source, then apply exact replacements to htmlPath. Never replace a file with a truncated preview. Edit with sandbox_edit_file/multi_edit_file (sandbox_write_file is create-only), then call dashboard_update with that exact htmlPath and read revisions. If the user requests a rename, include the new title in this same update; editing the HTML heading alone does not rename the Artifact. Save each batch automatically without human confirmation. Files are not dashboard saves. Preserve Binding semantics, stable component IDs and dependency locks unless requested. Update arrays upsert by ID, preserving omitted components; removals must name explicitly requested deletions. HTML updates retain existing element IDs. For new HTML, assign stable IDs to every KPI, chart, detail table and filter. Read and patch the current file instead of rebuilding unrelated sections. On conflict retain the editing file, open the latest draft and reconcile; never silently overwrite. Historical versions are read-only references. All Agent saves are drafts; only the Workbench human publish action creates formal versions. Read the live-dashboard skill for the complete workflow.";
189
171
  /** Run-local read receipts carry planning versions; authorization and CAS remain in the domain service. */
190
- export declare function createEditingTools(service: DashboardService, scope: XpertResolvedViewHostContext, context: IAgentMiddlewareContext, resolvePreviews: (ids: string[]) => AnalyticsBinding[], validateFinal: (manifest: DashboardManifest, requirePreviews: boolean) => void, onOpen?: (path: string, artifactId: string) => void): (import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{}, "strict", z.ZodTypeAny, {}, {}>, {}, {}, string> | import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{
172
+ export declare function createEditingTools(service: DashboardService, scope: XpertResolvedViewHostContext, context: IAgentMiddlewareContext, resolvePreviews: (ids: string[]) => AnalyticsBinding[], validateFinal: (manifest: DashboardManifest, requirePreviews: boolean, changedBindingIds?: string[]) => void, onOpen?: (path: string, artifactId: string) => void, currentTarget?: (config: unknown) => DashboardSelection | null | undefined): (import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{
173
+ page: z.ZodDefault<z.ZodNumber>;
174
+ }, "strict", z.ZodTypeAny, {
175
+ page: number;
176
+ }, {
177
+ page?: number | undefined;
178
+ }>, {
179
+ page: number;
180
+ }, {
181
+ page?: number | undefined;
182
+ }, string> | import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{
191
183
  editId: z.ZodString;
192
184
  versionId: z.ZodOptional<z.ZodString>;
193
185
  offset: z.ZodDefault<z.ZodNumber>;
@@ -225,7 +217,6 @@ export declare function createEditingTools(service: DashboardService, scope: Xpe
225
217
  }, {
226
218
  artifactId?: string | undefined;
227
219
  }, string> | import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{
228
- supersedesPaths: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
229
220
  controls: z.ZodOptional<z.ZodEffects<z.ZodArray<z.ZodObject<{
230
221
  controlId: z.ZodString;
231
222
  type: z.ZodEnum<["string", "number", "boolean", "date"]>;
@@ -306,14 +297,31 @@ export declare function createEditingTools(service: DashboardService, scope: Xpe
306
297
  datasetId: string;
307
298
  fields: string[];
308
299
  }[], unknown>>;
300
+ supersedesPaths: z.ZodOptional<z.ZodOptional<z.ZodArray<z.ZodEffects<z.ZodString, string, string>, "many">>>;
309
301
  previewIds: z.ZodOptional<z.ZodEffects<z.ZodArray<z.ZodString, "many">, string[], unknown>>;
310
302
  } & {
303
+ removals: z.ZodOptional<z.ZodObject<{
304
+ bindingIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
305
+ controlIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
306
+ viewIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
307
+ htmlElementIds: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
308
+ }, "strict", z.ZodTypeAny, {
309
+ bindingIds: string[];
310
+ controlIds: string[];
311
+ viewIds: string[];
312
+ htmlElementIds: string[];
313
+ }, {
314
+ bindingIds?: string[] | undefined;
315
+ controlIds?: string[] | undefined;
316
+ viewIds?: string[] | undefined;
317
+ htmlElementIds?: string[] | undefined;
318
+ }>>;
311
319
  title: z.ZodString;
312
320
  editId: z.ZodString;
313
321
  expectedRevision: z.ZodNumber;
314
322
  expectedCurrentVersionId: z.ZodNullable<z.ZodString>;
315
323
  htmlPath: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>>;
316
- libraries: z.ZodOptional<z.ZodEffects<z.ZodArray<z.ZodEnum<["echarts", "three"]>, "many">, ("echarts" | "three")[], ("echarts" | "three")[]>>;
324
+ libraries: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodArray<z.ZodEnum<["echarts", "three", "trend-workspace", "profit-atlas", "customer-brief"]>, "many">, ("echarts" | "three" | "trend-workspace" | "profit-atlas" | "customer-brief")[], ("echarts" | "three" | "trend-workspace" | "profit-atlas" | "customer-brief")[]>, ("echarts" | "three" | "trend-workspace" | "profit-atlas" | "customer-brief")[], ("echarts" | "three" | "trend-workspace" | "profit-atlas" | "customer-brief")[]>>;
317
325
  }, "strict", z.ZodTypeAny, {
318
326
  title: string;
319
327
  expectedRevision: number;
@@ -343,7 +351,13 @@ export declare function createEditingTools(service: DashboardService, scope: Xpe
343
351
  }[] | undefined;
344
352
  supersedesPaths?: string[] | undefined;
345
353
  previewIds?: string[] | undefined;
346
- libraries?: ("echarts" | "three")[] | undefined;
354
+ libraries?: ("echarts" | "three" | "trend-workspace" | "profit-atlas" | "customer-brief")[] | undefined;
355
+ removals?: {
356
+ bindingIds: string[];
357
+ controlIds: string[];
358
+ viewIds: string[];
359
+ htmlElementIds: string[];
360
+ } | undefined;
347
361
  htmlPath?: string | undefined;
348
362
  }, {
349
363
  title: string;
@@ -355,7 +369,13 @@ export declare function createEditingTools(service: DashboardService, scope: Xpe
355
369
  tables?: unknown;
356
370
  supersedesPaths?: string[] | undefined;
357
371
  previewIds?: unknown;
358
- libraries?: ("echarts" | "three")[] | undefined;
372
+ libraries?: ("echarts" | "three" | "trend-workspace" | "profit-atlas" | "customer-brief")[] | undefined;
373
+ removals?: {
374
+ bindingIds?: string[] | undefined;
375
+ controlIds?: string[] | undefined;
376
+ viewIds?: string[] | undefined;
377
+ htmlElementIds?: string[] | undefined;
378
+ } | undefined;
359
379
  htmlPath?: string | undefined;
360
380
  }>, {
361
381
  title: string;
@@ -386,7 +406,13 @@ export declare function createEditingTools(service: DashboardService, scope: Xpe
386
406
  }[] | undefined;
387
407
  supersedesPaths?: string[] | undefined;
388
408
  previewIds?: string[] | undefined;
389
- libraries?: ("echarts" | "three")[] | undefined;
409
+ libraries?: ("echarts" | "three" | "trend-workspace" | "profit-atlas" | "customer-brief")[] | undefined;
410
+ removals?: {
411
+ bindingIds: string[];
412
+ controlIds: string[];
413
+ viewIds: string[];
414
+ htmlElementIds: string[];
415
+ } | undefined;
390
416
  htmlPath?: string | undefined;
391
417
  }, {
392
418
  title: string;
@@ -398,6 +424,12 @@ export declare function createEditingTools(service: DashboardService, scope: Xpe
398
424
  tables?: unknown;
399
425
  supersedesPaths?: string[] | undefined;
400
426
  previewIds?: unknown;
401
- libraries?: ("echarts" | "three")[] | undefined;
427
+ libraries?: ("echarts" | "three" | "trend-workspace" | "profit-atlas" | "customer-brief")[] | undefined;
428
+ removals?: {
429
+ bindingIds?: string[] | undefined;
430
+ controlIds?: string[] | undefined;
431
+ viewIds?: string[] | undefined;
432
+ htmlElementIds?: string[] | undefined;
433
+ } | undefined;
402
434
  htmlPath?: string | undefined;
403
435
  }, string>)[];
@@ -11,36 +11,39 @@ const agent_schema_1 = require("./agent.schema");
11
11
  const dashboard_schema_1 = require("./dashboard.schema");
12
12
  const document_libraries_1 = require("./document-libraries");
13
13
  const resource_card_1 = require("./resource-card");
14
- exports.dashboardSelectionSchema = v3_1.z.object({ schemaVersion: v3_1.z.literal(1), artifactId: v3_1.z.string().uuid(), versionId: v3_1.z.string().uuid().optional(), draftRevision: v3_1.z.number().int().positive().optional() }).strict();
15
- exports.dashboardContextSchema = v3_1.z.object({ live_dashboard: exports.dashboardSelectionSchema.nullable().optional() });
16
- const configSchema = v3_1.z.object({ context: exports.dashboardContextSchema.optional(), configurable: v3_1.z.object({ context: exports.dashboardContextSchema.optional() }).optional() });
14
+ const editing_session_1 = require("./editing-session");
15
+ const editing_patch_1 = require("./editing-patch");
16
+ const editing_feedback_1 = require("./editing-feedback");
17
+ var editing_session_2 = require("./editing-session");
18
+ Object.defineProperty(exports, "dashboardSelectionSchema", { enumerable: true, get: function () { return editing_session_2.dashboardSelectionSchema; } });
19
+ Object.defineProperty(exports, "dashboardContextSchema", { enumerable: true, get: function () { return editing_session_2.dashboardContextSchema; } });
17
20
  const targetSchema = v3_1.z.object({ artifactId: v3_1.z.string().uuid().optional().describe('From dashboard_list or a saved receipt. Omit only for the current Workbench selection.'),
18
21
  versionId: v3_1.z.string().uuid().optional().describe('Exact historical version when requested. Otherwise open the selected or current version.') }).strict();
19
- exports.updateToolSchema = agent_schema_1.createToolSchema.partial().extend({ title: agent_schema_1.createToolSchema.shape.title.describe('Required saved Artifact title. Use the requested new title when renaming; otherwise copy the current title from dashboard_open. Submit it together with htmlPath when changing the page.'), editId: v3_1.z.string().uuid().describe("Stable Artifact ID returned by dashboard_open"), expectedRevision: v3_1.z.number().int().nonnegative().describe('Copy the current draft revision returned by dashboard_open.'), expectedCurrentVersionId: v3_1.z.string().uuid({ message: 'Copy the exact UUID from dashboard_open, or use JSON null when no formal version exists. Never use the strings None, null or an empty string.' }).nullable().describe('Copy dashboard_open.expectedCurrentVersionId exactly: UUID or actual JSON null. Never the strings None/null. Keep all requested changes (including title) when correcting another field.'), htmlPath: agent_schema_1.htmlPathSchema.optional(),
22
+ exports.updateToolSchema = agent_schema_1.createToolSchema.omit({ creationIntent: true }).partial().extend({ removals: editing_patch_1.removalsSchema, title: agent_schema_1.createToolSchema.shape.title.describe('Required saved Artifact title. Use the requested new title when renaming; otherwise copy the current title from dashboard_open. Submit it together with htmlPath when changing the page.'), editId: v3_1.z.string().uuid().describe("Stable Artifact ID returned by dashboard_open"), expectedRevision: v3_1.z.number().int().nonnegative().describe('Copy the current draft revision returned by dashboard_open.'), expectedCurrentVersionId: v3_1.z.string().uuid({ message: 'Copy the exact UUID from dashboard_open, or use JSON null when no formal version exists. Never use the strings None, null or an empty string.' }).nullable().describe('Copy dashboard_open.expectedCurrentVersionId exactly: UUID or actual JSON null. Never the strings None/null. Keep all requested changes (including title) when correcting another field.'), htmlPath: agent_schema_1.htmlPathSchema.optional(),
20
23
  libraries: document_libraries_1.libraryRequestsSchema.optional().describe('Omit to preserve the saved dependency versions. Supply the complete desired library IDs to explicitly add, remove or upgrade bundled libraries.') }).strict();
21
24
  const readHtmlSchema = v3_1.z.object({ editId: v3_1.z.string().uuid(), versionId: v3_1.z.string().uuid().optional().describe('Only for historical inspection; copy readVersionId from dashboard_open.'), offset: v3_1.z.number().int().min(0).max(300000).default(0),
22
25
  length: v3_1.z.number().int().min(1).max(24000).default(16000) }).strict();
23
- exports.EDITING_GUIDANCE = `Opening or reading a dashboard does not modify it. Do not save an unchanged editing copy or reopen/update merely to confirm a successful save; use the mutation receipt. For existing dashboards, call dashboard_open first and inspect presentation.type. For standard dashboards, update definition fields directly: a rename needs only title, editId, expectedRevision and expectedCurrentVersionId. No HTML file exists; omit htmlPath and libraries. For HTML dashboards only, open prepares a unique editing copy at htmlPath. Read with sandbox_read_file; edit with sandbox_edit_file/multi_edit_file (sandbox_write_file is create-only), then call dashboard_update with that exact htmlPath and read revisions. If the user requests a rename, include the new title in this same update; editing the HTML heading alone does not rename the Artifact. Save each batch automatically without human confirmation. Files are not dashboard saves. Preserve Binding semantics and dependency locks unless requested. On conflict retain the editing file, open the latest draft and reconcile; never silently overwrite. Historical versions are read-only references. All Agent saves are drafts; only the Workbench human publish action creates formal versions. Read the live-dashboard skill for the complete workflow.`;
26
+ exports.EDITING_GUIDANCE = `Opening or reading a dashboard does not modify it. A selection_required result is normal: show its message to the user and wait for an explicit selection; do not retry, infer a target from history, or create a replacement. Do not save an unchanged editing copy or reopen/update merely to confirm a successful save; use the mutation receipt. For existing dashboards, call dashboard_open first and inspect presentation.type. For standard dashboards, update definition fields directly: a rename needs only title, editId, expectedRevision and expectedCurrentVersionId. No HTML file exists; omit htmlPath and libraries. For HTML dashboards only, open prepares a unique editing copy at htmlPath. Read with sandbox_read_file. If a long HTML line is truncated, do not repeat the same read: use dashboard_read_html with editId and follow nextOffset until null to inspect the saved source, then apply exact replacements to htmlPath. Never replace a file with a truncated preview. Edit with sandbox_edit_file/multi_edit_file (sandbox_write_file is create-only), then call dashboard_update with that exact htmlPath and read revisions. If the user requests a rename, include the new title in this same update; editing the HTML heading alone does not rename the Artifact. Save each batch automatically without human confirmation. Files are not dashboard saves. Preserve Binding semantics, stable component IDs and dependency locks unless requested. Update arrays upsert by ID, preserving omitted components; removals must name explicitly requested deletions. HTML updates retain existing element IDs. For new HTML, assign stable IDs to every KPI, chart, detail table and filter. Read and patch the current file instead of rebuilding unrelated sections. On conflict retain the editing file, open the latest draft and reconcile; never silently overwrite. Historical versions are read-only references. All Agent saves are drafts; only the Workbench human publish action creates formal versions. Read the live-dashboard skill for the complete workflow.`;
24
27
  /** Run-local read receipts carry planning versions; authorization and CAS remain in the domain service. */
25
- function createEditingTools(service, scope, context, resolvePreviews, validateFinal, onOpen = () => undefined) {
28
+ function createEditingTools(service, scope, context, resolvePreviews, validateFinal, onOpen = () => undefined, currentTarget = editing_session_1.selectionFromConfig) {
26
29
  const target = (input, config) => {
27
- const parsed = configSchema.safeParse(config);
28
- // LangGraph can provide a top-level execution context alongside the request context.
29
- // An unrelated (or schema-stripped empty) execution context must not mask the selection.
30
- const selected = parsed.success ? parsed.data.context?.live_dashboard !== undefined
31
- ? parsed.data.context.live_dashboard : parsed.data.configurable?.context?.live_dashboard : undefined;
30
+ const selected = currentTarget(config);
32
31
  if (input.artifactId)
33
32
  return { artifactId: input.artifactId, versionId: input.versionId };
34
33
  if (!selected)
35
- throw new Error('DASHBOARD_TARGET_REQUIRED: select a dashboard in Workbench or use dashboard_list and provide its artifactId');
36
- return { artifactId: selected.artifactId, versionId: input.versionId };
34
+ return null;
35
+ return { artifactId: selected.artifactId,
36
+ versionId: input.versionId ?? (selected.draftRevision === undefined ? selected.versionId : undefined) };
37
37
  };
38
38
  return [
39
- (0, tools_1.tool)(async () => JSON.stringify({ dashboards: await service.list(scope) }), { name: 'dashboard_list',
40
- description: 'List saved dashboards in the current authorized host. Use returned IDs to open one; do not infer a target from recency.',
41
- schema: v3_1.z.object({}).strict(), verboseParsingErrors: true, metadata: { toolName: { en_US: 'List saved dashboards', zh_Hans: '查找已保存看板' } } }),
39
+ (0, tools_1.tool)(async ({ page }) => { const result = await service.listPage(scope, page, 50); return JSON.stringify({ dashboards: result.items, page: result.page, hasMore: result.hasMore }); }, { name: 'dashboard_list',
40
+ description: 'List saved dashboards in the current authorized host. Follow page and hasMore for the full list. Use returned IDs to open one; do not infer a target from recency.',
41
+ schema: v3_1.z.object({ page: v3_1.z.number().int().positive().default(1) }).strict(), verboseParsingErrors: true, metadata: { toolName: { en_US: 'List saved dashboards', zh_Hans: '查找已保存看板' } } }),
42
42
  (0, tools_1.tool)(async (input, config) => {
43
- const resolved = target(input, config), saved = await service.open(scope, resolved.artifactId, resolved.versionId);
43
+ const resolved = target(input, config);
44
+ if (!resolved)
45
+ return JSON.stringify((0, editing_feedback_1.dashboardSelectionRequired)(config));
46
+ const saved = await service.open(scope, resolved.artifactId, resolved.versionId);
44
47
  const editId = saved.artifactId;
45
48
  const { presentation, schema, id, ...definition } = saved.manifest;
46
49
  let htmlPath;
@@ -58,7 +61,7 @@ function createEditingTools(service, scope, context, resolvePreviews, validateFi
58
61
  : { state: 'published', versionNumber: saved.versionNumber ?? 0, versionId: resolved.versionId }) }, config);
59
62
  return JSON.stringify({ resourceCard, editId, expectedCurrentVersionId: saved.baseVersionId ?? saved.versionId ?? null, ...(resolved.versionId ? { readVersionId: resolved.versionId } : {}), expectedRevision: saved.draftRevision ?? 0, state: saved.isDraft ? 'draft' : 'published', artifactId: saved.artifactId, versionId: saved.versionId, versionNumber: saved.versionNumber,
60
63
  ...definition, presentation: presentation ? { type: 'html', characters: presentation.html.length, dependencies: presentation.dependencies ?? [] } : { type: 'standard' },
61
- ...(htmlPath ? { htmlPath, saveArguments: { editId, expectedRevision: saved.draftRevision ?? 0, expectedCurrentVersionId: saved.baseVersionId ?? saved.versionId ?? null, title: saved.manifest.title, htmlPath }, editingFile: { path: htmlPath, exists: true, hasUnsavedChanges: false, writeMode: 'edit_existing', read: { tool: 'sandbox_read_file', arguments: { file_path: htmlPath } } }, next: 'Opening only prepared a copy; it did not change the dashboard and requires no save. Call dashboard_update only after applying requested changes. Copy saveArguments, replacing title with the requested new name when renaming. expectedCurrentVersionId must remain actual JSON null or the returned UUID; never a string such as None. When repairing a failed update, retain all intended changes instead of dropping title. The editing file already exists: do not use sandbox_write_file on it. When renaming, include title and htmlPath together in one dashboard_update. A private editing copy is ready. Read it with sandbox_read_file, edit with sandbox_edit_file/multi_edit_file, then save using dashboard_update with this htmlPath and the returned revisions. File edits alone do not save the dashboard.' } : !presentation ? { next: 'Standard dashboard: update definition fields directly. A rename requires only title plus editId, expectedRevision and expectedCurrentVersionId. No HTML editing file exists; omit htmlPath and libraries unless intentionally converting to HTML.' } : {}) });
64
+ ...(resolved.versionId ? { next: 'Read-only published version. To edit, select the working draft in the Workbench version selector or explicitly open this Artifact without a versionId, then use its current draft revision. Publication requires a separate human action.' } : htmlPath ? { htmlPath, saveArguments: { editId, expectedRevision: saved.draftRevision ?? 0, expectedCurrentVersionId: saved.baseVersionId ?? saved.versionId ?? null, title: saved.manifest.title, htmlPath }, editingFile: { path: htmlPath, exists: true, hasUnsavedChanges: false, writeMode: 'edit_existing', read: { tool: 'sandbox_read_file', arguments: { file_path: htmlPath } } }, next: 'Opening only prepared a copy; it did not change the dashboard and requires no save. Call dashboard_update only after applying requested changes. Copy saveArguments, replacing title with the requested new name when renaming. expectedCurrentVersionId must remain actual JSON null or the returned UUID; never a string such as None. When repairing a failed update, retain all intended changes instead of dropping title. The editing file already exists: do not use sandbox_write_file on it. When renaming, include title and htmlPath together in one dashboard_update. A private editing copy is ready. Read it with sandbox_read_file. If long lines are truncated, use dashboard_read_html with editId and follow nextOffset until null instead of retrying file reads. This fallback reads the saved source; apply changes to htmlPath and never write a truncated preview. Edit with sandbox_edit_file/multi_edit_file, then save using dashboard_update with this htmlPath and the returned revisions. File edits alone do not save the dashboard.' } : !presentation ? { next: 'Standard dashboard: update definition fields directly. A rename requires only title plus editId, expectedRevision and expectedCurrentVersionId. No HTML editing file exists; omit htmlPath and libraries unless intentionally converting to HTML.' } : {}) });
62
65
  }, { name: 'dashboard_open', description: 'Read one authorized saved dashboard and obtain its stable editId and current draft revision. For a current HTML dashboard also prepares a unique Workspace editing copy without changing the Artifact. Historical versions remain read-only. Omit artifactId only for the selected Workbench dashboard.',
63
66
  schema: targetSchema, verboseParsingErrors: true, metadata: { toolName: { en_US: 'Read dashboard for editing', zh_Hans: '读取看板以继续修改' } } }),
64
67
  (0, tools_1.tool)(async (input) => {
@@ -75,13 +78,21 @@ function createEditingTools(service, scope, context, resolvePreviews, validateFi
75
78
  schema: readHtmlSchema, verboseParsingErrors: true, metadata: { toolName: { en_US: 'Read dashboard HTML', zh_Hans: '读取看板页面源码' } } }),
76
79
  (0, tools_1.tool)(async (input, config) => {
77
80
  const resolved = target(input, config);
81
+ if (!resolved)
82
+ return JSON.stringify((0, editing_feedback_1.dashboardSelectionRequired)(config));
78
83
  return JSON.stringify({ artifactId: resolved.artifactId, versions: await service.history(scope, resolved.artifactId) });
79
84
  }, { name: 'dashboard_history', description: 'List immutable versions of an authorized dashboard. Open a returned version to inspect it; history does not change the current version.',
80
85
  schema: targetSchema.omit({ versionId: true }), verboseParsingErrors: true, metadata: { toolName: { en_US: 'Read dashboard history', zh_Hans: '查看看板版本历史' } } }),
81
86
  (0, tools_1.tool)(async (input, config) => {
82
87
  const saved = await service.open(scope, input.editId);
83
- const { editId, expectedRevision, expectedCurrentVersionId, htmlPath, previewIds, libraries, ...changes } = input;
84
- const bindings = previewIds ? resolvePreviews(previewIds) : saved.manifest.bindings;
88
+ const { editId, expectedRevision, expectedCurrentVersionId, htmlPath, previewIds, libraries, removals, ...changes } = input;
89
+ const changedBindings = previewIds ? resolvePreviews(previewIds) : undefined;
90
+ const bindings = (0, editing_patch_1.upsertComponents)(saved.manifest.bindings, changedBindings, item => item.id, removals?.bindingIds);
91
+ const controls = (0, editing_patch_1.upsertComponents)(saved.manifest.controls, changes.controls, item => item.controlId, removals?.controlIds);
92
+ if (removals?.viewIds.some(id => ![...saved.manifest.charts, ...saved.manifest.tables].some(view => view.id === id)))
93
+ throw new Error('DASHBOARD_REMOVAL_INVALID: copy existing view IDs');
94
+ const charts = (0, editing_patch_1.upsertComponents)(saved.manifest.charts, changes.charts, item => item.id, removals?.viewIds.filter(id => saved.manifest.charts.some(item => item.id === id)));
95
+ const tables = (0, editing_patch_1.upsertComponents)(saved.manifest.tables, changes.tables, item => item.id, removals?.viewIds.filter(id => saved.manifest.tables.some(item => item.id === id)));
85
96
  let presentation = saved.manifest.presentation;
86
97
  let sourceFile;
87
98
  if (htmlPath) {
@@ -101,14 +112,15 @@ function createEditingTools(service, scope, context, resolvePreviews, validateFi
101
112
  throw new Error('DASHBOARD_HTML_REQUIRED');
102
113
  presentation = { ...presentation, dependencies: await (0, document_libraries_1.libraryLocks)(libraries) };
103
114
  }
104
- const manifest = dashboard_schema_1.dashboardSchema.parse({ ...saved.manifest, ...changes, bindings,
105
- ...(presentation ? { presentation, charts: changes.charts ?? [], tables: changes.tables ?? [] } : {}) });
106
- validateFinal(manifest, previewIds !== undefined);
115
+ (0, editing_patch_1.assertHtmlPreserved)(saved.manifest.presentation, presentation, removals?.htmlElementIds);
116
+ const manifest = dashboard_schema_1.dashboardSchema.parse({ ...saved.manifest, ...changes, bindings, controls, charts, tables,
117
+ ...(presentation ? { presentation } : {}) });
118
+ validateFinal(manifest, previewIds !== undefined, changedBindings?.map(item => item.id));
107
119
  const { schema, id, ...update } = manifest;
108
120
  const result = await service.update(scope, editId, expectedRevision, update, expectedCurrentVersionId);
109
121
  const resourceCard = await (0, resource_card_1.emitDashboardCard)(scope, { ...result, state: 'draft', revision: result.draftRevision }, config);
110
122
  return JSON.stringify({ dashboard: result, resourceCard, ...(sourceFile ? { sourceFile } : {}) });
111
- }, { name: 'dashboard_update', description: 'Update the opened dashboard in place, preserving its Artifact ID and version history. Supply stable editId and expectedRevision from dashboard_open, the explicit current or requested title, plus only other changed fields. Saves a persistent draft; does not publish. For an HTML rename, send title and htmlPath together; HTML headings do not rename the Artifact. htmlPath replaces the page; omitted previewIds preserves bindings, while supplied previewIds replaces the complete set with successful fresh previews. Conflicting edits fail instead of overwriting.',
123
+ }, { name: 'dashboard_update', description: 'Update the opened dashboard in place, preserving its Artifact ID and version history. Supply stable editId and expectedRevision from dashboard_open, the explicit current or requested title, plus only other changed fields. Saves a persistent draft; does not publish. For an HTML rename, send title and htmlPath together; HTML headings do not rename the Artifact. htmlPath replaces the page; omitted previewIds preserves bindings, while supplied previewIds upserts changed bindings by stable ID. Controls/charts/tables also upsert by ID. Use removals only for explicitly requested deletions. Preserve HTML IDs unless their removal is requested. Conflicting edits fail instead of overwriting.',
112
124
  schema: exports.updateToolSchema, verboseParsingErrors: true, metadata: { toolName: { en_US: 'Save dashboard changes', zh_Hans: '保存看板修改' } } })
113
125
  ];
114
126
  }
@@ -1 +1 @@
1
- export declare const HTML_DASHBOARD_GUIDANCE = "For free-layout HTML dashboards, read the installed live-dashboard skill's references/html-runtime.md through read_skill_file, relative to its SKILL.md path in the skill catalog. Create/edit the HTML with SandboxFile; then dashboard_create_html or dashboard_update validates and saves it. Do not use generic present_files to save or preview a live dashboard. Its preview belongs to the Live dashboards Workbench. Call dashboard_libraries for packaged ECharts/Three.js and the allowed HTTPS origins for online scripts, stylesheets and fonts. Tailwind CDN is supported through script src; pin a version when possible. Online resource failures must be repaired before saving. Do not add online URLs to the packaged libraries array; all data access remains declarative Binding queries through window.xpertLiveArtifact. The page MUST declare const live = window.xpertLiveArtifact in its script (live is not a host global), register live.subscribe(bindingId, render), await live.ready(), and invoke live.query(bindingId, typedControls) for initial data and filter changes. window.__datasets and window.__refresh are not platform APIs and are never populated by the host. Read the runtime reference and its html-lifecycle.md example before writing HTML; preserve that lifecycle while freely changing the layout. Use one complete Dataset for KPI, trend and details when they share the same metric and filters; do not create a redundant unfiltered total Binding. Never let filtered and unfiltered subscriptions both write the same KPI: a refresh would race and overwrite the selected-region total. Distinct global totals need distinct, clearly labeled elements. Native data selects require data-dashboard-control=\"<controlId>\"; purely visual selects use data-dashboard-presentation. A data filter must query, not only filter cached browser rows. bindSelect controls ONE binding only. For a shared dimension filter across multiple bindings, follow the skill custom-select example and query every affected binding with the same typed controls; matching control IDs do not broadcast automatically. bindSelect derives options from Dataset fields and is only for dimension filters. Fixed query enums (days, page size, granularity) need options from parameter.allowedValues and a typed change listener calling live.query; never use date rows as numeric day-count options.";
1
+ export declare const HTML_DASHBOARD_GUIDANCE = "For free-layout HTML dashboards, read the installed live-dashboard skill's references/html-runtime.md through read_skill_file, relative to its SKILL.md path in the skill catalog. Create/edit the HTML with SandboxFile; then dashboard_create_html or dashboard_update validates and saves it. Do not use generic present_files to save or preview a live dashboard. Its preview belongs to the Live dashboards Workbench. Call dashboard_libraries for packaged ECharts/Three.js. Network access is temporarily unrestricted for functional acceptance. CDN scripts/styles/fonts, CSS imports, external images and fetch/XHR are available under browser CORS and mixed-content rules. Tailwind CDN is supported; do not remove styling to avoid network validation. Platform business data remains declarative Binding queries through window.xpertLiveArtifact; never embed credentials. The page MUST declare const live = window.xpertLiveArtifact in its script (live is not a host global), register live.subscribe(bindingId, render), await live.ready(), and invoke live.query(bindingId, typedControls) for initial data and filter changes. window.__datasets and window.__refresh are not platform APIs and are never populated by the host. Read the runtime reference and its html-lifecycle.md example before writing HTML; preserve that lifecycle while freely changing the layout. Use one complete Dataset for KPI, trend and details when they share the same metric and filters; do not create a redundant unfiltered total Binding. Never let filtered and unfiltered subscriptions both write the same KPI: a refresh would race and overwrite the selected-region total. Distinct global totals need distinct, clearly labeled elements. Native data selects require data-dashboard-control=\"<controlId>\"; purely visual selects use data-dashboard-presentation. A data filter must query, not only filter cached browser rows. bindSelect controls ONE binding only. For a shared dimension filter across multiple bindings, follow the skill custom-select example and query every affected binding with the same typed controls; matching control IDs do not broadcast automatically. bindSelect derives options from Dataset fields and is only for dimension filters. Fixed query enums (days, page size, granularity) need options from parameter.allowedValues and a typed change listener calling live.query; never use date rows as numeric day-count options.";
@@ -1,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HTML_DASHBOARD_GUIDANCE = void 0;
4
- exports.HTML_DASHBOARD_GUIDANCE = `For free-layout HTML dashboards, read the installed live-dashboard skill's references/html-runtime.md through read_skill_file, relative to its SKILL.md path in the skill catalog. Create/edit the HTML with SandboxFile; then dashboard_create_html or dashboard_update validates and saves it. Do not use generic present_files to save or preview a live dashboard. Its preview belongs to the Live dashboards Workbench. Call dashboard_libraries for packaged ECharts/Three.js and the allowed HTTPS origins for online scripts, stylesheets and fonts. Tailwind CDN is supported through script src; pin a version when possible. Online resource failures must be repaired before saving. Do not add online URLs to the packaged libraries array; all data access remains declarative Binding queries through window.xpertLiveArtifact. The page MUST declare const live = window.xpertLiveArtifact in its script (live is not a host global), register live.subscribe(bindingId, render), await live.ready(), and invoke live.query(bindingId, typedControls) for initial data and filter changes. window.__datasets and window.__refresh are not platform APIs and are never populated by the host. Read the runtime reference and its html-lifecycle.md example before writing HTML; preserve that lifecycle while freely changing the layout. Use one complete Dataset for KPI, trend and details when they share the same metric and filters; do not create a redundant unfiltered total Binding. Never let filtered and unfiltered subscriptions both write the same KPI: a refresh would race and overwrite the selected-region total. Distinct global totals need distinct, clearly labeled elements. Native data selects require data-dashboard-control="<controlId>"; purely visual selects use data-dashboard-presentation. A data filter must query, not only filter cached browser rows. bindSelect controls ONE binding only. For a shared dimension filter across multiple bindings, follow the skill custom-select example and query every affected binding with the same typed controls; matching control IDs do not broadcast automatically. bindSelect derives options from Dataset fields and is only for dimension filters. Fixed query enums (days, page size, granularity) need options from parameter.allowedValues and a typed change listener calling live.query; never use date rows as numeric day-count options.`;
4
+ exports.HTML_DASHBOARD_GUIDANCE = `For free-layout HTML dashboards, read the installed live-dashboard skill's references/html-runtime.md through read_skill_file, relative to its SKILL.md path in the skill catalog. Create/edit the HTML with SandboxFile; then dashboard_create_html or dashboard_update validates and saves it. Do not use generic present_files to save or preview a live dashboard. Its preview belongs to the Live dashboards Workbench. Call dashboard_libraries for packaged ECharts/Three.js. Network access is temporarily unrestricted for functional acceptance. CDN scripts/styles/fonts, CSS imports, external images and fetch/XHR are available under browser CORS and mixed-content rules. Tailwind CDN is supported; do not remove styling to avoid network validation. Platform business data remains declarative Binding queries through window.xpertLiveArtifact; never embed credentials. The page MUST declare const live = window.xpertLiveArtifact in its script (live is not a host global), register live.subscribe(bindingId, render), await live.ready(), and invoke live.query(bindingId, typedControls) for initial data and filter changes. window.__datasets and window.__refresh are not platform APIs and are never populated by the host. Read the runtime reference and its html-lifecycle.md example before writing HTML; preserve that lifecycle while freely changing the layout. Use one complete Dataset for KPI, trend and details when they share the same metric and filters; do not create a redundant unfiltered total Binding. Never let filtered and unfiltered subscriptions both write the same KPI: a refresh would race and overwrite the selected-region total. Distinct global totals need distinct, clearly labeled elements. Native data selects require data-dashboard-control="<controlId>"; purely visual selects use data-dashboard-presentation. A data filter must query, not only filter cached browser rows. bindSelect controls ONE binding only. For a shared dimension filter across multiple bindings, follow the skill custom-select example and query every affected binding with the same typed controls; matching control IDs do not broadcast automatically. bindSelect derives options from Dataset fields and is only for dimension filters. Fixed query enums (days, page size, granularity) need options from parameter.allowedValues and a typed change listener calling live.query; never use date rows as numeric day-count options.`;
@@ -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,12 @@ 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({});
42
+ const providerOptionSchema = {
43
+ type: 'string',
44
+ 'x-ui': { component: 'remoteSelect', selectUrl: '/api/live-dashboard/provider-options' }
45
+ };
38
46
  exports.DASHBOARD_GUIDANCE = `For a new independent dashboard, use a new unused HTML file path. An existing filename or matching title is not authorization to edit another Artifact; do not switch from creation to dashboard_open/update because a file already exists. For a new dashboard, call dashboard_list_sources before searching raw files, reading old scripts or creating transformations. Named governed sources may already provide the required query; inspect their provider contracts first. File-backed sources do not imply that a script must be written. For creating or editing live dashboards, first read the installed live-dashboard skill using read_skill_file and follow only the references relevant to the task. Use SandboxFile tools for all source-file authoring; HTML alone is not a saved dashboard. Follow preview.validation.checks immediately before authoring HTML; each suggestedCall must actually succeed. Business definitions, real data previews and nullable-filter tests are required before saving new or changed Bindings. Do not invent business data or substitute a different metric without agreement. Report completion only from a successful dashboard_create/dashboard_create_html/dashboard_update draft receipt. Human publication is separate. Successful create, update and open tools emit a chat resource card automatically; resourceCard.status reports emission. Its Open button selects the exact dashboard in Workbench. Never fabricate a link, call present_files for dashboard HTML, or output card JSON as text. If card delivery is unavailable, report the saved receipt and use the Workbench picker.`;
39
47
  let DashboardMiddleware = class DashboardMiddleware {
40
48
  constructor(service) {
@@ -45,8 +53,11 @@ let DashboardMiddleware = class DashboardMiddleware {
45
53
  slashCommands: prompt_workflows_1.dashboardPromptWorkflows,
46
54
  // Tools inherit this self-contained icon through the platform middleware metadata.
47
55
  icon: dashboard_icon_1.dashboardIcon, features: [view_provider_1.FEATURE], configSchema: { type: 'object', properties: {
48
- allowedFinalProviders: { type: 'array', items: { type: 'string' }, title: { en_US: 'Allowed final dashboard providers', zh_Hans: '最终看板允许的 Provider' },
49
- description: { en_US: 'Optional provider IDs required for saved Agent dashboards. Upstream previews remain available.', zh_Hans: '可选的最终看板 Provider ID 列表,不限制上游来源预览。' } }
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: '每次执行的调用预算,包含配方读取和数据源准备;保存校验仍然必须通过。' } },
59
+ allowedFinalProviders: { type: 'array', minItems: 1, maxItems: 20, items: providerOptionSchema, title: { en_US: 'Allowed final dashboard providers', zh_Hans: '最终看板允许的数据源类型' },
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;暂不可用的已有值会保留,不影响上游来源预览。' } }
50
61
  }, additionalProperties: false }
51
62
  };
52
63
  }
@@ -54,10 +65,8 @@ let DashboardMiddleware = class DashboardMiddleware {
54
65
  const options = middlewareOptionsSchema.parse(_options);
55
66
  if (!context.xpertId)
56
67
  throw new Error('DASHBOARD_ASSISTANT_REQUIRED');
57
- const scope = {
58
- tenantId: context.tenantId ?? plugin_sdk_1.RequestContext.currentTenantId(), organizationId: context.organizationId ?? plugin_sdk_1.RequestContext.getOrganizationId(),
59
- userId: context.userId ?? plugin_sdk_1.RequestContext.currentUserId(), hostType: context.projectId ? 'project' : 'agent', hostId: context.projectId ?? context.xpertId, slots: []
60
- };
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 });
61
70
  // Evidence belongs to this run and actor, never a process-wide cache of business data.
62
71
  const discovered = new Map(), previews = new Map();
63
72
  const discovering = new Map();
@@ -110,13 +119,13 @@ let DashboardMiddleware = class DashboardMiddleware {
110
119
  throw new Error('DASHBOARD_PREVIEW_REQUIRED: use a previewId returned in this run');
111
120
  return binding;
112
121
  });
113
- const validateFinal = (manifest, requirePreviews) => {
122
+ const validateFinal = (manifest, requirePreviews, changedBindingIds) => {
114
123
  const allowedProviders = options.allowedFinalProviders;
115
124
  if (allowedProviders && manifest.bindings.some(binding => !allowedProviders.includes(binding.providerId)))
116
125
  throw new Error(`DASHBOARD_PROVIDER_REQUIRED: final bindings must use ${allowedProviders.join(', ')}`);
117
126
  if (requirePreviews) {
118
127
  const controls = Object.fromEntries(manifest.controls.filter(item => item.defaultValue !== undefined).map(item => [item.controlId, item.defaultValue ?? null]));
119
- for (const binding of manifest.bindings) {
128
+ for (const binding of manifest.bindings.filter(binding => !changedBindingIds || changedBindingIds.includes(binding.id))) {
120
129
  const previewId = previewKey(binding, controls);
121
130
  if (!previews.has(previewId))
122
131
  throw new Error('DASHBOARD_PREVIEW_REQUIRED: preview the exact binding and default controls before saving');
@@ -125,13 +134,16 @@ let DashboardMiddleware = class DashboardMiddleware {
125
134
  }
126
135
  };
127
136
  const save = async (input, config, html, dependencies = []) => {
128
- const { previewIds, supersedesPaths, ...definition } = input;
137
+ const { previewIds, supersedesPaths, creationIntent, ...definition } = input;
129
138
  const bindings = resolvePreviews(previewIds);
130
139
  const manifest = dashboard_schema_1.dashboardSchema.parse({ ...definition, bindings, schema: 'xpert.live-dashboard', id: (0, node_crypto_1.randomUUID)(),
131
140
  ...(html ? { presentation: { type: 'html', html, ...(dependencies.length ? { dependencies } : {}) } } : {}) });
132
141
  validateFinal(manifest, true);
133
142
  const key = hash({ input: { ...definition, previewIds }, html, dependencies });
134
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.');
135
147
  if (previous) {
136
148
  const receipt = await this.service.save(scope, previous.manifest, previous.receipt.baseVersionId, previous.receipt.draftRevision);
137
149
  created.set(key, { manifest: previous.manifest, receipt });
@@ -139,7 +151,7 @@ let DashboardMiddleware = class DashboardMiddleware {
139
151
  return JSON.stringify({ dashboard: receipt, resourceCard });
140
152
  }
141
153
  const sameTitle = [...created.values()].find(item => item.manifest.title === manifest.title);
142
- if (sameTitle)
154
+ if (sameTitle && !creationIntent)
143
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.`);
144
156
  const result = await this.service.save(scope, manifest, null);
145
157
  created.set(key, { manifest, receipt: result });
@@ -154,7 +166,9 @@ let DashboardMiddleware = class DashboardMiddleware {
154
166
  });
155
167
  const previewRetries = (0, preview_retry_1.createPreviewRetryGuard)();
156
168
  let modelCalls = 0;
157
- 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) }),
158
172
  wrapToolCall: async (request, handler) => {
159
173
  const name = request.toolCall.name;
160
174
  const args = request.toolCall.args;
@@ -166,6 +180,16 @@ let DashboardMiddleware = class DashboardMiddleware {
166
180
  ? await evidence.trackWrite(path, async () => handler(request)) : await previewRetries.run(request, handler);
167
181
  if (save && 'content' in result && (!('status' in result) || result.status !== 'error'))
168
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
+ }
169
193
  return result;
170
194
  }, wrapModelCall: async (request, handler) => {
171
195
  if (previewRetries.exhausted) {
@@ -177,12 +201,12 @@ let DashboardMiddleware = class DashboardMiddleware {
177
201
  // A noncompliant model must not restart the failed loop even after tools were removed.
178
202
  return result?.tool_calls?.length ? new messages_1.AIMessage(summary) : result;
179
203
  }
180
- const exhausted = ++modelCalls > 24;
204
+ const exhausted = ++modelCalls > options.maxModelCalls;
181
205
  if (exhausted && evidence.pending)
182
206
  throw new Error('DASHBOARD_SAVE_NOT_CONFIRMED: no successful draft save receipt; Workspace changes have not been saved to the dashboard.');
183
207
  const result = await handler({ ...request, ...(evidence.pending ? { toolChoice: 'required' } : {}), ...(exhausted ? { tools: [] } : {}), systemMessage: new messages_1.SystemMessage({ content: [
184
208
  ...(typeof request.systemMessage?.content === 'string' ? [{ type: 'text', text: request.systemMessage.content }] : request.systemMessage?.content ?? []),
185
- { 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.' : '') }
186
210
  ] }) });
187
211
  if (evidence.pending && result && !result.tool_calls?.length)
188
212
  throw new Error('DASHBOARD_SAVE_NOT_CONFIRMED: the model stopped without a successful draft save receipt.');
@@ -190,9 +214,9 @@ let DashboardMiddleware = class DashboardMiddleware {
190
214
  }, tools: [
191
215
  filterEvidence.tool,
192
216
  ...(0, definition_tools_1.createDefinitionTools)(context, () => { listedSources = undefined; }),
193
- (0, tools_1.tool)(async () => JSON.stringify({ libraries: await (0, document_libraries_1.libraryLocks)(['echarts', 'three']),
194
- online: { ...document_network_1.DASHBOARD_NETWORK_POLICY,
195
- 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.' },
196
220
  pageRuntime: {
197
221
  global: 'window.xpertLiveArtifact',
198
222
  bindingIds: [...new Set([...previews.values()].map(binding => binding.id))],
@@ -200,11 +224,15 @@ let DashboardMiddleware = class DashboardMiddleware {
200
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.',
201
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.'
202
226
  },
203
- 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.',
204
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.' },
205
- 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.',
206
234
  schema: v3_1.z.object({}).strict(), verboseParsingErrors: true, metadata: { toolName: { en_US: 'Discover dashboard libraries', zh_Hans: '查看看板前端图库' } } }),
207
- ...(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),
208
236
  (0, tools_1.tool)(async () => {
209
237
  listedSources = await this.service.sources(scope);
210
238
  return JSON.stringify(listedSources);