@inflexa-ai/harness 0.42.2 → 0.43.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. package/README.md +2 -2
  2. package/dist/agents/sandbox/shared.d.ts +1 -6
  3. package/dist/agents/sandbox/shared.js +0 -15
  4. package/dist/app/chat-turn.js +13 -1
  5. package/dist/config/environment-stores.d.ts +4 -2
  6. package/dist/contracts/chat-events.d.ts +9 -1
  7. package/dist/contracts/chat-frame.d.ts +3 -3
  8. package/dist/contracts/chat-frame.js +8 -3
  9. package/dist/contracts/chat-parts.d.ts +4 -1
  10. package/dist/contracts/data-profile.d.ts +4 -6
  11. package/dist/contracts/index.d.ts +1 -1
  12. package/dist/contracts/report-blocks.d.ts +1364 -75
  13. package/dist/contracts/report-blocks.js +221 -29
  14. package/dist/contracts/report-reference.js +1 -1
  15. package/dist/contracts/schemas/chat-events.d.ts +13 -0
  16. package/dist/contracts/schemas/chat-events.js +5 -0
  17. package/dist/contracts/schemas/chat-parts.d.ts +2 -2
  18. package/dist/contracts/schemas/index.d.ts +1 -1
  19. package/dist/contracts/schemas/index.js +1 -1
  20. package/dist/execution/input-signature.d.ts +7 -13
  21. package/dist/execution/input-signature.js +0 -0
  22. package/dist/execution/run-canceler.d.ts +3 -2
  23. package/dist/execution/run-canceler.js +3 -2
  24. package/dist/execution/run-event-stream.d.ts +16 -2
  25. package/dist/execution/run-event-stream.js +54 -3
  26. package/dist/index.d.ts +1 -8
  27. package/dist/index.js +5 -16
  28. package/dist/input-scan/enrich.d.ts +0 -2
  29. package/dist/input-scan/enrich.js +0 -1
  30. package/dist/lib/metrics.js +6 -7
  31. package/dist/lib/otel-spans.d.ts +4 -9
  32. package/dist/lib/otel-spans.js +3 -8
  33. package/dist/loop/run-agent.d.ts +4 -2
  34. package/dist/loop/run-agent.js +38 -23
  35. package/dist/memory/thread-history.d.ts +1 -1
  36. package/dist/prompts/report-session.d.ts +1 -1
  37. package/dist/prompts/report-session.js +17 -0
  38. package/dist/provenance/exec-frame.d.ts +1 -1
  39. package/dist/report-model/authoring-grammar.d.ts +416 -17
  40. package/dist/report-model/authoring-grammar.js +25 -4
  41. package/dist/report-model/block-walk.d.ts +20 -4
  42. package/dist/report-model/block-walk.js +64 -7
  43. package/dist/report-model/draft-finish.d.ts +6 -2
  44. package/dist/report-model/draft-finish.js +6 -1
  45. package/dist/report-model/production-resolver.d.ts +8 -0
  46. package/dist/report-model/production-resolver.js +1 -1
  47. package/dist/report-model/validate.d.ts +20 -10
  48. package/dist/report-model/validate.js +29 -13
  49. package/dist/report-render/chart-export.d.ts +52 -0
  50. package/dist/report-render/chart-export.js +192 -0
  51. package/dist/report-render/chart-presets.d.ts +46 -47
  52. package/dist/report-render/chart-presets.js +31 -101
  53. package/dist/report-render/chart-renderers.d.ts +163 -0
  54. package/dist/report-render/chart-renderers.js +1055 -0
  55. package/dist/report-render/chart-toolbox.d.ts +124 -0
  56. package/dist/report-render/chart-toolbox.js +595 -0
  57. package/dist/report-render/chart.d.ts +60 -27
  58. package/dist/report-render/chart.js +2323 -524
  59. package/dist/report-render/design.d.ts +476 -16
  60. package/dist/report-render/design.js +390 -56
  61. package/dist/report-render/figures/common.d.ts +321 -0
  62. package/dist/report-render/figures/common.js +759 -0
  63. package/dist/report-render/figures/dendrogram.d.ts +36 -0
  64. package/dist/report-render/figures/dendrogram.js +130 -0
  65. package/dist/report-render/figures/dense.d.ts +111 -0
  66. package/dist/report-render/figures/dense.js +204 -0
  67. package/dist/report-render/figures/dotplot.d.ts +28 -0
  68. package/dist/report-render/figures/dotplot.js +234 -0
  69. package/dist/report-render/figures/embedding.d.ts +43 -0
  70. package/dist/report-render/figures/embedding.js +360 -0
  71. package/dist/report-render/figures/equal-units.d.ts +101 -0
  72. package/dist/report-render/figures/equal-units.js +215 -0
  73. package/dist/report-render/figures/forest.d.ts +20 -0
  74. package/dist/report-render/figures/forest.js +398 -0
  75. package/dist/report-render/figures/gsea.d.ts +30 -0
  76. package/dist/report-render/figures/gsea.js +391 -0
  77. package/dist/report-render/figures/heatmap.d.ts +40 -0
  78. package/dist/report-render/figures/heatmap.js +395 -0
  79. package/dist/report-render/figures/index.d.ts +135 -0
  80. package/dist/report-render/figures/index.js +100 -0
  81. package/dist/report-render/figures/km.d.ts +16 -0
  82. package/dist/report-render/figures/km.js +406 -0
  83. package/dist/report-render/figures/label-room.d.ts +130 -0
  84. package/dist/report-render/figures/label-room.js +341 -0
  85. package/dist/report-render/figures/locuszoom.d.ts +50 -0
  86. package/dist/report-render/figures/locuszoom.js +675 -0
  87. package/dist/report-render/figures/lollipop.d.ts +16 -0
  88. package/dist/report-render/figures/lollipop.js +247 -0
  89. package/dist/report-render/figures/ma.d.ts +20 -0
  90. package/dist/report-render/figures/ma.js +114 -0
  91. package/dist/report-render/figures/manhattan.d.ts +60 -0
  92. package/dist/report-render/figures/manhattan.js +343 -0
  93. package/dist/report-render/figures/oncoprint.d.ts +41 -0
  94. package/dist/report-render/figures/oncoprint.js +420 -0
  95. package/dist/report-render/figures/pca.d.ts +37 -0
  96. package/dist/report-render/figures/pca.js +241 -0
  97. package/dist/report-render/figures/qq.d.ts +30 -0
  98. package/dist/report-render/figures/qq.js +182 -0
  99. package/dist/report-render/figures/roc.d.ts +17 -0
  100. package/dist/report-render/figures/roc.js +116 -0
  101. package/dist/report-render/figures/sankey.d.ts +48 -0
  102. package/dist/report-render/figures/sankey.js +353 -0
  103. package/dist/report-render/figures/upset.d.ts +60 -0
  104. package/dist/report-render/figures/upset.js +316 -0
  105. package/dist/report-render/figures/volcano.d.ts +25 -0
  106. package/dist/report-render/figures/volcano.js +166 -0
  107. package/dist/report-render/hybrid-svg.d.ts +75 -0
  108. package/dist/report-render/hybrid-svg.js +504 -0
  109. package/dist/report-render/number-format.d.ts +19 -0
  110. package/dist/report-render/number-format.js +25 -1
  111. package/dist/report-render/page.d.ts +41 -4
  112. package/dist/report-render/page.js +247 -12
  113. package/dist/report-render/render.d.ts +13 -4
  114. package/dist/report-render/render.js +37 -6
  115. package/dist/report-render/types.d.ts +23 -2
  116. package/dist/report-render/value-bridge.d.ts +38 -3
  117. package/dist/report-render/value-bridge.js +137 -6
  118. package/dist/report-render/views/chart-view.d.ts +42 -6
  119. package/dist/report-render/views/chart-view.js +70 -9
  120. package/dist/report-render/views/page-view.js +9 -10
  121. package/dist/runtime/assemble.js +2 -2
  122. package/dist/sandbox/client.d.ts +23 -32
  123. package/dist/sandbox/client.js +3 -11
  124. package/dist/sandbox/create-sandbox.d.ts +9 -24
  125. package/dist/sandbox/create-sandbox.js +15 -30
  126. package/dist/sandbox/docker-client.d.ts +12 -22
  127. package/dist/sandbox/docker-client.js +29 -43
  128. package/dist/sandbox/exec.d.ts +58 -0
  129. package/dist/sandbox/exec.js +197 -0
  130. package/dist/sandbox/identity.d.ts +5 -5
  131. package/dist/sandbox/identity.js +7 -10
  132. package/dist/sandbox/k8s-client.d.ts +4 -10
  133. package/dist/sandbox/k8s-client.js +5 -12
  134. package/dist/sandbox/liveness.d.ts +5 -12
  135. package/dist/sandbox/liveness.js +5 -12
  136. package/dist/sandbox/notification-sweep.d.ts +4 -7
  137. package/dist/sandbox/notification-sweep.js +4 -7
  138. package/dist/sandbox/reaper.d.ts +3 -3
  139. package/dist/sandbox/reaper.js +3 -3
  140. package/dist/sandbox/sandbox-error.d.ts +10 -11
  141. package/dist/sandbox/sandbox-error.js +10 -11
  142. package/dist/sandbox/sandbox-step-translate.d.ts +1 -1
  143. package/dist/sandbox/sandbox-step-translate.js +1 -1
  144. package/dist/sandbox/types.d.ts +31 -99
  145. package/dist/sandbox/types.js +17 -84
  146. package/dist/state/active-sandboxes.d.ts +7 -32
  147. package/dist/state/active-sandboxes.js +11 -46
  148. package/dist/state/index.d.ts +1 -2
  149. package/dist/state/index.js +1 -1
  150. package/dist/state/migrations/20261005120000_drop_the_active_exec_tracking.d.ts +7 -0
  151. package/dist/state/migrations/20261005120000_drop_the_active_exec_tracking.js +12 -0
  152. package/dist/state/migrations/index.js +2 -0
  153. package/dist/state/schema.d.ts +1 -5
  154. package/dist/state/schema.js +1 -6
  155. package/dist/state/step-executions.js +1 -2
  156. package/dist/tasks/data-profile.js +7 -20
  157. package/dist/tasks/derive-table-exec.d.ts +4 -14
  158. package/dist/tasks/derive-table-exec.js +9 -24
  159. package/dist/tasks/extract-values.d.ts +2 -2
  160. package/dist/tasks/extract-values.js +4 -7
  161. package/dist/tools/define-tool.d.ts +56 -13
  162. package/dist/tools/define-tool.js +28 -1
  163. package/dist/tools/execute-analysis.js +3 -3
  164. package/dist/tools/report-session/derive-table.d.ts +8 -9
  165. package/dist/tools/report-session/derive-table.js +3 -4
  166. package/dist/tools/report-session/preview-report.js +4 -49
  167. package/dist/tools/sandbox/list-available-packages.d.ts +1 -1
  168. package/dist/tools/sandbox/list-available-packages.js +5 -4
  169. package/dist/tools/workspace/edit-file.d.ts +5 -4
  170. package/dist/tools/workspace/edit-file.js +10 -13
  171. package/dist/tools/workspace/execute-command.d.ts +27 -42
  172. package/dist/tools/workspace/execute-command.js +42 -38
  173. package/dist/tools/workspace/index.d.ts +5 -6
  174. package/dist/tools/workspace/index.js +5 -6
  175. package/dist/tools/workspace/mutator.d.ts +43 -8
  176. package/dist/tools/workspace/mutator.js +45 -34
  177. package/dist/tools/workspace/run-exec.d.ts +3 -12
  178. package/dist/tools/workspace/run-exec.js +7 -24
  179. package/dist/tools/workspace/scan-inputs.d.ts +0 -3
  180. package/dist/tools/workspace/scan-inputs.js +0 -1
  181. package/dist/tools/workspace/write-file.d.ts +12 -3
  182. package/dist/tools/workspace/write-file.js +17 -13
  183. package/dist/workflows/sandbox-step.d.ts +7 -11
  184. package/dist/workflows/sandbox-step.js +24 -27
  185. package/package.json +5 -3
  186. package/dist/sandbox/await-exec.d.ts +0 -143
  187. package/dist/sandbox/await-exec.js +0 -417
  188. package/dist/sandbox/deliver-exec-event.d.ts +0 -25
  189. package/dist/sandbox/deliver-exec-event.js +0 -29
  190. package/dist/sandbox/digest.d.ts +0 -14
  191. package/dist/sandbox/digest.js +0 -17
  192. package/dist/sandbox/exec-id.d.ts +0 -12
  193. package/dist/sandbox/exec-id.js +0 -20
  194. package/dist/sandbox/hmac.d.ts +0 -62
  195. package/dist/sandbox/hmac.js +0 -72
  196. package/dist/sandbox/submit-exec.d.ts +0 -38
  197. package/dist/sandbox/submit-exec.js +0 -80
  198. package/dist/sandbox/watchdog.d.ts +0 -71
  199. package/dist/sandbox/watchdog.js +0 -161
@@ -14,12 +14,40 @@
14
14
  * rule only when an extra field fails the parse.
15
15
  */
16
16
  import { z } from "zod";
17
- import { ArtifactFileReferenceSchema, ArtifactTableReferenceSchema, CitationReferenceSchema, ReferenceSchema, ScalarReferenceSchema, } from "./report-reference.js";
17
+ import { ArtifactFileReferenceSchema, ArtifactTableReferenceSchema, ArtifactValueReferenceSchema, CitationReferenceSchema, ReferenceSchema, ScalarReferenceSchema, } from "./report-reference.js";
18
18
  /**
19
- * The chart vocabulary. The first seven values name one plot form each. The last four are presets of a
20
- * scientific plot, and the renderer expands a preset into a composition.
19
+ * The chart vocabulary. The first eleven values name one plot form each. Each value after them is a preset:
20
+ * the canonical figure of one plot of a field, which the renderer derives from its own figure module.
21
21
  */
22
- const ChartTypeSchema = z.enum(["bar", "line", "scatter", "histogram", "box", "heatmap", "pie", "volcano", "manhattan", "ma", "km"]);
22
+ const ChartTypeSchema = z.enum([
23
+ "bar",
24
+ "line",
25
+ "scatter",
26
+ "histogram",
27
+ "box",
28
+ "heatmap",
29
+ "pie",
30
+ "violin",
31
+ "stacked-bar",
32
+ "normalized-bar",
33
+ "radar",
34
+ "volcano",
35
+ "manhattan",
36
+ "ma",
37
+ "km",
38
+ "pca",
39
+ "embedding",
40
+ "dotplot",
41
+ "forest",
42
+ "roc",
43
+ "qq",
44
+ "gsea",
45
+ "oncoprint",
46
+ "lollipop",
47
+ "upset",
48
+ "sankey",
49
+ "locuszoom",
50
+ ]);
23
51
  /** The per-row transforms of a channel. Each derived value comes from one cell. */
24
52
  const ChartTransformSchema = z.enum(["log10", "neg_log10", "abs", "rank"]);
25
53
  /**
@@ -28,52 +56,170 @@ const ChartTransformSchema = z.enum(["log10", "neg_log10", "abs", "rank"]);
28
56
  */
29
57
  const ChartOrientationSchema = z.enum(["vertical", "horizontal"]);
30
58
  /**
31
- * The threshold pair of a preset that classifies its rows.
59
+ * The thresholds of a preset that classifies its rows.
32
60
  *
33
61
  * One declaration moves the guide lines and the color split together. Thus the split always lands on the
34
- * drawn lines, and no second declaration can disagree with the first one.
62
+ * drawn lines, and no second declaration can disagree with the first one. A volcano reads the pair. An MA
63
+ * reads the significance cut against its `p` column alone, thus the chart block holds the effect cut to the
64
+ * volcano.
35
65
  */
36
66
  const ChartThresholdsSchema = z.strictObject({
37
67
  significance: z
38
68
  .number()
39
69
  .positive()
40
- .describe("The significance cut of the p column, for example `0.05`. The guide line sits at the transformed value of it, and a point past the line reads as significant."),
70
+ .describe("The significance cut of the p column, for example `0.05`. On a `volcano` the guide line sits at the transformed value of it, and a point past the line reads as significant. On an `ma` a point whose `p` is under the cut draws in the signal color."),
41
71
  effect: z
42
72
  .number()
43
73
  .positive()
44
- .describe("The effect cut, for example `1`. One guide line sits at each side of zero, and a point past a line reads as a signal on that side."),
74
+ .optional()
75
+ .describe("The effect cut of a `volcano`, for example `1`. One guide line sits at each side of zero, and a point past a line reads as a signal on that side. A `volcano` needs it beside the significance cut, and an `ma` refuses it."),
45
76
  });
46
77
  /** The teaching text of the orientation. The quick path and the bar series form both carry it. */
47
78
  const ORIENTATION_DESCRIPTION = "The arrangement of the bars. `vertical` is the default, and it draws the categories along the bottom. `horizontal` draws them up the left side, and it is the form to reach for when a category name is long, for example a gene-set name: a long name reads on the y axis, and it is unreadable slanted under a vertical bar. The channels do not move with the orientation. `x` names the category column and `y` names the value column in both.";
48
79
  /**
49
- * One visual channel: a column name, or a column with a per-row transform.
80
+ * One visual channel: a column name, or a column with a per-row transform and a category order.
50
81
  *
51
- * The object form carries a column and a transform, and it carries no value. Thus a channel can never
52
- * bring a data literal into the chart option.
82
+ * The object form carries column names and closed enum members, and it carries no value. Thus a channel can
83
+ * never bring a data literal into the chart option. The refine holds the order direction to a declared sort
84
+ * column, because a direction with nothing to sort by names a rule that nothing reads.
53
85
  */
54
86
  export const ChartChannelSchema = z.union([
55
87
  z.string().describe("The column that feeds the channel."),
56
- z.strictObject({
88
+ z
89
+ .strictObject({
57
90
  column: z.string().describe("The column that feeds the channel."),
58
- transform: ChartTransformSchema.describe("The per-row transform. `log10` and `neg_log10` drop a cell that is not positive. `rank` gives the place of the cell in the ascending order of the column, and a tie shares its place."),
91
+ transform: ChartTransformSchema.optional().describe("The per-row transform. `log10` and `neg_log10` drop a cell that is not positive. `rank` gives the place of the cell in the ascending order of the column, and a tie shares its place. The axis title puts the label of the column inside the transform, for example `log10(Normalized count)`, thus the label names the untransformed column."),
92
+ orderBy: z
93
+ .string()
94
+ .optional()
95
+ .describe("The column that sorts the categories of this channel, for example the leaf order of a clustered heatmap or the score of a ranked bar. Each category must hold one value in this column. A tie keeps the order of the rows. It is legal on a channel that draws a category axis alone."),
96
+ order: z
97
+ .enum(["asc", "desc"])
98
+ .optional()
99
+ .describe("The direction of the `orderBy` sort. `asc` is the default, and `desc` puts the largest value first. It is legal beside `orderBy` alone."),
100
+ })
101
+ .refine((channel) => channel.order === undefined || channel.orderBy !== undefined, {
102
+ message: "`order` is legal beside `orderBy` alone. Name the column that sorts the categories.",
103
+ path: ["order"],
59
104
  }),
60
105
  ]);
106
+ /** The teaching text of the continuous color channel. The quick path and a series both carry it. */
107
+ const COLOR_DESCRIPTION = "A numeric column that colors each point or bar on a continuous scale, for example the expression of a gene over an embedding or the adjusted p of an enriched set. A column that holds values on both sides of zero takes a diverging scale centered on zero, and every other column takes a sequential scale. It is legal on a `scatter`, a `bar`, an `embedding`, a `dotplot`, and a `locuszoom`, and never beside a `group` channel. On an `embedding` a zero draws in a light gray ground, the scale clips at the 99th percentile, and the high values draw on top. On a `dotplot` the scale is sequential. On a `locuszoom` it names the r² of each variant with the lead variant, and the points take the five LD bins of LocusZoom. A row whose cell is not numeric draws no point, except on a `locuszoom`, where it draws gray.";
108
+ /** The teaching text of the size channel. The quick path and a series both carry it. */
109
+ const SIZE_DESCRIPTION = "A numeric column that sizes each point, for example the gene count of an enrichment dot plot. It is legal on a `scatter`, a `forest`, and a `dotplot`. On a `forest` it sets the area of each square, for example the weight of a study. On a `dotplot` a size legend of three reference circles names it. A row whose cell is not numeric draws no point.";
110
+ /** The teaching text of the lower interval bound. The quick path and a series both carry it. */
111
+ const LOW_DESCRIPTION = "The column of the lower bound of an interval around each plotted value, for example the lower confidence limit of a forest plot or of an error bar. Give `high` with it. The bound sits on the value axis, and a lower bound above its value is a fault. It is legal on a `bar`, a `scatter`, a `km`, a `forest`, and a `qq`. On a `km` the two bounds draw the confidence band of each curve as a step band. On a `qq` the two bounds draw the band of the null expectation under the points.";
112
+ /** The teaching text of the upper interval bound. The quick path and a series both carry it. */
113
+ const HIGH_DESCRIPTION = "The column of the upper bound of an interval around each plotted value. Give `low` with it. An upper bound under its value is a fault. It is legal on a `bar`, a `scatter`, a `km`, a `forest`, and a `qq`. On a `km` the two bounds draw the confidence band of each curve as a step band. On a `qq` the two bounds draw the band of the null expectation under the points.";
114
+ /** The teaching text of the facet channel. The quick path and a composition both carry it. */
115
+ const FACET_DESCRIPTION = "A category column that splits one table into small multiples: one panel for each value, in the order of the rows, with one shared axis range. At most 12 panels. It is legal on a `scatter`, a `line`, a `bar`, a `stacked-bar`, a `normalized-bar`, and an `embedding`. Each panel of an `embedding` keeps one unit on x and y. Each panel of a stacked form stacks the parts of its own rows, for example the cell-type shares of each donor under each condition.";
116
+ /** The teaching text of the shape channel. */
117
+ const SHAPE_DESCRIPTION = "A category column that sets the symbol of each point beside the color of `group`, for example the batch of each sample on a PCA plot. At most 6 categories. It is legal on a `pca` and a `scatter`. A `scatter` with a shape reads `x`, `y`, `group`, and `label` beside it, and no other channel.";
118
+ /** The teaching text of the p channel. */
119
+ const P_DESCRIPTION = "The p-value column that a figure reads beside its two axes. On an `ma` a point under the significance cut draws in the signal color, and every other point draws gray. On a `forest` the column prints as a text column beside the estimate column. It is legal on an `ma` and a `forest`.";
120
+ /** The teaching text of the censor channel. */
121
+ const CENSOR_DESCRIPTION = "The count of subjects censored at the time of the row. A row with a count above zero carries a small tick on its curve. It is legal on a `km`.";
122
+ /** The teaching text of the risk channel. */
123
+ const RISK_DESCRIPTION = "The number of subjects at risk at the time of the row. The number-at-risk table under the plot reads it at each tick of the time axis. It is legal on a `km`.";
124
+ /** The teaching text of the hit channel. */
125
+ const HIT_DESCRIPTION = "1 where the gene at the rank of the row is a member of the set, else 0. The middle panel draws one tick at each hit. It is legal on a `gsea`.";
126
+ /** The teaching text of the metric channel. */
127
+ const METRIC_DESCRIPTION = "The second numeric column of a figure. On a `gsea` it is the ranking metric at the rank of the row, for example the signed statistic of a differential test, and the bottom panel draws it as an area. On a `locuszoom` it is the recombination rate in cM/Mb at the position of the row, and it draws as a line on a right axis. It is legal on a `gsea` and a `locuszoom`.";
128
+ /** The teaching text of the tracks channel. */
129
+ const TRACKS_DESCRIPTION = "One to four category columns that draw as annotation strips along the x axis, for example the condition and the batch of each sample. Each column must hold one value for each x category. It is legal on a `heatmap` and an `oncoprint`.";
130
+ /** The one refine rule of an interval: the two bounds arrive together. */
131
+ function holdsBothBounds(encoding) {
132
+ return (encoding.low === undefined) === (encoding.high === undefined);
133
+ }
134
+ /** The refusal of a lone interval bound. */
135
+ const LONE_BOUND = { message: "An interval has two bounds. Give `low` and `high` together.", path: ["low"] };
61
136
  /** The mapping from a data column to a visual channel. Each channel is optional. */
62
- const ChartEncodingSchema = z.strictObject({
63
- x: ChartChannelSchema.optional().describe("The channel on the x axis."),
64
- y: ChartChannelSchema.optional().describe("The channel on the y axis."),
65
- group: ChartChannelSchema.optional().describe("The column that splits and colors the series."),
66
- value: ChartChannelSchema.optional().describe("The value column for a pie or heatmap."),
67
- label: z.string().optional().describe("The column that names each point. The name rides the tooltip of a bar, a line, and a scatter."),
137
+ const ChartEncodingSchema = z
138
+ .strictObject({
139
+ x: ChartChannelSchema.optional().describe("The channel on the x axis. On an `upset` it names the element of each membership row, for example a sample. On a `sankey` it names the source node of each flow. On a `locuszoom` it names the position in base pairs on one chromosome, and the axis reads in megabases."),
140
+ y: ChartChannelSchema.optional().describe("The channel on the y axis. On a `sankey` it names the target node of each flow."),
141
+ group: ChartChannelSchema.optional().describe("The column that splits and colors the series. On a `pie` it names the slices. On a `lollipop` it names the mutation class of each row, and a common class of the MAF standard takes its fixed color. On an `upset` it names the set of each membership row, for example a mutated gene. At most 12 sets. On a `sankey` it is optional, and it names the category of each flow: each flow takes the palette color of its category, and the nodes draw gray. On a `locuszoom` it is optional, and it names the chromosome of each row. The rows must hold one chromosome, and the x title names it."),
142
+ value: ChartChannelSchema.optional().describe("The value column of a `pie`, a `heatmap`, and a `sankey`. On a `pie` it sizes each slice, and `group` names the slices. On a `heatmap` it colors each cell. On a `sankey` it gives the size of each flow. On an `oncoprint` it names the alteration class of each gene and sample, for example `Missense_Mutation`. A row with an empty class names a sample with no alteration."),
143
+ label: z
144
+ .string()
145
+ .optional()
146
+ .describe("The column that names each point. The name rides the tooltip of a bar, a line, and a scatter. On a `lollipop` the three largest counts carry it as text, for example the protein change. On a `volcano` the ten most significant signal points show it, on a `manhattan` the lead variant of each chromosome shows it, and on a `locuszoom` the lead variant shows it."),
147
+ color: ChartChannelSchema.optional().describe(COLOR_DESCRIPTION),
148
+ size: ChartChannelSchema.optional().describe(SIZE_DESCRIPTION),
149
+ low: ChartChannelSchema.optional().describe(LOW_DESCRIPTION),
150
+ high: ChartChannelSchema.optional().describe(HIGH_DESCRIPTION),
151
+ facet: ChartChannelSchema.optional().describe(FACET_DESCRIPTION),
152
+ shape: ChartChannelSchema.optional().describe(SHAPE_DESCRIPTION),
153
+ p: ChartChannelSchema.optional().describe(P_DESCRIPTION),
154
+ censor: ChartChannelSchema.optional().describe(CENSOR_DESCRIPTION),
155
+ risk: ChartChannelSchema.optional().describe(RISK_DESCRIPTION),
156
+ hit: ChartChannelSchema.optional().describe(HIT_DESCRIPTION),
157
+ metric: ChartChannelSchema.optional().describe(METRIC_DESCRIPTION),
158
+ tracks: z.array(z.string().min(1)).min(1).max(4).optional().describe(TRACKS_DESCRIPTION),
159
+ })
160
+ .refine(holdsBothBounds, LONE_BOUND);
161
+ /**
162
+ * One statistic of a figure: a short name, and the one cell that gives its value.
163
+ *
164
+ * The value is an artifact reference and never a literal, thus a printed statistic is grounded exactly as a
165
+ * metric is.
166
+ */
167
+ export const ChartStatisticSchema = z.strictObject({
168
+ label: z.string().min(1).describe("The short name of the statistic, for example `Log-rank p`, `AUC`, or `λ`."),
169
+ value: ArtifactValueReferenceSchema.describe("The one cell that gives the value of the statistic."),
68
170
  });
171
+ /**
172
+ * The second table of a figure, and the four columns of it that the figure reads.
173
+ *
174
+ * The track binds a whole table exactly as the chart binding does, thus it resolves and it grounds the same
175
+ * way.
176
+ */
177
+ export const ChartTrackSchema = z.strictObject({
178
+ binding: ArtifactTableReferenceSchema.describe("The whole-table artifact of the track, for example the protein domains of a gene."),
179
+ start: z.string().min(1).describe("The column of the track table that holds the start position of each feature."),
180
+ end: z.string().min(1).describe("The column of the track table that holds the end position of each feature."),
181
+ label: z.string().min(1).describe("The column of the track table that names each feature, for example the domain name."),
182
+ length: z
183
+ .string()
184
+ .min(1)
185
+ .optional()
186
+ .describe("The column of the track table that holds the full length, for example the length of the protein. The x axis then ends at that length."),
187
+ });
188
+ /**
189
+ * The tree of one category axis of a heatmap: an edge list, and the three columns of it that the figure reads.
190
+ *
191
+ * The tree binds a whole table exactly as the chart binding does, thus it resolves and it grounds the same way.
192
+ */
193
+ export const ChartTreeSchema = z.strictObject({
194
+ binding: ArtifactTableReferenceSchema.describe("The whole-table artifact of the tree: one row for each edge of the linkage, for example the gene tree of a hierarchical clustering."),
195
+ parent: z.string().min(1).describe("The column of the tree table that names the parent node of each edge."),
196
+ child: z
197
+ .string()
198
+ .min(1)
199
+ .describe("The column of the tree table that names the child node of each edge. A leaf is a child that is never a parent, and it equals one category of its axis."),
200
+ height: z.string().min(1).describe("The column of the tree table that holds the height of the parent node of each edge, for example the merge distance."),
201
+ });
202
+ /** The trees of the two category axes of a heatmap. A member names one axis, and at least one member is present. */
203
+ const ChartTreesSchema = z
204
+ .strictObject({
205
+ x: ChartTreeSchema.optional().describe("The tree of the `x` categories. It draws above the matrix and above the tracks."),
206
+ y: ChartTreeSchema.optional().describe("The tree of the `y` categories. It draws at the left of the row names."),
207
+ })
208
+ .refine((trees) => trees.x !== undefined || trees.y !== undefined, { message: "The trees name at least one axis. Give `x`, `y`, or both." });
69
209
  /** The channels of one series. A series plots two channels, thus `x` and `y` are both present. */
70
- const ChartSeriesEncodingSchema = z.strictObject({
210
+ const ChartSeriesEncodingSchema = z
211
+ .strictObject({
71
212
  x: ChartChannelSchema.describe("The channel on the x axis."),
72
213
  y: ChartChannelSchema.describe("The channel on the y axis."),
73
214
  y0: ChartChannelSchema.optional().describe("The lower bound of a band. It is legal on an `area` series only."),
74
215
  group: ChartChannelSchema.optional().describe("The column that splits the series, one series for each value."),
75
216
  label: z.string().optional().describe("The column that names each point of the series."),
76
- });
217
+ color: ChartChannelSchema.optional().describe(COLOR_DESCRIPTION),
218
+ size: ChartChannelSchema.optional().describe(SIZE_DESCRIPTION),
219
+ low: ChartChannelSchema.optional().describe(LOW_DESCRIPTION),
220
+ high: ChartChannelSchema.optional().describe(HIGH_DESCRIPTION),
221
+ })
222
+ .refine(holdsBothBounds, LONE_BOUND);
77
223
  /**
78
224
  * One series of a composition: a plot form, its own channels, and an optional legend name.
79
225
  *
@@ -135,11 +281,15 @@ export const ChartAxesSchema = z.strictObject({
135
281
  x: ChartAxisSchema.optional().describe("The x axis."),
136
282
  y: ChartAxisSchema.optional().describe("The y axis."),
137
283
  });
138
- /** The full chart grammar: the series, the annotations, and the axes. Each series reads the one bound table. */
284
+ /**
285
+ * The full chart grammar: the series, the annotations, the axes, and the facet. Each series reads the one
286
+ * bound table.
287
+ */
139
288
  export const ChartCompositionSchema = z.strictObject({
140
289
  series: z.array(ChartSeriesSchema).min(1).describe("One series at least. Each one reads the bound table."),
141
290
  annotations: z.array(ChartAnnotationSchema).optional().describe("The guide lines, the guide bands, and the point names."),
142
291
  axes: ChartAxesSchema.optional().describe("The axis titles and the axis scales."),
292
+ facet: ChartChannelSchema.optional().describe(`${FACET_DESCRIPTION} On a composition, the first series has one of those forms.`),
143
293
  });
144
294
  /**
145
295
  * One typed list of a text block: the flag that selects the form, and the items.
@@ -202,11 +352,37 @@ export const ChartBlockSchema = z
202
352
  id: z.string().min(1).describe("The stable identity of the block."),
203
353
  title: z.string().optional(),
204
354
  binding: ArtifactTableReferenceSchema.describe("The whole-table artifact to plot."),
205
- chartType: ChartTypeSchema.optional().describe("The quick path. Give `encoding` with it, and omit `composition`. A preset (`volcano`, `manhattan`, `ma`, `km`) reads the same channels, and it applies its own transform and its own guide lines."),
355
+ chartType: ChartTypeSchema.optional().describe("The quick path. Give `encoding` with it, and omit `composition`. Each base type reads these channels. " +
356
+ "A `bar` draws the `y` value of each category of `x`. A `line` and a `scatter` draw `y` against `x`. A `histogram` counts the values of `x` alone in bins. " +
357
+ "A `box` draws the spread of `y` in each category of `x`, and a `violin` draws the density of `y` in each category of `x`. A `box` and a `violin` with no `group` draw each value as a point over the shape where a category holds 200 values or fewer. " +
358
+ "A `stacked-bar` stacks the `y` values of the `group` parts in each category of `x`, a `normalized-bar` stacks their shares, and a `radar` draws one polygon of `y` values for each `group` over the categories of `x`. These three need a `group` channel. " +
359
+ "A `heatmap` draws `value` over each pair of `x` and `y`, and its `tracks` draw annotation strips over the matrix. " +
360
+ "A `pie` reads no `x` and no `y`. Its `group` column names the slices, and its `value` column sizes them. " +
361
+ "A preset draws the canonical figure of one plot of a field from one table, and it reads its own channels: " +
362
+ "`volcano` (x effect, y p), `manhattan` (x cumulative position, y p, group chromosome), `ma` (x mean, y effect, p), `km` (x time, y survival, group arm, low, high, censor, risk), " +
363
+ "`pca` (x and y components, group, shape, label), `embedding` (x and y of a UMAP or a t-SNE, group or color, facet), `dotplot` (y category, x category or value, size, color), " +
364
+ "`forest` (y term, x estimate, low, high, p, size), `roc` (x false positive rate, y true positive rate, group), `qq` (x expected, y observed, low, high), " +
365
+ "`gsea` (x rank, y running score, group set, hit, metric), `oncoprint` (x sample, y gene, value alteration class, tracks), `lollipop` (x amino-acid position, y count, group class, label, and the track of the protein domains), " +
366
+ "`upset` (x element, group set, one row for each member of each set, and the figure counts the intersections), `sankey` (x source node, y target node, value flow, group, one row for each flow with no cycle), " +
367
+ "and `locuszoom` (x position on one chromosome, y p, color r² with the lead variant, label variant, metric recombination rate, group chromosome, and the track of the genes). " +
368
+ "A preset applies its own transform and its own guide lines."),
206
369
  encoding: ChartEncodingSchema.optional().describe("The channels of the quick path."),
207
- orientation: ChartOrientationSchema.optional().describe(`${ORIENTATION_DESCRIPTION} The field belongs to the \`bar\` chart type, and every other type refuses it.`),
208
- thresholds: ChartThresholdsSchema.optional().describe("The threshold pair of a `volcano`. It replaces the preset defaults of `0.05` and `1`, and it moves the guide lines and the color split together. State it to draw the volcano of a corrected significance column. Every other chart type reads no threshold, thus every other type refuses the field."),
370
+ orientation: ChartOrientationSchema.optional().describe(`${ORIENTATION_DESCRIPTION} The field belongs to the \`bar\`, the \`stacked-bar\`, and the \`normalized-bar\` chart types, and every other type refuses it.`),
371
+ thresholds: ChartThresholdsSchema.optional().describe("The thresholds of a `volcano` and of an `ma`. On a `volcano` give the pair: it replaces the preset defaults of `0.05` and `1`, and it moves the guide lines and the color split together. On an `ma` give the significance cut alone: it replaces the default of `0.1`, and it moves the color split of the `p` column. Every other chart type reads no threshold, thus every other type refuses the field."),
209
372
  composition: ChartCompositionSchema.optional().describe("The full grammar. Omit `chartType` and `encoding` with it."),
373
+ focus: z
374
+ .array(z.string())
375
+ .min(1)
376
+ .optional()
377
+ .describe("The category values of the finding. Each named category takes the one focus color, and every other category is muted. On a bar with no `group` channel a category is a value of `x`, and on every other chart it is a value of `group`. It is legal on a bar and the two stacked forms, and on a grouped scatter, line, box, violin, and radar. It is never legal beside a `color` channel."),
378
+ statistics: z
379
+ .array(ChartStatisticSchema)
380
+ .min(1)
381
+ .max(4)
382
+ .optional()
383
+ .describe("One to four statistics that the figure prints inside the plot, at the place that its field uses, for example a log-rank p, an AUC, or the genomic inflation λ. Each value binds one cell of a pinned artifact, and the page prints it in the number format of its column. It is legal on a `km`, a `roc`, a `qq`, and a `gsea`."),
384
+ track: ChartTrackSchema.optional().describe("A second table that the figure draws beside the bound table, for example the protein domains under the axis of a lollipop or the genes under a regional association plot. It is legal on a `lollipop` and a `locuszoom`."),
385
+ trees: ChartTreesSchema.optional().describe("The dendrogram of each category axis of a `heatmap`: one edge table for each axis, for example the gene tree and the sample tree of a hierarchical clustering. The axis takes the depth-first leaf order of its tree, thus an `orderBy` on the same axis refuses. It is legal on a `heatmap`."),
210
386
  caption: z.string().optional(),
211
387
  })
212
388
  .refine((block) => {
@@ -217,11 +393,20 @@ export const ChartBlockSchema = z
217
393
  const partialQuickPath = block.chartType !== undefined || block.encoding !== undefined || block.orientation !== undefined || block.thresholds !== undefined;
218
394
  return block.composition !== undefined ? !partialQuickPath : quickPath;
219
395
  }, { message: "A chart carries either `chartType` with `encoding`, or `composition`. The `orientation` and the `thresholds` belong to the quick path." })
220
- .refine((block) => block.thresholds === undefined || block.chartType === "volcano", {
221
- // The member is a pair, and the volcano is the one type that reads both values. A silent ignore
222
- // would teach the author a field that does nothing, thus the parse states the fault.
223
- message: "`thresholds` is legal beside the `volcano` chart type alone. Every other type reads no threshold pair.",
396
+ .refine((block) => block.thresholds === undefined || block.chartType === "volcano" || block.chartType === "ma", {
397
+ // A silent ignore would teach the author a field that does nothing, thus the parse states the fault.
398
+ message: "`thresholds` is legal beside the `volcano` and the `ma` chart types alone. Every other type reads no threshold.",
224
399
  path: ["thresholds"],
400
+ })
401
+ .refine((block) => block.chartType !== "volcano" || block.thresholds === undefined || block.thresholds.effect !== undefined, {
402
+ // The member moves the guide lines and the split of both axes, thus a volcano states both cuts.
403
+ message: "The thresholds of a `volcano` are a pair. Give the `effect` cut beside the `significance` cut.",
404
+ path: ["thresholds", "effect"],
405
+ })
406
+ .refine((block) => block.chartType !== "ma" || block.thresholds?.effect === undefined, {
407
+ // An MA splits its rows by the p column alone, thus an effect cut would move nothing.
408
+ message: "An `ma` reads the `significance` cut alone. Omit the `effect` cut.",
409
+ path: ["thresholds", "effect"],
225
410
  });
226
411
  /** A static image artifact. An image has no per-cell address, thus it is pinned whole-file. */
227
412
  export const FigureBlockSchema = z.strictObject({
@@ -290,3 +475,10 @@ export function channelColumn(channel) {
290
475
  export function channelTransform(channel) {
291
476
  return typeof channel === "string" ? undefined : channel.transform;
292
477
  }
478
+ /** The category order of one channel, or `undefined` when the channel declares none. */
479
+ export function channelOrder(channel) {
480
+ if (typeof channel === "string" || channel.orderBy === undefined) {
481
+ return undefined;
482
+ }
483
+ return { by: channel.orderBy, order: channel.order ?? "asc" };
484
+ }
@@ -131,7 +131,7 @@ export const ArtifactTableReferenceSchema = z.strictObject({
131
131
  // header and the axis title. Omit the key instead, which is the way to declare no label.
132
132
  .record(z.string(), z.string().min(1))
133
133
  .optional()
134
- .describe("The display name of each column, keyed by the raw column name. A label names what the column measures, for example `Adjusted p-value` for `padj`. The page shows the label in the table header and in the axis title, and the raw name stays on hover. A key that names no column of the artifact has no effect."),
134
+ .describe("The display name of each column, keyed by the raw column name. A label names what the column measures, for example `Adjusted p-value` for `padj`. The page shows the label in the table header and in the axis title, and the raw name stays on hover. Name the raw column, never a transform of it: the header shows the raw values, and a chart channel with a transform states the transform on its axis itself, for example `log10(Normalized count)`. A key that names no column of the artifact has no effect."),
135
135
  ...displayShape,
136
136
  });
137
137
  /**
@@ -14,6 +14,13 @@ export declare const TextDeltaEventSchema: z.ZodObject<{
14
14
  callPath: z.ZodArray<z.ZodString>;
15
15
  }, z.core.$strip>;
16
16
  }, z.core.$strip>;
17
+ export declare const IterationEventSchema: z.ZodObject<{
18
+ type: z.ZodLiteral<"iteration">;
19
+ source: z.ZodObject<{
20
+ agentId: z.ZodString;
21
+ callPath: z.ZodArray<z.ZodString>;
22
+ }, z.core.$strip>;
23
+ }, z.core.$strip>;
17
24
  /** Mirrors the `ToolOutcome` union — three states, never a boolean. */
18
25
  export declare const ToolOutcomeSchema: z.ZodEnum<{
19
26
  error: "error";
@@ -90,6 +97,12 @@ export declare const ChatEventSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
90
97
  agentId: z.ZodString;
91
98
  callPath: z.ZodArray<z.ZodString>;
92
99
  }, z.core.$strip>;
100
+ }, z.core.$strip>, z.ZodObject<{
101
+ type: z.ZodLiteral<"iteration">;
102
+ source: z.ZodObject<{
103
+ agentId: z.ZodString;
104
+ callPath: z.ZodArray<z.ZodString>;
105
+ }, z.core.$strip>;
93
106
  }, z.core.$strip>, z.ZodObject<{
94
107
  type: z.ZodLiteral<"tool-started">;
95
108
  toolUseId: z.ZodString;
@@ -12,6 +12,10 @@ export const TextDeltaEventSchema = z.object({
12
12
  text: z.string(),
13
13
  source: EventSourceSchema,
14
14
  });
15
+ export const IterationEventSchema = z.object({
16
+ type: z.literal("iteration"),
17
+ source: EventSourceSchema,
18
+ });
15
19
  /** Mirrors the `ToolOutcome` union — three states, never a boolean. */
16
20
  export const ToolOutcomeSchema = z.enum(["ok", "error", "denied"]);
17
21
  /** Mirrors `ToolCallOutcome` — the live three plus the record-only `incomplete`. */
@@ -47,6 +51,7 @@ export const ChatErrorEventSchema = z.object({
47
51
  });
48
52
  export const ChatEventSchema = z.discriminatedUnion("type", [
49
53
  TextDeltaEventSchema,
54
+ IterationEventSchema,
50
55
  ToolStartedEventSchema,
51
56
  ToolFinishedEventSchema,
52
57
  FinishEventSchema,
@@ -494,8 +494,8 @@ export declare const CompactionPartSchema: z.ZodObject<{
494
494
  id: z.ZodString;
495
495
  status: z.ZodEnum<{
496
496
  failed: "failed";
497
- done: "done";
498
497
  running: "running";
498
+ done: "done";
499
499
  }>;
500
500
  tokensBefore: z.ZodNumber;
501
501
  tokensAfter: z.ZodOptional<z.ZodNumber>;
@@ -810,8 +810,8 @@ export declare const ChatPartSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
810
810
  id: z.ZodString;
811
811
  status: z.ZodEnum<{
812
812
  failed: "failed";
813
- done: "done";
814
813
  running: "running";
814
+ done: "done";
815
815
  }>;
816
816
  tokensBefore: z.ZodNumber;
817
817
  tokensAfter: z.ZodOptional<z.ZodNumber>;
@@ -1,3 +1,3 @@
1
1
  export { PlanStepSchema, PresentationContentSchema, PresentationPartSchema, PlanPartSchema, RunCardPartSchema, FileReferenceEntrySchema, FileReferencePartSchema, RunStartedPartSchema, StepStatusSchema, DagStepStateSchema, DagStatePartSchema, StepPhaseSchema, StepActivityPartSchema, FileTreeEntrySchema, StepFileTreePartSchema, StepOutputFileSchema, StepOutputPartSchema, StepSummaryPartSchema, StepUsagePartSchema, StepBlockedPartSchema, SynthesizedFindingSchema, BiologicalThemeSchema, RunSynthesisPartSchema, SynthesisPhaseSchema, SynthesisProgressPartSchema, RunCompletedFindingSchema, RunCompletedPartSchema, RunFailedPartSchema, ChatPartSchema, } from "./chat-parts.js";
2
- export { EventSourceSchema, TextDeltaEventSchema, ToolStartedEventSchema, ToolFinishedEventSchema, FinishEventSchema, ChatErrorEventSchema, ChatEventSchema, } from "./chat-events.js";
2
+ export { EventSourceSchema, TextDeltaEventSchema, IterationEventSchema, ToolStartedEventSchema, ToolFinishedEventSchema, FinishEventSchema, ChatErrorEventSchema, ChatEventSchema, } from "./chat-events.js";
3
3
  export { TokenUsageRollupSchema } from "./usage.js";
@@ -1,3 +1,3 @@
1
1
  export { PlanStepSchema, PresentationContentSchema, PresentationPartSchema, PlanPartSchema, RunCardPartSchema, FileReferenceEntrySchema, FileReferencePartSchema, RunStartedPartSchema, StepStatusSchema, DagStepStateSchema, DagStatePartSchema, StepPhaseSchema, StepActivityPartSchema, FileTreeEntrySchema, StepFileTreePartSchema, StepOutputFileSchema, StepOutputPartSchema, StepSummaryPartSchema, StepUsagePartSchema, StepBlockedPartSchema, SynthesizedFindingSchema, BiologicalThemeSchema, RunSynthesisPartSchema, SynthesisPhaseSchema, SynthesisProgressPartSchema, RunCompletedFindingSchema, RunCompletedPartSchema, RunFailedPartSchema, ChatPartSchema, } from "./chat-parts.js";
2
- export { EventSourceSchema, TextDeltaEventSchema, ToolStartedEventSchema, ToolFinishedEventSchema, FinishEventSchema, ChatErrorEventSchema, ChatEventSchema, } from "./chat-events.js";
2
+ export { EventSourceSchema, TextDeltaEventSchema, IterationEventSchema, ToolStartedEventSchema, ToolFinishedEventSchema, FinishEventSchema, ChatErrorEventSchema, ChatEventSchema, } from "./chat-events.js";
3
3
  export { TokenUsageRollupSchema } from "./usage.js";
@@ -1,22 +1,16 @@
1
1
  /**
2
- * The input signature — the fixed-width comparand a completed profile persists in
3
- * place of a per-file identity list.
2
+ * The input signature — the fixed-width record of the kept input set that a completed
3
+ * profile persists in place of a per-file identity list.
4
4
  *
5
- * A per-file identity list would have exactly one consumer, the staleness comparison —
6
- * several thousand identifiers persisted, detoasted on every read of the row, and
7
- * compared element-wise to answer a question 40 bytes answer. The signature covers
8
- * `size` and `mtimeMs` as well, so it also notices that the bytes behind a path
9
- * changed — a `fileId` is a path identity and survives an in-place edit unchanged.
5
+ * It covers `size` and `mtimeMs` as well as the `fileId`, so it also records the bytes
6
+ * behind a path — a `fileId` is a path identity and survives an in-place edit unchanged.
7
+ * Nothing compares it: the staleness of a profile reads only the profile ledger.
10
8
  *
11
9
  * Order independence is a property of the digest, not of the caller: entries are
12
10
  * sorted canonically before hashing, so two enumerations of the same set agree.
13
11
  *
14
- * It digests KEPT files only — quarantine runs before the digest, on both sides of the
15
- * comparison. A partial-download twin or an OS droppings file was never part of what was
16
- * profiled, so its appearance, edit, or removal is not drift, and a profile that
17
- * re-triggered on it would re-run on churn nobody caused. A change to the quarantine rules
18
- * themselves recomputes every signature once on the next scan, which is the correct
19
- * consequence: the definition of "kept" changed.
12
+ * It digests KEPT files only — quarantine runs before the digest. A partial-download twin
13
+ * or an OS droppings file was never part of what was profiled.
20
14
  */
21
15
  import type { DataProfileInputSignature } from "../state/data-profile.js";
22
16
  import type { StagedInput } from "./staged-input.js";
Binary file
@@ -14,8 +14,9 @@
14
14
  * engine (see `dbos-workflow-purger.ts` for why the static `DBOS` facade is
15
15
  * the wrong tool here). `cancelWorkflows` is an optional test seam over it.
16
16
  *
17
- * No stream writes: `DBOS.writeStream` is body-only, so hosts synthesize the
18
- * terminal frame read-side.
17
+ * No stream writes: `DBOS.writeStream` writes only from inside the workflow
18
+ * that owns the stream, so the run-event reader (`run-event-stream.ts`)
19
+ * synthesizes the terminal part from the run row.
19
20
  */
20
21
  import type { Pool } from "pg";
21
22
  import type { AgentSession } from "../auth/types.js";
@@ -14,8 +14,9 @@
14
14
  * engine (see `dbos-workflow-purger.ts` for why the static `DBOS` facade is
15
15
  * the wrong tool here). `cancelWorkflows` is an optional test seam over it.
16
16
  *
17
- * No stream writes: `DBOS.writeStream` is body-only, so hosts synthesize the
18
- * terminal frame read-side.
17
+ * No stream writes: `DBOS.writeStream` writes only from inside the workflow
18
+ * that owns the stream, so the run-event reader (`run-event-stream.ts`)
19
+ * synthesizes the terminal part from the run row.
19
20
  */
20
21
  import { createNoopLogger } from "../lib/console-logger.js";
21
22
  import { deliverNotice } from "../lib/hooks.js";
@@ -30,9 +30,12 @@
30
30
  * that tore itself down on one bad part would be strictly less robust than the
31
31
  * writer it observes.
32
32
  */
33
+ import type { ResultAsync } from "neverthrow";
33
34
  import type { Pool } from "pg";
34
35
  import type { ChatPart } from "../contracts/chat-parts.js";
36
+ import { type DbError } from "../lib/db-result.js";
35
37
  import type { Logger } from "../lib/logger.js";
38
+ import type { RunStatus } from "../state/schema.js";
36
39
  /**
37
40
  * Construction-time dependencies. The pool is the application pool the run
38
41
  * ledger lives in; the logger is where every contained failure is reported, and
@@ -42,6 +45,10 @@ import type { Logger } from "../lib/logger.js";
42
45
  export interface RunEventStreamDeps {
43
46
  readonly pool: Pool;
44
47
  readonly logger?: Logger;
48
+ /** Reads the status of the run row, `null` when no row exists. Defaults to `queryRun` on `pool`. */
49
+ readonly readRunStatus?: (runId: string) => ResultAsync<RunStatus | null, DbError>;
50
+ /** The wait between two reads of the run row. Defaults to a timer. */
51
+ readonly sleep?: (ms: number) => Promise<void>;
45
52
  }
46
53
  /**
47
54
  * Receives one part. May be async — the subscription awaits it, so a handler is
@@ -66,8 +73,15 @@ export interface RunEventSubscribeOptions {
66
73
  export interface RunEventStream {
67
74
  /**
68
75
  * Deliver every part the run produces — parent and children alike — to
69
- * `onPart`, resolving when the run is terminal and every stream opened has
70
- * drained, or promptly when the signal aborts.
76
+ * `onPart`, resolving when the run is terminal, every stream opened has
77
+ * drained, and `onPart` has taken the last part, or promptly when the signal
78
+ * aborts.
79
+ *
80
+ * A canceled workflow runs no more steps, thus it never writes its own
81
+ * terminal part, and the canceler cannot write a stream. When the parent
82
+ * stream ends with no `data-run-completed` or `data-run-failed` part and
83
+ * the run row reads `canceled`, the last part delivered is a
84
+ * `data-run-failed` part with `reason: "canceled"`.
71
85
  *
72
86
  * Parts from one workflow arrive in the order that workflow wrote them. No
73
87
  * order is promised ACROSS the parent and its children: those workflows