@inflexa-ai/harness 0.39.1 → 0.40.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/README.md +2 -3
  2. package/dist/agents/conversation-agent.d.ts +3 -0
  3. package/dist/agents/conversation-agent.js +13 -2
  4. package/dist/agents/report-session-agent.d.ts +3 -0
  5. package/dist/agents/report-session-agent.js +2 -0
  6. package/dist/agents/sandbox/cheminformatics-agent.js +0 -3
  7. package/dist/agents/sandbox/shared.d.ts +11 -2
  8. package/dist/agents/sandbox/shared.js +4 -0
  9. package/dist/app/chat-turn.d.ts +75 -12
  10. package/dist/app/chat-turn.js +149 -21
  11. package/dist/app/message-assembly.d.ts +16 -52
  12. package/dist/app/message-assembly.js +41 -68
  13. package/dist/app/run-activity.d.ts +4 -6
  14. package/dist/app/run-activity.js +7 -27
  15. package/dist/app/spawn-report-session.d.ts +6 -11
  16. package/dist/app/spawn-report-session.js +5 -6
  17. package/dist/app/synthesize-run.d.ts +3 -0
  18. package/dist/app/synthesize-run.js +1 -0
  19. package/dist/billing/usage-recorder.d.ts +3 -4
  20. package/dist/billing/usage-recorder.js +3 -4
  21. package/dist/contracts/chat-parts.d.ts +11 -1
  22. package/dist/contracts/chat-parts.js +1 -0
  23. package/dist/contracts/index.d.ts +1 -1
  24. package/dist/contracts/part-registry.js +1 -0
  25. package/dist/contracts/report-blocks.d.ts +8 -8
  26. package/dist/contracts/schemas/chat-parts.d.ts +32 -9
  27. package/dist/contracts/schemas/chat-parts.js +10 -0
  28. package/dist/execution/artifact-metadata.d.ts +26 -33
  29. package/dist/execution/artifact-metadata.js +44 -106
  30. package/dist/execution/artifact-registration.d.ts +12 -2
  31. package/dist/execution/artifact-registration.js +9 -4
  32. package/dist/execution/post-step-pipeline.d.ts +26 -22
  33. package/dist/execution/post-step-pipeline.js +30 -24
  34. package/dist/execution/run-synthesis.d.ts +3 -0
  35. package/dist/execution/run-synthesis.js +11 -1
  36. package/dist/execution/step-summary.d.ts +21 -30
  37. package/dist/execution/step-summary.js +30 -57
  38. package/dist/index.d.ts +11 -10
  39. package/dist/index.js +16 -35
  40. package/dist/lib/zod-issues.js +2 -4
  41. package/dist/loop/compaction.d.ts +20 -0
  42. package/dist/loop/compaction.js +2 -0
  43. package/dist/loop/continue-agent.d.ts +40 -0
  44. package/dist/loop/continue-agent.js +38 -0
  45. package/dist/loop/metrics.d.ts +30 -22
  46. package/dist/loop/metrics.js +51 -22
  47. package/dist/loop/run-agent.d.ts +111 -13
  48. package/dist/loop/run-agent.js +467 -220
  49. package/dist/loop/run-to-terminal.d.ts +16 -17
  50. package/dist/loop/run-to-terminal.js +24 -35
  51. package/dist/loop/tool-mask.d.ts +23 -0
  52. package/dist/loop/tool-mask.js +46 -0
  53. package/dist/loop/tool-output.d.ts +48 -0
  54. package/dist/loop/tool-output.js +79 -0
  55. package/dist/loop/types.d.ts +6 -3
  56. package/dist/memory/ai-sdk-message-storage.d.ts +49 -2
  57. package/dist/memory/ai-sdk-message-storage.js +78 -2
  58. package/dist/memory/conversation-display-recorder.d.ts +6 -0
  59. package/dist/memory/conversation-display-recorder.js +64 -22
  60. package/dist/memory/conversation-display-replay.d.ts +5 -14
  61. package/dist/memory/conversation-display-replay.js +66 -32
  62. package/dist/memory/conversation-display-storage.d.ts +2 -1
  63. package/dist/memory/conversation-display-storage.js +2 -1
  64. package/dist/memory/conversation-view.d.ts +53 -0
  65. package/dist/memory/conversation-view.js +130 -0
  66. package/dist/memory/count-tokens.d.ts +5 -7
  67. package/dist/memory/count-tokens.js +7 -9
  68. package/dist/memory/thread-history.d.ts +74 -102
  69. package/dist/memory/thread-history.js +181 -246
  70. package/dist/memory/thread-store.d.ts +3 -3
  71. package/dist/memory/thread-store.js +7 -2
  72. package/dist/memory/tool-call-integrity.d.ts +15 -21
  73. package/dist/memory/tool-call-integrity.js +31 -38
  74. package/dist/memory/working-memory.d.ts +2 -5
  75. package/dist/memory/working-memory.js +2 -5
  76. package/dist/prompts/SOUL.d.ts +2 -2
  77. package/dist/prompts/SOUL.js +2 -7
  78. package/dist/prompts/analogical-reasoner.d.ts +1 -1
  79. package/dist/prompts/analogical-reasoner.js +16 -32
  80. package/dist/prompts/briefing.js +1 -1
  81. package/dist/prompts/compaction.d.ts +4 -0
  82. package/dist/prompts/compaction.js +24 -0
  83. package/dist/prompts/conversation.d.ts +1 -1
  84. package/dist/prompts/conversation.js +26 -35
  85. package/dist/prompts/execute-analysis/step-summary.js +7 -5
  86. package/dist/prompts/literature-reviewer.d.ts +1 -1
  87. package/dist/prompts/literature-reviewer.js +29 -34
  88. package/dist/prompts/planner.js +15 -29
  89. package/dist/prompts/report-session.d.ts +1 -1
  90. package/dist/prompts/report-session.js +2 -33
  91. package/dist/prompts/sandbox/bulk-transcriptomics-agent.d.ts +1 -1
  92. package/dist/prompts/sandbox/bulk-transcriptomics-agent.js +1 -2
  93. package/dist/prompts/sandbox/cheminformatics-agent.d.ts +1 -1
  94. package/dist/prompts/sandbox/cheminformatics-agent.js +10 -29
  95. package/dist/prompts/sandbox/chromatin-agent.d.ts +1 -1
  96. package/dist/prompts/sandbox/chromatin-agent.js +1 -2
  97. package/dist/prompts/sandbox/data-profiler.js +6 -3
  98. package/dist/prompts/sandbox/dna-methylation-agent.d.ts +1 -1
  99. package/dist/prompts/sandbox/dna-methylation-agent.js +2 -7
  100. package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
  101. package/dist/prompts/sandbox/drug-repurposing-agent.js +20 -24
  102. package/dist/prompts/sandbox/enrichment-agent.d.ts +1 -1
  103. package/dist/prompts/sandbox/enrichment-agent.js +3 -2
  104. package/dist/prompts/sandbox/genomic-variant-agent.d.ts +1 -1
  105. package/dist/prompts/sandbox/genomic-variant-agent.js +0 -2
  106. package/dist/prompts/sandbox/immune-profiling-agent.d.ts +1 -1
  107. package/dist/prompts/sandbox/immune-profiling-agent.js +7 -14
  108. package/dist/prompts/sandbox/metabolomics-agent.d.ts +1 -1
  109. package/dist/prompts/sandbox/metabolomics-agent.js +0 -2
  110. package/dist/prompts/sandbox/microbiome-agent.d.ts +1 -1
  111. package/dist/prompts/sandbox/microbiome-agent.js +14 -21
  112. package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
  113. package/dist/prompts/sandbox/multi-omics-integration-agent.js +9 -19
  114. package/dist/prompts/sandbox/multimodal-sc-agent.d.ts +1 -1
  115. package/dist/prompts/sandbox/multimodal-sc-agent.js +4 -5
  116. package/dist/prompts/sandbox/network-agent.d.ts +1 -1
  117. package/dist/prompts/sandbox/network-agent.js +7 -8
  118. package/dist/prompts/sandbox/pkpd-clinical-response-agent.d.ts +1 -1
  119. package/dist/prompts/sandbox/pkpd-clinical-response-agent.js +19 -19
  120. package/dist/prompts/sandbox/proteomics-agent.d.ts +1 -1
  121. package/dist/prompts/sandbox/proteomics-agent.js +3 -4
  122. package/dist/prompts/sandbox/scientific-executor.d.ts +1 -1
  123. package/dist/prompts/sandbox/scientific-executor.js +7 -10
  124. package/dist/prompts/sandbox/single-cell-agent.d.ts +1 -1
  125. package/dist/prompts/sandbox/single-cell-agent.js +2 -4
  126. package/dist/prompts/sandbox/spatial-omics-agent.d.ts +1 -1
  127. package/dist/prompts/sandbox/spatial-omics-agent.js +3 -5
  128. package/dist/prompts/sandbox/statistical-modeling-agent.d.ts +1 -1
  129. package/dist/prompts/sandbox/statistical-modeling-agent.js +1 -1
  130. package/dist/prompts/sandbox/translational-safety-agent.d.ts +1 -1
  131. package/dist/prompts/sandbox/translational-safety-agent.js +10 -19
  132. package/dist/prompts/sandbox-standards.d.ts +7 -7
  133. package/dist/prompts/sandbox-standards.js +74 -76
  134. package/dist/prompts/synthesis-agent.d.ts +1 -1
  135. package/dist/prompts/synthesis-agent.js +16 -42
  136. package/dist/providers/ai-sdk.d.ts +39 -6
  137. package/dist/providers/ai-sdk.js +117 -18
  138. package/dist/providers/errors.d.ts +2 -0
  139. package/dist/providers/errors.js +5 -0
  140. package/dist/providers/prompt-cache.d.ts +43 -41
  141. package/dist/providers/prompt-cache.js +47 -41
  142. package/dist/providers/reasoning.d.ts +10 -21
  143. package/dist/providers/reasoning.js +10 -21
  144. package/dist/providers/types.d.ts +21 -5
  145. package/dist/report-model/authoring-grammar.d.ts +2 -2
  146. package/dist/report-model/pin-snapshot.js +4 -10
  147. package/dist/runtime/assemble.d.ts +9 -7
  148. package/dist/runtime/assemble.js +7 -3
  149. package/dist/sandbox/create-sandbox.d.ts +2 -2
  150. package/dist/sandbox/submit-exec.d.ts +1 -1
  151. package/dist/schemas/artifact-manifest.d.ts +4 -4
  152. package/dist/state/artifacts.d.ts +11 -9
  153. package/dist/state/artifacts.js +112 -93
  154. package/dist/state/data-profile.d.ts +2 -2
  155. package/dist/state/data-profile.js +2 -2
  156. package/dist/state/init.js +39 -4
  157. package/dist/state/purge-analysis.js +4 -0
  158. package/dist/state/tool-outputs.d.ts +4 -0
  159. package/dist/state/tool-outputs.js +30 -0
  160. package/dist/tasks/data-profile.d.ts +31 -10
  161. package/dist/tasks/data-profile.js +116 -95
  162. package/dist/tools/ad-hoc-router.d.ts +8 -0
  163. package/dist/tools/ad-hoc-router.js +21 -2
  164. package/dist/tools/display/show-user.d.ts +1 -1
  165. package/dist/tools/execute-analysis.d.ts +3 -0
  166. package/dist/tools/execute-analysis.js +9 -1
  167. package/dist/tools/read-tool-output.d.ts +7 -0
  168. package/dist/tools/read-tool-output.js +130 -0
  169. package/dist/tools/research/generate-analogy-report.d.ts +10 -26
  170. package/dist/tools/research/generate-analogy-report.js +76 -163
  171. package/dist/tools/research/generate-plan.d.ts +3 -0
  172. package/dist/tools/research/generate-plan.js +4 -1
  173. package/dist/tools/research/literature-reviewer.d.ts +3 -0
  174. package/dist/tools/research/literature-reviewer.js +3 -0
  175. package/dist/tools/sandbox/report-blocker.d.ts +18 -5
  176. package/dist/tools/sandbox/report-blocker.js +54 -6
  177. package/dist/tools/sandbox/submit-file-metadata.d.ts +30 -0
  178. package/dist/tools/sandbox/submit-file-metadata.js +67 -0
  179. package/dist/tools/start-report-session.d.ts +1 -3
  180. package/dist/tools/start-report-session.js +0 -2
  181. package/dist/tools/workspace/execute-command.js +7 -4
  182. package/dist/tools/workspace/result-bounds.d.ts +4 -4
  183. package/dist/tools/workspace/result-bounds.js +6 -5
  184. package/dist/tools/workspace/workspace-search.d.ts +1 -1
  185. package/dist/workflows/execute-analysis.d.ts +3 -0
  186. package/dist/workflows/execute-analysis.js +6 -3
  187. package/dist/workflows/sandbox-step.d.ts +30 -9
  188. package/dist/workflows/sandbox-step.js +37 -8
  189. package/package.json +9 -9
  190. package/dist/providers/anthropic.d.ts +0 -25
  191. package/dist/providers/anthropic.js +0 -28
@@ -1,66 +1,33 @@
1
1
  /**
2
- * Chat-turn message assembly.
2
+ * Chat-turn message assembly: the view of the latest compaction marker, the user message, and then the
3
+ * context records. A kind gets a new record only when the view holds none of that kind, or when the hash
4
+ * of its text differs. The root loop compacts the view past its budget.
3
5
  *
4
- * Builds the `messages` array `runAgent` receives for one conversation turn:
5
- *
6
- * [ ...loadRecent(threadId, budget) ← stable, cacheable prefix
7
- * {user: cortex_analysis_state.context}, ← tail
8
- * {user: runActivityContext}, ← tail
9
- * {user: render(workingMemory)}, ← tail, `conversation` thread only
10
- * {user: normalizeUnicode(redactSecrets(input))} ] ← tail
11
- *
12
- * `system + tools + history` is the cacheable prefix — it only extends
13
- * turn-to-turn. Run activity, working memory, and analysis context go in the
14
- * **tail** as `user` messages: they change every turn, so a system-message
15
- * placement would bust the Anthropic cache prefix.
16
- *
17
- * A `report` thread does not get the working-memory tail. The spawn copies the
18
- * working-memory render into the child transcript at the anchor. A live render
19
- * sees state past that anchor, and that breaks the knowledge cap of a report
20
- * session. The analysis context and the run activity stay on both thread types.
21
- *
22
- * The history window of a `report` thread also keeps the first turn. That turn
23
- * is the seed, and it is the one record of the brief and of the copied render.
24
- * The window evicts the oldest turn first, thus a long session would drop its
25
- * own charter and keep only its tools. A `conversation` thread evicts as before,
26
- * because its live tail carries the working memory on each turn.
27
- *
28
- * Sanitization (`redactSecrets`, `normalizeUnicode`) is applied **once**, to
29
- * the new user input only — never to history, assistant messages, tool
30
- * results, the analysis context, or the rendered working memory.
31
- *
32
- * The returned `userMessage` is the sanitized user input on its own — the
33
- * route persists `[userMessage, ...loop output]` via `appendTurn`, so the
34
- * tail injections (ephemeral, re-derived each turn) are never written to the
35
- * thread store.
6
+ * The view of a `report` thread keeps the first turn, because that turn is the seed: the one record of
7
+ * the brief and of the copied working memory.
36
8
  */
37
9
  import { createNoopLogger } from "../lib/console-logger.js";
38
10
  import { unwrapOrThrow } from "../lib/result.js";
39
- import { stripUnansweredToolCalls } from "../memory/tool-call-integrity.js";
11
+ import { contextRecordMessage, contextRecordOf } from "../memory/ai-sdk-message-storage.js";
12
+ import { answerUnansweredToolCalls } from "../memory/tool-call-integrity.js";
40
13
  import { normalizeUnicode, redactSecrets } from "../input-sanitization.js";
41
14
  /**
42
- * Token budget for the `loadRecent` history window. Sized to leave headroom
43
- * under the model context window for the system prompt, tool definitions,
44
- * the three tail messages, and the output budget.
45
- */
46
- export const DEFAULT_HISTORY_TOKEN_BUDGET = 120_000;
47
- /**
48
- * Assemble the message array for one chat turn. Async — it reads the history
49
- * window and renders working memory.
15
+ * Assemble the message array for one chat turn. Async — it reads the view of
16
+ * the thread and renders working memory.
50
17
  */
51
18
  export async function assembleMessages(args) {
52
- const budget = args.tokenBudget ?? DEFAULT_HISTORY_TOKEN_BUDGET;
53
- const history = unwrapOrThrow(await args.history.loadRecent(args.threadId, budget, { keepFirstTurn: args.threadType === "report" }));
19
+ const history = unwrapOrThrow(await args.history.loadRecent(args.threadId, { keepFirstTurn: args.threadType === "report" }));
54
20
  // Read-side safety net for the wire contract: every tool call carries a
55
21
  // result. The loop upholds it at every exit, but the store outlives any one
56
22
  // writer — a row an older build wrote, or a hand-edited database, can carry
57
23
  // an unanswered call, and the provider boundary then refuses the WHOLE
58
- // transcript on every turn: the thread is wedged forever. Stripping the
59
- // orphan here degrades one message instead. The warning is the only account
60
- // a reader gets that the stored thread and the assembled one differ.
61
- const repaired = stripUnansweredToolCalls(history);
24
+ // transcript on every turn: the thread is wedged forever. The fix inserts the
25
+ // same not-run result the loop gives, in place, so no stored message changes
26
+ // and the prefix stays identical across turns. The warning is the only
27
+ // account a reader gets that the stored thread and the assembled one differ.
28
+ const repaired = answerUnansweredToolCalls(history);
62
29
  if (repaired.length > 0) {
63
- (args.logger ?? createNoopLogger()).named("assembly").warn("unanswered tool calls stripped from thread history", {
30
+ (args.logger ?? createNoopLogger()).named("assembly").warn("unanswered tool calls answered in thread history", {
64
31
  threadId: args.threadId,
65
32
  toolCallIds: repaired.map((d) => d.toolCallId),
66
33
  tools: repaired.map((d) => d.toolName),
@@ -71,27 +38,33 @@ export async function assembleMessages(args) {
71
38
  role: "user",
72
39
  content: normalizeUnicode(redactSecrets(args.userInput)),
73
40
  };
74
- const tail = [];
75
- // Analysis context — platform-supplied, trusted. Injected only when present.
41
+ const contextRecords = await contextRecordsFor(args, history);
42
+ return { messages: [...history, userMessage, ...contextRecords], userMessage, contextRecords };
43
+ }
44
+ /** The context records of one turn, in the order analysis context, run activity, working memory. */
45
+ export async function contextRecordsFor(args, history) {
46
+ const texts = [];
76
47
  if (args.analysisContext && args.analysisContext.trim().length > 0) {
77
- tail.push({
78
- role: "user",
79
- content: `[Analysis Context]\n${args.analysisContext}`,
80
- });
48
+ texts.push(["analysis-context", `[Analysis Context]\n${args.analysisContext}`]);
81
49
  }
82
- tail.push({
83
- role: "user",
84
- content: args.runActivityContext,
85
- });
86
- // Working memory — agent-authored and trusted. A `conversation` thread
87
- // always gets it, because the render names each section even when it is
88
- // empty. A `report` thread reads the frozen copy in its seed message.
50
+ texts.push(["run-activity", args.runActivityContext]);
51
+ // A `report` thread reads the frozen copy in its seed, because a live render sees state past its anchor.
89
52
  if (args.threadType !== "report") {
90
- tail.push({
91
- role: "user",
92
- content: unwrapOrThrow(await args.workingMemory.render(args.analysisId)),
93
- });
53
+ const render = unwrapOrThrow(await args.workingMemory.render(args.analysisId));
54
+ // An emptied memory needs a record of its own, or the last full copy would read as the current state.
55
+ texts.push(["working-memory", `[Working Memory]\n${render.length > 0 ? render : "The working memory is empty."}`]);
56
+ }
57
+ const latestHash = new Map();
58
+ for (const message of history) {
59
+ const record = contextRecordOf(message);
60
+ if (record !== undefined)
61
+ latestHash.set(record.kind, record.hash);
62
+ }
63
+ const records = [];
64
+ for (const [kind, text] of texts) {
65
+ const record = contextRecordMessage(kind, text);
66
+ if (contextRecordOf(record)?.hash !== latestHash.get(kind))
67
+ records.push(record);
94
68
  }
95
- tail.push(userMessage);
96
- return { messages: [...history, ...tail], userMessage };
69
+ return records;
97
70
  }
@@ -1,13 +1,11 @@
1
1
  /**
2
- * Ephemeral conversation-tail projection of non-terminal analysis runs.
3
- *
4
- * This is derived operational state, not memory: callers render it afresh for
5
- * each turn and never persist it to the thread.
2
+ * The Run Activity record of a chat turn. The render holds no time of the call, thus a set of runs
3
+ * that does not change gives byte-identical text, and the turn stores no new record.
6
4
  */
7
5
  import type { RunPage } from "../state/runs.js";
8
6
  /** Maximum detailed run rows paid into every conversation turn. */
9
7
  export declare const RUN_ACTIVITY_DETAIL_LIMIT = 20;
10
- /** Render the bounded run-activity snapshot injected into a conversation tail. */
11
- export declare function renderRunActivity(activity: RunPage, nowMs?: number): string;
8
+ /** Render the bounded run-activity snapshot of a conversation turn. */
9
+ export declare function renderRunActivity(activity: RunPage): string;
12
10
  /** Render an honest degraded state when the activity projection cannot be read. */
13
11
  export declare function renderRunActivityUnavailable(): string;
@@ -1,36 +1,16 @@
1
1
  /**
2
- * Ephemeral conversation-tail projection of non-terminal analysis runs.
3
- *
4
- * This is derived operational state, not memory: callers render it afresh for
5
- * each turn and never persist it to the thread.
2
+ * The Run Activity record of a chat turn. The render holds no time of the call, thus a set of runs
3
+ * that does not change gives byte-identical text, and the turn stores no new record.
6
4
  */
7
5
  /** Maximum detailed run rows paid into every conversation turn. */
8
6
  export const RUN_ACTIVITY_DETAIL_LIMIT = 20;
9
- function compactAge(startedAt, nowMs) {
10
- const startedMs = Date.parse(startedAt);
11
- if (!Number.isFinite(startedMs))
12
- return "unknown age";
13
- const elapsedSeconds = Math.max(0, Math.floor((nowMs - startedMs) / 1_000));
14
- if (elapsedSeconds < 60)
15
- return `${elapsedSeconds}s ago`;
16
- const elapsedMinutes = Math.floor(elapsedSeconds / 60);
17
- if (elapsedMinutes < 60)
18
- return `${elapsedMinutes}m ago`;
19
- const elapsedHours = Math.floor(elapsedMinutes / 60);
20
- if (elapsedHours < 24)
21
- return `${elapsedHours}h ago`;
22
- return `${Math.floor(elapsedHours / 24)}d ago`;
23
- }
24
- function renderRows(label, rows, nowMs) {
7
+ function renderRows(label, rows) {
25
8
  if (rows.length === 0)
26
9
  return [];
27
- return [
28
- `${label}:`,
29
- ...rows.map((run) => `- runId: ${run.runId} | planId: ${run.planId ?? "none"} | startedAt: ${run.startedAt} | started: ${compactAge(run.startedAt, nowMs)}`),
30
- ];
10
+ return [`${label}:`, ...rows.map((run) => `- runId: ${run.runId} | planId: ${run.planId ?? "none"} | startedAt: ${run.startedAt}`)];
31
11
  }
32
- /** Render the bounded run-activity snapshot injected into a conversation tail. */
33
- export function renderRunActivity(activity, nowMs = Date.now()) {
12
+ /** Render the bounded run-activity snapshot of a conversation turn. */
13
+ export function renderRunActivity(activity) {
34
14
  const lines = ["[Run Activity]"];
35
15
  if (activity.total === 0) {
36
16
  lines.push("No runs are currently running or suspended.");
@@ -38,7 +18,7 @@ export function renderRunActivity(activity, nowMs = Date.now()) {
38
18
  }
39
19
  const running = activity.runs.filter((run) => run.status === "running");
40
20
  const suspended = activity.runs.filter((run) => run.status === "suspended_insufficient_funds");
41
- lines.push(...renderRows("Running", running, nowMs), ...renderRows("Suspended", suspended, nowMs));
21
+ lines.push(...renderRows("Running", running), ...renderRows("Suspended", suspended));
42
22
  const omitted = activity.total - activity.runs.length;
43
23
  if (omitted > 0) {
44
24
  lines.push(`Showing ${activity.runs.length} of ${activity.total} non-terminal runs; ${omitted} omitted. Use inspect_run to page the full list.`);
@@ -77,11 +77,10 @@ import type { EnsureSessionStateResult } from "./report-session-runtime.js";
77
77
  * The set is closed. `parent_not_found` covers an absent parent and an archived
78
78
  * one alike, because `getThread` filters the tombstone and a spawn into hidden
79
79
  * state is not permitted. `parent_not_a_conversation` keeps the tree flat: a
80
- * report session cannot spawn another. `empty_parent_transcript` refuses a
81
- * report on a parent that holds no messages, because such a report reports
82
- * nothing. `no_browser` refuses every spawn under a composition that gives no
83
- * route to a look: no eyes seam, no capture seam, and no configured browser
84
- * endpoint. It carries the line that explains the absent capability.
80
+ * report session cannot spawn another. `no_browser` refuses every spawn under
81
+ * a composition that gives no route to a look: no eyes seam, no capture seam,
82
+ * and no configured browser endpoint. It carries the line that explains the
83
+ * absent capability.
85
84
  */
86
85
  export type SpawnRefusal = {
87
86
  readonly type: "no_browser";
@@ -97,10 +96,6 @@ export type SpawnRefusal = {
97
96
  readonly op: string;
98
97
  readonly parentThreadId: string;
99
98
  readonly threadType: ThreadType;
100
- } | {
101
- readonly type: "empty_parent_transcript";
102
- readonly op: string;
103
- readonly parentThreadId: string;
104
99
  };
105
100
  /** The paging inputs `listReportSessions` forwards to the thread listing. */
106
101
  export interface ReportSessionPaging {
@@ -237,8 +232,8 @@ export interface ReportSessionSpawn {
237
232
  * result on the ok channel, thus the outcome vocabulary holds no pin arm.
238
233
  *
239
234
  * Refused with a `SpawnRefusal`, no row written: a composition with no
240
- * eyes, an absent or archived parent, a parent that is not a conversation,
241
- * and a parent with no messages. A store refusal (`DbError`,
235
+ * eyes, an absent or archived parent, and a parent that is not a
236
+ * conversation. A store refusal (`DbError`,
242
237
  * `ThreadInputError`) passes through unchanged. A failed seed write purges
243
238
  * the child and returns the fault, thus no context-less thread survives.
244
239
  */
@@ -264,12 +264,11 @@ export function createReportSessionSpawn(deps) {
264
264
  if (parent.threadType !== "conversation") {
265
265
  return errAsync({ type: "parent_not_a_conversation", op: OP, parentThreadId, threadType: parent.threadType });
266
266
  }
267
- return history.latestSeq(parentThreadId).andThen((anchor) => {
268
- // `null` is a parent with no messages. A report on an empty transcript
269
- // reports nothing, so the refusal is the correct answer.
270
- if (anchor === null) {
271
- return errAsync({ type: "empty_parent_transcript", op: OP, parentThreadId });
272
- }
267
+ return history.latestSeq(parentThreadId).andThen((latest) => {
268
+ // A turn appends after its loop, thus the first ask of a chat finds
269
+ // no row. The seed and the tail carry the context, not the parent
270
+ // transcript. `-1` is the point before seq 0.
271
+ const anchor = latest ?? -1;
273
272
  // `total`, not the page length: N counts every existing report child,
274
273
  // not the count on one page. Two concurrent spawns can compose one N.
275
274
  // The result is two titles a user renames, and no identifier collides.
@@ -17,6 +17,7 @@
17
17
  import type { Pool } from "pg";
18
18
  import type { SynthesisPhase } from "@inflexa-ai/harness/contracts/chat-parts.js";
19
19
  import type { RunSession } from "../auth/types.js";
20
+ import type { ToolOutputStore } from "../loop/tool-output.js";
20
21
  import type { EmitFn } from "../loop/types.js";
21
22
  import type { ChatProvider, EmbeddingProvider } from "../providers/types.js";
22
23
  import type { BioToolKeys } from "../tools/bio/keys.js";
@@ -43,6 +44,8 @@ export interface SynthesizeRunDeps {
43
44
  readonly citationResolver: CitationResolver;
44
45
  /** LLM usage-accounting seam for the synthesizer loop; omitted falls back to the no-op recorder. */
45
46
  readonly usageRecorder?: UsageRecorder;
47
+ /** The store of the synthesizer and reviewer loops and of their read tools. */
48
+ readonly toolOutputStore?: ToolOutputStore;
46
49
  }
47
50
  /** Optional fields for a progress update, mirroring the wire part. */
48
51
  export interface SynthesisProgressExtra {
@@ -49,6 +49,7 @@ export async function synthesizeRun(deps, params) {
49
49
  bioKeys,
50
50
  citationResolver,
51
51
  usageRecorder,
52
+ ...(deps.toolOutputStore ? { toolOutputStore: deps.toolOutputStore } : {}),
52
53
  summaries,
53
54
  planNarrative,
54
55
  runId,
@@ -6,10 +6,9 @@
6
6
  * telemetry every completed LLM call produces. The harness names no storage or
7
7
  * display technology — it emits records; the embedder decides where they land.
8
8
  *
9
- * The loop is the single delivery site (it sees every reply in both execution
10
- * modes and already holds the session), so a realization is wired once at the
11
- * composition root and reaches every `runAgent` invocation through the deps
12
- * bags. OSS default: `createNoopUsageRecorder` (`./noop-usage-recorder.ts`).
9
+ * Delivery sites: the loop's `accountForChatCall`, and the two direct
10
+ * `provider.chat` callers outside a loop — the ad hoc router and the analogy
11
+ * conversion. OSS default: `createNoopUsageRecorder` (`./noop-usage-recorder.ts`).
13
12
  */
14
13
  import type { ResultAsync } from "neverthrow";
15
14
  import type { Scope } from "../auth/types.js";
@@ -6,9 +6,8 @@
6
6
  * telemetry every completed LLM call produces. The harness names no storage or
7
7
  * display technology — it emits records; the embedder decides where they land.
8
8
  *
9
- * The loop is the single delivery site (it sees every reply in both execution
10
- * modes and already holds the session), so a realization is wired once at the
11
- * composition root and reaches every `runAgent` invocation through the deps
12
- * bags. OSS default: `createNoopUsageRecorder` (`./noop-usage-recorder.ts`).
9
+ * Delivery sites: the loop's `accountForChatCall`, and the two direct
10
+ * `provider.chat` callers outside a loop — the ad hoc router and the analogy
11
+ * conversion. OSS default: `createNoopUsageRecorder` (`./noop-usage-recorder.ts`).
13
12
  */
14
13
  export {};
@@ -6,6 +6,7 @@
6
6
  * Presentation: data-presentation (agent-synthesized content),
7
7
  * data-plan (plan by id), data-file-reference (files by path)
8
8
  * Approval: data-ask (a tool's pending user-approval prompt)
9
+ * Compaction: data-compaction (a compaction of the conversation thread)
9
10
  * Run lifecycle: run-started, dag-state, run-completed, run-failed, run-synthesis,
10
11
  * synthesis-progress
11
12
  * Per-step live: step-activity, step-file-tree
@@ -382,4 +383,13 @@ export interface ReportRenderedPart {
382
383
  /** The title of the rendered document. */
383
384
  title?: string;
384
385
  }
385
- export type CortexChatPart = PresentationPart | PlanPart | RunCardPart | FileReferencePart | AskPart | RunStartedPart | DagStatePart | StepActivityPart | StepFileTreePart | StepOutputPart | StepSummaryPart | StepUsagePart | StepBlockedPart | RunSynthesisPart | SynthesisProgressPart | RunCompletedPart | RunFailedPart | ChildSessionStartedPart | ReportRenderedPart;
386
+ /** One compaction of the thread: `running`, then `done` or `failed` under the same id. A reload shows it as a divider. */
387
+ export interface CompactionPart {
388
+ type: "data-compaction";
389
+ id: string;
390
+ status: "running" | "done" | "failed";
391
+ tokensBefore: number;
392
+ tokensAfter?: number;
393
+ durationMs?: number;
394
+ }
395
+ export type CortexChatPart = PresentationPart | PlanPart | RunCardPart | FileReferencePart | AskPart | RunStartedPart | DagStatePart | StepActivityPart | StepFileTreePart | StepOutputPart | StepSummaryPart | StepUsagePart | StepBlockedPart | RunSynthesisPart | SynthesisProgressPart | RunCompletedPart | RunFailedPart | ChildSessionStartedPart | ReportRenderedPart | CompactionPart;
@@ -6,6 +6,7 @@
6
6
  * Presentation: data-presentation (agent-synthesized content),
7
7
  * data-plan (plan by id), data-file-reference (files by path)
8
8
  * Approval: data-ask (a tool's pending user-approval prompt)
9
+ * Compaction: data-compaction (a compaction of the conversation thread)
9
10
  * Run lifecycle: run-started, dag-state, run-completed, run-failed, run-synthesis,
10
11
  * synthesis-progress
11
12
  * Per-step live: step-activity, step-file-tree
@@ -2,7 +2,7 @@ export { ORGAN_SYSTEMS, OrganSystemSchema, ORGAN_SYSTEM_LABELS, organSystemLabel
2
2
  export type { OrganSystem } from "./organ-system.js";
3
3
  export { SEVERITIES, SeveritySchema } from "./severity.js";
4
4
  export type { Severity } from "./severity.js";
5
- export type { PlanStep, PresentationContent, PresentationPart, PlanPart, RunCardPart, FileReferenceEntry, FileReferencePart, AskPart, RunStartedPart, StepStatus, DagStepState, DagStatePart, StepPhase, StepActivityPart, FileTreeEntry, StepFileTreePart, StepOutputFile, StepOutputPart, StepSummaryPart, StepUsagePart, StepBlockedPart, SynthesizedFinding, BiologicalTheme, RunSynthesisPart, SynthesisPhase, SynthesisProgressPart, RunCompletedFinding, RunCompletedPart, RunFailedPart, ChildSessionStartedPart, ReportRenderedPart, CortexChatPart, } from "./chat-parts.js";
5
+ export type { PlanStep, PresentationContent, PresentationPart, PlanPart, RunCardPart, FileReferenceEntry, FileReferencePart, AskPart, RunStartedPart, StepStatus, DagStepState, DagStatePart, StepPhase, StepActivityPart, FileTreeEntry, StepFileTreePart, StepOutputFile, StepOutputPart, StepSummaryPart, StepUsagePart, StepBlockedPart, SynthesizedFinding, BiologicalTheme, RunSynthesisPart, SynthesisPhase, SynthesisProgressPart, RunCompletedFinding, RunCompletedPart, RunFailedPart, ChildSessionStartedPart, ReportRenderedPart, CompactionPart, CortexChatPart, } from "./chat-parts.js";
6
6
  export type { EventSource, TextDeltaEvent, ToolStartedEvent, ToolFinishedEvent, FinishEvent, ChatErrorEvent, CortexChatEvent } from "./chat-events.js";
7
7
  export type { TokenUsageRollup } from "./usage.js";
8
8
  export { DATA_PROFILE_RUN_LITERAL } from "./data-profile.js";
@@ -18,6 +18,7 @@ export const PART_REGISTRY = {
18
18
  "data-run-failed": { emitter: "workflow", consumer: "sidebar", transient: false, reconciling: false },
19
19
  "data-child-session-started": { emitter: "conversation", consumer: "conversation", transient: false, reconciling: false },
20
20
  "data-report-rendered": { emitter: "conversation", consumer: "conversation", transient: false, reconciling: false },
21
+ "data-compaction": { emitter: "conversation", consumer: "conversation", transient: true, reconciling: true },
21
22
  };
22
23
  export function isTransient(type) {
23
24
  return PART_REGISTRY[type].transient;
@@ -200,15 +200,15 @@ export declare const ChartAxesSchema: z.ZodObject<{
200
200
  x: z.ZodOptional<z.ZodObject<{
201
201
  title: z.ZodOptional<z.ZodString>;
202
202
  scale: z.ZodOptional<z.ZodEnum<{
203
- linear: "linear";
204
203
  log: "log";
204
+ linear: "linear";
205
205
  }>>;
206
206
  }, z.core.$strict>>;
207
207
  y: z.ZodOptional<z.ZodObject<{
208
208
  title: z.ZodOptional<z.ZodString>;
209
209
  scale: z.ZodOptional<z.ZodEnum<{
210
- linear: "linear";
211
210
  log: "log";
211
+ linear: "linear";
212
212
  }>>;
213
213
  }, z.core.$strict>>;
214
214
  }, z.core.$strict>;
@@ -297,15 +297,15 @@ export declare const ChartCompositionSchema: z.ZodObject<{
297
297
  x: z.ZodOptional<z.ZodObject<{
298
298
  title: z.ZodOptional<z.ZodString>;
299
299
  scale: z.ZodOptional<z.ZodEnum<{
300
- linear: "linear";
301
300
  log: "log";
301
+ linear: "linear";
302
302
  }>>;
303
303
  }, z.core.$strict>>;
304
304
  y: z.ZodOptional<z.ZodObject<{
305
305
  title: z.ZodOptional<z.ZodString>;
306
306
  scale: z.ZodOptional<z.ZodEnum<{
307
- linear: "linear";
308
307
  log: "log";
308
+ linear: "linear";
309
309
  }>>;
310
310
  }, z.core.$strict>>;
311
311
  }, z.core.$strict>>;
@@ -724,15 +724,15 @@ export declare const ChartBlockSchema: z.ZodObject<{
724
724
  x: z.ZodOptional<z.ZodObject<{
725
725
  title: z.ZodOptional<z.ZodString>;
726
726
  scale: z.ZodOptional<z.ZodEnum<{
727
- linear: "linear";
728
727
  log: "log";
728
+ linear: "linear";
729
729
  }>>;
730
730
  }, z.core.$strict>>;
731
731
  y: z.ZodOptional<z.ZodObject<{
732
732
  title: z.ZodOptional<z.ZodString>;
733
733
  scale: z.ZodOptional<z.ZodEnum<{
734
- linear: "linear";
735
734
  log: "log";
735
+ linear: "linear";
736
736
  }>>;
737
737
  }, z.core.$strict>>;
738
738
  }, z.core.$strict>>;
@@ -1168,15 +1168,15 @@ export declare const ATOM_BLOCK_SCHEMAS: readonly [z.ZodObject<{
1168
1168
  x: z.ZodOptional<z.ZodObject<{
1169
1169
  title: z.ZodOptional<z.ZodString>;
1170
1170
  scale: z.ZodOptional<z.ZodEnum<{
1171
- linear: "linear";
1172
1171
  log: "log";
1172
+ linear: "linear";
1173
1173
  }>>;
1174
1174
  }, z.core.$strict>>;
1175
1175
  y: z.ZodOptional<z.ZodObject<{
1176
1176
  title: z.ZodOptional<z.ZodString>;
1177
1177
  scale: z.ZodOptional<z.ZodEnum<{
1178
- linear: "linear";
1179
1178
  log: "log";
1179
+ linear: "linear";
1180
1180
  }>>;
1181
1181
  }, z.core.$strict>>;
1182
1182
  }, z.core.$strict>>;
@@ -204,7 +204,6 @@ export declare const DagStatePartSchema: z.ZodObject<{
204
204
  export declare const StepPhaseSchema: z.ZodEnum<{
205
205
  failed: "failed";
206
206
  complete: "complete";
207
- warning: "warning";
208
207
  "sandbox-init": "sandbox-init";
209
208
  executing: "executing";
210
209
  "generating-metadata": "generating-metadata";
@@ -212,6 +211,7 @@ export declare const StepPhaseSchema: z.ZodEnum<{
212
211
  indexing: "indexing";
213
212
  persisting: "persisting";
214
213
  retrying: "retrying";
214
+ warning: "warning";
215
215
  }>;
216
216
  export declare const StepActivityPartSchema: z.ZodObject<{
217
217
  type: z.ZodLiteral<"data-step-activity">;
@@ -221,7 +221,6 @@ export declare const StepActivityPartSchema: z.ZodObject<{
221
221
  phase: z.ZodEnum<{
222
222
  failed: "failed";
223
223
  complete: "complete";
224
- warning: "warning";
225
224
  "sandbox-init": "sandbox-init";
226
225
  executing: "executing";
227
226
  "generating-metadata": "generating-metadata";
@@ -229,6 +228,7 @@ export declare const StepActivityPartSchema: z.ZodObject<{
229
228
  indexing: "indexing";
230
229
  persisting: "persisting";
231
230
  retrying: "retrying";
231
+ warning: "warning";
232
232
  }>;
233
233
  activity: z.ZodString;
234
234
  }, z.core.$strip>;
@@ -260,9 +260,9 @@ export declare const StepOutputFileSchema: z.ZodObject<{
260
260
  fileType: z.ZodEnum<{
261
261
  output: "output";
262
262
  summary: "summary";
263
- log: "log";
264
- figure: "figure";
265
263
  script: "script";
264
+ figure: "figure";
265
+ log: "log";
266
266
  notebook: "notebook";
267
267
  }>;
268
268
  description: z.ZodString;
@@ -279,9 +279,9 @@ export declare const StepOutputPartSchema: z.ZodObject<{
279
279
  fileType: z.ZodEnum<{
280
280
  output: "output";
281
281
  summary: "summary";
282
- log: "log";
283
- figure: "figure";
284
282
  script: "script";
283
+ figure: "figure";
284
+ log: "log";
285
285
  notebook: "notebook";
286
286
  }>;
287
287
  description: z.ZodString;
@@ -473,6 +473,18 @@ export declare const ReportRenderedPartSchema: z.ZodObject<{
473
473
  renderedAt: z.ZodString;
474
474
  title: z.ZodOptional<z.ZodString>;
475
475
  }, z.core.$strip>;
476
+ export declare const CompactionPartSchema: z.ZodObject<{
477
+ type: z.ZodLiteral<"data-compaction">;
478
+ id: z.ZodString;
479
+ status: z.ZodEnum<{
480
+ failed: "failed";
481
+ done: "done";
482
+ running: "running";
483
+ }>;
484
+ tokensBefore: z.ZodNumber;
485
+ tokensAfter: z.ZodOptional<z.ZodNumber>;
486
+ durationMs: z.ZodOptional<z.ZodNumber>;
487
+ }, z.core.$strip>;
476
488
  export declare const CortexChatPartSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
477
489
  type: z.ZodLiteral<"data-presentation">;
478
490
  id: z.ZodString;
@@ -592,7 +604,6 @@ export declare const CortexChatPartSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
592
604
  phase: z.ZodEnum<{
593
605
  failed: "failed";
594
606
  complete: "complete";
595
- warning: "warning";
596
607
  "sandbox-init": "sandbox-init";
597
608
  executing: "executing";
598
609
  "generating-metadata": "generating-metadata";
@@ -600,6 +611,7 @@ export declare const CortexChatPartSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
600
611
  indexing: "indexing";
601
612
  persisting: "persisting";
602
613
  retrying: "retrying";
614
+ warning: "warning";
603
615
  }>;
604
616
  activity: z.ZodString;
605
617
  }, z.core.$strip>, z.ZodObject<{
@@ -627,9 +639,9 @@ export declare const CortexChatPartSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
627
639
  fileType: z.ZodEnum<{
628
640
  output: "output";
629
641
  summary: "summary";
630
- log: "log";
631
- figure: "figure";
632
642
  script: "script";
643
+ figure: "figure";
644
+ log: "log";
633
645
  notebook: "notebook";
634
646
  }>;
635
647
  description: z.ZodString;
@@ -765,4 +777,15 @@ export declare const CortexChatPartSchema: z.ZodDiscriminatedUnion<[z.ZodObject<
765
777
  id: z.ZodString;
766
778
  renderedAt: z.ZodString;
767
779
  title: z.ZodOptional<z.ZodString>;
780
+ }, z.core.$strip>, z.ZodObject<{
781
+ type: z.ZodLiteral<"data-compaction">;
782
+ id: z.ZodString;
783
+ status: z.ZodEnum<{
784
+ failed: "failed";
785
+ done: "done";
786
+ running: "running";
787
+ }>;
788
+ tokensBefore: z.ZodNumber;
789
+ tokensAfter: z.ZodOptional<z.ZodNumber>;
790
+ durationMs: z.ZodOptional<z.ZodNumber>;
768
791
  }, z.core.$strip>], "type">;
@@ -299,6 +299,15 @@ export const ReportRenderedPartSchema = z.object({
299
299
  renderedAt: z.string(),
300
300
  title: z.string().optional(),
301
301
  });
302
+ // ── Compaction ──────────────────────────────────────────────────────
303
+ export const CompactionPartSchema = z.object({
304
+ type: z.literal("data-compaction"),
305
+ id: z.string(),
306
+ status: z.enum(["running", "done", "failed"]),
307
+ tokensBefore: z.number().int().nonnegative(),
308
+ tokensAfter: z.number().int().nonnegative().optional(),
309
+ durationMs: z.number().int().nonnegative().optional(),
310
+ });
302
311
  // ── Union ───────────────────────────────────────────────────────────
303
312
  export const CortexChatPartSchema = z.discriminatedUnion("type", [
304
313
  PresentationPartSchema,
@@ -320,4 +329,5 @@ export const CortexChatPartSchema = z.discriminatedUnion("type", [
320
329
  RunFailedPartSchema,
321
330
  ChildSessionStartedPartSchema,
322
331
  ReportRenderedPartSchema,
332
+ CompactionPartSchema,
323
333
  ]);