@inflexa-ai/harness 0.44.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 (187) 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 +51 -0
  10. package/dist/app/approve-plan.js +84 -0
  11. package/dist/app/chat-turn.js +6 -3
  12. package/dist/app/data-profile-view.d.ts +1 -1
  13. package/dist/app/run-activity.js +4 -6
  14. package/dist/config/environment-stores.d.ts +4 -2
  15. package/dist/contracts/chat-events.d.ts +14 -2
  16. package/dist/contracts/chat-events.js +1 -1
  17. package/dist/contracts/chat-frame.d.ts +7 -0
  18. package/dist/contracts/chat-frame.js +24 -6
  19. package/dist/contracts/chat-parts.d.ts +73 -2
  20. package/dist/contracts/chat-parts.js +3 -1
  21. package/dist/contracts/index.d.ts +4 -2
  22. package/dist/contracts/index.js +1 -0
  23. package/dist/contracts/part-registry.js +3 -0
  24. package/dist/contracts/schemas/chat-events.d.ts +38 -0
  25. package/dist/contracts/schemas/chat-events.js +8 -0
  26. package/dist/contracts/schemas/chat-parts.d.ts +239 -15
  27. package/dist/contracts/schemas/chat-parts.js +80 -12
  28. package/dist/contracts/schemas/index.d.ts +2 -2
  29. package/dist/contracts/schemas/index.js +2 -2
  30. package/dist/contracts/subagent-activity.d.ts +12 -0
  31. package/dist/contracts/subagent-activity.js +76 -0
  32. package/dist/execution/artifact-metadata.js +1 -3
  33. package/dist/execution/dbos-workflow-purger.d.ts +2 -0
  34. package/dist/execution/dbos-workflow-purger.js +21 -14
  35. package/dist/execution/run-canceler.d.ts +33 -11
  36. package/dist/execution/run-canceler.js +130 -28
  37. package/dist/execution/run-end-record.d.ts +30 -0
  38. package/dist/execution/run-end-record.js +27 -0
  39. package/dist/execution/run-event-parts.d.ts +6 -5
  40. package/dist/execution/run-event-parts.js +6 -5
  41. package/dist/execution/run-event-stream.d.ts +18 -6
  42. package/dist/execution/run-event-stream.js +67 -17
  43. package/dist/execution/run-synthesis.d.ts +21 -0
  44. package/dist/execution/run-synthesis.js +77 -3
  45. package/dist/index.d.ts +28 -10
  46. package/dist/index.js +26 -2
  47. package/dist/lib/hooks.d.ts +1 -1
  48. package/dist/lib/metrics.d.ts +2 -2
  49. package/dist/loop/call-guard.d.ts +50 -0
  50. package/dist/loop/call-guard.js +90 -0
  51. package/dist/loop/compaction.d.ts +5 -0
  52. package/dist/loop/compaction.js +17 -0
  53. package/dist/loop/run-agent.d.ts +9 -0
  54. package/dist/loop/run-agent.js +16 -1
  55. package/dist/loop/run-to-terminal.js +4 -1
  56. package/dist/loop/types.d.ts +8 -0
  57. package/dist/memory/card-builders.d.ts +11 -0
  58. package/dist/memory/card-builders.js +35 -21
  59. package/dist/memory/conversation-display-recorder.d.ts +2 -2
  60. package/dist/memory/conversation-display-recorder.js +38 -4
  61. package/dist/memory/conversation-display-storage.d.ts +9 -2
  62. package/dist/memory/conversation-display-storage.js +18 -3
  63. package/dist/memory/thread-history.d.ts +42 -8
  64. package/dist/memory/thread-history.js +164 -45
  65. package/dist/memory/thread-store.d.ts +13 -0
  66. package/dist/memory/thread-store.js +15 -1
  67. package/dist/prompts/briefing.d.ts +57 -3
  68. package/dist/prompts/briefing.js +129 -2
  69. package/dist/prompts/conversation.d.ts +1 -1
  70. package/dist/prompts/conversation.js +3 -1
  71. package/dist/prompts/planner.js +13 -2
  72. package/dist/provenance/seam.d.ts +39 -20
  73. package/dist/provenance/seam.js +7 -5
  74. package/dist/providers/ai-sdk.js +93 -11
  75. package/dist/providers/types.d.ts +6 -0
  76. package/dist/reference-data/catalog.js +141 -2
  77. package/dist/report-model/authoring-grammar.d.ts +26 -26
  78. package/dist/report-model/pin-snapshot.d.ts +1 -1
  79. package/dist/report-model/pin-snapshot.js +2 -50
  80. package/dist/runtime/assemble.d.ts +8 -1
  81. package/dist/runtime/assemble.js +11 -0
  82. package/dist/runtime/boot.d.ts +3 -0
  83. package/dist/runtime/boot.js +10 -1
  84. package/dist/sandbox/image-packages.d.ts +26 -4
  85. package/dist/sandbox/image-packages.js +18 -4
  86. package/dist/sandbox/sandbox-step-translate.d.ts +23 -0
  87. package/dist/sandbox/sandbox-step-translate.js +24 -0
  88. package/dist/schemas/artifact-manifest.d.ts +1 -1
  89. package/dist/schemas/artifact-manifest.js +11 -9
  90. package/dist/schemas/data-profile-schemas.d.ts +12 -12
  91. package/dist/schemas/plan-schemas.d.ts +75 -21
  92. package/dist/schemas/plan-schemas.js +1 -1
  93. package/dist/schemas/run-synthesis.d.ts +2 -2
  94. package/dist/schemas/workflow-state.d.ts +113 -0
  95. package/dist/schemas/workflow-state.js +54 -0
  96. package/dist/state/analyses-metrics.d.ts +79 -7
  97. package/dist/state/analyses-metrics.js +141 -6
  98. package/dist/state/artifacts.d.ts +66 -3
  99. package/dist/state/artifacts.js +85 -7
  100. package/dist/state/data-profile.d.ts +4 -2
  101. package/dist/state/data-profile.js +7 -5
  102. package/dist/state/index.d.ts +9 -8
  103. package/dist/state/index.js +5 -5
  104. package/dist/state/migrations/20261012120000_index_the_runs_by_start.d.ts +8 -0
  105. package/dist/state/migrations/20261012120000_index_the_runs_by_start.js +12 -0
  106. package/dist/state/migrations/20261013120000_index_the_ended_runs_and_the_recent_threads.d.ts +8 -0
  107. package/dist/state/migrations/20261013120000_index_the_ended_runs_and_the_recent_threads.js +13 -0
  108. package/dist/state/migrations/20261013180000_narrow_the_active_run_index.d.ts +8 -0
  109. package/dist/state/migrations/20261013180000_narrow_the_active_run_index.js +13 -0
  110. package/dist/state/migrations/20261014120000_index_the_plans_by_parent_and_the_runs_by_plan.d.ts +8 -0
  111. package/dist/state/migrations/20261014120000_index_the_plans_by_parent_and_the_runs_by_plan.js +13 -0
  112. package/dist/state/migrations/index.js +8 -0
  113. package/dist/state/plans.d.ts +21 -0
  114. package/dist/state/plans.js +25 -0
  115. package/dist/state/runs.d.ts +79 -14
  116. package/dist/state/runs.js +150 -33
  117. package/dist/state/schema.d.ts +2 -0
  118. package/dist/state/step-executions.d.ts +9 -0
  119. package/dist/state/step-executions.js +22 -0
  120. package/dist/tasks/data-profile-activity.d.ts +7 -1
  121. package/dist/tasks/data-profile-activity.js +18 -9
  122. package/dist/tasks/data-profile.d.ts +15 -1
  123. package/dist/tasks/data-profile.js +16 -11
  124. package/dist/tools/ad-hoc-router.d.ts +2 -0
  125. package/dist/tools/ad-hoc-router.js +18 -2
  126. package/dist/tools/approval/gateway.js +28 -5
  127. package/dist/tools/capability-data.d.ts +36 -0
  128. package/dist/tools/capability-data.js +73 -0
  129. package/dist/tools/display/show-user.d.ts +1 -1
  130. package/dist/tools/execute-analysis.d.ts +80 -1
  131. package/dist/tools/execute-analysis.js +352 -110
  132. package/dist/tools/knowledge/check.d.ts +60 -0
  133. package/dist/tools/knowledge/check.js +92 -0
  134. package/dist/tools/knowledge/client.d.ts +382 -0
  135. package/dist/tools/knowledge/client.js +302 -0
  136. package/dist/tools/knowledge/environment.d.ts +78 -0
  137. package/dist/tools/knowledge/environment.js +105 -0
  138. package/dist/tools/knowledge/index.d.ts +49 -0
  139. package/dist/tools/knowledge/index.js +70 -0
  140. package/dist/tools/knowledge/local-slots.d.ts +62 -0
  141. package/dist/tools/knowledge/local-slots.js +177 -0
  142. package/dist/tools/knowledge/pin.d.ts +14 -0
  143. package/dist/tools/knowledge/pin.js +17 -0
  144. package/dist/tools/knowledge/recommend.d.ts +92 -0
  145. package/dist/tools/knowledge/recommend.js +98 -0
  146. package/dist/tools/knowledge/situation.d.ts +143 -0
  147. package/dist/tools/knowledge/situation.js +164 -0
  148. package/dist/tools/knowledge/skeleton.d.ts +69 -0
  149. package/dist/tools/knowledge/skeleton.js +202 -0
  150. package/dist/tools/knowledge/template.d.ts +137 -0
  151. package/dist/tools/knowledge/template.js +267 -0
  152. package/dist/tools/research/generate-plan.d.ts +23 -0
  153. package/dist/tools/research/generate-plan.js +203 -12
  154. package/dist/tools/research/guarded-address.d.ts +20 -0
  155. package/dist/tools/research/guarded-address.js +148 -0
  156. package/dist/tools/research/inspect-data-profile.d.ts +1 -1
  157. package/dist/tools/research/inspect-run.d.ts +1 -1
  158. package/dist/tools/research/inspect-run.js +7 -7
  159. package/dist/tools/research/searxng-engine.d.ts +40 -0
  160. package/dist/tools/research/searxng-engine.js +120 -0
  161. package/dist/tools/research/web-fetch.d.ts +124 -0
  162. package/dist/tools/research/web-fetch.js +627 -0
  163. package/dist/tools/research/web-search.d.ts +75 -0
  164. package/dist/tools/research/web-search.js +112 -0
  165. package/dist/tools/sandbox/list-available-packages.d.ts +7 -4
  166. package/dist/tools/sandbox/list-available-packages.js +12 -4
  167. package/dist/tools/sandbox/list-available-refs.d.ts +21 -0
  168. package/dist/tools/sandbox/list-available-refs.js +60 -3
  169. package/dist/tools/sandbox/report-blocker.js +5 -1
  170. package/dist/tools/terminal-text.d.ts +11 -0
  171. package/dist/tools/terminal-text.js +19 -0
  172. package/dist/tools/workspace/decision-record.d.ts +81 -0
  173. package/dist/tools/workspace/decision-record.js +117 -0
  174. package/dist/tools/workspace/edit-file.d.ts +3 -1
  175. package/dist/tools/workspace/edit-file.js +20 -2
  176. package/dist/tools/workspace/execute-command.d.ts +17 -0
  177. package/dist/tools/workspace/execute-command.js +28 -1
  178. package/dist/tools/workspace/index.d.ts +1 -0
  179. package/dist/tools/workspace/index.js +1 -0
  180. package/dist/tools/workspace/mutator.d.ts +1 -1
  181. package/dist/workflows/execute-analysis.d.ts +62 -21
  182. package/dist/workflows/execute-analysis.js +307 -79
  183. package/dist/workflows/plan/graph.d.ts +16 -0
  184. package/dist/workflows/plan/graph.js +36 -0
  185. package/dist/workflows/sandbox-step.d.ts +12 -0
  186. package/dist/workflows/sandbox-step.js +14 -12
  187. package/package.json +5 -1
@@ -0,0 +1,177 @@
1
+ /**
2
+ * The local slots of a template: the slots whose value is a fact of the
3
+ * machine (a path, a column name, a level label, a design formula). The
4
+ * service renders such a slot as its marker `{{name}}` and never sees its
5
+ * value; the host binds the value here, after the render and before the
6
+ * write, thus the value never leaves the machine.
7
+ *
8
+ * `bindLocalSlots` is a copy of the reference implementation the knowledge
9
+ * service exports (`src/render/render.ts` of the service). The two must give
10
+ * the same bytes: a marked render bound here equals a full render of the
11
+ * service for the same values. Keep the literal rules in step with the
12
+ * service when a slot type is added.
13
+ */
14
+ /** The local slots of a contract, in the order the template declares them. */
15
+ export function localParameters(parameters) {
16
+ return parameters.filter((parameter) => parameter.local === true);
17
+ }
18
+ const FORMULA_PATTERN = /^~\s*[A-Za-z0-9_.+*:()\s-]+$/;
19
+ /** Check one caller value against the contract of its slot, as the renderer of the service does. */
20
+ export function validateLocalSlot(parameter, value) {
21
+ const slot = parameter.name;
22
+ const permitted = parameter.enum;
23
+ switch (parameter.type) {
24
+ case "string":
25
+ case "formula": {
26
+ if (typeof value !== "string" || value.length === 0)
27
+ return { slot, reason: "a non-empty string is required" };
28
+ if (parameter.type === "formula" && !FORMULA_PATTERN.test(value))
29
+ return { slot, reason: "a formula must start with ~ and hold only names, +, :, *, and parentheses" };
30
+ if (permitted && !permitted.includes(value))
31
+ return { slot, reason: `the value "${value}" is not permitted`, permitted };
32
+ if (parameter.pattern && !new RegExp(parameter.pattern).test(value))
33
+ return { slot, reason: `the value must match ${parameter.pattern}` };
34
+ return undefined;
35
+ }
36
+ case "number":
37
+ case "integer": {
38
+ if (typeof value !== "number" || !Number.isFinite(value))
39
+ return { slot, reason: "a number is required" };
40
+ if (parameter.type === "integer" && !Number.isInteger(value))
41
+ return { slot, reason: "an integer is required" };
42
+ if (parameter.minimum !== undefined && value < parameter.minimum)
43
+ return { slot, reason: `the value must be at least ${parameter.minimum}` };
44
+ if (parameter.maximum !== undefined && value > parameter.maximum)
45
+ return { slot, reason: `the value must be at most ${parameter.maximum}` };
46
+ return undefined;
47
+ }
48
+ case "boolean":
49
+ return typeof value === "boolean" ? undefined : { slot, reason: "a boolean is required" };
50
+ case "string_list": {
51
+ if (!Array.isArray(value) || !value.every((item) => typeof item === "string"))
52
+ return { slot, reason: "a list of strings is required" };
53
+ if (permitted) {
54
+ const bad = value.find((item) => !permitted.includes(item));
55
+ if (bad !== undefined)
56
+ return { slot, reason: `the value "${bad}" is not permitted`, permitted };
57
+ }
58
+ return undefined;
59
+ }
60
+ default:
61
+ // The contract is a loose object of the wire, thus a type this host does not know is possible. It is
62
+ // refused, never rendered as text, because a wrong literal is a wrong script.
63
+ return { slot, reason: `the slot type ${parameter.type} is not one this host renders` };
64
+ }
65
+ }
66
+ /** A slot value as a literal of the language: R gives `TRUE` and `c(...)`, Python gives `True` and `[...]`. */
67
+ function literal(language, parameter, value) {
68
+ const python = language === "python";
69
+ switch (parameter.type) {
70
+ case "string":
71
+ return JSON.stringify(String(value));
72
+ case "formula":
73
+ return String(value);
74
+ case "number":
75
+ case "integer":
76
+ return String(value);
77
+ case "boolean":
78
+ return python ? (value ? "True" : "False") : value ? "TRUE" : "FALSE";
79
+ case "string_list": {
80
+ const items = Array.isArray(value) ? value : [String(value)];
81
+ const rendered = items.map((item) => JSON.stringify(String(item))).join(", ");
82
+ return python ? `[${rendered}]` : `c(${rendered})`;
83
+ }
84
+ default:
85
+ throw new Error(`unhandled slot type: ${parameter.type}`);
86
+ }
87
+ }
88
+ /** The literal of an optional slot that has no value: R keeps the type of the slot, Python has one absent scalar and an empty list. */
89
+ function absentLiteral(language, type) {
90
+ if (language === "python")
91
+ return type === "string_list" ? "[]" : "None";
92
+ switch (type) {
93
+ case "string":
94
+ return "NA_character_";
95
+ case "integer":
96
+ return "NA_integer_";
97
+ case "number":
98
+ return "NA_real_";
99
+ case "boolean":
100
+ return "NA";
101
+ case "string_list":
102
+ return "character(0)";
103
+ default:
104
+ return "NULL";
105
+ }
106
+ }
107
+ /** The marker of a slot as the service leaves it in a marked render. */
108
+ export function slotMarker(name) {
109
+ return `{{${name}}}`;
110
+ }
111
+ /**
112
+ * Bind the local values into a marked script. Each local marker takes the
113
+ * literal of its value, else of the default of the slot, else the absent
114
+ * literal when the slot is optional. A required local slot with no value
115
+ * and no default is an issue, and so is a value the contract refuses. The
116
+ * report names the source of each bound slot and keeps the `read` flag of
117
+ * the marked report of the service.
118
+ */
119
+ export function bindLocalSlots(template, script, localValues, marked = []) {
120
+ const issues = [];
121
+ const locals = localParameters(template.parameters);
122
+ const byName = new Map(locals.map((parameter) => [parameter.name, parameter]));
123
+ for (const name of Object.keys(localValues)) {
124
+ if (!byName.has(name))
125
+ issues.push({ slot: name, reason: "the template has no such local slot", permitted: [...byName.keys()] });
126
+ }
127
+ const values = new Map();
128
+ const slots = [];
129
+ for (const parameter of locals) {
130
+ const given = localValues[parameter.name];
131
+ if (given !== undefined) {
132
+ const issue = validateLocalSlot(parameter, given);
133
+ if (issue) {
134
+ issues.push(issue);
135
+ continue;
136
+ }
137
+ // Validated above: a string, a finite number, a boolean, or a list of strings.
138
+ const value = given;
139
+ values.set(parameter.name, value);
140
+ slots.push({ name: parameter.name, value, source: "caller", adaptable: true, lines: [] });
141
+ }
142
+ else if (parameter.default !== undefined) {
143
+ // The contract is loose on the wire; a default is a scalar or a list of strings by the contract of the service,
144
+ // and the service renders whatever it holds, thus only an absent default is absent here too.
145
+ const value = parameter.default;
146
+ values.set(parameter.name, value);
147
+ slots.push({
148
+ name: parameter.name,
149
+ value,
150
+ source: "default",
151
+ adaptable: true,
152
+ ...(parameter.default_source ? { default_source: parameter.default_source } : {}),
153
+ lines: [],
154
+ });
155
+ }
156
+ else if (parameter.required !== false) {
157
+ issues.push({ slot: parameter.name, reason: "the local slot is required and has no value" });
158
+ }
159
+ }
160
+ if (issues.length > 0)
161
+ return { ok: false, issues };
162
+ const bound = script.replace(/\{\{([a-z][a-z0-9_]*)\}\}/g, (whole, name) => {
163
+ const parameter = byName.get(name);
164
+ if (!parameter)
165
+ return whole;
166
+ const value = values.get(name);
167
+ return value === undefined ? absentLiteral(template.language, parameter.type) : literal(template.language, parameter, value);
168
+ });
169
+ const lines = bound.split("\n");
170
+ const readByName = new Map(marked.flatMap((entry) => (entry.read === undefined ? [] : [[entry.name, entry.read]])));
171
+ const located = slots.map((entry) => ({
172
+ ...entry,
173
+ lines: script.split("\n").flatMap((line, index) => (line.includes(slotMarker(entry.name)) && index < lines.length ? [index + 1] : [])),
174
+ ...(readByName.has(entry.name) ? { read: readByName.get(entry.name) } : {}),
175
+ }));
176
+ return { ok: true, script: bound, slots: located };
177
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * The release a plan pins. The planner tools of one plan generation share
3
+ * one pin: the first recommend answer sets it, and every later call of the
4
+ * service inside that plan carries it as `expected_snapshot`. A service that
5
+ * moved to another release between two calls then refuses the call, and the
6
+ * plan never mixes two releases in silence.
7
+ */
8
+ export interface SnapshotPin {
9
+ /** The pinned release digest, or `undefined` before the first answer. */
10
+ get(): string | undefined;
11
+ /** Pin the release of the first answer. A later call with another digest changes nothing: the first release is the plan's. */
12
+ set(digest: string): void;
13
+ }
14
+ export declare function createSnapshotPin(): SnapshotPin;
@@ -0,0 +1,17 @@
1
+ /**
2
+ * The release a plan pins. The planner tools of one plan generation share
3
+ * one pin: the first recommend answer sets it, and every later call of the
4
+ * service inside that plan carries it as `expected_snapshot`. A service that
5
+ * moved to another release between two calls then refuses the call, and the
6
+ * plan never mixes two releases in silence.
7
+ */
8
+ export function createSnapshotPin() {
9
+ let pinned;
10
+ return {
11
+ get: () => pinned,
12
+ set: (digest) => {
13
+ if (pinned === undefined)
14
+ pinned = digest;
15
+ },
16
+ };
17
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * `knowledge_recommend` — one typed situation in, one plan skeleton out.
3
+ *
4
+ * The description is the whole contract with the planner: when to call, what
5
+ * to send, and where the answer goes (the `grounding` field of each method
6
+ * step). No prompt names this tool. Whether a model calls it from the
7
+ * description alone is a Phase 0 measurement.
8
+ *
9
+ * The model receives one representation. The procedure of the service is
10
+ * folded into `plan_skeleton` (the shape `submit_plan` accepts), and the
11
+ * claims ride beside it, cited by id from the steps. The procedure itself
12
+ * stays out of the tool result: the same fact would otherwise reach the
13
+ * model three times, as a procedure step, as a claim view, and as a skeleton
14
+ * step.
15
+ */
16
+ import type { EnvironmentStorePaths } from "../../config/environment-stores.js";
17
+ import type { KnowledgeClient, KnowledgeRejected, KnowledgeSnapshotMismatch, KnowledgeUnavailable, RecommendResponse } from "./client.js";
18
+ import type { SnapshotPin } from "./pin.js";
19
+ import { type EnvironmentPaths, type RecommendWithEnvironment } from "./environment.js";
20
+ import { type SkeletonStep } from "./skeleton.js";
21
+ export interface KnowledgeRecommendDeps extends Omit<EnvironmentPaths, "farmLockFile"> {
22
+ readonly client: KnowledgeClient;
23
+ /** Host path of the farm `inflexa.lock`, or its resolver for each analysis. Absent, the packages read as unknown. */
24
+ readonly farmLockFile?: EnvironmentStorePaths["farmLockFile"];
25
+ /**
26
+ * Receives each answer the planner sees. The planner keeps the skeleton of
27
+ * the invocation, thus `submit_plan` can restore a grounding field that the
28
+ * model dropped when it copied a skeleton step.
29
+ */
30
+ readonly onAnswer?: (answer: KnowledgeRecommendAnswer) => void;
31
+ /**
32
+ * The release of the plan. The first answer sets it, and each later call
33
+ * of this tool and of `knowledge_check` sends it, thus a service that moved
34
+ * to another release between two calls refuses instead of mixing releases.
35
+ */
36
+ readonly pin?: SnapshotPin;
37
+ }
38
+ /**
39
+ * The model-facing answer of the tool: the skeleton and the claims, with the
40
+ * envelope of the service and the environment source. Never the procedure.
41
+ */
42
+ export interface KnowledgeRecommendAnswer {
43
+ readonly match: RecommendResponse["match"];
44
+ readonly snapshot: RecommendResponse["snapshot"];
45
+ /** The situation as the service normalized it. */
46
+ readonly situation?: Readonly<Record<string, unknown>>;
47
+ readonly flags: RecommendResponse["flags"];
48
+ /** The step types a flag removed. Absent when none. */
49
+ readonly dropped?: readonly string[];
50
+ readonly uncovered: readonly string[];
51
+ /** The step types whose method has no eligible template: covered by the knowledge, not by a vetted script. Absent when none. */
52
+ readonly unrealized?: readonly string[];
53
+ readonly environment_source?: RecommendWithEnvironment["environment_source"];
54
+ readonly plan_skeleton: readonly SkeletonStep[];
55
+ readonly claims: RecommendResponse["claims"];
56
+ readonly nearest?: RecommendResponse["nearest"];
57
+ readonly reason?: string;
58
+ }
59
+ export type KnowledgeRecommendOutput = KnowledgeRecommendAnswer | KnowledgeUnavailable | KnowledgeRejected | KnowledgeSnapshotMismatch;
60
+ /** The one representation: the envelope, the skeleton, and the claims. The procedure stays host-side. */
61
+ export declare function toPlannerAnswer(joined: RecommendWithEnvironment): KnowledgeRecommendAnswer;
62
+ export declare function createKnowledgeRecommendTool(deps: KnowledgeRecommendDeps): import("../define-tool.js").Tool<{
63
+ question: "enrichment" | "differential_expression" | "qc" | "full_plan" | "tf_activity" | "deconvolution" | "coexpression" | "clustering" | "survival" | "signature_scoring";
64
+ modality: "bulk_rna_seq";
65
+ data_state: "fastq" | "counts" | "tpm_or_fpkm" | "log_normalized";
66
+ organism: "other" | "human" | "mouse";
67
+ n_groups: number;
68
+ n_per_group_min: number;
69
+ n_per_group_max: number;
70
+ paired: boolean;
71
+ batch: "none" | "known_balanced" | "known_confounded" | "known_unbalanced" | "suspected";
72
+ count_source?: "unknown" | "salmon" | "kallisto" | "star_featurecounts" | "rsem" | undefined;
73
+ import_state?: "unknown" | "quantifications" | "estimated_counts_with_lengths" | "corrected_counts" | "integer_counts" | undefined;
74
+ n_samples?: number | undefined;
75
+ n_events?: number | undefined;
76
+ blocking_factor?: "other" | "individual" | "cell_line" | "litter" | "site" | "pair" | null | undefined;
77
+ block_structure?: "crossed" | "nested" | undefined;
78
+ covariates?: ("other" | "technical" | "sex" | "age" | "clinical")[] | undefined;
79
+ n_timepoints?: number | null | undefined;
80
+ continuous_predictor?: "other" | "score" | "time" | "age" | "dose" | null | undefined;
81
+ technical_replicates?: boolean | undefined;
82
+ sample_exclusions?: boolean | undefined;
83
+ feature_level?: "gene" | "exon" | "transcript" | undefined;
84
+ library_type?: "unknown" | "total" | "polyA" | "three_prime" | undefined;
85
+ strandedness?: "unknown" | "verified" | "declared_unverified" | undefined;
86
+ interaction?: boolean | undefined;
87
+ classifier?: boolean | undefined;
88
+ extra_analyses?: ("tf_activity" | "deconvolution" | "coexpression" | "clustering" | "survival" | "signature_scoring" | "variance_partition" | "pathway_activity" | "transcript_level" | "annotation")[] | undefined;
89
+ preferred_language?: "R" | "python" | undefined;
90
+ enrichment_input?: "ranked_list" | "gene_list" | "sample_scores" | undefined;
91
+ quality_flags?: ("low_depth_sample" | "outlier_sample" | "sample_identity_doubt" | "high_duplication" | "degraded_rna")[] | undefined;
92
+ }, KnowledgeRecommendOutput>;
@@ -0,0 +1,98 @@
1
+ /**
2
+ * `knowledge_recommend` — one typed situation in, one plan skeleton out.
3
+ *
4
+ * The description is the whole contract with the planner: when to call, what
5
+ * to send, and where the answer goes (the `grounding` field of each method
6
+ * step). No prompt names this tool. Whether a model calls it from the
7
+ * description alone is a Phase 0 measurement.
8
+ *
9
+ * The model receives one representation. The procedure of the service is
10
+ * folded into `plan_skeleton` (the shape `submit_plan` accepts), and the
11
+ * claims ride beside it, cited by id from the steps. The procedure itself
12
+ * stays out of the tool result: the same fact would otherwise reach the
13
+ * model three times, as a procedure step, as a claim view, and as a skeleton
14
+ * step.
15
+ */
16
+ import { ok } from "neverthrow";
17
+ import { defineTool } from "../define-tool.js";
18
+ import { joinEnvironment } from "./environment.js";
19
+ import { SITUATION_EXAMPLE, SituationFieldsSchema, toPreferences, toSituation } from "./situation.js";
20
+ import { buildPlanSkeleton } from "./skeleton.js";
21
+ function isRecord(value) {
22
+ return typeof value === "object" && value !== null && !Array.isArray(value);
23
+ }
24
+ function stringList(value) {
25
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : undefined;
26
+ }
27
+ /** The one representation: the envelope, the skeleton, and the claims. The procedure stays host-side. */
28
+ export function toPlannerAnswer(joined) {
29
+ const dropped = stringList(joined.dropped);
30
+ const unrealized = stringList(joined.unrealized);
31
+ return {
32
+ match: joined.match,
33
+ snapshot: joined.snapshot,
34
+ ...(isRecord(joined.situation) ? { situation: joined.situation } : {}),
35
+ flags: joined.flags,
36
+ ...(dropped ? { dropped } : {}),
37
+ uncovered: joined.uncovered,
38
+ ...(unrealized && unrealized.length > 0 ? { unrealized } : {}),
39
+ ...(joined.environment_source ? { environment_source: joined.environment_source } : {}),
40
+ plan_skeleton: buildPlanSkeleton(joined),
41
+ claims: joined.claims,
42
+ ...(joined.nearest ? { nearest: joined.nearest } : {}),
43
+ ...(joined.reason !== undefined ? { reason: joined.reason } : {}),
44
+ };
45
+ }
46
+ export function createKnowledgeRecommendTool(deps) {
47
+ return defineTool({
48
+ id: "knowledge_recommend",
49
+ description: "Get the cited, versioned procedure for a bulk RNA-seq analysis situation from the Inflexa knowledge service, folded into plan steps: " +
50
+ "which method for each step (QC, filter, normalize, model, test, shrinkage, multiple testing, enrichment, report), " +
51
+ "its settings with their sources, a tested script template per step, the rules that select them, and the evidence. " +
52
+ "Call it BEFORE you draft a method step for bulk RNA-seq differential expression, enrichment, or QC. " +
53
+ "Fill the situation from the Data Context (the design, the replicate counts, the batch structure, the data state, the quality concerns); never send a sample name or a file path. " +
54
+ `Example input: ${SITUATION_EXAMPLE}. ` +
55
+ "The answer holds one representation: `plan_skeleton`, the plan steps with the id, the name, the track, the agent, the packages, the dependencies, " +
56
+ "the caveats, the `alternatives`, the `forbids`, the `disputed` sides, the `environment`, and the `grounding` " +
57
+ "(the status, the snapshot, the claim ids, the template, the `settings`, and the reason) filled. " +
58
+ "`claims` holds one view per claim id the steps cite; cite a claim by its id, and never copy its text into the plan. " +
59
+ "Read the answer this way. `match: applicable`: copy each skeleton step into `submit_plan` as it is, including `grounding.settings`, " +
60
+ "then add the question, the acceptance criteria, the resources, and the step budget from the data profile. " +
61
+ "A skeleton step is the vetted default, not the only permitted method. When the design or the question does not fit its method, plan the step with your method: " +
62
+ "set `grounding.status: ungrounded`, keep the snapshot digest, cite no claim, and give the reason for the departure in `grounding.reason`. A departure the plan states is not a fault; a silent one is. " +
63
+ "`match: flag`: a rule changes the outcome (for example no replication, or a batch confounded with the condition); " +
64
+ "the skeleton step carries `status: flagged`, the rule id in its reason, and the flag message as its first caveat; obey the flag `outcome`. " +
65
+ "`dropped` lists the steps the procedure removed, because a flag removed inference or because the script of the test runs no such step (the shrinkage of limma and edgeR); do not plan them. " +
66
+ "`unrealized` lists the steps whose method has no vetted script template for this design; the skeleton step keeps the method and the claims, its caveat names the gap, " +
67
+ "and the plan states that the step runs without a vetted template instead of an invented one. " +
68
+ "`match: none`: no rule covers the central step; plan that step from your own knowledge with `status: ungrounded` and the reason, and keep the skeleton steps the answer holds. " +
69
+ "`match: unavailable`: the service did not answer; plan as usual and set `status: ungrounded`. " +
70
+ "`match: rejected`: a field was invalid; the answer names the field and the permitted values, so correct the call once. " +
71
+ "A step with `disputed` sides: choose one side and state the choice in the step. " +
72
+ "The `alternatives` of a step are also vetted methods; `forbids` names the method ids the rules forbid for the step, and a forbidden method stays forbidden. " +
73
+ "A caveat is one of: a warn flag; a parameter conflict between two rules (the step omits that parameter, so state the value you choose and both rule ids); " +
74
+ "a substitution (the template runs a substitute of the method of record, and the step method, package, and template are the substitute; the plan states it); " +
75
+ "a language limit (the requested language has no template that realizes the method for this design, or its template refuses a value of the rules; the step keeps the named template, and the plan states the limit). " +
76
+ "A step carries `environment` when the host bound the stores: `package.present` and its version from the packages installed in the sandbox, and `collection.present` with its path in the reference store. " +
77
+ "That is the environment answer; do not call the listing tools for a package or a collection the answer already reports. " +
78
+ "Call it once per situation; a second call with the same situation gives the same answer.",
79
+ inputSchema: SituationFieldsSchema,
80
+ describeCall: ({ question, n_per_group_min, n_per_group_max, batch }) => `${question}: ${n_per_group_min}-${n_per_group_max} per group, batch ${batch}`,
81
+ describeResult: (_input, result) => result.match === "applicable" ? `${result.plan_skeleton.length} steps, ${result.claims.length} claims` : result.match,
82
+ execute: async (input, ctx) => {
83
+ const answer = await deps.client.recommend(toSituation(input), "concise", toPreferences(input), deps.pin?.get());
84
+ if (answer.match === "unavailable" || answer.match === "rejected" || answer.match === "snapshot_mismatch")
85
+ return ok(answer);
86
+ deps.pin?.set(answer.snapshot.digest);
87
+ const farmLockFile = typeof deps.farmLockFile === "function" ? deps.farmLockFile(ctx.session.scope.analysisId) : deps.farmLockFile;
88
+ const joined = await joinEnvironment(answer, {
89
+ ...(farmLockFile ? { farmLockFile } : {}),
90
+ ...(deps.imagePackagesFile ? { imagePackagesFile: deps.imagePackagesFile } : {}),
91
+ ...(deps.refStorePath ? { refStorePath: deps.refStorePath } : {}),
92
+ });
93
+ const planner = toPlannerAnswer(joined);
94
+ deps.onAnswer?.(planner);
95
+ return ok(planner);
96
+ },
97
+ });
98
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * The situation fields as a flat tool input. Each `.describe()` is the whole
3
+ * of what the model knows about a field, thus the descriptions carry the
4
+ * definitions of the terms, and they tell the planner where in the Data
5
+ * Context each value comes from.
6
+ */
7
+ import { z } from "zod";
8
+ import type { KnowledgeSituation, KnowledgePreferences } from "./client.js";
9
+ export declare const SituationFieldsSchema: z.ZodObject<{
10
+ question: z.ZodEnum<{
11
+ enrichment: "enrichment";
12
+ differential_expression: "differential_expression";
13
+ qc: "qc";
14
+ full_plan: "full_plan";
15
+ tf_activity: "tf_activity";
16
+ deconvolution: "deconvolution";
17
+ coexpression: "coexpression";
18
+ clustering: "clustering";
19
+ survival: "survival";
20
+ signature_scoring: "signature_scoring";
21
+ }>;
22
+ modality: z.ZodLiteral<"bulk_rna_seq">;
23
+ data_state: z.ZodEnum<{
24
+ fastq: "fastq";
25
+ counts: "counts";
26
+ tpm_or_fpkm: "tpm_or_fpkm";
27
+ log_normalized: "log_normalized";
28
+ }>;
29
+ count_source: z.ZodOptional<z.ZodEnum<{
30
+ unknown: "unknown";
31
+ salmon: "salmon";
32
+ kallisto: "kallisto";
33
+ star_featurecounts: "star_featurecounts";
34
+ rsem: "rsem";
35
+ }>>;
36
+ import_state: z.ZodOptional<z.ZodEnum<{
37
+ unknown: "unknown";
38
+ quantifications: "quantifications";
39
+ estimated_counts_with_lengths: "estimated_counts_with_lengths";
40
+ corrected_counts: "corrected_counts";
41
+ integer_counts: "integer_counts";
42
+ }>>;
43
+ organism: z.ZodEnum<{
44
+ other: "other";
45
+ human: "human";
46
+ mouse: "mouse";
47
+ }>;
48
+ n_groups: z.ZodNumber;
49
+ n_per_group_min: z.ZodNumber;
50
+ n_per_group_max: z.ZodNumber;
51
+ n_samples: z.ZodOptional<z.ZodNumber>;
52
+ n_events: z.ZodOptional<z.ZodNumber>;
53
+ paired: z.ZodBoolean;
54
+ blocking_factor: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
55
+ other: "other";
56
+ individual: "individual";
57
+ cell_line: "cell_line";
58
+ litter: "litter";
59
+ site: "site";
60
+ pair: "pair";
61
+ }>>>;
62
+ block_structure: z.ZodOptional<z.ZodEnum<{
63
+ crossed: "crossed";
64
+ nested: "nested";
65
+ }>>;
66
+ batch: z.ZodEnum<{
67
+ none: "none";
68
+ known_balanced: "known_balanced";
69
+ known_confounded: "known_confounded";
70
+ known_unbalanced: "known_unbalanced";
71
+ suspected: "suspected";
72
+ }>;
73
+ covariates: z.ZodOptional<z.ZodArray<z.ZodEnum<{
74
+ other: "other";
75
+ technical: "technical";
76
+ sex: "sex";
77
+ age: "age";
78
+ clinical: "clinical";
79
+ }>>>;
80
+ n_timepoints: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
81
+ continuous_predictor: z.ZodOptional<z.ZodNullable<z.ZodEnum<{
82
+ other: "other";
83
+ score: "score";
84
+ time: "time";
85
+ age: "age";
86
+ dose: "dose";
87
+ }>>>;
88
+ technical_replicates: z.ZodOptional<z.ZodBoolean>;
89
+ sample_exclusions: z.ZodOptional<z.ZodBoolean>;
90
+ feature_level: z.ZodOptional<z.ZodEnum<{
91
+ gene: "gene";
92
+ exon: "exon";
93
+ transcript: "transcript";
94
+ }>>;
95
+ library_type: z.ZodOptional<z.ZodEnum<{
96
+ unknown: "unknown";
97
+ total: "total";
98
+ polyA: "polyA";
99
+ three_prime: "three_prime";
100
+ }>>;
101
+ strandedness: z.ZodOptional<z.ZodEnum<{
102
+ unknown: "unknown";
103
+ verified: "verified";
104
+ declared_unverified: "declared_unverified";
105
+ }>>;
106
+ interaction: z.ZodOptional<z.ZodBoolean>;
107
+ classifier: z.ZodOptional<z.ZodBoolean>;
108
+ extra_analyses: z.ZodOptional<z.ZodArray<z.ZodEnum<{
109
+ tf_activity: "tf_activity";
110
+ deconvolution: "deconvolution";
111
+ coexpression: "coexpression";
112
+ clustering: "clustering";
113
+ survival: "survival";
114
+ signature_scoring: "signature_scoring";
115
+ variance_partition: "variance_partition";
116
+ pathway_activity: "pathway_activity";
117
+ transcript_level: "transcript_level";
118
+ annotation: "annotation";
119
+ }>>>;
120
+ preferred_language: z.ZodOptional<z.ZodEnum<{
121
+ R: "R";
122
+ python: "python";
123
+ }>>;
124
+ enrichment_input: z.ZodOptional<z.ZodEnum<{
125
+ ranked_list: "ranked_list";
126
+ gene_list: "gene_list";
127
+ sample_scores: "sample_scores";
128
+ }>>;
129
+ quality_flags: z.ZodOptional<z.ZodArray<z.ZodEnum<{
130
+ low_depth_sample: "low_depth_sample";
131
+ outlier_sample: "outlier_sample";
132
+ sample_identity_doubt: "sample_identity_doubt";
133
+ high_duplication: "high_duplication";
134
+ degraded_rna: "degraded_rna";
135
+ }>>>;
136
+ }, z.core.$strip>;
137
+ export type SituationFields = z.infer<typeof SituationFieldsSchema>;
138
+ /** The situation as the client sends it: the fields as given, with an absent optional field omitted. */
139
+ export declare function toSituation(fields: SituationFields): KnowledgeSituation;
140
+ /** The preferences of the caller, beside the situation: the language of the template. */
141
+ export declare function toPreferences(fields: SituationFields): KnowledgePreferences | undefined;
142
+ /** One worked example, in the descriptions of the two planner tools. Examples raise complex-parameter accuracy. */
143
+ export declare const SITUATION_EXAMPLE: string;