@chengxianglibra/dsh-data-analysis 0.1.1 → 0.1.2

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 (195) hide show
  1. package/README.md +83 -34
  2. package/lib/bin/environment.js +1 -1
  3. package/lib/bin/presentation-lint.js +21 -0
  4. package/lib/bridges.js +2 -2
  5. package/lib/client/credentials/create-datasource.js +67 -0
  6. package/lib/client/credentials/install.js +191 -0
  7. package/lib/client/credentials/model.js +494 -0
  8. package/lib/client/credentials/styles.js +17 -0
  9. package/lib/client/workspace-header-action.js +11 -0
  10. package/lib/client.js +1133 -736
  11. package/lib/compatibility.js +1 -1
  12. package/lib/datasource/authoring.js +70 -0
  13. package/lib/datasource/bridge-programs.js +30 -18
  14. package/lib/datasource/bridge.js +100 -8
  15. package/lib/datasource/index.js +3 -2
  16. package/lib/datasource/python.js +115 -0
  17. package/lib/datasource/resolver-program.js +112 -0
  18. package/lib/datasource/rpc.js +88 -0
  19. package/lib/datasource/service.js +687 -0
  20. package/lib/datasource/shell-env.js +2 -333
  21. package/lib/datasource/test.js +24 -51
  22. package/lib/environment/binding.js +54 -9
  23. package/lib/environment/runtime.js +89 -78
  24. package/lib/environment/subprocess.js +6 -1
  25. package/lib/environment/summary.js +3 -3
  26. package/lib/environment/workspace.js +5 -0
  27. package/lib/index.js +1 -1
  28. package/lib/plugin.js +168 -51
  29. package/lib/presentation/assets/portable.js +606 -0
  30. package/lib/presentation/assets/static.js +945 -0
  31. package/lib/presentation/build/index.js +71 -0
  32. package/lib/presentation/commit.js +156 -0
  33. package/lib/presentation/contracts/charts.js +476 -0
  34. package/lib/presentation/contracts/editing.js +58 -0
  35. package/lib/presentation/contracts/index.js +651 -0
  36. package/lib/presentation/contracts/interaction.js +62 -0
  37. package/lib/presentation/contracts/types.js +15 -0
  38. package/lib/presentation/contracts/values.js +28 -0
  39. package/lib/presentation/delivery.js +56 -0
  40. package/lib/presentation/files.js +83 -0
  41. package/lib/presentation/index.js +6 -0
  42. package/lib/presentation/lint.js +46 -0
  43. package/lib/presentation/projection/files.js +85 -0
  44. package/lib/presentation/projection/index.js +195 -0
  45. package/lib/presentation/projection/program.js +144 -0
  46. package/lib/presentation/projection/sql-code.js +79 -0
  47. package/lib/presentation/receipt.js +34 -0
  48. package/lib/presentation/reports.js +122 -0
  49. package/lib/presentation/rpc.js +148 -0
  50. package/lib/presentation/tool.js +122 -0
  51. package/lib/python-execution-contracts.js +1 -0
  52. package/lib/python-execution.js +232 -0
  53. package/lib/semantic-browser/contracts.js +80 -0
  54. package/lib/semantic-browser/definition.js +64 -0
  55. package/lib/semantic-browser/program.js +119 -0
  56. package/lib/semantic-browser/service.js +72 -0
  57. package/lib/semantic-reference/bridge.js +118 -0
  58. package/lib/semantic-reference/contracts.js +113 -0
  59. package/lib/semantic-reference/index.js +3 -0
  60. package/lib/semantic-reference/rpc.js +122 -0
  61. package/lib/semantic-reference/search.js +94 -0
  62. package/lib/semantic-reference/usage.js +143 -0
  63. package/lib/types/bridges.d.ts +2 -2
  64. package/lib/types/client/credentials/create-datasource.d.ts +5 -0
  65. package/lib/types/client/credentials/install.d.ts +1 -0
  66. package/lib/types/client/credentials/model.d.ts +59 -0
  67. package/lib/types/client/credentials/styles.d.ts +1 -0
  68. package/lib/types/client/presentation/ask-dsh.d.ts +9 -0
  69. package/lib/types/client/presentation/chart-explorer.d.ts +12 -0
  70. package/lib/types/client/presentation/chart-geometry.d.ts +30 -0
  71. package/lib/types/client/presentation/chart-renderer.d.ts +10 -0
  72. package/lib/types/client/presentation/chart-specials.d.ts +10 -0
  73. package/lib/types/client/presentation/chart-styles.d.ts +1 -0
  74. package/lib/types/client/presentation/chart-tooltip.d.ts +13 -0
  75. package/lib/types/client/presentation/chart-view.d.ts +23 -0
  76. package/lib/types/client/presentation/copy-context.d.ts +5 -0
  77. package/lib/types/client/presentation/delivery-model.d.ts +54 -0
  78. package/lib/types/client/presentation/delivery.d.ts +77 -0
  79. package/lib/types/client/presentation/editor-controls.d.ts +12 -0
  80. package/lib/types/client/presentation/global-filters.d.ts +6 -0
  81. package/lib/types/client/presentation/host-entry.d.ts +12 -0
  82. package/lib/types/client/presentation/icons.d.ts +4 -0
  83. package/lib/types/client/presentation/install.d.ts +15 -0
  84. package/lib/types/client/presentation/markdown.d.ts +5 -0
  85. package/lib/types/client/presentation/model.d.ts +62 -0
  86. package/lib/types/client/presentation/reader.d.ts +11 -0
  87. package/lib/types/client/presentation/source-code-model.d.ts +16 -0
  88. package/lib/types/client/presentation/source-code.d.ts +11 -0
  89. package/lib/types/client/presentation/source-dialog.d.ts +13 -0
  90. package/lib/types/client/presentation/source-facts.d.ts +20 -0
  91. package/lib/types/client/presentation/source-format.d.ts +5 -0
  92. package/lib/types/client/presentation/source-highlight.d.ts +7 -0
  93. package/lib/types/client/presentation/sources.d.ts +13 -0
  94. package/lib/types/client/presentation/styles.d.ts +1 -0
  95. package/lib/types/client/presentation/table.d.ts +12 -0
  96. package/lib/types/client/presentation/use-element-width.d.ts +5 -0
  97. package/lib/types/client/workspace-header-action.d.ts +7 -0
  98. package/lib/types/client.d.ts +4 -122
  99. package/lib/types/compatibility.d.ts +2 -2
  100. package/lib/types/datasource/authoring.d.ts +25 -0
  101. package/lib/types/datasource/bridge.d.ts +21 -3
  102. package/lib/types/datasource/index.d.ts +3 -5
  103. package/lib/types/datasource/python.d.ts +4 -0
  104. package/lib/types/datasource/resolver-program.d.ts +6 -0
  105. package/lib/types/datasource/rpc.d.ts +4 -0
  106. package/lib/types/datasource/service.d.ts +112 -0
  107. package/lib/types/datasource/shell-env.d.ts +0 -29
  108. package/lib/types/datasource/test.d.ts +6 -29
  109. package/lib/types/environment/binding.d.ts +5 -1
  110. package/lib/types/environment/errors.d.ts +1 -1
  111. package/lib/types/environment/runtime.d.ts +5 -5
  112. package/lib/types/environment/types.d.ts +13 -2
  113. package/lib/types/index.d.ts +1 -1
  114. package/lib/types/plugin.d.ts +11 -5
  115. package/lib/types/presentation/build/index.d.ts +11 -0
  116. package/lib/types/presentation/commit.d.ts +4 -0
  117. package/lib/types/presentation/contracts/charts.d.ts +13 -0
  118. package/lib/types/presentation/contracts/editing.d.ts +8 -0
  119. package/lib/types/presentation/contracts/index.d.ts +11 -0
  120. package/lib/types/presentation/contracts/interaction.d.ts +8 -0
  121. package/lib/types/presentation/contracts/types.d.ts +252 -0
  122. package/lib/types/presentation/contracts/values.d.ts +9 -0
  123. package/lib/types/presentation/delivery.d.ts +3 -0
  124. package/lib/types/presentation/files.d.ts +7 -0
  125. package/lib/types/presentation/index.d.ts +6 -0
  126. package/lib/types/presentation/lint.d.ts +13 -0
  127. package/lib/types/presentation/projection/files.d.ts +2 -0
  128. package/lib/types/presentation/projection/index.d.ts +20 -0
  129. package/lib/types/presentation/projection/program.d.ts +2 -0
  130. package/lib/types/presentation/projection/sql-code.d.ts +2 -0
  131. package/lib/types/presentation/receipt.d.ts +14 -0
  132. package/lib/types/presentation/reports.d.ts +5 -0
  133. package/lib/types/presentation/rpc.d.ts +24 -0
  134. package/lib/types/presentation/tool.d.ts +12 -0
  135. package/lib/types/python-execution-contracts.d.ts +10 -0
  136. package/lib/types/python-execution.d.ts +10 -0
  137. package/lib/types/semantic-browser/contracts.d.ts +40 -0
  138. package/lib/types/semantic-browser/definition.d.ts +20 -0
  139. package/lib/types/semantic-browser/program.d.ts +2 -0
  140. package/lib/types/semantic-browser/service.d.ts +20 -0
  141. package/lib/types/semantic-reference/bridge.d.ts +11 -0
  142. package/lib/types/semantic-reference/contracts.d.ts +51 -0
  143. package/lib/types/semantic-reference/index.d.ts +3 -0
  144. package/lib/types/semantic-reference/rpc.d.ts +15 -0
  145. package/lib/types/semantic-reference/search.d.ts +10 -0
  146. package/lib/types/semantic-reference/usage.d.ts +43 -0
  147. package/package.json +43 -19
  148. package/python/presentation-kit/dist/dsh_data_analysis_presentation_kit-1.1.0-py3-none-any.whl +0 -0
  149. package/skills/dsh-data-analysis-presentation/SKILL.md +24 -0
  150. package/skills/dsh-data-analysis-presentation/references/chart-examples.md +18 -0
  151. package/skills/dsh-data-analysis-presentation/references/charts.md +55 -0
  152. package/skills/dsh-data-analysis-presentation/references/examples/artifact.draft.json +27 -0
  153. package/skills/dsh-data-analysis-presentation/references/examples/charts-bins.dataset.json +33 -0
  154. package/skills/dsh-data-analysis-presentation/references/examples/charts-boxes.dataset.json +45 -0
  155. package/skills/dsh-data-analysis-presentation/references/examples/charts-composition.dataset.json +33 -0
  156. package/skills/dsh-data-analysis-presentation/references/examples/charts-funnel.dataset.json +27 -0
  157. package/skills/dsh-data-analysis-presentation/references/examples/charts-precision.dataset.json +21 -0
  158. package/skills/dsh-data-analysis-presentation/references/examples/charts-series.dataset.json +58 -0
  159. package/skills/dsh-data-analysis-presentation/references/examples/charts-shares.dataset.json +33 -0
  160. package/skills/dsh-data-analysis-presentation/references/examples/charts-waterfall.dataset.json +44 -0
  161. package/skills/dsh-data-analysis-presentation/references/examples/charts.draft.json +392 -0
  162. package/skills/dsh-data-analysis-presentation/references/examples/comparison-selected.dataset.json +24 -0
  163. package/skills/dsh-data-analysis-presentation/references/examples/comparison-summary.dataset.json +22 -0
  164. package/skills/dsh-data-analysis-presentation/references/examples/comparison.dataset.json +18 -0
  165. package/skills/dsh-data-analysis-presentation/references/examples/comparison.draft.json +79 -0
  166. package/skills/dsh-data-analysis-presentation/references/examples/computed-sources.draft.json +35 -0
  167. package/skills/dsh-data-analysis-presentation/references/examples/computed.dataset.json +12 -0
  168. package/skills/dsh-data-analysis-presentation/references/examples/computed.draft.json +57 -0
  169. package/skills/dsh-data-analysis-presentation/references/examples/interaction-detail.dataset.json +30 -0
  170. package/skills/dsh-data-analysis-presentation/references/examples/interaction-summary.dataset.json +22 -0
  171. package/skills/dsh-data-analysis-presentation/references/examples/interaction.draft.json +278 -0
  172. package/skills/dsh-data-analysis-presentation/references/examples/source-only.draft.json +19 -0
  173. package/skills/dsh-data-analysis-presentation/references/examples/write-charts.py +73 -0
  174. package/skills/dsh-data-analysis-presentation/references/examples/write-comparison.py +42 -0
  175. package/skills/dsh-data-analysis-presentation/references/examples/write-computed.py +21 -0
  176. package/skills/dsh-data-analysis-presentation/references/examples/write-interaction.py +33 -0
  177. package/skills/dsh-data-analysis-presentation/references/examples.md +42 -0
  178. package/skills/dsh-data-analysis-presentation/references/interaction.md +56 -0
  179. package/skills/dsh-data-analysis-presentation/references/narrative.md +37 -0
  180. package/skills/dsh-data-analysis-presentation/references/schema.md +138 -0
  181. package/lib/datasource/access.js +0 -60
  182. package/lib/datasource/credentials.js +0 -24
  183. package/lib/evidence/bridge-program.js +0 -153
  184. package/lib/evidence/bridge.js +0 -166
  185. package/lib/evidence/index.js +0 -2
  186. package/lib/evidence/sources.js +0 -263
  187. package/lib/types/datasource/access.d.ts +0 -27
  188. package/lib/types/evidence/bridge.d.ts +0 -40
  189. package/lib/types/evidence/index.d.ts +0 -4
  190. package/lib/types/evidence/sources.d.ts +0 -57
  191. package/python/report-kit/dist/dsh_data_analysis_report_kit-3.0.0-py3-none-any.whl +0 -0
  192. package/skills/dsh-data-analysis-report/SKILL.md +0 -57
  193. package/skills/dsh-data-analysis-report/assets/marivo-artifact.js +0 -113
  194. package/skills/dsh-data-analysis-report/assets/marivo-session-dag.js +0 -1201
  195. package/skills/dsh-data-analysis-report/assets/report-data.js +0 -449
@@ -1,13 +1,6 @@
1
- import { randomBytes } from 'node:crypto';
2
- import { credentialRef, isCredentialRefName, } from '@deepseek-ai/dsh-credentials';
1
+ import { isCredentialRefName } from '@deepseek-ai/dsh-credentials';
3
2
  import { MarivoEnvironmentError } from "../environment/index.js";
4
- import { resolveMarivoDatasourceBridge, } from "./bridge.js";
5
3
  export const MARIVO_CREDENTIAL_STORAGE_PREFIX = 'DSH_DATA_ANALYSIS_CREDENTIAL_';
6
- export const MARIVO_CREDENTIAL_LEASE_PREFIX = '# dsh-marivo-credential-lease:';
7
- export const DEFAULT_MARIVO_CREDENTIAL_LEASE_TTL_MS = 30 * 60_000;
8
- export const DEFAULT_MARIVO_CREDENTIAL_LEASE_MAX_USES = 64;
9
- const TOKEN_PATTERN = /^[A-Za-z0-9_-]{43}$/;
10
- const SHELL_TOOL_NAMES = new Set(['bash', 'pwsh']);
11
4
  const HOST_SHELL_ENVIRONMENT_NAMES = new Set([
12
5
  'DSH_HOME',
13
6
  'DSH_SESSION_ID',
@@ -56,331 +49,7 @@ export function marivoCredentialStorageRef(ref) {
56
49
  .join('');
57
50
  return `${MARIVO_CREDENTIAL_STORAGE_PREFIX}${encoded}`;
58
51
  }
59
- function credentialBindings(refs) {
60
- return Object.freeze([...new Set(refs)].map((environmentRef) => ({
61
- environmentRef,
62
- storageRef: marivoCredentialStorageRef(environmentRef),
63
- })));
64
- }
65
- function bashPrelude(token, bindings) {
66
- return [
67
- `${MARIVO_CREDENTIAL_LEASE_PREFIX}${token}`,
68
- ...bindings.flatMap(({ environmentRef, storageRef }) => [
69
- `export ${environmentRef}="\${${storageRef}}"`,
70
- `unset ${storageRef}`,
71
- ]),
72
- 'export MARIVO_PERSIST_CREDENTIALS=0',
73
- ].join('\n');
74
- }
75
- function pwshPrelude(token, bindings) {
76
- return [
77
- `${MARIVO_CREDENTIAL_LEASE_PREFIX}${token}`,
78
- ...bindings.flatMap(({ environmentRef, storageRef }) => [
79
- `$env:${environmentRef} = $env:${storageRef}`,
80
- `Remove-Item Env:${storageRef}`,
81
- ]),
82
- "$env:MARIVO_PERSIST_CREDENTIALS = '0'",
83
- ].join('\n');
84
- }
85
- function leaseFailure(code, message, details = {}) {
86
- return new MarivoEnvironmentError(code, message, details);
87
- }
88
- function commandArguments(execution) {
89
- return typeof execution.arguments === 'object' &&
90
- execution.arguments !== null &&
91
- !Array.isArray(execution.arguments)
92
- ? execution.arguments
93
- : undefined;
94
- }
95
- function hasCredentialStorageRefAt(command, offset, caseInsensitive) {
96
- const candidate = caseInsensitive ? command.slice(offset).toUpperCase() : command.slice(offset);
97
- if (!candidate.startsWith(MARIVO_CREDENTIAL_STORAGE_PREFIX))
98
- return false;
99
- let cursor = MARIVO_CREDENTIAL_STORAGE_PREFIX.length;
100
- const firstHex = cursor;
101
- while (cursor < candidate.length && /[A-F0-9]/.test(candidate[cursor] ?? ''))
102
- cursor++;
103
- return (cursor > firstHex &&
104
- (cursor === candidate.length || !/[A-Z0-9_]/i.test(candidate[cursor] ?? '')));
105
- }
106
- function isBashCommentStart(command, offset) {
107
- if (offset === 0)
108
- return true;
109
- return /[\s;&|()]/.test(command[offset - 1] ?? '');
110
- }
111
- function containsCredentialDereference(execution) {
112
- const command = commandArguments(execution)?.command;
113
- if (typeof command !== 'string')
114
- return false;
115
- const pwsh = execution.name === 'pwsh';
116
- let quote;
117
- let comment = false;
118
- for (let index = 0; index < command.length; index++) {
119
- const character = command[index];
120
- if (comment) {
121
- if (character === '\n' || character === '\r')
122
- comment = false;
123
- continue;
124
- }
125
- if (quote === "'") {
126
- if (character === "'") {
127
- if (pwsh && command[index + 1] === "'")
128
- index++;
129
- else
130
- quote = undefined;
131
- }
132
- continue;
133
- }
134
- if (character === (pwsh ? '`' : '\\')) {
135
- index++;
136
- continue;
137
- }
138
- if (character === '#' && quote === undefined && (pwsh || isBashCommentStart(command, index))) {
139
- comment = true;
140
- continue;
141
- }
142
- if (character === "'" && quote === undefined) {
143
- quote = "'";
144
- continue;
145
- }
146
- if (character === '"') {
147
- quote = quote === '"' ? undefined : '"';
148
- continue;
149
- }
150
- if (character !== '$')
151
- continue;
152
- if (pwsh) {
153
- if (command.slice(index + 1, index + 5).toLowerCase() === 'env:' &&
154
- hasCredentialStorageRefAt(command, index + 5, true)) {
155
- return true;
156
- }
157
- if (command.slice(index + 1, index + 6).toLowerCase() === '{env:' &&
158
- hasCredentialStorageRefAt(command, index + 6, true)) {
159
- return true;
160
- }
161
- continue;
162
- }
163
- const refOffset = command[index + 1] === '{' ? index + 2 : index + 1;
164
- if (hasCredentialStorageRefAt(command, refOffset, false))
165
- return true;
166
- }
167
- return false;
168
- }
169
- function leaseToken(execution) {
170
- const command = commandArguments(execution)?.command;
171
- if (typeof command !== 'string')
172
- return undefined;
173
- const firstLine = command.split(/\r?\n/, 1)[0] ?? '';
174
- const candidate = firstLine.startsWith(MARIVO_CREDENTIAL_LEASE_PREFIX)
175
- ? firstLine.slice(MARIVO_CREDENTIAL_LEASE_PREFIX.length)
176
- : undefined;
177
- if (candidate !== undefined && TOKEN_PATTERN.test(candidate))
178
- return candidate;
179
- if (firstLine.includes('dsh-marivo-credential-')) {
180
- throw leaseFailure('shell-credential-lease-invalid', 'Marivo shell credential lease marker is malformed or unsupported');
181
- }
182
- return undefined;
183
- }
184
- function isHarnessPersistentShell(agent, name) {
185
- const definition = agent.ctx.tools.get(name, agent);
186
- return definition?.output.schema.type === 'string';
187
- }
188
52
  export function assertMarivoCredentialReferences(refs) {
189
- for (const ref of refs) {
53
+ for (const ref of refs)
190
54
  marivoCredentialStorageRef(ref);
191
- }
192
- }
193
- /** Own bounded leases and expose credential values only to each admitted Shell execution. */
194
- export class MarivoShellCredentialLeases {
195
- #ctx;
196
- #credentials;
197
- #ttlMs;
198
- #maxUses;
199
- #leases = new Map();
200
- #executionValues = new Map();
201
- #contributors = new Map();
202
- #disposed = false;
203
- constructor(ctx, credentials, options = {}) {
204
- const ttlMs = options.ttlMs ?? DEFAULT_MARIVO_CREDENTIAL_LEASE_TTL_MS;
205
- const maxUses = options.maxUses ?? DEFAULT_MARIVO_CREDENTIAL_LEASE_MAX_USES;
206
- if (!Number.isSafeInteger(ttlMs) ||
207
- ttlMs < 1 ||
208
- ttlMs > DEFAULT_MARIVO_CREDENTIAL_LEASE_TTL_MS) {
209
- throw new TypeError(`Marivo shell credential lease ttlMs must be an integer from 1 to ${DEFAULT_MARIVO_CREDENTIAL_LEASE_TTL_MS}`);
210
- }
211
- if (!Number.isSafeInteger(maxUses) ||
212
- maxUses < 1 ||
213
- maxUses > DEFAULT_MARIVO_CREDENTIAL_LEASE_MAX_USES) {
214
- throw new TypeError(`Marivo shell credential lease maxUses must be an integer from 1 to ${DEFAULT_MARIVO_CREDENTIAL_LEASE_MAX_USES}`);
215
- }
216
- this.#ctx = ctx;
217
- this.#credentials = credentials;
218
- this.#ttlMs = ttlMs;
219
- this.#maxUses = maxUses;
220
- }
221
- #ensureContributor(ref) {
222
- if (this.#disposed)
223
- throw new Error('Marivo shell credential leases are disposed');
224
- if (this.#contributors.has(ref))
225
- return;
226
- const dispose = shellEnvironmentRegistry(this.#ctx).register({
227
- name: `dsh-data-analysis:credential:${ref}`,
228
- variables: {
229
- [ref]: {
230
- description: 'Marivo datasource credential authorized for this Shell execution.',
231
- },
232
- },
233
- resolve: (execution) => {
234
- const value = this.#executionValues.get(execution)?.values.get(ref);
235
- return value === undefined ? {} : { [ref]: value };
236
- },
237
- });
238
- this.#contributors.set(ref, dispose);
239
- }
240
- #purgeExpired(now) {
241
- for (const [token, lease] of this.#leases) {
242
- if (lease.expiresAt <= now)
243
- this.#leases.delete(token);
244
- }
245
- }
246
- revokeLease(agent, bridge, datasourceName) {
247
- for (const [token, lease] of this.#leases) {
248
- if (lease.agent === agent &&
249
- lease.workspaceFingerprint === bridge.binding.fingerprint &&
250
- lease.datasourceName === datasourceName) {
251
- this.#leases.delete(token);
252
- }
253
- }
254
- }
255
- issueLease(agent, bridge, datasourceName, refs) {
256
- if (this.#disposed)
257
- throw new Error('Marivo shell credential leases are disposed');
258
- const now = Date.now();
259
- this.#purgeExpired(now);
260
- this.revokeLease(agent, bridge, datasourceName);
261
- assertMarivoCredentialReferences(refs);
262
- const bindings = credentialBindings(refs);
263
- for (const binding of bindings)
264
- this.#ensureContributor(binding.storageRef);
265
- const token = randomBytes(32).toString('base64url');
266
- this.#leases.set(token, {
267
- agent,
268
- workspaceFingerprint: bridge.binding.fingerprint,
269
- datasourceName,
270
- bindings,
271
- expiresAt: now + this.#ttlMs,
272
- remainingUses: this.#maxUses,
273
- });
274
- return {
275
- token,
276
- expires_in_ms: this.#ttlMs,
277
- max_uses: this.#maxUses,
278
- usage: 'bounded-foreground-shell-lease',
279
- bash_prelude: bashPrelude(token, bindings),
280
- pwsh_prelude: pwshPrelude(token, bindings),
281
- };
282
- }
283
- async #claim(agent, bridgeSource, execution, token) {
284
- const lease = this.#leases.get(token);
285
- if (lease === undefined) {
286
- throw leaseFailure('shell-credential-lease-invalid', 'Marivo shell credential lease is unknown; call marivo_datasource_access again');
287
- }
288
- if (lease.expiresAt <= Date.now()) {
289
- this.#leases.delete(token);
290
- throw leaseFailure('shell-credential-lease-expired', 'Marivo shell credential lease has expired; call marivo_datasource_access again', { datasourceName: lease.datasourceName });
291
- }
292
- if (execution.agent !== agent || lease.agent !== agent) {
293
- throw leaseFailure('shell-credential-lease-scope-mismatch', 'Marivo shell credential lease belongs to another Agent');
294
- }
295
- const bridge = await resolveMarivoDatasourceBridge(bridgeSource);
296
- if (this.#leases.get(token) !== lease) {
297
- throw leaseFailure('shell-credential-lease-invalid', 'Marivo shell credential lease was revoked; call marivo_datasource_access again');
298
- }
299
- if (lease.workspaceFingerprint !== bridge.binding.fingerprint) {
300
- throw leaseFailure('shell-credential-lease-scope-mismatch', 'Marivo shell credential lease belongs to another Workspace binding');
301
- }
302
- if (lease.expiresAt <= Date.now()) {
303
- this.#leases.delete(token);
304
- throw leaseFailure('shell-credential-lease-expired', 'Marivo shell credential lease has expired; call marivo_datasource_access again', { datasourceName: lease.datasourceName });
305
- }
306
- const args = commandArguments(execution);
307
- if (args?.run_in_background === true) {
308
- throw leaseFailure('shell-credential-injection-unsupported', 'Marivo shell credential leases cannot be used by background Shell executions', { tool: execution.name });
309
- }
310
- if (isHarnessPersistentShell(agent, execution.name)) {
311
- throw leaseFailure('shell-credential-injection-unsupported', `Marivo shell credential leases cannot be used by persistent ${execution.name}`, { tool: execution.name });
312
- }
313
- if (lease.remainingUses < 1) {
314
- this.#leases.delete(token);
315
- throw leaseFailure('shell-credential-lease-exhausted', 'Marivo shell credential lease is exhausted; call marivo_datasource_access again', { datasourceName: lease.datasourceName });
316
- }
317
- lease.remainingUses--;
318
- const values = new Map();
319
- for (const binding of lease.bindings) {
320
- execution.signal.throwIfAborted();
321
- let resolved;
322
- try {
323
- resolved = await this.#credentials.resolve(credentialRef(binding.storageRef));
324
- }
325
- catch {
326
- throw leaseFailure('shell-credential-lease-resolve-failed', 'A credential authorized by the Marivo shell lease could not be resolved', { datasourceName: lease.datasourceName, ref: binding.environmentRef });
327
- }
328
- if (resolved === undefined) {
329
- throw leaseFailure('shell-credential-lease-resolve-failed', 'A credential authorized by the Marivo shell lease is no longer configured', { datasourceName: lease.datasourceName, ref: binding.environmentRef });
330
- }
331
- values.set(binding.storageRef, resolved.value);
332
- }
333
- this.#executionValues.set(execution, { agent, values });
334
- }
335
- async prepareExecution(agent, bridgeSource, execution) {
336
- if (this.#disposed)
337
- throw new Error('Marivo shell credential leases are disposed');
338
- const token = leaseToken(execution);
339
- if (token === undefined) {
340
- if (containsCredentialDereference(execution)) {
341
- throw leaseFailure('shell-credential-lease-invalid', 'Marivo shell credential reference is not authorized because the exact prelude lease marker is missing. Call marivo_datasource_access again, copy the exact prelude, and use a foreground Shell execution', { tool: execution.name });
342
- }
343
- return false;
344
- }
345
- await this.#claim(agent, bridgeSource, execution, token);
346
- return true;
347
- }
348
- installAgent(agent, bridgeSource) {
349
- const stopPreExecute = agent.ctx.on('tools/pre-execute', async (execution, next) => {
350
- if (execution.agent === agent && SHELL_TOOL_NAMES.has(execution.name)) {
351
- await this.prepareExecution(agent, bridgeSource, execution);
352
- }
353
- return next();
354
- });
355
- const stopResult = agent.ctx.on('tools/result', (execution) => {
356
- if (execution.agent === agent)
357
- this.#executionValues.delete(execution);
358
- });
359
- let active = true;
360
- return () => {
361
- if (!active)
362
- return;
363
- active = false;
364
- stopPreExecute();
365
- stopResult();
366
- for (const [token, lease] of this.#leases) {
367
- if (lease.agent === agent)
368
- this.#leases.delete(token);
369
- }
370
- for (const [execution, snapshot] of this.#executionValues) {
371
- if (snapshot.agent === agent)
372
- this.#executionValues.delete(execution);
373
- }
374
- };
375
- }
376
- dispose() {
377
- if (this.#disposed)
378
- return;
379
- this.#disposed = true;
380
- this.#leases.clear();
381
- this.#executionValues.clear();
382
- for (const dispose of this.#contributors.values())
383
- dispose();
384
- this.#contributors.clear();
385
- }
386
55
  }
@@ -1,65 +1,38 @@
1
1
  import { defineTool } from '@deepseek-ai/dsh-tools';
2
- import { resolveMarivoDatasourceBridge, } from "./bridge.js";
3
- import { inspectMarivoDatasourceCredentials } from "./credentials.js";
2
+ import { resolveMarivoDatasourceBridge } from "./bridge.js";
4
3
  export const MARIVO_DATASOURCE_TEST_TOOL_NAME = 'marivo_datasource_test';
5
- function datasourceName(value) {
6
- if (typeof value !== 'string' || value.trim() === '' || value.length > 256) {
7
- throw new TypeError('marivo_datasource_test name must be a non-empty string of at most 256 characters');
8
- }
9
- return value;
10
- }
11
- function renderValue(value) {
12
- if (value.status === 'needs-credentials')
13
- return JSON.stringify(value);
14
- if (value.status === 'ok') {
15
- return `Marivo datasource ${value.name} connection test succeeded${value.latency_ms === null ? '' : ` in ${value.latency_ms} ms`}.`;
16
- }
17
- return JSON.stringify(value);
18
- }
19
- /** Build the scoped datasource connection-test Tool. */
20
- export function createMarivoDatasourceTestTool(bridgeSource, credentials, options) {
4
+ export function createMarivoDatasourceTestTool(source, service) {
21
5
  return defineTool({
22
6
  name: MARIVO_DATASOURCE_TEST_TOOL_NAME,
23
- description: 'Test one configured Marivo datasource connection. Missing datasource environment references are requested through the DSH credential service.',
7
+ description: 'Test a Marivo datasource through Host credentials. Missing credentials wait for the Web form while this call remains alive; configured connection failures return directly.',
24
8
  parameters: {
25
- name: {
26
- type: 'string',
27
- required: true,
28
- description: 'Configured Marivo datasource name.',
29
- },
9
+ name: { type: 'string', required: true, description: 'Configured datasource name.' },
30
10
  },
31
11
  output: {
32
12
  schema: { type: 'json' },
33
- render: (_args, value) => [
34
- { type: 'text', text: renderValue(value) },
35
- ],
13
+ render: (_args, value) => [{ type: 'text', text: JSON.stringify(value) }],
36
14
  },
37
- timeoutMs: 65_000,
38
15
  async execute(args, exec) {
39
- const name = datasourceName(args.name);
40
- const bridge = await resolveMarivoDatasourceBridge(bridgeSource);
41
- options.revokeShellLease(bridge, name);
42
- const described = await bridge.describe(name, exec.signal);
43
- const overlay = {};
44
- const missing = await inspectMarivoDatasourceCredentials(described.refs, credentials, exec.signal, (ref, value) => {
45
- overlay[ref] = value;
46
- });
47
- if (missing.length > 0)
48
- return { status: 'needs-credentials', name: described.name, refs: missing };
49
- const tested = await bridge.test(name, overlay, exec.signal);
50
- if (tested.ok) {
51
- return { status: 'ok', name: tested.name, latency_ms: tested.latency_ms };
52
- }
53
- return {
54
- status: 'failed',
55
- name: tested.name,
56
- latency_ms: tested.latency_ms,
57
- failure: tested.failure,
58
- repair: tested.repair,
59
- };
16
+ if (!args.name.trim() || args.name.length > 256)
17
+ throw new Error('Invalid datasource name');
18
+ return datasourceToolValue(await service.track(service.prepare('test', exec, () => resolveMarivoDatasourceBridge(source), args.name)));
60
19
  },
61
20
  });
62
21
  }
63
- export function registerMarivoDatasourceTestTool(ctx, bridgeSource, credentials, options) {
64
- return ctx.tools.register(createMarivoDatasourceTestTool(bridgeSource, credentials, options));
22
+ export function registerMarivoDatasourceTestTool(ctx, source, service) {
23
+ return ctx.tools.register(createMarivoDatasourceTestTool(source, service));
24
+ }
25
+ /** Preserve the datasource tools' public status family; raw Marivo results stay inside the service. */
26
+ export function datasourceToolValue(result) {
27
+ if (!('ok' in result))
28
+ return JSON.parse(JSON.stringify(result));
29
+ return JSON.parse(JSON.stringify(result.ok
30
+ ? { status: 'ok', name: result.name, latency_ms: result.latency_ms }
31
+ : {
32
+ status: 'failed',
33
+ name: result.name,
34
+ latency_ms: result.latency_ms,
35
+ failure: result.failure,
36
+ repair: result.repair,
37
+ }));
65
38
  }
@@ -39,10 +39,36 @@ _dsh_sys.argv = [_dsh_sys.argv[0], *_dsh_sys.argv[4:]]
39
39
  const IMPORT_IDENTITY_PROGRAM = String.raw `
40
40
  print(_dsh_json.dumps(_dsh_actual, sort_keys=True))
41
41
  `.trim();
42
- function redactSubprocessOutput(result, environmentOverlay) {
43
- if (environmentOverlay === undefined)
42
+ const PRESENTATION_IDENTITY_PRELUDE = String.raw `
43
+ try:
44
+ import dsh_data_analysis_presentation as _dsh_presentation
45
+ from importlib.metadata import distribution as _dsh_distribution
46
+ _dsh_presentation_distribution = _dsh_distribution("dsh-data-analysis-presentation-kit")
47
+ _dsh_presentation_path = _dsh_os.path.realpath(_dsh_presentation.__file__ or "")
48
+ if (
49
+ _dsh_presentation.__version__ != _dsh_expected_presentation["version"]
50
+ or _dsh_presentation_distribution.version != _dsh_expected_presentation["version"]
51
+ or _dsh_presentation_path != _dsh_os.path.realpath(_dsh_expected_presentation["packagePath"])
52
+ or _dsh_presentation_path != _dsh_os.path.realpath(_dsh_presentation_distribution.locate_file("dsh_data_analysis_presentation/__init__.py"))
53
+ or not callable(_dsh_presentation.write_dataset)
54
+ ):
55
+ raise ValueError("presentation helper identity mismatch")
56
+ except Exception:
57
+ print(_dsh_json.dumps({"kind": "presentation-identity-mismatch"}), file=_dsh_sys.stderr)
58
+ raise SystemExit(78)
59
+ `.trim();
60
+ function redactSubprocessOutput(result, environmentOverlay, secretValues = []) {
61
+ if (environmentOverlay === undefined && secretValues.length === 0)
44
62
  return result;
45
- const secrets = Object.values(environmentOverlay).filter((value) => value !== undefined && value !== '');
63
+ // These exact process controls carry no credential material. Redacting '1' would
64
+ // corrupt public schema IDs such as marivo.semantic_ref/v1.
65
+ const secrets = Object.entries(environmentOverlay ?? {})
66
+ .filter(([key, value]) => !((key === 'MARIVO_TELEMETRY' && value === 'off') ||
67
+ (key === 'PYTHONDONTWRITEBYTECODE' && value === '1')))
68
+ .map(([, value]) => value)
69
+ .filter((value) => value !== undefined && value !== '');
70
+ secrets.push(...secretValues.filter(Boolean));
71
+ secrets.sort((a, b) => b.length - a.length);
46
72
  const redactText = (source) => {
47
73
  let text = source;
48
74
  for (const secret of secrets)
@@ -116,6 +142,9 @@ function fingerprint(binding) {
116
142
  binding.marivoVersion,
117
143
  binding.packagePath,
118
144
  binding.subprocessPolicyId,
145
+ ...(binding.presentationKit === undefined
146
+ ? []
147
+ : [binding.presentationKit.version, binding.presentationKit.packagePath]),
119
148
  ];
120
149
  return createHash('sha256').update(JSON.stringify(payload)).digest('hex');
121
150
  }
@@ -142,7 +171,14 @@ export class MarivoEnvironment {
142
171
  subprocessPolicy;
143
172
  #failed = false;
144
173
  constructor(binding, subprocessPolicy) {
145
- this.binding = Object.freeze({ ...binding });
174
+ this.binding = Object.freeze({
175
+ ...binding,
176
+ ...(binding.presentationKit === undefined
177
+ ? {}
178
+ : {
179
+ presentationKit: Object.freeze({ ...binding.presentationKit }),
180
+ }),
181
+ });
146
182
  this.subprocessPolicy = subprocessPolicy;
147
183
  }
148
184
  get status() {
@@ -158,11 +194,14 @@ export class MarivoEnvironment {
158
194
  }
159
195
  if (request.program.trim() === '')
160
196
  throw new TypeError('checked Python program must not be empty');
197
+ const presentationPrelude = this.binding.presentationKit === undefined
198
+ ? ''
199
+ : `_dsh_expected_presentation = ${JSON.stringify(this.binding.presentationKit)}\n${PRESENTATION_IDENTITY_PRELUDE}\n`;
161
200
  const rawResult = await this.subprocessPolicy.run({
162
201
  executable: this.binding.pythonExecutable,
163
202
  args: [
164
203
  '-c',
165
- `${CHECKED_IDENTITY_PRELUDE}\n${request.program}`,
204
+ `${CHECKED_IDENTITY_PRELUDE}\n${presentationPrelude}${request.program}`,
166
205
  this.binding.pythonExecutable,
167
206
  this.binding.marivoVersion,
168
207
  this.binding.packagePath,
@@ -171,13 +210,14 @@ export class MarivoEnvironment {
171
210
  ...(request.environmentOverlay === undefined
172
211
  ? {}
173
212
  : { environmentOverlay: request.environmentOverlay }),
213
+ ...(request.stdin === undefined ? {} : { stdin: request.stdin }),
174
214
  ...(request.limits === undefined ? {} : { limits: request.limits }),
175
215
  ...(request.signal === undefined ? {} : { signal: request.signal }),
176
216
  });
177
- const result = redactSubprocessOutput(rawResult, request.environmentOverlay);
217
+ const result = redactSubprocessOutput(rawResult, request.environmentOverlay, request.secretValues);
178
218
  if (result.exitCode === 78) {
179
219
  this.#failed = true;
180
- throw new MarivoEnvironmentError('binding-identity-mismatch', 'Marivo import identity changed; explicit rebind is required', {
220
+ throw new MarivoEnvironmentError('binding-identity-mismatch', 'Bound Runtime import identity changed; explicit rebind is required', {
181
221
  fingerprint: this.binding.fingerprint,
182
222
  exitCode: result.exitCode,
183
223
  stderr: result.stderr.toString('utf8').slice(0, 2_000),
@@ -194,7 +234,7 @@ export class MarivoEnvironment {
194
234
  });
195
235
  if (result.exitCode !== 0) {
196
236
  this.#failed = true;
197
- throw new MarivoEnvironmentError('binding-identity-mismatch', 'Marivo import identity changed; explicit rebind is required', {
237
+ throw new MarivoEnvironmentError('binding-identity-mismatch', 'Bound Runtime import identity changed; explicit rebind is required', {
198
238
  fingerprint: this.binding.fingerprint,
199
239
  exitCode: result.exitCode,
200
240
  stderr: result.stderr.toString('utf8').slice(0, 2_000),
@@ -213,7 +253,7 @@ export class MarivoEnvironment {
213
253
  this.#failed = true;
214
254
  if (cause instanceof MarivoEnvironmentError)
215
255
  throw cause;
216
- throw new MarivoEnvironmentError('binding-identity-mismatch', 'Marivo import identity changed; explicit rebind is required', { fingerprint: this.binding.fingerprint }, { cause });
256
+ throw new MarivoEnvironmentError('binding-identity-mismatch', 'Bound Runtime import identity changed; explicit rebind is required', { fingerprint: this.binding.fingerprint }, { cause });
217
257
  }
218
258
  }
219
259
  }
@@ -225,6 +265,10 @@ export function createSharedMarivoRuntimeRunner(runtime, options = {}) {
225
265
  pythonExecutable: runtime.pythonExecutable,
226
266
  marivoVersion: runtime.marivoVersion,
227
267
  packagePath: runtime.packagePath,
268
+ presentationKit: {
269
+ version: runtime.presentationKitVersion,
270
+ packagePath: runtime.presentationKitPackagePath,
271
+ },
228
272
  subprocessPolicyId: subprocessPolicy.id,
229
273
  };
230
274
  return new MarivoEnvironment({ ...partialBinding, fingerprint: fingerprint(partialBinding) }, subprocessPolicy);
@@ -263,6 +307,7 @@ export async function bindMarivoEnvironment(config, options = {}) {
263
307
  pythonExecutable,
264
308
  marivoVersion: report.marivo.version,
265
309
  packagePath: normalizeAbsolute(report.marivo.package_path),
310
+ ...(options.presentationKit === undefined ? {} : { presentationKit: options.presentationKit }),
266
311
  subprocessPolicyId: subprocessPolicy.id,
267
312
  };
268
313
  const binding = {