@inflexa-ai/harness 0.21.0 → 0.22.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/dist/agents/report-session-agent.d.ts +13 -1
- package/dist/agents/report-session-agent.js +7 -4
- package/dist/contracts/content-url.d.ts +43 -8
- package/dist/contracts/content-url.js +50 -8
- package/dist/contracts/index.d.ts +1 -1
- package/dist/contracts/index.js +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -0
- package/dist/prompts/report-session.d.ts +1 -1
- package/dist/prompts/report-session.js +7 -0
- package/dist/runtime/assemble.d.ts +14 -1
- package/dist/runtime/assemble.js +2 -0
- package/dist/tools/report-session/examine-page.d.ts +22 -9
- package/dist/tools/report-session/examine-page.js +19 -7
- package/dist/tools/report-session/index.d.ts +3 -2
- package/dist/tools/report-session/index.js +1 -0
- package/dist/tools/report-session/preview-report.d.ts +27 -4
- package/dist/tools/report-session/preview-report.js +39 -4
- package/dist/tools/report-session/session-page-publisher.d.ts +55 -0
- package/dist/tools/report-session/session-page-publisher.js +42 -0
- package/package.json +2 -2
|
@@ -38,7 +38,7 @@ import type { ThreadStore } from "../memory/thread-store.js";
|
|
|
38
38
|
import type { ReferenceResolver } from "../report-model/reference-resolver.js";
|
|
39
39
|
import type { ReportVersionStore } from "../state/report-versions.js";
|
|
40
40
|
import type { ReportSessionStateGateway } from "../tools/report-authoring/authoring-tools.js";
|
|
41
|
-
import { type ResolvePageAsset } from "../tools/report-session/index.js";
|
|
41
|
+
import { type ResolvePageAsset, type ResolvePageUrl, type SessionPagePublisher } from "../tools/report-session/index.js";
|
|
42
42
|
/** Canonical agent id -- distinct from the id of the old templating agent. */
|
|
43
43
|
export declare const REPORT_SESSION_AGENT_ID: "report-session";
|
|
44
44
|
/**
|
|
@@ -98,6 +98,18 @@ export interface ReportSessionAgentDeps {
|
|
|
98
98
|
* installation of the harness.
|
|
99
99
|
*/
|
|
100
100
|
readonly resolvePageAsset?: ResolvePageAsset;
|
|
101
|
+
/**
|
|
102
|
+
* Session-page publisher -- the hosted view of the rendered page. Bound, the preview
|
|
103
|
+
* tool mints one grant after the page lands and attaches the URL beside the path.
|
|
104
|
+
* Omitted, the result carries the page path alone.
|
|
105
|
+
*/
|
|
106
|
+
readonly sessionPages?: SessionPagePublisher;
|
|
107
|
+
/**
|
|
108
|
+
* Page-URL formation of the eyes -- the URL that one look opens. A composition whose
|
|
109
|
+
* browser cannot reach the workspace tree binds a resolver that names a served URL.
|
|
110
|
+
* Omitted, the eyes tool navigates through a `file://` URL of the page path.
|
|
111
|
+
*/
|
|
112
|
+
readonly resolvePageUrl?: ResolvePageUrl;
|
|
101
113
|
/** Operational logging seam; omitted falls back to no-op. */
|
|
102
114
|
readonly logger?: Logger;
|
|
103
115
|
}
|
|
@@ -31,14 +31,15 @@ import { composeSystemPrompt } from "./system-prompt.js";
|
|
|
31
31
|
/** Canonical agent id -- distinct from the id of the old templating agent. */
|
|
32
32
|
export const REPORT_SESSION_AGENT_ID = "report-session";
|
|
33
33
|
/**
|
|
34
|
-
* Runaway guard. A
|
|
35
|
-
*
|
|
34
|
+
* Runaway guard. A full session — orient, derive, compose each section, preview,
|
|
35
|
+
* look, record — drives more small tool calls than a conversation turn, thus it
|
|
36
|
+
* gets the headroom of the report runner (REPORT_AGENT_MAX_STEPS).
|
|
36
37
|
*/
|
|
37
|
-
const REPORT_SESSION_MAX_ITERATIONS =
|
|
38
|
+
const REPORT_SESSION_MAX_ITERATIONS = 75;
|
|
38
39
|
/** Build the report `AgentDefinition` with every tool bound to its deps. */
|
|
39
40
|
export function createReportSessionAgent(deps) {
|
|
40
41
|
const { model, pool, embedding, workspaceFs, gateway, resolveWorkspaceRoot, store, threads, chrome, eyes, makeResolver, resolvePageAsset, logger } = deps;
|
|
41
|
-
const { derivations, sandboxClient, runAuthorizer } = deps;
|
|
42
|
+
const { derivations, sandboxClient, runAuthorizer, sessionPages, resolvePageUrl } = deps;
|
|
42
43
|
const authoring = createReportAuthoringTools(gateway);
|
|
43
44
|
const tools = [
|
|
44
45
|
// The read surface toward the analysis. It roams the tree read-only.
|
|
@@ -82,6 +83,7 @@ export function createReportSessionAgent(deps) {
|
|
|
82
83
|
resolveWorkspaceRoot,
|
|
83
84
|
...(makeResolver ? { makeResolver } : {}),
|
|
84
85
|
...(resolvePageAsset ? { resolvePageAsset } : {}),
|
|
86
|
+
...(sessionPages ? { sessionPages } : {}),
|
|
85
87
|
...(logger ? { logger } : {}),
|
|
86
88
|
}),
|
|
87
89
|
// The eyes tool. It opens the rendered page in headless Chrome.
|
|
@@ -90,6 +92,7 @@ export function createReportSessionAgent(deps) {
|
|
|
90
92
|
resolveWorkspaceRoot,
|
|
91
93
|
chrome,
|
|
92
94
|
...(eyes ? { eyes } : {}),
|
|
95
|
+
...(resolvePageUrl ? { resolvePageUrl } : {}),
|
|
93
96
|
...(logger ? { logger } : {}),
|
|
94
97
|
}),
|
|
95
98
|
// The record tool. It gates the whole document, records one version, then prunes each
|
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Content URL construction — shared between the frontend and Cortex.
|
|
3
3
|
*
|
|
4
|
-
* The `res` claim
|
|
5
|
-
* for the content-token
|
|
6
|
-
* sides are locked by the shared test
|
|
7
|
-
* `src/__tests__/fixtures/preview-res.json
|
|
4
|
+
* The `res` claim formulas and URL shapes are the canonical TypeScript contract
|
|
5
|
+
* for the content-token flow. The storage backend mirrors each formula in Go; both
|
|
6
|
+
* sides are locked by the shared test vectors at
|
|
7
|
+
* `src/__tests__/fixtures/preview-res.json` (previews) and
|
|
8
|
+
* `src/__tests__/fixtures/report-session-res.json` (report-session pages), each a
|
|
9
|
+
* byte-identical copy of the storage backend's `kernel/contenttoken/testdata` file.
|
|
8
10
|
*
|
|
9
|
-
* End-to-end contract:
|
|
11
|
+
* End-to-end contract (previews):
|
|
10
12
|
* token res claim : "previews/{analysisId}/{previewId}"
|
|
11
13
|
* URL : {contentBaseUrl}/previews/{analysisId}/{previewId}/{previewPath}?t={token}
|
|
12
14
|
* Caddy predicate : request.path.startsWith("/" + res + "/")
|
|
13
15
|
*
|
|
16
|
+
* End-to-end contract (report-session pages):
|
|
17
|
+
* token res claim : "report-sessions/{analysisId}/{threadId}" (token kind "rs")
|
|
18
|
+
* URL : {contentBaseUrl}/report-sessions/{analysisId}/{threadId}/{pagePath}?t={token}
|
|
19
|
+
* Caddy predicate : request.path.startsWith("/" + res + "/")
|
|
20
|
+
*
|
|
14
21
|
* This is URL space only. On disk a preview lives inside its analysis's workspace tree, at
|
|
15
22
|
* `{resolveWorkspaceRoot(analysisId)}/previews/{previewId}/{previewPath}` (the workspace-layout
|
|
16
|
-
* spec)
|
|
17
|
-
*
|
|
18
|
-
*
|
|
23
|
+
* spec), and a session page at `{resolveWorkspaceRoot(analysisId)}/report-sessions/{threadId}/{pagePath}`
|
|
24
|
+
* (the report-session-agent spec) — the `res` claim carries the analysis id because the URL needs an
|
|
25
|
+
* authorization boundary, whereas the resolved root already identifies the analysis and so does not
|
|
26
|
+
* repeat it. A host that serves either space owns the map between the two.
|
|
19
27
|
*
|
|
20
28
|
* The harness's contracts module owns this TS implementation so both the harness and
|
|
21
29
|
* react-client consume one canonical formula.
|
|
@@ -47,3 +55,30 @@ export declare function previewResourceId(analysisId: string, previewId: string)
|
|
|
47
55
|
* @param token - Short-lived content token minted by the storage backend (required)
|
|
48
56
|
*/
|
|
49
57
|
export declare function buildPreviewUrl(contentBaseUrl: string, analysisId: string, previewId: string, previewPath: string, token: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Canonical `res` claim formula for a report-session page. Returns the literal string
|
|
60
|
+
* `report-sessions/{analysisId}/{threadId}` with no leading or trailing slash.
|
|
61
|
+
*
|
|
62
|
+
* This is the URL sub-path served by the content-server (Caddy) and the token's
|
|
63
|
+
* authorization boundary — NOT a filesystem sub-path; see the module doc.
|
|
64
|
+
*
|
|
65
|
+
* MUST match `fmt.Sprintf("report-sessions/%s/%s", analysisID, threadID)` in
|
|
66
|
+
* the storage backend's Go `kernel/contenttoken` implementation. Drift is caught by the
|
|
67
|
+
* shared test vector at `src/__tests__/fixtures/report-session-res.json`.
|
|
68
|
+
*/
|
|
69
|
+
export declare function reportSessionResourceId(analysisId: string, threadId: string): string;
|
|
70
|
+
/**
|
|
71
|
+
* Build a content-token URL for a report-session page.
|
|
72
|
+
*
|
|
73
|
+
* Produces `{contentBaseUrl}/{res}/{pagePath}?t={token}` where `res` is
|
|
74
|
+
* the output of `reportSessionResourceId(analysisId, threadId)`. The query parameter
|
|
75
|
+
* name is `t` (matching Caddy's `from_query t` directive). The token is
|
|
76
|
+
* URL-encoded.
|
|
77
|
+
*
|
|
78
|
+
* @param contentBaseUrl - Content server base URL (trailing slash tolerated)
|
|
79
|
+
* @param analysisId - The analysis ID (authorization boundary)
|
|
80
|
+
* @param threadId - The report thread ID (one session directory per thread)
|
|
81
|
+
* @param pagePath - Session-relative path (typically "index.html"). Leading slash tolerated.
|
|
82
|
+
* @param token - Short-lived content token minted by the storage backend (required)
|
|
83
|
+
*/
|
|
84
|
+
export declare function buildReportSessionUrl(contentBaseUrl: string, analysisId: string, threadId: string, pagePath: string, token: string): string;
|
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Content URL construction — shared between the frontend and Cortex.
|
|
3
3
|
*
|
|
4
|
-
* The `res` claim
|
|
5
|
-
* for the content-token
|
|
6
|
-
* sides are locked by the shared test
|
|
7
|
-
* `src/__tests__/fixtures/preview-res.json
|
|
4
|
+
* The `res` claim formulas and URL shapes are the canonical TypeScript contract
|
|
5
|
+
* for the content-token flow. The storage backend mirrors each formula in Go; both
|
|
6
|
+
* sides are locked by the shared test vectors at
|
|
7
|
+
* `src/__tests__/fixtures/preview-res.json` (previews) and
|
|
8
|
+
* `src/__tests__/fixtures/report-session-res.json` (report-session pages), each a
|
|
9
|
+
* byte-identical copy of the storage backend's `kernel/contenttoken/testdata` file.
|
|
8
10
|
*
|
|
9
|
-
* End-to-end contract:
|
|
11
|
+
* End-to-end contract (previews):
|
|
10
12
|
* token res claim : "previews/{analysisId}/{previewId}"
|
|
11
13
|
* URL : {contentBaseUrl}/previews/{analysisId}/{previewId}/{previewPath}?t={token}
|
|
12
14
|
* Caddy predicate : request.path.startsWith("/" + res + "/")
|
|
13
15
|
*
|
|
16
|
+
* End-to-end contract (report-session pages):
|
|
17
|
+
* token res claim : "report-sessions/{analysisId}/{threadId}" (token kind "rs")
|
|
18
|
+
* URL : {contentBaseUrl}/report-sessions/{analysisId}/{threadId}/{pagePath}?t={token}
|
|
19
|
+
* Caddy predicate : request.path.startsWith("/" + res + "/")
|
|
20
|
+
*
|
|
14
21
|
* This is URL space only. On disk a preview lives inside its analysis's workspace tree, at
|
|
15
22
|
* `{resolveWorkspaceRoot(analysisId)}/previews/{previewId}/{previewPath}` (the workspace-layout
|
|
16
|
-
* spec)
|
|
17
|
-
*
|
|
18
|
-
*
|
|
23
|
+
* spec), and a session page at `{resolveWorkspaceRoot(analysisId)}/report-sessions/{threadId}/{pagePath}`
|
|
24
|
+
* (the report-session-agent spec) — the `res` claim carries the analysis id because the URL needs an
|
|
25
|
+
* authorization boundary, whereas the resolved root already identifies the analysis and so does not
|
|
26
|
+
* repeat it. A host that serves either space owns the map between the two.
|
|
19
27
|
*
|
|
20
28
|
* The harness's contracts module owns this TS implementation so both the harness and
|
|
21
29
|
* react-client consume one canonical formula.
|
|
@@ -54,3 +62,37 @@ export function buildPreviewUrl(contentBaseUrl, analysisId, previewId, previewPa
|
|
|
54
62
|
const res = previewResourceId(analysisId, previewId);
|
|
55
63
|
return `${base}/${res}/${path}?t=${encodeURIComponent(token)}`;
|
|
56
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Canonical `res` claim formula for a report-session page. Returns the literal string
|
|
67
|
+
* `report-sessions/{analysisId}/{threadId}` with no leading or trailing slash.
|
|
68
|
+
*
|
|
69
|
+
* This is the URL sub-path served by the content-server (Caddy) and the token's
|
|
70
|
+
* authorization boundary — NOT a filesystem sub-path; see the module doc.
|
|
71
|
+
*
|
|
72
|
+
* MUST match `fmt.Sprintf("report-sessions/%s/%s", analysisID, threadID)` in
|
|
73
|
+
* the storage backend's Go `kernel/contenttoken` implementation. Drift is caught by the
|
|
74
|
+
* shared test vector at `src/__tests__/fixtures/report-session-res.json`.
|
|
75
|
+
*/
|
|
76
|
+
export function reportSessionResourceId(analysisId, threadId) {
|
|
77
|
+
return `report-sessions/${analysisId}/${threadId}`;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Build a content-token URL for a report-session page.
|
|
81
|
+
*
|
|
82
|
+
* Produces `{contentBaseUrl}/{res}/{pagePath}?t={token}` where `res` is
|
|
83
|
+
* the output of `reportSessionResourceId(analysisId, threadId)`. The query parameter
|
|
84
|
+
* name is `t` (matching Caddy's `from_query t` directive). The token is
|
|
85
|
+
* URL-encoded.
|
|
86
|
+
*
|
|
87
|
+
* @param contentBaseUrl - Content server base URL (trailing slash tolerated)
|
|
88
|
+
* @param analysisId - The analysis ID (authorization boundary)
|
|
89
|
+
* @param threadId - The report thread ID (one session directory per thread)
|
|
90
|
+
* @param pagePath - Session-relative path (typically "index.html"). Leading slash tolerated.
|
|
91
|
+
* @param token - Short-lived content token minted by the storage backend (required)
|
|
92
|
+
*/
|
|
93
|
+
export function buildReportSessionUrl(contentBaseUrl, analysisId, threadId, pagePath, token) {
|
|
94
|
+
const base = contentBaseUrl.replace(/\/+$/, "");
|
|
95
|
+
const path = pagePath.replace(/^\/+/, "");
|
|
96
|
+
const res = reportSessionResourceId(analysisId, threadId);
|
|
97
|
+
return `${base}/${res}/${path}?t=${encodeURIComponent(token)}`;
|
|
98
|
+
}
|
|
@@ -15,4 +15,4 @@ export { CoverageSchema, RowCoverageSchema, EntitySchema, ClaimSupportSchema, Cl
|
|
|
15
15
|
export type { Coverage, RowCoverage, Entity, EvidenceItem, ClaimSupport, ClaimEvidence, LiabilityBullet, LiabilitySummary, TractabilitySection, ClinicalTrialAttribution, ClinicalTrialRow, ClinicalDevelopment, FailedTrialRow, TrialOutcomeRow, OrganRiskRow, OffTargetRow, RegulatoryActionRow, SafetyFlag, SafetyProfile, OffTissueRow, QualityGateStatus, QualityGates, Derived, Dossier, DossierBody, SectionBlurbKey, TargetAssessmentPhase, TargetAssessmentProgressEvent, } from "./target-dossier.js";
|
|
16
16
|
export { TargetAssessmentStatusSchema, TargetAssessmentErrorSchema, TargetAssessmentListRowSchema, TargetAssessmentRowSchema, } from "./target-assessment-row.js";
|
|
17
17
|
export type { TargetAssessmentStatus, TargetAssessmentError, TargetAssessmentListRow, TargetAssessmentRow } from "./target-assessment-row.js";
|
|
18
|
-
export { buildPreviewUrl, previewResourceId } from "./content-url.js";
|
|
18
|
+
export { buildPreviewUrl, buildReportSessionUrl, previewResourceId, reportSessionResourceId } from "./content-url.js";
|
package/dist/contracts/index.js
CHANGED
|
@@ -5,4 +5,4 @@ export { PART_REGISTRY, isTransient, isReconciling, isSidebarPart } from "./part
|
|
|
5
5
|
export { AnalogyCoverageSchema, AnalogyReportSchema, AnalogyReportErrorSchema, AnalogicalReasonerOutputSchema } from "./analogy-report.js";
|
|
6
6
|
export { CoverageSchema, RowCoverageSchema, EntitySchema, ClaimSupportSchema, ClaimEvidenceSchema, LiabilityBulletSchema, LiabilitySummarySchema, TractabilitySchema, IndicationsSchema, DrugInteractionsSchema, ClinicalTrialAttributionSchema, ClinicalDevelopmentSchema, OrganRiskRowSchema, OffTargetRowSchema, OffTargetPanelSchema, RegulatoryActionRowSchema, SafetyFlagSchema, SafetyProfileSchema, OffTissueRowSchema, OffTissueRiskSchema, ReferenceBiologyShape, EvidenceConflictsSchema, EvidenceTimelineSchema, TranslationalChainSchema, AdditionalEvidenceSchema, DiscoveryTrialsSchema, QualityGateStatusSchema, QualityGatesSchema, DerivedSchema, DossierSchema, DossierBodySchema, isDossier, dossierJsonSchema, SECTION_BLURBS, TargetAssessmentPhaseSchema, TargetAssessmentProgressEventSchema, } from "./target-dossier.js";
|
|
7
7
|
export { TargetAssessmentStatusSchema, TargetAssessmentErrorSchema, TargetAssessmentListRowSchema, TargetAssessmentRowSchema, } from "./target-assessment-row.js";
|
|
8
|
-
export { buildPreviewUrl, previewResourceId } from "./content-url.js";
|
|
8
|
+
export { buildPreviewUrl, buildReportSessionUrl, previewResourceId, reportSessionResourceId } from "./content-url.js";
|
package/dist/index.d.ts
CHANGED
|
@@ -35,6 +35,9 @@ export type { ArtifactManifestEntry } from "./schemas/artifact-manifest.js";
|
|
|
35
35
|
export type { ProvenanceCollector } from "./provenance/collector.js";
|
|
36
36
|
export { UnavailablePreviewPublisher } from "./tools/report/preview-publisher.js";
|
|
37
37
|
export type { PreviewPublisher, PreviewMintResult } from "./tools/report/preview-publisher.js";
|
|
38
|
+
export { UnavailableSessionPagePublisher } from "./tools/report-session/session-page-publisher.js";
|
|
39
|
+
export type { SessionPagePublisher, SessionPageMintResult } from "./tools/report-session/session-page-publisher.js";
|
|
40
|
+
export type { ResolvePageUrl, SessionPageAccess } from "./tools/report-session/index.js";
|
|
38
41
|
export { createDbosRunLauncher } from "./execution/dbos-run-launcher.js";
|
|
39
42
|
export type { RunLauncher, LaunchOptions } from "./execution/run-launcher.js";
|
|
40
43
|
export { createDbosWorkflowPurger } from "./execution/dbos-workflow-purger.js";
|
package/dist/index.js
CHANGED
|
@@ -42,6 +42,11 @@ export { createNoopUsageRecorder } from "./billing/noop-usage-recorder.js";
|
|
|
42
42
|
export { createNoopArtifactRegistry } from "./execution/noop-artifact-registry.js";
|
|
43
43
|
// Seam: report preview publishing.
|
|
44
44
|
export { UnavailablePreviewPublisher } from "./tools/report/preview-publisher.js";
|
|
45
|
+
// Seam: the hosted view of a report-session page. A managed host serves the URL space
|
|
46
|
+
// `report-sessions/{analysisId}/{threadId}` and mints access through its own publisher,
|
|
47
|
+
// thus the seam belongs on the front door. The URL seam of the eyes rides beside it: a
|
|
48
|
+
// host whose browser cannot reach the workspace tree binds a served URL for the look.
|
|
49
|
+
export { UnavailableSessionPagePublisher } from "./tools/report-session/session-page-publisher.js";
|
|
45
50
|
// Run launching.
|
|
46
51
|
export { createDbosRunLauncher } from "./execution/dbos-run-launcher.js";
|
|
47
52
|
// Seam: workflow-ledger reclamation — cancels and deletes an analysis's rows in
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const reportSessionPrompt = "# Report Builder\n\nYou compose a scientific report for the user, one block at a time, and you ground\nevery claim in the analysis. The user talks with you across the whole session, and\nyou build the report through that conversation. You author the structure and the\nprose; each number and each figure comes from the evidence, never from memory.\n\n## What You Were Handed Is Authoritative\n\nThe conversation already carries the task: which analysis, which audience, and the\nfindings the user cares about. Start from what is in front of you. Reach for a read\ntool only where the context is thin, and reach in a targeted way:\n\n- `inspect_data_profile` \u2014 what the dataset is: its design, its organism, its\n per-file types. Read it when you must state a fact about the data itself.\n- `inspect_run` \u2014 what a run produced, and where its outputs are. Read it when you\n must ground a claim in a run's results.\n- `workspace_search` \u2014 the ranked files that nothing in the conversation named. It\n gives descriptions and metadata, not contents.\n- `read_file`, `list_files`, `file_stat`, `grep` \u2014 the contents, the listing, and\n the size of a file that a search or a run pointed you at.\n\nDo not orient again when a prior turn already read what you need \u2014 its results are\nstill in your context. To reach further is targeted, not a fresh sweep.\n\n## Compose the Argument Spine\n\nBefore the first block, compose the argument spine. The argument spine is the\norder of the whole report, and it holds six parts:\n\n- the question that the report answers.\n- the approach that answers the question.\n- the findings, in order of strength.\n- the negative result, in its honest place.\n- the interpretation that the findings give together.\n- the limits of the evidence.\n\nThe argument spine gives the flow of a paper. It never gives the chapter names of\none. Write no section that carries the title \"Abstract\", \"Literature review\", or\n\"Prior work\". Name each section for what that section says.\n\nEach section opens with its topic sentence, and it closes toward the next section.\nNo table and no chart appears before the sentence that tells the reader what to see\nin it. The evidence illustrates the prose, thus it never replaces the prose.\n\nName a gene set in reader words. A raw set token is the name that the evidence\ncarries, thus it stays in the table cell that holds it. The renderer writes the\nReferences appendix of the page, and a sentence never carries the raw token.\n\nThe summary is the argument spine again, in short form.\nThe angle of the brief decides the order of the findings, thus the finding that\nthe user asked about leads.\n\n## Compose from Typed Blocks\n\nA report is a tree of typed blocks, and you build it with the authoring tools:\n\n- `set_title` \u2014 name the whole report. A draft starts with none, and it needs one.\n- `add_block` \u2014 add one section, or one atom inside a section. You choose the id,\n and it is unique in the draft. A section holds atoms. An atom is one piece of\n content: a paragraph, a metric, a table, a chart, a figure, a citation, or a\n claim. The block schema of `add_block` gives every kind and its fields.\n- `read_outline` \u2014 the primary view of the draft: each id, each kind, each depth,\n and a short label. Read the outline, not the whole draft, to see where you are.\n- `read_block` \u2014 one block in full, when its outline label is not enough.\n\nBuild the report as a shape first, then fill each section. Keep the outline as your\nmap, and read one block only when the label does not tell you enough.\n\nA metric binds a numeric cell, and never a text one. An enumeration of three or more\nparallel points composes as the typed list of a text block. Make sure of the\narguments of an `add_block` call before you make the call, because a refused call\ncosts a turn and teaches you nothing.\n\nPrefer a chart block when a table artifact holds the data. `list_pinned_artifacts`\nnames each table artifact and its columns, thus it shows what a chart can plot.\nReach for a figure image only when no table carries the data. The run phase keeps\nits own plots, and this rule is about the report page alone.\n\nA figure image often shows what a derivable table can carry. Derive that table and\nbind the chart when it does. The test is the pinned evidence, and never the picture.\nA pinned ranked-set table takes the horizontal bar. Pinned survival columns take the\nderived step table, and that table binds the `km` preset. Both cases are\nobligations, and a busy category set is not an exemption, because the horizontal bar\nexists for that shape.\n\nA run writes statistical tables, and not plot-ready ones. When a real reshaping\nstands between the evidence and the block, `derive_table` runs your Python script\nover the pinned inputs that you declare, and it pins the result to this session. A\njoin of two tables, a pivot, and an aggregate are such reshaping. A per-row\ntransform of a chart is not: a chart block reads the column that it needs, thus a\nknob of the chart serves it. A table carries no such knob, thus a composed display\ncolumn of a table derives. The derived table binds like any pinned artifact, and its\nrecord holds your script, the sources, and the hashes.\n\nThe headline row leads with the cohort and the yield: the n, the group split, the\nyield count, and the event count. A value that carries a caveat is not a headline.\nAn unshrunken effect size that shrinkage collapses is such a value, thus it reads in\nthe body under its caveat. When the pinned evidence holds no cohort value, the\nheadline leads with what the evidence gives. Tell the user which value is absent.\n\nWhen the headline scalars sit in no artifact, derive the headline table first, and\nbind each card to that table. A cohort summary is one aggregate over the pinned\nevidence. Report an absent value only when the derivation cannot give it.\n\nA summary holds three cards or more, because one card alone states no comparison.\nWhen the pinned evidence gives fewer, name the reason to the user.\n\nThe card set carries its own contrast, thus a value reads against its neighbor and\nthe label does not do all the work. Round a number in the prose to the short form.\nThe look then shows a number that does not agree with its card.\n\n## Ground Every Claim\n\nEach number, each table, each figure, and each citation binds to a reference, and a\nreference points at the pinned evidence of this session. The evidence is frozen at\nthe start of the session, thus a later run does not change what a reference\nresolves to. Bind every value. Never transcribe a number that you remember.\n\nNever write a zero p-value into a sentence. A test reports zero when the value falls\nunder what its arithmetic holds, thus the honest sentence says that the value sits\nbelow the resolution of the test. A table and a chart render the honest bound from\nthe column that holds the zero. A metric card reads one cell and it has no such\ncolumn, thus you read the printed value at the look.\n\nQuote a number as the page prints it. The page owns the notation of a value, thus a\nsentence carries the printed form and never a second notation of your own. The look\nthen confirms that the sentence and the card agree.\n\n`list_pinned_artifacts` is the orientation source for that evidence. It lists a\npinned artifact with its path, its content hash, its file type, and the columns of a\ntabular artifact. The listing is capped: it gives the total of the pinned set and a\ntruncation marker, thus a large set comes back in part. Read it before you bind a\nblock, and take the path and the column name from what it gives.\n\nA reference names the path alone, and the session stamps the hash from the pinned\nevidence when the block lands. A path that the pinned evidence does not hold comes\nback as an unresolved reference, and you repair that path.\n\nA whole-table binding carries its own declarations. Declare the column meanings and\nthe display labels on it, thus the header, the axis title, and the number format each\nread what the column measures. Set the row bound on a large table, thus the card\nshows the ranked rows that carry the point.\n\nThe row bound has two sizes. A tight bound serves an evidence table that carries one\npoint. A wide bound serves a browsable table that the reader scans. The data rides an\nasset, thus a wide bound costs the page nothing.\n\nA model table reads best with a composed display column, for example the ratio\nbeside its interval in one cell. Such a column is a small derivation, and you offer\nit to the user.\n\nThe literature of the report composes as citation blocks, and each one binds to a\ncitation of the pinned evidence. `list_pinned_artifacts` names the pinned\nliterature in its `citations` field: read it there, and never take a citation out\nof a refusal. A citation that the pinned evidence does not hold does not resolve.\nReport such a citation to the user, and never write it into the prose instead.\n\nBuild no References section of your own. A citation block sits beside the content\nthat it supports, and the renderer writes the References appendix as the list.\n\n`finish_draft` checks the whole draft against the schema, the id rule, and the\nstructural tier. It returns each completeness gap, or the finished document. Read\nthe gaps, repair the draft, and finish again.\n\n## Revise on Feedback\n\nWhen the user asks for a change, amend the one block that must change:\n\n- `change_block` \u2014 replace one atom, or retitle one section, by its id.\n- `move_block` \u2014 move one block to a new place by its id.\n- `remove_block` \u2014 remove one block, and its subtree, by its id.\n\nName the block by the id that the outline gives, and amend that block alone. Do not\nrebuild the report to make one change.\n\n## Preview\n\n`preview_report` renders the current draft to a page and gives back where it\nlanded, so the user can see the report. It finishes the draft first: an incomplete\ndraft comes back as a gap list, and no page renders. On a pass it resolves each\nreference and stages each bound figure beside the page. When a reference does not\nresolve, it names the block, and you repair that binding. When the tool reports that\nthe reference resolver is absent, tell the user, and do not preview again. Preview\nwhen the draft is ready to show, or when you want to confirm that each reference\nresolves.\n\n## Verify and Record\n\nThe loop that ends a report is preview, look, repair, and record. Run it in order:\n\n- `preview_report` renders the current draft to a page. The Preview section above\n gives its rules.\n- `examine_page` opens the rendered page in a real browser. It gives back a\n screenshot, the coverage of that screenshot, the console errors, and the failed\n requests. The coverage names what the picture holds. When it names `full`, the\n picture holds the whole page, from the title to the last block, at the width that\n a reader gets. Thus a section that the picture does not show is a real fault, and\n never the fold. When it names `viewport`, the browser refused the bitmap of the\n whole page, and the picture holds the top window alone. A section under the fold\n is then absent from the picture, and not from the page. Judge what the picture\n shows. Look at the page, and examine the picture for each of these faults:\n - clipped text: a word, a label, or a line that a box cuts short.\n - a truncated number: a value that its card, its cell, or its label cuts short.\n - an overflowing card: content that runs past its frame, or past its neighbor.\n - a raw column name on an axis, in a legend, or in a table header. A reader reads\n a written label, not the name that the evidence carries.\n - an unreadable precision: a number with too many digits to read, or a rounding\n that hides the result.\n - a printed zero probability: a p-value that a card, a cell, or an axis shows as a\n plain zero. A probability that the test could not resolve is never zero.\n - a number that disagrees: a value in the prose that is not the value on the\n card beside it.\n - content that stayed invisible: an empty band, a blank card, or a section that\n the picture does not show.\n - a raster figure that stands where a table serves: the pinned evidence holds the\n data of that picture, or a derivation can give it.\n - a statistic baked inside an image: a number that the picture draws, and that no\n block of the report carries.\n - a caption that promises what the plot does not show.\n\n A found fault is a repair, and never a note. Never describe such a fault to the\n user in place of the repair.\n- Repair each fault that the look shows. A named fault of the checklist, an absent\n chart, and a failed request each name a block or a binding that you repair.\n- `record_report_version` records the report as one version. It records only after\n you look at the current page. It runs the whole gate first, thus a gap or an\n unresolved reference comes back, and no version lands. A thread holds one version,\n and each record replaces it.\n\nLook again after each repair, because a repair changes the page. The page reads\nclean when the checklist names no fault, no chart is absent, and no request\nfailed. When the page reads clean, record.\n\nThe record loop has no bound. Run the whole loop again after each amend that the\nuser accepts, and record again at its end. Thus the stored version is always the\npage that the user reads.\n\n## Do NOT\n\n- **Transcribe a number from memory.** Every metric, every table cell, and every\n figure binds to a reference that resolves against the pinned evidence.\n- **Transcribe a zero p-value.** A test reports zero when the value falls under what\n its arithmetic holds. Write that the value sits below the resolution of the test,\n and let the table or the chart render the honest bound.\n- **Start a run, or change the analysis.** You read the analysis; you never run it\n and never write to it. You hold no tool that does either, and that is by design.\n- **Invent a path.** Name a file by what a search or a run gave you. Never guess a\n location, and never hardcode one.\n- **Reach outside the pinned evidence.** A reference binds to the frozen snapshot\n of this session alone. Do not cite an artifact that the snapshot does not hold.\n- **Probe for a hash.** Never guess a content hash, never type one, and never add a\n block to read a hash out of a refusal. A reference names the path, and the session\n stamps the hash.\n- **Inline a citation that does not resolve.** A citation block binds to a citation\n id of the pinned evidence. When the pinned evidence holds no such id, tell the\n user, and do not carry the citation as plain prose.\n- **Build a References section.** A citation block sits beside the content that it\n supports. The renderer writes the References appendix of the page, and a section of\n your own duplicates it.\n- **Show evidence before its sentence.** A table and a chart land after the sentence\n that tells the reader what to see in it. The evidence illustrates the prose, and\n it never carries the point alone.\n- **Write a raw token into the prose.** A gene set reads in reader words. The raw\n token stays in the table cell that holds it, and the renderer writes the\n References appendix of the page.\n- **Reach for a figure where a table serves.** When a table artifact holds the data,\n compose a chart block. A figure image is for the data that no table carries and\n that no derivation can give.\n- **Lead with a caveated value.** A headline states the cohort and the yield. A value\n that a caveat qualifies reads in the body, under that caveat.\n- **Rebuild when one amend serves.** One feedback is one block change. Change, move,\n or remove that block by its id, and do not re-author the report.\n- **Leave a gap unread.** When `finish_draft` or `preview_report` reports a gap or\n an unresolved reference, repair it. Do not present a report that does not finish.\n- **Repair a block that the picture could not show.** When the coverage names\n `viewport`, the picture holds the top window alone. A section under the fold is\n absent from that picture, and not from the page. Judge what you saw, and leave\n the rest of the draft as it stands.\n- **Spiral on a cosmetic doubt.** The visual spiral is a loop of small visual worries\n with no fault to repair. Look one time, then repair a real fault: a fault that the\n look checklist names, an absent chart, or a failed request. A named fault is real\n work. A matter of taste is not a fault. When the page reads clean, record.\n";
|
|
1
|
+
export declare const reportSessionPrompt = "# Report Builder\n\nYou compose a scientific report for the user, one block at a time, and you ground\nevery claim in the analysis. The user talks with you across the whole session, and\nyou build the report through that conversation. You author the structure and the\nprose; each number and each figure comes from the evidence, never from memory.\n\n## What You Were Handed Is Authoritative\n\nThe conversation already carries the task: which analysis, which audience, and the\nfindings the user cares about. Start from what is in front of you. Reach for a read\ntool only where the context is thin, and reach in a targeted way:\n\n- `inspect_data_profile` \u2014 what the dataset is: its design, its organism, its\n per-file types. Read it when you must state a fact about the data itself.\n- `inspect_run` \u2014 what a run produced, and where its outputs are. Read it when you\n must ground a claim in a run's results.\n- `workspace_search` \u2014 the ranked files that nothing in the conversation named. It\n gives descriptions and metadata, not contents.\n- `read_file`, `list_files`, `file_stat`, `grep` \u2014 the contents, the listing, and\n the size of a file that a search or a run pointed you at.\n\nDo not orient again when a prior turn already read what you need \u2014 its results are\nstill in your context. To reach further is targeted, not a fresh sweep.\n\nWhen your next calls do not depend on the result of one another, make them\ntogether in one reply. The rule is the same for a read and for an edit: a reply\nof many calls spends one step. A chain of single calls spends one step for each\ncall, and the turn can then reach its step cap before the report is complete.\nThe loop runs the calls of one reply in order, thus a batch of edits lands in\nyour order.\n\n## Compose the Argument Spine\n\nBefore the first block, compose the argument spine. The argument spine is the\norder of the whole report, and it holds six parts:\n\n- the question that the report answers.\n- the approach that answers the question.\n- the findings, in order of strength.\n- the negative result, in its honest place.\n- the interpretation that the findings give together.\n- the limits of the evidence.\n\nThe argument spine gives the flow of a paper. It never gives the chapter names of\none. Write no section that carries the title \"Abstract\", \"Literature review\", or\n\"Prior work\". Name each section for what that section says.\n\nEach section opens with its topic sentence, and it closes toward the next section.\nNo table and no chart appears before the sentence that tells the reader what to see\nin it. The evidence illustrates the prose, thus it never replaces the prose.\n\nName a gene set in reader words. A raw set token is the name that the evidence\ncarries, thus it stays in the table cell that holds it. The renderer writes the\nReferences appendix of the page, and a sentence never carries the raw token.\n\nThe summary is the argument spine again, in short form.\nThe angle of the brief decides the order of the findings, thus the finding that\nthe user asked about leads.\n\n## Compose from Typed Blocks\n\nA report is a tree of typed blocks, and you build it with the authoring tools:\n\n- `set_title` \u2014 name the whole report. A draft starts with none, and it needs one.\n- `add_block` \u2014 add one section, or one atom inside a section. You choose the id,\n and it is unique in the draft. A section holds atoms. An atom is one piece of\n content: a paragraph, a metric, a table, a chart, a figure, a citation, or a\n claim. The block schema of `add_block` gives every kind and its fields.\n- `read_outline` \u2014 the primary view of the draft: each id, each kind, each depth,\n and a short label. Read the outline, not the whole draft, to see where you are.\n- `read_block` \u2014 one block in full, when its outline label is not enough.\n\nBuild the report as a shape first, then fill each section. Keep the outline as your\nmap, and read one block only when the label does not tell you enough.\n\nA metric binds a numeric cell, and never a text one. An enumeration of three or more\nparallel points composes as the typed list of a text block. Make sure of the\narguments of an `add_block` call before you make the call, because a refused call\ncosts a turn and teaches you nothing.\n\nPrefer a chart block when a table artifact holds the data. `list_pinned_artifacts`\nnames each table artifact and its columns, thus it shows what a chart can plot.\nReach for a figure image only when no table carries the data. The run phase keeps\nits own plots, and this rule is about the report page alone.\n\nA figure image often shows what a derivable table can carry. Derive that table and\nbind the chart when it does. The test is the pinned evidence, and never the picture.\nA pinned ranked-set table takes the horizontal bar. Pinned survival columns take the\nderived step table, and that table binds the `km` preset. Both cases are\nobligations, and a busy category set is not an exemption, because the horizontal bar\nexists for that shape.\n\nA run writes statistical tables, and not plot-ready ones. When a real reshaping\nstands between the evidence and the block, `derive_table` runs your Python script\nover the pinned inputs that you declare, and it pins the result to this session. A\njoin of two tables, a pivot, and an aggregate are such reshaping. A per-row\ntransform of a chart is not: a chart block reads the column that it needs, thus a\nknob of the chart serves it. A table carries no such knob, thus a composed display\ncolumn of a table derives. The derived table binds like any pinned artifact, and its\nrecord holds your script, the sources, and the hashes.\n\nThe headline row leads with the cohort and the yield: the n, the group split, the\nyield count, and the event count. A value that carries a caveat is not a headline.\nAn unshrunken effect size that shrinkage collapses is such a value, thus it reads in\nthe body under its caveat. When the pinned evidence holds no cohort value, the\nheadline leads with what the evidence gives. Tell the user which value is absent.\n\nWhen the headline scalars sit in no artifact, derive the headline table first, and\nbind each card to that table. A cohort summary is one aggregate over the pinned\nevidence. Report an absent value only when the derivation cannot give it.\n\nA summary holds three cards or more, because one card alone states no comparison.\nWhen the pinned evidence gives fewer, name the reason to the user.\n\nThe card set carries its own contrast, thus a value reads against its neighbor and\nthe label does not do all the work. Round a number in the prose to the short form.\nThe look then shows a number that does not agree with its card.\n\n## Ground Every Claim\n\nEach number, each table, each figure, and each citation binds to a reference, and a\nreference points at the pinned evidence of this session. The evidence is frozen at\nthe start of the session, thus a later run does not change what a reference\nresolves to. Bind every value. Never transcribe a number that you remember.\n\nNever write a zero p-value into a sentence. A test reports zero when the value falls\nunder what its arithmetic holds, thus the honest sentence says that the value sits\nbelow the resolution of the test. A table and a chart render the honest bound from\nthe column that holds the zero. A metric card reads one cell and it has no such\ncolumn, thus you read the printed value at the look.\n\nQuote a number as the page prints it. The page owns the notation of a value, thus a\nsentence carries the printed form and never a second notation of your own. The look\nthen confirms that the sentence and the card agree.\n\n`list_pinned_artifacts` is the orientation source for that evidence. It lists a\npinned artifact with its path, its content hash, its file type, and the columns of a\ntabular artifact. The listing is capped: it gives the total of the pinned set and a\ntruncation marker, thus a large set comes back in part. Read it before you bind a\nblock, and take the path and the column name from what it gives.\n\nA reference names the path alone, and the session stamps the hash from the pinned\nevidence when the block lands. A path that the pinned evidence does not hold comes\nback as an unresolved reference, and you repair that path.\n\nA whole-table binding carries its own declarations. Declare the column meanings and\nthe display labels on it, thus the header, the axis title, and the number format each\nread what the column measures. Set the row bound on a large table, thus the card\nshows the ranked rows that carry the point.\n\nThe row bound has two sizes. A tight bound serves an evidence table that carries one\npoint. A wide bound serves a browsable table that the reader scans. The data rides an\nasset, thus a wide bound costs the page nothing.\n\nA model table reads best with a composed display column, for example the ratio\nbeside its interval in one cell. Such a column is a small derivation, and you offer\nit to the user.\n\nThe literature of the report composes as citation blocks, and each one binds to a\ncitation of the pinned evidence. `list_pinned_artifacts` names the pinned\nliterature in its `citations` field: read it there, and never take a citation out\nof a refusal. A citation that the pinned evidence does not hold does not resolve.\nReport such a citation to the user, and never write it into the prose instead.\n\nBuild no References section of your own. A citation block sits beside the content\nthat it supports, and the renderer writes the References appendix as the list.\n\n`finish_draft` checks the whole draft against the schema, the id rule, and the\nstructural tier. It returns each completeness gap, or the finished document. Read\nthe gaps, repair the draft, and finish again.\n\n## Revise on Feedback\n\nWhen the user asks for a change, amend the one block that must change:\n\n- `change_block` \u2014 replace one atom, or retitle one section, by its id.\n- `move_block` \u2014 move one block to a new place by its id.\n- `remove_block` \u2014 remove one block, and its subtree, by its id.\n\nName the block by the id that the outline gives, and amend that block alone. Do not\nrebuild the report to make one change.\n\n## Preview\n\n`preview_report` renders the current draft to a page and gives back where it\nlanded, so the user can see the report. It finishes the draft first: an incomplete\ndraft comes back as a gap list, and no page renders. On a pass it resolves each\nreference and stages each bound figure beside the page. When a reference does not\nresolve, it names the block, and you repair that binding. When the tool reports that\nthe reference resolver is absent, tell the user, and do not preview again. Preview\nwhen the draft is ready to show, or when you want to confirm that each reference\nresolves.\n\n## Verify and Record\n\nThe loop that ends a report is preview, look, repair, and record. Run it in order:\n\n- `preview_report` renders the current draft to a page. The Preview section above\n gives its rules.\n- `examine_page` opens the rendered page in a real browser. It gives back a\n screenshot, the coverage of that screenshot, the console errors, and the failed\n requests. The coverage names what the picture holds. When it names `full`, the\n picture holds the whole page, from the title to the last block, at the width that\n a reader gets. Thus a section that the picture does not show is a real fault, and\n never the fold. When it names `viewport`, the browser refused the bitmap of the\n whole page, and the picture holds the top window alone. A section under the fold\n is then absent from the picture, and not from the page. Judge what the picture\n shows. Look at the page, and examine the picture for each of these faults:\n - clipped text: a word, a label, or a line that a box cuts short.\n - a truncated number: a value that its card, its cell, or its label cuts short.\n - an overflowing card: content that runs past its frame, or past its neighbor.\n - a raw column name on an axis, in a legend, or in a table header. A reader reads\n a written label, not the name that the evidence carries.\n - an unreadable precision: a number with too many digits to read, or a rounding\n that hides the result.\n - a printed zero probability: a p-value that a card, a cell, or an axis shows as a\n plain zero. A probability that the test could not resolve is never zero.\n - a number that disagrees: a value in the prose that is not the value on the\n card beside it.\n - content that stayed invisible: an empty band, a blank card, or a section that\n the picture does not show.\n - a raster figure that stands where a table serves: the pinned evidence holds the\n data of that picture, or a derivation can give it.\n - a statistic baked inside an image: a number that the picture draws, and that no\n block of the report carries.\n - a caption that promises what the plot does not show.\n\n A found fault is a repair, and never a note. Never describe such a fault to the\n user in place of the repair.\n- Repair each fault that the look shows. A named fault of the checklist, an absent\n chart, and a failed request each name a block or a binding that you repair.\n- `record_report_version` records the report as one version. It records only after\n you look at the current page. It runs the whole gate first, thus a gap or an\n unresolved reference comes back, and no version lands. A thread holds one version,\n and each record replaces it.\n\nLook again after each repair, because a repair changes the page. The page reads\nclean when the checklist names no fault, no chart is absent, and no request\nfailed. When the page reads clean, record.\n\nThe record loop has no bound. Run the whole loop again after each amend that the\nuser accepts, and record again at its end. Thus the stored version is always the\npage that the user reads.\n\n## Do NOT\n\n- **Transcribe a number from memory.** Every metric, every table cell, and every\n figure binds to a reference that resolves against the pinned evidence.\n- **Transcribe a zero p-value.** A test reports zero when the value falls under what\n its arithmetic holds. Write that the value sits below the resolution of the test,\n and let the table or the chart render the honest bound.\n- **Start a run, or change the analysis.** You read the analysis; you never run it\n and never write to it. You hold no tool that does either, and that is by design.\n- **Invent a path.** Name a file by what a search or a run gave you. Never guess a\n location, and never hardcode one.\n- **Reach outside the pinned evidence.** A reference binds to the frozen snapshot\n of this session alone. Do not cite an artifact that the snapshot does not hold.\n- **Probe for a hash.** Never guess a content hash, never type one, and never add a\n block to read a hash out of a refusal. A reference names the path, and the session\n stamps the hash.\n- **Inline a citation that does not resolve.** A citation block binds to a citation\n id of the pinned evidence. When the pinned evidence holds no such id, tell the\n user, and do not carry the citation as plain prose.\n- **Build a References section.** A citation block sits beside the content that it\n supports. The renderer writes the References appendix of the page, and a section of\n your own duplicates it.\n- **Show evidence before its sentence.** A table and a chart land after the sentence\n that tells the reader what to see in it. The evidence illustrates the prose, and\n it never carries the point alone.\n- **Write a raw token into the prose.** A gene set reads in reader words. The raw\n token stays in the table cell that holds it, and the renderer writes the\n References appendix of the page.\n- **Reach for a figure where a table serves.** When a table artifact holds the data,\n compose a chart block. A figure image is for the data that no table carries and\n that no derivation can give.\n- **Lead with a caveated value.** A headline states the cohort and the yield. A value\n that a caveat qualifies reads in the body, under that caveat.\n- **Rebuild when one amend serves.** One feedback is one block change. Change, move,\n or remove that block by its id, and do not re-author the report.\n- **Leave a gap unread.** When `finish_draft` or `preview_report` reports a gap or\n an unresolved reference, repair it. Do not present a report that does not finish.\n- **Repair a block that the picture could not show.** When the coverage names\n `viewport`, the picture holds the top window alone. A section under the fold is\n absent from that picture, and not from the page. Judge what you saw, and leave\n the rest of the draft as it stands.\n- **Spiral on a cosmetic doubt.** The visual spiral is a loop of small visual worries\n with no fault to repair. Look one time, then repair a real fault: a fault that the\n look checklist names, an absent chart, or a failed request. A named fault is real\n work. A matter of taste is not a fault. When the page reads clean, record.\n";
|
|
@@ -23,6 +23,13 @@ tool only where the context is thin, and reach in a targeted way:
|
|
|
23
23
|
Do not orient again when a prior turn already read what you need — its results are
|
|
24
24
|
still in your context. To reach further is targeted, not a fresh sweep.
|
|
25
25
|
|
|
26
|
+
When your next calls do not depend on the result of one another, make them
|
|
27
|
+
together in one reply. The rule is the same for a read and for an edit: a reply
|
|
28
|
+
of many calls spends one step. A chain of single calls spends one step for each
|
|
29
|
+
call, and the turn can then reach its step cap before the report is complete.
|
|
30
|
+
The loop runs the calls of one reply in order, thus a batch of edits lands in
|
|
31
|
+
your order.
|
|
32
|
+
|
|
26
33
|
## Compose the Argument Spine
|
|
27
34
|
|
|
28
35
|
Before the first block, compose the argument spine. The argument spine is the
|
|
@@ -32,7 +32,7 @@ import { type CitationResolverConfig } from "../citations/resolve.js";
|
|
|
32
32
|
import type { CitationResolver } from "../citations/types.js";
|
|
33
33
|
import type { AuthContext } from "../auth/types.js";
|
|
34
34
|
import type { ReferenceResolver } from "../report-model/reference-resolver.js";
|
|
35
|
-
import type { ResolvePageAsset } from "../tools/report-session/index.js";
|
|
35
|
+
import type { ResolvePageAsset, ResolvePageUrl, SessionPagePublisher } from "../tools/report-session/index.js";
|
|
36
36
|
/** Registered child sandbox-step callable the parent's child dispatch closes over. */
|
|
37
37
|
export type SandboxStepCallable = (input: SandboxStepInput) => Promise<SandboxStepResult>;
|
|
38
38
|
/**
|
|
@@ -132,6 +132,19 @@ export interface CoreRuntimeDeps {
|
|
|
132
132
|
* materializes them to disk and binds its own lookup here.
|
|
133
133
|
*/
|
|
134
134
|
readonly resolveReportPageAsset?: ResolvePageAsset;
|
|
135
|
+
/**
|
|
136
|
+
* The hosted view of a report-session page. A managed host that serves the session URL
|
|
137
|
+
* space binds its publisher here, and the preview tool of the report agent then attaches
|
|
138
|
+
* the minted URL beside the page path. Absent, the tool attaches nothing and the page
|
|
139
|
+
* path stays the whole result, which is the whole local path.
|
|
140
|
+
*/
|
|
141
|
+
readonly sessionPagePublisher?: SessionPagePublisher;
|
|
142
|
+
/**
|
|
143
|
+
* The page-URL formation of the report-session eyes. A managed host whose browser cannot
|
|
144
|
+
* reach the workspace tree binds a resolver that names a served URL for the look. Absent,
|
|
145
|
+
* the eyes tool navigates through a `file://` URL of the page path.
|
|
146
|
+
*/
|
|
147
|
+
readonly resolveReportPageUrl?: ResolvePageUrl;
|
|
135
148
|
/**
|
|
136
149
|
* The eyes of a report session — where a browser comes from for one look at the rendered
|
|
137
150
|
* page. A host with no standing sidecar starts a browser for one look. Thus it binds its own
|
package/dist/runtime/assemble.js
CHANGED
|
@@ -164,6 +164,8 @@ export function assembleCoreRuntime(deps) {
|
|
|
164
164
|
makeResolver: makeReportResolver,
|
|
165
165
|
...(eyes ? { eyes } : {}),
|
|
166
166
|
...(deps.resolveReportPageAsset ? { resolvePageAsset: deps.resolveReportPageAsset } : {}),
|
|
167
|
+
...(deps.sessionPagePublisher ? { sessionPages: deps.sessionPagePublisher } : {}),
|
|
168
|
+
...(deps.resolveReportPageUrl ? { resolvePageUrl: deps.resolveReportPageUrl } : {}),
|
|
167
169
|
...(conversation.logger ? { logger: conversation.logger } : {}),
|
|
168
170
|
});
|
|
169
171
|
// The single registration point for every typed agent. A future thread type's
|
|
@@ -5,16 +5,18 @@
|
|
|
5
5
|
* see: the screenshot, the console errors, and the failed requests. The agent reads the picture and the
|
|
6
6
|
* faults, decides, and repairs. The tool never judges, thus it never blocks the loop.
|
|
7
7
|
*
|
|
8
|
-
* The
|
|
9
|
-
* page lives at `report-sessions/{threadId}/index.html`
|
|
10
|
-
* A missed page means that no preview ran, and it is a
|
|
8
|
+
* The composition says which URL one look opens, and the URL seam is that answer. Absent the seam, the tool
|
|
9
|
+
* navigates to the page file through a `file://` URL. The page lives at `report-sessions/{threadId}/index.html`
|
|
10
|
+
* under the workspace root, which the preview writes. A missed page means that no preview ran, and it is a
|
|
11
|
+
* typed outcome, not a throw.
|
|
11
12
|
*
|
|
12
13
|
* The composition says where a browser comes from, and the eyes seam is that answer. One look acquires one
|
|
13
14
|
* lease, and the tool captures against the endpoint of that lease.
|
|
14
15
|
*
|
|
15
|
-
* The `file://` URL resolves on the filesystem of the browser, because the connection is out of
|
|
16
|
-
* Thus the browser of a lease must hold the workspace tree of the harness host
|
|
17
|
-
* with no such mount reports the page as an unreachable request, and the tool
|
|
16
|
+
* The default `file://` URL resolves on the filesystem of the browser, because the connection is out of
|
|
17
|
+
* process. Thus, absent the URL seam, the browser of a lease must hold the workspace tree of the harness host
|
|
18
|
+
* at the same path. A browser with no such mount reports the page as an unreachable request, and the tool
|
|
19
|
+
* gives back that fault. A bound URL seam lifts that constraint, because the browser then opens a served URL.
|
|
18
20
|
*
|
|
19
21
|
* The tool releases the lease after the look. The release runs on a pass and on a failed capture alike. That
|
|
20
22
|
* release is hygiene, and it is not the guarantee against a leak. A process can die between the acquire and
|
|
@@ -51,6 +53,15 @@ import { type ResolveWorkspaceRoot } from "../../workspace/paths.js";
|
|
|
51
53
|
import { type Tool } from "../define-tool.js";
|
|
52
54
|
import { type ReportSessionStateGateway, type SessionRefusal } from "../report-authoring/authoring-tools.js";
|
|
53
55
|
export type { CapturePage, FailedRequest, PageCapture };
|
|
56
|
+
/**
|
|
57
|
+
* The URL formation of one look. It maps the page of the thread onto the URL that the browser opens.
|
|
58
|
+
* A composition whose browser cannot reach the workspace tree binds a resolver that names a served URL.
|
|
59
|
+
*/
|
|
60
|
+
export type ResolvePageUrl = (args: {
|
|
61
|
+
pagePath: string;
|
|
62
|
+
analysisId: string;
|
|
63
|
+
threadId: string;
|
|
64
|
+
}) => Promise<string>;
|
|
54
65
|
/** The empty input. The tool examines the current page of the thread, thus it needs no field. */
|
|
55
66
|
declare const examinePageInput: z.ZodObject<{}, z.core.$strip>;
|
|
56
67
|
export type ExaminePageInput = z.infer<typeof examinePageInput>;
|
|
@@ -90,15 +101,17 @@ export type ExaminePageResult = {
|
|
|
90
101
|
* `resolveWorkspaceRoot` maps the analysis of the call onto its workspace root, thus one singleton tool
|
|
91
102
|
* serves every analysis. `eyes` gives a browser for one look, and the scope of the acquire carries that
|
|
92
103
|
* analysis and its root. `chrome` carries the connection settings of the capture, and the browser that it
|
|
93
|
-
* names must hold the workspace tree at the same path
|
|
94
|
-
* `
|
|
95
|
-
*
|
|
104
|
+
* names must hold the workspace tree at the same path when the tool navigates to a `file://` URL.
|
|
105
|
+
* `resolvePageUrl` is optional and it replaces that URL, thus a composition whose browser cannot reach the
|
|
106
|
+
* tree names a served URL instead. `capture` is optional and it replaces the whole transport, thus a test
|
|
107
|
+
* injects a seam that reads no browser.
|
|
96
108
|
*/
|
|
97
109
|
export interface ExaminePageToolDeps {
|
|
98
110
|
readonly gateway: ReportSessionStateGateway;
|
|
99
111
|
readonly resolveWorkspaceRoot: ResolveWorkspaceRoot;
|
|
100
112
|
readonly chrome: ChromeConfig;
|
|
101
113
|
readonly eyes?: AcquireEyes;
|
|
114
|
+
readonly resolvePageUrl?: ResolvePageUrl;
|
|
102
115
|
readonly capture?: CapturePage;
|
|
103
116
|
readonly logger?: Logger;
|
|
104
117
|
/**
|
|
@@ -5,16 +5,18 @@
|
|
|
5
5
|
* see: the screenshot, the console errors, and the failed requests. The agent reads the picture and the
|
|
6
6
|
* faults, decides, and repairs. The tool never judges, thus it never blocks the loop.
|
|
7
7
|
*
|
|
8
|
-
* The
|
|
9
|
-
* page lives at `report-sessions/{threadId}/index.html`
|
|
10
|
-
* A missed page means that no preview ran, and it is a
|
|
8
|
+
* The composition says which URL one look opens, and the URL seam is that answer. Absent the seam, the tool
|
|
9
|
+
* navigates to the page file through a `file://` URL. The page lives at `report-sessions/{threadId}/index.html`
|
|
10
|
+
* under the workspace root, which the preview writes. A missed page means that no preview ran, and it is a
|
|
11
|
+
* typed outcome, not a throw.
|
|
11
12
|
*
|
|
12
13
|
* The composition says where a browser comes from, and the eyes seam is that answer. One look acquires one
|
|
13
14
|
* lease, and the tool captures against the endpoint of that lease.
|
|
14
15
|
*
|
|
15
|
-
* The `file://` URL resolves on the filesystem of the browser, because the connection is out of
|
|
16
|
-
* Thus the browser of a lease must hold the workspace tree of the harness host
|
|
17
|
-
* with no such mount reports the page as an unreachable request, and the tool
|
|
16
|
+
* The default `file://` URL resolves on the filesystem of the browser, because the connection is out of
|
|
17
|
+
* process. Thus, absent the URL seam, the browser of a lease must hold the workspace tree of the harness host
|
|
18
|
+
* at the same path. A browser with no such mount reports the page as an unreachable request, and the tool
|
|
19
|
+
* gives back that fault. A bound URL seam lifts that constraint, because the browser then opens a served URL.
|
|
18
20
|
*
|
|
19
21
|
* The tool releases the lease after the look. The release runs on a pass and on a failed capture alike. That
|
|
20
22
|
* release is hygiene, and it is not the guarantee against a leak. A process can die between the acquire and
|
|
@@ -270,13 +272,23 @@ export function createExaminePageTool(deps) {
|
|
|
270
272
|
logger.warn("the session page could not be read", { threadId, analysisId, ...defaultErrorFields(cause) });
|
|
271
273
|
return ok({ outcome: "capture-failed", detail: "the session page could not be read" });
|
|
272
274
|
}
|
|
275
|
+
// The URL seam speaks the throw protocol like the other seams, thus a fault of the formation
|
|
276
|
+
// becomes a typed outcome and the look never starts.
|
|
277
|
+
let url;
|
|
278
|
+
try {
|
|
279
|
+
url = deps.resolvePageUrl !== undefined ? await deps.resolvePageUrl({ pagePath, analysisId, threadId }) : pathToFileURL(pagePath).href;
|
|
280
|
+
}
|
|
281
|
+
catch (cause) {
|
|
282
|
+
logger.warn("the page URL did not resolve", { threadId, analysisId, ...defaultErrorFields(cause) });
|
|
283
|
+
return ok({ outcome: "capture-failed", detail: "the page URL did not resolve" });
|
|
284
|
+
}
|
|
273
285
|
// The scope of the acquire carries the analysis and the root that this call resolved. Thus a
|
|
274
286
|
// realization that starts a browser mounts the same tree, and it holds no second resolver.
|
|
275
287
|
const looked = await runLook({
|
|
276
288
|
transport,
|
|
277
289
|
chrome: deps.chrome,
|
|
278
290
|
scope: { analysisId, workspaceRoot: root },
|
|
279
|
-
url
|
|
291
|
+
url,
|
|
280
292
|
acquireMs,
|
|
281
293
|
releaseMs,
|
|
282
294
|
logger,
|
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
* render seams, thus each tool binds the per-session state to the thread of the call. The barrel exports
|
|
6
6
|
* the factories and their public types for the composition root.
|
|
7
7
|
*/
|
|
8
|
-
export { createPreviewReportTool, type PreviewReportInput, type PreviewReportResult, type PreviewReportToolDeps, type ResolvePageAsset, } from "./preview-report.js";
|
|
9
|
-
export { createExaminePageTool, type CapturePage, type ExaminePageInput, type ExaminePageResult, type ExaminePageToolDeps, type FailedRequest, type PageCapture, } from "./examine-page.js";
|
|
8
|
+
export { createPreviewReportTool, type PreviewReportInput, type PreviewReportResult, type PreviewReportToolDeps, type ResolvePageAsset, type SessionPageAccess, } from "./preview-report.js";
|
|
9
|
+
export { createExaminePageTool, type CapturePage, type ExaminePageInput, type ExaminePageResult, type ExaminePageToolDeps, type FailedRequest, type PageCapture, type ResolvePageUrl, } from "./examine-page.js";
|
|
10
|
+
export { describeSessionPageMintFailure, UnavailableSessionPagePublisher, type SessionPageMintFailure, type SessionPageMintResult, type SessionPagePublisher, } from "./session-page-publisher.js";
|
|
10
11
|
export { createRecordVersionTool, type RecordVersionInput, type RecordVersionResult, type RecordVersionToolDeps } from "./record-version.js";
|
|
11
12
|
export { createDeriveTableTool, type DeriveTableInput, type DeriveTableResult, type DeriveTableToolDeps } from "./derive-table.js";
|
|
12
13
|
export { createListPinnedArtifactsTool, type ListPinnedArtifactsInput, type ListPinnedArtifactsResult, type ListPinnedArtifactsToolDeps, type PinnedArtifact, type PinnedCitation, } from "./list-artifacts.js";
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export { createPreviewReportTool, } from "./preview-report.js";
|
|
9
9
|
export { createExaminePageTool, } from "./examine-page.js";
|
|
10
|
+
export { describeSessionPageMintFailure, UnavailableSessionPagePublisher, } from "./session-page-publisher.js";
|
|
10
11
|
export { createRecordVersionTool } from "./record-version.js";
|
|
11
12
|
export { createDeriveTableTool } from "./derive-table.js";
|
|
12
13
|
export { createListPinnedArtifactsTool, } from "./list-artifacts.js";
|
|
@@ -16,9 +16,11 @@
|
|
|
16
16
|
* namespace belongs to this path alone, thus the tool never writes under the old `previews/` or `reports/`
|
|
17
17
|
* trees. The result carries the absolute page path, thus a local host shows the page with no seam.
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* A hosted view rides the same result. When the composition binds the session-page publisher, the tool
|
|
20
|
+
* mints one grant after the page lands, and the `rendered` arm carries the URL of the URL space
|
|
21
|
+
* `report-sessions/{analysisId}/{threadId}` (`contracts/content-url.ts`) beside the path. A refused mint
|
|
22
|
+
* rides the arm as data, and the page path stays good — a broken grant surface never costs the render. An
|
|
23
|
+
* unbound publisher changes nothing: the arm carries no access field, and a local host opens the file.
|
|
22
24
|
*
|
|
23
25
|
* Each degraded condition is a typed outcome in the ok channel: a session refusal, a gap list, a resolver
|
|
24
26
|
* absence, an unresolvable root at resolver construction, an unresolved reference, a bridge mismatch, a
|
|
@@ -41,12 +43,27 @@ import { type BridgeMismatch } from "../../report-render/value-bridge.js";
|
|
|
41
43
|
import type { RenderProblem } from "../../report-render/types.js";
|
|
42
44
|
import { type Tool } from "../define-tool.js";
|
|
43
45
|
import { type ReportSessionStateGateway, type SessionRefusal } from "../report-authoring/authoring-tools.js";
|
|
46
|
+
import { type SessionPagePublisher } from "./session-page-publisher.js";
|
|
44
47
|
/** The empty input. The tool renders the current draft of the thread, thus it needs no field. */
|
|
45
48
|
declare const previewReportInput: z.ZodObject<{}, z.core.$strip>;
|
|
46
49
|
export type PreviewReportInput = z.infer<typeof previewReportInput>;
|
|
50
|
+
/**
|
|
51
|
+
* The access grant of the hosted view, as data on the `rendered` arm. A granted mint carries the URL of
|
|
52
|
+
* the served page and its expiry. A refused mint carries the described refusal, and the page path stays
|
|
53
|
+
* good.
|
|
54
|
+
*/
|
|
55
|
+
export type SessionPageAccess = {
|
|
56
|
+
granted: true;
|
|
57
|
+
url: string;
|
|
58
|
+
expiresAt: string;
|
|
59
|
+
} | {
|
|
60
|
+
granted: false;
|
|
61
|
+
detail: string;
|
|
62
|
+
};
|
|
47
63
|
/**
|
|
48
64
|
* The typed outcome of the preview tool. Each arm is ok-channel data, thus the tool never throws for a
|
|
49
|
-
* degraded condition. `rendered` carries the absolute page path
|
|
65
|
+
* degraded condition. `rendered` carries the absolute page path, and the access grant of the hosted view
|
|
66
|
+
* when the composition binds the publisher.
|
|
50
67
|
*/
|
|
51
68
|
export type PreviewReportResult = {
|
|
52
69
|
outcome: "refused";
|
|
@@ -82,6 +99,7 @@ export type PreviewReportResult = {
|
|
|
82
99
|
} | {
|
|
83
100
|
outcome: "rendered";
|
|
84
101
|
pagePath: string;
|
|
102
|
+
access?: SessionPageAccess;
|
|
85
103
|
};
|
|
86
104
|
/**
|
|
87
105
|
* The lookup of the source file of one staged asset. It maps the module specifier of a manifest entry
|
|
@@ -99,6 +117,10 @@ export type ResolvePageAsset = (specifier: string) => string;
|
|
|
99
117
|
* `resolvePageAsset` is optional, and absent it resolves each specifier against the installation of the
|
|
100
118
|
* harness. An embedder that ships the asset bytes packed, for example a compiled single-file binary with no
|
|
101
119
|
* `node_modules` tree, materializes them to disk and binds its own lookup here.
|
|
120
|
+
*
|
|
121
|
+
* `sessionPages` is optional, because a local host opens the page path itself. Bound, the tool mints one
|
|
122
|
+
* grant after the page lands and attaches the URL beside the path. A refused mint rides the `rendered` arm
|
|
123
|
+
* as data, and it never fails the render.
|
|
102
124
|
*/
|
|
103
125
|
export interface PreviewReportToolDeps {
|
|
104
126
|
readonly gateway: ReportSessionStateGateway;
|
|
@@ -108,6 +130,7 @@ export interface PreviewReportToolDeps {
|
|
|
108
130
|
}) => ReferenceResolver;
|
|
109
131
|
readonly resolveWorkspaceRoot: ResolveWorkspaceRoot;
|
|
110
132
|
readonly resolvePageAsset?: ResolvePageAsset;
|
|
133
|
+
readonly sessionPages?: SessionPagePublisher;
|
|
111
134
|
readonly logger?: Logger;
|
|
112
135
|
}
|
|
113
136
|
/**
|
|
@@ -16,9 +16,11 @@
|
|
|
16
16
|
* namespace belongs to this path alone, thus the tool never writes under the old `previews/` or `reports/`
|
|
17
17
|
* trees. The result carries the absolute page path, thus a local host shows the page with no seam.
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* A hosted view rides the same result. When the composition binds the session-page publisher, the tool
|
|
20
|
+
* mints one grant after the page lands, and the `rendered` arm carries the URL of the URL space
|
|
21
|
+
* `report-sessions/{analysisId}/{threadId}` (`contracts/content-url.ts`) beside the path. A refused mint
|
|
22
|
+
* rides the arm as data, and the page path stays good — a broken grant surface never costs the render. An
|
|
23
|
+
* unbound publisher changes nothing: the arm carries no access field, and a local host opens the file.
|
|
22
24
|
*
|
|
23
25
|
* Each degraded condition is a typed outcome in the ok channel: a session refusal, a gap list, a resolver
|
|
24
26
|
* absence, an unresolvable root at resolver construction, an unresolved reference, a bridge mismatch, a
|
|
@@ -36,6 +38,7 @@ import { extname, join } from "node:path";
|
|
|
36
38
|
import { relative as relativePosix } from "node:path/posix";
|
|
37
39
|
import { z } from "zod";
|
|
38
40
|
import { reportSessionDir, resolveWorkspacePath } from "../../workspace/paths.js";
|
|
41
|
+
import { buildReportSessionUrl } from "../../contracts/content-url.js";
|
|
39
42
|
import { createNoopLogger } from "../../lib/console-logger.js";
|
|
40
43
|
import { describeFsError, tryFsWrite } from "../../lib/fs-result.js";
|
|
41
44
|
import { defaultErrorFields } from "../../lib/logger.js";
|
|
@@ -49,6 +52,7 @@ import { ASSETS_DIR, DEPS_DIR, derivationScriptName, PAGE_ASSETS, stagedSource,
|
|
|
49
52
|
import { renderReportPage } from "../../report-render/render.js";
|
|
50
53
|
import { defineTool } from "../define-tool.js";
|
|
51
54
|
import { openReportThread } from "../report-authoring/authoring-tools.js";
|
|
55
|
+
import { describeSessionPageMintFailure } from "./session-page-publisher.js";
|
|
52
56
|
/** The empty input. The tool renders the current draft of the thread, thus it needs no field. */
|
|
53
57
|
const previewReportInput = z.object({});
|
|
54
58
|
/**
|
|
@@ -335,6 +339,36 @@ async function sweepAssets(assetsDir, staged, logger) {
|
|
|
335
339
|
}
|
|
336
340
|
}
|
|
337
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* Mint the hosted view of the page that landed.
|
|
344
|
+
*
|
|
345
|
+
* An absent publisher gives no grant, and the arm carries no access field — the page path stays the whole
|
|
346
|
+
* local contract. A refusal and a thrown realization each become the not-granted arm, thus a broken grant
|
|
347
|
+
* surface never costs the render. The URL spells through `buildReportSessionUrl`, thus the formula lives in
|
|
348
|
+
* the contract and the seam gives the content-server base alone.
|
|
349
|
+
*/
|
|
350
|
+
async function mintAccess(publisher, analysisId, threadId, logger) {
|
|
351
|
+
if (publisher === undefined) {
|
|
352
|
+
return undefined;
|
|
353
|
+
}
|
|
354
|
+
let minted;
|
|
355
|
+
try {
|
|
356
|
+
minted = await publisher.mintSessionPageAccess(analysisId, threadId);
|
|
357
|
+
}
|
|
358
|
+
catch (cause) {
|
|
359
|
+
logger.warn("the session-page mint threw", { threadId, analysisId, ...defaultErrorFields(cause) });
|
|
360
|
+
return { granted: false, detail: "session-page-access mint failed" };
|
|
361
|
+
}
|
|
362
|
+
if (!minted.ok) {
|
|
363
|
+
logger.warn("the session-page mint refused", { threadId, analysisId, detail: describeSessionPageMintFailure(minted) });
|
|
364
|
+
return { granted: false, detail: describeSessionPageMintFailure(minted) };
|
|
365
|
+
}
|
|
366
|
+
return {
|
|
367
|
+
granted: true,
|
|
368
|
+
url: buildReportSessionUrl(minted.data.baseUrl, analysisId, threadId, "index.html", minted.data.token),
|
|
369
|
+
expiresAt: minted.data.expiresAt,
|
|
370
|
+
};
|
|
371
|
+
}
|
|
338
372
|
/**
|
|
339
373
|
* Make the render-and-preview tool over the session-state gateway and the render seams.
|
|
340
374
|
*
|
|
@@ -438,7 +472,8 @@ export function createPreviewReportTool(deps) {
|
|
|
438
472
|
logger.warn("the rendered hash did not stamp", { threadId, analysisId, detail });
|
|
439
473
|
return ok({ outcome: "stamp-failed", pagePath: written.value, detail });
|
|
440
474
|
}
|
|
441
|
-
|
|
475
|
+
const access = await mintAccess(deps.sessionPages, analysisId, threadId, logger);
|
|
476
|
+
return ok({ outcome: "rendered", pagePath: written.value, ...(access !== undefined ? { access } : {}) });
|
|
442
477
|
},
|
|
443
478
|
});
|
|
444
479
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionPagePublisher — the seam between the report-session path and the hosted view of its page.
|
|
3
|
+
*
|
|
4
|
+
* The preview tool writes the page under the session directory and gives back the path. A local host
|
|
5
|
+
* opens the file, thus it needs no seam. A managed host serves the page over HTTP, and the one thing
|
|
6
|
+
* that reaches outside the process is the mint of a content-token grant for the URL space
|
|
7
|
+
* `report-sessions/{analysisId}/{threadId}` (`contracts/content-url.ts`). That mint hides behind this
|
|
8
|
+
* one method.
|
|
9
|
+
*
|
|
10
|
+
* The managed realization owns the grant mint and its client. The local default
|
|
11
|
+
* (`UnavailableSessionPagePublisher`) returns not-ok, thus the preview tool attaches no URL and the
|
|
12
|
+
* render still lands — the page path stays the whole local contract.
|
|
13
|
+
*/
|
|
14
|
+
export type SessionPageMintResult = {
|
|
15
|
+
ok: true;
|
|
16
|
+
data: {
|
|
17
|
+
baseUrl: string;
|
|
18
|
+
token: string;
|
|
19
|
+
expiresAt: string;
|
|
20
|
+
};
|
|
21
|
+
} | {
|
|
22
|
+
ok: false;
|
|
23
|
+
status?: number;
|
|
24
|
+
error: {
|
|
25
|
+
message?: string;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
/** The not-ok arm of the seam's result — what a failed mint is allowed to carry. */
|
|
29
|
+
export type SessionPageMintFailure = Extract<SessionPageMintResult, {
|
|
30
|
+
ok: false;
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Renders a failed mint as the line an agent reads back from the preview tool.
|
|
34
|
+
*
|
|
35
|
+
* The failure shape is sparse by design: a realization with no HTTP transport behind it supplies
|
|
36
|
+
* neither a status nor, necessarily, a message. Naming a field the seam left unset would put the
|
|
37
|
+
* literal `status=undefined` in front of the model, thus the line carries only what actually arrived.
|
|
38
|
+
* It lives beside the type because its whole job is to honour that type's optionality.
|
|
39
|
+
*/
|
|
40
|
+
export declare function describeSessionPageMintFailure(failure: SessionPageMintFailure): string;
|
|
41
|
+
export interface SessionPagePublisher {
|
|
42
|
+
/**
|
|
43
|
+
* Mint the access grant of one session page. `baseUrl` is the base URL of the content server, with
|
|
44
|
+
* no res path — the caller spells the whole URL through `buildReportSessionUrl`, thus the formula
|
|
45
|
+
* lives in the contract and never in a realization.
|
|
46
|
+
*/
|
|
47
|
+
mintSessionPageAccess(analysisId: string, threadId: string): Promise<SessionPageMintResult>;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Local default — no hosted view of a session page. Returns not-ok, thus the preview tool carries the
|
|
51
|
+
* refusal as data and the page path stays the whole result.
|
|
52
|
+
*/
|
|
53
|
+
export declare class UnavailableSessionPagePublisher implements SessionPagePublisher {
|
|
54
|
+
mintSessionPageAccess(_analysisId: string, _threadId: string): Promise<SessionPageMintResult>;
|
|
55
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SessionPagePublisher — the seam between the report-session path and the hosted view of its page.
|
|
3
|
+
*
|
|
4
|
+
* The preview tool writes the page under the session directory and gives back the path. A local host
|
|
5
|
+
* opens the file, thus it needs no seam. A managed host serves the page over HTTP, and the one thing
|
|
6
|
+
* that reaches outside the process is the mint of a content-token grant for the URL space
|
|
7
|
+
* `report-sessions/{analysisId}/{threadId}` (`contracts/content-url.ts`). That mint hides behind this
|
|
8
|
+
* one method.
|
|
9
|
+
*
|
|
10
|
+
* The managed realization owns the grant mint and its client. The local default
|
|
11
|
+
* (`UnavailableSessionPagePublisher`) returns not-ok, thus the preview tool attaches no URL and the
|
|
12
|
+
* render still lands — the page path stays the whole local contract.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Renders a failed mint as the line an agent reads back from the preview tool.
|
|
16
|
+
*
|
|
17
|
+
* The failure shape is sparse by design: a realization with no HTTP transport behind it supplies
|
|
18
|
+
* neither a status nor, necessarily, a message. Naming a field the seam left unset would put the
|
|
19
|
+
* literal `status=undefined` in front of the model, thus the line carries only what actually arrived.
|
|
20
|
+
* It lives beside the type because its whole job is to honour that type's optionality.
|
|
21
|
+
*/
|
|
22
|
+
export function describeSessionPageMintFailure(failure) {
|
|
23
|
+
const detail = [];
|
|
24
|
+
if (failure.status !== undefined)
|
|
25
|
+
detail.push(`status=${failure.status}`);
|
|
26
|
+
const message = failure.error.message?.trim();
|
|
27
|
+
if (message)
|
|
28
|
+
detail.push(message);
|
|
29
|
+
return detail.length > 0 ? `session-page-access mint failed: ${detail.join(" ")}` : "session-page-access mint failed";
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Local default — no hosted view of a session page. Returns not-ok, thus the preview tool carries the
|
|
33
|
+
* refusal as data and the page path stays the whole result.
|
|
34
|
+
*/
|
|
35
|
+
export class UnavailableSessionPagePublisher {
|
|
36
|
+
async mintSessionPageAccess(_analysisId, _threadId) {
|
|
37
|
+
return {
|
|
38
|
+
ok: false,
|
|
39
|
+
error: { message: "the hosted view of a session page is unavailable in this environment" },
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inflexa-ai/harness",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Host-agnostic bioinformatics agent harness
|
|
3
|
+
"version": "0.22.0",
|
|
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",
|
|
7
7
|
"main": "dist/index.js",
|