@chengxianglibra/dsh-data-analysis 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/README.md +136 -99
  2. package/cordis.patch.yml +1 -1
  3. package/lib/bin/environment.js +8 -6
  4. package/lib/client/credentials/changes.js +74 -0
  5. package/lib/client/credentials/create-datasource.js +126 -22
  6. package/lib/client/credentials/credential-draft.js +42 -0
  7. package/lib/client/credentials/defaults.js +11 -0
  8. package/lib/client/credentials/descriptions.js +46 -0
  9. package/lib/client/credentials/install.js +209 -81
  10. package/lib/client/credentials/model.js +323 -51
  11. package/lib/client/credentials/report-publishing.js +96 -0
  12. package/lib/client/credentials/styles.js +13 -9
  13. package/lib/client/i18n/context.js +22 -0
  14. package/lib/client/i18n/copy.js +57 -0
  15. package/lib/client/i18n/credentials.js +475 -0
  16. package/lib/client/i18n/host.js +27 -0
  17. package/lib/client/i18n/navigation.js +41 -0
  18. package/lib/client/i18n/presentation.js +577 -0
  19. package/lib/client/i18n/python.js +36 -0
  20. package/lib/client/i18n/semantic.js +441 -0
  21. package/lib/client/input-context.js +21 -0
  22. package/lib/client/python-tool/card.js +35 -0
  23. package/lib/client/python-tool/execution-error.js +35 -0
  24. package/lib/client/python-tool/install.js +9 -0
  25. package/lib/client/python-tool/model.js +101 -0
  26. package/lib/client/right-tabs/install.js +535 -0
  27. package/lib/client/right-tabs/live-delivery.js +58 -0
  28. package/lib/client/right-tabs/navigation.js +47 -0
  29. package/lib/client/right-tabs/page.js +178 -0
  30. package/lib/client/right-tabs/styles.js +11 -0
  31. package/lib/client/rpc.js +5 -0
  32. package/lib/client/semantic-reference.js +10 -0
  33. package/lib/client/workspace-header-action.js +14 -1
  34. package/lib/client/workspace-shortcuts.js +26 -0
  35. package/lib/client.js +365 -242
  36. package/lib/compatibility.js +1 -1
  37. package/lib/datasource/authoring.js +43 -4
  38. package/lib/datasource/bridge-programs.js +23 -20
  39. package/lib/datasource/bridge.js +46 -3
  40. package/lib/datasource/changes-contract.js +41 -0
  41. package/lib/datasource/changes.js +49 -0
  42. package/lib/datasource/configure.js +39 -0
  43. package/lib/datasource/defaults.js +25 -0
  44. package/lib/datasource/index.js +2 -1
  45. package/lib/datasource/python-options.js +21 -0
  46. package/lib/datasource/python.js +116 -25
  47. package/lib/datasource/rpc.js +75 -13
  48. package/lib/datasource/service.js +286 -11
  49. package/lib/datasource/test.js +2 -1
  50. package/lib/disclosure/activation.js +19 -7
  51. package/lib/disclosure/execution-guidance.js +52 -0
  52. package/lib/disclosure/help.js +4 -1
  53. package/lib/environment/runtime.js +35 -34
  54. package/lib/environment/workspace.js +18 -1
  55. package/lib/lifecycle.js +33 -0
  56. package/lib/plugin-agents.js +154 -0
  57. package/lib/plugin.js +94 -172
  58. package/lib/presentation/asset.js +34 -0
  59. package/lib/presentation/assets/portable.js +14 -14
  60. package/lib/presentation/assets/static.js +344 -73
  61. package/lib/presentation/build/index.js +3 -2
  62. package/lib/presentation/commit.js +19 -12
  63. package/lib/presentation/contracts/asset-path.js +9 -0
  64. package/lib/presentation/contracts/catalog.js +62 -0
  65. package/lib/presentation/contracts/charts.js +0 -23
  66. package/lib/presentation/contracts/index.js +17 -5
  67. package/lib/presentation/contracts/types.js +1 -1
  68. package/lib/presentation/delivery.js +45 -30
  69. package/lib/presentation/export-html.js +214 -0
  70. package/lib/presentation/files.js +4 -5
  71. package/lib/presentation/projection/index.js +2 -1
  72. package/lib/presentation/projection/program.js +2 -9
  73. package/lib/presentation/projection/sql-code.js +13 -13
  74. package/lib/presentation/reports.js +114 -17
  75. package/lib/presentation/rpc.js +96 -58
  76. package/lib/presentation/tool.js +6 -10
  77. package/lib/report-publishing/adapters.js +111 -0
  78. package/lib/report-publishing/config.js +69 -0
  79. package/lib/report-publishing/service.js +180 -0
  80. package/lib/rpc.js +49 -0
  81. package/lib/semantic-browser/contracts.js +17 -0
  82. package/lib/semantic-browser/program.js +10 -0
  83. package/lib/semantic-browser/service.js +40 -6
  84. package/lib/semantic-reference/bridge.js +10 -1
  85. package/lib/semantic-reference/contracts.js +20 -6
  86. package/lib/semantic-reference/environment.js +56 -0
  87. package/lib/semantic-reference/labels.js +16 -0
  88. package/lib/semantic-reference/rpc.js +39 -18
  89. package/lib/semantic-reference/search.js +5 -4
  90. package/lib/semantic-reference/usage.js +12 -3
  91. package/lib/tool-lifecycle.js +22 -0
  92. package/lib/types/client/credentials/changes.d.ts +9 -0
  93. package/lib/types/client/credentials/create-datasource.d.ts +3 -1
  94. package/lib/types/client/credentials/credential-draft.d.ts +14 -0
  95. package/lib/types/client/credentials/defaults.d.ts +2 -0
  96. package/lib/types/client/credentials/descriptions.d.ts +1 -0
  97. package/lib/types/client/credentials/install.d.ts +19 -1
  98. package/lib/types/client/credentials/model.d.ts +31 -11
  99. package/lib/types/client/credentials/report-publishing.d.ts +5 -0
  100. package/lib/types/client/credentials/styles.d.ts +1 -1
  101. package/lib/types/client/i18n/context.d.ts +17 -0
  102. package/lib/types/client/i18n/copy.d.ts +806 -0
  103. package/lib/types/client/i18n/credentials.d.ts +475 -0
  104. package/lib/types/client/i18n/host.d.ts +16 -0
  105. package/lib/types/client/i18n/navigation.d.ts +41 -0
  106. package/lib/types/client/i18n/presentation.d.ts +577 -0
  107. package/lib/types/client/i18n/python.d.ts +19 -0
  108. package/lib/types/client/i18n/semantic.d.ts +441 -0
  109. package/lib/types/client/input-context.d.ts +19 -0
  110. package/lib/types/client/presentation/ask-dsh.d.ts +4 -8
  111. package/lib/types/client/presentation/catalog-model.d.ts +24 -0
  112. package/lib/types/client/presentation/catalog.d.ts +20 -0
  113. package/lib/types/client/presentation/chart-tooltip.d.ts +2 -2
  114. package/lib/types/client/presentation/context-reference.d.ts +12 -0
  115. package/lib/types/client/presentation/copy-context.d.ts +2 -2
  116. package/lib/types/client/presentation/delivery-model.d.ts +27 -6
  117. package/lib/types/client/presentation/delivery.d.ts +8 -7
  118. package/lib/types/client/presentation/download.d.ts +2 -0
  119. package/lib/types/client/presentation/export-menu.d.ts +26 -0
  120. package/lib/types/client/presentation/export-view.d.ts +63 -0
  121. package/lib/types/client/presentation/host-entry.d.ts +8 -2
  122. package/lib/types/client/presentation/icons.d.ts +1 -1
  123. package/lib/types/client/presentation/model.d.ts +14 -15
  124. package/lib/types/client/presentation/reader.d.ts +15 -2
  125. package/lib/types/client/presentation/reference-source.d.ts +9 -0
  126. package/lib/types/client/presentation/source-code-model.d.ts +2 -1
  127. package/lib/types/client/presentation/source-code.d.ts +0 -5
  128. package/lib/types/client/presentation/source-dialog.d.ts +1 -2
  129. package/lib/types/client/presentation/sources.d.ts +5 -5
  130. package/lib/types/client/presentation/styles.d.ts +1 -1
  131. package/lib/types/client/presentation/table.d.ts +5 -1
  132. package/lib/types/client/python-tool/card.d.ts +2 -0
  133. package/lib/types/client/python-tool/execution-error.d.ts +3 -0
  134. package/lib/types/client/python-tool/install.d.ts +2 -0
  135. package/lib/types/client/python-tool/model.d.ts +20 -0
  136. package/lib/types/client/right-tabs/install.d.ts +18 -0
  137. package/lib/types/client/right-tabs/live-delivery.d.ts +8 -0
  138. package/lib/types/client/right-tabs/navigation.d.ts +17 -0
  139. package/lib/types/client/right-tabs/page.d.ts +36 -0
  140. package/lib/types/client/right-tabs/styles.d.ts +2 -0
  141. package/lib/types/client/rpc.d.ts +3 -0
  142. package/lib/types/client/semantic-reference.d.ts +4 -0
  143. package/lib/types/client/workspace-header-action.d.ts +1 -1
  144. package/lib/types/client/workspace-shortcuts.d.ts +15 -0
  145. package/lib/types/client.d.ts +6 -3
  146. package/lib/types/compatibility.d.ts +1 -1
  147. package/lib/types/datasource/authoring.d.ts +10 -0
  148. package/lib/types/datasource/bridge.d.ts +23 -2
  149. package/lib/types/datasource/changes-contract.d.ts +18 -0
  150. package/lib/types/datasource/changes.d.ts +15 -0
  151. package/lib/types/datasource/configure.d.ts +7 -0
  152. package/lib/types/datasource/defaults.d.ts +7 -0
  153. package/lib/types/datasource/index.d.ts +6 -1
  154. package/lib/types/datasource/python-options.d.ts +9 -0
  155. package/lib/types/datasource/python.d.ts +17 -2
  156. package/lib/types/datasource/rpc.d.ts +2 -1
  157. package/lib/types/datasource/service.d.ts +48 -5
  158. package/lib/types/datasource/test.d.ts +1 -1
  159. package/lib/types/disclosure/activation.d.ts +2 -1
  160. package/lib/types/disclosure/execution-guidance.d.ts +6 -0
  161. package/lib/types/disclosure/help.d.ts +1 -1
  162. package/lib/types/environment/types.d.ts +2 -1
  163. package/lib/types/environment/workspace.d.ts +1 -0
  164. package/lib/types/lifecycle.d.ts +8 -0
  165. package/lib/types/plugin-agents.d.ts +27 -0
  166. package/lib/types/plugin.d.ts +12 -28
  167. package/lib/types/presentation/asset.d.ts +6 -0
  168. package/lib/types/presentation/build/index.d.ts +7 -2
  169. package/lib/types/presentation/commit.d.ts +1 -1
  170. package/lib/types/presentation/contracts/asset-path.d.ts +2 -0
  171. package/lib/types/presentation/contracts/catalog.d.ts +27 -0
  172. package/lib/types/presentation/contracts/charts.d.ts +0 -2
  173. package/lib/types/presentation/contracts/types.d.ts +7 -4
  174. package/lib/types/presentation/delivery.d.ts +1 -1
  175. package/lib/types/presentation/export-html.d.ts +13 -0
  176. package/lib/types/presentation/reports.d.ts +6 -1
  177. package/lib/types/presentation/rpc.d.ts +3 -2
  178. package/lib/types/presentation/tool.d.ts +2 -2
  179. package/lib/types/report-publishing/adapters.d.ts +6 -0
  180. package/lib/types/report-publishing/config.d.ts +22 -0
  181. package/lib/types/report-publishing/service.d.ts +68 -0
  182. package/lib/types/rpc.d.ts +3 -0
  183. package/lib/types/semantic-browser/contracts.d.ts +7 -0
  184. package/lib/types/semantic-browser/service.d.ts +7 -1
  185. package/lib/types/semantic-reference/bridge.d.ts +1 -0
  186. package/lib/types/semantic-reference/contracts.d.ts +5 -2
  187. package/lib/types/semantic-reference/environment.d.ts +22 -0
  188. package/lib/types/semantic-reference/labels.d.ts +16 -0
  189. package/lib/types/semantic-reference/rpc.d.ts +1 -1
  190. package/lib/types/semantic-reference/search.d.ts +1 -1
  191. package/lib/types/tool-lifecycle.d.ts +4 -0
  192. package/lib/types/workspace-identity.d.ts +6 -0
  193. package/lib/workspace-identity.js +9 -0
  194. package/package.json +83 -61
  195. package/python/presentation-kit/dist/dsh_data_analysis_presentation_kit-1.1.0-py3-none-any.whl +0 -0
  196. package/skills/dsh-data-analysis-files/SKILL.md +32 -0
  197. package/skills/dsh-data-analysis-files/references/examples.md +93 -0
  198. package/skills/dsh-data-analysis-presentation/SKILL.md +10 -6
  199. package/skills/dsh-data-analysis-presentation/references/examples/artifact.draft.json +13 -4
  200. package/skills/dsh-data-analysis-presentation/references/examples/charts.draft.json +3 -2
  201. package/skills/dsh-data-analysis-presentation/references/examples/comparison.draft.json +3 -2
  202. package/skills/dsh-data-analysis-presentation/references/examples/computed-sources.draft.json +12 -5
  203. package/skills/dsh-data-analysis-presentation/references/examples/computed.draft.json +3 -2
  204. package/skills/dsh-data-analysis-presentation/references/examples/interaction.draft.json +3 -2
  205. package/skills/dsh-data-analysis-presentation/references/examples/source-only.draft.json +12 -4
  206. package/skills/dsh-data-analysis-presentation/references/examples.md +3 -1
  207. package/skills/dsh-data-analysis-presentation/references/export-html.md +7 -0
  208. package/skills/dsh-data-analysis-presentation/references/interaction.md +1 -1
  209. package/skills/dsh-data-analysis-presentation/references/narrative.md +1 -1
  210. package/skills/dsh-data-analysis-presentation/references/publishing.md +3 -0
  211. package/skills/dsh-data-analysis-presentation/references/schema.md +25 -1
  212. package/lib/types/client/presentation/install.d.ts +0 -15
package/lib/plugin.js CHANGED
@@ -1,28 +1,38 @@
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 { resolvePresentationWorkspace } from "./workspace-identity.js";
9
+ export { installMarivoPlugin } from "./plugin-agents.js";
10
+ export { resolvePresentationWorkspace } from "./workspace-identity.js";
11
+ import { referenceEnvironmentResolver } from "./semantic-reference/environment.js";
1
12
  /** Cordis lifecycle adapter for the Web-profile shared Marivo Runtime. */
2
13
  import process from 'node:process';
3
14
  import { fileURLToPath } from 'node:url';
4
15
  import { apply as installSkillFilesystem } from '@deepseek-ai/dsh-skill-filesystem';
5
16
  import { WorkspaceId } from '@deepseek-ai/dsh-workspace';
6
17
  import z from '@deepseek-ai/schemastery';
7
- import { createMarivoBridgeSet } from "./bridges.js";
8
18
  import { MarivoDatasourceBridge } from "./datasource/bridge.js";
9
- import { registerMarivoDatasourceTestTool } from "./datasource/index.js";
10
- import { registerMarivoPythonTool } from "./datasource/python.js";
19
+ import { DEFAULT_PYTHON_MAX_TIMEOUT_MS, DEFAULT_PYTHON_TIMEOUT_MS, resolvePythonOptions, } from "./datasource/python-options.js";
11
20
  import { registerCredentialRpc } from "./datasource/rpc.js";
12
21
  import { MarivoCredentialService } from "./datasource/service.js";
13
22
  import { registerMarivoRuntimeShellEnvironment } from "./datasource/shell-env.js";
14
23
  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";
24
+ import { createSharedMarivoRuntimeRunner, DEFAULT_SHARED_RUNTIME_INSTALL_TIMEOUT_MS, ensureSharedMarivoRuntime, MarivoWorkspaceEnvironmentManager, } from "./environment/index.js";
25
+ import { MarivoPresentationFileService, registerMarivoPresentationRpc, } from "./presentation/index.js";
18
26
  import { browserFailure, SemanticBrowserService } from "./semantic-browser/service.js";
19
27
  import { registerSemanticReferenceRpc, SemanticReferenceService, SemanticReferenceUsage, } from "./semantic-reference/index.js";
28
+ export { MARIVO_DATASOURCE_CREDENTIAL_PROMPT } from "./disclosure/execution-guidance.js";
20
29
  /** Cordis plugin name used by loader diagnostics and lifecycle logs. */
21
30
  export const name = 'dsh-data-analysis';
22
31
  /** Services that must exist before the plugin binds and watches Agent scopes. */
23
32
  export const inject = [
24
33
  'agents',
25
34
  'connection',
35
+ 'typert',
26
36
  'storageDomain',
27
37
  'workspaceRegistry',
28
38
  'credentials',
@@ -31,28 +41,18 @@ export const inject = [
31
41
  'systemPrompt',
32
42
  'tools',
33
43
  ];
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
44
  /** Cordis loader schema. Runtime defaults are resolved in {@link apply}. */
50
45
  export const Config = z.object({
46
+ // Defer validation to authoring so loader errors cannot echo configured values.
47
+ datasourceDefaults: z.any(),
48
+ reportPublishing: z.any(),
49
+ pythonTimeoutMs: z.number().default(DEFAULT_PYTHON_TIMEOUT_MS),
50
+ pythonMaxTimeoutMs: z.number().default(DEFAULT_PYTHON_MAX_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,51 @@ 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 closing;
203
104
  const browserService = new SemanticBrowserService({
204
105
  getWorkspace: (id) => ctx.workspaceRegistry.get(WorkspaceId(id)),
205
106
  projectRoot: (workspace) => config.projectRoot ?? process.env.DSH_DATA_ANALYSIS_PROJECT_ROOT ?? workspace.path,
206
107
  resolve: (root) => manager.resolve(root),
207
108
  });
109
+ const close = () => {
110
+ if (closing)
111
+ return closing;
112
+ closing = finishCleanup([
113
+ () => agentInstallation?.close(),
114
+ () => disposeCredentials?.(),
115
+ disposeChanges,
116
+ () => disposePublishing?.(),
117
+ () => disposePresentation?.(),
118
+ () => disposeReferences?.(),
119
+ () => browserService.close(),
120
+ () => credentialService.close(),
121
+ () => referenceService?.close(),
122
+ ]).then(() => finishCleanup([() => manager.close(), disposeRuntimeShellEnvironment]), async (error) => {
123
+ await finishCleanup([
124
+ () => manager.close(),
125
+ disposeRuntimeShellEnvironment,
126
+ () => {
127
+ throw error;
128
+ },
129
+ ]);
130
+ });
131
+ void closing.catch(() => { });
132
+ return closing;
133
+ };
208
134
  try {
135
+ unregisterChanges = ctx.typert.register(credentialChangesHost);
209
136
  installSkillFilesystem(ctx, {
210
137
  providerName: 'dsh-data-analysis-marivo',
211
138
  includeDefaultRoots: false,
@@ -219,10 +146,25 @@ export async function apply(ctx, config = {}) {
219
146
  watch: false,
220
147
  });
221
148
  const helpBridge = new MarivoHelpBridge(createSharedMarivoRuntimeRunner(runtime));
222
- disposePlugin = installMarivoPlugin(ctx, resolveEnvironment, {
149
+ const presentationFiles = new MarivoPresentationFileService(async (sessionId) => resolvePresentationWorkspace(ctx, sessionId), async (id) => {
150
+ const workspace = ctx.workspaceRegistry.get(WorkspaceId(id));
151
+ return workspace ? { id: String(workspace.id), path: workspace.path } : undefined;
152
+ });
153
+ disposePresentation = registerMarivoPresentationRpc(ctx.connection, presentationFiles);
154
+ const reportPublishing = new ReportPublishingService(publishingConfig, ctx.credentials, presentationFiles, undefined, (id) => {
155
+ const workspace = ctx.workspaceRegistry.get(WorkspaceId(id));
156
+ if (!workspace)
157
+ throw new Error('workspace-unavailable');
158
+ return workspace.title;
159
+ });
160
+ disposePublishing = registerPublishingCredentials(ctx.connection, reportPublishing, (id) => Boolean(ctx.workspaceRegistry.get(WorkspaceId(id))));
161
+ agentInstallation = createMarivoAgentInstallation(ctx, resolveEnvironment, {
162
+ ...pythonOptions,
223
163
  helpBridgeSource: helpBridge,
164
+ ...(publishingConfig ? { reportPublishing } : {}),
224
165
  credentialService,
225
166
  });
167
+ agentInstallation.install();
226
168
  disposeCredentials = registerCredentialRpc(ctx.connection, credentialService, async (id) => {
227
169
  const workspace = ctx.workspaceRegistry.get(WorkspaceId(id));
228
170
  if (!workspace)
@@ -232,11 +174,11 @@ export async function apply(ctx, config = {}) {
232
174
  const current = ctx.workspaceRegistry.get(WorkspaceId(id));
233
175
  if (!current ||
234
176
  current.path !== workspace.path ||
235
- (config.projectRoot ?? process.env.DSH_DATA_ANALYSIS_PROJECT_ROOT ?? current.path) !== root)
177
+ (config.projectRoot ?? process.env.DSH_DATA_ANALYSIS_PROJECT_ROOT ?? current.path) !==
178
+ root)
236
179
  throw new Error('Workspace changed');
237
180
  return new MarivoDatasourceBridge(environment);
238
- });
239
- disposePresentation = registerMarivoPresentationRpc(ctx.connection, new MarivoPresentationFileService(async (sessionId) => resolvePresentationWorkspace(ctx, sessionId)));
181
+ }, config.datasourceDefaults);
240
182
  let lastDiagnostic = -Infinity;
241
183
  const usage = new SemanticReferenceUsage(ctx.storageDomain, Date.now, () => {
242
184
  if (Date.now() - lastDiagnostic >= 30_000) {
@@ -244,22 +186,13 @@ export async function apply(ctx, config = {}) {
244
186
  console.warn('dsh-data-analysis semantic-reference usage unavailable');
245
187
  }
246
188
  });
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);
189
+ referenceService = new SemanticReferenceService(referenceEnvironmentResolver({
190
+ agent: (sessionId) => ctx.agents.list().find((item) => item.session.id === sessionId),
191
+ binding: (agent) => bindings.get(agent),
192
+ projectRoot: (agent) => configuredProjectRoot(config, agent),
193
+ resolve: resolveEnvironment,
194
+ workspace: (sessionId) => resolvePresentationWorkspace(ctx, sessionId),
195
+ }), usage);
263
196
  disposeReferences = registerSemanticReferenceRpc(ctx.connection, referenceService, async (_endpoint, payload, signal) => {
264
197
  try {
265
198
  return { ok: true, value: await browserService.read(payload, signal) };
@@ -273,24 +206,13 @@ export async function apply(ctx, config = {}) {
273
206
  });
274
207
  }
275
208
  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();
209
+ try {
210
+ await close();
211
+ }
212
+ catch (cleanupError) {
213
+ throw new AggregateError([error, cleanupError], 'Marivo installation and cleanup failed');
214
+ }
284
215
  throw error;
285
216
  }
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
- };
217
+ return close;
296
218
  }
@@ -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
+ }