@inflexa-ai/harness 0.39.2 → 0.41.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.
- package/README.md +2 -3
- package/dist/agents/conversation-agent.d.ts +3 -0
- package/dist/agents/conversation-agent.js +13 -2
- package/dist/agents/report-session-agent.d.ts +3 -0
- package/dist/agents/report-session-agent.js +2 -0
- package/dist/agents/sandbox/cheminformatics-agent.js +0 -3
- package/dist/agents/sandbox/shared.d.ts +11 -2
- package/dist/agents/sandbox/shared.js +4 -0
- package/dist/app/chat-turn.d.ts +104 -12
- package/dist/app/chat-turn.js +180 -21
- package/dist/app/message-assembly.d.ts +16 -52
- package/dist/app/message-assembly.js +41 -68
- package/dist/app/run-activity.d.ts +4 -6
- package/dist/app/run-activity.js +7 -27
- package/dist/app/spawn-report-session.d.ts +6 -11
- package/dist/app/spawn-report-session.js +5 -6
- package/dist/app/synthesize-run.d.ts +3 -0
- package/dist/app/synthesize-run.js +1 -0
- package/dist/billing/usage-recorder.d.ts +3 -4
- package/dist/billing/usage-recorder.js +3 -4
- package/dist/contracts/chat-parts.d.ts +11 -1
- package/dist/contracts/chat-parts.js +1 -0
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/part-registry.js +1 -0
- package/dist/contracts/report-blocks.d.ts +8 -8
- package/dist/contracts/schemas/chat-parts.d.ts +32 -9
- package/dist/contracts/schemas/chat-parts.js +10 -0
- package/dist/execution/artifact-metadata.d.ts +26 -33
- package/dist/execution/artifact-metadata.js +44 -106
- package/dist/execution/post-step-pipeline.d.ts +23 -16
- package/dist/execution/post-step-pipeline.js +19 -21
- package/dist/execution/run-synthesis.d.ts +3 -0
- package/dist/execution/run-synthesis.js +11 -1
- package/dist/execution/step-summary.d.ts +21 -30
- package/dist/execution/step-summary.js +30 -57
- package/dist/index.d.ts +9 -8
- package/dist/index.js +15 -34
- package/dist/lib/zod-issues.js +2 -4
- package/dist/loop/compaction.d.ts +20 -0
- package/dist/loop/compaction.js +2 -0
- package/dist/loop/continue-agent.d.ts +40 -0
- package/dist/loop/continue-agent.js +38 -0
- package/dist/loop/metrics.d.ts +30 -22
- package/dist/loop/metrics.js +51 -22
- package/dist/loop/run-agent.d.ts +111 -13
- package/dist/loop/run-agent.js +467 -220
- package/dist/loop/run-to-terminal.d.ts +16 -17
- package/dist/loop/run-to-terminal.js +24 -35
- package/dist/loop/tool-mask.d.ts +23 -0
- package/dist/loop/tool-mask.js +46 -0
- package/dist/loop/tool-output.d.ts +48 -0
- package/dist/loop/tool-output.js +79 -0
- package/dist/loop/types.d.ts +6 -3
- package/dist/memory/ai-sdk-message-storage.d.ts +49 -2
- package/dist/memory/ai-sdk-message-storage.js +78 -2
- package/dist/memory/conversation-display-recorder.d.ts +6 -0
- package/dist/memory/conversation-display-recorder.js +64 -22
- package/dist/memory/conversation-display-replay.d.ts +5 -14
- package/dist/memory/conversation-display-replay.js +66 -32
- package/dist/memory/conversation-display-storage.d.ts +2 -1
- package/dist/memory/conversation-display-storage.js +2 -1
- package/dist/memory/conversation-view.d.ts +53 -0
- package/dist/memory/conversation-view.js +130 -0
- package/dist/memory/count-tokens.d.ts +5 -7
- package/dist/memory/count-tokens.js +7 -9
- package/dist/memory/thread-history.d.ts +74 -102
- package/dist/memory/thread-history.js +181 -246
- package/dist/memory/thread-store.d.ts +3 -3
- package/dist/memory/thread-store.js +7 -2
- package/dist/memory/tool-call-integrity.d.ts +15 -21
- package/dist/memory/tool-call-integrity.js +31 -38
- package/dist/memory/working-memory.d.ts +2 -5
- package/dist/memory/working-memory.js +2 -5
- package/dist/prompts/SOUL.d.ts +2 -2
- package/dist/prompts/SOUL.js +2 -7
- package/dist/prompts/analogical-reasoner.d.ts +1 -1
- package/dist/prompts/analogical-reasoner.js +16 -32
- package/dist/prompts/briefing.js +1 -1
- package/dist/prompts/compaction.d.ts +4 -0
- package/dist/prompts/compaction.js +24 -0
- package/dist/prompts/conversation.d.ts +1 -1
- package/dist/prompts/conversation.js +26 -35
- package/dist/prompts/execute-analysis/step-summary.js +7 -5
- package/dist/prompts/literature-reviewer.d.ts +1 -1
- package/dist/prompts/literature-reviewer.js +29 -34
- package/dist/prompts/planner.js +15 -29
- package/dist/prompts/report-session.d.ts +1 -1
- package/dist/prompts/report-session.js +2 -33
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/bulk-transcriptomics-agent.js +1 -2
- package/dist/prompts/sandbox/cheminformatics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/cheminformatics-agent.js +10 -29
- package/dist/prompts/sandbox/chromatin-agent.d.ts +1 -1
- package/dist/prompts/sandbox/chromatin-agent.js +1 -2
- package/dist/prompts/sandbox/data-profiler.js +6 -3
- package/dist/prompts/sandbox/dna-methylation-agent.d.ts +1 -1
- package/dist/prompts/sandbox/dna-methylation-agent.js +2 -7
- package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
- package/dist/prompts/sandbox/drug-repurposing-agent.js +20 -24
- package/dist/prompts/sandbox/enrichment-agent.d.ts +1 -1
- package/dist/prompts/sandbox/enrichment-agent.js +3 -2
- package/dist/prompts/sandbox/genomic-variant-agent.d.ts +1 -1
- package/dist/prompts/sandbox/genomic-variant-agent.js +0 -2
- package/dist/prompts/sandbox/immune-profiling-agent.d.ts +1 -1
- package/dist/prompts/sandbox/immune-profiling-agent.js +7 -14
- package/dist/prompts/sandbox/metabolomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/metabolomics-agent.js +0 -2
- package/dist/prompts/sandbox/microbiome-agent.d.ts +1 -1
- package/dist/prompts/sandbox/microbiome-agent.js +14 -21
- package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multi-omics-integration-agent.js +9 -19
- package/dist/prompts/sandbox/multimodal-sc-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multimodal-sc-agent.js +4 -5
- package/dist/prompts/sandbox/network-agent.d.ts +1 -1
- package/dist/prompts/sandbox/network-agent.js +7 -8
- package/dist/prompts/sandbox/pkpd-clinical-response-agent.d.ts +1 -1
- package/dist/prompts/sandbox/pkpd-clinical-response-agent.js +19 -19
- package/dist/prompts/sandbox/proteomics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/proteomics-agent.js +3 -4
- package/dist/prompts/sandbox/scientific-executor.d.ts +1 -1
- package/dist/prompts/sandbox/scientific-executor.js +7 -10
- package/dist/prompts/sandbox/single-cell-agent.d.ts +1 -1
- package/dist/prompts/sandbox/single-cell-agent.js +2 -4
- package/dist/prompts/sandbox/spatial-omics-agent.d.ts +1 -1
- package/dist/prompts/sandbox/spatial-omics-agent.js +3 -5
- package/dist/prompts/sandbox/statistical-modeling-agent.d.ts +1 -1
- package/dist/prompts/sandbox/statistical-modeling-agent.js +1 -1
- package/dist/prompts/sandbox/translational-safety-agent.d.ts +1 -1
- package/dist/prompts/sandbox/translational-safety-agent.js +10 -19
- package/dist/prompts/sandbox-standards.d.ts +7 -7
- package/dist/prompts/sandbox-standards.js +74 -76
- package/dist/prompts/synthesis-agent.d.ts +1 -1
- package/dist/prompts/synthesis-agent.js +16 -42
- package/dist/providers/ai-sdk.d.ts +39 -6
- package/dist/providers/ai-sdk.js +117 -18
- package/dist/providers/errors.d.ts +2 -0
- package/dist/providers/errors.js +5 -0
- package/dist/providers/prompt-cache.d.ts +43 -41
- package/dist/providers/prompt-cache.js +47 -41
- package/dist/providers/reasoning.d.ts +10 -21
- package/dist/providers/reasoning.js +10 -21
- package/dist/providers/types.d.ts +21 -5
- package/dist/report-model/authoring-grammar.d.ts +2 -2
- package/dist/runtime/assemble.d.ts +9 -7
- package/dist/runtime/assemble.js +7 -3
- package/dist/sandbox/create-sandbox.d.ts +2 -2
- package/dist/sandbox/submit-exec.d.ts +1 -1
- package/dist/schemas/artifact-manifest.d.ts +4 -4
- package/dist/state/init.js +39 -4
- package/dist/state/purge-analysis.js +4 -0
- package/dist/state/tool-outputs.d.ts +4 -0
- package/dist/state/tool-outputs.js +30 -0
- package/dist/tasks/data-profile.d.ts +3 -0
- package/dist/tasks/data-profile.js +2 -0
- package/dist/tools/ad-hoc-router.d.ts +8 -0
- package/dist/tools/ad-hoc-router.js +21 -2
- package/dist/tools/display/show-user.d.ts +1 -1
- package/dist/tools/execute-analysis.d.ts +3 -0
- package/dist/tools/execute-analysis.js +9 -1
- package/dist/tools/read-tool-output.d.ts +7 -0
- package/dist/tools/read-tool-output.js +130 -0
- package/dist/tools/research/generate-analogy-report.d.ts +10 -26
- package/dist/tools/research/generate-analogy-report.js +76 -163
- package/dist/tools/research/generate-plan.d.ts +3 -0
- package/dist/tools/research/generate-plan.js +4 -1
- package/dist/tools/research/literature-reviewer.d.ts +3 -0
- package/dist/tools/research/literature-reviewer.js +3 -0
- package/dist/tools/sandbox/report-blocker.d.ts +18 -5
- package/dist/tools/sandbox/report-blocker.js +54 -6
- package/dist/tools/sandbox/submit-file-metadata.d.ts +30 -0
- package/dist/tools/sandbox/submit-file-metadata.js +67 -0
- package/dist/tools/start-report-session.d.ts +1 -3
- package/dist/tools/start-report-session.js +0 -2
- package/dist/tools/workspace/execute-command.js +7 -4
- package/dist/tools/workspace/result-bounds.d.ts +4 -4
- package/dist/tools/workspace/result-bounds.js +6 -5
- package/dist/tools/workspace/workspace-search.d.ts +1 -1
- package/dist/workflows/execute-analysis.d.ts +3 -0
- package/dist/workflows/execute-analysis.js +1 -0
- package/dist/workflows/sandbox-step.d.ts +30 -9
- package/dist/workflows/sandbox-step.js +37 -8
- package/package.json +9 -9
- package/dist/providers/anthropic.d.ts +0 -25
- package/dist/providers/anthropic.js +0 -28
|
@@ -1,38 +1,10 @@
|
|
|
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
|
-
*
|
|
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 type { ModelMessage } from "ai";
|
|
38
10
|
import type { Logger } from "../lib/logger.js";
|
|
@@ -40,16 +12,10 @@ import type { LoopMessage } from "../loop/types.js";
|
|
|
40
12
|
import type { ThreadHistory } from "../memory/thread-history.js";
|
|
41
13
|
import type { ThreadType } from "../memory/thread-store.js";
|
|
42
14
|
import type { WorkingMemoryStore } from "../memory/working-memory.js";
|
|
43
|
-
/**
|
|
44
|
-
* Token budget for the `loadRecent` history window. Sized to leave headroom
|
|
45
|
-
* under the model context window for the system prompt, tool definitions,
|
|
46
|
-
* the three tail messages, and the output budget.
|
|
47
|
-
*/
|
|
48
|
-
export declare const DEFAULT_HISTORY_TOKEN_BUDGET = 120000;
|
|
49
15
|
export interface AssembleMessagesArgs {
|
|
50
16
|
/** The conversation thread — a UI-generated UUID, never the analysisId. */
|
|
51
17
|
readonly threadId: string;
|
|
52
|
-
/** The type of the thread. A `report` thread
|
|
18
|
+
/** The type of the thread. A `report` thread gets no working-memory record, and its view keeps the seed. */
|
|
53
19
|
readonly threadType: ThreadType;
|
|
54
20
|
/** The analysis scope — keys working memory and (separately) the context. */
|
|
55
21
|
readonly analysisId: string;
|
|
@@ -57,29 +23,27 @@ export interface AssembleMessagesArgs {
|
|
|
57
23
|
readonly userInput: string;
|
|
58
24
|
/** `cortex_analysis_state.context`, already read by the route. `null` when absent. */
|
|
59
25
|
readonly analysisContext: string | null;
|
|
60
|
-
/**
|
|
26
|
+
/** The analysis-wide run activity that chat-turn preparation rendered for this turn. */
|
|
61
27
|
readonly runActivityContext: string;
|
|
62
|
-
/** The conversation message store — supplies the
|
|
28
|
+
/** The conversation message store — supplies the view of the thread. */
|
|
63
29
|
readonly history: ThreadHistory;
|
|
64
|
-
/** The working-memory store — rendered into
|
|
30
|
+
/** The working-memory store — rendered into a context record. */
|
|
65
31
|
readonly workingMemory: WorkingMemoryStore;
|
|
66
|
-
/** History-window token budget. Defaults to {@link DEFAULT_HISTORY_TOKEN_BUDGET}. */
|
|
67
|
-
readonly tokenBudget?: number;
|
|
68
32
|
/** Diagnostic seam for the history repair record; omitted falls back to no-op. */
|
|
69
33
|
readonly logger?: Logger;
|
|
70
34
|
}
|
|
71
35
|
export interface AssembledMessages {
|
|
72
36
|
/** The full message array for `runAgent`. */
|
|
73
37
|
readonly messages: LoopMessage[];
|
|
74
|
-
/**
|
|
75
|
-
* The sanitized user input as its own message — the genuine turn start.
|
|
76
|
-
* The route persists this plus the loop's output; the tail injections are
|
|
77
|
-
* not persisted.
|
|
78
|
-
*/
|
|
38
|
+
/** The sanitized user input as its own message — the genuine turn start. */
|
|
79
39
|
readonly userMessage: ModelMessage;
|
|
40
|
+
/** The context records after the user message. The turn stores them with the user message. */
|
|
41
|
+
readonly contextRecords: readonly ModelMessage[];
|
|
80
42
|
}
|
|
81
43
|
/**
|
|
82
|
-
* Assemble the message array for one chat turn. Async — it reads the
|
|
83
|
-
*
|
|
44
|
+
* Assemble the message array for one chat turn. Async — it reads the view of
|
|
45
|
+
* the thread and renders working memory.
|
|
84
46
|
*/
|
|
85
47
|
export declare function assembleMessages(args: AssembleMessagesArgs): Promise<AssembledMessages>;
|
|
48
|
+
/** The context records of one turn, in the order analysis context, run activity, working memory. */
|
|
49
|
+
export declare function contextRecordsFor(args: Pick<AssembleMessagesArgs, "threadType" | "analysisId" | "analysisContext" | "runActivityContext" | "workingMemory">, history: readonly ModelMessage[]): Promise<ModelMessage[]>;
|
|
@@ -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
|
-
*
|
|
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 {
|
|
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
|
-
*
|
|
43
|
-
*
|
|
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
|
|
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.
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
|
|
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
|
|
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
|
|
75
|
-
|
|
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
|
-
|
|
78
|
-
role: "user",
|
|
79
|
-
content: `[Analysis Context]\n${args.analysisContext}`,
|
|
80
|
-
});
|
|
48
|
+
texts.push(["analysis-context", `[Analysis Context]\n${args.analysisContext}`]);
|
|
81
49
|
}
|
|
82
|
-
|
|
83
|
-
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
96
|
-
return { messages: [...history, ...tail], userMessage };
|
|
69
|
+
return records;
|
|
97
70
|
}
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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
|
|
11
|
-
export declare function renderRunActivity(activity: RunPage
|
|
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;
|
package/dist/app/run-activity.js
CHANGED
|
@@ -1,36 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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
|
|
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
|
|
33
|
-
export function renderRunActivity(activity
|
|
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
|
|
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. `
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
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
|
|
241
|
-
*
|
|
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((
|
|
268
|
-
//
|
|
269
|
-
//
|
|
270
|
-
|
|
271
|
-
|
|
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 {
|
|
@@ -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
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
-
|
|
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>>;
|