@inflexa-ai/harness 0.45.0 → 0.45.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 (122) hide show
  1. package/dist/agents/conversation-agent.d.ts +10 -0
  2. package/dist/agents/conversation-agent.js +19 -1
  3. package/dist/agents/sandbox/shared.d.ts +23 -2
  4. package/dist/agents/sandbox/shared.js +51 -18
  5. package/dist/agents/sandbox/translational-safety-agent.js +26 -0
  6. package/dist/agents/sandbox/types.d.ts +24 -1
  7. package/dist/agents/sandbox-catalog.d.ts +14 -0
  8. package/dist/agents/sandbox-catalog.js +19 -0
  9. package/dist/app/approve-plan.d.ts +5 -2
  10. package/dist/app/approve-plan.js +1 -1
  11. package/dist/app/chat-turn.js +6 -3
  12. package/dist/config/environment-stores.d.ts +4 -2
  13. package/dist/contracts/chat-events.d.ts +12 -2
  14. package/dist/contracts/chat-events.js +1 -1
  15. package/dist/contracts/chat-frame.js +6 -2
  16. package/dist/contracts/index.d.ts +1 -1
  17. package/dist/contracts/schemas/chat-events.d.ts +25 -0
  18. package/dist/contracts/schemas/chat-events.js +7 -0
  19. package/dist/contracts/schemas/chat-parts.d.ts +39 -0
  20. package/dist/contracts/schemas/chat-parts.js +17 -0
  21. package/dist/contracts/schemas/index.d.ts +1 -1
  22. package/dist/contracts/schemas/index.js +1 -1
  23. package/dist/execution/dbos-workflow-purger.d.ts +2 -0
  24. package/dist/execution/dbos-workflow-purger.js +21 -14
  25. package/dist/execution/run-canceler.d.ts +33 -11
  26. package/dist/execution/run-canceler.js +129 -27
  27. package/dist/execution/run-end-record.d.ts +30 -0
  28. package/dist/execution/run-end-record.js +27 -0
  29. package/dist/index.d.ts +9 -5
  30. package/dist/index.js +11 -1
  31. package/dist/loop/call-guard.d.ts +50 -0
  32. package/dist/loop/call-guard.js +90 -0
  33. package/dist/loop/compaction.d.ts +5 -0
  34. package/dist/loop/compaction.js +17 -0
  35. package/dist/loop/run-agent.d.ts +9 -0
  36. package/dist/loop/run-agent.js +15 -1
  37. package/dist/loop/run-to-terminal.js +4 -1
  38. package/dist/loop/types.d.ts +6 -0
  39. package/dist/memory/conversation-display-recorder.d.ts +0 -2
  40. package/dist/memory/conversation-display-recorder.js +28 -3
  41. package/dist/memory/thread-history.d.ts +34 -7
  42. package/dist/memory/thread-history.js +151 -42
  43. package/dist/prompts/briefing.d.ts +57 -3
  44. package/dist/prompts/briefing.js +129 -2
  45. package/dist/prompts/planner.js +13 -2
  46. package/dist/provenance/seam.d.ts +23 -15
  47. package/dist/provenance/seam.js +7 -5
  48. package/dist/providers/ai-sdk.js +87 -7
  49. package/dist/providers/types.d.ts +6 -0
  50. package/dist/reference-data/catalog.js +141 -2
  51. package/dist/report-model/authoring-grammar.d.ts +26 -26
  52. package/dist/sandbox/image-packages.d.ts +26 -4
  53. package/dist/sandbox/image-packages.js +18 -4
  54. package/dist/schemas/data-profile-schemas.d.ts +12 -12
  55. package/dist/schemas/plan-schemas.d.ts +57 -3
  56. package/dist/schemas/plan-schemas.js +1 -1
  57. package/dist/schemas/workflow-state.d.ts +113 -0
  58. package/dist/schemas/workflow-state.js +54 -0
  59. package/dist/state/artifacts.d.ts +17 -1
  60. package/dist/state/artifacts.js +21 -3
  61. package/dist/state/index.d.ts +2 -2
  62. package/dist/state/index.js +1 -1
  63. package/dist/state/runs.d.ts +43 -9
  64. package/dist/state/runs.js +85 -14
  65. package/dist/state/step-executions.d.ts +9 -0
  66. package/dist/state/step-executions.js +22 -0
  67. package/dist/tools/ad-hoc-router.d.ts +2 -0
  68. package/dist/tools/ad-hoc-router.js +18 -2
  69. package/dist/tools/capability-data.d.ts +36 -0
  70. package/dist/tools/capability-data.js +73 -0
  71. package/dist/tools/execute-analysis.d.ts +37 -9
  72. package/dist/tools/execute-analysis.js +131 -21
  73. package/dist/tools/knowledge/check.d.ts +60 -0
  74. package/dist/tools/knowledge/check.js +92 -0
  75. package/dist/tools/knowledge/client.d.ts +382 -0
  76. package/dist/tools/knowledge/client.js +302 -0
  77. package/dist/tools/knowledge/environment.d.ts +78 -0
  78. package/dist/tools/knowledge/environment.js +105 -0
  79. package/dist/tools/knowledge/index.d.ts +49 -0
  80. package/dist/tools/knowledge/index.js +70 -0
  81. package/dist/tools/knowledge/local-slots.d.ts +62 -0
  82. package/dist/tools/knowledge/local-slots.js +177 -0
  83. package/dist/tools/knowledge/pin.d.ts +14 -0
  84. package/dist/tools/knowledge/pin.js +17 -0
  85. package/dist/tools/knowledge/recommend.d.ts +92 -0
  86. package/dist/tools/knowledge/recommend.js +98 -0
  87. package/dist/tools/knowledge/situation.d.ts +143 -0
  88. package/dist/tools/knowledge/situation.js +164 -0
  89. package/dist/tools/knowledge/skeleton.d.ts +69 -0
  90. package/dist/tools/knowledge/skeleton.js +202 -0
  91. package/dist/tools/knowledge/template.d.ts +137 -0
  92. package/dist/tools/knowledge/template.js +267 -0
  93. package/dist/tools/research/generate-plan.d.ts +23 -0
  94. package/dist/tools/research/generate-plan.js +203 -12
  95. package/dist/tools/research/guarded-address.d.ts +20 -0
  96. package/dist/tools/research/guarded-address.js +148 -0
  97. package/dist/tools/research/searxng-engine.d.ts +40 -0
  98. package/dist/tools/research/searxng-engine.js +120 -0
  99. package/dist/tools/research/web-fetch.d.ts +124 -0
  100. package/dist/tools/research/web-fetch.js +627 -0
  101. package/dist/tools/research/web-search.d.ts +75 -0
  102. package/dist/tools/research/web-search.js +112 -0
  103. package/dist/tools/sandbox/list-available-packages.d.ts +7 -4
  104. package/dist/tools/sandbox/list-available-packages.js +12 -4
  105. package/dist/tools/sandbox/list-available-refs.d.ts +21 -0
  106. package/dist/tools/sandbox/list-available-refs.js +60 -3
  107. package/dist/tools/sandbox/report-blocker.js +5 -1
  108. package/dist/tools/terminal-text.d.ts +11 -0
  109. package/dist/tools/terminal-text.js +19 -0
  110. package/dist/tools/workspace/decision-record.d.ts +81 -0
  111. package/dist/tools/workspace/decision-record.js +117 -0
  112. package/dist/tools/workspace/edit-file.d.ts +3 -1
  113. package/dist/tools/workspace/edit-file.js +20 -2
  114. package/dist/tools/workspace/execute-command.d.ts +17 -0
  115. package/dist/tools/workspace/execute-command.js +28 -1
  116. package/dist/tools/workspace/index.d.ts +1 -0
  117. package/dist/tools/workspace/index.js +1 -0
  118. package/dist/tools/workspace/mutator.d.ts +1 -1
  119. package/dist/workflows/execute-analysis.d.ts +58 -18
  120. package/dist/workflows/execute-analysis.js +273 -58
  121. package/dist/workflows/sandbox-step.d.ts +12 -0
  122. package/package.json +5 -1
@@ -0,0 +1,382 @@
1
+ /**
2
+ * The knowledge service interface of the harness.
3
+ *
4
+ * `KnowledgeClient` is the one contract between the harness and the remote
5
+ * knowledge service: four typed operations and the snapshot they answer
6
+ * from. The harness never knows about a license. It sees a client, or it sees
7
+ * nothing, and an absent client is a normal state in which no knowledge tool
8
+ * attaches. `createHttpKnowledgeClient` is the shipped realization over plain
9
+ * HTTPS with the retry and timeout policy of the other external tools.
10
+ *
11
+ * Every operation answers a data variant. A service that is configured but
12
+ * unreachable gives `{ match: "unavailable" }` after the retry policy, and the
13
+ * caller continues from the prose skills. A 400 names the field and the
14
+ * permitted values, thus a model corrects itself in one turn. A 404 on the
15
+ * contract lookup names the `template` field the same way. The response
16
+ * shapes below are the harness copy of the wire contract of the service, kept
17
+ * lenient with `looseObject`, thus a richer answer of a later snapshot still
18
+ * parses.
19
+ */
20
+ import { z } from "zod";
21
+ /** The role of the blocking factor. A role and never a column name, because the situation leaves the machine. */
22
+ export type BlockingRole = "individual" | "cell_line" | "litter" | "site" | "pair" | "other";
23
+ /** The role of a covariate of the design. A role and never a column name. */
24
+ export type CovariateRole = "sex" | "age" | "clinical" | "technical" | "other";
25
+ /** The role of the numeric variable of interest when the question is a trend, not a comparison of groups. */
26
+ export type PredictorRole = "dose" | "time" | "age" | "score" | "other";
27
+ /** The typed description of one analysis situation. Never a sample row, never an identifier, and never a column name. */
28
+ export interface KnowledgeSituation {
29
+ readonly question: "differential_expression" | "enrichment" | "qc" | "full_plan";
30
+ readonly modality: "bulk_rna_seq";
31
+ readonly data_state: "fastq" | "counts" | "tpm_or_fpkm" | "log_normalized";
32
+ readonly count_source?: "salmon" | "kallisto" | "star_featurecounts" | "rsem" | "unknown";
33
+ /** Whether the length correction of a quantifier occurred. Never required: the service sets `unknown` on counts. */
34
+ readonly import_state?: "quantifications" | "estimated_counts_with_lengths" | "corrected_counts" | "integer_counts" | "unknown";
35
+ readonly organism: "human" | "mouse" | "other";
36
+ readonly n_groups: number;
37
+ readonly n_per_group_min: number;
38
+ readonly n_per_group_max: number;
39
+ /** The total number of biological samples. Never required. */
40
+ readonly n_samples?: number;
41
+ /** The number of samples with an observed event, for a survival question. Never required. */
42
+ readonly n_events?: number;
43
+ readonly paired: boolean;
44
+ /** The role of the blocking factor, or null. The column name stays on the machine. */
45
+ readonly blocking_factor?: BlockingRole | null;
46
+ /** How the blocking factor relates to the condition: crossed, or nested inside one condition. */
47
+ readonly block_structure?: "crossed" | "nested";
48
+ readonly batch: "none" | "known_balanced" | "known_confounded" | "known_unbalanced" | "suspected";
49
+ /** The role of each covariate of the design, one entry per covariate. The column names stay on the machine. */
50
+ readonly covariates?: readonly CovariateRole[];
51
+ readonly n_timepoints?: number | null;
52
+ /** The role of the numeric variable of interest when the question is a trend over it; then `n_groups` is 1. The column name stays on the machine. */
53
+ readonly continuous_predictor?: PredictorRole | null;
54
+ /** True when some columns of the count table are technical replicates of one biological sample. */
55
+ readonly technical_replicates?: boolean;
56
+ /** The row unit of the count table. Absent for a gene-level table. */
57
+ readonly feature_level?: "gene" | "exon" | "transcript";
58
+ readonly library_type?: "polyA" | "total" | "three_prime" | "unknown";
59
+ readonly strandedness?: "verified" | "declared_unverified" | "unknown";
60
+ readonly interaction?: boolean;
61
+ /** True for a classifier of the samples into classes; absent for a per-sample score. */
62
+ readonly classifier?: boolean;
63
+ readonly quality_flags?: readonly ("low_depth_sample" | "outlier_sample" | "sample_identity_doubt" | "high_duplication" | "degraded_rna")[];
64
+ }
65
+ export declare const RecommendResponseSchema: z.ZodObject<{
66
+ match: z.ZodEnum<{
67
+ none: "none";
68
+ applicable: "applicable";
69
+ flag: "flag";
70
+ }>;
71
+ snapshot: z.ZodObject<{
72
+ date: z.ZodString;
73
+ digest: z.ZodString;
74
+ }, z.core.$strip>;
75
+ procedure: z.ZodArray<z.ZodObject<{
76
+ step: z.ZodString;
77
+ method: z.ZodOptional<z.ZodObject<{
78
+ id: z.ZodString;
79
+ label: z.ZodString;
80
+ }, z.core.$loose>>;
81
+ template: z.ZodOptional<z.ZodString>;
82
+ rules: z.ZodArray<z.ZodString>;
83
+ flags: z.ZodOptional<z.ZodArray<z.ZodObject<{
84
+ rule: z.ZodString;
85
+ severity: z.ZodString;
86
+ message: z.ZodString;
87
+ outcome: z.ZodOptional<z.ZodString>;
88
+ }, z.core.$loose>>>;
89
+ alternatives: z.ZodOptional<z.ZodArray<z.ZodObject<{
90
+ method: z.ZodString;
91
+ label: z.ZodString;
92
+ when: z.ZodString;
93
+ rules: z.ZodArray<z.ZodString>;
94
+ }, z.core.$loose>>>;
95
+ disputed: z.ZodOptional<z.ZodObject<{
96
+ rule: z.ZodString;
97
+ sides: z.ZodArray<z.ZodString>;
98
+ }, z.core.$loose>>;
99
+ parameters: z.ZodOptional<z.ZodArray<z.ZodObject<{
100
+ name: z.ZodString;
101
+ value: z.ZodUnknown;
102
+ default_source: z.ZodOptional<z.ZodString>;
103
+ }, z.core.$loose>>>;
104
+ conflicts: z.ZodOptional<z.ZodArray<z.ZodObject<{
105
+ parameter: z.ZodString;
106
+ entries: z.ZodArray<z.ZodObject<{
107
+ rule: z.ZodString;
108
+ value: z.ZodUnknown;
109
+ }, z.core.$loose>>;
110
+ }, z.core.$loose>>>;
111
+ substitution: z.ZodOptional<z.ZodObject<{
112
+ for: z.ZodString;
113
+ label: z.ZodString;
114
+ template: z.ZodString;
115
+ }, z.core.$loose>>;
116
+ limit: z.ZodOptional<z.ZodObject<{
117
+ requested_language: z.ZodEnum<{
118
+ R: "R";
119
+ python: "python";
120
+ }>;
121
+ reason: z.ZodString;
122
+ skipped: z.ZodArray<z.ZodObject<{
123
+ template: z.ZodString;
124
+ missing: z.ZodArray<z.ZodString>;
125
+ refused: z.ZodOptional<z.ZodString>;
126
+ }, z.core.$loose>>;
127
+ }, z.core.$loose>>;
128
+ unrealized: z.ZodOptional<z.ZodObject<{
129
+ reason: z.ZodString;
130
+ templates: z.ZodArray<z.ZodObject<{
131
+ template: z.ZodString;
132
+ why: z.ZodString;
133
+ }, z.core.$loose>>;
134
+ }, z.core.$loose>>;
135
+ }, z.core.$loose>>;
136
+ uncovered: z.ZodArray<z.ZodString>;
137
+ unrealized: z.ZodOptional<z.ZodArray<z.ZodString>>;
138
+ flags: z.ZodArray<z.ZodObject<{
139
+ rule: z.ZodString;
140
+ severity: z.ZodString;
141
+ message: z.ZodString;
142
+ outcome: z.ZodOptional<z.ZodString>;
143
+ }, z.core.$loose>>;
144
+ claims: z.ZodArray<z.ZodObject<{
145
+ id: z.ZodString;
146
+ statement: z.ZodString;
147
+ strength: z.ZodString;
148
+ evidence: z.ZodArray<z.ZodObject<{
149
+ doi: z.ZodOptional<z.ZodString>;
150
+ pmid: z.ZodOptional<z.ZodString>;
151
+ title: z.ZodString;
152
+ year: z.ZodNumber;
153
+ direction: z.ZodString;
154
+ }, z.core.$loose>>;
155
+ }, z.core.$loose>>;
156
+ nearest: z.ZodOptional<z.ZodArray<z.ZodObject<{
157
+ claim: z.ZodString;
158
+ title: z.ZodString;
159
+ failed: z.ZodArray<z.ZodString>;
160
+ }, z.core.$loose>>>;
161
+ reason: z.ZodOptional<z.ZodString>;
162
+ }, z.core.$loose>;
163
+ export type RecommendResponse = z.infer<typeof RecommendResponseSchema>;
164
+ export declare const CheckResponseSchema: z.ZodObject<{
165
+ ok: z.ZodBoolean;
166
+ snapshot: z.ZodObject<{
167
+ date: z.ZodString;
168
+ digest: z.ZodString;
169
+ }, z.core.$strip>;
170
+ violations: z.ZodArray<z.ZodObject<{
171
+ step_type: z.ZodString;
172
+ severity: z.ZodString;
173
+ rule: z.ZodString;
174
+ message: z.ZodString;
175
+ permitted: z.ZodOptional<z.ZodArray<z.ZodString>>;
176
+ }, z.core.$loose>>;
177
+ warnings: z.ZodArray<z.ZodObject<{
178
+ step_type: z.ZodString;
179
+ severity: z.ZodString;
180
+ rule: z.ZodString;
181
+ message: z.ZodString;
182
+ permitted: z.ZodOptional<z.ZodArray<z.ZodString>>;
183
+ }, z.core.$loose>>;
184
+ not_assessed: z.ZodOptional<z.ZodArray<z.ZodObject<{
185
+ step_type: z.ZodString;
186
+ reason: z.ZodString;
187
+ message: z.ZodString;
188
+ }, z.core.$loose>>>;
189
+ }, z.core.$loose>;
190
+ export type CheckResponse = z.infer<typeof CheckResponseSchema>;
191
+ export declare const RenderResponseSchema: z.ZodObject<{
192
+ ok: z.ZodLiteral<true>;
193
+ snapshot: z.ZodObject<{
194
+ date: z.ZodString;
195
+ digest: z.ZodString;
196
+ }, z.core.$strip>;
197
+ template: z.ZodObject<{
198
+ id: z.ZodString;
199
+ version: z.ZodString;
200
+ label: z.ZodString;
201
+ method: z.ZodObject<{
202
+ id: z.ZodString;
203
+ label: z.ZodString;
204
+ }, z.core.$loose>;
205
+ substitute_for: z.ZodOptional<z.ZodObject<{
206
+ id: z.ZodString;
207
+ label: z.ZodString;
208
+ }, z.core.$loose>>;
209
+ language: z.ZodString;
210
+ }, z.core.$loose>;
211
+ script: z.ZodString;
212
+ slots: z.ZodArray<z.ZodObject<{
213
+ name: z.ZodString;
214
+ value: z.ZodOptional<z.ZodUnknown>;
215
+ source: z.ZodString;
216
+ adaptable: z.ZodBoolean;
217
+ lines: z.ZodArray<z.ZodNumber>;
218
+ read: z.ZodOptional<z.ZodBoolean>;
219
+ }, z.core.$loose>>;
220
+ local_slots: z.ZodOptional<z.ZodArray<z.ZodString>>;
221
+ environment: z.ZodObject<{
222
+ match: z.ZodString;
223
+ }, z.core.$loose>;
224
+ syntax: z.ZodObject<{
225
+ status: z.ZodString;
226
+ }, z.core.$loose>;
227
+ outputs: z.ZodArray<z.ZodObject<{
228
+ name: z.ZodString;
229
+ path: z.ZodString;
230
+ description: z.ZodOptional<z.ZodString>;
231
+ }, z.core.$loose>>;
232
+ decision_record: z.ZodRecord<z.ZodString, z.ZodUnknown>;
233
+ }, z.core.$loose>;
234
+ export type RenderResponse = z.infer<typeof RenderResponseSchema>;
235
+ /**
236
+ * One slot of a template with the constraint a caller value must obey.
237
+ * `adaptable: false` marks a pinned slot: the render refuses a caller value
238
+ * for it. An adaptable slot without a default is required unless `required`
239
+ * is false.
240
+ */
241
+ declare const TemplateParameterSchema: z.ZodObject<{
242
+ name: z.ZodString;
243
+ type: z.ZodString;
244
+ description: z.ZodString;
245
+ adaptable: z.ZodBoolean;
246
+ local: z.ZodOptional<z.ZodBoolean>;
247
+ required: z.ZodOptional<z.ZodBoolean>;
248
+ default: z.ZodOptional<z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>]>>;
249
+ default_source: z.ZodOptional<z.ZodString>;
250
+ enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
251
+ pattern: z.ZodOptional<z.ZodString>;
252
+ minimum: z.ZodOptional<z.ZodNumber>;
253
+ maximum: z.ZodOptional<z.ZodNumber>;
254
+ }, z.core.$loose>;
255
+ export type TemplateParameter = z.infer<typeof TemplateParameterSchema>;
256
+ /**
257
+ * The contract of one template as `GET /v1/templates/{id}` serves it: every
258
+ * slot, pinned ones included, the inputs, and the outputs. The service adds
259
+ * fields such as `step_types` and `environment`; they pass through the loose
260
+ * remainder.
261
+ */
262
+ export declare const TemplateContractSchema: z.ZodObject<{
263
+ id: z.ZodString;
264
+ version: z.ZodString;
265
+ label: z.ZodString;
266
+ method: z.ZodString;
267
+ substitute_for: z.ZodOptional<z.ZodString>;
268
+ language: z.ZodString;
269
+ honors: z.ZodOptional<z.ZodArray<z.ZodString>>;
270
+ parameters: z.ZodArray<z.ZodObject<{
271
+ name: z.ZodString;
272
+ type: z.ZodString;
273
+ description: z.ZodString;
274
+ adaptable: z.ZodBoolean;
275
+ local: z.ZodOptional<z.ZodBoolean>;
276
+ required: z.ZodOptional<z.ZodBoolean>;
277
+ default: z.ZodOptional<z.ZodUnion<readonly [z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>, z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodNull]>>]>>;
278
+ default_source: z.ZodOptional<z.ZodString>;
279
+ enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
280
+ pattern: z.ZodOptional<z.ZodString>;
281
+ minimum: z.ZodOptional<z.ZodNumber>;
282
+ maximum: z.ZodOptional<z.ZodNumber>;
283
+ }, z.core.$loose>>;
284
+ inputs: z.ZodOptional<z.ZodArray<z.ZodObject<{
285
+ name: z.ZodString;
286
+ path: z.ZodString;
287
+ description: z.ZodOptional<z.ZodString>;
288
+ }, z.core.$loose>>>;
289
+ outputs: z.ZodArray<z.ZodObject<{
290
+ name: z.ZodString;
291
+ path: z.ZodString;
292
+ description: z.ZodOptional<z.ZodString>;
293
+ }, z.core.$loose>>;
294
+ notes: z.ZodOptional<z.ZodString>;
295
+ }, z.core.$loose>;
296
+ export type TemplateContract = z.infer<typeof TemplateContractSchema>;
297
+ /** The preferences of the caller: the language of the template the answer names. */
298
+ export interface KnowledgePreferences {
299
+ readonly language?: "R" | "python";
300
+ }
301
+ /** What a drafted step carries into the check. */
302
+ export interface DraftedStep {
303
+ readonly step_type: string;
304
+ readonly method: string;
305
+ /** The catalog id of the method (`M-dddd`), copied from the recommendation. An exact match wins over the wording. */
306
+ readonly method_id?: string;
307
+ readonly package?: string;
308
+ readonly parameters?: readonly {
309
+ readonly name: string;
310
+ readonly value: string | number | boolean;
311
+ }[];
312
+ /** The outcome the step states when the design forbids inference, for example `descriptive_only`. */
313
+ readonly outcome?: string;
314
+ }
315
+ export interface FarmPackage {
316
+ readonly name: string;
317
+ readonly version: string;
318
+ }
319
+ /** The service could not answer after the retry policy. The run continues from the prose skills. */
320
+ export interface KnowledgeUnavailable {
321
+ readonly match: "unavailable";
322
+ readonly reason: string;
323
+ }
324
+ /**
325
+ * The service serves another release than the one the plan pinned, and it
326
+ * did no work. A plan is not moved to a new release in silence: the caller
327
+ * proceeds as with an unavailable service, and it states the two digests.
328
+ */
329
+ export interface KnowledgeSnapshotMismatch {
330
+ readonly match: "snapshot_mismatch";
331
+ readonly message: string;
332
+ readonly expected: string;
333
+ readonly served: {
334
+ readonly date: string;
335
+ readonly digest: string;
336
+ };
337
+ }
338
+ /** The service refused the request and named the field or the slot and the permitted values. */
339
+ export interface KnowledgeRejected {
340
+ readonly match: "rejected";
341
+ readonly message: string;
342
+ readonly issues: readonly {
343
+ readonly field?: string;
344
+ readonly slot?: string;
345
+ readonly message?: string;
346
+ readonly reason?: string;
347
+ readonly permitted?: readonly string[];
348
+ }[];
349
+ }
350
+ /** What a render call carries beside the template and the slots. */
351
+ export interface RenderCallOptions {
352
+ /** The id of the plan step that runs the script. */
353
+ readonly step?: string;
354
+ /** The claims of the plan step that selected the template. Each must resolve in the served snapshot. */
355
+ readonly claims?: readonly string[];
356
+ /** The release digest the plan pinned. The service refuses another release with `snapshot_mismatch`. */
357
+ readonly expectedSnapshot?: string;
358
+ }
359
+ export interface KnowledgeClient {
360
+ /** `expectedSnapshot` pins the release of an earlier answer of the same plan; the first call of a plan has none. */
361
+ recommend(situation: KnowledgeSituation, responseFormat?: "concise" | "detailed", preferences?: KnowledgePreferences, expectedSnapshot?: string): Promise<RecommendResponse | KnowledgeUnavailable | KnowledgeRejected | KnowledgeSnapshotMismatch>;
362
+ check(situation: KnowledgeSituation, steps: readonly DraftedStep[], expectedSnapshot?: string): Promise<CheckResponse | KnowledgeUnavailable | KnowledgeRejected | KnowledgeSnapshotMismatch>;
363
+ /** `slots` never holds a local slot: the host binds those after the render, on the machine. */
364
+ render(template: string, slots: Readonly<Record<string, unknown>>, farm?: readonly FarmPackage[], options?: RenderCallOptions): Promise<RenderResponse | KnowledgeUnavailable | KnowledgeRejected | KnowledgeSnapshotMismatch>;
365
+ /**
366
+ * The slot contract of a template, by reference (`tpl-x@1.0.0`) or by id.
367
+ * The service serves one version per snapshot, thus the answer carries the
368
+ * version it serves and the caller compares it with the reference. An
369
+ * unknown template is `rejected` with the field `template`.
370
+ */
371
+ contract(template: string): Promise<TemplateContract | KnowledgeUnavailable | KnowledgeRejected>;
372
+ }
373
+ export interface HttpKnowledgeClientConfig {
374
+ /** The service origin, for example `https://knowledge.inflexa.ai`. */
375
+ readonly baseUrl: string;
376
+ /** The bearer key. The license of the service. */
377
+ readonly apiKey: string;
378
+ readonly timeoutMs?: number;
379
+ readonly maxRetries?: number;
380
+ }
381
+ export declare function createHttpKnowledgeClient(config: HttpKnowledgeClientConfig): KnowledgeClient;
382
+ export {};
@@ -0,0 +1,302 @@
1
+ /**
2
+ * The knowledge service interface of the harness.
3
+ *
4
+ * `KnowledgeClient` is the one contract between the harness and the remote
5
+ * knowledge service: four typed operations and the snapshot they answer
6
+ * from. The harness never knows about a license. It sees a client, or it sees
7
+ * nothing, and an absent client is a normal state in which no knowledge tool
8
+ * attaches. `createHttpKnowledgeClient` is the shipped realization over plain
9
+ * HTTPS with the retry and timeout policy of the other external tools.
10
+ *
11
+ * Every operation answers a data variant. A service that is configured but
12
+ * unreachable gives `{ match: "unavailable" }` after the retry policy, and the
13
+ * caller continues from the prose skills. A 400 names the field and the
14
+ * permitted values, thus a model corrects itself in one turn. A 404 on the
15
+ * contract lookup names the `template` field the same way. The response
16
+ * shapes below are the harness copy of the wire contract of the service, kept
17
+ * lenient with `looseObject`, thus a richer answer of a later snapshot still
18
+ * parses.
19
+ */
20
+ import { z } from "zod";
21
+ import { apiFetchValidated, describeApiError } from "../lib/api-utils.js";
22
+ // ── The answers, as the harness reads them ──────────────────────────
23
+ const SnapshotRefSchema = z.object({ date: z.string(), digest: z.string() });
24
+ /** A method of the catalog by id and label. */
25
+ const MethodRefSchema = z.looseObject({ id: z.string(), label: z.string() });
26
+ /** Two rules of equal specificity and strength disagree on a parameter. The step omits the parameter and names both. */
27
+ const ProcedureConflictSchema = z.looseObject({
28
+ parameter: z.string(),
29
+ entries: z.array(z.looseObject({ rule: z.string(), value: z.unknown() })),
30
+ });
31
+ /** The step method is a declared substitute: `for` names the method of record it stands in for. */
32
+ const ProcedureSubstitutionSchema = z.looseObject({ for: z.string(), label: z.string(), template: z.string() });
33
+ /**
34
+ * No template of the requested language is eligible; the step keeps the first template that is. A skipped template
35
+ * lacks a design requirement (`missing`), or its slot refuses a value that the rules give (`refused`).
36
+ */
37
+ const ProcedureLimitSchema = z.looseObject({
38
+ requested_language: z.enum(["R", "python"]),
39
+ reason: z.string(),
40
+ skipped: z.array(z.looseObject({ template: z.string(), missing: z.array(z.string()), refused: z.string().optional() })),
41
+ });
42
+ /** The rules select a method for the step, and no template of the method holds: the knowledge covers the step, and no vetted script does. */
43
+ const ProcedureUnrealizedSchema = z.looseObject({
44
+ reason: z.string(),
45
+ templates: z.array(z.looseObject({ template: z.string(), why: z.string() })),
46
+ });
47
+ const ProcedureStepSchema = z.looseObject({
48
+ step: z.string(),
49
+ method: MethodRefSchema.optional(),
50
+ template: z.string().optional(),
51
+ rules: z.array(z.string()),
52
+ flags: z.array(z.looseObject({ rule: z.string(), severity: z.string(), message: z.string(), outcome: z.string().optional() })).optional(),
53
+ alternatives: z.array(z.looseObject({ method: z.string(), label: z.string(), when: z.string(), rules: z.array(z.string()) })).optional(),
54
+ disputed: z.looseObject({ rule: z.string(), sides: z.array(z.string()) }).optional(),
55
+ parameters: z.array(z.looseObject({ name: z.string(), value: z.unknown(), default_source: z.string().optional() })).optional(),
56
+ conflicts: z.array(ProcedureConflictSchema).optional(),
57
+ substitution: ProcedureSubstitutionSchema.optional(),
58
+ limit: ProcedureLimitSchema.optional(),
59
+ unrealized: ProcedureUnrealizedSchema.optional(),
60
+ });
61
+ const ClaimSchema = z.looseObject({
62
+ id: z.string(),
63
+ statement: z.string(),
64
+ strength: z.string(),
65
+ evidence: z.array(z.looseObject({ doi: z.string().optional(), pmid: z.string().optional(), title: z.string(), year: z.number(), direction: z.string() })),
66
+ });
67
+ export const RecommendResponseSchema = z.looseObject({
68
+ match: z.enum(["applicable", "none", "flag"]),
69
+ snapshot: SnapshotRefSchema,
70
+ procedure: z.array(ProcedureStepSchema),
71
+ uncovered: z.array(z.string()),
72
+ /** The steps whose method has no eligible template in this situation. Absent when none. */
73
+ unrealized: z.array(z.string()).optional(),
74
+ flags: z.array(z.looseObject({ rule: z.string(), severity: z.string(), message: z.string(), outcome: z.string().optional() })),
75
+ claims: z.array(ClaimSchema),
76
+ nearest: z.array(z.looseObject({ claim: z.string(), title: z.string(), failed: z.array(z.string()) })).optional(),
77
+ reason: z.string().optional(),
78
+ });
79
+ const FindingSchema = z.looseObject({
80
+ step_type: z.string(),
81
+ severity: z.string(),
82
+ rule: z.string(),
83
+ message: z.string(),
84
+ permitted: z.array(z.string()).optional(),
85
+ });
86
+ /** A drafted step that no rule covers in the situation: neither passed nor failed. */
87
+ const NotAssessedSchema = z.looseObject({ step_type: z.string(), reason: z.string(), message: z.string() });
88
+ export const CheckResponseSchema = z.looseObject({
89
+ /** True when the assessed steps carry no violation and no warning. A step in `not_assessed` does not count. */
90
+ ok: z.boolean(),
91
+ snapshot: SnapshotRefSchema,
92
+ violations: z.array(FindingSchema),
93
+ warnings: z.array(FindingSchema),
94
+ not_assessed: z.array(NotAssessedSchema).optional(),
95
+ });
96
+ export const RenderResponseSchema = z.looseObject({
97
+ ok: z.literal(true),
98
+ snapshot: SnapshotRefSchema,
99
+ template: z.looseObject({
100
+ id: z.string(),
101
+ version: z.string(),
102
+ label: z.string(),
103
+ /** The method the script runs: the substitute when the template is one. */
104
+ method: MethodRefSchema,
105
+ /** The method of record when the template is a declared substitute. */
106
+ substitute_for: MethodRefSchema.optional(),
107
+ language: z.string(),
108
+ }),
109
+ /** The script with each local slot as its marker `{{name}}`. The host binds the local values before it writes the file. */
110
+ script: z.string(),
111
+ /** A local slot entry has no value: the host binds it. */
112
+ slots: z.array(z.looseObject({
113
+ name: z.string(),
114
+ value: z.unknown().optional(),
115
+ source: z.string(),
116
+ adaptable: z.boolean(),
117
+ lines: z.array(z.number()),
118
+ read: z.boolean().optional(),
119
+ })),
120
+ /** The local slots the host must bind: each marker still in `script`. */
121
+ local_slots: z.array(z.string()).optional(),
122
+ environment: z.looseObject({ match: z.string() }),
123
+ syntax: z.looseObject({ status: z.string() }),
124
+ outputs: z.array(z.looseObject({ name: z.string(), path: z.string(), description: z.string().optional() })),
125
+ decision_record: z.record(z.string(), z.unknown()),
126
+ });
127
+ // ── The template contract ───────────────────────────────────────────
128
+ const SlotScalarSchema = z.union([z.string(), z.number(), z.boolean(), z.null()]);
129
+ /** A slot value on the wire: a scalar, or a list of scalars. */
130
+ const SlotValueSchema = z.union([SlotScalarSchema, z.array(SlotScalarSchema)]);
131
+ /**
132
+ * One slot of a template with the constraint a caller value must obey.
133
+ * `adaptable: false` marks a pinned slot: the render refuses a caller value
134
+ * for it. An adaptable slot without a default is required unless `required`
135
+ * is false.
136
+ */
137
+ const TemplateParameterSchema = z.looseObject({
138
+ name: z.string(),
139
+ type: z.string(),
140
+ description: z.string(),
141
+ adaptable: z.boolean(),
142
+ /** True for a slot whose value is a fact of the machine (a path, a column name, a level label, a formula): the host binds it, and the service never sees it. */
143
+ local: z.boolean().optional(),
144
+ required: z.boolean().optional(),
145
+ default: SlotValueSchema.optional(),
146
+ default_source: z.string().optional(),
147
+ enum: z.array(z.string()).optional(),
148
+ pattern: z.string().optional(),
149
+ minimum: z.number().optional(),
150
+ maximum: z.number().optional(),
151
+ });
152
+ /** A file the script reads. The path names the slot that gives it, for example `{{counts_path}}`. */
153
+ const TemplateInputSchema = z.looseObject({ name: z.string(), path: z.string(), description: z.string().optional() });
154
+ /**
155
+ * The contract of one template as `GET /v1/templates/{id}` serves it: every
156
+ * slot, pinned ones included, the inputs, and the outputs. The service adds
157
+ * fields such as `step_types` and `environment`; they pass through the loose
158
+ * remainder.
159
+ */
160
+ export const TemplateContractSchema = z.looseObject({
161
+ id: z.string(),
162
+ version: z.string(),
163
+ label: z.string(),
164
+ /** The catalog id of the method the script runs. */
165
+ method: z.string(),
166
+ /** The catalog id of the method of record when the template is a declared substitute. */
167
+ substitute_for: z.string().optional(),
168
+ language: z.string(),
169
+ /** The design requirements the script realizes. Absent: not subject to them. */
170
+ honors: z.array(z.string()).optional(),
171
+ parameters: z.array(TemplateParameterSchema),
172
+ inputs: z.array(TemplateInputSchema).optional(),
173
+ outputs: z.array(z.looseObject({ name: z.string(), path: z.string(), description: z.string().optional() })),
174
+ /** The input requirements in prose, from the applicability of the template. */
175
+ notes: z.string().optional(),
176
+ });
177
+ const ValidationFailureSchema = z.looseObject({
178
+ error: z.literal("validation"),
179
+ message: z.string(),
180
+ issues: z.array(z.looseObject({
181
+ message: z.string().optional(),
182
+ reason: z.string().optional(),
183
+ field: z.string().optional(),
184
+ slot: z.string().optional(),
185
+ permitted: z.array(z.string()).optional(),
186
+ })),
187
+ });
188
+ /** The `message` of an error envelope, or the body itself when it is not one. */
189
+ function messageOf(body) {
190
+ try {
191
+ const raw = JSON.parse(body);
192
+ if (raw !== null && typeof raw === "object" && "message" in raw && typeof raw.message === "string")
193
+ return raw.message;
194
+ }
195
+ catch {
196
+ // Not JSON: the body is the message.
197
+ }
198
+ return body || undefined;
199
+ }
200
+ /** The template id of a reference: `tpl-x@1.0.0` gives `tpl-x`. The service serves one version, thus the route takes the id alone. */
201
+ function templateIdOf(reference) {
202
+ const at = reference.indexOf("@");
203
+ return at < 0 ? reference : reference.slice(0, at);
204
+ }
205
+ const SnapshotMismatchSchema = z.looseObject({
206
+ error: z.literal("snapshot_mismatch"),
207
+ message: z.string(),
208
+ expected: z.string(),
209
+ served: SnapshotRefSchema,
210
+ });
211
+ /**
212
+ * A 400 is a rejection that names the field and the permitted values. A 404
213
+ * is one only on a lookup route, where `notFoundField` names the field whose
214
+ * value the service does not hold. A 409 is a snapshot mismatch: the service
215
+ * serves another release than the pinned one. Every other failure is
216
+ * `unavailable`.
217
+ */
218
+ function refusalOf(error, notFoundField) {
219
+ if (error.type !== "http_status")
220
+ return undefined;
221
+ if (error.status === 404 && notFoundField) {
222
+ const message = messageOf(error.body) ?? `no such ${notFoundField}`;
223
+ return { match: "rejected", message, issues: [{ field: notFoundField, message }] };
224
+ }
225
+ if (error.status === 409) {
226
+ let raw;
227
+ try {
228
+ raw = JSON.parse(error.body);
229
+ }
230
+ catch {
231
+ return undefined;
232
+ }
233
+ const parsed = SnapshotMismatchSchema.safeParse(raw);
234
+ if (!parsed.success)
235
+ return undefined;
236
+ return { match: "snapshot_mismatch", message: parsed.data.message, expected: parsed.data.expected, served: parsed.data.served };
237
+ }
238
+ if (error.status !== 400)
239
+ return undefined;
240
+ let raw;
241
+ try {
242
+ raw = JSON.parse(error.body);
243
+ }
244
+ catch {
245
+ return { match: "rejected", message: error.body || "the service refused the request", issues: [] };
246
+ }
247
+ const parsed = ValidationFailureSchema.safeParse(raw);
248
+ if (!parsed.success)
249
+ return { match: "rejected", message: error.body, issues: [] };
250
+ return { match: "rejected", message: parsed.data.message, issues: parsed.data.issues };
251
+ }
252
+ export function createHttpKnowledgeClient(config) {
253
+ // Trim the trailing slashes with a loop, not a regex: CodeQL flags `/\/+$/` on library input as polynomial.
254
+ let base = config.baseUrl;
255
+ while (base.endsWith("/"))
256
+ base = base.slice(0, -1);
257
+ const authorization = `Bearer ${config.apiKey}`;
258
+ const policy = { timeoutMs: config.timeoutMs ?? 30_000, maxRetries: config.maxRetries ?? 2, retryDelayMs: 500 };
259
+ /** One call of the service: a POST with a JSON body, or a GET without one. Every call carries the bearer key. */
260
+ async function call(method, path, body, schema, notFoundField) {
261
+ const headers = body === undefined ? { authorization } : { "content-type": "application/json", authorization };
262
+ const result = await apiFetchValidated(`${base}${path}`, schema, {
263
+ ...policy,
264
+ method,
265
+ headers,
266
+ ...(body === undefined ? {} : { body: JSON.stringify(body) }),
267
+ });
268
+ if (result.isOk())
269
+ return result.value;
270
+ const refusal = refusalOf(result.error, notFoundField);
271
+ if (refusal)
272
+ return refusal;
273
+ return { match: "unavailable", reason: describeApiError(result.error) };
274
+ }
275
+ /** A contract lookup carries no pin, thus a mismatch cannot come back; the type says so. */
276
+ async function lookup(path, schema, notFoundField) {
277
+ const answer = await call("GET", path, undefined, schema, notFoundField);
278
+ // A GET without a body has no pin, thus a 409 is not among its answers; the guard keeps the type honest.
279
+ if (typeof answer === "object" && answer !== null && "match" in answer && answer.match === "snapshot_mismatch") {
280
+ return { match: "unavailable", reason: answer.message };
281
+ }
282
+ return answer;
283
+ }
284
+ return {
285
+ recommend: (situation, responseFormat, preferences, expectedSnapshot) => call("POST", "/v1/recommend", {
286
+ situation,
287
+ ...(responseFormat ? { response_format: responseFormat } : {}),
288
+ ...(preferences ? { preferences } : {}),
289
+ ...(expectedSnapshot ? { expected_snapshot: expectedSnapshot } : {}),
290
+ }, RecommendResponseSchema),
291
+ check: (situation, steps, expectedSnapshot) => call("POST", "/v1/check", { situation, steps, ...(expectedSnapshot ? { expected_snapshot: expectedSnapshot } : {}) }, CheckResponseSchema),
292
+ render: (template, slots, farm, options) => call("POST", "/v1/template/render", {
293
+ template,
294
+ slots,
295
+ ...(farm ? { farm } : {}),
296
+ ...(options?.step ? { step: options.step } : {}),
297
+ ...(options?.claims ? { claims: options.claims } : {}),
298
+ ...(options?.expectedSnapshot ? { expected_snapshot: options.expectedSnapshot } : {}),
299
+ }, RenderResponseSchema),
300
+ contract: (template) => lookup(`/v1/templates/${encodeURIComponent(templateIdOf(template))}`, TemplateContractSchema, "template"),
301
+ };
302
+ }