@chengxianglibra/dsh-data-analysis 0.1.2 → 0.2.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 (222) hide show
  1. package/README.en.md +135 -0
  2. package/README.md +105 -98
  3. package/cordis.patch.yml +1 -1
  4. package/lib/bin/environment.js +8 -6
  5. package/lib/client/credentials/changes.js +74 -0
  6. package/lib/client/credentials/create-datasource.js +126 -22
  7. package/lib/client/credentials/credential-draft.js +42 -0
  8. package/lib/client/credentials/defaults.js +11 -0
  9. package/lib/client/credentials/descriptions.js +46 -0
  10. package/lib/client/credentials/install.js +209 -81
  11. package/lib/client/credentials/model.js +323 -51
  12. package/lib/client/credentials/report-publishing.js +96 -0
  13. package/lib/client/credentials/styles.js +13 -9
  14. package/lib/client/i18n/context.js +22 -0
  15. package/lib/client/i18n/copy.js +60 -0
  16. package/lib/client/i18n/credentials.js +475 -0
  17. package/lib/client/i18n/host.js +28 -0
  18. package/lib/client/i18n/navigation.js +41 -0
  19. package/lib/client/i18n/presentation.js +577 -0
  20. package/lib/client/i18n/python.js +36 -0
  21. package/lib/client/i18n/semantic.js +441 -0
  22. package/lib/client/i18n/settings.js +30 -0
  23. package/lib/client/input-context.js +21 -0
  24. package/lib/client/python-tool/card.js +35 -0
  25. package/lib/client/python-tool/execution-error.js +35 -0
  26. package/lib/client/python-tool/install.js +9 -0
  27. package/lib/client/python-tool/model.js +101 -0
  28. package/lib/client/right-tabs/install.js +535 -0
  29. package/lib/client/right-tabs/live-delivery.js +58 -0
  30. package/lib/client/right-tabs/navigation.js +47 -0
  31. package/lib/client/right-tabs/page.js +178 -0
  32. package/lib/client/right-tabs/styles.js +11 -0
  33. package/lib/client/rpc.js +5 -0
  34. package/lib/client/semantic-reference.js +10 -0
  35. package/lib/client/settings/card.js +90 -0
  36. package/lib/client/settings/install.js +10 -0
  37. package/lib/client/workspace-header-action.js +14 -1
  38. package/lib/client/workspace-shortcuts.js +26 -0
  39. package/lib/client.js +388 -242
  40. package/lib/compatibility.js +1 -1
  41. package/lib/datasource/authoring.js +43 -4
  42. package/lib/datasource/bridge-programs.js +23 -20
  43. package/lib/datasource/bridge.js +46 -3
  44. package/lib/datasource/changes-contract.js +41 -0
  45. package/lib/datasource/changes.js +49 -0
  46. package/lib/datasource/configure.js +39 -0
  47. package/lib/datasource/defaults.js +25 -0
  48. package/lib/datasource/index.js +2 -1
  49. package/lib/datasource/python-options.js +15 -0
  50. package/lib/datasource/python.js +119 -25
  51. package/lib/datasource/rpc.js +75 -13
  52. package/lib/datasource/service.js +286 -11
  53. package/lib/datasource/test.js +3 -2
  54. package/lib/disclosure/activation.js +19 -7
  55. package/lib/disclosure/execution-guidance.js +49 -0
  56. package/lib/disclosure/help.js +4 -1
  57. package/lib/environment/runtime.js +35 -34
  58. package/lib/environment/workspace.js +18 -1
  59. package/lib/lifecycle.js +33 -0
  60. package/lib/plugin-agents.js +154 -0
  61. package/lib/plugin.js +98 -172
  62. package/lib/presentation/asset.js +34 -0
  63. package/lib/presentation/assets/portable.js +14 -14
  64. package/lib/presentation/assets/static.js +344 -73
  65. package/lib/presentation/build/index.js +3 -2
  66. package/lib/presentation/commit.js +19 -12
  67. package/lib/presentation/contracts/asset-path.js +9 -0
  68. package/lib/presentation/contracts/catalog.js +62 -0
  69. package/lib/presentation/contracts/charts.js +0 -23
  70. package/lib/presentation/contracts/index.js +17 -5
  71. package/lib/presentation/contracts/types.js +1 -1
  72. package/lib/presentation/delivery.js +45 -30
  73. package/lib/presentation/export-html.js +214 -0
  74. package/lib/presentation/files.js +4 -5
  75. package/lib/presentation/projection/index.js +2 -1
  76. package/lib/presentation/projection/program.js +2 -9
  77. package/lib/presentation/projection/sql-code.js +13 -13
  78. package/lib/presentation/reports.js +114 -17
  79. package/lib/presentation/rpc.js +96 -58
  80. package/lib/presentation/tool.js +6 -10
  81. package/lib/report-publishing/adapters.js +111 -0
  82. package/lib/report-publishing/config.js +69 -0
  83. package/lib/report-publishing/service.js +180 -0
  84. package/lib/rpc.js +49 -0
  85. package/lib/semantic-browser/contracts.js +17 -0
  86. package/lib/semantic-browser/program.js +10 -0
  87. package/lib/semantic-browser/service.js +40 -6
  88. package/lib/semantic-reference/bridge.js +10 -1
  89. package/lib/semantic-reference/contracts.js +20 -6
  90. package/lib/semantic-reference/environment.js +56 -0
  91. package/lib/semantic-reference/labels.js +16 -0
  92. package/lib/semantic-reference/rpc.js +39 -18
  93. package/lib/semantic-reference/search.js +5 -4
  94. package/lib/semantic-reference/usage.js +12 -3
  95. package/lib/settings.js +21 -0
  96. package/lib/tool-lifecycle.js +22 -0
  97. package/lib/types/client/credentials/changes.d.ts +9 -0
  98. package/lib/types/client/credentials/create-datasource.d.ts +3 -1
  99. package/lib/types/client/credentials/credential-draft.d.ts +14 -0
  100. package/lib/types/client/credentials/defaults.d.ts +2 -0
  101. package/lib/types/client/credentials/descriptions.d.ts +1 -0
  102. package/lib/types/client/credentials/install.d.ts +19 -1
  103. package/lib/types/client/credentials/model.d.ts +31 -11
  104. package/lib/types/client/credentials/report-publishing.d.ts +5 -0
  105. package/lib/types/client/credentials/styles.d.ts +1 -1
  106. package/lib/types/client/i18n/context.d.ts +17 -0
  107. package/lib/types/client/i18n/copy.d.ts +821 -0
  108. package/lib/types/client/i18n/credentials.d.ts +475 -0
  109. package/lib/types/client/i18n/host.d.ts +17 -0
  110. package/lib/types/client/i18n/navigation.d.ts +41 -0
  111. package/lib/types/client/i18n/presentation.d.ts +577 -0
  112. package/lib/types/client/i18n/python.d.ts +19 -0
  113. package/lib/types/client/i18n/semantic.d.ts +441 -0
  114. package/lib/types/client/i18n/settings.d.ts +16 -0
  115. package/lib/types/client/input-context.d.ts +19 -0
  116. package/lib/types/client/presentation/ask-dsh.d.ts +4 -8
  117. package/lib/types/client/presentation/catalog-model.d.ts +24 -0
  118. package/lib/types/client/presentation/catalog.d.ts +20 -0
  119. package/lib/types/client/presentation/chart-tooltip.d.ts +2 -2
  120. package/lib/types/client/presentation/context-reference.d.ts +12 -0
  121. package/lib/types/client/presentation/copy-context.d.ts +2 -2
  122. package/lib/types/client/presentation/delivery-model.d.ts +27 -6
  123. package/lib/types/client/presentation/delivery.d.ts +8 -7
  124. package/lib/types/client/presentation/download.d.ts +2 -0
  125. package/lib/types/client/presentation/export-menu.d.ts +26 -0
  126. package/lib/types/client/presentation/export-view.d.ts +63 -0
  127. package/lib/types/client/presentation/host-entry.d.ts +8 -2
  128. package/lib/types/client/presentation/icons.d.ts +1 -1
  129. package/lib/types/client/presentation/model.d.ts +14 -15
  130. package/lib/types/client/presentation/reader.d.ts +15 -2
  131. package/lib/types/client/presentation/reference-source.d.ts +9 -0
  132. package/lib/types/client/presentation/source-code-model.d.ts +2 -1
  133. package/lib/types/client/presentation/source-code.d.ts +0 -5
  134. package/lib/types/client/presentation/source-dialog.d.ts +1 -2
  135. package/lib/types/client/presentation/sources.d.ts +5 -5
  136. package/lib/types/client/presentation/styles.d.ts +1 -1
  137. package/lib/types/client/presentation/table.d.ts +5 -1
  138. package/lib/types/client/python-tool/card.d.ts +2 -0
  139. package/lib/types/client/python-tool/execution-error.d.ts +3 -0
  140. package/lib/types/client/python-tool/install.d.ts +2 -0
  141. package/lib/types/client/python-tool/model.d.ts +20 -0
  142. package/lib/types/client/right-tabs/install.d.ts +18 -0
  143. package/lib/types/client/right-tabs/live-delivery.d.ts +8 -0
  144. package/lib/types/client/right-tabs/navigation.d.ts +17 -0
  145. package/lib/types/client/right-tabs/page.d.ts +36 -0
  146. package/lib/types/client/right-tabs/styles.d.ts +2 -0
  147. package/lib/types/client/rpc.d.ts +3 -0
  148. package/lib/types/client/semantic-reference.d.ts +4 -0
  149. package/lib/types/client/settings/card.d.ts +9 -0
  150. package/lib/types/client/settings/install.d.ts +2 -0
  151. package/lib/types/client/workspace-header-action.d.ts +1 -1
  152. package/lib/types/client/workspace-shortcuts.d.ts +15 -0
  153. package/lib/types/client.d.ts +5 -2
  154. package/lib/types/compatibility.d.ts +1 -1
  155. package/lib/types/datasource/authoring.d.ts +10 -0
  156. package/lib/types/datasource/bridge.d.ts +23 -2
  157. package/lib/types/datasource/changes-contract.d.ts +18 -0
  158. package/lib/types/datasource/changes.d.ts +15 -0
  159. package/lib/types/datasource/configure.d.ts +7 -0
  160. package/lib/types/datasource/defaults.d.ts +7 -0
  161. package/lib/types/datasource/index.d.ts +6 -1
  162. package/lib/types/datasource/python-options.d.ts +8 -0
  163. package/lib/types/datasource/python.d.ts +17 -2
  164. package/lib/types/datasource/rpc.d.ts +2 -1
  165. package/lib/types/datasource/service.d.ts +48 -5
  166. package/lib/types/datasource/test.d.ts +1 -1
  167. package/lib/types/disclosure/activation.d.ts +2 -1
  168. package/lib/types/disclosure/execution-guidance.d.ts +6 -0
  169. package/lib/types/disclosure/help.d.ts +1 -1
  170. package/lib/types/environment/types.d.ts +2 -1
  171. package/lib/types/environment/workspace.d.ts +1 -0
  172. package/lib/types/lifecycle.d.ts +8 -0
  173. package/lib/types/plugin-agents.d.ts +29 -0
  174. package/lib/types/plugin.d.ts +12 -28
  175. package/lib/types/presentation/asset.d.ts +6 -0
  176. package/lib/types/presentation/build/index.d.ts +7 -2
  177. package/lib/types/presentation/commit.d.ts +1 -1
  178. package/lib/types/presentation/contracts/asset-path.d.ts +2 -0
  179. package/lib/types/presentation/contracts/catalog.d.ts +27 -0
  180. package/lib/types/presentation/contracts/charts.d.ts +0 -2
  181. package/lib/types/presentation/contracts/types.d.ts +7 -4
  182. package/lib/types/presentation/delivery.d.ts +1 -1
  183. package/lib/types/presentation/export-html.d.ts +13 -0
  184. package/lib/types/presentation/reports.d.ts +6 -1
  185. package/lib/types/presentation/rpc.d.ts +3 -2
  186. package/lib/types/presentation/tool.d.ts +2 -2
  187. package/lib/types/report-publishing/adapters.d.ts +6 -0
  188. package/lib/types/report-publishing/config.d.ts +22 -0
  189. package/lib/types/report-publishing/service.d.ts +68 -0
  190. package/lib/types/rpc.d.ts +3 -0
  191. package/lib/types/semantic-browser/contracts.d.ts +7 -0
  192. package/lib/types/semantic-browser/service.d.ts +7 -1
  193. package/lib/types/semantic-reference/bridge.d.ts +1 -0
  194. package/lib/types/semantic-reference/contracts.d.ts +5 -2
  195. package/lib/types/semantic-reference/environment.d.ts +22 -0
  196. package/lib/types/semantic-reference/labels.d.ts +16 -0
  197. package/lib/types/semantic-reference/rpc.d.ts +1 -1
  198. package/lib/types/semantic-reference/search.d.ts +1 -1
  199. package/lib/types/settings.d.ts +14 -0
  200. package/lib/types/tool-lifecycle.d.ts +4 -0
  201. package/lib/types/workspace-identity.d.ts +6 -0
  202. package/lib/workspace-identity.js +9 -0
  203. package/package.json +93 -61
  204. package/python/presentation-kit/dist/dsh_data_analysis_presentation_kit-1.1.0-py3-none-any.whl +0 -0
  205. package/skills/dsh-data-analysis-files/SKILL.md +44 -0
  206. package/skills/dsh-data-analysis-files/references/delivery.md +15 -0
  207. package/skills/dsh-data-analysis-files/references/examples.md +93 -0
  208. package/skills/dsh-data-analysis-presentation/SKILL.md +15 -15
  209. package/skills/dsh-data-analysis-presentation/references/examples/artifact.draft.json +13 -4
  210. package/skills/dsh-data-analysis-presentation/references/examples/charts.draft.json +3 -2
  211. package/skills/dsh-data-analysis-presentation/references/examples/comparison.draft.json +3 -2
  212. package/skills/dsh-data-analysis-presentation/references/examples/computed-sources.draft.json +12 -5
  213. package/skills/dsh-data-analysis-presentation/references/examples/computed.draft.json +3 -2
  214. package/skills/dsh-data-analysis-presentation/references/examples/interaction.draft.json +3 -2
  215. package/skills/dsh-data-analysis-presentation/references/examples/source-only.draft.json +12 -4
  216. package/skills/dsh-data-analysis-presentation/references/examples.md +3 -1
  217. package/skills/dsh-data-analysis-presentation/references/export-html.md +7 -0
  218. package/skills/dsh-data-analysis-presentation/references/interaction.md +1 -1
  219. package/skills/dsh-data-analysis-presentation/references/narrative.md +1 -1
  220. package/skills/dsh-data-analysis-presentation/references/publishing.md +3 -0
  221. package/skills/dsh-data-analysis-presentation/references/schema.md +29 -2
  222. package/lib/types/client/presentation/install.d.ts +0 -15
package/lib/plugin.js CHANGED
@@ -1,28 +1,39 @@
1
+ import { CredentialChangesService } from "./datasource/changes.js";
2
+ import { credentialChangesHost } from "./datasource/changes-contract.js";
3
+ import { finishCleanup } from "./lifecycle.js";
4
+ import { createMarivoAgentInstallation } from "./plugin-agents.js";
5
+ import { registerPublishingCredentials } from "./report-publishing/adapters.js";
6
+ import { resolvePublishingConfig } from "./report-publishing/config.js";
7
+ import { ReportPublishingService } from "./report-publishing/service.js";
8
+ import { installPythonSettings } from "./settings.js";
9
+ import { resolvePresentationWorkspace } from "./workspace-identity.js";
10
+ export { installMarivoPlugin } from "./plugin-agents.js";
11
+ export { resolvePresentationWorkspace } from "./workspace-identity.js";
12
+ import { referenceEnvironmentResolver } from "./semantic-reference/environment.js";
1
13
  /** Cordis lifecycle adapter for the Web-profile shared Marivo Runtime. */
2
14
  import process from 'node:process';
3
15
  import { fileURLToPath } from 'node:url';
4
16
  import { apply as installSkillFilesystem } from '@deepseek-ai/dsh-skill-filesystem';
5
17
  import { WorkspaceId } from '@deepseek-ai/dsh-workspace';
6
18
  import z from '@deepseek-ai/schemastery';
7
- import { createMarivoBridgeSet } from "./bridges.js";
8
19
  import { MarivoDatasourceBridge } from "./datasource/bridge.js";
9
- import { registerMarivoDatasourceTestTool } from "./datasource/index.js";
10
- import { registerMarivoPythonTool } from "./datasource/python.js";
20
+ import { DEFAULT_PYTHON_TIMEOUT_MS, resolvePythonOptions, } from "./datasource/python-options.js";
11
21
  import { registerCredentialRpc } from "./datasource/rpc.js";
12
22
  import { MarivoCredentialService } from "./datasource/service.js";
13
23
  import { registerMarivoRuntimeShellEnvironment } from "./datasource/shell-env.js";
14
24
  import { MarivoHelpBridge } from "./disclosure/bridge.js";
15
- import { installMarivoDisclosure, } from "./disclosure/index.js";
16
- import { createSharedMarivoRuntimeRunner, DEFAULT_SHARED_RUNTIME_INSTALL_TIMEOUT_MS, ensureSharedMarivoRuntime, MarivoEnvironment, MarivoWorkspaceEnvironmentManager, resolveMarivoEnvironmentSource, } from "./environment/index.js";
17
- import { installMarivoPresentationCodeDelivery, MarivoPresentationFileService, registerMarivoPresentationRpc, registerMarivoPresentTool, } from "./presentation/index.js";
25
+ import { createSharedMarivoRuntimeRunner, DEFAULT_SHARED_RUNTIME_INSTALL_TIMEOUT_MS, ensureSharedMarivoRuntime, MarivoWorkspaceEnvironmentManager, } from "./environment/index.js";
26
+ import { MarivoPresentationFileService, registerMarivoPresentationRpc, } from "./presentation/index.js";
18
27
  import { browserFailure, SemanticBrowserService } from "./semantic-browser/service.js";
19
28
  import { registerSemanticReferenceRpc, SemanticReferenceService, SemanticReferenceUsage, } from "./semantic-reference/index.js";
29
+ export { MARIVO_DATASOURCE_CREDENTIAL_PROMPT } from "./disclosure/execution-guidance.js";
20
30
  /** Cordis plugin name used by loader diagnostics and lifecycle logs. */
21
31
  export const name = 'dsh-data-analysis';
22
32
  /** Services that must exist before the plugin binds and watches Agent scopes. */
23
33
  export const inject = [
24
34
  'agents',
25
35
  'connection',
36
+ 'typert',
26
37
  'storageDomain',
27
38
  'workspaceRegistry',
28
39
  'credentials',
@@ -31,28 +42,17 @@ export const inject = [
31
42
  'systemPrompt',
32
43
  'tools',
33
44
  ];
34
- export const MARIVO_DATASOURCE_CREDENTIAL_PROMPT = [
35
- 'DSH Credentials owns Marivo datasource secrets. Never request values in chat, read credential files or ~/.marivo/secrets.toml, or write secrets to scripts, arguments, environment variables, reports or logs.',
36
- 'Use marivo_datasource_test after datasource changes, credential rotation, connection failures, or explicit user requests. Missing credentials wait for the Web form only while the original call remains alive.',
37
- 'Execute analysis, metadata inspection and semantic data reads through marivo_python. Declare all exact datasource names this execution may access, including datasources without passwords. Use datasources: [] only when no datasource will be accessed.',
38
- 'marivo_python waits for all missing credentials and validates the bound Workspace and datasource identities before taking one fresh snapshot and starting user code once. Configured credentials need no extra connection test. Ordinary Shell receives no datasource secret.',
39
- 'marivo_python installs credential_scope before user code. Create or resume Session/reader objects inside that execution, and close Sessions in finally. Do not replace the resolver, read SecretValue contents, or bypass Host scope with environment/cache configuration.',
40
- "Each marivo_python call uses a separate process. Closing releases that process's Session resources; it does not end the analytical question or delete durable Artifacts. Continue the same persistent Session identity across calls.",
41
- 'Configured credentials do not imply a valid connection or query permissions. Preserve real failures; never automatically replay a script with possible side effects.',
42
- 'Session lifetime pattern inside one marivo_python call (create/resume using current Runtime Help):\nsession = ...\ntry:\n ... # analysis using this Session\nfinally:\n session.close()',
43
- ].join('\n');
44
- const MARIVO_ANALYSIS_CLOSEOUT_PROMPT = [
45
- 'Analysis semantics and execution contracts come from the activated Runtime Skill and current Help.',
46
- 'Before completing any analysis, including a text-only answer, check each user question and each requested comparison scope against its evidence. Keep incomplete branches explicit; preserve comparison sides, direction and denominator. Match conclusion strength to evidence, and reflect limitations in the claims themselves.',
47
- ].join(' ');
48
- const PRESENTATION_PROMPT = 'Answer ordinary factual questions in text. For charts, tables, reports, dashboards or a readable source presentation, load the dsh-data-analysis-presentation skill and deliver through marivo_present. Existing data needs no prior Marivo skill activation; load the Runtime skills and live Help when new analysis or semantic authoring is needed.';
49
45
  /** Cordis loader schema. Runtime defaults are resolved in {@link apply}. */
50
46
  export const Config = z.object({
47
+ // Defer validation to authoring so loader errors cannot echo configured values.
48
+ datasourceDefaults: z.any(),
49
+ reportPublishing: z.any(),
50
+ pythonTimeoutMs: z.number().default(DEFAULT_PYTHON_TIMEOUT_MS),
51
51
  credentialInteraction: z.union(['web', 'none']).default('web'),
52
52
  projectRoot: z.string(),
53
53
  pythonExecutable: z.string(),
54
54
  runtimeRoot: z.string(),
55
- uvExecutable: z.string(),
55
+ bootstrapPythonExecutable: z.string(),
56
56
  installTimeoutMs: z.number().default(DEFAULT_SHARED_RUNTIME_INSTALL_TIMEOUT_MS),
57
57
  });
58
58
  function configuredProjectRoot(config, agent) {
@@ -62,124 +62,17 @@ function configuredProjectRoot(config, agent) {
62
62
  process.env.DSH_CWD ??
63
63
  process.cwd());
64
64
  }
65
- /**
66
- * Install one already-bound environment into every live or subsequently published Agent scope.
67
- * Installation is transactional for existing Agents and is disposed with the Cordis plugin.
68
- */
69
- export function installMarivoPlugin(ctx, environmentOrResolver, options = {}) {
70
- const installed = new Map();
71
- const credentials = options.credentials ?? ctx.credentials;
72
- if (credentials === undefined) {
73
- throw new Error('dsh-data-analysis requires the DSH credentials service');
74
- }
75
- const credentialService = options.credentialService ??
76
- new MarivoCredentialService(credentials, options.credentialInteraction);
77
- const stopCredentialUpdates = ctx.on('credentials/reference-updated', (ref) => credentialService.invalidateStorageRef(ref));
78
- const bridgeSets = new WeakMap();
79
- const install = (agent) => {
80
- if (installed.has(agent))
81
- return;
82
- const source = environmentOrResolver instanceof MarivoEnvironment
83
- ? environmentOrResolver
84
- : () => Promise.resolve(environmentOrResolver(agent));
85
- const resolveBridgeSet = async () => {
86
- const environment = await resolveMarivoEnvironmentSource(source);
87
- let bridges = bridgeSets.get(environment);
88
- if (bridges === undefined) {
89
- bridges = createMarivoBridgeSet(environment);
90
- bridgeSets.set(environment, bridges);
91
- }
92
- return bridges;
93
- };
94
- const helpSource = options.helpBridgeSource ?? (async () => (await resolveBridgeSet()).help);
95
- const datasourceSource = async () => (await resolveBridgeSet()).datasource;
96
- const presentationSource = async () => {
97
- const workspace = resolvePresentationWorkspace(ctx, String(agent.session.id));
98
- const projection = (await resolveBridgeSet()).presentation;
99
- const current = resolvePresentationWorkspace(ctx, String(agent.session.id));
100
- if (workspace.id !== current.id ||
101
- workspace.path !== current.path ||
102
- projection.binding.projectRoot !== current.path)
103
- throw new Error('Presentation Workspace changed or does not match the bound Runtime');
104
- return { workspaceId: current.id, projection };
105
- };
106
- const controller = installMarivoDisclosure(ctx, agent, helpSource, options);
107
- controller.addDisposer(registerMarivoDatasourceTestTool(agent.ctx, datasourceSource, credentialService));
108
- controller.addDisposer(registerMarivoPythonTool(agent.ctx, datasourceSource, credentialService));
109
- controller.addDisposer(() => credentialService.disposeAgent(agent));
110
- controller.addDisposer(registerMarivoPresentTool(agent.ctx, presentationSource, agent.session));
111
- controller.addDisposer(installMarivoPresentationCodeDelivery(agent.ctx));
112
- controller.addDisposer(agent.ctx.systemPrompt.section({
113
- name: 'marivo:presentation',
114
- order: 180,
115
- text: PRESENTATION_PROMPT,
116
- }));
117
- controller.addDisposer(agent.ctx.systemPrompt.section({
118
- name: 'marivo:datasource-credentials',
119
- order: 170,
120
- text: () => controller.activeSkills.some((skill) => ['marivo-analysis', 'marivo-semantic'].includes(skill))
121
- ? MARIVO_DATASOURCE_CREDENTIAL_PROMPT
122
- : '',
123
- }));
124
- controller.addDisposer(agent.ctx.systemPrompt.section({
125
- name: 'marivo:analysis-closeout',
126
- order: 175,
127
- text: () => controller.activeSkills.includes('marivo-analysis')
128
- ? MARIVO_ANALYSIS_CLOSEOUT_PROMPT
129
- : '',
130
- }));
131
- installed.set(agent, controller);
132
- };
133
- try {
134
- for (const agent of ctx.agents.list())
135
- install(agent);
136
- }
137
- catch (error) {
138
- for (const controller of installed.values())
139
- controller.dispose();
140
- stopCredentialUpdates();
141
- void credentialService.close();
142
- throw error;
143
- }
144
- const stopCreated = ctx.on('agent/created', ({ agent }) => {
145
- install(agent);
146
- });
147
- const stopDisposed = ctx.on('agent/disposed', ({ agent }) => {
148
- installed.get(agent)?.dispose();
149
- installed.delete(agent);
150
- });
151
- let active = true;
152
- return () => {
153
- if (!active)
154
- return;
155
- active = false;
156
- stopCreated();
157
- stopDisposed();
158
- stopCredentialUpdates();
159
- for (const controller of installed.values())
160
- controller.dispose();
161
- installed.clear();
162
- void credentialService.close();
163
- };
164
- }
165
- /** Resolve only Harness-owned, header-validated Workspace membership; never infer it from a path. */
166
- export function resolvePresentationWorkspace(ctx, sessionId) {
167
- const matches = ctx.workspaceRegistry
168
- .list()
169
- .filter((workspace) => workspace.sessionIds.some((id) => String(id) === sessionId));
170
- if (matches.length !== 1)
171
- throw new Error('Presentation requires one current Session Workspace');
172
- return { id: String(matches[0].id), path: matches[0].path };
173
- }
174
65
  /** Ensure the shared Runtime once, mount its skills, then bind each Workspace lazily. */
175
66
  export async function apply(ctx, config = {}) {
67
+ const publishingConfig = resolvePublishingConfig(config.reportPublishing);
68
+ const pythonOptions = resolvePythonOptions(config);
176
69
  const pythonExecutable = config.pythonExecutable ?? process.env.DSH_DATA_ANALYSIS_PYTHON;
177
70
  const runtimeRoot = config.runtimeRoot ?? process.env.DSH_DATA_ANALYSIS_RUNTIME_ROOT;
178
- const uvExecutable = config.uvExecutable ?? process.env.DSH_DATA_ANALYSIS_UV;
71
+ const bootstrapPythonExecutable = config.bootstrapPythonExecutable ?? process.env.DSH_DATA_ANALYSIS_BOOTSTRAP_PYTHON;
179
72
  const runtime = await ensureSharedMarivoRuntime({
180
73
  ...(pythonExecutable === undefined ? {} : { pythonExecutable }),
181
74
  ...(runtimeRoot === undefined ? {} : { runtimeRoot }),
182
- ...(uvExecutable === undefined ? {} : { uvExecutable }),
75
+ ...(bootstrapPythonExecutable === undefined ? {} : { bootstrapPythonExecutable }),
183
76
  ...(config.installTimeoutMs === undefined ? {} : { installTimeoutMs: config.installTimeoutMs }),
184
77
  });
185
78
  const disposeRuntimeShellEnvironment = registerMarivoRuntimeShellEnvironment(ctx, runtime.pythonExecutable);
@@ -195,17 +88,54 @@ export async function apply(ctx, config = {}) {
195
88
  return environment;
196
89
  };
197
90
  const credentialService = new MarivoCredentialService(ctx.credentials, config.credentialInteraction);
91
+ const credentialNotifications = new CredentialChangesService(ctx, credentialService);
92
+ let unregisterChanges;
93
+ const disposeChanges = async () => {
94
+ await credentialNotifications.close();
95
+ await unregisterChanges?.();
96
+ };
97
+ let disposePublishing;
198
98
  let disposeCredentials;
199
99
  let disposeReferences;
200
100
  let disposePresentation;
201
101
  let referenceService;
202
- let disposePlugin;
102
+ let agentInstallation;
103
+ let pythonSettings;
104
+ let closing;
203
105
  const browserService = new SemanticBrowserService({
204
106
  getWorkspace: (id) => ctx.workspaceRegistry.get(WorkspaceId(id)),
205
107
  projectRoot: (workspace) => config.projectRoot ?? process.env.DSH_DATA_ANALYSIS_PROJECT_ROOT ?? workspace.path,
206
108
  resolve: (root) => manager.resolve(root),
207
109
  });
110
+ const close = () => {
111
+ if (closing)
112
+ return closing;
113
+ closing = finishCleanup([
114
+ () => agentInstallation?.close(),
115
+ () => pythonSettings?.dispose(),
116
+ () => disposeCredentials?.(),
117
+ disposeChanges,
118
+ () => disposePublishing?.(),
119
+ () => disposePresentation?.(),
120
+ () => disposeReferences?.(),
121
+ () => browserService.close(),
122
+ () => credentialService.close(),
123
+ () => referenceService?.close(),
124
+ ]).then(() => finishCleanup([() => manager.close(), disposeRuntimeShellEnvironment]), async (error) => {
125
+ await finishCleanup([
126
+ () => manager.close(),
127
+ disposeRuntimeShellEnvironment,
128
+ () => {
129
+ throw error;
130
+ },
131
+ ]);
132
+ });
133
+ void closing.catch(() => { });
134
+ return closing;
135
+ };
208
136
  try {
137
+ pythonSettings = installPythonSettings(ctx, pythonOptions);
138
+ unregisterChanges = ctx.typert.register(credentialChangesHost);
209
139
  installSkillFilesystem(ctx, {
210
140
  providerName: 'dsh-data-analysis-marivo',
211
141
  includeDefaultRoots: false,
@@ -219,10 +149,26 @@ export async function apply(ctx, config = {}) {
219
149
  watch: false,
220
150
  });
221
151
  const helpBridge = new MarivoHelpBridge(createSharedMarivoRuntimeRunner(runtime));
222
- disposePlugin = installMarivoPlugin(ctx, resolveEnvironment, {
152
+ const presentationFiles = new MarivoPresentationFileService(async (sessionId) => resolvePresentationWorkspace(ctx, sessionId), async (id) => {
153
+ const workspace = ctx.workspaceRegistry.get(WorkspaceId(id));
154
+ return workspace ? { id: String(workspace.id), path: workspace.path } : undefined;
155
+ });
156
+ disposePresentation = registerMarivoPresentationRpc(ctx.connection, presentationFiles);
157
+ const reportPublishing = new ReportPublishingService(publishingConfig, ctx.credentials, presentationFiles, undefined, (id) => {
158
+ const workspace = ctx.workspaceRegistry.get(WorkspaceId(id));
159
+ if (!workspace)
160
+ throw new Error('workspace-unavailable');
161
+ return workspace.title;
162
+ });
163
+ disposePublishing = registerPublishingCredentials(ctx.connection, reportPublishing, (id) => Boolean(ctx.workspaceRegistry.get(WorkspaceId(id))));
164
+ agentInstallation = createMarivoAgentInstallation(ctx, resolveEnvironment, {
165
+ ...pythonOptions,
166
+ pythonOptionsSource: pythonSettings.get,
223
167
  helpBridgeSource: helpBridge,
168
+ ...(publishingConfig ? { reportPublishing } : {}),
224
169
  credentialService,
225
170
  });
171
+ agentInstallation.install();
226
172
  disposeCredentials = registerCredentialRpc(ctx.connection, credentialService, async (id) => {
227
173
  const workspace = ctx.workspaceRegistry.get(WorkspaceId(id));
228
174
  if (!workspace)
@@ -232,11 +178,11 @@ export async function apply(ctx, config = {}) {
232
178
  const current = ctx.workspaceRegistry.get(WorkspaceId(id));
233
179
  if (!current ||
234
180
  current.path !== workspace.path ||
235
- (config.projectRoot ?? process.env.DSH_DATA_ANALYSIS_PROJECT_ROOT ?? current.path) !== root)
181
+ (config.projectRoot ?? process.env.DSH_DATA_ANALYSIS_PROJECT_ROOT ?? current.path) !==
182
+ root)
236
183
  throw new Error('Workspace changed');
237
184
  return new MarivoDatasourceBridge(environment);
238
- });
239
- disposePresentation = registerMarivoPresentationRpc(ctx.connection, new MarivoPresentationFileService(async (sessionId) => resolvePresentationWorkspace(ctx, sessionId)));
185
+ }, config.datasourceDefaults);
240
186
  let lastDiagnostic = -Infinity;
241
187
  const usage = new SemanticReferenceUsage(ctx.storageDomain, Date.now, () => {
242
188
  if (Date.now() - lastDiagnostic >= 30_000) {
@@ -244,22 +190,13 @@ export async function apply(ctx, config = {}) {
244
190
  console.warn('dsh-data-analysis semantic-reference usage unavailable');
245
191
  }
246
192
  });
247
- referenceService = new SemanticReferenceService(async (sessionId, purpose) => {
248
- const agent = ctx.agents.list().find((item) => item.session.id === sessionId);
249
- if (!agent)
250
- throw new Error('unknown-session');
251
- const bound = bindings.get(agent);
252
- if (purpose === 'reference' &&
253
- (!bound || bound.root !== configuredProjectRoot(config, agent)))
254
- throw new Error('environment-unbound');
255
- const environment = await (purpose === 'reference'
256
- ? bound.environment
257
- : resolveEnvironment(agent));
258
- if (!ctx.agents.list().includes(agent) ||
259
- bindings.get(agent)?.root !== configuredProjectRoot(config, agent))
260
- throw new Error('environment-changed');
261
- return environment;
262
- }, usage);
193
+ referenceService = new SemanticReferenceService(referenceEnvironmentResolver({
194
+ agent: (sessionId) => ctx.agents.list().find((item) => item.session.id === sessionId),
195
+ binding: (agent) => bindings.get(agent),
196
+ projectRoot: (agent) => configuredProjectRoot(config, agent),
197
+ resolve: resolveEnvironment,
198
+ workspace: (sessionId) => resolvePresentationWorkspace(ctx, sessionId),
199
+ }), usage);
263
200
  disposeReferences = registerSemanticReferenceRpc(ctx.connection, referenceService, async (_endpoint, payload, signal) => {
264
201
  try {
265
202
  return { ok: true, value: await browserService.read(payload, signal) };
@@ -273,24 +210,13 @@ export async function apply(ctx, config = {}) {
273
210
  });
274
211
  }
275
212
  catch (error) {
276
- browserService.dispose();
277
- await disposePresentation?.();
278
- await disposeCredentials?.();
279
- await credentialService.close();
280
- await referenceService?.close();
281
- disposePlugin?.();
282
- manager.dispose();
283
- disposeRuntimeShellEnvironment();
213
+ try {
214
+ await close();
215
+ }
216
+ catch (cleanupError) {
217
+ throw new AggregateError([error, cleanupError], 'Marivo installation and cleanup failed');
218
+ }
284
219
  throw error;
285
220
  }
286
- return async () => {
287
- browserService.dispose();
288
- await disposePresentation?.();
289
- await disposeCredentials?.();
290
- await credentialService.close();
291
- await disposeReferences?.();
292
- disposePlugin?.();
293
- manager.dispose();
294
- disposeRuntimeShellEnvironment();
295
- };
221
+ return close;
296
222
  }
@@ -0,0 +1,34 @@
1
+ import { buildPresentation } from "./build/index.js";
2
+ import { parsePresentationDocument, } from "./contracts/index.js";
3
+ import { presentationAssetPath, presentationSha256, readPresentationAsset } from "./files.js";
4
+ /** Read a fixed, receipt-verified snapshot; never access analysis or current. */
5
+ export async function readReceiptAsset(root, workspaceId, receipt, asset, signal) {
6
+ signal.throwIfAborted();
7
+ if (receipt.workspaceId !== workspaceId)
8
+ throw new Error('asset-owner-mismatch');
9
+ for (const file of Object.values(receipt.files))
10
+ if (file.path !== presentationAssetPath(root, receipt.reportId, receipt.buildId, file.asset))
11
+ throw new Error('asset-path-mismatch');
12
+ const documentBytes = await readPresentationAsset(root, receipt.reportId, receipt.buildId, 'presentation.json', signal);
13
+ if (documentBytes.length !== receipt.files.document.bytes ||
14
+ presentationSha256(documentBytes) !== receipt.files.document.sha256)
15
+ throw new Error('asset-digest-mismatch');
16
+ const document = parsePresentationDocument(JSON.parse(new TextDecoder('utf-8', { fatal: true }).decode(documentBytes)));
17
+ if (document.workspaceId !== workspaceId ||
18
+ document.reportId !== receipt.reportId ||
19
+ document.buildId !== receipt.buildId ||
20
+ document.title !== receipt.title)
21
+ throw new Error('asset-owner-mismatch');
22
+ const bytes = asset === 'presentation.json'
23
+ ? documentBytes
24
+ : receipt.files.html
25
+ ? await readPresentationAsset(root, receipt.reportId, receipt.buildId, asset, signal)
26
+ : (await buildPresentation(document)).htmlBytes;
27
+ const file = asset === 'presentation.json'
28
+ ? receipt.files.document
29
+ : (receipt.files.html ?? { bytes: bytes.length, sha256: presentationSha256(bytes) });
30
+ if (bytes.length !== file.bytes || presentationSha256(bytes) !== file.sha256)
31
+ throw new Error('asset-digest-mismatch');
32
+ signal.throwIfAborted();
33
+ return { bytes, sha256: file.sha256 };
34
+ }