@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
@@ -17,6 +17,7 @@ import { ok } from "neverthrow";
17
17
  import { z } from "zod";
18
18
  import { defineTool, withToolCallRecord } from "../define-tool.js";
19
19
  import { unwrapOrThrow } from "../../lib/result.js";
20
+ import { noteScriptChange, scriptSha256 } from "./decision-record.js";
20
21
  const EditFileInputSchema = z.object({
21
22
  path: z
22
23
  .string()
@@ -172,6 +173,19 @@ export function createEditFileTool(deps) {
172
173
  });
173
174
  if (write.status !== "ok")
174
175
  return ok(write);
176
+ // A rendered script carries a decision record. The edit is a change the curator did not vet, and the
177
+ // record keeps it with the digest after the change, thus an empty `unvetted_edits` stays a fact.
178
+ const noted = await noteScriptChange({
179
+ filesystem: deps.workspaceFilesystem,
180
+ mutator: deps.mutator,
181
+ session: ctx.session,
182
+ ...(deps.workingDir !== undefined ? { workingDir: deps.workingDir } : {}),
183
+ invocationId: ctx.invocationId,
184
+ toolName: "edit_file",
185
+ scriptPath: path,
186
+ note: `edit_file: ${replaced.replacements} replacement(s)`,
187
+ sha256: scriptSha256(replaced.content),
188
+ });
175
189
  const lines = replaced.lines;
176
190
  return ok(withToolCallRecord({
177
191
  status: "ok",
@@ -179,8 +193,12 @@ export function createEditFileTool(deps) {
179
193
  replacements: replaced.replacements,
180
194
  bytesWritten: write.bytesWritten,
181
195
  ...(lines === undefined ? {} : { lines }),
182
- }, write.record));
196
+ ...(noted.status === "noted" ? { decision_record_noted: true } : {}),
197
+ }, noted.status === "noted" ? [write.record, noted.write] : [write.record]));
198
+ },
199
+ foldCallRecord: (records) => {
200
+ for (const record of records)
201
+ deps.mutator.recordWrite(record);
183
202
  },
184
- foldCallRecord: (record) => deps.mutator.recordWrite(record),
185
203
  });
186
204
  }
@@ -18,12 +18,20 @@
18
18
  * `sandbox-provenance-tracking`), and artifact registration reconciles them.
19
19
  * The frame of each exec rides the call record, and the fold feeds it to the
20
20
  * lineage collector of the step.
21
+ *
22
+ * A script the command names that carries a decision record is compared
23
+ * with the digest the record expects before it runs. A difference is a
24
+ * change that no file tool recorded, and the record gets it with the digest
25
+ * of the bytes that run, thus the record connects the cited decision to the
26
+ * code that ran.
21
27
  */
22
28
  import { type WithToolCallRecord } from "../define-tool.js";
23
29
  import type { SandboxClient } from "../../sandbox/client.js";
24
30
  import type { ProvenanceFrame, SandboxRef } from "../../sandbox/types.js";
25
31
  import type { ProvenanceCollector } from "../../provenance/collector.js";
32
+ import type { WorkspaceMutator, WriteRecord } from "./mutator.js";
26
33
  import { type BoundedExecResult } from "./result-bounds.js";
34
+ import type { WorkspaceFilesystem } from "../../workspace/filesystem.js";
27
35
  import type { Logger } from "../../lib/logger.js";
28
36
  /** What one exec leaves for the lineage collector of the step. */
29
37
  interface ExecCallRecord {
@@ -31,6 +39,8 @@ interface ExecCallRecord {
31
39
  readonly exitCode: number | null;
32
40
  readonly durationMs: number | null;
33
41
  readonly provenance?: ProvenanceFrame;
42
+ /** The decision-record writes of the script check before the exec, for the mutator. */
43
+ readonly recordWrites?: readonly WriteRecord[];
34
44
  }
35
45
  type ExecuteCommandResult = {
36
46
  readonly status: "ok";
@@ -58,6 +68,13 @@ export interface ExecuteCommandDeps {
58
68
  readonly lineageCollector?: ProvenanceCollector;
59
69
  /** Analysis resource mount root (`/{resourceId}`) — strips frame paths to relative. */
60
70
  readonly mountRoot?: string;
71
+ /**
72
+ * The workspace filesystem and the mutator of the step, for the record check of a
73
+ * script the command runs. Both absent (a read-only agent), no record is
74
+ * read or written.
75
+ */
76
+ readonly workspaceFilesystem?: WorkspaceFilesystem;
77
+ readonly mutator?: WorkspaceMutator;
61
78
  }
62
79
  export declare function createExecuteCommandTool(deps: ExecuteCommandDeps): import("../define-tool.js").Tool<{
63
80
  command: string[];
@@ -18,12 +18,19 @@
18
18
  * `sandbox-provenance-tracking`), and artifact registration reconciles them.
19
19
  * The frame of each exec rides the call record, and the fold feeds it to the
20
20
  * lineage collector of the step.
21
+ *
22
+ * A script the command names that carries a decision record is compared
23
+ * with the digest the record expects before it runs. A difference is a
24
+ * change that no file tool recorded, and the record gets it with the digest
25
+ * of the bytes that run, thus the record connects the cited decision to the
26
+ * code that ran.
21
27
  */
22
28
  import { posix as posixPath } from "node:path";
23
29
  import { ok } from "neverthrow";
24
30
  import { z } from "zod";
25
31
  import { defineTool, withToolCallRecord } from "../define-tool.js";
26
32
  import { feedExecFrame } from "../../provenance/exec-frame.js";
33
+ import { reconcileScriptRecord } from "./decision-record.js";
27
34
  import { EXEC_STREAM_BYTE_CAP, boundExecResult } from "./result-bounds.js";
28
35
  import { runSandboxExec } from "./run-exec.js";
29
36
  import { createNoopLogger } from "../../lib/console-logger.js";
@@ -86,6 +93,23 @@ export function createExecuteCommandTool(deps) {
86
93
  describeCall: ({ command }) => scriptToken(command) ?? command.join(" "),
87
94
  execute: async ({ command, cwd, env, timeoutSeconds }, ctx) => {
88
95
  const effectiveCwd = cwd === undefined ? defaultCwd : cwd.startsWith("/") ? cwd : posixPath.join(defaultCwd, cwd);
96
+ // Each script the argv names, as the sandbox resolves it. A relative token resolves against the cwd of the
97
+ // command, which is a `/{analysisId}/...` path, thus the workspace filesystem resolves it the same way.
98
+ const recordWrites = [];
99
+ if (deps.workspaceFilesystem && deps.mutator) {
100
+ for (const token of command.filter((arg) => SCRIPT_EXTENSIONS.test(arg))) {
101
+ const noted = await reconcileScriptRecord({
102
+ filesystem: deps.workspaceFilesystem,
103
+ mutator: deps.mutator,
104
+ session: ctx.session,
105
+ invocationId: ctx.invocationId,
106
+ toolName: "execute_command",
107
+ scriptPath: token.startsWith("/") ? token : posixPath.join(effectiveCwd, token),
108
+ });
109
+ if (noted.status === "noted")
110
+ recordWrites.push(noted.write);
111
+ }
112
+ }
89
113
  const result = await runSandboxExec({
90
114
  sandboxClient,
91
115
  sandbox,
@@ -97,18 +121,21 @@ export function createExecuteCommandTool(deps) {
97
121
  emit: ctx.emit,
98
122
  });
99
123
  const bounded = { status: "ok", ...boundExecResult(result) };
100
- if (!lineageCollector || !mountRoot)
124
+ if ((!lineageCollector || !mountRoot) && recordWrites.length === 0)
101
125
  return ok(bounded);
102
126
  const record = {
103
127
  command,
104
128
  exitCode: result.exitCode,
105
129
  durationMs: result.durationMs,
106
130
  ...(result.provenance ? { provenance: result.provenance } : {}),
131
+ ...(recordWrites.length > 0 ? { recordWrites } : {}),
107
132
  };
108
133
  return ok(withToolCallRecord(bounded, record));
109
134
  },
110
135
  // Best-effort: a collector failure must never fail the exec.
111
136
  foldCallRecord: (record) => {
137
+ for (const write of record.recordWrites ?? [])
138
+ deps.mutator?.recordWrite(write);
112
139
  if (!lineageCollector || !mountRoot)
113
140
  return;
114
141
  try {
@@ -21,5 +21,6 @@ export * from "./mutator.js";
21
21
  export * from "./execute-command.js";
22
22
  export * from "./write-file.js";
23
23
  export * from "./edit-file.js";
24
+ export * from "./decision-record.js";
24
25
  export { EXEC_STREAM_BYTE_CAP, boundExecResult } from "./result-bounds.js";
25
26
  export type { BoundedExecResult } from "./result-bounds.js";
@@ -23,4 +23,5 @@ export * from "./mutator.js";
23
23
  export * from "./execute-command.js";
24
24
  export * from "./write-file.js";
25
25
  export * from "./edit-file.js";
26
+ export * from "./decision-record.js";
26
27
  export { EXEC_STREAM_BYTE_CAP, boundExecResult } from "./result-bounds.js";
@@ -80,7 +80,7 @@ export type WriteOutcome = {
80
80
  * (`inflexa:tool` in the signed document) — the seam records the write without
81
81
  * inspecting its caller.
82
82
  */
83
- export type MutateToolName = "write_file" | "edit_file";
83
+ export type MutateToolName = "write_file" | "edit_file" | "knowledge_template" | "execute_command";
84
84
  export interface WorkspaceMutatorDeps {
85
85
  /** Absolute host root of this analysis's workspace tree — used by the resolver. */
86
86
  readonly workspaceRoot: string;
@@ -59,12 +59,16 @@
59
59
  * 5. `synthesizeFindings` (single sequential block, no sandbox)
60
60
  * 6. `collectAndComplete` (terminal — runs on ALL paths)
61
61
  * - determine final status from completed/cancelled/failed counts
62
- * - update `cortex_runs.status` + `error`, and inside that same step record
63
- * the run outcome metric plus the outcome of every settled step whose
64
- * child never wrote its own terminal row (a cancelled child, or one that
65
- * died before `failStep`)
66
- * - close running charge with matching reason
67
- * - revoke run authorization via the injected `runAuthorizer` seam
62
+ * - update `cortex_runs.status` + `error`. When the write changes the row,
63
+ * the same step emits `run_completed` before the commit, records the run
64
+ * outcome metric plus the outcome of every settled step whose child never
65
+ * wrote its own terminal row (a cancelled child, or one that died before
66
+ * `failStep`), closes the running charge with the matching outcome, and
67
+ * revokes the run authorization through the injected `runAuthorizer`
68
+ * - the write retries with backoff on a failed transaction. When the write is
69
+ * refused, or fails on each attempt, close and revoke in their own steps, then
70
+ * emit `run_completed`. After a failed write the row stays `running`, so the
71
+ * body records its run end as a workflow event for the run canceler
68
72
  * - emit terminal stream part
69
73
  * - WRITES NOTHING to the conversation thread (results are pull-only
70
74
  * via `inspectRun`)
@@ -82,6 +86,8 @@ import type { WorkingMemoryStore } from "../memory/working-memory.js";
82
86
  import type { AnalysisStep } from "../schemas/workflow-state.js";
83
87
  import type { ChatProvider, EmbeddingProvider } from "../providers/types.js";
84
88
  import type { BioToolKeys } from "../tools/bio/keys.js";
89
+ import type { KnowledgeClient } from "../tools/knowledge/client.js";
90
+ import type { TemplateBinding } from "../tools/knowledge/template.js";
85
91
  import type { ProvenanceSeam } from "../provenance/seam.js";
86
92
  import type { ToolOutputStore } from "../loop/tool-output.js";
87
93
  import type { RunCharge } from "../billing/run-charge.js";
@@ -256,16 +262,27 @@ export interface ExecuteAnalysisDeps {
256
262
  * carries the goal and the constraints as they are at the dispatch of that step.
257
263
  */
258
264
  readonly workingMemory: WorkingMemoryStore;
265
+ /**
266
+ * The knowledge client of the composition, when the embedder binds one.
267
+ * The seed composition reads the contract of the template a grounded step
268
+ * renders through it, and binds the plan settings to that contract.
269
+ * Absent, the seed says the contract was not retrieved, and the step agent
270
+ * learns the slots from the render answers alone.
271
+ */
272
+ readonly knowledge?: KnowledgeClient;
259
273
  /**
260
274
  * The provenance seam of the composition. The body reads its run emit member,
261
275
  * and it reads no other member.
262
276
  *
263
- * The emit is deliberately invoked directly in the workflow body (NOT wrapped
264
- * in `DBOS.runStep`) so that body re-execution on DBOS recovery re-fires it —
265
- * a cached step would suppress the recovery re-emission, defeating the
266
- * point. Idempotency is the consumer's job, achieved via the deterministic
267
- * identifiers the events carry, not via step caching. Every call site is
268
- * guarded so a throwing observer never fails the run.
277
+ * `run_started` and `step_completed` are emitted directly in the workflow body
278
+ * (NOT wrapped in `DBOS.runStep`) so that body re-execution on DBOS recovery
279
+ * re-fires them — a cached step would suppress the recovery re-emission.
280
+ * `run_completed` is emitted by the won `persist-final-status` step, or by the
281
+ * body after that step when the step did not emit in this execution (a refused
282
+ * or failed write, or a won step served from the DBOS cache), so a recovery
283
+ * replay still fires it. Idempotency is the consumer's job, achieved via the
284
+ * deterministic identifiers the events carry, not via step caching. Every call
285
+ * site is guarded so a throwing observer never fails the run.
269
286
  *
270
287
  * Events arrive with the run's `RunSession` from durable workflow input — a
271
288
  * persisting realization needs its credential, scope, and identity.
@@ -307,6 +324,7 @@ export declare function buildChildInput(args: {
307
324
  runId: string;
308
325
  workflowId: string;
309
326
  prompt: string;
327
+ templateBinding?: TemplateBinding;
310
328
  }): Omit<SandboxStepInput, "runSession">;
311
329
  /**
312
330
  * Compose one step's seed — the user-content message its agent opens on.
@@ -317,17 +335,26 @@ export declare function buildChildInput(args: {
317
335
  * rendered any earlier (at plan submission, say) cannot mention them, and the
318
336
  * agent is left to rediscover its own upstream.
319
337
  *
320
- * REPLAY: the caller wraps this in a `DBOS.runStep`, so the composed string is
321
- * checkpointed and a replay returns it verbatim — the DB and disk are not read
322
- * again and cannot drift the seed under a recovered run. Never call it
323
- * unwrapped from the workflow body.
338
+ * The template contract of a grounded step is read with the package, and the
339
+ * plan settings are bound to it: the seed describes the binding, and the child
340
+ * input carries it, from one checkpoint. The child fetches nothing.
341
+ *
342
+ * REPLAY: the caller wraps this in a `DBOS.runStep`, so the composed seed is
343
+ * checkpointed and a replay returns it verbatim — the DB, the disk, and the
344
+ * knowledge service are not read again and cannot drift the seed under a
345
+ * recovered run. Never call it unwrapped from the workflow body.
324
346
  */
325
347
  export declare function composeStepSeed(args: {
326
348
  input: ExecuteAnalysisInput;
327
349
  stepId: string;
328
350
  runId: string;
329
351
  deps: ExecuteAnalysisDeps;
330
- }): Promise<string>;
352
+ }): Promise<StepSeed>;
353
+ /** What `gatherStepPackage` gives: the data of the seed, and the binding of the plan settings when one holds. */
354
+ export interface StepPackage {
355
+ readonly briefing: StepBriefing;
356
+ readonly templateBinding?: TemplateBinding;
357
+ }
331
358
  /**
332
359
  * Do every read of one step's package and give the data its seed is rendered
333
360
  * from. All reads live here, so `composeStepBriefing` stays a pure renderer.
@@ -340,6 +367,9 @@ export declare function composeStepSeed(args: {
340
367
  * so an edit during a run reaches each later step, and two steps of one run
341
368
  * can carry different copies.
342
369
  *
370
+ * The contract of the template a grounded step renders is read here too, from
371
+ * the knowledge client, and the plan settings are bound to it.
372
+ *
343
373
  * A read failure throws (house rules): a swallowed error would be frozen into
344
374
  * the checkpointed seed and served to every replay of this step.
345
375
  */
@@ -348,7 +378,17 @@ export declare function gatherStepPackage(args: {
348
378
  stepId: string;
349
379
  runId: string;
350
380
  deps: ExecuteAnalysisDeps;
351
- }): Promise<StepBriefing>;
381
+ }): Promise<StepPackage>;
382
+ /**
383
+ * What `composeStepSeed` checkpoints: the seed, and the binding of the plan
384
+ * settings to the template of the step when the contract was retrieved and
385
+ * its served version is the version the plan names. JSON-serialisable, as a
386
+ * durable step result must be.
387
+ */
388
+ export interface StepSeed {
389
+ readonly prompt: string;
390
+ readonly templateBinding?: TemplateBinding;
391
+ }
352
392
  /**
353
393
  * Project a classified synthesis outcome onto the run-phase `synthesis` ledger
354
394
  * row's terminal update (see the run-synthesis-outcome spec). The blocker keeps