@inflexa-ai/harness 0.28.1 → 0.29.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 (83) hide show
  1. package/dist/app/data-profile-orientation.d.ts +52 -27
  2. package/dist/app/data-profile-orientation.js +331 -92
  3. package/dist/app/data-profile-view.d.ts +70 -0
  4. package/dist/app/data-profile-view.js +123 -0
  5. package/dist/app/synthesize-run.js +1 -1
  6. package/dist/contracts/data-profile.d.ts +276 -25
  7. package/dist/contracts/index.d.ts +3 -1
  8. package/dist/contracts/index.js +1 -0
  9. package/dist/contracts/profile-vocabulary.d.ts +103 -0
  10. package/dist/contracts/profile-vocabulary.js +449 -0
  11. package/dist/contracts/target-dossier.d.ts +21 -21
  12. package/dist/execution/input-signature.d.ts +16 -3
  13. package/dist/execution/input-signature.js +0 -0
  14. package/dist/execution/post-step-pipeline.js +1 -1
  15. package/dist/execution/staged-input.d.ts +4 -4
  16. package/dist/index.d.ts +5 -1
  17. package/dist/index.js +7 -0
  18. package/dist/input-scan/clustering.d.ts +63 -0
  19. package/dist/input-scan/clustering.js +208 -0
  20. package/dist/input-scan/companions.d.ts +30 -0
  21. package/dist/input-scan/companions.js +138 -0
  22. package/dist/input-scan/decoder/container_readout.py +171 -0
  23. package/dist/input-scan/detect-sets.d.ts +24 -0
  24. package/dist/input-scan/detect-sets.js +279 -0
  25. package/dist/input-scan/enrich.d.ts +62 -19
  26. package/dist/input-scan/enrich.js +132 -187
  27. package/dist/input-scan/formats.d.ts +13 -5
  28. package/dist/input-scan/formats.js +13 -5
  29. package/dist/input-scan/index.d.ts +10 -4
  30. package/dist/input-scan/index.js +7 -4
  31. package/dist/input-scan/markers.d.ts +21 -0
  32. package/dist/input-scan/markers.js +89 -0
  33. package/dist/input-scan/menu.d.ts +127 -0
  34. package/dist/input-scan/menu.js +0 -0
  35. package/dist/input-scan/quarantine.d.ts +17 -0
  36. package/dist/input-scan/quarantine.js +69 -0
  37. package/dist/input-scan/readout-budget.d.ts +32 -0
  38. package/dist/input-scan/readout-budget.js +61 -0
  39. package/dist/input-scan/readout.d.ts +33 -0
  40. package/dist/input-scan/readout.js +336 -0
  41. package/dist/input-scan/scan.d.ts +5 -4
  42. package/dist/input-scan/scan.js +5 -5
  43. package/dist/input-scan/set-types.d.ts +209 -0
  44. package/dist/input-scan/set-types.js +15 -0
  45. package/dist/input-scan/templates.d.ts +93 -0
  46. package/dist/input-scan/templates.js +369 -0
  47. package/dist/input-scan/tokens.d.ts +94 -0
  48. package/dist/input-scan/tokens.js +267 -0
  49. package/dist/input-scan/tuning.d.ts +55 -0
  50. package/dist/input-scan/tuning.js +55 -0
  51. package/dist/input-scan/types.d.ts +2 -2
  52. package/dist/prompts/conversation.d.ts +1 -1
  53. package/dist/prompts/conversation.js +8 -6
  54. package/dist/prompts/report-session.d.ts +1 -1
  55. package/dist/prompts/report-session.js +3 -2
  56. package/dist/prompts/sandbox/data-profiler.d.ts +10 -1
  57. package/dist/prompts/sandbox/data-profiler.js +197 -53
  58. package/dist/prompts/sandbox-standards.d.ts +1 -1
  59. package/dist/prompts/sandbox-standards.js +10 -7
  60. package/dist/schemas/data-profile-schemas.d.ts +943 -80
  61. package/dist/schemas/data-profile-schemas.js +300 -115
  62. package/dist/state/data-profile.d.ts +1 -1
  63. package/dist/state/index.d.ts +1 -1
  64. package/dist/state/vector-store.d.ts +13 -0
  65. package/dist/state/vector-store.js +8 -0
  66. package/dist/tasks/data-profile-absorb.d.ts +78 -0
  67. package/dist/tasks/data-profile-absorb.js +285 -0
  68. package/dist/tasks/data-profile-index.d.ts +34 -25
  69. package/dist/tasks/data-profile-index.js +160 -59
  70. package/dist/tasks/data-profile-resolve.d.ts +72 -0
  71. package/dist/tasks/data-profile-resolve.js +639 -0
  72. package/dist/tasks/data-profile.d.ts +16 -5
  73. package/dist/tasks/data-profile.js +270 -131
  74. package/dist/tools/research/inspect-data-profile.d.ts +48 -22
  75. package/dist/tools/research/inspect-data-profile.js +73 -54
  76. package/dist/tools/workspace/scan-inputs.d.ts +4 -4
  77. package/dist/tools/workspace/scan-inputs.js +13 -14
  78. package/dist/tools/workspace/workspace-search.d.ts +11 -2
  79. package/dist/tools/workspace/workspace-search.js +44 -16
  80. package/dist/workflows/target-assessment/phase4-assemble.d.ts +21 -21
  81. package/package.json +5 -3
  82. package/dist/input-scan/coverage.d.ts +0 -22
  83. package/dist/input-scan/coverage.js +0 -85
@@ -0,0 +1,285 @@
1
+ /**
2
+ * Incremental absorption — replaying a completed profile's recipe against a fresh scan.
3
+ *
4
+ * The recipe is keyed to scanner TEMPLATES, so it survives the scan that produced it: a
5
+ * tree that gained files for ten more subjects instantiates the same templates, and the
6
+ * operations that grouped it still say what its files are. Replaying them re-derives
7
+ * membership, counts, and the partition deterministically — no sandbox, no model — and the
8
+ * profile completes on arithmetic alone.
9
+ *
10
+ * Three outcomes past "there was nothing to replay". A replay claiming every kept file is a
11
+ * FULL absorb and completes the profile. A replay leaving files unclaimed is a PARTIAL
12
+ * absorb: those files are the delta, and the agent sees them alongside the resolution that
13
+ * already stands rather than an empty page. A recipe whose templates no longer resolve is
14
+ * STRANDED and falls back to a full re-profile — loudly, because silently absorbing the
15
+ * half that still resolves would persist a profile describing part of a tree while
16
+ * claiming to describe all of it.
17
+ *
18
+ * Group ids survive a replay because the reconstructed annotation carries the PERSISTED
19
+ * group name, which is what the original resolution derived the id from — including the
20
+ * `name — value` a slot split minted. That is checked rather than trusted: every group id
21
+ * the recipe named must be present in the replayed resolution, so a step that resolved to
22
+ * nothing strands the recipe instead of quietly deleting a group and renumbering its
23
+ * siblings out from under a dimension's `groupIds`.
24
+ *
25
+ * Dimensions are re-resolved, not copied. Their slot observations are persisted as
26
+ * template + slot position, so a replay binds each to whatever the FRESH scan has there
27
+ * and recomputes its cardinality and values — a scan-scoped slot id would name a
28
+ * different slot after the set order changed, and the numbers riding on it would describe
29
+ * some other slot entirely.
30
+ */
31
+ import { resolveProfileSubmission } from "./data-profile-resolve.js";
32
+ /** Delta paths named in the repair briefing before the tail folds into a count. */
33
+ export const MAX_DELTA_PATHS = 30;
34
+ /** Every kept path of a scan — members and leftovers alike. Companions ride on their member. */
35
+ function keptPaths(detected) {
36
+ const paths = new Set();
37
+ for (const set of detected.sets)
38
+ for (const member of set.members)
39
+ paths.add(member.path);
40
+ for (const member of detected.leftoverMembers)
41
+ paths.add(member.path);
42
+ return paths;
43
+ }
44
+ /**
45
+ * A persisted group read back as the annotation that produced it.
46
+ *
47
+ * `role` and `category` are widened to `string` on the contract, because a consumer
48
+ * rendering an old row must not fail on a retired id. They are carried through as they
49
+ * stand: a replay records what the profile already said, and re-adjudicating a category
50
+ * against today's catalogue would be authoring, which is the one thing a replay must not do.
51
+ */
52
+ function annotationOf(group, kept) {
53
+ const annotations = (group.memberAnnotations ?? []).filter((entry) => kept.has(entry.path));
54
+ return {
55
+ name: group.name,
56
+ memberRepresents: group.memberRepresents,
57
+ description: group.description,
58
+ role: group.role,
59
+ category: group.category,
60
+ ...(group.categoryLabel ? { categoryLabel: group.categoryLabel } : {}),
61
+ ...(group.subtype ? { subtype: group.subtype } : {}),
62
+ ...(group.categoryReason ? { categoryReason: group.categoryReason } : {}),
63
+ ...(annotations.length > 0 ? { memberAnnotations: annotations.map((entry) => ({ path: entry.path, note: entry.note })) } : {}),
64
+ };
65
+ }
66
+ class StrandedRecipe extends Error {
67
+ }
68
+ function requireSet(ctx, template, op) {
69
+ const set = ctx.setsByTemplate.get(template);
70
+ if (!set)
71
+ throw new StrandedRecipe(`${op}: the template "${template}" is not on the fresh menu`);
72
+ return set;
73
+ }
74
+ function requireGroup(ctx, groupId, op) {
75
+ const group = groupId ? ctx.groupsById.get(groupId) : undefined;
76
+ if (!group)
77
+ throw new StrandedRecipe(`${op}: the recipe names group "${groupId ?? "(none)"}", which the profile does not carry`);
78
+ return group;
79
+ }
80
+ /**
81
+ * Rebuild one recipe step as the operation that produced it.
82
+ *
83
+ * A split replays as a value mapping whatever it was authored as, and that is deliberate:
84
+ * a slot value the mapping does not name is a value nobody has judged, so its members fall
85
+ * out unclaimed and become the delta rather than landing in a group by default.
86
+ * `undefined` means the step addressed only files that are gone.
87
+ */
88
+ function replayStep(ctx, step) {
89
+ if (step.op === "use") {
90
+ const set = requireSet(ctx, step.templates[0] ?? "", "use");
91
+ return { op: "use", setId: set.id, group: annotationOf(requireGroup(ctx, step.groupIds[0], "use"), ctx.kept) };
92
+ }
93
+ if (step.op === "merge") {
94
+ const sets = step.templates.map((template) => requireSet(ctx, template, "merge"));
95
+ const group = requireGroup(ctx, step.groupIds[0], "merge");
96
+ return { op: "merge", setIds: sets.map((set) => set.id), group: annotationOf(group, ctx.kept), reason: group.reason ?? "" };
97
+ }
98
+ if (step.op === "group") {
99
+ const paths = (step.paths ?? []).filter((path) => ctx.kept.has(path));
100
+ // Every path gone means the group is gone. Absorbing the rest would report a FULL
101
+ // absorb over a profile that silently lost a group, renumbered its siblings, and
102
+ // left every dimension bound to it pointing at nothing.
103
+ if (paths.length === 0)
104
+ throw new StrandedRecipe(`group: every path the recipe grouped as "${step.groupIds[0] ?? "(none)"}" is gone from the tree`);
105
+ return { op: "group", paths, group: annotationOf(requireGroup(ctx, step.groupIds[0], "group"), ctx.kept) };
106
+ }
107
+ const set = requireSet(ctx, step.templates[0] ?? "", "split");
108
+ const slot = step.slotIndex !== undefined ? set.slots[step.slotIndex] : undefined;
109
+ if (!slot)
110
+ throw new StrandedRecipe(`split: the template "${set.pathTemplate}" no longer carries a slot at position ${step.slotIndex ?? "(none)"}`);
111
+ const mapped = (step.valueMapping ?? [])
112
+ .filter((entry) => entry.values.length > 0)
113
+ .map((entry) => ({ values: [...entry.values], group: annotationOf(requireGroup(ctx, entry.groupId, "split"), ctx.kept) }));
114
+ if (mapped.length === 0)
115
+ throw new StrandedRecipe(`split: the recipe for "${set.pathTemplate}" maps no slot value to a group`);
116
+ const reason = ctx.groupsById.get(step.groupIds[0] ?? "")?.reason ?? "";
117
+ return { op: "split", setId: set.id, by: { kind: "values", slotId: slot.id, groups: mapped }, reason };
118
+ }
119
+ /**
120
+ * Rebind one persisted dimension to the fresh scan.
121
+ *
122
+ * A slot observation is re-addressed through its template binding and nothing else: its
123
+ * cardinality and values are computed, not asserted, so carrying the persisted numbers
124
+ * across a scan would restate an old measurement under a new one's name. A binding that
125
+ * no longer resolves strands the recipe — rebinding to whatever now occupies that
126
+ * position would silently reattach the dimension to a different slot.
127
+ */
128
+ function replayDimension(ctx, dimension) {
129
+ const observations = dimension.observations.map((observation) => {
130
+ if (observation.kind !== "slot")
131
+ return { ...observation };
132
+ const binding = observation.binding;
133
+ if (!binding) {
134
+ throw new StrandedRecipe(`dimension "${dimension.label}": a slot observation carries no template binding, so it cannot be re-resolved`);
135
+ }
136
+ const set = ctx.setsByTemplate.get(binding.template);
137
+ if (!set)
138
+ throw new StrandedRecipe(`dimension "${dimension.label}": the template "${binding.template}" is not on the fresh menu`);
139
+ const slot = set.slots[binding.slotIndex];
140
+ if (!slot) {
141
+ throw new StrandedRecipe(`dimension "${dimension.label}": the template "${binding.template}" no longer carries a slot at position ${binding.slotIndex}`);
142
+ }
143
+ return { kind: "slot", setId: set.id, slotId: slot.id, ...(observation.note ? { note: observation.note } : {}) };
144
+ });
145
+ return {
146
+ label: dimension.label,
147
+ category: dimension.category,
148
+ ...(dimension.categoryLabel ? { categoryLabel: dimension.categoryLabel } : {}),
149
+ ...(dimension.scope ? { scope: dimension.scope } : {}),
150
+ ...(dimension.description ? { description: dimension.description } : {}),
151
+ observations,
152
+ ...(dimension.reconciliations && dimension.reconciliations.length > 0
153
+ ? { reconciliations: dimension.reconciliations.map((entry) => ({ ...entry })) }
154
+ : {}),
155
+ ...(dimension.nestsUnder ? { nestsUnder: { ...dimension.nestsUnder } } : {}),
156
+ ...(dimension.treatmentReason ? { treatmentReason: dimension.treatmentReason } : {}),
157
+ };
158
+ }
159
+ /** Every group id the authored steps named, including a value-mapped split's per-value groups. */
160
+ function namedGroupIds(steps) {
161
+ const ids = new Set();
162
+ for (const step of steps) {
163
+ for (const id of step.groupIds)
164
+ ids.add(id);
165
+ for (const entry of step.valueMapping ?? [])
166
+ ids.add(entry.groupId);
167
+ }
168
+ return [...ids];
169
+ }
170
+ /**
171
+ * Replay a completed profile's recipe against a fresh scan.
172
+ *
173
+ * Pure. Strandedness is decided both while rebuilding the operations — a template, slot,
174
+ * group id, or dimension binding that no longer resolves — and after resolving them, by
175
+ * checking that every group the recipe named came back. What remains is a fact about the
176
+ * tree having moved: files the recipe does not account for.
177
+ *
178
+ * The residue is replayed too. The recipe carries the paths the last resolution swept, so
179
+ * a file that profile already declined to classify re-sweeps deterministically and only a
180
+ * file NEW to the tree is delta — without that an unchanged tree would re-absorb as
181
+ * partial and wake the agent to re-judge what it already judged.
182
+ */
183
+ export function absorbRecipe(prior, detected, menu) {
184
+ if (!prior?.recipe?.length || !prior.groups?.length)
185
+ return { kind: "none" };
186
+ const ctx = {
187
+ setsByTemplate: new Map(menu.sets.map((set) => [set.pathTemplate, set])),
188
+ groupsById: new Map(prior.groups.map((group) => [group.id, group])),
189
+ kept: keptPaths(detected),
190
+ };
191
+ const authored = prior.recipe.filter((step) => step.op !== "unclassified");
192
+ const sweep = prior.recipe.find((step) => step.op === "unclassified");
193
+ if (authored.length === 0)
194
+ return { kind: "none" };
195
+ let operations;
196
+ let dimensions;
197
+ try {
198
+ operations = authored.map((step) => replayStep(ctx, step)).filter((operation) => operation !== undefined);
199
+ dimensions = (prior.dimensions ?? []).map((dimension) => replayDimension(ctx, dimension));
200
+ }
201
+ catch (err) {
202
+ if (err instanceof StrandedRecipe)
203
+ return { kind: "stranded", reason: err.message };
204
+ throw err;
205
+ }
206
+ if (operations.length !== authored.length) {
207
+ return { kind: "stranded", reason: "a step of the recipe addressed files or templates that are gone" };
208
+ }
209
+ const submission = {
210
+ operations,
211
+ ...(dimensions.length > 0 ? { dimensions } : {}),
212
+ analysisSummary: prior.summary,
213
+ domain: prior.domain ?? "",
214
+ organism: prior.organism ?? null,
215
+ };
216
+ // Final round by construction: no agent is awake to repair anything this raises, so a
217
+ // contested file sweeps rather than being awarded to whichever step ran first.
218
+ const resolution = resolveProfileSubmission(submission, detected, menu, { finalRound: true });
219
+ const missing = namedGroupIds(authored).filter((id) => !resolution.groups.some((group) => group.id === id));
220
+ if (missing.length > 0) {
221
+ return { kind: "stranded", reason: `the replay produced no group for ${missing.join(", ")}, so the recipe no longer describes the tree` };
222
+ }
223
+ if (resolution.dimensions.length !== (prior.dimensions?.length ?? 0)) {
224
+ return { kind: "stranded", reason: "a dimension of the profile did not re-resolve against the fresh scan" };
225
+ }
226
+ const swept = new Set(sweep?.paths ?? []);
227
+ const delta = resolution.unclaimed.filter((path) => !swept.has(path));
228
+ if (delta.length === 0)
229
+ return { kind: "full", operations, resolution };
230
+ return { kind: "partial", operations, resolution, delta };
231
+ }
232
+ /** A carried-forward group in full, so resubmitting it verbatim needs no recall. */
233
+ function renderAnnotation(group, indent = "") {
234
+ const lines = [
235
+ `${indent} - name: ${group.name}`,
236
+ `${indent} - role/category: ${group.role} / ${group.category}${group.subtype ? ` (${group.subtype})` : ""}`,
237
+ `${indent} - memberRepresents: ${group.memberRepresents}`,
238
+ `${indent} - description: ${group.description}`,
239
+ ];
240
+ for (const annotation of group.memberAnnotations ?? [])
241
+ lines.push(`${indent} - member ${annotation.path}: ${annotation.note}`);
242
+ return lines;
243
+ }
244
+ /**
245
+ * The repair briefing for a partial absorb.
246
+ *
247
+ * It hands the agent the resolution that already stands, in fresh menu terms, plus the
248
+ * files it does not cover. A drift event that added one directory should cost one
249
+ * operation, not a re-authoring of a grouping nobody disputes.
250
+ */
251
+ export function renderAbsorbDelta(outcome) {
252
+ const lines = [];
253
+ lines.push("This analysis was profiled before, and the previous profile's operations still resolve");
254
+ lines.push("against the menu above. They are carried forward here, ready to resubmit as they stand:");
255
+ lines.push("");
256
+ for (const operation of outcome.operations) {
257
+ if (operation.op === "use")
258
+ lines.push(`- \`use\` ${operation.setId}`, ...renderAnnotation(operation.group));
259
+ else if (operation.op === "merge")
260
+ lines.push(`- \`merge\` ${operation.setIds.join(" + ")} — ${operation.reason}`, ...renderAnnotation(operation.group));
261
+ else if (operation.op === "group")
262
+ lines.push(`- \`group\` ${operation.paths.join(", ")}`, ...renderAnnotation(operation.group));
263
+ else {
264
+ lines.push(`- \`split\` ${operation.setId} by ${operation.by.slotId} — ${operation.reason}`);
265
+ if (operation.by.kind === "values") {
266
+ for (const mapped of operation.by.groups)
267
+ lines.push(` - values ${mapped.values.join(", ")}`, ...renderAnnotation(mapped.group, " "));
268
+ }
269
+ }
270
+ }
271
+ lines.push("");
272
+ lines.push(`${outcome.delta.length} kept files are NEW to the tree and accounted for by nothing above:`);
273
+ for (const path of outcome.delta.slice(0, MAX_DELTA_PATHS))
274
+ lines.push(`- ${path}`);
275
+ if (outcome.delta.length > MAX_DELTA_PATHS)
276
+ lines.push(`- … ${outcome.delta.length - MAX_DELTA_PATHS} more`);
277
+ lines.push("");
278
+ lines.push("This is a REPAIR ROUND OVER THE DELTA, not a re-profile. Work out what those files are");
279
+ lines.push("and add the operations that claim them. Then resubmit the WHOLE operation list — the");
280
+ lines.push("carried-forward ones unchanged unless the new files change what a group MEANS, plus");
281
+ lines.push("yours. Do not re-author what already resolves, and do not re-read files the carried");
282
+ lines.push("operations already describe. Anything you still leave unclaimed sweeps into `unclassified`,");
283
+ lines.push("as the previous profile's own residue already has.");
284
+ return lines.join("\n");
285
+ }
@@ -1,28 +1,39 @@
1
1
  /**
2
2
  * The profile's vector index, as a pure projection.
3
3
  *
4
- * The index is a function of the scan crossed with the submitted kinds: for each kind,
5
- * one entry; for each entity, one entry templated from the kinds it participates in and
6
- * its own axis value. It has no persisted representation of its own and nothing derives
7
- * from it, so it can be rebuilt at any time by re-running the scan and reading `kinds`
8
- * off the existing profile — no model call, no re-profile. Improving the template,
9
- * adding a tier, or folding in entity attributes later are all re-projections.
4
+ * The index is a function of the persisted profile crossed with the scan it was taken
5
+ * against. It has no durable representation of its own and nothing derives from it, so it
6
+ * can be rebuilt at any time from those two things — no model call, no re-profile.
10
7
  *
11
- * Two tiers, because they answer different queries. A query naming a KIND of data
12
- * ("variant calls") wants one result carrying the set's count and path pattern; a
13
- * per-entity tier would return five arbitrary subjects out of thousands of
14
- * indistinguishable ones. A query naming an ENTITY ("PT0421") wants that entity. A
15
- * per-file tier adds nothing: a file is identified by its kind and its entity, and its
16
- * path is on the filesystem.
8
+ * Three tiers, because they answer three different queries. A query naming a KIND of data
9
+ * ("variant calls") wants the group: its meaning, its count, its template. A query naming
10
+ * what VARIES ("timepoint") wants the dimension and the evidence behind it. A query naming
11
+ * a particular FILE wants that file — but only the members an agent wrote about
12
+ * individually are worth an entry, because a member entry templated from its group's text
13
+ * is a near-duplicate of the group entry, and thousands of them are recall noise rather
14
+ * than recall.
17
15
  *
18
- * `type: "input"` keeps its existing meaning for the entity tier, so searches written
19
- * against it keep matching; `type: "input-kind"` is additive. Renaming `"input"` would
20
- * have silently broken existing search on every new analysis.
16
+ * `type: "input"` keeps its existing meaning — one entry addressing one workspace path —
17
+ * so a search filtered to it matches whichever era wrote the profile it reads.
18
+ *
19
+ * Every file-addressed entry stamps its workspace path into `metadata.path`; pattern
20
+ * entries (group, dimension, kind, entity) never do. That presence is the discriminator
21
+ * `workspace_search` documents, so a consumer needs no id parsing to tell them apart.
21
22
  */
23
+ import type { DataProfileResult } from "../contracts/data-profile.js";
22
24
  import type { InputScan } from "../input-scan/types.js";
23
- import type { DataProfileAxis, DataProfileFile, DataProfileKind } from "../state/data-profile.js";
24
- /** Entity entries built per analysis. Past this the entity tier is a liability, not a search aid. */
25
+ /** Entity entries built for a legacy profile. Past this the entity tier is a liability, not a search aid. */
25
26
  export declare const MAX_ENTITY_ENTRIES = 20000;
27
+ /**
28
+ * Every metadata `type` a profile writes, in either era.
29
+ *
30
+ * The index is a pure PROJECTION, so re-indexing replaces rather than merges: an upsert
31
+ * keyed by entry id leaves the entries of a renamed group, a dropped dimension, or a
32
+ * de-annotated member behind forever, searchable and wrong. Clearing exactly these types
33
+ * clears exactly what a profile wrote — step outputs, summaries, and syntheses carry their
34
+ * own types and are untouched.
35
+ */
36
+ export declare const PROFILE_INDEX_TYPES: readonly ["input-group", "input-dimension", "input", "input-kind"];
26
37
  export interface ProfileIndexEntry {
27
38
  readonly id: string;
28
39
  readonly text: string;
@@ -30,16 +41,14 @@ export interface ProfileIndexEntry {
30
41
  }
31
42
  export interface BuildProfileIndexArgs {
32
43
  readonly analysisId: string;
33
- readonly kinds: readonly DataProfileKind[];
34
- readonly axes?: readonly DataProfileAxis[];
35
- /** The scan the profile was taken against — the source of the entity value set. */
44
+ /** The profile as persisted. Whichever era wrote it, the entries are derived from it alone. */
45
+ readonly result: DataProfileResult;
46
+ /** The scan the profile was taken against — the source of a legacy profile's entity value set. */
36
47
  readonly scan?: InputScan;
37
- /** Individually described files, indexed only when there are no kinds to index. */
38
- readonly files?: readonly DataProfileFile[];
39
48
  }
40
49
  /**
41
- * Build every index entry for a completed profile. Deterministic and model-free: the
42
- * text is templated from the kind's own description and the entity's axis value, so
43
- * indexing costs no LLM tokens whatever the dataset's size.
50
+ * Build every index entry for a completed profile. Deterministic and model-free: each
51
+ * entry's text is templated from what the agent already wrote and what resolution already
52
+ * computed, so indexing costs no LLM tokens whatever the dataset's size.
44
53
  */
45
54
  export declare function buildProfileIndexEntries(args: BuildProfileIndexArgs): ProfileIndexEntry[];