@inflexa-ai/harness 0.23.0 → 0.24.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 +1 -2
- package/dist/agents/conversation-agent.d.ts +0 -9
- package/dist/contracts/chat-parts.d.ts +29 -33
- package/dist/contracts/content-url.d.ts +7 -41
- package/dist/contracts/content-url.js +7 -48
- package/dist/contracts/index.d.ts +2 -2
- package/dist/contracts/index.js +1 -1
- package/dist/contracts/message.d.ts +1 -1
- package/dist/contracts/message.js +1 -1
- package/dist/contracts/part-registry.js +2 -3
- package/dist/contracts/schemas/chat-parts.d.ts +16 -53
- package/dist/contracts/schemas/chat-parts.js +15 -25
- package/dist/contracts/schemas/index.d.ts +1 -1
- package/dist/contracts/schemas/index.js +1 -1
- package/dist/execution/run-authorizer.d.ts +1 -1
- package/dist/execution/run-authorizer.js +1 -1
- package/dist/index.d.ts +1 -3
- package/dist/index.js +0 -2
- package/dist/lib/zod-issues.d.ts +2 -3
- package/dist/lib/zod-issues.js +2 -3
- package/dist/loop/__fixtures__/scripted-provider.js +3 -1
- package/dist/loop/run-agent.d.ts +5 -0
- package/dist/loop/run-agent.js +10 -1
- package/dist/loop/run-to-terminal.d.ts +1 -1
- package/dist/loop/run-to-terminal.js +1 -1
- package/dist/memory/card-builders.d.ts +0 -27
- package/dist/memory/card-builders.js +0 -75
- package/dist/memory/conversation-display-backfill.d.ts +2 -4
- package/dist/memory/conversation-display-backfill.js +3 -9
- package/dist/memory/conversation-display-storage.d.ts +3 -4
- package/dist/memory/conversation-display-storage.js +3 -4
- package/dist/memory/reconstruct-cards.d.ts +5 -7
- package/dist/memory/reconstruct-cards.js +6 -20
- package/dist/runtime/boot.js +2 -12
- package/dist/tools/report-session/preview-report.d.ts +2 -1
- package/dist/tools/report-session/preview-report.js +9 -1
- package/dist/tools/start-report-session.js +2 -2
- package/dist/workspace/paths.d.ts +0 -21
- package/dist/workspace/paths.js +0 -37
- package/package.json +1 -3
- package/dist/agents/report-builder.d.ts +0 -34
- package/dist/agents/report-builder.js +0 -34
- package/dist/execution/report-render.d.ts +0 -39
- package/dist/execution/report-render.js +0 -98
- package/dist/execution/report-runner.d.ts +0 -111
- package/dist/execution/report-runner.js +0 -230
- package/dist/prompts/report-builder.d.ts +0 -1
- package/dist/prompts/report-builder.js +0 -174
- package/dist/tools/iterate-report.d.ts +0 -227
- package/dist/tools/iterate-report.js +0 -883
- package/dist/tools/lib/preview-lock.d.ts +0 -20
- package/dist/tools/lib/preview-lock.js +0 -35
- package/dist/tools/lib/report-preflight.d.ts +0 -39
- package/dist/tools/lib/report-preflight.js +0 -283
- package/dist/tools/report/build-report.d.ts +0 -13
- package/dist/tools/report/build-report.js +0 -27
- package/dist/tools/report/index.d.ts +0 -15
- package/dist/tools/report/index.js +0 -15
- package/dist/tools/report/mint-preview-url.d.ts +0 -26
- package/dist/tools/report/mint-preview-url.js +0 -47
- package/dist/tools/report/preview-publisher.d.ts +0 -53
- package/dist/tools/report/preview-publisher.js +0 -45
- package/dist/tools/report/preview-snapshot.d.ts +0 -34
- package/dist/tools/report/preview-snapshot.js +0 -98
- package/dist/tools/report/submit-report.d.ts +0 -26
- package/dist/tools/report/submit-report.js +0 -148
- package/dist/tools/report/version-fs.d.ts +0 -15
- package/dist/tools/report/version-fs.js +0 -165
|
@@ -39,14 +39,6 @@ export interface RunCardData {
|
|
|
39
39
|
title: string;
|
|
40
40
|
stepCount: number;
|
|
41
41
|
}
|
|
42
|
-
export interface PreviewCardData {
|
|
43
|
-
id: string;
|
|
44
|
-
previewId: string;
|
|
45
|
-
version: number;
|
|
46
|
-
title: string;
|
|
47
|
-
previewPath: string;
|
|
48
|
-
format: "html" | "pdf";
|
|
49
|
-
}
|
|
50
42
|
/** Build the `data-plan` payload by loading the stored plan. `ok(null)` when
|
|
51
43
|
* the plan is missing or no longer parses (absence is NOT an error) — the
|
|
52
44
|
* caller falls back to a chip. */
|
|
@@ -100,22 +92,3 @@ export declare function fileGroupHash(paths: string[], title: string | undefined
|
|
|
100
92
|
* path the live tool rejected would be resurrected into a card on reload.
|
|
101
93
|
*/
|
|
102
94
|
export declare function buildFileReferenceCardData(input: Record<string, unknown>): FileReferenceCardData | null;
|
|
103
|
-
/**
|
|
104
|
-
* Build the `data-report-preview` payload for an `iterate_report` tool_use. The
|
|
105
|
-
* preview's source of truth is the filesystem under
|
|
106
|
-
* `previews/{previewId}/`: directory layout yields `version`
|
|
107
|
-
* (latest `vN`) + `previewPath`, and `preview-meta.json` yields title/format.
|
|
108
|
-
* Mirrors `buildRunCardData` — the live emit knows the server-generated
|
|
109
|
-
* `previewId`, while the reconstruct-on-read path resolves it (from the
|
|
110
|
-
* iteration's `previewId` input, or else the analysis's latest preview). The
|
|
111
|
-
* card is keyed by `previewId` only, so every iteration's tool_use rebuilds
|
|
112
|
-
* the same card and the frontend groups them into one with all versions selectable.
|
|
113
|
-
* Null when nothing renders — the caller falls back to a chip.
|
|
114
|
-
*/
|
|
115
|
-
export declare function buildPreviewCardData(
|
|
116
|
-
/** Absolute host root of the analysis's workspace tree (previews live inside it). */
|
|
117
|
-
workspaceRoot: string, opts: {
|
|
118
|
-
previewId?: string;
|
|
119
|
-
title?: string;
|
|
120
|
-
format?: "html" | "pdf";
|
|
121
|
-
}): Promise<PreviewCardData | null>;
|
|
@@ -16,17 +16,12 @@
|
|
|
16
16
|
* under the part `type` directly.
|
|
17
17
|
*/
|
|
18
18
|
import { createHash } from "node:crypto";
|
|
19
|
-
import { readdir, readFile, stat } from "node:fs/promises";
|
|
20
|
-
import { join } from "node:path";
|
|
21
19
|
import { okAsync } from "neverthrow";
|
|
22
20
|
import { tryQuery } from "../lib/db-result.js";
|
|
23
|
-
import { tryFs } from "../lib/fs-result.js";
|
|
24
21
|
import { AnalysisPlanSchema } from "../schemas/workflow-state.js";
|
|
25
22
|
import { loadPlan } from "../state/index.js";
|
|
26
23
|
import { normalizeEchartSpec } from "../tools/display/normalize-echart-spec.js";
|
|
27
24
|
import { validatePath } from "../tools/lib/path-validation.js";
|
|
28
|
-
import { latestPreviewVersion, previewDir, PREVIEWS_ROOT } from "../workspace/paths.js";
|
|
29
|
-
const PREVIEW_META_FILE = "preview-meta.json";
|
|
30
25
|
function isRecord(value) {
|
|
31
26
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
32
27
|
}
|
|
@@ -176,73 +171,3 @@ export function buildFileReferenceCardData(input) {
|
|
|
176
171
|
const id = `pres-${fileGroupHash(files.map((f) => f.path), title)}`;
|
|
177
172
|
return { id, ...(title !== undefined ? { title } : {}), files };
|
|
178
173
|
}
|
|
179
|
-
/** The stored preview metadata, or `null` when the file is absent — a malformed
|
|
180
|
-
* file is a genuine read failure (`err`), folded to `null` at the call site. */
|
|
181
|
-
function readPreviewMeta(previewRootAbs) {
|
|
182
|
-
const path = join(previewRootAbs, PREVIEW_META_FILE);
|
|
183
|
-
return tryFs("cardBuilders.readPreviewMeta", async () => JSON.parse(await readFile(path, "utf8")), { path, onAbsent: () => null });
|
|
184
|
-
}
|
|
185
|
-
/** Directory names (not files) directly under `dir`, or `[]` when absent. */
|
|
186
|
-
function subdirs(dir) {
|
|
187
|
-
return tryFs("cardBuilders.subdirs", async () => (await readdir(dir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name), {
|
|
188
|
-
path: dir,
|
|
189
|
-
onAbsent: () => [],
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
/** The most-recently-modified previewId under an analysis, or null when none. */
|
|
193
|
-
function latestPreviewId(previewsRootAbs) {
|
|
194
|
-
return subdirs(previewsRootAbs).map(async (dirs) => {
|
|
195
|
-
if (dirs.length <= 1)
|
|
196
|
-
return dirs[0] ?? null;
|
|
197
|
-
let best = null;
|
|
198
|
-
for (const name of dirs) {
|
|
199
|
-
const path = join(previewsRootAbs, name);
|
|
200
|
-
const mtimeMs = await tryFs("cardBuilders.latestPreviewId.stat", async () => (await stat(path)).mtimeMs, { path }).unwrapOr(null); // skip an unreadable/absent entry
|
|
201
|
-
if (mtimeMs !== null && (!best || mtimeMs > best.mtimeMs)) {
|
|
202
|
-
best = { name, mtimeMs };
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
return best?.name ?? dirs[0];
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
/**
|
|
209
|
-
* Build the `data-report-preview` payload for an `iterate_report` tool_use. The
|
|
210
|
-
* preview's source of truth is the filesystem under
|
|
211
|
-
* `previews/{previewId}/`: directory layout yields `version`
|
|
212
|
-
* (latest `vN`) + `previewPath`, and `preview-meta.json` yields title/format.
|
|
213
|
-
* Mirrors `buildRunCardData` — the live emit knows the server-generated
|
|
214
|
-
* `previewId`, while the reconstruct-on-read path resolves it (from the
|
|
215
|
-
* iteration's `previewId` input, or else the analysis's latest preview). The
|
|
216
|
-
* card is keyed by `previewId` only, so every iteration's tool_use rebuilds
|
|
217
|
-
* the same card and the frontend groups them into one with all versions selectable.
|
|
218
|
-
* Null when nothing renders — the caller falls back to a chip.
|
|
219
|
-
*/
|
|
220
|
-
export async function buildPreviewCardData(
|
|
221
|
-
/** Absolute host root of the analysis's workspace tree (previews live inside it). */
|
|
222
|
-
workspaceRoot, opts) {
|
|
223
|
-
const previewsRootAbs = join(workspaceRoot, PREVIEWS_ROOT);
|
|
224
|
-
const resolveId = opts.previewId ? okAsync(opts.previewId) : latestPreviewId(previewsRootAbs);
|
|
225
|
-
return resolveId
|
|
226
|
-
.andThen((previewId) => {
|
|
227
|
-
if (!previewId)
|
|
228
|
-
return okAsync(null);
|
|
229
|
-
const previewRootAbs = join(workspaceRoot, previewDir(previewId));
|
|
230
|
-
return subdirs(previewRootAbs).andThen((versionDirs) => {
|
|
231
|
-
const version = latestPreviewVersion(versionDirs);
|
|
232
|
-
if (version === 0)
|
|
233
|
-
return okAsync(null);
|
|
234
|
-
return readPreviewMeta(previewRootAbs).map((meta) => {
|
|
235
|
-
const id = `prev-${createHash("sha256").update(previewId).digest("hex").slice(0, 16)}`;
|
|
236
|
-
return {
|
|
237
|
-
id,
|
|
238
|
-
previewId,
|
|
239
|
-
version,
|
|
240
|
-
title: opts.title ?? meta?.title ?? "Report",
|
|
241
|
-
previewPath: `v${version}/index.html`,
|
|
242
|
-
format: opts.format ?? (meta?.format === "pdf" ? "pdf" : "html"),
|
|
243
|
-
};
|
|
244
|
-
});
|
|
245
|
-
});
|
|
246
|
-
})
|
|
247
|
-
.unwrapOr(null);
|
|
248
|
-
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import type { Pool } from "pg";
|
|
2
2
|
import type { Tool } from "../tools/define-tool.js";
|
|
3
|
-
import type { ResolveWorkspaceRoot } from "../workspace/paths.js";
|
|
4
3
|
export interface ConversationDisplayBackfillDeps {
|
|
5
4
|
readonly pool: Pool;
|
|
6
|
-
readonly resolveWorkspaceRoot: ResolveWorkspaceRoot;
|
|
7
5
|
/**
|
|
8
6
|
* The conversation agent's composed roster, used to recover each legacy call's
|
|
9
7
|
* one-line detail from its persisted input. Tools contributed through the
|
|
@@ -17,8 +15,8 @@ export interface ConversationDisplayBackfillDeps {
|
|
|
17
15
|
* Freeze the migration renderer's output into display envelopes, once per legacy
|
|
18
16
|
* turn, so the runtime read path never has to reconstruct anything.
|
|
19
17
|
*
|
|
20
|
-
* Missing mutable resources degrade to a generic, cardless display — a
|
|
21
|
-
* that
|
|
18
|
+
* Missing mutable resources degrade to a generic, cardless display — a plan
|
|
19
|
+
* that is absent is normal historical absence, not a fault. Malformed stored rows
|
|
22
20
|
* and database faults remain startup-fatal: a turn that cannot be migrated must
|
|
23
21
|
* not be silently skipped and then read as if it had no display.
|
|
24
22
|
*/
|
|
@@ -10,8 +10,8 @@ const GENUINE_USER_SQL = `m.message_envelope->'message'->>'role' = 'user'
|
|
|
10
10
|
* Freeze the migration renderer's output into display envelopes, once per legacy
|
|
11
11
|
* turn, so the runtime read path never has to reconstruct anything.
|
|
12
12
|
*
|
|
13
|
-
* Missing mutable resources degrade to a generic, cardless display — a
|
|
14
|
-
* that
|
|
13
|
+
* Missing mutable resources degrade to a generic, cardless display — a plan
|
|
14
|
+
* that is absent is normal historical absence, not a fault. Malformed stored rows
|
|
15
15
|
* and database faults remain startup-fatal: a turn that cannot be migrated must
|
|
16
16
|
* not be silently skipped and then read as if it had no display.
|
|
17
17
|
*/
|
|
@@ -64,13 +64,7 @@ export async function backfillConversationDisplayEnvelopes(deps) {
|
|
|
64
64
|
});
|
|
65
65
|
let resolveCard;
|
|
66
66
|
if (head.analysis_id !== null) {
|
|
67
|
-
|
|
68
|
-
resolveCard = createCardResolver(deps.pool, head.analysis_id, deps.resolveWorkspaceRoot(head.analysis_id));
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
// A removed/moved workspace is normal historical absence.
|
|
72
|
-
resolveCard = undefined;
|
|
73
|
-
}
|
|
67
|
+
resolveCard = createCardResolver(deps.pool, head.analysis_id);
|
|
74
68
|
}
|
|
75
69
|
const cortex = await contentToCortexMessages(stored, { resolveCard, resolveDetail });
|
|
76
70
|
const uiMessages = cortexMessagesToConversationUI(cortex);
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* reconstruction.
|
|
16
16
|
*/
|
|
17
17
|
import { type DataUIPart, type UIMessage } from "ai";
|
|
18
|
-
import type { AskPart, FileReferencePart, PlanPart, PresentationPart,
|
|
18
|
+
import type { AskPart, ChildSessionStartedPart, FileReferencePart, PlanPart, PresentationPart, ReportRenderedPart, RunCardPart } from "../contracts/chat-parts.js";
|
|
19
19
|
import type { ToolCallOutcome } from "../contracts/chat-events.js";
|
|
20
20
|
import type { CortexMessage, CortexPart, ToolCallPart } from "../contracts/message.js";
|
|
21
21
|
export declare const SUPPORTED_DISPLAY_SCHEMA_VERSION = 1;
|
|
@@ -38,9 +38,8 @@ export type ConversationUIData = {
|
|
|
38
38
|
"run-card": Payload<RunCardPart>;
|
|
39
39
|
"file-reference": Payload<FileReferencePart>;
|
|
40
40
|
ask: Payload<AskPart>;
|
|
41
|
-
"
|
|
42
|
-
"report-
|
|
43
|
-
"report-session-started": Payload<ReportSessionStartedPart>;
|
|
41
|
+
"child-session-started": Payload<ChildSessionStartedPart>;
|
|
42
|
+
"report-rendered": Payload<ReportRenderedPart>;
|
|
44
43
|
};
|
|
45
44
|
export interface ConversationDisplayMetadata {
|
|
46
45
|
interrupted?: boolean;
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { validateUIMessages } from "ai";
|
|
18
18
|
import { z } from "zod";
|
|
19
|
-
import { AskPartSchema,
|
|
19
|
+
import { AskPartSchema, ChildSessionStartedPartSchema, FileReferencePartSchema, PlanPartSchema, PresentationPartSchema, ReportRenderedPartSchema, RunCardPartSchema, } from "../contracts/schemas/chat-parts.js";
|
|
20
20
|
import { ToolCallOutcomeSchema } from "../contracts/schemas/chat-events.js";
|
|
21
21
|
export const SUPPORTED_DISPLAY_SCHEMA_VERSION = 1;
|
|
22
22
|
const DisplayEnvelopeHeaderSchema = z.object({
|
|
@@ -54,9 +54,8 @@ const dataSchemas = {
|
|
|
54
54
|
"run-card": RunCardPartSchema.omit({ type: true }).strict(),
|
|
55
55
|
"file-reference": FileReferencePartSchema.omit({ type: true }).strict(),
|
|
56
56
|
ask: AskPartSchema.omit({ type: true }).strict(),
|
|
57
|
-
"
|
|
58
|
-
"report-
|
|
59
|
-
"report-session-started": ReportSessionStartedPartSchema.omit({ type: true }).strict(),
|
|
57
|
+
"child-session-started": ChildSessionStartedPartSchema.omit({ type: true }).strict(),
|
|
58
|
+
"report-rendered": ReportRenderedPartSchema.omit({ type: true }).strict(),
|
|
60
59
|
};
|
|
61
60
|
export function envelopeDisplayMessages(messages) {
|
|
62
61
|
if (messages.length === 0)
|
|
@@ -4,15 +4,13 @@
|
|
|
4
4
|
* through the pre-v1 direct-upgrade window, then remove its runtime fallback
|
|
5
5
|
* in a dedicated compatibility change.
|
|
6
6
|
*
|
|
7
|
-
* `show_plan` / `show_user` / `show_file` / `execute_analysis`
|
|
8
|
-
* `data-plan` / `data-presentation` / `data-file-reference` / `data-run-card`
|
|
9
|
-
* `data-report-preview` cards
|
|
7
|
+
* `show_plan` / `show_user` / `show_file` / `execute_analysis` emit
|
|
8
|
+
* `data-plan` / `data-presentation` / `data-file-reference` / `data-run-card` cards
|
|
10
9
|
* live over the chat SSE stream. On reload, `content-to-cortex` calls this
|
|
11
10
|
* resolver for each AI SDK tool-call part: a recognised display tool yields its card (rebuilt via the
|
|
12
11
|
* shared `card-builders`); anything else yields `null` and falls back to a
|
|
13
|
-
* generic `tool-call` chip. Keyed by the analysis `Pool` + id (plan / run)
|
|
14
|
-
* the
|
|
15
|
-
* be re-loaded.
|
|
12
|
+
* generic `tool-call` chip. Keyed by the analysis `Pool` + id (plan / run) so
|
|
13
|
+
* the card can be re-loaded.
|
|
16
14
|
*/
|
|
17
15
|
import type { CortexPart } from "@inflexa-ai/harness/contracts/message.js";
|
|
18
16
|
import type { Pool } from "pg";
|
|
@@ -26,4 +24,4 @@ export interface StoredToolCallForCard {
|
|
|
26
24
|
/** @deprecated Used only by the legacy display backfill/fallback. */
|
|
27
25
|
export type ToolCardResolver = (block: StoredToolCallForCard) => Promise<CortexPart | null>;
|
|
28
26
|
/** @deprecated Use persisted conversation display envelopes for runtime reads. */
|
|
29
|
-
export declare function createCardResolver(pool: Pool, analysisId: string
|
|
27
|
+
export declare function createCardResolver(pool: Pool, analysisId: string): ToolCardResolver;
|
|
@@ -4,38 +4,24 @@
|
|
|
4
4
|
* through the pre-v1 direct-upgrade window, then remove its runtime fallback
|
|
5
5
|
* in a dedicated compatibility change.
|
|
6
6
|
*
|
|
7
|
-
* `show_plan` / `show_user` / `show_file` / `execute_analysis`
|
|
8
|
-
* `data-plan` / `data-presentation` / `data-file-reference` / `data-run-card`
|
|
9
|
-
* `data-report-preview` cards
|
|
7
|
+
* `show_plan` / `show_user` / `show_file` / `execute_analysis` emit
|
|
8
|
+
* `data-plan` / `data-presentation` / `data-file-reference` / `data-run-card` cards
|
|
10
9
|
* live over the chat SSE stream. On reload, `content-to-cortex` calls this
|
|
11
10
|
* resolver for each AI SDK tool-call part: a recognised display tool yields its card (rebuilt via the
|
|
12
11
|
* shared `card-builders`); anything else yields `null` and falls back to a
|
|
13
|
-
* generic `tool-call` chip. Keyed by the analysis `Pool` + id (plan / run)
|
|
14
|
-
* the
|
|
15
|
-
* be re-loaded.
|
|
12
|
+
* generic `tool-call` chip. Keyed by the analysis `Pool` + id (plan / run) so
|
|
13
|
+
* the card can be re-loaded.
|
|
16
14
|
*/
|
|
17
15
|
import { unwrapOrThrow } from "../lib/result.js";
|
|
18
16
|
import { adHocPlanId, adHocRunId } from "../tools/analysis-invocation.js";
|
|
19
17
|
import { validatePath } from "../tools/lib/path-validation.js";
|
|
20
|
-
import { buildFileReferenceCardData, buildPlanCardData, buildPresentationCardData,
|
|
18
|
+
import { buildFileReferenceCardData, buildPlanCardData, buildPresentationCardData, buildRunCardData } from "./card-builders.js";
|
|
21
19
|
/** @deprecated Use persisted conversation display envelopes for runtime reads. */
|
|
22
|
-
export function createCardResolver(pool, analysisId
|
|
20
|
+
export function createCardResolver(pool, analysisId) {
|
|
23
21
|
return async (block) => {
|
|
24
22
|
if (block.type !== "tool_use")
|
|
25
23
|
return null;
|
|
26
24
|
const input = (block.input ?? {});
|
|
27
|
-
// The report tool is `submit_report`; legacy transcripts carry the
|
|
28
|
-
// former ids `iterate_report` / `iterateReport` — accept all three. The
|
|
29
|
-
// brief-carrying field is still `report`, so the card reads identically.
|
|
30
|
-
if (block.name === "submit_report" || block.name === "iterate_report" || block.name === "iterateReport") {
|
|
31
|
-
const report = (input.report ?? null);
|
|
32
|
-
const card = await buildPreviewCardData(workspaceRoot, {
|
|
33
|
-
previewId: typeof input.previewId === "string" ? input.previewId : undefined,
|
|
34
|
-
title: report && typeof report.title === "string" ? report.title : undefined,
|
|
35
|
-
format: input.format === "pdf" || input.format === "html" ? input.format : undefined,
|
|
36
|
-
});
|
|
37
|
-
return card ? { type: "data-report-preview", ...card } : null;
|
|
38
|
-
}
|
|
39
25
|
if (block.name === "show_user") {
|
|
40
26
|
// The live tool rejects a malformed/traversal echart `dataPath` and emits nothing; the
|
|
41
27
|
// persisted tool_use still carries it, so the reload path MUST re-validate before building
|
package/dist/runtime/boot.js
CHANGED
|
@@ -38,7 +38,6 @@
|
|
|
38
38
|
*/
|
|
39
39
|
import { unwrapOrThrow } from "../lib/result.js";
|
|
40
40
|
import { SANDBOX_AGENT_META } from "../agents/sandbox/index.js";
|
|
41
|
-
import { REPORT_BUILDER_AGENT_ID, REPORT_BUILDER_SKILLS } from "../agents/report-builder.js";
|
|
42
41
|
import { validateAgentSkills } from "../agents/sandbox/validate-skills.js";
|
|
43
42
|
import { initCortexState } from "../state/init.js";
|
|
44
43
|
import { backfillConversationDisplayEnvelopes } from "../memory/conversation-display-backfill.js";
|
|
@@ -49,16 +48,8 @@ import { markDraining } from "./lifecycle.js";
|
|
|
49
48
|
import { runShutdownSequence } from "./shutdown.js";
|
|
50
49
|
const noop = () => { };
|
|
51
50
|
const noopAsync = () => Promise.resolve();
|
|
52
|
-
/**
|
|
53
|
-
|
|
54
|
-
* report-builder is not in the sandbox catalog — it is not plannable — but it
|
|
55
|
-
* declares a pack all the same, and a missing one there surfaces as a failed
|
|
56
|
-
* render partway through a report rather than at boot.
|
|
57
|
-
*/
|
|
58
|
-
const SKILL_DECLARING_AGENTS = {
|
|
59
|
-
...SANDBOX_AGENT_META,
|
|
60
|
-
[REPORT_BUILDER_AGENT_ID]: { id: REPORT_BUILDER_AGENT_ID, skills: REPORT_BUILDER_SKILLS },
|
|
61
|
-
};
|
|
51
|
+
/** Every agent whose declared packs must resolve before the first run. */
|
|
52
|
+
const SKILL_DECLARING_AGENTS = SANDBOX_AGENT_META;
|
|
62
53
|
export async function bootHarness(deps) {
|
|
63
54
|
const { core, pool, skillsDir, logger } = deps;
|
|
64
55
|
(deps.initTelemetry ?? noop)();
|
|
@@ -73,7 +64,6 @@ export async function bootHarness(deps) {
|
|
|
73
64
|
const conversationAgent = unwrapOrThrow(runtime.agents.forThread("conversation"));
|
|
74
65
|
await backfillConversationDisplayEnvelopes({
|
|
75
66
|
pool,
|
|
76
|
-
resolveWorkspaceRoot: core.conversation.resolveWorkspaceRoot,
|
|
77
67
|
tools: conversationAgent.tools,
|
|
78
68
|
});
|
|
79
69
|
await deps.beforeLaunch?.();
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
* render problem, a figure that escapes the workspace root, a write failure, and a stamp failure. The tool
|
|
29
29
|
* never throws for one of them. When the page
|
|
30
30
|
* lands, the tool stamps the hash of the rendered draft on the session state, thus the eyes and the record
|
|
31
|
-
* know which draft the page shows.
|
|
31
|
+
* know which draft the page shows. It then emits one durable `data-report-rendered` part, thus the
|
|
32
|
+
* transcript places the render and a live client learns that a fresh page exists. The filesystem speaks the
|
|
32
33
|
* throw protocol, and the workspace-root seam signals an unresolvable resource the same way. Thus the
|
|
33
34
|
* write runs through the `tryFs` glue, which turns a genuine fault into the ok-channel outcome and lets a
|
|
34
35
|
* control-flow exception propagate.
|
|
@@ -28,12 +28,14 @@
|
|
|
28
28
|
* render problem, a figure that escapes the workspace root, a write failure, and a stamp failure. The tool
|
|
29
29
|
* never throws for one of them. When the page
|
|
30
30
|
* lands, the tool stamps the hash of the rendered draft on the session state, thus the eyes and the record
|
|
31
|
-
* know which draft the page shows.
|
|
31
|
+
* know which draft the page shows. It then emits one durable `data-report-rendered` part, thus the
|
|
32
|
+
* transcript places the render and a live client learns that a fresh page exists. The filesystem speaks the
|
|
32
33
|
* throw protocol, and the workspace-root seam signals an unresolvable resource the same way. Thus the
|
|
33
34
|
* write runs through the `tryFs` glue, which turns a genuine fault into the ok-channel outcome and lets a
|
|
34
35
|
* control-flow exception propagate.
|
|
35
36
|
*/
|
|
36
37
|
import { err, ok } from "neverthrow";
|
|
38
|
+
import { randomUUID } from "node:crypto";
|
|
37
39
|
import { copyFile, mkdir, readdir, rm, writeFile } from "node:fs/promises";
|
|
38
40
|
import { extname, join } from "node:path";
|
|
39
41
|
import { relative as relativePosix } from "node:path/posix";
|
|
@@ -475,6 +477,12 @@ export function createPreviewReportTool(deps) {
|
|
|
475
477
|
logger.warn("the rendered hash did not stamp", { threadId, analysisId, detail });
|
|
476
478
|
return ok({ outcome: "stamp-failed", pagePath: written.value, detail });
|
|
477
479
|
}
|
|
480
|
+
// The part rides the rendered arm only. Each degraded arm shows no
|
|
481
|
+
// fresh page, thus it emits nothing.
|
|
482
|
+
await ctx.emit({
|
|
483
|
+
type: "data-report-rendered",
|
|
484
|
+
data: { id: randomUUID(), renderedAt: new Date().toISOString(), title: document.title },
|
|
485
|
+
});
|
|
478
486
|
const access = await mintAccess(deps.makeSessionPages, analysisId, threadId, ctx.session.auth, logger);
|
|
479
487
|
return ok({ outcome: "rendered", pagePath: written.value, ...(access !== undefined ? { access } : {}) });
|
|
480
488
|
},
|
|
@@ -173,8 +173,8 @@ export function createStartReportSessionTool(deps) {
|
|
|
173
173
|
// names a session that an earlier turn announced, thus a second
|
|
174
174
|
// part would put a second entry into the transcript.
|
|
175
175
|
await ctx.emit({
|
|
176
|
-
type: "data-
|
|
177
|
-
data: { threadId: child.threadId, parentThreadId },
|
|
176
|
+
type: "data-child-session-started",
|
|
177
|
+
data: { threadId: child.threadId, parentThreadId, threadType: "report" },
|
|
178
178
|
});
|
|
179
179
|
return ok({ outcome: "started", threadId: child.threadId, title: child.title });
|
|
180
180
|
}
|
|
@@ -159,27 +159,6 @@ export declare function tailWritePrefix(args: {
|
|
|
159
159
|
* a step briefing names (see the harness-workspace-tools spec).
|
|
160
160
|
*/
|
|
161
161
|
export declare function toSandboxPath(workspaceRoot: string, resourceId: string, hostAbsPath: string): string;
|
|
162
|
-
/**
|
|
163
|
-
* Parent of every preview, workspace-root-relative. Callers that enumerate
|
|
164
|
-
* previews join this; callers that address one join {@link previewDir}. Both
|
|
165
|
-
* spell the segment through this constant so the tree has one owner.
|
|
166
|
-
*/
|
|
167
|
-
export declare const PREVIEWS_ROOT = "previews";
|
|
168
|
-
/**
|
|
169
|
-
* Versioned preview directory, workspace-root-relative. Previews live inside
|
|
170
|
-
* the analysis tree (`previews/{previewId}/v{N}`); the content-token `res`
|
|
171
|
-
* claim keeps the separate `previews/{analysisId}/{previewId}` formula in
|
|
172
|
-
* `contracts/content-url.ts` — that is URL space, not a filesystem sub-path,
|
|
173
|
-
* and hosts that serve previews map one onto the other themselves.
|
|
174
|
-
*/
|
|
175
|
-
export declare function previewVersionDir(previewId: string, version: number): string;
|
|
176
|
-
/** Preview root for a specific preview (all versions), workspace-root-relative. */
|
|
177
|
-
export declare function previewDir(previewId: string): string;
|
|
178
|
-
/**
|
|
179
|
-
* Find the highest version number from a list of version directory names.
|
|
180
|
-
* Expects names like "v1", "v2", etc. Returns 0 if no versions found.
|
|
181
|
-
*/
|
|
182
|
-
export declare function latestPreviewVersion(dirNames: string[]): number;
|
|
183
162
|
/**
|
|
184
163
|
* Parent of every report session, workspace-root-relative. Callers that
|
|
185
164
|
* enumerate the sessions join this. Callers that address one join
|
package/dist/workspace/paths.js
CHANGED
|
@@ -151,43 +151,6 @@ export function toSandboxPath(workspaceRoot, resourceId, hostAbsPath) {
|
|
|
151
151
|
}
|
|
152
152
|
return tail === "" ? `/${resourceId}` : `/${resourceId}/${tail}`;
|
|
153
153
|
}
|
|
154
|
-
/**
|
|
155
|
-
* Parent of every preview, workspace-root-relative. Callers that enumerate
|
|
156
|
-
* previews join this; callers that address one join {@link previewDir}. Both
|
|
157
|
-
* spell the segment through this constant so the tree has one owner.
|
|
158
|
-
*/
|
|
159
|
-
export const PREVIEWS_ROOT = "previews";
|
|
160
|
-
/**
|
|
161
|
-
* Versioned preview directory, workspace-root-relative. Previews live inside
|
|
162
|
-
* the analysis tree (`previews/{previewId}/v{N}`); the content-token `res`
|
|
163
|
-
* claim keeps the separate `previews/{analysisId}/{previewId}` formula in
|
|
164
|
-
* `contracts/content-url.ts` — that is URL space, not a filesystem sub-path,
|
|
165
|
-
* and hosts that serve previews map one onto the other themselves.
|
|
166
|
-
*/
|
|
167
|
-
export function previewVersionDir(previewId, version) {
|
|
168
|
-
return `${previewDir(previewId)}/v${version}`;
|
|
169
|
-
}
|
|
170
|
-
/** Preview root for a specific preview (all versions), workspace-root-relative. */
|
|
171
|
-
export function previewDir(previewId) {
|
|
172
|
-
assertSafeId(previewId, "previewId");
|
|
173
|
-
return `${PREVIEWS_ROOT}/${previewId}`;
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Find the highest version number from a list of version directory names.
|
|
177
|
-
* Expects names like "v1", "v2", etc. Returns 0 if no versions found.
|
|
178
|
-
*/
|
|
179
|
-
export function latestPreviewVersion(dirNames) {
|
|
180
|
-
let max = 0;
|
|
181
|
-
for (const name of dirNames) {
|
|
182
|
-
const match = name.match(/^v(\d+)$/);
|
|
183
|
-
if (match) {
|
|
184
|
-
const n = parseInt(match[1], 10);
|
|
185
|
-
if (n > max)
|
|
186
|
-
max = n;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
return max;
|
|
190
|
-
}
|
|
191
154
|
/**
|
|
192
155
|
* Parent of every report session, workspace-root-relative. Callers that
|
|
193
156
|
* enumerate the sessions join this. Callers that address one join
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inflexa-ai/harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "Host-agnostic bioinformatics agent harness — hand-rolled agent loop, DBOS-durable workflows, sandboxed compute.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -76,7 +76,6 @@
|
|
|
76
76
|
"mime": "^4.1.0",
|
|
77
77
|
"nanoid": "^5.1.16",
|
|
78
78
|
"neverthrow": "^8.2.0",
|
|
79
|
-
"nunjucks": "^3.2.4",
|
|
80
79
|
"openai": "^7.4.0",
|
|
81
80
|
"p-queue": "^9.3.3",
|
|
82
81
|
"pdf-parse": "^2.4.5",
|
|
@@ -89,7 +88,6 @@
|
|
|
89
88
|
"@types/bun": "latest",
|
|
90
89
|
"@types/dockerode": "^3.3.47",
|
|
91
90
|
"@types/node": "^24.13.3",
|
|
92
|
-
"@types/nunjucks": "^3.2.6",
|
|
93
91
|
"@types/pdf-parse": "^1.1.5",
|
|
94
92
|
"@types/pg": "^8.20.0",
|
|
95
93
|
"csstree-validator": "^4.0.1",
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Report Builder Agent — in-process templating agent (no sandbox).
|
|
3
|
-
*
|
|
4
|
-
* Authors HTML reports via Jinja2 templating. Runs in the Cortex Node
|
|
5
|
-
* process — file IO is in-process via the `versionFs` tool roster, rendering
|
|
6
|
-
* is Node-side via Nunjucks (`build_report` tool), and visual validation
|
|
7
|
-
* uses headless Chrome via the `preview_snapshot` tool.
|
|
8
|
-
*
|
|
9
|
-
* The agent receives a complete brief from iterate-report's pre-flight
|
|
10
|
-
* (assets staged, columns parsed, sections fully composed) and has no
|
|
11
|
-
* discovery surface — its workspace is rooted at the version directory.
|
|
12
|
-
*
|
|
13
|
-
* NOT a sandbox-agent catalog member (per design Decision #2 — not
|
|
14
|
-
* plannable). The full tool roster is constructed inside the runner so
|
|
15
|
-
* each tool shares the runner's closure-captured outcome state and
|
|
16
|
-
* preview-dir paths; this factory just packages the result into a plain
|
|
17
|
-
* `AgentDefinition`.
|
|
18
|
-
*/
|
|
19
|
-
import type { AgentDefinition } from "../loop/types.js";
|
|
20
|
-
import type { Tool } from "../tools/define-tool.js";
|
|
21
|
-
export declare const REPORT_BUILDER_AGENT_ID = "report-builder";
|
|
22
|
-
/** Skill pack the report-builder prompt directs the model to read (design-system reference). */
|
|
23
|
-
export declare const REPORT_BUILDER_SKILLS: readonly ["report-html"];
|
|
24
|
-
export interface CreateReportBuilderAgentDeps {
|
|
25
|
-
/** Model id — provenance / metric label; the provider owns the wire model. */
|
|
26
|
-
readonly model: string;
|
|
27
|
-
/**
|
|
28
|
-
* Full tool roster — the 4 custom report tools plus the in-process
|
|
29
|
-
* versionFs surface, all constructed inside the runner so they close
|
|
30
|
-
* over the iteration's outcome state and preview-dir paths.
|
|
31
|
-
*/
|
|
32
|
-
readonly tools: readonly Tool[];
|
|
33
|
-
}
|
|
34
|
-
export declare function createReportBuilderAgent(deps: CreateReportBuilderAgentDeps): AgentDefinition;
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Report Builder Agent — in-process templating agent (no sandbox).
|
|
3
|
-
*
|
|
4
|
-
* Authors HTML reports via Jinja2 templating. Runs in the Cortex Node
|
|
5
|
-
* process — file IO is in-process via the `versionFs` tool roster, rendering
|
|
6
|
-
* is Node-side via Nunjucks (`build_report` tool), and visual validation
|
|
7
|
-
* uses headless Chrome via the `preview_snapshot` tool.
|
|
8
|
-
*
|
|
9
|
-
* The agent receives a complete brief from iterate-report's pre-flight
|
|
10
|
-
* (assets staged, columns parsed, sections fully composed) and has no
|
|
11
|
-
* discovery surface — its workspace is rooted at the version directory.
|
|
12
|
-
*
|
|
13
|
-
* NOT a sandbox-agent catalog member (per design Decision #2 — not
|
|
14
|
-
* plannable). The full tool roster is constructed inside the runner so
|
|
15
|
-
* each tool shares the runner's closure-captured outcome state and
|
|
16
|
-
* preview-dir paths; this factory just packages the result into a plain
|
|
17
|
-
* `AgentDefinition`.
|
|
18
|
-
*/
|
|
19
|
-
import { reportBuilderPrompt } from "../prompts/report-builder.js";
|
|
20
|
-
import { composeSystemPrompt } from "./system-prompt.js";
|
|
21
|
-
export const REPORT_BUILDER_AGENT_ID = "report-builder";
|
|
22
|
-
/** Skill pack the report-builder prompt directs the model to read (design-system reference). */
|
|
23
|
-
export const REPORT_BUILDER_SKILLS = ["report-html"];
|
|
24
|
-
/** Runaway guard — Jinja recovery may take several build/edit cycles. */
|
|
25
|
-
const REPORT_BUILDER_MAX_ITERATIONS = 75;
|
|
26
|
-
export function createReportBuilderAgent(deps) {
|
|
27
|
-
return {
|
|
28
|
-
id: REPORT_BUILDER_AGENT_ID,
|
|
29
|
-
systemPrompt: composeSystemPrompt(reportBuilderPrompt),
|
|
30
|
-
model: deps.model,
|
|
31
|
-
tools: deps.tools,
|
|
32
|
-
maxIterations: REPORT_BUILDER_MAX_ITERATIONS,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Report renderer — Node-side Nunjucks render of v{N}/report.html.j2 to v{N}/index.html.
|
|
3
|
-
*
|
|
4
|
-
* Stateless pure module: no harness imports, no env reads at import time,
|
|
5
|
-
* safe to run standalone in tests.
|
|
6
|
-
*/
|
|
7
|
-
export interface RenderReportOptions {
|
|
8
|
-
versionDir: string;
|
|
9
|
-
templatesDir: string;
|
|
10
|
-
}
|
|
11
|
-
export interface RenderReportResult {
|
|
12
|
-
ok: boolean;
|
|
13
|
-
indexBytes: number;
|
|
14
|
-
error?: {
|
|
15
|
-
kind: "missing-template" | "syntax" | "runtime";
|
|
16
|
-
message: string;
|
|
17
|
-
line?: number;
|
|
18
|
-
snippet?: string;
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Render `${versionDir}/report.html.j2` to `${versionDir}/index.html`.
|
|
23
|
-
*
|
|
24
|
-
* Loader resolution order: [versionDir, templatesDir]. `report.html.j2`
|
|
25
|
-
* lives in versionDir; `extends "base.html.j2"` and `include "components/..."`
|
|
26
|
-
* resolve from templatesDir. Autoescape is OFF — templates own escaping.
|
|
27
|
-
*
|
|
28
|
-
* `${templatesDir}/echarts-theme.json` is read, parsed, re-stringified, and
|
|
29
|
-
* passed as `echarts_theme` (a JSON string) for safe inlining inside a
|
|
30
|
-
* `<script>` block.
|
|
31
|
-
*
|
|
32
|
-
* Returns `{ ok: true, indexBytes }` on success. On failure, returns
|
|
33
|
-
* `{ ok: false, indexBytes: 0, error }` with `error.kind`:
|
|
34
|
-
* - "missing-template" — `report.html.j2` (or any `extends`/`include` target)
|
|
35
|
-
* not found by either loader. Message identifies the missing path.
|
|
36
|
-
* - "syntax" — Nunjucks parse error in any loaded template.
|
|
37
|
-
* - "runtime" — anything else thrown during render or write.
|
|
38
|
-
*/
|
|
39
|
-
export declare function renderReport(opts: RenderReportOptions): Promise<RenderReportResult>;
|