@inflexa-ai/harness 0.28.1 → 0.30.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 (168) hide show
  1. package/dist/agents/conversation-agent.js +7 -4
  2. package/dist/agents/report-session-agent.js +1 -1
  3. package/dist/agents/sandbox/data-profiler.js +0 -1
  4. package/dist/agents/sandbox/immune-profiling-agent.js +3 -1
  5. package/dist/agents/sandbox/network-agent.js +0 -1
  6. package/dist/agents/sandbox/types.d.ts +1 -1
  7. package/dist/agents/sandbox/types.js +1 -1
  8. package/dist/app/data-profile-orientation.d.ts +52 -27
  9. package/dist/app/data-profile-orientation.js +331 -92
  10. package/dist/app/data-profile-view.d.ts +70 -0
  11. package/dist/app/data-profile-view.js +123 -0
  12. package/dist/app/synthesize-run.js +1 -1
  13. package/dist/config/resource-limits.d.ts +1 -1
  14. package/dist/config/resource-limits.js +1 -1
  15. package/dist/contracts/data-profile.d.ts +276 -25
  16. package/dist/contracts/index.d.ts +3 -1
  17. package/dist/contracts/index.js +1 -0
  18. package/dist/contracts/profile-vocabulary.d.ts +103 -0
  19. package/dist/contracts/profile-vocabulary.js +449 -0
  20. package/dist/contracts/schemas/chat-parts.d.ts +12 -12
  21. package/dist/contracts/severity.d.ts +2 -2
  22. package/dist/contracts/target-dossier.d.ts +91 -91
  23. package/dist/data/safety-panel-schema.d.ts +4 -4
  24. package/dist/execution/input-signature.d.ts +16 -3
  25. package/dist/execution/input-signature.js +0 -0
  26. package/dist/execution/post-step-pipeline.js +1 -1
  27. package/dist/execution/staged-input.d.ts +4 -4
  28. package/dist/index.d.ts +7 -2
  29. package/dist/index.js +14 -0
  30. package/dist/input-scan/clustering.d.ts +63 -0
  31. package/dist/input-scan/clustering.js +208 -0
  32. package/dist/input-scan/companions.d.ts +30 -0
  33. package/dist/input-scan/companions.js +138 -0
  34. package/dist/input-scan/decoder/container_readout.py +171 -0
  35. package/dist/input-scan/detect-sets.d.ts +24 -0
  36. package/dist/input-scan/detect-sets.js +279 -0
  37. package/dist/input-scan/enrich.d.ts +62 -19
  38. package/dist/input-scan/enrich.js +132 -187
  39. package/dist/input-scan/formats.d.ts +13 -5
  40. package/dist/input-scan/formats.js +13 -5
  41. package/dist/input-scan/index.d.ts +10 -4
  42. package/dist/input-scan/index.js +7 -4
  43. package/dist/input-scan/markers.d.ts +21 -0
  44. package/dist/input-scan/markers.js +89 -0
  45. package/dist/input-scan/menu.d.ts +127 -0
  46. package/dist/input-scan/menu.js +0 -0
  47. package/dist/input-scan/quarantine.d.ts +17 -0
  48. package/dist/input-scan/quarantine.js +69 -0
  49. package/dist/input-scan/readout-budget.d.ts +32 -0
  50. package/dist/input-scan/readout-budget.js +61 -0
  51. package/dist/input-scan/readout.d.ts +33 -0
  52. package/dist/input-scan/readout.js +336 -0
  53. package/dist/input-scan/scan.d.ts +5 -4
  54. package/dist/input-scan/scan.js +5 -5
  55. package/dist/input-scan/set-types.d.ts +209 -0
  56. package/dist/input-scan/set-types.js +15 -0
  57. package/dist/input-scan/templates.d.ts +93 -0
  58. package/dist/input-scan/templates.js +369 -0
  59. package/dist/input-scan/tokens.d.ts +94 -0
  60. package/dist/input-scan/tokens.js +267 -0
  61. package/dist/input-scan/tuning.d.ts +55 -0
  62. package/dist/input-scan/tuning.js +55 -0
  63. package/dist/input-scan/types.d.ts +2 -2
  64. package/dist/loop/run-agent.d.ts +13 -1
  65. package/dist/loop/run-agent.js +2 -1
  66. package/dist/prompts/SOUL.d.ts +2 -2
  67. package/dist/prompts/SOUL.js +17 -1
  68. package/dist/prompts/conversation.d.ts +1 -1
  69. package/dist/prompts/conversation.js +45 -71
  70. package/dist/prompts/planner.js +57 -10
  71. package/dist/prompts/report-session.d.ts +1 -1
  72. package/dist/prompts/report-session.js +3 -2
  73. package/dist/prompts/sandbox/bulk-transcriptomics-agent.d.ts +1 -1
  74. package/dist/prompts/sandbox/bulk-transcriptomics-agent.js +3 -1
  75. package/dist/prompts/sandbox/data-profiler.d.ts +10 -1
  76. package/dist/prompts/sandbox/data-profiler.js +197 -53
  77. package/dist/prompts/sandbox/dna-methylation-agent.d.ts +1 -1
  78. package/dist/prompts/sandbox/dna-methylation-agent.js +18 -12
  79. package/dist/prompts/sandbox/immune-profiling-agent.d.ts +1 -1
  80. package/dist/prompts/sandbox/immune-profiling-agent.js +6 -2
  81. package/dist/prompts/sandbox/metabolomics-agent.d.ts +1 -1
  82. package/dist/prompts/sandbox/metabolomics-agent.js +8 -4
  83. package/dist/prompts/sandbox/microbiome-agent.d.ts +1 -1
  84. package/dist/prompts/sandbox/microbiome-agent.js +12 -6
  85. package/dist/prompts/sandbox/network-agent.d.ts +1 -1
  86. package/dist/prompts/sandbox/network-agent.js +9 -6
  87. package/dist/prompts/sandbox/spatial-omics-agent.d.ts +1 -1
  88. package/dist/prompts/sandbox/spatial-omics-agent.js +4 -2
  89. package/dist/prompts/sandbox-standards.d.ts +1 -1
  90. package/dist/prompts/sandbox-standards.js +10 -7
  91. package/dist/providers/ai-sdk.js +34 -1
  92. package/dist/providers/reasoning.d.ts +65 -0
  93. package/dist/providers/reasoning.js +81 -0
  94. package/dist/providers/types.d.ts +14 -0
  95. package/dist/schemas/data-profile-schemas.d.ts +947 -84
  96. package/dist/schemas/data-profile-schemas.js +300 -115
  97. package/dist/schemas/run-synthesis.d.ts +4 -4
  98. package/dist/state/data-profile.d.ts +1 -1
  99. package/dist/state/index.d.ts +1 -1
  100. package/dist/state/vector-store.d.ts +13 -0
  101. package/dist/state/vector-store.js +8 -0
  102. package/dist/tasks/data-profile-absorb.d.ts +78 -0
  103. package/dist/tasks/data-profile-absorb.js +285 -0
  104. package/dist/tasks/data-profile-index.d.ts +34 -25
  105. package/dist/tasks/data-profile-index.js +160 -59
  106. package/dist/tasks/data-profile-resolve.d.ts +72 -0
  107. package/dist/tasks/data-profile-resolve.js +639 -0
  108. package/dist/tasks/data-profile.d.ts +16 -5
  109. package/dist/tasks/data-profile.js +270 -131
  110. package/dist/tools/bio/chembl.d.ts +1 -1
  111. package/dist/tools/bio/chembl.js +15 -10
  112. package/dist/tools/bio/comptox.js +5 -1
  113. package/dist/tools/bio/drug-gene-interactions.js +5 -2
  114. package/dist/tools/bio/gene-disease-evidence.d.ts +19 -9
  115. package/dist/tools/bio/gene-disease-evidence.js +78 -22
  116. package/dist/tools/bio/gene-preclinical-profile.d.ts +33 -13
  117. package/dist/tools/bio/gene-preclinical-profile.js +63 -22
  118. package/dist/tools/bio/lookup-annotation.d.ts +1 -0
  119. package/dist/tools/bio/lookup-annotation.js +28 -10
  120. package/dist/tools/bio/opentargets.d.ts +10 -2
  121. package/dist/tools/bio/opentargets.js +42 -13
  122. package/dist/tools/bio/pubchem.js +5 -2
  123. package/dist/tools/bio/pubmed.js +9 -3
  124. package/dist/tools/bio/search-clinical-trials.d.ts +25 -25
  125. package/dist/tools/bio/search-clinical-trials.js +56 -18
  126. package/dist/tools/bio/search-faers.d.ts +26 -6
  127. package/dist/tools/bio/search-faers.js +70 -18
  128. package/dist/tools/bio/search-gene.d.ts +19 -3
  129. package/dist/tools/bio/search-gene.js +69 -12
  130. package/dist/tools/bio/search-geo-datasets.js +4 -1
  131. package/dist/tools/bio/search-interactions.js +6 -2
  132. package/dist/tools/bio/target-safety.d.ts +2 -2
  133. package/dist/tools/bio/target-safety.js +3 -2
  134. package/dist/tools/lib/cbioportal-client.js +25 -6
  135. package/dist/tools/lib/chembl-client.d.ts +3 -1
  136. package/dist/tools/lib/chembl-client.js +6 -1
  137. package/dist/tools/lib/gwas-catalog-client.d.ts +7 -6
  138. package/dist/tools/lib/gwas-catalog-client.js +18 -6
  139. package/dist/tools/lib/openfda-client.d.ts +7 -5
  140. package/dist/tools/lib/openfda-client.js +17 -9
  141. package/dist/tools/lib/opentargets-client.d.ts +20 -0
  142. package/dist/tools/lib/opentargets-client.js +45 -0
  143. package/dist/tools/lib/pathway-client.d.ts +7 -0
  144. package/dist/tools/lib/pathway-client.js +68 -1
  145. package/dist/tools/research/context7-docs.js +3 -3
  146. package/dist/tools/research/generate-analogy-report.js +2 -2
  147. package/dist/tools/research/generate-plan.d.ts +3 -0
  148. package/dist/tools/research/generate-plan.js +64 -5
  149. package/dist/tools/research/inspect-data-profile.d.ts +48 -22
  150. package/dist/tools/research/inspect-data-profile.js +73 -54
  151. package/dist/tools/research/resolve-citation.js +8 -3
  152. package/dist/tools/research/search-arxiv.js +8 -3
  153. package/dist/tools/research/search-semantic-scholar.js +10 -4
  154. package/dist/tools/workspace/scan-inputs.d.ts +4 -4
  155. package/dist/tools/workspace/scan-inputs.js +13 -14
  156. package/dist/tools/workspace/workspace-search.d.ts +11 -2
  157. package/dist/tools/workspace/workspace-search.js +44 -16
  158. package/dist/workflows/sandbox-step.d.ts +6 -0
  159. package/dist/workflows/sandbox-step.js +31 -14
  160. package/dist/workflows/target-assessment/assemblers/safety.d.ts +1 -1
  161. package/dist/workflows/target-assessment/phase4-assemble.d.ts +38 -38
  162. package/dist/workflows/target-assessment/schemas.d.ts +12 -12
  163. package/dist/workflows/target-assessment/steps/phase2-aggregate.d.ts +6 -6
  164. package/dist/workflows/target-assessment/steps/phase3-aggregate.d.ts +6 -6
  165. package/dist/workflows/target-assessment/synthesis/index.d.ts +12 -12
  166. package/package.json +5 -3
  167. package/dist/input-scan/coverage.d.ts +0 -22
  168. package/dist/input-scan/coverage.js +0 -85
@@ -0,0 +1,123 @@
1
+ /**
2
+ * One reading of a persisted profile, whichever era wrote it.
3
+ *
4
+ * A snapshot carries the fields of its era: rows written under the groups-and-dimensions
5
+ * model carry `groups`/`dimensions`, and rows written before it carry `kinds`/`axes` and
6
+ * a `files` list. There is no version field to consult — optionality is the compatibility
7
+ * mechanism — so a consumer must read both, and reading both in five places is how they
8
+ * drift apart. They read it here instead.
9
+ *
10
+ * The projection is lossy in one direction only: it narrows the newer record to what the
11
+ * older consumers already handled. Nothing it drops is unavailable — the full record is
12
+ * on the row, and consumers that want the resolved structure read it directly.
13
+ */
14
+ /**
15
+ * Every number the observations reported, side by side and de-duplicated.
16
+ *
17
+ * There is no canonical cardinality: a metadata sheet describing one count and files
18
+ * existing for another both stand, so a single number would be a silent judgement between
19
+ * disagreeing sources.
20
+ */
21
+ export function dimensionCardinalities(dimension) {
22
+ const counts = [];
23
+ for (const observation of dimension.observations) {
24
+ if (observation.kind === "slot")
25
+ counts.push(observation.cardinality);
26
+ else if (observation.kind === "column" && observation.distinctValues !== undefined)
27
+ counts.push(observation.distinctValues);
28
+ else if (observation.kind === "document" && observation.statesCardinality !== undefined)
29
+ counts.push(observation.statesCardinality);
30
+ }
31
+ return [...new Set(counts)];
32
+ }
33
+ function examplesOf(dimension) {
34
+ for (const observation of dimension.observations) {
35
+ if (observation.kind === "slot" && observation.sampleValues.length > 0)
36
+ return observation.sampleValues;
37
+ if (observation.kind === "column" && observation.exampleValues.length > 0)
38
+ return observation.exampleValues;
39
+ }
40
+ return [];
41
+ }
42
+ function fromGroup(group) {
43
+ const format = group.formats.length === 1 ? group.formats[0].format : undefined;
44
+ return {
45
+ id: group.id,
46
+ name: group.name,
47
+ memberRepresents: group.memberRepresents,
48
+ description: group.description,
49
+ count: group.count,
50
+ pattern: group.displayPattern,
51
+ ...(format ? { format } : {}),
52
+ ...(group.slots ? { slotIds: group.slots.map((slot) => slot.id) } : {}),
53
+ ...(group.unclassified ? { unclassified: true } : {}),
54
+ };
55
+ }
56
+ /** The dataset's structure, from whichever field the snapshot carries it in. */
57
+ export function profileGroups(result) {
58
+ if (result.groups !== undefined)
59
+ return result.groups.map(fromGroup);
60
+ return (result.kinds ?? []).map((kind, index) => ({
61
+ id: `kind-${index + 1}`,
62
+ name: kind.name,
63
+ memberRepresents: kind.memberRepresents,
64
+ description: kind.description,
65
+ count: kind.count,
66
+ pattern: kind.pathPattern,
67
+ ...(kind.format ? { format: kind.format } : {}),
68
+ }));
69
+ }
70
+ /** What varies across the dataset, from whichever field the snapshot carries it in. */
71
+ export function profileDimensions(result) {
72
+ if (result.dimensions !== undefined) {
73
+ return result.dimensions.map((dimension) => ({
74
+ label: dimension.label,
75
+ cardinalities: dimensionCardinalities(dimension),
76
+ exampleValues: examplesOf(dimension),
77
+ ...(dimension.description ? { description: dimension.description } : {}),
78
+ }));
79
+ }
80
+ return (result.axes ?? []).map((axis) => ({
81
+ label: axis.label,
82
+ cardinalities: [axis.cardinality],
83
+ exampleValues: axis.exampleValues ?? [],
84
+ ...(axis.description ? { description: axis.description } : {}),
85
+ }));
86
+ }
87
+ /**
88
+ * The files the profile describes individually.
89
+ *
90
+ * On a groups-era row those are the members the agent annotated, which ride on the group
91
+ * they belong to; on an older row they are the `files` list. Either way this is a handful
92
+ * of notable inputs, never the dataset.
93
+ */
94
+ export function profileFileRecords(result) {
95
+ if (result.groups !== undefined) {
96
+ return result.groups.flatMap((group) => (group.memberAnnotations ?? []).map((annotation) => ({
97
+ path: annotation.path,
98
+ description: annotation.note,
99
+ ...(group.formats.length === 1 ? { format: group.formats[0].format } : {}),
100
+ })));
101
+ }
102
+ return result.files ?? [];
103
+ }
104
+ /**
105
+ * How many files the dataset holds, as far as the snapshot can establish it.
106
+ *
107
+ * The partition is the authority where present — it is a census, not an estimate. Older
108
+ * rows fall back to coverage, then to summed kind counts, then to `null`: reporting the
109
+ * described-file count would be a dataset size the row cannot support.
110
+ */
111
+ export function profileDatasetFileCount(result) {
112
+ if (result.partition)
113
+ return result.partition.keptFiles;
114
+ if (result.coverage)
115
+ return result.coverage.total;
116
+ if (result.kinds && result.kinds.length > 0)
117
+ return result.kinds.reduce((sum, kind) => sum + kind.count, 0);
118
+ return null;
119
+ }
120
+ /** Dataset-wide findings the agent wrote, from whichever field the snapshot carries them in. */
121
+ export function profileCaveats(result) {
122
+ return result.caveats ?? result.qualityAssessment?.concerns ?? [];
123
+ }
@@ -78,7 +78,7 @@ export async function synthesizeRun(deps, params) {
78
78
  unwrapOrThrow(await vectorStore.upsert({
79
79
  indexName,
80
80
  vectors: [vector],
81
- metadata: [{ text: synthesisText, type: "synthesis", runId }],
81
+ metadata: [{ text: synthesisText, type: "synthesis", runId, path: `runs/${runId}/synthesis.json` }],
82
82
  ids: [`/${analysisId}/runs/${runId}/synthesis.json`],
83
83
  }));
84
84
  }
@@ -46,7 +46,7 @@ export type ResourcePolicy = z.infer<typeof ResourcePolicySchema>;
46
46
  /** Default LLM-turn budget for a sandbox agent step. Covers simple,
47
47
  * standard, and complex analyses — extra headroom on simple steps is
48
48
  * cheaper than truncating complex ones. */
49
- export declare const DEFAULT_SANDBOX_MAX_STEPS = 75;
49
+ export declare const DEFAULT_SANDBOX_MAX_STEPS = 200;
50
50
  export declare class ResourceLimitsConfigError extends Error {
51
51
  constructor(message: string);
52
52
  }
@@ -33,7 +33,7 @@ export const ResourcePolicySchema = z.object({
33
33
  /** Default LLM-turn budget for a sandbox agent step. Covers simple,
34
34
  * standard, and complex analyses — extra headroom on simple steps is
35
35
  * cheaper than truncating complex ones. */
36
- export const DEFAULT_SANDBOX_MAX_STEPS = 75;
36
+ export const DEFAULT_SANDBOX_MAX_STEPS = 200;
37
37
  // ── Error ───────────────────────────────────────────────────────────
38
38
  export class ResourceLimitsConfigError extends Error {
39
39
  constructor(message) {
@@ -61,10 +61,12 @@ export interface DataProfileFile {
61
61
  /** Flat, identity-establishing facts (sparsity, delimiter, normalizationState, …). */
62
62
  metrics?: Record<string, string | number | boolean>;
63
63
  }
64
- /** Dataset-wide quality findings the profiler recorded. */
64
+ /**
65
+ * Dataset-wide quality findings, on rows of the era that recorded them. Superseded by
66
+ * `caveats`, which is agent-authored prose and nothing else.
67
+ */
65
68
  export interface DataProfileQualityAssessment {
66
69
  concerns: string[];
67
- strengths: string[];
68
70
  }
69
71
  /**
70
72
  * One repeating set of files — the dataset's structure, as opposed to its contents.
@@ -97,6 +99,251 @@ export interface DataProfileAxis {
97
99
  exampleValues?: string[];
98
100
  description?: string;
99
101
  }
102
+ /**
103
+ * A varying position in a group's display template, as the scan observed it.
104
+ *
105
+ * `sampleValues` is bounded and the record carries nothing wider: a full value
106
+ * enumeration would put the file set back in the row this record exists to keep out of
107
+ * it. The id is scan-scoped — it names the slot within the scan that produced this
108
+ * profile, and a dimension's slot observation refers to it.
109
+ */
110
+ export interface DataProfileGroupSlot {
111
+ id: string;
112
+ location: "directory" | "name";
113
+ index: number;
114
+ tokenClass: string;
115
+ distinctValues: number;
116
+ sampleValues: string[];
117
+ /** The slot this one repeats — one token in both a directory segment and the stem is ONE identity. */
118
+ sameAsSlot?: string;
119
+ }
120
+ /** A member the agent wrote about individually. Never exhaustive — that is the filesystem's job. */
121
+ export interface DataProfileMemberAnnotation {
122
+ path: string;
123
+ note: string;
124
+ }
125
+ /**
126
+ * Companion coverage across a group, computed per member.
127
+ *
128
+ * A machine finding, deliberately structured rather than folded into `caveats`: an
129
+ * average hides the one member whose index is missing, and that is the member a
130
+ * downstream step fails on.
131
+ */
132
+ export interface DataProfileCompanionCompleteness {
133
+ expectedCompanions: string[];
134
+ completeMembers: number;
135
+ incompleteMembers: number;
136
+ incompleteSample: {
137
+ path: string;
138
+ missingCompanions: string[];
139
+ }[];
140
+ }
141
+ /**
142
+ * One declared group — a claim about MEANING, resolved to a membership.
143
+ *
144
+ * `count` counts logical members (a data file and its companions count once); `fileCount`
145
+ * counts the files behind them, and it is `fileCount` that the partition accounting sums.
146
+ * `displayPattern` is the scanner's own template and is display-only: no agent authored
147
+ * it, and nothing computes membership from it.
148
+ */
149
+ export interface DataProfileGroup {
150
+ /** Stable within the profile, derived from the group's name. */
151
+ id: string;
152
+ name: string;
153
+ /** What ONE member represents. The grouping decision, stated apart from the description. */
154
+ memberRepresents: string;
155
+ description: string;
156
+ role: string;
157
+ category: string;
158
+ /** What the group actually is, when `category` is `other`. */
159
+ categoryLabel?: string;
160
+ subtype?: string;
161
+ /** Why the agent overrode a pre-suggestion, or chose an arguable category. */
162
+ categoryReason?: string;
163
+ /** Why this group was split off, or why these sets are one group. */
164
+ reason?: string;
165
+ count: number;
166
+ fileCount: number;
167
+ totalBytes: number;
168
+ /** The scanner template the members instantiate. Display-only. */
169
+ displayPattern: string;
170
+ formats: {
171
+ format: string;
172
+ count: number;
173
+ }[];
174
+ slots?: DataProfileGroupSlot[];
175
+ memberAnnotations?: DataProfileMemberAnnotation[];
176
+ completeness?: DataProfileCompanionCompleteness;
177
+ /** True for the swept residue — files no operation claimed. Visible by construction. */
178
+ unclassified?: boolean;
179
+ }
180
+ /** A measurement that was actually performed. Absent means unchecked, never "no overlap". */
181
+ export interface DataProfileChecked {
182
+ matched: number;
183
+ of: number;
184
+ }
185
+ /**
186
+ * Where a slot observation points, in terms that survive the scan that produced it.
187
+ *
188
+ * A slot id is per-scan ephemera — the same id names a different slot after a re-scan
189
+ * reorders the sets — so the durable binding is the set's template plus the slot's
190
+ * position within it, exactly as the recipe keys a split. A binding that no longer
191
+ * resolves strands the profile rather than rebinding to whatever now sits at that
192
+ * position.
193
+ */
194
+ export interface DataProfileSlotBinding {
195
+ template: string;
196
+ slotIndex: number;
197
+ }
198
+ /**
199
+ * One evidenced sighting of a dimension.
200
+ *
201
+ * A slot observation binds to a scanner slot, and its cardinality and values are computed
202
+ * from the scan rather than asserted; `groupIds` are the groups that carry that slot,
203
+ * derived from the operations. Slot bindings are the only link between a group and a
204
+ * dimension.
205
+ */
206
+ export type DataProfileObservation = {
207
+ kind: "slot";
208
+ groupIds: string[];
209
+ /** Scan-scoped, and display-only past the scan that wrote it. `binding` is what replays. */
210
+ slotId: string;
211
+ binding?: DataProfileSlotBinding;
212
+ tokenClass: string;
213
+ cardinality: number;
214
+ sampleValues: string[];
215
+ checked?: DataProfileChecked;
216
+ checkedAgainst?: string;
217
+ /**
218
+ * The slot the SCANNER found this one repeats, with the members whose two tokens
219
+ * disagreed. Its own performed measurement, in members rather than values — and
220
+ * the reason `checked` stays absent for such a pair: affix recovery strips literal
221
+ * text off one side, so an exact value-set intersection over two positions the
222
+ * scan matched one-to-one would report them wholly disjoint.
223
+ */
224
+ sameAsSlot?: string;
225
+ sameAsSlotMismatches?: number;
226
+ note?: string;
227
+ } | {
228
+ kind: "column";
229
+ path: string;
230
+ column: string;
231
+ exampleValues: string[];
232
+ distinctValues?: number;
233
+ checked?: DataProfileChecked;
234
+ checkedAgainst?: string;
235
+ note?: string;
236
+ } | {
237
+ kind: "document";
238
+ path: string;
239
+ citation: string;
240
+ statesCardinality?: number;
241
+ checked?: DataProfileChecked;
242
+ checkedAgainst?: string;
243
+ note?: string;
244
+ };
245
+ /**
246
+ * A dataset-level thing that varies, with the evidence it was seen in.
247
+ *
248
+ * There is no canonical cardinality: observations that disagree both stand, and
249
+ * `reconciliations` carries the delta. A renderer shows the numbers side by side.
250
+ */
251
+ export interface DataProfileDimension {
252
+ label: string;
253
+ category: string;
254
+ categoryLabel?: string;
255
+ /** Derived from the category, never agent-authored. */
256
+ scope: "biological" | "technical";
257
+ description?: string;
258
+ observations: DataProfileObservation[];
259
+ reconciliations?: {
260
+ note: string;
261
+ delta?: number;
262
+ }[];
263
+ nestsUnder?: {
264
+ dimension: string;
265
+ evidence: string;
266
+ };
267
+ /** Why the dataset deviates from the category's default treatment. */
268
+ treatmentReason?: string;
269
+ }
270
+ /** What the agent found when it looked for one of the standard dimensions. */
271
+ export interface DataProfileProbeReport {
272
+ probe: string;
273
+ outcome: "found" | "not-found" | "found-but-constant" | "attested";
274
+ dimension?: string;
275
+ searched?: string[];
276
+ reason?: string;
277
+ value?: string;
278
+ evidence?: string;
279
+ citation?: string;
280
+ path?: string;
281
+ }
282
+ /** Files the scan removed before structure was observed, with why. */
283
+ export interface DataProfileQuarantine {
284
+ count: number;
285
+ totalBytes: number;
286
+ reasons: {
287
+ reason: string;
288
+ count: number;
289
+ }[];
290
+ sample: string[];
291
+ }
292
+ /**
293
+ * The census, derived rather than declared.
294
+ *
295
+ * `keptFiles` equals the sum of every group's `fileCount`, `unclassified` included.
296
+ * Quarantined files are accounted separately, with their reasons. That the numbers sum
297
+ * is what turns "how much did the profile cover" from a question into a fact.
298
+ */
299
+ export interface DataProfilePartition {
300
+ scannedFiles: number;
301
+ keptFiles: number;
302
+ keptMembers: number;
303
+ groups: number;
304
+ unclassifiedMembers: number;
305
+ unclassifiedFiles: number;
306
+ quarantine: DataProfileQuarantine;
307
+ /**
308
+ * Members more than one operation claimed after the last repair round. Removed from
309
+ * every claimant and swept into `unclassified`, never awarded by precedence — a
310
+ * machine finding, so it is here rather than in the agent's caveats.
311
+ */
312
+ contested?: {
313
+ members: number;
314
+ sample: string[];
315
+ };
316
+ /** True when the walk stopped at its file ceiling, so the census covers part of the tree. */
317
+ scanTruncated?: boolean;
318
+ }
319
+ /**
320
+ * One operation, keyed to the scanner TEMPLATES it addressed rather than the menu ids.
321
+ *
322
+ * Menu ids are per-scan ephemera; templates survive a re-scan, so the recipe can be
323
+ * re-resolved against a changed tree.
324
+ *
325
+ * `unclassified` is the one step no agent authored: it records the paths resolution swept
326
+ * because no operation claimed them. It is carried so a replay can tell a file the last
327
+ * profile already declined to classify from a file that is new to the tree — without it
328
+ * an unchanged tree re-absorbs as partial and wakes the agent to re-judge what it already
329
+ * judged.
330
+ */
331
+ export interface DataProfileRecipeStep {
332
+ op: "use" | "split" | "merge" | "group" | "unclassified";
333
+ /** The templates this step addressed. Empty for an explicit path grouping. */
334
+ templates: string[];
335
+ /** Position of the split slot within the addressed set's slots. */
336
+ slotIndex?: number;
337
+ /** Slot values each resulting group claimed, for a value-mapped split. */
338
+ valueMapping?: {
339
+ groupId: string;
340
+ values: string[];
341
+ }[];
342
+ paths?: string[];
343
+ /** True when `paths` holds a prefix of the step's membership rather than all of it. */
344
+ pathsTruncated?: boolean;
345
+ groupIds: string[];
346
+ }
100
347
  /**
101
348
  * The comparand identifying WHICH files a profile covered and whether the SAME BYTES
102
349
  * were profiled.
@@ -126,17 +373,6 @@ export interface DataProfileCoverage {
126
373
  total: number;
127
374
  unmatchedSample?: string[];
128
375
  }
129
- /**
130
- * One profiled input file's legacy drift signature. `fileId` is a path identity (two
131
- * files at the same path share it regardless of content), so `size` + `mtimeMs` are what
132
- * let a consumer notice the bytes behind a path changed. Superseded by
133
- * {@link DataProfileInputSignature}, and still readable on older rows.
134
- */
135
- export interface DataProfileInputFile {
136
- fileId: string;
137
- size: number;
138
- mtimeMs: number;
139
- }
140
376
  /**
141
377
  * The profile snapshot stored in `cortex_analysis_state.data_profile_result`.
142
378
  *
@@ -148,25 +384,39 @@ export interface DataProfileInputFile {
148
384
  * It does NOT carry a record per input file. The workspace filesystem is the
149
385
  * authoritative file list — listing, grep, and the vector index all read the live tree —
150
386
  * so a copy here would be a stale duplicate on a row detoasted by every reader,
151
- * including the planner's, which reads a few hundred characters of it. `files` holds the
152
- * individually notable inputs; `kinds` holds the dataset's structure.
387
+ * including the planner's, which reads a few hundred characters of it. `groups` holds the
388
+ * dataset's structure, and the members an agent wrote about individually ride on the
389
+ * group they belong to.
390
+ *
391
+ * The writer emits `groups` and `dimensions`; `kinds`, `axes`, `files`, `coverage`, and
392
+ * `qualityAssessment` are what earlier eras wrote and stay readable. There is no version
393
+ * field: optionality is the compatibility mechanism, and a discriminator would be a
394
+ * second mechanism answering the same question.
153
395
  */
154
396
  export interface DataProfileResult {
155
397
  summary: string;
156
- /** Individually described files — notable singletons, not the dataset's contents. */
157
- files: DataProfileFile[];
158
- /** The repeating sets the tree was grouped into. Absent on a pre-kinds snapshot. */
398
+ /** The resolved groups the tree partitions into, `unclassified` included. */
399
+ groups?: DataProfileGroup[];
400
+ /** What varies across the dataset, each with its observations. */
401
+ dimensions?: DataProfileDimension[];
402
+ /** One outcome per standard dimension the agent was asked to look for. */
403
+ probes?: DataProfileProbeReport[];
404
+ /** The census. Derived at resolution — kept equals the sum over groups. */
405
+ partition?: DataProfilePartition;
406
+ /** The operations that produced the groups, keyed to scanner templates. */
407
+ recipe?: DataProfileRecipeStep[];
408
+ /** What a planner must know, in the agent's words. Machine findings live in the structured fields. */
409
+ caveats?: string[];
410
+ /** Individually described files — notable singletons on a pre-groups snapshot. */
411
+ files?: DataProfileFile[];
412
+ /** The repeating sets the tree was grouped into, on a snapshot of the kinds era. */
159
413
  kinds?: DataProfileKind[];
160
- /** What varies across those sets. Absent for the same reason as `kinds`. */
414
+ /** What varies across those sets, on a snapshot of the same era. */
161
415
  axes?: DataProfileAxis[];
162
- /** The drift comparand. Preferred over `inputFileIds`, which it replaces. */
416
+ /** The drift comparand. */
163
417
  inputSignature?: DataProfileInputSignature;
164
- /** How much of the scanned tree the kinds describe. Absent on pre-coverage snapshots. */
418
+ /** How much of the scanned tree the kinds describe, on a snapshot of the kinds era. Superseded by `partition`. */
165
419
  coverage?: DataProfileCoverage;
166
- /** The legacy identity-list comparand, on rows written before the signature existed. */
167
- inputFileIds?: string[];
168
- /** The legacy per-file drift signature, on rows of the same era. */
169
- inputFiles?: DataProfileInputFile[];
170
420
  /** ISO 8601 timestamp of profile completion. */
171
421
  profiledAt: string;
172
422
  /** Scientific domain (transcriptomics, cheminformatics, clinical, …). */
@@ -186,6 +436,7 @@ export interface DataProfileResult {
186
436
  accessions?: string[];
187
437
  /** Conditions, groups, comparisons, replicates, pairing. */
188
438
  experimentalDesign?: string;
439
+ /** Dataset-wide findings, on a snapshot of the era that recorded them. Superseded by `caveats`. */
189
440
  qualityAssessment?: DataProfileQualityAssessment;
190
441
  }
191
442
  /** The profile's lifecycle, as the ledger records it. */
@@ -6,7 +6,9 @@ export type { PlanStep, PresentationContent, PresentationPart, PlanPart, RunCard
6
6
  export type { EventSource, TextDeltaEvent, ToolStartedEvent, ToolFinishedEvent, FinishEvent, ChatErrorEvent, CortexChatEvent } from "./chat-events.js";
7
7
  export type { TokenUsageRollup } from "./usage.js";
8
8
  export { DATA_PROFILE_RUN_LITERAL } from "./data-profile.js";
9
- export type { DataProfileAxis, DataProfileCoverage, DataProfileFile, DataProfileInputFile, DataProfileInputSignature, DataProfileKind, DataProfileLifecycleStatus, DataProfileOrganism, DataProfileQualityAssessment, DataProfileResult, DataProfileSubjectSource, } from "./data-profile.js";
9
+ export type { DataProfileAxis, DataProfileChecked, DataProfileCompanionCompleteness, DataProfileCoverage, DataProfileDimension, DataProfileFile, DataProfileGroup, DataProfileGroupSlot, DataProfileInputSignature, DataProfileKind, DataProfileLifecycleStatus, DataProfileMemberAnnotation, DataProfileObservation, DataProfileOrganism, DataProfilePartition, DataProfileProbeReport, DataProfileQualityAssessment, DataProfileQuarantine, DataProfileRecipeStep, DataProfileResult, DataProfileSubjectSource, } from "./data-profile.js";
10
+ export { DIMENSION_CATEGORIES, DIMENSION_CATEGORY_IDS, DIMENSION_PROBE_IDS, DIMENSION_PROBES, GROUP_CATEGORIES, GROUP_CATEGORY_IDS, GROUP_ROLE_IDS, GROUP_ROLES, PROBE_OUTCOME_IDS, dimensionCategoryEntry, dimensionScope, groupCategoryEntry, } from "./profile-vocabulary.js";
11
+ export type { DimensionCategory, DimensionCategoryEntry, DimensionProbe, DimensionProbeEntry, DimensionScope, DimensionTreatment, GroupCategory, GroupCategoryEntry, GroupRole, GroupRoleEntry, ProbeOutcome, } from "./profile-vocabulary.js";
10
12
  export type { TextPart, ToolCallPart, CortexPart, CortexMessage } from "./message.js";
11
13
  export { PART_REGISTRY, isTransient, isReconciling, isSidebarPart } from "./part-registry.js";
12
14
  export type { CortexChatPartType, PartDescriptor, PartEmitter, PartConsumer } from "./part-registry.js";
@@ -1,6 +1,7 @@
1
1
  export { ORGAN_SYSTEMS, OrganSystemSchema, ORGAN_SYSTEM_LABELS, organSystemLabel } from "./organ-system.js";
2
2
  export { SEVERITIES, SeveritySchema } from "./severity.js";
3
3
  export { DATA_PROFILE_RUN_LITERAL } from "./data-profile.js";
4
+ export { DIMENSION_CATEGORIES, DIMENSION_CATEGORY_IDS, DIMENSION_PROBE_IDS, DIMENSION_PROBES, GROUP_CATEGORIES, GROUP_CATEGORY_IDS, GROUP_ROLE_IDS, GROUP_ROLES, PROBE_OUTCOME_IDS, dimensionCategoryEntry, dimensionScope, groupCategoryEntry, } from "./profile-vocabulary.js";
4
5
  export { PART_REGISTRY, isTransient, isReconciling, isSidebarPart } from "./part-registry.js";
5
6
  export { AnalogyCoverageSchema, AnalogyReportSchema, AnalogyReportErrorSchema, AnalogicalReasonerOutputSchema } from "./analogy-report.js";
6
7
  export { CoverageSchema, RowCoverageSchema, EntitySchema, ClaimSupportSchema, ClaimEvidenceSchema, LiabilityBulletSchema, LiabilitySummarySchema, TractabilitySchema, IndicationsSchema, DrugInteractionsSchema, ClinicalTrialAttributionSchema, ClinicalDevelopmentSchema, OrganRiskRowSchema, OffTargetRowSchema, OffTargetPanelSchema, RegulatoryActionRowSchema, SafetyFlagSchema, SafetyProfileSchema, OffTissueRowSchema, OffTissueRiskSchema, ReferenceBiologyShape, EvidenceConflictsSchema, EvidenceTimelineSchema, TranslationalChainSchema, AdditionalEvidenceSchema, DiscoveryTrialsSchema, QualityGateStatusSchema, QualityGatesSchema, DerivedSchema, DossierSchema, DossierBodySchema, isDossier, dossierJsonSchema, SECTION_BLURBS, TargetAssessmentPhaseSchema, TargetAssessmentProgressEventSchema, } from "./target-dossier.js";
@@ -0,0 +1,103 @@
1
+ /**
2
+ * The controlled vocabulary the profiler draws from, as data.
3
+ *
4
+ * One source, two readers: submit validation derives its enums from these lists and
5
+ * prompt assembly renders from the same entries, so the catalogue the agent is shown
6
+ * and the catalogue the validator enforces cannot diverge. Growth is an edit to these
7
+ * lists, measured by how often `other` is reached for.
8
+ *
9
+ * It lives in `contracts/` because the persisted profile carries these ids: a consumer
10
+ * rendering a stored group needs the category's meaning without importing the ledger,
11
+ * the schema module, or zod. Hosts do not extend it — the package owns the catalogue and
12
+ * versions it with the harness.
13
+ *
14
+ * Every category carries its nearest neighbour and the rule that separates them, because
15
+ * a category list without one is a list of labels an author picks by vibe. Every
16
+ * dimension category additionally carries a default treatment: whether a downstream step
17
+ * would TYPICALLY consume one value's files as a different substrate than another's.
18
+ * The agent follows the default and deviates only with a stated reason.
19
+ */
20
+ export declare const GROUP_ROLE_IDS: readonly ["data", "metadata", "index", "documentation", "reference"];
21
+ export type GroupRole = (typeof GROUP_ROLE_IDS)[number];
22
+ export interface GroupRoleEntry {
23
+ readonly id: GroupRole;
24
+ readonly definition: string;
25
+ }
26
+ /**
27
+ * Companions (`.bai`/`.tbi`/per-file `.md5`) take no role — they attach to members. A
28
+ * checksum or inventory file spanning MANY members is not a companion but a member of
29
+ * its own, category `manifest`; `index` is for tables that exist to be joined against,
30
+ * never for binary sidecars.
31
+ */
32
+ export declare const GROUP_ROLES: readonly GroupRoleEntry[];
33
+ export declare const GROUP_CATEGORY_IDS: readonly ["sequencing-reads", "alignment", "variant-calls", "copy-number", "structural-variants", "association-results", "regions-signal", "expression-matrix", "single-cell-matrix", "methylation", "proteomics", "metabolomics", "cytometry", "chemical-structures", "imaging", "clinical-table", "sample-annotation", "code", "document", "manifest", "reference-data", "other"];
34
+ export type GroupCategory = (typeof GROUP_CATEGORY_IDS)[number];
35
+ export interface GroupCategoryEntry {
36
+ readonly id: GroupCategory;
37
+ readonly definition: string;
38
+ /** The nearest neighbour and the rule that separates them. */
39
+ readonly note: string;
40
+ }
41
+ /**
42
+ * Coarse on purpose — a free `subtype` refines below the category. Content beats shape:
43
+ * a beta-value matrix is methylation whatever its layout, and a MAF is variant-calls
44
+ * whatever its extension. Grain and meaning decide.
45
+ */
46
+ export declare const GROUP_CATEGORIES: readonly GroupCategoryEntry[];
47
+ export declare const DIMENSION_CATEGORY_IDS: readonly ["subject", "sample", "organism-species", "model-system", "cohort-arm", "disease-state", "treatment", "timepoint", "tissue-site", "cell-type", "genetic-perturbation", "sex", "age-group", "developmental-stage", "environment", "assay-modality", "assay-target", "variant-origin", "sample-pairing", "processing-level", "assay-version", "batch", "replicate", "tool-variant", "library-prep", "data-partition", "other"];
48
+ export type DimensionCategory = (typeof DIMENSION_CATEGORY_IDS)[number];
49
+ export type DimensionScope = "biological" | "technical";
50
+ /**
51
+ * What the substrate test says for this category by default. `split` means one value's
52
+ * files are typically a different substrate to a downstream step, so the set is split
53
+ * into groups; `dimension` means the values are variants of one substrate and stay a
54
+ * slot, possibly bound to a dimension.
55
+ */
56
+ export type DimensionTreatment = "split" | "dimension";
57
+ export interface DimensionCategoryEntry {
58
+ readonly id: DimensionCategory;
59
+ readonly scope: DimensionScope;
60
+ readonly definition: string;
61
+ readonly defaultTreatment: DimensionTreatment;
62
+ /** Why the default reads the way it does, and the nearest neighbour it is not. */
63
+ readonly note: string;
64
+ }
65
+ export declare const DIMENSION_CATEGORIES: readonly DimensionCategoryEntry[];
66
+ export declare const DIMENSION_PROBE_IDS: readonly ["subject", "sample", "cohort-arm", "timepoint", "batch"];
67
+ export type DimensionProbe = (typeof DIMENSION_PROBE_IDS)[number];
68
+ /**
69
+ * A probe outcome, exactly one per probe.
70
+ *
71
+ * `not-found` is a correct and complete answer once the searched set is named — that is
72
+ * what removes the completeness pressure that otherwise invents dimensions. `attested`
73
+ * records prose-only evidence and can never justify a split.
74
+ */
75
+ export declare const PROBE_OUTCOME_IDS: readonly ["found", "not-found", "found-but-constant", "attested"];
76
+ export type ProbeOutcome = (typeof PROBE_OUTCOME_IDS)[number];
77
+ export interface DimensionProbeEntry {
78
+ readonly id: DimensionProbe;
79
+ /** Where a `found` typically comes from. */
80
+ readonly guidance: string;
81
+ }
82
+ /**
83
+ * The only dimensions the agent actively looks for. The searchable set is bounded and
84
+ * named: files in metadata- or documentation-role groups plus clinical-table and
85
+ * sample-annotation members, up to about ten files. Everything off this list is recorded
86
+ * only when encountered during ordinary orientation reading.
87
+ *
88
+ * Assay-modality and replicate are deliberately absent: varying modality is already
89
+ * surfaced by group categories, and probing for replicates invites inferring replicate
90
+ * structure from sample counts, which is fabrication by another name.
91
+ */
92
+ export declare const DIMENSION_PROBES: readonly DimensionProbeEntry[];
93
+ export declare function groupCategoryEntry(id: GroupCategory): GroupCategoryEntry;
94
+ export declare function dimensionCategoryEntry(id: DimensionCategory): DimensionCategoryEntry;
95
+ /**
96
+ * The scope a dimension takes.
97
+ *
98
+ * Derived from the category for everything in the catalogue. `other` is by definition
99
+ * outside it, so there is nothing to derive from and the scope is the agent's to declare:
100
+ * a biological thing that varies and has no category is still biological, and pinning
101
+ * every `other` to technical would misfile it. Undeclared, it defaults to technical.
102
+ */
103
+ export declare function dimensionScope(id: DimensionCategory, declared?: DimensionScope): DimensionScope;